note
	description: "[
		p_inter.c
		
		Handling interactions (i.e., collisions)
	]"
	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_INTER

inherit
	MOBJFLAG_T

	MOBJTYPE_T

	SFXENUM_T

	SPRITENUM_T

	D_ENGLSH

	CARD_T

	POWERTYPE_T

	WEAPONTYPE_T

	AMMOTYPE_T

create 
	make

feature 

	i_main: I_MAIN

	make (a_i_main: I_MAIN)
		do
			i_main := a_i_main
		end
	
feature 

	Bonusadd: INTEGER_32 = 6
	
feature 

	Maxammo: ARRAY [INTEGER_32]
		once
			create Result.make_filled (0, 0, 3)
			Result [0] := 200
			Result [1] := 50
			Result [2] := 300
			Result [3] := 50
		ensure
				Result.lower = 0
				Result.count = {AMMOTYPE_T}.numammo
			instance_free: class
		end
	
feature -- P_DamageMobj

	p_damagemobj (target: MOBJ_T; inflictor, source: detachable MOBJ_T; a_damage: INTEGER_32)
		local
			ang: ANGLE_T
			saved: INTEGER_32
			player: PLAYER_T
			thrust: FIXED_T
			temp: INTEGER_32
			damage: INTEGER_32
			player_using_chainsaw: BOOLEAN
			returned: BOOLEAN
		do
			if target.flags & Mf_shootable = 0 then
			elseif target.health <= 0 then
			else
				damage := a_damage
				if target.flags & Mf_skullfly /= 0 then
					target.momx := create {FIXED_T}.from_integer (0)
					target.momy := create {FIXED_T}.from_integer (0)
					target.momz := create {FIXED_T}.from_integer (0)
				end
				player := target.player
				if attached player and then i_main.G_game.gameskill = {DOOMDEF_H}.sk_baby then
					damage := damage |>> 1
				end
				player_using_chainsaw := attached source and then attached source.player as p and then p.readyweapon = {WEAPONTYPE_T}.wp_chainsaw
				if attached inflictor and target.flags & Mf_noclip = 0 and not player_using_chainsaw then
					ang := i_main.R_main.r_pointtoangle2 (inflictor.x, inflictor.y, target.x, target.y)
					check
							attached target.info as tinfo
					then
						thrust := create {FIXED_T}.from_integer (damage * ({M_FIXED}.fracunit |>> 3) * 100 // tinfo.mass)
					end
					if damage < 40 and damage > target.health and target.z - inflictor.z > create {FIXED_T}.from_integer (64 * {M_FIXED}.fracunit) and (i_main.M_random.p_random & 1 /= 0) then
						ang := ang + {R_MAIN}.ang180
						thrust := thrust * create {FIXED_T}.from_integer (4)
					end
				end
				if attached player then
					check
							attached target.subsector as sub and then attached sub.sector as s
					then
						if s.special = 11 and damage >= target.health then
							damage := target.health - 1
						end
					end
					if damage < 1000 and (player.cheats & {CHEAT_T}.cf_godmode /= 0 or player.powers [{POWERTYPE_T}.pw_invulnerability] /= 0) then
						returned := True
					end
					if not returned then
						if player.armortype /= 0 then
							if player.armortype = 1 then
								saved := damage // 3
							else
								saved := damage // 2
							end
							if player.armorpoints <= saved then
								saved := player.armorpoints
								player.armortype := 0
							end
							player.armorpoints := player.armorpoints - saved
							damage := damage - saved
						end
						player.health := player.health - damage
						if player.health < 0 then
							player.health := 0
						end
						player.attacker := source
						player.damagecount := player.damagecount + damage
						if player.damagecount > 100 then
							player.damagecount := 100
						end
						temp := damage.max (100)
						if player = i_main.G_game.Players [i_main.G_game.consoleplayer] then
							i_main.I_system.i_tactile (40, 10, 40 + temp * 2)
						end
					end
				end
				if not returned then
					target.health := target.health - damage
					if target.health <= 0 then
						p_killmobj (source, target)
						returned := True
					end
				end
				if not returned then
					check
							attached target.info as tinfo
					then
						if i_main.M_random.p_random < tinfo.painchance and target.flags & Mf_skullfly = 0 then
							target.flags := target.flags | Mf_justhit
							i_main.P_mobj.p_setmobjstate (target, tinfo.painstate).do_nothing
						end
						target.reactiontime := 0
						if (target.threshold = 0 or target.type = {MOBJTYPE_T}.mt_vile) and then attached source and then source /= target and then source.type /= {MOBJTYPE_T}.mt_vile then
							target.target := source
							target.threshold := {P_LOCAL}.basethreshold
							if target.state = {INFO}.states [tinfo.spawnstate] and tinfo.seestate /= {STATENUM_T}.s_null then
								i_main.P_mobj.p_setmobjstate (target, tinfo.seestate).do_nothing
							end
						end
					end
				end
			end
		end

	p_killmobj (source: detachable MOBJ_T; target: MOBJ_T)
		local
			item: INTEGER_32
			mo: MOBJ_T
			target_player_index: INTEGER_32
			returned: BOOLEAN
		do
			target.flags := target.flags & (Mf_shootable | Mf_float | Mf_skullfly).bit_not
			if target.type /= Mt_skull then
				target.flags := target.flags & Mf_nogravity.bit_not
			end
			target.flags := target.flags | Mf_corpse | Mf_dropoff
			target.height := target.height |>> 2
			if attached source and then attached source.player as player then
				if target.flags & Mf_countkill /= 0 then
					player.killcount := player.killcount + 1
				end
				if attached target.player as target_player then
					target_player_index := i_main.G_game.player_index (target_player)
					player.frags [target_player_index] := player.frags [target_player_index] + 1
				end
			elseif not i_main.G_game.netgame and target.flags & Mf_countkill /= 0 then
				i_main.G_game.Players [0].killcount := i_main.G_game.Players [0].killcount + 1
			end
			if attached target.player as target_player then
				if source = Void then
					target_player_index := i_main.G_game.player_index (target_player)
					target_player.frags [target_player_index] := target_player.frags [target_player_index] + 1
				end
				target.flags := target.flags & Mf_solid.bit_not
				target_player.playerstate := {PLAYER_T}.pst_dead
				i_main.P_pspr.p_dropweapon (target_player)
				if target_player = i_main.G_game.Players [i_main.G_game.consoleplayer] and i_main.Am_map.automapactive then
					i_main.Am_map.am_stop
				end
			end
			check
					attached target.info as tinfo
			then
				if target.health < - tinfo.spawnhealth and tinfo.xdeathstate /= 0 then
					i_main.P_mobj.p_setmobjstate (target, tinfo.xdeathstate).do_nothing
				else
					i_main.P_mobj.p_setmobjstate (target, tinfo.deathstate).do_nothing
				end
			end
			target.tics := target.tics - (i_main.M_random.p_random & 3)
			if target.tics < 1 then
				target.tics := 1
			end
			if target.type = Mt_wolfss or target.type = Mt_possessed then
				item := Mt_clip
			elseif target.type = Mt_shotguy then
				item := Mt_shotgun
			elseif target.type = Mt_chainguy then
				item := Mt_chaingun
			else
				returned := True
			end
			if not returned then
				mo := i_main.P_mobj.p_spawnmobj (target.x, target.y, create {FIXED_T}.from_integer ({P_LOCAL}.onfloorz), item)
				mo.flags := mo.flags | Mf_dropped
			end
		end

	p_touchspecialthing (special, toucher: MOBJ_T)
		local
			player: PLAYER_T
			i: INTEGER_32
			delta: FIXED_T
			sound: INTEGER_32
			returned: BOOLEAN
		do
			delta := special.z - toucher.z
			if delta > toucher.height or delta < create {FIXED_T}.from_integer (-8 * {M_FIXED}.fracunit) then
				returned := True
			end
			if not returned then
				sound := Sfx_itemup
				player := toucher.player
				if toucher.health <= 0 then
					returned := True
				end
			end
			if not returned then
				check
						attached player and then attached player.mo as mo
				then
					if special.sprite = Spr_arm1 then
						if not p_givearmor (player, 1) then
							returned := True
						else
							player.message := Gotarmor
						end
					elseif special.sprite = Spr_arm2 then
						if not p_givearmor (player, 2) then
							returned := True
						else
							player.message := Gotmega
						end
					elseif special.sprite = Spr_bon1 then
						player.health := player.health + 1
						if player.health > 200 then
							player.health := 200
						end
						mo.health := player.health
						player.message := Goththbonus
					elseif special.sprite = Spr_bon2 then
						player.armorpoints := player.armorpoints + 1
						if player.armorpoints > 200 then
							player.armorpoints := 200
						end
						if player.armortype = 0 then
							player.armortype := 1
						end
						player.message := Gotarmbonus
					elseif special.sprite = Spr_soul then
						player.health := player.health + 100
						if player.health > 200 then
							player.health := 200
						end
						mo.health := player.health
						player.message := Gotsuper
						sound := Sfx_getpow
					elseif special.sprite = Spr_mega then
						if i_main.Doomstat_h.gamemode /= {GAME_MODE_T}.commercial then
							returned := True
						else
							player.health := 200
							mo.health := player.health
							p_givearmor (player, 2).do_nothing
							player.message := Gotmsphere
							sound := Sfx_getpow
						end
					elseif special.sprite = Spr_bkey then
						if not player.cards [It_bluecard] then
							player.message := Gotbluecard
						end
						p_givecard (player, It_bluecard)
						if i_main.G_game.netgame then
							returned := True
						end
					elseif special.sprite = Spr_ykey then
						if not player.cards [It_yellowcard] then
							player.message := Gotyelwcard
						end
						p_givecard (player, It_yellowcard)
						if i_main.G_game.netgame then
							returned := True
						end
					elseif special.sprite = Spr_rkey then
						if not player.cards [It_redcard] then
							player.message := Gotredcard
						end
						p_givecard (player, It_redcard)
						if i_main.G_game.netgame then
							returned := True
						end
					elseif special.sprite = Spr_bsku then
						if not player.cards [It_blueskull] then
							player.message := Gotblueskul
						end
						p_givecard (player, It_blueskull)
						if i_main.G_game.netgame then
							returned := True
						end
					elseif special.sprite = Spr_ysku then
						if not player.cards [It_yellowskull] then
							player.message := Gotyelwskul
						end
						p_givecard (player, It_yellowskull)
						if i_main.G_game.netgame then
							returned := True
						end
					elseif special.sprite = Spr_rsku then
						if not player.cards [It_redskull] then
							player.message := Gotredskull
						end
						p_givecard (player, It_redskull)
						if i_main.G_game.netgame then
							returned := True
						end
					elseif special.sprite = Spr_stim then
						if not p_givebody (player, 10) then
							returned := True
						else
							player.message := Gotstim
						end
					elseif special.sprite = Spr_medi then
						if not p_givebody (player, 25) then
							returned := True
						else
							if player.health < 25 then
								player.message := Gotmedineed
							else
								player.message := Gotmedikit
							end
						end
					elseif special.sprite = Spr_pinv then
						if not p_givepower (player, Pw_invulnerability) then
							returned := True
						else
							player.message := Gotinvul
							sound := Sfx_getpow
						end
					elseif special.sprite = Spr_pstr then
						if not p_givepower (player, Pw_strength) then
							returned := True
						else
							player.message := Gotberserk
							if player.readyweapon /= Wp_fist then
								player.pendingweapon := Wp_fist
							end
							sound := Sfx_getpow
						end
					elseif special.sprite = Spr_pins then
						if not p_givepower (player, Pw_invisibility) then
							returned := True
						else
							player.message := Gotinvis
							sound := Sfx_getpow
						end
					elseif special.sprite = Spr_suit then
						if not p_givepower (player, Pw_ironfeet) then
							returned := True
						else
							player.message := Gotsuit
							sound := Sfx_getpow
						end
					elseif special.sprite = Spr_pmap then
						if not p_givepower (player, Pw_allmap) then
							returned := True
						else
							player.message := Gotmap
							sound := Sfx_getpow
						end
					elseif special.sprite = Spr_pvis then
						if not p_givepower (player, Pw_infrared) then
							returned := True
						else
							player.message := Gotvisor
							sound := Sfx_getpow
						end
					elseif special.sprite = Spr_clip then
						if special.flags & Mf_dropped /= 0 then
							if not p_giveammo (player, Am_clip, 0) then
								returned := True
							end
						else
							if not p_giveammo (player, Am_clip, 1) then
								returned := True
							end
						end
						if not returned then
							player.message := Gotclip
						end
					elseif special.sprite = Spr_ammo then
						if not p_giveammo (player, Am_clip, 5) then
							returned := True
						else
							player.message := Gotclipbox
						end
					elseif special.sprite = Spr_rock then
						if not p_giveammo (player, Am_misl, 1) then
							returned := True
						else
							player.message := Gotrocket
						end
					elseif special.sprite = Spr_brok then
						if not p_giveammo (player, Am_misl, 5) then
							returned := True
						else
							player.message := Gotrockbox
						end
					elseif special.sprite = Spr_cell then
						if not p_giveammo (player, Am_cell, 1) then
							returned := True
						else
							player.message := Gotcell
						end
					elseif special.sprite = Spr_celp then
						if not p_giveammo (player, Am_cell, 5) then
							returned := True
						else
							player.message := Gotcellbox
						end
					elseif special.sprite = Spr_shel then
						if not p_giveammo (player, Am_shell, 1) then
							returned := True
						else
							player.message := Gotshells
						end
					elseif special.sprite = Spr_sbox then
						if not p_giveammo (player, Am_shell, 5) then
							returned := True
						else
							player.message := Gotshellbox
						end
					elseif special.sprite = Spr_bpak then
						if not player.backpack then
							from
								i := 0
							until
								i >= Numammo
							loop
								player.maxammo [i] := player.maxammo [i] * 2
								i := i + 1
							end
							player.backpack := True
						end
						from
							i := 0
						until
							i >= Numammo
						loop
							p_giveammo (player, i, 1).do_nothing
							i := i + 1
						end
						player.message := Gotbackpack
					elseif special.sprite = Spr_bfug then
						if not p_giveweapon (player, Wp_bfg, False) then
							returned := True
						else
							player.message := Gotbfg9000
							sound := Sfx_wpnup
						end
					elseif special.sprite = Spr_mgun then
						if not p_giveweapon (player, Wp_chaingun, special.flags & Mf_dropped /= 0) then
							returned := True
						else
							player.message := Gotchaingun
							sound := Sfx_wpnup
						end
					elseif special.sprite = Spr_csaw then
						if not p_giveweapon (player, Wp_chainsaw, False) then
							returned := True
						else
							player.message := Gotchainsaw
							sound := Sfx_wpnup
						end
					elseif special.sprite = Spr_laun then
						if not p_giveweapon (player, Wp_missile, False) then
							returned := True
						else
							player.message := Gotlauncher
							sound := Sfx_wpnup
						end
					elseif special.sprite = Spr_plas then
						if not p_giveweapon (player, Wp_plasma, False) then
							returned := True
						else
							player.message := Gotplasma
							sound := Sfx_wpnup
						end
					elseif special.sprite = Spr_shot then
						if not p_giveweapon (player, Wp_shotgun, special.flags & Mf_dropped /= 0) then
							returned := True
						else
							player.message := Gotshotgun
							sound := Sfx_wpnup
						end
					elseif special.sprite = Spr_sgn2 then
						if not p_giveweapon (player, Wp_supershotgun, special.flags & Mf_dropped /= 0) then
							returned := True
						else
							player.message := Gotshotgun2
							sound := Sfx_wpnup
						end
					else
						{I_MAIN}.i_error ("P_SpecialThing: Unknown gettable thing%N")
					end
				end
			end
			if not returned then
				check
						attached player
				then
					if special.flags & Mf_countitem /= 0 then
						player.itemcount := player.itemcount + 1
					end
					i_main.P_mobj.p_removemobj (special)
					player.bonuscount := player.bonuscount + Bonusadd
					if player = i_main.G_game.Players [i_main.G_game.consoleplayer] then
						i_main.S_sound.s_startsound (Void, sound)
					end
				end
			end
		end

	p_givearmor (player: PLAYER_T; armortype: INTEGER_32): BOOLEAN
			-- Returns false if the armor is worse
			-- than the current armor
		local
			hits: INTEGER_32
		do
			hits := armortype * 100
			if player.armorpoints >= hits then
				Result := False
			else
				player.armortype := armortype
				player.armorpoints := hits
				Result := True
			end
		end

	p_givecard (player: PLAYER_T; card: INTEGER_32)
		do
			if player.cards [card] then
			else
				player.bonuscount := Bonusadd
				player.cards [card] := True
			end
		end

	p_givebody (player: PLAYER_T; num: INTEGER_32): BOOLEAN
			-- Returns false if the body isn't needed at all
		do
			if player.health >= {P_LOCAL}.maxhealth then
				Result := False
			else
				player.health := player.health + num
				if player.health > {P_LOCAL}.maxhealth then
					player.health := {P_LOCAL}.maxhealth
				end
				check
						attached player.mo as mo
				then
					mo.health := player.health
				end
				Result := True
			end
		end

	p_givepower (player: PLAYER_T; power: INTEGER_32): BOOLEAN
		do
			check
					attached player.mo as mo
			then
				if power = Pw_invulnerability then
					player.powers [power] := {DOOMDEF_H}.invulntics
					Result := True
				elseif power = Pw_invisibility then
					player.powers [power] := {DOOMDEF_H}.invistics
					mo.flags := mo.flags | Mf_shadow
					Result := True
				elseif power = Pw_infrared then
					player.powers [power] := {DOOMDEF_H}.infratics
					Result := True
				elseif power = Pw_ironfeet then
					player.powers [power] := {DOOMDEF_H}.irontics
					Result := True
				elseif power = Pw_strength then
					p_givebody (player, 100).do_nothing
					player.powers [power] := 1
					Result := True
				else
					if player.powers [power] /= 0 then
						Result := False
					else
						player.powers [power] := 1
						Result := True
					end
				end
			end
		end
	
feature -- P_GiveAmmo

	Clipammo: ARRAY [INTEGER_32]
		once
			Result := <<10, 4, 20, 1>>
			Result.rebase (0)
		end

	p_giveammo (player: PLAYER_T; ammo: INTEGER_32; a_num: INTEGER_32): BOOLEAN
			-- Num is the number of clip loads,
			-- not the individual count (0= 1/2 clip).
			-- Returns false if the ammo can't be picked up at all.
		require
				ammo >= 0 and ammo < Numammo
		local
			oldammo: INTEGER_32
			num: INTEGER_32
		do
			num := a_num
			if ammo = Am_noammo then
				Result := False
			elseif player.ammo [ammo] = player.maxammo [ammo] then
				Result := False
			else
				if num /= 0 then
					num := num * Clipammo [ammo]
				else
					num := Clipammo [ammo] // 2
				end
				if i_main.G_game.gameskill = {DOOMDEF_H}.sk_baby or i_main.G_game.gameskill = {DOOMDEF_H}.sk_nightmare then
					num := num |<< 1
				end
				oldammo := player.ammo [ammo]
				player.ammo [ammo] := player.ammo [ammo] + num
				if player.ammo [ammo] > player.maxammo [ammo] then
					player.ammo [ammo] := player.maxammo [ammo]
				end
				if oldammo /= 0 then
					Result := True
				else
					if ammo = Am_clip then
						if player.readyweapon = Wp_fist then
							if player.weaponowned [Wp_chaingun] then
								player.pendingweapon := Wp_chaingun
							else
								player.pendingweapon := Wp_pistol
							end
						end
					elseif ammo = Am_shell then
						if player.readyweapon = Wp_fist or player.readyweapon = Wp_pistol then
							if player.weaponowned [Wp_shotgun] then
								player.pendingweapon := Wp_shotgun
							end
						end
					elseif ammo = Am_cell then
						if player.readyweapon = Wp_fist or player.readyweapon = Wp_pistol then
							if player.weaponowned [Wp_plasma] then
								player.pendingweapon := Wp_plasma
							end
						end
					elseif ammo = Am_misl then
						if player.readyweapon = Wp_fist then
							if player.weaponowned [Wp_missile] then
								player.pendingweapon := Wp_missile
							end
						end
					end
					Result := True
				end
			end
		end

	p_giveweapon (player: PLAYER_T; weapon: INTEGER_32; dropped: BOOLEAN): BOOLEAN
			-- The weapon name may have a MF_DROPPED flag ored in.
		local
			gaveammo, gaveweapon: BOOLEAN
		do
			if i_main.G_game.netgame then
				{NOT_IMPLEMENTED}.not_implemented ("P_GiveWeapon", True)
			end
			if {D_ITEMS}.weaponinfo [weapon].ammo /= Am_noammo then
				if dropped then
					gaveammo := p_giveammo (player, {D_ITEMS}.weaponinfo [weapon].ammo, 1)
				else
					gaveammo := p_giveammo (player, {D_ITEMS}.weaponinfo [weapon].ammo, 2)
				end
			else
				gaveammo := False
			end
			if player.weaponowned [weapon] then
				gaveweapon := False
			else
				gaveweapon := True
				player.weaponowned [weapon] := True
				player.pendingweapon := weapon
			end
			Result := gaveweapon or gaveammo
		end
	
end -- class P_INTER

Generated by ISE EiffelStudio