Class ModuleHitbox : Actor { Default { Radius 5; Height 4; +SHOOTABLE; +NOGRAVITY; +NOCLIP; +DONTSPLASH; +NOBLOOD; } override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle ) { if ( target ) { if ( inflictor ) target.vel += level.Vec3Diff(inflictor.pos,pos).unit()*damage*0.2; else if ( source ) target.vel += level.Vec3Diff(source.pos,pos).unit()*damage*0.2; target.vel.z = 5; } if ( !target || (target.target && ((target.target == source) || target.target.isTeammate(source))) ) return 0; target.bAMBUSH = true; return 0; } override void Tick() { Super.Tick(); if ( !target ) { Destroy(); return; } SetOrigin(target.pos,true); if ( target.bMISSILE ) return; let bi = BlockThingsIterator.Create(self,32); while ( bi.Next() ) { if ( !bi.Thing || (bi.Thing != target.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_PlaySound("misc/i_pkup"); target.Destroy(); return; } } } States { Spawn: TNT1 A 10 A_AlertMonsters(0,AMF_TARGETEMITTER); Wait; } } 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; 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; 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.target = self; A_PlaySound("transloc/hum",CHAN_VOICE,0.5,true,2.0); } 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_PlaySound("transloc/spark"); int numpt = Random[Transloc](20,40); for ( int i=0; i= 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_PlaySound("transloc/throw",CHAN_WEAPON); invoker.FireEffect(); UTMainHandler.DoSwing(self,(FRandom[Translocator](-0.2,0.4),FRandom[Translocator](-0.2,0.7)),2,-0.3,3,SWING_Spring,2,3); A_AlertMonsters(); Vector3 x, y, z; [x, y, z] = dt_Matrix4.GetAxes(pitch,angle,roll); Vector3 origin = (pos.x,pos.y,player.viewz)+10.0*x-8.0*y-12.0*z; let p = Spawn("TranslocatorModule",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; } action void A_ReturnModule() { Weapon weap = Weapon(invoker); if ( !weap ) return; A_PlaySound("transloc/return",CHAN_WEAPON); invoker.FireEffect(); UTMainHandler.DoSwing(self,(FRandom[Translocator](-0.2,-0.4),FRandom[Translocator](-0.2,0.7)),3,-1,2,SWING_Spring,2,2); A_AlertMonsters(); if ( invoker.module && invoker.module.bAMBUSH ) { UTMainHandler.DoFlash(self,Color(255,255,255,255),50); A_PlaySound("transloc/spark",CHAN_WEAPON); 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_PlaySound("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); 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); for ( int i=0; i<40; i++ ) { 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_PlaySound("transloc/return",CHAN_WEAPON); if ( bBroken ) { UTMainHandler.DoFlash(self,Color(255,255,255,255),50); A_PlaySound("transloc/spark",CHAN_WEAPON); DamageMobj(invoker,self,int.max,'Telefrag',DMG_THRUSTLESS); } } override void OnDestroy() { Super.OnDestroy(); if ( module ) module.Destroy(); } Default { Tag "Translocator"; Inventory.PickupMessage "You got the Translocator Source Module."; 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; } }