Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
318 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 27861dc..75f63b9 100644
--- a/src/Audio.cpp
+++ b/src/Audio.cpp
@@ -1,318 +1,322 @@
/***************************************************************************
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>
#include <fstream>
-typedef std::map<std::string,Mix_Chunk*> SampleMap;
-typedef std::map<std::string,Mix_Music*> MusicMap;
-typedef SampleMap::iterator SampleMapIterator;
-typedef MusicMap::iterator MusicMapIterator;
+typedef std::map<std::string,Mix_Chunk*> CSampleMap;
+typedef std::map<std::string,Mix_Music*> CMusicMap;
+typedef CSampleMap::iterator CSampleMapIterator;
+typedef CMusicMap::iterator CMusicMapIterator;
-MszAudio* Audio = NULL;
+COpenMortalAudio* Audio = NULL;
+
+/**
+\ingroup Media
+*/
-class MszAudioPriv
+class COpenMortalAudioPriv
{
public:
bool m_bAudioOk;
int m_iNumChannels;
- SampleMap m_oSamples;
- MusicMap m_oMusics;
+ CSampleMap m_oSamples;
+ CMusicMap m_oMusics;
std::string m_sSoundDir;
};
#define SELF (*m_poPriv)
#define CHECKOK if ( ! m_poPriv->m_bAudioOk ) return;
-MszAudio::MszAudio()
+COpenMortalAudio::COpenMortalAudio()
{
Audio = this;
- m_poPriv = new MszAudioPriv;
+ m_poPriv = new COpenMortalAudioPriv;
m_poPriv->m_bAudioOk = false;
m_poPriv->m_iNumChannels = 0;
m_poPriv->m_sSoundDir = DATADIR;
m_poPriv->m_sSoundDir += "/sound/";
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(10);
LoadSampleMap();
}
-MszAudio::~MszAudio()
+COpenMortalAudio::~COpenMortalAudio()
{
delete m_poPriv;
m_poPriv = NULL;
}
-void MszAudio::LoadSampleMap()
+void COpenMortalAudio::LoadSampleMap()
{
CHECKOK;
std::string sFilename = m_poPriv->m_sSoundDir + "soundmap.txt";
std::ifstream oInput( sFilename.c_str() );
if ( !oInput.is_open() )
{
debug( "File %d could not be read.\n", sFilename.c_str() );
return;
}
std::string sLine;
while ( !oInput.eof() )
{
std::getline( oInput, sLine );
if ( sLine.size() == 0 ) continue;
size_t iFirstChar = sLine.find_first_not_of( " \t\r\n" );
if ( sLine.npos == iFirstChar
|| sLine[iFirstChar] == '#' )
{
// Empty line or comment
continue;
}
size_t iTitleEnd = sLine.find_first_of( " \t\r\n", iFirstChar );
if ( sLine.npos == iTitleEnd )
{
// Bad line
debug( "Bad line in config file: %s\n", sLine.c_str() );
continue;
}
size_t iFilenameStart = sLine.find_first_not_of( " \t\r\n", iTitleEnd );
if ( sLine.npos == iTitleEnd )
{
// Bad line
debug( "Bad line in config file: %s\n", sLine.c_str() );
continue;
}
size_t iFilenameEnd = sLine.find_last_not_of( " \t\r\n" );
std::string sSampleName = sLine.substr( iFirstChar, iTitleEnd-iFirstChar );
std::string sSampleFile = sLine.substr( iFilenameStart, iFilenameEnd - iFilenameStart + 1 );
debug( "MAPPING: '%s' => '%s'\n", sSampleName.c_str(), sSampleFile.c_str() );
LoadSample( sSampleFile.c_str(), sSampleName.c_str() );
}
oInput.close();
}
-void MszAudio::LoadSample( const char* a_pcFilename, const char* a_pcSampleName )
+void COpenMortalAudio::LoadSample( const char* a_pcFilename, const char* a_pcSampleName )
{
CHECKOK;
std::string sSampleName( a_pcSampleName ? a_pcSampleName : a_pcFilename );
std::string sSampleFile = m_poPriv->m_sSoundDir + 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 )
+void COpenMortalAudio::UnloadSample( const char* a_pcSampleName )
{
- SampleMapIterator it = m_poPriv->m_oSamples.find( a_pcSampleName );
+ CSampleMapIterator 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 )
+void COpenMortalAudio::PlaySample( const char* a_pcSampleName )
{
Mix_Chunk* poSample;
- SampleMapIterator it = m_poPriv->m_oSamples.find(a_pcSampleName);
+ CSampleMapIterator 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 )
+void COpenMortalAudio::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 )
+void COpenMortalAudio::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 )
+void COpenMortalAudio::UnloadMusic( const char* a_pcMusicName )
{
- MusicMapIterator it = m_poPriv->m_oMusics.find( a_pcMusicName );
+ CMusicMapIterator 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 )
+void COpenMortalAudio::PlayMusic( const char* a_pcMusicName )
{
- MusicMapIterator it = m_poPriv->m_oMusics.find( a_pcMusicName );
+ CMusicMapIterator 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 )
+void COpenMortalAudio::FadeMusic( int a_iMilliSec )
{
Mix_FadeOutMusic( a_iMilliSec );
}
-void MszAudio::SetMusicVolume( int a_iVolume )
+void COpenMortalAudio::SetMusicVolume( int a_iVolume )
{
Mix_VolumeMusic( a_iVolume * 128 / 100 );
}
-void MszAudio::StopMusic()
+void COpenMortalAudio::StopMusic()
{
Mix_HaltMusic();
}
-bool MszAudio::IsMusicPlaying()
+bool COpenMortalAudio::IsMusicPlaying()
{
- return Mix_PlayingMusic();
+ return Mix_PlayingMusic() != 0;
}
diff --git a/src/Audio.h b/src/Audio.h
index 94b9db6..b630c71 100644
--- a/src/Audio.h
+++ b/src/Audio.h
@@ -1,56 +1,63 @@
/***************************************************************************
Audio.h - description
-------------------
begin : Sat Jul 26 2003
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
-#ifndef MSZ_AUDIO_H
-#define MSZ_AUDIO_H
+#ifndef OPENMORTAL_AUDIO_H
+#define OPENMORTAL_AUDIO_H
#ifndef NULL
#define NULL 0
#endif
-class MszAudioPriv;
+class COpenMortalAudioPriv;
+/**
+\class COpenMortalAudio
+\brief Audio services for OpenMortal (sound and music).
+\ingroup Media
+
+This class is a wrapper around SDL_Mixer
+*/
-class MszAudio
+class COpenMortalAudio
{
public:
- MszAudio();
- ~MszAudio();
+ COpenMortalAudio();
+ ~COpenMortalAudio();
void LoadSampleMap();
public:
// Sample related methods
void LoadSample( const char* a_pcFilename, const char* a_pcSampleName=NULL );
void UnloadSample( const char* a_pcSampleName );
void PlaySample( const char* a_pcSampleName );
void PlayFile( const char* a_pcFilename );
// Music related methods
void LoadMusic( const char* a_pcFilename, const char* a_pcMusicName=NULL );
void UnloadMusic( const char* a_pcMusicName );
void PlayMusic( const char* a_pcMusicName );
void FadeMusic( int a_iMilliSec );
void SetMusicVolume( int a_iVolume );
void StopMusic();
bool IsMusicPlaying();
protected:
// Attributes
- MszAudioPriv* m_poPriv;
+ COpenMortalAudioPriv* m_poPriv;
};
-extern MszAudio* Audio;
+extern COpenMortalAudio* Audio;
-#endif // ifdef MSZ_AUDIO_H
+#endif // ifdef OPENMORTAL_AUDIO_H
diff --git a/src/Backend.cpp b/src/Backend.cpp
index a28bfab..f38c115 100644
--- a/src/Backend.cpp
+++ b/src/Backend.cpp
@@ -1,514 +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;
+CBackend g_oBackend;
/***************************************************************************
PRIVATE VARIABLES (perl variable space)
***************************************************************************/
SV
*perl_bgx, *perl_bgy,
*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()
+CBackend::CBackend()
{
m_iBgX = m_iBgY = 0;
m_iNumDoodads = m_iNumSounds = 0;
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()
+CBackend::~CBackend()
{
if ( NULL != my_perl )
{
perl_destruct( my_perl );
perl_free( my_perl );
my_perl = NULL;
}
}
-bool Backend::Construct()
+bool CBackend::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, 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, ... )
+const char* CBackend::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;
eval_pv(acBuffer,FALSE);
const char *pcError = SvPV_nolen(get_sv("@", FALSE));
if ( pcError && *pcError )
{
debug( "eval '%s': '%s'\n", acBuffer, pcError );
exit(0);
}
va_end( ap );
return pcError;
}
-const char* Backend::GetPerlString( const char* acScalarName )
+const char* CBackend::GetPerlString( const char* acScalarName )
{
SV* poScalar = get_sv( acScalarName, FALSE );
if ( NULL == poScalar )
{
return "";
}
return SvPV_nolen( poScalar );
}
-int Backend::GetPerlInt( const char* acScalarName )
+int CBackend::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()
+int CBackend::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 )
+FighterEnum CBackend::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()
+int CBackend::GetNumberOfAvailableFighters()
{
PerlEvalF( "GetNumberOfAvailableFighters();" ); // Defined in FighterStats.pl
return GetPerlInt( "CppNumberOfAvailableFighters" );
}
-void Backend::AdvancePerl()
+void CBackend::AdvancePerl()
{
PerlEvalF("GameAdvance();");
}
-void Backend::ReadFromPerl()
+void CBackend::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_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_iGameOver = SvIV( perl_over );
- m_bKO = SvIV( perl_ko );
+ m_bKO = SvIV( perl_ko ) != 0;
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 )
+bool CBackend::IsDead( int a_iPlayer )
{
return m_aoPlayers[ a_iPlayer ].m_iRealHitPoints <= 0;
}
-void Backend::PlaySounds()
+void CBackend::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 )
+void CBackend::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 )
+void CBackend::ReadFromString( const std::string& a_rsString )
{
ReadFromString( a_rsString.c_str() );
}
-void Backend::ReadFromString( const char* a_pcBuffer )
+void CBackend::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 f4f9beb..e4a3063 100644
--- a/src/Backend.h
+++ b/src/Backend.h
@@ -1,86 +1,95 @@
/***************************************************************************
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;
+class CRlePack;
#define MAXDOODADS 20
#define MAXSOUNDS 20
-
-class Backend
+
+/**
+\class CBackend
+\ingroup GameLogic
+\brief The CBackend class provides access to the perl game engine.
+
+The backend maintains just about all game-relevant data, such as fighters,
+player information, doodads, current game state, and so forth. CBackend
+provides access for the frontend to the backend's variables and functions.
+Some of this is done via custom methods (such as GetNumberOfFighters()),
+but certain functions are only available via the "generic" perl interface,
+PerlEvalF().
+*/
+
+class CBackend
{
public:
// Lifecycle
- Backend();
- ~Backend();
+ CBackend();
+ ~CBackend();
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_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;
-
-
-
+extern CBackend g_oBackend;
-int DoGame( char* replay, bool isReplay, bool bDebug );
#endif
diff --git a/src/Background.cpp b/src/Background.cpp
index f6a47be..5a3df8f 100644
--- a/src/Background.cpp
+++ b/src/Background.cpp
@@ -1,167 +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()
+CBackground::CBackground()
{
m_bOK = false;
m_iNumber = 0;
m_iFirstExtraLayer = 0;
}
-Background::~Background()
+CBackground::~CBackground()
{
Clear();
}
-void Background::Clear()
+void CBackground::Clear()
{
- for( LayerIterator it=m_aLayers.begin(); it!=m_aLayers.end(); ++it )
+ for( CLayerIterator it=m_aLayers.begin(); it!=m_aLayers.end(); ++it )
{
- BackgroundLayer& roLayer = *it;
+ SBackgroundLayer& 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 )
+void CBackground::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.jpg", a_iBackgroundNumber );
SDL_Surface* poImage = LoadBackground( acFilename, 64 );
if ( NULL == poImage )
{
// Couldn't load background.
return;
}
- BackgroundLayer oLayer;
+ SBackgroundLayer 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;
+ SBackgroundLayer 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 )
+void CBackground::AddExtraLayer( const SBackgroundLayer& a_roLayer )
{
m_aLayers.push_back( a_roLayer );
}
-void Background::DeleteExtraLayers()
+void CBackground::DeleteExtraLayers()
{
while ( m_aLayers.size() > m_iFirstExtraLayer )
{
SDL_FreeSurface( m_aLayers.back().m_poSurface );
m_aLayers.pop_back();
}
}
-bool Background::IsOK()
+bool CBackground::IsOK()
{
return m_bOK;
}
-void Background::Draw( int a_iXPosition, int a_iYPosition, int a_iYOffset )
+void CBackground::Draw( int a_iXPosition, int a_iYPosition, int a_iYOffset )
{
- for ( LayerIterator it = m_aLayers.begin(); it != m_aLayers.end(); ++it )
+ for ( CLayerIterator it = m_aLayers.begin(); it != m_aLayers.end(); ++it )
{
- BackgroundLayer& roLayer = *it;
+ SBackgroundLayer& 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 ) + a_iYOffset,
gamescreen->w*3 + 100, gamescreen->h + 100 );
}
}
diff --git a/src/Background.h b/src/Background.h
index 428364c..22c5c26 100644
--- a/src/Background.h
+++ b/src/Background.h
@@ -1,49 +1,67 @@
/***************************************************************************
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 SDL_Surface;
+
+/**
+\class CBackground
+\ingroup GameLogic
+\brief The CBackground class draws the multi-layered background of each arena.
+
+The backgrounds are identified by their number. Single-layer backgrounds do
+not have description files. Multi-layer backgrounds have a description file
+which has the following format:
+\li First line: number of layers(int)
+\li For each layer: First line: filename (relative to gfx directory)
+\li For each layer: Second line: x-displacement(int) y-displacement(int) distance(double)
+
+Extra layers can be added to the background. These are for dead fighters in
+team game mode.
+*/
-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
+class CBackground
{
+public:
+ struct SBackgroundLayer
+ {
+ SDL_Surface* m_poSurface;
+ int m_iXOffset;
+ int m_iYOffset;
+ double m_dDistance;
+ };
+
+private:
+ typedef std::vector<SBackgroundLayer> CLayerVector;
+ typedef CLayerVector::iterator CLayerIterator;
+
+ void Clear();
+
public:
- Background();
- ~Background();
+ CBackground();
+ ~CBackground();
- void Clear();
- void Load( int a_iBackgroundNumber );
- void AddExtraLayer( const BackgroundLayer& a_roLayer );
- void DeleteExtraLayers();
+ void Load( int a_iBackgroundNumber );
+ void AddExtraLayer( const SBackgroundLayer& a_roLayer );
+ void DeleteExtraLayers();
- bool IsOK();
- void Draw( int a_iXPosition, int a_iYPosition, int a_iYOffset );
+ bool IsOK();
+ void Draw( int a_iXPosition, int a_iYPosition, int a_iYOffset );
protected:
- int m_iNumber;
- int m_iFirstExtraLayer;
- bool m_bOK;
- LayerVector m_aLayers;
+ int m_iNumber;
+ int m_iFirstExtraLayer;
+ bool m_bOK;
+ CLayerVector m_aLayers;
};
#endif // __BACKGROUND_H
diff --git a/src/Chooser.h b/src/Chooser.h
index 1c0663e..3c63d12 100644
--- a/src/Chooser.h
+++ b/src/Chooser.h
@@ -1,73 +1,77 @@
/***************************************************************************
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"
-
+/**
+CChooser is a control for choosing fighters.
+It is used on the fighter selection screens.
+\ingroup PlayerSelect
+*/
class CChooser
{
public:
CChooser();
~CChooser();
void Start( SDL_Surface* m_poScreen );
void Stop();
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();
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:
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 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 d9b795a..8d6ec9a 100644
--- a/src/Demo.cpp
+++ b/src/Demo.cpp
@@ -1,495 +1,510 @@
/***************************************************************************
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()
+CDemo::CDemo()
{
m_poFlyingChars = NULL;
m_bAdvanceGame = false;
}
-Demo::~Demo()
+CDemo::~CDemo()
{
delete m_poFlyingChars;
m_poFlyingChars = NULL;
}
-int Demo::Advance( int a_iNumFrames, bool a_bFlip )
+int CDemo::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 )
+int CDemo::AdvanceFlyingChars( int a_iNumFrames )
{
m_poFlyingChars->Advance( a_iNumFrames );
return ( m_poFlyingChars->IsDone() ? 1 : 0 );
}
-int Demo::AdvanceGame( int a_iNumFrames )
+int CDemo::AdvanceGame( int a_iNumFrames )
{
for ( int i=0; i<a_iNumFrames; ++i )
{
g_oBackend.AdvancePerl();
}
return 0;
}
-int Demo::Run()
+int CDemo::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 ( 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()
+void CDemo::OnMenu()
{
::DoMenu();
}
+/**
+\ingroup Demo
+*/
-class CreditsDemo: public Demo
+class CCreditsDemo: public CDemo
{
public:
- CreditsDemo()
+ CCreditsDemo()
{
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_poFlyingChars = new CFlyingChars( 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( m_sText1.c_str(), CFlyingChars::FC_AlignCenter, true );
+ m_poFlyingChars->AddText( m_sText2.c_str(), CFlyingChars::FC_AlignJustify, true );
+ m_poFlyingChars->AddText( m_sText3.c_str(), CFlyingChars::FC_AlignCenter, true );
- m_poFlyingChars->AddText( "\n\n\n\n\n\n:)", FlyingChars::FC_AlignRight, true );
+ m_poFlyingChars->AddText( "\n\n\n\n\n\n:)", CFlyingChars::FC_AlignRight, true );
}
protected:
std::string m_sText1;
std::string m_sText2;
std::string m_sText3;
};
+/**
+\ingroup Demo
+*/
-class EuDemo: public Demo
+class CEuDemo: public CDemo
{
public:
- EuDemo()
+ CEuDemo()
{
m_poBackground = LoadBackground( "eu.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_poFlyingChars = new CFlyingChars( storyFont, oRect, -1 );
m_sText1 = "This version of OpenMortal was released on 2004-05-01, the day that Hungary "
"along with 9 other countries joined the European Union.\n\n\n\n\n\n\n\n\n";
- m_poFlyingChars->AddText( m_sText1.c_str(), FlyingChars::FC_AlignJustify, true );
+ m_poFlyingChars->AddText( m_sText1.c_str(), CFlyingChars::FC_AlignJustify, true );
}
protected:
std::string m_sText1;
};
+/**
+\ingroup Demo
+*/
-class Story1Demo: public Demo
+class CStory1Demo: public CDemo
{
public:
- Story1Demo()
+ CStory1Demo()
{
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_poFlyingChars = new CFlyingChars( storyFont, oRect, -1 );
m_sText1 = Translate( "Story1Text" );
- m_poFlyingChars->AddText( m_sText1.c_str(), FlyingChars::FC_AlignJustify, true );
+ m_poFlyingChars->AddText( m_sText1.c_str(), CFlyingChars::FC_AlignJustify, true );
}
protected:
std::string m_sText1;
};
+/**
+\ingroup Demo
+*/
-class Story2Demo: public Demo
+class CStory2Demo: public CDemo
{
public:
- Story2Demo()
+ CStory2Demo()
{
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_poFlyingChars = new CFlyingChars( storyFont, oRect, -1 );
m_sText1 = Translate( "Story2Text" );
- m_poFlyingChars->AddText( m_sText1.c_str(), FlyingChars::FC_AlignJustify, true );
+ m_poFlyingChars->AddText( m_sText1.c_str(), CFlyingChars::FC_AlignJustify, true );
}
protected:
std::string m_sText1;
};
+/**
+\ingroup Demo
+*/
-class MainScreenDemo: public Demo
+class CMainScreenDemo: public CDemo
{
public:
- MainScreenDemo()
+ CMainScreenDemo()
{
i = 0;
m_iTimeLeft = 50;
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(), 255 );
+ m_poPack = new CRlePack( 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()
+ ~CMainScreenDemo()
{
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;
+ CRlePack* 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];
#if defined (MACOSX)
//[segabor] path fix.
sprintf( acFilename, "%s/demo%d.om", DATADIR, aiOrder[iNext] );
#else
sprintf( acFilename, DATADIR "/demo%d.om", aiOrder[iNext] );
#endif
// 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;
+ CMainScreenDemo oDemo;
oDemo.Run();
}
while (1)
{
DoDemos_BREAKONEND;
{
- EuDemo oDemo;
+ CEuDemo oDemo;
oDemo.Run();
}
DoDemos_BREAKONEND;
DoReplayDemo();
DoDemos_BREAKONEND;
{
- Story1Demo oDemo;
+ CStory1Demo oDemo;
oDemo.Run();
}
DoDemos_BREAKONEND;
DoReplayDemo();
DoDemos_BREAKONEND;
{
- FighterStatsDemo oDemo;
+ CFighterStatsDemo oDemo;
oDemo.Run();
}
DoDemos_BREAKONEND;
DoReplayDemo();
DoDemos_BREAKONEND;
{
- Story2Demo oDemo;
+ CStory2Demo oDemo;
oDemo.Run();
}
DoDemos_BREAKONEND;
{
- FighterStatsDemo oDemo;
+ CFighterStatsDemo oDemo;
oDemo.Run();
}
DoDemos_BREAKONEND;
DoReplayDemo();
DoDemos_BREAKONEND;
{
- CreditsDemo oDemo;
+ CCreditsDemo oDemo;
oDemo.Run();
}
DoDemos_BREAKONEND;
{
- MainScreenDemo oDemo;
+ CMainScreenDemo oDemo;
oDemo.Run();
}
DoDemos_BREAKONEND;
}
}
diff --git a/src/Demo.h b/src/Demo.h
index 80bfe94..c29f24d 100644
--- a/src/Demo.h
+++ b/src/Demo.h
@@ -1,44 +1,54 @@
/***************************************************************************
Demo.h - description
-------------------
begin : Wed Aug 16 22:18:47 CEST 2003
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#ifndef DEMO_H
-#define DEMO_H
+#define DEMO_H
+
+/**
+\defgroup Demo Demos and special screens
+*/
-class FlyingChars;
+class CFlyingChars;
struct SDL_Surface;
-
-
-
-class Demo
+
+/**
+\class CDemo
+\ingroup Demo
+\brief CDemo is the base class for every specific demo.
+
+The demos are played by the DoDemos() function in a predefined order.
+*/
+
+class CDemo
{
public:
- Demo();
- virtual ~Demo();
+ CDemo();
+ virtual ~CDemo();
virtual int Run();
protected:
virtual int Advance( int a_iNumFrames, bool a_bFlip );
virtual int AdvanceFlyingChars( int a_iNumFrames );
virtual int AdvanceGame( int a_iNumFrames );
virtual void OnMenu();
protected:
- FlyingChars* m_poFlyingChars;
+ CFlyingChars* m_poFlyingChars;
bool m_bAdvanceGame;
SDL_Surface* m_poBackground;
};
#endif // DEMO_H
diff --git a/src/DrawRle.h b/src/DrawRle.h
index c0224bb..12e9a0a 100644
--- a/src/DrawRle.h
+++ b/src/DrawRle.h
@@ -1,345 +1,347 @@
+// This is not a real include file. It is used byt RlePack.cpp
+
// At this point the following methods should be defined:
// METHODNAME: The name of the drawing method
// METHODNAME_FLIPT: The name of the flipped drawing method
// PIXEL_PTR: type of pointer to the target surface pixels
// PUT_PIXEL(p,c): Draw pixel of color c to pixel ptr type p
// PITCH: This added to PIXEL_PTR type will result in the next scanline
-void METHODNAME_FLIP( RLE_SPRITE* src, int dx, int dy )
+void METHODNAME_FLIP( SRleSprite* src, int dx, int dy )
{
#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))
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)*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;
+ unsigned long col = (unsigned char) (*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;
+ unsigned long col = (unsigned char) (*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 METHODNAME( RLE_SPRITE* src, int dx, int dy )
+void METHODNAME( SRleSprite* src, int dx, int dy )
{
#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)*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 = (unsigned char) (*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;
+ unsigned long col = (unsigned char) (*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);
}
diff --git a/src/Event.h b/src/Event.h
index 6c0d9f1..160ea95 100644
--- a/src/Event.h
+++ b/src/Event.h
@@ -1,51 +1,54 @@
/***************************************************************************
Event.h - description
-------------------
begin : Sat Feb 14 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
#ifndef EVENT_H
#define EVENT_H
enum TMortalEventEnum
{
Me_NOTHING,
Me_QUIT,
Me_MENU,
Me_SKIP,
Me_PLAYERKEYDOWN,
Me_PLAYERKEYUP,
};
enum TMortalKeysEnum
{
Mk_UP = 0,
Mk_DOWN = 1,
Mk_LEFT = 2,
Mk_RIGHT = 3,
Mk_BLOCK = 4,
Mk_LPUNCH = 5,
Mk_HPUNCH = 6,
Mk_LKICK = 7,
Mk_HKICK = 8,
};
-
+
+/**
+\ingroup GameLogic
+*/
struct SMortalEvent
{
TMortalEventEnum m_enType;
int m_iPlayer;
int m_iKey;
};
bool TranslateEvent( const SDL_Event* a_poInSDLEvent, SMortalEvent* a_poOutEvent );
bool MortalPollEvent( SMortalEvent& a_roOutEvent );
void MortalWaitEvent( SMortalEvent& a_roOutEvent );
#endif
diff --git a/src/FighterEnum.h b/src/FighterEnum.h
index c676cbf..c300e64 100644
--- a/src/FighterEnum.h
+++ b/src/FighterEnum.h
@@ -1,62 +1,65 @@
/***************************************************************************
FighterEnum.h - description
-------------------
begin : 2003-09-03
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#ifndef FIGHTERENUM_H
#define FIGHTERENUM_H
/// FIGHTER ENUMERABLE
+/**
+\ingroup GameLogic
+*/
enum FighterEnum {
UNKNOWN = 0, ///< Must be the first element, must be 0 (used by iterations)
ULMAR,
UPI,
ZOLI,
CUMI,
SIRPI,
MACI,
BENCE,
GRIZLI,
DESCANT,
SURBA,
AMBRUS,
DANI,
KINGA,
MISI,
LASTFIGHTER, ///< This must terminate the list, iterations use it.
};
-/** The TintEnum contains values that can be passed to RlePack::SetTint.
+/** The TintEnum contains values that can be passed to CRlePack::SetTint.
-The tint is some modification of the original palette of an RlePack. This
+The tint is some modification of the original palette of an CRlePack. This
is used for two things:
\li In case both players choose the same fighter, player 2's fighter is
tinted so they won't get confused.
\li Some special effects (e.g. frozen) make the fighter tinted as well.
-The Tint of players is stored by PlayerSelect and applied by RlePack::SetTint().
+The Tint of players is stored by PlayerSelect and applied by CRlePack::SetTint().
*/
enum TintEnum {
NO_TINT = 0,
ZOMBIE_TINT,
GRAY_TINT,
DARK_TINT,
INVERTED_TINT,
FROZEN_TINT,
};
#endif // FIGHTERENUM_H
diff --git a/src/FighterStats.cpp b/src/FighterStats.cpp
index d68cc16..e479f07 100644
--- a/src/FighterStats.cpp
+++ b/src/FighterStats.cpp
@@ -1,255 +1,240 @@
/***************************************************************************
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];
+int CFighterStatsDemo::mg_iLastFighter = -1;
+FighterEnum CFighterStatsDemo::mg_aenFighterOrder[LASTFIGHTER-1];
-FighterStatsDemo::FighterStatsDemo( FighterEnum a_iFighter )
+CFighterStatsDemo::CFighterStatsDemo( FighterEnum a_iFighter )
{
m_iTimeLeft = 500;
m_poStaff = NULL;
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(%d);", 2 );
}
else
{
std::string sStaffFilename = DATADIR;
sStaffFilename += "/characters/STAFF.DAT";
- m_poStaff = new RlePack( sStaffFilename.c_str(), 255 );
+ m_poStaff = new CRlePack( 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 );
+ m_poFlyingChars = new CFlyingChars( creditsFont, oFlyingRect );
+ m_poFlyingChars->AddText( m_sStory.c_str(), CFlyingChars::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 );
+ m_poFlyingChars->AddText( "\n\nKEYS\n", CFlyingChars::FC_AlignCenter, true );
+ m_poFlyingChars->AddText( m_sKeys.c_str(), CFlyingChars::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 );
+ m_poFlyingChars->AddText( m_sKeys.c_str(), CFlyingChars::FC_AlignLeft, true );
}
}
-FighterStatsDemo::~FighterStatsDemo()
+CFighterStatsDemo::~CFighterStatsDemo()
{
delete m_poStaff;
}
-int FighterStatsDemo::Advance( int a_iNumFrames, bool a_bFlip )
+int CFighterStatsDemo::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/FighterStats.h b/src/FighterStats.h
index a416ec9..f61f295 100644
--- a/src/FighterStats.h
+++ b/src/FighterStats.h
@@ -1,35 +1,43 @@
/***************************************************************************
FighterStats.h - description
-------------------
begin : Sun Jan 25 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
#ifndef FIGHTERSTATS_H
#define FIGHTERSTATS_H
#include "Demo.h"
#include <string>
+
+/**
+\class CFighterStatDemo
+\ingroup Demo
+\brief This is a specialized CDemo which displays the stats of a fighter.
+
+The fighter is either random, or the winner of the last game.
+*/
-class FighterStatsDemo: public Demo
+class CFighterStatsDemo: public CDemo
{
public:
- FighterStatsDemo( FighterEnum a_iFighter = UNKNOWN );
- virtual ~FighterStatsDemo();
+ CFighterStatsDemo( FighterEnum a_iFighter = UNKNOWN );
+ virtual ~CFighterStatsDemo();
int Advance( int a_iNumFrames, bool a_bFlip );
protected:
int m_iTimeLeft;
FighterEnum m_enFighter;
- RlePack* m_poStaff;
+ CRlePack* m_poStaff;
std::string m_sKeys;
std::string m_sStory;
static int mg_iLastFighter; // index of the last fighter in the fighter order
static FighterEnum mg_aenFighterOrder[LASTFIGHTER-1];
};
#endif // FIGHTERSTATS_H
diff --git a/src/FlyingChars.cpp b/src/FlyingChars.cpp
index 9099f28..d9c4efb 100644
--- a/src/FlyingChars.cpp
+++ b/src/FlyingChars.cpp
@@ -1,406 +1,406 @@
/***************************************************************************
FlyingChars.cpp - description
-------------------
begin : Mon Aug 12 2003
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#include "FlyingChars.h"
#include "sge_surface.h"
#include "common.h"
int g_iLineTime = 100;
int g_iCharTime = 80;
-FlyingChars::FlyingChars( sge_bmpFont* a_poFont, const SDL_Rect& a_roRect, int a_iFontDisplacement )
+CFlyingChars::CFlyingChars( sge_bmpFont* a_poFont, const SDL_Rect& a_roRect, int a_iFontDisplacement )
{
m_poFont = a_poFont;
m_oRect = a_roRect;
m_iFontDisplacement = a_iFontDisplacement;
m_bDone = true;
m_iTimeToNextLine = 0;
m_iDelay = 0;
m_iLastLineY = a_roRect.y;
m_pcText = NULL;
m_enAlignment = FC_AlignLeft;
m_iTextOffset = 0;
m_bScrolling = false;
m_dScrollupRate = (double)(m_poFont->CharHeight+2) / (double)g_iLineTime;
m_dScrollup = 0.0;
}
-FlyingChars::~FlyingChars()
+CFlyingChars::~CFlyingChars()
{
}
-void FlyingChars::AddText( const char* a_pcText,
- TextAlignment a_enAlignment, bool a_bOneByOne )
+void CFlyingChars::AddText( const char* a_pcText,
+ TextAlignmentEnum a_enAlignment, bool a_bOneByOne )
{
- EnqueuedText oNewText;
+ SEnqueuedText oNewText;
oNewText.m_pcText = a_pcText;
oNewText.m_enAlignment = a_enAlignment;
m_oEnqueuedTexts.push_back( oNewText );
if ( a_bOneByOne && m_iLastLineY <= m_oRect.y )
{
m_iLastLineY = m_oRect.y + m_oRect.h - m_poFont->CharHeight;
}
else if ( 0 == m_pcText
|| m_iLastLineY <= m_oRect.y + m_oRect.h - m_poFont->CharHeight )
{
DequeueText();
}
}
-bool FlyingChars::IsDone()
+bool CFlyingChars::IsDone()
{
if ( m_oEnqueuedTexts.size() == 0
&& ( NULL == m_pcText || 0 == m_pcText[ m_iTextOffset] )
&& ( m_bDone ) )
{
return true;
}
return false;
}
-void FlyingChars::DequeueText()
+void CFlyingChars::DequeueText()
{
if ( 0 == m_oEnqueuedTexts.size() )
{
return;
}
- EnqueuedText& oEnqueuedText = m_oEnqueuedTexts.front();
+ SEnqueuedText& oEnqueuedText = m_oEnqueuedTexts.front();
m_pcText = (unsigned char*) oEnqueuedText.m_pcText;
m_enAlignment = oEnqueuedText.m_enAlignment;
m_iTextOffset = 0;
while ( m_iLastLineY <= m_oRect.y + m_oRect.h - m_poFont->CharHeight )
{
AddNextLine();
m_iTimeToNextLine += g_iLineTime;
if ( 0 == m_pcText[m_iTextOffset] )
{
break;
}
}
m_oEnqueuedTexts.pop_front();
}
-void FlyingChars::Advance( int a_iNumFrames )
+void CFlyingChars::Advance( int a_iNumFrames )
{
if ( a_iNumFrames > 5 ) a_iNumFrames = 5;
if ( a_iNumFrames <= 0 ) a_iNumFrames = 0;
m_bDone = true;
m_iTimeToNextLine -= a_iNumFrames;
if ( m_iTimeToNextLine < 0 )
{
m_iDelay = 0;
if ( !m_pcText
|| 0 == m_pcText[m_iTextOffset] )
{
DequeueText();
}
else
{
m_iTimeToNextLine += g_iLineTime;
AddNextLine();
}
}
m_dScrollup += a_iNumFrames * m_dScrollupRate;
int iScrollup = (int) m_dScrollup;
m_dScrollup -= iScrollup;
iScrollup *= 2;
- for ( FlyingLetterIterator it=m_oLetters.begin(); it!=m_oLetters.end(); ++it )
+ for ( CFlyingLetterIterator it=m_oLetters.begin(); it!=m_oLetters.end(); ++it )
{
- FlyingLetter& roLetter = *it;
+ SFlyingLetter& roLetter = *it;
if ( m_bScrolling )
{
roLetter.m_iDY -= iScrollup;
roLetter.m_iY -= iScrollup;
if ( roLetter.m_iDY < m_oRect.y * 2
&& roLetter.m_iDY >= 0 )
{
roLetter.m_iDY = -100;
roLetter.m_iTime = 40;
}
}
if (roLetter.m_iDelay > 0)
{
roLetter.m_iDelay -= a_iNumFrames;
continue;
}
if ( roLetter.m_iTime > 0 )
{
m_bDone = false;
int iEstX = roLetter.m_iSX * roLetter.m_iTime / 2 + roLetter.m_iX ;
if ( iEstX > roLetter.m_iDX )
{
roLetter.m_iSX -= a_iNumFrames;
}
else if ( iEstX < roLetter.m_iDX )
{
roLetter.m_iSX += a_iNumFrames;
}
roLetter.m_iX += roLetter.m_iSX * a_iNumFrames;
if ( roLetter.m_iSY * roLetter.m_iTime / 2 + roLetter.m_iY >= roLetter.m_iDY )
{
roLetter.m_iSY -= a_iNumFrames;
}
else
{
roLetter.m_iSY += a_iNumFrames;
}
roLetter.m_iY += roLetter.m_iSY * a_iNumFrames;
roLetter.m_iTime -= a_iNumFrames;
if ( roLetter.m_iTime <= 0 )
{
roLetter.m_iX = roLetter.m_iDX;
roLetter.m_iY = roLetter.m_iDY;
roLetter.m_iSX = roLetter.m_iSY = 0;
roLetter.m_iTime = 0;
}
}
}
}
-void FlyingChars::Draw()
+void CFlyingChars::Draw()
{
- for ( FlyingLetterIterator it=m_oLetters.begin(); it!=m_oLetters.end(); ++it )
+ for ( CFlyingLetterIterator it=m_oLetters.begin(); it!=m_oLetters.end(); ++it )
{
- FlyingLetter& roLetter = *it;
+ SFlyingLetter& roLetter = *it;
int iDestX, iDestY;
if (roLetter.m_iDelay > 0)
{
continue;
}
else if ( roLetter.m_iTime > 0 )
{
iDestX = roLetter.m_iX;
iDestY = roLetter.m_iY;
}
else
{
iDestX = roLetter.m_iX;
iDestY = roLetter.m_iY;
}
int iSrcX, iSrcW;
if ( ! m_poFont->CharPos )
{
iSrcX = roLetter.m_cLetter * m_poFont->CharWidth;
iSrcW = m_poFont->CharWidth;
}
else
{
int iOfs = ( ((unsigned int)roLetter.m_cLetter)-33)*2 + 1;
iSrcX = m_poFont->CharPos[iOfs];
iSrcW = m_poFont->CharPos[iOfs+1] - iSrcX;
}
//debug( "Letter %c at %d,%d\n", roLetter.m_cLetter, iDestX/2, iDestY/2 );
sge_Blit( m_poFont->FontSurface, gamescreen, iSrcX, m_poFont->yoffs,
iDestX/2, iDestY/2, iSrcW, m_poFont->CharHeight );
}
}
-void FlyingChars::AddNextLine()
+void CFlyingChars::AddNextLine()
{
if ( NULL == m_pcText )
{
return;
}
// 1. SCROLL UP EVERYTHING IF NECESSARY
if ( m_iLastLineY > m_oRect.y + m_oRect.h - m_poFont->CharHeight )
{
// scroll up every character
if ( !m_bScrolling )
{
m_bScrolling = true;
m_iTimeToNextLine = int( (m_iLastLineY - (m_oRect.y + m_oRect.h - m_poFont->CharHeight)) / m_dScrollupRate );
return;
}
m_iLastLineY = m_oRect.y + m_oRect.h - m_poFont->CharHeight;
}
SDL_Rect oRect;
const unsigned char* pcLineStart = m_pcText + m_iTextOffset;
if ( '\n' == *pcLineStart ) ++pcLineStart;
while (*pcLineStart == 32 || *pcLineStart == '\t' ) ++pcLineStart;
if ( 0 == *pcLineStart )
{
m_iTextOffset = pcLineStart - m_pcText;
return;
}
// 2. CALCULATE LINE WIDTH AND CONTENTS
const unsigned char* pcLineEnd = pcLineStart;
const unsigned char* pcNextWord = pcLineEnd;
int iNumWords = 0;
int iLineWidth = 0;
int iWidth = 0;
while (1)
{
++iNumWords;
if ( '\n' == *pcNextWord
|| 0 == *pcNextWord)
{
break;
}
// Skip the next 'white space' part
while (*pcNextWord == 32 || *pcNextWord == '\t' )
{
iWidth += GetCharWidth( *pcNextWord );
++pcNextWord;
}
// Skip the next 'non-whitespace' part
while (*pcNextWord != 32 && *pcNextWord != '\t'
&& *pcNextWord != '\n' && *pcNextWord != 0 )
{
iWidth += GetCharWidth( *pcNextWord );
++pcNextWord;
}
if ( iWidth > m_oRect.w )
{
// overflow
break;
}
pcLineEnd = pcNextWord;
iLineWidth = iWidth;
}
if ( pcLineEnd == pcLineStart )
{
pcLineEnd = pcNextWord;
iLineWidth = iWidth;
}
// 3. ADD LETTERS IN LINE
double dX = m_oRect.x;
double dSpaceLength = 0.0;
switch ( m_enAlignment )
{
case FC_AlignJustify:
if ( '\n' == *pcLineEnd
|| 0 == *pcLineEnd )
{
}
else
{
dSpaceLength = (m_oRect.w - iLineWidth) / double( iNumWords > 2 ? iNumWords-2 : 1 );
}
break;
case FC_AlignCenter:
dX += (m_oRect.w - iLineWidth) /2;
break;
case FC_AlignRight:
dX += (m_oRect.w - iLineWidth);
break;
default:
break;
}
- FlyingLetter oLetter;
+ SFlyingLetter oLetter;
oLetter.m_iDY = m_iLastLineY * 2;
for ( const unsigned char *pcChar = pcLineStart; pcChar<pcLineEnd; ++pcChar )
{
if ( *pcChar < 33 )
{
if ( *pcChar == 32 || *pcChar == '\t' )
{
dX += dSpaceLength;
// debug( "dX = %3.2f dSpaceLength = %2.2f\n", (float)dX, (float)dSpaceLength );
}
int iWidth = GetCharWidth( *pcChar );
dX += iWidth;
//debug( "dX = %3.2f iWidth = %d\n", (float)dX, iWidth );
continue;
}
oRect = sge_BF_TextSize( m_poFont, (char*) pcLineStart, pcChar-pcLineStart );
oLetter.m_iDX = (int) dX * 2;
oLetter.m_iX = rand() % (gamescreen->w * 2);
oLetter.m_iY = gamescreen->h * 2;
oLetter.m_iSX = 0;
oLetter.m_iSY = -45 + rand() % 15 ;
oLetter.m_iDelay = m_iDelay++;
oLetter.m_iTime = g_iCharTime;
oLetter.m_cLetter = *pcChar;
m_oLetters.push_back(oLetter);
dX += GetCharWidth( *pcChar );
}
m_iTextOffset = pcLineEnd - m_pcText;
m_iLastLineY += m_poFont->CharHeight + 2;
}
-int FlyingChars::GetCharWidth( unsigned char a_cChar )
+int CFlyingChars::GetCharWidth( unsigned char a_cChar )
{
if ( a_cChar == 0 )
{
return 0;
}
else if ( m_poFont->CharPos )
{
if ( a_cChar < 33 )
{
return m_poFont->CharPos[3] - m_poFont->CharPos[2] + 1;
}
else
{
int iOfs = ( ((unsigned int)a_cChar) - 33) * 2 + 1;
return m_poFont->CharPos[iOfs+1] - m_poFont->CharPos[iOfs] + m_iFontDisplacement;
}
}
return m_poFont->CharWidth;
}
diff --git a/src/FlyingChars.h b/src/FlyingChars.h
index e470054..1103861 100644
--- a/src/FlyingChars.h
+++ b/src/FlyingChars.h
@@ -1,89 +1,95 @@
/***************************************************************************
FlyingChars.h - description
-------------------
begin : Mon Aug 12 2003
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#ifndef FLYINGCHARS_H
#define FLYINGCHARS_H
#include "sge_bm_text.h"
#include <vector>
#include <list>
-
-
-struct FlyingLetter
-{
- int m_iX, m_iY;
- int m_iSX, m_iSY;
- int m_iDX, m_iDY;
- int m_iDelay;
- int m_iTime;
- unsigned char m_cLetter;
-};
-
-
-class FlyingChars
+
+/**
+\class CFlyingChars
+\brief Characters jumping onto the screen to form the text in demos
+
+\ingroup Media
+*/
+
+class CFlyingChars
{
+
+ struct SFlyingLetter
+ {
+ int m_iX, m_iY;
+ int m_iSX, m_iSY;
+ int m_iDX, m_iDY;
+ int m_iDelay;
+ int m_iTime;
+ unsigned char m_cLetter;
+ };
+
public:
- enum TextAlignment {
+ enum TextAlignmentEnum {
FC_AlignLeft,
FC_AlignRight,
FC_AlignCenter,
FC_AlignJustify,
};
public:
- FlyingChars( sge_bmpFont* a_poFont, const SDL_Rect& a_roRect, int a_iFontDisplacement = 0 );
- ~FlyingChars();
+ CFlyingChars( sge_bmpFont* a_poFont, const SDL_Rect& a_roRect, int a_iFontDisplacement = 0 );
+ ~CFlyingChars();
- void AddText( const char* a_pcText, TextAlignment a_enAlignment, bool bOneByOne );
+ void AddText( const char* a_pcText, TextAlignmentEnum a_enAlignment, bool bOneByOne );
void Advance( int a_iNumFrames );
void Draw();
bool IsDone();
int GetCount();
protected:
void AddNextLine();
int GetCharWidth( unsigned char a_cChar );
void DequeueText();
protected:
- struct EnqueuedText
+ struct SEnqueuedText
{
const char* m_pcText;
- TextAlignment m_enAlignment;
+ TextAlignmentEnum m_enAlignment;
};
- std::list<EnqueuedText> m_oEnqueuedTexts;
+ std::list<SEnqueuedText> m_oEnqueuedTexts;
bool m_bDone;
bool m_bScrolling;
double m_dScrollupRate;
double m_dScrollup;
- typedef std::vector<FlyingLetter> FlyingLetterList;
- typedef FlyingLetterList::iterator FlyingLetterIterator;
+ typedef std::vector<SFlyingLetter> CFlyingLetterList;
+ typedef CFlyingLetterList::iterator CFlyingLetterIterator;
sge_bmpFont* m_poFont;
int m_iFontDisplacement;
- FlyingLetterList m_oLetters;
+ CFlyingLetterList m_oLetters;
int m_iTimeToNextLine;
SDL_Rect m_oRect;
const unsigned char* m_pcText;
- TextAlignment m_enAlignment;
+ TextAlignmentEnum m_enAlignment;
int m_iTextOffset;
int m_iLastLineY;
int m_iDelay;
};
#endif // FLYINGCHARS_H
diff --git a/src/Game.cpp b/src/Game.cpp
index 2669a87..21967ed 100644
--- a/src/Game.cpp
+++ b/src/Game.cpp
@@ -1,1266 +1,1211 @@
/***************************************************************************
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;
+int CGame::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
***************************************************************************/
-
-
+/**
+\ingroup GameLogic
+*/
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_bWide, bool a_bDebug)
+CGame::CGame( 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 = new CBackground();
m_poBackground->Load(mg_iBackgroundNumber++);
if ( !m_poBackground->IsOK() )
{
m_poBackground->Load(1);
mg_iBackgroundNumber = 1;
}
m_poDoodads = LoadBackground( "Doodads.png", 48, 64, true );
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()
+CGame::~CGame()
{
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()
+int CGame::Run()
{
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;
}
// 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()
+std::string& CGame::GetReplay()
{
return m_sReplayString;
}
/***************************************************************************
GAME DRAWING METHODS
***************************************************************************/
-void Game::DrawHitPointDisplay( int a_iPlayer )
+void CGame::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::DrawHitPointDisplays()
+void CGame::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 + 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 + m_iYOffset;
- SDL_BlitSurface( m_poDoodads, &src, gamescreen, &dst );
- }
- if ( m_aiRoundsWonByPlayer[1] > 0 )
- {
- 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 + 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 + m_iYOffset );
-*/
}
/** Draws the background, using the m_poBackground object.
*/
-void Game::DrawBackground()
+void CGame::DrawBackground()
{
m_poBackground->Draw( g_oBackend.m_iBgX, g_oBackend.m_iBgY, m_iYOffset );
}
-void Game::AddBodyToBackground( int a_iPlayer )
+void CGame::AddBodyToBackground( int a_iPlayer )
{
- Backend::SPlayer& roPlayer = g_oBackend.m_aoPlayers[a_iPlayer];
- BackgroundLayer oLayer;
+ CBackend::SPlayer& roPlayer = g_oBackend.m_aoPlayers[a_iPlayer];
+ CBackground::SBackgroundLayer oLayer;
- RlePack* poPack = g_oPlayerSelect.GetPlayerInfo(a_iPlayer).m_poPack;
+ CRlePack* 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_pcName 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 )
+void CGame::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 + m_iYOffset, x2, y2 + m_iYOffset, a_iColor ) ;
}
}
/** Draws every doodad that is currently defined in the backend.
*/
-void Game::DrawDoodads()
+void CGame::DrawDoodads()
{
for ( int i=0; i<g_oBackend.m_iNumDoodads; ++i )
{
- Backend::SDoodad& roDoodad = g_oBackend.m_aoDoodads[i];
+ CBackend::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 > gamescreen->w ) iDoodadX = gamescreen->w - iWidth;
if ( iDoodadX < 0 ) iDoodadX = 0;
int iDoodadY = roDoodad.m_iY;
sge_BF_textout( gamescreen, fastFont, s, iDoodadX, iDoodadY + m_iYOffset );
continue;
}
if ( roDoodad.m_iGfxOwner >= 0 )
{
g_oPlayerSelect.GetPlayerInfo(roDoodad.m_iGfxOwner).m_poPack->Draw(
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 + 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()
+void CGame::Draw()
{
#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<g_oState.m_iNumPlayers; ++i )
{
- Backend::SPlayer& roPlayer = g_oBackend.m_aoPlayers[i];
+ CBackend::SPlayer& roPlayer = g_oBackend.m_aoPlayers[i];
int iFrame = roPlayer.m_iFrame;
if ( iFrame == 0 )
continue;
- RlePack* poPack = g_oPlayerSelect.GetPlayerInfo(i).m_poPack;
+ CRlePack* 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;
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<g_oState.m_iNumPlayers; ++i )
{
- Backend::SPlayer& roPlayer = g_oBackend.m_aoPlayers[i];
+ CBackend::SPlayer& roPlayer = g_oBackend.m_aoPlayers[i];
int iFrame = roPlayer.m_iFrame;
if ( iFrame == 0 )
continue;
- RlePack* poPack = g_oPlayerSelect.GetPlayerInfo(i).m_poPack;
+ CRlePack* poPack = g_oPlayerSelect.GetPlayerInfo(i).m_poPack;
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();
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 + 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 + m_iYOffset, AlignHCenter, C_WHITE, gamescreen, false );
}
else if ( Ph_REWIND == m_enGamePhase )
{
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 + 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 + m_iYOffset, AlignHCenter, C_WHITE, gamescreen );
}
if ( oFpsCounter.m_iFps > 0 )
{
sge_BF_textoutf( gamescreen, fastFont, 2, 455 + m_iYOffset, "%d fps", oFpsCounter.m_iFps );
}
SDL_Flip( gamescreen );
}
/***************************************************************************
GAME PROTECTED METHODS
***************************************************************************/
-bool Game::IsTeamMode()
+bool CGame::IsTeamMode()
{
return SState::Team_ONE_VS_ONE != g_oState.m_enTeamMode;
}
-bool Game::IsNetworkGame()
+bool CGame::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()
+bool CGame::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 )
+void CGame::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 )
+void CGame::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()
+int CGame::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 CGame::HandleKO()
{
}
-void Game::HurryUp()
+void CGame::HurryUp()
{
Audio->PlaySample( "GAME_HURRYUP" );
DrawGradientText( "HURRY UP!", titleFont, 200, gamescreen );
SDL_Delay( 1000 );
Audio->PlaySample( "GAME_HURRYUP_ENDS" );
}
-void Game::TimeUp()
+void CGame::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 )
+void CGame::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()
+void CGame::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,%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.
for ( int i=0; i<g_oState.m_iNumPlayers; ++i )
{
if ( g_oBackend.m_aoPlayers[i].m_iRealHitPoints <= -10000 )
{
// 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 );
}
}
}
if ( g_oBackend.m_iGameOver )
{
break;
}
if ( !IsMaster() )
{
if ( g_poNetwork->IsRoundOver() )
{
break;
}
}
}
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 )
+void CGame::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;
}
}
}
+// TODO move the backgroundnumber to SState
-
-int Game::GetBackgroundNumber() //static
+int CGame::GetBackgroundNumber() //static
{
return mg_iBackgroundNumber;
}
/** 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 )
{
bool bWide = g_oState.m_iNumPlayers > 2;
// CVideoModeChange oVideoMode( bWide );
- Game oGame( a_bIsReplay, bWide, a_bDebug );
+ CGame 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();
+ return CGame::GetBackgroundNumber();
}
diff --git a/src/Game.h b/src/Game.h
index f3ef2b7..19ef5f9 100644
--- a/src/Game.h
+++ b/src/Game.h
@@ -1,145 +1,155 @@
/***************************************************************************
Game.h - description
-------------------
begin : Mon Aug 27 2003
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#ifndef GAME_H
-#define GAME_H
+#define GAME_H
+
+/**
+\defgroup GameLogic Game logic (frontend + backend connection)
+
+This group of classes implement the frontend part of the game logic,
+including the game object itself, menus, and connection to the backend.
+*/
#include <string>
#include <vector>
#include <list>
struct SDL_Surface;
class Background;
-/**
+/**
+\ingroup GameLogic
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.
+/**
+\ingroup GameLogic
+
+The CGame 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
+class CGame
{
public:
- Game( bool a_bIsReplay, bool m_bWide, bool a_bDebug );
- ~Game();
+ CGame( bool a_bIsReplay, bool m_bWide, bool a_bDebug );
+ ~CGame();
int Run();
std::string& GetReplay();
void DoReplay( const char* a_pcReplayFile );
static int GetBackgroundNumber();
protected:
void Draw();
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;
+ CBackground* m_poBackground;
SDL_Surface* m_poDoodads;
int m_aiHitPointDisplayX[MAXPLAYERS];
int m_aiHitPointDisplayY[MAXPLAYERS];
bool m_abHitPointDisplayLeft[MAXPLAYERS];
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/Joystick.h b/src/Joystick.h
index 8d08304..289cd76 100644
--- a/src/Joystick.h
+++ b/src/Joystick.h
@@ -1,47 +1,50 @@
/***************************************************************************
Joystick.h - description
-------------------
begin : Sat Feb 14 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
#ifndef JOYSTICK_H
#define JOYSTICK_H
#include "SDL.h"
struct SMortalEvent;
-
+
+/**
+\ingroup Media
+*/
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/MortalNetwork.h b/src/MortalNetwork.h
index 7c1eb5a..50e134c 100644
--- a/src/MortalNetwork.h
+++ b/src/MortalNetwork.h
@@ -1,131 +1,136 @@
/***************************************************************************
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_types.h"
+
+/**
+\defgroup Network Networking layer
+Classes related to networking (MortalNet and internet game)
+*/
/** Mortal Network messages:
-
+\ingroup Network
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.h b/src/MortalNetworkImpl.h
index 17fa2a3..7223b30 100644
--- a/src/MortalNetworkImpl.h
+++ b/src/MortalNetworkImpl.h
@@ -1,165 +1,166 @@
/***************************************************************************
MortalNetworkImpl.h - description
-------------------
begin : Sun Jan 25 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
#ifndef MORTALNETWORKIMPL_H
#define MORTALNETWORKIMPL_H
#include "MortalNetwork.h"
#include "SDL_net.h"
#include <string>
#include <list>
typedef std::list<int> TIntList;
typedef std::list<std::string> TStringList;
-/**
+/**
+\ingroup Network
Implementation of the CMortalNetwork interface.
SUMMARY OF MESSAGES:
I <version> <username> - Introduction sent both ways on connection.
U <text> - Remote user's name
M <text> - Incoming Msg text.
S - Ready for the next round (synch).
G <text> - Update on the game backend data.
T <number> <number> - Update the game time and game phase.
K <number> <bool> - Key # up/down
H <number> - Hurryup and other special messages
O <number> <bool> - The round is over (who won, are there more rounds).
F <number> - I have switched to fighter X.
R - I have chosen a fighter.
Q <number> - Is fighter X available?
A <number> - Fighter A is available.
P <number> x3 - Game parameters
*/
class CMortalNetworkImpl: public CMortalNetwork
{
public:
CMortalNetworkImpl();
virtual ~CMortalNetworkImpl();
// Connection's lifecycle
bool Start( const char* a_pcServerName );
void Stop(); // Disconnect
bool IsConnectionAlive(); // Is the connection still good?
void Update(); // Read network traffic. Might get disconnected...
const char* GetLastError();
bool IsMaster(); // Am I Master or Slave?
// Msg related methods
const char* GetRemoteUsername(); // This is the name that is passed upon connection.
void SendMsg( const char* a_pcMsg ); // Prompt the user for a line of chat text
bool IsMsgAvailable(); // Returns true is a chatline has arrived
const char* GetMsg(); // The next chatline, or NULL if there are no more.
// Charater Selection methods
bool IsRemoteFighterAvailable( FighterEnum a_enFighter ); // Does the other computer have fighter X installed?
FighterEnum GetRemoteFighter(); // Returns the latest fighter chosen by the remote side.
bool IsRemoteSideReady(); // The other player is finished choosing.
void SendFighter( FighterEnum a_enFighter ); // Let the other side know that I switched to fighter X.
void SendReady(); // Let the other side know that I am ready.
void SendGameParams( int a_iGameSpeed, int a_iGameTime, int a_iHitPoints, int a_iBackgroundNumber );
SGameParams GetGameParams();
// Game methods
bool SynchStartRound();
void SendGameData( const char* a_pcGameData );
const char* GetLatestGameData();
void SendKeystroke( int a_iTime, int a_iKey, bool a_bPressed );
bool GetKeystroke( int& a_riOutTime, int& a_riOutKey, bool& a_rbPressed );
void SendGameTick( int a_iGameTick );
int GetGameTick();
void SendHurryup( int a_iHurryUpCode );
int GetHurryup() ;
void SendRoundOver( int a_iWhoWon, bool a_bGameOver );
int GetWhoWon();
bool IsRoundOver();
bool IsGameOver();
protected:
void SendRawData( char a_cID, const void* a_pData, int a_iLength );
void ReceiveMsg( void* a_pData, int a_iLength );
void ReceiveRemoteUserName( void* a_pData, int a_iLength );
void ReceiveGameData( void* a_pData, int a_iLength );
void ReceiveKeystroke( void* a_pData, int a_iLength );
void ReceiveFighter( void* a_pData, int a_iLength );
void ReceiveReady( void* a_pData, int a_iLength );
void ReceiveRoundOver( void* a_pData, int a_iLength );
void ReceiveGameTick( void* a_pData, int a_iLength );
void ReceiveHurryup( void* a_pData, int a_iLength );
void ReceiveRemoteFighterAvailable( void* a_pData, int a_iLength );
void ReceiveRemoteFighterQuery( void* a_pData, int a_iLength );
void ReceiveGameParams( void* a_pData, int a_iLength );
protected:
enum TNetworkState
{
NS_DISCONNECTED,
NS_CHARACTER_SELECTION,
NS_IN_GAME,
};
// Network METADATA
bool m_bNetworkAvailable; ///< Is the networking API initialized correctly?
TNetworkState m_enState; ///< The current state
bool m_bServer; ///< We are the server side.
bool m_bMaster; ///< We are the master side. (Initially the server side)
TCPsocket m_poSocket; ///< The TCP/IP network socket.
SDLNet_SocketSet m_poSocketSet; ///< SDLNet construct for watching the socket.
char m_acIncomingBuffer[2048]; ///< Received data goes here.
int m_iIncomingBufferSize; ///< How much of the buffer is filled?
std::string m_sLastError; ///< The last error message from SDLNet
TStringList m_asMsgs; ///< Incoming chatlines
// GAME DATA
std::string m_sRemoteUserName;
TIntList m_aiAvailableRemoteFighters;
FighterEnum m_enRemoteFighter;
bool m_bRemoteReady;
SGameParams m_oGameParams;
std::string m_sLatestGameData;
TIntList m_aiKeyTimes;
TIntList m_aiKeystrokes;
TIntList m_abKeystrokes;
bool m_bRoundOver;
int m_iWhoWon;
bool m_bGameOver;
int m_iGameTick;
int m_iHurryupCode;
// REMOTE QUERY RESPONSES
bool m_bSynchQueryResponse;
};
#endif // MORTALNETWORKIMPL_H
diff --git a/src/MszPerl.h b/src/MszPerl.h
index 95d245f..923c220 100644
--- a/src/MszPerl.h
+++ b/src/MszPerl.h
@@ -1,29 +1,32 @@
/***************************************************************************
MszPerl.h - description
-------------------
begin : Mon Jan 5 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
-/**
+/**
+\file MszPerl.h
+\ingroup GameLogic
+
This file helps include perl.h
The windows version of perl.h contains a lot of stupid #defines, this
file helps undefine them.
*/
#ifndef __MSZPERL_H
#define __MSZPERL_H
#include <EXTERN.h>
#include <perl.h>
#ifdef _WINDOWS
#undef bool
#undef chdir
#undef close
#undef eof
#define vsnprintf _vsnprintf
#endif
#endif // __MSZPERL_H
diff --git a/src/OnlineChat.cpp b/src/OnlineChat.cpp
index 61a3d92..822dbc3 100644
--- a/src/OnlineChat.cpp
+++ b/src/OnlineChat.cpp
@@ -1,742 +1,754 @@
/***************************************************************************
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,
};
+
+/**
+\ingroup Network
+Displays the 'you have been challenged' screen.
+*/
-class CChallengeMenu: public Menu
+class CChallengeMenu: public CMenu
{
public:
CChallengeMenu::CChallengeMenu( std::string a_sChallenger )
- : Menu( "You have been challenged!" ),
+ : CMenu( "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 )
+ void ItemActivated( int a_iItemCode, CMenuItem* 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;
};
+/**
+\ingroup Network
+
+The Chat menu is displayed instead of the regular CMenu when the user is
+connected to MortalNet. The Chat menu has a different set of operations.
+*/
+
-class CChatMenu: public Menu
+class CChatMenu: public CMenu
{
public:
CChatMenu( const TNickMap& a_roNicks )
- : Menu( "MortalNet Menu" ),
+ : CMenu( "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 )
+ void ItemActivated( int a_iItemCode, CMenuItem* 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_sChallengedNick = ((CEnumMenuItem*)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 );
+ CMenu::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;
+ CTextMenuItem* 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.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( "NETWORK_MESSAGE" );
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':
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':
{
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':
{
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':
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':
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/OnlineChat.h b/src/OnlineChat.h
index 68a4552..0ef7c65 100644
--- a/src/OnlineChat.h
+++ b/src/OnlineChat.h
@@ -1,92 +1,95 @@
/***************************************************************************
OnlineChat.h - description
-------------------
begin : Thu Jan 29 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
#ifndef ONLINECHAT_H
#define ONLINECHAT_H
-/** The "mortal.net" chat protocoll should be simple enough...
+/**
+\class COnlineChat
+\ingroup Network
+The "mortal.net" chat protocol should be simple enough...
[LOGGING IN]
Server: <greeting msg>
Client: N <version> <nickname>
Server: OK <actual nickname>
[CHATTING]
Server: J <user> <IP> - User joins.
Server: L <user> - User leaves.
Server: N <user> <newnick> - Nick change.
Server: W <user> <IP> - Whois response
Server: Y <num> - You are now known as...
Server: C <user> - Someone has challenged you!
Server: M <user> <text> - Public message.
Server: S <text> - Server message.
Client: M <text> - Send message.
Client: W <user> - WHOIS query
Client: L - Quit
Client: C <user> - Challenge a user
*/
#include "SDL_net.h"
#include <string>
#include <map>
class CReadline;
class CTextArea;
struct SDL_Surface;
typedef std::map<std::string,std::string> TNickMap;
class COnlineChat
{
public:
COnlineChat();
~COnlineChat();
bool Start();
void Stop();
void DoOnlineChat();
protected:
void Redraw();
void Update(); // Read network traffic. Might get disconnected...
void SendRawData( char a_cID, const std::string& a_rsData );
void ReceiveMsg( char a_cID, char* a_pcData );
void ReceiveUser( char a_cID, char* a_pcData );
void DrawNickList();
void Menu();
protected:
TCPsocket m_poSocket; ///< The TCP/IP network socket.
SDLNet_SocketSet m_poSocketSet; ///< SDLNet construct for watching the socket.
char m_acIncomingBuffer[4096]; ///< Received data goes here.
int m_iIncomingBufferSize; ///< How much of the buffer is filled?
std::string m_sLastError; ///< The last error message from SDLNet
SDL_Surface* m_poScreen;
SDL_Surface* m_poBackground;
CReadline* m_poReadline;
CTextArea* m_poTextArea;
bool m_bMyNickIsOk;
TNickMap m_asNicks;
};
#endif // ONLINECHAT_H
diff --git a/src/PlayerSelect.cpp b/src/PlayerSelect.cpp
index e581216..fd53d3b 100644
--- a/src/PlayerSelect.cpp
+++ b/src/PlayerSelect.cpp
@@ -1,768 +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;
+CPlayerSelect g_oPlayerSelect;
-PlayerSelect::PlayerSelect()
+CPlayerSelect::CPlayerSelect()
{
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;
}
}
/*************************************************************************
GENERAL PLAYER INFO
*************************************************************************/
-const PlayerInfo& PlayerSelect::GetPlayerInfo( int a_iPlayer )
+const SPlayerInfo& CPlayerSelect::GetPlayerInfo( int a_iPlayer )
{
return m_aoPlayers[ a_iPlayer ];
}
-PlayerInfo& PlayerSelect::EditPlayerInfo( int a_iPlayer )
+SPlayerInfo& CPlayerSelect::EditPlayerInfo( int a_iPlayer )
{
return m_aoPlayers[ a_iPlayer ];
}
-const char* PlayerSelect::GetFighterName( int a_iPlayer )
+const char* CPlayerSelect::GetFighterName( int a_iPlayer )
{
return m_aoPlayers[ a_iPlayer ].m_sFighterName.c_str();
}
-int PlayerSelect::GetFighterNameWidth( int a_iPlayer )
+int CPlayerSelect::GetFighterNameWidth( int a_iPlayer )
{
return m_aiFighterNameWidth[ a_iPlayer ];
}
-bool PlayerSelect::IsFighterAvailable( FighterEnum a_enFighter )
+bool CPlayerSelect::IsFighterAvailable( FighterEnum a_enFighter )
{
if ( a_enFighter <= UNKNOWN )
{
return false;
}
bool bLocalAvailable = IsLocalFighterAvailable( a_enFighter );
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 )
+bool CPlayerSelect::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 )
+bool CPlayerSelect::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.
+fighter, loads it, and returns the CRlePack.
-\return The freshly loaded RlePack, or NULL if it could not be loaded.
+\return The freshly loaded CRlePack, or NULL if it could not be loaded.
*/
-RlePack* PlayerSelect::LoadFighter( FighterEnum m_enFighter ) // static
+CRlePack* CPlayerSelect::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 );
+ CRlePack* pack = new CRlePack( a_pcFilename, COLORSPERPLAYER );
if ( pack->Count() <= 0 )
{
- debug( "Couldn't load RlePack: '%s'\n", a_pcFilename );
+ debug( "Couldn't load CRlePack: '%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
+The CRlePack 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 )
+void CPlayerSelect::SetPlayer( int a_iPlayer, FighterEnum a_enFighter )
{
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 = COLOROFFSETPLAYER1 + a_iPlayer*64;
- RlePack* poPack = LoadFighter( a_enFighter );
+ CRlePack* poPack = LoadFighter( a_enFighter );
poPack->OffsetSprites( iOffset );
if ( NULL == poPack )
{
- debug( "SetPlayer(%d,%d): Couldn't load RlePack\n", a_iPlayer, a_enFighter );
+ debug( "SetPlayer(%d,%d): Couldn't load CRlePack\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 )
+void CPlayerSelect::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()
+bool CPlayerSelect::IsNetworkGame()
{
return SState::IN_NETWORK == g_oState.m_enGameMode;
}
-FighterEnum PlayerSelect::GetFighterCell( int a_iIndex )
+FighterEnum CPlayerSelect::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 )
+void CPlayerSelect::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()
+void CPlayerSelect::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 )
+void CPlayerSelect::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 )
+void CPlayerSelect::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()
+void CPlayerSelect::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(%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, 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, 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()
+void CPlayerSelect::DoPlayerSelect()
{
CPlayerSelectController oController( SState::IN_NETWORK == g_oState.m_enGameMode );
oController.DoPlayerSelect();
}
#endif
diff --git a/src/PlayerSelect.h b/src/PlayerSelect.h
index 22459ff..391970d 100644
--- a/src/PlayerSelect.h
+++ b/src/PlayerSelect.h
@@ -1,93 +1,102 @@
/***************************************************************************
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)
+/**
+\defgroup PlayerSelect Fighter selection
+This module runs the fighter selection part of the game.
+*/
#include "FighterEnum.h"
#include "common.h"
#include <string>
#include <vector>
-class RlePack;
+class CRlePack;
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. */
+CRlePack.
+\ingroup PlayerSelect
+*/
-struct PlayerInfo
+struct SPlayerInfo
{
FighterEnum m_enFighter;
TintEnum m_enTint;
- RlePack* m_poPack;
+ CRlePack* 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
+/** 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
+effect.)
+
+This is the model part model-view-controller architecture of the player selection.
+
+
+\ingroup PlayerSelect
+*/
+
+class CPlayerSelect
{
public:
- PlayerSelect();
+ CPlayerSelect();
- const PlayerInfo& GetPlayerInfo( int a_iPlayer );
- PlayerInfo& EditPlayerInfo( int a_iPlayer );
+ const SPlayerInfo& GetPlayerInfo( int a_iPlayer );
+ SPlayerInfo& 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 );
- static RlePack* LoadFighter( FighterEnum m_enFighter );
-// bool IsNetworkGame();
-// FighterEnum GetFighterCell( int a_iIndex );
+ static CRlePack* LoadFighter( FighterEnum m_enFighter );
protected:
- PlayerInfo m_aoPlayers[MAXPLAYERS];
+ SPlayerInfo m_aoPlayers[MAXPLAYERS];
int m_aiFighterNameWidth[MAXPLAYERS];
};
-extern PlayerSelect g_oPlayerSelect;
+extern CPlayerSelect g_oPlayerSelect;
#endif // PLAYERSELECT_H
diff --git a/src/PlayerSelectController.cpp b/src/PlayerSelectController.cpp
index 375bfac..6f3b054 100644
--- a/src/PlayerSelectController.cpp
+++ b/src/PlayerSelectController.cpp
@@ -1,505 +1,505 @@
#include "PlayerSelectController.h"
#include "PlayerSelectView.h"
#include "PlayerSelect.h"
#include "Chooser.h"
#include "MortalNetwork.h"
#include "State.h"
#include "Backend.h"
#include "Event.h"
#include "Audio.h"
#include "TextArea.h"
#include "sge_tt_text.h"
#include "common.h"
int GetBackgroundNumber();
CPlayerSelectController::CPlayerSelectController( bool a_bNetworkGame )
{
m_bNetworkGame = a_bNetworkGame;
m_bTeamMode = SState::Team_CUSTOM == g_oState.m_enTeamMode;
m_poView = new CPlayerSelectView( a_bNetworkGame, m_bTeamMode );
}
CPlayerSelectController::~CPlayerSelectController()
{
delete m_poView;
m_poView = NULL;
}
void CPlayerSelectController::GetThisTick()
{
m_iLastTick = m_iThisTick;
while (1)
{
m_iThisTick = SDL_GetTicks() / m_iGameSpeed;
if ( m_iThisTick==m_iLastTick )
{
SDL_Delay(1);
}
else
{
break;
}
}
}
bool CPlayerSelectController::HandleChatKey( SDL_Event& a_roEvent )
{
if ( !m_bNetworkGame )
{
return false;
}
SDLKey enKey = a_roEvent.key.keysym.sym;
if ( enKey == SDLK_PAGEUP || enKey == SDLK_KP9 )
{
m_poView->GetTextArea()->ScrollUp();
return true;
}
if ( enKey == SDLK_PAGEDOWN || enKey == SDLK_KP3 )
{
m_poView->GetTextArea()->ScrollDown();
return true;
}
if ( !m_bChatActive && ( SDLK_TAB == enKey
|| SDLK_RETURN == enKey || SDLK_KP_ENTER == enKey ) )
{
// Activate chat.
m_acChatMsg[0] = 0;
m_poView->GetReadline()->Clear();
m_poView->GetReadline()->Restart( m_acChatMsg, strlen(m_acChatMsg), 256, C_LIGHTCYAN, C_BLACK, 255 );
m_bChatActive = true;
return true;
}
if ( !m_bChatActive )
{
return false;
}
if ( SDLK_ESCAPE == enKey || SDLK_TAB == enKey )
{
// Deactivate chat.
m_poView->GetReadline()->Clear();
m_bChatActive = false;
return true;
}
// other keys..
m_poView->GetReadline()->HandleKeyEvent( a_roEvent );
int iResult = m_poView->GetReadline()->GetResult();
if ( iResult < 0 )
{
// Escape was pressed?
m_poView->GetReadline()->Clear();
m_bChatActive = false;
}
if ( iResult > 0 )
{
if ( strlen( m_acChatMsg ) )
{
g_poNetwork->SendMsg( m_acChatMsg );
std::string sMsg = std::string("<") + g_oState.m_acNick + "> " + m_acChatMsg;
m_poView->GetTextArea()->AddString( sMsg.c_str(), C_LIGHTCYAN );
m_poView->GetTextArea()->Redraw();
m_acChatMsg[0] = 0;
m_poView->GetReadline()->Clear();
m_poView->GetReadline()->Restart( m_acChatMsg, strlen(m_acChatMsg), 256, C_LIGHTCYAN, C_BLACK, 255 );
}
else
{
m_poView->GetReadline()->Clear();
m_bChatActive = false;
}
}
return true;
}
void CPlayerSelectController::HandleEvents()
{
SDL_Event oSdlEvent;
while (SDL_PollEvent(&oSdlEvent))
{
if ( m_bNetworkGame
&& SDL_KEYDOWN == oSdlEvent.type )
{
if (HandleChatKey( oSdlEvent ))
{
continue;
}
}
if ( m_bNetworkGame
&& SDL_KEYDOWN == oSdlEvent.type
&& (oSdlEvent.key.keysym.sym == SDLK_RETURN || oSdlEvent.key.keysym.sym==SDLK_KP_ENTER) )
{
m_bChatActive = true;
continue;
}
SMortalEvent oEvent;
TranslateEvent( &oSdlEvent, &oEvent );
switch ( oEvent.m_enType )
{
case Me_QUIT:
g_oState.m_bQuitFlag = true;
break;
case Me_MENU:
DoMenu();
if ( m_bNetworkGame && g_poNetwork->IsMaster() )
{
g_poNetwork->SendGameParams( g_oState.m_iGameSpeed, g_oState.m_iGameTime, g_oState.m_iHitPoints, GetBackgroundNumber() );
}
break;
case Me_PLAYERKEYDOWN:
HandleKey( oEvent.m_iPlayer, oEvent.m_iKey );
break;
case Me_NOTHING:
case Me_SKIP:
case Me_PLAYERKEYUP:
break;
} // end of switch statement
}
}
void CPlayerSelectController::HandleKey( int a_iPlayer, int a_iKey )
{
if ( m_bNetworkGame )
{
a_iPlayer = g_poNetwork->IsMaster() ? 0 : 1;
}
if ( !m_abPlayerActive[a_iPlayer] )
{
return;
}
int iSetPlayer = a_iPlayer;
if ( m_bTeamMode && !g_oState.m_bTeamMultiselect )
{
iSetPlayer = 0;
}
// MOVE THE CURSOR
if ( a_iKey < 4 )
{
g_oChooser.MoveRectangle( a_iPlayer, a_iKey );
FighterEnum enNewFighter = g_oChooser.GetCurrentFighter( a_iPlayer );
if ( enNewFighter != g_oPlayerSelect.GetPlayerInfo( iSetPlayer ).m_enFighter )
{
Audio->PlaySample("PLAYER_SELECTION_CHANGES");
if ( IsFighterSelectable(enNewFighter) )
{
if ( m_bNetworkGame )
{
g_poNetwork->SendFighter( enNewFighter );
}
g_oPlayerSelect.SetPlayer( iSetPlayer, enNewFighter );
}
}
return;
}
// SELECT THE CURRENT FIGHTER
FighterEnum enFighter = g_oChooser.GetCurrentFighter( a_iPlayer );
if ( !IsFighterSelectable( enFighter ) )
{
// Current fighter cannot be selected.
return;
}
Audio->PlaySample("PLAYER_SELECTED");
g_oBackend.PerlEvalF( "PlayerSelected(%d);", iSetPlayer );
if ( m_bNetworkGame )
{
g_poNetwork->SendFighter( enFighter );
g_poNetwork->SendReady();
}
bool bKeepPlayerActive = false;
- PlayerInfo& roInfo = g_oPlayerSelect.EditPlayerInfo(a_iPlayer);
- const PlayerInfo& roOtherInfo = g_oPlayerSelect.GetPlayerInfo(1-a_iPlayer);
+ SPlayerInfo& roInfo = g_oPlayerSelect.EditPlayerInfo(a_iPlayer);
+ const SPlayerInfo& roOtherInfo = g_oPlayerSelect.GetPlayerInfo(1-a_iPlayer);
int iTeamSize = roInfo.m_aenTeam.size() + 1;
int iOtherTeamSize = roOtherInfo.m_aenTeam.size();
if ( m_bTeamMode )
{
m_poView->AddFighterToTeam( a_iPlayer, enFighter );
roInfo.m_aenTeam.push_back( enFighter );
if ( g_oState.m_bTeamMultiselect )
{
SetPlayerActive( a_iPlayer, iTeamSize < g_oState.m_iTeamSize );
}
else
{
--m_iNumberOfSelectableFighters;
g_oChooser.MarkFighter( enFighter, a_iPlayer ? C_LIGHTMAGENTA : C_LIGHTGREEN );
ActivateNextPlayer( a_iPlayer );
}
}
else
{
roInfo.m_aenTeam.clear();
roInfo.m_aenTeam.push_back( enFighter );
SetPlayerActive( a_iPlayer, false );
}
}
void CPlayerSelectController::HandleNetwork()
{
g_poNetwork->Update();
// 1. READ CHAT MESSAGES
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_poView->GetTextArea()->AddString( pcMsg, iColor );
bUpdateText = true;
}
if ( bUpdateText )
{
Audio->PlaySample("NETWORK_MESSAGE");
m_poView->GetTextArea()->Redraw();
}
// 2. READ PLAYER SELECTION ACTIONS
bool bMaster = g_poNetwork->IsMaster();
int iPlayer = bMaster ? 1 : 0;
if ( !m_abPlayerActive[iPlayer] )
{
return;
}
FighterEnum enOldFighter = g_oPlayerSelect.GetPlayerInfo(iPlayer).m_enFighter;
FighterEnum enRemoteFighter = g_poNetwork->GetRemoteFighter();
if ( enOldFighter != enRemoteFighter
&& enRemoteFighter != UNKNOWN )
{
Audio->PlaySample("PLAYER_SELECTION_CHANGES");
g_oPlayerSelect.SetPlayer( iPlayer, enRemoteFighter );
g_oChooser.SetRectangle( iPlayer, enRemoteFighter );
}
bool bDone = g_poNetwork->IsRemoteSideReady();
if ( bDone )
{
m_abPlayerActive[iPlayer] = false;
Audio->PlaySample("PLAYER_SELECTED");
g_oBackend.PerlEvalF( "PlayerSelected(%d);", iPlayer );
- PlayerInfo& roInfo = g_oPlayerSelect.EditPlayerInfo( iPlayer );
+ SPlayerInfo& roInfo = g_oPlayerSelect.EditPlayerInfo( iPlayer );
roInfo.m_aenTeam.clear();
roInfo.m_aenTeam.push_back( enRemoteFighter );
SetPlayerActive( iPlayer, false );
}
}
void CPlayerSelectController::MarkFighters()
{
int iNumberOfFighters = g_oBackend.GetNumberOfFighters();
int i;
for ( i=0; i<iNumberOfFighters; ++i )
{
FighterEnum enFighter = g_oBackend.GetFighterID( i );
if ( !g_oPlayerSelect.IsLocalFighterAvailable( enFighter ) )
{
g_oChooser.MarkFighter( enFighter, C_LIGHTRED );
}
else if ( !g_oPlayerSelect.IsFighterAvailable( enFighter ) )
{
g_oChooser.MarkFighter( enFighter, C_LIGHTBLUE );
}
}
}
void CPlayerSelectController::SetPlayerActive( int a_iPlayer, bool a_bActive )
{
m_abPlayerActive[a_iPlayer] = a_bActive;
g_oChooser.SetRectangleVisible( a_iPlayer, a_bActive );
}
/** This method finds the next player and lets him choose a fighter.
This only should be called in team mode without multiselect.
*/
void CPlayerSelectController::ActivateNextPlayer( int a_iCurrentPlayer )
{
// Go in the following order:
// 1 2 3 4 4 3 2 1 1 2 3 4 ...
int iNextPlayer = a_iCurrentPlayer;
if ( m_iNumberOfSelectableFighters <= 0 )
{
SetPlayerActive( a_iCurrentPlayer, false );
return;
}
if ( a_iCurrentPlayer == 0 )
{
iNextPlayer = 1;
}
else if ( g_oState.m_iNumPlayers-1 == a_iCurrentPlayer )
{
iNextPlayer = a_iCurrentPlayer - 1;
}
else
{
iNextPlayer = GetTeamSize(a_iCurrentPlayer-1) > GetTeamSize(a_iCurrentPlayer+1) ?
a_iCurrentPlayer+1 : a_iCurrentPlayer-1;
}
if ( GetTeamSize(a_iCurrentPlayer) <= GetTeamSize(iNextPlayer) )
{
iNextPlayer = a_iCurrentPlayer;
}
if ( iNextPlayer != a_iCurrentPlayer )
{
SetPlayerActive( a_iCurrentPlayer, false );
}
SetPlayerActive( iNextPlayer, GetTeamSize(iNextPlayer) < g_oState.m_iTeamSize );
}
int CPlayerSelectController::GetTeamSize( int a_iPlayer )
{
return g_oPlayerSelect.GetPlayerInfo( a_iPlayer ).m_aenTeam.size();
}
bool CPlayerSelectController::IsFighterSelectable( FighterEnum a_enFighter )
{
if ( !g_oPlayerSelect.IsFighterAvailable( a_enFighter ) )
{
return false;
}
if ( m_bTeamMode && !g_oState.m_bTeamMultiselect && g_oPlayerSelect.IsFighterInTeam(a_enFighter) )
{
return false;
}
return true;
}
void CPlayerSelectController::DoPlayerSelect()
{
// 1. INITIALIZE
if ( SState::IN_NETWORK == g_oState.m_enGameMode )
{
g_oState.m_enTeamMode = SState::Team_ONE_VS_ONE;
g_oState.m_iNumPlayers = 2;
}
int i;
for ( i=0; i<g_oState.m_iNumPlayers; ++i )
{
g_oPlayerSelect.SetPlayer( i, g_oChooser.GetCurrentFighter(i) );
SetPlayerActive( i, !m_bTeamMode || g_oState.m_bTeamMultiselect || 0 == i );
}
m_iNumberOfSelectableFighters = g_oBackend.GetNumberOfAvailableFighters();
MarkFighters();
if ( m_bTeamMode )
{
for ( int i=0; i<g_oState.m_iNumPlayers; ++i )
{
g_oPlayerSelect.EditPlayerInfo(i).m_aenTeam.clear();
}
}
if ( m_bNetworkGame && g_poNetwork->IsMaster() )
{
g_poNetwork->SendGameParams( g_oState.m_iGameSpeed, g_oState.m_iGameTime, g_oState.m_iHitPoints, GetBackgroundNumber() );
}
g_oBackend.PerlEvalF( "SelectStart(%d);", g_oState.m_iNumPlayers );
m_bChatActive = false;
m_iGameSpeed = 12 ;
m_iThisTick = SDL_GetTicks() / m_iGameSpeed;
m_iLastTick = m_iThisTick - 1;
// 2. RUN THE PLAYER SELECTION LOOP
while (1)
{
// 2.1. Wait for the next tick (on extremely fast machines..)
GetThisTick();
int iAdvance = m_iThisTick - m_iLastTick;
if ( iAdvance > 5 )
iAdvance = 5;
// 2.2. Handle events
HandleEvents();
if ( m_bNetworkGame )
{
HandleNetwork();
}
// 2.3. Update the view
m_poView->Advance( iAdvance );
m_poView->Draw();
if ( g_oState.m_bQuitFlag || SState::IN_DEMO == g_oState.m_enGameMode)
break;
//@ FIX THIS
if ( !m_abPlayerActive[0] && !m_abPlayerActive[1] && m_poView->IsOver() )
break;
}
}
diff --git a/src/PlayerSelectController.h b/src/PlayerSelectController.h
index f88d044..0f59912 100644
--- a/src/PlayerSelectController.h
+++ b/src/PlayerSelectController.h
@@ -1,49 +1,54 @@
#ifndef PLAYERSELECTCONTROLLER_H
#define PLAYERSELECTCONTROLLER_H
#include "SDL.h"
#include "FighterEnum.h"
#include "common.h"
class CPlayerSelectView;
+/**
+Controller part model-view-controller architecture of the player selection.
+\ingroup PlayerSelect
+*/
+
class CPlayerSelectController
{
public:
CPlayerSelectController( bool a_bNetworkGame );
~CPlayerSelectController();
void DoPlayerSelect();
protected:
void HandleEvents();
void HandleKey( int a_iPlayer, int a_iKey );
bool HandleChatKey( SDL_Event& a_roEvent );
void HandleNetwork();
void GetThisTick();
void MarkFighters();
void SetPlayerActive( int a_iPlayer, bool a_bActive );
void ActivateNextPlayer( int a_iCurrentPlayer );
int GetTeamSize( int a_iPlayer );
bool IsFighterSelectable( FighterEnum a_enFighter );
protected:
CPlayerSelectView* m_poView;
bool m_bNetworkGame;
bool m_bTeamMode;
bool m_bChatActive;
char m_acChatMsg[256];
int m_iNumberOfSelectableFighters;
bool m_abPlayerActive[MAXPLAYERS];
int m_iGameSpeed;
int m_iThisTick;
int m_iLastTick;
};
#endif // PLAYERSELECTCONTROLLER_H
diff --git a/src/PlayerSelectView.cpp b/src/PlayerSelectView.cpp
index 24624bc..c16cbe9 100644
--- a/src/PlayerSelectView.cpp
+++ b/src/PlayerSelectView.cpp
@@ -1,587 +1,598 @@
#include "sge_tt_text.h"
#include "sge_bm_text.h"
#include "sge_primitives.h"
#include "RlePack.h"
#include "PlayerSelectView.h"
#include "PlayerSelect.h"
#include "Chooser.h"
#include "TextArea.h"
#include "Backend.h"
#include "State.h"
#include "gfx.h"
#include "common.h"
+/**
+Displays teams in the player selection screen for CPlayerSelectView.
+\ingroup PlayerSelect
+*/
class CTeamDisplay
{
public:
CTeamDisplay( int a_iPlayer, SDL_Surface* a_poScreen, const SDL_Rect& a_roRect );
~CTeamDisplay();
void DrawTitle( Uint32 a_iColor );
void DrawQuestionMark( Uint32 a_iColor, int a_iNumber=-1 );
void AddFighter( FighterEnum a_enFighter );
void DrawFighter( FighterEnum a_enFighter, int a_iNumber );
SDL_Rect GetRect( int a_iNumber=-1 );
int GetCount() { return m_iCount; }
protected:
int m_iPlayer;
SDL_Surface* m_poScreen;
SDL_Rect m_oRect;
int m_iTitleX;
int m_iCount;
int m_iMax;
};
CTeamDisplay::CTeamDisplay( int a_iPlayer, SDL_Surface* a_poScreen, const SDL_Rect& a_roRect )
{
m_iPlayer = a_iPlayer;
m_poScreen = a_poScreen;
m_oRect = a_roRect;
m_iCount = 0;
m_iMax = g_oState.m_iTeamSize;
m_iTitleX = m_oRect.x;
const char* pcFormat = Translate("Team %d");
SDL_Rect oTextSize = sge_TTF_TextSize( impactFont, pcFormat );
int w = oTextSize.w;
m_oRect.w -= w;
m_oRect.x += w;
DrawTitle( C_WHITE );
for ( int i=0; i< m_iMax; ++i )
{
DrawQuestionMark( C_WHITE, i );
}
}
CTeamDisplay::~CTeamDisplay() {}
void CTeamDisplay::DrawTitle( Uint32 a_iColor )
{
char acBuffer[128];
const char* pcFormat = Translate("Team %d");
sprintf( acBuffer, pcFormat, m_iPlayer+1 );
DrawTextMSZ( acBuffer, impactFont, m_iTitleX, m_oRect.y+m_oRect.h/2,
AlignVCenter|UseShadow, a_iColor, m_poScreen, false );
}
void CTeamDisplay::DrawQuestionMark( Uint32 a_iColor, int a_iNumber )
{
if ( a_iNumber < 0 ) a_iNumber = m_iCount;
if ( a_iNumber >= m_iMax ) return;
SDL_Rect oRect = GetRect( a_iNumber );
sge_Rect( m_poScreen, oRect.x, oRect.y, oRect.x+oRect.w, oRect.y+oRect.h, C_WHITE );
DrawTextMSZ( "?", impactFont, oRect.x+oRect.w/2, oRect.y+oRect.h/2, AlignCenter, a_iColor, m_poScreen, false );
}
void CTeamDisplay::AddFighter( FighterEnum a_enFighter )
{
DrawQuestionMark( C_WHITE );
++m_iCount;
}
void CTeamDisplay::DrawFighter( FighterEnum a_enFighter, int a_iNumber )
{
SDL_Rect oRect = GetRect( a_iNumber );
g_oChooser.DrawPortrait( a_enFighter, m_poScreen, oRect );
sge_Rect( m_poScreen, oRect.x, oRect.y, oRect.x+oRect.w, oRect.y+oRect.h, C_WHITE );
}
SDL_Rect CTeamDisplay::GetRect( int a_iNumber )
{
if ( a_iNumber < 0 ) a_iNumber = m_iCount;
if ( m_oRect.h > 75 )
{
m_oRect.y += (m_oRect.h - 75) / 2;
m_oRect.h = 75;
}
int iGap = 5;
int iWidth = m_oRect.w / m_iMax;
if ( iWidth > m_oRect.h + iGap ) iWidth = m_oRect.h + iGap;
SDL_Rect oRect;
oRect.y = m_oRect.y;
oRect.h = m_oRect.h;
oRect.x = m_oRect.x + iWidth * a_iNumber;
oRect.w = iWidth - iGap;
return oRect;
}
IViewElement::IViewElement( CPlayerSelectView* a_poView, int a_iPriority )
{
m_poView = a_poView;
m_iPriority = a_iPriority;
m_poView->AddViewElement( this, m_iPriority );
}
IViewElement::~IViewElement()
{
}
int IViewElement::GetPriority() const
{
return m_iPriority;
}
-/**
+/**
+Flying portrait element for CPlayerSelectView.
The portrait of a given fighter will gracefully fly from the chooser
to a team display. The view element will delete itself afterwards.
+\ingroup PlayerSelect
*/
class CFlyingPortraitViewElement: public IViewElement
{
public:
CFlyingPortraitViewElement( CPlayerSelectView* a_poView, CTeamDisplay* a_poTeamDisplay,
FighterEnum a_enFighter, const SDL_Rect& a_oSrcRect, const SDL_Rect& a_oDstRect )
: IViewElement( a_poView, 0 )
{
m_poTeamDisplay = a_poTeamDisplay;
m_iTeamNumber = m_poTeamDisplay->GetCount();
m_enFighter = a_enFighter;
m_oRect = a_oSrcRect;
m_oDstRect = a_oDstRect;
m_dX = m_oRect.x + m_oRect.w / 2;
m_dY = m_oRect.y + m_oRect.h / 2;
m_dSize = MIN( m_oRect.w, m_oRect.h );
m_dSpeedX = 0.0;
m_dSpeedY = - m_dY / 15;
m_dSpeedSize= 0.0;
m_dTargetX = m_oDstRect.x + m_oDstRect.w / 2;
m_dTargetY = m_oDstRect.y + m_oDstRect.h / 2;
m_dTargetSize = MIN( m_oDstRect.w, m_oDstRect.h);
m_iTotalTime = 70 + m_dY / 7.5;
m_iTime = m_iTotalTime;
}
~CFlyingPortraitViewElement() {}
void Advance( int a_iNumFrames )
{
if ( m_dX + m_dSpeedX * 0.5 * m_iTime > m_dTargetX ) m_dSpeedX -= a_iNumFrames * 0.5; else m_dSpeedX += a_iNumFrames * 0.5;
if ( m_dY + m_dSpeedY * 0.5 * m_iTime > m_dTargetY ) m_dSpeedY -= a_iNumFrames * 0.5; else m_dSpeedY += a_iNumFrames * 0.5;
m_dX += m_dSpeedX * a_iNumFrames;
m_dY += m_dSpeedY * a_iNumFrames;
double dTargetSize = (m_iTime > m_iTotalTime/2) ? 75.0 : m_dTargetSize;
int iTargetTime = (m_iTime > m_iTotalTime/2) ? m_iTime : ( m_iTotalTime/2-m_iTime );
if ( m_dSize + m_dSpeedSize * iTargetTime > dTargetSize ) m_dSpeedSize -= a_iNumFrames * 0.3; else m_dSpeedSize += a_iNumFrames * 0.3;
m_dSize += m_dSpeedSize * a_iNumFrames;
m_oRect.x = int( m_dX - m_dSize / 2 );
m_oRect.y = int( m_dY - m_dSize / 2 );
m_oRect.w = m_oRect.h = (int)m_dSize;
m_iTime -= a_iNumFrames;
if ( m_iTime < 0 )
{
m_poTeamDisplay->DrawFighter( m_enFighter, m_iTeamNumber );
m_poView->RemoveViewElement( this );
delete( this );
}
}
void Draw()
{
g_oChooser.DrawPortrait( m_enFighter, gamescreen, m_oRect );
}
protected:
CTeamDisplay* m_poTeamDisplay;
int m_iTeamNumber;
FighterEnum m_enFighter;
double m_dX, m_dY, m_dSize;
double m_dSpeedX, m_dSpeedY, m_dSpeedSize;
double m_dTargetX, m_dTargetY, m_dTargetSize;
int m_iTotalTime;
int m_iTime;
SDL_Rect m_oDstRect;
SDL_Rect m_oRect;
int m_iNumber;
};
/**
-This view element implements the "opening courtain" effect at the start
-of the player selection routine. It removes itself when the effect is
-finished
+This view element implements the "opening courtain" effect at the start
+of the player selection routine.
+It removes itself when the effect is
+finished.
+\ingroup PlayerSelect
*/
class CCourtainViewElement: public IViewElement
{
public:
CCourtainViewElement( CPlayerSelectView* a_poView ) : IViewElement( a_poView, 0 )
{
m_iCourtain = 0;
m_iCourtainSpeed = 0;
m_iCourtainTime = 80;
}
~CCourtainViewElement() {}
void Advance( int a_iNumFrames )
{
if ( a_iNumFrames > 5 ) a_iNumFrames = 5;
if ( m_iCourtainTime <= 0 )
{
SDL_SetClipRect( gamescreen, NULL );
m_poView->RemoveViewElement( this );
delete( this );
}
if ( m_iCourtain + m_iCourtainSpeed * m_iCourtainTime /2 < 320 * 4 )
m_iCourtainSpeed += a_iNumFrames;
else
m_iCourtainSpeed -= a_iNumFrames;
m_iCourtain += m_iCourtainSpeed * a_iNumFrames;
m_iCourtainTime -= a_iNumFrames;
if ( m_iCourtainTime > 0 )
{
SDL_Rect oRect;
oRect.x = 320 - m_iCourtain/4; oRect.y = 0;
oRect.w = m_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 );
}
}
void Draw()
{
}
protected:
int m_iCourtain;
int m_iCourtainSpeed;
int m_iCourtainTime;
};
-/** This view element flashes the title and '?' mark of active teams. */
+/**
+This view element flashes the title and '?' mark of active teams.
+\ingroup PlayerSelect
+*/
class CTeamFlashViewElement: public IViewElement
{
public:
CTeamFlashViewElement( CPlayerSelectView* a_poView, int a_iPriority )
: IViewElement( a_poView, a_iPriority )
{
for ( int i=0; i<MAXPLAYERS; ++i )
{
m_bActive[i] = false;
}
}
~CTeamFlashViewElement() {}
void Advance( int a_iNumFrames )
{
m_iCycle = (m_iCycle + a_iNumFrames*10) % 512;
}
void Draw()
{
int iLum = m_iCycle < 256 ? m_iCycle : 511-m_iCycle;
for ( int i=0; i<g_oState.m_iNumPlayers; ++i )
{
if ( g_oChooser.IsRectangleVisible( i ) )
{
SDL_Color oColor;
switch (i)
{
case 0: oColor.r = 85; oColor.g = 255; oColor.b = 85; break;
case 1: oColor.r = 255; oColor.g = 85; oColor.b = 255; break;
case 2: oColor.r = 255; oColor.g = 255; oColor.b = 85; break;
default: oColor.r = 85; oColor.g = 85; oColor.b = 255; break;
}
Uint32 iColor = SDL_MapRGB( gamescreen->format, oColor.r*iLum/256, oColor.g*iLum/256, oColor.b*iLum/256 );
m_poView->GetTeamDisplay(i)->DrawTitle( iColor );
m_poView->GetTeamDisplay(i)->DrawQuestionMark( iColor );
m_bActive[i] = true;
}
else if ( m_bActive[i] )
{
m_poView->GetTeamDisplay(i)->DrawTitle( C_WHITE );
m_bActive[i] = false;
}
}
}
protected:
int m_iCycle;
bool m_bActive[MAXPLAYERS];
};
/*************************************************************************
CPLAYERSELECTVIEW CLASS
*************************************************************************/
CPlayerSelectView::CPlayerSelectView( bool a_bNetworkGame, bool a_bTeamMode )
{
m_bOver = false;
m_bNetworkGame = a_bNetworkGame;
m_bTeamMode = a_bTeamMode;
m_bTeamMultiselect = g_oState.m_bTeamMultiselect;
m_poReadline = NULL;
m_poTextArea = NULL;
m_iTime = 0;
int i;
for ( i=0; i<MAXPLAYERS; ++i ) m_apoTeamDisplays[i] = NULL;
SDL_FillRect( gamescreen, NULL, C_BLACK );
SDL_Flip( gamescreen );
m_poBackground = LoadBackground( "FighterStats.jpg", 64 ); //m_bNetworkGame ? "PlayerSelect_chat.png" : "PlayerSelect.png", 111 );
if ( m_poBackground ) SDL_SetColorKey( m_poBackground, 0, 0 );
new CCourtainViewElement(this);
if ( m_bTeamMode )
{
new CTeamFlashViewElement(this, 0);
SDL_Rect oRect;
oRect.x = 10; oRect.w = 620;
oRect.y = 380; oRect.h = 40;
m_apoTeamDisplays[0] = new CTeamDisplay( 0, m_poBackground, oRect );
oRect.y = 430; oRect.h = 40;
m_apoTeamDisplays[1] = new CTeamDisplay( 1, m_poBackground, oRect );
if ( g_oState.m_iNumPlayers >= 3 )
{
oRect.y = 330;
m_apoTeamDisplays[2] = new CTeamDisplay( 2, m_poBackground, oRect );
}
if ( g_oState.m_iNumPlayers >= 4 )
{
oRect.y = 280;
m_apoTeamDisplays[3] = new CTeamDisplay( 3, m_poBackground, oRect );
}
if ( m_bTeamMultiselect )
{
m_iChooserTop = 25;
m_iChooserHeight = 80 * 4;
m_iChooserLeft = 158;
m_iChooserWidth = 64*5;
}
else
{
DrawGradientText( "Choose A Fighter Dammit", titleFont, 10, m_poBackground );
m_iChooserTop = 75;
m_iChooserHeight = 80 * 4 - 50;
m_iChooserLeft = 180;
m_iChooserWidth = 430;
}
m_iFighterYOffset = -100;
m_iFighterNameYOffset = -95;
}
else if ( m_bNetworkGame )
{
m_iChooserLeft = 158;
m_iChooserTop = 26;
m_iChooserWidth = 64 * 5;
m_iChooserHeight = 64 * 4;
m_iFighterYOffset = -130;
m_iFighterNameYOffset = -170;
}
else
{
m_iChooserLeft = 158;
m_iChooserTop = 74;
m_iChooserWidth = 80*4;
m_iChooserHeight = 80*5;
m_iFighterYOffset = 0;
m_iFighterNameYOffset = 0;
}
g_oChooser.Start( m_poBackground );
g_oChooser.Resize( m_iChooserLeft, m_iChooserTop, m_iChooserLeft+m_iChooserWidth, m_iChooserTop+m_iChooserHeight );
g_oChooser.Draw();
if ( !m_bNetworkGame && !m_bTeamMode )
{
DrawGradientText( "Choose A Fighter Dammit", titleFont, 10, m_poBackground );
}
if ( m_bNetworkGame )
{
char acMsg[256];
sprintf( acMsg, "Press Enter to chat, Page Up/Page Down to scroll..." );
if ( m_bNetworkGame )
{
m_poReadline = new CReadline( m_bNetworkGame ? m_poBackground : NULL, chatFont,
acMsg, strlen(acMsg), 256, 15, 465, 610, C_LIGHTCYAN, C_BLACK, 255 );
m_poTextArea = new CTextArea( m_poBackground, chatFont, 15, 313, 610, 32*4 );
}
}
}
CPlayerSelectView::~CPlayerSelectView()
{
delete m_apoTeamDisplays[0]; m_apoTeamDisplays[0] = NULL;
delete m_apoTeamDisplays[1]; m_apoTeamDisplays[1] = NULL;
TViewElements::iterator it;
for ( it=m_apoElements.begin(); it!= m_apoElements.end(); ++it )
{
delete *it;
}
m_apoElements.clear();
delete m_poTextArea;
delete m_poReadline;
SDL_FreeSurface( m_poBackground );
}
void CPlayerSelectView::Advance( int a_iNumFrames )
{
m_iTime += a_iNumFrames;
for ( int i=0; i<a_iNumFrames; ++i )
{
g_oBackend.AdvancePerl();
TViewElements::iterator it;
}
TViewElements::iterator it;
TViewElements::iterator itNext;
for ( it = m_apoElements.begin(); it != m_apoElements.end(); )
{
itNext = it;
++itNext;
(*it)->Advance( a_iNumFrames );
it = itNext;
}
}
void CPlayerSelectView::Draw()
{
TViewElements::iterator it;
g_oBackend.ReadFromPerl();
m_bOver = g_oBackend.m_iGameOver;
g_oChooser.DrawRectangles(m_iTime);
SDL_BlitSurface( m_poBackground, NULL, gamescreen, NULL );
for ( it = m_apoElements.begin(); it != m_apoElements.end(); ++it )
{
(*it)->Draw();
}
for ( int i=0; i<g_oState.m_iNumPlayers; ++i )
{
if ( m_bTeamMode && 1==i && !m_bTeamMultiselect ) continue;
- const PlayerInfo& roPlayerInfo = g_oPlayerSelect.GetPlayerInfo( i );
+ const SPlayerInfo& roPlayerInfo = g_oPlayerSelect.GetPlayerInfo( i );
int iPlayerNameWidth = g_oPlayerSelect.GetFighterNameWidth( i );
if ( g_oBackend.m_aoPlayers[i].m_iFrame )
{
roPlayerInfo.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 + m_iFighterYOffset,
g_oBackend.m_aoPlayers[i].m_iFrame < 0 );
}
int x = ( m_iChooserLeft - iPlayerNameWidth ) / 2;
if ( x<10 ) x = 10;
if ( i ) x = gamescreen->w - x - iPlayerNameWidth;
sge_BF_textout( gamescreen, fastFont, g_oPlayerSelect.GetFighterName(i),
x, gamescreen->h - 40 + m_iFighterNameYOffset );
}
SDL_Flip( gamescreen );
}
CReadline* CPlayerSelectView::GetReadline()
{
return m_poReadline;
}
CTextArea* CPlayerSelectView::GetTextArea()
{
return m_poTextArea;
}
void CPlayerSelectView::AddViewElement( IViewElement* a_poElement, int a_iPriority )
{
m_apoElements.push_back( a_poElement );
}
void CPlayerSelectView::RemoveViewElement( IViewElement* a_poElement )
{
m_apoElements.remove( a_poElement );
}
void CPlayerSelectView::AddFighterToTeam( int a_iPlayer, FighterEnum a_enFighter )
{
new CFlyingPortraitViewElement( this, m_apoTeamDisplays[a_iPlayer], a_enFighter,
g_oChooser.GetFighterRect( a_enFighter ),
m_apoTeamDisplays[a_iPlayer]->GetRect() );
m_apoTeamDisplays[a_iPlayer]->AddFighter( a_enFighter );
}
bool CPlayerSelectView::IsOver()
{
return m_bOver;
}
CTeamDisplay* CPlayerSelectView::GetTeamDisplay( int a_iPlayer )
{
return m_apoTeamDisplays[a_iPlayer];
}
diff --git a/src/PlayerSelectView.h b/src/PlayerSelectView.h
index 759badd..4901d44 100644
--- a/src/PlayerSelectView.h
+++ b/src/PlayerSelectView.h
@@ -1,75 +1,82 @@
#ifndef PLAYERSELECTVIEW_H
#define PLAYERSELECTVIEW_H
#include "FighterEnum.h"
#include "State.h"
#include <list>
struct SDL_Surface;
class CTextArea;
class CReadline;
class CPlayerSelectView;
class CTeamDisplay;
+/**
+A visual element that runs independently within CPlayerSelectView
+\ingroup PlayerSelect
+*/
class IViewElement
{
public:
IViewElement( CPlayerSelectView* a_poView, int a_iPriority );
virtual ~IViewElement();
int GetPriority() const;
virtual void Advance( int a_iNumFrames ) = 0;
virtual void Draw() = 0;
protected:
CPlayerSelectView* m_poView;
int m_iPriority;
};
-
+/**
+View part model-view-controller architecture of the player selection.
+\ingroup PlayerSelect
+*/
class CPlayerSelectView
{
public:
CPlayerSelectView( bool a_bNetworkGame, bool a_bTeamMode );
~CPlayerSelectView();
void Advance( int a_iNumFrames );
void Draw();
CTextArea* GetTextArea();
CReadline* GetReadline();
void AddViewElement( IViewElement* a_poElement, int a_iPriority );
void RemoveViewElement( IViewElement* a_poElement );
void AddFighterToTeam( int a_iPlayer, FighterEnum a_enFighter );
bool IsOver();
CTeamDisplay* GetTeamDisplay( int a_iPlayer );
protected:
bool m_bTeamMode;
bool m_bTeamMultiselect;
bool m_bNetworkGame;
SDL_Surface* m_poBackground;
CTextArea* m_poTextArea;
CReadline* m_poReadline;
bool m_bOver;
int m_iChooserTop;
int m_iChooserLeft;
int m_iChooserHeight;
int m_iChooserWidth;
int m_iFighterYOffset;
int m_iFighterNameYOffset;
int m_iTime;
typedef std::list<IViewElement*> TViewElements;
TViewElements m_apoElements;
CTeamDisplay* m_apoTeamDisplays[MAXPLAYERS];
};
#endif // PLAYERSELECTVIEW_H
diff --git a/src/RlePack.cpp b/src/RlePack.cpp
index ffe2692..4aff488 100644
--- a/src/RlePack.cpp
+++ b/src/RlePack.cpp
@@ -1,665 +1,679 @@
/***************************************************************************
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 */
+
+/**
+\ingroup Media
+\brief CRleSprite is a runlength encoded sprite that can be blitted flipped.
+
+CRleSprite originally comes from Allegro. I use it in OpenMortal because
+it is relatively fast to blit and is economical on the memory too.
+*/
+struct SRleSprite
{
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
+
+/**
+\ingroup Media
+\brief Internal data of CRlePack
+*/
+struct CRlePack_P
{
SDL_Color m_aoPalette[256];
SDL_Color m_aoTintedPalette[256];
TintEnum m_enTint;
int m_iCount;
int m_iArraysize;
- RLE_SPRITE** m_pSprites;
+ SRleSprite** 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 );
+ void draw_rle_sprite8( SRleSprite* a_poSprite, int a_dx, int a_dy );
+ void draw_rle_sprite_v_flip8( SRleSprite* a_poSprite, int a_dx, int a_dy );
+ void draw_rle_sprite16( SRleSprite* a_poSprite, int a_dx, int a_dy );
+ void draw_rle_sprite_v_flip16( SRleSprite* a_poSprite, int a_dx, int a_dy );
+ void draw_rle_sprite24( SRleSprite* a_poSprite, int a_dx, int a_dy );
+ void draw_rle_sprite_v_flip24( SRleSprite* a_poSprite, int a_dx, int a_dy );
+ void draw_rle_sprite32( SRleSprite* a_poSprite, int a_dx, int a_dy );
+ void draw_rle_sprite_v_flip32( SRleSprite* a_poSprite, int a_dx, int a_dy );
};
-RlePack::RlePack( const char* a_pcFilename, int a_iNumColors )
+CRlePack::CRlePack( const char* a_pcFilename, int a_iNumColors )
{
- p = new RlePack_P;
+ p = new CRlePack_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()
+CRlePack::~CRlePack()
{
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()
+void CRlePack::Clear()
{
if ( p && p->m_pSprites )
{
delete[] p->m_pSprites;
p->m_pSprites = NULL;
}
}
-int RlePack::LoadFile( const char* a_pcFilename, int a_iNumColors )
+int CRlePack::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;
+ p->m_iColorCount = gamescreen->format->BitsPerPixel == 8 ? a_iNumColors : 256;
if ( iFileSize != iRead )
{
- debug( "Warning RlePack(): iFileSize=%d, iRead=%d\n", iFileSize, iRead );
+ debug( "Warning CRlePack(): 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 ];
+ p->m_pSprites = new SRleSprite*[ 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;
+ SRleSprite 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
+// #
+// # Here's the old implementation or read, left here for reference only.
+// #
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 ];
+ p->sprites = new SRleSprite*[ 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
+ else if (!strcmp( s, "RLE " )) // Found an SRleSprite
{
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 );
+ SRleSprite* sprite = (SRleSprite*) malloc( sizeof(SRleSprite) + 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()
+int CRlePack::Count()
{
return p->m_iCount;
}
-/** Worker method of RlePack::OffsetSprites() .*/
+/** Worker method of CRlePack::OffsetSprites() .*/
-void OffsetRLESprite( RLE_SPRITE* spr, int offset ) // Static method
+void OffsetRLESprite( SRleSprite* 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.
+CRlePack 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
+you might load an CRlePack with 16 colors, and another with 64 colors. You
+can offset the second CRlePack 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 )
+void CRlePack::OffsetSprites( int a_iOffset )
{
if ( (a_iOffset<=0) || (a_iOffset>255) || (8!=gamescreen->format->BitsPerPixel) )
return;
p->m_iColorOffset = a_iOffset;
int i;
- // Offset every RLE_SPRITE
+ // Offset every SRleSprite
for ( i=0; i<p->m_iCount; ++i )
{
OffsetRLESprite( p->m_pSprites[i], a_iOffset );
}
}
-void RlePack::SetTint( TintEnum a_enTint )
+void CRlePack::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.
+/** Loads the palette of the CRlePack 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()
+void CRlePack::ApplyPalette()
{
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 )
+int CRlePack::GetWidth( int a_iIndex )
{
if ( (a_iIndex<0) || (a_iIndex>=p->m_iCount) )
return -1;
- RLE_SPRITE* poSprite = p->m_pSprites[a_iIndex];
+ SRleSprite* 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 )
+int CRlePack::GetHeight( int a_iIndex )
{
if ( (a_iIndex<0) || (a_iIndex>=p->m_iCount) )
return -1;
- RLE_SPRITE* poSprite = p->m_pSprites[a_iIndex];
+ SRleSprite* poSprite = p->m_pSprites[a_iIndex];
if (!poSprite)
return -1;
return poSprite->h;
}
-#define METHODNAME RlePack_P::draw_rle_sprite8
-#define METHODNAME_FLIP RlePack_P::draw_rle_sprite_v_flip8
+#define METHODNAME CRlePack_P::draw_rle_sprite8
+#define METHODNAME_FLIP CRlePack_P::draw_rle_sprite_v_flip8
#define PIXEL_PTR unsigned char*
#define PUT_PIXEL(p,c) (*((unsigned char *)(p)) = (c))
#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 METHODNAME CRlePack_P::draw_rle_sprite16
+#define METHODNAME_FLIP CRlePack_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 METHODNAME CRlePack_P::draw_rle_sprite32
+#define METHODNAME_FLIP CRlePack_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 RlePack::Draw( int a_iIndex, int a_iX, int a_iY, bool a_bFlipped )
+void CRlePack::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];
+ SRleSprite* poSprite = p->m_pSprites[a_iIndex];
if (!poSprite)
return;
CSurfaceLocker oLock;
if ( a_bFlipped )
{
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
{
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 )
+SDL_Surface* CRlePack::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];
+ SRleSprite* 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 fb17d4d..6b8383c 100644
--- a/src/RlePack.h
+++ b/src/RlePack.h
@@ -1,60 +1,61 @@
/***************************************************************************
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 CRlePack_P;
struct SDL_Surface;
/**
-\class RlePack
-\brief RlePack is responsible for loading and drawing "sprites" from a .DAT file.
+\class CRlePack
+\brief CRlePack is responsible for loading and drawing "sprites" from a .DAT file.
+\ingroup Media
-RlePack loads the sprites from a .DAT file in its constructor. If there is
+CRlePack 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
+CRlePack 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
+class CRlePack
{
public:
- RlePack( const char* a_pcFilename, int a_iNumColors );
- ~RlePack();
+ CRlePack( const char* a_pcFilename, int a_iNumColors );
+ ~CRlePack();
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;
+ CRlePack_P* p;
};
#endif
diff --git a/src/State.cpp b/src/State.cpp
index 6f3498b..1621aa7 100644
--- a/src/State.cpp
+++ b/src/State.cpp
@@ -1,371 +1,371 @@
/***************************************************************************
State.cpp - description
-------------------
begin : Mon Aug 12 2003
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
//[segabor] using Xcode to build OpenMortal makes config.h unneccessary
#ifndef MACOSX
#include "../config.h"
#endif
#include "gfx.h"
#include "common.h"
#include "State.h"
#include "SDL_keysym.h"
#include "SDL_mixer.h"
#include <string>
#include <fstream>
#ifdef MACOSX
//[segabor]
#include <CoreFoundation/CoreFoundation.h>
#endif
//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";
#elif defined(MACOSX)
//[segabor] get os-ified path
return std::string(getenv("HOME")) + "/Library/Preferences/OpenMortal.cfg";
#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
#elif defined(MACOSX)
//[segabor]
#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[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_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<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 );
#elif defined(MACOSX)
//[segabor] OS X style locale handling
CFLocaleRef userLocaleRef = CFLocaleCopyCurrent();
char cbuff[255];
CFStringGetCString(CFLocaleGetIdentifier(userLocaleRef),
cbuff,
255,
kCFStringEncodingASCII);
m_acLanguage[0] = cbuff[0];
m_acLanguage[1] = cbuff[1];
m_acLanguage[2] = 0;
debug("Language code is %s\n", m_acLanguage);
#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;
}
}
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("FULLSCREEN", FALSE); if (poSv) m_bFullscreen = SvIV( poSv ) != 0;
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("SERVER", FALSE); if (poSv) m_bServer = SvIV( poSv ) != 0;
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<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<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 7564675..26b837b 100644
--- a/src/State.h
+++ b/src/State.h
@@ -1,74 +1,83 @@
/***************************************************************************
State.h - description
-------------------
begin : Mon Aug 12 2003
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#ifndef STATE_H
#define STATE_H
#define MAXPLAYERS 4
-
+
+/**
+\ingroup GameLogic
+SState aggregates all the state variables of OpenMortal that do not belong
+to the backend. This includes transient variables such as the current
+game mode (e.g. SStade::IN_DEMO) and configuration variables (such as the
+keyboard layout).
+
+The SState variables are manipulated by the CMenu.
+*/
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[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/TextArea.cpp b/src/TextArea.cpp
index 3b15321..dab81ab 100644
--- a/src/TextArea.cpp
+++ b/src/TextArea.cpp
@@ -1,170 +1,176 @@
/***************************************************************************
TextArea.cpp - description
-------------------
begin : Wed Jan 28 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
#include "TextArea.h"
#include "sge_surface.h"
#include "sge_tt_text.h"
#include "sge_primitives.h"
#include "gfx.h"
#include "common.h"
#include <ctype.h>
CTextArea::CTextArea( SDL_Surface* a_poScreen, _sge_TTFont* a_poFont, int a_x, int a_y, int a_w, int a_h )
{
m_poScreen = a_poScreen;
m_poFont = a_poFont;
m_iScrollOffset = 0;
m_oClipRect.x = x = a_x;
m_oClipRect.y = y = a_y;
m_oClipRect.w = w = a_w;
m_oClipRect.h = h = a_h;
m_poBackground = sge_copy_surface( a_poScreen, x, y, w, h );
}
CTextArea::~CTextArea()
{
SDL_FreeSurface( m_poBackground );
m_poBackground = NULL;
}
void CTextArea::Clear()
{
m_aiRowColors.clear();
m_asRowTexts.clear();
m_iScrollOffset = 0;
}
void CTextArea::TintBackground( int a_iColor, int a_iAlpha )
{
if ( NULL == m_poBackground )
{
return;
}
sge_FilledRectAlpha( m_poBackground, 0, 0, m_poBackground->w, m_poBackground->h, a_iColor, a_iAlpha );
}
void CTextArea::AddString( const char* a_poText, int a_iColor )
{
while ( a_poText && *a_poText )
{
// 1. FORMAT THE TEXT SO IT FITS US NICELY
SDL_Rect oRect;
int i, j;
for ( i=0; a_poText[i]; ++i )
{
oRect = sge_TTF_TextSize( m_poFont, a_poText, i );
if ( oRect.w > w )
{
if ( i>0 )--i;
break;
}
}
// i now points to the character AFTER the last good character.
//if ( a_poText[i] && i>1 ) --i;
// Trace back to the first space BEFORE i
if ( a_poText[i] )
{
for ( j = i; j>0; --j )
{
if ( isspace( a_poText[j] ) )
{
break;
}
}
if ( j>0 )
{
i = j;
}
}
// 2. ADD IT.
m_asRowTexts.push_front( std::string(a_poText,i) );
m_aiRowColors.push_front( a_iColor );
if ( m_iScrollOffset > 0 )
{
++m_iScrollOffset;
}
a_poText += i;
debug( "Added '%s', number of texts is %d\n", m_asRowTexts.front().c_str(), m_asRowTexts.size() );
}
}
void CTextArea::ScrollUp()
{
if ( m_iScrollOffset < (int) m_asRowTexts.size() - h / sge_TTF_FontHeight(m_poFont) )
{
++m_iScrollOffset;
Redraw();
}
}
void CTextArea::ScrollDown()
{
if ( m_iScrollOffset > 0 )
{
--m_iScrollOffset;
Redraw();
}
}
void CTextArea::Redraw()
{
TStringList::const_iterator itString = m_asRowTexts.begin();
TIntList::const_iterator itColors = m_aiRowColors.begin();
for ( int i=0; i<m_iScrollOffset; ++i )
{
++itString;
++itColors;
}
SDL_Rect oOldClipRect;
SDL_GetClipRect( m_poScreen, &oOldClipRect );
SDL_SetClipRect( m_poScreen, &m_oClipRect );
sge_TTF_AAOn();
sge_Blit( m_poBackground, m_poScreen, 0, 0, x, y, w, h );
int iRows = h / sge_TTF_FontHeight( m_poFont );
int yPos = y + (iRows-1) * sge_TTF_FontHeight( m_poFont );
for ( ; yPos >= y; yPos -= sge_TTF_FontHeight( m_poFont ) )
{
if ( itColors == m_aiRowColors.end() )
{
break;
}
// Print the current text
sge_tt_textout( m_poScreen, m_poFont, itString->c_str(), x, yPos + sge_TTF_FontAscent(m_poFont), *itColors, C_BLACK, 255 );
++itString;
++itColors;
}
sge_UpdateRect( m_poScreen, x, y, w, h );
SDL_SetClipRect( m_poScreen, &oOldClipRect );
sge_TTF_AAOff();
}
+/*
+int CTextArea::RenderLine( SDL_Surface* a_poScreen, const char* a_poText, int a_iLength, int a_iColor )
+{
+}
+*/
+
diff --git a/src/TextArea.h b/src/TextArea.h
index e6c64d5..3e41cc5 100644
--- a/src/TextArea.h
+++ b/src/TextArea.h
@@ -1,51 +1,56 @@
/***************************************************************************
TextArea.h - description
-------------------
begin : Wed Jan 28 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
#ifndef TEXTAREA_H
#define TEXTAREA_H
#include "SDL_video.h"
struct SDL_Surface;
struct _sge_TTFont;
#include <string>
#include <list>
typedef std::list<std::string> TStringList;
typedef std::list<int> TIntList;
+
+/**
+\brief CTextArea displays scrolling text.
+\ingroup Media
+*/
class CTextArea
{
public:
CTextArea( SDL_Surface* a_poScreen, _sge_TTFont* a_poFont, int a_x, int a_y, int a_w, int a_h );
~CTextArea();
void TintBackground( int a_iColor, int a_iAlpha );
void AddString( const char* a_poText, int a_iColor );
void Redraw();
void Clear();
void ScrollUp();
void ScrollDown();
protected:
SDL_Surface* m_poScreen;
SDL_Surface* m_poBackground;
_sge_TTFont* m_poFont;
SDL_Rect m_oClipRect;
int x, y, w, h;
int m_iScrollOffset;
TStringList m_asRowTexts;
TIntList m_aiRowColors;
};
#endif // TEXTAREA_H
diff --git a/src/common.h b/src/common.h
index bf40295..2666da9 100644
--- a/src/common.h
+++ b/src/common.h
@@ -1,65 +1,78 @@
/***************************************************************************
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
void debug( const char* format, ... );
#ifndef ABS
#define ABS(A) ( (A>=0) ? (A) : -(A) )
#endif
#ifndef MAX
#define MAX(A,B) ( (A) > (B) ? (A) : (B) )
#endif
#ifndef MIN
#define MIN(A,B) ( (A) < (B) ? (A) : (B) )
#endif
+// -----------------------------------------------------------------------
+// Main program methods
+// -----------------------------------------------------------------------
void DoMenu();
void GameOver( int a_iPlayerWon );
void DoDemos();
+int DoGame( char* replay, bool isReplay, bool bDebug );
void DoOnlineChat();
+
+// -----------------------------------------------------------------------
+// Other subroutines
+// -----------------------------------------------------------------------
+
bool Connect( const char* a_pcHostname );
const char* Translate( const char* a_pcText );
const char* TranslateUTF8( const char* a_pcText );
+// -----------------------------------------------------------------------
+// Global variables
+// -----------------------------------------------------------------------
+
+struct SDL_Surface;
extern SDL_Surface* gamescreen;
extern Uint32 C_BLACK;
extern Uint32 C_BLUE;
extern Uint32 C_GREEN;
extern Uint32 C_CYAN;
extern Uint32 C_RED;
extern Uint32 C_MAGENTA;
extern Uint32 C_ORANGE;
extern Uint32 C_LIGHTGRAY;
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
diff --git a/src/gfx.cpp b/src/gfx.cpp
index 1e7dede..ee54575 100644
--- a/src/gfx.cpp
+++ b/src/gfx.cpp
@@ -1,468 +1,458 @@
/***************************************************************************
gfx.cpp - description
-------------------
begin : Tue Apr 10 2001
copyright : (C) 2001 by UPi
email : upi@apocalypse.rulez.org
***************************************************************************/
#include <string.h>
-//[segabor]: On OS X we don't need malloc.h
-#ifndef MACOSX
-#include <malloc.h>
-#endif
-
#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) );
+ unicode_text = new Uint16[unicode_len+1]; // (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);
+ delete[] unicode_text; //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, bool a_bTransparent )
{
char acFilepath[FILENAME_MAX+1];
strcpy( acFilepath, DATADIR );
strcat( acFilepath, "/gfx/" );
strcat( acFilepath, a_pcFilename );
SDL_Surface* poBackground = IMG_Load( acFilepath );
if (!poBackground)
{
debug( "Can't load file: %s\n", acFilepath );
return NULL;
}
SDL_Palette* pal = poBackground->format->palette;
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 );
// 2. TRY TO LOAD AN IMAGE MASK
// This means trying to load a .png file which acts as a mask for the
// original [jpg] image.
// If the original file is <Basename>.jpg, the mask is <Basename>.mask.png
int iLength = strlen( acFilepath );
char acMaskFilename[FILENAME_MAX+1];
strncpy( acMaskFilename, acFilepath, iLength-4 );
acMaskFilename[iLength-4] = 0;
strcat( acMaskFilename, ".mask.png" );
SDL_Surface* poMask = IMG_Load( acMaskFilename );
if ( !poMask )
{
// No mask.
return poRetval;
}
if ( poMask->w < poRetval->w
|| poMask->h < poRetval->h )
{
debug( "Error loading mask for %s: mask is too small.\n", acFilepath );
SDL_FreeSurface( poMask );
return poRetval;
}
debug( "Loading mask for %s.\n", acFilepath );
Uint32 iTransparent = SDL_MapRGB( gamescreen->format, 255, 217, 0 ); // an unlikely color in openmortal..
Uint32 iMask = sge_GetPixel( poMask, 0, 0 );
Uint32 iPixel;
for ( int y = 0; y < poRetval->h; ++y ) {
for ( int x=0; x< poRetval->w; ++x ) {
iPixel = sge_GetPixel( poMask, x, y );
// debug( "%d ", iPixel );
if ( iPixel == iMask ) {
sge_PutPixel( poRetval, x, y, iTransparent );
}
}
// debug( "\n" );
}
SDL_FreeSurface( poMask );
SDL_SetColorKey( poRetval, SDL_SRCCOLORKEY, iTransparent );
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 568a76b..e508baf 100644
--- a/src/gfx.h
+++ b/src/gfx.h
@@ -1,86 +1,94 @@
/***************************************************************************
gfx.h - description
-------------------
begin : Tue Apr 10 2001
copyright : (C) 2001 by UPi
email : upi@apocalypse.rulez.org
***************************************************************************/
#ifndef _GFX_H
#define _GFX_H
+/**
+\defgroup Media Sound and graphics
+Classes that deal with sound, graphics, input, etc.
+*/
+
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, bool a_bTransparent = false );
bool SetVideoMode( bool a_bLarge, bool a_bFullscreen, int a_iAdditionalFlags=0 );
extern SDL_Surface* gamescreen;
+/**
+\ingroup Media
+*/
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 0ca3adc..99ca160 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,643 +1,762 @@
/***************************************************************************
main.cpp - description
-------------------
begin : Wed Aug 22 10:18:47 CEST 2001
copyright : (C) 2001 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
+/**
+\mainpage
+
+The document you read now describes the design of OpenMortal. This page
+serves as a starting point. The documentation is generated with doxygen
+(http://doxygen.org).
+
+OpenMortal consists of two main parts: the \b frontend and the \b backend.
+
+\li The frontend is a C++ program, responsible for multimedia
+(sounds, music, graphics) and general interaction with the players
+(menus, keyboard input), and the demo and intro screens.
+
+\li The backend is written in Perl, and is incorporated into the C++
+program with Perl embedding.
+
+
+
+\section s1 Modules
+
+The classes of OpenMortal are organized into the following groups (see Modules above).
+
+\li \ref Media - OpenMortal uses \b SDL (http://libsdl.org) for hardware
+access such as screen drawing, music, sound effect and keyboard input. For
+information and documentation of SDL, SDL_image, SDL_ttf and SDL_mixer,
+please look at the SDL homepage.
+
+\li \ref PlayerSelect
+\li \ref Network
+\li \ref Demo
+\li \ref GameLogic
+
+
+
+\section s2 Main Functions
+These global functions implement important parts of the program. They serve
+as entry points into functional parts.
+
+\li DoMenu() - Displays and runs the menu over the current screen
+\li GameOver() - Displays the "Final Judgeent" screen
+\li DoDemos() - Runs the demos in an endless loop until a game is started or the program ends.
+\li DoGame() - Runs the game.
+\li DoOnlineChat() - Connects to and runs the MortalNet.
+\li CPlayerSelect::DoPlayerSelect() - Runs the player selection screen.
+
+
+\section s3 Definitions
+
+Here are the definitions of terms used in this documentation.
+
+<dl>
+
+<dt>\b Player <dd>
+ Player refers to one of the two persons playing the game. A player chooses a fighter. The two players are referred to as "Player 1" and "Player 2", even though the C++ and perl code count arrays from 0.
+
+
+<dt>\b Fighter <dd>
+ Fighter is one of the many available characters. Usually there are only two fighters loaded at any time. Fighters are static: their properties never change. Maybe Fighter should be renamed to Character?
+<dt>\b Game <dd>
+ One game is the part of the program in which the players actually compete. The game consists of a number of rounds. The player selection, and gameover screen are not part of this definition.
+<dt>\b Round <dd>
+ One round starts with both players at full health, and ends either with a timeout or with a ko.
+<dt>\b Doodad <dd>
+ A graphical element on the game screen that is not the background or the characters. E.g. the "3x combo" text or a thrown projectile are doodads.
+<dt>\b Tint <dd>
+ A tint is a methodical change in a palette. There are many ways to tint (e.g. grayscaling, darkening, green chroma, etc). Usually when two players choose the same fighter, the fighter of player 2 is tinted.
+<dt>\b Scene <dd>
+ The description of a frozen moment in the course of a game. The Backend is responsible for calculating each consecutive scene. The number of scenes calculated per second is constant (except for the "hurry up mode", or if the computer is too slow).
+<dt>\b FPS <dd>
+ Frames Per Second. The FPS indicator on the screen during a game indicates the number of scenes drawn, not the number of scenes calculated by the backend.
+</dl>
+
+
+\section s4 C++ Coding conventions
+
+Historically two different coding conventions were mixed in OpenMortal.
+Hopefully most of the old conventions are eliminated by now.
+Here I will describe the new conventions:
+
+\li <B> Class names: </B> CMixedCaps.
+\li <B> Struct names: </B> SMixedCaps.
+\li <B> Typedef names: </B> CSomeTypedef (There's some traces of the old TSomeTypedef
+ left, these should be eliminated.
+\li <B> Enum names: </B> SomeThingEnum.
+\li <B> Enum values: </B> Prefix_ENUM_VALUE
+\li <B> Method names: </B> MixedCaps.
+\li <B> Variable names: </B> &lt;prefix&gt;VariableName.
+\li <B> Instance property names: </B> m_&lt;prefix&gt;VariableName,
+\li <B> Class property names </B>(a.k.a. static class variables): mg_&lt;prefix&gt;VariableName.
+\li <B> Method argument names: </B> a_&lt;prefix&gt;VariableName.
+If a reference or pointer argument is "output-only: a_&lt;prefix&gt;OutVariableName.
+\li <B> Global variable names: </B> g_&lt;prefix&gt;VariableName.
+
+The prefixes used are:
+
+\li <B> Array of something: </B> a&lt;something&gt;
+\li <B> Pointer to something: </B> p&lt;something&gt;
+\li <B> Reference of something: </B> r&lt;something&gt;
+\li <B> Basic types: </B> Integer: i; char: c; double: d; enum: en; object (class or struct): o; std::string: s;
+
+Example:
+\code
+CSomeExampleClass: public CSomeBaseClass
+{
+public:
+ CSomeExampleClass();
+ void SomeMethod( int& a_riOutSomething );
+protected:
+ int m_iSomething;
+ char* m_pcSomethingElse;
+ static enum SomeThingEnum
+ {
+ Ste_VALUE,
+ } mg_enWhatever;
+};
+\endcode
+*/
+
+
#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()
{
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);
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 );
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.jpg", 240 );
DrawTextMSZ( "Version " VERSION " - European Union Editition", 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(), 256 );
+ CRlePack pack( sStaffFilename.c_str(), 256 );
pack.ApplyPalette();
SDL_BlitSurface( background, NULL, gamescreen, &r );
SDL_Flip( gamescreen );
/* 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" };*/
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;
#ifdef MACOSX
//[segabor]
char char_buf[256];
sprintf(char_buf, "%s/characters", DATADIR);
g_oBackend.PerlEvalF( "$CppRetval = GetNumberOfFighterFiles('%s')", char_buf );
#else
g_oBackend.PerlEvalF( "$CppRetval = GetNumberOfFighterFiles('%s')", DATADIR "/characters" );
#endif
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 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()
{
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;
} oVideoModeChanger( g_oState.m_iNumPlayers > 2 );
#define IS_GAME_MODE (g_oState.m_enGameMode != SState::IN_DEMO \
&& g_oState.m_enGameMode != SState::IN_CHAT \
&& !g_oState.m_bQuitFlag)
Audio->PlaySample( "GAME_NEW" );
Audio->PlayMusic( "GameMusic" );
bool bNetworkGame = SState::IN_NETWORK == g_oState.m_enGameMode;
if ( bNetworkGame )
{
g_oState.m_enTeamMode = SState::Team_ONE_VS_ONE;
}
while ( IS_GAME_MODE )
{
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 );
+ CFighterStatsDemo 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 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]\n", argv[0] );
return 0;
}
}
if (init()<0)
{
return -1;
}
InitJoystick();
g_oState.SetLanguage( g_oState.m_acLanguage );
- new MszAudio;
+ new COpenMortalAudio;
// 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, UPI );
g_oPlayerSelect.SetPlayer( 1, ULMAR );
/*
{
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 0eb23c6..bdded17 100644
--- a/src/menu.cpp
+++ b/src/menu.cpp
@@ -1,1301 +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, gamescreen->w/2, iY, AlignHCenter|UseShadow, C_LIGHTCYAN, gamescreen );
iY += iYIncrement + 10;
}
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, gamescreen->w, 50 );
DrawTextMSZ( g_oBackend.GetPerlString("keysym"), inkFont, w+30, iY, UseShadow, C_WHITE, gamescreen );
sge_UpdateRect( gamescreen, w+10, iY, gamescreen->w, 50 );
iY += iYIncrement;
}
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 );
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, 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" )
+CNetworkMenu::CNetworkMenu(): CMenu( "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 );
+ CMenuItem* 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 )
+void CMenu::EnterName( const char* a_pcTitle, std::string& a_rsTarget, CTextMenuItem* 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), 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 )
+void CNetworkMenu::ItemActivated( int a_iItemCode, CMenuItem* a_poMenuItem )
{
switch ( a_iItemCode )
{
case MENU_SERVER:
{
- EnumMenuItem* poItem = (EnumMenuItem*) a_poMenuItem;
+ CEnumMenuItem* poItem = (CEnumMenuItem*) 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 )
+void CNetworkMenu::ItemChanged( int a_iItemCode, int a_iValue, CMenuItem* a_poMenuItem )
{
switch ( a_iItemCode )
{
case MENU_SERVER:
- m_bServer = a_iValue;
+ m_bServer = a_iValue != 0;
m_poServerMenuItem->SetEnabled(!m_bServer);
break;
}
}
/***************************************************************************
MENUITEM DEFINITION
***************************************************************************/
-MenuItem::MenuItem( Menu* a_poMenu, const char* a_pcUtf8Text, int a_iCode )
+CMenuItem::CMenuItem( CMenu* 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()
+CMenuItem::~CMenuItem()
{
}
-void MenuItem::Draw()
+void CMenuItem::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()
+void CMenuItem::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()
+void CMenuItem::Activate()
{
if ( m_poMenu )
{
m_poMenu->ItemActivated( m_iCode, this );
}
}
-void MenuItem::SetText( const char* a_pcUtf8Text, bool a_bCenter )
+void CMenuItem::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 )
+void CMenuItem::SetPosition( const SDL_Rect& a_roPosition )
{
m_oPosition = a_roPosition;
}
-void MenuItem::SetActive( bool a_bActive )
+void CMenuItem::SetActive( bool a_bActive )
{
if ( m_bActive == a_bActive )
{
return;
}
m_bActive = a_bActive;
Draw();
}
-void MenuItem::SetEnabled( bool a_bEnabled )
+void CMenuItem::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 )
+CEnumMenuItem::CEnumMenuItem( CMenu* a_poMenu, int a_iInitialValue, const char* a_pcUtf8Text, int a_iCode )
+: CMenuItem( a_poMenu, a_pcUtf8Text, a_iCode )
{
m_sUtf8Title = a_pcUtf8Text;
m_iMax = -1;
m_iValue = a_iInitialValue;
}
-EnumMenuItem::~EnumMenuItem()
+CEnumMenuItem::~CEnumMenuItem()
{
}
-int EnumMenuItem::GetCurrentValue()
+int CEnumMenuItem::GetCurrentValue()
{
return m_iValue <= m_iMax ? m_piValues[m_iValue] : 0;
}
-const char* EnumMenuItem::GetCurrentText()
+const char* CEnumMenuItem::GetCurrentText()
{
return m_iValue <= m_iMax ? m_ppcNames[m_iValue] : "";
}
-void EnumMenuItem::Draw()
+void CEnumMenuItem::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();
+ CMenuItem::Draw();
}
-void EnumMenuItem::Increment()
+void CEnumMenuItem::Increment()
{
if ( m_iValue < m_iMax )
{
++m_iValue;
Draw();
m_poMenu->ItemChanged( m_iCode, m_piValues[m_iValue], this );
Audio->PlaySample( "MENU_ITEM_VALUE_CHANGES" );
}
}
-void EnumMenuItem::Decrement()
+void CEnumMenuItem::Decrement()
{
if ( m_iValue > 0 )
{
--m_iValue;
Draw();
m_poMenu->ItemChanged( m_iCode, m_piValues[m_iValue], this );
Audio->PlaySample( "MENU_ITEM_VALUE_CHANGES" );
}
}
-void EnumMenuItem::SetEnumValues( const char ** a_ppcNames, const int * a_piValues )
+void CEnumMenuItem::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 )
+void CEnumMenuItem::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 );
+ debug( "CEnumMenuItem::SetMaxValue: value %d not found\n", a_iMaxValue );
}
/***************************************************************************
- TextMenuItem DEFINITION
+ CTextMenuItem DEFINITION
***************************************************************************/
-TextMenuItem::TextMenuItem( Menu* a_poMenu, const char* a_pcInitialValue, const char* a_pcUtf8Title, int a_iCode )
- : MenuItem( a_poMenu, a_pcUtf8Title, a_iCode )
+CTextMenuItem::CTextMenuItem( CMenu* a_poMenu, const char* a_pcInitialValue, const char* a_pcUtf8Title, int a_iCode )
+ : CMenuItem( a_poMenu, a_pcUtf8Title, a_iCode )
{
m_sTitle = a_pcUtf8Title;
m_sValue = a_pcInitialValue;
}
-TextMenuItem::~TextMenuItem()
+CTextMenuItem::~CTextMenuItem()
{
}
-void TextMenuItem::Draw()
+void CTextMenuItem::Draw()
{
m_sUtf8Text = Translate( m_sTitle.c_str() );
m_sUtf8Text += m_sValue;
- MenuItem::Draw();
+ CMenuItem::Draw();
}
-void TextMenuItem::SetValue( const char* a_pcValue )
+void CTextMenuItem::SetValue( const char* a_pcValue )
{
m_sValue = a_pcValue;
Draw();
}
/***************************************************************************
MENU DEFINITION
***************************************************************************/
-Menu::Menu( const char* a_pcTitle )
+CMenu::CMenu( const char* a_pcTitle )
: m_sTitle( a_pcTitle )
{
m_iCurrentItem = 0;
m_iReturnCode = -1;
m_bDone = false;
}
-Menu::~Menu()
+CMenu::~CMenu()
{
- ItemIterator it;
+ CItemIterator 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 )
+CMenuItem* CMenu::AddMenuItem( const char* a_pcUtf8Text, SDLKey a_tShortcut, int a_iCode )
{
- MenuItem* poItem = new MenuItem( this, a_pcUtf8Text, a_iCode );
+ CMenuItem* poItem = new CMenuItem( this, a_pcUtf8Text, a_iCode );
return AddMenuItem( poItem );
}
-EnumMenuItem* Menu::AddEnumMenuItem( const char* a_pcUtf8Text, int a_iInitialValue,
+CEnumMenuItem* CMenu::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 );
+ CEnumMenuItem* poItem = new CEnumMenuItem( 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 )
+CTextMenuItem* CMenu::AddTextMenuItem( const char* a_pcTitle, const char* a_pcValue, int a_iCode )
{
- TextMenuItem* poItem = new TextMenuItem( this, a_pcValue, a_pcTitle, a_iCode );
+ CTextMenuItem* poItem = new CTextMenuItem( this, a_pcValue, a_pcTitle, a_iCode );
AddMenuItem( poItem );
return poItem;
}
-MenuItem* Menu::AddMenuItem( MenuItem* a_poItem )
+CMenuItem* CMenu::AddMenuItem( CMenuItem* 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 )
+void CMenu::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 );
+ CMenuItem* 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
+CMenuItem* CMenu::GetMenuItem( int a_iCode ) const
{
- ItemList::const_iterator it;
+ CItemList::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 )
+void CMenu::InvokeSubmenu( CMenu* a_poMenu )
{
Audio->PlaySample( "MENU_ITEM_INVOKED" );
Clear();
m_iReturnCode = a_poMenu->Run();
if ( g_oState.m_bQuitFlag )
{
m_iReturnCode = 100;
m_bDone = true;
}
if ( m_iReturnCode < 0 )
{
Audio->PlaySample( "MENU_SUBMENU_END" );
Draw();
}
else
{
m_iReturnCode --;
m_bDone = true;
}
}
-void Menu::ItemActivated( int a_iItemCode, MenuItem* a_poMenuItem )
+void CMenu::ItemActivated( int a_iItemCode, CMenuItem* a_poMenuItem )
{
- debug( "Menu::ItemActivated( %d )\n", a_iItemCode );
+ debug( "CMenu::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();
+ CMenu* poMenu = new CNetworkMenu();
InvokeSubmenu( poMenu );
delete poMenu;
break;
}
case MENU_MULTI_PLAYER:
{
- Menu* poMenu = new Menu( "Multi Player" );
+ CMenu* poMenu = new CMenu( "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( "MENU_ITEM_INVOKED" );
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" );
+ CMenu* poMenu = new CMenu( "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" );
+ CMenu* poMenu = new CMenu( "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 )
+void CMenu::ItemChanged( int a_iItemCode, int a_iValue, CMenuItem* a_poMenuItem )
{
- debug( "Menu::ItemChanged( %d, %d )\n", a_iItemCode, a_iValue );
+ debug( "CMenu::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;
+ CMenuItem* 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()
+int CMenu::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];
+ CMenuItem* poItem = m_oItems[m_iCurrentItem];
poItem->Decrement();
break;
}
case SDLK_RIGHT:
{
- MenuItem* poItem = m_oItems[m_iCurrentItem];
+ CMenuItem* poItem = m_oItems[m_iCurrentItem];
poItem->Increment();
break;
}
case SDLK_RETURN:
{
- MenuItem* poItem = m_oItems[m_iCurrentItem];
+ CMenuItem* poItem = m_oItems[m_iCurrentItem];
if ( poItem->GetEnabled() )
{
poItem->Activate();
}
}
default:
break;
} // end of switch
}
Clear();
return m_iReturnCode;
}
-void Menu::Draw()
+void CMenu::Draw()
{
DrawGradientText( m_sTitle.c_str(), titleFont, 20, gamescreen );
- for ( ItemIterator it=m_oItems.begin(); it!=m_oItems.end(); ++it )
+ for ( CItemIterator it=m_oItems.begin(); it!=m_oItems.end(); ++it )
{
(*it)->Draw();
}
SDL_Flip( gamescreen );
}
-void Menu::FocusNext()
+void CMenu::FocusNext()
{
- MenuItem* poItem = NULL;
+ CMenuItem* 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("MENU_ITEM_SELECTION");
m_oItems[m_iCurrentItem]->SetActive(false);
m_oItems[iNextItem]->SetActive(true);
m_iCurrentItem = iNextItem;
}
}
-void Menu::FocusPrev()
+void CMenu::FocusPrev()
{
- MenuItem* poItem = NULL;
+ CMenuItem* 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("MENU_ITEM_SELECTION");
m_oItems[m_iCurrentItem]->SetActive(false);
m_oItems[iPrevItem]->SetActive(true);
m_iCurrentItem = iPrevItem;
}
}
-void Menu::Clear()
+void CMenu::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;
}
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 );
}
}
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 )
+void DoMenu( CMenu& a_roMenu )
{
Audio->PlaySample( "MENU_START" );
MakeMenuBackground();
a_roMenu.Clear();
a_roMenu.Draw();
a_roMenu.Run();
if ( !g_oState.m_bQuitFlag )
{
Audio->PlaySample("MENU_END");
}
if ( NULL != poBackground )
{
SDL_FreeSurface( poBackground );
poBackground = NULL;
}
}
void DoMenu()
{
MakeMenuBackground();
- Menu oMenu( "Main Menu" );
+ CMenu 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 f4c837f..1f7dda1 100644
--- a/src/menu.h
+++ b/src/menu.h
@@ -1,220 +1,259 @@
/***************************************************************************
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;
+class CMenuItem;
+class CEnumMenuItem;
+class CTextMenuItem;
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
-{
+/**
+\brief Base class for menu systems in OpenMortal.
+\ingroup GameLogic
+
+Menus are displayed over the current screen and allow for the editing
+of the game's state, SState. If you think in terms of model, view and
+controller, then SState is the model and CMenu is both the view and
+the controller.
+
+Menus have menu items. The items in turn can edit various state
+variables or invoke submenus. The menu ends then the user exits the
+toplevel menu or invokes a command which unrolls the entire menu
+stack.
+*/
+class CMenu
+{
public:
- Menu( const char* a_pcTitle );
- virtual ~Menu();
+ CMenu( const char* a_pcTitle );
+ virtual ~CMenu();
- 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,
+ virtual CMenuItem* AddMenuItem( const char* a_pcUtf8Text, SDLKey a_tShortcut = SDLK_UNKNOWN, int a_iCode = 0 );
+ virtual CEnumMenuItem* 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 CTextMenuItem* AddTextMenuItem( const char* a_pcTitle, const char* a_pcValue, int a_iCode = 0 );
+ virtual CMenuItem* AddMenuItem( CMenuItem* a_poItem );
virtual void AddOkCancel( int a_iOkCode = 0 );
- virtual MenuItem* GetMenuItem( int a_iCode ) const;
+ virtual CMenuItem* GetMenuItem( int a_iCode ) const;
- virtual void ItemActivated( int a_iItemCode, MenuItem* a_poMenuItem );
- virtual void ItemChanged( int a_iItemCode, int a_iValue, MenuItem* a_poMenuItem );
+ virtual void ItemActivated( int a_iItemCode, CMenuItem* a_poMenuItem );
+ virtual void ItemChanged( int a_iItemCode, int a_iValue, CMenuItem* 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 );
+ virtual void EnterName( const char* a_pcTitle, std::string& a_rsTarget, CTextMenuItem* a_poMenuItem, int a_iMaxlen );
protected:
virtual void FocusNext();
virtual void FocusPrev();
- virtual void InvokeSubmenu( Menu* a_poSubmenu );
+ virtual void InvokeSubmenu( CMenu* a_poSubmenu );
- typedef std::vector<MenuItem*> ItemList;
- typedef ItemList::iterator ItemIterator;
+ typedef std::vector<CMenuItem*> CItemList;
+ typedef CItemList::iterator CItemIterator;
std::string m_sTitle;
- ItemList m_oItems;
+ CItemList m_oItems;
int m_iCurrentItem;
int m_iReturnCode;
bool m_bDone;
};
+/**
+\ingroup GameLogic
-class MenuItem
+Basic menu item. Menu items have a code which they pass to their parent
+menu when they are activated. Menu items can be enabled or disabled.
+*/
+class CMenuItem
{
public:
- MenuItem( Menu* a_poMenu, const char* a_pcUtf8Text, int a_iCode = -1 );
- virtual ~MenuItem();
+ CMenuItem( CMenu* a_poMenu, const char* a_pcUtf8Text, int a_iCode = -1 );
+ virtual ~CMenuItem();
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() const { return m_bEnabled; }
virtual int GetCode() const { return m_iCode; }
protected:
- Menu* m_poMenu;
+ CMenu* 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;
};
+/**
+\ingroup GameLogic
-class EnumMenuItem: public MenuItem
+Enumerated menu items have an integer value, and a set of values and texts
+which they can display for the user. The user can switch between these
+values by incrementing and decrementing the value with the left and right
+arrow keys.
+*/
+
+class CEnumMenuItem: public CMenuItem
{
public:
- EnumMenuItem( Menu* a_poMenu, int a_iInitialValue, const char* a_pcUtf8Text, int a_iCode = -1 );
- virtual ~EnumMenuItem();
+ CEnumMenuItem( CMenu* a_poMenu, int a_iInitialValue, const char* a_pcUtf8Text, int a_iCode = -1 );
+ virtual ~CEnumMenuItem();
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;
};
+/**
+\ingroup GameLogic
+
+Text menu items are like regular menu items, but they have a text value
+which can be set. This value is displayed next to the regular name of
+the menu item.
+*/
-class TextMenuItem: public MenuItem
+class CTextMenuItem: public CMenuItem
{
public:
- TextMenuItem( Menu* a_poMenu, const char* a_pcInitialValue, const char* a_pcUtf8Title, int a_iCode );
- virtual ~TextMenuItem();
+ CTextMenuItem( CMenu* a_poMenu, const char* a_pcInitialValue, const char* a_pcUtf8Title, int a_iCode );
+ virtual ~CTextMenuItem();
virtual void Draw();
virtual void SetValue( const char* a_pcValue );
protected:
std::string m_sTitle;
std::string m_sValue;
};
+/**
+\ingroup GameLogic
+
+The Network displays and modifies the network connection parameters.
+*/
-class CNetworkMenu: public Menu
+class CNetworkMenu: public CMenu
{
public:
CNetworkMenu();
virtual ~CNetworkMenu();
void Connect();
- void ItemActivated( int a_iItemCode, MenuItem* a_poMenuItem );
- void ItemChanged( int a_iItemCode, int a_iValue, MenuItem* a_poMenuItem );
+ void ItemActivated( int a_iItemCode, CMenuItem* a_poMenuItem );
+ void ItemChanged( int a_iItemCode, int a_iValue, CMenuItem* a_poMenuItem );
protected:
bool m_bOK;
bool m_bServer;
std::string m_sHostname;
std::string m_sNick;
- TextMenuItem* m_poServerMenuItem;
- TextMenuItem* m_poNickMenuItem;
+ CTextMenuItem* m_poServerMenuItem;
+ CTextMenuItem* m_poNickMenuItem;
};
void DoMenu();
-void DoMenu( Menu& a_roMenu );
+void DoMenu( CMenu& a_roMenu );
#endif

File Metadata

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

Event Timeline