papermario/src/entity.c

1770 lines
57 KiB
C

#include "common.h"
#include "ld_addrs.h"
#include "entity.h"
#include "model.h"
#include "sprite/player.h"
#ifdef SHIFT
extern Addr WorldEntityHeapBottom;
extern Addr WorldEntityHeapBase;
#define WORLD_ENTITY_HEAP_BOTTOM (s32) WorldEntityHeapBottom
#define WORLD_ENTITY_HEAP_BASE (s32) WorldEntityHeapBase
#define entity_jan_iwa_VRAM (void*) entity_jan_iwa_VRAM
#define entity_sbk_omo_VRAM (void*) entity_sbk_omo_VRAM
#define entity_default_VRAM (void*) entity_default_VRAM
#else
#define WORLD_ENTITY_HEAP_BOTTOM 0x80250000
#define WORLD_ENTITY_HEAP_BASE 0x80267FF0
#define entity_jan_iwa_VRAM (void*) 0x802BAE00
#define entity_sbk_omo_VRAM (void*) 0x802BAE00
#define entity_default_VRAM (void*) 0x802BAE00
#endif
#if VERSION_JP // TODO remove once segments are split
extern Addr entity_default_ROM_END;
extern Addr entity_default_ROM_START;
extern Addr entity_jan_iwa_ROM_END;
extern Addr entity_jan_iwa_ROM_START;
extern Addr entity_sbk_omo_ROM_END;
extern Addr entity_sbk_omo_ROM_START;
#endif
s32 D_8014AFB0 = 255;
s32 CreateEntityVarArgBuffer[4];
HiddenPanelsData gCurrentHiddenPanels;
s32 gEntityHideMode;
s32 D_801512BC;
s32 D_80151304;
s32 D_80151344;
s32 entity_numEntities;
s32 gEntityHeapBase;
s32 gLastCreatedEntityIndex;
s32 gEntityHeapBottom;
s32 entity_numShadows;
s32 isAreaSpecificEntityDataLoaded;
s32 entity_updateCounter;
BSS EntityList gWorldEntityList;
BSS EntityList gBattleEntityList;
BSS EntityList* gCurrentEntityListPtr;
BSS s32 D_80151474;
BSS ShadowList gWorldShadowList;
BSS ShadowList gBattleShadowList;
BSS ShadowList* gCurrentShadowListPtr;
BSS s32 wEntityDataLoadedSize;
BSS s32 bEntityDataLoadedSize;
BSS s32 D_80151664;
BSS EntityBlueprint* wEntityBlueprint[MAX_ENTITIES + 2];
BSS EntityBlueprint* bEntityBlueprint[4];
BSS s32 D_801516F8;
BSS s32 D_801516FC;
extern Addr BattleEntityHeapBottom; // todo ???
void update_shadows(void);
s32 step_entity_commandlist(Entity* entity);
void entity_swizzle_anim_pointers(EntityBlueprint* entityData, void* baseAnim, void* baseGfx);
void render_shadows(void);
void update_entity_transform_matrix(Entity* entity);
void update_shadow_transform_matrix(Shadow* shadow);
void update_entity_inverse_rotation_matrix(Entity* entity);
void delete_entity(s32 entityIndex);
void delete_entity_and_unload_data(s32 entityIndex);
void _delete_shadow(s32 shadowIndex);
void reload_world_entity_data(void);
s32 entity_get_collision_flags(Entity* entity);
void entity_free_static_data(EntityBlueprint* data);
s32 create_entity_shadow(Entity* entity, f32 x, f32 y, f32 z);
void update_entity_shadow_position(Entity* entity);
void update_entities(void) {
s32 i;
D_801512BC = 0;
entity_numEntities = 0;
entity_updateCounter++;
for (i = 0; i < MAX_ENTITIES; i++) {
Entity* entity = get_entity_by_index(i);
if (entity != NULL) {
entity_numEntities++;
if (!(entity->flags & ENTITY_FLAG_SKIP_UPDATE)) {
if (entity->flags & ENTITY_FLAG_BOUND_SCRIPT_DIRTY) {
entity->flags &= ~ENTITY_FLAG_BOUND_SCRIPT_DIRTY;
if (!(entity->flags & ENTITY_FLAG_8000)) {
entity->flags |= ENTITY_FLAG_2000000;
}
entity->boundScript = start_script(entity->boundScriptBytecode, EVT_PRIORITY_A, EVT_FLAG_RUN_IMMEDIATELY);
}
if (entity->flags & ENTITY_FLAG_2000000) {
if (does_script_exist(entity->boundScript->id)) {
if (entity->flags & ENTITY_FLAG_HAS_ANIMATED_MODEL) {
update_model_animator(entity->virtualModelIndex);
} else {
exec_entity_model_commandlist(entity->virtualModelIndex);
}
if (entity->flags & ENTITY_FLAG_ALWAYS_FACE_CAMERA) {
entity->rot.y = -gCameras[gCurrentCameraID].curYaw;
}
if (!(entity->flags & ENTITY_FLAG_SKIP_UPDATE_TRANSFORM_MATRIX)) {
update_entity_transform_matrix(entity);
}
continue;
} else {
entity->flags &= ~ENTITY_FLAG_2000000;
}
}
if (entity->collisionTimer == 0) {
entity->collisionFlags = entity_get_collision_flags(entity);
if (entity->collisionFlags) {
EntityCallback handleCollision = entity->blueprint->fpHandleCollision;
if (handleCollision != NULL && handleCollision(entity) != 0) {
entity->collisionTimer = 10;
entity->flags |= ENTITY_FLAG_DETECTED_COLLISION;
}
}
} else {
entity->collisionTimer--;
if (entity->flags & ENTITY_FLAG_CONTINUOUS_COLLISION) {
if (entity->collisionTimer == 0) {
entity->flags &= ~(ENTITY_FLAG_DISABLE_COLLISION | ENTITY_FLAG_CONTINUOUS_COLLISION);
} else {
entity->flags |= ENTITY_FLAG_DISABLE_COLLISION;
}
} else if (entity->collisionTimer == 0) {
entity->flags &= ~ENTITY_FLAG_DETECTED_COLLISION;
entity->flags &= ~ENTITY_FLAG_PARTNER_COLLISION;
entity->collisionFlags = 0;
}
}
if (entity->flags & ENTITY_FLAG_ALWAYS_FACE_CAMERA) {
entity->rot.y = -gCameras[gCurrentCameraID].curYaw;
}
if (gGameStatusPtr->debugScripts == DEBUG_SCRIPTS_NONE) {
if (entity->updateScriptCallback != NULL) {
entity->updateScriptCallback(entity);
}
if (entity->scriptReadPos != NULL) {
if (entity->scriptDelay != 0) {
entity->scriptDelay--;
if (entity->scriptDelay == 0) {
while (step_entity_commandlist(entity));
}
}
}
}
if (!(entity->flags & ENTITY_FLAG_SKIP_UPDATE_TRANSFORM_MATRIX)) {
update_entity_transform_matrix(entity);
}
if (!(entity->flags & ENTITY_FLAG_DISABLE_COLLISION)) {
update_entity_inverse_rotation_matrix(entity);
}
if (entity->flags & ENTITY_FLAG_HAS_ANIMATED_MODEL) {
update_model_animator(entity->virtualModelIndex);
} else {
exec_entity_model_commandlist(entity->virtualModelIndex);
}
if (entity->shadowIndex >= 0) {
update_entity_shadow_position(entity);
}
if (entity->flags & ENTITY_FLAG_PENDING_INSTANCE_DELETE) {
delete_entity(entity->listIndex);
}
if (entity->flags & ENTITY_FLAG_PENDING_FULL_DELETE) {
delete_entity_and_unload_data(entity->listIndex);
}
}
}
}
update_shadows();
gCurrentHiddenPanels.tryFlipTrigger = FALSE;
}
void update_shadows(void) {
s32 i;
entity_numShadows = 0;
for (i = 0; i < MAX_SHADOWS; i++) {
Shadow* shadow = get_shadow_by_index(i);
if (shadow != NULL) {
entity_numShadows++;
if (!(shadow->flags & ENTITY_FLAG_SKIP_UPDATE)) {
if (shadow->flags & ENTITY_FLAG_ALWAYS_FACE_CAMERA) {
shadow->rot.y = -gCameras[gCurrentCameraID].curYaw;
}
update_shadow_transform_matrix(shadow);
if (shadow->flags & ENTITY_FLAG_HAS_ANIMATED_MODEL) {
update_model_animator(shadow->entityModelID);
} else {
exec_entity_model_commandlist(shadow->entityModelID);
}
if (shadow->flags & ENTITY_FLAG_PENDING_INSTANCE_DELETE) {
_delete_shadow(shadow->listIndex);
}
}
}
}
}
void set_entity_commandlist(Entity* entity, s32* entityScript) {
entity->scriptReadPos = entityScript;
entity->scriptDelay = 1;
entity->savedReadPos[0] = entity->scriptReadPos;
}
s32 step_entity_commandlist(Entity* entity) {
s32* args = entity->scriptReadPos;
s32 ret;
s32 labelId;
void (*tempfunc)(Entity*);
switch (*args++) {
case ENTITY_SCRIPT_OP_End:
entity->scriptDelay = -1;
entity->updateScriptCallback = NULL;
entity->scriptReadPos = NULL;
ret = FALSE;
break;
case ENTITY_SCRIPT_OP_Jump:
entity->scriptReadPos = (s32*)*args;
entity->scriptDelay = 1;
entity->savedReadPos[0] = entity->scriptReadPos;
ret = TRUE;
break;
case ENTITY_SCRIPT_OP_Call:
tempfunc = (void (*)(Entity*))(*args++);
entity->scriptReadPos = args;
(tempfunc)(entity);
ret = TRUE;
break;
case ENTITY_SCRIPT_OP_SetCallback:
entity->scriptDelay = *args++;
entity->updateScriptCallback = (s32 (*)(Entity*)) *args++;
entity->scriptReadPos = args++;
ret = FALSE;
break;
case ENTITY_SCRIPT_OP_Goto:
entity->scriptReadPos = entity->savedReadPos[*args];
ret = TRUE;
break;
case ENTITY_SCRIPT_OP_Label:
labelId = *args++;
entity->savedReadPos[labelId] = args;
entity->scriptReadPos = args;
ret = TRUE;
break;
case ENTITY_SCRIPT_OP_RestartBoundScript:
if (entity->boundScriptBytecode != NULL) {
entity->flags |= ENTITY_FLAG_BOUND_SCRIPT_DIRTY;
}
entity->scriptReadPos = args++;
ret = TRUE;
break;
case ENTITY_SCRIPT_OP_SetFlags:
entity->flags |= *args++;
entity->scriptReadPos = args++;
ret = TRUE;
break;
case ENTITY_SCRIPT_OP_ClearFlags:
entity->flags &= ~*args++;
entity->scriptReadPos = args++;
ret = TRUE;
break;
case ENTITY_SCRIPT_OP_PlaySound:
sfx_play_sound(*args++);
entity->scriptReadPos = args++;
ret = TRUE;
break;
default:
args++;
entity->scriptReadPos = args++;
ret = TRUE;
break;
}
return ret;
}
void exec_entity_commandlist(Entity* entity) {
while (step_entity_commandlist(entity));
}
void func_8010FD98(void* arg0, s32 alpha) {
if (alpha >= 255) {
gDPSetRenderMode(gMainGfxPos++, G_RM_AA_ZB_OPA_SURF, G_RM_AA_ZB_OPA_SURF2);
gDPSetCombineMode(gMainGfxPos++, G_CC_MODULATEIA, G_CC_MODULATEIA);
} else {
gDPSetCombineMode(gMainGfxPos++, PM_CC_01, PM_CC_02);
gDPSetPrimColor(gMainGfxPos++, 0, 0, 0, 0, 0, alpha);
}
}
void func_8010FE44(void* arg0) {
func_8010FD98(arg0, D_8014AFB0);
}
void entity_model_set_shadow_color(void* data) {
s32 alpha = (s32)data;
gDPSetCombineMode(gMainGfxPos++, PM_CC1_SHADOW, PM_CC2_SHADOW);
gDPSetPrimColor(gMainGfxPos++, 0, 0, 0, 0, 0, alpha);
}
void render_entities(void) {
s32 i;
for (i = 0; i < MAX_ENTITIES; i++) {
Entity* entity = get_entity_by_index(i);
if (entity != NULL) {
if (!gGameStatusPtr->isBattle) {
if (gEntityHideMode != ENTITY_HIDE_MODE_0 &&
!(entity->flags & ENTITY_FLAG_IGNORE_DISTANCE_CULLING) &&
dist2D(gPlayerStatusPtr->pos.x,
gPlayerStatusPtr->pos.z,
entity->pos.x,
entity->pos.z) > 200.0f
) {
continue;
}
if (gEntityHideMode == ENTITY_HIDE_MODE_1) {
if (!(entity->flags & ENTITY_FLAG_DRAW_IF_CLOSE_HIDE_MODE1)) {
continue;
}
} else if (gEntityHideMode == ENTITY_HIDE_MODE_2) {
if (!(entity->flags & ENTITY_FLAG_DRAW_IF_CLOSE_HIDE_MODE2)) {
continue;
}
}
}
if (!(entity->flags & ENTITY_FLAG_HIDDEN)) {
if (entity->flags & ENTITY_FLAG_HAS_ANIMATED_MODEL) {
if (D_8014AFB0 == 255) {
if (entity->renderSetupFunc != NULL) {
set_animator_render_callback(
entity->virtualModelIndex,
(void*)(u32) entity->listIndex,
(void (*)(void*)) entity->renderSetupFunc
);
}
} else {
set_animator_render_callback(
entity->virtualModelIndex,
(void*)(u32) entity->listIndex,
func_8010FE44
);
}
if (entity->gfxBaseAddr == NULL) {
render_animated_model(entity->virtualModelIndex, &entity->transformMatrix);
} else {
render_animated_model_with_vertices(entity->virtualModelIndex,
&entity->transformMatrix,
entity->vertexSegment,
entity->gfxBaseAddr);
}
} else {
if (D_8014AFB0 == 255) {
if (entity->renderSetupFunc != NULL) {
bind_entity_model_setupGfx(
entity->virtualModelIndex,
(void*)(u32) entity->listIndex,
(void (*)(void*)) entity->renderSetupFunc
);
} else {
get_entity_model(entity->virtualModelIndex)->fpSetupGfxCallback = NULL;
}
} else {
bind_entity_model_setupGfx(entity->virtualModelIndex, (void*)(u32)entity->listIndex, func_8010FE44);
}
if (entity->gfxBaseAddr == NULL) {
draw_entity_model_A(entity->virtualModelIndex, &entity->transformMatrix);
} else {
draw_entity_model_B(entity->virtualModelIndex,
&entity->transformMatrix,
entity->vertexSegment,
entity->gfxBaseAddr);
}
}
}
}
}
render_shadows();
}
void render_shadows(void) {
s32 i;
for (i = 0; i < MAX_SHADOWS; i++) {
Shadow* shadow = get_shadow_by_index(i);
if (shadow != NULL) {
if (shadow->flags & ENTITY_FLAG_HIDDEN) {
if (shadow->flags & ENTITY_FLAG_FADING_AWAY) {
shadow->alpha -= 20;
if (shadow->alpha <= 20) {
shadow->flags |= ENTITY_FLAG_PENDING_INSTANCE_DELETE;
}
}
} else if (shadow->flags & ENTITY_FLAG_HAS_ANIMATED_MODEL) {
if (shadow->vertexArray == NULL) {
render_animated_model(shadow->entityModelID, &shadow->transformMatrix);
} else {
render_animated_model_with_vertices(shadow->entityModelID,
&shadow->transformMatrix,
shadow->vertexSegment,
shadow->vertexArray);
}
} else {
if (shadow->flags & ENTITY_FLAG_FADING_AWAY) {
shadow->alpha -= 20;
if (shadow->alpha <= 20) {
shadow->flags |= ENTITY_FLAG_PENDING_INSTANCE_DELETE;
}
}
bind_entity_model_setupGfx(shadow->entityModelID, (void*)(u32)shadow->alpha, entity_model_set_shadow_color);
if (shadow->vertexArray == NULL) {
draw_entity_model_A(shadow->entityModelID, &shadow->transformMatrix);
} else {
draw_entity_model_B(shadow->entityModelID,
&shadow->transformMatrix,
shadow->vertexSegment,
shadow->vertexArray);
}
}
}
}
}
void update_entity_transform_matrix(Entity* entity) {
Matrix4f sp18;
Matrix4f sp58;
Matrix4f sp98;
Matrix4f spD8;
Matrix4f sp118;
Matrix4f sp158;
Matrix4f sp198;
if (entity->updateMatrixOverride != NULL) {
entity->updateMatrixOverride(entity);
return;
}
guTranslateF(sp58, entity->pos.x, entity->pos.y, entity->pos.z);
guRotateF(spD8, entity->rot.x, 1.0f, 0.0f, 0.0f);
guRotateF(sp118, entity->rot.y, 0.0f, 1.0f, 0.0f);
guRotateF(sp158, entity->rot.z, 0.0f, 0.0f, 1.0f);
guMtxCatF(sp158, spD8, sp18);
guMtxCatF(sp18, sp118, sp98);
guScaleF(sp198, entity->scale.x, entity->scale.y, entity->scale.z);
guMtxCatF(sp198, sp98, sp18);
guMtxCatF(sp18, sp58, sp98);
guMtxF2L(sp98, &entity->transformMatrix);
}
void update_shadow_transform_matrix(Shadow* shadow) {
Matrix4f sp18;
Matrix4f sp58;
Matrix4f sp98;
Matrix4f spD8;
Matrix4f sp118;
Matrix4f sp158;
Matrix4f sp198;
guTranslateF(sp58, shadow->pos.x, shadow->pos.y, shadow->pos.z);
guRotateF(sp118, shadow->rot.x, 1.0f, 0.0f, 0.0f);
guRotateF(spD8, shadow->rot.y, 0.0f, 1.0f, 0.0f);
guRotateF(sp158, shadow->rot.z, 0.0f, 0.0f, 1.0f);
guMtxCatF(sp158, sp118, sp98);
guMtxCatF(spD8, sp98, sp98);
guScaleF(sp198, shadow->scale.x, shadow->scale.y, shadow->scale.z);
guMtxCatF(sp198, sp98, sp18);
guMtxCatF(sp18, sp58, sp98);
guMtxF2L(sp98, &shadow->transformMatrix);
}
void update_entity_inverse_rotation_matrix(Entity* entity) {
Matrix4f sp18;
Matrix4f sp58;
guRotateF(sp18, -entity->rot.y, 0.0f, 1.0f, 0.0f);
guRotateF(sp58, -entity->rot.z, 0.0f, 0.0f, 1.0f);
guMtxCatF(sp18, sp58, sp18);
guRotateF(sp58, -entity->rot.x, 1.0f, 0.0f, 0.0f);
guMtxCatF(sp18, sp58, entity->inverseTransformMatrix);
entity->effectiveSize = sqrtf(((SQ(entity->aabb.x) + SQ(entity->aabb.z)) * 0.25f) + SQ(entity->aabb.y));
}
Entity* get_entity_by_index(s32 index) {
return (*gCurrentEntityListPtr)[index & 0xFFF];
}
Shadow* get_shadow_by_index(s32 index) {
return (*gCurrentShadowListPtr)[index & 0xFFF];
}
EntityList* get_entity_list(void) {
EntityList* ret;
if (!gGameStatusPtr->isBattle) {
ret = &gWorldEntityList;
} else {
ret = &gBattleEntityList;
}
return ret;
}
ShadowList* get_shadow_list(void) {
ShadowList* ret;
if (!gGameStatusPtr->isBattle) {
ret = &gWorldShadowList;
} else {
ret = &gBattleShadowList;
}
return ret;
}
s32 entity_start_script(Entity* entity) {
if (entity->boundScriptBytecode != NULL) {
entity->flags |= ENTITY_FLAG_BOUND_SCRIPT_DIRTY;
return 1;
}
return 0;
}
u32 get_entity_type(s32 index) {
Entity* entity = get_entity_by_index(index);
if (entity == NULL) {
return -1;
} else {
return entity->blueprint->entityType;
}
}
void delete_entity(s32 entityIndex) {
Entity* entity = get_entity_by_index(entityIndex);
if (entity->dataBuf.any != NULL) {
heap_free(entity->dataBuf.any);
}
if (!(entity->flags & ENTITY_FLAG_HAS_ANIMATED_MODEL)) {
free_entity_model_by_index(entity->virtualModelIndex);
} else {
delete_model_animator(get_animator_by_index(entity->virtualModelIndex));
}
if (entity->shadowIndex >= 0) {
Shadow* shadow = get_shadow_by_index(entity->shadowIndex);
shadow->flags |= ENTITY_FLAG_FADING_AWAY;
}
heap_free((*gCurrentEntityListPtr)[entityIndex]);
(*gCurrentEntityListPtr)[entityIndex] = NULL;
}
void delete_entity_and_unload_data(s32 entityIndex) {
Entity* entity = get_entity_by_index(entityIndex);
if (entity->dataBuf.any != NULL) {
heap_free(entity->dataBuf.any);
}
if (!(entity->flags & ENTITY_FLAG_HAS_ANIMATED_MODEL)) {
free_entity_model_by_index(entity->virtualModelIndex);
} else {
delete_model_animator(get_animator_by_index(entity->virtualModelIndex));
}
entity_free_static_data(entity->blueprint);
if (entity->shadowIndex >= 0) {
Shadow* shadow = get_shadow_by_index(entity->shadowIndex);
shadow->flags |= ENTITY_FLAG_FADING_AWAY;
}
heap_free((*gCurrentEntityListPtr)[entityIndex]);
(*gCurrentEntityListPtr)[entityIndex] = NULL;
}
void _delete_shadow(s32 shadowIndex) {
Shadow* shadow = get_shadow_by_index(shadowIndex);
free_entity_model_by_index(shadow->entityModelID);
heap_free((*gCurrentShadowListPtr)[shadowIndex]);
(*gCurrentShadowListPtr)[shadowIndex] = NULL;
}
s32 entity_get_collision_flags(Entity* entity) {
u32 listIndex = entity->listIndex;
s32 entityFlags = 0;
u32 flag;
if (entity->flags & ENTITY_FLAG_PARTNER_COLLISION) {
entityFlags = ENTITY_COLLISION_PARTNER;
entity->flags &= ~ENTITY_FLAG_PARTNER_COLLISION;
}
flag = gCollisionStatus.curFloor;
if (flag != -1 && (flag & COLLISION_WITH_ENTITY_BIT) && listIndex == (u8)flag) {
entityFlags |= ENTITY_COLLISION_PLAYER_TOUCH_FLOOR;
}
flag = gCollisionStatus.lastTouchedFloor;
if (flag != -1 && (flag & COLLISION_WITH_ENTITY_BIT) && listIndex == (u8)flag) {
entityFlags |= ENTITY_COLLISION_PLAYER_LAST_FLOOR;
}
flag = gCollisionStatus.curCeiling;
if (flag != -1 && (flag & COLLISION_WITH_ENTITY_BIT) && listIndex == (u8)flag) {
entityFlags |= ENTITY_COLLISION_PLAYER_TOUCH_CEILING;
}
flag = gCollisionStatus.pushingAgainstWall;
if (flag != -1 && (flag & COLLISION_WITH_ENTITY_BIT) && listIndex == (u8)flag) {
entityFlags |= ENTITY_COLLISION_PLAYER_PUSHING_AGAINST;
}
flag = gCollisionStatus.lastWallHammered;
if (flag != -1 && (flag & COLLISION_WITH_ENTITY_BIT) && listIndex == (u8)flag) {
entityFlags |= ENTITY_COLLISION_PLAYER_HAMMER;
}
flag = gCollisionStatus.curWall;
if (flag != -1 && (flag & COLLISION_WITH_ENTITY_BIT) && listIndex == (u8)flag && gPlayerStatusPtr->pressedButtons & BUTTON_A) {
entityFlags |= ENTITY_COLLISION_PLAYER_TOUCH_WALL;
}
return entityFlags;
}
s32 entity_try_partner_interaction_trigger(s32 entityIdx) {
s32 interacted = FALSE;
u32 entityType = get_entity_type(entityIdx);
s32 partnerID = get_current_partner_id();
Entity* entity;
switch (partnerID) {
case PARTNER_BOMBETTE:
switch (entityType) {
default:
return FALSE;
case ENTITY_TYPE_BLUE_SWITCH:
case ENTITY_TYPE_RED_SWITCH:
case ENTITY_TYPE_MULTI_TRIGGER_BLOCK:
case ENTITY_TYPE_BRICK_BLOCK:
case ENTITY_TYPE_MULTI_COIN_BRICK:
case ENTITY_TYPE_YELLOW_BLOCK:
case ENTITY_TYPE_SINGLE_TRIGGER_BLOCK:
case ENTITY_TYPE_HIDDEN_YELLOW_BLOCK:
case ENTITY_TYPE_HIDDEN_RED_BLOCK:
case ENTITY_TYPE_RED_BLOCK:
case ENTITY_TYPE_HAMMER1_BLOCK:
case ENTITY_TYPE_HAMMER1_BLOCK_TINY:
case ENTITY_TYPE_SUPER_BLOCK:
case ENTITY_TYPE_BOMBABLE_ROCK:
entity = get_entity_by_index(entityIdx);
entity->flags |= ENTITY_FLAG_PARTNER_COLLISION;
interacted = TRUE;
}
break;
case PARTNER_KOOPER:
switch (entityType) {
default:
return FALSE;
case ENTITY_TYPE_BLUE_SWITCH:
case ENTITY_TYPE_RED_SWITCH:
case ENTITY_TYPE_MULTI_TRIGGER_BLOCK:
case ENTITY_TYPE_BRICK_BLOCK:
case ENTITY_TYPE_MULTI_COIN_BRICK:
case ENTITY_TYPE_YELLOW_BLOCK:
case ENTITY_TYPE_SINGLE_TRIGGER_BLOCK:
case ENTITY_TYPE_HIDDEN_YELLOW_BLOCK:
case ENTITY_TYPE_HIDDEN_RED_BLOCK:
case ENTITY_TYPE_RED_BLOCK:
case ENTITY_TYPE_HEALING_BLOCK:
case ENTITY_TYPE_1C:
case ENTITY_TYPE_SAVE_POINT:
case ENTITY_TYPE_SUPER_BLOCK:
entity = get_entity_by_index(entityIdx);
entity->flags |= ENTITY_FLAG_PARTNER_COLLISION;
interacted = TRUE;
}
break;
}
return interacted;
}
s32 test_player_entity_aabb(Entity* entity) {
f32 yTemp = entity->pos.y - (gPlayerStatus.pos.y + gPlayerStatus.colliderHeight);
f32 xCollRadius;
f32 zCollRadius;
f32 xDist;
f32 zDist;
if (yTemp > 0.0f || gPlayerStatus.colliderHeight + entity->aabb.y < fabsf(yTemp)) {
return 0;
}
xCollRadius = (gPlayerStatus.colliderDiameter + entity->aabb.x) * 0.5;
xDist = fabsf(gPlayerStatus.pos.x - entity->pos.x);
zCollRadius = ((gPlayerStatus.colliderDiameter + entity->aabb.z) * 0.5);
zDist = fabsf(gPlayerStatus.pos.z - entity->pos.z);
if (xCollRadius < xDist || zCollRadius < zDist) {
return 0;
}
return 1;
}
s32 is_player_action_state(s8 actionState) {
return actionState == gPlayerStatus.actionState;
}
void entity_set_render_script(Entity* entity, EntityModelScript* cmdList) {
if (!(entity->flags & ENTITY_FLAG_HAS_ANIMATED_MODEL)) {
set_entity_model_render_command_list(entity->virtualModelIndex, cmdList);
}
}
void entity_reset_collision(Entity* entity) {
entity->collisionTimer = 0;
entity->flags &= ~ENTITY_FLAG_DETECTED_COLLISION;
}
void load_area_specific_entity_data(void) {
if (!isAreaSpecificEntityDataLoaded) {
if (gGameStatusPtr->areaID == AREA_JAN || gGameStatusPtr->areaID == AREA_IWA) {
DMA_COPY_SEGMENT(entity_jan_iwa);
} else if (gGameStatusPtr->areaID == AREA_SBK || gGameStatusPtr->areaID == AREA_OMO) {
DMA_COPY_SEGMENT(entity_sbk_omo);
} else {
DMA_COPY_SEGMENT(entity_default);
}
isAreaSpecificEntityDataLoaded = TRUE;
}
}
void clear_entity_data(b32 arg0) {
s32 i;
D_801516FC = 1;
entity_numEntities = 0;
entity_numShadows = 0;
entity_updateCounter = 0;
D_80151304 = 0;
if (!gGameStatusPtr->isBattle) {
gEntityHideMode = ENTITY_HIDE_MODE_0;
}
isAreaSpecificEntityDataLoaded = FALSE;
gCurrentHiddenPanels.panelsCount = 0;
gCurrentHiddenPanels.activateISpy = FALSE;
if (!arg0) {
D_80151344 = 0;
}
D_8014AFB0 = 255;
if (!gGameStatusPtr->isBattle) {
wEntityDataLoadedSize = 0;
for (i = 0; i < MAX_ENTITIES; i++) {
wEntityBlueprint[i] = NULL;
}
} else {
bEntityDataLoadedSize = 0;
for (i = 0; i < ARRAY_COUNT(bEntityBlueprint); i++) {
bEntityBlueprint[i] = NULL;
}
}
if (!gGameStatusPtr->isBattle) {
gEntityHeapBottom = WORLD_ENTITY_HEAP_BOTTOM;
gEntityHeapBase = WORLD_ENTITY_HEAP_BASE;
} else {
gEntityHeapBottom = (s32) BattleEntityHeapBottom;
gEntityHeapBase = gEntityHeapBottom + 0x3000;
}
gCurrentEntityListPtr = get_entity_list();
gCurrentShadowListPtr = get_shadow_list();
for (i = 0; i < MAX_ENTITIES; i++) {
(*gCurrentEntityListPtr)[i] = NULL;
}
for (i = 0; i < MAX_SHADOWS; i++) {
(*gCurrentShadowListPtr)[i] = NULL;
}
}
void init_entity_data(void) {
if (!gGameStatusPtr->isBattle) {
gEntityHeapBottom = WORLD_ENTITY_HEAP_BOTTOM;
gEntityHeapBase = WORLD_ENTITY_HEAP_BASE;
reload_world_entity_data();
} else {
s32 i;
for (i = 0; i < ARRAY_COUNT(bEntityBlueprint); i++) {
bEntityBlueprint[i] = 0;
}
gEntityHeapBottom = (s32) BattleEntityHeapBottom;
gEntityHeapBase = gEntityHeapBottom + 0x3000;
}
gCurrentEntityListPtr = get_entity_list();
gCurrentShadowListPtr = get_shadow_list();
entity_numEntities = 0;
entity_numShadows = 0;
}
void reload_world_entity_data(void) {
s32 i;
s32 totalSize = 0;
s32 temp1;
s32 dataLength;
void* gfxData;
void* animData;
for (i = 0; i < MAX_ENTITIES; i++) {
EntityBlueprint* bp = wEntityBlueprint[i];
if (bp == NULL) {
break;
}
if (!(bp->flags & ENTITY_FLAG_HAS_ANIMATED_MODEL)) {
void* gfxData;
dataLength = ((bp->dma.end - bp->dma.start) >> 2);
gfxData = (void*)(gEntityHeapBase - totalSize * 4 - dataLength * 4);
totalSize += dma_copy(bp->dma.start, bp->dma.end, gfxData) >> 2;
} else {
DmaEntry* dmaList = bp->dmaList;
if (bp->entityType == ENTITY_TYPE_RESET_MUNCHLESIA) {
gfxData = (void*)gEntityHeapBottom;
temp1 = dma_copy(dmaList[0].start, dmaList[0].end, gfxData) >> 2;
dma_copy(dmaList[1].start, dmaList[1].end, (void*)(gEntityHeapBottom + temp1 * 4)) >> 2;
animData = (void*)(gEntityHeapBottom + temp1 * 4);
entity_swizzle_anim_pointers(bp, animData, gfxData);
} else {
s32 temp5;
s32 q;
dataLength = ((dmaList[0].end - dmaList[0].start) >> 2);
q = gEntityHeapBase - totalSize * 4;
gfxData = (void*)(q - dataLength * 4);
totalSize += dma_copy(dmaList[0].start, dmaList[0].end, gfxData) >> 2;
dataLength = ((dmaList[1].end - dmaList[1].start) >> 2);
q = gEntityHeapBase - totalSize * 4;
animData = (void*)(q - dataLength * 4);
totalSize += dma_copy(dmaList[1].start, dmaList[1].end, animData) >> 2;
entity_swizzle_anim_pointers(bp, animData, gfxData);
}
}
}
}
void entity_swizzle_anim_pointers(EntityBlueprint* entityData, void* baseAnim, void* baseGfx) {
StaticAnimatorNode* node;
s32* ptr = (s32*)((s32)baseAnim + (s32)entityData->modelAnimationNodes);
while (TRUE) {
if (*ptr == -1) {
*ptr = 0;
return;
}
node = (StaticAnimatorNode*)((s32)baseAnim + ((*ptr) & 0xFFFF));
*ptr++ = (s32)node;
if ((s32)node->displayList != -1) {
node->displayList = (Gfx*)((s32)baseGfx + ((s32)(node->displayList) & 0xFFFF));
} else {
node->displayList = NULL;
}
if ((s32)node->sibling != -1) {
node->sibling = (StaticAnimatorNode*)((s32)baseAnim + ((s32)(node->sibling) & 0xFFFF));
} else {
node->sibling = NULL;
}
if ((s32)node->child != -1) {
node->child = (StaticAnimatorNode*)((s32)baseAnim + ((s32)(node->child) & 0xFFFF));
} else {
node->child = NULL;
}
if ((s32)node->vtxList != -1) {
node->vtxList = (Vtx*)((s32)baseGfx + ((s32)(node->vtxList) & 0xFFFFF));
} else {
node->vtxList = NULL;
}
}
}
s32 is_entity_data_loaded(Entity* entity, EntityBlueprint* blueprint, s32* loadedStart, s32* loadedEnd) {
EntityBlueprint** blueprints;
s32 i;
s32 ret;
s32 size;
DmaEntry* entDmaList;
*loadedStart = 0;
*loadedEnd = 0;
ret = FALSE;
if (!gGameStatusPtr->isBattle) {
blueprints = wEntityBlueprint;
} else {
blueprints = bEntityBlueprint;
}
for (i = 0; i < MAX_ENTITIES; i++, blueprints++) {
EntityBlueprint* bp = *blueprints;
if (bp == NULL) {
blueprints[0] = blueprint;
blueprints[1] = NULL;
ret = TRUE;
if (blueprint->flags & ENTITY_FLAG_HAS_ANIMATED_MODEL) {
s32 size;
entDmaList = blueprint->dmaList;
size = (entDmaList[0].end - entDmaList[0].start) >> 2;
*loadedEnd = *loadedStart + size;
}
break;
} else {
DmaEntry* bpDmaList = bp->dmaList;
do {} while (0); // TODO find better match
entDmaList = blueprint->dmaList;
if (bpDmaList == entDmaList) {
if (blueprint->flags & ENTITY_FLAG_HAS_ANIMATED_MODEL) {
s32 size = (bpDmaList[0].end - bpDmaList[0].start) >> 2;
*loadedEnd = *loadedStart + size;
}
break;
} else if (bp == blueprint) {
if (bp->flags & ENTITY_FLAG_HAS_ANIMATED_MODEL) {
s32 size = (entDmaList[0].end - entDmaList[0].start) >> 2;
*loadedEnd = *loadedStart + size;
}
break;
} else {
if (bp->flags & ENTITY_FLAG_HAS_ANIMATED_MODEL) {
s32 size = (bpDmaList[0].end - bpDmaList[0].start) >> 2;
*loadedEnd = *loadedStart = *loadedStart + size;
size = (bpDmaList[1].end - bpDmaList[1].start) >> 2;
*loadedStart = *loadedStart + size;
} else {
*loadedStart += (bp->dma.end - bp->dma.start) >> 2;
}
}
}
}
return ret;
}
void load_simple_entity_data(Entity* entity, EntityBlueprint* bp, s32 listIndex) {
s32 loadedStart;
s32 loadedEnd;
s32 entitySize;
u32 temp;
s32 totalSize;
entity->vertexSegment = 0xA;
if (!gGameStatusPtr->isBattle) {
totalSize = wEntityDataLoadedSize;
} else {
totalSize = bEntityDataLoadedSize;
}
if (is_entity_data_loaded(entity, bp, &loadedStart, &loadedEnd)) {
if (totalSize + ((bp->dma.end - bp->dma.start) >> 2) > 0x5FFCU) {
get_entity_type(entity->listIndex);
get_entity_type(entity->listIndex);
PANIC();
}
entitySize = (bp->dma.end - bp->dma.start) >> 2;
entity->gfxBaseAddr = (void*)(gEntityHeapBase - totalSize * 4 - entitySize * 4);
totalSize += dma_copy(bp->dma.start, bp->dma.end, entity->gfxBaseAddr) >> 2;
get_entity_type(entity->listIndex);
} else {
entitySize = (bp->dma.end - bp->dma.start) >> 2;
entity->gfxBaseAddr = (void*)(gEntityHeapBase - loadedStart * 4 - entitySize * 4);
get_entity_type(entity->listIndex);
}
if (!gGameStatusPtr->isBattle) {
wEntityDataLoadedSize = totalSize;
} else {
bEntityDataLoadedSize = totalSize;
}
}
void load_split_entity_data(Entity* entity, EntityBlueprint* entityData, s32 listIndex) {
s32 swizzlePointers = FALSE;
s32 loadedStart, loadedEnd;
void* animBaseAddr;
s16* animationScript;
StaticAnimatorNode** animationNodes;
s32 specialSize;
s32 dma1size;
s32 dma2size_1;
s32 dma2size_2;
s32 totalLoaded;
if (entityData->flags & ENTITY_FLAG_HAS_ANIMATED_MODEL) {
DmaEntry* dmaList = entityData->dmaList;
entity->vertexSegment = 0xA;
switch (entityData->entityType) {
case ENTITY_TYPE_RESET_MUNCHLESIA:
case ENTITY_TYPE_MUNCHLESIA_ENVELOP:
case ENTITY_TYPE_MUNCHLESIA_CHEWING:
case ENTITY_TYPE_MUNCHLESIA_RESET1:
specialSize = 0x1000;
break;
case ENTITY_TYPE_MUNCHLESIA_GRAB:
case ENTITY_TYPE_MUNCHLESIA_BEGIN_CHEW:
case ENTITY_TYPE_MUNCHLESIA_SPIT_OUT:
case ENTITY_TYPE_MUNCHLESIA_RESET2:
specialSize = 0x2BC0;
break;
default:
specialSize = 0;
break;
}
if (specialSize != 0) {
if (entityData->entityType == ENTITY_TYPE_RESET_MUNCHLESIA) {
is_entity_data_loaded(entity, entityData, &loadedStart, &loadedEnd);
}
specialSize -= 0x1000;
dma1size = dma_copy(dmaList[0].start, dmaList[0].end, (void*)(gEntityHeapBottom + specialSize * 4)) / 4;
entity->gfxBaseAddr = (void*)(gEntityHeapBottom + specialSize * 4);
dma_copy(dmaList[1].start, dmaList[1].end, (void*)(gEntityHeapBottom + specialSize * 4 + dma1size * 4));
animBaseAddr = (void*)(gEntityHeapBottom + specialSize * 4 + dma1size * 4);
swizzlePointers = TRUE;
} else if (is_entity_data_loaded(entity, entityData, &loadedStart, &loadedEnd)) {
if (!gGameStatusPtr->isBattle) {
totalLoaded = wEntityDataLoadedSize;
} else {
totalLoaded = bEntityDataLoadedSize;
}
if ((totalLoaded + ((dmaList[0].end - dmaList[0].start) >> 2)) > 0x5FFCU) {
get_entity_type(entity->listIndex);
PANIC();
}
if ((totalLoaded + ((dmaList[1].end - dmaList[1].start) >> 2)) > 0x5FFCU) {
get_entity_type(entity->listIndex);
PANIC();
}
dma2size_1 = dma_copy(dmaList[0].start, dmaList[0].end, dmaList[0].start + ((gEntityHeapBase - totalLoaded * 4 - (s32)dmaList[0].end) >> 2) * 4) >> 2;
entity->gfxBaseAddr = (void*)(gEntityHeapBase - totalLoaded * 4 - dma2size_1 * 4);
totalLoaded += dma2size_1;
dma2size_2 = dma_copy(dmaList[1].start, dmaList[1].end, dmaList[1].start + ((gEntityHeapBase - totalLoaded * 4 - (s32)dmaList[1].end) >> 2) * 4) >> 2;
animBaseAddr = (void*)(gEntityHeapBase - totalLoaded * 4 - dma2size_2 * 4);
totalLoaded += dma2size_2;
get_entity_type(entity->listIndex);
if (!gGameStatusPtr->isBattle) {
wEntityDataLoadedSize = totalLoaded;
} else {
bEntityDataLoadedSize = totalLoaded;
}
swizzlePointers = TRUE;
} else {
u32 temp = (dmaList[0].end - dmaList[0].start) >> 2;
entity->gfxBaseAddr = (void*)(gEntityHeapBase - loadedStart * 4 - temp * 4);
temp = (dmaList[1].end - dmaList[1].start) >> 2;
animBaseAddr = (void*)(gEntityHeapBase - loadedEnd * 4 - temp * 4);
get_entity_type(entity->listIndex);
}
} else {
entity->virtualModelIndex = create_model_animator(entityData->renderCommandList);
load_model_animator_tree(entity->virtualModelIndex, entityData->modelAnimationNodes);
update_model_animator(entity->virtualModelIndex);
return;
}
animationScript = entityData->renderCommandList;
animationNodes = (StaticAnimatorNode**)((s32)animBaseAddr + (s32)entityData->modelAnimationNodes);
if (swizzlePointers) {
entity_swizzle_anim_pointers(entityData, animBaseAddr, entity->gfxBaseAddr);
}
entity->virtualModelIndex = create_mesh_animator(animationScript, animBaseAddr);
load_mesh_animator_tree(entity->virtualModelIndex, animationNodes);
update_model_animator(entity->virtualModelIndex);
entity->flags |= ENTITY_FLAG_HAS_ANIMATED_MODEL;
}
s32 func_80111790(EntityBlueprint* data) {
s32 i;
for (i = 0; i < ARRAY_COUNT(*gCurrentEntityListPtr); i++) {
Entity* entity = (*gCurrentEntityListPtr)[i];
if (entity != NULL && entity->blueprint->dma.start != NULL) {
if (entity->blueprint->dma.start == entity->blueprint) {
return TRUE;
}
}
}
return FALSE;
}
void entity_free_static_data(EntityBlueprint* data) {
s32 freeSlot;
s32 size;
EntityBlueprint* bp;
for (freeSlot = 0; freeSlot < MAX_ENTITIES; freeSlot++) {
bp = wEntityBlueprint[freeSlot];
if (bp == NULL) {
break;
}
}
if (freeSlot < MAX_ENTITIES) {
bp = wEntityBlueprint[freeSlot - 1];
if (bp == data) {
if (bp->flags & ENTITY_FLAG_HAS_ANIMATED_MODEL) {
DmaEntry* dmaList = bp->dmaList;
size = ((dmaList[0].end - dmaList[0].start) >> 2);
size += ((dmaList[1].end - dmaList[1].start) >> 2);
if (!func_80111790(bp)) {
wEntityBlueprint[freeSlot - 1] = NULL;
wEntityDataLoadedSize -= size;
}
} else {
size = (bp->dma.end - bp->dma.start) >> 2;
if (!func_80111790(bp)) {
wEntityBlueprint[freeSlot - 1] = NULL;
wEntityDataLoadedSize -= size;
}
}
}
}
}
s32 create_entity(EntityBlueprint* bp, ...) {
va_list ap;
EntityBlueprint** bpPtr;
f32 x, y, z;
f32 rotY;
s32 listIndex;
Entity* entity;
s32* args;
va_start(ap, bp);
// needed to match
bpPtr = &bp;
*bpPtr = bp;
load_area_specific_entity_data();
x = va_arg(ap, s32);
y = va_arg(ap, s32);
z = va_arg(ap, s32);
rotY = va_arg(ap, s32);
args = &CreateEntityVarArgBuffer[2];
*args-- = 0;
*args-- = 0;
*args = 0;
for (listIndex = 3; listIndex > 0; listIndex--) {
s32 arg = va_arg(ap, s32);
if (arg == MAKE_ENTITY_END) {
break;
}
*args++ = arg;
}
va_end(ap);
for (listIndex = 0; listIndex < ARRAY_COUNT(*gCurrentEntityListPtr); listIndex++) {
if ((*gCurrentEntityListPtr)[listIndex] == NULL) {
break;
}
}
if (listIndex >= MAX_ENTITIES) {
return -1;
}
(*gCurrentEntityListPtr)[listIndex] = entity = heap_malloc(sizeof(*entity));
mem_clear(entity, sizeof(*entity));
entity->dataBuf.any = NULL;
if (bp->typeDataSize != 0) {
entity->dataBuf.any = heap_malloc(bp->typeDataSize);
mem_clear(entity->dataBuf.any, bp->typeDataSize);
}
entity->type = bp->entityType;
entity->listIndex = listIndex;
entity->boundScript = NULL;
entity->updateMatrixOverride = NULL;
entity->blueprint = bp;
entity->scriptReadPos = bp->updateEntityScript;
entity->scriptDelay = entity->scriptReadPos != NULL ? 1 : 0;
entity->savedReadPos[0] = bp->updateEntityScript;
entity->updateScriptCallback = NULL;
entity->flags = bp->flags | ENTITY_FLAG_CREATED;
entity->collisionFlags = 0;
entity->collisionTimer = 0;
entity->renderSetupFunc = NULL;
entity->pos.x = x;
entity->pos.y = y;
entity->pos.z = z;
entity->rot.x = 0.0f;
entity->rot.y = rotY;
entity->rot.z = 0.0f;
entity->scale.x = 1.0f;
entity->scale.y = 1.0f;
entity->scale.z = 1.0f;
entity->aabb.x = bp->aabbSize[0];
entity->aabb.y = bp->aabbSize[1];
entity->aabb.z = bp->aabbSize[2];
entity->unk_05 = 1;
entity->unk_08 = -1;
entity->alpha = 255;
entity->virtualModelIndex = -1;
entity->shadowIndex = -1;
entity->gfxBaseAddr = NULL;
if (!(bp->flags & ENTITY_FLAG_HAS_ANIMATED_MODEL)) {
if (bp->dma.start != 0) {
load_simple_entity_data(entity, bp, listIndex);
}
if (bp->renderCommandList != NULL) {
entity->virtualModelIndex = load_entity_model(bp->renderCommandList);
exec_entity_model_commandlist(entity->virtualModelIndex);
}
} else {
load_split_entity_data(entity, bp, listIndex);
}
if (bp->entityType != ENTITY_TYPE_SHADOW && (entity->flags & (ENTITY_FLAG_FIXED_SHADOW_SIZE | ENTITY_FLAG_HAS_SHADOW))) {
create_entity_shadow(entity, x, y, z);
}
switch (bp->entityType) {
case ENTITY_TYPE_BLUE_SWITCH:
case ENTITY_TYPE_RED_SWITCH:
case ENTITY_TYPE_SIMPLE_SPRING:
case ENTITY_TYPE_SCRIPT_SPRING:
case ENTITY_TYPE_STAR_BOX_LAUNCHER:
entity->flags |= ENTITY_FLAG_4000;
break;
}
if (bp->fpInit != NULL) {
bp->fpInit(entity);
}
update_entity_transform_matrix(entity);
return entity->listIndex;
}
s32 create_shadow_from_data(ShadowBlueprint* bp, f32 x, f32 y, f32 z) {
Shadow* shadow;
s32 i;
for (i = 0; i < ARRAY_COUNT(*gCurrentShadowListPtr); i++) {
if ((*gCurrentShadowListPtr)[i] == NULL) {
break;
}
}
ASSERT(i < ARRAY_COUNT(*gCurrentShadowListPtr));
shadow = heap_malloc(sizeof(*shadow));
(*gCurrentShadowListPtr)[i] = shadow;
mem_clear(shadow, sizeof(*shadow));
shadow->listIndex = i;
shadow->flags = bp->flags | ENTITY_FLAG_CREATED;
shadow->alpha = 128;
shadow->unk_06 = 0x80;
shadow->pos.x = x;
shadow->pos.y = y;
shadow->pos.z = z;
shadow->scale.x = 1.0f;
shadow->scale.y = 1.0f;
shadow->scale.z = 1.0f;
if (bp->animModelNode != NULL) {
shadow->flags |= ENTITY_FLAG_HAS_ANIMATED_MODEL;
shadow->entityModelID = create_model_animator(bp->renderCommandList);
load_model_animator_tree(shadow->entityModelID, bp->animModelNode);
} else {
shadow->entityModelID = load_entity_model(bp->renderCommandList);
}
if (bp->onCreateCallback != NULL) {
bp->onCreateCallback(shadow);
}
update_shadow_transform_matrix(shadow);
return shadow->listIndex;
}
API_CALLABLE(MakeEntity) {
Bytecode* args = script->ptrReadPos;
EntityBlueprint* entityData;
s32 x, y, z;
s32 flags;
s32 nextArg;
s32 entityIndex;
s32 endOfArgs;
s32* varArgBufPos;
if (isInitialCall != TRUE) {
return ApiStatus_DONE2;
}
entityData = (EntityBlueprint*)evt_get_variable(script, *args++);
varArgBufPos = &CreateEntityVarArgBuffer[2];
endOfArgs = MAKE_ENTITY_END;
x = evt_get_variable(script, *args++);
y = evt_get_variable(script, *args++);
z = evt_get_variable(script, *args++);
flags = evt_get_variable(script, *args++);
*varArgBufPos-- = 0;
*varArgBufPos-- = 0;
*varArgBufPos = 0;
do {
nextArg = evt_get_variable(script, *args++);
if (nextArg != endOfArgs) {
*varArgBufPos++ = nextArg;
}
} while (nextArg != endOfArgs);
entityIndex = create_entity(entityData, x, y, z, flags, CreateEntityVarArgBuffer[0], CreateEntityVarArgBuffer[1], CreateEntityVarArgBuffer[2], endOfArgs);
gLastCreatedEntityIndex = entityIndex;
script->varTable[0] = entityIndex;
return ApiStatus_DONE2;
}
API_CALLABLE(SetEntityCullMode) {
Entity* entity = get_entity_by_index(gLastCreatedEntityIndex);
Bytecode* args = script->ptrReadPos;
s32 mode = evt_get_variable(script, *args++);
if (mode == 0) {
entity->flags |= ENTITY_FLAG_DRAW_IF_CLOSE_HIDE_MODE1;
} else if (mode == 1) {
entity->flags |= ENTITY_FLAG_DRAW_IF_CLOSE_HIDE_MODE2;
} else if (mode == 2) {
entity->flags |= ENTITY_FLAG_DRAW_IF_CLOSE_HIDE_MODE2 | ENTITY_FLAG_DRAW_IF_CLOSE_HIDE_MODE1;
} else {
entity->flags |= ENTITY_FLAG_IGNORE_DISTANCE_CULLING | ENTITY_FLAG_DRAW_IF_CLOSE_HIDE_MODE2 |
ENTITY_FLAG_DRAW_IF_CLOSE_HIDE_MODE1;
}
return ApiStatus_DONE2;
}
API_CALLABLE(UseDynamicShadow) {
Entity* entity = get_entity_by_index(gLastCreatedEntityIndex);
Bytecode* args = script->ptrReadPos;
if (evt_get_variable(script, *args++)) {
Shadow* shadow;
entity->flags |= ENTITY_FLAG_HAS_DYNAMIC_SHADOW;
shadow = get_shadow_by_index(entity->shadowIndex);
shadow->flags |= ENTITY_FLAG_SHADOW_POS_DIRTY;
} else {
entity->flags &= ~ENTITY_FLAG_HAS_DYNAMIC_SHADOW;
}
return ApiStatus_DONE2;
}
API_CALLABLE(AssignScript) {
Bytecode* args = script->ptrReadPos;
if (isInitialCall == TRUE) {
EvtScript* toBind = (EvtScript*)evt_get_variable(script, *args++);
get_entity_by_index(gLastCreatedEntityIndex)->boundScriptBytecode = toBind;
return ApiStatus_DONE2;
}
return ApiStatus_DONE1;
}
API_CALLABLE(AssignSwitchFlag) {
Bytecode* args = script->ptrReadPos;
if (isInitialCall == TRUE) {
s32 areaFlag = evt_get_variable(script, *args++);
Entity* entity = get_entity_by_index(gLastCreatedEntityIndex);
SwitchData* data = entity->dataBuf.swtch;
data->areaFlagIndex = areaFlag;
if (get_area_flag(areaFlag) != 0) {
entity->flags |= ENTITY_FLAG_PENDING_INSTANCE_DELETE;
}
return ApiStatus_DONE2;
}
return ApiStatus_DONE1;
}
API_CALLABLE(AssignBlockFlag) {
Bytecode* args = script->ptrReadPos;
if (isInitialCall == TRUE) {
s32 index = evt_get_variable_index(script, *args++);
BlockData* data = get_entity_by_index(gLastCreatedEntityIndex)->dataBuf.block;
data->gameFlagIndex = index;
return ApiStatus_DONE2;
}
return ApiStatus_DONE1;
}
API_CALLABLE(AssignChestFlag) {
Bytecode* args = script->ptrReadPos;
if (isInitialCall == TRUE) {
ChestData* data = get_entity_by_index(gLastCreatedEntityIndex)->dataBuf.chest;
data->gameFlagIndex = evt_get_variable_index(script, *args);
return ApiStatus_DONE2;
}
return ApiStatus_DONE1;
}
API_CALLABLE(AssignPanelFlag) {
Bytecode* args = script->ptrReadPos;
if (isInitialCall == TRUE) {
HiddenPanelData* data = get_entity_by_index(gLastCreatedEntityIndex)->dataBuf.hiddenPanel;
data->pickupVar = evt_get_variable_index(script, *args++);
return ApiStatus_DONE2;
}
return ApiStatus_DONE1;
}
API_CALLABLE(AssignCrateFlag) {
Bytecode* args = script->ptrReadPos;
if (isInitialCall == TRUE) {
WoodenCrateData* data = get_entity_by_index(gLastCreatedEntityIndex)->dataBuf.crate;
data->globalFlagIndex = evt_get_variable_index(script, *args++);
return ApiStatus_DONE2;
}
return ApiStatus_DONE1;
}
s32 create_entity_shadow(Entity* entity, f32 x, f32 y, f32 z) {
u16 bpFlags = entity->blueprint->flags;
s32 type;
s16 shadowIndex;
if (bpFlags & ENTITY_FLAG_FIXED_SHADOW_SIZE) {
type = (bpFlags & ENTITY_FLAG_CIRCULAR_SHADOW) ? SHADOW_FIXED_CIRCLE : SHADOW_FIXED_SQUARE;
} else {
type = (bpFlags & ENTITY_FLAG_CIRCULAR_SHADOW) ? SHADOW_VARYING_CIRCLE : SHADOW_VARYING_SQUARE;
}
shadowIndex = create_shadow_type(type, x, y, z);
entity->shadowIndex = shadowIndex;
get_shadow_by_index(shadowIndex)->flags |= ENTITY_FLAG_DARK_SHADOW | ENTITY_FLAG_SHADOW_POS_DIRTY;
return entity->shadowIndex;
}
s32 create_shadow_type(s32 type, f32 x, f32 y, f32 z) {
s32 isFixedSize = FALSE;
ShadowBlueprint* bp = &CircularShadowA;
s32 shadowIndex;
switch (type) {
case SHADOW_FIXED_CIRCLE:
isFixedSize = TRUE;
case SHADOW_VARYING_CIRCLE:
bp = &CircularShadowA;
break;
case SHADOW_FIXED_SQUARE:
isFixedSize = TRUE;
case SHADOW_VARYING_SQUARE:
bp = &SquareShadow;
break;
case SHADOW_FIXED_ALT_CIRCLE:
isFixedSize = TRUE;
case SHADOW_VARYING_ALT_CIRCLE:
bp = &CircularShadowB;
break;
}
shadowIndex = create_shadow_from_data(bp, x, y, z);
if (isFixedSize) {
get_shadow_by_index(shadowIndex)->flags |= ENTITY_FLAG_FIXED_SHADOW_SIZE;
}
return shadowIndex;
}
void delete_shadow(s32 shadowIndex) {
_delete_shadow(shadowIndex);
}
void update_entity_shadow_position(Entity* entity) {
Shadow* shadow = get_shadow_by_index(entity->shadowIndex);
if (shadow != NULL) {
f32 rayX;
f32 rayY;
f32 rayZ;
f32 hitYaw;
f32 hitPitch;
f32 hitLength;
f32 origHitLength;
if (entity->alpha < 255) {
shadow->alpha = entity->alpha / 2;
} else {
u8 alphaTemp;
if (shadow->flags & ENTITY_FLAG_DARK_SHADOW) {
alphaTemp = 160;
} else {
alphaTemp = 128;
}
shadow->alpha = alphaTemp;
}
if (!(entity->flags & ENTITY_FLAG_HAS_DYNAMIC_SHADOW)) {
if (shadow->flags & ENTITY_FLAG_SHADOW_POS_DIRTY) {
shadow->flags &= ~ENTITY_FLAG_SHADOW_POS_DIRTY;
} else {
return;
}
}
rayX = entity->pos.x;
rayY = entity->pos.y;
rayZ = entity->pos.z;
if (!entity_raycast_down(&rayX, &rayY, &rayZ, &hitYaw, &hitPitch, &hitLength) && hitLength == 32767.0f) {
hitLength = 0.0f;
}
origHitLength = hitLength;
if (shadow->flags & ENTITY_FLAG_FIXED_SHADOW_SIZE) {
hitLength = 212.5f;
shadow->scale.x = entity->aabb.x / hitLength;
shadow->scale.z = entity->aabb.z / hitLength;
} else {
hitLength = ((hitLength / 150.0f) + 0.95) * 250.0;
shadow->scale.x = (entity->aabb.x / hitLength) * entity->scale.x;
shadow->scale.z = (entity->aabb.z / hitLength) * entity->scale.z;
}
shadow->pos.x = entity->pos.x;
shadow->pos.z = entity->pos.z;
shadow->pos.y = rayY;
entity->shadowPosY = rayY;
shadow->rot.x = hitYaw;
shadow->rot.z = hitPitch;
shadow->rot.y = entity->rot.y;
if (entity->pos.y < rayY) {
shadow->flags |= ENTITY_FLAG_SKIP_UPDATE;
entity->pos.y = rayY + 10.0f;
} else {
shadow->flags &= ~ENTITY_FLAG_SKIP_UPDATE;
}
shadow->flags = (shadow->flags & ~ENTITY_FLAG_HIDDEN) | ((u16)entity->flags & ENTITY_FLAG_HIDDEN);
if (!(entity->flags & ENTITY_FLAG_400) && origHitLength == 0.0f) {
shadow->flags |= ENTITY_FLAG_HIDDEN;
}
} else {
entity->shadowPosY = 0.0f;
}
}
s32 entity_raycast_down(f32* x, f32* y, f32* z, f32* hitYaw, f32* hitPitch, f32* hitLength) {
f32 hitX, hitY, hitZ;
f32 hitDepth;
f32 hitNx, hitNy, hitNz;
s32 entityID;
s32 colliderID;
s32 hitID;
s32 ret;
hitDepth = 32767.0f;
*hitLength = 32767.0f;
entityID = test_ray_entities(*x, *y, *z, 0.0f, -1.0f, 0.0f, &hitX, &hitY, &hitZ, &hitDepth, &hitNx, &hitNy, &hitNz);
hitID = -1;
ret = FALSE;
if ((entityID >= 0) && ((get_entity_type(entityID) != ENTITY_TYPE_PUSH_BLOCK) || (hitNx == 0.0f && hitNz == 0.0f && hitNy == 1.0))) {
hitID = entityID | COLLISION_WITH_ENTITY_BIT;
}
colliderID = test_ray_colliders(COLLIDER_FLAG_IGNORE_PLAYER, *x, *y, *z, 0.0f, -1.0f, 0.0f, &hitX, &hitY, &hitZ, &hitDepth, &hitNx,
&hitNy, &hitNz);
if (colliderID >= 0) {
hitID = colliderID;
}
if (hitID >= 0) {
*hitLength = hitDepth;
*y = hitY;
*hitYaw = -atan2(0.0f, 0.0f, hitNz * 100.0f, hitNy * 100.0f);
*hitPitch = -atan2(0.0f, 0.0f, hitNx * 100.0f, hitNy * 100.0f);
ret = TRUE;
} else {
*hitYaw = 0.0f;
*hitPitch = 0.0f;
}
return ret;
}
void set_standard_shadow_scale(Shadow* shadow, f32 height) {
if (!gGameStatusPtr->isBattle) {
shadow->scale.x = 0.13 - (height / 2600.0f);
} else {
shadow->scale.x = 0.12 - (height / 3600.0f);
}
if (shadow->scale.x < 0.01) {
shadow->scale.x = 0.01f;
}
shadow->scale.z = shadow->scale.x;
}
void set_npc_shadow_scale(Shadow* shadow, f32 height, f32 npcRadius) {
if (!gGameStatusPtr->isBattle) {
shadow->scale.x = 0.13 - (height / 2600.0f);
} else {
shadow->scale.x = 0.12 - (height / 3600.0f);
}
if (shadow->scale.x < 0.01) {
shadow->scale.x = 0.01f;
}
if (npcRadius > 60.0f) {
shadow->scale.z = shadow->scale.x * 2.0f;
} else {
shadow->scale.z = shadow->scale.x;
}
}
void set_peach_shadow_scale(Shadow* shadow, f32 scale) {
PlayerStatus* playerStatus = &gPlayerStatus;
f32 phi_f2 = 0.12f;
if (!gGameStatusPtr->isBattle) {
switch (playerStatus->anim) {
case ANIM_Peach2_Carried:
case ANIM_Peach2_Thrown:
case ANIM_Peach2_Land:
case ANIM_Peach3_TiedSideways:
shadow->scale.x = 0.26f - (scale / 2600.0f);
if (shadow->scale.x < 0.01) {
shadow->scale.x = 0.01f;
}
shadow->scale.z = 0.13f - (scale / 2600.0f);
if (shadow->scale.z < 0.01) {
shadow->scale.z = 0.01f;
}
return;
}
phi_f2 = 0.16f;
}
shadow->scale.x = phi_f2 - (scale / 3600.0f);
if (shadow->scale.x < 0.01) {
shadow->scale.x = 0.01f;
}
shadow->scale.z = shadow->scale.x;
}
s32 is_block_on_ground(Entity* block) {
f32 x = block->pos.x;
f32 y = block->pos.y;
f32 z = block->pos.z;
f32 hitYaw;
f32 hitPitch;
f32 hitLength;
s32 ret;
entity_raycast_down(&x, &y, &z, &hitYaw, &hitPitch, &hitLength);
ret = hitLength;
if (ret == 32767) {
ret = FALSE;
}
return ret;
}