mirror of
https://github.com/n64decomp/mk64
synced 2026-08-10 10:53:54 -04:00
1005 lines
38 KiB
C
1005 lines
38 KiB
C
#include <ultra64.h>
|
|
#include <macros.h>
|
|
#include <defines.h>
|
|
#include <actor_types.h>
|
|
#include "code_800029B0.h"
|
|
#include "math_util.h"
|
|
#include "memory.h"
|
|
#include "path.h"
|
|
#include "cpu_vehicles_camera_path.h"
|
|
#include "menu_items.h"
|
|
#include "collision.h"
|
|
#include "actors.h"
|
|
#include "actors_extended.h"
|
|
#include "audio/external.h"
|
|
#include "update_objects.h"
|
|
#include "effects.h"
|
|
#include "sounds.h"
|
|
|
|
void copy_collision(Collision* src, Collision* dest) {
|
|
dest->unk30 = src->unk30;
|
|
dest->unk32 = src->unk32;
|
|
dest->unk34 = src->unk34;
|
|
dest->meshIndexYX = src->meshIndexYX;
|
|
dest->meshIndexZY = src->meshIndexZY;
|
|
dest->meshIndexZX = src->meshIndexZX;
|
|
dest->surfaceDistance[0] = src->surfaceDistance[0];
|
|
dest->surfaceDistance[1] = src->surfaceDistance[1];
|
|
dest->surfaceDistance[2] = src->surfaceDistance[2];
|
|
|
|
vec3f_copy_return(dest->unk48, src->unk48);
|
|
vec3f_copy_return(dest->unk54, src->unk54);
|
|
vec3f_copy_return(dest->orientationVector, src->orientationVector);
|
|
}
|
|
|
|
void triple_shell_actor_collide_with_player(struct ShellActor* shell, s32 shellType) {
|
|
TripleShellParent* parent = (TripleShellParent*) &gActorList[shell->parentIndex];
|
|
|
|
parent->shellsAvailable--;
|
|
|
|
switch ((s16) shell->shellId) {
|
|
case 0:
|
|
parent->shellIndices[0] = -1.0f;
|
|
break;
|
|
case 1:
|
|
parent->shellIndices[1] = -1.0f;
|
|
break;
|
|
case 2:
|
|
parent->shellIndices[2] = -1.0f;
|
|
break;
|
|
}
|
|
|
|
shell->flags = 0x8000; // bitflag
|
|
shell->rotAngle = 0;
|
|
shell->velocity[1] = 3.0f;
|
|
shell->someTimer = 60;
|
|
|
|
switch (shellType) {
|
|
case ACTOR_GREEN_SHELL:
|
|
shell->state = GREEN_SHELL_HIT_A_RACER;
|
|
break;
|
|
case ACTOR_RED_SHELL:
|
|
shell->state = DESTROYED_SHELL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Sets velocities for a banana, used when a racer runs into
|
|
// a banana bunch.
|
|
void func_802B039C(struct BananaActor* banana) {
|
|
banana->state = DROPPED_BANANA;
|
|
banana->unk_04 = 0x00B4;
|
|
banana->velocity[0] = ((f32) (random_int(0x00C8) - 0x64) * 0.015);
|
|
banana->velocity[1] = ((f32) random_int(0x00C8)) * 0.015;
|
|
banana->velocity[2] = ((f32) (random_int(0x00C8) - 0x64) * 0.015);
|
|
}
|
|
|
|
void func_802B0464(s16 bananaIndex) {
|
|
struct BananaActor* banana;
|
|
|
|
if (bananaIndex != -1) {
|
|
banana = (struct BananaActor*) &gActorList[bananaIndex];
|
|
func_802B039C(banana);
|
|
func_802B0464(banana->youngerIndex);
|
|
}
|
|
}
|
|
|
|
void func_802B04E8(UNUSED struct BananaActor* arg0, s16 bananaIndex) {
|
|
struct BananaActor* banana;
|
|
|
|
if (bananaIndex != -1) {
|
|
banana = (struct BananaActor*) &gActorList[bananaIndex];
|
|
func_802B039C(banana);
|
|
func_802B04E8(banana, banana->elderIndex);
|
|
}
|
|
}
|
|
|
|
// Handle a banana being ran over while it is still part of a banana bunch
|
|
void destroy_banana_in_banana_bunch(struct BananaActor* banana) {
|
|
struct BananaBunchParent* temp_v0_2;
|
|
|
|
func_802B0464(banana->youngerIndex);
|
|
func_802B04E8(banana, banana->elderIndex);
|
|
if ((gPlayers[banana->playerId].type & PLAYER_HUMAN) != 0) {
|
|
func_800C9060(banana->playerId, SOUND_ARG_LOAD(0x19, 0x01, 0x90, 0x53));
|
|
}
|
|
banana->flags = -0x8000;
|
|
banana->unk_04 = 0x003C;
|
|
banana->state = DESTROYED_BANANA;
|
|
banana->velocity[1] = 3.0f;
|
|
temp_v0_2 = (struct BananaBunchParent*) &gActorList[banana->parentIndex];
|
|
temp_v0_2->bananaIndices[0] = -1;
|
|
temp_v0_2->bananaIndices[1] = -1;
|
|
temp_v0_2->bananaIndices[2] = -1;
|
|
temp_v0_2->bananaIndices[3] = -1;
|
|
temp_v0_2->bananaIndices[4] = -1;
|
|
}
|
|
|
|
// Drop a banana from a banana bunch?
|
|
void drop_banana_in_banana_bunch(struct BananaBunchParent* banana_bunch) {
|
|
s16 elderIndex;
|
|
struct BananaActor* banana;
|
|
|
|
banana_bunch->bananasAvailable -= 1;
|
|
if (banana_bunch->bananaIndices[4] != -1) {
|
|
banana = (struct BananaActor*) &gActorList[banana_bunch->bananaIndices[4]];
|
|
banana_bunch->bananaIndices[4] = -1;
|
|
} else if (banana_bunch->bananaIndices[3] != -1) {
|
|
banana = (struct BananaActor*) &gActorList[banana_bunch->bananaIndices[3]];
|
|
banana_bunch->bananaIndices[3] = -1;
|
|
} else if (banana_bunch->bananaIndices[2] != -1) {
|
|
banana = (struct BananaActor*) &gActorList[banana_bunch->bananaIndices[2]];
|
|
banana_bunch->bananaIndices[2] = -1;
|
|
} else if (banana_bunch->bananaIndices[1] != -1) {
|
|
banana = (struct BananaActor*) &gActorList[banana_bunch->bananaIndices[1]];
|
|
banana_bunch->bananaIndices[1] = -1;
|
|
} else if (banana_bunch->bananaIndices[0] != -1) {
|
|
banana = (struct BananaActor*) &gActorList[banana_bunch->bananaIndices[0]];
|
|
banana_bunch->bananaIndices[0] = -1;
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
banana->state = DROPPED_BANANA;
|
|
banana->unk_04 = 0x00B4;
|
|
banana->velocity[0] = 0.0f;
|
|
banana->velocity[1] = 1.5f;
|
|
banana->velocity[2] = 0.0f;
|
|
elderIndex = banana->elderIndex;
|
|
if (elderIndex != -1) {
|
|
((struct BananaActor*) &gActorList[elderIndex])->youngerIndex = -1;
|
|
}
|
|
}
|
|
|
|
// Drop a banana from a banana bunch and throw it forward?
|
|
void func_802B0788(s16 rawStickY, struct BananaBunchParent* banana_bunch, Player* player) {
|
|
Vec3f velocity;
|
|
struct BananaActor* banana;
|
|
struct BananaActor* elderBanana;
|
|
f32 var_f0;
|
|
f32 var_f12;
|
|
|
|
banana_bunch->bananasAvailable -= 1;
|
|
if (banana_bunch->bananaIndices[4] != -1) {
|
|
banana = (struct BananaActor*) &gActorList[banana_bunch->bananaIndices[4]];
|
|
banana_bunch->bananaIndices[4] = -1;
|
|
} else if (banana_bunch->bananaIndices[3] != -1) {
|
|
banana = (struct BananaActor*) &gActorList[banana_bunch->bananaIndices[3]];
|
|
banana_bunch->bananaIndices[3] = -1;
|
|
} else if (banana_bunch->bananaIndices[2] != -1) {
|
|
banana = (struct BananaActor*) &gActorList[banana_bunch->bananaIndices[2]];
|
|
banana_bunch->bananaIndices[2] = -1;
|
|
} else if (banana_bunch->bananaIndices[1] != -1) {
|
|
banana = (struct BananaActor*) &gActorList[banana_bunch->bananaIndices[1]];
|
|
banana_bunch->bananaIndices[1] = -1;
|
|
} else if (banana_bunch->bananaIndices[0] != -1) {
|
|
banana = (struct BananaActor*) &gActorList[banana_bunch->bananaIndices[0]];
|
|
banana_bunch->bananaIndices[0] = -1;
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
banana->state = DROPPED_BANANA;
|
|
banana->unk_04 = 0x001E;
|
|
if (banana->elderIndex != -1) {
|
|
elderBanana = (struct BananaActor*) &gActorList[banana->elderIndex];
|
|
elderBanana->youngerIndex = -1;
|
|
}
|
|
if (player->speed < 2.0f) {
|
|
var_f0 = ((rawStickY - 30.0f) / 20.0f) + 1.5f;
|
|
var_f12 = 4.0f;
|
|
} else {
|
|
var_f0 = ((rawStickY - 30.0f) / 20.0f) + 1.5f;
|
|
var_f12 = (player->speed * 0.75f) + 4.5f + var_f0;
|
|
}
|
|
vec3f_set(velocity, 0.0f, var_f0, var_f12);
|
|
vec3f_rotate_y(velocity, player->rotation[1] + player->unk_0C0);
|
|
banana->velocity[0] = velocity[0];
|
|
banana->velocity[1] = velocity[1];
|
|
banana->velocity[2] = velocity[2];
|
|
}
|
|
|
|
s32 func_802B09C0(s16 bananaId) {
|
|
struct BananaActor* banana;
|
|
if (bananaId == -1) {
|
|
return 0;
|
|
}
|
|
banana = (struct BananaActor*) &gActorList[bananaId];
|
|
if (banana->state == FIRST_BANANA_BUNCH_BANANA) {
|
|
return 1;
|
|
}
|
|
if (banana->state == BANANA_BUNCH_BANANA) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void update_actor_banana_bunch(struct BananaBunchParent* banana_bunch) {
|
|
UNUSED s32 pad[2];
|
|
Player* owner;
|
|
struct Controller* controller;
|
|
s32 someCount;
|
|
|
|
owner = &gPlayers[banana_bunch->playerId];
|
|
switch (banana_bunch->state) {
|
|
case 0:
|
|
func_802B2914(banana_bunch, owner, 0);
|
|
banana_bunch->unk_04 = 4;
|
|
banana_bunch->state = 1;
|
|
banana_bunch->bananasAvailable = 1;
|
|
break;
|
|
case 1:
|
|
banana_bunch->unk_04 -= 1;
|
|
if (banana_bunch->unk_04 == 0) {
|
|
func_802B2914(banana_bunch, owner, 1);
|
|
banana_bunch->unk_04 = 4;
|
|
banana_bunch->state = 2;
|
|
banana_bunch->bananasAvailable += 1;
|
|
}
|
|
break;
|
|
case 2:
|
|
banana_bunch->unk_04 -= 1;
|
|
if (banana_bunch->unk_04 == 0) {
|
|
func_802B2914(banana_bunch, owner, 2);
|
|
banana_bunch->unk_04 = 4;
|
|
banana_bunch->state = 3;
|
|
banana_bunch->bananasAvailable += 1;
|
|
}
|
|
break;
|
|
case 3:
|
|
banana_bunch->unk_04 -= 1;
|
|
if (banana_bunch->unk_04 == 0) {
|
|
func_802B2914(banana_bunch, owner, 3);
|
|
banana_bunch->unk_04 = 4;
|
|
banana_bunch->state = 4;
|
|
banana_bunch->bananasAvailable += 1;
|
|
}
|
|
break;
|
|
case 4:
|
|
banana_bunch->unk_04 -= 1;
|
|
if (banana_bunch->unk_04 == 0) {
|
|
func_802B2914(banana_bunch, owner, 4);
|
|
banana_bunch->unk_04 = 4;
|
|
banana_bunch->state = 5;
|
|
banana_bunch->bananasAvailable += 1;
|
|
}
|
|
break;
|
|
case 5:
|
|
banana_bunch->state = 6;
|
|
// Unnecessary type-casting done here purely to help with understanding.
|
|
// We're setting the ->flags of BananaActors, not plain Actors.
|
|
((struct BananaActor*) &gActorList[banana_bunch->bananaIndices[0]])->flags |= 0x5000;
|
|
((struct BananaActor*) &gActorList[banana_bunch->bananaIndices[1]])->flags |= 0x5000;
|
|
((struct BananaActor*) &gActorList[banana_bunch->bananaIndices[2]])->flags |= 0x5000;
|
|
((struct BananaActor*) &gActorList[banana_bunch->bananaIndices[3]])->flags |= 0x5000;
|
|
((struct BananaActor*) &gActorList[banana_bunch->bananaIndices[4]])->flags |= 0x5000;
|
|
break;
|
|
case 6:
|
|
someCount = 0;
|
|
if (func_802B09C0(banana_bunch->bananaIndices[0]) == 1) {
|
|
someCount = 1;
|
|
}
|
|
if (func_802B09C0(banana_bunch->bananaIndices[1]) == 1) {
|
|
someCount += 1;
|
|
}
|
|
if (func_802B09C0(banana_bunch->bananaIndices[2]) == 1) {
|
|
someCount += 1;
|
|
}
|
|
if (func_802B09C0(banana_bunch->bananaIndices[3]) == 1) {
|
|
someCount += 1;
|
|
}
|
|
if (func_802B09C0(banana_bunch->bananaIndices[4]) == 1) {
|
|
someCount += 1;
|
|
}
|
|
if (someCount == 0) {
|
|
destroy_actor((struct Actor*) banana_bunch);
|
|
owner->triggers &= ~DRAG_ITEM_EFFECT;
|
|
} else if ((owner->type & PLAYER_HUMAN) != 0) {
|
|
controller = &gControllers[banana_bunch->playerId];
|
|
if ((controller->buttonPressed & Z_TRIG) != 0) {
|
|
controller->buttonPressed &= ~Z_TRIG;
|
|
func_800C9060(owner - gPlayerOne, SOUND_ARG_LOAD(0x19, 0x00, 0x80, 0x12));
|
|
if ((controller->rawStickY >= 0x1F) &&
|
|
((controller->rawStickX < 0x28) && (controller->rawStickX >= -0x27))) {
|
|
func_802B0788(controller->rawStickY, banana_bunch, owner);
|
|
} else {
|
|
drop_banana_in_banana_bunch(banana_bunch);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool is_shell_exist(s16 arg0) {
|
|
struct ShellActor* actor;
|
|
if (arg0 < 0) {
|
|
return false;
|
|
}
|
|
actor = (struct ShellActor*) &gActorList[arg0];
|
|
if (actor->type == ACTOR_GREEN_SHELL) {
|
|
if (actor->state == TRIPLE_GREEN_SHELL) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
if (actor->state == TRIPLE_RED_SHELL) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void update_actor_triple_shell(TripleShellParent* parent, s16 shellType) {
|
|
UNUSED s32 pad[2];
|
|
s16 playerId;
|
|
UNUSED s32 pad2;
|
|
struct ShellActor* shell;
|
|
Vec3f someVelocity;
|
|
UNUSED s32 pad3;
|
|
s16 shellCount;
|
|
u16 someRotAngle;
|
|
Player* player;
|
|
|
|
playerId = parent->playerId;
|
|
player = &gPlayers[playerId];
|
|
parent->rotAngle += parent->rotVelocity;
|
|
someRotAngle = parent->rotAngle;
|
|
switch (parent->state) {
|
|
case SPAWN_FIRST_SHELL:
|
|
if (init_triple_shell(parent, &gPlayers[playerId], shellType, 0U) != -1) {
|
|
func_800C9060(playerId, SOUND_ARG_LOAD(0x19, 0x00, 0x80, 0x12));
|
|
parent->shellsAvailable += 1;
|
|
}
|
|
parent->state = SPAWN_SECOND_SHELL;
|
|
break;
|
|
case SPAWN_SECOND_SHELL:
|
|
if (parent->rotVelocity > 0) {
|
|
if (someRotAngle > DEGREES(300)) {
|
|
if (init_triple_shell(parent, &gPlayers[playerId], shellType, 1U) != -1) {
|
|
func_800C9060(playerId, SOUND_ARG_LOAD(0x19, 0x00, 0x80, 0x12));
|
|
parent->shellsAvailable += 1;
|
|
}
|
|
parent->state = SPAWN_THIRD_SHELL;
|
|
}
|
|
} else {
|
|
if (someRotAngle < DEGREES(60)) {
|
|
if (init_triple_shell(parent, &gPlayers[playerId], shellType, 1U) != -1) {
|
|
func_800C9060(playerId, SOUND_ARG_LOAD(0x19, 0x00, 0x80, 0x12));
|
|
parent->shellsAvailable += 1;
|
|
}
|
|
parent->state = SPAWN_THIRD_SHELL;
|
|
}
|
|
}
|
|
break;
|
|
case SPAWN_THIRD_SHELL:
|
|
if (parent->rotVelocity > 0) {
|
|
if ((someRotAngle > DEGREES(60)) && (someRotAngle < DEGREES(70))) {
|
|
if (init_triple_shell(parent, &gPlayers[playerId], shellType, 2U) != -1) {
|
|
func_800C9060(playerId, SOUND_ARG_LOAD(0x19, 0x00, 0x80, 0x12));
|
|
parent->shellsAvailable += 1;
|
|
}
|
|
parent->state = 3;
|
|
}
|
|
} else if ((someRotAngle < DEGREES(300)) && (someRotAngle > DEGREES(290))) {
|
|
if (init_triple_shell(parent, &gPlayers[playerId], shellType, 2U) != -1) {
|
|
func_800C9060(playerId, SOUND_ARG_LOAD(0x19, 0x00, 0x80, 0x12));
|
|
parent->shellsAvailable += 1;
|
|
}
|
|
parent->state = 3;
|
|
}
|
|
break;
|
|
case 3:
|
|
parent->state = 4;
|
|
shell = (struct ShellActor*) &gActorList[(s16) parent->shellIndices[0]];
|
|
shell->flags |= 0x4000;
|
|
shell = (struct ShellActor*) &gActorList[(s16) parent->shellIndices[1]];
|
|
shell->flags |= 0x4000;
|
|
shell = (struct ShellActor*) &gActorList[(s16) parent->shellIndices[2]];
|
|
shell->flags |= 0x4000;
|
|
break;
|
|
case 4:
|
|
shellCount = 0;
|
|
if (is_shell_exist(parent->shellIndices[0]) == 1) {
|
|
shellCount = 1;
|
|
} else {
|
|
parent->shellIndices[0] = -1.0f;
|
|
}
|
|
if (is_shell_exist(parent->shellIndices[1]) == 1) {
|
|
shellCount++;
|
|
} else {
|
|
parent->shellIndices[1] = -1.0f;
|
|
}
|
|
if (is_shell_exist(parent->shellIndices[2]) == 1) {
|
|
shellCount++;
|
|
} else {
|
|
parent->shellIndices[2] = -1.0f;
|
|
}
|
|
if (shellCount == 0) {
|
|
destroy_actor((struct Actor*) parent);
|
|
break;
|
|
}
|
|
if ((gControllers[parent->playerId].buttonPressed & Z_TRIG) != 0) {
|
|
parent->unk_08 += 1.0f;
|
|
gControllers[parent->playerId].buttonPressed &= ~Z_TRIG;
|
|
}
|
|
if (parent->unk_08 > 0.0f) {
|
|
if (parent->shellIndices[0] > 0.0f) {
|
|
shell = (struct ShellActor*) &gActorList[(s16) parent->shellIndices[0]];
|
|
/**
|
|
* Forces shell 1 to only fire inside a cone of -5 to +5 degrees in-front of the player.
|
|
* However, always evaluates to true, so the feature does not appear during gameplay.
|
|
* If enabled, pressing Z would not immediately release the shell. Instead,
|
|
* it waits until it's inside the cone. (See shell 2 and 3 farther down for same issue)
|
|
*/
|
|
if ((shell->rotAngle < DEGREES(5)) || (shell->rotAngle > -DEGREES(5))) {
|
|
someVelocity[0] = 0;
|
|
someVelocity[1] = 0;
|
|
someVelocity[2] = 8;
|
|
vec3f_rotate_y(someVelocity, player->rotation[1] + player->unk_0C0);
|
|
shell->velocity[0] = someVelocity[0];
|
|
shell->velocity[1] = someVelocity[1];
|
|
shell->velocity[2] = someVelocity[2];
|
|
shell->state = MOVING_SHELL;
|
|
shell->someTimer = 0x001E;
|
|
func_800C9060(parent->playerId, SOUND_ARG_LOAD(0x19, 0x00, 0x80, 0x04));
|
|
func_800C90F4(parent->playerId,
|
|
(player->characterId * 0x10) + SOUND_ARG_LOAD(0x29, 0x00, 0x80, 0x00));
|
|
if (parent->type == ACTOR_TRIPLE_RED_SHELL) {
|
|
add_red_shell_in_unexpired_actor_list(parent->shellIndices[0]);
|
|
} else {
|
|
add_green_shell_in_unexpired_actor_list(parent->shellIndices[0]);
|
|
}
|
|
parent->shellIndices[0] = -1.0f;
|
|
parent->shellsAvailable -= 1;
|
|
parent->unk_08 -= 1.0f;
|
|
break;
|
|
}
|
|
}
|
|
if (parent->shellIndices[1] > 0.0f) {
|
|
shell = (struct ShellActor*) &gActorList[(s16) parent->shellIndices[1]];
|
|
/**
|
|
* Forces shell 2 to fire inside a cone of 5 to 14.95 degrees (always evaluates to true, thus the feature is skipped)
|
|
* Unclear why it is 14.95 degrees instead of 15. Perhaps just a typo when entering the value as a s16.
|
|
*/
|
|
if ((shell->rotAngle < DEGREES(14.95)) || (shell->rotAngle > DEGREES(5))) {
|
|
someVelocity[0] = 0;
|
|
someVelocity[1] = 0;
|
|
someVelocity[2] = 8;
|
|
vec3f_rotate_y(someVelocity, player->rotation[1] + player->unk_0C0);
|
|
shell->velocity[0] = someVelocity[0];
|
|
shell->velocity[1] = someVelocity[1];
|
|
shell->velocity[2] = someVelocity[2];
|
|
shell->state = MOVING_SHELL;
|
|
shell->someTimer = 0x001E;
|
|
func_800C90F4(parent->playerId,
|
|
(player->characterId * 0x10) + SOUND_ARG_LOAD(0x29, 0x00, 0x80, 0x00));
|
|
func_800C9060(parent->playerId, SOUND_ARG_LOAD(0x19, 0x00, 0x80, 0x04));
|
|
if (parent->type == ACTOR_TRIPLE_RED_SHELL) {
|
|
add_red_shell_in_unexpired_actor_list(parent->shellIndices[1]);
|
|
} else {
|
|
add_green_shell_in_unexpired_actor_list(parent->shellIndices[1]);
|
|
}
|
|
parent->shellIndices[1] = -1.0f;
|
|
parent->shellsAvailable -= 1;
|
|
parent->unk_08 -= 1.0f;
|
|
break;
|
|
}
|
|
}
|
|
if (parent->shellIndices[2] > 0.0f) {
|
|
shell = (struct ShellActor*) &gActorList[(s16) parent->shellIndices[2]];
|
|
/**
|
|
* Forces shell 3 to fire inside a cone of -5 to -10 degrees.
|
|
* However, after testing, 10 should probably be replaced with 14.95 because
|
|
* the shell does multiple loops before it fires.
|
|
* Always evaluates to true, thus this feature is skipped.
|
|
*/
|
|
if ((shell->rotAngle < -DEGREES(5)) || (shell->rotAngle > -DEGREES(10))) {
|
|
someVelocity[0] = 0;
|
|
someVelocity[1] = 0;
|
|
someVelocity[2] = 8;
|
|
vec3f_rotate_y(someVelocity, player->rotation[1] + player->unk_0C0);
|
|
shell->velocity[0] = someVelocity[0];
|
|
shell->velocity[1] = someVelocity[1];
|
|
shell->velocity[2] = someVelocity[2];
|
|
shell->state = MOVING_SHELL;
|
|
shell->someTimer = 0x001E;
|
|
func_800C9060(parent->playerId, SOUND_ARG_LOAD(0x19, 0x00, 0x80, 0x04));
|
|
func_800C90F4(parent->playerId,
|
|
(player->characterId * 0x10) + SOUND_ARG_LOAD(0x29, 0x00, 0x80, 0x00));
|
|
if (parent->type == ACTOR_TRIPLE_RED_SHELL) {
|
|
add_red_shell_in_unexpired_actor_list(parent->shellIndices[2]);
|
|
} else {
|
|
add_green_shell_in_unexpired_actor_list(parent->shellIndices[2]);
|
|
}
|
|
parent->shellIndices[2] = -1.0f;
|
|
parent->shellsAvailable -= 1;
|
|
parent->unk_08 -= 1.0f;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
s32 use_banana_bunch_item(Player* player) {
|
|
Vec3f startingVelocity = { 0.0f, 0.0f, 0.0f };
|
|
Vec3s startingRot = { 0, 0, 0 };
|
|
Vec3f startingPos = { 0.0f, 0.0f, 0.0f };
|
|
s16 actorIndex;
|
|
struct BananaBunchParent* bananaBunch;
|
|
|
|
// this have a animation of spawning so see update_actor_banana_bunch
|
|
actorIndex = add_actor_to_empty_slot(startingPos, startingRot, startingVelocity, ACTOR_BANANA_BUNCH);
|
|
if (actorIndex < 0) {
|
|
return actorIndex;
|
|
}
|
|
bananaBunch = (struct BananaBunchParent*) &gActorList[actorIndex];
|
|
bananaBunch->state = 0;
|
|
bananaBunch->playerId = player - gPlayerOne;
|
|
player->triggers |= DRAG_ITEM_EFFECT;
|
|
return actorIndex;
|
|
}
|
|
|
|
s32 use_triple_shell_item(Player* player, s16 tripleShellType) {
|
|
Vec3f startingVelocity = { 0.0f, 0.0f, 0.0f };
|
|
Vec3s startingRot = { 0, 0, 0 };
|
|
Vec3f startingPos = { 0.0f, 0.0f, 0.0f };
|
|
s16 actorIndex;
|
|
TripleShellParent* parent;
|
|
|
|
// this have a animation of spawning so see update_actor_triple_shell
|
|
actorIndex = add_actor_to_empty_slot(startingPos, startingRot, startingVelocity, tripleShellType);
|
|
if (actorIndex < 0) {
|
|
return actorIndex;
|
|
}
|
|
parent = (TripleShellParent*) &gActorList[actorIndex];
|
|
parent->state = 0;
|
|
parent->rotVelocity = DEGREES(8);
|
|
parent->rotAngle = - DEGREES(180);
|
|
parent->playerId = player - gPlayerOne;
|
|
parent->shellsAvailable = 0;
|
|
parent->unk_08 = 0.0f;
|
|
return actorIndex;
|
|
}
|
|
|
|
s32 init_triple_shell(TripleShellParent* parent, Player* player, s16 shellType, u16 shellId) {
|
|
Vec3f startingVelocity = { 0.0f, 0.0f, 0.0f };
|
|
Vec3s startingRot = { 0, 0, 0 };
|
|
Vec3f startingPos;
|
|
s16 actorIndex;
|
|
struct ShellActor* shell;
|
|
|
|
startingPos[0] = 0.0f;
|
|
startingPos[1] = -player->boundingBoxSize;
|
|
startingPos[2] = player->boundingBoxSize - 4.0f;
|
|
mtxf_transform_vec3f_mat3(startingPos, player->orientationMatrix);
|
|
startingPos[0] += player->pos[0];
|
|
startingPos[1] += player->pos[1];
|
|
startingPos[2] += player->pos[2];
|
|
|
|
actorIndex = add_actor_to_empty_slot(startingPos, startingRot, startingVelocity, shellType);
|
|
if (actorIndex < 0) {
|
|
parent->shellIndices[shellId] = -1.0f;
|
|
return -1;
|
|
}
|
|
|
|
shell = (struct ShellActor*) &gActorList[actorIndex];
|
|
startingPos[0] = player->pos[0];
|
|
startingPos[1] = player->pos[1];
|
|
startingPos[2] = player->pos[2];
|
|
actor_terrain_collision(&shell->unk30, shell->boundingBoxSize + 1.0f, shell->pos[0], shell->pos[1], shell->pos[2],
|
|
startingPos[0], startingPos[1], startingPos[2]);
|
|
func_802B4E30((struct Actor*) shell);
|
|
shell->flags = 0x9000;
|
|
switch (shellType) {
|
|
case ACTOR_GREEN_SHELL:
|
|
shell->state = TRIPLE_GREEN_SHELL;
|
|
break;
|
|
case ACTOR_RED_SHELL:
|
|
shell->state = TRIPLE_RED_SHELL;
|
|
break;
|
|
}
|
|
shell->rotVelocity = 0;
|
|
shell->rotAngle = -DEGREES(180);
|
|
shell->playerId = player - gPlayerOne;
|
|
shell->parentIndex = (struct Actor*) parent - gActorList;
|
|
shell->shellId = shellId;
|
|
parent->shellIndices[shellId] = (struct Actor*) shell - gActorList;
|
|
return 1;
|
|
}
|
|
|
|
s32 use_green_shell_item(Player* player) {
|
|
Vec3f startingVelocity = { 0.0f, 0.0f, 0.0f };
|
|
Vec3s startingRot = { 0, 0, 0 };
|
|
Vec3f startingPos;
|
|
s16 actorIndex;
|
|
struct ShellActor* shell;
|
|
|
|
// place behind player
|
|
startingPos[0] = 0.0f;
|
|
startingPos[1] = -player->boundingBoxSize;
|
|
startingPos[2] = player->boundingBoxSize - 4.0f;
|
|
|
|
// rotate to match player orientation
|
|
mtxf_transform_vec3f_mat3(startingPos, player->orientationMatrix);
|
|
|
|
// move to player position
|
|
startingPos[0] += player->pos[0];
|
|
startingPos[1] += player->pos[1];
|
|
startingPos[2] += player->pos[2];
|
|
|
|
// spawn the shell
|
|
actorIndex = add_actor_to_empty_slot(startingPos, startingRot, startingVelocity, ACTOR_GREEN_SHELL);
|
|
if (actorIndex < 0) {
|
|
return actorIndex;
|
|
}
|
|
|
|
shell = (struct ShellActor*) &gActorList[actorIndex];
|
|
startingPos[0] = player->pos[0];
|
|
startingPos[1] = player->pos[1];
|
|
startingPos[2] = player->pos[2];
|
|
actor_terrain_collision(&shell->unk30, shell->boundingBoxSize + 1.0f, shell->pos[0], shell->pos[1], shell->pos[2],
|
|
startingPos[0], startingPos[1], startingPos[2]);
|
|
func_802B4E30((struct Actor*) shell);
|
|
shell->state = HELD_SHELL;
|
|
shell->rotVelocity = 0;
|
|
shell->rotAngle = -DEGREES(180);
|
|
shell->playerId = player - gPlayerOne;
|
|
return actorIndex;
|
|
}
|
|
|
|
s32 use_red_shell_item(Player* player) {
|
|
Vec3f startingVelocity = { 0.0f, 0.0f, 0.0f };
|
|
Vec3s startingRot = { 0, 0, 0 };
|
|
Vec3f startingPos;
|
|
s16 actorIndex;
|
|
struct ShellActor* shell;
|
|
|
|
// place behind player
|
|
startingPos[0] = 0.0f;
|
|
startingPos[1] = -player->boundingBoxSize;
|
|
startingPos[2] = player->boundingBoxSize - 4.0f;
|
|
|
|
// rotate to match player orientation
|
|
mtxf_transform_vec3f_mat3(startingPos, player->orientationMatrix);
|
|
|
|
// move to player position
|
|
startingPos[0] += player->pos[0];
|
|
startingPos[1] += player->pos[1];
|
|
startingPos[2] += player->pos[2];
|
|
|
|
// spawn the shell
|
|
actorIndex = add_actor_to_empty_slot(startingPos, startingRot, startingVelocity, ACTOR_RED_SHELL);
|
|
if (actorIndex < 0) {
|
|
return actorIndex;
|
|
}
|
|
|
|
shell = (struct ShellActor*) &gActorList[actorIndex];
|
|
startingPos[0] = player->pos[0];
|
|
startingPos[1] = player->pos[1];
|
|
startingPos[2] = player->pos[2];
|
|
actor_terrain_collision(&shell->unk30, shell->boundingBoxSize + 1.0f, shell->pos[0], shell->pos[1], shell->pos[2],
|
|
startingPos[0], startingPos[1], startingPos[2]);
|
|
func_802B4E30((struct Actor*) shell);
|
|
shell->state = HELD_SHELL;
|
|
shell->rotVelocity = 0;
|
|
shell->rotAngle = player->rotation[1] - DEGREES(180);
|
|
shell->playerId = player - gPlayerOne;
|
|
return actorIndex;
|
|
}
|
|
|
|
// Interestingly blue shells start their life as a red shell,
|
|
// and then just change the type from red to blue shell
|
|
void use_blue_shell_item(Player* player) {
|
|
gActorList[use_red_shell_item(player)].type = ACTOR_BLUE_SPINY_SHELL;
|
|
}
|
|
|
|
#include "actors/banana/update.inc.c"
|
|
|
|
// This function could reasonably be called "spawn_bananas_for_banana_bunch" or similar
|
|
void func_802B2914(struct BananaBunchParent* banana_bunch, Player* player, s16 bananaId) {
|
|
s16 actorIndex;
|
|
Vec3f startingVelocity;
|
|
Vec3s startingRot;
|
|
Vec3f startingPos;
|
|
UNUSED s32 pad;
|
|
UNUSED s32 pad2;
|
|
struct BananaActor* newBanana;
|
|
struct BananaActor* tempBanana;
|
|
|
|
startingPos[0] = 0.0f;
|
|
startingPos[1] = -player->boundingBoxSize;
|
|
startingPos[2] = -(player->boundingBoxSize + 4.0f);
|
|
mtxf_transform_vec3f_mat3(startingPos, player->orientationMatrix);
|
|
startingPos[0] += player->pos[0];
|
|
startingPos[1] += player->pos[1];
|
|
startingPos[2] += player->pos[2];
|
|
startingVelocity[0] = player->velocity[0];
|
|
startingVelocity[1] = player->velocity[1];
|
|
startingVelocity[2] = player->velocity[2];
|
|
startingRot[0] = 0;
|
|
startingRot[1] = 0;
|
|
startingRot[2] = 0;
|
|
actorIndex = add_actor_to_empty_slot(startingPos, startingRot, startingVelocity, ACTOR_BANANA);
|
|
if (actorIndex >= 0) {
|
|
newBanana = (struct BananaActor*) &gActorList[actorIndex];
|
|
startingPos[0] = player->pos[0];
|
|
startingPos[1] = player->pos[1];
|
|
startingPos[2] = player->pos[2];
|
|
actor_terrain_collision(&newBanana->unk30, newBanana->boundingBoxSize + 1.0f, newBanana->pos[0],
|
|
newBanana->pos[1], newBanana->pos[2], startingPos[0], startingPos[1], startingPos[2]);
|
|
func_802B4E30((struct Actor*) newBanana);
|
|
newBanana->flags = 0x9000;
|
|
newBanana->playerId = player - gPlayerOne;
|
|
newBanana->parentIndex = (struct Actor*) banana_bunch - gActorList;
|
|
newBanana->youngerIndex = -1;
|
|
newBanana->unk_04 = 0x0014;
|
|
newBanana->bananaId = bananaId;
|
|
switch (bananaId) {
|
|
case 0:
|
|
newBanana->state = 2;
|
|
banana_bunch->bananaIndices[0] = actorIndex;
|
|
newBanana->elderIndex = -1;
|
|
break;
|
|
case 1:
|
|
newBanana->state = 3;
|
|
banana_bunch->bananaIndices[1] = actorIndex;
|
|
newBanana->elderIndex = banana_bunch->bananaIndices[0];
|
|
tempBanana = (struct BananaActor*) &gActorList[banana_bunch->bananaIndices[0]];
|
|
tempBanana->youngerIndex = actorIndex;
|
|
break;
|
|
case 2:
|
|
newBanana->state = 3;
|
|
banana_bunch->bananaIndices[2] = actorIndex;
|
|
newBanana->elderIndex = banana_bunch->bananaIndices[1];
|
|
tempBanana = (struct BananaActor*) &gActorList[banana_bunch->bananaIndices[1]];
|
|
tempBanana->youngerIndex = actorIndex;
|
|
break;
|
|
case 3:
|
|
newBanana->state = 3;
|
|
banana_bunch->bananaIndices[3] = actorIndex;
|
|
newBanana->elderIndex = banana_bunch->bananaIndices[2];
|
|
tempBanana = (struct BananaActor*) &gActorList[banana_bunch->bananaIndices[2]];
|
|
tempBanana->youngerIndex = actorIndex;
|
|
break;
|
|
case 4:
|
|
newBanana->state = 3;
|
|
banana_bunch->bananaIndices[4] = actorIndex;
|
|
newBanana->elderIndex = banana_bunch->bananaIndices[3];
|
|
tempBanana = (struct BananaActor*) &gActorList[banana_bunch->bananaIndices[3]];
|
|
tempBanana->youngerIndex = actorIndex;
|
|
break;
|
|
}
|
|
if ((player->type & PLAYER_HUMAN) != 0) {
|
|
func_800C9060(player - gPlayerOne, SOUND_ARG_LOAD(0x19, 0x00, 0x80, 0x12));
|
|
}
|
|
}
|
|
}
|
|
|
|
s32 use_fake_itembox_item(Player* player) {
|
|
struct FakeItemBox* itemBox;
|
|
UNUSED s32 pad[5];
|
|
s16 actorIndex;
|
|
Vec3f startingVelocity;
|
|
Vec3s startingRot;
|
|
Vec3f startingPos;
|
|
|
|
// place behind player
|
|
startingPos[0] = 0.0f;
|
|
startingPos[1] = -player->boundingBoxSize;
|
|
startingPos[2] = -(player->boundingBoxSize + 4.0f);
|
|
|
|
// rotate to match player orientation
|
|
mtxf_transform_vec3f_mat3(startingPos, player->orientationMatrix);
|
|
|
|
// move to player position
|
|
startingPos[0] += player->pos[0];
|
|
startingPos[1] += player->pos[1];
|
|
startingPos[2] += player->pos[2];
|
|
|
|
// set the itembox's velocity to the player's velocity
|
|
startingVelocity[0] = player->velocity[0];
|
|
startingVelocity[1] = player->velocity[1];
|
|
startingVelocity[2] = player->velocity[2];
|
|
|
|
startingRot[0] = 0;
|
|
startingRot[1] = 0;
|
|
startingRot[2] = 0;
|
|
|
|
// spawn the itembox
|
|
actorIndex = add_actor_to_empty_slot(startingPos, startingRot, startingVelocity, ACTOR_FAKE_ITEM_BOX);
|
|
if (actorIndex < 0) {
|
|
return actorIndex;
|
|
}
|
|
itemBox = (struct FakeItemBox*) &gActorList[actorIndex];
|
|
itemBox->playerId = (player - gPlayerOne);
|
|
itemBox->state = HELD_FAKE_ITEM_BOX;
|
|
player->triggers |= DRAG_ITEM_EFFECT;
|
|
return actorIndex;
|
|
}
|
|
|
|
s32 use_banana_item(Player* player) {
|
|
UNUSED s32 pad[6];
|
|
u16 playerId;
|
|
s16 actorIndex;
|
|
struct BananaActor* banana;
|
|
Vec3f startingVelocity;
|
|
Vec3s startingRot;
|
|
Vec3f startingPos;
|
|
|
|
playerId = player - gPlayerOne;
|
|
if (playerId >= 8) {
|
|
return -1;
|
|
}
|
|
// Extremely weird fake match to fix a tiny stack difference
|
|
// place the banana in back of the player
|
|
startingPos[0, 0] = 0.0f;
|
|
startingPos[1] = -player->boundingBoxSize;
|
|
startingPos[2] = -(player->boundingBoxSize + 4.0f);
|
|
|
|
// apply the player's orientation to the banana
|
|
mtxf_transform_vec3f_mat3(startingPos, player->orientationMatrix);
|
|
|
|
// add the player's position to the banana's position
|
|
startingPos[0] += player->pos[0];
|
|
startingPos[1] += player->pos[1];
|
|
startingPos[2] += player->pos[2];
|
|
|
|
// set the banana's velocity to the player's velocity
|
|
startingVelocity[0] = player->velocity[0];
|
|
startingVelocity[1] = player->velocity[1];
|
|
startingVelocity[2] = player->velocity[2];
|
|
startingRot[0] = 0;
|
|
startingRot[1] = 0;
|
|
startingRot[2] = 0;
|
|
|
|
actorIndex = add_actor_to_empty_slot(startingPos, startingRot, startingVelocity, ACTOR_BANANA); // spawn banana
|
|
if (actorIndex < 0) {
|
|
return actorIndex;
|
|
}
|
|
banana = (struct BananaActor*) &gActorList[actorIndex];
|
|
banana->playerId = playerId;
|
|
banana->state = HELD_BANANA;
|
|
banana->unk_04 = 0x0014;
|
|
player->triggers |= DRAG_ITEM_EFFECT;
|
|
return actorIndex;
|
|
}
|
|
|
|
/**
|
|
* Strikes players with thunder
|
|
*
|
|
* @param Activating player (not to be struck)
|
|
*/
|
|
void use_thunder_item(Player* player) {
|
|
s32 index;
|
|
Player* otherPlayer;
|
|
|
|
func_8009E5BC();
|
|
if ((player->type & PLAYER_HUMAN) != 0) {
|
|
// Play sound.
|
|
func_800CAB4C(player - gPlayerOne);
|
|
}
|
|
|
|
for (index = 0; index < NUM_PLAYERS; index++) {
|
|
otherPlayer = &gPlayers[index];
|
|
if (player != otherPlayer) {
|
|
otherPlayer->triggers |= LIGHTNING_STRIKE_TRIGGER;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handles item use?
|
|
void player_use_item(Player* player) {
|
|
s32 playerId = player - gPlayerOne;
|
|
|
|
switch (player->currentItemCopy) {
|
|
case ITEM_GREEN_SHELL:
|
|
use_green_shell_item(player);
|
|
break;
|
|
case ITEM_RED_SHELL:
|
|
use_red_shell_item(player);
|
|
break;
|
|
case ITEM_BLUE_SPINY_SHELL:
|
|
use_blue_shell_item(player);
|
|
break;
|
|
case ITEM_BANANA:
|
|
use_banana_item(player);
|
|
break;
|
|
case ITEM_BANANA_BUNCH:
|
|
use_banana_bunch_item(player);
|
|
break;
|
|
case ITEM_MUSHROOM:
|
|
player->triggers |= SHROOM_TRIGGER;
|
|
break;
|
|
case ITEM_DOUBLE_MUSHROOM:
|
|
player->triggers |= SHROOM_TRIGGER;
|
|
break;
|
|
case ITEM_TRIPLE_MUSHROOM:
|
|
player->triggers |= SHROOM_TRIGGER;
|
|
break;
|
|
case ITEM_SUPER_MUSHROOM:
|
|
player->triggers |= SHROOM_TRIGGER;
|
|
break;
|
|
case ITEM_BOO:
|
|
player->triggers |= BOO_TRIGGER;
|
|
break;
|
|
case ITEM_STAR:
|
|
player->triggers |= STAR_TRIGGER;
|
|
break;
|
|
case ITEM_THUNDERBOLT:
|
|
use_thunder_item(player);
|
|
break;
|
|
case ITEM_FAKE_ITEM_BOX:
|
|
use_fake_itembox_item(player);
|
|
break;
|
|
case ITEM_TRIPLE_GREEN_SHELL:
|
|
use_triple_shell_item(player, ACTOR_TRIPLE_GREEN_SHELL);
|
|
break;
|
|
case ITEM_TRIPLE_RED_SHELL:
|
|
use_triple_shell_item(player, ACTOR_TRIPLE_RED_SHELL);
|
|
break;
|
|
}
|
|
consume_item(playerId);
|
|
}
|
|
|
|
// Check if a player is using an item?
|
|
void check_player_use_item(void) {
|
|
Player* player;
|
|
struct Controller* target;
|
|
struct Controller* controller;
|
|
struct Controller* loopController;
|
|
|
|
for (player = &gPlayers[0], loopController = &gControllers[0], target = &gControllers[4]; loopController != target;
|
|
player++, loopController++) {
|
|
controller = loopController;
|
|
if (prevent_item_use(player) == false) {
|
|
if ((player->type & PLAYER_INVISIBLE_OR_BOMB) != 0) {
|
|
if ((player - gPlayerTwo) == 0) {
|
|
controller = gControllerSix;
|
|
} else if ((player - gPlayerThree) == 0) {
|
|
controller = gControllerSeven;
|
|
} else {
|
|
if ((player - gPlayerOne) == 0) {
|
|
controller = gControllerEight;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (((player->type & PLAYER_HUMAN) != 0) && (player->currentItemCopy != ITEM_NONE) &&
|
|
((player->type & PLAYER_START_SEQUENCE) == 0)) {
|
|
if ((controller->buttonPressed & Z_TRIG) != 0) {
|
|
controller->buttonPressed &= ~Z_TRIG;
|
|
player_use_item(player);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#include "actors/green_shell/update.inc.c"
|
|
|
|
#include "actors/blue_and_red_shells/update.inc.c"
|
|
|
|
void func_802B4E30(struct Actor* arg0) {
|
|
if ((arg0->unk30.surfaceDistance[2] < 0.0f) && (arg0->unk30.unk34 == 1)) {
|
|
arg0->pos[0] -= (arg0->unk30.orientationVector[0] * arg0->unk30.surfaceDistance[2]);
|
|
arg0->pos[1] -= (arg0->unk30.orientationVector[1] * arg0->unk30.surfaceDistance[2]);
|
|
arg0->pos[2] -= (arg0->unk30.orientationVector[2] * arg0->unk30.surfaceDistance[2]);
|
|
}
|
|
if ((arg0->unk30.surfaceDistance[0] < 0.0f) && (arg0->unk30.unk30 == 1)) {
|
|
arg0->pos[0] -= (arg0->unk30.unk48[0] * arg0->unk30.surfaceDistance[0]);
|
|
arg0->pos[1] -= (arg0->unk30.unk48[1] * arg0->unk30.surfaceDistance[0]);
|
|
arg0->pos[2] -= (arg0->unk30.unk48[2] * arg0->unk30.surfaceDistance[0]);
|
|
}
|
|
if ((arg0->unk30.surfaceDistance[1] < 0.0f) && (arg0->unk30.unk32 == 1)) {
|
|
arg0->pos[0] -= (arg0->unk30.unk54[0] * arg0->unk30.surfaceDistance[1]);
|
|
arg0->pos[1] -= (arg0->unk30.unk54[1] * arg0->unk30.surfaceDistance[1]);
|
|
arg0->pos[2] -= (arg0->unk30.unk54[2] * arg0->unk30.surfaceDistance[1]);
|
|
}
|
|
}
|