2018-08-01 16:03:16 +02:00
|
|
|
//
|
|
|
|
// Created by Wouter Groeneveld on 26/07/18.
|
|
|
|
//
|
|
|
|
|
|
|
|
#include <cstdlib>
|
2018-08-08 13:00:27 +02:00
|
|
|
#include <libgba-sprite-engine/gba/tonc_memdef.h>
|
|
|
|
#include <libgba-sprite-engine/background/text_stream.h>
|
|
|
|
#include <libgba-sprite-engine/sprites/sprite.h>
|
2018-08-08 14:43:34 +02:00
|
|
|
#include <libgba-sprite-engine/gba_engine.h>
|
2018-08-01 16:03:16 +02:00
|
|
|
|
2018-08-09 12:35:05 +02:00
|
|
|
Sprite::Sprite(const Sprite &other) : Sprite(nullptr, 0, other.x, other.y, other.spriteSize) {
|
|
|
|
tileIndex = other.tileIndex;
|
|
|
|
}
|
|
|
|
|
2018-08-01 16:03:16 +02:00
|
|
|
Sprite::Sprite(const void *imageData, int imageSize, int x, int y, SpriteSize size)
|
2018-08-09 12:35:05 +02:00
|
|
|
: x(x), y(y), data(imageData), imageSize(imageSize), spriteSize(size),
|
2018-08-05 16:51:59 +02:00
|
|
|
animationDelay(0), amountOfFrames(0), currentFrame(0), animationCounter(0) {
|
2018-08-01 16:03:16 +02:00
|
|
|
setAttributesBasedOnSize(size);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Sprite::moveTo(int x, int y) {
|
|
|
|
this->x = x;
|
|
|
|
this->y = y;
|
2018-08-09 12:35:05 +02:00
|
|
|
if(oam) {
|
|
|
|
syncOam();
|
|
|
|
}
|
2018-08-01 16:03:16 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 11:35:52 +02:00
|
|
|
bool Sprite::isOffScreen() {
|
|
|
|
return x < 0 || x > GBA_SCREEN_WIDTH || y < 0 || y > GBA_SCREEN_HEIGHT;
|
|
|
|
}
|
|
|
|
|
2018-08-05 16:51:59 +02:00
|
|
|
void Sprite::flipHorizontally(bool flip) {
|
|
|
|
if(flip) {
|
|
|
|
oam->attr1 |= ATTR1_HFLIP;
|
|
|
|
} else {
|
|
|
|
oam->attr1 &= FLIP_HORIZONTAL_CLEAR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Sprite::flipVertically(bool flip) {
|
|
|
|
if(flip) {
|
|
|
|
oam->attr1 |= ATTR1_VFLIP;
|
|
|
|
} else {
|
|
|
|
oam->attr1 &= FLIP_VERTICAL_CLEAR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Sprite::syncVelocity() {
|
2018-08-01 16:03:16 +02:00
|
|
|
oam->attr0 = (oam->attr0 & ~ATTR0_Y_MASK) | (y & ATTR0_Y_MASK);
|
|
|
|
oam->attr1 = (oam->attr1 & ~ATTR1_X_MASK) | (x & ATTR1_X_MASK);
|
|
|
|
}
|
|
|
|
|
2018-08-05 16:51:59 +02:00
|
|
|
void Sprite::syncAnimation() {
|
2018-08-09 11:35:52 +02:00
|
|
|
int offset = w == 64 ? 2 : 1; // 64xY sprites don't seem to cut currFrame * w
|
2018-12-06 12:15:09 +01:00
|
|
|
int width = w == 16 ? 8 : w; // 16xY sprites: frame 192 -> 200 (x8) (skip last 2 bpps)
|
|
|
|
int newTileIndex = this->tileIndex + (currentFrame * width * offset);
|
2018-08-05 16:51:59 +02:00
|
|
|
|
|
|
|
oam->attr2 &= OAM_TILE_OFFSET_CLEAR;
|
|
|
|
oam->attr2 |= (newTileIndex & OAM_TILE_OFFSET_NEW);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Sprite::syncOam() {
|
|
|
|
syncVelocity();
|
|
|
|
syncAnimation();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Sprite::updateVelocity() {
|
2018-08-01 16:03:16 +02:00
|
|
|
this->x += this->dx;
|
2018-08-05 17:11:36 +02:00
|
|
|
this->y += this->dy;
|
2018-08-08 14:43:34 +02:00
|
|
|
|
|
|
|
if(stayWithinBounds) {
|
|
|
|
if(this->x < 0) this->x = 0;
|
|
|
|
if(this->y < 0) this->y = 0;
|
|
|
|
if(this->x > (GBA_SCREEN_WIDTH - this->w)) this->x = GBA_SCREEN_WIDTH - this->w;
|
|
|
|
if(this->y > (GBA_SCREEN_HEIGHT - this->h)) this->y = GBA_SCREEN_HEIGHT - this->h;
|
|
|
|
}
|
2018-08-05 16:51:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sprite::updateAnimation() {
|
2018-08-05 17:11:36 +02:00
|
|
|
if(!animating) return;
|
2018-08-05 16:51:59 +02:00
|
|
|
|
|
|
|
animationCounter++;
|
|
|
|
if(animationCounter > animationDelay) {
|
|
|
|
currentFrame++;
|
|
|
|
if(currentFrame > (amountOfFrames - 1)) {
|
|
|
|
currentFrame = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
animationCounter = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Sprite::update() {
|
|
|
|
updateVelocity();
|
|
|
|
updateAnimation();
|
2018-08-01 16:03:16 +02:00
|
|
|
syncOam();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Sprite::setAttributesBasedOnSize(SpriteSize size) {
|
|
|
|
switch (size) {
|
|
|
|
case SIZE_8_8: size_bits = 0; shape_bits = 0; w = 8; h = 8; break;
|
|
|
|
case SIZE_16_16: size_bits = 1; shape_bits = 0; w = 16; h = 16; break;
|
|
|
|
case SIZE_32_32: size_bits = 2; shape_bits = 0; w = 32; h = 32; break;
|
|
|
|
case SIZE_64_64: size_bits = 3; shape_bits = 0; w = 64; h = 64; break;
|
|
|
|
case SIZE_16_8: size_bits = 0; shape_bits = 1; w = 16; h = 8; break;
|
|
|
|
case SIZE_32_8: size_bits = 1; shape_bits = 1; w = 32; h = 8; break;
|
|
|
|
case SIZE_32_16: size_bits = 2; shape_bits = 1; w = 32; h = 16; break;
|
|
|
|
case SIZE_64_32: size_bits = 3; shape_bits = 1; w = 64; h = 32; break;
|
|
|
|
case SIZE_8_16: size_bits = 0; shape_bits = 2; w = 8; h = 16; break;
|
|
|
|
case SIZE_8_32: size_bits = 1; shape_bits = 2; w = 8; h = 32; break;
|
|
|
|
case SIZE_16_32: size_bits = 2; shape_bits = 2; w = 16; h = 32; break;
|
|
|
|
case SIZE_32_64: size_bits = 3; shape_bits = 2; w = 32; h = 64; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-08 14:43:34 +02:00
|
|
|
bool Sprite::collidesWith(Sprite &s2) {
|
|
|
|
const Sprite &s1 = *this;
|
|
|
|
|
|
|
|
if(s1.x < s2.x + s2.w &&
|
|
|
|
s1.x + s1.w > s2.x &&
|
|
|
|
s1.y < s2.y + s2.h &&
|
|
|
|
s1.h + s1.y > s2.y) {
|
2018-08-01 16:03:16 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Sprite::buildOam(int tileIndex) {
|
2018-08-05 16:51:59 +02:00
|
|
|
this->tileIndex = tileIndex;
|
2018-08-09 11:35:52 +02:00
|
|
|
|
|
|
|
if(!oam) {
|
|
|
|
this->oam = std::unique_ptr<OBJ_ATTR>(new OBJ_ATTR());
|
2018-08-01 16:03:16 +02:00
|
|
|
|
2018-12-06 14:54:29 +01:00
|
|
|
this->oam->attr0 = ATTR0_Y(this->y & 0x00FF) |
|
2018-08-09 12:35:05 +02:00
|
|
|
ATTR0_MODE(0) |
|
|
|
|
(GFX_MODE << 10) |
|
|
|
|
(MOSAIC_MODE << 12) |
|
|
|
|
(COLOR_MODE_256 << 13) |
|
|
|
|
(this->shape_bits << 14);
|
2018-12-06 14:54:29 +01:00
|
|
|
this->oam->attr1 = (this->x & 0x01FF) |
|
2018-08-09 12:35:05 +02:00
|
|
|
(AFFINE_FLAG_NONE_SET_YET << 9) |
|
|
|
|
(HORIZONTAL_FLIP_FLAG << 12) |
|
|
|
|
(VERTICAL_FLIP_FLAG << 13) |
|
|
|
|
(this->size_bits << 14);
|
|
|
|
}
|
2018-08-01 16:03:16 +02:00
|
|
|
|
|
|
|
this->oam->attr2 = ATTR2_ID(tileIndex) |
|
|
|
|
ATTR2_PRIO(priority) |
|
|
|
|
ATTR2_PALBANK(0);
|
2018-08-05 16:51:59 +02:00
|
|
|
}
|