diff --git a/artifacts/scripting/sfall opcode list.txt b/artifacts/scripting/sfall opcode list.txt index 5ffd4839..84843ea0 100644 --- a/artifacts/scripting/sfall opcode list.txt +++ b/artifacts/scripting/sfall opcode list.txt @@ -162,8 +162,8 @@ 0x81a3 - int eax_available() 0x81a4 - void set_eax_environment(int environment) -0x81a5 - void inc_npc_level(string npc) -0x8241 - int get_npc_level(string npc) +0x81a5 - void inc_npc_level(string npcName/int pid) +0x8241 - int get_npc_level(string npcName/int pid) 0x81a6 - int get_viewport_x() 0x81a7 - int get_viewport_y() diff --git a/sfall/ScriptOps/MiscOps.hpp b/sfall/ScriptOps/MiscOps.hpp index 51da348a..0284dc5b 100644 --- a/sfall/ScriptOps/MiscOps.hpp +++ b/sfall/ScriptOps/MiscOps.hpp @@ -538,152 +538,104 @@ static void __declspec(naked) EaxAvailable() { retn; } } -static char* NPCToInc; -static void _stdcall IncNPCLevel4(char* npc) { - if (_stricmp(npc, NPCToInc)) { - SafeWrite8(0x495C50, 0xE9); - SafeWrite32(0x495C51, 0x000001FC); - } else { - SafeWrite16(0x495C50, 0x840F); //Want to keep this check intact. - SafeWrite32(0x495C52, 0x000001FB); - //SafeMemSet(0x495C50, 0x90, 6); - SafeMemSet(0x495C77, 0x90, 6); //Check that the player is high enough for the npc to consider this level - //SafeMemSet(0x495C8C, 0x90, 6); //Check that the npc isn't already at its maximum level - SafeMemSet(0x495CEC, 0x90, 6); //Check that the npc hasn't already levelled up recently - if (!npcautolevel) { - SafeMemSet(0x495D22, 0x90, 6); //Random element +static const char* nameNPCToInc; +static long pidNPCToInc; +static bool onceNpcLoop; + +static void __cdecl IncNPCLevelFunc(const char* fmt, const char* name) { + TGameObj* mObj; + __asm { + push edx; + mov eax, [ebp + 0x150 - 0x1C + 16]; // ebp <- esp + mov edx, [eax]; + mov mObj, edx; + } + + if ((pidNPCToInc && (mObj && mObj->pid == pidNPCToInc)) || (!pidNPCToInc && !_stricmp(name, nameNPCToInc))) { + DebugPrintf(fmt, name); + + SafeWrite32(0x495C50, 0x01FB840F); // Want to keep this check intact. (restore) + + SafeMemSet(0x495C77, 0x90, 6); // Check that the player is high enough for the npc to consider this level + //SafeMemSet(0x495C8C, 0x90, 6); // Check that the npc isn't already at its maximum level + SafeMemSet(0x495CEC, 0x90, 6); // Check that the npc hasn't already levelled up recently + if (!npcAutoLevelEnabled) { + SafeWrite8(0x495CFB, 0xEB); // Disable random element + } + __asm mov [ebp + 0x150 - 0x28 + 16], 255; // set counter for exit loop + } else { + if (!onceNpcLoop) { + SafeWrite32(0x495C50, 0x01FCE9); // set goto next member + onceNpcLoop = true; } } + __asm pop edx; } -static void __declspec(naked) IncNPCLevel3() { - __asm { - pushad; - push eax; - call IncNPCLevel4; - popad; - push 0x495BF9; - retn; - } -} -static void _stdcall IncNPCLevel2(char* npc) { - NPCToInc = npc; - MakeJump(0x495BEB, IncNPCLevel3); //Replace the debug output with a jmp - __asm { - call partyMemberIncLevels_; - } - SafeWrite16(0x495C50, 0x840F); - SafeWrite32(0x495C52, 0x000001FB); + +static void _stdcall IncNPCLevel2() { + nameNPCToInc = opHandler.arg(0).asString(); + pidNPCToInc = opHandler.arg(0).asInt(); // set to 0 if passing npc name + if (pidNPCToInc == 0 && nameNPCToInc[0] == 0) return; + + MakeCall(0x495BF1, IncNPCLevelFunc); // Replace the debug output + __asm call partyMemberIncLevels_; + onceNpcLoop = false; + + // restore code + SafeWrite32(0x495C50, 0x01FB840F); SafeWrite16(0x495C77, 0x8C0F); SafeWrite32(0x495C79, 0x000001D4); //SafeWrite16(0x495C8C, 0x8D0F); //SafeWrite32(0x495C8E, 0x000001BF); SafeWrite16(0x495CEC, 0x850F); SafeWrite32(0x495CEE, 0x00000130); - if (!npcautolevel) { - SafeWrite16(0x495D22, 0x8F0F); - SafeWrite32(0x495D24, 0x00000129); + if (!npcAutoLevelEnabled) { + SafeWrite8(0x495CFB, 0x74); } } -static void __declspec(naked) IncNPCLevel() { - __asm { - push ebx; - push ecx; - push edx; - push edi; - mov edi, eax; - call interpretPopShort_; - mov edx, eax; - mov eax, edi; - call interpretPopLong_; - cmp dx, VAR_TYPE_STR2; - jz next; - cmp dx, VAR_TYPE_STR; - jnz end; -next: - mov ebx, eax; - mov eax, edi; - call interpretGetString_; - push eax; - call IncNPCLevel2; -end: - pop edi; - pop edx; - pop ecx; - pop ebx; - retn; - } -} -static int _stdcall get_npc_level2(DWORD numMembers, DWORD maxMembers, DWORD* members, DWORD* pids, DWORD* words, const char* name) { - for (DWORD i = 0; i < numMembers; i++) { - const char* name2; - __asm { - mov eax, members; - mov eax, [eax]; - call critter_name_ - mov name2, eax; - } - if (_stricmp(name, name2)) { - members += 4; - continue; - } - DWORD pid = ((DWORD*)members[0])[25]; - DWORD id = -1; - for (DWORD j = 0; j < maxMembers; j++) { +static void __declspec(naked) IncNPCLevel() { + _WRAP_OPCODE(IncNPCLevel2, 1, 0) +} + +static void _stdcall get_npc_level2() { + int level = -1; + DWORD pid = opHandler.arg(0).asInt(); // set to 0 if passing npc name + if (!pid) { + const char *critterName, *name = opHandler.arg(0).strValue(); + if (name[0] != 0) { + DWORD* members = *(DWORD**)_partyMemberList; + for (DWORD i = 0; i < *(DWORD*)_partyMemberCount; i++) { + __asm { + mov eax, members; + mov eax, [eax]; + call critter_name_; + mov critterName, eax; + } + if (!_stricmp(name, critterName)) { // found npc + pid = ((TGameObj*)*members)->pid; + break; + } + members += 4; + } + } + } + if (pid) { + DWORD* pids = *(DWORD**)_partyMemberPidList; + DWORD* lvlUpInfo = *(DWORD**)_partyMemberLevelUpInfoList; + for (DWORD j = 0; j < *(DWORD*)_partyMemberMaxCount; j++) { if (pids[j] == pid) { - id = j; + level = lvlUpInfo[j * 3]; break; } } - if (id == -1) break; - - return words[id * 3]; } - return 0; + opHandler.setReturn(level); } + static void __declspec(naked) get_npc_level() { - __asm { - push ebx; - push ecx; - push edx; - push edi; - mov edi, eax; - call interpretPopShort_; - mov edx, eax; - mov eax, edi; - call interpretPopLong_; - cmp dx, VAR_TYPE_STR2; - jz next; - cmp dx, VAR_TYPE_STR; - jnz fail; -next: - mov ebx, eax; - mov eax, edi; - call interpretGetString_; - push eax; - push ds:[_partyMemberLevelUpInfoList]; - push ds:[_partyMemberPidList]; - push ds:[_partyMemberList]; - push ds:[_partyMemberMaxCount]; - push ds:[_partyMemberCount]; - call get_npc_level2; - mov edx, eax; - jmp end; -fail: - xor edx, edx; -end: - mov eax, edi; - call interpretPushLong_; - mov eax, edi; - mov edx, VAR_TYPE_INT; - call interpretPushShort_; - pop edi; - pop edx; - pop ecx; - pop ebx; - retn; - } + _WRAP_OPCODE(get_npc_level2, 1, 1) } static int ParseIniSetting(const char* iniString, const char* &key, char section[], char file[]) { @@ -715,7 +667,7 @@ static char IniStrBuffer[128]; static DWORD _stdcall GetIniSetting2(const char* c, DWORD string) { const char* key; char section[33], file[67]; - + if (ParseIniSetting(c, key, section, file) < 0) { return -1; } diff --git a/sfall/main.cpp b/sfall/main.cpp index e5f6ed95..d75ad219 100644 --- a/sfall/main.cpp +++ b/sfall/main.cpp @@ -83,7 +83,7 @@ char dfModelName[65]; static const char* musicOverridePath = "data\\sound\\music\\"; DWORD AddUnarmedStatToGetYear = 0; -bool npcautolevel; +bool npcAutoLevelEnabled; static int* scriptDialog = nullptr; @@ -1134,8 +1134,8 @@ static void DllMain2() { DebugEditorInit(); - npcautolevel = GetPrivateProfileIntA("Misc", "NPCAutoLevel", 0, ini) != 0; - if (npcautolevel) { + npcAutoLevelEnabled = GetPrivateProfileIntA("Misc", "NPCAutoLevel", 0, ini) != 0; + if (npcAutoLevelEnabled) { dlog("Applying NPC autolevel patch.", DL_INIT); SafeWrite8(0x495CFB, 0xEB); // jmps 0x495D28 (skip random check) dlogr(" Done", DL_INIT); diff --git a/sfall/main.h b/sfall/main.h index b6b0e403..abceb38c 100644 --- a/sfall/main.h +++ b/sfall/main.h @@ -70,7 +70,7 @@ extern DWORD modifiedIni; extern char dmModelName[65]; extern char dfModelName[65]; -extern bool npcautolevel; +extern bool npcAutoLevelEnabled; DWORD GetAddedYears(bool isCheck = true); void SetAddedYears(DWORD years); diff --git a/sfall/skills.cpp b/sfall/skills.cpp index 1796fd91..e4137e7a 100644 --- a/sfall/skills.cpp +++ b/sfall/skills.cpp @@ -82,7 +82,7 @@ static int __fastcall SkillNegative(TGameObj* critter, int base, int skill) { } if (tagged) rawPoints *= 2; base += rawPoints; // add the negative skill points after calculating the skill level - if (base < -300) return -300; + if (base < 0) return max(-999, base); } return CheckSkillMax(critter, base); } @@ -330,7 +330,9 @@ void SkillsInit() { skills[i].base = GetPrivateProfileIntA("Skills", key, skills[i].base, file); sprintf(key, "SkillMulti%d", i); - skills[i].skillPointMulti = GetPrivateProfileIntA("Skills", key, skills[i].skillPointMulti, file); + int multi = GetPrivateProfileIntA("Skills", key, skills[i].skillPointMulti, file); + if (multi < 1) multi = 1; else if (multi > 10) multi = 10; + skills[i].skillPointMulti = multi; sprintf(key, "SkillImage%d", i); skills[i].image = GetPrivateProfileIntA("Skills", key, skills[i].image, file);