Reaction Module Desired Features

Prof. Garfield

Deity
Supporter
Joined
Mar 6, 2004
Messages
4,366
Location
Ontario
I'm planning on building a 'reaction' module for Lua, hopefully some time in the next few weeks (since @JPetroski needs a reaction module for Midway), and I'm interested to find out what features and/or characteristics people would like such a module to have.

Please speak up. I'd rather put in some features that won't be used than have someone (possibly myself) re-write the module in a couple years because I didn't anticipate a use case. Or, I can at least 'leave space' in the code for other use cases, even if I don't implement them immediately.

For those unfamiliar, reactions are used in Over the Reich. Basically, it is code that runs when a unit produces ammo for a ranged attack, and does damage to that unit based on the enemy units that are nearby. (I think it is possible to have a reaction against a unit before the first round of regular Civ II combat, but I haven't tested this.)

I'm not guaranteeing I'll re-implement all the features in Over the Reich, so mention good features from there if you think they are important. In particular, I'm not planning on doing 'splash' damage to nearby units in this module, on the basis that it complicates the code, requires more specification of the details on the part of the scenario designer, and forces the player to consider unit placement during the turn, and not just at the end of the turn. (This last reason is a significant part of the reason we're probably getting rid of splash damage in OTR.) If you think it is useful to have splash damage as an option, please speak up with your reasoning of why it is important. I'm certainly willing to put it in if it would be useful.

I'm thinking of a paradigm where there are two parts to the reaction. First part is the chance of the reacting unit to hit the trigger unit. The second part is the variation in how much damage is done if there actually is a hit. This way, modifications (such as improved technology) can make a change to one or the other individually. (In OTR, the damage schedule controls both in one schedule, rather than two, so the meaning of changes can get confusing).
 
Thanks for doing this, @Prof. Garfield

Anyone reading this - as Prof. Garfield alluded to, without this module, your ranged units are impervious to attack. However, this reaction module could also replace the ambush mechanism currently favored (where units are given invisible until attack, which is annoying for one, and secondly not very effective against a min/maxer given you can just right click a tile). This is a thread worth day dreaming in if you could ever see yourself using lua.

Good features from OTR I'd like to see return:
1. Ability to modify the power/existence of reactions if the reacting unit is:
*On certain terrain (like installation terrain, but in an ambush, could only fire reaction if enemy is in deep forest - could see use in Germanicus redone).
*In a city.
*On a certain unit (not sure if this is in OTR - it needs to be in Midway - units shouldn't react if they on carriers).
*On a certain map (necessary for any multilevel scenario).
*Reaction unit owner civ has a certain tech.
*Range and max attacks

2. Ability to modify the power/existence of reactions if the targeted unit is:
*On certain terrain (like clouds, but also maybe there could be a fortress terrain that "protects" ranged units from reaction attacks).
*In a city.
*On a certain unit (not sure if this is in OTR - it needs to be in Midway - units can't react on carriers).
*On a certain map (necessary for any multilevel scenario).
*Defending unit owner civ has a certain tech.
*Range and max attacks

3. Ability to have several schedules per unit that control how they interact. This really helps differentiate units, though it is complex for the designer. I think it's important that a fighter do more damage against a bomber than another fighter, for example.

New Features Not Yet Explored in OTR
*Ability to set a "flag" or "state" where certain units (be it of a unitType, or belonging to a certain civ) will or will not react depending on diplomacy settings between the civs. This wasn't an issue in OTR as there are only two civs, but any multi-civ situation (where unit.id are shared between them, at least) is going to create a problem. Allowing the check to be based on the diplomacy settings (which we can manipulate, as @Knighttime and @tootall_2012 did in Napoleon, would, I'd think, solve this for one, and secondly open up new things (maybe you could rename "Embassy" or "Cease Fire" to "Strategic Interest" and then allow reactions under those to kind of show the situation with U.S. airstrikes, for example - a civ might punish/help another civ via reaction without actually getting involved in the conflict to the extent of a full-blown war).

*I don't think it is yet in OTR (but I think you're planning to add it) - if a reaction is due to a ranged attack calling up ammo, then the reaction ought to be able to either definitely destroy the ammo (think SDI), weaken the ammo (think phalanx ship defense), or leave it alone (think counter artillery).

*Ability to manipulate Range and max attacks a bit more, based on certain circumstances. Maybe a destroyer on map 1 can target a U-Boat on map 0 at a range of 1, but it can target a light cruiser on map 1 at a range of 2.

*I'd like more flexibility or at least clarity in canReact (maybe I already have it and just didn't use it appropriately). My understanding was that both reactionGroups.LuftwaffeFighter and reactionGroups.luftwaffeBomber had to be a subset or drilled down version of reactionGroups.canInterceptGerman (all German aircraft that can be intercepted, period). This created problems. Perhaps they were in my head if I didn't understand it though. At any rate, if I don't currently have the ability to create a larger table for canReact like in reactionDamage, I'd like it.

