[BTS] DLL - Adding Turn-Based Feature for Units

Eusebio

Chieftain
Joined
Dec 8, 2020
Messages
21
This is the first time I'm really trying to mod the game trough the DLL, more specifically the modified DLL from Kmod, found here. In particular, what I'm trying to achieve for now is to simulate some sort of attrition feature, similar to that used in Rise of Nations (RoN). The basic idea would be:
-Add a new int variable for units to represent attrition. I think it should be independent from the unit's HP. This variable may change depending on the type of unit, techs researched and promotions. I'll try to add a Supply Wagon unit as well, just like RoN, which affects all of it's adjacent tiles.
-At the end of each turn, check every unit on the map and the plot where this unit is stationed. Given the ownership of the plot, the unit may lose one point of attrition. Specifically, this will happen if the unit is stationed in an neutral or 'no-rite-of-passage' territory. If, however, the unit is within one's own territory, one with rite-of-passage or an tile adjacent to a Supply Wagon unit, it's attrition points reset to the default value. In case the attrition count gets to 0, the unit is killed.

I believe this mechanic could make exploring the map or invading an enemy more challenging, opening opportunities for exploits (for example, targeting the enemy's supply wagon could cripple the invading army). Given that the whole checking-and-updating attrition should be done at the end of each turn, where should the place to call such function be?
 
Last edited:

<Nexus>

Traveler of the Multiverse
Joined
Jan 23, 2014
Messages
5,064
Location
In a constant brainstorm...
Check AND2 or CoM mods. They have a damage/turn feature depending on distance from city. It works rather well - I mean: it's not crippling the AI.

You can find the source code of the dll for CoM and you can find the whole mid in my signature.
 

f1rpo

plastics
Joined
May 22, 2014
Messages
1,422
Location
Germany
CvUnit::doTurn (CvUnit.cpp)

This gets executed for every unit at the start of its owner's turn, meaning e.g. that enemies get to attack a unit before it heals. Terrain damage and spies getting caught is also handled in doTurn – conceptually similar to your attrition effect.

The Charlemagne scenario that comes with BtS already has a UNIT_SUPPLY_TRAIN:
Pedia Text said:
In this scenario, units cannot heal at all when in enemy territory, unless accompanied by Supply Trains. Always be sure to bring along one or more trains when invading enemy lands. Note that Supply Trains are totally unarmed, so it's a good idea to provide a heavy military escort when sending one into dangerous territory.
Perhaps you could re-use the artwork. There's no custom AI code to re-use though – the scenario sets ENEMY_HEAL_RATE to 0 through XML (GlobalDefines) and gives its Supply Train a free Medic promotion, i.e. it relies on the standard AI behavior of trying to deploy some Medic-promoted units.

For the AND2 thing, I think GAMEOPTION_MOVEMENT_LIMITS would be the term to search for.
 

Eusebio

Chieftain
Joined
Dec 8, 2020
Messages
21
Thanks for the replies so far! I'll take a look the "Chronicles of Mankind" and "AND2" (I believe this one is A New Dawn?) to check the system <Nexus> talked about.
The Charlemagne mod really seems to do something close to what I want to achieve, though I'll test to see if it's enough of a replacement. In any case, having the artwork for the Supply Train will make things a lot easier indeed.
One more question that I've come to when trying to do this mod: I would like to present the current "attrition" value in the unit interface box (the window at the bottom left of the screen, which shows the Movement, Level, Experience, etc. of the unit once you select it). Are these kind of modifications to the interface done in the DLL or somewhere else?
 

f1rpo

plastics
Joined
May 22, 2014
Messages
1,422
Location
Germany
[...] and "AND2" (I believe this one is A New Dawn?)
Yes, "Rise of Mankind: A New Dawn 2" – difficult to abbreviate.
Are these kind of modifications to the interface done in the DLL or somewhere else?
In Python, specifically updateInfoPaneStrings in Screens\CvMainInterface.py; you may want to search for "getLevel" in there. Though most hover text (tooltips) - and also some other game text - is generated by CvGameTextMgr in the DLL (via CvDLLWidgetData).
 

Eusebio

Chieftain
Joined
Dec 8, 2020
Messages
21
Perhaps you could re-use the artwork. There's no custom AI code to re-use though – the scenario sets ENEMY_HEAL_RATE to 0 through XML (GlobalDefines) and gives its Supply Train a free Medic promotion, i.e. it relies on the standard AI behavior of trying to deploy some Medic-promoted units.

I was thinking about this scenario for a while. In order to keep my mod playable I would have to adapt the AI files about the existence of the new attrition feature I'm implementing, but I'm wondering if I can make the game rely on that standard AI behaviour by doing something similar to the Charlemagne scenario: the idea would be to change ENEMY_HEAL_RATE and NEUTRAL_HEAL_RATE both to 0, and giving the Supply Train it's Medic promotion; however, I would also add some sort of damage per turn on units outside of friendly territory, and not on a tile adjacent to a Supply Wagon. In this idea, the unit's hit-points would be directly affected by the attrition system instead of being somewhat independent as it was originally planned (which could lead to a somewhat bothersome need to keep healing units at a higher rate, but I will try to address it as well). Do you think adding this kind of changes to the Charlemagne concept (e.g. making even neutral territory dangerous) would require a change on the AI standard behaviour, or is it already able to learn how to deal in such a situation?
 

Eusebio

Chieftain
Joined
Dec 8, 2020
Messages
21
Well, I think it would change exploration greatly.
Part of the idea was supposed to be this, actualy. Making the world somewhat harder to explore thus making it more mysterious in a sense, kind of how the world looked like for people before globalization. I also had another idea involving the update of visibility of the map, which I could try to implement instead of (or in addition) this one. I just thought that the attrition mechanic in RoN was pretty interesting, and would like to try it in Civ IV.
As I said, my reasoning behind this different way of adding this feature was to avoid also having to deal with the AI scripting for now, though I'm sure I will have to deal with it eventually in the future. When you say these editings change exploration greatly, you think the standard AI is ready to deal with such changes, or I would need to teach the AI about this feature as well?
 

f1rpo

plastics
Joined
May 22, 2014
Messages
1,422
Location
Germany
I think this is pretty much the extent of the AI logic for deploying Medics:
Code:
void CvUnitAI::AI_counterMove()
{
   if (AI_guardCity(false, true, 1))
       return;
   if (getSameTileHeal() > 0 && !canAttack())
   {
       if (AI_shadow(UNITAI_ATTACK_CITY))
           return;
   }
   // ...
}
I.e. assuming that the Supply Train will be assigned the UNITAI_COUNTER AI type, it'll accompany a group of city attackers if there is no city to be guarded. Having taken a look at the K-Mod version of this function, however, there appears to be a bug: since the BtS expansion, AI_shadow only targets groups with loaded cargo, i.e. land units never get shadowed. [Edit: Sort of forgot that you're working with K-Mod, which fixes this bug. My test of the scenario was without K-Mod.]

