stinger_m/zscript/ueightball.zsc

617 lines
14 KiB
Text

Class URocketAmmo : Ammo
{
Default
{
Tag "$T_ROCKETCAN";
Inventory.Icon "I_Rocket";
Inventory.PickupMessage "";
Inventory.Amount 12;
Inventory.MaxAmount 48;
Ammo.BackpackAmount 9;
Ammo.BackpackMaxAmount 96;
Ammo.DropAmount 6;
+INVENTORY.IGNORESKILL;
}
override String PickupMessage()
{
if ( PickupMsg.Length() > 0 ) return Super.PickupMessage();
return String.Format("%s%d%s",StringTable.Localize("$I_ROCKETCANL"),Amount,StringTable.Localize("$I_ROCKETCANR"));
}
States
{
Spawn:
RPAK A 8 A_CheckProximity(1,"PlayerPawn",80,1,CPXF_ANCESTOR|CPXF_CHECKSIGHT);
Wait;
RPAK AABCDEFGHIJ 8;
RPAK J 8 A_CheckProximity(1,"PlayerPawn",80,0,CPXF_ANCESTOR|CPXF_CHECKSIGHT|CPXF_EXACT);
Goto Spawn+12;
RPAK JJIHGFEDCBA 8;
Goto Spawn;
}
}
Class URocketAmmo2 : URocketAmmo
{
Default
{
Tag "$T_ROCKETCAN2";
Inventory.Amount 2;
Ammo.DropAmount 2;
+INVENTORY.IGNORESKILL;
}
States
{
Spawn:
RCKT A 8 A_CheckProximity(1,"PlayerPawn",80,1,CPXF_ANCESTOR|CPXF_CHECKSIGHT);
Wait;
RCKT AA 8;
RCKT B 8 A_CheckProximity(1,"PlayerPawn",80,0,CPXF_ANCESTOR|CPXF_CHECKSIGHT|CPXF_EXACT);
Goto Spawn+3;
RCKT BB 8;
Goto Spawn;
}
}
Class URocketTrail : Actor
{
Default
{
RenderStyle "Add";
Radius 0.1;
Height 0;
+NOBLOCKMAP;
+NOGRAVITY;
+DONTSPLASH;
+FORCEXYBILLBOARD;
+NOTELEPORT;
Scale 0.4;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
let l = Spawn("RocketLight",pos);
l.target = self;
}
override void Tick()
{
Super.Tick();
if ( !target || !target.bMISSILE )
{
Destroy();
return;
}
SetOrigin(target.pos,true);
}
States
{
Spawn:
MFLA A -1 Bright;
Stop;
}
}
Class URocket : Actor
{
Vector3 InitialDir, Acceleration;
int ticcnt;
Default
{
Obituary "$O_EIGHTBALL";
DamageType 'RocketDeath';
Radius 2;
Height 2;
Speed 15;
PROJECTILE;
+SKYEXPLODE;
+EXPLODEONWATER;
+SEEKERMISSILE;
+FORCERADIUSDMG;
+NODAMAGETHRUST;
+INTERPOLATEANGLES;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
let l = Spawn("URocketTrail",pos);
l.target = self;
A_PlaySound("eightball/fly",CHAN_VOICE,1.0,true,2.5);
if ( tracer ) vel *= 0.9;
Acceleration = vel.unit()*50;
}
action void A_RocketExplode( int dmg, int rad )
{
bFORCEXYBILLBOARD = true;
A_SetRenderStyle(1.0,STYLE_Add);
A_SprayDecal("RocketBlast",50);
A_NoGravity();
A_SetScale(FRandomPick[ExploS](-2.5,2.5),FRandomPick[ExploS](-2.5,2.5));
UTMainHandler.DoBlast(self,rad,80000);
A_Explode(dmg,rad);
A_QuakeEx(3,3,3,8,0,rad+50,"",QF_RELATIVE|QF_SCALEDOWN,falloff:rad,rollIntensity:0.2);
A_PlaySound("utrl/explode",CHAN_VOICE);
A_AlertMonsters();
Spawn("RocketExplLight",pos);
int numpt = Random[Eightball](15,30);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Eightball](-1,1),FRandom[Eightball](-1,1),FRandom[Eightball](-1,1)).unit()*FRandom[Eightball](1,3);
let s = Spawn("UTSmoke",pos);
s.vel = pvel;
}
numpt = Random[Eightball](10,20);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Eightball](-1,1),FRandom[Eightball](-1,1),FRandom[Eightball](-1,1)).unit()*FRandom[Eightball](2,6);
let s = Spawn("UTSpark",pos);
s.vel = pvel;
}
numpt = Random[Eightball](35,70);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Eightball](-1,1),FRandom[Eightball](-1,1),FRandom[Eightball](-1,1)).unit()*FRandom[Eightball](2,12);
let s = Spawn("UTChip",pos);
s.vel = pvel;
s.scale *= FRandom[Eightball](0.9,2.7);
}
}
action void A_RocketSeek()
{
if ( invoker.InitialDir.length() < double.epsilon ) invoker.InitialDir = vel.unit();
if ( tracer && (tracer != target) )
{
Vector3 SeekingDir = level.Vec3Diff(pos,tracer.Vec3Offset(0,0,tracer.height/2)).unit();
if ( SeekingDir dot invoker.InitialDir > 0 )
{
double MagnitudeVel = Vel.length();
SeekingDir = (SeekingDir*0.5*MagnitudeVel+Vel).unit();
Vel = MagnitudeVel * SeekingDir;
invoker.Acceleration = 25 * SeekingDir;
}
}
}
States
{
Spawn:
RCKT ABC 1
{
roll += 30;
if ( invoker.ticcnt++ > 3 )
{
invoker.ticcnt = 0;
A_RocketSeek();
}
vel += invoker.Acceleration/TICRATE;
if ( vel.length() > 30. ) vel = Vel.unit()*30.;
Vector3 dir = vel.unit();
if ( waterlevel <= 0 ) vel = dir*min(vel.length()+1,24);
angle = atan2(dir.y,dir.x);
pitch = asin(-dir.z);
for ( int i=0; i<3; i++ )
{
let s = Spawn("UTSmoke",pos);
s.vel = (FRandom[Eightball](-0.2,0.2),FRandom[Eightball](-0.2,0.2),FRandom[Eightball](-0.2,0.2));
s.vel += vel*0.1;
}
}
Wait;
Death:
TNT1 A 0
{
A_RocketExplode(100,200);
return A_Jump(256,"Explo1","Explo2","Explo3","Explo4","Explo5");
}
Explo1:
EXP1 ABCDEFGH 3 Bright;
Stop;
Explo2:
EXP2 ABCDEFGH 3 Bright;
Stop;
Explo3:
EXP3 ABCDEFGH 3 Bright;
Stop;
Explo4:
EXP4 ABCDEFGH 3 Bright;
Stop;
Explo5:
EXP5 ABCDEFGH 3 Bright;
Stop;
}
}
Class UGrenade : URocket
{
double rollvel, pitchvel, anglevel;
Default
{
DamageType 'GrenadeDeath';
-NOGRAVITY;
+USEBOUNCESTATE;
-EXPLODEONWATER;
+CANBOUNCEWATER;
+NOEXPLODEFLOOR;
+DONTBOUNCEONSHOOTABLES;
BounceType "Hexen";
WallBounceFactor 0.75;
BounceFactor 0.75;
ReactionTime 85;
Speed 15;
Gravity 0.35;
}
override void PostBeginPlay()
{
Actor.PostBeginPlay();
rollvel = FRandom[Eightball](-8,8);
pitchvel = FRandom[Eightball](-8,8);
anglevel = FRandom[Eightball](-8,8);
ReactionTime += Random[Eightball](0,20);
}
States
{
Spawn:
RCKT A 1
{
if ( !bNOGRAVITY )
{
angle += anglevel;
pitch += pitchvel;
roll += rollvel;
}
let s = Spawn("UTSmoke",pos);
s.scale *= 2.0;
s.alpha *= 0.6;
s.vel = (FRandom[Eightball](-0.1,0.1),FRandom[Eightball](-0.1,0.1),FRandom[Eightball](-0.1,0.3));
s.vel += vel*0.05;
s.SetShade("000000");
A_Countdown();
}
Wait;
Bounce:
RCKT A 0
{
bHITOWNER = true;
A_PlaySound("utrl/bounce");
rollvel = FRandom[Eightball](-16,16);
pitchvel = FRandom[Eightball](-16,16);
anglevel = FRandom[Eightball](-16,16);
if ( vel.z > 10 ) vel.z = 0.5*(10+vel.z);
else if ( BlockingFloor && (vel.xy.length() < 0.5) )
{
vel *= 0;
bNOGRAVITY = true;
bMOVEWITHSECTOR = true;
ClearBounce();
}
}
Goto Spawn;
Death:
TNT1 A 0 A_RocketExplode(120,200);
Goto Super::Death+1;
}
}
Class Eightball : UnrealWeapon
{
bool LockedOn;
Actor LockedTarget;
TextureID lockontex;
int locktics;
bool bSingleRocket;
override int, int, bool, bool GetClipAmount()
{
return special1?special1:-1, -1, false, false;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
lockontex = TexMan.CheckForTexture("Crosshr6",TexMan.Type_Any);
}
override void PreRender( double lbottom )
{
if ( LockedTarget ) Screen.DrawTexture(lockontex,false,Screen.GetWidth()*0.5,Screen.GetHeight()*0.5);
}
override void Tick()
{
Super.Tick();
if ( !Owner ) return;
if ( Owner.Health <= 0 )
{
LockedTarget = null;
LockedOn = false;
crosshair = 0;
return;
}
if ( LockedOn && (!LockedTarget || (LockedTarget.Health <= 0) || !LockedTarget.bIsMonster || LockedTarget.bKilled || LockedTarget.bCorpse || !LockedTarget.bShootable || (Owner.player.ReadyWeapon != self)) )
{
LockedTarget = null;
LockedOn = false;
if ( Owner.player.ReadyWeapon == self )
{
Owner.A_PlaySound("utrl/seeklost",CHAN_WEAPON,Dampener.Active(Owner)?.1:1.);
if ( !Dampener.Active(Owner) ) Owner.A_AlertMonsters();
}
}
if ( LockedTarget ) crosshair = 99;
else crosshair = 0;
}
// consumes 1 ammo
action void A_LoadRocket( bool checktarget = true )
{
Weapon weap = Weapon(invoker);
if ( !weap ) return;
if ( weap.Ammo1.Amount <= 0 ) return;
if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
if ( weap.bAltFire )
{
invoker.LockedTarget = null;
invoker.LockedOn = false;
}
if ( checktarget && !weap.bAltFire ) A_CheckTarget();
invoker.special1++;
}
// refire that is ignored if there's no ammo
action void A_LoadedRefire( statelabel flash = null )
{
Weapon weap = Weapon(invoker);
if ( !weap ) return;
if ( weap.Ammo1.Amount <= 0 ) return;
A_Refire(flash);
}
// fire all the rockets (or grenades)
action void A_FireRockets( int num )
{
Weapon weap = Weapon(invoker);
if ( !weap ) return;
invoker.special1 = 0;
if ( weap.bAltFire )
{
A_PlaySound("utrl/altfire",CHAN_WEAPON,Dampener.Active(self)?.3:3.);
if ( !Dampener.Active(self) ) A_AlertMonsters();
}
else
{
A_PlaySound("utrl/fire",CHAN_WEAPON);
A_AlertMonsters();
}
invoker.FireEffect();
UTMainHandler.DoFlash(self,Color(64,255,0,0),1);
if ( self is 'UTPlayer' )
UTPlayer(self).PlayAttacking3();
A_QuakeEx(2+num,2+num,2+num,6+num,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.1+num*0.05);
for ( int i=0; i<num; i++ )
UTMainHandler.DoSwing(self,(FRandom[Eightball](-0.8,-0.8),FRandom[Eightball](-0.5,0.5)),1,-0.2,Random[Eightball](3,4),SWING_Spring,Random[Eightball](2,5),Random[Eightball](2,4));
Vector3 x, y, z, x2, y2, z2;
double a, s;
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = (pos.x,pos.y,player.viewz)+10*x-2*z;
[x2, y2, z2] = dt_CoordUtil.GetAxes(BulletSlope(),angle,roll);
Actor p;
if ( weap.bAltFire )
{
// grenades
for ( int i=0; i<num; i++ )
{
a = FRandom[Eightball](0,360);
s = FRandom[Eightball](0,(num>1)?12:0);
Vector3 dir = (x2+cos(a)*y2*s*0.004+sin(a)*z2*s*0.004).unit();
p = Spawn("UGrenade",origin+cos(a)*y*s+sin(a)*z*s);
p.vel = x*(vel dot x)*0.4 + dir*p.speed*FRandom[Eightball](1.0,1.2);
p.vel.z += 3.5;
p.target = self;
}
}
else if ( num <= 1 )
{
// single rocket
p = Spawn("URocket",origin+cos(a)*y*s+sin(a)*z*s);
p.vel = x2*p.speed;
p.target = self;
p.tracer = invoker.LockedTarget;
}
else if ( player.cmd.buttons&BT_ALTATTACK )
{
// rockets ("tight wad" as UT calls it)
double step = 360/num;
a = 90;
s = (num>1)?6:0;
for ( int i=0; i<num; i++ )
{
p = Spawn("URocket",origin+cos(a)*y*s+sin(a)*z*s);
p.vel = x2*p.speed;
p.target = self;
p.tracer = invoker.LockedTarget;
a += step;
}
}
else
{
// rockets (wide spread)
double range = 3.6*(num-1);
double step = range/(num-1);
s = -range*0.5;
for ( int i=0; i<num; i++ )
{
p = Spawn("URocket",origin+sin(s)*y);
p.vel = (x2+sin(s)*y2).unit()*p.speed;
p.target = self;
p.tracer = invoker.LockedTarget;
s += step;
}
}
// lose lock-on
if ( invoker.LockedOn ) A_PlaySound("utrl/seeklost",CHAN_6);
invoker.LockedTarget = null;
invoker.LockedOn = false;
for ( int i=0; i<12; i++ )
{
let s = Spawn("UTViewSmoke",origin);
UTViewSmoke(s).ofs = (10,0,-2);
UTViewSmoke(s).vvel = (FRandom[Eightball](0,1.2),FRandom[Eightball](-.4,.4),FRandom[Eightball](-.4,.4));
s.target = self;
s.scale *= 1.8;
s.alpha *= 0.8;
}
}
// lock-on check
action void A_CheckTarget()
{
let t = ThinkerIterator.Create("Actor");
Actor a;
double closest = double.max;
invoker.LockedTarget = null;
while ( a = Actor(t.Next()) )
{
if ( !a.bSHOOTABLE || (a.Health <= 0) || a.bKilled || !a.bIsMonster || a.bCorpse || (a == self) || isTeammate(a) || !CheckSight(a) ) continue;
Vector3 viewdir = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
Vector3 reldir = level.Vec3Diff(Vec2OffsetZ(0,0,player.viewz),a.Vec2OffsetZ(0,0,a.pos.z+a.height*0.5));
double reldist = reldir.length();
if ( reldist > 2000 ) continue;
if ( reldir.unit() dot viewdir < 0.99 ) continue;
if ( reldist < closest )
{
closest = reldist;
invoker.LockedTarget = a;
}
}
if ( invoker.LockedTarget )
{
A_PlaySound("utrl/seeklock",CHAN_WEAPON,Dampener.Active(self)?.1:1.);
if ( !Dampener.Active(self) ) A_AlertMonsters();
}
else if ( invoker.LockedOn )
{
A_PlaySound("utrl/seeklost",CHAN_WEAPON,Dampener.Active(self)?.1:1.);
if ( !Dampener.Active(self) ) A_AlertMonsters();
}
if ( invoker.LockedTarget ) invoker.LockedOn = true;
}
Default
{
Tag "$T_EIGHTBALL";
Inventory.PickupMessage "$I_EIGHTBALL";
Weapon.UpSound "utrl/select";
Weapon.SlotNumber 5;
Weapon.SelectionOrder 5;
Weapon.SlotPriority 1;
Weapon.AmmoType "URocketAmmo";
Weapon.AmmoUse 1;
Weapon.AmmoType2 "URocketAmmo";
Weapon.AmmoUse2 1;
Weapon.AmmoGive 6;
UTWeapon.DropAmmo 3;
}
States
{
Spawn:
EBLP A -1;
Stop;
EBLP B -1;
Stop;
Select:
EBLS A 1 A_Raise(int.max);
Wait;
Ready:
EBLS ABCDEFGHIJKLMNO 1 A_WeaponReady(WRF_NOFIRE);
Ready2:
EBLS O 0 A_CheckReload();
EBLL A 0 A_PlaySound("utrl/load",CHAN_6,Dampener.Active(self)?.1:1.);
EBLL ABCDEFGHIJK 1 A_WeaponReady(WRF_NOFIRE);
EBLL L 0 A_PlaySound("utrl/rotate",CHAN_ITEM,Dampener.Active(self)?.01:.1);
EBLL LMNOPQRSTUVWXYZ[ 1 A_WeaponReady(WRF_NOFIRE);
EBLL Z 0; // force no tweening
EBLI A 0;
Goto Idle;
Idle:
#### # 0 A_Overlay(-9999,"Dummy");
EBLI AB 50;
Goto Idle+1;
Dummy:
TNT1 A 1
{
invoker.locktics = 0;
invoker.special1 = 0;
A_CheckReload();
A_WeaponReady(WRF_ALLOWRELOAD);
}
TNT1 A 1
{
A_CheckReload();
A_WeaponReady(WRF_ALLOWRELOAD);
invoker.locktics++;
if ( invoker.locktics > 42 )
{
invoker.locktics = 0;
A_CheckTarget();
}
}
Wait;
Reload:
#### # 5
{
A_Overlay(-9999,"Null");
A_PlaySound("utrl/load",CHAN_6,Dampener.Active(self)?.03:.3);
if ( invoker.bSingleRocket = !invoker.bSingleRocket )
A_Print(StringTable.Localize("$M_SINGLEROCKETON"));
else A_Print(StringTable.Localize("$M_SINGLEROCKETOFF"));
}
Goto Idle;
Fire:
AltFire:
// one is loaded already
#### # 1
{
A_Overlay(-9999,"Null");
A_LoadRocket(false);
}
#### # 3 A_JumpIf(!invoker.bAltFire&&invoker.bSingleRocket,"Release");
#### # 0 A_LoadedRefire(1);
Goto Release;
Loading:
EBLI A 0;
EBLL A 0 A_LoadRocket();
EBLL A 0 A_PlaySound("utrl/load",CHAN_6,Dampener.Active(self)?.1:1.);
EBLL ABCDEFGHIJK 1;
EBLL L 0 A_PlaySound("utrl/rotate",CHAN_ITEM,Dampener.Active(self)?.01:.1);
EBLL LMNOPQRSTUVWXYZ[ 1;
EBLL Z 0;
EBLI A 0;
EBLI A 1
{
if ( invoker.special1 >= 6 )
{
if ( sting_ehold )
{
invoker.special2 = 70;
return ResolveState("LoadHold");
}
else return ResolveState("Release");
}
return ResolveState(null);
}
EBLI A 0 A_LoadedRefire("Loading");
Goto Release;
LoadHold:
EBLI A 2;
LoadHoldL:
EBLI C 1 A_JumpIf(--invoker.special2<0,"Release");
EBLI C 0 A_LoadedRefire("LoadHoldL");
Goto Release;
Release:
EBLF A 0 A_FireRockets(invoker.special1);
EBLF ABCDEFGH 3;
EBLS S 0;
Goto Ready2;
Deselect:
EBLD A 1 A_Overlay(-9999,"Null");
EBLD BCDEFGHIJKL 1;
EBLD L 1 A_Lower(int.max);
Wait;
}
}