Code:
canReact[unitAliases.SpitfireIX.id] = {maxAttacks = 2, range = 1, lowerAltitude = reactionGroups.canInterceptGerman}

as I have in reactionDamage

Code:
reactionDamage[unitAliases.SpitfireIX.id] ={high = {{triggerTypes = reactionGroups.luftwaffeFighter, damageSchedule = ds.SpitfireIXinterceptionFighter1},
{triggerTypes=reactionGroups.luftwaffeHeavyFighter, damageSchedule = ds.SpitfireIXinterceptionHeavyFighter1},
{triggerTypes=reactionGroups.luftwaffeBomber, damageSchedule = ds.SpitfireIXinterceptionBomb1}},
low = {{triggerTypes = reactionGroups.luftwaffeFighter, damageSchedule = ds.SpitfireIXinterceptionFighter2},
{triggerTypes=reactionGroups.luftwaffeHeavyFighter, damageSchedule = ds.SpitfireIXinterceptionHeavyFighter2},
{triggerTypes=reactionGroups.luftwaffeBomber, damageSchedule = ds.SpitfireIXinterceptionBomb2}},
dive = {{triggerTypes = reactionGroups.luftwaffeFighter, damageSchedule = ds.SpitfireIXinterceptionFighter4},
{triggerTypes=reactionGroups.luftwaffeHeavyFighter, damageSchedule = ds.SpitfireIXinterceptionHeavyFighter4},
{triggerTypes=reactionGroups.luftwaffeBomber, damageSchedule = ds.SpitfireIXinterceptionBomb4}}, }

*Ability for reaction to cost money.
*Ability for reaction to cost science beakers.

Nice to have but maybe beyond scope of what you're considering
*Ability for reaction in a city to add or remove an improvement from said city (could be used to represent terrorism - if "civilian" reacts to "terrorist" then "temple" is removed).
*Ability for reaction to change specialist in city (same idea/rationale for the need -- I'm trying to explore ways that this could be used beyond simple combat).
 
Thanks for doing this, @Prof. Garfield

I completely second this. :)

I don't think it is yet in OTR (but I think you're planning to add it) - if a reaction is due to a ranged attack calling up ammo, then the reaction ought to be able to either definitely destroy the ammo (think SDI), weaken the ammo (think phalanx ship defense), or leave it alone (think counter artillery).

This sounds especially interesting, thinking of the function of energy shields in battles in space. "Photon torpedoes are fired. Shields up"! :goodjob:
 
*I'd like more flexibility or at least clarity in canReact (maybe I already have it and just didn't use it appropriately). My understanding was that both reactionGroups.LuftwaffeFighter and reactionGroups.luftwaffeBomber had to be a subset or drilled down version of reactionGroups.canInterceptGerman (all German aircraft that can be intercepted, period). This created problems. Perhaps they were in my head if I didn't understand it though. At any rate, if I don't currently have the ability to create a larger table for canReact like in reactionDamage, I'd like it.

If you have some time, perhaps you could specify how you would want to specify the reaction details (damage, can react, etc.). I'd appreciate the input of others also. I realize that the Over the Reich system is rather confusing in that regard, and I have ideas of how to make it better, but I'm not the one who spent lots of time making specifications for everything.
 
This will prove to be a very interesting module to have indeed, though I'm not certain I understand what "doing 'splash' damage to nearby units" means. Is that a kind of collateral damage effect?