I've run the Charlemagne scenario on AI Play for 100 turns; doesn't look like the AI ever sends Supply Trains into enemy lands. It does produce a few Supply Trains, but they just patrol or sit in cities. And the city AI (governor) counts a fixed value per free promotion, i.e. it won't check how many Medics are needed.

So, yeah, this is not going to work when Supply Trains are important. (Healing in enemy territory is usually a bad idea anyway and 150 production for a strength-1 Medic doesn't seem cheap either, so the Charlemagne AI arguably isn't missing out on much.) With the bug fixed, the AI might be able to cover its city attackers more often than not, but exploration units, patrols in foreign and neutral territory and small attack/ pillage stacks would still be on their own. There is some (simplistic) code for deciding whether to award a Medic promotion. That could at least make the AI more inclined to promote units with Medic that have taken attrition damage. (Not in Charlemagne though because the mod restricts the promotion to Supply Trains.)
 
Last edited:

Eusebio

Chieftain
Joined
Dec 8, 2020
Messages
21
I already asked this in another place, but I would like to know: do you guys suggest I start modding trough Python instead of the DLL? The reason I went right with C++ here is that the attrition feature would be something checked on every unit from every player, every turn; the kind of feature that's basically always happening in the game. Therefore, I thought using C++ would be better here performance-wise. But taking a look at the History Rewritten mod, pretty much eveything it adds is done trough XML and Python, if I understood correctly it's DLL is the K-Mod's version, no changes done to it at all.
 

f1rpo

plastics
Joined
May 22, 2014
Messages
1,422
Location
Germany
My understanding is that HR uses Python as much as possible for compatibility with MacOS; perhaps see this recent post by @Leoreth for more details on that (though there might be more exact info in the HR subforum somewhere; Xyth would obviously know best). And the DLL source that Xyth attached here does have some additions beyond K-Mod. Plenty of changes from Advanced Diplomacy, don't know what else.

