Acts as a Children's Creche for alien units, if there is not already one at the base.
There is no such entry in the original Datalinks, nor in any likely *.txt file I looked at. I searched several files for the word "brood".
Acts as a Children’s Creche for native units, if there is not already one at the base. Decreases the cost of alien units built at the base by 25%. Gives the base a +2 Police rating. The Brood Pit is a place where alien life forms can be grown with relative ease. It also doubles as a punishment area. Becomes available upon discovery of Centauri Genetics (E7).
#; Brood Pit
#HELPFAC35
+1 $LINK<lifecycle=11> bonus to native units built here. -25% cost of alien units. Negative $LINK<LIFECYCLE=130004> effects
cancelled in base square. Base also receives a $LINK<+2 POLICE=130005> rating.
Ran both functions through a battery of automated tests and corrected a couple of issues that are now live. The only thing left is the more in depth assembly regression checks I run everything through.
This is a great breakdown of issues:
https://web.archive.org/web/20160313051407/http://civgaming.net/smac/acad_morale.shtml
Basically, I think there are three main issues with the code.
* Brood Pit double bonus with Creche
* SE morale issues with CC
* Headquarters doesn't give defense bonus as stated in manual: SMAC pg 104 > "Units in a headquarters base automatically gain +1 Morale when defending."
If anyone can think of others, let me know. Still have some other threads to read through and piece together any other problems.
I couldn't make heads or tails of the Drone Riots effect on morale, either. It added a (-) symbol but with no apparent change to the battle odds math. Here's the check in your code:
https://github.com/b-casey/OpenSMACX/blob/master/src/veh.cpp#L1956
Forgot to add break down of this code. So first it checks if that boolean parameter (checkDroneRiot) is set to true. Skimming through references, it looks like it's only set to false mostly on display code. Might end up renaming variable once more code is decompiled. Anyway, next it checks to see if the unit's home base is valid (I think it will be -1 for independent units). Then it checks to see if the home base has flag set indicating drone riots. Lastly, it checks to see if the faction rules MORALE variable is not set or false (!false == true).
MORALE = Morale modifier (if 0, indicates an exemption from negative modifiers from other sources).
If all these conditions are met, it subtracts 1 from morale local variable (--) then uses range function that basically bounds value between 0 and 6.
Also, and this is unrelated to morale, but it is documented that the aliens get +1 RESEARCH in addition to PLANET from the Manifold Nexus (aka Temple?): https://github.com/b-casey/OpenSMACX/blob/defe36bdb1604c738f9cdf842ce1c8caabc456f1/src/faction.cpp#L720
Yes, it's in the Datalinks under "Basic Concepts.. Landmarks, Volcanoes, Etc."
Bingo: Calling the range function in the riot check is a huge problem that actually isn't related to the Creche. If there is any reason for a unit to have negative morale penalties applied before the check, but its home base is rioting, the penalties will be voided because the range function in the riot check will reset the "morale" variable to 0, yes? Same if SE MORALE is net 0. Then the 0 will be applied to the unit's intrinsic morale from Veh[vehID].morale, returning the intrinsic morale unaffected.
With zero or negative MORALE SE, a rioting home base is better or the same as a peaceful one and a Creche doesn't matter:
-2 MORALE (morale = -1), no Creche, Drone Riot (morale = range(-2, 0, 6) = 0!!!), Disciplined = range(0+2, 0, 6) = 2 but really should be 0 (Very Green)
-2 MORALE (morale = -1), no Creche, no Drone Riot, Disciplined = range(-1+2,0,6) = 1 (Green, correct)
-2 MORALE (morale = -1), Creche (morale = 0 due to rounding), Drone Riot (range(-1,0,6) = 0!!!), Disciplined = range(0+2,0,6) = 2 but really should be 1 (Green) as that would be the Creche compensating for the morale negatives but not the riot.
-2 MORALE (morale = -1), Creche (morale = 0), no Drone Riot, Disciplined = range(0+2,0,6) = 2 (Disciplined, correct)
These values explain why somebody thought riots gave bonuses, which I suppose is true in a certain sense. The riot check should really apply a -1 modifier no matter what at the end of the morale calculation, I think.
I completely agree. At first I didn't quite understand but breakdown was helpful. This is definitely one of the bugs outlined in the wiki and in Maniac's write up. I'll correct this in my next commit.
Awesome! The wiki has a good outline of the issues. I think all or at least most of the problems with CC/BP and morale revolve around these two get_basic functions. Once I finish vetting code is 1-1 copy of original assembly, I'll try to summarize each issue, problematic code and suggest a way of fixing it via code. Once there is some kind of consensus, I'll apply/push fixes. It's nice that the project is getting to the point where I can work on some of these long standing problems at the code level.
I've started to go through existing references to CC/BP in code I've already decompiled and add comments what mechanic it's tied to. I figured this might be a good way to narrow down other potential problems. So far, haven't seen anything that's stood out other than this:
https://github.com/b-casey/OpenSMACX/blob/master/src/veh.cpp#L1941
Basically, it's halving negative effects of the base morale of a unit based on its home base. So this is independent of whether unit is inside a base that has a CC or BP. Although, for BP it never reaches this area of code since it exits out here:
https://github.com/b-casey/OpenSMACX/blob/master/src/veh.cpp#L1926
Yeah, I agree about HQ bonus. I don't really see this being left out as a balance issue since it's pretty narrow scope. My guess is just an oversight.
Base receives +2 on growth scale and +2 (sic) on efficiency scale. All negative morale effects are cancelled for units in base square; instead such units receive a +1 morale modifier. Reduces base’s vulnerability to enemy mind control.
Proper care and education for our children (GROWTH) remains a cornerstone of our entire colonization effort. Children not only shape our future; they determine in many ways our present. Men and women work harder knowing their children are safe and close at hand. (EFFIC) And never forget that, with children present, parents will defend their home to the death.
moraleModifier = socialMoraleModifier + localRiotMalus
if (inCrecheBase and moraleModifier < 0) {
moraleModifier = moraleModifier / 2 + 1
} else if (inCrecheBase) {
moraleModifier += 1
}
attackMorale = range(moraleModifier + Veh[vehID].morale, 0, 6)
No Riots Drone Riots
MORALE SE Modifier CC Halves+1 CC Cancels+1 Modifier-1 CC Halves+1 CC Cancels+1
-------------------------------------------------------------------------------
-4 -3 0 1 -4 -1 1
-3 -2 0 1 -3 0 1
-2 -1 1 1 -2 0 1
-1 -1 1 1 -2 0 1
0 0 1 1 -1 1 1
1 1 2 2 0 1 1
2 1 2 2 0 1 1
3 2 3 3 1 2 2
4 3 4 4 2 3 3
morale_veh() {
moraleModifier = factionMORALESE // initialize modifier with direct MORALE SE value
// changes the MORALE value into the actual -3 -2 -1 -1 0 1 1 2 3 modifier scale
if (moraleModifier <= -2) {
moraleModifier++
} else if (moraleModifier >= 2) {
moraleModifier--
}
// This is the unused Morale faction "rule" which can give flat morale bonuses
// or negatives or give a limited immunity to negative modifiers... see below
if (moraleRule < 0) {
moraleModifier -= moraleRule
}
// Creche check.
// None of this works as the manual says it does.
// First, it checks the unit's HOME base for a Creche not the base square where it is.
// Second, it halves then rounds towards zero (integer math?) the negative modifiers
// thus far AND NO OTHERS.
if (homeBaseHasCreche && moraleModifier < 0) {
moraleModifier /= 2
// math examples: -3 / 2 = -1, 1 / 2 = 0
}
// if the Morale "rule" is positive, add that value to the modifier.
if (moraleRule > 0) {
moraleModifier += moraleRule
}
// The Morale "flag" is moraleRule == 0
// If the flag is set and if at this point the modifier is less than 0, reset to 0.
// This only gives immunity to MORALE SE modifiers or itself AND NO OTHERS.
if (moraleFlag && moraleModifier < 0) {
moraleModifier = 0
}
// BUGGED
// Drone Riot check
// If the unit's HOME base is rioting, -1 morale
// AFAIK this is an undocumented effect of Drone Riots
// The way morale_veh function is coded allows for disabling this effect
// This is how it should work. The way it's written in the code is bugged.
if (homeRioting) {
moraleModifier--
}
// add the modifier to the intrinsic morale and return. Range makes sure the value is
// between Very Green and Elite.
morale = moraleModifier + Veh[vehID].morale;
return range(morale, 0, 6);
}
get_basic_offense(){
morale = morale_veh(attacker)
if (attacker is in a base square) {
// Another Creche check!
// If THIS base has a Creche...
if (baseHasCreche) {
// Flat +1 morale increase.
// Documented in manual except this time the check is in this base
// not the attacker's home. Incoherent.
// Note: This increase can go above the Elite bonus cap unless
// the unit is artillery due to a check way below...
morale++
// BUGGED!!!
// Drumroll please for the most frakked bug in Creche morale math...
// If the attacker's MORALE SE is less than or equal to -2
// Add 1, then subtract value from present morale.
// Subtracting a negative number is ADDITIVE.
// A faction with -4 MORALE SE will get a +3 bonus
// to their attack on top of the +1 bonus from just having the Creche.
if (attackerMORALESE <= -2) {
attackerMORALESE++
}
morale -= attackerMORALESE //(+50% attack bonus at -4 MORALE)
}
// Special rules if the attacker is artillery and in a base.
// I'm not sure if I read all of this right.
if (artyCombat) {
// Attacking artillery gets +1 morale if its MORALE SE is 2 or 3
// This undoes the adjustment that creates the morale modifier scale from MORALE SE
// Documented??
if (factionMORALESE is either 2 or 3) {
morale++
}
// Soporific gas pods help a defender if attacker is arty??
// Soporific gas pods should only work when a unit is attacking, yes?
if (defender has soporific gas pods) {
morale--
} else {
// some more morale for artillery if firing from base?
morale++
}
}
} // End of in base condition
// This range function makes sure artillery morale will never be lower than
// Green or higher than Elite. Artillery will never benefit from the potentially
// over 50% bonus that Creche's can provide while other units can.
if (artyCombat again) {
morale = range(morale, 1, 6)
}
}
Great break down! I've almost finished say_morale() too which I think might help give some insight into the whole +/- display of morale as well.
I agree about moraleMod or something being a better way to describe variable. I'll just change ret value to range(Veh[vehID].morale + moraleMod, 0, 6) or something.
Nice! I've started building a morale computer in Google Sheets but it's really rough right now. I want to be able to get all permutations of actual morale just by adjusting intrinsic veh and MORALE SE values.
The division between what adjustments are calculated in morale_veh versus get_basic_offense/defense is weird. A lot of judgment calls about the order of operations need to be made to untangle all of this. Did anybody know about the artillery special treatment??
Nice! I've started building a morale computer in Google Sheets but it's really rough right now. I want to be able to get all permutations of actual morale just by adjusting intrinsic veh and MORALE SE values.
Yeah, I think that will be helpful. The say_morale() should also help get a better idea what is visible to player and inconsistency with minus and pluses.The division between what adjustments are calculated in morale_veh versus get_basic_offense/defense is weird. A lot of judgment calls about the order of operations need to be made to untangle all of this. Did anybody know about the artillery special treatment??
I forgot to add last night, I'm not 100% sure isArtyCombat is the correct name for that parameter. The other two I'm certain about: PSI and bombardment (missile or artillery). I think I thought at some point this was for pure artillery combat (vs one sided bombardment like missile or artillery hitting non-artillery unit). That's on my todo as part of clean up to identify true meaning.
math to determine the correct moraleSocialModifier value goes here. Need to do an adjustment to get the correct scale, and undo it on +2 and +3 MORALE when unit is defending. That is all default behavior.
int moraleModifier = moraleSocialModifier + moraleRuleModifier
if (inBaseWithCreche and moraleModifier < 0) { // Brood Pit logic needed here. Manual indicates Creche/Pit effects should be limited to base square.
moraleModifier /= 2 // Rounds result towards 0. Manual says negs are cancelled but code consistently "halves" negs like this.
}
if (inBaseWithRiot) { // Undocumented AFAIK and poorly indicated. Easier to understand if it's in the base where the unit is.
moraleModifier--
}
if (defending and soporificAttacker) { // Placed here due to rule flag, see below.
moraleModifier -= 2
}
if (ruleflag and moraleModifier < 0) {
// Rule flag implies it's supposed to provide absolute immunity to negative morale modifiers.
// Not used by the game's default 14 factions so probably not worth a lot of debate.
moraleModifier = 0
}
if (inBaseWithCreche) { // Manual indicates Creche/Pit effects should be limited to base square.
moraleModifier++ // Flat +1 morale is in manual and current code.
}
if (defending and inBaseWithHQ) {
// Documented but unimplemented. Would be cool.
// Thinker now has auto-HQ relocation code for the AI so it might be a marginal buff for them.
moraleModifier++
}
return range(vehMorale + moraleModifier, 0, 6)
[/font]
Ahh, ok, I missed your comment in the code regarding arty.
I've started to convert the advanced morale wiki page into a more organized and systematic treatment of what you've uncovered thus far. The probe team and native life code looked whole and consistent so I did a write up. The Creche and Brood Pit problems are too annoying to deal with right now. :D
http://alphacentauri2.info/wiki/Morale_(Advanced) (http://alphacentauri2.info/wiki/Morale_(Advanced))
Saw your latest Github push and I'm reading through say_morale now. It looks to me that the intention behind the + and - symbols were to be a visual hint of the effective morale of a unit should it actually enter combat next to its intrinsic morale and each symbol represented one unit of morale. But it's an awful system because only the least documented of all bonuses are given symbols while some others aren't. Positive MORALE SE bonuses on defense, for example. It also forgets the documented but unimplemented HQ defense. I did a quick Ctrl F search for "+" in the manual and didn't see any explanation.
https://github.com/b-casey/OpenSMACX/blob/4d9c8b8d4bb51ce2584fc5f2172e4f73ba14ecbc/src/veh.cpp#L61 (https://github.com/b-casey/OpenSMACX/blob/4d9c8b8d4bb51ce2584fc5f2172e4f73ba14ecbc/src/veh.cpp#L61)
So it starts the morale string with the morale rank or lifecycle. ;b;
Combat units with a rioting home base get a (-) to represent its morale penalty and that's the only reason why the minus appears. ;b;
If the unit is in a base that has a Creche/Pit, it then attempts to count a + for every morale upgrade it thinks the Creche is adding, I think. Sometimes it removes the riot (-) but other times it doesn't. What a mess. :doh
After some concatenation it adds a (d) if the unit is the designated defender. Never noticed that, and it's not morale-related! ;q;
Not really sure what can be done. Do people like the (++++) and (-)(+++)? Can it be better? There isn't a lot of room down there next to words like Commando or Demon Boil.
I'm nearly done with my morale calculator. Right now this is what I'm thinking based on developer intentions and rationalizing confusing mechanics. Under this algorithm, a Creche does turn out to be powerful for a low MORALE faction but exclusively when put on the defensive in a base. For example, a Gaian unit (-1 MORALE, units start at Very Green) would defend a base with a Children's Creche at the effective level of Disciplined. But outside the base, it would revert to Very Green. A Spartan unit (+2 MORALE) would defend a Creched base at Veteran level. I wonder if AI Spartans have secretly suffered the most from the Creche bug since it would punish high morale.
<snip>
Awesome work with updating wiki!
Yeah, there is definitely some funky stuff with say_morale(). This is a pretty rough state draft. It will likely need some clean up so I'd hold off on using it as a definitive guide yet.
Sweet! I've finished going through and vetting the get_basic_defense() function in assembly. Caught one mistake on my end that slipped through automated tests. The other changes in the commit were to mirror original assembly (no logic changes) and minor formatting tweak for comment.
This is only line that changed from original logic, should be a 4 not a 0 to mirror original logic:
https://github.com/b-casey/OpenSMACX/blob/master/src/veh.cpp#L388
Finished reviewing and finalizing get_basic_offense() (https://github.com/b-casey/OpenSMACX/blob/master/src/veh.cpp#L283) and say_morale() (https://github.com/b-casey/OpenSMACX/blob/master/src/veh.cpp#L61). No real changes to get_basic_offense, logic mirrors original code. I just changed the one parameter to a more generic var name to reduce confusion until it can be identified. There were some problems with say_morale that I've corrected along with code clean up. I'll see if I can find any other morale related functions related to CC/BP bugs. Otherwise, now it's just a matter of finalizing and applying bug fixes.
I realized that I'd dox myself by sharing my Google Sheet which I'm not eager to do! I'll think of something...I think you can export as an Excel doc from Google Sheets. You might want to check what type of metadata is stored on exported file (ex. gmail account as owner/creator).
Not only is this Brood Pit check never reached, it's also superfluous. MORALE SE doesn't affect lifecycles so there are no penalties to reverse.
https://github.com/b-casey/OpenSMACX/blob/master/src/veh.cpp#L2042 (https://github.com/b-casey/OpenSMACX/blob/master/src/veh.cpp#L2042)
What does this logic mean in get_basic_defense? Does it mean defending a base from wild native life gives the defender an additional +1 morale?Correct. The 0 indexed faction is AI native life.
https://github.com/b-casey/OpenSMACX/blob/master/src/veh.cpp#L386 (https://github.com/b-casey/OpenSMACX/blob/master/src/veh.cpp#L386)
Do you know where the High Morale ability (ABL_TRAINED) is accounted for? I'd expect it where I linked below but the only reference to ABL_TRAINED I found is in veh.h.It looks like it directly adds to the Veh.morale value when one of upgrade functions is called. Here are references:
https://github.com/b-casey/OpenSMACX/blob/master/src/base.cpp#L565 (https://github.com/b-casey/OpenSMACX/blob/master/src/base.cpp#L565)
I think you can export as an Excel doc from Google Sheets. You might want to check what type of metadata is stored on exported file (ex. gmail account as owner/creator).
if (has_fac_built(FAC_HEADQUARTERS, baseIDDef)) { // bug fix: per manual
morale++; // "Units in a headquarters base automatically gain +1 Morale when defending."
}
So I've been looking over everything, still haven't figured the best way to handle the SE morale. However, two simple fixes would be BP/CC double bonus and defending HQ.
Removing the double Brood Pit bonus when Children’s Creche is present is simple. I think it's pretty clear from manual and in game text files it should be an either/or situation. This is done by removing the following code segments:
From here (https://github.com/b-casey/OpenSMACX/blob/master/src/veh.cpp#L416) to here (https://github.com/b-casey/OpenSMACX/blob/master/src/veh.cpp#L420) and from here (https://github.com/b-casey/OpenSMACX/blob/master/src/veh.cpp#L347) to here (https://github.com/b-casey/OpenSMACX/blob/master/src/veh.cpp#L351).
For +1 in defending HQ, you could insert above here (https://github.com/b-casey/OpenSMACX/blob/master/src/veh.cpp#L432) the following code:Code: [Select]if (has_fac_built(FAC_HEADQUARTERS, baseIDDef)) { // bug fix: per manual
morale++; // "Units in a headquarters base automatically gain +1 Morale when defending."
}