Author Topic: Morale function analysis  (Read 4197 times)

0 Members and 1 Guest are viewing this topic.

Offline scient

Morale function analysis
« on: May 05, 2013, 05:13:37 AM »
This is building off of my work on morale few years ago as well as trying to the understand game internal systems.  I'll be going through and decompiling the functions related to morale system to get better understanding and to solve some of bugs related to it.  I'd appreciate a discussion of my analysis and any bugs related to morale in this topic since I'm not expert on how mechanics should be working just how they are internally.

This first function is what is used to calculate the morale for any unit besides native AI.  When you click on a unit, this is function that gets called to determine unit morale.  It is also used as the initial morale before combat related morale bonuses are applied among other places.

Code: [Select]
int UNIT_morale_retrieve_root(int unitID, int droneRiotToggle, int moraleUpgradeToggle) {
int factionID = unit_data[unitID].faction_id;
if(!factionID) // checking if native life AI faction
return UNIT_morale_retrieve_native_AI(unitID, moraleUpgradeToggle);
int nMoraleFin, nProtoId = unit_data[unitID].proto_id, nMoraleMod;
if(unit_basic_data[nProtoId].plan == UNIT_Management::PLAN_INFO_WARFARE){
nMoraleMod = faction_data[factionID].SE_probe;
if(nMoraleMod < 0) nMoraleMod = 0;
if(nMoraleMod > 3) nMoraleMod = 3;

if(BASE_has_secret_project(BASE_Management::FACL_TELEPATHIC_MATRIX-BASE_Management::FACL_HUMAN_GENOME_PROJ, factionID))
nMoraleMod += 2; // "All of your probe teams receive a +2 morale modifier."

for(int nTech = 0; nTech < MAX_TECHNOLOGY_NUM; nTech++){
if(technology_data[nTech].TECH_struct_flags & TECH_Managment::TECH_flags_improve_probe){
if(TECH_check_faction_has(nTech, factionID))
nMoraleMod++;
}
}

nMoraleFin = unit_data[unitID].morale + nMoraleMod;
if(nMoraleFin < 2) nMoraleFin = 2; // Disciplined
if(nMoraleFin > 6) nMoraleFin = 6; // Elite
}else{ // all other unit types
if(nProtoId < MAX_BASIC_UNIT_NUM && unit_wpn_data[unit_basic_data[nProtoId].weapon_id].offensive < 0){
// checking if one of basic #UNITS in alpha/x.txt with PSI offensive
nMoraleFin = unit_data[unitID].morale;
}else{ // non-PSI basic units and all other new prototypes
nMoraleMod = faction_data[factionID].SE_morale; // current Social Engineering Morale
if(nMoraleMod < -4) nMoraleMod = -4; // see #SOCMORALE in alpha/x.txt
else if(nMoraleMod > 4) nMoraleMod = 4;
if(nMoraleMod < -1) nMoraleMod++;
else if(nMoraleMod > 1) nMoraleMod--;

int factionMorale = faction_basic_data[factionID].special_rules_MORALE; // parsed from faction txt file
if(factionMorale < 0)
nMoraleMod += factionMorale; // applying negative modifier

int unitHomeBase = unit_data[unitID].home_base_id; // base unit was built in
if(unitHomeBase >= 0){
if(BASE_has_facility_ex(BASE_Management::FACL_CHILDREN_CRECHE, unitHomeBase, 0) && nMoraleMod < 0)
nMoraleMod /= 2; // halving if negative bonus only
if(BASE_has_facility_ex(BASE_Management::FACL_BROOD_PIT, unitHomeBase, 0)
&& nProtoId < MAX_BASIC_UNIT_NUM){
if(unit_wpn_data[unit_basic_data[nProtoId].weapon_id].offensive < 0 
|| nProtoId == UNIT_Management::BSC_SPORE_LAUNCHER && nMoraleMod < 0)
nMoraleMod /= 2; // never accessed because of MAX_BASIC_UNIT_NUM check ; PSI basic units handled above
}
}

if(factionMorale > 0)
nMoraleMod += factionMorale; // applying positive modifier

int factionMoraleTgl = faction_basic_data[factionID].special_rules_bitfield & SMACX_CORE::FACT_MORALE;
if(factionMoraleTgl && nMoraleMod < 0)
nMoraleMod = 0; // "Morale modifier (if 0, indicates an exemption from negative modifiers from other sources)."

if(droneRiotToggle && unitHomeBase >= 0
&& base_data[unitHomeBase].base_struct_status_bitfield & BASE_Management::DRONE_RIOTS_ACTIVE && !factionMoraleTgl){
nMoraleMod--;
if(nMoraleMod < 0) nMoraleMod = 0; // potential bug with this logic zeroing out negative bonus
if(nMoraleMod > 6) nMoraleMod = 6;
}
nMoraleFin = unit_data[unitID].morale + nMoraleMod;
}
if(nMoraleFin < 0) nMoraleFin = 0; // Very Green (Hatchling)
if(nMoraleFin > 6) nMoraleFin = 6; // Elite (Demon Boil)
}
return nMoraleFin;
}

Some areas stand out to me. 

The first being how Probe teams can never be "Very Green" or "Green".  Rest of code relating to Probe team morale seems ok.

The next is how basic PSI units (defined in alpha/x.txt #UNITS) are handled.  They're excluded from any faction related modifications to morale, negative or positive.

Next is how units even outside of bases are affected by Children Creche and Brood Pit.  It's halving the negative effects due to SE morale and faction MORALE value as long as these facilities are in the home base.  However, the Brood Pit is never taken into account for prototyped units using PSI offensive.  I think this is in error because basic PSI offensive units skip over this code completely and non-basic PSI units will have a proto ID above MAX_BASIC_UNIT_NUM.  So due to how logic of function works, this code will never affect the morale modifier variable and is dead code.

Finally, drone riots.  Based on how this code works, it looks as if negative effects to morale can be completely negated while a unit's home base has drone riots active.  I have to look into it more but if nMoraleMod is negative going into drone riots code, it actually is a GOOD thing that base is rioting since it gets set to zero.


Offline Geo

Re: Morale function analysis
« Reply #1 on: May 05, 2013, 11:24:38 AM »
Two remarks:

Does this code (or another part somewhere else) do similar stuff for independent units (no homebase) and the free maintenance upkeep ability (no minerals drawn of the home base for upkeep)?

Offline scient

Re: Morale function analysis
« Reply #2 on: May 05, 2013, 12:38:05 PM »
Units without a home base call this code, they just skip part regarding CC/BP and drone riots. If a unit is independent, then home base value is set to -1.  That's reason for this check "unitHomeBase >= 0 " in those two locations I mentioned.   It's checking if a valid base ID was set.

ALL units except native AI (ie. red alien faction) use this to calculate the "base" line morale value for a unit before combat bonuses or other stuff.  There is completely separate area having to do with base upkeep which I've already identified but it doesn't interact with morale as far as I know.

Offline Fal

Re: Morale function analysis
« Reply #3 on: May 05, 2013, 02:38:24 PM »
I assume faction_data[factionID].SE_morale is your overall Morale SE rating, including faction bonuses and penalties.  What is faction_basic_data[factionID].special_rules_MORALE?  If that's the faction's inherent bonus/penalty to Morale then it's being counted multiple times.

Offline scient

Re: Morale function analysis
« Reply #4 on: May 05, 2013, 02:56:16 PM »
The SE_morale variable is based solely on cumulative Social Engineering morale value.  The ones that effect it are:

Fundamentalist : +1
Power : +2
Wealth : -2
Eudaimonic : -2
Thought Control : +2

When faction files are parsed, it stores MORALE value in special_rules_MORALE.  It does looks like it's being counted multiple times, but it isn't.  It first checks to see if it's less than zero, and then applies penalty if so.  I believe this is so that if SE_morale is negative and/or faction penalty exists, then the base facility modification of the halving of penalty is done before bonuses.  After the facility check, then it applies positive bonus.  If special_rules_MORALE is zero in faction files, then FACT_MORALE gets set and zeros out any negative penalty per description.

Offline Yitzi

Re: Morale function analysis
« Reply #5 on: May 05, 2013, 03:32:06 PM »
Just wondering...which is the location where this code is found?

Offline Fal

Re: Morale function analysis
« Reply #6 on: May 05, 2013, 07:45:49 PM »
The SE_morale variable is based solely on cumulative Social Engineering morale value.  The ones that effect it are:

Fundamentalist : +1
Power : +2
Wealth : -2
Eudaimonic : -2
Thought Control : +2

When faction files are parsed, it stores MORALE value in special_rules_MORALE.  It does looks like it's being counted multiple times, but it isn't.  It first checks to see if it's less than zero, and then applies penalty if so.  I believe this is so that if SE_morale is negative and/or faction penalty exists, then the base facility modification of the halving of penalty is done before bonuses.  After the facility check, then it applies positive bonus.  If special_rules_MORALE is zero in faction files, then FACT_MORALE gets set and zeros out any negative penalty per description.

Ok, I'm still unclear on what's happening in this section of code.

Code: [Select]
nMoraleMod = faction_data[factionID].SE_morale; // current Social Engineering Morale
if(nMoraleMod < -4) nMoraleMod = -4; // see #SOCMORALE in alpha/x.txt
else if(nMoraleMod > 4) nMoraleMod = 4;
if(nMoraleMod < -1) nMoraleMod++;
else if(nMoraleMod > 1) nMoraleMod--;

int factionMorale = faction_basic_data[factionID].special_rules_MORALE; // parsed from faction txt file
if(factionMorale < 0)
nMoraleMod += factionMorale; // applying negative modifier

int unitHomeBase = unit_data[unitID].home_base_id; // base unit was built in
if(unitHomeBase >= 0){
if(BASE_has_facility_ex(BASE_Management::FACL_CHILDREN_CRECHE, unitHomeBase, 0) && nMoraleMod < 0)
nMoraleMod /= 2; // halving if negative bonus only
if(BASE_has_facility_ex(BASE_Management::FACL_BROOD_PIT, unitHomeBase, 0)
&& nProtoId < MAX_BASIC_UNIT_NUM){
if(unit_wpn_data[unit_basic_data[nProtoId].weapon_id].offensive < 0 
|| nProtoId == UNIT_Management::BSC_SPORE_LAUNCHER && nMoraleMod < 0)
nMoraleMod /= 2; // never accessed because of MAX_BASIC_UNIT_NUM check ; PSI basic units handled above
}
}

if(factionMorale > 0)
nMoraleMod += factionMorale; // applying positive modifier

This is how I read this section of code:

1. Get the Morale SE rating from the faction's government/economy/values/future society settings, not including any inherent bonuses or penalties.
2. Set this number to -4 if it's less than -4, or set it to 4 if it's greater than 4.
3. Add 1 if it is less than -1, or subtract 1 if it is greater than 1.
4. Now grab the faction's inherent Morale bonus or penalty.
5. If the faction has an inherent Morale penalty, apply it now.
6. Check the unit's home base and divide a negative Morale modifier by 2 if it has a Children's Creche.
7. If the faction has an inherent Morale bonus, apply it now.

Is this correct?

Offline scient

Re: Morale function analysis
« Reply #7 on: May 05, 2013, 08:12:38 PM »
Just wondering...which is the location where this code is found?

For SMACX: 0x005C0E40.

Actually, I'll make a point to include offset as comment for functions I start decompiling and post.

This is how I read this section of code:

1. Get the Morale SE rating from the faction's government/economy/values/future society settings, not including any inherent bonuses or penalties.
2. Set this number to -4 if it's less than -4, or set it to 4 if it's greater than 4.
3. Add 1 if it is less than -1, or subtract 1 if it is greater than 1.
4. Now grab the faction's inherent Morale bonus or penalty.
5. If the faction has an inherent Morale penalty, apply it now.
6. Check the unit's home base and divide a negative Morale modifier by 2 if it has a Children's Creche.
7. If the faction has an inherent Morale bonus, apply it now.

Is this correct?

This is correct.

Offline Yitzi

Re: Morale function analysis
« Reply #8 on: May 05, 2013, 08:38:52 PM »
Speaking of bugs: I believe that when negating negative morale modifiers for units in a base with a creche, the game does not use this function, but rather just uses the result from social engineering/faction.  Thus, if the unit has the penalty reduced (either because that would put it below 0 or because its home base has a creche), the creche will actually add more than enough to compensate for the negative modifiers.

Also, while not technically a bug, the fact that the penalty is halved rounding down means that once you have creches, -1 MORALE is irrelevant (and so is -2 if you don't have command centers etc.).

Offline scient

Re: Morale function analysis
« Reply #9 on: May 05, 2013, 09:11:45 PM »
The two combat functions when attacking in base do actually call this function at start before applying other modifications.  I'm actually working on decompilimg them right now and will post them next.

COMBAT_morale_attack_calc() call to above function-> 0x005015DB
COMBAT_morale_defend_calc() call to above function -> 0x0050196B

Offline Fal

Re: Morale function analysis
« Reply #10 on: May 05, 2013, 11:05:30 PM »
This is how I read this section of code:

1. Get the Morale SE rating from the faction's government/economy/values/future society settings, not including any inherent bonuses or penalties.
2. Set this number to -4 if it's less than -4, or set it to 4 if it's greater than 4.
3. Add 1 if it is less than -1, or subtract 1 if it is greater than 1.
4. Now grab the faction's inherent Morale bonus or penalty.
5. If the faction has an inherent Morale penalty, apply it now.
6. Check the unit's home base and divide a negative Morale modifier by 2 if it has a Children's Creche.
7. If the faction has an inherent Morale bonus, apply it now.

Is this correct?

This is correct.

Then it looks like a faction bonus to Morale should be able to get around the +3 cap.  For example, the Spartans running Power/Thought Control would have +4 Morale from their SE choices, which will increase a unit with base Green Morale to Veteran.  Then their inherent +2 Morale bonus should apply afterwards and make the unit Elite, but this doesn't happen.

Edit: I've also been unable to get drone riots to remove a negative Morale modifier.

Offline scient

Re: Morale function analysis
« Reply #11 on: May 06, 2013, 01:42:11 AM »
Yah, it looks like unit_data[unitID].morale is getting modified else where on SE change.  So we have incomplete picture atm.  Once I start getting more functions finished which should be soon then might start to get a better idea of how everything is working together.

Offline Nexii

Re: Morale function analysis
« Reply #12 on: May 09, 2013, 12:48:08 AM »
This is how I read this section of code:

1. Get the Morale SE rating from the faction's government/economy/values/future society settings, not including any inherent bonuses or penalties.
2. Set this number to -4 if it's less than -4, or set it to 4 if it's greater than 4.
3. Add 1 if it is less than -1, or subtract 1 if it is greater than 1.
4. Now grab the faction's inherent Morale bonus or penalty.
5. If the faction has an inherent Morale penalty, apply it now.
6. Check the unit's home base and divide a negative Morale modifier by 2 if it has a Children's Creche.
7. If the faction has an inherent Morale bonus, apply it now.

Is this correct?

This is correct.

Then it looks like a faction bonus to Morale should be able to get around the +3 cap.  For example, the Spartans running Power/Thought Control would have +4 Morale from their SE choices, which will increase a unit with base Green Morale to Veteran.  Then their inherent +2 Morale bonus should apply afterwards and make the unit Elite, but this doesn't happen.

Edit: I've also been unable to get drone riots to remove a negative Morale modifier.

Wouldn't Inherent Morale bonus be different from SE Morale? 
Inherent Morale = #MORALE in alphax.txt (0 for Spartans)
SE Morale = #SOCMORALE in alphax.txt (2 for Spartans)

At least that's my guess glancing at alphax.txt.  Would require some more testing.  You could create some custom factions and see what happens.  I don't think any of the default factions would have an Inherent Morale bonus.

Offline scient

Re: Morale function analysis
« Reply #13 on: May 09, 2013, 08:02:18 PM »
#MORALE -> nMoraleFin (0 to 6) , nothing to do with SE or faction bonuses

#SOCMORALE -> based on values set from #SOCIO for various SE settings, again nothing to do with faction inherit bonuses.

Faction bonuses for Spartans would be in spartan.txt only and parsed on starting a new game or loading an existing game.

Offline Yitzi

Re: Morale function analysis
« Reply #14 on: May 09, 2013, 08:41:35 PM »
or loading an existing game.

I'm pretty sure that's incorrect; I believe that faction bonuses are stored as part of the save file and not re-checked each load.

 

* User

Welcome, Guest. Please login or register.
Did you miss your activation email?


Login with username, password and session length

Select language:

* Community poll

SMAC v.4 SMAX v.2 (or previous versions)
-=-
24 (7%)
XP Compatibility patch
-=-
9 (2%)
Gog version for Windows
-=-
103 (32%)
Scient (unofficial) patch
-=-
40 (12%)
Kyrub's latest patch
-=-
14 (4%)
Yitzi's latest patch
-=-
89 (28%)
AC for Mac
-=-
3 (0%)
AC for Linux
-=-
6 (1%)
Gog version for Mac
-=-
10 (3%)
No patch
-=-
16 (5%)
Total Members Voted: 314
AC2 Wiki Logo
-click pic for wik-

* Random quote

The Academician's private residences shall remain off-limits to the Genetic Inspectors. We possess no retroviral capability, we are not researching retroviral engineering, and we shall not allow this Council to violate faction privileges in the name of this ridiculous witch hunt!
~Fedor Petrov (Vice Provost for University Affairs)

* Select your theme

*
Templates: 5: index (default), PortaMx/Mainindex (default), PortaMx/Frames (default), Display (default), GenericControls (default).
Sub templates: 8: init, html_above, body_above, portamx_above, main, portamx_below, body_below, html_below.
Language files: 4: index+Modifications.english (default), TopicRating/.english (default), PortaMx/PortaMx.english (default), OharaYTEmbed.english (default).
Style sheets: 0: .
Files included: 45 - 1228KB. (show)
Queries used: 42.

[Show Queries]