Add custom collectible thing

This commit is contained in:
Garrett Cox 2024-12-04 20:26:11 -06:00
commit 05539fee03
7 changed files with 256 additions and 3 deletions

View file

@ -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
@ -455,9 +456,10 @@ typedef enum {
/* 0x79 */ GI_NUT_UPGRADE_30, /* 0x79 */ GI_NUT_UPGRADE_30,
/* 0x7A */ GI_NUT_UPGRADE_40, /* 0x7A */ GI_NUT_UPGRADE_40,
/* 0x7B */ GI_BULLET_BAG_50, /* 0x7B */ GI_BULLET_BAG_50,
/* 0x7C */ GI_ICE_TRAP, // freezes link when opened from a chest /* 0x7C */ GI_SHIP, // SOH [Enhancement] Added to enable custom item gives
/* 0x7D */ GI_TEXT_0, // no model appears over Link, shows text id 0 (pocket egg) /* 0x7D */ GI_ICE_TRAP, // freezes link when opened from a chest
/* 0x84 */ GI_MAX /* 0x7E */ GI_TEXT_0, // no model appears over Link, shows text id 0 (pocket egg)
/* 0x7F */ GI_MAX
} GetItemID; } GetItemID;
typedef enum { typedef enum {

View file

@ -0,0 +1,209 @@
#include "CustomCollectible.h"
#include <libultraship/libultraship.h>
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/randomizer/3drando/random.hpp"
#include "soh/frame_interpolation.h"
#include "soh/Enhancements/custom-message/CustomMessageManager.h"
extern "C" {
#include "z64actor.h"
#include "functions.h"
#include "variables.h"
#include "macros.h"
#include "objects/object_md/object_md.h"
extern PlayState* gPlayState;
}
EnItem00* CustomCollectible::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, 0);
EnItem00* enItem00 = (EnItem00*)actor;
if (actionFunc != NULL) {
enItem00->actionFunc = (EnItem00ActionFunc)actionFunc;
}
if (drawFunc != NULL) {
actor->draw = drawFunc;
}
return enItem00;
}
void CustomCollectible_Init(Actor* actor, PlayState* play) {
EnItem00* enItem00 = (EnItem00*)actor;
if (CUSTOM_ITEM_FLAGS & CustomCollectible::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 & CustomCollectible::HIDE_TILL_OVERHEAD) {
Actor_SetScale(actor, 0.0f);
} else {
Actor_SetScale(actor, 0.015f);
}
if (CUSTOM_ITEM_FLAGS & CustomCollectible::KEEP_ON_PLAYER) {
Math_Vec3f_Copy(&actor->world.pos, &GET_PLAYER(play)->actor.world.pos);
}
if (CUSTOM_ITEM_FLAGS & CustomCollectible::TOSS_ON_SPAWN) {
actor->velocity.y = 8.0f;
actor->speedXZ = 2.0f;
actor->gravity = -1.4f;
actor->world.rot.y = Rand_ZeroOne() * 40000.0f;
}
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 CustomCollectible_Draw(Actor* actor, PlayState* play) {
Matrix_Scale(30.0f, 30.0f, 30.0f, MTXMODE_APPLY);
GetItem_Draw(play, CUSTOM_ITEM_PARAM);
}
void CustomCollectible_Update(Actor* actor, PlayState* play) {
EnItem00* enItem00 = (EnItem00*)actor;
Player* player = GET_PLAYER(play);
if (!(CUSTOM_ITEM_FLAGS & CustomCollectible::STOP_SPINNING)) {
actor->shape.rot.y += 960;
}
if (CUSTOM_ITEM_FLAGS & CustomCollectible::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 & CustomCollectible::HIDE_TILL_OVERHEAD) {
Actor_SetScale(actor, 0.0f);
}
if (CUSTOM_ITEM_FLAGS & CustomCollectible::KEEP_ON_PLAYER) {
actor->gravity = 0.0f;
Math_Vec3f_Copy(&actor->world.pos, &GET_PLAYER(play)->actor.world.pos);
}
if (CUSTOM_ITEM_FLAGS & CustomCollectible::KILL_ON_TOUCH) {
// Pretty self explanatory, if the player is within range, kill the actor and call the action function
if ((actor->xzDistToPlayer <= 50.0f) && (fabsf(actor->yDistToPlayer) <= fabsf(20.0f))) {
if (enItem00->actionFunc != NULL) {
enItem00->actionFunc(enItem00, play);
CUSTOM_ITEM_FLAGS |= CustomCollectible::CALLED_ACTION;
}
Actor_Kill(actor);
}
} else if (CUSTOM_ITEM_FLAGS & CustomCollectible::GIVE_OVERHEAD) {
// If the item hasn't been picked up (unk_15A == -1) and the player is within range
if (enItem00->unk_15A == -1 && (actor->xzDistToPlayer <= 50.0f) &&
(fabsf(actor->yDistToPlayer) <= fabsf(20.0f))) {
// Fire the action function
if (enItem00->actionFunc != NULL) {
enItem00->actionFunc(enItem00, play);
CUSTOM_ITEM_FLAGS |= CustomCollectible::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 |= CustomCollectible::STOP_BOBBING;
CUSTOM_ITEM_FLAGS |= CustomCollectible::KEEP_ON_PLAYER;
}
// 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 = 45.0f;
// TODO: Check for adult?
// 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 & CustomCollectible::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, 20.0f);
} else {
if (enItem00->unk_15A == -1) {
CUSTOM_ITEM_FLAGS |= CustomCollectible::STOP_BOBBING;
CUSTOM_ITEM_FLAGS |= CustomCollectible::KEEP_ON_PLAYER;
CUSTOM_ITEM_FLAGS |= CustomCollectible::HIDE_TILL_OVERHEAD;
}
// 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 |= CustomCollectible::CALLED_ACTION;
}
// Override the bobbing animation to be a fixed height
actor->shape.yOffset = 900.0f;
Actor_SetScale(actor, 0.007f);
f32 height = 45.0f;
// TODO: Check for adult?
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_MoveForward(actor);
Actor_UpdateBgCheckInfo(play, actor, 20.0f, 15.0f, 15.0f, 0x1D);
}
if (actor->bgCheckFlags & 0x0003) {
actor->speedXZ = 0.0f;
}
Collider_UpdateCylinder(actor, &enItem00->collider);
CollisionCheck_SetAC(play, &play->colChkCtx, &enItem00->collider.base);
}
void CustomCollectible::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 = CustomCollectible_Init;
actor->update = CustomCollectible_Update;
actor->draw = CustomCollectible_Draw;
actor->destroy = NULL;
// 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;
});
}

View file

@ -0,0 +1,24 @@
extern "C" {
#include "z64actor.h"
}
#define CUSTOM_ITEM_FLAGS (actor->home.rot.x)
#define CUSTOM_ITEM_PARAM (actor->home.rot.z)
namespace CustomCollectible {
enum CustomCollectibleFlags : 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
};
void RegisterHooks();
EnItem00* Spawn(f32 posX, f32 posY, f32 posZ, s16 rot, s16 flags, s16 params, ActorFunc actionFunc = NULL,
ActorFunc drawFunc = NULL);
}; // namespace CustomCollectible

View file

@ -84,6 +84,7 @@ Sail* Sail::Instance;
#include "Enhancements/mods.h" #include "Enhancements/mods.h"
#include "Enhancements/game-interactor/GameInteractor.h" #include "Enhancements/game-interactor/GameInteractor.h"
#include "Enhancements/randomizer/draw.h" #include "Enhancements/randomizer/draw.h"
#include "Enhancements/custom-collectible/CustomCollectible.h"
#include <libultraship/libultraship.h> #include <libultraship/libultraship.h>
// Resource Types/Factories // Resource Types/Factories
@ -703,6 +704,7 @@ extern "C" void VanillaItemTable_Init() {
GET_ITEM(ITEM_NUT_UPGRADE_30, OBJECT_GI_NUTS, GID_NUTS, 0xA7, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_LESSER, MOD_NONE, GI_NUT_UPGRADE_30), GET_ITEM(ITEM_NUT_UPGRADE_30, OBJECT_GI_NUTS, GID_NUTS, 0xA7, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_LESSER, MOD_NONE, GI_NUT_UPGRADE_30),
GET_ITEM(ITEM_NUT_UPGRADE_40, OBJECT_GI_NUTS, GID_NUTS, 0xA8, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_LESSER, MOD_NONE, GI_NUT_UPGRADE_40), GET_ITEM(ITEM_NUT_UPGRADE_40, OBJECT_GI_NUTS, GID_NUTS, 0xA8, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_LESSER, MOD_NONE, GI_NUT_UPGRADE_40),
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(ITEM_SHIP, OBJECT_UNSET_16E, GID_MAXIMUM, 0x00, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_NONE, GI_SHIP),
GET_ITEM_NONE, GET_ITEM_NONE,
GET_ITEM_NONE, GET_ITEM_NONE,
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.
@ -1173,6 +1175,7 @@ extern "C" void InitOTR() {
DebugConsole_Init(); DebugConsole_Init();
InitMods(); InitMods();
CustomCollectible::RegisterHooks();
ActorDB::AddBuiltInCustomActors(); ActorDB::AddBuiltInCustomActors();
// #region SOH [Randomizer] TODO: Remove these and refactor spoiler file handling for randomizer // #region SOH [Randomizer] TODO: Remove these and refactor spoiler file handling for randomizer
CVarClear(CVAR_GENERAL("RandomizerNewFileDropped")); CVarClear(CVAR_GENERAL("RandomizerNewFileDropped"));

View file

@ -399,6 +399,9 @@ 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) {
if (drawId < 0 || drawId >= GID_MAXIMUM) {
return;
}
sDrawItemTable[drawId].drawFunc(play, drawId); sDrawItemTable[drawId].drawFunc(play, drawId);
} }

View file

@ -1891,6 +1891,12 @@ 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) {
// TODO: Add ShouldItemGive
// if (!GameInteractor_ShouldItemGive(item) || item == ITEM_SHIP) {
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 ( if (
IS_RANDO && IS_RANDO &&
@ -2486,6 +2492,11 @@ u8 Item_CheckObtainability(u8 item) {
s16 slot = SLOT(item); s16 slot = SLOT(item);
s32 temp; s32 temp;
// SOH [Enhancements] Added to enable custom item gives
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]);
} }

View file

@ -7312,6 +7312,7 @@ s32 Player_ActionHandler_2(Player* this, PlayState* play) {
interactedActor->id == ACTOR_EN_ITEM00 && interactedActor->id == ACTOR_EN_ITEM00 &&
interactedActor->params != ITEM00_HEART_PIECE && 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
) || ) ||