From f38b158a8e099cf9196bdf7ed9d948e5e979a82e Mon Sep 17 00:00:00 2001 From: DMD Date: Thu, 5 Nov 2020 20:25:59 +0200 Subject: [PATCH 1/2] Removed anim_goal_setups.cpp generation from normal run --- TemplePlus/animgoals/animgoals_hooks.cpp | 1276 +++++++++++----------- 1 file changed, 642 insertions(+), 634 deletions(-) diff --git a/TemplePlus/animgoals/animgoals_hooks.cpp b/TemplePlus/animgoals/animgoals_hooks.cpp index 11e88deb5..22e0615e6 100644 --- a/TemplePlus/animgoals/animgoals_hooks.cpp +++ b/TemplePlus/animgoals/animgoals_hooks.cpp @@ -1,634 +1,642 @@ - -#include "stdafx.h" - -#include - -#include "util/fixes.h" -#include - -#include "anim.h" -#include "animgoals_stackentry.h" -#include "obj.h" -#include "gamesystems/gamesystems.h" - -static int HandleOptionalSlotResult(std::optional slotId, AnimSlotId *slotIdOut) { - if (slotId) { - if (slotIdOut) { - *slotIdOut = *slotId; - } - return 1; - } - else { - if (slotIdOut) { - slotIdOut->Clear(); - } - return 0; - } -} - -static const char *AnimGoalDataNames[] = { - "AGDATA_SELF_OBJ", "AGDATA_TARGET_OBJ", "AGDATA_BLOCK_OBJ", - "AGDATA_SCRATCH_OBJ", "AGDATA_PARENT_OBJ", "AGDATA_TARGET_TILE", - "AGDATA_RANGE_DATA", "AGDATA_ANIM_ID", "AGDATA_ANIM_ID_PREV", - "AGDATA_ANIM_DATA", "AGDATA_SPELL_DATA", "AGDATA_SKILL_DATA", - "AGDATA_FLAGS_DATA", "AGDATA_SCRATCH_VAL1", "AGDATA_SCRATCH_VAL2", - "AGDATA_SCRATCH_VAL3", "AGDATA_SCRATCH_VAL4", "AGDATA_SCRATCH_VAL5", - "AGDATA_SCRATCH_VAL6", "AGDATA_SOUND_HANDLE" }; - -static class AnimGoalsHooks : public TempleFix { -public: - - std::map goalfunc_names; - - AnimGoalsHooks() { - - goalfunc_names[0x1000c9c0] = "GoalStateCallback1"sv; - goalfunc_names[0x1000ccf0] = "GoalStateCallback3"sv; // Always 1 - goalfunc_names[0x1000ce10] = "GoalSetOffAndDestroyParam1"sv; - goalfunc_names[0x1000ce60] = "GoalParam1ObjCloseToParam2Loc"sv; - goalfunc_names[0x1000cf10] = "GoalTargetLocWithinRadius"sv; - goalfunc_names[0x1000cfe0] = "GoalStateCallback7"sv; - goalfunc_names[0x1000d060] = "GoalStateCallback8"sv; - goalfunc_names[0x1000d150] = "GoalIsCurrentPathValid"sv; - goalfunc_names[0x1000d560] = "GoalCalcPathToTarget"sv; - goalfunc_names[0x1000db30] = "GoalCalcPathToTarget2"sv; - goalfunc_names[0x1000dca0] = "GoalKnockbackFunc"sv; - goalfunc_names[0x1000dd80] = "GoalMoveAwayFromObj"sv; - goalfunc_names[0x1000e250] = "GoalIsConcealed"sv; - goalfunc_names[0x1000e270] = "GoalIsProne"sv; - goalfunc_names[0x1000e2c0] = "GoalStunnedExpire"sv; - goalfunc_names[0x1000e4f0] = "GoalHasDoorInPath"sv; - goalfunc_names[0x1000e6f0] = "GoalFindPathNear"sv; - goalfunc_names[0x1000e8b0] = "GoalFindPathNearObject"sv; - goalfunc_names[0x1000ec10] = "GoalFindPathNearObjectCombat"sv; - goalfunc_names[0x1000efb0] = "GoalIsParam1Door"sv; - goalfunc_names[0x1000f000] = "GoalPlayDoorLockedSound"sv; - goalfunc_names[0x1000f0d0] = "GoalIsDoorMagicallyHeld"sv; - goalfunc_names[0x1000f140] = "GoalAttemptOpenDoor"sv; - goalfunc_names[0x1000f2c0] = "GoalIsDoorLocked"sv; - goalfunc_names[0x1000f350] = "GoalDoorAlwaysFalse"sv; - goalfunc_names[0x1000f400] = "GoalIsDoorUnlocked"sv; - goalfunc_names[0x1000f490] = "GoalSetRadiusTo2"sv; - goalfunc_names[0x1000f550] = "GoalUseObject"sv; - goalfunc_names[0x1000f860] = "GoalUseItemOnObj"sv; - goalfunc_names[0x1000f9a0] = "GoalUseItemOnObjWithSkillDummy"sv; - goalfunc_names[0x1000fbc0] = "GoalUseItemOnLoc"sv; - goalfunc_names[0x1000fce0] = "GoalUseItemOnLocWithSkillDummy"sv; - goalfunc_names[0x1000fec0] = "GoalSetNoFlee"sv; - goalfunc_names[0x1000ff10] = "GoalIsAlive"sv; - goalfunc_names[0x1000ff60] = "GoalPlaySoundScratch5"sv; - goalfunc_names[0x1000fff0] = "GoalAttemptAttackCheck"sv; - goalfunc_names[0x10010160] = "GoalCritterShouldNotAutoAnimate"sv; - goalfunc_names[0x100101d0] = "GoalAttackerHasRangedWeapon"sv; - goalfunc_names[0x10010250] = "GoalReturnTrue"sv; - goalfunc_names[0x10010290] = "GoalReturnFalse"sv; - goalfunc_names[0x100102c0] = "GoalAttemptSpell"sv; - goalfunc_names[0x10010410] = "GoalCastConjureEnd"sv; - goalfunc_names[0x100104a0] = "GoalDestroyParam1"sv; - goalfunc_names[0x10010500] = "GoalWasInterrupted"sv; - goalfunc_names[0x10010520] = "GoalIsAnimatingConjuration"sv; - goalfunc_names[0x100105f0] = "GoalStartConjurationAnim"sv; - goalfunc_names[0x10010760] = "GoalAreOnSameTile"sv; - goalfunc_names[0x100107e0] = "GoalPickpocketPerform"sv; - goalfunc_names[0x10010aa0] = "GoalActionPerform"sv; - goalfunc_names[0x10010b50] = "GoalCheckSlotFlag40000"sv; - goalfunc_names[0x10010b70] = "GoalCheckParam2AgainstStateFlagData"sv; - goalfunc_names[0x10010b90] = "GoalPickLock"sv; - goalfunc_names[0x10010cd0] = "GoalAttemptTrapDisarm"sv; - goalfunc_names[0x10010d60] = "GoalActionPerform2"sv; - goalfunc_names[0x10010e00] = "GoalHasReachWithMainWeapon"sv; - goalfunc_names[0x10010f70] = "GoalThrowItem"sv; - goalfunc_names[0x100110a0] = "GoalNotPreventedFromTalking"sv; - goalfunc_names[0x100111e0] = "GoalIsWithinTalkingDistance"sv; - goalfunc_names[0x100112d0] = "GoalInitiateDialog"sv; - goalfunc_names[0x10011370] = "GoalOpenDoorCleanup"sv; - goalfunc_names[0x10011420] = "GoalCloseDoorCleanup"sv; - goalfunc_names[0x100114d0] = "GoalIsDoorSticky"sv; - goalfunc_names[0x10011530] = "GoalIsLiveCritterNear"sv; - goalfunc_names[0x10011600] = "GoalSetRunningFlag"sv; - goalfunc_names[0x10011660] = "GoalEnterCombat"sv; - goalfunc_names[0x100117f0] = "GoalLeaveCombat"sv; - goalfunc_names[0x10011880] = "GoalPlayGetHitAnim"sv; - goalfunc_names[0x10011a30] = "GoalPlayDodgeAnim"sv; - goalfunc_names[0x10011be0] = "GoalPlayAnim"sv; - goalfunc_names[0x10011cf0] = "GoalSaveParam1InScratch"sv; - goalfunc_names[0x10011d20] = "GoalSaveStateDataInSkillData"sv; - goalfunc_names[0x10011d40] = "GoalSaveStateDataOrSpellRangeInRadius"sv; - goalfunc_names[0x10011dc0] = "GoalSetTargetLocFromObj"sv; - goalfunc_names[0x10011e70] = "GoalSetRadiusTo4"sv; - goalfunc_names[0x10011e90] = "GoalSetRadiusToAiSpread"sv; - goalfunc_names[0x10011f20] = "GoalIsCloserThanDesiredSpread"sv; - goalfunc_names[0x10012040] = "GoalTurnTowardsOrAway"sv; - goalfunc_names[0x100121b0] = "GoalPlayRotationAnim"sv; - goalfunc_names[0x100122a0] = "GoalRotate"sv; - goalfunc_names[0x100125f0] = "GoalIsRotatedTowardNextPathNode"sv; - goalfunc_names[0x100127b0] = "GoalIsRotatedTowardTarget"sv; - goalfunc_names[0x10012910] = "GoalSetRotationToParam2"sv; - goalfunc_names[0x10012a00] = "GoalSetRotationToFaceTargetObj"sv; - goalfunc_names[0x10012b60] = "GoalSetRotationToFaceTargetLoc"sv; - goalfunc_names[0x10012c70] = "GoalIsSlotFlag10NotSet"sv; - goalfunc_names[0x10012c80] = "GoalSlotFlagSet8If4AndNotSetYet"sv; - goalfunc_names[0x10012ca0] = "GoalProjectileCleanup"sv; - goalfunc_names[0x10012cf0] = "GoalAnimateCleanup"sv; - goalfunc_names[0x10012d10] = "GoalAnimateForever"sv; - goalfunc_names[0x10012fa0] = "GoalLoopWhileCloseToParty"sv; - goalfunc_names[0x10013080] = "GoalFreeSoundHandle"sv; - goalfunc_names[0x100130f0] = "GoalIsAliveAndConscious"sv; - goalfunc_names[0x10013250] = "GoalBeginMoveStraight"sv; - goalfunc_names[0x10013af0] = "GoalUpdateMoveStraight"sv; - goalfunc_names[0x100140c0] = "GoalSetNoBlockIfNotInParty"sv; - goalfunc_names[0x10014170] = "GoalDyingCleanup"sv; - goalfunc_names[0x100147d0] = "GoalMoveAlongPath"sv; - goalfunc_names[0x10014f10] = "GoalIsNotStackFlagsData20"sv; - goalfunc_names[0x10014f30] = "GoalJiggleAlongYAxis"sv; - goalfunc_names[0x10014ff0] = "GoalStartJigglingAlongYAxis"sv; - goalfunc_names[0x100150a0] = "GoalEndJigglingAlongYAxis"sv; - goalfunc_names[0x10015150] = "GoalIsNotStackFlagsData40"sv; - goalfunc_names[0x10015170] = "GoalSetSlotFlags4"sv; - goalfunc_names[0x10015240] = "GoalActionPerform3"sv; - goalfunc_names[0x10017100] = "GoalSpawnFireball"sv; - goalfunc_names[0x10017170] = "GoalPleaseMove"sv; - goalfunc_names[0x10017460] = "GoalIsTargetWithinRadius"sv; - goalfunc_names[0x10017570] = "GoalWander"sv; - goalfunc_names[0x10017810] = "GoalWanderSeekDarkness"sv; - goalfunc_names[0x10017b30] = "GoalIsDoorFullyClosed"sv; - goalfunc_names[0x10017dd0] = "GoalTriggerSpell"sv; - goalfunc_names[0x10017f80] = "GoalUnconcealCleanup"sv; - goalfunc_names[0x10018050] = "GoalResetToIdleAnim"sv; - goalfunc_names[0x10018160] = "GoalResetToIdleAnimUnstun"sv; - goalfunc_names[0x10018290] = "GoalThrowItemCleanup"sv; - goalfunc_names[0x10018400] = "GoalThrowItemPlayAnim"sv; - goalfunc_names[0x100185e0] = "GoalUnconcealAnimate"sv; - goalfunc_names[0x10018730] = "GoalStartIdleAnimIfCloseToParty"sv; - goalfunc_names[0x10018810] = "GoalStartFidgetAnimIfCloseToParty"sv; - goalfunc_names[0x100188f0] = "GoalContinueWithAnim"sv; - goalfunc_names[0x100189b0] = "GoalContinueWithAnim2"sv; - goalfunc_names[0x10018a70] = "GoalPlayDoorOpenAnim"sv; - goalfunc_names[0x10018b90] = "GoalContinueWithDoorOpenAnim"sv; - goalfunc_names[0x10018c50] = "GoalPlayDoorCloseAnim"sv; - goalfunc_names[0x10018d40] = "GoalContinueWithDoorCloseAnim"sv; - goalfunc_names[0x10018e00] = "GoalPickLockPlayPushDoorOpenAnim"sv; - goalfunc_names[0x10018ee0] = "GoalPickLockContinueWithAnim"sv; - goalfunc_names[0x10018fa0] = "GoalDyingPlaySoundAndRipples"sv; - goalfunc_names[0x10019070] = "GoalDyingContinueAnim"sv; - goalfunc_names[0x10019130] = "GoalAnimateFireDmgContinueAnim"sv; - goalfunc_names[0x100191f0] = "GoalStunnedPlayAnim"sv; - goalfunc_names[0x10019330] = "GoalStunnedContinueAnim"sv; - goalfunc_names[0x10019470] = "GoalPlayGetUpAnim"sv; - goalfunc_names[0x10019540] = "GoalPlayUnconcealAnim"sv; - goalfunc_names[0x10019630] = "GoalPlayMoveAnim"sv; - goalfunc_names[0x10019920] = "GoalPlayWaterRipples"sv; - goalfunc_names[0x100199b0] = "GoalContinueMoveStraight"sv; - goalfunc_names[0x10019c20] = "GoalApplyKnockback"sv; - goalfunc_names[0x10019e10] = "GoalDyingReturnTrue"sv; - goalfunc_names[0x10019e70] = "GoalAttemptMoveCleanup"sv; - goalfunc_names[0x10019f00] = "GoalAttackPlayWeaponHitEffect"sv; - goalfunc_names[0x1001a080] = "GoalAttackContinueWithAnim"sv; - goalfunc_names[0x1001a170] = "GoalAttackPlayIdleAnim"sv; - goalfunc_names[0x1001bf70] = "GoalMoveNearUpdateRadiusToReach"sv; - goalfunc_names[0x1001c100] = "GoalAttackEndTurnIfUnreachable"sv; - goalfunc_names[0x101f5850] = "AlwaysSucceed"sv; - goalfunc_names[0x10262530] = "AlwaysFail"sv; - } - - static std::string_view GetArgInfoText(int animParamType) { - if (animParamType < 21) { - return AnimGoalDataNames[animParamType]; - } - else if (animParamType == 31) { - return "SELF_OBJ_PRECISE_LOC"; - } - else if (animParamType == 32) { - return "TARGET_OBJ_PRECISE_LOC"; - } - else if (animParamType == 33) { - return "NULL_HANDLE"; - } - else if (animParamType == 34) { - return "TARGET_LOC_PRECISE"; - } - else { - return to_string(animParamType); - } - } - - static std::string GetTransitionDelay(AnimStateTransition transition) { - if (transition.delay == 0) { - return ""; - } - auto delayText = std::to_string(transition.delay); - if (transition.delay == transition.DelaySlot) { - delayText = "DELAY_SLOT"; - } - else if (transition.delay == transition.DelayRandom) { - delayText = "DELAY_RANDOM"; - } - else if (transition.delay == transition.DelayCustom) { - delayText = "DELAY_CUSTOM"; - } - return ", " + delayText; - } - - static std::string GetTransitionArgs(AnimStateTransition transition) { - /*int nextState = transition.newState & ~ASTF_MASK; - - // Simple transition into another state - if (!(transition.newState & ASTF_MASK)) { - return fmt::format("Transition::State({}{})", transition.newState - 1, GetTransitionDelay(transition)); - } - - if ((transition.newState & ASTF_POP_ALL) == ASTF_POP_ALL) { - return fmt::format("Transition::EndAll(){}", GetTransitionDelay(transition)); - } - - if ((transition.newState & ASTF_PUSH_GOAL) == ASTF_PUSH_GOAL) { - auto remainingMask = (transition.newState & ASTF_MASK) & ~ASTF_PUSH_GOAL; - if (remainingMask != 0 && remainingMask != ASTF_REWIND && remainingMask != ASTF_POP_GOAL) { - __debugbreak(); - } - return fmt::format("Transition::PushGoal({}){}", GetAnimGoalTypeName((AnimGoalType) nextState), GetTransitionDelay(transition)); - }*/ - - auto newState = transition.newState; - - std::vector flags; - if ((newState & ASTF_GOAL_INVALIDATE_PATH) == ASTF_GOAL_INVALIDATE_PATH) { - flags.push_back("T_INVALIDATE_PATH"); - newState &= ~ASTF_GOAL_INVALIDATE_PATH; - } - - if ((newState & ASTF_POP_GOAL) == ASTF_POP_GOAL) { - flags.push_back("T_POP_GOAL"); - newState &= ~ASTF_POP_GOAL; - } - - if ((newState & ASTF_POP_GOAL_TWICE) == ASTF_POP_GOAL_TWICE) { - flags.push_back("T_POP_GOAL_TWICE"); - newState &= ~ASTF_POP_GOAL_TWICE; - } - - if ((newState & ASTF_PUSH_GOAL) == ASTF_PUSH_GOAL) { - auto pushGoal = GetAnimGoalTypeName((AnimGoalType)(transition.newState & ~ASTF_MASK)); - flags.push_back(fmt::format("T_PUSH_GOAL({})", pushGoal)); - newState &= ~ASTF_PUSH_GOAL; - newState &= ASTF_MASK; - } - - if ((newState & ASTF_POP_ALL) == ASTF_POP_ALL) { - flags.push_back("T_POP_ALL"); - newState &= ~ASTF_POP_ALL; - } - - if ((newState & ASTF_REWIND) == ASTF_REWIND) { - flags.push_back("T_REWIND"); - newState &= ~ASTF_REWIND; - } - - if ((newState & ASTF_MASK) != 0) { - flags.push_back(fmt::format("0x{:x}", newState & ASTF_MASK)); - newState &= ~ASTF_MASK; - } - - if (!(transition.newState & ASTF_MASK)) { - flags.push_back(fmt::format("T_GOTO_STATE({})", transition.newState - 1)); - } - - std::string flagsString; - if (flags.empty()) { - flagsString = "0"; - } else { - for (size_t i = 0; i < flags.size(); i++) { - if (i > 0) { - flagsString += "|"; - } - flagsString += flags[i]; - } - } - - auto delayText = std::to_string(transition.delay); - if (transition.delay == transition.DelaySlot) { - delayText = "DELAY_SLOT"; - } else if (transition.delay == transition.DelayRandom) { - delayText = "DELAY_RANDOM"; - } else if (transition.delay == transition.DelayCustom) { - delayText = "DELAY_CUSTOM"; - } - - if (transition.delay != 0) { - return fmt::format("{}, {}", flagsString, delayText); - } else { - return fmt::format("{}", flagsString); - } - } - - std::string GetCallbackFunctionRef(uint32_t address) { - if (!goalfunc_names[address].empty()) { - return std::string(goalfunc_names[address]); - } - - return fmt::format("0x{:x}", address); - } - - virtual void apply() override { - using AnimGoalArray = const AnimGoal*[82]; - AnimGoalArray& goals = temple::GetRef(0x102BD1B0); - - - auto fh = fopen("anim_goal_setup.cpp", "wt"); - - for (auto entry : goalfunc_names) { - fmt::print(fh, "int {}(AnimSlot &slot); // Originally @ 0x{:x}\n", entry.second, entry.first); - } - - for (auto entry : goalfunc_names) { - fmt::print(fh, "\n// Originally @ 0x{:x}\n", entry.first); - fmt::print(fh, "int {}(AnimSlot &slot) {{\n", entry.second); - fmt::print(fh, "\tstatic org = temple::GetRef(0x{:x});\n", entry.first); - fmt::print(fh, "\treturn org(slot);\n"); - fmt::print(fh, "}}\n"); - } - - for (int i = 0; i < ag_count; i++) { - auto goal = goals[i]; - auto goalName = GetAnimGoalTypeName((AnimGoalType)i); - if (!goal) { - fmt::print(fh, "\n"); - fmt::print(fh, "// {} was undefined\n", goalName); - fmt::print(fh, "\n"); - continue; - } - - auto builderVar = std::string(GetAnimGoalTypeName((AnimGoalType)i)); - if (builderVar.find("ag_") == 0) { - builderVar.replace(0, 3, ""); - } - - fmt::print(fh, "// {}\n", goalName); - fmt::print(fh, "auto {} = AnimGoalBuilder(goals_[{}])", builderVar, goalName); - fmt::print(fh, "\n\t.SetPriority({})", GetAnimGoalPriorityText(goal->priority)); - if (goal->interruptAll) { - fmt::print(fh, "\n\t.SetInterruptAll(true)"); - } - if (goal->field_C) { - fmt::print(fh, "\n\t.SetFieldC(true)"); - } - if (goal->field_10) { - fmt::print(fh, "\n\t.SetField10(true)"); - } - if (goal->relatedGoal[0] != -1) { - fmt::print(fh, "\n\t.SetRelatedGoals("); - for (int j = 0; j < 3; j++) { - if (goal->relatedGoal[j] == -1) { - break; - } - if (j > 0) { - fmt::print(fh, ", "); - } - fmt::print(fh, "{}", GetAnimGoalTypeName((AnimGoalType) goal->relatedGoal[j])); - } - fmt::print(fh, ")"); - } - fmt::print(fh, ";\n"); - - for (int j = -1; j < goal->statecount; j++) { - AnimGoalState state; - if (j == -1) { - state = goal->state_special; - if (!state.callback) { - continue; - } - fmt::print(fh, "{}.AddCleanup({})", builderVar, GetCallbackFunctionRef((uint32_t)state.callback)); - } else { - state = goal->states[j]; - fmt::print(fh, "{}.AddState({}) // Index {}", builderVar, GetCallbackFunctionRef((uint32_t)state.callback), j); - } - - Expects(state.argInfo2 == -1 || state.argInfo1 != -1); - - if (state.argInfo1 != -1) { - fmt::print(fh, "\n\t.SetArgs({}", GetArgInfoText(state.argInfo1)); - - if (state.argInfo2 != -1) { - fmt::print(fh, ", {}", GetArgInfoText(state.argInfo2)); - } - fmt::print(fh, ")"); - } - - if (state.flagsData != -1) { - fmt::print(fh, "\n\t.SetFlagsData({})", state.flagsData); - } - - // Print transitions - if (j != -1) { - fmt::print(fh, "\n\t.OnSuccess({})", GetTransitionArgs(state.afterSuccess)); - } - if (j != -1) { - fmt::print(fh, "\n\t.OnFailure({})", GetTransitionArgs(state.afterFailure)); - } - - fmt::print(fh, ";\n"); - } - - fmt::print(fh, "\n"); - - } - - fclose(fh); - - // All functions that use 102BD1B0 should now be replaced (the goals array) - // breakRegion(0x102BD1B0, 0x102BD2F4); - - // anim_first_run_idx_for_obj - replaceFunction(0x10054e20, [](objHndl handle) { - return gameSystems->GetAnim().GetFirstRunSlotIdxForObj(handle); - }); - - // anim_next_run_idx_for_obj - replaceFunction(0x10054e70, [](int slotIndex, objHndl handle) { - return gameSystems->GetAnim().GetNextRunSlotIdxForObj(handle, slotIndex); - }); - - // anim_run_id_for_obj - replaceFunction(0x1000c430, [](objHndl handle, AnimSlotId *slotIdOut) { - - auto slotId = gameSystems->GetAnim().GetFirstRunSlotId(handle); - return HandleOptionalSlotResult(slotId, slotIdOut); - - }); - - // anim_get_highest_priority - replaceFunction(0x1000c500, [](objHndl handle) { - return gameSystems->GetAnim().GetCurrentPriority(handle); - }); - - // anim_pop_goal - replaceFunction(0x10016FC0, - [](AnimSlot &slot, const uint32_t *popFlags, const AnimGoal **newGoal, AnimSlotGoalStackEntry **newCurrentGoal, BOOL *stopProcessing) { - bool stopProcessingBool = *stopProcessing == TRUE; - gameSystems->GetAnim().PopGoal(slot, *popFlags, newGoal, newCurrentGoal, &stopProcessingBool); - *stopProcessing = stopProcessingBool ? TRUE : FALSE; - }); - - // anim_break_nodes_to_map - replaceFunction(0x1001af30, [](const char *mapName) { - gameSystems->GetAnim().SaveToMap(mapName); - return 0; - }); - - - replaceFunction(0x1001B830, [](const TimeEvent *evt) -> int { - auto result = 0; - - result = gameSystems->GetAnim().ProcessAnimEvent(evt); - temple::GetRef(0x102B2654) = gameSystems->GetAnim().mCurrentlyProcessingSlotIdx; - - return result; - }); - - // anim_is_running_goal - replaceFunction(0x10054f30, [](objHndl handle, AnimGoalType goalType, AnimSlotId *idOut) { - if (goalType == -1) { - return 0; // Weird... - } - - return gameSystems->GetAnim().DoesObjHaveGoal(handle, goalType, idOut) ? 1 : 0; - }); - - // anim_get_slot_with_fieldc_goal - replaceFunction(0x10054fd0, [](objHndl handle, AnimSlotId *idOut) { - - auto id = gameSystems->GetAnim().GetRunSlotWithGoalWithoutFieldC(handle); - return HandleOptionalSlotResult(id, idOut); - - }); - - // anim_has_attack_anim - replaceFunction(0x10055060, [](objHndl handle, AnimSlotId *idOut, objHndl target) { - auto id = gameSystems->GetAnim().HasAttackAnim(handle, target); - return HandleOptionalSlotResult(id, idOut); - }); - - // anim_cur_goal_has_field10_1 - replaceFunction(0x100551a0, [](AnimSlot *slot) { - return gameSystems->GetAnim().CurrentGoalHasField10_1(*slot) ? 1 : 0; - }); - - // anim_make_goal_self_true - replaceFunction(0x100556c0, [](AnimSlotGoalStackEntry *pGoalData, objHndl handle, AnimGoalType goalType) { - Expects(pGoalData); - return pGoalData->InitWithInterrupt(handle, goalType) ? 1 : 0; - }); - - // anim_make_goal_self_false - replaceFunction(0x100556e0, [](AnimSlotGoalStackEntry *pGoalData, objHndl handle, AnimGoalType goalType) { - return pGoalData->Init(handle, goalType) ? 1 : 0; - }); - - // anim_get_goal_with_same_objs - replaceFunction(0x10055ab0, [](objHndl handle, AnimSlotGoalStackEntry *goalData, AnimSlotId *idOut) { - auto id = gameSystems->GetAnim().GetSlotForGoalAndObjs(handle, *goalData); - return HandleOptionalSlotResult(id, idOut); - }); - - // anim_clear_for_obj - replaceFunction(0x1000c760, [](objHndl handle) { - return gameSystems->GetAnim().ClearForObject(handle) ? 1 : 0; - }); - - // anim_set_all_goals_cleared_callback - replaceFunction(0x1000c750, [](void(*callback)(void)) { - gameSystems->GetAnim().SetAllGoalsClearedCallback(callback); - }); - - // animpriv_get_slot - replaceFunction(0x10016c40, [](AnimSlotId *id, AnimSlot **slotOut) { - *slotOut = gameSystems->GetAnim().GetSlot(*id); - return (*slotOut != nullptr) ? 1 : 0; - }); - - // AnimSlotInterruptGoals - replaceFunction(0x10056090, [](AnimSlotId &animId, AnimGoalPriority priority) { - auto &slot = gameSystems->GetAnim().GetRunSlot(animId.slotIndex); - return gameSystems->GetAnim().InterruptGoals(slot, priority) ? 1 : 0; - }); - - // anim_interrupt_by_type - replaceFunction(0x10056350, [](objHndl handle, AnimGoalType interruptGoals, AnimGoalType keep) { - gameSystems->GetAnim().InterruptGoalsByType(handle, interruptGoals, keep); - return 1; - }); - - // Push Goal Impl - replaceFunction(0x10056600, [](AnimSlotGoalStackEntry* gdata, AnimSlotId*slotId, int allocSlot, int flags) { - Expects(gdata); - if (allocSlot != 1) { - throw TempleException("Push Goal should never be called with allocSlot=false"); - } - return gameSystems->GetAnim().PushGoalInternal(*gdata, slotId, flags) ? 1 : 0; - }); - - // animNumActiveGoals_inc - replaceFunction(0x10055bf0, [](AnimSlot *slot, AnimGoal *goal) { - gameSystems->GetAnim().IncreaseActiveGoalCount(*slot, *goal); - }); - - // animNumActiveGoals_dec - replaceFunction(0x10055ca0, [](AnimSlot *slot, AnimGoal *goal) { - gameSystems->GetAnim().DecreaseActiveGoalCount(*slot, *goal); - }); - - // anim_subgoal_add_func - replaceFunction(0x10056a50, [](AnimSlotId id, AnimSlotGoalStackEntry *goalStackEntry) { - return gameSystems->GetAnim().AddSubGoal(id, *goalStackEntry) ? 1 : 0; - }); - - replaceFunction(0x1001A2E0, [](objHndl handle, LocAndOffsets loc, PathQueryResult* pqr) { - return gameSystems->GetAnim().PushRunToTile(handle, loc, pqr) ? TRUE : FALSE; - }); - - replaceFunction(0x1001A930, []() { - gameSystems->GetAnim().TurnOnRunning(); - }); - - // Push Goal - replaceFunction(0x10056D20, [](AnimSlotGoalStackEntry* gdata, AnimSlotId*slotId) { - Expects(gdata); - return gameSystems->GetAnim().PushGoal(*gdata, slotId) ? TRUE : FALSE; - }); - - replaceFunction(0x10016A30, []() { - gameSystems->GetAnim().ProcessActionCallbacks(); - }); - - // animpriv_alloc_run_slot - replaceFunction(0x10055470, [](AnimSlotId *pAnimID) { - auto slotId = gameSystems->GetAnim().AllocSlot(); - if (slotId) { - *pAnimID = *slotId; - return 1; - } else { - return 0; - } - }); - - // anim_is_processing - replaceFunction(0x10054e10, []() { - return gameSystems->GetAnim().IsProcessing() ? 1 : 0; - }); - - // anim_push_fidget - replaceFunction(0x10015d70, [](objHndl handle) { - return gameSystems->GetAnim().PushFidget(handle); - }); - - // anim_get_last_anim_run_id_pushed_to - replaceFunction(0x10054ee0, [](AnimSlotId *slotIdOut) { - if (slotIdOut) { - *slotIdOut = gameSystems->GetAnim().GetLastSlotPushedTo(); - } - return 1; - }); - - } - -} hooks; + +#include "stdafx.h" + +#include + +#include "util/fixes.h" +#include + +#include "anim.h" +#include "animgoals_stackentry.h" +#include "obj.h" +#include "gamesystems/gamesystems.h" + +static int HandleOptionalSlotResult(std::optional slotId, AnimSlotId *slotIdOut) { + if (slotId) { + if (slotIdOut) { + *slotIdOut = *slotId; + } + return 1; + } + else { + if (slotIdOut) { + slotIdOut->Clear(); + } + return 0; + } +} + +static const char *AnimGoalDataNames[] = { + "AGDATA_SELF_OBJ", "AGDATA_TARGET_OBJ", "AGDATA_BLOCK_OBJ", + "AGDATA_SCRATCH_OBJ", "AGDATA_PARENT_OBJ", "AGDATA_TARGET_TILE", + "AGDATA_RANGE_DATA", "AGDATA_ANIM_ID", "AGDATA_ANIM_ID_PREV", + "AGDATA_ANIM_DATA", "AGDATA_SPELL_DATA", "AGDATA_SKILL_DATA", + "AGDATA_FLAGS_DATA", "AGDATA_SCRATCH_VAL1", "AGDATA_SCRATCH_VAL2", + "AGDATA_SCRATCH_VAL3", "AGDATA_SCRATCH_VAL4", "AGDATA_SCRATCH_VAL5", + "AGDATA_SCRATCH_VAL6", "AGDATA_SOUND_HANDLE" }; + +static class AnimGoalsHooks : public TempleFix { +public: + + std::map goalfunc_names; + + AnimGoalsHooks() { + + goalfunc_names[0x1000c9c0] = "GoalStateCallback1"sv; + goalfunc_names[0x1000ccf0] = "GoalStateCallback3"sv; // Always 1 + goalfunc_names[0x1000ce10] = "GoalSetOffAndDestroyParam1"sv; + goalfunc_names[0x1000ce60] = "GoalParam1ObjCloseToParam2Loc"sv; + goalfunc_names[0x1000cf10] = "GoalTargetLocWithinRadius"sv; + goalfunc_names[0x1000cfe0] = "GoalStateCallback7"sv; + goalfunc_names[0x1000d060] = "GoalStateCallback8"sv; + goalfunc_names[0x1000d150] = "GoalIsCurrentPathValid"sv; + goalfunc_names[0x1000d560] = "GoalCalcPathToTarget"sv; + goalfunc_names[0x1000db30] = "GoalCalcPathToTarget2"sv; + goalfunc_names[0x1000dca0] = "GoalKnockbackFunc"sv; + goalfunc_names[0x1000dd80] = "GoalMoveAwayFromObj"sv; + goalfunc_names[0x1000e250] = "GoalIsConcealed"sv; + goalfunc_names[0x1000e270] = "GoalIsProne"sv; + goalfunc_names[0x1000e2c0] = "GoalStunnedExpire"sv; + goalfunc_names[0x1000e4f0] = "GoalHasDoorInPath"sv; + goalfunc_names[0x1000e6f0] = "GoalFindPathNear"sv; + goalfunc_names[0x1000e8b0] = "GoalFindPathNearObject"sv; + goalfunc_names[0x1000ec10] = "GoalFindPathNearObjectCombat"sv; + goalfunc_names[0x1000efb0] = "GoalIsParam1Door"sv; + goalfunc_names[0x1000f000] = "GoalPlayDoorLockedSound"sv; + goalfunc_names[0x1000f0d0] = "GoalIsDoorMagicallyHeld"sv; + goalfunc_names[0x1000f140] = "GoalAttemptOpenDoor"sv; + goalfunc_names[0x1000f2c0] = "GoalIsDoorLocked"sv; + goalfunc_names[0x1000f350] = "GoalDoorAlwaysFalse"sv; + goalfunc_names[0x1000f400] = "GoalIsDoorUnlocked"sv; + goalfunc_names[0x1000f490] = "GoalSetRadiusTo2"sv; + goalfunc_names[0x1000f550] = "GoalUseObject"sv; + goalfunc_names[0x1000f860] = "GoalUseItemOnObj"sv; + goalfunc_names[0x1000f9a0] = "GoalUseItemOnObjWithSkillDummy"sv; + goalfunc_names[0x1000fbc0] = "GoalUseItemOnLoc"sv; + goalfunc_names[0x1000fce0] = "GoalUseItemOnLocWithSkillDummy"sv; + goalfunc_names[0x1000fec0] = "GoalSetNoFlee"sv; + goalfunc_names[0x1000ff10] = "GoalIsAlive"sv; + goalfunc_names[0x1000ff60] = "GoalPlaySoundScratch5"sv; + goalfunc_names[0x1000fff0] = "GoalAttemptAttackCheck"sv; + goalfunc_names[0x10010160] = "GoalCritterShouldNotAutoAnimate"sv; + goalfunc_names[0x100101d0] = "GoalAttackerHasRangedWeapon"sv; + goalfunc_names[0x10010250] = "GoalReturnTrue"sv; + goalfunc_names[0x10010290] = "GoalReturnFalse"sv; + goalfunc_names[0x100102c0] = "GoalAttemptSpell"sv; + goalfunc_names[0x10010410] = "GoalCastConjureEnd"sv; + goalfunc_names[0x100104a0] = "GoalDestroyParam1"sv; + goalfunc_names[0x10010500] = "GoalWasInterrupted"sv; + goalfunc_names[0x10010520] = "GoalIsAnimatingConjuration"sv; + goalfunc_names[0x100105f0] = "GoalStartConjurationAnim"sv; + goalfunc_names[0x10010760] = "GoalAreOnSameTile"sv; + goalfunc_names[0x100107e0] = "GoalPickpocketPerform"sv; + goalfunc_names[0x10010aa0] = "GoalActionPerform"sv; + goalfunc_names[0x10010b50] = "GoalCheckSlotFlag40000"sv; + goalfunc_names[0x10010b70] = "GoalCheckParam2AgainstStateFlagData"sv; + goalfunc_names[0x10010b90] = "GoalPickLock"sv; + goalfunc_names[0x10010cd0] = "GoalAttemptTrapDisarm"sv; + goalfunc_names[0x10010d60] = "GoalActionPerform2"sv; + goalfunc_names[0x10010e00] = "GoalHasReachWithMainWeapon"sv; + goalfunc_names[0x10010f70] = "GoalThrowItem"sv; + goalfunc_names[0x100110a0] = "GoalNotPreventedFromTalking"sv; + goalfunc_names[0x100111e0] = "GoalIsWithinTalkingDistance"sv; + goalfunc_names[0x100112d0] = "GoalInitiateDialog"sv; + goalfunc_names[0x10011370] = "GoalOpenDoorCleanup"sv; + goalfunc_names[0x10011420] = "GoalCloseDoorCleanup"sv; + goalfunc_names[0x100114d0] = "GoalIsDoorSticky"sv; + goalfunc_names[0x10011530] = "GoalIsLiveCritterNear"sv; + goalfunc_names[0x10011600] = "GoalSetRunningFlag"sv; + goalfunc_names[0x10011660] = "GoalEnterCombat"sv; + goalfunc_names[0x100117f0] = "GoalLeaveCombat"sv; + goalfunc_names[0x10011880] = "GoalPlayGetHitAnim"sv; + goalfunc_names[0x10011a30] = "GoalPlayDodgeAnim"sv; + goalfunc_names[0x10011be0] = "GoalPlayAnim"sv; + goalfunc_names[0x10011cf0] = "GoalSaveParam1InScratch"sv; + goalfunc_names[0x10011d20] = "GoalSaveStateDataInSkillData"sv; + goalfunc_names[0x10011d40] = "GoalSaveStateDataOrSpellRangeInRadius"sv; + goalfunc_names[0x10011dc0] = "GoalSetTargetLocFromObj"sv; + goalfunc_names[0x10011e70] = "GoalSetRadiusTo4"sv; + goalfunc_names[0x10011e90] = "GoalSetRadiusToAiSpread"sv; + goalfunc_names[0x10011f20] = "GoalIsCloserThanDesiredSpread"sv; + goalfunc_names[0x10012040] = "GoalTurnTowardsOrAway"sv; + goalfunc_names[0x100121b0] = "GoalPlayRotationAnim"sv; + goalfunc_names[0x100122a0] = "GoalRotate"sv; + goalfunc_names[0x100125f0] = "GoalIsRotatedTowardNextPathNode"sv; + goalfunc_names[0x100127b0] = "GoalIsRotatedTowardTarget"sv; + goalfunc_names[0x10012910] = "GoalSetRotationToParam2"sv; + goalfunc_names[0x10012a00] = "GoalSetRotationToFaceTargetObj"sv; + goalfunc_names[0x10012b60] = "GoalSetRotationToFaceTargetLoc"sv; + goalfunc_names[0x10012c70] = "GoalIsSlotFlag10NotSet"sv; + goalfunc_names[0x10012c80] = "GoalSlotFlagSet8If4AndNotSetYet"sv; + goalfunc_names[0x10012ca0] = "GoalProjectileCleanup"sv; + goalfunc_names[0x10012cf0] = "GoalAnimateCleanup"sv; + goalfunc_names[0x10012d10] = "GoalAnimateForever"sv; + goalfunc_names[0x10012fa0] = "GoalLoopWhileCloseToParty"sv; + goalfunc_names[0x10013080] = "GoalFreeSoundHandle"sv; + goalfunc_names[0x100130f0] = "GoalIsAliveAndConscious"sv; + goalfunc_names[0x10013250] = "GoalBeginMoveStraight"sv; + goalfunc_names[0x10013af0] = "GoalUpdateMoveStraight"sv; + goalfunc_names[0x100140c0] = "GoalSetNoBlockIfNotInParty"sv; + goalfunc_names[0x10014170] = "GoalDyingCleanup"sv; + goalfunc_names[0x100147d0] = "GoalMoveAlongPath"sv; + goalfunc_names[0x10014f10] = "GoalIsNotStackFlagsData20"sv; + goalfunc_names[0x10014f30] = "GoalJiggleAlongYAxis"sv; + goalfunc_names[0x10014ff0] = "GoalStartJigglingAlongYAxis"sv; + goalfunc_names[0x100150a0] = "GoalEndJigglingAlongYAxis"sv; + goalfunc_names[0x10015150] = "GoalIsNotStackFlagsData40"sv; + goalfunc_names[0x10015170] = "GoalSetSlotFlags4"sv; + goalfunc_names[0x10015240] = "GoalActionPerform3"sv; + goalfunc_names[0x10017100] = "GoalSpawnFireball"sv; + goalfunc_names[0x10017170] = "GoalPleaseMove"sv; + goalfunc_names[0x10017460] = "GoalIsTargetWithinRadius"sv; + goalfunc_names[0x10017570] = "GoalWander"sv; + goalfunc_names[0x10017810] = "GoalWanderSeekDarkness"sv; + goalfunc_names[0x10017b30] = "GoalIsDoorFullyClosed"sv; + goalfunc_names[0x10017dd0] = "GoalTriggerSpell"sv; + goalfunc_names[0x10017f80] = "GoalUnconcealCleanup"sv; + goalfunc_names[0x10018050] = "GoalResetToIdleAnim"sv; + goalfunc_names[0x10018160] = "GoalResetToIdleAnimUnstun"sv; + goalfunc_names[0x10018290] = "GoalThrowItemCleanup"sv; + goalfunc_names[0x10018400] = "GoalThrowItemPlayAnim"sv; + goalfunc_names[0x100185e0] = "GoalUnconcealAnimate"sv; + goalfunc_names[0x10018730] = "GoalStartIdleAnimIfCloseToParty"sv; + goalfunc_names[0x10018810] = "GoalStartFidgetAnimIfCloseToParty"sv; + goalfunc_names[0x100188f0] = "GoalContinueWithAnim"sv; + goalfunc_names[0x100189b0] = "GoalContinueWithAnim2"sv; + goalfunc_names[0x10018a70] = "GoalPlayDoorOpenAnim"sv; + goalfunc_names[0x10018b90] = "GoalContinueWithDoorOpenAnim"sv; + goalfunc_names[0x10018c50] = "GoalPlayDoorCloseAnim"sv; + goalfunc_names[0x10018d40] = "GoalContinueWithDoorCloseAnim"sv; + goalfunc_names[0x10018e00] = "GoalPickLockPlayPushDoorOpenAnim"sv; + goalfunc_names[0x10018ee0] = "GoalPickLockContinueWithAnim"sv; + goalfunc_names[0x10018fa0] = "GoalDyingPlaySoundAndRipples"sv; + goalfunc_names[0x10019070] = "GoalDyingContinueAnim"sv; + goalfunc_names[0x10019130] = "GoalAnimateFireDmgContinueAnim"sv; + goalfunc_names[0x100191f0] = "GoalStunnedPlayAnim"sv; + goalfunc_names[0x10019330] = "GoalStunnedContinueAnim"sv; + goalfunc_names[0x10019470] = "GoalPlayGetUpAnim"sv; + goalfunc_names[0x10019540] = "GoalPlayUnconcealAnim"sv; + goalfunc_names[0x10019630] = "GoalPlayMoveAnim"sv; + goalfunc_names[0x10019920] = "GoalPlayWaterRipples"sv; + goalfunc_names[0x100199b0] = "GoalContinueMoveStraight"sv; + goalfunc_names[0x10019c20] = "GoalApplyKnockback"sv; + goalfunc_names[0x10019e10] = "GoalDyingReturnTrue"sv; + goalfunc_names[0x10019e70] = "GoalAttemptMoveCleanup"sv; + goalfunc_names[0x10019f00] = "GoalAttackPlayWeaponHitEffect"sv; + goalfunc_names[0x1001a080] = "GoalAttackContinueWithAnim"sv; + goalfunc_names[0x1001a170] = "GoalAttackPlayIdleAnim"sv; + goalfunc_names[0x1001bf70] = "GoalMoveNearUpdateRadiusToReach"sv; + goalfunc_names[0x1001c100] = "GoalAttackEndTurnIfUnreachable"sv; + goalfunc_names[0x101f5850] = "AlwaysSucceed"sv; + goalfunc_names[0x10262530] = "AlwaysFail"sv; + } + + static std::string_view GetArgInfoText(int animParamType) { + if (animParamType < 21) { + return AnimGoalDataNames[animParamType]; + } + else if (animParamType == 31) { + return "SELF_OBJ_PRECISE_LOC"; + } + else if (animParamType == 32) { + return "TARGET_OBJ_PRECISE_LOC"; + } + else if (animParamType == 33) { + return "NULL_HANDLE"; + } + else if (animParamType == 34) { + return "TARGET_LOC_PRECISE"; + } + else { + return to_string(animParamType); + } + } + + static std::string GetTransitionDelay(AnimStateTransition transition) { + if (transition.delay == 0) { + return ""; + } + auto delayText = std::to_string(transition.delay); + if (transition.delay == transition.DelaySlot) { + delayText = "DELAY_SLOT"; + } + else if (transition.delay == transition.DelayRandom) { + delayText = "DELAY_RANDOM"; + } + else if (transition.delay == transition.DelayCustom) { + delayText = "DELAY_CUSTOM"; + } + return ", " + delayText; + } + + static std::string GetTransitionArgs(AnimStateTransition transition) { + /*int nextState = transition.newState & ~ASTF_MASK; + + // Simple transition into another state + if (!(transition.newState & ASTF_MASK)) { + return fmt::format("Transition::State({}{})", transition.newState - 1, GetTransitionDelay(transition)); + } + + if ((transition.newState & ASTF_POP_ALL) == ASTF_POP_ALL) { + return fmt::format("Transition::EndAll(){}", GetTransitionDelay(transition)); + } + + if ((transition.newState & ASTF_PUSH_GOAL) == ASTF_PUSH_GOAL) { + auto remainingMask = (transition.newState & ASTF_MASK) & ~ASTF_PUSH_GOAL; + if (remainingMask != 0 && remainingMask != ASTF_REWIND && remainingMask != ASTF_POP_GOAL) { + __debugbreak(); + } + return fmt::format("Transition::PushGoal({}){}", GetAnimGoalTypeName((AnimGoalType) nextState), GetTransitionDelay(transition)); + }*/ + + auto newState = transition.newState; + + std::vector flags; + if ((newState & ASTF_GOAL_INVALIDATE_PATH) == ASTF_GOAL_INVALIDATE_PATH) { + flags.push_back("T_INVALIDATE_PATH"); + newState &= ~ASTF_GOAL_INVALIDATE_PATH; + } + + if ((newState & ASTF_POP_GOAL) == ASTF_POP_GOAL) { + flags.push_back("T_POP_GOAL"); + newState &= ~ASTF_POP_GOAL; + } + + if ((newState & ASTF_POP_GOAL_TWICE) == ASTF_POP_GOAL_TWICE) { + flags.push_back("T_POP_GOAL_TWICE"); + newState &= ~ASTF_POP_GOAL_TWICE; + } + + if ((newState & ASTF_PUSH_GOAL) == ASTF_PUSH_GOAL) { + auto pushGoal = GetAnimGoalTypeName((AnimGoalType)(transition.newState & ~ASTF_MASK)); + flags.push_back(fmt::format("T_PUSH_GOAL({})", pushGoal)); + newState &= ~ASTF_PUSH_GOAL; + newState &= ASTF_MASK; + } + + if ((newState & ASTF_POP_ALL) == ASTF_POP_ALL) { + flags.push_back("T_POP_ALL"); + newState &= ~ASTF_POP_ALL; + } + + if ((newState & ASTF_REWIND) == ASTF_REWIND) { + flags.push_back("T_REWIND"); + newState &= ~ASTF_REWIND; + } + + if ((newState & ASTF_MASK) != 0) { + flags.push_back(fmt::format("0x{:x}", newState & ASTF_MASK)); + newState &= ~ASTF_MASK; + } + + if (!(transition.newState & ASTF_MASK)) { + flags.push_back(fmt::format("T_GOTO_STATE({})", transition.newState - 1)); + } + + std::string flagsString; + if (flags.empty()) { + flagsString = "0"; + } else { + for (size_t i = 0; i < flags.size(); i++) { + if (i > 0) { + flagsString += "|"; + } + flagsString += flags[i]; + } + } + + auto delayText = std::to_string(transition.delay); + if (transition.delay == transition.DelaySlot) { + delayText = "DELAY_SLOT"; + } else if (transition.delay == transition.DelayRandom) { + delayText = "DELAY_RANDOM"; + } else if (transition.delay == transition.DelayCustom) { + delayText = "DELAY_CUSTOM"; + } + + if (transition.delay != 0) { + return fmt::format("{}, {}", flagsString, delayText); + } else { + return fmt::format("{}", flagsString); + } + } + + std::string GetCallbackFunctionRef(uint32_t address) { + if (!goalfunc_names[address].empty()) { + return std::string(goalfunc_names[address]); + } + + return fmt::format("0x{:x}", address); + } + + void DumpGoalSetups(); + + virtual void apply() override { + + // DumpGoalSetups(); + + // All functions that use 102BD1B0 should now be replaced (the goals array) + // breakRegion(0x102BD1B0, 0x102BD2F4); + + // anim_first_run_idx_for_obj + replaceFunction(0x10054e20, [](objHndl handle) { + return gameSystems->GetAnim().GetFirstRunSlotIdxForObj(handle); + }); + + // anim_next_run_idx_for_obj + replaceFunction(0x10054e70, [](int slotIndex, objHndl handle) { + return gameSystems->GetAnim().GetNextRunSlotIdxForObj(handle, slotIndex); + }); + + // anim_run_id_for_obj + replaceFunction(0x1000c430, [](objHndl handle, AnimSlotId *slotIdOut) { + + auto slotId = gameSystems->GetAnim().GetFirstRunSlotId(handle); + return HandleOptionalSlotResult(slotId, slotIdOut); + + }); + + // anim_get_highest_priority + replaceFunction(0x1000c500, [](objHndl handle) { + return gameSystems->GetAnim().GetCurrentPriority(handle); + }); + + // anim_pop_goal + replaceFunction(0x10016FC0, + [](AnimSlot &slot, const uint32_t *popFlags, const AnimGoal **newGoal, AnimSlotGoalStackEntry **newCurrentGoal, BOOL *stopProcessing) { + bool stopProcessingBool = *stopProcessing == TRUE; + gameSystems->GetAnim().PopGoal(slot, *popFlags, newGoal, newCurrentGoal, &stopProcessingBool); + *stopProcessing = stopProcessingBool ? TRUE : FALSE; + }); + + // anim_break_nodes_to_map + replaceFunction(0x1001af30, [](const char *mapName) { + gameSystems->GetAnim().SaveToMap(mapName); + return 0; + }); + + + replaceFunction(0x1001B830, [](const TimeEvent *evt) -> int { + auto result = 0; + + result = gameSystems->GetAnim().ProcessAnimEvent(evt); + temple::GetRef(0x102B2654) = gameSystems->GetAnim().mCurrentlyProcessingSlotIdx; + + return result; + }); + + // anim_is_running_goal + replaceFunction(0x10054f30, [](objHndl handle, AnimGoalType goalType, AnimSlotId *idOut) { + if (goalType == -1) { + return 0; // Weird... + } + + return gameSystems->GetAnim().DoesObjHaveGoal(handle, goalType, idOut) ? 1 : 0; + }); + + // anim_get_slot_with_fieldc_goal + replaceFunction(0x10054fd0, [](objHndl handle, AnimSlotId *idOut) { + + auto id = gameSystems->GetAnim().GetRunSlotWithGoalWithoutFieldC(handle); + return HandleOptionalSlotResult(id, idOut); + + }); + + // anim_has_attack_anim + replaceFunction(0x10055060, [](objHndl handle, AnimSlotId *idOut, objHndl target) { + auto id = gameSystems->GetAnim().HasAttackAnim(handle, target); + return HandleOptionalSlotResult(id, idOut); + }); + + // anim_cur_goal_has_field10_1 + replaceFunction(0x100551a0, [](AnimSlot *slot) { + return gameSystems->GetAnim().CurrentGoalHasField10_1(*slot) ? 1 : 0; + }); + + // anim_make_goal_self_true + replaceFunction(0x100556c0, [](AnimSlotGoalStackEntry *pGoalData, objHndl handle, AnimGoalType goalType) { + Expects(pGoalData); + return pGoalData->InitWithInterrupt(handle, goalType) ? 1 : 0; + }); + + // anim_make_goal_self_false + replaceFunction(0x100556e0, [](AnimSlotGoalStackEntry *pGoalData, objHndl handle, AnimGoalType goalType) { + return pGoalData->Init(handle, goalType) ? 1 : 0; + }); + + // anim_get_goal_with_same_objs + replaceFunction(0x10055ab0, [](objHndl handle, AnimSlotGoalStackEntry *goalData, AnimSlotId *idOut) { + auto id = gameSystems->GetAnim().GetSlotForGoalAndObjs(handle, *goalData); + return HandleOptionalSlotResult(id, idOut); + }); + + // anim_clear_for_obj + replaceFunction(0x1000c760, [](objHndl handle) { + return gameSystems->GetAnim().ClearForObject(handle) ? 1 : 0; + }); + + // anim_set_all_goals_cleared_callback + replaceFunction(0x1000c750, [](void(*callback)(void)) { + gameSystems->GetAnim().SetAllGoalsClearedCallback(callback); + }); + + // animpriv_get_slot + replaceFunction(0x10016c40, [](AnimSlotId *id, AnimSlot **slotOut) { + *slotOut = gameSystems->GetAnim().GetSlot(*id); + return (*slotOut != nullptr) ? 1 : 0; + }); + + // AnimSlotInterruptGoals + replaceFunction(0x10056090, [](AnimSlotId &animId, AnimGoalPriority priority) { + auto &slot = gameSystems->GetAnim().GetRunSlot(animId.slotIndex); + return gameSystems->GetAnim().InterruptGoals(slot, priority) ? 1 : 0; + }); + + // anim_interrupt_by_type + replaceFunction(0x10056350, [](objHndl handle, AnimGoalType interruptGoals, AnimGoalType keep) { + gameSystems->GetAnim().InterruptGoalsByType(handle, interruptGoals, keep); + return 1; + }); + + // Push Goal Impl + replaceFunction(0x10056600, [](AnimSlotGoalStackEntry* gdata, AnimSlotId*slotId, int allocSlot, int flags) { + Expects(gdata); + if (allocSlot != 1) { + throw TempleException("Push Goal should never be called with allocSlot=false"); + } + return gameSystems->GetAnim().PushGoalInternal(*gdata, slotId, flags) ? 1 : 0; + }); + + // animNumActiveGoals_inc + replaceFunction(0x10055bf0, [](AnimSlot *slot, AnimGoal *goal) { + gameSystems->GetAnim().IncreaseActiveGoalCount(*slot, *goal); + }); + + // animNumActiveGoals_dec + replaceFunction(0x10055ca0, [](AnimSlot *slot, AnimGoal *goal) { + gameSystems->GetAnim().DecreaseActiveGoalCount(*slot, *goal); + }); + + // anim_subgoal_add_func + replaceFunction(0x10056a50, [](AnimSlotId id, AnimSlotGoalStackEntry *goalStackEntry) { + return gameSystems->GetAnim().AddSubGoal(id, *goalStackEntry) ? 1 : 0; + }); + + replaceFunction(0x1001A2E0, [](objHndl handle, LocAndOffsets loc, PathQueryResult* pqr) { + return gameSystems->GetAnim().PushRunToTile(handle, loc, pqr) ? TRUE : FALSE; + }); + + replaceFunction(0x1001A930, []() { + gameSystems->GetAnim().TurnOnRunning(); + }); + + // Push Goal + replaceFunction(0x10056D20, [](AnimSlotGoalStackEntry* gdata, AnimSlotId*slotId) { + Expects(gdata); + return gameSystems->GetAnim().PushGoal(*gdata, slotId) ? TRUE : FALSE; + }); + + replaceFunction(0x10016A30, []() { + gameSystems->GetAnim().ProcessActionCallbacks(); + }); + + // animpriv_alloc_run_slot + replaceFunction(0x10055470, [](AnimSlotId *pAnimID) { + auto slotId = gameSystems->GetAnim().AllocSlot(); + if (slotId) { + *pAnimID = *slotId; + return 1; + } else { + return 0; + } + }); + + // anim_is_processing + replaceFunction(0x10054e10, []() { + return gameSystems->GetAnim().IsProcessing() ? 1 : 0; + }); + + // anim_push_fidget + replaceFunction(0x10015d70, [](objHndl handle) { + return gameSystems->GetAnim().PushFidget(handle); + }); + + // anim_get_last_anim_run_id_pushed_to + replaceFunction(0x10054ee0, [](AnimSlotId *slotIdOut) { + if (slotIdOut) { + *slotIdOut = gameSystems->GetAnim().GetLastSlotPushedTo(); + } + return 1; + }); + + } + +} hooks; + +void AnimGoalsHooks::DumpGoalSetups() +{ + using AnimGoalArray = const AnimGoal* [82]; + AnimGoalArray& goals = temple::GetRef(0x102BD1B0); + + auto fh = fopen("anim_goal_setup.cpp", "wt"); + + for (auto entry : goalfunc_names) { + fmt::print(fh, "int {}(AnimSlot &slot); // Originally @ 0x{:x}\n", entry.second, entry.first); + } + + for (auto entry : goalfunc_names) { + fmt::print(fh, "\n// Originally @ 0x{:x}\n", entry.first); + fmt::print(fh, "int {}(AnimSlot &slot) {{\n", entry.second); + fmt::print(fh, "\tstatic org = temple::GetRef(0x{:x});\n", entry.first); + fmt::print(fh, "\treturn org(slot);\n"); + fmt::print(fh, "}}\n"); + } + + for (int i = 0; i < ag_count; i++) { + auto goal = goals[i]; + auto goalName = GetAnimGoalTypeName((AnimGoalType)i); + if (!goal) { + fmt::print(fh, "\n"); + fmt::print(fh, "// {} was undefined\n", goalName); + fmt::print(fh, "\n"); + continue; + } + + auto builderVar = std::string(GetAnimGoalTypeName((AnimGoalType)i)); + if (builderVar.find("ag_") == 0) { + builderVar.replace(0, 3, ""); + } + + fmt::print(fh, "// {}\n", goalName); + fmt::print(fh, "auto {} = AnimGoalBuilder(goals_[{}])", builderVar, goalName); + fmt::print(fh, "\n\t.SetPriority({})", GetAnimGoalPriorityText(goal->priority)); + if (goal->interruptAll) { + fmt::print(fh, "\n\t.SetInterruptAll(true)"); + } + if (goal->field_C) { + fmt::print(fh, "\n\t.SetFieldC(true)"); + } + if (goal->field_10) { + fmt::print(fh, "\n\t.SetField10(true)"); + } + if (goal->relatedGoal[0] != -1) { + fmt::print(fh, "\n\t.SetRelatedGoals("); + for (int j = 0; j < 3; j++) { + if (goal->relatedGoal[j] == -1) { + break; + } + if (j > 0) { + fmt::print(fh, ", "); + } + fmt::print(fh, "{}", GetAnimGoalTypeName((AnimGoalType)goal->relatedGoal[j])); + } + fmt::print(fh, ")"); + } + fmt::print(fh, ";\n"); + + for (int j = -1; j < goal->statecount; j++) { + AnimGoalState state; + if (j == -1) { + state = goal->state_special; + if (!state.callback) { + continue; + } + fmt::print(fh, "{}.AddCleanup({})", builderVar, GetCallbackFunctionRef((uint32_t)state.callback)); + } + else { + state = goal->states[j]; + fmt::print(fh, "{}.AddState({}) // Index {}", builderVar, GetCallbackFunctionRef((uint32_t)state.callback), j); + } + + Expects(state.argInfo2 == -1 || state.argInfo1 != -1); + + if (state.argInfo1 != -1) { + fmt::print(fh, "\n\t.SetArgs({}", GetArgInfoText(state.argInfo1)); + + if (state.argInfo2 != -1) { + fmt::print(fh, ", {}", GetArgInfoText(state.argInfo2)); + } + fmt::print(fh, ")"); + } + + if (state.flagsData != -1) { + fmt::print(fh, "\n\t.SetFlagsData({})", state.flagsData); + } + + // Print transitions + if (j != -1) { + fmt::print(fh, "\n\t.OnSuccess({})", GetTransitionArgs(state.afterSuccess)); + } + if (j != -1) { + fmt::print(fh, "\n\t.OnFailure({})", GetTransitionArgs(state.afterFailure)); + } + + fmt::print(fh, ";\n"); + } + + fmt::print(fh, "\n"); + + } + + fclose(fh); +} From a8febee51cbcb37a25f66214b4121eaa2e08361f Mon Sep 17 00:00:00 2001 From: DMD Date: Thu, 5 Nov 2020 22:11:55 +0200 Subject: [PATCH 2/2] Now saving hotkeys.sco in modules folder (and removed use of fopen) --- TemplePlus/gamesystems/legacysystems.cpp | 9 + TemplePlus/gamesystems/legacysystems.h | 3 +- TemplePlus/hotkeys.cpp | 668 +++++++++++------------ TemplePlus/hotkeys.h | 65 +-- 4 files changed, 369 insertions(+), 376 deletions(-) diff --git a/TemplePlus/gamesystems/legacysystems.cpp b/TemplePlus/gamesystems/legacysystems.cpp index 891ba6b3f..92247d7ad 100644 --- a/TemplePlus/gamesystems/legacysystems.cpp +++ b/TemplePlus/gamesystems/legacysystems.cpp @@ -30,6 +30,7 @@ #include "turn_based.h" #include "d20_race.h" #include "ai.h" +#include //***************************************************************************** @@ -868,6 +869,14 @@ D20System::~D20System() { shutdown(); damage.Exit(); } +void D20System::LoadModule() +{ + auto file = tio_fopen("hotkeys.sco", "rb"); + if (file) { + hotkeys.LoadHotkeys(file); + tio_fclose(file); + } +} void D20System::Reset() { auto reset = temple::GetPointer(0x1004c9b0); reset(); diff --git a/TemplePlus/gamesystems/legacysystems.h b/TemplePlus/gamesystems/legacysystems.h index 5a9ee00aa..93034e63a 100644 --- a/TemplePlus/gamesystems/legacysystems.h +++ b/TemplePlus/gamesystems/legacysystems.h @@ -196,11 +196,12 @@ class LevelSystem : public GameSystem { const std::string &GetName() const override; }; -class D20System : public GameSystem, public ResetAwareGameSystem, public TimeAwareGameSystem { +class D20System : public GameSystem, public ResetAwareGameSystem, public TimeAwareGameSystem, public ModuleAwareGameSystem { public: static constexpr auto Name = "D20"; D20System(const GameSystemConf &config); ~D20System(); + void LoadModule() override; void Reset() override; void AdvanceTime(uint32_t time) override; const std::string &GetName() const override; diff --git a/TemplePlus/hotkeys.cpp b/TemplePlus/hotkeys.cpp index 5d7bd5935..ba45cde29 100644 --- a/TemplePlus/hotkeys.cpp +++ b/TemplePlus/hotkeys.cpp @@ -1,344 +1,326 @@ -#include "stdafx.h" -#include "radialmenu.h" -#include "tig/tig_mes.h" -#include "hotkeys.h" -#include "tio\tio.h" -#include "gamesystems/legacy.h" -#include "util/fixes.h" -#include "obj.h" -#include "critter.h" -#include "action_sequence.h" -#include "ui/ui_systems.h" -#include "ui/ui_legacysystems.h" -#include "infrastructure/keyboard.h" - -HotkeySystem hotkeys; - -struct HotkeyAddresses : temple::AddressTable -{ - int * assignableKeys; // size 39 int array, holding DirectX Input Key numbers - RadialMenuEntry * hotkeyTable; // 39 entries; index corresponds to key given in assignableKeys - MesHandle * hotkeyMes; - char * hotkeyPopupBodyText; // confirmation dialog text; 512 chars - char * hotkeyTexts; // 128 chars each, 39 entries, describing the hotkey assignment - - RadialMenuEntry** radMenuEntryToBind; - int * keyIdxToBind; - - int(__cdecl* HotkeyTableSearch)(RadialMenuEntry* radMenuEntry); - const char * (__cdecl* GetDikLetter)(int dik); - const char * (__cdecl* GetHotkeyLetter)(RadialMenuEntry radMenuEntry); // fetches the letter assigned to this radial menu entry from hotkey.mes - int(__cdecl* IsReversedHotkey)(int dik); - void(__cdecl* HotkeyAssignCallback)(char * cancelFlag); // assigns a hotkey (in the hotkeyTable) according to some global vars - - - HotkeyAddresses() - { - rebase(assignableKeys, 0x102E8B78); - rebase(hotkeyTable, 0x10BD0248); - rebase(hotkeyMes, 0x10BD0D40); - rebase(hotkeyPopupBodyText, 0x10BD0D48); - rebase(hotkeyTexts, 0x10BD0F48); - - rebase(HotkeyTableSearch, 0x100F3DF0); - rebase(GetDikLetter, 0x100F3E30); - rebase(GetHotkeyLetter, 0x100F3E80); - rebase(IsReversedHotkey, 0x100F3ED0); - rebase(HotkeyAssignCallback, 0x100F4030); - - rebase(keyIdxToBind, 0x115B1ED0); - rebase(radMenuEntryToBind, 0x115B1ED4); - } -} hkAddresses; - - -class HotkeyReplacements : TempleFix -{ -public: - static BOOL HotkeyCompare(RadialMenuEntry& first, RadialMenuEntry & second); - static BOOL HotkeyActivate(objHndl obj); - void apply() override - { - replaceFunction(0x100F3B80, HotkeyInit); - replaceFunction(0x100F3BC0, HotkeyExit); - replaceFunction(0x100F3BD0, SaveHotkeys); - replaceFunction(0x100F3C80, LoadHotkeys); - replaceFunction(0x100F4030, HotkeyAssignCallback); - replaceFunction(0x100F0380, HotkeyCompare); - replaceFunction(0x100F0B80, HotkeyActivate); - - replaceFunction(0x10143450, [](InGameKeyEvent &msg, int modifier, int keyEvt){ - return uiSystems->GetManager().CharacterSelect(msg, modifier, keyEvt)? TRUE: FALSE; - }); - } -} hotkeyReplacements; - -BOOL HotkeyReplacements::HotkeyCompare(RadialMenuEntry& first, RadialMenuEntry& second) -{ - auto actionType = first.d20ActionType; - - if (actionType != second.d20ActionType) { - return FALSE; - } - - if (actionType == D20A_ACTIVATE_DEVICE_FREE - || actionType == D20A_ACTIVATE_DEVICE_STANDARD - || actionType == D20A_ACTIVATE_DEVICE_SPELL) - return first.textHash == second.textHash; - - if (actionType == D20A_USE_ITEM){ - if (first.d20SpellData.spellEnumOrg != second.d20SpellData.spellEnumOrg) - return FALSE; - - if (first.d20SpellData.metaMagicData != second.d20SpellData.metaMagicData) - return FALSE; - //return first.textHash == second.textHash; - return first.d20SpellData.spellSlotLevel == second.d20SpellData.spellSlotLevel; - - } - - if (first.d20ActionData1 != second.d20ActionData1) - return FALSE; - - if (first.d20SpellData.spellEnumOrg != second.d20SpellData.spellEnumOrg) - return FALSE; - - if (first.d20SpellData.metaMagicData != second.d20SpellData.metaMagicData) - return FALSE; - - if (first.d20ActionType == D20A_NONE &&first.textHash != second.textHash) - return FALSE; - - if (first.dispKey != second.dispKey) - return FALSE; - - return TRUE; -} - -BOOL HotkeyReplacements::HotkeyActivate(objHndl obj) -{ - auto radMenuForHK = temple::GetRef(0x115B2050); - - if (!radMenuForHK) - return FALSE; - - auto radMenuNodeCount = temple::GetRef(0x118676C0); - if (radMenuNodeCount > radMenuForHK->nodeCount) { - return FALSE; - } - - auto& activeRadialMenu = temple::GetRef(0x115B2048); - activeRadialMenu = radialMenus.GetForObj(obj); - - auto& radEntry = radMenuForHK->nodes[radMenuNodeCount -1].entry; - - auto& activeRadialMenuNode = temple::GetRef(0x115B204C); - activeRadialMenuNode = radMenuNodeCount - 1; - - if (radEntry.d20ActionType == D20A_CAST_SPELL) - actSeqSys.ActSeqSpellReset(); - else if (radEntry.d20ActionType == D20A_USE_ITEM && radEntry.d20SpellData.spellEnumOrg != 0){ - actSeqSys.ActSeqSpellReset(); - } - - auto nodeType = radEntry.type; - auto result = FALSE; - if (nodeType == RadialMenuEntryType::Action) - { - if (radEntry.callback){ - result = radEntry.callback(obj, &radEntry); - } - } - else if (nodeType == RadialMenuEntryType::Slider)// will toggle between min/max values - { - temple::GetRef(0x100F05C0)(obj, radEntry); // toggle value to min/max - temple::GetRef(0x100F05F0)(obj, radEntry); // activate / deactivate float line - result = FALSE; - } - else if (nodeType == RadialMenuEntryType::Toggle) - { - if (radEntry.callback) { - result = radEntry.callback(obj, &radEntry); - } - temple::GetRef(0x100F05F0)(obj, radEntry); // activate / deactivate float line - } - - - activeRadialMenu = nullptr; - activeRadialMenuNode = -1; - return result; - -} - -int HotkeySystem::SaveHotkeys(TioFile* file) -{ - for (int i = 0; i < NUM_ASSIGNABLE_HOTKEYS; i++) - { - RadialMenuEntry radMenu = hkAddresses.hotkeyTable[i]; - if (radMenu.d20ActionType != -2) - { - char hkText[128] = { 0, }; - memcpy(hkText, &hkAddresses.hotkeyTexts[128 * i], 128); - if (!tio_fwrite(&i, sizeof(i), 1, file) - || tio_fwrite(&radMenu, sizeof(RadialMenuEntry), 1, file) != 1 - || tio_fwrite(hkText, 1, 128, file) != 128) - return 0; - } - } - int terminator = -1; - return tio_fwrite(&terminator, sizeof(terminator), 1, file) == 1; -} - -int HotkeySystem::SaveHotkeys(FILE* file) -{ - for (int i = 0; i < NUM_ASSIGNABLE_HOTKEYS; i++) - { - RadialMenuEntry radMenu = hkAddresses.hotkeyTable[i]; - if (radMenu.d20ActionType != -2) - { - char hkText[128] = { 0, }; - memcpy(hkText, &hkAddresses.hotkeyTexts[128 * i], 128); - if (!fwrite(&i, sizeof(i), 1, file) - || fwrite(&radMenu, sizeof(RadialMenuEntry), 1, file) != 1 - || fwrite(&hkAddresses.hotkeyTexts[128 * i], 1, 128, file) != 128) - return 0; - } - } - int terminator = -1; - return fwrite(&terminator, sizeof(terminator), 1, file) == 1; -} - -int HotkeySystem::LoadHotkeys(GameSystemSaveFile* gsFile) -{ - int buffer; - while (tio_fread(&buffer, sizeof(buffer), 1, gsFile->file) == 1) - { - if (buffer == -1) { //terminator char - return 1; - } - - RadialMenuEntry radMenu; - if (tio_fread(&radMenu, sizeof(RadialMenuEntry), 1, gsFile->file) != 1 ) - break; - hkAddresses.hotkeyTable[buffer] = radMenu; - - char hkText[128]; - if (tio_fread(hkText, 1, 128, gsFile->file) != 128 ) - break; - memcpy(&hkAddresses.hotkeyTexts[128 * buffer], hkText, 128); - } - return 0; -} - -void HotkeySystem::HotkeyInit() -{ - for (int i = 0; i < NUM_ASSIGNABLE_HOTKEYS; i++) - { - hkAddresses.hotkeyTable[i].d20ActionType = D20A_UNASSIGNED; - } - mesFuncs.Open("mes\\hotkeys.mes", hkAddresses.hotkeyMes); - auto file = fopen("hotkeys.sco", "rb"); - int buffer; - if (file) - { - while (fread(&buffer, sizeof(buffer), 1, file) == 1) - { - if (buffer == -1) { - break; //terminator char - } - RadialMenuEntry radMenu; - if (fread(&radMenu, sizeof(RadialMenuEntry), 1, file) != 1) - break; - hkAddresses.hotkeyTable[buffer] = radMenu; - - char hkText[128]; - if (fread(hkText, 1, 128, file) != 128) - break; - memcpy(&hkAddresses.hotkeyTexts[128 * buffer], hkText, 128); - - } - fclose(file); - } - -} - -void HotkeySystem::HotkeyExit() -{ - mesFuncs.Close(*hkAddresses.hotkeyMes); - //auto file = tio_fopen("hotkeys.sco", "wb"); - - //tio_fclose(file); -} - -void HotkeySystem::HotkeyAssignCallback(int cancelFlag) -{ - if (!cancelFlag) - { - auto hotkeyTable = hkAddresses.hotkeyTable; - auto hkIdx = *hkAddresses.keyIdxToBind; - auto radMenuEntryToBind = *hkAddresses.radMenuEntryToBind; - - int radMenuIdx = hkAddresses.HotkeyTableSearch(radMenuEntryToBind); - if (radMenuIdx != -1) - hotkeyTable[radMenuIdx].d20ActionType = D20A_UNASSIGNED; - - hotkeyTable[hkIdx] = *radMenuEntryToBind; - auto hkText = &hkAddresses.hotkeyTexts[128 * hkIdx]; - strncpy(hkText, radMenuEntryToBind->text, 127); - hotkeyTable[hkIdx].text = hkText; - - auto file = fopen("hotkeys.sco", "wb"); - SaveHotkeys(file); - fclose(file); - - } -} - -BOOL HotkeySystem::IsReservedHotkey(uint32_t dinputKey) -{ - auto isReservedKey = temple::GetRef(0x100F3ED0); - return isReservedKey(dinputKey); -} - -int HotkeySystem::HotkeyReservedPopup(uint32_t dinputKey) -{ - auto hotkeyReservedPopup = temple::GetRef(0x100F3F20); - return hotkeyReservedPopup(dinputKey); -} - -BOOL HotkeySystem::IsNormalNonreservedHotkey(uint32_t dinputKey) -{ - auto isNormalNonreservedHotkey = temple::GetRef(0x100F3D20); - return isNormalNonreservedHotkey(dinputKey); -} - -bool HotkeySystem::IsKeyPressed(int virtualKey) -{ - return infrastructure::gKeyboard.IsKeyPressed(virtualKey); -} - -void __cdecl HotkeyInit() -{ - hotkeys.HotkeyInit(); -} - -void __cdecl HotkeyExit() -{ - hotkeys.HotkeyExit(); -} - -int __cdecl SaveHotkeys(TioFile* file) -{ - return hotkeys.SaveHotkeys(file); -} - - -int __cdecl LoadHotkeys(GameSystemSaveFile* file) -{ - return hotkeys.LoadHotkeys(file); -} - -void __cdecl HotkeyAssignCallback(int cancelFlag) -{ - hotkeys.HotkeyAssignCallback(cancelFlag); +#include "stdafx.h" +#include "radialmenu.h" +#include "tig/tig_mes.h" +#include "hotkeys.h" +#include "tio\tio.h" +#include "gamesystems/legacy.h" +#include "util/fixes.h" +#include "obj.h" +#include "critter.h" +#include "action_sequence.h" +#include "ui/ui_systems.h" +#include "ui/ui_legacysystems.h" +#include "infrastructure/keyboard.h" + +HotkeySystem hotkeys; + +struct HotkeyAddresses : temple::AddressTable +{ + int * assignableKeys; // size 39 int array, holding DirectX Input Key numbers + RadialMenuEntry * hotkeyTable; // 39 entries; index corresponds to key given in assignableKeys + MesHandle * hotkeyMes; + char * hotkeyPopupBodyText; // confirmation dialog text; 512 chars + char * hotkeyTexts; // 128 chars each, 39 entries, describing the hotkey assignment + + RadialMenuEntry** radMenuEntryToBind; + int * keyIdxToBind; + + int(__cdecl* HotkeyTableSearch)(RadialMenuEntry* radMenuEntry); + const char * (__cdecl* GetDikLetter)(int dik); + const char * (__cdecl* GetHotkeyLetter)(RadialMenuEntry radMenuEntry); // fetches the letter assigned to this radial menu entry from hotkey.mes + int(__cdecl* IsReversedHotkey)(int dik); + void(__cdecl* HotkeyAssignCallback)(char * cancelFlag); // assigns a hotkey (in the hotkeyTable) according to some global vars + + + HotkeyAddresses() + { + rebase(assignableKeys, 0x102E8B78); + rebase(hotkeyTable, 0x10BD0248); + rebase(hotkeyMes, 0x10BD0D40); + rebase(hotkeyPopupBodyText, 0x10BD0D48); + rebase(hotkeyTexts, 0x10BD0F48); + + rebase(HotkeyTableSearch, 0x100F3DF0); + rebase(GetDikLetter, 0x100F3E30); + rebase(GetHotkeyLetter, 0x100F3E80); + rebase(IsReversedHotkey, 0x100F3ED0); + rebase(HotkeyAssignCallback, 0x100F4030); + + rebase(keyIdxToBind, 0x115B1ED0); + rebase(radMenuEntryToBind, 0x115B1ED4); + } +} hkAddresses; + + +class HotkeyReplacements : TempleFix +{ +public: + static BOOL HotkeyCompare(RadialMenuEntry& first, RadialMenuEntry & second); + static BOOL HotkeyActivate(objHndl obj); + void apply() override + { + replaceFunction(0x100F3B80, HotkeyInit); + replaceFunction(0x100F3BC0, HotkeyExit); + replaceFunction(0x100F3BD0, SaveHotkeys); + replaceFunction(0x100F3C80, LoadHotkeys); + replaceFunction(0x100F4030, HotkeyAssignCallback); + replaceFunction(0x100F0380, HotkeyCompare); + replaceFunction(0x100F0B80, HotkeyActivate); + + replaceFunction(0x10143450, [](InGameKeyEvent &msg, int modifier, int keyEvt){ + return uiSystems->GetManager().CharacterSelect(msg, modifier, keyEvt)? TRUE: FALSE; + }); + } +} hotkeyReplacements; + +BOOL HotkeyReplacements::HotkeyCompare(RadialMenuEntry& first, RadialMenuEntry& second) +{ + auto actionType = first.d20ActionType; + + if (actionType != second.d20ActionType) { + return FALSE; + } + + if (actionType == D20A_ACTIVATE_DEVICE_FREE + || actionType == D20A_ACTIVATE_DEVICE_STANDARD + || actionType == D20A_ACTIVATE_DEVICE_SPELL) + return first.textHash == second.textHash; + + if (actionType == D20A_USE_ITEM){ + if (first.d20SpellData.spellEnumOrg != second.d20SpellData.spellEnumOrg) + return FALSE; + + if (first.d20SpellData.metaMagicData != second.d20SpellData.metaMagicData) + return FALSE; + //return first.textHash == second.textHash; + return first.d20SpellData.spellSlotLevel == second.d20SpellData.spellSlotLevel; + + } + + if (first.d20ActionData1 != second.d20ActionData1) + return FALSE; + + if (first.d20SpellData.spellEnumOrg != second.d20SpellData.spellEnumOrg) + return FALSE; + + if (first.d20SpellData.metaMagicData != second.d20SpellData.metaMagicData) + return FALSE; + + if (first.d20ActionType == D20A_NONE &&first.textHash != second.textHash) + return FALSE; + + if (first.dispKey != second.dispKey) + return FALSE; + + return TRUE; +} + +BOOL HotkeyReplacements::HotkeyActivate(objHndl obj) +{ + auto radMenuForHK = temple::GetRef(0x115B2050); + + if (!radMenuForHK) + return FALSE; + + auto radMenuNodeCount = temple::GetRef(0x118676C0); + if (radMenuNodeCount > radMenuForHK->nodeCount) { + return FALSE; + } + + auto& activeRadialMenu = temple::GetRef(0x115B2048); + activeRadialMenu = radialMenus.GetForObj(obj); + + auto& radEntry = radMenuForHK->nodes[radMenuNodeCount -1].entry; + + auto& activeRadialMenuNode = temple::GetRef(0x115B204C); + activeRadialMenuNode = radMenuNodeCount - 1; + + if (radEntry.d20ActionType == D20A_CAST_SPELL) + actSeqSys.ActSeqSpellReset(); + else if (radEntry.d20ActionType == D20A_USE_ITEM && radEntry.d20SpellData.spellEnumOrg != 0){ + actSeqSys.ActSeqSpellReset(); + } + + auto nodeType = radEntry.type; + auto result = FALSE; + if (nodeType == RadialMenuEntryType::Action) + { + if (radEntry.callback){ + result = radEntry.callback(obj, &radEntry); + } + } + else if (nodeType == RadialMenuEntryType::Slider)// will toggle between min/max values + { + temple::GetRef(0x100F05C0)(obj, radEntry); // toggle value to min/max + temple::GetRef(0x100F05F0)(obj, radEntry); // activate / deactivate float line + result = FALSE; + } + else if (nodeType == RadialMenuEntryType::Toggle) + { + if (radEntry.callback) { + result = radEntry.callback(obj, &radEntry); + } + temple::GetRef(0x100F05F0)(obj, radEntry); // activate / deactivate float line + } + + + activeRadialMenu = nullptr; + activeRadialMenuNode = -1; + return result; + +} + +int HotkeySystem::SaveHotkeys(TioFile* file) +{ + for (int i = 0; i < NUM_ASSIGNABLE_HOTKEYS; i++) + { + RadialMenuEntry radMenu = hkAddresses.hotkeyTable[i]; + if (radMenu.d20ActionType != -2) + { + char hkText[128] = { 0, }; + memcpy(hkText, &hkAddresses.hotkeyTexts[128 * i], 128); + if (!tio_fwrite(&i, sizeof(i), 1, file) + || tio_fwrite(&radMenu, sizeof(RadialMenuEntry), 1, file) != 1 + || tio_fwrite(hkText, 1, 128, file) != 128) + return 0; + } + } + int terminator = -1; + return tio_fwrite(&terminator, sizeof(terminator), 1, file) == 1; +} + +int HotkeySystem::SaveHotkeys(FILE* file) +{ + for (int i = 0; i < NUM_ASSIGNABLE_HOTKEYS; i++) + { + RadialMenuEntry radMenu = hkAddresses.hotkeyTable[i]; + if (radMenu.d20ActionType != -2) + { + char hkText[128] = { 0, }; + memcpy(hkText, &hkAddresses.hotkeyTexts[128 * i], 128); + if (!fwrite(&i, sizeof(i), 1, file) + || fwrite(&radMenu, sizeof(RadialMenuEntry), 1, file) != 1 + || fwrite(&hkAddresses.hotkeyTexts[128 * i], 1, 128, file) != 128) + return 0; + } + } + int terminator = -1; + return fwrite(&terminator, sizeof(terminator), 1, file) == 1; +} + +int HotkeySystem::LoadHotkeys(GameSystemSaveFile* gsFile) +{ + return LoadHotkeys(gsFile->file); +} + +int HotkeySystem::LoadHotkeys(TioFile* file) +{ + int buffer; + while (tio_fread(&buffer, sizeof(buffer), 1, file) == 1) + { + if (buffer == -1) { //terminator char + return 1; + } + + RadialMenuEntry radMenu; + if (tio_fread(&radMenu, sizeof(RadialMenuEntry), 1, file) != 1) + break; + hkAddresses.hotkeyTable[buffer] = radMenu; + + char hkText[128]; + if (tio_fread(hkText, 1, 128, file) != 128) + break; + memcpy(&hkAddresses.hotkeyTexts[128 * buffer], hkText, 128); + } + return 0; +} + +void HotkeySystem::HotkeyInit() +{ + for (int i = 0; i < NUM_ASSIGNABLE_HOTKEYS; i++) + { + hkAddresses.hotkeyTable[i].d20ActionType = D20A_UNASSIGNED; + } + mesFuncs.Open("mes\\hotkeys.mes", hkAddresses.hotkeyMes); +} + +void HotkeySystem::HotkeyExit() +{ + mesFuncs.Close(*hkAddresses.hotkeyMes); + //auto file = tio_fopen("hotkeys.sco", "wb"); + + //tio_fclose(file); +} + +void HotkeySystem::HotkeyAssignCallback(int cancelFlag) +{ + if (!cancelFlag) + { + auto hotkeyTable = hkAddresses.hotkeyTable; + auto hkIdx = *hkAddresses.keyIdxToBind; + auto radMenuEntryToBind = *hkAddresses.radMenuEntryToBind; + + int radMenuIdx = hkAddresses.HotkeyTableSearch(radMenuEntryToBind); + if (radMenuIdx != -1) + hotkeyTable[radMenuIdx].d20ActionType = D20A_UNASSIGNED; + + hotkeyTable[hkIdx] = *radMenuEntryToBind; + auto hkText = &hkAddresses.hotkeyTexts[128 * hkIdx]; + strncpy(hkText, radMenuEntryToBind->text, 127); + hotkeyTable[hkIdx].text = hkText; + + auto file = tio_fopen("hotkeys.sco", "wb"); + SaveHotkeys(file); + tio_fclose(file); + + } +} + +BOOL HotkeySystem::IsReservedHotkey(uint32_t dinputKey) +{ + auto isReservedKey = temple::GetRef(0x100F3ED0); + return isReservedKey(dinputKey); +} + +int HotkeySystem::HotkeyReservedPopup(uint32_t dinputKey) +{ + auto hotkeyReservedPopup = temple::GetRef(0x100F3F20); + return hotkeyReservedPopup(dinputKey); +} + +BOOL HotkeySystem::IsNormalNonreservedHotkey(uint32_t dinputKey) +{ + auto isNormalNonreservedHotkey = temple::GetRef(0x100F3D20); + return isNormalNonreservedHotkey(dinputKey); +} + +bool HotkeySystem::IsKeyPressed(int virtualKey) +{ + return infrastructure::gKeyboard.IsKeyPressed(virtualKey); +} + +void __cdecl HotkeyInit() +{ + hotkeys.HotkeyInit(); +} + +void __cdecl HotkeyExit() +{ + hotkeys.HotkeyExit(); +} + +int __cdecl SaveHotkeys(TioFile* file) +{ + return hotkeys.SaveHotkeys(file); +} + + +int __cdecl LoadHotkeys(GameSystemSaveFile* file) +{ + return hotkeys.LoadHotkeys(file); +} + +void __cdecl HotkeyAssignCallback(int cancelFlag) +{ + hotkeys.HotkeyAssignCallback(cancelFlag); } \ No newline at end of file diff --git a/TemplePlus/hotkeys.h b/TemplePlus/hotkeys.h index e075cee0e..446ec5660 100644 --- a/TemplePlus/hotkeys.h +++ b/TemplePlus/hotkeys.h @@ -1,33 +1,34 @@ -#pragma once -#include "common.h" - -#define NUM_ASSIGNABLE_HOTKEYS 39 - -struct GameSystemSaveFile; -struct TioFile; - -class HotkeySystem -{ -public: - int SaveHotkeys(TioFile * file); - int SaveHotkeys(FILE * file); - int LoadHotkeys(GameSystemSaveFile * file); - void HotkeyInit(); - void HotkeyExit(); - void HotkeyAssignCallback(int cancelFlag); - BOOL IsReservedHotkey(uint32_t dinputKey); - int HotkeyReservedPopup(uint32_t dinputKey); - BOOL IsNormalNonreservedHotkey(uint32_t dinputKey); - - - bool IsKeyPressed(int virtualKey); // input is VK_* -}; - - -extern HotkeySystem hotkeys; - -void __cdecl HotkeyInit(); -void __cdecl HotkeyExit(); -int __cdecl SaveHotkeys(TioFile* file); -int __cdecl LoadHotkeys(GameSystemSaveFile* file); +#pragma once +#include "common.h" + +#define NUM_ASSIGNABLE_HOTKEYS 39 + +struct GameSystemSaveFile; +struct TioFile; + +class HotkeySystem +{ +public: + int SaveHotkeys(TioFile * file); + int SaveHotkeys(FILE * file); + int LoadHotkeys(GameSystemSaveFile * file); + int LoadHotkeys(TioFile* file); + void HotkeyInit(); + void HotkeyExit(); + void HotkeyAssignCallback(int cancelFlag); + BOOL IsReservedHotkey(uint32_t dinputKey); + int HotkeyReservedPopup(uint32_t dinputKey); + BOOL IsNormalNonreservedHotkey(uint32_t dinputKey); + + + bool IsKeyPressed(int virtualKey); // input is VK_* +}; + + +extern HotkeySystem hotkeys; + +void __cdecl HotkeyInit(); +void __cdecl HotkeyExit(); +int __cdecl SaveHotkeys(TioFile* file); +int __cdecl LoadHotkeys(GameSystemSaveFile* file); void __cdecl HotkeyAssignCallback( int cancelFlag); \ No newline at end of file