Files
Shiip-of-Hakinian-Espanol/soh/soh/Enhancements/Holiday/Grimey.cpp
2024-12-17 04:12:28 -06:00

231 lines
7.4 KiB
C++

#include "Holiday.hpp"
#include "soh/Enhancements/randomizer/3drando/random.hpp"
#include "soh/frame_interpolation.h"
#include "soh_assets.h"
#include "overlays/actors/ovl_En_Nutsball/z_en_nutsball.h"
extern "C" {
#include "macros.h"
#include "functions.h"
#include "variables.h"
#include "objects/gameplay_field_keep/gameplay_field_keep.h"
extern PlayState* gPlayState;
void func_80ABBBA8(EnNutsball* nut, PlayState* play);
void EnNutsball_Draw(Actor* nut, PlayState* play);
}
#define AUTHOR "Grimey"
#define CVAR(v) "gHoliday." AUTHOR "." v
static bool spawningPenguins = false;
typedef enum {
PENGUIN_STATE_IDLE,
PENGUIN_STATE_WALK,
} PenguinState;
struct Penguin {
PenguinState state;
s16 timer;
s16 targetRot;
};
std::unordered_map<Actor*, Penguin> penguins;
void Penguin_Init(Actor* actor, PlayState* play) {
Penguin penguin;
penguin.state = PENGUIN_STATE_IDLE;
penguin.timer = 0;
actor->world.rot.y = penguin.targetRot = rand() % 0x10000;
penguins[actor] = penguin;
actor->gravity = -1.0f;
actor->flags &= ~ACTOR_FLAG_TARGETABLE;
}
void Penguin_Update(Actor* actor, PlayState* play) {
Penguin* penguin = &penguins[actor];
if (penguin->timer <= 0) {
if (penguin->state == PENGUIN_STATE_IDLE) {
penguin->state = (PenguinState)(rand() % 3);
penguin->timer = rand() % (20 * 10) + (20 * 3);
} else {
penguin->state = PENGUIN_STATE_IDLE;
penguin->timer = rand() % (20 * 10) + (20 * 3);
}
} else {
penguin->timer--;
}
if (rand() % 100 == 0) {
penguin->targetRot = rand() % 0x10000;
}
switch (penguin->state) {
case PENGUIN_STATE_IDLE:
break;
case PENGUIN_STATE_WALK:
actor->speedXZ = 0.5f;
break;
}
Math_SmoothStepToS(&actor->world.rot.y, penguin->targetRot, 1, 200, 0);
actor->shape.rot.y = actor->world.rot.y;
if (actor->speedXZ < 0.0f) {
actor->speedXZ = 0.0f;
}
Actor_MoveXZGravity(actor);
Actor_UpdateBgCheckInfo(play, actor, 10.0f, 10.0f, 0.0f, 0xFF);
}
void Penguin_Draw(Actor* actor, PlayState* play) {
OPEN_DISPS(play->state.gfxCtx);
Gfx_SetupDL_25Opa(play->state.gfxCtx);
Matrix_Scale(0.8f, 0.8f, 0.8f, MTXMODE_APPLY);
Matrix_Translate(0, 2000.0f, 0, MTXMODE_APPLY);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), G_MTX_MODELVIEW | G_MTX_LOAD);
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255);
gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gPenguinDL);
CLOSE_DISPS(play->state.gfxCtx);
}
void Penguin_Destroy(Actor* actor, PlayState* play) {
penguins.erase(actor);
}
static void OnConfigurationChanged() {
COND_HOOK(OnPlayerUpdate, CVarGetInteger(CVAR("Hailstorm"), 0), []() {
// Every frame has a 1/500 chance of spawning close hail
if (rand() % 500 == 0) {
int spawned = 0;
while (spawned < 1) {
Vec3f pos = GET_PLAYER(gPlayState)->actor.world.pos;
pos.x += (float)Random(0, 50) - 25.0f;
pos.z += (float)Random(0, 50) - 25.0f;
pos.y += 200.0f;
Actor* actor = Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_NUTSBALL, pos.x, pos.y, pos.z, 0, 0, 0, 0, false);
EnNutsball* nut = (EnNutsball*)actor;
nut->actor.draw = EnNutsball_Draw;
nut->actor.shape.rot.y = 0;
nut->timer = 0;
nut->actionFunc = func_80ABBBA8;
nut->actor.speedXZ = 0.0f;
nut->actor.gravity = -2.0f;
spawned++;
}
}
// Every frame has a 1/50 chance of spawning far hail
if (rand() % 50 == 0) {
int spawned = 0;
while (spawned < 1) {
Vec3f pos = GET_PLAYER(gPlayState)->actor.world.pos;
pos.x += (float)Random(0, 500) - 250.0f;
pos.z += (float)Random(0, 500) - 250.0f;
pos.y += 200.0f;
Actor* actor = Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_NUTSBALL, pos.x, pos.y, pos.z, 0, 0, 0, 0, false);
EnNutsball* nut = (EnNutsball*)actor;
nut->actor.draw = EnNutsball_Draw;
nut->actor.shape.rot.y = 0;
nut->timer = 0;
nut->actionFunc = func_80ABBBA8;
nut->actor.speedXZ = 0.0f;
nut->actor.gravity = -2.0f;
spawned++;
}
}
});
COND_HOOK(OnSceneSpawnActors, CVarGetInteger(CVAR("Penguins"), 0), []() {
penguins.clear();
if (gPlayState->sceneNum != SCENE_HYRULE_FIELD) {
return;
}
static Vec3f huddlePos;
static Vec3f spawnPos;
static f32 raycastResult;
static CollisionPoly poly;
spawningPenguins = true;
int huddlesSpawned = 0;
while (huddlesSpawned < 10) {
huddlePos.x = (float)(Random(
(gPlayState->sceneNum == SCENE_HYRULE_FIELD ? -10000 : -2700) + 10000,
(gPlayState->sceneNum == SCENE_HYRULE_FIELD ? 5000 : 2000) + 10000
) - (float)10000.0f);
huddlePos.y = 5000;
huddlePos.z = (float)(Random(
(gPlayState->sceneNum == SCENE_HYRULE_FIELD ? -1000 : -2000) + 10000,
(gPlayState->sceneNum == SCENE_HYRULE_FIELD ? 15000 : 2000) + 10000
) - (float)10000.0f);
if (BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &poly, &huddlePos) <= BGCHECK_Y_MIN) {
continue;
}
// 5-10
int huddleSize = rand() % 6 + 5;
int penguinsSpawned = 0;
while (penguinsSpawned < huddleSize) {
spawnPos.x = huddlePos.x + rand() % 100 - 50;
spawnPos.y = huddlePos.y;
spawnPos.z = huddlePos.z + rand() % 100 - 50;
raycastResult = BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &poly, &spawnPos);
if (raycastResult > BGCHECK_Y_MIN) {
Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_OE2, spawnPos.x, raycastResult, spawnPos.z, 0, 0, 0, 0, false);
penguinsSpawned++;
}
}
huddlesSpawned++;
}
spawningPenguins = false;
});
COND_ID_HOOK(ShouldActorInit, ACTOR_EN_OE2, CVarGetInteger(CVAR("Penguins"), 0), [](void* actorRef, bool* should) {
Actor* actor = (Actor*)actorRef;
if (spawningPenguins) {
actor->init = Penguin_Init;
actor->update = Penguin_Update;
actor->draw = Penguin_Draw;
actor->destroy = Penguin_Destroy;
}
});
}
static void DrawMenu() {
ImGui::SeparatorText(AUTHOR);
if (UIWidgets::EnhancementCheckbox("Penguins", CVAR("Penguins"))) {
OnConfigurationChanged();
}
UIWidgets::Tooltip("Penguins will spawn in huddles throughout hyrule");
if (UIWidgets::EnhancementCheckbox("Hailstorm", CVAR("Hailstorm"))) {
OnConfigurationChanged();
}
UIWidgets::Tooltip("Ever persistent hailstorm throughout hyrule");
}
static void RegisterMod() {
// #region Leave this alone unless you know what you are doing
OnConfigurationChanged();
// #endregion
// TODO: Anything you want to run once on startup
}
// TODO: Uncomment this line to enable the mod
static Holiday holiday(DrawMenu, RegisterMod);