Class UTPlayer : DoomPlayer { bool doprintnoammo; bool lastground; int lastwaterlevel; 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; Actor underwatersnd; int tempslide; double ssup; int corpsetime; bool headless, leglessR, leglessL, armlessR, armlessL, torsoless; // these are gibber hints to disable spawning some parts // headless: missing head // leglessR: missing right leg // leglessL: mising left leg // armlessR: missing right arm // armlessL: mising left arm // torsoless: missing torso int dolltype, voicetype; Property DollType : dolltype; Property VoiceType : voicetype; enum EDollType { DOLL_Male, DOLL_Female, DOLL_Boss }; enum EVoiceType { VOICE_None, VOICE_MaleOne, VOICE_MaleTwo, VOICE_FemaleOne, VOICE_FemaleTwo, VOICE_Boss }; const groundspeed = 400.; const swimspeed = 200.; const baseaccelrate = 2048.; const walkfactor = 0.3; const utaircontrol = 0.35; const swimspeed_doomish = 400.; const groundspeed_doomish = 600.; const dodgez = 210.; const utjumpz = 325.; const groundfriction = 8.; const fluidfriction = 1.2; const terminalvelocity = 2500.; const slantnormal = 0.7; // slope sliding will have to be handled eventually, but currently due to how much is hardcoded this is impossible Default { Player.StartItem "Enforcer"; Player.StartItem "ImpactHammer"; Player.StartItem "MiniAmmo", 30; Player.DamageScreenColor "FF 00 00"; Player.ViewHeight 46; Player.GruntSpeed 20; +NOMENU; +DONTTRANSLATE; UTPlayer.DollType DOLL_Male; UTPlayer.VoiceType VOICE_None; } // Have to modify the give cheat to handle UT armor override void CheatGive( String name, int amount ) { 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)(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[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[j]); if ( !type2 ) continue; let rep = GetReplacement(type2); if ( (rep != type2) && !(rep is "DehackedPickup") ) continue; readonly 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 // in non-vanilla mode, also gives body/thigh armor Class which[] = { "UTShieldBelt", "UTArmorBonus", "UTBodyArmor", "UTThighpads" }; Inventory inv; int mx = flak_vanillaarmor?2:4; for ( int i=0; i)(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 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[i]); if ( !type ) continue; let def = GetDefaultByType(type); if ( (!(gameinfo.gametype&GAME_Raven) && ((type is "UTActivatable") || (type is "UTActivatableHealth"))) || ((gameinfo.gametype&GAME_HERETIC) && (type is 'ActUTFullAmmoBox')) ) { // don't give activatables outside of Heretic/Hexen // don't give full ammo cubes outside of Hexen continue; } 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[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); } // modified so it displays the "has no ammo" message override Weapon PickWeapon( int slot, bool checkammo ) { doprintnoammo = true; let rslt = Super.PickWeapon(slot,checkammo); doprintnoammo = false; return rslt; } override void Tick() { Super.Tick(); if ( !flak_utmovement || !player || (player.mo != self) || (player.cheats&(CF_FROZEN|CF_TOTALLYFROZEN)) ) { bNOFRICTION = false; bNOFRICTIONBOUNCE = false; } else { bNOFRICTION = true; bNOFRICTIONBOUNCE = true; } if ( InStateSequence(CurState,FindState("See",true)) ) SetStateLabel("See2"); if ( (waterlevel >= 2) && (lastwaterlevel < 2) ) PlaySplash(1.); else if ( (waterlevel < 2) && (lastwaterlevel >= 2) ) PlaySurface(); if ( (waterlevel >= 3) && !underwatersnd ) { underwatersnd = Spawn("UTUnderSound",pos); underwatersnd.target = self; } else if ( (waterlevel < 3) && underwatersnd ) underwatersnd.Destroy(); lastwaterlevel = waterlevel; if ( !footsteps ) footsteps = CVar.GetCVar('flak_footsteps',players[consoleplayer]); if ( !footsteps.GetBool() || (Health <= 0) ) return; double ang = level.time/(20*TICRATE/35.)*360.; bool forcefootstep = false; if ( player.onground && !bNoGravity && !lastground && (waterlevel < 2) ) { player.jumptics = 0; if ( lastvelz < -8 ) { double vol = clamp((-lastvelz-8)*0.05,0.01,1.0); if ( ((waterlevel > 0) || GetFloorTerrain().IsLiquid) && !bOnMobj ) PlaySplash(vol); else A_PlaySound("*uland",CHAN_AUTO,vol); PlayLanding(); } else forcefootstep = true; } if ( tempslide ) { tempslide = max(0,tempslide-1); if ( !tempslide ) forcefootstep = true; } if ( forcefootstep || ((abs(sin(ang)) >= 1.0) && player.onground && lastground && (player.jumptics == 0) && (player.cmd.forwardmove || player.cmd.sidemove) && (waterlevel < 2)) ) { 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 ) PlayWetFootstep(vol); else PlayFootstep(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 CalcHeight() { if ( !flak_utmovement || !player || (player.mo != self) ) { Super.CalcHeight(); return; } double angle, bob; bool still = false; // no bobbing while: // - using noclip2 (equivalent to unreal's ghost cheat) // - flying // - swimming // - falling if ( !bNoGravity && player.onground && (waterlevel < 2) ) { player.bob = player.Vel dot player.Vel; if ( player.bob == 0 ) still = true; else { player.bob *= player.GetMoveBob(); if ( player.bob > MAXBOB ) player.bob = MAXBOB; } } else { // this still doesn't help because fly bob is hardcoded player.bob = 0; } double defaultviewheight = ViewHeight+player.crouchviewdelta; if ( player.cheats&CF_NOVELOCITY ) { player.viewz = pos.z+defaultviewheight; if ( player.viewz > ceilingz-4 ) player.viewz = ceilingz-4; return; } if ( still ) { if ( player.health > 0 ) { angle = Level.maptime/(120*TICRATE/35.)*360.; bob = player.GetStillBob()*sin(angle); } else bob = 0; } else { angle = Level.maptime/(20*TICRATE/35.)*360.; bob = player.bob*sin(angle)*((waterlevel>1)?0.25:0.5); } // move viewheight if ( player.playerstate == PST_LIVE ) { player.viewheight += player.deltaviewheight; if ( player.viewheight > defaultviewheight ) { player.viewheight = defaultviewheight; player.deltaviewheight = 0; } else if ( player.viewheight < (defaultviewheight/2) ) { player.viewheight = defaultviewheight/2; if ( player.deltaviewheight <= 0 ) player.deltaviewheight = 1/65536.; } if ( player.deltaviewheight ) { player.deltaviewheight += 0.25; if ( !player.deltaviewheight ) player.deltaviewheight = 1/65536.; } } if ( player.morphTics ) bob = 0; player.viewz = pos.z+player.viewheight+(bob*clamp(ViewBob,0.,1.5)); // [SP] Allow DECORATE changes to view bobbing speed. // handle smooth step down (hacky but looks ok) player.viewz += ssup; ssup = max(0,(ssup*0.7)-0.25); if ( floorclip && (player.playerstate != PST_DEAD) && (pos.z <= floorz) ) player.viewz -= Floorclip; if ( player.viewz > ceilingz-4 ) player.viewz = ceilingz-4; if ( player.viewz < floorz+4 ) player.viewz = floorz+4; } override void MovePlayer() { if ( !flak_utmovement || !player || (player.mo != self) || (player.cheats&(CF_FROZEN|CF_TOTALLYFROZEN)) ) { 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); // slant (aka steep slope) detection Vector3 floornormal = floorsector.floorplane.normal; for ( int i=0; i0) ) 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 < flak_taptics) && (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 && (waterlevel < 3) ) { if ( flak_tapdodge && (dodge.length() > 0) && !tempslide ) { if ( level.IsJumpingAllowed() ) { if ( flak_doomspeed ) vel += dodge.unit()*(groundspeed_doomish*1.5)/TICRATE; else vel += dodge.unit()*(groundspeed*1.5)/TICRATE; vel.z += dodgez/TICRATE; } else { if ( flak_doomspeed ) vel += dodge.unit()*(groundspeed_doomish*2.0)/TICRATE; else vel += dodge.unit()*(groundspeed*2.0)/TICRATE; tempslide = 8; } 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 && !tempslide ) 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 ( tempslide ) friction *= 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; // TODO attempt to replicate walk on ice velocity increase glitch if ( vel.xy.length() > maxvel ) vel.xy = vel.xy.unit()*maxvel; if ( !(player.cheats & CF_PREDICTING) ) { if ( acceleration.length() <= double.epsilon ) PlayIdle(); else PlayRunning(); } if ( tempslide ) player.vel *= 0; else player.vel = vel.xy; } } else if ( !bNoGravity && (waterlevel < 1) ) { // 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; if ( !(player.cheats & CF_PREDICTING) ) PlayIdle(); } else if ( (bFly && bFlyCheat) || (player.cheats&CF_NOCLIP2) ) { // fly cheat has infinite friction (player stops moving when movement keys are released) Vector3 dir = (0,0,0); if ( vel.length() > double.epsilon ) dir = vel.unit(); Vector3 x, y; [x, y] = dt_CoordUtil.GetAxes(pitch,angle,0); acceleration3 = x*player.cmd.forwardmove+y*player.cmd.sidemove; if ( player.cmd.buttons&BT_JUMP ) acceleration3.z = 0x500; else if ( player.cmd.buttons&BT_CROUCH ) acceleration3.z = -0x500; if ( acceleration3.length() <= double.epsilon ) vel *= 0; else { Vector3 acceldir = acceleration3.unit(); acceleration3 = acceldir*Min(acceleration3.length(),accelrate/TICRATE); vel = vel-(dir-acceldir)*vel.length(); } vel = vel+acceleration3/TICRATE; double maxvel; if ( flak_doomspeed ) maxvel = groundspeed_doomish/TICRATE; else maxvel = groundspeed/TICRATE; maxvel *= fs; if ( vel.length() > maxvel ) vel = vel.unit()*maxvel; player.vel *= 0; player.jumptics = -2; if ( !(player.cheats & CF_PREDICTING) ) PlayIdle(); } else { // swimming is pretty much like ground movement, but with much reduced friction and lower speed friction *= fluidfriction/groundfriction; Vector3 dir = (0,0,0); if ( vel.length() > double.epsilon ) dir = vel.unit(); double doomfriction = clamp(GetFriction()/ORIG_FRICTION,0.0,1.0); Vector3 x, y; [x, y] = dt_CoordUtil.GetAxes(pitch,angle,0); acceleration3 = x*player.cmd.forwardmove+y*player.cmd.sidemove; if ( player.cmd.buttons&BT_JUMP ) acceleration3.z = 0x500; else if ( player.cmd.buttons&BT_CROUCH ) acceleration3.z = -0x500; if ( acceleration3.length() <= double.epsilon ) { Vector3 oldvel = vel; vel = vel-(2*dir)*vel.length()*friction/TICRATE; if ( oldvel dot vel < 0.0 ) vel *= 0; } else { Vector3 acceldir = acceleration3.unit(); acceleration3 = acceldir*Min(acceleration3.length(),accelrate/TICRATE); vel = vel-(dir-acceldir)*vel.length()*friction/TICRATE; } vel = vel+acceleration3/TICRATE; double maxvel; if ( waterlevel < 2 ) // flying uses ground speed { if ( flak_doomspeed ) maxvel = groundspeed_doomish/TICRATE; else maxvel = groundspeed/TICRATE; } else { if ( flak_doomspeed ) maxvel = swimspeed_doomish/TICRATE; else maxvel = swimspeed/TICRATE; } maxvel *= fs*doomfriction; if ( vel.length() > maxvel ) vel = vel.unit()*maxvel; player.vel = vel.xy; player.jumptics = -2; if ( !(player.cheats & CF_PREDICTING) ) { if ( acceleration3.length() <= double.epsilon ) PlayIdle(); else PlayRunning(); } } if ( player.cheats & CF_REVERTPLEASE ) { player.cheats &= ~CF_REVERTPLEASE; player.camera = player.mo; } } override void CheckCrouch( bool totallyfrozen ) { if ( !flak_utmovement || !player || (player.mo != self) ) { Super.CheckCrouch(totallyfrozen); return; } if ( player.cmd.buttons&BT_JUMP ) player.cmd.buttons &= ~BT_CROUCH; // in UT you can't crouch unless you're on the ground // however for the sake of compatibility with a whole lot of maps, crouch-jumping will be a thing here if ( CanCrouch() && (player.health > 0) && level.IsCrouchingAllowed() ) { if ( !totallyfrozen ) { int crouchdir = player.crouching; if ( !crouchdir ) crouchdir = (player.cmd.buttons&BT_CROUCH)?-1:1; else if ( player.cmd.buttons&BT_CROUCH ) player.crouching = 0; if ( (crouchdir == 1) && (player.crouchfactor < 1) && (pos.Z+height < ceilingz) ) CrouchMove(1); else if ( (crouchdir == -1) && (player.crouchfactor > 0.5 )) CrouchMove(-1); } } else player.Uncrouch(); player.crouchoffset = -(ViewHeight)*(1-player.crouchfactor); } 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 ( level.IsJumpingAllowed() && player.onground && (player.jumpTics == 0) && (last_jump_held < gametic-1) && !bNoGravity && (waterlevel < 2) && !(player.cheats&(CF_FLY|CF_NOCLIP2)) ) { 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; } override void DeathThink() { Super.DeathThink(); if ( !flak_utmovement || !player || (player.mo != self) || (player.cheats&(CF_FROZEN|CF_TOTALLYFROZEN)) ) return; // unreal physics while dead double friction = FrictionToUnreal(); if ( !bNoGravity && player.onground && (waterlevel < 3) ) { // 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); 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; double maxvel; if ( flak_doomspeed ) maxvel = groundspeed_doomish/TICRATE; else maxvel = groundspeed/TICRATE; maxvel *= doomfriction; if ( vel.xy.length() > maxvel ) vel.xy = vel.xy.unit()*maxvel; player.vel *= 0; } else if ( !bNoGravity && (waterlevel < 1) ) { // air acceleration when falling Vector2 dir = (0,0); if ( vel.xy.length() > double.epsilon ) dir = vel.xy.unit(); double maxvel; if ( flak_doomspeed ) maxvel = groundspeed_doomish/TICRATE; else maxvel = groundspeed/TICRATE; // if new velocity is higher than ground speed, steer but don't increase it if ( vel.xy.length() > maxvel ) { double vsiz = vel.xy.length(); vel.xy = vel.xy.unit()*vsiz; } if ( vel.length() > terminalvelocity/TICRATE ) vel = vel.unit()*(terminalvelocity/TICRATE); player.vel *= 0; } else { // swimming is pretty much like ground movement, but with much reduced friction and lower speed friction *= fluidfriction/groundfriction; Vector3 dir = (0,0,0); if ( vel.length() > double.epsilon ) dir = vel.unit(); double doomfriction = clamp(GetFriction()/ORIG_FRICTION,0.0,1.0); Vector3 oldvel = vel; vel = vel-(2*dir)*vel.length()*friction/TICRATE; if ( oldvel dot vel < 0.0 ) vel *= 0; double maxvel; if ( flak_doomspeed ) maxvel = swimspeed_doomish/TICRATE; else maxvel = swimspeed/TICRATE; maxvel *= doomfriction; if ( vel.length() > maxvel ) vel = vel.unit()*maxvel; player.vel *= 0; } } virtual void PlayFootstep( double vol ) { A_PlaySound("ut/playerfootstep",CHAN_AUTO,vol); } virtual void PlaySplash( double vol ) { TerrainDef t = GetFloorTerrain(); String snd = "ut/wetsplash"; switch ( t.TerrainName ) { case 'UTLava': case 'Lava': snd = "ut/lavasplash"; break; case 'UTSlime': case 'UTNukage': case 'Slime': case 'Nukage': case 'Sludge': snd = "ut/slimesplash"; break; } A_PlaySound(snd,CHAN_AUTO,vol); } virtual void PlaySurface() { TerrainDef t = GetFloorTerrain(); String snd = "ut/wetsurface"; switch ( t.TerrainName ) { case 'UTLava': case 'Lava': snd = "ut/lavasurface"; break; case 'UTSlime': case 'UTNukage': case 'Slime': case 'Nukage': case 'Sludge': snd = "ut/slimesurface"; break; } A_PlaySound(snd,CHAN_AUTO); } virtual void PlayWetFootstep( double vol ) { TerrainDef t = GetFloorTerrain(); String snd = "ut/playerfootstepwet"; switch ( t.TerrainName ) { case 'UTLava': case 'Lava': snd = "ut/playerfootsteplava"; break; case 'UTSlime': case 'UTNukage': case 'Slime': case 'Nukage': case 'Sludge': snd = "ut/playerfootstepslime"; break; } A_PlaySound(snd,CHAN_AUTO,vol); } override void PlayIdle() { if ( player.Health <= 0 ) return; if ( !bNoGravity && player.onground && (waterlevel < 3) ) { // Ground if ( (player && (player.mo == self)) && player.cmd.yaw ) { if ( InStateSequence(CurState,FindState("See2")) || InStateSequence(CurState,FindState("SeeAir")) || InStateSequence(CurState,FindState("SeeSwim")) || InStateSequence(CurState,FindState("SeeCrouch")) || InStateSequence(CurState,FindState("SpawnAir")) || InStateSequence(CurState,FindState("SpawnSwim")) || InStateSequence(CurState,FindState("SpawnCrouch")) || InStateSequence(CurState,FindState("Spawn")+1) ) SetStateLabel("Turn"); } else if ( player.crouchdir == -1 ) { // Crouching if ( !InStateSequence(CurState,FindState("SpawnCrouch")) ) SetStateLabel("SpawnCrouch"); } else { if ( InStateSequence(CurState,FindState("See2")) || InStateSequence(CurState,FindState("SeeAir")) || InStateSequence(CurState,FindState("SeeSwim")) || InStateSequence(CurState,FindState("SeeCrouch")) || InStateSequence(CurState,FindState("SpawnAir")) || InStateSequence(CurState,FindState("SpawnSwim")) || InStateSequence(CurState,FindState("SpawnCrouch")) ) SetStateLabel("Spawn"); } } else if ( !bNoGravity && (waterlevel < 1) ) { // Falling if ( InStateSequence(CurState,FindState("See2")) || InStateSequence(CurState,FindState("SeeAir")) || InStateSequence(CurState,FindState("SeeSwim")) || InStateSequence(CurState,FindState("SeeCrouch")) || InStateSequence(CurState,FindState("Spawn")) || InStateSequence(CurState,FindState("SpawnSwim")) || InStateSequence(CurState,FindState("SpawnCrouch")) || InStateSequence(CurState,FindState("Turn")) ) SetStateLabel("SpawnAir"); } else { // Swimming if ( InStateSequence(CurState,FindState("See2")) || InStateSequence(CurState,FindState("SeeAir")) || InStateSequence(CurState,FindState("SeeSwim")) || InStateSequence(CurState,FindState("SeeCrouch")) || InStateSequence(CurState,FindState("Spawn")) || InStateSequence(CurState,FindState("SpawnAir")) || InStateSequence(CurState,FindState("SpawnCrouch")) || InStateSequence(CurState,FindState("Turn")) ) SetStateLabel("SpawnSwim"); } } override void PlayRunning() { if ( player.Health <= 0 ) return; if ( !bNoGravity && player.onground && (waterlevel < 3) ) { // Ground if ( player.crouchdir == -1 ) { // Crouching if ( !InStateSequence(CurState,FindState("SeeCrouch")) ) SetStateLabel("SeeCrouch"); } else if ( InStateSequence(CurState,FindState("Spawn")) || InStateSequence(CurState,FindState("SpawnAir")) || InStateSequence(CurState,FindState("SpawnSwim")) || InStateSequence(CurState,FindState("SpawnCrouch")) || InStateSequence(CurState,FindState("SeeAir")) || InStateSequence(CurState,FindState("SeeSwim")) || InStateSequence(CurState,FindState("SeeCrouch")) || InStateSequence(CurState,FindState("Turn")) ) SetStateLabel("See2"); } else if ( !bNoGravity && (waterlevel < 1) ) { // Falling if ( InStateSequence(CurState,FindState("Spawn")) || InStateSequence(CurState,FindState("SpawnAir")) || InStateSequence(CurState,FindState("SpawnSwim")) || InStateSequence(CurState,FindState("SpawnCrouch")) || InStateSequence(CurState,FindState("See2")) || InStateSequence(CurState,FindState("SeeSwim")) || InStateSequence(CurState,FindState("SeeCrouch")) || InStateSequence(CurState,FindState("Turn")) ) SetStateLabel("SeeAir"); } else { // Swimming if ( InStateSequence(CurState,FindState("Spawn")) || InStateSequence(CurState,FindState("SpawnAir")) || InStateSequence(CurState,FindState("SpawnSwim")) || InStateSequence(CurState,FindState("SpawnCrouch")) || InStateSequence(CurState,FindState("See2")) || InStateSequence(CurState,FindState("SeeAir")) || InStateSequence(CurState,FindState("SeeCrouch")) || InStateSequence(CurState,FindState("Turn")) ) SetStateLabel("SeeSwim"); } } override void PlayAttacking() { if ( player.Health <= 0 ) return; // no animation if crouched if ( (player && (player.mo == self)) && (player.crouchdir == -1) ) return; // check weapon type if ( (player.ReadyWeapon is 'SniperRifle') && (player.buttons&BT_ALTATTACK) ) return; if ( ((player.ReadyWeapon is 'BioRifle') && (player.buttons&BT_ALTATTACK)) || (player.ReadyWeapon is 'UTRocketLauncher') ) { if ( !InStateSequence(CurState,FindState("MissileRepStill")) ) SetStateLabel("MissileRepStill"); } else if ( ((player.ReadyWeapon is 'ImpactHammer') && (player.buttons&BT_ATTACK)) || ((player.ReadyWeapon is 'UTChainsaw') && (player.buttons&BT_ATTACK)) || (player.ReadyWeapon is 'PulseGun') || (player.ReadyWeapon is 'Minigun') ) { if ( !InStateSequence(CurState,FindState("MissileRep")) ) SetStateLabel("MissileRep"); } else SetStateLabel("Missile"); } override void PlayAttacking2() { PlayAttacking(); } virtual void PlayAttacking3() { if ( player.Health <= 0 ) return; if ( (player && (player.mo == self)) && (player.crouchdir == -1) ) return; SetStateLabel("Missile"); } virtual void PlayReloading() { if ( player.Health <= 0 ) return; if ( (player && (player.mo == self)) && (player.crouchdir == -1) ) return; SetStateLabel("Reload"); } virtual void PlayLanding() { if ( player.Health <= 0 ) return; if ( (player && (player.mo == self)) && (player.crouchdir == -1) ) return; SetStateLabel("Landing"); } void A_HeadPop() { headless = true; Class hclass = "UTHeadMale"; if ( DollType == DOLL_Boss ) hclass = "UTHeadBoss"; else if ( DollType == DOLL_Female ) hclass = "UTHeadFemale"; let h = UTHead(Spawn(hclass,Vec3Offset(0,0,viewheight))); if ( player ) { h.headowner = player; player.camera = h; } double ang = FRandom[Blod](0,360); double pt = FRandom[Blod](-90,-30); Vector3 dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt)); h.vel = vel*0.6+dir*FRandom[Blod](8.0,12.0); } void A_GibMe() { let a = Actor.Spawn("UTPlayerGibber",pos); a.vel = vel; a.Scale = Scale; a.A_SetSize(radius,height); UTGibber(a).Gibbed = self; } void A_DMFade() { if ( !deathmatch || player ) return; corpsetime++; if ( corpsetime > 350 ) A_FadeOut(0.03); } override double GetDeathHeight() { // no height reduction while still being zapped if ( DamageType == 'Zapped' ) return height; return Super.GetDeathHeight(); } // mostly a copy of A_FreezeDeathChunks but with some DT specific gib // behaviour void A_FreezeDeathChunksDT() { if ( (vel != (0,0,0)) && !bShattering ) { tics = 3*TICRATE; return; } vel = (0,0,0); A_PlaySound ("misc/icebreak",CHAN_BODY); // [RH] In Hexen, this creates a random number of shards (range [24,56]) // with no relation to the size of the self shattering. I think it should // base the number of shards on the size of the dead thing, so bigger // things break up into more shards than smaller things. // An actor with radius 20 and height 64 creates ~40 chunks. int numChunks = max(4,int(radius*Height)/32); int i = Random[FreezeDeathChunks](0,numChunks/4-1); for ( i=max(24,numChunks+i); i>=0; i-- ) { double xo = (random[FreezeDeathChunks]()-128)*radius/128; double yo = (random[FreezeDeathChunks]()-128)*radius/128; double zo = (random[FreezeDeathChunks]()*Height/255); Actor mo = Spawn("IceChunk",Vec3Offset(xo,yo,zo),ALLOW_REPLACE); if ( !mo ) continue; mo.SetState(mo.SpawnState+random[FreezeDeathChunks](0,2)); mo.Vel.X = random2[FreezeDeathChunks]()/128.; mo.Vel.Y = random2[FreezeDeathChunks]()/128.; mo.Vel.Z = (mo.pos.Z-pos.Z)/Height*4; } // Gib the player, the camera will attach to the head at this point A_GibMe(); A_NoBlocking(); SetStateLabel('null'); } void A_PainDT() { if ( waterlevel > 2 ) A_PlaySound("*death-drowning",CHAN_VOICE); else A_Pain(); } void A_PlayerScreamDT() { if ( waterlevel > 2 ) A_PlaySound("*death-drowning",CHAN_VOICE); else A_PlayerScream(); } void A_XScreamDT() { if ( waterlevel > 2 ) A_PlaySound("*death-drowning",CHAN_VOICE); else A_XScream(); } States { Spawn: #### # 5; PLAY ABCDEFG 8; Goto Spawn+1; Turn: #### # 6; PLYT A 0 A_JumpIf(!player||!player.cmd.yaw,"Spawn"); PLYT A 6; PLYT A 0 A_JumpIf(!player||!player.cmd.yaw,"Spawn"); PLYT A 6; PLYT B 0 A_JumpIf(!player||!player.cmd.yaw,"Spawn"); PLYT B 6; PLYT B 0 A_JumpIf(!player||!player.cmd.yaw,"Spawn"); PLYT B 6; Goto Turn+1; SpawnAir: #### # 5; PLYA A 50; PLYL A 50; Goto SpawnAir+1; SpawnSwim: #### # 5; PLYS ABCDEFG 6; Goto SpawnSwim+1; SpawnCrouch: #### # 5; PLYC A -1; Wait; // See state needs to be defined on each subclass for the selection menu See2: #### # 5; PLAY HIJKL 4; Goto See2+1; SeeAir: #### # 5; PLYA A 50; PLYL A 50; Goto SeeAir+1; SeeSwim: #### # 5; PLYS HIJKL 4; Goto SeeSwim+1; SeeCrouch: #### # 5; PLYC ABCDE 4; Goto SeeCrouch+1; Missile: #### # 2; PLAY MNOPQA 3; Goto Spawn; MissileRep: #### # 2; PLAY R 2; PLAY S 0 A_JumpIf(!player||!(player.buttons&(BT_ATTACK|BT_ALTATTACK)),"Spawn"); PLAY S 2; PLAY T 0 A_JumpIf(!player||!(player.buttons&(BT_ATTACK|BT_ALTATTACK)),"Spawn"); PLAY T 2; PLAY U 0 A_JumpIf(!player||!(player.buttons&(BT_ATTACK|BT_ALTATTACK)),"Spawn"); PLAY U 2; PLAY V 0 A_JumpIf(!player||!(player.buttons&(BT_ATTACK|BT_ALTATTACK)),"Spawn"); PLAY V 2; PLAY R 0 A_JumpIf(!player||!(player.buttons&(BT_ATTACK|BT_ALTATTACK)),"Spawn"); Goto MissileRep+1; MissileRepStill: #### # 2; PLAY R 2; PLAY R 0 A_JumpIf(!player||!(player.buttons&(BT_ATTACK|BT_ALTATTACK)),"Spawn"); Goto MissileRepStill+1; Reload: #### # 3; PLYR ABCDEFGH 4; Goto Spawn; Landing: #### # 4; PLYL A 3; Goto Spawn; Pain: #### # 0 A_Jump(256,1,3,5,7); #### # 2; PLAY W 3 A_PainDT(); Goto Spawn; #### # 2; PLAY Y 3 A_PainDT(); Goto Spawn; #### # 2; PLAY Z 3 A_PainDT(); Goto Spawn; Pain.Decapitated: #### # 2; PLAY X 3 A_PainDT(); Goto Spawn; Death.Decapitated: #### # 0 A_HeadPop(); PLD4 A 3 A_PlayerScreamDT(); PLD4 B 3 A_NoBlocking(); PLD4 CDEFGHIJKLMNO 3; PLD4 P 1 A_DMFade(); Wait; Death: #### # 0 A_JumpIf(vel.length()>20,"Death11"); #### # 0 A_JumpIf(vel.length()>10,"Death1"); #### # 0 A_Jump(256,"Death2","Death3","Death7","Death8"); Death11: #### # 3; PD11 A 3 A_PlayerScreamDT(); PD11 B 3 A_NoBlocking(); PD11 CDEFGHIJKLMNOPQ 3; PD11 R 1 A_DMFade(); Wait; Death.Zapped: #### # 3; PLD9 A 3 A_PlayerScreamDT(); PLD9 B 3 A_NoBlocking(); PLD9 CDEFGHIJKLMNOPQRST 2; PD9B A 2 A_SetSize(-1,height*0.25); // reduce now PD9B BCDEFGHI 2; PD9B J 1 A_DMFade(); Wait; Death8: #### # 3; PLD8 A 3 A_PlayerScreamDT(); PLD8 B 3 A_NoBlocking(); PLD8 CDEFGHIJKLMNOPQ 3; PLD8 R 1 A_DMFade(); Wait; Death7: #### # 3; PLD7 A 3 A_PlayerScreamDT(); PLD7 B 3 A_NoBlocking(); PLD7 CDEFGHIJKLMNOPQRST 3; PLD7 U 1 A_DMFade(); Wait; Death3: #### # 3; PLD3 A 3 A_PlayerScreamDT(); PLD3 B 3 A_NoBlocking(); PLD3 CDEFGHIJKL 3; PLD3 M 1 A_DMFade(); Wait; Death2: #### # 3; PLD2 A 3 A_PlayerScreamDT(); PLD2 B 3 A_NoBlocking(); PLD2 CDEFGHIJKLMNO 3; PLD2 P 1 A_DMFade(); Wait; Death1: #### # 3; PLD1 A 3 A_PlayerScreamDT(); PLD1 B 3 A_NoBlocking(); PLD1 CDEFGHIJKL 3; PLD1 M 1 A_DMFade(); Wait; XDeath: TNT1 A 350 { A_XScreamDT(); A_NoBlocking(); A_GibMe(); } TNT1 A 1 A_CheckPlayerDone(); Wait; Taunt1: #### # 5; PLT1 ABCDEFGHIJKLMNOPQRST 3; Goto Spawn; Taunt2: #### # 5; PLT2 ABCDEFGHIJKLMNOPQR 4; PLT2 R 8; PLAY A 3; Goto Spawn; Taunt3: #### # 5; PLT3 ABCDEFGHIJKLMNO 3; Goto Spawn; Taunt4: #### # 5; PLT4 ABCDEFGHIJKLMNO 3; Goto Spawn; Ice: PICE A 5 A_FreezeDeath(); PICE A 1 A_FreezeDeathChunksDT(); Wait; } } Class UTUnderSound : Actor { enum EFluidType { FLUID_WATER, FLUID_SLIME, FLUID_LAVA, FLUID_NITRO }; int curfluid, lastfluid; String fluidsounds[4]; Default { +NOBLOCKMAP; +NOGRAVITY; +DONTSPLASH; +NOINTERACTION; } int GetFluid() { int thisfluid = FLUID_WATER; // try to guess where we are for ( int i=0; i20,"Death5"); #### # 0 A_JumpIf(vel.length()>10,"Death2"); #### # 0 A_Jump(256,"Death1","Death3","Death4","Death7"); Death7: #### # 3; PLD7 A 3 A_PlayerScreamDT(); PLD7 B 3 A_NoBlocking(); PLD7 CDEFGHIJ 3; PLD7 K 1 A_DMFade(); Wait; Death5: #### # 0 A_LegPop(); PLD5 A 3 A_PlayerScreamDT(); PLD5 B 3 A_NoBlocking(); PLD5 CDEFGHIJKL 3; PLD5 M 1 A_DMFade(); Wait; Death4: #### # 3; PLD4 A 3 A_PlayerScreamDT(); PLD4 B 3 A_NoBlocking(); PLD4 CDEFGHIJKL 3; PLD4 M 1 A_DMFade(); Wait; Death3: #### # 3; PLD3 A 3 A_PlayerScreamDT(); PLD3 B 3 A_NoBlocking(); PLD3 CDEFGHIJKLMNO 3; PLD3 P 1 A_DMFade(); Wait; Death2: #### # 3; PLD2 A 3 A_PlayerScreamDT(); PLD2 B 3 A_NoBlocking(); PLD2 CDEFGHIJKLMNOPQ 3; PLD2 R 1 A_DMFade(); Wait; Death1: #### # 3; PLD1 A 3 A_PlayerScreamDT(); PLD1 B 3 A_NoBlocking(); PLD1 CDEFGHIJKLMNOPQRSTUV 3; PLD1 W 1 A_DMFade(); Wait; } } Class UTPlayerTFemale1 : UTPlayerTFemale { Default { Player.SoundClass "tfemale"; Player.DisplayName "$N_TFEMALE1"; Player.Portrait "Ivana"; UTPlayer.VoiceType VOICE_FemaleOne; -NOMENU; } States { See: TFEM A -1; Stop; } } Class UTPlayerTFemale2 : UTPlayerTFemale { Default { Player.SoundClass "tfemale"; Player.DisplayName "$N_TFEMALE2"; Player.Portrait "Lauren"; UTPlayer.VoiceType VOICE_FemaleTwo; -NOMENU; } States { See: TFEM B -1; Stop; } } Class UTPlayerTBoss : UTPlayer { transient CVar bossfootsteps; Default { Player.SoundClass "tboss"; Player.DisplayName "$N_TBOSS"; Player.Portrait "Xan"; UTPlayer.DollType DOLL_Boss; UTPlayer.VoiceType VOICE_Boss; // should have NOBLOOD, but Xan did bleed in vanilla UT so... // (this is what gave birth to the theory that Xan was actually Jerl Liandri himself) -NOMENU; } override void PlayFootstep( double vol ) { if ( !bossfootsteps ) bossfootsteps = CVar.GetCVar('flak_bossfootsteps',players[consoleplayer]); if ( bossfootsteps.GetBool() ) A_PlaySound("ut/bossfootstep",CHAN_5,vol); else Super.PlayFootstep(vol); } States { See: TBOS A -1; Stop; } } Class UTWeapon : Weapon { int DropAmmo; bool bExtraPickup; transient int lastnoammotic; Property DropAmmo: DropAmmo; // Drawstuffs under HUD virtual ui void PreRender( double lbottom ) {} // Drawstuffs over HUD virtual ui void PostRender( double lbottom ) {} // Future preparations for scripted textures virtual ui void RenderOverlay( RenderEvent e ) {} 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(); // don't slide on floor when dropped if ( CurState == ResolveState("Spawn")+1 ) { if ( pos.z <= floorz ) vel.xy *= 0; } if ( !Owner || !Owner.player || (Owner.player.ReadyWeapon != self) ) return; Owner.player.WeaponState |= WF_WEAPONBOBBING; // UT weapons always bob } virtual void FireEffect() { let amp = DamageAmplifier(Owner.FindInventory("DamageAmplifier",true)); if ( amp ) amp.FireEffect(); } override void DetachFromOwner() { Owner.A_StopSound(CHAN_WEAPON); Super.DetachFromOwner(); } override void OwnerDied() { if ( Owner.player && (Owner.player.ReadyWeapon == self) ) Owner.A_StopSound(CHAN_WEAPON); A_ClearRefire(); Super.OwnerDied(); } 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 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; } override void OnDrop( Actor dropper ) { Vector2 hofs = RotateVector((dropper.radius,0),dropper.angle); SetOrigin(dropper.Vec3Offset(hofs.x,hofs.y,dropper.height*0.5),false); Vector3 x, y, z; [x, y, z] = dt_CoordUtil.GetAxes(dropper.pitch,dropper.angle,dropper.roll); vel = x*12.0; vel.z += 4.0; angle = dropper.angle; pitch = 0; roll = 0; } override bool CheckAmmo( int fireMode, bool autoSwitch, bool requireAmmo, int ammocount ) { bool rslt = Super.CheckAmmo(fireMode,autoSwitch,requireAmmo,ammocount); if ( (gametic <= lastnoammotic) || rslt || !Owner.CheckLocalView() || !(Owner is 'UTPlayer') || !UTPlayer(Owner).doprintnoammo ) return rslt; lastnoammotic = gametic; Console.Printf(StringTable.Localize((Amount>1)?"$M_NOAMMO2":"$M_NOAMMO"),GetTag()); return rslt; } // For clips virtual clearscope int, int, bool, bool GetClipAmount() const { return -1, -1, false, false; } 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*(.5+.5*cos(GetAge()*30))); args[LIGHT_GREEN] = int(160*alpha*(.5+.5*cos(GetAge()*30))); args[LIGHT_BLUE] = int(255*alpha*(.5+.5*cos(GetAge()*30))); args[LIGHT_INTENSITY] = Random[Tele](10,14)*8; alpha -= 1./TICRATE; } } 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 -= 1.5/TICRATE; } } Class UTTeleportFog : Actor { Default { +NOBLOCKMAP; +NOTELEPORT; +NOGRAVITY; RenderStyle "Add"; } override void PostBeginPlay() { Super.PostBeginPlay(); Spawn("UTTeleportLight",Vec3Offset(0,0,16)); A_PlaySound ("misc/teleport"); Spawn("UTTeleportParticles",Vec3Offset(0,0,16)); } 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",Vec3Offset(0,0,16)); Spawn("UTRespawnParticles",Vec3Offset(0,0,12)); } 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.2; 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] = dt_CoordUtil.GetAxes(target.pitch,target.angle,target.roll); Vector3 origin = level.Vec3Offset(target.Vec2OffsetZ(0,0,target.player.viewz),x*ofs.x+y*ofs.y+z*ofs.z); SetOrigin(origin,true); bInvisible = (players[consoleplayer].camera != target); if ( isFrozen() ) 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.35; 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 ( isFrozen() ) 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 ( isFrozen() ) 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 ( isFrozen() ) 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] = dt_CoordUtil.GetAxes(target.pitch,target.angle,target.roll); Vector3 origin = level.Vec3Offset(target.Vec2OffsetZ(0,0,target.player.viewz),x*ofs.x+y*ofs.y+z*ofs.z); SetOrigin(origin,true); bInvisible = (players[consoleplayer].camera != target); if ( isFrozen() ) 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 "$T_REDSKULL"; Species "RedSkull"; Inventory.PickupMessage "$I_REDSKULL"; } States { Spawn: USKL A -1; Stop; } } Class UTGoldSkull : YellowSkull { Default { Tag "$T_GOLDSKULL"; Species "YellowSkull"; Inventory.PickupMessage "$I_GOLDSKULL"; } States { Spawn: USKL B -1; Stop; } } Class UTBlueSkull : BlueSkull { Default { Tag "$T_BLUESKULL"; Species "BlueSkull"; Inventory.PickupMessage "$I_BLUESKULL"; } States { Spawn: USKL C -1; Stop; } } Class UTRedKey : RedCard { Default { Tag "%T_REDKEY"; Species "RedCard"; Inventory.PickupMessage "$I_REDKEY"; } States { Spawn: UKEY A -1; Stop; } } Class UTGoldKey : YellowCard { Default { Tag "$T_GOLDKEY"; Species "YellowCard"; Inventory.PickupMessage "$I_GOLDKEY"; } States { Spawn: UKEY B -1; Stop; } } Class UTBlueKey : BlueCard { Default { Tag "$T_BLUEKEY"; Species "BlueCard"; Inventory.PickupMessage "$I_BLUEKEY"; } States { Spawn: UKEY C -1; Stop; } } Class ShredCorpseHitbox : Actor { int accdamage; Vector3 lastvel; bool wasonair; Default { +NOGRAVITY; -SOLID; +DONTSPLASH; +SHOOTABLE; +NOTELEPORT; Health int.max; } override void PostBeginPlay() { if ( !target ) { Destroy(); return; } accdamage = target.Health; A_SetSize(target.radius,target.height); if ( target.bIceCorpse ) bNOBLOOD = true; // ice statue } override void Tick() { Super.Tick(); if ( (!(target is 'UTPlayer') && !flak_corpsedamage) || !target || (target.Health > 0) || target.InStateSequence(target.CurState,target.FindState("XDeath",true)) ) { Destroy(); return; } SetOrigin(target.pos,true); A_SetSize(target.radius,target.height); if ( target.pos.z > target.floorz ) wasonair = true; else { if ( wasonair ) { A_PlaySound("misc/corpsefall",CHAN_BODY,clamp(-lastvel.z*0.2,0.1,1.0)); if ( lastvel.z < -20 ) DamageMobj(null,null,int.max,'Falling'); } wasonair = false; } lastvel = target.vel; } override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle ) { // somehow target can spontaneously stop existing while this is happening if ( !target ) return 0; if ( (target is 'UTPlayer') && (mod == 'Zapped') && (target.sprite == target.GetSpriteIndex('PLD9')) ) { // keep the zapping action on target.SetState(target.FindState("Death.Zapped")+Random[ZapMe](2,8)); damage /= 4; // push the corpse target.vel.xy += RotateVector((1,0),angle)*damage*0.1; // keep it afloat target.vel.z = max(0.1,target.vel.z+0.1); } accdamage -= damage; int gibhealth = (target.GibHealth==int.min)?-target.SpawnHealth():target.GibHealth; if ( accdamage < gibhealth ) { // force chunks if frozen if ( target.bIceCorpse ) { target.bSHATTERING = true; if ( target is 'UTPlayer' ) UTPlayer(target).A_FreezeDeathChunksDT(); else target.A_FreezeDeathChunks(); Destroy(); return 0; } // force gib (cheap ATM) State gib = target.FindState("XDeath",true); if ( !gib ) gib = target.FindState("Death.Extreme",true); if ( gib ) target.SetState(gib); Destroy(); } return 0; } } // imitates UE1 light type LT_TexturePaletteOnce/LT_TexturePaletteLoop Class PaletteLight : DynamicLight { Color pal[256]; bool IsLooping; Default { Tag "Explosion"; DynamicLight.Type "Point"; Args 0,0,0,80; ReactionTime 15; } private void UpdateLight() { int index = 255-((255*ReactionTime)/abs(default.ReactionTime)); args[LIGHT_RED] = pal[index].r; args[LIGHT_GREEN] = pal[index].g; args[LIGHT_BLUE] = pal[index].b; } override void PostBeginPlay() { Super.PostBeginPlay(); int lump = Wads.CheckNumForFullname(String.Format("palettes/%s.pal",GetTag())); String paldat = Wads.ReadLump(lump); for ( int i=0; i<256; i++ ) { pal[i].r = paldat.ByteAt(i*3); pal[i].g = paldat.ByteAt(i*3+1); pal[i].b = paldat.ByteAt(i*3+2); } if ( ReactionTime < 0 ) { ReactionTime = -ReactionTime; IsLooping = true; } UpdateLight(); } override void Tick() { Super.Tick(); if ( isFrozen() ) return; ReactionTime--; if ( ReactionTime < 0 ) { if ( !IsLooping ) { Destroy(); return; } else ReactionTime = abs(default.ReactionTime); } UpdateLight(); } } // A bParticles type actor, reads from the anivfile directly // Tag: semicolon-separated, starting with the model, then the particle class // of UTMeshParticle to use Class UTParticleMesh : Actor { Array px, py, pz; Array parts; Class pclass; int numframes, numverts; double animframe, animrate; Default { Tag "telepo;UTMeshParticle"; Args 28; // animation rate in fps ReactionTime 35; // total lifespan XScale 0.015; // scale of the model YScale 0.03; // scale of the model +NOGRAVITY; +NOCLIP; +DONTSPLASH; +NOTELEPORT; } override void PostBeginPlay() { Super.PostBeginPlay(); Array strs; strs.Clear(); GetTag().Split(strs,";"); pclass = strs[1]; int lump = Wads.CheckNumForFullname(String.Format("models/%s_a.3d",strs[0])); String anivfile = Wads.ReadLump(lump); numframes = anivfile.ByteAt(0); numframes |= anivfile.ByteAt(1)<<8; int fsiz = anivfile.ByteAt(2); fsiz |= anivfile.ByteAt(3)<<8; numverts = fsiz/4; px.Resize(numverts*numframes); py.Resize(numverts*numframes); pz.Resize(numverts*numframes); parts.Resize(numverts); int cursor = 4; for ( int i=0; i>11)&0x7ff)<<21, az = ((avert>>22)&0x3ff)<<22; px[i] = ax/2097152.; py[i] = ay/2097152.; pz[i] = az/4194304.; } Vector3 x, y, z; [x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll); for ( int i=0; i= numframes) || (ReactionTime <= 0) ) { Destroy(); return; } int framea = int(floor(animframe)), frameb = int(ceil(animframe)); double theta = animframe-framea; Vector3 posa, posb, ipos; Vector3 x, y, z; [x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll); for ( int i=0; i ShootThroughList; Actor ignoreme; static play void DoTrail( Actor target, Vector3 pos, Vector3 dir, int dist, int bubblechance ) { let t = new("UTBulletTrail"); t.ignoreme = target; t.ShootThroughList.Clear(); t.Trace(pos,level.PointInSector(pos.xy),dir,dist,0); for ( int i=0; i= 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; transient CVar str; GenericFlash Setup( Actor camera, Color c, int d ) { alpha = 1.0; col = c; duration = d; cam = camera; return self; } override bool Tick() { if ( duration > 0 ) alpha -= 1./duration; return (alpha<=0)||(!cam); } override void Draw( int bottom, int visibility ) { if ( automapactive || (visibility != BaseStatusBar.HUDMSGLayer_UnderHUD) ) return; if ( cam && (players[consoleplayer].camera != cam) ) return; if ( !str ) str = CVar.GetCVar('flak_flashstrength',players[consoleplayer]); Screen.Dim(col,(col.a/255.)*alpha*str.GetFloat(),0,0,Screen.GetWidth(),Screen.GetHeight()); } } Class QueuedFlash { Color c; int duration; int tic; Actor cam; } Class UTStaticHandler : StaticEventHandler { ui TextureID tex[2]; ui void StartMenu() { tex[0] = TexMan.CheckForTexture("DTLogo",TexMan.Type_Any); CVar protomenu = CVar.GetCVar('flak_protomenu',players[consoleplayer]); if ( !protomenu ) return; // this can happen int proto = protomenu.GetInt(); if ( proto ) { tex[1] = TexMan.CheckForTexture("protobg",TexMan.Type_Any); if ( gamestate != GS_TITLELEVEL ) return; if ( proto > 1 ) S_ChangeMusic("menu2"); else S_ChangeMusic("xyzdMenu"); } else { tex[1] = TexMan.CheckForTexture("finalbg",TexMan.Type_Any); if ( gamestate != GS_TITLELEVEL ) return; S_ChangeMusic("utmenu23"); } } override void ConsoleProcess( ConsoleEvent e ) { if ( e.Name ~== "refreshmenu" ) StartMenu(); } override void PostUiTick() { if ( gametic <= 0 ) StartMenu(); } override void RenderUnderlay( RenderEvent e ) { if ( gamestate != GS_TITLELEVEL ) return; double ar = Screen.GetAspectRatio(); Vector2 tsize = TexMan.GetScaledSize(tex[0]); double sar = tsize.x/tsize.y; Vector2 vsize; if ( sar > ar ) vsize = (tsize.x,tsize.x/ar); else if ( sar < ar ) vsize = (tsize.y*ar,tsize.y); else vsize = tsize; Screen.Dim("Black",1.0,0,0,Screen.GetWidth(),Screen.GetHeight()); Screen.DrawTexture(tex[0],false,(vsize.x-tsize.x)/2,(vsize.y-tsize.y)/2,DTA_VirtualWidthF,vsize.x,DTA_VirtualHeightF,vsize.y,DTA_KeepRatio,true); } override void RenderOverlay( RenderEvent e ) { // well this if sure is a long one if ( players[consoleplayer].camera.player && players[consoleplayer].camera.player.ReadyWeapon && (players[consoleplayer].camera.player.ReadyWeapon is 'UTWeapon') ) UTWeapon(players[consoleplayer].camera.player.ReadyWeapon).RenderOverlay(e); if ( !menuactive || (Menu.GetCurrentMenu() is 'ConversationMenu') ) return; if ( !CVar.GetCVar('flak_showmenu',players[consoleplayer]).GetBool() ) return; Screen.Dim("Black",1.0,0,0,Screen.GetWidth(),Screen.GetHeight()); Screen.DrawTexture(tex[1],true,0,0,DTA_VirtualWidth,1024,DTA_VirtualHeight,768); } } // nothing at all Class UTNothing : Actor { States { Spawn: TNT1 A 1; Stop; } } Class UTMainHandler : EventHandler { Array flashes; TextureID gframes[166]; transient ui int gframe; transient ui Font rfont; bool isbd, ispb; override void CheckReplacement( ReplaceEvent e ) { if ( e.IsFinal ) return; if ( (e.Replacee == 'Chainsaw') || (e.Replacee == 'Gauntlets') ) { if ( Random[Replacements](0,1) ) e.Replacement = 'UTChainsaw'; else e.Replacement = 'Enforcer'; } else if ( (e.Replacee == 'Fist') || (e.Replacee == 'Staff') ) e.Replacement = 'ImpactHammer'; else if ( (e.Replacee == 'Pistol') || (e.Replacee == 'GoldWand') ) e.Replacement = 'Enforcer'; else if ( (e.Replacee == 'Shotgun') || (e.Replacee == 'SuperShotgun') || (e.Replacee == 'Crossbow') ) { 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') || (e.Replacee == 'Blaster') ) { if ( Random[Replacements](0,2) ) e.Replacement = 'PulseGun'; else e.Replacement = 'Ripper2'; } else if ( (e.Replacee == 'RocketLauncher') || (e.Replacee == 'PhoenixRod') ) { if ( Random[Replacements](0,1) ) e.Replacement = 'FlakCannon'; else e.Replacement = 'UTRocketLauncher'; } else if ( (e.Replacee == 'PlasmaRifle') || (e.Replacee == 'SkullRod') ) { if ( Random[Replacements](0,1) ) e.Replacement = 'Minigun'; else e.Replacement = 'SniperRifle'; } else if ( (e.Replacee == 'BFG9000') || (e.Replacee == 'Mace') ) e.Replacement = 'WarheadLauncher'; else if ( (e.Replacee == 'Clip') || (e.Replacee == 'GoldWandAmmo') || (e.Replacee == 'GoldWandHefty') ) 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 == 'BlasterAmmo') || (e.Replacee == 'BlasterHefty') ) { if ( Random[Replacements](0,2) ) e.Replacement = 'PulseAmmo'; else e.Replacement = 'RipperAmmo'; } else if ( (e.Replacee == 'Shell') || (e.Replacee == 'CrossbowAmmo') ) { 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') || (e.Replacee == 'CrossbowHefty') ) { if ( Random[Replacements](0,1) ) e.Replacement = 'BioAmmo'; else e.Replacement = 'ShockAmmo'; } else if ( (e.Replacee == 'RocketAmmo') || (e.Replacee == 'PhoenixRodAmmo') || (e.Replacee == 'MaceAmmo') ) { 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') || (e.Replacee == 'PhoenixRodHefty') || (e.Replacee == 'MaceHefty') ) { if ( Random[Replacements](0,1) ) e.Replacement = 'FlakAmmo'; else e.Replacement = 'UTRocketAmmo'; } else if ( (e.Replacee == 'Cell') || (e.Replacee == 'SkullRodAmmo') ) { 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') || (e.Replacee == 'SkullRodHefty') ) { 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 = 'UTInvulnerability'; else if ( e.Replacee == 'Berserk' ) e.Replacement = 'UDamage'; else if ( (e.Replacee == 'ArtiTomeOfPower') || (e.Replacee == 'ArtiEgg') || (e.Replacee == 'ArtiPork') ) e.Replacement = 'ActUDamage'; else if ( e.Replacee == 'Soulsphere' ) e.Replacement = 'UTHealthPack'; else if ( e.Replacee == 'ArtiSuperHealth' ) e.Replacement = 'ActHealthPack'; else if ( (e.Replacee == 'Megasphere') || (e.Replacee == 'EnchantedShield') || (e.Replacee == 'ArtiBoostArmor') ) e.Replacement = 'UTShieldBelt'; else if ( (e.Replacee == 'ArtiInvulnerability') || (e.Replacee == 'ArtiInvulnerability2') ) e.Replacement = 'ActUTInvulnerability'; else if ( (e.Replacee == 'Allmap') || (e.Replacee == 'SuperMap') ) e.Replacement = 'UTMapRevealer'; else if ( e.Replacee == 'BlurSphere' ) e.Replacement = 'UTInvisibility'; else if ( e.Replacee == 'ArtiInvisibility' ) e.Replacement = 'ActUTInvisibility'; else if ( e.Replacee == 'Infrared' ) e.Replacement = 'UTNightVision'; else if ( e.Replacee == 'ArtiTorch' ) e.Replacement = 'ActUTNightVision'; else if ( e.Replacee == 'RadSuit' ) e.Replacement = 'UTJumpBoots'; else if ( (e.Replacee == 'ArtiFly') || (e.Replacee == 'ArtiSpeedBoots') ) e.Replacement = 'ActJumpBoots'; else if ( (e.Replacee == 'Backpack') || (e.Replacee == 'BagOfHolding') || (e.Replacee == 'ArtiHealingRadius') ) e.Replacement = 'UTBackpack'; else if ( (e.Replacee == 'ArmorBonus') || (e.Replacee == 'ArtiTimeBomb') || (e.Replacee is 'ArtiPoisonBag') || (e.Replacee is 'ArtiBlastRadius') ) e.Replacement = 'UTArmorBonus'; else if ( (e.Replacee == 'HealthBonus') || (e.Replacee == 'CrystalVial') ) e.Replacement = 'UTHealthBonus'; else if ( (e.Replacee == 'GreenArmor') || (e.Replacee == 'AmuletOfWarding') || (e.Replacee == 'PlatinumHelm') ) e.Replacement = 'UTThighPads'; else if ( e.Replacee == 'Silvershield' ) { if ( Random[Replacements](0,1) ) e.Replacement = 'UTThighPads'; else e.Replacement = 'UTBodyArmor'; } else if ( (e.Replacee == 'BlueArmor') || (e.Replacee == 'FalconShield') || (e.Replacee == 'MeshArmor') ) e.Replacement = 'UTBodyArmor'; else if ( e.Replacee == 'Stimpack' ) e.Replacement = 'UTMedBox'; else if ( e.Replacee == 'Medikit' ) e.Replacement = 'UTHealthBox'; else if ( e.Replacee == 'ArtiHealth' ) { if ( !Random[Replacements](0,3) ) e.Replacement = 'ActHealthBox'; else e.Replacement = 'ActMedBox'; } else if ( (e.Replacee == 'ArtiTeleport') || (e.Replacee == 'ArtiTeleportOther') || (e.Replacee == 'ArtiDarkServant') ) { // I have no idea what to replace this with, so just have some random stuff if ( (gameinfo.gametype&GAME_Hexen) && Random[Replacements](0,1) ) e.Replacement = 'UTBackpack'; else if ( Random[Replacements](0,1) ) e.Replacement = 'ActHealthPack'; else e.Replacement = 'ActUDamage'; } 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'; else if ( flak_blood && ((e.Replacee == 'Blood') || (e.Replacee == 'BloodSplatter') || (e.Replacee == 'AxeBlood')) ) e.Replacement = 'UTBlood'; else if ( e.Replacee == 'KeyYellow' ) e.Replacement = 'UTHereticYellowKey'; else if ( e.Replacee == 'KeyGreen' ) e.Replacement = 'UTHereticGreenKey'; else if ( e.Replacee == 'KeyBlue' ) e.Replacement = 'UTHereticBlueKey'; else if ( e.Replacee.GetClassName() == 'KeyRed' ) { // pretty sure that is a standardized name, so it should work for any heretic mod that adds it e.Replacement = 'UTHereticRedKey'; } // Hexen weapon replacements will attempt to follow some sort of progression // (also additional support for Hexmas because of course) else if ( (e.Replacee == 'FWeapFist') || (e.Replacee == 'CWeapMace') || (e.Replacee == 'MWeapWand') ) e.Replacement = 'ImpactHammer'; else if ( e.Replacee == 'FWeapAxe' ) e.Replacement = 'BioRifle'; else if ( e.Replacee == 'CWeapStaff' ) e.Replacement = 'ShockRifle'; else if ( e.Replacee == 'MWeapFrost' ) e.Replacement = 'PulseGun'; else if ( (e.Replacee == 'FWeaponPiece3') || (e.Replacee == 'FWeapQuietus') || (e.Replacee.GetClassName() == 'mkFullQuietus') ) e.Replacement = 'FlakCannon'; else if ( e.Replacee == 'CWeaponPiece3' ) e.Replacement = 'UTBackpack'; else if ( e.Replacee == 'MWeaponPiece3' ) e.Replacement = 'Enforcer'; else if ( e.Replacee == 'FWeapHammer' ) e.Replacement = 'Ripper2'; else if ( e.Replacee == 'CWeapFlame' ) e.Replacement = 'Minigun'; else if ( e.Replacee == 'MWeapLightning' ) e.Replacement = 'SniperRifle'; else if ( e.Replacee == 'FWeaponPiece2' ) e.Replacement = 'UTChainsaw'; else if ( (e.Replacee == 'CWeaponPiece2') || (e.Replacee == 'CWeapWraithverge') || (e.Replacee.GetClassName() == 'mkFullWraithverge') ) e.Replacement = 'UTRocketLauncher'; else if ( e.Replacee == 'MWeaponPiece2' ) e.Replacement = 'UTBackpack'; else if ( e.Replacee == 'FWeaponPiece1' ) e.Replacement = 'WarheadAmmo'; else if ( e.Replacee == 'CWeaponPiece1' ) e.Replacement = 'UTBackpack'; else if ( (e.Replacee == 'MWeaponPiece1') || (e.Replacee == 'MWeapBloodscourge') || (e.Replacee.GetClassName() == 'mkFullBloodscourge') ) e.Replacement = 'WarheadLauncher'; else if ( e.Replacee == 'Mana1' ) e.Replacement = 'UTMinorAmmoBox'; else if ( e.Replacee == 'Mana2' ) e.Replacement = 'UTMediumAmmoBox'; else if ( e.Replacee == 'Mana3' ) e.Replacement = 'UTMajorAmmoBox'; else if ( e.Replacee == 'ArtiBoostMana' ) e.Replacement = 'ActUTFullAmmoBox'; // TODO Strife replacements } 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 ) { isbd = ispb = false; for ( int i=0; i=256)&&(i<=260)))?baseceil:baseflor); else if ( gameinfo.gametype&GAME_HERETIC ) level.sectors[i].SetTexture(0,((i==47)||((i>=144)&&(i<=148)))?baseceil:baseflor); else if ( gameinfo.gametype&GAME_HEXEN ) level.sectors[i].SetTexture(0,((i==47)||((i>=152)&&(i<=155)))?baseceil:baseflor); level.sectors[i].SetXScale(0,2.); level.sectors[i].SetYScale(0,2.); } if ( level.sectors[i].GetTexture(1) == deftex ) { if ( gameinfo.gametype&GAME_DOOM ) level.sectors[i].SetTexture(1,((i==47)||((i>=256)&&(i<=260)))?baseflor:baseceil); else if ( gameinfo.gametype&GAME_HERETIC ) level.sectors[i].SetTexture(1,((i==47)||((i>=144)&&(i<=148)))?baseflor:baseceil); else if ( gameinfo.gametype&GAME_HEXEN ) level.sectors[i].SetTexture(1,((i==47)||((i>=152)&&(i<=155)))?baseflor:baseceil); level.sectors[i].SetXScale(1,2.); level.sectors[i].SetYScale(1,2.); } } for ( int i=0; i= gametic ) continue; flashes.Delete(i); i--; } } override void PostUiTick() { for ( int i=0; i 0) || e.Thing.bKilled || e.Thing.bCorpse ) return; if ( !e.Thing.player && !e.Thing.bIsMonster && !e.Thing.bCountKill ) return; if ( (e.Inflictor && e.Inflictor.player && (e.Inflictor != e.Thing)) || (e.DamageSource && e.DamageSource.player && (e.DamageSource != e.Thing)) ) { Weapon saw = Weapon(e.Inflictor.FindInventory("UTChainsaw")); if ( !saw ) saw = Weapon(e.DamageSource.FindInventory("UTChainsaw")); bool current = false; if ( e.Inflictor.player && e.Inflictor.player.ReadyWeapon == saw ) current = true; if ( e.DamageSource.player && (e.DamageSource.player.ReadyWeapon == saw) ) current = true; if ( flak_sawammo && saw && (saw.Ammo1.Amount < 40) && !current && !Random[SawDrop](0,9) ) { let a = Actor.Spawn("ChainsawAmmo",e.Thing.Vec3Offset(0,0,e.Thing.Height*.5)); a.TossItem(); } } } override void WorldThingDied( WorldEvent e ) { if ( e.Thing.bDONTGIB ) return; // gibbers if ( flak_gibs && !e.Thing.bNOBLOOD && (e.Thing.FindState("XDeath",true) || e.Thing.FindState("Death.Extreme",true)) && ((e.Inflictor && e.Inflictor.bEXTREMEDEATH) || (e.Thing.Health < e.Thing.GetGibHealth())) && (!e.Inflictor || !e.Inflictor.bNOEXTREMEDEATH) ) { // players use their own gibber if ( e.Thing is 'UTPlayer' ) return; // generic gibbing let a = Actor.Spawn("UTGibber",e.Thing.pos); a.vel = e.Thing.vel; a.Scale = e.Thing.Scale; a.Translation = e.Thing.BloodTranslation; if ( e.Thing.BloodColor ) a.SetShade(e.Thing.BloodColor); else a.SetShade(gameinfo.defaultbloodcolor); a.A_SetSize(e.Thing.radius,e.Thing.height); e.Thing.A_XScream(); e.Thing.A_NoBlocking(); e.Thing.A_BossDeath(); e.Thing.SetStateLabel("Null"); // instead of destroying, should prevent random VM aborts return; } // attach damage accumulator for corpses if ( !(e.Thing is 'UTPlayer') && !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(EventHandler.Find("UTMainHandler")); if ( !hnd ) return; // not supposed to happen 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) || a.bCANNOTPUSH || (a.Mass >= 10000000) ) continue; Vector3 midpoint = a.Vec3Offset(0,0,a.height*0.5); Vector3 dir = Level.Vec3Diff(Source.pos,midpoint); double dist = max(1,dir.length()); double damagescale = 1-max(0,(dist-a.radius)/ExplosionRadius); dir = dir/dist; if ( (a is 'ShredCorpseHitbox') && a.target ) a.target.vel += dir*damagescale*(MomentumTransfer/(Thinker.TICRATE*a.target.mass)); else a.vel += dir*damagescale*(MomentumTransfer/(Thinker.TICRATE*a.mass)); } } // Same for this static void DoKnockback( Actor Victim, Vector3 HitDirection, double MomentumTransfer ) { if ( !Victim || !Victim.bSHOOTABLE || Victim.bCANNOTPUSH || (Victim.Mass >= 10000000) ) return; if ( (Victim is 'ShredCorpseHitbox') && Victim.target ) Victim.target.vel += HitDirection*(MomentumTransfer/(Thinker.TICRATE*Victim.target.Mass)); else 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; } override void UiTick() { if ( !ispb && !isbd ) return; if ( !(gametic%2) ) { if ( !(gframe%11) && gframe < 160 ) { S_Sound("ut/malejump",CHAN_VOICE|CHAN_UI,(gframe>100)?.5:1.); S_Sound("ut/malejump",CHAN_WEAPON|CHAN_UI,(gframe>100)?.5:1.); } if ( ((gframe >= 37) && (gframe < 49) && !((gframe-37)%3)) || ((gframe >= 89) && (gframe < 101) && !((gframe-89)%3)) || ((gframe >= 141) && (gframe < 153) && !((gframe-141)%3)) ) { S_Sound("ut/land",CHAN_BODY|CHAN_UI,(gframe>100)?.5:1.); S_Sound("ut/land",CHAN_ITEM|CHAN_UI,(gframe>100)?.5:1.); } gframe++; } if ( gframe >= 166 ) gframe = 0; } // almost forgot about this override void RenderUnderlay( RenderEvent e ) { if ( !ispb && !isbd ) return; double ar = Screen.GetAspectRatio(); Vector2 tsize = TexMan.GetScaledSize(gframes[gframe]); double sar = tsize.x/tsize.y; Vector2 vsize; if ( sar > ar ) vsize = (tsize.y*ar,tsize.y); else if ( sar < ar ) vsize = (tsize.x,tsize.x/ar); else vsize = tsize; Screen.DrawTexture(gframes[gframe],false,(vsize.x-tsize.x)/2,(vsize.y-tsize.y)/2,DTA_VirtualWidthF,vsize.x,DTA_VirtualHeightF,vsize.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add); String txtme = "STOP LOADING BRUTAL DOOM WITH EVERYTHING"; if ( !rfont ) rfont = Font.GetFont('USmallFont'); Vector2 bcoord = ((CleanWidth-rfont.StringWidth(txtme))*.5,CleanHeight*0.8); for ( int i=0; i