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 //**************************************************************************
26 #include "../gamedefs.h"
30 IMPLEMENT_CLASS(V
, GameObject
)
33 #define TAG_HASH_MAX_BUCKETS (4096)
35 struct TagHashBucket
{
43 TArray
<TagHashBucket
> buckets
;
44 int first
[TAG_HASH_MAX_BUCKETS
];
48 //==========================================================================
52 //==========================================================================
53 TagHash
*tagHashAlloc () {
54 TagHash
*th
= (TagHash
*)Z_Calloc(sizeof(TagHash
));
55 for (int f
= 0; f
< TAG_HASH_MAX_BUCKETS
; ++f
) th
->first
[f
] = -1;
60 //==========================================================================
64 //==========================================================================
65 void tagHashClear (TagHash
*th
) {
67 for (int f
= 0; f
< TAG_HASH_MAX_BUCKETS
; ++f
) th
->first
[f
] = -1;
72 //==========================================================================
76 //==========================================================================
77 void tagHashFree (TagHash
*&th
) {
86 //==========================================================================
90 //==========================================================================
91 void tagHashPut (TagHash
*th
, int tag
, void *ptr
) {
92 if (!th
|| !ptr
|| !tag
|| tag
== -1) return;
93 // check for existing ptr
95 const int hash
= ((vuint32
)tag
)%TAG_HASH_MAX_BUCKETS
;
96 for (int hidx
= th
->first
[hash
]; hidx
>= 0; hidx
= th
->buckets
[hidx
].next
) {
97 TagHashBucket
&bk
= th
->buckets
[hidx
];
98 if (bk
.tag
== tag
&& bk
.ptr
== ptr
) return;
102 TagHashBucket
&bk
= th
->buckets
.alloc();
106 if (lastBIdx
== -1) {
107 vassert(th
->first
[hash
] == -1);
108 th
->first
[hash
] = th
->buckets
.length()-1;
110 vassert(th
->first
[hash
] != -1);
111 vassert(th
->buckets
[lastBIdx
].next
== -1);
112 th
->buckets
[lastBIdx
].next
= th
->buckets
.length()-1;
117 //==========================================================================
121 //==========================================================================
122 bool tagHashCheckTag (TagHash
*th
, int tag
, const void *ptr
) {
123 if (!th
|| !ptr
|| !tag
|| tag
== -1) return false;
124 const int hash
= ((vuint32
)tag
)%TAG_HASH_MAX_BUCKETS
;
125 for (int hidx
= th
->first
[hash
]; hidx
>= 0; hidx
= th
->buckets
[hidx
].next
) {
126 TagHashBucket
&bk
= th
->buckets
[hidx
];
127 if (bk
.tag
== tag
&& bk
.ptr
== ptr
) return true;
134 //==========================================================================
138 //==========================================================================
139 bool tagHashFirst (TagHashIter *it, TagHash *th, int tag) {
140 if (!it || !th || tag == 0) {
141 if (it) memset((void *)it, 0, sizeof(*it));
144 const int hash = ((vuint32)tag)%TAG_HASH_MAX_BUCKETS;
145 int hidx = th->first[hash];
146 while (hidx >= 0 && th->buckets[hidx].tag != tag) hidx = th->buckets[hidx].next;
148 memset((void *)it, 0, sizeof(*it));
158 //==========================================================================
162 //==========================================================================
163 bool tagHashNext (TagHashIter *it) {
164 if (!it || !it->th) return false;
166 int hidx = th->index;
168 hidx = th->buckets[hidx].next;
169 while (hidx >= 0 && th->buckets[hidx].tag != tag) hidx = th->buckets[hidx].next;
172 memset((void *)it, 0, sizeof(*it));
180 //==========================================================================
184 //==========================================================================
185 void *tagHashCurrent (TagHashIter *it) {
186 return (it && it->th ? it->th->buckets[it->index].ptr : nullptr);
191 //==========================================================================
195 //==========================================================================
196 int tagHashFirst (const TagHash
*th
, int tag
) {
197 if (!th
|| !tag
|| tag
== -1) return -1;
198 const int hash
= ((vuint32
)tag
)%TAG_HASH_MAX_BUCKETS
;
199 int hidx
= th
->first
[hash
];
200 while (hidx
>= 0 && th
->buckets
[hidx
].tag
!= tag
) hidx
= th
->buckets
[hidx
].next
;
205 //==========================================================================
209 //==========================================================================
210 int tagHashNext (const TagHash
*th
, int index
, int tag
) {
211 if (!th
|| index
< 0 || !tag
) return -1;
212 index
= th
->buckets
[index
].next
;
213 while (index
>= 0 && th
->buckets
[index
].tag
!= tag
) index
= th
->buckets
[index
].next
;
218 //==========================================================================
222 //==========================================================================
223 void *tagHashPtr (const TagHash
*th
, int index
) {
224 if (!th
|| index
< 0 || index
>= th
->buckets
.length()) return nullptr;
225 return (void *)(th
->buckets
[index
].ptr
);
229 //==========================================================================
233 //==========================================================================
234 int tagHashTag (const TagHash
*th
, int index
) {
235 if (!th
|| index
< 0 || index
>= th
->buckets
.length()) return -1;
236 return th
->buckets
[index
].tag
;
241 //==========================================================================
243 // sec_region_t::isBlockingExtraLine
245 //==========================================================================
246 bool sec_region_t::isBlockingExtraLine () const noexcept
{
247 if (!extraline
) return false;
248 if (!extraline
->frontside
|| extraline
->alpha
< 1.0f
|| (extraline
->flags
&ML_ADDITIVE
)) return false;
249 if (extraline
->frontside
->MidTexture
.id
<= 0) return false; // texture 0 is "none" (null)
250 if (efloor
.splane
->Alpha
< 1.0f
|| (efloor
.splane
->flags
&SPF_ADDITIVE
)) return false;
251 VTexture
*tex
= GTextureManager
[extraline
->frontside
->MidTexture
.id
]; // unanimated, nobody cares here
252 return (tex
&& tex
->Type
!= TEXTYPE_Null
&& !tex
->isSeeThrough());
257 //==========================================================================
259 // sector_t::CreateBaseRegion
261 //==========================================================================
262 void sector_t::CreateBaseRegion () {
264 sec_region_t
*reg
= (sec_region_t
*)Z_Calloc(sizeof(sec_region_t
));
265 reg
->efloor
.set(&floor
, false);
266 reg
->eceiling
.set(&ceiling
, false);
267 reg
->params
= ¶ms
;
268 reg
->extraline
= nullptr;
269 reg
->regflags
= sec_region_t::RF_BaseRegion
|sec_region_t::RF_NonSolid
;
274 //==========================================================================
276 // sector_t::DeleteAllRegions
278 //==========================================================================
279 void sector_t::DeleteAllRegions () {
281 sec_region_t
*r
= eregions
;
288 //==========================================================================
290 // sector_t::AllocRegion
292 //==========================================================================
293 sec_region_t
*sector_t::AllocRegion () {
294 sec_region_t
*reg
= (sec_region_t
*)Z_Calloc(sizeof(sec_region_t
));
295 sec_region_t
*last
= eregions
;
297 while (last
->next
) last
= last
->next
;
307 //==========================================================================
309 // seg_t::appendDecal
311 //==========================================================================
312 void seg_t::appendDecal (decal_t
*dc
) noexcept
{
314 vassert(dc
->dcsurf
== 0u);
320 DLListAppend(dc
, decalhead
, decaltail
);
324 //==========================================================================
326 // seg_t::removeDecal
328 // will not delete it
330 //==========================================================================
331 void seg_t::removeDecal (decal_t
*dc
) noexcept
{
333 vassert(dc
->dcsurf
== 0u);
334 vassert(dc
->seg
== this);
336 DLListRemove(dc
, decalhead
, decaltail
);
337 dc
->prev
= dc
->next
= nullptr;
342 //==========================================================================
344 // seg_t::killAllDecals
346 //==========================================================================
347 void seg_t::killAllDecals (VLevel
*Level
) noexcept
{
349 while (decalhead
) Level
->DestroyDecal(decalhead
); // this calls `removeDecal()`
356 //==========================================================================
360 // considers the line to be infinite
361 // returns side 0 or 1, -1 if box crosses the line
363 //==========================================================================
364 int line_t::Box2DSide (const float tmbox
[4]) const noexcept
{
365 unsigned p1
= 0, p2
= 0;
368 p1
= (tmbox
[BOX2D_TOP
] > v1
->y
);
369 p2
= (tmbox
[BOX2D_BOTTOM
] > v1
->y
);
370 if (dir
.x
< 0.0f
) { p1
^= 1; p2
^= 1; }
373 p1
= (tmbox
[BOX2D_RIGHT
] < v1
->x
);
374 p2
= (tmbox
[BOX2D_LEFT
] < v1
->x
);
375 if (dir
.y
< 0.0f
) { p1
^= 1; p2
^= 1; }
378 p1
= PointOnSide(TVec(tmbox
[BOX2D_LEFT
], tmbox
[BOX2D_TOP
], 0.0f
));
379 p2
= PointOnSide(TVec(tmbox
[BOX2D_RIGHT
], tmbox
[BOX2D_BOTTOM
], 0.0f
));
382 p1
= PointOnSide(TVec(tmbox
[BOX2D_RIGHT
], tmbox
[BOX2D_TOP
], 0.0f
));
383 p2
= PointOnSide(TVec(tmbox
[BOX2D_LEFT
], tmbox
[BOX2D_BOTTOM
], 0.0f
));
386 return (p1
== p2
? (int)p1
: -1);
391 //==========================================================================
395 //==========================================================================
396 static vuint8
*getFieldPtr (VFieldType
*fldtype
, VObject
*obj
, VName fldname
, int index
, VObject
*Self
) {
398 VObject::VMDumpCallStack();
400 GCon
->Logf(NAME_Error
, "cannot find field '%s' in null object, redirected from `%s`", *fldname
, *Self
->GetClass()->GetFullName());
402 GCon
->Logf(NAME_Error
, "cannot find field '%s' in null object", *fldname
);
406 VField
*fld
= obj
->GetClass()->FindField(fldname
);
408 VObject::VMDumpCallStack();
410 GCon
->Logf(NAME_Error
, "uservar '%s' not found in object of class `%s`", *fldname
, *obj
->GetClass()->GetFullName());
412 GCon
->Logf(NAME_Error
, "uservar '%s' not found in object of class `%s`, redirected from `%s`", *fldname
, *obj
->GetClass()->GetFullName(), *Self
->GetClass()->GetFullName());
416 if (fld
->Type
.Type
== TYPE_Array
) {
417 if (index
< 0 || fld
->Type
.ArrayDimInternal
< 0 || index
>= fld
->Type
.ArrayDimInternal
) {
418 VObject::VMDumpCallStack();
420 GCon
->Logf(NAME_Error
, "uservar '%s' array index out of bounds (%d) in object of class `%s`", *fldname
, index
, *obj
->GetClass()->GetFullName());
422 GCon
->Logf(NAME_Error
, "uservar '%s' array index out of bounds (%d) in object of class `%s`, redirected from `%s`", *fldname
, index
, *obj
->GetClass()->GetFullName(), *Self
->GetClass()->GetFullName());
426 VFieldType itt
= fld
->Type
.GetArrayInnerType();
427 if (fldtype
) *fldtype
= itt
;
428 return ((vuint8
*)obj
)+fld
->Ofs
+itt
.GetSize()*index
;
431 VObject::VMDumpCallStack();
433 GCon
->Logf(NAME_Error
, "cannot index non-array uservar '%s' in object of class `%s` (index is %d)", *fldname
, *obj
->GetClass()->GetFullName(), index
);
435 GCon
->Logf(NAME_Error
, "cannot index non-array uservar '%s' in object of class `%s` (index is %d), redirected from `%s`", *fldname
, *obj
->GetClass()->GetFullName(), index
, *Self
->GetClass()->GetFullName());
439 if (fldtype
) *fldtype
= fld
->Type
;
440 return ((vuint8
*)obj
)+fld
->Ofs
;
445 //==========================================================================
447 // VGameObject::getRedirection
449 //==========================================================================
450 static VObject
*getRedirection (VName fldname
, VGameObject
*gobj
) {
452 VObject::VMDumpCallStack();
453 GCon
->Logf(NAME_Error
, "cannot redirect field '%s' in none object", *fldname
);
456 if (gobj
->IsDestroyed()) {
457 VObject::VMDumpCallStack();
458 //Host_Error("cannot redirect field '%s' in dead object", *fldname);
459 GCon
->Logf(NAME_Warning
, "cannot redirect field '%s' in dead object", *fldname
);
462 if (!gobj
->_stateRouteSelf
) return gobj
;
463 if (gobj
->_stateRouteSelf
->IsDestroyed()) {
464 VObject::VMDumpCallStack();
465 //Host_Error("cannot redirect field '%s' in dead object, from '%s'", *fldname, *gobj->GetClass()->GetFullName());
466 GCon
->Logf(NAME_Warning
, "cannot redirect field '%s' in dead object, from '%s'", *fldname
, *gobj
->GetClass()->GetFullName());
469 return gobj
->_stateRouteSelf
;
473 //==========================================================================
475 // VGameObject::_get_user_var_int
477 //==========================================================================
478 int VGameObject::_get_user_var_int (VName fldname
, int index
) {
479 VObject
*xobj
= getRedirection(fldname
, this);
482 vuint8
*dptr
= getFieldPtr(&type
, xobj
, fldname
, index
, this);
485 case TYPE_Int
: return *(const vint32
*)dptr
;
486 case TYPE_Float
: return *(const float *)dptr
;
488 GCon
->Logf(NAME_Error
, "cannot get non-int uservar '%s'", *fldname
);
493 //==========================================================================
495 // VGameObject::_get_user_var_float
497 //==========================================================================
498 float VGameObject::_get_user_var_float (VName fldname
, int index
) {
499 VObject
*xobj
= getRedirection(fldname
, this);
502 vuint8
*dptr
= getFieldPtr(&type
, xobj
, fldname
, index
, this);
505 case TYPE_Int
: return *(const vint32
*)dptr
;
506 case TYPE_Float
: return *(const float *)dptr
;
508 GCon
->Logf(NAME_Error
, "cannot get non-float uservar '%s'", *fldname
);
513 //==========================================================================
515 // VGameObject::_set_user_var_int
517 //==========================================================================
518 void VGameObject::_set_user_var_int (VName fldname
, int value
, int index
) {
519 VObject
*xobj
= getRedirection(fldname
, this);
521 if (VStr::strEquCI(*fldname
, "user_trailstep")) {
522 GCon
->Logf(NAME_Debug
, "_set_user_var_int: fld='%s'; value=%d; self=%s:%u; redir=%s:%u", *fldname
, value
,
523 GetClass()->GetName(), GetUniqueId(), (xobj
? xobj
->GetClass()->GetName() : "<none>"), (xobj
? xobj
->GetUniqueId() : 0));
528 vuint8
*dptr
= getFieldPtr(&type
, xobj
, fldname
, index
, this);
531 case TYPE_Int
: *(vint32
*)dptr
= value
; return;
532 case TYPE_Float
: *(float *)dptr
= value
; return;
534 VObject::VMDumpCallStack();
535 GCon
->Logf(NAME_Error
, "cannot set non-int uservar '%s'", *fldname
);
539 //==========================================================================
541 // VGameObject::_set_user_var_float
543 //==========================================================================
544 void VGameObject::_set_user_var_float (VName fldname
, float value
, int index
) {
545 VObject
*xobj
= getRedirection(fldname
, this);
548 vuint8
*dptr
= getFieldPtr(&type
, xobj
, fldname
, index
, this);
551 case TYPE_Int
: *(vint32
*)dptr
= value
; return;
552 case TYPE_Float
: *(float *)dptr
= value
; return;
554 GCon
->Logf(NAME_Error
, "cannot set non-float uservar '%s'", *fldname
);
558 //==========================================================================
560 // VGameObject::_get_user_var_type
562 //==========================================================================
563 VGameObject::UserVarFieldType
VGameObject::_get_user_var_type (VName fldname
) {
564 VObject
*xobj
= getRedirection(fldname
, this);
565 if (!xobj
) return UserVarFieldType::None
; // dunno
566 VField
*fld
= xobj
->GetClass()->FindField(fldname
);
567 if (!fld
) return UserVarFieldType::None
;
568 if (fld
->Type
.Type
== TYPE_Array
) {
569 if (fld
->Type
.IsArray2D()) return UserVarFieldType::None
; // invalid
570 switch (fld
->Type
.GetArrayInnerType().Type
) {
571 case TYPE_Int
: return UserVarFieldType::IntArray
;
572 case TYPE_Float
: return UserVarFieldType::FloatArray
;
575 switch (fld
->Type
.Type
) {
576 case TYPE_Int
: return UserVarFieldType::Int
;
577 case TYPE_Float
: return UserVarFieldType::Float
;
580 return UserVarFieldType::None
; // invalid
584 //==========================================================================
586 // VGameObject::_get_user_var_dim
588 // array dimension; -1: not an array, or absent
590 //==========================================================================
591 int VGameObject::_get_user_var_dim (VName fldname
) {
592 VObject
*xobj
= getRedirection(fldname
, this);
593 if (!xobj
) return -1;
594 VField
*fld
= xobj
->GetClass()->FindField(fldname
);
596 if (fld
->Type
.Type
== TYPE_Array
) {
597 if (fld
->Type
.IsArray2D()) return -1; // invalid
598 int dim
= fld
->Type
.ArrayDimInternal
;
599 if (dim
< 0) return -1;
602 return -1; // invalid
606 // ////////////////////////////////////////////////////////////////////////// //
607 //native final int _get_user_var_int (name fldname, optional int index);
608 IMPLEMENT_FUNCTION(VGameObject
, _get_user_var_int
) {
610 VOptParamInt
index(0);
611 vobjGetParamSelf(fldname
, index
);
612 if (!Self
) { VObject::VMDumpCallStack(); Host_Error("cannot get field '%s' from null object", *fldname
); }
613 RET_INT(Self
->_get_user_var_int(fldname
, index
));
616 //native final float _get_user_var_float (name fldname, optional int index);
617 IMPLEMENT_FUNCTION(VGameObject
, _get_user_var_float
) {
619 VOptParamInt
index(0);
620 vobjGetParamSelf(fldname
, index
);
621 if (!Self
) { VObject::VMDumpCallStack(); Host_Error("cannot get field '%s' from null object", *fldname
); }
622 RET_FLOAT(Self
->_get_user_var_float(fldname
, index
));
625 //native final void _set_user_var_int (name fldname, int value, optional int index);
626 IMPLEMENT_FUNCTION(VGameObject
, _set_user_var_int
) {
629 VOptParamInt
index(0);
630 vobjGetParamSelf(fldname
, value
, index
);
631 if (!Self
) { VObject::VMDumpCallStack(); Host_Error("cannot set field '%s' in null object", *fldname
); }
632 Self
->_set_user_var_int(fldname
, value
, index
);
635 //native final void _set_user_var_float (name fldname, float value, optional int index);
636 IMPLEMENT_FUNCTION(VGameObject
, _set_user_var_float
) {
639 VOptParamInt
index(0);
640 vobjGetParamSelf(fldname
, value
, index
);
641 if (!Self
) { VObject::VMDumpCallStack(); Host_Error("cannot set field '%s' in null object", *fldname
); }
642 Self
->_set_user_var_float(fldname
, value
, index
);
645 // native final UserVarFieldType _get_user_var_type (name fldname);
646 IMPLEMENT_FUNCTION(VGameObject
, _get_user_var_type
) {
648 vobjGetParamSelf(fldname
);
649 if (!Self
) { VObject::VMDumpCallStack(); Host_Error("cannot check field '%s' in null object", *fldname
); }
650 RET_INT(Self
->_get_user_var_type(fldname
));
653 // native final int _get_user_var_dim (name fldname); // array dimension; -1: not an array, or absent
654 IMPLEMENT_FUNCTION(VGameObject
, _get_user_var_dim
) {
656 vobjGetParamSelf(fldname
);
657 if (!Self
) { VObject::VMDumpCallStack(); Host_Error("cannot check field '%s' in null object", *fldname
); }
658 RET_INT(Self
->_get_user_var_dim(fldname
));
662 //native static final TVec spGetNormal (const ref TSecPlaneRef sp);
663 IMPLEMENT_FUNCTION(VGameObject
, spGetNormal
) {
666 //P_GET_PTR(TSecPlaneRef, sp);
667 RET_VEC(sp
->GetNormal());
670 //native static final float spGetNormalZ (const ref TSecPlaneRef sp);
671 IMPLEMENT_FUNCTION(VGameObject
, spGetNormalZ
) {
674 //P_GET_PTR(TSecPlaneRef, sp);
675 RET_FLOAT(sp
->GetNormalZ());
678 //native static final float spGetDist (const ref TSecPlaneRef sp);
679 IMPLEMENT_FUNCTION(VGameObject
, spGetDist
) {
682 //P_GET_PTR(TSecPlaneRef, sp);
683 RET_FLOAT(sp
->GetDist());
686 //native static final float spGetPointZ (const ref TSecPlaneRef sp, const ref TVec p);
687 IMPLEMENT_FUNCTION(VGameObject
, spGetPointZ
) {
690 vobjGetParam(sp
, point
);
691 //P_GET_PTR(TVec, point);
692 //P_GET_PTR(TSecPlaneRef, sp);
693 RET_FLOAT(sp
->GetPointZClamped(point
->x
, point
->y
));
696 //native static final float spDotPoint (const ref TSecPlaneRef sp, const ref TVec point);
697 IMPLEMENT_FUNCTION(VGameObject
, spDotPoint
) {
700 vobjGetParam(sp
, point
);
701 //P_GET_PTR(TVec, point);
702 //P_GET_PTR(TSecPlaneRef, sp);
703 RET_FLOAT(sp
->DotPoint(*point
));
706 //native static final float spPointDistance (const ref TSecPlaneRef sp, const ref TVec point);
707 IMPLEMENT_FUNCTION(VGameObject
, spPointDistance
) {
710 vobjGetParam(sp
, point
);
711 //P_GET_PTR(TVec, point);
712 //P_GET_PTR(TSecPlaneRef, sp);
713 RET_FLOAT(sp
->PointDistance(*point
));
716 //native static final int spPointOnSide (const ref TSecPlaneRef sp, const ref TVec point);
717 IMPLEMENT_FUNCTION(VGameObject
, spPointOnSide
) {
720 vobjGetParam(sp
, point
);
721 //P_GET_PTR(TVec, point);
722 //P_GET_PTR(TSecPlaneRef, sp);
723 RET_INT(sp
->PointOnSide(*point
));
726 //native static final int spPointOnSideThreshold (const ref TSecPlaneRef sp, const ref TVec point);
727 IMPLEMENT_FUNCTION(VGameObject
, spPointOnSideThreshold
) {
730 vobjGetParam(sp
, point
);
731 //P_GET_PTR(TVec, point);
732 //P_GET_PTR(TSecPlaneRef, sp);
733 RET_INT(sp
->PointOnSideThreshold(*point
));
736 //native static final int spPointOnSideFri (const ref TSecPlaneRef sp, const ref TVec point);
737 IMPLEMENT_FUNCTION(VGameObject
, spPointOnSideFri
) {
740 vobjGetParam(sp
, point
);
741 //P_GET_PTR(TVec, point);
742 //P_GET_PTR(TSecPlaneRef, sp);
743 RET_INT(sp
->PointOnSideFri(*point
));
746 //native static final int spPointOnSide2 (const ref TSecPlaneRef sp, const ref TVec point);
747 IMPLEMENT_FUNCTION(VGameObject
, spPointOnSide2
) {
750 vobjGetParam(sp
, point
);
751 //P_GET_PTR(TVec, point);
752 //P_GET_PTR(TSecPlaneRef, sp);
753 RET_INT(sp
->PointOnSide2(*point
));
756 //native static final bool spSphereTouches (const ref TSecPlaneRef sp, const ref TVec center, float radius);
757 IMPLEMENT_FUNCTION(VGameObject
, spSphereTouches
) {
761 vobjGetParam(sp
, center
, radius
);
762 //P_GET_FLOAT(radius);
763 //P_GET_PTR(TVec, center);
764 //P_GET_PTR(TSecPlaneRef, sp);
765 RET_BOOL(sp
->SphereTouches(*center
, radius
));
768 //native static final int spSphereOnSide (const ref TSecPlaneRef sp, const ref TVec center, float radius);
769 IMPLEMENT_FUNCTION(VGameObject
, spSphereOnSide
) {
773 vobjGetParam(sp
, center
, radius
);
774 //P_GET_FLOAT(radius);
775 //P_GET_PTR(TVec, center);
776 //P_GET_PTR(TSecPlaneRef, sp);
777 RET_INT(sp
->SphereOnSide(*center
, radius
));
780 //native static final int spSphereOnSide2 (const ref TSecPlaneRef sp, const ref TVec center, float radius);
781 IMPLEMENT_FUNCTION(VGameObject
, spSphereOnSide2
) {
785 vobjGetParam(sp
, center
, radius
);
786 //P_GET_FLOAT(radius);
787 //P_GET_PTR(TVec, center);
788 //P_GET_PTR(TSecPlaneRef, sp);
789 RET_INT(sp
->SphereOnSide2(*center
, radius
));
792 //native static final float GetPointZClamped (const ref sec_plane_t plane, const TVec point);
793 IMPLEMENT_FUNCTION(VGameObject
, GetPointZClamped
) {
796 vobjGetParam(plane
, point
);
798 //P_GET_PTR(sec_plane_t, plane);
799 const float res
= plane
->GetPointZClamped(point
);
800 if (!isFiniteF(res
)) { VObject::VMDumpCallStack(); Sys_Error("invalid call to `GetPlanePointZ()` (probably called with vertical plane)"); }
804 //native static final float GetPointZRevClamped (const ref sec_plane_t plane, const TVec point);
805 IMPLEMENT_FUNCTION(VGameObject
, GetPointZRevClamped
) {
808 vobjGetParam(plane
, point
);
810 //P_GET_PTR(sec_plane_t, plane);
811 const float res
= plane
->GetPointZRevClamped(point
);
812 if (!isFiniteF(res
)) { VObject::VMDumpCallStack(); Sys_Error("invalid call to `GetPlanePointZ()` (probably called with vertical plane)"); }
816 //native static final bool SectorHas3DFloors (const sector_t *sector);
817 IMPLEMENT_FUNCTION(VGameObject
, SectorHas3DFloors
) {
819 vobjGetParam(sector
);
820 //P_GET_PTR(sector_t, sector);
822 RET_BOOL(sector
->Has3DFloors());
828 //native static final bool SectorHas3DSlopes (const sector_t *sector);
829 IMPLEMENT_FUNCTION(VGameObject
, SectorHas3DSlopes
) {
831 vobjGetParam(sector
);
832 //P_GET_PTR(sector_t, sector);
834 RET_BOOL(sector
->Has3DSlopes());
841 //==========================================================================
845 // checks for plane hit, returns hit point and `false` if hit
846 // plane flags should be already checked
848 //==========================================================================
849 //static final bool CheckPlanePass (const ref TSecPlaneRef plane, const TVec linestart, const TVec lineend,
850 // optional out TVec currhit, optional out bool isSky);
851 IMPLEMENT_FUNCTION(VGameObject
, CheckPlanePass
) {
852 TVec
ctmp(0.0f
, 0.0f
, 0.0f
);
855 P_GET_PTR_OPT(bool, isSky
, &tmpb
);
856 P_GET_PTR_OPT(TVec
, currhit
, &ctmp
);
858 P_GET_VEC(linestart
);
859 P_GET_PTR(TSecPlaneRef
, plane
);
861 RET_BOOL(VLevel::CheckPlanePass(*plane
, linestart
, lineend
, *currhit
, *isSky
));
865 //static final bool CheckPassPlanes (const sector_t *sector,
866 // TVec linestart, TVec lineend, int flagmask,
867 // optional out TVec outHitPoint, optional out TVec outHitNormal,
868 // optional out bool outIsSky);
869 IMPLEMENT_FUNCTION(VGameObject
, CheckPassPlanes
) {
871 TVec linestart
, lineend
;
873 VOptParamPtr
<TVec
> outHitPoint
;
874 VOptParamPtr
<TVec
> outHitNormal
;
875 VOptParamPtr
<bool> outIsSky
;
876 vobjGetParam(sector
, linestart
, lineend
, flagmask
, outHitPoint
, outHitNormal
, outIsSky
);
877 RET_BOOL(VLevel::CheckPassPlanes(sector
, linestart
, lineend
, flagmask
, outHitPoint
, outHitNormal
, outIsSky
, nullptr));
881 //static final float CheckPObjPassPlanes (const polyobj_t *po, TVec linestart, TVec lineend,
882 // optional out TVec outHitPoint, optional out TVec outHitNormal);
883 IMPLEMENT_FUNCTION(VGameObject
, CheckPObjPassPlanes
) {
885 TVec linestart
, lineend
;
886 VOptParamPtr
<TVec
> outHitPoint
;
887 VOptParamPtr
<TVec
> outHitNormal
;
888 vobjGetParam(po
, linestart
, lineend
, outHitPoint
, outHitNormal
);
889 RET_FLOAT(VLevel::CheckPObjPassPlanes(po
, linestart
, lineend
, outHitPoint
, outHitNormal
, nullptr));