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);
};
......
......@@ -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();
......
This diff is collapsed.
......@@ -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