mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-13 16:07:45 +00:00
- implemented FMultiBlockLinesIterator for checking a position across portals. This is not fully tested yet.
This commit is contained in:
parent
884a265d4a
commit
3841a5f626
6 changed files with 236 additions and 91 deletions
19
src/actor.h
19
src/actor.h
|
@ -1185,21 +1185,10 @@ public:
|
||||||
fixedvec3 ret = { X(), Y(), Z() };
|
fixedvec3 ret = { X(), Y(), Z() };
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
fixedvec3 PosRelative(AActor *other) const
|
fixedvec3 PosRelative(AActor *other) const;
|
||||||
{
|
fixedvec3 PosRelative(sector_t *sec) const;
|
||||||
fixedvec3 ret = { X(), Y(), Z() };
|
fixedvec3 PosRelative(line_t *line) const;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
fixedvec3 PosRelative(sector_t *sec) const
|
|
||||||
{
|
|
||||||
fixedvec3 ret = { X(), Y(), Z() };
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
fixedvec3 PosRelative(line_t *line) const
|
|
||||||
{
|
|
||||||
fixedvec3 ret = { X(), Y(), Z() };
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
fixed_t SoundX() const
|
fixed_t SoundX() const
|
||||||
{
|
{
|
||||||
return X();
|
return X();
|
||||||
|
|
|
@ -2,63 +2,6 @@
|
||||||
#define P_CHECKPOS_H
|
#define P_CHECKPOS_H
|
||||||
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// This is a dynamic array which holds its first MAX_STATIC entries in normal
|
|
||||||
// variables to avoid constant allocations which this would otherwise
|
|
||||||
// require.
|
|
||||||
//
|
|
||||||
// When collecting touched portal groups the normal cases are either
|
|
||||||
// no portals == one group or
|
|
||||||
// two portals = two groups
|
|
||||||
//
|
|
||||||
// Anything with more can happen but far less infrequently, so this
|
|
||||||
// organization helps avoiding the overhead from heap allocations
|
|
||||||
// in the vast majority of situations.
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
struct FPortalGroupArray
|
|
||||||
{
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
MAX_STATIC = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
FPortalGroupArray()
|
|
||||||
{
|
|
||||||
varused = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Clear()
|
|
||||||
{
|
|
||||||
data.Clear();
|
|
||||||
varused = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Add(DWORD num)
|
|
||||||
{
|
|
||||||
if (varused < MAX_STATIC) entry[varused++] = num;
|
|
||||||
else data.Push(num);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned Size()
|
|
||||||
{
|
|
||||||
return varused + data.Size();
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD operator[](unsigned index)
|
|
||||||
{
|
|
||||||
return index < MAX_STATIC ? entry[index] : data[index - MAX_STATIC];
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
DWORD entry[MAX_STATIC];
|
|
||||||
unsigned varused;
|
|
||||||
TArray<DWORD> data;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//============================================================================
|
//============================================================================
|
||||||
//
|
//
|
||||||
// Used by P_CheckPosition and P_TryMove in place of the original
|
// Used by P_CheckPosition and P_TryMove in place of the original
|
||||||
|
@ -95,7 +38,6 @@ struct FCheckPosition
|
||||||
bool DoRipping;
|
bool DoRipping;
|
||||||
TMap<AActor*, bool> LastRipped;
|
TMap<AActor*, bool> LastRipped;
|
||||||
|
|
||||||
FPortalGroupArray Groups;
|
|
||||||
int PushTime;
|
int PushTime;
|
||||||
|
|
||||||
FCheckPosition(bool rip=false)
|
FCheckPosition(bool rip=false)
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "p_3dmidtex.h"
|
#include "p_3dmidtex.h"
|
||||||
#include "p_blockmap.h"
|
#include "p_blockmap.h"
|
||||||
#include "r_utility.h"
|
#include "r_utility.h"
|
||||||
|
#include "portal.h"
|
||||||
|
|
||||||
// State.
|
// State.
|
||||||
#include "r_state.h"
|
#include "r_state.h"
|
||||||
|
@ -572,7 +573,7 @@ FBlockLinesIterator::FBlockLinesIterator(int _minx, int _miny, int _maxx, int _m
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
FBlockLinesIterator::FBlockLinesIterator(const FBoundingBox &box)
|
void FBlockLinesIterator::init(const FBoundingBox &box)
|
||||||
{
|
{
|
||||||
validcount++;
|
validcount++;
|
||||||
maxy = GetSafeBlockY(box.Top() - bmaporgy);
|
maxy = GetSafeBlockY(box.Top() - bmaporgy);
|
||||||
|
@ -582,6 +583,10 @@ FBlockLinesIterator::FBlockLinesIterator(const FBoundingBox &box)
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FBlockLinesIterator::FBlockLinesIterator(const FBoundingBox &box)
|
||||||
|
{
|
||||||
|
init(box);
|
||||||
|
}
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
//
|
//
|
||||||
|
@ -681,6 +686,82 @@ line_t *FBlockLinesIterator::Next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
//
|
||||||
|
// FMultiBlockLinesIterator :: FMultiBlockLinesIterator
|
||||||
|
//
|
||||||
|
// An iterator that can check multiple portal groups.
|
||||||
|
//
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
FMultiBlockLinesIterator::FMultiBlockLinesIterator(AActor *origin, fixed_t checkx, fixed_t checky, fixed_t checkradius)
|
||||||
|
{
|
||||||
|
checkpoint = origin->Pos();
|
||||||
|
if (checkx != FIXED_MAX) checkpoint.x = checkx;
|
||||||
|
if (checky != FIXED_MAX) checkpoint.y = checky;
|
||||||
|
P_CollectConnectedGroups(origin->Sector->PortalGroup, checkpoint, origin->Top(), checkradius, checklist);
|
||||||
|
checkpoint.z = checkradius;
|
||||||
|
basegroup = origin->Sector->PortalGroup;
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FMultiBlockLinesIterator::Next(FMultiBlockLinesIterator::CheckResult *item)
|
||||||
|
{
|
||||||
|
line_t *line = blockIterator.Next();
|
||||||
|
if (line != NULL)
|
||||||
|
{
|
||||||
|
item->line = line;
|
||||||
|
item->position = offset;
|
||||||
|
item->portalposition = portalposition;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (checklist[index] & FPortalGroupArray::UPPER)
|
||||||
|
{
|
||||||
|
if (continueup)
|
||||||
|
{
|
||||||
|
sector_t *sector = P_PointInSector(offset.x, offset.y);
|
||||||
|
if (!sector->PortalBlocksMovement(sector_t::ceiling))
|
||||||
|
{
|
||||||
|
startIteratorForGroup(sector->SkyBoxes[sector_t::ceiling]->Sector->PortalGroup);
|
||||||
|
return Next(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (checklist[index] & FPortalGroupArray::LOWER)
|
||||||
|
{
|
||||||
|
if (continuedown)
|
||||||
|
{
|
||||||
|
sector_t *sector = P_PointInSector(offset.x, offset.y);
|
||||||
|
if (!sector->PortalBlocksMovement(sector_t::floor))
|
||||||
|
{
|
||||||
|
startIteratorForGroup(sector->SkyBoxes[sector_t::floor]->Sector->PortalGroup);
|
||||||
|
return Next(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
if (index >= checklist.Size()) return false;
|
||||||
|
startIteratorForGroup(checklist[index]);
|
||||||
|
return Next(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FMultiBlockLinesIterator::startIteratorForGroup(int group)
|
||||||
|
{
|
||||||
|
offset = Displacements(basegroup, group);
|
||||||
|
offset.x += checkpoint.x;
|
||||||
|
offset.y += checkpoint.y;
|
||||||
|
FBoundingBox box(offset.x, offset.y, checkpoint.z);
|
||||||
|
blockIterator.init(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FMultiBlockLinesIterator::Reset()
|
||||||
|
{
|
||||||
|
continueup = continueup = true;
|
||||||
|
index = -1;
|
||||||
|
portalposition = PP_ORIGIN;
|
||||||
|
startIteratorForGroup(basegroup);
|
||||||
|
}
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
//
|
//
|
||||||
// FBlockThingsIterator :: FBlockThingsIterator
|
// FBlockThingsIterator :: FBlockThingsIterator
|
||||||
|
|
112
src/p_maputl.h
112
src/p_maputl.h
|
@ -108,8 +108,72 @@ void P_LineOpening (FLineOpening &open, AActor *thing, const line_t *linedef, fi
|
||||||
class FBoundingBox;
|
class FBoundingBox;
|
||||||
struct polyblock_t;
|
struct polyblock_t;
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// This is a dynamic array which holds its first MAX_STATIC entries in normal
|
||||||
|
// variables to avoid constant allocations which this would otherwise
|
||||||
|
// require.
|
||||||
|
//
|
||||||
|
// When collecting touched portal groups the normal cases are either
|
||||||
|
// no portals == one group or
|
||||||
|
// two portals = two groups
|
||||||
|
//
|
||||||
|
// Anything with more can happen but far less infrequently, so this
|
||||||
|
// organization helps avoiding the overhead from heap allocations
|
||||||
|
// in the vast majority of situations.
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
struct FPortalGroupArray
|
||||||
|
{
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
LOWER = 0x4000,
|
||||||
|
UPPER = 0x8000,
|
||||||
|
FLAT = 0xc000,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
MAX_STATIC = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
FPortalGroupArray()
|
||||||
|
{
|
||||||
|
varused = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clear()
|
||||||
|
{
|
||||||
|
data.Clear();
|
||||||
|
varused = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Add(DWORD num)
|
||||||
|
{
|
||||||
|
if (varused < MAX_STATIC) entry[varused++] = (WORD)num;
|
||||||
|
else data.Push((WORD)num);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned Size()
|
||||||
|
{
|
||||||
|
return varused + data.Size();
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD operator[](unsigned index)
|
||||||
|
{
|
||||||
|
return index < MAX_STATIC ? entry[index] : data[index - MAX_STATIC];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
WORD entry[MAX_STATIC];
|
||||||
|
unsigned varused;
|
||||||
|
TArray<WORD> data;
|
||||||
|
};
|
||||||
|
|
||||||
class FBlockLinesIterator
|
class FBlockLinesIterator
|
||||||
{
|
{
|
||||||
|
friend class FMultiBlockLinesIterator;
|
||||||
int minx, maxx;
|
int minx, maxx;
|
||||||
int miny, maxy;
|
int miny, maxy;
|
||||||
|
|
||||||
|
@ -120,6 +184,8 @@ class FBlockLinesIterator
|
||||||
|
|
||||||
void StartBlock(int x, int y);
|
void StartBlock(int x, int y);
|
||||||
|
|
||||||
|
FBlockLinesIterator() {}
|
||||||
|
void init(const FBoundingBox &box);
|
||||||
public:
|
public:
|
||||||
FBlockLinesIterator(int minx, int miny, int maxx, int maxy, bool keepvalidcount = false);
|
FBlockLinesIterator(int minx, int miny, int maxx, int maxy, bool keepvalidcount = false);
|
||||||
FBlockLinesIterator(const FBoundingBox &box);
|
FBlockLinesIterator(const FBoundingBox &box);
|
||||||
|
@ -127,6 +193,52 @@ public:
|
||||||
void Reset() { StartBlock(minx, miny); }
|
void Reset() { StartBlock(minx, miny); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FMultiBlockLinesIterator
|
||||||
|
{
|
||||||
|
fixedvec3 checkpoint;
|
||||||
|
fixedvec2 offset;
|
||||||
|
short basegroup;
|
||||||
|
short portalposition;
|
||||||
|
WORD index;
|
||||||
|
bool continueup;
|
||||||
|
bool continuedown;
|
||||||
|
FBlockLinesIterator blockIterator;
|
||||||
|
FPortalGroupArray checklist;
|
||||||
|
|
||||||
|
void startIteratorForGroup(int group);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PP_ORIGIN,
|
||||||
|
PP_ABOVE,
|
||||||
|
PP_BELOW,
|
||||||
|
PP_THROUGHLINE
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CheckResult
|
||||||
|
{
|
||||||
|
line_t *line;
|
||||||
|
fixedvec2 position;
|
||||||
|
int portalposition;
|
||||||
|
};
|
||||||
|
|
||||||
|
FMultiBlockLinesIterator(AActor *origin, fixed_t checkx = FIXED_MAX, fixed_t checky = FIXED_MAX, fixed_t checkradius = -1);
|
||||||
|
bool Next(CheckResult *item);
|
||||||
|
void Reset();
|
||||||
|
// for stopping group traversal through portals. Only the calling code can decide whether this is needed so this needs to be set from the outside.
|
||||||
|
void StopUp()
|
||||||
|
{
|
||||||
|
continueup = false;
|
||||||
|
}
|
||||||
|
void StopDown()
|
||||||
|
{
|
||||||
|
continuedown = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class FBlockThingsIterator
|
class FBlockThingsIterator
|
||||||
{
|
{
|
||||||
int minx, maxx;
|
int minx, maxx;
|
||||||
|
|
|
@ -1053,7 +1053,7 @@ void P_CreateLinkedPortals()
|
||||||
//
|
//
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
bool P_CollectConnectedGroups(AActor *actor, fixed_t newx, fixed_t newy, FPortalGroupArray &out)
|
bool P_CollectConnectedGroups(int startgroup, const fixedvec3 &position, fixed_t upperz, fixed_t checkradius, FPortalGroupArray &out)
|
||||||
{
|
{
|
||||||
// Keep this temporary work stuff static. This function can never be called recursively
|
// Keep this temporary work stuff static. This function can never be called recursively
|
||||||
// and this would have to be reallocated for each call otherwise.
|
// and this would have to be reallocated for each call otherwise.
|
||||||
|
@ -1071,9 +1071,9 @@ bool P_CollectConnectedGroups(AActor *actor, fixed_t newx, fixed_t newy, FPortal
|
||||||
processMask.clear();
|
processMask.clear();
|
||||||
foundPortals.Clear();
|
foundPortals.Clear();
|
||||||
|
|
||||||
int thisgroup = actor->Sector->PortalGroup;
|
int thisgroup = startgroup;
|
||||||
processMask.setBit(thisgroup);
|
processMask.setBit(thisgroup);
|
||||||
out.Add(thisgroup);
|
//out.Add(thisgroup);
|
||||||
|
|
||||||
for (unsigned i = 0; i < linkedPortals.Size(); i++)
|
for (unsigned i = 0; i < linkedPortals.Size(); i++)
|
||||||
{
|
{
|
||||||
|
@ -1082,7 +1082,7 @@ bool P_CollectConnectedGroups(AActor *actor, fixed_t newx, fixed_t newy, FPortal
|
||||||
FDisplacement &disp = Displacements(thisgroup, othergroup);
|
FDisplacement &disp = Displacements(thisgroup, othergroup);
|
||||||
if (!disp.isSet) continue; // no connection.
|
if (!disp.isSet) continue; // no connection.
|
||||||
|
|
||||||
FBoundingBox box(newx + disp.pos.x, newy + disp.pos.y, actor->radius);
|
FBoundingBox box(position.x + disp.pos.x, position.y + disp.pos.y, checkradius);
|
||||||
|
|
||||||
if (box.Right() <= ld->bbox[BOXLEFT]
|
if (box.Right() <= ld->bbox[BOXLEFT]
|
||||||
|| box.Left() >= ld->bbox[BOXRIGHT]
|
|| box.Left() >= ld->bbox[BOXRIGHT]
|
||||||
|
@ -1110,27 +1110,27 @@ bool P_CollectConnectedGroups(AActor *actor, fixed_t newx, fixed_t newy, FPortal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sector_t *sec = P_PointInSector(newx, newy);
|
sector_t *sec = P_PointInSector(position.x, position.y);
|
||||||
sector_t *wsec = sec;
|
sector_t *wsec = sec;
|
||||||
while (!wsec->PortalBlocksMovement(sector_t::ceiling) && actor->Top() > wsec->SkyBoxes[sector_t::ceiling]->threshold)
|
while (!wsec->PortalBlocksMovement(sector_t::ceiling) && upperz > wsec->SkyBoxes[sector_t::ceiling]->threshold)
|
||||||
{
|
{
|
||||||
sector_t *othersec = wsec->SkyBoxes[sector_t::ceiling]->Sector;
|
sector_t *othersec = wsec->SkyBoxes[sector_t::ceiling]->Sector;
|
||||||
FDisplacement &disp = Displacements(actor->Sector->PortalGroup, othersec->PortalGroup);
|
FDisplacement &disp = Displacements(startgroup, othersec->PortalGroup);
|
||||||
fixed_t dx = newx + disp.pos.x;
|
fixed_t dx = position.x + disp.pos.x;
|
||||||
fixed_t dy = newx + disp.pos.y;
|
fixed_t dy = position.y + disp.pos.y;
|
||||||
processMask.setBit(othersec->PortalGroup);
|
processMask.setBit(othersec->PortalGroup);
|
||||||
out.Add(othersec->PortalGroup);
|
out.Add(othersec->PortalGroup|FPortalGroupArray::UPPER);
|
||||||
wsec = P_PointInSector(dx, dy); // get upper sector at the exact spot we want to check and repeat
|
wsec = P_PointInSector(dx, dy); // get upper sector at the exact spot we want to check and repeat
|
||||||
retval = true;
|
retval = true;
|
||||||
}
|
}
|
||||||
wsec = sec;
|
wsec = sec;
|
||||||
while (!wsec->PortalBlocksMovement(sector_t::floor) && actor->Z() < wsec->SkyBoxes[sector_t::floor]->threshold)
|
while (!wsec->PortalBlocksMovement(sector_t::floor) && position.z < wsec->SkyBoxes[sector_t::floor]->threshold)
|
||||||
{
|
{
|
||||||
sector_t *othersec = wsec->SkyBoxes[sector_t::ceiling]->Sector;
|
sector_t *othersec = wsec->SkyBoxes[sector_t::floor]->Sector;
|
||||||
FDisplacement &disp = Displacements(actor->Sector->PortalGroup, othersec->PortalGroup);
|
FDisplacement &disp = Displacements(startgroup, othersec->PortalGroup);
|
||||||
fixed_t dx = newx + disp.pos.x;
|
fixed_t dx = position.x + disp.pos.x;
|
||||||
fixed_t dy = newx + disp.pos.y;
|
fixed_t dy = position.y + disp.pos.y;
|
||||||
processMask.setBit(othersec->PortalGroup);
|
processMask.setBit(othersec->PortalGroup|FPortalGroupArray::LOWER);
|
||||||
out.Add(othersec->PortalGroup);
|
out.Add(othersec->PortalGroup);
|
||||||
wsec = P_PointInSector(dx, dy); // get lower sector at the exact spot we want to check and repeat
|
wsec = P_PointInSector(dx, dy); // get lower sector at the exact spot we want to check and repeat
|
||||||
retval = true;
|
retval = true;
|
||||||
|
|
23
src/portal.h
23
src/portal.h
|
@ -125,7 +125,7 @@ void P_SpawnLinePortal(line_t* line);
|
||||||
void P_FinalizePortals();
|
void P_FinalizePortals();
|
||||||
bool P_ChangePortal(line_t *ln, int thisid, int destid);
|
bool P_ChangePortal(line_t *ln, int thisid, int destid);
|
||||||
void P_CreateLinkedPortals();
|
void P_CreateLinkedPortals();
|
||||||
bool P_CollectConnectedGroups(AActor *actor, fixed_t newx, fixed_t newy, FPortalGroupArray &out);
|
bool P_CollectConnectedGroups(int startgroup, const fixedvec3 &position, fixed_t upperz, fixed_t checkradius, FPortalGroupArray &out);
|
||||||
void P_CollectLinkedPortals();
|
void P_CollectLinkedPortals();
|
||||||
inline int P_NumPortalGroups()
|
inline int P_NumPortalGroups()
|
||||||
{
|
{
|
||||||
|
@ -245,4 +245,25 @@ inline FDisplacement §or_t::CeilingDisplacement()
|
||||||
return Displacements(PortalGroup, SkyBoxes[sector_t::ceiling]->Sector->PortalGroup);
|
return Displacements(PortalGroup, SkyBoxes[sector_t::ceiling]->Sector->PortalGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fixedvec3 AActor::PosRelative(AActor *other) const
|
||||||
|
{
|
||||||
|
FDisplacement &disp = Displacements(Sector->PortalGroup, other->Sector->PortalGroup);
|
||||||
|
fixedvec3 ret = { X() + disp.pos.x, Y() + disp.pos.y, Z() };
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fixedvec3 AActor::PosRelative(sector_t *sec) const
|
||||||
|
{
|
||||||
|
FDisplacement &disp = Displacements(Sector->PortalGroup, sec->PortalGroup);
|
||||||
|
fixedvec3 ret = { X() + disp.pos.x, Y() + disp.pos.y, Z() };
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fixedvec3 AActor::PosRelative(line_t *line) const
|
||||||
|
{
|
||||||
|
FDisplacement &disp = Displacements(Sector->PortalGroup, line->frontsector->PortalGroup);
|
||||||
|
fixedvec3 ret = { X() + disp.pos.x, Y() + disp.pos.y, Z() };
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
Loading…
Reference in a new issue