Master of Orion

Master of Orion

评价数不足
Formula Documentation
由 Spud Dastardly 制作
How does ground combat even really work? What about spying? Population growth? You'll find the answers here. This mod documents several of the hidden formulas in the game used to calculate stuff. Previously this was part of my Modding and Formula Documentation guide, but those guides have now been separated because the combined guide was getting too long. More formulas will be added later.

I've included in each section which yaml files contain the moddable globals so that modders can adjust the formulas to their liking. If there are any formulas you would like to see, let me know and I'll include them.
   
奖励
收藏
已收藏
取消收藏
Armor Resilience
This is a short one. Armor resilience reduces damage taken. The formula is

Weapon Damage = Base damage * Max(Armor penetration / Armor resilience, 0.75).

This means armor can only reduce damage by up to 25% from the base damage. However, usually armor penetration is quite high, so you rarely hit that 0.75 even with heavy armor. Basically against most weapons, heavy armor halves the damage you take, making it superior to reinforced hull except against weapons with very low armor penetration.

The 0.75 is moddable in Globals.yaml. The paramter is ARMOR_PENETRATION_LOWER_CAP.
Ground Combat Algorithm
This describes in detail the exact calculations made to resolve ground combat. This is the most complicated formula included in this guide.

There are 4 things that give you your base ground combat bonus: rifles, ship armors, special techs, and racial bonuses. Special techs include anti grav harness and personal shield.

Base Infantry = 0.5 + Rifle + Ship Armor + Special Techs + Racial Bonus
Base Armor = 1 + Rifle + Ship Armor

Relevant yaml parameters:
-- PlanetDefenses.yaml
- baseCombatRating: Defines the Rifle bonus
- infantryCombatRatingBonus: Adds to the Special Techs bonuses
- infantryHitPointBonus: Adds to the hit points of infantry (base value 1)
- armorHitPointBonus: Adds to the hit points of armor barracks units (base value 2)
-- ShipModules.yaml
- groundDefenseCombatBonus: Defines the Ship Armor bonus
-- RacialPerks.yaml
- perk_ground_combat_rating: Defines the racial bonus

We have the following values.

Total infantry HP = # of infantry * HP per infantry
Total armor HP = # of armor * HP per armor
("armor" refers to armor barracks units)

x_1 = 50 + Attacker Base Infantry * (1 + Fleet Leader Bonus) * 100
x_2 = 100+ Attacker Base Armor * 100
x_3 = 55 + Defender Base Infantry * (1 + Colony Leader Bonus / 100 + Colony Leader System Bonus / 100) * 100
x_4 = 55 + Defender Base Armor * 100

Note that attacking armor gets a 100 added, whereas defending armor only gets 55. Armor units are better for attacking than defending. However, there's no way to attack with armor barracks units, so you can effectively ignore the attacking armor.

Yaml parameters for leader bonuses:
-- LeaderBonuses.yaml
- bonus_colony_ground_combat_rating: Defines the Colony Leader Bonus. Note that in vanilla, ship leaders are erroneously given this bonus, but it doesn't affect offensive ground combat units.
- bonus_ship_ground_combat_rating: Defines the Fleet Leader Bonus. This is what ship leaders should be using. Note that this value in the formula is not divided by 100. As such, you should enter it into LeaderTypes as a decimal value (e.g. 0.1 instead of 10), and you should multiply the value_cost in LeaderBonuses by 100 for it to calculate salary correctly.
- bonus_general_system_ground_combat_rating: Defines the Colony Leader System Bonus. Unused in vanilla but functional.

Next, suppose the attacker has m infantry and n armor. We're going to define a series of new constants. Generate a series of random numbers

r_1, r_2, ..., r_m and s_1, s_2, ..., s_n
with each random number between 1 and 1.5. Define

a_i = x_1* r_i
for i = 1, ..., m, and
b_i = x_2* s_i
for i = 1, ..., n.

Let x_5 = Total Attacker Value = a_1 + ... + a_m + b_1 + ... + b_m
= x_1 (r_1 + ... + r_m) + x_2 (s_1+ ... + s_n).

The Total Attacker Value is a randomly weighed sum of attacker infantry and armor ratings. Now suppose the defender has j infantry and k marines. Generate random numbers

p_1, ..., p_j and q_1, ..., q_k
between 1 and 1.5, and define

c_i = x_3* p_i
for i = 1, ..., j, and
d_i = x_4* q_i.

Let x_6 = Total Defender Value = c_1 + ... + c_j + d_1 + ... + d_k
= x_3 (p_1 + ... + p_j) + x_4 (q_1 + ... + q_k).

Now we're going to generate some more random numbers, but these are between 0 and 1. Let

t_1, ..., t_j; u_1, ..., u_k; v_1, ..., v_m; and w_1,... w_n be random numbers between 0 and 1. Define

x_7 = t_1 + ... + t_j + u_1 + ... + u_k and
x_8 = v_1 + ... + v_m + w_1 + ... + w_n.

x_7 and x_8 are just sums of a bunch of random numbers between 0 and 1. They represent the "attack rolls" for each side. Now we compare some values. For each i = 1, ..., m, if

