Civ II Combat Statistics

Prof. Garfield

Deity
Supporter
Joined
Mar 6, 2004
Messages
4,398
Location
Ontario
I wrote a little event file to check the combat values of units.

Instructions:
1. Duplicate the scenario folder (this will work for any scenario).
2. Extract the attached events.lua into the new scenario folder.
3. Load a game (choosing to enable lua events).
4. Initiate combat. You will see the final combat statistics (these are multiplied by 8 compared to what you would expect). Combat won't take place, and the unit will be restored to full movement points.

I discovered that a unit with the submarine flag will only defend with 1 firepower, instead of their regular firepower.

I uploaded this to the latest PBEM thread, since I wrote it to inspect combat values in that scenario, but it is best to discuss findings in a separate thread.
 

Attachments

I've tested it with my Reformation scenario and also noticed, that the Dhow unit with submarine flag defends with 1 firepower instead of 4. Could it be a bug from the game itself?

upload_2021-10-20_17-37-8-png.612243

upload_2021-10-20_17-37-33-png.612244

When I disable the flag, the unit has it's actual 4 firepower
upload_2021-10-20_17-39-43-png.612245

This is consistent with my observations. This is probably an "undocumented feature" rather than a "bug." I think there are other combat situations, like shore bombardment, where the firepower of one or both units is changed. Someone probably figured that it's hard to fire back when you're being depth charged, so the firepower of the submarine should be reduced to 1.

It's good to know because I was thinking about to give most naval units the submarine flag for avoiding coastal bombardements.

Lua events can now prevent the combat (just as is done in this code) without the need to resort to submarine flags. It is also possible to 'give back' the firepower in the event. This stuff just hasn't been worked on much yet.
 
I've tested it with my Reformation scenario and also noticed, that the Dhow unit with submarine flag defends with 1 firepower instead of 4. Could it be a bug from the game itself?
When I disable the flag, the unit has it's actual 4 firepower
This is consistent with my observations. This is probably an "undocumented feature" rather than a "bug." I think there are other combat situations, like shore bombardment, where the firepower of one or both units is changed.
Yes, I agree that this is intentional (just undocumented).

This stuff just hasn't been worked on much yet.
Actually I have worked on it quite a bit recently, and I'm in the process of organizing the results in order to post them. (So please don't invest a lot of time in it unnecessarily!) In the meantime, if you have any specific questions, please let me know and I'll do my best to answer them.

My first intention was to write a post that served as an update to "The Complete Civilization II Combat Guide v1.1" with any necessary corrections, and then sharing additional combat rules which aren't mentioned there. Secondly, I'd also like to post Lua code that would basically recreate the game's combat calculations, which could serve as a starting point (or at least a valuable resource) for anyone seeking to modify them.

It's good to know because I was thinking about to give most naval units the submarine flag for avoiding coastal bombardements.
I actually did this in my Medieval Millennium mod, and it worked out pretty well. Fortunately none of my naval units have more than 1 firepower, so they weren't affected by the firepower reduction that you noticed. The only thing I'm not sure of is whether this affects the AI's desire to build those units, or how it deploys them within the game. In some of my Medieval Millennium games, it seems like the AI fills the seas with large numbers of ships, but rarely uses them to transport units.

Lua events can now prevent the combat (just as is done in this code) without the need to resort to submarine flags.
Yes, now that this is possible in TOTPP 0.16, I agree it would probably be a better approach to preventing shore bombardment. (At the time I released MM, TOTPP 0.15.1 was the latest version and that didn't provide this capability.) I'll add it to the list of improvements I'd like to make in the next Medieval Millennium update.
 
Actually I have worked on it quite a bit recently, and I'm in the process of organizing the results in order to post them. (So please don't invest a lot of time in it unnecessarily!) In the meantime, if you have any specific questions, please let me know and I'll do my best to answer them.

My first intention was to write a post that served as an update to "The Complete Civilization II Combat Guide v1.1" with any necessary corrections, and then sharing additional combat rules which aren't mentioned there. Secondly, I'd also like to post Lua code that would basically recreate the game's combat calculations, which could serve as a starting point (or at least a valuable resource) for anyone seeking to modify them.

