note
	description: "[
		p_sight.c
		
		LineOfSight/Visibility checks, uses REJECT Lookup Table.
	]"
	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_SIGHT

create 
	make

feature 

	i_main: I_MAIN

	make (a_i_main: I_MAIN)
		do
			i_main := a_i_main
			create strace
		end
	
feature -- P_CheckSight

	topslope: FIXED_T assign set_topslope
			-- slope to the top of target

	set_topslope (a_topslope: like topslope)
		do
			topslope := a_topslope
		end

	bottomslope: FIXED_T assign set_bottomslope
			-- slope to the bottom of target

	set_bottomslope (a_bottomslope: like bottomslope)
		do
			bottomslope := a_bottomslope
		end

	sightzstart: FIXED_T
			-- eye z of looker

	strace: DIVLINE_T
			-- from t1 to t2

	t2x: FIXED_T

	t2y: FIXED_T

	p_checksight (t1, t2: MOBJ_T): BOOLEAN
			-- Returns true
			-- if a straight line between t1 and t2 is unobstructed.
			-- Uses REJECT.
		local
			s1, s2: INTEGER_32
			pnum: INTEGER_32
			bytenum: INTEGER_32
			bitnum: INTEGER_32
		do
			check
					attached t1.subsector as sub and then attached sub.sector as sec
			then
				s1 := {UTILS [SECTOR_T]}.first_index (i_main.P_setup.sectors, sec)
			end
			check
					attached t2.subsector as sub and then attached sub.sector as sec
			then
				s2 := {UTILS [SECTOR_T]}.first_index (i_main.P_setup.sectors, sec)
			end
			pnum := s1 * i_main.P_setup.numsectors + s2
			bytenum := pnum |>> 3
			bitnum := 1 |<< (pnum & 7)
			check
					attached i_main.P_setup.rejectmatrix as rm
			then
				if rm [bytenum].to_integer_32 & bitnum /= 0 then
					Result := False
				else
					i_main.R_main.validcount := i_main.R_main.validcount + 1
					sightzstart := t1.z + t1.height - (t1.height |>> 2)
					topslope := (t2.z + t2.height) - sightzstart
					bottomslope := (t2.z) - sightzstart
					strace.x := t1.x
					strace.y := t1.y
					t2x := t2.x
					t2y := t2.y
					strace.dx := t2.x - t1.x
					strace.dy := t2.y - t1.y
					Result := p_crossbspnode (i_main.P_setup.numnodes - 1)
				end
			end
		end
	
