741 lines
17 KiB
Text
741 lines
17 KiB
Text
Class FlakAmmo : Ammo
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_FLAKAMMO";
|
|
Inventory.PickupMessage "";
|
|
Inventory.Amount 10;
|
|
Inventory.MaxAmount 50;
|
|
Ammo.BackpackAmount 20;
|
|
Ammo.BackpackMaxAmount 100;
|
|
Ammo.DropAmount 5;
|
|
}
|
|
override String PickupMessage()
|
|
{
|
|
return String.Format("%s%d%s",StringTable.Localize("$I_FLAKAMMOL"),Amount,StringTable.Localize("$I_FLAKAMMOR"));
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
FAMO A -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class FlakAmmo2 : FlakAmmo
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_FLAKAMMO2";
|
|
Inventory.PickupMessage "$I_FLAKAMMO2";
|
|
Inventory.Amount 1;
|
|
Ammo.DropAmount 1;
|
|
+INVENTORY.IGNORESKILL;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
FSLG A -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class ChunkLight : DynamicLight
|
|
{
|
|
Default
|
|
{
|
|
DynamicLight.Type "Point";
|
|
Args 255,224,128,8;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( !target )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
SetOrigin(target.pos,true);
|
|
if ( isFrozen() ) return;
|
|
args[LIGHT_RED] = int(255*(10-target.frame)*0.1);
|
|
args[LIGHT_GREEN] = int(224*(10-target.frame)*0.1);
|
|
args[LIGHT_BLUE] = int(128*(10-target.frame)*0.1);
|
|
}
|
|
}
|
|
|
|
Class ChunkTrail : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Radius 0.1;
|
|
Height 0;
|
|
+NOBLOCKMAP;
|
|
+NOGRAVITY;
|
|
+DONTSPLASH;
|
|
+FORCEXYBILLBOARD;
|
|
Scale 0.2;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
let l = Spawn("ChunkLight",pos);
|
|
l.target = self;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
if ( InStateSequence(CurState,FindState("Death")) ) return;
|
|
if ( !target )
|
|
{
|
|
int dist = FindState("Spawn").DistanceTo(CurState);
|
|
SetState(FindState("Death")+dist);
|
|
return;
|
|
}
|
|
SetOrigin(target.pos+(0,0,speed),true);
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
FGLO ABCDEFGHIJK 3 Bright;
|
|
Stop;
|
|
Death:
|
|
FGLO ABCDEFGHIJK 1 Bright;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class FlakAccumulator : Thinker
|
|
{
|
|
Actor victim;
|
|
int amount;
|
|
int cnt;
|
|
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
cnt++;
|
|
if ( cnt < 5 ) return;
|
|
Destroy();
|
|
}
|
|
|
|
static void Accumulate( Actor victim, int amount )
|
|
{
|
|
let ti = ThinkerIterator.Create("FlakAccumulator",STAT_USER);
|
|
FlakAccumulator a, match = null;
|
|
while ( a = FlakAccumulator(ti.Next()) )
|
|
{
|
|
if ( a.victim != victim ) continue;
|
|
match = a;
|
|
break;
|
|
}
|
|
if ( !match )
|
|
{
|
|
match = new("FlakAccumulator");
|
|
match.ChangeStatNum(STAT_USER);
|
|
match.victim = victim;
|
|
}
|
|
match.cnt = 0;
|
|
match.amount += amount;
|
|
}
|
|
|
|
static int GetAmount( Actor victim )
|
|
{
|
|
let ti = ThinkerIterator.Create("FlakAccumulator",STAT_USER);
|
|
FlakAccumulator a;
|
|
while ( a = FlakAccumulator(ti.Next()) )
|
|
{
|
|
if ( a.victim != victim ) continue;
|
|
return a.amount;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
Class FlakChunk : Actor
|
|
{
|
|
ChunkTrail trail;
|
|
double rollvel, pitchvel, yawvel;
|
|
double lifetime, lifespeed;
|
|
int lifetics;
|
|
Vector3 oldvel;
|
|
Default
|
|
{
|
|
Obituary "$O_FLAKCANNON";
|
|
Radius 2;
|
|
Height 2;
|
|
Speed 32;
|
|
DamageFunction 16;
|
|
DamageType 'Shredded';
|
|
BounceType "Hexen";
|
|
BounceFactor 1.0;
|
|
WallBounceFactor 1.0;
|
|
PROJECTILE;
|
|
+USEBOUNCESTATE;
|
|
+CANBOUNCEWATER;
|
|
+SKYEXPLODE;
|
|
+INTERPOLATEANGLES;
|
|
+HITTRACER;
|
|
Scale 0.3;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
lifetime = 0;
|
|
lifespeed = FRandom[Flak](0.004,0.008);
|
|
trail = ChunkTrail(Spawn("ChunkTrail",pos));
|
|
trail.target = self;
|
|
trail.speed = 1.2;
|
|
rollvel = FRandom[Flak](50,100)*RandomPick[Flak](-1,1);
|
|
pitchvel = FRandom[Flak](50,100)*RandomPick[Flak](-1,1);
|
|
yawvel = FRandom[Flak](50,100)*RandomPick[Flak](-1,1);
|
|
scale *= Frandom[Flak](0.8,1.2);
|
|
SetState(ResolveState("Spawn")+Random[Flak](0,3));
|
|
}
|
|
override void Tick()
|
|
{
|
|
oldvel = vel;
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
if ( waterlevel > 0 )
|
|
{
|
|
vel.xy *= 0.98;
|
|
rollvel *= 0.98;
|
|
pitchvel *= 0.98;
|
|
yawvel *= 0.98;
|
|
if ( trail ) trail.Destroy();
|
|
}
|
|
lifetics++;
|
|
if ( lifetics > 3 )
|
|
{
|
|
lifetics = 0;
|
|
if ( frame < 11 ) frame++;
|
|
}
|
|
lifetime += lifespeed;
|
|
if ( (waterlevel <= 0) && (frame < 10) && !(lifetics%2) )
|
|
{
|
|
let s = Spawn("UTSmoke",pos);
|
|
s.vel = (FRandom[Flak](-0.1,0.1),FRandom[Flak](-0.1,0.1),FRandom[Flak](-0.1,0.1));
|
|
s.alpha = scale.x/0.5;
|
|
s.SetShade("AAAAAA");
|
|
}
|
|
else if ( waterlevel > 0 )
|
|
{
|
|
let s = Spawn("UTBubble",pos);
|
|
s.vel = (FRandom[Flak](-0.1,0.1),FRandom[Flak](-0.1,0.1),FRandom[Flak](-0.1,0.1));
|
|
s.scale *= scale.x*0.5;
|
|
}
|
|
if ( trail ) trail.alpha = max(0,11-frame)/11.;
|
|
if ( InStateSequence(CurState,FindState("Death")) ) return;
|
|
roll += rollvel;
|
|
pitch += pitchvel;
|
|
angle += pitchvel;
|
|
}
|
|
void A_HandleBounce()
|
|
{
|
|
// chunks in vanilla have a special variation on the standard reflect formula that causes them to bounce differently when hitting a surface head-on
|
|
// (0.5 to 0.8 reduction perpendicular to the surface normal, to be specific)
|
|
Vector3 HitNormal = -vel.unit();
|
|
F3DFloor ff;
|
|
if ( BlockingFloor )
|
|
{
|
|
// find closest 3d floor for its normal
|
|
for ( int i=0; i<CurSector.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(CurSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
|
|
ff = CurSector.Get3DFloor(i);
|
|
break;
|
|
}
|
|
if ( ff ) HitNormal = -ff.top.Normal;
|
|
else HitNormal = BlockingFloor.floorplane.Normal;
|
|
}
|
|
else if ( BlockingCeiling )
|
|
{
|
|
// find closest 3d floor for its normal
|
|
for ( int i=0; i<CurSector.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(CurSector.Get3DFloor(i).bottom.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
|
|
ff = CurSector.Get3DFloor(i);
|
|
break;
|
|
}
|
|
if ( ff ) HitNormal = -ff.bottom.Normal;
|
|
else HitNormal = BlockingCeiling.ceilingplane.Normal;
|
|
}
|
|
else if ( BlockingLine )
|
|
{
|
|
HitNormal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();
|
|
if ( !BlockingLine.sidedef[1] || (CurSector == BlockingLine.frontsector) )
|
|
HitNormal *= -1;
|
|
}
|
|
else if ( BlockingMobj )
|
|
{
|
|
Vector3 diff = level.Vec3Diff(pos,BlockingMobj.pos);
|
|
if ( (pos.x+radius) <= (BlockingMobj.pos.x-BlockingMobj.radius) )
|
|
HitNormal = (-1,0,0);
|
|
else if ( (pos.x-radius) >= (BlockingMobj.pos.x+BlockingMobj.radius) )
|
|
HitNormal = (1,0,0);
|
|
else if ( (pos.y+radius) <= (BlockingMobj.pos.y-BlockingMobj.radius) )
|
|
HitNormal = (0,-1,0);
|
|
else if ( (pos.y-radius) >= (BlockingMobj.pos.y+BlockingMobj.radius) )
|
|
HitNormal = (0,1,0);
|
|
else if ( pos.z >= (BlockingMobj.pos.z+BlockingMobj.height) )
|
|
HitNormal = (0,0,1);
|
|
else if ( (pos.z+height) <= BlockingMobj.pos.z )
|
|
HitNormal = (0,0,-1);
|
|
}
|
|
// undo the bounce, we need to hook in our own
|
|
vel = oldvel;
|
|
// re-do the bounce with our formula
|
|
vel = 0.8*((vel dot HitNormal)*HitNormal*(-1.8+FRandom[Flak](0.0,0.8))+vel);
|
|
bHITOWNER = true;
|
|
int numpt = Random[Flak](2,3);
|
|
if ( (frame < 10) && Random[Flak](0,1) )
|
|
{
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[Flak](-1,1),FRandom[Flak](-1,1),FRandom[Flak](-1,1)).unit()*FRandom[Flak](2,4);
|
|
let s = Spawn("UTSpark",pos);
|
|
s.vel = pvel;
|
|
}
|
|
}
|
|
else A_SprayDecal("WallCrack",-8);
|
|
A_Gravity();
|
|
gravity = 0.35;
|
|
rollvel = FRandom[Flak](50,100)*RandomPick[Flak](-1,1)*(vel.length()/speed);
|
|
pitchvel = FRandom[Flak](50,100)*RandomPick[Flak](-1,1)*(vel.length()/speed);
|
|
yawvel = FRandom[Flak](50,100)*RandomPick[Flak](-1,1)*(vel.length()/speed);
|
|
A_PlaySound("flak/bounce",volume:0.3);
|
|
A_AlertMonsters();
|
|
if ( vel.length() < 5.0 ) ExplodeMissile();
|
|
}
|
|
override int DoSpecialDamage( Actor target, int damage, Name damagetype )
|
|
{
|
|
if ( vel.length() <= 5.0 ) return -1;
|
|
FlakAccumulator.Accumulate(target,damage);
|
|
int gibhealth = (target.GibHealth==int.min)?-target.SpawnHealth():target.GibHealth;
|
|
int calcdmg = FlakAccumulator.GetAmount(target);
|
|
if ( target.Health-calcdmg <= (gibhealth/2) ) bEXTREMEDEATH = true;
|
|
if ( !target.bNOBLOOD )
|
|
{
|
|
target.SpawnBlood(pos,AngleTo(target),damage);
|
|
A_PlaySound("flak/meat",volume:0.3);
|
|
A_AlertMonsters();
|
|
}
|
|
return damage;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
FCH1 A 0;
|
|
Goto Idle;
|
|
FCH2 A 0;
|
|
Goto Idle;
|
|
FCH3 A 0;
|
|
Goto Idle;
|
|
FCH4 A 0;
|
|
Goto Idle;
|
|
Idle:
|
|
#### # -1;
|
|
Stop;
|
|
Bounce:
|
|
#### # 0 A_HandleBounce();
|
|
Goto Idle;
|
|
Death:
|
|
#### # 0
|
|
{
|
|
bMOVEWITHSECTOR = true;
|
|
A_SetTics(Random[Flak](30,50));
|
|
}
|
|
#### # 1
|
|
{
|
|
A_SetScale(scale.x-0.002);
|
|
if ( scale.x <= 0.0 ) Destroy();
|
|
}
|
|
Wait;
|
|
Crash:
|
|
TNT1 A 0
|
|
{
|
|
let l = Spawn("BulletImpact",pos);
|
|
Vector3 dir;
|
|
if ( tracer ) dir = level.Vec3Diff(pos,tracer.Vec3Offset(0,0,tracer.height/2)).unit();
|
|
else dir = vel.unit();
|
|
l.angle = atan2(dir.y,dir.x);
|
|
l.pitch = asin(-dir.z);
|
|
A_PlaySound("flak/hit",volume:0.3);
|
|
A_AlertMonsters();
|
|
}
|
|
XDeath:
|
|
TNT1 A 1;
|
|
Stop;
|
|
Dummy:
|
|
FCH1 ABCDEFGHIJKL -1;
|
|
FCH2 ABCDEFGHIJKL -1;
|
|
FCH3 ABCDEFGHIJKL -1;
|
|
FCH4 ABCDEFGHIJKL -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class SlugSmoke : Actor
|
|
{
|
|
double lifetime, lifespeed;
|
|
Default
|
|
{
|
|
Radius 0.1;
|
|
Height 0;
|
|
+NOBLOCKMAP;
|
|
+NOGRAVITY;
|
|
+DONTSPLASH;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
lifetime = 0;
|
|
lifespeed = FRandom[Flak](0.004,0.008);
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
lifetime += lifespeed;
|
|
let s = Spawn("UTSmoke",pos);
|
|
s.vel = (FRandom[Flak](-0.5,0.5),FRandom[Flak](-0.5,0.5),FRandom[Flak](-0.5,0.5));
|
|
s.vel.z += 2.;
|
|
s.alpha = scale.x;
|
|
s.SetShade("AAAAAA");
|
|
scale.x = max(0,1-lifetime);
|
|
if ( scale.x <= 0 ) Destroy();
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class SlugLight : DynamicLight
|
|
{
|
|
double lifetime;
|
|
Default
|
|
{
|
|
DynamicLight.Type "Point";
|
|
Args 255,224,128,80;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
lifetime = 1.0;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
args[LIGHT_RED] = int(255*lifetime);
|
|
args[LIGHT_GREEN] = int(224*lifetime);
|
|
args[LIGHT_BLUE] = int(128*lifetime);
|
|
lifetime -= 0.05;
|
|
if ( lifetime <= 0 ) Destroy();
|
|
}
|
|
}
|
|
|
|
Class FlakSlug : Actor
|
|
{
|
|
Default
|
|
{
|
|
Obituary "$O_FLAKCANNON";
|
|
DamageType 'FlakDeath';
|
|
Radius 2;
|
|
Height 2;
|
|
Gravity 0.35;
|
|
Speed 20;
|
|
PROJECTILE;
|
|
-NOGRAVITY;
|
|
+SKYEXPLODE;
|
|
+EXPLODEONWATER;
|
|
+HITTRACER;
|
|
+FORCERADIUSDMG;
|
|
+NODAMAGETHRUST;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
vel.z += 3;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( waterlevel > 0 ) vel.xy *= 0.98;
|
|
}
|
|
action void A_FlakExplode()
|
|
{
|
|
bForceXYBillboard = true;
|
|
A_SetRenderStyle(1.0,STYLE_Add);
|
|
A_SprayDecal("RocketBlast",50);
|
|
A_NoGravity();
|
|
A_SetScale(1.2);
|
|
UTMainHandler.DoBlast(self,120,75000);
|
|
A_Explode(70,120);
|
|
A_QuakeEx(4,4,4,8,0,170,"",QF_RELATIVE|QF_SCALEDOWN,falloff:120,rollIntensity:0.2);
|
|
A_PlaySound("flak/explode",CHAN_VOICE);
|
|
A_AlertMonsters();
|
|
if ( !Tracer ) Spawn("SlugSmoke",pos);
|
|
Spawn("SlugLight",pos);
|
|
Vector3 x, y, z;
|
|
double a, s;
|
|
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
|
|
Actor p;
|
|
Vector3 spawnofs;
|
|
if ( BlockingMobj ) spawnofs = level.Vec3Diff(pos,BlockingMobj.Vec3Offset(0,0,BlockingMobj.height/2)).unit()*8;
|
|
else if ( BlockingFloor ) spawnofs = BlockingFloor.floorplane.Normal*8;
|
|
else if ( BlockingCeiling ) spawnofs = BlockingCeiling.ceilingplane.Normal*8;
|
|
else if ( BlockingLine )
|
|
{
|
|
spawnofs = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit()*8;
|
|
if ( !BlockingLine.sidedef[1] || (CurSector == BlockingLine.frontsector) )
|
|
spawnofs *= -1;
|
|
}
|
|
for ( int i=0; i<5; i++ )
|
|
{
|
|
p = Spawn("FlakChunk",Vec3Offset(spawnofs.x,spawnofs.y,spawnofs.z));
|
|
p.bHITOWNER = true;
|
|
a = FRandom[Flak](0,360);
|
|
s = FRandom[Flak](0,0.1);
|
|
Vector3 dir = (x+y*cos(a)*s+z*sin(a)*s).unit();
|
|
p.angle = atan2(dir.y,dir.x);
|
|
p.pitch = -asin(dir.z);
|
|
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*(p.speed+FRandom[Flak](-3,3));
|
|
p.target = target;
|
|
}
|
|
int numpt = Random[Flak](8,12);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[Flak](-1,1),FRandom[Flak](-1,1),FRandom[Flak](-1,1)).unit()*FRandom[Flak](2,8);
|
|
let s = Spawn("UTSpark",pos);
|
|
s.vel = pvel;
|
|
}
|
|
numpt = Random[Flak](15,30);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[Flak](-1,1),FRandom[Flak](-1,1),FRandom[Flak](-1,1)).unit()*FRandom[Flak](6,16);
|
|
let s = Spawn("UTChip",pos);
|
|
s.vel = pvel;
|
|
s.scale *= FRandom[Flak](0.9,1.8);
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
FSLG A 1
|
|
{
|
|
for ( int i=0; i<6; i++ )
|
|
{
|
|
let s = Spawn("UTSmoke",pos);
|
|
s.vel = (FRandom[Flak](-0.5,0.5),FRandom[Flak](-0.5,0.5),FRandom[Flak](-0.5,0.5));
|
|
s.alpha = 0.5;
|
|
s.SetShade("AAAAAA");
|
|
}
|
|
}
|
|
Wait;
|
|
Death:
|
|
EXP2 A 0 A_FlakExplode();
|
|
EXP2 ABCDEFGHIJKLMNOPQR 1 BRIGHT;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class FlakLight : DynamicLight
|
|
{
|
|
int cnt;
|
|
Default
|
|
{
|
|
DynamicLight.Type "Point";
|
|
args 255,224,128,150;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( !target )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
if ( target.player ) SetOrigin(target.Vec3Offset(0,0,target.player.viewz-target.pos.z),true);
|
|
else SetOrigin(target.pos,true);
|
|
if ( cnt++ > 2 ) Destroy();
|
|
}
|
|
}
|
|
|
|
Class FlakCannon : UTWeapon
|
|
{
|
|
action void A_Loading( bool first = false )
|
|
{
|
|
if ( first ) A_PlaySound("flak/load",CHAN_WEAPON);
|
|
else A_PlaySound("flak/reload",CHAN_6);
|
|
}
|
|
action void A_FireChunks()
|
|
{
|
|
Weapon weap = Weapon(invoker);
|
|
if ( !weap ) return;
|
|
if ( weap.Ammo1.Amount <= 0 ) return;
|
|
if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
|
|
A_PlaySound("flak/fire",CHAN_WEAPON);
|
|
invoker.FireEffect();
|
|
UTMainHandler.DoFlash(self,Color(160,255,96,0),1);
|
|
UTMainHandler.DoSwing(self,(FRandom[Flak](-0.3,-0.8),FRandom[Flak](-0.5,0.5)),4,-1.5,2,SWING_Spring,2,2);
|
|
A_AlertMonsters();
|
|
A_QuakeEx(1,1,1,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.05);
|
|
Vector3 x, y, z;
|
|
double a, s;
|
|
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
|
|
Vector3 origin = (pos.x,pos.y,player.viewz)+10.0*x+4.0*y-3.0*z;
|
|
A_Overlay(-2,"MuzzleFlash");
|
|
A_OverlayFlags(-2,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true);
|
|
A_OverlayRenderstyle(-2,STYLE_Add);
|
|
[x, y, z] = dt_CoordUtil.GetAxes(BulletSlope(),angle,roll);
|
|
Vector3 offsets[8]; // vanilla adds these to each chunk
|
|
offsets[0] = (0,0,0);
|
|
offsets[1] = -z;
|
|
offsets[2] = 2*y+z;
|
|
offsets[3] = -y;
|
|
offsets[4] = 2*y-z;
|
|
offsets[5] = (0,0,0);
|
|
offsets[6] = y-z;
|
|
offsets[7] = 2*y+z;
|
|
Actor p;
|
|
for ( int i=0; i<8; i++ )
|
|
{
|
|
p = Spawn("FlakChunk",level.Vec3Offset(origin,offsets[i]));
|
|
a = FRandom[Flak](0,360);
|
|
s = FRandom[Flak](0,0.1);
|
|
Vector3 dir = (x+y*cos(a)*s+z*sin(a)*s).unit();
|
|
p.angle = atan2(dir.y,dir.x);
|
|
p.pitch = -asin(dir.z);
|
|
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*(p.speed+FRandom[Flak](-3,3));
|
|
p.target = self;
|
|
}
|
|
int numpt = Random[Flak](20,30);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
let s = Spawn("UTViewSpark",origin);
|
|
UTViewSpark(s).ofs = (10,2,-3);
|
|
UTViewSpark(s).vvel = (FRandom[Flak](3,12),FRandom[Flak](-4,4),FRandom[Flak](-4,4));
|
|
s.target = self;
|
|
}
|
|
for ( int i=0; i<12; i++ )
|
|
{
|
|
let s = Spawn("UTViewSmoke",origin);
|
|
UTViewSmoke(s).ofs = (10,2,-3);
|
|
UTViewSmoke(s).vvel = (FRandom[Flak](0,1.2),FRandom[Flak](-.4,.4),FRandom[Flak](-.4,.4));
|
|
s.target = self;
|
|
s.scale *= 2.4;
|
|
s.alpha *= 0.5;
|
|
}
|
|
}
|
|
action void A_FireSlug()
|
|
{
|
|
Weapon weap = Weapon(invoker);
|
|
if ( !weap ) return;
|
|
if ( weap.Ammo1.Amount <= 0 ) return;
|
|
if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
|
|
A_PlaySound("flak/altfire",CHAN_WEAPON);
|
|
invoker.FireEffect();
|
|
UTMainHandler.DoFlash(self,Color(128,255,96,0),1);
|
|
UTMainHandler.DoSwing(self,(FRandom[Flak](-0.4,-0.8),FRandom[Flak](0.4,0.8)),4,-1,3,SWING_Spring,3,5);
|
|
A_AlertMonsters();
|
|
A_QuakeEx(2,2,2,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.1);
|
|
Vector3 x, y, z;
|
|
double a, s;
|
|
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
|
|
Vector3 origin = (pos.x,pos.y,player.viewz)+10.0*x+2.0*y-3.0*z;
|
|
A_Overlay(-2,"MuzzleFlash");
|
|
A_OverlayFlags(-2,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true);
|
|
A_OverlayRenderstyle(-2,STYLE_Add);
|
|
Actor p = Spawn("FlakSlug",origin);
|
|
p.angle = angle;
|
|
p.pitch = BulletSlope();
|
|
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed;
|
|
p.target = self;
|
|
int numpt = Random[Flak](10,15);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
let s = Spawn("UTViewSpark",origin);
|
|
UTViewSpark(s).ofs = (10,2,-3);
|
|
UTViewSpark(s).vvel = (FRandom[Flak](3,12),FRandom[Flak](-4,4),FRandom[Flak](-4,4));
|
|
s.target = self;
|
|
}
|
|
for ( int i=0; i<16; i++ )
|
|
{
|
|
let s = Spawn("UTViewSmoke",origin);
|
|
UTViewSmoke(s).ofs = (10,2,-3);
|
|
UTViewSmoke(s).vvel = (FRandom[Flak](0,1.2),FRandom[Flak](-.8,.8),FRandom[Flak](-.8,.8));
|
|
s.target = self;
|
|
s.scale *= 2.4;
|
|
s.alpha *= 0.5;
|
|
}
|
|
}
|
|
|
|
Default
|
|
{
|
|
Tag "$T_FLAKCANNON";
|
|
Inventory.PickupMessage "$I_FLAKCANNON";
|
|
Weapon.UpSound "flak/select";
|
|
Weapon.SlotNumber 8;
|
|
Weapon.SelectionOrder 2;
|
|
Weapon.AmmoType "FlakAmmo";
|
|
Weapon.AmmoUse 1;
|
|
Weapon.AmmoType2 "FlakAmmo";
|
|
Weapon.AmmoUse2 1;
|
|
Weapon.AmmoGive 10;
|
|
UTWeapon.DropAmmo 5;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
FPCK A -1;
|
|
Stop;
|
|
FPCK B -1;
|
|
Stop;
|
|
Ready:
|
|
FLKS ABDEGHJKMNPQSTVWYZ 1 A_WeaponReady(WRF_NOFIRE);
|
|
FKS2 BC 1 A_WeaponReady(WRF_NOFIRE);
|
|
FLKL A 1 A_Loading(true);
|
|
FLKL BCEFGIJKMNO 1;
|
|
Goto Idle;
|
|
Loading:
|
|
FLKL A 1
|
|
{
|
|
A_CheckReload();
|
|
if ( invoker.Ammo1.Amount > 0 ) A_Loading();
|
|
}
|
|
FLKL BCEFGIJKMNO 1;
|
|
Idle:
|
|
FLKI A 1 A_WeaponReady();
|
|
Wait;
|
|
Fire:
|
|
FLKF A 1 A_FireChunks();
|
|
FLKF BCDEFGHI 1;
|
|
FLKF J 4;
|
|
Goto Loading;
|
|
AltFire:
|
|
FLKA A 1 A_FireSlug();
|
|
FLKA BCDEFGHIJK 2;
|
|
FLKA K 4;
|
|
Goto Loading;
|
|
Select:
|
|
FLKS A 1 A_Raise(int.max);
|
|
Wait;
|
|
Deselect:
|
|
FLKD ABCDEFGHIJ 1;
|
|
FLKD J 1 A_Lower(int.max);
|
|
Wait;
|
|
MuzzleFlash:
|
|
FMUZ A 3 Bright
|
|
{
|
|
let l = Spawn("FlakLight",pos);
|
|
l.target = self;
|
|
}
|
|
Stop;
|
|
}
|
|
}
|