Commit 53646b83 authored by alistert's avatar alistert

Added existing OpenJazz code.

parents
/*
*
* OpenJazz.h
*
* 23rd August 2005: Created OpenJazz.h
* 31st January 2006: Created level.h from parts of OpenJazz.h
* 31st January 2006: Created player.h from parts of OpenJazz.h
* 3rd February 2009: Created menu.h from parts of OpenJazz.h
* 3rd February 2009: Created file.h from parts of OpenJazz.h
* 3rd February 2009: Created font.h from parts of OpenJazz.h
* 4th February 2009: Created palette.h from parts of OpenJazz.h
* 2nd March 2009: Created network.h from parts of OpenJazz.h
* 2nd June 2009: Created sound.h from parts of OpenJazz.h
* 3rd June 2009: Created network.h from parts of OpenJazz.h
* 13th July 2009: Created controls.h from parts of OpenJazz.h
* 13th July 2009: Created graphics.h from parts of OpenJazz.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _OPENJAZZ_H
#define _OPENJAZZ_H
#ifndef EXTERN
#define EXTERN extern
#endif
// Constants
// Numbers in -10 exponent fixed point
#define FE 128
#define FQ 256
#define FH 512
#define F1 1024
#define F2 2048
#define F4 4096
#define F8 8192
#define F10 10240
#define F12 12288
#define F16 16384
#define F20 20480
#define F24 24576
#define F32 32768
#define F36 36864
#define F40 40960
#define F80 81920
#define F64 65536
#define F100 102400
#define F160 163840
// File names
#define CONFIG_FILE "openjazz.cfg"
#define LOGO_FILE "openjazz.000"
#define LEVEL_FILE "openjazz.tmp"
#ifdef UPPERCASE_FILENAMES
#define F_MAINCHAR "MAINCHAR.000"
#define F_MENU "MENU.000"
#define F_PANEL "PANEL.000"
// File name formats
#define F_BLOCKS "BLOCKS"
#define F_LEVEL "LEVEL"
#define F_PLANET "PLANET"
#define F_SPRITES "SPRITES"
#else
#define F_MAINCHAR "mainchar.000"
#define F_MENU "menu.000"
#define F_PANEL "panel.000"
// File name formats
#define F_BLOCKS "blocks"
#define F_LEVEL "level"
#define F_PLANET "planet"
#define F_SPRITES "sprites"
#endif
// Standard string length
#define STRING_LENGTH 32
// Loop return type
#define NORMAL_LOOP 0
#define KEY_LOOP 1
#define JOYSTICK_LOOP 2
// Return values
#define E_DATA -14
#define E_VERSION -13
#define E_TIMEOUT -12
#define E_N_OTHER -11
#define E_N_CONNECT -10
#define E_N_ADDRESS -9
#define E_N_LISTEN -8
#define E_N_BIND -7
#define E_N_SOCKET -6
#define E_DEMOTYPE -5
#define E_FILE -4
#define E_VIDEO -3
#define E_UNUSED -2
#define E_QUIT -1
#define E_NONE 0
#define WON 1
#define LOST 2
#define JOYSTICKB 0x100
#define JOYSTICKANEG 0x200
#define JOYSTICKAPOS 0x300
// Time interval
#define T_FRAME 20
// Macros
// For fixed-point operations
#define FTOI(x) ((x) >> 10)
#define ITOF(x) ((x) << 10)
#define MUL(x, y) (((x) * (y)) >> 10)
#define DIV(x, y) (((x) << 10) / (y))
// Datatype
typedef int fixed;
// Variable
// Time
EXTERN unsigned int globalTicks;
// Functions in main.cpp
EXTERN int loop (int type);
// Functions in util.cpp
EXTERN bool fileExists (char *fileName);
EXTERN char * createString (char *string);
EXTERN char * createString (char *first, char *second);
EXTERN char * createFileName (char *type, int extension);
EXTERN char * createFileName (char *type, char *extension);
EXTERN char * createFileName (char *type, int level, int extension);
EXTERN char * createEditableString (char *string);
EXTERN void log (char *message);
EXTERN void log (char *message, char *detail);
EXTERN void log (char *message, int number);
EXTERN void logError (char *message, char *detail);
#endif
/*
*
* bonus.cpp
*
* 23rd August 2005: Created bonus.c
* 3rd February 2009: Renamed bonus.c to bonus.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Deals with the loading, running and freeing of bonus levels.
*
*/
#include "bonus.h"
#include "io/controls.h"
#include "io/file.h"
Bonus::Bonus (char * fileName) {
File *file;
try {
file = new File(fileName, false);
} catch (int e) {
throw e;
}
// TODO: Load bonus level data
delete file;
return;
}
Bonus::~Bonus () {
// Nothing to do
return;
}
int Bonus::play () {
while (true) {
if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT;
if (controls.release(C_ESCAPE)) return E_NONE;
// TODO: Bonus levels
}
return E_NONE;
}
/*
*
* bonus.h
*
* 3rd February 2009: Created bonus.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _BONUS_H
#define _BONUS_H
// Class
class Bonus {
public:
Bonus (char * fileName);
~Bonus ();
int play ();
};
#endif
/*
*
* clientgame.cpp
*
* 18th July 2009: Created clientgame.cpp from parts of game.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "game.h"
#include "gamemode.h"
#include "io/controls.h"
#include "io/file.h"
#include "io/gfx/font.h"
#include "io/gfx/paletteeffects.h"
#include "io/gfx/video.h"
#include "io/network.h"
#include "level/level.h"
#include "menu/menu.h"
#include "player/player.h"
#include <string.h>
ClientGame::ClientGame (char *address) {
unsigned char buffer[BUFFER_LENGTH];
unsigned int timeout;
int count, ret, mode;
sock = net->join(address);
if (sock < 0) throw sock; // Tee hee hee hee hee.
// Receive initialisation message
count = 0;
timeout = globalTicks + T_SCHECK + T_TIMEOUT;
// Wait for whole message to arrive
while (count < MTL_G_PROPS) {
if (loop(NORMAL_LOOP) == E_QUIT) {
net->close(sock);
throw E_QUIT;
}
if (controls.release(C_ESCAPE)) {
net->close(sock);
throw E_UNUSED;
}
SDL_Delay(T_FRAME);
clearScreen(0);
fontmn2->showString("WAITING FOR REPLY", screenW >> 2,
(screenH >> 1) - 16);
ret = net->recv(sock, buffer + count, MTL_G_PROPS - count);
if (ret > 0) count += ret;
if (globalTicks > timeout) {
net->close(sock);
throw E_TIMEOUT;
}
}
// Make sure message is valid
if (buffer[1] != MT_G_PROPS) {
net->close(sock);
throw E_DATA;
} else if (buffer[2] != 1) {
net->close(sock);
throw E_VERSION;
}
printf("Connected to server (version %d).\n", buffer[2]);
// Copy game parameters
mode = buffer[3];
difficulty = buffer[4];
maxPlayers = buffer[5];
nPlayers = buffer[6];
clientID = buffer[7];
printf("Game mode %d, difficulty %d, %d of %d players.\n", mode,
difficulty, nPlayers, maxPlayers);
if (nPlayers > maxPlayers) {
net->close(sock);
throw E_DATA;
}
gameMode = createGameMode(mode);
if (gameMode == NULL) {
net->close(sock);
throw E_DATA;
}
// Create players
nPlayers = 0;
players = new Player[maxPlayers];
// Download the level from the server
level = NULL;
levelFile = createString(LEVEL_FILE);
file = NULL;
ret = setLevel(NULL);
if (ret < 0) {
net->close(sock);
if (file) delete file;
delete gameMode;
throw ret;
}
// Add a new player to the game
buffer[0] = MTL_G_PJOIN + strlen(characterName);
buffer[1] = MT_G_PJOIN;
buffer[2] = clientID;
buffer[3] = 0; // Player's number, assigned by the server
buffer[4] = 0; // Player's team, assigned by the server
memcpy(buffer + 5, characterCols, 4);
memcpy(buffer + 9, characterName, strlen(characterName) + 1);
send(buffer);
// Wait for acknowledgement
localPlayer = NULL;
while (!localPlayer) {
if (loop(NORMAL_LOOP) == E_QUIT) {
net->close(sock);
if (file) delete file;
delete gameMode;
throw E_QUIT;
}
if (controls.release(C_ESCAPE)) {
net->close(sock);
if (file) delete file;
delete gameMode;
throw E_UNUSED;
}
clearScreen(0);
fontmn2->showString("JOINING GAME", screenW >> 2, (screenH >> 1) - 16);
ret = playFrame(-1);
if (ret < 0) {
net->close(sock);
if (file) delete file;
delete gameMode;
throw ret;
}
}
return;
}
ClientGame::~ClientGame () {
net->close(sock);
if (file) delete file;
delete gameMode;
return;
}
int ClientGame::setLevel (char *fileName) {
int ret;
// Free the palette effects
if (firstPE) {
delete firstPE;
firstPE = NULL;
}
usePalette(menu->palettes[1]);
// Wait for level data to start arriving
while (!file && levelFile) {
if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT;
if (controls.release(C_ESCAPE)) return E_UNUSED;
SDL_Delay(T_FRAME);
clearScreen(0);
fontmn2->showString("WAITING FOR SERVER", screenW >> 2,
(screenH >> 1) - 16);
ret = playFrame(-1);
if (ret < 0) return ret;
}
// Wait for level data to finish arriving
while (file && levelFile) {
if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT;
if (controls.release(C_ESCAPE)) return E_UNUSED;
SDL_Delay(T_FRAME);
clearScreen(0);
fontmn2->showString("downloaded", screenW >> 2, (screenH >> 1) - 16);
fontmn2->showNumber(file->tell(), (screenW >> 2) + 56, screenH >> 1);
fontmn2->showString("bytes", (screenW >> 2) + 64, screenH >> 1);
ret = playFrame(-1);
if (ret < 0) return ret;
}
return E_NONE;
}
void ClientGame::send (unsigned char *buffer) {
net->send(sock, buffer);
return;
}
int ClientGame::playFrame (int ticks) {
unsigned char sendBuffer[BUFFER_LENGTH];
int length, count;
bool firstMessage;
// Receive data from server
if (received == 0) {
// Not currently receiving a message
// See if there is a new message to receive
length = net->recv(sock, recvBuffer, 1);
if (length > 0) received++;
}
if (received > 0) {
// Currently receiving a message
// See if there is any more data
length = net->recv(sock, recvBuffer + received,
recvBuffer[0] - received);
if (length > 0) received += length;
// See if the whole message has arrived
if (received >= recvBuffer[0]) {
switch (recvBuffer[1] & MCMASK) {
case MC_GAME:
if (recvBuffer[1] == MT_G_LEVEL) {
if (!file) {
// Not already storing level data, so open the file
try {
file = new File(levelFile, true);
} catch (int e) {
return e;
}
firstMessage = true;
} else firstMessage = false;
file->seek((recvBuffer[2] << 8) + recvBuffer[3], true);
for (count = 4; count < recvBuffer[0]; count++)
file->storeChar(recvBuffer[count]);
// If a zero-length block has been sent, it is the last
if (recvBuffer[0] == MTL_G_LEVEL) {
if (firstMessage) {
// If the last message was also the first,
// then the run of levels has ended
delete[] levelFile;
levelFile = NULL;
}
delete file;
file = NULL;
}
break;
}
if ((recvBuffer[1] == MT_G_PJOIN) &&
(recvBuffer[3] < maxPlayers)) {
printf("Player %d joined the game.\n", recvBuffer[3]);
// Add the new player, and any that have been missed
for (count = nPlayers; count <= recvBuffer[3]; count++)
{
players[count].init((char *)recvBuffer + 9,
recvBuffer + 5, recvBuffer[4]);
players[count].reset();
printf("Player %d joined team %d.\n",
count, recvBuffer[4]);
}
nPlayers = count;
if (recvBuffer[2] == clientID)
localPlayer = players + recvBuffer[3];
}
if ((recvBuffer[1] == MT_G_PQUIT) &&
(recvBuffer[2] < nPlayers)) {
printf("Player %d left the game.\n", recvBuffer[2]);
// Remove the player
players[recvBuffer[2]].deinit();
// If necessary, move more recent players
for (count = recvBuffer[2]; count < nPlayers; count++)
memcpy(players + count, players + count + 1,
sizeof(Player));
// Clear duplicate pointers
memset(players + nPlayers, 0, sizeof(Player));
}
if (recvBuffer[1] == MT_G_CHECK) {
checkX = recvBuffer[2];
checkY = recvBuffer[3];
}
if (recvBuffer[1] == MT_G_SCORE) {
for (count = 0; count < nPlayers; count++) {
if (players[count].getTeam() == recvBuffer[2])
players[count].teamScore++;
}
}
break;
case MC_LEVEL:
if (level) level->receive(recvBuffer);
break;
case MC_PLAYER:
if (recvBuffer[2] < maxPlayers)
players[recvBuffer[2]].receive(recvBuffer);
break;
}
received = 0;
}
}
if (ticks >= checkTime) {
// Check for disconnection
if (!(net->isConnected(sock))) {
if (file) delete file;
file = NULL;
return E_N_OTHER;
}
checkTime = ticks + T_CCHECK;
}
if (localPlayer && (ticks >= sendTime)) {
// Update server
sendBuffer[0] = MTL_P_TEMP;
sendBuffer[1] = MT_P_TEMP;
sendBuffer[2] = 0;
localPlayer->send(sendBuffer);
send(sendBuffer);
sendTime = ticks + T_CSEND;
}
return E_NONE;
}
void ClientGame::score (unsigned char team) {
unsigned char buffer[MTL_G_SCORE];
// Inform server
buffer[0] = MTL_G_SCORE;
buffer[1] = MT_G_SCORE;
buffer[2] = team;
send(buffer);
return;
}
void ClientGame::setCheckpoint (unsigned char gridX, unsigned char gridY) {
unsigned char buffer[MTL_G_CHECK];
if (gameMode) {
buffer[0] = MTL_G_CHECK;
buffer[1] = MT_G_CHECK;
buffer[2] = gridX;
buffer[3] = gridY;
send(buffer);
}
return;
}
/*
*
* game.cpp
*
* 9th March 2009: Created game.cpp from parts of menu.cpp and level.cpp
* 3rd June 2009: Created network.cpp from parts of game.cpp
* 18th July 2009: Created servergame.cpp from parts of game.cpp
* 18th July 2009: Created clientgame.cpp from parts of game.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "game.h"
#include "gamemode.h"
#include "io/controls.h"
#include "io/gfx/font.h"
#include "io/gfx/video.h"
#include "io/sound.h"
#include "level/level.h"
#include "player/player.h"
#include "scene.h"
Game::Game () {
levelFile = NULL;
players = NULL;
return;
}
Game::Game (char *firstLevel, int gameDifficulty) {
levelFile = createString(firstLevel);
difficulty = gameDifficulty;
gameMode = NULL;
// Create the player
nPlayers = 1;
localPlayer = players = new Player[1];
localPlayer->init(characterName, NULL, 0);
return;
}
Game::~Game () {
if (levelFile) delete[] levelFile;
if (players != NULL) delete[] players;
localPlayer = NULL;
return;
}
int Game::setLevel (char *fileName) {
if (levelFile) delete[] levelFile;
if (!fileName) levelFile = NULL;
else levelFile = createString(fileName);
return E_NONE;
}
int Game::play () {
Scene * scene;
bool checkpoint;
int ret;
checkpoint = false;
// Play the level(s)
while (true) {
sendTime = checkTime = 0;
// Load the level
try {
level = new Level(levelFile, difficulty, checkpoint);
} catch (int e) {
return e;
}
ret = level->play();
switch (ret) {
case E_NONE: // Quit game
delete level;
playMusic("menusng.psm");
return E_NONE;
case WON: // Completed level
// If there is no next level, load and play the cutscene
if (!levelFile) {
scene = level->createScene();
delete level;
scene->play();
delete scene;
return E_NONE;
}
// Do not use old level's checkpoint coordinates
checkpoint = false;
break;
case LOST: // Lost level
if (!localPlayer->getLives()) {
delete level;
return E_NONE; // Not really a success...
}
// Use checkpoint coordinates
checkpoint = true;
break;
default: // Error
delete level;
return ret;
}
// Unload the previous level
delete level;
}
return E_NONE;
}
void Game::view () {
// Move the viewport towards the exit sign
if (TTOF(checkX) > viewX + (viewW << 9) + (160 * mspf)) viewX += 160 * mspf;
else if (TTOF(checkX) < viewX + (viewW << 9) - (160 * mspf))
viewX -= 160 * mspf;
if (TTOF(checkY) > viewY + (viewH << 9) + (160 * mspf)) viewY += 160 * mspf;
else if (TTOF(checkY) < viewY + (viewH << 9) - (160 * mspf))
viewY -= 160 * mspf;
return;
}
void Game::send (unsigned char *buffer) {
// Do nothing
return;
}
int Game::playFrame (int ticks) {
// Do nothing
return E_NONE;
}
void Game::score (unsigned char team) {
// Do nothing
return;
}
void Game::setCheckpoint (unsigned char gridX, unsigned char gridY) {
checkX = gridX;
checkY = gridY;
return;
}
void Game::resetPlayer (Player *player) {
player->reset();
player->setPosition(TTOF(checkX), TTOF(checkY));
return;
}
/*
*
* game.h
*
* 2nd March 2009: Created network.h from parts of OpenJazz.h
* 9th February 2009: Renamed network.h to game.h
* 2nd August 2009: Created gamemode.h from parts of game.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _GAME_H
#define _GAME_H
#include "io/network.h"
// Constants
// Time intervals
#define T_SSEND 20
#define T_SCHECK 1000
#define T_CSEND 10
#define T_CCHECK 1000
// Message categories and types
#define MCMASK 0xF0
#define MC_GAME 0x00
#define MC_LEVEL 0x10
#define MC_PLAYER 0x20
#define MT_G_PROPS 0x00 /* Game properties */
#define MT_G_PJOIN 0x01 /* New player joined */
#define MT_G_PQUIT 0x02 /* Player left */
#define MT_G_LEVEL 0x03 /* Level data */
#define MT_G_CHECK 0x04
#define MT_G_SCORE 0x05 /* Team scored a roast/lap/etc. */
#define MT_L_PROP 0x10 /* Level property */
#define MT_L_GRID 0x11 /* Change to gridElement */
#define MT_L_STAGE 0x12 /* Change in level stage */
#define MT_P_ANIMS 0x20 /* Player animations */
#define MT_P_TEMP 0x21 /* Temporary player properties, e.g. position */
// Minimum message lengths, including header
#define MTL_G_PROPS 8
#define MTL_G_PJOIN 10
#define MTL_G_PQUIT 3
#define MTL_G_LEVEL 4
#define MTL_G_CHECK 4
#define MTL_G_SCORE 3
#define MTL_L_PROP 5
#define MTL_L_GRID 6
#define MTL_L_STAGE 3
#define MTL_P_ANIMS (PANIMS + 3)
#define MTL_P_TEMP 45
#define BUFFER_LENGTH 255 /* Should always be big enough to hold any message */
// Classes
class File;
class Player;
class Game {
protected:
char *levelFile;
int difficulty;
int sendTime, checkTime;
unsigned char checkX, checkY;
Game ();
public:
Game (char *firstLevel, int gameDifficulty);
virtual ~Game ();
virtual int setLevel (char *fileName);
int play ();
void view ();
virtual void send (unsigned char *buffer);
virtual int playFrame (int ticks);
virtual void score (unsigned char team);
virtual void setCheckpoint (unsigned char gridX, unsigned char gridY);
void resetPlayer (Player *player);
};
class ServerGame : public Game {
private:
int clientStatus[MAX_CLIENTS]; /*
-2: Connected and operational
-1: Not connected
>=0: Number of bytes of the level that have been sent */
int clientPlayer[MAX_CLIENTS];
int clientSock[MAX_CLIENTS];
unsigned char recvBuffers[MAX_CLIENTS][BUFFER_LENGTH];
int received[MAX_CLIENTS];
unsigned char *levelData;
int levelSize;
int sock;
public:
ServerGame (int mode, char *firstLevel, int gameDifficulty);
~ServerGame ();
int setLevel (char *fileName);
void send (unsigned char *buffer);
int playFrame (int ticks);
void score (unsigned char team);
void setCheckpoint (unsigned char gridX, unsigned char gridY);
};
class ClientGame : public Game {
private:
File *file;
unsigned char recvBuffer[BUFFER_LENGTH];
int received;
int clientID;
int maxPlayers;
int sock;
public:
ClientGame (char *address);
~ClientGame ();
int setLevel (char *fileName);
void send (unsigned char *buffer);
int playFrame (int ticks);
void score (unsigned char team);
void setCheckpoint (unsigned char gridX, unsigned char gridY);
};
// Variable
EXTERN Game *game;
#endif
/*
*
* gamemode.cpp
*
* 2nd August 2009: Created gamemode.cpp from parts of servergame.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "game.h"
#include "gamemode.h"
#include "io/gfx/font.h"
#include "level/level.h"
#include "player/player.h"
bool GameMode::hit (Player *source, Player *victim) {
return true;
}
bool GameMode::kill (Player *source, Player *victim) {
if (source && (victim == localPlayer)) game->score(source->getTeam());
return true;
}
bool GameMode::endOfLevel (Player *player, unsigned char gridX,
unsigned char gridY) {
game->setCheckpoint(gridX, gridY);
level->setStage(LS_END);
return true;
}
void GameMode::outOfTime () {
return;
}
unsigned char CooperativeGameMode::chooseTeam () {
// All players are on the same team
return 0;
}
void CooperativeGameMode::drawScore () {
// Do nothing
return;
}
unsigned char FreeForAllGameMode::chooseTeam () {
// Every player is on a separate team
int count;
unsigned char team;
team = 1;
// Find a team number higher than any other
for (count = nPlayers - 1; count >= 0; count--) {
if (players[count].getTeam() > team)
team = players[count].getTeam() + 1;
}
return team;
}
void FreeForAllGameMode::drawScore () {
fontmn1->showNumber(localPlayer->teamScore, 64, 4);
return;
}
unsigned char TeamGameMode::chooseTeam () {
// Players are split between two teams
int count, difference;
// Calculate team imbalance
difference = 0;
for (count = 0; count < nPlayers; count++) {
if (players[count].getTeam()) difference++;
else difference--;
}
// Assign to the team with the least players
if (difference >= 0) return 0;
return 1;
}
void TeamGameMode::drawScore () {
fontmn1->showNumber(localPlayer->teamScore, 64, 4);
return;
}
int CoopGameMode::getMode () {
return M_COOP;
}
bool CoopGameMode::endOfLevel (Player *player, unsigned char gridX,
unsigned char gridY) {
game->setCheckpoint(gridX, gridY);
level->setStage(LS_END);
return true;
}
int BattleGameMode::getMode () {
return M_BATTLE;
}
int TeamBattleGameMode::getMode () {
return M_TEAMBATTLE;
}
int RaceGameMode::getMode () {
return M_RACE;
}
bool RaceGameMode::hit (Player *source, Player *victim) {
return false;
}
bool RaceGameMode::endOfLevel (Player *player, unsigned char gridX,
unsigned char gridY) {
if (player == localPlayer) game->score(player->getTeam());
game->resetPlayer(player);
return false;
}
GameMode * createGameMode (int mode) {
switch (mode) {
case M_COOP:
return new CoopGameMode();
case M_BATTLE:
return new BattleGameMode();
case M_TEAMBATTLE:
return new TeamBattleGameMode();
case M_RACE:
return new RaceGameMode();
}
return NULL;
}
/*
*
* gamemode.h
*
* 2nd August 2009: Created gamemode.h from parts of game.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _GAMEMODE_H
#define _GAMEMODE_H
#include "io/network.h"
// Constants
// Game modes
#define M_SINGLE 0
#define M_COOP 1
#define M_BATTLE 2
#define M_TEAMBATTLE 3
#define M_RACE 4
#define MAX_PLAYERS (MAX_CLIENTS + 1)
// Classes
class Player;
class GameMode {
public:
virtual int getMode () = 0;
virtual unsigned char chooseTeam () = 0;
virtual void drawScore () = 0;
virtual bool hit (Player *source, Player *victim);
virtual bool kill (Player *source, Player *victim);
virtual bool endOfLevel (Player *player, unsigned char gridX,
unsigned char gridY);
virtual void outOfTime ();
};
class CooperativeGameMode : public GameMode {
public:
unsigned char chooseTeam ();
virtual void drawScore ();
};
class FreeForAllGameMode : public GameMode {
public:
unsigned char chooseTeam ();
virtual void drawScore ();
};
class TeamGameMode : public GameMode {
public:
unsigned char chooseTeam ();
virtual void drawScore ();
};
class CoopGameMode : public CooperativeGameMode {
public:
int getMode ();
bool endOfLevel (Player *player, unsigned char gridX,
unsigned char gridY);
};
class BattleGameMode : public FreeForAllGameMode {
private:
int targetKills;
public:
int getMode ();
};
class TeamBattleGameMode : public TeamGameMode {
private:
int targetKills;
public:
int getMode ();
};
class RaceGameMode : public FreeForAllGameMode {
private:
int targetLaps;
public:
int getMode ();
bool hit (Player *source, Player *victim);
bool endOfLevel (Player *player, unsigned char gridX,
unsigned char gridY);
};
// Variable
EXTERN GameMode *gameMode; // NULL for single-player games
// Function
GameMode * createGameMode (int mode);
#endif
/*
*
* servergame.cpp
*
* 18th July 2009: Created servergame.cpp from parts of game.cpp
* 2nd August 2009: Created gamemode.cpp from parts of servergame.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "game.h"
#include "gamemode.h"
#include "io/controls.h"
#include "io/file.h"
#include "io/gfx/font.h"
#include "io/gfx/video.h"
#include "io/network.h"
#include "level/level.h"
#include "player/player.h"
#include <string.h>
ServerGame::ServerGame (int mode, char *firstLevel, int gameDifficulty) {
int count;
// Create the server
sock = net->host();
if (sock < 0) throw sock; // Tee hee. Throw sock.
// Create the players
nPlayers = 1;
localPlayer = players = new Player[MAX_PLAYERS];
localPlayer->init(characterName, characterCols, 0);
for (count = 0; count < MAX_CLIENTS; count++)
clientPlayer[count] = clientStatus[count] = -1;
// Copy the first level into memory
levelFile = NULL;
levelData = NULL;
count = setLevel(firstLevel);
if (count < 0) {
net->close(sock);
if (levelData) delete[] levelData;
throw count;
}
difficulty = gameDifficulty;
gameMode = createGameMode(mode);
return;
}
ServerGame::~ServerGame () {
int count;
for (count = 0; count < MAX_CLIENTS; count++) {
if (clientStatus[count] != -1) net->close(clientSock[count]);
}
net->close(sock);
if (levelData) delete[] levelData;
delete gameMode;
return;
}
int ServerGame::setLevel (char *fileName) {
File *file;
int count;
if (levelFile) delete[] levelFile;
if (levelData) delete[] levelData;
// The new level will be sent to all clients
for (count = 0; count < MAX_CLIENTS; count++) {
if (clientStatus[count] != -1) clientStatus[count] = 0;
}
if (!fileName) {
levelFile = NULL;
levelData = NULL;
return E_NONE;
}
try {
file = new File(fileName, false);
} catch (int e) {
levelFile = NULL;
levelData = NULL;
return e;
}
levelFile = createString(fileName);
// Load the entire file into memory
levelSize = file->getSize();
levelData = file->loadBlock(levelSize);
delete file;
// Modify the extension section to match the actual extension
count = levelSize - 5;
while (levelData[count - 1] != 3) count--;
levelData[count] = fileName[strlen(fileName) - 3];
levelData[count + 1] = fileName[strlen(fileName) - 2];
levelData[count + 2] = fileName[strlen(fileName) - 1];
return E_NONE;
}
void ServerGame::send (unsigned char *buffer) {
int count;
for (count = 0; count < MAX_CLIENTS; count++) {
// Send data to client, unless the data concerns the client's player
// Each client is solely responsible for its player's state
if ((clientStatus[count] != -1) &&
(((buffer[1] & MCMASK) != MC_PLAYER) ||
(buffer[2] != clientPlayer[count])))
net->send(clientSock[count], buffer);
}
return;
}
int ServerGame::playFrame (int ticks) {
unsigned char sendBuffer[BUFFER_LENGTH];
int count, pcount, length;
for (count = 0; count < MAX_CLIENTS; count++) {
if (clientStatus[count] >= 0) {
// Client is connected, but not operational
// Send a chunk of the level
length = levelSize - clientStatus[count];
if (length > 251) length = 251;
sendBuffer[0] = MTL_G_LEVEL + length;
sendBuffer[1] = MT_G_LEVEL;
sendBuffer[2] = clientStatus[count] >> 8;
sendBuffer[3] = clientStatus[count] & 255;
memcpy(sendBuffer + 4, levelData + clientStatus[count], length);
length = net->send(clientSock[count], sendBuffer);
// Client is operational if the whole level has been sent
// Otherwise, keep sending data
if (length == MTL_G_LEVEL) clientStatus[count] = -2;
else if (length > 0) clientStatus[count] += length - MTL_G_LEVEL;
}
if ((clientStatus[count] == -2) && (received[count] == 0)) {
// Client is operational, but not currently receiving a message
// See if there is a new message to receive
length = net->recv(clientSock[count], recvBuffers[count], 1);
if (length > 0) received[count]++;
}
if ((clientStatus[count] == -2) && (received[count] > 0)) {
// Currently receiving a message
// See if there is any more data
length = net->recv(clientSock[count],
recvBuffers[count] + received[count],
recvBuffers[count][0] - received[count]);
if (length > 0) received[count] += length;
// See if the whole message has arrived
if (received[count] >= recvBuffers[count][0]) {
switch (recvBuffers[count][1] & MCMASK) {
case MC_GAME:
if ((recvBuffers[count][1] == MT_G_PJOIN) &&
(clientPlayer[count] == -1)) {
printf("Player %d (client %d) joined the game.\n",
nPlayers, count);
// Set up the new player
recvBuffers[count][4] = gameMode->chooseTeam();
players[nPlayers].init((char *)(recvBuffers[count])
+ 9, recvBuffers[count] + 5,
recvBuffers[count][4]);
players[nPlayers].reset();
printf("Player %d joined team %d.\n",
nPlayers, recvBuffers[count][4]);
recvBuffers[count][3] = clientPlayer[count] =
nPlayers;
nPlayers++;
}
if (recvBuffers[count][1] == MT_G_CHECK) {
checkX = recvBuffers[count][2];
checkY = recvBuffers[count][3];
}
if (recvBuffers[count][1] == MT_G_SCORE) {
for (pcount = 0; pcount < nPlayers; pcount++) {
if (players[pcount].getTeam() ==
recvBuffers[pcount][2])
players[pcount].teamScore++;
}
}
break;
case MC_LEVEL:
level->receive(recvBuffers[count]);
break;
case MC_PLAYER:
// Assign player byte based on sender
recvBuffers[count][2] = clientPlayer[count];
players[clientPlayer[count]].
receive(recvBuffers[count]);
break;
}
// Update clients
send(recvBuffers[count]);
received[count] = 0;
}
}
if (ticks >= checkTime) {
if ((clientStatus[count] == -1) && levelData) {
// Client is not connected
// Check for new connection
clientSock[count] = net->accept(sock);
if (clientSock[count] != -1) {
printf("Client %d connected.\n", count);
clientPlayer[count] = -1;
received[count] = 0;
// Incorporate the new client
// Send data
sendBuffer[0] = MTL_G_PROPS;
sendBuffer[1] = MT_G_PROPS;
sendBuffer[2] = 1; // Server version
sendBuffer[3] = gameMode->getMode();
sendBuffer[4] = difficulty;
sendBuffer[5] = MAX_PLAYERS;
sendBuffer[6] = nPlayers; // Number of players
sendBuffer[7] = count; // Client's clientID
net->send(clientSock[count], sendBuffer);
// Initiate sending of level data
clientStatus[count] = 0;
// Inform the new client of the existing players
sendBuffer[1] = MT_G_PJOIN;
for (pcount = 0; pcount < nPlayers; pcount++) {
sendBuffer[0] = MTL_G_PJOIN +
strlen(players[pcount].getName());
sendBuffer[2] = count;
sendBuffer[3] = pcount;
sendBuffer[4] = players[pcount].getTeam();
memcpy(sendBuffer + 5, players[pcount].getCols(), 4);
memcpy(sendBuffer + 9, players[pcount].getName(),
strlen(players[pcount].getName()) + 1);
net->send(clientSock[count], sendBuffer);
}
}
} else {
// Client is connected
// Check for disconnection
if (!(net->isConnected(clientSock[count]))) {
printf("Client %d disconnected (code: %d).\n", count,
net->getError());
// Disconnect client
net->close(clientSock[count]);
clientStatus[count] = -1;
if (clientPlayer[count] != -1) {
// Remove the client's player
printf("Player %d (client %d) left the game.\n",
clientPlayer[count], count);
nPlayers--;
players[clientPlayer[count]].deinit();
// If necessary, move more recent players
for (pcount = clientPlayer[count]; pcount < nPlayers;
pcount++)
memcpy(players + pcount, players + pcount + 1,
sizeof(Player));
// Clear duplicate pointers
memset(players + nPlayers, 0, sizeof(Player));
// Inform remaining clients that the player has left
sendBuffer[0] = MTL_G_PQUIT;
sendBuffer[1] = MT_G_PQUIT;
sendBuffer[2] = clientPlayer[count];
send(sendBuffer);
clientPlayer[count] = -1;
}
}
}
}
}
if (ticks >= checkTime) checkTime = ticks + T_SCHECK;
if (ticks >= sendTime) {
// Update clients
sendBuffer[0] = MTL_P_TEMP;
sendBuffer[1] = MT_P_TEMP;
for (count = 0; count < nPlayers; count++) {
sendBuffer[2] = count;
players[count].send(sendBuffer);
send(sendBuffer);
}
sendTime = ticks + T_SSEND;
}
return E_NONE;
}
void ServerGame::score (unsigned char team) {
unsigned char buffer[MTL_G_SCORE];
int count;
// Inform clients
buffer[0] = MTL_G_SCORE;
buffer[1] = MT_G_SCORE;
buffer[2] = team;
send(buffer);
// Update self
for (count = 0; count < nPlayers; count++) {
if (players[count].getTeam() == team) players[count].teamScore++;
}
return;
}
void ServerGame::setCheckpoint (unsigned char gridX, unsigned char gridY) {
unsigned char buffer[MTL_G_CHECK];
if (gameMode) {
buffer[0] = MTL_G_CHECK;
buffer[1] = MT_G_CHECK;
buffer[2] = gridX;
buffer[3] = gridY;
send(buffer);
}
checkX = gridX;
checkY = gridY;
return;
}
/*
*
* controls.cpp
*
* 13th July 2009: Created controls.cpp from parts of main.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Deals with input.
*
*/
#include "controls.h"
Controls::Controls () {
int count;
keys[C_UP].key = SDLK_UP;
keys[C_DOWN].key = SDLK_DOWN;
keys[C_LEFT].key = SDLK_LEFT;
keys[C_RIGHT].key = SDLK_RIGHT;
#ifdef WIN32
keys[C_JUMP].key = SDLK_RALT;
keys[C_FIRE].key = SDLK_SPACE;
#else
keys[C_JUMP].key = SDLK_SPACE;
keys[C_FIRE].key = SDLK_LALT;
#endif
keys[C_CHANGE].key = SDLK_RCTRL;
keys[C_ENTER].key = SDLK_RETURN;
keys[C_ESCAPE].key = SDLK_ESCAPE;
keys[C_STATS].key = SDLK_F9;
keys[C_PAUSE].key = SDLK_p;
buttons[C_UP].button = -1;
buttons[C_DOWN].button = -1;
buttons[C_LEFT].button = -1;
buttons[C_RIGHT].button = -1;
buttons[C_JUMP].button = 1;
buttons[C_FIRE].button = 0;
buttons[C_CHANGE].button = 3;
buttons[C_ENTER].button = 0;
buttons[C_ESCAPE].button = -1;
buttons[C_STATS].button = -1;
buttons[C_PAUSE].button = -1;
axes[C_UP].axis = 1;
axes[C_UP].direction = false;
axes[C_DOWN].axis = 1;
axes[C_DOWN].direction = true;
axes[C_LEFT].axis = 0;
axes[C_LEFT].direction = false;
axes[C_RIGHT].axis = 0;
axes[C_RIGHT].direction = true;
axes[C_JUMP].axis = -1;
axes[C_FIRE].axis = -1;
axes[C_CHANGE].axis = -1;
axes[C_ENTER].axis = -1;
axes[C_ESCAPE].axis = -1;
axes[C_STATS].axis = -1;
axes[C_PAUSE].axis = -1;
for (count = 0; count < CONTROLS; count++) {
controls[count].time = 0;
controls[count].state = false;
}
return;
}
void Controls::setKey (int control, int key) {
keys[control].key = key;
return;
}
void Controls::setButton (int control, int button) {
buttons[control].button = button;
return;
}
void Controls::setAxis (int control, int axis, bool direction) {
axes[control].axis = axis;
axes[control].direction = direction;
return;
}
int Controls::getKey (int control) {
return keys[control].key;
}
int Controls::getButton (int control) {
return buttons[control].button;
}
int Controls::getAxis (int control) {
return axes[control].axis;
}
int Controls::getAxisDirection (int control) {
return axes[control].direction;
}
int Controls::update (SDL_Event *event, int type) {
int count;
count = CONTROLS;
switch (event->type) {
case SDL_KEYDOWN:
for (count = 0; count < CONTROLS; count++)
if (event->key.keysym.sym == keys[count].key) {
keys[count].state = true;
break;
}
if (type == KEY_LOOP) return event->key.keysym.sym;
break;
case SDL_KEYUP:
for (count = 0; count < CONTROLS; count++)
if (event->key.keysym.sym == keys[count].key) {
keys[count].state = false;
break;
}
break;
case SDL_JOYBUTTONDOWN:
for (count = 0; count < CONTROLS; count++)
if (event->jbutton.button == buttons[count].button) {
buttons[count].state = true;
break;
}
if (type == JOYSTICK_LOOP) return JOYSTICKB | event->jbutton.button;
break;
case SDL_JOYBUTTONUP:
for (count = 0; count < CONTROLS; count++)
if (event->jbutton.button == buttons[count].button) {
buttons[count].state = false;
break;
}
break;
case SDL_JOYAXISMOTION:
for (count = 0; count < CONTROLS; count++)
if (event->jaxis.axis == axes[count].axis) {
if (!axes[count].direction &&
(event->jaxis.value < -16384)) {
axes[count].state = true;
break;
}
else if (axes[count].direction &&
(event->jaxis.value > 16384)) {
axes[count].state = true;
break;
}
else axes[count].state = false;
}
if (type == JOYSTICK_LOOP) {
if (event->jaxis.value < -16384)
return JOYSTICKANEG | event->jaxis.axis;
if (event->jaxis.value > 16384)
return JOYSTICKAPOS | event->jaxis.axis;
}
break;
}
if (count < CONTROLS) {
if (!(keys[count].state || buttons[count].state || axes[count].state)) {
controls[count].time = 0;
controls[count].state = false;
}
}
return E_NONE;
}
void Controls::loop () {
int count;
// Apply controls to universal control tracking
for (count = 0; count < CONTROLS; count++)
controls[count].state = (controls[count].time < globalTicks) &&
(keys[count].state || buttons[count].state || axes[count].state);
return;
}
bool Controls::getState (int control) {
return controls[control].state;
}
bool Controls::release (int control) {
if (!controls[control].state) return false;
controls[control].time = globalTicks + T_KEY;
controls[control].state = false;
return true;
}
/*
*
* controls.h
*
* 13th July 2009: Created controls.h from parts of OpenJazz.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _INPUT_H
#define _INPUT_H
#include "OpenJazz.h"
#include <SDL/SDL.h>
// Constants
// Indexes for the keys / buttons / axes player controls arrays
#define C_UP 0
#define C_DOWN 1
#define C_LEFT 2
#define C_RIGHT 3
#define C_JUMP 4
#define C_FIRE 5
#define C_CHANGE 6 /* Change weapon */
#define C_ENTER 7
#define C_ESCAPE 8
#define C_STATS 9
#define C_PAUSE 10
// Size of those arrays
#define CONTROLS 11
// Time interval
#define T_KEY 500
// Class
class Controls {
private:
struct {
int key; // Keyboard key
bool state;
} keys[CONTROLS];
struct {
int button; // Joystick button
bool state;
} buttons[CONTROLS];
struct {
int axis; // Joystick axis
bool direction; // Axis direction
bool state;
} axes[CONTROLS];
struct {
unsigned int time; /* The time from which the control will respond
to being pressed */
bool state;
} controls[CONTROLS];
public:
Controls ();
void setKey (int control, int key);
void setButton (int control, int button);
void setAxis (int control, int axis, bool direction);
int getKey (int control);
int getButton (int control);
int getAxis (int control);
int getAxisDirection (int control);
int update (SDL_Event *event, int type);
void loop ();
bool getState (int control);
bool release (int control);
};
// Variable
EXTERN Controls controls;
#endif
/*
*
* file.cpp
*
* 3rd February 2009: Created file.cpp from parts of util.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "file.h"
#include "io/gfx/video.h"
File::File (char * fileName, bool write) {
// Open the file from the current directory
f = fopen(fileName, write ? "wb": "rb");
// If that succeeded, done
if (f) {
//log("Opened file", fileName);
filePath = createString(fileName);
return;
}
// Create the file path
filePath = createString(path, fileName);
// Open the file from the path
f = fopen(filePath, write ? "wb": "rb");
if (f == NULL) {
log("Could not open file", fileName);
delete[] filePath;
throw E_FILE;
}
//log("Opened file", filePath);
return;
}
File::~File () {
fclose(f);
//log("Closed file", filePath);
delete[] filePath;
return;
}
int File::getSize () {
int pos, size;
pos = ftell(f);
fseek(f, 0, SEEK_END);
size = ftell(f);
fseek(f, pos, SEEK_SET);
return size;
}
int File::tell () {
return ftell(f);
}
void File::seek (int offset, bool reset) {
fseek(f, offset, reset ? SEEK_SET: SEEK_CUR);
return;
}
unsigned char File::loadChar () {
return fgetc(f);
}
void File::storeChar (unsigned char val) {
fputc(val, f);
return;
}
unsigned short int File::loadShort () {
unsigned short int val;
val = fgetc(f);
val += fgetc(f) << 8;
return val;
}
void File::storeShort (unsigned short int val) {
fputc(val & 255, f);
fputc(val >> 8, f);
return;
}
signed long int File::loadInt () {
unsigned long int val;
val = fgetc(f);
val += fgetc(f) << 8;
val += fgetc(f) << 16;
val += fgetc(f) << 24;
return *((signed long int *)&val);
}
void File::storeInt (signed long int val) {
unsigned long int uval;
uval = *((unsigned long int *)&val);
fputc(uval & 255, f);
fputc((uval >> 8) & 255, f);
fputc((uval >> 16) & 255, f);
fputc(uval >> 24, f);
return;
}
unsigned char * File::loadBlock (int length) {
unsigned char *buffer;
buffer = new unsigned char[length];
fread(buffer, 1, length, f);
return buffer;
}
unsigned char * File::loadRLE (int length) {
unsigned char *buffer;
int rle, pos, byte, count, next;
// Determine the offset that follows the block
next = fgetc(f);
next += fgetc(f) << 8;
next += ftell(f);
buffer = new unsigned char[length];
pos = 0;
while (pos < length) {
rle = fgetc(f);
if (rle > 127) {
byte = fgetc(f);
for (count = 0; count < (rle & 127); count++) {
buffer[pos++] = byte;
if (pos >= length) break;
}
} else if (rle > 0) {
for (count = 0; count < rle; count++) {
buffer[pos++] = fgetc(f);
if (pos >= length) break;
}
} else buffer[pos++] = fgetc(f);
}
fseek(f, next, SEEK_SET);
return buffer;
}
void File::skipRLE () {
int next;
next = fgetc(f);
next += fgetc(f) << 8;
fseek(f, next, SEEK_CUR);
return;
}
char * File::loadString () {
char *string;
int length, count;
length = fgetc(f);
count = 0;
if (length) {
string = new char[length + 1];
for (; count < length; count++) string[count] = fgetc(f);
} else {
// If the length is not given, assume it is an 8.3 file name
string = new char[13];
for (; count < 9; count++) {
string[count] = fgetc(f);
if (string[count] == '.') {
string[++count] = fgetc(f);
string[++count] = fgetc(f);
string[++count] = fgetc(f);
count++;
break;
}
}
}
string[count] = 0;
return string;
}
SDL_Surface * File::loadSurface (int width, int height) {
return createSurface(loadRLE(width * height), width, height);
}
void File::loadPalette (SDL_Color *palette) {
unsigned char *buffer;
int count;
buffer = loadRLE(768);
for (count = 0; count < 256; count++) {
// Palette entries are 6-bit
// Shift them upwards to 8-bit, and fill in the lower 2 bits
palette[count].r = (buffer[count * 3] << 2) + (buffer[count * 3] >> 4);
palette[count].g = (buffer[(count * 3) + 1] << 2) +
(buffer[(count * 3) + 1] >> 4);
palette[count].b = (buffer[(count * 3) + 2] << 2) +
(buffer[(count * 3) + 2] >> 4);
}
delete[] buffer;
return;
}
/*
*
* file.h
*
* 3rd February 2009: Created file.h from parts of OpenJazz.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _FILE_H
#define _FILE_H
#include "OpenJazz.h"
#include <SDL/SDL.h>
#include <stdio.h>
// Class
class File {
private:
FILE *f;
char *filePath;
public:
File (char * fileName, bool write);
~File ();
int getSize ();
void seek (int offset, bool reset);
int tell ();
unsigned char loadChar ();
void storeChar (unsigned char val);
unsigned short int loadShort ();
void storeShort (unsigned short int val);
signed long int loadInt ();
void storeInt (signed long int val);
unsigned char * loadBlock (int length);
unsigned char * loadRLE (int length);
void skipRLE ();
char * loadString ();
SDL_Surface * loadSurface (int width, int height);
void loadPalette (SDL_Color *palette);
};
//Variable
// Path to game data
EXTERN char *path;
#endif
/*
*
* anim.cpp
*
* 26th July 2009: Created anim.cpp from parts of sprite.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "anim.h"
#include "sprite.h"
#include "level/level.h"
Anim::Anim () {
frame = 0;
return;
}
Anim::~Anim () {
return;
}
void Anim::setData (int amount, signed char x, signed char y) {
frames = amount;
xOffset = x << 2;
yOffset = y;
return;
}
void Anim::setFrame (int nextFrame, bool looping) {
if (looping) frame = nextFrame % frames;
else frame = (nextFrame >= frames)? frames - 1: nextFrame;
return;
}
void Anim::setFrameData (Sprite *sprite, signed char x, signed char y) {
sprites[frame] = sprite;
xOffsets[frame] = x << 2;
yOffsets[frame] = y;
return;
}
int Anim::getWidth () {
return sprites[frame]->getWidth();
}
int Anim::getHeight() {
return sprites[frame]->getHeight();
}
void Anim::draw (int x, int y) {
sprites[frame]->draw(FTOI(x) + xOffsets[frame] - xOffset - FTOI(viewX),
FTOI(y) + yOffsets[frame] - yOffset - FTOI(viewY));
return;
}
void Anim::setPalette (SDL_Color *palette, int start, int amount) {
sprites[frame]->setPalette(palette, 0, 256);
return;
}
void Anim::flashPalette (int index) {
sprites[frame]->flashPalette(index);
return;
}
void Anim::restorePalette () {
sprites[frame]->restorePalette();
return;
}
/*
*
* anim.h
*
* 26th July 2009: Created anim.h from parts of sprite.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _ANIM_H
#define _ANIM_H
#include <SDL/SDL.h>
// Classes
class Sprite;
class Anim {
private:
Sprite *sprites[19];
signed char xOffset;
signed char yOffset;
signed char xOffsets[19];
signed char yOffsets[19];
unsigned char frames; // Number of frames
unsigned char frame; // Current frame
public:
Anim ();
~Anim ();
void setData (int amount, signed char x, signed char y);
void setFrame (int nextFrame, bool looping);
void setFrameData (Sprite *frameSprite, signed char x, signed char y);
int getWidth ();
int getHeight ();
void draw (int x, int y);
void setPalette (SDL_Color *palette, int start, int amount);
void flashPalette (int index);
void restorePalette ();
};
#endif
/*
*
* font.cpp
*
* 23rd August 2005: Created font.c
* 3rd February 2009: Renamed font.c to font.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Deals with the loading, displaying and freeing of screen fonts.
*
*/
#include "../file.h"
#include "font.h"
#include "video.h"
#include <string.h>
Font::Font (char * fileName) {
File *file;
unsigned char *pixels, *character;
int rle, pos, byte, count, next, fileSize;
int chr, width, height, y;
// Load font from a font file
try {
file = new File(fileName, false);
} catch (int e) {
throw e;
}
file->seek(19, true);
width = file->loadChar() << 2;
h = file->loadChar() << 1;
w = new unsigned char[128];
pixels = new unsigned char[width * h * 128];
memset(pixels, 0, width * h * 128);
file->seek(23, true);
w[0] = width >> 1;
fileSize = file->getSize();
for (chr = 1; chr < 128; chr++) {
file->seek(2, false);
next = file->loadChar();
if (file->tell() > fileSize) break;
next += file->loadChar() << 8;
next += file->tell();
file->seek(1, false);
w[chr] = file->loadChar();
file->seek(1, false);
height = file->loadChar();
character = new unsigned char[(w[chr] * height) + 1];
pos = 0;
while (pos < w[chr] * height) {
rle = file->loadChar();
if (rle > 127) {
byte = file->loadChar();
for (count = 0; count < (rle & 127); count++) {
character[pos++] = byte;
if (pos >= w[chr] * height) break;
}
} else if (rle > 0) {
for (count = 0; count < rle; count++) {
character[pos++] = file->loadChar();
if (pos >= w[chr] * height) break;
}
} else break;
}
character[pos] = character[pos - 1];
for (y = 0; y < height; y++)
memcpy(pixels + (((chr * h) + y) * width),
character + (y * w[chr]) + 1, w[chr]);
delete[] character;
w[chr] += 2;
file->seek(next, true);
}
delete file;
for (; chr < 128; chr++) w[chr] = width >> 1;
surface = createSurface(pixels, width, h * 128);
SDL_SetColorKey(surface, SDL_SRCCOLORKEY, 0);
// Create ASCII->font map
if (!strcmp(fileName, "fontmn1.0fn")) {
for (count = 0; count < 48; count++) map[count] = 0;
for (; count < 58; count++) map[count] = count - 8;
for (; count < 65; count++) map[count] = 0;
for (; count < 91; count++) map[count] = count - 64;
for (; count < 97; count++) map[count] = 0;
for (; count < 123; count++) map[count] = count - 96;
for (; count < 128; count++) map[count] = 0;
} else if (!strcmp(fileName, "fontmn2.0fn")) {
for (count = 0; count < 43; count++) map[count] = 0;
map[33] = 89;
map[34] = 96;
map[39] = 95;
map[43] = 87;
map[44] = 83;
map[45] = 86;
map[46] = 84;
map[47] = 90;
count = 48;
for (; count < 58; count++) map[count] = count + 5;
map[58] = 94;
map[59] = 93;
map[60] = 0;
map[61] = 88;
map[62] = 0;
map[63] = 85;
map[64] = 0;
count = 65;
for (; count < 91; count++) map[count] = count - 38;
for (; count < 97; count++) map[count] = 0;
for (; count < 123; count++) map[count] = count - 96;
for (; count < 128; count++) map[count] = 0;
} else {
for (count = 0; count < 40; count++) map[count] = 0;
for (; count < 42; count++) map[count] = count + 25;
for (; count < 48; count++) map[count] = 0;
map[44] = 101;
map[45] = 104;
map[46] = 102;
for (; count < 58; count++) map[count] = count + 5;
for (; count < 65; count++) map[count] = 0;
map[63] = 103;
for (; count < 91; count++) map[count] = count - 64;
for (; count < 97; count++) map[count] = 0;
for (; count < 123; count++) map[count] = count - 70;
for (; count < 128; count++) map[count] = 0;
}
return;
}
Font::Font (File *file, bool big) {
unsigned char *pixels;
int rle, pos, index, count;
// Load font from panel.000
if (big) h = 8;
else h = 7;
pixels = new unsigned char[320 * h];
if (big) {
// Load the large panel font
// Starts at 4691 and goes 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-:.
pixels[0] = BLACK;
pos = 1;
file->seek(4691, true);
} else {
// Load the small panel font
// Starts at 6975 and goes 0123456789oo (where oo = infinity)
pos = 0;
file->seek(6975, true);
}
// RLE decompression and horizontal to vertical character rearrangement
while (pos < 320 * h) {
rle = file->loadChar();
if (rle >= 128) {
index = file->loadChar();
for (count = 0; count < (rle & 127); count++) {
pixels[(pos & 7) + ((pos / 320) * 8) +
(((pos % 320)>>3) * 8 * h)] = index;
pos++;
}
} else if (rle > 0) {
for (count = 0; count < rle; count++) {
pixels[(pos & 7) + ((pos / 320) * 8) +
(((pos % 320)>>3) * 8 * h)] = file->loadChar();
pos++;
}
} else break;
}
if (big) {
// Create ASCII->font map
for (count = 0; count < 45; count++) map[count] = 39;
map[45] = 36;
map[46] = 38;
for (count = 47; count < 48; count++) map[count] = 39;
for (; count < 58; count++) map[count] = count - 48;
map[58] = 37;
for (count = 59; count < 65; count++) map[count] = 39;
for (; count < 91; count++) map[count] = count - 55;
for (; count < 97; count++) map[count] = 39;
for (; count < 123; count++) map[count] = count - 87;
for (; count < 128; count++) map[count] = 39;
// Set font dimensions
w = new unsigned char[40];
for (count = 0; count < 40; count++) w[count] = 8;
} else {
// Create ASCII->font map
for (count = 0; count < 48; count++) map[count] = 12;
// Use :; to represent the infinity symbol
for (; count < 60; count++) map[count] = count - 48;
for (; count < 128; count++) map[count] = 12;
// Set font dimensions
w = new unsigned char[13];
for (count = 0; count < 13; count++) w[count] = 8;
}
surface = createSurface(pixels, 8, 40 * h);
return;
}
Font::~Font () {
SDL_FreeSurface(surface);
delete[] w;
return;
}
int Font::showString (char * s, int x, int y) {
SDL_Rect src, dst;
unsigned int count;
int xOffset, yOffset;
// Determine the characters' dimensions
src.x = 0;
src.h = h;
// Determine the position at which to draw the first character
xOffset = x;
yOffset = y;
// Go through each character of the string
for (count = 0; s[count]; count++) {
if (s[count] == '\n') {
xOffset = x;
yOffset += h;
} else {
// Determine the character's position on the screen
src.w = w[(int)(map[(int)(s[count])])];
dst.y = yOffset;
dst.x = xOffset;
// Determine the character's position in the font
if (s[count] >= 0) src.y = map[(int)(s[count])] * h;
else src.y = 0;
// Draw the character to the screen
SDL_BlitSurface(surface, &src, screen, &dst);
xOffset += w[(int)(map[(int)(s[count])])];
}
}
return xOffset;
}
void Font::showNumber (int n, int x, int y) {
SDL_Rect src, dst;
int count, offset;
// Determine the characters' dimensions
src.x = 0;
src.h = h;
// n being 0 is a special case. It must not be considered to be a trailing
// zero, as these are not displayed.
if (!n) {
// Determine 0's position on the screen
src.w = w[(int)(map['0'])];
dst.y = y;
dst.x = x - src.w;
// Determine 0's position in the font
src.y = map['0'] * h;
// Draw 0 to the screen
SDL_BlitSurface(surface, &src, screen, &dst);
return;
}
// Determine the length of the number to be drawn
if (n > 0) count = n;
else count = -n;
// Determine the position at which to draw the lowest digit
offset = x;
while (count) {
// Determine the digit's position on the screen
src.w = w[(int)(map['0' + (count % 10)])];
offset -= src.w;
dst.y = y;
dst.x = offset;
// Determine the digit's position in the font
src.y = map['0' + (count % 10)] * h;
// Draw the digit to the screen
SDL_BlitSurface(surface, &src, screen, &dst);
count /= 10;
}
// If needed, draw the negative sign
if (n < 0) {
// Determine the negative sign's position on the screen
src.w = w[(int)(map['-'])];
dst.y = y;
dst.x = offset - src.w;
// Determine the negative sign's position on the screen
src.y = map['-'] * h;
// Draw the negative sign to the screen
SDL_BlitSurface(surface, &src, screen, &dst);
}
return;
}
void Font::mapPalette (int start, int length, int newStart, int newLength) {
SDL_Color palette[256];
int count;
// Map a range of palette indices to another range
for (count = 0; count < length; count++)
palette[count].r = palette[count].g = palette[count].b =
(count * newLength / length) + newStart;
SDL_SetPalette(surface, SDL_LOGPAL, palette, start, length);
return;
}
void Font::restorePalette () {
::restorePalette(surface);
return;
}
/*
*
* font.h
*
* 3rd February 2009: Created font.h from parts of OpenJazz.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _FONT_H
#define _FONT_H
#include "OpenJazz.h"
#include <SDL/SDL.h>
// Classes
class File;
class Font {
private:
SDL_Surface *surface;
unsigned char *w;
unsigned char h; // Dimensions of the letters
char map[128]; // Maps ASCII values to letter positions
public:
Font (char *fileName);
Font (File *file, bool big);
~Font ();
int showString (char *s, int x, int y);
void showNumber (int n, int x, int y);
void mapPalette (int start, int length, int newStart,
int newLength);
void restorePalette ();
};
// Variables
EXTERN Font *font2; /* Taken from .0FN file name */
EXTERN Font *fontbig; /* Taken from .0FN file name */
EXTERN Font *fontiny; /* Taken from .0FN file name */
EXTERN Font *fontmn1; /* Taken from .0FN file name */
EXTERN Font *fontmn2; /* Taken from .0FN file name */
EXTERN Font *panelBigFont; /* Not a font file, found in PANEL.000 */
EXTERN Font *panelSmallFont; /* Not a font file, found in PANEL.000 */
#endif
/*
*
* paletteeffects.cpp
*
* 4th February 2009: Created palette.cpp from parts of main.cpp and util.cpp
* 1st August 2009: Renamed palette.cpp to paletteeffects.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "paletteeffects.h"
#include "video.h"
#include "level/level.h"
#include "player/player.h"
#include <string.h>
PaletteEffect::PaletteEffect (PaletteEffect * nextPE) {
next = nextPE;
return;
}
PaletteEffect::~PaletteEffect () {
if (next) delete next;
return;
}
void PaletteEffect::apply (SDL_Color *shownPalette, bool direct) {
// Apply the next palette effect
if (next) next->apply(shownPalette, direct);
return;
}
WhiteInPaletteEffect::WhiteInPaletteEffect (fixed newDuration,
PaletteEffect * nextPE) : PaletteEffect (nextPE) {
duration = newDuration;
whiteness = F1 + FH;
return;
}
void WhiteInPaletteEffect::apply (SDL_Color *shownPalette, bool direct) {
int count;
// Apply the next palette effect
if (next) next->apply(shownPalette, direct);
if (whiteness > F1) {
memset(shownPalette, 255, sizeof(SDL_Color) * 256);
whiteness -= ITOF(mspf) / duration;
} else if (whiteness > 0) {
for (count = 0; count < 256; count++) {
shownPalette[count].r = 255 -
FTOI((255 - shownPalette[count].r) * (F1 - whiteness));
shownPalette[count].g = 255 -
FTOI((255 - shownPalette[count].g) * (F1 - whiteness));
shownPalette[count].b = 255 -
FTOI((255 - shownPalette[count].b) * (F1 - whiteness));
}
whiteness -= ITOF(mspf) / duration;
}
if (direct)
SDL_SetPalette(screen, SDL_PHYSPAL, shownPalette, 0, 256);
return;
}
FadeInPaletteEffect::FadeInPaletteEffect (int newDuration,
PaletteEffect * nextPE) : PaletteEffect (nextPE) {
duration = newDuration;
blackness = F1 + FH;
return;
}
void FadeInPaletteEffect::apply (SDL_Color *shownPalette, bool direct) {
int count;
// Apply the next palette effect
if (next) next->apply(shownPalette, direct);
if (blackness > F1) {
memset(shownPalette, 0, sizeof(SDL_Color) * 256);
blackness -= ITOF(mspf) / duration;
} else if (blackness > 0) {
for (count = 0; count < 256; count++) {
shownPalette[count].r =
FTOI(shownPalette[count].r * (F1 - blackness));
shownPalette[count].g =
FTOI(shownPalette[count].g * (F1 - blackness));
shownPalette[count].b =
FTOI(shownPalette[count].b * (F1 - blackness));
}
blackness -= ITOF(mspf) / duration;
}
if (direct)
SDL_SetPalette(screen, SDL_PHYSPAL, shownPalette, 0, 256);
return;
}
WhiteOutPaletteEffect::WhiteOutPaletteEffect (int newDuration,
PaletteEffect * nextPE) : PaletteEffect (nextPE) {
duration = newDuration;
whiteness = 0;
return;
}
void WhiteOutPaletteEffect::apply (SDL_Color *shownPalette, bool direct) {
int count;
// Apply the next palette effect
if (next) next->apply(shownPalette, direct);
if (whiteness > F1) {
memset(shownPalette, 255, sizeof(SDL_Color) * 256);
} else if (whiteness > 0) {
for (count = 0; count < 256; count++) {
shownPalette[count].r = 255 -
FTOI((255 - shownPalette[count].r) * (F1 - whiteness));
shownPalette[count].g = 255 -
FTOI((255 - shownPalette[count].g) * (F1 - whiteness));
shownPalette[count].b = 255 -
FTOI((255 - shownPalette[count].b) * (F1 - whiteness));
}
whiteness += ITOF(mspf) / duration;
} else whiteness += ITOF(mspf) / duration;
if (direct)
SDL_SetPalette(screen, SDL_PHYSPAL, shownPalette, 0, 256);
return;
}
FadeOutPaletteEffect::FadeOutPaletteEffect (int newDuration,
PaletteEffect * nextPE) : PaletteEffect (nextPE) {
duration = newDuration;
blackness = -(F2 + F1);
return;
}
void FadeOutPaletteEffect::apply (SDL_Color *shownPalette, bool direct) {
int count;
// Apply the next palette effect
if (next) next->apply(shownPalette, direct);
if (blackness > F1) {
memset(shownPalette, 0, sizeof(SDL_Color) * 256);
} else if (blackness > 0) {
for (count = 0; count < 256; count++) {
shownPalette[count].r =
FTOI(shownPalette[count].r * (F1 - blackness));
shownPalette[count].g =
FTOI(shownPalette[count].g * (F1 - blackness));
shownPalette[count].b =
FTOI(shownPalette[count].b * (F1 - blackness));
}
blackness += ITOF(mspf) / duration;
} else blackness += ITOF(mspf) / duration;
if (direct)
SDL_SetPalette(screen, SDL_PHYSPAL, shownPalette, 0, 256);
return;
}
FlashPaletteEffect::FlashPaletteEffect (unsigned char newRed,
unsigned char newGreen, unsigned char newBlue, int newDuration,
PaletteEffect * nextPE) : PaletteEffect (nextPE) {
duration = newDuration;
progress = -F1;
red = newRed;
green = newGreen;
blue = newBlue;
return;
}
void FlashPaletteEffect::apply (SDL_Color *shownPalette, bool direct) {
int count;
// Apply the next palette effect
if (next) next->apply(shownPalette, direct);
if (progress < 0) {
for (count = 0; count < 256; count++) {
shownPalette[count].r = FTOI((shownPalette[count].r * -progress) +
(red * (progress + F1)));
shownPalette[count].g = FTOI((shownPalette[count].g * -progress) +
(green * (progress + F1)));
shownPalette[count].b = FTOI((shownPalette[count].b * -progress) +
(blue * (progress + F1)));
}
progress += ITOF(mspf) / duration;
} else if (progress < F1) {
for (count = 0; count < 256; count++) {
shownPalette[count].r = FTOI((shownPalette[count].r * progress) +
(red * (F1 - progress)));
shownPalette[count].g = FTOI((shownPalette[count].g * progress) +
(green * (F1 - progress)));
shownPalette[count].b = FTOI((shownPalette[count].b * progress) +
(blue * (F1 - progress)));
}
progress += ITOF(mspf) / duration;
}
if (direct)
SDL_SetPalette(screen, SDL_PHYSPAL, shownPalette, 0, 256);
return;
}
RotatePaletteEffect::RotatePaletteEffect (unsigned char newFirst,
int newAmount, fixed newSpeed, PaletteEffect * nextPE) :
PaletteEffect (nextPE) {
first = newFirst;
amount = newAmount;
speed = newSpeed;
position = 0;
return;
}
void RotatePaletteEffect::apply (SDL_Color *shownPalette, bool direct) {
int count;
// Apply the next palette effect
if (next) next->apply(shownPalette, direct);
for (count = 0; count < amount; count++) {
memcpy(shownPalette + first + count,
currentPalette + first +
((count + FTOI(position)) % amount), sizeof(SDL_Color));
}
position -= (mspf * speed) >> 10;
while (position < 0) position += ITOF(amount);
if (direct)
SDL_SetPalette(screen, SDL_PHYSPAL, shownPalette + first, first,
amount);
return;
}
SkyPaletteEffect::SkyPaletteEffect (unsigned char newFirst,
int newAmount, fixed newSpeed, SDL_Color *newSkyPalette,
PaletteEffect * nextPE) : PaletteEffect (nextPE) {
skyPalette = newSkyPalette;
first = newFirst;
amount = newAmount;
speed = newSpeed;
return;
}
void SkyPaletteEffect::apply (SDL_Color *shownPalette, bool direct) {
int position, count, y;
// Apply the next palette effect
if (next) next->apply(shownPalette, direct);
position = viewY + (viewH << 9) - F4;
if (screenW > 320) y = ((screenH - 1) / 100) + 1;
else y = ((screenH - 34) / 100) + 1;
count = (((position * speed) / y) >> 20) % 255;
if (direct) {
if (count > 255 - amount) {
SDL_SetPalette(screen, SDL_PHYSPAL, skyPalette + count, first,
255 - count);
SDL_SetPalette(screen, SDL_PHYSPAL, skyPalette,
first + (255 - count), count + amount - 255);
} else {
SDL_SetPalette(screen, SDL_PHYSPAL, skyPalette + count,
first, amount);
}
} else {
if (count > 255 - amount) {
memcpy(shownPalette + first, skyPalette + count,
sizeof(SDL_Color) * (255 - count));
memcpy(shownPalette + first + (255 - count), skyPalette,
sizeof(SDL_Color) * (count + amount - 255));
} else {
memcpy(shownPalette + first, skyPalette + count,
sizeof(SDL_Color) * amount);
}
}
return;
}
P2DPaletteEffect::P2DPaletteEffect (unsigned char newFirst,
int newAmount, fixed newSpeed, PaletteEffect * nextPE) :
PaletteEffect (nextPE) {
first = newFirst;
amount = newAmount;
speed = newSpeed;
return;
}
void P2DPaletteEffect::apply (SDL_Color *shownPalette, bool direct) {
int count, x, y, j;
// Apply the next palette effect
if (next) next->apply(shownPalette, direct);
x = FTOI(((256 * 32) - FTOI(viewX)) * speed);
y = FTOI(((64 * 32) - FTOI(viewY)) * speed);
for (count = 0; count < amount >> 3; count++) {
for (j = 0; j < 8; j++) {
memcpy(shownPalette + first + (count << 3) + j,
currentPalette + first + (((count + y) % 8) << 3) +
((j + x) % 8), sizeof(SDL_Color));
}
}
if (direct)
SDL_SetPalette(screen, SDL_PHYSPAL, shownPalette + first, first,
amount);
return;
}
P1DPaletteEffect::P1DPaletteEffect (unsigned char newFirst,
int newAmount, fixed newSpeed, PaletteEffect * nextPE) :
PaletteEffect (nextPE) {
first = newFirst;
amount = newAmount;
speed = newSpeed;
return;
}
void P1DPaletteEffect::apply (SDL_Color *shownPalette, bool direct) {
fixed position;
int count;
// Apply the next palette effect
if (next) next->apply(shownPalette, direct);
position = viewX + viewY;
for (count = 0; count < amount; count++) {
memcpy(shownPalette + first + count,
currentPalette + first + ((count +
(amount - 1 - (FTOI(MUL(position, speed)) % amount))) % amount),
sizeof(SDL_Color));
}
if (direct)
SDL_SetPalette(screen, SDL_PHYSPAL, shownPalette + first, first,
amount);
return;
}
WaterPaletteEffect::WaterPaletteEffect (fixed newDepth, PaletteEffect * nextPE)
: PaletteEffect (nextPE) {
depth = newDepth;
return;
}
void WaterPaletteEffect::apply (SDL_Color *shownPalette, bool direct) {
int position, count;
// Apply the next palette effect
if (next) next->apply(shownPalette, direct);
position = localPlayer->getY() - level->getWaterLevel(0);
if (position <= 0) return;
if (position < depth) {
for (count = 0; count < 256; count++) {
shownPalette[count].r = FTOI(currentPalette[count].r *
(1023 - DIV(position, depth)));
shownPalette[count].g = FTOI(currentPalette[count].g *
(1023 - DIV(position, depth)));
shownPalette[count].b = FTOI(currentPalette[count].b *
(1023 - DIV(position, depth)));
}
} else memset(shownPalette, 0, sizeof(SDL_Color) * 256);
if (direct)
SDL_SetPalette(screen, SDL_PHYSPAL, shownPalette, 0, 256);
return;
}
/*
*
* paletteeffects.h
*
* 4th February 2009: Created palette.h from parts of OpenJazz.h
* 1st August 2009: Renamed palette.h to paletteeffects.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _PALETTE_H
#define _PALETTE_H
#include "OpenJazz.h"
#include <SDL/SDL.h>
// Constants
// Types of palette effect
#define PE_FADE 0 /* Fades to black, then remains black */
#define PE_ROTATE 1 /* Cyclical colour animation */
// Level background palette effects
#define PE_SKY 2 /* Transfers the appropriate section of the background
palette to the main palette */
#define PE_2D 8 /* Real parallaxing background */
#define PE_1D 9 /* Diagonal lines parallaxing background */
#define PE_WATER 11 /* The deeper below water, the darker it gets */
// Class
class PaletteEffect {
protected:
PaletteEffect *next; // Next effect to use
public:
PaletteEffect (PaletteEffect * nextPE);
virtual ~PaletteEffect ();
virtual void apply (SDL_Color *shownPalette, bool direct);
};
class WhiteInPaletteEffect : public PaletteEffect {
private:
int duration; // Number of milliseconds the effect lasts
fixed whiteness;
public:
WhiteInPaletteEffect (int newDuration, PaletteEffect * nextPE);
void apply (SDL_Color *shownPalette, bool direct);
};
class FadeInPaletteEffect : public PaletteEffect {
private:
int duration; // Number of milliseconds the effect lasts
fixed blackness;
public:
FadeInPaletteEffect (int newDuration, PaletteEffect * nextPE);
void apply (SDL_Color *shownPalette, bool direct);
};
class WhiteOutPaletteEffect : public PaletteEffect {
private:
int duration; // Number of milliseconds the effect lasts
fixed whiteness;
public:
WhiteOutPaletteEffect (int newDuration, PaletteEffect * nextPE);
void apply (SDL_Color *shownPalette, bool direct);
};
class FadeOutPaletteEffect : public PaletteEffect {
private:
int duration; // Number of milliseconds the effect lasts
fixed blackness;
public:
FadeOutPaletteEffect (int newDuration, PaletteEffect * nextPE);
void apply (SDL_Color *shownPalette, bool direct);
};
class FlashPaletteEffect : public PaletteEffect {
private:
int duration; // Number of milliseconds the effect lasts
fixed progress;
unsigned char red, green, blue; // Flash colour
public:
FlashPaletteEffect (unsigned char newRed, unsigned char newGreen,
unsigned char newBlue, int newDuration, PaletteEffect * nextPE);
void apply (SDL_Color *shownPalette, bool direct);
};
class RotatePaletteEffect : public PaletteEffect {
private:
unsigned char first; /* The first palette index affected by the
effect */
int amount; /* The number of (consecutive) palette indices
affected by the effect */
fixed speed; // Rotations per second
fixed position;
public:
RotatePaletteEffect (unsigned char newFirst, int newAmount,
fixed newSpeed, PaletteEffect * nextPE);
void apply (SDL_Color *shownPalette, bool direct);
};
class SkyPaletteEffect : public PaletteEffect {
private:
SDL_Color *skyPalette;
unsigned char first; /* The first palette index affected by the
effect */
int amount; /* The number of (consecutive) palette
indices affected by the effect */
fixed speed; // Relative Y speed - as in Jazz 2
public:
SkyPaletteEffect (unsigned char newFirst, int newAmount,
fixed newSpeed, SDL_Color *newSkyPalette, PaletteEffect * nextPE);
void apply (SDL_Color *shownPalette, bool direct);
};
class P2DPaletteEffect : public PaletteEffect {
private:
unsigned char first; /* The first palette index affected by the
effect */
int amount; /* The number of (consecutive) palette indices
affected by the effect */
fixed speed; // Relative X & Y speed - as in Jazz 2
public:
P2DPaletteEffect (unsigned char newFirst, int newAmount,
fixed newSpeed, PaletteEffect * nextPE);
void apply (SDL_Color *shownPalette, bool direct);
};
class P1DPaletteEffect : public PaletteEffect {
private:
unsigned char first; /* The first palette index affected by the
effect */
int amount; /* The number of (consecutive) palette indices
affected by the effect */
fixed speed; // Relative X & Y speed - as in Jazz 2
public:
P1DPaletteEffect (unsigned char newFirst, int newAmount,
fixed newSpeed, PaletteEffect * nextPE);
void apply (SDL_Color *shownPalette, bool direct);
};
class WaterPaletteEffect : public PaletteEffect {
private:
fixed depth; /* Number of pixels between water surface and total
darkness */
public:
WaterPaletteEffect (fixed newDepth, PaletteEffect * nextPE);
void apply (SDL_Color *shownPalette, bool direct);
};
// Variable
EXTERN PaletteEffect *firstPE;
#endif
/*
*
* sprite.cpp
*
* 19th March 2009: Created sprite.cpp from parts of event.cpp and player.cpp
* 26th July 2009: Created anim.cpp from parts of sprite.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "video.h"
#include "sprite.h"
Sprite::Sprite () {
pixels = NULL;
return;
}
Sprite::~Sprite () {
if (pixels) SDL_FreeSurface(pixels);
return;
}
void Sprite::clearPixels () {
unsigned char *data;
if (pixels) SDL_FreeSurface(pixels);
data = new unsigned char[1];
*data = SKEY;
pixels = createSurface(data, 1, 1);
SDL_SetColorKey(pixels, SDL_SRCCOLORKEY, SKEY);
return;
}
void Sprite::setPixels (unsigned char *data, int width, int height) {
if (pixels) SDL_FreeSurface(pixels);
pixels = createSurface(data, width, height);
SDL_SetColorKey(pixels, SDL_SRCCOLORKEY, SKEY);
return;
}
int Sprite::getWidth () {
return pixels->w;
}
int Sprite::getHeight() {
return pixels->h;
}
void Sprite::setPalette (SDL_Color *palette, int start, int amount) {
SDL_SetPalette(pixels, SDL_LOGPAL, palette + start, start, amount);
return;
}
void Sprite::flashPalette (int index) {
SDL_Color palette[256];
int count;
// Map the whole palette to one index
for (count = 0; count < 256; count++)
palette[count].r = palette[count].g = palette[count].b = index;
SDL_SetPalette(pixels, SDL_LOGPAL, palette, 0, 256);
return;
}
void Sprite::restorePalette () {
::restorePalette(pixels);
return;
}
void Sprite::draw (int x, int y) {
SDL_Rect dst;
dst.x = x + xOffset;
dst.y = y + yOffset;
SDL_BlitSurface(pixels, NULL, screen, &dst);
return;
}
/*
*
* sprite.h
*
* 19th March 2009: Created sprite.h from parts of level.h
* 26th July 2009: Created anim.h from parts of sprite.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _SPRITE_H
#define _SPRITE_H
#include <SDL/SDL.h>
// Constant
#define SKEY 254 /* Sprite colour key */
// Class
class Sprite {
private:
SDL_Surface *pixels;
public:
unsigned char xOffset;
unsigned char yOffset;
Sprite ();
~Sprite ();
void clearPixels ();
void setPixels (unsigned char *data, int width, int height);
int getWidth ();
int getHeight ();
void draw (int x, int y);
void setPalette (SDL_Color *palette, int start, int amount);
void flashPalette (int index);
void restorePalette ();
};
#endif
/*
*
* video.cpp
*
* 13th July 2009: Created graphics.cpp from parts of util.cpp
* 26th July 2009: Renamed graphics.cpp to video.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Contains graphics utility functions.
*
*/
#include "video.h"
#include <string.h>
SDL_Surface * createSurface (unsigned char * pixels, int width, int height) {
SDL_Surface *ret;
int y;
// Create the surface
ret = SDL_CreateRGBSurface(SDL_HWSURFACE, width, height, 8, 0, 0, 0, 0);
// Set the surface's palette
SDL_SetPalette(ret, SDL_LOGPAL, logicalPalette, 0, 256);
// Upload pixel data to the surface
if (SDL_MUSTLOCK(ret)) SDL_LockSurface(ret);
for (y = 0; y < height; y++)
memcpy(((unsigned char *)(ret->pixels)) + (ret->pitch * y),
pixels + (width * y), width);
if (SDL_MUSTLOCK(ret)) SDL_UnlockSurface(ret);
// Free redundant pixel data
delete[] pixels;
return ret;
}
#ifndef FULLSCREEN_ONLY
void toggleFullscreen () {
fullscreen = !fullscreen;
if (fullscreen) {
SDL_ShowCursor(SDL_DISABLE);
screen = SDL_SetVideoMode(screenW, screenH, 8,
SDL_FULLSCREEN | SDL_DOUBLEBUF | SDL_HWSURFACE | SDL_HWPALETTE);
SDL_SetPalette(screen, SDL_LOGPAL, logicalPalette, 0, 256);
SDL_SetPalette(screen, SDL_PHYSPAL, currentPalette, 0, 256);
/* A real 8-bit display is quite likely if the user has the right video
card, the right video drivers, the right version of DirectX/whatever,
and the right version of SDL. In other words, it's not likely enough.
If a real palette is assumed when
a) there really is a real palette, there will be an extremely small
speed gain.
b) the palette is emulated, there will be a HUGE speed loss.
Therefore, assume the palette is emulated. */
// TODO: Find a better way
fakePalette = true;
} else {
screen = SDL_SetVideoMode(screenW, screenH, 8,
SDL_RESIZABLE | SDL_DOUBLEBUF | SDL_HWSURFACE | SDL_HWPALETTE);
SDL_SetPalette(screen, SDL_LOGPAL, logicalPalette, 0, 256);
SDL_SetPalette(screen, SDL_PHYSPAL, currentPalette, 0, 256);
SDL_ShowCursor(SDL_ENABLE);
/* Assume that in windowed mode the palette is being emulated.
This is extremely likely. */
// TODO: Find a better way
fakePalette = true;
}
return;
}
#endif
void usePalette (SDL_Color *palette) {
// Make palette changes invisible until the next draw. Hopefully.
clearScreen(SDL_MapRGB(screen->format, 0, 0, 0));
SDL_Flip(screen);
SDL_SetPalette(screen, SDL_PHYSPAL, palette, 0, 256);
currentPalette = palette;
return;
}
void restorePalette (SDL_Surface *surface) {
SDL_SetPalette(surface, SDL_LOGPAL, logicalPalette, 0, 256);
return;
}
void clearScreen (int index) {
SDL_FillRect(screen, NULL, index);
return;
}
void drawRect (int x, int y, int width, int height, int index) {
SDL_Rect dst;
dst.x = x;
dst.y = y;
dst.w = width;
dst.h = height;
SDL_FillRect(screen, &dst, index);
return;
}
/*
*
* video.h
*
* 13th July 2009: Created graphics.h from parts of OpenJazz.h
* 26th July 2009: Renamed graphics.h to video.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _GRAPHICS_H
#define _GRAPHICS_H
#include "OpenJazz.h"
#include <SDL/SDL.h>
// Constants
// Black palette index
#define BLACK 31
// Variables
EXTERN SDL_Surface *screen;
EXTERN int viewW, viewH, screenW, screenH;
#ifndef FULLSCREEN_ONLY
EXTERN bool fullscreen;
#endif
EXTERN bool fakePalette;
EXTERN int mspf;
// Palettes
EXTERN SDL_Color *currentPalette;
EXTERN SDL_Color logicalPalette[256];
// Panel
EXTERN SDL_Surface *panel;
EXTERN SDL_Surface *panelAmmo[5];
// Functions
EXTERN SDL_Surface * createSurface (unsigned char *pixels, int width,
int height);
#ifndef FULLSCREEN_ONLY
EXTERN void toggleFullscreen ();
#endif
EXTERN void usePalette (SDL_Color *palette);
EXTERN void restorePalette (SDL_Surface *surface);
EXTERN void clearScreen (int index);
EXTERN void drawRect (int x, int y, int width, int height,
int index);
#endif
/*
*
* network.cpp
*
* 3rd June 2009: Created network.cpp from parts of game.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "controls.h"
#include "gfx/font.h"
#include "gfx/video.h"
#include "network.h"
#ifdef WIN32
#include <winsock.h>
#define ioctl ioctlsocket
#define socklen_t int
#define EWOULDBLOCK WSAEWOULDBLOCK
#define MSG_NOSIGNAL 0
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#endif
Network::Network () {
#ifdef WIN32
WSADATA WSAData;
// Start Windows Sockets
WSAStartup(MAKEWORD(1, 0), &WSAData);
#endif
return;
}
Network::~Network () {
#ifdef WIN32
// Shut down Windows Sockets
WSACleanup();
#endif
return;
}
int Network::host () {
sockaddr_in sockAddr;
int sock, nonblock;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) return E_N_SOCKET;
// Make the socket non-blocking
nonblock = 1;
ioctl(sock, FIONBIO, (u_long *)&nonblock);
memset(&sockAddr, 0, sizeof(sockaddr_in));
sockAddr.sin_family = AF_INET;
sockAddr.sin_addr.s_addr = INADDR_ANY;
sockAddr.sin_port = htons(NET_PORT);
if (bind(sock, (sockaddr *)&sockAddr, sizeof(sockaddr_in))) {
close(sock);
return E_N_BIND;
}
if (listen(sock, MAX_CLIENTS) == -1) {
close(sock);
return E_N_LISTEN;
}
return sock;
}
int Network::join (char *address) {
sockaddr_in sockAddr;
fd_set writefds;
timeval timeouttv;
unsigned int timeout;
int sock, con;
// Create socket
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) return E_N_SOCKET;
// Make socket non-blocking
con = 1;
ioctl(sock, FIONBIO, (u_long *)&con);
// Connect to server
memset(&sockAddr, 0, sizeof(sockaddr_in));
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons(NET_PORT);
#ifdef WIN32
sockAddr.sin_addr.s_addr = inet_addr(address);
#else
if (inet_aton(address, &(sockAddr.sin_addr)) == 0) return E_N_ADDRESS;
#endif
// Initiate connection
con = connect(sock, (sockaddr *)&sockAddr, sizeof(sockAddr));
// If the connection completed, return
if (con == 0) return sock;
// Wait for connection to complete
con = 0;
timeout = globalTicks + T_TIMEOUT;
while (!con) {
if (loop(NORMAL_LOOP) == E_QUIT) {
close(sock);
return E_QUIT;
}
if (controls.release(C_ESCAPE)) {
close(sock);
return E_UNUSED;
}
clearScreen(0);
fontmn2->showString("CONNECTING TO SERVER", screenW >> 2,
(screenH >> 1) - 16);
FD_ZERO(&writefds);
FD_SET(sock, &writefds);
timeouttv.tv_sec = 0;
timeouttv.tv_usec = T_FRAME;
con = select(sock + 1, NULL, &writefds, NULL, &timeouttv);
if (con == -1) {
log("Could not connect to server - code", getError());
close(sock);
return E_N_CONNECT;
}
if (globalTicks > timeout) {
close(sock);
return E_TIMEOUT;
}
}
return sock;
}
int Network::accept (int sock) {
sockaddr_in sockAddr;
int clientSocket, length;
length = sizeof(sockaddr_in);
clientSocket = ::accept(sock, (sockaddr *)&sockAddr, (socklen_t *)&length);
if (clientSocket != -1) {
// Make the socket non-blocking
length = 1;
ioctl(clientSocket, FIONBIO, (u_long *)&length);
}
return clientSocket;
}
void Network::close (int sock) {
#ifdef WIN32
closesocket(sock);
#else
::close(sock);
#endif
return;
}
int Network::send (int sock, unsigned char *buffer) {
return ::send(sock, (char *)buffer, buffer[0], MSG_NOSIGNAL);
}
int Network::recv (int sock, unsigned char *buffer, int length) {
return ::recv(sock, (char *)buffer, length, MSG_NOSIGNAL);
}
bool Network::isConnected (int sock) {
int length;
char buffer;
// Check for incoming data
length = ::recv(sock, &buffer, 1, MSG_PEEK | MSG_NOSIGNAL);
// Still connected if data was received or if there was no data to receive
return (length != -1) || (getError() == EWOULDBLOCK);
}
int Network::getError () {
#ifdef WIN32
return WSAGetLastError();
#else
return errno;
#endif
}
/*
*
* network.h
*
* 3rd June 2009: Created network.h from parts of OpenJazz.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _NETWORK_H
#define _NETWORK_H
#include "OpenJazz.h"
// Constants
// Defaults
#define NET_ADDRESS "192.168.0.1"
#define NET_PORT 10052
// Timeout interval
#define T_TIMEOUT 30000
// Client limit
#define MAX_CLIENTS 31
// Class
class Network {
public:
Network ();
~Network ();
int host ();
int join (char *address);
int accept (int sock);
void close (int sock);
int send (int sock, unsigned char *buffer);
int recv (int sock, unsigned char *buffer, int length);
bool isConnected (int sock);
int getError ();
};
// Variables
EXTERN char *netAddress;
EXTERN Network *net;
#endif
/*
*
* sound.cpp
*
* 23rd August 2005: Created sound.c
* 3rd February 2009: Renamed sound.c to sound.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Deals with the loading, playing and freeing of music and sound effects.
*
* For music, USE_MODPLUG must be defined.
*
*/
#include "file.h"
#include "sound.h"
#include <SDL/SDL_audio.h>
#ifdef USE_MODPLUG
#include <modplug.h>
ModPlugFile *musicFile;
#endif
SDL_AudioSpec audioSpec;
void audioCallback (void * userdata, unsigned char * stream, int len) {
int count;
#ifdef USE_MODPLUG
// Read the next portion of music into the audio stream
if (musicFile) ModPlug_Read(musicFile, stream, len);
#endif
for (count = 0; count < nSounds; count++) {
if (sounds[count].position >= 0) {
// Add the next portion of the sound clip to the audio stream
if (len < sounds[count].length - sounds[count].position) {
// Play as much of the clip as possible
SDL_MixAudio(stream,
sounds[count].data + sounds[count].position, len,
SDL_MIX_MAXVOLUME >> 1);
sounds[count].position += len;
} else {
// Play the remainder of the clip
SDL_MixAudio(stream,
sounds[count].data + sounds[count].position,
sounds[count].length - sounds[count].position,
SDL_MIX_MAXVOLUME >> 1);
sounds[count].position = -1;
}
}
}
return;
}
void openAudio () {
SDL_AudioSpec asDesired;
#ifdef USE_MODPLUG
musicFile = NULL;
#endif
// Set up SDL audio
asDesired.freq = 44100;
asDesired.format = AUDIO_S16;
asDesired.channels = 2;
asDesired.samples = 2048;
asDesired.callback = audioCallback;
asDesired.userdata = NULL;
if (SDL_OpenAudio(&asDesired, &audioSpec) < 0)
logError("Unable to open audio", SDL_GetError());
// Load sounds
if (loadSounds("sounds.000") != E_NONE) sounds = NULL;
return;
}
void closeAudio () {
stopMusic();
SDL_CloseAudio();
freeSounds();
return;
}
void playMusic (char * fileName) {
#ifdef USE_MODPLUG
File *file;
ModPlug_Settings settings;
unsigned char *psmData;
int size;
// Stop any existing music playing
stopMusic();
// Load the music file
try {
file = new File(fileName, false);
} catch (int e) {
return;
}
// Find the size of the file
size = file->getSize();
// Read the entire file into memory
file->seek(0, true);
psmData = file->loadBlock(size);
delete file;
// Set up libmodplug
settings.mFlags = MODPLUG_ENABLE_NOISE_REDUCTION | MODPLUG_ENABLE_REVERB |
MODPLUG_ENABLE_MEGABASS | MODPLUG_ENABLE_SURROUND;
settings.mChannels = audioSpec.channels;
if ((audioSpec.format == AUDIO_U8) || (audioSpec.format == AUDIO_S8))
settings.mBits = 8;
else settings.mBits = 16;
settings.mFrequency = audioSpec.freq;
settings.mResamplingMode = MODPLUG_RESAMPLE_FIR;
settings.mReverbDepth = 25;
settings.mReverbDelay = 40;
settings.mBassAmount = 50;
settings.mBassRange = 10;
settings.mSurroundDepth = 50;
settings.mSurroundDelay = 40;
settings.mLoopCount = -1;
ModPlug_SetSettings(&settings);
// Load the file into libmodplug
musicFile = ModPlug_Load(psmData, size);
delete[] psmData;
if (!musicFile) {
logError("Could not play music file", fileName);
return;
}
// Start the music playing
SDL_PauseAudio(0);
#endif
return;
}
void stopMusic () {
#ifdef USE_MODPLUG
// Stop the music playing
SDL_PauseAudio(~0);
if (musicFile) {
ModPlug_Unload(musicFile);
musicFile = NULL;
}
#endif
return;
}
int loadSounds (char *fileName) {
File *file;
unsigned char *clip;
int count, sampleCount, rsFactor, offset, headerOffset;
try {
file = new File(fileName, false);
} catch (int e) {
return e;
}
// Calculate the resampling factor
if ((audioSpec.format == AUDIO_U8) || (audioSpec.format == AUDIO_S8))
rsFactor = audioSpec.freq / 5512;
else rsFactor = (audioSpec.freq / 5512) * 2;
// Locate the header data
file->seek(file->getSize() - 4, true);
headerOffset = file->loadInt();
// Calculate number of sounds
nSounds = (file->getSize() - headerOffset) / 18;
// Load sound clips
sounds = new Sound[nSounds];
for(count = 0; count < nSounds; count++) {
file->seek(headerOffset + (count * 18), true);
// Read the name of the clip
sounds[count].name = (char *)(file->loadBlock(12));
// Read the offset of the clip
offset = file->loadInt();
// Read the length of the clip
sounds[count].length = file->loadShort();
// Read the clip
file->seek(offset, true);
clip = file->loadBlock(sounds[count].length);
sounds[count].length *= rsFactor;
// Resample the clip
sounds[count].data = new unsigned char[sounds[count].length];
for (sampleCount = 0; sampleCount < sounds[count].length; sampleCount++)
sounds[count].data[sampleCount] = clip[sampleCount / rsFactor];
delete[] clip;
sounds[count].position = -1;
}
delete file;
return E_NONE;
}
void freeSounds () {
int count;
if (sounds != NULL) {
for (count = 0; count < nSounds; count++) {
delete[] sounds[count].data;
delete[] sounds[count].name;
}
delete[] sounds;
}
return;
}
void playSound (int newSound) {
// Set the sound to be played
if ((sounds != NULL) && (newSound >= 0)) sounds[newSound].position = 0;
return;
}
/*
*
* sound.h
*
* 2nd June 2009: Created sound.h from parts of OpenJazz.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _SOUND_H
#define _SOUND_H
#include "OpenJazz.h"
// Constants
// Sound effects
#define S_INVULN 0
#define S_MACHGUN 1
#define S_BOOM 2
#define S_OW 3
#define S_YUM 4
#define S_FIRE 5
#define S_UPLOOP 6
#define S_1UP 7
#define S_PHOTON 8
#define S_WAIT 9
#define S_ORB 10
#define S_JUMPA 11
#define S_GODLIKE 12
#define S_YEAHOO 13
#define S_BIRDY 14
#define S_FLAMER 15
#define S_ELECTR 16
#define S_SPRING 17
#define S_ROCKET 18
#define S_STOP 19
#define S_BLOCK 20
// Datatype
typedef struct {
unsigned char *data;
char *name;
int length;
int position;
} Sound;
// Variables
EXTERN Sound *sounds;
EXTERN int nSounds;
// Functions
EXTERN void openAudio ();
EXTERN void closeAudio ();
EXTERN void playMusic (char *fileName);
EXTERN void stopMusic ();
EXTERN int loadSounds (char *fileName);
EXTERN void freeSounds ();
EXTERN void playSound (int sound);
#endif
/*
*
* bullet.cpp
*
* 11th February 2009: Created bullet.cpp from parts of events.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "bullet.h"
#include "event/event.h"
#include "level.h"
#include "game/game.h"
#include "io/gfx/paletteeffects.h"
#include "io/gfx/sprite.h"
#include "io/gfx/video.h"
#include "player/bird.h"
#include "player/player.h"
Bullet::Bullet (Player *sourcePlayer, bool lower, int ticks) {
// Properties based on the player
source = sourcePlayer;
type = source->getAmmo(false) + 1;
if (!lower && (level->getBullet(type)[B_XSPEED | 2] != 0)) {
// Create the other bullet
next = new Bullet(source, true, ticks);
} else {
next = level->firstBullet;
}
direction = source->getFacing()? 1: 0;
direction |= lower? 2: 0;
x = source->getX() + (source->getFacing()? PXO_R: PXO_L);
y = source->getY() - F8;
if (type == 4) {
// TNT
type = -1;
dy = 0;
time = ticks + T_TNT;
// Red flash
firstPE = new FlashPaletteEffect(255, 0, 0, T_TNT, firstPE);
} else {
dy = level->getBullet(type)[B_YSPEED + direction] * 250 * F1;
time = ticks + T_BULLET;
level->playSound(level->getBullet(type)[B_STARTSOUND]);
}
return;
}
Bullet::Bullet (Event *sourceEvent, bool facing, int ticks) {
// Properties based on the event
next = level->firstBullet;
source = NULL;
type = sourceEvent->getProperty(E_BULLET);
direction = facing? 1: 0;
x = sourceEvent->getX() + (sourceEvent->getWidth() >> 1);
y = sourceEvent->getY() + (sourceEvent->getHeight() >> 1);
dy = level->getBullet(type)[B_YSPEED + direction] * 250 * F1;
time = ticks + T_BULLET;
level->playSound(level->getBullet(type)[B_STARTSOUND]);
return;
}
Bullet::Bullet (Bird *sourceBird, bool lower, int ticks) {
// Properties based on the bird and its player
source = sourceBird->getPlayer();
if (!lower) {
// Create the other bullet
next = new Bullet(sourceBird, true, ticks);
} else {
next = level->firstBullet;
}
type = 30;
direction = source->getFacing()? 1: 0;
direction |= lower? 2: 0;
x = sourceBird->getX() + (source->getFacing()? PXO_R: PXO_L);
y = sourceBird->getY();
dy = level->getBullet(type)[B_YSPEED + direction] * 250 * F1;
time = ticks + T_BULLET;
level->playSound(level->getBullet(type)[B_STARTSOUND]);
return;
}
Bullet::~Bullet () {
return;
}
Bullet * Bullet::getNext () {
return next;
}
void Bullet::removeNext () {
Bullet *newNext;
if (next) {
newNext = next->getNext();
delete next;
next = newNext;
}
return;
}
Player * Bullet::getSource () {
return source;
}
bool Bullet::playFrame (int ticks) {
signed char *set;
Event *event;
int count;
// Process the next bullet
if (next) {
if (next->playFrame(ticks)) removeNext();
}
if (level->getStage() != LS_END) {
// If the time has expired, destroy the bullet
if (ticks > time) {
// If the bullet is TNT, destroy all destructible events nearby
if (type == -1) {
event = level->firstEvent;
while (event) {
// If the event is within range, hit it
if (event->overlap(x - F160, y - F100, 2 * F160, 2 * F100))
event->hit(source, ticks);
event = event->getNext();
}
}
// Destroy the bullet
return true;
}
// If this is TNT, don't need to do anything else
if (type == -1) return false;
// Check if a player has been hit
for (count = 0; count < nPlayers; count++) {
if (players[count].overlap(x, y, F1, F1)) {
// If the hit was successful, destroy the bullet
if (players[count].hit(source, ticks)) return true;
}
}
if (source) {
// Check if an event has been hit
event = level->firstEvent;
while (event) {
// Check if the event has been hit
if (event->overlap(x, y, 0, 0)) {
// If the event is hittable, hit it and destroy the bullet
if (event->hit(source, ticks)) return true;
}
event = event->getNext();
}
}
}
set = level->getBullet(type);
// If the scenery has been hit and this is not a bouncer, destroy the bullet
if (level->checkMask(x, y) && (set[B_BEHAVIOUR] != 4)) {
level->playSound(set[B_FINISHSOUND]);
return true;
}
// Calculate trajectory
if (set[B_BEHAVIOUR] == 4) {
// Bounce the bullet
if (level->checkMaskDown(x, y - F4) && (dy < 0)) dy = 0;
else if (level->checkMaskDown(x, y + F4)) dy = -600 * F1;
else if (level->checkMaskDown(x - F4, y)) direction |= 1;
else if (level->checkMaskDown(x + F4, y)) direction &= ~1;
else dy += 6400 * mspf * set[B_GRAVITY];
} else dy += 6400 * mspf * set[B_GRAVITY];
// Apply trajectory
x += (set[B_XSPEED + direction] * 500 * F1 * mspf) >> 10;
y += (dy * mspf) >> 10;
// Do not destroy the bullet
return false;
}
void Bullet::draw () {
Sprite *sprite;
if (next) next->draw();
if (type == -1) sprite = level->getSprite(130);
else sprite =
level->getSprite(((unsigned char *)level->getBullet(type))
[B_SPRITE + direction]);
// Show the bullet
sprite->draw(FTOI(x) - (sprite->getWidth() >> 1) - FTOI(viewX),
FTOI(y) - (sprite->getWidth() >> 1) - FTOI(viewY));
return;
}
/*
*
* bullet.h
*
* 11th February 2009: Created bullet.h from parts of events.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _BULLET_H
#define _BULLET_H
#include "OpenJazz.h"
// Constants
// Indexes for elements of the bullet set
#define B_SPRITE 0
#define B_XSPEED 4
#define B_YSPEED 8
#define B_GRAVITY 12
#define B_FINISHANIM 16
#define B_FINISHSOUND 17
#define B_BEHAVIOUR 18
#define B_BEHAVIOR 18
#define B_STARTSOUND 19
// Survival time
#define T_BULLET 1000
#define T_TNT 300
// Classes
class Bird;
class Event;
class Player;
class Bullet {
private:
Bullet *next;
Player *source; // If NULL, was fired by an event
int type; // -1 is TNT, otherwise this indexes the bullet set
int direction; // 0: Left, 1: Right, 2: L (lower), 3: R (lower)
fixed x, y, dy;
int time; // The time at which the bullet will self-destruct
public:
Bullet (Player *sourcePlayer, bool lower, int ticks);
Bullet (Event *sourceEvent, bool facing, int ticks);
Bullet (Bird *sourceBird, bool lower, int ticks);
~Bullet ();
Bullet * getNext ();
void removeNext ();
Player * getSource ();
bool playFrame (int ticks);
void draw ();
};
#endif
/*
*
* demolevel.cpp
*
* 18th July 2009: Created demolevel.cpp from parts of level.cpp and
* levelload.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Deals with the loading and playing of demo levels.
*
*/
#include "level.h"
#include "game/game.h"
#include "game/gamemode.h"
#include "io/controls.h"
#include "io/file.h"
#include "io/gfx/font.h"
#include "io/gfx/video.h"
#include "player/player.h"
DemoLevel::DemoLevel (char *fileName) {
File *file;
char *levelFile;
int lNum, wNum, diff, ret;
gameMode = NULL;
try {
file = new File(fileName, false);
} catch (int e) {
throw e;
}
// Check this is a normal level
if (file->loadShort() == 0) throw E_DEMOTYPE;
// Level file to load
lNum = file->loadShort();
wNum = file->loadShort();
levelFile = createFileName(F_LEVEL, lNum, wNum);
// Difficulty
diff = file->loadShort();
macro = file->loadBlock(1024);
// Load level data
ret = load(levelFile, diff, false);
delete[] levelFile;
if (ret < 0) throw ret;
return;
}
DemoLevel::~DemoLevel () {
delete[] macro;
return;
}
int DemoLevel::play () {
int stats;
unsigned char macroPoint;
int ret;
// Arbitrary initial value
smoothfps = 50.0f;
tickOffset = globalTicks;
ticks = -10;
stats = S_NONE;
while (true) {
// Do general processing
if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT;
if (controls.release(C_ESCAPE)) return E_NONE;
if (controls.release(C_STATS)) stats ^= S_SCREEN;
timeCalcs(false);
// Use macro
macroPoint = macro[(ticks / 90) % 1024];
if (macroPoint & 128) return E_NONE;
if (macroPoint & 1) {
localPlayer->setControl(C_LEFT, false);
localPlayer->setControl(C_RIGHT, false);
localPlayer->setControl(C_UP, !(macroPoint & 4));
} else {
localPlayer->setControl(C_LEFT, !(macroPoint & 2));
localPlayer->setControl(C_RIGHT, macroPoint & 2);
localPlayer->setControl(C_UP, false);
}
localPlayer->setControl(C_DOWN, macroPoint & 8);
localPlayer->setControl(C_FIRE, macroPoint & 16);
localPlayer->setControl(C_CHANGE, macroPoint & 32);
localPlayer->setControl(C_JUMP, macroPoint & 64);
// Check if level has been won
if (getStage() == LS_END) return WON;
// Process frame-by-frame activity
ret = playFrame();
if (ret < 0) return ret;
// Handle player reactions
if (localPlayer->reacted(ticks) == PR_KILLED) return LOST;
// Draw the graphics
draw();
fontmn1->showString("DEMO", (screenW >> 1) - 36, 32);
// Draw graphics statistics
if (stats & S_SCREEN) {
drawRect(236, 9, 80, 32, BLACK);
panelBigFont->showNumber(screenW, 268, 15);
panelBigFont->showString("x", 272, 15);
panelBigFont->showNumber(screenH, 308, 15);
panelBigFont->showString("fps", 244, 27);
panelBigFont->showNumber((int)smoothfps, 308, 27);
}
}
return E_NONE;
}
/*
*
* event.cpp
*
* 1st January 2006: Created events.c from parts of level.c
* 3rd February 2009: Renamed events.c to events.cpp
* 5th February 2009: Added parts of events.cpp and level.cpp to player.cpp
* 11th February 2009: Created bullet.cpp from parts of events.cpp
* 1st March 2009: Created bird.cpp from parts of events.cpp
* 19th March 2009: Created sprite.cpp from parts of event.cpp and player.cpp
* 19th July 2009: Created eventframe.cpp from parts of events.cpp
* 19th July 2009: Renamed events.cpp to event.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Deals with events in ordinary levels.
*
*/
#include "../level.h"
#include "event.h"
#include "io/sound.h"
#include "player/player.h"
#include <math.h>
Event::Event (unsigned char gX, unsigned char gY, Event *nextEvent) {
next = nextEvent;
gridX = gX;
gridY = gY;
x = TTOF(gX);
y = TTOF(gY + 1);
flashTime = 0;
// Choose initial animation and direction
switch (getProperty(E_BEHAVIOUR)) {
case 21: // Destructible block
case 25: // Float up / Belt
case 38: // Sucker tubes
case 40: // Monochrome
case 57: // Bubbles
animType = 0;
break;
case 26: // Flip animation
animType = E_RIGHTANIM;
break;
default:
animType = E_LEFTANIM;
break;
}
return;
}
Event::~Event () {
return;
}
Event * Event::getNext () {
return next;
}
void Event::removeNext () {
Event *newNext;
if (next) {
newNext = next->getNext();
delete next;
next = newNext;
}
return;
}
void Event::destroy (int ticks) {
level->setEventTime(gridX, gridY, ticks + T_FINISH);
animType = ((animType == E_RIGHTANIM) || (animType == E_RSHOOTANIM)) ?
E_RFINISHANIM: E_LFINISHANIM;
level->playSound(getProperty(E_SOUND));
return;
}
bool Event::hit (Player *source, int ticks) {
int hitsRemaining;
// Deal with bullet collisions
if ((animType == E_LFINISHANIM) || (animType == E_RFINISHANIM) ||
(ticks < flashTime)) return false;
hitsRemaining = level->hitEvent(source, gridX, gridY);
// If the event cannot be hit, do not register hit
if (hitsRemaining < 0) return false;
// Check if the hit has destroyed the event
if (hitsRemaining == 0) destroy(ticks);
// The event has been hit, so it should flash
flashTime = ticks + T_FLASH;
// Register hit
return true;
}
bool Event::isFrom (unsigned char gX, unsigned char gY) {
return (gX == gridX) && (gY == gridY);
}
fixed Event::getX () {
return x;
}
fixed Event::getY () {
return y - getHeight();
}
fixed Event::getWidth () {
fixed width;
if (animType && (getProperty(animType) >= 0)) {
width = ITOF(level->getAnim(getProperty(animType))->getWidth());
// Blank sprites for e.g. invisible springs
if ((width == F1) && (getHeight() == F1)) return F32;
return width;
}
return F32;
}
fixed Event::getHeight () {
if (animType && (getProperty(animType) >= 0))
return ITOF(level->getAnim(getProperty(animType))->getHeight());
return F32;
}
bool Event::overlap (fixed left, fixed top, fixed width, fixed height) {
return (x + getWidth() >= left) && (x < left + width) &&
(y >= top) && (y - getHeight() < top + height);
}
signed char Event::getProperty (unsigned char property) {
signed char *set;
set = level->getEvent(gridX, gridY);
if (set) return set[property];
return 0;
}
/*
*
* event.h
*
* 4th February 2009: Created events.h from parts of level.h
* 11th February 2009: Created bullet.h from parts of events.h
* 1st March 2009: Created bird.h from parts of events.h
* 19th July 2009: Renamed events.h to event.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _EVENTS_H
#define _EVENTS_H
#include "OpenJazz.h"
// 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
// Delays
#define T_FLASH 100
#define T_FINISH 200
// Speed factors
#define ES_SLOW 80
#define ES_FAST 240
// Classes
class Player;
class Event {
private:
Event *next;
unsigned char gridX, gridY; // Grid position of the event
fixed x, y; // Actual position of the event
unsigned char animType; // E_LEFTANIM, etc, or 0
unsigned char frame;
int flashTime;
void destroy (int ticks);
public:
Event (unsigned char gX, unsigned char gY,
Event *nextEvent);
~Event ();
Event * getNext ();
void removeNext ();
bool hit (Player *source, int ticks);
bool isFrom (unsigned char gX, unsigned char gY);
fixed getX ();
fixed getY ();
fixed getWidth ();
fixed getHeight ();
bool overlap (fixed left, fixed top, fixed width,
fixed height);
signed char getProperty (unsigned char property);
bool playFrame (int ticks);
void draw (int ticks);
};
#endif
/*
*
* eventframe.cpp
*
* 19th July 2009: Created eventframe.cpp from parts of events.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Provides the once-per-frame functions of events.
*
*/
#include "../bullet.h"
#include "../level.h"
#include "event.h"
#include "io/gfx/video.h"
#include "io/sound.h"
#include "player/player.h"
#include <math.h>
bool Event::playFrame (int ticks) {
fixed width, height;
signed char *set;
fixed startX;
int count, offset;
float angle;
// Process the next event
if (next) {
if (next->playFrame(ticks)) removeNext();
}
// Get the event properties
set = level->getEvent(gridX, gridY);
// If the event has been removed from the grid, destroy it
if (!set) return true;
// 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 - F160) || (x > viewX + ITOF(viewW) + F160) ||
(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 true;
// Find frame
if (animType && (set[animType] >= 0)) {
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 / 40;
}
// Find dimensions
width = getWidth();
height = getHeight();
// Pre-movement platform position
startX = x;
// Handle behaviour
switch (set[E_BEHAVIOUR]) {
case 1:
// Sink down
y += ES_FAST * mspf / set[E_MOVEMENTSP];
break;
case 2:
// Walk from side to side
if (animType == E_LEFTANIM) x -= ES_FAST * mspf / set[E_MOVEMENTSP];
else if (animType == E_RIGHTANIM)
x += ES_FAST * mspf / set[E_MOVEMENTSP];
break;
case 3:
// Seek jazz
if (localPlayer->getX() + PXO_R < x)
x -= ES_FAST * mspf / set[E_MOVEMENTSP];
else if (localPlayer->getX() + PXO_L > x + width)
x += ES_FAST * mspf / set[E_MOVEMENTSP];
break;
case 4:
// Walk from side to side and down hills
if (!level->checkMaskDown(x + (width >> 1), y)) {
// Fall downwards
y += ES_FAST * mspf / set[E_MOVEMENTSP];
} else {
// Walk from side to side
if (animType == E_LEFTANIM)
x -= ES_FAST * mspf / set[E_MOVEMENTSP];
else if (animType == E_RIGHTANIM)
x += ES_FAST * mspf / set[E_MOVEMENTSP];
}
break;
case 5:
// TODO: Find out what this is
break;
case 6:
// Use the path from the level file
x = TTOF(gridX) + F16 + (level->pathX[level->pathNode] << 9);
y = TTOF(gridY) + (level->pathY[level->pathNode] << 9);
break;
case 7:
// Move back and forth horizontally with tail
if (animType == E_LEFTANIM) x -= ES_SLOW * mspf / set[E_MOVEMENTSP];
else if (animType == E_RIGHTANIM)
x += ES_SLOW * mspf / set[E_MOVEMENTSP];
break;
case 8:
// TODO: Bird-esque following
break;
case 9:
// TODO: Find out what this is
break;
case 10:
// TODO: Find out what this is
break;
case 11:
// Sink to ground
if (!level->checkMaskDown(x + (width >> 1), y))
y += ES_FAST * mspf / set[E_MOVEMENTSP];
break;
case 12:
// Move back and forth horizontally
if (animType == E_LEFTANIM) x -= ES_SLOW * mspf / set[E_MOVEMENTSP];
else if (animType == E_RIGHTANIM)
x += ES_SLOW * mspf / set[E_MOVEMENTSP];
break;
case 13:
// Move up and down
if (animType == E_LEFTANIM) y -= ES_SLOW * mspf / set[E_MOVEMENTSP];
else if (animType == E_RIGHTANIM)
y += ES_SLOW * mspf / set[E_MOVEMENTSP];
break;
case 14:
// TODO: Move back and forth rapidly
break;
case 15:
// TODO: Rise or lower to meet jazz
break;
case 16:
// Move across level to the left or right
x += set[E_MAGNITUDE] * ES_SLOW * mspf / set[E_MOVEMENTSP];
break;
case 17:
// TODO: Find out what this is
break;
case 18:
// TODO: Find out what this is
break;
case 19:
// TODO: Find out what this is
break;
case 20:
// TODO: Find out what this is
break;
case 21:
// Destructible block
if (level->getEventHits(gridX, gridY) >= set[E_HITSTOKILL])
level->setTile(gridX, gridY, set[E_MULTIPURPOSE]);
break;
case 22:
// TODO: Fall down in random spot and repeat
break;
case 23:
// TODO: Find out what this is
break;
case 24:
// TODO: Crawl along ground and go downstairs
break;
case 25:
// Float up / Belt
for (count = 0; count < nPlayers; count++) {
if (players[count].overlap(x, y - height, width, height)) {
if (set[E_YAXIS]) players[count].floatUp(set);
players[count].belt(set[E_MAGNITUDE]);
}
}
break;
case 26:
// TODO: Find out what this is
break;
case 27:
// TODO: Face jazz
break;
case 28:
// Bridge
if (localPlayer->overlap(x - F10,
y + ITOF(set[E_YAXIS]) - F32, set[E_MULTIPURPOSE] * F8,
F8 + F20)) {
if (!level->checkMaskDown(localPlayer->getX() + PXO_MID,
y + ITOF(set[E_YAXIS]) - F32 - F20)) {
// Player is on the bridge
localPlayer->setEvent(set);
localPlayer->setPosition(localPlayer->getX(),
y + ITOF(set[E_YAXIS]) - F32);
offset = (localPlayer->getX() + PXO_MID - x) >> 13;
if (offset < set[E_MULTIPURPOSE] >> 1) {
// Player is to the left of the centre of the bridge
y = TTOF(gridY) + F24 + ITOF(offset);
animType = E_LEFTANIM;
} else {
// Player is to the right of the centre of the bridge
y = TTOF(gridY) + F24 +
ITOF(set[E_MULTIPURPOSE] - offset);
animType = E_RIGHTANIM;
}
} else localPlayer->clearEvent(set, E_BEHAVIOUR);
} else {
// Player is not on the bridge
// Gradually stop the bridge sagging
if (y - F24 > TTOF(gridY)) y -= 16 * mspf;
}
break;
case 29:
// Rotate
// TODO: Verify the direction of rotation
offset = ITOF(set[E_BRIDGELENGTH] * set[E_CHAINLENGTH]);
angle = set[E_MAGNITUDE] * ticks / 2048.0f;
x = TTOF(gridX) + (int)(sin(angle) * offset);
y = TTOF(gridY) + (int)(cos(angle) * offset);
break;
case 30:
// Swing
offset = ITOF(set[E_BRIDGELENGTH] * set[E_CHAINLENGTH]);
angle = (set[E_CHAINANGLE] * 3.141592f / 128.0f) +
(set[E_MAGNITUDE] * ticks / 2048.0f);
x = TTOF(gridX) + (int)(sin(angle) * offset);
y = TTOF(gridY) + (int)fabs(cos(angle) * offset);
break;
case 31:
// Move horizontally
if (animType == E_LEFTANIM) x -= ES_FAST * mspf / set[E_MOVEMENTSP];
else x += ES_FAST * mspf / set[E_MOVEMENTSP];
break;
case 32:
// Move horizontally
if (animType == E_LEFTANIM) x -= ES_FAST * mspf / set[E_MOVEMENTSP];
else x += ES_FAST * mspf / set[E_MOVEMENTSP];
break;
case 33:
// Sparks-esque following
if (localPlayer->getFacing() && (x + width < localPlayer->getX())) {
x += ES_FAST * mspf / set[E_MOVEMENTSP];
if (y + height < localPlayer->getY() + PYO_TOP)
y += ES_SLOW * mspf / set[E_MOVEMENTSP];
else if (y > localPlayer->getY())
y -= ES_SLOW * mspf / set[E_MOVEMENTSP];
} else if (!localPlayer->getFacing() &&
(x > localPlayer->getX() + F32)) {
x -= ES_FAST * mspf / set[E_MOVEMENTSP];
if (y + height < localPlayer->getY() + PYO_TOP)
y += ES_SLOW * mspf / set[E_MOVEMENTSP];
else if (y > localPlayer->getY())
y -= ES_SLOW * mspf / set[E_MOVEMENTSP];
}
break;
case 34:
// Launching platform
if (ticks > level->getEventTime(gridX, gridY)) {
if (y <= TTOF(gridY) + F16 - (set[E_YAXIS] * F2))
level->setEventTime(gridX, gridY,
ticks + (set[E_MODIFIER] * 500));
else
y -= (y + (set[E_YAXIS] * F2) - TTOF(gridY)) * mspf / 320;
} else {
if (y < TTOF(gridY) + F16)
y += (y + (set[E_YAXIS] * F2) - TTOF(gridY)) * mspf / 320;
else y = TTOF(gridY) + F16;
}
break;
case 35:
// TODO: Find out what this is
break;
case 36:
// Walk from side to side and down hills, staying on-screen
if (!level->checkMaskDown(x + (width >> 1), y)) {
// Fall downwards
y += ES_FAST * mspf / set[E_MOVEMENTSP];
} else {
// Walk from side to side, staying on-screen
if (animType == E_LEFTANIM)
x -= ES_FAST * mspf / set[E_MOVEMENTSP];
else if (animType == E_RIGHTANIM)
x += ES_FAST * mspf / set[E_MOVEMENTSP];
}
break;
case 37:
// TODO: Find out what this is
break;
case 38:
// Sucker tubes
for (count = 0; count < nPlayers; count++) {
if (players[count].overlap(x + F8, y - height, width - F16,
height)) {
players[count].setSpeed(set[E_MAGNITUDE] * F40,
set[E_YAXIS]? set[E_MULTIPURPOSE] * -F20: 0);
}
}
break;
case 39:
// TODO: Collapsing floor
break;
case 40:
// TODO: Find out what this is
break;
case 41:
// TODO: Switch left & right anim periodically
break;
case 42:
// TODO: Find out what this is
break;
case 43:
// TODO: Find out what this is
break;
case 44:
// TODO: Leap to greet Jazz very quickly
break;
case 45:
// TODO: Find out what this is
break;
case 46:
// TODO: "Final" boss
break;
case 53:
// Dreempipes turtles
if (y > level->getWaterLevel(0)) {
if (animType == E_LEFTANIM)
x -= ES_SLOW * mspf / set[E_MOVEMENTSP];
else if (animType == E_RIGHTANIM)
x += ES_SLOW * mspf / set[E_MOVEMENTSP];
}
break;
default:
// TODO: Remaining event behaviours
break;
}
// Choose animation and direction
if ((animType == E_LEFTANIM) || (animType == E_RIGHTANIM)) {
switch (set[E_BEHAVIOUR]) {
case 2:
// Walk from side to side
if (animType == E_LEFTANIM) {
if (!level->checkMaskDown(x, y + F4) ||
level->checkMaskDown(x - F4, y - (height >> 1)))
animType = E_RIGHTANIM;
} else if (animType == E_RIGHTANIM) {
if (!level->checkMaskDown(x + width, y + F4) ||
level->checkMaskDown(x + width + F4, y - (height >> 1)))
animType = E_LEFTANIM;
}
break;
case 3:
// Seek jazz
if (localPlayer->getX() + PXO_R < x) animType = E_LEFTANIM;
else if (localPlayer->getX() + PXO_L > x + width)
animType = E_RIGHTANIM;
break;
case 4:
// Walk from side to side and down hills
if (level->checkMaskDown(x + (width >> 1), y)) {
// Walk from side to side
if (animType == E_LEFTANIM) {
if (level->checkMaskDown(x - F4,
y - (height >> 1) - F12)) animType = E_RIGHTANIM;
} else if (animType == E_RIGHTANIM) {
if (level->checkMaskDown(x + width + F4,
y - (height >> 1) - F12)) animType = E_LEFTANIM;
}
}
break;
case 6:
// Use the path from the level file
// Check movement direction
if ((level->pathNode < 3) || (level->pathX[level->pathNode] <=
level->pathX[level->pathNode - 3])) animType = E_LEFTANIM;
else animType = E_RIGHTANIM;
break;
case 7:
// Move back and forth horizontally with tail
if (animType == E_LEFTANIM) {
if (x < TTOF(gridX)) animType = E_RIGHTANIM;
} else if (animType == E_RIGHTANIM) {
if (x > TTOF(gridX) + F100) animType = E_LEFTANIM;
}
break;
case 12:
// Move back and forth horizontally
if (animType == E_LEFTANIM) {
if (level->checkMaskDown(x - F4, y - (height >> 1)))
animType = E_RIGHTANIM;
} else if (animType == E_RIGHTANIM) {
if (level->checkMaskDown(x + width + F4, y - (height >> 1)))
animType = E_LEFTANIM;
}
break;
case 13:
// Move up and down
if (animType == E_LEFTANIM) {
if (level->checkMaskDown(x + (width >> 1), y - height - F4))
animType = E_RIGHTANIM;
} else if (animType == E_RIGHTANIM) {
if (level->checkMaskDown(x + (width >> 1), y + F4))
animType = E_LEFTANIM;
}
break;
case 26:
// Flip animation
if (localPlayer->overlap(x, y - height, width, height))
animType = E_LEFTANIM;
else animType = E_RIGHTANIM;
break;
case 31:
// Moving platform
if (animType == E_LEFTANIM) {
if (level->checkMaskDown(x - F4, y - (height >> 1)))
animType = E_RIGHTANIM;
} else if (animType == E_RIGHTANIM) {
if (level->checkMaskDown(x + width + F4, y - (height >> 1)))
animType = E_LEFTANIM;
}
break;
case 32:
// Moving platform
if (x < TTOF(gridX) - (set[E_BRIDGELENGTH] << 14))
animType = E_RIGHTANIM;
else if (!animType ||
(x > TTOF(gridX + set[E_BRIDGELENGTH])))
animType = E_LEFTANIM;
break;
case 33:
// Sparks-esque following
if (localPlayer->getFacing() &&
(x + width < localPlayer->getX())) {
animType = E_RIGHTANIM;
} else if (!localPlayer->getFacing() &&
(x > localPlayer->getX() + F32)) {
animType = E_LEFTANIM;
}
break;
case 36:
// Walk from side to side and down hills, staying on-screen
if (level->checkMaskDown(x + (width >> 1), y)) {
// Walk from side to side, staying on-screen
if (animType == E_LEFTANIM) {
if (level->checkMaskDown(x - F4,
y - (height >> 1)) || (x - F4 < viewX))
animType = E_RIGHTANIM;
} else if (animType == E_RIGHTANIM) {
if (level->checkMaskDown(x + width + F4,
y - (height >> 1)) ||
(x + width + F4 > viewX + ITOF(viewW)))
animType = E_LEFTANIM;
}
}
break;
case 53:
// Dreempipes turtles
if (y > level->getWaterLevel(0)) {
if (animType == E_LEFTANIM) {
if (level->checkMaskDown(x - F4,
y - (height >> 1))) animType = E_RIGHTANIM;
} else if (animType == E_RIGHTANIM) {
if (level->checkMaskDown(x + width + F4,
y - (height >> 1))) animType = E_LEFTANIM;
} else animType = E_LEFTANIM;
}
break;
default:
if (localPlayer->getX() + PXO_MID < x + (width >> 1))
animType = E_LEFTANIM;
else animType = E_RIGHTANIM;
break;
}
}
// 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)) {
destroy(ticks);
}
// Generate bullet
if (set[E_BULLETSP]) {
if ((ticks % (set[E_BULLETSP] * 25) > (set[E_BULLETSP] * 25) - 200) &&
((animType == E_LEFTANIM) || (animType == E_RIGHTANIM))) {
if (animType == E_LEFTANIM) animType = E_LSHOOTANIM;
else animType = E_RSHOOTANIM;
}
if ((ticks % (set[E_BULLETSP] * 25) < (set[E_BULLETSP] * 25) - 200) &&
((animType == E_LSHOOTANIM) || (animType == E_RSHOOTANIM))) {
if (animType == E_RSHOOTANIM) {
level->firstBullet = new Bullet(this, true, ticks);
animType = E_RIGHTANIM;
} else {
level->firstBullet = new Bullet(this, false, ticks);
animType = E_LEFTANIM;
}
}
}
// If the reaction time has expired
if (level->getEventTime(gridX, gridY) &&
(ticks > level->getEventTime(gridX, gridY))) {
if ((animType == E_LFINISHANIM) || (animType == E_RFINISHANIM)) {
// The event has been destroyed, so remove it
level->clearEvent(gridX, gridY);
return true;
} else {
// Change the water level
if (set[E_MODIFIER] == 31) level->setWaterLevel(gridY);
level->setEventTime(gridX, gridY, 0);
}
}
if (level->getStage() == LS_END) return false;
// Handle contact with player
if ((animType != E_LFINISHANIM) && (animType != E_RFINISHANIM)) {
for (count = 0; count < nPlayers; count++) {
// Check if the player is touching the event
if (players[count].overlap(x, y - height, width, height)) {
if (set[E_MODIFIER] == 6) {
// Platform
if ((players[count].getY() <=
y + (PYS_FALL / mspf) - height)
&& !level->checkMaskDown(players[count].getX() +
PXO_MID, y - height - F20)) {
players[count].setEvent(set);
players[count].setPosition(players[count].getX() +
x - startX, y - height);
} else players[count].clearEvent(set, E_MODIFIER);
}
// If the player picks up the event, destroy it
if (players[count].touchEvent(gridX, gridY, ticks))
destroy(ticks);
}
}
}
return false;
}
void Event::draw (int ticks) {
Anim *anim;
signed char *set;
int count, midpoint;
fixed bridgex, bridgey, dsty;
// Uncomment the following to see the area of the event
/*drawRect(FTOI(getX() - viewX), FTOI(getY() - viewY), 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 || (set[animType] < 0)) return;
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;
anim = level->getAnim(set[animType]);
anim->setFrame(frame + gridX + gridY, true);
if (ticks < flashTime) anim->flashPalette(0);
// Draw the event
if (set[E_BEHAVIOUR] == 28) {
bridgex = x - F10;
dsty = y + ITOF(set[E_YAXIS]) - F32;
if (y - F24 > TTOF(gridY)) {
if (animType == E_LEFTANIM)
midpoint = FTOI(y - F24 - TTOF(gridY));
else midpoint = set[E_MULTIPURPOSE] - FTOI(y - F24 - TTOF(gridY));
} else midpoint = 0;
if (midpoint < set[E_MULTIPURPOSE] >> 1) {
for (count = 0; count < set[E_MULTIPURPOSE]; count++) {
bridgex += F8;
if (midpoint == 0) bridgey = dsty;
else if (count < midpoint)
bridgey = ((dsty - ITOF(midpoint)) *
(midpoint - count) / midpoint) +
(dsty * count / midpoint);
else
bridgey = ((dsty - ITOF(midpoint)) * (count - midpoint) /
(set[E_MULTIPURPOSE] - midpoint)) +
(dsty * (set[E_MULTIPURPOSE] - count) /
(set[E_MULTIPURPOSE] - midpoint));
anim->draw(bridgex, bridgey);
}
} else {
for (count = 0; count < set[E_MULTIPURPOSE]; count++) {
bridgex += F8;
if (midpoint == 0) bridgey = dsty;
else if (count < midpoint)
bridgey = ((dsty + ITOF(midpoint - set[E_MULTIPURPOSE])) *
(midpoint - count) / midpoint) +
(dsty * count / midpoint);
else
bridgey = ((dsty + ITOF(midpoint - set[E_MULTIPURPOSE])) *
(count - midpoint) /
(set[E_MULTIPURPOSE] - midpoint)) +
(dsty * (set[E_MULTIPURPOSE] - count) /
(set[E_MULTIPURPOSE] - midpoint));
anim->draw(bridgex, bridgey);
}
}
} else {
anim->draw(x, y);
}
if (ticks < flashTime) anim->restorePalette();
// If the event has been destroyed, draw an explosion
if (set[E_HITSTOKILL] &&
((animType == E_LFINISHANIM) || (animType == E_RFINISHANIM))) {
anim = level->getMiscAnim(2);
anim->setFrame(frame, false);
anim->draw(x, y);
}
if ((set[E_MODIFIER] == 8) && set[E_HITSTOKILL]) {
// Draw boss energy bar
count = 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(viewX + ITOF(viewW - 44), viewY + ITOF(count + 48));
if (ticks < flashTime) anim->restorePalette();
// Bar
drawRect(viewW - 40, count + 40, 12, 100 - count,
(ticks < flashTime)? 0: 32);
}
return;
}
/*
*
* level.cpp
*
* 23rd August 2005: Created level.c
* 1st January 2006: Created events.c from parts of level.c
* 22nd July 2008: Created levelload.c from parts of level.c
* 3rd February 2009: Renamed level.c to level.cpp
* 5th February 2009: Added parts of events.cpp and level.cpp to player.cpp
* 9th March 2009: Created game.cpp from parts of menu.cpp and level.cpp
* 18th July 2009: Created demolevel.cpp from parts of level.cpp and
* levelload.cpp
* 19th July 2009: Created levelframe.cpp from parts of level.cpp
* 19th July 2009: Added parts of levelload.cpp to level.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Deals with the creating, playing and freeing of levels.
*
*/
#include "bullet.h"
#include "event/event.h"
#include "level.h"
#include "game/game.h"
#include "game/gamemode.h"
#include "io/controls.h"
#include "io/file.h"
#include "io/gfx/font.h"
#include "io/gfx/paletteeffects.h"
#include "io/gfx/sprite.h"
#include "io/gfx/video.h"
#include "io/sound.h"
#include "menu/menu.h"
#include "player/player.h"
#include "scene.h"
#include <string.h>
Level::Level () {
// Do nothing
return;
}
Level::Level (char *fileName, unsigned char diff, bool checkpoint) {
int ret;
// Load level data
ret = load(fileName, diff, checkpoint);
if (ret < 0) throw ret;
return;
}
Level::~Level () {
// Free all data
stopMusic();
// Free the palette effects
if (firstPE) {
delete firstPE;
firstPE = NULL;
}
// Free events
if (firstEvent) {
while (firstEvent->getNext()) firstEvent->removeNext();
delete firstEvent;
firstEvent = NULL;
}
// Free bullets
if (firstBullet) {
while (firstBullet->getNext()) firstBullet->removeNext();
delete firstBullet;
firstBullet = NULL;
}
delete[] pathX;
delete[] pathY;
SDL_FreeSurface(tileSet);
delete[] spriteSet;
fontmn1->restorePalette();
delete[] sceneFile;
return;
}
bool Level::checkMask (fixed x, fixed y) {
// Anything off the edge of the map is solid
if ((x < 0) || (y < 0) || (x > TTOF(LW)) || (y > TTOF(LH)))
return true;
// Event 122 is one-way
if (grid[FTOT(y)][FTOT(x)].event == 122) return false;
// Check the mask in the tile in question
return mask[grid[FTOT(y)][FTOT(x)].tile][((y >> 9) & 56) + ((x >> 12) & 7)];
}
bool Level::checkMaskDown (fixed x, fixed y) {
// Anything off the edge of the map is solid
if ((x < 0) || (y < 0) || (x > TTOF(LW)) || (y > TTOF(LH)))
return true;
// Check the mask in the tile in question
return mask[grid[FTOT(y)][FTOT(x)].tile][((y >> 9) & 56) + ((x >> 12) & 7)];
}
bool Level::checkSpikes (fixed x, fixed y) {
// Anything off the edge of the map is not spikes
// Ignore the bottom, as it is deadly anyway
if ((x < 0) || (y < 0) || (x > TTOF(LW))) return false;
// Event 126 is spikes
if (grid[FTOT(y)][FTOT(x)].event != 126) return false;
// Check the mask in the tile in question
return mask[grid[FTOT(y)][FTOT(x)].tile][((y >> 9) & 56) + ((x >> 12) & 7)];
}
void Level::setNext (int nextLevel, int nextWorld) {
unsigned char buffer[MTL_L_PROP];
nextLevelNum = nextLevel;
nextWorldNum = nextWorld;
if (gameMode) {
buffer[0] = MTL_L_PROP;
buffer[1] = MT_L_PROP;
buffer[2] = 0; // set next level
buffer[3] = nextLevel;
buffer[4] = nextWorld;
game->send(buffer);
}
return;
}
void Level::setTile (unsigned char gridX, unsigned char gridY,
unsigned char tile) {
unsigned char buffer[MTL_L_GRID];
grid[gridY][gridX].tile = tile;
if (gameMode) {
buffer[0] = MTL_L_GRID;
buffer[1] = MT_L_GRID;
buffer[2] = gridX;
buffer[3] = gridY;
buffer[4] = 0; // tile variable
buffer[5] = tile;
game->send(buffer);
}
return;
}
signed char * Level::getEvent (unsigned char gridX, unsigned char gridY) {
int event = grid[gridY][gridX].event;
if (event) return eventSet[grid[gridY][gridX].event];
return NULL;
}
unsigned char Level::getEventHits (unsigned char gridX, unsigned char gridY) {
return grid[gridY][gridX].hits;
}
int Level::getEventTime (unsigned char gridX, unsigned char gridY) {
return grid[gridY][gridX].time;
}
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;
grid[gridY][gridX].event = 0;
if (gameMode) {
buffer[0] = MTL_L_GRID;
buffer[1] = MT_L_GRID;
buffer[2] = gridX;
buffer[3] = gridY;
buffer[4] = 2; // event variable
buffer[5] = 0;
game->send(buffer);
}
return;
}
int Level::hitEvent (Player *source, unsigned char gridX, unsigned char gridY) {
GridElement *ge;
unsigned char buffer[MTL_L_GRID];
int hitsToKill;
ge = grid[gridY] + gridX;
hitsToKill = eventSet[ge->event][E_HITSTOKILL];
// If the event cannot be hit, return negative
if (!hitsToKill) return -1;
// Increase the hit count
ge->hits++;
// Check if the event has been killed
if (ge->hits == hitsToKill) {
// Notify the player that shot the bullet
// If this returns false, ignore the hit
if (!source->shootEvent(gridX, gridY, ticks)) {
ge->hits--;
return 1;
}
}
if (gameMode) {
buffer[0] = MTL_L_GRID;
buffer[1] = MT_L_GRID;
buffer[2] = gridX;
buffer[3] = gridY;
buffer[4] = 3; // hits variable
buffer[5] = ge->hits;
game->send(buffer);
}
// Return the number of hits remaining until the event is destroyed
return hitsToKill - ge->hits;
}
void Level::setEventTime (unsigned char gridX, unsigned char gridY, int time) {
grid[gridY][gridX].time = time;
return;
}
signed char * Level::getBullet (unsigned char bullet) {
return bulletSet[bullet];
}
Sprite * Level::getSprite (unsigned char sprite) {
return spriteSet + sprite;
}
Anim * Level::getAnim (unsigned char anim) {
return animSet + anim;
}
Anim * Level::getMiscAnim (unsigned char anim) {
return animSet + miscAnims[anim];
}
void Level::addTimer () {
unsigned char buffer[MTL_L_PROP];
if (stage != LS_NORMAL) return;
endTime += 2 * 60 * 1000; // 2 minutes. Is this right?
if (gameMode) {
buffer[0] = MTL_L_PROP;
buffer[1] = MT_L_PROP;
buffer[2] = 2; // add timer
buffer[3] = 0;
buffer[4] = 0; // Don't really matter
game->send(buffer);
}
return;
}
void Level::setWaterLevel (unsigned char gridY) {
unsigned char buffer[MTL_L_PROP];
waterLevel = TTOF(gridY);
if (gameMode) {
buffer[0] = MTL_L_PROP;
buffer[1] = MT_L_PROP;
buffer[2] = 1; // set water level
buffer[3] = gridY;
buffer[4] = 0; // Doesn't really matter
game->send(buffer);
}
return;
}
fixed Level::getWaterLevel (int phase) {
if (phase & 1024) return waterLevel - ((phase & 1023) * 32);
return waterLevel - ((1024 - (phase & 1023)) * 32);
}
void Level::playSound (int sound) {
if (sound > 0) ::playSound(soundMap[sound - 1]);
return;
}
void Level::setStage (int newStage) {
unsigned char buffer[MTL_L_STAGE];
if (stage == newStage) return;
stage = newStage;
if (gameMode) {
buffer[0] = MTL_L_STAGE;
buffer[1] = MT_L_STAGE;
buffer[2] = stage;
game->send(buffer);
}
return;
}
int Level::getStage () {
return stage;
}
Scene * Level::createScene () {
return new Scene(sceneFile);
}
void Level::receive (unsigned char *buffer) {
// Interpret data received from client/server
switch (buffer[1]) {
case MT_L_PROP:
if (buffer[2] == 0) {
nextLevelNum = buffer[3];
nextWorldNum = buffer[4];
} else if (buffer[2] == 1) {
waterLevel = TTOF(buffer[3]);
} else if (buffer[2] == 2) {
if (stage == LS_NORMAL)
endTime += 2 * 60 * 1000; // 2 minutes. Is this right?
}
break;
case MT_L_GRID:
if (buffer[4] == 0) grid[buffer[3]][buffer[2]].tile = buffer[5];
else if (buffer[4] == 2)
grid[buffer[3]][buffer[2]].event = buffer[5];
else if (buffer[4] == 3)
grid[buffer[3]][buffer[2]].hits = buffer[5];
break;
case MT_L_STAGE:
stage = buffer[2];
break;
}
return;
}
void Level::timeCalcs (bool paused) {
// Calculate smoothed fps
smoothfps = smoothfps + 1 - (smoothfps * ((float)mspf) / 1000.0f);
/* This equation is a simplified version of
(fps * c) + (smoothfps * (1 - c))
where c = (1 / fps)
and fps = 1000 / mspf
In other words, the response of smoothFPS to changes in FPS decreases as the
framerate increases
The following version is for c = (1 / smoothfps)
*/
// smoothfps = (fps / smoothfps) + smoothfps - 1;
// Ignore outlandish values
if (smoothfps > 9999) smoothfps = 9999;
if (smoothfps < 1) smoothfps = 1;
// Number of ticks of gameplay since the level started
prevTicks = ticks;
ticks = globalTicks - tickOffset;
if (paused) {
tickOffset += ticks - prevTicks;
ticks = prevTicks;
} else if (ticks > prevTicks + 100) {
tickOffset += ticks - (prevTicks + 100);
ticks = prevTicks + 100;
}
return;
}
int Level::play () {
char *options[5] = {"continue game", "save game", "load game",
"setup options", "quit game"};
PaletteEffect *levelPE;
char *string;
bool paused, pmenu;
int stats, option;
int returnTime;
int perfect;
int timeBonus;
int count;
unsigned int width;
// Arbitrary initial value
smoothfps = 50.0f;
tickOffset = globalTicks;
ticks = -10;
pmenu = paused = false;
option = 0;
stats = S_NONE;
returnTime = 0;
timeBonus = -1;
perfect = 0;
while (true) {
// Do general processing
if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT;
if (controls.release(C_ESCAPE)) {
if (!gameMode) paused = !paused;
pmenu = !pmenu;
option = 0;
}
if (controls.release(C_PAUSE) && !gameMode) paused = !paused;
if (controls.release(C_STATS)) {
if (!gameMode) stats ^= S_SCREEN;
else stats = (stats + 1) & 3;
}
if (pmenu) {
// Deal with menu controls
if (controls.release(C_UP)) option = (option + 4) % 5;
if (controls.release(C_DOWN)) option = (option + 1) % 5;
if (controls.release(C_ENTER)) {
switch (option) {
case 0: // Continue
paused = pmenu = false;
break;
case 1: // Save
break;
case 2: // Load
break;
case 3: // Setup
if (!gameMode) {
// Don't want palette effects in setup menu
levelPE = firstPE;
firstPE = NULL;
if (menu->setup() == E_QUIT) return E_QUIT;
// Restore level palette
usePalette(palette);
// Restore palette effects
firstPE = levelPE;
}
break;
case 4: // Quit game
return E_NONE;
}
}
}
timeCalcs(paused);
// Check if level has been won
if (game && returnTime && (ticks > returnTime)) {
if (nextLevelNum == 99) count = game->setLevel(NULL);
else {
string = createFileName(F_LEVEL, nextLevelNum, nextWorldNum);
count = game->setLevel(string);
delete[] string;
}
if (count < 0) return count;
return WON;
}
// Process frame-by-frame activity
if (!paused) {
// Apply controls to local player
for (count = 0; count < PCONTROLS; count++)
localPlayer->setControl(count, controls.getState(count));
count = playFrame();
if (count < 0) return count;
// Handle player reactions
for (count = 0; count < nPlayers; count++) {
if (players[count].reacted(ticks) == PR_KILLED) {
if (!gameMode) return LOST;
game->resetPlayer(players + count);
}
}
}
// Draw the graphics
draw();
// If paused, draw "PAUSE"
if (paused && !pmenu)
fontmn1->showString("PAUSE", (screenW >> 1) - 44, 32);
// If this is a competitive game, draw the score
if (gameMode) gameMode->drawScore();
// Draw player list
if (stats & S_PLAYERS) {
width = 96;
for (count = 0; count < nPlayers; count++)
if ((strlen(players[count].getName()) * 8) + 57 > width)
width = (strlen(players[count].getName()) * 8) + 57;
drawRect((viewW >> 1) - 32, 11, width, (nPlayers * 12) + 1, BLACK);
for (count = 0; count < nPlayers; count++) {
panelBigFont->showNumber(count + 1, (viewW >> 1) - 8,
14 + (count * 12));
panelBigFont->showString(players[count].getName(),
viewW >> 1, 14 + (count * 12));
panelBigFont->showNumber(players[count].teamScore,
(viewW >> 1) + width - 40, 14 + (count * 12));
}
}
// Draw graphics statistics
if (stats & S_SCREEN) {
drawRect(viewW - 84, 11, 80, 25, BLACK);
panelBigFont->showNumber(screenW, viewW - 52, 14);
panelBigFont->showString("x", viewW - 48, 14);
panelBigFont->showNumber(screenH, viewW - 12, 14);
panelBigFont->showString("fps", viewW - 76, 26);
panelBigFont->showNumber((int)smoothfps, viewW - 12, 26);
}
if (stage == LS_END) {
// The level is over, so draw play statistics & bonuses
// Apply time bonus
if (timeBonus) {
count = mspf / 100;
if (!count) count = 1;
if (timeBonus == -1) {
timeBonus = ((endTime - ticks) / 60000) * 100;
if (timeBonus < 0) timeBonus = 0;
if ((localPlayer->getEnemies() == enemies) &&
(localPlayer->getItems() == items)) perfect = 100;
} else if (timeBonus - count >= 0) {
localPlayer->addScore(count);
timeBonus -= count;
} else {
localPlayer->addScore(timeBonus);
timeBonus = 0;
}
if (timeBonus == 0) {
returnTime = ticks + T_END;
firstPE = new WhiteOutPaletteEffect(T_END, firstPE);
::playSound(S_UPLOOP);
}
}
// Display statistics & bonuses
// TODO: Display percentage symbol
fontmn1->showString("TIME", (screenW >> 1) - 152,
(screenH >> 1) - 60);
fontmn1->showNumber(timeBonus, (screenW >> 1) + 124,
(screenH >> 1) - 60);
fontmn1->showString("ENEMIES", (screenW >> 1) - 152,
(screenH >> 1) - 40);
if (enemies)
fontmn1->showNumber((localPlayer->getEnemies() * 100) / enemies,
(screenW >> 1) + 124, (screenH >> 1) - 40);
else
fontmn1->showNumber(0, (screenW >> 1) + 124,
(screenH >> 1) - 40);
fontmn1->showString("ITEMS", (screenW >> 1) - 152,
(screenH >> 1) - 20);
if (items)
fontmn1->showNumber((localPlayer->getItems() * 100) / items,
(screenW >> 1) + 124, (screenH >> 1) - 20);
else
fontmn1->showNumber(0, (screenW >> 1) + 124,
(screenH >> 1) - 20);
fontmn1->showString("PERFECT", (screenW >> 1) - 152, screenH >> 1);
fontmn1->showNumber(perfect, (screenW >> 1) + 124, screenH >> 1);
fontmn1->showString("SCORE", (screenW >> 1) - 152,
(screenH >> 1) + 40);
fontmn1->showNumber(localPlayer->getScore(), (screenW >> 1) + 124,
(screenH >> 1) + 40);
}
if (pmenu) {
// Draw the menu
drawRect((screenW >> 2) - 8, (screenH >> 1) - 46, 144, 92, BLACK);
for (count = 0; count < 5; count++) {
if (count == option) fontmn2->mapPalette(240, 8, 47, -16);
else fontmn2->mapPalette(240, 8, 15, -16);
fontmn2->showString(options[count], screenW >> 2,
(screenH >> 1) + (count << 4) - 38);
}
fontmn2->restorePalette();
}
// Networking
if (gameMode) {
count = game->playFrame(ticks);
switch (count) {
case E_UNUSED:
return E_NONE;
case E_NONE:
break;
default:
return count;
}
}
}
return E_NONE;
}
/*
*
* level.h
*
* 31st January 2006: Created level.h from parts of OpenJazz.h
* 4th February 2009: Created events.h from parts of level.h
* 19th March 2009: Created sprite.h from parts of level.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/* "Tile" is a flexible term. Here it is used to refer specifically to the
individual elements of the tile set.
"Tiles" in the context of level units are referred to as grid elements. */
#ifndef _LEVEL_H
#define _LEVEL_H
#include "io/gfx/anim.h"
#include "OpenJazz.h"
#include <SDL/SDL.h>
// Constants
// Displayed statistics
#define S_NONE 0
#define S_PLAYERS 1
#define S_SCREEN 2
// General
#define LW 256 /* Level width */
#define LH 64 /* Level height */
#define EVENTS 127
#define ELENGTH 32 /* Length of events, in bytes */
#define BULLETS 32
#define BLENGTH 20 /* Length of bullets, in bytes */
#define ANIMS 128
#define TKEY 127 /* Tileset colour key */
// Stages
#define LS_NORMAL 0
#define LS_SUDDENDEATH 1
#define LS_END 2
// Fade delays
#define T_START 500
#define T_END 1000
// Macros
// For converting between tile positions and int/fixed values
#define FTOT(x) ((x) >> 15)
#define TTOF(x) ((x) << 15)
#define ITOT(x) ((x) >> 5)
#define TTOI(x) ((x) << 5)
// Datatype
typedef struct {
unsigned char tile; // Indexes the tile set
unsigned char bg; // 0 = Effect background, 1 = Black background
unsigned char event; // Indexes the event set
unsigned char hits; // Number of times the event has been shot
int time; /* Point at which the event will do something, e.g.
terminate */
} GridElement;
// Classes
class Bullet;
class Event;
class Player;
class Scene;
class Level {
private:
char *sceneFile;
Sprite *spriteSet; // 208 of which are usually in mainchar.000
SDL_Surface *tileSet;
Anim animSet[ANIMS];
char miscAnims[4];
signed char bulletSet[BULLETS][BLENGTH];
signed char eventSet[EVENTS][ELENGTH]; // Not all used
char mask[240][64]; // At most 240 tiles, all with 8 * 8 masks
GridElement grid[LH][LW]; // All levels are the same size
int soundMap[32];
SDL_Color palette[256];
SDL_Color skyPalette[256];
bool sky;
unsigned char skyOrb;
int sprites;
int levelNum, worldNum, nextLevelNum, nextWorldNum;
unsigned char difficulty;
int pathLength;
int endTime;
int enemies, items;
fixed waterLevel;
fixed energyBar;
int stage;
int loadSprites (char *fileName);
int loadTiles (char *fileName);
protected:
float smoothfps;
int tickOffset, prevTicks, ticks;
int load (char *fileName, unsigned char diff, bool checkpoint);
int playFrame ();
void draw ();
void timeCalcs (bool paused);
public:
Event *firstEvent;
Bullet *firstBullet;
int *pathX;
int *pathY;
int pathNode;
Level ();
Level (char *fileName, unsigned char diff,
bool checkpoint);
virtual ~Level ();
bool checkMask (fixed x, fixed y);
bool checkMaskDown (fixed x, fixed y);
bool checkSpikes (fixed x, fixed y);
void setNext (int nextLevel, int nextWorld);
void setTile (unsigned char gridX, unsigned char gridY,
unsigned char tile);
signed char * getEvent (unsigned char gridX, unsigned char gridY);
unsigned char getEventHits (unsigned char gridX, unsigned char gridY);
int getEventTime (unsigned char gridX, unsigned char gridY);
void clearEvent (unsigned char gridX, unsigned char gridY);
int hitEvent (Player *source, unsigned char gridX,
unsigned char gridY);
void setEventTime (unsigned char gridX, unsigned char gridY,
int time);
signed char * getBullet (unsigned char bullet);
Sprite * getSprite (unsigned char sprite);
Anim * getAnim (unsigned char anim);
Anim * getMiscAnim (unsigned char anim);
void addTimer ();
void setWaterLevel (unsigned char gridY);
fixed getWaterLevel (int phase);
void playSound (int sound);
void setStage (int stage);
int getStage ();
Scene * createScene ();
void receive (unsigned char *buffer);
virtual int play ();
};
class DemoLevel : public Level {
private:
unsigned char *macro;
public:
DemoLevel (char *fileName);
~DemoLevel ();
int play ();
};
// Variables
EXTERN Level *level;
EXTERN fixed viewX, viewY;
#endif
/*
*
* levelframe.cpp
*
* 19th July 2009: Created levelframe.cpp from parts of level.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Provides the once-per-frame functions for levels.
*
*/
#include "bullet.h"
#include "event/event.h"
#include "level.h"
#include "game/game.h"
#include "game/gamemode.h"
#include "io/gfx/font.h"
#include "io/gfx/video.h"
#include "player/player.h"
int Level::playFrame () {
Bullet *nextBullet;
Event *nextEvent;
int x, y;
// Search for active events
for (y = FTOT(viewY) - 5; y < ITOT(FTOI(viewY) + viewH) + 5; y++) {
for (x = FTOT(viewX) - 5; x < ITOT(FTOI(viewX) + viewW) + 5; x++)
{
if ((x >= 0) && (y >= 0) && (x < LW) && (y < LH) &&
grid[y][x].event && (grid[y][x].event < 121)) {
nextEvent = firstEvent;
while (nextEvent) {
// If the event has been found, stop searching
if (nextEvent->isFrom(x, y)) break;
nextEvent = nextEvent->getNext();
}
// If the event wasn't found, create it
if (!nextEvent) firstEvent = new Event(x, y, firstEvent);
}
}
}
// Determine the players' trajectories
for (x = 0; x < nPlayers; x++) players[x].control(ticks);
// Process active events
pathNode = (ticks >> 5) % pathLength;
if (firstEvent) {
if (firstEvent->playFrame(ticks)) {
nextEvent = firstEvent->getNext();
delete firstEvent;
firstEvent = nextEvent;
}
}
// Process bullets
if (firstBullet) {
if (firstBullet->playFrame(ticks)) {
nextBullet = firstBullet->getNext();
delete firstBullet;
firstBullet = nextBullet;
}
}
// Apply as much of those trajectories as possible, without going into the
// scenery
for (x = 0; x < nPlayers; x++) players[x].move(ticks);
// Check if time has run out
if (ticks > endTime) {
if (!gameMode) {
if ((difficulty >= 2) && (stage == LS_NORMAL))
localPlayer->kill(NULL, endTime);
} else gameMode->outOfTime();
}
// Calculate viewport
if (game && (stage == LS_END)) game->view();
else localPlayer->view(ticks);
// Ensure the new viewport is within the level
if (viewX < 0) viewX = 0;
if (FTOI(viewX) + viewW >= TTOI(LW)) viewX = ITOF(TTOI(LW) - viewW);
if (viewY < 0) viewY = 0;
if (FTOI(viewY) + viewH >= TTOI(LH)) viewY = ITOF(TTOI(LH) - viewH);
return E_NONE;
}
void Level::draw () {
GridElement *ge;
Event *event;
Bullet *bullet;
SDL_Rect src, dst;
int vX, vY;
int x, y, bgScale;
// Set tile drawing dimensions
src.w = TTOI(1);
src.h = TTOI(1);
src.x = 0;
// Use the local player's viewport
dst.x = 0;
dst.y = 0;
vX = FTOI(viewX);
vY = FTOI(viewY);
dst.w = viewW;
dst.h = viewH;
SDL_SetClipRect(screen, &dst);
if ((viewW < screenW) || (viewH < screenH)) clearScreen(15);
// If there is a sky, draw it
if (sky) {
// Background scale
if (screenW > 320) bgScale = ((screenH - 1) / 100) + 1;
else bgScale = ((screenH - 34) / 100) + 1;
for (y = 0; y < viewH; y += bgScale)
drawRect(0, y, screenW, bgScale, 156 + (y / bgScale));
// Show sun / moon / etc.
if (skyOrb) {
dst.x = (viewW * 4) / 5;
dst.y = (viewH * 3) / 25;
src.y = TTOI(skyOrb);
SDL_BlitSurface(tileSet, &src, screen, &dst);
}
} else {
// If there is no sky, draw a blank background
// This is only very occasionally actually visible
clearScreen(127);
}
// Show background tiles
for (y = 0; y <= ITOT(viewH - 1) + 1; y++) {
for (x = 0; x <= ITOT(viewW - 1) + 1; x++) {
// Get the grid element from the given coordinates
ge = grid[y + ITOT(vY)] + x + ITOT(vX);
// If this tile uses a black background, draw it
if (ge->bg)
drawRect(TTOI(x) - (vX & 31), TTOI(y) - (vY & 31), 32, 32,
BLACK);
// If this is not a foreground tile, draw it
if ((eventSet[ge->event][E_BEHAVIOUR] != 38) &&
((ge->event < 124) || (ge->event > 125)) ) {
dst.x = TTOI(x) - (vX & 31);
dst.y = TTOI(y) - (vY & 31);
src.y = TTOI(ge->tile);
SDL_BlitSurface(tileSet, &src, screen, &dst);
}
}
}
// Show active events
event = firstEvent;
while (event) {
event->draw(ticks);
event = event->getNext();
}
// Show the players
for (x = 0; x < nPlayers; x++) players[x].draw(ticks);
// Show bullets
bullet = firstBullet;
while (bullet) {
bullet->draw();
bullet = bullet->getNext();
}
// Show foreground tiles
for (y = 0; y <= ITOT(viewH - 1) + 1; y++) {
for (x = 0; x <= ITOT(viewW - 1) + 1; x++) {
// Get the grid element from the given coordinates
ge = grid[y + ITOT(vY)] + x + ITOT(vX);
// If this is an "animated" foreground tile, draw it
if (ge->event == 123) {
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]);
SDL_BlitSurface(tileSet, &src, screen, &dst);
}
// If this is a foreground tile, draw it
if ((ge->event == 124) || (ge->event == 125) ||
(eventSet[ge->event][E_BEHAVIOUR] == 38) ) {
dst.x = TTOI(x) - (vX & 31);
dst.y = TTOI(y) - (vY & 31);
src.y = TTOI(ge->tile);
SDL_BlitSurface(tileSet, &src, screen, &dst);
}
}
}
// Uncomment the following for a line showing the water level
/* drawRect(0, FTOI(getWaterLevel(ticks) - viewY), screenW, 2, 24); */
SDL_SetClipRect(screen, NULL);
// Show panel
// Change the ammo type display on the panel
dst.x = 250;
dst.y = 2;
SDL_BlitSurface(panelAmmo[localPlayer->getAmmo(false) + 1], NULL, panel,
&dst);
dst.x = 0;
dst.y = screenH - 33;
SDL_BlitSurface(panel, NULL, screen, &dst);
drawRect(0, screenH - 1, 320, 1, BLACK);
// Show panel data
// Show score
panelSmallFont->showNumber(localPlayer->getScore(), 84, screenH - 27);
// Show time remaining
if (endTime > ticks) x = endTime - ticks;
else x = 0;
y = x / (60 * 1000);
panelSmallFont->showNumber(y, 116, screenH - 27);
x -= (y * 60 * 1000);
y = x / 1000;
panelSmallFont->showNumber(y, 136, screenH - 27);
x -= (y * 1000);
y = x / 100;
panelSmallFont->showNumber(y, 148, screenH - 27);
// Show lives
panelSmallFont->showNumber(localPlayer->getLives(), 124, screenH - 13);
// Show planet number
if (worldNum <= 41) // Main game levels
panelSmallFont->showNumber((worldNum % 3) + 1, 184, screenH - 13);
else if ((worldNum >= 50) && (worldNum <= 52)) // Christmas levels
panelSmallFont->showNumber(worldNum - 49, 184, screenH - 13);
else panelSmallFont->showNumber(worldNum, 184, screenH - 13);
// Show level number
panelSmallFont->showNumber(levelNum + 1, 196, screenH - 13);
// Show ammo
if (localPlayer->getAmmo(false) == -1)
panelSmallFont->showString(":;", 225, screenH - 13);
else panelSmallFont->showNumber(localPlayer->getAmmo(true), 245,
screenH - 13);
// Draw the health bar
dst.x = 20;
x = localPlayer->getEnergy();
if (FTOI(energyBar) < (x << 4)) {
if ((x << 14) - energyBar < mspf * 40) energyBar = x << 14;
else energyBar += mspf * 40;
} else if (FTOI(energyBar) > (x << 4)) {
if (energyBar - (x << 14) < mspf * 40) energyBar = x << 14;
else energyBar -= mspf * 40;
}
if (energyBar > F1) {
dst.w = FTOI(energyBar) - 1;
// Choose energy bar colour
if (x == 4) x = 24;
else if (x == 3) x = 17;
else if (x == 2) x = 80;
else if (x <= 1) x = 32 + (((ticks / 75) * 4) & 15);
// Draw energy bar
drawRect(dst.x, screenH - 13, dst.w, 7, x);
dst.x += dst.w;
dst.w = 64 - dst.w;
} else dst.w = 64;
// Fill in remaining energy bar space with black
drawRect(dst.x, screenH - 13, dst.w, 7, BLACK);
return;
}
/*
*
* levelload.cpp
*
* 22nd July 2008: Created levelload.c from parts of level.c
* 3rd February 2009: Renamed levelload.c to levelload.cpp
* 18th July 2009: Created demolevel.cpp from parts of level.cpp and
* levelload.cpp
* 19th July 2009: Added parts of levelload.cpp to level.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Deals with the loading of level data.
*
*/
#include "bullet.h"
#include "event/event.h"
#include "level.h"
#include "game/game.h"
#include "game/gamemode.h"
#include "io/file.h"
#include "io/gfx/font.h"
#include "io/gfx/paletteeffects.h"
#include "io/gfx/sprite.h"
#include "io/gfx/video.h"
#include "io/sound.h"
#include "menu/menu.h"
#include "player/player.h"
#include <string.h>
int Level::loadSprites (char * fileName) {
File *file, *mainFile, *specFile;
unsigned char *pixels, *sorted;
int mainPos, specPos;
int count, x, y, width, height, m;
// Open fileName
try {
specFile = new File(fileName, false);
} catch (int e) {
return e;
}
// This function loads all the sprites, not fust those in fileName
try {
mainFile = new File(F_MAINCHAR, false);
} catch (int e) {
delete specFile;
return e;
}
sprites = specFile->loadShort();
// Include space in the sprite set for the blank sprite at the end
spriteSet = new Sprite[sprites + 1];
// Read horizontal offsets
for (count = 0; count < sprites; count++)
spriteSet[count].xOffset = specFile->loadChar() << 2;
// Read vertical offsets
for (count = 0; count < sprites; count++)
spriteSet[count].yOffset = specFile->loadChar();
// Find where the sprites start in fileName
specPos = specFile->tell();
// Find where the sprites start in mainchar.000
mainPos = 2;
// Loop through all the sprites to be loaded
for (count = 0; count < sprites; count++) {
// Go to the start of the current sprite or file indicator
specFile->seek(specPos, true);
mainFile->seek(mainPos, true);
/* If both fileName and mainchar.000 have file indicators, create a
blank sprite */
while ((specFile->loadChar() == 0xFF) &&
(mainFile->loadChar() == 0xFF)) {
// Go to the next sprite/file indicator
specFile->seek(1, false);
mainFile->seek(1, false);
// set the position of the next sprite/file indicators
specPos += 2;
mainPos += 2;
// Create a blank sprite
spriteSet[count].clearPixels();
count++;
}
// Return to the start of the sprite/file indicators
specFile->seek(specPos, true);
mainFile->seek(mainPos, true);
// Unless otherwise stated, load from fileName
file = specFile;
// Check if otherwise stated
if (file->loadChar() == 0xFF) {
file = mainFile;
} else file->seek(-1, false);
width = file->loadShort() << 2;
height = file->loadShort();
// Position of the next sprite or file indicator in each file
if (file == specFile) {
mainPos += 2;
specPos += 10 + (file->loadShort() << 2);
} else {
specPos += 2;
mainPos += 10 + (file->loadShort() << 2);
}
// m is for MAGIC
m = file->loadShort();
// Allocate space for descrambling
sorted = new unsigned char[width * height];
// Actually, m is for mask offset.
// Sprites can be either masked or not masked.
if (!m) {
// Not masked
// Load the pixel data directly for descrambling
file->seek(2, false);
// Read pixel data
pixels = file->loadBlock(width * height);
} else {
// Allocate space for pixel data
pixels = new unsigned char[width * height];
// Masked
// Load the pixel data according to the mask
// Masked sprites have their own next sprite offsets
if (file == specFile) {
specPos = file->loadShort() << 2;
} else {
mainPos = file->loadShort() << 2;
}
// Skip to mask
file->seek(m, false);
// Read the mask
// Each mask pixel is either 0 or 1
// Four pixels are packed into the lower end of each byte
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
if (!(x & 3)) m = file->loadChar();
pixels[(y * width) + x] = (m >> (x & 3)) & 1;
}
}
// Pixels are loaded if the corresponding mask pixel is 1, otherwise
// the transparent index is used. Pixels are scrambled, so the mask
// has to be scrambled the same way.
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
sorted[(((y >> 2) + ((x & 3) * (height >> 2))) * width) +
(x >> 2) +
(((y & 3) + ((height & 3) * (x & 3))) * (width >> 2))] =
pixels[(y * width) + x];
}
}
// Skip to pixels
file->seek(width >> 2, false);
// Next sprite offsets are relative to here
if (file == specFile) specPos += file->tell();
else mainPos += file->tell();
// Read pixels according to the scrambled mask
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
if (sorted[(y * width) + x] == 1) {
// The unmasked portions are transparent, so no masked
// portion should be transparent.
m = SKEY;
while (m == SKEY) m = file->loadChar();
// Use the acceptable pixel
pixels[(y * width) + x] = m;
} else {
// Use the transparent pixel
pixels[(y * width) + x] = SKEY;
}
}
}
}
// Rearrange pixels in correct order
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
sorted[(y * width) + x] =
pixels[(((y >> 2) + ((x & 3) * (height >> 2))) * width) +
(x >> 2) +
(((y & 3) + ((height & 3) * (x & 3))) * (width >> 2))];
}
}
// Convert the sprite to an SDL surface
spriteSet[count].setPixels(sorted, width, height);
// Free redundant data
delete[] pixels;
// Check if the next sprite exists
// If not, create blank sprites for the remainder
if (specPos >= file->getSize()) {
for (count++; count < sprites; count++) {
spriteSet[count].clearPixels();
}
} else {
specFile->seek(specPos, true);
}
}
delete mainFile;
delete specFile;
// Include a blank sprite at the end
spriteSet[sprites].clearPixels();
spriteSet[sprites].xOffset = 0;
spriteSet[sprites].yOffset = 0;
return E_NONE;
}
int Level::loadTiles (char * fileName) {
File *file;
unsigned char *buffer;
int rle, pos, index, count, fileSize;
int tiles;
try {
file = new File(fileName, false);
} catch (int e) {
return e;
}
// Load the palette
file->loadPalette(palette);
// Load the background palette
file->loadPalette(skyPalette);
// Skip the second, identical, background palette
file->skipRLE();
// Load the tile pixel indices
tiles = 240; // Never more than 240 tiles
buffer = new unsigned char[tiles * 1024];
file->seek(4, false);
pos = 0;
fileSize = file->getSize();
// Read the RLE pixels
// file::loadRLE() cannot be used, for reasons that will become clear
while ((pos < 1024 * tiles) && (file->tell() < fileSize)) {
rle = file->loadChar();
if (rle & 128) {
index = file->loadChar();
for (count = 0; count < (rle & 127); count++) buffer[pos++] = index;
} else if (rle) {
for (count = 0; count < rle; count++)
buffer[pos++] = file->loadChar();
} else { // This happens at the end of each tile
// 0 pixels means 1 pixel, apparently
buffer[pos++] = file->loadChar();
file->seek(2, false); /* I assume this is the length of the next
tile block */
if (pos == 1024 * 60) file->seek(2, false);
if (pos == 1024 * 120) file->seek(2, false);
if (pos == 1024 * 180) file->seek(2, false);
}
}
delete file;
// Work out how many tiles were actually loaded
// Should be a multiple of 60
tiles = pos / 1024;
tileSet = createSurface(buffer, TTOI(1), TTOI(tiles));
SDL_SetColorKey(tileSet, SDL_SRCCOLORKEY, TKEY);
return tiles;
}
int Level::load (char *fileName, unsigned char diff, bool checkpoint) {
File *file;
unsigned char *buffer;
char *string, *ext;
int tiles;
int count, x, y, type;
difficulty = diff;
// Show loading screen
// Open planet.### file
if (!strcmp(fileName, LEVEL_FILE)) {
// Using the downloaded level file
string = createString("DOWNLOADED");
} else {
// Load the planet's name from the planet.### file
string = createFileName(F_PLANET, fileName + strlen(fileName) - 3);
try {
file = new File(string, false);
} catch (int e) {
delete[] string;
return e;
}
delete[] string;
file->seek(2, true);
string = file->loadString();
delete file;
}
switch (fileName[5]) {
case '0':
ext = " LEVEL ONE";
break;
case '1':
ext = " LEVEL TWO";
break;
case '2':
string[0] = 0;
ext = "SECRET LEVEL";
break;
default:
ext = " LEVEL";
break;
}
usePalette(menu->palettes[1]);
clearScreen(0);
x = (screenW >> 1) - ((strlen(string) + strlen(ext)) << 2);
x = fontmn2->showString("LOADING ", x - 60, (screenH >> 1) - 16);
x = fontmn2->showString(string, x, (screenH >> 1) - 16);
fontmn2->showString(ext, x, (screenH >> 1) - 16);
delete[] string;
if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT;
// Open level file
try {
file = new File(fileName, false);
} catch (int e) {
return e;
}
// Load level data from a Level#.### file
// Load the blocks.### extension
// Skip past all level data
file->seek(39, true);
file->skipRLE();
file->skipRLE();
file->skipRLE();
file->skipRLE();
file->skipRLE();
file->skipRLE();
file->skipRLE();
file->skipRLE();
file->seek(598, false);
file->skipRLE();
file->seek(4, false);
file->skipRLE();
file->skipRLE();
file->seek(25, false);
file->skipRLE();
file->seek(3, false);
// Load the level number
levelNum = file->loadChar() ^ 210;
// Load the world number
worldNum = file->loadChar() ^ 4;
// Load tile set from appropriate blocks.###
// Load tile set extension
file->seek(8, false);
ext = file->loadString();
if (!strcmp(ext, "999")) {
// Use the level file's extension instead
delete[] ext;
ext = createString(fileName + strlen(fileName) - 3);
}
// Allocate space for file names
string = createFileName(F_BLOCKS, ext);
delete[] ext;
tiles = loadTiles(string);
delete[] string;
if (tiles < 0) {
delete file;
return tiles;
}
// Load sprite set from corresponding Sprites.###
string = createFileName(F_SPRITES, worldNum);
count = loadSprites(string);
delete[] string;
if (count < 0) {
SDL_FreeSurface(tileSet);
delete file;
return count;
}
// Skip to tile and event reference data
file->seek(39, true);
// Load tile and event references
buffer = file->loadRLE(LW * LH * 2);
// Create grid from data
for (x = 0; x < LW; x++) {
for (y = 0; y < LH; y++) {
grid[y][x].tile = buffer[(y + (x * LH)) << 1];
grid[y][x].bg = buffer[((y + (x * LH)) << 1) + 1] >> 7;
grid[y][x].event = buffer[((y + (x * LH)) << 1) + 1] & 127;
grid[y][x].hits = 0;
}
}
delete[] buffer;
// A mysterious block of mystery
file->skipRLE();
// Load mask data
buffer = file->loadRLE(tiles * 8);
// Unpack bits
for (count = 0; count < tiles; count++) {
for (y = 0; y < 8; y++) {
for (x = 0; x < 8; x++)
mask[count][(y << 3) + x] = (buffer[(count << 3) + y] >> x) & 1;
}
}
delete[] buffer;
/* Uncomment the code below if you want to see the mask instead of the tile
graphics during gameplay */
/*if (SDL_MUSTLOCK(tileSet)) SDL_LockSurface(tileSet);
for (count = 0; count < tiles; count++) {
for (y = 0; y < 32; y++) {
for (x = 0; x < 32; x++) {
if (mask[count][((y >> 2) << 3) + (x >> 2)] == 1)
((char *)(tileSet->pixels))
[(count * 1024) + (y * 32) + x] = 88;
}
}
}
if (SDL_MUSTLOCK(tileSet)) SDL_UnlockSurface(tileSet);*/
// Load special event path
buffer = file->loadRLE(8192);
pathLength = buffer[0] + (buffer[1] << 8);
pathNode = 0;
if (pathLength < 1) pathLength = 1;
pathX = new int[pathLength];
pathY = new int[pathLength];
for (count = 0; count < pathLength; count++) {
pathX[count] = ((signed char *)buffer)[(count << 1) + 3] << 2;
pathY[count] = ((signed char *)buffer)[(count << 1) + 2];
}
delete[] buffer;
// Load event set
buffer = file->loadRLE(EVENTS * ELENGTH);
// Fill event set with data
for (count = 0; count < EVENTS; count++) {
memcpy(eventSet[count], buffer + (count * ELENGTH), ELENGTH);
eventSet[count][E_MOVEMENTSP]++;
}
delete[] buffer;
// Process grid
enemies = items = 0;
for (x = 0; x < LW; x++) {
for (y = 0; y < LH; y++) {
// Eliminate event references for events of too high a difficulty
if (eventSet[grid[y][x].event][E_DIFFICULTY] > 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[grid[y][x].event][E_MODIFIER] == 0) &&
eventSet[grid[y][x].event][E_HITSTOKILL]) enemies++;
else if (eventSet[grid[y][x].event][E_ADDEDSCORE]) items++;
}
}
// Yet more doubtless essential data
file->skipRLE();
// Load animation set
buffer = file->loadRLE(ANIMS * 64);
// Create animation set based on that data
for (count = 0; count < ANIMS; count++) {
animSet[count].setData(buffer[(count * 64) + 6],
buffer[(count * 64) + 4], buffer[(count * 64) + 5]);
for (y = 0; y < buffer[(count * 64) + 6]; y++) {
// Get frame
x = buffer[(count * 64) + 7 + y];
if (x > sprites) x = sprites;
// Assign sprite and vertical offset
animSet[count].setFrame(y, true);
animSet[count].setFrameData(spriteSet + x,
buffer[(count * 64) + 26 + y], buffer[(count * 64) + 45 + y]);
}
}
delete[] buffer;
// At general data
// There's a a whole load of unknown data around here
// Like another one of those pesky RLE blocks
file->skipRLE();
// And 217 bytes of DOOM
file->seek(217, false);
// Load sound map
x = file->tell();
for (count = 0; count < 32; count++) {
file->seek(x + (count * 9), true);
string = file->loadString();
soundMap[count] = -1;
// Search for matching sound
for (y = 0; (y < nSounds) && (soundMap[count] == -1); y++) {
if (!strcmp(string, sounds[y].name)) soundMap[count] = y;
}
delete[] string;
}
file->seek(x + 288, true);
// Music file
string = file->loadString();
playMusic(string);
// 26 bytes of undiscovered usefulness, less the music file name
file->seek(x + 314, true);
delete[] string;
// End of episode cutscene
sceneFile = file->loadString();
// 52 bytes of undiscovered usefulness, less the cutscene file name
file->seek(x + 366, true);
// The players' coordinates
if (!checkpoint && game) {
x = file->loadShort();
y = file->loadShort() + 1;
game->setCheckpoint(x, y);
} else file->seek(4, false);
// Set the players' initial values
for (count = 0; count < nPlayers; count++)
game->resetPlayer(players + count);
// Next level
x = file->loadChar();
y = file->loadChar();
setNext(x, y);
// Thanks to Doubble Dutch for this next bit
file->seek(4, false);
waterLevel = ITOF(file->loadShort());
// Thanks to Feline and the JCS94 team for the next bits:
file->seek(3, false);
// Now at "Section 15"
// Load player's animation set references
buffer = file->loadRLE(PANIMS * 2);
string = new char[PANIMS + 3];
for (x = 0; x < PANIMS; x++) string[x + 3] = buffer[x << 1];
for (x = 0; x < nPlayers; x++) players[x].setAnims(string + 3);
if (gameMode) {
string[0] = MTL_P_ANIMS;
string[1] = MT_P_ANIMS;
string[2] = 0;
game->send((unsigned char *)string);
}
delete[] string;
delete[] buffer;
// Load Skip to bullet set
miscAnims[0] = file->loadChar();
miscAnims[1] = file->loadChar();
miscAnims[2] = file->loadChar();
miscAnims[3] = file->loadChar();
// Load bullet set
buffer = file->loadRLE(BULLETS * BLENGTH);
for (count = 0; count < BULLETS; count++) {
memcpy(bulletSet[count], buffer + (count * BLENGTH), BLENGTH);
// Make sure bullets go in the right direction
if (bulletSet[count][B_XSPEED] > 0)
bulletSet[count][B_XSPEED] = -bulletSet[count][B_XSPEED];
if (bulletSet[count][B_XSPEED | 1] < 0)
bulletSet[count][B_XSPEED | 1] = -bulletSet[count][B_XSPEED | 1];
if (bulletSet[count][B_XSPEED | 2] > 0)
bulletSet[count][B_XSPEED | 2] = -bulletSet[count][B_XSPEED | 2];
if (bulletSet[count][B_XSPEED | 3] < 0)
bulletSet[count][B_XSPEED | 3] = -bulletSet[count][B_XSPEED | 3];
}
delete[] buffer;
// Now at "Section 18." More skippability.
file->skipRLE();
// Now at "Section 19," THE MAGIC SECTION
// First byte is the background palette effect type
type = file->loadChar();
sky = false;
// Free any existing palette effects
if (firstPE) delete firstPE;
switch (type) {
case 2:
sky = true;
// Sky background effect
firstPE = new SkyPaletteEffect(156, 100, FH, skyPalette, NULL);
break;
case 8:
// Parallaxing background effect
firstPE = new P2DPaletteEffect(128, 64, FE, NULL);
break;
case 9:
// Diagonal stripes "parallaxing" background effect
firstPE = new P1DPaletteEffect(128, 32, FH, NULL);
break;
case 11:
// The deeper below water, the darker it gets
firstPE = new WaterPaletteEffect(TTOF(32), NULL);
break;
default:
// No effect
firstPE = NULL;
break;
}
// Palette animations
// These are applied to every level without a conflicting background effect
// As a result, there are a few levels with things animated that shouldn't
// be
// In Diamondus: The red/yellow palette animation
firstPE = new RotatePaletteEffect(112, 4, F32, firstPE);
// In Diamondus: The waterfall palette animation
firstPE = new RotatePaletteEffect(116, 8, F16, firstPE);
// The following were discoverd by Unknown/Violet
firstPE = new RotatePaletteEffect(124, 3, F16, firstPE);
if ((type != PE_1D) && (type != PE_2D))
firstPE = new RotatePaletteEffect(132, 8, F16, firstPE);
if ((type != PE_SKY) && (type != PE_2D))
firstPE = new RotatePaletteEffect(160, 32, -F16, firstPE);
if (type != PE_SKY) {
firstPE = new RotatePaletteEffect(192, 32, -F32, firstPE);
firstPE = new RotatePaletteEffect(224, 16, F16, firstPE);
}
// Level fade-in/white-in effect
if (checkpoint) firstPE = new FadeInPaletteEffect(T_START, firstPE);
else firstPE = new WhiteInPaletteEffect(T_START, firstPE);
file->seek(1, false);
skyOrb = file->loadChar(); /* A.k.a the sun, the moon, the brightest star,
that red planet with blue veins... */
// And that's us done!
delete file;
// Apply the palette to surfaces that already exist, e.g. fonts
usePalette(palette);
// Adjust fontmn1 to use level palette
fontmn1->mapPalette(224, 8, 14, -16);
// Set the tick at which the level will end
endTime = (5 - difficulty) * 2 * 60 * 1000;
// Set the level stage
stage = LS_NORMAL;
firstBullet = NULL;
firstEvent = NULL;
energyBar = 0;
return E_NONE;
}
/*
*
* main.cpp
*
* 23rd August 2005: Created main.c
* 22nd July 2008: Created util.c from parts of main.c
* 3rd February 2009: Renamed main.c to main.cpp
* 4th February 2009: Created palette.cpp from parts of main.cpp and util.cpp
* 13th July 2009: Created controls.cpp from parts of main.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Contains the main function.
*
*/
#define EXTERN
#include "game/game.h"
#include "game/gamemode.h"
#include "io/controls.h"
#include "io/file.h"
#include "io/gfx/font.h"
#include "io/gfx/paletteeffects.h"
#include "io/gfx/video.h"
#include "io/network.h"
#include "io/sound.h"
#include "level/level.h"
#include "menu/menu.h"
#include "player/player.h"
#include <string.h>
int loadMain () {
File *file;
unsigned char *pixels, *sorted;
int count, x, y;
// Initialise video settings
screenW = 320;
screenH = 200;
#ifndef FULLSCREEN_ONLY
fullscreen = false;
#endif
// Assume that in windowed mode the palette is being emulated
// This is extremely likely
// TODO: Find a better way
fakePalette = true;
firstPE = NULL;
// Create the player's name
characterName = createEditableString(CHAR_NAME);
// Assign the player's colour
characterCols[0] = CHAR_FUR;
characterCols[1] = CHAR_BAND;
characterCols[2] = CHAR_GUN;
characterCols[3] = CHAR_WBAND;
// Create the network address
netAddress = createString(NET_ADDRESS);
// Open config file
try {
file = new File(CONFIG_FILE, false);
} catch (int e) {
file = NULL;
}
// Check that the config file was opened, and has the correct version
if (file && (file->loadChar() == 1)) {
// Read video settings
screenW = file->loadShort();
screenH = file->loadShort();
#ifdef FULLSCREEN_ONLY
file->loadChar();
#else
fullscreen = file->loadChar();
#endif
// Read controls
for (count = 0; count < CONTROLS - 4; count++)
controls.setKey(count, (SDLKey)(file->loadInt()));
for (count = 0; count < CONTROLS; count++)
controls.setButton(count, file->loadInt());
for (count = 0; count < CONTROLS; count++)
controls.setAxis(count, file->loadInt(), file->loadInt());
// Read the player's name
for (count = 0; count < STRING_LENGTH; count++)
characterName[count] = file->loadChar();
characterName[STRING_LENGTH] = 0;
// Read the player's colours
characterCols[0] = file->loadChar();
characterCols[1] = file->loadChar();
characterCols[2] = file->loadChar();
characterCols[3] = file->loadChar();
delete file;
} else {
log("Valid configuration file not found.");
}
// Create the game's window
#ifdef FULLSCREEN_ONLY
screen = SDL_SetVideoMode(screenW, screenH, 8,
SDL_FULLSCREEN | SDL_DOUBLEBUF | SDL_HWSURFACE | SDL_HWPALETTE);
#else
screen = SDL_SetVideoMode(screenW, screenH, 8,
SDL_RESIZABLE | SDL_DOUBLEBUF | SDL_HWSURFACE | SDL_HWPALETTE);
#endif
if (!screen) {
logError("Could not set video mode", SDL_GetError());
delete characterName;
return E_VIDEO;
}
SDL_WM_SetCaption("OpenJazz", NULL);
if (SDL_NumJoysticks() > 0) SDL_JoystickOpen(0);
// Generate the logical palette
for (count = 0; count < 256; count++)
logicalPalette[count].r = logicalPalette[count].g =
logicalPalette[count].b = count;
restorePalette(screen);
// Set up audio
openAudio();
// Load the panel
try {
file = new File(F_PANEL, false);
} catch (int e) {
closeAudio();
delete characterName;
return e;
}
// Load the panel background
panel = file->loadSurface(320, 32);
// Load the panel's ammo graphics
sorted = new unsigned char[64 * 27];
file->seek(7537, true);
pixels = file->loadRLE(64 * 27);
for (y = 0; y < 27; y++) {
for (x = 0; x < 64; x++)
sorted[(y * 64) + x] = pixels[(y * 64) + (x >> 2) + ((x & 3) << 4)];
}
panelAmmo[0] = createSurface(sorted, 64, 27);
sorted = pixels; // Re-use the allocated memory
file->seek(8264, true);
pixels = file->loadRLE(64 * 27);
for (y = 0; y < 27; y++) {
for (x = 0; x < 64; x++)
sorted[(y * 64) + x] = pixels[(y * 64) + (x >> 2) + ((x & 3) << 4)];
}
panelAmmo[1] = createSurface(sorted, 64, 27);
sorted = pixels; // Re-use the allocated memory
file->seek(9550, true);
pixels = file->loadRLE(64 * 27);
for (y = 0; y < 27; y++) {
for (x = 0; x < 64; x++)
sorted[(y * 64) + x] = pixels[(y * 64) + (x >> 2) + ((x & 3) << 4)];
}
panelAmmo[2] = createSurface(sorted, 64, 27);
sorted = pixels; // Re-use the allocated memory
file->seek(11060, true);
pixels = file->loadRLE(64 * 27);
for (y = 0; y < 27; y++) {
for (x = 0; x < 64; x++)
sorted[(y * 64) + x] = pixels[(y * 64) + (x >> 2) + ((x & 3) << 4)];
}
panelAmmo[3] = createSurface(sorted, 64, 27);
sorted = pixels; // Re-use the allocated memory
file->seek(12258, true);
pixels = file->loadRLE(64 * 27);
for (y = 0; y < 27; y++) {
for (x = 0; x < 64; x++)
sorted[(y * 64) + x] = pixels[(y * 64) + (x >> 2) + ((x & 3) << 4)];
}
panelAmmo[4] = createSurface(sorted, 64, 27);
delete[] pixels; // Don't re-use the allocated memory
// Load fonts
panelBigFont = NULL;
panelSmallFont = NULL;
font2 = NULL;
fontbig = NULL;
fontiny = NULL;
fontmn1 = NULL;
try {
panelBigFont = new Font(file, true);
panelSmallFont = new Font(file, false);
font2 = new Font("font2.0fn");
fontbig = new Font("fontbig.0fn");
fontiny = new Font("fontiny.0fn");
fontmn1 = new Font("fontmn1.0fn");
fontmn2 = new Font("fontmn2.0fn");
} catch (int e) {
if (panelBigFont) delete panelBigFont;
if (panelSmallFont) delete panelSmallFont;
if (font2) delete font2;
if (fontbig) delete fontbig;
if (fontiny) delete fontiny;
if (fontmn1) delete fontmn1;
SDL_FreeSurface(panel);
SDL_FreeSurface(panelAmmo[0]);
SDL_FreeSurface(panelAmmo[1]);
SDL_FreeSurface(panelAmmo[2]);
SDL_FreeSurface(panelAmmo[3]);
SDL_FreeSurface(panelAmmo[4]);
closeAudio();
delete[] characterName;
delete file;
return e;
}
delete file;
// Establish arbitrary timing
mspf = 20;
globalTicks = SDL_GetTicks() - 20;
// Initiate networking
net = new Network();
return E_NONE;
}
void freeMain () {
File *file;
int count;
delete net;
delete panelBigFont;
delete panelSmallFont;
delete font2;
delete fontbig;
delete fontiny;
delete fontmn1;
delete fontmn2;
SDL_FreeSurface(panel);
SDL_FreeSurface(panelAmmo[0]);
SDL_FreeSurface(panelAmmo[1]);
SDL_FreeSurface(panelAmmo[2]);
SDL_FreeSurface(panelAmmo[3]);
SDL_FreeSurface(panelAmmo[4]);
closeAudio();
// Open config file
try {
file = new File(CONFIG_FILE, true);
} catch (int e) {
file = NULL;
}
// Check that the config file was opened
if (file) {
// Write the version number
file->storeChar(1);
// Write video settings
file->storeShort(screenW);
file->storeShort(screenH);
#ifdef FULLSCREEN_ONLY
file->storeChar(1);
#else
file->storeChar(fullscreen? ~0: 0);
#endif
// Write controls
for (count = 0; count < CONTROLS - 4; count++)
file->storeInt(controls.getKey(count));
for (count = 0; count < CONTROLS; count++)
file->storeInt(controls.getButton(count));
for (count = 0; count < CONTROLS; count++) {
file->storeInt(controls.getAxis(count));
file->storeInt(controls.getAxisDirection(count));
}
// Write the player's name
for (count = 0; count < STRING_LENGTH; count++)
file->storeChar(characterName[count]);
// Write the player's colour
file->storeChar(characterCols[0]);
file->storeChar(characterCols[1]);
file->storeChar(characterCols[2]);
file->storeChar(characterCols[3]);
delete file;
} else {
logError("Could not write configuration file",
"File could not be opened.");
}
delete[] characterName;
return;
}
int loop (int type) {
SDL_Color shownPalette[256];
SDL_Event event;
int ret;
// Show everything that has been drawn so far
SDL_Flip(screen);
// Calculate frame rate and key timing
ret = SDL_GetTicks();
mspf = ret - globalTicks;
if (mspf > 100) mspf = 100;
globalTicks = ret;
// Process system events
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
#ifndef FULLSCREEN_ONLY
// If Alt + Enter has been pressed, go to full screen
if ((event.key.keysym.sym == SDLK_RETURN) &&
(event.key.keysym.mod & KMOD_ALT)) toggleFullscreen();
#endif
// Break statement intentionally omitted
case SDL_KEYUP:
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
case SDL_JOYAXISMOTION:
ret = controls.update(&event, type);
if (ret != E_NONE) return ret;
break;
#ifndef FULLSCREEN_ONLY
case SDL_VIDEORESIZE:
screenW = event.resize.w;
screenH = event.resize.h;
screen = SDL_SetVideoMode(screenW, screenH, 8,
SDL_RESIZABLE | SDL_DOUBLEBUF | SDL_HWSURFACE |
SDL_HWPALETTE);
// The absence of a break statement is intentional
case SDL_VIDEOEXPOSE:
SDL_SetPalette(screen, SDL_LOGPAL, logicalPalette, 0, 256);
SDL_SetPalette(screen, SDL_PHYSPAL, currentPalette, 0, 256);
break;
#endif
case SDL_QUIT:
return E_QUIT;
}
}
controls.loop();
// Apply palette effects
if (firstPE) {
/* If the palette is being emulated, compile all palette changes and
apply them all at once.
If the palette is being used directly, apply all palette effects
directly. */
if (fakePalette) {
memcpy(shownPalette, currentPalette, sizeof(SDL_Color) * 256);
firstPE->apply(shownPalette, false);
SDL_SetPalette(screen, SDL_PHYSPAL, shownPalette, 0, 256);
} else {
firstPE->apply(shownPalette, true);
}
}
return E_NONE;
}
int main(int argc, char *argv[]) {
/*
Scene *scene;
*/
// Find path
if (argc < 2) {
// No path was given, use the path of the program
int count;
count = strlen(argv[0]) - 1;
// Search for directory separator
#ifdef WIN32
while ((argv[0][count] != '\\') && (count >= 0)) count--;
#else
while ((argv[0][count] != '/') && (count >= 0)) count--;
#endif
path = new char[count + 2];
// If a directory was found, copy it to the path
if (count >= 0) {
memcpy(path, argv[0], count + 1);
path[count + 1] = 0;
} else path[0] = 0;
} else {
// Copy the provided path, appending a directory separator as necessary
#ifdef WIN32
if (argv[1][strlen(argv[1]) - 1] != '\\') {
#else
if (argv[1][strlen(argv[1]) - 1] != '/') {
#endif
#ifdef WIN32
path = createString(argv[1], "\\");
#else
path = createString(argv[1], "/");
#endif
} else {
path = createString(argv[1]);
}
}
// Initialise SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK |
SDL_INIT_TIMER) < 0) {
logError("Could not start SDL", SDL_GetError());
return -1;
}
// Load universal game data and establish a window
if (loadMain() != E_NONE) {
SDL_Quit();
delete[] path;
return -1;
}
// Show the startup cutscene
/*
try {
scene = new Scene("startup.0sc");
} catch (int e) {
freeMain();
SDL_Quit();
delete[] path;
return e;
}
if (scene->play() != E_QUIT) {
delete scene;
*/
// Load the menu
try {
menu = new Menu();
} catch (int e) {
freeMain();
SDL_Quit();
delete[] path;
return e;
}
// Run the main menu
if (menu->main() == E_NONE) {
// Show the ending cutscene
/*
try {
scene = new Scene("end.0sc");
} catch (int e) {
delete menu;
freeMain();
SDL_Quit();
delete[] path;
return e;
}
scene->play();
delete scene;
*/
}
delete menu;
/*
} else delete scene;
*/
freeMain();
SDL_Quit();
delete[] path;
return 0;
}
/*
*
* gamemenu.cpp
*
* 18th July 2009: Created menugame.cpp from parts of menu.cpp
* 26th July 2009: Renamed menugame.cpp to gamemenu.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Deals with the running of the menus used to create a new game.
*
*/
#include "menu.h"
#include "game/game.h"
#include "game/gamemode.h"
#include "io/controls.h"
#include "io/gfx/font.h"
#include "io/gfx/video.h"
#include "io/sound.h"
int Menu::newGameDifficulty (int mode, int levelNum, int worldNum) {
char *options[4] = {"easy", "medium", "hard", "turbo"};
char *firstLevel;
SDL_Rect src, dst;
int count;
usePalette(palettes[1]);
while (true) {
if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT;
if (controls.release(C_ESCAPE)) return E_NONE;
SDL_Delay(T_FRAME);
clearScreen(0);
for (count = 0; count < 4; count++) {
if (count == difficulty) fontmn2->mapPalette(240, 8, 114, 16);
fontmn2->showString(options[count], screenW >> 2,
(screenH >> 1) + (count << 4) - 32);
if (count == difficulty) fontmn2->restorePalette();
}
src.x = (difficulty & 1) * 160;
src.y = (difficulty & 2) * 50;
src.w = 160;
src.h = 100;
dst.x = (screenW >> 1) - 40;
dst.y = (screenH >> 1) - 50;
SDL_BlitSurface(screens[2], &src, screen, &dst);
if (controls.release(C_UP)) difficulty = (difficulty + 3) % 4;
if (controls.release(C_DOWN)) difficulty = (difficulty + 1) % 4;
if (controls.release(C_ENTER)) {
playSound(S_ORB);
firstLevel = createFileName(F_LEVEL, levelNum, worldNum);
if (mode == M_SINGLE) {
try {
game = new Game(firstLevel, difficulty);
} catch (int e) {
delete[] firstLevel;
message("COULD NOT START GAME");
return e;
}
} else {
try {
game = new ServerGame(mode, firstLevel, difficulty);
} catch (int e) {
delete[] firstLevel;
message("COULD NOT CREATE SERVER");
return e;
}
}
// Play the level(s)
switch (game->play()) {
case E_QUIT:
delete game;
delete[] firstLevel;
return E_QUIT;
case E_FILE:
message("FILE NOT FOUND");
break;
}
delete game;
delete[] firstLevel;
return E_NONE;
}
}
return E_NONE;
}
int Menu::newGameLevel (int mode) {
int option, worldNum, levelNum;
worldNum = levelNum = option = 0;
usePalette(palettes[1]);
while (true) {
if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT;
if (controls.release(C_ESCAPE)) return E_NONE;
SDL_Delay(T_FRAME);
clearScreen(15);
if (option == 0) fontmn2->mapPalette(240, 8, 114, 16);
fontmn2->showString("choose world:", 32, screenH / 3);
fontmn2->showNumber(worldNum, 208, screenH / 3);
if (option == 0) fontmn2->restorePalette();
else fontmn2->mapPalette(240, 8, 114, 16);
fontmn2->showString("choose level:", 32, (screenH << 1) / 3);
fontmn2->showNumber(levelNum, 208, (screenH << 1) / 3);
if (option != 0) fontmn2->restorePalette();
if (controls.release(C_UP)) option ^= 1;
if (controls.release(C_DOWN)) option ^= 1;
if (controls.release(C_LEFT)) {
if (option) levelNum = (levelNum + 9) % 10;
else worldNum = (worldNum + 999) % 1000;
}
if (controls.release(C_RIGHT)) {
if (option) levelNum = (levelNum + 1) % 10;
else worldNum = (worldNum + 1) % 1000;
}
if (controls.release(C_ENTER)) {
playSound(S_ORB);
if (newGameDifficulty(mode, levelNum, worldNum) == E_QUIT)
return E_QUIT;
usePalette(palettes[1]);
}
}
return E_NONE;
}
int Menu::newGameEpisode (int mode) {
char *options[12] = {"episode 1", "episode 2", "episode 3", "episode 4",
"episode 5", "episode 6", "episode a", "episode b", "episode c",
"episode x", "bonus stage", "specific level"};
bool exists[12];
char *check;
SDL_Rect dst;
int episode, count, worldNum;
usePalette(palettes[2]);
for (count = 0; count < 10; count++) {
if (count < 6) worldNum = count * 3;
else if ((count >= 6) && (count < 9)) worldNum = (count + 4) * 3;
else worldNum = 50;
check = createFileName(F_LEVEL, 0, worldNum);
exists[count] = fileExists(check);
delete[] check;
if (exists[count]) restorePalette(screens[count + 3]);
else
SDL_SetPalette(screens[count + 3], SDL_LOGPAL, palettes[3], 0, 256);
}
exists[10] = false;
exists[11] = true;
episode = 0;
while (true) {
if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT;
if (controls.release(C_ESCAPE)) return E_NONE;
SDL_Delay(T_FRAME);
clearScreen(0);
if ((episode < episodes - 1) || (episode < 6)) {
dst.x = screenW - 150;
dst.y = (screenH - 110) >> 1;
SDL_BlitSurface(screens[episode + 3], NULL, screen, &dst);
} else if ((episode == 10) && (episodes > 6)) {
dst.x = screenW - 160;
dst.y = (screenH - 110) >> 1;
SDL_BlitSurface(screens[episodes + 2], NULL, screen, &dst);
}
for (count = 0; count < 12; count++) {
if (count == episode) {
fontmn2->mapPalette(240, 8, 79, -80);
drawRect((screenW >> 3) - 4, (screenH >> 1) + (count << 4) - 94,
136, 15, 79);
} else if (!exists[count])
fontmn2->mapPalette(240, 8, 94, -16);
fontmn2->showString(options[count], screenW >> 3,
(screenH >> 1) + (count << 4) - 92);
if ((count == episode) || (!exists[count]))
fontmn2->mapPalette(240, 8, 9, 80);
}
if (controls.release(C_UP)) episode = (episode + 11) % 12;
if (controls.release(C_DOWN)) episode = (episode + 1) % 12;
if (controls.release(C_ENTER)) {
playSound(S_ORB);
if (exists[episode]) {
if (episode < 10) {
if (episode < 6) worldNum = episode * 3;
else if ((episode >= 6) && (episode < 9))
worldNum = (episode + 4) * 3;
else worldNum = 50;
if (newGameDifficulty(mode, 0, worldNum) == E_QUIT)
return E_QUIT;
} else if (episode == 10) {
// TODO: Loading and playing of bonus levels
} else {
if (newGameLevel(mode) == E_QUIT) return E_QUIT;
}
usePalette(palettes[2]);
}
}
}
return E_NONE;
}
int Menu::joinGame () {
int ret;
ret = textInput("ip address:", &netAddress);
if (ret < 0) return ret;
try {
game = new ClientGame(netAddress);
} catch (int e) {
switch (e) {
case E_N_SOCKET:
message("SOCKET ERROR");
break;
case E_N_ADDRESS:
message("INVALID ADDRESS");
break;
case E_N_CONNECT:
message("COULD NOT CONNECT");
break;
case E_TIMEOUT:
message("OPERATION TIMED OUT");
break;
case E_DATA:
message("INCORRECT DATA\nRECEIVED");
break;
case E_VERSION:
message("WRONG SERVER VERSION");
break;
case E_UNUSED:
case E_QUIT:
break;
default:
message("COULD COMPLETE CONNECTION");
break;
}
return e;
}
// Play the level(s)
switch (game->play()) {
case E_QUIT:
delete game;
return E_QUIT;
case E_FILE:
message("FILE NOT FOUND");
break;
}
delete game;
return E_NONE;
}
int Menu::loadGame () {
// TODO: Actual loading of saved games
return newGameLevel(M_SINGLE);
}
/*
*
* mainmenu.cpp
*
* 19th July 2009: Created menumain.cpp from parts of menu.cpp
* 26th July 2009: Renamed menumain.cpp to mainmenu.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Deals with the running of the main menu and its generic sub-menus.
*
*/
#include "menu.h"
#include "game/game.h"
#include "game/gamemode.h"
#include "io/controls.h"
#include "io/gfx/video.h"
#include "io/sound.h"
#include "level/level.h"
#include "player/player.h"
#include "scene.h"
int Menu::main () {
char *newGameOptions[6] = {"new single player game", "new co-op game",
"new battle", "new team battle", "new race", "join game"};
Scene *scene;
SDL_Rect src, dst;
int option, suboption;
unsigned int idleTime;
int ret;
option = suboption = 0;
usePalette(palettes[0]);
// Demo timeout
idleTime = globalTicks + T_DEMO;
while (true) {
if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT;
if (controls.release(C_ESCAPE)) option = 5;
if (controls.release(C_UP)) option = (option + 5) % 6;
if (controls.release(C_DOWN)) option = (option + 1) % 6;
if (controls.release(C_ENTER)) {
playSound(S_ORB);
switch(option) {
case 0: // New game
while (true) {
ret = generic(newGameOptions, 6, &suboption);
if (ret == E_QUIT) return E_QUIT;
if (ret < 0) break;
if (suboption == 5) {
if (joinGame() == E_QUIT) return E_QUIT;
} else {
if (newGameEpisode(suboption) == E_QUIT)
return E_QUIT;
}
}
break;
case 1: // Load game
if (loadGame() == E_QUIT) return E_QUIT;
break;
case 2: // Instructions
try {
scene = new Scene("instruct.0sc");
} catch (int e) {
message("COULD NOT LOAD INSTRUCTIONS");
break;
}
if (scene->play() == E_QUIT) {
delete scene;
return E_QUIT;
}
delete scene;
break;
case 3: // Setup options
if (setup() == E_QUIT) return E_QUIT;
break;
case 4: // Order info
try {
scene = new Scene("order.0sc");
} catch (int e) {
message("COULD NOT LOAD ORDER INFO");
break;
}
if (scene->play() == E_QUIT) {
delete scene;
return E_QUIT;
}
delete scene;
break;
case 5: // Exit
return E_NONE;
}
// Restore the main menu palette
usePalette(palettes[0]);
// New demo timeout
idleTime = globalTicks + T_DEMO;
}
if (idleTime <= globalTicks) {
game = NULL;
// Create the player
nPlayers = 1;
localPlayer = players = new Player[1];
localPlayer->init(characterName, NULL, 0);
// Load the macro
try {
level = new DemoLevel("macro.2");
} catch (int e) {
delete[] players;
localPlayer = NULL;
break;
}
// Play the level
if (level->play() == E_QUIT) {
delete level;
delete[] players;
return E_QUIT;
}
delete level;
delete[] players;
localPlayer = NULL;
playMusic("menusng.psm");
// Restore the main menu palette
usePalette(palettes[0]);
idleTime = globalTicks + T_DEMO;
}
SDL_Delay(T_FRAME);
clearScreen(28);
dst.x = (screenW >> 2) - 72;
dst.y = screenH - (screenH >> 2);
SDL_BlitSurface(screens[14], NULL, screen, &dst);
dst.x = (screenW - 320) >> 1;
dst.y = (screenH - 200) >> 1;
SDL_BlitSurface(screens[0], NULL, screen, &dst);
switch (option) {
case 0:
src.x = 92;
src.y = 35;
src.w = 136;
src.h = 22;
break;
case 1:
src.x = 92;
src.y = 57;
src.w = 140;
src.h = 22;
break;
case 2:
src.x = 88;
src.y = 83;
src.w = 144;
src.h = 22;
break;
case 3:
src.x = 86;
src.y = 109;
src.w = 150;
src.h = 23;
break;
case 4:
src.x = 82;
src.y = 137;
src.w = 156;
src.h = 26;
break;
case 5:
src.x = 78;
src.y = 166;
src.w = 166;
src.h = 29;
break;
}
dst.x = ((screenW - 320) >> 1) + src.x;
dst.y = ((screenH - 200) >> 1) + src.y;
SDL_BlitSurface(screens[1], &src, screen, &dst);
}
return E_NONE;
}
/*
*
* menu.cpp
*
* 23rd of August 2005: Created menu.c
* 3rd of February 2009: Renamed menu.c to menu.cpp
* 9th March 2009: Created game.cpp from parts of menu.cpp and level.cpp
* 18th July 2009: Created menugame.cpp from parts of menu.cpp
* 18th July 2009: Created menuutil.cpp from parts of menu.cpp
* 18th July 2009: Created menusetup.cpp from parts of menu.cpp
* 19th July 2009: Created menumain.cpp from parts of menu.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Deals with the loading and freeing of the menu data.
*
*/
#include "menu.h"
#include "game/game.h"
#include "io/file.h"
#include "io/gfx/video.h"
#include "io/sound.h"
#include <time.h>
Menu::Menu () {
File *file;
unsigned char *pixels;
time_t currentTime;
int count, col;
// Load the OpenJazz logo
try {
file = new File(LOGO_FILE, false);
} catch (int e) {
throw e;
}
screens[14] = file->loadSurface(64, 40);
delete file;
// Load the menu graphics
try {
file = new File(F_MENU, false);
} catch (int e) {
SDL_FreeSurface(screens[14]);
throw e;
}
file->seek(0, true);
// Load the main menu graphics
file->loadPalette(palettes[0]);
screens[0] = file->loadSurface(320, 200);
screens[1] = file->loadSurface(320, 200);
if (file->getSize() > 200000) {
time(&currentTime);
// In December, load the Christmas menu graphics
if (localtime(&currentTime)->tm_mon == 11) {
SDL_FreeSurface(screens[0]);
SDL_FreeSurface(screens[1]);
file->loadPalette(palettes[0]);
screens[0] = file->loadSurface(320, 200);
screens[1] = file->loadSurface(320, 200);
} else {
file->skipRLE();
file->skipRLE();
file->skipRLE();
}
}
SDL_SetColorKey(screens[0], SDL_SRCCOLORKEY, 0);
SDL_SetColorKey(screens[1], SDL_SRCCOLORKEY, 0);
// Load the difficulty graphics
file->loadPalette(palettes[1]);
screens[2] = file->loadSurface(320, 200);
SDL_SetColorKey(screens[2], SDL_SRCCOLORKEY, 0);
// Default difficulty setting
difficulty = 1;
// Load the episode pictures (max. 10 episodes + bonus level)
// Load their palette
file->loadPalette(palettes[2]);
// Generate a greyscale mapping
for (count = 0; count < 256; count++) {
col = ((palettes[2][count].r >> 1) + (palettes[2][count].g << 1) +
(palettes[2][count].b >> 1)) >> 3;
if (col > 79) col = 79;
palettes[3][count].r = palettes[3][count].g = palettes[3][count].b =
col;
}
episodes = 11;
for (count = 0; count < 11; count++) {
screens[count + 3] = file->loadSurface(134, 110);
if (file->tell() >= file->getSize()) {
episodes = ++count;
for (; count < 11; count++) {
pixels = new unsigned char[1];
*pixels = 0;
screens[count + 3] = createSurface(pixels, 1, 1);
}
}
}
delete file;
playMusic("menusng.psm");
return;
}
Menu::~Menu () {
int count;
for (count = 0; count < 15; count++) SDL_FreeSurface(screens[count]);
return;
}
/*
*
* menu.h
*
* 3rd February 2009: Created menu.h from parts of OpenJazz.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _MENU_H
#define _MENU_H
#include "OpenJazz.h"
#include <SDL/SDL.h>
// Constant
// Demo timeout
#define T_DEMO 20000
// Class
class Menu {
private:
SDL_Surface *screens[15];
int episodes;
unsigned char difficulty;
int message (char *text);
int generic (char **optionNames, int options, int *chosen);
int textInput (char *request, char **text);
int newGameDifficulty (int mode, int levelNum, int worldNum);
int newGameLevel (int mode);
int newGameEpisode (int mode);
int joinGame ();
int loadGame ();
int setupKeyboard ();
int setupJoystick ();
int setupResolution ();
public:
SDL_Color palettes[4][256];
Menu ();
~Menu ();
int setup ();
int main ();
};
// Variable
EXTERN Menu *menu;
#endif
/*
*
* menuutil.cpp
*
* 18th July 2009: Created menuutil.cpp from parts of menu.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Provides various generic menus.
*
*/
#include "menu.h"
#include "io/controls.h"
#include "io/gfx/font.h"
#include "io/gfx/video.h"
#include "io/sound.h"
#include <string.h>
int Menu::message (char *text) {
// Display a message to the user
usePalette(palettes[1]);
while (true) {
if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT;
if (controls.release(C_ENTER) || controls.release(C_ESCAPE))
return E_NONE;
SDL_Delay(T_FRAME);
clearScreen(15);
// Draw the message
fontmn2->showString(text, screenW >> 2, (screenH >> 1) - 16);
}
return E_NONE;
}
int Menu::generic (char **optionNames, int options, int *chosen) {
// Let the user select from a menu of the given options
int count;
usePalette(palettes[1]);
if (*chosen >= options) *chosen = 0;
while (true) {
if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT;
if (controls.release(C_ESCAPE)) return E_UNUSED;
SDL_Delay(T_FRAME);
clearScreen(0);
for (count = 0; count < options; count++) {
if (count == *chosen) fontmn2->mapPalette(240, 8, 114, 16);
fontmn2->showString(optionNames[count], screenW >> 2,
(screenH >> 1) + (count << 4) - (options << 3));
if (count == *chosen) fontmn2->restorePalette();
}
if (controls.release(C_UP)) *chosen = (*chosen + options - 1) % options;
if (controls.release(C_DOWN)) *chosen = (*chosen + 1) % options;
if (controls.release(C_ENTER)) {
playSound(S_ORB);
return E_NONE;
}
}
return E_NONE;
}
int Menu::textInput (char *request, char **text) {
// Let the user to edit a text string
char *input;
int count, terminate, character, x;
unsigned int cursor;
// Create input string
input = createEditableString(*text);
cursor = strlen(input);
while (true) {
character = loop(KEY_LOOP);
if (character == E_QUIT) {
delete[] input;
return E_QUIT;
}
// Ensure there is space for another character
if (cursor < STRING_LENGTH) {
terminate = (input[cursor] == 0);
// If the character is valid, add it to the input string
if ((character == ' ') || (character == '.') ||
((character >= '0') && (character <= '9')) ||
((character >= 'a') && (character <= 'z'))) {
input[cursor] = character;
cursor++;
if (terminate) input[cursor] = 0;
} else if ((character >= 'A') && (character <= 'Z')) {
input[cursor] = character | 32;
cursor++;
if (terminate) input[cursor] = 0;
}
}
if ((character == SDLK_DELETE) && (cursor < strlen(input))) {
for (count = cursor; count < STRING_LENGTH; count++)
input[count] = input[count + 1];
}
if ((character == SDLK_BACKSPACE) && (cursor > 0)) {
for (count = cursor - 1; count < STRING_LENGTH; count++)
input[count] = input[count + 1];
cursor--;
}
if (controls.release(C_ESCAPE)) {
delete[] input;
return E_UNUSED;
}
SDL_Delay(T_FRAME);
clearScreen(15);
// Draw the prompt
fontmn2->showString(request, screenW >> 2, (screenH >> 1) - 16);
// Draw the section of the text before the cursor
fontmn2->mapPalette(240, 8, 114, 16);
terminate = input[cursor];
input[cursor] = 0;
x = fontmn2->showString(input, (screenW >> 2) + 8, screenH >> 1);
// Draw the cursor
drawRect(x, (screenH >> 1) + 10, 8, 2, 79);
// Draw the section of text after the cursor
input[cursor] = terminate;
fontmn2->showString(input + cursor, x, screenH >> 1);
fontmn2->restorePalette();
if (controls.release(C_LEFT) && (cursor > 0)) cursor--;
if (controls.release(C_RIGHT) && (cursor < strlen(input))) cursor++;
if (controls.release(C_ENTER)) {
playSound(S_ORB);
// Replace the original string with the input string
delete[] *text;
*text = input;
return E_NONE;
}
}
delete[] input;
return E_UNUSED;
}
/*
*
* setupmenu.cpp
*
* 18th July 2009: Created menusetup.cpp from parts of menu.cpp
* 26th July 2009: Renamed menusetup.cpp to setupmenu.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Deals with the running of setup menus.
*
*/
#include "menu.h"
#include "io/controls.h"
#include "io/gfx/font.h"
#include "io/gfx/video.h"
#include "io/sound.h"
#include "player/player.h"
int Menu::setupKeyboard () {
char *options[7] = {"up", "down", "left", "right", "jump", "fire",
"weapon"};
int progress, count, character;
bool used;
progress = 0;
while (true) {
character = loop(KEY_LOOP);
if (character == E_QUIT) return E_QUIT;
if (character > 0) {
used = false;
// Check if key is already in use
for (count = 0; count < CONTROLS; count++)
if (character == controls.getKey(count)) {
if (count != progress) used = true;
}
// If not, assign it to the current control
if (!used) {
controls.setKey(progress, character);
progress++;
if (progress == 7) {
// If all controls have been assigned, return
playSound(S_ORB);
return E_NONE;
}
}
}
if (controls.release(C_ESCAPE)) return E_NONE;
SDL_Delay(T_FRAME);
clearScreen(0);
for (count = 0; count < 7; count++) {
if (count < progress)
fontmn2->showString("okay", (screenW >> 2) + 176,
(screenH >> 1) + (count << 4) - 56);
else if (count == progress) fontmn2->mapPalette(240, 8, 114, 16);
fontmn2->showString(options[count], screenW >> 2,
(screenH >> 1) + (count << 4) - 56);
if (count == progress) {
fontmn2->showString("press key", (screenW >> 2) + 112,
(screenH >> 1) + (count << 4) - 56);
fontmn2->restorePalette();
}
}
}
return E_NONE;
}
int Menu::setupJoystick () {
char *options[7] = {"up", "down", "left", "right", "jump", "fire",
"weapon"};
int progress, count, control;
bool used;
progress = 0;
while (true) {
control = loop(JOYSTICK_LOOP);
if (control == E_QUIT) return E_QUIT;
switch (control & 0xF00) {
case JOYSTICKB:
used = false;
// Check if the button is already in use
for (count = 0; count < CONTROLS; count++)
if ((control & 0xFF) == controls.getButton(count)) {
if (count != progress) used = true;
}
// If not, assign it to the current control
if (!used) {
controls.setButton(progress, control & 0xFF);
progress++;
if (progress == 7) {
// If all controls have been assigned, return
playSound(S_ORB);
return E_NONE;
}
}
break;
case JOYSTICKANEG:
used = false;
// Check if the arrow is already in use
for (count = 0; count < CONTROLS; count++)
if (((control & 0xFF) == controls.getAxis(count)) &&
!controls.getAxisDirection(count)) {
if (count != progress) used = true;
}
// If not, assign it to the current control
if (!used) {
controls.setAxis(progress, control & 0xFF, false);
progress++;
if (progress == 7) {
// If all controls have been assigned, return
playSound(S_ORB);
return E_NONE;
}
}
break;
case JOYSTICKAPOS:
used = false;
// Check if the arrow is already in use
for (count = 0; count < CONTROLS; count++)
if (((control & 0xFF) == controls.getAxis(count)) &&
controls.getAxisDirection(count)) {
if (count != progress) used = true;
}
// If not, assign it to the current control
if (!used) {
controls.setAxis(progress, control & 0xFF, true);
progress++;
if (progress == 7) {
// If all controls have been assigned, return
playSound(S_ORB);
return E_NONE;
}
}
break;
}
if (controls.release(C_ESCAPE)) return E_NONE;
SDL_Delay(T_FRAME);
clearScreen(0);
for (count = 0; count < 7; count++) {
if (count < progress)
fontmn2->showString("okay", (screenW >> 2) + 176,
(screenH >> 1) + (count << 4) - 56);
else if (count == progress) fontmn2->mapPalette(240, 8, 114, 16);
fontmn2->showString(options[count], screenW >> 2,
(screenH >> 1) + (count << 4) - 56);
if (count == progress) {
fontmn2->showString("press control", (screenW >> 2) + 112,
(screenH >> 1) + (count << 4) - 56);
fontmn2->restorePalette();
}
}
}
return E_NONE;
}
int Menu::setupResolution () {
#ifndef FULLSCREEN_ONLY
int widthOptions[] = {320, 400, 512, 640, 720, 768, 800, 960, 1024, 1152,
1280, 1440, 1600, 1920};
int heightOptions[] = {200, 240, 300, 384, 400, 480, 576, 600, 720, 768,
800, 864, 900, 960, 1024, 1080, 1200};
SDL_Rect **resolutions;
int dimension, count, maxW, maxH;
dimension = 0;
if (fullscreen)
resolutions = SDL_ListModes(NULL,
SDL_FULLSCREEN | SDL_DOUBLEBUF | SDL_HWSURFACE | SDL_HWPALETTE);
else
resolutions = SDL_ListModes(NULL,
SDL_RESIZABLE | SDL_DOUBLEBUF | SDL_HWSURFACE | SDL_HWPALETTE);
if (resolutions == (SDL_Rect **)(-1)) {
maxW = 1920;
maxH = 1200;
} else {
maxW = 320;
maxH = 200;
for (count = 0; resolutions[count] != NULL; count++) {
if (resolutions[count]->w > maxW) maxW = resolutions[count]->w;
if (resolutions[count]->h > maxH) maxH = resolutions[count]->h;
}
}
while (true) {
if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT;
if (controls.release(C_ESCAPE)) return E_NONE;
if (controls.release(C_ENTER)) return E_NONE;
SDL_Delay(T_FRAME);
clearScreen(0);
// Show screen corners
drawRect(0, 0, 32, 32, 79);
drawRect(screenW - 32, 0, 32, 32, 79);
drawRect(screenW - 32, screenH - 32, 32, 32, 79);
drawRect(0, screenH - 32, 32, 32, 79);
fontmn2->showString("x", (screenW >> 2) + 40, screenH >> 1);
if (dimension == 0) fontmn2->mapPalette(240, 8, 114, 16);
fontmn2->showNumber(screenW, (screenW >> 2) + 32, screenH >> 1);
if (dimension == 0) fontmn2->restorePalette();
else fontmn2->mapPalette(240, 8, 114, 16);
fontmn2->showNumber(screenH, (screenW >> 2) + 104, screenH >> 1);
if (dimension != 0) fontmn2->restorePalette();
count = 0;
if (controls.release(C_LEFT)) dimension = !dimension;
if (controls.release(C_RIGHT)) dimension = !dimension;
if (controls.release(C_UP)) {
if ((dimension == 0) && (screenW < maxW)) {
while (screenW >= widthOptions[count]) count++;
screenW = widthOptions[count];
}
if ((dimension == 1) && (screenH < maxH)) {
while (screenH >= heightOptions[count]) count++;
screenH = heightOptions[count];
}
}
if (controls.release(C_DOWN)) {
if ((dimension == 0) && (screenW > 320)) {
count = 13;
while (screenW <= widthOptions[count]) count--;
screenW = widthOptions[count];
count = -1;
}
if ((dimension == 1) && (screenH > 200)) {
count = 16;
while (screenH <= heightOptions[count]) count--;
screenH = heightOptions[count];
count = -1;
}
}
// Check for a resolution change
if (count) {
playSound(S_ORB);
screen = SDL_SetVideoMode(screenW, screenH, 8,
(fullscreen? SDL_FULLSCREEN: SDL_RESIZABLE) | SDL_DOUBLEBUF |
SDL_HWSURFACE | SDL_HWPALETTE);
SDL_SetPalette(screen, SDL_LOGPAL, logicalPalette, 0, 256);
SDL_SetPalette(screen, SDL_PHYSPAL, currentPalette, 0, 256);
}
}
#endif
return E_NONE;
}
int Menu::setup () {
char *setupOptions[4] = {"character", "keyboard", "joystick", "resolution"};
char *setupCharacterOptions[5] = {"name", "fur", "bandana", "gun",
"wristband"};
char *setupCharacterColOptions[8] = {"white", "red", "orange", "yellow",
"green", "blue", "animation 1", "animation 2"};
unsigned char setupCharacterCols[8] = {PC_WHITE, PC_RED, PC_ORANGE,
PC_YELLOW, PC_LGREEN, PC_BLUE, PC_SANIM, PC_LANIM};
int ret;
int option, suboption, subsuboption;
option = 0;
while (true) {
ret = generic(setupOptions, 4, &option);
if (ret == E_UNUSED) return E_NONE;
if (ret < 0) return ret;
switch (option) {
case 0:
suboption = 0;
while (true) {
ret = generic(setupCharacterOptions, 5, &suboption);
if (ret == E_QUIT) return E_QUIT;
if (ret < 0) break;
switch (suboption) {
case 0: // Character name
textInput("character name:", &characterName);
break;
default: // Character colour
subsuboption = 0;
ret = generic(setupCharacterColOptions, 8,
&subsuboption);
if (ret == E_QUIT) return E_QUIT;
if (ret == E_NONE)
characterCols[suboption - 1] =
setupCharacterCols[subsuboption];
break;
}
}
break;
case 1:
setupKeyboard();
break;
case 2:
setupJoystick();
break;
case 3:
setupResolution();
break;
}
}
return E_NONE;
}
/*
*
* planet.cpp
*
* 23rd August 2005: Created planet.c
* 3rd February 2009: Renamed planet.c to planet.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Deals with the loading, displaying and freeing of the planet landing
* sequence.
*
*/
#include "planet.h"
#include "io/controls.h"
#include "io/file.h"
Planet::Planet (char * fileName) {
File *file;
try {
file = new File(fileName, false);
} catch (int e) {
throw e;
}
// TODO: Load planet file
delete file;
return;
}
Planet::~Planet () {
// Nothing to do
return;
}
int Planet::play () {
while (true) {
if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT;
if (controls.release(C_ESCAPE)) return E_NONE;
// TODO: Display planet
}
return E_NONE;
}
/*
*
* planet.h
*
* 3rd of February 2009: Created planet.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _PLANET_H
#define _PLANET_H
// Class
class Planet {
public:
Planet (char * fileName);
~Planet ();
int play ();
};
#endif
/*
*
* bird.cpp
*
* 1st March 2009: Created bird.cpp from parts of events.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "bird.h"
#include "player.h"
#include "io/gfx/video.h"
#include "level/bullet.h"
#include "level/event/event.h"
#include "level/level.h"
Bird::Bird (Player *rescuer, unsigned char gX, unsigned char gY) {
player = rescuer;
x = TTOF(gX);
y = TTOF(gY);
dx = 0;
dy = 0;
fleeing = false;
fireTime = 0;
return;
}
Bird::~Bird () {
return;
}
void Bird::reset () {
x = player->getX();
y = player->getY() - F64;
fireTime = 0;
return;
}
Player * Bird::getPlayer () {
return player;
}
void Bird::hit () {
fleeing = true;
return;
}
fixed Bird::getX () {
return x;
}
fixed Bird::getY () {
return y;
}
bool Bird::playFrame (int ticks) {
Event *nextEvent;
fixed eventX, eventY;
bool target;
if (fleeing) {
// Trajectory for flying away
dx = F80;
dy = -F80;
// If the bird has flown off-screen, remove it
if (y < viewY - F160) return true;
} else {
// Trajectory for flying towards the player
if ((x < player->getX() - F160) || (x > player->getX() + F160)) {
// Far away from the player
// Approach the player at a speed proportional to the distance
dx = player->getX() - x;
} else if (x < player->getX()) {
// To the left of the player, so move right
if (dx < F160) dx += 400 * mspf;
} else {
// To the right of the player, so move left
if (dx > -F160) dx -= 400 * mspf;
}
if (y > level->getWaterLevel(ticks) - F24) {
// Always stay above water
y = level->getWaterLevel(ticks) - F24;
dy = 0;
} else {
if ((y < player->getY() - F100) || (y > player->getY() + F100)) {
// Far away from the player
// Approach the player at a speed proportional to the distance
dy = (player->getY() - F64) - y;
} else if (y < player->getY() - F64) {
// Above the player, so move downwards
if (dy < F160) dy += 400 * mspf;
} else {
// Below the player, so move upwards
if (dy > -F160) dy -= 400 * mspf;
}
}
if (ticks > fireTime) {
// Check for nearby targets
target = false;
nextEvent = level->firstEvent;
if (player->getFacing()) {
while (nextEvent) {
eventX = nextEvent->getX();
eventY = nextEvent->getY();
if (nextEvent->getProperty(E_HITSTOKILL) &&
(eventX > x) && (eventX < x + F160) && (eventY > y) &&
(eventY < y + F100)) {
target = true;
break;
}
nextEvent = nextEvent->getNext();
}
} else {
while (nextEvent) {
eventX = nextEvent->getX();
eventY = nextEvent->getY();
if (nextEvent->getProperty(E_HITSTOKILL) &&
(eventX > x - F160) && (eventX < x) && (eventY > y) &&
(eventY < y + F100)) {
target = true;
break;
}
nextEvent = nextEvent->getNext();
}
}
// If there is a target in the vicinity, generate bullets
if (target) {
level->firstBullet = new Bullet(this, false, ticks);
fireTime = ticks + T_BIRD_FIRE;
}
}
}
// Apply trajectory
x += (dx * mspf) >> 10;
y += (dy * mspf) >> 10;
return false;
}
void Bird::draw (int ticks) {
Anim *anim;
anim = level->getAnim((player->getFacing() || fleeing)? BIRD_RIGHTANIM:
BIRD_LEFTANIM);
anim->setFrame(ticks / 80, true);
anim->draw(x, y);
return;
}
/*
*
* bird.h
*
* 1st March 2009: Created bird.h from parts of events.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _BIRD_H
#define _BIRD_H
#include "OpenJazz.h"
// Constants
// Animations
#define BIRD_LEFTANIM 51
#define BIRD_RIGHTANIM 52
// Time interval
#define T_BIRD_FIRE 500
// Classes
class Player;
class Bird {
private:
Player *player;
fixed x, y, dx, dy;
bool fleeing;
int fireTime;
public:
Bird (Player *player, unsigned char gX, unsigned char gY);
~Bird ();
void reset ();
Player * getPlayer ();
void hit ();
fixed getX ();
fixed getY ();
bool playFrame (int ticks);
void draw (int ticks);
};
#endif
/*
*
* player.cpp
*
* 3rd February 2009: Created player.cpp
* 5th February 2009: Added parts of events.cpp and level.cpp to player.cpp
* 19th March 2009: Created sprite.cpp from parts of event.cpp and player.cpp
* 18th July 2009: Created playerframe.cpp from parts of player.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Deals with the creation and destruction of players, and their interactions
* with other objects.
*
*/
#include "bird.h"
#include "player.h"
#include "game/game.h"
#include "game/gamemode.h"
#include "io/controls.h"
#include "io/gfx/font.h"
#include "io/gfx/paletteeffects.h"
#include "io/gfx/video.h"
#include "io/sound.h"
#include "level/event/event.h"
#include "level/level.h"
#include <string.h>
Player::Player () {
bird = NULL;
name = NULL;
return;
}
Player::~Player () {
deinit();
return;
}
void Player::init (char *playerName, unsigned char *playerCols,
unsigned char newTeam) {
int offsets[15] = {PC_WHITE, PC_SGREEN, PC_BLUE, PC_RED, PC_LGREEN,
PC_LEVEL1, PC_YELLOW, PC_LEVEL2, PC_ORANGE, PC_LEVEL3, PC_LEVEL4,
PC_SANIM, PC_LANIM, PC_LEVEL5, 256};
int count, start, length;
// Clear existing player
deinit();
// Assign name
name = createString(playerName);
// Assign initial values
memset(anims, 0, PANIMS);
score = 0;
lives = 3;
ammoType = -1;
ammo[0] = 0;
ammo[1] = 0;
ammo[2] = 0;
ammo[3] = 0;
fireSpeed = 0;
team = newTeam;
teamScore = 0;
// Create the player's palette
for (count = 0; count < 256; count++)
palette[count].r = palette[count].g = palette[count].b = count;
if (playerCols == NULL) return;
memcpy(cols, playerCols, 4);
// Fur colours
count = 0;
while (cols[0] >= offsets[count + 1]) count++;
start = offsets[count];
length = offsets[count + 1] - start;
for (count = 0; count < 16; count++)
palette[count + 48].r = palette[count + 48].g = palette[count + 48].b =
(count * length / 16) + start;
// Bandana colours
count = 0;
while (cols[1] >= offsets[count + 1]) count++;
start = offsets[count];
length = offsets[count + 1] - start;
for (count = 0; count < 16; count++)
palette[count + 32].r = palette[count + 32].g = palette[count + 32].b =
(count * length / 16) + start;
// Gun colours
count = 0;
while (cols[2] >= offsets[count + 1]) count++;
start = offsets[count];
length = offsets[count + 1] - start;
for (count = 0; count < 9; count++)
palette[count + 23].r = palette[count + 23].g = palette[count + 23].b =
(count * length / 9) + start;
// Wristband colours
count = 0;
while (cols[3] >= offsets[count + 1]) count++;
start = offsets[count];
length = offsets[count + 1] - start;
for (count = 0; count < 8; count++)
palette[count + 88].r = palette[count + 88].g = palette[count + 88].b =
(count * length / 8) + start;
return;
}
void Player::deinit () {
if (bird) delete bird;
bird = NULL;
if (name) delete[] name;
name = NULL;
return;
}
void Player::setAnims (char *newAnims) {
memcpy(anims, newAnims, PANIMS);
return;
}
char * Player::getName () {
return name;
}
unsigned char * Player::getCols () {
return cols;
}
void Player::reset () {
int count;
if (bird) bird->reset();
event = NULL;
for (count = 0; count < PCONTROLS; count++) pcontrols[count] = false;
energy = 4;
shield = 0;
floating = false;
facing = true;
reaction = PR_NONE;
reactionTime = 0;
jumpHeight = ITOF(92);
jumpY = TTOF(LH);
fastFeetTime = 0;
warpTime = 0;
dx = 0;
dy = 0;
enemies = items = 0;
return;
}
void Player::setControl (int control, bool state) {
pcontrols[control] = state;
return;
}
bool Player::shootEvent (unsigned char gridX, unsigned char gridY, int ticks) {
signed char *set;
set = level->getEvent(gridX, gridY);
addScore(set[E_ADDEDSCORE]);
switch (set[E_MODIFIER]) {
case 41: // Bonus level
if (getEnergy()) level->setNext(set[E_MULTIPURPOSE], set[E_YAXIS]);
// The lack of a break statement is intentional
case 8: // Boss
case 27: // End of level
if (getEnergy()) {
if (!gameMode) {
if (game) game->setCheckpoint(gridX, gridY);
level->setStage(LS_END);
} else return gameMode->endOfLevel(this, gridX, gridY);
}
break;
case 10: // Checkpoint
if (game) game->setCheckpoint(gridX, gridY);
break;
case 15:
addAmmo(0, 15);
break;
case 16:
addAmmo(1, 15);
break;
case 17:
addAmmo(2, 15);
break;
case 26:
fastFeetTime = ticks + T_FASTFEET;
break;
case 33:
if (shield < 2) shield = 2;
break;
case 34: // Bird
if (!bird) bird = new Bird(this, gridX, gridY);
break;
case 36:
shield = 6;
break;
}
// 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++;
return true;
}
bool Player::touchEvent (unsigned char gridX, unsigned char gridY, int ticks) {
signed char *set;
set = level->getEvent(gridX, gridY);
switch (set[E_MODIFIER]) {
case 0: // Hurt
case 8: // Boss
if ((set[E_BEHAVIOUR] < 37) || (set[E_BEHAVIOUR] > 44))
hit(NULL, ticks);
break;
case 1: // Invincibility
if (getEnergy()) {
reaction = PR_INVINCIBLE;
reactionTime = ticks + PRT_INVINCIBLE;
addScore(set[E_ADDEDSCORE]);
return true;
}
break;
case 2:
case 3: // Health
if (energy < 4) energy++;
addScore(set[E_ADDEDSCORE]);
return true;
case 4: // Extra life
if (lives < 99) lives++;
addScore(set[E_ADDEDSCORE]);
return true;
case 5: // High-jump feet
jumpHeight += F16;
return true;
case 9: // Sand timer
level->addTimer();
addScore(set[E_ADDEDSCORE]);
return true;
case 11: // Item
addScore(set[E_ADDEDSCORE]);
return true;
case 12: // Rapid fire
fireSpeed++;
return true;
case 13: // Warp
if (!warpTime) {
warpX = set[E_MULTIPURPOSE];
warpY = set[E_YAXIS];
warpTime = ticks + T_WARP;
// White flash
firstPE =
new FlashPaletteEffect(255, 255, 255, T_WARP, firstPE);
}
break;
case 18: // Ammo
addAmmo(0, 2);
addScore(set[E_ADDEDSCORE]);
return true;
case 19: // Ammo
addAmmo(1, 2);
addScore(set[E_ADDEDSCORE]);
return true;
case 20: // Ammo
addAmmo(2, 2);
addScore(set[E_ADDEDSCORE]);
return true;
case 29: // Upwards spring
setEvent(set);
level->playSound(set[E_SOUND]);
break;
case 30: // TNT
addAmmo(3, 1);
return true;
case 31: // Water level
if (!set[E_HITSTOKILL]) level->setWaterLevel(gridY);
break;
case 35: // Airboard, etc.
floating = true;
addScore(set[E_ADDEDSCORE]);
return true;
case 37: // Diamond
// Yellow flash
firstPE = new FlashPaletteEffect(255, 255, 0, 320, firstPE);
return true;
case 38: // Airboard, etc. off
floating = false;
break;
}
return false;
}
bool Player::hit (Player *source, int ticks) {
// Invulnerable if reacting to e.g. having been hit
if (reaction != PR_NONE) return false;
// Hits from the same team have no effect
if (source && (source->getTeam() == team)) return false;
if (shield == 3) shield = 0;
else if (shield) shield--;
else if (!gameMode || gameMode->hit(source, this)) {
energy--;
if (bird) bird->hit();
playSound(S_OW);
}
if (energy) {
reaction = PR_HURT;
reactionTime = ticks + PRT_HURT;
if (dx < 0) {
dx = PXS_RUN;
dy = PYS_JUMP;
} else {
dx = -PXS_RUN;
dy = PYS_JUMP;
}
} else {
kill(source, ticks);
}
return true;
}
void Player::kill (Player *source, int ticks) {
if (reaction != PR_NONE) return;
if (!gameMode || gameMode->kill(source, this)) {
energy = 0;
lives--;
reaction = PR_KILLED;
reactionTime = ticks + PRT_KILLED;
}
if (!gameMode) firstPE = new FadeOutPaletteEffect(T_END, firstPE);
return;
}
void Player::addScore (int addedScore) {
score += addedScore * 10;
return;
}
int Player::getScore () {
return score;
}
int Player::getEnergy () {
return energy;
}
int Player::getLives () {
return lives;
}
void Player::addAmmo (int type, int amount) {
if (!ammo[type]) ammoType = type;
ammo[type] += amount;
return;
}
int Player::getAmmo (bool amount) {
return amount? ammo[ammoType]: ammoType;
}
int Player::getEnemies () {
return enemies;
}
int Player::getItems () {
return items;
}
fixed Player::getX () {
return x;
}
fixed Player::getY () {
return y;
}
bool Player::overlap (fixed left, fixed top, fixed width, fixed height) {
return (x + PXO_R >= left) && (x + PXO_L < left + width) && (y >= top) &&
(y + PYO_TOP < top + height);
}
void Player::setPosition (fixed newX, fixed newY) {
x = newX;
y = newY;
return;
}
void Player::setSpeed (fixed newDx, fixed newDy) {
dx = newDx;
if (newDy) dy = newDy;
return;
}
bool Player::getFacing () {
return facing;
}
unsigned char Player::getTeam () {
return team;
}
void Player::floatUp (signed char *newEvent) {
event = newEvent;
if ((dy > 0) && level->checkMaskDown(x + PXO_MID, y + F4))
dy = event[E_MULTIPURPOSE] * -F40;
if (dy > event[E_MULTIPURPOSE] * -F40)
dy -= event[E_MULTIPURPOSE] * 320 * mspf;
jumpY = y - (8 * F16);
return;
}
void Player::belt (int speed) {
dx += speed * 160 * mspf;
return;
}
void Player::setEvent (signed char *newEvent) {
event = newEvent;
if (event[E_MODIFIER] == 29) // Upwards spring
jumpY = y + (event[E_MAGNITUDE] * (F20 + F1));
return;
}
void Player::clearEvent (signed char *newEvent, unsigned char property) {
// If the given property matches, clear the event
if (event && (event[property] == newEvent[property])) event = NULL;
return;
}
void Player::send (unsigned char *data) {
// Copy data to be sent to clients/server
data[3] = pcontrols[C_UP];
data[4] = pcontrols[C_DOWN];
data[5] = pcontrols[C_LEFT];
data[6] = pcontrols[C_RIGHT];
data[7] = pcontrols[C_JUMP];
data[8] = pcontrols[C_FIRE];
data[9] = bird? 1: 0;
data[10] = ammo[0] >> 8;
data[11] = ammo[0] & 255;
data[12] = ammo[1] >> 8;
data[13] = ammo[1] & 255;
data[14] = ammo[2] >> 8;
data[15] = ammo[2] & 255;
data[16] = ammo[3] >> 8;
data[17] = ammo[3] & 255;
data[18] = ammoType + 1;
data[19] = score >> 24;
data[20] = (score >> 16) & 255;
data[21] = (score >> 8) & 255;
data[22] = score & 255;
data[23] = energy;
data[24] = lives;
data[25] = shield;
data[26] = floating;
data[27] = facing;
data[28] = fireSpeed;
data[29] = jumpHeight >> 24;
data[30] = (jumpHeight >> 16) & 255;
data[31] = (jumpHeight >> 8) & 255;
data[32] = jumpHeight & 255;
data[33] = jumpY >> 24;
data[34] = (jumpY >> 16) & 255;
data[35] = (jumpY >> 8) & 255;
data[36] = jumpY & 255;
data[37] = x >> 24;
data[38] = (x >> 16) & 255;
data[39] = (x >> 8) & 255;
data[40] = x & 255;
data[41] = y >> 24;
data[42] = (y >> 16) & 255;
data[43] = (y >> 8) & 255;
data[44] = y & 255;
return;
}
void Player::receive (unsigned char *buffer) {
// Interpret data recieved from client/server
switch (buffer[1]) {
case MT_P_ANIMS:
setAnims((char *)buffer + 3);
break;
case MT_P_TEMP:
pcontrols[C_UP] = buffer[3];
pcontrols[C_DOWN] = buffer[4];
pcontrols[C_LEFT] = buffer[5];
pcontrols[C_RIGHT] = buffer[6];
pcontrols[C_JUMP] = buffer[7];
pcontrols[C_FIRE] = buffer[8];
pcontrols[C_CHANGE] = false;
if ((buffer[9] & 1) && !bird)
bird = new Bird(this, FTOT(x), FTOT(y));
if (!(buffer[9] & 1) && bird) {
delete bird;
bird = NULL;
}
ammo[0] = (buffer[10] << 8) + buffer[11];
ammo[1] = (buffer[12] << 8) + buffer[13];
ammo[2] = (buffer[14] << 8) + buffer[15];
ammo[3] = (buffer[16] << 8) + buffer[17];
ammoType = buffer[18] - 1;
score = (buffer[19] << 24) + (buffer[20] << 16) +
(buffer[21] << 8) + buffer[22];
energy = buffer[23];
lives = buffer[24];
shield = buffer[25];
floating = buffer[26];
facing = buffer[27];
fireSpeed = buffer[28];
jumpHeight = (buffer[29] << 24) + (buffer[30] << 16) +
(buffer[31] << 8) + buffer[32];
jumpY = (buffer[33] << 24) + (buffer[34] << 16) +
(buffer[35] << 8) + buffer[36];
x = (buffer[37] << 24) + (buffer[38] << 16) + (buffer[39] << 8) +
buffer[40];
y = (buffer[41] << 24) + (buffer[42] << 16) + (buffer[43] << 8) +
buffer[44];
break;
}
return;
}
int Player::reacted (int ticks) {
int oldReaction;
if ((reaction != PR_NONE) && (reactionTime < ticks)) {
oldReaction = reaction;
reaction = PR_NONE;
return oldReaction;
}
return PR_NONE;
}
/*
*
* player.h
*
* 31st January 2006: Created player.h from parts of OpenJazz.h
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/* "Tile" is a flexible term. Here it is used to refer specifically to the
individual elements of the tile set.
"Tiles" in the context of level units are referred to as grid elements. */
#ifndef _PLAYER_H
#define _PLAYER_H
#include "OpenJazz.h"
#include <SDL/SDL.h>
// Constants
// Player animations
#define PA_LWALK 0
#define PA_RWALK 1
#define PA_LJUMP 2
#define PA_RJUMP 3
#define PA_LSPIN 4
#define PA_RSPIN 5
#define PA_LSHOOT 6
#define PA_RSHOOT 7
#define PA_LCROUCH 8
#define PA_RCROUCH 9
#define PA_LFALL 10
#define PA_RFALL 11
#define PA_LHURT 12
#define PA_RHURT 13
#define PA_LLEAN 14
#define PA_RLEAN 15
#define PA_LBOARD 16
#define PA_RBOARD 17
#define PA_LSTAND 18
#define PA_RSTAND 19
#define PA_LEAT 20
#define PA_REAT 21
#define PA_LEDGE 22
#define PA_REDGE 23
#define PA_LOOKUP 24
#define PA_LOOKDOWN 25
#define PA_LSWIM 26
#define PA_RSWIM 27
#define PA_LRUN 28
#define PA_RRUN 29
#define PA_LDIE 30
#define PA_RDIE 31
#define PA_LSTOP 32
#define PA_RSTOP 33
#define PA_LHALT 34 /* Yeah, I was wondering the same thing... */
#define PA_RHALT 35
#define PA_RSPRING 36
#define PA_LSPRING 37 /* Surely these are the wrong way round? */
// Player facing
#define PF_LEFT 0
#define PF_RIGHT 1
// Player reactions
#define PR_NONE 0
#define PR_HURT 1
#define PR_KILLED 2
#define PR_INVINCIBLE 3
// Player reaction times
#define PRT_HURT 1000
#define PRT_HURTANIM 200
#define PRT_KILLED 2000
#define PRT_INVINCIBLE 10000
// Other time periods
#define T_FASTFEET 25000
#define T_WARP 1000
// Player offsets
#define PXO_L (F12 - F10)
#define PXO_ML (F12 - F4)
#define PXO_MID F12
#define PXO_MR (F12 + F4)
#define PXO_R (F12 + F10)
#define PYO_TOP (-F20)
#define PYO_MID (-F10)
// Player speeds
#define PXS_WALK ITOF(300)
#define PXS_RUN ITOF(325)
#define PXS_FFRUN ITOF(500)
#define PYS_FALL ITOF(350)
#define PYS_SINK ITOF(150)
#define PYS_JUMP ITOF(-350)
// Player accelerations
#define PXA_REVERSE 900
#define PXA_STOP 1000
#define PXA_WALK 500
#define PXA_RUN 200
#define PXA_FFRUN 200
#define PYA_GRAVITY 2750
#define PYA_SINK 1000
// Player colours
#define PC_WHITE 0
#define PC_SGREEN 16
#define PC_BLUE 23
#define PC_RED 32
#define PC_LGREEN 48
#define PC_LEVEL1 64
#define PC_YELLOW 75
#define PC_LEVEL2 80
#define PC_ORANGE 88
#define PC_LEVEL3 96
#define PC_LEVEL4 104
#define PC_SANIM 112
#define PC_LANIM 116
#define PC_LEVEL5 124
// Player defaults
#define CHAR_NAME "jazz"
#define CHAR_FUR PC_LGREEN
#define CHAR_BAND PC_RED
#define CHAR_GUN PC_BLUE
#define CHAR_WBAND PC_ORANGE
// General
#define PANIMS 38 /* Number of player animations. Is probably higher. */
#define PCONTROLS 7 /* Number of player controls. */
// Classes
class Bird;
class Player {
private:
Bird *bird;
char *name;
signed char *event; /* A member of the event set (spring, float up,
belt, platform) */
char anims[PANIMS];
bool pcontrols[PCONTROLS];
SDL_Color palette[256];
unsigned char cols[4];
int ammo[4];
int ammoType; /* -1 = blaster, 0 = toaster, 1 = missiles,
2 = bouncer 3 = TNT */
int score;
int energy;
int lives;
int shield; /* 0 = none, 1 = 1 yellow, 2 = 2 yellow,
3 = 1 orange, 4 = 2 orange, 5 = 3 orange, 6 = 4 orange */
bool floating; // false = normal, true = boarding/bird/etc.
bool facing;
int lookTime; /* Negative if looking up, positive if looking
down, 0 if neither */
int reaction;
int reactionTime;
int fireSpeed;
int fireTime;
fixed jumpHeight;
fixed jumpY;
int fastFeetTime;
unsigned char warpX, warpY;
int warpTime;
fixed x, y, dx, dy;
int enemies, items;
unsigned char team;
void addAmmo (int type, int amount);
public:
int teamScore;
Player ();
~Player ();
void init (char *playerName, unsigned char *cols,
unsigned char newTeam);
void deinit ();
void setAnims (char *newAnims);
char * getName ();
unsigned char * getCols ();
void reset ();
void setControl (int control, bool state);
bool shootEvent (unsigned char gridX, unsigned char gridY,
int ticks);
bool touchEvent (unsigned char gridX, unsigned char gridY,
int ticks);
bool hit (Player *source, int ticks);
void kill (Player *source, int ticks);
void addScore (int addedScore);
int getScore ();
int getEnergy ();
int getLives ();
int getAmmo (bool amount);
int getEnemies ();
int getItems ();
fixed getX ();
fixed getY ();
bool overlap (fixed left, fixed top, fixed width,
fixed height);
void setPosition (fixed newX, fixed newY);
void setSpeed (fixed newDx, fixed newDy);
bool getFacing ();
unsigned char getTeam ();
void floatUp (signed char *newEvent);
void belt (int speed);
void setEvent (signed char *newEvent);
void clearEvent (signed char *newEvent,
unsigned char property);
void send (unsigned char *data);
void receive (unsigned char *buffer);
void control (int ticks);
void move (int ticks);
void view (int ticks);
void draw (int ticks);
int reacted (int ticks);
};
// Variables
EXTERN Player *players;
EXTERN Player *localPlayer;
EXTERN int nPlayers;
// Configuration data
EXTERN char *characterName;
EXTERN unsigned char characterCols[4];
#endif
/*
*
* playerframe.cpp
*
* 18th July 2009: Created playerframe.cpp from parts of player.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Provides the once-per-frame functions of players.
*
*/
#include "bird.h"
#include "player.h"
#include "game/gamemode.h"
#include "io/controls.h"
#include "io/gfx/font.h"
#include "io/gfx/video.h"
#include "io/sound.h"
#include "level/bullet.h"
#include "level/event/event.h"
#include "level/level.h"
#include <math.h>
void Player::control (int ticks) {
// Respond to controls, unless the player has been killed
// If the player has been killed, drop but otherwise do not move
if (!energy) {
dx = 0;
if (floating) dy = 0;
else {
dy += PYA_GRAVITY * mspf;
if (dy > PYS_FALL) dy = PYS_FALL;
}
return;
}
if (pcontrols[C_RIGHT]) {
// Walk/run right
if (dx < 0) dx += PXA_REVERSE * mspf;
else if (dx < PXS_WALK) dx += PXA_WALK * mspf;
else if (dx < PXS_RUN) dx += PXA_RUN * mspf;
facing = true;
} else if (pcontrols[C_LEFT]) {
// Walk/run left
if (dx > 0) dx -= PXA_REVERSE * mspf;
else if (dx > -PXS_WALK) dx -= PXA_WALK * mspf;
else if (dx > -PXS_RUN) dx -= PXA_RUN * mspf;
facing = false;
} else {
// Slow down
if (dx > 0) {
if (dx < PXA_STOP * mspf) dx = 0;
else dx -= PXA_STOP * mspf;
}
if (dx < 0) {
if (dx > -PXA_STOP * mspf) dx = 0;
else dx += PXA_STOP * mspf;
}
}
if (dx < -PXS_RUN) dx = -PXS_RUN;
if (dx > PXS_RUN) dx = PXS_RUN;
if (floating) {
if (pcontrols[C_UP]) {
// Fly upwards
if (dy > 0) dy -= PXA_REVERSE * mspf;
else if (dy > -PXS_WALK) dy -= PXA_WALK * mspf;
else if (dy > -PXS_RUN) dy -= PXA_RUN * mspf;
} else if (pcontrols[C_DOWN]) {
// Fly downwards
if (dy < 0) dy += PXA_REVERSE * mspf;
else if (dy < PXS_WALK) dy += PXA_WALK * mspf;
else if (dy < PXS_RUN) dy += PXA_RUN * mspf;
} else {
// Slow down
if (dy > 0) {
if (dy < PXA_STOP * mspf) dy = 0;
else dy -= PXA_STOP * mspf;
}
if (dy < 0) {
if (dy > -PXA_STOP * mspf) dy = 0;
else dy += PXA_STOP * mspf;
}
}
if (event) {
if (event[E_MODIFIER] == 29) dy = event[E_MULTIPURPOSE] * -F20;
else if (event[E_BEHAVIOUR] == 25) dy = PYS_JUMP;
}
if (dy < -PXS_RUN) dy = -PXS_RUN;
if (dy > PXS_RUN) dy = PXS_RUN;
} else if (y > level->getWaterLevel(ticks)) {
if (pcontrols[C_JUMP]) {
// Swim upwards
if (dy > 0) dy -= PXA_REVERSE * mspf;
else if (dy > -PXS_WALK) dy -= PXA_WALK * mspf;
else if (dy > -PXS_RUN) dy -= PXA_RUN * mspf;
// Prepare to jump upon leaving the water
if (!level->checkMask(x + PXO_MID, y - F36)) {
jumpY = y - jumpHeight;
if (dx < 0) jumpY += dx >> 4;
else if (dx > 0) jumpY -= dx >> 4;
event = NULL;
}
} else if (pcontrols[C_DOWN]) {
// Swim downwards
if (dy < 0) dy += PXA_REVERSE * mspf;
else if (dy < PXS_WALK) dy += PXA_WALK * mspf;
else if (dy < PXS_RUN) dy += PXA_RUN * mspf;
} else {
// Sink
dy += PYA_SINK * mspf;
if (dy > PYS_SINK) dy = PYS_SINK;
}
if (dy < -PXS_RUN) dy = -PXS_RUN;
if (dy > PXS_RUN) dy = PXS_RUN;
} else {
if ((event && ((event[E_MODIFIER] == 6) ||
(event[E_BEHAVIOUR] == 28))) ||
level->checkMaskDown(x + PXO_ML, y + F2) ||
level->checkMaskDown(x + PXO_MID, y + F2) ||
level->checkMaskDown(x + PXO_MR, y + F2)) {
// Mask/platform/bridge below player
if (pcontrols[C_JUMP] && !level->checkMask(x + PXO_MID, y - F36)) {
// Jump
jumpY = y - jumpHeight;
// Increase jump height if walking/running
if (dx < 0) jumpY += dx >> 4;
else if (dx > 0) jumpY -= dx >> 4;
event = NULL;
playSound(S_JUMPA);
}
if (!lookTime) {
// If requested, look up or down
if (pcontrols[C_UP]) lookTime = -ticks;
else if (pcontrols[C_DOWN]) lookTime = ticks;
}
} else {
// No mask/platform/bridge below player
// Cannot look up or down
lookTime = 0;
}
// Stop jumping
if (!pcontrols[C_JUMP] &&
(!event || ((event[E_MODIFIER] != 29) &&
(event[E_BEHAVIOUR] != 25)))) jumpY = TTOF(LH);
// If jumping, rise
if (y >= jumpY) {
dy = (jumpY - y - F64) * 4;
// Avoid jumping to fast, unless caused by an event
if (!event && (dy < (ITOF(-92) - F64) * 4))
dy = (ITOF(-92) - F64) * 4;
} else {
// Fall under gravity
dy += PYA_GRAVITY * mspf;
if (dy > PYS_FALL) dy = PYS_FALL;
}
// Stop looking
if (!pcontrols[C_UP] && !pcontrols[C_DOWN]) lookTime = 0;
}
// If there is an obstacle above and the player is not floating up, stop
// rising
if (level->checkMask(x + PXO_MID, y + PYO_TOP - F4) && (jumpY < y) &&
(!event || event[E_BEHAVIOUR] != 25)) {
jumpY = TTOF(LH);
if (dy < 0) dy = 0;
if (event && (event[E_MODIFIER] != 6) && (event[E_BEHAVIOUR] != 28))
event = NULL;
}
// If jump completed, stop rising
if (y <= jumpY) {
jumpY = TTOF(LH);
if (event && (event[E_MODIFIER] != 6) && (event[E_BEHAVIOUR] != 28))
event = NULL;
}
// Handle firing
if (pcontrols[C_FIRE]) {
if (ticks > fireTime) {
// Create new bullet
level->firstBullet = new Bullet(this, false, ticks);
// Set when the next bullet can be fired
if (fireSpeed) fireTime = ticks + (1000 / fireSpeed);
else fireTime = 0x7FFFFFFF;
// Remove the bullet from the arsenal
if (ammoType != -1) ammo[ammoType]--;
/* If the current ammo type has been exhausted, use the previous
non-exhausted ammo type */
while ((ammoType > -1) && !ammo[ammoType]) ammoType--;
}
} else fireTime = 0;
// Check for a change in ammo
if (pcontrols[C_CHANGE]) {
if (this == localPlayer) controls.release(C_CHANGE);
ammoType = ((ammoType + 2) % 5) - 1;
// If there is no ammo of this type, go to the next type that has ammo
while ((ammoType > -1) && !ammo[ammoType])
ammoType = ((ammoType + 2) % 5) - 1;
}
// Deal with the bird
if (bird) {
if (bird->playFrame(ticks)) {
delete bird;
bird = NULL;
}
}
return;
}
void Player::move (int ticks) {
fixed pdx, pdy;
int count;
if (warpTime && (ticks > warpTime)) {
x = TTOF(warpX);
y = TTOF(warpY + 1);
warpTime = 0;
}
// Apply as much of the trajectory as possible, without going into the
// scenery
if (fastFeetTime > ticks) {
pdx = (dx * mspf * 3) >> 11;
pdy = (dy * mspf * 3) >> 11;
} else {
pdx = (dx * mspf) >> 10;
pdy = (dy * mspf) >> 10;
}
// First for the vertical component of the trajectory
if (pdy < 0) {
// Moving up
count = (-pdy) >> 12;
while (count > 0) {
if (level->checkMask(x + PXO_MID, y + PYO_TOP - F4)) break;
y -= F4;
count--;
}
pdy = (-pdy) & 4095;
if (!level->checkMask(x + PXO_MID, y + PYO_TOP - pdy)) y -= pdy;
else y &= ~4095;
} else if (pdy > 0) {
// Moving down
count = pdy >> 12;
while (count > 0) {
if (level->checkMaskDown(x + PXO_ML, y + F4) ||
level->checkMaskDown(x + PXO_MID, y + F4) ||
level->checkMaskDown(x + PXO_MR, y + F4)) break;
y += F4;
count--;
}
pdy &= 4095;
if (!(level->checkMaskDown(x + PXO_ML, y + pdy) ||
level->checkMaskDown(x + PXO_MID, y + pdy) ||
level->checkMaskDown(x + PXO_MR, y + pdy))) y += pdy;
else y |= 4095;
}
// Then for the horizontal component of the trajectory
if (pdx < 0) {
// Moving left
count = (-pdx) >> 12;
while (count > 0) {
// If there is an obstacle, stop
if (level->checkMask(x + PXO_L - F4, y + PYO_MID)) break;
x -= F4;
count--;
// If on an uphill slope, push the player upwards
if (level->checkMask(x + PXO_ML, y) &&
!level->checkMask(x + PXO_ML, y - F4)) y -= F4;
}
pdx = (-pdx) & 4095;
if (!level->checkMask(x + PXO_L - pdx, y + PYO_MID)) x -= pdx;
else x &= ~4095;
// If on an uphill slope, push the player upwards
while (level->checkMask(x + PXO_ML, y) &&
!level->checkMask(x + PXO_ML, y - F4)) y -= F1;
} else if (pdx > 0) {
// Moving right
count = pdx >> 12;
while (count > 0) {
// If there is an obstacle, stop
if (level->checkMask(x + PXO_R + F4, y + PYO_MID)) break;
x += F4;
count--;
// If on an uphill slope, push the player upwards
if (level->checkMask(x + PXO_MR, y) &&
!level->checkMask(x + PXO_MR, y - F4)) y -= F4;
}
pdx &= 4095;
if (!level->checkMask(x + PXO_R + pdx, y + PYO_MID)) x += pdx;
else x |= 4095;
// If on an uphill slope, push the player upwards
while (level->checkMask(x + PXO_MR, y) &&
!level->checkMask(x + PXO_MR, y - F4)) y -= F1;
}
// If using a float up event and have hit a ceiling, ignore event
if (event && (event[E_BEHAVIOUR] == 25) &&
level->checkMask(x + PXO_MID, y + PYO_TOP - F4)) {
jumpY = TTOF(LH);
event = NULL;
}
if (level->getStage() == LS_END) return;
// If the player has hit the bottom of the level, kill
if (y + F4 > TTOF(LH)) kill(NULL, ticks);
// Handle spikes
if (level->checkSpikes(x + PXO_MID, y + PYO_TOP - F4) ||
level->checkSpikes(x + PXO_MID, y + F4) ||
level->checkSpikes(x + PXO_L - F4, y + PYO_MID) ||
level->checkSpikes(x + PXO_R + F4, y + PYO_MID)) hit(NULL, ticks);
return;
}
void Player::view (int ticks) {
int oldViewX, oldViewY, speed;
// Calculate viewport
// Record old viewport position for applying lag
oldViewX = viewX;
oldViewY = viewY;
// Can we see below the panel?
viewW = screenW;
if (viewW > panel->w) viewH = screenH;
else viewH = screenH - 33;
// Find new position
viewX = x + F8 - (viewW << 9);
if (!lookTime || (ticks < 1000 + lookTime) || (ticks < 1000 - lookTime)) {
viewY = y - F24 - (viewH << 9);
} else if (lookTime > 0) {
if (ticks < 2000 + lookTime)
viewY = y - F24 - (64 * (lookTime + 1000 - ticks)) - (viewH << 9);
else viewY = y + F64 - F24 - (viewH << 9);
} else {
if (ticks < 2000 - lookTime)
viewY = y - F24 - (64 * (lookTime - 1000 + ticks)) - (viewH << 9);
else viewY = y - F64 - F24 - (viewH << 9);
}
// Apply lag proportional to player "speed"
speed = ((dx >= 0? dx: -dx) + (dy >= 0? dy: -dy)) >> 14;
if (mspf < speed) {
viewX = ((oldViewX * (speed - mspf)) + (viewX * mspf)) / speed;
viewY = ((oldViewY * (speed - mspf)) + (viewY * mspf)) / speed;
}
return;
}
void Player::draw (int ticks) {
Anim *an;
int anim, frame;
fixed xOffset, yOffset;
// The current frame for animations
if (reaction == PR_KILLED) frame = (ticks + PRT_KILLED - reactionTime) / 75;
else frame = ticks / 75;
// Choose player animation
if (reaction == PR_KILLED) anim = anims[facing? PA_RDIE: PA_LDIE];
else if ((reaction == PR_HURT) &&
(reactionTime - ticks > PRT_HURT - PRT_HURTANIM))
anim = anims[facing? PA_RHURT: PA_LHURT];
else if (y > level->getWaterLevel(ticks))
anim = anims[facing? PA_RSWIM: PA_LSWIM];
else if (floating) anim = anims[facing? PA_RBOARD: PA_LBOARD];
else if (dy >= 0) {
if ((event && ((event[E_MODIFIER] == 6) ||
(event[E_BEHAVIOUR] == 28))) ||
level->checkMaskDown(x + PXO_ML, y + F2) ||
level->checkMaskDown(x + PXO_MID, y + F2) ||
level->checkMaskDown(x + PXO_MR, y + F2) ||
level->checkMaskDown(x + PXO_ML, y + F8) ||
level->checkMaskDown(x + PXO_MID, y + F8) ||
level->checkMaskDown(x + PXO_MR, y + F8)) {
if (dx) {
if (dx <= -PXS_RUN) anim = anims[PA_LRUN];
else if (dx >= PXS_RUN) anim = anims[PA_RRUN];
else if ((dx < 0) && facing) anim = anims[PA_LSTOP];
else if ((dx > 0) && !facing) anim = anims[PA_RSTOP];
else anim = anims[facing? PA_RWALK: PA_LWALK];
} else {
if (!level->checkMaskDown(x + PXO_ML, y + F12) &&
!level->checkMaskDown(x + PXO_L, y + F2) &&
(!event || ((event[E_MODIFIER] != 6) &&
(event[E_BEHAVIOUR] != 28))))
anim = anims[PA_LEDGE];
else if (!level->checkMaskDown(x + PXO_MR, y + F12) &&
!level->checkMaskDown(x + PXO_R, y + F2) &&
(!event || ((event[E_MODIFIER] != 6) &&
(event[E_BEHAVIOUR] != 28))))
anim = anims[PA_REDGE];
else if (pcontrols[C_FIRE])
anim = anims[facing? PA_RSHOOT: PA_LSHOOT];
else if ((lookTime < 0) && (ticks > 1000 - lookTime))
anim = anims[PA_LOOKUP];
else if (lookTime > 0) {
if (ticks < 1000 + lookTime)
anim = anims[facing? PA_RCROUCH: PA_LCROUCH];
else anim = anims[PA_LOOKDOWN];
} else anim = anims[facing? PA_RSTAND: PA_LSTAND];
}
} else anim = anims[facing? PA_RFALL: PA_LFALL];
} else if (event && (event[E_MODIFIER] == 29))
anim = anims[facing? PA_RSPRING: PA_LSPRING];
else anim = anims[facing? PA_RJUMP: PA_LJUMP];
// Choose sprite
an = level->getAnim(anim);
an->setFrame(frame, reaction != PR_KILLED);
// Show the player
// Flash red if hurt, otherwise use player colour
if ((reaction == PR_HURT) && (!((ticks / 30) & 3)))
an->flashPalette(36);
else {
an->setPalette(palette, 23, 41);
an->setPalette(palette, 88, 8);
}
// Draw "motion blur"
if (fastFeetTime > ticks)
an->draw(x - (dx >> 6), y);
// Draw player
an->draw(x, y);
// Remove red flash or player colour from sprite
an->restorePalette();
if (reaction == PR_INVINCIBLE) {
// Show invincibility stars
xOffset = (int)(sin(ticks / 100.0f) * F12);
yOffset = (int)(cos(ticks / 100.0f) * F12);
an = level->getMiscAnim(0);
an->setFrame(frame, true);
an->draw(x + PXO_MID + xOffset, y + PYO_MID + yOffset);
an->setFrame(frame + 1, true);
an->draw(x + PXO_MID - xOffset, y + PYO_MID - yOffset);
an->setFrame(frame + 2, true);
an->draw(x + PXO_MID + yOffset, y + PYO_MID + xOffset);
an->setFrame(frame + 3, true);
an->draw(x + PXO_MID - yOffset, y + PYO_MID - xOffset);
} else if (shield > 2) {
// Show the 4-hit shield
xOffset = (int)(cos(ticks / 200.0f) * F20);
yOffset = (int)(sin(ticks / 200.0f) * F20);
an = level->getAnim(59);
an->draw(x + xOffset, y + PYO_TOP + yOffset);
if (shield > 3) an->draw(x - xOffset, y + PYO_TOP - yOffset);
if (shield > 4) an->draw(x + yOffset, y + PYO_TOP - xOffset);
if (shield > 5) an->draw(x - yOffset, y + PYO_TOP + xOffset);
} else if (shield) {
// Show the 2-hit shield
xOffset = (int)(cos(ticks / 200.0f) * F20);
yOffset = (int)(sin(ticks / 200.0f) * F20);
an = level->getAnim(50);
an->draw(x + xOffset, y + yOffset + PYO_TOP);
if (shield == 2) an->draw(x - xOffset, y + PYO_TOP - yOffset);
}
// Show the bird
if (bird) bird->draw(ticks);
// Show the player's name
if (gameMode)
panelBigFont->showString(name, FTOI(x - viewX),
FTOI(y - F32 - F16 - viewY));
return;
}
/*
*
* scene.cpp
*
* 23rd August 2005: Created scene.c
* 3rd February 2009: Created scene.h from parts of scene.c
* 3rd February 2009: Renamed scene.c to scene.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Deals with the loading, displaying and freeing of the cutscenes.
*
*/
#include "scene.h"
#include "io/controls.h"
#include "io/file.h"
#include "io/gfx/video.h"
#include "io/sound.h"
Scene::Scene (char * fileName) {
File *file;
char *string;
int type;
try {
file = new File(fileName, false);
} catch (int e) {
throw e;
}
// TODO: Correct loading of cutscene data
// Skip to files
file->seek(25, true);
file->seek(file->loadChar(), true);
// At this point, next bytes should be 0x50 0x01 0x00 0x00 0x00
// Then, (0x3f 0x02)
// OR (Nothing)
// Then, (0x2a
// Then the length of the music file name
// Then the bytes of the music file name)
// OR (0x3f, then another byte)
// OR (0x4c, not followed by any font stuff)
// OR (0xa6, then 20 bytes?)
// OR (Nothing)
// Then 0x58 0x01 0x00
// Then the length of a font file name
// Then a font file name
// Then 0x58 0x02 0x00
// Then the length of a font file name
// Then a font file name
file->seek(5, false);
type = file->loadChar();
while (type == 0x3f) {
file->seek(1, false);
type = file->loadChar();
}
if (type != 0x4C) {
if (type == 0x2A) {
// Music file name
string = file->loadString();
playMusic(string);
delete[] string;
} else if (type == 0x63) {
file->seek(1, false);
} else if (type == 0xA6) {
file->seek(20, false);
} else file->seek(-1, false); // type should be 58
while (file->loadChar() == 0x58) {
// Font names (file names minus extensions)
file->seek(2, false);
string = file->loadString();
// Do something with this
delete[] string;
}
}
file->seek(-1, false);
while (file->loadChar() == 0x3f) {
file->seek(1, false);
}
file->seek(-1, false);
// Then 0x4c 0x00 0x00 0x00 0x00 0x01 0x00
// Then, (0x46
// Then a small number, e.g. 0x01, 0x02
// Then 0x00 0x4a
// Then (0x02 0x5d)
// OR (0x01 0xdb)
// OR (0x57 0x14...)
// log("Initial search reached", file->tell());
// Skip to the palette
file->seek(23, true);
type = file->loadChar();
file->seek(19, true);
file->skipRLE();
file->seek((type * 4) - 11, false);
// Load the palette
// log("Palette", file->tell());
file->loadPalette(scenePalette);
usePalette(scenePalette);
file->seek(4, false);
// log("Pixels", file->tell());
sceneBGs[0] = file->loadSurface(320, 200);
delete file;
return;
}
Scene::~Scene () {
SDL_FreeSurface(sceneBGs[0]); // Temporary
return;
}
int Scene::play () {
SDL_Rect dst;
while (true) {
if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT;
if (controls.release(C_ENTER) || controls.release(C_ESCAPE))
return E_NONE;
// TODO: Display cutscene
// Temporary stuff
clearScreen(BLACK);
dst.x = (screenW - 320) >> 1;
dst.y = (screenH - 200) >> 1;
SDL_BlitSurface(sceneBGs[0], NULL, screen, &dst);
SDL_Delay(T_FRAME);
}
return E_NONE;
}
/*
*
* scene.h
*
* 3rd February 2009: Created scene.h from parts of scene.c
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _SCENE_H
#define _SCENE_H
#include <SDL/SDL.h>
// Class
class Scene {
private:
SDL_Surface *sceneBGs[1];
SDL_Color scenePalette[256];
public:
Scene (char * fileName);
~Scene ();
int play ();
};
#endif
/*
*
* util.cpp
*
* 22nd July 2008: Created util.c from parts of main.c
* 3rd February 2009: Renamed util.c to util.cpp
* 3rd February 2009: Created file.cpp from parts of util.cpp
* 4th February 2009: Created palette.cpp from parts of main.cpp and util.cpp
* 13th July 2009: Created graphics.cpp from parts of util.cpp
*
* Part of the OpenJazz project
*
*
* Copyright (c) 2005-2009 Alister Thomson
*
* OpenJazz is distributed under the terms of
* the GNU General Public License, version 2.0
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
* Contains core utility functions.
*
*/
#include "io/file.h"
#include <string.h>
bool fileExists (char * fileName) {
File *file;
//printf("Check: ");
try {
file = new File(fileName, false);
} catch (int e) {
return false;
}
delete file;
return true;
}
char * createString (char *string) {
char *cloned;
cloned = new char[strlen(string) + 1];
strcpy(cloned, string);
return cloned;
}
char * createString (char *first, char *second) {
char *concatenated;
concatenated = new char[strlen(first) + strlen(second) + 1];
strcpy(concatenated, first);
strcat(concatenated, second);
return concatenated;
}
char * createFileName (char *type, int extension) {
char *fileName;
int pos;
pos = strlen(type);
fileName = new char[pos + 5];
strcpy(fileName, type);
fileName[pos++] = '.';
fileName[pos++] = '0' + ((extension / 100) % 10);
fileName[pos++] = '0' + ((extension / 10) % 10);
fileName[pos++] = '0' + (extension % 10);
fileName[pos] = 0;
return fileName;
}
char * createFileName (char *type, char *extension) {
char *fileName;
int pos;
pos = strlen(type);
fileName = new char[strlen(type) + strlen(extension) + 2];
strcpy(fileName, type);
fileName[pos++] = '.';
strcpy(fileName + pos, extension);
return fileName;
}
char * createFileName (char *type, int level, int extension) {
char *fileName;
int pos;
pos = strlen(type);
fileName = new char[pos + 6];
strcpy(fileName, type);
fileName[pos++] = '0' + (level % 10);
fileName[pos++] = '.';
fileName[pos++] = '0' + ((extension / 100) % 10);
fileName[pos++] = '0' + ((extension / 10) % 10);
fileName[pos++] = '0' + (extension % 10);
fileName[pos] = 0;
return fileName;
}
char * createEditableString (char *string) {
char *cloned;
cloned = new char[STRING_LENGTH + 1];
strcpy(cloned, string);
return cloned;
}
void log (char *message) {
printf("%s\n", message);
return;
}
void log (char *message, char *detail) {
printf("%s: %s\n", message, detail);
return;
}
void log (char *message, int number) {
printf("%s: %d\n", message, number);
return;
}
void logError (char *message, char *detail) {
fprintf(stderr, "%s: %s\n", message, detail);
return;
}
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