libvwad: updated -- vwadwrite: free file buffers on close (otherwise archive creation...
[k8vavoom.git] / source / psim / p_gameobject.cpp
blobc6984b8aef0983fca21be985b566d4c046d0787a
1 //**************************************************************************
2 //**
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
9 //**
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
12 //**
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.
16 //**
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.
21 //**
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/>.
24 //**
25 //**************************************************************************
26 #include "../gamedefs.h"
27 #include "p_decal.h"
30 IMPLEMENT_CLASS(V, GameObject)
33 #define TAG_HASH_MAX_BUCKETS (4096)
35 struct TagHashBucket {
36 int tag;
37 void *ptr;
38 int next;
42 struct TagHash {
43 TArray<TagHashBucket> buckets;
44 int first[TAG_HASH_MAX_BUCKETS];
48 //==========================================================================
50 // tagHashAlloc
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;
56 return th;
60 //==========================================================================
62 // tagHashClear
64 //==========================================================================
65 void tagHashClear (TagHash *th) {
66 if (!th) return;
67 for (int f = 0; f < TAG_HASH_MAX_BUCKETS; ++f) th->first[f] = -1;
68 th->buckets.reset();
72 //==========================================================================
74 // tagHashFree
76 //==========================================================================
77 void tagHashFree (TagHash *&th) {
78 if (th) {
79 th->buckets.clear();
80 Z_Free(th);
81 th = nullptr;
86 //==========================================================================
88 // tagHashPut
90 //==========================================================================
91 void tagHashPut (TagHash *th, int tag, void *ptr) {
92 if (!th || !ptr || !tag || tag == -1) return;
93 // check for existing ptr
94 int lastBIdx = -1;
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;
99 lastBIdx = hidx;
101 // append new bucket
102 TagHashBucket &bk = th->buckets.alloc();
103 bk.tag = tag;
104 bk.ptr = ptr;
105 bk.next = -1;
106 if (lastBIdx == -1) {
107 vassert(th->first[hash] == -1);
108 th->first[hash] = th->buckets.length()-1;
109 } else {
110 vassert(th->first[hash] != -1);
111 vassert(th->buckets[lastBIdx].next == -1);
112 th->buckets[lastBIdx].next = th->buckets.length()-1;
117 //==========================================================================
119 // tagHashCheckTag
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;
129 return false;
134 //==========================================================================
136 // tagHashFirst
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));
142 return false;
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;
147 if (hidx < 0) {
148 memset((void *)it, 0, sizeof(*it));
149 return false;
151 it->th = th;
152 it->tag = tag;
153 it->index = hidx;
154 return true;
158 //==========================================================================
160 // tagHashNext
162 //==========================================================================
163 bool tagHashNext (TagHashIter *it) {
164 if (!it || !it->th) return false;
165 const tag = th->tag;
166 int hidx = th->index;
167 if (hidx >= 0) {
168 hidx = th->buckets[hidx].next;
169 while (hidx >= 0 && th->buckets[hidx].tag != tag) hidx = th->buckets[hidx].next;
171 if (hidx < 0) {
172 memset((void *)it, 0, sizeof(*it));
173 return false;
175 it->index = hidx;
176 return true;
180 //==========================================================================
182 // tagHashCurrent
184 //==========================================================================
185 void *tagHashCurrent (TagHashIter *it) {
186 return (it && it->th ? it->th->buckets[it->index].ptr : nullptr);
191 //==========================================================================
193 // tagHashFirst
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;
201 return hidx;
205 //==========================================================================
207 // tagHashNext
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;
214 return index;
218 //==========================================================================
220 // tagHashPtr
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 //==========================================================================
231 // tagHashTag
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 () {
263 vassert(!eregions);
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 = &params;
268 reg->extraline = nullptr;
269 reg->regflags = sec_region_t::RF_BaseRegion|sec_region_t::RF_NonSolid;
270 eregions = reg;
274 //==========================================================================
276 // sector_t::DeleteAllRegions
278 //==========================================================================
279 void sector_t::DeleteAllRegions () {
280 while (eregions) {
281 sec_region_t *r = eregions;
282 eregions = r->next;
283 Z_Free(r);
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;
296 if (last) {
297 while (last->next) last = last->next;
298 last->next = reg;
299 } else {
300 eregions = reg;
302 return reg;
307 //==========================================================================
309 // seg_t::appendDecal
311 //==========================================================================
312 void seg_t::appendDecal (decal_t *dc) noexcept {
313 if (!dc) return;
314 vassert(dc->dcsurf == 0u);
315 vassert(!dc->prev);
316 vassert(!dc->next);
317 vassert(!dc->seg);
318 vassert(!dc->sreg);
319 dc->seg = this;
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 {
332 if (!dc) return;
333 vassert(dc->dcsurf == 0u);
334 vassert(dc->seg == this);
335 vassert(!dc->sreg);
336 DLListRemove(dc, decalhead, decaltail);
337 dc->prev = dc->next = nullptr;
338 dc->seg = nullptr;
342 //==========================================================================
344 // seg_t::killAllDecals
346 //==========================================================================
347 void seg_t::killAllDecals (VLevel *Level) noexcept {
348 vassert(Level);
349 while (decalhead) Level->DestroyDecal(decalhead); // this calls `removeDecal()`
350 vassert(!decalhead);
351 vassert(!decaltail);
356 //==========================================================================
358 // line_t::Box2DSide
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;
366 switch (slopetype) {
367 case ST_HORIZONTAL:
368 p1 = (tmbox[BOX2D_TOP] > v1->y);
369 p2 = (tmbox[BOX2D_BOTTOM] > v1->y);
370 if (dir.x < 0.0f) { p1 ^= 1; p2 ^= 1; }
371 break;
372 case ST_VERTICAL:
373 p1 = (tmbox[BOX2D_RIGHT] < v1->x);
374 p2 = (tmbox[BOX2D_LEFT] < v1->x);
375 if (dir.y < 0.0f) { p1 ^= 1; p2 ^= 1; }
376 break;
377 case ST_POSITIVE:
378 p1 = PointOnSide(TVec(tmbox[BOX2D_LEFT], tmbox[BOX2D_TOP], 0.0f));
379 p2 = PointOnSide(TVec(tmbox[BOX2D_RIGHT], tmbox[BOX2D_BOTTOM], 0.0f));
380 break;
381 case ST_NEGATIVE:
382 p1 = PointOnSide(TVec(tmbox[BOX2D_RIGHT], tmbox[BOX2D_TOP], 0.0f));
383 p2 = PointOnSide(TVec(tmbox[BOX2D_LEFT], tmbox[BOX2D_BOTTOM], 0.0f));
384 break;
386 return (p1 == p2 ? (int)p1 : -1);
391 //==========================================================================
393 // getFieldPtr
395 //==========================================================================
396 static vuint8 *getFieldPtr (VFieldType *fldtype, VObject *obj, VName fldname, int index, VObject *Self) {
397 if (!obj) {
398 VObject::VMDumpCallStack();
399 if (Self) {
400 GCon->Logf(NAME_Error, "cannot find field '%s' in null object, redirected from `%s`", *fldname, *Self->GetClass()->GetFullName());
401 } else {
402 GCon->Logf(NAME_Error, "cannot find field '%s' in null object", *fldname);
404 return nullptr;
406 VField *fld = obj->GetClass()->FindField(fldname);
407 if (!fld) {
408 VObject::VMDumpCallStack();
409 if (Self == obj) {
410 GCon->Logf(NAME_Error, "uservar '%s' not found in object of class `%s`", *fldname, *obj->GetClass()->GetFullName());
411 } else {
412 GCon->Logf(NAME_Error, "uservar '%s' not found in object of class `%s`, redirected from `%s`", *fldname, *obj->GetClass()->GetFullName(), *Self->GetClass()->GetFullName());
414 return nullptr;
416 if (fld->Type.Type == TYPE_Array) {
417 if (index < 0 || fld->Type.ArrayDimInternal < 0 || index >= fld->Type.ArrayDimInternal) {
418 VObject::VMDumpCallStack();
419 if (Self == obj) {
420 GCon->Logf(NAME_Error, "uservar '%s' array index out of bounds (%d) in object of class `%s`", *fldname, index, *obj->GetClass()->GetFullName());
421 } else {
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());
424 return nullptr;
426 VFieldType itt = fld->Type.GetArrayInnerType();
427 if (fldtype) *fldtype = itt;
428 return ((vuint8 *)obj)+fld->Ofs+itt.GetSize()*index;
429 } else {
430 if (index != 0) {
431 VObject::VMDumpCallStack();
432 if (Self == obj) {
433 GCon->Logf(NAME_Error, "cannot index non-array uservar '%s' in object of class `%s` (index is %d)", *fldname, *obj->GetClass()->GetFullName(), index);
434 } else {
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());
437 return nullptr;
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) {
451 if (!gobj) {
452 VObject::VMDumpCallStack();
453 GCon->Logf(NAME_Error, "cannot redirect field '%s' in none object", *fldname);
454 return nullptr;
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);
460 return nullptr;
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());
467 return nullptr;
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);
480 if (!xobj) return 0;
481 VFieldType type;
482 vuint8 *dptr = getFieldPtr(&type, xobj, fldname, index, this);
483 if (!dptr) return 0;
484 switch (type.Type) {
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);
489 return 0;
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);
500 if (!xobj) return 0;
501 VFieldType type;
502 vuint8 *dptr = getFieldPtr(&type, xobj, fldname, index, this);
503 if (!dptr) return 0;
504 switch (type.Type) {
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);
509 return 0;
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);
520 #if 0
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));
525 #endif
526 if (!xobj) return;
527 VFieldType type;
528 vuint8 *dptr = getFieldPtr(&type, xobj, fldname, index, this);
529 if (!dptr) return;
530 switch (type.Type) {
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);
546 if (!xobj) return;
547 VFieldType type;
548 vuint8 *dptr = getFieldPtr(&type, xobj, fldname, index, this);
549 if (!dptr) return;
550 switch (type.Type) {
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;
574 } else {
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);
595 if (!fld) return -1;
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;
600 return dim;
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) {
609 VName fldname;
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) {
618 VName fldname;
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) {
627 VName fldname;
628 int value;
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) {
637 VName fldname;
638 float value;
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) {
647 VName fldname;
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) {
655 VName fldname;
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) {
664 TSecPlaneRef *sp;
665 vobjGetParam(sp);
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) {
672 TSecPlaneRef *sp;
673 vobjGetParam(sp);
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) {
680 TSecPlaneRef *sp;
681 vobjGetParam(sp);
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) {
688 TSecPlaneRef *sp;
689 TVec *point;
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) {
698 TSecPlaneRef *sp;
699 TVec *point;
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) {
708 TSecPlaneRef *sp;
709 TVec *point;
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) {
718 TSecPlaneRef *sp;
719 TVec *point;
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) {
728 TSecPlaneRef *sp;
729 TVec *point;
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) {
738 TSecPlaneRef *sp;
739 TVec *point;
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) {
748 TSecPlaneRef *sp;
749 TVec *point;
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) {
758 TSecPlaneRef *sp;
759 TVec *center;
760 float radius;
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) {
770 TSecPlaneRef *sp;
771 TVec *center;
772 float radius;
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) {
782 TSecPlaneRef *sp;
783 TVec *center;
784 float radius;
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) {
794 sec_plane_t *plane;
795 TVec point;
796 vobjGetParam(plane, point);
797 //P_GET_VEC(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)"); }
801 RET_FLOAT(res);
804 //native static final float GetPointZRevClamped (const ref sec_plane_t plane, const TVec point);
805 IMPLEMENT_FUNCTION(VGameObject, GetPointZRevClamped) {
806 sec_plane_t *plane;
807 TVec point;
808 vobjGetParam(plane, point);
809 //P_GET_VEC(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)"); }
813 RET_FLOAT(res);
816 //native static final bool SectorHas3DFloors (const sector_t *sector);
817 IMPLEMENT_FUNCTION(VGameObject, SectorHas3DFloors) {
818 sector_t *sector;
819 vobjGetParam(sector);
820 //P_GET_PTR(sector_t, sector);
821 if (sector) {
822 RET_BOOL(sector->Has3DFloors());
823 } else {
824 RET_BOOL(false);
828 //native static final bool SectorHas3DSlopes (const sector_t *sector);
829 IMPLEMENT_FUNCTION(VGameObject, SectorHas3DSlopes) {
830 sector_t *sector;
831 vobjGetParam(sector);
832 //P_GET_PTR(sector_t, sector);
833 if (sector) {
834 RET_BOOL(sector->Has3DSlopes());
835 } else {
836 RET_BOOL(false);
841 //==========================================================================
843 // CheckPlanePass
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);
853 bool tmpb = false;
855 P_GET_PTR_OPT(bool, isSky, &tmpb);
856 P_GET_PTR_OPT(TVec, currhit, &ctmp);
857 P_GET_VEC(lineend);
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) {
870 sector_t *sector;
871 TVec linestart, lineend;
872 vuint32 flagmask;
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) {
884 polyobj_t *po;
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));