Thanks for letting me know! Creating a combat power calculator is something that I've done a bit of work on, but not too much yet. You mention modification as one application of having a combat calculator. Something else I'd like to try is a system where you can override the selected defender of a tile. Basically, you could "select" a different unit to defend the tile, and manually assign damage to either the attacker or selected defender, perhaps showing a text box to advise the player of what is happening. That would be almost as good as an event that explicitly allowed selecting the unit to defend a tile.

I'll attach the work I've done, just in case there is something of value to you. Note: in the readRules.lua file (which parses rules.txt files for easier use), there is a call to gen.getScenarioDirectory(). The general library takes this information from events.lua, rather than using the debug tools itself, so you will have to re-write that line if you're not using a recent version of my template.
 

Attachments

Lua events can now prevent the combat (just as is done in this code) without the need to resort to submarine flags. It is also possible to 'give back' the firepower in the event. This stuff just hasn't been worked on much yet.

This sounds interesting. So I can use naval units without using the submarine flag and they can't do any shore bombardments?

Do you have an example for a code like this which I can use?
 
I actually did this in my Medieval Millennium mod, and it worked out pretty well. Fortunately none of my naval units have more than 1 firepower, so they weren't affected by the firepower reduction that you noticed. The only thing I'm not sure of is whether this affects the AI's desire to build those units, or how it deploys them within the game. In some of my Medieval Millennium games, it seems like the AI fills the seas with large numbers of ships, but rarely uses them to transport units.

Tootall played my scenario and in his savegame the AI builded a lot of naval units too, France mostly Galleys and England Caravels and the Ottomans Galleys and Galleasses. At least England used them as transport ships because they conquered some cities in North America. And they didn't get them via events.
 
This sounds interesting. So I can use naval units without using the submarine flag and they can't do any shore bombardments?

Do you have an example for a code like this which I can use?

This is the current contents of initiateCombat.lua:

Code:
local onInitiateCombat = {}
function onInitiateCombat.makeCoroutine(attacker,defender,attackerDie,attackerPower,defenderDie,defenderPower)
    local maxCombatRounds = math.huge -- If you want to limit combat to a specific number of
                                        -- turns, set this variable
    return coroutine.create(function()
        local round = 0
        while(round < maxCombatRounds and attacker.hitpoints >0 and defender.hitpoints > 0) do
            if false then
                -- If the coroutine yields true as its first value,
                -- the game's default combat resolution is skipped for that round
                -- and the designer is responsible for updating damage.
                -- The second value yielded is either the attacker or the defender,
                -- this is used to render animations etc.
                -- In this case the coroutine resumes without any values.
                coroutine.yield(true,defender)
            else
                --If the coroutine yields false as its first value,
                --the game runs its default combat algorithm. The designer
                --can additionally yield modified values for attackerDie,
                --attackerPower, defenderDie and defenderPower (in this order)
                --which will be used by the game for that round.
                local newAttackerDie = nil
                local newAttackerFirepower = nil
                local newDefenderDie = nil
                local newDefenderFirepower = nil
                local result = coroutine.yield(false,newAttackerDie,newAttackerFirepower,newDefenderDie,newDefenderFirepower)
                --In this case the coroutine resumes with the result of the round,
                --a table containing four values:
                    -- winner, this is either attacker or defender.
                    -- attackerRoll, the result of the attacker's die roll
                    -- defenderRoll, the result of the defender's die roll
                    -- reroll, true if a reroll happened. This can happen only
                         -- if the attacker is tribe 0, the defender is a unit
                         -- guarding a city, and the city is the capital or
                         -- the tribe has less than 8 cities in total and
                         -- the attacker's die roll is higher than the
                         -- defender's. A reroll can happen at most once.
            end
            round = round+1
        end
        -- once we get here, combat stops
    end)
end
return onInitiateCombat

The easiest way to achieve this should be to modify this line:
Code:
    local maxCombatRounds = math.huge -- If you want to limit combat to a specific number of
                                        -- turns, set this variable

We'll set maxCombatRounds to 0 if the unit isn't supposed to attack. (untested code follows)

