Commit bee8896d authored by alistert's avatar alistert

Event refactoring, incorporating various bug fixes.

parent 806e2402
......@@ -13,7 +13,7 @@ objects = src/bonus/bonus.o \
src/jj2level/jj2layer.o src/jj2level/jj2level.o \
src/jj2level/jj2levelframe.o src/jj2level/jj2levelload.o \
src/level/event/bridge.o src/level/event/guardians.o \
src/level/event/event.o src/level/event/eventframe.o \
src/level/event/event.o src/level/event/standardevent.o \
src/level/bullet.o src/level/demolevel.o src/level/level.o \
src/level/levelframe.o src/level/levelload.o src/level/movable.o \
src/menu/gamemenu.o src/menu/mainmenu.o src/menu/menu.o \
......
......@@ -56,7 +56,7 @@ OBJS = src/bonus/bonus.o \
src/jj2level/jj2layer.o src/jj2level/jj2level.o \
src/jj2level/jj2levelframe.o src/jj2level/jj2levelload.o \
src/level/event/bridge.o src/level/event/guardians.o \
src/level/event/event.o src/level/event/eventframe.o \
src/level/event/event.o src/level/event/standardevent.o \
src/level/bullet.o src/level/demolevel.o src/level/level.o \
src/level/levelframe.o src/level/levelload.o src/level/movable.o \
src/menu/gamemenu.o src/menu/mainmenu.o src/menu/menu.o \
......
......@@ -13,7 +13,7 @@ objects = src/bonus/bonus.o \
src/jj2level/jj2layer.o src/jj2level/jj2level.o \
src/jj2level/jj2levelframe.o src/jj2level/jj2levelload.o \
src/level/event/bridge.o src/level/event/guardians.o \
src/level/event/event.o src/level/event/eventframe.o \
src/level/event/event.o src/level/event/standardevent.o \
src/level/bullet.o src/level/demolevel.o src/level/level.o \
src/level/levelframe.o src/level/levelload.o src/level/movable.o \
src/menu/gamemenu.o src/menu/mainmenu.o src/menu/menu.o \
......
......@@ -17,7 +17,7 @@ objects = src/bonus/bonus.o \
src/jj2level/jj2layer.o src/jj2level/jj2level.o \
src/jj2level/jj2levelframe.o src/jj2level/jj2levelload.o \
src/level/event/bridge.o src/level/event/guardians.o \
src/level/event/event.o src/level/event/eventframe.o \
src/level/event/event.o src/level/event/standardevent.o \
src/level/bullet.o src/level/demolevel.o src/level/level.o \
src/level/levelframe.o src/level/levelload.o src/level/movable.o \
src/menu/gamemenu.o src/menu/mainmenu.o src/menu/menu.o \
......
......@@ -27,6 +27,9 @@
#include "level/level.h"
/**
* Create empty animation.
*/
Anim::Anim () {
sprites = new Sprite *[19];
......@@ -42,6 +45,9 @@ Anim::Anim () {
}
/**
* Delete animation.
*/
Anim::~Anim () {
delete[] sprites;
......@@ -53,21 +59,32 @@ Anim::~Anim () {
}
void Anim::setData (int amount, signed char sX, signed char sY, signed char aX, signed char aY, unsigned char a, signed char y) {
/**
* Set overall animation data.
*
* @param length Number of frames
* @param sX Bullet generation x-coordinate
* @param sY Bullet generation y-coordinate
* @param aX Accessory animation x-coordinate
* @param aY Accessory animation y-coordinate
* @param a Accessory animation index
* @param y Vertical offset
*/
void Anim::setData (int length, signed char sX, signed char sY, signed char aX, signed char aY, unsigned char a, signed char y) {
if (amount > 19) {
if (length > 19) {
delete[] sprites;
delete[] xOffsets;
delete[] yOffsets;
sprites = new Sprite *[amount];
xOffsets = new signed char[amount];
yOffsets = new signed char[amount];
sprites = new Sprite *[length];
xOffsets = new signed char[length];
yOffsets = new signed char[length];
}
frames = amount;
frames = length;
shootX = sX;
shootY = sY;
accessoryX = aX;
......@@ -80,6 +97,12 @@ void Anim::setData (int amount, signed char sX, signed char sY, signed char aX,
}
/**
* Set current frame.
*
* @param nextFrame The frame to use
* @param looping Whether the animation should stop at the end or loop
*/
void Anim::setFrame (int nextFrame, bool looping) {
if (looping) frame = nextFrame % frames;
......@@ -90,6 +113,13 @@ void Anim::setFrame (int nextFrame, bool looping) {
}
/**
* Set the data for the current frame.
*
* @param sprite Sprite to use
* @param x Horizontal offset
* @param y Vertical offset
*/
void Anim::setFrameData (Sprite *sprite, signed char x, signed char y) {
sprites[frame] = sprite;
......@@ -101,6 +131,11 @@ void Anim::setFrameData (Sprite *sprite, signed char x, signed char y) {
}
/**
* Determine the width of the current frame.
*
* @return The width of the current frame
*/
int Anim::getWidth () {
return sprites[frame]->getWidth();
......@@ -108,6 +143,11 @@ int Anim::getWidth () {
}
/**
* Determine the height of the current frame.
*
* @return The height of the current frame
*/
int Anim::getHeight () {
return sprites[frame]->getHeight();
......@@ -115,6 +155,23 @@ int Anim::getHeight () {
}
/**
* Determine the length of the animation.
*
* @return The length of the animation
*/
int Anim::getLength () {
return frames;
}
/**
* Determine the bullet generation x-coordinate of the current frame.
*
* @return The bullet generation x-coordinate
*/
fixed Anim::getShootX () {
return ITOF(shootX + (xOffsets[frame] << 2));
......@@ -122,6 +179,11 @@ fixed Anim::getShootX () {
}
/**
* Determine the bullet generation y-coordinate of the current frame.
*
* @return The bullet generation y-coordinate
*/
fixed Anim::getShootY () {
return ITOF(shootY + yOffsets[frame] - yOffset);
......@@ -129,6 +191,11 @@ fixed Anim::getShootY () {
}
/**
* Determine the accessory animation x-coordinate.
*
* @return The accessory animation x-coordinate
*/
fixed Anim::getAccessoryX () {
return ITOF(accessoryX << 2);
......@@ -136,6 +203,11 @@ fixed Anim::getAccessoryX () {
}
/**
* Determine the accessory animation y-coordinate.
*
* @return The accessory animation y-coordinate
*/
fixed Anim::getAccessoryY () {
return ITOF(accessoryY - yOffset);
......@@ -143,6 +215,11 @@ fixed Anim::getAccessoryY () {
}
/**
* Determine the accessory bullet generation x-coordinate of the current frame.
*
* @return The accessory bullet generation x-coordinate
*/
fixed Anim::getAccessoryShootX () {
return ITOF(shootX + (accessoryX << 2) + xOffsets[frame]);
......@@ -150,6 +227,11 @@ fixed Anim::getAccessoryShootX () {
}
/**
* Determine the accessory bullet generation y-coordinate of the current frame.
*
* @return The accessory bullet generation y-coordinate
*/
fixed Anim::getAccessoryShootY () {
return ITOF(shootY + accessoryY + yOffsets[frame] - yOffset);
......@@ -157,6 +239,11 @@ fixed Anim::getAccessoryShootY () {
}
/**
* Determine the vertical offset.
*
* @return The vertical offset
*/
fixed Anim::getOffset () {
if (!ignoreDefaultYOffset && yOffset == 0)
......@@ -167,6 +254,11 @@ fixed Anim::getOffset () {
}
/**
* Determine the accessory animation.
*
* @return The accessory animation
*/
Anim* Anim::getAccessory() {
return level->getAnim(accessory);
......@@ -174,6 +266,12 @@ Anim* Anim::getAccessory() {
}
/**
* Draw current frame.
*
* @param x X-coordinate at which to draw
* @param y Y-coordinate at which to draw
*/
void Anim::draw (fixed x, fixed y) {
// In case yOffset is zero, and the ignore default offset flag is set,
......@@ -201,6 +299,13 @@ void Anim::draw (fixed x, fixed y) {
}
/**
* Draw current frame scaled.
*
* @param x X-coordinate at which to draw
* @param y Y-coordinate at which to draw
* @param scale Scaling factor
*/
void Anim::drawScaled (fixed x, fixed y, fixed scale) {
// Used to draw bonus level player, so no offset
......@@ -211,13 +316,25 @@ void Anim::drawScaled (fixed x, fixed y, fixed scale) {
}
/**
* Disable default vertical offset.
*/
void Anim::disableDefaultOffset() {
ignoreDefaultYOffset = true;
return;
}
/**
* Set the current frame's palette.
*
* @param palette The new palette to use
* @param start The first entry to use
* @param amount The number of entries to use
*/
void Anim::setPalette (SDL_Color *palette, int start, int amount) {
sprites[frame]->setPalette(palette, 0, 256);
......@@ -227,6 +344,11 @@ void Anim::setPalette (SDL_Color *palette, int start, int amount) {
}
/**
* Turn the whole of the current frame a single colour.
*
* @param index The index of the colour to use
*/
void Anim::flashPalette (int index) {
sprites[frame]->flashPalette(index);
......@@ -236,6 +358,9 @@ void Anim::flashPalette (int index) {
}
/**
* Restore the current frame's original palette.
*/
void Anim::restorePalette () {
sprites[frame]->restorePalette();
......
......@@ -39,14 +39,14 @@ class Anim {
private:
Sprite** sprites; ///< Sprite images
signed char* xOffsets;
signed char* yOffsets;
signed char* xOffsets; ///< Horizontal offsets for each frame
signed char* yOffsets; ///< Vertical offsets for each frame
bool ignoreDefaultYOffset;
signed char shootX;
signed char shootY;
signed char accessoryX;
signed char accessoryY;
signed char yOffset;
signed char shootX; ///< Bullet generation x-coordinate
signed char shootY; ///< Bullet generation y-coordinate
signed char accessoryX; ///< Accessory animation x-coordinate
signed char accessoryY; ///< Accessory animation y-coordinate
signed char yOffset; ///< Vertical offset
unsigned char frames; ///< Number of frames
unsigned char frame; ///< Current frame
unsigned char accessory; ///< Number of an animation that is an accessory to this animation
......@@ -56,11 +56,12 @@ class Anim {
Anim ();
~Anim ();
void setData (int amount, signed char sX, signed char sY, signed char aX, signed char aY, unsigned char a, signed char y);
void setData (int length, signed char sX, signed char sY, signed char aX, signed char aY, unsigned char a, signed char y);
void setFrame (int nextFrame, bool looping);
void setFrameData (Sprite *frameSprite, signed char x, signed char y);
int getWidth ();
int getHeight ();
int getLength ();
fixed getShootX ();
fixed getShootY ();
fixed getAccessoryX ();
......
......@@ -33,6 +33,13 @@
#include "player/levelplayer.h"
/**
* Create a bullet fired by a player.
*
* @param sourcePlayer The player that fired the bullet
* @param lower Indicates if this the second of two bullets to be created
* @param ticks Time
*/
Bullet::Bullet (LevelPlayer* sourcePlayer, bool lower, unsigned int ticks) {
Anim* anim;
......@@ -89,33 +96,16 @@ Bullet::Bullet (LevelPlayer* sourcePlayer, bool lower, unsigned int ticks) {
}
Bullet::Bullet (Event* sourceEvent, bool facing, unsigned int ticks) {
Anim* anim;
// Properties based on the event
next = level->bullets;
source = NULL;
type = sourceEvent->getProperty(E_BULLET);
direction = facing? 1: 0;
sprite = level->getSprite(((unsigned char *)level->getBullet(type))[B_SPRITE + direction]);
anim = level->getAnim(sourceEvent->getProperty(facing? E_LSHOOTANIM: E_RSHOOTANIM));
x = sourceEvent->getX() + anim->getShootX();
y = sourceEvent->getY() + anim->getShootY() - ITOF((sprite->getHeight() / 2) - 2);
dx = level->getBullet(type)[B_XSPEED + direction] * 500 * F1;
dy = level->getBullet(type)[B_YSPEED + direction] * 250 * F1;
time = ticks + T_BULLET;
level->playSound(level->getBullet(type)[B_STARTSOUND]);
return;
}
Bullet::Bullet (fixed xStart, fixed yStart, int bullet, bool facing, unsigned int ticks) {
/**
* Create a bullet fired by an event.
*
* @param xStart The x-coordinate of the bullet
* @param yStart The y-coordinate of the bullet
* @param bullet Type
* @param facing The direction of the bullet
* @param ticks Time
*/
Bullet::Bullet (fixed xStart, fixed yStart, unsigned char bullet, bool facing, unsigned int ticks) {
// Properties based on a given bullet type and starting position
......@@ -138,6 +128,13 @@ Bullet::Bullet (fixed xStart, fixed yStart, int bullet, bool facing, unsigned in
}
/**
* Create a bullet fired by a bird.
*
* @param sourceBird The bird that fired the bullet
* @param lower Indicates if this the second of two bullets to be created
* @param ticks Time
*/
Bullet::Bullet (Bird* sourceBird, bool lower, unsigned int ticks) {
// Properties based on the bird and its player
......@@ -172,6 +169,9 @@ Bullet::Bullet (Bird* sourceBird, bool lower, unsigned int ticks) {
}
/**
* Delete all bullets.
*/
Bullet::~Bullet () {
if (next) delete next;
......@@ -181,6 +181,11 @@ Bullet::~Bullet () {
}
/**
* Delete this bullet.
*
* @return The next bullet
*/
Bullet* Bullet::remove () {
Bullet* oldNext;
......@@ -194,6 +199,11 @@ Bullet* Bullet::remove () {
}
/**
* Get the player responsible for this bullet.
*
* @return The player (NULL if fired by an event)
*/
LevelPlayer* Bullet::getSource () {
return source;
......@@ -201,6 +211,14 @@ LevelPlayer* Bullet::getSource () {
}
/**
* Bullet iteration.
*
* @param ticks Time
* @param msps Ticks per step
*
* @return Remaining bullet
*/
Bullet* Bullet::step (unsigned int ticks, int msps) {
signed char* set;
......@@ -325,6 +343,11 @@ Bullet* Bullet::step (unsigned int ticks, int msps) {
}
/**
* Draw the bullet.
*
* @param change Time since last iteration
*/
void Bullet::draw (int change) {
if (next) next->draw(change);
......
......@@ -59,7 +59,7 @@ class Sprite;
class Bullet : public Movable {
private:
Bullet* next;
Bullet* next; ///< The next bullet
LevelPlayer* source; ///< Source player. If NULL, was fired by an event
Sprite* sprite; ///< Sprite
int type; ///< -1 is TNT, otherwise indexes the bullet set
......@@ -70,9 +70,8 @@ class Bullet : public Movable {
public:
Bullet (LevelPlayer* sourcePlayer, bool lower, unsigned int ticks);
Bullet (Event* sourceEvent, bool facing, unsigned int ticks);
Bullet (Bird* sourceBird, bool lower, unsigned int ticks);
Bullet (fixed xStart, fixed yStart, int bullet, bool facing, unsigned int ticks);
Bullet (fixed xStart, fixed yStart, unsigned char bullet, bool facing, unsigned int ticks);
~Bullet ();
LevelPlayer* getSource ();
......
......@@ -30,29 +30,22 @@
#include "player/levelplayer.h"
Bridge::Bridge (unsigned char gX, unsigned char gY) {
signed char* set;
set = level->getEvent(gX, gY);
x = TTOF(gX);
y = TTOF(gY) + ITOF(set[E_YAXIS]) - F8 - F1;
dx = 0;
dy = 0;
/**
* Create bridge.
*
* @param gX X-coordinate
* @param gY Y-coordinate
*/
Bridge::Bridge (unsigned char gX, unsigned char gY) : Event(gX, gY) {
next = level->getEvents();
gridX = gX;
gridY = gY;
animType = E_LEFTANIM;
flashTime = 0;
y = TTOF(gY) + ITOF(set->multiB);
// Bridges should ignore the default yOffsets
dontUseAnimOffset();
noAnimOffset = true;
// leftDipX and rightDipX used to store leftmost and rightmost player on bridge
// Start with minimum values
leftDipX = set[E_MULTIPURPOSE] * set[E_BRIDGELENGTH] * F4;
leftDipX = set->multiA * set->pieceSize * F4;
rightDipX = 0;
return;
......@@ -60,10 +53,17 @@ Bridge::Bridge (unsigned char gX, unsigned char gY) {
}
/**
* Bridge iteration.
*
* @param ticks Time
* @param msps Ticks per step
*
* @return Remaining event
*/
Event* Bridge::step (unsigned int ticks, int msps) {
LevelPlayer* levelPlayer;
signed char* set;
int count;
fixed bridgeLength, playerDipX, playerDipY;
......@@ -73,7 +73,7 @@ Event* Bridge::step (unsigned int ticks, int msps) {
if (!set) return remove();
bridgeLength = set[E_MULTIPURPOSE] * set[E_BRIDGELENGTH] * F4;
bridgeLength = set->multiA * set->pieceSize * F4;
// Gradually stop the bridge sagging
......@@ -92,8 +92,8 @@ Event* Bridge::step (unsigned int ticks, int msps) {
if (playerDipX < bridgeLength >> 1) playerDipY = playerDipX >> 3;
else playerDipY = (bridgeLength - playerDipX) >> 3;
if (levelPlayer->overlap(x, y + playerDipY - F4, bridgeLength, F8) &&
!level->checkMaskDown(x + playerDipX, y + playerDipY - F32)) {
if (levelPlayer->overlap(x, y - F8 + playerDipY - F4, bridgeLength, F8) &&
!level->checkMaskDown(x + playerDipX, y - F8 + playerDipY - F32)) {
// Player is on the bridge
......@@ -103,7 +103,7 @@ Event* Bridge::step (unsigned int ticks, int msps) {
if (playerDipX > rightDipX) rightDipX = playerDipX;
levelPlayer->setPosition(levelPlayer->getX(), y + playerDipY);
levelPlayer->setPosition(levelPlayer->getX(), y - F8 - F1 + playerDipY);
} else levelPlayer->clearEvent(gridX, gridY);
......@@ -115,52 +115,55 @@ Event* Bridge::step (unsigned int ticks, int msps) {
}
/**
* Draw bridge.
*
* @param ticks Time
* @param change Time since last iteration
*/
void Bridge::draw (unsigned int ticks, int change) {
Anim *anim;
signed char *set;
Anim* anim;
unsigned char frame;
int count;
fixed bridgeLength, leftDipY, rightDipY;
fixed bridgeLength, anchorY, leftDipY, rightDipY;
if (next) next->draw(ticks, change);
// Get the event properties
set = level->getEvent(gridX, gridY);
// If the event has been removed from the grid, do not show it
if (!set) return;
// Check if the event has anything to draw
if (!animType || (set[animType] < 0)) return;
if ((animType == E_NOANIM) || ((set->anims[animType] & 0x7F) == 0)) return;
if (set[E_ANIMSP]) frame = ticks / (set[E_ANIMSP] * 40);
else frame = ticks / 20;
frame = ticks / (set->animSpeed << 5);
anim = level->getAnim(set[animType]);
anim = getAnim();
anim->setFrame(frame + gridX + gridY, true);
// Draw the bridge
bridgeLength = set[E_MULTIPURPOSE] * set[E_BRIDGELENGTH] * F4;
bridgeLength = set->multiA * set->pieceSize * F4;
anchorY = getDrawY(change) - F10 + anim->getOffset();
if (rightDipX >= leftDipX) {
leftDipY = (leftDipX <= (bridgeLength >> 1)) ? leftDipX >> 3: (bridgeLength - leftDipX) >> 3;
rightDipY = (rightDipX <= (bridgeLength >> 1)) ? rightDipX >> 3: (bridgeLength - rightDipX) >> 3;
for (count = 0; count < bridgeLength; count += F4 * set[E_BRIDGELENGTH]) {
for (count = 0; count < bridgeLength; count += F4 * set->pieceSize) {
if (count < leftDipX)
anim->draw(getDrawX(change) + count, getDrawY(change) + TTOF(1) + (count * leftDipY / leftDipX));
else if (count < dy)
anim->draw(getDrawX(change) + count, getDrawY(change) + TTOF(1) + leftDipY + ((count - leftDipX) * (rightDipY - leftDipY) / (rightDipX - leftDipX)));
anim->draw(getDrawX(change) + count, anchorY + (count * leftDipY / leftDipX));
else if (count < rightDipX)
anim->draw(getDrawX(change) + count, anchorY + leftDipY + ((count - leftDipX) * (rightDipY - leftDipY) / (rightDipX - leftDipX)));
else
anim->draw(getDrawX(change) + count, getDrawY(change) + TTOF(1) + ((bridgeLength - count) * rightDipY / (bridgeLength - rightDipX)));
anim->draw(getDrawX(change) + count, anchorY + ((bridgeLength - count) * rightDipY / (bridgeLength - rightDipX)));
}
......@@ -174,12 +177,12 @@ void Bridge::draw (unsigned int ticks, int change) {
// Dip
rightDipY = (rightDipX < bridgeLength - leftDipX) ? rightDipX >> 3: (bridgeLength - leftDipX) >> 3;
for (count = 0; count < bridgeLength; count += F4 * set[E_BRIDGELENGTH]) {
for (count = 0; count < bridgeLength; count += F4 * set->pieceSize) {
if (count < leftDipY)
anim->draw(getDrawX(change) + count, getDrawY(change) + TTOF(1) + (count * rightDipY / leftDipY));
anim->draw(getDrawX(change) + count, anchorY + (count * rightDipY / leftDipY));
else
anim->draw(getDrawX(change) + count, getDrawY(change) + TTOF(1) + ((bridgeLength - count) * rightDipY / (bridgeLength - leftDipY)));
anim->draw(getDrawX(change) + count, anchorY + ((bridgeLength - count) * rightDipY / (bridgeLength - leftDipY)));
}
......
......@@ -16,6 +16,7 @@
* 19th July 2009: Renamed events.cpp to event.cpp
* 2nd March 2010: Created guardians.cpp from parts of event.cpp and eventframe.cpp
* 2nd March 2010: Created bridge.cpp from parts of event.cpp and eventframe.cpp
* 5th February 2011: Moved parts of eventframe.cpp to event.cpp
*
* @section Licence
* Copyright (c) 2005-2010 Alister Thomson
......@@ -36,21 +37,23 @@
#include "../level.h"
#include "event.h"
#include "io/gfx/video.h"
#include "io/sound.h"
#include "player/player.h"
#include "util.h"
Event::Event () {
return;
}
/**
* Create event
*
* @param gX X-coordinate
* @param gY Y-coordinate
*/
Event::Event (unsigned char gX, unsigned char gY) {
set = level->getEvent(gX, gY);
x = TTOF(gX);
y = TTOF(gY + 1);
dx = 0;
......@@ -61,59 +64,17 @@ Event::Event (unsigned char gX, unsigned char gY) {
gridY = gY;
flashTime = 0;
onlyLAnimOffset = false;
onlyRAnimOffset = false;
noAnimOffset = false;
switch (getProperty(E_BEHAVIOUR)) {
case 2: // Walk from side to side
case 4: // Walk from side to side and down hills
animType = E_LEFTANIM;
useRightAnimOffset();
break;
case 6: // Use the path from the level file
case 7: // Flying snake behavior
animType = E_LEFTANIM;
dontUseAnimOffset();
break;
case 21: // Destructible block
case 25: // Float up / Belt
case 37: // Sucker tubes
case 38: // Sucker tubes
case 40: // Monochrome
case 57: // Bubbles
animType = 0;
break;
case 26: // Flip animation
animType = E_RIGHTANIM;
useLeftAnimOffset();
break;
default:
animType = E_LEFTANIM;
break;
}
noAnimOffset = false;
return;
}
/**
* Delete all events
*/
Event::~Event () {
if (next) delete next;
......@@ -123,6 +84,11 @@ Event::~Event () {
}
/**
* Delete this event
*
* @return The next event
*/
Event* Event::remove () {
Event *oldNext;
......@@ -136,6 +102,11 @@ Event* Event::remove () {
}
/**
* Get the next event
*
* @return The next event
*/
Event * Event::getNext () {
return next;
......@@ -143,14 +114,18 @@ Event * Event::getNext () {
}
/**
* Initiate the destruction of the event
*
* @param ticks Time
*/
void Event::destroy (unsigned int ticks) {
level->setEventTime(gridX, gridY, ticks + T_FINISH);
animType = E_LFINISHANIM | (animType & 1);
animType = ((animType == E_RIGHTANIM) || (animType == E_RSHOOTANIM)) ?
E_RFINISHANIM: E_LFINISHANIM;
level->setEventTime(gridX, gridY, ticks + (getAnim()->getLength() * set->animSpeed << 3));
level->playSound(getProperty(E_SOUND));
level->playSound(set->sound);
return;
......@@ -170,8 +145,7 @@ bool Event::hit (LevelPlayer *source, unsigned int ticks) {
int hitsRemaining;
// Check if event has already been destroyed
if ((animType == E_LFINISHANIM) || (animType == E_RFINISHANIM) ||
(ticks < flashTime)) return false;
if (((animType & ~1) == E_LFINISHANIM) || (ticks < flashTime)) return false;
hitsRemaining = level->hitEvent(gridX, gridY, source);
......@@ -190,17 +164,23 @@ bool Event::hit (LevelPlayer *source, unsigned int ticks) {
}
/**
* Determine whether or not the event is an enemy
*
* @return Whether or not the event is an enemy
*/
bool Event::isEnemy () {
signed char *set;
set = level->getEvent(gridX, gridY);
return set[E_HITSTOKILL] && (set[E_MODIFIER] == 0);
return set->strength && (set->modifier == 0);
}
/**
* Determine whether or not the event is from the given position
*
* @return Whether or not the event is from the given position
*/
bool Event::isFrom (unsigned char gX, unsigned char gY) {
return (gX == gridX) && (gY == gridY);
......@@ -208,15 +188,20 @@ bool Event::isFrom (unsigned char gX, unsigned char gY) {
}
/**
* Get the width of the event
*
* @return The width of the event
*/
fixed Event::getWidth () {
fixed width;
if (!animType) return F32;
if (animType == E_NOANIM) return F32;
if (getProperty(animType) <= 0) return 0;
if ((set->anims[animType] & 0x7F) == 0) return 0;
width = ITOF(level->getAnim(getProperty(animType))->getWidth());
width = ITOF(getAnim()->getWidth());
// Blank sprites for e.g. invisible springs
if ((width == F1) && (getHeight() == F1)) return F32;
......@@ -226,22 +211,37 @@ fixed Event::getWidth () {
}
/**
* Get the height of the event
*
* @return The height of the event
*/
fixed Event::getHeight () {
if (!animType) return F32;
if (animType == E_NOANIM) return F32;
if (getProperty(animType) <= 0) return 0;
if ((set->anims[animType] & 0x7F) == 0) return 0;
return ITOF(level->getAnim(getProperty(animType))->getHeight());
return ITOF(getAnim()->getHeight());
}
/**
* Determine whether or not the event is overlapping the given area
*
* @param left The x-coordinate of the left of the area
* @param top The y-coordinate of the top of the area
* @param width The width of the area
* @param height The height of the area
*
* @return Whether or not there is an overlap
*/
bool Event::overlap (fixed left, fixed top, fixed width, fixed height) {
fixed offset = 0;
if (getAnim(animType) && noAnimOffset)
offset = getAnim(animType)->getOffset();
if (getAnim() && noAnimOffset)
offset = getAnim()->getOffset();
return (x + getWidth() >= left) &&
(x < left + width) &&
......@@ -251,42 +251,99 @@ bool Event::overlap (fixed left, fixed top, fixed width, fixed height) {
}
signed char Event::getProperty (unsigned char property) {
/**
* Get the current animation
*
* @return Animation
*/
Anim* Event::getAnim () {
if (animType == E_NOANIM) return NULL;
// If there is no shooting animation, use the normal animation instead
if (((animType & ~1) == E_LSHOOTANIM) && (set->anims[animType] == 0))
return level->getAnim(set->anims[animType & 1] & 0x7F);
return level->getAnim(set->anims[animType] & 0x7F);
}
signed char *set;
/**
* Functionality required by all event types on each iteration
*
* @param ticks Time
* @param msps Ticks per step
*
* @return Animation
*/
EventType* Event::prepareStep (unsigned int ticks, int msps) {
// Process the next event
if (next) next = next->step(ticks, msps);
// Get the event properties
set = level->getEvent(gridX, gridY);
if (set) return set[property];
// If the event has been removed from the grid, destroy it
if (!set) return NULL;
// If the event and its origin are off-screen, the event is not in the
// process of self-destruction, remove it
if (((animType & ~1) != E_LFINISHANIM) &&
((x < viewX - F192) || (x > viewX + ITOF(viewW) + F192) ||
(y < viewY - F160) || (y > viewY + ITOF(viewH) + F160)) &&
((gridX < FTOT(viewX) - 1) ||
(gridX > ITOT(FTOI(viewX) + viewW) + 1) ||
(gridY < FTOT(viewY) - 1) ||
(gridY > ITOT(FTOI(viewY) + viewH) + 1))) return NULL;
return 0;
return set;
}
Anim* Event::getAnim (unsigned char property) {
/**
* Draw the event's energy bar
*
* @param ticks Time
*/
void Event::drawEnergy (unsigned int ticks) {
switch (property) {
Anim* anim;
int hits;
case E_LEFTANIM:
case E_RIGHTANIM:
case E_LFINISHANIM:
case E_RFINISHANIM:
case E_LSHOOTANIM:
case E_RSHOOTANIM:
if (!set || set->modifier != 8) {
if (getProperty(property) < 0)
return level->getAnim(getProperty(property) + 128);
if (next) next->drawEnergy(ticks);
return level->getAnim(getProperty(property));
return;
default:
} else if (set->strength) {
return 0;
// Draw boss energy bar
}
hits = level->getEventHits(gridX, gridY) * 100 / set->strength;
}
// Devan head
anim = level->getMiscAnim(1);
anim->setFrame(0, true);
if (ticks < flashTime) anim->flashPalette(0);
anim->draw(ITOF(viewW - 44), ITOF(hits + 48));
if (ticks < flashTime) anim->restorePalette();
// Bar
drawRect(viewW - 40, hits + 40, 12, 100 - hits, (ticks < flashTime)? 0: 32);
}
return;
}
......@@ -29,6 +29,7 @@
#define _EVENT_H
#include "../level.h"
#include "../movable.h"
#include "OpenJazz.h"
......@@ -37,39 +38,17 @@
// Constants
// Indexes for elements of the event set
/* Names taken from J1CS/JCS94 and J1E
* ...Except of course it carries on the fine JCF tradition of changing the
* spelling of words such as "behavior" */
#define E_DIFFICULTY 0
#define E_REFLECTION 2
#define E_BEHAVIOR 4
#define E_BEHAVIOUR 4
#define E_LEFTANIM 5
#define E_RIGHTANIM 6
#define E_MAGNITUDE 8
#define E_HITSTOKILL 9
#define E_MODIFIER 10
#define E_ADDEDSCORE 11
#define E_BULLET 12
#define E_BULLETSP 13
#define E_MOVEMENTSP 15
#define E_ANIMSP 17
#define E_SOUND 21
#define E_MULTIPURPOSE 22
#define E_YAXIS 23
#define E_BRIDGELENGTH 24
#define E_CHAINLENGTH 25
#define E_CHAINANGLE 26
#define E_LFINISHANIM 28
#define E_RFINISHANIM 29
#define E_LSHOOTANIM 30
#define E_RSHOOTANIM 31
// Animations
#define E_LEFTANIM 0
#define E_RIGHTANIM 1
#define E_LFINISHANIM 2
#define E_RFINISHANIM 3
#define E_LSHOOTANIM 4
#define E_RSHOOTANIM 5
#define E_NOANIM 6
// Delays
#define T_FLASH 100
#define T_FINISH 200
#define T_SHOOT 300
// Speed factors
#define ES_SLOW ITOF(80)
......@@ -84,28 +63,25 @@ class LevelPlayer;
class Event : public Movable {
protected:
Event* next;
Event* next; ///< Next event
EventType* set; ///< Type
unsigned char gridX, gridY; ///< Grid position of the event
unsigned char animType; ///< E_LEFTANIM, etc, or 0
unsigned char frame; ///< Current animation frame
unsigned int flashTime;///< Time flash will end
unsigned char animType; ///< Animation type (E_LEFTANIM, etc.)
unsigned int flashTime; ///< Time flash will end
bool noAnimOffset;
bool onlyLAnimOffset;
bool onlyRAnimOffset;
Event ();
Event (unsigned char gX, unsigned char gY);
Event* remove ();
void destroy (unsigned int ticks);
fixed getWidth ();
Anim* getAnim ();
fixed getHeight ();
signed char* prepareStep (unsigned int ticks, int msps);
void useLeftAnimOffset ();
void useRightAnimOffset ();
void dontUseAnimOffset ();
fixed getWidth ();
EventType* prepareStep (unsigned int ticks, int msps);
public:
Event (unsigned char gX, unsigned char gY);
virtual ~Event ();
Event* getNext ();
......@@ -113,14 +89,31 @@ class Event : public Movable {
bool isEnemy ();
bool isFrom (unsigned char gX, unsigned char gY);
virtual bool overlap (fixed left, fixed top, fixed width, fixed height);
signed char getProperty (unsigned char property);
Anim* getAnim (unsigned char property);
virtual Event* step (unsigned int ticks, int msps);
virtual void draw (unsigned int ticks, int change);
virtual Event* step (unsigned int ticks, int msps) = 0;
virtual void draw (unsigned int ticks, int change) = 0;
void drawEnergy (unsigned int ticks);
};
/// Standard JJ1 level event
class StandardEvent : public Event {
private:
fixed node; ///< Current event path node
bool onlyLAnimOffset;
bool onlyRAnimOffset;
void move (unsigned int ticks, int msps);
public:
StandardEvent (unsigned char gX, unsigned char gY);
Event* step (unsigned int ticks, int msps);
void draw (unsigned int ticks, int change);
};
/// JJ1 level bridge
class Bridge : public Event {
......
......@@ -36,17 +36,13 @@
#include <stdlib.h>
Guardian::Guardian(unsigned char gX, unsigned char gY) {
x = TTOF(gX);
y = TTOF(gY + 1);
dx = 0;
dy = 0;
next = level->getEvents();
gridX = gX;
gridY = gY;
flashTime = 0;
/**
* Create guardian.
*
* @param gX X-coordinate
* @param gY Y-coordinate
*/
Guardian::Guardian(unsigned char gX, unsigned char gY) : Event(gX, gY) {
stage = 0;
......@@ -55,6 +51,12 @@ Guardian::Guardian(unsigned char gX, unsigned char gY) {
}
/**
* Create episode B guardian.
*
* @param gX X-coordinate
* @param gY Y-coordinate
*/
DeckGuardian::DeckGuardian (unsigned char gX, unsigned char gY) : Guardian(gX, gY) {
return;
......@@ -62,6 +64,16 @@ DeckGuardian::DeckGuardian (unsigned char gX, unsigned char gY) : Guardian(gX, g
}
/**
* Determine whether or not the guardian overlaps the given area.
*
* @param left The x-coordinate of the left of the area
* @param top The y-coordinate of the top of the area
* @param width The width of the area
* @param height The height of the area
*
* @return Whether or not there is an overlap
*/
bool DeckGuardian::overlap (fixed left, fixed top, fixed width, fixed height) {
if (stage == 0)
......@@ -81,9 +93,16 @@ bool DeckGuardian::overlap (fixed left, fixed top, fixed width, fixed height) {
}
/**
* Episode B guardian iteration.
*
* @param ticks Time
* @param msps Ticks per step
*
* @return Remaining event
*/
Event* DeckGuardian::step (unsigned int ticks, int msps) {
signed char* set;
int count;
......@@ -92,8 +111,6 @@ Event* DeckGuardian::step (unsigned int ticks, int msps) {
if (!set) return remove();
// Handle behaviour
count = level->getEventHits(gridX, gridY);
if (count < 8) stage = 0;
......@@ -104,9 +121,9 @@ Event* DeckGuardian::step (unsigned int ticks, int msps) {
// If the event has been destroyed, play its finishing animation and set its
// reaction time
if (set[E_HITSTOKILL] &&
(level->getEventHits(gridX, gridY) >= set[E_HITSTOKILL]) &&
(animType != E_LFINISHANIM) && (animType != E_RFINISHANIM)) {
if (set->strength &&
(level->getEventHits(gridX, gridY) >= set->strength) &&
((animType & ~1) != E_LFINISHANIM)) {
destroy(ticks);
......@@ -117,7 +134,7 @@ Event* DeckGuardian::step (unsigned int ticks, int msps) {
if (level->getEventTime(gridX, gridY) &&
(ticks > level->getEventTime(gridX, gridY))) {
if ((animType == E_LFINISHANIM) || (animType == E_RFINISHANIM)) {
if ((animType & ~1) == E_LFINISHANIM) {
// The event has been destroyed, so remove it
level->clearEvent(gridX, gridY);
......@@ -138,18 +155,20 @@ Event* DeckGuardian::step (unsigned int ticks, int msps) {
}
/**
* Draw episode B guardian.
*
* @param ticks Time
* @param change Time since last iteration
*/
void DeckGuardian::draw (unsigned int ticks, int change) {
Anim* anim;
signed char* set;
if (next) next->draw(ticks, change);
// Get the event properties
set = level->getEvent(gridX, gridY);
// If the event has been removed from the grid, do not show it
if (!set) return;
......@@ -178,10 +197,14 @@ void DeckGuardian::draw (unsigned int ticks, int change) {
}
/**
* Create episode 1 guardian.
*
* @param gX X-coordinate
* @param gY Y-coordinate
*/
MedGuardian::MedGuardian(unsigned char gX, unsigned char gY) : Guardian(gX, gY) {
animType = E_LEFTANIM;
stage = 0;
direction = 1;
shoot = false;
......@@ -190,23 +213,29 @@ MedGuardian::MedGuardian(unsigned char gX, unsigned char gY) : Guardian(gX, gY)
}
/*bool MedGuardian::overlap(fixed left, fixed top, fixed width, fixed height) {
return false;
}*/
/**
* Episode 1 guardian iteration.
*
* @param ticks Time
* @param msps Ticks per step
*
* @return Remaining event
*/
Event* MedGuardian::step(unsigned int ticks, int msps) {
Anim *anim = getAnim(animType);
Anim *anim = getAnim();
fixed sin = fSin(ticks / 2);
fixed cos = fCos(ticks / 2);
if (level->getEventHits(gridX, gridY) >= getProperty(E_HITSTOKILL) / 2)
set = prepareStep(ticks, msps);
if (!set) return remove();
if (level->getEventHits(gridX, gridY) >= set->strength / 2)
stage = 1;
if (level->getEventHits(gridX, gridY) >= getProperty(E_HITSTOKILL))
if (level->getEventHits(gridX, gridY) >= set->strength)
stage = 2;
// Stage 0: Move in an eight shape and fire the occasional shot
......@@ -262,10 +291,10 @@ Event* MedGuardian::step(unsigned int ticks, int msps) {
}
// Decide if there should be a shot
if ((ticks % (getProperty(E_BULLETSP) * 25) >
(unsigned int)(getProperty(E_BULLETSP) * 25) - T_SHOOT)) {
if ((ticks % (set->bulletPeriod * 25) >
(unsigned int)(set->bulletPeriod * 25) - 300)) {
level->setEventTime(gridX, gridY, ticks + T_SHOOT);
level->setEventTime(gridX, gridY, ticks + 300);
shoot = true;
}
......@@ -275,12 +304,12 @@ Event* MedGuardian::step(unsigned int ticks, int msps) {
(ticks > level->getEventTime(gridX, gridY)) &&
shoot) {
if ((getProperty(E_BULLET) < 32) &&
(level->getBullet(getProperty(E_BULLET))[B_SPRITE] != 0))
if ((set->bullet < 32) &&
(level->getBullet(set->bullet)[B_SPRITE] != 0))
level->bullets = new Bullet(
x + anim->getAccessoryShootX(),
y + anim->getAccessoryShootY(),
getProperty(E_BULLET), (animType != E_LEFTANIM), ticks);
set->bullet, (animType != E_LEFTANIM), ticks);
shoot = false;
......@@ -368,7 +397,7 @@ Event* MedGuardian::step(unsigned int ticks, int msps) {
level->setTile(
FTOT(x + ITOF((anim->getWidth() / 2))),
FTOT(y) + 1,
getProperty(E_MAGNITUDE));
set->magnitude);
direction = 8;
......@@ -397,10 +426,17 @@ Event* MedGuardian::step(unsigned int ticks, int msps) {
}
/**
* Draw episode 1 guardian.
*
* @param ticks Time
* @param change Time since last iteration
*/
void MedGuardian::draw(unsigned int ticks, int change) {
Anim *anim;
Anim *accessory;
unsigned char frame;
if (next) next->draw(ticks, change);
......@@ -408,21 +444,23 @@ void MedGuardian::draw(unsigned int ticks, int change) {
fixed yChange = getDrawY(change);
if (getProperty(E_ANIMSP))
frame = ticks / (getProperty(E_ANIMSP) * 40);
else
frame = ticks / 20;
frame = ticks / (set->animSpeed << 5);
if (stage == 0)
anim = getAnim(animType);
anim = getAnim();
else
anim = getAnim(animType == E_LEFTANIM ? E_LFINISHANIM : E_RFINISHANIM);
anim = level->getAnim(set->anims[E_LFINISHANIM | (animType & 1)] & 0x7F);
anim->setFrame(frame + gridX + gridY, true);
if (ticks < flashTime) anim->flashPalette(0);
anim->draw(xChange, yChange);
if (ticks < flashTime) anim->restorePalette();
accessory = anim->getAccessory();
accessory->setFrame(frame + gridX + gridY, true);
accessory->disableDefaultOffset();
......
......@@ -36,7 +36,6 @@ class Guardian : public Event {
protected:
int stage;
public:
Guardian (unsigned char gX, unsigned char gY);
};
......
/**
*
* @file eventframe.cpp
* @file standardevent.cpp
*
* Part of the OpenJazz project
*
......@@ -9,6 +9,8 @@
* 19th July 2009: Created eventframe.cpp from parts of events.cpp
* 2nd March 2010: Created guardians.cpp from parts of event.cpp and eventframe.cpp
* 2nd March 2010: Created bridge.cpp from parts of event.cpp and eventframe.cpp
* 5th February 2011: Moved parts of eventframe.cpp to event.cpp
* 5th February 2011: Renamed eventframe.cpp to standardevent.cpp
*
* @section Licence
* Copyright (c) 2005-2010 Alister Thomson
......@@ -30,7 +32,7 @@
#include "../level.h"
#include "event.h"
#include "io/gfx/video.h"
#include "io/gfx/sprite.h"
#include "io/sound.h"
#include "player/levelplayer.h"
#include "util.h"
......@@ -38,61 +40,88 @@
#include <stdlib.h>
signed char* Event::prepareStep (unsigned int ticks, int msps) {
/**
* Create standard event.
*
* @param gX X-coordinate
* @param gY Y-coordinate
*/
StandardEvent::StandardEvent (unsigned char gX, unsigned char gY) : Event(gX, gY) {
signed char* set;
node = 0;
onlyLAnimOffset = false;
onlyRAnimOffset = false;
// Process the next event
if (next) next = next->step(ticks, msps);
switch (set->movement) {
case 2: // Walk from side to side
case 4: // Walk from side to side and down hills
// Get the event properties
set = level->getEvent(gridX, gridY);
animType = E_LEFTANIM;
onlyRAnimOffset = true;
// If the event has been removed from the grid, destroy it
if (!set) return NULL;
break;
// If the event and its origin are off-screen, the event is not in the
// process of self-destruction, remove it
if ((animType != E_LFINISHANIM) && (animType != E_RFINISHANIM) &&
((x < viewX - F192) || (x > viewX + ITOF(viewW) + F192) ||
(y < viewY - F160) || (y > viewY + ITOF(viewH) + F160)) &&
((gridX < FTOT(viewX) - 1) ||
(gridX > ITOT(FTOI(viewX) + viewW) + 1) ||
(gridY < FTOT(viewY) - 1) ||
(gridY > ITOT(FTOI(viewY) + viewH) + 1))) return NULL;
case 6: // Use the path from the level file
case 7: // Flying snake behavior
animType = E_LEFTANIM;
noAnimOffset = true;
// Find frame
if (animType && (set[animType] >= 0)) {
break;
if ((animType != E_LEFTANIM) && (animType != E_RIGHTANIM))
frame = (ticks + T_FINISH - level->getEventTime(gridX, gridY)) / 40;
else if (set[E_ANIMSP])
frame = ticks / (set[E_ANIMSP] * 40);
else
frame = ticks / 20;
case 21: // Destructible block
case 25: // Float up / Belt
case 37: // Sucker tubes
case 38: // Sucker tubes
case 40: // Monochrome
case 57: // Bubbles
animType = E_NOANIM;
break;
case 26: // Flip animation
animType = E_RIGHTANIM;
onlyLAnimOffset = true;
break;
default:
break;
}
return set;
return;
}
Event* Event::step (unsigned int ticks, int msps) {
/**
* Move standard event.
*
* @param ticks Time
* @param msps Ticks per step
*/
void StandardEvent::move (unsigned int ticks, int msps) {
LevelPlayer* levelPlayer;
fixed width, height;
signed char* set;
int count;
int offset;
int length;
fixed angle;
set = prepareStep(ticks, msps);
if ((animType & ~1) == E_LSHOOTANIM) {
if (!set) return remove();
dx = 0;
dy = 0;
return;
}
levelPlayer = localPlayer->getLevelPlayer();
......@@ -101,9 +130,9 @@ Event* Event::step (unsigned int ticks, int msps) {
width = getWidth();
height = getHeight();
// Handle behaviour
// Handle movement
switch (set[E_BEHAVIOUR]) {
switch (set->movement) {
case 1:
......@@ -160,16 +189,18 @@ Event* Event::step (unsigned int ticks, int msps) {
case 6:
count = level->path[set[E_MULTIPURPOSE]].node;
node = (node + (msps << 5)) % ITOF(level->path[set->multiA].length);
// Use the path from the level file
dx = TTOF(gridX) + ITOF(level->path[set[E_MULTIPURPOSE]].x[count]) - x;
dy = TTOF(gridY) + ITOF(level->path[set[E_MULTIPURPOSE]].y[count]) - y;
dx = TTOF(gridX) + ITOF(level->path[set->multiA].x[FTOI(node)]) - x;
dy = TTOF(gridY) + ITOF(level->path[set->multiA].y[FTOI(node)]) - y;
dx = ((dx << 10) / msps) * set[E_MOVEMENTSP];
dy = ((dy << 10) / msps) * set[E_MOVEMENTSP];
x += dx;
y += dy;
dx = (dx << 10) / msps;
dy = (dy << 10) / msps;
break;
return;
case 7:
......@@ -238,8 +269,8 @@ Event* Event::step (unsigned int ticks, int msps) {
case 16:
// Move across level to the left or right
if (set[E_MAGNITUDE] == 0) dx = -ES_SLOW;
else dx = set[E_MAGNITUDE] * ES_SLOW;
if (set->magnitude == 0) dx = -ES_SLOW;
else dx = set->magnitude * ES_SLOW;
break;
......@@ -270,8 +301,8 @@ Event* Event::step (unsigned int ticks, int msps) {
case 21:
// Destructible block
if (level->getEventHits(gridX, gridY) >= set[E_HITSTOKILL])
level->setTile(gridX, gridY, set[E_MULTIPURPOSE]);
if (level->getEventHits(gridX, gridY) >= set->strength)
level->setTile(gridX, gridY, set->multiA);
break;
......@@ -309,29 +340,35 @@ Event* Event::step (unsigned int ticks, int msps) {
// Rotate
offset = set[E_BRIDGELENGTH] * set[E_CHAINLENGTH];
angle = set[E_MAGNITUDE] * ticks / 13;
length = set->pieceSize * set->pieces;
angle = set->magnitude * ticks / 13;
dx = TTOF(gridX) + (fSin(angle) * offset) - x;
dy = TTOF(gridY) + ((fCos(angle) + F1) * offset) - y;
dx = ((dx << 10) / msps) * set[E_MOVEMENTSP];
dy = ((dy << 10) / msps) * set[E_MOVEMENTSP];
dx = TTOF(gridX) + (fSin(angle) * length) - x;
dy = TTOF(gridY) + ((fCos(angle) + F1) * length) - y;
break;
x += dx;
y += dy;
dx = (dx << 10) / msps;
dy = (dy << 10) / msps;
return;
case 30:
// Swing
offset = set[E_BRIDGELENGTH] * set[E_CHAINLENGTH];
angle = (set[E_CHAINANGLE] << 2) + (set[E_MAGNITUDE] * ticks / 13);
length = set->pieceSize * set->pieces;
angle = (set->angle << 2) + (set->magnitude * ticks / 13);
dx = TTOF(gridX) + (fSin(angle) * offset) - x;
dy = TTOF(gridY) + ((abs(fCos(angle)) + F1) * offset) - y;
dx = ((dx << 10) / msps) * set[E_MOVEMENTSP];
dy = ((dy << 10) / msps) * set[E_MOVEMENTSP];
dx = TTOF(gridX) + (fSin(angle) * length) - x;
dy = TTOF(gridY) + ((abs(fCos(angle)) + F1) * length) - y;
break;
x += dx;
y += dy;
dx = (dx << 10) / msps;
dy = (dy << 10) / msps;
return;
case 31:
......@@ -385,14 +422,18 @@ Event* Event::step (unsigned int ticks, int msps) {
if (ticks > level->getEventTime(gridX, gridY)) {
if (animType == E_LEFTANIM)
dy = -(F16 + y - (TTOF(gridY) - (set[E_MULTIPURPOSE] * F12))) * 10;
dy = -(F16 + y - (TTOF(gridY) - (set->multiA * F12))) * 10;
else
dy = (F16 + y - (TTOF(gridY) - (set[E_MULTIPURPOSE] * F12))) * 10;
dy = (F16 + y - (TTOF(gridY) - (set->multiA * F12))) * 10;
} else {
dy = TTOF(gridY) + F16 - y;
dy = ((dy << 10) / msps) * set[E_MOVEMENTSP];
y += dy;
dy = ((dy << 10) / msps);
return;
}
......@@ -455,8 +496,9 @@ Event* Event::step (unsigned int ticks, int msps) {
if (players[count].getLevelPlayer()->overlap(x + F8, y + F4 - height, width - F16,
height - F8)) {
players[count].getLevelPlayer()->setSpeed(set[E_YAXIS]? set[E_MAGNITUDE] * F4: set[E_MAGNITUDE] * F40,
set[E_YAXIS]? set[E_MULTIPURPOSE] * -F20: 0);
players[count].getLevelPlayer()->setSpeed(
set->multiB? set->magnitude * F4: set->magnitude * F40,
set->multiB? set->multiA * -F20: 0);
}
......@@ -539,16 +581,50 @@ Event* Event::step (unsigned int ticks, int msps) {
}
dx /= set[E_MOVEMENTSP];
dy /= set[E_MOVEMENTSP];
dx /= set->speed;
dy /= set->speed;
x += (dx * msps) >> 10;
y += (dy * msps) >> 10;
return;
}
/**
* Event iteration.
*
* @param ticks Time
* @param msps Ticks per step
*
* @return Remaining event
*/
Event* StandardEvent::step (unsigned int ticks, int msps) {
LevelPlayer* levelPlayer;
fixed width, height;
int count;
set = prepareStep(ticks, msps);
if (!set) return remove();
levelPlayer = localPlayer->getLevelPlayer();
// Find dimensions
width = getWidth();
height = getHeight();
// Move
move(ticks, msps);
// Choose animation and direction
if ((animType == E_LEFTANIM) || (animType == E_RIGHTANIM)) {
if ((animType & ~1) == E_LEFTANIM) {
switch (set[E_BEHAVIOUR]) {
switch (set->movement) {
case 2:
......@@ -605,12 +681,9 @@ Event* Event::step (unsigned int ticks, int msps) {
case 6:
// Use the path from the level file
count = level->path[set[E_MULTIPURPOSE]].node;
// Check movement direction
if ((count < 3) ||
(level->path[set[E_MULTIPURPOSE]].x[count] <= level->path[set[E_MULTIPURPOSE]].x[count - 3]))
if ((FTOI(node) < 3) ||
(level->path[set->multiA].x[FTOI(node)] <= level->path[set->multiA].x[FTOI(node) - 3]))
animType = E_LEFTANIM;
else
animType = E_RIGHTANIM;
......@@ -702,9 +775,9 @@ Event* Event::step (unsigned int ticks, int msps) {
// Moving platform
if (x < TTOF(gridX) - (set[E_BRIDGELENGTH] << 14))
if (x < TTOF(gridX) - (set->pieceSize << 14))
animType = E_RIGHTANIM;
else if (!animType || (x > TTOF(gridX + set[E_BRIDGELENGTH])))
else if (x > TTOF(gridX + set->pieceSize))
animType = E_LEFTANIM;
break;
......@@ -733,12 +806,12 @@ Event* Event::step (unsigned int ticks, int msps) {
if (ticks > level->getEventTime(gridX, gridY)) {
if (y <= F16 + TTOF(gridY) - (set[E_MULTIPURPOSE] * F12))
if (y <= F16 + TTOF(gridY) - (set->multiA * F12))
animType = E_RIGHTANIM;
else if (y >= F16 + TTOF(gridY)) {
animType = E_LEFTANIM;
level->setEventTime(gridX, gridY, ticks + (set[E_YAXIS] * 50));
level->setEventTime(gridX, gridY, ticks + (set->multiB * 50));
}
......@@ -762,7 +835,7 @@ Event* Event::step (unsigned int ticks, int msps) {
} else if (animType == E_RIGHTANIM) {
if (level->checkMaskDown(x + width + F4, y - (height >> 1)) ||
(x + width + F4 > viewX + ITOF(viewW)))
(x + width + F4 > viewX + ITOF(320)))
animType = E_LEFTANIM;
}
......@@ -808,26 +881,27 @@ Event* Event::step (unsigned int ticks, int msps) {
// If the event has been destroyed, play its finishing animation and set its
// reaction time
if (set[E_HITSTOKILL] &&
(level->getEventHits(gridX, gridY) >= set[E_HITSTOKILL]) &&
(animType != E_LFINISHANIM) && (animType != E_RFINISHANIM)) {
if (set->strength &&
(level->getEventHits(gridX, gridY) >= set->strength) &&
((animType & ~1) != E_LFINISHANIM)) {
destroy(ticks);
}
// Generate bullet
if (set[E_BULLETSP]) {
if (set->bulletPeriod) {
if ((ticks % (set[E_BULLETSP] * 25) >
(unsigned int)(set[E_BULLETSP] * 25) - T_SHOOT) &&
((animType == E_LEFTANIM) || (animType == E_RIGHTANIM))) {
count = level->getAnim(set->anims[E_LSHOOTANIM | (animType & 1)])->getLength() * set->animSpeed << 5;
if ((ticks % (set->bulletPeriod * 32) > (unsigned int)(set->bulletPeriod * 32) - count) &&
((animType & ~1) == E_LEFTANIM)) {
// Enter firing mode
if (animType == E_LEFTANIM) animType = E_LSHOOTANIM;
else animType = E_RSHOOTANIM;
level->setEventTime(gridX, gridY, ticks + T_SHOOT);
level->setEventTime(gridX, gridY, ticks + count);
}
......@@ -838,28 +912,23 @@ Event* Event::step (unsigned int ticks, int msps) {
if (level->getEventTime(gridX, gridY) &&
(ticks > level->getEventTime(gridX, gridY))) {
if ((animType == E_LFINISHANIM) || (animType == E_RFINISHANIM)) {
if ((animType & ~1) == E_LFINISHANIM) {
// The event has been destroyed, so remove it
level->clearEvent(gridX, gridY);
return remove();
} else if (animType == E_LSHOOTANIM) {
} else if ((animType & ~1) == E_LSHOOTANIM) {
if ((set[E_BULLET] < 32) &&
(level->getBullet(set[E_BULLET])[B_SPRITE] != 0))
level->bullets = new Bullet(this, false, ticks);
if ((set->bullet < 32) &&
(level->getBullet(set->bullet)[B_SPRITE | (animType & 1)] != 0))
level->bullets = new Bullet(
x + getAnim()->getShootX(),
y + getAnim()->getShootY() - F4,
set->bullet, (animType & 1)? true: false, ticks);
animType = E_LEFTANIM;
} else if (animType == E_RSHOOTANIM) {
if ((set[E_BULLET] < 32) &&
(level->getBullet(set[E_BULLET])[B_SPRITE + 1] != 0))
level->bullets = new Bullet(this, true, ticks);
animType = E_RIGHTANIM;
animType = E_LEFTANIM | (animType & 1);
} else {
......@@ -872,7 +941,7 @@ Event* Event::step (unsigned int ticks, int msps) {
if (level->getStage() == LS_END) return this;
if ((animType == E_LFINISHANIM) || (animType == E_RFINISHANIM)) return this;
if ((animType & ~1) == E_LFINISHANIM) return this;
// Handle contact with player
......@@ -885,10 +954,10 @@ Event* Event::step (unsigned int ticks, int msps) {
fixed offset = 0;
if (getAnim(animType) && noAnimOffset)
offset = getAnim(animType)->getOffset();
if ((animType != E_NOANIM) && getAnim() && noAnimOffset)
offset = getAnim()->getOffset();
if (set[E_MODIFIER] == 6) {
if (set->modifier == 6) {
if (width && height &&
levelPlayer->overlap(x, y + offset - height, width - F8, F8) &&
......@@ -924,57 +993,57 @@ Event* Event::step (unsigned int ticks, int msps) {
}
void Event::draw (unsigned int ticks, int change) {
/**
* Draw standard event.
*
* @param ticks Time
* @param change Time since last iteration
*/
void StandardEvent::draw (unsigned int ticks, int change) {
Anim* anim;
signed char* set;
bool drawExplosion;
unsigned char frame;
if (next) next->draw(ticks, change);
// Uncomment the following to see the area of the event
/*drawRect(FTOI(getDrawX(change) - viewX),
FTOI(getDrawY(change) - (viewY + getHeight())), FTOI(getWidth()),
/*drawRect(FTOI(getDrawX(change)),
FTOI(getDrawY(change) - getHeight()), FTOI(getWidth()),
FTOI(getHeight()), 88);*/
// Get the event properties
set = level->getEvent(gridX, gridY);
// If the event has been removed from the grid, do not show it
if (!set) return;
// Check if the event has anything to draw
if (!animType) return;
if (animType == E_NOANIM) return;
// Decide on the exact frame to draw
if ((animType == E_LFINISHANIM) || (animType == E_RFINISHANIM))
frame = (ticks + T_FINISH - level->getEventTime(gridX, gridY)) / 40;
else if (set[E_ANIMSP])
frame = ticks / (set[E_ANIMSP] * 40);
else
frame = ticks / 20;
// Decide on the frame to draw
anim = getAnim();
// Calculate new positions
fixed changeX = getDrawX(change);
fixed changeY = getDrawY(change);
if ((animType & ~1) == E_LFINISHANIM) {
anim = getAnim(animType);
frame = (ticks + (anim->getLength() * set->animSpeed << 3) - level->getEventTime(gridX, gridY)) / (set->animSpeed << 3);
// Check if an explosive effect should be drawn
drawExplosion = false;
if (set[animType] < 0) {
} else if ((animType & ~1) == E_LSHOOTANIM) {
// Explosions may only occur with finish animations
drawExplosion = (animType == E_RFINISHANIM || animType == E_LFINISHANIM);
frame = (ticks + (anim->getLength() * set->animSpeed << 5) - level->getEventTime(gridX, gridY)) / (set->animSpeed << 5);
} else {
frame = (ticks / (set->animSpeed << 5)) + gridX + gridY;
}
// Decide on the frame to draw
anim->setFrame(frame + gridX + gridY, true);
anim->setFrame(frame, true);
// Calculate new positions
fixed changeX = getDrawX(change);
fixed changeY = getDrawY(change);
// Correct the position without altering the animation
......@@ -986,19 +1055,21 @@ void Event::draw (unsigned int ticks, int change) {
else if (onlyLAnimOffset && animType == E_RIGHTANIM) {
changeY += anim->getOffset();
changeY -= getAnim(E_LEFTANIM)->getOffset();
changeY -= level->getAnim(set->anims[E_LEFTANIM] & 0x7F)->getOffset();
}
else if (onlyRAnimOffset && animType == E_LEFTANIM) {
changeY += anim->getOffset();
changeY -= getAnim(E_RIGHTANIM)->getOffset();
changeY -= level->getAnim(set->anims[E_RIGHTANIM] & 0x7F)->getOffset();
}
// Draw the event
if (drawExplosion) {
// Check if an explosive effect should be drawn
if (((animType & ~1) == E_LFINISHANIM) && (set->anims[animType] & 0x80)) {
// In case of an explosion
......@@ -1032,8 +1103,7 @@ void Event::draw (unsigned int ticks, int change) {
// If the event has been destroyed, draw an explosion
if (set[E_HITSTOKILL] &&
((animType == E_LFINISHANIM) || (animType == E_RFINISHANIM))) {
if (set->strength && ((animType & ~1) == E_LFINISHANIM)) {
anim = level->getMiscAnim(2);
anim->setFrame(frame, false);
......@@ -1046,73 +1116,3 @@ void Event::draw (unsigned int ticks, int change) {
}
void Event::drawEnergy (unsigned int ticks) {
Anim* anim;
signed char* set;
int hits;
// Get the event properties
set = level->getEvent(gridX, gridY);
if (!set || set[E_MODIFIER] != 8) {
if (next) next->drawEnergy(ticks);
return;
} else if (set[E_HITSTOKILL]) {
// Draw boss energy bar
hits = level->getEventHits(gridX, gridY) * 100 / set[E_HITSTOKILL];
// Devan head
anim = level->getMiscAnim(1);
anim->setFrame(0, true);
if (ticks < flashTime) anim->flashPalette(0);
anim->draw(ITOF(viewW - 44), ITOF(hits + 48));
if (ticks < flashTime) anim->restorePalette();
// Bar
drawRect(viewW - 40, hits + 40, 12, 100 - hits, (ticks < flashTime)? 0: 32);
}
return;
}
void Event::useLeftAnimOffset() {
onlyLAnimOffset = true;
return;
}
void Event::useRightAnimOffset() {
onlyRAnimOffset = true;
return;
}
void Event::dontUseAnimOffset() {
noAnimOffset = true;
return;
}
......@@ -57,6 +57,9 @@
#include <string.h>
/**
* Base constructor for DemoLevel sub-class.
*/
Level::Level () {
// Do nothing
......@@ -66,6 +69,14 @@ Level::Level () {
}
/**
* Create a level.
*
* @param fileName Name of the file containing the level data.
* @param diff Difficulty level
* @param checkpoint Whether or not the player(s) will start at a checkpoint
* @param multi Whether or not the level will be multi-player
*/
Level::Level (char* fileName, unsigned char diff, bool checkpoint, bool multi) {
int ret;
......@@ -83,6 +94,9 @@ Level::Level (char* fileName, unsigned char diff, bool checkpoint, bool multi) {
}
/**
* Delete HUD graphical data.
*/
void Level::deletePanel () {
SDL_FreeSurface(panel);
......@@ -97,6 +111,9 @@ void Level::deletePanel () {
}
/**
* Delete the level.
*/
Level::~Level () {
int count;
......@@ -130,6 +147,14 @@ Level::~Level () {
}
/**
* Determine whether or not the given point is solid when travelling upwards.
*
* @param x X-coordinate
* @param y Y-coordinate
*
* @return Solidity
*/
bool Level::checkMaskUp (fixed x, fixed y) {
GridElement *ge;
......@@ -149,6 +174,14 @@ bool Level::checkMaskUp (fixed x, fixed y) {
}
/**
* Determine whether or not the given point is solid when travelling downwards.
*
* @param x X-coordinate
* @param y Y-coordinate
*
* @return Solidity
*/
bool Level::checkMaskDown (fixed x, fixed y) {
// Anything off the edge of the map is solid
......@@ -161,6 +194,14 @@ bool Level::checkMaskDown (fixed x, fixed y) {
}
/**
* Determine whether or not the given point should cause damage to the player.
*
* @param x X-coordinate
* @param y Y-coordinate
*
* @return Painful solidity
*/
bool Level::checkSpikes (fixed x, fixed y) {
GridElement *ge;
......@@ -180,6 +221,11 @@ bool Level::checkSpikes (fixed x, fixed y) {
}
/**
* Determine the level's world number.
*
* @return World number
*/
int Level::getWorld() {
return worldNum;
......@@ -187,6 +233,12 @@ int Level::getWorld() {
}
/**
* Set which level will come next.
*
* @param nextLevel Next level number
* @param nextWorld Next level's world number
*/
void Level::setNext (int nextLevel, int nextWorld) {
unsigned char buffer[MTL_L_PROP];
......@@ -211,6 +263,13 @@ void Level::setNext (int nextLevel, int nextWorld) {
}
/**
* Set the tile at the given location.
*
* @param gridX X-coordinate of the tile
* @param gridY Y-coordinate of the tile
* @param tile The new tile
*/
void Level::setTile (unsigned char gridX, unsigned char gridY, unsigned char tile) {
unsigned char buffer[MTL_L_GRID];
......@@ -235,6 +294,11 @@ void Level::setTile (unsigned char gridX, unsigned char gridY, unsigned char til
}
/**
* Get the active events.
*
* @return The first active event
*/
Event* Level::getEvents () {
return events;
......@@ -242,17 +306,33 @@ Event* Level::getEvents () {
}
signed char* Level::getEvent (unsigned char gridX, unsigned char gridY) {
/**
* Get the event data for the event from the given tile.
*
* @param gridX X-coordinate of the tile
* @param gridY Y-coordinate of the tile
*
* @return Event data
*/
EventType* Level::getEvent (unsigned char gridX, unsigned char gridY) {
int event = grid[gridY][gridX].event;
if (event) return eventSet[grid[gridY][gridX].event];
if (event) return eventSet + event;
return NULL;
}
/**
* Get the hits incurred by the event from the given tile.
*
* @param gridX X-coordinate of the tile
* @param gridY Y-coordinate of the tile
*
* @return Number of hits
*/
unsigned char Level::getEventHits (unsigned char gridX, unsigned char gridY) {
return grid[gridY][gridX].hits;
......@@ -260,6 +340,14 @@ unsigned char Level::getEventHits (unsigned char gridX, unsigned char gridY) {
}
/**
* Get the set time for the event from the given tile.
*
* @param gridX X-coordinate of the tile
* @param gridY Y-coordinate of the tile
*
* @return Time
*/
unsigned int Level::getEventTime (unsigned char gridX, unsigned char gridY) {
return grid[gridY][gridX].time;
......@@ -267,13 +355,19 @@ unsigned int Level::getEventTime (unsigned char gridX, unsigned char gridY) {
}
/**
* Remove the event from the given tile.
*
* @param gridX X-coordinate of the tile
* @param gridY Y-coordinate of the tile
*/
void Level::clearEvent (unsigned char gridX, unsigned char gridY) {
unsigned char buffer[MTL_L_GRID];
// Ignore if the event has been un-destroyed
if (!grid[gridY][gridX].hits &&
eventSet[grid[gridY][gridX].event][E_HITSTOKILL]) return;
eventSet[grid[gridY][gridX].event].strength) return;
grid[gridY][gridX].event = 0;
......@@ -295,6 +389,15 @@ void Level::clearEvent (unsigned char gridX, unsigned char gridY) {
}
/**
* Register a hit on the event from the given tile.
*
* @param gridX X-coordinate of the tile
* @param gridY Y-coordinate of the tile
* @param source The player that fired the bullet that hit the event
*
* @return The remaining number of hits until the event is destroyed
*/
int Level::hitEvent (unsigned char gridX, unsigned char gridY, LevelPlayer* source) {
GridElement* ge;
......@@ -303,7 +406,7 @@ int Level::hitEvent (unsigned char gridX, unsigned char gridY, LevelPlayer* sour
ge = grid[gridY] + gridX;
hitsToKill = eventSet[ge->event][E_HITSTOKILL];
hitsToKill = eventSet[ge->event].strength;
// If the event cannot be hit, return negative
if (!hitsToKill) return -1;
......@@ -339,12 +442,18 @@ int Level::hitEvent (unsigned char gridX, unsigned char gridY, LevelPlayer* sour
}
// Return the number of hits remaining until the event is destroyed
return hitsToKill - ge->hits;
}
/**
* Set the time of the event from the given tile.
*
* @param gridX X-coordinate of the tile
* @param gridY Y-coordinate of the tile
* @param time Time
*/
void Level::setEventTime (unsigned char gridX, unsigned char gridY, unsigned int time) {
grid[gridY][gridX].time = time;
......@@ -354,6 +463,13 @@ void Level::setEventTime (unsigned char gridX, unsigned char gridY, unsigned int
}
/**
* Get the bullet data for the given bullet type.
*
* @param bullet Bullet type
*
* @return Bullet data
*/
signed char* Level::getBullet (unsigned char bullet) {
return bulletSet[bullet];
......@@ -361,6 +477,13 @@ signed char* Level::getBullet (unsigned char bullet) {
}
/**
* Get a sprite.
*
* @param sprite Sprite number
*
* @return Sprite
*/
Sprite* Level::getSprite (unsigned char sprite) {
return spriteSet + sprite;
......@@ -368,6 +491,13 @@ Sprite* Level::getSprite (unsigned char sprite) {
}
/**
* Get an animation.
*
* @param anim Animation number
*
* @return Animation
*/
Anim* Level::getAnim (unsigned char anim) {
return animSet + anim;
......@@ -375,6 +505,13 @@ Anim* Level::getAnim (unsigned char anim) {
}
/**
* Get a "miscellaneous" animation.
*
* @param anim Animation number
*
* @return Animation
*/
Anim* Level::getMiscAnim (unsigned char anim) {
return animSet + miscAnims[anim];
......@@ -382,6 +519,11 @@ Anim* Level::getMiscAnim (unsigned char anim) {
}
/**
* Set the water level.
*
* @param gridY New water level y-coordinate
*/
void Level::setWaterLevel (unsigned char gridY) {
unsigned char buffer[MTL_L_PROP];
......@@ -405,6 +547,11 @@ void Level::setWaterLevel (unsigned char gridY) {
}
/**
* Determine the water level.
*
* @return The y-coordinate of the water level
*/
fixed Level::getWaterLevel () {
return waterLevel;
......@@ -412,6 +559,11 @@ fixed Level::getWaterLevel () {
}
/**
* Play a sound.
*
* @param sound Number of the sound to play
*/
void Level::playSound (int sound) {
if (sound > 0) ::playSound(soundMap[sound - 1]);
......@@ -421,6 +573,14 @@ void Level::playSound (int sound) {
}
/**
* Start a flash palette effect.
*
* @param red Red component of flash colour
* @param green Green component of flash colour
* @param blue Blue component of flash colour
* @param duration Duration of the flash effect
*/
void Level::flash (unsigned char red, unsigned char green, unsigned char blue, int duration) {
paletteEffects = new FlashPaletteEffect(red, green, blue, duration, paletteEffects);
......@@ -430,6 +590,11 @@ void Level::flash (unsigned char red, unsigned char green, unsigned char blue, i
}
/**
* Play the bonus level.
*
* @return Error code
*/
int Level::playBonus () {
Bonus *bonus;
......@@ -519,6 +684,11 @@ void Level::receive (unsigned char* buffer) {
}
/**
* Play the level.
*
* @return Error code
*/
int Level::play () {
LevelPlayer* levelPlayer;
......
......@@ -75,13 +75,35 @@ typedef struct {
} GridElement;
/// JJ1 level event type
typedef struct {
unsigned char anims[6]; ///< Indices of animations
signed char reflection; ///< Whether or not to show a reflection
signed char movement; ///< Movement type
signed char magnitude; ///< Usage depends on event type
signed char strength; ///< Number of hits required to destroy the event
signed char modifier; ///< Modifier
unsigned char points; ///< Points obtained by getting/destroying the event
unsigned char bullet; ///< Type of bullet the event fires
unsigned char bulletPeriod; ///< The time between successive bullet shots
unsigned char speed; ///< The speed at which the event moves
unsigned char animSpeed; ///< The speed of the event's animation
unsigned char sound; ///< The sound played on the appropriate trigger
signed char multiA; ///< Usage depends on event type
signed char multiB; ///< Usage depends on event type
signed char pieceSize; ///< Size of pieces in bridges, swinging balls chains, etc.
signed char pieces; ///< Number of pieces in bridges, swinging ball chains, etc.
signed char angle; ///< Initial angle of swinging balls, etc.
} EventType;
/// Pre-defined JJ1 event movement path
typedef struct {
short int* x; ///< X-coordinates for each node
short int* y; ///< Y-coordinates for each node
unsigned char length; ///< Number of nodes
unsigned char node; ///< Current node
} EventPath;
......@@ -108,17 +130,17 @@ class Level : public BaseLevel {
Anim animSet[ANIMS]; ///< Animations
char miscAnims[4]; ///< Further animations
signed char bulletSet[BULLETS][BLENGTH]; ///< Bullet types
signed char eventSet[EVENTS][ELENGTH]; ///< Event types
EventType eventSet[EVENTS]; ///< Event types
char mask[240][64]; ///< Tile masks. At most 240 tiles, all with 8 * 8 masks
GridElement grid[LH][LW]; ///< Level grid. All levels are the same size
int soundMap[32]; ///< Maps event sound effect numbers to actual sound effect indices
SDL_Color skyPalette[256]; ///< Full palette for sky background
bool sky; ///< Whether or not to use sky background
unsigned char skyOrb; ///< The tile to use as the background sun/moon/etc.
int levelNum; ///<
int worldNum; ///<
int nextLevelNum; ///<
int nextWorldNum; ///<
int levelNum; ///< Number of current level
int worldNum; ///< Number of current world
int nextLevelNum; ///< Number of next level
int nextWorldNum; ///< Number of next world
unsigned char difficulty; ///< Difficulty setting (0 = easy, 1 = medium, 2 = hard, 3 = turbo)
int enemies; ///< Number of enemies to kill
fixed waterLevel; ///< Height of water
......@@ -136,6 +158,8 @@ class Level : public BaseLevel {
protected:
Font* font; ///< On-screen message font
Level ();
int load (char* fileName, unsigned char diff, bool checkpoint);
int step ();
void draw ();
......@@ -144,7 +168,6 @@ class Level : public BaseLevel {
Bullet* bullets; ///< Active bullets
EventPath path[PATHS]; ///< Pre-defined event movement paths
Level ();
Level (char* fileName, unsigned char diff, bool checkpoint, bool multi);
virtual ~Level ();
......@@ -155,7 +178,7 @@ class Level : public BaseLevel {
void setNext (int nextLevel, int nextWorld);
void setTile (unsigned char gridX, unsigned char gridY, unsigned char tile);
Event* getEvents ();
signed char* getEvent (unsigned char gridX, unsigned char gridY);
EventType* getEvent (unsigned char gridX, unsigned char gridY);
unsigned char getEventHits (unsigned char gridX, unsigned char gridY);
unsigned int getEventTime (unsigned char gridX, unsigned char gridY);
void clearEvent (unsigned char gridX, unsigned char gridY);
......
......@@ -41,6 +41,11 @@
#include "util.h"
/**
* Level iteration.
*
* @return Error code
*/
int Level::step () {
Event *event;
......@@ -75,7 +80,7 @@ int Level::step () {
// If the event wasn't found, create it
if (!event) {
switch (getEvent(x, y)[E_BEHAVIOUR]) {
switch (getEvent(x, y)->movement) {
case 28:
......@@ -97,7 +102,7 @@ int Level::step () {
default:
events = new Event(x, y);
events = new StandardEvent(x, y);
break;
......@@ -118,8 +123,6 @@ int Level::step () {
// Process active events
for (x = 0; x < PATHS; x++) path[x].node = (ticks >> 5) % path[x].length;
if (events) events = events->step(ticks, msps);
......@@ -179,6 +182,9 @@ int Level::step () {
/**
* Draw the level.
*/
void Level::draw () {
GridElement *ge;
......@@ -260,7 +266,7 @@ void Level::draw () {
// If this is not a foreground tile, draw it
if ((eventSet[ge->event][E_BEHAVIOUR] != 38) &&
if ((eventSet[ge->event].movement != 38) &&
((ge->event < 124) || (ge->event > 125)) ) {
dst.x = TTOI(x) - (vX & 31);
......@@ -306,15 +312,15 @@ void Level::draw () {
dst.x = TTOI(x) - (vX & 31);
dst.y = TTOI(y) - (vY & 31);
if (ticks & 64) src.y = TTOI(eventSet[ge->event][E_YAXIS]);
else src.y = TTOI(eventSet[ge->event][E_MULTIPURPOSE]);
if (ticks & 64) src.y = TTOI(eventSet[ge->event].multiB);
else src.y = TTOI(eventSet[ge->event].multiA);
SDL_BlitSurface(tileSet, &src, canvas, &dst);
}
// If this is a foreground tile, draw it
if ((ge->event == 124) || (ge->event == 125) ||
(eventSet[ge->event][E_BEHAVIOUR] == 38) ) {
(eventSet[ge->event].movement == 38) ) {
dst.x = TTOI(x) - (vX & 31);
dst.y = TTOI(y) - (vY & 31);
......
......@@ -50,6 +50,11 @@
#define SKEY 254 /* Sprite colour key */
/**
* Load the HUD graphical data.
*
* @return Error code
*/
int Level::loadPanel () {
File* file;
......@@ -139,6 +144,12 @@ int Level::loadPanel () {
}
/**
* Load a sprite.
*
* @param file File from which to load the sprite data
* @param sprite Sprite that will receive the loaded data
*/
void Level::loadSprite (File* file, Sprite* sprite) {
unsigned char* pixels;
......@@ -194,6 +205,13 @@ void Level::loadSprite (File* file, Sprite* sprite) {
}
/**
* Load sprites.
*
* @param fileName Name of the file containing the level-specific sprites
*
* @return Error code
*/
int Level::loadSprites (char * fileName) {
File* mainFile = NULL;
......@@ -318,6 +336,13 @@ int Level::loadSprites (char * fileName) {
}
/**
* Load the tileset.
*
* @param fileName Name of the file containing the tileset
*
* @return The number of tiles loaded
*/
int Level::loadTiles (char* fileName) {
File* file;
......@@ -409,13 +434,22 @@ int Level::loadTiles (char* fileName) {
}
/**
* Load the level.
*
* @param fileName Name of the file containing the level data
* @param diff Difficulty level
* @param checkpoint Whether or not the player(s) will start at a checkpoint
*
* @return Error code
*/
int Level::load (char* fileName, unsigned char diff, bool checkpoint) {
Anim* pAnims[PANIMS];
File* file;
unsigned char* buffer;
const char* ext;
char* string;
char* string = NULL;
int tiles;
int count, x, y, type;
unsigned char startX, startY;
......@@ -705,7 +739,6 @@ int Level::load (char* fileName, unsigned char diff, bool checkpoint) {
for (type = 0; type < PATHS; type++) {
path[type].length = buffer[type << 9] + (buffer[(type << 9) + 1] << 8);
path[type].node = 0;
if (path[type].length < 1) path[type].length = 1;
path[type].x = new short int[path[type].length];
path[type].y = new short int[path[type].length];
......@@ -729,14 +762,31 @@ int Level::load (char* fileName, unsigned char diff, bool checkpoint) {
// Fill event set with data
for (count = 0; count < EVENTS; count++) {
memcpy(eventSet[count], buffer + (count * ELENGTH), ELENGTH);
eventSet[count][E_MOVEMENTSP]++;
eventSet[count].anims[0] = buffer[(count * ELENGTH) + 5];
eventSet[count].anims[1] = buffer[(count * ELENGTH) + 6];
eventSet[count].anims[2] = buffer[(count * ELENGTH) + 28];
eventSet[count].anims[3] = buffer[(count * ELENGTH) + 29];
eventSet[count].anims[4] = buffer[(count * ELENGTH) + 30];
eventSet[count].anims[5] = buffer[(count * ELENGTH) + 31];
eventSet[count].reflection = buffer[(count * ELENGTH) + 2];
eventSet[count].movement = buffer[(count * ELENGTH) + 4];
eventSet[count].magnitude = buffer[(count * ELENGTH) + 8];
eventSet[count].strength = buffer[(count * ELENGTH) + 9];
eventSet[count].modifier = buffer[(count * ELENGTH) + 10];
eventSet[count].points = buffer[(count * ELENGTH) + 11];
eventSet[count].bullet = buffer[(count * ELENGTH) + 12];
eventSet[count].bulletPeriod = buffer[(count * ELENGTH) + 13];
eventSet[count].speed = buffer[(count * ELENGTH) + 15] + 1;
eventSet[count].animSpeed = buffer[(count * ELENGTH) + 17] + 1;
eventSet[count].sound = buffer[(count * ELENGTH) + 21];
eventSet[count].multiA = buffer[(count * ELENGTH) + 22];
eventSet[count].multiB = buffer[(count * ELENGTH) + 23];
eventSet[count].pieceSize = buffer[(count * ELENGTH) + 24];
eventSet[count].pieces = buffer[(count * ELENGTH) + 25];
eventSet[count].angle = buffer[(count * ELENGTH) + 26];
}
delete[] buffer;
// Process grid
enemies = items = 0;
......@@ -750,12 +800,12 @@ int Level::load (char* fileName, unsigned char diff, bool checkpoint) {
if (type) {
// Eliminate event references for events of too high a difficulty
if (eventSet[type][E_DIFFICULTY] > difficulty) grid[y][x].event = 0;
if (buffer[type * ELENGTH] > difficulty) grid[y][x].event = 0;
// If the event hurts and can be killed, it is an enemy
// Anything else that scores is an item
if ((eventSet[type][E_MODIFIER] == 0) && eventSet[type][E_HITSTOKILL]) enemies++;
else if (eventSet[type][E_ADDEDSCORE]) items++;
if ((eventSet[type].modifier == 0) && eventSet[type].strength) enemies++;
else if (eventSet[type].points) items++;
}
......@@ -763,6 +813,8 @@ int Level::load (char* fileName, unsigned char diff, bool checkpoint) {
}
delete[] buffer;
// Yet more doubtless essential data
file->skipRLE();
......
......@@ -38,6 +38,15 @@
#include <string.h>
/**
* Create a JJ1 level player.
*
* @param parent The game player corresponding to this level player.
* @param newAnims Animations
* @param startX Starting position x-coordinate
* @param startY Starting position y-coordinate
* @param hasBird Whether or not the player is being accompanied by a bird
*/
LevelPlayer::LevelPlayer (Player* parent, Anim** newAnims, unsigned char startX, unsigned char startY, bool hasBird) {
int offsets[15] = {PCO_GREY, PCO_SGREEN, PCO_BLUE, PCO_RED, PCO_LGREEN,
......@@ -111,6 +120,9 @@ LevelPlayer::LevelPlayer (Player* parent, Anim** newAnims, unsigned char startX,
}
/**
* Delete the JJ1 level player.
*/
LevelPlayer::~LevelPlayer () {
if (bird) delete bird;
......@@ -120,6 +132,12 @@ LevelPlayer::~LevelPlayer () {
}
/**
* Reset the player's position, energy etc.
*
* @param startX New x-coordinate
* @param startY New y-coordinate
*/
void LevelPlayer::reset (unsigned char startX, unsigned char startY) {
event = LPE_NONE;
......@@ -143,6 +161,9 @@ void LevelPlayer::reset (unsigned char startX, unsigned char startY) {
}
/**
* Add to the player's item tally.
*/
void LevelPlayer::addItem () {
items++;
......@@ -152,6 +173,12 @@ void LevelPlayer::addItem () {
}
/**
* If the player is tied to the event from the given tile, untie it.
*
* @param gridX X-coordinate of the tile
* @param gridY Y-coordinate of the tile
*/
void LevelPlayer::clearEvent (unsigned char gridX, unsigned char gridY) {
// If the location matches, clear the event
......@@ -163,6 +190,11 @@ void LevelPlayer::clearEvent (unsigned char gridX, unsigned char gridY) {
}
/**
* Determine the player's current animation.
*
* @return The current animation
*/
Anim* LevelPlayer::getAnim () {
return anims[animType];
......@@ -170,6 +202,11 @@ Anim* LevelPlayer::getAnim () {
}
/**
* Determine the number of enemies the player has killed.
*
* @return Number of enemies killed
*/
int LevelPlayer::getEnemies () {
return enemies;
......@@ -177,6 +214,11 @@ int LevelPlayer::getEnemies () {
}
/**
* Determine the player's current energy level.
*
* @return Energy level
*/
int LevelPlayer::getEnergy () {
return energy;
......@@ -184,6 +226,11 @@ int LevelPlayer::getEnergy () {
}
/**
* Determine the direction the player is facing.
*
* @return True if the player is facing right
*/
bool LevelPlayer::getFacing () {
return facing;
......@@ -191,6 +238,11 @@ bool LevelPlayer::getFacing () {
}
/**
* Determine the number of items the player has collected.
*
* @return Number of items collected
*/
int LevelPlayer::getItems () {
return items;
......@@ -198,6 +250,11 @@ int LevelPlayer::getItems () {
}
/**
* Determine whether or not the player is being accompanied by a bird.
*
* @return Whether or not the player is being accompanied by a bird
*/
bool LevelPlayer::hasBird () {
return bird;
......@@ -205,6 +262,11 @@ bool LevelPlayer::hasBird () {
}
/**
* Determine whether or not the player has collected a gem.
*
* @return Whether or not the player has collected a gem
*/
bool LevelPlayer::hasGem () {
return gem;
......@@ -212,6 +274,14 @@ bool LevelPlayer::hasGem () {
}
/**
* Deal with bullet collisions.
*
* @param source Player that fired the bullet (NULL if an event)
* @param ticks Time
*
* @return Whether or not the hit was successful
*/
bool LevelPlayer::hit (Player *source, unsigned int ticks) {
// Invulnerable if reacting to e.g. having been hit
......@@ -261,6 +331,12 @@ bool LevelPlayer::hit (Player *source, unsigned int ticks) {
}
/**
* Kill the player.
*
* @param source Player responsible for the kill (NULL if due to an event or time)
* @param ticks time
*/
void LevelPlayer::kill (Player *source, unsigned int ticks) {
if (reaction != PR_NONE) return;
......@@ -282,6 +358,16 @@ void LevelPlayer::kill (Player *source, unsigned int ticks) {
}
/**
* Determine whether or not the player is overlapping the given area.
*
* @param left The x-coordinate of the left of the area
* @param top The y-coordinate of the top of the area
* @param width The width of the area
* @param height The height of the area
*
* @return Whether or not there is an overlap
*/
bool LevelPlayer::overlap (fixed left, fixed top, fixed width, fixed height) {
return (x + PXO_R >= left) && (x + PXO_L < left + width) &&
......@@ -290,6 +376,13 @@ bool LevelPlayer::overlap (fixed left, fixed top, fixed width, fixed height) {
}
/**
* Handle the player's reaction.
*
* @param ticks Time
*
* @return The reaction the player has just finished
*/
PlayerReaction LevelPlayer::reacted (unsigned int ticks) {
PlayerReaction oldReaction;
......@@ -308,20 +401,26 @@ PlayerReaction LevelPlayer::reacted (unsigned int ticks) {
}
/**
* Tie the player to the event from the given tile.
*
* @param gridX X-coordinate of the tile
* @param gridY Y-coordinate of the tile
*/
void LevelPlayer::setEvent (unsigned char gridX, unsigned char gridY) {
signed char *set;
EventType *set;
set = level->getEvent(gridX, gridY);
if (set[E_MODIFIER] == 29) {
if (set->modifier == 29) {
// Upwards spring
jumpY = y + (set[E_MAGNITUDE] * (F20 + F1));
jumpY = y + (set->magnitude * (F20 + F1));
event = LPE_SPRING;
} else if (set[E_MODIFIER] == 6) event = LPE_PLATFORM;
else if (set[E_BEHAVIOUR] == 28) event = LPE_PLATFORM;
} else if (set->modifier == 6) event = LPE_PLATFORM;
else if (set->movement == 28) event = LPE_PLATFORM;
else return;
eventX = gridX;
......@@ -332,6 +431,12 @@ void LevelPlayer::setEvent (unsigned char gridX, unsigned char gridY) {
}
/**
* Set the player's position.
*
* @param newX New x-coordinate
* @param newY New y-coordinate
*/
void LevelPlayer::setPosition (fixed newX, fixed newY) {
x = newX;
......@@ -342,6 +447,12 @@ void LevelPlayer::setPosition (fixed newX, fixed newY) {
}
/**
* Set the player's speed.
*
* @param newDx New x-speed
* @param newDy New y-speed
*/
void LevelPlayer::setSpeed (fixed newDx, fixed newDy) {
dx = newDx;
......@@ -352,17 +463,26 @@ void LevelPlayer::setSpeed (fixed newDx, fixed newDy) {
}
/**
* Take the event from the given tile.
*
* @param gridX X-coordinate of the tile
* @param gridY Y-coordinate of the tile
* @param ticks Time
*
* @return Whether or not the event should be destroyed.
*/
bool LevelPlayer::takeEvent (unsigned char gridX, unsigned char gridY, unsigned int ticks) {
signed char *set;
EventType* set;
set = level->getEvent(gridX, gridY);
switch (set[E_MODIFIER]) {
switch (set->modifier) {
case 41: // Bonus level
if (energy) level->setNext(set[E_MULTIPURPOSE], set[E_YAXIS]);
if (energy) level->setNext(set->multiA, set->multiB);
// The lack of a break statement is intentional
......@@ -526,31 +646,41 @@ bool LevelPlayer::takeEvent (unsigned char gridX, unsigned char gridY, unsigned
}
player->addScore(set[E_ADDEDSCORE] * 10);
player->addScore(set->points * 10);
// Add to player's enemy/item tally
// If the event hurts and can be killed, it is an enemy
// Anything else that scores is an item
if ((set[E_MODIFIER] == 0) && set[E_HITSTOKILL]) enemies++;
else if (set[E_ADDEDSCORE]) items++;
if ((set->modifier == 0) && set->strength) enemies++;
else if (set->points) items++;
return true;
}
/**
* Called when the player has touched the event from the given tile.
*
* @param gridX X-coordinate of the tile
* @param gridY Y-coordinate of the tile
* @param ticks Time
* @param msps Ticks per step
*
* @return Whether or not the event should be destroyed.
*/
bool LevelPlayer::touchEvent (unsigned char gridX, unsigned char gridY, unsigned int ticks, int msps) {
signed char *set;
EventType* set;
set = level->getEvent(gridX, gridY);
switch (set[E_MODIFIER]) {
switch (set->modifier) {
case 0: // Hurt
case 8: // Boss
if ((set[E_BEHAVIOUR] < 37) || (set[E_BEHAVIOUR] > 44)) hit(NULL, ticks);
if ((set->movement < 37) || (set->movement > 44)) hit(NULL, ticks);
break;
......@@ -562,8 +692,8 @@ bool LevelPlayer::touchEvent (unsigned char gridX, unsigned char gridY, unsigned
if (!warpTime) {
warpX = set[E_MULTIPURPOSE];
warpY = set[E_YAXIS];
warpX = set->multiA;
warpY = set->multiB;
warpTime = ticks + T_WARP;
// White flash
......@@ -575,7 +705,7 @@ bool LevelPlayer::touchEvent (unsigned char gridX, unsigned char gridY, unsigned
case 28: // Belt
x += set[E_MAGNITUDE] * 4 * msps;
x += set->magnitude * 4 * msps;
break;
......@@ -583,7 +713,7 @@ bool LevelPlayer::touchEvent (unsigned char gridX, unsigned char gridY, unsigned
setEvent(gridX, gridY);
level->playSound(set[E_SOUND]);
level->playSound(set->sound);
break;
......@@ -595,26 +725,26 @@ bool LevelPlayer::touchEvent (unsigned char gridX, unsigned char gridY, unsigned
case 32: // Float up / sideways
if (set[E_YAXIS]) {
if (set->multiB) {
eventX = gridX;
eventY = gridY;
event = LPE_FLOAT;
if (dy > set[E_MULTIPURPOSE] * -F20)
dy -= set[E_MULTIPURPOSE] * 320 * msps;
if (dy > set->multiA * -F20)
dy -= set->multiA * 320 * msps;
jumpY = y - (8 * F16);
} else if (set[E_MAGNITUDE] < 0) {
} else if (set->magnitude < 0) {
if (!level->checkMaskDown(x + PXO_L + (set[E_MAGNITUDE] * 20 * msps), y + PYO_MID))
x += set[E_MAGNITUDE] * 20 * msps;
if (!level->checkMaskDown(x + PXO_L + (set->magnitude * 20 * msps), y + PYO_MID))
x += set->magnitude * 20 * msps;
} else {
if (!level->checkMaskDown(x + PXO_R + (set[E_MAGNITUDE] * 20 * msps), y + PYO_MID))
x += set[E_MAGNITUDE] * 20 * msps;
if (!level->checkMaskDown(x + PXO_R + (set->magnitude * 20 * msps), y + PYO_MID))
x += set->magnitude * 20 * msps;
}
......@@ -628,7 +758,7 @@ bool LevelPlayer::touchEvent (unsigned char gridX, unsigned char gridY, unsigned
default:
if (!set[E_HITSTOKILL]) return takeEvent(gridX, gridY, ticks);
if (!set->strength) return takeEvent(gridX, gridY, ticks);
break;
......@@ -639,6 +769,11 @@ bool LevelPlayer::touchEvent (unsigned char gridX, unsigned char gridY, unsigned
}
/**
* Fill a buffer with player data.
*
* @param buffer The buffer
*/
void LevelPlayer::send (unsigned char *buffer) {
// Copy data to be sent to clients/server
......@@ -670,6 +805,11 @@ void LevelPlayer::send (unsigned char *buffer) {
}
/**
* Adjust player data based on the contents of a given buffer.
*
* @param buffer The buffer
*/
void LevelPlayer::receive (unsigned char *buffer) {
int count;
......
......@@ -43,7 +43,10 @@
/**
* Respond to controls, unless the player has been killed
* Respond to controls, unless the player has been killed.
*
* @param ticks Time
* @param msps Ticks per step
*/
void LevelPlayer::control (unsigned int ticks, int msps) {
......@@ -159,7 +162,7 @@ void LevelPlayer::control (unsigned int ticks, int msps) {
if (event != LPE_NONE) {
if (event == LPE_SPRING) dy = level->getEvent(eventX, eventY)[E_MULTIPURPOSE] * -F20;
if (event == LPE_SPRING) dy = level->getEvent(eventX, eventY)->multiA * -F20;
else if (event == LPE_FLOAT) dy = PYS_JUMP;
}
......@@ -241,7 +244,7 @@ void LevelPlayer::control (unsigned int ticks, int msps) {
// Spring/float up speed limit
if ((event == LPE_SPRING) || (event == LPE_FLOAT)) {
speed = level->getEvent(eventX, eventY)[E_MULTIPURPOSE] * -F20;
speed = level->getEvent(eventX, eventY)->multiA * -F20;
if (speed >= 0) speed = PYS_JUMP;
......@@ -413,6 +416,12 @@ void LevelPlayer::control (unsigned int ticks, int msps) {
}
/**
* Move the player.
*
* @param ticks Time
* @param msps Ticks per step
*/
void LevelPlayer::move (unsigned int ticks, int msps) {
fixed pdx, pdy;
......@@ -621,7 +630,10 @@ void LevelPlayer::move (unsigned int ticks, int msps) {
/**
* Calculate viewport
* Calculate viewport.
*
* @param ticks Time
* @param mspf Ticks per frame
*/
void LevelPlayer::view (unsigned int ticks, int mspf) {
......@@ -670,6 +682,12 @@ void LevelPlayer::view (unsigned int ticks, int mspf) {
}
/**
* Draw the player.
*
* @param ticks Time
* @param change Time since last step
*/
void LevelPlayer::draw (unsigned int ticks, int change) {
Anim *an;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment