flak_m/zscript/translocator.zsc
Marisa the Magician 602a89cc68 1.2 update w/ GZDoom 4.9 features:
- Changeable player skins.
 - Ammo LEDs are now 1:1 with UT by using canvas textures.
 - Integrate some add-ons, including reskins.
 - Various fixes (some backported from Demolitionist).
 - Migrated from libeye to Gutamatics.
2022-11-05 23:59:16 +01:00

685 lines
15 KiB
Text

Class ModuleHitbox : Actor
{
Default
{
Radius 5;
Height 4;
+SHOOTABLE;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+NOBLOOD;
+NOTELEPORT;
}
override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle )
{
if ( master )
{
if ( inflictor ) master.vel += level.Vec3Diff(inflictor.pos,pos).unit()*damage*0.2;
else if ( source ) master.vel += level.Vec3Diff(source.pos,pos).unit()*damage*0.2;
master.vel.z = 5;
}
if ( !master || (master.target && ((master.target == source) || master.target.isTeammate(source))) ) return 0;
Health = 0;
master.bAMBUSH = true;
return 0;
}
override void Tick()
{
Super.Tick();
if ( !master )
{
Destroy();
return;
}
SetOrigin(master.pos,true);
if ( master.bMISSILE ) return;
let bi = BlockThingsIterator.Create(self,32);
while ( bi.Next() )
{
if ( !bi.Thing || (bi.Thing != master.target) ) continue;
if ( (Distance2D(bi.Thing)-bi.Thing.radius <= radius) && ((bi.Thing.pos.z <= pos.z+height) && (bi.Thing.pos.z+bi.Thing.height >= pos.z-height)) )
{
A_StartSound("misc/i_pkup",CHAN_ITEM);
master.Destroy();
return;
}
}
}
States
{
Spawn:
TNT1 A 10 A_AlertMonsters(0,AMF_TARGETEMITTER);
Wait;
Death:
TNT1 A -1;
Stop;
}
}
Class TranslocatorGlowLight : DynamicLight
{
Default
{
DynamicLight.Type "Point";
Args 255,255,255,40;
}
override void Tick()
{
Super.Tick();
if ( !target )
{
Destroy();
return;
}
SetOrigin(target.pos,true);
}
}
Class TranslocatorGlow : Actor
{
override void PostBeginPlay()
{
Super.PostBeginPlay();
let l = Spawn("TranslocatorGlowLight",pos);
l.target = self;
l.args[0] = fillcolor.r;
l.args[1] = fillcolor.g;
l.args[2] = fillcolor.b;
}
override void Tick()
{
Super.Tick();
if ( !target )
{
Destroy();
return;
}
SetOrigin(target.Vec3Offset(0,0,10),true);
}
Default
{
Radius 0.1;
Height 0;
+NOCLIP;
+NOGRAVITY;
+DONTSPLASH;
+FORCEXYBILLBOARD;
+NOTELEPORT;
RenderStyle "AddShaded";
StencilColor "FFFFFF";
Scale 0.5;
}
States
{
Spawn:
TGLO A -1 Bright;
Stop;
}
}
Class TranslocatorModule : Actor
{
Actor b;
bool alreadyhit;
Default
{
Radius 2;
Height 2;
Speed 16;
PROJECTILE;
-NOGRAVITY;
+USEBOUNCESTATE;
+SKYEXPLODE;
+HITTRACER;
+MOVEWITHSECTOR;
+CANBOUNCEWATER;
+BLOCKASPLAYER;
-ALLOWBOUNCEONACTORS;
+DONTBOUNCEONSHOOTABLES;
BounceType "Hexen";
BounceFactor 0.3;
WallBounceFactor 0.3;
Gravity 0.35;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
if ( !target )
{
Destroy();
return;
}
b = Spawn("ModuleHitbox",pos);
b.master = self;
A_StartSound("transloc/hum",CHAN_VOICE,CHANF_LOOPING,.5,2.);
}
override bool CanCollideWith( Actor other, bool passive )
{
return (other != tracer);
}
action void A_LightUp()
{
let l = Spawn("TranslocatorGlow",Vec3Offset(0,0,10));
l.target = self;
if ( !target || !target.player ) return;
Color gcol;
if ( deathmatch && (target.player.GetTeam() < teams.size()) ) gcol = teams[target.player.GetTeam()].mName;
else gcol = target.player.GetColor();
// maximize brightness
if ( (gcol.r+gcol.g+gcol.b) <= 0 ) gcol = "White";
else
{
int maxcomp = max(gcol.r,max(gcol.g,gcol.b));
int newr = int(gcol.r*(255./maxcomp));
int newg = int(gcol.g*(255./maxcomp));
int newb = int(gcol.b*(255./maxcomp));
gcol = Color(newr,newg,newb);
}
l.SetShade(gcol);
}
override void Tick()
{
Super.Tick();
if ( bAMBUSH && !Random[Transloc](0,40) )
{
A_StartSound("transloc/spark",CHAN_BODY,CHANF_OVERLAP);
int numpt = Random[Transloc](20,40);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Transloc](-1,1),FRandom[Transloc](-1,1),FRandom[Transloc](-1,1)).unit()*FRandom[Transloc](0.1,1.2);
let s = Spawn("UTSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[Transloc](128,192));
}
numpt = Random[Transloc](4,12);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Transloc](-1,1),FRandom[Transloc](-1,1),FRandom[Transloc](-1,1)).unit()*FRandom[Transloc](2,8);
let s = Spawn("UTSpark",pos);
s.vel = pvel;
}
}
}
States
{
Spawn:
TMOD A -1;
Stop;
Bounce:
TMOD A 0
{
if ( alreadyhit )
{
ClearBounce();
ExplodeMissile();
}
else
{
A_SetPitch(0);
A_StartSound("transloc/bounce");
if ( BlockingFloor || (tracer && (pos.z >= tracer.pos.z+tracer.height) && tracer.bACTLIKEBRIDGE) ) alreadyhit = true;
}
}
Goto Spawn;
Death:
TMOD A 0
{
A_SetPitch(0);
if ( tracer && !tracer.bACTLIKEBRIDGE )
{
SetOrigin(tracer.Vec2OffsetZ(0,0,pos.z),false);
vel.xy *= 0;
tracer = null;
bHITTRACER = false;
}
}
TMOD A 12;
TMOD B 8;
TMOD C -1 A_LightUp();
Stop;
}
}
Class OldTranslocatorModule : TranslocatorModule
{
States
{
Death:
TMOD A -1
{
A_SetPitch(0);
if ( tracer && !tracer.bACTLIKEBRIDGE )
{
SetOrigin(tracer.Vec2OffsetZ(0,0,pos.z),false);
vel.xy *= 0;
tracer = null;
bHITTRACER = false;
}
}
Stop;
}
}
Class TranslocatorAfterimageParticle : Actor
{
Default
{
RenderStyle "Add";
Scale 0.6;
Alpha 0.4;
+NOCLIP;
+NOGRAVITY;
+DONTSPLASH;
+NOTELEPORT;
}
override void Tick()
{
Super.Tick();
A_SetScale(.1+.2*alpha);
}
States
{
Spawn:
TPEF A 1 Bright A_FadeOut(.4/TICRATE);
Wait;
}
}
Class TranslocatorAfterimage : Actor
{
Array<Actor> particles;
Vector3 spreaddir;
Default
{
RenderStyle "AddStencil";
StencilColor "FF0000";
+NOCLIP;
+NOGRAVITY;
+DONTSPLASH;
+NOTELEPORT;
Radius 0.1;
Height 0;
Speed 1;
Alpha 0.1;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
if ( !target )
{
Destroy();
return;
}
scale = target.scale;
angle = target.angle-90;
roll = -90;
Vector3 x, y, z;
[x, y, z] = dt_CoordUtil.GetAxes(0,angle,roll);
int lump = Wads.CheckNumForFullname("models/TeleSoldier_a.3d");
String anivfile = Wads.ReadLump(lump);
int numframes = anivfile.ByteAt(0);
numframes |= anivfile.ByteAt(1)<<8;
int fsiz = anivfile.ByteAt(2);
fsiz |= anivfile.ByteAt(3)<<8;
particles.Resize(fsiz/4);
int cursor = 4;
for ( int i=0; i<fsiz/4; i++ )
{
int avert = anivfile.ByteAt(cursor++);
avert |= anivfile.ByteAt(cursor++)<<8;
avert |= anivfile.ByteAt(cursor++)<<16;
avert |= anivfile.ByteAt(cursor++)<<24;
int ax = ((avert&0x7ff)<<21),
ay = ((avert>>11)&0x7ff)<<21,
az = ((avert>>22)&0x3ff)<<22;
Vector3 ppos = (ax/2097152.-5.,ay/2097152.+1.25,az/4194304.);
ppos.xy *= 0.0625;
ppos.z *= 0.125;
particles[i] = Spawn("TranslocatorAfterimageParticle",level.Vec3Offset(pos,ppos.x*x+ppos.y*y+ppos.z*z+(0,0,30)));
particles[i].vel = (FRandom[Transloc](-.25,.25),FRandom[Transloc](-.25,.25),FRandom[Transloc](-.25,.25));
}
}
void A_Spread()
{
for ( int i=0; i<particles.Size(); i++ )
{
if ( !particles[i] ) continue;
particles[i].vel += spreaddir*speed*FRandom[Transloc](0.6,1.3);
}
}
States
{
Spawn:
TNT1 A 10;
TNT1 A 1 A_Spread();
Wait;
}
}
Class TranslocatorAmmo : Ammo
{
Default
{
Tag "$T_TRANSLOCATORAMMO";
Inventory.Amount 6;
Inventory.MaxAmount 6;
Ammo.BackpackAmount 0;
Ammo.BackpackMaxAmount 6;
}
}
Class Translocator : UTWeapon
{
Actor module;
double ammocharge;
override int, int, bool, bool GetClipAmount()
{
if ( !flak_transloc2k4 ) return Super.GetClipAmount();
return (ammocharge>0)?int(ammocharge*9.9):-1, -1, false, false;
}
override void Tick()
{
Super.Tick();
if ( !Owner ) return;
if ( flak_transloc2k4 )
{
AmmoType1 = "TranslocatorAmmo";
if ( !Ammo1 ) Ammo1 = NonIdioticAddAmmo(Owner,AmmoType1,6);
if ( Ammo1.Amount >= Ammo1.MaxAmount ) return;
if ( ammocharge >= 1. )
{
ammocharge = 0.;
Ammo1.Amount = min(Ammo1.Amount+1,Ammo1.MaxAmount);
}
else ammocharge = min(1.,ammocharge+0.4/TICRATE);
return;
}
if ( Ammo1 ) Ammo1.Destroy();
if ( AmmoType1 ) AmmoType1 = null;
}
action void A_ThrowModule()
{
Weapon weap = Weapon(invoker);
if ( !weap ) return;
A_StartSound("transloc/throw",CHAN_WEAPON);
invoker.FireEffect();
A_AlertMonsters();
Vector3 x, y, z;
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x-8*y-12*z);
let p = Spawn("TranslocatorModule",origin);
p.target = self;
p.angle = angle;
p.pitch = BulletSlope();
p.A_SetSize(radius);
Vector3 dir = dt_Utility.Vec3FromAngle(p.angle,p.pitch);
dir.z += 0.35*(1-abs(dir.z));
p.vel = dir*p.speed;
invoker.module = p;
}
action void A_ReturnModule()
{
Weapon weap = Weapon(invoker);
if ( !weap ) return;
A_StartSound("transloc/return",CHAN_WEAPON);
invoker.FireEffect();
A_AlertMonsters();
if ( invoker.module && invoker.module.bAMBUSH )
{
UTMainHandler.DoFlash(self,Color(255,255,255,255),50);
for ( int i=0; i<3; i++ )
A_StartSound("transloc/spark",CHAN_WEAPONMISC,CHANF_OVERLAP);
DamageMobj(invoker,self,int.max,'Telefrag',DMG_THRUSTLESS);
}
if ( invoker.module ) invoker.module.Destroy();
}
action void A_Translocate()
{
Weapon weap = Weapon(invoker);
if ( !weap ) return;
if ( !invoker.module )
{
invoker.FireEffect();
A_AlertMonsters();
A_StartSound("transloc/return",CHAN_WEAPON);
return;
}
// consume ammo if any
if ( weap.Ammo1 ) weap.Ammo1.Amount = max(0,weap.Ammo1.Amount-1);
// check if there's enough space
Vector3 oldpos = pos, newpos = invoker.module.pos;
double modulefloorz = invoker.module.floorz, moduleceilingz = invoker.module.ceilingz;
bool bBroken = invoker.module.bAMBUSH;
invoker.module.Destroy();
invoker.FireEffect();
A_AlertMonsters();
// squeeze down new z if ceiling is in the way
if ( (newpos.z+height > moduleceilingz) ) newpos.z = max(modulefloorz,moduleceilingz-height);
// temporarily disable telefragging for all allies
bool oldnotele[MAXPLAYERS];
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || !players[i].mo ) continue;
oldnotele[i] = players[i].mo.bNOTELEFRAG;
if ( !IsFriend(players[i].mo) ) continue;
players[i].mo.bNOTELEFRAG = true;
}
if ( Warp(self,newpos.x,newpos.y,newpos.z,flags:WARPF_ABSOLUTEPOSITION|WARPF_TESTONLY) && TeleportMove(newpos,true) )
{
SpawnTeleportFog(oldpos,true,false);
Vector3 diff = level.Vec3Diff(oldpos,newpos);
let a = Spawn("TranslocatorAfterimage",oldpos);
a.target = self;
TranslocatorAfterimage(a).spreaddir = diff.unit();
a.speed = (diff.length()/400)**0.5;
SpawnTeleportFog(newpos,false,false);
player.fov = min(175,player.desiredfov+60);
}
else A_StartSound("transloc/return",CHAN_WEAPON);
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || !players[i].mo ) continue;
players[i].mo.bNOTELEFRAG = oldnotele[i];
}
if ( bBroken )
{
UTMainHandler.DoFlash(self,Color(255,255,255,255),50);
for ( int i=0; i<3; i++ )
A_StartSound("transloc/spark",CHAN_WEAPONMISC,CHANF_OVERLAP);
DamageMobj(invoker,self,int.max,'Telefrag',DMG_THRUSTLESS);
}
}
override void OnDestroy()
{
Super.OnDestroy();
if ( module ) module.Destroy();
}
Default
{
Tag "$T_TRANSLOCATOR";
Inventory.PickupMessage "$I_TRANSLOCATOR";
Weapon.SlotNumber 1;
Weapon.SelectionOrder 10;
+WEAPON.NO_AUTO_SWITCH;
+WEAPON.CHEATNOTWEAPON;
+WEAPON.AMMO_OPTIONAL;
+WEAPON.ALT_AMMO_OPTIONAL;
}
States
{
Spawn:
TLCP A -1;
Stop;
TLCP B -1;
Stop;
Select:
TLCS A 1 A_Raise(int.max);
Wait;
Ready:
TLCS A 0 A_JumpIf(invoker.module,"Ready2");
TLCS ABCDEFGHIJKL 1 A_WeaponReady(WRF_NOFIRE);
TLCS L 0 A_JumpIf(invoker.module,"Idle");
Goto Idle2;
Ready2:
TLD2 GFEDCBA 2 A_WeaponReady(WRF_NOFIRE);
Idle:
TLCI A 0 A_Overlay(-9999,"Dummy"); // little hackeroo to make this more responsive
TLCI AB 25 A_JumpIf(!invoker.module,"PickedUp");
Goto Idle+1;
Dummy:
TNT1 A 2
{
A_WeaponReady();
return A_JumpIf(!invoker.module,"Null");
}
Wait;
Idle2:
TLI2 ABCDEFGHIJKLMNOPQRS 2 A_WeaponReady(WRF_NOSECONDARY);
Loop;
PickedUp:
TLCI A 5;
TLI2 A 0;
Goto Idle2;
Fire:
TLCF A 0 A_JumpIf(invoker.module||(invoker.Ammo1&&invoker.Ammo1.Amount<=0),"Return");
TLCF A 0 A_ThrowModule();
TLCF ABCDEFGH 1;
TLCF IJKLMNOPQRS 1 A_WeaponReady(WRF_NOPRIMARY);
Goto Idle;
Return:
TLCF A 0 A_ReturnModule();
TLCF ABCDEFGH 1;
Goto Idle2;
AltFire:
TLCT A 0 A_Translocate();
TLCT ABCDEFGHIJKLM 1;
Goto Idle2;
Deselect:
TLCD A 0 A_JumpIf(invoker.module,"Deselect2");
TLCD ABCDEFG 1;
TLCD G 1 A_Lower(int.max);
Wait;
Deselect2:
TLD2 ABCDEFG 1;
TLD2 G 1 A_Lower(int.max);
Wait;
}
}
Class OldTranslocator : Translocator
{
action void A_ThrowModuleOld()
{
Weapon weap = Weapon(invoker);
if ( !weap ) return;
A_StartSound("transloc/throw",CHAN_WEAPON);
invoker.FireEffect();
A_AlertMonsters();
Vector3 x, y, z;
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = (pos.x,pos.y,player.viewz)+15.0*x-10.0*y-4.0*z;
let p = Spawn("OldTranslocatorModule",origin);
p.target = self;
p.angle = angle;
p.pitch = BulletSlope();
p.A_SetSize(radius);
Vector3 dir = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch));
dir.z += 0.35*(1-abs(dir.z));
p.vel = dir*p.speed;
invoker.module = p;
}
States
{
Spawn:
TLCP A -1;
Stop;
TLCP B -1;
Stop;
Select:
TLCS A 1 A_Raise(int.max);
Wait;
Ready:
TLCS A 0 A_JumpIf(invoker.module,"Ready2");
TLCS ABCDEFGHIJKLMNO 1 A_WeaponReady(WRF_NOFIRE);
TLCS O 0 A_JumpIf(invoker.module,"Idle");
Goto Idle2;
Ready2:
TLS2 ABCDEFGHIJKLMNO 1 A_WeaponReady(WRF_NOFIRE);
Goto Idle;
Idle:
TLCI ABCDEFGHIJKLMNOPQRSTUVWXYZ 1
{
if ( !invoker.module )
return ResolveState("PickedUp");
A_WeaponReady();
return ResolveState(null);
}
TLI2 ABCDEFGHIJKLMNOPQRSTUVWXYZ 1
{
if ( !invoker.module )
return ResolveState("PickedUp");
A_WeaponReady();
return ResolveState(null);
}
TLI3 ABCDEFGH 1
{
if ( !invoker.module )
return ResolveState("PickedUp");
A_WeaponReady();
return ResolveState(null);
}
Loop;
Idle2:
TLI4 ABCDEFGHIJKLMNOPQRSTUVWXYZ 1 A_WeaponReady(WRF_NOSECONDARY);
TLI5 ABCDEFGHIJKLMNOPQRSTUVWXYZ 1 A_WeaponReady(WRF_NOSECONDARY);
TLI6 ABCDEFGH 1 A_WeaponReady(WRF_NOSECONDARY);
TLI6 H 0;
Loop;
PickedUp:
TLCI A 5;
TLR2 A 0 A_ReturnModule();
Goto Return2;
Fire:
TLCF A 0 A_JumpIf(invoker.module||(invoker.Ammo1&&invoker.Ammo1.Amount<=0),"Return");
TLCF ABCDEFGHIJK 1;
TLCF K 0 A_ThrowModuleOld();
TLCF LMNOPQRST 1 A_WeaponReady(WRF_NOPRIMARY);
Goto Idle;
Return:
TLCR ABCDEFGHIJKLMNOP 1;
TLCR Q 1 A_ReturnModule();
TLCR RST 1;
Return2:
TLR2 ABCDEFGHIJKLMNOPQRST 1;
TLR2 T 0;
Goto Idle2;
AltFire:
TLCT ABCDEFGHIJK 1;
TLCT L 1 A_Translocate();
TLCT MNOPQRST 1;
Goto PickedUp;
Deselect:
TLCD A 0 A_JumpIf(invoker.module,"Deselect2");
TLCD ABCDEFGIJK 1;
TLCD K 1 A_Lower(int.max);
Wait;
Deselect2:
TLD2 ABCDEFGHIJK 1;
TLD2 K 1 A_Lower(int.max);
Wait;
}
}