Code:
local maxCombatRounds = nil
if attacker.type.domain == 2 and defender.location.baseTerrain.type ~= 10 then
    maxCombatRounds = 0
else
    maxCombatRounds = math.huge
end
The if statement checks if the attacker's type is a sea unit, and the defender's location is on land, and, if so, stops combat from happening. It occurs to me that the AI might not handle this well and simply keep trying to attack the target (I believe this would waste a movement each attempt), so some more thought may be required.
 
My first intention was to write a post that served as an update to "The Complete Civilization II Combat Guide v1.1" with any necessary corrections, and then sharing additional combat rules which aren't mentioned there. Secondly, I'd also like to post Lua code that would basically recreate the game's combat calculations, which could serve as a starting point (or at least a valuable resource) for anyone seeking to modify them.
I just created a new thread with this combat information in the General Discussions area -- since this content is relevant to the base game and not just scenarios, I felt that was a better fit than appending it to this thread in the Scenario League area. Comments and feedback are welcome there.
 
Last edited:
Lua events can now prevent the combat (just as is done in this code) without the need to resort to submarine flags.
Yes, now that this is possible in TOTPP 0.16, I agree it would probably be a better approach to preventing shore bombardment. (At the time I released MM, TOTPP 0.15.1 was the latest version and that didn't provide this capability.) I'll add it to the list of improvements I'd like to make in the next Medieval Millennium update.
I tried to implement this into Medieval Millennium tonight, writing code to prevent shore bombardment without resorting to giving all ships the submarine flag, but unfortunately ran into a roadblock.

The first issue is that even when I prevent the combat with coroutine.yield(true, defender) there is still attack animation that takes place -- even though no damage is taken by either unit, it looks like the defender lost a round. And it's always the defender, too: even if I use coroutine.yield(true, attacker) the animation still appears on top of the defending unit for some reason. But overall, this is a minor concern -- a visual distraction, but not really detrimental to gameplay.

Second, as you noted in the code of your combat-value-firepower-inspector.zip in the first post above, the attempted attack increments attacker.moveSpent, so this needs to be subtracted in the event code in order to leave the attacker "unchanged". Easy enough, done.

But third, and here's where I'm stuck: the attempted attack also increments "attacks made this turn" for the unit, and that's causing a problem due to the Rules.txt settings I have in the @ ATTACKS section. In MM, I limit all unit types to at most 1 attack per turn. So if a ship attempts shore bombardment and my event blocks the combat, the ship is now prevented (for the remainder of that turn) from making a different valid attack on an opponent's ship at sea.

This made me realize (and I'm going to tag @TheNamelessOne here) that it would be great if unittype.attacksPerTurn could be added as a new get/set field, reflecting the contents of the @ ATTACKS section, and also unit.attacksThisTurn could be added as a new get/set field to give visibility to this counter for each unit. If I had the latter, then I could simply reset that to 0 after the attempted shore bombardment and I think I'd be in business.

Without that, the only thing I can imagine doing is actually deleting the attacking unit entirely and immediately creating a perfect copy of it on the same tile, except that the copy wouldn't have attempted an attack this turn -- so it would be free to conduct a valid attack on another ship. But that also requires transferring any units onboard the first ship to its replacement. And at this point I'm thinking that maybe leaving the submarine flag in place is the simpler and better option.

I haven't gotten far enough yet to tell whether the AI will get trapped in an infinite loop of attempting shore bombardments that are continuously blocked. I hope not, but that's another factor that inclines me to think maybe the submarine flag is preferable.

I'm open to any feedback or suggestions!
 
Last edited:
But third, and here's where I'm stuck: the attempted attack also increments "attacks made this turn" for the unit, and that's causing a problem due to the Rules.txt settings I have in the @ ATTACKS section. In MM, I limit all unit types to at most 1 attack per turn. So if a ship attempts shore bombardment and my event blocks the combat, the ship is now prevented (for the remainder of that turn) from making a different valid attack on an opponent's ship at sea.

You may also have to reset the 'moved' flag for the unit (the flag that checks if a unit has moved on the last turn in order to determine healing).

Code:
--gen.isMoved(unit)-->boolean
--gen.setMoved(unit)-->void
--gen.clearMoved(unit)-->void

Without that, the only thing I can imagine doing is actually deleting the attacking unit entirely and immediately creating a perfect copy of it on the same tile, except that the copy wouldn't have attempted an attack this turn -- so it would be free to conduct a valid attack on another ship. But that also requires transferring any units onboard the first ship to its replacement. And at this point I'm thinking that maybe leaving the submarine flag in place is the simpler and better option.

I agree it is looking like the submarine flag will be easier. I'm also pretty sure you can't set the carriedBy attribute, so that would complicate things. It will be easier to simply correct the firepower of defensive submarine flagged units.
 
You may also have to reset the 'moved' flag for the unit (the flag that checks if a unit has moved on the last turn in order to determine healing).
Yeah, I actually tried that already, but it didn't help. Even with this bit reset and full movement points, the game "knows" the unit has already conducted an attack this turn, and won't allow it to attack again due to my limit of 1 attack per turn.

I agree it is looking like the submarine flag will be easier. I'm also pretty sure you can't set the carriedBy attribute, so that would complicate things. It will be easier to simply correct the firepower of defensive submarine flagged units.
unit.carriedBy was get only in TOTPP 0.15.1, but set capability was added in 0.16. So I think the transfer might work, but the whole idea just seems like a pretty extreme thing to be happening a lot in the game (because the AI is pretty aggressive with using shore bombardment at every opportunity). But perhaps I could try it anyway just to see how it goes.

My primary concern about using the submarine flag isn't the firepower issue, but rather the degree to which it affects AI usage of the unit. In the base game, subs can't carry land units and are role 2 (naval superiority). But all Medieval Millennium ships can carry land units, and some are role 4 (sea transport) instead of role 2. Does the AI build and use them based on their capacity and role, or does the sub flag itself affect usage -- perhaps overriding those other two factors? It feels like I see lots of AI ships "patrolling" and rarely transporting units -- although I know that the AI has always been terrible at managing a naval invasion, so perhaps this behavior is only to be expected and has nothing to do with the sub flag. Plus, letting the AI build ships that it thinks can perform shore bombardment, but then blocking that via Lua, might throw the AI strategy routines even more off-kilter. :undecide:
 
Last edited:
unit.carriedBy was get only in TOTPP 0.15.1, but set capability was added in 0.16.

That's good to know.

My primary concern about using the submarine flag isn't the firepower issue, but rather the degree to which it affects AI usage of the unit. In the base game, subs can't carry land units and are role 2 (naval superiority). But all Medieval Millennium ships can carry land units, and some are role 4 (sea transport) instead of role 2. Does the AI build and use them based on their capacity and role, or does the sub flag itself affect usage -- perhaps overriding those other two factors? It feels like I see lots of AI ships "patrolling" and rarely transporting units -- although I know that the AI has always been terrible at managing a naval invasion, so perhaps this behavior is only to be expected and has nothing to do with the sub flag. Plus, letting the AI build ships that it thinks can perform shore bombardment, but then blocking that via Lua, might throw the AI strategy routines even more off-kilter. :undecide:

We might be getting to the point where we have to start writing code to help the AI. Suppose for the sake of argument that we know that giving the sub flag impedes the AI's ability to use a ship as a transport. To fix this, we take away the sub flag, and find a way to keep the ship from bombarding land. It is looking like this code will be somewhat convoluted anyway, and we are still left with the incompetent sea transport AI anyway.

If, instead, we simply try to help the AI, we might actually get a lot more value for the code. Consider this outline for sea transport code:

1. Analyse game to determine if a tribe should attempt an overseas venture.

2. Choose some units from the tribe to participate in this venture.

3. Save details of the venture in the state table, perhaps checking each turn that it still makes sense.

4. Each turn, if a venture exists, give goto orders to all involved units to travel to a port of embarkation. Any unit already in port will have its movement spent.

5. When all units are in port, set ground unit order to sleep and give sea units a goto order to the destination (or, perhaps, some waypoint). Trust the AI to unload when it reaches the destination.

This doesn't seem like it would be that hard to do, at least to try for a proof of concept. It wouldn't be up to human player capacity, but it might be better than what the AI already does.
 
We might be getting to the point where we have to start writing code to help the AI.
True, and in fact I already have MM code which does this in other areas -- building city improvements is one good example, where the way I repurposed improvements and defined city specialists left the AI at too much of a disadvantage.

Suppose for the sake of argument that we know that giving the sub flag impedes the AI's ability to use a ship as a transport. To fix this, we take away the sub flag, and find a way to keep the ship from bombarding land. It is looking like this code will be somewhat convoluted anyway, and we are still left with the incompetent sea transport AI anyway.
Completely agree.

If, instead, we simply try to help the AI, we might actually get a lot more value for the code. Consider this outline for sea transport code:

1. Analyse game to determine if a tribe should attempt an overseas venture
A great start, but... my initial reaction is that's a major project all on its own, and perhaps especially within the context of Medieval Millennium. In a wartime scenario on a known map where you're trying to nudge the AI down a more-or-less historical path, this seems somewhat reasonable. In MM, played on a new random map each time, it feels like I'd have to start by just getting Lua to understand the spatial arrangement of the world, the relative geographical position of each tribe, the possibilities for new settlements, the likelihood of successful conquests, ... the list goes on.

This doesn't seem like it would be that hard to do, at least to try for a proof of concept. It wouldn't be up to human player capacity, but it might be better than what the AI already does.
Since the bar is pretty low :lol: perhaps you're right. But when it comes to determining the cost/benefit of a seaborne venture, it might just be more than I'm willing to tackle within MM. On the other hand, there probably are some less grandiose things I could tweak to make the AI a little more competent -- focusing less on high-level strategic goals, and more on tactical actions. For example, if an AI ship departs from a city with unused carrying capacity, and that city contains units it can spare (by some to-be-determined formula), then teleport extra units onto the ship. This doesn't really give the AI a roadmap for what to do, but it might make it a little more effective in things it's already trying to do.

Thanks for the feedback and ideas... even if I'm hesitant to jump all the way into your proposal, I think you've convinced me that leaving the sub flag in place is the way to go, and I'll focus my Lua efforts on other ways of making improvements.
 
A great start, but... my initial reaction is that's a major project all on its own, and perhaps especially within the context of Medieval Millennium. In a wartime scenario on a known map where you're trying to nudge the AI down a more-or-less historical path, this seems somewhat reasonable. In MM, played on a new random map each time, it feels like I'd have to start by just getting Lua to understand the spatial arrangement of the world, the relative geographical position of each tribe, the possibilities for new settlements, the likelihood of successful conquests, ... the list goes on.

I think you might be letting the perfect be the enemy of the good here. Try a simple model. Give each military unit a 'rating'. For each city, add up the total rating of all the units within, say, 10 squares of the city, for each tribe. If the calculating tribe has a rating at least 80% of the best tribe's rating, the city in question is 'defended,' and extra units can be put to other goals, for example shoring up military power of deficient cities. (Perhaps you could use leader personality to tweak thresholds.)

If all cities are 'defended', then your code could (not necessarily will) choose some strategic goal for the tribe. That might be to simply increase military forces (perhaps neighbours are powerful, but just not too close), perhaps building some siege equipment to try to attack cities, perhaps massing a force to take a particular city, perhaps sending an amphibious force somewhere. Rather than trying to choose the 'best' action, run a calculation to rule out stupid actions (like amphibious attack against an enemy with 3x your navy size), and choose a goal at random (maybe influenced by leader personality or something). I'm inclined to think that civs trying complicated things with some competence will make for a better experience even if they fail, especially if you establish a baseline of defence before they try something.
 
I think you might be letting the perfect be the enemy of the good here.
Well, maybe so. It certainly wouldn't be the first time I've been guilty of that! (Probably won't be the last either...)

But ironically, that's also kind of the reason why I'm hesitant to try this. I've made a lot of improvements already to MM, taking advantage of features in TOTPP 0.16 and 0.17, and at some point I'd like to release an updated version. So I'm trying really hard to limit "scope creep" and break the cycle of adding two new features to my "to do" list for every one that I cross off. Trying to improve the AI is a great idea, but any major efforts in that direction might have to be deferred, at least for now.
 
Back
Top Bottom