From 4ffbb2774240a95ef5f7e043260003fdf7c1f726 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 23 Nov 2021 00:03:57 -0600 Subject: [PATCH 1/3] Bug fixes and AI enhancements for transports. 1. Fixes the method for setting colony population to ensure enforcement of the planet's current maximum size. Addresses a number of possible bugs (some observed) as per impacts to: a) Colony.capturedByTransport: Eliminates ability to massively overpopulate a captured colony, *and then potentially forward-transport excess colonists, including in a cascading attack sequence*. Since the game does not cap the maximum number of transports than can land in a ground attack the way MOO1 did, this can result in the ability for a single colony to send a quantify of transports far in excess of any amount that would be allowable even for the best possible maximally terraformed gaia planet. b) Colony.capturedOrion: (same considerations as capturedByTransport). c) Empire.takeAbandonedSystem: It appears that it was previously possible to assign an arbitrarily large population when acquiring an abandoned colony. d) ColonyEcology.commitTurn: (Theoretical) Capping growth to the planet currentSize limit appears to rely entirely on the prior population growth and purchase calculations being done (i.e. constrained) correctly. Based on IDE-assisted search for uses of setPopulation, there is no turn-processing logic for fallback enforcement of the currentSize limit in the event of errors in the calculations. No indications have been encountered of this working incorrectly at this time, but still... brittle. i) NOTE: The check for setting the populationGrowthCompleted flag also appears possibly flawed as it seems to check against the maximum possible size when all terraforming available to the empire has been applied, as opposed to the amount that has *actually* been applied to the planet. Unclear as to what the impact might be, and thus whether this behaves as intended for whatever consumes this flag. - Also applied by incorporation to the adjustPopulation method, with corresponding updates to use the adjust method instead of set when appropriate. - Provided a return value to indicate transports lost due to exceeding the colony max size, and left placeholders for what should probably be notifications to the player when this happens. Not yet sure how to provide the translations for the prospective notification though (Google translate?). 2. Fixes scheduleTransportsToSystem to actually constrain to the maximum amount allowed. (The computed value "xPop" was not actually being used. But it was what was logged!) 3. Fixes logic for determining that the attacker has a fleet in control of the target system, when not using combat transporters. The previous logic was only checking that the empire had a fleet "at" the target system. Unfortunately this led to a (observed) situation where an opponent repeatedly sent large numbers of transports to their certain death, because it would find that a fleet it had sent was at the system. But unfortanately for them, a fleet that has retreated is still considered "at" the system in a deployed (not orbiting) state -- and they kept retreating their fleets because they were not sending anywhere near the force necessary to displace the fleet I was holding in orbit at the system. 4. When relying on combat transporters, adjust force size for the effect of the target having subspace interdictors if indicated by available spy intelligence. --- src/rotp/model/ai/base/AIGeneral.java | 11 ++-- src/rotp/model/colony/Colony.java | 76 +++++++++++++++++---------- 2 files changed, 56 insertions(+), 31 deletions(-) diff --git a/src/rotp/model/ai/base/AIGeneral.java b/src/rotp/model/ai/base/AIGeneral.java index 8c6997952..a6196d6aa 100644 --- a/src/rotp/model/ai/base/AIGeneral.java +++ b/src/rotp/model/ai/base/AIGeneral.java @@ -285,10 +285,15 @@ public void orderInvasionFleet(EmpireView v, StarSystem sys, float enemyFleetSiz EmpireView ev = empire.viewForEmpire(empire.sv.empId(sysId)); float targetTech = ev.spies().tech().avgTechLevel(); // modnar: target tech level - if (empire.sv.hasFleetForEmpire(sys.id, empire)) + if (empire.sv.orbitingFleet(sys.id) != null) launchGroundTroops(v, sys, mult); - else if (empire.combatTransportPct() > 0) - launchGroundTroops(v, sys, mult/empire.combatTransportPct()); + else if (empire.combatTransportPct() > 0) { + float transPct = empire.combatTransportPct(); + if (ev.spies().tech().subspaceInterdiction()) { + transPct /= 2; + } + launchGroundTroops(v, sys, mult / transPct); + } float baseBCPresent = empire.sv.bases(sys.id)*empire.tech().newMissileBaseCost(); float bcMultiplier = 1 + (empire.sv.hostilityLevel(sys.id)); diff --git a/src/rotp/model/colony/Colony.java b/src/rotp/model/colony/Colony.java index 82c288ee1..94e5f8a8e 100644 --- a/src/rotp/model/colony/Colony.java +++ b/src/rotp/model/colony/Colony.java @@ -186,7 +186,7 @@ private void init() { for (int i = 0; i < spending.length; i++) spending[i].init(this); - setPopulation(2); + population(2); shipyard().goToNextDesign(); defense().updateMissileBase(); defense().maxBases(empire().defaultMaxBases()); @@ -257,18 +257,32 @@ public String printString() { + (float) Math.round(defense().shield() * 100) / 100; } - public int displayPopulation() { return population < 1 ? (int) Math.ceil(population) : (int) population; } - public float population() { return population; } - public void setPopulation(float pop) { population = pop; } - public void adjustPopulation(float pop) { population += pop; } - public int rebels() { return rebels; } - public void rebels(int i) { rebels = i; } - public int deltaPopulation() { return (int) population - (int) previousPopulation - (int) inTransport(); } - public boolean destroyed() { return population <= 0; } - public boolean inRebellion() { return rebellion && (rebels > 0); } - public float rebellionPct() { return rebels / population(); } - public boolean hasOrders() { return !orders.isEmpty(); } - + public int displayPopulation() { return population < 1 ? (int) Math.ceil(population) : (int) population; } + public int rebels() { return rebels; } + public void rebels(int i) { rebels = i; } + public int deltaPopulation() { return (int) population - (int) previousPopulation - (int) inTransport(); } + public boolean destroyed() { return population <= 0; } + public boolean inRebellion() { return rebellion && (rebels > 0); } + public float rebellionPct() { return rebels / population(); } + public boolean hasOrders() { return !orders.isEmpty(); } + + public float population() { return population; } + private void population(float pop) { population = pop; } + public int setPopulation(float pop) { + float newPop = population() + pop; + float currentMaxPop = planet().currentSize(); + float lost = newPop - currentMaxPop; + if (lost > 0) { + population(currentMaxPop); + return (int) lost; + } + population(newPop); + return 0; + } + public float adjustPopulation(float pop) { + return setPopulation(population() + pop); + } + public boolean isDeveloped() { return defense().isCompleted() && industry().isCompleted() && ecology().isCompleted(); } @@ -367,7 +381,7 @@ public void removeColonyOrder(Colony.Orders order) { } public void setHomeworldValues() { - setPopulation(50); + population(50); previousPopulation = population(); industry().factories(30); industry().previousFactories(30); @@ -904,7 +918,7 @@ public void launchTransports() { abandon(); return; } - setPopulation(population() - transport().size()); + adjustPopulation(-transport().size()); transport = new Transport(starSystem()); if (empire.isPlayerControlled()) starSystem().transportSprite().launch(); @@ -927,7 +941,7 @@ public void scheduleTransportsToSystem(StarSystem dest, int pop) { if ((dest == starSystem()) || (xPop == 0)) clearTransport(); else { - transport().size(pop); + transport().size(xPop); transport().setDest(dest); transport().setDefaultTravelSpeed(); } @@ -937,7 +951,9 @@ public void scheduleTransportsToSystem(StarSystem dest, int pop) { empire.setVisibleShips(); } public void acceptTransport(Transport t) { - setPopulation(min(planet.currentSize(), (population() + t.size()))); + if (adjustPopulation(t.size()) > 0) { + // TODO: Some transports lost because max population was reached. Ought to be an alert? + } log("Accepting ", str(t.size()), " transports at: ", starSystem().name(), ". New pop:", fmt(population(), 2)); t.size(0); } @@ -948,7 +964,7 @@ public void resistTransportWithRebels(Transport tr) { log(str(rebels), " ", empire().raceName(), " rebels at ", starSystem().name(), " resisting ", str(tr.size()), " ", tr.empire().raceName(), " transports"); captives = population() - rebels; - setPopulation(rebels); + population(rebels); if (population() > 0) { if (empire.isPlayerControlled() || tr.empire().isPlayerControlled()) @@ -958,7 +974,7 @@ public void resistTransportWithRebels(Transport tr) { } rebels = (int) population(); - setPopulation(rebels + captives); + population(rebels + captives); captives = 0; // are there rebels left? @@ -1091,10 +1107,10 @@ public boolean singleCombatAgainstTransports(Transport tr) { if (attRoll < defRoll) tr.size(tr.size() - 1); else - setPopulation(population() - 1); + adjustPopulation(-1); if (population() <= 0) - setPopulation(0); + population(0); // true: attacker defeated // false: defender defeated @@ -1138,7 +1154,9 @@ private void capturedByTransport(Transport tr) { } } - setPopulation(tr.size()); + if ((setPopulation(tr.size()) > 0) && (tr.empire().isPlayerControlled())) { + // TODO: Excess transports lost -- ought to be an alert? + } tr.size(0); shipyard().capturedBy(tr.empire()); industry().capturedBy(tr.empire()); @@ -1167,7 +1185,9 @@ private void capturedByTransport(Transport tr) { loser.goExtinct(); } public void capturedOrion(Transport tr) { - setPopulation(tr.size()); + if ((setPopulation(tr.size()) > 0) && (tr.empire().isPlayerControlled())) { + // TODO: Excess transports lost -- ought to be an alert? + } tr.size(0); industry().capturedBy(tr.empire()); defense().capturedBy(tr.empire()); @@ -1195,7 +1215,7 @@ public void takeTargetedCollateralDamage(float damage) { float newPop = max(0, population() - (damage / TARGETED_DAMAGE_FOR_POPLOSS)); float newFact = max(0, industry().factories() - (damage / TARGETED_DAMAGE_FOR_FACTLOSS)); - setPopulation(newPop); + population(newPop); industry().factories(newFact); if (population() <= 0) @@ -1205,7 +1225,7 @@ public void takeUntargetedCollateralDamage(float damage) { float newPop = max(0, population() - (damage / UNTARGETED_DAMAGE_FOR_POPLOSS)); float newFact = max(0, industry().factories() - (damage / UNTARGETED_DAMAGE_FOR_FACTLOSS)); - setPopulation(newPop); + population(newPop); industry().factories(newFact); if (population() <= 0) @@ -1214,7 +1234,7 @@ public void takeUntargetedCollateralDamage(float damage) { public void takeBioweaponDamage(float damage) { float popLost = max(0, damage - tech().antidoteLevel()); - setPopulation(max(0, population() - popLost)); + population(max(0, population() - popLost)); float newWaste = popLost * 10; ecology().addWaste(newWaste); @@ -1231,7 +1251,7 @@ public void abandon() { sys.addEvent(new SystemAbandonedEvent(empire.id)); sys.abandoned(true); - setPopulation(0); + population(0); rebels = 0; captives = 0; rebellion = false; @@ -1260,7 +1280,7 @@ public void destroy() { StarSystem sys = starSystem(); sys.addEvent(new SystemDestroyedEvent(empire.lastAttacker())); - setPopulation(0); + population(0); rebels = 0; captives = 0; rebellion = false; From 0c78c7e78f68b1a8ba99b0d6f9f3eb332d5af9f1 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 23 Nov 2021 00:12:11 -0600 Subject: [PATCH 2/3] Check for player-control at one more spot for possible new alert. --- src/rotp/model/colony/Colony.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rotp/model/colony/Colony.java b/src/rotp/model/colony/Colony.java index 94e5f8a8e..9cd645953 100644 --- a/src/rotp/model/colony/Colony.java +++ b/src/rotp/model/colony/Colony.java @@ -951,7 +951,7 @@ public void scheduleTransportsToSystem(StarSystem dest, int pop) { empire.setVisibleShips(); } public void acceptTransport(Transport t) { - if (adjustPopulation(t.size()) > 0) { + if ((adjustPopulation(t.size()) > 0) && t.empire().isPlayerControlled()) { // TODO: Some transports lost because max population was reached. Ought to be an alert? } log("Accepting ", str(t.size()), " transports at: ", starSystem().name(), ". New pop:", fmt(population(), 2)); From e95b3c654beacc60cab404557bcd2452dd23becd Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 23 Nov 2021 00:21:33 -0600 Subject: [PATCH 3/3] Fix accidental combination of adjustPopulation logic into setPopulation. --- src/rotp/model/colony/Colony.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rotp/model/colony/Colony.java b/src/rotp/model/colony/Colony.java index 9cd645953..dccde7008 100644 --- a/src/rotp/model/colony/Colony.java +++ b/src/rotp/model/colony/Colony.java @@ -269,14 +269,13 @@ public String printString() { public float population() { return population; } private void population(float pop) { population = pop; } public int setPopulation(float pop) { - float newPop = population() + pop; float currentMaxPop = planet().currentSize(); - float lost = newPop - currentMaxPop; + float lost = pop - currentMaxPop; if (lost > 0) { population(currentMaxPop); return (int) lost; } - population(newPop); + population(pop); return 0; } public float adjustPopulation(float pop) {