For my part, I've long wanted to create a Battle of Tarawa scenario as a companion to my other Pacific game, the battle of Iwo Jima. This time it would be a tactical scenario at the platoon level. As your title indicates, it would be a reaction module, i.e. an attacking unit would be subject to reaction fire, and therefore subject to damage, when attacking an enemy unit.

This is probably out of the scope of your module, but I thought I would enquire nonetheless. Is it possible for a defensive work, like a bunker type or machine gun nest unit (which have 0 MP), inflict damage during its turn to units that are adjacent to it?
 
If you have some time, perhaps you could specify how you would want to specify the reaction details (damage, can react, etc.). I'd appreciate the input of others also. I realize that the Over the Reich system is rather confusing in that regard, and I have ideas of how to make it better, but I'm not the one who spent lots of time making specifications for everything.

I'm not totally sure that I understand the question, sorry. I guess what I found confusing was that there's only one entry below, which indicated to me that this was going to have to be the "general class" of units from which I might further differentiate "sub classes" (such as bomber/fighter). Also, I only had one mapcheck (lowerAltitude here) that I could use and so I think you had to create a workaround to prevent the B-17s and B-24s from reacting defensively on the lower map. I would think if canReact held a table that contained additional tables (such as in reactionDamage) that would avoid the need to have "side code" for the 17s? But again, it's totally possible this was there all along and I just didn't realize it.

canReact[unitAliases.SpitfireIX.id] = {maxAttacks = 2, range = 1, lowerAltitude = reactionGroups.canInterceptGerman}

This is probably out of the scope of your module, but I thought I would enquire nonetheless. Is it possible for a defensive work, like a bunker type or machine gun nest unit (which have 0 MP), inflict damage during its turn to units that are adjacent to it?

You need a trigger but as Prof. Garfield has indicated that onActivate fires every time the AI moves a space, a 0 MP player-controlled defensive work should fire whenever an AI approaches. It shouldn't matter if it is a 0 MP unit. What matters is that something can trigger it. onActivate doesn't check every space for the human (or else we would have used it to reduce fuel rather than by pressing 'k' in OTR) so you'll need another trigger. It could defend itself from attack if the player-controlled unit called up ammo.
 
This will prove to be a very interesting module to have indeed, though I'm not certain I understand what "doing 'splash' damage to nearby units" means. Is that a kind of collateral damage effect?

'Splash' damage is a term sometimes used in video games if a targeted unit is not the only one that takes damage. If you've ever played Starcraft, siege tanks do 'splash' damage to units near the actual target.

In the case of Over the Reich, we figured that if an 88 is firing a lot of flak, it might have a particular target, but other stuff nearby might also be hit as well. This had two problems. One was that you could accidentally decimate your own aircraft if you accidentally positioned above some of your flak (this was 'solved' by a 'practice' calculation of the result, and if the practice didn't have an attack do lots more damage to the enemy, then the gun didn't fire). The other is that it makes moving around during the turn complicated, since the first units you move near a target will take more damage from flak than later units. A simple example is that you can't move your fighters to cover the bomber retreat path until after all your bombing attacks have been made, since otherwise they would take damage from the flak reactive fire.

I'm inclined to think that the increased tactical variety given with the availability of splash damage isn't worth the headaches. If someone thinks otherwise, that's fine, but I'd like to hear. My 'strong' argument is that in the existing Civ II game, the active player can choose the order in which his units attack and move, but nothing other than actually making an attack exposes the units to risk. They can move around as they please and only their position at the end of the turn matters. Reactive splash damage changes this paradigm.

This is probably out of the scope of your module, but I thought I would enquire nonetheless. Is it possible for a defensive work, like a bunker type or machine gun nest unit (which have 0 MP), inflict damage during its turn to units that are adjacent to it?

I have a couple possibilities:

First, is to use an 'after production' event and go through all the units in the game, checking for gun batteries that should fire, and then run the 'firing' code for each one.

Second, is to give the bunker 1 movement point, and use an onActivation trigger. When the unit activates (and has full movement), run the firing code and expend the units movement points, so it can't actually move around.

