So is there a way of translating that into math? Is it just adding up the values of those four traits?
The actual math is done inside this pair of functions:
int __thiscall unit_personality::getOverallScore(unit_pe
int EAX:4 <RETURN>
unit_personali RCX:8 (auto) this
int32_t[50] * RDX:8 weights
unit_personality::getOverallScore
140910ae0 0 48 89 5c 24 08 MOV qword ptr [RSP + local_res8],RBX
140910ae5 0 48 89 6c 24 10 MOV qword ptr [RSP + local_res10],RBP
140910aea 0 48 89 74 24 18 MOV qword ptr [RSP + local_res18],RSI
140910aef 0 57 PUSH RDI
140910af0 008 48 83 ec 20 SUB RSP,0x20
140910af4 028 33 f6 XOR ESI,ESI
140910af6 028 48 8b fa MOV RDI,RDX
140910af9 028 0f b7 de MOVZX EBX,SI
140910afc 028 48 8b e9 MOV RBP,RCX
140910aff 028 90 NOP
LAB_140910b00 XREF[1]: 140910b34(j)
140910b00 028 44 8b 07 MOV R8D,dword ptr [RDI]
140910b03 028 45 85 c0 TEST R8D,R8D
140910b06 028 7e 0f JLE LAB_140910b17
140910b08 028 0f bf d3 MOVSX EDX,BX
140910b0b 028 48 8b cd MOV RCX,RBP
140910b0e 028 e8 3d 00 00 00 CALL unit_personality::checkTrait int checkTrait(unit_personality
140910b13 028 03 f0 ADD ESI,EAX
140910b15 028 eb 12 JMP LAB_140910b29
LAB_140910b17 XREF[1]: 140910b06(j)
140910b17 028 79 10 JNS LAB_140910b29
140910b19 028 41 f7 d8 NEG R8D
140910b1c 028 0f bf d3 MOVSX EDX,BX
140910b1f 028 48 8b cd MOV RCX,RBP
140910b22 028 e8 29 00 00 00 CALL unit_personality::checkTrait int checkTrait(unit_personality
140910b27 028 2b f0 SUB ESI,EAX
LAB_140910b29 XREF[2]: 140910b15(j), 140910b17(j)
140910b29 028 66 ff c3 INC BX
140910b2c 028 48 83 c7 04 ADD RDI,0x4
140910b30 028 66 83 fb 32 CMP BX,0x32
140910b34 028 7c ca JL LAB_140910b00
140910b36 028 48 8b 5c 24 30 MOV RBX,qword ptr [RSP + local_res8+0x28]
140910b3b 028 8b c6 MOV EAX,ESI
140910b3d 028 48 8b 74 24 40 MOV RSI,qword ptr [RSP + local_res18+0x28]
140910b42 028 48 8b 6c 24 38 MOV RBP,qword ptr [RSP + local_res10+0x28]
140910b47 028 48 83 c4 20 ADD RSP,0x20
140910b4b 008 5f POP RDI
140910b4c 0 c3 RET
...
int __thiscall unit_personality::checkTrait(unit_persona
int EAX:4 <RETURN>
unit_personali RCX:8 (auto) this
personality_fa EDX:4 type
int R8D:4 rolls
unit_personality::checkTrait
140910b50 0 48 89 5c 24 10 MOV qword ptr [RSP + local_res10],RBX
140910b55 0 57 PUSH RDI
140910b56 008 48 83 ec 20 SUB RSP,0x20
140910b5a 028 41 8b d8 MOV EBX,R8D
140910b5d 028 4c 63 c2 MOVSXD R8,EDX
140910b60 028 48 8b 91 58 MOV RDX,qword ptr [RCX + 0x158]
01 00 00
140910b67 028 42 0f b7 84 MOVZX EAX,word ptr [RCX + R8*0x2 + 0x80]
41 80 00 00 00
140910b70 028 48 85 d2 TEST RDX,RDX
140910b73 028 74 16 JZ LAB_140910b8b
140910b75 028 66 42 03 04 42 ADD AX,word ptr [RDX + R8*0x2]
140910b7a 028 79 04 JNS LAB_140910b80
140910b7c 028 33 c0 XOR EAX,EAX
140910b7e 028 eb 0b JMP LAB_140910b8b
LAB_140910b80 XREF[1]: 140910b7a(j)
140910b80 028 66 83 f8 64 CMP AX,0x64
140910b84 028 7e 05 JLE LAB_140910b8b
140910b86 028 b8 64 00 00 00 MOV EAX,0x64
LAB_140910b8b XREF[3]: 140910b73(j), 140910b7e(j),
140910b84(j)
140910b8b 028 33 ff XOR EDI,EDI
140910b8d 028 85 db TEST EBX,EBX
140910b8f 028 7e 5d JLE LAB_140910bee
LAB_140910b91 XREF[4]: 14106e98c(*), 14106e99c(*),
141ef2ec0(*), 141ef2ec8(*)
140910b91 028 48 89 74 24 30 MOV qword ptr [RSP + local_res8+0x28],RSI
140910b96 028 0f bf f0 MOVSX ESI,AX
140910b99 028 ff c6 INC ESI
140910b9b 028 0f 1f 44 00 00 NOP dword ptr [RAX + RAX*0x1]
LAB_140910ba0 XREF[1]: 140910be7(j)
140910ba0 028 83 fe 01 CMP ESI,0x1
140910ba3 028 77 04 JA LAB_140910ba9
140910ba5 028 33 c0 XOR EAX,EAX
140910ba7 028 eb 38 JMP LAB_140910be1
LAB_140910ba9 XREF[1]: 140910ba3(j)
140910ba9 028 e8 12 b5 0e 00 CALL mt_trandom int32_t mt_trandom(void)
140910bae 028 44 8b c0 MOV R8D,EAX
140910bb1 028 b8 03 00 00 00 MOV EAX,0x3
140910bb6 028 41 f7 e0 MUL R8D
140910bb9 028 41 8b c0 MOV EAX,R8D
140910bbc 028 2b c2 SUB EAX,EDX
140910bbe 028 d1 e8 SHR EAX,1
140910bc0 028 03 c2 ADD EAX,EDX
140910bc2 028 33 d2 XOR EDX,EDX
140910bc4 028 c1 e8 1e SHR EAX,0x1e
140910bc7 028 69 c0 ff ff IMUL EAX,EAX,0x7fffffff
ff 7f
140910bcd 028 44 2b c0 SUB R8D,EAX
140910bd0 028 b8 ff ff ff 7f MOV EAX,0x7fffffff
140910bd5 028 f7 f6 DIV ESI
140910bd7 028 33 d2 XOR EDX,EDX
140910bd9 028 8d 48 01 LEA ECX,[RAX + 0x1]
140910bdc 028 41 8b c0 MOV EAX,R8D
140910bdf 028 f7 f1 DIV ECX
LAB_140910be1 XREF[1]: 140910ba7(j)
140910be1 028 03 f8 ADD EDI,EAX
140910be3 028 ff cb DEC EBX
140910be5 028 85 db TEST EBX,EBX
140910be7 028 7f b7 JG LAB_140910ba0
140910be9 028 48 8b 74 24 30 MOV RSI,qword ptr [RSP + local_res8+0x28]
LAB_140910bee XREF[3]: 140910b8f(j), 141ef2ecc(*),
141ef2ed4(*)
140910bee 028 8b c7 MOV EAX,EDI
140910bf0 028 48 8b 5c 24 38 MOV RBX,qword ptr [RSP + local_res10+0x28]
140910bf5 028 48 83 c4 20 ADD RSP,0x20
140910bf9 008 5f POP RDI
140910bfa 0 c3 RET
Also, I mentioned that it's all x86 assembly, but
Ghidra is also able to produce a reasonable approximation of C code:
int __thiscall unit_personality::getOverallScore(unit_personality *this,int32_t (*weights) [50])
{
int iVar1;
short sVar2;
int iVar3;
iVar3 = 0;
sVar2 = 0;
do {
iVar1 = (*weights)[0];
if (iVar1 < 1) {
if (iVar1 < 0) {
iVar1 = checkTrait(this,(int)sVar2,-iVar1);
iVar3 -= iVar1;
}
}
else {
iVar1 = checkTrait(this,(int)sVar2,iVar1);
iVar3 += iVar1;
}
sVar2 += 1;
weights = (int32_t (*) [50])(*weights + 1);
} while (sVar2 < 0x32);
return iVar3;
}
int __thiscall
unit_personality::checkTrait(unit_personality *this,personality_facet_type type,int rolls)
{
uint16_t uVar1;
uint uVar2;
int iVar3;
uVar1 = (&(this->traits).LOVE_PROPENSITY)[type];
if (this->temporary_trait_changes != NULL) {
uVar1 += (&this->temporary_trait_changes->LOVE_PROPENSITY)[type];
if ((short)uVar1 < 0) {
uVar1 = 0;
}
else {
if (100 < (short)uVar1) {
uVar1 = 100;
}
}
}
iVar3 = 0;
if (0 < rolls) {
do {
if ((int)(short)uVar1 + 1U < 2) {
uVar2 = 0;
}
else {
uVar2 = mt_trandom();
uVar2 = (uVar2 % 0x7fffffff) /
((int)(0x7fffffff / (ulonglong)((int)(short)uVar1 + 1U)) + 1U);
}
iVar3 += uVar2;
rolls += -1;
} while (0 < rolls);
}
return iVar3;
}
The actual trait checks are
random, but during worldgen they will be under the influence of one of the worldgen seeds (probably the History one) so they'll still be consistent.
I should note that all of these functions started out with names like "FUN_140910ae0" and "FUN_140910b50" - it was only through manual analysis that the
types of all the different function parameters were identified (typically by starting from code where the data types can be trivially identified, and then tracing outwards from there) and meaningful names could be assigned to the functions themselves.
It also greatly helps that a small part of the source code for the Linux version is available (the "g_src" directory, which mainly only contains the code for rendering graphics and handling keyboard/mouse input).