feature 

	p_crossbspnode (bspnum: INTEGER_32): BOOLEAN
			-- Returns true
			-- if strace crosses the given node successfully.
		local
			bsp: NODE_T
			side: INTEGER_32
		do
			if bspnum & {DOOMDATA_H}.nf_subsector /= 0 then
				if bspnum = -1 then
					Result := p_crosssubsector (0)
				else
					Result := p_crosssubsector (bspnum & {DOOMDATA_H}.nf_subsector.bit_not)
				end
			else
				bsp := i_main.P_setup.nodes [bspnum]
				side := p_divlineside (strace.x, strace.y, bsp)
				if side = 2 then
					side := 0
				end
				if not p_crossbspnode (bsp.children [side].to_integer_32) then
					Result := False
				else
					if side = p_divlineside (t2x, t2y, bsp) then
						Result := True
					else
						Result := p_crossbspnode (bsp.children [side.bit_xor (1)].to_integer_32)
					end
				end
			end
		end

	p_divlineside (x, y: FIXED_T; node: DIVLINE_T): INTEGER_32
			-- Returns side 0 (front), 1 (back), or 2 (on)
		local
			dx, dy: FIXED_T
			left, right: FIXED_T
		do
			if node.dx = create {FIXED_T}.from_integer (0) then
				if x = node.x then
					Result := 2
				elseif x <= node.x then
					Result := (node.dy > create {FIXED_T}.from_integer (0)).to_integer
				else
					Result := (node.dy < create {FIXED_T}.from_integer (0)).to_integer
				end
			elseif node.dy = create {FIXED_T}.from_integer (0) then
				if x = node.y then
					Result := 2
				elseif y <= node.y then
					Result := (node.dx < create {FIXED_T}.from_integer (0)).to_integer
				else
					Result := (node.dx > create {FIXED_T}.from_integer (0)).to_integer
				end
			else
				dx := (x - node.x)
				dy := (y - node.y)
				left := (node.dy |>> {M_FIXED}.fracbits) * (dx |>> {M_FIXED}.fracbits)
				right := (dy |>> {M_FIXED}.fracbits) * (node.dx |>> {M_FIXED}.fracbits)
				if right < left then
					Result := 0
				else
					if left = right then
						Result := 2
					else
						Result := 1
					end
				end
			end
		end

	p_crosssubsector (num: INTEGER_32): BOOLEAN
			-- Returns true
			-- if strace crosses the given subsector successfully
		require
			rangecheck: num < i_main.P_setup.subsectors.count
		local
			seg: SEG_T
			seg_i: INTEGER_32
			line: LINE_T
			s1, s2: INTEGER_32
			count: INTEGER_32
			sub: SUBSECTOR_T
			front, back: SECTOR_T
			opentop, openbottom: FIXED_T
			divl: DIVLINE_T
			v1, v2: VERTEX_T
			frac: FIXED_T
			slope: FIXED_T
			returned: BOOLEAN
		do
			create divl
			sub := i_main.P_setup.subsectors [num]
			count := sub.numlines.to_integer_32
			seg_i := sub.firstline.to_integer_32
			from
			until
				returned or count = 0
			loop
				seg := i_main.P_setup.segs [seg_i]
				line := seg.linedef
				if line.validcount = i_main.R_main.validcount then
				else
					line.validcount := i_main.R_main.validcount
					v1 := line.v1
					v2 := line.v2
					s1 := p_divlineside (v1.x, v1.y, strace)
					s2 := p_divlineside (v2.x, v2.y, strace)
					if s1 = s2 then
					else
						divl.x := v1.x
						divl.y := v1.y
						divl.dx := v2.x - v1.x
						divl.dy := v2.y - v1.y
						s1 := p_divlineside (strace.x, strace.y, divl)
						s2 := p_divlineside (t2x, t2y, divl)
						if s1 = s2 then
						else
							if line.flags.to_integer_32 & {DOOMDATA_H}.ml_twosided = 0 then
								Result := False
								returned := True
							else
								front := seg.frontsector
								back := seg.backsector
								check
										attached front and then attached back
								then
									if front.floorheight = back.floorheight and front.ceilingheight = back.ceilingheight then
									else
										if front.ceilingheight < back.ceilingheight then
											opentop := front.ceilingheight
										else
											opentop := back.ceilingheight
										end
										if front.floorheight > back.floorheight then
											openbottom := front.floorheight
										else
											openbottom := back.floorheight
										end
										if openbottom >= opentop then
											Result := False
											returned := True
										else
											frac := p_interceptvector2 (strace, divl)
											if front.floorheight /= back.floorheight then
												slope := {M_FIXED}.fixeddiv (openbottom - sightzstart, frac)
												if slope > bottomslope then
													bottomslope := slope
												end
											end
											if front.ceilingheight /= back.ceilingheight then
												slope := {M_FIXED}.fixeddiv (opentop - sightzstart, frac)
												if slope < topslope then
													topslope := slope
												end
											end
											if topslope <= bottomslope then
												Result := False
												returned := True
											end
										end
									end
								end
							end
						end
					end
				end
				seg_i := seg_i + 1
				count := count - 1
			end
			if not returned then
				Result := True
			end
		end

	p_interceptvector2 (v2, v1: DIVLINE_T): FIXED_T
			-- Returns the fractional intercept point
			-- along the first divline.
			-- This is only called by the addthings and addlines traversers
		local
			frac, num, den: FIXED_T
		do
			den := {M_FIXED}.fixedmul (v1.dy |>> 8, v2.dx) - {M_FIXED}.fixedmul (v1.dx |>> 8, v2.dy)
			if den = create {FIXED_T}.from_integer (0) then
				Result := create {FIXED_T}.from_integer (0)
			else
				num := {M_FIXED}.fixedmul ((v1.x - v2.x) |>> 8, v1.dy) + {M_FIXED}.fixedmul ((v2.y - v1.y) |>> 8, v1.dx)
				frac := {M_FIXED}.fixeddiv (num, den)
				Result := frac
			end
		end
	
end -- class P_SIGHT

Generated by ISE EiffelStudio