1 //**************************************************************************
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
13 //** This program is free software: you can redistribute it and/or modify
14 //** it under the terms of the GNU General Public License as published by
15 //** the Free Software Foundation, version 3 of the License ONLY.
17 //** This program is distributed in the hope that it will be useful,
18 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 //** GNU General Public License for more details.
22 //** You should have received a copy of the GNU General Public License
23 //** along with this program. If not, see <http://www.gnu.org/licenses/>.
25 //**************************************************************************
27 //** LineOfSight/Visibility checks, uses REJECT Lookup Table.
29 //** This uses specialized forms of the maputils routines for optimized
32 //**************************************************************************
33 #include "../gamedefs.h"
37 static VCvarB
compat_better_sight("compat_better_sight", true, "Check more points in LOS calculations?", CVAR_Archive
);
38 static VCvarB
dbg_disable_cansee("dbg_disable_cansee", false, "Disable CanSee processing (for debug)?", CVAR_PreInit
|CVAR_NoShadow
);
40 //k8: for some reason, sight checks ignores base sector region
41 // i don't think that this is a right thing to do, so i removed that
44 //==========================================================================
48 // LineOfSight/Visibility checks, uses REJECT Lookup Table. This uses
49 // specialised forms of the maputils routines for optimized performance
50 // Returns true if a straight line between t1 and t2 is unobstructed.
52 //==========================================================================
53 bool VEntity::CanSee (VEntity
*Other
, bool forShooting
, bool alwaysBetter
) {
54 return CanSeeEx(Other
, (forShooting
? CSE_ForShooting
: CSE_NothingZero
)|(alwaysBetter
? CSE_AlwaysBetter
: CSE_NothingZero
)|CSE_CheckBaseRegion
);
58 //==========================================================================
62 // LineOfSight/Visibility checks, uses REJECT Lookup Table. This uses
63 // specialised forms of the maputils routines for optimized performance
64 // Returns true if a straight line between t1 and t2 is unobstructed.
66 //==========================================================================
67 bool VEntity::CanSeeEx (VEntity
*Other
, unsigned flags
) {
68 if (dbg_disable_cansee
) return false;
70 if (Other
== this) return true; // it can see itself (obviously)
72 // if we have no base sector for any object, it cannot see each other
73 if (!Other
|| !Other
->BaseSector
|| !BaseSector
) return false;
74 if (IsGoingToDie() || Other
->IsGoingToDie()) return false;
76 // first check for trivial rejection
77 if (XLevel
->IsRejectedVis(BaseSector
, Other
->BaseSector
)) return false; // can't possibly be connected
79 // killough 11/98: shortcut for melee situations
80 // same subsector? obviously visible
81 // this is not true for base sectors, though
82 if (SubSector
== Other
->SubSector
) {
83 // if we have some 3d pobjs or 3d floors at this subsector, do not early exit
84 if (!SubSector
->Has3DPObjs() && !SubSector
->sector
->HasAnyExtraFloors()) return true;
85 // check for the same polyobject
86 if (SubSector
->isInnerPObj()) {
87 // two entities can have some 3d pobj subsector only if they're on or inside a pobj
88 //const float pz1 = SubSector->sector->ownpobj->poceiling.maxz;
89 //if (Origin.z == pz1 || Other.Origin.z == pz1) return true;
94 bool forShooting
= !!(flags
&CSE_ForShooting
);
95 bool alwaysBetter
= !!(flags
&CSE_AlwaysBetter
);
97 if (alwaysBetter
) forShooting
= false;
99 bool cbs
= (!forShooting
&& (alwaysBetter
|| compat_better_sight
));
101 if (cbs
&& !alwaysBetter
) {
102 // turn off "better sight" if it is not forced, and neither entity is monster
103 //cbs = (IsPlayerOrMonster() && Other->IsPlayerOrMonster());
104 cbs
= (IsMonster() || Other
->IsMonster());
105 //if (!cbs) GCon->Logf(NAME_Debug, "%s: better sight forced to 'OFF', checking sight to '%s' (not a player, not a monster)", GetClass()->GetName(), Other->GetClass()->GetName());
108 // if too far, don't do "better sight" (it doesn't worth it anyway)
110 const float distSq
= (Origin
-Other
->Origin
).length2DSquared();
111 cbs
= (distSq
< 680.0*680.0); // arbitrary number
112 //if (!cbs) GCon->Logf(NAME_Debug, "%s: better sight forced to 'OFF', checking sight to '%s' (dist=%g)", GetClass()->GetName(), Other->GetClass()->GetName(), sqrtf(distSq));
117 //dirR = YawVectorRight(Angles.yaw);
120 ang
.yaw
= Angles
.yaw
;
123 AngleVectors(ang
, dirF
, dirR
, dirU
);
125 dirF
= dirR
= TVec::ZeroVector
;
127 //if (forShooting) dirR = TVec::ZeroVector; // just in case, lol
128 return XLevel
->CastCanSee(BaseSubSector
, Origin
, Height
, dirF
, dirR
, Other
->Origin
, Other
->GetMoveRadius(), Other
->Height
,
129 !(flags
&CSE_CheckBaseRegion
)/*skip base region*/, Other
->BaseSubSector
, /*alwaysBetter*/cbs
,
130 !!(flags
&CSE_IgnoreBlockAll
), !!(flags
&CSE_IgnoreFakeFloors
));
135 //==========================================================================
139 //==========================================================================
140 IMPLEMENT_FUNCTION(VEntity
, CanSee
) {
142 VOptParamBool
disableBetterSight(false);
143 vobjGetParamSelf(Other
, disableBetterSight
);
144 //if (!Self) { VObject::VMDumpCallStack(); Sys_Error("empty `self`!"); }
145 RET_BOOL(Self
->CanSee(Other
, disableBetterSight
));
148 IMPLEMENT_FUNCTION(VEntity
, CanSeeAdv
) {
150 vobjGetParamSelf(Other
);
151 //if (!Self) { VObject::VMDumpCallStack(); Sys_Error("empty `self`!"); }
152 RET_BOOL(Self
->CanSee(Other
, false, true));
155 IMPLEMENT_FUNCTION(VEntity
, CanShoot
) {
157 vobjGetParamSelf(Other
);
158 //if (!Self) { VObject::VMDumpCallStack(); Sys_Error("empty `self`!"); }
159 RET_BOOL(Self
->CanShoot(Other
));