I don't think that performance would necessarily be an issue if you went with a Python(-only) implementation, but Python may simply not get calls from the DLL at all the right points in time. E.g. CvUnit::doTurn does not make any Python call. I'm sure there's some way to update an attrition counter in Python once per turn, but doTurn really seems like the most appropriate and consistent place. Then, storing the attrition counter in a savegame – possible in Python (via CyUnit::setScriptData I think), but easier in the DLL.
 

pecheneg

Prince
Joined
Mar 28, 2021
Messages
469
I believe this mechanic could make exploring the map or invading an enemy more challenging, opening opportunities for exploits (for example, targeting the enemy's supply wagon could cripple the invading army).

Part of the idea was supposed to be this, actualy. Making the world somewhat harder to explore thus making it more mysterious in a sense, kind of how the world looked like for people before globalization.

The nuance is that
1. It has never been a problem to send a small detachment to the "edge of the world" - the conquistadors and especially the conquerors of Siberia will confirm this. A small detachment could simply exist by hunting / fishing – and this method of supply played a significant role even among the Romans. Hunters are one of the official variants of legion specialists (immunes)
2. It was also not a problem to supply sufficiently large forces in a densely populated and fertile area. For example, because by looting a farm where there were food supplies for at least six months for 3 people, it was possible to provide 540 people with food for a day (180 * 3). Therefore, for example, quite mobile armies of nomads managed without a wagon train in agricultural areas. Without such radical methods, food could be bought.
3. The problems began in a situation like "Napoleon crossed the border of Russia."
4. The possibility of wagon train support had a finite radius – the wagon train also ate and sooner or later ate itself without even supplying the army.
That is, in pure theory, a realistic model should take into account the stack size and

1. Fertility of the area. For example, a stack of three units can do without a wagon train on a cell with fertility 3, but even a lone unit will starve in the desert. At the same time, checking the position of the unit in your version is assumed in any case. Maybe it makes sense to take into account the territorial context?

Further, specific mechanics are already required and everything is abstract here.
2. Robbery if necessary.
3. The depletion factor of the baggage train, if there is one.

At the same time, if the robbery can be realized, then the "disappearing" wagons will clearly drive the AI crazy.
 
Last edited:

<Nexus>

Traveler of the Multiverse
Joined
Jan 23, 2014
Messages
5,064
Location
In a constant brainstorm...
In my mod units are talking damage per turn (25%) if they are too far from your cities. That mechanic works very well.
A good addition would be pillaging tiles for food to heal the unit. Something like this: the pillaged improvement gives +X :food: --> the pillaging unit heals X*10% health.
 

pecheneg

Prince
Joined
Mar 28, 2021
Messages
469
In my mod units are talking damage per turn (25%) if they are too far from your cities. That mechanic works very well.
Yes, it models well the mechanics of the limited reach of the supply train – without the wagons themselves. At a long distance, the wagon train supplies itself, yes. At the same time, the idea of dramatically tightening logistical problems for large armies is very interesting in a gaming sense, realistic and entirely correct.
But the fact is that the modern political and ethnic map is largely the result of the fact that there was no such strict restriction for small detachments. As a result, not only vast territories with militarily weak populations were captured. There was also a marine colonization with limited seizing on the coasts – for a huge range. As a result, the maritime powers fought with each other in gigantic spaces from Brazil to Indonesia. And it was interesting and very cool.
In Civilization, such expansion is already severely limited by the high cost of remote cities. As a result, there are no colonial wars at the other end of the map until the very end of the game – and this is sad. The limitation on the range for any armies only aggravates the situation.
Ideally, it would be good
1. Link the scale of logistics problems to the size of the detachments, which either allows you to rely on local resources, or not.
2. Calculate the penalty for the remoteness of cities in different ways, depending on how the route goes, etc. For example, in the age of sail, one land cell can be equal to five sea cells. The difference in the efficiency of supply by land and by sea/rivers is very large even now. In the "age of sail" before the advent of railways, it was simply huge.
Ideally, roads and rivers can be taken into account, but this is pure projectionism and extremism.

A good addition would be pillaging tiles for food to heal the unit. Something like this: the pillaged improvement gives +X :food: --> the pillaging unit heals X*10% health.

Pillagе is cool, yes. By the way, it would be interesting to provide for the possibility of "buying" - treatment for money in neutral countries.
 
Top Bottom