diff --git a/src/actor.h b/src/actor.h index 0dd103582..248847e5b 100644 --- a/src/actor.h +++ b/src/actor.h @@ -1188,6 +1188,7 @@ public: AActor *BlockingMobj; // Actor 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 *BlockingFloor; // Sector that blocked the last move (floor plane slope) diff --git a/src/namedef.h b/src/namedef.h index c66890551..ecac82b56 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -670,10 +670,12 @@ xx(Owner) xx(HealthFloor) xx(HealthCeiling) +xx(Health3D) xx(DamageSpecial) xx(DeathSpecial) xx(HealthFloorGroup) xx(HealthCeilingGroup) +xx(Health3DGroup) xx(HealthGroup) // USDF keywords diff --git a/src/p_3dfloors.cpp b/src/p_3dfloors.cpp index 47506bfc0..4d9984eaa 100644 --- a/src/p_3dfloors.cpp +++ b/src/p_3dfloors.cpp @@ -358,6 +358,7 @@ bool P_CheckFor3DFloorHit(AActor * mo, double z, bool trigger) if (fabs(z - rover->top.plane->ZatPoint(mo)) < EQUAL_EPSILON) { mo->BlockingFloor = rover->model; + mo->Blocking3DFloor = rover->model; if (trigger) rover->model->TriggerSectorActions (mo, SECSPAC_HitFloor); 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) { mo->BlockingCeiling = rover->model; + mo->Blocking3DFloor = rover->model; if (trigger) rover->model->TriggerSectorActions (mo, SECSPAC_HitCeiling); return true; } @@ -762,8 +764,10 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li FTextureID highestfloorpic; int highestfloorterrain = -1; FTextureID lowestceilingpic; - sector_t *lowestceilingsec = NULL, *highestfloorsec = NULL; + sector_t *lowestceilingsec = nullptr, *highestfloorsec = nullptr; secplane_t *highestfloorplanes[2] = { &open.frontfloorplane, &open.backfloorplane }; + F3DFloor *lowestceilingffloor = nullptr; + F3DFloor *highestfloorffloor = nullptr; highestfloorpic.SetInvalid(); lowestceilingpic.SetInvalid(); @@ -788,6 +792,7 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li lowestceiling = ff_bottom; lowestceilingpic = *rover->bottom.texture; lowestceilingsec = j == 0 ? linedef->frontsector : linedef->backsector; + lowestceilingffloor = rover; } 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; highestfloorterrain = rover->model->GetTerrain(rover->top.isceiling); highestfloorsec = j == 0 ? linedef->frontsector : linedef->backsector; + highestfloorffloor = rover; } 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.floorterrain = highestfloorterrain; open.bottomsec = highestfloorsec; + open.bottomffloor = highestfloorffloor; } 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.ceilingpic = lowestceilingpic; open.topsec = lowestceilingsec; + open.topffloor = lowestceilingffloor; } open.lowfloor = MIN(lowestfloor[0], lowestfloor[1]); diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 712cddf1b..10038cb6d 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -6862,6 +6862,11 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) return (ss->healthfloorgroup && (grp = P_GetHealthGroup(ss->healthfloorgroup))) ? grp->health : ss->healthfloor; } + else if (part == SECPART_3D) + { + return (ss->health3dgroup && (grp = P_GetHealthGroup(ss->health3dgroup))) + ? grp->health : ss->health3d; + } return 0; } diff --git a/src/p_destructible.cpp b/src/p_destructible.cpp index 039ee302c..3d213b689 100755 --- a/src/p_destructible.cpp +++ b/src/p_destructible.cpp @@ -37,6 +37,8 @@ void P_SetHealthGroupHealth(int group, int health) lsector->healthceiling = health; if (lsector->healthfloorgroup == group) 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; 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) { + if (damage < 0) damage = 0; + if (!damage) return; + line->health -= damage; 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) { - int sectorhealth = (part == SECPART_Ceiling) ? sector->healthceiling : sector->healthfloor; - int newhealth = sectorhealth - damage; + if (damage < 0) damage = 0; + if (!damage) return; + + int* sectorhealth; + int group; + int dmg; + int dth; + switch (part) + { + case SECPART_Ceiling: + sectorhealth = §or->healthceiling; + group = sector->healthceilinggroup; + dmg = SECSPAC_DamageCeiling; + dth = SECSPAC_DeathCeiling; + break; + case SECPART_Floor: + sectorhealth = §or->healthfloor; + group = sector->healthfloorgroup; + dmg = SECSPAC_DamageFloor; + dth = SECSPAC_DeathFloor; + break; + case SECPART_3D: + sectorhealth = §or->health3d; + group = sector->health3dgroup; + dmg = SECSPAC_Damage3D; + dth = SECSPAC_Death3D; + break; + default: + return; + } + + int newhealth = *sectorhealth - damage; if (newhealth < 0) newhealth = 0; - if (part == SECPART_Ceiling) - sector->healthceiling = newhealth; - else sector->healthfloor = newhealth; + + *sectorhealth = newhealth; // callbacks here - int dmg = (part == SECPART_Ceiling) ? SECSPAC_DamageCeiling : SECSPAC_DamageFloor; - int dth = (part == SECPART_Ceiling) ? SECSPAC_DeathCeiling : SECSPAC_DeathFloor; if (sector->SecActTarget) { sector->TriggerSectorActions(source, dmg); @@ -117,7 +155,6 @@ void P_DamageSector(sector_t* sector, AActor* source, int damage, FName damagety sector->TriggerSectorActions(source, dth); } - int group = (part == SECPART_Ceiling) ? sector->healthceilinggroup : sector->healthfloorgroup; if (dogroups && 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 // + + // 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.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) { + // 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; if (trace.HitType == TRACE_HitFloor && trace.Sector->healthfloor > 0 && P_CheckSectorVulnerable(trace.Sector, SECPART_Floor)) sectorhealth = trace.Sector->healthfloor; @@ -290,16 +362,23 @@ static DVector2 PGRA_ClosestPointOnLine2D(DVector2 x, DVector2 p1, DVector2 p2) return p1 + p2p1.Unit() * r; } -static void PGRA_InsertIfCloser(TMap& 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" // expected - Trace hits nothing + check.Z += EQUAL_EPSILON; // this is so that floor under the rocket doesn't block explosion DVector3 ptVec = (pt - check); double ptDst = ptVec.Length() - 0.5; ptVec.MakeUnit(); FTraceResults res; - bool isblocked = Trace(check, checksector, ptVec, ptDst, 0, 0xFFFFFFFF, nullptr, res); - if (isblocked) return; + bool isblocked = Trace(check, checksector, ptVec, ptDst, 0, ML_BLOCKEVERYTHING, nullptr, res); + return isblocked; +} + +static void PGRA_InsertIfCloser(TMap& 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); // 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; int grp = srcsector->healthceilinggroup; 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); } @@ -347,12 +426,48 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage DVector3 spotTo = bombspot->Pos() - srcsector->floorplane.Normal() * dstfloor; int grp = srcsector->healthfloorgroup; 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); } + 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 - FBoundingBox bombbox(bombspot->X(), bombspot->Y(), bombdistance * 16); + FBoundingBox bombbox(bombspot->X(), bombspot->Y(), bombdistance); FBlockLinesIterator it(bombbox); line_t* ln; int vc = validcount; @@ -370,7 +485,7 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage sector_t* sector = side->sector; side_t* otherside = ln->sidedef[!sd]; 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 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 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_top2 = othersector ? othersector->ceilingplane.ZatPoint(to2d) : z_top1; 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)); if (ln->health && P_CheckLinedefVulnerable(ln, sd)) { 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. 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); } - else + else if (!PGRA_CheckExplosionBlocked(to3d_fullheight, bombspot->Pos(), srcsector)) { // otherwise just damage line 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)) { - z_top2 = othersector->ceilingplane.ZatPoint(to2d); 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)); int grp = othersector->healthceilinggroup; 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); } } 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 { DVector3 to3d_lower(to2d.X, to2d.Y, clamp(bombspot->Z(), z_bottom1, z_bottom2)); int grp = othersector->healthfloorgroup; 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); } } + + // 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. @@ -487,6 +624,11 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage assert(damageGroupPair->Value.sector != nullptr); 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 { 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 @@ -525,6 +733,8 @@ bool P_CheckLinedefVulnerable(line_t* line, int side, int sectorpart) 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); secplane_t* plane = (part == SECPART_Ceiling) ? §or->ceilingplane : §or->floorplane; if (texture == skyflatnum) @@ -577,4 +787,4 @@ void P_SerializeHealthGroups(FSerializer& arc) arc.EndArray(); } -} \ No newline at end of file +} diff --git a/src/p_destructible.h b/src/p_destructible.h index c9c5e81ab..f1ac1a0de 100755 --- a/src/p_destructible.h +++ b/src/p_destructible.h @@ -17,7 +17,8 @@ struct FHealthGroup enum { SECPART_Floor = 0, - SECPART_Ceiling = 1 + SECPART_Ceiling = 1, + SECPART_3D = 2 }; 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_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_CheckSectorVulnerable(sector_t* sector, int part); diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 081c7f400..2422dfe19 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -3522,6 +3522,12 @@ FUNC(LS_Sector_SetHealth) if (sector->healthfloorgroup) P_SetHealthGroupHealth(sector->healthfloorgroup, arg2); } + else if (arg1 == SECPART_3D) + { + sector->health3d = arg2; + if (sector->health3dgroup) + P_SetHealthGroupHealth(sector->health3dgroup, arg2); + } } return true; } diff --git a/src/p_map.cpp b/src/p_map.cpp index ed4f0d089..838612a37 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -1071,6 +1071,8 @@ bool PIT_CheckLine(FMultiBlockLinesIterator &mit, FMultiBlockLinesIterator::Chec tm.ceilingpic = open.ceilingpic; tm.ceilingline = 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.abovemidtex = open.abovemidtex; tm.thing->BlockingLine = ld; + if (open.bottomffloor) + tm.thing->Blocking3DFloor = open.bottomffloor->model; } else if (open.bottom == tm.floorz) { diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index 008edb210..48396b7cf 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -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.topffloor = open.bottomffloor = nullptr; // Check 3D floors if (actor != NULL) { diff --git a/src/p_maputl.h b/src/p_maputl.h index 4dfa99e0f..23e3f3ed6 100644 --- a/src/p_maputl.h +++ b/src/p_maputl.h @@ -110,6 +110,8 @@ struct FLineOpening int floorterrain; bool touchmidtex; bool abovemidtex; + F3DFloor *topffloor; + F3DFloor *bottomffloor; }; static const double LINEOPEN_MIN = -FLT_MAX; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 67d934060..1788161ba 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -296,6 +296,7 @@ DEFINE_FIELD(AActor, lastbump) DEFINE_FIELD(AActor, DesignatedTeam) DEFINE_FIELD(AActor, BlockingMobj) DEFINE_FIELD(AActor, BlockingLine) +DEFINE_FIELD(AActor, Blocking3DFloor) DEFINE_FIELD(AActor, BlockingCeiling) DEFINE_FIELD(AActor, BlockingFloor) DEFINE_FIELD(AActor, PoisonDamage) @@ -481,6 +482,7 @@ void AActor::Serialize(FSerializer &arc) A("smokecounter", smokecounter) ("blockingmobj", BlockingMobj) A("blockingline", BlockingLine) + A("blocking3dfloor", Blocking3DFloor) A("blockingceiling", BlockingCeiling) A("blockingfloor", BlockingFloor) A("visibletoteam", VisibleToTeam) @@ -1947,26 +1949,7 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target, bool onsky) // [ZZ] line damage callback if (line) { - 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; - 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()); - } + P_ProjectileHitLinedef(mo, line); } if (mo->flags3 & MF3_EXPLOCOUNT) @@ -2761,13 +2744,11 @@ explode: } if (mo->BlockingCeiling) // hit floor or ceiling while XY movement { - if (mo->BlockingCeiling->healthceiling > 0 && P_CheckSectorVulnerable(mo->BlockingCeiling, SECPART_Ceiling)) - P_DamageSector(mo->BlockingCeiling, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos()); + P_ProjectileHitPlane(mo, SECPART_Ceiling); } if (mo->BlockingFloor) { - if (mo->BlockingFloor->healthfloor > 0 && P_CheckSectorVulnerable(mo->BlockingFloor, SECPART_Floor)) - P_DamageSector(mo->BlockingFloor, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos()); + P_ProjectileHitPlane(mo, SECPART_Floor); } P_ExplodeMissile (mo, mo->BlockingLine, BlockingMobj, onsky); return Oldfloorz; @@ -3163,8 +3144,7 @@ void P_ZMovement (AActor *mo, double oldfloorz) } P_HitFloor (mo); // hit floor: direct damage callback - if (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()); + P_ProjectileHitPlane(mo, SECPART_Floor); P_ExplodeMissile (mo, NULL, NULL, onsky); return; } @@ -3270,8 +3250,7 @@ void P_ZMovement (AActor *mo, double oldfloorz) else onsky = true; } // hit ceiling: direct damage callback - if (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_ProjectileHitPlane(mo, SECPART_Ceiling); P_ExplodeMissile (mo, NULL, NULL, onsky); return; } @@ -4521,6 +4500,7 @@ void AActor::Tick () BlockingMobj = nullptr; sector_t* oldBlockingCeiling = BlockingCeiling; sector_t* oldBlockingFloor = BlockingFloor; + Blocking3DFloor = nullptr; BlockingFloor = nullptr; BlockingCeiling = nullptr; double oldfloorz = P_XYMovement (this, cumm); diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 93b583c58..53f4959a2 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -297,6 +297,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, sector_t &p, sector_t ("scrolls", scroll, nul) ("healthfloor", p.healthfloor, def->healthfloor) ("healthceiling", p.healthceiling, def->healthceiling) + ("health3d", p.health3d, def->health3d) // GZDoom exclusive: .Array("reflect", p.reflect, def->reflect, 2, true) .EndObject(); diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index f8b6d2c4d..11652abfc 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -1792,6 +1792,10 @@ public: sec->healthceiling = CheckInt(key); break; + case NAME_Health3D: + sec->health3d = CheckInt(key); + break; + case NAME_HealthFloorGroup: sec->healthfloorgroup = CheckInt(key); break; @@ -1799,6 +1803,10 @@ public: case NAME_HealthCeilingGroup: sec->healthceilinggroup = CheckInt(key); break; + + case NAME_Health3DGroup: + sec->health3dgroup = CheckInt(key); + break; default: break; diff --git a/src/r_defs.h b/src/r_defs.h index 5163e8f83..cb394cd41 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -282,6 +282,8 @@ enum SECSPAC_DamageCeiling=1<<12, // Trigger when ceiling is damaged SECSPAC_DeathFloor = 1<<13, // Trigger when floor 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 @@ -1098,8 +1100,10 @@ public: // default is 0, which means no special behavior int healthfloor; int healthceiling; + int health3d; int healthfloorgroup; int healthceilinggroup; + int health3dgroup; }; diff --git a/wadsrc/static/mapinfo/common.txt b/wadsrc/static/mapinfo/common.txt index e83fc78f4..4f24b51e8 100644 --- a/wadsrc/static/mapinfo/common.txt +++ b/wadsrc/static/mapinfo/common.txt @@ -92,6 +92,8 @@ DoomEdNums 9601 = SecActDamageCeiling 9602 = SecActDeathFloor 9603 = SecActDeathCeiling + 9604 = SecActDamage3D + 9605 = SecActDeath3D 9800 = PointLight 9801 = PointLightPulse 9802 = PointLightFlicker diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 48172b821..3d4e74fc8 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -184,6 +184,7 @@ class Actor : Thinker native native int DesignatedTeam; native Actor BlockingMobj; native Line BlockingLine; + native Sector Blocking3DFloor; native Sector BlockingCeiling; native Sector BlockingFloor; native int PoisonDamage; diff --git a/wadsrc/static/zscript/shared/sectoraction.txt b/wadsrc/static/zscript/shared/sectoraction.txt index 6b93d9805..8daada166 100644 --- a/wadsrc/static/zscript/shared/sectoraction.txt +++ b/wadsrc/static/zscript/shared/sectoraction.txt @@ -18,7 +18,9 @@ class SectorAction : Actor SECSPAC_DamageFloor = 1<<11, SECSPAC_DamageCeiling = 1<<12, SECSPAC_DeathFloor = 1<<13, - SECSPAC_DeathCeiling = 1<<14 + SECSPAC_DeathCeiling = 1<<14, + SECSPAC_Damage3D = 1<<15, + SECSPAC_Death3D = 1<<16 }; default @@ -252,6 +254,30 @@ class SecActDeathCeiling : SectorAction 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