mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Remove even more code dupe from p_sight.c
This commit is contained in:
parent
fdd016eafc
commit
eef87c4676
1 changed files with 90 additions and 204 deletions
294
src/p_sight.c
294
src/p_sight.c
|
|
@ -38,11 +38,13 @@ typedef struct
|
||||||
boolean alreadyHates; // For bot traversal, for if the bot is already in a sector it doesn't want to be
|
boolean alreadyHates; // For bot traversal, for if the bot is already in a sector it doesn't want to be
|
||||||
} los_t;
|
} los_t;
|
||||||
|
|
||||||
|
typedef boolean (*los_init_t)(mobj_t *, mobj_t *, register los_t *);
|
||||||
typedef boolean (*los_valid_t)(seg_t *, divline_t *, register los_t *);
|
typedef boolean (*los_valid_t)(seg_t *, divline_t *, register los_t *);
|
||||||
typedef boolean (*los_valid_poly_t)(polyobj_t *, divline_t *, register los_t *);
|
typedef boolean (*los_valid_poly_t)(polyobj_t *, divline_t *, register los_t *);
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
los_init_t init; // Initialization function. If true, we'll continue with checking across linedefs. If false, end early with failure.
|
||||||
los_valid_t validate; // Validation function. If true, continue iterating for possible success. If false, end early with failure.
|
los_valid_t validate; // Validation function. If true, continue iterating for possible success. If false, end early with failure.
|
||||||
los_valid_poly_t validatePolyobj; // If not NULL, then we will also check polyobject lines using this func.
|
los_valid_poly_t validatePolyobj; // If not NULL, then we will also check polyobject lines using this func.
|
||||||
} los_funcs_t;
|
} los_funcs_t;
|
||||||
|
|
@ -507,11 +509,6 @@ static boolean P_CrossSubsector(size_t num, register los_t *los, register los_fu
|
||||||
|
|
||||||
static boolean P_CrossBSPNode(INT32 bspnum, register los_t *los, register los_funcs_t *funcs)
|
static boolean P_CrossBSPNode(INT32 bspnum, register los_t *los, register los_funcs_t *funcs)
|
||||||
{
|
{
|
||||||
if (funcs->validate == NULL)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!(bspnum & NF_SUBSECTOR))
|
while (!(bspnum & NF_SUBSECTOR))
|
||||||
{
|
{
|
||||||
register node_t *bsp = nodes + bspnum;
|
register node_t *bsp = nodes + bspnum;
|
||||||
|
|
@ -533,79 +530,22 @@ static boolean P_CrossBSPNode(INT32 bspnum, register los_t *los, register los_fu
|
||||||
return P_CrossSubsector((bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR), los, funcs);
|
return P_CrossSubsector((bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR), los, funcs);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
static boolean P_InitCheckSight(mobj_t *t1, mobj_t *t2, register los_t *los)
|
||||||
// P_CheckSight
|
|
||||||
//
|
|
||||||
// Returns true if a straight line between t1 and t2 is unobstructed.
|
|
||||||
// Uses REJECT.
|
|
||||||
//
|
|
||||||
boolean P_CheckSight(mobj_t *t1, mobj_t *t2)
|
|
||||||
{
|
{
|
||||||
const sector_t *s1, *s2;
|
const sector_t *s1, *s2;
|
||||||
size_t pnum;
|
|
||||||
los_t los;
|
|
||||||
los_funcs_t funcs;
|
|
||||||
|
|
||||||
// First check for trivial rejection.
|
|
||||||
if (!t1 || !t2)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
I_Assert(!P_MobjWasRemoved(t1));
|
|
||||||
I_Assert(!P_MobjWasRemoved(t2));
|
|
||||||
|
|
||||||
if (!t1->subsector || !t2->subsector
|
|
||||||
|| !t1->subsector->sector || !t2->subsector->sector)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
s1 = t1->subsector->sector;
|
|
||||||
s2 = t2->subsector->sector;
|
|
||||||
pnum = (s1-sectors)*numsectors + (s2-sectors);
|
|
||||||
|
|
||||||
if (rejectmatrix != NULL)
|
|
||||||
{
|
|
||||||
// Check in REJECT table.
|
|
||||||
if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// killough 11/98: shortcut for melee situations
|
|
||||||
// same subsector? obviously visible
|
|
||||||
// haleyjd 02/23/06: can't do this if there are polyobjects in the subsec
|
|
||||||
if (!t1->subsector->polyList &&
|
|
||||||
t1->subsector == t2->subsector)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// An unobstructed LOS is possible.
|
// An unobstructed LOS is possible.
|
||||||
// Now look from eyes of t1 to any part of t2.
|
// Now look from eyes of t1 to any part of t2.
|
||||||
sightcounts[1]++;
|
sightcounts[1]++;
|
||||||
|
|
||||||
validcount++;
|
|
||||||
|
|
||||||
los.compareThing = t1;
|
|
||||||
los.alreadyHates = false;
|
|
||||||
|
|
||||||
los.topslope =
|
|
||||||
(los.bottomslope = t2->z - (los.sightzstart =
|
|
||||||
t1->z + t1->height -
|
|
||||||
(t1->height>>2))) + t2->height;
|
|
||||||
los.strace.dx = (los.t2x = t2->x) - (los.strace.x = t1->x);
|
|
||||||
los.strace.dy = (los.t2y = t2->y) - (los.strace.y = t1->y);
|
|
||||||
|
|
||||||
if (t1->x > t2->x)
|
|
||||||
los.bbox[BOXRIGHT] = t1->x, los.bbox[BOXLEFT] = t2->x;
|
|
||||||
else
|
|
||||||
los.bbox[BOXRIGHT] = t2->x, los.bbox[BOXLEFT] = t1->x;
|
|
||||||
|
|
||||||
if (t1->y > t2->y)
|
|
||||||
los.bbox[BOXTOP] = t1->y, los.bbox[BOXBOTTOM] = t2->y;
|
|
||||||
else
|
|
||||||
los.bbox[BOXTOP] = t2->y, los.bbox[BOXBOTTOM] = t1->y;
|
|
||||||
|
|
||||||
// Prevent SOME cases of looking through 3dfloors
|
// Prevent SOME cases of looking through 3dfloors
|
||||||
//
|
//
|
||||||
// This WILL NOT work for things like 3d stairs with monsters behind
|
// This WILL NOT work for things like 3d stairs with monsters behind
|
||||||
// them - they will still see you! TODO: Fix.
|
// them - they will still see you! TODO: Fix.
|
||||||
//
|
//
|
||||||
|
s1 = t1->subsector->sector;
|
||||||
|
s2 = t2->subsector->sector;
|
||||||
|
|
||||||
if (s1 == s2) // Both sectors are the same.
|
if (s1 == s2) // Both sectors are the same.
|
||||||
{
|
{
|
||||||
ffloor_t *rover;
|
ffloor_t *rover;
|
||||||
|
|
@ -629,8 +569,8 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2)
|
||||||
bottomz2 = P_GetFFloorBottomZAt(rover, t2->x, t2->y);
|
bottomz2 = P_GetFFloorBottomZAt(rover, t2->x, t2->y);
|
||||||
|
|
||||||
// Check for blocking floors here.
|
// Check for blocking floors here.
|
||||||
if ((los.sightzstart < bottomz1 && t2->z >= topz2)
|
if ((los->sightzstart < bottomz1 && t2->z >= topz2)
|
||||||
|| (los.sightzstart >= topz1 && t2->z + t2->height < bottomz2))
|
|| (los->sightzstart >= topz1 && t2->z + t2->height < bottomz2))
|
||||||
{
|
{
|
||||||
// no way to see through that
|
// no way to see through that
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -641,182 +581,66 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2)
|
||||||
|
|
||||||
if (rover->fofflags & FOF_BOTHPLANES || !(rover->fofflags & FOF_INVERTPLANES))
|
if (rover->fofflags & FOF_BOTHPLANES || !(rover->fofflags & FOF_INVERTPLANES))
|
||||||
{
|
{
|
||||||
if (los.sightzstart >= topz1 && t2->z + t2->height < topz2)
|
if (los->sightzstart >= topz1 && t2->z + t2->height < topz2)
|
||||||
return false; // blocked by upper outside plane
|
return false; // blocked by upper outside plane
|
||||||
|
|
||||||
if (los.sightzstart < bottomz1 && t2->z >= bottomz2)
|
if (los->sightzstart < bottomz1 && t2->z >= bottomz2)
|
||||||
return false; // blocked by lower outside plane
|
return false; // blocked by lower outside plane
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rover->fofflags & FOF_BOTHPLANES || rover->fofflags & FOF_INVERTPLANES)
|
if (rover->fofflags & FOF_BOTHPLANES || rover->fofflags & FOF_INVERTPLANES)
|
||||||
{
|
{
|
||||||
if (los.sightzstart < topz1 && t2->z >= topz2)
|
if (los->sightzstart < topz1 && t2->z >= topz2)
|
||||||
return false; // blocked by upper inside plane
|
return false; // blocked by upper inside plane
|
||||||
|
|
||||||
if (los.sightzstart >= bottomz1 && t2->z + t2->height < bottomz2)
|
if (los->sightzstart >= bottomz1 && t2->z + t2->height < bottomz2)
|
||||||
return false; // blocked by lower inside plane
|
return false; // blocked by lower inside plane
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
funcs.validate = &P_IsVisible;
|
return true;
|
||||||
funcs.validatePolyobj = &P_IsVisiblePolyObj;
|
|
||||||
|
|
||||||
// the head node is the last node output
|
|
||||||
return P_CrossBSPNode((INT32)numnodes - 1, &los, &funcs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean P_TraceBlockingLines(mobj_t *t1, mobj_t *t2)
|
static boolean P_InitTraceBotTraversal(mobj_t *t1, mobj_t *t2, register los_t *los)
|
||||||
{
|
{
|
||||||
const sector_t *s1, *s2;
|
(void)t2;
|
||||||
size_t pnum;
|
|
||||||
los_t los;
|
|
||||||
los_funcs_t funcs;
|
|
||||||
|
|
||||||
// First check for trivial rejection.
|
|
||||||
if (!t1 || !t2)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
I_Assert(!P_MobjWasRemoved(t1));
|
|
||||||
I_Assert(!P_MobjWasRemoved(t2));
|
|
||||||
|
|
||||||
if (!t1->subsector || !t2->subsector
|
|
||||||
|| !t1->subsector->sector || !t2->subsector->sector)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
s1 = t1->subsector->sector;
|
|
||||||
s2 = t2->subsector->sector;
|
|
||||||
pnum = (s1-sectors)*numsectors + (s2-sectors);
|
|
||||||
|
|
||||||
if (rejectmatrix != NULL)
|
|
||||||
{
|
|
||||||
// Check in REJECT table.
|
|
||||||
if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// killough 11/98: shortcut for melee situations
|
|
||||||
// same subsector? obviously visible
|
|
||||||
// haleyjd 02/23/06: can't do this if there are polyobjects in the subsec
|
|
||||||
if (!t1->subsector->polyList &&
|
|
||||||
t1->subsector == t2->subsector)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
validcount++;
|
|
||||||
|
|
||||||
los.strace.dx = (los.t2x = t2->x) - (los.strace.x = t1->x);
|
|
||||||
los.strace.dy = (los.t2y = t2->y) - (los.strace.y = t1->y);
|
|
||||||
|
|
||||||
if (t1->x > t2->x)
|
|
||||||
los.bbox[BOXRIGHT] = t1->x, los.bbox[BOXLEFT] = t2->x;
|
|
||||||
else
|
|
||||||
los.bbox[BOXRIGHT] = t2->x, los.bbox[BOXLEFT] = t1->x;
|
|
||||||
|
|
||||||
if (t1->y > t2->y)
|
|
||||||
los.bbox[BOXTOP] = t1->y, los.bbox[BOXBOTTOM] = t2->y;
|
|
||||||
else
|
|
||||||
los.bbox[BOXTOP] = t2->y, los.bbox[BOXBOTTOM] = t1->y;
|
|
||||||
|
|
||||||
los.compareThing = t1;
|
|
||||||
los.alreadyHates = false;
|
|
||||||
|
|
||||||
funcs.validate = &P_CanTraceBlockingLine;
|
|
||||||
|
|
||||||
// the head node is the last node output
|
|
||||||
return P_CrossBSPNode((INT32)numnodes - 1, &los, &funcs);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// ANOTHER version, this time for bot traversal.
|
|
||||||
//
|
|
||||||
|
|
||||||
boolean P_TraceBotTraversal(mobj_t *t1, mobj_t *t2)
|
|
||||||
{
|
|
||||||
const sector_t *s1, *s2;
|
|
||||||
size_t pnum;
|
|
||||||
los_t los;
|
|
||||||
los_funcs_t funcs;
|
|
||||||
|
|
||||||
// First check for trivial rejection.
|
|
||||||
if (!t1 || !t2)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
I_Assert(!P_MobjWasRemoved(t1));
|
|
||||||
I_Assert(!P_MobjWasRemoved(t2));
|
|
||||||
|
|
||||||
if (!t1->subsector || !t2->subsector
|
|
||||||
|| !t1->subsector->sector || !t2->subsector->sector)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
s1 = t1->subsector->sector;
|
|
||||||
s2 = t2->subsector->sector;
|
|
||||||
pnum = (s1-sectors)*numsectors + (s2-sectors);
|
|
||||||
|
|
||||||
if (rejectmatrix != NULL)
|
|
||||||
{
|
|
||||||
// Check in REJECT table.
|
|
||||||
if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// killough 11/98: shortcut for melee situations
|
|
||||||
// same subsector? obviously visible
|
|
||||||
// haleyjd 02/23/06: can't do this if there are polyobjects in the subsec
|
|
||||||
if (!t1->subsector->polyList &&
|
|
||||||
t1->subsector == t2->subsector)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
validcount++;
|
|
||||||
|
|
||||||
los.strace.dx = (los.t2x = t2->x) - (los.strace.x = t1->x);
|
|
||||||
los.strace.dy = (los.t2y = t2->y) - (los.strace.y = t1->y);
|
|
||||||
|
|
||||||
if (t1->x > t2->x)
|
|
||||||
los.bbox[BOXRIGHT] = t1->x, los.bbox[BOXLEFT] = t2->x;
|
|
||||||
else
|
|
||||||
los.bbox[BOXRIGHT] = t2->x, los.bbox[BOXLEFT] = t1->x;
|
|
||||||
|
|
||||||
if (t1->y > t2->y)
|
|
||||||
los.bbox[BOXTOP] = t1->y, los.bbox[BOXBOTTOM] = t2->y;
|
|
||||||
else
|
|
||||||
los.bbox[BOXTOP] = t2->y, los.bbox[BOXBOTTOM] = t1->y;
|
|
||||||
|
|
||||||
los.compareThing = t1;
|
|
||||||
if (t1->player != NULL)
|
if (t1->player != NULL)
|
||||||
{
|
{
|
||||||
los.alreadyHates = K_BotHatesThisSector(
|
los->alreadyHates = K_BotHatesThisSector(
|
||||||
t1->player, t1->subsector->sector,
|
t1->player, t1->subsector->sector,
|
||||||
t1->x, t1->y
|
t1->x, t1->y
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
los.alreadyHates = false;
|
los->alreadyHates = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
funcs.validate = &P_CanBotTraverse;
|
return true;
|
||||||
|
|
||||||
// the head node is the last node output
|
|
||||||
return P_CrossBSPNode((INT32)numnodes - 1, &los, &funcs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean P_TraceWaypointTraversal(mobj_t *t1, mobj_t *t2)
|
static boolean P_CompareMobjsAcrossLines(mobj_t *t1, mobj_t *t2, register los_funcs_t *funcs)
|
||||||
{
|
{
|
||||||
|
los_t los;
|
||||||
const sector_t *s1, *s2;
|
const sector_t *s1, *s2;
|
||||||
size_t pnum;
|
size_t pnum;
|
||||||
los_t los;
|
|
||||||
los_funcs_t funcs;
|
|
||||||
|
|
||||||
// First check for trivial rejection.
|
// First check for trivial rejection.
|
||||||
if (!t1 || !t2)
|
if (!t1 || !t2)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
I_Assert(!P_MobjWasRemoved(t1));
|
I_Assert(!P_MobjWasRemoved(t1));
|
||||||
I_Assert(!P_MobjWasRemoved(t2));
|
I_Assert(!P_MobjWasRemoved(t2));
|
||||||
|
|
||||||
if (!t1->subsector || !t2->subsector
|
if (!t1->subsector || !t2->subsector
|
||||||
|| !t1->subsector->sector || !t2->subsector->sector)
|
|| !t1->subsector->sector || !t2->subsector->sector)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
s1 = t1->subsector->sector;
|
s1 = t1->subsector->sector;
|
||||||
s2 = t2->subsector->sector;
|
s2 = t2->subsector->sector;
|
||||||
|
|
@ -826,7 +650,9 @@ boolean P_TraceWaypointTraversal(mobj_t *t1, mobj_t *t2)
|
||||||
{
|
{
|
||||||
// Check in REJECT table.
|
// Check in REJECT table.
|
||||||
if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected
|
if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// killough 11/98: shortcut for melee situations
|
// killough 11/98: shortcut for melee situations
|
||||||
|
|
@ -834,10 +660,19 @@ boolean P_TraceWaypointTraversal(mobj_t *t1, mobj_t *t2)
|
||||||
// haleyjd 02/23/06: can't do this if there are polyobjects in the subsec
|
// haleyjd 02/23/06: can't do this if there are polyobjects in the subsec
|
||||||
if (!t1->subsector->polyList &&
|
if (!t1->subsector->polyList &&
|
||||||
t1->subsector == t2->subsector)
|
t1->subsector == t2->subsector)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
validcount++;
|
validcount++;
|
||||||
|
|
||||||
|
los.compareThing = t1;
|
||||||
|
los.alreadyHates = false;
|
||||||
|
|
||||||
|
los.topslope =
|
||||||
|
(los.bottomslope = t2->z - (los.sightzstart =
|
||||||
|
t1->z + t1->height -
|
||||||
|
(t1->height>>2))) + t2->height;
|
||||||
los.strace.dx = (los.t2x = t2->x) - (los.strace.x = t1->x);
|
los.strace.dx = (los.t2x = t2->x) - (los.strace.x = t1->x);
|
||||||
los.strace.dy = (los.t2y = t2->y) - (los.strace.y = t1->y);
|
los.strace.dy = (los.t2y = t2->y) - (los.strace.y = t1->y);
|
||||||
|
|
||||||
|
|
@ -851,11 +686,62 @@ boolean P_TraceWaypointTraversal(mobj_t *t1, mobj_t *t2)
|
||||||
else
|
else
|
||||||
los.bbox[BOXTOP] = t2->y, los.bbox[BOXBOTTOM] = t1->y;
|
los.bbox[BOXTOP] = t2->y, los.bbox[BOXBOTTOM] = t1->y;
|
||||||
|
|
||||||
los.compareThing = t1;
|
if (funcs->init != NULL)
|
||||||
los.alreadyHates = false;
|
{
|
||||||
|
if (funcs->init(t1, t2, &los) == false)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The only required function.
|
||||||
|
I_Assert(funcs->validate != NULL);
|
||||||
|
|
||||||
|
// the head node is the last node output
|
||||||
|
return P_CrossBSPNode((INT32)numnodes - 1, &los, funcs);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// P_CheckSight
|
||||||
|
//
|
||||||
|
// Returns true if a straight line between t1 and t2 is unobstructed.
|
||||||
|
// Uses REJECT.
|
||||||
|
//
|
||||||
|
boolean P_CheckSight(mobj_t *t1, mobj_t *t2)
|
||||||
|
{
|
||||||
|
los_funcs_t funcs = {0};
|
||||||
|
|
||||||
|
funcs.init = &P_InitCheckSight;
|
||||||
|
funcs.validate = &P_IsVisible;
|
||||||
|
funcs.validatePolyobj = &P_IsVisiblePolyObj;
|
||||||
|
|
||||||
|
return P_CompareMobjsAcrossLines(t1, t2, &funcs);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean P_TraceBlockingLines(mobj_t *t1, mobj_t *t2)
|
||||||
|
{
|
||||||
|
los_funcs_t funcs = {0};
|
||||||
|
|
||||||
|
funcs.validate = &P_CanTraceBlockingLine;
|
||||||
|
|
||||||
|
return P_CompareMobjsAcrossLines(t1, t2, &funcs);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean P_TraceBotTraversal(mobj_t *t1, mobj_t *t2)
|
||||||
|
{
|
||||||
|
los_funcs_t funcs = {0};
|
||||||
|
|
||||||
|
funcs.init = &P_InitTraceBotTraversal;
|
||||||
|
funcs.validate = &P_CanBotTraverse;
|
||||||
|
|
||||||
|
return P_CompareMobjsAcrossLines(t1, t2, &funcs);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean P_TraceWaypointTraversal(mobj_t *t1, mobj_t *t2)
|
||||||
|
{
|
||||||
|
los_funcs_t funcs = {0};
|
||||||
|
|
||||||
funcs.validate = &P_CanWaypointTraverse;
|
funcs.validate = &P_CanWaypointTraverse;
|
||||||
|
|
||||||
// the head node is the last node output
|
return P_CompareMobjsAcrossLines(t1, t2, &funcs);
|
||||||
return P_CrossBSPNode((INT32)numnodes - 1, &los, &funcs);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue