Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
295 KB
Referenced Files
None
Subscribers
None
This file is larger than 256 KB, so syntax highlighting was skipped.
This document is not UTF8. It was detected as JIS and converted to UTF8 for display.
diff --git a/src/Audio.cpp b/src/Audio.cpp
index 89757ec..86f6197 100644
--- a/src/Audio.cpp
+++ b/src/Audio.cpp
@@ -1,256 +1,256 @@
/***************************************************************************
Audio.cpp - description
-------------------
begin : Sat Jul 26 2003
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#include "Audio.h"
#include "State.h"
#include "common.h"
#include "SDL_mixer.h"
#include <string>
#include <map>
typedef std::map<std::string,Mix_Chunk*> SampleMap;
typedef std::map<std::string,Mix_Music*> MusicMap;
typedef SampleMap::iterator SampleMapIterator;
typedef MusicMap::iterator MusicMapIterator;
MszAudio* Audio = NULL;
class MszAudioPriv
{
public:
bool m_bAudioOk;
- int m_iNumChannels;
+ int m_iNumChannels;
SampleMap m_oSamples;
MusicMap m_oMusics;
};
#define SELF (*m_poPriv)
#define CHECKOK if ( ! m_poPriv->m_bAudioOk ) return;
MszAudio::MszAudio()
{
Audio = this;
m_poPriv = new MszAudioPriv;
m_poPriv->m_bAudioOk = false;
m_poPriv->m_iNumChannels = 0;
SDL_version compile_version;
const SDL_version *link_version;
MIX_VERSION(&compile_version);
debug( "compiled with SDL_mixer version: %d.%d.%d\n",
compile_version.major, compile_version.minor, compile_version.patch);
link_version=Mix_Linked_Version();
debug("running with SDL_mixer version: %d.%d.%d\n",
link_version->major, link_version->minor, link_version->patch);
if ( Mix_OpenAudio( MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT,
2 /*stereo*/, 1024*2 /*chunksize*/ ) < 0 )
{
debug("Mix_OpenAudio: %s\n", Mix_GetError());
SELF.m_bAudioOk = false;
}
else
{
SELF.m_bAudioOk = true;
}
- m_poPriv->m_iNumChannels = Mix_AllocateChannels(8);
+ m_poPriv->m_iNumChannels = Mix_AllocateChannels(10);
}
MszAudio::~MszAudio()
{
delete m_poPriv;
m_poPriv = NULL;
}
void MszAudio::LoadSample( const char* a_pcFilename, const char* a_pcSampleName )
{
CHECKOK;
std::string sSampleName( a_pcSampleName ? a_pcSampleName : a_pcFilename );
std::string sSampleFile = DATADIR;
sSampleFile += "/sound/";
sSampleFile += a_pcFilename;
if ( m_poPriv->m_oSamples.count( sSampleName ) )
{
debug( "Key %s already loaded", a_pcSampleName );
return;
}
Mix_Chunk *poSample;
poSample=Mix_LoadWAV(sSampleFile.c_str());
if(!poSample)
{
debug("Mix_LoadWAV: %s\n", Mix_GetError());
return;
}
m_poPriv->m_oSamples[ sSampleName ] = poSample;
}
void MszAudio::UnloadSample( const char* a_pcSampleName )
{
SampleMapIterator it = m_poPriv->m_oSamples.find( a_pcSampleName );
if ( m_poPriv->m_oSamples.end() == it )
{
debug( "UnloadSample: sample %s not found", a_pcSampleName );
return;
}
Mix_FreeChunk( (*it).second );
m_poPriv->m_oSamples.erase( it );
}
void MszAudio::PlaySample( const char* a_pcSampleName )
{
Mix_Chunk* poSample;
SampleMapIterator it = m_poPriv->m_oSamples.find(a_pcSampleName);
if ( m_poPriv->m_oSamples.end() == it )
{
// Try to load the sample..
LoadSample( a_pcSampleName );
it = m_poPriv->m_oSamples.find(a_pcSampleName);
if ( m_poPriv->m_oSamples.end() == it )
{
debug( "PlaySample: sample %s not found", a_pcSampleName );
return;
}
}
poSample = (*it).second;
int iVolume = g_oState.m_iSoundVolume * 128 / 100;
if ( poSample->volume != iVolume )
{
Mix_VolumeChunk( poSample, iVolume );
}
if ( -1 == Mix_PlayChannel( -1, poSample, 0 ) )
{
debug( "PlaySample: Mix_PlayChannel: %s\n",Mix_GetError());
}
}
void MszAudio::PlayFile( const char* a_pcFileName )
{
Mix_Chunk *poSample;
poSample=Mix_LoadWAV("sample.wav");
if(!poSample)
{
debug("Mix_LoadWAV: %s\n", Mix_GetError());
return;
}
if ( -1 == Mix_PlayChannel( -1, poSample, 0 ) )
{
debug( "PlaySample: Mix_PlayChannel: %s\n",Mix_GetError());
}
Mix_FreeChunk( poSample );
}
void MszAudio::LoadMusic( const char* a_pcFilename, const char* a_pcMusicName )
{
CHECKOK;
std::string sMusicName( a_pcMusicName ? a_pcMusicName : a_pcFilename );
std::string sMusicFile = DATADIR;
sMusicFile += "/sound/";
sMusicFile += a_pcFilename;
if ( m_poPriv->m_oMusics.count( sMusicName ) )
{
debug( "Key %s already loaded", a_pcMusicName );
return;
}
Mix_Music *poMusic;
poMusic=Mix_LoadMUS(sMusicFile.c_str());
if(!poMusic)
{
debug("Mix_LoadMUS: %s\n", Mix_GetError());
return;
}
m_poPriv->m_oMusics[ sMusicName ] = poMusic;
}
void MszAudio::UnloadMusic( const char* a_pcMusicName )
{
MusicMapIterator it = m_poPriv->m_oMusics.find( a_pcMusicName );
if ( m_poPriv->m_oMusics.end() == it )
{
debug( "UnloadMusic: music %s not found", a_pcMusicName );
return;
}
Mix_FreeMusic( (*it).second );
m_poPriv->m_oMusics.erase( it );
}
void MszAudio::PlayMusic( const char* a_pcMusicName )
{
MusicMapIterator it = m_poPriv->m_oMusics.find( a_pcMusicName );
if ( m_poPriv->m_oMusics.end() == it )
{
debug( "PlayMusic: music %s not found", a_pcMusicName );
return;
}
SetMusicVolume( g_oState.m_iMusicVolume );
//Mix_PlayMusic( (*it).second, -1 );
Mix_FadeInMusic( (*it).second, -1, 100 );
}
void MszAudio::FadeMusic( int a_iMilliSec )
{
Mix_FadeOutMusic( a_iMilliSec );
}
void MszAudio::SetMusicVolume( int a_iVolume )
{
Mix_VolumeMusic( a_iVolume * 128 / 100 );
}
void MszAudio::StopMusic()
{
Mix_HaltMusic();
}
bool MszAudio::IsMusicPlaying()
{
return Mix_PlayingMusic();
}
diff --git a/src/Backend.cpp b/src/Backend.cpp
index 469487d..1831ecf 100644
--- a/src/Backend.cpp
+++ b/src/Backend.cpp
@@ -1,476 +1,514 @@
/***************************************************************************
Backend.cpp - description
-------------------
begin : Mon Dec 9 2002
copyright : (C) 2002 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#include "common.h"
#include "Backend.h"
#include "Audio.h"
+#include "State.h"
#include <string>
#include <stdarg.h>
#include "MszPerl.h"
/***************************************************************************
PUBLIC EXPORTED VARIABLES
***************************************************************************/
PerlInterpreter* my_perl;
Backend g_oBackend;
/***************************************************************************
PRIVATE VARIABLES (perl variable space)
***************************************************************************/
SV
*perl_bgx, *perl_bgy,
- *perl_p1x, *perl_p1y, *perl_p1f, *perl_p1h,
- *perl_p2x, *perl_p2y, *perl_p2f, *perl_p2h,
+ *perl_px[MAXPLAYERS], *perl_py[MAXPLAYERS], *perl_pf[MAXPLAYERS],
+ *perl_ph[MAXPLAYERS], *perl_phreal[MAXPLAYERS],
*perl_gametick, *perl_over, *perl_ko;
SV
*perl_doodad_x, *perl_doodad_y,
*perl_doodad_t, *perl_doodad_f,
*perl_doodad_dir, *perl_doodad_gfxowner,
*perl_doodad_text;
SV
*perl_sound, *perl_Translated;
/***************************************************************************
TRANSLATION SERVICES
***************************************************************************/
const char* Translate( const char* a_pcText )
{
dSP ;
ENTER ;
SAVETMPS ;
PUSHMARK(SP) ;
XPUSHs(sv_2mortal(newSVpv(a_pcText, 0)));
PUTBACK ;
call_pv("Translate", G_DISCARD);
FREETMPS ;
LEAVE ;
if ( NULL == perl_Translated )
{
perl_Translated = get_sv("Translated", TRUE);
}
return SvPV_nolen( perl_Translated );
}
const char* TranslateUTF8( const char* a_pcText )
{
dSP ;
ENTER ;
SAVETMPS ;
PUSHMARK(SP) ;
XPUSHs(sv_2mortal(newSVpv(a_pcText, 0)));
PUTBACK ;
call_pv("Translate", G_DISCARD);
FREETMPS ;
LEAVE ;
if ( NULL == perl_Translated )
{
perl_Translated = get_sv("Translated", TRUE);
}
return SvPVutf8_nolen( perl_Translated );
}
/***************************************************************************
BACKEND CLASS IMPLEMENTATION
***************************************************************************/
#define PERLEVAL(A) eval_pv(A, TRUE);
#define PERLCALL(PROC,A,B) { \
dSP; \
ENTER; \
SAVETMPS; \
PUSHMARK(SP); \
XPUSHs(sv_2mortal(newSViv(A))); \
XPUSHs(sv_2mortal(newSViv(B))); \
PUTBACK ; \
\
call_pv( (PROC), G_DISCARD ); \
\
FREETMPS; \
LEAVE; \
}
Backend::Backend()
{
m_iBgX = m_iBgY = 0;
m_iNumDoodads = m_iNumSounds = 0;
- for ( int i=0; i<2; ++i )
+ for ( int i=0; i<MAXPLAYERS; ++i )
{
m_aoPlayers[i].m_iX = m_aoPlayers[i].m_iY = 0;
m_aoPlayers[i].m_iFrame = 0;
m_aoPlayers[i].m_iHitPoints = 0;
}
}
Backend::~Backend()
{
if ( NULL != my_perl )
{
perl_destruct( my_perl );
perl_free( my_perl );
my_perl = NULL;
}
}
bool Backend::Construct()
{
if ( my_perl != NULL )
{
// Already inited
return false;
}
perl_bgx = NULL;
perl_doodad_x = NULL;
std::string sFileName = DATADIR;
sFileName += "/script";
#ifndef _WINDOWS
chdir( sFileName.c_str() );
#endif
+// char *perl_argv[] = {"", "-d:Trace", "Backend.pl"};
+// int perl_argc = 3;
char *perl_argv[] = {"", "Backend.pl"};
+ int perl_argc = 2;
my_perl = perl_alloc();
if ( my_perl == NULL )
{
return false;
}
perl_construct( my_perl );
- if ( perl_parse( my_perl, NULL, 2, perl_argv, (char**)NULL ) )
+ if ( perl_parse( my_perl, NULL, perl_argc, perl_argv, (char**)NULL ) )
{
char *error = SvPV_nolen(get_sv("@", FALSE));
fprintf( stderr, "%s", error );
return false;
}
if ( perl_run( my_perl ) )
{
char *error = SvPV_nolen(get_sv("@", FALSE));
fprintf( stderr, "%s", error );
return false;
}
return true;
}
const char* Backend::PerlEvalF( const char* a_pcFormat, ... )
{
va_list ap;
va_start( ap, a_pcFormat );
char acBuffer[1024];
vsnprintf( acBuffer, 1023, a_pcFormat, ap );
acBuffer[1023] = 0;
- PERLEVAL(acBuffer);
+ eval_pv(acBuffer,FALSE);
const char *pcError = SvPV_nolen(get_sv("@", FALSE));
- if ( pcError )
+ if ( pcError && *pcError )
{
- fprintf( stderr, "%s", pcError );
+ debug( "eval '%s': '%s'\n", acBuffer, pcError );
+ exit(0);
}
va_end( ap );
return pcError;
}
const char* Backend::GetPerlString( const char* acScalarName )
{
SV* poScalar = get_sv( acScalarName, FALSE );
if ( NULL == poScalar )
{
return "";
}
return SvPV_nolen( poScalar );
}
int Backend::GetPerlInt( const char* acScalarName )
{
SV* poScalar = get_sv( acScalarName, FALSE );
if ( NULL == poScalar )
{
return 0;
}
return SvIV( poScalar );
}
/** Returns the total number of registered fighters in the backend.
This may be more than the actual number of playable characters, as
some or many characters may not be ready or installed.
\see GetFighterID
+\see GetNumberOfAvailableFighters
*/
int Backend::GetNumberOfFighters()
{
PerlEvalF( "$::CppNumberOfFighters = scalar keys %::FighterStats;" );
return GetPerlInt( "CppNumberOfFighters" );
}
+
/** Returns the ID of a fighter. The index parameter should start from
zero, and be less than the value returned by GetNumberOfFighters().
\see GetNumberOfFighters
*/
FighterEnum Backend::GetFighterID( int a_iIndex )
{
PerlEvalF( "$::CppFighterID = (sort { $a - $b } keys %%::FighterStats)[%d];", a_iIndex );
return (FighterEnum) GetPerlInt( "CppFighterID" );
}
+/** Returns the number of fighters that are locally availaible for play.
+This number is smaller or equal to the value returned by GetNumberOfFighters().
+
+Note that the method GetFighterID() returns ID's of non-available
+fighters as well, so it can only be used in conjunction with GetNumberOfFighters()
+
+\see GetNumberOfFighters
+*/
+int Backend::GetNumberOfAvailableFighters()
+{
+ PerlEvalF( "GetNumberOfAvailableFighters();" ); // Defined in FighterStats.pl
+ return GetPerlInt( "CppNumberOfAvailableFighters" );
+}
+
void Backend::AdvancePerl()
{
- PERLEVAL("GameAdvance();");
+ PerlEvalF("GameAdvance();");
}
void Backend::ReadFromPerl()
{
+ int i;
+
if ( perl_bgx == NULL )
{
perl_gametick = get_sv("gametick", TRUE);
perl_bgx = get_sv("bgx", TRUE);
perl_bgy = get_sv("bgy", TRUE);
- perl_p1x = get_sv("p1x", TRUE);
- perl_p1y = get_sv("p1y", TRUE);
- perl_p1f = get_sv("p1f", TRUE);
- perl_p1h = get_sv("p1h", TRUE);
- perl_p2x = get_sv("p2x", TRUE);
- perl_p2y = get_sv("p2y", TRUE);
- perl_p2f = get_sv("p2f", TRUE);
- perl_p2h = get_sv("p2h", TRUE);
perl_over= get_sv("over", TRUE);
perl_ko = get_sv("ko", TRUE);
+
+ char acVarName[128];
+
+ for ( i=0; i<MAXPLAYERS; ++i )
+ {
+ sprintf( acVarName, "p%dx", i+1 );
+ perl_px[i] = get_sv(acVarName, TRUE);
+ sprintf( acVarName, "p%dy", i+1 );
+ perl_py[i] = get_sv(acVarName, TRUE);
+ sprintf( acVarName, "p%df", i+1 );
+ perl_pf[i] = get_sv(acVarName, TRUE);
+ sprintf( acVarName, "p%dh", i+1 );
+ perl_ph[i] = get_sv(acVarName, TRUE);
+ sprintf( acVarName, "p%dhreal", i+1 );
+ perl_phreal[i] = get_sv(acVarName, TRUE);
+ }
}
m_iGameTick = SvIV( perl_gametick );
m_iBgX = SvIV( perl_bgx );
m_iBgY = SvIV( perl_bgy );
- m_aoPlayers[0].m_iX = SvIV( perl_p1x );
- m_aoPlayers[0].m_iY = SvIV( perl_p1y );
- m_aoPlayers[0].m_iFrame = SvIV( perl_p1f );
- m_aoPlayers[0].m_iHitPoints = SvIV( perl_p1h ) / 10;
- m_aoPlayers[1].m_iX = SvIV( perl_p2x );
- m_aoPlayers[1].m_iY = SvIV( perl_p2y );
- m_aoPlayers[1].m_iFrame = SvIV( perl_p2f );
- m_aoPlayers[1].m_iHitPoints = SvIV( perl_p2h ) / 10;
m_iGameOver = SvIV( perl_over );
m_bKO = SvIV( perl_ko );
+
+ for ( i=0; i<g_oState.m_iNumPlayers; ++i )
+ {
+ m_aoPlayers[i].m_iX = SvIV( perl_px[i] );
+ m_aoPlayers[i].m_iY = SvIV( perl_py[i] );
+ m_aoPlayers[i].m_iFrame = SvIV( perl_pf[i] );
+ m_aoPlayers[i].m_iHitPoints = SvIV( perl_ph[i] ) / 10;
+ m_aoPlayers[i].m_iRealHitPoints = SvIV( perl_phreal[i] );
+ }
// READ DOODAD DATA
if ( perl_doodad_x == NULL )
{
perl_doodad_x = get_sv("doodad_x", TRUE);
perl_doodad_y = get_sv("doodad_y", TRUE);
perl_doodad_t = get_sv("doodad_t", TRUE);
perl_doodad_f = get_sv("doodad_f", TRUE);
perl_doodad_dir = get_sv("doodad_dir", TRUE);
perl_doodad_gfxowner = get_sv("doodad_gfxowner", TRUE);
perl_doodad_text = get_sv("doodad_text", TRUE);
}
for ( m_iNumDoodads=0; m_iNumDoodads<MAXDOODADS; ++m_iNumDoodads )
{
PERLEVAL("GetNextDoodadData();");
SDoodad& oDoodad = m_aoDoodads[m_iNumDoodads];
oDoodad.m_iType = SvIV(perl_doodad_t);
if ( oDoodad.m_iType < 0 )
{
break;
}
oDoodad.m_iX = SvIV(perl_doodad_x);
oDoodad.m_iY = SvIV(perl_doodad_y);
oDoodad.m_iFrame = SvIV(perl_doodad_f);
oDoodad.m_iDir = SvIV(perl_doodad_dir);
oDoodad.m_iGfxOwner = SvIV(perl_doodad_gfxowner);
if ( oDoodad.m_iType == 0 )
{
oDoodad.m_sText = SvPV_nolen(perl_doodad_text);
}
else
{
oDoodad.m_sText = "";
}
}
// READ SOUND DATA
if ( perl_sound == NULL )
{
perl_sound = get_sv("sound", TRUE);
}
for ( m_iNumSounds=0; m_iNumSounds<MAXSOUNDS; ++m_iNumSounds )
{
PERLEVAL("GetNextSoundData();");
const char* pcSound = SvPV_nolen(perl_sound);
if ( NULL == pcSound
|| 0 == *pcSound )
{
break;
}
m_asSounds[ m_iNumSounds ] = pcSound;
//Audio->PlaySample( pcSound );
}
}
+bool Backend::IsDead( int a_iPlayer )
+{
+ return m_aoPlayers[ a_iPlayer ].m_iRealHitPoints <= 0;
+}
+
+
void Backend::PlaySounds()
{
for ( int i=0; i<m_iNumSounds; ++i )
{
Audio->PlaySample( m_asSounds[i].c_str() );
}
}
/***************************************************************************
PLAYBACK STRING CONVERSION ROUTINES
***************************************************************************/
void Backend::WriteToString( std::string& a_rsOutString )
{
char acBuffer[2048];
int iNumChars = sprintf( acBuffer, "%d %d %d %d %d %d %d %d %d %d %d ",
m_iBgX, m_iBgY,
m_aoPlayers[0].m_iX, m_aoPlayers[0].m_iY, m_aoPlayers[0].m_iFrame, m_aoPlayers[0].m_iHitPoints,
m_aoPlayers[1].m_iX, m_aoPlayers[1].m_iY, m_aoPlayers[1].m_iFrame, m_aoPlayers[1].m_iHitPoints,
m_iNumDoodads );
int i;
for ( i = 0; i<m_iNumDoodads; ++i )
{
SDoodad& roDoodad = m_aoDoodads[i];
iNumChars += sprintf( acBuffer+iNumChars, "%d %d %d %d %d %d %d %s ",
roDoodad.m_iX, roDoodad.m_iY, roDoodad.m_iType, roDoodad.m_iFrame,
roDoodad.m_iDir, roDoodad.m_iGfxOwner,
roDoodad.m_sText.size(), roDoodad.m_sText.c_str() );
}
iNumChars += sprintf( acBuffer+iNumChars, "%d ", m_iNumSounds );
for ( i = 0; i<m_iNumSounds; ++i )
{
iNumChars += sprintf( acBuffer+iNumChars, " %d %s",
m_asSounds[i].size(), m_asSounds[i].c_str() );
}
// debug( "Frame: '%s'\n", acBuffer );
a_rsOutString = acBuffer;
}
void Backend::ReadFromString( const std::string& a_rsString )
{
ReadFromString( a_rsString.c_str() );
}
void Backend::ReadFromString( const char* a_pcBuffer )
{
if ( strlen( a_pcBuffer ) < 10 )
{
m_iNumDoodads = m_iNumSounds = 0;
return;
}
int iNumMatches;
int iOffset, iTotal;
iNumMatches = sscanf( a_pcBuffer, "%d %d %d %d %d %d %d %d %d %d %d%n",
&m_iBgX, &m_iBgY,
&m_aoPlayers[0].m_iX, &m_aoPlayers[0].m_iY, &m_aoPlayers[0].m_iFrame, &m_aoPlayers[0].m_iHitPoints,
&m_aoPlayers[1].m_iX, &m_aoPlayers[1].m_iY, &m_aoPlayers[1].m_iFrame, &m_aoPlayers[1].m_iHitPoints,
&m_iNumDoodads, &iTotal );
if ( m_iNumDoodads > MAXDOODADS )
{
m_iNumDoodads = m_iNumSounds = 0;
return;
}
int i, j;
for ( i=0; i<m_iNumDoodads; ++i )
{
SDoodad& roDoodad = m_aoDoodads[i];
iNumMatches += sscanf( a_pcBuffer+iTotal, "%d %d %d %d %d %d %d %n",
&roDoodad.m_iX, &roDoodad.m_iY, &roDoodad.m_iType, &roDoodad.m_iFrame,
&roDoodad.m_iDir, &roDoodad.m_iGfxOwner,
&j, &iOffset );
iTotal += iOffset;
roDoodad.m_sText.assign( a_pcBuffer + iTotal, j );
iTotal += j;
}
iNumMatches += sscanf( a_pcBuffer + iTotal, "%d%n",
&m_iNumSounds, &iOffset );
if ( m_iNumSounds > MAXSOUNDS )
{
m_iNumSounds = 0;
return;
}
iTotal += iOffset;
for ( i=0; i<m_iNumSounds; ++i )
{
iNumMatches += sscanf( a_pcBuffer+iTotal, "%d %n",
&j, &iOffset );
iTotal += iOffset;
m_asSounds[i].assign( a_pcBuffer + iTotal, j );
iTotal += j;
}
}
diff --git a/src/Backend.h b/src/Backend.h
index 4d5eb57..f4f9beb 100644
--- a/src/Backend.h
+++ b/src/Backend.h
@@ -1,84 +1,86 @@
/***************************************************************************
Backend.h - description
-------------------
begin : Sun Dec 8 2002
copyright : (C) 2002 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#ifndef BACKEND_H
#define BACKEND_H
#include <string>
#include "FighterEnum.h"
class RlePack;
#define MAXDOODADS 20
#define MAXSOUNDS 20
class Backend
{
public:
// Lifecycle
Backend();
~Backend();
bool Construct();
// Miscellaneous
const char* PerlEvalF( const char* a_pcFormat, ... );
const char* GetPerlString( const char* a_pcScalarName );
int GetPerlInt( const char* a_pcScalarName );
// Fighter enumeration
int GetNumberOfFighters();
FighterEnum GetFighterID( int a_iIndex );
+ int GetNumberOfAvailableFighters();
// Game data
void AdvancePerl();
void ReadFromPerl();
+ bool IsDead( int a_iPlayer );
void PlaySounds();
void WriteToString( std::string& a_rsOutString );
void ReadFromString( const std::string& a_rsString );
void ReadFromString( const char* a_pcBuffer );
public:
int m_iGameTick;
int m_iGameOver;
bool m_bKO;
int m_iBgX, m_iBgY;
int m_iNumDoodads;
int m_iNumSounds;
struct SPlayer
{
- int m_iX, m_iY, m_iFrame, m_iHitPoints;
- } m_aoPlayers[2];
+ int m_iX, m_iY, m_iFrame, m_iHitPoints, m_iRealHitPoints;
+ } m_aoPlayers[MAXPLAYERS];
struct SDoodad
{
int m_iX, m_iY, m_iType, m_iFrame;
int m_iDir, m_iGfxOwner;
std::string m_sText;
} m_aoDoodads[ MAXDOODADS ];
std::string m_asSounds[ MAXSOUNDS ];
};
extern Backend g_oBackend;
int DoGame( char* replay, bool isReplay, bool bDebug );
#endif
diff --git a/src/Background.cpp b/src/Background.cpp
index fd3b729..fcd5732 100644
--- a/src/Background.cpp
+++ b/src/Background.cpp
@@ -1,141 +1,167 @@
/***************************************************************************
Background.cpp - description
-------------------
begin : Sun Jan 11 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
#include "Background.h"
#include "SDL.h"
#include "sge_surface.h"
#include "gfx.h"
#include "common.h"
#include <string>
#include <fstream>
/* Calculating background distance:
BW: background width
SW: screen width (640)
AW: arena width (1920)
D: distance
(AW - SW) * D = BW - SW
D = (BW - SW) / (AW - SW)
D = (BW - 640) / 1280
*/
Background::Background()
{
m_bOK = false;
m_iNumber = 0;
+ m_iFirstExtraLayer = 0;
}
Background::~Background()
{
Clear();
}
void Background::Clear()
{
for( LayerIterator it=m_aLayers.begin(); it!=m_aLayers.end(); ++it )
{
BackgroundLayer& roLayer = *it;
if ( roLayer.m_poSurface )
{
SDL_FreeSurface( roLayer.m_poSurface );
roLayer.m_poSurface = NULL;
}
}
m_aLayers.clear();
m_bOK = false;
m_iNumber = 0;
+ m_iFirstExtraLayer = 0;
}
void Background::Load( int a_iBackgroundNumber )
{
char acFilename[FILENAME_MAX+1];
// 1. Try loading a description-based background.
sprintf( acFilename, "%s/gfx/level%d.desc", DATADIR, a_iBackgroundNumber );
std::ifstream oInput( acFilename );
if ( !oInput.is_open() )
{
// Description-based background not found. Try simple image-based
// background.
sprintf( acFilename, "level%d.png", a_iBackgroundNumber );
SDL_Surface* poImage = LoadBackground( acFilename, 64 );
if ( NULL == poImage )
{
// Couldn't load background.
return;
}
BackgroundLayer oLayer;
oLayer.m_poSurface = poImage;
oLayer.m_iXOffset = 0;
oLayer.m_iYOffset = 0;
oLayer.m_dDistance = 1.0;
m_aLayers.push_back( oLayer );
m_iNumber = a_iBackgroundNumber;
+ m_iFirstExtraLayer = m_aLayers.size();
m_bOK = true;
return;
}
// 2. Parse description.
int iNumLayers;
oInput >> iNumLayers;
for ( int i=0; i<iNumLayers; ++i )
{
BackgroundLayer oLayer;
std::string sFilename;
oInput >> sFilename >> oLayer.m_iXOffset >> oLayer.m_iYOffset >> oLayer.m_dDistance;
oLayer.m_poSurface = LoadBackground( sFilename.c_str(), 64, 0 );
if ( NULL == oLayer.m_poSurface )
{
continue;
}
m_aLayers.push_back( oLayer );
}
+ m_iFirstExtraLayer = m_aLayers.size();
m_bOK = m_aLayers.size() > 0;
m_iNumber = m_bOK ? a_iBackgroundNumber : 0;
}
+/** Adds a layer to the background.
+
+The background object will assume ownership of the given structure, including
+the surface within.
+*/
+
+void Background::AddExtraLayer( const BackgroundLayer& a_roLayer )
+{
+ m_aLayers.push_back( a_roLayer );
+}
+
+
+void Background::DeleteExtraLayers()
+{
+ while ( m_aLayers.size() > m_iFirstExtraLayer )
+ {
+ SDL_FreeSurface( m_aLayers.back().m_poSurface );
+ m_aLayers.pop_back();
+ }
+}
+
+
bool Background::IsOK()
{
return m_bOK;
}
-void Background::Draw( int a_iXPosition, int a_iYPosition )
+void Background::Draw( int a_iXPosition, int a_iYPosition, int a_iYOffset )
{
for ( LayerIterator it = m_aLayers.begin(); it != m_aLayers.end(); ++it )
{
BackgroundLayer& roLayer = *it;
sge_Blit( roLayer.m_poSurface, gamescreen,
0, 0, // source position
roLayer.m_iXOffset - (int)( ((double)a_iXPosition) * roLayer.m_dDistance ),
- roLayer.m_iYOffset - (int)( ((double)a_iYPosition) * roLayer.m_dDistance ),
- SCREENWIDTH*3 + 100, SCREENHEIGHT + 100 );
+ roLayer.m_iYOffset - (int)( ((double)a_iYPosition) * roLayer.m_dDistance ) + a_iYOffset,
+ gamescreen->w*3 + 100, gamescreen->h + 100 );
}
}
diff --git a/src/Background.h b/src/Background.h
index 18862b9..428364c 100644
--- a/src/Background.h
+++ b/src/Background.h
@@ -1,45 +1,49 @@
/***************************************************************************
Background.h - description
-------------------
begin : Sun Jan 11 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
#ifndef __BACKGROUND_H
#define __BACKGROUND_H
#include <vector>
struct SDL_Surface;
struct BackgroundLayer
{
SDL_Surface* m_poSurface;
int m_iXOffset;
int m_iYOffset;
double m_dDistance;
};
typedef std::vector<BackgroundLayer> LayerVector;
typedef LayerVector::iterator LayerIterator;
class Background
{
public:
Background();
~Background();
void Clear();
void Load( int a_iBackgroundNumber );
+ void AddExtraLayer( const BackgroundLayer& a_roLayer );
+ void DeleteExtraLayers();
+
bool IsOK();
- void Draw( int a_iXPosition, int a_iYPosition );
+ void Draw( int a_iXPosition, int a_iYPosition, int a_iYOffset );
protected:
int m_iNumber;
+ int m_iFirstExtraLayer;
bool m_bOK;
LayerVector m_aLayers;
};
#endif // __BACKGROUND_H
diff --git a/src/Chooser.cpp b/src/Chooser.cpp
index dc3d993..8e1904d 100644
--- a/src/Chooser.cpp
+++ b/src/Chooser.cpp
@@ -1,163 +1,432 @@
/***************************************************************************
Chooser.cpp - description
-------------------
begin : Tue Jan 27 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
#include "Chooser.h"
+#include "PlayerSelect.h"
#include "Backend.h"
+#include "Event.h"
+#include "State.h"
#include "SDL_image.h"
#include "gfx.h"
#include "common.h"
+#include "sge_primitives.h"
CChooser g_oChooser;
CChooser::CChooser()
{
m_iNumberOfFighters = -1;
+ m_poScreen = NULL;
+ for ( int i=0; i<MAXPLAYERS; ++i )
+ {
+ m_abRectangleVisible[i] = false;
+ m_aiPlayerPosition[i] = 0;
+ }
+ x1=y1=0;
+ x2=y2=1;
+ m_iRows = m_iCols = 1;
}
CChooser::~CChooser()
{
// Should free the portraits..
}
+
void CChooser::Init()
{
if ( m_iNumberOfFighters >= 0 )
{
// Should only run once.
return;
}
+ m_aiColors[0] = C_LIGHTGREEN;
+ m_aiColors[1] = C_LIGHTMAGENTA;
+ m_aiColors[2] = C_YELLOW;
+ m_aiColors[3] = C_LIGHTBLUE;
+
// 1. Query the list of fighters from PlayerSelect.
m_iNumberOfFighters = g_oBackend.GetNumberOfFighters();
if ( m_iNumberOfFighters > 100 )
{
m_iNumberOfFighters = 100;
}
-
+ if ( m_iNumberOfFighters < 0 )
+ {
+ m_iNumberOfFighters = 0;
+ }
+
char pcFilename[FILENAME_MAX+1];
const char* s;
int i;
for ( i=0; i<m_iNumberOfFighters; ++i )
{
// Load the info of fighter #i
FighterEnum enFighter = g_oBackend.GetFighterID( i );
debug( "Fighter %d ID is %d\n", i, enFighter );
m_aenFighters[i] = enFighter;
// Load the portrait of fighter #i
g_oBackend.PerlEvalF( "GetFighterStats(%d);", enFighter );
s = g_oBackend.GetPerlString( "Portrait" );
strcpy( pcFilename, DATADIR );
strcat( pcFilename, "/characters/" );
strcat( pcFilename, s );
m_apoPortraits[i] = IMG_Load( pcFilename );
+ if ( m_apoPortraits[i] ) SDL_SetColorKey( m_apoPortraits[i], 0, 0 );
}
for ( i=m_iNumberOfFighters; i<100; ++i )
{
m_aenFighters[i] = UNKNOWN;
m_apoPortraits[i] = NULL;
}
Resize( 158, 74, 483, 479 );
+
+ for ( i=0; i<MAXPLAYERS; ++i )
+ {
+ m_aiPlayerPosition[i] = FighterToPosition( g_oPlayerSelect.GetPlayerInfo(i).m_enFighter );
+ }
}
#define LINEWIDTH 5
-void CChooser::Draw( SDL_Surface* a_poSurface )
+void CChooser::Draw()
{
Init();
- // 1. CREATE A BLACK AND WHITE BUFFER
-
- SDL_Surface* poBuffer = SDL_CreateRGBSurface( SDL_SWSURFACE, x2 - x1, y2 - y1, 8, 0, 0, 0, 0 );
-
- SDL_Color aoColors[256];
int i;
- for ( i=0; i<256; ++i )
- {
- aoColors[i].r = aoColors[i].g = aoColors[i].b = i;
- }
- SDL_SetColors( poBuffer, aoColors, 0, 256);
- SDL_SetColorKey( poBuffer, SDL_SRCCOLORKEY, 0 );
-
- // 2. START BLITTING THE CHARACTER PORTRAITS
-
- for ( i = 0; i<m_iNumberOfFighters; ++i )
+
+ for ( i = 0; i<m_iCols * m_iRows; ++i )
{
+
// Blit portrait # i
- if ( NULL == m_apoPortraits[i] )
+ if ( i >= m_iNumberOfFighters
+ || NULL == m_apoPortraits[i] )
{
+ DrawRectangle( i, C_BLACK );
continue;
}
// Calculate the bounding rectangle of the character portrait
- int iCol = i % m_iCols;
- int iRow = i / m_iCols;
- int w = (x2-x1-LINEWIDTH) / m_iCols + 1;
- int h = (y2-y1-LINEWIDTH) / m_iRows + 1;
- int x = (x2-x1-LINEWIDTH) * iCol / m_iCols + LINEWIDTH;
- int y = (y2-y1-LINEWIDTH) * iRow / m_iRows + LINEWIDTH;
-
+ SDL_Rect oDst = GetRect(i);
SDL_Rect oSrc;
- oSrc.x = oSrc.y = 0;
- oSrc.w = w;
- oSrc.h = h;
- SDL_Rect oDst;
- oDst.x = x;
- oDst.y = y;
+ oSrc.x = (m_apoPortraits[i]->w - oDst.w) / 2;
+ oSrc.y = (m_apoPortraits[i]->h - oDst.h) / 2;
+ oSrc.w = oDst.w;
+ oSrc.h = oDst.h;
- debug( "Drawing portrait %d at %d:%d %dx%d\n", i, x, y, w, h );
-
- SDL_BlitSurface( m_apoPortraits[i], &oSrc, poBuffer, &oDst );
+ Uint32 iBgColor = m_aenFighters[i] < 100 ? C_YELLOW: C_LIGHTBLUE;
+ sge_FilledRectAlpha( m_poScreen, oDst.x, oDst.y, oDst.x+oDst.w, oDst.y+oDst.h, iBgColor, 64 );
+ SDL_BlitSurface( m_apoPortraits[i], &oSrc, m_poScreen, &oDst );
+ DrawRectangle( i, C_BLACK );
}
+}
+
- SDL_Rect oDst;
- oDst.x = x1;
- oDst.y = y1;
- SDL_BlitSurface( poBuffer, NULL, a_poSurface, &oDst );
+
+void CChooser::Start( SDL_Surface* a_poScreen )
+{
+ Init();
+ m_poScreen = a_poScreen;
+}
+
+
+
+void CChooser::Stop()
+{
+ m_poScreen = NULL;
}
void CChooser::Resize( int a_x1, int a_y1, int a_x2, int a_y2 )
{
+ Init();
x1 = a_x1;
y1 = a_y1;
- x2 = a_x2;
- y2 = a_y2;
+ x2 = a_x2 + LINEWIDTH;
+ y2 = a_y2 + LINEWIDTH;
m_iRows = 1;
m_iCols = 1;
while ( m_iRows * m_iCols < m_iNumberOfFighters )
{
double dColDensity = double(x2-x1) / double(m_iCols);
double dRowDensity = double(y2-y1) / double(m_iRows);
- if ( dRowDensity > dColDensity )
+ if ( dRowDensity > dColDensity *1.1 )
{
m_iRows ++;
}
else
{
m_iCols ++;
}
}
}
+
+void CChooser::MarkFighter( FighterEnum a_enFighter, Uint32 a_iColor )
+{
+ SDL_Rect oRect = GetFighterRect( a_enFighter );
+ if ( oRect.w <= 0 )
+ {
+ return;
+ }
+
+ int x1 = oRect.x;
+ int y1 = oRect.y;
+ int w = oRect.w;
+ int h = oRect.h;
+
+ sge_Line(m_poScreen, x1+5, y1+5, x1 + w-10, y1 + h-10, a_iColor);
+ sge_Line(m_poScreen, x1 + w-10, y1+5, x1+5, y1 + h-10, a_iColor);
+ x1++;
+ sge_Line(m_poScreen, x1+5, y1+5, x1 + w-10, y1 + h-10, a_iColor);
+ sge_Line(m_poScreen, x1 + w-10, y1+5, x1+5, y1 + h-10, a_iColor);
+ y1++;
+ sge_Line(m_poScreen, x1+5, y1+5, x1 + w-10, y1 + h-10, a_iColor);
+ sge_Line(m_poScreen, x1 + w-10, y1+5, x1+5, y1 + h-10, a_iColor);
+ x1--;
+ sge_Line(m_poScreen, x1+5, y1+5, x1 + w-10, y1 + h-10, a_iColor);
+ sge_Line(m_poScreen, x1 + w-10, y1+5, x1+5, y1 + h-10, a_iColor);
+}
+
+
+
+SDL_Surface* CChooser::GetPortrait( FighterEnum a_enFighter )
+{
+ Init();
+
+ for ( int i=0; i<m_iNumberOfFighters; ++i )
+ {
+ if ( m_aenFighters[i] == a_enFighter )
+ {
+ return m_apoPortraits[i];
+ }
+ }
+ return NULL;
+}
+
+
+
+void CChooser::DrawPortrait( FighterEnum a_enFighter, SDL_Surface* a_poScreen, const SDL_Rect& a_roRect )
+{
+ SDL_Surface* poPortrait = GetPortrait( a_enFighter );
+
+ if ( NULL == poPortrait )
+ {
+ return ;
+ }
+
+ SDL_Rect oDst = a_roRect;
+ SDL_Rect oSrc;
+ oSrc.x = (poPortrait->w - oDst.w) / 2;
+ oSrc.y = (poPortrait->h - oDst.h) / 2;
+ oSrc.w = oDst.w;
+ oSrc.h = oDst.h;
+
+ SDL_BlitSurface( poPortrait, &oSrc, a_poScreen, &oDst );
+}
+
+
+
+FighterEnum CChooser::GetCurrentFighter( int a_iPlayer )
+{
+ return PositionToFighter( m_aiPlayerPosition[ a_iPlayer ] );
+}
+
+
+void CChooser::MoveRectangle( int a_iPlayer, int a_iDirection )
+{
+ int& riPlayerPosition = m_aiPlayerPosition[ a_iPlayer ];
+
+ int iNew = riPlayerPosition;
+
+ switch ( a_iDirection )
+ {
+ case Mk_UP: if ( iNew >= m_iCols ) iNew -= m_iCols; break;
+ case Mk_DOWN: if ( iNew / m_iCols < m_iRows-1 ) iNew += m_iCols; break;
+ case Mk_LEFT: if ( iNew % m_iCols > 0 ) --iNew; break;
+ case Mk_RIGHT: if ( iNew % m_iCols < m_iCols-1 ) ++iNew; break;
+ }
+
+ if ( iNew != riPlayerPosition )
+ {
+ ClearRectangle( a_iPlayer );
+ riPlayerPosition = iNew;
+ DrawRectangle( a_iPlayer );
+ }
+}
+
+
+void CChooser::SetRectangle( int a_iPlayer, FighterEnum a_enFighter )
+{
+ int& riPlayerPosition = m_aiPlayerPosition[ a_iPlayer ];
+ int iNew = FighterToPosition( a_enFighter );
+
+ if ( iNew != riPlayerPosition )
+ {
+ ClearRectangle( a_iPlayer );
+ riPlayerPosition = iNew;
+ DrawRectangle( a_iPlayer );
+ }
+}
+
+
+void CChooser::SetRectangleVisible( int a_iPlayer, bool a_bVisible )
+{
+ m_abRectangleVisible[ a_iPlayer ] = a_bVisible;
+ if ( a_bVisible )
+ {
+ DrawRectangle( a_iPlayer );
+ }
+ else
+ {
+ ClearRectangle( a_iPlayer );
+ }
+}
+
+
+bool CChooser::IsRectangleVisible( int a_iPlayer )
+{
+ return m_abRectangleVisible[ a_iPlayer ];
+}
+
+
+void CChooser::DrawRectangles( int a_iStartingWith )
+{
+ a_iStartingWith = a_iStartingWith % g_oState.m_iNumPlayers;
+ int i = a_iStartingWith;
+
+ while (1)
+ {
+ DrawRectangle(i);
+ i = (i+1) % g_oState.m_iNumPlayers;
+
+ if ( i==a_iStartingWith)
+ break;
+ }
+}
+
+
+
+int CChooser::FighterToPosition( FighterEnum a_enFighter )
+{
+ for ( int i=0; i<m_iNumberOfFighters; ++i )
+ {
+ if ( m_aenFighters[i] == a_enFighter )
+ {
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+
+FighterEnum CChooser::PositionToFighter( int a_iPosition )
+{
+ if ( a_iPosition < 0
+ || a_iPosition >= m_iNumberOfFighters )
+ {
+ return UNKNOWN;
+ }
+ return m_aenFighters[a_iPosition];
+}
+
+
+SDL_Rect CChooser::GetFighterRect( FighterEnum a_enFighter )
+{
+ return GetRect( FighterToPosition( a_enFighter ) );
+}
+
+
+SDL_Rect CChooser::GetRect( int a_iPosition )
+{
+ SDL_Rect oRect;
+ oRect.x = oRect.y = oRect.w = oRect.h = 0;
+
+ if ( a_iPosition < 0 || a_iPosition >= m_iRows*m_iCols )
+ return oRect;
+
+ int iRow = a_iPosition / m_iCols;
+ int iCol = a_iPosition % m_iCols;
+ oRect.x = (x2-x1-LINEWIDTH) * iCol / m_iCols + LINEWIDTH + x1;
+ oRect.y = (y2-y1-LINEWIDTH) * iRow / m_iRows + LINEWIDTH + y1;
+ oRect.w = (x2-x1-LINEWIDTH) * (iCol+1) / m_iCols + LINEWIDTH + x1 - oRect.x;
+ oRect.h = (y2-y1-LINEWIDTH) * (iRow+1) / m_iRows + LINEWIDTH + y1 - oRect.y;
+
+ return oRect;
+}
+
+
+void CChooser::DrawRectangle( int a_iPlayer )
+{
+ if ( ! m_abRectangleVisible[a_iPlayer] )
+ {
+ return;
+ }
+
+ DrawRectangle( m_aiPlayerPosition[a_iPlayer], m_aiColors[a_iPlayer] );
+}
+
+
+void CChooser::ClearRectangle( int a_iPlayer )
+{
+ DrawRectangle( m_aiPlayerPosition[a_iPlayer], C_BLACK );
+}
+
+
+void CChooser::DrawRectangle( int a_iPosition, Uint32 a_iColor )
+{
+ SDL_Rect oRect = GetRect( a_iPosition );
+ oRect.x -= LINEWIDTH - 1;
+ oRect.y -= LINEWIDTH - 1;
+
+/* ===1====
+ | |
+ 3 4
+ | |
+ ===2==== */
+
+ SDL_Rect r = oRect;
+ r.h = LINEWIDTH;
+ r.w += LINEWIDTH;
+ SDL_Rect r1 = r;
+ SDL_FillRect( m_poScreen, &r1, a_iColor );
+
+ r.y += oRect.h;
+ r1 = r;
+ SDL_FillRect( m_poScreen, &r1, a_iColor );
+
+ r.y = oRect.y;
+ r.w = LINEWIDTH;
+ r.h = oRect.h;
+ r1 = r;
+ SDL_FillRect( m_poScreen, &r1, a_iColor );
+
+ r.x += oRect.w;
+ r1 = r;
+ SDL_FillRect( m_poScreen, &r1, a_iColor );
+}
+
diff --git a/src/Chooser.h b/src/Chooser.h
index 6414e85..1c0663e 100644
--- a/src/Chooser.h
+++ b/src/Chooser.h
@@ -1,57 +1,73 @@
/***************************************************************************
Chooser.h - description
-------------------
begin : Tue Jan 27 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
#ifndef CHOOSER_H
#define CHOOSER_H
#include "FighterEnum.h"
+#include "common.h"
+#include "SDL.h"
-struct SDL_Surface;
class CChooser
{
public:
CChooser();
~CChooser();
+
+ void Start( SDL_Surface* m_poScreen );
+ void Stop();
- void Draw( SDL_Surface* a_poSurface );
- void DrawRectangles();
- void Resize( int x1, int y1, int x2, int y2 );
- FighterEnum GetCurrentFighter( int a_iPlayer );
- int GetCurrentPosition( int a_iPlayer );
- bool GetDone( int a_iPlayer );
-
- void SetDone( int a_iPlayer, bool a_bDone );
- void SetCurrentFighter( int a_iPlayer );
- void Move( int a_iPlayer, int a_iDirection );
+ void Resize( int x1, int y1, int x2, int y2 );
+ void Draw();
+ void MarkFighter( FighterEnum a_enFighter, Uint32 a_iColor );
+ SDL_Surface* GetPortrait( FighterEnum a_enFighter );
+ void DrawPortrait( FighterEnum a_enFighter, SDL_Surface* a_poScreen, const SDL_Rect& a_roRect );
+
+ FighterEnum GetCurrentFighter( int a_iPlayer );
+
+ void MoveRectangle( int a_iPlayer, int a_iDirection );
+ void SetRectangle( int a_iPlayer, FighterEnum a_enFighter );
+ void SetRectangleVisible( int a_iPlayer, bool a_bVisible );
+ bool IsRectangleVisible( int a_iPlayer );
+ void DrawRectangles( int a_iStartingWith );
+ SDL_Rect GetFighterRect( FighterEnum a_enFighter );
protected:
- void Init();
+ void Init();
+ int FighterToPosition( FighterEnum a_enFighter );
+ FighterEnum PositionToFighter( int a_iPosition );
+ SDL_Rect GetRect( int a_iPosition );
+
+ void ClearRectangle( int a_iPlayer );
+ void DrawRectangle( int a_iPlayer );
+ void DrawRectangle( int a_iPosition, Uint32 a_iColor );
protected:
- FighterEnum m_aenFighters[100];
- SDL_Surface* m_apoPortraits[100];
- bool m_iPlayerDone[2];
- int m_iPlayerX[2];
- int m_iPlayerY[2];
+ SDL_Surface* m_poScreen;
+ FighterEnum m_aenFighters[100];
+ SDL_Surface* m_apoPortraits[100];
+ bool m_abRectangleVisible[MAXPLAYERS];
+ int m_aiPlayerPosition[MAXPLAYERS];
+ Uint32 m_aiColors[MAXPLAYERS];
- int x1, y1, x2, y2;
+ int x1, y1, x2, y2;
- int m_iNumberOfFighters;
- int m_iRows;
- int m_iCols;
+ int m_iNumberOfFighters;
+ int m_iRows;
+ int m_iCols;
};
extern CChooser g_oChooser;
#endif // CHOOSER_H
diff --git a/src/Demo.cpp b/src/Demo.cpp
index c302fbb..083268a 100644
--- a/src/Demo.cpp
+++ b/src/Demo.cpp
@@ -1,451 +1,461 @@
/***************************************************************************
Demo.cpp - description
-------------------
begin : Wed Aug 16 22:18:47 CEST 2003
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#include "FlyingChars.h"
#include "SDL.h"
#include "State.h"
#include "gfx.h"
#include "common.h"
#include "Backend.h"
#include "RlePack.h"
#include "FighterStats.h" // #includes Demo.h
#include "Event.h"
#include "config.h"
Demo::Demo()
{
m_poFlyingChars = NULL;
m_bAdvanceGame = false;
}
Demo::~Demo()
{
delete m_poFlyingChars;
m_poFlyingChars = NULL;
}
int Demo::Advance( int a_iNumFrames, bool a_bFlip )
{
int iRetVal = 1;
if ( a_iNumFrames > 5 )
a_iNumFrames = 5;
if ( m_poBackground )
{
SDL_BlitSurface( m_poBackground, NULL, gamescreen, NULL );
}
if ( m_poFlyingChars )
{
iRetVal &= AdvanceFlyingChars( a_iNumFrames );
m_poFlyingChars->Draw();
}
if ( m_bAdvanceGame )
{
iRetVal &= AdvanceGame( a_iNumFrames );
//@ DRAW GAME?
}
if ( a_bFlip )
{
SDL_Flip( gamescreen );
}
return iRetVal;
}
int Demo::AdvanceFlyingChars( int a_iNumFrames )
{
m_poFlyingChars->Advance( a_iNumFrames );
return ( m_poFlyingChars->IsDone() ? 1 : 0 );
}
int Demo::AdvanceGame( int a_iNumFrames )
{
for ( int i=0; i<a_iNumFrames; ++i )
{
g_oBackend.AdvancePerl();
}
return 0;
}
int Demo::Run()
{
SState::TGameMode enOriginalGameMode = g_oState.m_enGameMode;
int thisTick, lastTick, firstTick, gameSpeed;
gameSpeed = 12;
/*
if ( m_poBackground )
{
DrawTextMSZ( "Press Escape for the menu", impactFont, 10, 450, UseShadow, C_WHITE, m_poBackground );
}
*/
thisTick = SDL_GetTicks() / gameSpeed;
lastTick = thisTick - 1;
firstTick = SDL_GetTicks() / gameSpeed;
while ( 1 )
{
// 1. Wait for the next tick (on extremely fast machines..)
while (1)
{
thisTick = SDL_GetTicks() / gameSpeed;
if ( thisTick==lastTick )
{
SDL_Delay(1);
}
else
{
break;
}
}
// 2. Call ADVANCE.
int iRetVal = Advance(thisTick-lastTick, true);
lastTick = thisTick;
if ( iRetVal )
{
return 0;
}
// 3. Handle events.
+ SDL_Event oSdlEvent;
SMortalEvent oEvent;
- while ( MortalPollEvent(oEvent) )
+ while ( SDL_PollEvent(&oSdlEvent) )
{
+ TranslateEvent( &oSdlEvent, &oEvent );
switch (oEvent.m_enType)
{
case Me_QUIT:
g_oState.m_bQuitFlag = true;
break;
case Me_MENU:
OnMenu();
break;
case Me_PLAYERKEYDOWN:
if ( oEvent.m_iKey < 4 )
break;
if ( SState::IN_DEMO == g_oState.m_enGameMode )
{
OnMenu();
}
else
{
return 0;
}
break;
case Me_SKIP:
return 0;
case Me_NOTHING:
+ if ( SDL_KEYDOWN == oSdlEvent.type && SState::IN_DEMO == g_oState.m_enGameMode )
+ {
+ OnMenu();
+ continue;
+ }
+
+ break;
+
case Me_PLAYERKEYUP:
break;
} // switch
} // while MortalPollEvent
if ( g_oState.m_enGameMode != enOriginalGameMode
|| g_oState.m_bQuitFlag )
{
return 1;
}
} // while 1;
}
void Demo::OnMenu()
{
::DoMenu();
}
class CreditsDemo: public Demo
{
public:
CreditsDemo()
{
- m_poBackground = LoadBackground( "Credits.png", 240 );
+ m_poBackground = LoadBackground( "Credits.jpg", 240 );
SDL_UnlockSurface( m_poBackground );
DrawGradientText( "Credits", titleFont, 20, m_poBackground );
SDL_Flip( m_poBackground );
SDL_Rect oRect;
oRect.x = 110; oRect.w = gamescreen->w - 220;
oRect.y = 100; oRect.h = 350;
m_poFlyingChars = new FlyingChars( creditsFont, oRect );
m_sText1 = Translate( "CreditsText1" );
m_sText2 = Translate( "CreditsText2" );
m_sText3 = Translate( "CreditsText3" );
m_poFlyingChars->AddText( m_sText1.c_str(), FlyingChars::FC_AlignCenter, true );
m_poFlyingChars->AddText( m_sText2.c_str(), FlyingChars::FC_AlignJustify, true );
m_poFlyingChars->AddText( m_sText3.c_str(), FlyingChars::FC_AlignCenter, true );
m_poFlyingChars->AddText( "\n\n\n\n\n\n:)", FlyingChars::FC_AlignRight, true );
}
protected:
std::string m_sText1;
std::string m_sText2;
std::string m_sText3;
};
class Story1Demo: public Demo
{
public:
Story1Demo()
{
- m_poBackground = LoadBackground( "Story1.png", 240 );
+ m_poBackground = LoadBackground( "Story1.jpg", 240 );
SDL_UnlockSurface( m_poBackground );
SDL_Rect oRect;
oRect.x = 50; oRect.w = gamescreen->w - 100;
oRect.y = 50; oRect.h = gamescreen->h - 100;
m_poFlyingChars = new FlyingChars( storyFont, oRect, -1 );
m_sText1 = Translate( "Story1Text" );
m_poFlyingChars->AddText( m_sText1.c_str(), FlyingChars::FC_AlignJustify, true );
}
protected:
std::string m_sText1;
};
class Story2Demo: public Demo
{
public:
Story2Demo()
{
- m_poBackground = LoadBackground( "Story2.png", 240 );
+ m_poBackground = LoadBackground( "Story2.jpg", 240 );
SDL_UnlockSurface( m_poBackground );
SDL_Rect oRect;
oRect.x = 50; oRect.w = gamescreen->w - 100;
oRect.y = 50; oRect.h = gamescreen->h - 100;
m_poFlyingChars = new FlyingChars( storyFont, oRect, -1 );
m_sText1 = Translate( "Story2Text" );
m_poFlyingChars->AddText( m_sText1.c_str(), FlyingChars::FC_AlignJustify, true );
}
protected:
std::string m_sText1;
};
class MainScreenDemo: public Demo
{
public:
MainScreenDemo()
{
i = 0;
m_iTimeLeft = 50;
- m_poBackground = LoadBackground( "Mortal.png", 240 );
+ m_poBackground = LoadBackground( "Mortal.jpg", 240 );
DrawTextMSZ( "Version " VERSION " ゥ 2003-2004 by UPi", inkFont, 320, 430, UseShadow | AlignHCenter, C_WHITE, m_poBackground, false );
std::string sStaffFilename = DATADIR;
sStaffFilename += "/characters/STAFF.DAT";
- m_poPack = new RlePack( sStaffFilename.c_str(), 240 );
+ m_poPack = new RlePack( sStaffFilename.c_str(), 255 );
m_poPack->ApplyPalette();
SDL_BlitSurface( m_poBackground, NULL, gamescreen, NULL );
SDL_Flip( gamescreen );
int j, k, l;
for ( j=0; j<14; ++j )
{
m_aiOrder[j] = j;
m_bShown[j] = false;
}
for ( j=0; j<14; ++j )
{
k = rand() % 14;
l = m_aiOrder[j]; m_aiOrder[j] = m_aiOrder[k]; m_aiOrder[k] = l;
}
}
~MainScreenDemo()
{
delete m_poPack;
m_poPack = NULL;
}
int Advance( int a_iNumFrames, bool a_bFlip )
{
static int x[14] = {
0, 26, 67, 125, 159, 209,
249, 289, 358, 397, 451, 489, 532, 161 };
static int y[14] = {
5, 4, 5, 5, 5, 7,
4, 0, 7, 5, 5, 6, 5, 243 };
m_iTimeLeft -= a_iNumFrames;
if ( m_iTimeLeft <= 0
&& i >= 14 )
{
return 1;
}
if ( m_iTimeLeft <= 0 )
{
m_bShown[ m_aiOrder[i] ] = true;
for ( int j=0; j<=14; ++j )
{
if ( m_bShown[j] )
{
m_poPack->Draw( j, x[j], y[j], false );
}
}
SDL_Flip( gamescreen );
++i;
m_iTimeLeft += 20;
if ( i >= 14 )
{
m_iTimeLeft += 50;
}
}
return 0;
}
protected:
RlePack* m_poPack;
int m_iTimeLeft;
int i;
int m_aiOrder[14];
bool m_bShown[14];
};
void DoReplayDemo()
{
static int aiOrder[6] = {-1, -1, -1, -1, -1, -1};
static int iNext = 0;
if ( aiOrder[0]<0 )
{
// shuffle
int i, j, k;
for ( i=0; i<6; ++i ) aiOrder[i]=i;
for ( i=0; i<6; ++i )
{
j = rand() % 6;
k = aiOrder[i];
aiOrder[i] = aiOrder[j];
aiOrder[j] = k;
}
iNext = 0;
}
char acFilename[1024];
sprintf( acFilename, DATADIR "/demo%d.om", aiOrder[iNext] );
// DoGame( acFilename, true, false );
iNext = ( iNext + 1 ) % 6;
}
static bool g_bFirstTime = true;
void DoDemos()
{
#define DoDemos_BREAKONEND \
if ( g_oState.m_enGameMode != SState::IN_DEMO \
|| g_oState.m_bQuitFlag ) \
return;
if ( g_bFirstTime )
{
g_bFirstTime = false;
}
else
{
MainScreenDemo oDemo;
oDemo.Run();
}
while (1)
{
DoDemos_BREAKONEND;
DoReplayDemo();
DoDemos_BREAKONEND;
{
Story1Demo oDemo;
oDemo.Run();
}
DoDemos_BREAKONEND;
DoReplayDemo();
DoDemos_BREAKONEND;
{
FighterStatsDemo oDemo;
oDemo.Run();
}
DoDemos_BREAKONEND;
DoReplayDemo();
DoDemos_BREAKONEND;
{
Story2Demo oDemo;
oDemo.Run();
}
DoDemos_BREAKONEND;
{
FighterStatsDemo oDemo;
oDemo.Run();
}
DoDemos_BREAKONEND;
DoReplayDemo();
DoDemos_BREAKONEND;
{
CreditsDemo oDemo;
oDemo.Run();
}
DoDemos_BREAKONEND;
{
MainScreenDemo oDemo;
oDemo.Run();
}
DoDemos_BREAKONEND;
}
}
diff --git a/src/FighterStats.cpp b/src/FighterStats.cpp
index 5b83a58..d68cc16 100644
--- a/src/FighterStats.cpp
+++ b/src/FighterStats.cpp
@@ -1,255 +1,255 @@
/***************************************************************************
FighterStats.cpp - description
-------------------
begin : Tue Dec 10 2002
copyright : (C) 2002 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#include <stdio.h>
#include <string>
#include "PlayerSelect.h"
#include "FlyingChars.h"
#include "SDL.h"
#include "SDL_video.h"
#include "SDL_image.h"
#include "sge_tt_text.h"
#include "common.h"
#include "gfx.h"
#include "RlePack.h"
#include "Backend.h"
#include "State.h"
#include "FighterStats.h"
#include "MszPerl.h"
extern PerlInterpreter* my_perl;
#define LEFTMARGIN 160
#define TOPMARGIN 70
#define RIGHTMARGIN 630
#define LINEHEIGHT 35
#define GAPWIDTH 20
#define DESCMARGIN 50
#define LEFTMARGIN2 ((LEFTMARGIN+RIGHTMARGIN+GAPWIDTH)/2)
/*
void DrawMultiLineText( const char* text, _sge_TTFont* font, int x, int y,
int fg, SDL_Surface* target )
{
const char* s1, * s2;
char line[1024];
s1 = s2 = text;
while (1)
{
while ( isspace(*s2) ) s2++;
if ( !*s2 )
break;
s1 = s2; // s1: start of line.
while ( (*s2 != '\n') && *s2 ) s2++;
strncpy( line, s1, s2-s1 );
line[s2-s1] = 0;
DrawTextMSZ( line, font, x, y, 0, fg, target );
y += sge_TTF_FontLineSkip( font );
}
}
*/
int FighterStatsDemo::mg_iLastFighter = -1;
FighterEnum FighterStatsDemo::mg_aenFighterOrder[LASTFIGHTER-1];
FighterStatsDemo::FighterStatsDemo( FighterEnum a_iFighter )
{
m_iTimeLeft = 500;
m_poStaff = NULL;
- m_poBackground = LoadBackground( "FighterStats.png", 64 );
+ m_poBackground = LoadBackground( "FighterStats.jpg", 64 );
DrawGradientText( "Fighter Stats", titleFont, 10, m_poBackground );
SDL_BlitSurface( m_poBackground, NULL, gamescreen, NULL );
SDL_Flip( gamescreen );
if ( mg_iLastFighter < 0 )
{
// First run; create shuffled array of fighters.
mg_iLastFighter = 0;
int i, j;
FighterEnum k;
for ( i=0; i<LASTFIGHTER-1; ++i )
{
mg_aenFighterOrder[i] = (FighterEnum)(i+1);
}
for ( i=0; i<LASTFIGHTER-1; ++i )
{
j = rand() % (LASTFIGHTER-1);
k = mg_aenFighterOrder[i];
mg_aenFighterOrder[i] = mg_aenFighterOrder[j];
mg_aenFighterOrder[j] = k;
}
}
if ( a_iFighter <= UNKNOWN )
{
mg_iLastFighter = (mg_iLastFighter+1) % (LASTFIGHTER-1);
m_enFighter = mg_aenFighterOrder[mg_iLastFighter];
}
else
{
m_enFighter = a_iFighter;
}
if ( g_oPlayerSelect.IsFighterAvailable( m_enFighter ) )
{
g_oPlayerSelect.SetPlayer( 0, m_enFighter );
- g_oBackend.PerlEvalF( "SelectStart();" );
+ g_oBackend.PerlEvalF( "SelectStart(%d);", 2 );
}
else
{
std::string sStaffFilename = DATADIR;
sStaffFilename += "/characters/STAFF.DAT";
- m_poStaff = new RlePack( sStaffFilename.c_str(), 240 );
+ m_poStaff = new RlePack( sStaffFilename.c_str(), 255 );
}
g_oBackend.PerlEvalF("GetFighterStats(%d);", m_enFighter );
_sge_TTFont* font = impactFont;
int y = TOPMARGIN;
AV *StatTags = get_av( "StatTags", FALSE );
char *s, *sTag;
s = SvPV_nolen(get_sv("Name", FALSE));
DrawTextMSZ( s, inkFont, (LEFTMARGIN + RIGHTMARGIN)/2, y, AlignHCenter, C_WHITE, m_poBackground );
y+= 10;
s = SvPV_nolen(get_sv("Team", FALSE ));
sTag = SvPV_nolen( *av_fetch( StatTags, 1, false ) );
int i = DrawTextMSZ( sTag, font, LEFTMARGIN, y+=LINEHEIGHT, 0, C_YELLOW, m_poBackground );
DrawTextMSZ( s, font, LEFTMARGIN+i, y, 0, C_ORANGE, m_poBackground, false );
s = SvPV_nolen(get_sv("Style", FALSE ));
sTag = SvPV_nolen( *av_fetch( StatTags, 2, false ) );
i = DrawTextMSZ( sTag, font, LEFTMARGIN2, y, 0, C_YELLOW, m_poBackground );
DrawTextMSZ( s, font, LEFTMARGIN2+i, y, 0, C_ORANGE, m_poBackground, false );
s = SvPV_nolen(get_sv("Age", FALSE ));
sTag = SvPV_nolen( *av_fetch( StatTags, 3, false ) );
i = DrawTextMSZ( sTag, font, LEFTMARGIN, y+=LINEHEIGHT, 0, C_YELLOW, m_poBackground );
DrawTextMSZ( s, font, LEFTMARGIN+i, y, 0, C_ORANGE, m_poBackground, false );
s = SvPV_nolen(get_sv("Weight", FALSE ));
sTag = SvPV_nolen( *av_fetch( StatTags, 4, false ) );
i = DrawTextMSZ( sTag, font, LEFTMARGIN2, y, 0, C_YELLOW, m_poBackground );
DrawTextMSZ( s, font, LEFTMARGIN2+i, y, 0, C_ORANGE, m_poBackground, false );
s = SvPV_nolen(get_sv("Height", FALSE ));
sTag = SvPV_nolen( *av_fetch( StatTags, 5, false ) );
i = DrawTextMSZ( sTag, font, LEFTMARGIN, y+=LINEHEIGHT, 0, C_YELLOW, m_poBackground );
DrawTextMSZ( s, font, LEFTMARGIN+i, y, 0, C_ORANGE, m_poBackground, false );
s = SvPV_nolen(get_sv("Shoe", FALSE ));
sTag = SvPV_nolen( *av_fetch( StatTags, 6, false ) );
i = DrawTextMSZ( sTag, font, LEFTMARGIN2, y, 0, C_YELLOW, m_poBackground );
DrawTextMSZ( s, font, LEFTMARGIN2+i, y, 0, C_ORANGE, m_poBackground, false );
m_sStory = SvPV_nolen(get_sv("Story", FALSE ));
SDL_Rect oFlyingRect;
oFlyingRect.x = LEFTMARGIN;
oFlyingRect.y = y+DESCMARGIN;
oFlyingRect.w = gamescreen->w - oFlyingRect.x - 20;
oFlyingRect.h = gamescreen->h - oFlyingRect.y - 10;
m_poFlyingChars = new FlyingChars( creditsFont, oFlyingRect );
m_poFlyingChars->AddText( m_sStory.c_str(), FlyingChars::FC_AlignJustify, false );
if ( g_oPlayerSelect.IsFighterAvailable( m_enFighter ) )
{
m_sKeys = SvPV_nolen(get_sv("Keys", TRUE ));
m_poFlyingChars->AddText( "\n\nKEYS\n", FlyingChars::FC_AlignCenter, true );
m_poFlyingChars->AddText( m_sKeys.c_str(), FlyingChars::FC_AlignCenter, true );
}
else
{
m_sKeys = Translate("Unfortunately this fighter is not yet playable.");
m_poFlyingChars->AddText( m_sKeys.c_str(), FlyingChars::FC_AlignLeft, true );
}
}
FighterStatsDemo::~FighterStatsDemo()
{
delete m_poStaff;
}
int FighterStatsDemo::Advance( int a_iNumFrames, bool a_bFlip )
{
if ( a_iNumFrames > 5 ) a_iNumFrames = 5;
if ( m_poFlyingChars->IsDone() )
{
m_iTimeLeft -= a_iNumFrames;
}
AdvanceFlyingChars( a_iNumFrames );
SDL_BlitSurface( m_poBackground, NULL, gamescreen, NULL );
m_poFlyingChars->Draw();
// 2. Advance as many ticks as necessary..
if ( g_oPlayerSelect.IsFighterAvailable( m_enFighter ) )
{
for (int i=0; i<a_iNumFrames; ++i )
{
g_oBackend.AdvancePerl();
}
int p1x = SvIV(get_sv("p1x", TRUE));
int p1y = SvIV(get_sv("p1y", TRUE));
int p1f = SvIV(get_sv("p1f", TRUE));
if (p1f) g_oPlayerSelect.GetPlayerInfo(0).m_poPack->Draw( ABS(p1f)-1, p1x, p1y, p1f<0 );
}
else
{
static FighterEnum f[14] = {
UPI, ZOLI, SURBA, ULMAR, MISI, BENCE,
DESCANT, KINGA, GRIZLI, SIRPI, MACI, DANI, CUMI,
AMBRUS };
for ( int i=0; i<14; ++i )
{
if ( m_enFighter == f[i] )
{
//m_poStaff->draw( i, 10, 120 );
break;
}
}
}
if ( SState::IN_DEMO != g_oState.m_enGameMode )
{
sge_BF_textout( gamescreen, fastFont, Translate("Press F1 to skip..."), 230, 450 );
}
SDL_Flip( gamescreen );
return (m_iTimeLeft > 0) ? 0 : 1;
}
diff --git a/src/Game.cpp b/src/Game.cpp
index bce2d16..f128622 100644
--- a/src/Game.cpp
+++ b/src/Game.cpp
@@ -1,1082 +1,1282 @@
/***************************************************************************
Game.cpp - description
-------------------
begin : Mon Sep 24 2001
copyright : (C) 2001 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#include <string.h>
#include <stdio.h>
#include "SDL_image.h"
#include "sge_surface.h"
#include "sge_primitives.h"
#include "sge_bm_text.h"
#include "SDL_events.h"
#include "SDL_keysym.h"
#include <fstream>
#include "Event.h"
#include "PlayerSelect.h"
#include "Background.h"
#include "common.h"
#include "gfx.h"
#include "Backend.h"
#include "RlePack.h"
#include "State.h"
#include "Game.h"
#include "Audio.h"
#include "MortalNetwork.h"
#include "MszPerl.h"
extern PerlInterpreter* my_perl;
int Game::mg_iBackgroundNumber = 1;
/*
GAME PALETTE
From To Num Desc
---------------------------------------
0 .. 63 64 Background
64 .. 111 48 Doodads
112 .. 175 64 1st Fighter
176 .. 239 64 2nd Fighter
240 .. 255 16 basic colors
*/
#define MAXFRAMESKIP 5
/***************************************************************************
SFpsCounter CLASS
***************************************************************************/
struct SFpsCounter
{
int m_iLastCheck; // Last second then Tick() was called
int m_iFrames; // The number of frames in this second so far
int m_iFps; // The number of frames in the last second
void Reset()
{
m_iLastCheck = m_iFrames = m_iFps = 0;
}
void Tick()
{
int iSecond = SDL_GetTicks() / 1000;
if ( iSecond > m_iLastCheck )
{
m_iLastCheck = iSecond;
m_iFps = m_iFrames;
m_iFrames = 0;
// fprintf( stderr, "%d ", m_iFps);
}
++m_iFrames;
}
} oFpsCounter;
/***************************************************************************
CKeyQueue CLASS
***************************************************************************/
CKeyQueue::CKeyQueue()
{
Reset();
}
CKeyQueue::~CKeyQueue() {}
void CKeyQueue::Reset()
{
m_oKeys.clear();
}
void CKeyQueue::EnqueueKey( int a_iAtTime, int a_iPlayer, int a_iKey, bool a_bDown )
{
debug( "EnqueueKey( %d, %d, %d, %d ) at %d\n", a_iAtTime, a_iPlayer, a_iKey, a_bDown, g_oBackend.m_iGameTick );
SEnqueuedKey oKey;
oKey.iTime = a_iAtTime;
oKey.iPlayer = a_iPlayer;
oKey.iKey = a_iKey;
oKey.bDown = a_bDown;
if ( m_oKeys.size() == 0 )
{
m_oKeys.push_front( oKey );
return;
}
// Try to find the appropriate time in the list
// We maintain the list so that it is sorted in a descending time
// order. This means that usually we enqueue keys at the front and
// dequeue them at the end, but sometimes a key is inserted in mid-queue.
TEnqueuedKeyList::iterator it;
for ( it=m_oKeys.begin(); it!=m_oKeys.end(); ++it )
{
if ( it->iTime <= a_iAtTime )
{
m_oKeys.insert( it, oKey );
return;
}
}
// a_iAtTime is smaller than any time in the queue, so it goes to the end.
m_oKeys.push_back( oKey );
}
/**
If
*/
void CKeyQueue::DequeueKeys( int a_iToTime )
{
while ( m_oKeys.size() > 0
&& m_oKeys.back().iTime <= a_iToTime )
{
SEnqueuedKey& roKey = m_oKeys.back();
debug( "Dequeued key at %d tick: %d time, %d player, %d key, %d down\n", a_iToTime, roKey.iTime, roKey.iPlayer, roKey.iKey, roKey.bDown );
g_oBackend.PerlEvalF( roKey.bDown ? "KeyDown(%d,%d);" : "KeyUp(%d,%d);", roKey.iPlayer, roKey.iKey );
m_oKeys.pop_back();
}
}
/***************************************************************************
GAME PUBLIC METHODS
***************************************************************************/
-Game::Game( bool a_bIsReplay, bool a_bDebug)
+Game::Game( bool a_bIsReplay, bool a_bWide, bool a_bDebug)
{
m_bIsReplay = a_bIsReplay;
+ m_bWide = a_bWide;
m_bDebug = a_bDebug;
m_enInitialGameMode = g_oState.m_enGameMode;
if ( IsNetworkGame() )
{
mg_iBackgroundNumber = g_poNetwork->GetGameParams().iBackgroundNumber;
}
m_poBackground = new Background();
m_poBackground->Load(mg_iBackgroundNumber++);
if ( !m_poBackground->IsOK() )
{
m_poBackground->Load(1);
mg_iBackgroundNumber = 1;
}
- m_poDoodads = LoadBackground( "Doodads.png", 48, 64 );
+ m_poDoodads = LoadBackground( "Doodads.png", 48, 64, true );
- m_aiRoundsWonByPlayer[0] = m_aiRoundsWonByPlayer[1] = 0;
+ int i;
+ for ( i=0; i<g_oState.m_iNumPlayers; ++i )
+ {
+ m_aiRoundsWonByPlayer[i] = 0;
+ m_aiHitPointDisplayX[i] = 4 + ((i%2) * 396) + (m_bWide ? 130 : 0);
+ m_aiHitPointDisplayY[i] = 10 + (i/2) * 45;
+ m_abHitPointDisplayLeft[i] = ! (i%2);
+ }
+
m_iNumberOfRounds = 0;
SDL_EnableUNICODE( 0 );
m_iEnqueueDelay = 10;
+
}
Game::~Game()
{
delete m_poBackground;
m_poBackground = NULL;
SDL_FreeSurface( m_poDoodads );
m_poDoodads = NULL;
}
/** Runs a whole game, with two or three rounds.
\retval 0 if player 1 has won.
\retval 1 if player 2 has won.
\retval -1 if the game was a draw.
*/
int Game::Run()
{
- do
+ while (1)
{
+ // 1. START A ROUND
+ // This will update m_iNumberOfRounds and m_aiRoundsWonByPlayer[]
+
m_sReplayString = "";
m_aReplayOffsets.clear();
DoOneRound();
if ( g_oState.m_bQuitFlag
|| m_enInitialGameMode != g_oState.m_enGameMode )
{
return -1;
}
- } while ( m_aiRoundsWonByPlayer[0] < 2
- && m_aiRoundsWonByPlayer[1] < 2
- && m_iNumberOfRounds < 3 );
-
- if ( m_aiRoundsWonByPlayer[1] > m_aiRoundsWonByPlayer[0] ) return 1;
- if ( m_aiRoundsWonByPlayer[1] < m_aiRoundsWonByPlayer[0] ) return 0;
- return -1;
+
+ // 2. CHECK FOR END-OF-MATCH CONDITIONS
+
+ int i;
+ for ( i=0; i<g_oState.m_iNumPlayers; ++i )
+ {
+ if ( m_aiRoundsWonByPlayer[i] >= 2 ) {
+ return i;
+ }
+ }
+
+ if ( m_iNumberOfRounds > g_oState.m_iNumPlayers )
+ {
+ return -1;
+ }
+ }
}
/** Returns the replay string of the last round.
*/
std::string& Game::GetReplay()
{
return m_sReplayString;
}
/***************************************************************************
GAME DRAWING METHODS
***************************************************************************/
+
+
+void Game::DrawHitPointDisplay( int a_iPlayer )
+{
+ int iX = m_aiHitPointDisplayX[a_iPlayer];
+ int iY = m_aiHitPointDisplayY[a_iPlayer];
+ bool bLeft = m_abHitPointDisplayLeft[a_iPlayer];
+ int iHp = g_oBackend.m_aoPlayers[a_iPlayer].m_iHitPoints;
+
+ SDL_Rect oSrcRect, oDstRect;
+
+ // The green part
+ oSrcRect.x = bLeft ? 0 : (100-iHp)*2;
+ oSrcRect.y = 154;
+ oSrcRect.h = 20;
+ oSrcRect.w = iHp * 2;
+
+ oDstRect.y = iY + m_iYOffset;
+ oDstRect.x = iX + oSrcRect.x + (bLeft ? 36 : 0 );
+
+ SDL_BlitSurface( m_poDoodads, &oSrcRect, gamescreen, &oDstRect );
+
+ // The red part
+ if ( bLeft )
+ oDstRect.x += iHp * 2;
+ else
+ oDstRect.x = iX;
+ oSrcRect.x = (100+iHp) * 2;
+ oSrcRect.w = (100-iHp) * 2;
+ SDL_BlitSurface( m_poDoodads, &oSrcRect, gamescreen, &oDstRect );
+
+ // The "won" icon
+
+ oSrcRect.x = 0;
+ oSrcRect.y = 276;
+ oSrcRect.w = 32;
+ oSrcRect.h = 32;
+ if ( m_aiRoundsWonByPlayer[a_iPlayer] > 0 )
+ {
+ oDstRect.x = iX + (bLeft ? 0 : 204);
+ oDstRect.y = iY-4 + m_iYOffset;
+ SDL_BlitSurface( m_poDoodads, &oSrcRect , gamescreen, &oDstRect );
+ }
+
+ int iTextW = g_oPlayerSelect.GetFighterNameWidth(a_iPlayer);
+ int iTextX = bLeft ? iX + 230 - iTextW : iX + 10 ;
+ if ( iTextX + iTextW + 5 > gamescreen->w ) iTextX = gamescreen->w - iTextW - 5;
+ if ( iTextX < iX + 5 ) iTextX = iX + 5;
+ sge_BF_textout( gamescreen, fastFont, g_oPlayerSelect.GetFighterName(a_iPlayer),
+ iTextX, iY/*+ 38*/ + m_iYOffset );
+
+}
+
/** Draws the hitpoint bars that are displayed on the top of the screen.
Also draws the fighter names below the bars.
Input variables:
\li g_oBackend.m_aoPlayers[x].m_iHitPoints
\li g_oPlayerSelect.GetFighterName
*/
-void Game::DrawHitPointDisplay()
+void Game::DrawHitPointDisplays()
{
+ for ( int i=0; i<g_oState.m_iNumPlayers; ++i )
+ {
+ DrawHitPointDisplay(i);
+ }
+/*
int hp1 = g_oBackend.m_aoPlayers[0].m_iHitPoints;// * 100 / g_oState.m_iHitPoints;
int hp2 = g_oBackend.m_aoPlayers[1].m_iHitPoints;// * 100 / g_oState.m_iHitPoints;
SDL_Rect src, dst;
src.y = 154;
src.h = 20;
- dst.y = 15;
+ dst.y = 15 + m_iYOffset;
// Player 1, green part.
dst.x = 40;
src.x = 0;
src.w = hp1*2;
SDL_BlitSurface( m_poDoodads, &src, gamescreen, &dst );
// Player 1, red part.
dst.x += hp1*2;
src.x = (100 + hp1)*2;
src.w = (100-hp1)*2;
SDL_BlitSurface( m_poDoodads, &src, gamescreen, &dst );
// Player 2, red part.
dst.x = 400;
src.x = 200;
src.w = (100-hp2)*2;
SDL_BlitSurface( m_poDoodads, &src, gamescreen, &dst );
// Player 2, green part.
dst.x = 400 + (100-hp2)*2;
src.x = (100-hp2)*2;
src.w = hp2*2;
SDL_BlitSurface( m_poDoodads, &src, gamescreen, &dst );
// "Won" icon for Player 1
src.x = 0; src.y = 276; src.w = 32; src.h = 32;
if ( m_aiRoundsWonByPlayer[0] > 0 )
{
- dst.x = 4; dst.y = 11;
+ dst.x = 4; dst.y = 11 + m_iYOffset;
SDL_BlitSurface( m_poDoodads, &src, gamescreen, &dst );
}
if ( m_aiRoundsWonByPlayer[1] > 0 )
{
- dst.x = 604; dst.y = 11;
+ dst.x = 604; dst.y = 11 + m_iYOffset;
SDL_BlitSurface( m_poDoodads, &src, gamescreen, &dst );
}
int iTextX = 230 - g_oPlayerSelect.GetFighterNameWidth(0);
if ( iTextX < 5 ) iTextX = 5;
sge_BF_textout( gamescreen, fastFont, g_oPlayerSelect.GetFighterName(0),
- iTextX, 38 );
+ iTextX, 38 + m_iYOffset );
iTextX = g_oPlayerSelect.GetFighterNameWidth(1);
iTextX = iTextX < (635-410) ? 410 : 635-iTextX;
sge_BF_textout( gamescreen, fastFont, g_oPlayerSelect.GetFighterName(1),
- iTextX, 38 );
+ iTextX, 38 + m_iYOffset );
+*/
}
/** Draws the background, using the m_poBackground object.
*/
void Game::DrawBackground()
{
- m_poBackground->Draw( g_oBackend.m_iBgX, g_oBackend.m_iBgY );
+ m_poBackground->Draw( g_oBackend.m_iBgX, g_oBackend.m_iBgY, m_iYOffset );
+}
+
+
+void Game::AddBodyToBackground( int a_iPlayer )
+{
+ Backend::SPlayer& roPlayer = g_oBackend.m_aoPlayers[a_iPlayer];
+ BackgroundLayer oLayer;
+
+ RlePack* poPack = g_oPlayerSelect.GetPlayerInfo(a_iPlayer).m_poPack;
+
+ oLayer.m_iXOffset = roPlayer.m_iX + g_oBackend.m_iBgX;
+ oLayer.m_iYOffset = roPlayer.m_iY;
+ oLayer.m_dDistance = 1.0;
+ oLayer.m_poSurface = poPack->CreateSurface( ABS(roPlayer.m_iFrame)-1, roPlayer.m_iFrame<0 );
+
+ m_poBackground->AddExtraLayer( oLayer );
}
/** In debug mode, this method is used to draw the frame of the fighters.
\param a_sName The name of the polygon (in the perl namespace)
\param a_iColor The game color to draw the polygon with.
*/
void Game::DrawPoly( const char* a_pcName, int a_iColor )
{
AV *poList;
int n;
poList = get_av( a_pcName, FALSE );
if ( poList == NULL )
{
return;
}
n = av_len( poList ) + 1;
if ( n< 2 )
{
return;
}
for ( int i=0; i<n; i += 2 )
{
int j = (i+2) % n;
int x1 = SvIV( *av_fetch( poList, i, false) );
int y1 = SvIV( *av_fetch( poList, i+1, false) );
int x2 = SvIV( *av_fetch( poList, j, false) );
int y2 = SvIV( *av_fetch( poList, j+1, false) );
- sge_Line( gamescreen, x1, y1, x2, y2, a_iColor ) ;
+ sge_Line( gamescreen, x1, y1 + m_iYOffset, x2, y2 + m_iYOffset, a_iColor ) ;
}
}
/** Draws every doodad that is currently defined in the backend.
*/
void Game::DrawDoodads()
{
for ( int i=0; i<g_oBackend.m_iNumDoodads; ++i )
{
Backend::SDoodad& roDoodad = g_oBackend.m_aoDoodads[i];
if ( 0 == roDoodad.m_iType )
{
// Handle text doodads
const char *s = roDoodad.m_sText.c_str();
int iWidth = sge_BF_TextSize(fastFont, s).w;
int iDoodadX = roDoodad.m_iX - iWidth/2;
- if ( iDoodadX + iWidth > 640 ) iDoodadX = 640 - iWidth;
+ if ( iDoodadX + iWidth > gamescreen->w ) iDoodadX = gamescreen->w - iWidth;
if ( iDoodadX < 0 ) iDoodadX = 0;
int iDoodadY = roDoodad.m_iY;
- sge_BF_textout( gamescreen, fastFont, s, iDoodadX, iDoodadY );
+ sge_BF_textout( gamescreen, fastFont, s, iDoodadX, iDoodadY + m_iYOffset );
continue;
}
- if ( roDoodad.m_iGfxOwner < 2 )
+ if ( roDoodad.m_iGfxOwner >= 0 )
{
g_oPlayerSelect.GetPlayerInfo(roDoodad.m_iGfxOwner).m_poPack->Draw(
- roDoodad.m_iFrame, roDoodad.m_iX, roDoodad.m_iY, roDoodad.m_iDir < 1 );
+ roDoodad.m_iFrame, roDoodad.m_iX, roDoodad.m_iY + m_iYOffset, roDoodad.m_iDir < 1 );
continue;
}
+
SDL_Rect rsrc, rdst;
+ int w, h, y0;
rdst.x = roDoodad.m_iX;
- rdst.y = roDoodad.m_iY;
- rsrc.x = 64 * roDoodad.m_iFrame;
- rsrc.y = 0;
- rsrc.w = 64;
- rsrc.h = 64;
+ rdst.y = roDoodad.m_iY + m_iYOffset;
+ if ( 5 == roDoodad.m_iType )
+ {
+ y0 = 308;
+ w = h = 24;
+ }
+ else
+ {
+ y0 = 0;
+ w = h = 64;
+ }
+ rsrc.x = w * roDoodad.m_iFrame;
+ rsrc.y = y0;
+ rsrc.w = w;
+ rsrc.h = h;
SDL_BlitSurface( m_poDoodads, &rsrc, gamescreen, &rdst );
//debug( "Doodad x: %d, y: %d, t: %d, f: %d\n", dx, dy, dt, df );
}
}
/** Draws the entire game screen:
\li First, the background.
\li The players.
\li The debug wireframes (if debugging is turned on)
\li The doodads.
\li The hitpoint display.
\li The gametime display.
\li The FPS display.
\li The "Round x" text during Ph_Start
Input:
\li m_enGamePhase
\li g_oBackend.m_iGameTime
\li m_iNumberOfRounds
\li oFpsCounter
*/
void Game::Draw()
{
- #define GROUNDZERO 440
+ #define GROUNDZERO (440 + m_iYOffset)
+
+ m_iYOffset = ( gamescreen->h - 480 ) / 2;
+
+ if ( m_iYOffset )
+ {
+ SDL_Rect oRect;
+ oRect.x = 0;
+ oRect.w = gamescreen->w;
+ oRect.y = m_iYOffset;
+ oRect.h = 480;
+ SDL_SetClipRect( gamescreen, &oRect );
+ }
+ else
+ {
+ SDL_SetClipRect( gamescreen, NULL );
+ }
DrawBackground();
// DRAW THE SHADOWS
int i;
- for ( i=0; i<2; ++i )
+ for ( i=0; i<g_oState.m_iNumPlayers; ++i )
{
Backend::SPlayer& roPlayer = g_oBackend.m_aoPlayers[i];
int iFrame = roPlayer.m_iFrame;
if ( iFrame == 0 )
continue;
RlePack* poPack = g_oPlayerSelect.GetPlayerInfo(i).m_poPack;
int w = poPack->GetWidth( ABS(iFrame)-1 );
int h = poPack->GetHeight( ABS(iFrame)-1 );
h = GROUNDZERO - ( h + roPlayer.m_iY ); // Distance of feet from ground
if ( h < 0 ) h = 0;
if ( h > 500 ) h = 500;
h = 500 - h;
int h2 = 15 * h / 500;
h = ( w / 2 ) * h / 500;
- sge_FilledEllipse( gamescreen,
- g_oBackend.m_aoPlayers[i].m_iX + w/2, GROUNDZERO,
- h, h2, C_BLACK );
+ if ( gamescreen->format->BitsPerPixel <= 8 )
+ {
+ sge_FilledEllipse( gamescreen, g_oBackend.m_aoPlayers[i].m_iX + w/2, GROUNDZERO,
+ h, h2, C_BLACK );
+ }
+ else
+ {
+ sge_FilledEllipseAlpha( gamescreen, g_oBackend.m_aoPlayers[i].m_iX + w/2, GROUNDZERO,
+ h, h2, C_BLACK, 128 );
+ }
}
- for ( i=0; i<2; ++i )
+ for ( i=0; i<g_oState.m_iNumPlayers; ++i )
{
Backend::SPlayer& roPlayer = g_oBackend.m_aoPlayers[i];
int iFrame = roPlayer.m_iFrame;
if ( iFrame == 0 )
continue;
RlePack* poPack = g_oPlayerSelect.GetPlayerInfo(i).m_poPack;
- poPack->Draw( ABS(iFrame)-1, roPlayer.m_iX, roPlayer.m_iY, iFrame<0 );
+ poPack->Draw( ABS(iFrame)-1, roPlayer.m_iX, roPlayer.m_iY + m_iYOffset, iFrame<0 );
}
if ( m_bDebug )
{
DrawPoly( "p1head", C_LIGHTRED );
DrawPoly( "p1body", C_LIGHTGREEN );
DrawPoly( "p1legs", C_LIGHTBLUE );
DrawPoly( "p1hit", C_YELLOW );
DrawPoly( "p2head", C_LIGHTRED );
DrawPoly( "p2body", C_LIGHTGREEN );
DrawPoly( "p2legs", C_LIGHTBLUE );
DrawPoly( "p2hit", C_YELLOW );
}
DrawDoodads();
- DrawHitPointDisplay();
+ DrawHitPointDisplays();
if ( Ph_NORMAL == m_enGamePhase )
{
char s[100];
sprintf( s, "%d", m_iGameTime ); // m_iGameTime is maintained by DoGame
- DrawTextMSZ( s, inkFont, 320, 10, AlignHCenter, C_LIGHTCYAN, gamescreen, false );
+ DrawTextMSZ( s, inkFont, 320, 10 + m_iYOffset, AlignHCenter, C_LIGHTCYAN, gamescreen, false );
}
else if ( Ph_START == m_enGamePhase )
{
char s[100];
const char* format = Translate( "Round %d" );
sprintf( s, format, m_iNumberOfRounds+1 );
- DrawTextMSZ( s, inkFont, 320, 200, AlignHCenter, C_WHITE, gamescreen, false );
+ DrawTextMSZ( s, inkFont, 320, 200 + m_iYOffset, AlignHCenter, C_WHITE, gamescreen, false );
}
else if ( Ph_REWIND == m_enGamePhase )
{
- DrawTextMSZ( "REW", inkFont, 320, 10, AlignHCenter, C_WHITE, gamescreen );
- sge_BF_textout( gamescreen, fastFont, Translate("Press F1 to skip..."), 230, 450 );
+ DrawTextMSZ( "REW", inkFont, 320, 10 + m_iYOffset, AlignHCenter, C_WHITE, gamescreen );
+ sge_BF_textout( gamescreen, fastFont, Translate("Press F1 to skip..."), 230, 450 + m_iYOffset );
}
else if ( Ph_SLOWFORWARD == m_enGamePhase )
{
- DrawTextMSZ( "REPLAY", inkFont, 320, 10, AlignHCenter, C_WHITE, gamescreen );
- sge_BF_textout( gamescreen, fastFont, Translate("Press F1 to skip..."), 230, 450 );
+ DrawTextMSZ( "REPLAY", inkFont, 320, 10 + m_iYOffset, AlignHCenter, C_WHITE, gamescreen );
+ sge_BF_textout( gamescreen, fastFont, Translate("Press F1 to skip..."), 230, 450 + m_iYOffset );
}
else if ( Ph_REPLAY == m_enGamePhase )
{
- DrawTextMSZ( "DEMO", inkFont, 320, 10, AlignHCenter, C_WHITE, gamescreen );
+ DrawTextMSZ( "DEMO", inkFont, 320, 10 + m_iYOffset, AlignHCenter, C_WHITE, gamescreen );
}
if ( oFpsCounter.m_iFps > 0 )
{
- sge_BF_textoutf( gamescreen, fastFont, 2, 2, "%d fps", oFpsCounter.m_iFps );
+ sge_BF_textoutf( gamescreen, fastFont, 2, 455 + m_iYOffset, "%d fps", oFpsCounter.m_iFps );
}
SDL_Flip( gamescreen );
}
/***************************************************************************
GAME PROTECTED METHODS
***************************************************************************/
+bool Game::IsTeamMode()
+{
+ return SState::Team_ONE_VS_ONE != g_oState.m_enTeamMode;
+}
+
+
bool Game::IsNetworkGame()
{
return SState::IN_NETWORK == g_oState.m_enGameMode;
}
/** Returns true if we control our own data, or false if the network supplies
us with game data. */
bool Game::IsMaster()
{
return !IsNetworkGame() || g_poNetwork->IsMaster();
}
/**
This method reads and updates the game's variables. In replay mode,
this is done by parsing the replay string. Otherwise the perl
backend is advanced the given number of steps.
Whichever the case, the variables will be available in g_oBackend.
Only the backend-driven variables are modified, the GamePhase and
GameTime remain unchanged; these are up for DoOneRound and friends
to modify.
*/
void Game::Advance( int a_iNumFrames )
{
if ( m_bIsReplay )
{
// Replay mode...
m_iFrame += a_iNumFrames;
if ( m_iFrame >= ((int)m_aReplayOffsets.size())-1 ) m_iFrame = m_aReplayOffsets.size() - 2;
if ( m_iFrame <= 0 ) m_iFrame = 0;
std::string sFrameDesc = m_sReplayString.substr(
m_aReplayOffsets[m_iFrame],
m_aReplayOffsets[m_iFrame+1] - m_aReplayOffsets[m_iFrame] );
g_oBackend.ReadFromString( sFrameDesc );
return;
}
static std::string sFrameDesc;
int i;
if ( IsNetworkGame() )
{
g_poNetwork->SendGameTick( g_oBackend.m_iGameTick );
g_poNetwork->Update();
int i = 0;
while ( g_poNetwork->GetGameTick() + m_iEnqueueDelay < g_oBackend.m_iGameTick + a_iNumFrames )
{
++i;
if ( i > 300 ) {
// Waited for too long..
g_poNetwork->Stop();
}
// The remote side is lagging behind.. Wait for it.
SDL_Delay( 10 );
g_poNetwork->Update();
if ( m_enInitialGameMode != g_oState.m_enGameMode ) {
return;
}
}
int iTime;
int iKey;
bool bPressed;
while ( g_poNetwork->GetKeystroke( iTime, iKey, bPressed ) )
{
debug( "Got GetKeystroke: %d, %d, %d at %d\n", iTime, iKey, bPressed, g_oBackend.m_iGameTick );
// g_oBackend.PerlEvalF( bPressed ? "KeyDown(%d,%d);" : "KeyUp(%d,%d);", 1, iKey );
m_oKeyQueue.EnqueueKey( iTime, IsMaster() ? 1 : 0, iKey, bPressed );
if ( iTime <= g_oBackend.m_iGameTick )
{
debug( "KEY ARRIVED TOO LATE!!!\n" );
}
}
}
while ( a_iNumFrames > 0 )
{
-- a_iNumFrames;
g_oBackend.AdvancePerl();
g_oBackend.ReadFromPerl();
g_oBackend.PlaySounds();
m_oKeyQueue.DequeueKeys( g_oBackend.m_iGameTick );
g_oBackend.WriteToString( sFrameDesc );
m_sReplayString += sFrameDesc;
m_sReplayString += '\n';
m_aReplayOffsets.push_back( m_sReplayString.size() );
}
}
/** A helper method of ProcessEvents; it manages keypresses and releases of
players. It is only called when keypresses are actually relevant for the
backend (not during instant replay, etc).
*/
void Game::HandleKey( int a_iPlayer, int a_iKey, bool a_bDown )
{
int iCurrentTick = g_oBackend.m_iGameTick + m_iEnqueueDelay;
if ( IsNetworkGame() )
{
a_iPlayer = IsMaster() ? 0 : 1;
g_poNetwork->SendKeystroke( iCurrentTick, a_iKey, a_bDown );
}
m_oKeyQueue.EnqueueKey( iCurrentTick, a_iPlayer, a_iKey, a_bDown );
}
/** ProcessEvents reads events from the SDL event system.
Relevant key events are fed to the backend.
Esc brings up the menu.
Returns 1 on quit event (e.g. if the current game or replay should be aborted), 0 otherwise.
*/
int Game::ProcessEvents()
{
SMortalEvent oEvent;
while (MortalPollEvent(oEvent))
{
switch (oEvent.m_enType)
{
case Me_QUIT:
g_oState.m_bQuitFlag = true;
return 1;
case Me_MENU:
if ( Ph_REWIND == m_enGamePhase
|| Ph_SLOWFORWARD == m_enGamePhase )
{
// Menu counts as 'skip' during instant replay
return 1;
}
if ( !IsNetworkGame() )
{
SState::TGameMode enMode = g_oState.m_enGameMode;
::DoMenu();
return g_oState.m_enGameMode == enMode ? 0 : 1;
}
break;
case Me_SKIP:
return 1;
case Me_PLAYERKEYDOWN:
case Me_PLAYERKEYUP:
{
if ( Ph_NORMAL != m_enGamePhase &&
Ph_REPLAY != m_enGamePhase )
break;
HandleKey( oEvent.m_iPlayer, oEvent.m_iKey, Me_PLAYERKEYDOWN == oEvent.m_enType );
break;
}
case Me_NOTHING:
break;
} // End of switch
} // End of while polling events;
return 0;
}
+void Game::HandleKO()
+{
+}
+
+
void Game::HurryUp()
{
Audio->PlaySample( "aroooga.voc" );
DrawGradientText( "HURRY UP!", titleFont, 200, gamescreen );
SDL_Delay( 1000 );
Audio->PlaySample( "machine_start.voc" );
}
void Game::TimeUp()
{
DrawGradientText( "TIME IS UP!", titleFont, 200, gamescreen );
SDL_Delay( 1000 );
}
/** This methods starts and runs the "instant replay" mode that is done
at the end of a game round. This means doing phases Ph_REWIND and Ph_SLOWFORWARD.
Rewind will go back in time 200 ticks before the parameter a_iKoAt.
*/
void Game::InstantReplay( int a_iKoAt )
{
int iCurrentFrame = m_aReplayOffsets.size() - 200;
int iThisTick, iLastTick, iGameSpeed;
m_enGamePhase = Ph_REWIND;
iGameSpeed = 8;
iThisTick = SDL_GetTicks() / iGameSpeed;
iLastTick = iThisTick - 1;
while ( iCurrentFrame < (int)m_aReplayOffsets.size() - 150 )
{
// 1. Wait for the next tick (on extremely fast machines..)
while (iThisTick == iLastTick)
{
iThisTick = SDL_GetTicks() / iGameSpeed;
if ( iThisTick==iLastTick ) SDL_Delay(1);
}
// 2. Advance as many ticks as necessary..
int iNumTicks = iThisTick - iLastTick;
if ( iNumTicks > 10 ) iNumTicks = 10;
if ( iNumTicks < 0 ) iNumTicks = 0;
iCurrentFrame += ( Ph_REWIND == m_enGamePhase ) ? -iNumTicks : +iNumTicks ;
if ( Ph_REWIND == m_enGamePhase
&& ( iCurrentFrame < a_iKoAt - 200 || iCurrentFrame <= 0 )
)
{
m_enGamePhase = Ph_SLOWFORWARD;
iGameSpeed = 16;
SDL_Delay(500);
}
iLastTick = iThisTick;
if ( iCurrentFrame < 0 ) iCurrentFrame = 0;
m_iFrame = iCurrentFrame;
if ( m_iFrame >= ((int)m_aReplayOffsets.size())-1 ) m_iFrame = m_aReplayOffsets.size() - 2;
if ( m_iFrame <= 0 ) m_iFrame = 0;
std::string sFrameDesc = m_sReplayString.substr(
m_aReplayOffsets[m_iFrame],
m_aReplayOffsets[m_iFrame+1] - m_aReplayOffsets[m_iFrame] );
//debug( "PB: Frame %d ofs %d-%d; data: '%s'\n", m_iFrame,
// m_aReplayOffsets[m_iFrame], m_aReplayOffsets[m_iFrame+1], sFrameDesc.c_str() );
g_oBackend.ReadFromString( sFrameDesc );
if ( ProcessEvents() )
{
break;
}
oFpsCounter.Tick();
// 3. Draw the next game screen..
Draw();
if ( g_oState.m_bQuitFlag
|| SState::IN_DEMO == g_oState.m_enGameMode )
{
break;
}
}
}
/** This methods executes one round of gameplay.
The game progresses through phases Ph_START, Ph_NORMAL, and
Ph_KO. If a KO happened, it will invoke InstantReplay. At the end of
the round m_aiRoundsWonByPlayer[x] will be incremented depending on the
outcome. m_iNumberOfRounds will also increase by 1.
*/
void Game::DoOneRound()
{
m_enGamePhase = Ph_START;
+ m_poBackground->DeleteExtraLayers();
+
+ int iTeamSize = (SState::Team_ONE_VS_ONE==g_oState.m_enTeamMode) ?
+ 1 : g_oPlayerSelect.GetPlayerInfo(0).m_aenTeam.size();
+ int aiTeamNumber[MAXPLAYERS];
+
+ for ( int i=0; i<g_oState.m_iNumPlayers; ++i )
+ {
+ aiTeamNumber[i] = 0;
+ }
+
+ if ( IsTeamMode() )
+ {
+ for ( int i=0; i<g_oState.m_iNumPlayers; ++i )
+ {
+ g_oPlayerSelect.SetPlayer( i, g_oPlayerSelect.GetPlayerInfo(i).m_aenTeam[aiTeamNumber[i]] );
+ }
+ }
- g_oBackend.PerlEvalF( "GameStart(%d,%d);",
+ g_oBackend.PerlEvalF( "GameStart(%d,%d,%d,%d,%d);",
IsMaster() ? g_oState.m_iHitPoints : g_poNetwork->GetGameParams().iHitPoints,
+ g_oState.m_iNumPlayers,
+ iTeamSize,
+ m_bWide,
m_bDebug );
g_oBackend.ReadFromPerl();
if ( IsNetworkGame() )
{
g_poNetwork->SynchStartRound();
g_poNetwork->SendGameTick( g_oBackend.m_iGameTick-1 );
}
int iKoFrame = -1;
double dGameTime = 2 * 1000; // Only for the "greeting phase", the real gametime will be set after.
int iThisTick, iLastTick, iGameSpeed;
bool bHurryUp = false;
bool bReplayAfter = true;
iGameSpeed = IsMaster() ? g_oState.m_iGameSpeed : g_poNetwork->GetGameParams().iGameSpeed;
iThisTick = SDL_GetTicks() / iGameSpeed;
iLastTick = iThisTick - 1;
m_oKeyQueue.Reset();
oFpsCounter.Reset();
// 1. DO THE NORMAL GAME ROUND (START, NORMAL, KO, TIMEUP)
while ( dGameTime >= 0 )
{
if ( m_enInitialGameMode != g_oState.m_enGameMode )
{
return;
}
// 1. Wait for the next tick (on extremely fast machines..)
while (iThisTick == iLastTick)
{
iThisTick = SDL_GetTicks() / iGameSpeed;
if ( iThisTick==iLastTick ) SDL_Delay(1);
}
// 2. Advance as many ticks as necessary..
int iNumTicks = iThisTick - iLastTick;
if ( iNumTicks > MAXFRAMESKIP ) iNumTicks = MAXFRAMESKIP;
Advance( iNumTicks );
dGameTime -= iNumTicks * iGameSpeed;
// 3. Check for state transitions and game time.
// START -> NORMAL
// NORMAL -> KO
// NORMAL -> TIMEUP
// bHurryUp flag can be set during NORMAL phase
if ( Ph_START == m_enGamePhase ) // Check for the end of the START phase
{
if ( dGameTime <= 0 )
{
m_enGamePhase = Ph_NORMAL;
dGameTime = (IsMaster() ? g_oState.m_iGameTime : g_poNetwork->GetGameParams().iGameTime) * 1000;
}
}
else if ( Ph_NORMAL == m_enGamePhase ) // Check for the end of the NORMAL phase
{
if ( dGameTime < 10 * 1000
&& !bHurryUp )
{
bHurryUp = true;
g_poNetwork->SendHurryup( 1 );
HurryUp();
iGameSpeed = iGameSpeed * 3 / 4;
}
if ( g_oBackend.m_bKO )
{
m_enGamePhase = Ph_KO;
dGameTime = 10 * 1000;
iKoFrame = m_aReplayOffsets.size();
}
else if ( dGameTime <= 0 )
{
m_enGamePhase = Ph_TIMEUP;
g_poNetwork->SendHurryup( 2 );
TimeUp();
break;
}
}
m_iGameTime = (int) ((dGameTime + 500.0) / 1000.0);
iLastTick = iThisTick;
// ProcessEvents will read keyboard/gamepad input
// It will also transmit them to the remote side in a network game.
if ( ProcessEvents() || g_oState.m_bQuitFlag )
{
bReplayAfter = false;
break;
}
oFpsCounter.Tick();
// 3. Draw the next game screen..
Draw();
// 4. Check 'end of round' condition.
- if ( !IsMaster() )
+ for ( int i=0; i<g_oState.m_iNumPlayers; ++i )
{
- if ( g_poNetwork->IsRoundOver() )
+ if ( g_oBackend.m_aoPlayers[i].m_iRealHitPoints <= -10000 )
{
- break;
+ // We have a dead player here.
+ if ( aiTeamNumber[i] < iTeamSize-1 )
+ {
+ ++aiTeamNumber[i];
+ AddBodyToBackground( i );
+ FighterEnum enFighter = g_oPlayerSelect.GetPlayerInfo(i).m_aenTeam[ aiTeamNumber[i] ];
+
+ g_oPlayerSelect.SetPlayer( i, enFighter );
+ g_oBackend.PerlEvalF( "NextTeamMember(%d,%d);", i, enFighter );
+ }
}
}
- else if ( g_oBackend.m_iGameOver )
+
+ if ( g_oBackend.m_iGameOver )
{
break;
}
+
+ if ( !IsMaster() )
+ {
+ if ( g_poNetwork->IsRoundOver() )
+ {
+ break;
+ }
+ }
}
- int p1h = g_oBackend.m_aoPlayers[0].m_iHitPoints;
- int p2h = g_oBackend.m_aoPlayers[1].m_iHitPoints;
+ int p1h = g_oBackend.m_aoPlayers[0].m_iRealHitPoints;
+ int p2h = g_oBackend.m_aoPlayers[1].m_iRealHitPoints;
// 3. DO THE REPLAY (IF THERE WAS A KO)
if ( iKoFrame>0 && bReplayAfter && !IsNetworkGame() )
{
InstantReplay( iKoFrame );
}
// 4. END OF ROUND
debug( "Game over; p1h = %d; p2h = %d\n", p1h, p2h );
if ( IsMaster() )
{
int iWhoWon = -1;
if ( p1h > p2h ) { ++m_aiRoundsWonByPlayer[0]; iWhoWon = 0; }
if ( p2h > p1h ) { ++m_aiRoundsWonByPlayer[1]; iWhoWon = 1; }
if ( IsNetworkGame() )
{
g_poNetwork->SendGameTick( g_oBackend.m_iGameTick + m_iEnqueueDelay * 100 );
g_poNetwork->SendRoundOver( iWhoWon, m_iNumberOfRounds > 0 );
}
}
else
{
int iWhoWon = g_poNetwork->GetWhoWon();
if ( iWhoWon>=0 )
{
++m_aiRoundsWonByPlayer[iWhoWon];
}
}
++m_iNumberOfRounds;
}
void Game::DoReplay( const char* a_pcReplayFile )
{
std::ifstream oInput( a_pcReplayFile );
int iPlayer1, iPlayer2;
oInput >> iPlayer1 >> iPlayer2;
std::string sLine;
std::getline( oInput, sLine );
g_oPlayerSelect.SetPlayer( 0, (FighterEnum) iPlayer1 );
g_oPlayerSelect.SetPlayer( 1, (FighterEnum) iPlayer2 );
int iThisTick, iLastTick, iGameSpeed;
m_enGamePhase = Ph_REPLAY;
iGameSpeed = 12;
iThisTick = SDL_GetTicks() / iGameSpeed;
iLastTick = iThisTick - 1;
while ( !oInput.eof() )
{
// 1. Wait for the next tick (on extremely fast machines..)
while (iThisTick == iLastTick)
{
iThisTick = SDL_GetTicks() / iGameSpeed;
if ( iThisTick==iLastTick ) SDL_Delay(1);
}
// 2. Advance as many ticks as necessary..
int iNumTicks = iThisTick - iLastTick;
if ( iNumTicks > 5 ) iNumTicks = 5;
for ( int i=0; i< iNumTicks; ++i )
{
std::getline( oInput, sLine );
}
if ( 0 == sLine.size() )
{
break;
}
iLastTick = iThisTick;
g_oBackend.ReadFromString( sLine );
if ( ProcessEvents() )
{
break;
}
oFpsCounter.Tick();
// 3. Draw the next game screen..
Draw();
if ( g_oState.m_bQuitFlag )
{
break;
}
}
}
int Game::GetBackgroundNumber() //static
{
return mg_iBackgroundNumber;
}
+class CVideoModeChange
+{
+public:
+ CVideoModeChange( bool a_bWide )
+ {
+ m_bWide = a_bWide;
+ if ( m_bWide ) SetVideoMode( true, g_oState.m_bFullscreen );
+ }
+ ~CVideoModeChange()
+ {
+ if ( m_bWide ) SetVideoMode( false, g_oState.m_bFullscreen );
+ }
+ bool m_bWide;
+};
+
+
/** Public static function.
Other parts of OpenMortal need not include "Game.h" so long as they have
the definition of this method (defined in "common.h"). The method runs
a cycle of the game (either a normal game, or replay).
In replay mode, DoReplay() is called, and the replay file is required.
In normal mode, Run() is called. The replay file is recorded, if it is not NULL.
*/
int DoGame( char* a_pcReplayFile, bool a_bIsReplay, bool a_bDebug )
{
- Game oGame( a_bIsReplay, a_bDebug );
+ bool bWide = g_oState.m_iNumPlayers > 2;
+ CVideoModeChange oVideoMode( bWide );
+ Game oGame( a_bIsReplay, bWide, a_bDebug );
if ( a_bIsReplay )
{
if ( NULL == a_pcReplayFile )
{
return 0;
}
oGame.DoReplay( a_pcReplayFile );
return 0;
}
else
{
int iRetval = oGame.Run();
if ( NULL != a_pcReplayFile )
{
std::ofstream oOutput( a_pcReplayFile );
oOutput <<
g_oPlayerSelect.GetPlayerInfo(0).m_enFighter << ' ' <<
g_oPlayerSelect.GetPlayerInfo(1).m_enFighter << '\n' << oGame.GetReplay();
}
return iRetval;
}
}
int GetBackgroundNumber()
{
return Game::GetBackgroundNumber();
}
diff --git a/src/Game.h b/src/Game.h
index e4a631f..f3ef2b7 100644
--- a/src/Game.h
+++ b/src/Game.h
@@ -1,135 +1,145 @@
/***************************************************************************
Game.h - description
-------------------
begin : Mon Aug 27 2003
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#ifndef GAME_H
#define GAME_H
#include <string>
#include <vector>
#include <list>
struct SDL_Surface;
class Background;
/**
CKeyQueue is used to introduce a certain amount of negative or positive
lag to keystrokes.
The keys are sent to the backend and the remote site set to a future time.
The time is measured in the backend's game ticks. So if the current key
is pressed at game time 80, and enqueued to game time 90, there will be an
artificial lag of 10 game ticks.
The actual length of game ticks depends on the game speed (set in the menu),
for a normal game its 1000/80 = 12.5 ms. This artificial lag is useful for
network games.
*/
class CKeyQueue
{
public:
CKeyQueue();
~CKeyQueue();
void Reset();
void EnqueueKey( int a_iAtTime, int a_iPlayer, int a_iKey, bool a_bDown );
void DequeueKeys( int a_iToTime );
protected:
struct SEnqueuedKey
{
int iTime;
int iPlayer;
int iKey;
bool bDown;
};
typedef std::list<SEnqueuedKey> TEnqueuedKeyList;
TEnqueuedKeyList m_oKeys;
};
/**
The Game class is for running the frontend of a game.
This involves reading the game state data from a source (be it a replay
file or the backend), handling the keystrokes and network, etc.
*/
class Game
{
public:
- Game( bool a_bIsReplay, bool a_bDebug );
+ Game( bool a_bIsReplay, bool m_bWide, bool a_bDebug );
~Game();
int Run();
std::string& GetReplay();
void DoReplay( const char* a_pcReplayFile );
static int GetBackgroundNumber();
protected:
void Draw();
- void DrawHitPointDisplay();
+ void DrawHitPointDisplay( int a_iPlayer);
+ void DrawHitPointDisplays();
void DrawBackground();
void DrawDoodads();
void DrawPoly( const char* a_pcName, int a_iColor );
+ void AddBodyToBackground( int a_iPlayer );
void DoOneRound();
void Advance( int a_iNumFrames );
int ProcessEvents();
void HandleKey( int a_iPlayer, int a_iKey, bool a_bDown );
+ void HandleKO();
void HurryUp();
void TimeUp();
void InstantReplay( int a_iKoAt );
+ bool IsTeamMode();
bool IsNetworkGame();
bool IsMaster();
void ReadKeysFromNetwork();
protected:
static int mg_iBackgroundNumber;
bool m_bIsReplay;
+ bool m_bWide; ///< 800 or 640 pixel width.
+ int m_iYOffset; ///< For wide mode.
bool m_bDebug;
Background* m_poBackground;
SDL_Surface* m_poDoodads;
+
+ int m_aiHitPointDisplayX[MAXPLAYERS];
+ int m_aiHitPointDisplayY[MAXPLAYERS];
+ bool m_abHitPointDisplayLeft[MAXPLAYERS];
- int m_aiRoundsWonByPlayer[2];
+ int m_aiRoundsWonByPlayer[MAXPLAYERS];
int m_iNumberOfRounds;
int m_iFrame;
int m_iGameTime;
CKeyQueue m_oKeyQueue;
int m_iEnqueueDelay;
std::string m_sReplayString;
std::vector<int> m_aReplayOffsets;
enum TGamePhaseEnum // This enum assumes its values during DoOneRound
{
Ph_START, // "Round X" displayed, fighters getting ready
Ph_NORMAL, // During the fight
Ph_TIMEUP, // Time is up, no KO, no replay.
Ph_KO, // There is a KO, forward until the guy is down
Ph_REWIND, // There was a KO, rewinding until before the KO
Ph_SLOWFORWARD, // Playing back the KO
Ph_REPLAY, // Replay mode
} m_enGamePhase;
-
+
SState::TGameMode m_enInitialGameMode; // must make sure it's still the same.
};
#endif
diff --git a/src/GameOver.cpp b/src/GameOver.cpp
index 60eb3a6..6cb0962 100644
--- a/src/GameOver.cpp
+++ b/src/GameOver.cpp
@@ -1,218 +1,218 @@
/***************************************************************************
GameOver.cpp - description
-------------------
begin : Wed Aug 21 20:25:30 CEST 2003
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#include "PlayerSelect.h"
#include "SDL.h"
#include "gfx.h"
#include "Backend.h"
#include "common.h"
#include "State.h"
#include "RlePack.h"
#include "Audio.h"
#include "Event.h"
#include <stdio.h>
void DrawPlayer( int i )
{
int iFrame = g_oBackend.m_aoPlayers[i].m_iFrame;
if ( 0 != iFrame )
{
g_oPlayerSelect.GetPlayerInfo(i).m_poPack->Draw(
ABS(iFrame)-1,
g_oBackend.m_aoPlayers[i].m_iX,
g_oBackend.m_aoPlayers[i].m_iY - 15,
iFrame<0 );
}
}
void GameOver( int a_iPlayerWon )
{
- SDL_Surface* poBackground = LoadBackground( "GameOver.png", 112 );
+ SDL_Surface* poBackground = LoadBackground( "GameOver.jpg", 112 );
DrawGradientText( "Final Judgement", titleFont, 20, poBackground );
DrawTextMSZ( "Continue?", inkFont, 320, 100, AlignHCenter, C_LIGHTCYAN, poBackground );
- SDL_Surface* poFoot = LoadBackground( "Foot.png", 112 );
+ SDL_Surface* poFoot = LoadBackground( "Foot.png", 112, 0, true );
SDL_BlitSurface( poBackground, NULL, gamescreen, NULL );
g_oBackend.PerlEvalF( "JudgementStart(%d);", a_iPlayerWon );
int thisTick, lastTick, firstTick, iGameSpeed;
iGameSpeed = 14 ;
firstTick = thisTick = SDL_GetTicks() / iGameSpeed;
lastTick = thisTick - 1;
char acString[100];
int iTimeLeft = 8000 / iGameSpeed;
int FOOTHEIGHT = poFoot->h;
int GROUNDLEVEL = 440;
int iFootY = -FOOTHEIGHT;
bool bTimeUp = false;
bool bKeyPressed = false;
int iCounter = -1;
while (1)
{
// 1. Wait for the next tick (on extremely fast machines..)
while (1)
{
thisTick = SDL_GetTicks() / iGameSpeed;
if ( thisTick==lastTick )
{
SDL_Delay(1);
}
else
{
break;
}
}
// 2. Advance as many ticks as necessary..
if ( thisTick - lastTick > 5 )
{
iTimeLeft -= 5;
}
else
{
iTimeLeft = iTimeLeft - thisTick + lastTick;
}
if ( iTimeLeft < 0 && !bTimeUp )
{
bTimeUp = true;
Audio->FadeMusic( 1500 );
}
if ( bTimeUp )
{
iFootY += 12 * (thisTick - lastTick );
if ( iFootY > GROUNDLEVEL - FOOTHEIGHT )
{
break;
}
}
int iNumFrames = thisTick - lastTick;
for ( int i=0; i<iNumFrames; ++i )
{
g_oBackend.AdvancePerl();
};
lastTick = thisTick;
SMortalEvent oEvent;
while (MortalPollEvent(oEvent))
{
switch (oEvent.m_enType)
{
case Me_QUIT:
g_oState.m_bQuitFlag = true;
break;
case Me_MENU:
DoMenu();
break;
case Me_PLAYERKEYDOWN:
if ( 1-a_iPlayerWon == oEvent.m_iPlayer
&& oEvent.m_iKey>= 4 )
{
bKeyPressed = true;
}
break;
case Me_NOTHING:
case Me_PLAYERKEYUP:
case Me_SKIP:
break;
} // switch statement
} // Polling events
g_oBackend.ReadFromPerl();
SDL_BlitSurface( poBackground, NULL, gamescreen, NULL );
DrawPlayer( a_iPlayerWon );
if ( bTimeUp )
{
SDL_Rect oRect;
oRect.x = 0;
oRect.y = iFootY + FOOTHEIGHT - 10;
oRect.w = gamescreen->w;
oRect.h = gamescreen->h - oRect.y;
SDL_SetClipRect(gamescreen, &oRect);
}
DrawPlayer( 1-a_iPlayerWon );
SDL_SetClipRect( gamescreen, NULL );
if ( !bTimeUp )
{
int iNewCounter = iTimeLeft * iGameSpeed / 1000;
sprintf( acString, "%d", iNewCounter );
DrawTextMSZ( acString, inkFont, 320, 130, AlignHCenter, C_LIGHTCYAN, gamescreen );
if ( iNewCounter != iCounter )
{
Audio->PlaySample( "alarm.voc" );
iCounter = iNewCounter;
}
}
else
{
SDL_Rect oRect;
oRect.x = 40;
oRect.y = iFootY;
SDL_BlitSurface( poFoot, NULL, gamescreen, &oRect );
}
SDL_Flip( gamescreen );
if ( g_oState.m_bQuitFlag ||
SState::IN_DEMO == g_oState.m_enGameMode ||
bKeyPressed )
{
break;
}
}
if ( g_oState.m_bQuitFlag ||
SState::IN_DEMO == g_oState.m_enGameMode )
{
SDL_FreeSurface( poBackground );
return;
}
SDL_FillRect( gamescreen, NULL, C_BLACK );
if ( bTimeUp )
{
SDL_UnlockSurface( gamescreen );
DrawGradientText( "SPLAT!", titleFont, 220, gamescreen );
Audio->PlaySample( "splat2.voc" );
SDL_Flip( gamescreen );
SDL_Delay( 1500 );
g_oState.m_enGameMode = SState::IN_DEMO;
}
else
{
SDL_Flip( gamescreen );
}
SDL_FreeSurface( poBackground );
return;
}
diff --git a/src/Joystick.cpp b/src/Joystick.cpp
index 5df6f3f..c9faf3c 100644
--- a/src/Joystick.cpp
+++ b/src/Joystick.cpp
@@ -1,256 +1,256 @@
/***************************************************************************
Joystick.cpp - description
-------------------
begin : Sat Feb 14 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
-#include "SDL/SDL.h"
+#include "SDL.h"
#include "Event.h"
#include "common.h"
#include "Joystick.h"
#include <string.h>
CJoystick g_oJoystick;
CJoystick::CJoystick()
{
m_iNumJoysticks = 0;
}
CJoystick::~CJoystick()
{
}
int InitJoystick()
{
return g_oJoystick.Init();
}
int CJoystick::Init()
{
m_iNumJoysticks = 0;
int iResult = SDL_InitSubSystem(SDL_INIT_JOYSTICK);
if ( iResult < 0 )
{
debug( "CJoystick::Init() failed: %s\n", SDL_GetError() );
return 0;
}
int iNumJoysticks = SDL_NumJoysticks();
debug( "CJoystick::Init(): Number of joysticks is %d\n", iNumJoysticks );
int i;
for ( i=0; i<iNumJoysticks; ++i )
{
const char* poName = SDL_JoystickName(i);
debug( "CJoystick::Init(): Joystick #%d is '%s'\n", i, poName );
SDL_Joystick* poJoystick = SDL_JoystickOpen(i);
if ( NULL == poJoystick )
{
continue;
}
m_apoJoysticks[m_iNumJoysticks] = poJoystick;
m_apcJoystickNames[m_iNumJoysticks] = poName;
m_abWorkaround[m_iNumJoysticks] = false;
m_aiHorizontalAxis[m_iNumJoysticks] = 0;
m_aiVerticalAxis[m_iNumJoysticks] = 0;
debug( "CJoystick::Init(): Joystick #%d has %d axes, %d buttons, %d balls, %d hats\n",
i,
SDL_JoystickNumAxes( poJoystick ),
SDL_JoystickNumButtons( poJoystick ),
SDL_JoystickNumBalls( poJoystick ),
SDL_JoystickNumHats( poJoystick ) );
if ( strstr( poName, "SIGHT FIGHTER ACTION USB" ) )
{
m_abWorkaround[m_iNumJoysticks] = true;
debug( "CJoystick::Init(): Infuriatingly stupid joystick detected.\n" );
}
++m_iNumJoysticks;
}
if ( m_iNumJoysticks )
{
SDL_JoystickEventState( SDL_ENABLE );
}
return m_iNumJoysticks;
}
const char* CJoystick::GetJoystickName( int a_iJoystickNumber )
{
if ( a_iJoystickNumber >= m_iNumJoysticks
|| a_iJoystickNumber < 0 )
{
return NULL;
}
return m_apcJoystickNames[a_iJoystickNumber];
}
bool CJoystick::TranslateEvent( const SDL_Event* a_poInEvent, SMortalEvent* a_poOutEvent )
{
a_poOutEvent->m_enType = Me_NOTHING;
switch ( a_poInEvent->type )
{
case SDL_JOYAXISMOTION:
{
int iJoyNumber = a_poInEvent->jaxis.which;
int iAxisNumber = a_poInEvent->jaxis.axis;
int iValue = a_poInEvent->jaxis.value;
if ( iAxisNumber > 1 )
{
// Only translate axis 0 and 1.
return false;
}
if ( m_abWorkaround[iJoyNumber] )
{
iValue = AxisWorkaround( a_poInEvent->jaxis );
debug( "Translated %d to %d\n", a_poInEvent->jaxis.value, iValue );
}
// Try the new value into motion.
int& riOldValue = iAxisNumber ? m_aiVerticalAxis[iJoyNumber] : m_aiHorizontalAxis[iJoyNumber];
if ( -1 == riOldValue )
{
if ( iValue < -3200 )
{
return false;
}
riOldValue = 0;
a_poOutEvent->m_enType = Me_PLAYERKEYUP;
a_poOutEvent->m_iPlayer = iJoyNumber;
a_poOutEvent->m_iKey = iAxisNumber ? Mk_UP : Mk_LEFT;
return true;
}
if ( 0 == riOldValue )
{
if ( iValue < -6400 )
{
riOldValue = -1;
a_poOutEvent->m_enType = Me_PLAYERKEYDOWN;
a_poOutEvent->m_iPlayer = iJoyNumber;
a_poOutEvent->m_iKey = iAxisNumber ? Mk_UP : Mk_LEFT;
return true;
}
if ( iValue > 6400 )
{
riOldValue = 1;
a_poOutEvent->m_enType = Me_PLAYERKEYDOWN;
a_poOutEvent->m_iPlayer = iJoyNumber;
a_poOutEvent->m_iKey = iAxisNumber ? Mk_DOWN : Mk_RIGHT;
return true;
}
return false;
}
if ( 1 == riOldValue )
{
if ( iValue > 3200 )
{
return false;
}
riOldValue = 0;
a_poOutEvent->m_enType = Me_PLAYERKEYUP;
a_poOutEvent->m_iPlayer = iJoyNumber;
a_poOutEvent->m_iKey = iAxisNumber ? Mk_DOWN : Mk_RIGHT;
return true;
}
riOldValue = 0;
return false;
}
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
{
int iJoyNumber = a_poInEvent->jbutton.which;
int iButton = a_poInEvent->jbutton.button;
int iPressed = a_poInEvent->jbutton.state == SDL_PRESSED;
debug( "Button down: joy %d, button %d, state %d\n", iJoyNumber, iButton, iPressed );
if ( iButton > 4 )
{
if ( iButton == SDL_JoystickNumButtons(m_apoJoysticks[iJoyNumber]) - 1
&& iPressed )
{
a_poOutEvent->m_enType = Me_MENU;
return true;
}
return false;
}
if ( iJoyNumber > 1 )
{
return false;
}
a_poOutEvent->m_enType = iPressed ? Me_PLAYERKEYDOWN : Me_PLAYERKEYUP;
a_poOutEvent->m_iPlayer = iJoyNumber;
a_poOutEvent->m_iKey = iButton + 4;
return true;
}
case SDL_JOYBALLMOTION:
if( a_poInEvent->jball.ball == 0 )
{
// Nothing for now.
}
break;
case SDL_JOYHATMOTION:
if ( a_poInEvent->jhat.hat & SDL_HAT_UP )
{
// Nothing for now.
}
if ( a_poInEvent->jhat.hat & SDL_HAT_LEFT )
{
// Nothing for now.
}
if ( a_poInEvent->jhat.hat & SDL_HAT_RIGHTDOWN )
{
// Nothing for now.
}
break;
} // end of switch statement
return false;
}
int CJoystick::AxisWorkaround( const SDL_JoyAxisEvent& a_roEvent )
{
if ( 0 == a_roEvent.value ) return -32768;
if ( 127 == a_roEvent.value ) return 0;
if ( -1 == a_roEvent.value ) return 32768;
return 0;
}
diff --git a/src/Joystick.h b/src/Joystick.h
index 9803e62..8d08304 100644
--- a/src/Joystick.h
+++ b/src/Joystick.h
@@ -1,47 +1,47 @@
/***************************************************************************
Joystick.h - description
-------------------
begin : Sat Feb 14 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
#ifndef JOYSTICK_H
#define JOYSTICK_H
-#include "SDL/SDL.h"
+#include "SDL.h"
struct SMortalEvent;
class CJoystick
{
public:
CJoystick();
~CJoystick();
int Init();
bool TranslateEvent( const SDL_Event* a_poInEvent, SMortalEvent* a_poOutEvent );
const char* GetJoystickName( int a_iJoystickNumber );
protected:
int AxisWorkaround( const SDL_JoyAxisEvent& a_roEvent );
enum {
mg_iMaxJoysticks = 32,
};
int m_iNumJoysticks;
SDL_Joystick* m_apoJoysticks[mg_iMaxJoysticks];
const char* m_apcJoystickNames[mg_iMaxJoysticks];
bool m_abWorkaround[mg_iMaxJoysticks];
int m_aiHorizontalAxis[mg_iMaxJoysticks];
int m_aiVerticalAxis[mg_iMaxJoysticks];
};
extern CJoystick g_oJoystick;
#endif // JOYSTICK_H
diff --git a/src/Makefile.am b/src/Makefile.am
index c05aa30..4821b8a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,29 +1,29 @@
## Process this file with automake to produce Makefile.in
bin_PROGRAMS = openmortal
-openmortal_SOURCES = Audio.cpp Backend.cpp common.cpp \
- Demo.cpp FighterStats.cpp FlyingChars.cpp Game.cpp gfx.cpp main.cpp \
- menu.cpp PlayerSelect.cpp RlePack.cpp sge_bm_text.cpp \
- sge_primitives.cpp sge_surface.cpp sge_tt_text.cpp State.cpp \
- GameOver.cpp Background.cpp MortalNetworkImpl.cpp Chooser.cpp \
- TextArea.cpp OnlineChat.cpp Joystick.cpp
+openmortal_SOURCES = \
+ Audio.cpp FlyingChars.cpp MortalNetworkImpl.cpp sge_primitives.cpp \
+ Backend.cpp Game.cpp OnlineChat.cpp sge_surface.cpp \
+ Background.cpp GameOver.cpp PlayerSelectController.cpp sge_tt_text.cpp \
+ Chooser.cpp gfx.cpp PlayerSelect.cpp State.cpp \
+ common.cpp Joystick.cpp PlayerSelectView.cpp TextArea.cpp \
+ Demo.cpp main.cpp RlePack.cpp \
+ FighterStats.cpp menu.cpp sge_bm_text.cpp
-EXTRA_DIST = Audio.h Backend.h common.h Demo.h FlyingChars.h gfx.h \
- menu.h RlePack.h sge_bm_text.h sge_config.h sge_internal.h \
- sge_primitives.h sge_surface.h sge_tt_text.h State.h \
- Game.h FighterEnum.h PlayerSelect.h MszPerl.h \
- Audio.cpp Backend.cpp common.cpp \
- Demo.cpp FighterStats.cpp FlyingChars.cpp Game.cpp gfx.cpp main.cpp \
- menu.cpp PlayerSelect.cpp RlePack.cpp sge_bm_text.cpp \
- sge_primitives.cpp sge_surface.cpp sge_tt_text.cpp State.cpp \
- GameOver.cpp Background.h MortalNetwork.h MortalNetworkImpl.h \
- FighterStats.h Chooser.h TextArea.h OnlineChat.h Joystick.h Event.h
+EXTRA_DIST = $(openmortal_SOURCES)\
+ Audio.h Event.h menu.h PlayerSelectView.h sge_tt_text.h \
+ Backend.h FighterEnum.h MortalNetwork.h RlePack.h State.h \
+ Background.h FighterStats.h MortalNetworkImpl.h sge_bm_text.h TextArea.h \
+ Chooser.h FlyingChars.h MszPerl.h sge_config.h \
+ common.h Game.h OnlineChat.h sge_internal.h \
+ Demo.h gfx.h PlayerSelectController.h sge_primitives.h \
+ DrawRle.h Joystick.h PlayerSelect.h sge_surface.h
CXXFLAGS= @CXXFLAGS@ -DDATADIR=\"${pkgdatadir}\" -Wall
# set the include path found by configure
#INCLUDES= $(all_includes)
# the library search path.
#msz_LDFLAGS = $(all_libraries)
diff --git a/src/Makefile.in b/src/Makefile.in
index 3c177bb..564929a 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -1,366 +1,368 @@
# Makefile.in generated automatically by automake 1.4-p6 from Makefile.am
# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
SHELL = @SHELL@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
sbindir = @sbindir@
libexecdir = @libexecdir@
datadir = @datadir@
sysconfdir = @sysconfdir@
sharedstatedir = @sharedstatedir@
localstatedir = @localstatedir@
libdir = @libdir@
infodir = @infodir@
mandir = @mandir@
includedir = @includedir@
oldincludedir = /usr/include
DESTDIR =
pkgdatadir = $(datadir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
top_builddir = ..
ACLOCAL = @ACLOCAL@
AUTOCONF = @AUTOCONF@
AUTOMAKE = @AUTOMAKE@
AUTOHEADER = @AUTOHEADER@
INSTALL = @INSTALL@
INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
INSTALL_DATA = @INSTALL_DATA@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
transform = @program_transform_name@
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_alias = @build_alias@
build_triplet = @build@
host_alias = @host_alias@
host_triplet = @host@
target_alias = @target_alias@
target_triplet = @target@
AUTODIRS = @AUTODIRS@
CXX = @CXX@
FT2_CFLAGS = @FT2_CFLAGS@
FT2_CONFIG = @FT2_CONFIG@
FT2_LIBS = @FT2_LIBS@
MAKEINFO = @MAKEINFO@
PACKAGE = @PACKAGE@
PERL = @PERL@
SDL_CFLAGS = @SDL_CFLAGS@
SDL_CONFIG = @SDL_CONFIG@
SDL_LIBS = @SDL_LIBS@
VERSION = @VERSION@
perl_embed_ccflags = @perl_embed_ccflags@
perl_embed_ldflags = @perl_embed_ldflags@
bin_PROGRAMS = openmortal
-openmortal_SOURCES = Audio.cpp Backend.cpp common.cpp Demo.cpp FighterStats.cpp FlyingChars.cpp Game.cpp gfx.cpp main.cpp menu.cpp PlayerSelect.cpp RlePack.cpp sge_bm_text.cpp sge_primitives.cpp sge_surface.cpp sge_tt_text.cpp State.cpp GameOver.cpp Background.cpp MortalNetworkImpl.cpp Chooser.cpp TextArea.cpp OnlineChat.cpp Joystick.cpp
+openmortal_SOURCES = Audio.cpp FlyingChars.cpp MortalNetworkImpl.cpp sge_primitives.cpp Backend.cpp Game.cpp OnlineChat.cpp sge_surface.cpp Background.cpp GameOver.cpp PlayerSelectController.cpp sge_tt_text.cpp Chooser.cpp gfx.cpp PlayerSelect.cpp State.cpp common.cpp Joystick.cpp PlayerSelectView.cpp TextArea.cpp Demo.cpp main.cpp RlePack.cpp FighterStats.cpp menu.cpp sge_bm_text.cpp
-EXTRA_DIST = Audio.h Backend.h common.h Demo.h FlyingChars.h gfx.h menu.h RlePack.h sge_bm_text.h sge_config.h sge_internal.h sge_primitives.h sge_surface.h sge_tt_text.h State.h Game.h FighterEnum.h PlayerSelect.h MszPerl.h Audio.cpp Backend.cpp common.cpp Demo.cpp FighterStats.cpp FlyingChars.cpp Game.cpp gfx.cpp main.cpp menu.cpp PlayerSelect.cpp RlePack.cpp sge_bm_text.cpp sge_primitives.cpp sge_surface.cpp sge_tt_text.cpp State.cpp GameOver.cpp Background.h MortalNetwork.h MortalNetworkImpl.h FighterStats.h Chooser.h TextArea.h OnlineChat.h Joystick.h Event.h
+EXTRA_DIST = $(openmortal_SOURCES) Audio.h Event.h menu.h PlayerSelectView.h sge_tt_text.h Backend.h FighterEnum.h MortalNetwork.h RlePack.h State.h Background.h FighterStats.h MortalNetworkImpl.h sge_bm_text.h TextArea.h Chooser.h FlyingChars.h MszPerl.h sge_config.h common.h Game.h OnlineChat.h sge_internal.h Demo.h gfx.h PlayerSelectController.h sge_primitives.h DrawRle.h Joystick.h PlayerSelect.h sge_surface.h
CXXFLAGS = @CXXFLAGS@ -DDATADIR=\"${pkgdatadir}\" -Wall
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
CONFIG_HEADER = ../config.h
CONFIG_CLEAN_FILES =
PROGRAMS = $(bin_PROGRAMS)
DEFS = @DEFS@ -I. -I$(srcdir) -I..
CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
-openmortal_OBJECTS = Audio.o Backend.o common.o Demo.o FighterStats.o \
-FlyingChars.o Game.o gfx.o main.o menu.o PlayerSelect.o RlePack.o \
-sge_bm_text.o sge_primitives.o sge_surface.o sge_tt_text.o State.o \
-GameOver.o Background.o MortalNetworkImpl.o Chooser.o TextArea.o \
-OnlineChat.o Joystick.o
+openmortal_OBJECTS = Audio.o FlyingChars.o MortalNetworkImpl.o \
+sge_primitives.o Backend.o Game.o OnlineChat.o sge_surface.o \
+Background.o GameOver.o PlayerSelectController.o sge_tt_text.o \
+Chooser.o gfx.o PlayerSelect.o State.o common.o Joystick.o \
+PlayerSelectView.o TextArea.o Demo.o main.o RlePack.o FighterStats.o \
+menu.o sge_bm_text.o
openmortal_LDADD = $(LDADD)
openmortal_DEPENDENCIES =
openmortal_LDFLAGS =
CXXCOMPILE = $(CXX) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
CXXLD = $(CXX)
CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@
DIST_COMMON = Makefile.am Makefile.in
DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
TAR = gtar
GZIP_ENV = --best
DEP_FILES = .deps/Audio.P .deps/Backend.P .deps/Background.P \
.deps/Chooser.P .deps/Demo.P .deps/FighterStats.P .deps/FlyingChars.P \
.deps/Game.P .deps/GameOver.P .deps/Joystick.P \
.deps/MortalNetworkImpl.P .deps/OnlineChat.P .deps/PlayerSelect.P \
-.deps/RlePack.P .deps/State.P .deps/TextArea.P .deps/common.P \
-.deps/gfx.P .deps/main.P .deps/menu.P .deps/sge_bm_text.P \
-.deps/sge_primitives.P .deps/sge_surface.P .deps/sge_tt_text.P
+.deps/PlayerSelectController.P .deps/PlayerSelectView.P .deps/RlePack.P \
+.deps/State.P .deps/TextArea.P .deps/common.P .deps/gfx.P .deps/main.P \
+.deps/menu.P .deps/sge_bm_text.P .deps/sge_primitives.P \
+.deps/sge_surface.P .deps/sge_tt_text.P
SOURCES = $(openmortal_SOURCES)
OBJECTS = $(openmortal_OBJECTS)
all: all-redirect
.SUFFIXES:
.SUFFIXES: .S .c .cpp .o .s
$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(BUILT_SOURCES)
cd $(top_builddir) \
&& CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
mostlyclean-binPROGRAMS:
clean-binPROGRAMS:
-test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
distclean-binPROGRAMS:
maintainer-clean-binPROGRAMS:
install-binPROGRAMS: $(bin_PROGRAMS)
@$(NORMAL_INSTALL)
$(mkinstalldirs) $(DESTDIR)$(bindir)
@list='$(bin_PROGRAMS)'; for p in $$list; do \
if test -f $$p; then \
echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \
$(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
else :; fi; \
done
uninstall-binPROGRAMS:
@$(NORMAL_UNINSTALL)
list='$(bin_PROGRAMS)'; for p in $$list; do \
rm -f $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
done
.s.o:
$(COMPILE) -c $<
.S.o:
$(COMPILE) -c $<
mostlyclean-compile:
-rm -f *.o core *.core
clean-compile:
distclean-compile:
-rm -f *.tab.c
maintainer-clean-compile:
openmortal: $(openmortal_OBJECTS) $(openmortal_DEPENDENCIES)
@rm -f openmortal
$(CXXLINK) $(openmortal_LDFLAGS) $(openmortal_OBJECTS) $(openmortal_LDADD) $(LIBS)
.cpp.o:
$(CXXCOMPILE) -c $<
tags: TAGS
ID: $(HEADERS) $(SOURCES) $(LISP)
list='$(SOURCES) $(HEADERS)'; \
unique=`for i in $$list; do echo $$i; done | \
awk ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
here=`pwd` && cd $(srcdir) \
&& mkid -f$$here/ID $$unique $(LISP)
TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP)
tags=; \
here=`pwd`; \
list='$(SOURCES) $(HEADERS)'; \
unique=`for i in $$list; do echo $$i; done | \
awk ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
|| (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS)
mostlyclean-tags:
clean-tags:
distclean-tags:
-rm -f TAGS ID
maintainer-clean-tags:
distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
subdir = src
distdir: $(DISTFILES)
here=`cd $(top_builddir) && pwd`; \
top_distdir=`cd $(top_distdir) && pwd`; \
distdir=`cd $(distdir) && pwd`; \
cd $(top_srcdir) \
&& $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --gnu src/Makefile
@for file in $(DISTFILES); do \
d=$(srcdir); \
if test -d $$d/$$file; then \
cp -pr $$d/$$file $(distdir)/$$file; \
else \
test -f $(distdir)/$$file \
|| ln $$d/$$file $(distdir)/$$file 2> /dev/null \
|| cp -p $$d/$$file $(distdir)/$$file || :; \
fi; \
done
DEPS_MAGIC := $(shell mkdir .deps > /dev/null 2>&1 || :)
-include $(DEP_FILES)
mostlyclean-depend:
clean-depend:
distclean-depend:
-rm -rf .deps
maintainer-clean-depend:
%.o: %.c
@echo '$(COMPILE) -c $<'; \
$(COMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
@-cp .deps/$(*F).pp .deps/$(*F).P; \
tr ' ' '\012' < .deps/$(*F).pp \
| sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
>> .deps/$(*F).P; \
rm .deps/$(*F).pp
%.lo: %.c
@echo '$(LTCOMPILE) -c $<'; \
$(LTCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
@-sed -e 's/^\([^:]*\)\.o[ ]*:/\1.lo \1.o :/' \
< .deps/$(*F).pp > .deps/$(*F).P; \
tr ' ' '\012' < .deps/$(*F).pp \
| sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
>> .deps/$(*F).P; \
rm -f .deps/$(*F).pp
%.o: %.cpp
@echo '$(CXXCOMPILE) -c $<'; \
$(CXXCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
@-cp .deps/$(*F).pp .deps/$(*F).P; \
tr ' ' '\012' < .deps/$(*F).pp \
| sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
>> .deps/$(*F).P; \
rm .deps/$(*F).pp
%.lo: %.cpp
@echo '$(LTCXXCOMPILE) -c $<'; \
$(LTCXXCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
@-sed -e 's/^\([^:]*\)\.o[ ]*:/\1.lo \1.o :/' \
< .deps/$(*F).pp > .deps/$(*F).P; \
tr ' ' '\012' < .deps/$(*F).pp \
| sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
>> .deps/$(*F).P; \
rm -f .deps/$(*F).pp
info-am:
info: info-am
dvi-am:
dvi: dvi-am
check-am: all-am
check: check-am
installcheck-am:
installcheck: installcheck-am
install-exec-am: install-binPROGRAMS
install-exec: install-exec-am
install-data-am:
install-data: install-data-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
install: install-am
uninstall-am: uninstall-binPROGRAMS
uninstall: uninstall-am
all-am: Makefile $(PROGRAMS)
all-redirect: all-am
install-strip:
$(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
installdirs:
$(mkinstalldirs) $(DESTDIR)$(bindir)
mostlyclean-generic:
clean-generic:
distclean-generic:
-rm -f Makefile $(CONFIG_CLEAN_FILES)
-rm -f config.cache config.log stamp-h stamp-h[0-9]*
maintainer-clean-generic:
mostlyclean-am: mostlyclean-binPROGRAMS mostlyclean-compile \
mostlyclean-tags mostlyclean-depend mostlyclean-generic
mostlyclean: mostlyclean-am
clean-am: clean-binPROGRAMS clean-compile clean-tags clean-depend \
clean-generic mostlyclean-am
clean: clean-am
distclean-am: distclean-binPROGRAMS distclean-compile distclean-tags \
distclean-depend distclean-generic clean-am
distclean: distclean-am
maintainer-clean-am: maintainer-clean-binPROGRAMS \
maintainer-clean-compile maintainer-clean-tags \
maintainer-clean-depend maintainer-clean-generic \
distclean-am
@echo "This command is intended for maintainers to use;"
@echo "it deletes files that may require special tools to rebuild."
maintainer-clean: maintainer-clean-am
.PHONY: mostlyclean-binPROGRAMS distclean-binPROGRAMS clean-binPROGRAMS \
maintainer-clean-binPROGRAMS uninstall-binPROGRAMS install-binPROGRAMS \
mostlyclean-compile distclean-compile clean-compile \
maintainer-clean-compile tags mostlyclean-tags distclean-tags \
clean-tags maintainer-clean-tags distdir mostlyclean-depend \
distclean-depend clean-depend maintainer-clean-depend info-am info \
dvi-am dvi check check-am installcheck-am installcheck install-exec-am \
install-exec install-data-am install-data install-am install \
uninstall-am uninstall all-redirect all-am all installdirs \
mostlyclean-generic distclean-generic clean-generic \
maintainer-clean-generic clean mostlyclean distclean maintainer-clean
# set the include path found by configure
#INCLUDES= $(all_includes)
# the library search path.
#msz_LDFLAGS = $(all_libraries)
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
diff --git a/src/MortalNetwork.h b/src/MortalNetwork.h
index 1dd0114..7c1eb5a 100644
--- a/src/MortalNetwork.h
+++ b/src/MortalNetwork.h
@@ -1,131 +1,131 @@
/***************************************************************************
MortalNetwork.h - description
-------------------
begin : Sun Jan 25 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
#ifndef MORTALNETWORK_H
#define MORTALNETWORK_H
#include "FighterEnum.h"
-#include "SDL/SDL_types.h"
+#include "SDL_types.h"
/** Mortal Network messages:
TYPICAL MESSAGE FLOW:
<connection is established>
Introduction messages are sent (version checking, usernames)
<players go to the character selection screen>
1. F <number> messages go both ways as players choose their characters.
2. R message goes in both direction when players have finished choosing.
<both sides go to the game screen>
In odd rounds, the "server" if the "master" and the "client" is the "slave"
In even rounds, the "client" if the "master" and the "server" is the "slave"
Both the master and the slave send an S message to synchronize the game start.
The master sends G <text> messages to update the backend on the slave side.
The slave sends K <number> <bool> messages to communicate keystrokes to the master side.
The master sends O <number> <bool> message when the round is over.
Back to game start synchronization.
<both sides go to final judgement - may disconnect the game>
<both sides go back to the character selection screen>
OTHERS:
Msgs can be send on the character selection screen with M <text>.
The connection can be broken at any time. IsConnectionAlive() must be called
periodically.
The "server" is always appears as player 1, the "client" is always player 2.
However, they both use the "Player 1" keys.
*/
class CMortalNetwork
{
public:
struct SGameParams
{
Uint32 iGameTime;
Uint32 iGameSpeed;
Uint32 iHitPoints;
Uint32 iBackgroundNumber;
};
public:
static void Create();
// Connection's lifecycle
virtual bool Start( const char* a_pcServerName ) = 0; // Accept connection, or connect to given server
virtual void Stop() = 0; // Disconnect
virtual bool IsConnectionAlive() = 0; // Is the connection still good?
virtual void Update() = 0; // Read network traffic. Might get disconnected...
virtual const char* GetLastError() = 0;
virtual bool IsMaster() = 0; // Am I Master or Slave?
// Msg related methods
virtual const char* GetRemoteUsername() = 0; // This is the name that is passed upon connection.
virtual void SendMsg( const char* a_rsMsg ) = 0; // Prompt the user for a line of chat text
virtual bool IsMsgAvailable() = 0; // Returns true is a chatline has arrived
virtual const char* GetMsg() = 0; // The next chatline, or NULL if there are no more.
// Charater Selection methods
virtual bool IsRemoteFighterAvailable( FighterEnum a_enFighter ) = 0; // Does the other computer have fighter X installed?
virtual FighterEnum GetRemoteFighter() = 0; // Returns the latest fighter chosen by the remote side.
virtual bool IsRemoteSideReady() = 0; // The other player is finished choosing.
virtual void SendFighter( FighterEnum a_enFighter ) = 0; // Let the other side know that I switched to fighter X.
virtual void SendReady() = 0; // Let the other side know that I am ready.
virtual void SendGameParams( int a_iGameSpeed, int a_iGameTime, int a_iHitPoints, int a_iBackgroundNumber ) = 0;
virtual SGameParams GetGameParams() = 0;
// Game methods
virtual bool SynchStartRound() = 0;
virtual void SendGameData( const char* a_pcGameData ) = 0;
virtual const char* GetLatestGameData() = 0;
virtual void SendKeystroke( int a_iTime, int a_iKey, bool a_bPressed ) = 0;
virtual bool GetKeystroke( int& a_riOutTime, int& a_riOutKey, bool& a_rbPressed ) = 0;
virtual void SendGameTick( int a_iGameTick ) = 0;
virtual int GetGameTick() = 0;
virtual void SendHurryup( int a_iHurryUpCode ) = 0;
virtual int GetHurryup() = 0;
virtual void SendRoundOver( int a_iWhoWon, bool a_bGameOver ) = 0;
virtual bool IsRoundOver() = 0;
virtual bool IsGameOver() = 0;
virtual int GetWhoWon() = 0;
};
extern CMortalNetwork* g_poNetwork;
#endif // MORTALNETWORK_H
diff --git a/src/MortalNetworkImpl.cpp b/src/MortalNetworkImpl.cpp
index 2f9e4c3..d442734 100644
--- a/src/MortalNetworkImpl.cpp
+++ b/src/MortalNetworkImpl.cpp
@@ -1,939 +1,941 @@
/***************************************************************************
MortalNetworkImpl.cpp - description
-------------------
begin : Sun Jan 25 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
#include "MortalNetworkImpl.h"
#include "State.h"
#include "PlayerSelect.h"
#include "common.h"
#include "config.h"
#define MORTALNETWORKPORT 0x3A22
#define MAXSTRINGLENGTH 900
// Some graphics routines, defined in menu.cpp
void MortalNetworkResetMessages( bool a_bClear );
void MortalNetworkMessage( const char* format, ... );
bool MortalNetworkCheckKey();
const char* GetGameTimeString( int a_iValue );
const char* GetGameSpeedString( int a_iValue );
const char* GetHitPointsString( int a_iValue );
CMortalNetwork* g_poNetwork = NULL;
void CMortalNetwork::Create() // static
{
if ( NULL == g_poNetwork )
{
g_poNetwork = new CMortalNetworkImpl;
}
}
CMortalNetworkImpl::CMortalNetworkImpl()
{
m_enState = NS_DISCONNECTED;
m_bServer = false;
m_bMaster = false;
m_poSocket = NULL;
m_enRemoteFighter = UNKNOWN;
m_bRemoteReady = false;
m_bRoundOver = false;
m_iWhoWon = -1;
m_bGameOver = false;
m_iGameTick = 0;
m_iHurryupCode = 0;
m_iIncomingBufferSize = 0;
if(SDLNet_Init()==-1)
{
m_bNetworkAvailable = false;
m_sLastError = SDLNet_GetError();
debug ( "Error opening SDLNet: %s\n", m_sLastError.c_str() );
}
m_bNetworkAvailable = true;
}
CMortalNetworkImpl::~CMortalNetworkImpl()
{
Stop();
}
bool CMortalNetworkImpl::Start( const char* a_pcServerName )
{
#define RETURNNOERROR { \
debug( "%s\n", m_sLastError.c_str() ); \
return false; }
#define RETURNWITHERROR { \
m_sLastError = SDLNet_GetError(); \
debug( "%s\n", m_sLastError.c_str() ); \
return false; }
#define RETURNWITHADDITIONALERROR { \
m_sLastError += SDLNet_GetError(); \
debug( "%s\n", m_sLastError.c_str() ); \
return false; }
if ( !m_bNetworkAvailable )
{
return false;
}
debug( "CMortalNetworkImpl::Start( %s )\n", a_pcServerName ? a_pcServerName : "NULL" );
IPaddress oAddress;
if ( a_pcServerName )
{
MortalNetworkMessage( Translate("Resolving hostname (%s)..."), a_pcServerName );
}
int iResult = SDLNet_ResolveHost( &oAddress, (char*) a_pcServerName, MORTALNETWORKPORT );
if ( iResult )
{
m_sLastError = Translate( "Couldn't resolve host." );
RETURNNOERROR;
}
debug( "IP Address of server is 0x%x\n", oAddress.host );
if ( a_pcServerName )
{
Uint32 ipaddr=SDL_SwapBE32(oAddress.host);
MortalNetworkMessage("Connecting to %d.%d.%d.%d port %d",
ipaddr>>24, (ipaddr>>16)&0xff, (ipaddr>>8)&0xff, ipaddr&0xff, MORTALNETWORKPORT );
}
if ( !a_pcServerName )
{
// SERVER-MODE CONNECTION
m_poSocket = SDLNet_TCP_Open( &oAddress );
if ( NULL == m_poSocket ) RETURNWITHERROR;
// Wait for connection ...
MortalNetworkMessage ( Translate("Waiting for connection... (press any key to abort)") );
MortalNetworkMessage( Translate("You must have port 14882 open for this to work.") );
TCPsocket poClient;
while ( 1 )
{
poClient = SDLNet_TCP_Accept( m_poSocket );
if ( poClient ) break;
if (MortalNetworkCheckKey()) break;;
SDL_Delay( 100 );
}
SDLNet_TCP_Close( m_poSocket );
if ( NULL == poClient )
{
m_sLastError = "No connection.";
return false;
}
IPaddress* poRemoteAddress = SDLNet_TCP_GetPeerAddress(poClient);
if ( !poRemoteAddress )
{
RETURNWITHERROR;
}
Uint32 ipaddr=SDL_SwapBE32(poRemoteAddress->host);
MortalNetworkMessage("Accepted connection from %d.%d.%d.%d port %d",
ipaddr>>24, (ipaddr>>16)&0xff, (ipaddr>>8)&0xff, ipaddr&0xff, MORTALNETWORKPORT);
// Set the client socket as our socket, and drop the server socket.
m_poSocket = poClient;
}
else
{
// CLIENT-MODE CONNECTION
MortalNetworkMessage ( Translate("Waiting for connection... (press any key to abort)") );
while (1)
{
m_poSocket = SDLNet_TCP_Open( &oAddress );
if ( m_poSocket ) break;
if ( MortalNetworkCheckKey() ) break;
SDL_Delay( 100 );
}
if ( NULL == m_poSocket )
{
RETURNWITHERROR;
}
}
// CONNECTION ESTABLISHED. SEND INTRO PACKETS
MortalNetworkMessage( Translate("Connection established.") );
struct SIntroPackage
{
char cID;
char acVersion[10];
} oIntroPackage, oRemotePackage;
oIntroPackage.cID = 'I';
strncpy( oIntroPackage.acVersion, VERSION, 10 );
oIntroPackage.acVersion[9] = 0;
debug( "Sending intro package... " );
int iRetval = SDLNet_TCP_Send( m_poSocket, &oIntroPackage, sizeof( oIntroPackage ) );
if ( iRetval < (int) sizeof( oIntroPackage ) )
{
RETURNWITHERROR;
}
iRetval = SDLNet_TCP_Recv( m_poSocket, &oRemotePackage, sizeof( oRemotePackage ) );
if ( iRetval <= 0 )
{
RETURNWITHERROR;
}
if ( iRetval < (int) sizeof( oRemotePackage )
|| oRemotePackage.cID != 'I'
|| strncmp( oRemotePackage.acVersion, VERSION, 9 ) )
{
m_sLastError = Translate( "The remote side has a different version of OpenMortal running." );
RETURNNOERROR;
}
MortalNetworkMessage( Translate("Life is good.") );
m_enState = NS_CHARACTER_SELECTION;
m_bServer = NULL == a_pcServerName;
m_bMaster = m_bServer;
m_sLastError = "";
m_asMsgs.clear();
m_enRemoteFighter = UNKNOWN;
m_bRemoteReady = false;
m_sLatestGameData = "";
m_aiKeystrokes.clear();
m_aiKeystrokes.clear();
m_abKeystrokes.clear();
m_iGameTick = 0;
m_iHurryupCode = 0;
m_iIncomingBufferSize = 0;
m_aiAvailableRemoteFighters.clear();
m_oGameParams.iGameTime = m_oGameParams.iGameSpeed = m_oGameParams.iHitPoints = m_oGameParams.iBackgroundNumber = 0;
m_sRemoteUserName = "HIM";
m_poSocketSet = SDLNet_AllocSocketSet( 1 );
SDLNet_TCP_AddSocket( m_poSocketSet, m_poSocket ); // Check for errors?
SendRawData( 'U', g_oState.m_acNick, strlen(g_oState.m_acNick)+1 );
return true;
}
void CMortalNetworkImpl::Stop()
{
if ( NS_DISCONNECTED == m_enState )
{
return;
}
g_oState.m_enGameMode = SState::IN_DEMO;
m_enState = NS_DISCONNECTED;
SDLNet_FreeSocketSet( m_poSocketSet );
SDLNet_TCP_Close( m_poSocket );
}
bool CMortalNetworkImpl::IsConnectionAlive()
{
return ( NS_DISCONNECTED != m_enState );
}
#define DISCONNECTONCOMMUNICATIONERROR { \
m_sLastError = Translate("Communication error. Disconnecting."); \
Stop(); \
return; }
#define DISCONNECTWITH(A) { \
m_sLastError = Translate("Communication error. Disconnecting."); \
Stop(); \
return(A); }
#define CHECKCONNECTION if ( NS_DISCONNECTED == m_enState ) return;
void CMortalNetworkImpl::Update()
{
CHECKCONNECTION;
// 1. CHECK FOR STUFF TO READ
int iRetval = SDLNet_CheckSockets( m_poSocketSet, 0 );
if ( iRetval <= 0 )
{
return;
}
// 2. APPEND AT MOST 1024 bytes TO THE END OF THE INCOMING BUFFER
iRetval = SDLNet_TCP_Recv( m_poSocket, m_acIncomingBuffer + m_iIncomingBufferSize, 1024 );
if ( iRetval <= 0 )
{
m_sLastError = SDLNet_GetError();
Stop();
return;
}
m_iIncomingBufferSize += iRetval;
// 3. CONSUME THE INCOMING BUFFER.
// We always make sure the incoming buffer starts with a package header.
int iOffset = 0;
while ( iOffset < m_iIncomingBufferSize )
{
// 3.1. Check if we have enough data to receive the package.
if ( m_iIncomingBufferSize - iOffset < 4 )
{
// Not enough space left for a full header.
debug( "Not enough space left for a full header (%d).\n", m_iIncomingBufferSize-iOffset );
break;
}
unsigned int iLengthOfPackage = SDL_SwapBE16(*((Uint16*)(m_acIncomingBuffer + iOffset + 1)));
if ( iLengthOfPackage > 1000 )
{
debug( "Maximum package size exceeded.\n" );
DISCONNECTONCOMMUNICATIONERROR;
}
// debug( "Receiving stuff.. %c type, %d package length, offset %d in buffer, %d bytes in buffer\n",
// m_acIncomingBuffer[iOffset], iLengthOfPackage, iOffset, m_iIncomingBufferSize );
if ( iOffset + 4 + (int)iLengthOfPackage > m_iIncomingBufferSize )
{
// Not enough space left for the actual package.
debug( "Not enough space left for the actual package.\n" );
break;
}
// 3.2. Receive the data.
switch ( m_acIncomingBuffer[iOffset] )
{
case 'M': ReceiveMsg( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'F': ReceiveFighter( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'R': ReceiveReady( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'S': m_bSynchQueryResponse=true; break;
case 'G': ReceiveGameData( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'K': ReceiveKeystroke( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'O': ReceiveRoundOver( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'T': ReceiveGameTick( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'H': ReceiveHurryup( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'A': ReceiveRemoteFighterAvailable( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'Q': ReceiveRemoteFighterQuery( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'P': ReceiveGameParams( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'U': ReceiveRemoteUserName( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
default:
{
debug( "Bad ID: %c (%d)\n", m_acIncomingBuffer[iOffset], m_acIncomingBuffer[iOffset] );
DISCONNECTONCOMMUNICATIONERROR;
}
}
if ( !IsConnectionAlive() )
{
return;
}
iOffset += iLengthOfPackage + 4;
}
// 4. MOVE LEFTOVER DATA TO THE BEGINNING OF THE INCOMING BUFFER
// The leftover data starts at iOffset, and is (m_iIncomingBufferSize-iOffset) long.
memmove( m_acIncomingBuffer, m_acIncomingBuffer + iOffset, m_iIncomingBufferSize-iOffset );
m_iIncomingBufferSize -= iOffset;
}
const char* CMortalNetworkImpl::GetLastError()
{
return m_sLastError.c_str();
}
bool CMortalNetworkImpl::IsMaster()
{
return m_bMaster;
}
void CMortalNetworkImpl::ReceiveRemoteUserName( void* a_pData, int a_iLength )
{
if ( a_iLength < 1 || a_iLength > MAXSTRINGLENGTH ) DISCONNECTONCOMMUNICATIONERROR;
char* acData = (char*) a_pData;
acData[ a_iLength-1 ] = 0; // Last char should be 0, just making sure..
m_sRemoteUserName = acData;
}
const char* CMortalNetworkImpl::GetRemoteUsername()
{
return m_sRemoteUserName.c_str();
}
/*************************************************************************
MSG RELATED METHODS
*************************************************************************/
/** All sent data must go through this method. It ensures the well-formed
header for the data.
The header itself looks like this:
ID char
Length Uint16
Reserved char
This is followed by as many bytes as the Length is.
*/
void CMortalNetworkImpl::SendRawData( char a_cID, const void* a_pData, int a_iLength )
{
CHECKCONNECTION;
int iPacketLength = a_iLength + 4;
char *pcBuffer = new char[iPacketLength];
pcBuffer[0] = a_cID;
*((Uint16*)(pcBuffer+1)) = SDL_SwapBE16( a_iLength );
pcBuffer[3] = 0;
if ( a_iLength > 0 )
{
memcpy( pcBuffer+4, a_pData, a_iLength );
}
int iRetval = SDLNet_TCP_Send( m_poSocket, pcBuffer, iPacketLength );
if ( iRetval != iPacketLength ) DISCONNECTONCOMMUNICATIONERROR;
+
+ delete [] pcBuffer;
}
void CMortalNetworkImpl::SendMsg( const char* a_pcMsg )
{
CHECKCONNECTION;
int iMsgLen = strlen( a_pcMsg ) + 1;
if ( iMsgLen > MAXSTRINGLENGTH )
{
// Will not be 0 terminated if exceeds length!
iMsgLen = MAXSTRINGLENGTH;
}
SendRawData( 'M', a_pcMsg, iMsgLen );
}
void CMortalNetworkImpl::ReceiveMsg( void* a_pData, int a_iLength )
{
if ( a_iLength < 1 || a_iLength > MAXSTRINGLENGTH ) DISCONNECTONCOMMUNICATIONERROR;
char* pcData = (char*) a_pData;
pcData[ a_iLength-1 ] = 0; // Last char should be 0, just making sure..
std::string sMsg = "<" + m_sRemoteUserName + "> " + pcData;
m_asMsgs.push_back( sMsg );
}
bool CMortalNetworkImpl::IsMsgAvailable()
{
return m_asMsgs.size() > 0;
}
const char* CMortalNetworkImpl::GetMsg()
{
static std::string sLastMsg;
if ( IsMsgAvailable() )
{
sLastMsg = m_asMsgs.front();
m_asMsgs.pop_front();
return sLastMsg.c_str();
}
return NULL;
}
/*************************************************************************
CHARACTER SELECTION RELATED METHODS
*************************************************************************/
/** Unfortunately not all STL are created equal... Some do not have the
find algorithm. So, here is our feeble implementation... */
template<class InputIterator, class EqualityComparable>
InputIterator MszFind(InputIterator first, InputIterator last, const EqualityComparable& value )
{
while ( first != last
&& *first != value )
{
++first;
}
return first;
}
bool CMortalNetworkImpl::IsRemoteFighterAvailable( FighterEnum a_enFighter )
{
if ( 0 == a_enFighter )
{
return false;
}
// Check if we already have it cached.
if ( MszFind( m_aiAvailableRemoteFighters.begin(), m_aiAvailableRemoteFighters.end(), a_enFighter ) != m_aiAvailableRemoteFighters.end() )
{
return true;
}
if ( MszFind( m_aiAvailableRemoteFighters.begin(), m_aiAvailableRemoteFighters.end(), -a_enFighter ) != m_aiAvailableRemoteFighters.end() )
{
return false;
}
Uint32 iFighter = SDL_SwapBE32( a_enFighter );
SendRawData( 'Q', &iFighter, sizeof(Uint32) );
for ( int i=0; i<100; ++i )
{
SDL_Delay(10);
Update();
if ( MszFind( m_aiAvailableRemoteFighters.begin(), m_aiAvailableRemoteFighters.end(), a_enFighter ) != m_aiAvailableRemoteFighters.end() )
{
return true;
}
if ( MszFind( m_aiAvailableRemoteFighters.begin(), m_aiAvailableRemoteFighters.end(), -a_enFighter ) != m_aiAvailableRemoteFighters.end() )
{
return false;
}
}
m_aiAvailableRemoteFighters.push_front( -a_enFighter );
return false;
}
void CMortalNetworkImpl::ReceiveRemoteFighterQuery( void* a_pData, int a_iLength )
{
if ( a_iLength != sizeof(Uint32) ) DISCONNECTONCOMMUNICATIONERROR;
FighterEnum iFighter = (FighterEnum) SDL_SwapBE32( *((Uint32*)a_pData) );
bool bAvailable = g_oPlayerSelect.IsLocalFighterAvailable( iFighter );
Uint32 iResponse = bAvailable ? iFighter : iFighter + 100000;
debug( "ReceiveRemoteFighterQuery: %d -> %d\n", iFighter, iResponse );
iResponse = SDL_SwapBE32( iResponse );
SendRawData( 'A', &iResponse, sizeof(Uint32) );
}
void CMortalNetworkImpl::ReceiveRemoteFighterAvailable( void* a_pData, int a_iLength )
{
if ( a_iLength != sizeof(Uint32) ) DISCONNECTONCOMMUNICATIONERROR;
Uint32 iFighter = SDL_SwapBE32( *((Uint32*)a_pData) );
debug( "ReceiveRemoteFighterAvailable: %d\n", iFighter );
if ( iFighter >= 100000 )
{
m_aiAvailableRemoteFighters.push_front( -(iFighter-100000) );
}
else
{
m_aiAvailableRemoteFighters.push_front( iFighter );
}
}
void CMortalNetworkImpl::SendFighter( FighterEnum a_enFighter )
{
CHECKCONNECTION;
Uint32 iFighter = SDL_SwapBE32( a_enFighter );
SendRawData( 'F', &iFighter, sizeof (iFighter) );
}
void CMortalNetworkImpl::ReceiveFighter( void* a_pcData, int a_iLength )
{
Uint32 iFighter;
if ( a_iLength != sizeof(iFighter) ) DISCONNECTONCOMMUNICATIONERROR;
iFighter = *((Uint32*)a_pcData);
m_enRemoteFighter = (FighterEnum) SDL_SwapBE32( iFighter );
debug( "ReceiveFighter: %d\n", m_enRemoteFighter );
}
FighterEnum CMortalNetworkImpl::GetRemoteFighter()
{
return m_enRemoteFighter;
}
void CMortalNetworkImpl::SendReady()
{
CHECKCONNECTION;
SendRawData( 'R', NULL, 0 );
}
void CMortalNetworkImpl::ReceiveReady( void* a_pData, int a_iLength )
{
if ( a_iLength != 0 ) DISCONNECTONCOMMUNICATIONERROR;
m_bRemoteReady = true;
}
bool CMortalNetworkImpl::IsRemoteSideReady()
{
return m_bRemoteReady;
}
void CMortalNetworkImpl::SendGameParams( int a_iGameSpeed, int a_iGameTime, int a_iHitPoints, int a_iBackgroundNumber )
{
CHECKCONNECTION;
if ( (int)m_oGameParams.iGameSpeed == a_iGameSpeed
&& (int)m_oGameParams.iGameTime == a_iGameTime
&& (int)m_oGameParams.iHitPoints == a_iHitPoints
&& (int)m_oGameParams.iBackgroundNumber == a_iBackgroundNumber )
{
// Nothing to update.
return;
}
m_oGameParams.iGameSpeed = a_iGameSpeed;
m_oGameParams.iGameTime = a_iGameTime;
m_oGameParams.iHitPoints = a_iHitPoints;
m_oGameParams.iBackgroundNumber = a_iBackgroundNumber;
SGameParams oPackage;
oPackage.iGameSpeed = SDL_SwapBE32( a_iGameSpeed );
oPackage.iGameTime = SDL_SwapBE32( a_iGameTime );
oPackage.iHitPoints = SDL_SwapBE32( a_iHitPoints );
oPackage.iBackgroundNumber = SDL_SwapBE32( a_iBackgroundNumber );
SendRawData( 'P', &oPackage, sizeof(SGameParams) );
}
void CMortalNetworkImpl::ReceiveGameParams( void* a_pData, int a_iLength )
{
if ( a_iLength != sizeof(SGameParams) ) DISCONNECTONCOMMUNICATIONERROR;
SGameParams* poPackage = (SGameParams*) a_pData;
if ( m_oGameParams.iGameSpeed != SDL_SwapBE32( poPackage->iGameSpeed ) )
{
m_oGameParams.iGameSpeed = SDL_SwapBE32( poPackage->iGameSpeed );
m_asMsgs.push_back( std::string("*** ") + GetGameSpeedString( m_oGameParams.iGameSpeed ) );
}
if ( m_oGameParams.iGameTime != SDL_SwapBE32( poPackage->iGameTime ) )
{
m_oGameParams.iGameTime = SDL_SwapBE32( poPackage->iGameTime );
m_asMsgs.push_back( std::string("*** ") + GetGameTimeString( m_oGameParams.iGameTime) );
}
if ( m_oGameParams.iHitPoints != SDL_SwapBE32( poPackage->iHitPoints ) )
{
m_oGameParams.iHitPoints = SDL_SwapBE32( poPackage->iHitPoints );
m_asMsgs.push_back( std::string("*** ") + GetHitPointsString( m_oGameParams.iHitPoints ) );
}
m_oGameParams.iBackgroundNumber = SDL_SwapBE32( poPackage->iBackgroundNumber );
}
CMortalNetworkImpl::SGameParams CMortalNetworkImpl::GetGameParams()
{
return m_oGameParams;
}
/*************************************************************************
GAME RELATED METHODS
*************************************************************************/
bool CMortalNetworkImpl::SynchStartRound()
{
debug( "SynchStartRound STARTED.\n" );
m_bSynchQueryResponse = false;
// run until both sides manage to get a SYNCH
int i=0;
while ( !m_bSynchQueryResponse )
{
SendRawData('S', NULL, 0);
if ( !IsConnectionAlive() ) break;
Update();
SDL_Delay(100);
if ( !IsConnectionAlive() ) break;
++i;
if ( i == 10 )
{
debug( "Synch is slow...\n" );
MortalNetworkResetMessages( true );
MortalNetworkMessage( "Synching with remote side..." );
MortalNetworkMessage( "Press any key to disconnect." );
}
if ( i > 10
&& MortalNetworkCheckKey() )
{
Stop();
return false;
}
}
if ( IsConnectionAlive() )
{
m_enState = NS_IN_GAME;
m_bRoundOver = false;
m_bGameOver = false;
m_bRemoteReady = false;
m_bSynchQueryResponse = false;
m_iHurryupCode = -1;
}
return IsConnectionAlive();
debug( "SynchStartRound FINISHED.\n" );
}
void CMortalNetworkImpl::SendGameData( const char* a_pcGameData )
{
CHECKCONNECTION;
int iMsgLen = strlen( a_pcGameData ) + 1;
if ( iMsgLen > MAXSTRINGLENGTH )
{
// Will not be 0 terminated if exceeds length!
iMsgLen = MAXSTRINGLENGTH;
}
SendRawData( 'G', a_pcGameData, iMsgLen );
}
void CMortalNetworkImpl::ReceiveGameData( void* a_pData, int a_iLength )
{
if ( a_iLength < 1 || a_iLength > MAXSTRINGLENGTH ) DISCONNECTONCOMMUNICATIONERROR;
char* pcData = (char*) a_pData;
pcData[ a_iLength-1 ] = 0; // Last char should be 0, just making sure..
if ( pcData[0] )
{
m_sLatestGameData = pcData;
}
}
const char* CMortalNetworkImpl::GetLatestGameData()
{
return m_sLatestGameData.c_str();
}
struct SKeystrokePackage
{
Uint32 iTime;
char cKey;
bool bPressed;
};
void CMortalNetworkImpl::SendKeystroke( int a_iTime, int a_iKey, bool a_bPressed )
{
CHECKCONNECTION;
SKeystrokePackage oPackage;
oPackage.iTime = SDL_SwapBE32( a_iTime );
oPackage.cKey = a_iKey;
oPackage.bPressed = a_bPressed;
SendRawData( 'K', &oPackage, sizeof( oPackage) );
}
void CMortalNetworkImpl::ReceiveKeystroke( void* a_pData, int a_iLength )
{
if ( a_iLength != (int)sizeof(SKeystrokePackage) ) DISCONNECTONCOMMUNICATIONERROR;
SKeystrokePackage* poPackage = (SKeystrokePackage*) a_pData;
m_aiKeyTimes.push_back( SDL_SwapBE32(poPackage->iTime) );
m_aiKeystrokes.push_back( poPackage->cKey );
m_abKeystrokes.push_back( poPackage->bPressed );
}
bool CMortalNetworkImpl::GetKeystroke( int& a_riOutTime, int& a_riOutKey, bool& a_rbOutPressed )
{
if ( m_aiKeystrokes.size() == 0 )
{
return false;
}
a_riOutTime = m_aiKeyTimes.front();
a_riOutKey = m_aiKeystrokes.front();
a_rbOutPressed = m_abKeystrokes.front();
m_aiKeyTimes.pop_front();
m_aiKeystrokes.pop_front();
m_abKeystrokes.pop_front();
// debug( "GetKeystroke: %d, %d\n", a_riOutKey, a_rbOutPressed );
return true;
}
struct SGameTickPackage
{
Uint32 iGameTick;
};
void CMortalNetworkImpl::SendGameTick( int a_iGameTick )
{
CHECKCONNECTION;
if ( a_iGameTick < 0 ) a_iGameTick = 0;
SGameTickPackage oPackage;
oPackage.iGameTick = SDL_SwapBE32( a_iGameTick );
SendRawData( 'T', &oPackage, sizeof(SGameTickPackage) );
}
void CMortalNetworkImpl::ReceiveGameTick( void* a_pData, int a_iLength )
{
if ( a_iLength != sizeof(SGameTickPackage) ) DISCONNECTONCOMMUNICATIONERROR;
SGameTickPackage* poPackage = (SGameTickPackage*) a_pData;
m_iGameTick = SDL_SwapBE32( poPackage->iGameTick );
}
int CMortalNetworkImpl::GetGameTick()
{
return m_iGameTick;
}
void CMortalNetworkImpl::SendHurryup( int a_iHurryUpCode )
{
CHECKCONNECTION;
int iPackage = SDL_SwapBE32( a_iHurryUpCode );
SendRawData( 'H', &iPackage, sizeof(int) );
}
void CMortalNetworkImpl::ReceiveHurryup( void* a_pData, int a_iLength )
{
if ( a_iLength != sizeof(int) ) DISCONNECTONCOMMUNICATIONERROR;
m_iHurryupCode = SDL_SwapBE32( *((int*)a_pData) );
}
int CMortalNetworkImpl::GetHurryup()
{
int iRetval = m_iHurryupCode;
m_iHurryupCode = -1;
return iRetval;
}
struct SRoundOrder
{
int iWhoWon;
bool bGameOver;
};
void CMortalNetworkImpl::SendRoundOver( int a_iWhoWon, bool a_bGameOver )
{
CHECKCONNECTION;
SRoundOrder oPackage;
oPackage.iWhoWon = a_iWhoWon;
oPackage.bGameOver = a_bGameOver;
SendRawData( 'O', &oPackage, sizeof(SRoundOrder) );
if ( a_bGameOver )
{
m_enState = NS_CHARACTER_SELECTION;
}
debug ( "SendRoundOver: %d, %d\n", a_iWhoWon, a_bGameOver );
}
void CMortalNetworkImpl::ReceiveRoundOver( void* a_pData, int a_iLength )
{
if ( a_iLength != sizeof(SRoundOrder) ) DISCONNECTONCOMMUNICATIONERROR;
SRoundOrder* poPackage = (SRoundOrder*) a_pData;
m_iWhoWon = poPackage->iWhoWon;
m_bGameOver = poPackage->bGameOver;
m_bRoundOver = true;
debug ( "ReceiveRoundOver: %d, %d\n", m_iWhoWon, m_bGameOver );
}
bool CMortalNetworkImpl::IsRoundOver()
{
return m_bRoundOver;
}
bool CMortalNetworkImpl::IsGameOver()
{
return m_bGameOver;
}
int CMortalNetworkImpl::GetWhoWon()
{
return m_iWhoWon;
}
diff --git a/src/OnlineChat.cpp b/src/OnlineChat.cpp
index 79d8c55..96cea17 100644
--- a/src/OnlineChat.cpp
+++ b/src/OnlineChat.cpp
@@ -1,742 +1,742 @@
/***************************************************************************
OnlineChat.cpp - description
-------------------
begin : Fri Jan 30 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
#include "OnlineChat.h"
#include "TextArea.h"
#include "State.h"
#include "menu.h"
#include "Audio.h"
#include "sge_tt_text.h"
#include "gfx.h"
#include "common.h"
#include "config.h"
#include "Event.h"
//#include "SDL_video.h"
#define MORTALNETSERVER "apocalypse.game-host.org"
#define MORTALNETWORKPORT 0x3A23
// Layout
#define READLINE_Y 440
#define NICKLIST_X 500
void MortalNetworkResetMessages( bool a_bClear );
void MortalNetworkMessage( const char* format, ... );
bool MortalNetworkCheckKey();
/*************************************************************************
CHAT / CHALLENGE MENUS
*************************************************************************/
enum
{
MENU_DISCONNECT = 100,
MENU_CHALLENGE,
MENU_CHANGENICK,
MENU_OK,
MENU_ACCEPTCHALLENGE,
MENU_REJECTHALLENGE,
};
class CChallengeMenu: public Menu
{
public:
CChallengeMenu::CChallengeMenu( std::string a_sChallenger )
: Menu( "You have been challenged!" ),
m_sChallenger( a_sChallenger )
{
m_bAccepted = false;
AddTextMenuItem( "ACCEPT CHALLENGE FROM ", m_sChallenger.c_str(), MENU_ACCEPTCHALLENGE );
AddMenuItem( "NO, I'M REALLY SCARED", SDLK_UNKNOWN, MENU_REJECTHALLENGE );
}
~CChallengeMenu() {}
bool GetAccepted()
{
return m_bAccepted;
}
void ItemActivated( int a_iItemCode, MenuItem* a_poMenuItem )
{
switch ( a_iItemCode )
{
case MENU_ACCEPTCHALLENGE:
m_bAccepted = true;
// intentional fall through
case MENU_REJECTHALLENGE:
m_iReturnCode = 100;
m_bDone = true;
break;
}
}
protected:
std::string m_sChallenger;
bool m_bAccepted;
};
class CChatMenu: public Menu
{
public:
CChatMenu( const TNickMap& a_roNicks )
: Menu( "MortalNet Menu" ),
m_roNicks( a_roNicks )
{
int i=0;
TNickMap::const_iterator it;
for ( it=m_roNicks.begin();
it != m_roNicks.end() && i < 1023;
++it, ++i )
{
if ( it->first == g_oState.m_acNick )
{
--i;
continue;
}
m_apcNicks[i] = (it->first).c_str();
m_aiNicks[i] = i;
}
m_apcNicks[i] = NULL;
m_sNick = g_oState.m_acNick;
AddMenuItem( "DISCONNECT", SDLK_UNKNOWN, MENU_DISCONNECT );
if ( i > 0 )
{
AddEnumMenuItem( "CHALLENGE USER: ", 0, m_apcNicks, m_aiNicks, MENU_CHALLENGE );
}
m_poNickMenuItem = AddTextMenuItem( "Nickname: ", g_oState.m_acNick, MENU_CHANGENICK );
AddMenuItem( "~OPTIONS", SDLK_o, MENU_OPTIONS );
AddMenuItem( "QUIT", SDLK_UNKNOWN, MENU_QUIT );
AddOkCancel( MENU_OK );
}
virtual ~CChatMenu() {}
std::string GetNick()
{
return m_sNick;
}
std::string GetChallengedNick()
{
return m_sChallengedNick;
}
void ItemActivated( int a_iItemCode, MenuItem* a_poMenuItem )
{
switch ( a_iItemCode )
{
case MENU_DISCONNECT:
g_oState.m_enGameMode = SState::IN_DEMO;
m_bDone = true;
m_iReturnCode = -1;
break;
case MENU_CHANGENICK:
{
EnterName( "Nickname: ", m_sNick, m_poNickMenuItem, 12 );
break;
}
case MENU_CHALLENGE:
{
m_sChallengedNick = ((EnumMenuItem*)a_poMenuItem)->GetCurrentText();
m_bDone = true;
m_iReturnCode = 100;
break;
}
case MENU_OK:
{
m_bDone = true;
m_iReturnCode = -1;
break;
}
default:
Menu::ItemActivated( a_iItemCode, a_poMenuItem );
} // end of switch statement
}
protected:
std::string m_sChallengedNick;
std::string m_sNick;
const TNickMap& m_roNicks;
const char* m_apcNicks[1024];
int m_aiNicks[1024];
TextMenuItem* m_poNickMenuItem;
};
/*************************************************************************
CHAT CONNECT/DISCONNECT
*************************************************************************/
COnlineChat::COnlineChat()
{
m_poScreen = gamescreen;
m_poBackground = NULL;
m_poSocket = NULL;
m_poSocketSet = NULL;
m_iIncomingBufferSize = 0;
m_poReadline = NULL;
m_poTextArea = NULL;
}
COnlineChat::~COnlineChat()
{
Stop();
if ( m_poBackground )
{
SDL_FreeSurface( m_poBackground );
m_poBackground = NULL;
}
}
#define CHECKCONNECTION if ( m_poSocket == NULL ) return;
bool COnlineChat::Start()
{
if ( m_poSocket != NULL )
{
return true; // Already connected.
}
#define RETURNNOERROR { \
debug( "%s\n", m_sLastError.c_str() ); \
return false; }
#define RETURNWITHERROR { \
m_sLastError = SDLNet_GetError(); \
debug( "%s\n", m_sLastError.c_str() ); \
return false; }
#define RETURNWITHADDITIONALERROR { \
m_sLastError += SDLNet_GetError(); \
debug( "%s\n", m_sLastError.c_str() ); \
return false; }
- m_poBackground = LoadBackground( "FighterStats.png", 64 );
+ m_poBackground = LoadBackground( "FighterStats.jpg", 64 );
if ( NULL == m_poBackground )
{
return false; // Should carp
}
SDL_BlitSurface( m_poBackground, NULL, m_poScreen, NULL );
SDL_Flip( m_poScreen );
MortalNetworkResetMessages( false );
MortalNetworkMessage( Translate("Resolving hostname (%s)..."), MORTALNETSERVER );
IPaddress oAddress;
int iResult = SDLNet_ResolveHost( &oAddress, MORTALNETSERVER, MORTALNETWORKPORT );
if ( iResult )
{
m_sLastError = Translate( "Couldn't resolve host." );
RETURNNOERROR;
}
debug( "IP Address of server is 0x%x\n", oAddress.host );
Uint32 ipaddr=SDL_SwapBE32(oAddress.host);
MortalNetworkMessage("Connecting to %d.%d.%d.%d port %d",
ipaddr>>24, (ipaddr>>16)&0xff, (ipaddr>>8)&0xff, ipaddr&0xff, oAddress.port);
MortalNetworkMessage ( Translate("Waiting for connection... (press any key to abort)") );
while (1)
{
m_poSocket = SDLNet_TCP_Open( &oAddress );
if ( m_poSocket ) break;
if ( MortalNetworkCheckKey() ) break;
SDL_Delay( 100 );
}
if ( NULL == m_poSocket )
{
RETURNWITHERROR;
}
// CONNECTION ESTABLISHED. SEND INTRO PACKETS
MortalNetworkMessage( Translate("Connection established.") );
m_poSocketSet = SDLNet_AllocSocketSet( 1 );
SDLNet_TCP_AddSocket( m_poSocketSet, m_poSocket ); // Check for errors?
m_iIncomingBufferSize = 0;
m_bMyNickIsOk = false;
m_asNicks.clear();
SendRawData( 'N', g_oState.m_acNick );
return ( m_poSocket != NULL );
}
void COnlineChat::Stop()
{
if ( m_poSocketSet )
{
SDLNet_FreeSocketSet( m_poSocketSet );
m_poSocketSet = NULL;
}
if ( m_poSocket )
{
SDLNet_TCP_Close( m_poSocket );
m_poSocket = NULL;
}
m_bMyNickIsOk = false;
m_asNicks.clear();
if ( g_oState.m_enGameMode == SState::IN_CHAT )
{
g_oState.m_enGameMode = SState::IN_DEMO;
}
}
/*************************************************************************
SENDING / RECEIVING CHAT DATA
*************************************************************************/
void COnlineChat::SendRawData( char a_cID, const std::string& a_rsData )
{
CHECKCONNECTION;
SDLNet_TCP_Send( m_poSocket, &a_cID, 1 );
SDLNet_TCP_Send( m_poSocket, (void*) a_rsData.c_str(), a_rsData.length() );
char cNL = '\n';
SDLNet_TCP_Send( m_poSocket, &cNL, 1 );
}
void COnlineChat::Update()
{
CHECKCONNECTION;
// 1. CHECK FOR STUFF TO READ
int iRetval = SDLNet_CheckSockets( m_poSocketSet, 0 );
if ( iRetval <= 0 )
{
return;
}
// 2. APPEND AT MOST 1024 bytes TO THE END OF THE INCOMING BUFFER
// CHECK FOR BUFFER OVERFLOW HERE.
if ( m_iIncomingBufferSize >= 1024*3 )
{
m_acIncomingBuffer[m_iIncomingBufferSize] = '\n';
m_acIncomingBuffer[m_iIncomingBufferSize+1] = 'x';
m_iIncomingBufferSize += 1;
}
else
{
iRetval = SDLNet_TCP_Recv( m_poSocket, m_acIncomingBuffer + m_iIncomingBufferSize, 1024 );
if ( iRetval <= 0 )
{
m_sLastError = SDLNet_GetError();
Stop();
return;
}
m_iIncomingBufferSize += iRetval;
}
// 3. CONSUME THE INCOMING BUFFER.
// We always make sure the incoming buffer starts with a package header.
int iOffset = 0;
m_acIncomingBuffer[ m_iIncomingBufferSize ] = 0;
bool bRedraw = false;
while ( iOffset < m_iIncomingBufferSize )
{
// 3.1. Find the end of the line.
char* pcLineEnd = strchr( m_acIncomingBuffer + iOffset, '\n' );
if ( NULL == pcLineEnd )
{
// The buffer doesn't have the end of the line (yet)
debug( "Buffer doesn't have the NL (%d buffer, %d offset)\n", m_iIncomingBufferSize, iOffset );
break;
}
*pcLineEnd = 0;
debug( "Receiving message: %d ('%c') type, %d length, %d offset, %d in buffer.\n",
m_acIncomingBuffer[iOffset], m_acIncomingBuffer[iOffset],
pcLineEnd - m_acIncomingBuffer - iOffset, iOffset, m_iIncomingBufferSize );
// 3.2. Receive the data.
switch ( m_acIncomingBuffer[iOffset] )
{
case 'M':
case 'S':
ReceiveMsg( m_acIncomingBuffer[iOffset], m_acIncomingBuffer + iOffset + 1 ); bRedraw = true; break;
case 'J':
case 'L':
case 'N':
case 'Y':
case 'W':
case 'C':
ReceiveUser( m_acIncomingBuffer[iOffset], m_acIncomingBuffer + iOffset + 1 ); bRedraw = true; break;
}
CHECKCONNECTION;
iOffset = pcLineEnd - m_acIncomingBuffer + 1;
}
// 4. MOVE LEFTOVER DATA TO THE BEGINNING OF THE INCOMING BUFFER
// The leftover data starts at iOffset, and is (m_iIncomingBufferSize-iOffset) long.
memmove( m_acIncomingBuffer, m_acIncomingBuffer + iOffset, m_iIncomingBufferSize-iOffset );
m_iIncomingBufferSize -= iOffset;
if ( bRedraw && m_poTextArea )
{
Audio->PlaySample( "pop.wav" );
m_poTextArea->Redraw();
}
}
/*************************************************************************
INCOMING MESSAGES METHODS
*************************************************************************/
void COnlineChat::ReceiveMsg( char a_cID, char* a_pcData )
{
debug( "Received message %c, content is '%s'\n", a_cID, a_pcData );
if ( m_poTextArea )
{
m_poTextArea->AddString( a_pcData, a_cID == 'M' ? C_YELLOW : C_WHITE );
}
}
void COnlineChat::ReceiveUser( char a_cID, char* a_pcData )
{
debug( "Received user %c, content is '%s'\n", a_cID, a_pcData );
// Split up the data.
int iNumWords = 1;
char* pcFirstWord = a_pcData;
char* pcSecondWord = strchr( a_pcData, ' ' );
if ( pcSecondWord )
{
iNumWords = 2;
*pcSecondWord = 0;
++pcSecondWord;
}
int iColor = C_WHITE;
char acMsg[1024];
acMsg[0] = 0;
switch ( a_cID )
{
case 'J':
- snprintf( acMsg, 1024, "*** %s has joined MortalChat from %s.", pcFirstWord, pcSecondWord );
+ sprintf( acMsg, "*** %s has joined MortalChat from %s.", pcFirstWord, pcSecondWord );
if ( strcmp( pcFirstWord, g_oState.m_acNick ) != 0 )
{
m_asNicks[ pcFirstWord ] = pcSecondWord;
}
break;
case 'L':
{
- snprintf( acMsg, 1024, "*** %s has left MortalChat.", pcFirstWord );
+ sprintf( acMsg, "*** %s has left MortalChat.", pcFirstWord );
iColor = C_LIGHTRED;
m_asNicks.erase( pcFirstWord );
debug( "# of Nicks: %d\n", m_asNicks.size() );
break;
}
case 'N':
{
- snprintf( acMsg, 1024, "%s is now known as %s", pcFirstWord, pcSecondWord );
+ sprintf( acMsg, "%s is now known as %s", pcFirstWord, pcSecondWord );
iColor = C_LIGHTGRAY;
std::string sHost = m_asNicks[pcFirstWord];
m_asNicks.erase( pcFirstWord );
m_asNicks[ pcSecondWord ] = sHost;
break;
}
case 'Y':
- snprintf( acMsg, 1024, "You are now known as %s", pcFirstWord );
+ sprintf( acMsg, "You are now known as %s", pcFirstWord );
iColor = C_LIGHTCYAN;
m_bMyNickIsOk = true;
m_asNicks.erase( g_oState.m_acNick );
m_asNicks[ pcFirstWord ] = "";
strncpy( g_oState.m_acNick, pcFirstWord, 127 );
g_oState.m_acNick[127] = 0;
break;
case 'W':
- snprintf( acMsg, 1024, "%s is hailing from %s", pcFirstWord, pcSecondWord );
+ sprintf( acMsg, "%s is hailing from %s", pcFirstWord, pcSecondWord );
iColor = C_LIGHTGRAY;
m_asNicks[ pcFirstWord ] = pcSecondWord;
break;
case 'C':
CChallengeMenu oMenu( pcFirstWord );
DoMenu( oMenu );
if ( oMenu.GetAccepted() )
{
MortalNetworkResetMessages( true );
Connect( m_asNicks[pcFirstWord].c_str() );
}
Redraw();
break;
}
if ( m_poTextArea && m_bMyNickIsOk )
{
m_poTextArea->AddString( acMsg, iColor );
DrawNickList();
}
}
/*************************************************************************
DRAWING THE SCREEN
*************************************************************************/
void COnlineChat::Redraw()
{
SDL_BlitSurface( m_poBackground, NULL, m_poScreen, NULL );
m_poReadline->Redraw();
m_poTextArea->Redraw();
DrawNickList();
SDL_Flip( m_poScreen );
}
void COnlineChat::DrawNickList()
{
SDL_Rect oNickListRect;
oNickListRect.x = NICKLIST_X;
oNickListRect.y = 10;
oNickListRect.w = gamescreen->w - oNickListRect.x - 10;
oNickListRect.h = READLINE_Y - oNickListRect.y - 10;
SDL_Rect oOldClipRect;
SDL_GetClipRect( m_poScreen, &oOldClipRect );
SDL_SetClipRect( m_poScreen, &oNickListRect );
SDL_BlitSurface( m_poBackground, &oNickListRect, m_poScreen, &oNickListRect );
int y = oNickListRect.y + sge_TTF_FontAscent( chatFont );
int yEnd = oNickListRect.y + oNickListRect.h - sge_TTF_FontDescent( chatFont );
TNickMap::const_iterator it;
for ( it = m_asNicks.begin();
it != m_asNicks.end() && y <= yEnd;
++it , y += sge_TTF_FontHeight( chatFont ) )
{
int iColor = (it->first) == g_oState.m_acNick ? C_LIGHTCYAN : C_WHITE;
sge_tt_textout( m_poScreen, chatFont, (it->first).c_str(), oNickListRect.x, y, iColor, C_BLACK, 255 );
}
SDL_UpdateRect( m_poScreen, oNickListRect.x, oNickListRect.y, oNickListRect.w, oNickListRect.h );
SDL_SetClipRect( m_poScreen, NULL );
}
extern int g_iMessageY;
void COnlineChat::DoOnlineChat()
{
if ( !Start() )
{
if ( !g_oState.m_bQuitFlag )
{
DrawTextMSZ( "Couldn't connect", inkFont, 320, g_iMessageY, AlignHCenter|UseShadow, C_LIGHTRED, gamescreen );
DrawTextMSZ( m_sLastError.c_str(), impactFont, 320, g_iMessageY + 40, AlignHCenter|UseShadow, C_LIGHTRED, gamescreen, false );
GetKey( true );
}
return;
}
// CREATE USER INTERFACE ELEMENTS
char acMsg[256];
SDL_Event event;
SDL_BlitSurface( m_poBackground, NULL, m_poScreen, NULL );
SDL_Flip( m_poScreen );
m_poTextArea = new CTextArea( m_poScreen, chatFont, 10, 10, NICKLIST_X-20, READLINE_Y-20 );
m_poTextArea->TintBackground( C_DARKGRAY, 128 );
m_poReadline = new CReadline( m_poScreen, chatFont,
acMsg, 0, 255,
10, READLINE_Y + sge_TTF_FontAscent(chatFont), 620, C_LIGHTCYAN, C_BLACK, 255 );
while (1)
{
if ( g_oState.m_enGameMode != SState::IN_CHAT ) break;
if ( NULL == m_poSocket ) break;
Update();
if ( NULL == m_poSocket ) break;
SDL_Delay( 10 );
if ( g_oState.m_bQuitFlag ) break;
while (SDL_PollEvent(&event))
{
if ( SDL_KEYDOWN != event.type )
{
SMortalEvent oEvent;
TranslateEvent( &event, &oEvent );
switch (oEvent.m_enType)
{
case Me_MENU:
Menu();
break;
case Me_QUIT:
g_oState.m_bQuitFlag = true;
break;
default:
break;
}
continue;
}
// HANDLE SCROLLING THE TEXT AREA
if ( event.type == SDL_KEYDOWN )
{
SDLKey enKey = event.key.keysym.sym;
if ( enKey == SDLK_PAGEUP || enKey == SDLK_KP9 )
{
m_poTextArea->ScrollUp();
continue;
}
if ( enKey == SDLK_PAGEDOWN || enKey == SDLK_KP3 )
{
m_poTextArea->ScrollDown();
continue;
}
if ( enKey == SDLK_ESCAPE )
{
Menu();
continue;
}
}
m_poReadline->HandleKeyEvent( event );
int iResult = m_poReadline->GetResult();
if ( iResult > 0 )
{
if ( strlen( acMsg ) )
{
SendRawData( 'M', acMsg );
std::string sMsg = std::string("<") + g_oState.m_acNick + "> " + acMsg;
m_poTextArea->AddString( sMsg.c_str(), C_LIGHTCYAN );
m_poTextArea->Redraw();
}
m_poReadline->Clear();
acMsg[0] = 0;
m_poReadline->Restart( acMsg, strlen(acMsg), 256, C_LIGHTCYAN, C_BLACK, 255 );
}
}
}
if ( NULL == m_poSocket
&& !g_oState.m_bQuitFlag )
{
DrawTextMSZ( "Connection closed.", inkFont, 320, 210, AlignHCenter | UseShadow, C_WHITE, m_poScreen );
DrawTextMSZ( m_sLastError.c_str(), impactFont, 320, 250, AlignHCenter | UseShadow, C_WHITE, m_poScreen );
SDL_Delay( 1000 );
GetKey( true );
}
delete m_poReadline;
m_poReadline = NULL;
delete m_poTextArea;
m_poTextArea = NULL;
Stop();
}
void COnlineChat::Menu()
{
CChatMenu oMenu( m_asNicks );
DoMenu( oMenu );
if ( !g_oState.m_bQuitFlag )
{
if ( oMenu.GetNick() != g_oState.m_acNick )
{
SendRawData( 'N', oMenu.GetNick() );
}
if ( oMenu.GetChallengedNick().length() )
{
SendRawData( 'C', oMenu.GetChallengedNick() );
MortalNetworkResetMessages( true );
Connect( NULL );
}
Redraw();
}
}
/** Static global entry point for chatting. */
void DoOnlineChat()
{
g_oState.m_enGameMode = SState::IN_CHAT;
COnlineChat oChat;
oChat.DoOnlineChat();
}
diff --git a/src/PlayerSelect.cpp b/src/PlayerSelect.cpp
index e40a96c..e581216 100644
--- a/src/PlayerSelect.cpp
+++ b/src/PlayerSelect.cpp
@@ -1,743 +1,768 @@
/***************************************************************************
PlayerSelect.cpp - description
-------------------
begin : Sun Dec 8 2002
copyright : (C) 2002 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#include <stdio.h>
#include "PlayerSelect.h"
+#include "PlayerSelectController.h"
#include "SDL.h"
#include "SDL_video.h"
#include "SDL_image.h"
#include "sge_primitives.h"
#include "sge_surface.h"
#include "common.h"
#include "Audio.h"
#include "sge_bm_text.h"
#include "gfx.h"
#include "RlePack.h"
#include "Backend.h"
#include "State.h"
#include "MortalNetwork.h"
#include "Chooser.h"
#include "sge_tt_text.h"
#include "TextArea.h"
#include "Event.h"
#ifndef NULL
#define NULL 0
#endif
/***************************************************************************
PUBLIC EXPORTED VARIABLES
***************************************************************************/
PlayerSelect g_oPlayerSelect;
-/***************************************************************************
- PRIVATE VARIABLES (perl variable space)
-***************************************************************************/
-
-
-FighterEnum ChooserCells[5][4] = {
- { ZOLI, UPI, CUMI, SIRPI },
- { ULMAR, MACI, GRIZLI, DESCANT },
- { DANI, AMBRUS, BENCE, SURBA },
- { (FighterEnum)100, (FighterEnum)101, (FighterEnum)102, (FighterEnum)103 },
- { (FighterEnum)104, (FighterEnum)105, KINGA, MISI }
-};
-
-FighterEnum ChooserCellsChat[4][5] = {
- { ZOLI, UPI, CUMI, SIRPI, ULMAR },
- { MACI, BENCE, GRIZLI, DESCANT, SURBA },
- { DANI, AMBRUS, (FighterEnum)100, (FighterEnum)102, KINGA },
- { (FighterEnum)104, (FighterEnum)105, (FighterEnum)103, (FighterEnum)101, MISI },
-};
-int GetBackgroundNumber(); // defined in Game.cpp
-
-
PlayerSelect::PlayerSelect()
{
- for ( int i=0; i<2; ++i )
+ for ( int i=0; i<MAXPLAYERS; ++i )
{
m_aoPlayers[i].m_enFighter = UNKNOWN;
m_aoPlayers[i].m_enTint = NO_TINT;
m_aoPlayers[i].m_poPack = NULL;
}
-
- m_iP1 = 0;
- m_iP2 = 3;
}
+
+/*************************************************************************
+ GENERAL PLAYER INFO
+*************************************************************************/
+
const PlayerInfo& PlayerSelect::GetPlayerInfo( int a_iPlayer )
{
- return m_aoPlayers[ a_iPlayer ? 1 : 0 ];
+ return m_aoPlayers[ a_iPlayer ];
+}
+
+
+PlayerInfo& PlayerSelect::EditPlayerInfo( int a_iPlayer )
+{
+ return m_aoPlayers[ a_iPlayer ];
}
const char* PlayerSelect::GetFighterName( int a_iPlayer )
{
- return m_aoPlayers[ a_iPlayer ? 1 : 0 ].m_sFighterName.c_str();
+ return m_aoPlayers[ a_iPlayer ].m_sFighterName.c_str();
}
int PlayerSelect::GetFighterNameWidth( int a_iPlayer )
{
- return m_aiFighterNameWidth[ a_iPlayer ? 1 : 0 ];
+ return m_aiFighterNameWidth[ a_iPlayer ];
}
bool PlayerSelect::IsFighterAvailable( FighterEnum a_enFighter )
{
if ( a_enFighter <= UNKNOWN )
{
return false;
}
bool bLocalAvailable = IsLocalFighterAvailable( a_enFighter );
- if ( !IsNetworkGame() || !bLocalAvailable )
+ if ( SState::IN_NETWORK != g_oState.m_enGameMode || !bLocalAvailable )
{
return bLocalAvailable;
}
// Check the remote site
return g_poNetwork->IsRemoteFighterAvailable( a_enFighter );
}
bool PlayerSelect::IsLocalFighterAvailable( FighterEnum a_enFighter )
{
if ( a_enFighter <= UNKNOWN )
{
return false;
}
g_oBackend.PerlEvalF("GetFighterStats(%d);", a_enFighter);
const char* pcDatafile = g_oBackend.GetPerlString("Datafile");
return pcDatafile && *pcDatafile;
}
+bool PlayerSelect::IsFighterInTeam( FighterEnum a_enFighter )
+{
+ std::vector<FighterEnum>::const_iterator it;
+ for ( int i=0; i<g_oState.m_iNumPlayers; ++i )
+ {
+ std::vector<FighterEnum>& roTeam = m_aoPlayers[i].m_aenTeam;
+ for ( it=roTeam.begin(); it!=roTeam.end(); ++it )
+ {
+ if ( a_enFighter == *it )
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+
/** LoadFighter simply looks up the filename associated with the given
fighter, loads it, and returns the RlePack.
\return The freshly loaded RlePack, or NULL if it could not be loaded.
*/
RlePack* PlayerSelect::LoadFighter( FighterEnum m_enFighter ) // static
{
char a_pcFilename[FILENAME_MAX+1];
const char* s;
g_oBackend.PerlEvalF( "GetFighterStats(%d);", m_enFighter );
s = g_oBackend.GetPerlString( "Datafile" );
strcpy( a_pcFilename, DATADIR );
strcat( a_pcFilename, "/characters/" );
strcat( a_pcFilename, s );
RlePack* pack = new RlePack( a_pcFilename, COLORSPERPLAYER );
if ( pack->Count() <= 0 )
{
debug( "Couldn't load RlePack: '%s'\n", a_pcFilename );
delete pack;
return NULL;
}
return pack;
}
/** SetPlayer loads the given fighter for the given player.
The RlePack is loaded first. If that succeeds, then the perl backend is
set too. The tint and palette of both players are set. */
void PlayerSelect::SetPlayer( int a_iPlayer, FighterEnum a_enFighter )
{
- if ( a_iPlayer ) a_iPlayer = 1; // It's 0 or 1.
-
if ( m_aoPlayers[a_iPlayer].m_enFighter == a_enFighter )
{
if ( m_aoPlayers[a_iPlayer].m_poPack )
{
m_aoPlayers[a_iPlayer].m_poPack->ApplyPalette();
}
return;
}
if ( !IsFighterAvailable( a_enFighter ) )
{
return;
}
- int iOffset = a_iPlayer ? COLOROFFSETPLAYER2 : COLOROFFSETPLAYER1;
+ int iOffset = COLOROFFSETPLAYER1 + a_iPlayer*64;
RlePack* poPack = LoadFighter( a_enFighter );
poPack->OffsetSprites( iOffset );
if ( NULL == poPack )
{
debug( "SetPlayer(%d,%d): Couldn't load RlePack\n", a_iPlayer, a_enFighter );
return;
}
delete m_aoPlayers[a_iPlayer].m_poPack;
m_aoPlayers[a_iPlayer].m_poPack = poPack;
m_aoPlayers[a_iPlayer].m_enFighter = a_enFighter;
g_oBackend.PerlEvalF( "SetPlayerNumber(%d,%d);", a_iPlayer, a_enFighter );
m_aoPlayers[a_iPlayer].m_sFighterName = g_oBackend.GetPerlString( "PlayerName" );
m_aiFighterNameWidth[a_iPlayer] = sge_BF_TextSize( fastFont, GetFighterName(a_iPlayer) ).w;
TintEnum enTint = NO_TINT;
+ //@ CLASHES WITH OTHER PLAYERS NEED TO BE HANDLED
if ( m_aoPlayers[0].m_enFighter == m_aoPlayers[1].m_enFighter )
{
enTint = TintEnum( (rand() % 4) + 1 );
}
SetTint( 1, enTint );
m_aoPlayers[a_iPlayer].m_poPack->ApplyPalette();
}
void PlayerSelect::SetTint( int a_iPlayer, TintEnum a_enTint )
{
m_aoPlayers[a_iPlayer].m_enTint = a_enTint;
if ( m_aoPlayers[a_iPlayer].m_poPack )
{
m_aoPlayers[a_iPlayer].m_poPack->SetTint( a_enTint );
m_aoPlayers[a_iPlayer].m_poPack->ApplyPalette();
}
}
+/*************************************************************************
+ METHODS RELATED TO THE FIGHTER SELECTION PROCESS
+*************************************************************************/
+
+/*
bool PlayerSelect::IsNetworkGame()
{
return SState::IN_NETWORK == g_oState.m_enGameMode;
}
FighterEnum PlayerSelect::GetFighterCell( int a_iIndex )
{
if ( IsNetworkGame() )
{
return ChooserCellsChat[a_iIndex/m_iChooserCols][a_iIndex%m_iChooserCols];
}
else
{
return ChooserCells[a_iIndex/m_iChooserCols][a_iIndex%m_iChooserCols];
}
}
void PlayerSelect::HandleKey( int a_iPlayer, int a_iKey )
{
// If we are in network mode, all keys count as the local player's...
if ( IsNetworkGame() )
{
a_iPlayer = g_poNetwork->IsMaster() ? 0 : 1;
}
int& riP = a_iPlayer ? m_iP2 : m_iP1;
int iOldP = riP;
bool& rbDone = a_iPlayer ? m_bDone2 : m_bDone1;
if ( rbDone )
{
return;
}
switch ( a_iKey )
{
case 0: // up
if ( riP >= m_iChooserCols ) riP -= m_iChooserCols;
break;
case 1: // down
if ( (riP/m_iChooserCols) < (m_iChooserRows-1) ) riP += m_iChooserCols;
break;
case 2: // left
if ( (riP % m_iChooserCols) > 0 ) riP--;
break;
case 3: // right
if ( (riP % m_iChooserCols) < (m_iChooserCols-1) ) riP++;
break;
default:
if ( IsFighterAvailable( GetFighterCell(riP) ) )
{
Audio->PlaySample("magic.voc");
rbDone = true;
g_oBackend.PerlEvalF( "PlayerSelected(%d);", a_iPlayer );
if ( IsNetworkGame() )
{
g_poNetwork->SendFighter( GetFighterCell(riP) );
g_poNetwork->SendReady();
}
return;
}
}
if ( iOldP != riP )
{
Audio->PlaySample("strange_quack.voc");
if ( IsFighterAvailable( GetFighterCell(riP) ) )
{
if ( IsNetworkGame() )
{
g_poNetwork->SendFighter( GetFighterCell(riP) );
}
SetPlayer( a_iPlayer, GetFighterCell(riP) );
}
}
}
void PlayerSelect::HandleNetwork()
{
g_poNetwork->Update();
bool bUpdateText = false;
while ( g_poNetwork->IsMsgAvailable() )
{
const char* pcMsg = g_poNetwork->GetMsg();
int iColor = C_YELLOW;
if ( pcMsg[0] == '*' && pcMsg[1] == '*' && pcMsg[2] == '*' ) iColor = C_WHITE;
m_poTextArea->AddString( pcMsg, iColor );
bUpdateText = true;
}
if ( bUpdateText )
{
Audio->PlaySample("pop.wav");
m_poTextArea->Redraw();
}
bool bMaster = g_poNetwork->IsMaster();
int iPlayer = bMaster ? 1 : 0;
int& riP = bMaster ? m_iP2 : m_iP1;
bool& rbDone = bMaster ? m_bDone2 : m_bDone1;
if ( rbDone )
{
return;
}
int iOldP = riP;
FighterEnum enOldFighter = GetFighterCell(iOldP);
FighterEnum enRemoteFighter = g_poNetwork->GetRemoteFighter();
if ( enOldFighter != enRemoteFighter
&& enRemoteFighter != UNKNOWN )
{
Audio->PlaySample("strange_quack.voc");
SetPlayer( iPlayer, enRemoteFighter );
int i, j;
for ( i=0; i<m_iChooserRows; ++i )
{
for ( int j=0; j<m_iChooserCols; ++j )
{
if ( ChooserCellsChat[i][j] == enRemoteFighter )
{
riP = i * m_iChooserCols + j;
break;
}
}
}
}
bool bDone = g_poNetwork->IsRemoteSideReady();
if ( bDone )
{
rbDone = true;
Audio->PlaySample("magic.voc");
g_oBackend.PerlEvalF( "PlayerSelected(%d);", iPlayer );
}
}
void PlayerSelect::DrawRect( int a_iPos, int a_iColor )
{
int iRow = a_iPos / m_iChooserCols;
int iCol = a_iPos % m_iChooserCols;
SDL_Rect r, r1;
r.x = m_iChooserLeft + iCol * m_iChooserWidth;
r.y = m_iChooserTop + iRow * m_iChooserHeight;
r.w = m_iChooserWidth + 5;
r.h = 5;
r1 = r;
SDL_FillRect( gamescreen, &r1, a_iColor );
r.y += m_iChooserHeight;
r1 = r;
SDL_FillRect( gamescreen, &r1, a_iColor );
r.y -= m_iChooserHeight;
r.w = 5;
r.h = m_iChooserHeight + 5;
r1 = r;
SDL_FillRect( gamescreen, &r1, a_iColor );
r.x += m_iChooserWidth;
r1 = r;
SDL_FillRect( gamescreen, &r1, a_iColor );
}
void PlayerSelect::CheckPlayer( SDL_Surface* a_poBackground, int a_iRow, int a_iCol, int a_iColor )
{
int x1, y1;
x1 = m_iChooserLeft + a_iCol * m_iChooserWidth +5;
y1 = m_iChooserTop + a_iRow * m_iChooserHeight +5;
sge_Line(a_poBackground, x1+5, y1+5, x1 + m_iChooserWidth-10, y1 + m_iChooserHeight-10, a_iColor);
sge_Line(a_poBackground, x1 + m_iChooserWidth-10, y1+5, x1+5, y1 + m_iChooserHeight-10, a_iColor);
x1++;
sge_Line(a_poBackground, x1+5, y1+5, x1 + m_iChooserWidth-10, y1 + m_iChooserHeight-10, a_iColor);
sge_Line(a_poBackground, x1 + m_iChooserWidth-10, y1+5, x1+5, y1 + m_iChooserHeight-10, a_iColor);
y1++;
sge_Line(a_poBackground, x1+5, y1+5, x1 + m_iChooserWidth-10, y1 + m_iChooserHeight-10, a_iColor);
sge_Line(a_poBackground, x1 + m_iChooserWidth-10, y1+5, x1+5, y1 + m_iChooserHeight-10, a_iColor);
x1--;
sge_Line(a_poBackground, x1+5, y1+5, x1 + m_iChooserWidth-10, y1 + m_iChooserHeight-10, a_iColor);
sge_Line(a_poBackground, x1 + m_iChooserWidth-10, y1+5, x1+5, y1 + m_iChooserHeight-10, a_iColor);
}
+*/
+
+#if 0
+
void PlayerSelect::DoPlayerSelect()
{
// 1. Set up: Load background, mark unavailable fighters
bool bNetworkMode = IsNetworkGame();
if ( bNetworkMode )
{
m_iChooserLeft = 158;
m_iChooserTop = 26;
m_iChooserHeight = 64;
m_iChooserWidth = 64;
m_iChooserRows = 4;
m_iChooserCols = 5;
}
else
{
m_iChooserLeft = 158;
m_iChooserTop = 74;
m_iChooserHeight = 80;
m_iChooserWidth = 80;
m_iChooserRows = 5;
m_iChooserCols = 4;
}
SDL_FillRect( gamescreen, NULL, C_BLACK );
SDL_Flip( gamescreen );
SDL_Surface* poBackground = LoadBackground( bNetworkMode ? "PlayerSelect_chat.png" : "PlayerSelect.png", 111 );
if ( !bNetworkMode )
{
DrawGradientText( "Choose A Fighter Dammit", titleFont, 10, poBackground );
}
//g_oChooser.Draw( poBackground );
int i, j;
for ( i=0; i<m_iChooserRows; ++i )
{
for ( int j=0; j<m_iChooserCols; ++j )
{
if ( bNetworkMode )
{
if ( !IsLocalFighterAvailable(ChooserCellsChat[i][j]) &&
UNKNOWN != ChooserCellsChat[i][j] )
{
CheckPlayer( poBackground, i, j, C_LIGHTRED );
}
else if ( !IsFighterAvailable(ChooserCellsChat[i][j]) &&
UNKNOWN != ChooserCellsChat[i][j] )
{
CheckPlayer( poBackground, i, j, C_LIGHTBLUE );
}
}
else
{
if ( !IsFighterAvailable(ChooserCells[i][j]) &&
UNKNOWN != ChooserCells[i][j] )
{
CheckPlayer( poBackground, i, j, C_LIGHTRED );
}
}
}
}
for ( i=0; i<2; ++i )
{
if ( m_aoPlayers[i].m_poPack ) m_aoPlayers[i].m_poPack->ApplyPalette();
}
SetPlayer( 0, GetFighterCell(m_iP1) );
SetPlayer( 1, GetFighterCell(m_iP2) );
if ( bNetworkMode && g_poNetwork->IsMaster() )
{
g_poNetwork->SendGameParams( g_oState.m_iGameSpeed, g_oState.m_iGameTime, g_oState.m_iHitPoints, GetBackgroundNumber() );
}
// 2. Run selection screen
- g_oBackend.PerlEvalF( "SelectStart();" );
+ g_oBackend.PerlEvalF( "SelectStart(%d);", g_oState.m_iNumPlayers );
m_bDone1 = m_bDone2 = false;
int thisTick, lastTick, gameSpeed;
gameSpeed = 12 ;
thisTick = SDL_GetTicks() / gameSpeed;
lastTick = thisTick - 1;
i = 0;
int over = 0;
int iCourtain = 0;
int iCourtainSpeed = 0;
int iCourtainTime = 80;
SDL_Event event;
// Chat is 165:318 - 470:470
char acMsg[256];
sprintf( acMsg, "Press Enter to chat, Page Up/Page Down to scroll..." );
bool bDoingChat = false;
if ( bNetworkMode )
{
m_poReadline = new CReadline( IsNetworkGame() ? poBackground : NULL, chatFont,
acMsg, strlen(acMsg), 256, 15, 465, 610, C_LIGHTCYAN, C_BLACK, 255 );
m_poTextArea = new CTextArea( poBackground, chatFont, 15, 313, 610, 32*4 );
}
else
{
m_poReadline = NULL;
m_poTextArea = NULL;
}
while (1)
{
// 1. Wait for the next tick (on extremely fast machines..)
while (1)
{
thisTick = SDL_GetTicks() / gameSpeed;
if ( thisTick==lastTick )
{
SDL_Delay(1);
}
else
{
break;
}
}
// 2. Advance as many ticks as necessary..
if ( iCourtainTime > 0 )
{
int iAdvance = thisTick - lastTick;
if ( iAdvance > 5 ) iAdvance = 5;
if ( iCourtain + iCourtainSpeed * iCourtainTime /2 < 320 * 4 )
iCourtainSpeed += iAdvance;
else
iCourtainSpeed -= iAdvance;
iCourtain += iCourtainSpeed * iAdvance;
iCourtainTime -= iAdvance;
if ( iCourtainTime > 0 )
{
SDL_Rect oRect;
oRect.x = 320 - iCourtain/4; oRect.y = 0;
oRect.w = iCourtain / 2; oRect.h = gamescreen->h;
if ( oRect.x < 0 ) oRect.x = 0;
if ( oRect.w > gamescreen->w ) oRect.w = gamescreen->w;
SDL_SetClipRect( gamescreen, &oRect );
}
else
{
SDL_SetClipRect( gamescreen, NULL );
}
}
int iNumFrames = thisTick - lastTick;
if ( iNumFrames>5 ) iNumFrames = 5;
for ( i=0; i<iNumFrames; ++i )
{
g_oBackend.AdvancePerl();
}
lastTick = thisTick;
while (SDL_PollEvent(&event))
{
if ( SDL_QUIT == event.type )
{
g_oState.m_bQuitFlag = true;
break;
}
// HANDLE SCROLLING THE TEXT AREA
if ( event.type == SDL_KEYDOWN && IsNetworkGame() )
{
SDLKey enKey = event.key.keysym.sym;
if ( enKey == SDLK_PAGEUP || enKey == SDLK_KP9 )
{
m_poTextArea->ScrollUp();
continue;
}
if ( enKey == SDLK_PAGEDOWN || enKey == SDLK_KP3 )
{
m_poTextArea->ScrollDown();
continue;
}
+
}
// HANDLE CHATTING
if ( bDoingChat && SDL_KEYDOWN==event.type )
{
// The chat thingy will handle this event.
m_poReadline->HandleKeyEvent( event );
int iResult = m_poReadline->GetResult();
if ( iResult < 0 )
{
// Escape was pressed?
m_poReadline->Clear();
bDoingChat = false;
}
if ( iResult > 0 )
{
if ( strlen( acMsg ) )
{
g_poNetwork->SendMsg( acMsg );
std::string sMsg = std::string("<") + g_oState.m_acNick + "> " + acMsg;
m_poTextArea->AddString( sMsg.c_str(), C_LIGHTCYAN );
m_poTextArea->Redraw();
m_poReadline->Clear();
acMsg[0] = 0;
m_poReadline->Restart( acMsg, strlen(acMsg), 256, C_LIGHTCYAN, C_BLACK, 255 );
}
else
{
m_poReadline->Clear();
bDoingChat = false;
}
}
continue;
}
// HANDLE OTHER TYPES OF EVENTS
if ( IsNetworkGame() && bDoingChat == false &&
SDL_KEYDOWN == event.type &&
(event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym==SDLK_KP_ENTER) )
{
bDoingChat = true;
acMsg[0] = 0;
m_poReadline->Clear();
m_poReadline->Restart( acMsg, strlen(acMsg), 256, C_LIGHTCYAN, C_BLACK, 255 );
break;
}
SMortalEvent oEvent;
TranslateEvent( &event, &oEvent );
switch ( oEvent.m_enType )
{
case Me_QUIT:
g_oState.m_bQuitFlag = true;
break;
case Me_MENU:
DoMenu();
if ( IsNetworkGame() && g_poNetwork->IsMaster() )
{
g_poNetwork->SendGameParams( g_oState.m_iGameSpeed, g_oState.m_iGameTime, g_oState.m_iHitPoints, GetBackgroundNumber() );
}
break;
case Me_PLAYERKEYDOWN:
- DrawRect( m_iP1, 240 );
- DrawRect( m_iP2, 240 );
+ DrawRect( m_iP1, C_BLACK );
+ DrawRect( m_iP2, C_BLACK );
HandleKey( oEvent.m_iPlayer, oEvent.m_iKey );
break;
case Me_NOTHING:
case Me_SKIP:
case Me_PLAYERKEYUP:
break;
} // end of switch statement
} // Polling events
if ( IsNetworkGame() )
{
HandleNetwork();
}
g_oBackend.ReadFromPerl();
over = g_oBackend.m_iGameOver;
SDL_BlitSurface( poBackground, NULL, gamescreen, NULL );
- if ( !m_bDone1) DrawRect( m_iP1, 250 );
- if ( !m_bDone2) DrawRect( m_iP2, 253 );
+ if ( !m_bDone1) DrawRect( m_iP1, C_LIGHTGREEN );
+ if ( !m_bDone2) DrawRect( m_iP2, C_LIGHTMAGENTA );
for ( i=0; i<2; ++i )
{
int iYOffset = bNetworkMode ? -130 : 0;
if ( g_oBackend.m_aoPlayers[i].m_iFrame )
{
m_aoPlayers[i].m_poPack->Draw(
ABS(g_oBackend.m_aoPlayers[i].m_iFrame)-1,
g_oBackend.m_aoPlayers[i].m_iX, g_oBackend.m_aoPlayers[i].m_iY + iYOffset,
g_oBackend.m_aoPlayers[i].m_iFrame < 0 );
}
int x = ( m_iChooserLeft - m_aiFighterNameWidth[i] ) / 2;
if ( x<10 ) x = 10;
if ( i ) x = gamescreen->w - x - m_aiFighterNameWidth[i];
sge_BF_textout( gamescreen, fastFont, GetFighterName(i),
x, gamescreen->h - 30 + iYOffset - (bNetworkMode ? 40 : 0) );
}
SDL_Flip( gamescreen );
if (over || g_oState.m_bQuitFlag || SState::IN_DEMO == g_oState.m_enGameMode) break;
}
delete m_poReadline;
delete m_poTextArea;
SDL_FreeSurface( poBackground );
SDL_SetClipRect( gamescreen, NULL );
return;
}
+#else
+
+void PlayerSelect::DoPlayerSelect()
+{
+ CPlayerSelectController oController( SState::IN_NETWORK == g_oState.m_enGameMode );
+ oController.DoPlayerSelect();
+}
+
+#endif
diff --git a/src/PlayerSelect.h b/src/PlayerSelect.h
index e74e8fd..22459ff 100644
--- a/src/PlayerSelect.h
+++ b/src/PlayerSelect.h
@@ -1,97 +1,93 @@
/***************************************************************************
PlayerSelect.h - description
-------------------
begin : 2003-09-05
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#ifndef PLAYERSELECT_H
#define PLAYERSELECT_H
#define COLORSPERPLAYER 64
#define COLOROFFSETPLAYER1 112
#define COLOROFFSETPLAYER2 (COLOROFFSETPLAYER1+64)
#include "FighterEnum.h"
+#include "common.h"
+
#include <string>
+#include <vector>
class RlePack;
struct SDL_Surface;
class CTextArea;
class CReadline;
/** PlayerInfo structure stores information about a PLAYER.
In OpenMortal, the term PLAYER refers to one of the two guys playing the
game, as opposed to FIGHTER, which refers to one of the many playable
characters.
PlayerInfo stores: the player's selected fighter, the fighters tint and
RlePack. */
struct PlayerInfo
{
FighterEnum m_enFighter;
TintEnum m_enTint;
RlePack* m_poPack;
std::string m_sFighterName;
+
+ std::vector<FighterEnum> m_aenTeam;
};
/** This class implements services that allows players to select their
fighters. It also stores info about which fighter is available, and
allows other parts of the program to programmatically assign a fighter
to a player, and set fighter tints (this is used by e.g. the "frozen"
effect.) */
class PlayerSelect
{
public:
PlayerSelect();
const PlayerInfo& GetPlayerInfo( int a_iPlayer );
+ PlayerInfo& EditPlayerInfo( int a_iPlayer );
const char* GetFighterName( int a_iPlayer );
int GetFighterNameWidth( int a_iPlayer );
void DoPlayerSelect();
void SetPlayer( int a_iPlayer, FighterEnum a_enFighter );
void SetTint( int a_iPlayer, TintEnum a_enFighter );
bool IsFighterAvailable( FighterEnum a_enFighter );
bool IsLocalFighterAvailable( FighterEnum a_enFighter );
+ bool IsFighterInTeam( FighterEnum a_enFighter );
+
protected:
- void HandleKey( int a_iPlayer, int a_iKey );
- void HandleNetwork();
- void DrawRect( int a_iPos, int a_iColor );
- void CheckPlayer( SDL_Surface* a_poBackground, int a_iRow, int a_iCol, int a_iColor );
+// void HandleKey( int a_iPlayer, int a_iKey );
+// void HandleNetwork();
+// void DrawRect( int a_iPos, int a_iColor );
+// void CheckPlayer( SDL_Surface* a_poBackground, int a_iRow, int a_iCol, int a_iColor );
static RlePack* LoadFighter( FighterEnum m_enFighter );
- bool IsNetworkGame();
- FighterEnum GetFighterCell( int a_iIndex );
+// bool IsNetworkGame();
+// FighterEnum GetFighterCell( int a_iIndex );
protected:
- PlayerInfo m_aoPlayers[2];
- int m_iP1, m_iP2; // Chooser cells for player 1 and 2
- bool m_bDone1, m_bDone2; // Has player n chosen a player?
- int m_aiFighterNameWidth[2];
-
- int m_iChooserLeft;
- int m_iChooserTop;
- int m_iChooserWidth;
- int m_iChooserHeight;
- int m_iChooserRows;
- int m_iChooserCols;
-
- CTextArea* m_poTextArea;
- CReadline* m_poReadline;
+ PlayerInfo m_aoPlayers[MAXPLAYERS];
+ int m_aiFighterNameWidth[MAXPLAYERS];
};
extern PlayerSelect g_oPlayerSelect;
#endif // PLAYERSELECT_H
diff --git a/src/RlePack.cpp b/src/RlePack.cpp
index d00bebd..ffe2692 100644
--- a/src/RlePack.cpp
+++ b/src/RlePack.cpp
@@ -1,837 +1,665 @@
/***************************************************************************
RlePack.cpp - description
-------------------
begin : Mon Sep 24 2001
copyright : (C) 2001 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#include "RlePack.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "SDL.h"
+#include "gfx.h"
#include "common.h"
/// Sanity: This is the maximal number of entries in a .DAT file.
#define MAXDATACOUNT 65530
inline void ChangeEndian32( Uint32& a_riArg )
{
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
char* pcArg = (char*)&a_riArg;
char cTemp;
cTemp = pcArg[0]; pcArg[0] = pcArg[3]; pcArg[3] = cTemp;
cTemp = pcArg[1]; pcArg[1] = pcArg[2]; pcArg[2] = cTemp;
#endif
}
inline void ChangeEndian16( Uint16& a_riArg )
{
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
char* pcArg = (char*)&a_riArg;
char cTemp;
cTemp = pcArg[0]; pcArg[0] = pcArg[1]; pcArg[1] = cTemp;
#endif
}
inline Uint32 ConvertEndian32( Uint32 a_iArg )
{
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
char* pcArg = (char*)&a_iArg;
char cTemp;
cTemp = pcArg[0]; pcArg[0] = pcArg[3]; pcArg[3] = cTemp;
cTemp = pcArg[1]; pcArg[1] = pcArg[2]; pcArg[2] = cTemp;
#endif
return a_iArg;
}
inline Uint16 ConvertEndian16( Uint16 a_iArg )
{
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
char* pcArg = (char*)&a_iArg;
char cTemp;
cTemp = pcArg[0]; pcArg[0] = pcArg[1]; pcArg[1] = cTemp;
#endif
return a_iArg;
}
typedef struct RLE_SPRITE /* a RLE compressed sprite */
{
Uint16 dummy; // For better alignment... NASTY NASTY HACK!!
Uint16 color_depth; /* color depth of the image */
Uint16 w, h; /* width and height in pixels */
Uint32 size;
signed char dat[0];
} RLE_SPRITE;
struct RlePack_P
{
SDL_Color m_aoPalette[256];
SDL_Color m_aoTintedPalette[256];
TintEnum m_enTint;
int m_iCount;
int m_iArraysize;
RLE_SPRITE** m_pSprites;
void* m_pData;
int m_iColorCount;
int m_iColorOffset;
+ Uint32 m_aiRGBPalette[256];
+
+ void draw_rle_sprite8( RLE_SPRITE* a_poSprite, int a_dx, int a_dy );
+ void draw_rle_sprite_v_flip8( RLE_SPRITE* a_poSprite, int a_dx, int a_dy );
+ void draw_rle_sprite16( RLE_SPRITE* a_poSprite, int a_dx, int a_dy );
+ void draw_rle_sprite_v_flip16( RLE_SPRITE* a_poSprite, int a_dx, int a_dy );
+ void draw_rle_sprite24( RLE_SPRITE* a_poSprite, int a_dx, int a_dy );
+ void draw_rle_sprite_v_flip24( RLE_SPRITE* a_poSprite, int a_dx, int a_dy );
+ void draw_rle_sprite32( RLE_SPRITE* a_poSprite, int a_dx, int a_dy );
+ void draw_rle_sprite_v_flip32( RLE_SPRITE* a_poSprite, int a_dx, int a_dy );
};
RlePack::RlePack( const char* a_pcFilename, int a_iNumColors )
{
p = new RlePack_P;
p->m_enTint = NO_TINT;
p->m_iCount = 0;
p->m_iArraysize = 0;
p->m_pSprites = NULL;
p->m_pData = NULL;
p->m_iColorCount = 0;
p->m_iColorOffset = 0;
// Load file and stuff
LoadFile( a_pcFilename, a_iNumColors );
}
RlePack::~RlePack()
{
if (!p)
return;
if (p->m_pSprites)
{
delete[] p->m_pSprites;
p->m_pSprites = NULL;
}
free( p->m_pData );
delete( p );
p = NULL;
}
void RlePack::Clear()
{
if ( p && p->m_pSprites )
{
delete[] p->m_pSprites;
p->m_pSprites = NULL;
}
}
int RlePack::LoadFile( const char* a_pcFilename, int a_iNumColors )
{
FILE* f;
f = fopen( a_pcFilename, "rb" );
if (f==NULL)
{
debug( "Can't open file '%s'.\n", a_pcFilename );
return -1;
}
fseek( f, 0, SEEK_END );
long iFileSize = ftell ( f );
p->m_pData = malloc( iFileSize );
if ( NULL == p->m_pData )
{
fclose( f );
return -1;
}
fseek( f, 0, SEEK_SET );
int iRead = fread( p->m_pData, 1, iFileSize, f );
fclose( f );
p->m_iColorCount = a_iNumColors;
if ( iFileSize != iRead )
{
debug( "Warning RlePack(): iFileSize=%d, iRead=%d\n", iFileSize, iRead );
}
struct SHeader
{
char acDummy[8];
Uint32 iDatacount;
} *poHeader = (SHeader*) p->m_pData;
ChangeEndian32( poHeader->iDatacount );
debug( "File '%s' contains %d entries.\n", a_pcFilename, poHeader->iDatacount );
if (poHeader->iDatacount>MAXDATACOUNT) poHeader->iDatacount = MAXDATACOUNT; // Sanity
p->m_iArraysize = poHeader->iDatacount;
p->m_pSprites = new RLE_SPRITE*[ poHeader->iDatacount ];
char* pcNext = ((char*)p->m_pData) + sizeof(SHeader);
char* pcEnd = ((char*)p->m_pData) + iFileSize;
while ( pcNext < pcEnd - 4 )
{
if ( 0 == strncmp( pcNext, "prop", 4 ) )
{
struct SProperty
{
char acName[4];
Uint32 iSize;
} *poProperty = (SProperty*) (pcNext+4);
ChangeEndian32( poProperty->iSize );
pcNext += 4 + sizeof(SProperty) + poProperty->iSize;
}
else if ( 0 == strncmp( pcNext, "RLE ", 4 ) )
{
struct SRLE
{
RLE_SPRITE oSprite;
} *poRle = (SRLE*) (pcNext+10);
poRle->oSprite.color_depth = ConvertEndian16(poRle->oSprite.color_depth);
poRle->oSprite.w = ConvertEndian16(poRle->oSprite.w);
poRle->oSprite.h = ConvertEndian16(poRle->oSprite.h);
poRle->oSprite.size = ConvertEndian32(poRle->oSprite.size);
p->m_pSprites[p->m_iCount] = &(poRle->oSprite);
p->m_iCount++;
pcNext += 10 + sizeof( SRLE ) + poRle->oSprite.size;
}
else if ( 0 == strncmp( pcNext, "PAL ", 4 ) )
{
struct SPAL
{
Uint32 iLength1;
Uint32 iLength;
SDL_Color aoColors[256];
} *poPal = (SPAL*) (pcNext+4);
ChangeEndian32( poPal->iLength );
int iNumColors = poPal->iLength>1024 ? 1024 : poPal->iLength;
iNumColors /= 4;
for (int i=0; i< iNumColors; i++)
{
p->m_aoPalette[i].r = poPal->aoColors[i].r*4;
p->m_aoPalette[i].g = poPal->aoColors[i].g*4;
p->m_aoPalette[i].b = poPal->aoColors[i].b*4;
p->m_aoPalette[i].unused = 0;
p->m_aoTintedPalette[i] = p->m_aoPalette[i];
}
pcNext += 4 + 8 + poPal->iLength;
}
else
{
struct SUnknown
{
Uint32 iSize;
} *poUnknown = (SUnknown*) (pcNext+4);
ChangeEndian32( poUnknown->iSize );
debug( "Unknown: '%4s', size: %d\n", pcNext, poUnknown->iSize );
pcNext += 4 + sizeof(SUnknown) + poUnknown->iSize;
}
}
return p->m_iCount;
#if 0
int datacount;
#define READDW(I) { \
unsigned char data[4]; \
fread( data, 4, 1, f ); \
(I) = (data[0]<<24) + (data[1]<<16) + (data[2]<<8) + data[3]; }
#define READW(I) { \
unsigned char data[2]; \
fread( data, 2, 1, f ); \
(I) = (data[0]<<8) + data[1]; }
#define READCH(S,C) { \
fread( S, C, 1, f ); S[C] = 0; }
fseek( f, 8, SEEK_SET ); // Skip header
READDW( datacount );
debug( "File '%s' contains %d entries.\n", filename, datacount );
if (datacount>500) datacount = 500; // Sanity
p->arraysize = datacount;
p->sprites = new RLE_SPRITE*[ datacount ];
while( (!feof(f)) && (!ferror(f)) && (datacount>0) )
{
char s[10];
READCH( s, 4 );
if ( !strcmp( s, "prop" )) // Found a property
{
fseek( f, 4, SEEK_CUR );
unsigned int propsize;
READDW( propsize );
fseek( f, propsize, SEEK_CUR );
}
else if (!strcmp( s, "RLE " )) // Found an RLE_SPRITE
{
datacount--;
unsigned int length, bpp, width, height, size;
READDW( length );
READDW( length );
READW( bpp );
READW( width );
READW( height );
READDW( size );
RLE_SPRITE* sprite = (RLE_SPRITE*) malloc( sizeof(RLE_SPRITE) + size );
p->sprites[ p->count ] = sprite;
(p->count)++;
sprite->w = width;
sprite->h = height;
sprite->color_depth = bpp;
sprite->size = size;
fread( sprite->dat, 1, size, f );
}
else if (!strcmp( s, "PAL ")) // Found a palette
{
datacount--;
unsigned int length, pallength;
READDW( length );
READDW( length );
pallength = length>1024 ? 1024 : length;
pallength /= 4;
for (unsigned int i=0; i< pallength; i++)
{
char c[4];
fread( c, 4, 1, f );
p->palette[i].r = c[0]*4;
p->palette[i].g = c[1]*4;
p->palette[i].b = c[2]*4;
p->palette[i].unused = 0;
}
fseek( f, length - pallength*4, SEEK_CUR );
}
else // Found something else
{
debug( "Unknown: %s.", s );
datacount--;
unsigned int length;
READDW( length );
READDW( length );
fseek( f, length, SEEK_CUR );
}
}
fclose( f );
#endif
}
int RlePack::Count()
{
return p->m_iCount;
}
+/** Worker method of RlePack::OffsetSprites() .*/
+
void OffsetRLESprite( RLE_SPRITE* spr, int offset ) // Static method
{
if (!spr || !offset) return;
signed char *s = spr->dat;
signed char c;
int y;
for (y=0; y<spr->h; y++)
{
c = *s++;
while (c)
{
// For positive c: solid pixels.
for ( ; c>0; c-- )
{
*s = (*s) + offset;
s++;
}
c = *s++;
}
}
}
+
+/** Offsets the sprites "logical" palette values by the given offset.
+This is only relevant in 8BPP mode; in other color depths this is a
+no-op.
+
+Explanation: RlePacks have an internal palette which contains up to 256
+colors. These colors are always indexed from 0 up. However, if you load
+two RlePacks with different palettes, the palettes will collide, and one
+RlePack will be displayed with an incorrect palette.
+
+To work around this, you can offset one of the sprites palette. For example,
+you might load an RlePack with 16 colors, and another with 64 colors. You
+can offset the second RlePack by 16 colors; the total effect is that the
+two RlePacks now use 80 colors of the available 256 colors, the first using
+colors 0-15, the second using colors 16-79.
+*/
+
void RlePack::OffsetSprites( int a_iOffset )
{
- if ( (a_iOffset<=0) || (a_iOffset>255) )
+ if ( (a_iOffset<=0) || (a_iOffset>255) || (8!=gamescreen->format->BitsPerPixel) )
return;
p->m_iColorOffset = a_iOffset;
int i;
// Offset every RLE_SPRITE
for ( i=0; i<p->m_iCount; ++i )
{
OffsetRLESprite( p->m_pSprites[i], a_iOffset );
}
}
void RlePack::SetTint( TintEnum a_enTint )
{
int i;
switch( a_enTint )
{
case ZOMBIE_TINT:
{
for ( i=0; i<p->m_iColorCount; ++i )
{
p->m_aoTintedPalette[i].r = 0;
p->m_aoTintedPalette[i].g = p->m_aoPalette[i].g;
p->m_aoTintedPalette[i].b = 0;
}
break;
}
case GRAY_TINT:
{
int j;
for ( i=0; i<p->m_iColorCount; ++i )
{
j = (p->m_aoPalette[i].r + p->m_aoPalette[i].g + p->m_aoPalette[i].b)/4;
p->m_aoTintedPalette[i].r = j;
p->m_aoTintedPalette[i].g = j;
p->m_aoTintedPalette[i].b = j;
}
break;
}
case DARK_TINT:
{
for ( i=0; i<p->m_iColorCount; ++i )
{
p->m_aoTintedPalette[i].r = int(p->m_aoPalette[i].r) * 2 / 3;
p->m_aoTintedPalette[i].g = int(p->m_aoPalette[i].g) * 2 / 3;
p->m_aoTintedPalette[i].b = int(p->m_aoPalette[i].b) * 2 / 3;
}
break;
}
case INVERTED_TINT:
{
for ( i=0; i<p->m_iColorCount; ++i )
{
p->m_aoTintedPalette[i].r = 255 - p->m_aoPalette[i].r;
p->m_aoTintedPalette[i].g = 255 - p->m_aoPalette[i].g;
p->m_aoTintedPalette[i].b = 255 - p->m_aoPalette[i].b;
}
break;
}
case NO_TINT:
default:
{
for ( i=0; i<p->m_iColorCount; ++i )
{
p->m_aoTintedPalette[i] = p->m_aoPalette[i];
}
break;
}
} // end of switch( a_enTint )
}
+/** Loads the palette of the RlePack to the gamescreen.
+This only works in 8BPP mode; in other modes the palette is always considered
+to be loaded, and this is a no-operation.
+*/
+
void RlePack::ApplyPalette()
{
- SDL_SetColors( gamescreen, p->m_aoTintedPalette, p->m_iColorOffset, p->m_iColorCount );
+ if ( 8 == gamescreen->format->BitsPerPixel )
+ {
+ SDL_SetColors( gamescreen, p->m_aoTintedPalette, p->m_iColorOffset, p->m_iColorCount );
+ }
+ else
+ {
+ // Now is the time to compile m_aiRGBPalette
+ for ( int i=0; i<p->m_iColorCount; ++i )
+ {
+ SDL_Color& roColor = p->m_aoTintedPalette[i];
+ p->m_aiRGBPalette[i] = SDL_MapRGB( gamescreen->format, roColor.r, roColor.g, roColor.b );
+ }
+ }
}
+/** Returns the width of a given sprite in pixels.
+
+\param a_iIndex The index of the sprite, 0 <= a_iIndex < Count()
+*/
+
int RlePack::GetWidth( int a_iIndex )
{
if ( (a_iIndex<0) || (a_iIndex>=p->m_iCount) )
return -1;
RLE_SPRITE* poSprite = p->m_pSprites[a_iIndex];
if (!poSprite)
return -1;
return poSprite->w;
}
+/** Returns the height of a given sprite in pixels.
+
+\param a_iIndex The index of the sprite, 0 <= a_iIndex < Count()
+*/
+
int RlePack::GetHeight( int a_iIndex )
{
if ( (a_iIndex<0) || (a_iIndex>=p->m_iCount) )
return -1;
RLE_SPRITE* poSprite = p->m_pSprites[a_iIndex];
if (!poSprite)
return -1;
return poSprite->h;
}
-void draw_rle_sprite_v_flip( RLE_SPRITE* src, int dx, int dy ) // static method
-{
-#define RLE_PTR signed char*
-#define RLE_IS_EOL(c) ((c) == 0)
+#define METHODNAME RlePack_P::draw_rle_sprite8
+#define METHODNAME_FLIP RlePack_P::draw_rle_sprite_v_flip8
#define PIXEL_PTR unsigned char*
-#define OFFSET_PIXEL_PTR(p,x) ((PIXEL_PTR) (p) + (x))
-#define INC_PIXEL_PTR(p) ((p)++)
-#define DEC_PIXEL_PTR(p) ((p)--)
#define PUT_PIXEL(p,c) (*((unsigned char *)(p)) = (c))
-//#define PUT_PIXEL(p,c) bmp_write8((unsigned long) (p), (c))
-
- int x, y, w, h; // width and height of visible area
- int dxbeg, dybeg; // beginning in destination
- int sxbeg, sybeg; // beginning in source
- RLE_PTR s;
-
- SDL_Surface* dst = gamescreen;
- // Clip to dst->clip_rect
- int dst_cl = dst->clip_rect.x;
- int dst_cr = dst->clip_rect.w + dst_cl - 1;
- int dst_ct = dst->clip_rect.y;
- int dst_cb = dst->clip_rect.h + dst_ct;
-
-// if (dst->clip)
- if (1)
- {
-
- int tmp;
-
- dxbeg = dx;
- if ( dst_cl > dx ) dxbeg = dst_cl;
-
- tmp = dx + src->w - dst_cr;
- sxbeg = ((tmp < 0) ? 0 : tmp);
-
- tmp = dx + src->w;
- if (tmp > dst_cr ) tmp = dst_cr;
- w = tmp - dxbeg;
- if (w <= 0)
- return;
-
- tmp = dst_ct - dy;
- sybeg = ((tmp < 0) ? 0 : tmp);
- dybeg = sybeg + dy;
-
- tmp = dst_cb - dy;
- h = ((tmp > src->h) ? src->h : tmp) - sybeg;
- if (h <= 0)
- return;
- }
- else {
- w = src->w;
- h = src->h;
- sxbeg = 0;
- sybeg = 0;
- dxbeg = dx;
- dybeg = dy;
- }
-
- s = (RLE_PTR) (src->dat);
- dxbeg += w;
-
- /* Clip top. */
- for (y = sybeg - 1; y >= 0; y--) {
- long c = *s++;
-
- while (!RLE_IS_EOL(c)) {
- if (c > 0)
- s += c;
- c = *s++;
- }
- }
-
- //@@@ bmp_select(dst);
-
- /* Visible part. */
- for (y = 0; y < h; y++) {
- //@@@ PIXEL_PTR d = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg + y), dxbeg);
- PIXEL_PTR d = (PIXEL_PTR) dst->pixels;
- d += (dybeg+y)*dst->pitch;
- d = OFFSET_PIXEL_PTR( d, dxbeg );
- long c = *s++;
-
- /* Clip left. */
- for (x = sxbeg; x > 0; ) {
- if (RLE_IS_EOL(c))
- goto next_line;
- else if (c > 0) {
- /* Run of solid pixels. */
- if ((x - c) >= 0) {
- /* Fully clipped. */
- x -= c;
- s += c;
- }
- else {
- /* Visible on the right. */
- c -= x;
- s += x;
- break;
- }
- }
- else {
- /* Run of transparent pixels. */
- if ((x + c) >= 0) {
- /* Fully clipped. */
- x += c;
- }
- else {
- /* Visible on the right. */
- c += x;
- break;
- }
- }
-
- c = *s++;
- }
-
- /* Visible part. */
- for (x = w; x > 0; ) {
- if (RLE_IS_EOL(c))
- goto next_line;
- else if (c > 0) {
- /* Run of solid pixels. */
- if ((x - c) >= 0) {
- /* Fully visible. */
- x -= c;
- for (c--; c >= 0; s++, DEC_PIXEL_PTR(d), c--) {
- unsigned long col = *s;
- PUT_PIXEL(d, col);
- }
- }
- else {
- /* Clipped on the right. */
- c -= x;
- for (x--; x >= 0; s++, DEC_PIXEL_PTR(d), x--) {
- unsigned long col = *s;
- PUT_PIXEL(d, col);
- }
- break;
- }
- }
- else {
- /* Run of transparent pixels. */
- x += c;
- d = OFFSET_PIXEL_PTR(d, c);
- }
-
- c = *s++;
- }
-
- /* Clip right. */
- while (!RLE_IS_EOL(c)) {
- if (c > 0)
- s += c;
- c = *s++;
- }
-
- next_line: ;
- }
-
- //@@@bmp_unwrite_line(dst);
-}
+#define PITCH (dst->pitch)
+#include "DrawRle.h"
+#undef METHODNAME
+#undef METHODNAME_FLIP
+#undef PIXEL_PTR
+#undef PUT_PIXEL
+#undef PITCH
+
+#define METHODNAME RlePack_P::draw_rle_sprite16
+#define METHODNAME_FLIP RlePack_P::draw_rle_sprite_v_flip16
+#define PIXEL_PTR Uint16*
+#define PUT_PIXEL(p,c) (*((PIXEL_PTR )(p)) = (m_aiRGBPalette[c]))
+#define PITCH (dst->pitch / 2)
+#include "DrawRle.h"
+
+#undef METHODNAME
+#undef METHODNAME_FLIP
+#undef PIXEL_PTR
+#undef PUT_PIXEL
+#undef PITCH
+
+#define METHODNAME RlePack_P::draw_rle_sprite32
+#define METHODNAME_FLIP RlePack_P::draw_rle_sprite_v_flip32
+#define PIXEL_PTR Uint32*
+#define PUT_PIXEL(p,c) (*((PIXEL_PTR )(p)) = (m_aiRGBPalette[c]))
+#define PITCH (dst->pitch / 4)
+#include "DrawRle.h"
+
+
-void draw_rle_sprite( RLE_SPRITE* src, int dx, int dy ) // static method
-{
-#define RLE_PTR signed char*
-#define RLE_IS_EOL(c) ((c) == 0)
-#define PIXEL_PTR unsigned char*
-#define OFFSET_PIXEL_PTR(p,x) ((PIXEL_PTR) (p) + (x))
-#define INC_PIXEL_PTR(p) ((p)++)
-#define DEC_PIXEL_PTR(p) ((p)--)
-#define PUT_PIXEL(p,c) (*((unsigned char *)(p)) = (c))
-//#define PUT_PIXEL(p,c) bmp_write8((unsigned long) (p), (c))
-
- int x, y, w, h; // width and height of visible area
- int dxbeg, dybeg; // beginning in destination
- int sxbeg, sybeg; // beginning in source
- RLE_PTR s;
-
- SDL_Surface* dst = gamescreen;
- // Clip to dst->clip_rect
- int dst_cl = dst->clip_rect.x;
- int dst_cr = dst->clip_rect.w + dst_cl;
- int dst_ct = dst->clip_rect.y;
- int dst_cb = dst->clip_rect.h + dst_ct;
-
-// if (dst->clip)
- if (1)
- {
-
- int tmp;
-
- tmp = dst_cl - dx;
- sxbeg = ((tmp < 0) ? 0 : tmp);
- dxbeg = sxbeg + dx;
-
- tmp = dst_cr - dx;
- w = ((tmp > src->w) ? src->w : tmp) - sxbeg;
- if ( w<=0 ) return;
-
- tmp = dst_ct - dy;
- sybeg = ((tmp < 0) ? 0 : tmp);
- dybeg = sybeg + dy;
-
- tmp = dst_cb - dy;
- h = ((tmp > src->h) ? src->h : tmp) - sybeg;
- if ( h<=0 ) return;
- }
- else {
- w = src->w;
- h = src->h;
- sxbeg = 0;
- sybeg = 0;
- dxbeg = dx;
- dybeg = dy;
- }
-
- s = (RLE_PTR) (src->dat);
-
- /* Clip top. */
- for (y = sybeg - 1; y >= 0; y--) {
- long c = *s++;
-
- while (!RLE_IS_EOL(c)) {
- if (c > 0)
- s += c;
- c = *s++;
- }
- }
-
- //@@@ bmp_select(dst);
-
- /* Visible part. */
- for (y = 0; y < h; y++) {
- //@@@ PIXEL_PTR d = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg + y), dxbeg);
- PIXEL_PTR d = (PIXEL_PTR) dst->pixels;
- d += (dybeg+y)*dst->pitch;
- d = OFFSET_PIXEL_PTR( d, dxbeg );
- long c = *s++;
-
- /* Clip left. */
- for (x = sxbeg; x > 0; ) {
- if (RLE_IS_EOL(c))
- goto next_line;
- else if (c > 0) {
- /* Run of solid pixels. */
- if ((x - c) >= 0) {
- /* Fully clipped. */
- x -= c;
- s += c;
- }
- else {
- /* Visible on the right. */
- c -= x;
- s += x;
- break;
- }
- }
- else {
- /* Run of transparent pixels. */
- if ((x + c) >= 0) {
- /* Fully clipped. */
- x += c;
- }
- else {
- /* Visible on the right. */
- c += x;
- break;
- }
- }
-
- c = *s++;
- }
-
- /* Visible part. */
- for (x = w; x > 0; ) {
- if (RLE_IS_EOL(c))
- goto next_line;
- else if (c > 0) {
- /* Run of solid pixels. */
- if ((x - c) >= 0) {
- /* Fully visible. */
- x -= c;
- for (c--; c >= 0; s++, INC_PIXEL_PTR(d), c--) {
- unsigned long col = *s;
- PUT_PIXEL(d, col);
- }
- }
- else {
- /* Clipped on the right. */
- c -= x;
- for (x--; x >= 0; s++, INC_PIXEL_PTR(d), x--) {
- unsigned long col = *s;
- PUT_PIXEL(d, col);
- }
- break;
- }
- }
- else {
- /* Run of transparent pixels. */
- x += c;
- d = OFFSET_PIXEL_PTR(d, -c);
- }
-
- c = *s++;
- }
-
- /* Clip right. */
- while (!RLE_IS_EOL(c)) {
- if (c > 0)
- s += c;
- c = *s++;
- }
-
- next_line: ;
- }
-
- //@@@bmp_unwrite_line(dst);
-}
void RlePack::Draw( int a_iIndex, int a_iX, int a_iY, bool a_bFlipped )
{
if ( (a_iIndex<0) || (a_iIndex>=p->m_iCount) )
return;
RLE_SPRITE* poSprite = p->m_pSprites[a_iIndex];
if (!poSprite)
return;
+ CSurfaceLocker oLock;
+
if ( a_bFlipped )
- draw_rle_sprite_v_flip( poSprite, a_iX, a_iY );
+ {
+ switch (gamescreen->format->BitsPerPixel)
+ {
+ case 8:
+ p->draw_rle_sprite_v_flip8( poSprite, a_iX, a_iY ); break;
+ case 15:
+ case 16:
+ p->draw_rle_sprite_v_flip16( poSprite, a_iX, a_iY ); break;
+ case 32:
+ p->draw_rle_sprite_v_flip32( poSprite, a_iX, a_iY ); break;
+ }
+ }
else
- draw_rle_sprite( poSprite, a_iX, a_iY );
-
+ {
+ switch (gamescreen->format->BitsPerPixel)
+ {
+ case 8:
+ p->draw_rle_sprite8( poSprite, a_iX, a_iY ); break;
+ case 15:
+ case 16:
+ p->draw_rle_sprite16( poSprite, a_iX, a_iY ); break;
+ case 32:
+ p->draw_rle_sprite32( poSprite, a_iX, a_iY ); break;
+ }
+ }
}
+
+SDL_Surface* RlePack::CreateSurface( int a_iIndex, bool a_bFlipped )
+{
+ if ( (a_iIndex<0) || (a_iIndex>=p->m_iCount) )
+ return NULL;
+
+ RLE_SPRITE* poSprite = p->m_pSprites[a_iIndex];
+ if (!poSprite)
+ return NULL;
+
+ SDL_Surface* poSurface = SDL_CreateRGBSurface( SDL_SWSURFACE, poSprite->w, poSprite->h, gamescreen->format->BitsPerPixel,
+ gamescreen->format->Rmask, gamescreen->format->Gmask, gamescreen->format->Bmask, gamescreen->format->Amask );
+
+ if ( NULL == poSurface )
+ {
+ return NULL;
+ }
+
+ if ( gamescreen->format->BitsPerPixel <= 8 )
+ {
+ SDL_SetColors( poSurface, gamescreen->format->palette->colors, 0, gamescreen->format->palette->ncolors );
+ }
+
+ SDL_FillRect( poSurface, NULL, 0 );// C_LIGHTGREEN );
+ SDL_SetColorKey( poSurface, SDL_SRCCOLORKEY, 0 ); //C_LIGHTGREEN );
+
+ SDL_Surface* poTemp = gamescreen;
+ gamescreen = poSurface;
+ Draw( a_iIndex, 0, 0, a_bFlipped );
+ gamescreen = poTemp;
+
+ return poSurface;
+}
diff --git a/src/RlePack.h b/src/RlePack.h
index 13a95cf..fb17d4d 100644
--- a/src/RlePack.h
+++ b/src/RlePack.h
@@ -1,59 +1,60 @@
/***************************************************************************
RlePack.h - description
-------------------
begin : Mon Sep 24 2001
copyright : (C) 2001 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#ifndef __RLEPACK_H
#define __RLEPACK_H
#include "FighterEnum.h"
struct RlePack_P;
-
+struct SDL_Surface;
/**
\class RlePack
\brief RlePack is responsible for loading and drawing "sprites" from a .DAT file.
RlePack loads the sprites from a .DAT file in its constructor. If there is
an error (e.g. file doesn't exist), the number of sprites loaded will be 0.
RlePack is usually used to store the many frames of a fighter in MSZ. It is
also used for the 'cast' in the MainScreenDemo.
The palette by default ranges from 1 to N (the number of colors). This,
however, can be changed with OffsetSprites(). This is used to make sure that
the two loaded fighters don't overwrite each others palettes or the background
palette.
The 'tint' can be set with the SetTint method. It will not immediately appear
until ApplyPalette() is called. For an explanation about tints, please see the
TintEnum documentation.
\sa TintEnum
*/
class RlePack
{
public:
RlePack( const char* a_pcFilename, int a_iNumColors );
~RlePack();
void Clear();
int LoadFile( const char* a_pcFilename, int a_iNumColors );
int Count();
void OffsetSprites( int a_iOffset );
void SetTint( TintEnum a_enTint );
void ApplyPalette();
int GetWidth( int a_iIndex );
int GetHeight( int a_iIndex );
void Draw( int a_iIndex, int a_iX, int a_iY, bool a_bFlipped=false );
+ SDL_Surface* CreateSurface( int a_iIndex, bool a_bFlipped=false );
private:
RlePack_P* p;
};
#endif
diff --git a/src/State.cpp b/src/State.cpp
index 0b5e771..a855344 100644
--- a/src/State.cpp
+++ b/src/State.cpp
@@ -1,310 +1,340 @@
/***************************************************************************
State.cpp - description
-------------------
begin : Mon Aug 12 2003
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#include "../config.h"
-#include "SDL.h"
+#include "gfx.h"
#include "common.h"
#include "State.h"
+#include "SDL_keysym.h"
+#include "SDL_mixer.h"
+
#include <string>
#include <fstream>
//include <unistd.h>
#include "Backend.h"
#include "MszPerl.h"
extern PerlInterpreter* my_perl;
SState g_oState;
std::string GetConfigHeader()
{
std::string sHeader( "Simple config file " );
sHeader += PACKAGE " " VERSION;
return sHeader;
}
std::string GetConfigFilename()
{
#ifdef _WINDOWS
if ( NULL != g_oState.m_pcArgv0 )
{
return std::string(g_oState.m_pcArgv0) + ".ini";
}
return "c:\\openmortal.ini";
#else
return std::string(getenv("HOME")) + "/.openmortalrc";
#endif
}
SState::SState()
{
// 1. SET THE TRIVIAL DEFAULTS
m_enGameMode = IN_DEMO;
m_bQuitFlag = false;
m_pcArgv0 = NULL;
+ m_iNumPlayers = 2;
+ m_enTeamMode = Team_ONE_VS_ONE;
+ m_iTeamSize = 5;
+ m_bTeamMultiselect = false;
+
m_iGameTime = 60;
m_iHitPoints = 100;
m_iGameSpeed = 12;
#ifdef _WINDOWS
#ifdef _DEBUG
m_bFullscreen = false;
#else
m_bFullscreen = true;
#endif
#else
m_bFullscreen = false;
#endif
m_iChannels = 2;
m_iMixingRate = MIX_DEFAULT_FREQUENCY;
m_iMixingBits = 2;
m_iMusicVolume = 50;
m_iSoundVolume = 100;
- static const int aiDefaultKeys[2][9] = {
+ static const int aiDefaultKeys[MAXPLAYERS][9] = {
{ SDLK_UP, SDLK_DOWN, SDLK_LEFT, SDLK_RIGHT, SDLK_PAGEDOWN,
SDLK_DELETE, SDLK_INSERT, SDLK_END, SDLK_HOME },
{ SDLK_w, SDLK_s, SDLK_a, SDLK_d, SDLK_x,
- SDLK_f, SDLK_r, SDLK_g, SDLK_t }
+ SDLK_f, SDLK_r, SDLK_g, SDLK_t },
+ { SDLK_u, SDLK_j, SDLK_h, SDLK_k, SDLK_i,
+ SDLK_b, SDLK_n, SDLK_m, SDLK_COMMA },
+ { SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, SDLK_F6,
+ SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10 },
};
- for ( int i=0; i<2; ++i )
+ for ( int i=0; i<MAXPLAYERS; ++i )
for ( int j=0; j<9; ++j )
m_aiPlayerKeys[i][j] = aiDefaultKeys[i][j];
strcpy( m_acLatestServer, "apocalypse.rulez.org" );
m_bServer = false;
strcpy( m_acNick, "Mortal" );
strcpy( m_acLanguage, "en" );
// 2. SO FAR THESE WERE THE EASY DEFAULTS
// NOW MOVE ON TO THE TRICKIER ONES.
// 2.1. FIND THE LANGUAGE
#ifdef _WINDOWS
LANGID iLangID = GetUserDefaultLangID() & 0x007f;
const char* pcLang;
switch ( iLangID )
{
case 0x0e: pcLang = "hu";
case 0x0c: pcLang = "fr";
case 0x0a: pcLang = "es";
default: pcLang = "en";
}
strcpy( m_acNick, pcLang );
#else
// Read the locale from the operating system
char* pcLocale = setlocale( LC_CTYPE, NULL );
debug( "The locale returned by the operating system is '%s'\n", pcLocale ? pcLocale : "NULL" );
if ( NULL == pcLocale
|| strcmp( pcLocale, "C") == 0 )
{
// Try the 'GETENV' method
pcLocale = getenv( "LANG" );
debug( "The LANG envvar is '%s'\n", pcLocale ? pcLocale : "NULL" );
}
if ( NULL != pcLocale )
{
strncpy( m_acLanguage, pcLocale, 2 );
m_acLanguage[2] = 0;
}
else
{
strcpy( m_acLanguage, "en" );
}
#endif
// 2.2. FIND THE USER NAME
#ifdef _WINDOWS
m_acNick[0] = 0;
DWORD iLen = 127;
BOOL iResult = GetUserName( m_acNick, &iLen );
if ( 0 == iResult )
{
m_acNick[0] = 0;
debug( "GetUserName failed: %d.\n", iResult );
}
#else
int iResult = getlogin_r( m_acNick, 127 );
if ( iResult )
{
debug( "getlogin_r failed: %d\n", iResult );
strcpy( m_acNick, getenv("USER") );
}
#endif
if ( !m_acNick[0] )
{
strcpy( m_acNick, "Mortal"); // Last-ditch default..
}
};
void SState::ToggleFullscreen()
{
m_bFullscreen = !m_bFullscreen;
bool bPaletted = ( gamescreen->format->BitsPerPixel <= 8 );
SDL_Color aoPalette[256];
int iNumColors = 0;
if ( bPaletted )
{
iNumColors = gamescreen->format->palette->ncolors;
if ( iNumColors > 256 ) iNumColors = 256;
for ( int i=0; i<iNumColors; ++i )
{
aoPalette[i].r = gamescreen->format->palette->colors[i].r;
aoPalette[i].g = gamescreen->format->palette->colors[i].g;
aoPalette[i].b = gamescreen->format->palette->colors[i].b;
aoPalette[i].unused = 0;
}
}
- gamescreen = SDL_SetVideoMode( gamescreen->w, gamescreen->h,
- gamescreen->format->BitsPerPixel,
- m_bFullscreen ? SDL_FULLSCREEN : SDL_SWSURFACE );
+ SetVideoMode( gamescreen->w > 640, m_bFullscreen );
if ( bPaletted )
{
SDL_SetPalette( gamescreen, SDL_LOGPAL | SDL_PHYSPAL, aoPalette, 0, iNumColors );
}
}
void SState::SetLanguage( const char* a_pcLanguage )
{
if ( m_acLanguage != a_pcLanguage )
{
strncpy( m_acLanguage, a_pcLanguage, 9 );
m_acLanguage[9] = 0;
}
g_oBackend.PerlEvalF( "SetLanguage('%s');", m_acLanguage );
SV* poSv = get_sv("LanguageNumber", FALSE);
if (poSv)
{
m_iLanguageCode = SvIV( poSv );
}
else
{
m_iLanguageCode = 0;
}
}
void SState::SetServer( const char* a_pcServer )
{
if ( a_pcServer )
{
strncpy( m_acLatestServer, a_pcServer, 255 );
m_acLatestServer[255] = 0;
m_bServer = false;
}
else
{
m_bServer = true;
}
}
void SState::Load()
{
std::string sFilename = GetConfigFilename();
g_oBackend.PerlEvalF( "ParseConfig('%s');", sFilename.c_str() );
SV* poSv;
+
+// poSv = get_sv("", FALSE); if (poSv) m_ = SvIV( poSv );
+
+ poSv = get_sv("NUMPLAYERS", FALSE); if (poSv) m_iNumPlayers = SvIV( poSv );
+ poSv = get_sv("TEAMMODE", FALSE); if (poSv) m_enTeamMode = (TTeamModeEnum) SvIV( poSv );
+ poSv = get_sv("TEAMSIZE", FALSE); if (poSv) m_iTeamSize = SvIV( poSv );
+ poSv = get_sv("TEAMMULTISELECT", FALSE); if (poSv) m_bTeamMultiselect = SvIV( poSv );
+
poSv = get_sv("GAMETIME", FALSE); if (poSv) m_iGameTime = SvIV( poSv );
poSv = get_sv("HITPOINTS", FALSE); if (poSv) m_iHitPoints = SvIV( poSv );
poSv = get_sv("GAMESPEED", FALSE); if (poSv) m_iGameSpeed = SvIV( poSv );
+
poSv = get_sv("FULLSCREEN", FALSE); if (poSv) m_bFullscreen = SvIV( poSv );
poSv = get_sv("CHANNELS", FALSE); if (poSv) m_iChannels = SvIV( poSv );
poSv = get_sv("MIXINGRATE", FALSE); if (poSv) m_iMixingRate = SvIV( poSv );
poSv = get_sv("MIXINGBITS", FALSE); if (poSv) m_iMixingBits = SvIV( poSv );
poSv = get_sv("MUSICVOLUME", FALSE); if (poSv) m_iMusicVolume = SvIV( poSv );
poSv = get_sv("SOUNDVOLUME", FALSE); if (poSv) m_iSoundVolume = SvIV( poSv );
poSv = get_sv("LANGUAGE", FALSE); if (poSv) { strncpy( m_acLanguage, SvPV_nolen( poSv ), 9 ); m_acLanguage[9] = 0; }
+
poSv = get_sv("LATESTSERVER", FALSE); if (poSv) { strncpy( m_acLatestServer, SvPV_nolen( poSv ), 255 ); m_acLatestServer[255] = 0; }
poSv = get_sv("SERVER", FALSE); if (poSv) m_bServer = SvIV( poSv );
poSv = get_sv("NICK", FALSE); if (poSv) { strncpy( m_acNick, SvPV_nolen( poSv ), 127 ); m_acNick[127] = 0; }
char pcBuffer[1024];
- for ( int i=0; i<2; ++i )
+ for ( int i=0; i<MAXPLAYERS; ++i )
{
for ( int j=0; j<9; ++j )
{
sprintf( pcBuffer, "PLAYER%dKEY%d", i, j );
poSv = get_sv(pcBuffer, FALSE); if (poSv) m_aiPlayerKeys[i][j] = SvIV( poSv );
}
}
}
void SState::Save()
{
std::string sFilename = GetConfigFilename();
std::ofstream oStream( sFilename.c_str(), std::ios_base::out | std::ios_base::trunc );
if ( oStream.rdstate() & std::ios::failbit )
{
debug( "Unable to open config file: %s\n", sFilename.c_str() );
return;
}
oStream << GetConfigHeader() << '\n';
+
+// oStream << "=" << m_ << '\n';
+
+ oStream << "NUMPLAYERS=" << m_iNumPlayers << '\n';
+ oStream << "TEAMMODE=" << m_enTeamMode << '\n';
+ oStream << "TEAMSIZE=" << m_iTeamSize << '\n';
+ oStream << "TEAMMULTISELECT=" << m_bTeamMultiselect << '\n';
+
oStream << "GAMETIME=" << m_iGameTime << '\n';
oStream << "HITPOINTS=" << m_iHitPoints << '\n';
oStream << "GAMESPEED=" << m_iGameSpeed << '\n';
+
oStream << "FULLSCREEN=" << m_bFullscreen << '\n';
oStream << "CHANNELS=" << m_iChannels << '\n';
oStream << "MIXINGRATE=" << m_iMixingRate << '\n';
oStream << "MIXINGBITS=" << m_iMixingBits << '\n';
oStream << "MUSICVOLUME=" << m_iMusicVolume << '\n';
oStream << "SOUNDVOLUME=" << m_iSoundVolume << '\n';
oStream << "LANGUAGE=" << m_acLanguage << '\n';
+
oStream << "LATESTSERVER=" << m_acLatestServer << '\n';
oStream << "SERVER=" << m_bServer << '\n';
oStream << "NICK=" << m_acNick << '\n';
- for ( int i=0; i<2; ++i )
+ for ( int i=0; i<MAXPLAYERS; ++i )
{
for ( int j=0; j<9; ++j )
{
oStream << "PLAYER" <<i<< "KEY" <<j<< '=' << m_aiPlayerKeys[i][j] << '\n';
}
}
oStream.flush();
oStream.close();
}
diff --git a/src/State.h b/src/State.h
index e211a27..7564675 100644
--- a/src/State.h
+++ b/src/State.h
@@ -1,64 +1,74 @@
/***************************************************************************
State.h - description
-------------------
begin : Mon Aug 12 2003
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#ifndef STATE_H
#define STATE_H
-#include "SDL_mixer.h"
-#include "SDL_keysym.h"
+
+#define MAXPLAYERS 4
+
struct SState
{
enum TGameMode {
IN_DEMO,
IN_SINGLE,
IN_MULTI,
IN_NETWORK,
IN_CHAT,
} m_enGameMode;
-
+
bool m_bQuitFlag; // true if quit event came
const char* m_pcArgv0; // Set by main to argv[0]
// CONFIGURATION VARIABLES
+ int m_iNumPlayers; // The number of players =2
+ enum TTeamModeEnum {
+ Team_ONE_VS_ONE,
+ Team_GOOD_VS_EVIL,
+ Team_CUSTOM,
+ } m_enTeamMode; // Team mode
+ int m_iTeamSize; // The size of each team.
+ int m_bTeamMultiselect; // Can the same player be selected twice?
+
int m_iGameTime; // Time of rounds in seconds.
int m_iHitPoints; // The initial number of hit points.
int m_iGameSpeed; // The speed of the game (fps = 1000/GameSpeed)
bool m_bFullscreen; // True in fullscreen mode.
int m_iChannels; // 1: mono, 2: stereo
int m_iMixingRate; // The mixing rate, in kHz
int m_iMixingBits; // 1: 8bit, 2: 16bit
int m_iMusicVolume; // Volume of music; 0: off, 100: max
int m_iSoundVolume; // Volume of sound effects; 0: off, 100: max
- int m_aiPlayerKeys[2][9]; // Player keysyms
+ int m_aiPlayerKeys[MAXPLAYERS][9]; // Player keysyms
char m_acLanguage[10]; // Language ID (en,hu,fr,es,..)
int m_iLanguageCode; // Non-persistend language code (set by backend based on the language)
char m_acLatestServer[256]; // Last server
bool m_bServer; // We were server in the last network game
char m_acNick[128]; // The user name on the last server.
SState();
void Load();
void Save();
void ToggleFullscreen();
void SetLanguage( const char* a_pcLanguage );
void SetServer( const char* a_pcServer );
};
extern SState g_oState;
#endif
diff --git a/src/common.cpp b/src/common.cpp
index 4d5125d..923141e 100644
--- a/src/common.cpp
+++ b/src/common.cpp
@@ -1,177 +1,177 @@
/***************************************************************************
common.cpp - description
-------------------
begin : Fri Aug 24 2001
copyright : (C) 2001 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#include <stdio.h>
#include <stdarg.h>
#include "common.h"
#include "State.h"
#include "Joystick.h"
-#include "SDL/SDL.h"
+#include "SDL.h"
#include "Event.h"
SDL_Surface* gamescreen = NULL;
void debug( const char* format, ... )
{
va_list ap;
va_start( ap, format );
vfprintf( stderr, format, ap );
va_end( ap );
}
bool FindPlayerKey( SDLKey a_enKey, int& a_riOutPlayer, int& a_riOutKey )
{
- for (int i=0; i<2; i++)
+ for (int i=0; i<MAXPLAYERS; i++)
{
for (int j=0; j<9; j++ )
{
if (g_oState.m_aiPlayerKeys[i][j] == a_enKey )
{
a_riOutPlayer = i;
a_riOutKey = j;
return true;
}
}
}
return false;
}
/**
TranslateEvent is an important function in the OpenMortal event processing
chain. It takes an SDL_Event and converts it to a game related event. This
allows for the "transparent" handling of joysticks and such.
\param a_poInEvent The SDL event which is to be translated.
\param a_poOutEvent The output event.
\returns true if the event could be translated (it was relevant for the game),
or false if it couldn't (a_poOutEvent will be set to Me_NOTHING).
*/
bool TranslateEvent( const SDL_Event* a_poInEvent, SMortalEvent* a_poOutEvent )
{
a_poOutEvent->m_enType = Me_NOTHING;
switch ( a_poInEvent->type )
{
case SDL_QUIT:
a_poOutEvent->m_enType = Me_QUIT;
g_oState.m_bQuitFlag = true;
return true;
case SDL_KEYDOWN:
{
SDLKey enKey = a_poInEvent->key.keysym.sym;
if ( enKey == SDLK_ESCAPE )
{
a_poOutEvent->m_enType = Me_MENU;
return true;
}
if ( enKey == SDLK_F1 )
{
a_poOutEvent->m_enType = Me_SKIP;
return true;
}
// Check the player keys
int iPlayer;
int iKey;
bool bFound = FindPlayerKey( enKey, iPlayer, iKey );
if ( bFound )
{
a_poOutEvent->m_enType = Me_PLAYERKEYDOWN;
a_poOutEvent->m_iPlayer = iPlayer;
a_poOutEvent->m_iKey = iKey;
return true;
}
break;
}
case SDL_KEYUP:
{
SDLKey enKey = a_poInEvent->key.keysym.sym;
int iPlayer;
int iKey;
bool bFound = FindPlayerKey( enKey, iPlayer, iKey );
if ( bFound )
{
a_poOutEvent->m_enType = Me_PLAYERKEYUP;
a_poOutEvent->m_iPlayer = iPlayer;
a_poOutEvent->m_iKey = iKey;
return true;
}
break;
}
case SDL_JOYAXISMOTION:
case SDL_JOYBALLMOTION:
case SDL_JOYHATMOTION:
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
{
return g_oJoystick.TranslateEvent( a_poInEvent, a_poOutEvent );
}
} // switch
return false;
}
/** MortalPollEvent works like SDL_PollEvent, except that it
returns an SMortalEvent.
*/
bool MortalPollEvent( SMortalEvent& a_roOutEvent )
{
SDL_Event oEvent;
while ( SDL_PollEvent( &oEvent ) )
{
if ( TranslateEvent( &oEvent, &a_roOutEvent ) )
{
return true;
}
}
a_roOutEvent.m_enType = Me_NOTHING;
return false;
}
/** MortalWaitEvent works like SDL_WaitEvent, except that it
returns an SMortalEvent.
*/
void MortalWaitEvent( SMortalEvent& a_roOutEvent )
{
SDL_Event oEvent;
if ( SDL_WaitEvent( &oEvent ) )
{
if ( TranslateEvent( &oEvent, &a_roOutEvent ) )
{
return;
}
}
a_roOutEvent.m_enType = Me_NOTHING;
return;
}
diff --git a/src/common.h b/src/common.h
index d376c6d..bf40295 100644
--- a/src/common.h
+++ b/src/common.h
@@ -1,61 +1,65 @@
/***************************************************************************
common.h - description
-------------------
begin : Fri Aug 24 2001
copyright : (C) 2001 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#ifndef __COMMON_H
#define __COMMON_H
+#ifndef _SDL_types_h
+#include "SDL_types.h"
+#endif
struct SDL_Surface;
+#define MAXPLAYERS 4
-#define SCREENWIDTH 640
-#define SCREENHEIGHT 480
-#define GAMEBITS 3
-#define GAMEBITS2 (1<<GAMEBITS)
void debug( const char* format, ... );
+#ifndef ABS
#define ABS(A) ( (A>=0) ? (A) : -(A) )
-
-#ifdef _SDL_video_h
-
-extern SDL_Surface* gamescreen;
+#endif
+#ifndef MAX
+#define MAX(A,B) ( (A) > (B) ? (A) : (B) )
+#endif
+#ifndef MIN
+#define MIN(A,B) ( (A) < (B) ? (A) : (B) )
+#endif
void DoMenu();
void GameOver( int a_iPlayerWon );
void DoDemos();
void DoOnlineChat();
bool Connect( const char* a_pcHostname );
const char* Translate( const char* a_pcText );
const char* TranslateUTF8( const char* a_pcText );
-#define C_BLACK 240
-#define C_BLUE 241
-#define C_GREEN 242
-#define C_CYAN 243
+extern SDL_Surface* gamescreen;
-#define C_RED 244
-#define C_MAGENTA 245
-#define C_ORANGE 246
-#define C_LIGHTGRAY 247
+extern Uint32 C_BLACK;
+extern Uint32 C_BLUE;
+extern Uint32 C_GREEN;
+extern Uint32 C_CYAN;
-#define C_DARKGRAY 248
-#define C_LIGHTBLUE 249
-#define C_LIGHTGREEN 250
-#define C_LIGHTCYAN 251
+extern Uint32 C_RED;
+extern Uint32 C_MAGENTA;
+extern Uint32 C_ORANGE;
+extern Uint32 C_LIGHTGRAY;
-#define C_LIGHTRED 252
-#define C_LIGHTMAGENTA 253
-#define C_YELLOW 254
-#define C_WHITE 255
+extern Uint32 C_DARKGRAY;
+extern Uint32 C_LIGHTBLUE;
+extern Uint32 C_LIGHTGREEN;
+extern Uint32 C_LIGHTCYAN;
+extern Uint32 C_LIGHTRED;
+extern Uint32 C_LIGHTMAGENTA;
+extern Uint32 C_YELLOW;
+extern Uint32 C_WHITE;
-#endif
#endif
diff --git a/src/gfx.cpp b/src/gfx.cpp
index cdd1039..8a975a1 100644
--- a/src/gfx.cpp
+++ b/src/gfx.cpp
@@ -1,362 +1,427 @@
/***************************************************************************
gfx.cpp - description
-------------------
begin : Tue Apr 10 2001
copyright : (C) 2001 by UPi
email : upi@apocalypse.rulez.org
***************************************************************************/
#include <string.h>
#include <malloc.h>
#include "SDL.h"
#include "SDL_video.h"
#include "SDL_image.h"
+
#include "sge_tt_text.h"
#include "sge_surface.h"
#ifndef __COMMON_H
#include "common.h"
#endif
#ifndef _GFX_H
#include "gfx.h"
#endif
#include "State.h"
#include "Event.h"
+int CSurfaceLocker::m_giLockCount = 0;
+
+
+
Uint16 *UTF8_to_UNICODE(Uint16 *unicode, const char *utf8, int len);
void sge_TTF_SizeText( _sge_TTFont*font, const char* text, int* x, int* y )
{
#ifdef MSZ_USES_UTF8
Uint16 *unicode_text;
int unicode_len;
/* Copy the UTF-8 text to a UNICODE text buffer */
unicode_len = strlen(text);
unicode_text = (Uint16 *)malloc( (unicode_len+1) * sizeof (Uint16) );
if ( unicode_text == NULL )
{
SDL_SetError("SGE - Out of memory");
*x = *y = 0;
return;
}
UTF8_to_UNICODE(unicode_text, text, unicode_len);
/* Render the new text */
SDL_Rect r = sge_TTF_TextSizeUNI(font, unicode_text);
/* Free the text buffer and return */
free(unicode_text);
#else
SDL_Rect r = sge_TTF_TextSize( font, text );
#endif
*x = r.w;
*y = r.h;
}
int DrawTextMSZ( const char* string, _sge_TTFont* font, int x, int y, int flags,
int fg, SDL_Surface* target, bool a_bTranslate )
{
int retval = 0;
if (!string || !*string) return retval;
if ( a_bTranslate )
{
string = Translate( string );
}
if (flags & UseTilde)
{
char *str2 = strdup( string );
char onechar[2];
char *c1, *c2, *c3;
int w = 0;
int i, j;
bool notend;
if (flags & AlignHCenter)
{
// Determine width of the string without the stupid tildes
c1 = c2 = str2;
notend = true;
while (notend)
{
c2 = c1; // c1: start of this run
while (*c2 && (*c2!='~')) c2++; // c2: end of this run
notend = *c2;
*c2 = 0;
sge_TTF_SizeText( font, c1, &i, &j);
w += i;
if (notend) { *c2='~'; c1=c2+1; } // next run..
}
x -= w/2;
}
flags &= ~(UseTilde | AlignHCenter);
c1 = str2;
onechar[1]=0;
notend = true;
while (1)
{
c2 = c1;
while (*c2 && (*c2!='~')) c2++; // c2: end of this run
notend = *c2;
*c2 = 0;
sge_TTF_SizeText( font, c1, &i, &j);
DrawTextMSZ( c1, font, x, y, flags, fg, target, false );
x += i;
// At this point, we're either at a ~ or end of the text (notend)
if (!notend) break;
onechar[0]= *++c2; // Could be 0, if ~ at end.
if (!onechar[0]) break;
sge_TTF_SizeText( font, onechar, &i, &j);
DrawTextMSZ( onechar, font, x, y, flags, C_LIGHTCYAN, target, false );
x += i;
retval += i;
c1 = c2+1;
if (!*c1) break; // ~X was end of string
}
delete( str2 );
return retval;
}
SDL_Rect dest;
int w, h;
sge_TTF_SizeText( font, string, &w, &h );
dest.w = retval = w;
dest.h = h;
dest.x = flags & AlignHCenter ? x-dest.w/2 : x;
dest.y = flags & AlignVCenter ? y-dest.h/2 : y;
//debug( "X: %d, Y: %d, W: %d, H: %d\n", dest.x, dest.y, dest.w, dest.h );
+
+ CSurfaceLocker oLock;
+
if ( flags & UseShadow )
{
#ifdef MSZ_USES_UTF8
sge_tt_textout_UTF8( target, font, string, dest.x+2, dest.y+2+sge_TTF_FontAscent(font), C_BLACK, C_BLACK, 255 );
#else
-
sge_tt_textout( target, font, string, dest.x+2, dest.y+2+sge_TTF_FontAscent(font), C_BLACK, C_BLACK, 255 );
#endif
}
sge_TTF_AAOn();
#ifdef MSZ_USES_UTF8
dest = sge_tt_textout_UTF8( target, font, string, dest.x, dest.y+sge_TTF_FontAscent(font), fg, C_BLACK, 255 );
#else
dest = sge_tt_textout( target, font, string, dest.x, dest.y+sge_TTF_FontAscent(font), fg, C_BLACK, 255 );
#endif
sge_TTF_AAOff();
return dest.w;
}
void DrawGradientText( const char* text, _sge_TTFont* font, int y, SDL_Surface* target, bool a_bTranslate )
{
int i, j;
if ( a_bTranslate )
{
text = Translate( text );
}
// 1. CREATE OFFSCREEN SURFACE
SDL_Rect size = sge_TTF_TextSize( font, (char*)text );
size.w += 2;
size.h += 2;
size.x = 320 - size.w / 2;
if ( size.x < 0 ) size.x = 0;
size.y = y;
SDL_Surface* surface = SDL_CreateRGBSurface( SDL_SRCCOLORKEY, size.w, size.h, 8, 0,0,0,0 );
+
if ( NULL == surface )
{
debug( "DrawGradientText: Couldn't allocate %d by %d surface!\n", size.w, size.h );
return;
}
// 2. SET OFFSCREEN SURFACE COLORS
SDL_SetColorKey( surface, SDL_SRCCOLORKEY, 0 );
SDL_Color colors[256];
colors[0].r = colors[0].g = colors[0].b = 0;
colors[1] = colors[0];
// The rest is red->yellow gradient.
for ( i=2; i<255; ++i )
{
int j = i > 25 ? i-25 : 0;
colors[i].r = 255;
colors[i].g = 255-j;
colors[i].b = 0;
}
SDL_SetColors( surface, colors, 0, 256 );
// 3. DRAW TEXT, APPLY BORDER, APPLY GRADIENT.
int y1 = sge_TTF_FontAscent(font);
sge_tt_textout( surface, font, text,
1, y1, 255, 0, 255);
-
+ if ( SDL_MUSTLOCK(surface) ) SDL_LockSurface(surface);
for ( y=1; y<size.h-1; ++y )
{
int color = 254 * y / (size.h-1) + 1;
unsigned char *p0, *p1, *p2;
p1 = (unsigned char*) surface->pixels;
p1 += surface->pitch * y + 1;
p0 = p1 - surface->pitch;
p2 = p1 + surface->pitch;
for ( int x=1; x<size.w-1; ++x, ++p0, ++p1, ++p2 )
{
if ( *p1 > 2 )
{
*p1 = color;
}
else
{
if ( (*(p1-1) > 2) || (*(p1+1) > 2) || *p0 > 2 || *p2 > 2 )
{
*p1 = 1;
}
}
}
}
+ if ( SDL_MUSTLOCK(surface) ) SDL_UnlockSurface(surface);
// 4. FINALLY
SDL_BlitSurface( surface, NULL, target, &size );
SDL_FreeSurface( surface );
SDL_UpdateRect( target, size.x, size.y, size.w, size.h );
}
SDL_Color MakeColor( Uint8 r, Uint8 g, Uint8 b )
{
SDL_Color color;
color.r = r; color.g = g; color.b = b; color.unused = 0;
return color;
}
/**
Waits for a key event and returns it.
\param a_bTranslate If this is true, then keypad events will also be
read and processed info keys (cursor, return and escape).
*/
SDLKey GetKey( bool a_bTranslate )
{
SDL_Event oSdlEvent;
SMortalEvent oEvent;
while (SDL_WaitEvent(&oSdlEvent))
{
if ( SDL_KEYDOWN == oSdlEvent.type )
{
return oSdlEvent.key.keysym.sym;
}
if ( SDL_QUIT == oSdlEvent.type )
+
{
g_oState.m_bQuitFlag = true;
return SDLK_ESCAPE;
}
if ( ! a_bTranslate )
{
continue;
}
// Handle gamepad and others
TranslateEvent( &oSdlEvent, &oEvent );
switch (oEvent.m_enType)
{
case Me_QUIT:
g_oState.m_bQuitFlag = true;
return SDLK_ESCAPE;
case Me_PLAYERKEYDOWN:
switch ( oEvent.m_iKey ) {
case Mk_UP: return SDLK_UP;
case Mk_DOWN: return SDLK_DOWN;
case Mk_LEFT: return SDLK_LEFT;
case Mk_RIGHT: return SDLK_RIGHT;
default: return SDLK_RETURN;
}
break;
case Me_MENU:
return SDLK_ESCAPE;
default:
break;
} // switch statement
} // Polling events
// Code will never reach this point, unless there's an error.
return SDLK_ESCAPE;
}
-SDL_Surface* LoadBackground( const char* a_pcFilename, int a_iNumColors, int a_iPaletteOffset )
+SDL_Surface* LoadBackground( const char* a_pcFilename, int a_iNumColors, int a_iPaletteOffset, bool a_bTransparent )
{
char filepath[FILENAME_MAX+1];
strcpy( filepath, DATADIR );
strcat( filepath, "/gfx/" );
strcat( filepath, a_pcFilename );
SDL_Surface* poBackground = IMG_Load( filepath );
if (!poBackground)
{
debug( "Can't load file: %s\n", filepath );
return NULL;
}
SDL_Palette* pal = poBackground->format->palette;
- if ( pal )
+ if ( pal && gamescreen->format->palette )
{
int ncolors = pal->ncolors;
if (ncolors>a_iNumColors) ncolors = a_iNumColors;
if (ncolors+a_iPaletteOffset > 255) ncolors = 255 - a_iPaletteOffset;
SDL_SetColors( gamescreen, pal->colors, a_iPaletteOffset, ncolors );
}
SDL_Surface* poRetval = SDL_DisplayFormat( poBackground );
SDL_FreeSurface( poBackground );
+/* if ( !a_bTransparent && poRetval )
+ {
+ SDL_SetColorKey( poRetval, 0, 0 );
+ }
+ else
+ {
+ SDL_SetColorKey( poRetval, SDL_SRCCOLORKEY, 0 );
+ }
+*/
return poRetval;
}
+bool SetVideoMode( bool a_bLarge, bool a_bFullScreen, int a_iAdditionalFlags )
+{
+ // SET THE PARAMETERS FOR THE VIDEO MODE
+
+ int iBpp = 16; // Try the display's BPP first.
+ if ( NULL != gamescreen )
+ {
+ iBpp = gamescreen->format->BitsPerPixel;
+ }
+
+ int iFlags = a_iAdditionalFlags;
+ if ( a_bFullScreen )
+ {
+ iFlags |= SDL_FULLSCREEN;
+ }
+
+ // CALL SDL_SetVideoMode
+
+ int iWidth = a_bLarge ? 800 : 640;
+ int iHeight = a_bLarge ? 600 : 480;
+// if ( !a_bFullScreen ) iHeight = 480;
+
+ gamescreen = SDL_SetVideoMode( iWidth, iHeight, iBpp, iFlags );
+ if ( NULL == gamescreen )
+ {
+ debug( "SDL_SetVideoMode( %d, %d, %d, %d ) failed.\n", iWidth, iHeight, iBpp, iFlags );
+ return false;
+ }
+
+ // IF THE DISPLAY IS 24BPP OR 8 BPP OR LESS, EMULATE 16 BPP INSTEAD
+ // (because we are lazy and won't write 8bpp and 24bpp code anymore)
+
+ if ( gamescreen->format->BytesPerPixel != 2
+ && gamescreen->format->BytesPerPixel != 4 )
+ {
+ gamescreen = SDL_SetVideoMode( iWidth, iHeight, 16, iFlags );
+ if ( NULL == gamescreen )
+ {
+ debug( "SDL_SetVideoMode( %d, %d, %d, %d ) failed.\n", iWidth, iHeight, 16, iFlags );
+ return false;
+ }
+ }
+
+ return true;
+}
+
diff --git a/src/gfx.h b/src/gfx.h
index d62832f..568a76b 100644
--- a/src/gfx.h
+++ b/src/gfx.h
@@ -1,51 +1,86 @@
/***************************************************************************
gfx.h - description
-------------------
begin : Tue Apr 10 2001
copyright : (C) 2001 by UPi
email : upi@apocalypse.rulez.org
***************************************************************************/
#ifndef _GFX_H
#define _GFX_H
struct _sge_TTFont;
+#include "SDL.h"
+
enum GFX_Constants {
AlignHCenter = 1,
AlignVCenter = 2,
AlignCenter = 3,
UseTilde = 4,
UseShadow = 8,
};
int DrawTextMSZ( const char* text, _sge_TTFont* font, int x, int y,
int flags, int fg, SDL_Surface* target, bool a_bTranslate = true );
void DrawGradientText( const char* text, _sge_TTFont* font, int y,
SDL_Surface* target, bool a_bTranslate = true );
SDL_Color MakeColor( Uint8 r, Uint8 g, Uint8 b );
SDLKey GetKey( bool a_bTranslate );
-SDL_Surface* LoadBackground( const char* a_pcFilename, int a_iNumColors, int a_iPaletteOffset=0 );
+
+SDL_Surface* LoadBackground( const char* a_pcFilename, int a_iNumColors, int a_iPaletteOffset=0, bool a_bTransparent = false );
+
+bool SetVideoMode( bool a_bLarge, bool a_bFullscreen, int a_iAdditionalFlags=0 );
+
+extern SDL_Surface* gamescreen;
+
+class CSurfaceLocker
+{
+public:
+ inline CSurfaceLocker()
+ {
+ if ( 0 == m_giLockCount )
+ {
+ if (SDL_MUSTLOCK(gamescreen)) {
+ SDL_LockSurface( gamescreen );
+ }
+ }
+ ++m_giLockCount;
+ }
+ inline ~CSurfaceLocker()
+ {
+ --m_giLockCount;
+ if ( 0 == m_giLockCount )
+ {
+ if (SDL_MUSTLOCK(gamescreen)) {
+ SDL_UnlockSurface( gamescreen );
+ }
+ }
+ }
+
+protected:
+ static int m_giLockCount;
+};
extern _sge_TTFont* titleFont; // Largest font, for titles
extern _sge_TTFont* inkFont; // Medium-size front, headings
extern _sge_TTFont* impactFont; // Smallest font, for long descriptions
extern _sge_TTFont* chatFont; // small but legible.
#ifdef sge_bm_text_H
extern sge_bmpFont* fastFont; // In-game text, e.g. combo text
extern sge_bmpFont* creditsFont;
extern sge_bmpFont* storyFont;
#endif
#endif
diff --git a/src/main.cpp b/src/main.cpp
index 3076da9..594e0e8 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,515 +1,615 @@
/***************************************************************************
main.cpp - description
-------------------
begin : Wed Aug 22 10:18:47 CEST 2001
copyright : (C) 2001 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
+#include "config.h"
#include "PlayerSelect.h"
#include "SDL_video.h"
#include "sge_tt_text.h"
#include "sge_bm_text.h"
#include "sge_surface.h"
#include "SDL.h"
#include "SDL_image.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <string>
#include "common.h"
#include "gfx.h"
#include "Audio.h"
#include "RlePack.h"
#include "Backend.h"
#include "State.h"
#include "FighterStats.h"
#include "MortalNetwork.h"
#ifdef _WINDOWS
#undef DATADIR // GRRR.. windows keyword...
#include <windows.h>
#define DATADIR "../data"
#endif
_sge_TTFont* inkFont;
_sge_TTFont* impactFont;
_sge_TTFont* titleFont;
_sge_TTFont* chatFont;
sge_bmpFont* fastFont;
sge_bmpFont* creditsFont;
sge_bmpFont* storyFont;
bool bDebug = false;
+Uint32 C_BLACK, C_BLUE, C_GREEN, C_CYAN, C_RED, C_MAGENTA, C_ORANGE, C_LIGHTGRAY,
+ C_DARKGRAY, C_LIGHTBLUE, C_LIGHTGREEN, C_LIGHTCYAN, C_LIGHTRED, C_LIGHTMAGENTA, C_YELLOW, C_WHITE;
+
SDL_Color Colors[] =
{
{ 0, 0, 0, 0 }, { 0, 0, 42, 0 }, { 0, 42, 0, 0 }, { 0, 42, 42, 0 },
{ 42, 0, 0, 0 }, { 42, 0, 42, 0 }, { 63, 42, 0, 0 }, { 42, 42, 42, 0 },
{ 21, 21, 21, 0 }, { 21, 21, 63, 0 }, { 21, 63, 21, 0 }, { 21, 63, 63, 0 },
{ 63, 21, 21, 0 }, { 63, 21, 63, 0 }, { 63, 63, 21, 0 }, { 63, 63, 63, 0 }
};
void Complain( const char* a_pcError )
{
#ifdef _WINDOWS
::MessageBoxA( 0, a_pcError, "OpenMortal", MB_ICONEXCLAMATION );
#else
fprintf( stderr, "%s", a_pcError );
#endif
}
_sge_TTFont* LoadTTF( const char* a_pcFilename, int a_iSize )
{
std::string sPath = std::string(DATADIR) + "/fonts/" + a_pcFilename;
_sge_TTFont* poFont = sge_TTF_OpenFont( sPath.c_str(), a_iSize );
if ( NULL == poFont )
{
Complain( ("Couldn't load font: " + sPath).c_str() );
}
return poFont;
}
sge_bmpFont* LoadBMPFont( const char* a_pcFilename )
{
std::string sPath = std::string(DATADIR) + "/fonts/" + a_pcFilename;
sge_bmpFont* poFont = sge_BF_OpenFont( sPath.c_str(), SGE_BFSFONT | SGE_BFTRANSP );
if ( NULL == poFont )
{
Complain( ("Couldn't load font: " + sPath).c_str() );
}
return poFont;
}
-int init( int iFlags )
+int init()
{
if (SDL_Init(SDL_INIT_VIDEO /*| SDL_INIT_AUDIO*/) < 0)
{
fprintf(stderr, "Failed to initialize SDL: %s\n", SDL_GetError());
return -1;
}
atexit(SDL_Quit);
- int d = SDL_VideoModeOK(640, 480, 8, iFlags);
- if (d == 0)
- {
- fprintf(stderr, "requested video mode not available\n");
-// return -1;
- }
-
- gamescreen = SDL_SetVideoMode(640, 480, 8, iFlags);
+ SetVideoMode( false, g_oState.m_bFullscreen );
if (gamescreen == NULL)
{
fprintf(stderr, "failed to set video mode: %s\n", SDL_GetError());
return -1;
}
SDL_WM_SetCaption( "OpenMortal", "OpenMortal" );
std::string sPath = std::string(DATADIR) + "/gfx/icon.png";
SDL_WM_SetIcon(IMG_Load(sPath.c_str()), NULL);
SDL_ShowCursor( SDL_DISABLE );
- for ( int i=0; i<16; ++i ) { Colors[i].r *=4; Colors[i].g *=4; Colors[i].b *=4; }
- SDL_SetColors( gamescreen, Colors, 256-16, 16 );
+ int i;
+ for ( i=0; i<16; ++i )
+ {
+ Colors[i].r *=4; Colors[i].g *=4; Colors[i].b *=4;
+ }
+ if ( gamescreen->format->BitsPerPixel > 8 )
+ {
+ i = 0;
+ C_BLACK = SDL_MapRGB( gamescreen->format, Colors[i].r, Colors[i].g, Colors[i].b ); ++i;
+ C_BLUE = SDL_MapRGB( gamescreen->format, Colors[i].r, Colors[i].g, Colors[i].b ); ++i;
+ C_GREEN = SDL_MapRGB( gamescreen->format, Colors[i].r, Colors[i].g, Colors[i].b ); ++i;
+ C_CYAN = SDL_MapRGB( gamescreen->format, Colors[i].r, Colors[i].g, Colors[i].b ); ++i;
+
+ C_RED = SDL_MapRGB( gamescreen->format, Colors[i].r, Colors[i].g, Colors[i].b ); ++i;
+ C_MAGENTA = SDL_MapRGB( gamescreen->format, Colors[i].r, Colors[i].g, Colors[i].b ); ++i;
+ C_ORANGE = SDL_MapRGB( gamescreen->format, Colors[i].r, Colors[i].g, Colors[i].b ); ++i;
+ C_LIGHTGRAY = SDL_MapRGB( gamescreen->format, Colors[i].r, Colors[i].g, Colors[i].b ); ++i;
+
+ C_DARKGRAY = SDL_MapRGB( gamescreen->format, Colors[i].r, Colors[i].g, Colors[i].b ); ++i;
+ C_LIGHTBLUE = SDL_MapRGB( gamescreen->format, Colors[i].r, Colors[i].g, Colors[i].b ); ++i;
+ C_LIGHTGREEN = SDL_MapRGB( gamescreen->format, Colors[i].r, Colors[i].g, Colors[i].b ); ++i;
+ C_LIGHTCYAN = SDL_MapRGB( gamescreen->format, Colors[i].r, Colors[i].g, Colors[i].b ); ++i;
+
+ C_LIGHTRED = SDL_MapRGB( gamescreen->format, Colors[i].r, Colors[i].g, Colors[i].b ); ++i;
+ C_LIGHTMAGENTA = SDL_MapRGB( gamescreen->format, Colors[i].r, Colors[i].g, Colors[i].b ); ++i;
+ C_YELLOW = SDL_MapRGB( gamescreen->format, Colors[i].r, Colors[i].g, Colors[i].b ); ++i;
+ C_WHITE = SDL_MapRGB( gamescreen->format, Colors[i].r, Colors[i].g, Colors[i].b ); ++i;
+ }
+ else
+ {
+ SDL_SetColors( gamescreen, Colors, 256-16, 16 );
+ C_BLACK = 240;
+ C_BLUE = 241;
+ C_GREEN = 242;
+ C_CYAN = 243;
+
+ C_RED = 244;
+ C_MAGENTA = 245;
+ C_ORANGE = 246;
+ C_LIGHTGRAY = 247;
+
+ C_DARKGRAY = 248;
+ C_LIGHTBLUE = 249;
+ C_LIGHTGREEN = 250;
+ C_LIGHTCYAN = 251;
+
+ C_LIGHTRED = 252;
+ C_LIGHTMAGENTA = 253;
+ C_YELLOW = 254;
+ C_WHITE = 255;
+ }
if ( sge_TTF_Init() )
{
fprintf(stderr, "couldn't start ttf engine: %s\n", SDL_GetError());
return -1;
}
sge_TTF_AAOff();
inkFont = LoadTTF( "aardvark.ttf", 20 );
if ( !inkFont ) return -1;
impactFont = LoadTTF( "bradybun.ttf", 20 ); // gooddogc.ttf, 20
if ( !impactFont ) return -1;
titleFont = LoadTTF( "deadgrit.ttf", 48 ); // deadgrit.ttf, 48
if ( !titleFont ) return -1;
chatFont = LoadTTF( "thin.ttf", 20 ); // deadgrit.ttf, 48
if ( !chatFont ) return -1;
fastFont = LoadBMPFont( "brandybun3.png" );
if ( !fastFont ) return -1;
creditsFont = LoadBMPFont( "CreditsFont2.png" );//"fangfont.png" );
if ( !creditsFont ) return -1;
storyFont = LoadBMPFont( "glossyfont.png" );
if ( !storyFont ) return -1;
return 0;
}
int init2()
{
if ( !g_oBackend.Construct() )
{
fprintf(stderr, "couldn't start backend.\n" );
return -1;
}
return 0;
}
int DrawMainScreen()
{
- SDL_Surface* background = LoadBackground( "Mortal.png", 240 );
+ SDL_Surface* background = LoadBackground( "Mortal.jpg", 240 );
DrawTextMSZ( "Version " VERSION " ゥ 2003-2004 by UPi", inkFont, 320, 430, UseShadow | AlignHCenter, C_WHITE, background, false );
SDL_Rect r;
r.x = r.y = 0;
std::string sStaffFilename = DATADIR;
sStaffFilename += "/characters/STAFF.DAT";
- RlePack pack( sStaffFilename.c_str(), 240 );
+ RlePack pack( sStaffFilename.c_str(), 255 );
pack.ApplyPalette();
- //SDL_SetColors( gamescreen, pack.getPalette(), 0, 240 );
SDL_BlitSurface( background, NULL, gamescreen, &r );
SDL_Flip( gamescreen );
- char* filename[15] = {
+/* char* filename[15] = {
"Jacint.pl", "Jozsi.pl", "Agent.pl", "Mrsmith.pl",
"Sleepy.pl", "Tejszin.pl",
"UPi.pl", "Zoli.pl", "Ulmar.pl", "Bence.pl",
- "Descant.pl", "Grizli.pl", "Sirpi.pl", "Macy.pl", "Cumi.pl" };
+ "Descant.pl", "Grizli.pl", "Sirpi.pl", "Macy.pl", "Cumi.pl" };*/
int x[14] = {
0, 26, 67, 125, 159, 209,
249, 289, 358, 397, 451, 489, 532, 161 };
int y[14] = {
5, 4, 5, 5, 5, 7,
4, 0, 7, 5, 5, 6, 5, 243 };
-
+
+ /*
int i;
g_oBackend.PerlEvalF( "eval( \"require '%s/characters/Kinga.pl';\" )", DATADIR );
g_oBackend.PerlEvalF( "eval( \"require '%s/characters/Ambrus.pl';\" )", DATADIR );
g_oBackend.PerlEvalF( "eval( \"require '%s/characters/Dani.pl';\" )", DATADIR );
for ( i=0; i<15; ++i )
{
pack.Draw( i, x[i], y[i], false );
SDL_Flip( gamescreen );
if ( filename[i] != NULL )
{
debug( "Loading fighter %s", filename[i] );
g_oBackend.PerlEvalF( "eval( \"require '%s/characters/%s';\" )", DATADIR, filename[i] );
+
+
}
}
int retval = 0;
i = 0;
+ */
+
+ int iNumFighterFiles, i;
+
+ g_oBackend.PerlEvalF( "$CppRetval = GetNumberOfFighterFiles('%s')", DATADIR "/characters" );
+ iNumFighterFiles = g_oBackend.GetPerlInt( "CppRetval" );
+
+ for ( i=0; i<iNumFighterFiles; ++i )
+ {
+ g_oBackend.PerlEvalF( "LoadFighterFile(%d);", i );
+
+ if ( i < 15 ) {
+ pack.Draw( i, x[i], y[i], false );
+ SDL_Flip( gamescreen );
+ }
+ }
SDL_FreeSurface( background );
- return retval;
+ return 0;
}
int InitJoystick();
/**
The game loop consists of the following events:
\li Player selection
\li DoGame
\li GameOver and FighterStatsDemo (not in network mode)
The loop ends if the game mode changes to a non-game mode (e.g. IN_DEMO or IN_CHAT)
*/
void GameLoop()
{
#define IS_GAME_MODE (g_oState.m_enGameMode != SState::IN_DEMO \
&& g_oState.m_enGameMode != SState::IN_CHAT \
&& !g_oState.m_bQuitFlag)
Audio->PlaySample( "car_start.voc" );
Audio->PlayMusic( "GameMusic" );
bool bNetworkGame = SState::IN_NETWORK == g_oState.m_enGameMode;
while ( IS_GAME_MODE )
{
- g_oPlayerSelect.DoPlayerSelect();
+ if ( SState::Team_GOOD_VS_EVIL == g_oState.m_enTeamMode )
+ {
+ std::vector<FighterEnum>& roTeam0 = g_oPlayerSelect.EditPlayerInfo(0).m_aenTeam;
+ std::vector<FighterEnum>& roTeam1 = g_oPlayerSelect.EditPlayerInfo(1).m_aenTeam;
+ roTeam0.clear();
+ roTeam1.clear();
+
+ roTeam0.push_back( SIRPI );
+ roTeam0.push_back( MACI );
+ roTeam0.push_back( GRIZLI );
+ roTeam0.push_back( DANI );
+ roTeam0.push_back( KINGA );
+ roTeam0.push_back( CUMI );
+
+ roTeam1.push_back( ZOLI );
+ roTeam1.push_back( ULMAR );
+ roTeam1.push_back( BENCE );
+ roTeam1.push_back( AMBRUS );
+ roTeam1.push_back( DESCANT ); // Temporary assignment
+ roTeam1.push_back( UPI );
+
+ for ( int i=0; i<10; ++i )
+ {
+ int j = rand() % ( roTeam0.size() -1 );
+ int k = rand() % ( roTeam0.size() -1 );
+ FighterEnum enTemp;
+ enTemp = roTeam0[j]; roTeam0[j] = roTeam0[k]; roTeam0[k] = enTemp;
+
+ j = rand() % ( roTeam1.size() -1 );
+ k = rand() % ( roTeam1.size() -1 );
+ enTemp = roTeam1[j]; roTeam1[j] = roTeam1[k]; roTeam1[k] = enTemp;
+ }
+ }
+ else
+ {
+ g_oPlayerSelect.DoPlayerSelect();
+ }
if ( !IS_GAME_MODE ) break;
//sprintf( acReplayFile, "/tmp/msz%d.replay", ++iGameNumber );
int iGameResult = DoGame( NULL, false, bDebug );
//int iGameResult = DoGame( acReplayFile, false, bDebug );
//DoGame( acReplayFile, true, bDebug );
debug ( "iGameResult = %d\n", iGameResult );
if ( !IS_GAME_MODE ) break;
if ( iGameResult >= 0 && !bNetworkGame )
{
GameOver( iGameResult );
FighterStatsDemo oDemo( g_oPlayerSelect.GetPlayerInfo( iGameResult ).m_enFighter );
oDemo.Run();
}
if ( !IS_GAME_MODE ) break;
}
if ( bNetworkGame && !g_oState.m_bQuitFlag )
{
DrawTextMSZ( "Connection closed.", inkFont, 320, 210, AlignHCenter | UseShadow, C_WHITE, gamescreen );
DrawTextMSZ( g_poNetwork->GetLastError(), impactFont, 320, 250, AlignHCenter | UseShadow, C_WHITE, gamescreen );
SDL_Delay( 1000 );
GetKey( true );
}
if ( !g_oState.m_bQuitFlag )
{
Audio->PlayMusic( "DemoMusic" );
}
}
/**
The chat loop consists of:
\li DoOnlineChat
\li GameLoop (if a game was started
The loop ends if DoOnlineChat returns with a quit or disconnect
(not IN_NETWORK mode).
*/
void ChatLoop()
{
while (1)
{
DoOnlineChat();
if ( g_oState.m_bQuitFlag ||
SState::IN_CHAT != g_oState.m_enGameMode )
{
break;
}
if ( SState::IN_NETWORK == g_oState.m_enGameMode )
{
GameLoop();
}
if ( g_oState.m_bQuitFlag ) break;
g_oState.m_enGameMode = SState::IN_CHAT;
}
}
int main(int argc, char *argv[])
{
srand( (unsigned int)time(NULL) );
if ( 0 != init2() )
{
fprintf( stderr, "init2() failed." );
return -1;
}
g_oState.m_pcArgv0 = argv[0];
g_oState.Load();
CMortalNetwork::Create();
bDebug = false;
- int iFlags = SDL_SWSURFACE | SDL_HWPALETTE;
-
-
- if ( g_oState.m_bFullscreen )
- {
- iFlags |= SDL_FULLSCREEN;
- }
-
int i;
for ( i=1; i<argc; ++i )
{
if ( !strcmp(argv[i], "-debug") )
{
bDebug = true;
}
+/*
else if ( !strcmp(argv[i], "-fullscreen") )
{
iFlags |= SDL_FULLSCREEN;
}
else if ( !strcmp(argv[i], "-hwsurface") )
{
iFlags |= SDL_HWSURFACE;
}
else if ( !strcmp(argv[i], "-doublebuf") )
{
iFlags |= SDL_DOUBLEBUF;
}
else if ( !strcmp(argv[i], "-anyformat") )
{
iFlags |= SDL_ANYFORMAT;
}
+
+*/
else
+
{
- printf( "Usage: %s [-debug] [-fullscreen] [-hwsurface] [-doublebuf] [-anyformat]\n", argv[0] );
+// printf( "Usage: %s [-debug] [-fullscreen] [-hwsurface] [-doublebuf] [-anyformat]\n", argv[0] );
+ printf( "Usage: %s [-debug]\n", argv[0] );
return 0;
}
}
- if (init( iFlags )<0)
+ if (init()<0)
{
return -1;
}
InitJoystick();
g_oState.SetLanguage( g_oState.m_acLanguage );
new MszAudio;
// Audio->LoadMusic( "Last_Ninja_-_The_Wilderness.mid", "DemoMusic" );
Audio->LoadMusic( "ride.mod", "DemoMusic" );
Audio->PlayMusic( "DemoMusic" );
Audio->LoadMusic( "2nd_pm.s3m", "GameMusic" );
DrawMainScreen();
-
g_oPlayerSelect.SetPlayer( 0, ZOLI );
- g_oPlayerSelect.SetPlayer( 1, SIRPI );
-
- /*
- int nextFighter = 0;
- int describeOrder[ (int)LASTFIGHTER ];
-
- for ( i=0; i<(int)LASTFIGHTER; ++i ) describeOrder[i] = i;
- for ( i=0; i<100; ++i )
- {
- int j = rand() % (int)LASTFIGHTER;
- int k = rand() % (int)LASTFIGHTER;
- int l;
- l = describeOrder[j];
- describeOrder[j] = describeOrder[k];
- describeOrder[k] = l;
- }
- */
+ g_oPlayerSelect.SetPlayer( 1, ZOLI );
/*
{
int iGameNumber=0;
char acReplayFile[1024];
for ( i=0; i<15; ++i )
{
sprintf( acReplayFile, DATADIR "/msz%i.replay", i );
DrawTextMSZ( acReplayFile, impactFont, 10, 10, 0, C_WHITE, gamescreen );
SDL_Delay(5000 );
DoGame( acReplayFile, true, bDebug );
}
}
*/
+
+/* while ( !g_oState.m_bQuitFlag )
+ {
+ g_oState.m_enGameMode = SState::IN_MULTI;
+ g_oState.m_bTeamMultiselect = false;
+ g_oState.m_iTeamSize = 3;
+ g_oState.m_enTeamMode = SState::Team_CUSTOM;
+
+ std::vector<FighterEnum>& roTeam0 = g_oPlayerSelect.EditPlayerInfo(0).m_aenTeam;
+ std::vector<FighterEnum>& roTeam1 = g_oPlayerSelect.EditPlayerInfo(1).m_aenTeam;
+
+ roTeam0.clear();
+ roTeam0.push_back( ZOLI );
+ roTeam0.push_back( SIRPI );
+ roTeam0.push_back( MACI );
+ roTeam1.clear();
+ roTeam1.push_back( UPI );
+ roTeam1.push_back( ZOLI );
+ roTeam1.push_back( ULMAR );
+
+ DoGame( NULL, false, false );
+ }
+*/
while ( 1 )
{
if ( g_oState.m_bQuitFlag ) break;
switch ( g_oState.m_enGameMode )
{
case SState::IN_DEMO:
DoDemos();
continue;
case SState::IN_CHAT:
ChatLoop();
continue;
default:
GameLoop();
continue;
}
#if 0
// Remaining are game modes: IN_SINGLE, IN_MULTI, IN_NETWORK
Audio->PlaySample( "car_start.voc" );
Audio->PlayMusic( "GameMusic" );
bNetworkGame = false;
while ( g_oState.m_enGameMode != SState::IN_DEMO
&& g_oState.m_enGameMode != SState::IN_CHAT
&& !g_oState.m_bQuitFlag )
{
bNetworkGame = SState::IN_NETWORK == g_oState.m_enGameMode;
g_oPlayerSelect.DoPlayerSelect();
if ( g_oState.m_bQuitFlag || g_oState.m_enGameMode == SState::IN_DEMO ) break;
//sprintf( acReplayFile, "/tmp/msz%d.replay", ++iGameNumber );
int iGameResult = DoGame( NULL, false, bDebug );
//int iGameResult = DoGame( acReplayFile, false, bDebug );
//DoGame( acReplayFile, true, bDebug );
if ( g_oState.m_bQuitFlag || g_oState.m_enGameMode == SState::IN_DEMO ) break;
debug ( "iGameResult = %d\n", iGameResult );
if ( iGameResult >= 0 && !bNetworkGame )
{
GameOver( iGameResult );
FighterStatsDemo oDemo( g_oPlayerSelect.GetPlayerInfo( iGameResult ).m_enFighter );
oDemo.Run();
}
if ( g_oState.m_bQuitFlag || g_oState.m_enGameMode == SState::IN_DEMO ) break;
}
if ( bNetworkGame && !g_oState.m_bQuitFlag )
{
DrawTextMSZ( "Connection closed.", inkFont, 320, 210, AlignHCenter | UseShadow, C_WHITE, gamescreen );
DrawTextMSZ( g_poNetwork->GetLastError(), impactFont, 320, 250, AlignHCenter | UseShadow, C_WHITE, gamescreen );
SDL_Delay( 1000 );
GetKey( true );
}
if ( g_oState.m_bQuitFlag ) break;
Audio->PlayMusic( "DemoMusic" );
#endif
}
g_oState.Save();
SDL_Quit();
return EXIT_SUCCESS;
}
diff --git a/src/menu.cpp b/src/menu.cpp
index 81d77e9..25564ca 100644
--- a/src/menu.cpp
+++ b/src/menu.cpp
@@ -1,1216 +1,1301 @@
/***************************************************************************
menu.cpp - description
-------------------
begin : Sun Aug 3 2003
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#include "SDL.h"
#include "SDL_video.h"
+#include "sge_primitives.h"
#include "menu.h"
#include "gfx.h"
#include "State.h"
#include "common.h"
#include "Audio.h"
#include "Backend.h"
#include "sge_tt_text.h"
#include "sge_surface.h"
#include "MortalNetwork.h"
#include "Joystick.h"
#include <stdarg.h>
const char* g_ppcGameTime[] = { "0:30", "0:45", "1:00", "1:15", "1:30", "1:45", "2:00", "3:00", "5:00", NULL };
const int g_piGameTime[] = { 30, 45, 60, 75, 90, 105, 120, 180, 300 };
const char* g_ppcHitPoints[] = { "BABY", "VERY LOW", "LOW", "NORMAL", "HIGH", "VERY HIGH", "NEAR IMMORTAL", NULL };
const int g_piHitPoints[] = { 1, 10, 50, 100, 150, 200, 500 };
const char* g_ppcGameSpeed[] = { "SNAIL RACE", "SLOW", "NORMAL", "TURBO", "KUNG-FU MOVIE", NULL };
const int g_piGameSpeed[] = { 16, 14, 12, 10, 8 };
const char* g_ppcChannels[] = { "MONO", "STEREO", NULL };
const int g_piChannels[] = { 1, 2 };
const char* g_ppcMixingRate[] = { "LOW", "MEDIUM", "HIGH", NULL };
const int g_piMixingRate[] = { 1, 2, 3 };
const char* g_ppcMixingBits[] = { "8 bit", "16 bit", NULL };
const int g_piMixingBits[] = { 1, 2 };
const char* g_ppcVolume[] = { "OFF", "10%", "20%", "30%", "40%", "50%", "60%", "70%", "80%", "90%", "100%", NULL };
const int g_piVolume[] = { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
const char* g_ppcLanguage[] = { "English", "Spanish", "Francais", "Magyar", "Portugues", NULL };
const int g_piLanguage[] = { 0, 1, 2, 3, 4 };
const char* g_ppcLanguageCodes[] = { "en", "es", "fr", "hu", "pt" };
const char* g_ppcServer[] = { "Connect to game", "Create game", NULL };
int g_piServer[] = { 0, 1 };
+const char* g_ppcTeamMode[] = { "1 VS 1", "Good VS Evil", "Custom teams", NULL };
+int g_piTeamMode[] = { SState::Team_ONE_VS_ONE, SState::Team_GOOD_VS_EVIL, SState::Team_CUSTOM };
+const char* g_ppcTeamSize[] = { "2", "3", "4", "5", "6", "7", "8", "9", "10", NULL };
+int g_piTeamSize[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+const char* g_ppcNumPlayers[] = { "2", "3", "4", NULL };
+int g_piNumPlayers[] = { 2, 3, 4 };
+
+const char* g_ppcYesNo[] = { "Yes", "No", NULL };
+int g_piYesNo[] = { 1, 0 };
SDL_Surface* poBackground = NULL;
void InputKeys( int a_iPlayerNumber )
{
SDL_BlitSurface( poBackground, NULL, gamescreen, NULL );
DrawGradientText( "Input keys", titleFont, 10, gamescreen );
SDL_Flip( gamescreen );
static const char* apcKeyNames[9] = { "up", "down", "left", "right", "block",
"low punch", "high punch", "low kick", "high kick" };
char acBuffer[1024];
char acSide[128];
char acFormat[128];
int iY = 75;
int iYIncrement = 35;
SDLKey enKey;
const char* pcJoyName = g_oJoystick.GetJoystickName( a_iPlayerNumber );
if ( NULL != pcJoyName )
{
- DrawTextMSZ( pcJoyName, inkFont, 320, iY, AlignHCenter|UseShadow, C_LIGHTCYAN, gamescreen );
+ DrawTextMSZ( pcJoyName, inkFont, gamescreen->w/2, iY, AlignHCenter|UseShadow, C_LIGHTCYAN, gamescreen );
iY += iYIncrement + 10;
}
- DrawTextMSZ( "Press Escape to abort", inkFont, 320, 470-iYIncrement, AlignHCenter|UseShadow, C_LIGHTGRAY, gamescreen );
+ DrawTextMSZ( "Press Escape to abort", inkFont, gamescreen->w/2, gamescreen->h-10-iYIncrement,
+ AlignHCenter|UseShadow, C_LIGHTGRAY, gamescreen );
strcpy( acSide, Translate(a_iPlayerNumber ? "Left" : "Right") );
strcpy( acFormat, Translate("%s player-'%s'?") );
for ( int i=0; i<9; ++i )
{
// 1. PRINT THE FONT AND THE CURRENT KEYSYM
sprintf( acBuffer, acFormat, acSide, Translate(apcKeyNames[i]) );
int w = DrawTextMSZ( acBuffer, inkFont, 10, iY, UseShadow, C_WHITE, gamescreen );
enKey = (SDLKey) g_oState.m_aiPlayerKeys[a_iPlayerNumber][i];
g_oBackend.PerlEvalF( "GetKeysym(%d);", enKey );
DrawTextMSZ( g_oBackend.GetPerlString("keysym"), inkFont, w+30, iY, UseShadow, C_LIGHTCYAN, gamescreen );
// 2. INPUT THE NEW KEY
enKey = GetKey( false );
if ( SDLK_ESCAPE == enKey )
{
SDL_BlitSurface( poBackground, NULL, gamescreen, NULL );
SDL_Flip( gamescreen );
return;
}
// 3. PRINT THE NEW KEY
g_oState.m_aiPlayerKeys[a_iPlayerNumber][i] = enKey;
g_oBackend.PerlEvalF( "GetKeysym(%d);", enKey );
- sge_Blit( poBackground, gamescreen, w+10, iY, w+10, iY, 640, 50 );
+ sge_Blit( poBackground, gamescreen, w+10, iY, w+10, iY, gamescreen->w, 50 );
DrawTextMSZ( g_oBackend.GetPerlString("keysym"), inkFont, w+30, iY, UseShadow, C_WHITE, gamescreen );
- sge_UpdateRect( gamescreen, w+10, iY, 640, 50 );
+ sge_UpdateRect( gamescreen, w+10, iY, gamescreen->w, 50 );
iY += iYIncrement;
}
- sge_Blit( poBackground, gamescreen, 0, 470-iYIncrement, 0, 470-iYIncrement, 640, 480 );
- sge_UpdateRect( gamescreen, 0, 470-iYIncrement, 640, 480 );
- DrawTextMSZ( "Thanks!", inkFont, 320, iY + 20, UseShadow | AlignCenter, C_WHITE, gamescreen );
+ sge_Blit( poBackground, gamescreen, 0, 470-iYIncrement, 0, 470-iYIncrement, gamescreen->w, gamescreen->h );
+ sge_UpdateRect( gamescreen, 0, 470-iYIncrement, gamescreen->w, gamescreen->h );
+ DrawTextMSZ( "Thanks!", inkFont, gamescreen->w/2, iY + 20, UseShadow | AlignCenter, C_WHITE, gamescreen );
GetKey( true );
SDL_BlitSurface( poBackground, NULL, gamescreen, NULL );
SDL_Flip( gamescreen );
}
-
-
-
-
/***************************************************************************
NETWORK MENU DEFINITION
***************************************************************************/
int g_iMessageY;
static char g_acMessageBuffer[1024];
void MortalNetworkResetMessages( bool a_bClear )
{
if ( a_bClear )
{
SDL_FillRect( gamescreen, NULL, C_BLACK );
SDL_Flip( gamescreen );
g_iMessageY = 185;
}
else
{
g_iMessageY = 185;
}
}
void MortalNetworkMessage( const char* format, ... )
{
char acBuffer[1024];
va_list ap;
va_start( ap, format );
- vsnprintf( acBuffer, 1023, format, ap );
+ vsprintf( acBuffer, format, ap );
va_end( ap );
DrawTextMSZ( acBuffer, impactFont, 20, g_iMessageY, 0, C_LIGHTGRAY, gamescreen );
g_iMessageY += 25;
}
bool MortalNetworkCheckKey()
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
g_oState.m_bQuitFlag = true;
return true;
case SDL_KEYDOWN:
case SDL_JOYBUTTONDOWN:
return true;
} // switch statement
} // Polling events
return false;
}
bool Connect( const char* a_pcHostname )
{
MortalNetworkResetMessages( false );
bool bOK = g_poNetwork->Start( a_pcHostname );
if ( bOK )
{
// Store these settings as they are pretty good.
g_oState.SetServer( a_pcHostname );
g_oState.m_enGameMode = SState::IN_NETWORK;
}
else
{
// Print error message
const char* acError = g_poNetwork->GetLastError();
- DrawTextMSZ( "Couldn't connect", inkFont, 320, g_iMessageY, AlignHCenter|UseShadow, C_LIGHTRED, gamescreen );
- DrawTextMSZ( acError, impactFont, 320, g_iMessageY + 40, AlignHCenter|UseShadow, C_LIGHTRED, gamescreen, false );
+ DrawTextMSZ( "Couldn't connect", inkFont, gamescreen->w/2, g_iMessageY, AlignHCenter|UseShadow, C_LIGHTRED, gamescreen );
+ DrawTextMSZ( acError, impactFont, gamescreen->w/2, g_iMessageY + 40, AlignHCenter|UseShadow, C_LIGHTRED, gamescreen, false );
}
// Wait for a key, unless Quit was issued.
if ( !g_oState.m_bQuitFlag )
{
if ( bOK )
{
// Wait for 1 sec, or keystroke.
for ( int i=0; i<10; ++i )
{
if ( MortalNetworkCheckKey() ) break;
SDL_Delay( 100 );
}
}
else
{
GetKey( true );
}
}
if ( g_oState.m_bQuitFlag )
{
bOK = false;
}
return bOK;
}
const char* FindString( const char* a_ppcNames[], const int a_piValues[], int a_iValue )
{
for ( int i=0; NULL != a_ppcNames[i]; ++i )
{
if ( a_iValue == a_piValues[i] )
{
return a_ppcNames[i];
}
}
return "(unknown)";
}
const char* GetGameTimeString( int a_iValue )
{
strcpy( g_acMessageBuffer, Translate("GAME TIME: ") );
strcat( g_acMessageBuffer, Translate(FindString( g_ppcGameTime, g_piGameTime, a_iValue)) );
return g_acMessageBuffer;
}
const char* GetGameSpeedString( int a_iValue )
{
strcpy( g_acMessageBuffer, Translate("GAME SPEED: ") );
strcat( g_acMessageBuffer, Translate(FindString( g_ppcGameSpeed, g_piGameSpeed, a_iValue)) );
return g_acMessageBuffer;
}
const char* GetHitPointsString( int a_iValue )
{
strcpy( g_acMessageBuffer, Translate("STAMINA: ") );
strcat( g_acMessageBuffer, Translate(FindString( g_ppcHitPoints, g_piHitPoints, a_iValue) ) );
return g_acMessageBuffer;
}
CNetworkMenu::CNetworkMenu(): Menu( "Network Play Setup" )
{
m_bOK = false;
m_bServer = g_oState.m_bServer;
m_sHostname = g_oState.m_acLatestServer;
m_sNick = g_oState.m_acNick;
AddMenuItem( "Find an opponent on MortalNet", SDLK_UNKNOWN, MENU_MORTALNET );
AddMenuItem( "START NETWORK GAME!", SDLK_UNKNOWN, MENU_CONNECT );
m_poNickMenuItem = AddTextMenuItem( "Nickname: ", m_sNick.c_str(), MENU_NICK );
AddEnumMenuItem( "Network mode: ", m_bServer ? 1 : 0, g_ppcServer, g_piServer, MENU_SERVER );
m_poServerMenuItem = AddTextMenuItem( "Connect to: ", m_sHostname.c_str(), MENU_HOSTNAME );
m_poServerMenuItem->SetEnabled(!m_bServer);
MenuItem* poItem = AddMenuItem( "Cancel", SDLK_UNKNOWN, MENU_CANCEL );
SDL_Rect oRect;
oRect.x = gamescreen->w - 150; oRect.w = 150;
oRect.y = gamescreen->h - 50; oRect.h = 30;
poItem->SetPosition( oRect );
}
CNetworkMenu::~CNetworkMenu() {}
void CNetworkMenu::Connect()
{
Clear();
SDL_Flip( gamescreen );
m_bOK = ::Connect( m_bServer ? NULL : m_sHostname.c_str() );
if ( g_oState.m_bQuitFlag || m_bOK )
{
m_bDone = true;
m_iReturnCode = 100;
}
Clear();
Draw();
}
void Menu::EnterName( const char* a_pcTitle, std::string& a_rsTarget, TextMenuItem* a_poMenuItem, int a_iMaxlen )
{
Clear();
Draw();
if ( a_iMaxlen > 255 ) a_iMaxlen = 255;
char acBuffer[256];
strncpy( acBuffer, a_rsTarget.c_str(), 255 );
acBuffer[255] = 0;
int y = m_oItems.size() * 40 + 100;
int x = DrawTextMSZ( a_pcTitle, impactFont, 20, y, 0, C_WHITE, gamescreen );
int iRetval;
{
CReadline oReadline( gamescreen, impactFont, acBuffer, strlen(acBuffer), a_iMaxlen,
- 20+x, y + sge_TTF_FontAscent(impactFont), 600, C_LIGHTCYAN, C_BLACK, 255 );
+ 20+x, y + sge_TTF_FontAscent(impactFont), gamescreen->w-40, C_LIGHTCYAN, C_BLACK, 255 );
iRetval = oReadline.Execute();
}
if ( iRetval == -1 )
{
m_bDone = true;
m_iReturnCode = 100;
g_oState.m_bQuitFlag = true;
}
if ( iRetval > 0 )
{
a_rsTarget = acBuffer;
a_poMenuItem->SetValue( acBuffer );
}
Clear();
Draw();
}
void CNetworkMenu::ItemActivated( int a_iItemCode, MenuItem* a_poMenuItem )
{
switch ( a_iItemCode )
{
case MENU_SERVER:
{
EnumMenuItem* poItem = (EnumMenuItem*) a_poMenuItem;
if ( m_bServer )
{
poItem->Decrement();
}
else
{
poItem->Increment();
}
break;
}
case MENU_CONNECT:
strcpy( g_oState.m_acNick, m_sNick.c_str() );
Connect();
break;
case MENU_CANCEL:
m_bOK = false;
m_bDone = true;
m_iReturnCode = -1;
break;
case MENU_HOSTNAME:
EnterName( "Server name: ", m_sHostname, m_poServerMenuItem,128 );
break;
case MENU_NICK:
EnterName( "Nickname: ", m_sNick, m_poNickMenuItem, 12 );
strcpy( g_oState.m_acNick, m_sNick.c_str() );
break;
case MENU_MORTALNET:
g_oState.m_enGameMode = SState::IN_CHAT;
m_bOK = false;
m_bDone = true;
m_iReturnCode = 100;
break;
}
}
void CNetworkMenu::ItemChanged( int a_iItemCode, int a_iValue, MenuItem* a_poMenuItem )
{
switch ( a_iItemCode )
{
case MENU_SERVER:
m_bServer = a_iValue;
m_poServerMenuItem->SetEnabled(!m_bServer);
break;
}
}
/***************************************************************************
MENUITEM DEFINITION
***************************************************************************/
MenuItem::MenuItem( Menu* a_poMenu, const char* a_pcUtf8Text, int a_iCode )
: m_sUtf8Text( a_pcUtf8Text )
{
m_poMenu = a_poMenu;
m_iCode = a_iCode;
m_oPosition.x = m_oPosition.y = 100;
m_oPosition.w = m_oPosition.h = 100;
m_bCenter = true;
m_iHighColor = C_WHITE;
m_iLowColor = C_LIGHTGRAY;
m_iInactiveColor = C_DARKGRAY;
m_iBackgroundColor = C_BLACK;
m_bActive = false;
m_bEnabled = true;
}
MenuItem::~MenuItem()
{
}
void MenuItem::Draw()
{
if ( NULL != poBackground )
{
SDL_BlitSurface( poBackground, &m_oPosition, gamescreen, &m_oPosition );
}
else
{
SDL_FillRect( gamescreen, &m_oPosition, 0 );
}
int iX = m_oPosition.x;
int iY = m_oPosition.y;
if ( m_bCenter )
{
iX += m_oPosition.w / 2;
}
DrawTextMSZ( m_sUtf8Text.c_str(), inkFont, iX, iY,
UseTilde | UseShadow | (m_bCenter ? AlignHCenter : 0),
m_bEnabled ? (m_bActive ? m_iHighColor : m_iLowColor) : m_iInactiveColor,
gamescreen );
SDL_UpdateRect( gamescreen, m_oPosition.x, m_oPosition.y, m_oPosition.w, m_oPosition.h );
}
void MenuItem::Clear()
{
// debug( "Clear: %d:%d %dx%d\n", m_oPosition.x, m_oPosition.y, m_oPosition.w, m_oPosition.h );
if (poBackground )
{
SDL_Rect oDest = m_oPosition;
SDL_BlitSurface( poBackground, &m_oPosition, gamescreen, &oDest );
}
else
{
SDL_FillRect( gamescreen, &m_oPosition, C_WHITE );
}
SDL_UpdateRect( gamescreen, m_oPosition.x, m_oPosition.y, m_oPosition.w, m_oPosition.h );
}
void MenuItem::Activate()
{
if ( m_poMenu )
{
m_poMenu->ItemActivated( m_iCode, this );
}
}
void MenuItem::SetText( const char* a_pcUtf8Text, bool a_bCenter )
{
m_sUtf8Text = a_pcUtf8Text;
m_bCenter = a_bCenter;
Draw();
}
void MenuItem::SetPosition( const SDL_Rect& a_roPosition )
{
m_oPosition = a_roPosition;
}
void MenuItem::SetActive( bool a_bActive )
{
if ( m_bActive == a_bActive )
{
return;
}
m_bActive = a_bActive;
Draw();
}
void MenuItem::SetEnabled( bool a_bEnabled )
{
if ( m_bEnabled == a_bEnabled )
{
return;
}
m_bEnabled = a_bEnabled;
Draw();
}
/***************************************************************************
ENUMMENUITEM DEFINITION
***************************************************************************/
EnumMenuItem::EnumMenuItem( Menu* a_poMenu, int a_iInitialValue, const char* a_pcUtf8Text, int a_iCode )
: MenuItem( a_poMenu, a_pcUtf8Text, a_iCode )
{
m_sUtf8Title = a_pcUtf8Text;
m_iMax = -1;
m_iValue = a_iInitialValue;
}
EnumMenuItem::~EnumMenuItem()
{
}
int EnumMenuItem::GetCurrentValue()
{
return m_iValue <= m_iMax ? m_piValues[m_iValue] : 0;
}
const char* EnumMenuItem::GetCurrentText()
{
return m_iValue <= m_iMax ? m_ppcNames[m_iValue] : "";
}
void EnumMenuItem::Draw()
{
m_sUtf8Text = Translate( m_sUtf8Title.c_str() );
if ( m_iValue <= m_iMax )
{
m_sUtf8Text += Translate(m_ppcNames[m_iValue]);
}
if ( m_iValue > 0 )
{
m_sUtf8Text = "< " + m_sUtf8Text;
}
if ( m_iValue < m_iMax )
{
m_sUtf8Text += " >";
}
MenuItem::Draw();
}
void EnumMenuItem::Increment()
{
if ( m_iValue < m_iMax )
{
++m_iValue;
Draw();
m_poMenu->ItemChanged( m_iCode, m_piValues[m_iValue], this );
Audio->PlaySample( "ding.voc" );
}
}
void EnumMenuItem::Decrement()
{
if ( m_iValue > 0 )
{
--m_iValue;
Draw();
m_poMenu->ItemChanged( m_iCode, m_piValues[m_iValue], this );
Audio->PlaySample( "ding.voc" );
}
}
void EnumMenuItem::SetEnumValues( const char ** a_ppcNames, const int * a_piValues )
{
m_ppcNames = a_ppcNames;
m_piValues = a_piValues;
int i;
bool bFoundValue = false;
for ( i=0; NULL != a_ppcNames[i]; ++i )
{
if ( !bFoundValue &&
m_iValue == a_piValues[i] )
{
bFoundValue = true;
m_iValue = i;
}
}
if ( !bFoundValue )
{
m_iValue = 0;
}
m_iMax = i-1;
}
+void EnumMenuItem::SetMaxValue( int a_iMaxValue )
+{
+ for ( int i=0; NULL != m_ppcNames[i]; ++i )
+ {
+ if ( m_piValues[i] == a_iMaxValue )
+ {
+ m_iMax = i;
+ break;
+ }
+ }
+
+ debug( "EnumMenuItem::SetMaxValue: value %d not found\n", a_iMaxValue );
+}
+
+
+
/***************************************************************************
TextMenuItem DEFINITION
***************************************************************************/
TextMenuItem::TextMenuItem( Menu* a_poMenu, const char* a_pcInitialValue, const char* a_pcUtf8Title, int a_iCode )
: MenuItem( a_poMenu, a_pcUtf8Title, a_iCode )
{
m_sTitle = a_pcUtf8Title;
m_sValue = a_pcInitialValue;
}
TextMenuItem::~TextMenuItem()
{
}
void TextMenuItem::Draw()
{
m_sUtf8Text = Translate( m_sTitle.c_str() );
m_sUtf8Text += m_sValue;
MenuItem::Draw();
}
void TextMenuItem::SetValue( const char* a_pcValue )
{
m_sValue = a_pcValue;
Draw();
}
/***************************************************************************
MENU DEFINITION
***************************************************************************/
Menu::Menu( const char* a_pcTitle )
: m_sTitle( a_pcTitle )
{
m_iCurrentItem = 0;
m_iReturnCode = -1;
m_bDone = false;
}
Menu::~Menu()
{
ItemIterator it;
for ( it = m_oItems.begin(); it != m_oItems.end(); ++it )
{
delete *it;
}
}
MenuItem* Menu::AddMenuItem( const char* a_pcUtf8Text, SDLKey a_tShortcut, int a_iCode )
{
MenuItem* poItem = new MenuItem( this, a_pcUtf8Text, a_iCode );
return AddMenuItem( poItem );
}
EnumMenuItem* Menu::AddEnumMenuItem( const char* a_pcUtf8Text, int a_iInitialValue,
const char** a_ppcNames, const int* a_piValues, int a_iCode )
{
EnumMenuItem* poItem = new EnumMenuItem( this, a_iInitialValue, a_pcUtf8Text, a_iCode );
poItem->SetEnumValues( a_ppcNames, a_piValues );
AddMenuItem( poItem );
return poItem;
}
TextMenuItem* Menu::AddTextMenuItem( const char* a_pcTitle, const char* a_pcValue, int a_iCode )
{
TextMenuItem* poItem = new TextMenuItem( this, a_pcValue, a_pcTitle, a_iCode );
AddMenuItem( poItem );
return poItem;
}
MenuItem* Menu::AddMenuItem( MenuItem* a_poItem )
{
m_oItems.push_back( a_poItem );
SDL_Rect oRect;
oRect.x = 0; oRect.w = gamescreen->w;
oRect.y = m_oItems.size() * 40 + 100;
oRect.h = sge_TTF_FontHeight( inkFont );
a_poItem->SetPosition( oRect );
return a_poItem;
}
void Menu::AddOkCancel( int a_iOkCode )
{
SDL_Rect oRect;
oRect.x = 0; oRect.w = 150;
oRect.y = gamescreen->h - 50; oRect.h = sge_TTF_FontHeight( inkFont );
MenuItem* poItem = AddMenuItem( "~OK", SDLK_o, a_iOkCode );
poItem->SetPosition( oRect );
// poItem = AddMenuItem( "Cancel", SDLK_UNKNOWN, 0 );
oRect.x = gamescreen->w - 150;
poItem->SetPosition( oRect );
}
+
+MenuItem* Menu::GetMenuItem( int a_iCode ) const
+{
+ ItemList::const_iterator it;
+ for ( it = m_oItems.begin(); it != m_oItems.end(); ++it )
+ {
+ if ( *it && (*it)->GetCode() == a_iCode )
+ {
+ return *it;
+ }
+ }
+ debug( "Couldn't find menu item %d\n", a_iCode );
+ return NULL;
+}
+
+
+
void Menu::InvokeSubmenu( Menu* a_poMenu )
{
Audio->PlaySample( "strange_button.voc" );
Clear();
m_iReturnCode = a_poMenu->Run();
if ( g_oState.m_bQuitFlag )
{
m_iReturnCode = 100;
m_bDone = true;
}
if ( m_iReturnCode < 0 )
{
Audio->PlaySample( "pop.voc" );
Draw();
}
else
{
m_iReturnCode --;
m_bDone = true;
}
}
void Menu::ItemActivated( int a_iItemCode, MenuItem* a_poMenuItem )
{
debug( "Menu::ItemActivated( %d )\n", a_iItemCode );
switch ( a_iItemCode )
{
case MENU_QUIT:
m_bDone = true;
m_iReturnCode = 100;
g_oState.m_bQuitFlag = true;
break;
case MENU_SURRENDER:
m_bDone = true;
m_iReturnCode = 100;
if ( SState::IN_NETWORK == g_oState.m_enGameMode )
{
g_poNetwork->Stop();
}
g_oState.m_enGameMode = SState::IN_DEMO;
break;
case MENU_NETWORK_GAME:
{
Menu* poMenu = new CNetworkMenu();
InvokeSubmenu( poMenu );
delete poMenu;
break;
}
-
+
case MENU_MULTI_PLAYER:
+ {
+ Menu* poMenu = new Menu( "Multi Player" );
+ poMenu->AddMenuItem( "START GAME", SDLK_UNKNOWN, MENU_MULTI_PLAYER_START );
+ //poMenu->AddEnumMenuItem( "Number of Players: ", g_oState.m_iNumPlayers, g_ppcNumPlayers, g_piNumPlayers, MENU_NUM_PLAYERS );
+ poMenu->AddEnumMenuItem( "Team mode: ", g_oState.m_enTeamMode, g_ppcTeamMode, g_piTeamMode, MENU_TEAM_MODE );
+ poMenu->AddEnumMenuItem( "Team size: ", g_oState.m_iTeamSize, g_ppcTeamSize, g_piTeamSize, MENU_TEAM_SIZE )
+ ->SetEnabled( SState::Team_CUSTOM == g_oState.m_enTeamMode );
+ poMenu->AddEnumMenuItem( "Cloning allowed: ", g_oState.m_bTeamMultiselect, g_ppcYesNo, g_piYesNo, MENU_TEAM_MULTISELECT )
+ ->SetEnabled( SState::Team_CUSTOM == g_oState.m_enTeamMode );
+ InvokeSubmenu( poMenu );
+ delete poMenu;
+ break;
+ }
+
+ case MENU_MULTI_PLAYER_START:
m_bDone = true;
m_iReturnCode = 100;
g_oState.m_enGameMode = SState::IN_MULTI;
break;
case MENU_FULLSCREEN:
Audio->PlaySample( "strange_button.voc" );
g_oState.ToggleFullscreen();
if ( NULL != poBackground )
{
SDL_BlitSurface( poBackground, NULL, gamescreen, NULL );
}
else
{
SDL_FillRect( gamescreen, NULL, 0 );
}
a_poMenuItem->SetText( g_oState.m_bFullscreen ? "~FULLSCREEN ON" : "~FULLSCREEN OFF", true );
Draw();
break;
case MENU_OPTIONS_OK:
m_bDone = true;
m_iReturnCode = -1;
break;
case MENU_OPTIONS:
{
Menu* poMenu = new Menu( "Options" );
if ( g_oState.m_enGameMode != SState::IN_NETWORK || g_poNetwork->IsMaster() )
{
poMenu->AddEnumMenuItem( "GAME SPEED: ", g_oState.m_iGameSpeed, g_ppcGameSpeed, g_piGameSpeed, MENU_GAME_SPEED );
poMenu->AddEnumMenuItem( "GAME TIME: ", g_oState.m_iGameTime, g_ppcGameTime, g_piGameTime, MENU_GAME_TIME );
poMenu->AddEnumMenuItem( "STAMINA: ", g_oState.m_iHitPoints, g_ppcHitPoints, g_piHitPoints, MENU_TOTAL_HIT_POINTS );
}
poMenu->AddMenuItem( "~SOUND", SDLK_s, MENU_SOUND );
poMenu->AddMenuItem( g_oState.m_bFullscreen ? "~FULLSCREEN ON" : "~FULLSCREEN OFF", SDLK_f, MENU_FULLSCREEN );
poMenu->AddMenuItem( "~RIGHT PLAYER KEYS", SDLK_r, MENU_KEYS_RIGHT );
poMenu->AddMenuItem( "~LEFT PLAYER KEYS", SDLK_l, MENU_KEYS_LEFT );
poMenu->AddOkCancel( MENU_OPTIONS_OK );
InvokeSubmenu( poMenu );
delete poMenu;
break;
}
case MENU_SOUND:
{
Menu* poMenu = new Menu( "Sound" );
poMenu->AddEnumMenuItem( "CHANNELS: ", 1, g_ppcChannels, g_piChannels, MENU_CHANNELS )->SetEnabled(false);
poMenu->AddEnumMenuItem( "SOUND QUALITY: ", 2, g_ppcMixingRate, g_piMixingRate, MENU_MIXING_RATE )->SetEnabled(false);
poMenu->AddEnumMenuItem( "SOUND FIDELITY: ", 2, g_ppcMixingBits, g_piMixingBits, MENU_BITS )->SetEnabled(false);
poMenu->AddEnumMenuItem( "MUSIC VOLUME: ", g_oState.m_iMusicVolume, g_ppcVolume, g_piVolume, MENU_MUSIC_VOLUME );
poMenu->AddEnumMenuItem( "EFFECTS VOLUME: ", g_oState.m_iSoundVolume, g_ppcVolume, g_piVolume, MENU_SOUND_VOLUME );
poMenu->AddOkCancel( MENU_SOUND_OK );
InvokeSubmenu( poMenu );
delete poMenu;
break;
}
case MENU_SOUND_OK:
m_bDone = true;
m_iReturnCode = -1;
break;
case MENU_KEYS_LEFT:
InputKeys(1);
Draw();
break;
case MENU_KEYS_RIGHT:
InputKeys(0);
Draw();
break;
default:
break;
}
}
void Menu::ItemChanged( int a_iItemCode, int a_iValue, MenuItem* a_poMenuItem )
{
debug( "Menu::ItemChanged( %d, %d )\n", a_iItemCode, a_iValue );
switch ( a_iItemCode )
{
+ case MENU_NUM_PLAYERS:
+ g_oState.m_iNumPlayers = a_iValue;
+ break;
+
+ case MENU_TEAM_MODE:
+ {
+ g_oState.m_enTeamMode = (SState::TTeamModeEnum) a_iValue;
+ MenuItem* poItem;
+ poItem = GetMenuItem( MENU_TEAM_SIZE );
+ if ( poItem ) poItem->SetEnabled( SState::Team_CUSTOM==g_oState.m_enTeamMode );
+ poItem = GetMenuItem( MENU_TEAM_MULTISELECT );
+ if ( poItem ) poItem->SetEnabled( SState::Team_CUSTOM==g_oState.m_enTeamMode );
+ break;
+ }
+
+ case MENU_TEAM_SIZE:
+ g_oState.m_iTeamSize = a_iValue;
+ break;
+
+ case MENU_TEAM_MULTISELECT:
+ g_oState.m_bTeamMultiselect = a_iValue;
+ break;
+
case MENU_MUSIC_VOLUME:
g_oState.m_iMusicVolume = a_iValue;
Audio->SetMusicVolume( a_iValue );
break;
case MENU_SOUND_VOLUME:
g_oState.m_iSoundVolume = a_iValue;
break;
case MENU_GAME_TIME:
g_oState.m_iGameTime = a_iValue;
break;
case MENU_GAME_SPEED:
g_oState.m_iGameSpeed = a_iValue;
break;
case MENU_TOTAL_HIT_POINTS:
g_oState.m_iHitPoints = a_iValue;
break;
case MENU_LANGUAGE:
g_oState.SetLanguage( g_ppcLanguageCodes[ a_iValue ] );
Clear();
Draw();
break;
} // end of switch a_iItemCode
}
/** Run executes the menus, maybe invoking submenus as well. The
menus modify the global game state.
Returns 0, or the number of parent menus that should be cleared. */
int Menu::Run()
{
if ( m_oItems[m_iCurrentItem]->GetEnabled() )
{
m_oItems[m_iCurrentItem]->SetActive(true);
}
else
{
FocusNext();
}
Draw();
while ( !m_bDone )
{
if ( g_oState.m_bQuitFlag )
{
m_bDone = true;
m_iReturnCode = -1;
break;
}
SDLKey enKey = GetKey( true );
if ( g_oState.m_bQuitFlag ||
SDLK_ESCAPE == enKey )
{
m_bDone = true;
m_iReturnCode = -1;
break;
}
switch ( enKey )
{
case SDLK_UP:
{
FocusPrev();
break;
} // end of SDLK_UP
case SDLK_DOWN:
{
FocusNext();
break;
} // end of SDLK_DOWN
case SDLK_LEFT:
{
MenuItem* poItem = m_oItems[m_iCurrentItem];
poItem->Decrement();
break;
}
case SDLK_RIGHT:
{
MenuItem* poItem = m_oItems[m_iCurrentItem];
poItem->Increment();
break;
}
case SDLK_RETURN:
{
MenuItem* poItem = m_oItems[m_iCurrentItem];
if ( poItem->GetEnabled() )
{
poItem->Activate();
}
}
default:
break;
} // end of switch
}
Clear();
return m_iReturnCode;
}
void Menu::Draw()
{
DrawGradientText( m_sTitle.c_str(), titleFont, 20, gamescreen );
for ( ItemIterator it=m_oItems.begin(); it!=m_oItems.end(); ++it )
{
(*it)->Draw();
}
SDL_Flip( gamescreen );
}
void Menu::FocusNext()
{
MenuItem* poItem = NULL;
int iNextItem;
for ( iNextItem = m_iCurrentItem+1; iNextItem < (int) m_oItems.size(); ++iNextItem )
{
poItem = m_oItems[iNextItem];
if ( poItem->GetEnabled() )
{
break;
}
poItem = NULL;
}
if ( NULL != poItem )
{
Audio->PlaySample("strange_quack.voc");
m_oItems[m_iCurrentItem]->SetActive(false);
m_oItems[iNextItem]->SetActive(true);
m_iCurrentItem = iNextItem;
}
}
void Menu::FocusPrev()
{
MenuItem* poItem = NULL;
int iPrevItem;
for ( iPrevItem = m_iCurrentItem-1; iPrevItem >= 0; --iPrevItem )
{
poItem = m_oItems[iPrevItem];
if ( poItem->GetEnabled() )
{
break;
}
poItem = NULL;
}
if ( NULL != poItem )
{
Audio->PlaySample("strange_quack.voc");
m_oItems[m_iCurrentItem]->SetActive(false);
m_oItems[iPrevItem]->SetActive(true);
m_iCurrentItem = iPrevItem;
}
}
void Menu::Clear()
{
if (poBackground)
{
SDL_BlitSurface( poBackground, 0, gamescreen, 0 );
}
else
{
SDL_FillRect( gamescreen, NULL, 0 );
}
}
void MakeMenuBackground()
{
if ( poBackground != NULL )
{
return;
}
poBackground = SDL_ConvertSurface( gamescreen, gamescreen->format, SDL_SWSURFACE );
if ( NULL == poBackground )
{
debug( "DoMenu: Couldn't allocate background.\n" );
+ return;
}
- else
+
+ if ( gamescreen->format->BitsPerPixel <= 8 )
{
int i;
SDL_Rect oRect;
oRect.x = 0; oRect.w = poBackground->w; oRect.h = 1;
for ( i=0; i<poBackground->h; i += 2 )
{
oRect.y = i;
SDL_FillRect( poBackground, &oRect, C_BLACK );
}
oRect.w = 1; oRect.y = 0; oRect.h = poBackground->h;
for ( i=0; i<poBackground->w; i+=2 )
{
oRect.x = i;
SDL_FillRect(poBackground, &oRect, C_BLACK );
}
-
- SDL_BlitSurface( poBackground, 0, gamescreen, 0 );
- SDL_Flip( gamescreen );
}
+ else
+ {
+ sge_FilledRectAlpha( poBackground, 0, 0, poBackground->w, poBackground->h, C_BLACK, 128 );
+ sge_FilledRectAlpha( poBackground, 0, 0, poBackground->w, poBackground->h, C_BLUE, 64 );
+ }
+
+ SDL_BlitSurface( poBackground, 0, gamescreen, 0 );
+ SDL_Flip( gamescreen );
}
void DoMenu( Menu& a_roMenu )
{
Audio->PlaySample( "crashhh.voc" );
MakeMenuBackground();
a_roMenu.Clear();
a_roMenu.Draw();
a_roMenu.Run();
if ( !g_oState.m_bQuitFlag )
{
Audio->PlaySample("shades_rollup.voc");
}
if ( NULL != poBackground )
{
SDL_FreeSurface( poBackground );
poBackground = NULL;
}
}
void DoMenu()
{
MakeMenuBackground();
Menu oMenu( "Main Menu" );
if ( SState::IN_DEMO == g_oState.m_enGameMode )
{
oMenu.AddMenuItem( "~SINGLE PLAYER GAME", SDLK_s, MENU_SINGLE_PLAYER )->SetEnabled(false);
oMenu.AddMenuItem( "~NETWORK GAME", SDLK_n, MENU_NETWORK_GAME );
oMenu.AddMenuItem( "~MULTI PLAYER GAME", SDLK_m, MENU_MULTI_PLAYER );
}
else
{
oMenu.AddMenuItem( "~SURRENDER GAME", SDLK_s, MENU_SURRENDER );
}
oMenu.AddEnumMenuItem( "~LANGUAGE: ", g_oState.m_iLanguageCode, g_ppcLanguage, g_piLanguage, MENU_LANGUAGE );
oMenu.AddMenuItem( "~OPTIONS", SDLK_o, MENU_OPTIONS );
oMenu.AddMenuItem( "~INFO", SDLK_i, MENU_INFO )->SetEnabled(false);
oMenu.AddMenuItem( "QUIT", SDLK_UNKNOWN, MENU_QUIT );
DoMenu( oMenu );
}
diff --git a/src/menu.h b/src/menu.h
index 4d3b7db..f4c837f 100644
--- a/src/menu.h
+++ b/src/menu.h
@@ -1,210 +1,220 @@
/***************************************************************************
menu.h - description
-------------------
begin : Sun Aug 3 2003
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#ifndef MENU_H
#define MENU_H
#include <string>
#include <vector>
class MenuItem;
class EnumMenuItem;
class TextMenuItem;
enum
{
/* Master menu structure:
MAIN MENU
*/
MENU_UNKNOWN,
MENU_SURRENDER,
MENU_SINGLE_PLAYER,
MENU_EASY,
MENU_MEDIUM,
MENU_HARD,
MENU_MULTI_PLAYER,
+ MENU_MULTI_PLAYER_START,
+ MENU_NUM_PLAYERS, // 2-4
+ MENU_TEAM_MODE, // 1 vs 1, good vs evil, evil vs good, custom
+ MENU_TEAM_SIZE, // 2-10
+ MENU_TEAM_MULTISELECT, // yes / no
MENU_NETWORK_GAME,
MENU_SERVER,
MENU_HOSTNAME,
MENU_NICK,
MENU_CONNECT,
MENU_MORTALNET,
MENU_CANCEL,
MENU_OPTIONS,
MENU_GAME_SPEED,
MENU_GAME_TIME, // ( :30 - 5:00 )
MENU_TOTAL_HIT_POINTS, // ( 25 - 1000 )
MENU_SOUND,
MENU_CHANNELS, // MONO / STEREO
MENU_MIXING_RATE, // 11kHz / 22kHz / 44.1 kHz
MENU_BITS, // 8 bit / 16 bit
MENU_MUSIC_VOLUME, // (0% - 100%)
MENU_SOUND_VOLUME, // (0% - 100%)
MENU_SOUND_OK,
MENU_FULLSCREEN,
MENU_KEYS_RIGHT,
MENU_KEYS_LEFT,
MENU_OPTIONS_OK,
MENU_LANGUAGE,
MENU_INFO,
MENU_QUIT, // (confirm)
};
class Menu
{
+
public:
Menu( const char* a_pcTitle );
virtual ~Menu();
- virtual MenuItem* AddMenuItem( const char* a_pcUtf8Text, SDLKey a_tShortcut = SDLK_UNKNOWN, int a_iCode = 0 );
- virtual EnumMenuItem* AddEnumMenuItem( const char* a_pcUtf8Text, int a_iInitialValue,
- const char** a_ppcNames, const int* a_piValues, int a_iCode = 0 );
- virtual TextMenuItem* AddTextMenuItem( const char* a_pcTitle, const char* a_pcValue, int a_iCode = 0 );
- virtual MenuItem* AddMenuItem( MenuItem* a_poItem );
- virtual void AddOkCancel( int a_iOkCode = 0 );
- virtual void ItemActivated( int a_iItemCode, MenuItem* a_poMenuItem );
- virtual void ItemChanged( int a_iItemCode, int a_iValue, MenuItem* a_poMenuItem );
- virtual int Run();
+ virtual MenuItem* AddMenuItem( const char* a_pcUtf8Text, SDLKey a_tShortcut = SDLK_UNKNOWN, int a_iCode = 0 );
+ virtual EnumMenuItem* AddEnumMenuItem( const char* a_pcUtf8Text, int a_iInitialValue,
+ const char** a_ppcNames, const int* a_piValues, int a_iCode = 0 );
+ virtual TextMenuItem* AddTextMenuItem( const char* a_pcTitle, const char* a_pcValue, int a_iCode = 0 );
+ virtual MenuItem* AddMenuItem( MenuItem* a_poItem );
+ virtual void AddOkCancel( int a_iOkCode = 0 );
+ virtual MenuItem* GetMenuItem( int a_iCode ) const;
- virtual void Draw();
- virtual void Clear();
- virtual void EnterName( const char* a_pcTitle, std::string& a_rsTarget, TextMenuItem* a_poMenuItem, int a_iMaxlen );
+ virtual void ItemActivated( int a_iItemCode, MenuItem* a_poMenuItem );
+ virtual void ItemChanged( int a_iItemCode, int a_iValue, MenuItem* a_poMenuItem );
+ virtual int Run();
+
+ virtual void Draw();
+ virtual void Clear();
+ virtual void EnterName( const char* a_pcTitle, std::string& a_rsTarget, TextMenuItem* a_poMenuItem, int a_iMaxlen );
protected:
- virtual void FocusNext();
- virtual void FocusPrev();
- virtual void InvokeSubmenu( Menu* a_poSubmenu );
+ virtual void FocusNext();
+ virtual void FocusPrev();
+ virtual void InvokeSubmenu( Menu* a_poSubmenu );
typedef std::vector<MenuItem*> ItemList;
typedef ItemList::iterator ItemIterator;
- std::string m_sTitle;
- ItemList m_oItems;
- int m_iCurrentItem;
- int m_iReturnCode;
- bool m_bDone;
+ std::string m_sTitle;
+ ItemList m_oItems;
+ int m_iCurrentItem;
+ int m_iReturnCode;
+ bool m_bDone;
};
class MenuItem
{
public:
MenuItem( Menu* a_poMenu, const char* a_pcUtf8Text, int a_iCode = -1 );
virtual ~MenuItem();
virtual void Draw();
virtual void Clear();
virtual void Activate();
virtual void Increment() {};
virtual void Decrement() {};
virtual void SetText( const char* a_pcUtf8Text, bool a_bCenter );
virtual void SetPosition( const SDL_Rect& a_roPosition );
virtual void SetActive( bool a_bActive );
virtual void SetEnabled( bool a_bEnabled );
- virtual bool GetEnabled() { return m_bEnabled; }
+ virtual bool GetEnabled() const { return m_bEnabled; }
+ virtual int GetCode() const { return m_iCode; }
protected:
Menu* m_poMenu;
// appearance
std::string m_sUtf8Text;
SDL_Rect m_oPosition;
bool m_bCenter;
Uint32 m_iHighColor;
Uint32 m_iLowColor;
Uint32 m_iInactiveColor;
Uint32 m_iBackgroundColor;
// data content
int m_iCode;
bool m_bActive;
bool m_bEnabled;
};
class EnumMenuItem: public MenuItem
{
public:
EnumMenuItem( Menu* a_poMenu, int a_iInitialValue, const char* a_pcUtf8Text, int a_iCode = -1 );
virtual ~EnumMenuItem();
int GetCurrentValue();
const char* GetCurrentText();
virtual void Draw();
virtual void Increment();
virtual void Decrement();
virtual void SetEnumValues( const char ** a_ppcNames, const int * a_piValues );
+ virtual void SetMaxValue( int a_iMaxValue );
protected:
int m_iValue;
int m_iMax;
std::string m_sUtf8Title;
const char** m_ppcNames;
const int* m_piValues;
};
class TextMenuItem: public MenuItem
{
public:
TextMenuItem( Menu* a_poMenu, const char* a_pcInitialValue, const char* a_pcUtf8Title, int a_iCode );
virtual ~TextMenuItem();
virtual void Draw();
virtual void SetValue( const char* a_pcValue );
protected:
std::string m_sTitle;
std::string m_sValue;
};
class CNetworkMenu: public Menu
{
public:
CNetworkMenu();
- ~CNetworkMenu();
+ virtual ~CNetworkMenu();
void Connect();
void ItemActivated( int a_iItemCode, MenuItem* a_poMenuItem );
void ItemChanged( int a_iItemCode, int a_iValue, MenuItem* a_poMenuItem );
protected:
bool m_bOK;
bool m_bServer;
std::string m_sHostname;
std::string m_sNick;
TextMenuItem* m_poServerMenuItem;
TextMenuItem* m_poNickMenuItem;
};
void DoMenu();
void DoMenu( Menu& a_roMenu );
#endif

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jun 16, 12:30 AM (2 w, 1 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
71370
Default Alt Text
(295 KB)

Event Timeline