a_i < v_i * x_6 / x_8, then subtract 1 from the total attacker infantry HP unless attacker infantry HP = 0, in which case subtract 1 from the total attacker armor HP. For each i = 1, ..., n, if

b_i < w_i * x_6 / x_8, then subtract 1 from the total attacker infantry HP unless attacker infantry HP = 0, in which case subtract 1 from the total attacker armor HP.

For each i = 1, ..., j, if
c_i < t_i * x_5 / x_7, then subtract 1 from the total defender infantry HP unless defender HP = 0, in which case subtract 1 from the total defender armor HP. For each i = 1, ..., k, if

d_i < t_i * x_5 / x_7, then subtract 1 from the total defender infantry HP unless defender HP = 0, in which case subtract 1 from the total defender armor HP.

What this means is, if each attacker doesn't roll well enough, then it dies, but the more attackers you have, the lower that chance is. Similarly for defenders. If you have enough attackers or defenders, you won't lose any units.

New attacker infantry count = ceiling ( total attacker infantry HP / HP per infantry)
New attacker armor count = ceiling (total attacker armor HP / HP per armor)
New defender infantry count = ceiling ( total defender infantry HP / HP per infantry)
New defender armor count = ceiling ( total defender armor HP / HP per armor)

(the ceiling function chooses the smallest integer larger than the input value)

If new defender infantry count + new defender armor count <= 0 and new attacker infantry count + new attacker armor count >0, then attacker wins.
Else, if new attacker infantry count + new attacker armor count <= 0, then defender wins.
Else, combat repeats using new total infantry/armor counts.
Population Growth Formula
This section describes how new population is produced on planets. To reach the next population on a planet, you build up surplus food until you hit a certain total amount, and then a new population is added. The formulas give the total amount of food needed to reach the next population.

Non-Lithovore Population Growth
If current pop is less than or equal to half max population:
Required food = Universe Multiplier * Total Growth Multiplier * (minRequiredFood + (maxRequiredFoodStart - minRequiredFood) * [2 * (Current population -1) / (Max population - 2) - 1] ^ INCREMENTAL_GROWTH_FACTOR)

If current pop is greater than half max population,
Required food = Universe Multiplier * Total Growth Multiplier * (minRequiredFood + (maxRequiredFoodEnd - minRequiredFood) * [2 * (Current population -1) / (Max population - 2) - 1] ^ INCREMENTAL_GROWTH_FACTOR)

Relevant yaml parameters:
- INCREMENTAL_GROWTH_FACTOR is located in Globals.yaml. It is set to 2 by default.
- minRequiredFoodStart is the amount of food needed to go from 1 population to 2, defined in PlanetSizeTypes.yaml
- minRequiredFood is the amount of food needed to go from half of max population to half of max + 1, defined in PlanetSizeTypes.yaml
- maxRequiredFoodEnd is the amount of food needed to go from max - 1 to max population.. Defined in PlanetSizeTypes.yaml


The multipliers are:
-- Universe multiplier = 1 + population_growth_speed, where the possible values for population_growth_speed are given in PopulationGrowthSpeeds.yaml
-- Total Growth Multiplier = max{MIN_POPULATION_GROWTH_MULTIPLIER, 1 - Population growth from structures - Population growth from tech achievements - Population growth from race traits - Leader Bonus / 100 + Growth bonus from events + Growth multiplier from resources}, where
- MIN_POPULATION_GROWTH_MULTIPLIER is in Globals.yaml (default value 0.1)
- Population growth from structures is the sum of values of all built structures with populationGrowthMultiplierBonus in ColonyStructureTypes.yaml
- Population growth from tech achievements is the bonus from Mircobiotics and Universal Antidote, defined in TechAchievements.yaml
- Population growth from racial traits is from racialperks.yaml
- Leader Bonus refers to bonus_colony_population_growth in LeaderBonuses.yaml
- Growth bonus from events refers to random events, in particular populationGrowthRequirementMultiplier from RandomEvents.yaml
- Growth multiplier from resources refers to the unused but functional parameter populationGrowthPercentualBonus in PlanetaryResourceTypes.yaml.
EDIT: The Total Growth Multiplier has been changed in patch 55.1. In short it's now (1/(1+bonuses)) instead of (1-bonuses). I'll update this section later.

Example: INCREMENTAL_GROWTH_MULTIPLIER = 2, minRequiredFoodStart = 125, minRequiredFood = 75, maxRequiredFoodEnd = 250, max population 10, current population 3, Universe multiplier on fast growth speed = 0.75, no other multipliers:

Food required = 0.75 (75 + (125 - 75) * [2 (3 - 1) / (10 - 2) - 1] ^ 2 = 65.625 food.

Food required graph for parameters above with current population = x: 0<x<=5[www.wolframalpha.com], 5<x<10[www.wolframalpha.com]

This produces a Sigmoid-like growth curve. The optimal amount of population to have for maximal population growth is always half of max population if max is even, or (max-1)/2 if max is odd.

Planets with lithovore population
In the previous section, I gave the formula for calculating the total number of required food to get to the next population. Lithovore races still need to reach this exact same food total, but the "food" or rather "life points" they produce is a constant value lithovoreLifePointProduction defined in PlanetMineralRichnessTypes.yaml.

They build up life points until they reach the required total and then add a population once they hit the total.

For mixed race planets with lithovore and nonlithovore races, the formula is
Life points added = Surplus food*non-lithovore population/total population + lithovoreLifePointProduction*lithovore population/total population.

E.g. Given 4 surplus food, 3 non lithovore population, lithovoreLifePointProduction = 6, and 5 lithovore population, the number of life points added is 4*3/8+6*5/8=5.25.

As for how it selects which race's population to add, that is determined by random chance, where if you have more of a certain race, that race has a proportionately higher chance of being selected, and the chances of selecting a given race are weighted by its population growth speed.
Spying Mission Risk Formula
This section documents how the chance of failing a spy mission is calculated. The formula is

Chance of failure = mission risk factor * (1 + defender empire security - BASE_SECURITY_FACTOR * (BASE_ESPIONAGE_RATING * spy level + tech achievements and racial bonuses))
If the mission is a colony mission, instead of empire security in the formula, you use empire security + security structure bonuses.

Empire security = leader bonus / 100 + structure empire security bonuses + (Sum of all colony security structure bonuses) / (Total # of colonies) + (1 + racial bonus)* (sum of 0.01 * agent on counterespionage level + tech achievements)

- Mission risk factor is defined in EspionageMissions.yaml
- Structure security bonuses are in ColonyStructureTypes.yaml
- Racial bonus is in RacialPerks.yaml
- BASE_SECURITY_FACTOR and BASE_ESPIONAGE_RATING are in Globals.yaml (default values 0.5 and 0.1 respectively)
- Tech achievements include neural scanner and stealth suit, defined in techachievements.yaml.

Note, global DNA scanners do add a bit to empire security, and then empire security adds back onto the colony bonus. If you had a global DNA scanner on every planet, this would give you double the bonus against colony missions.

It's always worth mentioning that stealth suit will actually increase your missions risk in the vanilla game. This is fixed by UCP.

The game rolls a number between 0 and 1 once to determine if you fail or not, and if you do fail, it randomly selects which turn you are going to fail on. The random number is selected based on the turn number and galaxy seed, so rewinding your game and trying the same mission again won't work unless you start it on a different turn (and then it still might fail).

Example 1: If you have a level 1 spy doing steal charts and the opponent has two level 1 spies on counterespionage and no other security except for a spy center (+20% system security), and suppose the opponent has 5 colonies. Then the empire security rating is 0.20 / 5 + 2 * 0.01 = 0.06 or 6%.

Suppose you have no spying bonus. Then the risk is 0.3 * (1 + 0.06 - 0.5 * (0.1)) = 0.3 * (1.01) = 0.303, so your chance of failing is 30.3%.

Example 2: You have stealth suit and a level 3 spy (no other bonuses) and are attempting to steal tech. The enemy has total empire security of 10%. Failure chance = 0.5 * (1 + 0.1 - 0.5 * (0.3 + 0.25)) = 41.25 %.
5 条留言
KeilahMartin 2024 年 12 月 4 日 下午 4:45 
I don't see the 'to hit' formula, but I believe it is:
y=100/(1+2^(-(a-d)/16)), where a is the attacker's beam attack value, and d is the defender's beam defense.
I'm not sure how range affects this.
Spud Dastardly  [作者] 2022 年 8 月 22 日 下午 6:14 
Well, yes good point. My analysis only took into account the minimum food needed to reach the next population.
rflash 2022 年 8 月 22 日 下午 12:52 
Great guide, however I do have some observations regarding the population growth conclusions.
In most cases the best way to get the fastest growth is to be at max pop/2 + 1, as long as you can use that extra pop to farm.

Some examples :
1. You have a small gaia planet, so 12 max population. Assuming a very slow growth rate and no bonuses if you are at 6 population you need 112.5 food to grow. If you are at 7 population you need 120 food, but if you farm with that extra pop even at only +2 food/turn you need 4 turns to break even.
2. You have a medium terran planet, so 13 max population. Assuming a normal growth rate and a 25% bonus if you are at 6 population you need 60.3 food to grow, but if you have 7 population you only need 61.2 food. Obviously the extra population is worth it.
DrakenKin 2020 年 7 月 30 日 上午 10:47 
One thing worth mentioning is that armor pen can INCREASE damage if the target has less armor than you have armor pen.

Weapon Damage = Base damage * Max(Armor penetration / Armor resilience, 0.75).

If we're attacking a frigate (7 armor) with mass driver (10 armor penetration) :

dmg = 12 * max (10 / 7 , 0.75)
10/7 = 1.43, it's bigger than 0.75, so we end up boosting damage :

dmg = 12 * 1.43 = 17.16 damage!

We end up with a damage boost greater than the base value we started with.
DrakenKin 2020 年 7 月 29 日 上午 8:44 
Great guide, thanks!