Destructible geometry - minor fixes and 3D floor support

This commit is contained in:
ZZYZX 2018-11-04 06:53:37 +02:00 committed by Christoph Oelckers
parent 752a64c840
commit d85e5afdfb
17 changed files with 317 additions and 53 deletions

View file

@ -1188,6 +1188,7 @@ public:
AActor *BlockingMobj; // Actor that blocked the last move AActor *BlockingMobj; // Actor that blocked the last move
line_t *BlockingLine; // Line that blocked the last move line_t *BlockingLine; // Line that blocked the last move
sector_t *Blocking3DFloor; // 3D floor that blocked the last move (if any)
sector_t *BlockingCeiling; // Sector that blocked the last move (ceiling plane slope) sector_t *BlockingCeiling; // Sector that blocked the last move (ceiling plane slope)
sector_t *BlockingFloor; // Sector that blocked the last move (floor plane slope) sector_t *BlockingFloor; // Sector that blocked the last move (floor plane slope)

View file

@ -670,10 +670,12 @@ xx(Owner)
xx(HealthFloor) xx(HealthFloor)
xx(HealthCeiling) xx(HealthCeiling)
xx(Health3D)
xx(DamageSpecial) xx(DamageSpecial)
xx(DeathSpecial) xx(DeathSpecial)
xx(HealthFloorGroup) xx(HealthFloorGroup)
xx(HealthCeilingGroup) xx(HealthCeilingGroup)
xx(Health3DGroup)
xx(HealthGroup) xx(HealthGroup)
// USDF keywords // USDF keywords

View file

@ -358,6 +358,7 @@ bool P_CheckFor3DFloorHit(AActor * mo, double z, bool trigger)
if (fabs(z - rover->top.plane->ZatPoint(mo)) < EQUAL_EPSILON) if (fabs(z - rover->top.plane->ZatPoint(mo)) < EQUAL_EPSILON)
{ {
mo->BlockingFloor = rover->model; mo->BlockingFloor = rover->model;
mo->Blocking3DFloor = rover->model;
if (trigger) rover->model->TriggerSectorActions (mo, SECSPAC_HitFloor); if (trigger) rover->model->TriggerSectorActions (mo, SECSPAC_HitFloor);
return true; return true;
} }
@ -385,6 +386,7 @@ bool P_CheckFor3DCeilingHit(AActor * mo, double z, bool trigger)
if(fabs(z - rover->bottom.plane->ZatPoint(mo)) < EQUAL_EPSILON) if(fabs(z - rover->bottom.plane->ZatPoint(mo)) < EQUAL_EPSILON)
{ {
mo->BlockingCeiling = rover->model; mo->BlockingCeiling = rover->model;
mo->Blocking3DFloor = rover->model;
if (trigger) rover->model->TriggerSectorActions (mo, SECSPAC_HitCeiling); if (trigger) rover->model->TriggerSectorActions (mo, SECSPAC_HitCeiling);
return true; return true;
} }
@ -762,8 +764,10 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li
FTextureID highestfloorpic; FTextureID highestfloorpic;
int highestfloorterrain = -1; int highestfloorterrain = -1;
FTextureID lowestceilingpic; FTextureID lowestceilingpic;
sector_t *lowestceilingsec = NULL, *highestfloorsec = NULL; sector_t *lowestceilingsec = nullptr, *highestfloorsec = nullptr;
secplane_t *highestfloorplanes[2] = { &open.frontfloorplane, &open.backfloorplane }; secplane_t *highestfloorplanes[2] = { &open.frontfloorplane, &open.backfloorplane };
F3DFloor *lowestceilingffloor = nullptr;
F3DFloor *highestfloorffloor = nullptr;
highestfloorpic.SetInvalid(); highestfloorpic.SetInvalid();
lowestceilingpic.SetInvalid(); lowestceilingpic.SetInvalid();
@ -788,6 +792,7 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li
lowestceiling = ff_bottom; lowestceiling = ff_bottom;
lowestceilingpic = *rover->bottom.texture; lowestceilingpic = *rover->bottom.texture;
lowestceilingsec = j == 0 ? linedef->frontsector : linedef->backsector; lowestceilingsec = j == 0 ? linedef->frontsector : linedef->backsector;
lowestceilingffloor = rover;
} }
if(delta1 <= delta2 && (!restrict || thing->Z() >= ff_top)) if(delta1 <= delta2 && (!restrict || thing->Z() >= ff_top))
@ -798,6 +803,7 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li
highestfloorpic = *rover->top.texture; highestfloorpic = *rover->top.texture;
highestfloorterrain = rover->model->GetTerrain(rover->top.isceiling); highestfloorterrain = rover->model->GetTerrain(rover->top.isceiling);
highestfloorsec = j == 0 ? linedef->frontsector : linedef->backsector; highestfloorsec = j == 0 ? linedef->frontsector : linedef->backsector;
highestfloorffloor = rover;
} }
if (ff_top > highestfloorplanes[j]->ZatPoint(x, y)) if (ff_top > highestfloorplanes[j]->ZatPoint(x, y))
{ {
@ -814,6 +820,7 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li
open.floorpic = highestfloorpic; open.floorpic = highestfloorpic;
open.floorterrain = highestfloorterrain; open.floorterrain = highestfloorterrain;
open.bottomsec = highestfloorsec; open.bottomsec = highestfloorsec;
open.bottomffloor = highestfloorffloor;
} }
if (highestfloorplanes[0] != &open.frontfloorplane) if (highestfloorplanes[0] != &open.frontfloorplane)
{ {
@ -831,6 +838,7 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li
open.top = lowestceiling; open.top = lowestceiling;
open.ceilingpic = lowestceilingpic; open.ceilingpic = lowestceilingpic;
open.topsec = lowestceilingsec; open.topsec = lowestceilingsec;
open.topffloor = lowestceilingffloor;
} }
open.lowfloor = MIN(lowestfloor[0], lowestfloor[1]); open.lowfloor = MIN(lowestfloor[0], lowestfloor[1]);

View file

@ -6862,6 +6862,11 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
return (ss->healthfloorgroup && (grp = P_GetHealthGroup(ss->healthfloorgroup))) return (ss->healthfloorgroup && (grp = P_GetHealthGroup(ss->healthfloorgroup)))
? grp->health : ss->healthfloor; ? grp->health : ss->healthfloor;
} }
else if (part == SECPART_3D)
{
return (ss->health3dgroup && (grp = P_GetHealthGroup(ss->health3dgroup)))
? grp->health : ss->health3d;
}
return 0; return 0;
} }

View file

@ -37,6 +37,8 @@ void P_SetHealthGroupHealth(int group, int health)
lsector->healthceiling = health; lsector->healthceiling = health;
if (lsector->healthfloorgroup == group) if (lsector->healthfloorgroup == group)
lsector->healthfloor = health; lsector->healthfloor = health;
if (lsector->health3dgroup == group)
lsector->health3d = health;
} }
} }
@ -70,11 +72,20 @@ void P_DamageHealthGroup(FHealthGroup* grp, void* object, AActor* source, int da
lsector->healthfloor = grp->health + damage; lsector->healthfloor = grp->health + damage;
P_DamageSector(lsector, source, damage, damagetype, SECPART_Floor, position, false); P_DamageSector(lsector, source, damage, damagetype, SECPART_Floor, position, false);
} }
if (lsector->health3dgroup == group && (lsector != object || part != SECPART_3D))
{
lsector->health3d = grp->health + damage;
P_DamageSector(lsector, source, damage, damagetype, SECPART_3D, position, false);
}
} }
} }
void P_DamageLinedef(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool dogroups) void P_DamageLinedef(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool dogroups)
{ {
if (damage < 0) damage = 0;
if (!damage) return;
line->health -= damage; line->health -= damage;
if (line->health < 0) line->health = 0; if (line->health < 0) line->health = 0;
@ -100,16 +111,43 @@ void P_DamageLinedef(line_t* line, AActor* source, int damage, FName damagetype,
void P_DamageSector(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool dogroups) void P_DamageSector(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool dogroups)
{ {
int sectorhealth = (part == SECPART_Ceiling) ? sector->healthceiling : sector->healthfloor; if (damage < 0) damage = 0;
int newhealth = sectorhealth - damage; if (!damage) return;
int* sectorhealth;
int group;
int dmg;
int dth;
switch (part)
{
case SECPART_Ceiling:
sectorhealth = &sector->healthceiling;
group = sector->healthceilinggroup;
dmg = SECSPAC_DamageCeiling;
dth = SECSPAC_DeathCeiling;
break;
case SECPART_Floor:
sectorhealth = &sector->healthfloor;
group = sector->healthfloorgroup;
dmg = SECSPAC_DamageFloor;
dth = SECSPAC_DeathFloor;
break;
case SECPART_3D:
sectorhealth = &sector->health3d;
group = sector->health3dgroup;
dmg = SECSPAC_Damage3D;
dth = SECSPAC_Death3D;
break;
default:
return;
}
int newhealth = *sectorhealth - damage;
if (newhealth < 0) newhealth = 0; if (newhealth < 0) newhealth = 0;
if (part == SECPART_Ceiling)
sector->healthceiling = newhealth; *sectorhealth = newhealth;
else sector->healthfloor = newhealth;
// callbacks here // callbacks here
int dmg = (part == SECPART_Ceiling) ? SECSPAC_DamageCeiling : SECSPAC_DamageFloor;
int dth = (part == SECPART_Ceiling) ? SECSPAC_DeathCeiling : SECSPAC_DeathFloor;
if (sector->SecActTarget) if (sector->SecActTarget)
{ {
sector->TriggerSectorActions(source, dmg); sector->TriggerSectorActions(source, dmg);
@ -117,7 +155,6 @@ void P_DamageSector(sector_t* sector, AActor* source, int damage, FName damagety
sector->TriggerSectorActions(source, dth); sector->TriggerSectorActions(source, dth);
} }
int group = (part == SECPART_Ceiling) ? sector->healthceilinggroup : sector->healthfloorgroup;
if (dogroups && group) if (dogroups && group)
{ {
FHealthGroup* grp = P_GetHealthGroup(group); FHealthGroup* grp = P_GetHealthGroup(group);
@ -222,6 +259,14 @@ void P_GeometryLineAttack(FTraceResults& trace, AActor* thing, int damage, FName
{ {
// [ZZ] hitscan geometry damage logic // [ZZ] hitscan geometry damage logic
// //
// check 3d floor, but still allow the wall to take generic damage
if (trace.HitType == TRACE_HitWall && trace.Tier == TIER_FFloor)
{
if (trace.ffloor && trace.ffloor->model && trace.ffloor->model->health3d)
P_DamageSector(trace.ffloor->model, thing, damage, damageType, SECPART_3D, trace.HitPos);
}
if (trace.HitType == TRACE_HitWall && P_CheckLinedefVulnerable(trace.Line, trace.Side)) if (trace.HitType == TRACE_HitWall && P_CheckLinedefVulnerable(trace.Line, trace.Side))
{ {
if (trace.Tier == TIER_Lower || trace.Tier == TIER_Upper) // process back sector health if any if (trace.Tier == TIER_Lower || trace.Tier == TIER_Upper) // process back sector health if any
@ -246,6 +291,33 @@ void P_GeometryLineAttack(FTraceResults& trace, AActor* thing, int damage, FName
} }
else if (trace.HitType == TRACE_HitFloor || trace.HitType == TRACE_HitCeiling) else if (trace.HitType == TRACE_HitFloor || trace.HitType == TRACE_HitCeiling)
{ {
// check for 3d floors. if a 3d floor was hit, it'll block any interaction with the sector planes at the same point, if present.
// i.e. if there are 3d floors at the same height as the sector's real floor/ceiling, and they blocked the shot, then it won't damage.
bool hit3dfloors = false;
sector_t* sector = trace.Sector;
for (auto f : sector->e->XFloor.ffloors)
{
if (!(f->flags & FF_EXISTS)) continue;
if (!(f->flags & FF_SOLID) || (f->flags & FF_SHOOTTHROUGH)) continue;
if (!f->model) continue;
if (trace.HitType == TRACE_HitFloor && fabs(f->top.plane->ZatPoint(trace.HitPos.XY())-trace.HitPos.Z) <= EQUAL_EPSILON)
{
if (f->model->health3d)
P_DamageSector(f->model, thing, damage, damageType, SECPART_3D, trace.HitPos);
hit3dfloors = true;
}
else if (trace.HitType == TRACE_HitCeiling && fabs(f->bottom.plane->ZatPoint(trace.HitPos.XY())-trace.HitPos.Z) <= EQUAL_EPSILON)
{
if (f->model->health3d)
P_DamageSector(f->model, thing, damage, damageType, SECPART_3D, trace.HitPos);
hit3dfloors = true;
}
}
if (hit3dfloors)
return;
int sectorhealth = 0; int sectorhealth = 0;
if (trace.HitType == TRACE_HitFloor && trace.Sector->healthfloor > 0 && P_CheckSectorVulnerable(trace.Sector, SECPART_Floor)) if (trace.HitType == TRACE_HitFloor && trace.Sector->healthfloor > 0 && P_CheckSectorVulnerable(trace.Sector, SECPART_Floor))
sectorhealth = trace.Sector->healthfloor; sectorhealth = trace.Sector->healthfloor;
@ -290,16 +362,23 @@ static DVector2 PGRA_ClosestPointOnLine2D(DVector2 x, DVector2 p1, DVector2 p2)
return p1 + p2p1.Unit() * r; return p1 + p2p1.Unit() * r;
} }
static void PGRA_InsertIfCloser(TMap<int, pgra_data_t>& damageGroupPos, int group, DVector3 pt, DVector3 check, sector_t* checksector, sector_t* sector, line_t* line, int secpart) static bool PGRA_CheckExplosionBlocked(DVector3 pt, DVector3 check, sector_t* checksector)
{ {
// simple solid geometry sight check between "check" and "pt" // simple solid geometry sight check between "check" and "pt"
// expected - Trace hits nothing // expected - Trace hits nothing
check.Z += EQUAL_EPSILON; // this is so that floor under the rocket doesn't block explosion
DVector3 ptVec = (pt - check); DVector3 ptVec = (pt - check);
double ptDst = ptVec.Length() - 0.5; double ptDst = ptVec.Length() - 0.5;
ptVec.MakeUnit(); ptVec.MakeUnit();
FTraceResults res; FTraceResults res;
bool isblocked = Trace(check, checksector, ptVec, ptDst, 0, 0xFFFFFFFF, nullptr, res); bool isblocked = Trace(check, checksector, ptVec, ptDst, 0, ML_BLOCKEVERYTHING, nullptr, res);
if (isblocked) return; return isblocked;
}
static void PGRA_InsertIfCloser(TMap<int, pgra_data_t>& damageGroupPos, int group, DVector3 pt, DVector3 check, sector_t* checksector, sector_t* sector, line_t* line, int secpart)
{
if (PGRA_CheckExplosionBlocked(pt, check, checksector))
return;
pgra_data_t* existing = damageGroupPos.CheckKey(group); pgra_data_t* existing = damageGroupPos.CheckKey(group);
// not present or distance is closer // not present or distance is closer
@ -337,7 +416,7 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage
DVector3 spotTo = bombspot->Pos() - srcsector->ceilingplane.Normal() * dstceiling; DVector3 spotTo = bombspot->Pos() - srcsector->ceilingplane.Normal() * dstceiling;
int grp = srcsector->healthceilinggroup; int grp = srcsector->healthceilinggroup;
if (grp <= 0) if (grp <= 0)
grp = 0x80000000 | (srcsector->sectornum & 0x7FFFFFFF); grp = 0x80000000 | (srcsector->sectornum & 0x0FFFFFFF);
PGRA_InsertIfCloser(damageGroupPos, grp, spotTo, bombspot->Pos(), srcsector, srcsector, nullptr, SECPART_Ceiling); PGRA_InsertIfCloser(damageGroupPos, grp, spotTo, bombspot->Pos(), srcsector, srcsector, nullptr, SECPART_Ceiling);
} }
@ -347,12 +426,48 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage
DVector3 spotTo = bombspot->Pos() - srcsector->floorplane.Normal() * dstfloor; DVector3 spotTo = bombspot->Pos() - srcsector->floorplane.Normal() * dstfloor;
int grp = srcsector->healthfloorgroup; int grp = srcsector->healthfloorgroup;
if (grp <= 0) if (grp <= 0)
grp = 0x40000000 | (srcsector->sectornum & 0x7FFFFFFF); grp = 0x40000000 | (srcsector->sectornum & 0x0FFFFFFF);
PGRA_InsertIfCloser(damageGroupPos, grp, spotTo, bombspot->Pos(), srcsector, srcsector, nullptr, SECPART_Floor); PGRA_InsertIfCloser(damageGroupPos, grp, spotTo, bombspot->Pos(), srcsector, srcsector, nullptr, SECPART_Floor);
} }
for (auto f : srcsector->e->XFloor.ffloors)
{
if (!(f->flags & FF_EXISTS)) continue;
if (!(f->flags & FF_SOLID)) continue;
if (!f->model || !f->model->health3d) continue;
double ff_top = f->top.plane->ZatPoint(bombspot->Pos());
double ff_bottom = f->bottom.plane->ZatPoint(bombspot->Pos());
if (ff_top < ff_bottom) // ignore eldritch geometry
continue;
int grp = f->model->health3dgroup;
if (grp <= 0)
grp = 0x20000000 | (f->model->sectornum & 0x0FFFFFFF);
DVector3 spotTo;
if (bombspot->Z() < ff_bottom) // use bottom plane
{
double dst = f->bottom.plane->Normal() | (bombspot->Pos() + f->bottom.plane->Normal()*f->bottom.plane->D);
spotTo = bombspot->Pos() - f->bottom.plane->Normal() * dst;
}
else if (bombspot->Z() > ff_top) // use top plane
{
double dst = f->top.plane->Normal() | (bombspot->Pos() + f->top.plane->Normal()*f->top.plane->D);
spotTo = bombspot->Pos() - f->top.plane->Normal() * dst;
}
else // explosion right inside the floor. do 100% damage
{
spotTo = bombspot->Pos();
}
PGRA_InsertIfCloser(damageGroupPos, grp, spotTo, bombspot->Pos(), srcsector, f->model, nullptr, SECPART_3D);
}
// enumerate all lines around // enumerate all lines around
FBoundingBox bombbox(bombspot->X(), bombspot->Y(), bombdistance * 16); FBoundingBox bombbox(bombspot->X(), bombspot->Y(), bombdistance);
FBlockLinesIterator it(bombbox); FBlockLinesIterator it(bombbox);
line_t* ln; line_t* ln;
int vc = validcount; int vc = validcount;
@ -370,7 +485,7 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage
sector_t* sector = side->sector; sector_t* sector = side->sector;
side_t* otherside = ln->sidedef[!sd]; side_t* otherside = ln->sidedef[!sd];
sector_t* othersector = otherside ? otherside->sector : nullptr; sector_t* othersector = otherside ? otherside->sector : nullptr;
if (!ln->health && (!othersector || (!othersector->healthfloor && !othersector->healthceiling))) if (!ln->health && (!othersector || (!othersector->healthfloor && !othersector->healthceiling && !othersector->e->XFloor.ffloors.Size())))
continue; // non-interactive geometry continue; // non-interactive geometry
DVector2 to2d = PGRA_ClosestPointOnLine2D(bombspot->Pos().XY(), side->V1()->p, side->V2()->p); DVector2 to2d = PGRA_ClosestPointOnLine2D(bombspot->Pos().XY(), side->V1()->p, side->V2()->p);
@ -378,13 +493,15 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage
double distto2d = (to2d - pos2d).Length(); double distto2d = (to2d - pos2d).Length();
double z_top1, z_top2, z_bottom1, z_bottom2; // here, z_top1 is closest to the ceiling, and z_bottom1 is closest to the floor. double z_top1, z_top2, z_bottom1, z_bottom2; // here, z_top1 is closest to the ceiling, and z_bottom1 is closest to the floor.
z_top1 = sector->ceilingplane.ZatPoint(to2d); z_top1 = sector->ceilingplane.ZatPoint(to2d);
z_top2 = othersector ? othersector->ceilingplane.ZatPoint(to2d) : z_top1;
z_bottom1 = sector->floorplane.ZatPoint(to2d); z_bottom1 = sector->floorplane.ZatPoint(to2d);
z_bottom2 = othersector ? othersector->floorplane.ZatPoint(to2d) : z_bottom1;
DVector3 to3d_fullheight(to2d.X, to2d.Y, clamp(bombspot->Z(), z_bottom1, z_top1)); DVector3 to3d_fullheight(to2d.X, to2d.Y, clamp(bombspot->Z(), z_bottom1, z_top1));
if (ln->health && P_CheckLinedefVulnerable(ln, sd)) if (ln->health && P_CheckLinedefVulnerable(ln, sd))
{ {
bool cantdamage = false; bool cantdamage = false;
bool linefullheight = othersector && !!(ln->flags & (ML_BLOCKEVERYTHING)); bool linefullheight = !othersector || !!(ln->flags & (ML_BLOCKEVERYTHING));
// decide specific position to affect on a line. // decide specific position to affect on a line.
if (!linefullheight) if (!linefullheight)
{ {
@ -410,7 +527,7 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage
{ {
PGRA_InsertIfCloser(damageGroupPos, ln->healthgroup, to3d_fullheight, bombspot->Pos(), srcsector, nullptr, ln, -1); PGRA_InsertIfCloser(damageGroupPos, ln->healthgroup, to3d_fullheight, bombspot->Pos(), srcsector, nullptr, ln, -1);
} }
else else if (!PGRA_CheckExplosionBlocked(to3d_fullheight, bombspot->Pos(), srcsector))
{ {
// otherwise just damage line // otherwise just damage line
double dst = (to3d_fullheight - bombspot->Pos()).Length(); double dst = (to3d_fullheight - bombspot->Pos()).Length();
@ -429,29 +546,49 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage
if (othersector && othersector->healthceiling && P_CheckLinedefVulnerable(ln, sd, SECPART_Ceiling)) if (othersector && othersector->healthceiling && P_CheckLinedefVulnerable(ln, sd, SECPART_Ceiling))
{ {
z_top2 = othersector->ceilingplane.ZatPoint(to2d);
if (z_top2 < z_top1) // we have front side to hit against if (z_top2 < z_top1) // we have front side to hit against
{ {
DVector3 to3d_upper(to2d.X, to2d.Y, clamp(bombspot->Z(), z_top2, z_top1)); DVector3 to3d_upper(to2d.X, to2d.Y, clamp(bombspot->Z(), z_top2, z_top1));
int grp = othersector->healthceilinggroup; int grp = othersector->healthceilinggroup;
if (grp <= 0) if (grp <= 0)
grp = 0x80000000 | (othersector->sectornum & 0x7FFFFFFF); grp = 0x80000000 | (othersector->sectornum & 0x0FFFFFFF);
PGRA_InsertIfCloser(damageGroupPos, grp, to3d_upper, bombspot->Pos(), srcsector, othersector, nullptr, SECPART_Ceiling); PGRA_InsertIfCloser(damageGroupPos, grp, to3d_upper, bombspot->Pos(), srcsector, othersector, nullptr, SECPART_Ceiling);
} }
} }
if (othersector && othersector->healthfloor && P_CheckLinedefVulnerable(ln, sd, SECPART_Floor)) if (othersector && othersector->healthfloor && P_CheckLinedefVulnerable(ln, sd, SECPART_Floor))
{ {
z_bottom2 = othersector->floorplane.ZatPoint(to2d);
if (z_bottom2 > z_bottom1) // we have front side to hit against if (z_bottom2 > z_bottom1) // we have front side to hit against
{ {
DVector3 to3d_lower(to2d.X, to2d.Y, clamp(bombspot->Z(), z_bottom1, z_bottom2)); DVector3 to3d_lower(to2d.X, to2d.Y, clamp(bombspot->Z(), z_bottom1, z_bottom2));
int grp = othersector->healthfloorgroup; int grp = othersector->healthfloorgroup;
if (grp <= 0) if (grp <= 0)
grp = 0x40000000 | (othersector->sectornum & 0x7FFFFFFF); grp = 0x40000000 | (othersector->sectornum & 0x0FFFFFFF);
PGRA_InsertIfCloser(damageGroupPos, grp, to3d_lower, bombspot->Pos(), srcsector, othersector, nullptr, SECPART_Floor); PGRA_InsertIfCloser(damageGroupPos, grp, to3d_lower, bombspot->Pos(), srcsector, othersector, nullptr, SECPART_Floor);
} }
} }
// check 3d floors
for (auto f : othersector->e->XFloor.ffloors)
{
if (!(f->flags & FF_EXISTS)) continue;
if (!(f->flags & FF_SOLID)) continue;
if (!f->model || !f->model->health3d) continue;
// 3d floors over real ceiling, or under real floor, are ignored
double z_ff_top = clamp(f->top.plane->ZatPoint(to2d), z_bottom2, z_top2);
double z_ff_bottom = clamp(f->bottom.plane->ZatPoint(to2d), z_bottom2, z_top2);
if (z_ff_top < z_ff_bottom)
continue; // also ignore eldritch geometry
DVector3 to3d_ffloor(to2d.X, to2d.Y, clamp(bombspot->Z(), z_ff_bottom, z_ff_top));
int grp = f->model->health3dgroup;
if (grp <= 0)
grp = 0x20000000 | (f->model->sectornum & 0x0FFFFFFF);
PGRA_InsertIfCloser(damageGroupPos, grp, to3d_ffloor, bombspot->Pos(), srcsector, f->model, nullptr, SECPART_3D);
}
} }
// damage health groups and sectors. // damage health groups and sectors.
@ -487,6 +624,11 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage
assert(damageGroupPair->Value.sector != nullptr); assert(damageGroupPair->Value.sector != nullptr);
P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, SECPART_Floor, pos); P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, SECPART_Floor, pos);
} }
else if (grp & 0x20000000) // sector 3d
{
assert(damageGroupPair->Value.sector != nullptr);
P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, SECPART_3D, pos);
}
else else
{ {
assert((damageGroupPair->Value.sector != nullptr) != (damageGroupPair->Value.line != nullptr)); assert((damageGroupPair->Value.sector != nullptr) != (damageGroupPair->Value.line != nullptr));
@ -497,6 +639,72 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage
} }
} }
//==========================================================================
//
// P_ProjectileHitLinedef
//
// Called if P_ExplodeMissile was called against a wall.
//==========================================================================
void P_ProjectileHitLinedef(AActor* mo, line_t* line)
{
// detect 3d floor hit
if (mo->Blocking3DFloor)
{
if (mo->Blocking3DFloor->health3d > 0)
P_DamageSector(mo->Blocking3DFloor, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_3D, mo->Pos());
}
int wside = P_PointOnLineSide(mo->Pos(), line);
int oside = !wside;
side_t* otherside = line->sidedef[oside];
// check if hit upper or lower part
if (otherside)
{
sector_t* othersector = otherside->sector;
// find closest pos from line to MO.
// this logic is so that steep slopes work correctly (value at the line is used, instead of value below the rocket actor)
DVector2 moRelPos = line->v1->p - mo->Pos().XY();
DVector2 lineNormal = line->delta.Rotated90CW().Unit();
double moRelDst = lineNormal | moRelPos;
DVector2 moPos = mo->Pos().XY() - lineNormal*fabs(moRelDst);
double otherfloorz = othersector->floorplane.ZatPoint(moPos);
double otherceilingz = othersector->ceilingplane.ZatPoint(moPos);
double zbottom = mo->Pos().Z;
double ztop = mo->Pos().Z + mo->Height;
if (zbottom < (otherfloorz + EQUAL_EPSILON) && othersector->healthfloor > 0 && P_CheckLinedefVulnerable(line, wside, SECPART_Floor))
{
P_DamageSector(othersector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos());
}
if (ztop > (otherceilingz - EQUAL_EPSILON) && othersector->healthceiling > 0 && P_CheckLinedefVulnerable(line, wside, SECPART_Ceiling))
{
P_DamageSector(othersector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos());
}
}
if (line->health > 0 && P_CheckLinedefVulnerable(line, wside))
{
P_DamageLinedef(line, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, wside, mo->Pos());
}
}
void P_ProjectileHitPlane(AActor* mo, int part)
{
// detect 3d floor hit
if (mo->Blocking3DFloor)
{
if (mo->Blocking3DFloor->health3d > 0)
P_DamageSector(mo->Blocking3DFloor, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_3D, mo->Pos());
return;
}
if (part == SECPART_Floor && mo->Sector->healthfloor > 0 && P_CheckSectorVulnerable(mo->Sector, SECPART_Floor))
P_DamageSector(mo->Sector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos());
else if (part == SECPART_Ceiling && mo->Sector->healthceiling > 0 && P_CheckSectorVulnerable(mo->Sector, SECPART_Ceiling))
P_DamageSector(mo->Sector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos());
}
//========================================================================== //==========================================================================
// //
// P_CheckLinedefVulnerable // P_CheckLinedefVulnerable
@ -525,6 +733,8 @@ bool P_CheckLinedefVulnerable(line_t* line, int side, int sectorpart)
bool P_CheckSectorVulnerable(sector_t* sector, int part) bool P_CheckSectorVulnerable(sector_t* sector, int part)
{ {
if (part == SECPART_3D)
return true;
FTextureID texture = sector->GetTexture((part == SECPART_Ceiling) ? sector_t::ceiling : sector_t::floor); FTextureID texture = sector->GetTexture((part == SECPART_Ceiling) ? sector_t::ceiling : sector_t::floor);
secplane_t* plane = (part == SECPART_Ceiling) ? &sector->ceilingplane : &sector->floorplane; secplane_t* plane = (part == SECPART_Ceiling) ? &sector->ceilingplane : &sector->floorplane;
if (texture == skyflatnum) if (texture == skyflatnum)
@ -577,4 +787,4 @@ void P_SerializeHealthGroups(FSerializer& arc)
arc.EndArray(); arc.EndArray();
} }
} }

View file

@ -17,7 +17,8 @@ struct FHealthGroup
enum enum
{ {
SECPART_Floor = 0, SECPART_Floor = 0,
SECPART_Ceiling = 1 SECPART_Ceiling = 1,
SECPART_3D = 2
}; };
void P_InitHealthGroups(); void P_InitHealthGroups();
@ -32,6 +33,8 @@ void P_DamageLinedef(line_t* line, AActor* source, int damage, FName damagetype,
void P_GeometryLineAttack(FTraceResults& trace, AActor* thing, int damage, FName damageType); void P_GeometryLineAttack(FTraceResults& trace, AActor* thing, int damage, FName damageType);
void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage, int bombdistance, FName damagetype, int fulldamagedistance); void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage, int bombdistance, FName damagetype, int fulldamagedistance);
void P_ProjectileHitLinedef(AActor* projectile, line_t* line);
void P_ProjectileHitPlane(AActor* projectile, int part);
bool P_CheckLinedefVulnerable(line_t* line, int side, int part = -1); bool P_CheckLinedefVulnerable(line_t* line, int side, int part = -1);
bool P_CheckSectorVulnerable(sector_t* sector, int part); bool P_CheckSectorVulnerable(sector_t* sector, int part);

View file

@ -3522,6 +3522,12 @@ FUNC(LS_Sector_SetHealth)
if (sector->healthfloorgroup) if (sector->healthfloorgroup)
P_SetHealthGroupHealth(sector->healthfloorgroup, arg2); P_SetHealthGroupHealth(sector->healthfloorgroup, arg2);
} }
else if (arg1 == SECPART_3D)
{
sector->health3d = arg2;
if (sector->health3dgroup)
P_SetHealthGroupHealth(sector->health3dgroup, arg2);
}
} }
return true; return true;
} }

View file

@ -1071,6 +1071,8 @@ bool PIT_CheckLine(FMultiBlockLinesIterator &mit, FMultiBlockLinesIterator::Chec
tm.ceilingpic = open.ceilingpic; tm.ceilingpic = open.ceilingpic;
tm.ceilingline = ld; tm.ceilingline = ld;
tm.thing->BlockingLine = ld; tm.thing->BlockingLine = ld;
if (open.topffloor)
tm.thing->Blocking3DFloor = open.topffloor->model;
} }
} }
@ -1086,6 +1088,8 @@ bool PIT_CheckLine(FMultiBlockLinesIterator &mit, FMultiBlockLinesIterator::Chec
tm.touchmidtex = open.touchmidtex; tm.touchmidtex = open.touchmidtex;
tm.abovemidtex = open.abovemidtex; tm.abovemidtex = open.abovemidtex;
tm.thing->BlockingLine = ld; tm.thing->BlockingLine = ld;
if (open.bottomffloor)
tm.thing->Blocking3DFloor = open.bottomffloor->model;
} }
else if (open.bottom == tm.floorz) else if (open.bottom == tm.floorz)
{ {

View file

@ -242,6 +242,7 @@ void P_LineOpening (FLineOpening &open, AActor *actor, const line_t *linedef, co
open.backfloorplane.SetAtHeight(LINEOPEN_MIN, sector_t::floor); open.backfloorplane.SetAtHeight(LINEOPEN_MIN, sector_t::floor);
} }
open.topffloor = open.bottomffloor = nullptr;
// Check 3D floors // Check 3D floors
if (actor != NULL) if (actor != NULL)
{ {

View file

@ -110,6 +110,8 @@ struct FLineOpening
int floorterrain; int floorterrain;
bool touchmidtex; bool touchmidtex;
bool abovemidtex; bool abovemidtex;
F3DFloor *topffloor;
F3DFloor *bottomffloor;
}; };
static const double LINEOPEN_MIN = -FLT_MAX; static const double LINEOPEN_MIN = -FLT_MAX;

View file

@ -296,6 +296,7 @@ DEFINE_FIELD(AActor, lastbump)
DEFINE_FIELD(AActor, DesignatedTeam) DEFINE_FIELD(AActor, DesignatedTeam)
DEFINE_FIELD(AActor, BlockingMobj) DEFINE_FIELD(AActor, BlockingMobj)
DEFINE_FIELD(AActor, BlockingLine) DEFINE_FIELD(AActor, BlockingLine)
DEFINE_FIELD(AActor, Blocking3DFloor)
DEFINE_FIELD(AActor, BlockingCeiling) DEFINE_FIELD(AActor, BlockingCeiling)
DEFINE_FIELD(AActor, BlockingFloor) DEFINE_FIELD(AActor, BlockingFloor)
DEFINE_FIELD(AActor, PoisonDamage) DEFINE_FIELD(AActor, PoisonDamage)
@ -481,6 +482,7 @@ void AActor::Serialize(FSerializer &arc)
A("smokecounter", smokecounter) A("smokecounter", smokecounter)
("blockingmobj", BlockingMobj) ("blockingmobj", BlockingMobj)
A("blockingline", BlockingLine) A("blockingline", BlockingLine)
A("blocking3dfloor", Blocking3DFloor)
A("blockingceiling", BlockingCeiling) A("blockingceiling", BlockingCeiling)
A("blockingfloor", BlockingFloor) A("blockingfloor", BlockingFloor)
A("visibletoteam", VisibleToTeam) A("visibletoteam", VisibleToTeam)
@ -1947,26 +1949,7 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target, bool onsky)
// [ZZ] line damage callback // [ZZ] line damage callback
if (line) if (line)
{ {
int wside = P_PointOnLineSide(mo->Pos(), line); P_ProjectileHitLinedef(mo, line);
int oside = !wside;
side_t* otherside = line->sidedef[oside];
// check if hit upper or lower part
if (otherside)
{
sector_t* othersector = otherside->sector;
double otherfloorz = othersector->floorplane.ZatPoint(mo->Pos());
double otherceilingz = othersector->ceilingplane.ZatPoint(mo->Pos());
double actualz = mo->Pos().Z;
if (actualz < otherfloorz && othersector->healthfloor > 0 && P_CheckLinedefVulnerable(line, wside, SECPART_Floor))
P_DamageSector(othersector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos());
if (actualz > otherceilingz && othersector->healthceiling > 0 && P_CheckLinedefVulnerable(line, wside, SECPART_Ceiling))
P_DamageSector(othersector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos());
}
if (line->health > 0 && P_CheckLinedefVulnerable(line, wside))
{
P_DamageLinedef(line, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, wside, mo->Pos());
}
} }
if (mo->flags3 & MF3_EXPLOCOUNT) if (mo->flags3 & MF3_EXPLOCOUNT)
@ -2761,13 +2744,11 @@ explode:
} }
if (mo->BlockingCeiling) // hit floor or ceiling while XY movement if (mo->BlockingCeiling) // hit floor or ceiling while XY movement
{ {
if (mo->BlockingCeiling->healthceiling > 0 && P_CheckSectorVulnerable(mo->BlockingCeiling, SECPART_Ceiling)) P_ProjectileHitPlane(mo, SECPART_Ceiling);
P_DamageSector(mo->BlockingCeiling, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos());
} }
if (mo->BlockingFloor) if (mo->BlockingFloor)
{ {
if (mo->BlockingFloor->healthfloor > 0 && P_CheckSectorVulnerable(mo->BlockingFloor, SECPART_Floor)) P_ProjectileHitPlane(mo, SECPART_Floor);
P_DamageSector(mo->BlockingFloor, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos());
} }
P_ExplodeMissile (mo, mo->BlockingLine, BlockingMobj, onsky); P_ExplodeMissile (mo, mo->BlockingLine, BlockingMobj, onsky);
return Oldfloorz; return Oldfloorz;
@ -3163,8 +3144,7 @@ void P_ZMovement (AActor *mo, double oldfloorz)
} }
P_HitFloor (mo); P_HitFloor (mo);
// hit floor: direct damage callback // hit floor: direct damage callback
if (mo->Sector->healthfloor > 0 && P_CheckSectorVulnerable(mo->Sector, SECPART_Floor)) P_ProjectileHitPlane(mo, SECPART_Floor);
P_DamageSector(mo->Sector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos());
P_ExplodeMissile (mo, NULL, NULL, onsky); P_ExplodeMissile (mo, NULL, NULL, onsky);
return; return;
} }
@ -3270,8 +3250,7 @@ void P_ZMovement (AActor *mo, double oldfloorz)
else onsky = true; else onsky = true;
} }
// hit ceiling: direct damage callback // hit ceiling: direct damage callback
if (mo->Sector->healthceiling > 0 && P_CheckSectorVulnerable(mo->Sector, SECPART_Ceiling)) P_ProjectileHitPlane(mo, SECPART_Ceiling);
P_DamageSector(mo->Sector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos());
P_ExplodeMissile (mo, NULL, NULL, onsky); P_ExplodeMissile (mo, NULL, NULL, onsky);
return; return;
} }
@ -4521,6 +4500,7 @@ void AActor::Tick ()
BlockingMobj = nullptr; BlockingMobj = nullptr;
sector_t* oldBlockingCeiling = BlockingCeiling; sector_t* oldBlockingCeiling = BlockingCeiling;
sector_t* oldBlockingFloor = BlockingFloor; sector_t* oldBlockingFloor = BlockingFloor;
Blocking3DFloor = nullptr;
BlockingFloor = nullptr; BlockingFloor = nullptr;
BlockingCeiling = nullptr; BlockingCeiling = nullptr;
double oldfloorz = P_XYMovement (this, cumm); double oldfloorz = P_XYMovement (this, cumm);

