mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2025-08-22 06:13:45 -07:00
Port various systems/patterns from 2ship
This commit is contained in:
parent
1d716a5975
commit
ebad1874ac
17 changed files with 543 additions and 18 deletions
|
@ -306,6 +306,7 @@ typedef enum {
|
||||||
/* 0x99 */ ITEM_STICK_UPGRADE_30,
|
/* 0x99 */ ITEM_STICK_UPGRADE_30,
|
||||||
/* 0x9A */ ITEM_NUT_UPGRADE_30,
|
/* 0x9A */ ITEM_NUT_UPGRADE_30,
|
||||||
/* 0x9B */ ITEM_NUT_UPGRADE_40,
|
/* 0x9B */ ITEM_NUT_UPGRADE_40,
|
||||||
|
/* 0x9C */ ITEM_SHIP, // SOH [Enhancement] Added to enable custom item gives
|
||||||
/* 0xFC */ ITEM_LAST_USED = 0xFC,
|
/* 0xFC */ ITEM_LAST_USED = 0xFC,
|
||||||
/* 0xFE */ ITEM_NONE_FE = 0xFE,
|
/* 0xFE */ ITEM_NONE_FE = 0xFE,
|
||||||
/* 0xFF */ ITEM_NONE = 0xFF
|
/* 0xFF */ ITEM_NONE = 0xFF
|
||||||
|
@ -457,7 +458,8 @@ typedef enum {
|
||||||
/* 0x7B */ GI_BULLET_BAG_50,
|
/* 0x7B */ GI_BULLET_BAG_50,
|
||||||
/* 0x7C */ GI_ICE_TRAP, // freezes link when opened from a chest
|
/* 0x7C */ GI_ICE_TRAP, // freezes link when opened from a chest
|
||||||
/* 0x7D */ GI_TEXT_0, // no model appears over Link, shows text id 0 (pocket egg)
|
/* 0x7D */ GI_TEXT_0, // no model appears over Link, shows text id 0 (pocket egg)
|
||||||
/* 0x84 */ GI_MAX
|
/* 0x7E */ GI_SHIP, // SOH [Enhancement] Added to enable custom item gives
|
||||||
|
/* 0x7E */ GI_MAX
|
||||||
} GetItemID;
|
} GetItemID;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
260
soh/soh/Enhancements/custom-item/CustomItem.cpp
Normal file
260
soh/soh/Enhancements/custom-item/CustomItem.cpp
Normal file
|
@ -0,0 +1,260 @@
|
||||||
|
#include "CustomItem.h"
|
||||||
|
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "z64actor.h"
|
||||||
|
#include "functions.h"
|
||||||
|
#include "variables.h"
|
||||||
|
#include "macros.h"
|
||||||
|
extern PlayState* gPlayState;
|
||||||
|
}
|
||||||
|
|
||||||
|
// #region These were copied from z_en_item00.c
|
||||||
|
static ColliderCylinderInit sCylinderInit = {
|
||||||
|
{
|
||||||
|
COLTYPE_NONE,
|
||||||
|
AT_NONE,
|
||||||
|
AC_ON | AT_TYPE_PLAYER,
|
||||||
|
OC1_NONE,
|
||||||
|
OC2_NONE,
|
||||||
|
COLSHAPE_CYLINDER,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ELEMTYPE_UNK0,
|
||||||
|
{ 0x00000000, 0x00, 0x00 },
|
||||||
|
{ 0x00000010, 0x00, 0x00 },
|
||||||
|
TOUCH_NONE | TOUCH_SFX_NORMAL,
|
||||||
|
BUMP_ON,
|
||||||
|
OCELEM_NONE,
|
||||||
|
},
|
||||||
|
{ 10, 30, 0, { 0, 0, 0 } },
|
||||||
|
};
|
||||||
|
|
||||||
|
static InitChainEntry sInitChain[] = {
|
||||||
|
ICHAIN_F32(targetArrowOffset, 2000, ICHAIN_STOP),
|
||||||
|
};
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
EnItem00* CustomItem::Spawn(f32 posX, f32 posY, f32 posZ, s16 rot, s16 flags, s16 params, ActorFunc actionFunc,
|
||||||
|
ActorFunc drawFunc) {
|
||||||
|
if (!gPlayState) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Actor* actor = Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_ITEM00, posX, posY, posZ, flags, rot, params,
|
||||||
|
ITEM00_NONE, false);
|
||||||
|
EnItem00* enItem00 = (EnItem00*)actor;
|
||||||
|
|
||||||
|
if (actionFunc != NULL) {
|
||||||
|
enItem00->actionFunc = (EnItem00ActionFunc)actionFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drawFunc != NULL) {
|
||||||
|
actor->draw = drawFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return enItem00;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomItem_Init(Actor* actor, PlayState* play) {
|
||||||
|
EnItem00* enItem00 = (EnItem00*)actor;
|
||||||
|
|
||||||
|
if (CUSTOM_ITEM_FLAGS & CustomItem::STOP_BOBBING) {
|
||||||
|
actor->shape.yOffset = 1250.0f;
|
||||||
|
} else {
|
||||||
|
actor->shape.yOffset = (Math_SinS(actor->shape.rot.y) * 150.0f) + 1250.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CUSTOM_ITEM_FLAGS & CustomItem::HIDE_TILL_OVERHEAD) {
|
||||||
|
Actor_SetScale(actor, 0.0f);
|
||||||
|
} else {
|
||||||
|
Actor_SetScale(actor, 0.015f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CUSTOM_ITEM_FLAGS & CustomItem::KEEP_ON_PLAYER) {
|
||||||
|
Math_Vec3f_Copy(&actor->world.pos, &GET_PLAYER(play)->actor.world.pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CUSTOM_ITEM_FLAGS & CustomItem::TOSS_ON_SPAWN) {
|
||||||
|
actor->velocity.y = 8.0f;
|
||||||
|
actor->speedXZ = 2.0f;
|
||||||
|
actor->gravity = -1.4f;
|
||||||
|
actor->world.rot.y = Rand_ZeroOne() * 40000.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
Actor_ProcessInitChain(actor, sInitChain);
|
||||||
|
Collider_InitCylinder(play, &enItem00->collider);
|
||||||
|
Collider_SetCylinder(play, &enItem00->collider, actor, &sCylinderInit);
|
||||||
|
|
||||||
|
enItem00->unk_15A = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// By default this will just assume the GID was passed in as the rot z, if you want different functionality you should
|
||||||
|
// override the draw
|
||||||
|
void CustomItem_Draw(Actor* actor, PlayState* play) {
|
||||||
|
Matrix_Scale(30.0f, 30.0f, 30.0f, MTXMODE_APPLY);
|
||||||
|
GetItem_Draw(play, CUSTOM_ITEM_PARAM);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once the item is touched we need to clear movement vars so the item doesn't sink in the players hands/above head
|
||||||
|
void CustomItem_ItemTouched(Actor* actor, PlayState* play) {
|
||||||
|
actor->speedXZ = 0.0f;
|
||||||
|
actor->velocity.y = 0.0f;
|
||||||
|
actor->gravity = 0.0f;
|
||||||
|
actor->shape.yOffset = 1250.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomItem_Update(Actor* actor, PlayState* play) {
|
||||||
|
EnItem00* enItem00 = (EnItem00*)actor;
|
||||||
|
Player* player = GET_PLAYER(play);
|
||||||
|
|
||||||
|
if (!(CUSTOM_ITEM_FLAGS & CustomItem::STOP_SPINNING)) {
|
||||||
|
actor->shape.rot.y += 960;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(CUSTOM_ITEM_FLAGS & CustomItem::STOP_BOBBING)) {
|
||||||
|
actor->shape.yOffset = (Math_SinS(actor->shape.rot.y) * 150.0f) + 1250.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CUSTOM_ITEM_FLAGS & CustomItem::HIDE_TILL_OVERHEAD) {
|
||||||
|
Actor_SetScale(actor, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CUSTOM_ITEM_FLAGS & CustomItem::KEEP_ON_PLAYER) {
|
||||||
|
Math_Vec3f_Copy(&actor->world.pos, &GET_PLAYER(play)->actor.world.pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Player range check accounting for goron rolling behavior. Matches EnItem00 range check.
|
||||||
|
bool playerInRangeOfPickup = (actor->xzDistToPlayer <= 30.0f) && (fabsf(actor->yDistToPlayer) <= fabsf(50.0f));
|
||||||
|
|
||||||
|
if (CUSTOM_ITEM_FLAGS & CustomItem::KILL_ON_TOUCH) {
|
||||||
|
// Pretty self explanatory, if the player is within range, kill the actor and call the action function
|
||||||
|
if (playerInRangeOfPickup) {
|
||||||
|
if (enItem00->actionFunc != NULL) {
|
||||||
|
enItem00->actionFunc(enItem00, play);
|
||||||
|
CUSTOM_ITEM_FLAGS |= CustomItem::CALLED_ACTION;
|
||||||
|
}
|
||||||
|
Actor_Kill(actor);
|
||||||
|
}
|
||||||
|
} else if (CUSTOM_ITEM_FLAGS & CustomItem::GIVE_OVERHEAD) {
|
||||||
|
// If the item hasn't been picked up (unk_15A == -1) and the player is within range
|
||||||
|
if (enItem00->unk_15A == -1 && playerInRangeOfPickup) {
|
||||||
|
// Fire the action function
|
||||||
|
if (enItem00->actionFunc != NULL) {
|
||||||
|
enItem00->actionFunc(enItem00, play);
|
||||||
|
CUSTOM_ITEM_FLAGS |= CustomItem::CALLED_ACTION;
|
||||||
|
}
|
||||||
|
Sfx_PlaySfxCentered(NA_SE_SY_GET_ITEM);
|
||||||
|
// Set the unk_15A to 15, this indicates the item has been picked up and will start the overhead animation
|
||||||
|
enItem00->unk_15A = 15;
|
||||||
|
CUSTOM_ITEM_FLAGS |= CustomItem::STOP_BOBBING;
|
||||||
|
CUSTOM_ITEM_FLAGS |= CustomItem::KEEP_ON_PLAYER;
|
||||||
|
CustomItem_ItemTouched(actor, play);
|
||||||
|
// Move to player right away on this frame
|
||||||
|
Math_Vec3f_Copy(&actor->world.pos, &GET_PLAYER(play)->actor.world.pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the item has been picked up
|
||||||
|
if (enItem00->unk_15A > 0) {
|
||||||
|
// Reduce the size a bit, but also makes it visible for HIDE_TILL_OVERHEAD
|
||||||
|
Actor_SetScale(actor, 0.010f);
|
||||||
|
|
||||||
|
// Decrement the unk_15A, which will be used to bob the item up and down
|
||||||
|
enItem00->unk_15A--;
|
||||||
|
|
||||||
|
// Account for the different heights of the player forms
|
||||||
|
f32 height = LINK_IS_ADULT ? 60.0f : 45.0f;
|
||||||
|
|
||||||
|
// Bob the item up and down
|
||||||
|
actor->world.pos.y += (height + (Math_SinS(enItem00->unk_15A * 15000) * (enItem00->unk_15A * 0.3f)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, once the bobbing animation is done, kill the actor
|
||||||
|
if (enItem00->unk_15A == 0) {
|
||||||
|
Actor_Kill(actor);
|
||||||
|
}
|
||||||
|
} else if (CUSTOM_ITEM_FLAGS & CustomItem::GIVE_ITEM_CUTSCENE) {
|
||||||
|
// If the item hasn't been picked up and the player is within range
|
||||||
|
|
||||||
|
if (!Actor_HasParent(actor, play) && enItem00->unk_15A == -1) {
|
||||||
|
Actor_OfferGetItem(actor, play, GI_SHIP, 50.0f, 80.0f);
|
||||||
|
} else {
|
||||||
|
if (enItem00->unk_15A == -1) {
|
||||||
|
// actor->shape.yOffset = 1250.0f;
|
||||||
|
CUSTOM_ITEM_FLAGS |= CustomItem::STOP_BOBBING;
|
||||||
|
// Math_Vec3f_Copy(&actor->world.pos, &GET_PLAYER(play)->actor.world.pos);
|
||||||
|
CUSTOM_ITEM_FLAGS |= CustomItem::KEEP_ON_PLAYER;
|
||||||
|
// Actor_SetScale(actor, 0.0f);
|
||||||
|
CUSTOM_ITEM_FLAGS |= CustomItem::HIDE_TILL_OVERHEAD;
|
||||||
|
CustomItem_ItemTouched(actor, play);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin incrementing the unk_15A, indicating the item has been picked up
|
||||||
|
enItem00->unk_15A++;
|
||||||
|
|
||||||
|
// For the first 20 frames, wait while the player's animation plays
|
||||||
|
if (enItem00->unk_15A >= 20) {
|
||||||
|
// After the first 20 frames, show the item and call the action function
|
||||||
|
if (enItem00->unk_15A == 20 && enItem00->actionFunc != NULL) {
|
||||||
|
enItem00->actionFunc(enItem00, play);
|
||||||
|
CUSTOM_ITEM_FLAGS |= CustomItem::CALLED_ACTION;
|
||||||
|
}
|
||||||
|
// Override the bobbing animation to be a fixed height
|
||||||
|
actor->shape.yOffset = 900.0f;
|
||||||
|
Actor_SetScale(actor, 0.007f);
|
||||||
|
|
||||||
|
// Account for the different heights of the player forms
|
||||||
|
f32 height = LINK_IS_ADULT ? 60.0f : 45.0f;
|
||||||
|
|
||||||
|
actor->world.pos.y += height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once the player is no longer in the "Give Item" state, kill the actor
|
||||||
|
if (!(player->stateFlags1 & PLAYER_STATE1_GETTING_ITEM)) {
|
||||||
|
Actor_Kill(actor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actor->gravity != 0.0f) {
|
||||||
|
Actor_MoveXZGravity(actor);
|
||||||
|
Actor_UpdateBgCheckInfo(play, actor, 20.0f, 15.0f, 15.0f, 0x1D);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actor->bgCheckFlags & 0x0003) {
|
||||||
|
actor->speedXZ = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CUSTOM_ITEM_FLAGS & CustomItem::ABLE_TO_BOOMERANG) {
|
||||||
|
Collider_UpdateCylinder(actor, &enItem00->collider);
|
||||||
|
CollisionCheck_SetAC(play, &play->colChkCtx, &enItem00->collider.base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomItem_Destroy(Actor* actor, PlayState* play) {
|
||||||
|
EnItem00* enItem00 = (EnItem00*)actor;
|
||||||
|
|
||||||
|
Collider_DestroyCylinder(play, &enItem00->collider);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomItem::RegisterHooks() {
|
||||||
|
GameInteractor::Instance->RegisterGameHookForID<GameInteractor::ShouldActorInit>(
|
||||||
|
ACTOR_EN_ITEM00, [](void* actorRef, bool* should) {
|
||||||
|
Actor* actor = (Actor*)actorRef;
|
||||||
|
if (actor->params != ITEM00_NONE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
actor->init = CustomItem_Init;
|
||||||
|
actor->update = CustomItem_Update;
|
||||||
|
actor->draw = CustomItem_Draw;
|
||||||
|
actor->destroy = CustomItem_Destroy;
|
||||||
|
|
||||||
|
// Set the rotX/rotZ back to 0, the original values can be accessed from actor->home
|
||||||
|
actor->world.rot.x = 0;
|
||||||
|
actor->world.rot.z = 0;
|
||||||
|
actor->shape.rot.x = 0;
|
||||||
|
actor->shape.rot.y = 0;
|
||||||
|
actor->shape.rot.z = 0;
|
||||||
|
});
|
||||||
|
}
|
25
soh/soh/Enhancements/custom-item/CustomItem.h
Normal file
25
soh/soh/Enhancements/custom-item/CustomItem.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
extern "C" {
|
||||||
|
#include "z64actor.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CUSTOM_ITEM_FLAGS (actor->home.rot.x)
|
||||||
|
#define CUSTOM_ITEM_PARAM (actor->home.rot.z)
|
||||||
|
|
||||||
|
namespace CustomItem {
|
||||||
|
|
||||||
|
enum CustomItemFlags : int16_t {
|
||||||
|
KILL_ON_TOUCH = 1 << 0, // 0000 0000 0000 0001
|
||||||
|
GIVE_OVERHEAD = 1 << 1, // 0000 0000 0000 0010
|
||||||
|
GIVE_ITEM_CUTSCENE = 1 << 2, // 0000 0000 0000 0100
|
||||||
|
HIDE_TILL_OVERHEAD = 1 << 3, // 0000 0000 0000 1000
|
||||||
|
KEEP_ON_PLAYER = 1 << 4, // 0000 0000 0001 0000
|
||||||
|
STOP_BOBBING = 1 << 5, // 0000 0000 0010 0000
|
||||||
|
STOP_SPINNING = 1 << 6, // 0000 0000 0100 0000
|
||||||
|
CALLED_ACTION = 1 << 7, // 0000 0000 1000 0000
|
||||||
|
TOSS_ON_SPAWN = 1 << 8, // 0000 0001 0000 0000
|
||||||
|
ABLE_TO_BOOMERANG = 1 << 9, // 0000 0010 0000 0000
|
||||||
|
};
|
||||||
|
void RegisterHooks();
|
||||||
|
EnItem00* Spawn(f32 posX, f32 posY, f32 posZ, s16 rot, s16 flags, s16 params, ActorFunc actionFunc = NULL,
|
||||||
|
ActorFunc drawFunc = NULL);
|
||||||
|
}; // namespace CustomItem
|
|
@ -1,5 +1,7 @@
|
||||||
#include "CustomMessageManager.h"
|
#include "CustomMessageManager.h"
|
||||||
#include "CustomMessageInterfaceAddon.h"
|
#include "CustomMessageInterfaceAddon.h"
|
||||||
|
#include "CustomMessageTypes.h"
|
||||||
|
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
@ -7,6 +9,14 @@
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include <variables.h>
|
#include <variables.h>
|
||||||
|
|
||||||
|
#include "soh/util.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "functions.h"
|
||||||
|
|
||||||
|
extern PlayState* gPlayState;
|
||||||
|
}
|
||||||
|
|
||||||
using namespace std::literals::string_literals;
|
using namespace std::literals::string_literals;
|
||||||
|
|
||||||
static const std::unordered_map<std::string, char> textBoxSpecialCharacters = {
|
static const std::unordered_map<std::string, char> textBoxSpecialCharacters = {
|
||||||
|
@ -212,6 +222,27 @@ const TextBoxPosition& CustomMessage::GetTextBoxPosition() const {
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CustomMessage::LoadIntoFont() {
|
||||||
|
MessageContext* msgCtx = &gPlayState->msgCtx;
|
||||||
|
Font* font = &msgCtx->font;
|
||||||
|
char* buffer = font->msgBuf;
|
||||||
|
const int maxBufferSize = sizeof(font->msgBuf);
|
||||||
|
|
||||||
|
font->charTexBuf[0] = (type << 4) | position;
|
||||||
|
|
||||||
|
std::string content = GetEnglish(MF_RAW);
|
||||||
|
switch (gSaveContext.language) {
|
||||||
|
case LANGUAGE_FRA:
|
||||||
|
content = GetFrench(MF_RAW);
|
||||||
|
break;
|
||||||
|
case LANGUAGE_GER:
|
||||||
|
content = GetGerman(MF_RAW);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
msgCtx->msgLength = font->msgLength = SohUtils::CopyStringToCharBuffer(buffer, content, maxBufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
CustomMessage CustomMessage::operator+(const CustomMessage& right) const {
|
CustomMessage CustomMessage::operator+(const CustomMessage& right) const {
|
||||||
std::vector<std::string> newColors = colors;
|
std::vector<std::string> newColors = colors;
|
||||||
std::vector<std::string> rColors = right.GetColors();
|
std::vector<std::string> rColors = right.GetColors();
|
||||||
|
@ -798,3 +829,21 @@ bool CustomMessageManager::AddCustomMessageTable(std::string tableID) {
|
||||||
CustomMessageTable newMessageTable;
|
CustomMessageTable newMessageTable;
|
||||||
return messageTables.emplace(tableID, newMessageTable).second;
|
return messageTables.emplace(tableID, newMessageTable).second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CustomMessageManager::SetActiveCustomMessage(CustomMessage message) {
|
||||||
|
activeCustomMessage = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomMessageManager::StartTextbox(CustomMessage message) {
|
||||||
|
activeCustomMessage = message;
|
||||||
|
|
||||||
|
Message_StartTextbox(gPlayState, TEXT_CUSTOM_MESSAGE, &GET_PLAYER(gPlayState)->actor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomMessageManager::RegisterHooks() {
|
||||||
|
GameInteractor::Instance->RegisterGameHookForID<GameInteractor::OnOpenText>(
|
||||||
|
TEXT_CUSTOM_MESSAGE, [&](u16* textId, bool* loadFromMessageTable) {
|
||||||
|
*loadFromMessageTable = false;
|
||||||
|
activeCustomMessage.LoadIntoFont();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -76,6 +76,9 @@ class CustomMessage {
|
||||||
void SetTextBoxType(TextBoxType boxType);
|
void SetTextBoxType(TextBoxType boxType);
|
||||||
const TextBoxPosition& GetTextBoxPosition() const;
|
const TextBoxPosition& GetTextBoxPosition() const;
|
||||||
|
|
||||||
|
// To only be used with OnOpenText hook
|
||||||
|
void LoadIntoFont();
|
||||||
|
|
||||||
CustomMessage operator+(const CustomMessage& right) const;
|
CustomMessage operator+(const CustomMessage& right) const;
|
||||||
CustomMessage operator+(const std::string& right) const;
|
CustomMessage operator+(const std::string& right) const;
|
||||||
void operator+=(const std::string& right);
|
void operator+=(const std::string& right);
|
||||||
|
@ -236,11 +239,15 @@ class CustomMessageManager {
|
||||||
|
|
||||||
bool InsertCustomMessage(std::string tableID, uint16_t textID, CustomMessage message);
|
bool InsertCustomMessage(std::string tableID, uint16_t textID, CustomMessage message);
|
||||||
|
|
||||||
|
CustomMessage activeCustomMessage;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static CustomMessageManager* Instance;
|
static CustomMessageManager* Instance;
|
||||||
|
|
||||||
CustomMessageManager() = default;
|
CustomMessageManager() = default;
|
||||||
|
|
||||||
|
void RegisterHooks();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Formats the provided Custom Message Entry and inserts it into the table with the provided tableID,
|
* @brief Formats the provided Custom Message Entry and inserts it into the table with the provided tableID,
|
||||||
* with the provided giid (getItemID) as its key. This function also inserts the icon corresponding to
|
* with the provided giid (getItemID) as its key. This function also inserts the icon corresponding to
|
||||||
|
@ -299,6 +306,22 @@ class CustomMessageManager {
|
||||||
* already exists.)
|
* already exists.)
|
||||||
*/
|
*/
|
||||||
bool AddCustomMessageTable(std::string tableID);
|
bool AddCustomMessageTable(std::string tableID);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the active custom message, which will be used the next time
|
||||||
|
* TEXT_CUSTOM_MESSAGE is used for a text box.
|
||||||
|
*
|
||||||
|
* @param message the message to set as active
|
||||||
|
*/
|
||||||
|
void SetActiveCustomMessage(CustomMessage message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Displays a custom message in a textbox. This is the same as calling
|
||||||
|
* SetActiveCustomMessage and then beginning a textbox with TEXT_CUSTOM_MESSAGE.
|
||||||
|
*
|
||||||
|
* @param message the message to set as active
|
||||||
|
*/
|
||||||
|
void StartTextbox(CustomMessage message);
|
||||||
};
|
};
|
||||||
|
|
||||||
class MessageNotFoundException : public std::exception {
|
class MessageNotFoundException : public std::exception {
|
||||||
|
|
|
@ -14,6 +14,7 @@ typedef enum {
|
||||||
TEXT_SKULLTULA_PEOPLE_MAKE_YOU_VERY_RICH = 0x0027,
|
TEXT_SKULLTULA_PEOPLE_MAKE_YOU_VERY_RICH = 0x0027,
|
||||||
TEXT_SKULLTULA_PEOPLE_CURSE_HAS_BEEN_BROKEN = 0x0028,
|
TEXT_SKULLTULA_PEOPLE_CURSE_HAS_BEEN_BROKEN = 0x0028,
|
||||||
TEXT_SKULLTULA_PEOPLE_SAVING_MY_KIDS = 0x0029,
|
TEXT_SKULLTULA_PEOPLE_SAVING_MY_KIDS = 0x0029,
|
||||||
|
TEXT_CUSTOM_MESSAGE = 0x004B, // Unused
|
||||||
TEXT_ITEM_KEY_SMALL = 0x0060,
|
TEXT_ITEM_KEY_SMALL = 0x0060,
|
||||||
TEXT_ITEM_DUNGEON_MAP = 0x0066,
|
TEXT_ITEM_DUNGEON_MAP = 0x0066,
|
||||||
TEXT_CHEST_GAME_REAL_GAMBLER = 0x006E,
|
TEXT_CHEST_GAME_REAL_GAMBLER = 0x006E,
|
||||||
|
|
95
soh/soh/Enhancements/game-interactor/GIEventQueue.cpp
Normal file
95
soh/soh/Enhancements/game-interactor/GIEventQueue.cpp
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
#include "GameInteractor.h"
|
||||||
|
#include "soh/Enhancements/custom-item/CustomItem.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "variables.h"
|
||||||
|
#include "macros.h"
|
||||||
|
#include "functions.h"
|
||||||
|
|
||||||
|
extern SaveContext gSaveContext;
|
||||||
|
extern PlayState* gPlayState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessEvents() {
|
||||||
|
Player* player = GET_PLAYER(gPlayState);
|
||||||
|
|
||||||
|
// If the player has a message active, stop
|
||||||
|
if (gPlayState->msgCtx.msgMode != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the player is in a blocking cutscene, stop
|
||||||
|
if (Player_InBlockingCsMode(gPlayState, player)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If player is dead, stop
|
||||||
|
if (player->stateFlags1 & PLAYER_STATE1_DEAD) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is an event active, stop
|
||||||
|
const auto& currentEvent = GameInteractor::Instance->currentEvent;
|
||||||
|
if (auto e = std::get_if<GIEventNone>(¤tEvent)) {
|
||||||
|
// no-op
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are no events that need to happen, stop
|
||||||
|
if (GameInteractor::Instance->events.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameInteractor::Instance->currentEvent = GameInteractor::Instance->events.front();
|
||||||
|
const auto& nextEvent = GameInteractor::Instance->currentEvent;
|
||||||
|
|
||||||
|
if (auto e = std::get_if<GIEventGiveItem>(&nextEvent)) {
|
||||||
|
EnItem00* enItem00;
|
||||||
|
// If the player is climbing or in the air, deliver the item without a cutscene but freeze the player
|
||||||
|
if (!e->showGetItemCutscene ||
|
||||||
|
(player->stateFlags1 &
|
||||||
|
(PLAYER_STATE1_CHARGING_SPIN_ATTACK | PLAYER_STATE1_HANGING_OFF_LEDGE | PLAYER_STATE1_CLIMBING_LEDGE |
|
||||||
|
PLAYER_STATE1_JUMPING | PLAYER_STATE1_FREEFALL | PLAYER_STATE1_FIRST_PERSON |
|
||||||
|
PLAYER_STATE1_CLIMBING_LADDER | PLAYER_STATE1_IN_WATER)) ||
|
||||||
|
(Player_GetExplosiveHeld(player) > -1)) {
|
||||||
|
enItem00 = CustomItem::Spawn(
|
||||||
|
player->actor.world.pos.x, player->actor.world.pos.y, player->actor.world.pos.z, 0,
|
||||||
|
CustomItem::GIVE_OVERHEAD | CustomItem::HIDE_TILL_OVERHEAD | CustomItem::KEEP_ON_PLAYER, e->param,
|
||||||
|
[](Actor* actor, PlayState* play) {
|
||||||
|
Player* player = GET_PLAYER(gPlayState);
|
||||||
|
const auto& nextEvent = GameInteractor::Instance->currentEvent;
|
||||||
|
if (auto e = std::get_if<GIEventGiveItem>(&nextEvent)) {
|
||||||
|
e->giveItem(actor, play);
|
||||||
|
if (e->showGetItemCutscene) {
|
||||||
|
player->actor.freezeTimer = 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
e->drawItem);
|
||||||
|
} else {
|
||||||
|
enItem00 = CustomItem::Spawn(
|
||||||
|
player->actor.world.pos.x, player->actor.world.pos.y, player->actor.world.pos.z, 0,
|
||||||
|
CustomItem::GIVE_ITEM_CUTSCENE | CustomItem::HIDE_TILL_OVERHEAD | CustomItem::KEEP_ON_PLAYER, e->param,
|
||||||
|
e->giveItem, e->drawItem);
|
||||||
|
}
|
||||||
|
enItem00->actor.destroy = [](Actor* actor, PlayState* play) {
|
||||||
|
if (!(CUSTOM_ITEM_FLAGS & CustomItem::CALLED_ACTION)) {
|
||||||
|
// Event was not handled, requeue it
|
||||||
|
GameInteractor::Instance->events.push_back(GameInteractor::Instance->currentEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
GameInteractor::Instance->currentEvent = GIEventNone{};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
GameInteractor::Instance->events.erase(GameInteractor::Instance->events.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameInteractor::RegisterOwnHooks() {
|
||||||
|
// Cleanup all hooks at the start of each frame
|
||||||
|
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameStateMainStart>(
|
||||||
|
[]() { GameInteractor::Instance->RemoveAllQueuedHooks(); });
|
||||||
|
|
||||||
|
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnPlayerUpdate>(ProcessEvents);
|
||||||
|
}
|
|
@ -108,6 +108,22 @@ void GameInteractor_SetTriforceHuntCreditsWarpActive(uint8_t state);
|
||||||
#pragma message("Compiling without <source_location> support, the Hook Debugger will not be available")
|
#pragma message("Compiling without <source_location> support, the Hook Debugger will not be available")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct GIEventNone {};
|
||||||
|
|
||||||
|
struct GIEventGiveItem {
|
||||||
|
// Whether or not to show the get item cutscene. If true and the player is in the air, the
|
||||||
|
// player will instead be frozen for a few seconds. If this is true you _must_ call
|
||||||
|
// CustomMessage::SetActiveCustomMessage in the giveItem function otherwise you'll just see a blank message.
|
||||||
|
bool showGetItemCutscene;
|
||||||
|
// Arbitrary s16 that can be accessed from within the give/draw functions with CUSTOM_ITEM_PARAM
|
||||||
|
s16 param;
|
||||||
|
// These are run in the context of an item00 actor. This isn't super important but can be useful in some cases
|
||||||
|
ActorFunc giveItem;
|
||||||
|
ActorFunc drawItem;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::variant<GIEventNone, GIEventGiveItem> GIEvent;
|
||||||
|
|
||||||
typedef uint32_t HOOK_ID;
|
typedef uint32_t HOOK_ID;
|
||||||
|
|
||||||
enum HookType {
|
enum HookType {
|
||||||
|
@ -193,6 +209,8 @@ class GameInteractor {
|
||||||
public:
|
public:
|
||||||
static GameInteractor* Instance;
|
static GameInteractor* Instance;
|
||||||
|
|
||||||
|
void RegisterOwnHooks();
|
||||||
|
|
||||||
// Game State
|
// Game State
|
||||||
class State {
|
class State {
|
||||||
public:
|
public:
|
||||||
|
@ -225,6 +243,10 @@ class GameInteractor {
|
||||||
static GameInteractionEffectQueryResult ApplyEffect(GameInteractionEffectBase* effect);
|
static GameInteractionEffectQueryResult ApplyEffect(GameInteractionEffectBase* effect);
|
||||||
static GameInteractionEffectQueryResult RemoveEffect(RemovableGameInteractionEffect* effect);
|
static GameInteractionEffectQueryResult RemoveEffect(RemovableGameInteractionEffect* effect);
|
||||||
|
|
||||||
|
// EventQueue
|
||||||
|
std::vector<GIEvent> events = {};
|
||||||
|
GIEvent currentEvent = GIEventNone();
|
||||||
|
|
||||||
// Game Hooks
|
// Game Hooks
|
||||||
HOOK_ID nextHookId = 1;
|
HOOK_ID nextHookId = 1;
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ DEFINE_HOOK(OnSetDoAction, (uint16_t action));
|
||||||
DEFINE_HOOK(OnOcarinaSongAction, ());
|
DEFINE_HOOK(OnOcarinaSongAction, ());
|
||||||
DEFINE_HOOK(OnCuccoOrChickenHatch, ());
|
DEFINE_HOOK(OnCuccoOrChickenHatch, ());
|
||||||
DEFINE_HOOK(OnShopSlotChange, (uint8_t cursorIndex, int16_t price));
|
DEFINE_HOOK(OnShopSlotChange, (uint8_t cursorIndex, int16_t price));
|
||||||
|
DEFINE_HOOK(ShouldActorInit, (void* actor, bool* result));
|
||||||
DEFINE_HOOK(OnActorInit, (void* actor));
|
DEFINE_HOOK(OnActorInit, (void* actor));
|
||||||
DEFINE_HOOK(OnActorUpdate, (void* actor));
|
DEFINE_HOOK(OnActorUpdate, (void* actor));
|
||||||
DEFINE_HOOK(OnActorKill, (void* actor));
|
DEFINE_HOOK(OnActorKill, (void* actor));
|
||||||
|
@ -42,6 +43,7 @@ DEFINE_HOOK(OnPlayerProcessStick, ());
|
||||||
DEFINE_HOOK(OnPlayerShieldControl, (float_t * sp50, float_t* sp54));
|
DEFINE_HOOK(OnPlayerShieldControl, (float_t * sp50, float_t* sp54));
|
||||||
DEFINE_HOOK(OnPlayDestroy, ());
|
DEFINE_HOOK(OnPlayDestroy, ());
|
||||||
DEFINE_HOOK(OnPlayDrawEnd, ());
|
DEFINE_HOOK(OnPlayDrawEnd, ());
|
||||||
|
DEFINE_HOOK(OnOpenText, (u16 * textId, bool* loadFromMessageTable));
|
||||||
DEFINE_HOOK(OnVanillaBehavior, (GIVanillaBehavior flag, bool* result, va_list originalArgs));
|
DEFINE_HOOK(OnVanillaBehavior, (GIVanillaBehavior flag, bool* result, va_list originalArgs));
|
||||||
DEFINE_HOOK(OnSaveFile, (int32_t fileNum));
|
DEFINE_HOOK(OnSaveFile, (int32_t fileNum));
|
||||||
DEFINE_HOOK(OnLoadFile, (int32_t fileNum));
|
DEFINE_HOOK(OnLoadFile, (int32_t fileNum));
|
||||||
|
|
|
@ -19,9 +19,6 @@ void GameInteractor_ExecuteOnExitGame(int32_t fileNum) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameInteractor_ExecuteOnGameStateMainStart() {
|
void GameInteractor_ExecuteOnGameStateMainStart() {
|
||||||
// Cleanup all hooks at the start of each frame
|
|
||||||
GameInteractor::Instance->RemoveAllQueuedHooks();
|
|
||||||
|
|
||||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnGameStateMainStart>();
|
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnGameStateMainStart>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,6 +98,15 @@ void GameInteractor_ExecuteOnShopSlotChangeHooks(uint8_t cursorIndex, int16_t pr
|
||||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnShopSlotChange>(cursorIndex, price);
|
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnShopSlotChange>(cursorIndex, price);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GameInteractor_ShouldActorInit(void* actor) {
|
||||||
|
bool result = true;
|
||||||
|
GameInteractor::Instance->ExecuteHooks<GameInteractor::ShouldActorInit>(actor, &result);
|
||||||
|
GameInteractor::Instance->ExecuteHooksForID<GameInteractor::ShouldActorInit>(((Actor*)actor)->id, actor, &result);
|
||||||
|
GameInteractor::Instance->ExecuteHooksForPtr<GameInteractor::ShouldActorInit>((uintptr_t)actor, actor, &result);
|
||||||
|
GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::ShouldActorInit>(actor, &result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void GameInteractor_ExecuteOnActorInit(void* actor) {
|
void GameInteractor_ExecuteOnActorInit(void* actor) {
|
||||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnActorInit>(actor);
|
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnActorInit>(actor);
|
||||||
GameInteractor::Instance->ExecuteHooksForID<GameInteractor::OnActorInit>(((Actor*)actor)->id, actor);
|
GameInteractor::Instance->ExecuteHooksForID<GameInteractor::OnActorInit>(((Actor*)actor)->id, actor);
|
||||||
|
@ -183,6 +189,12 @@ void GameInteractor_ExecuteOnPlayDrawEnd() {
|
||||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayDrawEnd>();
|
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayDrawEnd>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameInteractor_ExecuteOnOpenText(u16* textId, bool* loadFromMessageTable) {
|
||||||
|
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnOpenText>(textId, loadFromMessageTable);
|
||||||
|
GameInteractor::Instance->ExecuteHooksForID<GameInteractor::OnOpenText>(*textId, textId, loadFromMessageTable);
|
||||||
|
GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::OnOpenText>(textId, loadFromMessageTable);
|
||||||
|
}
|
||||||
|
|
||||||
bool GameInteractor_Should(GIVanillaBehavior flag, u32 result, ...) {
|
bool GameInteractor_Should(GIVanillaBehavior flag, u32 result, ...) {
|
||||||
// Only the external function can use the Variadic Function syntax
|
// Only the external function can use the Variadic Function syntax
|
||||||
// To pass the va args to the next caller must be done using va_list and reading the args into it
|
// To pass the va args to the next caller must be done using va_list and reading the args into it
|
||||||
|
|
|
@ -28,6 +28,7 @@ void GameInteractor_ExecuteOnPlayerUpdate();
|
||||||
void GameInteractor_ExecuteOnSetDoAction(uint16_t action);
|
void GameInteractor_ExecuteOnSetDoAction(uint16_t action);
|
||||||
void GameInteractor_ExecuteOnOcarinaSongAction();
|
void GameInteractor_ExecuteOnOcarinaSongAction();
|
||||||
void GameInteractor_ExecuteOnCuccoOrChickenHatch();
|
void GameInteractor_ExecuteOnCuccoOrChickenHatch();
|
||||||
|
bool GameInteractor_ShouldActorInit(void* actor);
|
||||||
void GameInteractor_ExecuteOnActorInit(void* actor);
|
void GameInteractor_ExecuteOnActorInit(void* actor);
|
||||||
void GameInteractor_ExecuteOnActorUpdate(void* actor);
|
void GameInteractor_ExecuteOnActorUpdate(void* actor);
|
||||||
void GameInteractor_ExecuteOnActorKill(void* actor);
|
void GameInteractor_ExecuteOnActorKill(void* actor);
|
||||||
|
@ -45,6 +46,7 @@ void GameInteractor_ExecuteOnPlayerProcessStick();
|
||||||
void GameInteractor_ExecuteOnShopSlotChangeHooks(uint8_t cursorIndex, int16_t price);
|
void GameInteractor_ExecuteOnShopSlotChangeHooks(uint8_t cursorIndex, int16_t price);
|
||||||
void GameInteractor_ExecuteOnPlayDestroy();
|
void GameInteractor_ExecuteOnPlayDestroy();
|
||||||
void GameInteractor_ExecuteOnPlayDrawEnd();
|
void GameInteractor_ExecuteOnPlayDrawEnd();
|
||||||
|
void GameInteractor_ExecuteOnOpenText(u16* textId, bool* loadFromMessageTable);
|
||||||
bool GameInteractor_Should(GIVanillaBehavior flag, uint32_t result, ...);
|
bool GameInteractor_Should(GIVanillaBehavior flag, uint32_t result, ...);
|
||||||
|
|
||||||
// MARK: - Save Files
|
// MARK: - Save Files
|
||||||
|
|
|
@ -129,6 +129,7 @@ Sail* Sail::Instance;
|
||||||
|
|
||||||
#include "soh/config/ConfigUpdaters.h"
|
#include "soh/config/ConfigUpdaters.h"
|
||||||
#include "soh/ShipInit.hpp"
|
#include "soh/ShipInit.hpp"
|
||||||
|
#include "soh/Enhancements/custom-item/CustomItem.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "src/overlays/actors/ovl_En_Dns/z_en_dns.h"
|
#include "src/overlays/actors/ovl_En_Dns/z_en_dns.h"
|
||||||
|
@ -743,6 +744,7 @@ extern "C" void VanillaItemTable_Init() {
|
||||||
GET_ITEM(ITEM_BULLET_BAG_50, OBJECT_GI_DEKUPOUCH, GID_BULLET_BAG_50, 0x6C, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_NONE, GI_BULLET_BAG_50),
|
GET_ITEM(ITEM_BULLET_BAG_50, OBJECT_GI_DEKUPOUCH, GID_BULLET_BAG_50, 0x6C, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_NONE, GI_BULLET_BAG_50),
|
||||||
GET_ITEM_NONE,
|
GET_ITEM_NONE,
|
||||||
GET_ITEM_NONE,
|
GET_ITEM_NONE,
|
||||||
|
GET_ITEM(ITEM_SHIP, OBJECT_GI_DEKUPOUCH, GID_MAXIMUM,TEXT_CUSTOM_MESSAGE, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_JUNK, MOD_NONE, GI_SHIP),
|
||||||
GET_ITEM_NONE // GI_MAX - if you need to add to this table insert it before this entry.
|
GET_ITEM_NONE // GI_MAX - if you need to add to this table insert it before this entry.
|
||||||
// clang-format on
|
// clang-format on
|
||||||
};
|
};
|
||||||
|
@ -1245,6 +1247,9 @@ extern "C" void InitOTR() {
|
||||||
OTRExtScanner();
|
OTRExtScanner();
|
||||||
VanillaItemTable_Init();
|
VanillaItemTable_Init();
|
||||||
DebugConsole_Init();
|
DebugConsole_Init();
|
||||||
|
CustomMessageManager::Instance->RegisterHooks();
|
||||||
|
GameInteractor::Instance->RegisterOwnHooks();
|
||||||
|
CustomItem::RegisterHooks();
|
||||||
|
|
||||||
InitMods();
|
InitMods();
|
||||||
ActorDB::AddBuiltInCustomActors();
|
ActorDB::AddBuiltInCustomActors();
|
||||||
|
|
|
@ -1254,14 +1254,19 @@ void Actor_Init(Actor* actor, PlayState* play) {
|
||||||
ActorShape_Init(&actor->shape, 0.0f, NULL, 0.0f);
|
ActorShape_Init(&actor->shape, 0.0f, NULL, 0.0f);
|
||||||
if (Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) {
|
if (Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) {
|
||||||
Actor_SetObjectDependency(play, actor);
|
Actor_SetObjectDependency(play, actor);
|
||||||
actor->init(actor, play);
|
if (GameInteractor_ShouldActorInit(actor)) {
|
||||||
actor->init = NULL;
|
actor->init(actor, play);
|
||||||
|
actor->init = NULL;
|
||||||
|
|
||||||
GameInteractor_ExecuteOnActorInit(actor);
|
GameInteractor_ExecuteOnActorInit(actor);
|
||||||
|
|
||||||
// For enemy health bar we need to know the max health during init
|
// For enemy health bar we need to know the max health during init
|
||||||
if (actor->category == ACTORCAT_ENEMY) {
|
if (actor->category == ACTORCAT_ENEMY) {
|
||||||
actor->maximumHealth = actor->colChkInfo.health;
|
actor->maximumHealth = actor->colChkInfo.health;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
actor->init = NULL;
|
||||||
|
Actor_Kill(actor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2615,14 +2620,19 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) {
|
||||||
if (actor->init != NULL) {
|
if (actor->init != NULL) {
|
||||||
if (Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) {
|
if (Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) {
|
||||||
Actor_SetObjectDependency(play, actor);
|
Actor_SetObjectDependency(play, actor);
|
||||||
actor->init(actor, play);
|
if (GameInteractor_ShouldActorInit(actor)) {
|
||||||
actor->init = NULL;
|
actor->init(actor, play);
|
||||||
|
actor->init = NULL;
|
||||||
|
|
||||||
GameInteractor_ExecuteOnActorInit(actor);
|
GameInteractor_ExecuteOnActorInit(actor);
|
||||||
|
|
||||||
// For enemy health bar we need to know the max health during init
|
// For enemy health bar we need to know the max health during init
|
||||||
if (actor->category == ACTORCAT_ENEMY) {
|
if (actor->category == ACTORCAT_ENEMY) {
|
||||||
actor->maximumHealth = actor->colChkInfo.health;
|
actor->maximumHealth = actor->colChkInfo.health;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
actor->init = NULL;
|
||||||
|
Actor_Kill(actor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
actor = actor->next;
|
actor = actor->next;
|
||||||
|
|
|
@ -399,6 +399,10 @@ DrawItemTableEntry sDrawItemTable[] = {
|
||||||
* Calls the corresponding draw function for the given draw ID
|
* Calls the corresponding draw function for the given draw ID
|
||||||
*/
|
*/
|
||||||
void GetItem_Draw(PlayState* play, s16 drawId) {
|
void GetItem_Draw(PlayState* play, s16 drawId) {
|
||||||
|
// SoH [Enhancements] Prevent any UB here, GID_MAXIMUM useful for overriding GI draws
|
||||||
|
if (drawId < 0 || drawId >= GID_MAXIMUM)
|
||||||
|
return;
|
||||||
|
|
||||||
sDrawItemTable[drawId].drawFunc(play, drawId);
|
sDrawItemTable[drawId].drawFunc(play, drawId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2713,6 +2713,9 @@ void Message_OpenText(PlayState* play, u16 textId) {
|
||||||
Font* font = &msgCtx->font;
|
Font* font = &msgCtx->font;
|
||||||
s16 textBoxType;
|
s16 textBoxType;
|
||||||
|
|
||||||
|
bool loadFromMessageTable = true;
|
||||||
|
GameInteractor_ExecuteOnOpenText(&textId, &loadFromMessageTable);
|
||||||
|
|
||||||
sDisplayNextMessageAsEnglish = false;
|
sDisplayNextMessageAsEnglish = false;
|
||||||
|
|
||||||
if (msgCtx->msgMode == MSGMODE_NONE) {
|
if (msgCtx->msgMode == MSGMODE_NONE) {
|
||||||
|
@ -2783,7 +2786,9 @@ void Message_OpenText(PlayState* play, u16 textId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RANDOTODO: Use this for ice trap messages
|
// RANDOTODO: Use this for ice trap messages
|
||||||
if (CustomMessage_RetrieveIfExists(play)) {
|
if (!loadFromMessageTable) {
|
||||||
|
// no-op
|
||||||
|
} else if (CustomMessage_RetrieveIfExists(play)) {
|
||||||
osSyncPrintf("Found custom message");
|
osSyncPrintf("Found custom message");
|
||||||
if (gSaveContext.language == LANGUAGE_JPN) {
|
if (gSaveContext.language == LANGUAGE_JPN) {
|
||||||
sDisplayNextMessageAsEnglish = true;
|
sDisplayNextMessageAsEnglish = true;
|
||||||
|
|
|
@ -1872,6 +1872,10 @@ u8 Return_Item(u8 itemID, ModIndex modId, ItemID returnItem) {
|
||||||
* @return u8
|
* @return u8
|
||||||
*/
|
*/
|
||||||
u8 Item_Give(PlayState* play, u8 item) {
|
u8 Item_Give(PlayState* play, u8 item) {
|
||||||
|
// SoH [Enhancements] Ignore ITEM_SHIP, used for CustomItem
|
||||||
|
if (item == ITEM_SHIP)
|
||||||
|
return ITEM_NONE;
|
||||||
|
|
||||||
// prevents getting sticks without the bag in case something got missed
|
// prevents getting sticks without the bag in case something got missed
|
||||||
if (IS_RANDO && (item == ITEM_STICK || item == ITEM_STICKS_5 || item == ITEM_STICKS_10) &&
|
if (IS_RANDO && (item == ITEM_STICK || item == ITEM_STICKS_5 || item == ITEM_STICKS_10) &&
|
||||||
Randomizer_GetSettingValue(RSK_SHUFFLE_DEKU_STICK_BAG) && CUR_UPG_VALUE(UPG_STICKS) == 0) {
|
Randomizer_GetSettingValue(RSK_SHUFFLE_DEKU_STICK_BAG) && CUR_UPG_VALUE(UPG_STICKS) == 0) {
|
||||||
|
@ -2475,6 +2479,10 @@ u8 Item_CheckObtainability(u8 item) {
|
||||||
s16 slot = SLOT(item);
|
s16 slot = SLOT(item);
|
||||||
s32 temp;
|
s32 temp;
|
||||||
|
|
||||||
|
// SoH [Enhancements] Ignore ITEM_SHIP, used for CustomItem
|
||||||
|
if (item == ITEM_SHIP)
|
||||||
|
return ITEM_NONE;
|
||||||
|
|
||||||
if (item >= ITEM_STICKS_5) {
|
if (item >= ITEM_STICKS_5) {
|
||||||
slot = SLOT(sExtraItemBases[item - ITEM_STICKS_5]);
|
slot = SLOT(sExtraItemBases[item - ITEM_STICKS_5]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7339,7 +7339,7 @@ s32 Player_ActionHandler_2(Player* this, PlayState* play) {
|
||||||
// Only skip cutscenes for drops when they're items/consumables from bushes/rocks/enemies.
|
// Only skip cutscenes for drops when they're items/consumables from bushes/rocks/enemies.
|
||||||
uint8_t isDropToSkip =
|
uint8_t isDropToSkip =
|
||||||
(interactedActor->id == ACTOR_EN_ITEM00 && interactedActor->params != ITEM00_HEART_PIECE &&
|
(interactedActor->id == ACTOR_EN_ITEM00 && interactedActor->params != ITEM00_HEART_PIECE &&
|
||||||
interactedActor->params != ITEM00_SMALL_KEY &&
|
interactedActor->params != ITEM00_SMALL_KEY && interactedActor->params != ITEM00_NONE &&
|
||||||
interactedActor->params != ITEM00_SOH_GIVE_ITEM_ENTRY &&
|
interactedActor->params != ITEM00_SOH_GIVE_ITEM_ENTRY &&
|
||||||
interactedActor->params != ITEM00_SOH_GIVE_ITEM_ENTRY_GI) ||
|
interactedActor->params != ITEM00_SOH_GIVE_ITEM_ENTRY_GI) ||
|
||||||
interactedActor->id == ACTOR_EN_KAREBABA || interactedActor->id == ACTOR_EN_DEKUBABA;
|
interactedActor->id == ACTOR_EN_KAREBABA || interactedActor->id == ACTOR_EN_DEKUBABA;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue