Page MenuHomePhabricator (Chris)

No OneTemporary

Size
239 KB
Referenced Files
None
Subscribers
None
This document is not UTF8. It was detected as JIS and converted to UTF8 for display.
diff --git a/src/Backend.cpp b/src/Backend.cpp
index 852c450..7febcf0 100644
--- a/src/Backend.cpp
+++ b/src/Backend.cpp
@@ -1,446 +1,474 @@
/***************************************************************************
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 <string>
#include <stdarg.h>
#include "MszPerl.h"
/***************************************************************************
PUBLIC EXPORTED VARIABLES
***************************************************************************/
PerlInterpreter* my_perl;
Backend g_oBackend;
/***************************************************************************
PRIVATE VARIABLES (perl variable space)
***************************************************************************/
SV
*perl_bgx, *perl_bgy,
*perl_p1x, *perl_p1y, *perl_p1f, *perl_p1h,
*perl_p2x, *perl_p2y, *perl_p2f, *perl_p2h,
- *perl_time, *perl_over, *perl_ko;
+ *perl_over, *perl_ko;
SV
*perl_doodad_x, *perl_doodad_y,
*perl_doodad_t, *perl_doodad_f,
*perl_doodad_dir, *perl_doodad_gfxowner,
*perl_doodad_text;
SV
*perl_sound, *perl_Translated;
/***************************************************************************
TRANSLATION SERVICES
***************************************************************************/
const char* Translate( const char* a_pcText )
{
dSP ;
ENTER ;
SAVETMPS ;
PUSHMARK(SP) ;
XPUSHs(sv_2mortal(newSVpv(a_pcText, 0)));
PUTBACK ;
call_pv("Translate", G_DISCARD);
FREETMPS ;
LEAVE ;
if ( NULL == perl_Translated )
{
perl_Translated = get_sv("Translated", TRUE);
}
return SvPV_nolen( perl_Translated );
}
const char* TranslateUTF8( const char* a_pcText )
{
dSP ;
ENTER ;
SAVETMPS ;
PUSHMARK(SP) ;
XPUSHs(sv_2mortal(newSVpv(a_pcText, 0)));
PUTBACK ;
call_pv("Translate", G_DISCARD);
FREETMPS ;
LEAVE ;
if ( NULL == perl_Translated )
{
perl_Translated = get_sv("Translated", TRUE);
}
return SvPVutf8_nolen( perl_Translated );
}
/***************************************************************************
BACKEND CLASS IMPLEMENTATION
***************************************************************************/
#define PERLEVAL(A) eval_pv(A, TRUE);
#define PERLCALL(PROC,A,B) { \
dSP; \
ENTER; \
SAVETMPS; \
PUSHMARK(SP); \
XPUSHs(sv_2mortal(newSViv(A))); \
XPUSHs(sv_2mortal(newSViv(B))); \
PUTBACK ; \
\
call_pv( (PROC), G_DISCARD ); \
\
FREETMPS; \
LEAVE; \
}
Backend::Backend()
{
m_iBgX = m_iBgY = 0;
m_iNumDoodads = m_iNumSounds = 0;
for ( int i=0; i<2; ++i )
{
m_aoPlayers[i].m_iX = m_aoPlayers[i].m_iY = 0;
m_aoPlayers[i].m_iFrame = 0;
m_aoPlayers[i].m_iHitPoints = 0;
}
}
Backend::~Backend()
{
if ( NULL != my_perl )
{
perl_destruct( my_perl );
perl_free( my_perl );
my_perl = NULL;
}
}
bool Backend::Construct()
{
if ( my_perl != NULL )
{
// Already inited
return false;
}
perl_bgx = NULL;
perl_doodad_x = NULL;
std::string sFileName = DATADIR;
sFileName += "/script";
#ifndef _WINDOWS
chdir( sFileName.c_str() );
#endif
char *perl_argv[] = {"", "Backend.pl"};
my_perl = perl_alloc();
if ( my_perl == NULL )
{
return false;
}
perl_construct( my_perl );
if ( perl_parse( my_perl, NULL, 2, perl_argv, (char**)NULL ) )
{
char *error = SvPV_nolen(get_sv("@", FALSE));
fprintf( stderr, "%s", error );
return false;
}
if ( perl_run( my_perl ) )
{
char *error = SvPV_nolen(get_sv("@", FALSE));
fprintf( stderr, "%s", error );
return false;
}
return true;
}
const char* Backend::PerlEvalF( const char* a_pcFormat, ... )
{
va_list ap;
va_start( ap, a_pcFormat );
char acBuffer[1024];
vsnprintf( acBuffer, 1023, a_pcFormat, ap );
acBuffer[1023] = 0;
PERLEVAL(acBuffer);
const char *pcError = SvPV_nolen(get_sv("@", FALSE));
if ( pcError )
{
fprintf( stderr, "%s", pcError );
}
va_end( ap );
return pcError;
}
const char* Backend::GetPerlString( const char* acScalarName )
{
SV* poScalar = get_sv( acScalarName, FALSE );
if ( NULL == poScalar )
{
return "";
}
return SvPV_nolen( poScalar );
}
int Backend::GetPerlInt( const char* acScalarName )
{
SV* poScalar = get_sv( acScalarName, FALSE );
if ( NULL == poScalar )
{
return 0;
}
return SvIV( poScalar );
}
+/** Returns the total number of registered fighters in the backend.
+This may be more than the actual number of playable characters, as
+some or many characters may not be ready or installed.
+
+\see GetFighterID
+*/
+int Backend::GetNumberOfFighters()
+{
+ PerlEvalF( "$::CppNumberOfFighters = scalar keys %::FighterStats;" );
+ return GetPerlInt( "CppNumberOfFighters" );
+}
+
+/** Returns the ID of a fighter. The index parameter should start from
+zero, and be less than the value returned by GetNumberOfFighters().
+
+\see GetNumberOfFighters
+*/
+FighterEnum Backend::GetFighterID( int a_iIndex )
+{
+ PerlEvalF( "$::CppFighterID = (sort { $a - $b } keys %%::FighterStats)[%d];", a_iIndex );
+ return (FighterEnum) GetPerlInt( "CppFighterID" );
+}
+
+
+
+
void Backend::AdvancePerl()
{
PERLEVAL("GameAdvance();");
}
void Backend::ReadFromPerl()
{
if ( perl_bgx == NULL )
{
perl_bgx = get_sv("bgx", TRUE);
perl_bgy = get_sv("bgy", TRUE);
perl_p1x = get_sv("p1x", TRUE);
perl_p1y = get_sv("p1y", TRUE);
perl_p1f = get_sv("p1f", TRUE);
perl_p1h = get_sv("p1h", TRUE);
perl_p2x = get_sv("p2x", TRUE);
perl_p2y = get_sv("p2y", TRUE);
perl_p2f = get_sv("p2f", TRUE);
perl_p2h = get_sv("p2h", TRUE);
- perl_time= get_sv("time", TRUE);
perl_over= get_sv("over", TRUE);
perl_ko = get_sv("ko", TRUE);
}
m_iBgX = SvIV( perl_bgx );
m_iBgY = SvIV( perl_bgy );
m_aoPlayers[0].m_iX = SvIV( perl_p1x );
m_aoPlayers[0].m_iY = SvIV( perl_p1y );
m_aoPlayers[0].m_iFrame = SvIV( perl_p1f );
m_aoPlayers[0].m_iHitPoints = SvIV( perl_p1h ) / 10;
m_aoPlayers[1].m_iX = SvIV( perl_p2x );
m_aoPlayers[1].m_iY = SvIV( perl_p2y );
m_aoPlayers[1].m_iFrame = SvIV( perl_p2f );
m_aoPlayers[1].m_iHitPoints = SvIV( perl_p2h ) / 10;
- m_iGameTime = SvIV( perl_time );
m_iGameOver = SvIV( perl_over );
m_bKO = SvIV( perl_ko );
// 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 );
}
}
void Backend::PlaySounds()
{
for ( int i=0; i<m_iNumSounds; ++i )
{
Audio->PlaySample( m_asSounds[i].c_str() );
}
}
/***************************************************************************
PLAYBACK STRING CONVERSION ROUTINES
***************************************************************************/
void Backend::WriteToString( std::string& a_rsOutString )
{
char acBuffer[2048];
int iNumChars = sprintf( acBuffer, "%d %d %d %d %d %d %d %d %d %d %d ",
m_iBgX, m_iBgY,
m_aoPlayers[0].m_iX, m_aoPlayers[0].m_iY, m_aoPlayers[0].m_iFrame, m_aoPlayers[0].m_iHitPoints,
m_aoPlayers[1].m_iX, m_aoPlayers[1].m_iY, m_aoPlayers[1].m_iFrame, m_aoPlayers[1].m_iHitPoints,
m_iNumDoodads );
int i;
for ( i = 0; i<m_iNumDoodads; ++i )
{
SDoodad& roDoodad = m_aoDoodads[i];
iNumChars += sprintf( acBuffer+iNumChars, "%d %d %d %d %d %d %d %s ",
roDoodad.m_iX, roDoodad.m_iY, roDoodad.m_iType, roDoodad.m_iFrame,
roDoodad.m_iDir, roDoodad.m_iGfxOwner,
roDoodad.m_sText.size(), roDoodad.m_sText.c_str() );
}
iNumChars += sprintf( acBuffer+iNumChars, "%d ", m_iNumSounds );
for ( i = 0; i<m_iNumSounds; ++i )
{
iNumChars += sprintf( acBuffer+iNumChars, " %d %s",
m_asSounds[i].size(), m_asSounds[i].c_str() );
}
// debug( "Frame: '%s'\n", acBuffer );
a_rsOutString = acBuffer;
}
void Backend::ReadFromString( const std::string& a_rsString )
{
- if ( a_rsString.length() < 10 )
+ ReadFromString( a_rsString.c_str() );
+}
+
+
+void Backend::ReadFromString( const char* a_pcBuffer )
+{
+ if ( strlen( a_pcBuffer ) < 10 )
{
m_iNumDoodads = m_iNumSounds = 0;
return;
}
- const char* pcBuffer = a_rsString.c_str();
-
int iNumMatches;
int iOffset, iTotal;
- iNumMatches = sscanf( pcBuffer, "%d %d %d %d %d %d %d %d %d %d %d%n",
+ 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( pcBuffer+iTotal, "%d %d %d %d %d %d %d %n",
+ 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( pcBuffer + iTotal, j );
+ roDoodad.m_sText.assign( a_pcBuffer + iTotal, j );
iTotal += j;
}
- iNumMatches += sscanf( pcBuffer + iTotal, "%d%n",
+ 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( pcBuffer+iTotal, "%d %n",
+ iNumMatches += sscanf( a_pcBuffer+iTotal, "%d %n",
&j, &iOffset );
iTotal += iOffset;
- m_asSounds[i].assign( pcBuffer + iTotal, j );
+ m_asSounds[i].assign( a_pcBuffer + iTotal, j );
iTotal += j;
}
}
diff --git a/src/Backend.h b/src/Backend.h
index 5497e5b..51586cf 100644
--- a/src/Backend.h
+++ b/src/Backend.h
@@ -1,78 +1,83 @@
/***************************************************************************
Backend.h - description
-------------------
begin : Sun Dec 8 2002
copyright : (C) 2002 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#ifndef BACKEND_H
#define BACKEND_H
#include <string>
#include "FighterEnum.h"
class RlePack;
#define MAXDOODADS 20
#define MAXSOUNDS 20
class Backend
{
public:
// Lifecycle
Backend();
~Backend();
bool Construct();
// Miscellaneous
const char* PerlEvalF( const char* a_pcFormat, ... );
const char* GetPerlString( const char* a_pcScalarName );
int GetPerlInt( const char* a_pcScalarName );
+
+ // Fighter enumeration
+
+ int GetNumberOfFighters();
+ FighterEnum GetFighterID( int a_iIndex );
// Game data
void AdvancePerl();
void ReadFromPerl();
void PlaySounds();
void WriteToString( std::string& a_rsOutString );
void ReadFromString( const std::string& a_rsString );
+ void ReadFromString( const char* a_pcBuffer );
public:
- int m_iGameTime;
int m_iGameOver;
bool m_bKO;
int m_iBgX, m_iBgY;
int m_iNumDoodads;
int m_iNumSounds;
struct SPlayer
{
int m_iX, m_iY, m_iFrame, m_iHitPoints;
} m_aoPlayers[2];
struct SDoodad
{
int m_iX, m_iY, m_iType, m_iFrame;
int m_iDir, m_iGfxOwner;
std::string m_sText;
} m_aoDoodads[ MAXDOODADS ];
std::string m_asSounds[ MAXSOUNDS ];
};
extern Backend g_oBackend;
int DoGame( char* replay, bool isReplay, bool bDebug );
#endif
diff --git a/src/Chooser.cpp b/src/Chooser.cpp
new file mode 100644
index 0000000..dc3d993
--- /dev/null
+++ b/src/Chooser.cpp
@@ -0,0 +1,163 @@
+/***************************************************************************
+ Chooser.cpp - description
+ -------------------
+ begin : Tue Jan 27 2004
+ copyright : (C) 2004 by upi
+ email : upi@feel
+ ***************************************************************************/
+
+
+#include "Chooser.h"
+#include "Backend.h"
+
+#include "SDL_image.h"
+#include "gfx.h"
+#include "common.h"
+
+
+CChooser g_oChooser;
+
+
+CChooser::CChooser()
+{
+ m_iNumberOfFighters = -1;
+}
+
+
+CChooser::~CChooser()
+{
+ // Should free the portraits..
+}
+
+void CChooser::Init()
+{
+ if ( m_iNumberOfFighters >= 0 )
+ {
+ // Should only run once.
+ return;
+ }
+
+ // 1. Query the list of fighters from PlayerSelect.
+
+ m_iNumberOfFighters = g_oBackend.GetNumberOfFighters();
+ if ( m_iNumberOfFighters > 100 )
+ {
+ m_iNumberOfFighters = 100;
+ }
+
+ char pcFilename[FILENAME_MAX+1];
+ const char* s;
+ int i;
+
+ for ( i=0; i<m_iNumberOfFighters; ++i )
+ {
+ // Load the info of fighter #i
+ FighterEnum enFighter = g_oBackend.GetFighterID( i );
+ debug( "Fighter %d ID is %d\n", i, enFighter );
+ m_aenFighters[i] = enFighter;
+
+ // Load the portrait of fighter #i
+ g_oBackend.PerlEvalF( "GetFighterStats(%d);", enFighter );
+ s = g_oBackend.GetPerlString( "Portrait" );
+
+ strcpy( pcFilename, DATADIR );
+ strcat( pcFilename, "/characters/" );
+ strcat( pcFilename, s );
+
+ m_apoPortraits[i] = IMG_Load( pcFilename );
+ }
+ for ( i=m_iNumberOfFighters; i<100; ++i )
+ {
+ m_aenFighters[i] = UNKNOWN;
+ m_apoPortraits[i] = NULL;
+ }
+
+ Resize( 158, 74, 483, 479 );
+}
+
+
+#define LINEWIDTH 5
+
+void CChooser::Draw( SDL_Surface* a_poSurface )
+{
+ Init();
+
+ // 1. CREATE A BLACK AND WHITE BUFFER
+
+ SDL_Surface* poBuffer = SDL_CreateRGBSurface( SDL_SWSURFACE, x2 - x1, y2 - y1, 8, 0, 0, 0, 0 );
+
+ SDL_Color aoColors[256];
+ int i;
+ for ( i=0; i<256; ++i )
+ {
+ aoColors[i].r = aoColors[i].g = aoColors[i].b = i;
+ }
+ SDL_SetColors( poBuffer, aoColors, 0, 256);
+ SDL_SetColorKey( poBuffer, SDL_SRCCOLORKEY, 0 );
+
+ // 2. START BLITTING THE CHARACTER PORTRAITS
+
+ for ( i = 0; i<m_iNumberOfFighters; ++i )
+ {
+ // Blit portrait # i
+
+ if ( NULL == m_apoPortraits[i] )
+ {
+ continue;
+ }
+
+ // Calculate the bounding rectangle of the character portrait
+
+ int iCol = i % m_iCols;
+ int iRow = i / m_iCols;
+ int w = (x2-x1-LINEWIDTH) / m_iCols + 1;
+ int h = (y2-y1-LINEWIDTH) / m_iRows + 1;
+ int x = (x2-x1-LINEWIDTH) * iCol / m_iCols + LINEWIDTH;
+ int y = (y2-y1-LINEWIDTH) * iRow / m_iRows + LINEWIDTH;
+
+ SDL_Rect oSrc;
+ oSrc.x = oSrc.y = 0;
+ oSrc.w = w;
+ oSrc.h = h;
+ SDL_Rect oDst;
+ oDst.x = x;
+ oDst.y = y;
+
+ debug( "Drawing portrait %d at %d:%d %dx%d\n", i, x, y, w, h );
+
+ SDL_BlitSurface( m_apoPortraits[i], &oSrc, poBuffer, &oDst );
+ }
+
+ SDL_Rect oDst;
+ oDst.x = x1;
+ oDst.y = y1;
+ SDL_BlitSurface( poBuffer, NULL, a_poSurface, &oDst );
+}
+
+
+
+void CChooser::Resize( int a_x1, int a_y1, int a_x2, int a_y2 )
+{
+ x1 = a_x1;
+ y1 = a_y1;
+ x2 = a_x2;
+ y2 = a_y2;
+ m_iRows = 1;
+ m_iCols = 1;
+
+ while ( m_iRows * m_iCols < m_iNumberOfFighters )
+ {
+ double dColDensity = double(x2-x1) / double(m_iCols);
+ double dRowDensity = double(y2-y1) / double(m_iRows);
+
+ if ( dRowDensity > dColDensity )
+ {
+ m_iRows ++;
+ }
+ else
+ {
+ m_iCols ++;
+ }
+ }
+}
+
diff --git a/src/Chooser.h b/src/Chooser.h
new file mode 100644
index 0000000..6414e85
--- /dev/null
+++ b/src/Chooser.h
@@ -0,0 +1,57 @@
+/***************************************************************************
+ 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"
+
+struct SDL_Surface;
+
+
+class CChooser
+{
+public:
+ CChooser();
+ ~CChooser();
+
+ void Draw( SDL_Surface* a_poSurface );
+ void DrawRectangles();
+ void Resize( int x1, int y1, int x2, int y2 );
+ FighterEnum GetCurrentFighter( int a_iPlayer );
+ int GetCurrentPosition( int a_iPlayer );
+ bool GetDone( int a_iPlayer );
+
+ void SetDone( int a_iPlayer, bool a_bDone );
+ void SetCurrentFighter( int a_iPlayer );
+ void Move( int a_iPlayer, int a_iDirection );
+
+protected:
+ void Init();
+
+protected:
+ FighterEnum m_aenFighters[100];
+ SDL_Surface* m_apoPortraits[100];
+ bool m_iPlayerDone[2];
+ int m_iPlayerX[2];
+ int m_iPlayerY[2];
+
+ 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/Game.cpp b/src/Game.cpp
index f83fdfb..d15c5d3 100644
--- a/src/Game.cpp
+++ b/src/Game.cpp
@@ -1,801 +1,981 @@
/***************************************************************************
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 "PlayerSelect.h"
#include "Background.h"
#include "common.h"
#include "gfx.h"
#include "Backend.h"
#include "RlePack.h"
#include "State.h"
#include "Game.h"
#include "Audio.h"
#include "MortalNetwork.h"
#include "MszPerl.h"
extern PerlInterpreter* my_perl;
int Game::mg_iBackgroundNumber = 1;
/*
GAME PALETTE
From To Num Desc
---------------------------------------
0 .. 63 64 Background
64 .. 111 48 Doodads
112 .. 175 64 1st Fighter
176 .. 239 64 2nd Fighter
240 .. 255 16 basic colors
*/
#define MAXFRAMESKIP 5
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;
/***************************************************************************
GAME PUBLIC METHODS
***************************************************************************/
Game::Game( bool a_bIsReplay, bool a_bDebug)
{
m_bIsReplay = a_bIsReplay;
m_bDebug = a_bDebug;
+ m_enInitialGameMode = g_oState.m_enGameMode;
m_poBackground = new Background();
m_poBackground->Load(mg_iBackgroundNumber++);
if ( !m_poBackground->IsOK() )
{
m_poBackground->Load(1);
mg_iBackgroundNumber = 1;
}
m_poDoodads = LoadBackground( "Doodads.png", 48, 64 );
m_aiRoundsWonByPlayer[0] = m_aiRoundsWonByPlayer[1] = 0;
m_iNumberOfRounds = 0;
}
Game::~Game()
{
delete m_poBackground;
m_poBackground = NULL;
SDL_FreeSurface( m_poDoodads );
m_poDoodads = NULL;
}
+/** Runs a whole game, with two or three rounds.
+
+\retval 0 if player 1 has won.
+\retval 1 if player 2 has won.
+\retval -1 if the game was a draw.
+*/
int Game::Run()
{
do
{
m_sReplayString = "";
m_aReplayOffsets.clear();
DoOneRound();
if ( g_oState.m_bQuitFlag
- || SState::IN_DEMO == g_oState.m_enGameMode )
+ || m_enInitialGameMode != g_oState.m_enGameMode )
{
return -1;
}
} while ( m_aiRoundsWonByPlayer[0] < 2
&& m_aiRoundsWonByPlayer[1] < 2
&& m_iNumberOfRounds < 3 );
if ( m_aiRoundsWonByPlayer[1] > m_aiRoundsWonByPlayer[0] ) return 1;
if ( m_aiRoundsWonByPlayer[1] < m_aiRoundsWonByPlayer[0] ) return 0;
return -1;
}
+/** Returns the replay string of the last round.
+*/
std::string& Game::GetReplay()
{
return m_sReplayString;
}
/***************************************************************************
GAME DRAWING METHODS
***************************************************************************/
+/** Draws the hitpoint bars that are displayed on the top of the screen.
+Also draws the fighter names below the bars.
+
+Input variables:
+\li g_oBackend.m_aoPlayers[x].m_iHitPoints
+\li g_oPlayerSelect.GetFighterName
+*/
void Game::DrawHitPointDisplay()
{
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;
// 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;
SDL_BlitSurface( m_poDoodads, &src, gamescreen, &dst );
}
if ( m_aiRoundsWonByPlayer[1] > 0 )
{
dst.x = 604; dst.y = 11;
SDL_BlitSurface( m_poDoodads, &src, gamescreen, &dst );
}
sge_BF_textout( gamescreen, fastFont, g_oPlayerSelect.GetFighterName(0),
230 - g_oPlayerSelect.GetFighterNameWidth(0), 38 );
sge_BF_textout( gamescreen, fastFont, g_oPlayerSelect.GetFighterName(1),
410, 38 );
}
+/** Draws the background, using the m_poBackground object.
+*/
void Game::DrawBackground()
{
m_poBackground->Draw( g_oBackend.m_iBgX, g_oBackend.m_iBgY );
- /*
- sge_Blit( m_poBackground, gamescreen,
- g_oBackend.m_iBgX, g_oBackend.m_iBgY,
- 0, 0, SCREENWIDTH, SCREENHEIGHT );
- */
}
+/** In debug mode, this method is used to draw the frame of the fighters.
-void Game::DrawPoly( const char* sName, int color )
+\param a_sName The name of the polygon (in the perl namespace)
+\param a_iColor The game color to draw the polygon with.
+*/
+void Game::DrawPoly( const char* a_pcName, int a_iColor )
{
- AV *iList;
+ AV *poList;
int n;
- iList = get_av( sName, FALSE );
- if ( iList == NULL )
+ poList = get_av( a_pcName, FALSE );
+ if ( poList == NULL )
{
return;
}
- n = av_len( iList ) + 1;
+ 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( iList, i, false) );
- int y1 = SvIV( *av_fetch( iList, i+1, false) );
- int x2 = SvIV( *av_fetch( iList, j, false) );
- int y2 = SvIV( *av_fetch( iList, j+1, false) );
+ int x1 = SvIV( *av_fetch( poList, i, false) );
+ int y1 = SvIV( *av_fetch( poList, i+1, false) );
+ int x2 = SvIV( *av_fetch( poList, j, false) );
+ int y2 = SvIV( *av_fetch( poList, j+1, false) );
- sge_Line( gamescreen, x1, y1, x2, y2, color ) ;
+ sge_Line( gamescreen, x1, y1, x2, y2, a_iColor ) ;
}
}
+/** Draws every doodad that is currently defined in the backend.
+*/
void Game::DrawDoodads()
{
for ( int i=0; i<g_oBackend.m_iNumDoodads; ++i )
{
Backend::SDoodad& roDoodad = g_oBackend.m_aoDoodads[i];
if ( 0 == roDoodad.m_iType )
{
// Handle text doodads
const char *s = roDoodad.m_sText.c_str();
int iWidth = sge_BF_TextSize(fastFont, s).w;
int iDoodadX = roDoodad.m_iX - iWidth/2;
if ( iDoodadX + iWidth > 640 ) iDoodadX = 640 - iWidth;
if ( iDoodadX < 0 ) iDoodadX = 0;
int iDoodadY = roDoodad.m_iY;
sge_BF_textout( gamescreen, fastFont, s, iDoodadX, iDoodadY );
continue;
}
if ( roDoodad.m_iGfxOwner < 2 )
{
g_oPlayerSelect.GetPlayerInfo(roDoodad.m_iGfxOwner).m_poPack->Draw(
roDoodad.m_iFrame, roDoodad.m_iX, roDoodad.m_iY, roDoodad.m_iDir < 1 );
continue;
}
SDL_Rect rsrc, rdst;
rdst.x = roDoodad.m_iX;
rdst.y = roDoodad.m_iY;
rsrc.x = 64 * roDoodad.m_iFrame;
rsrc.y = 0;
rsrc.w = 64;
rsrc.h = 64;
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()
{
DrawBackground();
for ( int i=0; i<2; ++i )
{
int iFrame = g_oBackend.m_aoPlayers[i].m_iFrame;
if ( 0 != iFrame )
{
g_oPlayerSelect.GetPlayerInfo(i).m_poPack->Draw(
ABS(iFrame)-1,
g_oBackend.m_aoPlayers[i].m_iX,
g_oBackend.m_aoPlayers[i].m_iY,
iFrame<0 );
}
}
if ( m_bDebug )
{
DrawPoly( "p1head", C_LIGHTRED );
DrawPoly( "p1body", C_LIGHTGREEN );
DrawPoly( "p1legs", C_LIGHTBLUE );
DrawPoly( "p1hit", C_YELLOW );
DrawPoly( "p2head", C_LIGHTRED );
DrawPoly( "p2body", C_LIGHTGREEN );
DrawPoly( "p2legs", C_LIGHTBLUE );
DrawPoly( "p2hit", C_YELLOW );
}
DrawDoodads();
DrawHitPointDisplay();
if ( Ph_NORMAL == m_enGamePhase )
{
char s[100];
- sprintf( s, "%d", g_oBackend.m_iGameTime );
+ sprintf( s, "%d", m_iGameTime ); // m_iGameTime is maintained by DoGame
DrawTextMSZ( s, inkFont, 320, 10, AlignHCenter, C_LIGHTCYAN, gamescreen, false );
}
else if ( Ph_START == m_enGamePhase )
{
char s[100];
- const char* format = TranslateUTF8( "Round %d" );
+ const char* format = Translate( "Round %d" );
sprintf( s, format, m_iNumberOfRounds+1 );
DrawTextMSZ( s, inkFont, 320, 200, AlignHCenter, C_WHITE, gamescreen, false );
}
else if ( Ph_REWIND == m_enGamePhase )
{
DrawTextMSZ( "REW", inkFont, 320, 10, AlignHCenter, C_WHITE, gamescreen );
sge_BF_textout( gamescreen, fastFont, Translate("Press F1 to skip..."), 230, 450 );
}
else if ( Ph_SLOWFORWARD == m_enGamePhase )
{
DrawTextMSZ( "REPLAY", inkFont, 320, 10, AlignHCenter, C_WHITE, gamescreen );
sge_BF_textout( gamescreen, fastFont, Translate("Press F1 to skip..."), 230, 450 );
}
else if ( Ph_REPLAY == m_enGamePhase )
{
DrawTextMSZ( "DEMO", inkFont, 320, 10, AlignHCenter, C_WHITE, gamescreen );
}
if ( oFpsCounter.m_iFps > 0 )
{
sge_BF_textoutf( gamescreen, fastFont, 2, 2, "%d fps", oFpsCounter.m_iFps );
}
SDL_Flip( gamescreen );
}
/***************************************************************************
GAME PROTECTED METHODS
***************************************************************************/
bool Game::IsNetworkGame()
{
return SState::IN_NETWORK == g_oState.m_enGameMode;
}
+/** Returns true if we control our own data, or false if the network supplies
+us with game data. */
+bool Game::IsMaster()
+{
+ return !IsNetworkGame() || g_poNetwork->IsMaster();
+}
+
+
/**
This method reads and updates the game's variables. In replay mode,
this is done by parsing the replay string. Otherwise the perl
backend is advanced the given number of steps.
Whichever the case, the variables will be available in g_oBackend.
+
+Only the backend-driven variables are modified, the GamePhase and
+GameTime remain unchanged; these are up for DoOneRound and friends
+to modify.
*/
void Game::Advance( int a_iNumFrames )
{
if ( m_bIsReplay )
{
// Replay mode...
m_iFrame += a_iNumFrames;
if ( m_iFrame >= ((int)m_aReplayOffsets.size())-1 ) m_iFrame = m_aReplayOffsets.size() - 2;
if ( m_iFrame <= 0 ) m_iFrame = 0;
std::string sFrameDesc = m_sReplayString.substr(
m_aReplayOffsets[m_iFrame],
m_aReplayOffsets[m_iFrame+1] - m_aReplayOffsets[m_iFrame] );
g_oBackend.ReadFromString( sFrameDesc );
return;
}
if ( IsNetworkGame() )
{
bool bMaster = g_poNetwork->IsMaster();
-
+ g_poNetwork->Update();
+ if (!bMaster)
+ {
+ // We don't run our own backend, just pull the data from the network.
+ const char* pcRemoteBackend = g_poNetwork->GetLatestGameData();
+ g_oBackend.ReadFromString( pcRemoteBackend );
+ return;
+ }
}
+ static std::string sFrameDesc;
+
while ( a_iNumFrames > 0 )
{
-- a_iNumFrames;
- std::string sFrameDesc;
g_oBackend.AdvancePerl();
g_oBackend.ReadFromPerl();
g_oBackend.PlaySounds();
g_oBackend.WriteToString( sFrameDesc );
m_sReplayString += sFrameDesc;
m_sReplayString += '\n';
m_aReplayOffsets.push_back( m_sReplayString.size() );
}
+
+ if ( IsNetworkGame() && sFrameDesc.size() )
+ {
+ g_poNetwork->SendGameData( sFrameDesc.c_str() );
+ g_oBackend.PlaySounds();
+ }
+
}
+/** A helper method of ProcessEvents; it manages keypresses and releases of
+players.
+*/
+
+void Game::HandleKey( int a_iPlayer, int a_iKey, bool a_bDown )
+{
+ if ( IsMaster() )
+ {
+ if ( IsNetworkGame() )
+ {
+ a_iPlayer = 0;
+ }
+ g_oBackend.PerlEvalF( a_bDown ? "KeyDown(%d,%d);" : "KeyUp(%d,%d);", a_iPlayer, a_iKey );
+ }
+ else
+ {
+ g_poNetwork->SendKeystroke( 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()
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
g_oState.m_bQuitFlag = true;
return 1;
case SDL_KEYDOWN:
{
- if ( event.key.keysym.sym == SDLK_ESCAPE )
+ if ( event.key.keysym.sym == SDLK_ESCAPE /*&& !IsNetworkGame()*/ )
{
SState::TGameMode enMode = g_oState.m_enGameMode;
::DoMenu( true );
return g_oState.m_enGameMode == enMode ? 0 : 1;
}
- if ( event.key.keysym.sym == SDLK_F1 )
+ if ( event.key.keysym.sym == SDLK_F1 /*&& !IsNetworkGame()*/ )
{
// shortcut: abort the current round. This shall is here
// to ease testing, and will be removed from the final
// version.
return 1;
}
if ( Ph_NORMAL != m_enGamePhase &&
Ph_REPLAY != m_enGamePhase )
break;
for (int i=0; i<2; i++)
{
for (int j=0; j<9; j++ )
{
if (g_oState.m_aiPlayerKeys[i][j] == event.key.keysym.sym)
{
if (g_oState.m_enGameMode == SState::IN_DEMO)
{
g_oState.m_enGameMode = SState::IN_MULTI;
return 1;
}
-
- g_oBackend.PerlEvalF( "KeyDown(%d,%d);", i, j );
+ HandleKey( i, j, true );
+ //g_oBackend.PerlEvalF( "KeyDown(%d,%d);", i, j );
return 0;
}
}
}
break;
}
case SDL_KEYUP:
{
if ( Ph_NORMAL != m_enGamePhase )
break;
for (int i=0; i<2; i++)
{
for (int j=0; j<9; j++ )
{
if (g_oState.m_aiPlayerKeys[i][j] == event.key.keysym.sym)
{
+ HandleKey( i, j, false );
g_oBackend.PerlEvalF( "KeyUp(%d,%d);", i, j );
return 0;
}
}
}
break;
}
} // End of switch
} // End of while SDL_PollEvent;
return 0;
}
void Game::HurryUp()
{
Audio->PlaySample( "aroooga.voc" );
DrawGradientText( "HURRY UP!", titleFont, 200, gamescreen );
SDL_Delay( 1000 );
Audio->PlaySample( "machine_start.voc" );
}
void Game::TimeUp()
{
DrawGradientText( "TIME IS UP!", titleFont, 200, gamescreen );
SDL_Delay( 1000 );
}
+/** This methods starts and runs the "instant replay" mode that is done
+at the end of a game round. This means doing phases Ph_REWIND and Ph_SLOWFORWARD.
+
+Rewind will go back in time 200 ticks before the parameter a_iKoAt.
+*/
void Game::InstantReplay( int a_iKoAt )
{
int iCurrentFrame = m_aReplayOffsets.size() - 200;
int iThisTick, iLastTick, iGameSpeed;
m_enGamePhase = Ph_REWIND;
iGameSpeed = 8;
iThisTick = SDL_GetTicks() / iGameSpeed;
iLastTick = iThisTick - 1;
while ( iCurrentFrame < (int)m_aReplayOffsets.size() - 150 )
{
// 1. Wait for the next tick (on extremely fast machines..)
while (iThisTick == iLastTick)
{
iThisTick = SDL_GetTicks() / iGameSpeed;
if ( iThisTick==iLastTick ) SDL_Delay(1);
}
// 2. Advance as many ticks as necessary..
int iNumTicks = iThisTick - iLastTick;
if ( iNumTicks > 10 ) iNumTicks = 10;
if ( iNumTicks < 0 ) iNumTicks = 0;
iCurrentFrame += ( Ph_REWIND == m_enGamePhase ) ? -iNumTicks : +iNumTicks ;
if ( Ph_REWIND == m_enGamePhase
&& ( iCurrentFrame < a_iKoAt - 200 || iCurrentFrame <= 0 )
)
{
m_enGamePhase = Ph_SLOWFORWARD;
iGameSpeed = 16;
SDL_Delay(500);
}
iLastTick = iThisTick;
if ( iCurrentFrame < 0 ) iCurrentFrame = 0;
m_iFrame = iCurrentFrame;
if ( m_iFrame >= ((int)m_aReplayOffsets.size())-1 ) m_iFrame = m_aReplayOffsets.size() - 2;
if ( m_iFrame <= 0 ) m_iFrame = 0;
std::string sFrameDesc = m_sReplayString.substr(
m_aReplayOffsets[m_iFrame],
m_aReplayOffsets[m_iFrame+1] - m_aReplayOffsets[m_iFrame] );
//debug( "PB: Frame %d ofs %d-%d; data: '%s'\n", m_iFrame,
// m_aReplayOffsets[m_iFrame], m_aReplayOffsets[m_iFrame+1], sFrameDesc.c_str() );
g_oBackend.ReadFromString( sFrameDesc );
if ( ProcessEvents() )
{
break;
}
oFpsCounter.Tick();
// 3. Draw the next game screen..
Draw();
if ( g_oState.m_bQuitFlag
|| SState::IN_DEMO == g_oState.m_enGameMode )
{
break;
}
}
}
+/** This methods executes one round of gameplay.
+
+The game progresses through phases Ph_START, Ph_NORMAL, and
+Ph_KO. If a KO happened, it will invoke InstantReplay. At the end of
+the round m_aiRoundsWonByPlayer[x] will be incremented depending on the
+outcome. m_iNumberOfRounds will also increase by 1.
+*/
void Game::DoOneRound()
{
m_enGamePhase = Ph_START;
g_oBackend.PerlEvalF( "GameStart(%d,%d);", g_oState.m_iHitPoints, m_bDebug );
-
+
+ if ( IsNetworkGame() )
+ {
+ g_poNetwork->SynchStartRound();
+ }
+
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 = g_oState.m_iGameSpeed;
iThisTick = SDL_GetTicks() / iGameSpeed;
iLastTick = iThisTick - 1;
oFpsCounter.Reset();
-
- // 1. DO THE NORMAL GAME ROUND and HURRYUP
+
+ // 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;
-
- if ( Ph_START == m_enGamePhase )
- {
- if ( dGameTime <= 0 )
- {
- m_enGamePhase = Ph_NORMAL;
- dGameTime = g_oState.m_iGameTime * 1000;
- }
- }
- else if ( Ph_NORMAL == m_enGamePhase )
+
+ // 3. Check for state transitions and game time.
+ // START -> NORMAL
+ // NORMAL -> KO
+ // NORMAL -> TIMEUP
+ // bHurryUp flag can be set during NORMAL phase
+
+ if ( !IsNetworkGame()
+ || (IsNetworkGame() && g_poNetwork->IsMaster()) )
{
- if ( dGameTime < 10 * 1000
- && !bHurryUp )
+ if ( Ph_START == m_enGamePhase ) // Check for the end of the START phase
{
- bHurryUp = true;
- HurryUp();
- iGameSpeed = iGameSpeed * 3 / 4;
+ if ( dGameTime <= 0 )
+ {
+ m_enGamePhase = Ph_NORMAL;
+ dGameTime = g_oState.m_iGameTime * 1000;
+ }
}
- if ( g_oBackend.m_bKO )
+ else if ( Ph_NORMAL == m_enGamePhase ) // Check for the end of the NORMAL phase
{
- m_enGamePhase = Ph_KO;
- dGameTime = 10 * 1000;
- iKoFrame = m_aReplayOffsets.size();
+ if ( dGameTime < 10 * 1000
+ && !bHurryUp )
+ {
+ bHurryUp = true;
+ 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;
+ TimeUp();
+ break;
+ }
}
- else if ( dGameTime <= 0 )
+
+ m_iGameTime = (int) ((dGameTime + 500.0) / 1000.0);
+
+ if ( IsNetworkGame() )
{
- m_enGamePhase = Ph_TIMEUP;
- TimeUp();
- break;
+ g_poNetwork->SendGameTime( m_iGameTime, m_enGamePhase );
}
}
+ else
+ {
+ m_iGameTime = g_poNetwork->GetGameTime();
+ TGamePhaseEnum enOldPhase = m_enGamePhase;
+ m_enGamePhase = (TGamePhaseEnum) g_poNetwork->GetGamePhase();
+ dGameTime = 1000.0; // ignored.
+ }
- g_oBackend.m_iGameTime = (int) ((dGameTime + 500.0) / 1000.0);
iLastTick = iThisTick;
if ( ProcessEvents() || g_oState.m_bQuitFlag )
{
bReplayAfter = false;
break;
}
+
+ if ( IsNetworkGame() && IsMaster() )
+ {
+ int iKey;
+ bool bPressed;
+ while ( g_poNetwork->GetKeystroke( iKey, bPressed ) )
+ {
+ debug( "Got GetKeystroke: %d, %d\n", iKey, bPressed );
+ g_oBackend.PerlEvalF( bPressed ? "KeyDown(%d,%d);" : "KeyUp(%d,%d);", 1, iKey );
+ }
+ }
oFpsCounter.Tick();
// 3. Draw the next game screen..
Draw();
- if ( g_oBackend.m_iGameOver )
+ // 4. Check 'end of round' condition.
+
+ if ( !IsMaster() )
+ {
+ if ( g_poNetwork->IsRoundOver() )
+ {
+ break;
+ }
+ }
+ else if ( g_oBackend.m_iGameOver )
{
break;
}
}
+ int p1h = g_oBackend.m_aoPlayers[0].m_iHitPoints;
+ int p2h = g_oBackend.m_aoPlayers[1].m_iHitPoints;
+
// 3. DO THE REPLAY (IF THERE WAS A KO)
- if ( iKoFrame>0 && bReplayAfter )
+ if ( iKoFrame>0 && bReplayAfter && !IsNetworkGame() )
{
InstantReplay( iKoFrame );
}
// 4. END OF ROUND
-
- int p1h = g_oBackend.m_aoPlayers[0].m_iHitPoints;
- int p2h = g_oBackend.m_aoPlayers[1].m_iHitPoints;
debug( "Game over; p1h = %d; p2h = %d\n", p1h, p2h );
- if ( p1h > p2h ) ++m_aiRoundsWonByPlayer[0];
- if ( p2h > p1h ) ++m_aiRoundsWonByPlayer[1];
+ 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->SendRoundOver( iWhoWon, m_iNumberOfRounds > 0 );
+ }
+ }
+ else
+ {
+ int iWhoWon = g_poNetwork->GetWhoWon();
+ if ( iWhoWon>=0 )
+ {
+ ++m_aiRoundsWonByPlayer[iWhoWon];
+ }
+ }
+
++m_iNumberOfRounds;
}
void Game::DoReplay( const char* a_pcReplayFile )
{
std::ifstream oInput( a_pcReplayFile );
int iPlayer1, iPlayer2;
oInput >> iPlayer1 >> iPlayer2;
std::string sLine;
std::getline( oInput, sLine );
g_oPlayerSelect.SetPlayer( 0, (FighterEnum) iPlayer1 );
g_oPlayerSelect.SetPlayer( 1, (FighterEnum) iPlayer2 );
int iThisTick, iLastTick, iGameSpeed;
m_enGamePhase = Ph_REPLAY;
iGameSpeed = 12;
iThisTick = SDL_GetTicks() / iGameSpeed;
iLastTick = iThisTick - 1;
while ( !oInput.eof() )
{
// 1. Wait for the next tick (on extremely fast machines..)
while (iThisTick == iLastTick)
{
iThisTick = SDL_GetTicks() / iGameSpeed;
if ( iThisTick==iLastTick ) SDL_Delay(1);
}
// 2. Advance as many ticks as necessary..
int iNumTicks = iThisTick - iLastTick;
if ( iNumTicks > 5 ) iNumTicks = 5;
for ( int i=0; i< iNumTicks; ++i )
{
std::getline( oInput, sLine );
}
if ( 0 == sLine.size() )
{
break;
}
iLastTick = iThisTick;
g_oBackend.ReadFromString( sLine );
if ( ProcessEvents() )
{
break;
}
oFpsCounter.Tick();
// 3. Draw the next game screen..
Draw();
if ( g_oState.m_bQuitFlag )
{
break;
}
}
}
+
+
+
+/** Public static function.
+
+Other parts of OpenMortal need not include "Game.h" so long as they have
+the definition of this method (defined in "common.h"). The method runs
+a cycle of the game (either a normal game, or replay).
+
+In replay mode, DoReplay() is called, and the replay file is required.
+In normal mode, Run() is called. The replay file is recorded, if it is not NULL.
+*/
+
int DoGame( char* a_pcReplayFile, bool a_bIsReplay, bool a_bDebug )
{
Game oGame( a_bIsReplay, a_bDebug );
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;
}
}
diff --git a/src/Game.h b/src/Game.h
index 1580bf3..8e02915 100644
--- a/src/Game.h
+++ b/src/Game.h
@@ -1,75 +1,81 @@
/***************************************************************************
Game.h - description
-------------------
begin : Mon Aug 27 2003
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#ifndef GAME_H
#define GAME_H
#include <string>
#include <vector>
struct SDL_Surface;
class Background;
class Game
{
public:
Game( bool a_bIsReplay, bool a_bDebug );
~Game();
int Run();
std::string& GetReplay();
void DoReplay( const char* a_pcReplayFile );
protected:
void Draw();
void DrawHitPointDisplay();
void DrawBackground();
void DrawDoodads();
void DrawPoly( const char* a_pcName, int a_iColor );
void DoOneRound();
void Advance( int a_iNumFrames );
int ProcessEvents();
+ void HandleKey( int a_iPlayer, int a_iKey, bool a_bDown );
+
void HurryUp();
void TimeUp();
void InstantReplay( int a_iKoAt );
bool IsNetworkGame();
+ bool IsMaster();
protected:
static int mg_iBackgroundNumber;
bool m_bIsReplay;
bool m_bDebug;
Background* m_poBackground;
SDL_Surface* m_poDoodads;
int m_aiRoundsWonByPlayer[2];
int m_iNumberOfRounds;
int m_iFrame;
+ int m_iGameTime;
std::string m_sReplayString;
std::vector<int> m_aReplayOffsets;
- enum // This enum assumes its values during DoOneRound
+ 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/Makefile.am b/src/Makefile.am
index 0b3aba2..83c23bb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,28 +1,29 @@
## Process this file with automake to produce Makefile.in
bin_PROGRAMS = openmortal
openmortal_SOURCES = Audio.cpp Backend.cpp common.cpp \
Demo.cpp FighterStats.cpp FlyingChars.cpp Game.cpp gfx.cpp main.cpp \
menu.cpp PlayerSelect.cpp RlePack.cpp sge_bm_text.cpp \
sge_primitives.cpp sge_surface.cpp sge_tt_text.cpp State.cpp \
- GameOver.cpp Background.cpp MortalNetworkImpl.cpp
+ GameOver.cpp Background.cpp MortalNetworkImpl.cpp Chooser.cpp \
+ TextArea.cpp
EXTRA_DIST = Audio.h Backend.h common.h Demo.h FlyingChars.h gfx.h \
menu.h RlePack.h sge_bm_text.h sge_config.h sge_internal.h \
sge_primitives.h sge_surface.h sge_tt_text.h State.h \
Game.h FighterEnum.h PlayerSelect.h MszPerl.h \
Audio.cpp Backend.cpp common.cpp \
Demo.cpp FighterStats.cpp FlyingChars.cpp Game.cpp gfx.cpp main.cpp \
menu.cpp PlayerSelect.cpp RlePack.cpp sge_bm_text.cpp \
sge_primitives.cpp sge_surface.cpp sge_tt_text.cpp State.cpp \
GameOver.cpp Background.h MortalNetwork.h MortalNetworkImpl.h \
- FighterStats.h
+ FighterStats.h Chooser.h TextArea.h
CXXFLAGS= @CXXFLAGS@ -DDATADIR=\"${pkgdatadir}\" -Wall
# set the include path found by configure
#INCLUDES= $(all_includes)
# the library search path.
#msz_LDFLAGS = $(all_libraries)
diff --git a/src/Makefile.in b/src/Makefile.in
index d6ad6bb..3d2fbe1 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -1,364 +1,365 @@
# Makefile.in generated automatically by automake 1.4-p6 from Makefile.am
# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
SHELL = @SHELL@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
sbindir = @sbindir@
libexecdir = @libexecdir@
datadir = @datadir@
sysconfdir = @sysconfdir@
sharedstatedir = @sharedstatedir@
localstatedir = @localstatedir@
libdir = @libdir@
infodir = @infodir@
mandir = @mandir@
includedir = @includedir@
oldincludedir = /usr/include
DESTDIR =
pkgdatadir = $(datadir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
top_builddir = ..
ACLOCAL = @ACLOCAL@
AUTOCONF = @AUTOCONF@
AUTOMAKE = @AUTOMAKE@
AUTOHEADER = @AUTOHEADER@
INSTALL = @INSTALL@
INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
INSTALL_DATA = @INSTALL_DATA@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
transform = @program_transform_name@
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_alias = @build_alias@
build_triplet = @build@
host_alias = @host_alias@
host_triplet = @host@
target_alias = @target_alias@
target_triplet = @target@
AUTODIRS = @AUTODIRS@
CXX = @CXX@
FT2_CFLAGS = @FT2_CFLAGS@
FT2_CONFIG = @FT2_CONFIG@
FT2_LIBS = @FT2_LIBS@
MAKEINFO = @MAKEINFO@
PACKAGE = @PACKAGE@
PERL = @PERL@
SDL_CFLAGS = @SDL_CFLAGS@
SDL_CONFIG = @SDL_CONFIG@
SDL_LIBS = @SDL_LIBS@
VERSION = @VERSION@
perl_embed_ccflags = @perl_embed_ccflags@
perl_embed_ldflags = @perl_embed_ldflags@
bin_PROGRAMS = openmortal
-openmortal_SOURCES = Audio.cpp Backend.cpp common.cpp Demo.cpp FighterStats.cpp FlyingChars.cpp Game.cpp gfx.cpp main.cpp menu.cpp PlayerSelect.cpp RlePack.cpp sge_bm_text.cpp sge_primitives.cpp sge_surface.cpp sge_tt_text.cpp State.cpp GameOver.cpp Background.cpp MortalNetworkImpl.cpp
+openmortal_SOURCES = Audio.cpp Backend.cpp common.cpp Demo.cpp FighterStats.cpp FlyingChars.cpp Game.cpp gfx.cpp main.cpp menu.cpp PlayerSelect.cpp RlePack.cpp sge_bm_text.cpp sge_primitives.cpp sge_surface.cpp sge_tt_text.cpp State.cpp GameOver.cpp Background.cpp MortalNetworkImpl.cpp Chooser.cpp TextArea.cpp
-EXTRA_DIST = Audio.h Backend.h common.h Demo.h FlyingChars.h gfx.h menu.h RlePack.h sge_bm_text.h sge_config.h sge_internal.h sge_primitives.h sge_surface.h sge_tt_text.h State.h Game.h FighterEnum.h PlayerSelect.h MszPerl.h Audio.cpp Backend.cpp common.cpp Demo.cpp FighterStats.cpp FlyingChars.cpp Game.cpp gfx.cpp main.cpp menu.cpp PlayerSelect.cpp RlePack.cpp sge_bm_text.cpp sge_primitives.cpp sge_surface.cpp sge_tt_text.cpp State.cpp GameOver.cpp Background.h MortalNetwork.h MortalNetworkImpl.h FighterStats.h
+EXTRA_DIST = Audio.h Backend.h common.h Demo.h FlyingChars.h gfx.h menu.h RlePack.h sge_bm_text.h sge_config.h sge_internal.h sge_primitives.h sge_surface.h sge_tt_text.h State.h Game.h FighterEnum.h PlayerSelect.h MszPerl.h Audio.cpp Backend.cpp common.cpp Demo.cpp FighterStats.cpp FlyingChars.cpp Game.cpp gfx.cpp main.cpp menu.cpp PlayerSelect.cpp RlePack.cpp sge_bm_text.cpp sge_primitives.cpp sge_surface.cpp sge_tt_text.cpp State.cpp GameOver.cpp Background.h MortalNetwork.h MortalNetworkImpl.h FighterStats.h Chooser.h TextArea.h
CXXFLAGS = @CXXFLAGS@ -DDATADIR=\"${pkgdatadir}\" -Wall
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
CONFIG_HEADER = ../config.h
CONFIG_CLEAN_FILES =
PROGRAMS = $(bin_PROGRAMS)
DEFS = @DEFS@ -I. -I$(srcdir) -I..
CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
openmortal_OBJECTS = Audio.o Backend.o common.o Demo.o FighterStats.o \
FlyingChars.o Game.o gfx.o main.o menu.o PlayerSelect.o RlePack.o \
sge_bm_text.o sge_primitives.o sge_surface.o sge_tt_text.o State.o \
-GameOver.o Background.o MortalNetworkImpl.o
+GameOver.o Background.o MortalNetworkImpl.o Chooser.o TextArea.o
openmortal_LDADD = $(LDADD)
openmortal_DEPENDENCIES =
openmortal_LDFLAGS =
CXXCOMPILE = $(CXX) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
CXXLD = $(CXX)
CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@
DIST_COMMON = Makefile.am Makefile.in
DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
TAR = gtar
GZIP_ENV = --best
DEP_FILES = .deps/Audio.P .deps/Backend.P .deps/Background.P \
-.deps/Demo.P .deps/FighterStats.P .deps/FlyingChars.P .deps/Game.P \
-.deps/GameOver.P .deps/MortalNetworkImpl.P .deps/PlayerSelect.P \
-.deps/RlePack.P .deps/State.P .deps/common.P .deps/gfx.P .deps/main.P \
-.deps/menu.P .deps/sge_bm_text.P .deps/sge_primitives.P \
-.deps/sge_surface.P .deps/sge_tt_text.P
+.deps/Chooser.P .deps/Demo.P .deps/FighterStats.P .deps/FlyingChars.P \
+.deps/Game.P .deps/GameOver.P .deps/MortalNetworkImpl.P \
+.deps/PlayerSelect.P .deps/RlePack.P .deps/State.P .deps/TextArea.P \
+.deps/common.P .deps/gfx.P .deps/main.P .deps/menu.P \
+.deps/sge_bm_text.P .deps/sge_primitives.P .deps/sge_surface.P \
+.deps/sge_tt_text.P
SOURCES = $(openmortal_SOURCES)
OBJECTS = $(openmortal_OBJECTS)
all: all-redirect
.SUFFIXES:
.SUFFIXES: .S .c .cpp .o .s
$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(BUILT_SOURCES)
cd $(top_builddir) \
&& CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
mostlyclean-binPROGRAMS:
clean-binPROGRAMS:
-test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
distclean-binPROGRAMS:
maintainer-clean-binPROGRAMS:
install-binPROGRAMS: $(bin_PROGRAMS)
@$(NORMAL_INSTALL)
$(mkinstalldirs) $(DESTDIR)$(bindir)
@list='$(bin_PROGRAMS)'; for p in $$list; do \
if test -f $$p; then \
echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \
$(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
else :; fi; \
done
uninstall-binPROGRAMS:
@$(NORMAL_UNINSTALL)
list='$(bin_PROGRAMS)'; for p in $$list; do \
rm -f $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
done
.s.o:
$(COMPILE) -c $<
.S.o:
$(COMPILE) -c $<
mostlyclean-compile:
-rm -f *.o core *.core
clean-compile:
distclean-compile:
-rm -f *.tab.c
maintainer-clean-compile:
openmortal: $(openmortal_OBJECTS) $(openmortal_DEPENDENCIES)
@rm -f openmortal
$(CXXLINK) $(openmortal_LDFLAGS) $(openmortal_OBJECTS) $(openmortal_LDADD) $(LIBS)
.cpp.o:
$(CXXCOMPILE) -c $<
tags: TAGS
ID: $(HEADERS) $(SOURCES) $(LISP)
list='$(SOURCES) $(HEADERS)'; \
unique=`for i in $$list; do echo $$i; done | \
awk ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
here=`pwd` && cd $(srcdir) \
&& mkid -f$$here/ID $$unique $(LISP)
TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP)
tags=; \
here=`pwd`; \
list='$(SOURCES) $(HEADERS)'; \
unique=`for i in $$list; do echo $$i; done | \
awk ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
|| (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS)
mostlyclean-tags:
clean-tags:
distclean-tags:
-rm -f TAGS ID
maintainer-clean-tags:
distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
subdir = src
distdir: $(DISTFILES)
here=`cd $(top_builddir) && pwd`; \
top_distdir=`cd $(top_distdir) && pwd`; \
distdir=`cd $(distdir) && pwd`; \
cd $(top_srcdir) \
&& $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --gnu src/Makefile
@for file in $(DISTFILES); do \
d=$(srcdir); \
if test -d $$d/$$file; then \
cp -pr $$d/$$file $(distdir)/$$file; \
else \
test -f $(distdir)/$$file \
|| ln $$d/$$file $(distdir)/$$file 2> /dev/null \
|| cp -p $$d/$$file $(distdir)/$$file || :; \
fi; \
done
DEPS_MAGIC := $(shell mkdir .deps > /dev/null 2>&1 || :)
-include $(DEP_FILES)
mostlyclean-depend:
clean-depend:
distclean-depend:
-rm -rf .deps
maintainer-clean-depend:
%.o: %.c
@echo '$(COMPILE) -c $<'; \
$(COMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
@-cp .deps/$(*F).pp .deps/$(*F).P; \
tr ' ' '\012' < .deps/$(*F).pp \
| sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
>> .deps/$(*F).P; \
rm .deps/$(*F).pp
%.lo: %.c
@echo '$(LTCOMPILE) -c $<'; \
$(LTCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
@-sed -e 's/^\([^:]*\)\.o[ ]*:/\1.lo \1.o :/' \
< .deps/$(*F).pp > .deps/$(*F).P; \
tr ' ' '\012' < .deps/$(*F).pp \
| sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
>> .deps/$(*F).P; \
rm -f .deps/$(*F).pp
%.o: %.cpp
@echo '$(CXXCOMPILE) -c $<'; \
$(CXXCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
@-cp .deps/$(*F).pp .deps/$(*F).P; \
tr ' ' '\012' < .deps/$(*F).pp \
| sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
>> .deps/$(*F).P; \
rm .deps/$(*F).pp
%.lo: %.cpp
@echo '$(LTCXXCOMPILE) -c $<'; \
$(LTCXXCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
@-sed -e 's/^\([^:]*\)\.o[ ]*:/\1.lo \1.o :/' \
< .deps/$(*F).pp > .deps/$(*F).P; \
tr ' ' '\012' < .deps/$(*F).pp \
| sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
>> .deps/$(*F).P; \
rm -f .deps/$(*F).pp
info-am:
info: info-am
dvi-am:
dvi: dvi-am
check-am: all-am
check: check-am
installcheck-am:
installcheck: installcheck-am
install-exec-am: install-binPROGRAMS
install-exec: install-exec-am
install-data-am:
install-data: install-data-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
install: install-am
uninstall-am: uninstall-binPROGRAMS
uninstall: uninstall-am
all-am: Makefile $(PROGRAMS)
all-redirect: all-am
install-strip:
$(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
installdirs:
$(mkinstalldirs) $(DESTDIR)$(bindir)
mostlyclean-generic:
clean-generic:
distclean-generic:
-rm -f Makefile $(CONFIG_CLEAN_FILES)
-rm -f config.cache config.log stamp-h stamp-h[0-9]*
maintainer-clean-generic:
mostlyclean-am: mostlyclean-binPROGRAMS mostlyclean-compile \
mostlyclean-tags mostlyclean-depend mostlyclean-generic
mostlyclean: mostlyclean-am
clean-am: clean-binPROGRAMS clean-compile clean-tags clean-depend \
clean-generic mostlyclean-am
clean: clean-am
distclean-am: distclean-binPROGRAMS distclean-compile distclean-tags \
distclean-depend distclean-generic clean-am
distclean: distclean-am
maintainer-clean-am: maintainer-clean-binPROGRAMS \
maintainer-clean-compile maintainer-clean-tags \
maintainer-clean-depend maintainer-clean-generic \
distclean-am
@echo "This command is intended for maintainers to use;"
@echo "it deletes files that may require special tools to rebuild."
maintainer-clean: maintainer-clean-am
.PHONY: mostlyclean-binPROGRAMS distclean-binPROGRAMS clean-binPROGRAMS \
maintainer-clean-binPROGRAMS uninstall-binPROGRAMS install-binPROGRAMS \
mostlyclean-compile distclean-compile clean-compile \
maintainer-clean-compile tags mostlyclean-tags distclean-tags \
clean-tags maintainer-clean-tags distdir mostlyclean-depend \
distclean-depend clean-depend maintainer-clean-depend info-am info \
dvi-am dvi check check-am installcheck-am installcheck install-exec-am \
install-exec install-data-am install-data install-am install \
uninstall-am uninstall all-redirect all-am all installdirs \
mostlyclean-generic distclean-generic clean-generic \
maintainer-clean-generic clean mostlyclean distclean maintainer-clean
# set the include path found by configure
#INCLUDES= $(all_includes)
# the library search path.
#msz_LDFLAGS = $(all_libraries)
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
diff --git a/src/MortalNetwork.h b/src/MortalNetwork.h
index 0fbeaa3..058b238 100644
--- a/src/MortalNetwork.h
+++ b/src/MortalNetwork.h
@@ -1,121 +1,127 @@
/***************************************************************************
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"
/** Mortal Network messages:
TYPICAL MESSAGE FLOW:
<connection is established>
Introduction messages are sent (version checking, usernames)
<players go to the character selection screen>
1. F <number> messages go both ways as players choose their characters.
2. R message goes in both direction when players have finished choosing.
<both sides go to the game screen>
In odd rounds, the "server" if the "master" and the "client" is the "slave"
In even rounds, the "client" if the "master" and the "server" is the "slave"
Both the master and the slave send an S message to synchronize the game start.
The master sends G <text> messages to update the backend on the slave side.
The slave sends K <number> <bool> messages to communicate keystrokes to the master side.
The master sends O <number> <bool> message when the round is over.
Back to game start synchronization.
<both sides go to final judgement - may disconnect the game>
<both sides go back to the character selection screen>
OTHERS:
Msgs can be send on the character selection screen with M <text>.
The connection can be broken at any time. IsConnectionAlive() must be called
periodically.
The "server" is always appears as player 1, the "client" is always player 2.
However, they both use the "Player 1" keys.
SUMMARY OF MESSAGES:
I <version> <username> - Introduction sent both ways on connection.
G <text> - Update on the game backend data.
K <number> <bool> - Key # up/down
M <text> - Incoming Msg text.
F <number> - I have switched to fighter X.
R - I have chosen a fighter.
S - Ready for the next round (synch).
O <number> <bool> - The round is over (who won, are there more rounds).
+T <number> <number> - Update the game time and game phase.
*/
class CMortalNetwork
{
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.
// Game methods
virtual void SynchStartRound() = 0;
virtual void SendGameData( const char* a_pcGameData ) = 0;
virtual const char* GetLatestGameData() = 0;
virtual void SendKeystroke( int a_iKey, bool a_bPressed ) = 0;
- virtual bool GetKeystroke( int& a_riOutKey, bool a_rbPressed ) = 0;
+ virtual bool GetKeystroke( int& a_riOutKey, bool& a_rbPressed ) = 0;
+ virtual void SendGameTime( int a_iGameTime, int a_iGamePhase ) = 0;
+ virtual int GetGameTime() = 0;
+ virtual int GetGamePhase() = 0;
+
virtual void SendRoundOver( int a_iWhoWon, bool a_bGameOver ) = 0;
- virtual bool IsRoundOver( int& a_riOutWhoWon ) = 0;
+ virtual bool IsRoundOver() = 0;
virtual bool IsGameOver() = 0;
+ virtual int GetWhoWon() = 0;
};
extern CMortalNetwork* g_poNetwork;
#endif // MORTALNETWORK_H
diff --git a/src/MortalNetworkImpl.cpp b/src/MortalNetworkImpl.cpp
index 277aa83..2f15b04 100644
--- a/src/MortalNetworkImpl.cpp
+++ b/src/MortalNetworkImpl.cpp
@@ -1,668 +1,726 @@
/***************************************************************************
MortalNetworkImpl.cpp - description
-------------------
begin : Sun Jan 25 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
#include "MortalNetworkImpl.h"
#include "State.h"
#include "common.h"
#include "config.h"
#define MORTALNETWORKPORT 0x3A22
void MortalNetworkMessage( const char* format, ... );
bool MortalNetworkCheckKey();
CMortalNetwork* g_poNetwork = NULL;
void CMortalNetwork::Create() // static
{
if ( NULL == g_poNetwork )
{
g_poNetwork = new CMortalNetworkImpl;
}
}
CMortalNetworkImpl::CMortalNetworkImpl()
{
m_enState = NS_DISCONNECTED;
m_bServer = false;
m_bMaster = false;
m_poSocket = NULL;
m_enRemoteFighter = UNKNOWN;
m_bRemoteReady = false;
m_bRoundOver = false;
m_iWhoWon = -1;
m_bGameOver = false;
+ m_iGameTime = 0;
+ m_iGamePhase = 0;
if(SDLNet_Init()==-1)
{
m_bNetworkAvailable = false;
m_sLastError = SDLNet_GetError();
debug ( "Error opening SDLNet: %s\n", m_sLastError.c_str() );
}
m_bNetworkAvailable = true;
}
CMortalNetworkImpl::~CMortalNetworkImpl()
{
Stop();
}
bool CMortalNetworkImpl::Start( const char* a_pcServerName )
{
#define RETURNNOERROR { \
debug( "%s\n", m_sLastError.c_str() ); \
return false; }
#define RETURNWITHERROR { \
m_sLastError = SDLNet_GetError(); \
debug( "%s\n", m_sLastError.c_str() ); \
return false; }
#define RETURNWITHADDITIONALERROR { \
m_sLastError += SDLNet_GetError(); \
debug( "%s\n", m_sLastError.c_str() ); \
return false; }
if ( !m_bNetworkAvailable )
{
return false;
}
debug( "CMortalNetworkImpl::Start( %s )\n", a_pcServerName ? a_pcServerName : "NULL" );
IPaddress oAddress;
if ( a_pcServerName )
{
MortalNetworkMessage( Translate("Resolving hostname...") );
}
int iResult = SDLNet_ResolveHost( &oAddress, a_pcServerName, MORTALNETWORKPORT );
if ( iResult )
{
m_sLastError = Translate( "Couldn't resolve host." );
RETURNNOERROR;
}
debug( "IP Address of server is 0x%x\n", oAddress.host );
if ( a_pcServerName )
{
Uint32 ipaddr=SDL_SwapBE32(oAddress.host);
MortalNetworkMessage("Connecting to %d.%d.%d.%d port %d",
ipaddr>>24, (ipaddr>>16)&0xff, (ipaddr>>8)&0xff, ipaddr&0xff, oAddress.port);
}
if ( !a_pcServerName )
{
// SERVER-MODE CONNECTION
m_poSocket = SDLNet_TCP_Open( &oAddress );
// Wait for connection ...
MortalNetworkMessage ( Translate("Waiting for connection... (press any key to abort)") );
TCPsocket poClient;
while ( 1 )
{
poClient = SDLNet_TCP_Accept( m_poSocket );
if ( poClient ) break;
if (MortalNetworkCheckKey()) break;;
SDL_Delay( 100 );
}
SDLNet_TCP_Close( m_poSocket );
if ( NULL == poClient )
{
m_sLastError = "No connection.";
return false;
}
IPaddress* poRemoteAddress = SDLNet_TCP_GetPeerAddress(poClient);
if ( !poRemoteAddress )
{
RETURNWITHERROR;
}
Uint32 ipaddr=SDL_SwapBE32(poRemoteAddress->host);
MortalNetworkMessage("Accepted connection from %d.%d.%d.%d port %d",
ipaddr>>24, (ipaddr>>16)&0xff, (ipaddr>>8)&0xff, ipaddr&0xff, oAddress.port);
// Set the client socket as our socket, and drop the server socket.
m_poSocket = poClient;
}
else
{
// CLIENT-MODE CONNECTION
MortalNetworkMessage ( Translate("Waiting for connection... (press any key to abort)") );
while (1)
{
m_poSocket = SDLNet_TCP_Open( &oAddress );
if ( m_poSocket ) break;
if ( MortalNetworkCheckKey() ) break;
SDL_Delay( 100 );
}
if ( NULL == m_poSocket )
{
RETURNWITHERROR;
}
}
// CONNECTION ESTABLISHED. SEND INTRO PACKETS
MortalNetworkMessage( Translate("Connection established.") );
struct SIntroPackage
{
char cID;
char acVersion[10];
} oIntroPackage, oRemotePackage;
oIntroPackage.cID = 'I';
strncpy( oIntroPackage.acVersion, VERSION, 10 );
oIntroPackage.acVersion[9] = 0;
debug( "Sending intro package... " );
int iRetval = SDLNet_TCP_Send( m_poSocket, &oIntroPackage, sizeof( oIntroPackage ) );
if ( iRetval < (int) sizeof( oIntroPackage ) )
{
RETURNWITHERROR;
}
iRetval = SDLNet_TCP_Recv( m_poSocket, &oRemotePackage, sizeof( oRemotePackage ) );
if ( iRetval <= 0 )
{
RETURNWITHERROR;
}
if ( iRetval < (int) sizeof( oRemotePackage )
|| oRemotePackage.cID != 'I'
|| strncmp( oRemotePackage.acVersion, VERSION, 9 ) )
{
m_sLastError = Translate( "The remote side has a different version of OpenMortal running." );
RETURNNOERROR;
}
MortalNetworkMessage( Translate("Life is good.") );
m_enState = NS_CHARACTER_SELECTION;
m_bServer = NULL == a_pcServerName;
m_bMaster = m_bServer;
m_sLastError = "";
m_asMsgs.clear();
m_enRemoteFighter = UNKNOWN;
m_bRemoteReady = false;
m_sLatestGameData = "";
m_aiKeystrokes.clear();
m_abKeystrokes.clear();
+ m_iGameTime = 0;
+ m_iGamePhase = 0;
+ m_iIncomingBufferSize = 0;
m_poSocketSet = SDLNet_AllocSocketSet( 1 );
SDLNet_TCP_AddSocket( m_poSocketSet, m_poSocket ); // Check for errors?
- while (!MortalNetworkCheckKey()) SDL_Delay( 100 );
-
return true;
}
void CMortalNetworkImpl::Stop()
{
if ( NS_DISCONNECTED == m_enState )
{
return;
}
g_oState.m_enGameMode = SState::IN_DEMO;
m_enState = NS_DISCONNECTED;
SDLNet_FreeSocketSet( m_poSocketSet );
SDLNet_TCP_Close( m_poSocket );
}
bool CMortalNetworkImpl::IsConnectionAlive()
{
return ( NS_DISCONNECTED != m_enState );
}
#define DISCONNECTONCOMMUNICATIONERROR { \
m_sLastError = Translate("Communication error. Disconnecting."); \
Stop(); \
return; }
#define DISCONNECTWITH(A) { \
m_sLastError = Translate("Communication error. Disconnecting."); \
Stop(); \
return(A); }
#define CHECKCONNECTION if ( NS_DISCONNECTED == m_enState ) return;
void CMortalNetworkImpl::Update()
{
CHECKCONNECTION;
+
+ // 1. CHECK FOR STUFF TO READ
int iRetval = SDLNet_CheckSockets( m_poSocketSet, 0 );
if ( iRetval <= 0 )
{
return;
}
+
+ // 2. APPEND AT MOST 1024 bytes TO THE END OF THE INCOMING BUFFER
- char acBuffer[1024];
- iRetval = SDLNet_TCP_Recv( m_poSocket, acBuffer, 1024 );
+ iRetval = SDLNet_TCP_Recv( m_poSocket, m_acIncomingBuffer + m_iIncomingBufferSize, 1024 );
if ( iRetval <= 0 )
{
m_sLastError = SDLNet_GetError();
Stop();
return;
}
+ m_iIncomingBufferSize += iRetval;
- // OK, we've read it. Now let's see what it is.
+ // 3. CONSUME THE INCOMING BUFFER.
+ // We always make sure the incoming buffer starts with a package header.
int iOffset = 0;
- while ( iOffset < iRetval )
+ while ( iOffset < m_iIncomingBufferSize )
{
- debug( "Received stuff.. %c type, %d length, %d offset\n", acBuffer[iOffset], iRetval, iOffset );
-
- if ( NS_CHARACTER_SELECTION == m_enState )
+ // 3.1. Check if we have enough data to receive the package.
+
+ if ( m_iIncomingBufferSize - iOffset < 4 )
{
- switch ( acBuffer[iOffset] )
- {
- case 'M': iOffset += ReceiveMsg( acBuffer+iOffset, iRetval ); break;
- case 'F': iOffset += ReceiveFighter( acBuffer+iOffset, iRetval ); break;
- case 'R': iOffset += ReceiveReady( acBuffer+iOffset, iRetval ); break;
- case 'S': ++iOffset; break;
- default: DISCONNECTONCOMMUNICATIONERROR;
- }
+ // Not enough space left for a full header.
+ debug( "Not enough space left for a full header (%d).\n", m_iIncomingBufferSize-iOffset );
+ break;
}
- else
+
+ unsigned int iLengthOfPackage = SDL_SwapBE16(*((Uint16*)(m_acIncomingBuffer + iOffset + 1)));
+ if ( iLengthOfPackage > 1000 )
+ {
+ debug( "Maximum package size exceeded.\n" );
+ DISCONNECTONCOMMUNICATIONERROR;
+ }
+ debug( "Receiving stuff.. %c type, %d package length, offset %d in buffer, %d bytes in buffer\n",
+ m_acIncomingBuffer[iOffset], iLengthOfPackage, iOffset, m_iIncomingBufferSize );
+
+ if ( iOffset + 4 + (int)iLengthOfPackage > m_iIncomingBufferSize )
+ {
+ // Not enough space left for the actual package.
+ debug( "Not enough space left for the actual package.\n" );
+ break;
+ }
+
+ // 3.2. Receive the data.
+
+ switch ( m_acIncomingBuffer[iOffset] )
{
- switch ( acBuffer[iOffset] )
+ case 'M': ReceiveMsg( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
+ case 'F': ReceiveFighter( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
+ case 'R': ReceiveReady( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
+ case 'S': m_bSynchQueryResponse=true; break;
+ case 'G': ReceiveGameData( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
+ case 'K': ReceiveKeystroke( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
+ case 'O': ReceiveRoundOver( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
+ case 'T': ReceiveGameTime( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
+ default:
{
- case 'M': iOffset += ReceiveMsg( acBuffer+iOffset, iRetval ); break;
- case 'G': iOffset += ReceiveGameData( acBuffer+iOffset, iRetval ); break;
- case 'K': iOffset += ReceiveKeystroke( acBuffer+iOffset, iRetval ); break;
- case 'O': iOffset += ReceiveRoundOver( acBuffer+iOffset, iRetval ); break;
- case 'S': ++iOffset; break;
- default: DISCONNECTONCOMMUNICATIONERROR;
+ debug( "Bad ID: %c (%d)\n", m_acIncomingBuffer[iOffset], m_acIncomingBuffer[iOffset] );
+ DISCONNECTONCOMMUNICATIONERROR;
}
}
if ( !IsConnectionAlive() )
{
return;
}
+
+ iOffset += iLengthOfPackage + 4;
}
+
+ // 4. MOVE LEFTOVER DATA TO THE BEGINNING OF THE INCOMING BUFFER
+ // The leftover data starts at iOffset, and is (m_iIncomingBufferSize-iOffset) long.
+
+ memmove( m_acIncomingBuffer, m_acIncomingBuffer + iOffset, m_iIncomingBufferSize-iOffset );
+ m_iIncomingBufferSize -= iOffset;
}
const char* CMortalNetworkImpl::GetLastError()
{
return m_sLastError.c_str();
}
bool CMortalNetworkImpl::IsMaster()
{
return m_bMaster;
}
-/*
- enum TNetworkState
- {
- NS_DISCONNECTED,
- NS_CHARACTER_SELECTION,
- NS_IN_GAME,
- };
-
- TNetworkState m_enState;
- bool m_bServer;
- bool m_bMaster;
- TCPsocket m_poSocket;
-
- std::list<std::string> m_asMsgs;
-
- // GAME DATA
-
- FighterEnum m_enRemoteFighter;
- bool m_bRemoteReady;
-
- std::string m_sLatestGameData;
- std::list<int> m_iKeystrokes;
- std::list<int> m_bKeystrokes;
-
- bool m_bRoundOver;
- int m_iWhoWon;
- bool m_bGameOver;
-*/
-
-
const char* CMortalNetworkImpl::GetRemoteUsername()
{
return "upi";
}
/*************************************************************************
MSG RELATED METHODS
*************************************************************************/
-#define MAXSTRINGLENGTH 900
-struct SMsgPackage
-{
- char cID;
- Uint16 iLength;
- char acData[1024];
-};
-void CMortalNetworkImpl::InternalSendString( const char* a_pcText, char a_cID )
+/** All sent data must go through this method. It ensures the well-formed
+header for the data.
+The header itself looks like this:
+ID char
+Length Uint16
+Reserved char
+This is followed by as many bytes as the Length is.
+*/
+void CMortalNetworkImpl::SendRawData( char a_cID, const void* a_pData, int a_iLength )
{
- CHECKCONNECTION;
-
- if ( NULL == a_pcText
- || 0 == *a_pcText )
- {
- return;
- }
+ char acBuffer[4];
+ acBuffer[0] = a_cID;
+ *((Uint16*)(acBuffer+1)) = SDL_SwapBE16( a_iLength );
+ acBuffer[3] = 0;
- int iLength = strlen( a_pcText );
- if ( iLength > MAXSTRINGLENGTH ) iLength = MAXSTRINGLENGTH;
+ int iRetval = SDLNet_TCP_Send( m_poSocket, &acBuffer, 4 );
+ if ( iRetval != 4 ) DISCONNECTONCOMMUNICATIONERROR;
- SMsgPackage oPackage;
- oPackage.cID = a_cID;
- oPackage.iLength = iLength;
- strncpy( oPackage.acData, a_pcText, iLength );
- oPackage.acData[iLength] = 0;
-
- int iPackageLength = iLength + sizeof(char) + sizeof(Uint16);
-
- int iRetval = SDLNet_TCP_Send( m_poSocket, &oPackage, iPackageLength );
- if ( iRetval < iPackageLength ) DISCONNECTONCOMMUNICATIONERROR;
-}
-
-char* CMortalNetworkImpl::InternalReceiveString( void* a_pData, int a_iLength, int& a_riOutLength )
-{
- a_riOutLength = -1;
-
- // Verify data length vs package length
-
- SMsgPackage* pcPackage = (SMsgPackage*) a_pData;
- if ( a_iLength < (int) sizeof(char) + (int) sizeof(Uint16) + 1 )
+ if ( a_iLength )
{
- DISCONNECTWITH(NULL);
+ iRetval = SDLNet_TCP_Send( m_poSocket, (void*) a_pData, a_iLength );
+ if (iRetval != a_iLength ) DISCONNECTONCOMMUNICATIONERROR;
}
-
- a_riOutLength = sizeof(char) + sizeof(Uint16) + pcPackage->iLength;
- if ( pcPackage->iLength > MAXSTRINGLENGTH
- || a_iLength < a_riOutLength )
- {
- DISCONNECTWITH(NULL);
- }
-
- pcPackage->acData[ pcPackage->iLength ] = 0;
- return pcPackage->acData;
}
+#define MAXSTRINGLENGTH 900
+
+
void CMortalNetworkImpl::SendMsg( const char* a_pcMsg )
{
- InternalSendString( a_pcMsg, 'M' );
+ CHECKCONNECTION;
+
+ int iMsgLen = strlen( a_pcMsg ) + 1;
+ if ( iMsgLen > MAXSTRINGLENGTH )
+ {
+ // Will not be 0 terminated if exceeds length!
+ iMsgLen = MAXSTRINGLENGTH;
+ }
+ SendRawData( 'M', a_pcMsg, iMsgLen );
}
-int CMortalNetworkImpl::ReceiveMsg( void* a_pData, int a_iLength )
+void CMortalNetworkImpl::ReceiveMsg( void* a_pData, int a_iLength )
{
- int iRetval;
- char* pcMsg = InternalReceiveString( a_pData, a_iLength, iRetval );
-
- if ( iRetval > 0 )
- {
- m_asMsgs.push_back( pcMsg );
- }
+ if ( a_iLength < 1 || a_iLength > MAXSTRINGLENGTH ) DISCONNECTONCOMMUNICATIONERROR;
+
+ char* acData = (char*) a_pData;
+ acData[ a_iLength-1 ] = 0; // Last char should be 0, just making sure..
- return iRetval;
+ m_asMsgs.push_back( acData );
}
bool CMortalNetworkImpl::IsMsgAvailable()
{
return m_asMsgs.size() > 0;
}
const char* CMortalNetworkImpl::GetMsg()
{
static std::string sLastMsg;
if ( IsMsgAvailable() )
{
sLastMsg = m_asMsgs.front();
m_asMsgs.pop_front();
return sLastMsg.c_str();
}
return NULL;
}
/*************************************************************************
CHARACTER SELECTION RELATED METHODS
*************************************************************************/
bool CMortalNetworkImpl::IsRemoteFighterAvailable( FighterEnum a_enFighter )
{
return true;
}
-struct SFighterPackage
-{
- char cID;
- Uint32 iFighter;
-};
-
void CMortalNetworkImpl::SendFighter( FighterEnum a_enFighter )
{
CHECKCONNECTION;
-
- SFighterPackage oPackage;
- oPackage.cID = 'F';
- oPackage.iFighter = SDL_SwapBE32( a_enFighter );
- int iRetval = SDLNet_TCP_Send( m_poSocket, &oPackage, sizeof(oPackage) );
- if ( iRetval < (int) sizeof(oPackage) ) DISCONNECTONCOMMUNICATIONERROR;
+
+ Uint32 iFighter = SDL_SwapBE32( a_enFighter );
+ SendRawData( 'F', &iFighter, sizeof (iFighter) );
}
-int CMortalNetworkImpl::ReceiveFighter( void* a_pcData, int a_iLength )
+void CMortalNetworkImpl::ReceiveFighter( void* a_pcData, int a_iLength )
{
- SFighterPackage *poPackage = (SFighterPackage*) a_pcData;
- if ( a_iLength < (int) sizeof(SFighterPackage) ) DISCONNECTWITH(-1);
+ Uint32 iFighter;
+ if ( a_iLength != sizeof(iFighter) ) DISCONNECTONCOMMUNICATIONERROR;
+ iFighter = *((Uint32*)a_pcData);
- m_enRemoteFighter = (FighterEnum) SDL_SwapBE32( poPackage->iFighter );
+ m_enRemoteFighter = (FighterEnum) SDL_SwapBE32( iFighter );
debug( "ReceiveFighter: %d\n", m_enRemoteFighter );
- return sizeof( SFighterPackage );
}
FighterEnum CMortalNetworkImpl::GetRemoteFighter()
{
return m_enRemoteFighter;
}
void CMortalNetworkImpl::SendReady()
{
CHECKCONNECTION;
-
- char cReady = 'R';
- int iRetval = SDLNet_TCP_Send( m_poSocket, &cReady, sizeof(cReady) );
- if ( iRetval != sizeof(cReady) ) DISCONNECTONCOMMUNICATIONERROR;
+
+ SendRawData( 'R', NULL, 0 );
}
-int CMortalNetworkImpl::ReceiveReady( void* a_pData, int a_iLength )
+void CMortalNetworkImpl::ReceiveReady( void* a_pData, int a_iLength )
{
- if ( a_iLength < (int) sizeof(char) ) DISCONNECTWITH(-1);
+ if ( a_iLength != 0 ) DISCONNECTONCOMMUNICATIONERROR;
m_bRemoteReady = true;
- return sizeof(char);
}
bool CMortalNetworkImpl::IsRemoteSideReady()
{
return m_bRemoteReady;
}
/*************************************************************************
GAME RELATED METHODS
*************************************************************************/
void CMortalNetworkImpl::SynchStartRound()
{
+ debug( "SynchStartRound STARTED.\n" );
+
m_bSynchQueryResponse = false;
// run until both sides manage to get a SYNCH
- char cID = 'S';
-
while ( !m_bSynchQueryResponse )
{
CHECKCONNECTION;
- int iRetval = SDLNet_TCP_Send( m_poSocket, &cID, 1 );
- if ( iRetval < 1 ) DISCONNECTONCOMMUNICATIONERROR;
+ SendRawData('S', NULL, 0);
+ if ( !IsConnectionAlive() ) break;
Update();
SDL_Delay(200);
if ( !IsConnectionAlive() ) break;
}
+
+ if ( IsConnectionAlive() )
+ {
+ m_enState = NS_IN_GAME;
+ m_bRoundOver = false;
+ m_bGameOver = false;
+ m_bRemoteReady = false;
+ m_bSynchQueryResponse = false;
+ }
+
+ debug( "SynchStartRound FINISHED.\n" );
}
+
+
+
+
+
void CMortalNetworkImpl::SendGameData( const char* a_pcGameData )
{
- InternalSendString( a_pcGameData, 'G' );
+ int iMsgLen = strlen( a_pcGameData ) + 1;
+ if ( iMsgLen > MAXSTRINGLENGTH )
+ {
+ // Will not be 0 terminated if exceeds length!
+ iMsgLen = MAXSTRINGLENGTH;
+ }
+ SendRawData( 'G', a_pcGameData, iMsgLen );
}
-int CMortalNetworkImpl::ReceiveGameData( void* a_pData, int a_iLength )
+void CMortalNetworkImpl::ReceiveGameData( void* a_pData, int a_iLength )
{
- int iRetval;
- char* pcData = InternalReceiveString( a_pData, a_iLength, iRetval );
+ if ( a_iLength < 1 || a_iLength > MAXSTRINGLENGTH ) DISCONNECTONCOMMUNICATIONERROR;
- if ( iRetval > 0 )
+ char* pcData = (char*) a_pData;
+ pcData[ a_iLength-1 ] = 0; // Last char should be 0, just making sure..
+
+ if ( pcData[0] )
{
m_sLatestGameData = pcData;
}
-
- return iRetval;
}
const char* CMortalNetworkImpl::GetLatestGameData()
{
return m_sLatestGameData.c_str();
}
struct SKeystrokePackage
{
- char cID;
char cKey;
bool bPressed;
};
void CMortalNetworkImpl::SendKeystroke( int a_iKey, bool a_bPressed )
{
SKeystrokePackage oPackage;
- oPackage.cID = 'K';
oPackage.cKey = a_iKey;
oPackage.bPressed = a_bPressed;
- int iRetval = SDLNet_TCP_Send( m_poSocket, &oPackage, sizeof(oPackage) );
- if ( iRetval < (int)sizeof(oPackage) ) DISCONNECTONCOMMUNICATIONERROR;
+ SendRawData( 'K', &oPackage, sizeof( oPackage) );
}
-int CMortalNetworkImpl::ReceiveKeystroke( void* a_pData, int a_iLength )
+void CMortalNetworkImpl::ReceiveKeystroke( void* a_pData, int a_iLength )
{
- if ( a_iLength < (int)sizeof(SKeystrokePackage) ) DISCONNECTWITH(-1);
+ if ( a_iLength != (int)sizeof(SKeystrokePackage) ) DISCONNECTONCOMMUNICATIONERROR;
SKeystrokePackage* poPackage = (SKeystrokePackage*) a_pData;
m_aiKeystrokes.push_back( poPackage->cKey );
m_abKeystrokes.push_back( poPackage->bPressed );
- return sizeof(SKeystrokePackage);
}
-bool CMortalNetworkImpl::GetKeystroke( int& a_riOutKey, bool a_rbOutPressed )
+bool CMortalNetworkImpl::GetKeystroke( int& a_riOutKey, bool& a_rbOutPressed )
{
if ( m_aiKeystrokes.size() == 0 )
{
return false;
}
a_riOutKey = m_aiKeystrokes.front();
a_rbOutPressed = m_abKeystrokes.front();
m_aiKeystrokes.pop_front();
m_abKeystrokes.pop_front();
+
+ debug( "GetKeystroke: %d, %d\n", a_riOutKey, a_rbOutPressed );
return true;
}
-void CMortalNetworkImpl::SendRoundOver( int a_iWhoWon, bool a_bGameOver )
+struct SGameTimePackage
+{
+ int iGameTime;
+ int iGamePhase;
+};
+
+void CMortalNetworkImpl::SendGameTime( int a_iGameTime, int a_iGamePhase )
{
+ if ( a_iGameTime == m_iGameTime
+ && a_iGamePhase == m_iGamePhase )
+ {
+ return; // Nothing to update, the other side already knows.
+ }
+
+ SGameTimePackage oPackage;
+ m_iGameTime = a_iGameTime;
+ m_iGamePhase = a_iGamePhase;
+ oPackage.iGameTime = SDL_SwapBE32( a_iGameTime );
+ oPackage.iGamePhase = SDL_SwapBE32( a_iGamePhase );
+
+ SendRawData( 'T', &oPackage, sizeof(SGameTimePackage) );
}
+void CMortalNetworkImpl::ReceiveGameTime( void* a_pData, int a_iLength )
+{
+ if ( a_iLength != sizeof(SGameTimePackage) ) DISCONNECTONCOMMUNICATIONERROR;
+ SGameTimePackage* poPackage = (SGameTimePackage*) a_pData;
+
+ m_iGameTime = SDL_SwapBE32( poPackage->iGameTime );
+ m_iGamePhase = SDL_SwapBE32( poPackage->iGamePhase );
+}
-int CMortalNetworkImpl::ReceiveRoundOver( void* a_pData, int a_iLength )
+int CMortalNetworkImpl::GetGameTime()
{
- return a_iLength;
+ return m_iGameTime;
}
-bool CMortalNetworkImpl::IsRoundOver( int& a_riOutWhoWon )
+int CMortalNetworkImpl::GetGamePhase()
{
- return false;
+ return m_iGamePhase;
+}
+
+
+
+
+struct SRoundOrder
+{
+ int iWhoWon;
+ bool bGameOver;
+};
+
+void CMortalNetworkImpl::SendRoundOver( int a_iWhoWon, bool a_bGameOver )
+{
+ SRoundOrder oPackage;
+
+ oPackage.iWhoWon = a_iWhoWon;
+ oPackage.bGameOver = a_bGameOver;
+ SendRawData( 'O', &oPackage, sizeof(SRoundOrder) );
+
+ if ( a_bGameOver )
+ {
+ m_enState = NS_CHARACTER_SELECTION;
+ }
+}
+
+void CMortalNetworkImpl::ReceiveRoundOver( void* a_pData, int a_iLength )
+{
+ if ( a_iLength != sizeof(SRoundOrder) ) DISCONNECTONCOMMUNICATIONERROR;
+ SRoundOrder* poPackage = (SRoundOrder*) a_pData;
+ m_iWhoWon = poPackage->iWhoWon;
+ m_bGameOver = poPackage->bGameOver;
+ m_bRoundOver = true;
+}
+
+bool CMortalNetworkImpl::IsRoundOver()
+{
+ return m_bRoundOver;
}
bool CMortalNetworkImpl::IsGameOver()
{
- return false;
+ return m_bGameOver;
}
+int CMortalNetworkImpl::GetWhoWon()
+{
+ return m_iWhoWon;
+}
diff --git a/src/MortalNetworkImpl.h b/src/MortalNetworkImpl.h
index 6cad767..8e50745 100644
--- a/src/MortalNetworkImpl.h
+++ b/src/MortalNetworkImpl.h
@@ -1,113 +1,126 @@
/***************************************************************************
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>
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.
// Game methods
void SynchStartRound();
void SendGameData( const char* a_pcGameData );
const char* GetLatestGameData();
void SendKeystroke( int a_iKey, bool a_bPressed );
- bool GetKeystroke( int& a_riOutKey, bool a_rbPressed );
+ bool GetKeystroke( int& a_riOutKey, bool& a_rbPressed );
+
+ void SendGameTime( int a_iGameTime, int a_iGamePhase );
+ int GetGameTime();
+ int GetGamePhase();
void SendRoundOver( int a_iWhoWon, bool a_bGameOver );
- bool IsRoundOver( int& a_riOutWhoWon );
+ int GetWhoWon();
+ bool IsRoundOver();
bool IsGameOver();
protected:
- void InternalSendString( const char* a_pcText, char a_cID );
- char* InternalReceiveString( void* a_pData, int a_iLength, int& a_riOutLength );
+ void SendRawData( char a_cID, const void* a_pData, int a_iLength );
+
+ //void InternalSendString( const char* a_pcText, char a_cID );
+ //char* InternalReceiveString( void* a_pData, int a_iLength, int& a_riOutLength );
- int ReceiveMsg( void* a_pData, int a_iLength );
- int ReceiveGameData( void* a_pData, int a_iLength );
- int ReceiveKeystroke( void* a_pData, int a_iLength );
- int ReceiveFighter( void* a_pData, int a_iLength );
- int ReceiveReady( void* a_pData, int a_iLength );
- int ReceiveRoundOver( void* a_pData, int a_iLength );
+ void ReceiveMsg( 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 ReceiveGameTime( void* a_pData, int a_iLength );
protected:
bool m_bNetworkAvailable;
enum TNetworkState
{
NS_DISCONNECTED,
NS_CHARACTER_SELECTION,
NS_IN_GAME,
};
TNetworkState m_enState;
bool m_bServer;
bool m_bMaster;
TCPsocket m_poSocket;
SDLNet_SocketSet m_poSocketSet;
+
+ char m_acIncomingBuffer[2048];
+ int m_iIncomingBufferSize;
std::string m_sLastError;
std::list<std::string> m_asMsgs;
// GAME DATA
FighterEnum m_enRemoteFighter;
bool m_bRemoteReady;
std::string m_sLatestGameData;
std::list<int> m_aiKeystrokes;
std::list<int> m_abKeystrokes;
bool m_bRoundOver;
int m_iWhoWon;
bool m_bGameOver;
+ int m_iGameTime;
+ int m_iGamePhase;
// REMOTE QUERY RESPONSES
bool m_bSynchQueryResponse;
};
#endif // MORTALNETWORKIMPL_H
diff --git a/src/PlayerSelect.cpp b/src/PlayerSelect.cpp
index c462440..e939a5d 100644
--- a/src/PlayerSelect.cpp
+++ b/src/PlayerSelect.cpp
@@ -1,558 +1,668 @@
/***************************************************************************
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 "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"
-#define CHOOSERLEFT 158
-#define CHOOSERTOP 74
-#define CHOOSERHEIGHT 80
-#define CHOOSERWIDTH 80
-#define CHOOSERROWS 5
-#define CHOOSERCOLS 4
-
#ifndef NULL
#define NULL 0
#endif
/***************************************************************************
PUBLIC EXPORTED VARIABLES
***************************************************************************/
PlayerSelect g_oPlayerSelect;
/***************************************************************************
PRIVATE VARIABLES (perl variable space)
***************************************************************************/
-/*
-int p1 = 0;
-int p2 = 3;
-bool done1 = false;
-bool done2 = false;
-*/
-
-
-/*
-FighterEnum ChooserCells[CHOOSERROWS][CHOOSERCOLS] = {
- { ZOLI, UPI, CUMI, SIRPI },
- { ULMAR, MACI, BENCE, GRIZLI },
- { AMBRUS, DESCANT, SURBA, DANI },
- { UNKNOWN, KINGA, MISI, UNKNOWN },
-};
-*/
-FighterEnum ChooserCells[CHOOSERROWS][CHOOSERCOLS] = {
+FighterEnum ChooserCells[5][4] = {
{ ZOLI, UPI, CUMI, SIRPI },
{ ULMAR, MACI, GRIZLI, DESCANT },
{ DANI, AMBRUS, BENCE, SURBA },
{ (FighterEnum)100, (FighterEnum)101, (FighterEnum)102, (FighterEnum)103 },
{ (FighterEnum)104, (FighterEnum)105, KINGA, MISI }
};
+FighterEnum ChooserCellsChat[4][5] = {
+ { ZOLI, UPI, CUMI, SIRPI, ULMAR },
+ { MACI, BENCE, GRIZLI, DESCANT, SURBA },
+ { DANI, AMBRUS, (FighterEnum)100, (FighterEnum)102, KINGA },
+ { (FighterEnum)104, (FighterEnum)105, (FighterEnum)103, (FighterEnum)101, MISI },
+};
+
PlayerSelect::PlayerSelect()
{
for ( int i=0; i<2; ++i )
{
m_aoPlayers[i].m_enFighter = UNKNOWN;
m_aoPlayers[i].m_enTint = NO_TINT;
m_aoPlayers[i].m_poPack = NULL;
}
-
+
m_iP1 = 0;
- m_iP2 = CHOOSERCOLS-1;
+ m_iP2 = 3;
}
const PlayerInfo& PlayerSelect::GetPlayerInfo( int a_iPlayer )
{
return m_aoPlayers[ a_iPlayer ? 1 : 0 ];
}
const char* PlayerSelect::GetFighterName( int a_iPlayer )
{
return m_aoPlayers[ a_iPlayer ? 1 : 0 ].m_sFighterName.c_str();
}
int PlayerSelect::GetFighterNameWidth( int a_iPlayer )
{
return m_aiFighterNameWidth[ a_iPlayer ? 1 : 0 ];
}
bool PlayerSelect::IsFighterAvailable( 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 );
}
/** LoadFighter simply looks up the filename associated with the given
-fighter, loads it, and returns the RlePack.
+fighter, loads it, and returns the RlePack.
\return The freshly loaded RlePack, or NULL if it could not be loaded.
*/
RlePack* PlayerSelect::LoadFighter( FighterEnum m_enFighter ) // static
{
char a_pcFilename[FILENAME_MAX+1];
const char* s;
-
+
g_oBackend.PerlEvalF( "GetFighterStats(%d);", m_enFighter );
s = g_oBackend.GetPerlString( "Datafile" );
strcpy( a_pcFilename, DATADIR );
strcat( a_pcFilename, "/characters/" );
strcat( a_pcFilename, s );
RlePack* pack = new RlePack( a_pcFilename, COLORSPERPLAYER );
if ( pack->Count() <= 0 )
{
debug( "Couldn't load RlePack: '%s'\n", a_pcFilename );
delete pack;
return NULL;
}
-
+
return pack;
}
/** SetPlayer loads the given fighter for the given player.
The RlePack is loaded first. If that succeeds, then the perl backend is
set too. The tint and palette of both players are set. */
void PlayerSelect::SetPlayer( int a_iPlayer, FighterEnum a_enFighter )
{
if ( a_iPlayer ) a_iPlayer = 1; // It's 0 or 1.
-
+
if ( m_aoPlayers[a_iPlayer].m_enFighter == a_enFighter )
{
return;
}
if ( !IsFighterAvailable( a_enFighter ) )
{
return;
}
-
+
int iOffset = a_iPlayer ? COLOROFFSETPLAYER2 : COLOROFFSETPLAYER1;
RlePack* poPack = LoadFighter( a_enFighter );
poPack->OffsetSprites( iOffset );
-
+
if ( NULL == poPack )
{
debug( "SetPlayer(%d,%d): Couldn't load RlePack\n", a_iPlayer, a_enFighter );
return;
- }
-
+ }
+
delete m_aoPlayers[a_iPlayer].m_poPack;
m_aoPlayers[a_iPlayer].m_poPack = poPack;
m_aoPlayers[a_iPlayer].m_enFighter = a_enFighter;
-
+
g_oBackend.PerlEvalF( "SetPlayerNumber(%d,%d);", a_iPlayer, a_enFighter );
m_aoPlayers[a_iPlayer].m_sFighterName = g_oBackend.GetPerlString( "PlayerName" );
m_aiFighterNameWidth[a_iPlayer] = sge_BF_TextSize( fastFont, GetFighterName(a_iPlayer) ).w;
-
+
TintEnum enTint = NO_TINT;
-
+
if ( m_aoPlayers[0].m_enFighter == m_aoPlayers[1].m_enFighter )
{
enTint = TintEnum( (rand() % 4) + 1 );
}
SetTint( 1, enTint );
m_aoPlayers[a_iPlayer].m_poPack->ApplyPalette();
}
void PlayerSelect::SetTint( int a_iPlayer, TintEnum a_enTint )
{
m_aoPlayers[a_iPlayer].m_enTint = a_enTint;
if ( m_aoPlayers[a_iPlayer].m_poPack )
{
m_aoPlayers[a_iPlayer].m_poPack->SetTint( a_enTint );
m_aoPlayers[a_iPlayer].m_poPack->ApplyPalette();
}
}
bool PlayerSelect::IsNetworkGame()
{
return SState::IN_NETWORK == g_oState.m_enGameMode;
}
+
+FighterEnum PlayerSelect::GetFighterCell( int a_iIndex )
+{
+ if ( IsNetworkGame() )
+ {
+ return ChooserCellsChat[a_iIndex/m_iChooserCols][a_iIndex%m_iChooserCols];
+ }
+ else
+ {
+ return ChooserCells[a_iIndex/m_iChooserCols][a_iIndex%m_iChooserCols];
+ }
+}
+
+
+
void PlayerSelect::HandleKey( int a_iPlayer, int a_iKey )
{
// If we are in network mode, all keys count as the local player's...
if ( IsNetworkGame() )
{
a_iPlayer = g_poNetwork->IsMaster() ? 0 : 1;
}
-
+
int& riP = a_iPlayer ? m_iP2 : m_iP1;
int iOldP = riP;
-
+
bool& rbDone = a_iPlayer ? m_bDone2 : m_bDone1;
if ( rbDone )
{
return;
}
-
+
switch ( a_iKey )
{
case 0: // up
- if ( riP >= CHOOSERCOLS ) riP -= CHOOSERCOLS;
+ if ( riP >= m_iChooserCols ) riP -= m_iChooserCols;
break;
case 1: // down
- if ( (riP/CHOOSERCOLS) < (CHOOSERROWS-1) ) riP += CHOOSERCOLS;
+ if ( (riP/m_iChooserCols) < (m_iChooserRows-1) ) riP += m_iChooserCols;
break;
case 2: // left
- if ( (riP % CHOOSERCOLS) > 0 ) riP--;
+ if ( (riP % m_iChooserCols) > 0 ) riP--;
break;
case 3: // right
- if ( (riP % CHOOSERCOLS) < (CHOOSERCOLS-1) ) riP++;
+ if ( (riP % m_iChooserCols) < (m_iChooserCols-1) ) riP++;
break;
default:
- if ( IsFighterAvailable( ChooserCells[riP/CHOOSERCOLS][riP%CHOOSERCOLS] ) )
+ if ( IsFighterAvailable( GetFighterCell(riP) ) )
{
Audio->PlaySample("magic.voc");
-
+
rbDone = true;
g_oBackend.PerlEvalF( "PlayerSelected(%d);", a_iPlayer );
if ( IsNetworkGame() )
{
- g_poNetwork->SendFighter( ChooserCells[riP/CHOOSERCOLS][riP%CHOOSERCOLS] );
+ g_poNetwork->SendFighter( GetFighterCell(riP) );
g_poNetwork->SendReady();
}
return;
}
}
if ( iOldP != riP )
{
Audio->PlaySample("strange_quack.voc");
- if ( IsFighterAvailable( ChooserCells[riP/CHOOSERCOLS][riP%CHOOSERCOLS] ) )
+ if ( IsFighterAvailable( GetFighterCell(riP) ) )
{
if ( IsNetworkGame() )
{
- g_poNetwork->SendFighter( ChooserCells[riP/CHOOSERCOLS][riP%CHOOSERCOLS] );
+ g_poNetwork->SendFighter( GetFighterCell(riP) );
}
- SetPlayer( a_iPlayer, ChooserCells[riP/CHOOSERCOLS][riP%CHOOSERCOLS] );
+ SetPlayer( a_iPlayer, GetFighterCell(riP) );
}
}
}
void PlayerSelect::HandleNetwork()
{
g_poNetwork->Update();
-
+
+ bool bUpdateText = false;
+ while ( g_poNetwork->IsMsgAvailable() )
+ {
+ m_poTextArea->AddString( g_poNetwork->GetMsg(), C_YELLOW );
+ bUpdateText = true;
+ }
+ if ( bUpdateText )
+ {
+ 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 = ChooserCells[iOldP/CHOOSERCOLS][iOldP%CHOOSERCOLS];
+ 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<CHOOSERROWS; ++i )
+ for ( i=0; i<m_iChooserRows; ++i )
{
- for ( int j=0; j<CHOOSERCOLS; ++j )
+ for ( int j=0; j<m_iChooserCols; ++j )
{
- if ( ChooserCells[i][j] == enRemoteFighter )
+ if ( ChooserCellsChat[i][j] == enRemoteFighter )
{
- riP = i * CHOOSERCOLS + j;
+ riP = i * m_iChooserCols + j;
break;
}
}
}
}
bool bDone = g_poNetwork->IsRemoteSideReady();
if ( bDone )
{
rbDone = true;
Audio->PlaySample("magic.voc");
g_oBackend.PerlEvalF( "PlayerSelected(%d);", iPlayer );
}
}
void PlayerSelect::DrawRect( int a_iPos, int a_iColor )
{
- int iRow = a_iPos / CHOOSERCOLS;
- int iCol = a_iPos % CHOOSERCOLS;
+ int iRow = a_iPos / m_iChooserCols;
+ int iCol = a_iPos % m_iChooserCols;
SDL_Rect r, r1;
-
- r.x = CHOOSERLEFT + iCol * CHOOSERWIDTH;
- r.y = CHOOSERTOP + iRow * CHOOSERHEIGHT;
- r.w = CHOOSERWIDTH + 5;
+
+ 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 += CHOOSERHEIGHT;
+
+ r.y += m_iChooserHeight;
r1 = r;
SDL_FillRect( gamescreen, &r1, a_iColor );
-
- r.y -= CHOOSERHEIGHT;
+
+ r.y -= m_iChooserHeight;
r.w = 5;
- r.h = CHOOSERHEIGHT + 5;
+ r.h = m_iChooserHeight + 5;
r1 = r;
SDL_FillRect( gamescreen, &r1, a_iColor );
- r.x += CHOOSERWIDTH;
+ 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 x1, y1;
- x1 = CHOOSERLEFT + a_iCol * CHOOSERWIDTH +5;
- y1 = CHOOSERTOP + a_iRow * CHOOSERHEIGHT +5;
-
- sge_Line(a_poBackground, x1+5, y1+5, x1 + CHOOSERWIDTH-10, y1 + CHOOSERHEIGHT-10, 252);
- sge_Line(a_poBackground, x1 + CHOOSERWIDTH-10, y1+5, x1+5, y1 + CHOOSERHEIGHT-10, 252);
+ 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, 252);
+ sge_Line(a_poBackground, x1 + m_iChooserWidth-10, y1+5, x1+5, y1 + m_iChooserHeight-10, 252);
x1++;
- sge_Line(a_poBackground, x1+5, y1+5, x1 + CHOOSERWIDTH-10, y1 + CHOOSERHEIGHT-10, 252);
- sge_Line(a_poBackground, x1 + CHOOSERWIDTH-10, y1+5, x1+5, y1 + CHOOSERHEIGHT-10, 252);
+ sge_Line(a_poBackground, x1+5, y1+5, x1 + m_iChooserWidth-10, y1 + m_iChooserHeight-10, 252);
+ sge_Line(a_poBackground, x1 + m_iChooserWidth-10, y1+5, x1+5, y1 + m_iChooserHeight-10, 252);
y1++;
- sge_Line(a_poBackground, x1+5, y1+5, x1 + CHOOSERWIDTH-10, y1 + CHOOSERHEIGHT-10, 252);
- sge_Line(a_poBackground, x1 + CHOOSERWIDTH-10, y1+5, x1+5, y1 + CHOOSERHEIGHT-10, 252);
+ sge_Line(a_poBackground, x1+5, y1+5, x1 + m_iChooserWidth-10, y1 + m_iChooserHeight-10, 252);
+ sge_Line(a_poBackground, x1 + m_iChooserWidth-10, y1+5, x1+5, y1 + m_iChooserHeight-10, 252);
x1--;
- sge_Line(a_poBackground, x1+5, y1+5, x1 + CHOOSERWIDTH-10, y1 + CHOOSERHEIGHT-10, 252);
- sge_Line(a_poBackground, x1 + CHOOSERWIDTH-10, y1+5, x1+5, y1 + CHOOSERHEIGHT-10, 252);
+ sge_Line(a_poBackground, x1+5, y1+5, x1 + m_iChooserWidth-10, y1 + m_iChooserHeight-10, 252);
+ sge_Line(a_poBackground, x1 + m_iChooserWidth-10, y1+5, x1+5, y1 + m_iChooserHeight-10, 252);
}
void PlayerSelect::DoPlayerSelect()
{
// 1. Set up: Load background, mark unavailable fighters
+ bool bNetworkMode = IsNetworkGame();
+
+ if ( IsNetworkGame() )
+ {
+ m_iChooserLeft = 158;
+ m_iChooserTop = 74;
+ 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( "PlayerSelect.png", 111 );
-
+ SDL_Surface* poBackground = LoadBackground( IsNetworkGame() ? "PlayerSelect_chat.png" : "PlayerSelect.png", 111 );
+
DrawGradientText( "Choose A Fighter Dammit", titleFont, 10, poBackground );
+ //g_oChooser.Draw( poBackground );
- int i, j;
- for ( i=0; i<CHOOSERROWS; ++i )
+ int i, j;
+ for ( i=0; i<m_iChooserRows; ++i )
{
- for ( int j=0; j<CHOOSERCOLS; ++j )
+ for ( int j=0; j<m_iChooserCols; ++j )
{
- if ( !IsFighterAvailable(ChooserCells[i][j]) &&
- UNKNOWN != ChooserCells[i][j] )
+ if ( IsNetworkGame() )
{
- CheckPlayer( poBackground, i, j );
+ if ( !IsFighterAvailable(ChooserCellsChat[i][j]) &&
+ UNKNOWN != ChooserCellsChat[i][j] )
+ {
+ CheckPlayer( poBackground, i, j );
+ }
+ }
+ else
+ {
+ if ( !IsFighterAvailable(ChooserCells[i][j]) &&
+ UNKNOWN != ChooserCells[i][j] )
+ {
+ CheckPlayer( poBackground, i, j );
+ }
}
}
}
for ( i=0; i<2; ++i )
{
if ( m_aoPlayers[i].m_poPack ) m_aoPlayers[i].m_poPack->ApplyPalette();
- }
-
- SetPlayer( 0, ChooserCells[m_iP1/CHOOSERCOLS][m_iP1%CHOOSERCOLS] );
- SetPlayer( 1, ChooserCells[m_iP2/CHOOSERCOLS][m_iP2%CHOOSERCOLS] );
-
+ }
+
+ SetPlayer( 0, GetFighterCell(m_iP1) );
+ SetPlayer( 1, GetFighterCell(m_iP2) );
+
// 2. Run selection screen
-
+
g_oBackend.PerlEvalF( "SelectStart();" );
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:350 - 470:470
+ char acMsg[256];
+ sprintf( acMsg, "Press Enter to chat..." );
+ bool bDoingChat = false;
+
+ if ( IsNetworkGame() )
+ {
+ m_poReadline = new CReadline( IsNetworkGame() ? poBackground : NULL, impactFont,
+ acMsg, strlen(acMsg), 256, 15, 465, 610, C_LIGHTCYAN, C_BLACK, 255 );
+ m_poTextArea = new CTextArea( poBackground, impactFont, 15, 350, 610, 32*3 );
+ }
+ 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;
+ }
+
+ if ( bDoingChat )
+ {
+ // 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 );
+ m_poTextArea->AddString( acMsg, C_WHITE );
+ 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;
+ }
+
switch (event.type)
{
- case SDL_QUIT:
- g_oState.m_bQuitFlag = true;
- break;
case SDL_KEYDOWN:
{
if ( event.key.keysym.sym == SDLK_ESCAPE )
{
DoMenu( false );
break;
}
-
+ if ( IsNetworkGame() && bDoingChat == false &&
+ (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;
+ }
for ( i=0; i<2; i++ )
{
for ( j=0; j<9; j++ )
{
if (g_oState.m_aiPlayerKeys[i][j] == event.key.keysym.sym)
{
DrawRect( m_iP1, 240 );
DrawRect( m_iP2, 240 );
HandleKey( i, j );
}
}
}
}
break;
} // switch statement
} // Polling events
-
+
if ( IsNetworkGame() )
{
HandleNetwork();
}
-
+
g_oBackend.ReadFromPerl();
-
over = g_oBackend.m_iGameOver;
-
+
SDL_BlitSurface( poBackground, NULL, gamescreen, NULL );
if ( !m_bDone1) DrawRect( m_iP1, 250 );
if ( !m_bDone2) DrawRect( m_iP2, 253 );
-
+
for ( i=0; i<2; ++i )
{
+ int iYOffset = bNetworkMode ? -120 : 0;
if ( g_oBackend.m_aoPlayers[i].m_iFrame )
{
- m_aoPlayers[i].m_poPack->Draw(
+ 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,
+ 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 = ( CHOOSERLEFT - m_aiFighterNameWidth[i] ) / 2;
+ 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 );
+ x, IsNetworkGame() ? 10 : gamescreen->h - 30 + iYOffset );
}
-
+
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;
}
diff --git a/src/PlayerSelect.h b/src/PlayerSelect.h
index 2ed09f0..5da23aa 100644
--- a/src/PlayerSelect.h
+++ b/src/PlayerSelect.h
@@ -1,84 +1,96 @@
/***************************************************************************
PlayerSelect.h - description
-------------------
begin : 2003-09-05
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#ifndef PLAYERSELECT_H
#define PLAYERSELECT_H
#define COLORSPERPLAYER 64
#define COLOROFFSETPLAYER1 112
#define COLOROFFSETPLAYER2 (COLOROFFSETPLAYER1+64)
#include "FighterEnum.h"
#include <string>
class RlePack;
struct SDL_Surface;
+class CTextArea;
+class CReadline;
/** PlayerInfo structure stores information about a PLAYER.
In OpenMortal, the term PLAYER refers to one of the two guys playing the
game, as opposed to FIGHTER, which refers to one of the many playable
characters.
PlayerInfo stores: the player's selected fighter, the fighters tint and
RlePack. */
struct PlayerInfo
{
FighterEnum m_enFighter;
TintEnum m_enTint;
RlePack* m_poPack;
std::string m_sFighterName;
};
/** This class implements services that allows players to select their
fighters. It also stores info about which fighter is available, and
allows other parts of the program to programmatically assign a fighter
to a player, and set fighter tints (this is used by e.g. the "frozen"
effect.) */
class PlayerSelect
{
public:
PlayerSelect();
const PlayerInfo& GetPlayerInfo( int a_iPlayer );
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 );
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 );
static RlePack* LoadFighter( FighterEnum m_enFighter );
bool IsNetworkGame();
+ FighterEnum GetFighterCell( int a_iIndex );
protected:
PlayerInfo m_aoPlayers[2];
int m_iP1, m_iP2; // Chooser cells for player 1 and 2
bool m_bDone1, m_bDone2; // Has player n chosen a player?
int m_aiFighterNameWidth[2];
-
+
+ int m_iChooserLeft;
+ int m_iChooserTop;
+ int m_iChooserWidth;
+ int m_iChooserHeight;
+ int m_iChooserRows;
+ int m_iChooserCols;
+
+ CTextArea* m_poTextArea;
+ CReadline* m_poReadline;
};
extern PlayerSelect g_oPlayerSelect;
#endif // PLAYERSELECT_H
diff --git a/src/TextArea.cpp b/src/TextArea.cpp
new file mode 100644
index 0000000..cbff4b0
--- /dev/null
+++ b/src/TextArea.cpp
@@ -0,0 +1,75 @@
+/***************************************************************************
+ 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 "gfx.h"
+#include "common.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_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::AddString( const char* a_poText, int a_iColor )
+{
+ m_asRowTexts.push_front( a_poText );
+ m_aiRowColors.push_front( a_iColor );
+
+ debug( "Added '%s', number of texts is %d\n", a_poText, m_asRowTexts.size() );
+}
+
+
+void CTextArea::Redraw()
+{
+ TStringList::const_iterator itString = m_asRowTexts.begin();
+ TIntList::const_iterator itColors = m_aiRowColors.begin();
+
+ SDL_Rect oOldClipRect;
+ SDL_GetClipRect( m_poScreen, &oOldClipRect );
+ SDL_SetClipRect( m_poScreen, &m_oClipRect );
+
+ sge_Blit( m_poBackground, m_poScreen, 0, 0, x, y, w, h );
+
+ for ( int yPos = y + h - sge_TTF_FontHeight( m_poFont );
+ yPos >= y /*+ sge_TTF_FontAscent( m_poFont )*/;
+ 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 );
+}
+
diff --git a/src/TextArea.h b/src/TextArea.h
new file mode 100644
index 0000000..03814d8
--- /dev/null
+++ b/src/TextArea.h
@@ -0,0 +1,47 @@
+/***************************************************************************
+ 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;
+
+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 AddString( const char* a_poText, int a_iColor );
+ void Redraw();
+
+protected:
+ SDL_Surface* m_poScreen;
+ SDL_Surface* m_poBackground;
+ _sge_TTFont* m_poFont;
+ SDL_Rect m_oClipRect;
+ int x, y, w, h;
+
+ int m_iNumRows;
+ TStringList m_asRowTexts;
+ TIntList m_aiRowColors;
+};
+
+
+#endif // TEXTAREA_H
diff --git a/src/gfx.h b/src/gfx.h
index a2493c6..bbe2eae 100644
--- a/src/gfx.h
+++ b/src/gfx.h
@@ -1,45 +1,50 @@
/***************************************************************************
gfx.h - description
-------------------
begin : Tue Apr 10 2001
copyright : (C) 2001 by UPi
email : upi@apocalypse.rulez.org
***************************************************************************/
#ifndef _GFX_H
#define _GFX_H
struct _sge_TTFont;
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();
SDL_Surface* LoadBackground( const char* a_pcFilename, int a_iNumColors, int a_iPaletteOffset=0 );
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
#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 a903f46..3e92278 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,382 +1,383 @@
/***************************************************************************
main.cpp - description
-------------------
begin : Wed Aug 22 10:18:47 CEST 2001
copyright : (C) 2001 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "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_bmpFont* fastFont;
sge_bmpFont* creditsFont;
sge_bmpFont* storyFont;
SDL_Color Colors[] =
{
{ 0, 0, 0, 0 }, { 0, 0, 42, 0 }, { 0, 42, 0, 0 }, { 0, 42, 42, 0 },
{ 42, 0, 0, 0 }, { 42, 0, 42, 0 }, { 63, 42, 0, 0 }, { 42, 42, 42, 0 },
{ 21, 21, 21, 0 }, { 21, 21, 63, 0 }, { 21, 63, 21, 0 }, { 21, 63, 63, 0 },
{ 63, 21, 21, 0 }, { 63, 21, 63, 0 }, { 63, 63, 21, 0 }, { 63, 63, 63, 0 }
};
void Complain( const char* a_pcError )
{
#ifdef _WINDOWS
::MessageBoxA( 0, a_pcError, "OpenMortal", MB_ICONEXCLAMATION );
#else
fprintf( stderr, "%s", a_pcError );
#endif
}
_sge_TTFont* LoadTTF( const char* a_pcFilename, int a_iSize )
{
std::string sPath = std::string(DATADIR) + "/fonts/" + a_pcFilename;
_sge_TTFont* poFont = sge_TTF_OpenFont( sPath.c_str(), a_iSize );
if ( NULL == poFont )
{
Complain( ("Couldn't load font: " + sPath).c_str() );
}
return poFont;
}
sge_bmpFont* LoadBMPFont( const char* a_pcFilename )
{
std::string sPath = std::string(DATADIR) + "/fonts/" + a_pcFilename;
sge_bmpFont* poFont = sge_BF_OpenFont( sPath.c_str(), SGE_BFSFONT | SGE_BFTRANSP );
if ( NULL == poFont )
{
Complain( ("Couldn't load font: " + sPath).c_str() );
}
return poFont;
}
int init( int iFlags )
{
if (SDL_Init(SDL_INIT_VIDEO /*| SDL_INIT_AUDIO*/) < 0)
{
fprintf(stderr, "Failed to initialize SDL: %s\n", SDL_GetError());
return -1;
}
atexit(SDL_Quit);
int d = SDL_VideoModeOK(640, 480, 8, iFlags);
if (d == 0)
{
fprintf(stderr, "requested video mode not available\n");
// return -1;
}
gamescreen = SDL_SetVideoMode(640, 480, 8, iFlags);
if (gamescreen == NULL)
{
fprintf(stderr, "failed to set video mode: %s\n", SDL_GetError());
return -1;
}
SDL_WM_SetCaption( "OpenMortal", "OpenMortal" );
std::string sPath = std::string(DATADIR) + "/gfx/icon.png";
SDL_WM_SetIcon(IMG_Load(sPath.c_str()), NULL);
SDL_ShowCursor( SDL_DISABLE );
for ( int i=0; i<16; ++i ) { Colors[i].r *=4; Colors[i].g *=4; Colors[i].b *=4; }
SDL_SetColors( gamescreen, Colors, 256-16, 16 );
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;
fastFont = LoadBMPFont( "brandybun3.png" );
if ( !fastFont ) return -1;
creditsFont = LoadBMPFont( "CreditsFont2.png" );//"fangfont.png" );
if ( !creditsFont ) return -1;
storyFont = LoadBMPFont( "glossyfont.png" );
if ( !storyFont ) return -1;
return 0;
}
int init2()
{
if ( !g_oBackend.Construct() )
{
fprintf(stderr, "couldn't start backend.\n" );
return -1;
}
return 0;
}
int DrawMainScreen()
{
SDL_Surface* background = LoadBackground( "Mortal.png", 240 );
SDL_Rect r;
r.x = r.y = 0;
std::string sStaffFilename = DATADIR;
sStaffFilename += "/characters/STAFF.DAT";
RlePack pack( sStaffFilename.c_str(), 240 );
pack.ApplyPalette();
//SDL_SetColors( gamescreen, pack.getPalette(), 0, 240 );
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/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;
SDL_FreeSurface( background );
return retval;
}
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();
bool bDebug = false;
int iFlags = SDL_SWSURFACE | SDL_HWPALETTE;
if ( g_oState.m_bFullscreen )
{
iFlags |= SDL_FULLSCREEN;
}
int i;
for ( i=1; i<argc; ++i )
{
if ( !strcmp(argv[i], "-debug") )
{
bDebug = true;
}
else if ( !strcmp(argv[i], "-fullscreen") )
{
iFlags |= SDL_FULLSCREEN;
}
else if ( !strcmp(argv[i], "-hwsurface") )
{
iFlags |= SDL_HWSURFACE;
}
else if ( !strcmp(argv[i], "-doublebuf") )
{
iFlags |= SDL_DOUBLEBUF;
}
else if ( !strcmp(argv[i], "-anyformat") )
{
iFlags |= SDL_ANYFORMAT;
}
else
{
printf( "Usage: %s [-debug] [-fullscreen] [-hwsurface] [-doublebuf] [-anyformat]\n", argv[0] );
return 0;
}
}
if (init( iFlags )<0)
{
return -1;
}
g_oState.SetLanguage( g_oState.m_acLanguage );
new MszAudio;
- Audio->LoadMusic( "Last_Ninja_-_The_Wilderness.mid", "DemoMusic" );
+// Audio->LoadMusic( "Last_Ninja_-_The_Wilderness.mid", "DemoMusic" );
+ Audio->LoadMusic( "VUJICS.IT", "DemoMusic" );
Audio->PlayMusic( "DemoMusic" );
Audio->LoadMusic( "2nd_pm.s3m", "GameMusic" );
DrawMainScreen();
g_oPlayerSelect.SetPlayer( 0, ZOLI );
g_oPlayerSelect.SetPlayer( 1, SIRPI );
/*
int nextFighter = 0;
int describeOrder[ (int)LASTFIGHTER ];
for ( i=0; i<(int)LASTFIGHTER; ++i ) describeOrder[i] = i;
for ( i=0; i<100; ++i )
{
int j = rand() % (int)LASTFIGHTER;
int k = rand() % (int)LASTFIGHTER;
int l;
l = describeOrder[j];
describeOrder[j] = describeOrder[k];
describeOrder[k] = l;
}
*/
/*
{
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 );
}
}
*/
bool bNetworkGame = false;
while ( 1 )
{
if ( g_oState.m_bQuitFlag ) break;
DoDemos();
if ( g_oState.m_bQuitFlag ) break;
Audio->PlaySample( "car_start.voc" );
Audio->PlayMusic( "GameMusic" );
bNetworkGame = false;
while ( g_oState.m_enGameMode != SState::IN_DEMO
&& !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 )
{
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 );
GetKey();
}
if ( g_oState.m_bQuitFlag ) break;
Audio->PlayMusic( "DemoMusic" );
}
g_oState.Save();
SDL_Quit();
return EXIT_SUCCESS;
}
diff --git a/src/menu.cpp b/src/menu.cpp
index a5ad14d..3b58e74 100644
--- a/src/menu.cpp
+++ b/src/menu.cpp
@@ -1,1051 +1,1075 @@
/***************************************************************************
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 "menu.h"
#include "gfx.h"
#include "State.h"
#include "common.h"
#include "Audio.h"
#include "Backend.h"
#include "sge_tt_text.h"
#include "MortalNetwork.h"
#include <stdarg.h>
enum
{
/* Master menu structure:
MAIN MENU
*/
MENU_UNKNOWN,
MENU_SURRENDER,
MENU_SINGLE_PLAYER,
MENU_EASY,
MENU_MEDIUM,
MENU_HARD,
MENU_MULTI_PLAYER,
MENU_NETWORK_GAME,
MENU_SERVER,
MENU_HOSTNAME,
MENU_CONNECT,
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)
};
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", NULL };
const int g_piLanguage[] = { 0, 1, 2, 3 };
const char* g_ppcLanguageCodes[] = { "en", "es", "fr", "hu" };
const char* g_ppcServer[] = { "Connect to game", "Create game", NULL };
int g_piServer[] = { 0, 1 };
SDL_Surface* poBackground = NULL;
void InputKeys( int a_iPlayerNumber )
{
SDL_BlitSurface( poBackground, NULL, gamescreen, NULL );
DrawGradientText( "Input keys", titleFont, 20, 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];
int iY = 70;
for ( int i=0; i<9; ++i )
{
sprintf( acBuffer,
Translate("%s player-'%s'?"),
Translate(a_iPlayerNumber ? "Left" : "Right"),
Translate(apcKeyNames[i]) );
DrawTextMSZ( acBuffer, inkFont, 10, iY, UseShadow, C_WHITE, gamescreen );
SDLKey enKey = GetKey();
if ( SDLK_ESCAPE == enKey )
{
SDL_BlitSurface( poBackground, NULL, gamescreen, NULL );
SDL_Flip( gamescreen );
return;
}
g_oBackend.PerlEvalF( "GetKeysym(%d);", enKey );
DrawTextMSZ( g_oBackend.GetPerlString("keysym"), inkFont, 530, iY, UseShadow, C_WHITE, gamescreen );
g_oState.m_aiPlayerKeys[a_iPlayerNumber][i] = enKey;
iY += 35;
}
DrawTextMSZ( "Thanks!", inkFont, 320, iY + 20, UseShadow | AlignCenter, C_WHITE, gamescreen );
GetKey();
SDL_BlitSurface( poBackground, NULL, gamescreen, NULL );
SDL_Flip( gamescreen );
}
/***************************************************************************
NETWORK MENU DEFINITION
***************************************************************************/
static int g_iMessageY;
void MortalNetworkMessage( const char* format, ... )
{
char acBuffer[1024];
va_list ap;
va_start( ap, format );
vsnprintf( acBuffer, 1023, 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:
{
return true;
}
break;
} // switch statement
} // Polling events
return false;
}
class CNetworkMenu: public Menu
{
public:
CNetworkMenu(): Menu( "Network Play Setup" )
{
m_bOK = false;
m_bServer = g_oState.m_bServer;
m_sHostname = g_oState.m_acLatestServer;
+ AddMenuItem( "START NETWORK GAME!", SDLK_UNKNOWN, MENU_CONNECT );
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);
- AddMenuItem( "START NETWORK GAME!", SDLK_UNKNOWN, MENU_CONNECT );
MenuItem* poItem = AddMenuItem( "Cancel", SDLK_UNKNOWN, MENU_CANCEL );
SDL_Rect oRect;
oRect.x = gamescreen->w - 150; oRect.w = 150;
oRect.y = gamescreen->h - 50; oRect.h = 30;
poItem->SetPosition( oRect );
}
~CNetworkMenu() {}
void ItemActivated( int a_iItemCode, MenuItem* a_poMenuItem )
{
switch ( a_iItemCode )
{
case MENU_SERVER:
{
EnumMenuItem* poItem = (EnumMenuItem*) a_poMenuItem;
if ( m_bServer )
{
poItem->Decrement();
}
else
{
poItem->Increment();
}
break;
}
case MENU_CONNECT:
{
Clear();
Draw();
g_iMessageY = 260;
m_bOK = g_poNetwork->Start( m_bServer ? NULL : m_sHostname.c_str() );
if ( m_bOK )
{
g_oState.SetServer( m_bServer ? NULL : m_sHostname.c_str() );
g_oState.m_enGameMode = SState::IN_NETWORK;
m_bDone = true;
m_iReturnCode = 100;
}
else
{
const char* acError = g_poNetwork->GetLastError();
DrawTextMSZ( "Couldn't connect", inkFont, 320, g_iMessageY, AlignHCenter|UseShadow, C_LIGHTRED, gamescreen );
DrawTextMSZ( acError, impactFont, 320, g_iMessageY + 40, AlignHCenter|UseShadow, C_LIGHTRED, gamescreen, false );
}
-
+
+ if ( !g_oState.m_bQuitFlag )
+ {
+ if ( m_bOK )
+ {
+ for ( int i=0; i<10; ++i ) if ( MortalNetworkCheckKey() ) break;
+ }
+ else
+ {
+ GetKey();
+ }
+ Clear();
+ Draw();
+ }
+
if ( g_oState.m_bQuitFlag )
{
m_bDone = true;
m_iReturnCode = 100;
}
break;
}
case MENU_CANCEL:
m_bOK = false;
m_bDone = true;
m_iReturnCode = -1;
break;
case MENU_HOSTNAME:
Clear();
Draw();
char acBuffer[256];
strncpy( acBuffer, m_sHostname.c_str(), 255 );
acBuffer[255] = 0;
int x = DrawTextMSZ( "Server name: ", impactFont, 20, 270, 0, C_WHITE, gamescreen );
+
+ int iRetval;
+ {
+ SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL );
+ CReadline oReadline( gamescreen, impactFont, acBuffer, strlen(acBuffer), 255,
+ 20+x, 270 + sge_TTF_FontAscent(impactFont), 600, C_LIGHTCYAN, C_BLACK, 255 );
+ iRetval = oReadline.Execute();
+ }
- int iRetval = sge_tt_input( gamescreen, impactFont, acBuffer, SGE_IBG, strlen(acBuffer), 255,
- 20+x, 270 + sge_TTF_FontAscent(impactFont), C_LIGHTCYAN, C_BLACK, 255 );
- //DECLSPEC int sge_tt_input(SDL_Surface *screen,sge_TTFont *font,char *string,Uint8 flags, int pos,int len,Sint16 x,Sint16 y, Uint32 fcol, Uint32 bcol, int Alpha);
if ( iRetval == -1 )
{
m_bDone = true;
m_iReturnCode = 100;
g_oState.m_bQuitFlag = true;
}
if ( iRetval > 0 )
{
m_sHostname = acBuffer;
m_poServerMenuItem->SetValue( acBuffer );
}
Clear();
Draw();
break;
}
}
void ItemChanged( int a_iItemCode, int a_iValue, MenuItem* a_poMenuItem )
{
switch ( a_iItemCode )
{
case MENU_SERVER:
m_bServer = a_iValue;
m_poServerMenuItem->SetEnabled(!m_bServer);
break;
}
}
protected:
bool m_bOK;
bool m_bServer;
std::string m_sHostname;
TextMenuItem* m_poServerMenuItem;
};
/***************************************************************************
MENUITEM DEFINITION
***************************************************************************/
MenuItem::MenuItem( Menu* a_poMenu, const char* a_pcUtf8Text, int a_iCode )
: m_sUtf8Text( a_pcUtf8Text )
{
m_poMenu = a_poMenu;
m_iCode = a_iCode;
m_oPosition.x = m_oPosition.y = 100;
m_oPosition.w = m_oPosition.h = 100;
m_bCenter = true;
m_iHighColor = C_WHITE;
m_iLowColor = C_LIGHTGRAY;
m_iInactiveColor = C_DARKGRAY;
m_iBackgroundColor = C_BLACK;
m_bActive = false;
m_bEnabled = true;
}
MenuItem::~MenuItem()
{
}
void MenuItem::Draw()
{
if ( NULL != poBackground )
{
SDL_BlitSurface( poBackground, &m_oPosition, gamescreen, &m_oPosition );
}
else
{
SDL_FillRect( gamescreen, &m_oPosition, 0 );
}
int iX = m_oPosition.x;
int iY = m_oPosition.y;
if ( m_bCenter )
{
iX += m_oPosition.w / 2;
}
DrawTextMSZ( m_sUtf8Text.c_str(), inkFont, iX, iY,
UseTilde | UseShadow | (m_bCenter ? AlignHCenter : 0),
m_bEnabled ? (m_bActive ? m_iHighColor : m_iLowColor) : m_iInactiveColor,
gamescreen );
SDL_UpdateRect( gamescreen, m_oPosition.x, m_oPosition.y, m_oPosition.w, m_oPosition.h );
}
void MenuItem::Clear()
{
// debug( "Clear: %d:%d %dx%d\n", m_oPosition.x, m_oPosition.y, m_oPosition.w, m_oPosition.h );
if (poBackground )
{
SDL_Rect oDest = m_oPosition;
SDL_BlitSurface( poBackground, &m_oPosition, gamescreen, &oDest );
}
else
{
SDL_FillRect( gamescreen, &m_oPosition, C_WHITE );
}
-
-
+
SDL_UpdateRect( gamescreen, m_oPosition.x, m_oPosition.y, m_oPosition.w, m_oPosition.h );
}
void MenuItem::Activate()
{
if ( m_poMenu )
{
m_poMenu->ItemActivated( m_iCode, this );
}
}
void MenuItem::SetText( const char* a_pcUtf8Text, bool a_bCenter )
{
m_sUtf8Text = a_pcUtf8Text;
m_bCenter = a_bCenter;
Draw();
}
void MenuItem::SetPosition( const SDL_Rect& a_roPosition )
{
m_oPosition = a_roPosition;
}
void MenuItem::SetActive( bool a_bActive )
{
if ( m_bActive == a_bActive )
{
return;
}
m_bActive = a_bActive;
Draw();
}
void MenuItem::SetEnabled( bool a_bEnabled )
{
if ( m_bEnabled == a_bEnabled )
{
return;
}
m_bEnabled = a_bEnabled;
Draw();
}
/***************************************************************************
ENUMMENUITEM DEFINITION
***************************************************************************/
EnumMenuItem::EnumMenuItem( Menu* a_poMenu, int a_iInitialValue, const char* a_pcUtf8Text, int a_iCode )
: MenuItem( a_poMenu, a_pcUtf8Text, a_iCode )
{
m_sUtf8Title = a_pcUtf8Text;
m_iMax = -1;
m_iValue = a_iInitialValue;
}
EnumMenuItem::~EnumMenuItem()
{
}
void EnumMenuItem::Draw()
{
m_sUtf8Text = Translate( m_sUtf8Title.c_str() );
if ( m_iValue <= m_iMax )
{
m_sUtf8Text += Translate(m_ppcNames[m_iValue]);
}
if ( m_iValue > 0 )
{
m_sUtf8Text = "< " + m_sUtf8Text;
}
if ( m_iValue < m_iMax )
{
m_sUtf8Text += " >";
}
MenuItem::Draw();
}
void EnumMenuItem::Increment()
{
if ( m_iValue < m_iMax )
{
++m_iValue;
Draw();
m_poMenu->ItemChanged( m_iCode, m_piValues[m_iValue], this );
Audio->PlaySample( "ding.voc" );
}
}
void EnumMenuItem::Decrement()
{
if ( m_iValue > 0 )
{
--m_iValue;
Draw();
m_poMenu->ItemChanged( m_iCode, m_piValues[m_iValue], this );
Audio->PlaySample( "ding.voc" );
}
}
void EnumMenuItem::SetEnumValues( const char ** a_ppcNames, const int * a_piValues )
{
m_ppcNames = a_ppcNames;
m_piValues = a_piValues;
int i;
bool bFoundValue = false;
+
for ( i=0; NULL != a_ppcNames[i]; ++i )
{
if ( !bFoundValue &&
m_iValue == a_piValues[i] )
{
bFoundValue = true;
m_iValue = i;
}
}
if ( !bFoundValue )
{
m_iValue = 0;
}
m_iMax = i-1;
}
/***************************************************************************
TextMenuItem DEFINITION
***************************************************************************/
TextMenuItem::TextMenuItem( Menu* a_poMenu, const char* a_pcInitialValue, const char* a_pcUtf8Title, int a_iCode )
: MenuItem( a_poMenu, a_pcUtf8Title, a_iCode )
{
m_sTitle = a_pcUtf8Title;
m_sValue = a_pcInitialValue;
}
TextMenuItem::~TextMenuItem()
{
}
void TextMenuItem::Draw()
{
m_sUtf8Text = Translate( m_sTitle.c_str() );
m_sUtf8Text += m_sValue;
MenuItem::Draw();
}
void TextMenuItem::SetValue( const char* a_pcValue )
{
m_sValue = a_pcValue;
Draw();
}
/***************************************************************************
MENU DEFINITION
***************************************************************************/
Menu::Menu( const char* a_pcTitle )
: m_sTitle( a_pcTitle )
{
m_iCurrentItem = 0;
m_iReturnCode = -1;
m_bDone = false;
}
Menu::~Menu()
{
ItemIterator it;
+
for ( it = m_oItems.begin(); it != m_oItems.end(); ++it )
{
delete *it;
}
}
MenuItem* Menu::AddMenuItem( const char* a_pcUtf8Text, SDLKey a_tShortcut, int a_iCode )
{
MenuItem* poItem = new MenuItem( this, a_pcUtf8Text, a_iCode );
return AddMenuItem( poItem );
}
EnumMenuItem* Menu::AddEnumMenuItem( const char* a_pcUtf8Text, int a_iInitialValue,
const char** a_ppcNames, const int* a_piValues, int a_iCode )
{
EnumMenuItem* poItem = new EnumMenuItem( this, a_iInitialValue, a_pcUtf8Text, a_iCode );
poItem->SetEnumValues( a_ppcNames, a_piValues );
AddMenuItem( poItem );
return poItem;
}
TextMenuItem* Menu::AddTextMenuItem( const char* a_pcTitle, const char* a_pcValue, int a_iCode )
{
TextMenuItem* poItem = new TextMenuItem( this, a_pcValue, a_pcTitle, a_iCode );
AddMenuItem( poItem );
return poItem;
}
MenuItem* Menu::AddMenuItem( MenuItem* a_poItem )
{
m_oItems.push_back( a_poItem );
SDL_Rect oRect;
oRect.x = 0; oRect.w = gamescreen->w;
oRect.y = m_oItems.size() * 40 + 100;
oRect.h = 43;
a_poItem->SetPosition( oRect );
return a_poItem;
}
void Menu::AddOkCancel( int a_iOkCode )
{
SDL_Rect oRect;
oRect.x = 0; oRect.w = 150;
oRect.y = gamescreen->h - 50; oRect.h = 30;
MenuItem* poItem = AddMenuItem( "~OK", SDLK_o, a_iOkCode );
poItem->SetPosition( oRect );
// poItem = AddMenuItem( "Cancel", SDLK_UNKNOWN, 0 );
oRect.x = gamescreen->w - 150;
poItem->SetPosition( oRect );
}
void Menu::InvokeSubmenu( Menu* a_poMenu )
{
Audio->PlaySample( "strange_button.voc" );
Clear();
m_iReturnCode = a_poMenu->Run();
if ( g_oState.m_bQuitFlag )
{
m_iReturnCode = 100;
m_bDone = true;
}
if ( m_iReturnCode < 0 )
{
Audio->PlaySample( "pop.voc" );
Draw();
}
else
{
m_iReturnCode --;
m_bDone = true;
}
}
void Menu::ItemActivated( int a_iItemCode, MenuItem* a_poMenuItem )
{
debug( "Menu::ItemActivated( %d )\n", a_iItemCode );
switch ( a_iItemCode )
{
case MENU_QUIT:
m_bDone = true;
m_iReturnCode = 100;
g_oState.m_bQuitFlag = true;
break;
case MENU_SURRENDER:
m_bDone = true;
m_iReturnCode = 100;
g_oState.m_enGameMode = SState::IN_DEMO;
break;
case MENU_NETWORK_GAME:
{
Menu* poMenu = new CNetworkMenu();
InvokeSubmenu( poMenu );
delete poMenu;
break;
}
case MENU_MULTI_PLAYER:
m_bDone = true;
m_iReturnCode = 100;
g_oState.m_enGameMode = SState::IN_MULTI;
break;
case MENU_FULLSCREEN:
Audio->PlaySample( "strange_button.voc" );
g_oState.ToggleFullscreen();
if ( NULL != poBackground )
{
SDL_BlitSurface( poBackground, NULL, gamescreen, NULL );
}
else
{
SDL_FillRect( gamescreen, NULL, 0 );
}
a_poMenuItem->SetText( g_oState.m_bFullscreen ? "~FULLSCREEN ON" : "~FULLSCREEN OFF", true );
Draw();
break;
case MENU_OPTIONS_OK:
m_bDone = true;
m_iReturnCode = -1;
break;
case MENU_OPTIONS:
{
Menu* poMenu = new Menu( "Options" );
poMenu->AddEnumMenuItem( "GAME SPEED: ", g_oState.m_iGameSpeed, g_ppcGameSpeed, g_piGameSpeed, MENU_GAME_SPEED );
poMenu->AddEnumMenuItem( "GAME TIME: ", g_oState.m_iGameTime, g_ppcGameTime, g_piGameTime, MENU_GAME_TIME );
poMenu->AddEnumMenuItem( "STAMINA: ", g_oState.m_iHitPoints, g_ppcHitPoints, g_piHitPoints, MENU_TOTAL_HIT_POINTS );
poMenu->AddMenuItem( "~SOUND", SDLK_s, MENU_SOUND );
poMenu->AddMenuItem( g_oState.m_bFullscreen ? "~FULLSCREEN ON" : "~FULLSCREEN OFF", SDLK_f, MENU_FULLSCREEN );
poMenu->AddMenuItem( "~RIGHT PLAYER KEYS", SDLK_r, MENU_KEYS_RIGHT );
poMenu->AddMenuItem( "~LEFT PLAYER KEYS", SDLK_l, MENU_KEYS_LEFT );
poMenu->AddOkCancel( MENU_OPTIONS_OK );
InvokeSubmenu( poMenu );
delete poMenu;
break;
}
case MENU_SOUND:
{
Menu* poMenu = new Menu( "Sound" );
poMenu->AddEnumMenuItem( "CHANNELS: ", 1, g_ppcChannels, g_piChannels, MENU_CHANNELS )->SetEnabled(false);
poMenu->AddEnumMenuItem( "SOUND QUALITY: ", 2, g_ppcMixingRate, g_piMixingRate, MENU_MIXING_RATE )->SetEnabled(false);
poMenu->AddEnumMenuItem( "SOUND FIDELITY: ", 2, g_ppcMixingBits, g_piMixingBits, MENU_BITS )->SetEnabled(false);
poMenu->AddEnumMenuItem( "MUSIC VOLUME: ", g_oState.m_iMusicVolume, g_ppcVolume, g_piVolume, MENU_MUSIC_VOLUME );
poMenu->AddEnumMenuItem( "EFFECTS VOLUME: ", g_oState.m_iSoundVolume, g_ppcVolume, g_piVolume, MENU_SOUND_VOLUME );
poMenu->AddOkCancel( MENU_SOUND_OK );
InvokeSubmenu( poMenu );
delete poMenu;
break;
}
case MENU_SOUND_OK:
m_bDone = true;
m_iReturnCode = -1;
break;
case MENU_KEYS_LEFT:
InputKeys(1);
Draw();
break;
case MENU_KEYS_RIGHT:
InputKeys(0);
Draw();
break;
default:
break;
}
}
void Menu::ItemChanged( int a_iItemCode, int a_iValue, MenuItem* a_poMenuItem )
{
debug( "Menu::ItemChanged( %d, %d )\n", a_iItemCode, a_iValue );
switch ( a_iItemCode )
{
case MENU_MUSIC_VOLUME:
g_oState.m_iMusicVolume = a_iValue;
Audio->SetMusicVolume( a_iValue );
break;
case MENU_SOUND_VOLUME:
g_oState.m_iSoundVolume = a_iValue;
break;
case MENU_GAME_TIME:
g_oState.m_iGameTime = a_iValue;
break;
case MENU_GAME_SPEED:
g_oState.m_iGameSpeed = a_iValue;
break;
case MENU_TOTAL_HIT_POINTS:
g_oState.m_iHitPoints = a_iValue;
break;
case MENU_LANGUAGE:
g_oState.SetLanguage( g_ppcLanguageCodes[ a_iValue ] );
Clear();
Draw();
break;
} // end of switch a_iItemCode
}
/** Run executes the menus, maybe invoking submenus as well. The
menus modify the global game state.
Returns 0, or the number of parent menus that should be cleared. */
int Menu::Run()
{
if ( m_oItems[m_iCurrentItem]->GetEnabled() )
{
m_oItems[m_iCurrentItem]->SetActive(true);
}
else
{
FocusNext();
}
Draw();
while ( !m_bDone )
{
SDLKey enKey = GetKey();
if ( g_oState.m_bQuitFlag ||
SDLK_ESCAPE == enKey )
+
{
m_bDone = true;
m_iReturnCode = -1;
break;
}
switch ( enKey )
{
case SDLK_UP:
{
FocusPrev();
break;
} // end of SDLK_UP
case SDLK_DOWN:
{
FocusNext();
break;
} // end of SDLK_DOWN
case SDLK_LEFT:
{
MenuItem* poItem = m_oItems[m_iCurrentItem];
poItem->Decrement();
break;
}
case SDLK_RIGHT:
{
MenuItem* poItem = m_oItems[m_iCurrentItem];
poItem->Increment();
break;
}
+
case SDLK_RETURN:
{
MenuItem* poItem = m_oItems[m_iCurrentItem];
if ( poItem->GetEnabled() )
{
poItem->Activate();
}
}
default:
break;
} // end of switch
}
Clear();
return m_iReturnCode;
}
void Menu::Draw()
{
DrawGradientText( m_sTitle.c_str(), titleFont, 20, gamescreen );
for ( ItemIterator it=m_oItems.begin(); it!=m_oItems.end(); ++it )
{
(*it)->Draw();
}
SDL_Flip( gamescreen );
}
void Menu::FocusNext()
{
MenuItem* poItem = NULL;
int iNextItem;
for ( iNextItem = m_iCurrentItem+1; iNextItem < (int) m_oItems.size(); ++iNextItem )
{
poItem = m_oItems[iNextItem];
if ( poItem->GetEnabled() )
{
break;
}
poItem = NULL;
}
if ( NULL != poItem )
{
Audio->PlaySample("strange_quack.voc");
m_oItems[m_iCurrentItem]->SetActive(false);
m_oItems[iNextItem]->SetActive(true);
m_iCurrentItem = iNextItem;
}
}
void Menu::FocusPrev()
{
MenuItem* poItem = NULL;
+
int iPrevItem;
for ( iPrevItem = m_iCurrentItem-1; iPrevItem >= 0; --iPrevItem )
{
poItem = m_oItems[iPrevItem];
if ( poItem->GetEnabled() )
{
break;
}
poItem = NULL;
}
if ( NULL != poItem )
{
Audio->PlaySample("strange_quack.voc");
m_oItems[m_iCurrentItem]->SetActive(false);
m_oItems[iPrevItem]->SetActive(true);
m_iCurrentItem = iPrevItem;
}
}
void Menu::Clear()
{
if (poBackground)
{
SDL_BlitSurface( poBackground, 0, gamescreen, 0 );
}
else
{
SDL_FillRect( gamescreen, NULL, 0 );
}
}
void DoMenu( bool a_bDrawBackground )
{
Audio->PlaySample( "crashhh.voc" );
poBackground = SDL_ConvertSurface( gamescreen, gamescreen->format, SDL_SWSURFACE );
if ( NULL == poBackground )
{
debug( "DoMenu: Couldn't allocate background.\n" );
}
else
{
int i;
SDL_Rect oRect;
oRect.x = 0; oRect.w = poBackground->w; oRect.h = 1;
for ( i=0; i<poBackground->h; i += 2 )
{
oRect.y = i;
SDL_FillRect( poBackground, &oRect, C_BLACK );
}
oRect.w = 1; oRect.y = 0; oRect.h = poBackground->h;
for ( i=0; i<poBackground->w; i+=2 )
{
oRect.x = i;
SDL_FillRect(poBackground, &oRect, C_BLACK );
}
SDL_BlitSurface( poBackground, 0, gamescreen, 0 );
SDL_Flip( gamescreen );
}
Menu oMenu( "Main Menu" );
if ( SState::IN_DEMO == g_oState.m_enGameMode )
{
oMenu.AddMenuItem( "~SINGLE PLAYER GAME", SDLK_s, MENU_SINGLE_PLAYER )->SetEnabled(false);
oMenu.AddMenuItem( "~NETWORK GAME", SDLK_n, MENU_NETWORK_GAME );
oMenu.AddMenuItem( "~MULTI PLAYER GAME", SDLK_m, MENU_MULTI_PLAYER );
}
else
{
oMenu.AddMenuItem( "~SURRENDER GAME", SDLK_s, MENU_SURRENDER );
}
oMenu.AddEnumMenuItem( "~LANGUAGE: ", g_oState.m_iLanguageCode, g_ppcLanguage, g_piLanguage, MENU_LANGUAGE );
oMenu.AddMenuItem( "~OPTIONS", SDLK_o, MENU_OPTIONS );
oMenu.AddMenuItem( "~INFO", SDLK_i, MENU_INFO )->SetEnabled(false);
oMenu.AddMenuItem( "QUIT", SDLK_UNKNOWN, MENU_QUIT );
+
oMenu.Run();
if ( !g_oState.m_bQuitFlag )
{
Audio->PlaySample("shades_rollup.voc");
}
//SDL_BlitSurface( poBackground, 0, gamescreen, 0 );
//SDL_Flip( gamescreen );
SDL_FreeSurface( poBackground );
poBackground = NULL;
}
diff --git a/src/sge_surface.cpp b/src/sge_surface.cpp
index 5107226..ca979de 100644
--- a/src/sge_surface.cpp
+++ b/src/sge_surface.cpp
@@ -1,883 +1,900 @@
/*
* SDL Graphics Extension
* Pixel, surface and color functions
*
* Started 990815 (split from sge_draw 010611)
*
* License: LGPL v2+ (see the file LICENSE)
* (c)1999-2001 Anders Lindstr?m
*/
/*********************************************************************
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
*********************************************************************/
/*
* Some of this code is taken from the "Introduction to SDL" and
* John Garrison's PowerPak
*/
#include "SDL.h"
#include <math.h>
#include <string.h>
#include <stdarg.h>
#include "sge_surface.h"
/* Globals used for sge_Update/sge_Lock */
Uint8 _sge_update=1;
Uint8 _sge_lock=1;
/**********************************************************************************/
/** Misc. functions **/
/**********************************************************************************/
//==================================================================================
// Turns off automatic update (to avoid tearing).
//==================================================================================
void sge_Update_OFF(void)
{
_sge_update=0;
}
//==================================================================================
// Turns on automatic update (default)
//==================================================================================
void sge_Update_ON(void)
{
_sge_update=1;
}
//==================================================================================
// Turns off automatic locking of surfaces
//==================================================================================
void sge_Lock_OFF(void)
{
_sge_lock=0;
}
//==================================================================================
// Turns on automatic locking (default)
//==================================================================================
void sge_Lock_ON(void)
{
_sge_lock=1;
}
//==================================================================================
// Returns update&locking mode (1-on and 0-off)
//==================================================================================
Uint8 sge_getUpdate(void)
{
return _sge_update;
}
Uint8 sge_getLock(void)
{
return _sge_lock;
}
//==================================================================================
// SDL_UpdateRect does nothing if any part of the rectangle is outside the surface
// --- This version always work
//==================================================================================
void sge_UpdateRect(SDL_Surface *screen, Sint16 x, Sint16 y, Uint16 w, Uint16 h)
{
if(_sge_update!=1 || screen != SDL_GetVideoSurface()){return;}
if(x>=screen->w || y>=screen->h){return;}
Sint16 a,b;
a=w; b=h;
if(x < 0){x=0;}
if(y < 0){y=0;}
if(a+x > screen->w){a=screen->w-x;}
if(b+y > screen->h){b=screen->h-y;}
SDL_UpdateRect(screen,x,y,a,b);
}
//==================================================================================
// Creates a 32bit (8/8/8/8) alpha surface
// Map colors with sge_MapAlpha() and then use the Uint32 color versions of
// SGEs routines
//==================================================================================
SDL_Surface *sge_CreateAlphaSurface(Uint32 flags, int width, int height)
{
return SDL_CreateRGBSurface(flags,width,height,32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
}
//==================================================================================
// Returns the Uint32 color value for a 32bit (8/8/8/8) alpha surface
//==================================================================================
Uint32 sge_MapAlpha(Uint8 R, Uint8 G, Uint8 B, Uint8 A)
{
Uint32 color=0;
color|=R<<24;
color|=G<<16;
color|=B<<8;
color|=A;
return color;
}
//==================================================================================
// Sets an SDL error string
// Accepts formated argument - like printf()
// SDL_SetError() also does this, but it does not use standard syntax (why?)
//==================================================================================
void sge_SetError(const char *format, ...)
{
char buf[256];
va_list ap;
#ifdef __WIN32__
va_start((va_list*)ap, format); //Stupid w32 crosscompiler
#else
va_start(ap, format);
#endif
vsprintf(buf, format, ap);
va_end(ap);
SDL_SetError(buf);
}
/**********************************************************************************/
/** Pixel functions **/
/**********************************************************************************/
//==================================================================================
// Fast put pixel
//==================================================================================
void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
{
if(x>=sge_clip_xmin(surface) && x<=sge_clip_xmax(surface) && y>=sge_clip_ymin(surface) && y<=sge_clip_ymax(surface)){
switch (surface->format->BytesPerPixel) {
case 1: { /* Assuming 8-bpp */
*((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
}
break;
case 2: { /* Probably 15-bpp or 16-bpp */
*((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
}
break;
case 3: { /* Slow 24-bpp mode, usually not used */
Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
/* Gack - slow, but endian correct */
*(pix+surface->format->Rshift/8) = color>>surface->format->Rshift;
*(pix+surface->format->Gshift/8) = color>>surface->format->Gshift;
*(pix+surface->format->Bshift/8) = color>>surface->format->Bshift;
*(pix+surface->format->Ashift/8) = color>>surface->format->Ashift;
}
break;
case 4: { /* Probably 32-bpp */
*((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
}
break;
}
}
}
//==================================================================================
// Fast put pixel (RGB)
//==================================================================================
void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint8 R, Uint8 G, Uint8 B)
{
_PutPixel(surface,x,y, SDL_MapRGB(surface->format, R, G, B));
}
//==================================================================================
// Fastest put pixel functions (don't mess up indata, thank you)
//==================================================================================
void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
{
*((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
}
void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
{
*((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
}
void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
{
Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
/* Gack - slow, but endian correct */
*(pix+surface->format->Rshift/8) = color>>surface->format->Rshift;
*(pix+surface->format->Gshift/8) = color>>surface->format->Gshift;
*(pix+surface->format->Bshift/8) = color>>surface->format->Bshift;
*(pix+surface->format->Ashift/8) = color>>surface->format->Ashift;
}
void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
{
*((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
}
void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
{
switch ( dest->format->BytesPerPixel ) {
case 1:
*((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
break;
case 2:
*((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
break;
case 3:
_PutPixel24(dest,x,y,color);
break;
case 4:
*((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
break;
}
}
//==================================================================================
// Safe put pixel
//==================================================================================
void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
{
if ( SDL_MUSTLOCK(surface) && _sge_lock ) {
if ( SDL_LockSurface(surface) < 0 ) {
return;
}
}
_PutPixel(surface, x, y, color);
if ( SDL_MUSTLOCK(surface) && _sge_lock ) {
SDL_UnlockSurface(surface);
}
if(_sge_update!=1){return;}
sge_UpdateRect(surface, x, y, 1, 1);
}
//==================================================================================
// Safe put pixel (RGB)
//==================================================================================
void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint8 R, Uint8 G, Uint8 B)
{
sge_PutPixel(surface,x,y, SDL_MapRGB(surface->format, R, G, B));
}
//==================================================================================
// Calculate y pitch offset
// (the y pitch offset is constant for the same y coord and surface)
//==================================================================================
Sint32 sge_CalcYPitch(SDL_Surface *dest,Sint16 y)
{
if(y>=sge_clip_ymin(dest) && y<=sge_clip_ymax(dest)){
switch ( dest->format->BytesPerPixel ) {
case 1:
return y*dest->pitch;
break;
case 2:
return y*dest->pitch/2;
break;
case 3:
return y*dest->pitch;
break;
case 4:
return y*dest->pitch/4;
break;
}
}
return -1;
}
//==================================================================================
// Put pixel with precalculated y pitch offset
//==================================================================================
void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
{
if(x>=sge_clip_xmin(surface) && x<=sge_clip_xmax(surface) && ypitch>=0){
switch (surface->format->BytesPerPixel) {
case 1: { /* Assuming 8-bpp */
*((Uint8 *)surface->pixels + ypitch + x) = color;
}
break;
case 2: { /* Probably 15-bpp or 16-bpp */
*((Uint16 *)surface->pixels + ypitch + x) = color;
}
break;
case 3: { /* Slow 24-bpp mode, usually not used */
/* Gack - slow, but endian correct */
Uint8 *pix = (Uint8 *)surface->pixels + ypitch + x*3;
*(pix+surface->format->Rshift/8) = color>>surface->format->Rshift;
*(pix+surface->format->Gshift/8) = color>>surface->format->Gshift;
*(pix+surface->format->Bshift/8) = color>>surface->format->Bshift;
*(pix+surface->format->Ashift/8) = color>>surface->format->Ashift;
}
break;
case 4: { /* Probably 32-bpp */
*((Uint32 *)surface->pixels + ypitch + x) = color;
}
break;
}
}
}
//==================================================================================
// Get pixel
//==================================================================================
Uint32 sge_GetPixel(SDL_Surface *surface, Sint16 x, Sint16 y)
{
switch (surface->format->BytesPerPixel) {
case 1: { /* Assuming 8-bpp */
return *((Uint8 *)surface->pixels + y*surface->pitch + x);
}
break;
case 2: { /* Probably 15-bpp or 16-bpp */
return *((Uint16 *)surface->pixels + y*surface->pitch/2 + x);
}
break;
case 3: { /* Slow 24-bpp mode, usually not used */
Uint8 *pix;
int shift;
Uint32 color=0;
pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
shift = surface->format->Rshift;
color = *(pix+shift/8)<<shift;
shift = surface->format->Gshift;
color|= *(pix+shift/8)<<shift;
shift = surface->format->Bshift;
color|= *(pix+shift/8)<<shift;
shift = surface->format->Ashift;
color|= *(pix+shift/8)<<shift;
return color;
}
break;
case 4: { /* Probably 32-bpp */
return *((Uint32 *)surface->pixels + y*surface->pitch/4 + x);
}
break;
}
return 0;
}
//==================================================================================
// Put pixel with alpha blending
//==================================================================================
void _PutPixelAlpha(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color, Uint8 alpha)
{
if(x>=sge_clip_xmin(surface) && x<=sge_clip_xmax(surface) && y>=sge_clip_ymin(surface) && y<=sge_clip_ymax(surface)){
Uint32 Rmask = surface->format->Rmask, Gmask = surface->format->Gmask, Bmask = surface->format->Bmask, Amask = surface->format->Amask;
Uint32 R,G,B,A=0;
switch (surface->format->BytesPerPixel) {
case 1: { /* Assuming 8-bpp */
if( alpha == 255 ){
*((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
}else{
Uint8 *pixel = (Uint8 *)surface->pixels + y*surface->pitch + x;
Uint8 dR = surface->format->palette->colors[*pixel].r;
Uint8 dG = surface->format->palette->colors[*pixel].g;
Uint8 dB = surface->format->palette->colors[*pixel].b;
Uint8 sR = surface->format->palette->colors[color].r;
Uint8 sG = surface->format->palette->colors[color].g;
Uint8 sB = surface->format->palette->colors[color].b;
dR = dR + ((sR-dR)*alpha >> 8);
dG = dG + ((sG-dG)*alpha >> 8);
dB = dB + ((sB-dB)*alpha >> 8);
*pixel = SDL_MapRGB(surface->format, dR, dG, dB);
}
}
break;
case 2: { /* Probably 15-bpp or 16-bpp */
if( alpha == 255 ){
*((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
}else{
Uint16 *pixel = (Uint16 *)surface->pixels + y*surface->pitch/2 + x;
Uint32 dc = *pixel;
R = ((dc & Rmask) + (( (color & Rmask) - (dc & Rmask) ) * alpha >> 8)) & Rmask;
G = ((dc & Gmask) + (( (color & Gmask) - (dc & Gmask) ) * alpha >> 8)) & Gmask;
B = ((dc & Bmask) + (( (color & Bmask) - (dc & Bmask) ) * alpha >> 8)) & Bmask;
if( Amask )
A = ((dc & Amask) + (( (color & Amask) - (dc & Amask) ) * alpha >> 8)) & Amask;
*pixel= R | G | B | A;
}
}
break;
case 3: { /* Slow 24-bpp mode, usually not used */
Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
Uint8 rshift8=surface->format->Rshift/8;
Uint8 gshift8=surface->format->Gshift/8;
Uint8 bshift8=surface->format->Bshift/8;
Uint8 ashift8=surface->format->Ashift/8;
if( alpha == 255 ){
*(pix+rshift8) = color>>surface->format->Rshift;
*(pix+gshift8) = color>>surface->format->Gshift;
*(pix+bshift8) = color>>surface->format->Bshift;
*(pix+ashift8) = color>>surface->format->Ashift;
}else{
Uint8 dR, dG, dB, dA=0;
Uint8 sR, sG, sB, sA=0;
pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
dR = *((pix)+rshift8);
dG = *((pix)+gshift8);
dB = *((pix)+bshift8);
dA = *((pix)+ashift8);
sR = (color>>surface->format->Rshift)&0xff;
sG = (color>>surface->format->Gshift)&0xff;
sB = (color>>surface->format->Bshift)&0xff;
sA = (color>>surface->format->Ashift)&0xff;
dR = dR + ((sR-dR)*alpha >> 8);
dG = dG + ((sG-dG)*alpha >> 8);
dB = dB + ((sB-dB)*alpha >> 8);
dA = dA + ((sA-dA)*alpha >> 8);
*((pix)+rshift8) = dR;
*((pix)+gshift8) = dG;
*((pix)+bshift8) = dB;
*((pix)+ashift8) = dA;
}
}
break;
case 4: { /* Probably 32-bpp */
if( alpha == 255 ){
*((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
}else{
Uint32 *pixel = (Uint32 *)surface->pixels + y*surface->pitch/4 + x;
Uint32 dc = *pixel;
R = ((dc & Rmask) + (( (color & Rmask) - (dc & Rmask) ) * alpha >> 8)) & Rmask;
G = ((dc & Gmask) + (( (color & Gmask) - (dc & Gmask) ) * alpha >> 8)) & Gmask;
B = ((dc & Bmask) + (( (color & Bmask) - (dc & Bmask) ) * alpha >> 8)) & Bmask;
if( Amask )
A = ((dc & Amask) + (( (color & Amask) - (dc & Amask) ) * alpha >> 8)) & Amask;
*pixel = R | G | B | A;
}
}
break;
}
}
}
void sge_PutPixelAlpha(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color, Uint8 alpha)
{
if ( SDL_MUSTLOCK(surface) && _sge_lock )
if ( SDL_LockSurface(surface) < 0 )
return;
_PutPixelAlpha(surface,x,y,color,alpha);
/* unlock the display */
if (SDL_MUSTLOCK(surface) && _sge_lock) {
SDL_UnlockSurface(surface);
}
if(_sge_update!=1){return;}
sge_UpdateRect(surface, x, y, 1, 1);
}
void _PutPixelAlpha(SDL_Surface *surface, Sint16 x, Sint16 y, Uint8 R, Uint8 G, Uint8 B, Uint8 alpha)
{
_PutPixelAlpha(surface,x,y, SDL_MapRGB(surface->format, R, G, B),alpha);
}
void sge_PutPixelAlpha(SDL_Surface *surface, Sint16 x, Sint16 y, Uint8 R, Uint8 G, Uint8 B, Uint8 alpha)
{
sge_PutPixelAlpha(surface,x,y, SDL_MapRGB(surface->format, R, G, B), alpha);
}
/**********************************************************************************/
/** Block functions **/
/**********************************************************************************/
//==================================================================================
// The sge_write_block* functions copies the given block (a surface line) directly
// to the surface. This is *much* faster then using the put pixel functions to
// update a line. The block consist of Surface->w (the width of the surface) numbers
// of color values. Note the difference in byte size for the block elements for
// different color dephts. 24 bpp is slow and not included!
//==================================================================================
void sge_write_block8(SDL_Surface *Surface, Uint8 *block, Sint16 y)
{
memcpy( (Uint8 *)Surface->pixels + y*Surface->pitch, block, sizeof(Uint8)*Surface->w );
}
void sge_write_block16(SDL_Surface *Surface, Uint16 *block, Sint16 y)
{
memcpy( (Uint16 *)Surface->pixels + y*Surface->pitch/2, block, sizeof(Uint16)*Surface->w );
}
void sge_write_block32(SDL_Surface *Surface, Uint32 *block, Sint16 y)
{
memcpy( (Uint32 *)Surface->pixels + y*Surface->pitch/4, block, sizeof(Uint32)*Surface->w );
}
//==================================================================================
// ...and get
//==================================================================================
void sge_read_block8(SDL_Surface *Surface, Uint8 *block, Sint16 y)
{
memcpy( block,(Uint8 *)Surface->pixels + y*Surface->pitch, sizeof(Uint8)*Surface->w );
}
void sge_read_block16(SDL_Surface *Surface, Uint16 *block, Sint16 y)
{
memcpy( block,(Uint16 *)Surface->pixels + y*Surface->pitch/2, sizeof(Uint16)*Surface->w );
}
void sge_read_block32(SDL_Surface *Surface, Uint32 *block, Sint16 y)
{
memcpy( block,(Uint32 *)Surface->pixels + y*Surface->pitch/4, sizeof(Uint32)*Surface->w );
}
/**********************************************************************************/
/** Blitting/surface functions **/
/**********************************************************************************/
//==================================================================================
// Clear surface to color
//==================================================================================
void sge_ClearSurface(SDL_Surface *Surface, Uint32 color)
{
SDL_FillRect(Surface,NULL, color);
if(_sge_update!=1){return;}
SDL_UpdateRect(Surface, 0,0,0,0);
}
//==================================================================================
// Clear surface to color (RGB)
//==================================================================================
void sge_ClearSurface(SDL_Surface *Surface, Uint8 R, Uint8 G, Uint8 B)
{
sge_ClearSurface(Surface,SDL_MapRGB(Surface->format, R, G, B));
}
//==================================================================================
// Blit from one surface to another
// Warning! Alpha and color key is lost (=0) on Src surface
//==================================================================================
int sge_BlitTransparent(SDL_Surface *Src, SDL_Surface *Dest, Sint16 SrcX, Sint16 SrcY, Sint16 DestX, Sint16 DestY, Sint16 W, Sint16 H, Uint32 Clear, Uint8 Alpha)
{
SDL_Rect src, dest;
int ret;
/* Dest clipping */
#if SDL_VERSIONNUM(SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL) < \
SDL_VERSIONNUM(1, 1, 5)
int flag=0;
if (DestX < Dest->clip_minx){
SrcX += Dest->clip_minx-DestX;
W -= Dest->clip_minx-DestX-1;
DestX=Dest->clip_minx;
}
if (DestY < Dest->clip_miny){
SrcY +=Dest->clip_miny-DestY;
H -= Dest->clip_miny-DestY-1;
DestY=Dest->clip_miny;
}
if ((DestX + W) > Dest->clip_maxx){
W = W - ((DestX + W) - Dest->clip_maxx)+1;
if(W<=0){SDL_SetError("SGE - Blit error");return -1;}
}
if ((DestY + H) > Dest->clip_maxy){
H = H - ((DestY + H) - Dest->clip_maxy)+1;
if(H<=0){SDL_SetError("SGE - Blit error");return -1;}
}
#endif
/* Initialize our rectangles */
src.x = SrcX;
src.y = SrcY;
src.w = W;
src.h = H;
dest.x = DestX;
dest.y = DestY;
dest.w = W;
dest.h = H;
/* We don't care about src clipping, only dest! */
#if SDL_VERSIONNUM(SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL) < \
SDL_VERSIONNUM(1, 1, 5)
if ( (Src->flags & SDL_SRCCLIPPING) == SDL_SRCCLIPPING){
Src->flags &= ~SDL_SRCCLIPPING; flag=1;
}
#endif
/* Set the color to be transparent */
SDL_SetColorKey(Src, SDL_SRCCOLORKEY, Clear);
/* Set the alpha value */
SDL_SetAlpha(Src, SDL_SRCALPHA, Alpha);
/* Blit */
ret=SDL_BlitSurface(Src, &src, Dest, &dest);
/* Set the correct flag */
#if SDL_VERSIONNUM(SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL) < \
SDL_VERSIONNUM(1, 1, 5)
if (flag==1){
Src->flags |= SDL_SRCCLIPPING;
}
#endif
/* Set normal levels */
SDL_SetAlpha(Src,0,0);
SDL_SetColorKey(Src,0,0);
return ret;
}
//==================================================================================
// Blit from one surface to another (not touching alpha or color key -
// use SDL_SetColorKey and SDL_SetAlpha)
//==================================================================================
int sge_Blit(SDL_Surface *Src, SDL_Surface *Dest, Sint16 SrcX, Sint16 SrcY, Sint16 DestX, Sint16 DestY, Sint16 W, Sint16 H)
{
SDL_Rect src, dest;
int ret;
/* Dest clipping */
#if SDL_VERSIONNUM(SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL) < \
SDL_VERSIONNUM(1, 1, 5)
int flag=0;
if (DestX < Dest->clip_minx){
SrcX += Dest->clip_minx-DestX;
W -= Dest->clip_minx-DestX -1;
DestX=Dest->clip_minx;
}
if (DestY < Dest->clip_miny){
SrcY +=Dest->clip_miny-DestY;
H -= Dest->clip_miny-DestY -1;
DestY=Dest->clip_miny;
}
if ((DestX + W) > Dest->clip_maxx){
W = W - ((DestX + W) - Dest->clip_maxx)+1;
if(W<=0){SDL_SetError("SGE - Blit error");return -1;}
}
if ((DestY + H) > Dest->clip_maxy){
H = H - ((DestY + H) - Dest->clip_maxy)+1;
if(H<=0){SDL_SetError("SGE - Blit error");return -1;}
}
#endif
/* Initialize our rectangles */
src.x = SrcX;
src.y = SrcY;
src.w = W;
src.h = H;
dest.x = DestX;
dest.y = DestY;
dest.w = W;
dest.h = H;
/* We don't care about src clipping, only dest! */
#if SDL_VERSIONNUM(SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL) < \
SDL_VERSIONNUM(1, 1, 5)
if ( (Src->flags & SDL_SRCCLIPPING) == SDL_SRCCLIPPING){
Src->flags &= ~SDL_SRCCLIPPING; flag=1;
}
#endif
/* Blit */
ret=SDL_BlitSurface(Src, &src, Dest, &dest);
/* Set the correct flag */
#if SDL_VERSIONNUM(SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL) < \
SDL_VERSIONNUM(1, 1, 5)
if (flag==1){
Src->flags |= SDL_SRCCLIPPING;
}
#endif
return ret;
}
//==================================================================================
// Copies a surface to a new...
//==================================================================================
SDL_Surface *sge_copy_surface(SDL_Surface *src)
{
return SDL_ConvertSurface(src, src->format,SDL_SWSURFACE);
}
+SDL_Surface *sge_copy_surface(SDL_Surface *a_poSource, Sint16 x, Sint16 y, Sint16 w, Sint16 h)
+{
+ SDL_Surface* poDest;
+ poDest = SDL_CreateRGBSurface( SDL_SWSURFACE, w, h, a_poSource->format->BitsPerPixel,
+ a_poSource->format->Rmask, a_poSource->format->Gmask, a_poSource->format->Bmask, a_poSource->format->Amask );
+ if ( a_poSource->format->BitsPerPixel <= 8 )
+ {
+ SDL_SetColors( poDest, a_poSource->format->palette->colors, 0, a_poSource->format->palette->ncolors );
+ }
+
+ sge_Blit( a_poSource, poDest, x, y, 0, 0, w, h );
+ return poDest;
+}
+
+
+
+
/**********************************************************************************/
/** Palette functions **/
/**********************************************************************************/
//==================================================================================
// Fill in a palette entry with R, G, B componenets
//==================================================================================
SDL_Color sge_FillPaletteEntry(Uint8 R, Uint8 G, Uint8 B)
{
SDL_Color color;
color.r = R;
color.g = G;
color.b = B;
return color;
}
//==================================================================================
// Get the RGB of a color value
// Needed in those dark days before SDL 1.0
//==================================================================================
SDL_Color sge_GetRGB(SDL_Surface *Surface, Uint32 Color)
{
SDL_Color rgb;
SDL_GetRGB(Color, Surface->format, &(rgb.r), &(rgb.g), &(rgb.b));
return(rgb);
}
//==================================================================================
// Fades from (sR,sG,sB) to (dR,dG,dB), puts result in ctab[start] to ctab[stop]
//==================================================================================
void sge_Fader(SDL_Surface *Surface, Uint8 sR,Uint8 sG,Uint8 sB, Uint8 dR,Uint8 dG,Uint8 dB,Uint32 *ctab,int start, int stop)
{
// (sR,sG,sB) and (dR,dG,dB) are two points in space (the RGB cube).
/* The vector for the straight line */
int v[3];
v[0]=dR-sR; v[1]=dG-sG; v[2]=dB-sB;
/* Ref. point */
int x0=sR, y0=sG, z0=sB;
// The line's equation is:
// x= x0 + v[0] * t
// y= y0 + v[1] * t
// z= z0 + v[2] * t
//
// (x,y,z) will travel between the two points when t goes from 0 to 1.
int i=start;
double step=1.0/((stop+1)-start);
for(double t=0.0; t<=1.0 && i<=stop ; t+=step){
ctab[i++]=SDL_MapRGB(Surface->format, (Uint8)(x0+v[0]*t), (Uint8)(y0+v[1]*t), (Uint8)(z0+v[2]*t) );
}
}
//==================================================================================
// Fades from (sR,sG,sB,sA) to (dR,dG,dB,dA), puts result in ctab[start] to ctab[stop]
//==================================================================================
void sge_AlphaFader(Uint8 sR,Uint8 sG,Uint8 sB,Uint8 sA, Uint8 dR,Uint8 dG,Uint8 dB,Uint8 dA, Uint32 *ctab,int start, int stop)
{
// (sR,sG,sB,sA) and (dR,dG,dB,dA) are two points in hyperspace (the RGBA hypercube).
/* The vector for the straight line */
int v[4];
v[0]=dR-sR; v[1]=dG-sG; v[2]=dB-sB; v[3]=dA-sA;
/* Ref. point */
int x0=sR, y0=sG, z0=sB, w0=sA;
// The line's equation is:
// x= x0 + v[0] * t
// y= y0 + v[1] * t
// z= z0 + v[2] * t
// w= w0 + v[3] * t
//
// (x,y,z,w) will travel between the two points when t goes from 0 to 1.
int i=start;
double step=1.0/((stop+1)-start);
for(double t=0.0; t<=1.0 && i<=stop ; t+=step)
ctab[i++]=sge_MapAlpha((Uint8)(x0+v[0]*t), (Uint8)(y0+v[1]*t), (Uint8)(z0+v[2]*t), (Uint8)(w0+v[3]*t));
}
//==================================================================================
// Copies a nice rainbow palette to the color table (ctab[start] to ctab[stop]).
// You must also set the intensity of the palette (0-bright 255-dark)
//==================================================================================
void sge_SetupRainbowPalette(SDL_Surface *Surface,Uint32 *ctab,int intensity, int start, int stop)
{
int slice=(int)((stop-start)/6);
/* Red-Yellow */
sge_Fader(Surface, 255,intensity,intensity, 255,255,intensity, ctab, start,slice);
/* Yellow-Green */
sge_Fader(Surface, 255,255,intensity, intensity,255,intensity, ctab, slice+1, 2*slice);
/* Green-Turquoise blue */
sge_Fader(Surface, intensity,255,intensity, intensity,255,255, ctab, 2*slice+1, 3*slice);
/* Turquoise blue-Blue */
sge_Fader(Surface, intensity,255,255, intensity,intensity,255, ctab, 3*slice+1, 4*slice);
/* Blue-Purple */
sge_Fader(Surface, intensity,intensity,255, 255,intensity,255, ctab, 4*slice+1, 5*slice);
/* Purple-Red */
sge_Fader(Surface, 255,intensity,255, 255,intensity,intensity, ctab, 5*slice+1, stop);
}
//==================================================================================
// Copies a B&W palette to the color table (ctab[start] to ctab[stop]).
//==================================================================================
void sge_SetupBWPalette(SDL_Surface *Surface,Uint32 *ctab,int start, int stop)
{
sge_Fader(Surface, 0,0,0, 255,255,255, ctab,start,stop);
}
diff --git a/src/sge_surface.h b/src/sge_surface.h
index 36fee5e..df039d4 100644
--- a/src/sge_surface.h
+++ b/src/sge_surface.h
@@ -1,97 +1,98 @@
/*
* SDL Graphics Extension
* Pixel, surface and color functions (header)
*
* Started 990815 (split from sge_draw 010611)
*
* License: LGPL v2+ (see the file LICENSE)
* (c)1999-2001 Anders Lindstr?m
*/
/*********************************************************************
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
*********************************************************************/
#ifndef sge_surface_H
#define sge_surface_H
#include "SDL.h"
#include "sge_internal.h"
/*
* Obsolete function names
*/
#define sge_copy_sblock8 sge_write_block8
#define sge_copy_sblock16 sge_write_block16
#define sge_copy_sblock32 sge_write_block32
#define sge_get_sblock8 sge_read_block8
#define sge_get_sblock16 sge_read_block16
#define sge_get_sblock32 sge_read_block32
#ifdef _SGE_C
extern "C" {
#endif
DECLSPEC void sge_Update_OFF(void);
DECLSPEC void sge_Update_ON(void);
DECLSPEC void sge_Lock_OFF(void);
DECLSPEC void sge_Lock_ON(void);
DECLSPEC Uint8 sge_getUpdate(void);
DECLSPEC Uint8 sge_getLock(void);
DECLSPEC void sge_UpdateRect(SDL_Surface *screen, Sint16 x, Sint16 y, Uint16 w, Uint16 h);
DECLSPEC SDL_Surface *sge_CreateAlphaSurface(Uint32 flags, int width, int height);
DECLSPEC Uint32 sge_MapAlpha(Uint8 R, Uint8 G, Uint8 B, Uint8 A);
DECLSPEC void sge_SetError(const char *format, ...);
DECLSPEC void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color);
DECLSPEC void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color);
DECLSPEC void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color);
DECLSPEC void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color);
DECLSPEC void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color);
DECLSPEC void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color);
DECLSPEC Sint32 sge_CalcYPitch(SDL_Surface *dest,Sint16 y);
DECLSPEC void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color);
DECLSPEC void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color);
DECLSPEC Uint32 sge_GetPixel(SDL_Surface *surface, Sint16 x, Sint16 y);
DECLSPEC void _PutPixelAlpha(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color, Uint8 alpha);
DECLSPEC void sge_PutPixelAlpha(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color, Uint8 alpha);
DECLSPEC void sge_write_block8(SDL_Surface *Surface, Uint8 *block, Sint16 y);
DECLSPEC void sge_write_block16(SDL_Surface *Surface, Uint16 *block, Sint16 y);
DECLSPEC void sge_write_block32(SDL_Surface *Surface, Uint32 *block, Sint16 y);
DECLSPEC void sge_read_block8(SDL_Surface *Surface, Uint8 *block, Sint16 y);
DECLSPEC void sge_read_block16(SDL_Surface *Surface, Uint16 *block, Sint16 y);
DECLSPEC void sge_read_block32(SDL_Surface *Surface, Uint32 *block, Sint16 y);
DECLSPEC void sge_ClearSurface(SDL_Surface *Surface, Uint32 color);
DECLSPEC int sge_BlitTransparent(SDL_Surface *Src, SDL_Surface *Dest, Sint16 SrcX, Sint16 SrcY, Sint16 DestX, Sint16 DestY, Sint16 W, Sint16 H, Uint32 Clear, Uint8 Alpha);
DECLSPEC int sge_Blit(SDL_Surface *Src, SDL_Surface *Dest, Sint16 SrcX, Sint16 SrcY, Sint16 DestX, Sint16 DestY, Sint16 W, Sint16 H);
DECLSPEC SDL_Surface *sge_copy_surface(SDL_Surface *src);
+DECLSPEC SDL_Surface *sge_copy_surface(SDL_Surface *src, Sint16 SrcX, Sint16 SrcY, Sint16 W, Sint16 H);
DECLSPEC SDL_Color sge_GetRGB(SDL_Surface *Surface, Uint32 Color);
DECLSPEC SDL_Color sge_FillPaletteEntry (Uint8 R, Uint8 G, Uint8 B);
DECLSPEC void sge_Fader(SDL_Surface *Surface, Uint8 sR,Uint8 sG,Uint8 sB, Uint8 dR,Uint8 dG,Uint8 dB,Uint32 *ctab,int start, int stop);
DECLSPEC void sge_AlphaFader(Uint8 sR,Uint8 sG,Uint8 sB,Uint8 sA, Uint8 dR,Uint8 dG,Uint8 dB,Uint8 dA, Uint32 *ctab,int start, int stop);
DECLSPEC void sge_SetupRainbowPalette(SDL_Surface *Surface,Uint32 *ctab,int intensity, int start, int stop);
DECLSPEC void sge_SetupBWPalette(SDL_Surface *Surface,Uint32 *ctab,int start, int stop);
#ifdef _SGE_C
}
#endif
#ifndef sge_C_ONLY
DECLSPEC void _PutPixel(SDL_Surface *screen, Sint16 x, Sint16 y, Uint8 R, Uint8 G, Uint8 B);
DECLSPEC void sge_PutPixel(SDL_Surface *screen, Sint16 x, Sint16 y, Uint8 R, Uint8 G, Uint8 B);
DECLSPEC void _PutPixelAlpha(SDL_Surface *surface, Sint16 x, Sint16 y, Uint8 R, Uint8 G, Uint8 B, Uint8 alpha);
DECLSPEC void sge_PutPixelAlpha(SDL_Surface *surface, Sint16 x, Sint16 y, Uint8 R, Uint8 G, Uint8 B, Uint8 alpha);
DECLSPEC void sge_ClearSurface(SDL_Surface *Surface, Uint8 R, Uint8 G, Uint8 B);
#endif /* sge_C_ONLY */
#endif /* sge_surface_H */
diff --git a/src/sge_tt_text.cpp b/src/sge_tt_text.cpp
index f6336b3..61dce6a 100644
--- a/src/sge_tt_text.cpp
+++ b/src/sge_tt_text.cpp
@@ -1,1470 +1,1760 @@
/*
* SDL Graphics Extension
* Text/TrueType font functions
*
* Started 990815
*
* License: LGPL v2+ (see the file LICENSE)
* (c)1999-2001 Anders Lindstr?m
*
* Uses the excellent FreeType 2 library, available at:
* http://www.freetype.org/
*/
/*********************************************************************
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
*********************************************************************/
/*
* Most of this code is taken from the SDL ttf lib by Sam Lantinga
* <slouken@devolution.com>
*/
#include "SDL.h"
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <string.h>
#include <stdarg.h>
#include <math.h>
#include "sge_surface.h"
#include "sge_primitives.h"
#include "sge_tt_text.h"
#ifndef _SGE_NOTTF
#include <ft2build.h>
#include <freetype/freetype.h>
#include <freetype/ftoutln.h>
#include <freetype/ttnameid.h>
/* The structure used to hold glyph information (cached) */
struct glyph {
int stored;
FT_UInt index;
//FT_Bitmap bitmap;
FT_Bitmap pixmap;
int minx;
int maxx;
int miny;
int maxy;
int yoffset;
int advance;
Uint16 cached;
};
/* the truetype font structure */
struct _sge_TTFont{
FT_Face face;
/* Font metrics */
int height;
int ascent;
int descent;
int lineskip;
/* The font style */
Uint8 style;
/* Extra width in glyph bounds for text styles */
int glyph_overhang;
float glyph_italics;
/* Information in the font for underlining */
int underline_offset;
int underline_height;
/* For now, support Latin-1 character set caching */
glyph *current;
glyph cache[256];
glyph scratch;
};
/* Macro to convert a character to a Unicode value -- assume already Unicode */
//Should really make a proper convert algorithm someday
#define UNICODE(c) c
/* FIXME: Right now we assume the gray-scale renderer Freetype is using
supports 256 shades of gray, but we should instead key off of num_grays
in the result FT_Bitmap after the FT_Render_Glyph() call. */
#define NUM_GRAYS 256
/* Handy routines for converting from fixed point */
#define FT_FLOOR(X) ((X & -64) / 64)
#define FT_CEIL(X) (((X + 63) & -64) / 64)
#define CACHED_METRICS 0x10
#define CACHED_BITMAP 0x01
#define CACHED_PIXMAP 0x02
/* The FreeType font engine/library */
static FT_Library _sge_library;
static int _sge_TTF_initialized = 0;
Uint8 _sge_TTF_AA=1; //Rendering mode: 0-OFF, 1-AA, 2-Alpha
/**********************************************************************************/
/** Open/misc font functions **/
/**********************************************************************************/
//==================================================================================
// Turns TTF AntiAliasing On/Off or alpha (nice but slow) (Default: On)
//==================================================================================
void sge_TTF_AAOff(void)
{
_sge_TTF_AA=0;
}
void sge_TTF_AAOn(void)
{
_sge_TTF_AA=1;
}
void sge_TTF_AA_Alpha(void)
{
_sge_TTF_AA=2;
}
//==================================================================================
// Closes the ttf engine, done by exit
//==================================================================================
void sge_TTF_Quit(void)
{
if ( _sge_TTF_initialized ) {
FT_Done_FreeType( _sge_library );
}
_sge_TTF_initialized = 0;
}
//==================================================================================
// Starts the ttf engine, must be called first
//==================================================================================
int sge_TTF_Init(void)
{
FT_Error error;
error = FT_Init_FreeType( &_sge_library );
if ( error ) {
SDL_SetError("SGE - Couldn't init FreeType engine");
return(-1);
} else {
_sge_TTF_initialized = 1;
}
atexit(sge_TTF_Quit); //dont't trust the user...
return(0);
}
//==================================================================================
// Some helper functions
//==================================================================================
void Flush_Glyph(glyph *glyph)
{
glyph->stored = 0;
glyph->index = 0;
//if( glyph->bitmap.buffer ) {
// free( glyph->bitmap.buffer );
// glyph->bitmap.buffer = 0;
//}
if( glyph->pixmap.buffer ) {
free( glyph->pixmap.buffer );
glyph->pixmap.buffer = 0;
}
glyph->cached = 0;
}
void Flush_Cache(sge_TTFont *font)
{
int i;
int size = sizeof( font->cache ) / sizeof( font->cache[0] );
for( i = 0; i < size; ++i ) {
if( font->cache[i].cached ) {
Flush_Glyph( &font->cache[i] );
}
}
if( font->scratch.cached ) {
Flush_Glyph( &font->scratch );
}
}
//==================================================================================
// Remove font from memory
//==================================================================================
void sge_TTF_CloseFont(sge_TTFont *font)
{
Flush_Cache( font );
FT_Done_Face( font->face );
free( font );
}
//==================================================================================
// Open the TT font file and returns the font with pt size
//==================================================================================
sge_TTFont *sge_TTF_OpenFont(const char *file, int ptsize)
{
sge_TTFont *font;
FT_Error error;
FT_Face face;
FT_Fixed scale;
font = (sge_TTFont *)malloc(sizeof(*font));
if ( font == NULL ) {
SDL_SetError("SGE - Out of memory");
return(NULL);
}
memset(font, 0, sizeof(*font));
/* Open the font and create ancillary data */
error = FT_New_Face( _sge_library, file, 0, &font->face );
if ( error ) {
sge_SetError("SGE - Couldn't load font file: %s",file);
free(font);
return(NULL);
}
face = font->face;
/* Make sure that our font face is scalable (global metrics) */
if ( ! FT_IS_SCALABLE(face) ) {
sge_SetError("SGE - Font face is not scalable: %s",file);
sge_TTF_CloseFont( font );
return NULL;
}
/* Set the character size and use 96 DPI */
error = FT_Set_Char_Size( font->face, 0, ptsize * 64, 96, 96 );
//error = FT_Set_Pixel_Sizes( font->face, 0, ptsize );
if( error ) {
sge_SetError("SGE - Couldn't set font size: %s",file);
sge_TTF_CloseFont( font );
return NULL;
}
/* Get the scalable font metrics for this font */
scale = face->size->metrics.y_scale;
font->ascent = FT_CEIL(FT_MulFix(face->bbox.yMax, scale));
font->descent = FT_CEIL(FT_MulFix(face->bbox.yMin, scale));
font->height = font->ascent - font->descent + /* baseline */ 1;
font->lineskip = FT_CEIL(FT_MulFix(face->height, scale));
font->underline_offset = FT_FLOOR(FT_MulFix(face->underline_position, scale));
font->underline_height = FT_FLOOR(FT_MulFix(face->underline_thickness, scale));
if ( font->underline_height < 1 ) {
font->underline_height = 1;
}
/* Set the default font style */
font->style = SGE_TTF_NORMAL;
font->glyph_overhang = face->size->metrics.y_ppem / 10;
/* x offset = cos(((90.0-12)/360)*2*M_PI), or 12 degree angle */
font->glyph_italics = 0.207f;
font->glyph_italics *= font->height;
return font;
}
//==================================================================================
// Load a glyph
//==================================================================================
FT_Error Load_Glyph(sge_TTFont *font, Uint16 ch, glyph *cached, int want )
{
FT_Face face;
FT_Error error;
FT_GlyphSlot glyph;
FT_Glyph_Metrics* metrics;
FT_Outline* outline;
//assert( font );
//assert( font->face );
face = font->face;
/* Load the glyph */
if ( ! cached->index ) {
cached->index = FT_Get_Char_Index( face, ch );
}
error = FT_Load_Glyph( face, cached->index, FT_LOAD_DEFAULT );
if( error ) {
return error;
}
/* Get our glyph shortcuts */
glyph = face->glyph;
metrics = &glyph->metrics;
outline = &glyph->outline;
/* Get the glyph metrics if desired */
if ( (want & CACHED_METRICS) && !(cached->stored & CACHED_METRICS) ) {
/* Get the bounding box */
cached->minx = FT_FLOOR(metrics->horiBearingX);
cached->maxx = cached->minx + FT_CEIL(metrics->width);
cached->maxy = FT_FLOOR(metrics->horiBearingY);
cached->miny = cached->maxy - FT_CEIL(metrics->height);
cached->yoffset = font->ascent - cached->maxy;
cached->advance = FT_CEIL(metrics->horiAdvance);
/* Adjust for bold and italic text */
if ( font->style & SGE_TTF_BOLD ) {
cached->maxx += font->glyph_overhang;
}
if ( font->style & SGE_TTF_ITALIC ) {
cached->maxx += (int)ceil(font->glyph_italics);
}
cached->stored |= CACHED_METRICS;
}
if ( ((want & CACHED_BITMAP) && !(cached->stored & CACHED_BITMAP)) ||
((want & CACHED_PIXMAP) && !(cached->stored & CACHED_PIXMAP)) ) {
//int mono = (want & CACHED_BITMAP);
int i;
FT_Bitmap* src;
FT_Bitmap* dst;
/* Handle the italic style */
if( font->style & SGE_TTF_ITALIC ) {
FT_Matrix shear;
shear.xx = 1 << 16;
shear.xy = (int) ( font->glyph_italics * ( 1 << 16 ) ) / font->height;
shear.yx = 0;
shear.yy = 1 << 16;
FT_Outline_Transform( outline, &shear );
}
/* Render the glyph */
//if ( mono ) {
// error = FT_Render_Glyph( glyph, ft_render_mode_mono );
//} else {
error = FT_Render_Glyph( glyph, ft_render_mode_normal );
//}
if( error ) {
return error;
}
/* Copy over information to cache */
src = &glyph->bitmap;
//if ( mono ) {
// dst = &cached->bitmap;
//} else {
dst = &cached->pixmap;
//}
memcpy( dst, src, sizeof( *dst ) );
//if ( mono ) {
// dst->pitch *= 8;
//}
/* Adjust for bold and italic text */
if( font->style & SGE_TTF_BOLD ) {
int bump = font->glyph_overhang;
dst->pitch += bump;
dst->width += bump;
}
if( font->style & SGE_TTF_ITALIC ) {
int bump = (int)ceil(font->glyph_italics);
dst->pitch += bump;
dst->width += bump;
}
dst->buffer = (unsigned char *)malloc( dst->pitch * dst->rows );
if( !dst->buffer ) {
return FT_Err_Out_Of_Memory;
}
memset( dst->buffer, 0, dst->pitch * dst->rows );
for( i = 0; i < src->rows; i++ ) {
int soffset = i * src->pitch;
int doffset = i * dst->pitch;
/*if ( mono ) {
unsigned char *srcp = src->buffer + soffset;
unsigned char *dstp = dst->buffer + doffset;
int j;
for ( j = 0; j < src->width; j += 8 ) {
unsigned char ch = *srcp++;
*dstp++ = (ch&0x80) >> 7;
ch <<= 1;
*dstp++ = (ch&0x80) >> 7;
ch <<= 1;
*dstp++ = (ch&0x80) >> 7;
ch <<= 1;
*dstp++ = (ch&0x80) >> 7;
ch <<= 1;
*dstp++ = (ch&0x80) >> 7;
ch <<= 1;
*dstp++ = (ch&0x80) >> 7;
ch <<= 1;
*dstp++ = (ch&0x80) >> 7;
ch <<= 1;
*dstp++ = (ch&0x80) >> 7;
}
} else {*/
memcpy(dst->buffer+doffset,
src->buffer+soffset, src->pitch);
//}
}
/* Handle the bold style */
if ( font->style & SGE_TTF_BOLD ) {
int row;
int col;
int offset;
int pixel;
Uint8* pixmap;
/* The pixmap is a little hard, we have to add and clamp */
for( row = dst->rows - 1; row >= 0; --row ) {
pixmap = (Uint8*) dst->buffer + row * dst->pitch;
for( offset=1; offset <= font->glyph_overhang; ++offset ) {
for( col = dst->width - 1; col > 0; --col ) {
pixel = (pixmap[col] + pixmap[col-1]);
if( pixel > NUM_GRAYS - 1 ) {
pixel = NUM_GRAYS - 1;
}
pixmap[col] = (Uint8) pixel;
}
}
}
}
/* Mark that we rendered this format */
//if ( mono ) {
// cached->stored |= CACHED_BITMAP;
//} else {
cached->stored |= CACHED_PIXMAP;
//}
}
/* We're done, mark this glyph cached */
cached->cached = ch;
return 0;
}
//==================================================================================
// Find glyph
//==================================================================================
FT_Error Find_Glyph(sge_TTFont *font, Uint16 ch, int want)
{
int retval = 0;
if( ch < 256 ) {
font->current = &font->cache[ch];
} else {
if ( font->scratch.cached != ch ) {
Flush_Glyph( &font->scratch );
}
font->current = &font->scratch;
}
if ( (font->current->stored & want) != want ) {
retval = Load_Glyph( font, ch, font->current, want );
}
return retval;
}
//==================================================================================
// Change the size of font
//==================================================================================
int sge_TTF_SetFontSize(sge_TTFont *font, int ptsize)
{
FT_Error error;
FT_Fixed scale;
FT_Face face;
/* Set the character size and use 96 DPI */
error = FT_Set_Char_Size( font->face, 0, ptsize * 64, 96, 96 );
//error = FT_Set_Pixel_Sizes( font->face, 0, ptsize );
if( error ) {
sge_SetError("SGE - Couldn't set font size");
sge_TTF_CloseFont( font );
return -1;
}
Flush_Cache(font);
face = font->face;
/* Get the scalable font metrics for this font */
scale = face->size->metrics.y_scale;
font->ascent = FT_CEIL(FT_MulFix(face->bbox.yMax, scale));
font->descent = FT_CEIL(FT_MulFix(face->bbox.yMin, scale));
font->height = font->ascent - font->descent + /* baseline */ 1;
font->lineskip = FT_CEIL(FT_MulFix(face->height, scale));
font->underline_offset = FT_FLOOR(FT_MulFix(face->underline_position, scale));
font->underline_height = FT_FLOOR(FT_MulFix(face->underline_thickness, scale));
if ( font->underline_height < 1 ) {
font->underline_height = 1;
}
/* Set the default font style */
//font->style = SGE_TTF_NORMAL;
font->glyph_overhang = face->size->metrics.y_ppem / 10;
/* x offset = cos(((90.0-12)/360)*2*M_PI), or 12 degree angle */
font->glyph_italics = 0.207f;
font->glyph_italics *= font->height;
return 0;
}
//==================================================================================
// Get font geometrics
//==================================================================================
int sge_TTF_FontHeight(sge_TTFont *font)
{
return(font->height);
}
int sge_TTF_FontAscent(sge_TTFont *font)
{
return(font->ascent);
}
int sge_TTF_FontDescent(sge_TTFont *font)
{
return(font->descent);
}
int sge_TTF_FontLineSkip(sge_TTFont *font)
{
return(font->lineskip);
}
int sge_TTF_GlyphMetrics(sge_TTFont *font, Uint16 ch, int* minx, int* maxx, int* miny, int* maxy, int* advance)
{
FT_Error error;
error = Find_Glyph(font, ch, CACHED_METRICS);
if ( error ) {
return -1;
}
if ( minx ) {
*minx = font->current->minx;
}
if ( maxx ) {
*maxx = font->current->maxx;
}
if ( miny ) {
*miny = font->current->miny;
}
if ( maxy ) {
*maxy = font->current->maxy;
}
if ( advance ) {
*advance = font->current->advance;
}
return 0;
}
//==================================================================================
// Set font style
//==================================================================================
void sge_TTF_SetFontStyle(sge_TTFont *font, Uint8 style)
{
font->style = style;
Flush_Cache(font);
}
//==================================================================================
// Get font style
//==================================================================================
Uint8 sge_TTF_GetFontStyle(sge_TTFont *font)
{
return(font->style);
}
#endif /* _SGE_NOTTF */
//==================================================================================
// Convert the Latin-1 text to UNICODE
//==================================================================================
Uint16 *ASCII_to_UNICODE(Uint16 *unicode, const char *text, int len)
{
int i;
for ( i=0; i < len; ++i ) {
unicode[i] = ((const unsigned char *)text)[i];
}
unicode[i] = 0;
return unicode;
}
Uint16 *sge_Latin1_Uni(const char *text)
{
Uint16 *unicode_text;
int i, unicode_len;
/* Copy the Latin-1 text to a UNICODE text buffer */
unicode_len = strlen(text);
unicode_text = (Uint16 *)malloc((unicode_len+1)*(sizeof *unicode_text));
if ( unicode_text == NULL ) {
SDL_SetError("SGE - Out of memory");
return(NULL);
}
for ( i=0; i < unicode_len; ++i ) {
unicode_text[i] = ((const unsigned char *)text)[i];
}
unicode_text[i] = 0;
return(unicode_text);
}
//==================================================================================
// Convert the UTF-8 text to UNICODE
//==================================================================================
Uint16 *UTF8_to_UNICODE(Uint16 *unicode, const char *utf8, int len)
{
int i, j;
Uint16 ch;
for ( i=0, j=0; i < len; ++i, ++j ) {
ch = ((const unsigned char *)utf8)[i];
if ( ch >= 0xF0 ) {
ch = (Uint16)(utf8[i]&0x07) << 18;
ch |= (Uint16)(utf8[++i]&0x3F) << 12;
ch |= (Uint16)(utf8[++i]&0x3F) << 6;
ch |= (Uint16)(utf8[++i]&0x3F);
} else
if ( ch >= 0xE0 ) {
ch = (Uint16)(utf8[i]&0x3F) << 12;
ch |= (Uint16)(utf8[++i]&0x3F) << 6;
ch |= (Uint16)(utf8[++i]&0x3F);
} else
if ( ch >= 0xC0 ) {
ch = (Uint16)(utf8[i]&0x3F) << 6;
ch |= (Uint16)(utf8[++i]&0x3F);
}
unicode[j] = ch;
}
unicode[j] = 0;
return unicode;
}
Uint16 *sge_UTF8_Uni(const char *text)
{
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 *unicode_text));
if ( unicode_text == NULL ) {
SDL_SetError("SGE - Out of memory");
return(NULL);
}
return UTF8_to_UNICODE(unicode_text, text, unicode_len);
}
#ifndef _SGE_NOTTF
//==================================================================================
// Get the width of the text with the given font
//==================================================================================
SDL_Rect sge_TTF_TextSizeUNI(sge_TTFont *font, const Uint16 *text)
{
SDL_Rect ret; ret.x=0; ret.y=0, ret.w=0, ret.h=0;
const Uint16 *ch;
int x, z;
int minx, maxx;
int miny, maxy;
glyph *glyph;
FT_Error error;
/* Initialize everything to 0 */
if ( ! _sge_TTF_initialized ) {
return ret;
}
minx = miny = 0;
maxx = maxy = 0;
/* Load each character and sum it's bounding box */
x= 0;
for ( ch=text; *ch; ++ch ) {
error = Find_Glyph(font, *ch, CACHED_METRICS);
if ( error ) {
return ret;
}
glyph = font->current;
z = x + glyph->minx;
if ( minx > z ) {
minx = z;
}
if ( font->style & SGE_TTF_BOLD ) {
x += font->glyph_overhang;
}
if ( glyph->advance > glyph->maxx ) {
z = x + glyph->advance;
} else {
z = x + glyph->maxx;
}
if ( maxx < z ) {
maxx = z;
}
x += glyph->advance;
if ( glyph->miny < miny ) {
miny = glyph->miny;
}
if ( glyph->maxy > maxy ) {
maxy = glyph->maxy;
}
}
/* Fill the bounds rectangle */
ret.w = (maxx - minx);
//ret.h = (maxy - miny); /* This is correct, but breaks many applications */
ret.h = font->height;
return ret;
}
SDL_Rect sge_TTF_TextSize(sge_TTFont *font, const char *text)
{
SDL_Rect ret; ret.x=ret.y=ret.w=ret.y=0;
Uint16 *unicode_text;
int unicode_len;
/* Copy the Latin-1 text to a UNICODE text buffer */
unicode_len = strlen(text);
unicode_text = (Uint16 *)malloc((unicode_len+1)*(sizeof *unicode_text));
if ( unicode_text == NULL ) {
SDL_SetError("SGE - Out of memory");
return ret;
}
ASCII_to_UNICODE(unicode_text, text, unicode_len);
/* Render the new text */
ret = sge_TTF_TextSizeUNI(font, unicode_text);
/* Free the text buffer and return */
free(unicode_text);
return ret;
}
/**********************************************************************************/
/** TTF output functions **/
/**********************************************************************************/
//==================================================================================
// TT Render (unicode)
// Returns an 8bit or 32bit(8/8/8/8-alpha) surface with TT text
//==================================================================================
SDL_Surface *sge_TTF_RenderUNICODE(sge_TTFont *font,const Uint16 *text, SDL_Color fg, SDL_Color bg)
{
int xstart, width;
int w, h;
SDL_Surface *textbuf;
SDL_Palette *palette;
int index;
int rdiff, gdiff, bdiff;
const Uint16 *ch;
Uint8 *src, *dst;
Uint32 *dst32;
Uint32 alpha=0;
Uint32 pixel=0;
Uint32 Rmask=0, Gmask=0, Bmask=0, Amask=0;
int row, col;
FT_Error error;
/* Get the dimensions of the text surface */
SDL_Rect ret=sge_TTF_TextSizeUNI(font, text);
w=ret.w; h=ret.h;
if ( !w ) {
SDL_SetError("SGE - Text has zero width");
return(NULL);
}
/* Create the target surface */
width = w;
if(_sge_TTF_AA!=2) /* Allocate an 8-bit pixmap */
textbuf = SDL_AllocSurface(SDL_SWSURFACE, w, h, 8, 0, 0, 0, 0);
else{ /* Allocate an 32-bit alpha pixmap */
if ( SDL_BYTEORDER == SDL_LIL_ENDIAN ) {
Rmask = 0x000000FF;
Gmask = 0x0000FF00;
Bmask = 0x00FF0000;
Amask = 0xFF000000;
} else {
Rmask = 0xFF000000;
Gmask = 0x00FF0000;
Bmask = 0x0000FF00;
Amask = 0x000000FF;
}
textbuf = SDL_AllocSurface(SDL_SWSURFACE, w, h, 32, Rmask, Gmask, Bmask, Amask);
}
if ( textbuf == NULL ) {
SDL_SetError("SGE - Out of memory");
return(NULL);
}
/* Setup our colors */
switch(_sge_TTF_AA){
case 0:{ /* No fancy antialiasing or alpha component */
palette = textbuf->format->palette;
palette->colors[0].r = bg.r;
palette->colors[0].g = bg.g;
palette->colors[0].b = bg.b;
palette->colors[1].r = fg.r;
palette->colors[1].g = fg.g;
palette->colors[1].b = fg.b;
}
break;
case 1:{ /* Fill the palette with NUM_GRAYS levels of shading from bg to fg */
palette = textbuf->format->palette;
rdiff = fg.r - bg.r;
gdiff = fg.g - bg.g;
bdiff = fg.b - bg.b;
for ( index=0; index< NUM_GRAYS; ++index ) {
palette->colors[index].r = bg.r + (index*rdiff)/(NUM_GRAYS-1);
palette->colors[index].g = bg.g + (index*gdiff)/(NUM_GRAYS-1);
palette->colors[index].b = bg.b + (index*bdiff)/(NUM_GRAYS-1);
}
}
break;
case 2:{ /* Alpha component magic */
sge_ClearSurface(textbuf, SDL_MapRGBA(textbuf->format, bg.r,bg.g,bg.b,SDL_ALPHA_TRANSPARENT));
//pixel = (fg.r<<16)|(fg.g<<8)|fg.b;
pixel = (fg.b<<16)|(fg.g<<8)|fg.r;
}
break;
}
/* Load and render each character */
xstart = 0;
for ( ch=text; *ch; ++ch ) {
error = Find_Glyph(font, *ch, CACHED_METRICS|CACHED_PIXMAP);
if ( ! error ) {
w = font->current->pixmap.width;
src = (Uint8 *)font->current->pixmap.buffer;
for ( row = 0; row < font->current->pixmap.rows; ++row ) {
dst = (Uint8 *)textbuf->pixels + (row + font->current->yoffset)* textbuf->pitch + xstart + font->current->minx;
switch(_sge_TTF_AA){
case 0:{ /* Normal */
src = font->current->pixmap.buffer + row * font->current->pixmap.pitch;
for ( col=w; col>0; --col ) {
*dst++ |= (*src++<NUM_GRAYS/2)? 0:1;
}
}
break;
case 1:{ /* Antialiasing */
src = font->current->pixmap.buffer + row * font->current->pixmap.pitch;
for ( col=w; col>0; --col ) {
*dst++ |= *src++;
}
}
break;
case 2:{ /* Alpha */
dst32 = (Uint32 *)textbuf->pixels + (row + font->current->yoffset)* textbuf->pitch/4 + xstart + font->current->minx;
for ( col=w; col>0; --col ) {
alpha = *src++;
*dst32++ |= pixel | (alpha << 24);
}
}
break;
}
}
xstart += font->current->advance;
if ( font->style & SGE_TTF_BOLD ) {
xstart += font->glyph_overhang;
}
}
}
/* Handle the underline style */
if ( font->style & SGE_TTF_UNDERLINE ) {
int row_offset;
row_offset = font->ascent - font->underline_offset - 1;
if ( row_offset > textbuf->h ) {
row_offset = (textbuf->h-1) - font->underline_height;
}
if(_sge_TTF_AA==0){
dst = (Uint8 *)textbuf->pixels + row_offset * textbuf->pitch;
for ( row=font->underline_height; row>0; --row ) {
memset(dst, 1, textbuf->w );
dst += textbuf->pitch;
}
}else if(_sge_TTF_AA==1){
dst = (Uint8 *)textbuf->pixels + row_offset * textbuf->pitch;
for ( row=font->underline_height; row>0; --row ) {
memset(dst, NUM_GRAYS - 1, textbuf->w );
dst += textbuf->pitch;
}
}else{
pixel |= Amask;
dst32 = (Uint32 *)textbuf->pixels+row_offset*textbuf->pitch/4;
for ( row=font->underline_height; row>0; --row ) {
for ( col=0; col < textbuf->w; ++col ) {
dst32[col] = pixel;
}
dst32 += textbuf->pitch/4;
}
}
}
return(textbuf);
}
//==================================================================================
// Renders the Unicode string to TrueType on surface, with the color fcolor.
// bcolor is the target color for the antialiasing.
// Alpha sets the transparency of the text (255-solid, 0-max).
//==================================================================================
SDL_Rect sge_tt_textout_UNI(SDL_Surface *Surface, sge_TTFont *font, const Uint16 *uni, Sint16 x, Sint16 y, Uint32 fcolor, Uint32 bcolor, int Alpha)
{
SDL_Rect ret; ret.x=0; ret.y=0; ret.w=0; ret.h=0;
SDL_Color temp;
SDL_Surface *text;
text=sge_TTF_RenderUNICODE(font,uni,sge_GetRGB(Surface,fcolor),sge_GetRGB(Surface,bcolor));
if(text==NULL){return ret;}
/* Align the surface text to the baseline */
Uint16 ascent=font->ascent;
temp=sge_GetRGB(Surface,bcolor);
sge_BlitTransparent(text,Surface,0,0,x,y-ascent,text->w,text->h,SDL_MapRGB(text->format,temp.r,temp.g,temp.b),Alpha);
sge_UpdateRect(Surface,x,y-ascent,text->w,text->h);
ret.x=x; ret.y=y-ascent; ret.w=text->w; ret.h=text->h;
SDL_FreeSurface(text);
return ret;
}
//==================================================================================
// Renders the Unicode string to TrueType on surface, with the color fcolor.
// bcolor is the target color for the antialiasing.
// Alpha sets the transparency of the text (0-solid, 255-max). (RGB)
//==================================================================================
SDL_Rect sge_tt_textout_UNI(SDL_Surface *Surface, sge_TTFont *font, const Uint16 *uni, Sint16 x, Sint16 y, Uint8 fR, Uint8 fG, Uint8 fB, Uint8 bR, Uint8 bG, Uint8 bB, int Alpha)
{
SDL_Rect ret; ret.x=0; ret.y=0; ret.w=0; ret.h=0;
SDL_Surface *text;
text=sge_TTF_RenderUNICODE(font,uni,sge_FillPaletteEntry(fR,fG,fB),sge_FillPaletteEntry(bR,bG,bB));
if(text==NULL){return ret;}
/* Align the surface text to the baseline */
Uint16 ascent=font->ascent;
sge_BlitTransparent(text,Surface,0,0,x,y-ascent,text->w,text->h,SDL_MapRGB(text->format,bR,bG,bB),Alpha);
sge_UpdateRect(Surface,x,y-ascent,text->w,text->h);
ret.x=x; ret.y=y-ascent; ret.w=text->w; ret.h=text->h;
SDL_FreeSurface(text);
return ret;
}
//==================================================================================
// Renders the Latin-1 string to TrueType on surface, with the color fcolor.
// bcolor is the target color for the antialiasing.
// Alpha sets the transparency of the text (0-solid, 255-max).
//==================================================================================
SDL_Rect sge_tt_textout(SDL_Surface *Surface, sge_TTFont *font, const char *string, Sint16 x, Sint16 y, Uint32 fcolor, Uint32 bcolor, int Alpha)
{
SDL_Rect ret;
Uint16 *uni;
uni=sge_Latin1_Uni(string);
ret=sge_tt_textout_UNI(Surface,font,uni,x,y,fcolor,bcolor,Alpha);
free(uni);
return ret;
}
//==================================================================================
// Renders the Latin-1 string to TrueType on surface, with the color fcolor.
// bcolor is the target color for the antialiasing.
// Alpha sets the transparency of the text (0-solid, 255-max). (RGB)
//==================================================================================
SDL_Rect sge_tt_textout(SDL_Surface *Surface, sge_TTFont *font, const char *string, Sint16 x, Sint16 y, Uint8 fR, Uint8 fG, Uint8 fB, Uint8 bR, Uint8 bG, Uint8 bB, int Alpha)
{
SDL_Rect ret;
Uint16 *uni;
uni=sge_Latin1_Uni(string);
ret=sge_tt_textout_UNI(Surface,font,uni,x,y, fR,fG,fB, bR,bG,bB, Alpha);
free(uni);
return ret;
}
//==================================================================================
// Renders the UTF-8 string to TrueType on surface, with the color fcolor.
// bcolor is the target color for the antialiasing.
// Alpha sets the transparency of the text (0-solid, 255-max).
//==================================================================================
SDL_Rect sge_tt_textout_UTF8(SDL_Surface *Surface, sge_TTFont *font, const char *string, Sint16 x, Sint16 y, Uint32 fcolor, Uint32 bcolor, int Alpha)
{
SDL_Rect ret;
Uint16 *uni;
uni=sge_UTF8_Uni(string);
ret=sge_tt_textout_UNI(Surface,font,uni,x,y,fcolor,bcolor,Alpha);
free(uni);
return ret;
}
//==================================================================================
// Renders the UTF-8 string to TrueType on surface, with the color fcolor.
// bcolor is the target color for the antialiasing.
// Alpha sets the transparency of the text (0-solid, 255-max). (RGB)
//==================================================================================
SDL_Rect sge_tt_textout_UTF8(SDL_Surface *Surface, sge_TTFont *font, const char *string, Sint16 x, Sint16 y, Uint8 fR, Uint8 fG, Uint8 fB, Uint8 bR, Uint8 bG, Uint8 bB, int Alpha)
{
SDL_Rect ret;
Uint16 *uni;
uni=sge_UTF8_Uni(string);
ret=sge_tt_textout_UNI(Surface,font,uni,x,y, fR,fG,fB, bR,bG,bB, Alpha);
free(uni);
return ret;
}
//==================================================================================
// Renders the formatet Latin-1 string to TrueType on surface, with the color fcolor.
// bcolor is the target color for the antialiasing.
// Alpha sets the transparency of the text (0-solid, 255-max). (RGB ONLY)
// * just like printf(char *format,...) *
//==================================================================================
SDL_Rect sge_tt_textoutf(SDL_Surface *Surface, sge_TTFont *font, Sint16 x, Sint16 y, Uint8 fR, Uint8 fG, Uint8 fB, Uint8 bR, Uint8 bG, Uint8 bB, int Alpha ,const char *format,...)
{
char buf[256];
va_list ap;
#ifdef __WIN32__
va_start((va_list*)ap, format); //Stupid win32 crosscompiler
#else
va_start(ap, format);
#endif
vsprintf(buf, format, ap);
va_end(ap);
return sge_tt_textout(Surface, font, buf, x,y, fR,fG,fB, bR,bG,bB, Alpha);
}
/**********************************************************************************/
/** TTF 'input' functions **/
/**********************************************************************************/
// First some internel functions for TTF input
//==================================================================================
// Fast update function for TTF input
//
// type=0 - in ret smaller then out ret
// type=1 - in ret bigger then out ret
// type=3 - safe
//==================================================================================
SDL_Rect fast_update(SDL_Surface *Surface,SDL_Surface *buffer,SDL_Rect ret, int type,sge_TTFont *font,Uint16 *string, Sint16 x,Sint16 y, Uint32 fcol, Uint32 bcol,int Alpha)
{
if(type==0){
sge_Update_OFF();
sge_FilledRect(Surface, ret.x, ret.y, ret.x+ret.w, ret.y+ret.h, bcol);
ret=sge_tt_textout_UNI(Surface,font,string, x,y, fcol, bcol, 0);
sge_Update_ON();
sge_UpdateRect(Surface, ret.x, ret.y, ret.w, ret.h);
}
else if(type==1){
SDL_Rect temp;
sge_Update_OFF();
sge_FilledRect(Surface, ret.x, ret.y, ret.x+ret.w, ret.y+ret.h, bcol);
temp=sge_tt_textout_UNI(Surface,font,string, x,y, fcol, bcol, 0);
sge_Update_ON();
sge_UpdateRect(Surface, ret.x, ret.y, ret.w, ret.h);
ret=temp;
}
else{
SDL_Rect temp;
sge_Update_OFF();
sge_FilledRect(Surface, ret.x, ret.y, ret.x+ret.w, ret.y+ret.h, bcol);
temp=sge_tt_textout_UNI(Surface,font,string, x,y, fcol, bcol, 0);
sge_Update_ON();
if(ret.w>=temp.w){
sge_UpdateRect(Surface, ret.x, ret.y, ret.w, ret.h);
}
else{
sge_UpdateRect(Surface, temp.x, temp.y, temp.w, temp.h);
}
ret=temp;
}
return ret;
}
//==================================================================================
// Update function for TTF input that preserve background
//
// type=0 - in ret smaller then out ret
// type=1 - in ret bigger then out ret
// type=3 - safe
//==================================================================================
SDL_Rect nice_update(SDL_Surface *Surface,SDL_Surface *buffer,SDL_Rect ret, int type,sge_TTFont *font,Uint16 *string, Sint16 x,Sint16 y, Uint32 fcol, Uint32 bcol, int Alpha)
{
if(type==0){
sge_Update_OFF();
sge_Blit(buffer,Surface, ret.x, ret.y, ret.x, ret.y, ret.w, ret.h);
ret=sge_tt_textout_UNI(Surface,font,string, x,y, fcol, bcol, Alpha);
sge_Update_ON();
sge_UpdateRect(Surface, ret.x, ret.y, ret.w, ret.h);
}
else if(type==1){
SDL_Rect temp;
sge_Update_OFF();
sge_Blit(buffer,Surface, ret.x, ret.y, ret.x, ret.y, ret.w, ret.h);
temp=sge_tt_textout_UNI(Surface,font,string, x,y, fcol, bcol, Alpha);
sge_Update_ON();
sge_UpdateRect(Surface, ret.x, ret.y, ret.w, ret.h);
ret=temp;
}
else{
SDL_Rect temp;
sge_Update_OFF();
sge_Blit(buffer,Surface, ret.x, ret.y, ret.x, ret.y, ret.w, ret.h);
temp=sge_tt_textout_UNI(Surface,font,string, x,y, fcol, bcol, Alpha);
sge_Update_ON();
if(ret.w>=temp.w){
sge_UpdateRect(Surface, ret.x, ret.y, ret.w, ret.h);
}
else{
sge_UpdateRect(Surface, temp.x, temp.y, temp.w, temp.h);
}
ret=temp;
}
return ret;
}
#endif /* _SGE_NOTTF */
//==================================================================================
// Handle keyrepeats
//==================================================================================
int keyrepeat(SDL_Event *event, int wait)
{
int keydown=1,c=0,ret=0;
SDL_Event ev;
do{
if(SDL_PollEvent(&ev)==1){
if(ev.type==SDL_QUIT){ret=-1;keydown=0;}
if(ev.type==SDL_KEYUP || ev.type==SDL_KEYDOWN){ //Keyrepeat cancelled
keydown=0;
if(ev.type==SDL_KEYDOWN){
SDL_PeepEvents(&ev,1, SDL_ADDEVENT, 0); //Return the newly pressed key to the event queue
}
}
}
SDL_Delay(10);
c++;
if(c>wait && keydown==1){ //trigers keyrepeat
ret=1;
SDL_PeepEvents(event,1, SDL_ADDEVENT, 0); //Return the old key to the event queue
keydown=0;
}
}while(keydown==1);
return ret;
}
//==================================================================================
// Insert a element
//==================================================================================
void insert_char(Uint16 *string, Uint16 ch, int pos, int max)
{
if(pos>max || pos<0){return;}
else if(pos==max){string[pos]=ch;}
else{
for(int i=max; i>=pos; i--){
string[i+1]=string[i];
}
string[pos]=ch;
}
}
//==================================================================================
// Delete a element
//==================================================================================
void delete_char(Uint16 *string, int pos, int max)
{
if(pos>max || pos<0){return;}
else if(pos==max){string[pos]=0;}
else{
for(int i=pos; i<=max-1; i++){
string[i]=string[i+1];
}
string[max]=0;
}
}
#ifndef _SGE_NOTTF
// These functions handle keyboard input and shows the result on screen. The text
// can be edited with [Backspace], [Delete], [Left arrow] and [Right arrow].
// Text input is terminated when [Return] or [Enter] is pressed, or if a quit event
// is recived.
// The sge_tt_input functions puts the result in 'string'.
//
// Flags: SGE_IBG - Keeps background, else bye bye background
// SGE_IDEL - Delete text on exit
// SGE_INOKR - No keyrepeat
// (use OR | to give more than one)
//
// If you want a 'default' text you can copy it to string before call and set pos to
// the first empty element in string - ex. "Hello" => pos=5. If not - zero.
// len is the max numbers of chars editable - ex. if you set the default text to "100"
// and only want 3 digits out, set len to 3. string should have atleast len+1 elements
// allocated
// This is *not* a fast, optimized function - but it gets the job done...
// Return:
// Zero or above - the lenght of the string
// -1 recieved a quit event (the lenght is lost)
// -2 invalid indata
// -3 out of memory
//==================================================================================
// Text input UNICODE (the core)
//==================================================================================
int sge_tt_input_UNI(SDL_Surface *screen,sge_TTFont *font,Uint16 *string, Uint8 flags, int pos,int len,Sint16 x,Sint16 y, Uint32 fcol, Uint32 bcol, int Alpha)
{
if(len<pos || pos<0 || len<0){return -2;}
Uint16 cur=124;//The charactar for the cursor - '|'
int max; //The strings size
/* Set update function */
SDL_Rect (*_update)(SDL_Surface *screen,SDL_Surface *buffer,SDL_Rect ret, int type,sge_TTFont *font,Uint16 *string, Sint16 x,Sint16 y, Uint32 fcol, Uint32 bcol, int Alpha);
SDL_Surface *buffer;
if(flags&SGE_FLAG1){ /* Keep background? */
_update=nice_update;
buffer=SDL_DisplayFormat(screen); /* Fixme: Yum! Memory! */
if(buffer==NULL){SDL_SetError("SGE - Out of memory");return -3;}
}
else{ /* nope */
_update=fast_update;
buffer=NULL;
Alpha=0;
}
SDL_Rect ret;
max=pos;
string[pos+1]=0;
SDL_EnableUNICODE(1);
/* Init cursor */
string[pos]=cur;
ret=sge_tt_textout_UNI(screen,font,string, x,y, fcol, bcol, Alpha);
SDL_Event event;
int quit=0;
do{
/* Check events */
SDL_WaitEvent(&event);
if(event.type==SDL_QUIT){quit=-1;}
else if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_ESCAPE){quit=1;}
else if(event.type==SDL_KEYDOWN && (event.key.keysym.sym==SDLK_RETURN || event.key.keysym.sym==SDLK_KP_ENTER)){quit=1;}
else if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_BACKSPACE){
if(pos>0){
/* Delete char cursor-1 */
delete_char(string,pos-1,max); pos--; max--;
ret=_update(screen,buffer,ret,1,font,string, x,y, fcol, bcol, Alpha);
/* Handle keyrepeat */
if(!(flags&SGE_FLAG3))
if(keyrepeat(&event, 20)==-1){quit=-1;}
}
}
else if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_RIGHT){
if(pos!=max && pos!=len){
/* Move cursor right */
delete_char(string,pos,max);pos++;
insert_char(string,cur,pos,max);
ret=_update(screen,buffer,ret,3,font,string, x,y, fcol, bcol, Alpha);
/* Handle keyrepeat */
if(!(flags&SGE_FLAG3))
if(keyrepeat(&event, 20)==-1){quit=-1;}
}
}
else if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_LEFT){
if(pos>0){
/* Move cursor left */
delete_char(string,pos,max);pos--;
insert_char(string,cur,pos,max);
ret=_update(screen,buffer,ret,3,font,string, x,y, fcol, bcol, Alpha);
/* Handle keyrepeat */
if(!(flags&SGE_FLAG3))
if(keyrepeat(&event, 20)==-1){quit=-1;}
}
}
else if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_DELETE){
/* Delete char cursor+1 */
if(pos!=max && pos!=len){
delete_char(string,pos+1,max);max--;
ret=_update(screen,buffer,ret,1,font,string, x,y, fcol, bcol, Alpha);
/* Handle keyrepeat */
if(!(flags&SGE_FLAG3))
if(keyrepeat(&event, 20)==-1){quit=-1;}
}
}
else if(event.type==SDL_KEYDOWN && event.key.keysym.unicode!=0){
/* Insert new char */
if(max!=len){
max++;
insert_char(string, event.key.keysym.unicode, pos,max); pos++;
ret=_update(screen,buffer,ret,0,font,string, x,y, fcol, bcol, Alpha);
/* Handle keyrepeat */
if(!(flags&SGE_FLAG3))
if(keyrepeat(&event, 40)==-1){quit=-1;}
}
}
}while(quit==0);
/* Remove the cursor from string */
delete_char(string,pos,max);
if(flags&SGE_FLAG2){ //Remove the text
if(flags&SGE_FLAG1){
sge_Blit(buffer,screen, ret.x, ret.y, ret.x, ret.y, ret.w, ret.h);
sge_UpdateRect(screen, ret.x, ret.y, ret.w, ret.h);
}
else{
sge_FilledRect(screen,ret.x, ret.y, ret.x+ret.w, ret.y+ret.h,bcol);
}
}
else{ //Draw text without cursor
ret=_update(screen,buffer,ret,1,font,string, x,y, fcol, bcol, Alpha);
}
if(flags&SGE_FLAG1){SDL_FreeSurface(buffer);}
if(quit==-1){return -1;} //Waaa! The user killed me!
return max;
}
+
+
+CReadline::CReadline( SDL_Surface *a_poScreen, sge_TTFont *a_poFont,
+ char *a_pcString, int a_iPos, int a_iLen,
+ int a_x, int a_y, int a_w, Uint32 a_iFCol, Uint32 a_iBCol, int a_iAlpha )
+{
+ m_iResult = 0;
+ m_poScreen = a_poScreen;
+ m_poFont = a_poFont;
+ m_piString = 0;
+ m_iLen = -1;
+ w = a_w;
+ x = a_x;
+ y = a_y;
+
+ SDL_EnableUNICODE(1);
+
+ // Create background copy.
+
+ m_oWorkArea.x = x;
+ m_oWorkArea.y = y - sge_TTF_FontAscent(m_poFont);
+ m_oWorkArea.w = w;
+ m_oWorkArea.h = sge_TTF_FontHeight(a_poFont);
+
+ m_poBackground = sge_copy_surface( a_poScreen, m_oWorkArea.x, m_oWorkArea.y, m_oWorkArea.w, m_oWorkArea.h );
+
+ Restart( a_pcString, a_iPos, a_iLen, a_iFCol, a_iBCol, a_iAlpha );
+}
+
+
+void CReadline::Restart( char *a_pcString, int a_iPos, int a_iLen,
+ Uint32 a_iFCol, Uint32 a_iBCol, int a_iAlpha )
+{
+ if ( NULL == m_piString
+ || m_iLen <= a_iLen )
+ {
+ if ( m_piString ) delete[] m_piString;
+ m_iLen = a_iLen;
+ m_piString = new Uint16[a_iLen+2];
+ }
+
+ m_iResult = 0;
+
+ m_pcLatin1String = a_pcString;
+ m_iPos = a_iPos;
+ m_iFCol = a_iFCol;
+ m_iBCol = a_iBCol;
+ m_iAlpha = a_iAlpha;
+
+ // Convert the original string into an unicode string
+
+ int i;
+ for(i=0; i<m_iPos; i++)
+ {
+ m_piString[i]=(unsigned char)m_pcLatin1String[i];
+ }
+ m_piString[m_iPos]=0;
+
+ // Insert the cursor at the end
+
+ m_iCursor = 124; // '|' character
+ m_iMax=m_iPos;
+ m_piString[m_iPos+1]=0;
+ m_piString[m_iPos]=m_iCursor;
+ m_oUpdateRect = sge_tt_textout_UNI( m_poScreen, m_poFont, m_piString, x, y, m_iFCol, m_iBCol, m_iAlpha );
+}
+
+
+CReadline::~CReadline()
+{
+ delete[] m_piString;
+ SDL_EnableUNICODE(0);
+ SDL_FreeSurface( m_poBackground );
+ m_poBackground = NULL;
+}
+
+
+/** Returns the current state of the line input.
+\retval -1 A quit event was encountered.
+\retval -2 Escape was pressed
+\retval 0 Input is in progress.
+\retval 1 Input has finished.
+*/
+int CReadline::GetResult()
+{
+ if ( m_iResult == 0 )
+ {
+ return m_iResult;
+ }
+
+ sge_Blit( m_poBackground, m_poScreen, m_oUpdateRect.x, m_oUpdateRect.y,
+ m_oUpdateRect.x, m_oUpdateRect.y, m_oUpdateRect.w, m_oUpdateRect.h);
+ sge_UpdateRect(m_poScreen, m_oUpdateRect.x, m_oUpdateRect.y, m_oUpdateRect.w, m_oUpdateRect.h);
+
+ if ( m_iResult < 0 )
+ {
+ return m_iResult;
+ }
+
+ delete_char( m_piString, m_iPos, m_iMax );
+
+ memset( m_pcLatin1String, 0, sizeof(char)*(m_iPos+1) );
+ for( int i=0; i<=m_iMax; i++)
+ {
+ m_pcLatin1String[i] = (char)m_piString[i];
+ }
+
+ insert_char( m_piString, m_iCursor, m_iPos, m_iMax );
+
+ return 1;
+}
+
+
+
+
+/** Internal method for redrawing the string. */
+void CReadline::Update( int a_iCode )
+{
+ //m_oUpdateRect = nice_update( m_poScreen, m_poBackground, m_oUpdateRect, a_iCode, m_poFont, m_piString,
+ // x, y, m_iFCol, m_iBCol, m_iAlpha );
+
+ SDL_Rect oOldClipRect;
+ SDL_GetClipRect( m_poScreen, &oOldClipRect );
+ SDL_SetClipRect( m_poScreen, &m_oWorkArea );
+
+ sge_Blit( m_poBackground, m_poScreen, 0, 0, m_oWorkArea.x, m_oWorkArea.y, m_oWorkArea.w, m_oWorkArea.h );
+ sge_tt_textout_UNI( m_poScreen, m_poFont, m_piString, x, y, m_iFCol, m_iBCol, m_iAlpha );
+ sge_UpdateRect( m_poScreen, m_oWorkArea.x, m_oWorkArea.y, m_oWorkArea.w, m_oWorkArea.h );
+
+ SDL_SetClipRect( m_poScreen, &oOldClipRect );
+}
+
+
+void CReadline::Clear()
+{
+ sge_Blit( m_poBackground, m_poScreen, 0, 0, m_oWorkArea.x, m_oWorkArea.y, m_oWorkArea.w, m_oWorkArea.h );
+}
+
+
+/** Runs the event queue until the input is finished.
+\see GetResult
+*/
+int CReadline::Execute()
+{
+ int iRetval;
+ SDL_Event e;
+ while ( 1 )
+ {
+ SDL_WaitEvent( &e );
+ HandleKeyEvent( e );
+ iRetval = GetResult();
+ if ( iRetval )
+ break;
+ }
+
+ return iRetval;
+}
+
+
+
+void CReadline::HandleKeyEvent( SDL_Event& a_roEvent )
+{
+ if(a_roEvent.type==SDL_QUIT)
+ {
+ m_iResult = -1;
+ return;
+ }
+
+ if ( a_roEvent.type != SDL_KEYDOWN )
+ {
+ return;
+ }
+
+ if( a_roEvent.key.keysym.sym==SDLK_ESCAPE )
+ {
+ m_iResult = -2;
+ return;
+ }
+
+ if ( a_roEvent.key.keysym.sym==SDLK_RETURN
+ || a_roEvent.key.keysym.sym==SDLK_KP_ENTER )
+ {
+ m_iResult = 1;
+ return;
+ }
+
+ if( a_roEvent.key.keysym.sym==SDLK_BACKSPACE )
+ {
+ if ( m_iPos == 0 )
+ {
+ return;
+ }
+ /* Delete char cursor-1 */
+ delete_char(m_piString,m_iPos-1,m_iMax);
+ m_iPos--;
+ m_iMax--;
+ Update( 1 );
+ return;
+ }
+
+ if( a_roEvent.key.keysym.sym==SDLK_RIGHT
+ && m_iPos!=m_iMax && m_iPos!=m_iLen )
+ {
+ /* Move cursor right */
+ delete_char(m_piString,m_iPos,m_iMax);
+ m_iPos++;
+ insert_char(m_piString,m_iCursor,m_iPos,m_iMax);
+ Update( 3 );
+ return;
+ }
+
+ if ( a_roEvent.key.keysym.sym==SDLK_LEFT
+ && m_iPos>0 )
+ {
+ /* Move cursor left */
+ delete_char(m_piString,m_iPos,m_iMax);
+ m_iPos--;
+ insert_char(m_piString,m_iCursor,m_iPos,m_iMax);
+ Update( 3 );
+ return;
+ }
+
+ if( a_roEvent.key.keysym.sym==SDLK_DELETE )
+ {
+ if ( m_iPos!=m_iMax && m_iPos!=m_iLen )
+ {
+ delete_char(m_piString,m_iPos+1,m_iMax);
+ m_iMax--;
+ Update( 1 );
+ }
+ return;
+ }
+
+ if( a_roEvent.key.keysym.unicode!=0
+ && m_iMax != m_iLen )
+ {
+ m_iMax++;
+ insert_char(m_piString, a_roEvent.key.keysym.unicode, m_iPos, m_iMax);
+ m_iPos++;
+ Update( 0 );
+ }
+}
+
+
+/*
+SDL_Rect CReadline::NiceUpdate( SDL_Surface *Surface,SDL_Surface *buffer,
+ SDL_Rect ret, int type,sge_TTFont *font,Uint16 *string, Sint16 x,Sint16 y,
+ Uint32 fcol, Uint32 bcol, int Alpha )
+{
+ if(type==0){
+ sge_Update_OFF();
+ sge_Blit(buffer,Surface, ret.x, ret.y, ret.x, ret.y, ret.w, ret.h);
+ ret=sge_tt_textout_UNI(Surface,font,string, x,y, fcol, bcol, Alpha);
+ sge_Update_ON();
+ sge_UpdateRect(Surface, ret.x, ret.y, ret.w, ret.h);
+ }
+ else if(type==1){
+ SDL_Rect temp;
+
+ sge_Update_OFF();
+ sge_Blit(buffer,Surface, ret.x, ret.y, ret.x, ret.y, ret.w, ret.h);
+ temp=sge_tt_textout_UNI(Surface,font,string, x,y, fcol, bcol, Alpha);
+ sge_Update_ON();
+ sge_UpdateRect(Surface, ret.x, ret.y, ret.w, ret.h);
+ ret=temp;
+ }
+ else{
+ SDL_Rect temp;
+
+ sge_Update_OFF();
+ sge_Blit(buffer,Surface, ret.x, ret.y, ret.x, ret.y, ret.w, ret.h);
+ temp=sge_tt_textout_UNI(Surface,font,string, x,y, fcol, bcol, Alpha);
+ sge_Update_ON();
+ if(ret.w>=temp.w){
+ sge_UpdateRect(Surface, ret.x, ret.y, ret.w, ret.h);
+ }
+ else{
+ sge_UpdateRect(Surface, temp.x, temp.y, temp.w, temp.h);
+ }
+ ret=temp;
+ }
+
+ return ret;
+}
+*/
+
+
+
+
+
//==================================================================================
// Text input UNICODE (RGB)
//==================================================================================
int sge_tt_input_UNI(SDL_Surface *screen,sge_TTFont *font,Uint16 *string,Uint8 flags, int pos,int len,Sint16 x,Sint16 y, Uint8 fR, Uint8 fG, Uint8 fB, Uint8 bR,Uint8 bG,Uint8 bB, int Alpha)
{
return sge_tt_input_UNI(screen,font,string,flags,pos,len,x,y,SDL_MapRGB(screen->format, fR,fG,fB),SDL_MapRGB(screen->format, bR,bG,bB),Alpha);
}
//==================================================================================
// Text input Latin1
//
// Will fail miserable if ret<0!
//==================================================================================
int sge_tt_input(SDL_Surface *screen,sge_TTFont *font,char *string,Uint8 flags, int pos,int len,Sint16 x,Sint16 y, Uint32 fcol, Uint32 bcol, int Alpha)
{
if(len<pos || pos<0 || len<0){return -2;}
#ifndef __GNUC__
Uint16 *uni=new Uint16[len+2]; //ANSI C++
#else
Uint16 uni[len+2];
#endif
int ret;
int i;
if(pos!=0){
for(i=0; i<pos; i++){ //Convert Latin1 => Uni
uni[i]=(unsigned char)string[i];
}
}
uni[pos]=0;
ret=sge_tt_input_UNI(screen,font,uni,flags,pos,len,x,y,fcol,bcol,Alpha);
memset(string,0,sizeof(char)*(pos+1));
if(ret>0){
for( i=0; i<=ret; i++){ //Convert Uni => Latin1
string[i] = (char)uni[i];
}
}
#ifndef __GNUC__
delete[] uni;
#endif
return ret;
}
//==================================================================================
// Text input Latin1 (RGB)
//==================================================================================
int sge_tt_input(SDL_Surface *screen,sge_TTFont *font,char *string,Uint8 flags, int pos,int len,Sint16 x,Sint16 y, Uint8 fR, Uint8 fG, Uint8 fB, Uint8 bR,Uint8 bG,Uint8 bB, int Alpha)
{
return sge_tt_input(screen,font,string,flags,pos,len,x,y,SDL_MapRGB(screen->format, fR,fG,fB),SDL_MapRGB(screen->format, bR,bG,bB),Alpha);
}
#endif /* _SGE_NOTTF */
diff --git a/src/sge_tt_text.h b/src/sge_tt_text.h
index 94d7d0f..7658fb3 100644
--- a/src/sge_tt_text.h
+++ b/src/sge_tt_text.h
@@ -1,101 +1,145 @@
/*
* SDL Graphics Extension
* Text/TrueType functions (header)
*
* Started 990815
*
* License: LGPL v2+ (see the file LICENSE)
* (c)1999-2001 Anders Lindstr?m
*
* Uses the excellent FreeType 2 library, available at:
* http://www.freetype.org/
*/
/*********************************************************************
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
*********************************************************************/
#ifndef sge_tt_text_H
#define sge_tt_text_H
#include "SDL.h"
#include "sge_internal.h"
/* Text input flags */
#define SGE_IBG SGE_FLAG1
#define SGE_IDEL SGE_FLAG2
#define SGE_INOKR SGE_FLAG3
#ifndef _SGE_NOTTF
/* the truetype font structure */
typedef struct _sge_TTFont sge_TTFont;
/* Font style */
#define SGE_TTF_NORMAL SGE_FLAG0
#define SGE_TTF_BOLD SGE_FLAG1
#define SGE_TTF_ITALIC SGE_FLAG2
#define SGE_TTF_UNDERLINE SGE_FLAG3
#endif /* _SGE_NOTTF */
#ifdef _SGE_C
extern "C" {
#endif
#ifndef _SGE_NOTTF
DECLSPEC void sge_TTF_AAOff(void);
DECLSPEC void sge_TTF_AAOn(void);
DECLSPEC void sge_TTF_AA_Alpha(void);
DECLSPEC int sge_TTF_Init(void);
DECLSPEC sge_TTFont *sge_TTF_OpenFont(const char *file, int ptsize);
DECLSPEC int sge_TTF_SetFontSize(sge_TTFont *font, int ptsize);
DECLSPEC int sge_TTF_FontHeight(sge_TTFont *font);
DECLSPEC int sge_TTF_FontAscent(sge_TTFont *font);
DECLSPEC int sge_TTF_FontDescent(sge_TTFont *font);
DECLSPEC int sge_TTF_FontLineSkip(sge_TTFont *font);
DECLSPEC void sge_TTF_SetFontStyle(sge_TTFont *font, Uint8 style);
DECLSPEC Uint8 sge_TTF_GetFontStyle(sge_TTFont *font);
DECLSPEC void sge_TTF_CloseFont(sge_TTFont *font);
DECLSPEC SDL_Rect sge_TTF_TextSizeUNI(sge_TTFont *font, const Uint16 *text);
DECLSPEC SDL_Rect sge_TTF_TextSize(sge_TTFont *Font, const char *Text);
DECLSPEC SDL_Rect sge_tt_textout(SDL_Surface *Surface, sge_TTFont *font, const char *string, Sint16 x, Sint16 y, Uint32 fcolor, Uint32 bcolor, int Alpha);
DECLSPEC SDL_Rect sge_tt_textout_UTF8(SDL_Surface *Surface, sge_TTFont *font, const char *string, Sint16 x, Sint16 y, Uint32 fcolor, Uint32 bcolor, int Alpha);
DECLSPEC SDL_Rect sge_tt_textout_UNI(SDL_Surface *Surface, sge_TTFont *font, const Uint16 *uni, Sint16 x, Sint16 y, Uint32 fcolor, Uint32 bcolor, int Alpha);
DECLSPEC SDL_Rect sge_tt_textoutf(SDL_Surface *Surface, sge_TTFont *font, Sint16 x, Sint16 y, Uint8 fR, Uint8 fG, Uint8 fB, Uint8 bR, Uint8 bG, Uint8 bB, int Alpha , const char *format,...);
DECLSPEC int sge_tt_input_UNI(SDL_Surface *screen,sge_TTFont *font,Uint16 *string,Uint8 flags, int pos,int len,Sint16 x,Sint16 y, Uint32 fcol, Uint32 bcol, int Alpha);
DECLSPEC int sge_tt_input(SDL_Surface *screen,sge_TTFont *font,char *string,Uint8 flags, int pos,int len,Sint16 x,Sint16 y, Uint32 fcol, Uint32 bcol, int Alpha);
DECLSPEC SDL_Surface *sge_TTF_RenderUNICODE(sge_TTFont *font,const Uint16 *text, SDL_Color fg, SDL_Color bg);
DECLSPEC SDL_Rect fast_update(SDL_Surface *Surface,SDL_Surface *buffer,SDL_Rect ret, int type,sge_TTFont *font,Uint16 *string, Sint16 x,Sint16 y, Uint32 fcol, Uint32 bcol,int Alpha);
DECLSPEC SDL_Rect nice_update(SDL_Surface *Surface,SDL_Surface *buffer,SDL_Rect ret, int type,sge_TTFont *font,Uint16 *string, Sint16 x,Sint16 y, Uint32 fcol, Uint32 bcol, int Alpha);
#endif /* _SGE_NOTTF */
DECLSPEC int keyrepeat(SDL_Event *event, int wait);
DECLSPEC void insert_char(Uint16 *string, Uint16 ch, int pos, int max);
DECLSPEC void delete_char(Uint16 *string, int pos, int max);
DECLSPEC Uint16 *sge_Latin1_Uni(const char *text);
#ifdef _SGE_C
}
#endif
#ifndef sge_C_ONLY
#ifndef _SGE_NOTTF
DECLSPEC SDL_Rect sge_tt_textout(SDL_Surface *Surface, sge_TTFont *font, const char *string, Sint16 x, Sint16 y, Uint8 fR, Uint8 fG, Uint8 fB, Uint8 bR, Uint8 bG, Uint8 bB, int Alpha);
DECLSPEC SDL_Rect sge_tt_textout_UTF8(SDL_Surface *Surface, sge_TTFont *font, const char *string, Sint16 x, Sint16 y, Uint8 fR, Uint8 fG, Uint8 fB, Uint8 bR, Uint8 bG, Uint8 bB, int Alpha);
DECLSPEC SDL_Rect sge_tt_textout_UNI(SDL_Surface *Surface, sge_TTFont *font, const Uint16 *uni, Sint16 x, Sint16 y, Uint8 fR, Uint8 fG, Uint8 fB, Uint8 bR, Uint8 bG, Uint8 bB, int Alpha);
DECLSPEC int sge_tt_input_UNI(SDL_Surface *screen,sge_TTFont *font,Uint16 *string,Uint8 flags, int pos,int len,Sint16 x,Sint16 y, Uint8 fR, Uint8 fG, Uint8 fB, Uint8 bR,Uint8 bG,Uint8 bB, int Alpha);
DECLSPEC int sge_tt_input(SDL_Surface *screen,sge_TTFont *font,char *string,Uint8 flags, int pos,int len,Sint16 x,Sint16 y, Uint8 fR, Uint8 fG, Uint8 fB, Uint8 bR,Uint8 bG,Uint8 bB, int Alpha);
#endif /* _SGE_NOTTF */
#endif /* sge_C_ONLY */
+
+class CReadline
+{
+public:
+ CReadline( SDL_Surface *a_poScreen, _sge_TTFont *a_poFont,
+ char *a_pcString, int a_iPos, int a_iLen,
+ int a_x, int a_y, int a_w, Uint32 a_iFCol, Uint32 a_iBCol, int a_iAlpha );
+ ~CReadline();
+
+ void Restart( char *a_pcString, int a_iPos, int a_iLen,
+ Uint32 a_iFCol, Uint32 a_iBCol, int a_iAlpha );
+ int GetResult();
+ void HandleKeyEvent( SDL_Event& a_roEvent );
+ void Clear();
+
+ int Execute();
+
+protected:
+ void Update( int a_iCode );
+ void NiceUpdate( int a_iCode );
+
+protected:
+ SDL_Surface* m_poScreen;
+ SDL_Surface* m_poBackground;
+ SDL_Rect m_oWorkArea;
+
+ _sge_TTFont* m_poFont;
+ char* m_pcLatin1String;
+ Uint16* m_piString;
+ int m_iPos;
+ int m_iLen;
+ int x, y, w;
+ Uint32 m_iFCol;
+ Uint32 m_iBCol;
+ int m_iAlpha;
+
+ SDL_Rect m_oUpdateRect;
+ char m_iCursor;
+ int m_iMax;
+ int m_iResult;
+};
+
+
+
#endif /* sge_tt_text_H */

File Metadata

Mime Type
text/x-diff
Expires
Wed, Feb 4, 2:10 PM (13 h, 39 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
55621
Default Alt Text
(239 KB)

Event Timeline