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 { Mixin UTMissileFix; 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= 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 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_Utility.GetAxes(angle,0,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>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; i0)?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_Utility.GetAxes(angle,pitch,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