510 lines
15 KiB
C
510 lines
15 KiB
C
#include "global.h"
|
|
#include "berry.h"
|
|
#include "bike.h"
|
|
#include "field_camera.h"
|
|
#include "field_player_avatar.h"
|
|
#include "fieldmap.h"
|
|
#include "event_object_movement.h"
|
|
#include "gpu_regs.h"
|
|
#include "menu.h"
|
|
#include "overworld.h"
|
|
#include "rotating_gate.h"
|
|
#include "sprite.h"
|
|
#include "text.h"
|
|
|
|
EWRAM_DATA bool8 gUnusedBikeCameraAheadPanback = FALSE;
|
|
|
|
// Static type declarations
|
|
struct FieldCameraOffset
|
|
{
|
|
u8 xPixelOffset;
|
|
u8 yPixelOffset;
|
|
u8 xTileOffset;
|
|
u8 yTileOffset;
|
|
bool8 copyBGToVRAM;
|
|
};
|
|
|
|
// static functions
|
|
static void RedrawMapSliceNorth(struct FieldCameraOffset *cameraOffset, const struct MapLayout *mapLayout);
|
|
static void RedrawMapSliceSouth(struct FieldCameraOffset *cameraOffset, const struct MapLayout *mapLayout);
|
|
static void RedrawMapSliceEast(struct FieldCameraOffset *cameraOffset, const struct MapLayout *mapLayout);
|
|
static void RedrawMapSliceWest(struct FieldCameraOffset *cameraOffset, const struct MapLayout *mapLayout);
|
|
static s32 MapPosToBgTilemapOffset(struct FieldCameraOffset *a, s32 x, s32 y);
|
|
static void DrawWholeMapViewInternal(int x, int y, const struct MapLayout *mapLayout);
|
|
static void DrawMetatileAt(const struct MapLayout *mapLayout, u16, int, int);
|
|
static void DrawMetatile(s32 a, u16 *b, u16 c);
|
|
static void CameraPanningCB_PanAhead(void);
|
|
|
|
// IWRAM bss vars
|
|
static struct FieldCameraOffset sFieldCameraOffset;
|
|
static s16 sHorizontalCameraPan;
|
|
static s16 sVerticalCameraPan;
|
|
static bool8 sBikeCameraPanFlag;
|
|
static void (*sFieldCameraPanningCallback)(void);
|
|
|
|
struct CameraObject gFieldCamera;
|
|
u16 gTotalCameraPixelOffsetY;
|
|
u16 gTotalCameraPixelOffsetX;
|
|
|
|
// text
|
|
static void ResetCameraOffset(struct FieldCameraOffset *cameraOffset)
|
|
{
|
|
cameraOffset->xTileOffset = 0;
|
|
cameraOffset->yTileOffset = 0;
|
|
cameraOffset->xPixelOffset = 0;
|
|
cameraOffset->yPixelOffset = 0;
|
|
cameraOffset->copyBGToVRAM = TRUE;
|
|
}
|
|
|
|
static void AddCameraTileOffset(struct FieldCameraOffset *cameraOffset, u32 xOffset, u32 yOffset)
|
|
{
|
|
cameraOffset->xTileOffset += xOffset;
|
|
cameraOffset->xTileOffset %= 32;
|
|
cameraOffset->yTileOffset += yOffset;
|
|
cameraOffset->yTileOffset %= 32;
|
|
}
|
|
|
|
static void AddCameraPixelOffset(struct FieldCameraOffset *cameraOffset, u32 xOffset, u32 yOffset)
|
|
{
|
|
cameraOffset->xPixelOffset += xOffset;
|
|
cameraOffset->yPixelOffset += yOffset;
|
|
}
|
|
|
|
void ResetFieldCamera(void)
|
|
{
|
|
ResetCameraOffset(&sFieldCameraOffset);
|
|
}
|
|
|
|
void FieldUpdateBgTilemapScroll(void)
|
|
{
|
|
u32 r4, r5;
|
|
r5 = sFieldCameraOffset.xPixelOffset + sHorizontalCameraPan;
|
|
r4 = sVerticalCameraPan + sFieldCameraOffset.yPixelOffset + 8;
|
|
|
|
SetGpuReg(REG_OFFSET_BG1HOFS, r5);
|
|
SetGpuReg(REG_OFFSET_BG1VOFS, r4);
|
|
SetGpuReg(REG_OFFSET_BG2HOFS, r5);
|
|
SetGpuReg(REG_OFFSET_BG2VOFS, r4);
|
|
SetGpuReg(REG_OFFSET_BG3HOFS, r5);
|
|
SetGpuReg(REG_OFFSET_BG3VOFS, r4);
|
|
}
|
|
|
|
void GetCameraOffsetWithPan(s16 *x, s16 *y)
|
|
{
|
|
*x = sFieldCameraOffset.xPixelOffset + sHorizontalCameraPan;
|
|
*y = sFieldCameraOffset.yPixelOffset + sVerticalCameraPan + 8;
|
|
}
|
|
|
|
void DrawWholeMapView(void)
|
|
{
|
|
DrawWholeMapViewInternal(gSaveBlock1Ptr->pos.x, gSaveBlock1Ptr->pos.y, gMapHeader.mapLayout);
|
|
sFieldCameraOffset.copyBGToVRAM = TRUE;
|
|
}
|
|
|
|
static void DrawWholeMapViewInternal(int x, int y, const struct MapLayout *mapLayout)
|
|
{
|
|
u8 i;
|
|
u8 j;
|
|
u32 r6;
|
|
u8 temp;
|
|
|
|
for (i = 0; i < 32; i += 2)
|
|
{
|
|
temp = sFieldCameraOffset.yTileOffset + i;
|
|
if (temp >= 32)
|
|
temp -= 32;
|
|
r6 = temp * 32;
|
|
for (j = 0; j < 32; j += 2)
|
|
{
|
|
temp = sFieldCameraOffset.xTileOffset + j;
|
|
if (temp >= 32)
|
|
temp -= 32;
|
|
DrawMetatileAt(mapLayout, r6 + temp, x + j / 2, y + i / 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void RedrawMapSlicesForCameraUpdate(struct FieldCameraOffset *cameraOffset, int x, int y)
|
|
{
|
|
const struct MapLayout *mapLayout = gMapHeader.mapLayout;
|
|
|
|
if (x > 0)
|
|
RedrawMapSliceWest(cameraOffset, mapLayout);
|
|
if (x < 0)
|
|
RedrawMapSliceEast(cameraOffset, mapLayout);
|
|
if (y > 0)
|
|
RedrawMapSliceNorth(cameraOffset, mapLayout);
|
|
if (y < 0)
|
|
RedrawMapSliceSouth(cameraOffset, mapLayout);
|
|
cameraOffset->copyBGToVRAM = TRUE;
|
|
}
|
|
|
|
static void RedrawMapSliceNorth(struct FieldCameraOffset *cameraOffset, const struct MapLayout *mapLayout)
|
|
{
|
|
u8 i;
|
|
u8 temp;
|
|
u32 r7;
|
|
|
|
temp = cameraOffset->yTileOffset + 28;
|
|
if (temp >= 32)
|
|
temp -= 32;
|
|
r7 = temp * 32;
|
|
for (i = 0; i < 32; i += 2)
|
|
{
|
|
temp = cameraOffset->xTileOffset + i;
|
|
if (temp >= 32)
|
|
temp -= 32;
|
|
DrawMetatileAt(mapLayout, r7 + temp, gSaveBlock1Ptr->pos.x + i / 2, gSaveBlock1Ptr->pos.y + 14);
|
|
}
|
|
}
|
|
|
|
static void RedrawMapSliceSouth(struct FieldCameraOffset *cameraOffset, const struct MapLayout *mapLayout)
|
|
{
|
|
u8 i;
|
|
u8 temp;
|
|
u32 r7 = cameraOffset->yTileOffset * 32;
|
|
|
|
for (i = 0; i < 32; i += 2)
|
|
{
|
|
temp = cameraOffset->xTileOffset + i;
|
|
if (temp >= 32)
|
|
temp -= 32;
|
|
DrawMetatileAt(mapLayout, r7 + temp, gSaveBlock1Ptr->pos.x + i / 2, gSaveBlock1Ptr->pos.y);
|
|
}
|
|
}
|
|
|
|
static void RedrawMapSliceEast(struct FieldCameraOffset *cameraOffset, const struct MapLayout *mapLayout)
|
|
{
|
|
u8 i;
|
|
u8 temp;
|
|
u32 r6 = cameraOffset->xTileOffset;
|
|
|
|
for (i = 0; i < 32; i += 2)
|
|
{
|
|
temp = cameraOffset->yTileOffset + i;
|
|
if (temp >= 32)
|
|
temp -= 32;
|
|
DrawMetatileAt(mapLayout, temp * 32 + r6, gSaveBlock1Ptr->pos.x, gSaveBlock1Ptr->pos.y + i / 2);
|
|
}
|
|
}
|
|
|
|
static void RedrawMapSliceWest(struct FieldCameraOffset *cameraOffset, const struct MapLayout *mapLayout)
|
|
{
|
|
u8 i;
|
|
u8 temp;
|
|
u8 r5 = cameraOffset->xTileOffset + 28;
|
|
|
|
if (r5 >= 32)
|
|
r5 -= 32;
|
|
for (i = 0; i < 32; i += 2)
|
|
{
|
|
temp = cameraOffset->yTileOffset + i;
|
|
if (temp >= 32)
|
|
temp -= 32;
|
|
DrawMetatileAt(mapLayout, temp * 32 + r5, gSaveBlock1Ptr->pos.x + 14, gSaveBlock1Ptr->pos.y + i / 2);
|
|
}
|
|
}
|
|
|
|
void CurrentMapDrawMetatileAt(int x, int y)
|
|
{
|
|
int offset = MapPosToBgTilemapOffset(&sFieldCameraOffset, x, y);
|
|
|
|
if (offset >= 0)
|
|
{
|
|
DrawMetatileAt(gMapHeader.mapLayout, offset, x, y);
|
|
sFieldCameraOffset.copyBGToVRAM = TRUE;
|
|
}
|
|
}
|
|
|
|
void DrawDoorMetatileAt(int x, int y, u16 *arr)
|
|
{
|
|
int offset = MapPosToBgTilemapOffset(&sFieldCameraOffset, x, y);
|
|
|
|
if (offset >= 0)
|
|
{
|
|
DrawMetatile(1, arr, offset);
|
|
sFieldCameraOffset.copyBGToVRAM = TRUE;
|
|
}
|
|
}
|
|
|
|
static void DrawMetatileAt(const struct MapLayout *mapLayout, u16 offset, int x, int y)
|
|
{
|
|
u16 metatileId = MapGridGetMetatileIdAt(x, y);
|
|
u16 *metatiles;
|
|
|
|
if (metatileId > NUM_METATILES_TOTAL)
|
|
metatileId = 0;
|
|
if (metatileId < NUM_METATILES_IN_PRIMARY)
|
|
metatiles = mapLayout->primaryTileset->metatiles;
|
|
else
|
|
{
|
|
metatiles = mapLayout->secondaryTileset->metatiles;
|
|
metatileId -= NUM_METATILES_IN_PRIMARY;
|
|
}
|
|
DrawMetatile(MapGridGetMetatileLayerTypeAt(x, y), metatiles + metatileId * 8, offset);
|
|
}
|
|
|
|
static void DrawMetatile(s32 metatileLayerType, u16 *metatiles, u16 offset)
|
|
{
|
|
switch (metatileLayerType)
|
|
{
|
|
case 2: // LAYER_TYPE_
|
|
// Draw metatile's bottom layer to the bottom background layer.
|
|
gBGTilemapBuffers3[offset] = metatiles[0];
|
|
gBGTilemapBuffers3[offset + 1] = metatiles[1];
|
|
gBGTilemapBuffers3[offset + 0x20] = metatiles[2];
|
|
gBGTilemapBuffers3[offset + 0x21] = metatiles[3];
|
|
|
|
// Draw transparent tiles to the middle background layer.
|
|
gBGTilemapBuffers1[offset] = 0;
|
|
gBGTilemapBuffers1[offset + 1] = 0;
|
|
gBGTilemapBuffers1[offset + 0x20] = 0;
|
|
gBGTilemapBuffers1[offset + 0x21] = 0;
|
|
|
|
// Draw metatile's top layer to the top background layer.
|
|
gBGTilemapBuffers2[offset] = metatiles[4];
|
|
gBGTilemapBuffers2[offset + 1] = metatiles[5];
|
|
gBGTilemapBuffers2[offset + 0x20] = metatiles[6];
|
|
gBGTilemapBuffers2[offset + 0x21] = metatiles[7];
|
|
break;
|
|
case 1: // LAYER_TYPE_COVERED_BY_OBJECTS
|
|
// Draw metatile's bottom layer to the bottom background layer.
|
|
gBGTilemapBuffers3[offset] = metatiles[0];
|
|
gBGTilemapBuffers3[offset + 1] = metatiles[1];
|
|
gBGTilemapBuffers3[offset + 0x20] = metatiles[2];
|
|
gBGTilemapBuffers3[offset + 0x21] = metatiles[3];
|
|
|
|
// Draw metatile's top layer to the middle background layer.
|
|
gBGTilemapBuffers1[offset] = metatiles[4];
|
|
gBGTilemapBuffers1[offset + 1] = metatiles[5];
|
|
gBGTilemapBuffers1[offset + 0x20] = metatiles[6];
|
|
gBGTilemapBuffers1[offset + 0x21] = metatiles[7];
|
|
|
|
// Draw transparent tiles to the top background layer.
|
|
gBGTilemapBuffers2[offset] = 0;
|
|
gBGTilemapBuffers2[offset + 1] = 0;
|
|
gBGTilemapBuffers2[offset + 0x20] = 0;
|
|
gBGTilemapBuffers2[offset + 0x21] = 0;
|
|
break;
|
|
case 0: // LAYER_TYPE_NORMAL
|
|
// Draw garbage to the bottom background layer.
|
|
gBGTilemapBuffers3[offset] = 0x3014;
|
|
gBGTilemapBuffers3[offset + 1] = 0x3014;
|
|
gBGTilemapBuffers3[offset + 0x20] = 0x3014;
|
|
gBGTilemapBuffers3[offset + 0x21] = 0x3014;
|
|
|
|
// Draw metatile's bottom layer to the middle background layer.
|
|
gBGTilemapBuffers1[offset] = metatiles[0];
|
|
gBGTilemapBuffers1[offset + 1] = metatiles[1];
|
|
gBGTilemapBuffers1[offset + 0x20] = metatiles[2];
|
|
gBGTilemapBuffers1[offset + 0x21] = metatiles[3];
|
|
|
|
// Draw metatile's top layer to the top background layer, which covers object event sprites.
|
|
gBGTilemapBuffers2[offset] = metatiles[4];
|
|
gBGTilemapBuffers2[offset + 1] = metatiles[5];
|
|
gBGTilemapBuffers2[offset + 0x20] = metatiles[6];
|
|
gBGTilemapBuffers2[offset + 0x21] = metatiles[7];
|
|
break;
|
|
}
|
|
ScheduleBgCopyTilemapToVram(1);
|
|
ScheduleBgCopyTilemapToVram(2);
|
|
ScheduleBgCopyTilemapToVram(3);
|
|
}
|
|
|
|
static s32 MapPosToBgTilemapOffset(struct FieldCameraOffset *cameraOffset, s32 x, s32 y)
|
|
{
|
|
x -= gSaveBlock1Ptr->pos.x;
|
|
x *= 2;
|
|
if (x >= 32 || x < 0)
|
|
return -1;
|
|
x = x + cameraOffset->xTileOffset;
|
|
if (x >= 32)
|
|
x -= 32;
|
|
|
|
y = (y - gSaveBlock1Ptr->pos.y) * 2;
|
|
if (y >= 32 || y < 0)
|
|
return -1;
|
|
y = y + cameraOffset->yTileOffset;
|
|
if (y >= 32)
|
|
y -= 32;
|
|
|
|
return y * 32 + x;
|
|
}
|
|
|
|
static void CameraUpdateCallback(struct CameraObject *fieldCamera)
|
|
{
|
|
if (fieldCamera->spriteId != 0)
|
|
{
|
|
fieldCamera->movementSpeedX = gSprites[fieldCamera->spriteId].data[2];
|
|
fieldCamera->movementSpeedY = gSprites[fieldCamera->spriteId].data[3];
|
|
}
|
|
}
|
|
|
|
void ResetCameraUpdateInfo(void)
|
|
{
|
|
gFieldCamera.movementSpeedX = 0;
|
|
gFieldCamera.movementSpeedY = 0;
|
|
gFieldCamera.x = 0;
|
|
gFieldCamera.y = 0;
|
|
gFieldCamera.spriteId = 0;
|
|
gFieldCamera.callback = NULL;
|
|
}
|
|
|
|
u32 InitCameraUpdateCallback(u8 trackedSpriteId)
|
|
{
|
|
if (gFieldCamera.spriteId != 0)
|
|
DestroySprite(&gSprites[gFieldCamera.spriteId]);
|
|
gFieldCamera.spriteId = AddCameraObject(trackedSpriteId);
|
|
gFieldCamera.callback = CameraUpdateCallback;
|
|
return 0;
|
|
}
|
|
|
|
void CameraUpdate(void)
|
|
{
|
|
int deltaX;
|
|
int deltaY;
|
|
int curMovementOffsetY;
|
|
int curMovementOffsetX;
|
|
int movementSpeedX;
|
|
int movementSpeedY;
|
|
|
|
if (gFieldCamera.callback != NULL)
|
|
gFieldCamera.callback(&gFieldCamera);
|
|
movementSpeedX = gFieldCamera.movementSpeedX;
|
|
movementSpeedY = gFieldCamera.movementSpeedY;
|
|
deltaX = 0;
|
|
deltaY = 0;
|
|
curMovementOffsetX = gFieldCamera.x;
|
|
curMovementOffsetY = gFieldCamera.y;
|
|
|
|
|
|
if (curMovementOffsetX == 0 && movementSpeedX != 0)
|
|
{
|
|
if (movementSpeedX > 0)
|
|
deltaX = 1;
|
|
else
|
|
deltaX = -1;
|
|
}
|
|
if (curMovementOffsetY == 0 && movementSpeedY != 0)
|
|
{
|
|
if (movementSpeedY > 0)
|
|
deltaY = 1;
|
|
else
|
|
deltaY = -1;
|
|
}
|
|
if (curMovementOffsetX != 0 && curMovementOffsetX == -movementSpeedX)
|
|
{
|
|
if (movementSpeedX > 0)
|
|
deltaX = 1;
|
|
else
|
|
deltaX = -1;
|
|
}
|
|
if (curMovementOffsetY != 0 && curMovementOffsetY == -movementSpeedY)
|
|
{
|
|
if (movementSpeedY > 0)
|
|
deltaX = 1;
|
|
else
|
|
deltaX = -1;
|
|
}
|
|
|
|
gFieldCamera.x += movementSpeedX;
|
|
gFieldCamera.x = gFieldCamera.x - 16 * (gFieldCamera.x / 16);
|
|
gFieldCamera.y += movementSpeedY;
|
|
gFieldCamera.y = gFieldCamera.y - 16 * (gFieldCamera.y / 16);
|
|
|
|
if (deltaX != 0 || deltaY != 0)
|
|
{
|
|
CameraMove(deltaX, deltaY);
|
|
UpdateObjectEventsForCameraUpdate(deltaX, deltaY);
|
|
RotatingGatePuzzleCameraUpdate(deltaX, deltaY);
|
|
SetBerryTreesSeen();
|
|
AddCameraTileOffset(&sFieldCameraOffset, deltaX * 2, deltaY * 2);
|
|
RedrawMapSlicesForCameraUpdate(&sFieldCameraOffset, deltaX * 2, deltaY * 2);
|
|
}
|
|
|
|
AddCameraPixelOffset(&sFieldCameraOffset, movementSpeedX, movementSpeedY);
|
|
gTotalCameraPixelOffsetX -= movementSpeedX;
|
|
gTotalCameraPixelOffsetY -= movementSpeedY;
|
|
}
|
|
|
|
void MoveCameraAndRedrawMap(int deltaX, int deltaY) //unused
|
|
{
|
|
CameraMove(deltaX, deltaY);
|
|
UpdateObjectEventsForCameraUpdate(deltaX, deltaY);
|
|
DrawWholeMapView();
|
|
gTotalCameraPixelOffsetX -= deltaX * 16;
|
|
gTotalCameraPixelOffsetY -= deltaY * 16;
|
|
}
|
|
|
|
void SetCameraPanningCallback(void (*callback)(void))
|
|
{
|
|
sFieldCameraPanningCallback = callback;
|
|
}
|
|
|
|
void SetCameraPanning(s16 a, s16 b)
|
|
{
|
|
sHorizontalCameraPan = a;
|
|
sVerticalCameraPan = b + 32;
|
|
}
|
|
|
|
void InstallCameraPanAheadCallback(void)
|
|
{
|
|
sFieldCameraPanningCallback = CameraPanningCB_PanAhead;
|
|
sBikeCameraPanFlag = FALSE;
|
|
sHorizontalCameraPan = 0;
|
|
sVerticalCameraPan = 32;
|
|
}
|
|
|
|
void UpdateCameraPanning(void)
|
|
{
|
|
if (sFieldCameraPanningCallback != NULL)
|
|
sFieldCameraPanningCallback();
|
|
//Update sprite offset of overworld objects
|
|
gSpriteCoordOffsetX = gTotalCameraPixelOffsetX - sHorizontalCameraPan;
|
|
gSpriteCoordOffsetY = gTotalCameraPixelOffsetY - sVerticalCameraPan - 8;
|
|
}
|
|
|
|
static void CameraPanningCB_PanAhead(void)
|
|
{
|
|
u8 var;
|
|
|
|
if (gUnusedBikeCameraAheadPanback == FALSE)
|
|
{
|
|
InstallCameraPanAheadCallback();
|
|
}
|
|
else
|
|
{
|
|
// this code is never reached
|
|
if (gPlayerAvatar.tileTransitionState == T_TILE_TRANSITION)
|
|
{
|
|
sBikeCameraPanFlag ^= 1;
|
|
if (sBikeCameraPanFlag == FALSE)
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
sBikeCameraPanFlag = FALSE;
|
|
}
|
|
|
|
var = GetPlayerMovementDirection();
|
|
if (var == 2)
|
|
{
|
|
if (sVerticalCameraPan > -8)
|
|
sVerticalCameraPan -= 2;
|
|
}
|
|
else if (var == 1)
|
|
{
|
|
if (sVerticalCameraPan < 72)
|
|
sVerticalCameraPan += 2;
|
|
}
|
|
else if (sVerticalCameraPan < 32)
|
|
{
|
|
sVerticalCameraPan += 2;
|
|
}
|
|
else if (sVerticalCameraPan > 32)
|
|
{
|
|
sVerticalCameraPan -= 2;
|
|
}
|
|
}
|
|
}
|