From 666e5cf62d7bbcf4b74fc3907dc94db9a5d3c22e Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 23 May 2023 23:32:11 +0300 Subject: [PATCH 001/136] Fix mouse events processing See alexbatalov/fallout1-ce#55 --- src/dinput.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/dinput.cc b/src/dinput.cc index 4fae607..a5c1dfc 100644 --- a/src/dinput.cc +++ b/src/dinput.cc @@ -50,6 +50,14 @@ bool mouseDeviceUnacquire() // 0x4E053C bool mouseDeviceGetData(MouseData* mouseState) { + // CE: This function is sometimes called outside loops calling `get_input` + // and subsequently `GNW95_process_message`, so mouse events might not be + // handled by SDL yet. + // + // TODO: Move mouse events processing into `GNW95_process_message` and + // update mouse position manually. + SDL_PumpEvents(); + Uint32 buttons = SDL_GetRelativeMouseState(&(mouseState->x), &(mouseState->y)); mouseState->buttons[0] = (buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; mouseState->buttons[1] = (buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; From d641fefc1387886b7cd357b121feab4e93c3d660 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 23 May 2023 23:43:00 +0300 Subject: [PATCH 002/136] Fix open door sound See alexbatalov/fallout1-ce#71 --- src/proto_instance.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/proto_instance.cc b/src/proto_instance.cc index 8dd9483..0c57d8a 100644 --- a/src/proto_instance.cc +++ b/src/proto_instance.cc @@ -1766,7 +1766,7 @@ int _obj_use_door(Object* a1, Object* a2, int a3) animationRegisterCallback(a2, a2, (AnimationCallback*)_set_door_state_open, -1); } - const char* sfx = sfxBuildOpenName(a2, SCENERY_SOUND_EFFECT_CLOSED); + const char* sfx = sfxBuildOpenName(a2, SCENERY_SOUND_EFFECT_OPEN); animationRegisterPlaySoundEffect(a2, sfx, -1); animationRegisterAnimate(a2, ANIM_STAND, 0); From 61293bd39c0f680efb06023f4484f1ac5a149145 Mon Sep 17 00:00:00 2001 From: Vlad Date: Tue, 23 May 2023 22:45:18 +0200 Subject: [PATCH 003/136] Fix ruined stack in rm_mult_objs_from_inven and name remaining opcodes (#289) --- src/interpreter.cc | 80 ++++++++++++++++++++-------------------- src/interpreter_extra.cc | 6 +-- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/interpreter.cc b/src/interpreter.cc index 23793ac..59534ec 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -87,17 +87,17 @@ static void opLeaveCriticalSection(Program* program); static void opEnterCriticalSection(Program* program); static void opJump(Program* program); static void opCall(Program* program); -static void op801F(Program* program); -static void op801C(Program* program); -static void op801D(Program* program); -static void op8020(Program* program); -static void op8021(Program* program); -static void op8025(Program* program); -static void op8026(Program* program); -static void op8022(Program* program); -static void op8023(Program* program); -static void op8024(Program* program); -static void op801E(Program* program); +static void opPopFlags(Program* program); +static void opPopReturn(Program* program); +static void opPopExit(Program* program); +static void opPopFlagsReturn(Program* program); +static void opPopFlagsExit(Program* program); +static void opPopFlagsReturnValExit(Program* program); +static void opPopFlagsReturnValExitExtern(Program* program); +static void opPopFlagsReturnExtern(Program* program); +static void opPopFlagsExitExtern(Program* program); +static void opPopFlagsReturnValExtern(Program* program); +static void opPopAddress(Program* program); static void opAtoD(Program* program); static void opDtoA(Program* program); static void opExitProgram(Program* program); @@ -2042,7 +2042,7 @@ static void opCall(Program* program) } // 0x46B590 -static void op801F(Program* program) +static void opPopFlags(Program* program) { program->windowId = programStackPopInteger(program); program->checkWaitFunc = (InterpretCheckWaitFunc*)programStackPopPointer(program); @@ -2051,13 +2051,13 @@ static void op801F(Program* program) // pop stack 2 -> set program address // 0x46B63C -static void op801C(Program* program) +static void opPopReturn(Program* program) { program->instructionPointer = programReturnStackPopInteger(program); } // 0x46B658 -static void op801D(Program* program) +static void opPopExit(Program* program) { program->instructionPointer = programReturnStackPopInteger(program); @@ -2065,37 +2065,37 @@ static void op801D(Program* program) } // 0x46B67C -static void op8020(Program* program) +static void opPopFlagsReturn(Program* program) { - op801F(program); + opPopFlags(program); program->instructionPointer = programReturnStackPopInteger(program); } // 0x46B698 -static void op8021(Program* program) +static void opPopFlagsExit(Program* program) { - op801F(program); + opPopFlags(program); program->instructionPointer = programReturnStackPopInteger(program); program->flags |= PROGRAM_FLAG_0x40; } // 0x46B6BC -static void op8025(Program* program) +static void opPopFlagsReturnValExit(Program* program) { ProgramValue value = programStackPopValue(program); - op801F(program); + opPopFlags(program); program->instructionPointer = programReturnStackPopInteger(program); program->flags |= PROGRAM_FLAG_0x40; programStackPushValue(program, value); } // 0x46B73C -static void op8026(Program* program) +static void opPopFlagsReturnValExitExtern(Program* program) { ProgramValue value = programStackPopValue(program); - op801F(program); + opPopFlags(program); Program* v1 = (Program*)programReturnStackPopPointer(program); v1->checkWaitFunc = (InterpretCheckWaitFunc*)programReturnStackPopPointer(program); @@ -2109,9 +2109,9 @@ static void op8026(Program* program) } // 0x46B808 -static void op8022(Program* program) +static void opPopFlagsReturnExtern(Program* program) { - op801F(program); + opPopFlags(program); Program* v1 = (Program*)programReturnStackPopPointer(program); v1->checkWaitFunc = (InterpretCheckWaitFunc*)programReturnStackPopPointer(program); @@ -2121,9 +2121,9 @@ static void op8022(Program* program) } // 0x46B86C -static void op8023(Program* program) +static void opPopFlagsExitExtern(Program* program) { - op801F(program); + opPopFlags(program); Program* v1 = (Program*)programReturnStackPopPointer(program); v1->checkWaitFunc = (InterpretCheckWaitFunc*)programReturnStackPopPointer(program); @@ -2136,11 +2136,11 @@ static void op8023(Program* program) // pop value from stack 1 and push it to script popped from stack 2 // 0x46B8D8 -static void op8024(Program* program) +static void opPopFlagsReturnValExtern(Program* program) { ProgramValue value = programStackPopValue(program); - op801F(program); + opPopFlags(program); Program* v10 = (Program*)programReturnStackPopPointer(program); v10->checkWaitFunc = (InterpretCheckWaitFunc*)programReturnStackPopPointer(program); @@ -2164,7 +2164,7 @@ static void op8024(Program* program) } // 0x46BA10 -static void op801E(Program* program) +static void opPopAddress(Program* program) { programReturnStackPopValue(program); } @@ -2540,17 +2540,17 @@ void interpreterRegisterOpcodeHandlers() interpreterRegisterOpcode(OPCODE_SWAPA, opSwapReturnStack); interpreterRegisterOpcode(OPCODE_POP, opPop); interpreterRegisterOpcode(OPCODE_DUP, opDuplicate); - interpreterRegisterOpcode(OPCODE_POP_RETURN, op801C); - interpreterRegisterOpcode(OPCODE_POP_EXIT, op801D); - interpreterRegisterOpcode(OPCODE_POP_ADDRESS, op801E); - interpreterRegisterOpcode(OPCODE_POP_FLAGS, op801F); - interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN, op8020); - interpreterRegisterOpcode(OPCODE_POP_FLAGS_EXIT, op8021); - interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN_EXTERN, op8022); - interpreterRegisterOpcode(OPCODE_POP_FLAGS_EXIT_EXTERN, op8023); - interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN_VAL_EXTERN, op8024); - interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN_VAL_EXIT, op8025); - interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN_VAL_EXIT_EXTERN, op8026); + interpreterRegisterOpcode(OPCODE_POP_RETURN, opPopReturn); + interpreterRegisterOpcode(OPCODE_POP_EXIT, opPopExit); + interpreterRegisterOpcode(OPCODE_POP_ADDRESS, opPopAddress); + interpreterRegisterOpcode(OPCODE_POP_FLAGS, opPopFlags); + interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN, opPopFlagsReturn); + interpreterRegisterOpcode(OPCODE_POP_FLAGS_EXIT, opPopFlagsExit); + interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN_EXTERN, opPopFlagsReturnExtern); + interpreterRegisterOpcode(OPCODE_POP_FLAGS_EXIT_EXTERN, opPopFlagsExitExtern); + interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN_VAL_EXTERN, opPopFlagsReturnValExtern); + interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN_VAL_EXIT, opPopFlagsReturnValExit); + interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN_VAL_EXIT_EXTERN, opPopFlagsReturnValExitExtern); interpreterRegisterOpcode(OPCODE_CHECK_PROCEDURE_ARGUMENT_COUNT, opCheckProcedureArgumentCount); interpreterRegisterOpcode(OPCODE_LOOKUP_PROCEDURE_BY_NAME, opLookupStringProc); interpreterRegisterOpcode(OPCODE_POP_BASE, opPopBase); diff --git a/src/interpreter_extra.cc b/src/interpreter_extra.cc index 12f3222..b2d3295 100644 --- a/src/interpreter_extra.cc +++ b/src/interpreter_extra.cc @@ -3098,8 +3098,7 @@ static void _op_inven_cmds(Program* program) break; } } else { - // FIXME: Should be inven_cmds. - scriptPredefinedError(program, "anim", SCRIPT_ERROR_OBJECT_IS_NULL); + scriptPredefinedError(program, "inven_cmds", SCRIPT_ERROR_OBJECT_IS_NULL); } programStackPushPointer(program, item); @@ -3657,7 +3656,8 @@ static void opRemoveMultipleObjectsFromInventory(Program* program) Object* owner = static_cast(programStackPopPointer(program)); if (owner == NULL || item == NULL) { - // FIXME: Ruined stack. + scriptPredefinedError(program, "rm_mult_objs_from_inven", SCRIPT_ERROR_OBJECT_IS_NULL); + programStackPushInteger(program, 0); return; } From 42c541012c3ba8a58b9441b6bc38f9b15d1dabc1 Mon Sep 17 00:00:00 2001 From: Vasilii Rogin Date: Tue, 23 May 2023 23:50:37 +0300 Subject: [PATCH 004/136] Fix crash in _map_age_dead_critters (#258) Co-authored-by: Alexander Batalov --- src/object.cc | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/object.cc b/src/object.cc index 60b3bd3..a597a76 100644 --- a/src/object.cc +++ b/src/object.cc @@ -2133,27 +2133,18 @@ Object* objectFindFirst() { gObjectFindElevation = 0; - ObjectListNode* objectListNode; for (gObjectFindTile = 0; gObjectFindTile < HEX_GRID_SIZE; gObjectFindTile++) { - objectListNode = gObjectListHeadByTile[gObjectFindTile]; - if (objectListNode) { - break; + ObjectListNode* objectListNode = gObjectListHeadByTile[gObjectFindTile]; + while (objectListNode != NULL) { + Object* object = objectListNode->obj; + if (!artIsObjectTypeHidden(FID_TYPE(object->fid))) { + gObjectFindLastObjectListNode = objectListNode; + return object; + } + objectListNode = objectListNode->next; } } - if (gObjectFindTile == HEX_GRID_SIZE) { - gObjectFindLastObjectListNode = NULL; - return NULL; - } - - while (objectListNode != NULL) { - if (artIsObjectTypeHidden(FID_TYPE(objectListNode->obj->fid)) == 0) { - gObjectFindLastObjectListNode = objectListNode; - return objectListNode->obj; - } - objectListNode = objectListNode->next; - } - gObjectFindLastObjectListNode = NULL; return NULL; } @@ -2167,9 +2158,14 @@ Object* objectFindNext() ObjectListNode* objectListNode = gObjectFindLastObjectListNode->next; - while (gObjectFindTile < HEX_GRID_SIZE) { + while (true) { if (objectListNode == NULL) { - objectListNode = gObjectListHeadByTile[gObjectFindTile++]; + gObjectFindTile++; + if (gObjectFindTile >= HEX_GRID_SIZE) { + break; + } + + objectListNode = gObjectListHeadByTile[gObjectFindTile]; } while (objectListNode != NULL) { @@ -2219,9 +2215,14 @@ Object* objectFindNextAtElevation() ObjectListNode* objectListNode = gObjectFindLastObjectListNode->next; - while (gObjectFindTile < HEX_GRID_SIZE) { + while (true) { if (objectListNode == NULL) { - objectListNode = gObjectListHeadByTile[gObjectFindTile++]; + gObjectFindTile++; + if (gObjectFindTile >= HEX_GRID_SIZE) { + break; + } + + objectListNode = gObjectListHeadByTile[gObjectFindTile]; } while (objectListNode != NULL) { From 53a4437be951f75b10a6d4acb73137868cb59b13 Mon Sep 17 00:00:00 2001 From: Vasilii Rogin Date: Tue, 23 May 2023 23:51:04 +0300 Subject: [PATCH 005/136] Remove trailing comma after reading strParseIntWithKey (#259) --- src/string_parsers.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/string_parsers.cc b/src/string_parsers.cc index e0d4b62..092b87f 100644 --- a/src/string_parsers.cc +++ b/src/string_parsers.cc @@ -208,6 +208,10 @@ int strParseIntWithKey(char** stringPtr, const char* key, int* valuePtr, const c *(str + v4) = tmp2; *(str + v2) = tmp1; + if (**stringPtr == ',') { + *stringPtr = *stringPtr + 1; + } + return result; } From 70b0b6166444c11901b345835e4e57293b0695ec Mon Sep 17 00:00:00 2001 From: Vasilii Rogin Date: Wed, 24 May 2023 21:32:09 +0300 Subject: [PATCH 006/136] Delete critter from combat during _partyFixMultipleMembers (#277) Co-authored-by: Alexander Batalov --- src/party_member.cc | 102 ++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 56 deletions(-) diff --git a/src/party_member.cc b/src/party_member.cc index b01e441..b4474c8 100644 --- a/src/party_member.cc +++ b/src/party_member.cc @@ -6,6 +6,7 @@ #include "animation.h" #include "color.h" +#include "combat.h" #include "combat_ai.h" #include "combat_ai_defs.h" #include "config.h" @@ -80,7 +81,7 @@ static int _partyMemberPrepItemSave(Object* object); static int _partyMemberItemSave(Object* object); static int _partyMemberItemRecover(STRUCT_519DA8* a1); static int _partyMemberClearItemList(); -static int _partyFixMultipleMembers(); +static int partyFixMultipleMembers(); static int _partyMemberCopyLevelInfo(Object* object, int a2); // 0x519D9C @@ -655,7 +656,7 @@ int _partyMemberRecoverLoad() _partyStatePrepped = 0; if (!_isLoadingGame()) { - _partyFixMultipleMembers(); + partyFixMultipleMembers(); } return 0; @@ -759,7 +760,7 @@ int partyMembersLoad(File* stream) } } - _partyFixMultipleMembers(); + partyFixMultipleMembers(); for (int index = 1; index < gPartyMemberDescriptionsLength; index++) { STRU_519DBC* ptr_519DBC = &(_partyMemberLevelUpInfoList[index]); @@ -1196,16 +1197,14 @@ int partyGetBestSkillValue(int skill) } // 0x495620 -static int _partyFixMultipleMembers() +static int partyFixMultipleMembers() { debugPrint("\n\n\n[Party Members]:"); + // NOTE: Original code is slightly different (uses two nested loops). int critterCount = 0; - for (Object* obj = objectFindFirst(); obj != NULL; obj = objectFindNext()) { - if (PID_TYPE(obj->pid) == OBJ_TYPE_CRITTER) { - critterCount++; - } - + Object* obj = objectFindFirst(); + while (obj != NULL) { bool isPartyMember = false; for (int index = 1; index < gPartyMemberDescriptionsLength; index++) { if (obj->pid == gPartyMemberPids[index]) { @@ -1214,61 +1213,52 @@ static int _partyFixMultipleMembers() } } - if (!isPartyMember) { - continue; - } + if (isPartyMember) { + debugPrint("\n PM: %s", critterGetName(obj)); - debugPrint("\n PM: %s", critterGetName(obj)); - - bool v19 = false; - if (obj->sid == -1) { - v19 = true; - } else { - Object* v7 = NULL; - for (int i = 0; i < gPartyMembersLength; i++) { - if (obj->pid == gPartyMembers[i].object->pid) { - v7 = gPartyMembers[i].object; - break; + bool remove = false; + if (obj->sid == -1) { + remove = true; + } else { + // NOTE: Uninline. + Object* partyMember = partyMemberFindByPid(obj->pid); + if (partyMember != NULL && partyMember != obj) { + if (partyMember->sid == obj->sid) { + obj->sid = -1; + } + remove = true; } } - if (v7 != NULL && obj != v7) { - if (v7->sid == obj->sid) { - obj->sid = -1; + if (remove) { + // NOTE: Uninline. + if (obj != partyMemberFindByPid(obj->pid)) { + debugPrint("\nDestroying evil critter doppleganger!"); + + if (obj->sid != -1) { + scriptRemove(obj->sid); + obj->sid = -1; + } else { + if (queueRemoveEventsByType(obj, EVENT_TYPE_SCRIPT) == -1) { + debugPrint("\nERROR Removing Timed Events on FIX remove!!\n"); + } + } + + _combat_delete_critter(obj); + + objectDestroy(obj, NULL); + + // Start over. + critterCount = 0; + obj = objectFindFirst(); + continue; + } else { + debugPrint("\nError: Attempting to destroy evil critter doppleganger FAILED!"); } - v19 = true; } } - if (!v19) { - continue; - } - - Object* v10 = NULL; - for (int i = 0; i < gPartyMembersLength; i++) { - if (obj->pid == gPartyMembers[i].object->pid) { - v10 = gPartyMembers[i].object; - } - } - - // TODO: Probably wrong. - if (obj == v10) { - debugPrint("\nError: Attempting to destroy evil critter doppleganger FAILED!"); - continue; - } - - debugPrint("\nDestroying evil critter doppleganger!"); - - if (obj->sid != -1) { - scriptRemove(obj->sid); - obj->sid = -1; - } else { - if (queueRemoveEventsByType(obj, EVENT_TYPE_SCRIPT) == -1) { - debugPrint("\nERROR Removing Timed Events on FIX remove!!\n"); - } - } - - objectDestroy(obj, NULL); + obj = objectFindNext(); } for (int index = 0; index < gPartyMembersLength; index++) { From 62c5c4757c6886f7d3e5a0d3ca3d10e57b9e4c1a Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Wed, 24 May 2023 21:52:50 +0300 Subject: [PATCH 007/136] Fix comparing pointers and integers See #189 --- src/interpreter.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/interpreter.cc b/src/interpreter.cc index 59534ec..caac822 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -932,7 +932,7 @@ static void opConditionalOperatorNotEqual(Program* program) result = value[1].integerValue != value[0].integerValue; break; case VALUE_TYPE_PTR: - result = (intptr_t)(value[1].integerValue) != (intptr_t)(value[0].pointerValue); + result = (uintptr_t)(value[1].integerValue) != (uintptr_t)(value[0].pointerValue); break; default: assert(false && "Should be unreachable"); @@ -941,7 +941,7 @@ static void opConditionalOperatorNotEqual(Program* program) case VALUE_TYPE_PTR: switch (value[0].opcode) { case VALUE_TYPE_INT: - result = (intptr_t)(value[1].pointerValue) != (intptr_t)(value[0].integerValue); + result = (uintptr_t)(value[1].pointerValue) != (uintptr_t)(value[0].integerValue); break; case VALUE_TYPE_PTR: result = value[1].pointerValue != value[0].pointerValue; @@ -1028,7 +1028,7 @@ static void opConditionalOperatorEqual(Program* program) result = value[1].integerValue == value[0].integerValue; break; case VALUE_TYPE_PTR: - result = (intptr_t)(value[1].integerValue) == (intptr_t)(value[0].pointerValue); + result = (uintptr_t)(value[1].integerValue) == (uintptr_t)(value[0].pointerValue); break; default: assert(false && "Should be unreachable"); @@ -1037,7 +1037,7 @@ static void opConditionalOperatorEqual(Program* program) case VALUE_TYPE_PTR: switch (value[0].opcode) { case VALUE_TYPE_INT: - result = (intptr_t)(value[1].pointerValue) == (intptr_t)(value[0].integerValue); + result = (uintptr_t)(value[1].pointerValue) == (uintptr_t)(value[0].integerValue); break; case VALUE_TYPE_PTR: result = value[1].pointerValue == value[0].pointerValue; @@ -1131,7 +1131,7 @@ static void opConditionalOperatorLessThanEquals(Program* program) case VALUE_TYPE_PTR: switch (value[0].opcode) { case VALUE_TYPE_INT: - result = (intptr_t)value[1].pointerValue <= (intptr_t)value[0].integerValue; + result = (uintptr_t)value[1].pointerValue <= (uintptr_t)value[0].integerValue; break; default: assert(false && "Should be unreachable"); @@ -1385,7 +1385,7 @@ static void opConditionalOperatorGreaterThan(Program* program) case VALUE_TYPE_PTR: switch (value[0].opcode) { case VALUE_TYPE_INT: - result = (intptr_t)value[1].pointerValue > (intptr_t)value[0].integerValue; + result = (uintptr_t)value[1].pointerValue > (uintptr_t)value[0].integerValue; break; default: assert(false && "Should be unreachable"); From fe0d767521f70591ef4c4a19a350f95ed62de2a6 Mon Sep 17 00:00:00 2001 From: Vasilii Rogin Date: Tue, 30 May 2023 09:06:55 +0300 Subject: [PATCH 008/136] Add SFall arrays (#269) Co-authored-by: Alexander Batalov --- CMakeLists.txt | 10 +- src/game.cc | 8 + src/interpreter.cc | 46 ++- src/interpreter.h | 21 +- src/party_member.cc | 78 +++-- src/party_member.h | 4 + src/scripts.cc | 3 + src/sfall_arrays.cc | 697 +++++++++++++++++++++++++++++++++++++++++++ src/sfall_arrays.h | 34 +++ src/sfall_opcodes.cc | 208 ++++++++++++- 10 files changed, 1063 insertions(+), 46 deletions(-) create mode 100644 src/sfall_arrays.cc create mode 100644 src/sfall_arrays.h diff --git a/CMakeLists.txt b/CMakeLists.txt index dc311d2..1f3bd73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) set(EXECUTABLE_NAME fallout2-ce) -if (APPLE) +if(APPLE) if(IOS) set(CMAKE_OSX_DEPLOYMENT_TARGET "11" CACHE STRING "") set(CMAKE_OSX_ARCHITECTURES "arm64" CACHE STRING "") @@ -20,7 +20,7 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED YES) set(CMAKE_CXX_EXTENSIONS NO) -if (ANDROID) +if(ANDROID) add_library(${EXECUTABLE_NAME} SHARED) else() add_executable(${EXECUTABLE_NAME} WIN32 MACOSX_BUNDLE) @@ -271,6 +271,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC "src/sfall_lists.h" "src/sfall_opcodes.cc" "src/sfall_opcodes.h" + "src/sfall_arrays.cc" + "src/sfall_arrays.h" "src/touch.cc" "src/touch.h" ) @@ -297,7 +299,7 @@ if(WIN32) ) endif() -if (WIN32) +if(WIN32) target_sources(${EXECUTABLE_NAME} PUBLIC "os/windows/fallout2-ce.ico" "os/windows/fallout2-ce.rc" @@ -377,7 +379,7 @@ if(APPLE) else() install(TARGETS ${EXECUTABLE_NAME} DESTINATION .) - if (CPACK_BUNDLE_APPLE_CERT_APP) + if(CPACK_BUNDLE_APPLE_CERT_APP) install(CODE " execute_process(COMMAND codesign --deep --force --options runtime --sign \"${CPACK_BUNDLE_APPLE_CERT_APP}\" ${CMAKE_BINARY_DIR}/Fallout II Community Edition.app) " diff --git a/src/game.cc b/src/game.cc index 5383fa4..b7bfe0a 100644 --- a/src/game.cc +++ b/src/game.cc @@ -50,6 +50,7 @@ #include "random.h" #include "scripts.h" #include "settings.h" +#include "sfall_arrays.h" #include "sfall_config.h" #include "sfall_global_vars.h" #include "sfall_lists.h" @@ -348,6 +349,11 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4 return -1; } + if (!sfallArraysInit()) { + debugPrint("Failed on sfallArraysInit"); + return -1; + } + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_MISC, &gMiscMessageList); return 0; @@ -395,6 +401,7 @@ void gameReset() sfallGlobalVarsReset(); sfallListsReset(); messageListRepositoryReset(); + sfallArraysReset(); } // 0x442C34 @@ -403,6 +410,7 @@ void gameExit() debugPrint("\nGame Exit\n"); // SFALL + sfallArraysExit(); sfallListsExit(); sfallGlobalVarsExit(); premadeCharactersExit(); diff --git a/src/interpreter.cc b/src/interpreter.cc index caac822..3653f4d 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -3252,7 +3252,7 @@ void* programReturnStackPopPointer(Program* program) return programValue.pointerValue; } -bool ProgramValue::isEmpty() +bool ProgramValue::isEmpty() const { switch (opcode) { case VALUE_TYPE_INT: @@ -3269,19 +3269,19 @@ bool ProgramValue::isEmpty() } // Matches Sfall implementation. -bool ProgramValue::isInt() +bool ProgramValue::isInt() const { return opcode == VALUE_TYPE_INT; } // Matches Sfall implementation. -bool ProgramValue::isFloat() +bool ProgramValue::isFloat() const { return opcode == VALUE_TYPE_FLOAT; } // Matches Sfall implementation. -float ProgramValue::asFloat() +float ProgramValue::asFloat() const { switch (opcode) { case VALUE_TYPE_INT: @@ -3293,4 +3293,42 @@ float ProgramValue::asFloat() } } +bool ProgramValue::isString() const +{ + return opcode == VALUE_TYPE_STRING || opcode == VALUE_TYPE_DYNAMIC_STRING; +} + +ProgramValue::ProgramValue() +{ + opcode = VALUE_TYPE_INT; + integerValue = 0; +} +ProgramValue::ProgramValue(int value) +{ + opcode = VALUE_TYPE_INT; + integerValue = value; +}; +ProgramValue::ProgramValue(Object* value) +{ + opcode = VALUE_TYPE_PTR; + pointerValue = value; +}; + +bool ProgramValue::isPointer() const +{ + return opcode == VALUE_TYPE_PTR; +} + +int ProgramValue::asInt() const +{ + switch (opcode) { + case VALUE_TYPE_INT: + return integerValue; + case VALUE_TYPE_FLOAT: + return static_cast(floatValue); + default: + return 0; + } +} + } // namespace fallout diff --git a/src/interpreter.h b/src/interpreter.h index f84347f..835e2fd 100644 --- a/src/interpreter.h +++ b/src/interpreter.h @@ -1,6 +1,7 @@ #ifndef INTERPRETER_H #define INTERPRETER_H +#include "object.h" #include #include @@ -140,7 +141,12 @@ typedef struct Procedure { int field_14; } Procedure; -typedef struct ProgramValue { +class ProgramValue { +public: + ProgramValue(); + ProgramValue(int value); + ProgramValue(Object* value); + opcode_t opcode; union { int integerValue; @@ -148,11 +154,14 @@ typedef struct ProgramValue { void* pointerValue; }; - bool isEmpty(); - bool isInt(); - bool isFloat(); - float asFloat(); -} ProgramValue; + bool isEmpty() const; + bool isInt() const; + bool isFloat() const; + bool isString() const; + float asFloat() const; + bool isPointer() const; + int asInt() const; +}; typedef std::vector ProgramStack; diff --git a/src/party_member.cc b/src/party_member.cc index b4474c8..16aabc6 100644 --- a/src/party_member.cc +++ b/src/party_member.cc @@ -64,22 +64,22 @@ typedef struct STRU_519DBC { int field_8; // early what? } STRU_519DBC; -typedef struct STRUCT_519DA8 { +typedef struct PartyMemberListItem { Object* object; Script* script; int* vars; - struct STRUCT_519DA8* next; -} STRUCT_519DA8; + struct PartyMemberListItem* next; +} PartyMemberListItem; static int partyMemberGetDescription(Object* object, PartyMemberDescription** partyMemberDescriptionPtr); static void partyMemberDescriptionInit(PartyMemberDescription* partyMemberDescription); -static int _partyMemberPrepLoadInstance(STRUCT_519DA8* a1); -static int _partyMemberRecoverLoadInstance(STRUCT_519DA8* a1); +static int _partyMemberPrepLoadInstance(PartyMemberListItem* a1); +static int _partyMemberRecoverLoadInstance(PartyMemberListItem* a1); static int _partyMemberNewObjID(); static int _partyMemberNewObjIDRecurseFind(Object* object, int objectId); static int _partyMemberPrepItemSave(Object* object); static int _partyMemberItemSave(Object* object); -static int _partyMemberItemRecover(STRUCT_519DA8* a1); +static int _partyMemberItemRecover(PartyMemberListItem* a1); static int _partyMemberClearItemList(); static int partyFixMultipleMembers(); static int _partyMemberCopyLevelInfo(Object* object, int a2); @@ -91,12 +91,12 @@ int gPartyMemberDescriptionsLength = 0; int* gPartyMemberPids = NULL; // -static STRUCT_519DA8* _itemSaveListHead = NULL; +static PartyMemberListItem* _itemSaveListHead = NULL; // List of party members, it's length is [gPartyMemberDescriptionsLength] + 20. // // 0x519DA8 -static STRUCT_519DA8* gPartyMembers = NULL; +PartyMemberListItem* gPartyMembers = NULL; // Number of critters added to party. // @@ -150,7 +150,7 @@ int partyMembersInit() memset(gPartyMemberPids, 0, sizeof(*gPartyMemberPids) * gPartyMemberDescriptionsLength); - gPartyMembers = (STRUCT_519DA8*)internal_malloc(sizeof(*gPartyMembers) * (gPartyMemberDescriptionsLength + 20)); + gPartyMembers = (PartyMemberListItem*)internal_malloc(sizeof(*gPartyMembers) * (gPartyMemberDescriptionsLength + 20)); if (gPartyMembers == NULL) { goto err; } @@ -379,7 +379,7 @@ int partyMemberAdd(Object* object) } for (int index = 0; index < gPartyMembersLength; index++) { - STRUCT_519DA8* partyMember = &(gPartyMembers[index]); + PartyMemberListItem* partyMember = &(gPartyMembers[index]); if (partyMember->object == object || partyMember->object->pid == object->pid) { return 0; } @@ -390,7 +390,7 @@ int partyMemberAdd(Object* object) return -1; } - STRUCT_519DA8* partyMember = &(gPartyMembers[gPartyMembersLength]); + PartyMemberListItem* partyMember = &(gPartyMembers[gPartyMembersLength]); partyMember->object = object; partyMember->script = NULL; partyMember->vars = NULL; @@ -435,7 +435,7 @@ int partyMemberRemove(Object* object) int index; for (index = 1; index < gPartyMembersLength; index++) { - STRUCT_519DA8* partyMember = &(gPartyMembers[index]); + PartyMemberListItem* partyMember = &(gPartyMembers[index]); if (partyMember->object == object) { break; } @@ -480,7 +480,7 @@ int _partyMemberPrepSave() _partyStatePrepped = 1; for (int index = 0; index < gPartyMembersLength; index++) { - STRUCT_519DA8* ptr = &(gPartyMembers[index]); + PartyMemberListItem* ptr = &(gPartyMembers[index]); if (index > 0) { ptr->object->flags &= ~(OBJECT_NO_REMOVE | OBJECT_NO_SAVE); @@ -499,7 +499,7 @@ int _partyMemberPrepSave() int _partyMemberUnPrepSave() { for (int index = 0; index < gPartyMembersLength; index++) { - STRUCT_519DA8* ptr = &(gPartyMembers[index]); + PartyMemberListItem* ptr = &(gPartyMembers[index]); if (index > 0) { ptr->object->flags |= (OBJECT_NO_REMOVE | OBJECT_NO_SAVE); @@ -523,7 +523,7 @@ int partyMembersSave(File* stream) if (fileWriteInt32(stream, _partyMemberItemCount) == -1) return -1; for (int index = 1; index < gPartyMembersLength; index++) { - STRUCT_519DA8* partyMember = &(gPartyMembers[index]); + PartyMemberListItem* partyMember = &(gPartyMembers[index]); if (fileWriteInt32(stream, partyMember->object->id) == -1) return -1; } @@ -547,7 +547,7 @@ int _partyMemberPrepLoad() _partyStatePrepped = 1; for (int index = 0; index < gPartyMembersLength; index++) { - STRUCT_519DA8* ptr_519DA8 = &(gPartyMembers[index]); + PartyMemberListItem* ptr_519DA8 = &(gPartyMembers[index]); if (_partyMemberPrepLoadInstance(ptr_519DA8) != 0) { return -1; } @@ -558,7 +558,7 @@ int _partyMemberPrepLoad() // partyMemberPrepLoadInstance // 0x49480C -static int _partyMemberPrepLoadInstance(STRUCT_519DA8* a1) +static int _partyMemberPrepLoadInstance(PartyMemberListItem* a1) { Object* obj = a1->object; @@ -643,7 +643,7 @@ int _partyMemberRecoverLoad() debugPrint("[Party Member %d]: %s\n", index, critterGetName(gPartyMembers[index].object)); } - STRUCT_519DA8* v6 = _itemSaveListHead; + PartyMemberListItem* v6 = _itemSaveListHead; while (v6 != NULL) { _itemSaveListHead = v6->next; @@ -664,7 +664,7 @@ int _partyMemberRecoverLoad() // partyMemberRecoverLoadInstance // 0x494A88 -static int _partyMemberRecoverLoadInstance(STRUCT_519DA8* a1) +static int _partyMemberRecoverLoadInstance(PartyMemberListItem* a1) { if (a1->script == NULL) { showMesageBox("\n Error!: partyMemberRecoverLoadInstance: No script!"); @@ -801,7 +801,7 @@ int _partyMemberSyncPosition() int n = 0; int distance = 2; for (int index = 1; index < gPartyMembersLength; index++) { - STRUCT_519DA8* partyMember = &(gPartyMembers[index]); + PartyMemberListItem* partyMember = &(gPartyMembers[index]); Object* partyMemberObj = partyMember->object; if ((partyMemberObj->flags & OBJECT_HIDDEN) == 0 && PID_TYPE(partyMemberObj->pid) == OBJ_TYPE_CRITTER) { int rotation; @@ -833,7 +833,7 @@ int _partyMemberRestingHeal(int a1) } for (int index = 0; index < gPartyMembersLength; index++) { - STRUCT_519DA8* partyMember = &(gPartyMembers[index]); + PartyMemberListItem* partyMember = &(gPartyMembers[index]); if (PID_TYPE(partyMember->object->pid) == OBJ_TYPE_CRITTER) { int healingRate = critterGetStat(partyMember->object, STAT_HEALING_RATE); critterAdjustHitPoints(partyMember->object, v1 * healingRate); @@ -860,7 +860,7 @@ Object* partyMemberFindByPid(int pid) bool _isPotentialPartyMember(Object* object) { for (int index = 0; index < gPartyMembersLength; index++) { - STRUCT_519DA8* partyMember = &(gPartyMembers[index]); + PartyMemberListItem* partyMember = &(gPartyMembers[index]); if (partyMember->object->pid == gPartyMemberPids[index]) { return true; } @@ -976,7 +976,7 @@ static int _partyMemberNewObjIDRecurseFind(Object* obj, int objectId) int _partyMemberPrepItemSaveAll() { for (int partyMemberIndex = 0; partyMemberIndex < gPartyMembersLength; partyMemberIndex++) { - STRUCT_519DA8* partyMember = &(gPartyMembers[partyMemberIndex]); + PartyMemberListItem* partyMember = &(gPartyMembers[partyMemberIndex]); Inventory* inventory = &(partyMember->object->data.inventory); for (int inventoryItemIndex = 0; inventoryItemIndex < inventory->length; inventoryItemIndex++) { @@ -1025,7 +1025,7 @@ static int _partyMemberItemSave(Object* object) object->id = script->field_1C; } - STRUCT_519DA8* node = (STRUCT_519DA8*)internal_malloc(sizeof(*node)); + PartyMemberListItem* node = (PartyMemberListItem*)internal_malloc(sizeof(*node)); if (node == NULL) { showMesageBox("\n Error!: partyMemberItemSave: Out of memory!"); exit(1); @@ -1053,7 +1053,7 @@ static int _partyMemberItemSave(Object* object) node->vars = NULL; } - STRUCT_519DA8* temp = _itemSaveListHead; + PartyMemberListItem* temp = _itemSaveListHead; _itemSaveListHead = node; node->next = temp; } @@ -1069,7 +1069,7 @@ static int _partyMemberItemSave(Object* object) // partyMemberItemRecover // 0x495388 -static int _partyMemberItemRecover(STRUCT_519DA8* a1) +static int _partyMemberItemRecover(PartyMemberListItem* a1) { int sid = -1; if (scriptAdd(&sid, SCRIPT_TYPE_ITEM) == -1) { @@ -1108,7 +1108,7 @@ static int _partyMemberItemRecover(STRUCT_519DA8* a1) static int _partyMemberClearItemList() { while (_itemSaveListHead != NULL) { - STRUCT_519DA8* node = _itemSaveListHead; + PartyMemberListItem* node = _itemSaveListHead; _itemSaveListHead = _itemSaveListHead->next; if (node->script != NULL) { @@ -1262,7 +1262,7 @@ static int partyFixMultipleMembers() } for (int index = 0; index < gPartyMembersLength; index++) { - STRUCT_519DA8* partyMember = &(gPartyMembers[index]); + PartyMemberListItem* partyMember = &(gPartyMembers[index]); Script* script; if (scriptGetScript(partyMember->object->sid, &script) != -1) { @@ -1454,7 +1454,7 @@ bool partyMemberSupportsChemUse(Object* object, int chemUse) int _partyMemberIncLevels() { int i; - STRUCT_519DA8* ptr; + PartyMemberListItem* ptr; Object* obj; PartyMemberDescription* party_member; const char* name; @@ -1622,7 +1622,7 @@ static int _partyMemberCopyLevelInfo(Object* critter, int a2) bool partyIsAnyoneCanBeHealedByRest() { for (int index = 1; index < gPartyMembersLength; index++) { - STRUCT_519DA8* ptr = &(gPartyMembers[index]); + PartyMemberListItem* ptr = &(gPartyMembers[index]); Object* object = ptr->object; if (PID_TYPE(object->pid) != OBJ_TYPE_CRITTER) continue; @@ -1649,7 +1649,7 @@ int partyGetMaxWoundToHealByRest() int maxWound = 0; for (int index = 1; index < gPartyMembersLength; index++) { - STRUCT_519DA8* ptr = &(gPartyMembers[index]); + PartyMemberListItem* ptr = &(gPartyMembers[index]); Object* object = ptr->object; if (PID_TYPE(object->pid) != OBJ_TYPE_CRITTER) continue; @@ -1670,4 +1670,20 @@ int partyGetMaxWoundToHealByRest() return maxWound; } +std::vector get_all_party_members_objects(bool include_hidden) +{ + std::vector value; + value.reserve(gPartyMembersLength); + for (int index = 0; index < gPartyMembersLength; index++) { + auto object = gPartyMembers[index].object; + if (include_hidden + || (PID_TYPE(object->pid) == OBJ_TYPE_CRITTER + && !critterIsDead(object) + && (object->flags & OBJECT_HIDDEN) == 0)) { + value.push_back(object); + } + } + return value; +} + } // namespace fallout diff --git a/src/party_member.h b/src/party_member.h index 84635aa..7129749 100644 --- a/src/party_member.h +++ b/src/party_member.h @@ -1,8 +1,11 @@ #ifndef PARTY_MEMBER_H #define PARTY_MEMBER_H +#include + #include "db.h" #include "obj_types.h" +#include "scripts.h" namespace fallout { @@ -42,6 +45,7 @@ bool partyMemberSupportsChemUse(Object* object, int chemUse); int _partyMemberIncLevels(); bool partyIsAnyoneCanBeHealedByRest(); int partyGetMaxWoundToHealByRest(); +std::vector get_all_party_members_objects(bool include_hidden); } // namespace fallout diff --git a/src/scripts.cc b/src/scripts.cc index 7a35d39..7de46fd 100644 --- a/src/scripts.cc +++ b/src/scripts.cc @@ -29,6 +29,7 @@ #include "proto.h" #include "proto_instance.h" #include "queue.h" +#include "sfall_arrays.h" #include "sfall_config.h" #include "stat.h" #include "svga.h" @@ -1027,6 +1028,8 @@ int scriptsHandleRequests() inventoryOpenStealing(gScriptsRequestedStealingBy, gScriptsRequestedStealingFrom); } + DeleteAllTempArrays(); + return 0; } diff --git a/src/sfall_arrays.cc b/src/sfall_arrays.cc new file mode 100644 index 0000000..e074b4c --- /dev/null +++ b/src/sfall_arrays.cc @@ -0,0 +1,697 @@ +#include "sfall_arrays.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "interpreter.h" + +namespace fallout { + +static constexpr ArrayId kInitialArrayId = 1; + +#define ARRAY_MAX_STRING (255) // maximum length of string to be stored as array key or value +#define ARRAY_MAX_SIZE (100000) // maximum number of array elements, + +// special actions for arrays using array_resize operator +#define ARRAY_ACTION_SORT (-2) +#define ARRAY_ACTION_RSORT (-3) +#define ARRAY_ACTION_REVERSE (-4) +#define ARRAY_ACTION_SHUFFLE (-5) + +template +static void ListSort(std::vector& arr, int type, Compare cmp) +{ + switch (type) { + case ARRAY_ACTION_SORT: // sort ascending + std::sort(arr.begin(), arr.end(), cmp); + break; + case ARRAY_ACTION_RSORT: // sort descending + std::sort(arr.rbegin(), arr.rend(), cmp); + break; + case ARRAY_ACTION_REVERSE: // reverse elements + std::reverse(arr.rbegin(), arr.rend()); + break; + case ARRAY_ACTION_SHUFFLE: // shuffle elements + std::random_device rd; + std::mt19937 g(rd()); + std::shuffle(arr.begin(), arr.end(), g); + break; + } +} + +enum class ArrayElementType { + INT, + FLOAT, + STRING, + POINTER +}; + +/** + * This is mostly the same as ProgramValue but it owns strings. + * + * This is done because when we pop dynamic string element from + * the stack we decrease ref count for this string and it memory + * can be freed. + * + * In theory arrays can be shared between programs so we also + * have to copy static strings. + * + */ +class ArrayElement { +public: + ArrayElement() + : type(ArrayElementType::INT) + , value({ 0 }) + { + } + + ArrayElement(const ArrayElement& other) = delete; + + ArrayElement& operator=(const ArrayElement& rhs) = delete; + + ArrayElement(ArrayElement&& other) noexcept + : type(ArrayElementType::INT) + , value({ 0 }) + { + std::swap(type, other.type); + std::swap(value, other.value); + } + + ArrayElement& operator=(ArrayElement&& other) noexcept + { + std::swap(type, other.type); + std::swap(value, other.value); + return *this; + } + + ArrayElement(ProgramValue programValue, Program* program) + { + switch (programValue.opcode) { + case VALUE_TYPE_INT: + type = ArrayElementType::INT; + value.integerValue = programValue.integerValue; + break; + case VALUE_TYPE_FLOAT: + type = ArrayElementType::FLOAT; + value.floatValue = programValue.floatValue; + break; + case VALUE_TYPE_PTR: + type = ArrayElementType::POINTER; + value.pointerValue = programValue.pointerValue; + break; + case VALUE_TYPE_STRING: + case VALUE_TYPE_DYNAMIC_STRING: + setString(programGetString(program, programValue.opcode, programValue.integerValue), -1); + break; + default: + type = ArrayElementType::INT; + value.integerValue = 0; + break; + } + } + + ArrayElement(const char* str) + { + setString(str, -1); + } + + ArrayElement(const char* str, size_t sLen) + { + setString(str, sLen); + } + + ProgramValue toValue(Program* program) const + { + ProgramValue out; + switch (type) { + case ArrayElementType::INT: + out.opcode = VALUE_TYPE_INT; + out.integerValue = value.integerValue; + break; + case ArrayElementType::FLOAT: + out.opcode = VALUE_TYPE_FLOAT; + out.floatValue = value.floatValue; + break; + case ArrayElementType::POINTER: + out.opcode = VALUE_TYPE_PTR; + out.pointerValue = value.pointerValue; + break; + case ArrayElementType::STRING: + out.opcode = VALUE_TYPE_DYNAMIC_STRING; + out.integerValue = programPushString(program, value.stringValue); + break; + } + return out; + } + + bool operator<(ArrayElement const& other) const + { + if (type != other.type) { + return type < other.type; + } + + switch (type) { + case ArrayElementType::INT: + return value.integerValue < other.value.integerValue; + case ArrayElementType::FLOAT: + return value.floatValue < other.value.floatValue; + case ArrayElementType::POINTER: + return value.pointerValue < other.value.pointerValue; + case ArrayElementType::STRING: + return strcmp(value.stringValue, other.value.stringValue) < 0; + default: + return false; + } + } + + bool operator==(ArrayElement const& other) const + { + if (type != other.type) { + return false; + } + + switch (type) { + case ArrayElementType::INT: + return value.integerValue == other.value.integerValue; + case ArrayElementType::FLOAT: + return value.floatValue == other.value.floatValue; + case ArrayElementType::POINTER: + return value.pointerValue == other.value.pointerValue; + case ArrayElementType::STRING: + return strcmp(value.stringValue, other.value.stringValue) == 0; + default: + return false; + } + } + + ~ArrayElement() + { + if (type == ArrayElementType::STRING) { + free(value.stringValue); + } + } + +private: + void setString(const char* str, size_t sLen) + { + type = ArrayElementType::STRING; + + if (sLen == -1) { + sLen = strlen(str); + } + + if (sLen >= ARRAY_MAX_STRING) { + sLen = ARRAY_MAX_STRING - 1; // memory safety + } + + value.stringValue = (char*)malloc(sLen + 1); + memcpy(value.stringValue, str, sLen); + value.stringValue[sLen] = '\0'; + } + + ArrayElementType type; + union { + int integerValue; + float floatValue; + char* stringValue; + void* pointerValue; + } value; +}; + +class SFallArray { +public: + SFallArray(unsigned int flags) + : flags(flags) + { + } + virtual ~SFallArray() = default; + virtual ProgramValue GetArrayKey(int index, Program* program) = 0; + virtual ProgramValue GetArray(const ProgramValue& key, Program* program) = 0; + virtual void SetArray(const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program) = 0; + virtual void SetArray(const ProgramValue& key, ArrayElement&& val, bool allowUnset) = 0; + virtual void ResizeArray(int newLen) = 0; + virtual ProgramValue ScanArray(const ProgramValue& value, Program* program) = 0; + virtual int size() = 0; + + bool isReadOnly() const + { + return (flags & SFALL_ARRAYFLAG_CONSTVAL) != 0; + } + +protected: + unsigned int flags; +}; + +class SFallArrayList : public SFallArray { +public: + SFallArrayList() = delete; + + SFallArrayList(unsigned int len, unsigned int flags) + : SFallArray(flags) + { + values.resize(len); + } + + int size() + { + return static_cast(values.size()); + } + + ProgramValue GetArrayKey(int index, Program* program) + { + if (index < -1 || index > size()) { + return ProgramValue(0); + } + + if (index == -1) { // special index to indicate if array is associative + return ProgramValue(0); + } + + return ProgramValue(index); + } + + ProgramValue GetArray(const ProgramValue& key, Program* program) + { + auto index = key.asInt(); + if (index < 0 || index >= size()) { + return ProgramValue(0); + } + + return values[index].toValue(program); + } + + void SetArray(const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program) + { + SetArray(key, ArrayElement { val, program }, allowUnset); + } + + void SetArray(const ProgramValue& key, ArrayElement&& val, bool allowUnset) + { + if (key.isInt()) { + auto index = key.asInt(); + if (index >= 0 && index < size()) { + std::swap(values[index], val); + } + } + } + + void ResizeArray(int newLen) + { + if (newLen == -1 || size() == newLen) { + return; + } + + if (newLen == 0) { + values.clear(); + } else if (newLen > 0) { + if (newLen > ARRAY_MAX_SIZE) { + newLen = ARRAY_MAX_SIZE; // safety + } + + values.resize(newLen); + } else if (newLen >= ARRAY_ACTION_SHUFFLE) { + ListSort(values, newLen, std::less()); + } + } + + ProgramValue ScanArray(const ProgramValue& value, Program* program) + { + auto element = ArrayElement { value, program }; + for (int i = 0; i < size(); i++) { + if (element == values[i]) { + return ProgramValue(i); + } + } + return ProgramValue(-1); + } + +private: + std::vector values; +}; + +class SFallArrayAssoc : public SFallArray { +public: + SFallArrayAssoc() = delete; + + SFallArrayAssoc(unsigned int flags) + : SFallArray(flags) + { + } + + int size() + { + return static_cast(pairs.size()); + } + + ProgramValue GetArrayKey(int index, Program* program) + { + if (index < -1 || index > size()) { + return ProgramValue(0); + } + + if (index == -1) { // special index to indicate if array is associative + return ProgramValue(1); + } + + return pairs[index].key.toValue(program); + } + + ProgramValue GetArray(const ProgramValue& key, Program* program) + { + auto keyEl = ArrayElement { key, program }; + auto it = std::find_if(pairs.begin(), pairs.end(), [&keyEl](const KeyValuePair& pair) -> bool { + return pair.key == keyEl; + }); + + if (it == pairs.end()) { + return ProgramValue(0); + } + + auto index = it - pairs.begin(); + return pairs[index].value.toValue(program); + } + + void SetArray(const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program) + { + auto keyEl = ArrayElement { key, program }; + auto it = std::find_if(pairs.begin(), pairs.end(), [&keyEl](const KeyValuePair& pair) -> bool { + return pair.key == keyEl; + }); + + if (it != pairs.end() && isReadOnly()) { + // don't update value of key + return; + } + + if (allowUnset && !isReadOnly() && val.isInt() && val.asInt() == 0) { + // after assigning zero to a key, no need to store it, because "get_array" returns 0 for non-existent keys: try unset + if (it != pairs.end()) { + pairs.erase(it); + } + } else { + if (it == pairs.end()) { + // size check + if (size() >= ARRAY_MAX_SIZE) { + return; + } + + pairs.push_back(KeyValuePair { std::move(keyEl), ArrayElement { val, program } }); + } else { + auto index = it - pairs.begin(); + pairs[index].value = ArrayElement { val, program }; + } + } + } + + void SetArray(const ProgramValue& key, ArrayElement&& val, bool allowUnset) + { + assert(false && "This method is not used for associative arrays thus it is not implemented"); + } + + void ResizeArray(int newLen) + { + if (newLen == -1 || size() == newLen) { + return; + } + + // only allow to reduce number of elements (adding range of elements is meaningless for maps) + if (newLen >= 0 && newLen < size()) { + pairs.resize(newLen); + } else if (newLen < 0) { + if (newLen < (ARRAY_ACTION_SHUFFLE - 2)) return; + MapSort(newLen); + } + } + + ProgramValue ScanArray(const ProgramValue& value, Program* program) + { + auto valueEl = ArrayElement { value, program }; + auto it = std::find_if(pairs.begin(), pairs.end(), [&valueEl](const KeyValuePair& pair) { + return pair.value == valueEl; + }); + + if (it == pairs.end()) { + return ProgramValue(-1); + } + + return it->key.toValue(program); + } + +private: + struct KeyValuePair { + ArrayElement key; + ArrayElement value; + }; + + void MapSort(int type) + { + bool sortByValue = false; + if (type < ARRAY_ACTION_SHUFFLE) { + type += 4; + sortByValue = true; + } + + if (sortByValue) { + ListSort(pairs, type, [](const KeyValuePair& a, const KeyValuePair& b) -> bool { + return a.value < b.value; + }); + } else { + ListSort(pairs, type, [](const KeyValuePair& a, const KeyValuePair& b) -> bool { + return a.key < b.key; + }); + } + } + + std::vector pairs; +}; + +struct SfallArraysState { + std::unordered_map> arrays; + std::unordered_set temporaryArrayIds; + int nextArrayId = kInitialArrayId; +}; + +static SfallArraysState* _state = nullptr; + +bool sfallArraysInit() +{ + _state = new (std::nothrow) SfallArraysState(); + if (_state == nullptr) { + return false; + } + + return true; +} + +void sfallArraysReset() +{ + if (_state != nullptr) { + _state->arrays.clear(); + _state->temporaryArrayIds.clear(); + _state->nextArrayId = kInitialArrayId; + } +} + +void sfallArraysExit() +{ + if (_state != nullptr) { + delete _state; + } +} + +ArrayId CreateArray(int len, unsigned int flags) +{ + flags = (flags & ~1); // reset 1 bit + + if (len < 0) { + flags |= SFALL_ARRAYFLAG_ASSOC; + } else if (len > ARRAY_MAX_SIZE) { + len = ARRAY_MAX_SIZE; // safecheck + } + + ArrayId arrayId = _state->nextArrayId++; + + if (flags & SFALL_ARRAYFLAG_ASSOC) { + _state->arrays.emplace(std::make_pair(arrayId, std::make_unique(flags))); + } else { + _state->arrays.emplace(std::make_pair(arrayId, std::make_unique(len, flags))); + } + + return arrayId; +} + +ArrayId CreateTempArray(int len, unsigned int flags) +{ + ArrayId arrayId = CreateArray(len, flags); + _state->temporaryArrayIds.insert(arrayId); + return arrayId; +} + +SFallArray* get_array_by_id(ArrayId arrayId) +{ + auto it = _state->arrays.find(arrayId); + if (it == _state->arrays.end()) { + return nullptr; + } + + return it->second.get(); +} + +ProgramValue GetArrayKey(ArrayId arrayId, int index, Program* program) +{ + auto arr = get_array_by_id(arrayId); + if (arr == nullptr) { + return ProgramValue(0); + } + + return arr->GetArrayKey(index, program); +} + +int LenArray(ArrayId arrayId) +{ + auto arr = get_array_by_id(arrayId); + if (arr == nullptr) { + return -1; + } + + return arr->size(); +} + +ProgramValue GetArray(ArrayId arrayId, const ProgramValue& key, Program* program) +{ + auto arr = get_array_by_id(arrayId); + if (arr == nullptr) { + return ProgramValue(0); + } + + return arr->GetArray(key, program); +} + +void SetArray(ArrayId arrayId, const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program) +{ + auto arr = get_array_by_id(arrayId); + if (arr == nullptr) { + return; + } + + arr->SetArray(key, val, allowUnset, program); +} + +void FreeArray(ArrayId arrayId) +{ + // TODO: remove from saved_arrays + _state->arrays.erase(arrayId); +} + +void FixArray(ArrayId arrayId) +{ + _state->temporaryArrayIds.erase(arrayId); +} + +void DeleteAllTempArrays() +{ + for (auto it = _state->temporaryArrayIds.begin(); it != _state->temporaryArrayIds.end(); ++it) { + FreeArray(*it); + } + _state->temporaryArrayIds.clear(); +} + +void ResizeArray(ArrayId arrayId, int newLen) +{ + auto arr = get_array_by_id(arrayId); + if (arr == nullptr) { + return; + } + + arr->ResizeArray(newLen); +} + +int StackArray(const ProgramValue& key, const ProgramValue& val, Program* program) +{ + // CE: Sfall uses eponymous global variable which is always the id of the + // last created array. + ArrayId stackArrayId = _state->nextArrayId - 1; + + auto arr = get_array_by_id(stackArrayId); + if (arr == nullptr) { + return 0; + } + + auto size = arr->size(); + if (size >= ARRAY_MAX_SIZE) { + return 0; + } + + if (key.asInt() >= size) { + arr->ResizeArray(size + 1); + } + + SetArray(stackArrayId, key, val, false, program); + return 0; +} + +ProgramValue ScanArray(ArrayId arrayId, const ProgramValue& val, Program* program) +{ + auto arr = get_array_by_id(arrayId); + if (arr == nullptr) { + return ProgramValue(-1); + } + + return arr->ScanArray(val, program); +} + +ArrayId StringSplit(const char* str, const char* split) +{ + size_t splitLen = strlen(split); + + ArrayId arrayId = CreateTempArray(0, 0); + auto arr = get_array_by_id(arrayId); + + if (splitLen == 0) { + int count = static_cast(strlen(str)); + + arr->ResizeArray(count); + for (int i = 0; i < count; i++) { + arr->SetArray(ProgramValue { i }, ArrayElement { &str[i], 1 }, false); + } + } else { + int count = 1; + const char* ptr = str; + while (true) { + const char* newptr = strstr(ptr, split); + if (newptr == nullptr) { + break; + } + + count++; + ptr = newptr + splitLen; + } + arr->ResizeArray(count); + + count = 0; + ptr = str; + while (true) { + const char* newptr = strstr(ptr, split); + size_t len = (newptr != nullptr) ? newptr - ptr : strlen(ptr); + + arr->SetArray(ProgramValue { count }, ArrayElement { ptr, len }, false); + + if (newptr == nullptr) { + break; + } + + count++; + ptr = newptr + splitLen; + } + } + return arrayId; +} + +} // namespace fallout diff --git a/src/sfall_arrays.h b/src/sfall_arrays.h new file mode 100644 index 0000000..bd96ad5 --- /dev/null +++ b/src/sfall_arrays.h @@ -0,0 +1,34 @@ +#ifndef FALLOUT_SFALL_ARRAYS_H_ +#define FALLOUT_SFALL_ARRAYS_H_ + +#include "interpreter.h" + +namespace fallout { + +#define SFALL_ARRAYFLAG_ASSOC (1) // is map +#define SFALL_ARRAYFLAG_CONSTVAL (2) // don't update value of key if the key exists in map +#define SFALL_ARRAYFLAG_RESERVED (4) + +using ArrayId = unsigned int; + +bool sfallArraysInit(); +void sfallArraysReset(); +void sfallArraysExit(); +ArrayId CreateArray(int len, unsigned int flags); +ArrayId CreateTempArray(int len, unsigned int flags); +ProgramValue GetArrayKey(ArrayId arrayId, int index, Program* program); +int LenArray(ArrayId arrayId); +ProgramValue GetArray(ArrayId arrayId, const ProgramValue& key, Program* program); +void SetArray(ArrayId arrayId, const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program); +void FreeArray(ArrayId arrayId); +void FixArray(ArrayId id); +void ResizeArray(ArrayId arrayId, int newLen); +void DeleteAllTempArrays(); +int StackArray(const ProgramValue& key, const ProgramValue& val, Program* program); +ProgramValue ScanArray(ArrayId arrayId, const ProgramValue& val, Program* program); + +ArrayId StringSplit(const char* str, const char* split); + +} // namespace fallout + +#endif /* FALLOUT_SFALL_ARRAYS_H_ */ diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index 103d29f..d03ee54 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -1,5 +1,7 @@ #include "sfall_opcodes.h" +#include + #include "animation.h" #include "art.h" #include "combat.h" @@ -12,8 +14,10 @@ #include "message.h" #include "mouse.h" #include "object.h" +#include "party_member.h" #include "proto.h" #include "scripts.h" +#include "sfall_arrays.h" #include "sfall_global_vars.h" #include "sfall_lists.h" #include "stat.h" @@ -453,6 +457,53 @@ static void op_tile_under_cursor(Program* program) programStackPushInteger(program, tile); } +// substr +static void opSubstr(Program* program) +{ + auto length = programStackPopInteger(program); + auto startPos = programStackPopInteger(program); + const char* str = programStackPopString(program); + + char buf[5120] = { 0 }; + + int len = strlen(str); + + if (startPos < 0) { + startPos += len; // start from end + if (startPos < 0) { + startPos = 0; + } + } + + if (length < 0) { + length += len - startPos; // cutoff at end + if (length == 0) { + programStackPushString(program, buf); + return; + } + length = abs(length); // length can't be negative + } + + // check position + if (startPos >= len) { + // start position is out of string length, return empty string + programStackPushString(program, buf); + return; + } + + if (length == 0 || length + startPos > len) { + length = len - startPos; // set the correct length, the length of characters goes beyond the end of the string + } + + if (length > sizeof(buf) - 1) { + length = sizeof(buf) - 1; + } + + memcpy(buf, &str[startPos], length); + buf[length] = '\0'; + programStackPushString(program, buf); +} + // strlen static void opGetStringLength(Program* program) { @@ -485,6 +536,146 @@ static void opGetMessage(Program* program) programStackPushString(program, text); } +// get_array_key +static void opGetArrayKey(Program* program) +{ + auto index = programStackPopInteger(program); + auto arrayId = programStackPopInteger(program); + auto value = GetArrayKey(arrayId, index, program); + programStackPushValue(program, value); +} + +// create_array +static void opCreateArray(Program* program) +{ + auto flags = programStackPopInteger(program); + auto len = programStackPopInteger(program); + auto arrayId = CreateArray(len, flags); + programStackPushInteger(program, arrayId); +} + +// temp_array +static void opTempArray(Program* program) +{ + auto flags = programStackPopInteger(program); + auto len = programStackPopInteger(program); + auto arrayId = CreateTempArray(len, flags); + programStackPushInteger(program, arrayId); +} + +// fix_array +static void opFixArray(Program* program) +{ + auto arrayId = programStackPopInteger(program); + FixArray(arrayId); +} + +// string_split +static void opStringSplit(Program* program) +{ + auto split = programStackPopString(program); + auto str = programStackPopString(program); + auto arrayId = StringSplit(str, split); + programStackPushInteger(program, arrayId); +} + +// set_array +static void opSetArray(Program* program) +{ + auto value = programStackPopValue(program); + auto key = programStackPopValue(program); + auto arrayId = programStackPopInteger(program); + SetArray(arrayId, key, value, true, program); +} + +// arrayexpr +static void opStackArray(Program* program) +{ + auto value = programStackPopValue(program); + auto key = programStackPopValue(program); + auto returnValue = StackArray(key, value, program); + programStackPushInteger(program, returnValue); +} + +// scan array +static void opScanArray(Program* program) +{ + auto value = programStackPopValue(program); + auto arrayId = programStackPopInteger(program); + auto returnValue = ScanArray(arrayId, value, program); + programStackPushValue(program, returnValue); +} + +// get_array +static void opGetArray(Program* program) +{ + auto key = programStackPopValue(program); + auto arrayId = programStackPopValue(program); + + if (arrayId.isInt()) { + auto value = GetArray(arrayId.integerValue, key, program); + programStackPushValue(program, value); + } else if (arrayId.isString() && key.isInt()) { + auto pos = key.asInt(); + auto str = programGetString(program, arrayId.opcode, arrayId.integerValue); + + char buf[2] = { 0 }; + if (pos < strlen(str)) { + buf[0] = str[pos]; + programStackPushString(program, buf); + } else { + programStackPushString(program, buf); + } + } +} + +// free_array +static void opFreeArray(Program* program) +{ + auto arrayId = programStackPopInteger(program); + FreeArray(arrayId); +} + +// len_array +static void opLenArray(Program* program) +{ + auto arrayId = programStackPopInteger(program); + programStackPushInteger(program, LenArray(arrayId)); +} + +// resize_array +static void opResizeArray(Program* program) +{ + auto newLen = programStackPopInteger(program); + auto arrayId = programStackPopInteger(program); + ResizeArray(arrayId, newLen); +} + +// party_member_list +static void opPartyMemberList(Program* program) +{ + auto includeHidden = programStackPopInteger(program); + auto objects = get_all_party_members_objects(includeHidden); + auto arrayId = CreateTempArray(objects.size(), SFALL_ARRAYFLAG_RESERVED); + for (int i = 0; i < LenArray(arrayId); i++) { + SetArray(arrayId, ProgramValue { i }, ProgramValue { objects[i] }, false, program); + } + programStackPushInteger(program, arrayId); +} + +// type_of +static void opTypeOf(Program* program) +{ + auto value = programStackPopValue(program); + if (value.isInt()) { + programStackPushInteger(program, 1); + } else if (value.isFloat()) { + programStackPushInteger(program, 2); + } else { + programStackPushInteger(program, 3); + }; +} + // round static void opRound(Program* program) { @@ -619,15 +810,30 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x8220, opGetScreenWidth); interpreterRegisterOpcode(0x8221, opGetScreenHeight); interpreterRegisterOpcode(0x8228, op_get_attack_type); + interpreterRegisterOpcode(0x822D, opCreateArray); + interpreterRegisterOpcode(0x822E, opSetArray); + interpreterRegisterOpcode(0x822F, opGetArray); + interpreterRegisterOpcode(0x8230, opFreeArray); + interpreterRegisterOpcode(0x8231, opLenArray); + interpreterRegisterOpcode(0x8232, opResizeArray); + interpreterRegisterOpcode(0x8233, opTempArray); + interpreterRegisterOpcode(0x8234, opFixArray); + interpreterRegisterOpcode(0x8235, opStringSplit); interpreterRegisterOpcode(0x8237, opParseInt); interpreterRegisterOpcode(0x8238, op_atof); + interpreterRegisterOpcode(0x8239, opScanArray); interpreterRegisterOpcode(0x824B, op_tile_under_cursor); + interpreterRegisterOpcode(0x824E, opSubstr); interpreterRegisterOpcode(0x824F, opGetStringLength); + interpreterRegisterOpcode(0x8253, opTypeOf); + interpreterRegisterOpcode(0x8256, opGetArrayKey); + interpreterRegisterOpcode(0x8257, opStackArray); interpreterRegisterOpcode(0x8263, op_power); - interpreterRegisterOpcode(0x826B, opGetMessage); interpreterRegisterOpcode(0x8267, opRound); + interpreterRegisterOpcode(0x826B, opGetMessage); interpreterRegisterOpcode(0x826E, op_make_straight_path); interpreterRegisterOpcode(0x826F, op_obj_blocking_at); + interpreterRegisterOpcode(0x8271, opPartyMemberList); interpreterRegisterOpcode(0x8274, opArtExists); interpreterRegisterOpcode(0x827F, op_div); } From 681b7c388b7b9fcd80855a5e3e3abc0d92adfdb9 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 30 May 2023 09:12:51 +0300 Subject: [PATCH 009/136] Fix sfall opcode comments --- src/sfall_opcodes.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index d03ee54..f6f3ede 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -536,7 +536,7 @@ static void opGetMessage(Program* program) programStackPushString(program, text); } -// get_array_key +// array_key static void opGetArrayKey(Program* program) { auto index = programStackPopInteger(program); @@ -597,7 +597,7 @@ static void opStackArray(Program* program) programStackPushInteger(program, returnValue); } -// scan array +// scan_array static void opScanArray(Program* program) { auto value = programStackPopValue(program); From 03bf5078932636d7d5c1cdfcfdc58abbdde4b448 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 30 May 2023 12:52:55 +0300 Subject: [PATCH 010/136] Add ini opcodes (#293) --- CMakeLists.txt | 2 + src/config.cc | 28 +++++---- src/game.cc | 5 ++ src/sfall_config.cc | 2 + src/sfall_config.h | 2 + src/sfall_ini.cc | 146 +++++++++++++++++++++++++++++++++++++++++++ src/sfall_ini.h | 19 ++++++ src/sfall_opcodes.cc | 29 +++++++++ 8 files changed, 221 insertions(+), 12 deletions(-) create mode 100644 src/sfall_ini.cc create mode 100644 src/sfall_ini.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f3bd73..6eb6375 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -267,6 +267,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC "src/sfall_config.h" "src/sfall_global_vars.cc" "src/sfall_global_vars.h" + "src/sfall_ini.cc" + "src/sfall_ini.h" "src/sfall_lists.cc" "src/sfall_lists.h" "src/sfall_opcodes.cc" diff --git a/src/config.cc b/src/config.cc index 966b0d4..2ec1aad 100644 --- a/src/config.cc +++ b/src/config.cc @@ -280,24 +280,28 @@ bool configRead(Config* config, const char* filePath, bool isDb) if (isDb) { File* stream = fileOpen(filePath, "rb"); - if (stream != NULL) { - while (fileReadString(string, sizeof(string), stream) != NULL) { - configParseLine(config, string); - } - fileClose(stream); + + // CE: Return `false` if file does not exists in database. + if (stream == NULL) { + return false; } + + while (fileReadString(string, sizeof(string), stream) != NULL) { + configParseLine(config, string); + } + fileClose(stream); } else { FILE* stream = compat_fopen(filePath, "rt"); - if (stream != NULL) { - while (compat_fgets(string, sizeof(string), stream) != NULL) { - configParseLine(config, string); - } - fclose(stream); + // CE: Return `false` if file does not exists on the file system. + if (stream == NULL) { + return false; } - // FIXME: This function returns `true` even if the file was not actually - // read. I'm pretty sure it's bug. + while (compat_fgets(string, sizeof(string), stream) != NULL) { + configParseLine(config, string); + } + fclose(stream); } return true; diff --git a/src/game.cc b/src/game.cc index b7bfe0a..4412024 100644 --- a/src/game.cc +++ b/src/game.cc @@ -53,6 +53,7 @@ #include "sfall_arrays.h" #include "sfall_config.h" #include "sfall_global_vars.h" +#include "sfall_ini.h" #include "sfall_lists.h" #include "skill.h" #include "skilldex.h" @@ -354,6 +355,10 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4 return -1; } + char* customConfigBasePath; + configGetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_INI_CONFIG_FOLDER, &customConfigBasePath); + sfall_ini_set_base_path(customConfigBasePath); + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_MISC, &gMiscMessageList); return 0; diff --git a/src/sfall_config.cc b/src/sfall_config.cc index c0f4b6c..c38b825 100644 --- a/src/sfall_config.cc +++ b/src/sfall_config.cc @@ -58,6 +58,8 @@ bool sfallConfigInit(int argc, char** argv) configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER3, 270); configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER4, 360); + configSetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_INI_CONFIG_FOLDER, ""); + char path[COMPAT_MAX_PATH]; char* executable = argv[0]; char* ch = strrchr(executable, '\\'); diff --git a/src/sfall_config.h b/src/sfall_config.h index 86aa7a7..f165450 100644 --- a/src/sfall_config.h +++ b/src/sfall_config.h @@ -8,6 +8,7 @@ namespace fallout { #define SFALL_CONFIG_FILE_NAME "ddraw.ini" #define SFALL_CONFIG_MISC_KEY "Misc" +#define SFALL_CONFIG_SCRIPTS_KEY "Scripts" #define SFALL_CONFIG_DUDE_NATIVE_LOOK_JUMPSUIT_MALE_KEY "MaleDefaultModel" #define SFALL_CONFIG_DUDE_NATIVE_LOOK_JUMPSUIT_FEMALE_KEY "FemaleDefaultModel" @@ -68,6 +69,7 @@ namespace fallout { #define SFALL_CONFIG_TOWN_MAP_HOTKEYS_FIX_KEY "TownMapHotkeysFix" #define SFALL_CONFIG_EXTRA_MESSAGE_LISTS_KEY "ExtraGameMsgFileList" #define SFALL_CONFIG_NUMBERS_IS_DIALOG_KEY "NumbersInDialogue" +#define SFALL_CONFIG_INI_CONFIG_FOLDER "IniConfigFolder" #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MULTIPLIER 1 #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR 3 diff --git a/src/sfall_ini.cc b/src/sfall_ini.cc new file mode 100644 index 0000000..ee4aced --- /dev/null +++ b/src/sfall_ini.cc @@ -0,0 +1,146 @@ +#include "sfall_ini.h" + +#include +#include + +#include "config.h" +#include "platform_compat.h" + +namespace fallout { + +/// The max length of `fileName` chunk in the triplet. +static constexpr size_t kFileNameMaxSize = 63; + +/// The max length of `section` chunk in the triplet. +static constexpr size_t kSectionMaxSize = 32; + +/// Special .ini file names which are accessed without adding base path. +static constexpr const char* kSystemConfigFileNames[] = { + "ddraw.ini", + "f2_res.ini", +}; + +static char basePath[COMPAT_MAX_PATH]; + +/// Parses "fileName|section|key" triplet into parts. `fileName` and `section` +/// chunks are copied into appropriate variables. Returns the pointer to `key`, +/// or `nullptr` on any error. +static const char* parse_ini_triplet(const char* triplet, char* fileName, char* section) +{ + const char* fileNameSectionSep = strchr(triplet, '|'); + if (fileNameSectionSep == nullptr) { + return nullptr; + } + + size_t fileNameLength = fileNameSectionSep - triplet; + if (fileNameLength > kFileNameMaxSize) { + return nullptr; + } + + const char* sectionKeySep = strchr(fileNameSectionSep + 1, '|'); + if (sectionKeySep == nullptr) { + return nullptr; + } + + size_t sectionLength = sectionKeySep - fileNameSectionSep - 1; + if (sectionLength > kSectionMaxSize) { + return nullptr; + } + + strncpy(fileName, triplet, fileNameLength); + fileName[fileNameLength] = '\0'; + + strncpy(section, fileNameSectionSep + 1, sectionLength); + section[sectionLength] = '\0'; + + return sectionKeySep + 1; +} + +/// Returns `true` if given `fileName` is a special system .ini file name. +static bool is_system_file_name(const char* fileName) +{ + for (auto& systemFileName : kSystemConfigFileNames) { + if (compat_stricmp(systemFileName, fileName) == 0) { + return true; + } + } + + return false; +} + +void sfall_ini_set_base_path(const char* path) +{ + if (path != nullptr) { + strcpy(basePath, path); + + size_t length = strlen(basePath); + if (length > 0) { + if (basePath[length - 1] == '\\' || basePath[length - 1] == '/') { + basePath[length - 1] = '\0'; + } + } + } else { + basePath[0] = '\0'; + } +} + +bool sfall_ini_get_int(const char* triplet, int* value) +{ + char string[20]; + if (!sfall_ini_get_string(triplet, string, sizeof(string))) { + return false; + } + + *value = atol(string); + + return true; +} + +bool sfall_ini_get_string(const char* triplet, char* value, size_t size) +{ + char fileName[kFileNameMaxSize]; + char section[kSectionMaxSize]; + + const char* key = parse_ini_triplet(triplet, fileName, section); + if (key == nullptr) { + return false; + } + + Config config; + if (!configInit(&config)) { + return false; + } + + char path[COMPAT_MAX_PATH]; + bool loaded = false; + + if (basePath[0] != '\0' && !is_system_file_name(fileName)) { + // Attempt to load requested file in base directory. + snprintf(path, sizeof(path), "%s\\%s", basePath, fileName); + loaded = configRead(&config, path, false); + } + + if (!loaded) { + // There was no base path set, requested file is a system config, or + // non-system config file was not found the base path - attempt to load + // from current working directory. + strcpy(path, fileName); + loaded = configRead(&config, path, false); + } + + bool ok = false; + if (loaded) { + char* stringValue; + if (configGetString(&config, section, key, &stringValue)) { + strncpy(value, stringValue, size - 1); + value[size - 1] = '\0'; + ok = true; + } + } + + configFree(&config); + + return ok; +} + +} // namespace fallout diff --git a/src/sfall_ini.h b/src/sfall_ini.h new file mode 100644 index 0000000..ac4cdd8 --- /dev/null +++ b/src/sfall_ini.h @@ -0,0 +1,19 @@ +#ifndef FALLOUT_SFALL_INI_H_ +#define FALLOUT_SFALL_INI_H_ + +#include + +namespace fallout { + +/// Sets base directory to lookup .ini files. +void sfall_ini_set_base_path(const char* path); + +/// Reads integer key identified by "fileName|section|key" triplet into `value`. +bool sfall_ini_get_int(const char* triplet, int* value); + +/// Reads string key identified by "fileName|section|key" triplet into `value`. +bool sfall_ini_get_string(const char* triplet, char* value, size_t size); + +} // namespace fallout + +#endif /* FALLOUT_SFALL_INI_H_ */ diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index f6f3ede..3b101ce 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -19,6 +19,7 @@ #include "scripts.h" #include "sfall_arrays.h" #include "sfall_global_vars.h" +#include "sfall_ini.h" #include "sfall_lists.h" #include "stat.h" #include "svga.h" @@ -147,6 +148,19 @@ static void opGetGlobalInt(Program* program) programStackPushInteger(program, value); } +// get_ini_setting +static void op_get_ini_setting(Program* program) +{ + const char* string = programStackPopString(program); + + int value; + if (sfall_ini_get_int(string, &value)) { + programStackPushInteger(program, value); + } else { + programStackPushInteger(program, -1); + } +} + // get_game_mode static void opGetGameMode(Program* program) { @@ -181,6 +195,19 @@ static void op_set_bodypart_hit_modifier(Program* program) combat_set_hit_location_penalty(hit_location, penalty); } +// get_ini_string +static void op_get_ini_string(Program* program) +{ + const char* string = programStackPopString(program); + + char value[256]; + if (sfall_ini_get_string(string, value, sizeof(value))) { + programStackPushString(program, value); + } else { + programStackPushInteger(program, -1); + } +} + // sqrt static void op_sqrt(Program* program) { @@ -785,11 +812,13 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x8193, opGetCurrentHand); interpreterRegisterOpcode(0x819D, opSetGlobalVar); interpreterRegisterOpcode(0x819E, opGetGlobalInt); + interpreterRegisterOpcode(0x81AC, op_get_ini_setting); interpreterRegisterOpcode(0x81AF, opGetGameMode); interpreterRegisterOpcode(0x81B3, op_get_uptime); interpreterRegisterOpcode(0x81B6, op_set_car_current_town); interpreterRegisterOpcode(0x81DF, op_get_bodypart_hit_modifier); interpreterRegisterOpcode(0x81E0, op_set_bodypart_hit_modifier); + interpreterRegisterOpcode(0x81EB, op_get_ini_string); interpreterRegisterOpcode(0x81EC, op_sqrt); interpreterRegisterOpcode(0x81ED, op_abs); interpreterRegisterOpcode(0x8204, op_get_proto_data); From 32ed3c3271c0dc32c94deb1ea118db6c954f8306 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 30 May 2023 12:56:51 +0300 Subject: [PATCH 011/136] Add FixNamespaceComments --- .clang-format | 1 + src/vcr.cc | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index fccbbe7..771f375 100644 --- a/.clang-format +++ b/.clang-format @@ -1,2 +1,3 @@ BasedOnStyle: WebKit AllowShortIfStatementsOnASingleLine: WithoutElse +FixNamespaceComments: true diff --git a/src/vcr.cc b/src/vcr.cc index b11245a..54791f2 100644 --- a/src/vcr.cc +++ b/src/vcr.cc @@ -438,4 +438,4 @@ bool vcrReadEntry(VcrEntry* vcrEntry, File* stream) return false; } -} // fallout +} // namespace fallout From ec722475b6a9f9c3a440b275e5d5d129b48746f7 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 30 May 2023 14:21:50 +0300 Subject: [PATCH 012/136] Add op_create_message_window See #200 --- src/sfall_opcodes.cc | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index 3b101ce..307a65b 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -4,13 +4,16 @@ #include "animation.h" #include "art.h" +#include "color.h" #include "combat.h" +#include "dbox.h" #include "debug.h" #include "game.h" #include "input.h" #include "interface.h" #include "interpreter.h" #include "item.h" +#include "memory.h" #include "message.h" #include "mouse.h" #include "object.h" @@ -448,6 +451,47 @@ static void opGetScreenHeight(Program* program) programStackPushInteger(program, screenGetHeight()); } +// create_message_window +static void op_create_message_window(Program* program) +{ + static bool showing = false; + + if (showing) { + return; + } + + const char* string = programStackPopString(program); + if (string == nullptr || string[0] == '\0') { + return; + } + + char* copy = internal_strdup(string); + + const char* body[4]; + int count = 0; + + char* pch = strchr(copy, '\n'); + while (pch != nullptr && count < 4) { + *pch = '\0'; + body[count++] = pch + 1; + pch = strchr(pch + 1, '\n'); + } + + showing = true; + showDialogBox(copy, + body, + count, + 192, + 116, + _colorTable[32328], + nullptr, + _colorTable[32328], + DIALOG_BOX_LARGE); + showing = false; + + internal_free(copy); +} + // get_attack_type static void op_get_attack_type(Program* program) { @@ -838,6 +882,7 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x821E, op_get_mouse_buttons); interpreterRegisterOpcode(0x8220, opGetScreenWidth); interpreterRegisterOpcode(0x8221, opGetScreenHeight); + interpreterRegisterOpcode(0x8224, op_create_message_window); interpreterRegisterOpcode(0x8228, op_get_attack_type); interpreterRegisterOpcode(0x822D, opCreateArray); interpreterRegisterOpcode(0x822E, opSetArray); From 89839be3af31f1784951f15f1cfff07ad40e1fe3 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Wed, 31 May 2023 21:48:44 +0300 Subject: [PATCH 013/136] Add global scripts (#294) --- CMakeLists.txt | 2 + src/combat.cc | 5 + src/config.cc | 6 +- src/game.cc | 8 ++ src/interpreter.cc | 3 +- src/interpreter.h | 1 + src/loadsave.cc | 4 + src/main.cc | 8 ++ src/scripts.cc | 6 +- src/scripts.h | 2 + src/sfall_config.cc | 1 + src/sfall_config.h | 1 + src/sfall_global_scripts.cc | 213 ++++++++++++++++++++++++++++++++++++ src/sfall_global_scripts.h | 23 ++++ src/sfall_ini.cc | 8 +- src/sfall_opcodes.cc | 36 ++++++ src/worldmap.cc | 5 + 17 files changed, 323 insertions(+), 9 deletions(-) create mode 100644 src/sfall_global_scripts.cc create mode 100644 src/sfall_global_scripts.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6eb6375..cd91dbd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -267,6 +267,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC "src/sfall_config.h" "src/sfall_global_vars.cc" "src/sfall_global_vars.h" + "src/sfall_global_scripts.cc" + "src/sfall_global_scripts.h" "src/sfall_ini.cc" "src/sfall_ini.h" "src/sfall_lists.cc" diff --git a/src/combat.cc b/src/combat.cc index cde7b7d..8548137 100644 --- a/src/combat.cc +++ b/src/combat.cc @@ -37,6 +37,7 @@ #include "scripts.h" #include "settings.h" #include "sfall_config.h" +#include "sfall_global_scripts.h" #include "skill.h" #include "stat.h" #include "svga.h" @@ -3144,6 +3145,10 @@ static int _combat_input() } int keyCode = inputGetInput(); + + // SFALL: CombatLoopHook. + sfall_gl_scr_process_main(); + if (_action_explode_running()) { // NOTE: Uninline. _combat_turn_run(); diff --git a/src/config.cc b/src/config.cc index 2ec1aad..5a90fae 100644 --- a/src/config.cc +++ b/src/config.cc @@ -397,7 +397,7 @@ static bool configParseLine(Config* config, char* string) // keys there. // Skip leading whitespace. - while (isspace(*string)) { + while (isspace(static_cast(*string))) { string++; } @@ -500,7 +500,7 @@ static bool configTrimString(char* string) // Starting from the end of the string, loop while it's a whitespace and // decrement string length. char* pch = string + length - 1; - while (length != 0 && isspace(*pch)) { + while (length != 0 && isspace(static_cast(*pch))) { length--; pch--; } @@ -511,7 +511,7 @@ static bool configTrimString(char* string) // Starting from the beginning of the string loop while it's a whitespace // and decrement string length. pch = string; - while (isspace(*pch)) { + while (isspace(static_cast(*pch))) { pch++; length--; } diff --git a/src/game.cc b/src/game.cc index 4412024..c215edf 100644 --- a/src/game.cc +++ b/src/game.cc @@ -52,6 +52,7 @@ #include "settings.h" #include "sfall_arrays.h" #include "sfall_config.h" +#include "sfall_global_scripts.h" #include "sfall_global_vars.h" #include "sfall_ini.h" #include "sfall_lists.h" @@ -355,6 +356,11 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4 return -1; } + if (!sfall_gl_scr_init()) { + debugPrint("Failed on sfall_gl_scr_init"); + return -1; + } + char* customConfigBasePath; configGetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_INI_CONFIG_FOLDER, &customConfigBasePath); sfall_ini_set_base_path(customConfigBasePath); @@ -407,6 +413,7 @@ void gameReset() sfallListsReset(); messageListRepositoryReset(); sfallArraysReset(); + sfall_gl_scr_reset(); } // 0x442C34 @@ -415,6 +422,7 @@ void gameExit() debugPrint("\nGame Exit\n"); // SFALL + sfall_gl_scr_exit(); sfallArraysExit(); sfallListsExit(); sfallGlobalVarsExit(); diff --git a/src/interpreter.cc b/src/interpreter.cc index 3653f4d..b2e0cd1 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -43,7 +43,6 @@ static opcode_t programReturnStackPopInt16(Program* program); static int programReturnStackPopInt32(Program* program); static void _detachProgram(Program* program); static void _purgeProgram(Program* program); -static void programFree(Program* program); static opcode_t _getOp(Program* program); static void programMarkHeap(Program* program); static void opNoop(Program* program); @@ -421,7 +420,7 @@ static void _purgeProgram(Program* program) } // 0x467614 -static void programFree(Program* program) +void programFree(Program* program) { // NOTE: Uninline. _detachProgram(program); diff --git a/src/interpreter.h b/src/interpreter.h index 835e2fd..6095292 100644 --- a/src/interpreter.h +++ b/src/interpreter.h @@ -204,6 +204,7 @@ void _interpretOutputFunc(int (*func)(char*)); int _interpretOutput(const char* format, ...); [[noreturn]] void programFatalError(const char* str, ...); void _interpretDecStringRef(Program* program, opcode_t a2, int a3); +void programFree(Program* program); Program* programCreateByPath(const char* path); char* programGetString(Program* program, opcode_t opcode, int offset); char* programGetIdentifier(Program* program, int offset); diff --git a/src/loadsave.cc b/src/loadsave.cc index d469311..ebee1e6 100644 --- a/src/loadsave.cc +++ b/src/loadsave.cc @@ -45,6 +45,7 @@ #include "random.h" #include "scripts.h" #include "settings.h" +#include "sfall_global_scripts.h" #include "skill.h" #include "stat.h" #include "svga.h" @@ -1676,6 +1677,9 @@ static int lsgLoadGameInSlot(int slot) _loadingGame = 0; + // SFALL: Start global scripts. + sfall_gl_scr_exec_start_proc(); + return 0; } diff --git a/src/main.cc b/src/main.cc index a016f16..9c90e29 100644 --- a/src/main.cc +++ b/src/main.cc @@ -33,6 +33,7 @@ #include "selfrun.h" #include "settings.h" #include "sfall_config.h" +#include "sfall_global_scripts.h" #include "svga.h" #include "text_font.h" #include "window.h" @@ -148,6 +149,9 @@ int falloutMain(int argc, char** argv) _main_load_new(mapNameCopy); free(mapNameCopy); + // SFALL: AfterNewGameStartHook. + sfall_gl_scr_exec_start_proc(); + mainLoop(); paletteFadeTo(gPaletteWhite); @@ -357,6 +361,10 @@ static void mainLoop() sharedFpsLimiter.mark(); int keyCode = inputGetInput(); + + // SFALL: MainLoopHook. + sfall_gl_scr_process_main(); + gameHandleKey(keyCode, false); scriptsHandleRequests(); diff --git a/src/scripts.cc b/src/scripts.cc index 7de46fd..f0d2c60 100644 --- a/src/scripts.cc +++ b/src/scripts.cc @@ -31,6 +31,7 @@ #include "queue.h" #include "sfall_arrays.h" #include "sfall_config.h" +#include "sfall_global_scripts.h" #include "stat.h" #include "svga.h" #include "tile.h" @@ -145,7 +146,7 @@ static const int gGameTimeDaysPerMonth[12] = { }; // 0x51C758 -static const char* gScriptProcNames[28] = { +const char* gScriptProcNames[SCRIPT_PROC_COUNT] = { "no_p_proc", "start", "spatial_p_proc", @@ -2589,6 +2590,9 @@ void scriptsExecMapUpdateProc() // 0x4A67EC void scriptsExecMapUpdateScripts(int proc) { + // SFALL: Run global scripts. + sfall_gl_scr_exec_map_update_scripts(proc); + _scr_SpatialsEnabled = false; int fixedParam = 0; diff --git a/src/scripts.h b/src/scripts.h index 8efdd1d..0c4b508 100644 --- a/src/scripts.h +++ b/src/scripts.h @@ -145,6 +145,8 @@ typedef struct Script { int field_DC; } Script; +extern const char* gScriptProcNames[SCRIPT_PROC_COUNT]; + int gameTimeGetTime(); void gameTimeGetDate(int* monthPtr, int* dayPtr, int* yearPtr); int gameTimeGetHour(); diff --git a/src/sfall_config.cc b/src/sfall_config.cc index c38b825..b85a83b 100644 --- a/src/sfall_config.cc +++ b/src/sfall_config.cc @@ -59,6 +59,7 @@ bool sfallConfigInit(int argc, char** argv) configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER4, 360); configSetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_INI_CONFIG_FOLDER, ""); + configSetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_GLOBAL_SCRIPT_PATHS, ""); char path[COMPAT_MAX_PATH]; char* executable = argv[0]; diff --git a/src/sfall_config.h b/src/sfall_config.h index f165450..1f786ce 100644 --- a/src/sfall_config.h +++ b/src/sfall_config.h @@ -70,6 +70,7 @@ namespace fallout { #define SFALL_CONFIG_EXTRA_MESSAGE_LISTS_KEY "ExtraGameMsgFileList" #define SFALL_CONFIG_NUMBERS_IS_DIALOG_KEY "NumbersInDialogue" #define SFALL_CONFIG_INI_CONFIG_FOLDER "IniConfigFolder" +#define SFALL_CONFIG_GLOBAL_SCRIPT_PATHS "GlobalScriptPaths" #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MULTIPLIER 1 #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR 3 diff --git a/src/sfall_global_scripts.cc b/src/sfall_global_scripts.cc new file mode 100644 index 0000000..1cd65d7 --- /dev/null +++ b/src/sfall_global_scripts.cc @@ -0,0 +1,213 @@ +#include "sfall_global_scripts.h" + +#include +#include +#include +#include + +#include "db.h" +#include "input.h" +#include "platform_compat.h" +#include "scripts.h" +#include "sfall_config.h" + +namespace fallout { + +struct GlobalScript { + Program* program = nullptr; + int procs[SCRIPT_PROC_COUNT] = { 0 }; + int repeat = 0; + int count = 0; + int mode = 0; + bool once = true; +}; + +struct GlobalScriptsState { + std::vector paths; + std::vector globalScripts; +}; + +static GlobalScriptsState* state = nullptr; + +bool sfall_gl_scr_init() +{ + state = new (std::nothrow) GlobalScriptsState(); + if (state == nullptr) { + return false; + } + + char* paths; + configGetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_GLOBAL_SCRIPT_PATHS, &paths); + + char* curr = paths; + while (curr != nullptr && *curr != '\0') { + char* end = strchr(curr, ','); + if (end != nullptr) { + *end = '\0'; + } + + char drive[COMPAT_MAX_DRIVE]; + char dir[COMPAT_MAX_DIR]; + compat_splitpath(curr, drive, dir, nullptr, nullptr); + + char** files; + int filesLength = fileNameListInit(curr, &files, 0, 0); + if (filesLength != 0) { + for (int index = 0; index < filesLength; index++) { + char path[COMPAT_MAX_PATH]; + compat_makepath(path, drive, dir, files[index], nullptr); + + state->paths.push_back(std::string { path }); + } + + fileNameListFree(&files, 0); + } + + if (end != nullptr) { + *end = ','; + curr = end + 1; + } else { + curr = nullptr; + } + } + + std::sort(state->paths.begin(), state->paths.end()); + + return true; +} + +void sfall_gl_scr_reset() +{ + if (state != nullptr) { + sfall_gl_scr_remove_all(); + } +} + +void sfall_gl_scr_exit() +{ + if (state != nullptr) { + sfall_gl_scr_remove_all(); + + delete state; + state = nullptr; + } +} + +void sfall_gl_scr_exec_start_proc() +{ + for (auto& path : state->paths) { + Program* program = programCreateByPath(path.c_str()); + if (program != nullptr) { + GlobalScript scr; + scr.program = program; + + for (int action = 0; action < SCRIPT_PROC_COUNT; action++) { + scr.procs[action] = programFindProcedure(program, gScriptProcNames[action]); + } + + state->globalScripts.push_back(std::move(scr)); + + _interpret(program, -1); + } + } + + tickersAdd(sfall_gl_scr_process_input); +} + +void sfall_gl_scr_remove_all() +{ + tickersRemove(sfall_gl_scr_process_input); + + for (auto& scr : state->globalScripts) { + programFree(scr.program); + } + + state->globalScripts.clear(); +} + +void sfall_gl_scr_exec_map_update_scripts(int action) +{ + for (auto& scr : state->globalScripts) { + if (scr.mode == 0 || scr.mode == 3) { + if (scr.procs[action] != -1) { + _executeProcedure(scr.program, scr.procs[action]); + } + } + } +} + +static void sfall_gl_scr_process_simple(int mode1, int mode2) +{ + for (auto& scr : state->globalScripts) { + if (scr.repeat != 0 && (scr.mode == mode1 || scr.mode == mode2)) { + scr.count++; + if (scr.count >= scr.repeat) { + _executeProcedure(scr.program, scr.procs[SCRIPT_PROC_START]); + scr.count = 0; + } + } + } +} + +void sfall_gl_scr_process_main() +{ + sfall_gl_scr_process_simple(0, 3); +} + +void sfall_gl_scr_process_input() +{ + sfall_gl_scr_process_simple(1, 1); +} + +void sfall_gl_scr_process_worldmap() +{ + sfall_gl_scr_process_simple(2, 3); +} + +static GlobalScript* sfall_gl_scr_map_program_to_scr(Program* program) +{ + auto it = std::find_if(state->globalScripts.begin(), + state->globalScripts.end(), + [&program](const GlobalScript& scr) { + return scr.program == program; + }); + return it != state->globalScripts.end() ? &(*it) : nullptr; +} + +void sfall_gl_scr_set_repeat(Program* program, int frames) +{ + GlobalScript* scr = sfall_gl_scr_map_program_to_scr(program); + if (scr != nullptr) { + scr->repeat = frames; + } +} + +void sfall_gl_scr_set_type(Program* program, int type) +{ + if (type < 0 || type > 3) { + return; + } + + GlobalScript* scr = sfall_gl_scr_map_program_to_scr(program); + if (scr != nullptr) { + scr->mode = type; + } +} + +bool sfall_gl_scr_is_loaded(Program* program) +{ + GlobalScript* scr = sfall_gl_scr_map_program_to_scr(program); + if (scr != nullptr) { + if (scr->once) { + scr->once = false; + return true; + } + + return false; + } + + // Not a global script. + return true; +} + +} // namespace fallout diff --git a/src/sfall_global_scripts.h b/src/sfall_global_scripts.h new file mode 100644 index 0000000..bf8eadf --- /dev/null +++ b/src/sfall_global_scripts.h @@ -0,0 +1,23 @@ +#ifndef FALLOUT_SFALL_GLOBAL_SCRIPTS_H_ +#define FALLOUT_SFALL_GLOBAL_SCRIPTS_H_ + +#include "interpreter.h" + +namespace fallout { + +bool sfall_gl_scr_init(); +void sfall_gl_scr_reset(); +void sfall_gl_scr_exit(); +void sfall_gl_scr_exec_start_proc(); +void sfall_gl_scr_remove_all(); +void sfall_gl_scr_exec_map_update_scripts(int action); +void sfall_gl_scr_process_main(); +void sfall_gl_scr_process_input(); +void sfall_gl_scr_process_worldmap(); +void sfall_gl_scr_set_repeat(Program* program, int frames); +void sfall_gl_scr_set_type(Program* program, int type); +bool sfall_gl_scr_is_loaded(Program* program); + +} // namespace fallout + +#endif /* FALLOUT_SFALL_GLOBAL_SCRIPTS_H_ */ diff --git a/src/sfall_ini.cc b/src/sfall_ini.cc index ee4aced..91af85c 100644 --- a/src/sfall_ini.cc +++ b/src/sfall_ini.cc @@ -128,19 +128,21 @@ bool sfall_ini_get_string(const char* triplet, char* value, size_t size) loaded = configRead(&config, path, false); } - bool ok = false; + // NOTE: Sfall's `GetIniSetting` returns error code (-1) only when it cannot + // parse triplet. Otherwise the default for string settings is empty string. + value[0] = '\0'; + if (loaded) { char* stringValue; if (configGetString(&config, section, key, &stringValue)) { strncpy(value, stringValue, size - 1); value[size - 1] = '\0'; - ok = true; } } configFree(&config); - return ok; + return true; } } // namespace fallout diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index 307a65b..e21a079 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -21,6 +21,7 @@ #include "proto.h" #include "scripts.h" #include "sfall_arrays.h" +#include "sfall_global_scripts.h" #include "sfall_global_vars.h" #include "sfall_ini.h" #include "sfall_lists.h" @@ -101,6 +102,30 @@ static void op_get_year(Program* program) programStackPushInteger(program, year); } +// game_loaded +static void op_game_loaded(Program* program) +{ + bool loaded = sfall_gl_scr_is_loaded(program); + programStackPushInteger(program, loaded ? 1 : 0); +} + +// set_global_script_repeat +static void op_set_global_script_repeat(Program* program) +{ + int frames = programStackPopInteger(program); + sfall_gl_scr_set_repeat(program, frames); +} + +// key_pressed +static void op_key_pressed(Program* program) +{ + int key = programStackPopInteger(program); + + // TODO: Incomplete. + + programStackPushInteger(program, 0); +} + // in_world_map static void op_in_world_map(Program* program) { @@ -121,6 +146,13 @@ static void opGetCurrentHand(Program* program) programStackPushInteger(program, interfaceGetCurrentHand()); } +// set_global_script_type +static void op_set_global_script_type(Program* program) +{ + int type = programStackPopInteger(program); + sfall_gl_scr_set_type(program, type); +} + // set_sfall_global static void opSetGlobalVar(Program* program) { @@ -851,9 +883,13 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x815C, op_get_pc_base_stat); interpreterRegisterOpcode(0x815D, opGetPcBonusStat); interpreterRegisterOpcode(0x8163, op_get_year); + interpreterRegisterOpcode(0x8164, op_game_loaded); + interpreterRegisterOpcode(0x816A, op_set_global_script_repeat); + interpreterRegisterOpcode(0x816C, op_key_pressed); interpreterRegisterOpcode(0x8170, op_in_world_map); interpreterRegisterOpcode(0x8172, op_set_world_map_pos); interpreterRegisterOpcode(0x8193, opGetCurrentHand); + interpreterRegisterOpcode(0x819B, op_set_global_script_type); interpreterRegisterOpcode(0x819D, opSetGlobalVar); interpreterRegisterOpcode(0x819E, opGetGlobalInt); interpreterRegisterOpcode(0x81AC, op_get_ini_setting); diff --git a/src/worldmap.cc b/src/worldmap.cc index 9b6ff3d..3243d8d 100644 --- a/src/worldmap.cc +++ b/src/worldmap.cc @@ -41,6 +41,7 @@ #include "scripts.h" #include "settings.h" #include "sfall_config.h" +#include "sfall_global_scripts.h" #include "skill.h" #include "stat.h" #include "string_parsers.h" @@ -2982,6 +2983,10 @@ static int wmWorldMapFunc(int a1) sharedFpsLimiter.mark(); int keyCode = inputGetInput(); + + // SFALL: WorldmapLoopHook. + sfall_gl_scr_process_worldmap(); + unsigned int now = getTicks(); int mouseX; From bf639a2c4a98c9e050cbc8bad1900a01fe66c496 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 1 Jun 2023 17:10:36 +0300 Subject: [PATCH 014/136] Fix copying files on case-sensitive file systems Closes #273 --- src/file_utils.cc | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/file_utils.cc b/src/file_utils.cc index 3c47e5b..8d3ebc1 100644 --- a/src/file_utils.cc +++ b/src/file_utils.cc @@ -150,16 +150,8 @@ int _gzdecompress_file(const char* existingFilePath, const char* newFilePath) static void fileCopy(const char* existingFilePath, const char* newFilePath) { - char nativeExistingFilePath[COMPAT_MAX_PATH]; - strcpy(nativeExistingFilePath, existingFilePath); - compat_windows_path_to_native(nativeExistingFilePath); - - char nativeNewFilePath[COMPAT_MAX_PATH]; - strcpy(nativeNewFilePath, newFilePath); - compat_windows_path_to_native(nativeNewFilePath); - - FILE* in = fopen(nativeExistingFilePath, "rb"); - FILE* out = fopen(nativeNewFilePath, "wb"); + FILE* in = compat_fopen(existingFilePath, "rb"); + FILE* out = compat_fopen(newFilePath, "wb"); if (in != NULL && out != NULL) { std::vector buffer(0xFFFF); From 37e4822ed5dee6158225ec262d65be96cc459d0e Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 1 Jun 2023 19:01:03 +0300 Subject: [PATCH 015/136] Add keyboard opcodes (#295) --- CMakeLists.txt | 2 + src/sfall_kb_helpers.cc | 297 ++++++++++++++++++++++++++++++++++++++++ src/sfall_kb_helpers.h | 10 ++ src/sfall_opcodes.cc | 15 +- 4 files changed, 320 insertions(+), 4 deletions(-) create mode 100644 src/sfall_kb_helpers.cc create mode 100644 src/sfall_kb_helpers.h diff --git a/CMakeLists.txt b/CMakeLists.txt index cd91dbd..4c6c918 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -271,6 +271,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC "src/sfall_global_scripts.h" "src/sfall_ini.cc" "src/sfall_ini.h" + "src/sfall_kb_helpers.cc" + "src/sfall_kb_helpers.h" "src/sfall_lists.cc" "src/sfall_lists.h" "src/sfall_opcodes.cc" diff --git a/src/sfall_kb_helpers.cc b/src/sfall_kb_helpers.cc new file mode 100644 index 0000000..0e761f3 --- /dev/null +++ b/src/sfall_kb_helpers.cc @@ -0,0 +1,297 @@ +#include "sfall_kb_helpers.h" + +#include + +/// Maps DirectInput DIK constants to SDL scancodes. +static constexpr SDL_Scancode kDiks[256] = { + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_ESCAPE, // DIK_ESCAPE + SDL_SCANCODE_1, // DIK_1 + SDL_SCANCODE_2, // DIK_2 + SDL_SCANCODE_3, // DIK_3 + SDL_SCANCODE_4, // DIK_4 + SDL_SCANCODE_5, // DIK_5 + SDL_SCANCODE_6, // DIK_6 + SDL_SCANCODE_7, // DIK_7 + SDL_SCANCODE_8, // DIK_8 + SDL_SCANCODE_9, // DIK_9 + SDL_SCANCODE_0, // DIK_0 + SDL_SCANCODE_MINUS, // DIK_MINUS + SDL_SCANCODE_EQUALS, // DIK_EQUALS + SDL_SCANCODE_BACKSPACE, // DIK_BACK + SDL_SCANCODE_TAB, // DIK_TAB + SDL_SCANCODE_Q, // DIK_Q + SDL_SCANCODE_W, // DIK_W + SDL_SCANCODE_E, // DIK_E + SDL_SCANCODE_R, // DIK_R + SDL_SCANCODE_T, // DIK_T + SDL_SCANCODE_Y, // DIK_Y + SDL_SCANCODE_U, // DIK_U + SDL_SCANCODE_I, // DIK_I + SDL_SCANCODE_O, // DIK_O + SDL_SCANCODE_P, // DIK_P + SDL_SCANCODE_LEFTBRACKET, // DIK_LBRACKET + SDL_SCANCODE_RIGHTBRACKET, // DIK_RBRACKET + SDL_SCANCODE_RETURN, // DIK_RETURN + SDL_SCANCODE_LCTRL, // DIK_LCONTROL + SDL_SCANCODE_A, // DIK_A + SDL_SCANCODE_S, // DIK_S + SDL_SCANCODE_D, // DIK_D + SDL_SCANCODE_F, // DIK_F + SDL_SCANCODE_G, // DIK_G + SDL_SCANCODE_H, // DIK_H + SDL_SCANCODE_J, // DIK_J + SDL_SCANCODE_K, // DIK_K + SDL_SCANCODE_L, // DIK_L + SDL_SCANCODE_SEMICOLON, // DIK_SEMICOLON + SDL_SCANCODE_APOSTROPHE, // DIK_APOSTROPHE + SDL_SCANCODE_GRAVE, // DIK_GRAVE + SDL_SCANCODE_LSHIFT, // DIK_LSHIFT + SDL_SCANCODE_BACKSLASH, // DIK_BACKSLASH + SDL_SCANCODE_Z, // DIK_Z + SDL_SCANCODE_X, // DIK_X + SDL_SCANCODE_C, // DIK_C + SDL_SCANCODE_V, // DIK_V + SDL_SCANCODE_B, // DIK_B + SDL_SCANCODE_N, // DIK_N + SDL_SCANCODE_M, // DIK_M + SDL_SCANCODE_COMMA, // DIK_COMMA + SDL_SCANCODE_PERIOD, // DIK_PERIOD + SDL_SCANCODE_SLASH, // DIK_SLASH + SDL_SCANCODE_RSHIFT, // DIK_RSHIFT + SDL_SCANCODE_KP_MULTIPLY, // DIK_MULTIPLY + SDL_SCANCODE_LALT, // DIK_LMENU + SDL_SCANCODE_SPACE, // DIK_SPACE + SDL_SCANCODE_CAPSLOCK, // DIK_CAPITAL + SDL_SCANCODE_F1, // DIK_F1 + SDL_SCANCODE_F2, // DIK_F2 + SDL_SCANCODE_F3, // DIK_F3 + SDL_SCANCODE_F4, // DIK_F4 + SDL_SCANCODE_F5, // DIK_F5 + SDL_SCANCODE_F6, // DIK_F6 + SDL_SCANCODE_F7, // DIK_F7 + SDL_SCANCODE_F8, // DIK_F8 + SDL_SCANCODE_F9, // DIK_F9 + SDL_SCANCODE_F10, // DIK_F10 + SDL_SCANCODE_NUMLOCKCLEAR, // DIK_NUMLOCK + SDL_SCANCODE_SCROLLLOCK, // DIK_SCROLL + SDL_SCANCODE_KP_7, // DIK_NUMPAD7 + SDL_SCANCODE_KP_8, // DIK_NUMPAD8 + SDL_SCANCODE_KP_9, // DIK_NUMPAD9 + SDL_SCANCODE_KP_MINUS, // DIK_SUBTRACT + SDL_SCANCODE_KP_4, // DIK_NUMPAD4 + SDL_SCANCODE_KP_5, // DIK_NUMPAD5 + SDL_SCANCODE_KP_6, // DIK_NUMPAD6 + SDL_SCANCODE_KP_PLUS, // DIK_ADD + SDL_SCANCODE_KP_1, // DIK_NUMPAD1 + SDL_SCANCODE_KP_2, // DIK_NUMPAD2 + SDL_SCANCODE_KP_3, // DIK_NUMPAD3 + SDL_SCANCODE_KP_0, // DIK_NUMPAD0 + SDL_SCANCODE_KP_PERIOD, // DIK_DECIMAL + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_F11, // DIK_F11 + SDL_SCANCODE_F12, // DIK_F12 + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_KP_EQUALS, // DIK_NUMPADEQUALS + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, // DIK_AT + SDL_SCANCODE_UNKNOWN, // DIK_COLON + SDL_SCANCODE_UNKNOWN, // DIK_UNDERLINE + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, // DIK_STOP + SDL_SCANCODE_UNKNOWN, // DIK_AX + SDL_SCANCODE_UNKNOWN, // DIK_UNLABELED + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_KP_ENTER, // DIK_NUMPADENTER + SDL_SCANCODE_RCTRL, // DIK_RCONTROL + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_KP_COMMA, // DIK_NUMPADCOMMA + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_SLASH, // DIK_DIVIDE + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_SYSREQ, // DIK_SYSRQ + SDL_SCANCODE_RALT, // DIK_RMENU + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_HOME, // DIK_HOME + SDL_SCANCODE_UP, // DIK_UP + SDL_SCANCODE_PAGEUP, // DIK_PRIOR + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_LEFT, // DIK_LEFT + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_RIGHT, // DIK_RIGHT + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_END, // DIK_END + SDL_SCANCODE_DOWN, // DIK_DOWN + SDL_SCANCODE_PAGEDOWN, // DIK_NEXT + SDL_SCANCODE_INSERT, // DIK_INSERT + SDL_SCANCODE_DELETE, // DIK_DELETE + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_LGUI, // DIK_LWIN + SDL_SCANCODE_RGUI, // DIK_RWIN + SDL_SCANCODE_APPLICATION, // DIK_APPS + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, +}; + +/// Translates Sfall key code (DIK or VK constant) to SDL scancode. +static SDL_Scancode get_scancode_from_key(int key) +{ + return kDiks[key & 0xFF]; +} + +bool sfall_kb_is_key_pressed(int key) +{ + SDL_Scancode scancode = get_scancode_from_key(key); + if (scancode == SDL_SCANCODE_UNKNOWN) { + return false; + } + + const Uint8* state = SDL_GetKeyboardState(nullptr); + return state[scancode] != 0; +} + +void sfall_kb_press_key(int key) +{ + SDL_Scancode scancode = get_scancode_from_key(key); + if (scancode == SDL_SCANCODE_UNKNOWN) { + return; + } + + SDL_Event event; + event.key.keysym.scancode = scancode; + + event.type = SDL_KEYDOWN; + SDL_PushEvent(&event); + + event.type = SDL_KEYUP; + SDL_PushEvent(&event); +} diff --git a/src/sfall_kb_helpers.h b/src/sfall_kb_helpers.h new file mode 100644 index 0000000..ff22959 --- /dev/null +++ b/src/sfall_kb_helpers.h @@ -0,0 +1,10 @@ +#ifndef FALLOUT_SFALL_KB_HELPERS_H_ +#define FALLOUT_SFALL_KB_HELPERS_H_ + +/// Returns `true` if given key is pressed. +bool sfall_kb_is_key_pressed(int key); + +/// Simulates pressing `key`. +void sfall_kb_press_key(int key); + +#endif /* FALLOUT_SFALL_KB_HELPERS_H_ */ diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index e21a079..3cabe4a 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -24,6 +24,7 @@ #include "sfall_global_scripts.h" #include "sfall_global_vars.h" #include "sfall_ini.h" +#include "sfall_kb_helpers.h" #include "sfall_lists.h" #include "stat.h" #include "svga.h" @@ -94,6 +95,13 @@ static void opGetPcBonusStat(Program* program) programStackPushInteger(program, value); } +// tap_key +static void op_tap_key(Program* program) +{ + int key = programStackPopInteger(program); + sfall_kb_press_key(key); +} + // get_year static void op_get_year(Program* program) { @@ -120,10 +128,8 @@ static void op_set_global_script_repeat(Program* program) static void op_key_pressed(Program* program) { int key = programStackPopInteger(program); - - // TODO: Incomplete. - - programStackPushInteger(program, 0); + bool pressed = sfall_kb_is_key_pressed(key); + programStackPushInteger(program, pressed ? 1 : 0); } // in_world_map @@ -882,6 +888,7 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x815B, opSetPcBonusStat); interpreterRegisterOpcode(0x815C, op_get_pc_base_stat); interpreterRegisterOpcode(0x815D, opGetPcBonusStat); + interpreterRegisterOpcode(0x8162, op_tap_key); interpreterRegisterOpcode(0x8163, op_get_year); interpreterRegisterOpcode(0x8164, op_game_loaded); interpreterRegisterOpcode(0x816A, op_set_global_script_repeat); From cf5f865a23e84a23e7ec27cf2d05f0a262f3b9d5 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 3 Jun 2023 08:06:49 +0300 Subject: [PATCH 016/136] Add load/save global vars (#296) --- src/game.cc | 8 +-- src/interpreter.cc | 10 ++++ src/loadsave.cc | 86 ++++++++++++++++++++++++++++ src/sfall_global_scripts.cc | 7 +++ src/sfall_global_scripts.h | 1 + src/sfall_global_vars.cc | 111 +++++++++++++++++++++++++++--------- src/sfall_global_vars.h | 18 +++--- src/sfall_opcodes.cc | 8 +-- 8 files changed, 206 insertions(+), 43 deletions(-) diff --git a/src/game.cc b/src/game.cc index c215edf..abe6613 100644 --- a/src/game.cc +++ b/src/game.cc @@ -341,8 +341,8 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4 // SFALL premadeCharactersInit(); - if (!sfallGlobalVarsInit()) { - debugPrint("Failed on sfallGlobalVarsInit"); + if (!sfall_gl_vars_init()) { + debugPrint("Failed on sfall_gl_vars_init"); return -1; } @@ -409,7 +409,7 @@ void gameReset() _init_options_menu(); // SFALL - sfallGlobalVarsReset(); + sfall_gl_vars_reset(); sfallListsReset(); messageListRepositoryReset(); sfallArraysReset(); @@ -425,7 +425,7 @@ void gameExit() sfall_gl_scr_exit(); sfallArraysExit(); sfallListsExit(); - sfallGlobalVarsExit(); + sfall_gl_vars_exit(); premadeCharactersExit(); tileDisable(); diff --git a/src/interpreter.cc b/src/interpreter.cc index b2e0cd1..ab866ce 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -14,6 +14,7 @@ #include "interpreter_lib.h" #include "memory_manager.h" #include "platform_compat.h" +#include "sfall_global_scripts.h" #include "svga.h" namespace fallout { @@ -3011,6 +3012,15 @@ Program* runScript(char* name) // 0x46E1EC void _updatePrograms() { + // CE: Implementation is different. Sfall inserts global scripts into + // program list upon creation, so engine does not diffirentiate between + // global and normal scripts. Global scripts in CE are not part of program + // list, so we need a separate call to continue execution (usually + // non-critical calls scheduled from managed windows). One more thing to + // note is that global scripts in CE cannot handle conditional/timed procs + // (which are not used anyway). + sfall_gl_scr_update(_cpuBurstSize); + ProgramListNode* curr = gInterpreterProgramListHead; while (curr != NULL) { ProgramListNode* next = curr->next; diff --git a/src/loadsave.cc b/src/loadsave.cc index ebee1e6..e5d8db6 100644 --- a/src/loadsave.cc +++ b/src/loadsave.cc @@ -46,6 +46,7 @@ #include "scripts.h" #include "settings.h" #include "sfall_global_scripts.h" +#include "sfall_global_vars.h" #include "skill.h" #include "stat.h" #include "svga.h" @@ -1588,6 +1589,73 @@ static int lsgPerformSaveGame() fileClose(_flptr); + // SFALL: Save sfallgv.sav. + snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1); + strcat(_gmpath, "sfallgv.sav"); + + _flptr = fileOpen(_gmpath, "wb"); + if (_flptr != NULL) { + do { + if (!sfall_gl_vars_save(_flptr)) { + debugPrint("LOADSAVE (SFALL): ** Error saving global vars **\n"); + break; + } + + // TODO: For now fill remaining sections with zeros to that Sfall + // can successfully read our global vars and skip the rest. + + int nextObjectId = 0; + if (fileWrite(&nextObjectId, sizeof(nextObjectId), 1, _flptr) != 1) { + debugPrint("LOADSAVE (SFALL): ** Error saving next object id **\n"); + break; + } + + int addedYears = 0; + if (fileWrite(&addedYears, sizeof(addedYears), 1, _flptr) != 1) { + debugPrint("LOADSAVE (SFALL): ** Error saving added years **\n"); + break; + } + + int fakeTraitsCount = 0; + if (fileWrite(&fakeTraitsCount, sizeof(fakeTraitsCount), 1, _flptr) != 1) { + debugPrint("LOADSAVE (SFALL): ** Error saving fake traits **\n"); + break; + } + + int fakePerksCount = 0; + if (fileWrite(&fakePerksCount, sizeof(fakePerksCount), 1, _flptr) != 1) { + debugPrint("LOADSAVE (SFALL): ** Error saving fake perks **\n"); + break; + } + + int fakeSelectablePerksCount = 0; + if (fileWrite(&fakeSelectablePerksCount, sizeof(fakeSelectablePerksCount), 1, _flptr) != 1) { + debugPrint("LOADSAVE (SFALL): ** Error saving fake selectable perks **\n"); + break; + } + + int arraysCountOld = 0; + if (fileWrite(&arraysCountOld, sizeof(arraysCountOld), 1, _flptr) != 1) { + debugPrint("LOADSAVE (SFALL): ** Error saving arrays (old fmt) **\n"); + break; + } + + int arraysCountNew = 0; + if (fileWrite(&arraysCountNew, sizeof(arraysCountNew), 1, _flptr) != 1) { + debugPrint("LOADSAVE (SFALL): ** Error saving arrays (new fmt) **\n"); + break; + } + + int drugPidsCount = 0; + if (fileWrite(&drugPidsCount, sizeof(drugPidsCount), 1, _flptr) != 1) { + debugPrint("LOADSAVE (SFALL): ** Error saving drug pids **\n"); + break; + } + } while (0); + + fileClose(_flptr); + } + snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1); _MapDirErase(_gmpath, "BAK"); @@ -1663,6 +1731,24 @@ static int lsgLoadGameInSlot(int slot) debugPrint("LOADSAVE: Total load data read: %ld bytes.\n", fileTell(_flptr)); fileClose(_flptr); + // SFALL: Load sfallgv.sav. + snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1); + strcat(_gmpath, "sfallgv.sav"); + + _flptr = fileOpen(_gmpath, "rb"); + if (_flptr != NULL) { + do { + if (!sfall_gl_vars_load(_flptr)) { + debugPrint("LOADSAVE (SFALL): ** Error loading global vars **\n"); + break; + } + + // TODO: For now silently ignore remaining sections. + } while (0); + + fileClose(_flptr); + } + snprintf(_str, sizeof(_str), "%s\\", "MAPS"); _MapDirErase(_str, "BAK"); _proto_dude_update_gender(); diff --git a/src/sfall_global_scripts.cc b/src/sfall_global_scripts.cc index 1cd65d7..067d4fb 100644 --- a/src/sfall_global_scripts.cc +++ b/src/sfall_global_scripts.cc @@ -210,4 +210,11 @@ bool sfall_gl_scr_is_loaded(Program* program) return true; } +void sfall_gl_scr_update(int burstSize) +{ + for (auto& scr : state->globalScripts) { + _interpret(scr.program, burstSize); + } +} + } // namespace fallout diff --git a/src/sfall_global_scripts.h b/src/sfall_global_scripts.h index bf8eadf..7ed11c7 100644 --- a/src/sfall_global_scripts.h +++ b/src/sfall_global_scripts.h @@ -17,6 +17,7 @@ void sfall_gl_scr_process_worldmap(); void sfall_gl_scr_set_repeat(Program* program, int frames); void sfall_gl_scr_set_type(Program* program, int type); bool sfall_gl_scr_is_loaded(Program* program); +void sfall_gl_scr_update(int burstSize); } // namespace fallout diff --git a/src/sfall_global_vars.cc b/src/sfall_global_vars.cc index b62c0dc..b01c337 100644 --- a/src/sfall_global_vars.cc +++ b/src/sfall_global_vars.cc @@ -10,72 +10,127 @@ struct SfallGlobalVarsState { std::unordered_map vars; }; -static bool sfallGlobalVarsStore(uint64_t key, int value); -static bool sfallGlobalVarsFetch(uint64_t key, int& value); +#pragma pack(push) +#pragma pack(8) -static SfallGlobalVarsState* _state; +/// Matches Sfall's `GlobalVar` to maintain binary compatibility. +struct GlobalVarEntry { + uint64_t key; + int32_t value; + int32_t unused; +}; -bool sfallGlobalVarsInit() +#pragma pack(pop) + +static bool sfall_gl_vars_store(uint64_t key, int value); +static bool sfall_gl_vars_fetch(uint64_t key, int& value); + +static SfallGlobalVarsState* sfall_gl_vars_state = nullptr; + +bool sfall_gl_vars_init() { - _state = new (std::nothrow) SfallGlobalVarsState(); - if (_state == nullptr) { + sfall_gl_vars_state = new (std::nothrow) SfallGlobalVarsState(); + if (sfall_gl_vars_state == nullptr) { return false; } return true; } -void sfallGlobalVarsReset() +void sfall_gl_vars_reset() { - _state->vars.clear(); + sfall_gl_vars_state->vars.clear(); } -void sfallGlobalVarsExit() +void sfall_gl_vars_exit() { - if (_state != nullptr) { - delete _state; - _state = nullptr; + if (sfall_gl_vars_state != nullptr) { + delete sfall_gl_vars_state; + sfall_gl_vars_state = nullptr; } } -bool sfallGlobalVarsStore(const char* key, int value) +bool sfall_gl_vars_save(File* stream) +{ + int count = static_cast(sfall_gl_vars_state->vars.size()); + if (fileWrite(&count, sizeof(count), 1, stream) != 1) { + return false; + } + + GlobalVarEntry entry = { 0 }; + for (auto& pair : sfall_gl_vars_state->vars) { + entry.key = pair.first; + entry.value = pair.second; + + if (fileWrite(&entry, sizeof(entry), 1, stream) != 1) { + return false; + } + } + + return true; +} + +bool sfall_gl_vars_load(File* stream) +{ + int count; + if (fileRead(&count, sizeof(count), 1, stream) != 1) { + return false; + } + + sfall_gl_vars_state->vars.reserve(count); + + GlobalVarEntry entry; + while (count > 0) { + if (fileRead(&entry, sizeof(entry), 1, stream) != 1) { + return false; + } + + sfall_gl_vars_state->vars[entry.key] = static_cast(entry.value); + + count--; + } + + return true; +} + +bool sfall_gl_vars_store(const char* key, int value) { if (strlen(key) != 8) { return false; } uint64_t numericKey = *(reinterpret_cast(key)); - return sfallGlobalVarsStore(numericKey, value); + return sfall_gl_vars_store(numericKey, value); } -bool sfallGlobalVarsStore(int key, int value) +bool sfall_gl_vars_store(int key, int value) { - return sfallGlobalVarsStore(static_cast(key), value); + return sfall_gl_vars_store(static_cast(key), value); } -bool sfallGlobalVarsFetch(const char* key, int& value) +bool sfall_gl_vars_fetch(const char* key, int& value) { if (strlen(key) != 8) { return false; } uint64_t numericKey = *(reinterpret_cast(key)); - return sfallGlobalVarsFetch(numericKey, value); + return sfall_gl_vars_fetch(numericKey, value); } -bool sfallGlobalVarsFetch(int key, int& value) +bool sfall_gl_vars_fetch(int key, int& value) { - return sfallGlobalVarsFetch(static_cast(key), value); + return sfall_gl_vars_fetch(static_cast(key), value); } -static bool sfallGlobalVarsStore(uint64_t key, int value) +static bool sfall_gl_vars_store(uint64_t key, int value) { - auto it = _state->vars.find(key); - if (it == _state->vars.end()) { - _state->vars.emplace(key, value); + auto it = sfall_gl_vars_state->vars.find(key); + if (it == sfall_gl_vars_state->vars.end()) { + sfall_gl_vars_state->vars.emplace(key, value); } else { if (value == 0) { - _state->vars.erase(it); + sfall_gl_vars_state->vars.erase(it); } else { it->second = value; } @@ -84,10 +139,10 @@ static bool sfallGlobalVarsStore(uint64_t key, int value) return true; } -static bool sfallGlobalVarsFetch(uint64_t key, int& value) +static bool sfall_gl_vars_fetch(uint64_t key, int& value) { - auto it = _state->vars.find(key); - if (it == _state->vars.end()) { + auto it = sfall_gl_vars_state->vars.find(key); + if (it == sfall_gl_vars_state->vars.end()) { return false; } diff --git a/src/sfall_global_vars.h b/src/sfall_global_vars.h index 184fc6f..dfa7133 100644 --- a/src/sfall_global_vars.h +++ b/src/sfall_global_vars.h @@ -1,15 +1,19 @@ #ifndef FALLOUT_SFALL_GLOBAL_VARS_H_ #define FALLOUT_SFALL_GLOBAL_VARS_H_ +#include "db.h" + namespace fallout { -bool sfallGlobalVarsInit(); -void sfallGlobalVarsReset(); -void sfallGlobalVarsExit(); -bool sfallGlobalVarsStore(const char* key, int value); -bool sfallGlobalVarsStore(int key, int value); -bool sfallGlobalVarsFetch(const char* key, int& value); -bool sfallGlobalVarsFetch(int key, int& value); +bool sfall_gl_vars_init(); +void sfall_gl_vars_reset(); +void sfall_gl_vars_exit(); +bool sfall_gl_vars_save(File* stream); +bool sfall_gl_vars_load(File* stream); +bool sfall_gl_vars_store(const char* key, int value); +bool sfall_gl_vars_store(int key, int value); +bool sfall_gl_vars_fetch(const char* key, int& value); +bool sfall_gl_vars_fetch(int key, int& value); } // namespace fallout diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index 3cabe4a..ea45d4e 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -167,9 +167,9 @@ static void opSetGlobalVar(Program* program) if ((variable.opcode & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) { const char* key = programGetString(program, variable.opcode, variable.integerValue); - sfallGlobalVarsStore(key, value.integerValue); + sfall_gl_vars_store(key, value.integerValue); } else if (variable.opcode == VALUE_TYPE_INT) { - sfallGlobalVarsStore(variable.integerValue, value.integerValue); + sfall_gl_vars_store(variable.integerValue, value.integerValue); } } @@ -181,9 +181,9 @@ static void opGetGlobalInt(Program* program) int value = 0; if ((variable.opcode & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) { const char* key = programGetString(program, variable.opcode, variable.integerValue); - sfallGlobalVarsFetch(key, value); + sfall_gl_vars_fetch(key, value); } else if (variable.opcode == VALUE_TYPE_INT) { - sfallGlobalVarsFetch(variable.integerValue, value); + sfall_gl_vars_fetch(variable.integerValue, value); } programStackPushInteger(program, value); From 0ffcb107af4b440f06d380d9da851383f45761ea Mon Sep 17 00:00:00 2001 From: Austin Hurst Date: Fri, 16 Jun 2023 01:18:15 -0300 Subject: [PATCH 017/136] Implement SCALE_2X for Fallout 2 (#306) --- src/svga.cc | 19 ++++++++++++++++--- src/svga.h | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/svga.cc b/src/svga.cc index e361fea..0d90650 100644 --- a/src/svga.cc +++ b/src/svga.cc @@ -102,6 +102,7 @@ void _zero_vid_mem() int _GNW95_init_mode_ex(int width, int height, int bpp) { bool fullscreen = true; + int scale = 1; Config resolutionConfig; if (configInit(&resolutionConfig)) { @@ -121,6 +122,18 @@ int _GNW95_init_mode_ex(int width, int height, int bpp) fullscreen = !windowed; } + int scaleValue; + if (configGetInt(&resolutionConfig, "MAIN", "SCALE_2X", &scaleValue)) { + scale = scaleValue + 1; // 0 = 1x, 1 = 2x + // Only allow scaling if resulting game resolution is >= 640x480 + if ((width / scale) < 640 || (height / scale) < 480) { + scale = 1; + } else { + width /= scale; + height /= scale; + } + } + configGetBool(&resolutionConfig, "IFACE", "IFACE_BAR_MODE", &gInterfaceBarMode); configGetInt(&resolutionConfig, "IFACE", "IFACE_BAR_WIDTH", &gInterfaceBarWidth); configGetInt(&resolutionConfig, "IFACE", "IFACE_BAR_SIDE_ART", &gInterfaceSidePanelsImageId); @@ -129,7 +142,7 @@ int _GNW95_init_mode_ex(int width, int height, int bpp) configFree(&resolutionConfig); } - if (_GNW95_init_window(width, height, fullscreen) == -1) { + if (_GNW95_init_window(width, height, fullscreen, scale) == -1) { return -1; } @@ -157,7 +170,7 @@ int _init_vesa_mode(int width, int height) } // 0x4CAEDC -int _GNW95_init_window(int width, int height, bool fullscreen) +int _GNW95_init_window(int width, int height, bool fullscreen, int scale) { if (gSdlWindow == NULL) { SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); @@ -172,7 +185,7 @@ int _GNW95_init_window(int width, int height, bool fullscreen) windowFlags |= SDL_WINDOW_FULLSCREEN; } - gSdlWindow = SDL_CreateWindow(gProgramWindowTitle, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, windowFlags); + gSdlWindow = SDL_CreateWindow(gProgramWindowTitle, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width * scale, height * scale, windowFlags); if (gSdlWindow == NULL) { return -1; } diff --git a/src/svga.h b/src/svga.h index 510d724..b5882df 100644 --- a/src/svga.h +++ b/src/svga.h @@ -31,7 +31,7 @@ void _get_start_mode_(); void _zero_vid_mem(); int _GNW95_init_mode_ex(int width, int height, int bpp); int _init_vesa_mode(int width, int height); -int _GNW95_init_window(int width, int height, bool fullscreen); +int _GNW95_init_window(int width, int height, bool fullscreen, int scale); int directDrawInit(int width, int height, int bpp); void directDrawFree(); void directDrawSetPaletteInRange(unsigned char* a1, int a2, int a3); From cf98070cd00d47d53ce4fea435d3999582dbe786 Mon Sep 17 00:00:00 2001 From: c6 <31777460+c6-dev@users.noreply.github.com> Date: Fri, 16 Jun 2023 09:01:44 +0400 Subject: [PATCH 018/136] Add Sfall AutoQuickSave (#305) --- src/loadsave.cc | 20 ++++++++++++++++++++ src/sfall_config.cc | 1 + src/sfall_config.h | 1 + 3 files changed, 22 insertions(+) diff --git a/src/loadsave.cc b/src/loadsave.cc index e5d8db6..e05dc85 100644 --- a/src/loadsave.cc +++ b/src/loadsave.cc @@ -45,6 +45,7 @@ #include "random.h" #include "scripts.h" #include "settings.h" +#include "sfall_config.h" #include "sfall_global_scripts.h" #include "sfall_global_vars.h" #include "skill.h" @@ -328,6 +329,9 @@ static int gLoadSaveWindowOldFont; static FrmImage _loadsaveFrmImages[LOAD_SAVE_FRM_COUNT]; +static int quickSaveSlots = 0; +static bool autoQuickSaveSlots = false; + // 0x47B7E4 void _InitLoadSave() { @@ -338,6 +342,11 @@ void _InitLoadSave() _MapDirErase("MAPS\\", "SAV"); _MapDirErase(PROTO_DIR_NAME "\\" CRITTERS_DIR_NAME "\\", PROTO_FILE_EXT); _MapDirErase(PROTO_DIR_NAME "\\" ITEMS_DIR_NAME "\\", PROTO_FILE_EXT); + + configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_AUTO_QUICK_SAVE, &quickSaveSlots); + if (quickSaveSlots > 0 && quickSaveSlots <= 10) { + autoQuickSaveSlots = true; + } } // 0x47B85C @@ -359,7 +368,18 @@ int lsgSaveGame(int mode) _ls_error_code = 0; _patches = settings.system.master_patches_path.c_str(); + // SFALL: skip slot selection if auto quicksave is enabled + if (autoQuickSaveSlots) { + _quick_done = true; + } + if (mode == LOAD_SAVE_MODE_QUICK && _quick_done) { + // SFALL: cycle through first N slots for quicksaving + if (autoQuickSaveSlots) { + if (++_slot_cursor >= quickSaveSlots) { + _slot_cursor = 0; + } + } snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1); strcat(_gmpath, "SAVE.DAT"); diff --git a/src/sfall_config.cc b/src/sfall_config.cc index b85a83b..353bb37 100644 --- a/src/sfall_config.cc +++ b/src/sfall_config.cc @@ -57,6 +57,7 @@ bool sfallConfigInit(int argc, char** argv) configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER2, 180); configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER3, 270); configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER4, 360); + configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_AUTO_QUICK_SAVE, 0); configSetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_INI_CONFIG_FOLDER, ""); configSetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_GLOBAL_SCRIPT_PATHS, ""); diff --git a/src/sfall_config.h b/src/sfall_config.h index 1f786ce..da2c298 100644 --- a/src/sfall_config.h +++ b/src/sfall_config.h @@ -71,6 +71,7 @@ namespace fallout { #define SFALL_CONFIG_NUMBERS_IS_DIALOG_KEY "NumbersInDialogue" #define SFALL_CONFIG_INI_CONFIG_FOLDER "IniConfigFolder" #define SFALL_CONFIG_GLOBAL_SCRIPT_PATHS "GlobalScriptPaths" +#define SFALL_CONFIG_AUTO_QUICK_SAVE "AutoQuickSave" #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MULTIPLIER 1 #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR 3 From fe035d8514baffc77e80bf313d3755d592cdae1d Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 16 Jun 2023 08:12:38 +0300 Subject: [PATCH 019/136] Improve loadsave.cc readability --- src/loadsave.cc | 253 ++++++++++++++++++++++++------------------------ src/loadsave.h | 2 +- 2 files changed, 128 insertions(+), 127 deletions(-) diff --git a/src/loadsave.cc b/src/loadsave.cc index e05dc85..4886ba8 100644 --- a/src/loadsave.cc +++ b/src/loadsave.cc @@ -61,6 +61,12 @@ namespace fallout { +#define LOAD_SAVE_SIGNATURE "FALLOUT SAVE FILE" +#define LOAD_SAVE_DESCRIPTION_LENGTH 30 +#define LOAD_SAVE_HANDLER_COUNT 27 + +#define LSGAME_MSG_NAME "LSGAME.MSG" + #define LS_WINDOW_WIDTH 640 #define LS_WINDOW_HEIGHT 480 @@ -83,8 +89,8 @@ namespace fallout { #define ITEMS_DIR_NAME "items" #define PROTO_FILE_EXT "pro" -#define LOAD_SAVE_DESCRIPTION_LENGTH (30) -#define LOAD_SAVE_HANDLER_COUNT (27) +typedef int LoadGameHandler(File* stream); +typedef int SaveGameHandler(File* stream); typedef enum LoadSaveWindowType { LOAD_SAVE_WINDOW_TYPE_SAVE_GAME, @@ -107,33 +113,28 @@ typedef enum LoadSaveScrollDirection { LOAD_SAVE_SCROLL_DIRECTION_DOWN, } LoadSaveScrollDirection; -typedef int LoadGameHandler(File* stream); -typedef int SaveGameHandler(File* stream); - -#define LSGAME_MSG_NAME ("LSGAME.MSG") - -typedef struct STRUCT_613D30 { - char field_0[24]; - short field_18; - short field_1A; +typedef struct LoadSaveSlotData { + char signature[24]; + short versionMinor; + short versionMajor; // TODO: The type is probably char, but it's read with the same function as // reading unsigned chars, which in turn probably result of collapsing // reading functions. - unsigned char field_1C; - char character_name[32]; + unsigned char versionRelease; + char characterName[32]; char description[LOAD_SAVE_DESCRIPTION_LENGTH]; - short field_5C; - short field_5E; - short field_60; - int field_64; - short field_68; - short field_6A; - short field_6C; - int field_70; - short field_74; - short field_76; - char file_name[16]; -} STRUCT_613D30; + short fileMonth; + short fileDay; + short fileYear; + int fileTime; + short gameMonth; + short gameDay; + short gameYear; + int gameTime; + short elevation; + short map; + char fileName[16]; +} LoadSaveSlotData; typedef enum LoadSaveFrm { LOAD_SAVE_FRM_BACKGROUND, @@ -156,10 +157,10 @@ static int lsgLoadGameInSlot(int slot); static int lsgSaveHeaderInSlot(int slot); static int lsgLoadHeaderInSlot(int slot); static int _GetSlotList(); -static void _ShowSlotList(int a1); -static void _DrawInfoBox(int a1); -static int _LoadTumbSlot(int a1); -static int _GetComment(int a1); +static void _ShowSlotList(int windowType); +static void _DrawInfoBox(int slot); +static int _LoadTumbSlot(int slot); +static int _GetComment(int slot); static int _get_input_str2(int win, int doneKeyCode, int cancelKeyCode, char* description, int maxLength, int x, int y, int textColor, int backgroundColor, int flags); static int _DummyFunc(File* stream); static int _PrepLoad(File* stream); @@ -167,8 +168,8 @@ static int _EndLoad(File* stream); static int _GameMap2Slot(File* stream); static int _SlotMap2Game(File* stream); static int _mygets(char* dest, File* stream); -static int _copy_file(const char* a1, const char* a2); -static int _MapDirErase(const char* path, const char* a2); +static int _copy_file(const char* existingFileName, const char* newFileName); +static int _MapDirErase(const char* path, const char* extension); static int _SaveBackup(); static int _RestoreSave(); static int _LoadObjDudeCid(File* stream); @@ -201,7 +202,7 @@ static bool gLoadSaveWindowIsoWasEnabled = false; static int _map_backup_count = -1; // 0x5193C8 -static int _automap_db_flag = 0; +static bool _automap_db_flag = false; // 0x5193CC static const char* _patches = NULL; @@ -269,7 +270,7 @@ static LoadGameHandler* _master_load_list[LOAD_SAVE_HANDLER_COUNT] = { }; // 0x5194C4 -static int _loadingGame = 0; +static bool _loadingGame = false; // lsgame.msg // @@ -277,7 +278,7 @@ static int _loadingGame = 0; static MessageList gLoadSaveMessageList; // 0x613D30 -static STRUCT_613D30 _LSData[10]; +static LoadSaveSlotData _LSData[10]; // 0x614280 static int _LSstatus[10]; @@ -493,7 +494,7 @@ int lsgSaveGame(int mode) break; } - _ShowSlotList(0); + _ShowSlotList(LOAD_SAVE_WINDOW_TYPE_SAVE_GAME); _DrawInfoBox(_slot_cursor); windowRefresh(gLoadSaveWindow); @@ -998,7 +999,7 @@ int lsgLoadGame(int mode) break; } - _ShowSlotList(2); + _ShowSlotList(LOAD_SAVE_WINDOW_TYPE_LOAD_GAME); _DrawInfoBox(_slot_cursor); windowRefresh(gLoadSaveWindow); renderPresent(); @@ -1169,7 +1170,7 @@ int lsgLoadGame(int mode) break; } - _ShowSlotList(2); + _ShowSlotList(LOAD_SAVE_WINDOW_TYPE_LOAD_GAME); _DrawInfoBox(_slot_cursor); windowRefresh(gLoadSaveWindow); } @@ -1216,7 +1217,7 @@ int lsgLoadGame(int mode) } _DrawInfoBox(_slot_cursor); - _ShowSlotList(2); + _ShowSlotList(LOAD_SAVE_WINDOW_TYPE_LOAD_GAME); } windowRefresh(gLoadSaveWindow); @@ -1692,7 +1693,7 @@ static int lsgPerformSaveGame() } // 0x47DC60 -int _isLoadingGame() +bool _isLoadingGame() { return _loadingGame; } @@ -1700,7 +1701,7 @@ int _isLoadingGame() // 0x47DC68 static int lsgLoadGameInSlot(int slot) { - _loadingGame = 1; + _loadingGame = true; if (isInCombat()) { interfaceBarEndButtonsHide(false); @@ -1711,13 +1712,13 @@ static int lsgLoadGameInSlot(int slot) snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1); strcat(_gmpath, "SAVE.DAT"); - STRUCT_613D30* ptr = &(_LSData[slot]); + LoadSaveSlotData* ptr = &(_LSData[slot]); debugPrint("\nLOADSAVE: Load name: %s\n", ptr->description); _flptr = fileOpen(_gmpath, "rb"); if (_flptr == NULL) { debugPrint("\nLOADSAVE: ** Error opening load game file for reading! **\n"); - _loadingGame = 0; + _loadingGame = false; return -1; } @@ -1726,7 +1727,7 @@ static int lsgLoadGameInSlot(int slot) debugPrint("\nLOADSAVE: ** Error reading save game header! **\n"); fileClose(_flptr); gameReset(); - _loadingGame = 0; + _loadingGame = false; return -1; } @@ -1741,7 +1742,7 @@ static int lsgLoadGameInSlot(int slot) debugPrint("LOADSAVE: Load function #%d data size read: %d bytes.\n", index, fileTell(_flptr) - pos); fileClose(_flptr); gameReset(); - _loadingGame = 0; + _loadingGame = false; return -1; } @@ -1781,7 +1782,7 @@ static int lsgLoadGameInSlot(int slot) debugPrint("\nError: Couldn't find LoadSave Message!"); } - _loadingGame = 0; + _loadingGame = false; // SFALL: Start global scripts. sfall_gl_scr_exec_start_proc(); @@ -1794,10 +1795,10 @@ static int lsgSaveHeaderInSlot(int slot) { _ls_error_code = 4; - STRUCT_613D30* ptr = &(_LSData[slot]); - strncpy(ptr->field_0, "FALLOUT SAVE FILE", 24); + LoadSaveSlotData* ptr = &(_LSData[slot]); + strncpy(ptr->signature, LOAD_SAVE_SIGNATURE, 24); - if (fileWrite(ptr->field_0, 1, 24, _flptr) == -1) { + if (fileWrite(ptr->signature, 1, 24, _flptr) == -1) { return -1; } @@ -1805,22 +1806,22 @@ static int lsgSaveHeaderInSlot(int slot) temp[0] = VERSION_MAJOR; temp[1] = VERSION_MINOR; - ptr->field_18 = temp[0]; - ptr->field_1A = temp[1]; + ptr->versionMinor = temp[0]; + ptr->versionMajor = temp[1]; if (fileWriteInt16List(_flptr, temp, 2) == -1) { return -1; } - ptr->field_1C = VERSION_RELEASE; + ptr->versionRelease = VERSION_RELEASE; if (fileWriteUInt8(_flptr, VERSION_RELEASE) == -1) { return -1; } char* characterName = critterGetName(gDude); - strncpy(ptr->character_name, characterName, 32); + strncpy(ptr->characterName, characterName, 32); - if (fileWrite(ptr->character_name, 32, 1, _flptr) != 1) { + if (fileWrite(ptr->characterName, 32, 1, _flptr) != 1) { return -1; } @@ -1835,16 +1836,16 @@ static int lsgSaveHeaderInSlot(int slot) temp[1] = local->tm_mon + 1; temp[2] = local->tm_year + 1900; - ptr->field_5E = temp[0]; - ptr->field_5C = temp[1]; - ptr->field_60 = temp[2]; - ptr->field_64 = local->tm_hour + local->tm_min; + ptr->fileDay = temp[0]; + ptr->fileMonth = temp[1]; + ptr->fileYear = temp[2]; + ptr->fileTime = local->tm_hour + local->tm_min; if (fileWriteInt16List(_flptr, temp, 3) == -1) { return -1; } - if (_db_fwriteLong(_flptr, ptr->field_64) == -1) { + if (_db_fwriteLong(_flptr, ptr->fileTime) == -1) { return -1; } @@ -1856,23 +1857,23 @@ static int lsgSaveHeaderInSlot(int slot) temp[0] = month; temp[1] = day; temp[2] = year; - ptr->field_70 = gameTimeGetTime(); + ptr->gameTime = gameTimeGetTime(); if (fileWriteInt16List(_flptr, temp, 3) == -1) { return -1; } - if (_db_fwriteLong(_flptr, ptr->field_70) == -1) { + if (_db_fwriteLong(_flptr, ptr->gameTime) == -1) { return -1; } - ptr->field_74 = gElevation; - if (fileWriteInt16(_flptr, ptr->field_74) == -1) { + ptr->elevation = gElevation; + if (fileWriteInt16(_flptr, ptr->elevation) == -1) { return -1; } - ptr->field_76 = mapGetCurrentMap(); - if (fileWriteInt16(_flptr, ptr->field_76) == -1) { + ptr->map = mapGetCurrentMap(); + if (fileWriteInt16(_flptr, ptr->map) == -1) { return -1; } @@ -1881,8 +1882,8 @@ static int lsgSaveHeaderInSlot(int slot) // NOTE: Uppercased from "sav". char* v1 = _strmfe(_str, mapName, "SAV"); - strncpy(ptr->file_name, v1, 16); - if (fileWrite(ptr->file_name, 16, 1, _flptr) != 1) { + strncpy(ptr->fileName, v1, 16); + if (fileWrite(ptr->fileName, 16, 1, _flptr) != 1) { return -1; } @@ -1905,13 +1906,13 @@ static int lsgLoadHeaderInSlot(int slot) { _ls_error_code = 3; - STRUCT_613D30* ptr = &(_LSData[slot]); + LoadSaveSlotData* ptr = &(_LSData[slot]); - if (fileRead(ptr->field_0, 1, 24, _flptr) != 24) { + if (fileRead(ptr->signature, 1, 24, _flptr) != 24) { return -1; } - if (strncmp(ptr->field_0, "FALLOUT SAVE FILE", 18) != 0) { + if (strncmp(ptr->signature, LOAD_SAVE_SIGNATURE, 18) != 0) { debugPrint("\nLOADSAVE: ** Invalid save file on load! **\n"); _ls_error_code = 2; return -1; @@ -1922,20 +1923,20 @@ static int lsgLoadHeaderInSlot(int slot) return -1; } - ptr->field_18 = v8[0]; - ptr->field_1A = v8[1]; + ptr->versionMinor = v8[0]; + ptr->versionMajor = v8[1]; - if (fileReadUInt8(_flptr, &(ptr->field_1C)) == -1) { + if (fileReadUInt8(_flptr, &(ptr->versionRelease)) == -1) { return -1; } - if (ptr->field_18 != 1 || ptr->field_1A != 2 || ptr->field_1C != 'R') { - debugPrint("\nLOADSAVE: Load slot #%d Version: %d.%d%c\n", slot, ptr->field_18, ptr->field_1A, ptr->field_1C); + if (ptr->versionMinor != 1 || ptr->versionMajor != 2 || ptr->versionRelease != 'R') { + debugPrint("\nLOADSAVE: Load slot #%d Version: %d.%d%c\n", slot, ptr->versionMinor, ptr->versionMajor, ptr->versionRelease); _ls_error_code = 1; return -1; } - if (fileRead(ptr->character_name, 32, 1, _flptr) != 1) { + if (fileRead(ptr->characterName, 32, 1, _flptr) != 1) { return -1; } @@ -1947,11 +1948,11 @@ static int lsgLoadHeaderInSlot(int slot) return -1; } - ptr->field_5C = v8[0]; - ptr->field_5E = v8[1]; - ptr->field_60 = v8[2]; + ptr->fileMonth = v8[0]; + ptr->fileDay = v8[1]; + ptr->fileYear = v8[2]; - if (_db_freadInt(_flptr, &(ptr->field_64)) == -1) { + if (_db_freadInt(_flptr, &(ptr->fileTime)) == -1) { return -1; } @@ -1959,23 +1960,23 @@ static int lsgLoadHeaderInSlot(int slot) return -1; } - ptr->field_68 = v8[0]; - ptr->field_6A = v8[1]; - ptr->field_6C = v8[2]; + ptr->gameMonth = v8[0]; + ptr->gameDay = v8[1]; + ptr->gameYear = v8[2]; - if (_db_freadInt(_flptr, &(ptr->field_70)) == -1) { + if (_db_freadInt(_flptr, &(ptr->gameTime)) == -1) { return -1; } - if (fileReadInt16(_flptr, &(ptr->field_74)) == -1) { + if (fileReadInt16(_flptr, &(ptr->elevation)) == -1) { return -1; } - if (fileReadInt16(_flptr, &(ptr->field_76)) == -1) { + if (fileReadInt16(_flptr, &(ptr->map)) == -1) { return -1; } - if (fileRead(ptr->file_name, 1, 16, _flptr) != 16) { + if (fileRead(ptr->fileName, 1, 16, _flptr) != 16) { return -1; } @@ -2029,7 +2030,7 @@ static int _GetSlotList() } // 0x47E6D8 -static void _ShowSlotList(int a1) +static void _ShowSlotList(int windowType) { bufferFill(gLoadSaveWindowBuffer + LS_WINDOW_WIDTH * 87 + 55, 230, 353, LS_WINDOW_WIDTH, gLoadSaveWindowBuffer[LS_WINDOW_WIDTH * 86 + 55] & 0xFF); @@ -2037,7 +2038,7 @@ static void _ShowSlotList(int a1) for (int index = 0; index < 10; index += 1) { int color = index == _slot_cursor ? _colorTable[32747] : _colorTable[992]; - const char* text = getmsg(&gLoadSaveMessageList, &gLoadSaveMessageListItem, a1 != 0 ? 110 : 109); + const char* text = getmsg(&gLoadSaveMessageList, &gLoadSaveMessageListItem, windowType != 0 ? 110 : 109); snprintf(_str, sizeof(_str), "[ %s %.2d: ]", text, index + 1); fontDrawText(gLoadSaveWindowBuffer + LS_WINDOW_WIDTH * y + 55, _str, LS_WINDOW_WIDTH, LS_WINDOW_WIDTH, color); @@ -2071,7 +2072,7 @@ static void _ShowSlotList(int a1) } // 0x47E8E0 -static void _DrawInfoBox(int a1) +static void _DrawInfoBox(int slot) { blitBufferToBuffer(_loadsaveFrmImages[LOAD_SAVE_FRM_BACKGROUND].getData() + LS_WINDOW_WIDTH * 254 + 396, 164, 60, LS_WINDOW_WIDTH, gLoadSaveWindowBuffer + LS_WINDOW_WIDTH * 254 + 396, 640); @@ -2079,26 +2080,28 @@ static void _DrawInfoBox(int a1) const char* text; int color = _colorTable[992]; - switch (_LSstatus[a1]) { + switch (_LSstatus[slot]) { case SLOT_STATE_OCCUPIED: - do { - STRUCT_613D30* ptr = &(_LSData[a1]); - fontDrawText(gLoadSaveWindowBuffer + LS_WINDOW_WIDTH * 254 + 396, ptr->character_name, LS_WINDOW_WIDTH, LS_WINDOW_WIDTH, color); + if (1) { + LoadSaveSlotData* ptr = &(_LSData[slot]); + fontDrawText(gLoadSaveWindowBuffer + LS_WINDOW_WIDTH * 254 + 396, ptr->characterName, LS_WINDOW_WIDTH, LS_WINDOW_WIDTH, color); - int v4 = ptr->field_70 / 600; - int v5 = v4 % 60; - int v6 = 25 * (v4 / 60 % 24); - int v21 = 4 * v6 + v5; - - text = getmsg(&gLoadSaveMessageList, &gLoadSaveMessageListItem, 116 + ptr->field_68); - snprintf(_str, sizeof(_str), "%.2d %s %.4d %.4d", ptr->field_6A, text, ptr->field_6C, v21); + snprintf(_str, + sizeof(_str), + "%.2d %s %.4d %.4d", + ptr->gameDay, + getmsg(&gLoadSaveMessageList, &gLoadSaveMessageListItem, 116 + ptr->gameMonth), + ptr->gameYear, + 100 * ((ptr->gameTime / 600) / 60 % 24) + (ptr->gameTime / 600) % 60); int v2 = fontGetLineHeight(); fontDrawText(gLoadSaveWindowBuffer + LS_WINDOW_WIDTH * (256 + v2) + 397, _str, LS_WINDOW_WIDTH, LS_WINDOW_WIDTH, color); - const char* v22 = mapGetName(ptr->field_76, ptr->field_74); - const char* v9 = mapGetCityName(ptr->field_76); - snprintf(_str, sizeof(_str), "%s %s", v9, v22); + snprintf(_str, + sizeof(_str), + "%s %s", + mapGetCityName(ptr->map), + mapGetName(ptr->map, ptr->elevation)); int y = v2 + 3 + v2 + 256; short beginnings[WORD_WRAP_MAX_COUNT]; @@ -2113,7 +2116,7 @@ static void _DrawInfoBox(int a1) y += v2 + 2; } } - } while (0); + } return; case SLOT_STATE_EMPTY: // Empty. @@ -2140,30 +2143,28 @@ static void _DrawInfoBox(int a1) } // 0x47EC48 -static int _LoadTumbSlot(int a1) +static int _LoadTumbSlot(int slot) { - File* stream; - int v2; - - v2 = _LSstatus[_slot_cursor]; - if (v2 != 0 && v2 != 2 && v2 != 3) { + if (_LSstatus[_slot_cursor] != SLOT_STATE_EMPTY + && _LSstatus[_slot_cursor] != SLOT_STATE_ERROR + && _LSstatus[_slot_cursor] != SLOT_STATE_UNSUPPORTED_VERSION) { snprintf(_str, sizeof(_str), "%s\\%s%.2d\\%s", "SAVEGAME", "SLOT", _slot_cursor + 1, "SAVE.DAT"); debugPrint(" Filename %s\n", _str); - stream = fileOpen(_str, "rb"); + File* stream = fileOpen(_str, "rb"); if (stream == NULL) { - debugPrint("\nLOADSAVE: ** (A) Error reading thumbnail #%d! **\n", a1); + debugPrint("\nLOADSAVE: ** (A) Error reading thumbnail #%d! **\n", slot); return -1; } if (fileSeek(stream, 131, SEEK_SET) != 0) { - debugPrint("\nLOADSAVE: ** (B) Error reading thumbnail #%d! **\n", a1); + debugPrint("\nLOADSAVE: ** (B) Error reading thumbnail #%d! **\n", slot); fileClose(stream); return -1; } if (fileRead(_thumbnail_image, LS_PREVIEW_SIZE, 1, stream) != 1) { - debugPrint("\nLOADSAVE: ** (C) Error reading thumbnail #%d! **\n", a1); + debugPrint("\nLOADSAVE: ** (C) Error reading thumbnail #%d! **\n", slot); fileClose(stream); return -1; } @@ -2175,7 +2176,7 @@ static int _LoadTumbSlot(int a1) } // 0x47ED5C -static int _GetComment(int a1) +static int _GetComment(int slot) { // Maintain original position in original resolution, otherwise center it. int commentWindowX = screenGetWidth() != 640 @@ -2276,7 +2277,7 @@ static int _GetComment(int a1) char description[LOAD_SAVE_DESCRIPTION_LENGTH]; if (_LSstatus[_slot_cursor] == SLOT_STATE_OCCUPIED) { - strncpy(description, _LSData[a1].description, LOAD_SAVE_DESCRIPTION_LENGTH); + strncpy(description, _LSData[slot].description, LOAD_SAVE_DESCRIPTION_LENGTH); } else { memset(description, '\0', LOAD_SAVE_DESCRIPTION_LENGTH); } @@ -2285,8 +2286,8 @@ static int _GetComment(int a1) int backgroundColor = *(_loadsaveFrmImages[LOAD_SAVE_FRM_BOX].getData() + _loadsaveFrmImages[LOAD_SAVE_FRM_BOX].getWidth() * 35 + 24); if (_get_input_str2(window, 507, 508, description, LOAD_SAVE_DESCRIPTION_LENGTH - 1, 24, 35, _colorTable[992], backgroundColor, 0) == 0) { - strncpy(_LSData[a1].description, description, LOAD_SAVE_DESCRIPTION_LENGTH); - _LSData[a1].description[LOAD_SAVE_DESCRIPTION_LENGTH - 1] = '\0'; + strncpy(_LSData[slot].description, description, LOAD_SAVE_DESCRIPTION_LENGTH); + _LSData[slot].description[LOAD_SAVE_DESCRIPTION_LENGTH - 1] = '\0'; rc = 1; } else { rc = 0; @@ -2416,7 +2417,7 @@ static int _PrepLoad(File* stream) gameReset(); gameMouseSetCursor(MOUSE_CURSOR_WAIT_PLANET); gMapHeader.name[0] = '\0'; - gameTimeSetTime(_LSData[_slot_cursor].field_70); + gameTimeSetTime(_LSData[_slot_cursor].gameTime); return 0; } @@ -2424,7 +2425,7 @@ static int _PrepLoad(File* stream) static int _EndLoad(File* stream) { wmMapMusicStart(); - dudeSetName(_LSData[_slot_cursor].character_name); + dudeSetName(_LSData[_slot_cursor].characterName); interfaceBarRefresh(); indicatorBarRefresh(); tileWindowRefresh(); @@ -2635,7 +2636,7 @@ static int _SlotMap2Game(File* stream) return -1; } - if (mapLoadSaved(_LSData[_slot_cursor].file_name) == -1) { + if (mapLoadSaved(_LSData[_slot_cursor].fileName) == -1) { debugPrint("LOADSAVE: returning 13\n"); return -1; } @@ -2671,7 +2672,7 @@ static int _mygets(char* dest, File* stream) } // 0x47FE58 -static int _copy_file(const char* a1, const char* a2) +static int _copy_file(const char* existingFileName, const char* newFileName) { File* stream1; File* stream2; @@ -2685,7 +2686,7 @@ static int _copy_file(const char* a1, const char* a2) buf = NULL; result = -1; - stream1 = fileOpen(a1, "rb"); + stream1 = fileOpen(existingFileName, "rb"); if (stream1 == NULL) { goto out; } @@ -2695,7 +2696,7 @@ static int _copy_file(const char* a1, const char* a2) goto out; } - stream2 = fileOpen(a2, "wb"); + stream2 = fileOpen(newFileName, "wb"); if (stream2 == NULL) { goto out; } @@ -2836,7 +2837,7 @@ static int _SaveBackup() char* v2 = _strmfe(_str2, "AUTOMAP.DB", "BAK"); snprintf(_str1, sizeof(_str1), "%s\\%s", _gmpath, v2); - _automap_db_flag = 0; + _automap_db_flag = false; File* stream2 = fileOpen(_str0, "rb"); if (stream2 != NULL) { @@ -2846,7 +2847,7 @@ static int _SaveBackup() return -1; } - _automap_db_flag = 1; + _automap_db_flag = true; } return 0; diff --git a/src/loadsave.h b/src/loadsave.h index 77e6757..32e1263 100644 --- a/src/loadsave.h +++ b/src/loadsave.h @@ -18,7 +18,7 @@ void _InitLoadSave(); void _ResetLoadSave(); int lsgSaveGame(int mode); int lsgLoadGame(int mode); -int _isLoadingGame(); +bool _isLoadingGame(); void lsgInit(); int _MapDirEraseFile_(const char* a1, const char* a2); From c4c2806071aebeb6ff257c0ef46bc0edefc02268 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 13 Jul 2023 13:04:31 +0300 Subject: [PATCH 020/136] Fix game time calculations (#307) --- src/loadsave.cc | 6 ++--- src/map.cc | 4 +-- src/map.h | 2 +- src/pipboy.cc | 8 +++--- src/queue.cc | 70 +++++++++++++++++++------------------------------ src/queue.h | 2 +- src/random.cc | 2 +- src/scripts.cc | 12 +++------ src/scripts.h | 4 +-- src/skill.cc | 2 +- 10 files changed, 45 insertions(+), 67 deletions(-) diff --git a/src/loadsave.cc b/src/loadsave.cc index 4886ba8..645dcab 100644 --- a/src/loadsave.cc +++ b/src/loadsave.cc @@ -130,7 +130,7 @@ typedef struct LoadSaveSlotData { short gameMonth; short gameDay; short gameYear; - int gameTime; + unsigned int gameTime; short elevation; short map; char fileName[16]; @@ -1863,7 +1863,7 @@ static int lsgSaveHeaderInSlot(int slot) return -1; } - if (_db_fwriteLong(_flptr, ptr->gameTime) == -1) { + if (fileWriteUInt32(_flptr, ptr->gameTime) == -1) { return -1; } @@ -1964,7 +1964,7 @@ static int lsgLoadHeaderInSlot(int slot) ptr->gameDay = v8[1]; ptr->gameYear = v8[2]; - if (_db_freadInt(_flptr, &(ptr->gameTime)) == -1) { + if (fileReadUInt32(_flptr, &(ptr->gameTime)) == -1) { return -1; } diff --git a/src/map.cc b/src/map.cc index 4166e60..8b07d6f 100644 --- a/src/map.cc +++ b/src/map.cc @@ -1746,7 +1746,7 @@ static int mapHeaderWrite(MapHeader* ptr, File* stream) if (fileWriteInt32(stream, ptr->darkness) == -1) return -1; if (fileWriteInt32(stream, ptr->globalVariablesCount) == -1) return -1; if (fileWriteInt32(stream, ptr->field_34) == -1) return -1; - if (fileWriteInt32(stream, ptr->lastVisitTime) == -1) return -1; + if (fileWriteUInt32(stream, ptr->lastVisitTime) == -1) return -1; if (fileWriteInt32List(stream, ptr->field_3C, 44) == -1) return -1; return 0; @@ -1766,7 +1766,7 @@ static int mapHeaderRead(MapHeader* ptr, File* stream) if (fileReadInt32(stream, &(ptr->darkness)) == -1) return -1; if (fileReadInt32(stream, &(ptr->globalVariablesCount)) == -1) return -1; if (fileReadInt32(stream, &(ptr->field_34)) == -1) return -1; - if (fileReadInt32(stream, &(ptr->lastVisitTime)) == -1) return -1; + if (fileReadUInt32(stream, &(ptr->lastVisitTime)) == -1) return -1; if (fileReadInt32List(stream, ptr->field_3C, 44) == -1) return -1; return 0; diff --git a/src/map.h b/src/map.h index 0cdebb1..09247f8 100644 --- a/src/map.h +++ b/src/map.h @@ -54,7 +54,7 @@ typedef struct MapHeader { int field_34; // Time in game ticks when PC last visited this map. - int lastVisitTime; + unsigned int lastVisitTime; int field_3C[44]; } MapHeader; diff --git a/src/pipboy.cc b/src/pipboy.cc index 5b58020..45f79db 100644 --- a/src/pipboy.cc +++ b/src/pipboy.cc @@ -1962,7 +1962,7 @@ static bool pipboyRest(int hours, int minutes, int duration) double v2 = v1 * (1.0 / 1440.0) * 3.5 + 0.25; double v3 = (double)minutes / v1 * v2; if (minutes != 0) { - int gameTime = gameTimeGetTime(); + unsigned int gameTime = gameTimeGetTime(); double v4 = v3 * 20.0; int v5 = 0; @@ -2025,7 +2025,7 @@ static bool pipboyRest(int hours, int minutes, int duration) } if (hours != 0 && !rc) { - int gameTime = gameTimeGetTime(); + unsigned int gameTime = gameTimeGetTime(); double v7 = (v2 - v3) * 20.0; for (int hour = 0; hour < (int)v7; hour++) { @@ -2143,9 +2143,7 @@ static bool pipboyRest(int hours, int minutes, int duration) } } - int gameTime = gameTimeGetTime(); - int nextEventGameTime = queueGetNextEventTime(); - if (gameTime > nextEventGameTime) { + if (gameTimeGetTime() > queueGetNextEventTime()) { if (queueProcessEvents()) { debugPrint("PIPBOY: Returning from Queue trigger...\n"); _proc_bail_flag = 1; diff --git a/src/queue.cc b/src/queue.cc index 96d1b62..ab0b54c 100644 --- a/src/queue.cc +++ b/src/queue.cc @@ -18,8 +18,7 @@ namespace fallout { typedef struct QueueListNode { - // TODO: Make unsigned. - int time; + unsigned int time; int type; Object* owner; void* data; @@ -102,7 +101,7 @@ int queueLoad(File* stream) break; } - if (fileReadInt32(stream, &(queueListNode->time)) == -1) { + if (fileReadUInt32(stream, &(queueListNode->time)) == -1) { internal_free(queueListNode); rc = -1; break; @@ -169,27 +168,20 @@ int queueLoad(File* stream) } } - if (oldListHead != NULL) { - QueueListNode** v13 = &gQueueListHead; - QueueListNode* v15; - do { - while (true) { - QueueListNode* v14 = *v13; - if (v14 == NULL) { - break; - } - - if (v14->time > oldListHead->time) { - break; - } - - v13 = &(v14->next); + while (oldListHead != NULL) { + QueueListNode** queueListNodePtr = &gQueueListHead; + while (*queueListNodePtr != NULL) { + if ((*queueListNodePtr)->time > oldListHead->time) { + break; } - v15 = oldListHead->next; - oldListHead->next = *v13; - *v13 = oldListHead; - oldListHead = v15; - } while (v15 != NULL); + + queueListNodePtr = &((*queueListNodePtr)->next); + } + + QueueListNode* next = oldListHead->next; + oldListHead->next = *queueListNodePtr; + *queueListNodePtr = oldListHead; + oldListHead = next; } return rc; @@ -217,7 +209,7 @@ int queueSave(File* stream) Object* object = queueListNode->owner; int objectId = object != NULL ? object->id : -2; - if (fileWriteInt32(stream, queueListNode->time) == -1) { + if (fileWriteUInt32(stream, queueListNode->time) == -1) { return -1; } @@ -250,9 +242,7 @@ int queueAddEvent(int delay, Object* obj, void* data, int eventType) return -1; } - int v1 = gameTimeGetTime(); - int v2 = v1 + delay; - newQueueListNode->time = v2; + newQueueListNode->time = gameTimeGetTime() + delay; newQueueListNode->type = eventType; newQueueListNode->owner = obj; newQueueListNode->data = data; @@ -261,22 +251,18 @@ int queueAddEvent(int delay, Object* obj, void* data, int eventType) obj->flags |= OBJECT_QUEUED; } - QueueListNode** v3 = &gQueueListHead; + QueueListNode** queueListNodePtr = &gQueueListHead; - if (gQueueListHead != NULL) { - QueueListNode* v4; + while (*queueListNodePtr != NULL) { + if (newQueueListNode->time < (*queueListNodePtr)->time) { + break; + } - do { - v4 = *v3; - if (v2 < v4->time) { - break; - } - v3 = &(v4->next); - } while (v4->next != NULL); + queueListNodePtr = &((*queueListNodePtr)->next); } - newQueueListNode->next = *v3; - *v3 = newQueueListNode; + newQueueListNode->next = *queueListNodePtr; + *queueListNodePtr = newQueueListNode; return 0; } @@ -357,7 +343,7 @@ bool queueHasEvent(Object* owner, int eventType) // 0x4A26D0 int queueProcessEvents() { - int time = gameTimeGetTime(); + unsigned int time = gameTimeGetTime(); int v1 = 0; while (gQueueListHead != NULL) { @@ -437,10 +423,8 @@ void _queue_clear_type(int eventType, QueueEventHandler* fn) } } -// TODO: Make unsigned. -// // 0x4A2808 -int queueGetNextEventTime() +unsigned int queueGetNextEventTime() { if (gQueueListHead == NULL) { return 0; diff --git a/src/queue.h b/src/queue.h index d77179e..78b7197 100644 --- a/src/queue.h +++ b/src/queue.h @@ -66,7 +66,7 @@ bool queueHasEvent(Object* owner, int eventType); int queueProcessEvents(); void queueClear(); void _queue_clear_type(int eventType, QueueEventHandler* fn); -int queueGetNextEventTime(); +unsigned int queueGetNextEventTime(); void _queue_leaving_map(); bool queueIsEmpty(); void* queueFindFirstEvent(Object* owner, int eventType); diff --git a/src/random.cc b/src/random.cc index 82f47b3..321e389 100644 --- a/src/random.cc +++ b/src/random.cc @@ -100,7 +100,7 @@ int randomRoll(int difficulty, int criticalSuccessModifier, int* howMuchPtr) // 0x4A3030 static int randomTranslateRoll(int delta, int criticalSuccessModifier) { - int gameTime = gameTimeGetTime(); + unsigned int gameTime = gameTimeGetTime(); // SFALL: Remove criticals time limits. bool criticalsTimeLimitsRemoved = false; diff --git a/src/scripts.cc b/src/scripts.cc index f0d2c60..e517466 100644 --- a/src/scripts.cc +++ b/src/scripts.cc @@ -127,7 +127,7 @@ static int _script_engine_game_mode = 0; // Game time in ticks (1/10 second). // // 0x51C720 -static int gGameTime = 302400; +static unsigned int gGameTime = 302400; // 0x51C724 static const int gGameTimeDaysPerMonth[12] = { @@ -277,12 +277,10 @@ static int gMovieTimerArtimer2; static int gMovieTimerArtimer3; static int gMovieTimerArtimer4; -// TODO: Make unsigned. -// // Returns game time in ticks (1/10 second). // // 0x4A3330 -int gameTimeGetTime() +unsigned int gameTimeGetTime() { return gGameTime; } @@ -351,7 +349,7 @@ char* gameTimeGetTimeString() // TODO: Make unsigned. // // 0x4A347C -void gameTimeSetTime(int time) +void gameTimeSetTime(unsigned int time) { if (time == 0) { time = 1; @@ -775,9 +773,7 @@ static void _script_chk_timed_events() if (v1) { while (!queueIsEmpty()) { - int time = gameTimeGetTime(); - int v2 = queueGetNextEventTime(); - if (time < v2) { + if (gameTimeGetTime() < queueGetNextEventTime()) { break; } diff --git a/src/scripts.h b/src/scripts.h index 0c4b508..e061fd0 100644 --- a/src/scripts.h +++ b/src/scripts.h @@ -147,13 +147,13 @@ typedef struct Script { extern const char* gScriptProcNames[SCRIPT_PROC_COUNT]; -int gameTimeGetTime(); +unsigned int gameTimeGetTime(); void gameTimeGetDate(int* monthPtr, int* dayPtr, int* yearPtr); int gameTimeGetHour(); char* gameTimeGetTimeString(); void gameTimeAddTicks(int a1); void gameTimeAddSeconds(int a1); -void gameTimeSetTime(int time); +void gameTimeSetTime(unsigned int time); int gameTimeScheduleUpdateEvent(); int gameTimeEventProcess(Object* obj, void* data); int _scriptsCheckGameEvents(int* moviePtr, int window); diff --git a/src/skill.cc b/src/skill.cc index 9093068..2ea2a2f 100644 --- a/src/skill.cc +++ b/src/skill.cc @@ -1151,7 +1151,7 @@ static int skillGetFreeUsageSlot(int skill) } } - int time = gameTimeGetTime(); + unsigned int time = gameTimeGetTime(); int hoursSinceLastUsage = (time - _timesSkillUsed[skill][0]) / GAME_TIME_TICKS_PER_HOUR; if (hoursSinceLastUsage <= 24) { return -1; From ffe90eef49f3e04c7fc0f3ebc5a6280af1971826 Mon Sep 17 00:00:00 2001 From: Vlad Date: Thu, 13 Jul 2023 12:35:26 +0200 Subject: [PATCH 021/136] Improve code readability (#315) --- src/animation.cc | 47 ++++---- src/animation.h | 4 +- src/combat.cc | 273 ++++++++++++++++++++++----------------------- src/combat.h | 14 +-- src/combat_ai.cc | 183 +++++++++++++++--------------- src/game_sound.cc | 10 +- src/interpreter.cc | 98 ++++++++-------- src/interpreter.h | 12 +- src/object.cc | 42 +++---- src/object.h | 8 +- src/queue.cc | 9 +- src/scripts.cc | 8 +- 12 files changed, 353 insertions(+), 355 deletions(-) diff --git a/src/animation.cc b/src/animation.cc index 5663fe6..0b496c2 100644 --- a/src/animation.cc +++ b/src/animation.cc @@ -1711,6 +1711,7 @@ int _make_path(Object* object, int from, int to, unsigned char* rotations, int a return pathfinderFindPath(object, from, to, rotations, a5, _obj_blocking_at); } +// TODO: move pathfinding into another unit // 0x415EFC int pathfinderFindPath(Object* object, int from, int to, unsigned char* rotations, int a5, PathBuilderCallback* callback) { @@ -1939,18 +1940,18 @@ static int _tile_idistance(int tile1, int tile2) } // 0x4163AC -int _make_straight_path(Object* a1, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6) +int _make_straight_path(Object* obj, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6) { - return _make_straight_path_func(a1, from, to, straightPathNodeList, obstaclePtr, a6, _obj_blocking_at); + return _make_straight_path_func(obj, from, to, straightPathNodeList, obstaclePtr, a6, _obj_blocking_at); } // TODO: Rather complex, but understandable, needs testing. // // 0x4163C8 -int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6, PathBuilderCallback* callback) +int _make_straight_path_func(Object* obj, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6, PathBuilderCallback* callback) { if (obstaclePtr != NULL) { - Object* obstacle = callback(a1, from, a1->elevation); + Object* obstacle = callback(obj, from, obj->elevation); if (obstacle != NULL) { if (obstacle != *obstaclePtr && (a6 != 32 || (obstacle->flags & OBJECT_SHOOT_THRU) == 0)) { *obstaclePtr = obstacle; @@ -1961,13 +1962,13 @@ int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* str int fromX; int fromY; - tileToScreenXY(from, &fromX, &fromY, a1->elevation); + tileToScreenXY(from, &fromX, &fromY, obj->elevation); fromX += 16; fromY += 8; int toX; int toY; - tileToScreenXY(to, &toX, &toY, a1->elevation); + tileToScreenXY(to, &toX, &toY, obj->elevation); toX += 16; toY += 8; @@ -2005,7 +2006,7 @@ int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* str if (ddx <= ddy) { int middle = ddx - ddy / 2; while (true) { - tile = tileFromScreenXY(tileX, tileY, a1->elevation); + tile = tileFromScreenXY(tileX, tileY, obj->elevation); v22 += 1; if (v22 == a6) { @@ -2016,9 +2017,9 @@ int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* str if (straightPathNodeList != NULL) { StraightPathNode* pathNode = &(straightPathNodeList[pathNodeIndex]); pathNode->tile = tile; - pathNode->elevation = a1->elevation; + pathNode->elevation = obj->elevation; - tileToScreenXY(tile, &fromX, &fromY, a1->elevation); + tileToScreenXY(tile, &fromX, &fromY, obj->elevation); pathNode->x = tileX - fromX - 16; pathNode->y = tileY - fromY - 8; } @@ -2044,10 +2045,10 @@ int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* str if (tile != prevTile) { if (obstaclePtr != NULL) { - Object* obj = callback(a1, tile, a1->elevation); - if (obj != NULL) { - if (obj != *obstaclePtr && (a6 != 32 || (obj->flags & OBJECT_SHOOT_THRU) == 0)) { - *obstaclePtr = obj; + Object* obstacle = callback(obj, tile, obj->elevation); + if (obstacle != NULL) { + if (obstacle != *obstaclePtr && (a6 != 32 || (obstacle->flags & OBJECT_SHOOT_THRU) == 0)) { + *obstaclePtr = obstacle; break; } } @@ -2058,7 +2059,7 @@ int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* str } else { int middle = ddy - ddx / 2; while (true) { - tile = tileFromScreenXY(tileX, tileY, a1->elevation); + tile = tileFromScreenXY(tileX, tileY, obj->elevation); v22 += 1; if (v22 == a6) { @@ -2069,9 +2070,9 @@ int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* str if (straightPathNodeList != NULL) { StraightPathNode* pathNode = &(straightPathNodeList[pathNodeIndex]); pathNode->tile = tile; - pathNode->elevation = a1->elevation; + pathNode->elevation = obj->elevation; - tileToScreenXY(tile, &fromX, &fromY, a1->elevation); + tileToScreenXY(tile, &fromX, &fromY, obj->elevation); pathNode->x = tileX - fromX - 16; pathNode->y = tileY - fromY - 8; } @@ -2097,10 +2098,10 @@ int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* str if (tile != prevTile) { if (obstaclePtr != NULL) { - Object* obj = callback(a1, tile, a1->elevation); - if (obj != NULL) { - if (obj != *obstaclePtr && (a6 != 32 || (obj->flags & OBJECT_SHOOT_THRU) == 0)) { - *obstaclePtr = obj; + Object* obstacle = callback(obj, tile, obj->elevation); + if (obstacle != NULL) { + if (obstacle != *obstaclePtr && (a6 != 32 || (obstacle->flags & OBJECT_SHOOT_THRU) == 0)) { + *obstaclePtr = obstacle; break; } } @@ -2118,9 +2119,9 @@ int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* str if (straightPathNodeList != NULL) { StraightPathNode* pathNode = &(straightPathNodeList[pathNodeIndex]); pathNode->tile = tile; - pathNode->elevation = a1->elevation; + pathNode->elevation = obj->elevation; - tileToScreenXY(tile, &fromX, &fromY, a1->elevation); + tileToScreenXY(tile, &fromX, &fromY, obj->elevation); pathNode->x = tileX - fromX - 16; pathNode->y = tileY - fromY - 8; } @@ -2128,7 +2129,7 @@ int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* str pathNodeIndex += 1; } else { if (pathNodeIndex > 0 && straightPathNodeList != NULL) { - straightPathNodeList[pathNodeIndex - 1].elevation = a1->elevation; + straightPathNodeList[pathNodeIndex - 1].elevation = obj->elevation; } } diff --git a/src/animation.h b/src/animation.h index 4e146a4..9f0e297 100644 --- a/src/animation.h +++ b/src/animation.h @@ -145,8 +145,8 @@ int animationRegisterAnimateForever(Object* owner, int anim, int delay); int animationRegisterPing(int flags, int delay); int _make_path(Object* object, int from, int to, unsigned char* a4, int a5); int pathfinderFindPath(Object* object, int from, int to, unsigned char* rotations, int a5, PathBuilderCallback* callback); -int _make_straight_path(Object* a1, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6); -int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6, PathBuilderCallback* callback); +int _make_straight_path(Object* object, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6); +int _make_straight_path_func(Object* object, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6, PathBuilderCallback* callback); void _object_animate(); int _check_move(int* actionPointsPtr); int _dude_move(int actionPoints); diff --git a/src/combat.cc b/src/combat.cc index 8548137..70098c0 100644 --- a/src/combat.cc +++ b/src/combat.cc @@ -116,7 +116,7 @@ static int attackComputeCriticalHit(Attack* a1); static int _attackFindInvalidFlags(Object* a1, Object* a2); static int attackComputeCriticalFailure(Attack* attack); static void _do_random_cripple(int* flagsPtr); -static int attackDetermineToHit(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode, bool a6); +static int attackDetermineToHit(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode, bool useDistance); static void attackComputeDamage(Attack* attack, int ammoQuantity, int a3); static void _check_for_death(Object* a1, int a2, int* a3); static void _set_new_results(Object* a1, int a2); @@ -3476,16 +3476,16 @@ void attackInit(Attack* attack, Object* attacker, Object* defender, int hitMode, } // 0x422F3C -int _combat_attack(Object* a1, Object* a2, int hitMode, int hitLocation) +int _combat_attack(Object* attacker, Object* defender, int hitMode, int hitLocation) { - if (a1 != gDude && hitMode == HIT_MODE_PUNCH && randomBetween(1, 4) == 1) { - int fid = buildFid(OBJ_TYPE_CRITTER, a1->fid & 0xFFF, ANIM_KICK_LEG, (a1->fid & 0xF000) >> 12, (a1->fid & 0x70000000) >> 28); + if (attacker != gDude && hitMode == HIT_MODE_PUNCH && randomBetween(1, 4) == 1) { + int fid = buildFid(OBJ_TYPE_CRITTER, attacker->fid & 0xFFF, ANIM_KICK_LEG, (attacker->fid & 0xF000) >> 12, (attacker->fid & 0x70000000) >> 28); if (artExists(fid)) { hitMode = HIT_MODE_KICK; } } - attackInit(&_main_ctd, a1, a2, hitMode, hitLocation); + attackInit(&_main_ctd, attacker, defender, hitMode, hitLocation); debugPrint("computing attack...\n"); if (attackCompute(&_main_ctd) == -1) { @@ -3513,7 +3513,7 @@ int _combat_attack(Object* a1, Object* a2, int hitMode, int hitLocation) bool aiming; if (_main_ctd.defenderHitLocation == HIT_LOCATION_TORSO || _main_ctd.defenderHitLocation == HIT_LOCATION_UNCALLED) { - if (a1 == gDude) { + if (attacker == gDude) { interfaceGetCurrentHitMode(&hitMode, &aiming); } else { aiming = false; @@ -3522,22 +3522,22 @@ int _combat_attack(Object* a1, Object* a2, int hitMode, int hitLocation) aiming = true; } - int actionPoints = weaponGetActionPointCost(a1, _main_ctd.hitMode, aiming); + int actionPoints = weaponGetActionPointCost(attacker, _main_ctd.hitMode, aiming); debugPrint("sequencing attack...\n"); if (_action_attack(&_main_ctd) == -1) { return -1; } - if (actionPoints > a1->data.critter.combat.ap) { - a1->data.critter.combat.ap = 0; + if (actionPoints > attacker->data.critter.combat.ap) { + attacker->data.critter.combat.ap = 0; } else { - a1->data.critter.combat.ap -= actionPoints; + attacker->data.critter.combat.ap -= actionPoints; } - if (a1 == gDude) { - interfaceRenderActionPoints(a1->data.critter.combat.ap, _combat_free_move); - _critter_set_who_hit_me(a1, a2); + if (attacker == gDude) { + interfaceRenderActionPoints(attacker->data.critter.combat.ap, _combat_free_move); + _critter_set_who_hit_me(attacker, defender); } // SFALL @@ -3545,19 +3545,19 @@ int _combat_attack(Object* a1, Object* a2, int hitMode, int hitLocation) _combat_call_display = 1; _combat_cleanup_enabled = 1; - aiInfoSetLastTarget(a1, a2); + aiInfoSetLastTarget(attacker, defender); debugPrint("running attack...\n"); return 0; } -// Returns tile one step closer from [a1] to [a2] +// Returns tile one step closer from [attacker] to [target] // // 0x423104 -int _combat_bullet_start(const Object* a1, const Object* a2) +int _combat_bullet_start(const Object* attacker, const Object* target) { - int rotation = tileGetRotationTo(a1->tile, a2->tile); - return tileGetTileInDirection(a1->tile, rotation, 1); + int rotation = tileGetRotationTo(attacker->tile, target->tile); + return tileGetTileInDirection(attacker->tile, rotation, 1); } // 0x423128 @@ -3942,17 +3942,17 @@ static int attackCompute(Attack* attack) attack->tile = tile; - Object* v25 = attack->defender; - _make_straight_path_func(v25, attack->defender->tile, attack->tile, NULL, &v25, 32, _obj_shoot_blocking_at); - if (v25 != NULL && v25 != attack->defender) { - attack->tile = v25->tile; + Object* accidentalTarget = attack->defender; + _make_straight_path_func(accidentalTarget, attack->defender->tile, attack->tile, NULL, &accidentalTarget, 32, _obj_shoot_blocking_at); + if (accidentalTarget != NULL && accidentalTarget != attack->defender) { + attack->tile = accidentalTarget->tile; } else { - v25 = _obj_blocking_at(NULL, attack->tile, attack->defender->elevation); + accidentalTarget = _obj_blocking_at(NULL, attack->tile, attack->defender->elevation); } - if (v25 != NULL && (v25->flags & OBJECT_SHOOT_THRU) == 0) { + if (accidentalTarget != NULL && (accidentalTarget->flags & OBJECT_SHOOT_THRU) == 0) { attack->attackerFlags |= DAM_HIT; - attack->defender = v25; + attack->defender = accidentalTarget; attackComputeDamage(attack, 1, 2); } } @@ -3974,75 +3974,75 @@ static int attackCompute(Attack* attack) // compute_explosion_on_extras // 0x423C10 -void _compute_explosion_on_extras(Attack* attack, int a2, bool isGrenade, int a4) +void _compute_explosion_on_extras(Attack* attack, bool isFromAttacker, bool isGrenade, bool noDamage) { - Object* attacker; + Object* targetObj; - if (a2) { - attacker = attack->attacker; + if (isFromAttacker) { + targetObj = attack->attacker; } else { if ((attack->attackerFlags & DAM_HIT) != 0) { - attacker = attack->defender; + targetObj = attack->defender; } else { - attacker = NULL; + targetObj = NULL; } } - int tile; - if (attacker != NULL) { - tile = attacker->tile; + int explosionTile; + if (targetObj != NULL) { + explosionTile = targetObj->tile; } else { - tile = attack->tile; + explosionTile = attack->tile; } - if (tile == -1) { + if (explosionTile == -1) { debugPrint("\nError: compute_explosion_on_extras: Called with bad target/tileNum"); return; } - // TODO: The math in this loop is rather complex and hard to understand. - int v20; - int v22 = 0; + int ringTileIdx; + int radius = 0; int rotation = 0; - int v5 = -1; - int v19 = tile; + int tile = -1; + int ringFirstTile = explosionTile; // SFALL int maxTargets = explosionGetMaxTargets(); + // Check adjacent tiles for possible targets, going ring-by-ring while (attack->extrasLength < maxTargets) { - if (v22 != 0 && (v5 == -1 || (v5 = tileGetTileInDirection(v5, rotation, 1)) != v19)) { - v20++; - if (v20 % v22 == 0) { + if (radius != 0 && (tile == -1 || (tile = tileGetTileInDirection(tile, rotation, 1)) != ringFirstTile)) { + ringTileIdx++; + if (ringTileIdx % radius == 0) { // the larger the radius, the slower we rotate rotation += 1; if (rotation == ROTATION_COUNT) { rotation = ROTATION_NE; } } } else { - v22++; - if (isGrenade && weaponGetGrenadeExplosionRadius(attack->weapon) < v22) { - v5 = -1; - } else if (isGrenade || weaponGetRocketExplosionRadius(attack->weapon) >= v22) { - v5 = tileGetTileInDirection(v19, ROTATION_NE, 1); + radius++; // go to the next ring + if (isGrenade && weaponGetGrenadeExplosionRadius(attack->weapon) < radius) { + tile = -1; + } else if (isGrenade || weaponGetRocketExplosionRadius(attack->weapon) >= radius) { + tile = tileGetTileInDirection(ringFirstTile, ROTATION_NE, 1); } else { - v5 = -1; + tile = -1; } - v19 = v5; + ringFirstTile = tile; rotation = ROTATION_SE; - v20 = 0; + ringTileIdx = 0; } - if (v5 == -1) { + if (tile == -1) { break; } - Object* obstacle = _obj_blocking_at(attacker, v5, attack->attacker->elevation); + Object* obstacle = _obj_blocking_at(targetObj, tile, attack->attacker->elevation); if (obstacle != NULL && FID_TYPE(obstacle->fid) == OBJ_TYPE_CRITTER && (obstacle->data.critter.combat.results & DAM_DEAD) == 0 && (obstacle->flags & OBJECT_SHOOT_THRU) == 0 - && !_combat_is_shot_blocked(obstacle, obstacle->tile, tile, NULL, NULL)) { + && !_combat_is_shot_blocked(obstacle, obstacle->tile, explosionTile, NULL, NULL)) { if (obstacle == attack->attacker) { attack->attackerFlags &= ~DAM_HIT; attackComputeDamage(attack, 1, 2); @@ -4060,7 +4060,7 @@ void _compute_explosion_on_extras(Attack* attack, int a2, bool isGrenade, int a4 attack->extrasHitLocation[index] = HIT_LOCATION_TORSO; attack->extras[index] = obstacle; attackInit(&_explosion_ctd, attack->attacker, obstacle, attack->hitMode, HIT_LOCATION_TORSO); - if (!a4) { + if (!noDamage) { _explosion_ctd.attackerFlags |= DAM_HIT; attackComputeDamage(&_explosion_ctd, 1, 2); } @@ -4282,26 +4282,26 @@ static void _do_random_cripple(int* flagsPtr) } // 0x42436C -int _determine_to_hit(Object* a1, Object* a2, int hitLocation, int hitMode) +int _determine_to_hit(Object* attacker, Object* defender, int hitLocation, int hitMode) { - return attackDetermineToHit(a1, a1->tile, a2, hitLocation, hitMode, true); + return attackDetermineToHit(attacker, attacker->tile, defender, hitLocation, hitMode, true); } // 0x424380 -int _determine_to_hit_no_range(Object* a1, Object* a2, int hitLocation, int hitMode, unsigned char* a5) +int _determine_to_hit_no_range(Object* attacker, Object* defender, int hitLocation, int hitMode, unsigned char* a5) { - return attackDetermineToHit(a1, a1->tile, a2, hitLocation, hitMode, false); + return attackDetermineToHit(attacker, attacker->tile, defender, hitLocation, hitMode, false); } // 0x424394 -int _determine_to_hit_from_tile(Object* a1, int tile, Object* a3, int hitLocation, int hitMode) +int _determine_to_hit_from_tile(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode) { - return attackDetermineToHit(a1, tile, a3, hitLocation, hitMode, true); + return attackDetermineToHit(attacker, tile, defender, hitLocation, hitMode, true); } // determine_to_hit // 0x4243A8 -static int attackDetermineToHit(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode, bool a6) +static int attackDetermineToHit(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode, bool useDistance) { Object* weapon = critterGetWeaponForHitMode(attacker, hitMode); @@ -4311,32 +4311,30 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in bool isRangedWeapon = false; - int accuracy; + int toHit; if (weapon == NULL || isUnarmedHitMode(hitMode)) { - accuracy = skillGetValue(attacker, SKILL_UNARMED); + toHit = skillGetValue(attacker, SKILL_UNARMED); } else { - accuracy = weaponGetSkillValue(attacker, hitMode); - - int modifier = 0; + toHit = weaponGetSkillValue(attacker, hitMode); int attackType = weaponGetAttackTypeForHitMode(weapon, hitMode); if (attackType == ATTACK_TYPE_RANGED || attackType == ATTACK_TYPE_THROW) { isRangedWeapon = true; - int v29 = 0; - int v25 = 0; + int perceptionBonusMult = 0; + int minEffectiveDist = 0; int weaponPerk = weaponGetPerk(weapon); switch (weaponPerk) { case PERK_WEAPON_LONG_RANGE: - v29 = 4; + perceptionBonusMult = 4; break; case PERK_WEAPON_SCOPE_RANGE: - v29 = 5; - v25 = 8; + perceptionBonusMult = 5; + minEffectiveDist = 8; break; default: - v29 = 2; + perceptionBonusMult = 2; break; } @@ -4347,71 +4345,72 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in perception += 2 * perkGetRank(gDude, PERK_SHARPSHOOTER); } + int distanceMod = 0; // SFALL: Fix for `determine_to_hit_func` function taking distance // into account when called from `determine_to_hit_no_range`. - if (defender != NULL && a6) { - modifier = objectGetDistanceBetweenTiles(attacker, tile, defender, defender->tile); + if (defender != NULL && useDistance) { + distanceMod = objectGetDistanceBetweenTiles(attacker, tile, defender, defender->tile); } else { - modifier = 0; + distanceMod = 0; } - if (modifier >= v25) { - int penalty = attacker == gDude - ? v29 * (perception - 2) - : v29 * perception; + if (distanceMod >= minEffectiveDist) { + int perceptionBonus = attacker == gDude + ? perceptionBonusMult * (perception - 2) + : perceptionBonusMult * perception; - modifier -= penalty; + distanceMod -= perceptionBonus; } else { - modifier += v25; + distanceMod += minEffectiveDist; } - if (-2 * perception > modifier) { - modifier = -2 * perception; + if (distanceMod < -2 * perception) { + distanceMod = -2 * perception; } - if (modifier >= 0) { + if (distanceMod >= 0) { if ((attacker->data.critter.combat.results & DAM_BLIND) != 0) { - modifier *= -12; + distanceMod *= -12; } else { - modifier *= -4; + distanceMod *= -4; } } else { - modifier *= -4; + distanceMod *= -4; } - if (a6 || modifier > 0) { - accuracy += modifier; + if (useDistance || distanceMod > 0) { + toHit += distanceMod; } - modifier = 0; + int numCrittersInLof = 0; - if (defender != NULL && a6) { - _combat_is_shot_blocked(attacker, tile, defender->tile, defender, &modifier); + if (defender != NULL && useDistance) { + _combat_is_shot_blocked(attacker, tile, defender->tile, defender, &numCrittersInLof); } - accuracy -= 10 * modifier; + toHit -= 10 * numCrittersInLof; } if (attacker == gDude && traitIsSelected(TRAIT_ONE_HANDER)) { if (weaponIsTwoHanded(weapon)) { - accuracy -= 40; + toHit -= 40; } else { - accuracy += 20; + toHit += 20; } } int minStrength = weaponGetMinStrengthRequired(weapon); - modifier = minStrength - critterGetStat(attacker, STAT_STRENGTH); + int minStrengthMod = minStrength - critterGetStat(attacker, STAT_STRENGTH); if (attacker == gDude && perkGetRank(gDude, PERK_WEAPON_HANDLING) != 0) { - modifier -= 3; + minStrengthMod -= 3; } - if (modifier > 0) { - accuracy -= 20 * modifier; + if (minStrengthMod > 0) { + toHit -= 20 * minStrengthMod; } if (weaponGetPerk(weapon) == PERK_WEAPON_ACCURATE) { - accuracy += 20; + toHit += 20; } } @@ -4422,17 +4421,17 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in armorClass = 0; } - accuracy -= armorClass; + toHit -= armorClass; } if (isRangedWeapon) { - accuracy += hit_location_penalty[hitLocation]; + toHit += hit_location_penalty[hitLocation]; } else { - accuracy += hit_location_penalty[hitLocation] / 2; + toHit += hit_location_penalty[hitLocation] / 2; } if (defender != NULL && (defender->flags & OBJECT_MULTIHEX) != 0) { - accuracy += 15; + toHit += 15; } if (attacker == gDude) { @@ -4447,45 +4446,45 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in } if (lightIntensity <= 26214) - accuracy -= 40; + toHit -= 40; else if (lightIntensity <= 39321) - accuracy -= 25; + toHit -= 25; else if (lightIntensity <= 52428) - accuracy -= 10; + toHit -= 10; } if (_gcsd != NULL) { - accuracy += _gcsd->accuracyBonus; + toHit += _gcsd->accuracyBonus; } if ((attacker->data.critter.combat.results & DAM_BLIND) != 0) { - accuracy -= 25; + toHit -= 25; } if (targetIsCritter && defender != NULL && (defender->data.critter.combat.results & (DAM_KNOCKED_OUT | DAM_KNOCKED_DOWN)) != 0) { - accuracy += 40; + toHit += 40; } if (attacker->data.critter.combat.team != gDude->data.critter.combat.team) { switch (settings.preferences.combat_difficulty) { case 0: - accuracy -= 20; + toHit -= 20; break; case 2: - accuracy += 20; + toHit += 20; break; } } - if (accuracy > 95) { - accuracy = 95; + if (toHit > 95) { + toHit = 95; } - if (accuracy < -100) { + if (toHit < -100) { debugPrint("Whoa! Bad skill value in determine_to_hit!\n"); } - return accuracy; + return toHit; } // 0x4247B8 @@ -5880,39 +5879,35 @@ void _combat_highlight_change() _combat_highlight = targetHighlight; } -// Probably calculates line of sight or determines if object can see other object. +// Checks if line of fire to the target object is blocked or not. Optionally calculate number of critters on the line of fire. // // 0x426CC4 -bool _combat_is_shot_blocked(Object* a1, int from, int to, Object* a4, int* a5) +bool _combat_is_shot_blocked(Object* sourceObj, int from, int to, Object* targetObj, int* numCrittersOnLof) { - if (a5 != NULL) { - *a5 = 0; + if (numCrittersOnLof != NULL) { + *numCrittersOnLof = 0; } - Object* obstacle = a1; + Object* obstacle = sourceObj; int current = from; while (obstacle != NULL && current != to) { - _make_straight_path_func(a1, current, to, 0, &obstacle, 32, _obj_shoot_blocking_at); + _make_straight_path_func(sourceObj, current, to, 0, &obstacle, 32, _obj_shoot_blocking_at); if (obstacle != NULL) { - if (FID_TYPE(obstacle->fid) != OBJ_TYPE_CRITTER && obstacle != a4) { + if (FID_TYPE(obstacle->fid) != OBJ_TYPE_CRITTER && obstacle != targetObj) { return true; } - if (a5 != NULL) { - if (obstacle != a4) { - if (a4 != NULL) { - // SFALL: Fix for combat_is_shot_blocked_ engine - // function not taking the flags of critters in the - // line of fire into account when calculating the hit - // chance penalty of ranged attacks in - // determine_to_hit_func_ engine function. - if ((obstacle->data.critter.combat.results & (DAM_DEAD | DAM_KNOCKED_DOWN | DAM_KNOCKED_OUT)) == 0) { - *a5 += 1; + if (numCrittersOnLof != NULL && obstacle != targetObj && targetObj != NULL) { + // SFALL: Fix for combat_is_shot_blocked_ engine + // function not taking the flags of critters in the + // line of fire into account when calculating the hit + // chance penalty of ranged attacks in + // determine_to_hit_func_ engine function. + if ((obstacle->data.critter.combat.results & (DAM_DEAD | DAM_KNOCKED_DOWN | DAM_KNOCKED_OUT)) == 0) { + *numCrittersOnLof += 1; - if ((obstacle->flags & OBJECT_MULTIHEX) != 0) { - *a5 += 1; - } - } + if ((obstacle->flags & OBJECT_MULTIHEX) != 0) { + *numCrittersOnLof += 1; } } } diff --git a/src/combat.h b/src/combat.h index 4dc0430..2438b37 100644 --- a/src/combat.h +++ b/src/combat.h @@ -34,13 +34,13 @@ void _combat_over_from_load(); void _combat_give_exps(int exp_points); void _combat_turn_run(); void _combat(STRUCT_664980* attack); -void attackInit(Attack* attack, Object* a2, Object* a3, int a4, int a5); -int _combat_attack(Object* a1, Object* a2, int a3, int a4); -int _combat_bullet_start(const Object* a1, const Object* a2); -void _compute_explosion_on_extras(Attack* attack, int a2, bool isGrenade, int a4); +void attackInit(Attack* attack, Object* attacker, Object* defender, int hitMode, int hitLocation); +int _combat_attack(Object* attacker, Object* defender, int hitMode, int hitLocation); +int _combat_bullet_start(const Object* attacker, const Object* target); +void _compute_explosion_on_extras(Attack* attack, bool isFromAttacker, bool isGrenade, bool noDamage); int _determine_to_hit(Object* a1, Object* a2, int hitLocation, int hitMode); -int _determine_to_hit_no_range(Object* a1, Object* a2, int a3, int a4, unsigned char* a5); -int _determine_to_hit_from_tile(Object* a1, int a2, Object* a3, int a4, int a5); +int _determine_to_hit_no_range(Object* attacker, Object* defender, int hitLocation, int hitMode, unsigned char* a5); +int _determine_to_hit_from_tile(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode); void attackComputeDeathFlags(Attack* attack); void _apply_damage(Attack* attack, bool animated); void _combat_display(Attack* attack); @@ -52,7 +52,7 @@ void _combat_attack_this(Object* a1); void _combat_outline_on(); void _combat_outline_off(); void _combat_highlight_change(); -bool _combat_is_shot_blocked(Object* a1, int from, int to, Object* a4, int* a5); +bool _combat_is_shot_blocked(Object* sourceObj, int from, int to, Object* targetObj, int* numCrittersOnLof); int _combat_player_knocked_out_by(); int _combat_explode_scenery(Object* a1, Object* a2); void _combat_delete_critter(Object* obj); diff --git a/src/combat_ai.cc b/src/combat_ai.cc index 797d9e0..3ee2082 100644 --- a/src/combat_ai.cc +++ b/src/combat_ai.cc @@ -2327,61 +2327,61 @@ static int _ai_pick_hit_mode(Object* attacker, Object* weapon, Object* defender) } // 0x429FC8 -static int _ai_move_steps_closer(Object* a1, Object* a2, int actionPoints, bool taunt) +static int _ai_move_steps_closer(Object* critter, Object* target, int actionPoints, bool taunt) { if (actionPoints <= 0) { return -1; } - int distance = aiGetDistance(a1); + int distance = aiGetDistance(critter); if (distance == DISTANCE_STAY) { return -1; } if (distance == DISTANCE_STAY_CLOSE) { - if (a2 != gDude) { - int currentDistance = objectGetDistanceBetween(a1, gDude); + if (target != gDude) { + int currentDistance = objectGetDistanceBetween(critter, gDude); if (currentDistance > 5 - && objectGetDistanceBetween(a2, gDude) > 5 + && objectGetDistanceBetween(target, gDude) > 5 && currentDistance + actionPoints > 5) { return -1; } } } - if (objectGetDistanceBetween(a1, a2) <= 1) { + if (objectGetDistanceBetween(critter, target) <= 1) { return -1; } reg_anim_begin(ANIMATION_REQUEST_RESERVED); if (taunt) { - _combatai_msg(a1, NULL, AI_MESSAGE_TYPE_MOVE, 0); + _combatai_msg(critter, NULL, AI_MESSAGE_TYPE_MOVE, 0); } - Object* v18 = a2; + Object* initialTarget = target; bool shouldUnhide; - if ((a2->flags & OBJECT_MULTIHEX) != 0) { + if ((target->flags & OBJECT_MULTIHEX) != 0) { shouldUnhide = true; - a2->flags |= OBJECT_HIDDEN; + target->flags |= OBJECT_HIDDEN; } else { shouldUnhide = false; } - if (pathfinderFindPath(a1, a1->tile, a2->tile, NULL, 0, _obj_blocking_at) == 0) { + if (pathfinderFindPath(critter, critter->tile, target->tile, NULL, 0, _obj_blocking_at) == 0) { _moveBlockObj = NULL; - if (pathfinderFindPath(a1, a1->tile, a2->tile, NULL, 0, _obj_ai_blocking_at) == 0 + if (pathfinderFindPath(critter, critter->tile, target->tile, NULL, 0, _obj_ai_blocking_at) == 0 && _moveBlockObj != NULL && PID_TYPE(_moveBlockObj->pid) == OBJ_TYPE_CRITTER) { if (shouldUnhide) { - a2->flags &= ~OBJECT_HIDDEN; + target->flags &= ~OBJECT_HIDDEN; } - a2 = _moveBlockObj; - if ((a2->flags & OBJECT_MULTIHEX) != 0) { + target = _moveBlockObj; + if ((target->flags & OBJECT_MULTIHEX) != 0) { shouldUnhide = true; - a2->flags |= OBJECT_HIDDEN; + target->flags |= OBJECT_HIDDEN; } else { shouldUnhide = false; } @@ -2389,25 +2389,25 @@ static int _ai_move_steps_closer(Object* a1, Object* a2, int actionPoints, bool } if (shouldUnhide) { - a2->flags &= ~OBJECT_HIDDEN; + target->flags &= ~OBJECT_HIDDEN; } - int tile = a2->tile; - if (a2 == v18) { - _cai_retargetTileFromFriendlyFire(a1, a2, &tile); + int tile = target->tile; + if (target == initialTarget) { + _cai_retargetTileFromFriendlyFire(critter, target, &tile); } - if (actionPoints >= critterGetStat(a1, STAT_MAXIMUM_ACTION_POINTS) / 2 && artCritterFidShouldRun(a1->fid)) { - if ((a2->flags & OBJECT_MULTIHEX) != 0) { - animationRegisterRunToObject(a1, a2, actionPoints, 0); + if (actionPoints >= critterGetStat(critter, STAT_MAXIMUM_ACTION_POINTS) / 2 && artCritterFidShouldRun(critter->fid)) { + if ((target->flags & OBJECT_MULTIHEX) != 0) { + animationRegisterRunToObject(critter, target, actionPoints, 0); } else { - animationRegisterRunToTile(a1, tile, a1->elevation, actionPoints, 0); + animationRegisterRunToTile(critter, tile, critter->elevation, actionPoints, 0); } } else { - if ((a2->flags & OBJECT_MULTIHEX) != 0) { - animationRegisterMoveToObject(a1, a2, actionPoints, 0); + if ((target->flags & OBJECT_MULTIHEX) != 0) { + animationRegisterMoveToObject(critter, target, actionPoints, 0); } else { - animationRegisterMoveToTile(a1, tile, a1->elevation, actionPoints, 0); + animationRegisterMoveToTile(critter, tile, critter->elevation, actionPoints, 0); } } @@ -2665,35 +2665,35 @@ static int _ai_attack(Object* attacker, Object* defender, int hitMode) } // 0x42A7D8 -static int _ai_try_attack(Object* a1, Object* a2) +static int _ai_try_attack(Object* attacker, Object* defender) { - _critter_set_who_hit_me(a1, a2); + _critter_set_who_hit_me(attacker, defender); - CritterCombatData* combatData = &(a1->data.critter.combat); + CritterCombatData* combatData = &(attacker->data.critter.combat); bool taunt = true; - Object* weapon = critterGetItem2(a1); + Object* weapon = critterGetItem2(attacker); if (weapon != NULL && itemGetType(weapon) != ITEM_TYPE_WEAPON) { weapon = NULL; } - int hitMode = _ai_pick_hit_mode(a1, weapon, a2); - int minToHit = aiGetPacket(a1)->min_to_hit; + int hitMode = _ai_pick_hit_mode(attacker, weapon, defender); + int minToHit = aiGetPacket(attacker)->min_to_hit; - int actionPoints = a1->data.critter.combat.ap; + int actionPoints = attacker->data.critter.combat.ap; int safeDistance = 0; - int v42 = 0; + int actionPointsToUse = 0; if (weapon != NULL - || (critterGetBodyType(a2) == BODY_TYPE_BIPED - && ((a2->fid & 0xF000) >> 12 == 0) - && artExists(buildFid(OBJ_TYPE_CRITTER, a1->fid & 0xFFF, ANIM_THROW_PUNCH, 0, a1->rotation + 1)))) { + || (critterGetBodyType(defender) == BODY_TYPE_BIPED + && ((defender->fid & 0xF000) >> 12 == 0) + && artExists(buildFid(OBJ_TYPE_CRITTER, attacker->fid & 0xFFF, ANIM_THROW_PUNCH, 0, attacker->rotation + 1)))) { // SFALL: Check the safety of weapons based on the selected attack mode // instead of always the primary weapon hit mode. - if (_combat_safety_invalidate_weapon(a1, weapon, hitMode, a2, &safeDistance)) { - _ai_switch_weapons(a1, &hitMode, &weapon, a2); + if (_combat_safety_invalidate_weapon(attacker, weapon, hitMode, defender, &safeDistance)) { + _ai_switch_weapons(attacker, &hitMode, &weapon, defender); } } else { - _ai_switch_weapons(a1, &hitMode, &weapon, a2); + _ai_switch_weapons(attacker, &hitMode, &weapon, defender); } unsigned char rotations[800]; @@ -2704,37 +2704,37 @@ static int _ai_try_attack(Object* a1, Object* a2) break; } - int reason = _combat_check_bad_shot(a1, a2, hitMode, false); + int reason = _combat_check_bad_shot(attacker, defender, hitMode, false); if (reason == COMBAT_BAD_SHOT_NO_AMMO) { // out of ammo - if (aiHaveAmmo(a1, weapon, &ammo)) { + if (aiHaveAmmo(attacker, weapon, &ammo)) { int remainingAmmoQuantity = weaponReload(weapon, ammo); if (remainingAmmoQuantity == 0 && ammo != NULL) { _obj_destroy(ammo); } if (remainingAmmoQuantity != -1) { - int volume = _gsound_compute_relative_volume(a1); + int volume = _gsound_compute_relative_volume(attacker); const char* sfx = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_READY, weapon, hitMode, NULL); _gsound_play_sfx_file_volume(sfx, volume); - _ai_magic_hands(a1, weapon, 5002); + _ai_magic_hands(attacker, weapon, 5002); // SFALL: Fix incorrect AP cost when AI reloads a weapon. // CE: There is a commented out code which checks // available action points before performing reload. Not // sure why it was commented, probably needs additional // testing. - int actionPointsRequired = weaponGetActionPointCost(a1, HIT_MODE_RIGHT_WEAPON_RELOAD, false); - if (a1->data.critter.combat.ap >= actionPointsRequired) { - a1->data.critter.combat.ap -= actionPointsRequired; + int actionPointsRequired = weaponGetActionPointCost(attacker, HIT_MODE_RIGHT_WEAPON_RELOAD, false); + if (attacker->data.critter.combat.ap >= actionPointsRequired) { + attacker->data.critter.combat.ap -= actionPointsRequired; } else { - a1->data.critter.combat.ap = 0; + attacker->data.critter.combat.ap = 0; } } } else { - ammo = _ai_search_environ(a1, ITEM_TYPE_AMMO); + ammo = _ai_search_environ(attacker, ITEM_TYPE_AMMO); if (ammo != NULL) { - ammo = _ai_retrieve_object(a1, ammo); + ammo = _ai_retrieve_object(attacker, ammo); if (ammo != NULL) { int remainingAmmoQuantity = weaponReload(weapon, ammo); if (remainingAmmoQuantity == 0) { @@ -2742,62 +2742,63 @@ static int _ai_try_attack(Object* a1, Object* a2) } if (remainingAmmoQuantity != -1) { - int volume = _gsound_compute_relative_volume(a1); + int volume = _gsound_compute_relative_volume(attacker); const char* sfx = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_READY, weapon, hitMode, NULL); _gsound_play_sfx_file_volume(sfx, volume); - _ai_magic_hands(a1, weapon, 5002); + _ai_magic_hands(attacker, weapon, 5002); // SFALL: Fix incorrect AP cost when AI reloads a // weapon. // CE: See note above, probably need to check // available action points before performing // reload. - int actionPointsRequired = weaponGetActionPointCost(a1, HIT_MODE_RIGHT_WEAPON_RELOAD, false); - if (a1->data.critter.combat.ap >= actionPointsRequired) { - a1->data.critter.combat.ap -= actionPointsRequired; + int actionPointsRequired = weaponGetActionPointCost(attacker, HIT_MODE_RIGHT_WEAPON_RELOAD, false); + if (attacker->data.critter.combat.ap >= actionPointsRequired) { + attacker->data.critter.combat.ap -= actionPointsRequired; } else { - a1->data.critter.combat.ap = 0; + attacker->data.critter.combat.ap = 0; } } } } else { - int volume = _gsound_compute_relative_volume(a1); + int volume = _gsound_compute_relative_volume(attacker); const char* sfx = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_OUT_OF_AMMO, weapon, hitMode, NULL); _gsound_play_sfx_file_volume(sfx, volume); - _ai_magic_hands(a1, weapon, 5001); + _ai_magic_hands(attacker, weapon, 5001); - if (_inven_unwield(a1, 1) == 0) { + if (_inven_unwield(attacker, 1) == 0) { _combat_turn_run(); } - _ai_switch_weapons(a1, &hitMode, &weapon, a2); + _ai_switch_weapons(attacker, &hitMode, &weapon, defender); } } } else if (reason == COMBAT_BAD_SHOT_NOT_ENOUGH_AP || reason == COMBAT_BAD_SHOT_ARM_CRIPPLED || reason == COMBAT_BAD_SHOT_BOTH_ARMS_CRIPPLED) { // 3 - not enough action points // 6 - crippled one arm for two-handed weapon // 7 - both hands crippled - if (_ai_switch_weapons(a1, &hitMode, &weapon, a2) == -1) { + if (_ai_switch_weapons(attacker, &hitMode, &weapon, defender) == -1) { return -1; } } else if (reason == COMBAT_BAD_SHOT_OUT_OF_RANGE) { // target out of range - int accuracy = _determine_to_hit_no_range(a1, a2, HIT_LOCATION_UNCALLED, hitMode, rotations); - if (accuracy < minToHit) { - debugPrint("%s: FLEEING: Can't possibly Hit Target!", critterGetName(a1)); - _ai_run_away(a1, a2); + int toHitNoRange = _determine_to_hit_no_range(attacker, defender, HIT_LOCATION_UNCALLED, hitMode, rotations); + if (toHitNoRange < minToHit) { + // hit chance is too low even at point blank range (not taking range into account) + debugPrint("%s: FLEEING: Can't possibly Hit Target!", critterGetName(attacker)); + _ai_run_away(attacker, defender); return 0; } if (weapon != NULL) { - if (_ai_move_steps_closer(a1, a2, actionPoints, taunt) == -1) { + if (_ai_move_steps_closer(attacker, defender, actionPoints, taunt) == -1) { return -1; } taunt = false; } else { - if (_ai_switch_weapons(a1, &hitMode, &weapon, a2) == -1 || weapon == NULL) { + if (_ai_switch_weapons(attacker, &hitMode, &weapon, defender) == -1 || weapon == NULL) { // NOTE: Uninline. - if (_ai_move_closer(a1, a2, taunt) == -1) { + if (_ai_move_closer(attacker, defender, taunt) == -1) { return -1; } } @@ -2805,66 +2806,66 @@ static int _ai_try_attack(Object* a1, Object* a2) } } else if (reason == COMBAT_BAD_SHOT_AIM_BLOCKED) { // aim is blocked - if (_ai_move_steps_closer(a1, a2, a1->data.critter.combat.ap, taunt) == -1) { + if (_ai_move_steps_closer(attacker, defender, attacker->data.critter.combat.ap, taunt) == -1) { return -1; } taunt = false; } else if (reason == COMBAT_BAD_SHOT_OK) { - int accuracy = _determine_to_hit(a1, a2, HIT_LOCATION_UNCALLED, hitMode); + int accuracy = _determine_to_hit(attacker, defender, HIT_LOCATION_UNCALLED, hitMode); if (safeDistance != 0) { - if (_ai_move_away(a1, a2, safeDistance) == -1) { + if (_ai_move_away(attacker, defender, safeDistance) == -1) { return -1; } } if (accuracy < minToHit) { - int accuracyNoRange = _determine_to_hit_no_range(a1, a2, HIT_LOCATION_UNCALLED, hitMode, rotations); - if (accuracyNoRange < minToHit) { - debugPrint("%s: FLEEING: Can't possibly Hit Target!", critterGetName(a1)); - _ai_run_away(a1, a2); + int toHitNoRange = _determine_to_hit_no_range(attacker, defender, HIT_LOCATION_UNCALLED, hitMode, rotations); + if (toHitNoRange < minToHit) { + debugPrint("%s: FLEEING: Can't possibly Hit Target!", critterGetName(attacker)); + _ai_run_away(attacker, defender); return 0; } if (actionPoints > 0) { - int v24 = pathfinderFindPath(a1, a1->tile, a2->tile, rotations, 0, _obj_blocking_at); - if (v24 == 0) { - v42 = actionPoints; + int pathLength = pathfinderFindPath(attacker, attacker->tile, defender->tile, rotations, 0, _obj_blocking_at); + if (pathLength == 0) { + actionPointsToUse = actionPoints; } else { - if (v24 < actionPoints) { - actionPoints = v24; + if (pathLength < actionPoints) { + actionPoints = pathLength; } - int tile = a1->tile; + int tile = attacker->tile; int index; for (index = 0; index < actionPoints; index++) { tile = tileGetTileInDirection(tile, rotations[index], 1); - v42++; + actionPointsToUse++; - int v27 = _determine_to_hit_from_tile(a1, tile, a2, HIT_LOCATION_UNCALLED, hitMode); - if (v27 >= minToHit) { + int toHit = _determine_to_hit_from_tile(attacker, tile, defender, HIT_LOCATION_UNCALLED, hitMode); + if (toHit >= minToHit) { break; } } if (index == actionPoints) { - v42 = actionPoints; + actionPointsToUse = actionPoints; } } } - if (_ai_move_steps_closer(a1, a2, v42, taunt) == -1) { - debugPrint("%s: FLEEING: Can't possibly get closer to Target!", critterGetName(a1)); - _ai_run_away(a1, a2); + if (_ai_move_steps_closer(attacker, defender, actionPointsToUse, taunt) == -1) { + debugPrint("%s: FLEEING: Can't possibly get closer to Target!", critterGetName(attacker)); + _ai_run_away(attacker, defender); return 0; } taunt = false; - if (_ai_attack(a1, a2, hitMode) == -1 || weaponGetActionPointCost(a1, hitMode, 0) > a1->data.critter.combat.ap) { + if (_ai_attack(attacker, defender, hitMode) == -1 || weaponGetActionPointCost(attacker, hitMode, 0) > attacker->data.critter.combat.ap) { return -1; } } else { - if (_ai_attack(a1, a2, hitMode) == -1 || weaponGetActionPointCost(a1, hitMode, 0) > a1->data.critter.combat.ap) { + if (_ai_attack(attacker, defender, hitMode) == -1 || weaponGetActionPointCost(attacker, hitMode, 0) > attacker->data.critter.combat.ap) { return -1; } } diff --git a/src/game_sound.cc b/src/game_sound.cc index 618457b..f1dc99b 100644 --- a/src/game_sound.cc +++ b/src/game_sound.cc @@ -1370,7 +1370,7 @@ char* gameSoundBuildInterfaceName(const char* a1) // 0x451760 char* sfxBuildWeaponName(int effectType, Object* weapon, int hitMode, Object* target) { - int v6; + int soundVariant; char weaponSoundCode; char effectTypeCode; char materialCode; @@ -1384,12 +1384,12 @@ char* sfxBuildWeaponName(int effectType, Object* weapon, int hitMode, Object* ta if (hitMode != HIT_MODE_LEFT_WEAPON_PRIMARY && hitMode != HIT_MODE_RIGHT_WEAPON_PRIMARY && hitMode != HIT_MODE_PUNCH) { - v6 = 2; + soundVariant = 2; } else { - v6 = 1; + soundVariant = 1; } } else { - v6 = 1; + soundVariant = 1; } int damageType = weaponGetDamageType(NULL, weapon); @@ -1438,7 +1438,7 @@ char* sfxBuildWeaponName(int effectType, Object* weapon, int hitMode, Object* ta } } - snprintf(_sfx_file_name, sizeof(_sfx_file_name), "W%c%c%1d%cXX%1d", effectTypeCode, weaponSoundCode, v6, materialCode, 1); + snprintf(_sfx_file_name, sizeof(_sfx_file_name), "W%c%c%1d%cXX%1d", effectTypeCode, weaponSoundCode, soundVariant, materialCode, 1); compat_strupr(_sfx_file_name); return _sfx_file_name; } diff --git a/src/interpreter.cc b/src/interpreter.cc index ab866ce..a147ce8 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -31,13 +31,13 @@ static int _outputStr(char* a1); static int _checkWait(Program* program); static char* programGetCurrentProcedureName(Program* s); static opcode_t stackReadInt16(unsigned char* data, int pos); -static int stackReadInt32(unsigned char* a1, int a2); -static void stackWriteInt16(int value, unsigned char* a2, int a3); -static void stackWriteInt32(int value, unsigned char* stack, int pos); -static void stackPushInt16(unsigned char* a1, int* a2, int value); -static void stackPushInt32(unsigned char* a1, int* a2, int value); -static int stackPopInt32(unsigned char* a1, int* a2); -static opcode_t stackPopInt16(unsigned char* a1, int* a2); +static int stackReadInt32(unsigned char* data, int pos); +static void stackWriteInt16(int value, unsigned char* data, int pos); +static void stackWriteInt32(int value, unsigned char* data, int pos); +static void stackPushInt16(unsigned char* data, int* pointer, int value); +static void stackPushInt32(unsigned char* data, int* pointer, int value); +static int stackPopInt32(unsigned char* data, int* pointer); +static opcode_t stackPopInt16(unsigned char* data, int* pointer); static void _interpretIncStringRef(Program* program, opcode_t opcode, int value); static void programReturnStackPushInt16(Program* program, int value); static opcode_t programReturnStackPopInt16(Program* program); @@ -51,7 +51,7 @@ static void opPush(Program* program); static void opPushBase(Program* program); static void opPopBase(Program* program); static void opPopToBase(Program* program); -static void op802C(Program* program); +static void opSetGlobal(Program* program); static void opDump(Program* program); static void opDelayedCall(Program* program); static void opConditionalCall(Program* program); @@ -230,17 +230,17 @@ static char* programGetCurrentProcedureName(Program* program) int procedureCount = stackReadInt32(program->procedures, 0); unsigned char* ptr = program->procedures + 4; - int procedureOffset = stackReadInt32(ptr, 16); - int identifierOffset = stackReadInt32(ptr, 0); + int procedureOffset = stackReadInt32(ptr, offsetof(Procedure, bodyOffset)); + int identifierOffset = stackReadInt32(ptr, offsetof(Procedure, nameOffset)); for (int index = 0; index < procedureCount; index++) { - int nextProcedureOffset = stackReadInt32(ptr + 24, 16); + int nextProcedureOffset = stackReadInt32(ptr + 24, offsetof(Procedure, bodyOffset)); if (program->instructionPointer >= procedureOffset && program->instructionPointer < nextProcedureOffset) { return (char*)(program->identifiers + identifierOffset); } ptr += 24; - identifierOffset = stackReadInt32(ptr, 0); + identifierOffset = stackReadInt32(ptr, offsetof(Procedure, nameOffset)); } return _aCouldnTFindPro; @@ -712,7 +712,7 @@ static void opPopToBase(Program* program) } // 0x467DE0 -static void op802C(Program* program) +static void opSetGlobal(Program* program) { program->basePointer = program->stackValues->size(); } @@ -745,10 +745,10 @@ static void opDelayedCall(Program* program) delay += 1000 * _timerFunc() / _timerTick; } - int flags = stackReadInt32(procedure_ptr, 4); + int flags = stackReadInt32(procedure_ptr, offsetof(Procedure, flags)); - stackWriteInt32(delay, procedure_ptr, 8); - stackWriteInt32(flags | PROCEDURE_FLAG_TIMED, procedure_ptr, 4); + stackWriteInt32(delay, procedure_ptr, offsetof(Procedure, time)); + stackWriteInt32(flags | PROCEDURE_FLAG_TIMED, procedure_ptr, offsetof(Procedure, flags)); } // 0x468034 @@ -761,10 +761,10 @@ static void opConditionalCall(Program* program) } unsigned char* procedure_ptr = program->procedures + 4 + 24 * data[0]; - int flags = stackReadInt32(procedure_ptr, 4); + int flags = stackReadInt32(procedure_ptr, offsetof(Procedure, flags)); - stackWriteInt32(flags | PROCEDURE_FLAG_CONDITIONAL, procedure_ptr, 4); - stackWriteInt32(data[1], procedure_ptr, 12); + stackWriteInt32(flags | PROCEDURE_FLAG_CONDITIONAL, procedure_ptr, offsetof(Procedure, flags)); + stackWriteInt32(data[1], procedure_ptr, offsetof(Procedure, conditionOffset)); } // 0x46817C @@ -788,9 +788,9 @@ static void opCancel(Program* program) } Procedure* proc = (Procedure*)(program->procedures + 4 + data * sizeof(*proc)); - proc->field_4 = 0; - proc->field_8 = 0; - proc->field_C = 0; + proc->flags = 0; + proc->time = 0; + proc->conditionOffset = 0; } // 0x468330 @@ -802,9 +802,9 @@ static void opCancelAll(Program* program) // TODO: Original code uses different approach, check. Procedure* proc = (Procedure*)(program->procedures + 4 + index * sizeof(*proc)); - proc->field_4 = 0; - proc->field_8 = 0; - proc->field_C = 0; + proc->flags = 0; + proc->time = 0; + proc->conditionOffset = 0; } } @@ -2030,12 +2030,12 @@ static void opCall(Program* program) unsigned char* ptr = program->procedures + 4 + 24 * value; - int flags = stackReadInt32(ptr, 4); - if ((flags & 4) != 0) { + int flags = stackReadInt32(ptr, offsetof(Procedure, flags)); + if ((flags & PROCEDURE_FLAG_IMPORTED) != 0) { // TODO: Incomplete. } else { - program->instructionPointer = stackReadInt32(ptr, 16); - if ((flags & 0x10) != 0) { + program->instructionPointer = stackReadInt32(ptr, offsetof(Procedure, bodyOffset)); + if ((flags & PROCEDURE_FLAG_CRITICAL) != 0) { program->flags |= PROGRAM_FLAG_CRITICAL_SECTION; } } @@ -2238,7 +2238,7 @@ static void opFetchProcedureAddress(Program* program) { int procedureIndex = programStackPopInteger(program); - int address = stackReadInt32(program->procedures + 4 + sizeof(Procedure) * procedureIndex, 16); + int address = stackReadInt32(program->procedures + 4 + sizeof(Procedure) * procedureIndex, offsetof(Procedure, bodyOffset)); programStackPushInteger(program, address); } @@ -2298,8 +2298,8 @@ static void opExportProcedure(Program* program) unsigned char* proc_ptr = program->procedures + 4 + sizeof(Procedure) * procedureIndex; - char* procedureName = programGetIdentifier(program, stackReadInt32(proc_ptr, 0)); - int procedureAddress = stackReadInt32(proc_ptr, 16); + char* procedureName = programGetIdentifier(program, stackReadInt32(proc_ptr, offsetof(Procedure, nameOffset))); + int procedureAddress = stackReadInt32(proc_ptr, offsetof(Procedure, bodyOffset)); if (externalProcedureCreate(program, procedureName, procedureAddress, argumentCount) != 0) { char err[256]; @@ -2468,9 +2468,9 @@ static void opCheckProcedureArgumentCount(Program* program) int expectedArgumentCount = programStackPopInteger(program); int procedureIndex = programStackPopInteger(program); - int actualArgumentCount = stackReadInt32(program->procedures + 4 + 24 * procedureIndex, 20); + int actualArgumentCount = stackReadInt32(program->procedures + 4 + 24 * procedureIndex, offsetof(Procedure, argCount)); if (actualArgumentCount != expectedArgumentCount) { - const char* identifier = programGetIdentifier(program, stackReadInt32(program->procedures + 4 + 24 * procedureIndex, 0)); + const char* identifier = programGetIdentifier(program, stackReadInt32(program->procedures + 4 + 24 * procedureIndex, offsetof(Procedure, nameOffset))); char err[260]; snprintf(err, sizeof(err), "Wrong number of args to procedure %s\n", identifier); programFatalError(err); @@ -2491,7 +2491,7 @@ static void opLookupStringProc(Program* program) // Start with 1 since we've skipped main procedure, which is always at // index 0. for (int index = 1; index < procedureCount; index++) { - int offset = stackReadInt32(procedurePtr, 0); + int offset = stackReadInt32(procedurePtr, offsetof(Procedure, nameOffset)); const char* procedureName = programGetIdentifier(program, offset); if (compat_stricmp(procedureName, procedureNameToLookup) == 0) { programStackPushInteger(program, index); @@ -2556,7 +2556,7 @@ void interpreterRegisterOpcodeHandlers() interpreterRegisterOpcode(OPCODE_POP_BASE, opPopBase); interpreterRegisterOpcode(OPCODE_POP_TO_BASE, opPopToBase); interpreterRegisterOpcode(OPCODE_PUSH_BASE, opPushBase); - interpreterRegisterOpcode(OPCODE_SET_GLOBAL, op802C); + interpreterRegisterOpcode(OPCODE_SET_GLOBAL, opSetGlobal); interpreterRegisterOpcode(OPCODE_FETCH_PROCEDURE_ADDRESS, opFetchProcedureAddress); interpreterRegisterOpcode(OPCODE_DUMP, opDump); interpreterRegisterOpcode(OPCODE_IF, opIf); @@ -2776,9 +2776,9 @@ void _executeProc(Program* program, int procedureIndex) char err[256]; procedurePtr = program->procedures + 4 + sizeof(Procedure) * procedureIndex; - procedureFlags = stackReadInt32(procedurePtr, 4); + procedureFlags = stackReadInt32(procedurePtr, offsetof(Procedure, flags)); if ((procedureFlags & PROCEDURE_FLAG_IMPORTED) != 0) { - procedureIdentifier = programGetIdentifier(program, stackReadInt32(procedurePtr, 0)); + procedureIdentifier = programGetIdentifier(program, stackReadInt32(procedurePtr, offsetof(Procedure, nameOffset))); externalProgram = externalProcedureGetProgram(procedureIdentifier, &externalProcedureAddress, &externalProcedureArgumentCount); if (externalProgram != NULL) { if (externalProcedureArgumentCount == 0) { @@ -2795,7 +2795,7 @@ void _executeProc(Program* program, int procedureIndex) _setupExternalCall(program, externalProgram, externalProcedureAddress, 28); procedurePtr = externalProgram->procedures + 4 + sizeof(Procedure) * procedureIndex; - procedureFlags = stackReadInt32(procedurePtr, 4); + procedureFlags = stackReadInt32(procedurePtr, offsetof(Procedure, flags)); if ((procedureFlags & PROCEDURE_FLAG_CRITICAL) != 0) { // NOTE: Uninline. @@ -2803,7 +2803,7 @@ void _executeProc(Program* program, int procedureIndex) _interpret(externalProgram, 0); } } else { - procedureAddress = stackReadInt32(procedurePtr, 16); + procedureAddress = stackReadInt32(procedurePtr, offsetof(Procedure, bodyOffset)); // NOTE: Uninline. _setupCall(program, procedureAddress, 20); @@ -2826,7 +2826,7 @@ int programFindProcedure(Program* program, const char* name) unsigned char* ptr = program->procedures + 4; for (int index = 0; index < procedureCount; index++) { - int identifierOffset = stackReadInt32(ptr, offsetof(Procedure, field_0)); + int identifierOffset = stackReadInt32(ptr, offsetof(Procedure, nameOffset)); if (compat_stricmp((char*)(program->identifiers + identifierOffset), name) == 0) { return index; } @@ -2851,10 +2851,10 @@ void _executeProcedure(Program* program, int procedureIndex) jmp_buf env; procedurePtr = program->procedures + 4 + sizeof(Procedure) * procedureIndex; - procedureFlags = stackReadInt32(procedurePtr, 4); + procedureFlags = stackReadInt32(procedurePtr, offsetof(Procedure, flags)); if ((procedureFlags & PROCEDURE_FLAG_IMPORTED) != 0) { - procedureIdentifier = programGetIdentifier(program, stackReadInt32(procedurePtr, 0)); + procedureIdentifier = programGetIdentifier(program, stackReadInt32(procedurePtr, offsetof(Procedure, nameOffset))); externalProgram = externalProcedureGetProgram(procedureIdentifier, &externalProcedureAddress, &externalProcedureArgumentCount); if (externalProgram != NULL) { if (externalProcedureArgumentCount == 0) { @@ -2872,7 +2872,7 @@ void _executeProcedure(Program* program, int procedureIndex) _interpretOutput(err); } } else { - procedureAddress = stackReadInt32(procedurePtr, 16); + procedureAddress = stackReadInt32(procedurePtr, offsetof(Procedure, bodyOffset)); // NOTE: Uninline. _setupCall(program, procedureAddress, 24); @@ -2908,14 +2908,14 @@ static void _doEvents() procedurePtr = programListNode->program->procedures + 4; for (procedureIndex = 0; procedureIndex < procedureCount; procedureIndex++) { - procedureFlags = stackReadInt32(procedurePtr, 4); + procedureFlags = stackReadInt32(procedurePtr, offsetof(Procedure, flags)); if ((procedureFlags & PROCEDURE_FLAG_CONDITIONAL) != 0) { memcpy(env, programListNode->program, sizeof(env)); oldProgramFlags = programListNode->program->flags; oldInstructionPointer = programListNode->program->instructionPointer; programListNode->program->flags = 0; - programListNode->program->instructionPointer = stackReadInt32(procedurePtr, 12); + programListNode->program->instructionPointer = stackReadInt32(procedurePtr, offsetof(Procedure, conditionOffset)); _interpret(programListNode->program, -1); if ((programListNode->program->flags & PROGRAM_FLAG_0x04) == 0) { @@ -2926,16 +2926,16 @@ static void _doEvents() if (data != 0) { // NOTE: Uninline. - stackWriteInt32(0, procedurePtr, 4); + stackWriteInt32(0, procedurePtr, offsetof(Procedure, flags)); _executeProc(programListNode->program, procedureIndex); } } memcpy(programListNode->program, env, sizeof(env)); } else if ((procedureFlags & PROCEDURE_FLAG_TIMED) != 0) { - if ((unsigned int)stackReadInt32(procedurePtr, 8) < time) { + if ((unsigned int)stackReadInt32(procedurePtr, offsetof(Procedure, time)) < time) { // NOTE: Uninline. - stackWriteInt32(0, procedurePtr, 4); + stackWriteInt32(0, procedurePtr, offsetof(Procedure, flags)); _executeProc(programListNode->program, procedureIndex); } } diff --git a/src/interpreter.h b/src/interpreter.h index 6095292..6f40416 100644 --- a/src/interpreter.h +++ b/src/interpreter.h @@ -133,12 +133,12 @@ enum RawValueType { typedef unsigned short opcode_t; typedef struct Procedure { - int field_0; - int field_4; - int field_8; - int field_C; - int field_10; - int field_14; + int nameOffset; + int flags; + int time; + int conditionOffset; + int bodyOffset; + int argCount; } Procedure; class ProgramValue { diff --git a/src/object.cc b/src/object.cc index a597a76..4c46d21 100644 --- a/src/object.cc +++ b/src/object.cc @@ -2375,10 +2375,10 @@ bool _obj_occupied(int tile, int elevation) } // 0x48B848 -Object* _obj_blocking_at(Object* a1, int tile, int elev) +Object* _obj_blocking_at(Object* excludeObj, int tile, int elev) { ObjectListNode* objectListNode; - Object* v7; + Object* obj; int type; if (!hexGridTileIsValid(tile)) { @@ -2387,14 +2387,14 @@ Object* _obj_blocking_at(Object* a1, int tile, int elev) objectListNode = gObjectListHeadByTile[tile]; while (objectListNode != NULL) { - v7 = objectListNode->obj; - if (v7->elevation == elev) { - if ((v7->flags & OBJECT_HIDDEN) == 0 && (v7->flags & OBJECT_NO_BLOCK) == 0 && v7 != a1) { - type = FID_TYPE(v7->fid); + obj = objectListNode->obj; + if (obj->elevation == elev) { + if ((obj->flags & OBJECT_HIDDEN) == 0 && (obj->flags & OBJECT_NO_BLOCK) == 0 && obj != excludeObj) { + type = FID_TYPE(obj->fid); if (type == OBJ_TYPE_CRITTER || type == OBJ_TYPE_SCENERY || type == OBJ_TYPE_WALL) { - return v7; + return obj; } } } @@ -2406,15 +2406,15 @@ Object* _obj_blocking_at(Object* a1, int tile, int elev) if (hexGridTileIsValid(neighboor)) { objectListNode = gObjectListHeadByTile[neighboor]; while (objectListNode != NULL) { - v7 = objectListNode->obj; - if ((v7->flags & OBJECT_MULTIHEX) != 0) { - if (v7->elevation == elev) { - if ((v7->flags & OBJECT_HIDDEN) == 0 && (v7->flags & OBJECT_NO_BLOCK) == 0 && v7 != a1) { - type = FID_TYPE(v7->fid); + obj = objectListNode->obj; + if ((obj->flags & OBJECT_MULTIHEX) != 0) { + if (obj->elevation == elev) { + if ((obj->flags & OBJECT_HIDDEN) == 0 && (obj->flags & OBJECT_NO_BLOCK) == 0 && obj != excludeObj) { + type = FID_TYPE(obj->fid); if (type == OBJ_TYPE_CRITTER || type == OBJ_TYPE_SCENERY || type == OBJ_TYPE_WALL) { - return v7; + return obj; } } } @@ -2428,7 +2428,7 @@ Object* _obj_blocking_at(Object* a1, int tile, int elev) } // 0x48B930 -Object* _obj_shoot_blocking_at(Object* obj, int tile, int elev) +Object* _obj_shoot_blocking_at(Object* excludeObj, int tile, int elev) { if (!hexGridTileIsValid(tile)) { return NULL; @@ -2439,7 +2439,7 @@ Object* _obj_shoot_blocking_at(Object* obj, int tile, int elev) Object* candidate = objectListItem->obj; if (candidate->elevation == elev) { unsigned int flags = candidate->flags; - if ((flags & OBJECT_HIDDEN) == 0 && ((flags & OBJECT_NO_BLOCK) == 0 || (flags & OBJECT_SHOOT_THRU) == 0) && candidate != obj) { + if ((flags & OBJECT_HIDDEN) == 0 && ((flags & OBJECT_NO_BLOCK) == 0 || (flags & OBJECT_SHOOT_THRU) == 0) && candidate != excludeObj) { int type = FID_TYPE(candidate->fid); // SFALL: Fix to prevent corpses from blocking line of fire. if ((type == OBJ_TYPE_CRITTER && !critterIsDead(candidate)) @@ -2464,7 +2464,7 @@ Object* _obj_shoot_blocking_at(Object* obj, int tile, int elev) unsigned int flags = candidate->flags; if ((flags & OBJECT_MULTIHEX) != 0) { if (candidate->elevation == elev) { - if ((flags & OBJECT_HIDDEN) == 0 && (flags & OBJECT_NO_BLOCK) == 0 && candidate != obj) { + if ((flags & OBJECT_HIDDEN) == 0 && (flags & OBJECT_NO_BLOCK) == 0 && candidate != excludeObj) { int type = FID_TYPE(candidate->fid); // SFALL: Fix to prevent corpses from blocking line of // fire. @@ -2484,7 +2484,7 @@ Object* _obj_shoot_blocking_at(Object* obj, int tile, int elev) } // 0x48BA20 -Object* _obj_ai_blocking_at(Object* a1, int tile, int elevation) +Object* _obj_ai_blocking_at(Object* excludeObj, int tile, int elevation) { if (!hexGridTileIsValid(tile)) { return NULL; @@ -2496,7 +2496,7 @@ Object* _obj_ai_blocking_at(Object* a1, int tile, int elevation) if (object->elevation == elevation) { if ((object->flags & OBJECT_HIDDEN) == 0 && (object->flags & OBJECT_NO_BLOCK) == 0 - && object != a1) { + && object != excludeObj) { int objectType = FID_TYPE(object->fid); if (objectType == OBJ_TYPE_CRITTER || objectType == OBJ_TYPE_SCENERY @@ -2525,7 +2525,7 @@ Object* _obj_ai_blocking_at(Object* a1, int tile, int elevation) if (object->elevation == elevation) { if ((object->flags & OBJECT_HIDDEN) == 0 && (object->flags & OBJECT_NO_BLOCK) == 0 - && object != a1) { + && object != excludeObj) { int objectType = FID_TYPE(object->fid); if (objectType == OBJ_TYPE_CRITTER || objectType == OBJ_TYPE_SCENERY @@ -2571,7 +2571,7 @@ int _obj_scroll_blocking_at(int tile, int elev) } // 0x48BB88 -Object* _obj_sight_blocking_at(Object* a1, int tile, int elevation) +Object* _obj_sight_blocking_at(Object* excludeObj, int tile, int elevation) { ObjectListNode* objectListNode = gObjectListHeadByTile[tile]; while (objectListNode != NULL) { @@ -2579,7 +2579,7 @@ Object* _obj_sight_blocking_at(Object* a1, int tile, int elevation) if (object->elevation == elevation && (object->flags & OBJECT_HIDDEN) == 0 && (object->flags & OBJECT_LIGHT_THRU) == 0 - && object != a1) { + && object != excludeObj) { int objectType = FID_TYPE(object->fid); if (objectType == OBJ_TYPE_SCENERY || objectType == OBJ_TYPE_WALL) { return object; diff --git a/src/object.h b/src/object.h index c397317..f9ccfb5 100644 --- a/src/object.h +++ b/src/object.h @@ -71,11 +71,11 @@ Object* objectFindFirstAtLocation(int elevation, int tile); Object* objectFindNextAtLocation(); void objectGetRect(Object* obj, Rect* rect); bool _obj_occupied(int tile_num, int elev); -Object* _obj_blocking_at(Object* a1, int tile_num, int elev); -Object* _obj_shoot_blocking_at(Object* obj, int tile, int elev); -Object* _obj_ai_blocking_at(Object* a1, int tile, int elevation); +Object* _obj_blocking_at(Object* excludeObj, int tile_num, int elev); +Object* _obj_shoot_blocking_at(Object* excludeObj, int tile, int elev); +Object* _obj_ai_blocking_at(Object* excludeObj, int tile, int elevation); int _obj_scroll_blocking_at(int tile_num, int elev); -Object* _obj_sight_blocking_at(Object* a1, int tile_num, int elev); +Object* _obj_sight_blocking_at(Object* excludeObj, int tile_num, int elev); int objectGetDistanceBetween(Object* object1, Object* object2); int objectGetDistanceBetweenTiles(Object* object1, int tile1, Object* object2, int tile2); int objectListCreate(int tile, int elevation, int objectType, Object*** objectsPtr); diff --git a/src/queue.cc b/src/queue.cc index ab0b54c..42d6bf9 100644 --- a/src/queue.cc +++ b/src/queue.cc @@ -344,18 +344,19 @@ bool queueHasEvent(Object* owner, int eventType) int queueProcessEvents() { unsigned int time = gameTimeGetTime(); - int v1 = 0; + // TODO: this is 0 or 1, but in some cases -1. Probably needs to be bool. + int stopProcess = 0; while (gQueueListHead != NULL) { QueueListNode* queueListNode = gQueueListHead; - if (time < queueListNode->time || v1 != 0) { + if (time < queueListNode->time || stopProcess != 0) { break; } gQueueListHead = queueListNode->next; EventTypeDescription* eventTypeDescription = &(gEventTypeDescriptions[queueListNode->type]); - v1 = eventTypeDescription->handlerProc(queueListNode->owner, queueListNode->data); + stopProcess = eventTypeDescription->handlerProc(queueListNode->owner, queueListNode->data); if (eventTypeDescription->freeProc != NULL) { eventTypeDescription->freeProc(queueListNode->data); @@ -364,7 +365,7 @@ int queueProcessEvents() internal_free(queueListNode); } - return v1; + return stopProcess; } // 0x4A2748 diff --git a/src/scripts.cc b/src/scripts.cc index e517466..ddcf5c6 100644 --- a/src/scripts.cc +++ b/src/scripts.cc @@ -405,7 +405,7 @@ int gameTimeScheduleUpdateEvent() int gameTimeEventProcess(Object* obj, void* data) { int movie_index; - int v4; + int stopProcess; movie_index = -1; @@ -421,17 +421,17 @@ int gameTimeEventProcess(Object* obj, void* data) _scriptsCheckGameEvents(&movie_index, -1); } - v4 = _critter_check_rads(gDude); + stopProcess = _critter_check_rads(gDude); _queue_clear_type(4, 0); gameTimeScheduleUpdateEvent(); if (movie_index != -1) { - v4 = 1; + stopProcess = 1; } - return v4; + return stopProcess; } // 0x4A3690 From 3af2a4925064933c7998e552adc31a516e3f3fb9 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Wed, 19 Jul 2023 11:39:01 +0300 Subject: [PATCH 022/136] Review debug_register_log --- src/debug.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/debug.cc b/src/debug.cc index 56e765e..29df660 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -56,7 +56,7 @@ void _debug_register_mono() // 0x4C6D18 void _debug_register_log(const char* fileName, const char* mode) { - if ((mode[0] == 'w' && mode[1] == 'a') && mode[1] == 't') { + if ((mode[0] == 'w' || mode[0] == 'a') && mode[1] == 't') { if (_fd != NULL) { fclose(_fd); } From a7628ccaeaceffe9d6b1d59f24eefea8efdc5c41 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Wed, 19 Jul 2023 11:43:10 +0300 Subject: [PATCH 023/136] Fix bufferDrawLine Missing 1 pixel in the middle of the vertical line. --- src/draw.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/draw.cc b/src/draw.cc index 674b0ac..721a95e 100644 --- a/src/draw.cc +++ b/src/draw.cc @@ -27,7 +27,7 @@ void bufferDrawLine(unsigned char* buf, int pitch, int x1, int y1, int x2, int y p1 = buf + pitch * y1 + x1; p2 = buf + pitch * y2 + x2; - while (p1 < p2) { + while (p1 <= p2) { *p1 = color; *p2 = color; p1 += pitch; From 90d1545bb2607366be62bd3333954d592b4d08ab Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Wed, 19 Jul 2023 11:45:55 +0300 Subject: [PATCH 024/136] Uninline debug_register_func --- src/debug.cc | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/debug.cc b/src/debug.cc index 29df660..e793758 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -104,14 +104,8 @@ void _debug_register_env() // NOTE: Uninline. _debug_register_screen(); } else if (strcmp(copy, "gnw") == 0) { - if (gDebugPrintProc != _win_debug) { - if (_fd != NULL) { - fclose(_fd); - _fd = NULL; - } - - gDebugPrintProc = _win_debug; - } + // NOTE: Uninline. + _debug_register_func(_win_debug); } internal_free(copy); From 3a17818ceb7dafb3ec3ffc9ac3c2a906d823fa5c Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Wed, 19 Jul 2023 11:51:13 +0300 Subject: [PATCH 025/136] Handle debug mode Closes #298 --- src/game.cc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/game.cc b/src/game.cc index abe6613..648e501 100644 --- a/src/game.cc +++ b/src/game.cc @@ -65,6 +65,7 @@ #include "trait.h" #include "version.h" #include "window_manager.h" +#include "window_manager_private.h" #include "worldmap.h" namespace fallout { @@ -168,6 +169,20 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4 showSplash(); } + // CE: Handle debug mode (exactly as seen in `mapper2.exe`). + const char* debugMode = settings.debug.mode.c_str(); + if (compat_stricmp(debugMode, "environment") == 0) { + _debug_register_env(); + } else if (compat_stricmp(debugMode, "screen") == 0) { + _debug_register_screen(); + } else if (compat_stricmp(debugMode, "log") == 0) { + _debug_register_log("debug.log", "wt"); + } else if (compat_stricmp(debugMode, "mono") == 0) { + _debug_register_mono(); + } else if (compat_stricmp(debugMode, "gnw") == 0) { + _debug_register_func(_win_debug); + } + interfaceFontsInit(); fontManagerAdd(&gModernFontManager); fontSetCurrent(font); From 8414effc04aea76036f6be595649e82e4ab7a1ad Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Wed, 19 Jul 2023 12:01:20 +0300 Subject: [PATCH 026/136] Fix GNW color indexes --- src/window_manager.cc | 20 ++++++++++---------- src/window_manager_private.cc | 5 ++--- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/window_manager.cc b/src/window_manager.cc index e0f170a..ba460c0 100644 --- a/src/window_manager.cc +++ b/src/window_manager.cc @@ -549,9 +549,9 @@ void windowDrawText(int win, const char* str, int width, int x, int y, int color } } - if (color & 0xFF00) { - int t = (color & 0xFF00) >> 8; - textColor = (color & ~0xFFFF) | _colorTable[_GNW_wcolor[t]]; + if ((color & 0xFF00) != 0) { + int colorIndex = (color & 0xFF) - 1; + textColor = (color & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]]; } else { textColor = color; } @@ -582,9 +582,9 @@ void windowDrawLine(int win, int left, int top, int right, int bottom, int color return; } - if (color & 0xFF00) { - int t = (color & 0xFF00) >> 8; - color = (color & ~0xFFFF) | _colorTable[_GNW_wcolor[t]]; + if ((color & 0xFF00) != 0) { + int colorIndex = (color & 0xFF) - 1; + color = (color & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]]; } bufferDrawLine(window->buffer, window->width, left, top, right, bottom, color); @@ -604,8 +604,8 @@ void windowDrawRect(int win, int left, int top, int right, int bottom, int color } if ((color & 0xFF00) != 0) { - int v1 = (color & 0xFF00) >> 8; - color = (color & ~0xFFFF) | _colorTable[_GNW_wcolor[v1]]; + int colorIndex = (color & 0xFF) - 1; + color = (color & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]]; } if (right < left) { @@ -643,8 +643,8 @@ void windowFill(int win, int x, int y, int width, int height, int color) color = _colorTable[_GNW_wcolor[0]] & 0xFF; } } else if ((color & 0xFF00) != 0) { - int v1 = (color & 0xFF00) >> 8; - color = (color & ~0xFFFF) | _colorTable[_GNW_wcolor[v1]]; + int colorIndex = (color & 0xFF) - 1; + color = (color & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]]; } if (color < 256) { diff --git a/src/window_manager_private.cc b/src/window_manager_private.cc index af94de7..bd0b3ff 100644 --- a/src/window_manager_private.cc +++ b/src/window_manager_private.cc @@ -645,9 +645,8 @@ int _win_msg(const char* string, int x, int y, int color) int textColor; if ((color & 0xFF00) != 0) { - int index = (color & 0xFF) - 1; - textColor = _colorTable[_GNW_wcolor[index]]; - textColor |= color & ~0xFFFF; + int colorIndex = (color & 0xFF) - 1; + textColor = (color & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]]; } else { textColor = color; } From 2d8a637f7dd1d3e27c335b1801ca63f1e727f04f Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Wed, 19 Jul 2023 12:05:39 +0300 Subject: [PATCH 027/136] Force debug window font --- src/window_manager_private.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/window_manager_private.cc b/src/window_manager_private.cc index bd0b3ff..1076a3d 100644 --- a/src/window_manager_private.cc +++ b/src/window_manager_private.cc @@ -722,6 +722,12 @@ int _win_debug(char* string) return -1; } + // CE: Debug window metrics were designed for default DOS-style font (0). + // We don't expect caller to properly set one, so without manually forcing + // it debug window might contain mixed fonts. + int oldFont = fontGetCurrent(); + fontSetCurrent(0); + int lineHeight = fontGetLineHeight(); if (_wd == -1) { @@ -828,6 +834,9 @@ int _win_debug(char* string) windowRefresh(_wd); + // CE: Restore font. + fontSetCurrent(oldFont); + return 0; } From ce5d07e09c70e816a6ec2400013f3058f7386cf7 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Wed, 19 Jul 2023 21:27:20 +0300 Subject: [PATCH 028/136] Add win_yes_no --- src/window_manager_private.cc | 81 +++++++++++++++++++++++++++++++++++ src/window_manager_private.h | 1 + 2 files changed, 82 insertions(+) diff --git a/src/window_manager_private.cc b/src/window_manager_private.cc index 1076a3d..2f3ae58 100644 --- a/src/window_manager_private.cc +++ b/src/window_manager_private.cc @@ -617,6 +617,87 @@ int _win_get_str(char* dest, int length, const char* title, int x, int y) return 0; } +// 0x4DB920 +int win_yes_no(const char* question, int x, int y, int color) +{ + if (!gWindowSystemInitialized) { + return -1; + } + + int height = 3 * fontGetLineHeight() + 16; + int width = std::max(fontGetStringWidth(question) + 16, 144) + 16; + + int win = windowCreate(x, y, width, height, 0x100, WINDOW_MODAL | WINDOW_MOVE_ON_TOP); + if (win == -1) { + return -1; + } + + windowDrawBorder(win); + + unsigned char* windowBuffer = windowGetWindow(win)->buffer; + + int textColor; + if ((color & 0xFF00) != 0) { + int colorIndex = (color & 0xFF) - 1; + textColor = (color & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]]; + } else { + textColor = color; + } + + fontDrawText(windowBuffer + 8 * width + 16, + question, + width, + width, + textColor); + + _win_register_text_button(win, + width / 2 - 64, + height - fontGetLineHeight() - 14, + -1, + -1, + -1, + 'y', + "Yes", + 0); + _win_register_text_button(win, + width / 2 + 16, + height - fontGetLineHeight() - 14, + -1, + -1, + -1, + 'n', + "No", + 0); + windowRefresh(win); + + // NOTE: Original code is slightly different but does the same thing. + int rc = -1; + while (rc == -1) { + sharedFpsLimiter.mark(); + + switch (inputGetInput()) { + case KEY_ESCAPE: + case 'n': + case 'N': + rc = 0; + break; + case KEY_RETURN: + case KEY_SPACE: + case 'y': + case 'Y': + rc = 1; + break; + } + + renderPresent(); + sharedFpsLimiter.throttle(); + } + + windowDestroy(win); + + return rc; +} + // 0x4DBA98 int _win_msg(const char* string, int x, int y, int color) { diff --git a/src/window_manager_private.h b/src/window_manager_private.h index 449a2ed..bbef908 100644 --- a/src/window_manager_private.h +++ b/src/window_manager_private.h @@ -16,6 +16,7 @@ extern char gProgramWindowTitle[256]; int _win_list_select(const char* title, char** fileList, int fileListLength, ListSelectionHandler* callback, int x, int y, int color); int _win_list_select_at(const char* title, char** items, int itemsLength, ListSelectionHandler* callback, int x, int y, int color, int start); int _win_get_str(char* dest, int length, const char* title, int x, int y); +int win_yes_no(const char* question, int x, int y, int color); int _win_msg(const char* string, int x, int y, int color); int _win_pull_down(char** items, int itemsLength, int x, int y, int color); int _create_pull_down(char** stringList, int stringListLength, int x, int y, int foregroundColor, int backgroundColor, Rect* rect); From 1506ae7048715b61997340cd7f6fd72827e6f275 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 20 Jul 2023 08:53:26 +0300 Subject: [PATCH 029/136] Add win_timed_msg --- src/window_manager_private.cc | 67 ++++++++++++++++++++++++++++++++++- src/window_manager_private.h | 3 +- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/window_manager_private.cc b/src/window_manager_private.cc index 2f3ae58..f844259 100644 --- a/src/window_manager_private.cc +++ b/src/window_manager_private.cc @@ -1316,6 +1316,71 @@ void _GNW_intr_exit() } } +// 0x4DD4C8 +int win_timed_msg(const char* msg, int color) +{ + if (!gWindowSystemInitialized) { + return -1; + } + + if (_tm_add == _tm_kill) { + return -1; + } + + int width = fontGetStringWidth(msg) + 2 * _tm_text_x; + int x = _scr_center_x - width / 2; + int height = _tm_h; + int y; + + int index; + for (index = 0; index < 5; index++) { + if (!_tm_location[index].field_0) { + _tm_location[index].field_0 = true; + y = _tm_location[index].field_4; + break; + } + } + + if (index == 5) { + return -1; + } + + int win = windowCreate(x, y, width, height, 0x100 | 1, WINDOW_MOVE_ON_TOP); + windowDrawBorder(win); + windowDrawText(win, msg, 0, _tm_text_x, _tm_text_y, color); + + int btn = buttonCreate(win, + 0, + 0, + width, + height, + -1, + -1, + -1, + -1, + NULL, + NULL, + NULL, + 0); + buttonSetMouseCallbacks(btn, NULL, NULL, NULL, _tm_click_response); + + windowRefresh(win); + + _tm_queue[_tm_add].field_4 = win; + _tm_queue[_tm_add].field_0 = getTicks(); + _tm_queue[_tm_add].field_8 = index; + + _tm_add++; + if (_tm_add == 5) { + _tm_add = 0; + } else if (_tm_kill == -1) { + _tm_kill = 0; + tickersAdd(_tm_watch_msgs); + } + + return 0; +} + // 0x4DD66C void _tm_watch_msgs() { @@ -1402,7 +1467,7 @@ void _tm_kill_out_of_order(int queueIndex) } // 0x4DD82C -void _tm_click_response(int btn) +void _tm_click_response(int btn, int keyCode) { int win; int queueIndex; diff --git a/src/window_manager_private.h b/src/window_manager_private.h index bbef908..d337040 100644 --- a/src/window_manager_private.h +++ b/src/window_manager_private.h @@ -33,10 +33,11 @@ int _GNW_process_menu(MenuBar* menuBar, int pulldownIndex); size_t _calc_max_field_chars_wcursor(int value1, int value2); void _GNW_intr_init(); void _GNW_intr_exit(); +int win_timed_msg(const char* msg, int color); void _tm_watch_msgs(); void _tm_kill_msg(); void _tm_kill_out_of_order(int queueIndex); -void _tm_click_response(int btn); +void _tm_click_response(int btn, int keyCode); int _tm_index_active(int queueIndex); } // namespace fallout From dde3255b07c8f925c48e831c0e6e687b491485c8 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 20 Jul 2023 09:21:26 +0300 Subject: [PATCH 030/136] Fix endless loop while cleaning up timed msgs --- src/window_manager_private.cc | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/window_manager_private.cc b/src/window_manager_private.cc index f844259..a9c230c 100644 --- a/src/window_manager_private.cc +++ b/src/window_manager_private.cc @@ -1402,26 +1402,21 @@ void _tm_watch_msgs() // 0x4DD6C0 void _tm_kill_msg() { - int v0; - - v0 = _tm_kill; - if (v0 != -1) { + if (_tm_kill != -1) { windowDestroy(_tm_queue[_tm_kill].field_4); _tm_location[_tm_queue[_tm_kill].field_8].field_0 = 0; - if (v0 == 5) { - v0 = 0; + _tm_kill++; + if (_tm_kill == 5) { + _tm_kill = 0; } - if (v0 == _tm_add) { + if (_tm_kill == _tm_add) { _tm_add = 0; _tm_kill = -1; tickersRemove(_tm_watch_msgs); - v0 = _tm_kill; } } - - _tm_kill = v0; } // 0x4DD744 From d9a173678a0cf495a0177e52309a3fdabd95ff62 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 20 Jul 2023 09:39:15 +0300 Subject: [PATCH 031/136] Cleanup timed msgs code --- src/window_manager_private.cc | 247 ++++++++++++++++++---------------- src/window_manager_private.h | 5 - 2 files changed, 128 insertions(+), 124 deletions(-) diff --git a/src/window_manager_private.cc b/src/window_manager_private.cc index a9c230c..ca11d9b 100644 --- a/src/window_manager_private.cc +++ b/src/window_manager_private.cc @@ -17,17 +17,14 @@ namespace fallout { -typedef struct STRUCT_6B2340 { - int field_0; - int field_4; -} STRUCT_6B2340; +/// Maximum number of timed messages. +static constexpr int kTimedMsgs = 5; -typedef struct STRUCT_6B2370 { - int field_0; - // win - int field_4; - int field_8; -} STRUCT_6B2370; +static void tm_watch_msgs(); +static void tm_kill_msg(); +static void tm_kill_out_of_order(int queueIndex); +static void tm_click_response(int btn, int keyCode); +static bool tm_index_active(int queueIndex); // 0x51E414 static int _wd = -1; @@ -36,34 +33,45 @@ static int _wd = -1; static MenuBar* _curr_menu = NULL; // 0x51E41C -static bool _tm_watch_active = false; +static bool tm_watch_active = false; +// NOTE: Also anonymous in the original code. +// // 0x6B2340 -static STRUCT_6B2340 _tm_location[5]; +static struct { + bool taken; + int y; +} tm_location[kTimedMsgs]; // 0x6B2368 -static int _tm_text_x; +static int tm_text_x; // 0x6B236C -static int _tm_h; +static int tm_h; +// NOTE: Also anonymous in the original code. +// // 0x6B2370 -static STRUCT_6B2370 _tm_queue[5]; +static struct { + unsigned int created; + int id; + int location; +} tm_queue[kTimedMsgs]; // 0x6B23AC -static unsigned int _tm_persistence; +static unsigned int tm_persistence; // 0x6B23B0 -static int _scr_center_x; +static int scr_center_x; // 0x6B23B4 -static int _tm_text_y; +static int tm_text_y; // 0x6B23B8 -static int _tm_kill; +static int tm_kill; // 0x6B23BC -static int _tm_add; +static int tm_add; // x // @@ -1280,39 +1288,40 @@ size_t _calc_max_field_chars_wcursor(int value1, int value2) // 0x4DD3EC void _GNW_intr_init() { - int v1, v2; - int i; + int spacing; + int top; + int index; - _tm_persistence = 3000; - _tm_add = 0; - _tm_kill = -1; - _scr_center_x = _scr_size.right / 2; + tm_persistence = 3000; + tm_add = 0; + tm_kill = -1; + scr_center_x = _scr_size.right / 2; if (_scr_size.bottom >= 479) { - _tm_text_y = 16; - _tm_text_x = 16; + tm_text_y = 16; + tm_text_x = 16; } else { - _tm_text_y = 10; - _tm_text_x = 10; + tm_text_y = 10; + tm_text_x = 10; } - _tm_h = 2 * _tm_text_y + fontGetLineHeight(); + tm_h = 2 * tm_text_y + fontGetLineHeight(); - v1 = _scr_size.bottom >> 3; - v2 = _scr_size.bottom >> 2; + spacing = _scr_size.bottom / 8; + top = _scr_size.bottom / 4; - for (i = 0; i < 5; i++) { - _tm_location[i].field_4 = v1 * i + v2; - _tm_location[i].field_0 = 0; + for (index = 0; index < kTimedMsgs; index++) { + tm_location[index].y = spacing * index + top; + tm_location[index].taken = false; } } // 0x4DD4A4 void _GNW_intr_exit() { - tickersRemove(_tm_watch_msgs); - while (_tm_kill != -1) { - _tm_kill_msg(); + tickersRemove(tm_watch_msgs); + while (tm_kill != -1) { + tm_kill_msg(); } } @@ -1323,31 +1332,31 @@ int win_timed_msg(const char* msg, int color) return -1; } - if (_tm_add == _tm_kill) { + if (tm_add == tm_kill) { return -1; } - int width = fontGetStringWidth(msg) + 2 * _tm_text_x; - int x = _scr_center_x - width / 2; - int height = _tm_h; + int width = fontGetStringWidth(msg) + 2 * tm_text_x; + int x = scr_center_x - width / 2; + int height = tm_h; int y; int index; - for (index = 0; index < 5; index++) { - if (!_tm_location[index].field_0) { - _tm_location[index].field_0 = true; - y = _tm_location[index].field_4; + for (index = 0; index < kTimedMsgs; index++) { + if (!tm_location[index].taken) { + tm_location[index].taken = true; + y = tm_location[index].y; break; } } - if (index == 5) { + if (index == kTimedMsgs) { return -1; } int win = windowCreate(x, y, width, height, 0x100 | 1, WINDOW_MOVE_ON_TOP); windowDrawBorder(win); - windowDrawText(win, msg, 0, _tm_text_x, _tm_text_y, color); + windowDrawText(win, msg, 0, tm_text_x, tm_text_y, color); int btn = buttonCreate(win, 0, @@ -1362,142 +1371,142 @@ int win_timed_msg(const char* msg, int color) NULL, NULL, 0); - buttonSetMouseCallbacks(btn, NULL, NULL, NULL, _tm_click_response); + buttonSetMouseCallbacks(btn, NULL, NULL, NULL, tm_click_response); windowRefresh(win); - _tm_queue[_tm_add].field_4 = win; - _tm_queue[_tm_add].field_0 = getTicks(); - _tm_queue[_tm_add].field_8 = index; + tm_queue[tm_add].id = win; + tm_queue[tm_add].created = getTicks(); + tm_queue[tm_add].location = index; - _tm_add++; - if (_tm_add == 5) { - _tm_add = 0; - } else if (_tm_kill == -1) { - _tm_kill = 0; - tickersAdd(_tm_watch_msgs); + tm_add++; + if (tm_add == kTimedMsgs) { + tm_add = 0; + } else if (tm_kill == -1) { + tm_kill = 0; + tickersAdd(tm_watch_msgs); } return 0; } // 0x4DD66C -void _tm_watch_msgs() +void tm_watch_msgs() { - if (_tm_watch_active) { + if (tm_watch_active) { return; } - _tm_watch_active = 1; - while (_tm_kill != -1) { - if (getTicksSince(_tm_queue[_tm_kill].field_0) < _tm_persistence) { + tm_watch_active = true; + while (tm_kill != -1) { + if (getTicksSince(tm_queue[tm_kill].created) < tm_persistence) { break; } - _tm_kill_msg(); + tm_kill_msg(); } - _tm_watch_active = 0; + tm_watch_active = false; } // 0x4DD6C0 -void _tm_kill_msg() +void tm_kill_msg() { - if (_tm_kill != -1) { - windowDestroy(_tm_queue[_tm_kill].field_4); - _tm_location[_tm_queue[_tm_kill].field_8].field_0 = 0; + if (tm_kill != -1) { + windowDestroy(tm_queue[tm_kill].id); + tm_location[tm_queue[tm_kill].location].taken = false; - _tm_kill++; - if (_tm_kill == 5) { - _tm_kill = 0; + tm_kill++; + if (tm_kill == kTimedMsgs) { + tm_kill = 0; } - if (_tm_kill == _tm_add) { - _tm_add = 0; - _tm_kill = -1; - tickersRemove(_tm_watch_msgs); + if (tm_kill == tm_add) { + tm_add = 0; + tm_kill = -1; + tickersRemove(tm_watch_msgs); } } } // 0x4DD744 -void _tm_kill_out_of_order(int queueIndex) +void tm_kill_out_of_order(int queue_index) { - int v7; - int v6; + int copy_from; + int copy_to; - if (_tm_kill == -1) { + if (tm_kill == -1) { return; } - if (!_tm_index_active(queueIndex)) { + if (!tm_index_active(queue_index)) { return; } - windowDestroy(_tm_queue[queueIndex].field_4); + windowDestroy(tm_queue[queue_index].id); - _tm_location[_tm_queue[queueIndex].field_8].field_0 = 0; + tm_location[tm_queue[queue_index].location].taken = false; - if (queueIndex != _tm_kill) { - v6 = queueIndex; - do { - v7 = v6 - 1; - if (v7 < 0) { - v7 = 4; - } + copy_to = queue_index; + while (copy_to != tm_kill) { + copy_from = copy_to - 1; + if (copy_from < 0) { + copy_from = kTimedMsgs - 1; + } - memcpy(&(_tm_queue[v6]), &(_tm_queue[v7]), sizeof(STRUCT_6B2370)); - v6 = v7; - } while (v7 != _tm_kill); + tm_queue[copy_to] = tm_queue[copy_from]; + copy_to = copy_from; } - if (++_tm_kill == 5) { - _tm_kill = 0; + tm_kill++; + if (tm_kill == kTimedMsgs) { + tm_kill = 0; } - if (_tm_add == _tm_kill) { - _tm_add = 0; - _tm_kill = -1; - tickersRemove(_tm_watch_msgs); + if (tm_add == tm_kill) { + tm_add = 0; + tm_kill = -1; + tickersRemove(tm_watch_msgs); } } // 0x4DD82C -void _tm_click_response(int btn, int keyCode) +void tm_click_response(int btn, int keyCode) { int win; - int queueIndex; + int queue_index; - if (_tm_kill == -1) { + if (tm_kill == -1) { return; } win = buttonGetWindowId(btn); - queueIndex = _tm_kill; - while (win != _tm_queue[queueIndex].field_4) { - queueIndex++; - if (queueIndex == 5) { - queueIndex = 0; + queue_index = tm_kill; + while (win != tm_queue[queue_index].id) { + queue_index++; + if (queue_index == kTimedMsgs) { + queue_index = 0; } - if (queueIndex == _tm_kill || !_tm_index_active(queueIndex)) + if (queue_index == tm_kill || !tm_index_active(queue_index)) { return; + } } - _tm_kill_out_of_order(queueIndex); + tm_kill_out_of_order(queue_index); } // 0x4DD870 -int _tm_index_active(int queueIndex) +bool tm_index_active(int queue_index) { - if (_tm_kill != _tm_add) { - if (_tm_kill >= _tm_add) { - if (queueIndex >= _tm_add && queueIndex < _tm_kill) - return 0; - } else if (queueIndex < _tm_kill || queueIndex >= _tm_add) { - return 0; - } + if (tm_kill == tm_add) { + return true; } - return 1; + + if (tm_kill < tm_add) { + return queue_index >= tm_kill && queue_index < tm_add; + } + + return queue_index < tm_add || queue_index >= tm_kill; } } // namespace fallout diff --git a/src/window_manager_private.h b/src/window_manager_private.h index d337040..c4e00e4 100644 --- a/src/window_manager_private.h +++ b/src/window_manager_private.h @@ -34,11 +34,6 @@ size_t _calc_max_field_chars_wcursor(int value1, int value2); void _GNW_intr_init(); void _GNW_intr_exit(); int win_timed_msg(const char* msg, int color); -void _tm_watch_msgs(); -void _tm_kill_msg(); -void _tm_kill_out_of_order(int queueIndex); -void _tm_click_response(int btn, int keyCode); -int _tm_index_active(int queueIndex); } // namespace fallout From 867d28ac6259c72a69b4ceac0badbc02f59f6d38 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 20 Jul 2023 10:13:21 +0300 Subject: [PATCH 032/136] Add rect_clip --- src/geometry.cc | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ src/geometry.h | 1 + 2 files changed, 57 insertions(+) diff --git a/src/geometry.cc b/src/geometry.cc index e18eabb..9eb97fb 100644 --- a/src/geometry.cc +++ b/src/geometry.cc @@ -107,6 +107,62 @@ void _rect_clip_list(RectListNode** rectListNodePtr, Rect* rect) } } +// 0x4C6AAC +RectListNode* rect_clip(Rect* b, Rect* t) +{ + RectListNode* list = NULL; + Rect clipped_t; + + if (rectIntersection(t, b, &clipped_t) == 0) { + RectListNode** next = &list; + Rect clipped_b[4]; + int k; + + clipped_b[0].left = b->left; + clipped_b[0].top = b->top; + clipped_b[0].right = b->right; + clipped_b[0].bottom = clipped_t.top - 1; + + clipped_b[1].left = b->left; + clipped_b[1].top = clipped_t.top; + clipped_b[1].right = clipped_t.left - 1; + clipped_b[1].bottom = clipped_t.bottom; + + clipped_b[2].left = clipped_t.right + 1; + clipped_b[2].top = clipped_t.top; + clipped_b[2].right = b->right; + clipped_b[2].bottom = clipped_t.bottom; + + clipped_b[3].left = b->left; + clipped_b[3].top = clipped_t.bottom + 1; + clipped_b[3].right = b->right; + clipped_b[3].bottom = b->bottom; + + for (k = 0; k < 4; k++) { + if (clipped_b[k].left <= clipped_b[k].right && clipped_b[k].top <= clipped_b[k].bottom) { + *next = _rect_malloc(); + if (*next == NULL) { + return NULL; + } + + (*next)->rect = clipped_b[k]; + (*next)->next = NULL; + next = &((*next)->next); + } + } + } else { + list = _rect_malloc(); + if (list == NULL) { + return NULL; + } + + list->rect = *b; + list->next = NULL; + } + + return list; +} + // 0x4C6BB8 RectListNode* _rect_malloc() { diff --git a/src/geometry.h b/src/geometry.h index da322fe..095258c 100644 --- a/src/geometry.h +++ b/src/geometry.h @@ -27,6 +27,7 @@ typedef struct RectListNode { void _GNW_rect_exit(); void _rect_clip_list(RectListNode** rectListNodePtr, Rect* rect); +RectListNode* rect_clip(Rect* b, Rect* t); RectListNode* _rect_malloc(); void _rect_free(RectListNode* entry); void rectUnion(const Rect* s1, const Rect* s2, Rect* r); From b07a7bcefdb0d2008d64719eb842052cb023fd8e Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 20 Jul 2023 10:20:05 +0300 Subject: [PATCH 033/136] Complete win_drag --- src/window_manager.cc | 76 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 8 deletions(-) diff --git a/src/window_manager.cc b/src/window_manager.cc index ba460c0..d9ac144 100644 --- a/src/window_manager.cc +++ b/src/window_manager.cc @@ -32,7 +32,7 @@ static void windowFree(int win); static void _win_buffering(bool a1); static void _win_move(int win_index, int x, int y); static void _win_clip(Window* window, RectListNode** rect, unsigned char* a3); -static void _win_drag(int win); +static void win_drag(int win); static void _refresh_all(Rect* rect, unsigned char* a2); static Button* buttonGetButton(int btn, Window** out_win); static int paletteOpenFileImpl(const char* path, int flags); @@ -1000,10 +1000,13 @@ void _win_clip(Window* window, RectListNode** rectListNodePtr, unsigned char* a3 } // 0x4D765C -void _win_drag(int win) +void win_drag(int win) { - // TODO: Probably somehow related to self-run functionality, skip for now. Window* window = windowGetWindow(win); + int dx = 0; + int dy = 0; + int mx; + int my; if (!gWindowSystemInitialized) { return; @@ -1015,8 +1018,7 @@ void _win_drag(int win) windowShow(win); - Rect rect; - rectCopy(&rect, &(window->rect)); + Rect rect = window->rect; tickersExecute(); @@ -1024,7 +1026,65 @@ void _win_drag(int win) _mouse_info(); } - if ((window->flags & WINDOW_MANAGED) && (window->rect.left & 3)) { + while ((mouseGetEvent() & MOUSE_EVENT_ANY_BUTTON_UP) == 0) { + sharedFpsLimiter.mark(); + + if (dx != 0 || dy != 0) { + window->rect.left += dx; + window->rect.top += dy; + window->rect.right += dx; + window->rect.bottom += dy; + _GNW_win_refresh(window, &(window->rect), NULL); + + RectListNode* rectListNode = rect_clip(&rect, &(window->rect)); + while (rectListNode != NULL) { + RectListNode* next = rectListNode->next; + + // NOTE: Uninline. + windowRefreshAll(&(rectListNode->rect)); + + _rect_free(rectListNode); + rectListNode = next; + } + + rect = window->rect; + } + + mouseGetPosition(&mx, &my); + tickersExecute(); + + if (vcrUpdate() != 3) { + _mouse_info(); + } + + dx = mx; + dy = my; + mouseGetPosition(&mx, &my); + + dx = mx - dx; + dy = my - dy; + + if (dx + window->rect.left < _scr_size.left) { + dx = _scr_size.left - window->rect.left; + } + + if (dx + window->rect.right > _scr_size.right) { + dx = _scr_size.right - window->rect.right; + } + + if (dy + window->rect.top < _scr_size.top) { + dy = _scr_size.top - window->rect.top; + } + + if (dy + window->rect.bottom > _scr_size.bottom) { + dy = _scr_size.bottom - window->rect.bottom; + } + + renderPresent(); + sharedFpsLimiter.throttle(); + } + + if ((window->flags & WINDOW_MANAGED) != 0 && (window->rect.left & 3) != 0) { _win_move(window->id, window->rect.left, window->rect.top); } } @@ -1990,14 +2050,14 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr) if ((button->flags & BUTTON_FLAG_0x10) != 0 && (mouseEvent & MOUSE_EVENT_ANY_BUTTON_DOWN) != 0 && (mouseEvent & MOUSE_EVENT_ANY_BUTTON_REPEAT) == 0) { - _win_drag(window->id); + win_drag(window->id); _button_draw(button, window, button->normalImage, true, NULL, true); } } else if ((window->flags & WINDOW_FLAG_0x80) != 0) { v25 |= mouseEvent << 8; if ((mouseEvent & MOUSE_EVENT_ANY_BUTTON_DOWN) != 0 && (mouseEvent & MOUSE_EVENT_ANY_BUTTON_REPEAT) == 0) { - _win_drag(window->id); + win_drag(window->id); } } From 50d7c71db16a8497058eec69361c80d4e88c7cd6 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 20 Jul 2023 11:13:01 +0300 Subject: [PATCH 034/136] Clarify dragging flags --- src/window_manager.cc | 4 ++-- src/window_manager.h | 10 +++++++--- src/window_manager_private.cc | 4 ++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/window_manager.cc b/src/window_manager.cc index d9ac144..f3d01ce 100644 --- a/src/window_manager.cc +++ b/src/window_manager.cc @@ -2047,13 +2047,13 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr) } if (button != NULL) { - if ((button->flags & BUTTON_FLAG_0x10) != 0 + if ((button->flags & BUTTON_DRAG_HANDLE) != 0 && (mouseEvent & MOUSE_EVENT_ANY_BUTTON_DOWN) != 0 && (mouseEvent & MOUSE_EVENT_ANY_BUTTON_REPEAT) == 0) { win_drag(window->id); _button_draw(button, window, button->normalImage, true, NULL, true); } - } else if ((window->flags & WINDOW_FLAG_0x80) != 0) { + } else if ((window->flags & WINDOW_DRAGGABLE_BY_BACKGROUND) != 0) { v25 |= mouseEvent << 8; if ((mouseEvent & MOUSE_EVENT_ANY_BUTTON_DOWN) != 0 && (mouseEvent & MOUSE_EVENT_ANY_BUTTON_REPEAT) == 0) { diff --git a/src/window_manager.h b/src/window_manager.h index 265aa7e..99180d7 100644 --- a/src/window_manager.h +++ b/src/window_manager.h @@ -42,8 +42,10 @@ typedef enum WindowFlags { WINDOW_MODAL = 0x10, WINDOW_TRANSPARENT = 0x20, WINDOW_FLAG_0x40 = 0x40, - // Draggable? - WINDOW_FLAG_0x80 = 0x80, + + /// Specifies that the window is draggable by clicking and moving anywhere + /// in its background. + WINDOW_DRAGGABLE_BY_BACKGROUND = 0x80, WINDOW_MANAGED = 0x100, } WindowFlags; @@ -52,7 +54,9 @@ typedef enum ButtonFlags { BUTTON_FLAG_0x02 = 0x02, BUTTON_FLAG_0x04 = 0x04, BUTTON_FLAG_DISABLED = 0x08, - BUTTON_FLAG_0x10 = 0x10, + + /// Specifies that the button is a drag handle for parent window. + BUTTON_DRAG_HANDLE = 0x10, BUTTON_FLAG_TRANSPARENT = 0x20, BUTTON_FLAG_0x40 = 0x40, BUTTON_FLAG_GRAPHIC = 0x010000, diff --git a/src/window_manager_private.cc b/src/window_manager_private.cc index ca11d9b..2f4c706 100644 --- a/src/window_manager_private.cc +++ b/src/window_manager_private.cc @@ -322,7 +322,7 @@ int _win_list_select_at(const char* title, char** items, int itemsLength, ListSe NULL, NULL, NULL, - BUTTON_FLAG_0x10); + BUTTON_DRAG_HANDLE); windowRefresh(win); @@ -885,7 +885,7 @@ int _win_debug(char* string) NULL, NULL, NULL, - BUTTON_FLAG_0x10); + BUTTON_DRAG_HANDLE); } char temp[2]; From 769599c463ec62545c5c382b2d6e0e18b7b2ef98 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 21 Jul 2023 08:04:10 +0300 Subject: [PATCH 035/136] Fix closing same file twice Closes #318 --- src/loadsave.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/loadsave.cc b/src/loadsave.cc index 645dcab..90f09c3 100644 --- a/src/loadsave.cc +++ b/src/loadsave.cc @@ -2733,7 +2733,7 @@ out: } if (stream2 != NULL) { - fileClose(stream1); + fileClose(stream2); } if (buf != NULL) { From 6dcbcb97ade576f210a2c04ea828be4f68dbddad Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 21 Jul 2023 10:50:33 +0300 Subject: [PATCH 036/136] Add process_pull_down --- src/window_manager_private.cc | 219 +++++++++++++++++++++++++++++++++- 1 file changed, 217 insertions(+), 2 deletions(-) diff --git a/src/window_manager_private.cc b/src/window_manager_private.cc index 2f4c706..2330211 100644 --- a/src/window_manager_private.cc +++ b/src/window_manager_private.cc @@ -1218,8 +1218,223 @@ int _win_input_str(int win, char* dest, int maxLength, int x, int y, int textCol // 0x4DBD04 int process_pull_down(int win, Rect* rect, char** items, int itemsLength, int foregroundColor, int backgroundColor, MenuBar* menuBar, int pulldownIndex) { - // TODO: Incomplete. - return -1; + if (menuBar != NULL) { + unsigned char* parentWindowBuffer = windowGetWindow(menuBar->win)->buffer; + MenuPulldown* pulldown = &(menuBar->pulldowns[pulldownIndex]); + + int x = pulldown->rect.left; + int y = pulldown->rect.top; + int width = pulldown->rect.right - x + 1; + int height = pulldown->rect.bottom - y + 1; + + int color1 = menuBar->foregroundColor; + if ((color1 & 0xFF00) != 0) { + int colorIndex = (color1 & 0xFF) - 1; + color1 = (color1 & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]]; + } + + int color2 = menuBar->backgroundColor; + if ((color2 & 0xFF00) != 0) { + int colorIndex = (color2 & 0xFF) - 1; + color2 = (color2 & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]]; + } + + _swap_color_buf(parentWindowBuffer + width * y + x, + width, + height, + windowGetWidth(menuBar->win), + color1, + color2); + windowRefreshRect(menuBar->win, &(pulldown->rect)); + } + + unsigned char* windowBuffer = windowGetWindow(win)->buffer; + int width = rectGetWidth(rect); + int height = rectGetHeight(rect); + + int focusedIndex = -1; + int rc; + int mx1; + int my1; + int mx2; + int my2; + int input; + + mouseGetPosition(&mx1, &my1); + + while (1) { + sharedFpsLimiter.mark(); + + input = inputGetInput(); + if (input != -1) { + break; + } + + mouseGetPosition(&mx2, &my2); + + if (mx2 < mx1 - 4 + || mx2 > mx1 + 4 + || my2 < my1 - 4 + || my2 > my1 + 4) { + break; + } + + renderPresent(); + sharedFpsLimiter.throttle(); + } + + while (1) { + sharedFpsLimiter.mark(); + + mouseGetPosition(&mx2, &my2); + + if (input == -2) { + if (menuBar != NULL) { + if (_mouse_click_in(menuBar->rect.left, menuBar->rect.top, menuBar->rect.right, menuBar->rect.bottom)) { + int index; + for (index = 0; index < menuBar->pulldownsLength; index++) { + MenuPulldown* pulldown = &(menuBar->pulldowns[index]); + if (_mouse_click_in(pulldown->rect.left, pulldown->rect.top, pulldown->rect.right, pulldown->rect.bottom)) { + break; + } + } + + if (index < menuBar->pulldownsLength && index != pulldownIndex) { + rc = -2 - index; + break; + } + } + } + } + + if ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_UP) != 0 + || ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_DOWN) != 0 + && (mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_REPEAT) == 0)) { + if (_mouse_click_in(rect->left, rect->top + 8, rect->right, rect->bottom - 9)) { + rc = focusedIndex; + } else { + rc = -1; + } + break; + } + + bool done = false; + switch (input) { + case KEY_ESCAPE: + rc = -1; + done = true; + break; + case KEY_RETURN: + rc = focusedIndex; + done = true; + break; + case KEY_ARROW_LEFT: + if (menuBar != NULL && pulldownIndex > 0) { + rc = -2 - (pulldownIndex - 1); + done = true; + } + break; + case KEY_ARROW_RIGHT: + if (menuBar != NULL && pulldownIndex < menuBar->pulldownsLength - 1) { + rc = -2 - (pulldownIndex + 1); + done = true; + } + break; + case KEY_ARROW_UP: + while (focusedIndex > 0) { + focusedIndex--; + + if (items[focusedIndex][0] != '\0') { + break; + } + } + input = -3; + break; + case KEY_ARROW_DOWN: + while (focusedIndex < itemsLength - 1) { + focusedIndex++; + + if (items[focusedIndex][0] != '\0') { + break; + } + } + input = -3; + break; + default: + if (mx2 != mx1 || my2 != my1) { + if (_mouse_click_in(rect->left, rect->top + 8, rect->right, rect->bottom - 9)) { + input = (my2 - rect->top - 8) / fontGetLineHeight(); + if (input != -1) { + focusedIndex = items[input][0] != '\0' ? input : -1; + input = -3; + } + } + + mx1 = mx2; + my1 = my2; + } + break; + } + + if (done) { + break; + } + + if (input == -3) { + windowFill(win, 2, 8, width - 4, height - 16, backgroundColor); + _win_text(win, items, itemsLength, width - 4, 2, 8, foregroundColor); + + if (focusedIndex != -1) { + _lighten_buf(windowBuffer + (focusedIndex * fontGetLineHeight() + 8) * width + 2, + width - 4, + fontGetLineHeight(), + width); + } + + windowRefresh(win); + } + + input = inputGetInput(); + + renderPresent(); + sharedFpsLimiter.throttle(); + } + + if (menuBar != NULL) { + unsigned char* parentWindowBuffer = windowGetWindow(menuBar->win)->buffer; + MenuPulldown* pulldown = &(menuBar->pulldowns[pulldownIndex]); + + int x = pulldown->rect.left; + int y = pulldown->rect.top; + int width = pulldown->rect.right - x + 1; + int height = pulldown->rect.bottom - y + 1; + + int color1 = menuBar->foregroundColor; + if ((color1 & 0xFF00) != 0) { + int colorIndex = (color1 & 0xFF) - 1; + color1 = (color1 & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]]; + } + + int color2 = menuBar->backgroundColor; + if ((color2 & 0xFF00) != 0) { + int colorIndex = (color2 & 0xFF) - 1; + color2 = (color2 & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]]; + } + + _swap_color_buf(parentWindowBuffer + width * y + x, + width, + height, + windowGetWidth(menuBar->win), + color1, + color2); + windowRefreshRect(menuBar->win, &(pulldown->rect)); + + renderPresent(); + } + + windowDestroy(win); + + return rc; } // 0x4DC930 From f6cfc1fa3aa5b8471873b1d5db5edf8a76500cb2 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 21 Jul 2023 11:02:54 +0300 Subject: [PATCH 037/136] Clarify GNW_check_menu_bars param --- src/window_manager.cc | 9 ++++----- src/window_manager.h | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/window_manager.cc b/src/window_manager.cc index f3d01ce..f014295 100644 --- a/src/window_manager.cc +++ b/src/window_manager.cc @@ -1257,19 +1257,18 @@ Button* buttonGetButton(int btn, Window** windowPtr) } // 0x4D7A34 -int _GNW_check_menu_bars(int a1) +int _GNW_check_menu_bars(int input) { if (!gWindowSystemInitialized) { return -1; } - int v1 = a1; for (int index = gWindowsLength - 1; index >= 1; index--) { Window* window = gWindows[index]; if (window->menuBar != NULL) { for (int pulldownIndex = 0; pulldownIndex < window->menuBar->pulldownsLength; pulldownIndex++) { - if (v1 == window->menuBar->pulldowns[pulldownIndex].keyCode) { - v1 = _GNW_process_menu(window->menuBar, pulldownIndex); + if (input == window->menuBar->pulldowns[pulldownIndex].keyCode) { + input = _GNW_process_menu(window->menuBar, pulldownIndex); break; } } @@ -1280,7 +1279,7 @@ int _GNW_check_menu_bars(int a1) } } - return v1; + return input; } // 0x4D69DC diff --git a/src/window_manager.h b/src/window_manager.h index 99180d7..2f76563 100644 --- a/src/window_manager.h +++ b/src/window_manager.h @@ -177,7 +177,7 @@ int windowGetWidth(int win); int windowGetHeight(int win); int windowGetRect(int win, Rect* rect); int _win_check_all_buttons(); -int _GNW_check_menu_bars(int a1); +int _GNW_check_menu_bars(int input); void programWindowSetTitle(const char* title); bool showMesageBox(const char* str); int buttonCreate(int win, int x, int y, int width, int height, int mouseEnterEventCode, int mouseExitEventCode, int mouseDownEventCode, int mouseUpEventCode, unsigned char* up, unsigned char* dn, unsigned char* hover, int flags); From 7a45ac72f021fd05d51a0764db22a92d96b4389b Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 21 Jul 2023 11:10:15 +0300 Subject: [PATCH 038/136] Add reading mapper config --- src/game_config.cc | 20 ++++++++++++++++++-- src/game_config.h | 2 +- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/game_config.cc b/src/game_config.cc index dde349d..7063ec7 100644 --- a/src/game_config.cc +++ b/src/game_config.cc @@ -125,10 +125,26 @@ bool gameConfigInit(bool isMapper, int argc, char** argv) char* ch = strrchr(executable, '\\'); if (ch != NULL) { *ch = '\0'; - snprintf(gGameConfigFilePath, sizeof(gGameConfigFilePath), "%s\\%s", executable, GAME_CONFIG_FILE_NAME); + if (isMapper) { + snprintf(gGameConfigFilePath, + sizeof(gGameConfigFilePath), + "%s\\%s", + executable, + MAPPER_CONFIG_FILE_NAME); + } else { + snprintf(gGameConfigFilePath, + sizeof(gGameConfigFilePath), + "%s\\%s", + executable, + GAME_CONFIG_FILE_NAME); + } *ch = '\\'; } else { - strcpy(gGameConfigFilePath, GAME_CONFIG_FILE_NAME); + if (isMapper) { + strcpy(gGameConfigFilePath, MAPPER_CONFIG_FILE_NAME); + } else { + strcpy(gGameConfigFilePath, GAME_CONFIG_FILE_NAME); + } } // Read contents of `fallout2.cfg` into config. The values from the file diff --git a/src/game_config.h b/src/game_config.h index a444762..78a1299 100644 --- a/src/game_config.h +++ b/src/game_config.h @@ -5,8 +5,8 @@ namespace fallout { -// The file name of the main config file. #define GAME_CONFIG_FILE_NAME "fallout2.cfg" +#define MAPPER_CONFIG_FILE_NAME "mapper2.cfg" #define GAME_CONFIG_SYSTEM_KEY "system" #define GAME_CONFIG_PREFERENCES_KEY "preferences" From d27eafe03a46512d4cd2c6c38fe2e65ba0c51aa2 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 21 Jul 2023 11:22:57 +0300 Subject: [PATCH 039/136] Fix win_get_str return code --- src/window_manager_private.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/window_manager_private.cc b/src/window_manager_private.cc index 2330211..beef020 100644 --- a/src/window_manager_private.cc +++ b/src/window_manager_private.cc @@ -612,7 +612,7 @@ int _win_get_str(char* dest, int length, const char* title, int x, int y) windowRefresh(win); - _win_input_str(win, + int rc = _win_input_str(win, dest, length, 16, @@ -622,7 +622,7 @@ int _win_get_str(char* dest, int length, const char* title, int x, int y) windowDestroy(win); - return 0; + return rc; } // 0x4DB920 From ccabbd76888b61f42399d89d500160b089c4f32d Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 22 Jul 2023 14:46:51 +0300 Subject: [PATCH 040/136] Add proto_is_subtype --- src/proto.cc | 17 +++++++++++++++++ src/proto.h | 1 + 2 files changed, 18 insertions(+) diff --git a/src/proto.cc b/src/proto.cc index efb05b0..421939e 100644 --- a/src/proto.cc +++ b/src/proto.cc @@ -828,6 +828,23 @@ int _proto_dude_init(const char* path) return 0; } +// 0x49FEDC +bool proto_is_subtype(Proto* proto, int subtype) +{ + if (subtype == -1) { + return true; + } + + switch (PID_TYPE(proto->pid)) { + case OBJ_TYPE_ITEM: + return proto->item.type == subtype; + case OBJ_TYPE_SCENERY: + return proto->scenery.type == subtype; + } + + return false; +} + // proto_data_member // 0x49FFD8 int protoGetDataMember(int pid, int member, ProtoDataMemberValue* value) diff --git a/src/proto.h b/src/proto.h index d6f13e6..e68140f 100644 --- a/src/proto.h +++ b/src/proto.h @@ -118,6 +118,7 @@ int objectDataWrite(Object* obj, File* stream); int _proto_update_init(Object* obj); int _proto_dude_update_gender(); int _proto_dude_init(const char* path); +bool proto_is_subtype(Proto* proto, int subtype); int protoGetDataMember(int pid, int member, ProtoDataMemberValue* value); int protoInit(); void protoReset(); From a4daffa30e1636df56a4a96947670db95ee379bc Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 22 Jul 2023 14:49:56 +0300 Subject: [PATCH 041/136] Add proto_copy_proto --- src/proto.cc | 28 ++++++++++++++++++++++++++++ src/proto.h | 1 + 2 files changed, 29 insertions(+) diff --git a/src/proto.cc b/src/proto.cc index 421939e..f1d6aea 100644 --- a/src/proto.cc +++ b/src/proto.cc @@ -828,6 +828,34 @@ int _proto_dude_init(const char* path) return 0; } +// 0x49FE74 +int proto_copy_proto(int srcPid, int dstPid) +{ + int srcType; + int dstType; + Proto* src; + Proto* dst; + + srcType = PID_TYPE(srcPid); + dstType = PID_TYPE(dstPid); + if (srcType != dstType) { + return -1; + } + + if (protoGetProto(srcPid, &src) == -1) { + return -1; + } + + if (protoGetProto(dstPid, &dst) == -1) { + return -1; + } + + memcpy(dst, src, _proto_sizes[srcType]); + dst->pid = dstPid; + + return 0; +} + // 0x49FEDC bool proto_is_subtype(Proto* proto, int subtype) { diff --git a/src/proto.h b/src/proto.h index e68140f..9448a09 100644 --- a/src/proto.h +++ b/src/proto.h @@ -118,6 +118,7 @@ int objectDataWrite(Object* obj, File* stream); int _proto_update_init(Object* obj); int _proto_dude_update_gender(); int _proto_dude_init(const char* path); +int proto_copy_proto(int srcPid, int dstPid); bool proto_is_subtype(Proto* proto, int subtype); int protoGetDataMember(int pid, int member, ProtoDataMemberValue* value); int protoInit(); From ffc440c8097f0b34282f0b398b22f8d7867fa773 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 22 Jul 2023 14:53:22 +0300 Subject: [PATCH 042/136] Add proto_misc_init --- src/proto.cc | 19 +++++++++++++++++++ src/proto.h | 1 + 2 files changed, 20 insertions(+) diff --git a/src/proto.cc b/src/proto.cc index f1d6aea..33a505a 100644 --- a/src/proto.cc +++ b/src/proto.cc @@ -828,6 +828,25 @@ int _proto_dude_init(const char* path) return 0; } +// 0x49FDFC +int proto_misc_init(Proto* proto, int a2) +{ + int v1 = a2 & 0xFFFFFF; + + proto->misc.pid = -1; + proto->misc.messageId = 100 * v1; + proto->misc.fid = buildFid(OBJ_TYPE_MISC, v1 - 1, 0, 0, 0); + if (!artExists(proto->misc.fid)) { + proto->misc.fid = buildFid(OBJ_TYPE_MISC, 0, 0, 0, 0); + } + proto->misc.lightDistance = 0; + proto->misc.lightIntensity = 0; + proto->misc.flags = 0; + proto->misc.extendedFlags = 0; + + return 0; +} + // 0x49FE74 int proto_copy_proto(int srcPid, int dstPid) { diff --git a/src/proto.h b/src/proto.h index 9448a09..5573a87 100644 --- a/src/proto.h +++ b/src/proto.h @@ -118,6 +118,7 @@ int objectDataWrite(Object* obj, File* stream); int _proto_update_init(Object* obj); int _proto_dude_update_gender(); int _proto_dude_init(const char* path); +int proto_misc_init(Proto* proto, int a2); int proto_copy_proto(int srcPid, int dstPid); bool proto_is_subtype(Proto* proto, int subtype); int protoGetDataMember(int pid, int member, ProtoDataMemberValue* value); From 1d310eb6266308138e653bd9d3a31cbd4d536f0f Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 22 Jul 2023 14:58:47 +0300 Subject: [PATCH 043/136] Add proto_tile_init --- src/proto.cc | 19 +++++++++++++++++++ src/proto.h | 1 + 2 files changed, 20 insertions(+) diff --git a/src/proto.cc b/src/proto.cc index 33a505a..b6d544f 100644 --- a/src/proto.cc +++ b/src/proto.cc @@ -828,6 +828,25 @@ int _proto_dude_init(const char* path) return 0; } +// 0x49FD84 +int proto_tile_init(Proto* proto, int a2) +{ + int v1 = a2 & 0xFFFFFF; + + proto->tile.pid = -1; + proto->tile.messageId = 100 * v1; + proto->tile.fid = buildFid(OBJ_TYPE_TILE, v1 - 1, 0, 0, 0); + if (!artExists(proto->tile.fid)) { + proto->tile.fid = buildFid(OBJ_TYPE_TILE, 0, 0, 0, 0); + } + proto->tile.flags = 0; + proto->tile.extendedFlags = 0x2000; + proto->tile.sid = -1; + proto->tile.material = 1; + + return 0; +} + // 0x49FDFC int proto_misc_init(Proto* proto, int a2) { diff --git a/src/proto.h b/src/proto.h index 5573a87..e771b8d 100644 --- a/src/proto.h +++ b/src/proto.h @@ -118,6 +118,7 @@ int objectDataWrite(Object* obj, File* stream); int _proto_update_init(Object* obj); int _proto_dude_update_gender(); int _proto_dude_init(const char* path); +int proto_tile_init(Proto* proto, int a2); int proto_misc_init(Proto* proto, int a2); int proto_copy_proto(int srcPid, int dstPid); bool proto_is_subtype(Proto* proto, int subtype); From 6a75ec07e9edd6ca9a3aa47fa5017346a2bb2908 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 22 Jul 2023 14:59:32 +0300 Subject: [PATCH 044/136] Add proto_wall_init --- src/proto.cc | 21 +++++++++++++++++++++ src/proto.h | 1 + 2 files changed, 22 insertions(+) diff --git a/src/proto.cc b/src/proto.cc index b6d544f..cf4af89 100644 --- a/src/proto.cc +++ b/src/proto.cc @@ -828,6 +828,27 @@ int _proto_dude_init(const char* path) return 0; } +// 0x49FCFC +int proto_wall_init(Proto* proto, int a2) +{ + int v1 = a2 & 0xFFFFFF; + + proto->wall.pid = -1; + proto->wall.messageId = 100 * v1; + proto->wall.fid = buildFid(OBJ_TYPE_WALL, v1 - 1, 0, 0, 0); + if (!artExists(proto->wall.fid)) { + proto->wall.fid = buildFid(OBJ_TYPE_WALL, 0, 0, 0, 0); + } + proto->wall.lightDistance = 0; + proto->wall.lightIntensity = 0; + proto->wall.flags = 0; + proto->wall.extendedFlags = 0x2000; + proto->wall.sid = -1; + proto->wall.material = 1; + + return 0; +} + // 0x49FD84 int proto_tile_init(Proto* proto, int a2) { diff --git a/src/proto.h b/src/proto.h index e771b8d..ba4626b 100644 --- a/src/proto.h +++ b/src/proto.h @@ -118,6 +118,7 @@ int objectDataWrite(Object* obj, File* stream); int _proto_update_init(Object* obj); int _proto_dude_update_gender(); int _proto_dude_init(const char* path); +int proto_wall_init(Proto* proto, int a2); int proto_tile_init(Proto* proto, int a2); int proto_misc_init(Proto* proto, int a2); int proto_copy_proto(int srcPid, int dstPid); From 01a68af4a4db88feb1c8235d6467896078a2c4d6 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 22 Jul 2023 15:05:00 +0300 Subject: [PATCH 045/136] Add proto_scenery_subdata_init --- src/proto.cc | 31 +++++++++++++++++++++++++++++++ src/proto.h | 1 + 2 files changed, 32 insertions(+) diff --git a/src/proto.cc b/src/proto.cc index cf4af89..597af60 100644 --- a/src/proto.cc +++ b/src/proto.cc @@ -828,6 +828,37 @@ int _proto_dude_init(const char* path) return 0; } +// 0x49FC74 +int proto_scenery_subdata_init(Proto* proto, int type) +{ + switch (type) { + case SCENERY_TYPE_DOOR: + proto->scenery.data.door.openFlags = 0; + proto->scenery.extendedFlags |= 0x800; + break; + case SCENERY_TYPE_STAIRS: + proto->scenery.data.stairs.field_0 = -1; + proto->scenery.data.stairs.field_4 = -1; + proto->scenery.extendedFlags |= 0x800; + break; + case SCENERY_TYPE_ELEVATOR: + proto->scenery.data.elevator.type = -1; + proto->scenery.data.elevator.level = -1; + proto->scenery.extendedFlags |= 0x800; + break; + case SCENERY_TYPE_LADDER_UP: + proto->scenery.data.ladder.field_0 = -1; + proto->scenery.extendedFlags |= 0x800; + break; + case SCENERY_TYPE_LADDER_DOWN: + proto->scenery.data.ladder.field_0 = -1; + proto->scenery.extendedFlags |= 0x800; + break; + } + + return 0; +} + // 0x49FCFC int proto_wall_init(Proto* proto, int a2) { diff --git a/src/proto.h b/src/proto.h index ba4626b..982af88 100644 --- a/src/proto.h +++ b/src/proto.h @@ -118,6 +118,7 @@ int objectDataWrite(Object* obj, File* stream); int _proto_update_init(Object* obj); int _proto_dude_update_gender(); int _proto_dude_init(const char* path); +int proto_scenery_subdata_init(Proto* proto, int type); int proto_wall_init(Proto* proto, int a2); int proto_tile_init(Proto* proto, int a2); int proto_misc_init(Proto* proto, int a2); From 3d5cb4c3f435fdac4048d2d7f504b7582edf8354 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 22 Jul 2023 15:07:39 +0300 Subject: [PATCH 046/136] Add proto_scenery_init --- src/proto.cc | 24 ++++++++++++++++++++++++ src/proto.h | 1 + 2 files changed, 25 insertions(+) diff --git a/src/proto.cc b/src/proto.cc index 597af60..312aa05 100644 --- a/src/proto.cc +++ b/src/proto.cc @@ -828,6 +828,30 @@ int _proto_dude_init(const char* path) return 0; } +// 0x49FBBC +int proto_scenery_init(Proto* proto, int a2) +{ + int v1 = a2 & 0xFFFFFF; + + proto->scenery.pid = -1; + proto->scenery.messageId = 100 * v1; + proto->scenery.fid = buildFid(OBJ_TYPE_SCENERY, v1 - 1, 0, 0, 0); + if (!artExists(proto->scenery.fid)) { + proto->scenery.fid = buildFid(OBJ_TYPE_SCENERY, 0, 0, 0, 0); + } + proto->scenery.lightDistance = 0; + proto->scenery.lightIntensity = 0; + proto->scenery.flags = 0; + proto->scenery.extendedFlags = 0x2000; + proto->scenery.sid = -1; + proto->scenery.type = SCENERY_TYPE_GENERIC; + proto_scenery_subdata_init(proto, proto->scenery.type); + proto->scenery.field_2C = -1; + proto->scenery.field_34 = '0'; + + return 0; +} + // 0x49FC74 int proto_scenery_subdata_init(Proto* proto, int type) { diff --git a/src/proto.h b/src/proto.h index 982af88..4b09e3b 100644 --- a/src/proto.h +++ b/src/proto.h @@ -118,6 +118,7 @@ int objectDataWrite(Object* obj, File* stream); int _proto_update_init(Object* obj); int _proto_dude_update_gender(); int _proto_dude_init(const char* path); +int proto_scenery_init(Proto* proto, int a2); int proto_scenery_subdata_init(Proto* proto, int type); int proto_wall_init(Proto* proto, int a2); int proto_tile_init(Proto* proto, int a2); From fd9fa802046a3e658073bdadb08e6f060b597716 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 22 Jul 2023 15:22:05 +0300 Subject: [PATCH 047/136] Add proto_item_subdata_init --- src/proto.cc | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/proto.h | 1 + 2 files changed, 84 insertions(+) diff --git a/src/proto.cc b/src/proto.cc index 312aa05..2771b1a 100644 --- a/src/proto.cc +++ b/src/proto.cc @@ -370,6 +370,89 @@ char* protoGetDescription(int pid) return protoGetMessage(pid, PROTOTYPE_MESSAGE_DESCRIPTION); } +// 0x49EBFC +int proto_item_subdata_init(Proto* proto, int type) +{ + int index; + + switch (type) { + case ITEM_TYPE_ARMOR: + proto->item.data.armor.armorClass = 0; + + for (index = 0; index < DAMAGE_TYPE_COUNT; index++) { + proto->item.data.armor.damageResistance[index] = 0; + proto->item.data.armor.damageThreshold[index] = 0; + } + + proto->item.data.armor.perk = -1; + proto->item.data.armor.maleFid = -1; + proto->item.data.armor.femaleFid = -1; + break; + case ITEM_TYPE_CONTAINER: + proto->item.data.container.openFlags = 0; + proto->item.data.container.maxSize = 250; + proto->item.extendedFlags |= 0x800; + break; + case ITEM_TYPE_DRUG: + proto->item.data.drug.stat[0] = STAT_STRENGTH; + proto->item.data.drug.stat[1] = -1; + proto->item.data.drug.stat[2] = -1; + proto->item.data.drug.amount[0] = 0; + proto->item.data.drug.amount[1] = 0; + proto->item.data.drug.amount[2] = 0; + proto->item.data.drug.duration1 = 0; + proto->item.data.drug.amount1[0] = 0; + proto->item.data.drug.amount1[1] = 0; + proto->item.data.drug.amount1[2] = 0; + proto->item.data.drug.duration2 = 0; + proto->item.data.drug.amount2[0] = 0; + proto->item.data.drug.amount2[1] = 0; + proto->item.data.drug.amount2[2] = 0; + proto->item.data.drug.addictionChance = 0; + proto->item.data.drug.withdrawalEffect = 0; + proto->item.data.drug.withdrawalOnset = 0; + proto->item.extendedFlags |= 0x1000; + break; + case ITEM_TYPE_WEAPON: + proto->item.data.weapon.animationCode = 0; + proto->item.data.weapon.minDamage = 0; + proto->item.data.weapon.maxDamage = 0; + proto->item.data.weapon.damageType = 0; + proto->item.data.weapon.maxRange1 = 0; + proto->item.data.weapon.maxRange2 = 0; + proto->item.data.weapon.projectilePid = -1; + proto->item.data.weapon.minStrength = 0; + proto->item.data.weapon.actionPointCost1 = 0; + proto->item.data.weapon.actionPointCost2 = 0; + proto->item.data.weapon.criticalFailureType = 0; + proto->item.data.weapon.perk = -1; + proto->item.data.weapon.rounds = 0; + proto->item.data.weapon.caliber = 0; + proto->item.data.weapon.ammoTypePid = -1; + proto->item.data.weapon.ammoCapacity = 0; + proto->item.data.weapon.soundCode = 0; + break; + case ITEM_TYPE_AMMO: + proto->item.data.ammo.caliber = 0; + proto->item.data.ammo.quantity = 20; + proto->item.data.ammo.armorClassModifier = 0; + proto->item.data.ammo.damageResistanceModifier = 0; + proto->item.data.ammo.damageMultiplier = 1; + proto->item.data.ammo.damageDivisor = 1; + break; + case ITEM_TYPE_MISC: + proto->item.data.misc.powerTypePid = -1; + proto->item.data.misc.powerType = 20; + break; + case ITEM_TYPE_KEY: + proto->item.data.key.keyCode = -1; + proto->item.extendedFlags |= 0x1000; + break; + } + + return 0; +} + // 0x49EDB4 static int _proto_critter_init(Proto* a1, int a2) { diff --git a/src/proto.h b/src/proto.h index 4b09e3b..e96e8f6 100644 --- a/src/proto.h +++ b/src/proto.h @@ -112,6 +112,7 @@ int _proto_action_can_pickup(int pid); char* protoGetMessage(int pid, int message); char* protoGetName(int pid); char* protoGetDescription(int pid); +int proto_item_subdata_init(Proto* proto, int type); void objectDataReset(Object* obj); int objectDataRead(Object* obj, File* stream); int objectDataWrite(Object* obj, File* stream); From 3c7248af5fda7d6ec405f5bc025e0180cc4d3afc Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 22 Jul 2023 15:34:32 +0300 Subject: [PATCH 048/136] Add proto_item_init --- src/proto.cc | 28 ++++++++++++++++++++++++++++ src/proto.h | 1 + 2 files changed, 29 insertions(+) diff --git a/src/proto.cc b/src/proto.cc index 2771b1a..007c580 100644 --- a/src/proto.cc +++ b/src/proto.cc @@ -370,6 +370,34 @@ char* protoGetDescription(int pid) return protoGetMessage(pid, PROTOTYPE_MESSAGE_DESCRIPTION); } +// 0x49EB2C +int proto_item_init(Proto* proto, int a2) +{ + int v1 = a2 & 0xFFFFFF; + + proto->item.pid = -1; + proto->item.messageId = 100 * v1; + proto->item.fid = buildFid(OBJ_TYPE_ITEM, v1 - 1, 0, 0, 0); + if (!artExists(proto->item.fid)) { + proto->item.fid = buildFid(OBJ_TYPE_ITEM, 0, 0, 0, 0); + } + proto->item.lightDistance = 0; + proto->item.lightIntensity = 0; + proto->item.flags = 0xA0000008; + proto->item.extendedFlags = 0xA000; + proto->item.sid = -1; + proto->item.type = ITEM_TYPE_MISC; + proto_item_subdata_init(proto, proto->item.type); + proto->item.material = 1; + proto->item.size = 1; + proto->item.weight = 10; + proto->item.cost = 0; + proto->item.inventoryFid = -1; + proto->item.field_80 = '0'; + + return 0; +} + // 0x49EBFC int proto_item_subdata_init(Proto* proto, int type) { diff --git a/src/proto.h b/src/proto.h index e96e8f6..a9a6126 100644 --- a/src/proto.h +++ b/src/proto.h @@ -112,6 +112,7 @@ int _proto_action_can_pickup(int pid); char* protoGetMessage(int pid, int message); char* protoGetName(int pid); char* protoGetDescription(int pid); +int proto_item_init(Proto* proto, int a2); int proto_item_subdata_init(Proto* proto, int type); void objectDataReset(Object* obj); int objectDataRead(Object* obj, File* stream); From d6c3d74b3f854345e01fec1ed79cdc584d6d43d7 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 22 Jul 2023 15:36:51 +0300 Subject: [PATCH 049/136] Clarify proto_make_path address --- src/proto.cc | 12 ++++++------ src/proto.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/proto.cc b/src/proto.cc index 007c580..234cf5b 100644 --- a/src/proto.cc +++ b/src/proto.cc @@ -187,8 +187,8 @@ static char** _perk_code_strs; // 0x6648BC static char** _critter_stats_list; -// NOTE: Inlined. -void _proto_make_path(char* path, int pid) +// 0x49E270 +void proto_make_path(char* path, int pid) { strcpy(path, _cd_path_base); strcat(path, _proto_path_base); @@ -211,7 +211,7 @@ int _proto_list_str(int pid, char* proto_path) } char path[COMPAT_MAX_PATH]; - _proto_make_path(path, pid); + proto_make_path(path, pid); strcat(path, "\\"); strcat(path, artGetObjectTypeName(PID_TYPE(pid))); strcat(path, ".lst"); @@ -1521,7 +1521,7 @@ static int _proto_header_load() ptr->max_entries_num = 1; char path[COMPAT_MAX_PATH]; - _proto_make_path(path, index << 24); + proto_make_path(path, index << 24); strcat(path, "\\"); strcat(path, artGetObjectTypeName(index)); strcat(path, ".lst"); @@ -1931,7 +1931,7 @@ int _proto_save_pid(int pid) } char path[260]; - _proto_make_path(path, pid); + proto_make_path(path, pid); strcat(path, "\\"); _proto_list_str(pid, path + strlen(path)); @@ -1952,7 +1952,7 @@ int _proto_save_pid(int pid) static int _proto_load_pid(int pid, Proto** protoPtr) { char path[COMPAT_MAX_PATH]; - _proto_make_path(path, pid); + proto_make_path(path, pid); strcat(path, "\\"); if (_proto_list_str(pid, path + strlen(path)) == -1) { diff --git a/src/proto.h b/src/proto.h index a9a6126..61c7575 100644 --- a/src/proto.h +++ b/src/proto.h @@ -102,7 +102,7 @@ extern char _cd_path_base[COMPAT_MAX_PATH]; extern MessageList gProtoMessageList; extern char* _proto_none_str; -void _proto_make_path(char* path, int pid); +void proto_make_path(char* path, int pid); int _proto_list_str(int pid, char* proto_path); size_t proto_size(int type); bool _proto_action_can_use(int pid); From c72d8778bac317635b791b8695ee53480ad4b29b Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 22 Jul 2023 16:53:01 +0300 Subject: [PATCH 050/136] Add proto_new --- src/proto.cc | 42 ++++++++++++++++++++++++++++++++++++++++++ src/proto.h | 1 + 2 files changed, 43 insertions(+) diff --git a/src/proto.cc b/src/proto.cc index 234cf5b..7bc7004 100644 --- a/src/proto.cc +++ b/src/proto.cc @@ -2031,6 +2031,48 @@ static int _proto_find_free_subnode(int type, Proto** protoPtr) return 0; } +// 0x4A1E90 +int proto_new(int* pid, int type) +{ + Proto* proto; + + if (_proto_find_free_subnode(type, &proto) == -1) { + return -1; + } + + *pid = _proto_new_id(type) | (type << 24); + switch (type) { + case OBJ_TYPE_ITEM: + proto_item_init(proto, *pid); + proto->item.pid = *pid; + break; + case OBJ_TYPE_CRITTER: + _proto_critter_init(proto, *pid); + proto->critter.pid = *pid; + break; + case OBJ_TYPE_SCENERY: + proto_scenery_init(proto, *pid); + proto->scenery.pid = *pid; + break; + case OBJ_TYPE_WALL: + proto_wall_init(proto, *pid); + proto->wall.pid = *pid; + break; + case OBJ_TYPE_TILE: + proto_tile_init(proto, *pid); + proto->tile.pid = *pid; + break; + case OBJ_TYPE_MISC: + proto_misc_init(proto, *pid); + proto->misc.pid = *pid; + break; + default: + return -1; + } + + return 0; +} + // Evict top most proto cache block. // // 0x4A2040 diff --git a/src/proto.h b/src/proto.h index 61c7575..99c9d7e 100644 --- a/src/proto.h +++ b/src/proto.h @@ -132,6 +132,7 @@ int protoInit(); void protoReset(); void protoExit(); int _proto_save_pid(int pid); +int proto_new(int* pid, int type); void _proto_remove_all(); int protoGetProto(int pid, Proto** out_proto); int _ResetPlayer(); From cbb9cbc6fd2f2cfddf737c2f8ddc12bf58d615bd Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 22 Jul 2023 17:00:24 +0300 Subject: [PATCH 051/136] Clarify some proto params --- src/proto.cc | 87 ++++++++++++++++++++++++++-------------------------- src/proto.h | 13 ++++---- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/src/proto.cc b/src/proto.cc index 7bc7004..001217f 100644 --- a/src/proto.cc +++ b/src/proto.cc @@ -24,7 +24,6 @@ namespace fallout { -static int _proto_critter_init(Proto* a1, int a2); static int objectCritterCombatDataRead(CritterCombatData* data, File* stream); static int objectCritterCombatDataWrite(CritterCombatData* data, File* stream); static int _proto_update_gen(Object* obj); @@ -39,8 +38,8 @@ static int _proto_load_pid(int pid, Proto** out_proto); static int _proto_find_free_subnode(int type, Proto** out_ptr); static void _proto_remove_some_list(int type); static void _proto_remove_list(int type); -static int _proto_new_id(int a1); -static int _proto_max_id(int a1); +static int _proto_new_id(int type); +static int _proto_max_id(int type); // 0x50CF3C static char _aProto_0[] = "proto\\"; @@ -482,31 +481,31 @@ int proto_item_subdata_init(Proto* proto, int type) } // 0x49EDB4 -static int _proto_critter_init(Proto* a1, int a2) +int proto_critter_init(Proto* proto, int pid) { if (!_protos_been_initialized) { return -1; } - int v1 = a2 & 0xFFFFFF; + int num = pid & 0xFFFFFF; - a1->pid = -1; - a1->messageId = 100 * v1; - a1->fid = buildFid(OBJ_TYPE_CRITTER, v1 - 1, 0, 0, 0); - a1->critter.lightDistance = 0; - a1->critter.lightIntensity = 0; - a1->critter.flags = 0x20000000; - a1->critter.extendedFlags = 0x6000; - a1->critter.sid = -1; - a1->critter.data.flags = 0; - a1->critter.data.bodyType = 0; - a1->critter.headFid = -1; - a1->critter.aiPacket = 1; - if (!artExists(a1->fid)) { - a1->fid = buildFid(OBJ_TYPE_CRITTER, 0, 0, 0, 0); + proto->pid = -1; + proto->messageId = 100 * num; + proto->fid = buildFid(OBJ_TYPE_CRITTER, num - 1, 0, 0, 0); + proto->critter.lightDistance = 0; + proto->critter.lightIntensity = 0; + proto->critter.flags = 0x20000000; + proto->critter.extendedFlags = 0x6000; + proto->critter.sid = -1; + proto->critter.data.flags = 0; + proto->critter.data.bodyType = 0; + proto->critter.headFid = -1; + proto->critter.aiPacket = 1; + if (!artExists(proto->fid)) { + proto->fid = buildFid(OBJ_TYPE_CRITTER, 0, 0, 0, 0); } - CritterProtoData* data = &(a1->critter.data); + CritterProtoData* data = &(proto->critter.data); data->experience = 60; data->killType = 0; data->damageType = 0; @@ -940,13 +939,13 @@ int _proto_dude_init(const char* path) } // 0x49FBBC -int proto_scenery_init(Proto* proto, int a2) +int proto_scenery_init(Proto* proto, int pid) { - int v1 = a2 & 0xFFFFFF; + int num = pid & 0xFFFFFF; proto->scenery.pid = -1; - proto->scenery.messageId = 100 * v1; - proto->scenery.fid = buildFid(OBJ_TYPE_SCENERY, v1 - 1, 0, 0, 0); + proto->scenery.messageId = 100 * num; + proto->scenery.fid = buildFid(OBJ_TYPE_SCENERY, num - 1, 0, 0, 0); if (!artExists(proto->scenery.fid)) { proto->scenery.fid = buildFid(OBJ_TYPE_SCENERY, 0, 0, 0, 0); } @@ -995,13 +994,13 @@ int proto_scenery_subdata_init(Proto* proto, int type) } // 0x49FCFC -int proto_wall_init(Proto* proto, int a2) +int proto_wall_init(Proto* proto, int pid) { - int v1 = a2 & 0xFFFFFF; + int num = pid & 0xFFFFFF; proto->wall.pid = -1; - proto->wall.messageId = 100 * v1; - proto->wall.fid = buildFid(OBJ_TYPE_WALL, v1 - 1, 0, 0, 0); + proto->wall.messageId = 100 * num; + proto->wall.fid = buildFid(OBJ_TYPE_WALL, num - 1, 0, 0, 0); if (!artExists(proto->wall.fid)) { proto->wall.fid = buildFid(OBJ_TYPE_WALL, 0, 0, 0, 0); } @@ -1016,13 +1015,13 @@ int proto_wall_init(Proto* proto, int a2) } // 0x49FD84 -int proto_tile_init(Proto* proto, int a2) +int proto_tile_init(Proto* proto, int pid) { - int v1 = a2 & 0xFFFFFF; + int num = pid & 0xFFFFFF; proto->tile.pid = -1; - proto->tile.messageId = 100 * v1; - proto->tile.fid = buildFid(OBJ_TYPE_TILE, v1 - 1, 0, 0, 0); + proto->tile.messageId = 100 * num; + proto->tile.fid = buildFid(OBJ_TYPE_TILE, num - 1, 0, 0, 0); if (!artExists(proto->tile.fid)) { proto->tile.fid = buildFid(OBJ_TYPE_TILE, 0, 0, 0, 0); } @@ -1035,13 +1034,13 @@ int proto_tile_init(Proto* proto, int a2) } // 0x49FDFC -int proto_misc_init(Proto* proto, int a2) +int proto_misc_init(Proto* proto, int pid) { - int v1 = a2 & 0xFFFFFF; + int num = pid & 0xFFFFFF; proto->misc.pid = -1; - proto->misc.messageId = 100 * v1; - proto->misc.fid = buildFid(OBJ_TYPE_MISC, v1 - 1, 0, 0, 0); + proto->misc.messageId = 100 * num; + proto->misc.fid = buildFid(OBJ_TYPE_MISC, num - 1, 0, 0, 0); if (!artExists(proto->misc.fid)) { proto->misc.fid = buildFid(OBJ_TYPE_MISC, 0, 0, 0, 0); } @@ -1353,7 +1352,7 @@ int protoInit() compat_mkdir(path); // TODO: Get rid of cast. - _proto_critter_init((Proto*)&gDudeProto, 0x1000000); + proto_critter_init((Proto*)&gDudeProto, 0x1000000); gDudeProto.pid = 0x1000000; gDudeProto.fid = buildFid(OBJ_TYPE_CRITTER, 1, 0, 0, 0); @@ -1472,7 +1471,7 @@ void protoReset() int i; // TODO: Get rid of cast. - _proto_critter_init((Proto*)&gDudeProto, 0x1000000); + proto_critter_init((Proto*)&gDudeProto, 0x1000000); gDudeProto.pid = 0x1000000; gDudeProto.fid = buildFid(OBJ_TYPE_CRITTER, 1, 0, 0, 0); @@ -2047,7 +2046,7 @@ int proto_new(int* pid, int type) proto->item.pid = *pid; break; case OBJ_TYPE_CRITTER: - _proto_critter_init(proto, *pid); + proto_critter_init(proto, *pid); proto->critter.pid = *pid; break; case OBJ_TYPE_SCENERY: @@ -2162,18 +2161,18 @@ int protoGetProto(int pid, Proto** protoPtr) } // 0x4A21DC -static int _proto_new_id(int a1) +static int _proto_new_id(int type) { - int result = _protoLists[a1].max_entries_num; - _protoLists[a1].max_entries_num = result + 1; + int result = _protoLists[type].max_entries_num; + _protoLists[type].max_entries_num = result + 1; return result; } // 0x4A2214 -static int _proto_max_id(int a1) +static int _proto_max_id(int type) { - return _protoLists[a1].max_entries_num; + return _protoLists[type].max_entries_num; } // 0x4A22C0 diff --git a/src/proto.h b/src/proto.h index 99c9d7e..f00eef9 100644 --- a/src/proto.h +++ b/src/proto.h @@ -112,19 +112,20 @@ int _proto_action_can_pickup(int pid); char* protoGetMessage(int pid, int message); char* protoGetName(int pid); char* protoGetDescription(int pid); -int proto_item_init(Proto* proto, int a2); +int proto_item_init(Proto* proto, int pid); int proto_item_subdata_init(Proto* proto, int type); +int proto_critter_init(Proto* proto, int pid); void objectDataReset(Object* obj); int objectDataRead(Object* obj, File* stream); int objectDataWrite(Object* obj, File* stream); int _proto_update_init(Object* obj); int _proto_dude_update_gender(); int _proto_dude_init(const char* path); -int proto_scenery_init(Proto* proto, int a2); +int proto_scenery_init(Proto* proto, int pid); int proto_scenery_subdata_init(Proto* proto, int type); -int proto_wall_init(Proto* proto, int a2); -int proto_tile_init(Proto* proto, int a2); -int proto_misc_init(Proto* proto, int a2); +int proto_wall_init(Proto* proto, int pid); +int proto_tile_init(Proto* proto, int pid); +int proto_misc_init(Proto* proto, int pid); int proto_copy_proto(int srcPid, int dstPid); bool proto_is_subtype(Proto* proto, int subtype); int protoGetDataMember(int pid, int member, ProtoDataMemberValue* value); @@ -134,7 +135,7 @@ void protoExit(); int _proto_save_pid(int pid); int proto_new(int* pid, int type); void _proto_remove_all(); -int protoGetProto(int pid, Proto** out_proto); +int protoGetProto(int pid, Proto** protoPtr); int _ResetPlayer(); static bool isExitGridPid(int pid) From 4b65bdf0217d22dd0f5309d7b72ca0fb2c2c3267 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 22 Jul 2023 17:09:19 +0300 Subject: [PATCH 052/136] Add scripts_request_townmap --- src/scripts.cc | 14 ++++++++++++-- src/scripts.h | 3 ++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/scripts.cc b/src/scripts.cc index ddcf5c6..58dab5d 100644 --- a/src/scripts.cc +++ b/src/scripts.cc @@ -913,8 +913,8 @@ int scriptsHandleRequests() } } - if ((gScriptsRequests & SCRIPT_REQUEST_0x02) != 0) { - gScriptsRequests &= ~SCRIPT_REQUEST_0x02; + if ((gScriptsRequests & SCRIPT_REQUEST_TOWN_MAP) != 0) { + gScriptsRequests &= ~SCRIPT_REQUEST_TOWN_MAP; wmTownMap(); } @@ -1128,6 +1128,16 @@ void _scripts_request_combat_locked(STRUCT_664980* a1) gScriptsRequests |= (SCRIPT_REQUEST_0x0400 | SCRIPT_REQUEST_COMBAT); } +// 0x4A461C +void scripts_request_townmap() +{ + if (isInCombat()) { + _game_user_wants_to_quit = 1; + } + + gScriptsRequests |= SCRIPT_REQUEST_TOWN_MAP; +} + // request_world_map() // 0x4A4644 void scriptsRequestWorldMap() diff --git a/src/scripts.h b/src/scripts.h index e061fd0..5cc3220 100644 --- a/src/scripts.h +++ b/src/scripts.h @@ -25,7 +25,7 @@ namespace fallout { typedef enum ScriptRequests { SCRIPT_REQUEST_COMBAT = 0x01, - SCRIPT_REQUEST_0x02 = 0x02, + SCRIPT_REQUEST_TOWN_MAP = 0x02, SCRIPT_REQUEST_WORLD_MAP = 0x04, SCRIPT_REQUEST_ELEVATOR = 0x08, SCRIPT_REQUEST_EXPLOSION = 0x10, @@ -175,6 +175,7 @@ int scriptsHandleRequests(); int _scripts_check_state_in_combat(); int scriptsRequestCombat(STRUCT_664980* a1); void _scripts_request_combat_locked(STRUCT_664980* ptr); +void scripts_request_townmap(); void scriptsRequestWorldMap(); int scriptsRequestElevator(Object* a1, int a2); int scriptsRequestExplosion(int tile, int elevation, int minDamage, int maxDamage); From 28d1d410d5117145027c5203d0dcd46a61dac4ba Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 06:08:23 +0300 Subject: [PATCH 053/136] Add get_num_i --- src/window_manager_private.cc | 111 ++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/src/window_manager_private.cc b/src/window_manager_private.cc index beef020..26d5c8f 100644 --- a/src/window_manager_private.cc +++ b/src/window_manager_private.cc @@ -20,6 +20,7 @@ namespace fallout { /// Maximum number of timed messages. static constexpr int kTimedMsgs = 5; +static int get_num_i(int win, int* value, int max_chars_wcursor, bool clear, bool allow_negative, int x, int y); static void tm_watch_msgs(); static void tm_kill_msg(); static void tm_kill_out_of_order(int queueIndex); @@ -1500,6 +1501,116 @@ size_t _calc_max_field_chars_wcursor(int value1, int value2) return std::max(len1, len2) + 1; } +// 0x4DD0AC +int get_num_i(int win, int* value, int max_chars_wcursor, bool clear, bool allow_negative, int x, int y) +{ + bool first_press = false; + + Window* window = windowGetWindow(win); + if (window == NULL) { + return -1; + } + + int original = *value; + + int width = max_chars_wcursor * fontGetMonospacedCharacterWidth(); + int height = fontGetLineHeight(); + + char* string = (char*)internal_malloc(max_chars_wcursor + 1); + + if (clear) { + string[0] = '\0'; + } else { + snprintf(string, + max_chars_wcursor + 1, + "%d", + *value); + } + + int cursorPos = strlen(string); + string[cursorPos] = '_'; + string[cursorPos + 1] = '\0'; + + windowDrawText(win, string, width, x, y, 0x100 | 4); + + Rect rect; + rect.left = x; + rect.top = y; + rect.right = x + width; + rect.bottom = y + height; + windowRefreshRect(win, &rect); + + bool done = false; + while (cursorPos <= max_chars_wcursor && !done) { + sharedFpsLimiter.mark(); + + int input = inputGetInput(); + if (input == KEY_RETURN) { + done = true; + } else if (input == KEY_BACKSPACE) { + if (cursorPos > 0) { + int stringWidth = fontGetStringWidth(string); + if (first_press) { + string[0] = '_'; + string[1] = '\0'; + cursorPos = 1; + first_press = false; + } else { + string[cursorPos - 1] = '_'; + string[cursorPos] = '\0'; + cursorPos--; + } + + windowFill(win, x, y, stringWidth, height, 0x100 | 1); + windowDrawText(win, string, width, x, y, 0x100 | 4); + windowRefreshRect(win, &rect); + } + } else if (input == KEY_ESCAPE) { + *value = original; + internal_free(string); + + return -1; + } else if (input == KEY_ARROW_LEFT) { + if (cursorPos > 0) { + int stringWidth = fontGetStringWidth(string); + string[cursorPos - 1] = '_'; + string[cursorPos] = '\0'; + windowFill(win, x, y, stringWidth, height, 0x100 | 1); + windowDrawText(win, string, width, x, y, 0x100 | 4); + windowRefreshRect(win, &rect); + + first_press = false; + cursorPos--; + } + } else { + if (cursorPos != max_chars_wcursor - 1) { + if ((input == '-' && allow_negative) + || (input >= '0' && input <= '9')) { + string[cursorPos] = input; + string[cursorPos + 1] = '_'; + string[cursorPos + 2] = '\0'; + + int stringWidth = fontGetStringWidth(string); + windowFill(win, x, y, stringWidth, height, 0x100 | 1); + windowDrawText(win, string, width, x, y, 0x100 | 4); + windowRefreshRect(win, &rect); + + first_press = false; + cursorPos++; + } + } + } + + renderPresent(); + sharedFpsLimiter.throttle(); + } + + *value = atoi(string); + internal_free(string); + + return 0; +} + // 0x4DD3EC void _GNW_intr_init() { From 127de143189a6d5732089f6c825a7f411832c9fd Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 07:20:56 +0300 Subject: [PATCH 054/136] Add win_get_num_i --- src/window_manager_private.cc | 107 ++++++++++++++++++++++++++++++++++ src/window_manager_private.h | 1 + 2 files changed, 108 insertions(+) diff --git a/src/window_manager_private.cc b/src/window_manager_private.cc index 26d5c8f..9c5f04e 100644 --- a/src/window_manager_private.cc +++ b/src/window_manager_private.cc @@ -1216,6 +1216,113 @@ int _win_input_str(int win, char* dest, int maxLength, int x, int y, int textCol return 0; } +// 0x4DCD68 +int win_get_num_i(int* value, int min, int max, bool clear, const char* title, int x, int y) +{ + if (!gWindowSystemInitialized) { + return -1; + } + + if (max < min) { + return -1; + } + + if (*value < min) { + *value = min; + } else if (*value > max) { + *value = max; + } + + int original = *value; + int max_chars_wcursor = _calc_max_field_chars_wcursor(min, max); + if (max_chars_wcursor == -1) { + return -1; + } + + int v2 = fontGetMonospacedCharacterWidth() * max_chars_wcursor; + + int width = fontGetStringWidth(title); + if (width < v2) { + width = v2; + } + + width += 16; + if (width < 160) { + width = 160; + } + + int height = 5 * fontGetLineHeight() + 16; + int v3 = (width - v2) / 2; + int v4 = fontGetLineHeight(); + int v5 = fontGetLineHeight() + 2; + + int win = windowCreate(x, y, width, height, 0x100, WINDOW_MODAL | WINDOW_MOVE_ON_TOP); + if (win == -1) { + return -1; + } + + windowDrawBorder(win); + windowFill(win, v3, v4 + 14, v2, v5, 0x100 | 1); + windowDrawText(win, title, width - 16, 8, 8, 0x100 | 5); + + bufferDrawRectShadowed(windowGetBuffer(win), + width, + v3 - 2, + v4 + 12, + v3 + v2 + 1, + v4 + 14 + v5 - 1, + _colorTable[_GNW_wcolor[2]], + _colorTable[_GNW_wcolor[1]]); + + _win_register_text_button(win, + width / 2 - 72, + height - fontGetLineHeight() - 14, + -1, + -1, + -1, + KEY_RETURN, + "Done", + 0); + + _win_register_text_button(win, + width / 2 + 16, + height - fontGetLineHeight() - 14, + -1, + -1, + -1, + KEY_ESCAPE, + "Cancel", + 0); + + char* hint = (char*)internal_malloc(80); + if (hint == NULL) { + return -1; + } + + sprintf(hint, "Please enter a number between %d and %d.", min, max); + windowRefresh(win); + + int rc; + while (1) { + rc = get_num_i(win, value, max_chars_wcursor, clear, min < 0, v3, v4 + 14); + if (*value >= min && *value <= max) { + break; + } + + if (rc == -1) { + break; + } + + _win_msg(hint, x - 70, y + 100, 0x100 | 6); + *value = original; + } + + internal_free(hint); + windowDestroy(win); + + return rc; +} + // 0x4DBD04 int process_pull_down(int win, Rect* rect, char** items, int itemsLength, int foregroundColor, int backgroundColor, MenuBar* menuBar, int pulldownIndex) { diff --git a/src/window_manager_private.h b/src/window_manager_private.h index c4e00e4..cfdecc7 100644 --- a/src/window_manager_private.h +++ b/src/window_manager_private.h @@ -30,6 +30,7 @@ int _win_width_needed(char** fileNameList, int fileNameListLength); int _win_input_str(int win, char* dest, int maxLength, int x, int y, int textColor, int backgroundColor); int process_pull_down(int win, Rect* rect, char** items, int itemsLength, int a5, int a6, MenuBar* menuBar, int pulldownIndex); int _GNW_process_menu(MenuBar* menuBar, int pulldownIndex); +int win_get_num_i(int* value, int min, int max, bool clear, const char* title, int x, int y); size_t _calc_max_field_chars_wcursor(int value1, int value2); void _GNW_intr_init(); void _GNW_intr_exit(); From 78ae0fb3bf1d2acfb7e90e7b3d0f57ebac41466a Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 09:25:56 +0300 Subject: [PATCH 055/136] Add mapper_mark_all_exit_grids --- src/mapper/mapper.cc | 30 ++++++++++++++++++++++++++++++ src/mapper/mapper.h | 12 ++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/mapper/mapper.cc create mode 100644 src/mapper/mapper.h diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc new file mode 100644 index 0000000..a3885e9 --- /dev/null +++ b/src/mapper/mapper.cc @@ -0,0 +1,30 @@ +#include "mapper/mapper.h" + +#include "object.h" +#include "proto.h" + +namespace fallout { + +static void mapper_mark_all_exit_grids(); + +// 0x559748 +MapTransition mapInfo = { -1, -1, 0, 0 }; + +// 0x48C704 +void mapper_mark_all_exit_grids() +{ + Object* obj; + + obj = objectFindFirstAtElevation(gElevation); + while (obj != NULL) { + if (isExitGridPid(obj->pid)) { + obj->data.misc.map = mapInfo.map; + obj->data.misc.tile = mapInfo.tile; + obj->data.misc.elevation = mapInfo.elevation; + obj->data.misc.rotation = mapInfo.rotation; + } + obj = objectFindNextAtElevation(); + } +} + +} // namespace fallout diff --git a/src/mapper/mapper.h b/src/mapper/mapper.h new file mode 100644 index 0000000..3d6adb3 --- /dev/null +++ b/src/mapper/mapper.h @@ -0,0 +1,12 @@ +#ifndef FALLOUT_MAPPER_MAPPER_H_ +#define FALLOUT_MAPPER_MAPPER_H_ + +#include "map.h" + +namespace fallout { + +extern MapTransition mapInfo; + +} // namespace fallout + +#endif /* FALLOUT_MAPPER_MAPPER_H_ */ From e68c2a4784c80e389f05a809c1bd83cd1fc9fe58 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 09:39:13 +0300 Subject: [PATCH 056/136] Add mapper_mark_exit_grid --- src/mapper/mapper.cc | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index a3885e9..da5072b 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -1,15 +1,45 @@ #include "mapper/mapper.h" +#include "game_mouse.h" #include "object.h" #include "proto.h" namespace fallout { +static int mapper_mark_exit_grid(); static void mapper_mark_all_exit_grids(); // 0x559748 MapTransition mapInfo = { -1, -1, 0, 0 }; +// 0x48C678 +int mapper_mark_exit_grid() +{ + int y; + int x; + int tile; + Object* obj; + + for (y = -2000; y != 2000; y += 200) { + for (x = -10; x < 10; x++) { + tile = gGameMouseBouncingCursor->tile + y + x; + + obj = objectFindFirstAtElevation(gElevation); + while (obj != NULL) { + if (isExitGridPid(obj->pid)) { + obj->data.misc.map = mapInfo.map; + obj->data.misc.tile = mapInfo.tile; + obj->data.misc.elevation = mapInfo.elevation; + obj->data.misc.rotation = mapInfo.rotation; + } + obj = objectFindNextAtElevation(); + } + } + } + + return 0; +} + // 0x48C704 void mapper_mark_all_exit_grids() { From 1366b050b081cffd506605927da4a066c77e696e Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 09:44:09 +0300 Subject: [PATCH 057/136] Add mapper_inven_unwield --- src/mapper/mapper.cc | 29 +++++++++++++++++++++++++++++ src/mapper/mapper.h | 3 +++ 2 files changed, 32 insertions(+) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index da5072b..77b9551 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -1,6 +1,9 @@ #include "mapper/mapper.h" +#include "animation.h" +#include "art.h" #include "game_mouse.h" +#include "inventory.h" #include "object.h" #include "proto.h" @@ -12,6 +15,32 @@ static void mapper_mark_all_exit_grids(); // 0x559748 MapTransition mapInfo = { -1, -1, 0, 0 }; +// 0x48C604 +int mapper_inven_unwield(Object* obj, int right_hand) +{ + Object* item; + int fid; + + reg_anim_begin(ANIMATION_REQUEST_RESERVED); + + if (right_hand) { + item = critterGetItem2(obj); + } else { + item = critterGetItem1(obj); + } + + if (item != NULL) { + item->flags &= ~OBJECT_IN_ANY_HAND; + } + + animationRegisterAnimate(obj, ANIM_PUT_AWAY, 0); + + fid = buildFid(OBJ_TYPE_CRITTER, obj->fid & 0xFFF, 0, 0, (obj->fid & 0x70000000) >> 28); + animationRegisterSetFid(obj, fid, 0); + + return reg_anim_end(); +} + // 0x48C678 int mapper_mark_exit_grid() { diff --git a/src/mapper/mapper.h b/src/mapper/mapper.h index 3d6adb3..f69546b 100644 --- a/src/mapper/mapper.h +++ b/src/mapper/mapper.h @@ -2,11 +2,14 @@ #define FALLOUT_MAPPER_MAPPER_H_ #include "map.h" +#include "obj_types.h" namespace fallout { extern MapTransition mapInfo; +int mapper_inven_unwield(Object* obj, int right_hand); + } // namespace fallout #endif /* FALLOUT_MAPPER_MAPPER_H_ */ From 480094a3ebe154642beea08df940121129a0ec8f Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 09:49:22 +0300 Subject: [PATCH 058/136] Add redraw_toolname --- src/mapper/mapper.cc | 18 ++++++++++++++++++ src/mapper/mapper.h | 2 ++ 2 files changed, 20 insertions(+) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index 77b9551..22b775c 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -6,15 +6,33 @@ #include "inventory.h" #include "object.h" #include "proto.h" +#include "svga.h" +#include "window_manager.h" namespace fallout { +static void redraw_toolname(); static int mapper_mark_exit_grid(); static void mapper_mark_all_exit_grids(); // 0x559748 MapTransition mapInfo = { -1, -1, 0, 0 }; +// 0x6EC4AC +int tool_win; + +// 0x48B230 +void redraw_toolname() +{ + Rect rect; + + rect.left = _scr_size.right - _scr_size.left - 149; + rect.top = 60; + rect.right = _scr_size.right - _scr_size.left + 1; + rect.bottom = 95; + windowRefreshRect(tool_win, &rect); +} + // 0x48C604 int mapper_inven_unwield(Object* obj, int right_hand) { diff --git a/src/mapper/mapper.h b/src/mapper/mapper.h index f69546b..c85597b 100644 --- a/src/mapper/mapper.h +++ b/src/mapper/mapper.h @@ -8,6 +8,8 @@ namespace fallout { extern MapTransition mapInfo; +extern int tool_win; + int mapper_inven_unwield(Object* obj, int right_hand); } // namespace fallout From 9faa427a286c16e3da3c4cb80ecd04e0f76a743c Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 09:52:59 +0300 Subject: [PATCH 059/136] Add clear_toolname --- src/mapper/mapper.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index 22b775c..a555404 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -12,6 +12,7 @@ namespace fallout { static void redraw_toolname(); +static void clear_toolname(); static int mapper_mark_exit_grid(); static void mapper_mark_all_exit_grids(); @@ -33,6 +34,15 @@ void redraw_toolname() windowRefreshRect(tool_win, &rect); } +// 0x48B278 +void clear_toolname() +{ + windowDrawText(tool_win, "", 120, _scr_size.right - _scr_size.left - 149, 60, 260); + windowDrawText(tool_win, "", 120, _scr_size.right - _scr_size.left - 149, 70, 260); + windowDrawText(tool_win, "", 120, _scr_size.right - _scr_size.left - 149, 80, 260); + redraw_toolname(); +} + // 0x48C604 int mapper_inven_unwield(Object* obj, int right_hand) { From 8cc48cf52a94e9b8c29fc6ae9f2f6e9d94be57d2 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 09:55:58 +0300 Subject: [PATCH 060/136] Add update_high_obj_name --- src/mapper/mapper.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index a555404..81d7ac7 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -13,6 +13,7 @@ namespace fallout { static void redraw_toolname(); static void clear_toolname(); +static void update_high_obj_name(Object* obj); static int mapper_mark_exit_grid(); static void mapper_mark_all_exit_grids(); @@ -43,6 +44,19 @@ void clear_toolname() redraw_toolname(); } +// 0x48B5BC +void update_high_obj_name(Object* obj) +{ + Proto* proto; + + if (protoGetProto(obj->pid, &proto) != -1) { + windowDrawText(tool_win, protoGetName(obj->pid), 120, _scr_size.right - _scr_size.left - 149, 60, 260); + windowDrawText(tool_win, "", 120, _scr_size.right - _scr_size.left - 149, 70, 260); + windowDrawText(tool_win, "", 120, _scr_size.right - _scr_size.left - 149, 80, 260); + redraw_toolname(); + } +} + // 0x48C604 int mapper_inven_unwield(Object* obj, int right_hand) { From c652312157b834d4c689eefbc43e338be0cc60f2 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 10:01:58 +0300 Subject: [PATCH 061/136] Add print_toolbar_name --- src/mapper/mapper.cc | 29 +++++++++++++++++++++++++++++ src/mapper/mapper.h | 2 ++ 2 files changed, 31 insertions(+) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index 81d7ac7..b74fbef 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -1,7 +1,11 @@ #include "mapper/mapper.h" +#include + #include "animation.h" #include "art.h" +#include "color.h" +#include "draw.h" #include "game_mouse.h" #include "inventory.h" #include "object.h" @@ -20,9 +24,34 @@ static void mapper_mark_all_exit_grids(); // 0x559748 MapTransition mapInfo = { -1, -1, 0, 0 }; +// 0x6EC4A8 +unsigned char* tool; + // 0x6EC4AC int tool_win; +// 0x48B16C +void print_toolbar_name(int object_type) +{ + Rect rect; + char name[80]; + + rect.left = 0; + rect.top = 0; + rect.right = 0; + rect.bottom = 22; + bufferFill(tool + 2 + 2 * (_scr_size.right - _scr_size.left) + 2, + 96, + _scr_size.right - _scr_size.left + 1, + 19, + _colorTable[21140]); + + sprintf(name, "%s", artGetObjectTypeName(object_type)); + name[0] = toupper(name[0]); + windowDrawText(tool_win, name, 0, 7, 7, _colorTable[32747] | 0x2000000); + windowRefreshRect(tool_win, &rect); +} + // 0x48B230 void redraw_toolname() { diff --git a/src/mapper/mapper.h b/src/mapper/mapper.h index c85597b..cc106d0 100644 --- a/src/mapper/mapper.h +++ b/src/mapper/mapper.h @@ -8,8 +8,10 @@ namespace fallout { extern MapTransition mapInfo; +extern unsigned char* tool; extern int tool_win; +void print_toolbar_name(int object_type); int mapper_inven_unwield(Object* obj, int right_hand); } // namespace fallout From 4eaea0e3c096d2126946dc26e2d1d2ce1b2041a8 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 10:10:09 +0300 Subject: [PATCH 062/136] Add proto_user_is_librarian --- src/mapper/mapper.cc | 14 ++++++++++++++ src/mapper/mp_proto.cc | 8 ++++++++ src/mapper/mp_proto.h | 10 ++++++++++ 3 files changed, 32 insertions(+) create mode 100644 src/mapper/mp_proto.cc create mode 100644 src/mapper/mp_proto.h diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index b74fbef..e5f20b6 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -8,13 +8,16 @@ #include "draw.h" #include "game_mouse.h" #include "inventory.h" +#include "mapper/mp_proto.h" #include "object.h" #include "proto.h" +#include "settings.h" #include "svga.h" #include "window_manager.h" namespace fallout { +static bool proto_user_is_librarian(); static void redraw_toolname(); static void clear_toolname(); static void update_high_obj_name(Object* obj); @@ -30,6 +33,17 @@ unsigned char* tool; // 0x6EC4AC int tool_win; +// 0x487784 +bool proto_user_is_librarian() +{ + if (!settings.mapper.librarian) { + return false; + } + + can_modify_protos = true; + return true; +} + // 0x48B16C void print_toolbar_name(int object_type) { diff --git a/src/mapper/mp_proto.cc b/src/mapper/mp_proto.cc new file mode 100644 index 0000000..3e89993 --- /dev/null +++ b/src/mapper/mp_proto.cc @@ -0,0 +1,8 @@ +#include "mapper/mp_proto.h" + +namespace fallout { + +// 0x559C60 +bool can_modify_protos = false; + +} // namespace fallout diff --git a/src/mapper/mp_proto.h b/src/mapper/mp_proto.h new file mode 100644 index 0000000..2bf6e8d --- /dev/null +++ b/src/mapper/mp_proto.h @@ -0,0 +1,10 @@ +#ifndef FALLOUT_MAPPER_MP_PROTO_H_ +#define FALLOUT_MAPPER_MP_PROTO_H_ + +namespace fallout { + +extern bool can_modify_protos; + +} // namespace fallout + +#endif /* FALLOUT_MAPPER_MP_PROTO_H_ */ From 9f08c73b3ce3ae99dd4375e25a59433c10b81d56 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 10:15:51 +0300 Subject: [PATCH 063/136] Add categoryUnhide --- src/mapper/mapper.cc | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index e5f20b6..38cfa5e 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -17,6 +17,7 @@ namespace fallout { +static int categoryUnhide(); static bool proto_user_is_librarian(); static void redraw_toolname(); static void clear_toolname(); @@ -27,12 +28,31 @@ static void mapper_mark_all_exit_grids(); // 0x559748 MapTransition mapInfo = { -1, -1, 0, 0 }; +// 0x5598AC +static int categoryWin = -1; + +// 0x5598B0 +static bool categoryIsHidden = false; + // 0x6EC4A8 unsigned char* tool; // 0x6EC4AC int tool_win; +// 0x487774 +int categoryUnhide() +{ + if (categoryWin == -1) { + return -1; + } + + windowShow(categoryWin); + categoryIsHidden = false; + + return 0; +} + // 0x487784 bool proto_user_is_librarian() { From f933bff8ae615bf242f15f3f60badb6ca29fb583 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 10:16:59 +0300 Subject: [PATCH 064/136] Add categoryHide --- src/mapper/mapper.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index 38cfa5e..4b3aea2 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -17,6 +17,7 @@ namespace fallout { +static int categoryHide(); static int categoryUnhide(); static bool proto_user_is_librarian(); static void redraw_toolname(); @@ -40,6 +41,19 @@ unsigned char* tool; // 0x6EC4AC int tool_win; +// 0x487728 +int categoryHide() +{ + if (categoryWin == -1) { + return -1; + } + + windowHide(categoryWin); + categoryIsHidden = true; + + return 0; +} + // 0x487774 int categoryUnhide() { From b1049fceb1f61aa4cff4f6d772e376ddd5fffe39 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 10:17:52 +0300 Subject: [PATCH 065/136] Add categoryToggleState --- src/mapper/mapper.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index 4b3aea2..65af3cf 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -18,6 +18,7 @@ namespace fallout { static int categoryHide(); +static int categoryToggleState(); static int categoryUnhide(); static bool proto_user_is_librarian(); static void redraw_toolname(); @@ -54,6 +55,16 @@ int categoryHide() return 0; } +// 0x487768 +int categoryToggleState() +{ + if (categoryIsHidden) { + return categoryUnhide(); + } else { + return categoryHide(); + } +} + // 0x487774 int categoryUnhide() { From 661c68902959a4909b088f92ff77f5b30fd8bd1d Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 10:19:02 +0300 Subject: [PATCH 066/136] Add categoryExit --- src/mapper/mapper.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index 65af3cf..a33d59a 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -17,6 +17,7 @@ namespace fallout { +static int categoryExit(); static int categoryHide(); static int categoryToggleState(); static int categoryUnhide(); @@ -42,6 +43,19 @@ unsigned char* tool; // 0x6EC4AC int tool_win; +// 0x487700 +int categoryExit() +{ + if (categoryWin == -1) { + return -1; + } + + windowDestroy(categoryWin); + categoryWin = -1; + + return 0; +} + // 0x487728 int categoryHide() { From 3331ad02b0d0a45fbea84d060e7d069afd503961 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 10:20:54 +0300 Subject: [PATCH 067/136] Add categoryInit --- src/mapper/mapper.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index a33d59a..58c5ac7 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -17,6 +17,7 @@ namespace fallout { +static int categoryInit(); static int categoryExit(); static int categoryHide(); static int categoryToggleState(); @@ -43,6 +44,12 @@ unsigned char* tool; // 0x6EC4AC int tool_win; +// 0x4875B4 +int categoryInit() +{ + return 0; +} + // 0x487700 int categoryExit() { From 093d4f23d84b91e441c1308f89a4e1f035544688 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 10:22:42 +0300 Subject: [PATCH 068/136] Add bookmarkUnHide --- src/mapper/mapper.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index 58c5ac7..24d3fe3 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -17,6 +17,7 @@ namespace fallout { +static void bookmarkUnHide(); static int categoryInit(); static int categoryExit(); static int categoryHide(); @@ -32,6 +33,9 @@ static void mapper_mark_all_exit_grids(); // 0x559748 MapTransition mapInfo = { -1, -1, 0, 0 }; +// 0x5598A8 +static int bookmarkWin = -1; + // 0x5598AC static int categoryWin = -1; @@ -44,6 +48,14 @@ unsigned char* tool; // 0x6EC4AC int tool_win; +// 0x4875F8 +void bookmarkUnHide() +{ + if (bookmarkWin != -1) { + windowShow(bookmarkWin); + } +} + // 0x4875B4 int categoryInit() { From a2d61dc7c4213d3535c3dd3f6602baf0ba458db9 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 10:23:55 +0300 Subject: [PATCH 069/136] Add bookmarkHide --- src/mapper/mapper.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index 24d3fe3..97c9b42 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -17,6 +17,7 @@ namespace fallout { +static void bookmarkHide(); static void bookmarkUnHide(); static int categoryInit(); static int categoryExit(); @@ -48,6 +49,14 @@ unsigned char* tool; // 0x6EC4AC int tool_win; +// 0x4875E0 +void bookmarkHide() +{ + if (bookmarkWin != -1) { + windowHide(bookmarkWin); + } +} + // 0x4875F8 void bookmarkUnHide() { From a27b2496d469f4b146a38b2ef0fe617afdd9e171 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 10:25:58 +0300 Subject: [PATCH 070/136] Add bookmarkExit --- src/mapper/mapper.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index 97c9b42..df8e57e 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -17,6 +17,7 @@ namespace fallout { +static int bookmarkExit(); static void bookmarkHide(); static void bookmarkUnHide(); static int categoryInit(); @@ -49,6 +50,19 @@ unsigned char* tool; // 0x6EC4AC int tool_win; +// 0x4875B8 +int bookmarkExit() +{ + if (bookmarkWin == -1) { + return -1; + } + + windowDestroy(bookmarkWin); + bookmarkWin = -1; + + return 0; +} + // 0x4875E0 void bookmarkHide() { From d16f0010ac345da2fcc1f1cf422a88b0c65bad26 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 10:26:43 +0300 Subject: [PATCH 071/136] Add bookmarkInit --- src/mapper/mapper.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index df8e57e..b0c55d5 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -17,6 +17,7 @@ namespace fallout { +static int bookmarkInit(); static int bookmarkExit(); static void bookmarkHide(); static void bookmarkUnHide(); @@ -50,6 +51,12 @@ unsigned char* tool; // 0x6EC4AC int tool_win; +// 0x4875B4 +int bookmarkInit() +{ + return 0; +} + // 0x4875B8 int bookmarkExit() { From 7327588fc3ebad3731d576bdc50f7bc04f2af305 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 10:35:16 +0300 Subject: [PATCH 072/136] Add MapperInit --- src/mapper/mapper.cc | 55 ++++++++++++++++++++++++++++++++++++++++++++ src/mapper/mapper.h | 3 +++ 2 files changed, 58 insertions(+) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index b0c55d5..e419825 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -8,6 +8,7 @@ #include "draw.h" #include "game_mouse.h" #include "inventory.h" +#include "kb.h" #include "mapper/mp_proto.h" #include "object.h" #include "proto.h" @@ -17,6 +18,7 @@ namespace fallout { +static void MapperInit(); static int bookmarkInit(); static int bookmarkExit(); static void bookmarkHide(); @@ -45,12 +47,65 @@ static int categoryWin = -1; // 0x5598B0 static bool categoryIsHidden = false; +// 0x6EAA40 +int menu_val_0[8]; + +// 0x6EAA60 +int menu_val_2[8]; + +// 0x6EC408 +int menu_val_1[21]; + // 0x6EC4A8 unsigned char* tool; // 0x6EC4AC int tool_win; +// 0x485E00 +void MapperInit() +{ + menu_val_0[0] = KEY_ALT_N; + menu_val_0[1] = KEY_ALT_O; + menu_val_0[2] = KEY_ALT_S; + menu_val_0[3] = KEY_ALT_A; + menu_val_0[4] = KEY_ESCAPE; + menu_val_0[5] = KEY_ALT_K; + menu_val_0[6] = KEY_ALT_I; + menu_val_0[7] = KEY_ESCAPE; + + menu_val_1[0] = KEY_ALT_U; + menu_val_1[1] = KEY_ALT_Y; + menu_val_1[2] = KEY_ESCAPE; + menu_val_1[3] = KEY_ALT_G; + menu_val_1[4] = 4186; + menu_val_1[5] = 4188; + menu_val_1[6] = KEY_ESCAPE; + menu_val_1[7] = KEY_ALT_B; + menu_val_1[8] = KEY_ALT_E; + menu_val_1[9] = KEY_ALT_D; + menu_val_1[10] = KEY_LOWERCASE_B; + menu_val_1[11] = 2165; + menu_val_1[12] = 3123; + menu_val_1[13] = KEY_ALT_Z; + menu_val_1[14] = 5677; + menu_val_1[15] = 5678; + menu_val_1[16] = 5679; + menu_val_1[17] = 5666; + menu_val_1[18] = KEY_ESCAPE; + menu_val_1[19] = 5406; + menu_val_1[20] = 5405; + + menu_val_2[0] = KEY_LOWERCASE_I; + menu_val_2[1] = 5400; + menu_val_2[2] = KEY_LOWERCASE_S; + menu_val_2[3] = KEY_CTRL_F8; + menu_val_2[4] = 5410; + menu_val_2[5] = KEY_GRAVE; + menu_val_2[6] = KEY_ALT_W; + menu_val_2[7] = 5544; +} + // 0x4875B4 int bookmarkInit() { diff --git a/src/mapper/mapper.h b/src/mapper/mapper.h index cc106d0..3eb594c 100644 --- a/src/mapper/mapper.h +++ b/src/mapper/mapper.h @@ -8,6 +8,9 @@ namespace fallout { extern MapTransition mapInfo; +extern int menu_val_0[8]; +extern int menu_val_2[8]; +extern int menu_val_1[21]; extern unsigned char* tool; extern int tool_win; From cdfd308193f08211d0259861e454412f13d42ee2 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 11:01:03 +0300 Subject: [PATCH 073/136] Add mapper_main --- src/mapper/mapper.cc | 42 ++++++++++++++++++++++++++++++++++++++++++ src/mapper/mapper.h | 1 + src/memory.cc | 5 +---- src/memory.h | 1 + 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index e419825..c1cc87b 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -10,6 +10,7 @@ #include "inventory.h" #include "kb.h" #include "mapper/mp_proto.h" +#include "memory.h" #include "object.h" #include "proto.h" #include "settings.h" @@ -19,6 +20,8 @@ namespace fallout { static void MapperInit(); +static int mapper_edit_init(int argc, char** argv); +static void mapper_edit_exit(); static int bookmarkInit(); static int bookmarkExit(); static void bookmarkHide(); @@ -29,6 +32,7 @@ static int categoryHide(); static int categoryToggleState(); static int categoryUnhide(); static bool proto_user_is_librarian(); +static void edit_mapper(); static void redraw_toolname(); static void clear_toolname(); static void update_high_obj_name(Object* obj); @@ -62,6 +66,24 @@ unsigned char* tool; // 0x6EC4AC int tool_win; +// gnw_main +// 0x485DD0 +int mapper_main(int argc, char** argv) +{ + MapperInit(); + + if (mapper_edit_init(argc, argv) == -1) { + mem_check(); + return 0; + } + + edit_mapper(); + mapper_edit_exit(); + mem_check(); + + return 0; +} + // 0x485E00 void MapperInit() { @@ -106,6 +128,20 @@ void MapperInit() menu_val_2[7] = 5544; } +// 0x485F94 +int mapper_edit_init(int argc, char** argv) +{ + // TODO: Incomplete. + + return 0; +} + +// 0x48752C +void mapper_edit_exit() +{ + // TODO: Incomplete. +} + // 0x4875B4 int bookmarkInit() { @@ -207,6 +243,12 @@ bool proto_user_is_librarian() return true; } +// 0x4877D0 +void edit_mapper() +{ + // TODO: Incomplete. +} + // 0x48B16C void print_toolbar_name(int object_type) { diff --git a/src/mapper/mapper.h b/src/mapper/mapper.h index 3eb594c..4ae73cf 100644 --- a/src/mapper/mapper.h +++ b/src/mapper/mapper.h @@ -14,6 +14,7 @@ extern int menu_val_1[21]; extern unsigned char* tool; extern int tool_win; +int mapper_main(int argc, char** argv); void print_toolbar_name(int object_type); int mapper_inven_unwield(Object* obj, int right_hand); diff --git a/src/memory.cc b/src/memory.cc index 5f15047..c51ff39 100644 --- a/src/memory.cc +++ b/src/memory.cc @@ -32,7 +32,6 @@ typedef struct MemoryBlockFooter { static void* memoryBlockMallocImpl(size_t size); static void* memoryBlockReallocImpl(void* ptr, size_t size); static void memoryBlockFreeImpl(void* ptr); -static void memoryBlockPrintStats(); static void* mem_prep_block(void* block, size_t size); static void memoryBlockValidate(void* block); @@ -176,10 +175,8 @@ static void memoryBlockFreeImpl(void* ptr) } } -// NOTE: Not used. -// // 0x4C5C5C -static void memoryBlockPrintStats() +void mem_check() { if (gMallocProc == memoryBlockMallocImpl) { debugPrint("Current memory allocated: %6d blocks, %9u bytes total\n", gMemoryBlocksCurrentCount, gMemoryBlocksCurrentSize); diff --git a/src/memory.h b/src/memory.h index facaa54..96b0364 100644 --- a/src/memory.h +++ b/src/memory.h @@ -9,6 +9,7 @@ char* internal_strdup(const char* string); void* internal_malloc(size_t size); void* internal_realloc(void* ptr, size_t size); void internal_free(void* ptr); +void mem_check(); } // namespace fallout From 4b51b2f29bafbc4518d63b6f65cea5caec138d62 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 14:15:20 +0300 Subject: [PATCH 074/136] Add tile_toggle_roof --- src/tile.cc | 11 +++++++++++ src/tile.h | 1 + 2 files changed, 12 insertions(+) diff --git a/src/tile.cc b/src/tile.cc index af68a39..4bee912 100644 --- a/src/tile.cc +++ b/src/tile.cc @@ -654,6 +654,17 @@ static void tileRefreshGame(Rect* rect, int elevation) gTileWindowRefreshProc(&rectToUpdate); } +// 0x4B1634 +void tile_toggle_roof(bool refresh) +{ + gTileRoofIsVisible = !gTileRoofIsVisible; + + if (refresh) { + // NOTE: Uninline. + tileWindowRefresh(); + } +} + // 0x4B166C int tileRoofIsVisible() { diff --git a/src/tile.h b/src/tile.h index ae227d2..97acc8f 100644 --- a/src/tile.h +++ b/src/tile.h @@ -26,6 +26,7 @@ void tileEnable(); void tileWindowRefreshRect(Rect* rect, int elevation); void tileWindowRefresh(); int tileSetCenter(int tile, int flags); +void tile_toggle_roof(bool refresh); int tileRoofIsVisible(); int tileToScreenXY(int tile, int* x, int* y, int elevation); int tileFromScreenXY(int x, int y, int elevation, bool ignoreBounds = false); From 053e70dde4eb027d204c01bfc6f1424f259d1105 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 23 Jul 2023 14:27:52 +0300 Subject: [PATCH 075/136] Add gmouse_set_mapper_mode --- src/game_mouse.cc | 6 ++++++ src/game_mouse.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/game_mouse.cc b/src/game_mouse.cc index 9f82fcd..fca7c79 100644 --- a/src/game_mouse.cc +++ b/src/game_mouse.cc @@ -1328,6 +1328,12 @@ int gameMouseGetCursor() return gGameMouseCursor; } +// 0x44C9F0 +void gmouse_set_mapper_mode(int mode) +{ + _gmouse_mapper_mode = mode; +} + // 0x44C9F8 void _gmouse_3d_enable_modes() { diff --git a/src/game_mouse.h b/src/game_mouse.h index fc65633..2b99efe 100644 --- a/src/game_mouse.h +++ b/src/game_mouse.h @@ -86,6 +86,7 @@ void gameMouseRefresh(); void _gmouse_handle_event(int mouseX, int mouseY, int mouseState); int gameMouseSetCursor(int cursor); int gameMouseGetCursor(); +void gmouse_set_mapper_mode(int mode); void gameMouseSetMode(int a1); int gameMouseGetMode(); void gameMouseCycleMode(); From 4cd40b33afc0fd0e7e4af85a8dd5729d5ea67a19 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 06:50:07 +0300 Subject: [PATCH 076/136] Add mapper_edit_init --- src/graph_lib.cc | 8 + src/graph_lib.h | 1 + src/mapper/map_func.cc | 11 + src/mapper/map_func.h | 10 + src/mapper/mapper.cc | 1088 +++++++++++++++++++++++++++++++++++++++- src/mapper/mp_proto.cc | 6 + src/mapper/mp_proto.h | 2 + src/mapper/mp_targt.cc | 19 + src/mapper/mp_targt.h | 11 + src/mapper/mp_text.cc | 13 + src/mapper/mp_text.h | 10 + 11 files changed, 1178 insertions(+), 1 deletion(-) create mode 100644 src/mapper/map_func.cc create mode 100644 src/mapper/map_func.h create mode 100644 src/mapper/mp_targt.cc create mode 100644 src/mapper/mp_targt.h create mode 100644 src/mapper/mp_text.cc create mode 100644 src/mapper/mp_text.h diff --git a/src/graph_lib.cc b/src/graph_lib.cc index 08a65dd..6a432bd 100644 --- a/src/graph_lib.cc +++ b/src/graph_lib.cc @@ -52,6 +52,14 @@ unsigned char HighRGB(unsigned char color) return std::max(std::max(r, g), b); } +// 0x44ED98 +int load_lbm_to_buf(const char* path, unsigned char* buffer, int a3, int a4, int a5, int a6, int a7) +{ + // TODO: Incomplete. + + return -1; +} + // 0x44F250 int graphCompress(unsigned char* a1, unsigned char* a2, int a3) { diff --git a/src/graph_lib.h b/src/graph_lib.h index 4364973..8278c6a 100644 --- a/src/graph_lib.h +++ b/src/graph_lib.h @@ -4,6 +4,7 @@ namespace fallout { unsigned char HighRGB(unsigned char color); +int load_lbm_to_buf(const char* path, unsigned char* buffer, int a3, int a4, int a5, int a6, int a7); int graphCompress(unsigned char* a1, unsigned char* a2, int a3); int graphDecompress(unsigned char* a1, unsigned char* a2, int a3); void grayscalePaletteUpdate(int a1, int a2); diff --git a/src/mapper/map_func.cc b/src/mapper/map_func.cc new file mode 100644 index 0000000..9d96f2e --- /dev/null +++ b/src/mapper/map_func.cc @@ -0,0 +1,11 @@ +#include "mapper/map_func.h" + +namespace fallout { + +// 0x4825B0 +void setup_map_dirs() +{ + // TODO: Incomplete. +} + +} // namespace fallout diff --git a/src/mapper/map_func.h b/src/mapper/map_func.h new file mode 100644 index 0000000..fb71b2b --- /dev/null +++ b/src/mapper/map_func.h @@ -0,0 +1,10 @@ +#ifndef FALLOUT_MAPPER_MAP_FUNC_H_ +#define FALLOUT_MAPPER_MAP_FUNC_H_ + +namespace fallout { + +void setup_map_dirs(); + +} // namespace fallout + +#endif /* FALLOUT_MAPPER_MAP_FUNC_H_ */ diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index c1cc87b..786b5a5 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -5,17 +5,26 @@ #include "animation.h" #include "art.h" #include "color.h" +#include "debug.h" #include "draw.h" +#include "game.h" #include "game_mouse.h" +#include "graph_lib.h" #include "inventory.h" #include "kb.h" +#include "mapper/map_func.h" #include "mapper/mp_proto.h" +#include "mapper/mp_targt.h" +#include "mapper/mp_text.h" #include "memory.h" +#include "mouse.h" #include "object.h" #include "proto.h" #include "settings.h" #include "svga.h" +#include "tile.h" #include "window_manager.h" +#include "window_manager_private.h" namespace fallout { @@ -33,15 +42,141 @@ static int categoryToggleState(); static int categoryUnhide(); static bool proto_user_is_librarian(); static void edit_mapper(); +static void mapper_load_toolbar(int a1, int a2); static void redraw_toolname(); static void clear_toolname(); static void update_high_obj_name(Object* obj); static int mapper_mark_exit_grid(); static void mapper_mark_all_exit_grids(); +// TODO: Underlying menu/pulldown interface wants menu items to be non-const, +// needs some refactoring. + +static char kSeparator[] = ""; + +static char kNew[] = " New "; +static char kOpen[] = " Open "; +static char kSave[] = " Save "; +static char kSaveAs[] = " Save As... "; +static char kInfo[] = " Info "; +static char kOpenFromText[] = " Open From Text "; +static char kQuit[] = " Quit "; + +static char kCreatePattern[] = " Create Pattern "; +static char kUsePattern[] = " Use Pattern "; +static char kMoveMap[] = " Move Map "; +static char kMoveMapElev[] = " Move Map Elev "; +static char kCopyMapElev[] = " Copy Map Elev "; +static char kEditObjDude[] = " Edit Obj_dude "; +static char kFlushCache[] = " Flush Cache "; +static char kToggleAnimStepping[] = " Toggle Anim Stepping "; +static char kFixMapObjectsToPids[] = " Fix map-objects to pids "; +static char kSetBookmark[] = " Set Bookmark "; +static char kToggleBlockObjView[] = " Toggle Block Obj View "; +static char kToggleClickToScroll[] = " Toggle Click-To-Scroll "; +static char kSetExitGridData[] = " Set Exit-Grid Data "; +static char kMarkExitGrids[] = " Mark Exit-Grids "; +static char kMarkAllExitGrids[] = " Mark *ALL* Exit Grids "; +static char kClearMapLevel[] = " *Clear Map Level* "; +static char kCreateAllMapTexts[] = " Create ALL MAP TEXTS "; +static char kRebuildAllMaps[] = " Rebuild ALL MAPS "; + +static char kListAllScripts[] = " List all Scripts "; +static char kSetStartHex[] = " Set Start Hex "; +static char kPlaceSpatialScript[] = " Place Spatial Script "; +static char kDeleteSpatialScript[] = " Delete Spatial Script "; +static char kDeleteAllSpatialScripts[] = " Delete *ALL* Spatial SCRIPTS! "; +static char kCreateScript[] = " Create Script "; +static char kSetMapScript[] = " Set Map Script "; +static char kShowMapScript[] = " Show Map Script "; + +static char kRebuildWeapons[] = " Rebuild Weapons "; +static char kRebuildProtoList[] = " Rebuild Proto List "; +static char kRebuildAll[] = " Rebuild ALL "; +static char kRebuildBinary[] = " Rebuild Binary "; +static char kArtToProtos[] = " Art => New Protos "; +static char kSwapPrototypse[] = " Swap Prototypes "; + +// 0x559648 +char* menu_0[] = { + kNew, + kOpen, + kSave, + kSaveAs, + kSeparator, + kInfo, + kOpenFromText, + kQuit, +}; + +// 0x559668 +char* menu_1[] = { + kCreatePattern, + kUsePattern, + kSeparator, + kMoveMap, + kMoveMapElev, + kCopyMapElev, + kSeparator, + kEditObjDude, + kFlushCache, + kToggleAnimStepping, + kFixMapObjectsToPids, + kSetBookmark, + kToggleBlockObjView, + kToggleClickToScroll, + kSetExitGridData, + kMarkExitGrids, + kMarkAllExitGrids, + kClearMapLevel, + kSeparator, + kCreateAllMapTexts, + kRebuildAllMaps, +}; + +// 0x5596BC +char* menu_2[] = { + kListAllScripts, + kSetStartHex, + kPlaceSpatialScript, + kDeleteSpatialScript, + kDeleteAllSpatialScripts, + kCreateScript, + kSetMapScript, + kShowMapScript, +}; + +// 0x5596DC +char* menu_3[] = { + kRebuildWeapons, + kRebuildProtoList, + kRebuildAll, + kRebuildBinary, + kSeparator, + kArtToProtos, + kSwapPrototypse, +}; + +// 0x5596F8 +char** menu_names[] = { + menu_0, + menu_1, + menu_2, + menu_3, +}; + // 0x559748 MapTransition mapInfo = { -1, -1, 0, 0 }; +// 0x559880 +int max_art_buttons = 7; + +// 0x559884 +int art_scale_width = 49; + +// 0x559888 +int art_scale_height = 48; + // 0x5598A8 static int bookmarkWin = -1; @@ -57,15 +192,234 @@ int menu_val_0[8]; // 0x6EAA60 int menu_val_2[8]; +// 0x6EAA80 +unsigned char e_num[4][19 * 26]; + // 0x6EC408 int menu_val_1[21]; +// 0x6EC468 +unsigned char* art_shape; + +// 0x6EC46C +int to_paint_bid; + +// 0x6EC470 +int edit_bid; + +// 0x6EC474 +int paste_bid; + +// 0x6EC478 +int misc_bid; + +// 0x6EC47C +int tile_bid; + +// 0x6EC480 +int copy_bid; + +// 0x6EC484 +int delete_bid; + +// 0x6EC488 +int wall_bid; + +// 0x6EC48C +int obj_bid; + +// 0x6EC490 +int to_topdown_bid; + +// 0x6EC494 +int roof_bid; + +// 0x6EC498 +int hex_bid; + +// 0x6EC49C +int to_iso_bid; + +// 0x6EC4A0 +int scen_bid; + +// 0x6EC4A4 +int crit_bid; + // 0x6EC4A8 unsigned char* tool; // 0x6EC4AC int tool_win; +// 0x6EC4B0 +int menu_bar; + +// 0x6EC4B4 +unsigned char* lbm_buf; + +// 0x6EC4B8 +unsigned char height_inc_up[18 * 23]; + +// 0x6EC656 +unsigned char height_dec_up[18 * 23]; + +// 0x6EC7F4 +unsigned char height_dec_down[18 * 23]; + +// 0x6EC992 +unsigned char height_inc_down[18 * 23]; + +// 0x6ECB30 +unsigned char obj_down[66 * 13]; + +// 0x6ECE8A +unsigned char to_iso_down[58 * 13]; + +// 0x6ED17C +unsigned char scen_up[66 * 13]; + +// 0x6ED4D6 +unsigned char roof_up[58 * 13]; + +// 0x6ED7C8 +unsigned char crit_down[66 * 13]; + +// 0x6EDB22 +unsigned char obj_up[66 * 13]; + +// 0x6EDE7C +unsigned char crit_up[66 * 13]; + +// 0x6EE1D6 +unsigned char to_topdown_down[58 * 13]; + +// 0x6EE4C8 +unsigned char hex_up[58 * 13]; + +// 0x6EE7BA +unsigned char hex_down[58 * 13]; + +// 0x6EEAAC +unsigned char to_topdown_up[58 * 13]; + +// 0x6EED9E +unsigned char scen_down[66 * 13]; + +// 0x6EF0F8 +unsigned char edec_down[18 * 23]; + +// 0x6EF296 +unsigned char to_iso_up[58 * 13]; + +// 0x6EF588 +unsigned char roof_down[58 * 13]; + +// 0x6EF87A +unsigned char r_up[18 * 23]; + +// 0x6EFA18 +unsigned char einc_down[18 * 23]; + +// 0x6EFBB6 +unsigned char shift_l_up[18 * 23]; + +// 0x6EFD54 +unsigned char edec_up[18 * 23]; + +// 0x6EFEF2 +unsigned char shift_r_up[18 * 23]; + +// 0x6F0090 +unsigned char shift_r_down[18 * 23]; + +// 0x6F022E +unsigned char r_down[18 * 23]; + +// 0x6F03CC +unsigned char einc_up[18 * 23]; + +// 0x6F056A +unsigned char l_down[18 * 23]; + +// 0x6F0708 +unsigned char shift_l_down[18 * 23]; + +// 0x6F08A6 +unsigned char l_up[18 * 23]; + +// 0x6F0A44 +unsigned char to_edit_up[45 * 43]; + +// 0x6F11D3 +unsigned char erase_up[45 * 43]; + +// 0x6F1962 +unsigned char copy_group_up[45 * 43]; + +// 0x6F20F1 +unsigned char to_paint_down[45 * 43]; + +// 0x6F2880 +unsigned char erase_down[45 * 43]; + +// 0x6F300F +unsigned char copy_group_down[45 * 43]; + +// 0x6F379E +unsigned char to_edit_down[45 * 43]; + +// 0x6F3F2D +unsigned char copy_up[49 * 19]; + +// 0x6F42D0 +unsigned char misc_down[53 * 18]; + +// 0x6F4581 +unsigned char wall_down[53 * 18]; + +// 0x6F4832 +unsigned char delete_up[49 * 19]; + +// 0x6F4BD5 +unsigned char edit_up[49 * 19]; + +// 0x6F4F78 +unsigned char tile_up[53 * 18]; + +// 0x6F5229 +unsigned char edit_down[49 * 19]; + +// 0x6F55CC +unsigned char paste_down[49 * 19]; + +// 0x6F596F +unsigned char delete_down[49 * 19]; + +// 0x6F5D12 +unsigned char tile_down[53 * 18]; + +// 0x6F5FC3 +unsigned char copy_down[49 * 19]; + +// 0x6F6366 +unsigned char misc_up[53 * 18]; + +// 0x6F6617 +unsigned char paste_up[49 * 19]; + +// 0x6F69BA +unsigned char to_paint_up[1935]; + +// 0x6F7149 +unsigned char wall_up[53 * 18]; + +// 0x6F73FA +bool draw_mode; + +// 0x6F73FB +bool view_mode; + // gnw_main // 0x485DD0 int mapper_main(int argc, char** argv) @@ -131,7 +485,733 @@ void MapperInit() // 0x485F94 int mapper_edit_init(int argc, char** argv) { - // TODO: Incomplete. + int index; + + if (gameInitWithOptions("FALLOUT Mapper", true, 2, 0, argc, argv) == -1) { + return -1; + } + + tileEnable(); + gmouse_set_mapper_mode(true); + settings.system.executable = "mapper"; + + if (settings.mapper.override_librarian) { + can_modify_protos = true; + target_override_protection(); + } + + setup_map_dirs(); + mapper_load_toolbar(4, 0); + + max_art_buttons = (_scr_size.right - _scr_size.left - 136) / 50; + art_shape = (unsigned char*)internal_malloc(art_scale_height * art_scale_width); + if (art_shape == NULL) { + printf("Can't malloc memory!!\n"); + exit(1); + } + + menu_bar = windowCreate(0, + 0, + rectGetWidth(&_scr_size), + 16, + _colorTable[0], + WINDOW_HIDDEN); + _win_register_menu_bar(menu_bar, + 0, + 0, + rectGetWidth(&_scr_size), + 16, + 260, + _colorTable[8456]); + _win_register_menu_pulldown(menu_bar, + 8, + "FILE", + 289, + 8, + menu_names[0], + 260, + _colorTable[8456]); + _win_register_menu_pulldown(menu_bar, + 40, + "TOOLS", + 303, + 21, + menu_names[1], + 260, + _colorTable[8456]); + _win_register_menu_pulldown(menu_bar, + 80, + "SCRIPTS", + 276, + 8, + menu_names[2], + 260, + _colorTable[8456]); + + if (can_modify_protos) { + _win_register_menu_pulldown(menu_bar, + 130, + "LIBRARIAN", + 292, + 6, + &(menu_1[14]), + 260, + _colorTable[8456]); + } + + tool_win = windowCreate(0, + _scr_size.bottom - 99, + rectGetWidth(&_scr_size), + 100, + 256, + 0); + tool = windowGetBuffer(tool_win); + + lbm_buf = (unsigned char*)internal_malloc(640 * 480); + load_lbm_to_buf("data\\mapper2.lbm", + lbm_buf, + rectGetWidth(&_scr_size), + 0, + 0, + _scr_size.right - _scr_size.left, + 479); + + // + blitBufferToBuffer(lbm_buf + 380 * rectGetWidth(&_scr_size), + rectGetWidth(&_scr_size), + 100, + rectGetWidth(&_scr_size), + tool, + rectGetWidth(&_scr_size)); + + // + blitBufferToBuffer(lbm_buf + 406 * (rectGetWidth(&_scr_size)) + 101, + 18, + 23, + rectGetWidth(&_scr_size), + l_up, + 18); + blitBufferToBuffer(lbm_buf + 253 * (rectGetWidth(&_scr_size)) + 101, + 18, + 23, + rectGetWidth(&_scr_size), + l_down, + 18); + buttonCreate(tool_win, + 101, + 26, + 18, + 23, + -1, + -1, + 45, + -1, + l_up, + l_down, + NULL, + 0); + + // + blitBufferToBuffer(lbm_buf + 406 * (rectGetWidth(&_scr_size)) + 622, + 18, + 23, + rectGetWidth(&_scr_size), + r_up, + 18); + blitBufferToBuffer(lbm_buf + 253 * (rectGetWidth(&_scr_size)) + 622, + 18, + 23, + rectGetWidth(&_scr_size), + r_down, + 18); + buttonCreate(tool_win, + _scr_size.right - 18, + 1, + 18, + 23, + -1, + -1, + 61, + -1, + r_up, + r_down, + NULL, + 0); + + // + blitBufferToBuffer(lbm_buf + 381 * (rectGetWidth(&_scr_size)) + 101, + 18, + 23, + rectGetWidth(&_scr_size), + shift_l_up, + 18); + blitBufferToBuffer(lbm_buf + 228 * (rectGetWidth(&_scr_size)) + 101, + 18, + 23, + rectGetWidth(&_scr_size), + shift_l_down, + 18); + buttonCreate(tool_win, + 101, + 1, + 18, + 23, + -1, + -1, + 95, + -1, + shift_l_up, + shift_l_down, + NULL, + 0); + + // + blitBufferToBuffer(lbm_buf + 381 * (rectGetWidth(&_scr_size)) + 622, + 18, + 23, + rectGetWidth(&_scr_size), + shift_r_up, + 18); + blitBufferToBuffer(lbm_buf + 228 * (rectGetWidth(&_scr_size)) + 622, + 18, + 23, + rectGetWidth(&_scr_size), + shift_r_down, + 18); + buttonCreate(tool_win, + _scr_size.right - 18, + 1, + 18, + 23, + -1, + -1, + 43, + -1, + shift_r_up, + shift_r_down, + NULL, + 0); + + // + for (index = 0; index < max_art_buttons; index++) { + int btn = buttonCreate(tool_win, + index * (art_scale_width + 1) + 121, + 1, + art_scale_width, + art_scale_height, + index + max_art_buttons + 161, + 58, + 160 + index, + -1, + NULL, + NULL, + NULL, + 0); + buttonSetRightMouseCallbacks(btn, 160 + index, -1, NULL, NULL); + } + + // ELEVATION INC + blitBufferToBuffer(lbm_buf + 431 * (rectGetWidth(&_scr_size)) + 1, + 18, + 23, + rectGetWidth(&_scr_size), + einc_up, + 18); + blitBufferToBuffer(lbm_buf + 325 * (rectGetWidth(&_scr_size)) + 1, + 18, + 23, + rectGetWidth(&_scr_size), + einc_down, + 18); + buttonCreate(tool_win, + 1, + 51, + 18, + 23, + -1, + -1, + 329, + -1, + einc_up, + einc_down, + NULL, + 0); + + // ELEVATION DEC + blitBufferToBuffer(lbm_buf + 456 * (rectGetWidth(&_scr_size)) + 1, + 18, + 23, + rectGetWidth(&_scr_size), + edec_up, + 18); + blitBufferToBuffer(lbm_buf + 350 * (rectGetWidth(&_scr_size)) + 1, + 18, + 23, + rectGetWidth(&_scr_size), + edec_down, + 18); + buttonCreate(tool_win, + 1, + 76, + 18, + 23, + -1, + -1, + 337, + -1, + edec_up, + edec_down, + NULL, + 0); + + // ELEVATION + for (index = 0; index < 4; index++) { + blitBufferToBuffer(lbm_buf + 293 * rectGetWidth(&_scr_size) + 19 * index, + 19, + 26, + rectGetWidth(&_scr_size), + e_num[1], + 19); + } + + view_mode = false; + + // + blitBufferToBuffer(lbm_buf + 169 * (rectGetWidth(&_scr_size)) + 64, + 58, + 13, + rectGetWidth(&_scr_size), + to_iso_up, + 58); + blitBufferToBuffer(lbm_buf + 108 * (rectGetWidth(&_scr_size)) + 64, + 58, + 13, + rectGetWidth(&_scr_size), + to_iso_down, + 58); + + // ROOF + blitBufferToBuffer(lbm_buf + 464 * (rectGetWidth(&_scr_size)) + 64, + 58, + 13, + rectGetWidth(&_scr_size), + roof_up, + 58); + blitBufferToBuffer(lbm_buf + 358 * (rectGetWidth(&_scr_size)) + 64, + 58, + 13, + rectGetWidth(&_scr_size), + roof_down, + 58); + roof_bid = buttonCreate(tool_win, + 64, + 69, + 58, + 13, + -1, + -1, + 'r', + 'r', + roof_up, + roof_down, + NULL, + BUTTON_FLAG_0x01); + + if (tileRoofIsVisible()) { + tile_toggle_roof(false); + } + + // HEX + blitBufferToBuffer(lbm_buf + 464 * (rectGetWidth(&_scr_size)) + 64, + 58, + 13, + rectGetWidth(&_scr_size), + hex_up, + 58); + blitBufferToBuffer(lbm_buf + 358 * (rectGetWidth(&_scr_size)) + 64, + 58, + 13, + rectGetWidth(&_scr_size), + hex_down, + 58); + hex_bid = buttonCreate(tool_win, + 64, + 84, + 58, + 13, + -1, + -1, + 350, + 350, + hex_up, + hex_down, + NULL, + BUTTON_FLAG_0x01); + + // OBJ + blitBufferToBuffer(lbm_buf + 434 * (rectGetWidth(&_scr_size)) + 125, + 66, + 13, + rectGetWidth(&_scr_size), + obj_up, + 66); + blitBufferToBuffer(lbm_buf + 328 * (rectGetWidth(&_scr_size)) + 125, + 66, + 13, + rectGetWidth(&_scr_size), + obj_down, + 66); + obj_bid = buttonCreate(tool_win, + 125, + 54, + 66, + 13, + -1, + -1, + 350, + 350, + obj_up, + obj_down, + NULL, + BUTTON_FLAG_0x01); + + // CRIT + blitBufferToBuffer(lbm_buf + 449 * (rectGetWidth(&_scr_size)) + 125, + 66, + 13, + rectGetWidth(&_scr_size), + crit_up, + 66); + blitBufferToBuffer(lbm_buf + 343 * (rectGetWidth(&_scr_size)) + 125, + 66, + 13, + rectGetWidth(&_scr_size), + crit_down, + 66); + crit_bid = buttonCreate(tool_win, + 125, + 69, + 66, + 13, + -1, + -1, + 351, + 351, + crit_up, + crit_down, + NULL, + BUTTON_FLAG_0x01); + + // SCEN + blitBufferToBuffer(lbm_buf + 434 * (rectGetWidth(&_scr_size)) + 194, + 53, + 13, + rectGetWidth(&_scr_size), + scen_up, + 53); + blitBufferToBuffer(lbm_buf + 328 * (rectGetWidth(&_scr_size)) + 194, + 53, + 13, + rectGetWidth(&_scr_size), + scen_down, + 53); + scen_bid = buttonCreate(tool_win, + 125, + 84, + 66, + 13, + -1, + -1, + 352, + 352, + scen_up, + scen_down, + NULL, + BUTTON_FLAG_0x01); + + // WALL + blitBufferToBuffer(lbm_buf + 434 * (rectGetWidth(&_scr_size)) + 194, + 53, + 13, + rectGetWidth(&_scr_size), + wall_up, + 53); + blitBufferToBuffer(lbm_buf + 328 * (rectGetWidth(&_scr_size)) + 194, + 53, + 13, + rectGetWidth(&_scr_size), + wall_down, + 53); + wall_bid = buttonCreate(tool_win, + 194, + 54, + 53, + 13, + -1, + -1, + 355, + 355, + wall_up, + wall_down, + NULL, + BUTTON_FLAG_0x01); + + // MISC + blitBufferToBuffer(lbm_buf + 464 * (rectGetWidth(&_scr_size)) + 194, + 53, + 13, + rectGetWidth(&_scr_size), + misc_up, + 53); + blitBufferToBuffer(lbm_buf + 358 * (rectGetWidth(&_scr_size)) + 194, + 53, + 13, + rectGetWidth(&_scr_size), + misc_down, + 53); + misc_bid = buttonCreate(tool_win, + 194, + 84, + 53, + 13, + -1, + -1, + 355, + 355, + misc_up, + misc_down, + NULL, + BUTTON_FLAG_0x01); + + // HEIGHT INC + blitBufferToBuffer(lbm_buf + 431 * rectGetWidth(&_scr_size) + 251, + 18, + 23, + rectGetWidth(&_scr_size), + height_inc_up, + 18); + blitBufferToBuffer(lbm_buf + 325 * rectGetWidth(&_scr_size) + 251, + 18, + 23, + rectGetWidth(&_scr_size), + height_inc_down, + 18); + buttonCreate(tool_win, + 251, + 51, + 18, + 23, + -1, + -1, + 371, + -1, + height_dec_up, + height_dec_down, + NULL, + 0); + + // HEIGHT DEC + blitBufferToBuffer(lbm_buf + 456 * rectGetWidth(&_scr_size) + 251, + 18, + 23, + rectGetWidth(&_scr_size), + height_dec_up, + 18); + blitBufferToBuffer(lbm_buf + 350 * rectGetWidth(&_scr_size) + 251, + 18, + 23, + rectGetWidth(&_scr_size), + height_dec_down, + 18); + buttonCreate(tool_win, + 251, + 76, + 18, + 23, + -1, + -1, + 371, + -1, + height_dec_up, + height_dec_down, + NULL, + 0); + + // ARROWS + for (index = 0; index < ROTATION_COUNT; index++) { + } + + // COPY + blitBufferToBuffer(lbm_buf + 435 * (rectGetWidth(&_scr_size)) + 325, + 49, + 19, + rectGetWidth(&_scr_size), + copy_up, + 49); + blitBufferToBuffer(lbm_buf + 329 * (rectGetWidth(&_scr_size)) + 325, + 49, + 19, + rectGetWidth(&_scr_size), + copy_down, + 49); + copy_bid = buttonCreate(tool_win, + 325, + 55, + 49, + 19, + -1, + -1, + 99, + -1, + copy_up, + copy_down, + 0, + 0); + + // PASTE + blitBufferToBuffer(lbm_buf + 457 * (rectGetWidth(&_scr_size)) + 325, + 49, + 19, + rectGetWidth(&_scr_size), + paste_up, + 49); + blitBufferToBuffer(lbm_buf + 351 * (rectGetWidth(&_scr_size)) + 325, + 49, + 19, + rectGetWidth(&_scr_size), + paste_down, + 49); + paste_bid = buttonCreate(tool_win, + 325, + 77, + 49, + 19, + -1, + -1, + 67, + -1, + paste_up, + paste_down, + NULL, + 0); + + // EDIT + blitBufferToBuffer(lbm_buf + 435 * (rectGetWidth(&_scr_size)) + 378, + 49, + 19, + rectGetWidth(&_scr_size), + edit_up, + 49); + blitBufferToBuffer(lbm_buf + 329 * (rectGetWidth(&_scr_size)) + 378, + 49, + 19, + rectGetWidth(&_scr_size), + edit_down, + 49); + edit_bid = buttonCreate(tool_win, + 378, + 55, + 49, + 19, + -1, + -1, + 101, + -1, + edit_up, + edit_down, + NULL, + 0); + + // DELETE + blitBufferToBuffer(lbm_buf + 457 * rectGetWidth(&_scr_size) + 378, + 49, + 19, + rectGetWidth(&_scr_size), + delete_up, + 49); + blitBufferToBuffer(lbm_buf + 351 * rectGetWidth(&_scr_size) + 378, + 49, + 19, + rectGetWidth(&_scr_size), + delete_down, + 49); + delete_bid = buttonCreate(tool_win, + 378, + 77, + 49, + 19, + -1, + -1, + 339, + -1, + delete_up, + delete_down, + NULL, + 0); + + draw_mode = false; + + blitBufferToBuffer(lbm_buf + 169 * rectGetWidth(&_scr_size) + 430, + 45, + 43, + rectGetWidth(&_scr_size), + to_edit_up, + 45); + blitBufferToBuffer(lbm_buf + 108 * rectGetWidth(&_scr_size) + 430, + 45, + 43, + rectGetWidth(&_scr_size), + to_edit_down, + 45); + + blitBufferToBuffer(lbm_buf + 169 * rectGetWidth(&_scr_size) + 327, + 45, + 43, + rectGetWidth(&_scr_size), + copy_group_up, + 45); + blitBufferToBuffer(lbm_buf + 108 * rectGetWidth(&_scr_size) + 327, + 45, + 43, + rectGetWidth(&_scr_size), + copy_group_down, + 45); + + blitBufferToBuffer(lbm_buf + 169 * rectGetWidth(&_scr_size) + 379, + 45, + 43, + rectGetWidth(&_scr_size), + erase_up, + 45); + blitBufferToBuffer(lbm_buf + 108 * rectGetWidth(&_scr_size) + 379, + 45, + 43, + rectGetWidth(&_scr_size), + erase_down, + 45); + + internal_free(lbm_buf); + windowRefresh(tool_win); + + if (bookmarkInit() == -1) { + debugPrint("\nbookmarkInit() Failed!"); + } + + if (categoryInit() == -1) { + debugPrint("\ncategoryInit() Failed!"); + } + + tileScrollBlockingDisable(); + tileScrollLimitingDisable(); + init_mapper_protos(); + _map_init(); + target_init(); + mouseShowCursor(); + + if (settings.mapper.rebuild_protos) { + proto_build_all_texts(); + settings.mapper.rebuild_protos = false; + } return 0; } @@ -249,6 +1329,12 @@ void edit_mapper() // TODO: Incomplete. } +// 0x48AFFC +void mapper_load_toolbar(int a1, int a2) +{ + // TODO: Incomplete. +} + // 0x48B16C void print_toolbar_name(int object_type) { diff --git a/src/mapper/mp_proto.cc b/src/mapper/mp_proto.cc index 3e89993..7678d9c 100644 --- a/src/mapper/mp_proto.cc +++ b/src/mapper/mp_proto.cc @@ -5,4 +5,10 @@ namespace fallout { // 0x559C60 bool can_modify_protos = false; +// 0x4922F8 +void init_mapper_protos() +{ + // TODO: Incomplete. +} + } // namespace fallout diff --git a/src/mapper/mp_proto.h b/src/mapper/mp_proto.h index 2bf6e8d..63cda50 100644 --- a/src/mapper/mp_proto.h +++ b/src/mapper/mp_proto.h @@ -5,6 +5,8 @@ namespace fallout { extern bool can_modify_protos; +void init_mapper_protos(); + } // namespace fallout #endif /* FALLOUT_MAPPER_MP_PROTO_H_ */ diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc new file mode 100644 index 0000000..b9892bd --- /dev/null +++ b/src/mapper/mp_targt.cc @@ -0,0 +1,19 @@ +#include "mapper/mp_targt.h" + +namespace fallout { + +// 0x49B2F0 +void target_override_protection() +{ + // TODO: Incomplete. +} + +// 0x49B424 +int target_init() +{ + // TODO: Incomplete. + + return 0; +} + +} // namespace fallout diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h new file mode 100644 index 0000000..9e7f071 --- /dev/null +++ b/src/mapper/mp_targt.h @@ -0,0 +1,11 @@ +#ifndef FALLOUT_MAPPER_MP_TARGT_H_ +#define FALLOUT_MAPPER_MP_TARGT_H_ + +namespace fallout { + +void target_override_protection(); +int target_init(); + +} // namespace fallout + +#endif /* FALLOUT_MAPPER_MP_TARGT_H_ */ diff --git a/src/mapper/mp_text.cc b/src/mapper/mp_text.cc new file mode 100644 index 0000000..3d40fb8 --- /dev/null +++ b/src/mapper/mp_text.cc @@ -0,0 +1,13 @@ +#include "mapper/mp_text.h" + +namespace fallout { + +// 0x49DAC4 +int proto_build_all_texts() +{ + // TODO: Incomplete. + + return 0; +} + +} // namespace fallout diff --git a/src/mapper/mp_text.h b/src/mapper/mp_text.h new file mode 100644 index 0000000..b275878 --- /dev/null +++ b/src/mapper/mp_text.h @@ -0,0 +1,10 @@ +#ifndef FALLOUT_MAPPER_MP_TEXT_H_ +#define FALLOUT_MAPPER_MP_TEXT_H_ + +namespace fallout { + +int proto_build_all_texts(); + +} // namespace fallout + +#endif /* FALLOUT_MAPPER_MP_TEXT_H_ */ From f6e9a9d975cfe85fa080b44bf8b464b4039d80f1 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 07:15:58 +0300 Subject: [PATCH 077/136] Make MapDirErase public --- src/loadsave.cc | 35 +++++++++++++++++------------------ src/loadsave.h | 1 + 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/loadsave.cc b/src/loadsave.cc index 90f09c3..a0fa76b 100644 --- a/src/loadsave.cc +++ b/src/loadsave.cc @@ -169,7 +169,6 @@ static int _GameMap2Slot(File* stream); static int _SlotMap2Game(File* stream); static int _mygets(char* dest, File* stream); static int _copy_file(const char* existingFileName, const char* newFileName); -static int _MapDirErase(const char* path, const char* extension); static int _SaveBackup(); static int _RestoreSave(); static int _LoadObjDudeCid(File* stream); @@ -340,9 +339,9 @@ void _InitLoadSave() _slot_cursor = 0; _patches = settings.system.master_patches_path.c_str(); - _MapDirErase("MAPS\\", "SAV"); - _MapDirErase(PROTO_DIR_NAME "\\" CRITTERS_DIR_NAME "\\", PROTO_FILE_EXT); - _MapDirErase(PROTO_DIR_NAME "\\" ITEMS_DIR_NAME "\\", PROTO_FILE_EXT); + MapDirErase("MAPS\\", "SAV"); + MapDirErase(PROTO_DIR_NAME "\\" CRITTERS_DIR_NAME "\\", PROTO_FILE_EXT); + MapDirErase(PROTO_DIR_NAME "\\" ITEMS_DIR_NAME "\\", PROTO_FILE_EXT); configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_AUTO_QUICK_SAVE, &quickSaveSlots); if (quickSaveSlots > 0 && quickSaveSlots <= 10) { @@ -353,9 +352,9 @@ void _InitLoadSave() // 0x47B85C void _ResetLoadSave() { - _MapDirErase("MAPS\\", "SAV"); - _MapDirErase(PROTO_DIR_NAME "\\" CRITTERS_DIR_NAME "\\", PROTO_FILE_EXT); - _MapDirErase(PROTO_DIR_NAME "\\" ITEMS_DIR_NAME "\\", PROTO_FILE_EXT); + MapDirErase("MAPS\\", "SAV"); + MapDirErase(PROTO_DIR_NAME "\\" CRITTERS_DIR_NAME "\\", PROTO_FILE_EXT); + MapDirErase(PROTO_DIR_NAME "\\" ITEMS_DIR_NAME "\\", PROTO_FILE_EXT); } // SaveGame @@ -1570,7 +1569,7 @@ static int lsgPerformSaveGame() debugPrint("\nLOADSAVE: ** Error opening save game for writing! **\n"); _RestoreSave(); snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1); - _MapDirErase(_gmpath, "BAK"); + MapDirErase(_gmpath, "BAK"); _partyMemberUnPrepSave(); backgroundSoundResume(); return -1; @@ -1583,7 +1582,7 @@ static int lsgPerformSaveGame() fileClose(_flptr); _RestoreSave(); snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1); - _MapDirErase(_gmpath, "BAK"); + MapDirErase(_gmpath, "BAK"); _partyMemberUnPrepSave(); backgroundSoundResume(); return -1; @@ -1597,7 +1596,7 @@ static int lsgPerformSaveGame() fileClose(_flptr); _RestoreSave(); snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1); - _MapDirErase(_gmpath, "BAK"); + MapDirErase(_gmpath, "BAK"); _partyMemberUnPrepSave(); backgroundSoundResume(); return -1; @@ -1678,7 +1677,7 @@ static int lsgPerformSaveGame() } snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1); - _MapDirErase(_gmpath, "BAK"); + MapDirErase(_gmpath, "BAK"); gLoadSaveMessageListItem.num = 140; if (messageListGetItem(&gLoadSaveMessageList, &gLoadSaveMessageListItem)) { @@ -1771,7 +1770,7 @@ static int lsgLoadGameInSlot(int slot) } snprintf(_str, sizeof(_str), "%s\\", "MAPS"); - _MapDirErase(_str, "BAK"); + MapDirErase(_str, "BAK"); _proto_dude_update_gender(); // Game Loaded. @@ -2487,7 +2486,7 @@ static int _GameMap2Slot(File* stream) snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1); - if (_MapDirErase(_gmpath, "SAV") == -1) { + if (MapDirErase(_gmpath, "SAV") == -1) { fileNameListFree(&fileNameList, 0); return -1; } @@ -2566,19 +2565,19 @@ static int _SlotMap2Game(File* stream) snprintf(_str0, sizeof(_str0), "%s\\", PROTO_DIR_NAME "\\" CRITTERS_DIR_NAME); - if (_MapDirErase(_str0, PROTO_FILE_EXT) == -1) { + if (MapDirErase(_str0, PROTO_FILE_EXT) == -1) { debugPrint("LOADSAVE: returning 3\n"); return -1; } snprintf(_str0, sizeof(_str0), "%s\\", PROTO_DIR_NAME "\\" ITEMS_DIR_NAME); - if (_MapDirErase(_str0, PROTO_FILE_EXT) == -1) { + if (MapDirErase(_str0, PROTO_FILE_EXT) == -1) { debugPrint("LOADSAVE: returning 4\n"); return -1; } snprintf(_str0, sizeof(_str0), "%s\\", "MAPS"); - if (_MapDirErase(_str0, "SAV") == -1) { + if (MapDirErase(_str0, "SAV") == -1) { debugPrint("LOADSAVE: returning 5\n"); return -1; } @@ -2749,11 +2748,11 @@ void lsgInit() { char path[COMPAT_MAX_PATH]; snprintf(path, sizeof(path), "%s\\", "MAPS"); - _MapDirErase(path, "SAV"); + MapDirErase(path, "SAV"); } // 0x480040 -static int _MapDirErase(const char* relativePath, const char* extension) +int MapDirErase(const char* relativePath, const char* extension) { char path[COMPAT_MAX_PATH]; snprintf(path, sizeof(path), "%s*.%s", relativePath, extension); diff --git a/src/loadsave.h b/src/loadsave.h index 32e1263..567548e 100644 --- a/src/loadsave.h +++ b/src/loadsave.h @@ -20,6 +20,7 @@ int lsgSaveGame(int mode); int lsgLoadGame(int mode); bool _isLoadingGame(); void lsgInit(); +int MapDirErase(const char* path, const char* extension); int _MapDirEraseFile_(const char* a1, const char* a2); } // namespace fallout From 57711cae5f61121c71b3e828657c2712a531a7d7 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 07:17:21 +0300 Subject: [PATCH 078/136] Add mapper_edit_exit --- src/mapper/map_func.cc | 6 ++++++ src/mapper/map_func.h | 1 + src/mapper/mapper.cc | 32 +++++++++++++++++++++++++++++++- src/mapper/mp_targt.cc | 8 ++++++++ src/mapper/mp_targt.h | 1 + 5 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/mapper/map_func.cc b/src/mapper/map_func.cc index 9d96f2e..b9f297d 100644 --- a/src/mapper/map_func.cc +++ b/src/mapper/map_func.cc @@ -8,4 +8,10 @@ void setup_map_dirs() // TODO: Incomplete. } +// 0x4826B4 +void copy_proto_lists() +{ + // TODO: Incomplete. +} + } // namespace fallout diff --git a/src/mapper/map_func.h b/src/mapper/map_func.h index fb71b2b..1292604 100644 --- a/src/mapper/map_func.h +++ b/src/mapper/map_func.h @@ -4,6 +4,7 @@ namespace fallout { void setup_map_dirs(); +void copy_proto_lists(); } // namespace fallout diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index 786b5a5..8afe3eb 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -12,6 +12,7 @@ #include "graph_lib.h" #include "inventory.h" #include "kb.h" +#include "loadsave.h" #include "mapper/map_func.h" #include "mapper/mp_proto.h" #include "mapper/mp_targt.h" @@ -97,6 +98,8 @@ static char kRebuildBinary[] = " Rebuild Binary "; static char kArtToProtos[] = " Art => New Protos "; static char kSwapPrototypse[] = " Swap Prototypes "; +static char kTmpMapName[] = "TMP$MAP#.MAP"; + // 0x559648 char* menu_0[] = { kNew, @@ -177,6 +180,9 @@ int art_scale_width = 49; // 0x559888 int art_scale_height = 48; +// 0x5598A4 +static char* tmp_map_name = kTmpMapName; + // 0x5598A8 static int bookmarkWin = -1; @@ -1219,7 +1225,31 @@ int mapper_edit_init(int argc, char** argv) // 0x48752C void mapper_edit_exit() { - // TODO: Incomplete. + remove(tmp_map_name); + remove("\\fallout\\cd\\data\\maps\\TMP$MAP#.MAP"); + remove("\\fallout\\cd\\data\\maps\\TMP$MAP#.CFG"); + + MapDirErase("MAPS\\", "SAV"); + + if (can_modify_protos) { + copy_proto_lists(); + + // NOTE: There is a call to an ambiguous function at `0x4B9ACC`, likely + // `proto_save`. + } + + target_exit(); + _map_exit(); + bookmarkExit(); + categoryExit(); + + windowDestroy(tool_win); + tool = NULL; + + windowDestroy(menu_bar); + + internal_free(art_shape); + gameExit(); } // 0x4875B4 diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index b9892bd..1bbea4a 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -16,4 +16,12 @@ int target_init() return 0; } +// 0x49B434 +int target_exit() +{ + // TODO: Incomplete. + + return 0; +} + } // namespace fallout diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h index 9e7f071..ef308e4 100644 --- a/src/mapper/mp_targt.h +++ b/src/mapper/mp_targt.h @@ -5,6 +5,7 @@ namespace fallout { void target_override_protection(); int target_init(); +int target_exit(); } // namespace fallout From 708862ca24d5d28ba957f4ff07e6b8492168d976 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 08:03:25 +0300 Subject: [PATCH 079/136] Add target_overriden --- src/mapper/mp_targt.cc | 9 +++++++++ src/mapper/mp_targt.h | 1 + 2 files changed, 10 insertions(+) diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index 1bbea4a..390fcb1 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -2,12 +2,21 @@ namespace fallout { +// 0x559DBC +static bool tgt_overriden = false; + // 0x49B2F0 void target_override_protection() { // TODO: Incomplete. } +// 0x49B2F0 +bool target_overriden() +{ + return tgt_overriden; +} + // 0x49B424 int target_init() { diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h index ef308e4..d70651c 100644 --- a/src/mapper/mp_targt.h +++ b/src/mapper/mp_targt.h @@ -4,6 +4,7 @@ namespace fallout { void target_override_protection(); +bool target_overriden(); int target_init(); int target_exit(); From ac1f4477ef81edcb74c79b4c23cb2839ed474304 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 08:08:01 +0300 Subject: [PATCH 080/136] Add target_make_path --- src/mapper/mp_targt.cc | 26 ++++++++++++++++++++++++++ src/mapper/mp_targt.h | 1 + 2 files changed, 27 insertions(+) diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index 390fcb1..0bc7aed 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -1,7 +1,18 @@ #include "mapper/mp_targt.h" +#include + +#include "art.h" +#include "proto.h" + namespace fallout { +// 0x53F354 +static char default_target_path_base[] = "\\fallout2\\dev\\proto\\"; + +// 0x559CD0 +static char* target_path_base = default_target_path_base; + // 0x559DBC static bool tgt_overriden = false; @@ -17,6 +28,21 @@ bool target_overriden() return tgt_overriden; } +// 0x49B34C +void target_make_path(char* path, int pid) +{ + if (_cd_path_base[0] != '\0' && _cd_path_base[1] == ':') { + strncpy(path, _cd_path_base, 2); + strcat(path, target_path_base); + } else { + strcpy(path, target_path_base); + } + + if (pid != -1) { + strcat(path, artGetObjectTypeName(PID_TYPE(pid))); + } +} + // 0x49B424 int target_init() { diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h index d70651c..1c0a782 100644 --- a/src/mapper/mp_targt.h +++ b/src/mapper/mp_targt.h @@ -5,6 +5,7 @@ namespace fallout { void target_override_protection(); bool target_overriden(); +void target_make_path(char* path, int pid); int target_init(); int target_exit(); From 3a271a399f7e41e955859676fc78eb0790bb2744 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 08:10:46 +0300 Subject: [PATCH 081/136] Add pick_rot --- src/mapper/mp_targt.cc | 15 +++++++++++++++ src/mapper/mp_targt.h | 1 + 2 files changed, 16 insertions(+) diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index 0bc7aed..bc0c6e1 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -4,6 +4,7 @@ #include "art.h" #include "proto.h" +#include "window_manager_private.h" namespace fallout { @@ -59,4 +60,18 @@ int target_exit() return 0; } +// 0x49BD98 +int pick_rot() +{ + int value; + win_get_num_i(&value, + -1, + 5, + false, + "Rotation", + 100, + 100); + return value; +} + } // namespace fallout diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h index 1c0a782..b27660a 100644 --- a/src/mapper/mp_targt.h +++ b/src/mapper/mp_targt.h @@ -8,6 +8,7 @@ bool target_overriden(); void target_make_path(char* path, int pid); int target_init(); int target_exit(); +int pick_rot(); } // namespace fallout From f6461a44dc3c58cdef44dcc2ed4d2b39c8891dc8 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 08:13:45 +0300 Subject: [PATCH 082/136] Add target_pick_global_var --- src/mapper/mp_targt.cc | 26 ++++++++++++++++++++++++++ src/mapper/mp_targt.h | 1 + 2 files changed, 27 insertions(+) diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index bc0c6e1..4a8f517 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -3,6 +3,7 @@ #include #include "art.h" +#include "game.h" #include "proto.h" #include "window_manager_private.h" @@ -74,4 +75,29 @@ int pick_rot() return value; } +// 0x49BDD0 +int target_pick_global_var(int* value_ptr) +{ + int value; + int rc; + + if (gGameGlobalVarsLength == 0) { + return -1; + } + + rc = win_get_num_i(&value, + 0, + gGameGlobalVarsLength - 1, + false, + "Global Variable Index #:", + 100, + 100); + if (rc == -1) { + return -1; + } + + *value_ptr = value; + return 0; +} + } // namespace fallout diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h index b27660a..b553db3 100644 --- a/src/mapper/mp_targt.h +++ b/src/mapper/mp_targt.h @@ -9,6 +9,7 @@ void target_make_path(char* path, int pid); int target_init(); int target_exit(); int pick_rot(); +int target_pick_global_var(int* value_ptr); } // namespace fallout From ca206da4af2c6013572ca91570d931c0c091dea1 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 08:17:04 +0300 Subject: [PATCH 083/136] Add target_pick_map_var --- src/map.cc | 4 ++-- src/map.h | 2 ++ src/mapper/mp_targt.cc | 26 ++++++++++++++++++++++++++ src/mapper/mp_targt.h | 1 + 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/map.cc b/src/map.cc index 8b07d6f..333935f 100644 --- a/src/map.cc +++ b/src/map.cc @@ -108,7 +108,7 @@ int* gMapLocalVars = NULL; // map_vars // 0x51956C -static int* gMapGlobalVars = NULL; +int* gMapGlobalVars = NULL; // local_vars_num // 0x519570 @@ -116,7 +116,7 @@ int gMapLocalVarsLength = 0; // map_vars_num // 0x519574 -static int gMapGlobalVarsLength = 0; +int gMapGlobalVarsLength = 0; // Current elevation. // diff --git a/src/map.h b/src/map.h index 09247f8..70e8d7f 100644 --- a/src/map.h +++ b/src/map.h @@ -69,7 +69,9 @@ typedef void IsoWindowRefreshProc(Rect* rect); extern int gMapSid; extern int* gMapLocalVars; +extern int* gMapGlobalVars; extern int gMapLocalVarsLength; +extern int gMapGlobalVarsLength; extern int gElevation; extern MessageList gMapMessageList; diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index 4a8f517..e5249d2 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -4,6 +4,7 @@ #include "art.h" #include "game.h" +#include "map.h" #include "proto.h" #include "window_manager_private.h" @@ -100,4 +101,29 @@ int target_pick_global_var(int* value_ptr) return 0; } +// 0x49BE20 +int target_pick_map_var(int* value_ptr) +{ + int value; + int rc; + + if (gMapGlobalVarsLength == 0) { + return -1; + } + + rc = win_get_num_i(&value, + 0, + gMapGlobalVarsLength - 1, + false, + "Map Variable Index #:", + 100, + 100); + if (rc == -1) { + return -1; + } + + *value_ptr = value; + return 0; +} + } // namespace fallout diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h index b553db3..d22c700 100644 --- a/src/mapper/mp_targt.h +++ b/src/mapper/mp_targt.h @@ -10,6 +10,7 @@ int target_init(); int target_exit(); int pick_rot(); int target_pick_global_var(int* value_ptr); +int target_pick_map_var(int* value_ptr); } // namespace fallout From f93aa710a424f9ee2d550c32ed44a39a864c1586 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 08:20:15 +0300 Subject: [PATCH 084/136] Add target_pick_local_var --- src/mapper/mp_targt.cc | 25 +++++++++++++++++++++++++ src/mapper/mp_targt.h | 1 + 2 files changed, 26 insertions(+) diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index e5249d2..b654011 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -126,4 +126,29 @@ int target_pick_map_var(int* value_ptr) return 0; } +// 0x49BE70 +int target_pick_local_var(int* value_ptr) +{ + int value; + int rc; + + if (gMapLocalVarsLength == 0) { + return -1; + } + + rc = win_get_num_i(&value, + 0, + gMapLocalVarsLength - 1, + false, + "Local Variable Index #:", + 100, + 100); + if (rc == -1) { + return -1; + } + + *value_ptr = value; + return 0; +} + } // namespace fallout diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h index d22c700..2a0578d 100644 --- a/src/mapper/mp_targt.h +++ b/src/mapper/mp_targt.h @@ -11,6 +11,7 @@ int target_exit(); int pick_rot(); int target_pick_global_var(int* value_ptr); int target_pick_map_var(int* value_ptr); +int target_pick_local_var(int* value_ptr); } // namespace fallout From e0e0db219c42e7feab6c43ced26ae80baab7d9ba Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 08:27:42 +0300 Subject: [PATCH 085/136] Add combat_ai_num --- src/combat_ai.cc | 6 ++++++ src/combat_ai.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/combat_ai.cc b/src/combat_ai.cc index 3ee2082..2cf475e 100644 --- a/src/combat_ai.cc +++ b/src/combat_ai.cc @@ -701,6 +701,12 @@ static int aiPacketWrite(File* stream, AiPacket* ai) return 0; } +// 0x428058 +int combat_ai_num() +{ + return gAiPacketsLength; +} + // Get ai from object // // 0x4280B4 diff --git a/src/combat_ai.h b/src/combat_ai.h index 12cf21d..e182a2c 100644 --- a/src/combat_ai.h +++ b/src/combat_ai.h @@ -30,6 +30,7 @@ void aiReset(); int aiExit(); int aiLoad(File* stream); int aiSave(File* stream); +int combat_ai_num(); int aiGetAreaAttackMode(Object* obj); int aiGetRunAwayMode(Object* obj); int aiGetBestWeapon(Object* obj); From 49804a52e6d821f0e354e3b527720878b5e240e5 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 08:29:19 +0300 Subject: [PATCH 086/136] Add combat_ai_name --- src/combat_ai.cc | 18 ++++++++++++++++++ src/combat_ai.h | 1 + 2 files changed, 19 insertions(+) diff --git a/src/combat_ai.cc b/src/combat_ai.cc index 2cf475e..2110b9d 100644 --- a/src/combat_ai.cc +++ b/src/combat_ai.cc @@ -707,6 +707,24 @@ int combat_ai_num() return gAiPacketsLength; } +// 0x428060 +char* combat_ai_name(int packet_num) +{ + int index; + + if (packet_num < 0 || packet_num >= gAiPacketsLength) { + return NULL; + } + + for (index = 0; index < gAiPacketsLength; index++) { + if (gAiPackets[index].packet_num == packet_num) { + return gAiPackets[index].name; + } + } + + return NULL; +} + // Get ai from object // // 0x4280B4 diff --git a/src/combat_ai.h b/src/combat_ai.h index e182a2c..1e11dfb 100644 --- a/src/combat_ai.h +++ b/src/combat_ai.h @@ -31,6 +31,7 @@ int aiExit(); int aiLoad(File* stream); int aiSave(File* stream); int combat_ai_num(); +char* combat_ai_name(int packet_num); int aiGetAreaAttackMode(Object* obj); int aiGetRunAwayMode(Object* obj); int aiGetBestWeapon(Object* obj); From c1258cbb4c7957cdbaa2fd6b5925bc4aea1d0360 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 08:43:27 +0300 Subject: [PATCH 087/136] Add proto_pick_ai_packet --- src/mapper/mp_proto.cc | 45 ++++++++++++++++++++++++++++++++++++++++++ src/mapper/mp_proto.h | 1 + 2 files changed, 46 insertions(+) diff --git a/src/mapper/mp_proto.cc b/src/mapper/mp_proto.cc index 7678d9c..10d36da 100644 --- a/src/mapper/mp_proto.cc +++ b/src/mapper/mp_proto.cc @@ -1,5 +1,12 @@ #include "mapper/mp_proto.h" +#include + +#include "color.h" +#include "combat_ai.h" +#include "memory.h" +#include "window_manager_private.h" + namespace fallout { // 0x559C60 @@ -11,4 +18,42 @@ void init_mapper_protos() // TODO: Incomplete. } +// 0x497568 +int proto_pick_ai_packet(int* value) +{ + int count; + char** names; + int index; + int rc; + + count = combat_ai_num(); + if (count <= 0) { + return -1; + } + + names = (char**)internal_malloc(sizeof(char*) * count); + for (index = 0; index < count; index++) { + names[index] = (char*)internal_malloc(strlen(combat_ai_name(index)) + 1); + strcpy(names[index], combat_ai_name(index)); + } + + rc = _win_list_select("AI Packet", + names, + count, + NULL, + 50, + 100, + _colorTable[15855]); + if (rc != -1) { + *value = rc; + } + + for (index = 0; index < count; index++) { + internal_free(names[index]); + } + + internal_free(names); + return 0; +} + } // namespace fallout diff --git a/src/mapper/mp_proto.h b/src/mapper/mp_proto.h index 63cda50..58a7277 100644 --- a/src/mapper/mp_proto.h +++ b/src/mapper/mp_proto.h @@ -6,6 +6,7 @@ namespace fallout { extern bool can_modify_protos; void init_mapper_protos(); +int proto_pick_ai_packet(int* value); } // namespace fallout From e9f3b3488892c3056d8582dee5906822db849ebf Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 08:55:43 +0300 Subject: [PATCH 088/136] Add mp_pick_kill_type --- src/mapper/mp_proto.cc | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/mapper/mp_proto.cc b/src/mapper/mp_proto.cc index 10d36da..a9e1e8b 100644 --- a/src/mapper/mp_proto.cc +++ b/src/mapper/mp_proto.cc @@ -4,11 +4,14 @@ #include "color.h" #include "combat_ai.h" +#include "critter.h" #include "memory.h" #include "window_manager_private.h" namespace fallout { +static int mp_pick_kill_type(); + // 0x559C60 bool can_modify_protos = false; @@ -18,6 +21,25 @@ void init_mapper_protos() // TODO: Incomplete. } +// 0x497520 +int mp_pick_kill_type() +{ + char* names[KILL_TYPE_COUNT]; + int index; + + for (index = 0; index < KILL_TYPE_COUNT; index++) { + names[index] = killTypeGetName(index); + } + + return _win_list_select("Kill Type", + names, + KILL_TYPE_COUNT, + NULL, + 50, + 100, + _colorTable[15855]); +} + // 0x497568 int proto_pick_ai_packet(int* value) { From 369a54f836d648745434adfa33ef1ac9464cfae5 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 09:04:11 +0300 Subject: [PATCH 089/136] Add critter_flag_set --- src/critter.cc | 18 ++++++++++++++++++ src/critter.h | 1 + 2 files changed, 19 insertions(+) diff --git a/src/critter.cc b/src/critter.cc index 4439eb0..50fa5f3 100644 --- a/src/critter.cc +++ b/src/critter.cc @@ -1396,4 +1396,22 @@ bool _critter_flag_check(int pid, int flag) return (proto->critter.data.flags & flag) != 0; } +// 0x42E6F0 +void critter_flag_set(int pid, int flag) +{ + Proto* proto; + + if (pid == -1) { + return; + } + + if (PID_TYPE(pid) != OBJ_TYPE_CRITTER) { + return; + } + + protoGetProto(pid, &proto); + + proto->critter.data.flags |= flag; +} + } // namespace fallout diff --git a/src/critter.h b/src/critter.h index 7423834..ea83f8d 100644 --- a/src/critter.h +++ b/src/critter.h @@ -70,6 +70,7 @@ int critterGetMovementPointCostAdjustedForCrippledLegs(Object* critter, int a2); bool critterIsEncumbered(Object* critter); bool critterIsFleeing(Object* a1); bool _critter_flag_check(int pid, int flag); +void critter_flag_set(int pid, int flag); } // namespace fallout From f3f61abc18d643a5b6d00519274e269d2b36e0db Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 09:07:02 +0300 Subject: [PATCH 090/136] Add critter_flag_unset --- src/critter.cc | 18 ++++++++++++++++++ src/critter.h | 1 + 2 files changed, 19 insertions(+) diff --git a/src/critter.cc b/src/critter.cc index 50fa5f3..c576549 100644 --- a/src/critter.cc +++ b/src/critter.cc @@ -1414,4 +1414,22 @@ void critter_flag_set(int pid, int flag) proto->critter.data.flags |= flag; } +// 0x42E71C +void critter_flag_unset(int pid, int flag) +{ + Proto* proto; + + if (pid == -1) { + return; + } + + if (PID_TYPE(pid) != OBJ_TYPE_CRITTER) { + return; + } + + protoGetProto(pid, &proto); + + proto->critter.data.flags &= ~flag; +} + } // namespace fallout diff --git a/src/critter.h b/src/critter.h index ea83f8d..ef4f873 100644 --- a/src/critter.h +++ b/src/critter.h @@ -71,6 +71,7 @@ bool critterIsEncumbered(Object* critter); bool critterIsFleeing(Object* a1); bool _critter_flag_check(int pid, int flag); void critter_flag_set(int pid, int flag); +void critter_flag_unset(int pid, int flag); } // namespace fallout From f78096917810438e70c003d7b808867c3d7824c5 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 10:46:31 +0300 Subject: [PATCH 091/136] Add proto_critter_flags_modify --- src/mapper/mp_proto.cc | 136 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/src/mapper/mp_proto.cc b/src/mapper/mp_proto.cc index a9e1e8b..a3d9ef8 100644 --- a/src/mapper/mp_proto.cc +++ b/src/mapper/mp_proto.cc @@ -6,21 +6,157 @@ #include "combat_ai.h" #include "critter.h" #include "memory.h" +#include "proto.h" #include "window_manager_private.h" namespace fallout { +#define CRITTER_FLAG_COUNT 10 + +static int proto_critter_flags_modify(int pid); static int mp_pick_kill_type(); // 0x559C60 bool can_modify_protos = false; +// 0x559C6C +static int critFlagList[CRITTER_FLAG_COUNT] = { + CRITTER_NO_STEAL, + CRITTER_NO_DROP, + CRITTER_NO_LIMBS, + CRITTER_NO_AGE, + CRITTER_NO_HEAL, + CRITTER_INVULNERABLE, + CRITTER_FLAT, + CRITTER_SPECIAL_DEATH, + CRITTER_LONG_LIMBS, + CRITTER_NO_KNOCKBACK, +}; + // 0x4922F8 void init_mapper_protos() { // TODO: Incomplete. } +// 0x496120 +int proto_critter_flags_modify(int pid) +{ + Proto* proto; + int rc; + int flags = 0; + int index; + + if (protoGetProto(pid, &proto) == -1) { + return -1; + } + + rc = win_yes_no("Can't be stolen from?", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_NO_STEAL; + } + + rc = win_yes_no("Can't Drop items?", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_NO_DROP; + } + + rc = win_yes_no("Can't lose limbs?", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_NO_LIMBS; + } + + rc = win_yes_no("Dead Bodies Can't Age?", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_NO_AGE; + } + + rc = win_yes_no("Can't Heal by Aging?", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_NO_HEAL; + } + + rc = win_yes_no("Is Invlunerable????", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_INVULNERABLE; + } + + rc = win_yes_no("Can't Flatten on Death?", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_FLAT; + } + + rc = win_yes_no("Has Special Death?", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_SPECIAL_DEATH; + } + + rc = win_yes_no("Has Extra Hand-To-Hand Range?", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_LONG_LIMBS; + } + + rc = win_yes_no("Can't be knocked back?", 340, 200, _colorTable[15855]); + if (rc == -1) { + return -1; + } + + if (rc == 1) { + flags |= CRITTER_NO_KNOCKBACK; + } + + if (!can_modify_protos) { + win_timed_msg("Can't modify protos!", _colorTable[31744] | 0x10000); + return -1; + } + + for (index = 0; index < CRITTER_FLAG_COUNT; index++) { + if ((critFlagList[index] & flags) != 0) { + critter_flag_set(pid, critFlagList[index]); + } else { + critter_flag_unset(pid, critFlagList[index]); + } + } + + return 0; +} + // 0x497520 int mp_pick_kill_type() { From c5ad0c294fd76cf4e42e8326a5dc6cc55a7ce114 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 10:53:04 +0300 Subject: [PATCH 092/136] Add proto_critter_flags_redraw --- src/mapper/mp_proto.cc | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/mapper/mp_proto.cc b/src/mapper/mp_proto.cc index a3d9ef8..fc03401 100644 --- a/src/mapper/mp_proto.cc +++ b/src/mapper/mp_proto.cc @@ -7,12 +7,14 @@ #include "critter.h" #include "memory.h" #include "proto.h" +#include "window_manager.h" #include "window_manager_private.h" namespace fallout { #define CRITTER_FLAG_COUNT 10 +static void proto_critter_flags_redraw(int win, int pid); static int proto_critter_flags_modify(int pid); static int mp_pick_kill_type(); @@ -33,12 +35,45 @@ static int critFlagList[CRITTER_FLAG_COUNT] = { CRITTER_NO_KNOCKBACK, }; +// 0x559C94 +static const char* critFlagStrs[CRITTER_FLAG_COUNT] = { + "_Steal", + "_Drop", + "_Limbs", + "_Ages", + "_Heal", + "Invuln.,", + "_Flattens", + "Special", + "Rng", + "_Knock", +}; + // 0x4922F8 void init_mapper_protos() { // TODO: Incomplete. } +// 0x4960B8 +void proto_critter_flags_redraw(int win, int pid) +{ + int index; + int color; + int x = 110; + + for (index = 0; index < CRITTER_FLAG_COUNT; index++) { + if (_critter_flag_check(pid, critFlagList[index])) { + color = _colorTable[992]; + } else { + color = _colorTable[10570]; + } + + windowDrawText(win, critFlagStrs[index], 44, x, 195, color | 0x10000); + x += 48; + } +} + // 0x496120 int proto_critter_flags_modify(int pid) { From 6ce6639f10edaf11b7bfc65626701f4467f6d2e2 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 10:57:31 +0300 Subject: [PATCH 093/136] Add proto_wall_light_str --- src/mapper/mp_proto.cc | 36 ++++++++++++++++++++++++++++++++++++ src/mapper/mp_proto.h | 1 + 2 files changed, 37 insertions(+) diff --git a/src/mapper/mp_proto.cc b/src/mapper/mp_proto.cc index fc03401..8adc13f 100644 --- a/src/mapper/mp_proto.cc +++ b/src/mapper/mp_proto.cc @@ -18,6 +18,16 @@ static void proto_critter_flags_redraw(int win, int pid); static int proto_critter_flags_modify(int pid); static int mp_pick_kill_type(); +// 0x559B94 +static const char* wall_light_strs[] = { + "North/South", + "East/West", + "North Corner", + "South Corner", + "East Corner", + "West Corner", +}; + // 0x559C60 bool can_modify_protos = false; @@ -55,6 +65,32 @@ void init_mapper_protos() // TODO: Incomplete. } +// 0x495438 +const char* proto_wall_light_str(int flags) +{ + if ((flags & 0x8000000) != 0) { + return wall_light_strs[1]; + } + + if ((flags & 0x10000000) != 0) { + return wall_light_strs[2]; + } + + if ((flags & 0x20000000) != 0) { + return wall_light_strs[3]; + } + + if ((flags & 0x40000000) != 0) { + return wall_light_strs[4]; + } + + if ((flags & 0x80000000) != 0) { + return wall_light_strs[5]; + } + + return wall_light_strs[0]; +} + // 0x4960B8 void proto_critter_flags_redraw(int win, int pid) { diff --git a/src/mapper/mp_proto.h b/src/mapper/mp_proto.h index 58a7277..e6a520e 100644 --- a/src/mapper/mp_proto.h +++ b/src/mapper/mp_proto.h @@ -6,6 +6,7 @@ namespace fallout { extern bool can_modify_protos; void init_mapper_protos(); +const char* proto_wall_light_str(int flags); int proto_pick_ai_packet(int* value); } // namespace fallout From f1f4cac316f231f07ba13434bc96dcccd036c383 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 15:15:06 +0300 Subject: [PATCH 094/136] Add proto_choose_container_flags --- src/mapper/mp_proto.cc | 148 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/src/mapper/mp_proto.cc b/src/mapper/mp_proto.cc index 8adc13f..1133674 100644 --- a/src/mapper/mp_proto.cc +++ b/src/mapper/mp_proto.cc @@ -5,8 +5,11 @@ #include "color.h" #include "combat_ai.h" #include "critter.h" +#include "input.h" +#include "kb.h" #include "memory.h" #include "proto.h" +#include "svga.h" #include "window_manager.h" #include "window_manager_private.h" @@ -14,10 +17,17 @@ namespace fallout { #define CRITTER_FLAG_COUNT 10 +#define YES 0 +#define NO 1 + +static int proto_choose_container_flags(Proto* proto); static void proto_critter_flags_redraw(int win, int pid); static int proto_critter_flags_modify(int pid); static int mp_pick_kill_type(); +static char kYes[] = "YES"; +static char kNo[] = "NO"; + // 0x559B94 static const char* wall_light_strs[] = { "North/South", @@ -28,6 +38,15 @@ static const char* wall_light_strs[] = { "West Corner", }; +// 0x559C50 +static char* yesno[] = { + kYes, + kNo, +}; + +// 0x559C58 +int edit_window_color = 1; + // 0x559C60 bool can_modify_protos = false; @@ -65,6 +84,135 @@ void init_mapper_protos() // TODO: Incomplete. } +// 0x492840 +int proto_choose_container_flags(Proto* proto) +{ + int win = windowCreate(320, + 185, + 220, + 205, + edit_window_color, + WINDOW_MOVE_ON_TOP); + if (win == -1) { + return -1; + } + + _win_register_text_button(win, + 10, + 11, + -1, + -1, + -1, + '1', + "Magic Hands Grnd", + 0); + + if ((proto->item.data.container.openFlags & 0x1) != 0) { + windowDrawText(win, + yesno[YES], + 50, + 125, + 15, + _colorTable[32747] | 0x10000); + } else { + windowDrawText(win, + yesno[NO], + 50, + 125, + 15, + _colorTable[32747] | 0x10000); + } + + _win_register_text_button(win, + 10, + 32, + -1, + -1, + -1, + '2', + "Cannot Pick Up", + 0); + + if (_proto_action_can_pickup(proto->pid)) { + windowDrawText(win, + yesno[YES], + 50, + 125, + 36, + _colorTable[32747] | 0x10000); + } else { + windowDrawText(win, + yesno[NO], + 50, + 125, + 36, + _colorTable[32747] | 0x10000); + } + + windowDrawBorder(win); + windowRefresh(win); + + while (1) { + sharedFpsLimiter.mark(); + + int input = inputGetInput(); + if (input == KEY_ESCAPE + || input == KEY_BAR + || input == KEY_RETURN) { + break; + } + + if (input == '1') { + proto->item.data.container.openFlags ^= 0x1; + + if ((proto->item.data.container.openFlags & 0x1) != 0) { + windowDrawText(win, + yesno[YES], + 50, + 125, + 15, + _colorTable[32747] | 0x10000); + } else { + windowDrawText(win, + yesno[NO], + 50, + 125, + 15, + _colorTable[32747] | 0x10000); + } + + windowRefresh(win); + } else if (input == '2') { + proto->item.extendedFlags ^= 0x8000; + + if (_proto_action_can_pickup(proto->pid)) { + windowDrawText(win, + yesno[YES], + 50, + 125, + 36, + _colorTable[32747] | 0x10000); + } else { + windowDrawText(win, + yesno[NO], + 50, + 125, + 36, + _colorTable[32747] | 0x10000); + } + + windowRefresh(win); + } + + renderPresent(); + sharedFpsLimiter.throttle(); + } + + windowDestroy(win); + + return 0; +} + // 0x495438 const char* proto_wall_light_str(int flags) { From 1de7ef015184d6c1f980a98240179039555a7f64 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 24 Jul 2023 15:19:18 +0300 Subject: [PATCH 095/136] Add init_mapper_protos --- src/mapper/mp_proto.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mapper/mp_proto.cc b/src/mapper/mp_proto.cc index 1133674..513fb57 100644 --- a/src/mapper/mp_proto.cc +++ b/src/mapper/mp_proto.cc @@ -7,6 +7,7 @@ #include "critter.h" #include "input.h" #include "kb.h" +#include "mapper/mp_targt.h" #include "memory.h" #include "proto.h" #include "svga.h" @@ -81,7 +82,8 @@ static const char* critFlagStrs[CRITTER_FLAG_COUNT] = { // 0x4922F8 void init_mapper_protos() { - // TODO: Incomplete. + edit_window_color = _colorTable[10570]; + can_modify_protos = target_overriden(); } // 0x492840 From 8329d2fcf6818e970397ae2484d3623a4c6a5f33 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 25 Jul 2023 11:14:41 +0300 Subject: [PATCH 096/136] Add map_scr_remove_all_spatials --- src/mapper/mp_scrpt.cc | 55 ++++++++++++++++++++++++++++++++++++++++++ src/mapper/mp_scrpt.h | 10 ++++++++ 2 files changed, 65 insertions(+) create mode 100644 src/mapper/mp_scrpt.cc create mode 100644 src/mapper/mp_scrpt.h diff --git a/src/mapper/mp_scrpt.cc b/src/mapper/mp_scrpt.cc new file mode 100644 index 0000000..5a100f9 --- /dev/null +++ b/src/mapper/mp_scrpt.cc @@ -0,0 +1,55 @@ +#include "mapper/mp_scrpt.h" + +#include "art.h" +#include "object.h" +#include "scripts.h" +#include "tile.h" + +namespace fallout { + +// 0x49B214 +int map_scr_remove_all_spatials() +{ + int elevation; + Script* scr; + Object* obj; + int sid; + + for (elevation = 0; elevation < ELEVATION_COUNT; elevation++) { + scr = scriptGetFirstSpatialScript(elevation); + while (scr != NULL) { + scriptRemove(scr->sid); + + scr = scriptGetFirstSpatialScript(elevation); + } + + obj = objectFindFirstAtElevation(elevation); + while (obj != NULL) { + if (buildFid(OBJ_TYPE_INTERFACE, 3, 0, 0, 0) == obj->fid) { + objectDestroy(obj, NULL); + + obj = objectFindFirstAtElevation(elevation); + continue; + } + + obj = objectFindNextAtElevation(); + } + } + + tileWindowRefresh(); + + for (sid = 0; sid < 15000; sid++) { + if (scriptGetScript(sid, &scr) != -1) { + if (scr->owner != NULL) { + if (scr->owner->pid == 0x500000C) { + scr->owner->sid = -1; + scriptRemove(sid); + } + } + } + } + + return 0; +} + +} // namespace fallout diff --git a/src/mapper/mp_scrpt.h b/src/mapper/mp_scrpt.h new file mode 100644 index 0000000..6912a8e --- /dev/null +++ b/src/mapper/mp_scrpt.h @@ -0,0 +1,10 @@ +#ifndef FALLOUT_MAPPER_MP_SCRPTR_H_ +#define FALLOUT_MAPPER_MP_SCRPTR_H_ + +namespace fallout { + +int map_scr_remove_all_spatials(); + +} // namespace fallout + +#endif /* FALLOUT_MAPPER_MP_SCRPTR_H_ */ From cba1fc8f61fb7bb5f4ef5c386356c9c7a6eef694 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 25 Jul 2023 11:56:20 +0300 Subject: [PATCH 097/136] Add map_scr_remove_spatial --- src/mapper/mp_scrpt.cc | 35 +++++++++++++++++++++++++++++++++++ src/mapper/mp_scrpt.h | 1 + 2 files changed, 36 insertions(+) diff --git a/src/mapper/mp_scrpt.cc b/src/mapper/mp_scrpt.cc index 5a100f9..eb2f9c6 100644 --- a/src/mapper/mp_scrpt.cc +++ b/src/mapper/mp_scrpt.cc @@ -7,6 +7,41 @@ namespace fallout { +// 0x49B170 +int map_scr_remove_spatial(int tile, int elevation) +{ + Script* scr; + Object* obj; + Rect rect; + + scr = scriptGetFirstSpatialScript(elevation); + while (scr != NULL) { + if (builtTileGetTile(scr->sp.built_tile) == tile) { + scriptRemove(scr->sid); + + scr = scriptGetFirstSpatialScript(elevation); + continue; + } + + scr = scriptGetNextSpatialScript(); + } + + obj = objectFindFirstAtElevation(elevation); + while (obj != NULL) { + if (obj->tile == tile && buildFid(OBJ_TYPE_INTERFACE, 3, 0, 0, 0) == obj->fid) { + objectDestroy(obj, &rect); + tileWindowRefreshRect(&rect, elevation); + + obj = objectFindFirstAtElevation(elevation); + continue; + } + + obj = objectFindNextAtElevation(); + } + + return 0; +} + // 0x49B214 int map_scr_remove_all_spatials() { diff --git a/src/mapper/mp_scrpt.h b/src/mapper/mp_scrpt.h index 6912a8e..64c2d59 100644 --- a/src/mapper/mp_scrpt.h +++ b/src/mapper/mp_scrpt.h @@ -3,6 +3,7 @@ namespace fallout { +int map_scr_remove_spatial(int tile, int elevation); int map_scr_remove_all_spatials(); } // namespace fallout From f14f37f970a8ceb686a6a5e0bd7b165b3395b717 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 14:39:31 +0300 Subject: [PATCH 098/136] Add target_override_protection --- src/mapper/mp_proto.cc | 6 ++++++ src/mapper/mp_proto.h | 1 + src/mapper/mp_targt.cc | 13 ++++++++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/mapper/mp_proto.cc b/src/mapper/mp_proto.cc index 513fb57..2b0ef90 100644 --- a/src/mapper/mp_proto.cc +++ b/src/mapper/mp_proto.cc @@ -29,6 +29,12 @@ static int mp_pick_kill_type(); static char kYes[] = "YES"; static char kNo[] = "NO"; +// 0x53DAFC +static char default_proto_builder_name[36] = "EVERTS SCOTTY"; + +// 0x559924 +char* proto_builder_name = default_proto_builder_name; + // 0x559B94 static const char* wall_light_strs[] = { "North/South", diff --git a/src/mapper/mp_proto.h b/src/mapper/mp_proto.h index e6a520e..e4e1443 100644 --- a/src/mapper/mp_proto.h +++ b/src/mapper/mp_proto.h @@ -3,6 +3,7 @@ namespace fallout { +extern char* proto_builder_name; extern bool can_modify_protos; void init_mapper_protos(); diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index b654011..1ea92df 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -5,6 +5,7 @@ #include "art.h" #include "game.h" #include "map.h" +#include "mapper/mp_proto.h" #include "proto.h" #include "window_manager_private.h" @@ -22,7 +23,17 @@ static bool tgt_overriden = false; // 0x49B2F0 void target_override_protection() { - // TODO: Incomplete. + char* name; + + tgt_overriden = true; + + name = getenv("MAIL_NAME"); + if (name != NULL) { + // NOTE: Unsafe, backing buffer is 32 byte max. + strcpy(proto_builder_name, strupr(name)); + } else { + strcpy(proto_builder_name, "UNKNOWN"); + } } // 0x49B2F0 From 59c43590cca46147f245265a738f1148cfe5f3f9 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 15:29:59 +0300 Subject: [PATCH 099/136] Add target_header_save --- src/mapper/mp_targt.cc | 34 ++++++++++++++++++++++++++++++++++ src/mapper/mp_targt.h | 1 + 2 files changed, 35 insertions(+) diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index 1ea92df..7d7799b 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -11,9 +11,20 @@ namespace fallout { +#define TARGET_DAT "target.dat" + +typedef struct TargetList { + int field_0; + int field_4; + int field_8; +} TargetList; + // 0x53F354 static char default_target_path_base[] = "\\fallout2\\dev\\proto\\"; +// 0x559CC4 +static TargetList targetlist = { 0 }; + // 0x559CD0 static char* target_path_base = default_target_path_base; @@ -73,6 +84,29 @@ int target_exit() return 0; } +// 0x49B454 +int target_header_save() +{ + char path[COMPAT_MAX_PATH]; + FILE* stream; + + target_make_path(path, -1); + strcat(path, TARGET_DAT); + + stream = fopen(path, "wb"); + if (stream == NULL) { + return -1; + } + + if (fwrite(&targetlist, sizeof(targetlist), 1, stream) != 1) { + // FIXME: Leaking `stream`. + return -1; + } + + fclose(stream); + return 0; +} + // 0x49BD98 int pick_rot() { diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h index 2a0578d..1fd3d19 100644 --- a/src/mapper/mp_targt.h +++ b/src/mapper/mp_targt.h @@ -8,6 +8,7 @@ bool target_overriden(); void target_make_path(char* path, int pid); int target_init(); int target_exit(); +int target_header_save(); int pick_rot(); int target_pick_global_var(int* value_ptr); int target_pick_map_var(int* value_ptr); From ed7ffac816e3bab34586d2c6c00664be7f416859 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 15:31:23 +0300 Subject: [PATCH 100/136] Add target_header_load --- src/mapper/mp_targt.cc | 26 ++++++++++++++++++++++++++ src/mapper/mp_targt.h | 1 + 2 files changed, 27 insertions(+) diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index 7d7799b..c5f1cff 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -107,6 +107,32 @@ int target_header_save() return 0; } +// 0x49B4E8 +int target_header_load() +{ + char path[COMPAT_MAX_PATH]; + FILE* stream; + + target_make_path(path, -1); + strcat(path, TARGET_DAT); + + stream = fopen(path, "rb"); + if (stream == NULL) { + return -1; + } + + if (fread(&targetlist, sizeof(targetlist), 1, stream) != 1) { + // FIXME: Leaking `stream`. + return -1; + } + + targetlist.field_0 = 0; + targetlist.field_4 = 0; + + fclose(stream); + return 0; +} + // 0x49BD98 int pick_rot() { diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h index 1fd3d19..af47cc9 100644 --- a/src/mapper/mp_targt.h +++ b/src/mapper/mp_targt.h @@ -9,6 +9,7 @@ void target_make_path(char* path, int pid); int target_init(); int target_exit(); int target_header_save(); +int target_header_load(); int pick_rot(); int target_pick_global_var(int* value_ptr); int target_pick_map_var(int* value_ptr); From c3a6d07dc41cb4c8fc29655579b63e0e9e97433d Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 16:24:07 +0300 Subject: [PATCH 101/136] Add target_find_free_subnode --- src/mapper/mp_targt.cc | 37 ++++++++++++++++++++++++++++++----- src/mapper/mp_targt.h | 44 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index c5f1cff..6d2a75a 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -6,6 +6,7 @@ #include "game.h" #include "map.h" #include "mapper/mp_proto.h" +#include "memory.h" #include "proto.h" #include "window_manager_private.h" @@ -13,9 +14,14 @@ namespace fallout { #define TARGET_DAT "target.dat" +typedef struct TargetNode { + TargetSubNode subnode; + struct TargetNode* next; +} TargetNode; + typedef struct TargetList { - int field_0; - int field_4; + TargetNode* tail; + int count; int field_8; } TargetList; @@ -23,7 +29,7 @@ typedef struct TargetList { static char default_target_path_base[] = "\\fallout2\\dev\\proto\\"; // 0x559CC4 -static TargetList targetlist = { 0 }; +static TargetList targetlist = { NULL, 0, 0 }; // 0x559CD0 static char* target_path_base = default_target_path_base; @@ -126,13 +132,34 @@ int target_header_load() return -1; } - targetlist.field_0 = 0; - targetlist.field_4 = 0; + targetlist.tail = NULL; + targetlist.count = 0; fclose(stream); return 0; } +// 0x49B9C0 +int target_find_free_subnode(TargetSubNode** subnode_ptr) +{ + TargetNode* node = (TargetNode*)internal_malloc(sizeof(TargetNode)); + if (node == NULL) { + *subnode_ptr = NULL; + return -1; + } + + *subnode_ptr = &(node->subnode); + + node->subnode.field_0 = -1; + node->subnode.field_28 = 0; + node->next = targetlist.tail; + + targetlist.tail = node; + targetlist.count++; + + return 0; +} + // 0x49BD98 int pick_rot() { diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h index af47cc9..2769cc4 100644 --- a/src/mapper/mp_targt.h +++ b/src/mapper/mp_targt.h @@ -3,6 +3,49 @@ namespace fallout { +typedef struct TargetSubNode { + int field_0; + int field_4; + int field_8; + int field_C; + int field_10; + int field_14; + int field_18; + int field_1C; + int field_20; + int field_24; + int field_28; + int field_2C; + int field_30; + int field_34; + int field_38; + int field_3C; + int field_40; + int field_44; + int field_48; + int field_4C; + int field_50; + int field_54; + int field_58; + int field_5C; + int field_60; + int field_64; + int field_68; + int field_6C; + int field_70; + int field_74; + int field_78; + int field_7C; + int field_80; + int field_84; + int field_88; + int field_8C; + int field_90; + int field_94; + int field_98; + int field_9C; +} TargetSubNode; + void target_override_protection(); bool target_overriden(); void target_make_path(char* path, int pid); @@ -10,6 +53,7 @@ int target_init(); int target_exit(); int target_header_save(); int target_header_load(); +int target_find_free_subnode(TargetSubNode** subnode_ptr); int pick_rot(); int target_pick_global_var(int* value_ptr); int target_pick_map_var(int* value_ptr); From fd3cd887b6d76b542773f9eeee161a0ff169f114 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 16:40:18 +0300 Subject: [PATCH 102/136] Add target_load --- src/mapper/mp_targt.cc | 56 +++++++++++++++++++++++++++++++++++++++++- src/mapper/mp_targt.h | 3 ++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index 6d2a75a..6d4ba60 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -139,6 +139,60 @@ int target_header_load() return 0; } +// 0x49B6BC +int target_load(int pid, TargetSubNode** subnode_ptr) +{ + char path[COMPAT_MAX_PATH]; + size_t len; + char* extension; + FILE* stream; + TargetSubNode* subnode; + + target_make_path(path, pid); + + len = strlen(path); + path[len] = '\\'; + _proto_list_str(pid, path + len + 1); + + extension = strchr(path + len + 1, '.'); + if (extension != NULL) { + strcpy(extension + 1, "tgt"); + } else { + strcat(path, ".tgt"); + } + + stream = fopen(path, "rb"); + if (stream == NULL) { + *subnode_ptr = NULL; + return -1; + } + + if (target_find_free_subnode(&subnode) == -1) { + *subnode_ptr = NULL; + // FIXME: Leaks `stream`. + return -1; + } + + fread(subnode, sizeof(TargetSubNode), 1, stream); + + *subnode_ptr = subnode; + + while (subnode->next != NULL) { + subnode->next = (TargetSubNode*)internal_malloc(sizeof(TargetSubNode)); + if (subnode->next == NULL) { + // FIXME: Leaks `stream`. + return -1; + } + + subnode = subnode->next; + fread(subnode, sizeof(TargetSubNode), 1, stream); + } + + fclose(stream); + + return 0; +} + // 0x49B9C0 int target_find_free_subnode(TargetSubNode** subnode_ptr) { @@ -151,7 +205,7 @@ int target_find_free_subnode(TargetSubNode** subnode_ptr) *subnode_ptr = &(node->subnode); node->subnode.field_0 = -1; - node->subnode.field_28 = 0; + node->subnode.next = NULL; node->next = targetlist.tail; targetlist.tail = node; diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h index 2769cc4..d85b20f 100644 --- a/src/mapper/mp_targt.h +++ b/src/mapper/mp_targt.h @@ -14,7 +14,7 @@ typedef struct TargetSubNode { int field_1C; int field_20; int field_24; - int field_28; + struct TargetSubNode* next; int field_2C; int field_30; int field_34; @@ -53,6 +53,7 @@ int target_init(); int target_exit(); int target_header_save(); int target_header_load(); +int target_load(int pid, TargetSubNode** subnode_ptr); int target_find_free_subnode(TargetSubNode** subnode_ptr); int pick_rot(); int target_pick_global_var(int* value_ptr); From 25a3de62e7b67227727c8f347b8cbd9aebb7131a Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 17:05:26 +0300 Subject: [PATCH 103/136] Add target_ptr --- src/mapper/mp_targt.cc | 14 ++++++++++++++ src/mapper/mp_targt.h | 1 + 2 files changed, 15 insertions(+) diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index 6d4ba60..d920c71 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -214,6 +214,20 @@ int target_find_free_subnode(TargetSubNode** subnode_ptr) return 0; } +// 0x49BD00 +int target_ptr(int pid, TargetSubNode** subnode_ptr) +{ + TargetNode* node = targetlist.tail; + while (node != NULL) { + if (node->subnode.field_0 == pid) { + *subnode_ptr = &(node->subnode); + return 0; + } + } + + return target_load(pid, subnode_ptr); +} + // 0x49BD98 int pick_rot() { diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h index d85b20f..e169605 100644 --- a/src/mapper/mp_targt.h +++ b/src/mapper/mp_targt.h @@ -55,6 +55,7 @@ int target_header_save(); int target_header_load(); int target_load(int pid, TargetSubNode** subnode_ptr); int target_find_free_subnode(TargetSubNode** subnode_ptr); +int target_ptr(int pid, TargetSubNode** subnode_ptr); int pick_rot(); int target_pick_global_var(int* value_ptr); int target_pick_map_var(int* value_ptr); From caf28a728609c6d37ae5f1e56491b36ec8ae794e Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 17:07:43 +0300 Subject: [PATCH 104/136] Add target_tid_ptr --- src/mapper/mp_targt.cc | 18 ++++++++++++++++++ src/mapper/mp_targt.h | 1 + 2 files changed, 19 insertions(+) diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index d920c71..c9da5ff 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -228,6 +228,24 @@ int target_ptr(int pid, TargetSubNode** subnode_ptr) return target_load(pid, subnode_ptr); } +// 0x49BD38 +int target_tid_ptr(int pid, int tid, TargetSubNode** subnode_ptr) +{ + TargetSubNode* subnode; + if (target_ptr(pid, &subnode) == -1) { + return -1; + } + + while (subnode != NULL) { + if (subnode->field_4 == tid) { + *subnode_ptr = subnode; + return 0; + } + } + + return -1; +} + // 0x49BD98 int pick_rot() { diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h index e169605..1c830c8 100644 --- a/src/mapper/mp_targt.h +++ b/src/mapper/mp_targt.h @@ -56,6 +56,7 @@ int target_header_load(); int target_load(int pid, TargetSubNode** subnode_ptr); int target_find_free_subnode(TargetSubNode** subnode_ptr); int target_ptr(int pid, TargetSubNode** subnode_ptr); +int target_tid_ptr(int pid, int tid, TargetSubNode** subnode_ptr); int pick_rot(); int target_pick_global_var(int* value_ptr); int target_pick_map_var(int* value_ptr); From 96573a7209a9c138ada8084fe1ac654a3e005fdf Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 17:14:49 +0300 Subject: [PATCH 105/136] Add target_remove_all --- src/mapper/mp_targt.cc | 28 ++++++++++++++++++++++++++++ src/mapper/mp_targt.h | 1 + 2 files changed, 29 insertions(+) diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index c9da5ff..c72ca2a 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -214,6 +214,34 @@ int target_find_free_subnode(TargetSubNode** subnode_ptr) return 0; } +// 0x49BCBC +int target_remove_all() +{ + TargetNode* node; + TargetNode* node_next; + TargetSubNode* subnode; + TargetSubNode* subnode_next; + + node = targetlist.tail; + targetlist.tail = NULL; + + while (node != NULL) { + node_next = node->next; + + subnode = node->subnode.next; + while (subnode != NULL) { + subnode_next = subnode->next; + internal_free(subnode); + subnode = subnode_next; + } + + internal_free(node); + node = node_next; + } + + return 0; +} + // 0x49BD00 int target_ptr(int pid, TargetSubNode** subnode_ptr) { diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h index 1c830c8..9c65ed5 100644 --- a/src/mapper/mp_targt.h +++ b/src/mapper/mp_targt.h @@ -55,6 +55,7 @@ int target_header_save(); int target_header_load(); int target_load(int pid, TargetSubNode** subnode_ptr); int target_find_free_subnode(TargetSubNode** subnode_ptr); +int target_remove_all(); int target_ptr(int pid, TargetSubNode** subnode_ptr); int target_tid_ptr(int pid, int tid, TargetSubNode** subnode_ptr); int pick_rot(); From 05b4c125e6420932109435d83781a574b38a9b47 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 17:19:51 +0300 Subject: [PATCH 106/136] Add target_save --- src/mapper/mp_targt.cc | 41 +++++++++++++++++++++++++++++++++++++++++ src/mapper/mp_targt.h | 1 + 2 files changed, 42 insertions(+) diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index c72ca2a..12b9cc9 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -139,6 +139,47 @@ int target_header_load() return 0; } +// 0x49B58C +int target_save(int pid) +{ + char path[COMPAT_MAX_PATH]; + size_t len; + char* extension; + FILE* stream; + TargetSubNode* subnode; + + if (target_ptr(pid, &subnode) == -1) { + return -1; + } + + target_make_path(path, pid); + + len = strlen(path); + path[len] = '\\'; + _proto_list_str(pid, path + len + 1); + + extension = strchr(path + len + 1, '.'); + if (extension != NULL) { + strcpy(extension + 1, "tgt"); + } else { + strcat(path, ".tgt"); + } + + stream = fopen(path, "wb"); + if (stream == NULL) { + return -1; + } + + while (subnode != NULL) { + fwrite(subnode, sizeof(TargetSubNode), 1, stream); + subnode = subnode->next; + } + + fclose(stream); + + return 0; +} + // 0x49B6BC int target_load(int pid, TargetSubNode** subnode_ptr) { diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h index 9c65ed5..db84194 100644 --- a/src/mapper/mp_targt.h +++ b/src/mapper/mp_targt.h @@ -53,6 +53,7 @@ int target_init(); int target_exit(); int target_header_save(); int target_header_load(); +int target_save(int pid); int target_load(int pid, TargetSubNode** subnode_ptr); int target_find_free_subnode(TargetSubNode** subnode_ptr); int target_remove_all(); From 29e81f3bf46283783c43680c38a7acacff57f455 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 17:37:21 +0300 Subject: [PATCH 107/136] Add target_remove --- src/mapper/mp_targt.cc | 36 ++++++++++++++++++++++++++++++++++++ src/mapper/mp_targt.h | 1 + 2 files changed, 37 insertions(+) diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index 12b9cc9..b0c4595 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -255,6 +255,42 @@ int target_find_free_subnode(TargetSubNode** subnode_ptr) return 0; } +// 0x49BBD4 +int target_remove(int pid) +{ + TargetNode* node; + TargetSubNode* subnode; + TargetSubNode* subnode_next; + + node = targetlist.tail; + while (node != NULL) { + if (node->subnode.field_0 == pid) { + break; + } + node = node->next; + } + + if (node == NULL) { + return -1; + } + + subnode = node->subnode.next; + + if (node == targetlist.tail) { + targetlist.tail = targetlist.tail->next; + } + + internal_free(node); + + while (subnode != NULL) { + subnode_next = subnode->next; + internal_free(subnode); + subnode = subnode_next; + } + + return 0; +} + // 0x49BCBC int target_remove_all() { diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h index db84194..3c23959 100644 --- a/src/mapper/mp_targt.h +++ b/src/mapper/mp_targt.h @@ -56,6 +56,7 @@ int target_header_load(); int target_save(int pid); int target_load(int pid, TargetSubNode** subnode_ptr); int target_find_free_subnode(TargetSubNode** subnode_ptr); +int target_remove(int pid); int target_remove_all(); int target_ptr(int pid, TargetSubNode** subnode_ptr); int target_tid_ptr(int pid, int tid, TargetSubNode** subnode_ptr); From fe47d88035d385c39f633b2c1db0d4070ff7f02c Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 17:47:43 +0300 Subject: [PATCH 108/136] Add target_remove_tid --- src/mapper/mp_targt.cc | 46 ++++++++++++++++++++++++++++++++++++++++++ src/mapper/mp_targt.h | 1 + 2 files changed, 47 insertions(+) diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index b0c4595..5d41fbc 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -291,6 +291,52 @@ int target_remove(int pid) return 0; } +// 0x49BC3C +int target_remove_tid(int pid, int tid) +{ + TargetNode* node; + TargetSubNode* subnode; + TargetSubNode* subnode_next; + + node = targetlist.tail; + while (node != NULL) { + if (node->subnode.field_0 == pid) { + break; + } + node = node->next; + } + + if (node == NULL) { + return -1; + } + + if (node->subnode.field_4 == tid) { + subnode_next = node->subnode.next; + if (subnode_next != NULL) { + memcpy(&(node->subnode), subnode_next, sizeof(TargetSubNode)); + internal_free(subnode_next); + } else { + target_remove(pid); + } + + // FIXME: Should probably return 0 here. + } else { + subnode = &(node->subnode); + while (subnode != NULL) { + subnode_next = subnode->next; + if (subnode_next->field_4 == tid) { + subnode->next = subnode_next->next; + internal_free(subnode_next); + return 0; + } + + subnode = subnode_next; + } + } + + return -1; +} + // 0x49BCBC int target_remove_all() { diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h index 3c23959..0d88665 100644 --- a/src/mapper/mp_targt.h +++ b/src/mapper/mp_targt.h @@ -57,6 +57,7 @@ int target_save(int pid); int target_load(int pid, TargetSubNode** subnode_ptr); int target_find_free_subnode(TargetSubNode** subnode_ptr); int target_remove(int pid); +int target_remove_tid(int pid, int tid); int target_remove_all(); int target_ptr(int pid, TargetSubNode** subnode_ptr); int target_tid_ptr(int pid, int tid, TargetSubNode** subnode_ptr); From 8d2d5df65f49cac845f4d89dc7b26b3159f9abab Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 17:54:17 +0300 Subject: [PATCH 109/136] Add target_new --- src/mapper/mp_targt.cc | 52 +++++++++++++++++++++++++++++++++++------- src/mapper/mp_targt.h | 5 ++-- 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index 5d41fbc..994e62b 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -22,7 +22,7 @@ typedef struct TargetNode { typedef struct TargetList { TargetNode* tail; int count; - int field_8; + int next_tid; } TargetList; // 0x53F354 @@ -245,7 +245,7 @@ int target_find_free_subnode(TargetSubNode** subnode_ptr) *subnode_ptr = &(node->subnode); - node->subnode.field_0 = -1; + node->subnode.pid = -1; node->subnode.next = NULL; node->next = targetlist.tail; @@ -255,6 +255,42 @@ int target_find_free_subnode(TargetSubNode** subnode_ptr) return 0; } +// 0x49BA10 +int target_new(int pid, int* tid_ptr) +{ + TargetSubNode* subnode; + TargetSubNode* new_subnode; + + if (target_ptr(pid, &subnode) == -1) { + if (target_find_free_subnode(&subnode) == -1) { + return -1; + } + } + + new_subnode = (TargetSubNode*)internal_malloc(sizeof(TargetSubNode)); + if (new_subnode == NULL) { + return -1; + } + + new_subnode->next = NULL; + + while (subnode->next != NULL) { + subnode = subnode->next; + } + + subnode->next = new_subnode; + + new_subnode->pid = pid; + new_subnode->tid = targetlist.next_tid; + + *tid_ptr = targetlist.next_tid; + + targetlist.count++; + targetlist.next_tid++; + + return 0; +} + // 0x49BBD4 int target_remove(int pid) { @@ -264,7 +300,7 @@ int target_remove(int pid) node = targetlist.tail; while (node != NULL) { - if (node->subnode.field_0 == pid) { + if (node->subnode.pid == pid) { break; } node = node->next; @@ -300,7 +336,7 @@ int target_remove_tid(int pid, int tid) node = targetlist.tail; while (node != NULL) { - if (node->subnode.field_0 == pid) { + if (node->subnode.pid == pid) { break; } node = node->next; @@ -310,7 +346,7 @@ int target_remove_tid(int pid, int tid) return -1; } - if (node->subnode.field_4 == tid) { + if (node->subnode.tid == tid) { subnode_next = node->subnode.next; if (subnode_next != NULL) { memcpy(&(node->subnode), subnode_next, sizeof(TargetSubNode)); @@ -324,7 +360,7 @@ int target_remove_tid(int pid, int tid) subnode = &(node->subnode); while (subnode != NULL) { subnode_next = subnode->next; - if (subnode_next->field_4 == tid) { + if (subnode_next->tid == tid) { subnode->next = subnode_next->next; internal_free(subnode_next); return 0; @@ -370,7 +406,7 @@ int target_ptr(int pid, TargetSubNode** subnode_ptr) { TargetNode* node = targetlist.tail; while (node != NULL) { - if (node->subnode.field_0 == pid) { + if (node->subnode.pid == pid) { *subnode_ptr = &(node->subnode); return 0; } @@ -388,7 +424,7 @@ int target_tid_ptr(int pid, int tid, TargetSubNode** subnode_ptr) } while (subnode != NULL) { - if (subnode->field_4 == tid) { + if (subnode->tid == tid) { *subnode_ptr = subnode; return 0; } diff --git a/src/mapper/mp_targt.h b/src/mapper/mp_targt.h index 0d88665..eb70695 100644 --- a/src/mapper/mp_targt.h +++ b/src/mapper/mp_targt.h @@ -4,8 +4,8 @@ namespace fallout { typedef struct TargetSubNode { - int field_0; - int field_4; + int pid; + int tid; int field_8; int field_C; int field_10; @@ -56,6 +56,7 @@ int target_header_load(); int target_save(int pid); int target_load(int pid, TargetSubNode** subnode_ptr); int target_find_free_subnode(TargetSubNode** subnode_ptr); +int target_new(int pid, int* tid_ptr); int target_remove(int pid); int target_remove_tid(int pid, int tid); int target_remove_all(); From 8929941d138e194e04bfac1b5d2bfd8731a3a3b1 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 18:07:37 +0300 Subject: [PATCH 110/136] Add target_init --- src/mapper/mp_targt.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index 994e62b..c85ba6f 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -77,7 +77,8 @@ void target_make_path(char* path, int pid) // 0x49B424 int target_init() { - // TODO: Incomplete. + target_remove_all(); + target_header_load(); return 0; } From e0b18a878513261106117580a8d9519fe38e7cf8 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 18:08:21 +0300 Subject: [PATCH 111/136] Add target_exit --- src/mapper/mp_targt.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/mapper/mp_targt.cc b/src/mapper/mp_targt.cc index c85ba6f..ab61837 100644 --- a/src/mapper/mp_targt.cc +++ b/src/mapper/mp_targt.cc @@ -86,7 +86,12 @@ int target_init() // 0x49B434 int target_exit() { - // TODO: Incomplete. + if (can_modify_protos) { + target_header_save(); + target_remove_all(); + } else { + target_remove_all(); + } return 0; } From 1c2fd05bcdaa36f027d1fae89300130ef5f2afff Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 18:33:32 +0300 Subject: [PATCH 112/136] Add proto_subdata_setup_int_button --- src/mapper/mp_proto.cc | 55 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/mapper/mp_proto.cc b/src/mapper/mp_proto.cc index 2b0ef90..7e787fd 100644 --- a/src/mapper/mp_proto.cc +++ b/src/mapper/mp_proto.cc @@ -22,6 +22,7 @@ namespace fallout { #define NO 1 static int proto_choose_container_flags(Proto* proto); +static int proto_subdata_setup_int_button(const char* title, int key, int value, int min_value, int max_value, int* y, int a7); static void proto_critter_flags_redraw(int win, int pid); static int proto_critter_flags_modify(int pid); static int mp_pick_kill_type(); @@ -57,6 +58,9 @@ int edit_window_color = 1; // 0x559C60 bool can_modify_protos = false; +// 0x559C68 +static int subwin = -1; + // 0x559C6C static int critFlagList[CRITTER_FLAG_COUNT] = { CRITTER_NO_STEAL, @@ -221,6 +225,57 @@ int proto_choose_container_flags(Proto* proto) return 0; } +// 0x492A3C +int proto_subdata_setup_int_button(const char* title, int key, int value, int min_value, int max_value, int* y, int a7) +{ + char text[36]; + int button_x; + int value_offset_x; + + button_x = 10; + value_offset_x = 90; + + if (a7 == 9) { + *y -= 189; + } + + if (a7 > 8) { + button_x = 165; + value_offset_x -= 16; + } + + _win_register_text_button(subwin, + button_x, + *y, + -1, + -1, + -1, + key, + title, + 0); + + if (value >= min_value && value < max_value) { + sprintf(text, "%d", value); + windowDrawText(subwin, + text, + 38, + button_x + value_offset_x, + *y + 4, + _colorTable[32747] | 0x10000); + } else { + windowDrawText(subwin, + "", + 38, + button_x + value_offset_x, + *y + 4, + _colorTable[31744] | 0x10000); + } + + *y += 21; + + return 0; +} + // 0x495438 const char* proto_wall_light_str(int flags) { From b0b161ceb5b1ab5da62f782085941a1517ffe01a Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 19:31:16 +0300 Subject: [PATCH 113/136] Add proto_subdata_setup_fid_button --- src/art.cc | 8 ++++++ src/art.h | 1 + src/mapper/mp_proto.cc | 58 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/src/art.cc b/src/art.cc index bb1a0f3..1e92439 100644 --- a/src/art.cc +++ b/src/art.cc @@ -438,6 +438,14 @@ void artRender(int fid, unsigned char* dest, int width, int height, int pitch) artUnlock(handle); } +// mapper2.exe: 0x40A03C +int art_list_str(int fid, char* name) +{ + // TODO: Incomplete. + + return -1; +} + // 0x419160 Art* artLock(int fid, CacheEntry** handlePtr) { diff --git a/src/art.h b/src/art.h index f7c31b4..650b241 100644 --- a/src/art.h +++ b/src/art.h @@ -123,6 +123,7 @@ char* artGetObjectTypeName(int objectType); int artIsObjectTypeHidden(int objectType); int artGetFidgetCount(int headFid); void artRender(int fid, unsigned char* dest, int width, int height, int pitch); +int art_list_str(int fid, char* name); Art* artLock(int fid, CacheEntry** cache_entry); unsigned char* artLockFrameData(int fid, int frame, int direction, CacheEntry** out_cache_entry); unsigned char* artLockFrameDataReturningSize(int fid, CacheEntry** out_cache_entry, int* widthPtr, int* heightPtr); diff --git a/src/mapper/mp_proto.cc b/src/mapper/mp_proto.cc index 7e787fd..8de5c5d 100644 --- a/src/mapper/mp_proto.cc +++ b/src/mapper/mp_proto.cc @@ -2,6 +2,7 @@ #include +#include "art.h" #include "color.h" #include "combat_ai.h" #include "critter.h" @@ -23,6 +24,7 @@ namespace fallout { static int proto_choose_container_flags(Proto* proto); static int proto_subdata_setup_int_button(const char* title, int key, int value, int min_value, int max_value, int* y, int a7); +static int proto_subdata_setup_fid_button(const char* title, int key, int fid, int* y, int a5); static void proto_critter_flags_redraw(int win, int pid); static int proto_critter_flags_modify(int pid); static int mp_pick_kill_type(); @@ -276,6 +278,62 @@ int proto_subdata_setup_int_button(const char* title, int key, int value, int mi return 0; } +// 0x492B28 +int proto_subdata_setup_fid_button(const char* title, int key, int fid, int* y, int a5) +{ + char text[36]; + char* pch; + int button_x; + int value_offset_x; + + button_x = 10; + value_offset_x = 90; + + if (a5 == 9) { + *y -= 189; + } + + if (a5 > 8) { + button_x = 165; + value_offset_x -= 16; + } + + _win_register_text_button(subwin, + button_x, + *y, + -1, + -1, + -1, + key, + title, + 0); + + if (art_list_str(fid, text) != -1) { + pch = strchr(text, '.'); + if (pch != NULL) { + *pch = '\0'; + } + + windowDrawText(subwin, + text, + 80, + button_x + value_offset_x, + *y + 4, + _colorTable[32747] | 0x10000); + } else { + windowDrawText(subwin, + "None", + 80, + button_x + value_offset_x, + *y + 4, + _colorTable[992] | 0x10000); + } + + *y += 21; + + return 0; +} + // 0x495438 const char* proto_wall_light_str(int flags) { From 4a94b19f604403b16c4568c2684cab6059951783 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 19:36:17 +0300 Subject: [PATCH 114/136] Add proto_subdata_setup_pid_button --- src/mapper/mp_proto.cc | 50 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/mapper/mp_proto.cc b/src/mapper/mp_proto.cc index 8de5c5d..6e1b1be 100644 --- a/src/mapper/mp_proto.cc +++ b/src/mapper/mp_proto.cc @@ -25,6 +25,7 @@ namespace fallout { static int proto_choose_container_flags(Proto* proto); static int proto_subdata_setup_int_button(const char* title, int key, int value, int min_value, int max_value, int* y, int a7); static int proto_subdata_setup_fid_button(const char* title, int key, int fid, int* y, int a5); +static int proto_subdata_setup_pid_button(const char* title, int key, int pid, int* y, int a5); static void proto_critter_flags_redraw(int win, int pid); static int proto_critter_flags_modify(int pid); static int mp_pick_kill_type(); @@ -334,6 +335,55 @@ int proto_subdata_setup_fid_button(const char* title, int key, int fid, int* y, return 0; } +// 0x492C20 +int proto_subdata_setup_pid_button(const char* title, int key, int pid, int* y, int a5) +{ + int button_x; + int value_offset_x; + + button_x = 10; + value_offset_x = 90; + + if (a5 == 9) { + *y -= 189; + } + + if (a5 > 8) { + button_x = 165; + value_offset_x = 74; + } + + _win_register_text_button(subwin, + button_x, + *y, + -1, + -1, + -1, + key, + title, + 0); + + if (pid != -1) { + windowDrawText(subwin, + protoGetName(pid), + 49, + button_x + value_offset_x, + *y + 4, + _colorTable[32747] | 0x10000); + } else { + windowDrawText(subwin, + "None", + 49, + button_x + value_offset_x, + *y + 4, + _colorTable[992] | 0x10000); + } + + *y += 21; + + return 0; +} + // 0x495438 const char* proto_wall_light_str(int flags) { From 84288612ef147fc89abbea2eac1771bdcaf9a1af Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 19:43:56 +0300 Subject: [PATCH 115/136] Add map_toggle_block_obj_viewing_on --- src/mapper/map_func.cc | 9 +++++++++ src/mapper/map_func.h | 1 + 2 files changed, 10 insertions(+) diff --git a/src/mapper/map_func.cc b/src/mapper/map_func.cc index b9f297d..def62b9 100644 --- a/src/mapper/map_func.cc +++ b/src/mapper/map_func.cc @@ -2,6 +2,9 @@ namespace fallout { +// 0x5595CC +static bool block_obj_view_on = false; + // 0x4825B0 void setup_map_dirs() { @@ -14,4 +17,10 @@ void copy_proto_lists() // TODO: Incomplete. } +// 0x485D44 +bool map_toggle_block_obj_viewing_on() +{ + return block_obj_view_on; +} + } // namespace fallout diff --git a/src/mapper/map_func.h b/src/mapper/map_func.h index 1292604..44d3166 100644 --- a/src/mapper/map_func.h +++ b/src/mapper/map_func.h @@ -5,6 +5,7 @@ namespace fallout { void setup_map_dirs(); void copy_proto_lists(); +bool map_toggle_block_obj_viewing_on(); } // namespace fallout From 9178abc53db553736283ded9c72146d5f3b13e7e Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 19:47:15 +0300 Subject: [PATCH 116/136] Add toolbar_proto --- src/mapper/map_func.cc | 12 ++++++++++++ src/mapper/map_func.h | 1 + src/proto.cc | 3 +-- src/proto.h | 1 + 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/mapper/map_func.cc b/src/mapper/map_func.cc index def62b9..b9f853b 100644 --- a/src/mapper/map_func.cc +++ b/src/mapper/map_func.cc @@ -1,5 +1,7 @@ #include "mapper/map_func.h" +#include "proto.h" + namespace fallout { // 0x5595CC @@ -17,6 +19,16 @@ void copy_proto_lists() // TODO: Incomplete. } +// 0x484400 +int toolbar_proto(int type, int id) +{ + if (id < proto_max_id(type)) { + return (type << 24) | id; + } else { + return -1; + } +} + // 0x485D44 bool map_toggle_block_obj_viewing_on() { diff --git a/src/mapper/map_func.h b/src/mapper/map_func.h index 44d3166..e779f28 100644 --- a/src/mapper/map_func.h +++ b/src/mapper/map_func.h @@ -5,6 +5,7 @@ namespace fallout { void setup_map_dirs(); void copy_proto_lists(); +int toolbar_proto(int type, int id); bool map_toggle_block_obj_viewing_on(); } // namespace fallout diff --git a/src/proto.cc b/src/proto.cc index 001217f..f266498 100644 --- a/src/proto.cc +++ b/src/proto.cc @@ -39,7 +39,6 @@ static int _proto_find_free_subnode(int type, Proto** out_ptr); static void _proto_remove_some_list(int type); static void _proto_remove_list(int type); static int _proto_new_id(int type); -static int _proto_max_id(int type); // 0x50CF3C static char _aProto_0[] = "proto\\"; @@ -2170,7 +2169,7 @@ static int _proto_new_id(int type) } // 0x4A2214 -static int _proto_max_id(int type) +int proto_max_id(int type) { return _protoLists[type].max_entries_num; } diff --git a/src/proto.h b/src/proto.h index f00eef9..f49d1a6 100644 --- a/src/proto.h +++ b/src/proto.h @@ -137,6 +137,7 @@ int proto_new(int* pid, int type); void _proto_remove_all(); int protoGetProto(int pid, Proto** protoPtr); int _ResetPlayer(); +int proto_max_id(int type); static bool isExitGridPid(int pid) { From 7f920fe13b623f229c0cfca14ced7f0f994e3bac Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 19:57:14 +0300 Subject: [PATCH 117/136] Add erase_rect --- src/mapper/map_func.cc | 22 ++++++++++++++++++++++ src/mapper/map_func.h | 3 +++ 2 files changed, 25 insertions(+) diff --git a/src/mapper/map_func.cc b/src/mapper/map_func.cc index b9f853b..f328b0a 100644 --- a/src/mapper/map_func.cc +++ b/src/mapper/map_func.cc @@ -1,6 +1,7 @@ #include "mapper/map_func.h" #include "proto.h" +#include "window_manager.h" namespace fallout { @@ -19,6 +20,27 @@ void copy_proto_lists() // TODO: Incomplete. } +// 0x4843A0 +void erase_rect(Rect* rect) +{ + Rect r = *rect; + + r.bottom = rect->top; + windowRefreshAll(&r); + + r.bottom = rect->bottom; + r.left = rect->right; + windowRefreshAll(&r); + + r.left = rect->left; + r.top = rect->bottom; + windowRefreshAll(&r); + + r.top = rect->top; + r.right = rect->left; + windowRefreshAll(&r); +} + // 0x484400 int toolbar_proto(int type, int id) { diff --git a/src/mapper/map_func.h b/src/mapper/map_func.h index e779f28..0961c90 100644 --- a/src/mapper/map_func.h +++ b/src/mapper/map_func.h @@ -1,10 +1,13 @@ #ifndef FALLOUT_MAPPER_MAP_FUNC_H_ #define FALLOUT_MAPPER_MAP_FUNC_H_ +#include "geometry.h" + namespace fallout { void setup_map_dirs(); void copy_proto_lists(); +void erase_rect(Rect* rect); int toolbar_proto(int type, int id); bool map_toggle_block_obj_viewing_on(); From 84aecfa823c5808e5946dbfd58be6e85414aca59 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 20:07:02 +0300 Subject: [PATCH 118/136] Add draw_rect --- src/mapper/map_func.cc | 26 ++++++++++++++++++++++++++ src/mapper/map_func.h | 1 + 2 files changed, 27 insertions(+) diff --git a/src/mapper/map_func.cc b/src/mapper/map_func.cc index f328b0a..8b032f2 100644 --- a/src/mapper/map_func.cc +++ b/src/mapper/map_func.cc @@ -1,6 +1,8 @@ #include "mapper/map_func.h" +#include "memory.h" #include "proto.h" +#include "svga.h" #include "window_manager.h" namespace fallout { @@ -20,6 +22,30 @@ void copy_proto_lists() // TODO: Incomplete. } +// 0x4842D4 +void draw_rect(Rect* rect, unsigned char color) +{ + int width = rect->right - rect->left; + int height = rect->bottom - rect->top; + int max_dimension; + + if (height < width) { + max_dimension = width; + } else { + max_dimension = height; + } + + unsigned char* buffer = (unsigned char*)internal_malloc(max_dimension); + if (buffer != NULL) { + memset(buffer, color, max_dimension); + _scr_blit(buffer, width, 1, 0, 0, width, 1, rect->left, rect->top); + _scr_blit(buffer, 1, height, 0, 0, 1, height, rect->left, rect->top); + _scr_blit(buffer, width, 1, 0, 0, width, 1, rect->left, rect->bottom); + _scr_blit(buffer, 1, height, 0, 0, 1, height, rect->right, rect->top); + internal_free(buffer); + } +} + // 0x4843A0 void erase_rect(Rect* rect) { diff --git a/src/mapper/map_func.h b/src/mapper/map_func.h index 0961c90..3d03b42 100644 --- a/src/mapper/map_func.h +++ b/src/mapper/map_func.h @@ -7,6 +7,7 @@ namespace fallout { void setup_map_dirs(); void copy_proto_lists(); +void draw_rect(Rect* rect); void erase_rect(Rect* rect); int toolbar_proto(int type, int id); bool map_toggle_block_obj_viewing_on(); From bb3513956c010803bbbd46c7d354f4b85c957e34 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 21:34:41 +0300 Subject: [PATCH 119/136] Add op_force_encounter --- src/sfall_opcodes.cc | 17 +++++++++++++++ src/worldmap.cc | 52 ++++++++++++++++++++++++++++++++++++++++++++ src/worldmap.h | 7 ++++++ 3 files changed, 76 insertions(+) diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index ea45d4e..8d8cd57 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -138,6 +138,13 @@ static void op_in_world_map(Program* program) programStackPushInteger(program, GameMode::isInGameMode(GameMode::kWorldmap) ? 1 : 0); } +// force_encounter +static void op_force_encounter(Program* program) +{ + int map = programStackPopInteger(program); + wmForceEncounter(map, 0); +} + // set_world_map_pos static void op_set_world_map_pos(Program* program) { @@ -541,6 +548,14 @@ static void op_get_attack_type(Program* program) } } +// force_encounter_with_flags +static void op_force_encounter_with_flags(Program* program) +{ + unsigned int flags = programStackPopInteger(program); + int map = programStackPopInteger(program); + wmForceEncounter(map, flags); +} + // atoi static void opParseInt(Program* program) { @@ -894,6 +909,7 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x816A, op_set_global_script_repeat); interpreterRegisterOpcode(0x816C, op_key_pressed); interpreterRegisterOpcode(0x8170, op_in_world_map); + interpreterRegisterOpcode(0x8171, op_force_encounter); interpreterRegisterOpcode(0x8172, op_set_world_map_pos); interpreterRegisterOpcode(0x8193, opGetCurrentHand); interpreterRegisterOpcode(0x819B, op_set_global_script_type); @@ -927,6 +943,7 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x8221, opGetScreenHeight); interpreterRegisterOpcode(0x8224, op_create_message_window); interpreterRegisterOpcode(0x8228, op_get_attack_type); + interpreterRegisterOpcode(0x8229, op_force_encounter_with_flags); interpreterRegisterOpcode(0x822D, opCreateArray); interpreterRegisterOpcode(0x822E, opSetArray); interpreterRegisterOpcode(0x822F, opGetArray); diff --git a/src/worldmap.cc b/src/worldmap.cc index 3243d8d..8b30255 100644 --- a/src/worldmap.cc +++ b/src/worldmap.cc @@ -817,6 +817,8 @@ static double gGameTimeIncRemainder = 0.0; static FrmImage _backgroundFrmImage; static FrmImage _townFrmImage; static bool wmFaded = false; +static int wmForceEncounterMapId = -1; +static unsigned int wmForceEncounterFlags = 0; static inline bool cityIsValid(int city) { @@ -929,6 +931,9 @@ static int wmGenDataInit() wmGenData.tabsScrollingDelta = 0; wmGenData.viewportMaxX = 0; + wmForceEncounterMapId = -1; + wmForceEncounterFlags = 0; + return 0; } @@ -979,6 +984,9 @@ static int wmGenDataReset() wmMarkSubTileRadiusVisited(wmGenData.worldPosX, wmGenData.worldPosY); + wmForceEncounterMapId = -1; + wmForceEncounterFlags = 0; + return 0; } @@ -3347,6 +3355,33 @@ static int wmRndEncounterOccurred() } } + // SFALL: Handle forced encounter. + // CE: In Sfall a check for forced encounter is inserted instead of check + // for Horrigan encounter (above). This implemenation gives Horrigan + // encounter a priority. + if (wmForceEncounterMapId != -1) { + if ((wmForceEncounterFlags & ENCOUNTER_FLAG_NO_CAR) != 0) { + if (wmGenData.isInCar) { + wmMatchAreaContainingMapIdx(wmForceEncounterMapId, &(wmGenData.currentCarAreaId)); + } + } + + // For unknown reason fadeout and blinking icon are mutually exclusive. + if ((wmForceEncounterFlags & ENCOUNTER_FLAG_FADEOUT) != 0) { + wmFadeOut(); + } else if ((wmForceEncounterFlags & ENCOUNTER_FLAG_NO_ICON) == 0) { + bool special = (wmForceEncounterFlags & ENCOUNTER_FLAG_ICON_SP) != 0; + wmBlinkRndEncounterIcon(special); + } + + mapLoadById(wmForceEncounterMapId); + + wmForceEncounterMapId = -1; + wmForceEncounterFlags = 0; + + return 1; + } + // NOTE: Uninline. wmPartyFindCurSubTile(); @@ -6608,4 +6643,21 @@ void wmCarSetCurrentArea(int area) wmGenData.currentCarAreaId = area; } +void wmForceEncounter(int map, unsigned int flags) +{ + if ((wmForceEncounterFlags & (1 << 31)) != 0) { + return; + } + + wmForceEncounterMapId = map; + wmForceEncounterFlags = flags; + + // I don't quite understand the reason why locking needs one more flag. + if ((wmForceEncounterFlags & ENCOUNTER_FLAG_LOCK) != 0) { + wmForceEncounterFlags |= (1 << 31); + } else { + wmForceEncounterFlags &= ~(1 << 31); + } +} + } // namespace fallout diff --git a/src/worldmap.h b/src/worldmap.h index 901b0a6..20ea1c2 100644 --- a/src/worldmap.h +++ b/src/worldmap.h @@ -229,6 +229,12 @@ typedef enum Map { MAP_IN_GAME_MOVIE1 = 149, } Map; +#define ENCOUNTER_FLAG_NO_CAR 0x1 +#define ENCOUNTER_FLAG_LOCK 0x2 +#define ENCOUNTER_FLAG_NO_ICON 0x4 +#define ENCOUNTER_FLAG_ICON_SP 0x8 +#define ENCOUNTER_FLAG_FADEOUT 0x10 + extern unsigned char* circleBlendTable; int wmWorldMap_init(); @@ -279,6 +285,7 @@ int wmTeleportToArea(int areaIdx); void wmSetPartyWorldPos(int x, int y); void wmCarSetCurrentArea(int area); +void wmForceEncounter(int map, unsigned int flags); } // namespace fallout From 427672741a58e87196511371e17411f1324d7f21 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 2 Sep 2023 22:06:37 +0300 Subject: [PATCH 120/136] Add op_list_as_array --- src/sfall_arrays.cc | 20 ++++++++++ src/sfall_arrays.h | 1 + src/sfall_lists.cc | 92 ++++++++++++++++++++++++-------------------- src/sfall_lists.h | 3 ++ src/sfall_opcodes.cc | 9 +++++ 5 files changed, 84 insertions(+), 41 deletions(-) diff --git a/src/sfall_arrays.cc b/src/sfall_arrays.cc index e074b4c..c0d064b 100644 --- a/src/sfall_arrays.cc +++ b/src/sfall_arrays.cc @@ -11,6 +11,7 @@ #include #include "interpreter.h" +#include "sfall_lists.h" namespace fallout { @@ -647,6 +648,25 @@ ProgramValue ScanArray(ArrayId arrayId, const ProgramValue& val, Program* progra return arr->ScanArray(val, program); } +ArrayId ListAsArray(int type) +{ + std::vector objects; + sfall_lists_fill(type, objects); + + int count = static_cast(objects.size()); + ArrayId arrayId = CreateTempArray(count, 0); + auto arr = get_array_by_id(arrayId); + + // A little bit ugly and likely inefficient. + for (int index = 0; index < count; index++) { + arr->SetArray(ProgramValue { index }, + ArrayElement { ProgramValue { objects[index] }, nullptr }, + false); + } + + return arrayId; +} + ArrayId StringSplit(const char* str, const char* split) { size_t splitLen = strlen(split); diff --git a/src/sfall_arrays.h b/src/sfall_arrays.h index bd96ad5..d976afb 100644 --- a/src/sfall_arrays.h +++ b/src/sfall_arrays.h @@ -26,6 +26,7 @@ void ResizeArray(ArrayId arrayId, int newLen); void DeleteAllTempArrays(); int StackArray(const ProgramValue& key, const ProgramValue& val, Program* program); ProgramValue ScanArray(ArrayId arrayId, const ProgramValue& val, Program* program); +ArrayId ListAsArray(int type); ArrayId StringSplit(const char* str, const char* split); diff --git a/src/sfall_lists.cc b/src/sfall_lists.cc index a582ae2..55150b1 100644 --- a/src/sfall_lists.cc +++ b/src/sfall_lists.cc @@ -1,7 +1,6 @@ #include "sfall_lists.h" #include -#include #include "object.h" #include "scripts.h" @@ -66,46 +65,7 @@ int sfallListsCreate(int listType) int listId = _state->nextListId++; List& list = _state->lists[listId]; - if (listType == LIST_TILES) { - // For unknown reason this list type is not implemented in Sfall. - } else if (listType == LIST_SPATIAL) { - for (int elevation = 0; elevation < ELEVATION_COUNT; elevation++) { - Script* script = scriptGetFirstSpatialScript(elevation); - while (script != nullptr) { - Object* obj = script->owner; - if (obj == nullptr) { - obj = scriptGetSelf(script->program); - } - list.objects.push_back(obj); - script = scriptGetNextSpatialScript(); - } - } - } else { - // CE: Implementation is slightly different. Sfall manually loops thru - // elevations (3) and hexes (40000) and use |objectFindFirstAtLocation| - // (originally |obj_find_first_at_tile|) to obtain next object. This - // functionality is already implemented in |objectFindFirst| and - // |objectFindNext|. - // - // As a small optimization |LIST_ALL| is handled separately since there - // is no need to check object type. - if (listType == LIST_ALL) { - Object* obj = objectFindFirst(); - while (obj != nullptr) { - list.objects.push_back(obj); - obj = objectFindNext(); - } - } else { - Object* obj = objectFindFirst(); - while (obj != nullptr) { - int objectType = PID_TYPE(obj->pid); - if (objectType < kObjectTypeToListTypeSize && kObjectTypeToListType[objectType] == listType) { - list.objects.push_back(obj); - } - obj = objectFindNext(); - } - } - } + sfall_lists_fill(listType, list.objects); return listId; } @@ -131,4 +91,54 @@ void sfallListsDestroy(int listId) } } +void sfall_lists_fill(int type, std::vector& objects) +{ + if (type == LIST_TILES) { + // For unknown reason this list type is not implemented in Sfall. + return; + } + + objects.reserve(100); + + if (type == LIST_SPATIAL) { + for (int elevation = 0; elevation < ELEVATION_COUNT; elevation++) { + Script* script = scriptGetFirstSpatialScript(elevation); + while (script != nullptr) { + Object* obj = script->owner; + if (obj == nullptr) { + obj = scriptGetSelf(script->program); + } + objects.push_back(obj); + script = scriptGetNextSpatialScript(); + } + } + } else { + // CE: Implementation is slightly different. Sfall manually loops thru + // elevations (3) and hexes (40000) and use |objectFindFirstAtLocation| + // (originally |obj_find_first_at_tile|) to obtain next object. This + // functionality is already implemented in |objectFindFirst| and + // |objectFindNext|. + // + // As a small optimization |LIST_ALL| is handled separately since there + // is no need to check object type. + if (type == LIST_ALL) { + Object* obj = objectFindFirst(); + while (obj != nullptr) { + objects.push_back(obj); + obj = objectFindNext(); + } + } else { + Object* obj = objectFindFirst(); + while (obj != nullptr) { + int objectType = PID_TYPE(obj->pid); + if (objectType < kObjectTypeToListTypeSize + && kObjectTypeToListType[objectType] == type) { + objects.push_back(obj); + } + obj = objectFindNext(); + } + } + } +} + } // namespace fallout diff --git a/src/sfall_lists.h b/src/sfall_lists.h index cbcd239..67f3455 100644 --- a/src/sfall_lists.h +++ b/src/sfall_lists.h @@ -1,6 +1,8 @@ #ifndef FALLOUT_SFALL_LISTS_H_ #define FALLOUT_SFALL_LISTS_H_ +#include + #include "obj_types.h" namespace fallout { @@ -22,6 +24,7 @@ void sfallListsExit(); int sfallListsCreate(int listType); Object* sfallListsGetNext(int listId); void sfallListsDestroy(int listId); +void sfall_lists_fill(int type, std::vector& objects); } // namespace fallout diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index 8d8cd57..032e1db 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -556,6 +556,14 @@ static void op_force_encounter_with_flags(Program* program) wmForceEncounter(map, flags); } +// list_as_array +static void op_list_as_array(Program* program) +{ + int type = programStackPopInteger(program); + int arrayId = ListAsArray(type); + programStackPushInteger(program, arrayId); +} + // atoi static void opParseInt(Program* program) { @@ -953,6 +961,7 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x8233, opTempArray); interpreterRegisterOpcode(0x8234, opFixArray); interpreterRegisterOpcode(0x8235, opStringSplit); + interpreterRegisterOpcode(0x8236, op_list_as_array); interpreterRegisterOpcode(0x8237, opParseInt); interpreterRegisterOpcode(0x8238, op_atof); interpreterRegisterOpcode(0x8239, opScanArray); From b706756ec960599f02ab6007e7e0516925edd53e Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 3 Sep 2023 13:34:16 +0300 Subject: [PATCH 121/136] Add op_get_script --- src/sfall_opcodes.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index 032e1db..a07f21d 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -275,6 +275,13 @@ static void op_abs(Program* program) } } +// get_script +static void op_get_script(Program* program) +{ + Object* obj = static_cast(programStackPopPointer(program)); + programStackPushInteger(program, obj->field_80 + 1); +} + // get_proto_data static void op_get_proto_data(Program* program) { @@ -932,6 +939,7 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x81EB, op_get_ini_string); interpreterRegisterOpcode(0x81EC, op_sqrt); interpreterRegisterOpcode(0x81ED, op_abs); + interpreterRegisterOpcode(0x81F5, op_get_script); interpreterRegisterOpcode(0x8204, op_get_proto_data); interpreterRegisterOpcode(0x8205, op_set_proto_data); interpreterRegisterOpcode(0x820D, opListBegin); From b40bfcc64a820ea3f50b4268d539fdb3566fb99a Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 3 Sep 2023 16:26:52 +0300 Subject: [PATCH 122/136] Add some metarules --- CMakeLists.txt | 2 + src/combat.cc | 5 + src/combat.h | 1 + src/game_mouse.cc | 5 + src/game_mouse.h | 1 + src/inventory.cc | 5 + src/inventory.h | 1 + src/sfall_ini.cc | 49 +++++++ src/sfall_ini.h | 6 + src/sfall_metarules.cc | 289 +++++++++++++++++++++++++++++++++++++++++ src/sfall_metarules.h | 12 ++ src/sfall_opcodes.cc | 50 +++++++ src/window.cc | 55 +++++++- src/window.h | 4 + 14 files changed, 483 insertions(+), 2 deletions(-) create mode 100644 src/sfall_metarules.cc create mode 100644 src/sfall_metarules.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c6c918..ae87c6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -275,6 +275,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC "src/sfall_kb_helpers.h" "src/sfall_lists.cc" "src/sfall_lists.h" + "src/sfall_metarules.cc" + "src/sfall_metarules.h" "src/sfall_opcodes.cc" "src/sfall_opcodes.h" "src/sfall_arrays.cc" diff --git a/src/combat.cc b/src/combat.cc index 70098c0..6d28fe0 100644 --- a/src/combat.cc +++ b/src/combat.cc @@ -6825,4 +6825,9 @@ void combat_reset_hit_location_penalty() } } +Attack* combat_get_data() +{ + return &_main_ctd; +} + } // namespace fallout diff --git a/src/combat.h b/src/combat.h index 2438b37..6a1667c 100644 --- a/src/combat.h +++ b/src/combat.h @@ -74,6 +74,7 @@ bool damageModGetDisplayBonusDamage(); int combat_get_hit_location_penalty(int hit_location); void combat_set_hit_location_penalty(int hit_location, int penalty); void combat_reset_hit_location_penalty(); +Attack* combat_get_data(); static inline bool isInCombat() { diff --git a/src/game_mouse.cc b/src/game_mouse.cc index fca7c79..193ee4f 100644 --- a/src/game_mouse.cc +++ b/src/game_mouse.cc @@ -2464,4 +2464,9 @@ void gameMouseRefreshImmediately() renderPresent(); } +Object* gmouse_get_outlined_object() +{ + return gGameMouseHighlightedItem; +} + } // namespace fallout diff --git a/src/game_mouse.h b/src/game_mouse.h index 2b99efe..73544b2 100644 --- a/src/game_mouse.h +++ b/src/game_mouse.h @@ -103,6 +103,7 @@ void gameMouseLoadItemHighlight(); void _gmouse_remove_item_outline(Object* object); void gameMouseRefreshImmediately(); +Object* gmouse_get_outlined_object(); } // namespace fallout diff --git a/src/inventory.cc b/src/inventory.cc index 351584c..4b53126 100644 --- a/src/inventory.cc +++ b/src/inventory.cc @@ -5981,4 +5981,9 @@ int _inven_set_timer(Object* a1) return seconds; } +Object* inven_get_current_target_obj() +{ + return _target_stack[_target_curr_stack]; +} + } // namespace fallout diff --git a/src/inventory.h b/src/inventory.h index 58dcf31..7f4aa00 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -27,6 +27,7 @@ int inventoryOpenLooting(Object* a1, Object* a2); int inventoryOpenStealing(Object* a1, Object* a2); void inventoryOpenTrade(int win, Object* a2, Object* a3, Object* a4, int a5); int _inven_set_timer(Object* a1); +Object* inven_get_current_target_obj(); } // namespace fallout diff --git a/src/sfall_ini.cc b/src/sfall_ini.cc index 91af85c..f4a2c91 100644 --- a/src/sfall_ini.cc +++ b/src/sfall_ini.cc @@ -145,4 +145,53 @@ bool sfall_ini_get_string(const char* triplet, char* value, size_t size) return true; } +bool sfall_ini_set_int(const char* triplet, int value) +{ + char stringValue[20]; + compat_itoa(value, stringValue, 10); + + return sfall_ini_set_string(triplet, stringValue); +} + +bool sfall_ini_set_string(const char* triplet, const char* value) +{ + char fileName[kFileNameMaxSize]; + char section[kSectionMaxSize]; + + const char* key = parse_ini_triplet(triplet, fileName, section); + if (key == nullptr) { + return false; + } + + Config config; + if (!configInit(&config)) { + return false; + } + + char path[COMPAT_MAX_PATH]; + bool loaded = false; + + if (basePath[0] != '\0' && !is_system_file_name(fileName)) { + // Attempt to load requested file in base directory. + snprintf(path, sizeof(path), "%s\\%s", basePath, fileName); + loaded = configRead(&config, path, false); + } + + if (!loaded) { + // There was no base path set, requested file is a system config, or + // non-system config file was not found the base path - attempt to load + // from current working directory. + strcpy(path, fileName); + loaded = configRead(&config, path, false); + } + + configSetString(&config, section, key, value); + + bool saved = configWrite(&config, path, false); + + configFree(&config); + + return saved; +} + } // namespace fallout diff --git a/src/sfall_ini.h b/src/sfall_ini.h index ac4cdd8..092b40c 100644 --- a/src/sfall_ini.h +++ b/src/sfall_ini.h @@ -14,6 +14,12 @@ bool sfall_ini_get_int(const char* triplet, int* value); /// Reads string key identified by "fileName|section|key" triplet into `value`. bool sfall_ini_get_string(const char* triplet, char* value, size_t size); +/// Writes integer key identified by "fileName|section|key" triplet. +bool sfall_ini_set_int(const char* triplet, int value); + +/// Writes string key identified by "fileName|section|key" triplet. +bool sfall_ini_set_string(const char* triplet, const char* value); + } // namespace fallout #endif /* FALLOUT_SFALL_INI_H_ */ diff --git a/src/sfall_metarules.cc b/src/sfall_metarules.cc new file mode 100644 index 0000000..558645b --- /dev/null +++ b/src/sfall_metarules.cc @@ -0,0 +1,289 @@ +#include "sfall_metarules.h" + +#include "combat.h" +#include "debug.h" +#include "game.h" +#include "game_dialog.h" +#include "game_mouse.h" +#include "interface.h" +#include "inventory.h" +#include "object.h" +#include "sfall_ini.h" +#include "text_font.h" +#include "tile.h" +#include "window.h" +#include "worldmap.h" + +namespace fallout { + +typedef void(MetaruleHandler)(Program* program, int args); + +// Simplified cousin of `SfallMetarule` from Sfall. +typedef struct MetaruleInfo { + const char* name; + MetaruleHandler* handler; + int minArgs; + int maxArgs; +} MetaruleInfo; + +static void mf_car_gas_amount(Program* program, int args); +static void mf_combat_data(Program* program, int args); +static void mf_critter_inven_obj2(Program* program, int args); +static void mf_dialog_obj(Program* program, int args); +static void mf_get_cursor_mode(Program* program, int args); +static void mf_get_flags(Program* program, int args); +static void mf_get_object_data(Program* program, int args); +static void mf_get_text_width(Program* program, int args); +static void mf_intface_redraw(Program* program, int args); +static void mf_loot_obj(Program* program, int args); +static void mf_metarule_exist(Program* program, int args); +static void mf_outlined_object(Program* program, int args); +static void mf_set_cursor_mode(Program* program, int args); +static void mf_set_flags(Program* program, int args); +static void mf_set_ini_setting(Program* program, int args); +static void mf_set_outline(Program* program, int args); +static void mf_show_window(Program* program, int args); +static void mf_tile_refresh_display(Program* program, int args); + +constexpr MetaruleInfo kMetarules[] = { + { "car_gas_amount", mf_car_gas_amount, 0, 0 }, + { "combat_data", mf_combat_data, 0, 0 }, + { "critter_inven_obj2", mf_critter_inven_obj2, 2, 2 }, + { "dialog_obj", mf_dialog_obj, 0, 0 }, + { "get_cursor_mode", mf_get_cursor_mode, 0, 0 }, + { "get_flags", mf_get_flags, 1, 1 }, + { "get_object_data", mf_get_object_data, 2, 2 }, + { "get_text_width", mf_get_text_width, 1, 1 }, + { "intface_redraw", mf_intface_redraw, 0, 1 }, + { "loot_obj", mf_loot_obj, 0, 0 }, + { "metarule_exist", mf_metarule_exist, 1, 1 }, + { "outlined_object", mf_outlined_object, 0, 0 }, + { "set_cursor_mode", mf_set_cursor_mode, 1, 1 }, + { "set_flags", mf_set_flags, 2, 2 }, + { "set_ini_setting", mf_set_ini_setting, 2, 2 }, + { "set_outline", mf_set_outline, 2, 2 }, + { "show_window", mf_show_window, 0, 1 }, + { "tile_refresh_display", mf_tile_refresh_display, 0, 0 }, +}; + +constexpr int kMetarulesMax = sizeof(kMetarules) / sizeof(kMetarules[0]); + +void mf_car_gas_amount(Program* program, int args) +{ + programStackPushInteger(program, wmCarGasAmount()); +} + +void mf_combat_data(Program* program, int args) +{ + if (isInCombat()) { + programStackPushPointer(program, combat_get_data()); + } else { + programStackPushPointer(program, nullptr); + } +} + +void mf_critter_inven_obj2(Program* program, int args) +{ + int slot = programStackPopInteger(program); + Object* obj = static_cast(programStackPopPointer(program)); + + switch (slot) { + case 0: + programStackPushPointer(program, critterGetArmor(obj)); + break; + case 1: + programStackPushPointer(program, critterGetItem2(obj)); + break; + case 2: + programStackPushPointer(program, critterGetItem1(obj)); + break; + case -2: + programStackPushInteger(program, obj->data.inventory.length); + break; + default: + programFatalError("mf_critter_inven_obj2: invalid type"); + } +} + +void mf_dialog_obj(Program* program, int args) +{ + if (GameMode::isInGameMode(GameMode::kDialog)) { + programStackPushPointer(program, gGameDialogSpeaker); + } else { + programStackPushPointer(program, nullptr); + } +} + +void mf_get_cursor_mode(Program* program, int args) +{ + programStackPushInteger(program, gameMouseGetMode()); +} + +void mf_get_flags(Program* program, int args) +{ + Object* object = static_cast(programStackPopPointer(program)); + programStackPushInteger(program, object->flags); +} + +void mf_get_object_data(Program* program, int args) +{ + size_t offset = static_cast(programStackPopInteger(program)); + void* ptr = programStackPopPointer(program); + + if (offset % 4 != 0) { + programFatalError("mf_get_object_data: bad offset %d", offset); + } + + int value = *reinterpret_cast(reinterpret_cast(ptr) + offset); + programStackPushInteger(program, value); +} + +void mf_get_text_width(Program* program, int args) +{ + const char* string = programStackPopString(program); + programStackPushInteger(program, fontGetStringWidth(string)); +} + +void mf_intface_redraw(Program* program, int args) +{ + if (args == 0) { + interfaceBarRefresh(); + } else { + // TODO: Incomplete. + programFatalError("mf_intface_redraw: not implemented"); + } + + programStackPushInteger(program, -1); +} + +void mf_loot_obj(Program* program, int args) +{ + if (GameMode::isInGameMode(GameMode::kInventory)) { + programStackPushPointer(program, inven_get_current_target_obj()); + } else { + programStackPushPointer(program, nullptr); + } +} + +void mf_metarule_exist(Program* program, int args) +{ + const char* metarule = programStackPopString(program); + + for (int index = 0; index < kMetarulesMax; index++) { + if (strcmp(kMetarules[index].name, metarule) == 0) { + programStackPushInteger(program, 1); + return; + } + } + + programStackPushInteger(program, 0); +} + +void mf_outlined_object(Program* program, int args) +{ + programStackPushPointer(program, gmouse_get_outlined_object()); +} + +void mf_set_cursor_mode(Program* program, int args) +{ + int mode = programStackPopInteger(program); + gameMouseSetMode(mode); + programStackPushInteger(program, -1); +} + +void mf_set_flags(Program* program, int args) +{ + int flags = programStackPopInteger(program); + Object* object = static_cast(programStackPopPointer(program)); + + object->flags = flags; + + programStackPushInteger(program, -1); +} + +void mf_set_ini_setting(Program* program, int args) +{ + ProgramValue value = programStackPopValue(program); + const char* triplet = programStackPopString(program); + + if (value.isString()) { + const char* stringValue = programGetString(program, value.opcode, value.integerValue); + if (!sfall_ini_set_string(triplet, stringValue)) { + debugPrint("set_ini_setting: unable to write '%s' to '%s'", + stringValue, + triplet); + } + } else { + int integerValue = value.asInt(); + if (!sfall_ini_set_int(triplet, integerValue)) { + debugPrint("set_ini_setting: unable to write '%d' to '%s'", + integerValue, + triplet); + } + } + + programStackPushInteger(program, -1); +} + +void mf_set_outline(Program* program, int args) +{ + int outline = programStackPopInteger(program); + Object* object = static_cast(programStackPopPointer(program)); + object->outline = outline; + programStackPushInteger(program, -1); +} + +void mf_show_window(Program* program, int args) +{ + if (args == 0) { + _windowShow(); + } else if (args == 1) { + const char* windowName = programStackPopString(program); + if (!_windowShowNamed(windowName)) { + debugPrint("show_window: window '%s' is not found", windowName); + } + } + + programStackPushInteger(program, -1); +} + +void mf_tile_refresh_display(Program* program, int args) +{ + tileWindowRefresh(); + programStackPushInteger(program, -1); +} + +void sfall_metarule(Program* program, int args) +{ + static ProgramValue values[6]; + + for (int index = 0; index < args; index++) { + values[index] = programStackPopValue(program); + } + + const char* metarule = programStackPopString(program); + + for (int index = 0; index < args; index++) { + programStackPushValue(program, values[index]); + } + + int metaruleIndex = -1; + for (int index = 0; index < kMetarulesMax; index++) { + if (strcmp(kMetarules[index].name, metarule) == 0) { + metaruleIndex = index; + break; + } + } + + if (metaruleIndex == -1) { + programFatalError("op_sfall_func: '%s' is not implemented", metarule); + } + + if (args < kMetarules[metaruleIndex].minArgs || args > kMetarules[metaruleIndex].maxArgs) { + programFatalError("op_sfall_func: '%s': invalid number of args", metarule); + } + + kMetarules[metaruleIndex].handler(program, args); +} + +} // namespace fallout diff --git a/src/sfall_metarules.h b/src/sfall_metarules.h new file mode 100644 index 0000000..360fc0c --- /dev/null +++ b/src/sfall_metarules.h @@ -0,0 +1,12 @@ +#ifndef FALLOUT_SFALL_METARULES_H_ +#define FALLOUT_SFALL_METARULES_H_ + +#include "interpreter.h" + +namespace fallout { + +void sfall_metarule(Program* program, int args); + +} // namespace fallout + +#endif /* FALLOUT_SFALL_METARULES_H_ */ diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index a07f21d..2534e4b 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -26,6 +26,7 @@ #include "sfall_ini.h" #include "sfall_kb_helpers.h" #include "sfall_lists.h" +#include "sfall_metarules.h" #include "stat.h" #include "svga.h" #include "tile.h" @@ -889,6 +890,48 @@ static void opArtExists(Program* program) programStackPushInteger(program, artExists(fid)); } +// sfall_func0 +static void op_sfall_func0(Program* program) +{ + sfall_metarule(program, 0); +} + +// sfall_func1 +static void op_sfall_func1(Program* program) +{ + sfall_metarule(program, 1); +} + +// sfall_func2 +static void op_sfall_func2(Program* program) +{ + sfall_metarule(program, 2); +} + +// sfall_func3 +static void op_sfall_func3(Program* program) +{ + sfall_metarule(program, 3); +} + +// sfall_func4 +static void op_sfall_func4(Program* program) +{ + sfall_metarule(program, 4); +} + +// sfall_func5 +static void op_sfall_func5(Program* program) +{ + sfall_metarule(program, 5); +} + +// sfall_func6 +static void op_sfall_func6(Program* program) +{ + sfall_metarule(program, 6); +} + // div (/) static void op_div(Program* program) { @@ -986,6 +1029,13 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x826F, op_obj_blocking_at); interpreterRegisterOpcode(0x8271, opPartyMemberList); interpreterRegisterOpcode(0x8274, opArtExists); + interpreterRegisterOpcode(0x8276, op_sfall_func0); + interpreterRegisterOpcode(0x8277, op_sfall_func1); + interpreterRegisterOpcode(0x8278, op_sfall_func2); + interpreterRegisterOpcode(0x8279, op_sfall_func3); + interpreterRegisterOpcode(0x827A, op_sfall_func4); + interpreterRegisterOpcode(0x827B, op_sfall_func5); + interpreterRegisterOpcode(0x827C, op_sfall_func6); interpreterRegisterOpcode(0x827F, op_div); } diff --git a/src/window.cc b/src/window.cc index fd9e9d8..8d496dd 100644 --- a/src/window.cc +++ b/src/window.cc @@ -70,6 +70,8 @@ typedef struct ManagedWindow { typedef int (*INITVIDEOFN)(); +static void redrawButton(ManagedButton* managedButton); + // 0x51DCAC static int _holdTime = 250; @@ -663,6 +665,38 @@ void _setButtonGFX(int width, int height, unsigned char* normal, unsigned char* } } +// 0x4B75F4 +static void redrawButton(ManagedButton* managedButton) +{ + _win_register_button_image(managedButton->btn, managedButton->normal, managedButton->pressed, managedButton->hover, false); +} + +// 0x4B7610 +bool _windowHide() +{ + ManagedWindow* managedWindow = &(gManagedWindows[gCurrentManagedWindowIndex]); + if (managedWindow->window == -1) { + return false; + } + + windowHide(managedWindow->window); + + return true; +} + +// 0x4B7648 +bool _windowShow() +{ + ManagedWindow* managedWindow = &(gManagedWindows[gCurrentManagedWindowIndex]); + if (managedWindow->window == -1) { + return false; + } + + windowShow(managedWindow->window); + + return true; +} + // 0x4B7734 int _windowWidth() { @@ -1714,7 +1748,8 @@ bool _windowAddButtonGfx(const char* buttonName, char* pressedFileName, char* no buttonSetMask(managedButton->btn, managedButton->normal); } - _win_register_button_image(managedButton->btn, managedButton->normal, managedButton->pressed, managedButton->hover, 0); + // NOTE: Uninline. + redrawButton(managedButton); return true; } @@ -1952,7 +1987,8 @@ bool _windowAddButtonTextWithOffsets(const char* buttonName, const char* text, i buttonSetMask(managedButton->btn, managedButton->normal); } - _win_register_button_image(managedButton->btn, managedButton->normal, managedButton->pressed, managedButton->hover, 0); + // NOTE: Uninline. + redrawButton(managedButton); return true; } @@ -2646,4 +2682,19 @@ void _fillBuf3x3(unsigned char* src, int srcWidth, int srcHeight, unsigned char* destWidth); } +bool _windowShowNamed(const char* windowName) +{ + for (int index = 0; index < MANAGED_WINDOW_COUNT; index++) { + ManagedWindow* managedWindow = &(gManagedWindows[index]); + if (managedWindow->window != -1) { + if (compat_stricmp(managedWindow->name, windowName) == 0) { + windowShow(managedWindow->window); + return true; + } + } + } + + return false; +} + } // namespace fallout diff --git a/src/window.h b/src/window.h index 2ae415c..dde6b19 100644 --- a/src/window.h +++ b/src/window.h @@ -63,6 +63,8 @@ void _doRightButtonPress(int btn, int keyCode); void sub_4B704C(int btn, int mouseEvent); void _doRightButtonRelease(int btn, int keyCode); void _setButtonGFX(int width, int height, unsigned char* normal, unsigned char* pressed, unsigned char* a5); +bool _windowHide(); +bool _windowShow(); int _windowWidth(); int _windowHeight(); bool _windowDraw(); @@ -127,6 +129,8 @@ void _drawScaledBuf(unsigned char* dest, int destWidth, int destHeight, unsigned void _alphaBltBuf(unsigned char* src, int srcWidth, int srcHeight, int srcPitch, unsigned char* alphaWindowBuffer, unsigned char* alphaBuffer, unsigned char* dest, int destPitch); void _fillBuf3x3(unsigned char* src, int srcWidth, int srcHeight, unsigned char* dest, int destWidth, int destHeight); +bool _windowShowNamed(const char* name); + } // namespace fallout #endif /* WINDOW_H */ From ad3e0eb752150c78cb5fbe48dd0dfc44c033d4f0 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 3 Sep 2023 16:51:29 +0300 Subject: [PATCH 123/136] Add op_explosions_metarule --- src/sfall_opcodes.cc | 71 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index 2534e4b..d070539 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -34,6 +34,18 @@ namespace fallout { +typedef enum ExplosionMetarule { + EXPL_FORCE_EXPLOSION_PATTERN = 1, + EXPL_FORCE_EXPLOSION_ART = 2, + EXPL_FORCE_EXPLOSION_RADIUS = 3, + EXPL_FORCE_EXPLOSION_DMGTYPE = 4, + EXPL_STATIC_EXPLOSION_RADIUS = 5, + EXPL_GET_EXPLOSION_DAMAGE = 6, + EXPL_SET_DYNAMITE_EXPLOSION_DAMAGE = 7, + EXPL_SET_PLASTIC_EXPLOSION_DAMAGE = 8, + EXPL_SET_EXPLOSION_MAX_TARGET = 9, +} ExplosionMetarule; + static constexpr int kVersionMajor = 4; static constexpr int kVersionMinor = 3; static constexpr int kVersionPatch = 4; @@ -651,6 +663,64 @@ static void opGetStringLength(Program* program) programStackPushInteger(program, static_cast(strlen(string))); } +// metarule2_explosions +static void op_explosions_metarule(Program* program) +{ + int param2 = programStackPopInteger(program); + int param1 = programStackPopInteger(program); + int metarule = programStackPopInteger(program); + + switch (metarule) { + case EXPL_FORCE_EXPLOSION_PATTERN: + if (param1 != 0) { + explosionSetPattern(2, 4); + } else { + explosionSetPattern(0, 6); + } + programStackPushInteger(program, 0); + break; + case EXPL_FORCE_EXPLOSION_ART: + explosionSetFrm(param1); + programStackPushInteger(program, 0); + break; + case EXPL_FORCE_EXPLOSION_RADIUS: + explosionSetRadius(param1); + programStackPushInteger(program, 0); + break; + case EXPL_FORCE_EXPLOSION_DMGTYPE: + explosionSetDamageType(param1); + programStackPushInteger(program, 0); + break; + case EXPL_STATIC_EXPLOSION_RADIUS: + weaponSetGrenadeExplosionRadius(param1); + weaponSetRocketExplosionRadius(param2); + programStackPushInteger(program, 0); + break; + case EXPL_GET_EXPLOSION_DAMAGE: + if (1) { + int minDamage; + int maxDamage; + explosiveGetDamage(param1, &minDamage, &maxDamage); + + ArrayId arrayId = CreateTempArray(2, 0); + SetArray(arrayId, ProgramValue { 0 }, ProgramValue { minDamage }, false, program); + SetArray(arrayId, ProgramValue { 1 }, ProgramValue { maxDamage }, false, program); + + programStackPushInteger(program, arrayId); + } + break; + case EXPL_SET_DYNAMITE_EXPLOSION_DAMAGE: + explosiveSetDamage(PROTO_ID_DYNAMITE_I, param1, param2); + break; + case EXPL_SET_PLASTIC_EXPLOSION_DAMAGE: + explosiveSetDamage(PROTO_ID_PLASTIC_EXPLOSIVES_I, param1, param2); + break; + case EXPL_SET_EXPLOSION_MAX_TARGET: + explosionSetMaxTargets(param1); + break; + } +} + // pow (^) static void op_power(Program* program) { @@ -1022,6 +1092,7 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x8253, opTypeOf); interpreterRegisterOpcode(0x8256, opGetArrayKey); interpreterRegisterOpcode(0x8257, opStackArray); + interpreterRegisterOpcode(0x8261, op_explosions_metarule); interpreterRegisterOpcode(0x8263, op_power); interpreterRegisterOpcode(0x8267, opRound); interpreterRegisterOpcode(0x826B, opGetMessage); From 6e9f1ae51734d583d949375a7c0fefd291b3c456 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 3 Sep 2023 18:23:19 +0300 Subject: [PATCH 124/136] Add op_set_self --- src/interpreter_extra.cc | 23 +++++++++++++++++++++-- src/scripts.cc | 2 ++ src/scripts.h | 2 ++ src/sfall_opcodes.cc | 14 ++++++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/interpreter_extra.cc b/src/interpreter_extra.cc index b2d3295..ca3e5e0 100644 --- a/src/interpreter_extra.cc +++ b/src/interpreter_extra.cc @@ -1580,12 +1580,22 @@ static void opPickup(Program* program) return; } - if (script->target == NULL) { + Object* self = script->target; + + // SFALL: Override `self` via `op_set_self`. + // CE: Implementation is different. Sfall integrates via `scriptGetSid` by + // returning fake script with overridden `self` (and `target` in this case). + if (script->overriddenSelf != nullptr) { + self = script->overriddenSelf; + script->overriddenSelf = nullptr; + } + + if (self == NULL) { scriptPredefinedError(program, "pickup_obj", SCRIPT_ERROR_OBJECT_IS_NULL); return; } - actionPickUp(script->target, object); + actionPickUp(self, object); } // drop_obj @@ -4548,6 +4558,15 @@ static void opUseObjectOnObject(Program* program) } Object* self = scriptGetSelf(program); + + // SFALL: Override `self` via `op_set_self`. + // CE: Implementation is different. Sfall integrates via `scriptGetSid` by + // returning fake script with overridden `self`. + if (script->overriddenSelf != nullptr) { + self = script->overriddenSelf; + script->overriddenSelf = nullptr; + } + if (PID_TYPE(self->pid) == OBJ_TYPE_CRITTER) { _action_use_an_item_on_object(self, target, item); } else { diff --git a/src/scripts.cc b/src/scripts.cc index 58dab5d..1485fff 100644 --- a/src/scripts.cc +++ b/src/scripts.cc @@ -2214,6 +2214,8 @@ int scriptAdd(int* sidPtr, int scriptType) scr->procs[index] = SCRIPT_PROC_NO_PROC; } + scr->overriddenSelf = nullptr; + scriptListExtent->length++; return 0; diff --git a/src/scripts.h b/src/scripts.h index 5cc3220..782591c 100644 --- a/src/scripts.h +++ b/src/scripts.h @@ -143,6 +143,8 @@ typedef struct Script { int field_D4; int field_D8; int field_DC; + + Object* overriddenSelf; } Script; extern const char* gScriptProcNames[SCRIPT_PROC_COUNT]; diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index d070539..d202c1b 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -345,6 +345,19 @@ static void op_set_proto_data(Program* program) *reinterpret_cast(reinterpret_cast(proto) + offset) = value; } +// set_self +static void op_set_self(Program* program) +{ + Object* obj = static_cast(programStackPopPointer(program)); + + int sid = scriptGetSid(program); + + Script* scr; + if (scriptGetScript(sid, &scr) == 0) { + scr->overriddenSelf = obj; + } +} + // list_begin static void opListBegin(Program* program) { @@ -1055,6 +1068,7 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x81F5, op_get_script); interpreterRegisterOpcode(0x8204, op_get_proto_data); interpreterRegisterOpcode(0x8205, op_set_proto_data); + interpreterRegisterOpcode(0x8206, op_set_self); interpreterRegisterOpcode(0x820D, opListBegin); interpreterRegisterOpcode(0x820E, opListNext); interpreterRegisterOpcode(0x820F, opListEnd); From 8c61b0bd8c9818506340562c21960356c951abe0 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 3 Sep 2023 19:15:50 +0300 Subject: [PATCH 125/136] Add sort_rect --- src/mapper/map_func.cc | 20 ++++++++++++++++++++ src/mapper/map_func.h | 1 + 2 files changed, 21 insertions(+) diff --git a/src/mapper/map_func.cc b/src/mapper/map_func.cc index 8b032f2..0ba769c 100644 --- a/src/mapper/map_func.cc +++ b/src/mapper/map_func.cc @@ -22,6 +22,26 @@ void copy_proto_lists() // TODO: Incomplete. } +// 0x484294 +void sort_rect(Rect* a, Rect* b) +{ + if (b->right > b->left) { + a->left = b->left; + a->right = b->right; + } else { + a->left = b->right; + a->right = b->left; + } + + if (b->bottom > b->top) { + a->top = b->top; + a->bottom = b->bottom; + } else { + a->top = b->bottom; + a->bottom = b->top; + } +} + // 0x4842D4 void draw_rect(Rect* rect, unsigned char color) { diff --git a/src/mapper/map_func.h b/src/mapper/map_func.h index 3d03b42..843f72a 100644 --- a/src/mapper/map_func.h +++ b/src/mapper/map_func.h @@ -7,6 +7,7 @@ namespace fallout { void setup_map_dirs(); void copy_proto_lists(); +void sort_rect(Rect* a, Rect* b); void draw_rect(Rect* rect); void erase_rect(Rect* rect); int toolbar_proto(int type, int id); From 30a3bf9b7127874ce3aae21834abc72af1d00b7e Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 3 Sep 2023 20:12:20 +0300 Subject: [PATCH 126/136] Add pick_region --- src/input.cc | 7 +++++++ src/input.h | 1 + src/mapper/map_func.cc | 44 ++++++++++++++++++++++++++++++++++++++++++ src/mapper/map_func.h | 3 ++- 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/input.cc b/src/input.cc index ccfa5a6..1bf56ac 100644 --- a/src/input.cc +++ b/src/input.cc @@ -199,6 +199,13 @@ int inputGetInput() return -1; } +// 0x4C8BC8 +void get_input_position(int* x, int* y) +{ + *x = _input_mx; + *y = _input_my; +} + // 0x4C8BDC void _process_bk() { diff --git a/src/input.h b/src/input.h index 93a0c93..5539af1 100644 --- a/src/input.h +++ b/src/input.h @@ -13,6 +13,7 @@ typedef int(ScreenshotHandler)(int width, int height, unsigned char* buffer, uns int inputInit(int a1); void inputExit(); int inputGetInput(); +void get_input_position(int* x, int* y); void _process_bk(); void enqueueInputEvent(int a1); void inputEventQueueReset(); diff --git a/src/mapper/map_func.cc b/src/mapper/map_func.cc index 0ba769c..92a96fa 100644 --- a/src/mapper/map_func.cc +++ b/src/mapper/map_func.cc @@ -1,6 +1,10 @@ #include "mapper/map_func.h" +#include "color.h" +#include "game_mouse.h" +#include "input.h" #include "memory.h" +#include "mouse.h" #include "proto.h" #include "svga.h" #include "window_manager.h" @@ -22,6 +26,46 @@ void copy_proto_lists() // TODO: Incomplete. } +// 0x4841C4 +void pick_region(Rect* rect) +{ + Rect temp; + int x; + int y; + + gameMouseSetCursor(MOUSE_CURSOR_PLUS); + gameMouseObjectsHide(); + + while (1) { + if (inputGetInput() == -2 + && (mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_DOWN) != 0) { + break; + } + } + + get_input_position(&x, &y); + temp.left = x; + temp.top = y; + temp.right = x; + temp.bottom = y; + + while ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_UP) == 0) { + inputGetInput(); + + get_input_position(&x, &y); + + if (x != temp.right || y != temp.bottom) { + erase_rect(rect); + sort_rect(rect, &temp); + draw_rect(rect, _colorTable[32747]); + } + } + + erase_rect(rect); + gameMouseSetCursor(MOUSE_CURSOR_ARROW); + gameMouseObjectsShow(); +} + // 0x484294 void sort_rect(Rect* a, Rect* b) { diff --git a/src/mapper/map_func.h b/src/mapper/map_func.h index 843f72a..71517e9 100644 --- a/src/mapper/map_func.h +++ b/src/mapper/map_func.h @@ -7,8 +7,9 @@ namespace fallout { void setup_map_dirs(); void copy_proto_lists(); +void pick_region(Rect* rect); void sort_rect(Rect* a, Rect* b); -void draw_rect(Rect* rect); +void draw_rect(Rect* rect, unsigned char color); void erase_rect(Rect* rect); int toolbar_proto(int type, int id); bool map_toggle_block_obj_viewing_on(); From 0d83cff24ec98ebacc0962cbe5f39a6fd02eaa1c Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 3 Sep 2023 20:24:36 +0300 Subject: [PATCH 127/136] Add place_entrance_hex --- src/actions.cc | 3 +++ src/actions.h | 2 ++ src/mapper/map_func.cc | 30 ++++++++++++++++++++++++++++++ src/mapper/map_func.h | 1 + 4 files changed, 36 insertions(+) diff --git a/src/actions.cc b/src/actions.cc index 292e5ce..8368546 100644 --- a/src/actions.cc +++ b/src/actions.cc @@ -48,6 +48,9 @@ typedef enum ScienceRepairTargetType { // 0x5106D0 static bool _action_in_explode = false; +// 0x5106D4 +int rotation; + // 0x5106E0 static const int gNormalDeathAnimations[DAMAGE_TYPE_COUNT] = { ANIM_DANCING_AUTOFIRE, diff --git a/src/actions.h b/src/actions.h index 67d425f..86c0f31 100644 --- a/src/actions.h +++ b/src/actions.h @@ -6,6 +6,8 @@ namespace fallout { +extern int rotation; + int _action_attack(Attack* attack); int _action_use_an_item_on_object(Object* a1, Object* a2, Object* a3); int _action_use_an_object(Object* a1, Object* a2); diff --git a/src/mapper/map_func.cc b/src/mapper/map_func.cc index 92a96fa..1fcd136 100644 --- a/src/mapper/map_func.cc +++ b/src/mapper/map_func.cc @@ -1,13 +1,17 @@ #include "mapper/map_func.h" +#include "actions.h" #include "color.h" #include "game_mouse.h" #include "input.h" +#include "map.h" #include "memory.h" #include "mouse.h" #include "proto.h" #include "svga.h" +#include "tile.h" #include "window_manager.h" +#include "window_manager_private.h" namespace fallout { @@ -26,6 +30,32 @@ void copy_proto_lists() // TODO: Incomplete. } +// 0x482708 +void place_entrance_hex() +{ + int x; + int y; + int tile; + + while (inputGetInput() != -2) { + } + + if ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_DOWN) != 0) { + if (_mouse_click_in(0, 0, _scr_size.right - _scr_size.left, _scr_size.bottom - _scr_size.top - 100)) { + mouseGetPosition(&x, &y); + + tile = tileFromScreenXY(x, y, gElevation); + if (tile != -1) { + if (tileSetCenter(tile, TILE_SET_CENTER_FLAG_IGNORE_SCROLL_RESTRICTIONS) == 0) { + mapSetEnteringLocation(tile, gElevation, rotation); + } else { + win_timed_msg("ERROR: Entrance out of range!", _colorTable[31744]); + } + } + } + } +} + // 0x4841C4 void pick_region(Rect* rect) { diff --git a/src/mapper/map_func.h b/src/mapper/map_func.h index 71517e9..ff06627 100644 --- a/src/mapper/map_func.h +++ b/src/mapper/map_func.h @@ -7,6 +7,7 @@ namespace fallout { void setup_map_dirs(); void copy_proto_lists(); +void place_entrance_hex(); void pick_region(Rect* rect); void sort_rect(Rect* a, Rect* b); void draw_rect(Rect* rect, unsigned char color); From ee1923866729e13f25aaa695edfb6a88b9e4a9e9 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 3 Sep 2023 20:48:23 +0300 Subject: [PATCH 128/136] Add update_toolname --- src/mapper/mapper.cc | 80 ++++++++++++++++++++++++++++++++++++++++++++ src/proto.cc | 2 +- src/proto.h | 1 + 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index 8afe3eb..ff3d184 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -46,6 +46,7 @@ static void edit_mapper(); static void mapper_load_toolbar(int a1, int a2); static void redraw_toolname(); static void clear_toolname(); +static void update_toolname(int* pid, int type, int id); static void update_high_obj_name(Object* obj); static int mapper_mark_exit_grid(); static void mapper_mark_all_exit_grids(); @@ -1408,6 +1409,85 @@ void clear_toolname() redraw_toolname(); } +// 0x48B328 +void update_toolname(int* pid, int type, int id) +{ + Proto* proto; + + *pid = toolbar_proto(type, id); + + if (protoGetProto(*pid, &proto) == -1) { + return; + } + + windowDrawText(tool_win, + protoGetName(proto->pid), + 120, + _scr_size.right - _scr_size.left - 149, + 60, + 260); + + switch (PID_TYPE(proto->pid)) { + case OBJ_TYPE_ITEM: + windowDrawText(tool_win, + gItemTypeNames[proto->item.type], + 120, + _scr_size.right - _scr_size.left - 149, + 70, + 260); + break; + case OBJ_TYPE_CRITTER: + windowDrawText(tool_win, + "", + 120, + _scr_size.right - _scr_size.left - 149, + 70, + 260); + break; + case OBJ_TYPE_WALL: + windowDrawText(tool_win, + proto_wall_light_str(proto->wall.flags), + 120, + _scr_size.right - _scr_size.left - 149, + 70, + 260); + break; + case OBJ_TYPE_TILE: + windowDrawText(tool_win, + "", + 120, + _scr_size.right - _scr_size.left - 149, + 70, + 260); + break; + case OBJ_TYPE_MISC: + windowDrawText(tool_win, + "", + 120, + _scr_size.right - _scr_size.left - 149, + 70, + 260); + break; + default: + windowDrawText(tool_win, + "", + 120, + _scr_size.right - _scr_size.left - 149, + 70, + 260); + break; + } + + windowDrawText(tool_win, + "", + 120, + _scr_size.right - _scr_size.left - 149, + 80, + 260); + + redraw_toolname(); +} + // 0x48B5BC void update_high_obj_name(Object* obj) { diff --git a/src/proto.cc b/src/proto.cc index f266498..0920054 100644 --- a/src/proto.cc +++ b/src/proto.cc @@ -167,7 +167,7 @@ char* _proto_none_str; static char* gBodyTypeNames[BODY_TYPE_COUNT]; // 0x664834 -static char* gItemTypeNames[ITEM_TYPE_COUNT]; +char* gItemTypeNames[ITEM_TYPE_COUNT]; // 0x66484C static char* gDamageTypeNames[DAMAGE_TYPE_COUNT]; diff --git a/src/proto.h b/src/proto.h index f49d1a6..40cded9 100644 --- a/src/proto.h +++ b/src/proto.h @@ -101,6 +101,7 @@ extern char _cd_path_base[COMPAT_MAX_PATH]; extern MessageList gProtoMessageList; extern char* _proto_none_str; +extern char* gItemTypeNames[ITEM_TYPE_COUNT]; void proto_make_path(char* path, int pid); int _proto_list_str(int pid, char* proto_path); From d46ee075838c5f853c2d2c4c12f6e69a77eb9099 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 3 Sep 2023 21:30:59 +0300 Subject: [PATCH 129/136] Add mapper_destroy_highlight_obj --- src/mapper/mapper.cc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index ff3d184..2902444 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -48,6 +48,7 @@ static void redraw_toolname(); static void clear_toolname(); static void update_toolname(int* pid, int type, int id); static void update_high_obj_name(Object* obj); +static void mapper_destroy_highlight_obj(Object** a1, Object** a2); static int mapper_mark_exit_grid(); static void mapper_mark_all_exit_grids(); @@ -1501,6 +1502,28 @@ void update_high_obj_name(Object* obj) } } +// 0x48B680 +void mapper_destroy_highlight_obj(Object** a1, Object** a2) +{ + Rect rect; + int elevation; + + if (a2 != NULL && *a2 != NULL) { + elevation = (*a2)->elevation; + reg_anim_clear(*a2); + objectDestroy(*a2, &rect); + tileWindowRefreshRect(&rect, elevation); + *a2 = NULL; + } + + if (a1 != NULL && *a1 != NULL) { + elevation = (*a1)->elevation; + objectDestroy(*a1, &rect); + tileWindowRefreshRect(&rect, elevation); + *a1 = NULL; + } +} + // 0x48C604 int mapper_inven_unwield(Object* obj, int right_hand) { From def8effa9f1026441119de40046e6c83094f7370 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 3 Sep 2023 21:42:17 +0300 Subject: [PATCH 130/136] Add handle_new_map --- src/mapper/mapper.cc | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index 2902444..de0d778 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -49,6 +49,8 @@ static void clear_toolname(); static void update_toolname(int* pid, int type, int id); static void update_high_obj_name(Object* obj); static void mapper_destroy_highlight_obj(Object** a1, Object** a2); +static void update_art(int a1, int a2); +static void handle_new_map(int* a1, int* a2); static int mapper_mark_exit_grid(); static void mapper_mark_all_exit_grids(); @@ -182,6 +184,9 @@ int art_scale_width = 49; // 0x559888 int art_scale_height = 48; +// 0x5598A0 +static bool map_entered = false; + // 0x5598A4 static char* tmp_map_name = kTmpMapName; @@ -1524,6 +1529,45 @@ void mapper_destroy_highlight_obj(Object** a1, Object** a2) } } +// 0x48B850 +void update_art(int a1, int a2) +{ + // TODO: Incomplete. +} + +// 0x48C524 +void handle_new_map(int* a1, int* a2) +{ + Rect rect; + + rect.left = 30; + rect.top = 62; + rect.right = 50; + rect.bottom = 88; + blitBufferToBuffer(e_num[gElevation], + 19, + 26, + 19, + tool + rect.top * rectGetWidth(&_scr_size) + rect.left, + rectGetWidth(&_scr_size)); + windowRefreshRect(tool_win, &rect); + + if (*a1 < 0 || *a1 > 6) { + *a1 = 4; + } + + *a2 = 0; + update_art(*a1, *a2); + + print_toolbar_name(OBJ_TYPE_TILE); + + map_entered = false; + + if (tileRoofIsVisible()) { + tile_toggle_roof(true); + } +} + // 0x48C604 int mapper_inven_unwield(Object* obj, int right_hand) { From a30fb4cfdd179a66074ea9d5ac2e2e7220a43bd4 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 3 Sep 2023 22:21:14 +0300 Subject: [PATCH 131/136] Add mapper_refresh_rotation --- src/mapper/mapper.cc | 101 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/src/mapper/mapper.cc b/src/mapper/mapper.cc index de0d778..f69d23f 100644 --- a/src/mapper/mapper.cc +++ b/src/mapper/mapper.cc @@ -2,6 +2,7 @@ #include +#include "actions.h" #include "animation.h" #include "art.h" #include "color.h" @@ -49,6 +50,7 @@ static void clear_toolname(); static void update_toolname(int* pid, int type, int id); static void update_high_obj_name(Object* obj); static void mapper_destroy_highlight_obj(Object** a1, Object** a2); +static void mapper_refresh_rotation(); static void update_art(int a1, int a2); static void handle_new_map(int* a1, int* a2); static int mapper_mark_exit_grid(); @@ -104,6 +106,26 @@ static char kSwapPrototypse[] = " Swap Prototypes "; static char kTmpMapName[] = "TMP$MAP#.MAP"; +// 0x559618 +int rotate_arrows_x_offs[] = { + 31, + 38, + 31, + 11, + 3, + 11, +}; + +// 0x559630 +int rotate_arrows_y_offs[] = { + 7, + 23, + 37, + 37, + 23, + 7, +}; + // 0x559648 char* menu_0[] = { kNew, @@ -208,6 +230,9 @@ int menu_val_2[8]; // 0x6EAA80 unsigned char e_num[4][19 * 26]; +// 0x6EBD28 +unsigned char rotate_arrows[2][6][10 * 10]; + // 0x6EC408 int menu_val_1[21]; @@ -1052,6 +1077,36 @@ int mapper_edit_init(int argc, char** argv) // ARROWS for (index = 0; index < ROTATION_COUNT; index++) { + int x = rotate_arrows_x_offs[index] + 285; + int y = rotate_arrows_y_offs[index] + 25; + unsigned char v1 = lbm_buf[27 * (_scr_size.right + 1) + 287]; + int k; + + blitBufferToBuffer(lbm_buf + y * rectGetWidth(&_scr_size) + x, + 10, + 10, + rectGetWidth(&_scr_size), + rotate_arrows[1][index], + 10); + + for (k = 0; k < 100; k++) { + if (rotate_arrows[1][index][k] == v1) { + rotate_arrows[1][index][k] = 0; + } + } + + blitBufferToBuffer(lbm_buf + y * rectGetWidth(&_scr_size) + x - 52, + 10, + 10, + rectGetWidth(&_scr_size), + rotate_arrows[0][index], + 10); + + for (k = 0; k < 100; k++) { + if (rotate_arrows[1][index][k] == v1) { + rotate_arrows[1][index][k] = 0; + } + } } // COPY @@ -1529,6 +1584,52 @@ void mapper_destroy_highlight_obj(Object** a1, Object** a2) } } +// 0x48B6EC +void mapper_refresh_rotation() +{ + Rect rect; + char string[2]; + int index; + + rect.left = 270; + rect.top = 431 - (_scr_size.bottom - 99); + rect.right = 317; + rect.bottom = rect.top + 47; + + sprintf(string, "%d", rotation); + + if (tool != NULL) { + windowFill(tool_win, + 290, + 452 - (_scr_size.bottom - 99), + 10, + 12, + tool[(452 - (_scr_size.bottom - 99)) * (_scr_size.right + 1) + 289]); + windowDrawText(tool_win, + string, + 10, + 292, + 452 - (_scr_size.bottom - 99), + 0x2010104); + + for (index = 0; index < 6; index++) { + int x = rotate_arrows_x_offs[index] + 269; + int y = rotate_arrows_y_offs[index] + (430 - (_scr_size.bottom - 99)); + + blitBufferToBufferTrans(rotate_arrows[index == rotation][index], + 10, + 10, + 10, + tool + y * (_scr_size.right + 1) + x, + _scr_size.right + 1); + } + + windowRefreshRect(tool_win, &rect); + } else { + debugPrint("Error: mapper_refresh_rotation: tool buffer invalid!"); + } +} + // 0x48B850 void update_art(int a1, int a2) { From 91806d8ccb4215c9df3bf626112d4f101fba6e6b Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 3 Sep 2023 22:31:52 +0300 Subject: [PATCH 132/136] Fix missing include --- src/sfall_metarules.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sfall_metarules.cc b/src/sfall_metarules.cc index 558645b..8c3e110 100644 --- a/src/sfall_metarules.cc +++ b/src/sfall_metarules.cc @@ -1,5 +1,7 @@ #include "sfall_metarules.h" +#include + #include "combat.h" #include "debug.h" #include "game.h" From 0e447c55a814cbc5472ca8359ecfdc9f24235c0b Mon Sep 17 00:00:00 2001 From: Eir Nym <485399+eirnym@users.noreply.github.com> Date: Thu, 28 Sep 2023 16:26:52 +0200 Subject: [PATCH 133/136] Upgrade zlib to 1.3 (#323) --- third_party/zlib/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/zlib/CMakeLists.txt b/third_party/zlib/CMakeLists.txt index 7a89186..7cca827 100644 --- a/third_party/zlib/CMakeLists.txt +++ b/third_party/zlib/CMakeLists.txt @@ -2,7 +2,7 @@ include(FetchContent) FetchContent_Declare(zlib GIT_REPOSITORY "https://github.com/madler/zlib" - GIT_TAG "v1.2.11" + GIT_TAG "v1.3" ) FetchContent_GetProperties(zlib) From 81fce5f4a29a51c983780225b9f0538573bea727 Mon Sep 17 00:00:00 2001 From: Vasilii Rogin Date: Mon, 9 Oct 2023 20:50:59 +0300 Subject: [PATCH 134/136] Fix wrong pointer dereferecing in aiFindAttackers (#324) --- src/combat_ai.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/combat_ai.cc b/src/combat_ai.cc index 2110b9d..315f574 100644 --- a/src/combat_ai.cc +++ b/src/combat_ai.cc @@ -1464,7 +1464,7 @@ static int aiFindAttackers(Object* critter, Object** whoHitMePtr, Object** whoHi *whoHitFriendPtr = NULL; } - if (*whoHitByFriendPtr != NULL) { + if (whoHitByFriendPtr != NULL) { *whoHitByFriendPtr = NULL; } From f411d75643211dce56772ff54c7a21494b6a1d9f Mon Sep 17 00:00:00 2001 From: sonilyan <286258386@qq.com> Date: Tue, 31 Oct 2023 18:46:25 +0800 Subject: [PATCH 135/136] Add overriddenSelf initialization for scriptRead (#329) --- src/scripts.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/scripts.cc b/src/scripts.cc index 1485fff..7c8755c 100644 --- a/src/scripts.cc +++ b/src/scripts.cc @@ -1998,6 +1998,8 @@ static int scriptRead(Script* scr, File* stream) scr->localVarsCount = 0; } + scr->overriddenSelf = nullptr; + return 0; } From 601f0c7b44a4bab719bed92d6196e25c1a1ea8ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=BBP=2E=28P=20izzy=29?= Date: Tue, 16 Jan 2024 06:06:47 -0600 Subject: [PATCH 136/136] fix CMake condition to allow build on OpenBSD & FreeBSD (#341) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ae87c6e..d7f8f39 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -362,7 +362,7 @@ add_subdirectory("third_party/fpattern") target_link_libraries(${EXECUTABLE_NAME} ${FPATTERN_LIBRARY}) target_include_directories(${EXECUTABLE_NAME} PRIVATE ${FPATTERN_INCLUDE_DIR}) -if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Linux") +if((NOT ${CMAKE_SYSTEM_NAME} MATCHES "Linux") AND (NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") AND (NOT ${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")) add_subdirectory("third_party/zlib") add_subdirectory("third_party/sdl2") else()