flak_m/zscript/utcommon.zsc
Marisa Kirisame 957f976b29 Made visual recoil toggleable (and with configurable strength, too).
Cleaned up some code (mostly related to server CVars).
Proper angle interpolation.
Snap player to floor when walking down steps like UE does (looks kinda twitchy due to lack of view height smoothing).
2018-09-24 15:00:02 +02:00

1631 lines
42 KiB
Text

Class UTPlayer : DoomPlayer
{
bool lastground;
int lastgroundtic;
double lastvelz, prevvelz;
transient CVar footsteps;
Vector2 acceleration;
Vector3 acceleration3;
int last_fm, last_sm;
int last_fm_tap, last_sm_tap;
int last_tap_fm, last_tap_sm;
int last_jump_held;
const groundspeed = 400.;
const baseaccelrate = 2048.;
const walkfactor = 0.3;
const utaircontrol = 0.35;
const groundspeed_doomish = 600.;
const terminalvelocity = 2500.;
const dodgez = 210.;
const utjumpz = 325.;
Default
{
Player.StartItem "Enforcer";
Player.StartItem "ImpactHammer";
Player.StartItem "MiniAmmo", 30;
Player.DamageScreenColor "FF 00 00";
Player.ViewHeight 46;
Player.GruntSpeed 20;
}
// Have to modify the give cheat to handle UT armor
override void CheatGive( String name, int amount )
{
if ( PlayerNumber() != consoleplayer )
A_Log(String.Format("%s is a cheater: give %s\n",player.GetUserName(),name));
if ( !player.mo || (player.health <= 0) ) return;
int giveall = ALL_NO;
if ( name ~== "all" ) giveall = ALL_YES;
else if (name ~== "everything") giveall = ALL_YESYES;
if ( name ~== "health" )
{
if ( amount > 0 )
{
health += amount;
player.health = health;
}
else player.health = health = GetMaxHealth(true);
}
if ( giveall || (name ~== "backpack") )
{
// Select the correct type of backpack based on the game
let type = (class<Inventory>)(gameinfo.backpacktype);
if ( type ) GiveInventory(type,1,true);
if ( !giveall ) return;
}
if ( giveall || (name ~== "ammo") )
{
// Find every unique type of ammo. Give it to the player if
// he doesn't have it already, and set each to its maximum.
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
let type = (class<Ammo>)(AllActorClasses[i]);
if ( !type || (type.GetParentClass() != "Ammo") )
continue;
// Only give if it's for a valid weapon, unless using "give everything"
bool isvalid = false;
for ( int j=0; j<AllActorClasses.Size(); j++ )
{
let type2 = (class<Weapon>)(AllActorClasses[j]);
if ( !type2 ) continue;
let rep = GetReplacement(type2);
if ( (rep != type2) && !(rep is "DehackedPickup") ) continue;
readonly<Weapon> weap = GetDefaultByType(type2);
if ( !player.weapons.LocateWeapon(type2) || (weap.bCheatNotWeapon && (giveall != ALL_YESYES)) ) continue;
if ( (weap.AmmoType1 == type) || (weap.AmmoType2 == type) )
{
isvalid = true;
break;
}
}
if ( !isvalid ) continue;
let ammoitem = FindInventory(type);
if ( !ammoitem )
{
ammoitem = Inventory(Spawn(type));
ammoitem.AttachToOwner(self);
ammoitem.Amount = ammoitem.MaxAmount;
}
else if ( ammoitem.Amount < ammoitem.MaxAmount )
ammoitem.Amount = ammoitem.MaxAmount;
}
if ( !giveall ) return;
}
if ( giveall || (name ~== "armor") )
{
// Doom Tournament just gives the player a shield belt and maximum bonuses
let belt = Inventory(Spawn("UTShieldBelt"));
if ( !belt.CallTryPickup(self) ) belt.Destroy();
let bonus = Inventory(Spawn("UTArmorBonus"));
bonus.Amount = bonus.MaxAmount;
if ( !bonus.CallTryPickup(self) ) bonus.Destroy();
level.total_items -= 2; // spawning them in raises item count
if ( !giveall ) return;
}
if ( giveall || (name ~== "keys") )
{
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
if ( !(AllActorClasses[i] is "Key") ) continue;
let keyitem = GetDefaultByType(AllActorClasses[i]);
if ( keyitem.special1 )
{
let item = Inventory(Spawn(AllActorClasses[i]));
if ( !item.CallTryPickup(self) ) item.Destroy();
}
}
if ( !giveall ) return;
}
if ( giveall || (name ~== "weapons") )
{
let savedpending = player.PendingWeapon;
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
let type = (class<Weapon>)(AllActorClasses[i]);
if ( !type || (type == "Weapon") ) continue;
// Don't give replaced weapons unless the replacement was done by Dehacked.
let rep = GetReplacement(type);
if ( (rep == type) || (rep is "DehackedPickup") )
{
// Give the weapon only if it is set in a weapon slot.
if ( !player.weapons.LocateWeapon(type) ) continue;
readonly<Weapon> def = GetDefaultByType(type);
if ( (giveall == ALL_YESYES) || !def.bCheatNotWeapon )
GiveInventory(type,1,true);
}
}
player.PendingWeapon = savedpending;
if ( !giveall ) return;
}
if ( giveall || (name ~== "artifacts") )
{
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
let type = (class<Inventory>)(AllActorClasses[i]);
if ( !type ) continue;
let def = GetDefaultByType (type);
if ( def.Icon.isValid() && (def.MaxAmount > 1) &&
!(type is "PuzzleItem") && !(type is "Powerup") && !(type is "Ammo") && !(type is "Armor"))
{
// Do not give replaced items unless using "give everything"
if ( (giveall == ALL_YESYES) || (GetReplacement(type) == type) )
GiveInventory(type,(amount<=0)?def.MaxAmount:amount,true);
}
}
if ( !giveall ) return;
}
if ( giveall || (name ~== "puzzlepieces") )
{
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
let type = (class<PuzzleItem>)(AllActorClasses[i]);
if ( !type ) continue;
let def = GetDefaultByType(type);
if ( !def.Icon.isValid() ) continue;
// Do not give replaced items unless using "give everything"
if ( (giveall == ALL_YESYES) || (GetReplacement(type) == type) )
GiveInventory(type,(amount<=0)?def.MaxAmount:amount,true);
}
if ( !giveall ) return;
}
if ( giveall ) return;
let type = name;
if ( !type )
{
if ( PlayerNumber() == consoleplayer )
A_Log(String.Format("Unknown item \"%s\"\n",name));
}
else GiveInventory(type,amount,true);
}
override void Tick()
{
Super.Tick();
if ( !player || (player.mo != self) ) return;
if ( !footsteps ) footsteps = CVar.GetCVar('flak_footsteps',players[consoleplayer]);
if ( !footsteps.GetBool() ) return;
double ang = level.time/(20*TICRATE/35.)*360.;
bool forcefootstep = false;
if ( player.onground && !bNoGravity && !lastground && (waterlevel < 3) )
{
player.jumptics = 0;
if ( lastvelz < -8 )
{
double vol = clamp((-lastvelz-8)*0.05,0.01,1.0);
if ( ((waterlevel > 0) || GetFloorTerrain().IsLiquid) && !bOnMobj ) A_PlaySound("ut/wetsplash",CHAN_AUTO,vol);
else A_PlaySound("*uland",CHAN_AUTO,vol);
}
else forcefootstep = true;
}
if ( forcefootstep || ((abs(sin(ang)) >= 1.0) && player.onground && lastground && (player.jumptics == 0) && (player.cmd.forwardmove || player.cmd.sidemove) && (waterlevel < 3)) )
{
double vol = abs(vel.xy.length())*0.03;
if ( forcefootstep ) vol = clamp(-lastvelz*0.05,0.01,1.0);
if ( (waterlevel > 0) || GetFloorTerrain().IsLiquid && !bOnMobj ) A_PlaySound("ut/playerfootstepwet",CHAN_5,vol);
else A_PlaySound("ut/playerfootstep",CHAN_5,vol);
}
lastground = player.onground;
lastvelz = prevvelz;
prevvelz = vel.z;
}
double FrictionToUnreal()
{
double fin = GetFriction();
if ( fin >= 1.0 ) return 0.0;
return 734.2969*fin*fin-1485.0868*fin+750.7899;
}
override void MovePlayer()
{
if ( !flak_utmovement || !player || (player.mo != self) )
{
Super.MovePlayer();
return;
}
bNODROPOFF = false;
UserCmd cmd = player.cmd;
if ( player.turnticks )
{
player.turnticks--;
Angle += (180./TURN180_TICKS);
}
else Angle += cmd.yaw*(360./65536.);
player.onground = (pos.z <= floorz) || bOnMobj || bMBFBouncer || (player.cheats & CF_NOCLIP2);
if ( player.onground ) lastgroundtic = gametic;
if ( !player.onground && (abs(pos.z-floorz) <= maxdropoffheight) && (player.jumptics == 0) )
{
SetOrigin(Vec2OffsetZ(0,0,floorz),true);
player.onground = true;
}
double friction = FrictionToUnreal();
double fs = TweakSpeeds(1.0,0.0);
if ( !flak_doomspeed )
{
if ( cmd.buttons&BT_SPEED ) fs *= walkfactor;
}
else fs *= max(abs(cmd.forwardmove/12800.),abs(cmd.sidemove/10240.));
if ( CanCrouch() && (player.crouchfactor != -1) ) fs *= player.crouchfactor;
acceleration = rotatevector((cmd.forwardmove,-cmd.sidemove),angle);
double accelrate = baseaccelrate*fs;
Vector2 dodge = (0,0);
int fm = cmd.forwardmove;
int sm = cmd.sidemove;
if ( fm )
{
int clk = abs(gametic-last_fm_tap);
if ( (clk < 8) && (last_fm*fm == 0) && (last_tap_fm*fm>0) )
dodge += RotateVector((fm,0),angle).unit();
if ( !last_fm && (last_jump_held < gametic-1) )
{
last_fm_tap = gametic;
last_tap_fm = fm;
}
}
last_fm = fm;
if ( sm )
{
int clk = abs(gametic-last_sm_tap);
if ( (clk < 8) && (last_sm*sm == 0) && (last_tap_sm*sm>0) )
dodge += RotateVector((0,-sm),angle).unit();
if ( !last_sm && (last_jump_held < gametic-1) )
{
last_sm_tap = gametic;
last_tap_sm = sm;
}
}
last_sm = sm;
if ( !bNoGravity && player.onground )
{
if ( !waterlevel && (dodge.length() > 0) )
{
if ( flak_doomspeed ) vel += dodge.unit()*(groundspeed_doomish*1.5)/TICRATE;
else vel += dodge.unit()*(groundspeed*1.5)/TICRATE;
vel.z += dodgez/TICRATE;
bOnMobj = false;
if ( !(player.cheats&CF_PREDICTING) )
A_PlaySound("*jump",CHAN_BODY);
if ( player.cheats & CF_REVERTPLEASE )
{
player.cheats &= ~CF_REVERTPLEASE;
player.camera = player.mo;
}
player.vel *= 0;
player.jumptics = -2;
}
else
{
if ( flak_nowalkdrop )
bNODROPOFF = ((acceleration.length() > double.epsilon) && (cmd.buttons&BT_SPEED));
// Hook in Unreal physics
Vector2 dir = (0,0);
if ( vel.xy.length() > double.epsilon ) dir = vel.xy.unit();
double doomfriction = clamp(GetFriction()/ORIG_FRICTION,0.0,1.0);
if ( acceleration.length() <= double.epsilon )
{
Vector2 oldvel = vel.xy;
vel.xy = vel.xy - (2 * dir) * vel.xy.length() * friction/TICRATE;
if ( oldvel dot vel.xy < 0.0 ) vel.xy *= 0;
}
else
{
Vector2 acceldir = acceleration.unit();
acceleration = acceldir * Min(acceleration.length(), accelrate/TICRATE);
vel.xy = vel.xy - (dir - acceldir) * vel.xy.length() * friction/TICRATE;
}
vel.xy = vel.xy + acceleration/TICRATE;
double maxvel;
if ( flak_doomspeed ) maxvel = groundspeed_doomish/TICRATE;
else maxvel = groundspeed/TICRATE;
maxvel *= fs*doomfriction;
if ( vel.xy.length() > maxvel ) vel.xy = vel.xy.unit()*maxvel;
if ( !(player.cheats & CF_PREDICTING) )
{
if ( acceleration.length() <= double.epsilon ) PlayIdle();
else PlayRunning();
}
player.vel = vel.xy;
}
}
else if ( !bNoGravity && !waterlevel )
{
// air acceleration when falling
float maxaccel = accelrate/TICRATE;
if ( vel.xy.length() < (40./TICRATE) )
maxaccel += (40.-vel.xy.length())/TICRATE;
if ( acceleration.length() > maxaccel )
acceleration = acceleration.unit()*maxaccel;
Vector2 dir = (0,0);
if ( vel.xy.length() > double.epsilon ) dir = vel.xy.unit();
if ( acceleration.length() > double.epsilon )
{
Vector2 acceldir = acceleration.unit();
acceleration = acceldir * Min(acceleration.length(), accelrate/TICRATE);
}
acceleration *= flak_doomaircontrol?level.aircontrol:utaircontrol;
double maxvel;
if ( flak_doomspeed ) maxvel = (groundspeed_doomish*fs)/TICRATE;
else maxvel = (groundspeed*fs)/TICRATE;
// if new velocity is higher than ground speed, steer but don't increase it
if ( (vel.xy+acceleration/TICRATE).length() > maxvel )
{
double vsiz = vel.xy.length();
vel.xy = (vel.xy+acceleration/TICRATE).unit()*vsiz;
}
else vel.xy = vel.xy+acceleration/TICRATE;
if ( vel.length() > terminalvelocity/TICRATE ) vel = vel.unit()*(terminalvelocity/TICRATE);
player.vel *= 0;
player.jumptics = -2;
}
else
{
// swimming uses standard physics, otherwise things break
double forwardmove, sidemove;
double bobfactor;
double friction, movefactor;
double fm, sm;
[friction, movefactor] = GetFriction();
bobfactor = (friction<ORIG_FRICTION)?movefactor:ORIG_FRICTION_FACTOR;
fm = cmd.forwardmove;
sm = cmd.sidemove;
[fm, sm] = TweakSpeeds(fm,sm);
fm *= Speed/256;
sm *= Speed/256;
if ( CanCrouch() && (player.crouchfactor != 1) )
{
fm *= player.crouchfactor;
sm *= player.crouchfactor;
bobfactor *= player.crouchfactor;
}
forwardmove = fm*movefactor*(35/TICRATE);
sidemove = sm*movefactor*(35/TICRATE);
if ( forwardmove )
{
Bob(Angle, cmd.forwardmove*bobfactor/256.,true);
ForwardThrust(forwardmove,Angle);
}
if ( sidemove )
{
let a = Angle-90;
Bob(a,cmd.sidemove*bobfactor/256.,false);
Thrust(sidemove,a);
}
if ( !(player.cheats&CF_PREDICTING) && ((forwardmove != 0) || (sidemove != 0)) )
PlayRunning();
}
if ( player.cheats & CF_REVERTPLEASE )
{
player.cheats &= ~CF_REVERTPLEASE;
player.camera = player.mo;
}
}
override void CheckJump()
{
if ( !flak_utmovement || !player || (player.mo != self) )
{
Super.CheckJump();
return;
}
if ( player.cmd.buttons&BT_JUMP )
{
if ( player.crouchoffset ) player.crouching = 1;
else if ( waterlevel >= 2 ) Vel.z = 4*Speed;
else if ( bNoGravity ) Vel.z = 3.;
else if ( level.IsJumpingAllowed() && player.onground && (player.jumpTics == 0) && (last_jump_held < gametic-1) )
{
double jumpvelz;
if ( flak_doomspeed ) jumpvelz = jumpz;
else jumpvelz = utjumpz/TICRATE;
double jumpfac = 0;
for ( let p = Inv; p != null; p = p.Inv )
{
let pp = PowerHighJump(p);
if ( !pp ) continue;
double f = pp.Strength;
if ( f > jumpfac ) jumpfac = f;
}
if ( jumpfac > 0 ) jumpvelz *= jumpfac;
Vel.z += jumpvelz;
bOnMobj = false;
player.jumpTics = -1;
if ( !(player.cheats&CF_PREDICTING) )
A_PlaySound("*jump",CHAN_BODY);
}
last_jump_held = gametic;
}
if ( !player.onground || player.jumptics )
last_jump_held = gametic;
}
}
// Random Spawner that passes through dropped status to items
Class RandomSpawner2 : RandomSpawner
{
override void PostSpawn( Actor spawned )
{
if ( !bDROPPED ) return;
if ( spawned is 'Inventory' ) Inventory(spawned).bTOSSED = bDROPPED;
if ( spawned is 'UTWeapon' )
{
spawned.SetState(spawned.ResolveState("Spawn")+1);
Inventory(spawned).bALWAYSPICKUP = true;
if ( UTWeapon(spawned).DropAmmo > 0 )
Weapon(spawned).AmmoGive1 = min(UTWeapon(spawned).DropAmmo,Weapon(spawned).AmmoGive1);
}
}
}
Class UTWeapon : Weapon
{
int DropAmmo;
bool bExtraPickup;
Property DropAmmo: DropAmmo;
// Drawstuffs under HUD
virtual ui void PreRender( double lbottom ) {}
// Drawstuffs over HUD
virtual ui void PostRender( double lbottom ) {}
override Inventory CreateTossable( int amt )
{
if ( Ammo1 && (Ammo1.Amount <= 0) ) return null;
Inventory d = Super.CreateTossable(amt);
if ( d && (d.GetClass() == GetClass()) )
{
d.SetState(d.ResolveState("Spawn")+1);
d.bALWAYSPICKUP = true;
}
return d;
}
override bool SpecialDropAction( Actor dropper )
{
SetState(ResolveState("Spawn")+1);
bALWAYSPICKUP = true;
return false;
}
override void Tick()
{
Super.Tick();
if ( !Owner || !Owner.player || (Owner.player.ReadyWeapon != self) ) return;
Owner.player.WeaponState |= WF_WEAPONBOBBING; // UT weapons always bob
}
void FireEffect()
{
let amp = DamageAmplifier(Owner.FindInventory("DamageAmplifier",true));
if ( amp ) amp.FireEffect();
}
override void OwnerDied()
{
Super.OwnerDied();
A_ClearRefire();
}
override bool HandlePickup( Inventory item )
{
if (item.GetClass() == GetClass())
{
if ( Weapon(item).PickupForAmmo(self) )
item.bPickupGood = true;
if ( (MaxAmount > 1) || bALWAYSPICKUP )
return Inventory.HandlePickup(item);
return true;
}
return false;
}
override bool ShouldStay()
{
if ( ((multiplayer && (!deathmatch && !alwaysapplydmflags)) || sv_weaponstay) && !bDropped )
return (!bExtraPickup && !bALWAYSPICKUP);
return false;
}
override bool TryPickup( in out Actor toucher )
{
if ( !bExtraPickup ) bExtraPickup = ((MaxAmount > 1) && (toucher.CountInv(GetClass()) < MaxAmount));
return Super.TryPickup(toucher);
}
// Whole chain of function rewrites because of some stupid hardcoded deathmatch ammo multiplier
override bool TryPickupRestricted( in out Actor toucher )
{
if ( ShouldStay() ) return false;
bExtraPickup = false;
bool gaveSome = !!(NonIdioticAddAmmo(toucher,AmmoType1,AmmoGive1));
gaveSome |= !!(NonIdioticAddAmmo(toucher,AmmoType2,AmmoGive2));
if ( gaveSome ) GoAwayAndDie();
return gaveSome;
}
override void AttachToOwner( Actor other )
{
bExtraPickup = false;
Inventory.AttachToOwner(other);
Ammo1 = NonIdioticAddAmmo(Owner,AmmoType1,AmmoGive1);
Ammo2 = NonIdioticAddAmmo(Owner,AmmoType2,AmmoGive2);
SisterWeapon = AddWeapon(SisterWeaponType);
if ( Owner.player )
{
if ( !Owner.player.GetNeverSwitch() && !bNo_Auto_Switch )
Owner.player.PendingWeapon = self;
if ( Owner.player.mo == players[consoleplayer].camera )
StatusBar.ReceivedWeapon(self);
}
GivenAsMorphWeapon = false;
}
// rewrite of AddAmmo without stupid hardcoded 2.5x ammo multiplier
protected Ammo NonIdioticAddAmmo( Actor other, Class<Ammo> ammotype, int amount )
{
if ( !ammotype ) return null;
Ammo ammoitem;
if ( !bIgnoreSkill ) amount = int(amount*G_SkillPropertyFloat(SKILLP_AmmoFactor));
ammoitem = Ammo(other.FindInventory(ammotype));
if ( !ammoitem )
{
ammoitem = Ammo(Spawn(ammotype));
ammoitem.Amount = min(amount,ammoitem.MaxAmount);
ammoitem.AttachToOwner(other);
}
else if ( ammoitem.Amount < ammoitem.MaxAmount )
{
ammoitem.Amount += amount;
if ( ammoitem.Amount > ammoitem.MaxAmount )
ammoitem.Amount = ammoitem.MaxAmount;
}
return ammoitem;
}
Default
{
Weapon.BobStyle "Smooth";
Weapon.BobSpeed 1.5;
Weapon.BobRangeX 0.2;
Weapon.BobRangeY 0.4;
Weapon.YAdjust 0;
+WEAPON.NOALERT;
}
}
Class UTTeleportLight : DynamicLight
{
Default
{
DynamicLight.Type "Point";
Args 128,160,255,80;
}
override void Tick()
{
Super.Tick();
if ( alpha <= 0 )
{
Destroy();
return;
}
args[LIGHT_RED] = int(128*alpha);
args[LIGHT_GREEN] = int(160*alpha);
args[LIGHT_BLUE] = int(255*alpha);
args[LIGHT_INTENSITY] = Random[Tele](10,14)*8;
alpha -= 1./35;
}
}
Class UTItemLight : DynamicLight
{
Default
{
DynamicLight.Type "Point";
Args 255,224,160,48;
}
override void Tick()
{
Super.Tick();
if ( alpha <= 0 )
{
Destroy();
return;
}
args[LIGHT_RED] = int(255*alpha);
args[LIGHT_GREEN] = int(224*alpha);
args[LIGHT_BLUE] = int(160*alpha);
args[LIGHT_INTENSITY] = Random[Tele](6,8)*8;
alpha -= 3./35;
}
}
Class UTTeleportFog : Actor
{
Default
{
+NOBLOCKMAP;
+NOTELEPORT;
+NOGRAVITY;
RenderStyle "Add";
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
Spawn("UTTeleportLight",pos+(0,0,16));
A_PlaySound ("misc/teleport");
}
States
{
Spawn:
TELE ABCDEFGHIJKLMNOPQRSTUVWXYZ 1 Bright A_FadeOut(1./35);
TEL2 ABCDEFGHI 1 Bright A_FadeOut(1./35);
Stop;
}
}
Class UTItemFog : Actor
{
Default
{
+NOBLOCKMAP;
+NOTELEPORT;
+NOGRAVITY;
RenderStyle "Add";
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
Spawn("UTItemLight",pos+(0,0,16));
}
States
{
Spawn:
TNT1 A 1;
Stop;
}
}
Class UTSpark : Actor
{
Default
{
RenderStyle "Add";
Radius 2;
Height 2;
+NOBLOCKMAP;
+FORCEXYBILLBOARD;
+MISSILE;
+MOVEWITHSECTOR;
+THRUACTORS;
+NOTELEPORT;
+DONTSPLASH;
BounceType "Doom";
BounceFactor 0.4;
Gravity 0.5;
Scale 0.05;
}
override void Tick()
{
Super.Tick();
if ( waterlevel > 0 )
{
let b = Spawn("UTBubble",pos);
b.vel = vel;
b.scale *= 0.3;
Destroy();
}
}
States
{
Spawn:
SPRK A 1 Bright A_FadeOut(0.01);
Wait;
Death:
SPRK A 1 Bright A_FadeOut(0.05);
Wait;
}
}
Class UTViewSpark : UTSpark
{
Default
{
+NOCLIP;
-MISSILE;
BounceType "None";
}
Vector3 ofs, vvel;
override void Tick()
{
Actor.Tick();
if ( !target || !target.player )
{
Destroy();
return;
}
Vector3 x, y, z;
[x, y, z] = Matrix4.GetAxes(target.pitch,target.angle,target.roll);
Vector3 origin = x*ofs.x+y*ofs.y+z*ofs.z+(0,0,target.player.viewz);
SetOrigin(target.Vec2OffsetZ(origin.x,origin.y,origin.z),true);
bInvisible = (players[consoleplayer].camera != target);
if ( level.frozen || globalfreeze ) return;
ofs += vvel;
vvel.z -= 0.1;
scale *= 0.8;
if ( (scale.x <= 0.01) || (waterlevel > 0) ) Destroy();
}
}
Class UTChip : Actor
{
int deadtimer;
double rollvel, anglevel, pitchvel;
Default
{
Radius 2;
Height 2;
+NOBLOCKMAP;
+MISSILE;
+MOVEWITHSECTOR;
+THRUACTORS;
+NOTELEPORT;
+DONTSPLASH;
+INTERPOLATEANGLES;
BounceType "Doom";
BounceFactor 0.3;
Gravity 0.7;
Scale 0.2;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
deadtimer = 0;
anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
frame = Random[Junk](0,3);
scale *= Frandom[Junk](0.8,1.2);
}
override void Tick()
{
Super.Tick();
if ( level.frozen || globalfreeze ) return;
if ( InStateSequence(CurState,ResolveState("Death")) )
{
deadtimer++;
if ( deadtimer > 300 ) A_FadeOut(0.05);
return;
}
}
States
{
Spawn:
CHIP # 1
{
angle += anglevel;
pitch += pitchvel;
roll += rollvel;
}
Loop;
Bounce:
CHIP # 0
{
anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
}
Goto Spawn;
Death:
CHIP # -1;
Stop;
Dummy:
CHIP ABCD -1;
Stop;
}
}
Class UTBubble : Actor
{
Default
{
RenderStyle "Add";
Radius 2;
Height 2;
+NOBLOCKMAP;
+NOGRAVITY;
+DONTSPLASH;
+FORCEXYBILLBOARD;
+NOTELEPORT;
Scale 0.05;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
double ang, pt;
scale *= FRandom[Puff](0.5,1.5);
ang = FRandom[Puff](0,360);
pt = FRandom[Puff](-90,90);
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.2,0.8);
if ( waterlevel <= 0 ) Destroy();
SetState(ResolveState("Spawn")+Random[Puff](0,2));
}
override void Tick()
{
Super.Tick();
if ( level.frozen || globalfreeze ) return;
vel *= 0.96;
vel.z += 0.05;
if ( (waterlevel <= 0) || !Random[Puff](0,100) ) Destroy();
}
States
{
Spawn:
BUBL ABC -1;
Stop;
}
}
Class UTSmoke : Actor
{
Default
{
RenderStyle "Shaded";
StencilColor "FFFFFF";
Radius 2;
Height 2;
+NOBLOCKMAP;
+NOGRAVITY;
+DONTSPLASH;
+FORCEXYBILLBOARD;
+THRUACTORS;
+NOTELEPORT;
+CANBOUNCEWATER;
-BOUNCEAUTOOFF;
BounceType "Hexen";
BounceFactor 1.0;
WallBounceFactor 1.0;
Scale 0.5;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
double ang, pt;
scale *= FRandom[Puff](0.5,1.5);
alpha *= FRandom[Puff](0.5,1.5);
ang = FRandom[Puff](0,360);
pt = FRandom[Puff](-90,90);
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.2,0.8);
}
override void Tick()
{
Super.Tick();
if ( level.frozen || globalfreeze ) return;
vel *= 0.96;
vel.z += 0.01;
A_FadeOut(1/32.);
if ( waterlevel > 0 )
{
let b = Spawn("UTBubble",pos);
b.vel = vel;
Destroy();
}
}
States
{
Spawn:
TNT1 A 0 NoDelay A_Jump(255,"US1","US2","US3","US4","US5","US6","US7","US8","US9","US10");
Stop;
US1:
US1_ ABCDEFGHIJKLMNOP 2;
Stop;
US2:
US2_ ABCDEFGHIJKLMNOP 2;
Stop;
US3:
US3_ ABCDEFGHIJKLMNOP 2;
Stop;
US4:
US4_ ABCDEFGHIJKLMNO 2;
Stop;
US5:
US5_ ABCDEFGHIJKLMNO 2;
Stop;
US6:
US6_ ABCDEFGHIJKLMNOP 2;
Stop;
US7:
US7_ ABCDEFGHIJKLMNOP 2;
Stop;
US8:
US8_ ABCDEFGHIJKLMNOP 2;
Stop;
US9:
US9_ ABCDEFGHIJKLMNOP 2;
Stop;
US10:
US10 ABCDEFGHIJKLMNOP 2;
Stop;
}
}
Class UTSmallSmoke : UTSmoke
{
override void PostBeginPlay()
{
Actor.PostBeginPlay();
double ang, pt;
scale *= FRandom[Puff](0.1,0.3);
alpha *= FRandom[Puff](0.5,1.5);
ang = FRandom[Puff](0,360);
pt = FRandom[Puff](-90,90);
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.04,0.16);
}
}
Class UTViewSmoke : UTSmoke
{
Vector3 ofs, vvel;
override void PostBeginPlay()
{
Actor.PostBeginPlay();
double ang, pt;
scale *= FRandom[Puff](0.1,0.3);
alpha *= FRandom[Puff](0.5,1.5);
ang = FRandom[Puff](0,360);
pt = FRandom[Puff](-90,90);
vvel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.04,0.16);
}
override void Tick()
{
Actor.Tick();
if ( !target || !target.player )
{
Destroy();
return;
}
Vector3 x, y, z;
[x, y, z] = Matrix4.GetAxes(target.pitch,target.angle,target.roll);
Vector3 origin = x*ofs.x+y*ofs.y+z*ofs.z+(0,0,target.player.viewz);
SetOrigin(target.Vec2OffsetZ(origin.x,origin.y,origin.z),true);
bInvisible = (players[consoleplayer].camera != target);
if ( level.frozen || globalfreeze ) return;
ofs += vvel;
vvel *= 0.96;
vvel.z += 0.01;
A_FadeOut(1/32.);
if ( waterlevel > 0 ) Destroy();
}
}
Class UTStaticViewSmoke : UTViewSmoke
{
override void PostBeginPlay()
{
Actor.PostBeginPlay();
scale *= FRandom[Puff](0.1,0.3);
alpha *= FRandom[Puff](0.5,1.5);
}
}
Class UTRedSkull : RedSkull
{
Default
{
Tag "Red Skull";
Species "RedSkull";
Inventory.PickupMessage "You got the Red Skull.";
}
States
{
Spawn:
USKL A -1;
Stop;
}
}
Class UTGoldSkull : YellowSkull
{
Default
{
Tag "Gold Skull";
Species "YellowSkull";
Inventory.PickupMessage "You got the Gold Skull.";
}
States
{
Spawn:
USKL B -1;
Stop;
}
}
Class UTBlueSkull : BlueSkull
{
Default
{
Tag "Blue Skull";
Species "BlueSkull";
Inventory.PickupMessage "You got the Blue Skull.";
}
States
{
Spawn:
USKL C -1;
Stop;
}
}
Class UTRedKey : RedCard
{
Default
{
Tag "Red Key";
Species "RedCard";
Inventory.PickupMessage "You got the Red Key.";
}
States
{
Spawn:
UKEY A -1;
Stop;
}
}
Class UTGoldKey : YellowCard
{
Default
{
Tag "Gold Key";
Species "YellowCard";
Inventory.PickupMessage "You got the Gold Key.";
}
States
{
Spawn:
UKEY B -1;
Stop;
}
}
Class UTBlueKey : BlueCard
{
Default
{
Tag "Blue Key";
Species "BlueCard";
Inventory.PickupMessage "You got the Blue Key.";
}
States
{
Spawn:
UKEY C -1;
Stop;
}
}
Class ShredCorpseHitbox : Actor
{
int accdamage;
Default
{
+NOGRAVITY;
-SOLID;
+DONTSPLASH;
+SHOOTABLE;
Health int.max;
}
override void PostBeginPlay()
{
if ( !target )
{
Destroy();
return;
}
accdamage = target.Health;
A_SetSize(target.radius,target.height);
}
override void Tick()
{
Super.Tick();
if ( !target || (target.Health > 0) || target.InStateSequence(target.CurState,target.FindState("XDeath")) )
{
Destroy();
return;
}
SetOrigin(target.pos,true);
A_SetSize(target.radius,target.height);
}
override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle )
{
accdamage -= damage;
int gibhealth = (target.GibHealth==int.min)?-target.SpawnHealth():target.GibHealth;
if ( accdamage < gibhealth )
{
// force gib (cheap ATM)
State gib = target.FindState("XDeath");
if ( gib ) target.SetState(gib);
Destroy();
}
return 0;
}
}
Enum ESwingMode
{
SWING_Straight, // constant increment
SWING_Spring, // bounces back after a delay
};
Class Swinger : Thinker
{
Actor target;
Vector2 dir;
double inc, rmul;
int steps, mode, delay;
double str, tstr;
int cnt, cstate;
Enum ESwingerState
{
STATE_Initial,
STATE_Wait,
STATE_Return,
};
override void Tick()
{
if ( !target ) cstate = -1;
switch ( cstate )
{
case STATE_Initial:
target.A_SetAngle(target.angle+dir.x*str*flak_swingerstrength,SPF_INTERPOLATE);
target.A_SetPitch(target.pitch+dir.y*str*flak_swingerstrength,SPF_INTERPOLATE);
str += inc;
if ( ++cnt >= steps )
{
cnt = 0;
str = tstr/steps;
cstate = (mode==SWING_Straight)?(-1):(delay>0)?STATE_Wait:STATE_Return;
}
else tstr += str;
break;
case STATE_Wait:
if ( ++cnt >= delay )
{
cnt = 0;
cstate = STATE_Return;
}
break;
case STATE_Return:
target.A_SetAngle(target.angle-dir.x*(str/rmul)*flak_swingerstrength,SPF_INTERPOLATE);
target.A_SetPitch(target.pitch-dir.y*(str/rmul)*flak_swingerstrength,SPF_INTERPOLATE);
if ( ++cnt >= steps*rmul )
{
cnt = 0;
cstate = -1;
}
break;
default:
Destroy();
return;
}
}
}
Class GenericFlash : HUDMessageBase
{
Color col;
int duration;
double alpha;
Actor cam;
GenericFlash Setup( Actor camera, Color c, int d )
{
alpha = 1.0;
col = c;
duration = d;
cam = camera;
return self;
}
override bool Tick()
{
alpha -= 1./duration;
return (alpha<=0);
}
override void Draw( int bottom, int visibility )
{
if ( automapactive || (visibility != BaseStatusBar.HUDMSGLayer_UnderHUD) ) return;
if ( cam && (players[consoleplayer].camera != cam) ) return;
Screen.Dim(col,(col.a/255.)*alpha,0,0,Screen.GetWidth(),Screen.GetHeight());
}
}
Class QueuedFlash
{
Color c;
int duration;
int tic;
Actor cam;
}
Class UTMainHandler : StaticEventHandler
{
ui TextureID tex;
Array<QueuedFlash> flashes;
override void CheckReplacement( ReplaceEvent e )
{
if ( e.Replacee == 'Chainsaw' )
{
if ( Random[Replacements](0,1) ) e.Replacement = 'UTChainsaw';
else e.Replacement = 'Enforcer';
}
else if ( e.Replacee == 'Fist' ) e.Replacement = 'ImpactHammer';
else if ( e.Replacee == 'Pistol' ) e.Replacement = 'Enforcer';
else if ( (e.Replacee == 'Shotgun') || (e.Replacee == 'SuperShotgun') )
{
if ( !Random[Replacements](0,3) ) e.Replacement = 'Enforcer';
else if ( Random[Replacements](0,1) ) e.Replacement = 'BioRifle';
else e.Replacement = 'ShockRifle';
}
else if ( e.Replacee == 'Chaingun' )
{
if ( Random[Replacements](0,2) ) e.Replacement = 'PulseGun';
else e.Replacement = 'Ripper2';
}
else if ( e.Replacee == 'RocketLauncher' )
{
if ( Random[Replacements](0,1) ) e.Replacement = 'FlakCannon';
else e.Replacement = 'UTRocketLauncher';
}
else if ( e.Replacee == 'PlasmaRifle' )
{
if ( Random[Replacements](0,1) ) e.Replacement = 'Minigun';
else e.Replacement = 'SniperRifle';
}
else if ( e.Replacee == 'BFG9000' ) e.Replacement = 'WarheadLauncher';
else if ( e.Replacee == 'Clip' ) e.Replacement = 'EClip';
else if ( e.Replacee == 'ClipBox' )
{
if ( !Random[Replacements](0,2) ) e.Replacement = 'EClip';
else if ( Random[Replacements](0,2) ) e.Replacement = 'PulseAmmo';
else e.Replacement = 'RipperAmmo';
}
else if ( e.Replacee == 'Shell' )
{
if ( !Random[Replacements](0,2) ) e.Replacement = 'EClip';
else if ( Random[Replacements](0,1) ) e.Replacement = 'BioAmmo2';
else e.Replacement = 'ShockAmmo2';
}
else if ( e.Replacee == 'ShellBox' )
{
if ( Random[Replacements](0,1) ) e.Replacement = 'BioAmmo';
else e.Replacement = 'ShockAmmo';
}
else if ( e.Replacee == 'RocketAmmo' )
{
if ( Random[Replacements](0,1) )
{
if ( !Random[Replacements](0,3) ) e.Replacement = 'FlakAmmo';
else e.Replacement = 'FlakAmmo2';
}
else
{
if ( !Random[Replacements](0,3) ) e.Replacement = 'UTRocketAmmo';
else e.Replacement = 'UTRocketAmmo2';
}
}
else if ( e.Replacee == 'RocketBox' )
{
if ( Random[Replacements](0,1) ) e.Replacement = 'FlakAmmo';
else e.Replacement = 'UTRocketAmmo';
}
else if ( e.Replacee == 'Cell' )
{
if ( Random[Replacements](0,1) ) e.Replacement = 'EClip';
else if ( !Random[Replacements](0,3) ) e.Replacement = 'RifleAmmo';
else e.Replacement = 'RifleAmmo2';
}
else if ( e.Replacee == 'CellPack' )
{
if ( !Random[Replacements](0,6) ) e.Replacement = 'WarheadAmmo';
else if ( Random[Replacements](0,1) ) e.Replacement = 'MiniAmmo';
else e.Replacement = 'RifleAmmo';
}
else if ( e.Replacee == 'InvulnerabilitySphere' ) e.Replacement = 'EnhancedShockRifle';
else if ( e.Replacee == 'Berserk' ) e.Replacement = 'UDamage';
else if ( e.Replacee == 'Soulsphere' ) e.Replacement = 'UTHealthPack';
else if ( e.Replacee == 'Megasphere' ) e.Replacement = 'UTShieldBelt';
else if ( e.Replacee == 'Allmap' ) e.Replacement = 'UTMapRevealer';
else if ( e.Replacee == 'BlurSphere' ) e.Replacement = 'UTInvisibility';
else if ( e.Replacee == 'Infrared' ) e.Replacement = 'Searchlight';
else if ( e.Replacee == 'RadSuit' ) e.Replacement = 'UTJumpBoots';
else if ( e.Replacee == 'Backpack' ) e.Replacement = 'UTBackpack';
else if ( e.Replacee == 'ArmorBonus' ) e.Replacement = 'UTArmorBonus';
else if ( e.Replacee == 'HealthBonus' ) e.Replacement = 'UTHealthBonus';
else if ( e.Replacee == 'GreenArmor' ) e.Replacement = 'UTThighPads';
else if ( e.Replacee == 'BlueArmor' ) e.Replacement = 'UTBodyArmor';
else if ( e.Replacee == 'Stimpack' ) e.Replacement = 'UTMedBox';
else if ( e.Replacee == 'Medikit' ) e.Replacement = 'UTHealthBox';
else if ( e.Replacee == 'RedCard' ) e.Replacement = 'UTRedKey';
else if ( e.Replacee == 'BlueCard' ) e.Replacement = 'UTBlueKey';
else if ( e.Replacee == 'YellowCard' ) e.Replacement = 'UTGoldKey';
else if ( e.Replacee == 'RedSkull' ) e.Replacement = 'UTRedSkull';
else if ( e.Replacee == 'BlueSkull' ) e.Replacement = 'UTBlueSkull';
else if ( e.Replacee == 'YellowSkull' ) e.Replacement = 'UTGoldSkull';
else if ( e.Replacee == 'TeleportFog' ) e.Replacement = 'UTTeleportFog';
else if ( e.Replacee == 'ItemFog' ) e.Replacement = 'UTItemFog';
}
private Actor AddLight( Vector3 pos, Color col, int radius )
{
Actor l = Actor.Spawn("PointLightAttenuated",pos);
if ( !l ) return null;
l.args[0] = col.r;
l.args[1] = col.g;
l.args[2] = col.b;
l.args[3] = radius;
return l;
}
private Actor AddAmbient( Vector3 pos, String snd, double volume = 1., double attenuation = ATTN_NORM )
{
Actor a = Actor.Spawn("MapSpot",pos);
if ( !a ) return null;
a.A_PlaySound(snd,CHAN_BODY,volume,true,attenuation);
return a;
}
override void WorldLoaded( WorldEvent e )
{
if ( gamestate != GS_LEVEL || e.IsSaveGame ) return;
// prettify Kinsie's test map for a more Unreal feel
if ( level.GetChecksum() ~== "FBC3B6622A8B74AE06DE01E70007AC33" )
{
TextureID deftex = TexMan.CheckForTexture("-noflat-",TexMan.Type_Any);
TextureID skytx = TexMan.CheckForTexture("BlueSky",TexMan.Type_Any);
TextureID baseflor = TexMan.CheckForTexture("rClfFlr0",TexMan.Type_Any);
TextureID baseceil = TexMan.CheckForTexture("rClfBas0",TexMan.Type_Any);
TextureID basewall = TexMan.CheckForTexture("uAlnWl2b",TexMan.Type_Any);
TextureID xbasewall = TexMan.CheckForTexture("xAlnWl2b",TexMan.Type_Any);
TextureID glasstex = TexMan.CheckForTexture("Glassg",TexMan.Type_Any);
level.ChangeSky(skytx,skytx);
for ( int i=0; i<level.sectors.size(); i++ )
{
level.sectors[i].lightlevel = 0;
level.sectors[i].SetPlaneLight(0,0);
level.sectors[i].SetPlaneLight(1,0);
// open some ceilings
if ( level.sectors[i].ceilingplane.ZAtPoint(level.sectors[i].centerspot) == 1280 )
level.sectors[i].SetTexture(1,skyflatnum);
if ( level.sectors[i].GetTexture(0) == deftex )
{
level.sectors[i].SetTexture(0,(i==47)?baseceil:baseflor);
level.sectors[i].SetXScale(0,2.);
level.sectors[i].SetYScale(0,2.);
}
if ( level.sectors[i].GetTexture(1) == deftex )
{
level.sectors[i].SetTexture(1,(i==47)?baseflor:baseceil);
level.sectors[i].SetXScale(1,2.);
level.sectors[i].SetYScale(1,2.);
}
}
for ( int i=0; i<level.sides.size(); i++ )
{
level.sides[i].light = 0;
for ( int j=0; j<3; j++ )
{
if ( level.sides[i].GetTexture(j) != deftex ) continue;
if ( (i==529) || (i==530) || (i==533) || (i==534) )
{
level.sides[i].SetTexture(j,xbasewall);
level.sides[i].SetTextureYOffset(j,-2304);
}
else level.sides[i].SetTexture(j,basewall);
level.sides[i].SetTextureXScale(j,2.);
level.sides[i].SetTextureYScale(j,2.);
}
}
// fixup
for ( int i=215; i<246; i++ )
{
if ( (i==218) || (i==221) || (i==227)
|| (i==230) || (i==232) || (i==234)
|| (i==238) || (i==243) ) continue;
level.sectors[i].SetSpecialColor(0,"00 00 00");
}
level.sectors[53].SetFade("00 00 20");
level.sides[1844].SetTexture(1,glasstex);
level.lines[945].alpha = 0.5;
AddLight((0,-288,128),"E0 E0 FF",256);
AddLight((-2560,1024,1280),"E0 E0 FF",1024);
AddLight((0,1024,1280),"E0 E0 FF",1024);
AddLight((2560,1024,1280),"E0 E0 FF",1024);
AddLight((-384,-160,64),"FF FF FF",128);
AddLight((-384,-288,64),"FF FF FF",128);
AddLight((-384,-416,64),"FF FF FF",128);
AddLight((0,2816,96),"FF FF FF",512);
AddLight((2904,1344,128),"80 80 FF",256);
AddLight((3408,1344,128),"80 80 FF",256);
AddLight((1568,1760,64),"20 20 80",128);
AddLight((1824,1760,64),"80 20 20",128);
AddLight((2080,1760,64),"FF 80 20",128);
AddLight((2336,1760,64),"20 FF 20",128);
AddLight((2592,1760,64),"80 80 20",128);
AddLight((2848,1760,64),"A0 A0 30",128);
AddLight((2944,960,64),"20 FF 20",128);
AddLight((2944,736,64),"20 FF 20",128);
AddLight((3264,960,64),"FF 20 20",128);
AddLight((3264,736,64),"A0 A0 30",128);
AddLight((3264,512,64),"80 80 20",128);
AddLight((3584,960,64),"80 80 20",128);
AddLight((3584,736,64),"20 FF 20",128);
AddLight((3584,512,64),"FF 80 20",128);
AddLight((3584,288,64),"FF 80 FF",128);
AddAmbient((0,-288,192),"testamb/wind1",0.5,1.6);
AddAmbient((-2560,1024,768),"testamb/wind1",0.4,0.8);
AddAmbient((0,1024,768),"testamb/wind1",0.4,0.8);
AddAmbient((2560,1024,768),"testamb/wind1",0.4,0.8);
AddAmbient((768,1600,1280),"testamb/wind2",0.8,1.6);
AddAmbient((3174,1344,128),"testamb/water",0.4,1.6);
AddAmbient((1568,1760,64),"testamb/water",0.4,2.4);
AddAmbient((2080,1760,64),"testamb/lava",0.8,2.4);
AddAmbient((2336,1760,64),"testamb/slime",0.4,2.4);
AddAmbient((2944,960,64),"testamb/slime",0.4,2.4);
AddAmbient((2944,736,64),"testamb/slime",0.4,2.4);
AddAmbient((3584,736,64),"testamb/slime",0.4,2.4);
AddAmbient((3584,512,64),"testamb/lava",0.8,2.4);
}
}
override void WorldThingSpawned( WorldEvent e )
{
if ( flak_nobosstelefrag && e.Thing.bBOSS ) e.Thing.bNOTELEFRAG = true;
}
ui void StartMenu()
{
CVar protomenu = CVar.GetCVar('flak_protomenu',players[consoleplayer]);
if ( !protomenu ) return; // this can happen
int proto = protomenu.GetInt();
if ( proto )
{
tex = TexMan.CheckForTexture("protobg",TexMan.Type_Any);
if ( gamestate != GS_TITLELEVEL ) return;
if ( proto > 1 ) S_ChangeMusic("menu2");
else S_ChangeMusic("xyzdMenu");
}
else
{
tex = TexMan.CheckForTexture("finalbg",TexMan.Type_Any);
if ( gamestate != GS_TITLELEVEL ) return;
S_ChangeMusic("utmenu23");
}
}
override void PlayerEntered( PlayerEvent e )
{
if ( flak_translocator )
players[e.playernumber].mo.GiveInventory("Translocator",1);
}
override void PlayerRespawned( PlayerEvent e )
{
if ( flak_translocator )
players[e.playernumber].mo.GiveInventory("Translocator",1);
}
override void NetworkProcess( ConsoleEvent e )
{
if ( e.Name ~== "refreshtrans" )
{
if ( flak_translocator )
{
for ( int i=0; i<MAXPLAYERS; i++ ) if ( playeringame[i] ) players[i].mo.GiveInventory("Translocator",1);
}
else
{
for ( int i=0; i<MAXPLAYERS; i++ ) if ( playeringame[i] ) players[i].mo.TakeInventory("Translocator",1);
}
}
}
override void ConsoleProcess( ConsoleEvent e )
{
if ( e.Name ~== "refreshmenu" ) StartMenu();
if ( e.Name ~== "refreshtrans" ) EventHandler.SendNetworkEvent("refreshtrans");
}
override void WorldTick()
{
for ( int i=0; i<flashes.size(); i++ )
{
if ( flashes[i].tic >= gametic ) continue;
flashes.Delete(i);
i--;
}
}
override void PostUiTick()
{
for ( int i=0; i<flashes.size(); i++ )
{
if ( flashes[i].tic < gametic ) continue;
GenericFlash gf = new("GenericFlash").Setup(flashes[i].cam,flashes[i].c,flashes[i].duration);
StatusBar.AttachMessage(gf,0,BaseStatusBar.HUDMSGLayer_UnderHUD);
}
if ( gametic <= 0 ) StartMenu();
}
override void RenderOverlay( RenderEvent e )
{
if ( !menuactive ) return;
if ( tex.IsNull() || !tex.IsValid() ) return;
if ( !CVar.GetCVar('flak_showmenu',players[consoleplayer]).GetBool() ) return;
Screen.Dim("Black",1.0,0,0,Screen.GetWidth(),Screen.GetHeight());
Screen.DrawTexture(tex,true,0,0,DTA_VirtualWidth,1024,DTA_VirtualHeight,768);
}
override void WorldThingDied( WorldEvent e )
{
if ( e.Thing.bDONTGIB ) return;
// attach damage accumulator for corpses
if ( !flak_corpsedamage ) return;
let a = Actor.Spawn("ShredCorpseHitbox",e.Thing.pos);
a.target = e.Thing;
}
static void DoFlash( Actor camera, Color c, int duration )
{
QueuedFlash qf = new("QueuedFlash");
qf.duration = duration;
qf.c = c;
qf.tic = gametic;
qf.cam = camera;
let hnd = UTMainHandler(StaticEventHandler.Find("UTMainHandler"));
hnd.flashes.push(qf);
}
// Doom's explosions aren't fully 3D
static void DoBlast( Actor Source, double ExplosionRadius, double MomentumTransfer )
{
BlockThingsIterator bi = BlockThingsIterator.Create(Source,ExplosionRadius);
while ( bi.Next() )
{
Actor a = bi.Thing;
if ( !a || !a.bSHOOTABLE || !Source.CheckSight(a,0xf) || (a == Source) || (Source.Distance3D(a) > ExplosionRadius) )
continue;
Vector3 midpoint = a.Vec3Offset(0,0,a.height*0.5);
a.vel += Level.Vec3Diff(Source.pos,midpoint).unit()*(MomentumTransfer/(Thinker.TICRATE*a.mass));
}
}
// Same for this
static void DoKnockback( Actor Victim, Vector3 HitDirection, double MomentumTransfer )
{
if ( !Victim ) return;
Victim.vel += HitDirection*(MomentumTransfer/(Thinker.TICRATE*Victim.Mass));
}
static void DoSwing( Actor target, Vector2 dir, double initial, double inc, int steps, int mode = 0, int delay = 0, double rmul = 1.0 )
{
if ( !flak_swingers ) return;
let s = new("Swinger");
s.ChangeStatNum(Thinker.STAT_USER);
s.target = target;
s.dir = dir;
s.inc = inc;
s.rmul = rmul;
s.steps = steps;
s.mode = mode;
s.delay = delay;
s.cnt = 0;
s.cstate = 0;
s.str = initial;
s.tstr = initial;
}
}