Third, which is in scope, is to make the bunker a reactive unit.

I'm not totally sure that I understand the question, sorry. I guess what I found confusing was that there's only one entry below, which indicated to me that this was going to have to be the "general class" of units from which I might further differentiate "sub classes" (such as bomber/fighter). Also, I only had one mapcheck (lowerAltitude here) that I could use and so I think you had to create a workaround to prevent the B-17s and B-24s from reacting defensively on the lower map. I would think if canReact held a table that contained additional tables (such as in reactionDamage) that would avoid the need to have "side code" for the 17s? But again, it's totally possible this was there all along and I just didn't realize it.

canReact[unitAliases.SpitfireIX.id] = {maxAttacks = 2, range = 1, lowerAltitude = reactionGroups.canInterceptGerman}

I wasn't trying to ask something specifically about this problem, it had just reminded me of something I should ask. Let me see if I can make it clearer:

A big part of making reaction code is putting in the specifics of how reactions work for the current scenario. That is, you will have to put in the data for the specific scenario. (Like the reaction groups, etc.) Do you have any ideas on how should that be organized and expressed? OTR has most stuff defined in a table organized around the reacting unit. Would it make more sense for technology buffs and debuffs, for example, to be defined in a separate table organized around technologies instead?
 
A big part of making reaction code is putting in the specifics of how reactions work for the current scenario. That is, you will have to put in the data for the specific scenario. (Like the reaction groups, etc.) Do you have any ideas on how should that be organized and expressed? OTR has most stuff defined in a table organized around the reacting unit. Would it make more sense for technology buffs and debuffs, for example, to be defined in a separate table organized around technologies instead?

I think you're darned either way. Lua is powerful but it is unwieldy. One of the bigger pains in OTR is just how many tables you have to find first and then edit. Adding a new unit to the game can easily take 15-20 minutes once you get in the groove, partially because there's just so many places to check. So part of me wants to say, "I really with I had a units table, where I could simply store everything I need to about that unit."

But then I think about what that would look like in OTR, and you'd have enormous tables where the information is buried in monster paragraphs and bug proofing would be a chore because of it, so I'm inclined to lean towards the "have the information in many places" route.

I think the most important thing is for you to do what makes sense for you and you enjoy first and foremost, but that at some point, we might want to all sit down and decide as a group how events will "be" organized, with the aim of at least structuring them in the same order and flow from scenario to scenario (like a rules file). This would be especially important, I'd think, if you pursue your civClone (and I definitely owe you countless hours of work on the project after all the help you've given me from OTR, so issue the call to arms whenever needed).
 
I'm thinking I may have been closer to the mark than I thought with the reaction code I wrote for OTR. The way I wrote reaction in OTR was to have a basic 'framework' of code in its own file, and then I expect the scenario designer to provide certain functions to fill in the details. In OTR, I wrote these functions, except that they relied on tables of data, which I let JPetroski fill in.

I can't just write the 'framework' and then tell the scenario designer to provide five or six functions, since that would still leave the barrier to entry too high. But what I can do is provide a couple of functions that turn some data tables into the appropriate five or six functions. There can be a 'basic' function that turns a relatively simple specification table into a basic reaction system, and a couple more complicated ones that use several tables and involve more complicated features. And, if someone has very detailed and nuanced desires, they can still write their own functions to get the results, without having to worry about whether I foresaw their specific usage.
 
One of the bigger pains in OTR is just how many tables you have to find first and then edit. Adding a new unit to the game can easily take 15-20 minutes once you get in the groove, partially because there's just so many places to check. So part of me wants to say, "I really with I had a units table, where I could simply store everything I need to about that unit."

But then I think about what that would look like in OTR, and you'd have enormous tables where the information is buried in monster paragraphs and bug proofing would be a chore because of it, so I'm inclined to lean towards the "have the information in many places" route.
I can totally relate to this philosophical debate, and have bounced back and forth in terms of which way I lean. There are pros and cons of both approaches and it really seems situational to me, depending on what I'm trying to add or change.

