flak_m/zscript/eightball.zsc
Marisa Kirisame fb96c7523e Portal awareness adjustments to various vector operations.
Got rid of the deprecated Matrix4.GetAxes method. Next step is to get rid of more stuff by migrating to libeye.
Mirrored Translocator model so it shows the actually detailed side. At some point in UT's development it got flipped around for some reason.
Weapon code cleanup (most noticeable on states).
Backported scope shader from Doomreal.
Added optional "dummied out" Sniper zoom sounds from a dubious source.
2019-09-28 20:06:51 +02:00

631 lines
15 KiB
Text

Class UTRocketAmmo : Ammo
{
Default
{
Tag "$T_ROCKETAMMO";
Inventory.PickupMessage "$I_ROCKETAMMO";
Inventory.Amount 12;
Inventory.MaxAmount 48;
Ammo.BackpackAmount 12;
Ammo.BackpackMaxAmount 96;
Ammo.DropAmount 3;
}
States
{
Spawn:
RPAK A -1;
Stop;
}
}
// There was no single rocket ammo in UT, so this one is also just improvised like the Redeemer ammo pickup
Class UTRocketAmmo2 : UTRocketAmmo
{
Default
{
Tag "$T_ROCKETAMMO2";
Inventory.PickupMessage "$I_ROCKETAMMO2";
Inventory.Amount 1;
Ammo.DropAmount 1;
+INVENTORY.IGNORESKILL;
}
States
{
Spawn:
RCKT A -1;
Stop;
}
}
Class RocketLight : DynamicLight
{
Default
{
DynamicLight.Type "Point";
Args 255,224,128,32;
}
override void Tick()
{
Super.Tick();
if ( !target )
{
Destroy();
return;
}
SetOrigin(target.pos,true);
}
}
Class UTRocketTrail : Actor
{
Default
{
RenderStyle "Add";
Radius 0.1;
Height 0;
+NOBLOCKMAP;
+NOGRAVITY;
+DONTSPLASH;
+FORCEXYBILLBOARD;
+NOTELEPORT;
Scale 0.7;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
let l = Spawn("RocketLight",pos);
l.target = self;
}
override void Tick()
{
Super.Tick();
if ( !target || target.InStateSequence(target.CurState,target.FindState("Death")) )
{
Destroy();
return;
}
SetOrigin(target.pos,true);
}
States
{
Spawn:
RFLA A -1 Bright;
Stop;
}
}
Class RocketExplLight : PaletteLight
{
Default
{
Args 0,0,0,120;
ReactionTime 20;
}
}
Class UTRocket : 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("UTRocketTrail",pos);
l.target = self;
A_PlaySound("utrl/fly",CHAN_VOICE,1.0,true,2.5,pitch:1.5625);
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(0.75);
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 B 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,160);
SSMX ABCDEFGHIJ 2 Bright;
Stop;
}
}
Class UTGrenade : UTRocket
{
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,140);
Goto Super::Death+1;
}
}
Class UTRocketLauncher : UTWeapon
{
bool LockedOn;
Actor LockedTarget;
TextureID lockontex;
int locktics;
bool bSingleRocket;
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);
}
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);
else A_PlaySound("utrl/fire",CHAN_WEAPON);
invoker.FireEffect();
UTMainHandler.DoFlash(self,Color(64,255,0,0),1);
if ( self is 'UTPlayer' )
UTPlayer(self).PlayAttacking3();
A_AlertMonsters();
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.4,-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 = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3*y-3*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("UTGrenade",level.Vec3Offset(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("UTRocket",level.Vec3Offset(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("UTRocket",level.Vec3Offset(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("UTRocket",level.Vec3Offset(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,3,-3);
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);
else if ( invoker.LockedOn ) A_PlaySound("utrl/seeklost",CHAN_WEAPON);
if ( invoker.LockedTarget ) invoker.LockedOn = true;
}
Default
{
Tag "$T_EIGHTBALL";
Inventory.PickupMessage "$I_EIGHTBALL";
Weapon.UpSound "utrl/select";
Weapon.SlotNumber 9;
Weapon.SelectionOrder 1;
Weapon.AmmoType "UTRocketAmmo";
Weapon.AmmoUse 1;
Weapon.AmmoType2 "UTRocketAmmo";
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 ABCDEFGHIJKLMNOPQRST 1 A_WeaponReady(WRF_NOFIRE);
Idle:
EBLI A 1
{
invoker.locktics = 0;
invoker.special1 = 0;
A_CheckReload();
A_WeaponReady(WRF_ALLOWRELOAD);
}
EBLI A 1
{
A_CheckReload();
A_WeaponReady(WRF_ALLOWRELOAD);
invoker.locktics++;
if ( invoker.locktics > 42 )
{
invoker.locktics = 0;
A_CheckTarget();
}
}
Wait;
Reload:
EBLI A 5
{
A_PlaySound("utrl/load",CHAN_6,0.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
EBLI A 1 A_LoadRocket(false);
EBLI A 2 A_JumpIf((!invoker.bAltFire&&invoker.bSingleRocket)||(invoker.Ammo1.Amount<=0)||!(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)),"FireOne");
EBLI A 0; // no tween
// load two
EBR1 A 0 A_PlaySound("utrl/rotate",CHAN_6,0.1);
EBR1 ABCDEFG 2 A_JumpIf(!(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)),"FireOne");
EBL2 A 0 A_PlaySound("utrl/load",CHAN_6);
EBL2 ABCDEFG 3 A_JumpIf(!(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)),"FireOne");
EBR2 A 0
{
A_LoadRocket();
return A_JumpIf((invoker.Ammo1.Amount<=0)||!(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)),"FireTwo");
}
// load three
EBR2 A 0 A_PlaySound("utrl/rotate",CHAN_6,0.1);
EBR2 ABCDEFG 2 A_JumpIf(!(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)),"FireTwo");
EBL3 A 0 A_PlaySound("utrl/load",CHAN_6);
EBL3 ABCDEFG 3 A_JumpIf(!(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)),"FireTwo");
EBR3 A 0
{
A_LoadRocket();
return A_JumpIf((invoker.Ammo1.Amount<=0)||!(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)),"FireThree");
}
// load four
EBR3 A 0 A_PlaySound("utrl/rotate",CHAN_6,0.1);
EBR3 ABCDEFG 2 A_JumpIf(!(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)),"FireThree");
EBL4 A 0 A_PlaySound("utrl/load",CHAN_6);
EBL4 ABCDEFG 3 A_JumpIf(!(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)),"FireThree");
EBR4 A 0
{
A_LoadRocket();
return A_JumpIf((invoker.Ammo1.Amount<=0)||!(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)),"FireFour");
}
// load five
EBR4 A 0 A_PlaySound("utrl/rotate",CHAN_6,0.1);
EBR4 ABCDEFG 2 A_JumpIf(!(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)),"FireFour");
EBL5 A 0 A_PlaySound("utrl/load",CHAN_6);
EBL5 ABCDEFG 3 A_JumpIf(!(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)),"FireFour");
EBR5 A 0
{
A_LoadRocket();
return A_JumpIf((invoker.Ammo1.Amount<=0)||!(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)),"FireFive");
}
// load six
EBR5 A 0 A_PlaySound("utrl/rotate",CHAN_6,0.1);
EBR5 ABCDEFG 2 A_JumpIf(!(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)),"FireFive");
EBL6 A 0 A_PlaySound("utrl/load",CHAN_6);
EBL6 ABCDEF 3 A_JumpIf(!(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)),"FireFive");
EBL6 F 0 A_LoadRocket();
Goto FireSix;
FireOne:
EBF1 A 0 A_FireRockets(1);
EBF1 ABCDEFGH 2;
EBL1 A 0 A_CheckReload();
EBL1 A 2 A_PlaySound("utrl/load",CHAN_6);
EBL1 BCDEFG 2;
EBLI A 0;
Goto Idle;
FireTwo:
EBF2 A 0 A_FireRockets(2);
EBF2 ABCDEFGHIJK 2;
EBL1 A 0 A_CheckReload();
EBL1 A 2 A_PlaySound("utrl/load",CHAN_6);
EBL1 BCDEFG 2;
EBLI A 0;
Goto Idle;
FireThree:
EBF3 A 0 A_FireRockets(3);
EBF3 ABCDEFGHIJ 2;
EBL1 A 0 A_CheckReload();
EBL1 A 2 A_PlaySound("utrl/load",CHAN_6);
EBL1 BCDEFG 2;
EBLI A 0;
Goto Idle;
FireFour:
EBF4 A 0 A_FireRockets(4);
EBF4 ABCDEFGHIJK 2;
EBL1 A 0 A_CheckReload();
EBL1 A 2 A_PlaySound("utrl/load",CHAN_6);
EBL1 BCDEFG 2;
EBLI A 0;
Goto Idle;
FireFive:
EBF5 A 0 A_FireRockets(5);
EBF5 ABCDEFGHIJKLM 2;
EBL1 A 0 A_CheckReload();
EBL1 A 2 A_PlaySound("utrl/load",CHAN_6);
EBL1 BCDEFG 2;
EBLI A 0;
Goto Idle;
FireSix:
EBF6 A 0 A_FireRockets(6);
EBF6 ABCDEFGHIJKLMNOP 2;
EBL1 A 0 A_CheckReload();
EBL1 A 2 A_PlaySound("utrl/load",CHAN_6);
EBL1 BCDEFG 2;
EBLI A 0;
Goto Idle;
Deselect:
EBLD ABCDEFGHIJK 1;
EBLD K 1 A_Lower(int.max);
Wait;
}
}