mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2024-11-15 01:01:33 +00:00
Make linedef type 6 closer to ZDoom's Sector_SetPortal
This commit is contained in:
parent
03daf721ef
commit
5b387ec94a
11 changed files with 236 additions and 108 deletions
|
@ -4285,7 +4285,6 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
|
|||
|
||||
// Portal objects
|
||||
"MT_SKYBOX",
|
||||
"MT_PORTALREFPOINT",
|
||||
|
||||
// Debris
|
||||
"MT_SPARK", //spark
|
||||
|
|
|
@ -2352,11 +2352,11 @@ static void HWR_AddLine(seg_t * line)
|
|||
gl_backsector = R_FakeFlat(gl_backsector, &tempsec, NULL, NULL, true);
|
||||
|
||||
if (gl_backsector->ceilingpic == skyflatnum && gl_frontsector->ceilingpic == skyflatnum
|
||||
&& !(gl_backsector->portal_ceiling.exists || gl_frontsector->portal_ceiling.exists))
|
||||
&& !(P_SectorHasCeilingPortal(gl_backsector) || P_SectorHasCeilingPortal(gl_frontsector)))
|
||||
bothceilingssky = true;
|
||||
|
||||
if (gl_backsector->floorpic == skyflatnum && gl_frontsector->floorpic == skyflatnum
|
||||
&& !(gl_backsector->portal_floor.exists || gl_frontsector->portal_floor.exists))
|
||||
&& !(P_SectorHasFloorPortal(gl_backsector) || P_SectorHasFloorPortal(gl_frontsector)))
|
||||
bothfloorssky = true;
|
||||
|
||||
if (bothceilingssky && bothfloorssky) // everything's sky? let's save us a bit of time then
|
||||
|
|
27
src/info.c
27
src/info.c
|
@ -20759,33 +20759,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
{ // MT_PORTALREFPOINT
|
||||
781, // doomednum
|
||||
S_INVISIBLE, // spawnstate
|
||||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
8, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
12*FRACUNIT, // radius
|
||||
24*FRACUNIT, // height
|
||||
0, // display offset
|
||||
10, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_SCENERY|MF_NOBLOCKMAP|MF_NOGRAVITY, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
{ // MT_SPARK
|
||||
-1, // doomednum
|
||||
S_SPRK1, // spawnstate
|
||||
|
|
|
@ -5116,7 +5116,6 @@ typedef enum mobj_type
|
|||
|
||||
// Portal objects
|
||||
MT_SKYBOX,
|
||||
MT_PORTALREFPOINT,
|
||||
|
||||
// Debris
|
||||
MT_SPARK, //spark
|
||||
|
|
|
@ -980,9 +980,7 @@ static void P_LoadVertices(UINT8 *data)
|
|||
|
||||
static void InitializeSectorPortal(sectorportal_t *secportal)
|
||||
{
|
||||
secportal->exists = false;
|
||||
secportal->target.x = secportal->target.y = secportal->target.z = 0;
|
||||
secportal->target.angle = 0;
|
||||
memset(secportal, 0, sizeof(*secportal));
|
||||
}
|
||||
|
||||
static void P_InitializeSector(sector_t *ss)
|
||||
|
|
165
src/p_spec.c
165
src/p_spec.c
|
@ -6186,22 +6186,54 @@ fixed_t P_GetSectorGravityFactor(sector_t *sec)
|
|||
return sec->gravity;
|
||||
}
|
||||
|
||||
boolean P_CompareSectorPortals(sectorportal_t *a, sectorportal_t *b)
|
||||
static boolean P_IsSectorPortalValid(sectorportal_t *secportal)
|
||||
{
|
||||
return !memcmp(a, b, sizeof(sectorportal_t));
|
||||
switch (secportal->type)
|
||||
{
|
||||
case SECPORTAL_LINE:
|
||||
case SECPORTAL_FLOOR:
|
||||
case SECPORTAL_CEILING:
|
||||
return true;
|
||||
case SECPORTAL_OBJECT:
|
||||
return secportal->mobj && !P_MobjWasRemoved(secportal->mobj);
|
||||
case SECPORTAL_SKYBOX:
|
||||
return skyboxmo[0] && !P_MobjWasRemoved(skyboxmo[0]);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void SetSectorPortal(sectorportal_t *secportal, sector_t *target_sector, fixed_t default_z, INT32 viewpoint_tag)
|
||||
boolean P_SectorHasFloorPortal(sector_t *sector)
|
||||
{
|
||||
secportal->exists = target_sector;
|
||||
secportal->target.x = target_sector->soundorg.x;
|
||||
secportal->target.y = target_sector->soundorg.y;
|
||||
secportal->target.z = default_z;
|
||||
secportal->target.angle = 0;
|
||||
return P_IsSectorPortalValid(§or->portal_floor);
|
||||
}
|
||||
|
||||
if (viewpoint_tag <= 0)
|
||||
return;
|
||||
boolean P_SectorHasCeilingPortal(sector_t *sector)
|
||||
{
|
||||
return P_IsSectorPortalValid(§or->portal_ceiling);
|
||||
}
|
||||
|
||||
boolean P_CompareSectorPortals(sectorportal_t *a, sectorportal_t *b)
|
||||
{
|
||||
if (a->type != b->type)
|
||||
return false;
|
||||
|
||||
switch (a->type)
|
||||
{
|
||||
case SECPORTAL_LINE:
|
||||
return a->line.start == b->line.start && a->line.dest == b->line.dest;
|
||||
case SECPORTAL_FLOOR:
|
||||
case SECPORTAL_CEILING:
|
||||
return a->sector == b->sector;
|
||||
case SECPORTAL_OBJECT:
|
||||
return a->mobj == b->mobj;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static mobj_t *GetSectorPortalObject(INT32 tag)
|
||||
{
|
||||
for (thinker_t *th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
|
||||
{
|
||||
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
|
||||
|
@ -6209,18 +6241,14 @@ static void SetSectorPortal(sectorportal_t *secportal, sector_t *target_sector,
|
|||
|
||||
mobj_t *mo = (mobj_t *)th;
|
||||
|
||||
if (mo->type != MT_PORTALREFPOINT || mo->spawnpoint == NULL)
|
||||
if (mo->spawnpoint == NULL)
|
||||
continue;
|
||||
|
||||
if (!Tag_Find(&mo->spawnpoint->tags, viewpoint_tag))
|
||||
continue;
|
||||
|
||||
secportal->target.x = mo->x;
|
||||
secportal->target.y = mo->y;
|
||||
secportal->target.z = mo->z;
|
||||
secportal->target.angle = mo->angle;
|
||||
return;
|
||||
if (Tag_Find(&mo->spawnpoint->tags, tag))
|
||||
return mo;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** After the map has loaded, scans for specials that spawn 3Dfloors and
|
||||
|
@ -6390,26 +6418,97 @@ void P_SpawnSpecials(boolean fromnetsave)
|
|||
|
||||
case 6: // Sector portal
|
||||
{
|
||||
INT32 s1, s2;
|
||||
TAG_ITER_SECTORS(lines[i].args[0], s1) // Target sector tag
|
||||
int target_sector_tag = lines[i].args[0];
|
||||
int portal_type = lines[i].args[1];
|
||||
int plane_type = lines[i].args[2];
|
||||
int misc = lines[i].args[3];
|
||||
|
||||
boolean floor, ceiling;
|
||||
if (plane_type == TMP_BOTH)
|
||||
floor = ceiling = true;
|
||||
else
|
||||
{
|
||||
TAG_ITER_SECTORS(lines[i].args[1], s2) // Sector tag to make a portal to
|
||||
floor = plane_type == TMP_FLOOR;
|
||||
ceiling = plane_type == TMP_CEILING;
|
||||
}
|
||||
|
||||
INT32 s1 = -1;
|
||||
|
||||
TAG_ITER_SECTORS(target_sector_tag, s1) // Target sector tag
|
||||
{
|
||||
sectorportal_t *floorportal = §ors[s1].portal_floor;
|
||||
sectorportal_t *ceilportal = §ors[s1].portal_ceiling;
|
||||
|
||||
// Line portal
|
||||
if (portal_type == 0)
|
||||
{
|
||||
sector_t *target_sector = §ors[s2];
|
||||
boolean floor, ceiling;
|
||||
|
||||
if (lines[i].args[2] == TMP_BOTH)
|
||||
floor = ceiling = true;
|
||||
else
|
||||
INT32 linenum = -1;
|
||||
TAG_ITER_LINES(misc, linenum)
|
||||
{
|
||||
floor = lines[i].args[2] == TMP_FLOOR;
|
||||
ceiling = lines[i].args[2] == TMP_CEILING;
|
||||
if (lines[linenum].special == 6
|
||||
&& lines[linenum].args[0] == target_sector_tag
|
||||
&& lines[linenum].args[1] == portal_type
|
||||
&& lines[linenum].args[2] == plane_type
|
||||
&& lines[linenum].args[3] == 1)
|
||||
{
|
||||
if (floor)
|
||||
{
|
||||
floorportal->type = SECPORTAL_LINE;
|
||||
floorportal->line.start = &lines[i];
|
||||
floorportal->line.dest = &lines[linenum];
|
||||
}
|
||||
if (ceiling)
|
||||
{
|
||||
ceilportal->type = SECPORTAL_LINE;
|
||||
ceilportal->line.start = &lines[i];
|
||||
ceilportal->line.dest = &lines[linenum];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// Skybox portal
|
||||
else if (portal_type == 2)
|
||||
{
|
||||
if (floor)
|
||||
SetSectorPortal(§ors[s1].portal_floor, target_sector, target_sector->ceilingheight, lines[i].args[3]);
|
||||
floorportal->type = SECPORTAL_SKYBOX;
|
||||
if (ceiling)
|
||||
SetSectorPortal(§ors[s1].portal_ceiling, target_sector, target_sector->floorheight, lines[i].args[3]);
|
||||
ceilportal->type = SECPORTAL_SKYBOX;
|
||||
}
|
||||
// Plane portal
|
||||
else if (portal_type == 7)
|
||||
{
|
||||
INT32 s2 = -1;
|
||||
TAG_ITER_SECTORS(misc, s2) // Sector tag to make a portal to
|
||||
{
|
||||
sector_t *target_sector = §ors[s2];
|
||||
if (floor)
|
||||
{
|
||||
floorportal->type = SECPORTAL_CEILING;
|
||||
floorportal->sector = target_sector;
|
||||
}
|
||||
if (ceiling)
|
||||
{
|
||||
ceilportal->type = SECPORTAL_FLOOR;
|
||||
ceilportal->sector = target_sector;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Use mobj as viewpoint
|
||||
else if (portal_type == 8)
|
||||
{
|
||||
mobj_t *mobj = GetSectorPortalObject(misc);
|
||||
if (!mobj)
|
||||
break;
|
||||
if (floor)
|
||||
{
|
||||
floorportal->type = SECPORTAL_OBJECT;
|
||||
floorportal->mobj = mobj;
|
||||
}
|
||||
if (ceiling)
|
||||
{
|
||||
ceilportal->type = SECPORTAL_OBJECT;
|
||||
ceilportal->mobj = mobj;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -521,6 +521,8 @@ INT32 P_FindMinSurroundingLight(sector_t *sector, INT32 max);
|
|||
void P_SetupSignExit(player_t *player);
|
||||
boolean P_IsFlagAtBase(mobjtype_t flag);
|
||||
|
||||
boolean P_SectorHasFloorPortal(sector_t *sector);
|
||||
boolean P_SectorHasCeilingPortal(sector_t *sector);
|
||||
boolean P_CompareSectorPortals(sectorportal_t *a, sectorportal_t *b);
|
||||
|
||||
boolean P_IsMobjTouchingSectorPlane(mobj_t *mo, sector_t *sec);
|
||||
|
|
|
@ -488,12 +488,12 @@ static void R_AddLine(seg_t *line)
|
|||
// hack to allow height changes in outdoor areas
|
||||
// This is what gets rid of the upper textures if there should be sky
|
||||
if (backsector->ceilingpic == skyflatnum && frontsector->ceilingpic == skyflatnum
|
||||
&& !(backsector->portal_ceiling.exists || frontsector->portal_ceiling.exists))
|
||||
&& !(P_SectorHasCeilingPortal(backsector) || P_SectorHasCeilingPortal(frontsector)))
|
||||
bothceilingssky = true;
|
||||
|
||||
// likewise, but for floors and upper textures
|
||||
if (backsector->floorpic == skyflatnum && frontsector->floorpic == skyflatnum
|
||||
&& !(backsector->portal_floor.exists || frontsector->portal_floor.exists))
|
||||
&& !(P_SectorHasFloorPortal(backsector) || P_SectorHasFloorPortal(frontsector)))
|
||||
bothfloorssky = true;
|
||||
|
||||
if (bothceilingssky && bothfloorssky) // everything's sky? let's save us a bit of time then
|
||||
|
@ -918,7 +918,7 @@ static void R_Subsector(size_t num)
|
|||
|| (frontsector->heightsec != -1 && sectors[frontsector->heightsec].ceilingpic == skyflatnum))
|
||||
{
|
||||
floorplane = R_FindPlane(frontsector, frontsector->floorheight, frontsector->floorpic, floorlightlevel,
|
||||
frontsector->floorxoffset, frontsector->flooryoffset, frontsector->floorangle, floorcolormap, NULL, NULL, frontsector->f_slope, frontsector->portal_floor.exists ? &frontsector->portal_floor : NULL);
|
||||
frontsector->floorxoffset, frontsector->flooryoffset, frontsector->floorangle, floorcolormap, NULL, NULL, frontsector->f_slope, P_SectorHasFloorPortal(frontsector) ? &frontsector->portal_floor : NULL);
|
||||
}
|
||||
else
|
||||
floorplane = NULL;
|
||||
|
@ -929,7 +929,7 @@ static void R_Subsector(size_t num)
|
|||
{
|
||||
ceilingplane = R_FindPlane(frontsector, frontsector->ceilingheight, frontsector->ceilingpic,
|
||||
ceilinglightlevel, frontsector->ceilingxoffset, frontsector->ceilingyoffset, frontsector->ceilingangle,
|
||||
ceilingcolormap, NULL, NULL, frontsector->c_slope, frontsector->portal_ceiling.exists ? &frontsector->portal_ceiling : NULL);
|
||||
ceilingcolormap, NULL, NULL, frontsector->c_slope, P_SectorHasCeilingPortal(frontsector) ? &frontsector->portal_ceiling : NULL);
|
||||
}
|
||||
else
|
||||
ceilingplane = NULL;
|
||||
|
|
24
src/r_defs.h
24
src/r_defs.h
|
@ -208,13 +208,27 @@ typedef enum
|
|||
BT_STRONG,
|
||||
} busttype_e;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SECPORTAL_NONE,
|
||||
SECPORTAL_LINE,
|
||||
SECPORTAL_OBJECT,
|
||||
SECPORTAL_SKYBOX,
|
||||
SECPORTAL_FLOOR,
|
||||
SECPORTAL_CEILING,
|
||||
} secportaltype_e;
|
||||
|
||||
typedef struct sectorportal_s
|
||||
{
|
||||
boolean exists;
|
||||
struct {
|
||||
fixed_t x, y, z;
|
||||
angle_t angle;
|
||||
} target;
|
||||
secportaltype_e type;
|
||||
union {
|
||||
struct {
|
||||
struct line_s *start;
|
||||
struct line_s *dest;
|
||||
} line;
|
||||
struct sector_s *sector;
|
||||
struct mobj_s *mobj;
|
||||
};
|
||||
} sectorportal_t;
|
||||
|
||||
typedef struct ffloor_s
|
||||
|
|
106
src/r_portal.c
106
src/r_portal.c
|
@ -141,30 +141,17 @@ void Portal_Remove (portal_t* portal)
|
|||
Z_Free(portal);
|
||||
}
|
||||
|
||||
/** Creates a portal out of two lines and a determined screen range.
|
||||
*
|
||||
* line1 determines the entrance, and line2 the exit.
|
||||
* x1 and x2 determine the screen's column bounds.
|
||||
|
||||
* The view's offset from the entry line center is obtained,
|
||||
* and then rotated&translated to the exit line's center.
|
||||
* When the portal renders, it will create the illusion of
|
||||
* the two lines being seamed together.
|
||||
*/
|
||||
void Portal_Add2Lines (const INT32 line1, const INT32 line2, const INT32 x1, const INT32 x2)
|
||||
static void Portal_GetViewpointForLine(portal_t *portal, line_t *start, line_t *dest)
|
||||
{
|
||||
portal_t* portal = Portal_Add(x1, x2);
|
||||
|
||||
// Offset the portal view by the linedef centers
|
||||
line_t* start = &lines[line1];
|
||||
line_t* dest = &lines[line2];
|
||||
|
||||
angle_t dangle = R_PointToAngle2(0,0,dest->dx,dest->dy) - R_PointToAngle2(start->dx,start->dy,0,0);
|
||||
|
||||
fixed_t disttopoint;
|
||||
angle_t angtopoint;
|
||||
|
||||
vertex_t dest_c, start_c;
|
||||
struct {
|
||||
fixed_t x, y;
|
||||
} dest_c, start_c;
|
||||
|
||||
// looking glass center
|
||||
start_c.x = (start->v1->x + start->v2->x) / 2;
|
||||
|
@ -182,6 +169,26 @@ void Portal_Add2Lines (const INT32 line1, const INT32 line2, const INT32 x1, con
|
|||
portal->viewy = dest_c.y + FixedMul(FINESINE(angtopoint>>ANGLETOFINESHIFT), disttopoint);
|
||||
portal->viewz = viewz + dest->frontsector->floorheight - start->frontsector->floorheight;
|
||||
portal->viewangle = viewangle + dangle;
|
||||
}
|
||||
|
||||
/** Creates a portal out of two lines and a determined screen range.
|
||||
*
|
||||
* line1 determines the entrance, and line2 the exit.
|
||||
* x1 and x2 determine the screen's column bounds.
|
||||
|
||||
* The view's offset from the entry line center is obtained,
|
||||
* and then rotated&translated to the exit line's center.
|
||||
* When the portal renders, it will create the illusion of
|
||||
* the two lines being seamed together.
|
||||
*/
|
||||
void Portal_Add2Lines (const INT32 line1, const INT32 line2, const INT32 x1, const INT32 x2)
|
||||
{
|
||||
portal_t* portal = Portal_Add(x1, x2);
|
||||
|
||||
line_t* start = &lines[line1];
|
||||
line_t* dest = &lines[line2];
|
||||
|
||||
Portal_GetViewpointForLine(portal, start, dest);
|
||||
|
||||
portal->clipline = line2;
|
||||
|
||||
|
@ -314,9 +321,17 @@ void Portal_AddSkybox (const visplane_t* plane)
|
|||
void Portal_AddSectorPortal (const visplane_t* plane)
|
||||
{
|
||||
INT16 start, end;
|
||||
sector_t *source = plane->sector;
|
||||
fixed_t x, y, z, angle;
|
||||
sectorportal_t *secportal = plane->portalsector;
|
||||
|
||||
if (secportal->type == SECPORTAL_NONE)
|
||||
return;
|
||||
else if (secportal->type == SECPORTAL_SKYBOX)
|
||||
{
|
||||
Portal_AddSkybox(plane);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TrimVisplaneBounds(plane, &start, &end))
|
||||
return;
|
||||
|
||||
|
@ -324,24 +339,53 @@ void Portal_AddSectorPortal (const visplane_t* plane)
|
|||
|
||||
Portal_ClipVisplane(plane, portal);
|
||||
|
||||
fixed_t refx = source->soundorg.x - viewx;
|
||||
fixed_t refy = source->soundorg.y - viewy;
|
||||
portal->clipline = -1;
|
||||
|
||||
// Rotate the X/Y to match the target angle
|
||||
if (secportal->target.angle)
|
||||
switch (secportal->type)
|
||||
{
|
||||
fixed_t x = refx, y = refy;
|
||||
angle_t ang = secportal->target.angle >> ANGLETOFINESHIFT;
|
||||
refx = FixedMul(x, FINECOSINE(ang)) - FixedMul(y, FINESINE(ang));
|
||||
refy = FixedMul(x, FINESINE(ang)) + FixedMul(y, FINECOSINE(ang));
|
||||
case SECPORTAL_LINE:
|
||||
Portal_GetViewpointForLine(portal, secportal->line.start, secportal->line.dest);
|
||||
return;
|
||||
case SECPORTAL_OBJECT:
|
||||
if (!secportal->mobj || P_MobjWasRemoved(secportal->mobj))
|
||||
return;
|
||||
x = secportal->mobj->x;
|
||||
y = secportal->mobj->y;
|
||||
z = secportal->mobj->z;
|
||||
angle = secportal->mobj->angle;
|
||||
break;
|
||||
case SECPORTAL_FLOOR:
|
||||
x = secportal->sector->soundorg.x;
|
||||
y = secportal->sector->soundorg.y;
|
||||
z = secportal->sector->floorheight;
|
||||
angle = 0;
|
||||
break;
|
||||
case SECPORTAL_CEILING:
|
||||
x = secportal->sector->soundorg.x;
|
||||
y = secportal->sector->soundorg.y;
|
||||
z = secportal->sector->ceilingheight;
|
||||
angle = 0;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
portal->viewx = secportal->target.x - refx;
|
||||
portal->viewy = secportal->target.y - refy;
|
||||
portal->viewz = secportal->target.z + viewz;
|
||||
portal->viewangle = secportal->target.angle + viewangle;
|
||||
fixed_t refx = plane->sector->soundorg.x - viewx;
|
||||
fixed_t refy = plane->sector->soundorg.y - viewy;
|
||||
|
||||
portal->clipline = -1;
|
||||
// Rotate the X/Y to match the target angle
|
||||
if (angle != 0)
|
||||
{
|
||||
fixed_t tr_x = refx, tr_y = refy;
|
||||
angle_t ang = angle >> ANGLETOFINESHIFT;
|
||||
refx = FixedMul(tr_x, FINECOSINE(ang)) - FixedMul(tr_y, FINESINE(ang));
|
||||
refy = FixedMul(tr_x, FINESINE(ang)) + FixedMul(tr_y, FINECOSINE(ang));
|
||||
}
|
||||
|
||||
portal->viewx = x - refx;
|
||||
portal->viewy = y - refy;
|
||||
portal->viewz = z + viewz;
|
||||
portal->viewangle = angle + viewangle;
|
||||
}
|
||||
|
||||
/** Creates portals for the currently existing sky visplanes.
|
||||
|
|
|
@ -1930,7 +1930,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
|
|||
|| !P_CompareSectorPortals(&frontsector->portal_ceiling, &backsector->portal_ceiling)
|
||||
|| (frontsector->ffloors != backsector->ffloors && !Tag_Compare(&frontsector->tags, &backsector->tags)))
|
||||
{
|
||||
markceiling = true;
|
||||
markceiling = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue