From 4822a3d3198091e82c1df7aa4a8130e1f83e9694 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 5 Dec 2024 18:51:30 +0300 Subject: [PATCH 1/8] Explosion_Damage WIP --- src/extensions/combat/combatext_hooks.cpp | 392 ++++++++++++++++++++++ 1 file changed, 392 insertions(+) diff --git a/src/extensions/combat/combatext_hooks.cpp b/src/extensions/combat/combatext_hooks.cpp index b798053ba..3a88d5ed6 100644 --- a/src/extensions/combat/combatext_hooks.cpp +++ b/src/extensions/combat/combatext_hooks.cpp @@ -28,6 +28,10 @@ #include "tibsun_inline.h" #include "buildingext_hooks.h" #include "combatext_hooks.h" + +#include "aircraft.h" +#include "anim.h" +#include "animtype.h" #include "vinifera_globals.h" #include "combat.h" #include "cell.h" @@ -40,11 +44,25 @@ #include "extension.h" #include "fatal.h" #include "asserthandler.h" +#include "building.h" +#include "coord.h" #include "debughandler.h" #include "hooker.h" #include "hooker_macros.h" +#include "infantry.h" +#include "infantrytype.h" +#include "mouse.h" +#include "particlesys.h" +#include "particlesystype.h" +#include "tactical.h" +#include "team.h" +#include "teamtype.h" +#include "unit.h" +#include "unittype.h" +#include "veinholemonster.h" #include "verses.h" +#include "voxelanim.h" /** @@ -116,6 +134,380 @@ int Vinifera_Modify_Damage(int damage, WarheadTypeClass* warhead, ArmorType armo } +static bool Bridge_Helper1(CellClass& cellptr) +{ + if (cellptr.IsBridge) { + CellClass& bridge_owner = Map[cellptr.IsBridgeOwner ? cellptr.Pos : cellptr.BridgeOwner->Pos]; + if (bridge_owner.Overlay == OVERLAY_BRIDGE1 || bridge_owner.Overlay == OVERLAY_BRIDGE2) { + return true; + } + } + + return false; +} + + +/*********************************************************************************************** + * Explosion_Damage -- Inflict an explosion damage affect. * + * * + * Processes the collateral damage affects typically caused by an * + * explosion. * + * * + * INPUT: coord -- The coordinate of ground zero. * + * * + * strength -- Raw damage points at ground zero. * + * * + * source -- Source of the explosion (who is responsible). * + * * + * warhead -- The kind of explosion to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine can consume some time and will affect the AI * + * of nearby enemy units (possibly). * + * * + * HISTORY: * + * 08/16/1991 JLB : Created. * + * 11/30/1991 JLB : Uses coordinate system. * + * 12/27/1991 JLB : Radius of explosion damage effect. * + * 04/13/1994 JLB : Streamlined. * + * 04/16/1994 JLB : Warhead damage type modifier. * + * 04/17/1994 JLB : Cleaned up. * + * 06/20/1994 JLB : Uses object pointers to distribute damage. * + * 06/20/1994 JLB : Source is a pointer. * + * 06/18/1996 JLB : Strength could be negative for healing effects. * + *============================================================================================= */ +void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClass* source, const WarheadTypeClass* warhead, bool explode_tib) +{ + Cell cell; // Cell number under explosion. + ObjectClass* object; // Working object pointer. + DynamicVectorClass objects; // Maximum number of objects that can be damaged. + int distance; // Distance to unit. + int range; // Damage effect radius. + int range2; // Damage effect radius. + + if (Special.IsInert || !warhead) return; + + if (!strength && !warhead->IsWebby) return; + + range = CELL_LEPTON_W; + range2 = CELL_LEPTON_W + (CELL_LEPTON_W >> 1); + cell = Coord_Cell(coord); + + CellClass* cellptr = &Map[cell]; + + /** + * Fill the list of unit IDs that will have damage + * assessed upon them. First pass over all objects that + * could be in flight, because they are not present in cell data. + */ + if (Map.Get_Cell_Height(Cell_Coord(cell)) < coord.Z) { + + for (int index = 0; index < Aircrafts.Count(); index++) { + AircraftClass* aircraft = Aircrafts[index]; + + if (aircraft->IsActive) { + if (aircraft->IsDown && aircraft->Strength > 0) { + distance = Distance(coord, aircraft->Center_Coord()); + if (distance < range) { + objects.Add(aircraft); + } + } + } + } + + for (int index = 0; index < Infantry.Count(); index++) { + InfantryClass* infantry = Infantry[index]; + + if (infantry->IsActive && infantry->Class->IsJumpJet) { + if (infantry->IsDown && infantry->Strength > 0) { + distance = Distance(coord, infantry->Center_Coord()); + if (distance < range) { + objects.Add(infantry); + } + } + } + } + + for (int index = 0; index < Units.Count(); index++) { + UnitClass* unit = Units[index]; + + if (unit->IsActive && unit->Class->IsJellyfish) { + if (unit->IsDown && unit->Strength > 0) { + distance = Distance(coord, unit->Center_Coord()); + if (distance < range) { + objects.Add(unit); + } + } + } + } + } + + bool isbridge = cellptr->IsBridge && coord.Z > BRIDGE_HEIGHT / 2 + Map.Get_Cell_Height(coord); + ObjectClass* impacto = cellptr->Cell_Occupier(isbridge); + + /** + * Fill the list of unit IDs that will have damage + * assessed upon them. The units can be lifted from + * the cell data directly. + */ + for (FacingType i = FACING_NONE; i < FACING_COUNT; i++) { + + /** + * Fetch a pointer to the cell to examine. This is either + * an adjacent cell or the center cell. Damage never spills + * further than one cell away. + */ + if (i != FACING_NONE) { + cellptr = &Map[cell].Adjacent_Cell(i); + } + + /** + * Add all objects in this cell to the list of objects to possibly apply + * damage to. Do not include overlapping objects; selection state can affect + * the overlappers, and this causes multiplayer games to go out of sync. + */ + object = cellptr->Cell_Occupier(isbridge); + while (object) { + if (object->Kind_Of() == RTTI_UNIT && + Scen->SpecialFlags.IsHarvesterImmune && + Rule->HarvesterUnit.Is_Present(static_cast(object->Class_Of()))) { + continue; + } + objects.Add(object); + object = object->Next; + } + + /** + * If there is a veinhole monster, it may be destroyed. + */ + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const* optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (optr->IsVeinholeMonster) { + VeinholeMonsterClass* veinhole = VeinholeMonsterClass::Fetch_At(cell); + if (veinhole) { + objects.Add(veinhole); + } + } + } + } + + /** + * Sweep through the units to be damaged and damage them. When damaging + * buildings, consider a hit on any cell the building occupies as if it + * were a direct hit on the building's center. + */ + for (int index = 0; index < objects.Count(); index++) { + object = objects[index]; + + object->IsToDamage = false; + if (object->IsActive && !(object->Kind_Of() == RTTI_BUILDING && reinterpret_cast(object)->Class->IsInvisibleInGame)) { + if (object->Kind_Of() == RTTI_BUILDING && impacto == object) { + distance = 0; + } + else { + distance = Distance_Level_Snap(coord, object->Target_Coord()); + if (object->Kind_Of() == RTTI_AIRCRAFT) { + distance /= 2; + } + } + if (object->Strength > 0 && object->IsDown && !object->IsInLimbo && distance < range2) { + int damage = strength; + if (warhead == Rule->IonStormWarhead && + object->Is_Foot() && + static_cast(object)->Team && + !static_cast(object)->Team->Class->IsIonImmune) { + continue; + } + object->Take_Damage(damage, distance, warhead, source); + } + } + } + + double rockerval = std::min(strength * 0.01, 4.0); + if (warhead->IsRocker) { + if (rockerval > 0.3) { + const int cell_radius = 3; + for (int x = -cell_radius; x <= cell_radius; x++) { + for (int y = -cell_radius; y <= cell_radius; y++) { + int xpos = cell.X + x; + int ypos = cell.Y + y; + + object = Map[Cell(xpos, ypos)].Cell_Occupier(isbridge); + while (object) { + TechnoClass* techno = object->As_Techno(); + if (techno) { + if (xpos == cell.X && ypos == cell.Y && source) { + Coordinate rockercoord = (source->Get_Coord() - techno->Get_Coord()); + TPoint3D rockervec = TPoint3D(rockercoord.X, rockercoord.Y, rockercoord.Z).Normalized() * 10.0f; + techno->Rock(Coordinate(rockervec.X, rockervec.Y, rockervec.Z), rockerval); + } + else { + techno->Rock(coord, rockerval); + } + } + object = object->Next; + } + } + } + + } + } + + /** + * If there is a wall present at this location, it may be destroyed. Check to + * make sure that the warhead is of the kind that can destroy walls. + */ + cellptr = &Map[cell]; + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const* optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (optr->IsChainReaction) { + if ((!optr->IsTiberium || warhead->IsTiberiumDestroyer) && explode_tib) { + Chain_Reaction_Damage(cell); + cellptr->Reduce_Tiberium(strength / 10); + } + } + if (optr->IsWall) { + if (warhead->IsWallDestroyer || (warhead->IsWoodDestroyer && optr->Armor == ARMOR_WOOD)) { + Map[cell].Reduce_Wall(strength); + } + } + if (cellptr->Overlay == OVERLAY_NONE) { + TechnoClass::Update_Mission_Targets(cellptr); + } + } + + bool remove_overlay = true; + if (Scen->SpecialFlags.IsDestroyableBridges && warhead->IsWallDestroyer) { + + if (Bridge_Helper1(*cellptr)) + { + + } + else + { + IsometricTileType ttype = cellptr->Tile - BridgeSet + 1; + if (ttype == BridgeMiddle1 + 0 || ttype == BridgeMiddle1 + 1 || + ttype == BridgeMiddle1 + 2 || ttype == BridgeMiddle1 + 3 || + ttype == BridgeMiddle2 + 0 || ttype == BridgeMiddle2 + 1 || + ttype == BridgeMiddle2 + 2 || ttype == BridgeMiddle2 + 3) { + + if (!cellptr->IsBridge || (coord.Z <= BRIDGE_HEIGHT + CELL_HEIGHT(cellptr->Level + 1)) && coord.Z > BRIDGE_HEIGHT + CELL_HEIGHT(cellptr->Level - 2)) + { + if ((warhead->IsWallDestroyer && (warhead == Rule->IonCannonWarhead || Random_Pick(1, Rule->BridgeStrength) < strength))) { + for (int i = 0; i < (warhead == Rule->IonCannonWarhead ? 4 : 1); i++) { + if (Map.Destroy_Bridge_At(cell)) { + TechnoClass::Update_Mission_Targets(cellptr); + break; + } + } + Point2D point; + TacticalMap->Coord_To_Pixel(coord, point); + TacticalMap->Register_Dirty_Area(Rect(point.X - 128, point.Y - 128, 256, 256), false); + } + } + + + } + } + + //if (cellptr->IsBridge) + //{ + // CellClass& bridge_owner = Map[cellptr->IsBridgeOwner ? cellptr->Pos : cellptr->BridgeOwner->Pos]; + // if (bridge_owner.Overlay == OVERLAY_BRIDGE1 || bridge_owner.Overlay == OVERLAY_BRIDGE2) + + // if (!cellptr->IsBridgeOwner) + //} + + ///** + // * If there is a bridge at this location, then it may be destroyed by the + // * combat damage. + // */ + //if (cellptr->TType == TEMPLATE_BRIDGE1 || cellptr->TType == TEMPLATE_BRIDGE2 || + // cellptr->TType == TEMPLATE_BRIDGE1H || cellptr->TType == TEMPLATE_BRIDGE2H || + // cellptr->TType == TEMPLATE_BRIDGE_1A || cellptr->TType == TEMPLATE_BRIDGE_1B || + // cellptr->TType == TEMPLATE_BRIDGE_2A || cellptr->TType == TEMPLATE_BRIDGE_2B || + // cellptr->TType == TEMPLATE_BRIDGE_3A || cellptr->TType == TEMPLATE_BRIDGE_3B) { + + // if ((warhead->IsWallDestroyer && (warhead == Rule->IonCannonWarhead || Random_Pick(1, Rule->BridgeStrength) < strength))) { + // for (int i = 0; i < (warhead == Rule->IonCannonWarhead ? 4 : 1); i++) { + // if (Map.Destroy_Bridge_At(cell)) { + // TechnoClass::Update_Mission_Targets(cellptr); + // break; + // } + // } + // Point2D point; + // TacticalMap->Coord_To_Pixel(coord, point); + // TacticalMap->Register_Dirty_Area(Rect(point.X - 128, point.Y - 128, 256, 256), false); + // } + //} + + } + + if (remove_overlay) { + if (cellptr->Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(cellptr->Overlay).IsExplodes) { + cellptr->Redraw_Overlay(); + cellptr->Overlay = OVERLAY_NONE; + cellptr->Recalc_Attributes(); + Map.Update_Cell_Zone(cellptr->Pos); + Map.Update_Cell_Subzones(cellptr->Pos); + TechnoClass::Update_Mission_Targets(cellptr); + + new AnimClass(Rule->BarrelExplode, coord); + Explosion_Damage(coord, Rule->AmmoCrateDamage, nullptr, Rule->C4Warhead, true); + for (int i = 0; i < Rule->BarrelDebris.Count(); i++) { + if (Percent_Chance(15)) { + new VoxelAnimClass(Rule->BarrelDebris[i], coord); + } + } + if (Percent_Chance(25)) { + ParticleSystemClass* barrelps = new ParticleSystemClass(Rule->BarrelParticle, coord); + barrelps->Spawn_Held_Particle(coord, coord); + } + + for (FacingType i = FACING_N; i < FACING_COUNT; i += 2) { + Coordinate adjacent = Adjacent_Coord_With_Height(coord, i); + if (Map[adjacent].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[adjacent].Overlay).IsExplodes) { + new AnimClass(AnimTypeClass::As_Pointer(AnimTypeClass::From_Name("FIRE3")), coord, Random_Pick(1, 3) + 3); + } + } + } + + if (strength > warhead->DeformThreshhold) { + const int chance = static_cast(strength) * 0.01 * warhead->Deform * 100.0; + if (Percent_Chance(chance) && (!Map[coord].IsBridge) || coord.Z < BRIDGE_HEIGHT + Map.Get_Cell_Height(coord)) { + Map.Deform(Coord_Cell(coord), false); + } + } + + if (cellptr->Is_Tile_Destroyable_Cliff()) { + if (Percent_Chance(Rule->CollapseChance)) { + Map.Collapse_Cliff(*cellptr); + } + } + + if (warhead->Particle) { + if (warhead->Particle->BehavesLike == BEHAVIOUR_SMOKE) { + MasterParticle->Spawn_Held_Particle(coord, coord); + } + else { + ParticleSystemClass* ps = new ParticleSystemClass(warhead->Particle, coord); + ps->Spawn_Held_Particle(coord, coord); + } + } + + if ((warhead->IsWallDestroyer || warhead->IsFire) && (!Map[coord].IsBridge || coord.Z < BRIDGE_HEIGHT + Map.Get_Cell_Height(coord))) { + Map.field_DC.Clear(); + if (Map.Crack_Ice(*cellptr, nullptr)) { + Map.Recalc_Ice(); + } + } + } +} + + /** * convert a float to integer with the desired scale. * From 55ff96086d27081f9809b7986de6c8c2d4e290b3 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 5 Dec 2024 22:10:21 +0300 Subject: [PATCH 2/8] Finish Explosion_Damage --- src/extensions/combat/combatext_hooks.cpp | 230 ++++++++++++---------- 1 file changed, 122 insertions(+), 108 deletions(-) diff --git a/src/extensions/combat/combatext_hooks.cpp b/src/extensions/combat/combatext_hooks.cpp index 3a88d5ed6..6f455f8e3 100644 --- a/src/extensions/combat/combatext_hooks.cpp +++ b/src/extensions/combat/combatext_hooks.cpp @@ -134,16 +134,45 @@ int Vinifera_Modify_Damage(int damage, WarheadTypeClass* warhead, ArmorType armo } -static bool Bridge_Helper1(CellClass& cellptr) +static CellClass* Get_Bridge_Owner(CellClass& cellptr) { if (cellptr.IsBridge) { - CellClass& bridge_owner = Map[cellptr.IsBridgeOwner ? cellptr.Pos : cellptr.BridgeOwner->Pos]; - if (bridge_owner.Overlay == OVERLAY_BRIDGE1 || bridge_owner.Overlay == OVERLAY_BRIDGE2) { - return true; - } + return &Map[cellptr.IsBridgeOwner ? cellptr.Pos : cellptr.BridgeOwner->Pos]; } - return false; + return nullptr; +} + + +static bool Is_Bridge_Height(CellClass& cellptr, const Coordinate& coord) +{ + return cellptr.IsBridge && (coord.Z > BRIDGE_HEIGHT + CELL_HEIGHT(cellptr.Level + 1)) && (coord.Z <= BRIDGE_HEIGHT + CELL_HEIGHT(cellptr.Level - 2)); +} + + +static bool Is_Tile_Bridge_Middle(IsometricTileType type) +{ + const IsometricTileType ttype = type - BridgeSet + 1; + return (ttype == BridgeMiddle1 + 0 || ttype == BridgeMiddle1 + 1 || + ttype == BridgeMiddle1 + 2 || ttype == BridgeMiddle1 + 3 || + ttype == BridgeMiddle2 + 0 || ttype == BridgeMiddle2 + 1 || + ttype == BridgeMiddle2 + 2 || ttype == BridgeMiddle2 + 3); +} + + +static bool Is_Tile_Train_Bridge_Middle(IsometricTileType type) +{ + const IsometricTileType ttype = type - TrainBridgeSet + 1; + return (ttype == BridgeMiddle1 + 0 || ttype == BridgeMiddle1 + 1 || + ttype == BridgeMiddle1 + 2 || ttype == BridgeMiddle1 + 3 || + ttype == BridgeMiddle2 + 0 || ttype == BridgeMiddle2 + 1 || + ttype == BridgeMiddle2 + 2 || ttype == BridgeMiddle2 + 3); +} + + +static bool Is_Not_On_Bridge(const Coordinate& coord) +{ + return !Map[coord].IsBridge || coord.Z < BRIDGE_HEIGHT + Map.Get_Cell_Height(coord); } @@ -379,130 +408,114 @@ void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClas } } - bool remove_overlay = true; + /** + * If there is a bridge at this location, then it may be destroyed by the + * combat damage. + */ if (Scen->SpecialFlags.IsDestroyableBridges && warhead->IsWallDestroyer) { - - if (Bridge_Helper1(*cellptr)) - { - - } - else - { - IsometricTileType ttype = cellptr->Tile - BridgeSet + 1; - if (ttype == BridgeMiddle1 + 0 || ttype == BridgeMiddle1 + 1 || - ttype == BridgeMiddle1 + 2 || ttype == BridgeMiddle1 + 3 || - ttype == BridgeMiddle2 + 0 || ttype == BridgeMiddle2 + 1 || - ttype == BridgeMiddle2 + 2 || ttype == BridgeMiddle2 + 3) { - - if (!cellptr->IsBridge || (coord.Z <= BRIDGE_HEIGHT + CELL_HEIGHT(cellptr->Level + 1)) && coord.Z > BRIDGE_HEIGHT + CELL_HEIGHT(cellptr->Level - 2)) - { - if ((warhead->IsWallDestroyer && (warhead == Rule->IonCannonWarhead || Random_Pick(1, Rule->BridgeStrength) < strength))) { - for (int i = 0; i < (warhead == Rule->IonCannonWarhead ? 4 : 1); i++) { - if (Map.Destroy_Bridge_At(cell)) { - TechnoClass::Update_Mission_Targets(cellptr); - break; - } + const CellClass* bridge_owner_cell = Get_Bridge_Owner(*cellptr); + + if (bridge_owner_cell && bridge_owner_cell->Is_Overlay_Bridge() + || Is_Tile_Bridge_Middle(cellptr->Tile)) { + if (!Is_Bridge_Height(*cellptr, coord)) { + if ((warhead->IsWallDestroyer && (warhead == Rule->IonCannonWarhead || Random_Pick(1, Rule->BridgeStrength) < strength))) { + for (int i = 0; i < (warhead == Rule->IonCannonWarhead ? 4 : 1); i++) { + if (Map.Destroy_Bridge_At(cell)) { + TechnoClass::Update_Mission_Targets(cellptr); + break; } - Point2D point; - TacticalMap->Coord_To_Pixel(coord, point); - TacticalMap->Register_Dirty_Area(Rect(point.X - 128, point.Y - 128, 256, 256), false); } + Point2D point; + TacticalMap->Coord_To_Pixel(coord, point); + TacticalMap->Register_Dirty_Area(Rect(point.X - 128, point.Y - 128, 256, 256), false); } - - } } - //if (cellptr->IsBridge) - //{ - // CellClass& bridge_owner = Map[cellptr->IsBridgeOwner ? cellptr->Pos : cellptr->BridgeOwner->Pos]; - // if (bridge_owner.Overlay == OVERLAY_BRIDGE1 || bridge_owner.Overlay == OVERLAY_BRIDGE2) - - // if (!cellptr->IsBridgeOwner) - //} - - ///** - // * If there is a bridge at this location, then it may be destroyed by the - // * combat damage. - // */ - //if (cellptr->TType == TEMPLATE_BRIDGE1 || cellptr->TType == TEMPLATE_BRIDGE2 || - // cellptr->TType == TEMPLATE_BRIDGE1H || cellptr->TType == TEMPLATE_BRIDGE2H || - // cellptr->TType == TEMPLATE_BRIDGE_1A || cellptr->TType == TEMPLATE_BRIDGE_1B || - // cellptr->TType == TEMPLATE_BRIDGE_2A || cellptr->TType == TEMPLATE_BRIDGE_2B || - // cellptr->TType == TEMPLATE_BRIDGE_3A || cellptr->TType == TEMPLATE_BRIDGE_3B) { - - // if ((warhead->IsWallDestroyer && (warhead == Rule->IonCannonWarhead || Random_Pick(1, Rule->BridgeStrength) < strength))) { - // for (int i = 0; i < (warhead == Rule->IonCannonWarhead ? 4 : 1); i++) { - // if (Map.Destroy_Bridge_At(cell)) { - // TechnoClass::Update_Mission_Targets(cellptr); - // break; - // } - // } - // Point2D point; - // TacticalMap->Coord_To_Pixel(coord, point); - // TacticalMap->Register_Dirty_Area(Rect(point.X - 128, point.Y - 128, 256, 256), false); - // } - //} - - } - - if (remove_overlay) { - if (cellptr->Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(cellptr->Overlay).IsExplodes) { - cellptr->Redraw_Overlay(); - cellptr->Overlay = OVERLAY_NONE; - cellptr->Recalc_Attributes(); - Map.Update_Cell_Zone(cellptr->Pos); - Map.Update_Cell_Subzones(cellptr->Pos); - TechnoClass::Update_Mission_Targets(cellptr); - - new AnimClass(Rule->BarrelExplode, coord); - Explosion_Damage(coord, Rule->AmmoCrateDamage, nullptr, Rule->C4Warhead, true); - for (int i = 0; i < Rule->BarrelDebris.Count(); i++) { - if (Percent_Chance(15)) { - new VoxelAnimClass(Rule->BarrelDebris[i], coord); + if (bridge_owner_cell && bridge_owner_cell->Is_Overlay_Rail_Bridge() + || Is_Tile_Train_Bridge_Middle(cellptr->Tile)) { + if (!Is_Bridge_Height(*cellptr, coord)) { + if ((warhead->IsWallDestroyer && (warhead == Rule->IonCannonWarhead || Random_Pick(1, Rule->BridgeStrength) < strength))) { + for (int i = 0; i < (warhead == Rule->IonCannonWarhead ? 4 : 1); i++) { + if (Map.Destroy_Bridge_At(cell)) { + TechnoClass::Update_Mission_Targets(cellptr); + break; + } + } + Point2D point; + TacticalMap->Coord_To_Pixel(coord, point); + TacticalMap->Register_Dirty_Area(Rect(point.X - 96, point.Y - 96, 192, 192), false); } } - if (Percent_Chance(25)) { - ParticleSystemClass* barrelps = new ParticleSystemClass(Rule->BarrelParticle, coord); - barrelps->Spawn_Held_Particle(coord, coord); - } + } - for (FacingType i = FACING_N; i < FACING_COUNT; i += 2) { - Coordinate adjacent = Adjacent_Coord_With_Height(coord, i); - if (Map[adjacent].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[adjacent].Overlay).IsExplodes) { - new AnimClass(AnimTypeClass::As_Pointer(AnimTypeClass::From_Name("FIRE3")), coord, Random_Pick(1, 3) + 3); + if (cellptr->Is_Overlay_Low_Bridge()) { + if (warhead == Rule->IonCannonWarhead || Random_Pick(1, Rule->BridgeStrength) < strength) { + bool destroyed = Map.Destroy_Low_Bridge_At(cell); + Map.Destroy_Low_Bridge_At(cell); + if (destroyed) { + TechnoClass::Update_Mission_Targets(cellptr); } } } + } - if (strength > warhead->DeformThreshhold) { - const int chance = static_cast(strength) * 0.01 * warhead->Deform * 100.0; - if (Percent_Chance(chance) && (!Map[coord].IsBridge) || coord.Z < BRIDGE_HEIGHT + Map.Get_Cell_Height(coord)) { - Map.Deform(Coord_Cell(coord), false); + if (cellptr->Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(cellptr->Overlay).IsExplodes) { + cellptr->Redraw_Overlay(); + cellptr->Overlay = OVERLAY_NONE; + cellptr->Recalc_Attributes(); + Map.Update_Cell_Zone(cellptr->Pos); + Map.Update_Cell_Subzones(cellptr->Pos); + TechnoClass::Update_Mission_Targets(cellptr); + + new AnimClass(Rule->BarrelExplode, coord); + Explosion_Damage(coord, Rule->AmmoCrateDamage, nullptr, Rule->C4Warhead, true); + for (int i = 0; i < Rule->BarrelDebris.Count(); i++) { + if (Percent_Chance(15)) { + new VoxelAnimClass(Rule->BarrelDebris[i], coord); + break; } } + if (Percent_Chance(25)) { + ParticleSystemClass* barrelps = new ParticleSystemClass(Rule->BarrelParticle, coord); + barrelps->Spawn_Held_Particle(coord, coord); + } - if (cellptr->Is_Tile_Destroyable_Cliff()) { - if (Percent_Chance(Rule->CollapseChance)) { - Map.Collapse_Cliff(*cellptr); + for (FacingType i = FACING_N; i < FACING_COUNT; i += 2) { + Coordinate adjacent = Adjacent_Coord_With_Height(coord, i); + if (Map[adjacent].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[adjacent].Overlay).IsExplodes) { + new AnimClass(AnimTypeClass::As_Pointer(AnimTypeClass::From_Name("FIRE3")), coord, Random_Pick(1, 3) + 3); } } + } + + if (strength > warhead->DeformThreshhold) { + const int chance = static_cast(strength) * 0.01 * warhead->Deform * 100.0; + if (Percent_Chance(chance) && Is_Not_On_Bridge(coord)) { + Map.Deform(Coord_Cell(coord), false); + } + } - if (warhead->Particle) { - if (warhead->Particle->BehavesLike == BEHAVIOUR_SMOKE) { - MasterParticle->Spawn_Held_Particle(coord, coord); - } - else { - ParticleSystemClass* ps = new ParticleSystemClass(warhead->Particle, coord); - ps->Spawn_Held_Particle(coord, coord); - } + if (cellptr->Is_Tile_Destroyable_Cliff()) { + if (Percent_Chance(Rule->CollapseChance)) { + Map.Collapse_Cliff(*cellptr); } + } - if ((warhead->IsWallDestroyer || warhead->IsFire) && (!Map[coord].IsBridge || coord.Z < BRIDGE_HEIGHT + Map.Get_Cell_Height(coord))) { - Map.field_DC.Clear(); - if (Map.Crack_Ice(*cellptr, nullptr)) { - Map.Recalc_Ice(); - } + if (warhead->Particle) { + if (warhead->Particle->BehavesLike == BEHAVIOUR_SMOKE) { + MasterParticle->Spawn_Held_Particle(coord, coord); + } + else { + ParticleSystemClass* ps = new ParticleSystemClass(warhead->Particle, coord); + ps->Spawn_Held_Particle(coord, coord); + } + } + + if ((warhead->IsWallDestroyer || warhead->IsFire) && Is_Not_On_Bridge(coord)) { + Map.field_DC.Clear(); + if (Map.Crack_Ice(*cellptr, nullptr)) { + Map.Recalc_Ice(); } } } @@ -679,4 +692,5 @@ void CombatExtension_Hooks() Patch_Jump(0x00460244, &_Explosion_Damage_IsIceDestruction_Patch); Patch_Jump(0x00460477, &_Do_Flash_CombatLightSize_Patch); Patch_Jump(0x0045EB60, &Vinifera_Modify_Damage); + Patch_Jump(0x0045EEB0, &Vinifera_Explosion_Damage); } From 45b55b4798d276ae25e1caebad26acf67cfba3b4 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 5 Dec 2024 22:53:56 +0300 Subject: [PATCH 3/8] Incorporate previous patches into the reimplementation --- src/extensions/combat/combatext_hooks.cpp | 114 +++++----------------- 1 file changed, 24 insertions(+), 90 deletions(-) diff --git a/src/extensions/combat/combatext_hooks.cpp b/src/extensions/combat/combatext_hooks.cpp index 6f455f8e3..6e3669577 100644 --- a/src/extensions/combat/combatext_hooks.cpp +++ b/src/extensions/combat/combatext_hooks.cpp @@ -399,7 +399,19 @@ void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClas } } if (optr->IsWall) { - if (warhead->IsWallDestroyer || (warhead->IsWoodDestroyer && optr->Armor == ARMOR_WOOD)) { + + /** + * #issue-410 + * + * Implements IsWallAbsoluteDestroyer for WarheadTypes. + * + * @author: CCHyper + */ + const auto warheadtypeext = Extension::Fetch(warhead); + if (warheadtypeext->IsWallAbsoluteDestroyer) { + Map[cell].Reduce_Wall(-1); + } + else if (warhead->IsWallDestroyer || (warhead->IsWoodDestroyer && optr->Armor == ARMOR_WOOD)) { Map[cell].Reduce_Wall(strength); } } @@ -512,7 +524,17 @@ void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClas } } - if ((warhead->IsWallDestroyer || warhead->IsFire) && Is_Not_On_Bridge(coord)) { + /** + * #issue-897 + * + * Implements IsIceDestruction scenario option for preventing destruction of ice, + * and IceStrength for configuring the chance for ice getting destroyed. + * + * @author: Rampastring + */ + if ((warhead->IsWallDestroyer || warhead->IsFire) + && (RuleExtension->IceStrength <= 0 || Random_Pick(0, RuleExtension->IceStrength) < strength) + && Is_Not_On_Bridge(coord)) { Map.field_DC.Clear(); if (Map.Crack_Ice(*cellptr, nullptr)) { Map.Recalc_Ice(); @@ -533,92 +555,6 @@ static int Scale_Float_To_Int(float value, int scale) } -/** - * #issue-410 - * - * Implements IsWallAbsoluteDestroyer for WarheadTypes. - * - * @author: CCHyper - */ -DECLARE_PATCH(_Explosion_Damage_IsWallAbsoluteDestroyer_Patch) -{ - GET_REGISTER_STATIC(OverlayTypeClass *, overlay, esi); - GET_REGISTER_STATIC(const WarheadTypeClass *, warhead, ebx); - GET_REGISTER_STATIC(CellClass *, cellptr, edi); - GET_STACK_STATIC(int, strength, esp, 0x54); - static const WarheadTypeClassExtension *warheadtypeext; - - /** - * Check to make sure that the warhead is of the kind that can destroy walls. - */ - if (overlay->IsWall) { - - /** - * Is this warhead capable of instantly destroying the wall regardless - * of damage? If so, then pass -1 into Reduce_Wall to remove the wall - * section from the cell. - */ - warheadtypeext = Extension::Fetch(warhead); - if (warheadtypeext->IsWallAbsoluteDestroyer) { - cellptr->Reduce_Wall(-1); - - /** - * Original check. - */ - } else if (warhead->IsWallDestroyer || warhead->IsWoodDestroyer || overlay->Armor == ARMOR_WOOD) { - cellptr->Reduce_Wall(strength); - } - - } - - JMP_REG(ecx, 0x0045FAD0); -} - - -/** - * #issue-897 - * - * Implements IsIceDestruction scenario option for preventing destruction of ice, - * and IceStrength for configuring the chance for ice getting destroyed. - * - * @author: Rampastring - */ -DECLARE_PATCH(_Explosion_Damage_IsIceDestruction_Patch) -{ - GET_REGISTER_STATIC(const WarheadTypeClass *, warhead, edi); - GET_STACK_STATIC(int, strength, esp, 0x54); - - if (!ScenExtension->IsIceDestruction) { - goto no_ice_destruction; - } - - /** - * Stolen bytes/code here. - */ - if (warhead->IsWallDestroyer || warhead->IsConventional) { - - /** - * Allow destroying ice if the strength of ice is 0 or the random number check allows it. - */ - if (RuleExtension->IceStrength <= 0 || Random_Pick(0, RuleExtension->IceStrength) < strength) { - goto allow_ice_destruction; - } - } - - /** - * Don't allow destroying ice, continue execution after ice-destruction logic. - */ -no_ice_destruction: - JMP_REG(ecx, 0x004602DF); - - /** - * Allow destroying any potential ice on the cell. - */ -allow_ice_destruction: - JMP_REG(ecx, 0x0046025C); -} - - /** * #issue-412 * @@ -688,8 +624,6 @@ DECLARE_PATCH(_Do_Flash_CombatLightSize_Patch) */ void CombatExtension_Hooks() { - Patch_Jump(0x0045FAA0, &_Explosion_Damage_IsWallAbsoluteDestroyer_Patch); - Patch_Jump(0x00460244, &_Explosion_Damage_IsIceDestruction_Patch); Patch_Jump(0x00460477, &_Do_Flash_CombatLightSize_Patch); Patch_Jump(0x0045EB60, &Vinifera_Modify_Damage); Patch_Jump(0x0045EEB0, &Vinifera_Explosion_Damage); From c9dc2605bc2824ed7fd4b933b541a74b7cf31f41 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 5 Dec 2024 23:09:23 +0300 Subject: [PATCH 4/8] Adjust inline --- src/extensions/combat/combatext_hooks.cpp | 32 +++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/extensions/combat/combatext_hooks.cpp b/src/extensions/combat/combatext_hooks.cpp index 6e3669577..c3ddb0cb3 100644 --- a/src/extensions/combat/combatext_hooks.cpp +++ b/src/extensions/combat/combatext_hooks.cpp @@ -144,9 +144,9 @@ static CellClass* Get_Bridge_Owner(CellClass& cellptr) } -static bool Is_Bridge_Height(CellClass& cellptr, const Coordinate& coord) +static bool Is_Bridge_Level(CellClass& cellptr, const Coordinate& coord) { - return cellptr.IsBridge && (coord.Z > BRIDGE_HEIGHT + CELL_HEIGHT(cellptr.Level + 1)) && (coord.Z <= BRIDGE_HEIGHT + CELL_HEIGHT(cellptr.Level - 2)); + return !cellptr.IsBridge || coord.Z > BRIDGE_HEIGHT + CELL_HEIGHT(cellptr.Level - 2) && coord.Z <= BRIDGE_HEIGHT + CELL_HEIGHT(cellptr.Level + 1); } @@ -206,7 +206,7 @@ static bool Is_Not_On_Bridge(const Coordinate& coord) * 06/20/1994 JLB : Source is a pointer. * * 06/18/1996 JLB : Strength could be negative for healing effects. * *============================================================================================= */ -void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClass* source, const WarheadTypeClass* warhead, bool explode_tib) +void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClass* source, const WarheadTypeClass* warhead, bool do_chain_reaction) { Cell cell; // Cell number under explosion. ObjectClass* object; // Working object pointer. @@ -272,8 +272,8 @@ void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClas } } - bool isbridge = cellptr->IsBridge && coord.Z > BRIDGE_HEIGHT / 2 + Map.Get_Cell_Height(coord); - ObjectClass* impacto = cellptr->Cell_Occupier(isbridge); + const bool isaltoccupier = cellptr->IsBridge && coord.Z > BRIDGE_HEIGHT / 2 + Map.Get_Cell_Height(coord); + ObjectClass* impacto = cellptr->Cell_Occupier(isaltoccupier); /** * Fill the list of unit IDs that will have damage @@ -296,7 +296,7 @@ void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClas * damage to. Do not include overlapping objects; selection state can affect * the overlappers, and this causes multiplayer games to go out of sync. */ - object = cellptr->Cell_Occupier(isbridge); + object = cellptr->Cell_Occupier(isaltoccupier); while (object) { if (object->Kind_Of() == RTTI_UNIT && Scen->SpecialFlags.IsHarvesterImmune && @@ -354,7 +354,7 @@ void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClas } } - double rockerval = std::min(strength * 0.01, 4.0); + const double rockerval = std::min(strength * 0.01, 4.0); if (warhead->IsRocker) { if (rockerval > 0.3) { const int cell_radius = 3; @@ -363,7 +363,7 @@ void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClas int xpos = cell.X + x; int ypos = cell.Y + y; - object = Map[Cell(xpos, ypos)].Cell_Occupier(isbridge); + object = Map[Cell(xpos, ypos)].Cell_Occupier(isaltoccupier); while (object) { TechnoClass* techno = object->As_Techno(); if (techno) { @@ -393,7 +393,7 @@ void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClas OverlayTypeClass const* optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); if (optr->IsChainReaction) { - if ((!optr->IsTiberium || warhead->IsTiberiumDestroyer) && explode_tib) { + if (!(optr->IsTiberium && !warhead->IsTiberiumDestroyer) && do_chain_reaction) { Chain_Reaction_Damage(cell); cellptr->Reduce_Tiberium(strength / 10); } @@ -429,8 +429,8 @@ void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClas if (bridge_owner_cell && bridge_owner_cell->Is_Overlay_Bridge() || Is_Tile_Bridge_Middle(cellptr->Tile)) { - if (!Is_Bridge_Height(*cellptr, coord)) { - if ((warhead->IsWallDestroyer && (warhead == Rule->IonCannonWarhead || Random_Pick(1, Rule->BridgeStrength) < strength))) { + if (Is_Bridge_Level(*cellptr, coord)) { + if (warhead->IsWallDestroyer && (warhead == Rule->IonCannonWarhead || Random_Pick(1, Rule->BridgeStrength) < strength)) { for (int i = 0; i < (warhead == Rule->IonCannonWarhead ? 4 : 1); i++) { if (Map.Destroy_Bridge_At(cell)) { TechnoClass::Update_Mission_Targets(cellptr); @@ -446,8 +446,8 @@ void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClas if (bridge_owner_cell && bridge_owner_cell->Is_Overlay_Rail_Bridge() || Is_Tile_Train_Bridge_Middle(cellptr->Tile)) { - if (!Is_Bridge_Height(*cellptr, coord)) { - if ((warhead->IsWallDestroyer && (warhead == Rule->IonCannonWarhead || Random_Pick(1, Rule->BridgeStrength) < strength))) { + if (Is_Bridge_Level(*cellptr, coord)) { + if (warhead->IsWallDestroyer && (warhead == Rule->IonCannonWarhead || Random_Pick(1, Rule->BridgeStrength) < strength)) { for (int i = 0; i < (warhead == Rule->IonCannonWarhead ? 4 : 1); i++) { if (Map.Destroy_Bridge_At(cell)) { TechnoClass::Update_Mission_Targets(cellptr); @@ -463,7 +463,7 @@ void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClas if (cellptr->Is_Overlay_Low_Bridge()) { if (warhead == Rule->IonCannonWarhead || Random_Pick(1, Rule->BridgeStrength) < strength) { - bool destroyed = Map.Destroy_Low_Bridge_At(cell); + const bool destroyed = Map.Destroy_Low_Bridge_At(cell); Map.Destroy_Low_Bridge_At(cell); if (destroyed) { TechnoClass::Update_Mission_Targets(cellptr); @@ -489,8 +489,8 @@ void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClas } } if (Percent_Chance(25)) { - ParticleSystemClass* barrelps = new ParticleSystemClass(Rule->BarrelParticle, coord); - barrelps->Spawn_Held_Particle(coord, coord); + ParticleSystemClass* ps = new ParticleSystemClass(Rule->BarrelParticle, coord); + ps->Spawn_Held_Particle(coord, coord); } for (FacingType i = FACING_N; i < FACING_COUNT; i += 2) { From 7251a94aaeb572774a4c25d411f0795cbca42268 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Fri, 6 Dec 2024 02:54:24 +0300 Subject: [PATCH 5/8] Include Jumpjet vehicles in the list of objects to damage --- src/extensions/combat/combatext_hooks.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/extensions/combat/combatext_hooks.cpp b/src/extensions/combat/combatext_hooks.cpp index c3ddb0cb3..a044aeb7a 100644 --- a/src/extensions/combat/combatext_hooks.cpp +++ b/src/extensions/combat/combatext_hooks.cpp @@ -63,6 +63,7 @@ #include "veinholemonster.h" #include "verses.h" #include "voxelanim.h" +#include "jumpjetlocomotion.h" /** @@ -261,7 +262,7 @@ void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClas for (int index = 0; index < Units.Count(); index++) { UnitClass* unit = Units[index]; - if (unit->IsActive && unit->Class->IsJellyfish) { + if (unit->IsActive && (unit->Class->IsJellyfish || unit->Class->Locomotor == __uuidof(JumpjetLocomotionClass))) { if (unit->IsDown && unit->Strength > 0) { distance = Distance(coord, unit->Center_Coord()); if (distance < range) { From 548a260c81216bf4194075cc47c42c9ed7a690c8 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Fri, 6 Dec 2024 03:25:32 +0300 Subject: [PATCH 6/8] Make it so that aircraft only get reduced damage distance when in air --- src/extensions/combat/combatext_hooks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extensions/combat/combatext_hooks.cpp b/src/extensions/combat/combatext_hooks.cpp index a044aeb7a..438db9d43 100644 --- a/src/extensions/combat/combatext_hooks.cpp +++ b/src/extensions/combat/combatext_hooks.cpp @@ -338,7 +338,7 @@ void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClas } else { distance = Distance_Level_Snap(coord, object->Target_Coord()); - if (object->Kind_Of() == RTTI_AIRCRAFT) { + if (object->Kind_Of() == RTTI_AIRCRAFT && object->In_Air()) { distance /= 2; } } From 6ca98aebcee715d0f9b6ddd098feecfa029e6d67 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Sat, 14 Dec 2024 16:47:06 +0300 Subject: [PATCH 7/8] Move helpers to CellClass --- src/extensions/combat/combatext_hooks.cpp | 80 +++++------------------ 1 file changed, 17 insertions(+), 63 deletions(-) diff --git a/src/extensions/combat/combatext_hooks.cpp b/src/extensions/combat/combatext_hooks.cpp index 438db9d43..2886e1e55 100644 --- a/src/extensions/combat/combatext_hooks.cpp +++ b/src/extensions/combat/combatext_hooks.cpp @@ -135,78 +135,32 @@ int Vinifera_Modify_Damage(int damage, WarheadTypeClass* warhead, ArmorType armo } -static CellClass* Get_Bridge_Owner(CellClass& cellptr) -{ - if (cellptr.IsBridge) { - return &Map[cellptr.IsBridgeOwner ? cellptr.Pos : cellptr.BridgeOwner->Pos]; - } - - return nullptr; -} - - static bool Is_Bridge_Level(CellClass& cellptr, const Coordinate& coord) { return !cellptr.IsBridge || coord.Z > BRIDGE_HEIGHT + CELL_HEIGHT(cellptr.Level - 2) && coord.Z <= BRIDGE_HEIGHT + CELL_HEIGHT(cellptr.Level + 1); } -static bool Is_Tile_Bridge_Middle(IsometricTileType type) -{ - const IsometricTileType ttype = type - BridgeSet + 1; - return (ttype == BridgeMiddle1 + 0 || ttype == BridgeMiddle1 + 1 || - ttype == BridgeMiddle1 + 2 || ttype == BridgeMiddle1 + 3 || - ttype == BridgeMiddle2 + 0 || ttype == BridgeMiddle2 + 1 || - ttype == BridgeMiddle2 + 2 || ttype == BridgeMiddle2 + 3); -} - - -static bool Is_Tile_Train_Bridge_Middle(IsometricTileType type) -{ - const IsometricTileType ttype = type - TrainBridgeSet + 1; - return (ttype == BridgeMiddle1 + 0 || ttype == BridgeMiddle1 + 1 || - ttype == BridgeMiddle1 + 2 || ttype == BridgeMiddle1 + 3 || - ttype == BridgeMiddle2 + 0 || ttype == BridgeMiddle2 + 1 || - ttype == BridgeMiddle2 + 2 || ttype == BridgeMiddle2 + 3); -} - - static bool Is_Not_On_Bridge(const Coordinate& coord) { return !Map[coord].IsBridge || coord.Z < BRIDGE_HEIGHT + Map.Get_Cell_Height(coord); } -/*********************************************************************************************** - * Explosion_Damage -- Inflict an explosion damage affect. * - * * - * Processes the collateral damage affects typically caused by an * - * explosion. * - * * - * INPUT: coord -- The coordinate of ground zero. * - * * - * strength -- Raw damage points at ground zero. * - * * - * source -- Source of the explosion (who is responsible). * - * * - * warhead -- The kind of explosion to process. * - * * - * OUTPUT: none * - * * - * WARNINGS: This routine can consume some time and will affect the AI * - * of nearby enemy units (possibly). * - * * - * HISTORY: * - * 08/16/1991 JLB : Created. * - * 11/30/1991 JLB : Uses coordinate system. * - * 12/27/1991 JLB : Radius of explosion damage effect. * - * 04/13/1994 JLB : Streamlined. * - * 04/16/1994 JLB : Warhead damage type modifier. * - * 04/17/1994 JLB : Cleaned up. * - * 06/20/1994 JLB : Uses object pointers to distribute damage. * - * 06/20/1994 JLB : Source is a pointer. * - * 06/18/1996 JLB : Strength could be negative for healing effects. * - *============================================================================================= */ +/** + * Inflict an explosion damage affect. + * + * @author: 08/16/1991 JLB : Created. + * 11/30/1991 JLB : Uses coordinate system. + * 12/27/1991 JLB : Radius of explosion damage effect. + * 04/13/1994 JLB : Streamlined. + * 04/16/1994 JLB : Warhead damage type modifier. + * 04/17/1994 JLB : Cleaned up. + * 06/20/1994 JLB : Uses object pointers to distribute damage. + * 06/20/1994 JLB : Source is a pointer. + * 06/18/1996 JLB : Strength could be negative for healing effects. + * 12/14/2024 ZivDero : Adjustments for Tiberian Sun + */ void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClass* source, const WarheadTypeClass* warhead, bool do_chain_reaction) { Cell cell; // Cell number under explosion. @@ -426,10 +380,10 @@ void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClas * combat damage. */ if (Scen->SpecialFlags.IsDestroyableBridges && warhead->IsWallDestroyer) { - const CellClass* bridge_owner_cell = Get_Bridge_Owner(*cellptr); + const CellClass* bridge_owner_cell = cellptr->Get_Bridge_Owner(); if (bridge_owner_cell && bridge_owner_cell->Is_Overlay_Bridge() - || Is_Tile_Bridge_Middle(cellptr->Tile)) { + || cellptr->Is_Tile_Bridge_Middle()) { if (Is_Bridge_Level(*cellptr, coord)) { if (warhead->IsWallDestroyer && (warhead == Rule->IonCannonWarhead || Random_Pick(1, Rule->BridgeStrength) < strength)) { for (int i = 0; i < (warhead == Rule->IonCannonWarhead ? 4 : 1); i++) { @@ -446,7 +400,7 @@ void Vinifera_Explosion_Damage(const Coordinate& coord, int strength, TechnoClas } if (bridge_owner_cell && bridge_owner_cell->Is_Overlay_Rail_Bridge() - || Is_Tile_Train_Bridge_Middle(cellptr->Tile)) { + || cellptr->Is_Tile_Train_Bridge_Middle()) { if (Is_Bridge_Level(*cellptr, coord)) { if (warhead->IsWallDestroyer && (warhead == Rule->IonCannonWarhead || Random_Pick(1, Rule->BridgeStrength) < strength)) { for (int i = 0; i < (warhead == Rule->IonCannonWarhead ? 4 : 1); i++) { From 01bd5fca1476bbb90c79f0ad47a9ef8a2484d043 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Sat, 14 Dec 2024 16:49:04 +0300 Subject: [PATCH 8/8] Add docs --- CREDITS.md | 1 + docs/Bugfixes.md | 1 + docs/Whats-New.md | 1 + 3 files changed, 3 insertions(+) diff --git a/CREDITS.md b/CREDITS.md index 537673166..5d903a822 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -208,4 +208,5 @@ This page lists all the individual contributions to the project by their author. - Allow turning off "sticky" technologies. - Allow disabling the ActLike check on construction yards to allow for faction-specific MCVs. - Finalize the feature for animations to spawn additional animations. + - VehicleTypes with Jumpjet locomotion now take damage in flight. diff --git a/docs/Bugfixes.md b/docs/Bugfixes.md index 9aafdc8d7..e9642feef 100644 --- a/docs/Bugfixes.md +++ b/docs/Bugfixes.md @@ -63,3 +63,4 @@ This page lists all vanilla bugs fixed by Vinifera. - Fix a bug where units could gain veterancy by killing allies. - Fix a bug where AI players would send teams to attack their allies. - `[CombatDamage]->MinDamage` now works as expected and damage is no long always a minimum of `1`. +- VehicleTypes with Jumpjet locomotion now take damage in flight. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index bf264ce05..f7187324e 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -231,6 +231,7 @@ Vanilla fixes: - Fix a bug where units could gain veterancy by killing allies (by ZivDero) - Fix a bug where AI players would send teams to attack their allies (by ZivDero) - `[CombatDamage]->MinDamage` now works as expected and damage is no long always a minimum of `1` (by ZivDero) +- VehicleTypes with Jumpjet locomotion now take damage in flight (by ZivDero)