note description: "[ p_mobj.c Moving object handling. Spawn functions. ]" license: "[ Copyright (C) 1993-1996 by id Software, Inc. Copyright (C) 2005-2014 Simon Howard Copyright (C) 2021 Ilgiz Mustafin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ]" class P_MOBJ inherit MOBJFLAG_T STATENUM_T MOBJTYPE_T create make feature i_main: I_MAIN make (a_i_main: like i_main) do i_main := a_i_main itemrespawnque := {REF_ARRAY_CREATOR [MAPTHING_T]}.make_ref_array ({P_LOCAL}.itemquesize) create itemrespawntime.make_filled (0, 0, {P_LOCAL}.itemquesize) end feature -- P_RemoveMobj iquehead: INTEGER_32 assign set_iquehead set_iquehead (a_iquehead: like iquehead) do iquehead := a_iquehead end iquetail: INTEGER_32 assign set_iquetail set_iquetail (a_iquetail: like iquetail) do iquetail := a_iquetail end p_spawnplayer (mthing: MAPTHING_T) -- Called when a player is spawned on the level. -- Most of the player structure stays unchanged -- between levels. local p: PLAYER_T x, y, z: FIXED_T mobj: MOBJ_T i: INTEGER_32 do if not i_main.G_game.Playeringame [mthing.type - 1.to_integer_32] then else p := i_main.G_game.Players [mthing.type - 1.to_integer_32] if p.playerstate = {PLAYER_T}.pst_reborn then i_main.G_game.g_playerreborn (mthing.type - 1.to_integer_32) end x := create {FIXED_T}.from_integer (mthing.x.to_integer_32 |<< {M_FIXED}.fracbits) y := create {FIXED_T}.from_integer (mthing.y.to_integer_32 |<< {M_FIXED}.fracbits) z := create {FIXED_T}.from_integer ({P_LOCAL}.onfloorz) mobj := p_spawnmobj (x, y, z, {INFO}.mt_player) if mthing.type > 1 then mobj.flags := mobj.flags | (mthing.type - 1).to_integer_32 |<< Mf_transshift end mobj.angle := {R_MAIN}.ang45 * create {ANGLE_T}.from_natural ((mthing.angle.to_natural_32 // 45)) mobj.player := p mobj.health := p.health p.mo := mobj p.playerstate := {PLAYER_T}.pst_live p.refire := 0 p.message := Void p.damagecount := 0 p.bonuscount := 0 p.extralight := 0 p.fixedcolormap := 0 p.viewheight := create {FIXED_T}.from_integer ({P_LOCAL}.viewheight) i_main.P_pspr.p_setuppsprites (p) if i_main.G_game.deathmatch then from i := 0 until i >= {CARD_T}.numcards loop p.cards [i] := True i := i + 1 end end if mthing.type - 1.to_integer_32 = i_main.G_game.consoleplayer then i_main.St_stuff.st_start i_main.Hu_stuff.hu_start end end end p_spawnmapthing (mthing: MAPTHING_T) local i: INTEGER_32 b: INTEGER_32 mobj: MOBJ_T x, y, z: FIXED_T returned: BOOLEAN do if mthing.type = 11 then if i_main.P_setup.deathmatch_p < 10 then i_main.P_setup.deathmatchstarts [i_main.P_setup.deathmatch_p] := mthing i_main.P_setup.deathmatch_p := i_main.P_setup.deathmatch_p + 1 end elseif mthing.type <= 4 then i_main.P_setup.playerstarts [mthing.type - 1] := mthing if not i_main.G_game.deathmatch then p_spawnplayer (mthing) end elseif not i_main.G_game.netgame and mthing.options & 16 /= 0 then else if i_main.G_game.gameskill = {G_GAME}.sk_baby then b := 1 elseif i_main.G_game.gameskill = {G_GAME}.sk_nightmare then b := 4 else b := 1 |<< (i_main.G_game.gameskill - 1) end if mthing.options.to_integer_32 & b = 0 then returned := True end if not returned then from i := 0 until i >= {INFO}.nummobjtypes or else mthing.type.to_integer_32 = {INFO}.mobjinfo [i].doomednum loop i := i + 1 end if i = {INFO}.nummobjtypes then {I_MAIN}.i_error ("P_SpawnMapThing: Unknown type " + mthing.type.out + " at (" + mthing.x.out + ", " + mthing.y.out + ")") end if i_main.G_game.deathmatch and {INFO}.mobjinfo [i].flags & Mf_notdmatch /= 0 then returned := True end end if not returned then if i_main.D_main.nomonsters and (i = {INFO}.mt_skull or {INFO}.mobjinfo [i].flags & Mf_countkill /= 0) then returned := True end end if not returned then x := create {FIXED_T}.from_integer (mthing.x.to_integer_32 |<< {M_FIXED}.fracbits) y := create {FIXED_T}.from_integer (mthing.y.to_integer_32 |<< {M_FIXED}.fracbits) if {INFO}.mobjinfo [i].flags & Mf_spawnceiling /= 0 then z := create {FIXED_T}.from_integer ({P_LOCAL}.onceilingz) else z := create {FIXED_T}.from_integer ({P_LOCAL}.onfloorz) end mobj := p_spawnmobj (x, y, z, i) mobj.spawnpoint := mthing if mobj.tics > 0 then mobj.tics := 1 + (i_main.M_random.p_random \\ mobj.tics) end if mobj.flags & Mf_countkill /= 0 then i_main.G_game.totalkills := i_main.G_game.totalkills + 1 end if mobj.flags & Mf_countitem /= 0 then i_main.G_game.totalitems := i_main.G_game.totalitems + 1 end mobj.angle := create {ANGLE_T}.from_natural (({R_MAIN}.ang45.as_integer_32 * (mthing.angle.to_integer_32 // 45)).to_natural_32) if mthing.options.to_integer_32 & {DOOMDEF_H}.mtf_ambush /= 0 then mobj.flags := mobj.flags | Mf_ambush end end end end feature -- P_SpawnMobj p_spawnmobj (x, y, z: FIXED_T; type: INTEGER_32): MOBJ_T local mobj: MOBJ_T st: STATE_T info: MOBJINFO_T do create mobj.make info := {INFO}.mobjinfo [type] mobj.type := type mobj.info := info mobj.x := x mobj.y := y mobj.radius := create {FIXED_T}.from_integer (info.radius) mobj.height := create {FIXED_T}.from_integer (info.height) mobj.flags := info.flags mobj.health := info.spawnhealth if i_main.G_game.gameskill /= {DOOMDEF_H}.sk_nightmare then mobj.reactiontime := info.reactiontime end mobj.lastlook := i_main.M_random.p_random \\ {DOOMDEF_H}.maxplayers st := {INFO}.states [info.spawnstate] mobj.state := st mobj.tics := st.tics.to_integer_32 mobj.sprite := st.sprite mobj.frame := st.frame.to_integer_32 i_main.P_maputl.p_setthingposition (mobj) after_p_set_thing_position (mobj, z) mobj.thinker.function := agent p_mobjthinker (mobj) i_main.P_tick.p_addthinker (mobj) Result := mobj end after_p_set_thing_position (mobj: MOBJ_T; z: FIXED_T) -- Part of P_SpawnMobj for updating coords from subsector info do check attached mobj.subsector as subsector then check attached subsector.sector as sector then mobj.floorz := sector.floorheight mobj.ceilingz := sector.ceilingheight end end if z = create {FIXED_T}.from_integer ({P_LOCAL}.onfloorz) then mobj.z := mobj.floorz elseif z = create {FIXED_T}.from_integer ({P_LOCAL}.onceilingz) then check attached mobj.info as attached_info then mobj.z := mobj.ceilingz - create {FIXED_T}.from_integer (attached_info.height) end else mobj.z := z end end feature p_mobjthinker (mobj: MOBJ_T) local returned: BOOLEAN do if mobj.momx /= create {FIXED_T}.from_integer (0) or mobj.momy /= create {FIXED_T}.from_integer (0) or mobj.flags & Mf_skullfly /= 0 then p_xymovement (mobj) if mobj.thinker.function = Void then returned := True end end if not returned then if mobj.z /= mobj.floorz or mobj.momz /= create {FIXED_T}.from_integer (0) then p_zmovement (mobj) if mobj.thinker.function = Void then returned := True end end end if not returned then if mobj.tics /= -1 then mobj.tics := mobj.tics - 1 if mobj.tics = 0 then check attached mobj.state as state then if not p_setmobjstate (mobj, state.nextstate) then returned := True end end end else if mobj.flags & Mf_countkill = 0 then returned := True end if not returned then if not i_main.G_game.respawnmonsters then returned := True end end if not returned then mobj.movecount := mobj.movecount + 1 if mobj.movecount < 12 * 35 then returned := True end end if not returned then if i_main.P_tick.leveltime & 31 /= 0 then returned := True end end if not returned then if i_main.M_random.p_random > 4 then returned := True end end if not returned then p_nightmarerespawn (mobj) end end end end Stopspeed: INTEGER_32 = 4096 Friction: INTEGER_32 = 59392 p_xymovement (mo: MOBJ_T) local ptryx: FIXED_T ptryy: FIXED_T xmove: FIXED_T ymove: FIXED_T returned: BOOLEAN did_once: BOOLEAN do if mo.momx = create {FIXED_T}.from_integer (0) and mo.momy = create {FIXED_T}.from_integer (0) then if mo.flags & Mf_skullfly /= 0 then mo.flags := mo.flags & Mf_skullfly.bit_not mo.momx := create {FIXED_T}.from_integer (0) mo.momy := create {FIXED_T}.from_integer (0) mo.momz := create {FIXED_T}.from_integer (0) check attached mo.info as info then p_setmobjstate (mo, info.spawnstate).do_nothing end end returned := True end if not returned then if mo.momx > create {FIXED_T}.from_integer ({P_LOCAL}.maxmove) then mo.momx := create {FIXED_T}.from_integer ({P_LOCAL}.maxmove) elseif mo.momx < create {FIXED_T}.from_integer (- {P_LOCAL}.maxmove) then mo.momx := create {FIXED_T}.from_integer (- {P_LOCAL}.maxmove) end if mo.momy > create {FIXED_T}.from_integer ({P_LOCAL}.maxmove) then mo.momy := create {FIXED_T}.from_integer ({P_LOCAL}.maxmove) elseif mo.momy < create {FIXED_T}.from_integer (- {P_LOCAL}.maxmove) then mo.momy := create {FIXED_T}.from_integer (- {P_LOCAL}.maxmove) end xmove := mo.momx ymove := mo.momy end from until returned or (did_once and xmove = create {FIXED_T}.from_integer (0) and ymove = create {FIXED_T}.from_integer (0)) loop did_once := True if xmove > create {FIXED_T}.from_integer ({P_LOCAL}.maxmove // 2) or ymove > create {FIXED_T}.from_integer ({P_LOCAL}.maxmove // 2) then ptryx := mo.x + xmove // create {FIXED_T}.from_integer (2) ptryy := mo.y + ymove // create {FIXED_T}.from_integer (2) xmove := xmove |>> 1 ymove := ymove |>> 1 else ptryx := mo.x + xmove ptryy := mo.y + ymove xmove := create {FIXED_T}.from_integer (0) ymove := create {FIXED_T}.from_integer (0) end if not i_main.P_map.p_trymove (mo, ptryx, ptryy) then if attached mo.player then i_main.P_map.p_slidemove (mo) elseif mo.flags & Mf_missile /= 0 then if attached i_main.P_map.ceilingline as cl and then attached cl.backsector as bs and then bs.ceilingpic.to_integer_32 = i_main.R_sky.skyflatnum then p_removemobj (mo) returned := True else p_explodemissile (mo) end else mo.momx := create {FIXED_T}.from_integer (0) mo.momy := create {FIXED_T}.from_integer (0) end end end if not returned then if attached mo.player as player and then player.cheats & {CHEAT_T}.cf_nomomentum /= 0 then mo.momx := create {FIXED_T}.from_integer (0) mo.momy := create {FIXED_T}.from_integer (0) returned := True end end if not returned then if mo.flags & (Mf_missile | Mf_skullfly) /= 0 then returned := True end end if not returned then if mo.z > mo.floorz then returned := True end end if not returned then if mo.flags & Mf_corpse /= 0 then if mo.momx > create {FIXED_T}.from_integer ({M_FIXED}.fracunit // 4) or mo.momx < create {FIXED_T}.from_integer (- {M_FIXED}.fracunit // 4) or mo.momy > create {FIXED_T}.from_integer ({M_FIXED}.fracunit // 4) or mo.momy < create {FIXED_T}.from_integer (- {M_FIXED}.fracunit // 4) then check attached mo.subsector as sub and then attached sub.sector as sector then if mo.floorz /= sector.floorheight then returned := True end end end end end if not returned then if mo.momx > create {FIXED_T}.from_integer (- Stopspeed) and mo.momx < create {FIXED_T}.from_integer (Stopspeed) and mo.momy > create {FIXED_T}.from_integer (- Stopspeed) and mo.momy < create {FIXED_T}.from_integer (Stopspeed) and (mo.player = Void or (attached mo.player as player and then player.cmd.forwardmove = 0 and then player.cmd.sidemove = 0)) then if attached mo.player as player and then attached player.mo as pmo and then attached pmo.state as st and then st.is_player_run then p_setmobjstate (pmo, S_play).do_nothing end mo.momx := create {FIXED_T}.from_integer (0) mo.momy := create {FIXED_T}.from_integer (0) else mo.momx := {M_FIXED}.fixedmul (mo.momx, create {FIXED_T}.from_integer (Friction)) mo.momy := {M_FIXED}.fixedmul (mo.momy, create {FIXED_T}.from_integer (Friction)) end end end p_explodemissile (mo: MOBJ_T) do mo.momx := create {FIXED_T}.from_integer (0) mo.momy := create {FIXED_T}.from_integer (0) mo.momz := create {FIXED_T}.from_integer (0) p_setmobjstate (mo, {INFO}.mobjinfo [mo.type].deathstate).do_nothing mo.tics := mo.tics - (i_main.M_random.p_random & 3) if mo.tics < 1 then mo.tics := 1 end mo.flags := mo.flags & Mf_missile.bit_not check attached mo.info as i then if i.deathsound /= 0 then i_main.S_sound.s_startsound (mo, i.deathsound) end end end p_zmovement (mo: MOBJ_T) local dist: FIXED_T delta: FIXED_T returned: BOOLEAN do if attached mo.player as player and then mo.z < mo.floorz then player.viewheight := player.viewheight - (mo.floorz - mo.z) player.deltaviewheight := create {FIXED_T}.from_integer (({P_LOCAL}.viewheight - player.viewheight.as_integer_32) |>> 3) end mo.z := mo.z + mo.momz if mo.flags & Mf_float /= 0 and attached mo.target as target then dist := i_main.P_maputl.p_aproxdistance (mo.x - target.x, mo.y - target.y) delta := (target.z + (mo.height |>> 1)) - mo.z if delta < create {FIXED_T}.from_integer (0) and dist < - (delta * create {FIXED_T}.from_integer (3)) then mo.z := mo.z - create {FIXED_T}.from_integer ({P_LOCAL}.floatspeed) elseif delta > create {FIXED_T}.from_integer (0) and dist < (delta * create {FIXED_T}.from_integer (3)) then mo.z := mo.z + create {FIXED_T}.from_integer ({P_LOCAL}.floatspeed) end end if mo.z <= mo.floorz then if mo.flags & Mf_skullfly /= 0 then mo.momz := - mo.momz end if mo.momz < create {FIXED_T}.from_integer (0) then if attached mo.player as player and mo.momz < create {FIXED_T}.from_integer (- {P_LOCAL}.gravity * 8) then player.deltaviewheight := mo.momz |>> 3 i_main.S_sound.s_startsound (mo, {SFXENUM_T}.sfx_oof) end mo.momz := create {FIXED_T}.from_integer (0) end mo.z := mo.floorz if mo.flags & Mf_missile /= 0 and mo.flags & Mf_noclip = 0 then p_explodemissile (mo) returned := True end elseif not returned and mo.flags & Mf_nogravity = 0 then if mo.momz = create {FIXED_T}.from_integer (0) then mo.momz := create {FIXED_T}.from_integer (- {P_LOCAL}.gravity * 2) else mo.momz := mo.momz - create {FIXED_T}.from_integer ({P_LOCAL}.gravity) end end if not returned then if mo.z + mo.height > mo.ceilingz then if mo.momz > create {FIXED_T}.from_integer (0) then mo.momz := create {FIXED_T}.from_integer (0) end mo.z := mo.ceilingz - mo.height if mo.flags & Mf_skullfly /= 0 then mo.momz := - mo.momz end if mo.flags & Mf_missile /= 0 and mo.flags & Mf_noclip = 0 then p_explodemissile (mo) end end end end p_nightmarerespawn (mo: MOBJ_T) do {NOT_IMPLEMENTED}.not_implemented ("P_NightmareRespawn", False) end p_respawnspecials do {NOT_IMPLEMENTED}.not_implemented ("P_RespawnSpecials", False) end p_setmobjstate (mobj: MOBJ_T; a_state: INTEGER_32): BOOLEAN -- Returns true if the mobj is still present local st: STATE_T state: INTEGER_32 did_once: BOOLEAN do state := a_state from Result := True did_once := False until did_once and (not Result or mobj.tics /= 0) loop did_once := True if state = S_null then mobj.state := Void p_removemobj (mobj) Result := False else st := {INFO}.states [state] mobj.state := st mobj.tics := st.tics.to_integer_32 mobj.sprite := st.sprite mobj.frame := st.frame.to_integer_32 if attached st.action as action then action.call (i_main.P_enemy, mobj) end state := st.nextstate end end end feature -- P_RemoveMobj itemrespawnque: ARRAY [MAPTHING_T] itemrespawntime: ARRAY [INTEGER_32] p_removemobj (mobj: MOBJ_T) do if mobj.flags & Mf_special /= 0 and mobj.flags & Mf_dropped = 0 and mobj.type /= Mt_inv and mobj.type /= Mt_ins then check attached mobj.spawnpoint as sp then itemrespawnque [iquehead] := sp end itemrespawntime [iquehead] := i_main.P_tick.leveltime iquehead := (iquehead + 1) & ({P_LOCAL}.itemquesize - 1) if iquehead = iquetail then iquetail := (iquetail + 1) & ({P_LOCAL}.itemquesize - 1) end end i_main.P_maputl.p_unsetthingposition (mobj) i_main.S_sound.s_stopsound (mobj) i_main.P_tick.p_removethinker (mobj) end feature p_spawnpuff (x, y, a_z: FIXED_T) local th: MOBJ_T z: FIXED_T do z := a_z + create {FIXED_T}.from_integer (((i_main.M_random.p_random - i_main.M_random.p_random) |<< 10)) th := p_spawnmobj (x, y, z, Mt_puff) th.momz := create {FIXED_T}.from_integer ({M_FIXED}.fracunit) th.tics := th.tics - (i_main.M_random.p_random & 3) if th.tics < 1 then th.tics := 1 end if i_main.P_map.attackrange = create {FIXED_T}.from_integer ({P_LOCAL}.meleerange) then p_setmobjstate (th, S_puff3).do_nothing end end p_spawnblood (x, y, a_z: FIXED_T; damage: INTEGER_32) local th: MOBJ_T z: FIXED_T do z := a_z + create {FIXED_T}.from_integer (((i_main.M_random.p_random - i_main.M_random.p_random) |<< 10)) th := p_spawnmobj (x, y, z, Mt_blood) th.momz := create {FIXED_T}.from_integer ({M_FIXED}.fracunit * 2) th.tics := th.tics - (i_main.M_random.p_random & 3) if th.tics < 1 then th.tics := 1 end if damage <= 12 and damage >= 9 then p_setmobjstate (th, S_blood2).do_nothing elseif damage < 9 then p_setmobjstate (th, S_blood3).do_nothing end end p_spawnmissile (source, dest: MOBJ_T; type: INTEGER_32): MOBJ_T local th: MOBJ_T an: ANGLE_T dist: INTEGER_32 do th := p_spawnmobj (source.x, source.y, source.z + create {FIXED_T}.from_integer (4 * 8 * {M_FIXED}.fracunit), type) check attached th.info as thinfo then i_main.S_sound.s_startsound (th, thinfo.seesound) end th.target := source an := i_main.R_main.r_pointtoangle2 (source.x, source.y, dest.x, dest.y) if dest.flags & Mf_shadow /= 0 then an := an + create {ANGLE_T}.from_natural (((i_main.M_random.p_random - i_main.M_random.p_random) |<< 20).to_natural_32) end th.angle := an an := an |>> {R_MAIN}.angletofineshift check attached th.info as i then th.momx := {M_FIXED}.fixedmul (create {FIXED_T}.from_integer (i.speed), i_main.R_main.Finecosine [an.as_integer_32]) th.momy := {M_FIXED}.fixedmul (create {FIXED_T}.from_integer (i.speed), create {FIXED_T}.from_integer (i_main.R_main.Finesine [an.as_integer_32])) dist := i_main.P_maputl.p_aproxdistance (dest.x - source.x, dest.y - source.y).as_integer_32 dist := dist // i.speed end if dist < 1 then dist := 1 end th.momz := (dest.z - source.z) // create {FIXED_T}.from_integer (dist) p_checkmissilespawn (th) Result := th end p_checkmissilespawn (th: MOBJ_T) -- Moves the missile forward a bit -- and possibly explodes it right there do th.tics := th.tics - (i_main.M_random.p_random & 3) if th.tics < 1 then th.tics := 1 end th.x := th.x + (th.momx |>> 1) th.y := th.y + (th.momy |>> 1) th.z := th.z + (th.momz |>> 1) if not i_main.P_map.p_trymove (th, th.x, th.y) then p_explodemissile (th) end end end -- class P_MOBJ
Generated by ISE EiffelStudio