diff --git a/MekHQ/src/mekhq/campaign/unit/damage/AeroDamageApplier.java b/MekHQ/src/mekhq/campaign/unit/damage/AeroDamageApplier.java deleted file mode 100644 index 389081a155..0000000000 --- a/MekHQ/src/mekhq/campaign/unit/damage/AeroDamageApplier.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. - * - * This file is part of MekHQ. - * - * MekHQ is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * MekHQ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with MekHQ. If not, see . - */ - -package mekhq.campaign.unit.damage; - -import megamek.common.Aero; -import megamek.common.HitData; -import megamek.common.IEntityRemovalConditions; - -import java.util.ArrayList; -import java.util.List; - -import static megamek.common.Compute.randomInt; - -/** - * Applies damage to an Aero entity. - * @param entity Aero entity - * @param crewMustSurvive Whether the crew must survive - * @param entityMustSurvive Whether the entity must survive - * @author Luana Coppio - */ -public record AeroDamageApplier(Aero entity, boolean crewMustSurvive, boolean entityMustSurvive) implements DamageApplier { - - @Override - public int getRandomHitLocation() { - var entity = entity(); - List validLocations = new ArrayList<>(); - for (int i = 0; i < entity.locations(); i++) { - if (entity.getOArmor(i) <= 0) { - continue; - } - if (!entity.isLocationBlownOff(i) && entity.getSI() > 0) { - validLocations.add(i); - } - } - - return validLocations.isEmpty() ? -1 : validLocations.get(randomInt(validLocations.size())); - } - - @Override - public void destroyLocationAfterEjection() { - entity().setDestroyed(true); - entity().setRemovalCondition(IEntityRemovalConditions.REMOVE_DEVASTATED); - } - - @Override - public HitDetails damageInternals(HitDetails hitDetails) { - HitData hit = hitDetails.hit(); - var entity = entity(); - int currentInternalValue = entity.getSI(); - int newInternalValue = Math.max(currentInternalValue + hitDetails.setArmorValueTo(), 0); - entity.setArmor(0, hit); - entity.setSI(newInternalValue); - applyDamageToEquipments(hit); - if (newInternalValue == 0) { - hitDetails = destroyLocation(hitDetails); - } - return hitDetails; - } -} diff --git a/MekHQ/src/mekhq/campaign/unit/damage/DamageApplier.java b/MekHQ/src/mekhq/campaign/unit/damage/DamageApplier.java deleted file mode 100644 index d484f441b0..0000000000 --- a/MekHQ/src/mekhq/campaign/unit/damage/DamageApplier.java +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. - * - * This file is part of MekHQ. - * - * MekHQ is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * MekHQ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with MekHQ. If not, see . - */ -package mekhq.campaign.unit.damage; - -import megamek.common.*; -import megamek.logging.MMLogger; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * @author Luana Coppio - */ -public interface DamageApplier { - MMLogger logger = MMLogger.create(DamageApplier.class); - - record HitDetails(HitData hit, int damageToApply, int setArmorValueTo, boolean hitInternal, int hitCrew) { - public HitDetails withCrewDamage(int crewDamage) { - return new HitDetails(hit, damageToApply, setArmorValueTo, hitInternal, crewDamage); - } - public HitDetails killsCrew() { - return new HitDetails(hit, damageToApply, setArmorValueTo, hitInternal, Crew.DEATH); - } - } - - E entity(); - - boolean crewMustSurvive(); - - boolean entityMustSurvive(); - - /** - * Applies damage to the entity in clusters of a given size. - * This is USUALLY the function you will want to use. - * - * @param dmg the total damage to apply - * @param clusterSize the size of the clusters - */ - default int applyDamageInClusters(int dmg, int clusterSize) { - int totalDamage = dmg; - int damageApplied = 0; - while (totalDamage > 0) { - if (entity().isCrippled() && entity().getRemovalCondition() == IEntityRemovalConditions.REMOVE_DEVASTATED) { - // devastated units don't need to take any damage anymore - break; - } - var clusterDamage = Math.min(totalDamage, clusterSize); - applyDamage(clusterDamage); - totalDamage -= clusterDamage; - damageApplied += clusterDamage; - } - return damageApplied; - } - - /** - * Applies damage to the entity. - * - * @param dmg the total damage to apply - */ - default void applyDamage(int dmg) { - int hitLocation = getRandomHitLocation(); - if (hitLocation == -1) { - entity().setDestroyed(true); - return; - } - HitData hit = getHitData(hitLocation); - HitDetails hitDetails = setupHitDetails(hit, dmg); - applyDamage(hitDetails); - } - - /** - * Returns the location to hit. - * - * @return returns a valid random location to be hit. - */ - default int getRandomHitLocation() { - var entity = entity(); - List validLocations = new ArrayList<>(); - for (int i = 0; i < entity.locations(); i++) { - if (entity.getOArmor(i) <= 0) { - continue; - } - var locationIsNotBlownOff = !entity.isLocationBlownOff(i); - var locationIsNotDestroyed = entity.getInternal(i) > 0; - if (locationIsNotBlownOff && locationIsNotDestroyed) { - validLocations.add(i); - } - } - Collections.shuffle(validLocations); - return validLocations.isEmpty() ? -1 : validLocations.get(0); - } - - /** - * Hits the entity with the given hit details. - * - * @param hitDetails the hit details - */ - default void applyDamage(HitDetails hitDetails) { - hitDetails = damageArmor(hitDetails); - if (hitDetails.hitInternal()) { - hitDetails = damageInternals(hitDetails); - } - tryToDamageCrew(hitDetails.hitCrew()); - } - - /** - * Destroys the location after the crew has been ejected. - */ - default void destroyLocationAfterEjection() { - // default implementation does nothing - } - - /** - * Applies damage to the internals of the entity. - * - * @param hitDetails the hit details - */ - default HitDetails damageInternals(HitDetails hitDetails) { - HitData hit = hitDetails.hit(); - var entity = entity(); - int currentInternalValue = entity.getInternal(hit); - int newInternalValue = Math.max(currentInternalValue + hitDetails.setArmorValueTo(), entityMustSurvive() ? 1 : 0); - entity.setArmor(0, hit); - logger.trace("[{}] Damage: {} - Internal at: {}", entity.getDisplayName(), hitDetails.damageToApply(), newInternalValue); - entity.setInternal(newInternalValue, hit); - applyDamageToEquipments(hit); - if (newInternalValue == 0) { - hitDetails = destroyLocation(hitDetails); - } - return hitDetails; - } - - /** - * Destroys the location of the entity. This one is used when you have the HitData. - * - * @param hitDetails the hit details with information about the location - */ - default HitDetails destroyLocation(HitDetails hitDetails) { - destroyLocation(hitDetails.hit().getLocation()); - return hitDetails.killsCrew(); - } - - /** - * Destroys the location of the entity. This one is used when the location is already known. - * - * @param location the location index in the entity - */ - default void destroyLocation(int location) { - var entity = entity(); - logger.trace("[{}] Destroying location {}", entity.getDisplayName(), location); - entity.destroyLocation(location); - entity.setDestroyed(true); - setEntityDestroyed(entity); - } - - /** - * Tries to damage the crew of the entity. If the crew is dead, the entity is marked as destroyed. - * The crew won't be damaged if they are already ejected or already dead. - * This function also does try to not outright kill the crew, as it has proven to be a bit too deadly. - * @param hitCrew the amount of hits to apply to ALL crew - */ - default void tryToDamageCrew(int hitCrew) { - if (hitCrew == 0) { - return; - } - - var entity = entity(); - Crew crew = entity.getCrew(); - if (crew == null || crew.isEjected() || crew.isDead()) { - return; - } - var hits = tryToNotKillTheCrew(hitCrew, crew); - - crew.setHits(hits, 0); - logger.trace("[{}] Crew hit ({} hits)", entity().getDisplayName(), crew.getHits()); - if (crew.isDead()) { - logger.trace("[{}] Crew died", entity().getDisplayName()); - entity.setDestroyed(true); - setEntityDestroyed(entity); - } - } - - /** - * Tries to not kill the crew. This function will set the amount of hits the crew will take. - * If the crew must survive, the crew will try to eject instead, and if they can't be - * ejected, it will set the total hits to DEATH - 1 (so... 5). - * @param hitCrew the amount of hits to apply to ALL crew - * @param crew the crew to apply the hits to - * @return the amount of hits to apply to the crew - */ - private Integer tryToNotKillTheCrew(int hitCrew, Crew crew) { - var hits = Math.min(crew.getHits() + hitCrew, Crew.DEATH); - - if (hits == Crew.DEATH) { - if (crewMustSurvive()) { - hits = Compute.randomIntInclusive(4) + 1; - } - if (tryToEjectCrew()) { - destroyLocationAfterEjection(); - } - } - return hits; - } - - /** - * Sets the entity as destroyed. - * @param entity entity to be set as destroyed - * @param the type of the entity - */ - static void setEntityDestroyed(E entity) { - if (entity.getRemovalCondition() != IEntityRemovalConditions.REMOVE_DEVASTATED) { - entity.setRemovalCondition(IEntityRemovalConditions.REMOVE_SALVAGEABLE); - entity.setSalvage(true); - logger.trace("[{}] Entity destroyed", entity.getDisplayName()); - } - } - - /** - * Sets the entity as destroyed by ejection if possible. - * @param entity entity to be set as destroyed - * @param the type of the entity - */ - static void setEntityDestroyedByEjection(E entity) { - if (entity.getRemovalCondition() != IEntityRemovalConditions.REMOVE_DEVASTATED) { - entity.setRemovalCondition(IEntityRemovalConditions.REMOVE_EJECTED); - entity.setSalvage(true); - logger.trace("[{}] Entity destroyed by ejection", entity.getDisplayName()); - } - } - - /** - * Tries to eject the crew of the entity if possible. - * - * @return true if the crew was ejected - */ - default boolean tryToEjectCrew() { - var entity = entity(); - var crew = entity.getCrew(); - if (crew == null || crew.isEjected() || !entity().isEjectionPossible() || entityMustSurvive()) { - return false; - } - crew.setEjected(true); - entity.setDestroyed(true); - setEntityDestroyedByEjection(entity); - logger.trace("[{}] Crew ejected", entity().getDisplayName()); - return true; - } - - /** - * Applies damage to the equipments of the entity. - * - * @param hit the hit data with information about the location - */ - default void applyDamageToEquipments(HitData hit) { - var entity = entity(); - var criticalSlots = entity.getCriticalSlots(hit.getLocation()); - Collections.shuffle(criticalSlots); - for (CriticalSlot slot : criticalSlots) { - if (slot != null && slot.isHittable() && !slot.isHit() && !slot.isDestroyed()) { - slot.setHit(true); - slot.setDestroyed(true); - logger.trace("[{}] Equipment destroyed: {}", entity.getDisplayName(), slot); - break; - } - } - } - - /** - * Applies damage only to the armor of the entity. - * - * @param hitDetails the hit details - */ - default HitDetails damageArmor(HitDetails hitDetails) { - var currentArmorValue = Math.max(hitDetails.setArmorValueTo(), 0); - entity().setArmor(currentArmorValue, hitDetails.hit()); - logger.trace("[{}] Damage: {} - Armor at: {}", entity().getDisplayName(), hitDetails.damageToApply(), currentArmorValue); - return hitDetails; - } - - /** - * Returns the hit data for the given hit location. - * - * @param hitLocation the hit location - * @return the hit data - */ - default HitData getHitData(int hitLocation) { - return new HitData(hitLocation, false, HitData.EFFECT_NONE); - } - - /** - * Sets up the hit details for the given hit and damage. - * - * @param hit the hit data - * @param damageToApply the damage to apply - * @return the hit details - */ - default HitDetails setupHitDetails(HitData hit, int damageToApply) { - int currentArmorValue = entity().getArmor(hit); - int setArmorValueTo = currentArmorValue - damageToApply; - boolean hitInternal = setArmorValueTo < 0; - int hitCrew = hitInternal ? 1 : 0; - - return new HitDetails(hit, damageToApply, setArmorValueTo, hitInternal, hitCrew); - } -} diff --git a/MekHQ/src/mekhq/campaign/unit/damage/DamageApplierChooser.java b/MekHQ/src/mekhq/campaign/unit/damage/DamageApplierChooser.java deleted file mode 100644 index e1aaad37d6..0000000000 --- a/MekHQ/src/mekhq/campaign/unit/damage/DamageApplierChooser.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. - * - * This file is part of MekHQ. - * - * MekHQ is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * MekHQ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with MekHQ. If not, see . - */ - -package mekhq.campaign.unit.damage; - -import megamek.common.*; - -/** - * @author Luana Coppio - */ -public class DamageApplierChooser { - - - /** - * Choose the correct DamageHandler for the given entity. - * A damage handler is a class that handles applying damage on an entity, be it a Mek, Infantry, etc. - * It can damage internal, armor, cause criticals, kill crew, set limbs as blown-off, can even destroy the entity, - * @param entity the entity to choose the handler for - * @return the correct DamageHandler for the given entity - */ - public static DamageApplier choose(Entity entity) { - return choose(entity, EntityFinalState.ANY); - } - - /** - * Choose the correct DamageHandler for the given entity. - * A damage handler is a class that handles applying damage on an entity, be it a Mek, Infantry, etc. - * It can damage internal, armor, cause criticals, kill crew, set limbs as blown-off, can even destroy the entity, - * This one also accepts parameters to indicate if the crew must survive and if the entity must survive. - * @param entity the entity to choose the handler for - * @param entityFinalState if the crew must survive and/or entity must survive - * @return the correct DamageHandler for the given entity - */ - public static DamageApplier choose( - Entity entity, EntityFinalState entityFinalState) { - return choose(entity, entityFinalState.crewMustSurvive, entityFinalState.entityMustSurvive); - } - - /** - * Choose the correct DamageHandler for the given entity. - * A damage handler is a class that handles applying damage on an entity, be it a Mek, Infantry, etc. - * It can damage internal, armor, cause critical, kill crew, set limbs as blown-off, can even destroy the entity, - * This one also accepts parameters to indicate if the crew must survive and if the entity must survive. - * @param entity the entity to choose the handler for - * @param crewMustSurvive if the crew must survive - * @param entityMustSurvive if the entity must survive - * @return the correct DamageHandler for the given entity - */ - public static DamageApplier choose( - Entity entity, boolean crewMustSurvive, boolean entityMustSurvive) { - - if (entity instanceof Infantry) { - return new InfantryDamageApplier((Infantry) entity); - } else if (entity instanceof Mek) { - return new MekDamageApplier((Mek) entity, crewMustSurvive, entityMustSurvive); - } else if (entity instanceof GunEmplacement) { - return new GunEmplacementDamageApplier((GunEmplacement) entity, crewMustSurvive, entityMustSurvive); - } else if (entity instanceof Aero) { - return new AeroDamageApplier((Aero) entity, crewMustSurvive, entityMustSurvive); - } - return new SimpleDamageApplier(entity, crewMustSurvive, entityMustSurvive); - } - - /** - * Automatically applies damage to the entity based on the "removal condition" provided. - * The damage is calculated as being a percentage of the total armor of the unit, then it is transformed in a roll of dices - * which the average roll is that amount, then the total damage is calculated and applied in clusters of 5 damage. It rolls a minimum of - * 1 dice of damage. - * The removal condition is a code that indicates why the entity is being removed from the game. - * It will decide if the unit or entity must survive based on the type of removal condition. - * The removal conditions are: - * * RETREAT: crew must survive, entity must survive, 80% of the total armor is applied as damage - * * SALVAGEABLE: crew may die, entity must be destroyed, 75% of the total armor is applied as damage - * * CAPTURED: crew must survive, entity must be destroyed, 33% of the total armor is applied as damage - * * EJECTED: crew must survive, entity must be destroyed, 33% of the total armor is applied as damage - * * DEVASTATED: crew may survive, entity must be destroyed, 500% of the total armor is applied as damage - * * OTHER: crew may die, entity may be destroyed, 33% of the total armor applied as damage - * The amount of damage applied present right now was decided arbitrarily and can be changed later, maybe even make it follow - * a config file, client settings, etc. - * - * @param entity the entity to choose the handler for - * @param removalCondition the reason why the entity is being removed - */ - public static void damageRemovedEntity(Entity entity, int removalCondition) { - var numberOfDices = getNumberOfDices(entity, removalCondition); - var damage = Compute.d6(numberOfDices); - var clusterSize = 5; - - var retreating = removalCondition == IEntityRemovalConditions.REMOVE_IN_RETREAT; - var captured = removalCondition == IEntityRemovalConditions.REMOVE_CAPTURED; - var ejected = removalCondition == IEntityRemovalConditions.REMOVE_EJECTED; - var devastated = removalCondition == IEntityRemovalConditions.REMOVE_DEVASTATED; - var salvageable = removalCondition == IEntityRemovalConditions.REMOVE_SALVAGEABLE; - - var crewMustSurvive = retreating || captured || ejected; - var entityMustSurvive = !devastated && !salvageable && !ejected; - - DamageApplierChooser.choose(entity, crewMustSurvive, entityMustSurvive) - .applyDamageInClusters(damage, clusterSize); - } - - private static int getNumberOfDices(Entity entity, int removalCondition) { - double targetDamage = switch (removalCondition) { - case IEntityRemovalConditions.REMOVE_CAPTURED, IEntityRemovalConditions.REMOVE_EJECTED -> entity.getTotalOArmor() * 1.2; - case IEntityRemovalConditions.REMOVE_DEVASTATED -> entity.getTotalOArmor() * 5; // no damage is actually applied - case IEntityRemovalConditions.REMOVE_IN_RETREAT -> entity.getTotalOArmor() * 0.8; - case IEntityRemovalConditions.REMOVE_SALVAGEABLE -> entity.getTotalOArmor() * 0.75; - default -> entity.getTotalOArmor() * 0.33; - }; - - return Math.max(1, (int) (targetDamage / 6 / 0.6)); - } - -} diff --git a/MekHQ/src/mekhq/campaign/unit/damage/EntityFinalState.java b/MekHQ/src/mekhq/campaign/unit/damage/EntityFinalState.java deleted file mode 100644 index d819434cff..0000000000 --- a/MekHQ/src/mekhq/campaign/unit/damage/EntityFinalState.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. - * - * This file is part of MekHQ. - * - * MekHQ is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * MekHQ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with MekHQ. If not, see . - */ - -package mekhq.campaign.unit.damage; - - -public enum EntityFinalState { - ANY(false, false), - CREW_MUST_SURVIVE(true, false), - ENTITY_MUST_SURVIVE(false, true), - CREW_AND_ENTITY_MUST_SURVIVE(true, true); - - final boolean crewMustSurvive; - final boolean entityMustSurvive; - - EntityFinalState(boolean crewMustSurvive, boolean entityMustSurvive) { - this.crewMustSurvive = crewMustSurvive; - this.entityMustSurvive = entityMustSurvive; - } -} diff --git a/MekHQ/src/mekhq/campaign/unit/damage/GunEmplacementDamageApplier.java b/MekHQ/src/mekhq/campaign/unit/damage/GunEmplacementDamageApplier.java deleted file mode 100644 index 6830960112..0000000000 --- a/MekHQ/src/mekhq/campaign/unit/damage/GunEmplacementDamageApplier.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. - * - * This file is part of MekHQ. - * - * MekHQ is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * MekHQ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with MekHQ. If not, see . - */ - -package mekhq.campaign.unit.damage; - - -import megamek.common.GunEmplacement; - -public record GunEmplacementDamageApplier(GunEmplacement entity, boolean crewMustSurvive, boolean entityMustSurvive) - implements DamageApplier { - - @Override - public int getRandomHitLocation() { - if (entity.isDestroyed()) { - return -1; - } - return 0; - } - -} diff --git a/MekHQ/src/mekhq/campaign/unit/damage/InfantryDamageApplier.java b/MekHQ/src/mekhq/campaign/unit/damage/InfantryDamageApplier.java deleted file mode 100644 index 40dfc54436..0000000000 --- a/MekHQ/src/mekhq/campaign/unit/damage/InfantryDamageApplier.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. - * - * This file is part of MekHQ. - * - * MekHQ is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * MekHQ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with MekHQ. If not, see . - */ - -package mekhq.campaign.unit.damage; - -import megamek.common.*; - -/** - * @author Luana Coppio - */ -public record InfantryDamageApplier(Infantry entity) implements DamageApplier { - - @Override - public boolean crewMustSurvive() { - return false; - } - - @Override - public boolean entityMustSurvive() { - return false; - } - - @Override - public HitDetails damageArmor(HitDetails hitDetails) { - if (entity() instanceof BattleArmor te) { - for (int i = 0; i < te.getTroopers(); i++) { - var currentValueArmor = te.getArmor(BattleArmor.LOC_SQUAD); - var newArmorValue = Math.max(currentValueArmor - 1, 0); - if (te.getArmor(BattleArmor.LOC_TROOPER_1 + i) > 0) { - te.setArmor(newArmorValue, BattleArmor.LOC_TROOPER_1 + i); - } - } - } - if (entity().isCrippled()) { - entity().setDestroyed(true); - } - return hitDetails; - } - - @Override - public HitDetails damageInternals(HitDetails hitDetails) { - if (entity() instanceof BattleArmor te) { - for (int i = 0; i < te.getTroopers(); i++) { - var currentValue = te.getInternal(BattleArmor.LOC_SQUAD); - var newValue = Math.max(currentValue - 1, 0); - if (te.getInternal(BattleArmor.LOC_TROOPER_1 + i) > 0) { - te.setInternal(newValue, BattleArmor.LOC_TROOPER_1 + i); - } - } - } else { - var currentValue = entity().getInternal(Infantry.LOC_INFANTRY); - var newValue = Math.max(currentValue - 1, 0); - entity().setInternal(newValue, Infantry.LOC_INFANTRY); - } - - if (entity().isCrippled()) { - entity().setDestroyed(true); - } - return hitDetails; - } -} diff --git a/MekHQ/src/mekhq/campaign/unit/damage/MekDamageApplier.java b/MekHQ/src/mekhq/campaign/unit/damage/MekDamageApplier.java deleted file mode 100644 index be083408e5..0000000000 --- a/MekHQ/src/mekhq/campaign/unit/damage/MekDamageApplier.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. - * - * This file is part of MekHQ. - * - * MekHQ is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * MekHQ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with MekHQ. If not, see . - */ - -package mekhq.campaign.unit.damage; - - -import megamek.common.*; -import megamek.common.util.weightedMaps.WeightedDoubleMap; - -import java.util.Collections; -import java.util.Objects; -import java.util.Set; - -import static megamek.common.Compute.rollD6; -import static megamek.common.CriticalSlot.TYPE_SYSTEM; - -/** - * @author Luana Coppio - */ -public record MekDamageApplier(Mek entity, boolean crewMustSurvive, boolean entityMustSurvive) implements DamageApplier { - - // Target roll to hit the rear arc of the mek randomly - private static final int REAR_ARC_HIT_CHANCE = 11; - private static final Set criticalSystems = Set.of(Mek.SYSTEM_ENGINE, Mek.SYSTEM_GYRO, Mek.SYSTEM_LIFE_SUPPORT, - Mek.SYSTEM_SENSORS, Mek.SYSTEM_COCKPIT); - private static final Set criticalLocations = Set.of(Mek.LOC_CT, Mek.LOC_HEAD, Mek.LOC_LT, Mek.LOC_RT); - - @Override - public int getRandomHitLocation() { - var entity = entity(); - WeightedDoubleMap weightedDoubleMap = new WeightedDoubleMap<>(); - for (int i = 0; i < entity.locations(); i++) { - if (entity.getOArmor(i) <= 0) { - continue; - } - var locationIsNotBlownOff = !entity.isLocationBlownOff(i); - var locationIsNotDestroyed = entity.getInternal(i) > 0; - var locationIsNotHead = Mek.LOC_HEAD != i; - var weight = locationIsNotHead ? 6.0 : 1.0; - if (locationIsNotBlownOff && locationIsNotDestroyed) { - weightedDoubleMap.add(weight, i); - } - } - return weightedDoubleMap.randomOptionalItem().orElse(-1); - } - - @Override - public HitDetails setupHitDetails(HitData hit, int dmg) { - int currentArmorValue = entity.getArmor(hit); - int setArmorValueTo = currentArmorValue - dmg; - boolean hitInternal = setArmorValueTo < 0; - boolean isHeadHit = (entity.getCockpitType() != Mek.COCKPIT_TORSO_MOUNTED) && (hit.getLocation() == Mek.LOC_HEAD); - int hitCrew = isHeadHit || hitInternal ? 1 : 0; - - return new HitDetails(hit, dmg, setArmorValueTo, hitInternal, hitCrew); - } - - @Override - public HitData getHitData(int hitLocation) { - boolean hitRearArc = Compute.rollD6(2).isTargetRollSuccess(REAR_ARC_HIT_CHANCE); - return getHitData(hitLocation, hitRearArc); - } - - /** - * Returns the hit data for the given location, considering if the hit is on the rear arc or not. - * @param hitLocation the location of the hit - * @param hitRearArc if the hit is on the rear arc - * @return the hit data - */ - public HitData getHitData(int hitLocation, boolean hitRearArc) { - return new HitData(hitLocation, hitRearArc && hitLocationHasRear(hitLocation), HitData.EFFECT_NONE); - } - - private boolean hitLocationHasRear(int hitLocation) { - return switch(hitLocation) { - case Mek.LOC_CT, Mek.LOC_LT, Mek.LOC_RT -> true; - default -> false; - }; - } - - - @Override - public HitDetails destroyLocation(HitDetails hitDetails) { - var entity = entity(); - var hit = hitDetails.hit(); - entity.destroyLocation(hit.getLocation()); - if (hit.getLocation() == Mek.LOC_CT || hit.getLocation() == Mek.LOC_HEAD) { - entity.setDestroyed(true); - if (hit.getLocation() == Mek.LOC_HEAD) { - if (entity.isEjectionPossible()) { - var toHit = new ToHitData(); - toHit.addModifier(4, "Ejection"); - if (rollD6(2).isTargetRollSuccess(toHit)) { - hitDetails = hitDetails.withCrewDamage(2); - } else { - hitDetails = hitDetails.killsCrew(); - } - } else { - hitDetails = hitDetails.killsCrew(); - } - DamageApplier.setEntityDestroyed(entity); - } else { - entity.setSalvage(false); - entity.setRemovalCondition(IEntityRemovalConditions.REMOVE_DEVASTATED); - logger.trace("[{}] Mek devastated!", entity.getDisplayName()); - } - } - if (hit.getLocation() == Mek.LOC_RLEG || hit.getLocation() == Mek.LOC_LLEG) { - // leg destroyed causes a fall which damages the pilot - hitDetails.withCrewDamage(1); - } - return hitDetails; - } - - @Override - public void applyDamageToEquipments(HitData hit) { - var entity = entity(); - var criticalSlots = entity.getCriticalSlots(hit.getLocation()); - Collections.shuffle(criticalSlots); - if (entityMustSurvive() && criticalLocations.contains(hit.getLocation())) { - criticalSlots = criticalSlots.stream().filter(Objects::nonNull) - .filter(slot -> !(slot.getType() == TYPE_SYSTEM && criticalSystems.contains(slot.getIndex()))) - .toList(); - } - for (CriticalSlot slot : criticalSlots) { - if (slot != null && slot.isHittable() && !slot.isHit() && !slot.isDestroyed()) { - slot.setHit(true); - slot.setDestroyed(true); - logger.trace("[{}] Slot {} destroyed", entity.getDisplayName(), slot.getIndex()); - break; - } - } - // TODO: check if damage on ammo - // TODO: check if damage on life support - // TODO: check if damage on cockpit - } - - @Override - public void destroyLocationAfterEjection(){ - var entity = entity(); - entity.destroyLocation(Mek.LOC_HEAD); - } - - @Override - public HitDetails damageInternals(HitDetails hitDetails) { - HitData hit = hitDetails.hit(); - var entity = entity(); - int currentInternalValue = entity.getInternal(hit); - int newInternalValue = Math.max(currentInternalValue + hitDetails.setArmorValueTo(), 0); - entity.setArmor(0, hit); - if (entityMustSurvive() && !canLoseLocation(hit)) { - newInternalValue = Math.max(newInternalValue, Compute.d6()); - } - entity.setInternal(newInternalValue, hit); - applyDamageToEquipments(hit); - if (newInternalValue == 0) { - hitDetails = destroyLocation(hitDetails); - } - return hitDetails; - } - - private boolean canLoseLocation(HitData hitData) { - var location = hitData.getLocation(); - if (!entityMustSurvive()) { - return true; - } - - if (location == Mek.LOC_CT || location == Mek.LOC_HEAD) { - return false; - } - if (location == Mek.LOC_LT || location == Mek.LOC_RT) { - if (entity.getCritical(location, Mek.SYSTEM_ENGINE) == null) { - return true; - } - } - if (location == Mek.LOC_LLEG || location == Mek.LOC_RLEG) { - return !entity.isLocationBlownOff(Mek.LOC_LLEG) && !entity.isLocationBlownOff(Mek.LOC_RLEG); - } - - return true; - } -} diff --git a/MekHQ/src/mekhq/campaign/unit/damage/SimpleDamageApplier.java b/MekHQ/src/mekhq/campaign/unit/damage/SimpleDamageApplier.java deleted file mode 100644 index 506c2ea16e..0000000000 --- a/MekHQ/src/mekhq/campaign/unit/damage/SimpleDamageApplier.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. - * - * This file is part of MekHQ. - * - * MekHQ is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * MekHQ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with MekHQ. If not, see . - */ - -package mekhq.campaign.unit.damage; - - -import megamek.common.Entity; - -/** - * @author Luana Coppio - */ -public record SimpleDamageApplier(Entity entity, boolean crewMustSurvive, boolean entityMustSurvive) - implements DamageApplier {}