pokeemerald/src/rotating_tile_puzzle.c

339 lines
13 KiB
C
Raw Normal View History

2018-11-19 17:16:01 +01:00
#include "global.h"
#include "event_object_movement.h"
#include "fieldmap.h"
#include "malloc.h"
#include "rotating_tile_puzzle.h"
2018-11-19 17:16:01 +01:00
#include "script_movement.h"
#include "constants/event_object_movement_constants.h"
2018-12-28 19:29:21 +01:00
#include "constants/event_objects.h"
#include "constants/metatile_labels.h"
2018-11-19 17:16:01 +01:00
2019-10-16 10:09:30 +02:00
extern const u8 RotatingTilePuzzle_Movement_ShiftRight[];
extern const u8 RotatingTilePuzzle_Movement_ShiftDown[];
extern const u8 RotatingTilePuzzle_Movement_ShiftLeft[];
extern const u8 RotatingTilePuzzle_Movement_ShiftUp[];
extern const u8 RotatingTilePuzzle_Movement_FaceRight[];
extern const u8 RotatingTilePuzzle_Movement_FaceDown[];
extern const u8 RotatingTilePuzzle_Movement_FaceLeft[];
extern const u8 RotatingTilePuzzle_Movement_FaceUp[];
#define ROTATE_COUNTERCLOCKWISE 0
#define ROTATE_CLOCKWISE 1
#define ROTATE_NONE 2
struct RotatingTileObject
2018-11-19 17:16:01 +01:00
{
2019-10-16 10:09:30 +02:00
u8 prevPuzzleTileNum;
2018-11-19 17:16:01 +01:00
u8 eventTemplateId;
};
struct RotatingTilePuzzle
2018-11-19 17:16:01 +01:00
{
2019-10-16 10:09:30 +02:00
struct RotatingTileObject objects[EVENT_OBJECTS_COUNT];
u8 numObjects;
bool8 isTrickHouse;
2018-11-19 17:16:01 +01:00
};
// This file's functions.
2019-10-16 10:09:30 +02:00
static void SaveRotatingTileObject(u8 eventTemplateId, u8 arg1);
static void TurnUnsavedRotatingTileObject(u8 eventTemplateId, u8 arg1);
2018-11-19 17:16:01 +01:00
// EWRAM vars
EWRAM_DATA static struct RotatingTilePuzzle *sRotatingTilePuzzle = NULL;
2018-11-19 17:16:01 +01:00
// code
void InitRotatingTilePuzzle(bool8 isTrickHouse)
2018-11-19 17:16:01 +01:00
{
if (sRotatingTilePuzzle == NULL)
sRotatingTilePuzzle = AllocZeroed(sizeof(*sRotatingTilePuzzle));
2018-11-19 17:16:01 +01:00
sRotatingTilePuzzle->isTrickHouse = isTrickHouse;
2018-11-19 17:16:01 +01:00
}
2019-10-16 10:09:30 +02:00
void FreeRotatingTilePuzzle(void)
2018-11-19 17:16:01 +01:00
{
u8 id;
if (sRotatingTilePuzzle != NULL)
FREE_AND_SET_NULL(sRotatingTilePuzzle);
2018-11-19 17:16:01 +01:00
2018-12-28 19:29:21 +01:00
id = GetEventObjectIdByLocalIdAndMap(EVENT_OBJ_ID_PLAYER, 0, 0);
2018-11-19 17:16:01 +01:00
EventObjectClearHeldMovementIfFinished(&gEventObjects[id]);
2019-09-16 06:22:50 +02:00
ScriptMovement_UnfreezeEventObjects();
2018-11-19 17:16:01 +01:00
}
2019-10-16 10:09:30 +02:00
u16 MoveRotatingTileObjects(u8 puzzleNumber)
2018-11-19 17:16:01 +01:00
{
u8 i;
struct EventObjectTemplate *eventObjects = gSaveBlock1Ptr->eventObjectTemplates;
2018-11-19 17:16:01 +01:00
u16 localId = 0;
for (i = 0; i < EVENT_OBJECT_TEMPLATES_COUNT; i++)
{
s32 puzzleTileStart;
2019-10-16 10:09:30 +02:00
u8 puzzleTileNum;
s16 x = eventObjects[i].x + 7;
s16 y = eventObjects[i].y + 7;
2018-11-19 17:16:01 +01:00
u16 metatile = MapGridGetMetatileIdAt(x, y);
if (!sRotatingTilePuzzle->isTrickHouse)
2019-10-16 10:09:30 +02:00
puzzleTileStart = METATILE_MossdeepGym_YellowArrow_Right;
2018-11-19 17:16:01 +01:00
else
puzzleTileStart = METATILE_TrickHousePuzzle_Arrow_YellowOnWhite_Right;
2018-11-19 17:16:01 +01:00
2019-10-16 10:09:30 +02:00
// Object is on a metatile before the puzzle tile section
// UB: Because this is not if (metatile < puzzleTileStart), for the trick house (metatile - puzzleTileStart) below can result in casting a negative value to u8
if (metatile < METATILE_MossdeepGym_YellowArrow_Right)
2018-11-19 17:16:01 +01:00
continue;
2019-10-16 10:09:30 +02:00
// Object is on a metatile after the puzzle tile section (never occurs, in both cases the puzzle tiles are last)
if ((u8)((metatile - puzzleTileStart) / 8) >= 5)
2018-11-19 17:16:01 +01:00
continue;
2019-10-16 10:09:30 +02:00
// Object is on a metatile in puzzle tile section, but not one of the currently rotating color
if ((u8)((metatile - puzzleTileStart) / 8) != puzzleNumber)
2018-11-19 17:16:01 +01:00
continue;
2019-10-16 10:09:30 +02:00
puzzleTileNum = (u8)((metatile - puzzleTileStart) % 8);
// First 4 puzzle tiles are the colored arrows
if (puzzleTileNum < 4)
2018-11-19 17:16:01 +01:00
{
s8 x = 0;
s8 y = 0;
const u8 *movementScript;
2019-10-16 10:09:30 +02:00
switch (puzzleTileNum)
2018-11-19 17:16:01 +01:00
{
2019-10-16 10:09:30 +02:00
case 0: // Right Arrow
movementScript = RotatingTilePuzzle_Movement_ShiftRight;
2018-11-19 17:16:01 +01:00
x = 1;
break;
2019-10-16 10:09:30 +02:00
case 1: // Down Arrow
movementScript = RotatingTilePuzzle_Movement_ShiftDown;
2018-11-19 17:16:01 +01:00
y = 1;
break;
2019-10-16 10:09:30 +02:00
case 2: // Left Arrow
movementScript = RotatingTilePuzzle_Movement_ShiftLeft;
2018-11-19 17:16:01 +01:00
x = -1;
break;
2019-10-16 10:09:30 +02:00
case 3: // Up Arrow
movementScript = RotatingTilePuzzle_Movement_ShiftUp;
2018-11-19 17:16:01 +01:00
y = -1;
break;
default:
continue;
}
eventObjects[i].x += x;
eventObjects[i].y += y;
if (GetEventObjectIdByLocalIdAndMap(eventObjects[i].localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup) != EVENT_OBJECTS_COUNT)
2018-11-19 17:16:01 +01:00
{
2019-10-16 10:09:30 +02:00
SaveRotatingTileObject(i, puzzleTileNum);
localId = eventObjects[i].localId;
2018-11-19 17:16:01 +01:00
ScriptMovement_StartObjectMovementScript(localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, movementScript);
}
2019-10-16 10:09:30 +02:00
// Never reached in normal gameplay
2018-11-19 17:16:01 +01:00
else
{
2019-10-16 10:09:30 +02:00
TurnUnsavedRotatingTileObject(i, puzzleTileNum);
2018-11-19 17:16:01 +01:00
}
}
}
return localId;
}
2019-10-16 10:09:30 +02:00
void TurnRotatingTileObjects(void)
2018-11-19 17:16:01 +01:00
{
u8 i;
s32 puzzleTileStart;
struct EventObjectTemplate *eventObjects;
2018-11-19 17:16:01 +01:00
if (sRotatingTilePuzzle == NULL)
2018-11-19 17:16:01 +01:00
return;
if (!sRotatingTilePuzzle->isTrickHouse)
2019-10-16 10:09:30 +02:00
puzzleTileStart = METATILE_MossdeepGym_YellowArrow_Right;
2018-11-19 17:16:01 +01:00
else
puzzleTileStart = METATILE_TrickHousePuzzle_Arrow_YellowOnWhite_Right;
2018-11-19 17:16:01 +01:00
eventObjects = gSaveBlock1Ptr->eventObjectTemplates;
2019-10-16 10:09:30 +02:00
for (i = 0; i < sRotatingTilePuzzle->numObjects; i++)
2018-11-19 17:16:01 +01:00
{
2019-10-16 10:09:30 +02:00
s32 rotation;
s8 tileDifference;
2018-11-19 17:16:01 +01:00
u8 eventObjectId;
s16 x = eventObjects[sRotatingTilePuzzle->objects[i].eventTemplateId].x + 7;
s16 y = eventObjects[sRotatingTilePuzzle->objects[i].eventTemplateId].y + 7;
2018-11-19 17:16:01 +01:00
u16 metatile = MapGridGetMetatileIdAt(x, y);
2019-10-16 10:09:30 +02:00
// NOTE: The following 2 assignments and if else could all be replaced with rotation = ROTATE_COUNTERCLOCKWISE
// For an object to be saved in sRotatingTilePuzzle->objects, it must have been on a colored arrow tile
// After the first assignment, tileDifference will always be a number [0-3] representing which arrow tile the object is on now (0: right, 1: down, 2: left, 3: up)
// prevPuzzleTileNum will similarly be a number [0-3] representing the arrow tile the object just moved from
// All the puzzles are oriented counter-clockwise and can only move 1 step at a time, so the difference between the current tile and the previous tile will always either be -1 or 3 (0-1, 1-2, 2-3, 3-0)
// Which means tileDifference will always either be -1 or 3 after the below subtraction, and rotation will always be ROTATE_COUNTERCLOCKWISE after the following conditionals
tileDifference = (u8)((metatile - puzzleTileStart) % 8);
tileDifference -= (sRotatingTilePuzzle->objects[i].prevPuzzleTileNum);
// Always true, see above
if (tileDifference < 0 || tileDifference == 3)
2018-11-19 17:16:01 +01:00
{
2019-10-16 10:09:30 +02:00
// Always false, see above
if (tileDifference == -3)
rotation = ROTATE_CLOCKWISE;
2018-11-19 17:16:01 +01:00
else
2019-10-16 10:09:30 +02:00
rotation = ROTATE_COUNTERCLOCKWISE;
2018-11-19 17:16:01 +01:00
}
else
{
2019-10-16 10:09:30 +02:00
if (tileDifference > 0)
rotation = ROTATE_CLOCKWISE;
2018-11-19 17:16:01 +01:00
else
2019-10-16 10:09:30 +02:00
rotation = ROTATE_NONE;
2018-11-19 17:16:01 +01:00
}
eventObjectId = GetEventObjectIdByLocalIdAndMap(eventObjects[sRotatingTilePuzzle->objects[i].eventTemplateId].localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup);
2018-11-19 17:16:01 +01:00
if (eventObjectId != EVENT_OBJECTS_COUNT)
{
const u8 *movementScript;
u8 direction = gEventObjects[eventObjectId].facingDirection;
2019-10-16 10:09:30 +02:00
if (rotation == ROTATE_COUNTERCLOCKWISE)
2018-11-19 17:16:01 +01:00
{
switch (direction)
{
case DIR_EAST:
2019-10-16 10:09:30 +02:00
movementScript = RotatingTilePuzzle_Movement_FaceUp;
eventObjects[sRotatingTilePuzzle->objects[i].eventTemplateId].movementType = MOVEMENT_TYPE_FACE_UP;
2018-11-19 17:16:01 +01:00
break;
case DIR_SOUTH:
2019-10-16 10:09:30 +02:00
movementScript = RotatingTilePuzzle_Movement_FaceRight;
eventObjects[sRotatingTilePuzzle->objects[i].eventTemplateId].movementType = MOVEMENT_TYPE_FACE_RIGHT;
2018-11-19 17:16:01 +01:00
break;
case DIR_WEST:
2019-10-16 10:09:30 +02:00
movementScript = RotatingTilePuzzle_Movement_FaceDown;
eventObjects[sRotatingTilePuzzle->objects[i].eventTemplateId].movementType = MOVEMENT_TYPE_FACE_DOWN;
2018-11-19 17:16:01 +01:00
break;
case DIR_NORTH:
2019-10-16 10:09:30 +02:00
movementScript = RotatingTilePuzzle_Movement_FaceLeft;
eventObjects[sRotatingTilePuzzle->objects[i].eventTemplateId].movementType = MOVEMENT_TYPE_FACE_LEFT;
2018-11-19 17:16:01 +01:00
break;
default:
continue;
}
ScriptMovement_StartObjectMovementScript(eventObjects[sRotatingTilePuzzle->objects[i].eventTemplateId].localId,
2018-11-19 17:16:01 +01:00
gSaveBlock1Ptr->location.mapNum,
gSaveBlock1Ptr->location.mapGroup,
movementScript);
}
2019-10-16 10:09:30 +02:00
// Never reached
else if (rotation == ROTATE_CLOCKWISE)
2018-11-19 17:16:01 +01:00
{
switch (direction)
{
case DIR_EAST:
2019-10-16 10:09:30 +02:00
movementScript = RotatingTilePuzzle_Movement_FaceDown;
eventObjects[sRotatingTilePuzzle->objects[i].eventTemplateId].movementType = MOVEMENT_TYPE_FACE_DOWN;
2018-11-19 17:16:01 +01:00
break;
case DIR_SOUTH:
2019-10-16 10:09:30 +02:00
movementScript = RotatingTilePuzzle_Movement_FaceLeft;
eventObjects[sRotatingTilePuzzle->objects[i].eventTemplateId].movementType = MOVEMENT_TYPE_FACE_LEFT;
2018-11-19 17:16:01 +01:00
break;
case DIR_WEST:
2019-10-16 10:09:30 +02:00
movementScript = RotatingTilePuzzle_Movement_FaceUp;
eventObjects[sRotatingTilePuzzle->objects[i].eventTemplateId].movementType = MOVEMENT_TYPE_FACE_UP;
2018-11-19 17:16:01 +01:00
break;
case DIR_NORTH:
2019-10-16 10:09:30 +02:00
movementScript = RotatingTilePuzzle_Movement_FaceRight;
eventObjects[sRotatingTilePuzzle->objects[i].eventTemplateId].movementType = MOVEMENT_TYPE_FACE_RIGHT;
2018-11-19 17:16:01 +01:00
break;
default:
continue;
}
ScriptMovement_StartObjectMovementScript(eventObjects[sRotatingTilePuzzle->objects[i].eventTemplateId].localId,
2018-11-19 17:16:01 +01:00
gSaveBlock1Ptr->location.mapNum,
gSaveBlock1Ptr->location.mapGroup,
movementScript);
}
}
}
}
2019-10-16 10:09:30 +02:00
static void SaveRotatingTileObject(u8 eventTemplateId, u8 puzzleTileNum)
2018-11-19 17:16:01 +01:00
{
2019-10-16 10:09:30 +02:00
sRotatingTilePuzzle->objects[sRotatingTilePuzzle->numObjects].eventTemplateId = eventTemplateId;
sRotatingTilePuzzle->objects[sRotatingTilePuzzle->numObjects].prevPuzzleTileNum = puzzleTileNum;
sRotatingTilePuzzle->numObjects++;
2018-11-19 17:16:01 +01:00
}
2019-10-16 10:09:30 +02:00
// Functionally unused
static void TurnUnsavedRotatingTileObject(u8 eventTemplateId, u8 puzzleTileNum)
2018-11-19 17:16:01 +01:00
{
2019-10-16 10:09:30 +02:00
s8 tileDifference;
s32 rotation;
s32 puzzleTileStart;
2018-11-19 17:16:01 +01:00
u16 movementType;
struct EventObjectTemplate *eventObjects = gSaveBlock1Ptr->eventObjectTemplates;
s16 x = eventObjects[eventTemplateId].x + 7;
s16 y = eventObjects[eventTemplateId].y + 7;
2018-11-19 17:16:01 +01:00
u16 metatile = MapGridGetMetatileIdAt(x, y);
if (!sRotatingTilePuzzle->isTrickHouse)
2019-10-16 10:09:30 +02:00
puzzleTileStart = METATILE_MossdeepGym_YellowArrow_Right;
2018-11-19 17:16:01 +01:00
else
puzzleTileStart = METATILE_TrickHousePuzzle_Arrow_YellowOnWhite_Right;
2018-11-19 17:16:01 +01:00
2019-10-16 10:09:30 +02:00
tileDifference = (u8)((metatile - puzzleTileStart) % 8);
tileDifference -= puzzleTileNum;
if (tileDifference < 0 || tileDifference == 3)
rotation = ROTATE_COUNTERCLOCKWISE;
else if (tileDifference > 0 || tileDifference == -3)
rotation = ROTATE_CLOCKWISE;
2018-11-19 17:16:01 +01:00
else
2019-10-16 10:09:30 +02:00
rotation = ROTATE_NONE;
2018-11-19 17:16:01 +01:00
movementType = eventObjects[eventTemplateId].movementType;
2019-10-16 10:09:30 +02:00
if (rotation == ROTATE_COUNTERCLOCKWISE)
2018-11-19 17:16:01 +01:00
{
switch (movementType)
{
case MOVEMENT_TYPE_FACE_RIGHT:
eventObjects[eventTemplateId].movementType = MOVEMENT_TYPE_FACE_UP;
2018-11-19 17:16:01 +01:00
break;
case MOVEMENT_TYPE_FACE_DOWN:
eventObjects[eventTemplateId].movementType = MOVEMENT_TYPE_FACE_RIGHT;
2018-11-19 17:16:01 +01:00
break;
case MOVEMENT_TYPE_FACE_LEFT:
eventObjects[eventTemplateId].movementType = MOVEMENT_TYPE_FACE_DOWN;
2018-11-19 17:16:01 +01:00
break;
case MOVEMENT_TYPE_FACE_UP:
eventObjects[eventTemplateId].movementType = MOVEMENT_TYPE_FACE_LEFT;
2018-11-19 17:16:01 +01:00
break;
default:
break;
}
}
2019-10-16 10:09:30 +02:00
else if (rotation == ROTATE_CLOCKWISE)
2018-11-19 17:16:01 +01:00
{
switch (movementType)
{
case MOVEMENT_TYPE_FACE_RIGHT:
eventObjects[eventTemplateId].movementType = MOVEMENT_TYPE_FACE_DOWN;
2018-11-19 17:16:01 +01:00
break;
case MOVEMENT_TYPE_FACE_DOWN:
eventObjects[eventTemplateId].movementType = MOVEMENT_TYPE_FACE_LEFT;
2018-11-19 17:16:01 +01:00
break;
case MOVEMENT_TYPE_FACE_LEFT:
eventObjects[eventTemplateId].movementType = MOVEMENT_TYPE_FACE_UP;
2018-11-19 17:16:01 +01:00
break;
case MOVEMENT_TYPE_FACE_UP:
eventObjects[eventTemplateId].movementType = MOVEMENT_TYPE_FACE_RIGHT;
2018-11-19 17:16:01 +01:00
break;
default:
break;
}
}
}