Skip to content

Commit

Permalink
Add snowballs to kak & hyrule field
Browse files Browse the repository at this point in the history
  • Loading branch information
garrettjoecox committed Nov 27, 2023
1 parent 523eeed commit 32c3f0e
Show file tree
Hide file tree
Showing 5 changed files with 325 additions and 0 deletions.
17 changes: 17 additions & 0 deletions soh/soh/ActorDB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,22 @@ static ActorDBInit EnPartnerInit = {
};
extern "C" s16 gEnPartnerId;

#include "src/overlays/actors/ovl_En_Snowball/z_en_snowball.h"
static ActorDBInit EnSnowballInit = {
"En_Snowball",
"Snowball",
ACTORCAT_ITEMACTION,
(ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED | ACTOR_FLAG_DRAGGED_BY_HOOKSHOT | ACTOR_FLAG_CAN_PRESS_SWITCH),
OBJECT_GAMEPLAY_KEEP,
sizeof(EnSnowball),
(ActorFunc)EnSnowball_Init,
(ActorFunc)EnSnowball_Destroy,
(ActorFunc)EnSnowball_Update,
(ActorFunc)EnSnowball_Draw,
nullptr,
};
extern "C" s16 gEnSnowballId;

#include "src/overlays/actors/ovl_En_ChristmasTree/z_en_christmastree.h"
static ActorDBInit EnChristmasTreeInit = {
"En_ChristmasTree",
Expand Down Expand Up @@ -638,6 +654,7 @@ extern "C" s16 gEnChristmasDecoId;

void ActorDB::AddBuiltInCustomActors() {
gEnPartnerId = ActorDB::Instance->AddEntry(EnPartnerInit).entry.id;
gEnSnowballId = ActorDB::Instance->AddEntry(EnSnowballInit).entry.id;
gEnChristmasTreeId = ActorDB::Instance->AddEntry(EnChristmasTreeInit).entry.id;
gEnChristmasDecoId = ActorDB::Instance->AddEntry(EnChristmasDecoInit).entry.id;
}
Expand Down
36 changes: 36 additions & 0 deletions soh/soh/Enhancements/mods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ extern "C" {
#include "functions.h"
extern SaveContext gSaveContext;
extern PlayState* gPlayState;
extern "C" s16 gEnSnowballId;
extern void Overlay_DisplayText(float duration, const char* text);
uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum);
}
Expand Down Expand Up @@ -1059,6 +1060,40 @@ void RegisterRandomizedEnemySizes() {
});
}

static CollisionPoly snowballPoly;
static Vec3f snowballPos;
static f32 raycastResult;

void RegisterSnowballs() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneSpawnActors>([]() {
if (gPlayState->sceneNum != SCENE_HYRULE_FIELD && gPlayState->sceneNum != SCENE_KAKARIKO_VILLAGE) {
return;
}

int actorsSpawned = 0;

while (actorsSpawned < 30) {
snowballPos.x = (float)(Random(
(gPlayState->sceneNum == SCENE_HYRULE_FIELD ? -10000 : -2700) + 10000,
(gPlayState->sceneNum == SCENE_HYRULE_FIELD ? 5000 : 2000) + 10000
) - (float)10000.0f);
snowballPos.y = 5000;
snowballPos.z = (float)(Random(
(gPlayState->sceneNum == SCENE_HYRULE_FIELD ? -1000 : -2000) + 10000,
(gPlayState->sceneNum == SCENE_HYRULE_FIELD ? 15000 : 2000) + 10000
) - (float)10000.0f);

raycastResult = BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &snowballPoly, &snowballPos);

if (raycastResult > BGCHECK_Y_MIN) {
Actor_Spawn(&gPlayState->actorCtx, gPlayState, gEnSnowballId, snowballPos.x, raycastResult,
snowballPos.z, 0, 0, 0, gPlayState->sceneNum == SCENE_HYRULE_FIELD, 0);
actorsSpawned++;
}
}
});
}

void InitMods() {
RegisterTTS();
RegisterInfiniteMoney();
Expand Down Expand Up @@ -1088,5 +1123,6 @@ void InitMods() {
RegisterAltTrapTypes();
RegisterRandomizerSheikSpawn();
RegisterRandomizedEnemySizes();
RegisterSnowballs();
NameTag_RegisterHooks();
}
1 change: 1 addition & 0 deletions soh/src/code/z_play.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ PlayState* gPlayState;
s16 firstInit = 0;

s16 gEnPartnerId;
s16 gEnSnowballId;
s16 gEnChristmasTreeId;
s16 gEnChristmasDecoId;

Expand Down
240 changes: 240 additions & 0 deletions soh/src/overlays/actors/ovl_En_Snowball/z_en_snowball.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
/*
* File: z_en_snowball.c
* Overlay: ovl_En_Snowball
* Description: Rollable Snowball
*/

#include "z_en_snowball.h"
#include "objects/gameplay_keep/gameplay_keep.h"
#include "objects/object_goroiwa/object_goroiwa.h"
#include "soh_assets.h"

#define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED

void EnSnowball_Init(Actor* thisx, PlayState* play);
void EnSnowball_Destroy(Actor* thisx, PlayState* play);
void EnSnowball_Update(Actor* thisx, PlayState* play);
void EnSnowball_Draw(Actor* thisx, PlayState* play);

static ColliderJntSphElementInit sJntSphElementsInit[] = {
{
{
ELEMTYPE_UNK0,
{ 0x20000000, 0x00, 0x04 },
{ 0x00000000, 0x00, 0x00 },
TOUCH_ON | TOUCH_SFX_NORMAL,
BUMP_NONE,
OCELEM_ON,
},
{ 0, { { 0, 0, 0 }, 14 }, 100 },
},
};

static ColliderJntSphInit sJntSphInit = {
{
COLTYPE_NONE,
AT_ON | AT_TYPE_ENEMY,
AC_NONE,
OC1_ON | OC1_TYPE_ALL,
OC2_TYPE_2,
COLSHAPE_JNTSPH,
},
1,
sJntSphElementsInit,
};

static CollisionCheckInfoInit sColChkInfoInit = { 0, 3, 15, MASS_HEAVY };

void EnSnowball_UpdateCollider(EnSnowball* this) {
Sphere16* worldSphere = &this->collider.elements[0].dim.worldSphere;

worldSphere->center.x = this->actor.world.pos.x;
worldSphere->center.y = this->actor.world.pos.y + (this->actor.scale.x * 500.0f);
worldSphere->center.z = this->actor.world.pos.z;
worldSphere->radius = (this->actor.scale.x * 500.0f);
}

void EnSnowball_InitCollider(EnSnowball* this, PlayState* play) {
Collider_InitJntSph(play, &this->collider);
Collider_SetJntSph(play, &this->collider, &this->actor, &sJntSphInit, this->colliderItems);
EnSnowball_UpdateCollider(this);
this->collider.elements[0].dim.worldSphere.radius = (this->actor.scale.x * 500.0f);
}

static InitChainEntry sInitChain[] = {
ICHAIN_F32_DIV1000(gravity, -860, ICHAIN_CONTINUE), ICHAIN_F32_DIV1000(minVelocityY, -15000, ICHAIN_CONTINUE),
ICHAIN_VEC3F_DIV1000(scale, 5, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneForward, 1500, ICHAIN_CONTINUE),
ICHAIN_F32(uncullZoneScale, 150, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneDownward, 1500, ICHAIN_STOP),
};

void EnSnowball_Init(Actor* thisx, PlayState* play) {
EnSnowball* this = (EnSnowball*)thisx;

Actor_ProcessInitChain(&this->actor, sInitChain);
EnSnowball_InitCollider(this, play);
CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit);
ActorShape_Init(&this->actor.shape, 595.0f, ActorShadow_DrawCircle, 9.4f);
this->actor.shape.shadowAlpha = 200;

if (thisx->params == 1) {
this->actor.speedXZ += 5.0f;
this->actor.world.rot.y = Rand_ZeroFloat(65536.0f);
}
}

void EnSnowball_Destroy(Actor* thisx, PlayState* play) {
EnSnowball* this = (EnSnowball*)thisx;

Collider_DestroyJntSph(play, &this->collider);
}

void EnSnowball_Update(Actor* thisx, PlayState* play) {
EnSnowball* this = (EnSnowball*)thisx;
Actor* player = GET_PLAYER(play);

// Kill the actor if it falls too far
if (thisx->world.pos.y < -10000.0f) {
Actor_Kill(thisx);
return;
}

u8 meanBoulder = thisx->params == 1 && this->actor.scale.x > 0.1f;

// Check if the player is close enough to start rolling
if (this->actor.xzDistToPlayer < MAX(20.0f, this->actor.scale.x * 600.0f) && !meanBoulder) {

// Calculate direction from actor to player
f32 dx = player->world.pos.x - thisx->world.pos.x;
f32 dz = player->world.pos.z - thisx->world.pos.z;
s16 angleToPlayer = Math_Atan2S(dz, dx);

// Set the actor's rotation and speed
thisx->world.rot.y = angleToPlayer + 0x8000;
this->actor.speedXZ = MAX(5.0f, this->actor.speedXZ);
}

if (this->collider.base.atFlags & AT_HIT) {
this->collider.base.atFlags &= ~AT_HIT;
// Calculate direction from actor to player
f32 dx = player->world.pos.x - thisx->world.pos.x;
f32 dz = player->world.pos.z - thisx->world.pos.z;
s16 angleToPlayer = Math_Atan2S(dz, dx);

// Set the actor's rotation and speed
thisx->world.rot.y = angleToPlayer + 0x8000;

func_8002F6D4(play, &this->actor, 2.0f, this->actor.yawTowardsPlayer, 0.0f, 0);
Player_PlaySfx(&GET_PLAYER(play)->actor, NA_SE_PL_BODY_HIT);
}

// Slow down the actor and increase it's scale
if (this->actor.speedXZ > 0.0f) {
CollisionPoly snowballPoly;
u8 goingUp = this->actor.world.pos.y - this->prevY > 0.001f;
u8 goingDown = this->actor.world.pos.y - this->prevY < -0.001f;

// friction
if (thisx->params != 1) {
this->actor.speedXZ -= 0.1f;
}

if (goingDown) {
// Increase the speed if going down hill
f32 speed = (this->prevY - this->actor.world.pos.y) * 0.15f;
this->actor.speedXZ += MIN(speed, 0.5f);
} else if (goingUp) {
// Reduce the speed if going up hill
this->actor.speedXZ -= (this->actor.world.pos.y - this->prevY) * 0.1f;
}

if (goingUp || goingDown) {
// Check if going straight, one degree right, or one degree left will result in steeper slope
// Check straight
Vec3f snowballPos = this->actor.world.pos;
snowballPos.y += 100.0f;
snowballPos.x += Math_SinS(this->actor.world.rot.y) * 1.0f;
snowballPos.z += Math_CosS(this->actor.world.rot.y) * 1.0f;
float straightSlope = BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &snowballPoly, &snowballPos);

// Check one degree right
snowballPos = this->actor.world.pos;
snowballPos.y += 100.0f;
snowballPos.x += Math_SinS(this->actor.world.rot.y + 0x100) * 1.0f;
snowballPos.z += Math_CosS(this->actor.world.rot.y + 0x100) * 1.0f;
float rightSlope = BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &snowballPoly, &snowballPos);

// Check one degree left
snowballPos = this->actor.world.pos;
snowballPos.y += 100.0f;
snowballPos.x += Math_SinS(this->actor.world.rot.y - 0x100) * 1.0f;
snowballPos.z += Math_CosS(this->actor.world.rot.y - 0x100) * 1.0f;
float leftSlope = BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &snowballPoly, &snowballPos);

if (straightSlope > rightSlope || straightSlope > leftSlope) {
if (rightSlope < leftSlope) {
this->actor.world.rot.y += 0x100;
} else {
this->actor.world.rot.y -= 0x100;
}
}
}

// Check if the actor is colliding with a wall and bounce off
if (thisx->bgCheckFlags & 8) {
if (ABS((s16)(thisx->wallYaw - thisx->world.rot.y)) > 0x4000) {
thisx->world.rot.y = ((thisx->wallYaw - thisx->world.rot.y) + thisx->wallYaw) - 0x8000;
}
if (thisx->params != 1) {
thisx->speedXZ *= 0.7f;
}
thisx->bgCheckFlags &= ~8;
if (this->actor.speedXZ > 5.0f) {
Audio_PlayActorSound2(thisx, NA_SE_EV_BOMB_BOUND);
}
}

Actor_SetScale(&this->actor, MIN(0.15f, this->actor.scale.x + (this->actor.speedXZ * 0.00001f)));
}

if (this->actor.speedXZ < 0.0f) {
this->actor.speedXZ = 0.0f;
}

// Based on speed and scale, rotate the snowball
// The larger the snowball, the slower it rotates
this->sRot += (this->actor.speedXZ * 15.0f) / this->actor.scale.x;

// record the actor's position
this->prevY = this->actor.world.pos.y;

// Process movement (moves foward based on speed and rotation)
Actor_MoveForward(thisx);

// Prevent actor from going through the ground or walls
Actor_UpdateBgCheckInfo(play, &this->actor, MAX(10.0f, this->actor.scale.x * 250.0f), MAX(10.0f, this->actor.scale.x * 500.0f), 0.0f, 0xFF);

EnSnowball_UpdateCollider(this);
// Add collision checks if the actor is a mean boulder
if (meanBoulder) {
CollisionCheck_SetAT(play, &play->colChkCtx, &this->collider.base);
CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base);
}
}

void EnSnowball_Draw(Actor* thisx, PlayState* play) {
EnSnowball* this = (EnSnowball*)thisx;

OPEN_DISPS(play->state.gfxCtx);

Gfx_SetupDL_25Opa(play->state.gfxCtx);

Matrix_RotateZYX(this->sRot, thisx->world.rot.y, 0, MTXMODE_APPLY);

Matrix_Translate(0.0f, 7600.0f, -148.649f, MTXMODE_APPLY);
Matrix_Scale(4.844f, 4.844f, 4.844f, MTXMODE_APPLY);
gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);

gSPDisplayList(POLY_OPA_DISP++, gLinkAdultPompomDL);

CLOSE_DISPS(play->state.gfxCtx);
}
31 changes: 31 additions & 0 deletions soh/src/overlays/actors/ovl_En_Snowball/z_en_snowball.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef Z_EN_SNOWBALL_H
#define Z_EN_SNOWBALL_H

#include <libultraship/libultra.h>
#include "global.h"

struct EnSnowball;

typedef void (*EnSnowballActionFunc)(struct EnSnowball*, PlayState*);

typedef struct EnSnowball {
Actor actor;

ColliderJntSph collider;
ColliderJntSphElement colliderItems[1];
s16 sRot;
f32 prevY;
} EnSnowball;

#ifdef __cplusplus
extern "C" {
#endif
void EnSnowball_Init(Actor* thisx, PlayState* play);
void EnSnowball_Destroy(Actor* thisx, PlayState* play);
void EnSnowball_Update(Actor* thisx, PlayState* play);
void EnSnowball_Draw(Actor* thisx, PlayState* play);
#ifdef __cplusplus
}
#endif

#endif

0 comments on commit 32c3f0e

Please sign in to comment.