mm/src/code/z_snap.c

204 lines
6.5 KiB
C

#include "global.h"
#include "z64snap.h"
#include "overlays/actors/ovl_En_Kakasi/z_en_kakasi.h"
#define PICTO_SEEN_IN_SCENE 1
#define PICTO_SEEN_ANYWHERE 2
/**
* Test every loaded actor to see if it is a pictographable `PictoActor`, and if so, run its `validationFunc` to set
* appropriate flags.
*
* @return s32 Number of pictograph actors validly captured.
*/
s32 Snap_RecordPictographedActors(PlayState* play) {
PictoActor* pictoActor;
Actor* actor;
s32 category = 0;
s32 seen;
s32 validCount = 0;
gSaveContext.save.pictoFlags0 = 0;
gSaveContext.save.pictoFlags1 = 0;
if (play->sceneNum == SCENE_20SICHITAI) {
Snap_SetFlag(PICTOGRAPH_IN_SWAMP);
}
for (; category < ACTORCAT_MAX; category++) {
for (actor = play->actorCtx.actorLists[category].first; actor != NULL; actor = actor->next) {
seen = 0;
// Actors which must be pictographed in a specific scene
switch (play->sceneNum) {
case SCENE_20SICHITAI:
if ((actor->id == ACTOR_EN_MNK) || (actor->id == ACTOR_EN_BIGOKUTA)) {
seen = PICTO_SEEN_IN_SCENE;
}
break;
default:
seen = 0;
break;
}
if (actor->id) {
; // Needed to match
}
// Actors which may be pictographed anywhere
switch (actor->id) {
case ACTOR_EN_KAKASI:
if (GET_KAKASI_ABOVE_GROUND(actor) == 1) {
seen |= PICTO_SEEN_ANYWHERE;
break; //! @bug break is inside conditional, meaning it falls through if it is false
}
// FALLTHROUGH
case ACTOR_EN_ZOV:
seen |= PICTO_SEEN_ANYWHERE;
break;
case ACTOR_EN_BAL:
seen |= PICTO_SEEN_ANYWHERE;
break;
case ACTOR_EN_DNQ:
seen |= PICTO_SEEN_ANYWHERE;
break;
case ACTOR_EN_GE1:
case ACTOR_EN_GE3:
case ACTOR_EN_KAIZOKU:
case ACTOR_EN_GE2:
seen |= PICTO_SEEN_ANYWHERE;
break;
}
// If actor is recordable, run its validity function and record if valid
if (seen) {
pictoActor = (PictoActor*)actor;
if (pictoActor->validationFunc != NULL) {
if ((pictoActor->validationFunc)(play, actor) == 0) {
validCount++;
}
}
}
}
}
return validCount;
}
// Only used in this file
void Snap_SetFlag(s32 flag) {
if (flag < 0x20) {
gSaveContext.save.pictoFlags0 |= (1 << flag);
} else {
flag &= 0x1F;
gSaveContext.save.pictoFlags1 |= (1 << flag);
}
}
// Unused
void Snap_UnsetFlag(s32 flag) {
if (flag < 0x20) {
gSaveContext.save.pictoFlags0 &= ~(1 << flag);
} else {
flag &= 0x1F;
gSaveContext.save.pictoFlags1 &= ~(1 << flag);
}
}
u32 Snap_CheckFlag(s32 flag) {
SaveContext* saveCtx = &gSaveContext;
if (flag < 0x20) {
return saveCtx->save.pictoFlags0 & (1 << flag);
} else {
flag &= 0x1F;
return saveCtx->save.pictoFlags1 & (1 << flag);
}
}
s16 Snap_AbsS(s16 val) {
return ABS(val);
}
/**
* Test if a pictograph is a valid pictograph of an actor
*
* @param play
* @param actor Actor to test
* @param flag Flag to set if checks successful
* @param pos position of point to test (generally a particular point in the actor's model)
* @param rot rotation of point to test (for facing camera)
* @param distanceMin closest point may be
* @param distanceMax farthest away point may be
* @param angleRange Size of range that counts as facing the camera (-1 is used for any allowed)
* @return s32 0 on success, `or`ed combination of the validity flag indices if not
*
* @note It is generally not possible to recover the actual failure mode(s) from the return value: oring `ret` with the
* actual flag rather than its index would rectify this.
*/
s32 Snap_ValidatePictograph(PlayState* play, Actor* actor, s32 flag, Vec3f* pos, Vec3s* rot, f32 distanceMin,
f32 distanceMax, s16 angleRange) {
Vec3f projectedPos;
s16 x;
s16 y;
f32 distance;
CollisionPoly* poly;
Camera* camera = GET_ACTIVE_CAM(play);
Actor* actors[2];
s32 ret = 0;
s32 bgId;
// Check distance
distance = OLib_Vec3fDist(pos, &camera->eye);
if ((distance < distanceMin) || (distanceMax < distance)) {
Snap_SetFlag(PICTOGRAPH_BAD_DISTANCE);
ret = PICTOGRAPH_BAD_DISTANCE;
}
// Check rot is facing camera?
x = Snap_AbsS(Camera_GetCamDirPitch(camera) + rot->x);
y = Snap_AbsS(Camera_GetCamDirYaw(camera) - BINANG_SUB(rot->y, 0x7FFF));
if ((0 < angleRange) && ((angleRange < x) || (angleRange < y))) {
Snap_SetFlag(PICTOGRAPH_BAD_ANGLE);
ret |= PICTOGRAPH_BAD_ANGLE;
}
// Check in capture region
Actor_GetProjectedPos(play, pos, &projectedPos, &distance);
// Convert to projected position to device coordinates, shift to be relative to the capture region's top-left corner
x = (s16)PROJECTED_TO_SCREEN_X(projectedPos, distance) - PICTO_CAPTURE_REGION_TOPLEFT_X;
y = (s16)PROJECTED_TO_SCREEN_Y(projectedPos, distance) - PICTO_CAPTURE_REGION_TOPLEFT_Y;
// checks if the coordinates are within the capture region
if ((x < 0) || (x > PICTO_RESOLUTION_HORIZONTAL) || (y < 0) || (y > PICTO_RESOLUTION_VERTICAL)) {
Snap_SetFlag(PICTOGRAPH_NOT_IN_VIEW);
ret |= PICTOGRAPH_NOT_IN_VIEW;
}
// Check not obscured by bg collision
if (BgCheck_ProjectileLineTest(&play->colCtx, pos, &camera->eye, &projectedPos, &poly, true, true, true, true,
&bgId)) {
Snap_SetFlag(PICTOGRAPH_BEHIND_BG);
ret |= PICTOGRAPH_BEHIND_BG;
}
// Check not obscured by actor collision
actors[0] = actor;
actors[1] = &GET_PLAYER(play)->actor;
if (CollisionCheck_LineOCCheck(play, &play->colChkCtx, pos, &camera->eye, actors, 2)) {
Snap_SetFlag(PICTOGRAPH_BEHIND_COLLISION);
ret |= PICTOGRAPH_BEHIND_COLLISION;
}
// If all of the above checks pass, set the flag
if (ret == 0) {
Snap_SetFlag(flag);
}
return ret;
}