View file

@ -297,6 +297,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, sector_t &p, sector_t
("scrolls", scroll, nul) ("scrolls", scroll, nul)
("healthfloor", p.healthfloor, def->healthfloor) ("healthfloor", p.healthfloor, def->healthfloor)
("healthceiling", p.healthceiling, def->healthceiling) ("healthceiling", p.healthceiling, def->healthceiling)
("health3d", p.health3d, def->health3d)
// GZDoom exclusive: // GZDoom exclusive:
.Array("reflect", p.reflect, def->reflect, 2, true) .Array("reflect", p.reflect, def->reflect, 2, true)
.EndObject(); .EndObject();

View file

@ -1792,6 +1792,10 @@ public:
sec->healthceiling = CheckInt(key); sec->healthceiling = CheckInt(key);
break; break;
case NAME_Health3D:
sec->health3d = CheckInt(key);
break;
case NAME_HealthFloorGroup: case NAME_HealthFloorGroup:
sec->healthfloorgroup = CheckInt(key); sec->healthfloorgroup = CheckInt(key);
break; break;
@ -1799,6 +1803,10 @@ public:
case NAME_HealthCeilingGroup: case NAME_HealthCeilingGroup:
sec->healthceilinggroup = CheckInt(key); sec->healthceilinggroup = CheckInt(key);
break; break;
case NAME_Health3DGroup:
sec->health3dgroup = CheckInt(key);
break;
default: default:
break; break;

View file

@ -282,6 +282,8 @@ enum
SECSPAC_DamageCeiling=1<<12, // Trigger when ceiling is damaged SECSPAC_DamageCeiling=1<<12, // Trigger when ceiling is damaged
SECSPAC_DeathFloor = 1<<13, // Trigger when floor has 0 hp SECSPAC_DeathFloor = 1<<13, // Trigger when floor has 0 hp
SECSPAC_DeathCeiling= 1<<14, // Trigger when ceiling has 0 hp SECSPAC_DeathCeiling= 1<<14, // Trigger when ceiling has 0 hp
SECSPAC_Damage3D = 1<<15, // Trigger when controlled 3d floor is damaged
SECSPAC_Death3D = 1<<16 // Trigger when controlled 3d floor has 0 hp
}; };
struct secplane_t struct secplane_t
@ -1098,8 +1100,10 @@ public:
// default is 0, which means no special behavior // default is 0, which means no special behavior
int healthfloor; int healthfloor;
int healthceiling; int healthceiling;
int health3d;
int healthfloorgroup; int healthfloorgroup;
int healthceilinggroup; int healthceilinggroup;
int health3dgroup;
}; };

View file

@ -92,6 +92,8 @@ DoomEdNums
9601 = SecActDamageCeiling 9601 = SecActDamageCeiling
9602 = SecActDeathFloor 9602 = SecActDeathFloor
9603 = SecActDeathCeiling 9603 = SecActDeathCeiling
9604 = SecActDamage3D
9605 = SecActDeath3D
9800 = PointLight 9800 = PointLight
9801 = PointLightPulse 9801 = PointLightPulse
9802 = PointLightFlicker 9802 = PointLightFlicker

View file

@ -184,6 +184,7 @@ class Actor : Thinker native
native int DesignatedTeam; native int DesignatedTeam;
native Actor BlockingMobj; native Actor BlockingMobj;
native Line BlockingLine; native Line BlockingLine;
native Sector Blocking3DFloor;
native Sector BlockingCeiling; native Sector BlockingCeiling;
native Sector BlockingFloor; native Sector BlockingFloor;
native int PoisonDamage; native int PoisonDamage;

View file

@ -18,7 +18,9 @@ class SectorAction : Actor
SECSPAC_DamageFloor = 1<<11, SECSPAC_DamageFloor = 1<<11,
SECSPAC_DamageCeiling = 1<<12, SECSPAC_DamageCeiling = 1<<12,
SECSPAC_DeathFloor = 1<<13, SECSPAC_DeathFloor = 1<<13,
SECSPAC_DeathCeiling = 1<<14 SECSPAC_DeathCeiling = 1<<14,
SECSPAC_Damage3D = 1<<15,
SECSPAC_Death3D = 1<<16
}; };
default default
@ -252,6 +254,30 @@ class SecActDeathCeiling : SectorAction
override bool CanTrigger (Actor triggerer) { return !!special; } override bool CanTrigger (Actor triggerer) { return !!special; }
} }
// Triggered when controlled 3d floor is damaged ----------------------------------
class SecActDamage3D : SectorAction
{
Default
{
Health SECSPAC_Damage3D;
}
// [ZZ] damage is unconditional, so this as well
override bool CanTrigger (Actor triggerer) { return !!special; }
}
// Triggered when controlled 3d floor is reduced to 0 hp ----------------------------------
class SecActDeath3D : SectorAction
{
Default
{
Health SECSPAC_Death3D;
}
// [ZZ] damage is unconditional, so this as well
override bool CanTrigger (Actor triggerer) { return !!special; }
}
//========================================================================== //==========================================================================
// //
// Music changer. Uses the sector action class to do its job // Music changer. Uses the sector action class to do its job