Customizable Unit Counter System & Civ4-Style Best Defender Selection#31
Customizable Unit Counter System & Civ4-Style Best Defender Selection#31bingyu-893 wants to merge 13 commits intomaxpetul:masterfrom
Conversation
…te application of counter rules. - Introduced a new boolean to manage terrain skipping based on the context of the call, preventing stale data issues from previous computations.
|
Hello. Thanks for the contribution. I glanced over it and it looks alright except for a few issues:
|
- support dynamic district types - Add details of terrain types to config file
-turn off some features in default.c3x_config.ini -Actually, the line endings are already LF, but in my last commit I made a typo.
|
The support for dynamic districts looks good. However, whatever you did to change the line endings to CRLF didn't work; the endings in injected_code.c and other files are still LF. You'll know the line endings are fixed when GitHub no longer shows that this PR touches every line in the files. For example, if you go to the "files changed" tab, for injected_code.c, it shows +37,115 and -36,558, indicating that this PR changers every line in the file. Those numbers should be much smaller showing only your changes. Same applies to civ_prog_objects.csv, default.c3x_config.ini, and C3X.h. |
This reverts commit 56f08f9.
- Added a new condition to counter_rule that checks the combat_experience of the unit. - This allows for more dynamic and challenging combat scenarios based on the unit's experience level.
|
Hi @maxpetul, Just wanted to give you a quick update! I've fixed the line ending issue you pointed out earlier. Additionally, I've implemented a new feature: players can now customize extra combat bonuses based on a unit's experience level directly within the |
|
Hello again.
Thank you, the line ending issue is fixed now. However that has revealed some other issues with this PR, it's undoing some recent commits. For example, looking at the changes to injected_code.c around
Are you planning to add that to this PR or start a new one? I'll leave it up to you but I'd prefer if you finished this one off first then started a new one for the combat odds display. As for advice, unfortunately I don't know where you'd hook into the UI, I'd have to look around for that. It also depends how you want to display the odds. I'm imaging a box that appears in one corner of the map display when the player hovers over a target, like in Civ 4. So you'd want to hook into the mouse hover event for Main_Screen_Form, but again, I haven't located that yet. Other than that, you'd probably want to draw a background for the box, that would be |
Thanks for catching that! I will fetch the latest changes from the upstream branch and merge them into mine. Since my new features are mostly isolated to the counter system, there shouldn't be any functional conflicts. I'll get this sorted out soon.
I agree. Splitting this into two PRs is definitely a much better idea for easier management. I'll focus on polishing and finishing up this PR first.
Thank you for the pointers on the UI side, I will research these methods once I start on the new PR. |
|
I had a look at that old branch I mentioned. It's probably not going to be useful since back then I was attempting to add a new form to the game with its own widgets such as buttons. That's overkill if all you want to do is display some text. However, I did some poking around today exploring how the display of combat odds might be implemented. It's actually a feature I've had on my mind for years but have never gotten around to doing myself, so I'm eager to see it done. To capture which tile the mouse is hovering over, you'd want to hook the 28th method (m27) in the Main Screen Form vtable, which I've named I'm still not sure what's the best way to draw the box with some text in it onto the screen. Drawing to the main screen's canvas in |
|
|
|
I experimented with this PR in game and found a few issues. There's one big issue in that combat animations are messed up and the unit shown on the tile when you attack it is not necessarily the one you end up fighting. My test was that I created a scenario where the player can use cavalry to attack a tile with an enemy rifleman and guerilla. The rifleman was modded to have +7 bonus HP and the guerilla to have -2 bonus HP and +1 defense. The test was to make sure that the game selected the rifleman as the defender since it's more likely to win combat although the guerilla is more likely to win a single round, when use_civ4_style_best_defender is on. What I discovered is that the game will show the guerilla as the top defender but when you attack you end up fighting the rifleman. And another bug, the game will continue showing the guerilla while combat is going on, it will be just standing there while the rifleman fights underneath, not shown. Then there are the smaller issues:
It's not clear to me what the purpose of the |
|
Thank you for the detailed testing and feedback! The issues you brought up, especially regarding the UI and combat animation mismatch, are exactly what I've been trying to wrap my head around these past few days. I also completely missed the interaction with Armies ( Unfortunately, my schedule is quite packed at the moment, so realistically it might take me until late May to get all these bugs fixed and the underlying logic refactored as you suggested. Regardless, thank you so much for all the guidance. I will get back to working on this PR as soon as I have the time! |
Hi @maxpetul,
I really love your C3X mod; it’s made playing Civ 3 so much easier and more enjoyable. This has inspired me to develop some new features for C3X, so I’ve had a go at creating a feature: a configurable unit counter (rock-paper-scissors) system, alongside an optional Civilization 4 style "Best Defender" selection logic for stack combat.
Here is the overall structure of the code:
1. Configuration & Toggles (in
default.c3x_config.ini)enable_unit_counters: Global toggle. Iffalse, the entire system is bypassed.unit_group: Allows grouping unit types for easier rule matching (syntax matchesbuilding_prereqs_for_units).counter_rule: The core ruleset. Supports modifiers for both attacker and defender (e.g.,self_atk_pct,enemy_def_pct). The logic dictates that the first unit listed in the rule is alwaysself, and the second isenemy, applying bidirectionally regardless of who initiates the attack.use_civ4_style_best_defender: When enabled (requiresenable_unit_counters), melee combat will dynamically select the defender that is hardest to defeat based on post-counter combat odds, aligning both the actual combat and the top unit displayed on the map stack.2. Data Structures (
C3X.h)struct unit_counter_group(Name + array ofUnitTypeID).struct counter_rulefor granular matching:UCM_ANY (*), orUCM_GROUP.terrain_type,only_in_city,district_id, andignore_terrain(negates defender's terrain bonuses).3. Core Algorithm
apply_counter_rules(...)to handle modifier calculation.aa(attacker multiplier) anddd(defender multiplier) to100. It then iterates through all configuredcounter_rulesand applies cumulative percentage multipliers for all matching criteria (chained multiplication for multiple valid rules).ignore_terrainflag if any matching rule mandates it.4. Combat Context & Engine Hooks
counter_combat_ctxto store active modifiers (attacker_atk_pct,defender_def_pct,ignore_terrain).patch_Unit_get_attack_strengthandpatch_Unit_get_defense_strengthto return the modifiedbase * pct / 100when the context is active for the current attacker/defender.patch_Fighter_get_odds_for_main_combat_loop. Because the originalFighter_get_combat_oddsreads rawUnitType.Attack / Defence, the implementation temporarily alters theUnitTypetable stats based onapply_counter_rules, runs the probe, and restores them.5. Civ4 Best Defender Logic & UI Synchronization
find_civ4_best_defender_againstenumerates valid defenders in a tile, simulatesapply_counter_rules, and invokesFighter_get_combat_odds. Selects the candidate with the highest defense odds. In case of a tie, falls back toFighter_prefer_first_defender_1(comparing modified effective defense) to maintain vanilla "cheapest unit defends first" behavior.patch_Main_Screen_Form_find_visible_unitto ensure the top unit of the stack matches the predicted best defender. Additionally, hookedpatch_Fighter_fightto override the engine's pre-selected defender when attacking from an adjacent tile, completely eliminating the "UI shows one unit, combat engages another" mismatch.6. Out of Scope