SetLevel's missile level is an int: both callers push the skill level as a whole dword
Ghidra v1846 · 4ca0e98
nLevel (Stack[0x8]) is declared int16_t, but both callers push the skill argument's level dword as it is: CreatingMissile 0059fe41 MOV ECX,dword ptr [ESI+0x30] / 0059fe44 PUSH ECX (call 0059fe46), MakeMissile 004cda47 MOV EAX,dword ptr [EDI+0x30] / 004cda4a PUSH EAX (call 004cda4c). On the VC8 SP1 bench (tree 3dba660) an int16_t parameter makes the compiler narrow that argument even with no cast in the source (MOVZX EAX,word ptr [ESI+0x30]), and only an int parameter gives the whole-dword load the original has. The body storing the low word (0064a1f6 MOV CX,word ptr [EBP+0xc]; 0064a1fa MOV word ptr [EAX+0xc],CX) is what VC8 makes of an int stored into a 16-bit field; SetLevel itself still matches as int. The callers' similarity does not move yet: their remaining differences are elsewhere (call order, registers).
Changes 1, applied together
Set prototype
win 0x0064a1e0signaturevoid SetLevel(D2UnitStrc * pUnit, int16_t nLevel)→void SetLevel(D2UnitStrc * pUnit, int nLevel)convention__stdcall