I know OTR has advanced a loooonng way from when I did this, but if you remember the way I originally wrote the "strategic bombing" event for you, it was set up to take the information one way, but then programmatically flip it around so that events could use it in reverse. Specifically, you provided a table that was organized by city, then improvement type, and had values for the tile coordinates. But I wrote a function to go through all that and flip it around, and create a table in memory that had the tile as the "outside layer", so an event could efficiently start with that and then find the improvement and city.

So at a theoretical level, I've been wondering about the merits of that approach even "earlier in the pipeline", so to speak, in the design phase. It seems like it ought to be possible to have huge data structures that can be analyzed or even defined from either direction, with Lua scripts to flip the data back and forth. So you ought to be able to provide a new unit as one object, but also ask Lua to give you back "all the reaction data for all units" (just as an example of one concept, hope that isn't a poor one!) and have it present that limited set of data back to you, organized differently, for proofreading or analysis. And then you ought to be able to revise the data in that format and resubmit it, to update the individual units again. I don't have any concrete examples of how this might happen or what effort would be involved, but I keep coming back to it as an idea since the "best" data structure for me (as a human) to work with isn't consistent. So why can't I make it the machine's job to adapt to what I want in each case?

I can't just write the 'framework' and then tell the scenario designer to provide five or six functions, since that would still leave the barrier to entry too high. But what I can do is provide a couple of functions that turn some data tables into the appropriate five or six functions. There can be a 'basic' function that turns a relatively simple specification table into a basic reaction system, and a couple more complicated ones that use several tables and involve more complicated features. And, if someone has very detailed and nuanced desires, they can still write their own functions to get the results, without having to worry about whether I foresaw their specific usage.
This is more or less the situation I feel like I'm facing with the supply lines module, FWIW. I'm interested to see how this turns out for you, since I have a lot of the same concerns (being cognizant of barrier to entry, goal of providing basic behavior that isn't too difficult to implement, goal of establishing a framework that manages customization without it becoming a hard limit to someone who has nuanced desires and wants to push the boundary).
 
I'm not sure if this is the best thread to ask this but I believe it may be as air protected stacks went hand in hand with onActivate in OTR.

I want to remove air protected stacks in a SP scenario I'm working on. I don't have the AI calling up ammo, so would need to rely on the onActivate trigger when they move each space. I believe I remember reading that @Prof. Garfield mentioned that "onActivate" checks each time the AI goes to move a single square.

Am I remembering correctly and if so, do we have any idea how this works?

Imagine from west to east three tiles 1, 2, 3. "3" has the desired target that I want uncovered.

Would it be possible to have the defender teleported over If an enemy unit got to "2?"
 
I'm not sure if this is the best thread to ask this but I believe it may be as air protected stacks went hand in hand with onActivate in OTR.

I want to remove air protected stacks in a SP scenario I'm working on. I don't have the AI calling up ammo, so would need to rely on the onActivate trigger when they move each space. I believe I remember reading that @Prof. Garfield mentioned that "onActivate" checks each time the AI goes to move a single square.

Am I remembering correctly and if so, do we have any idea how this works?

Imagine from west to east three tiles 1, 2, 3. "3" has the desired target that I want uncovered.

Would it be possible to have the defender teleported over If an enemy unit got to "2?"

Yes, onActivate does appear to work for each square an AI unit moves to. Therefore, if your onActivate trigger checks all adjacent squares for air protected stacks, the onActivate trigger on square "2" would detect the stack on square "3," and move the offending air unit. There might be a problem with this taking too long and causing noticeable lag, but that's something that we'll have to try in order to find out. An after production event could also make the check and move units if necessary.

I'm not scheduled to work Thursday and Friday, so I will probably have time to write a generic move air units off stack then.
 
@JPetroski

I've attached a copy of the General Library, which has a function
Code:
gen.clearAdjacentAirProtection(unit)
This clears air protected stacks in squares adjacent to the unit, ignoring the other units of that tribe (so activating your unit adjacent to your own air protected stack won't unstack it).

Use it in the onActivation event, and it should do the job.

I've only tested it briefly, so let me know if you run into problems. The general library also has the 'building block' functions for this, so more complicated setups (like we have in OTR) are possible.
 

Attachments

  • 5-June-2020-generalLibrary.lua.zip
    10.9 KB · Views: 159
Top Bottom