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/FalloutEngine/Variables_def.h b/sfall/FalloutEngine/Variables_def.h index 51147a36..d79cdd2d 100644 --- a/sfall/FalloutEngine/Variables_def.h +++ b/sfall/FalloutEngine/Variables_def.h @@ -130,10 +130,10 @@ VAR_(optionsButtonUpKey, DWORD) VAR_(outlined_object, GameObject*) VAR_(partyMemberAIOptions, DWORD) VAR_(partyMemberCount, DWORD) -VAR_(partyMemberLevelUpInfoList, DWORD) +VAR_(partyMemberLevelUpInfoList, DWORD*) VAR_(partyMemberList, DWORD*) // each struct - 4 integers, first integer - objPtr VAR_(partyMemberMaxCount, DWORD) -VAR_(partyMemberPidList, DWORD) +VAR_(partyMemberPidList, DWORD*) VAR_(patches, char*) VAR_(paths, PathNode*) // array VAR2(pc_crit_succ_eff, CritInfo, 9, 6) // 9 body parts, 6 effects diff --git a/sfall/Modules/Scripting/Handlers/Misc.cpp b/sfall/Modules/Scripting/Handlers/Misc.cpp index 623cc81d..0b9aba15 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.cpp +++ b/sfall/Modules/Scripting/Handlers/Misc.cpp @@ -550,44 +550,51 @@ void __declspec(naked) op_eax_available() { } } -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); +static const char* nameNPCToInc; +static long pidNPCToInc; +static bool onceNpcLoop; - //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 +static void __cdecl IncNPCLevel(const char* fmt, const char* name) { + fo::GameObject* mObj; + __asm { + push edx; + mov eax, [ebp + 0x150 - 0x1C + 16]; // ebp <- esp + mov edx, [eax]; + mov mObj, edx; + } + + if ((pidNPCToInc && (mObj && mObj->protoId == pidNPCToInc)) || (!pidNPCToInc && !_stricmp(name, nameNPCToInc))) { + fo::func::debug_printf(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) { - SafeMemSet(0x495D22, 0x90, 6); //Random element + 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; - } -} +void sf_inc_npc_level(OpcodeContext& ctx) { + nameNPCToInc = ctx.arg(0).asString(); + pidNPCToInc = ctx.arg(0).asInt(); // set to 0 if passing npc name + if (pidNPCToInc == 0 && nameNPCToInc[0] == 0) return; -static void _stdcall IncNPCLevel2(char* npc) { - NPCToInc = npc; - MakeJump(0x495BEB, IncNPCLevel3); //Replace the debug output with a jmp - __asm { - call fo::funcoffs::partyMemberIncLevels_; - } - SafeWrite16(0x495C50, 0x840F); - SafeWrite32(0x495C52, 0x000001FB); + MakeCall(0x495BF1, IncNPCLevel); // Replace the debug output + __asm call fo::funcoffs::partyMemberIncLevels_; + onceNpcLoop = false; + + // restore code + SafeWrite32(0x495C50, 0x01FB840F); SafeWrite16(0x495C77, 0x8C0F); SafeWrite32(0x495C79, 0x000001D4); //SafeWrite16(0x495C8C, 0x8D0F); @@ -595,112 +602,42 @@ static void _stdcall IncNPCLevel2(char* npc) { SafeWrite16(0x495CEC, 0x850F); SafeWrite32(0x495CEE, 0x00000130); if (!npcAutoLevelEnabled) { - SafeWrite16(0x495D22, 0x8F0F); - SafeWrite32(0x495D24, 0x00000129); + SafeWrite8(0x495CFB, 0x74); } } -void __declspec(naked) op_inc_npc_level() { - __asm { - push ebx; - push ecx; - push edx; - push edi; - mov edi, eax; - call fo::funcoffs::interpretPopShort_; - mov edx, eax; - mov eax, edi; - call fo::funcoffs::interpretPopLong_; - cmp dx, VAR_TYPE_STR2; - jz next; - cmp dx, VAR_TYPE_STR; - jnz end; -next: - mov ebx, eax; - mov eax, edi; - call fo::funcoffs::interpretGetString_; - push eax; - call IncNPCLevel2; -end: - pop edi; - pop edx; - pop ecx; - pop ebx; - retn; +void sf_get_npc_level(OpcodeContext& ctx) { + int level = -1; + DWORD pid = ctx.arg(0).asInt(); // set to 0 if passing npc name + if (!pid) { + const char *critterName, *name = ctx.arg(0).strValue(); + if (name[0] != 0) { + DWORD* members = fo::var::partyMemberList; + for (DWORD i = 0; i < fo::var::partyMemberCount; i++) { + __asm { + mov eax, members; + mov eax, [eax]; + call fo::funcoffs::critter_name_; + mov critterName, eax; + } + if (!_stricmp(name, critterName)) { // found npc + pid = ((fo::GameObject*)*members)->protoId; + break; + } + members += 4; + } + } } -} - -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 fo::funcoffs::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++) { + if (pid) { + DWORD* pids = fo::var::partyMemberPidList; + for (DWORD j = 0; j < fo::var::partyMemberMaxCount; j++) { if (pids[j] == pid) { - id = j; + level = fo::var::partyMemberLevelUpInfoList[j * 3]; break; } } - if (id == -1) break; - - return words[id * 3]; - } - return 0; -} - -void __declspec(naked) op_get_npc_level() { - __asm { - push ebx; - push ecx; - push edx; - push edi; - mov edi, eax; - call fo::funcoffs::interpretPopShort_; - mov edx, eax; - mov eax, edi; - call fo::funcoffs::interpretPopLong_; - cmp dx, VAR_TYPE_STR2; - jz next; - cmp dx, VAR_TYPE_STR; - jnz fail; -next: - mov ebx, eax; - mov eax, edi; - call fo::funcoffs::interpretGetString_; - push eax; - push ds:[FO_VAR_partyMemberLevelUpInfoList]; - push ds:[FO_VAR_partyMemberPidList]; - push ds:[FO_VAR_partyMemberList]; - push ds:[FO_VAR_partyMemberMaxCount]; - push ds:[FO_VAR_partyMemberCount]; - call get_npc_level2; - mov edx, eax; - jmp end; -fail: - xor edx, edx; -end: - mov eax, edi; - call fo::funcoffs::interpretPushLong_; - mov eax, edi; - mov edx, VAR_TYPE_INT; - call fo::funcoffs::interpretPushShort_; - pop edi; - pop edx; - pop ecx; - pop ebx; - retn; } + ctx.setReturn(level); } static int ParseIniSetting(const char* iniString, const char* &key, char section[], char file[]) { @@ -732,7 +669,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/Modules/Scripting/Handlers/Misc.h b/sfall/Modules/Scripting/Handlers/Misc.h index 42803fc7..9ae70366 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.h +++ b/sfall/Modules/Scripting/Handlers/Misc.h @@ -72,9 +72,9 @@ void __declspec() op_toggle_active_hand(); void __declspec() op_eax_available(); -void __declspec() op_inc_npc_level(); +void sf_inc_npc_level(OpcodeContext&); -void __declspec() op_get_npc_level(); +void sf_get_npc_level(OpcodeContext&); void __declspec() op_get_ini_setting(); diff --git a/sfall/Modules/Scripting/Opcodes.cpp b/sfall/Modules/Scripting/Opcodes.cpp index ebf6beb5..7708d88d 100644 --- a/sfall/Modules/Scripting/Opcodes.cpp +++ b/sfall/Modules/Scripting/Opcodes.cpp @@ -68,6 +68,7 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {0x19d, "set_sfall_global", sf_set_sfall_global, 2, false, {ARG_INTSTR, ARG_NUMBER}}, {0x19e, "get_sfall_global_int", sf_get_sfall_global_int, 1, true, {ARG_INTSTR}}, {0x19f, "get_sfall_global_float", sf_get_sfall_global_float, 1, true, {ARG_INTSTR}}, + {0x1a5, "inc_npc_level", sf_inc_npc_level, 1, false, {ARG_INTSTR}}, {0x1dc, "show_iface_tag", sf_show_iface_tag, 1, false, {ARG_INT}}, {0x1dd, "hide_iface_tag", sf_hide_iface_tag, 1, false, {ARG_INT}}, @@ -130,6 +131,7 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {0x237, "atoi", sf_atoi, 1, true, {ARG_STRING}}, {0x238, "atof", sf_atof, 1, true, {ARG_STRING}}, {0x239, "scan_array", sf_scan_array, 2, true, {ARG_OBJECT, ARG_ANY}}, + {0x241, "get_npc_level", sf_get_npc_level, 1, true, {ARG_INTSTR}}, {0x24e, "substr", sf_substr, 3, true, {ARG_STRING, ARG_INT, ARG_INT}}, {0x24f, "strlen", sf_strlen, 1, true, {ARG_STRING}}, @@ -302,7 +304,6 @@ void InitNewOpcodes() { opcodes[0x1a2] = op_set_skill_max; opcodes[0x1a3] = op_eax_available; //opcodes[0x1a4] = op_set_eax_environment; - opcodes[0x1a5] = op_inc_npc_level; opcodes[0x1a6] = op_get_viewport_x; opcodes[0x1a7] = op_get_viewport_y; opcodes[0x1a8] = op_set_viewport_x; @@ -398,7 +399,6 @@ void InitNewOpcodes() { opcodes[0x23e] = op_force_aimed_shots; opcodes[0x23f] = op_disable_aimed_shots; opcodes[0x240] = op_mark_movie_played; - opcodes[0x241] = op_get_npc_level; opcodes[0x242] = op_set_critter_skill_points; opcodes[0x243] = op_get_critter_skill_points; opcodes[0x244] = op_set_available_skill_points; diff --git a/sfall/Modules/Skills.cpp b/sfall/Modules/Skills.cpp index 934491d2..d2257e1b 100644 --- a/sfall/Modules/Skills.cpp +++ b/sfall/Modules/Skills.cpp @@ -100,11 +100,10 @@ static int __fastcall SkillNegative(fo::GameObject* critter, int base, int skill int rawPoints = skillNegPoints; if (rawPoints) { if (rawPoints < SKILL_MIN_LIMIT) rawPoints = SKILL_MIN_LIMIT; - fo::SkillInfo *skills = fo::var::skill_data; - rawPoints *= skills[skill].skillPointMulti; + rawPoints *= fo::var::skill_data[skill].skillPointMulti; if (fo::func::skill_is_tagged(skill)) 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); } @@ -373,7 +372,9 @@ void Skills::init() { 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);