papermario/src/effects.c

343 lines
12 KiB
C

#include "common.h"
#include "effects.h"
#include "ld_addrs.h"
typedef s32 TlbEntry[0x1000 / 4];
typedef TlbEntry TlbMappablePage[15];
SHIFT_BSS EffectGraphics gEffectGraphicsData[15];
SHIFT_BSS EffectInstance* gEffectInstances[96];
extern TlbMappablePage D_80197000;
extern Addr D_801A6000;
#define FX_ENTRY(name, gfx_name) { \
name##_main, effect_##name##_ROM_START, effect_##name##_ROM_END, effect_##name##_VRAM, gfx_name##_ROM_START, \
gfx_name##_ROM_END \
}
#include "effects/effect_table.c"
s32 D_8007FEB8[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 5, 3, 4, 13, 60, 0, 512, 0, 0, 3, 0 };
/// Used for unbound function points in effect structs.
void stub_effect_delegate(EffectInstance* effect) {
}
void set_effect_pos_offset(EffectInstance* effect, f32 x, f32 y, f32 z) {
s32* data = effect->data.any;
((f32*)data)[1] = x;
((f32*)data)[2] = y;
((f32*)data)[3] = z;
}
void clear_effect_data(void) {
s32 i;
for (i = 0; i < ARRAY_COUNT(gEffectGraphicsData); i++) {
gEffectGraphicsData[i].flags = 0;
}
for (i = 0; i < ARRAY_COUNT(gEffectInstances); i++) {
gEffectInstances[i] = NULL;
}
osUnmapTLBAll();
osMapTLB(0x10, NULL, _325AD0_VRAM, (s32)&D_801A6000 & 0xFFFFFF, -1, -1);
DMA_COPY_SEGMENT(_325AD0);
}
void func_80059D48(void) {
}
void update_effects(void) {
if (!(gOverrideFlags & (GLOBAL_OVERRIDES_800 | GLOBAL_OVERRIDES_400))) {
EffectGraphics* effectGraphics;
s32 i;
// reset free delay for each EffectGraphics touched in previous update
for (i = 0, effectGraphics = gEffectGraphicsData; i < ARRAY_COUNT(gEffectGraphicsData); i++, effectGraphics++) {
if (effectGraphics->flags & FX_GRAPHICS_LOADED) {
if (!(effectGraphics->flags & FX_GRAPHICS_CAN_FREE)) {
effectGraphics->flags |= FX_GRAPHICS_CAN_FREE;
effectGraphics->freeDelay = 3;
}
}
}
// update each EffectInstances
for (i = 0; i < ARRAY_COUNT(gEffectInstances); i++) {
EffectInstance* effectInstance = gEffectInstances[i];
if (effectInstance != NULL && (effectInstance->flags & FX_INSTANCE_FLAG_ENABLED)) {
effectInstance->graphics->flags &= ~FX_GRAPHICS_CAN_FREE;
if (gGameStatusPtr->isBattle) {
if (effectInstance->flags & FX_INSTANCE_FLAG_BATTLE) {
effectInstance->graphics->update(effectInstance);
effectInstance->flags |= FX_INSTANCE_FLAG_HAS_UPDATED;
}
} else {
if (!(effectInstance->flags & FX_INSTANCE_FLAG_BATTLE)) {
effectInstance->graphics->update(effectInstance);
effectInstance->flags |= FX_INSTANCE_FLAG_HAS_UPDATED;
}
}
}
}
// free any EffectGraphics which haven't been used recently
for (i = 0, effectGraphics = gEffectGraphicsData; i < ARRAY_COUNT(gEffectGraphicsData); i++, effectGraphics++) {
if (effectGraphics->flags & FX_GRAPHICS_LOADED) {
if (effectGraphics->flags & FX_GRAPHICS_CAN_FREE) {
if (effectGraphics->freeDelay != 0) {
effectGraphics->freeDelay--;
} else {
if (effectGraphics->data != NULL) {
general_heap_free(effectGraphics->data);
effectGraphics->data = NULL;
}
effectGraphics->flags = FX_GRAPHICS_DISABLED;
osUnmapTLB(i);
}
}
}
}
}
}
void render_effects_world(void) {
s32 i;
for (i = 0; i < ARRAY_COUNT(gEffectInstances); i++) {
EffectInstance* effectInstance = gEffectInstances[i];
if (effectInstance != NULL) {
if (effectInstance->flags & FX_INSTANCE_FLAG_ENABLED) {
if (effectInstance->flags & FX_INSTANCE_FLAG_HAS_UPDATED) {
if (gGameStatusPtr->isBattle) {
if (effectInstance->flags & FX_INSTANCE_FLAG_BATTLE) {
effectInstance->graphics->renderWorld(effectInstance);
}
} else {
if (!(effectInstance->flags & FX_INSTANCE_FLAG_BATTLE)) {
effectInstance->graphics->renderWorld(effectInstance);
}
}
}
}
}
}
}
void render_effects_UI(void) {
s32 cond = TRUE;
s32 i;
for (i = 0; i < ARRAY_COUNT(gEffectInstances); i++) {
EffectInstance* effectInstance = gEffectInstances[i];
if (effectInstance != NULL) {
if (effectInstance->flags & FX_INSTANCE_FLAG_ENABLED) {
if (effectInstance->flags & FX_INSTANCE_FLAG_HAS_UPDATED) {
void (*renderUI)(EffectInstance* effect);
if (gGameStatusPtr->isBattle && !(effectInstance->flags & FX_INSTANCE_FLAG_BATTLE)) {
continue;
}
if (!gGameStatusPtr->isBattle && effectInstance->flags & FX_INSTANCE_FLAG_BATTLE) {
continue;
}
renderUI = effectInstance->graphics->renderUI;
if (renderUI != stub_effect_delegate) {
if (cond) {
Camera* camera = &gCameras[gCurrentCameraID];
gDPPipeSync(gMainGfxPos++);
gSPViewport(gMainGfxPos++, &camera->vp);
gSPClearGeometryMode(gMainGfxPos++, G_ZBUFFER | G_SHADE | G_CULL_BOTH | G_FOG |
G_LIGHTING | G_TEXTURE_GEN | G_TEXTURE_GEN_LINEAR | G_LOD |
G_SHADING_SMOOTH | G_CLIPPING | 0x40F9FA);
gSPSetGeometryMode(gMainGfxPos++, G_ZBUFFER | G_SHADE | G_CULL_BACK | G_SHADING_SMOOTH);
gDPSetScissor(gMainGfxPos++, G_SC_NON_INTERLACE,
camera->viewportStartX,
camera->viewportStartY,
camera->viewportStartX + camera->viewportW,
camera->viewportStartY + camera->viewportH);
gSPClipRatio(gMainGfxPos++, FRUSTRATIO_2);
cond = FALSE;
if (!(camera->flags & CAMERA_FLAG_ORTHO)) {
gSPPerspNormalize(gMainGfxPos++, camera->perspNorm);
gSPMatrix(gMainGfxPos++, &gDisplayContext->camPerspMatrix[gCurrentCameraID],
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION);
}
}
renderUI(effectInstance);
}
}
}
}
}
}
EffectInstance* create_effect_instance(EffectBlueprint* effectBp) {
EffectInstance* newEffectInst;
EffectGraphics* effectGraphics;
s32 i;
// Search for an unused instance
for (i = 0; i < ARRAY_COUNT(gEffectInstances); i++) {
if (gEffectInstances[i] == NULL) {
break;
}
}
ASSERT(i < ARRAY_COUNT(gEffectInstances));
// Allocate space for the new instance
gEffectInstances[i] = newEffectInst = general_heap_malloc(sizeof(*newEffectInst));
ASSERT(newEffectInst != NULL);
effectGraphics = &gEffectGraphicsData[0];
newEffectInst->effectIndex = effectBp->effectID;
newEffectInst->flags = FX_INSTANCE_FLAG_ENABLED;
// Look for a loaded effect of the proper index
for (i = 0; i < ARRAY_COUNT(gEffectGraphicsData); i++) {
if ((effectGraphics->flags & FX_GRAPHICS_LOADED) && (effectGraphics->effectIndex == effectBp->effectID)) {
break;
}
effectGraphics++;
}
ASSERT(i < ARRAY_COUNT(gEffectGraphicsData));
// If this is the first new instance of the effect, initialize the function pointers
if (effectGraphics->instanceCounter == 0) {
effectGraphics->update = effectBp->update;
if (effectGraphics->update == NULL) {
effectGraphics->renderWorld = stub_effect_delegate;
}
effectGraphics->renderWorld = effectBp->renderWorld;
// @bug? null check for renderUI instead of renderWorld
if (effectGraphics->renderUI == NULL) {
effectGraphics->renderUI = stub_effect_delegate;
}
effectGraphics->renderUI = effectBp->renderUI;
if (effectGraphics->renderUI == NULL) {
effectGraphics->renderUI = stub_effect_delegate;
}
}
effectGraphics->instanceCounter++;
newEffectInst->graphics = effectGraphics;
if (effectBp->init != NULL) {
effectBp->init(newEffectInst);
}
if (gGameStatusPtr->isBattle) {
newEffectInst->flags |= FX_INSTANCE_FLAG_BATTLE;
}
return newEffectInst;
}
void remove_effect(EffectInstance* effectInstance) {
s32 i;
for (i = 0; i < ARRAY_COUNT(gEffectInstances); i++) {
if (gEffectInstances[i] == effectInstance) {
break;
}
}
ASSERT(i < ARRAY_COUNT(gEffectInstances));
if (effectInstance->data.any == NULL) {
general_heap_free(effectInstance);
gEffectInstances[i] = NULL;
} else {
general_heap_free(effectInstance->data.any);
general_heap_free(effectInstance);
gEffectInstances[i] = NULL;
}
}
void remove_all_effects(void) {
s32 i;
for (i = 0; i < ARRAY_COUNT(gEffectInstances); i++) {
EffectInstance* effect = gEffectInstances[i];
if (effect != NULL && effect->flags & FX_INSTANCE_FLAG_BATTLE) {
if (effect->data.any != NULL) {
general_heap_free(effect->data.any);
}
general_heap_free(effect);
gEffectInstances[i] = NULL;
}
}
}
s32 load_effect(s32 effectIndex) {
EffectTableEntry* effectEntry = &gEffectTable[effectIndex];
EffectGraphics* effectGraphics;
TlbMappablePage* tlbMappablePages;
s32 i;
// Look for a loaded effect matching the desired index
for (i = 0, effectGraphics = &gEffectGraphicsData[0]; i < ARRAY_COUNT(gEffectGraphicsData); i++) {
if (effectGraphics->flags & FX_GRAPHICS_LOADED && effectGraphics->effectIndex == effectIndex) {
break;
}
effectGraphics++;
}
// If an effect was found within the table, initialize it and return
if (i < ARRAY_COUNT(gEffectGraphicsData)) {
effectGraphics->effectIndex = effectIndex;
effectGraphics->instanceCounter = 0;
effectGraphics->flags = FX_GRAPHICS_LOADED;
return 1;
}
// If a loaded effect wasn't found, look for the first empty space
for (i = 0, effectGraphics = &gEffectGraphicsData[0]; i < ARRAY_COUNT(gEffectGraphicsData); i++) {
if (!(effectGraphics->flags & FX_GRAPHICS_LOADED)) {
break;
}
effectGraphics++;
}
// If no empty space was found, panic
ASSERT(i < ARRAY_COUNT(gEffectGraphicsData));
// Map space for the effect
tlbMappablePages = &D_80197000;
osMapTLB(i, 0, effectEntry->dmaDest, (s32)((*tlbMappablePages)[i]) & 0xFFFFFF, -1, -1);
// Copy the effect into the newly mapped space
dma_copy(effectEntry->dmaStart, effectEntry->dmaEnd, effectEntry->dmaDest);
// If there's graphics data for the effect, allocate space and copy into the new space
if (effectEntry->graphicsDmaStart != NULL) {
void* effectDataBuf = general_heap_malloc(effectEntry->graphicsDmaEnd - effectEntry->graphicsDmaStart);
effectGraphics->data = effectDataBuf;
ASSERT(effectDataBuf != NULL);
dma_copy(effectEntry->graphicsDmaStart, effectEntry->graphicsDmaEnd, effectGraphics->data);
}
// Initialize the newly loaded effect data
effectGraphics->effectIndex = effectIndex;
effectGraphics->instanceCounter = 0;
effectGraphics->flags = FX_GRAPHICS_LOADED;
return 1;
}