mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2024-11-23 21:03:02 +00:00
Serialize sector portals
This commit is contained in:
parent
6303e37fbe
commit
ff5d53e54d
7 changed files with 178 additions and 130 deletions
|
@ -2927,64 +2927,16 @@ static void HWR_Subsector(size_t num)
|
|||
}
|
||||
|
||||
//SoM: 4/7/2000: Test to make Boom water work in Hardware mode.
|
||||
gl_frontsector = R_FakeFlat(gl_frontsector, &tempsec, &floorlightlevel,
|
||||
&ceilinglightlevel, false);
|
||||
//FIXME: Use floorlightlevel and ceilinglightlevel insted of lightlevel.
|
||||
gl_frontsector = R_FakeFlat(gl_frontsector, &tempsec, &floorlightlevel, &ceilinglightlevel, false);
|
||||
|
||||
floorcolormap = ceilingcolormap = gl_frontsector->extra_colormap;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// sector lighting, DISABLED because it's done in HWR_StoreWallRange
|
||||
// ------------------------------------------------------------------------
|
||||
/// \todo store a RGBA instead of just intensity, allow coloured sector lighting
|
||||
//light = (FUBYTE)(sub->sector->lightlevel & 0xFF) / 255.0f;
|
||||
//gl_cursectorlight.red = light;
|
||||
//gl_cursectorlight.green = light;
|
||||
//gl_cursectorlight.blue = light;
|
||||
//gl_cursectorlight.alpha = light;
|
||||
|
||||
// ----- end special tricks -----
|
||||
cullFloorHeight = P_GetSectorFloorZAt (gl_frontsector, viewx, viewy);
|
||||
cullCeilingHeight = P_GetSectorCeilingZAt(gl_frontsector, viewx, viewy);
|
||||
locFloorHeight = P_GetSectorFloorZAt (gl_frontsector, gl_frontsector->soundorg.x, gl_frontsector->soundorg.y);
|
||||
locCeilingHeight = P_GetSectorCeilingZAt(gl_frontsector, gl_frontsector->soundorg.x, gl_frontsector->soundorg.y);
|
||||
|
||||
if (gl_frontsector->ffloors)
|
||||
{
|
||||
boolean anyMoved = gl_frontsector->moved;
|
||||
|
||||
if (anyMoved == false)
|
||||
{
|
||||
for (rover = gl_frontsector->ffloors; rover; rover = rover->next)
|
||||
{
|
||||
sector_t *controlSec = §ors[rover->secnum];
|
||||
if (controlSec->moved == true)
|
||||
{
|
||||
anyMoved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (anyMoved == true)
|
||||
{
|
||||
gl_frontsector->numlights = sub->sector->numlights = 0;
|
||||
R_Prep3DFloors(gl_frontsector);
|
||||
sub->sector->lightlist = gl_frontsector->lightlist;
|
||||
sub->sector->numlights = gl_frontsector->numlights;
|
||||
sub->sector->moved = gl_frontsector->moved = false;
|
||||
}
|
||||
|
||||
light = R_GetPlaneLight(gl_frontsector, locFloorHeight, false);
|
||||
if (gl_frontsector->floorlightsec == -1 && !gl_frontsector->floorlightabsolute)
|
||||
floorlightlevel = max(0, min(255, *gl_frontsector->lightlist[light].lightlevel + gl_frontsector->floorlightlevel));
|
||||
floorcolormap = *gl_frontsector->lightlist[light].extra_colormap;
|
||||
|
||||
light = R_GetPlaneLight(gl_frontsector, locCeilingHeight, false);
|
||||
if (gl_frontsector->ceilinglightsec == -1 && !gl_frontsector->ceilinglightabsolute)
|
||||
ceilinglightlevel = max(0, min(255, *gl_frontsector->lightlist[light].lightlevel + gl_frontsector->ceilinglightlevel));
|
||||
ceilingcolormap = *gl_frontsector->lightlist[light].extra_colormap;
|
||||
}
|
||||
R_CheckSectorLightLists(sub->sector, gl_frontsector, &floorlightlevel, &ceilinglightlevel, &floorcolormap, &ceilingcolormap);
|
||||
|
||||
sub->sector->extra_colormap = gl_frontsector->extra_colormap;
|
||||
|
||||
|
|
131
src/p_saveg.c
131
src/p_saveg.c
|
@ -41,20 +41,19 @@ UINT8 *save_p;
|
|||
|
||||
// Block UINT32s to attempt to ensure that the correct data is
|
||||
// being sent and received
|
||||
#define ARCHIVEBLOCK_MISC 0x7FEEDEED
|
||||
#define ARCHIVEBLOCK_PLAYERS 0x7F448008
|
||||
#define ARCHIVEBLOCK_WORLD 0x7F8C08C0
|
||||
#define ARCHIVEBLOCK_POBJS 0x7F928546
|
||||
#define ARCHIVEBLOCK_THINKERS 0x7F37037C
|
||||
#define ARCHIVEBLOCK_SPECIALS 0x7F228378
|
||||
#define ARCHIVEBLOCK_EMBLEMS 0x7F4A5445
|
||||
#define ARCHIVEBLOCK_MISC 0x7FEEDEED
|
||||
#define ARCHIVEBLOCK_PLAYERS 0x7F448008
|
||||
#define ARCHIVEBLOCK_WORLD 0x7F8C08C0
|
||||
#define ARCHIVEBLOCK_POBJS 0x7F928546
|
||||
#define ARCHIVEBLOCK_THINKERS 0x7F37037C
|
||||
#define ARCHIVEBLOCK_SPECIALS 0x7F228378
|
||||
#define ARCHIVEBLOCK_EMBLEMS 0x7F4A5445
|
||||
#define ARCHIVEBLOCK_SECPORTALS 0x7FBE34C9
|
||||
|
||||
// Note: This cannot be bigger
|
||||
// than an UINT16
|
||||
typedef enum
|
||||
{
|
||||
// RFLAGPOINT = 0x01,
|
||||
// BFLAGPOINT = 0x02,
|
||||
CAPSULE = 0x04,
|
||||
AWAYVIEW = 0x08,
|
||||
FIRSTAXIS = 0x10,
|
||||
|
@ -853,20 +852,22 @@ static void P_NetUnArchiveWaypoints(void)
|
|||
#define SD_DIFF3 0x80
|
||||
|
||||
// diff3 flags
|
||||
#define SD_TAGLIST 0x01
|
||||
#define SD_COLORMAP 0x02
|
||||
#define SD_TAGLIST 0x01
|
||||
#define SD_COLORMAP 0x02
|
||||
#define SD_CRUMBLESTATE 0x04
|
||||
#define SD_FLOORLIGHT 0x08
|
||||
#define SD_CEILLIGHT 0x10
|
||||
#define SD_FLAG 0x20
|
||||
#define SD_SPECIALFLAG 0x40
|
||||
#define SD_DIFF4 0x80
|
||||
#define SD_FLOORLIGHT 0x08
|
||||
#define SD_CEILLIGHT 0x10
|
||||
#define SD_FLAG 0x20
|
||||
#define SD_SPECIALFLAG 0x40
|
||||
#define SD_DIFF4 0x80
|
||||
|
||||
//diff4 flags
|
||||
#define SD_DAMAGETYPE 0x01
|
||||
#define SD_TRIGGERTAG 0x02
|
||||
#define SD_TRIGGERER 0x04
|
||||
#define SD_GRAVITY 0x08
|
||||
#define SD_DAMAGETYPE 0x01
|
||||
#define SD_TRIGGERTAG 0x02
|
||||
#define SD_TRIGGERER 0x04
|
||||
#define SD_GRAVITY 0x08
|
||||
#define SD_FLOORPORTAL 0x10
|
||||
#define SD_CEILPORTAL 0x20
|
||||
|
||||
#define LD_FLAG 0x01
|
||||
#define LD_SPECIAL 0x02
|
||||
|
@ -1064,6 +1065,10 @@ static void ArchiveSectors(void)
|
|||
diff4 |= SD_TRIGGERER;
|
||||
if (ss->gravity != spawnss->gravity)
|
||||
diff4 |= SD_GRAVITY;
|
||||
if (ss->portal_floor != spawnss->portal_floor)
|
||||
diff4 |= SD_FLOORPORTAL;
|
||||
if (ss->portal_ceiling != spawnss->portal_ceiling)
|
||||
diff4 |= SD_CEILPORTAL;
|
||||
|
||||
if (ss->ffloors && CheckFFloorDiff(ss))
|
||||
diff |= SD_FFLOORS;
|
||||
|
@ -1145,6 +1150,10 @@ static void ArchiveSectors(void)
|
|||
WRITEUINT8(save_p, ss->triggerer);
|
||||
if (diff4 & SD_GRAVITY)
|
||||
WRITEFIXED(save_p, ss->gravity);
|
||||
if (diff4 & SD_FLOORPORTAL)
|
||||
WRITEUINT32(save_p, ss->portal_floor);
|
||||
if (diff4 & SD_CEILPORTAL)
|
||||
WRITEUINT32(save_p, ss->portal_ceiling);
|
||||
if (diff & SD_FFLOORS)
|
||||
ArchiveFFloors(ss);
|
||||
}
|
||||
|
@ -1265,6 +1274,10 @@ static void UnArchiveSectors(void)
|
|||
sectors[i].triggerer = READUINT8(save_p);
|
||||
if (diff4 & SD_GRAVITY)
|
||||
sectors[i].gravity = READFIXED(save_p);
|
||||
if (diff4 & SD_FLOORPORTAL)
|
||||
sectors[i].portal_floor = READUINT32(save_p);
|
||||
if (diff4 & SD_CEILPORTAL)
|
||||
sectors[i].portal_ceiling = READUINT32(save_p);
|
||||
|
||||
if (diff & SD_FFLOORS)
|
||||
UnArchiveFFloors(§ors[i]);
|
||||
|
@ -4735,6 +4748,82 @@ static inline void P_NetUnArchiveEmblems(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void P_NetArchiveSectorPortals(void)
|
||||
{
|
||||
WRITEUINT32(save_p, ARCHIVEBLOCK_SECPORTALS);
|
||||
|
||||
WRITEUINT32(save_p, secportalcount);
|
||||
|
||||
for (size_t i = 0; i < secportalcount; i++)
|
||||
{
|
||||
UINT8 type = secportals[i].type;
|
||||
|
||||
WRITEUINT8(save_p, type);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case SECPORTAL_LINE:
|
||||
WRITEUINT32(save_p, SaveLine(secportals[i].line.start));
|
||||
WRITEUINT32(save_p, SaveLine(secportals[i].line.dest));
|
||||
break;
|
||||
case SECPORTAL_PLANE:
|
||||
case SECPORTAL_HORIZON:
|
||||
case SECPORTAL_FLOOR:
|
||||
case SECPORTAL_CEILING:
|
||||
WRITEUINT32(save_p, SaveSector(secportals[i].sector));
|
||||
break;
|
||||
case SECPORTAL_OBJECT:
|
||||
if (secportals[i].mobj && !P_MobjWasRemoved(secportals[i].mobj))
|
||||
SaveMobjnum(secportals[i].mobj);
|
||||
else
|
||||
WRITEUINT32(save_p, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void P_NetUnArchiveSectorPortals(void)
|
||||
{
|
||||
if (READUINT32(save_p) != ARCHIVEBLOCK_SECPORTALS)
|
||||
I_Error("Bad $$$.sav at archive block Secportals");
|
||||
|
||||
Z_Free(secportals);
|
||||
P_InitSectorPortals();
|
||||
|
||||
UINT32 count = READUINT32(save_p);
|
||||
|
||||
for (UINT32 i = 0; i < count; i++)
|
||||
{
|
||||
UINT32 id = P_NewSectorPortal();
|
||||
|
||||
sectorportal_t *secportal = &secportals[id];
|
||||
|
||||
secportal->type = READUINT8(save_p);
|
||||
|
||||
switch (secportal->type)
|
||||
{
|
||||
case SECPORTAL_LINE:
|
||||
secportal->line.start = LoadLine(READUINT32(save_p));
|
||||
secportal->line.dest = LoadLine(READUINT32(save_p));
|
||||
break;
|
||||
case SECPORTAL_PLANE:
|
||||
case SECPORTAL_HORIZON:
|
||||
case SECPORTAL_FLOOR:
|
||||
case SECPORTAL_CEILING:
|
||||
secportal->sector = LoadSector(READUINT32(save_p));
|
||||
break;
|
||||
case SECPORTAL_OBJECT:
|
||||
id = READUINT32(save_p);
|
||||
secportal->mobj = (id == 0) ? NULL : P_FindNewPosition(id);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void P_ArchiveLuabanksAndConsistency(void)
|
||||
{
|
||||
UINT8 i, banksinuse = NUM_LUABANKS;
|
||||
|
@ -4821,6 +4910,7 @@ void P_SaveNetGame(boolean resending)
|
|||
P_NetArchiveSpecials();
|
||||
P_NetArchiveColormaps();
|
||||
P_NetArchiveWaypoints();
|
||||
P_NetArchiveSectorPortals();
|
||||
}
|
||||
LUA_Archive();
|
||||
|
||||
|
@ -4861,6 +4951,7 @@ boolean P_LoadNetGame(boolean reloading)
|
|||
P_NetUnArchiveSpecials();
|
||||
P_NetUnArchiveColormaps();
|
||||
P_NetUnArchiveWaypoints();
|
||||
P_NetUnArchiveSectorPortals();
|
||||
P_RelinkPointers();
|
||||
P_FinishMobjs();
|
||||
}
|
||||
|
|
|
@ -6209,6 +6209,8 @@ UINT32 P_NewSectorPortal(void)
|
|||
secportals = Z_Realloc(secportals, secportalcapacity * sizeof(sectorportal_t), PU_LEVEL, NULL);
|
||||
}
|
||||
|
||||
secportals[i].type = SECPORTAL_NONE;
|
||||
|
||||
return (UINT32)i;
|
||||
}
|
||||
|
||||
|
@ -6536,11 +6538,13 @@ void P_SpawnSpecials(boolean fromnetsave)
|
|||
{
|
||||
sectorportal_t *floorportal = P_SectorGetFloorPortalOrCreate(lines[i].frontsector);
|
||||
floorportal->type = type;
|
||||
floorportal->sector = lines[i].frontsector;
|
||||
}
|
||||
if (ceiling)
|
||||
{
|
||||
sectorportal_t *ceilportal = P_SectorGetCeilingPortalOrCreate(lines[i].frontsector);
|
||||
ceilportal->type = type;
|
||||
ceilportal->sector = lines[i].frontsector;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
115
src/r_bsp.c
115
src/r_bsp.c
|
@ -847,51 +847,6 @@ static void R_AddPolyObjects(subsector_t *sub)
|
|||
|
||||
drawseg_t *firstseg;
|
||||
|
||||
static void R_CheckSectorLightLists(sector_t *sector, sector_t *fakeflat, INT32 *floorlightlevel, INT32 *ceilinglightlevel, extracolormap_t **floorcolormap, extracolormap_t **ceilingcolormap)
|
||||
{
|
||||
// Check and prep all 3D floors. Set the sector floor/ceiling light levels and colormaps.
|
||||
if (fakeflat->ffloors)
|
||||
{
|
||||
fixed_t floorcenterz = P_GetSectorFloorZAt (fakeflat, fakeflat->soundorg.x, fakeflat->soundorg.y);
|
||||
fixed_t ceilingcenterz = P_GetSectorCeilingZAt(fakeflat, fakeflat->soundorg.x, fakeflat->soundorg.y);
|
||||
|
||||
boolean anyMoved = fakeflat->moved;
|
||||
|
||||
if (anyMoved == false)
|
||||
{
|
||||
for (ffloor_t *rover = fakeflat->ffloors; rover; rover = rover->next)
|
||||
{
|
||||
sector_t *controlSec = §ors[rover->secnum];
|
||||
|
||||
if (controlSec->moved == true)
|
||||
{
|
||||
anyMoved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (anyMoved == true)
|
||||
{
|
||||
fakeflat->numlights = sector->numlights = 0;
|
||||
R_Prep3DFloors(fakeflat);
|
||||
sector->lightlist = fakeflat->lightlist;
|
||||
sector->numlights = fakeflat->numlights;
|
||||
sector->moved = fakeflat->moved = false;
|
||||
}
|
||||
|
||||
INT32 light = R_GetPlaneLight(fakeflat, floorcenterz, false);
|
||||
if (fakeflat->floorlightsec == -1 && !fakeflat->floorlightabsolute)
|
||||
*floorlightlevel = max(0, min(255, *fakeflat->lightlist[light].lightlevel + fakeflat->floorlightlevel));
|
||||
*floorcolormap = *fakeflat->lightlist[light].extra_colormap;
|
||||
|
||||
light = R_GetPlaneLight(fakeflat, ceilingcenterz, false);
|
||||
if (fakeflat->ceilinglightsec == -1 && !fakeflat->ceilinglightabsolute)
|
||||
*ceilinglightlevel = max(0, min(255, *fakeflat->lightlist[light].lightlevel + fakeflat->ceilinglightlevel));
|
||||
*ceilingcolormap = *fakeflat->lightlist[light].extra_colormap;
|
||||
}
|
||||
}
|
||||
|
||||
static void R_Subsector(size_t num)
|
||||
{
|
||||
INT32 count, floorlightlevel, ceilinglightlevel, light;
|
||||
|
@ -1098,18 +1053,18 @@ static void R_Subsector(size_t num)
|
|||
}
|
||||
}
|
||||
|
||||
// killough 9/18/98: Fix underwater slowdown, by passing real sector
|
||||
// instead of fake one. Improve sprite lighting by basing sprite
|
||||
// lightlevels on floor & ceiling lightlevels in the surrounding area.
|
||||
//
|
||||
// 10/98 killough:
|
||||
//
|
||||
// NOTE: TeamTNT fixed this bug incorrectly, messing up sprite lighting!!!
|
||||
// That is part of the 242 effect!!! If you simply pass sub->sector to
|
||||
// the old code you will not get correct lighting for underwater sprites!!!
|
||||
// Either you must pass the fake sector and handle validcount here, on the
|
||||
// real sector, or you must account for the lighting in some other way,
|
||||
// like passing it as an argument.
|
||||
// killough 9/18/98: Fix underwater slowdown, by passing real sector
|
||||
// instead of fake one. Improve sprite lighting by basing sprite
|
||||
// lightlevels on floor & ceiling lightlevels in the surrounding area.
|
||||
//
|
||||
// 10/98 killough:
|
||||
//
|
||||
// NOTE: TeamTNT fixed this bug incorrectly, messing up sprite lighting!!!
|
||||
// That is part of the 242 effect!!! If you simply pass sub->sector to
|
||||
// the old code you will not get correct lighting for underwater sprites!!!
|
||||
// Either you must pass the fake sector and handle validcount here, on the
|
||||
// real sector, or you must account for the lighting in some other way,
|
||||
// like passing it as an argument.
|
||||
R_AddSprites(sub->sector, (floorlightlevel+ceilinglightlevel)/2);
|
||||
|
||||
firstseg = NULL;
|
||||
|
@ -1120,7 +1075,6 @@ static void R_Subsector(size_t num)
|
|||
|
||||
while (count--)
|
||||
{
|
||||
// CONS_Debug(DBG_GAMELOGIC, "Adding normal line %d...(%d)\n", line->linedef-lines, leveltime);
|
||||
if (!line->glseg && !line->polyseg) // ignore segs that belong to polyobjects
|
||||
R_AddLine(line);
|
||||
line++;
|
||||
|
@ -1128,6 +1082,51 @@ static void R_Subsector(size_t num)
|
|||
}
|
||||
}
|
||||
|
||||
void R_CheckSectorLightLists(sector_t *sector, sector_t *fakeflat, INT32 *floorlightlevel, INT32 *ceilinglightlevel, extracolormap_t **floorcolormap, extracolormap_t **ceilingcolormap)
|
||||
{
|
||||
// Check and prep all 3D floors. Set the sector floor/ceiling light levels and colormaps.
|
||||
if (fakeflat->ffloors)
|
||||
{
|
||||
fixed_t floorcenterz = P_GetSectorFloorZAt (fakeflat, fakeflat->soundorg.x, fakeflat->soundorg.y);
|
||||
fixed_t ceilingcenterz = P_GetSectorCeilingZAt(fakeflat, fakeflat->soundorg.x, fakeflat->soundorg.y);
|
||||
|
||||
boolean anyMoved = fakeflat->moved;
|
||||
|
||||
if (anyMoved == false)
|
||||
{
|
||||
for (ffloor_t *rover = fakeflat->ffloors; rover; rover = rover->next)
|
||||
{
|
||||
sector_t *controlSec = §ors[rover->secnum];
|
||||
|
||||
if (controlSec->moved == true)
|
||||
{
|
||||
anyMoved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (anyMoved == true)
|
||||
{
|
||||
fakeflat->numlights = sector->numlights = 0;
|
||||
R_Prep3DFloors(fakeflat);
|
||||
sector->lightlist = fakeflat->lightlist;
|
||||
sector->numlights = fakeflat->numlights;
|
||||
sector->moved = fakeflat->moved = false;
|
||||
}
|
||||
|
||||
INT32 light = R_GetPlaneLight(fakeflat, floorcenterz, false);
|
||||
if (fakeflat->floorlightsec == -1 && !fakeflat->floorlightabsolute)
|
||||
*floorlightlevel = max(0, min(255, *fakeflat->lightlist[light].lightlevel + fakeflat->floorlightlevel));
|
||||
*floorcolormap = *fakeflat->lightlist[light].extra_colormap;
|
||||
|
||||
light = R_GetPlaneLight(fakeflat, ceilingcenterz, false);
|
||||
if (fakeflat->ceilinglightsec == -1 && !fakeflat->ceilinglightabsolute)
|
||||
*ceilinglightlevel = max(0, min(255, *fakeflat->lightlist[light].lightlevel + fakeflat->ceilinglightlevel));
|
||||
*ceilingcolormap = *fakeflat->lightlist[light].extra_colormap;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// R_Prep3DFloors
|
||||
//
|
||||
|
|
|
@ -57,4 +57,5 @@ boolean R_IsEmptyLine(seg_t *line, sector_t *front, sector_t *back);
|
|||
|
||||
INT32 R_GetPlaneLight(sector_t *sector, fixed_t planeheight, boolean underside);
|
||||
void R_Prep3DFloors(sector_t *sector);
|
||||
void R_CheckSectorLightLists(sector_t *sector, sector_t *fakeflat, INT32 *floorlightlevel, INT32 *ceilinglightlevel, extracolormap_t **floorcolormap, extracolormap_t **ceilingcolormap);
|
||||
#endif
|
||||
|
|
|
@ -216,7 +216,8 @@ typedef enum
|
|||
SECPORTAL_HORIZON, // Eternity Engine's horizon portal type
|
||||
SECPORTAL_OBJECT, // Uses an object as the reference view
|
||||
SECPORTAL_FLOOR, // Uses a sector as the reference view; the view height is aligned with the sector's floor
|
||||
SECPORTAL_CEILING // Uses a sector as the reference view; the view height is aligned with the sector's ceiling
|
||||
SECPORTAL_CEILING, // Uses a sector as the reference view; the view height is aligned with the sector's ceiling
|
||||
SECPORTAL_NONE = 0xFF
|
||||
} secportaltype_e;
|
||||
|
||||
typedef struct sectorportal_s
|
||||
|
|
|
@ -373,7 +373,7 @@ void Portal_AddSectorPortal (const visplane_t* plane)
|
|||
case SECPORTAL_PLANE:
|
||||
case SECPORTAL_HORIZON:
|
||||
portal->is_horizon = true;
|
||||
portal->horizon_sector = plane->sector;
|
||||
portal->horizon_sector = secportal->sector;
|
||||
x = plane->sector->soundorg.x;
|
||||
y = plane->sector->soundorg.y;
|
||||
if (secportal->type == SECPORTAL_PLANE)
|
||||
|
|
Loading…
Reference in a new issue