Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
57 KB
Referenced Files
None
Subscribers
None
diff --git a/src/Background.h b/src/Background.h
index 1de1eb0..18862b9 100644
--- a/src/Background.h
+++ b/src/Background.h
@@ -1,45 +1,45 @@
/***************************************************************************
Background.h - description
-------------------
begin : Sun Jan 11 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
#ifndef __BACKGROUND_H
#define __BACKGROUND_H
#include <vector>
-class SDL_Surface;
+struct SDL_Surface;
struct BackgroundLayer
{
SDL_Surface* m_poSurface;
int m_iXOffset;
int m_iYOffset;
double m_dDistance;
};
typedef std::vector<BackgroundLayer> LayerVector;
typedef LayerVector::iterator LayerIterator;
class Background
{
public:
Background();
~Background();
void Clear();
void Load( int a_iBackgroundNumber );
bool IsOK();
void Draw( int a_iXPosition, int a_iYPosition );
protected:
int m_iNumber;
bool m_bOK;
LayerVector m_aLayers;
};
#endif // __BACKGROUND_H
diff --git a/src/FighterStats.h b/src/FighterStats.h
index 5e7e418..a416ec9 100644
--- a/src/FighterStats.h
+++ b/src/FighterStats.h
@@ -1,35 +1,35 @@
/***************************************************************************
FighterStats.h - description
-------------------
begin : Sun Jan 25 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
#ifndef FIGHTERSTATS_H
#define FIGHTERSTATS_H
-#include <Demo.h>
+#include "Demo.h"
#include <string>
class FighterStatsDemo: public Demo
{
public:
FighterStatsDemo( FighterEnum a_iFighter = UNKNOWN );
virtual ~FighterStatsDemo();
int Advance( int a_iNumFrames, bool a_bFlip );
protected:
int m_iTimeLeft;
FighterEnum m_enFighter;
RlePack* m_poStaff;
std::string m_sKeys;
std::string m_sStory;
static int mg_iLastFighter; // index of the last fighter in the fighter order
static FighterEnum mg_aenFighterOrder[LASTFIGHTER-1];
};
#endif // FIGHTERSTATS_H
diff --git a/src/Game.cpp b/src/Game.cpp
index 6413c99..715ad8b 100644
--- a/src/Game.cpp
+++ b/src/Game.cpp
@@ -1,1035 +1,1035 @@
/***************************************************************************
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
|| 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 );
}
int iTextX = 230 - g_oPlayerSelect.GetFighterNameWidth(0);
if ( iTextX < 5 ) iTextX = 5;
sge_BF_textout( gamescreen, fastFont, g_oPlayerSelect.GetFighterName(0),
iTextX, 38 );
iTextX = g_oPlayerSelect.GetFighterNameWidth(1);
iTextX = iTextX < (635-410) ? 410 : 635-iTextX;
sge_BF_textout( gamescreen, fastFont, g_oPlayerSelect.GetFighterName(1),
iTextX, 38 );
}
/** Draws the background, using the m_poBackground object.
*/
void Game::DrawBackground()
{
m_poBackground->Draw( g_oBackend.m_iBgX, g_oBackend.m_iBgY );
}
/** In debug mode, this method is used to draw the frame of the fighters.
\param a_sName The name of the polygon (in the perl namespace)
\param a_iColor The game color to draw the polygon with.
*/
void Game::DrawPoly( const char* a_pcName, int a_iColor )
{
AV *poList;
int n;
poList = get_av( a_pcName, FALSE );
if ( poList == NULL )
{
return;
}
n = av_len( poList ) + 1;
if ( n< 2 )
{
return;
}
for ( int i=0; i<n; i += 2 )
{
int j = (i+2) % n;
int x1 = SvIV( *av_fetch( poList, i, false) );
int y1 = SvIV( *av_fetch( poList, i+1, false) );
int x2 = SvIV( *av_fetch( poList, j, false) );
int y2 = SvIV( *av_fetch( poList, j+1, false) );
sge_Line( gamescreen, x1, y1, x2, y2, a_iColor ) ;
}
}
/** 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()
{
#define GROUNDZERO 440
DrawBackground();
// DRAW THE SHADOWS
int i;
for ( i=0; i<2; ++i )
{
Backend::SPlayer& roPlayer = g_oBackend.m_aoPlayers[i];
int iFrame = roPlayer.m_iFrame;
if ( iFrame == 0 )
continue;
RlePack* poPack = g_oPlayerSelect.GetPlayerInfo(i).m_poPack;
int w = poPack->GetWidth( ABS(iFrame)-1 );
int h = poPack->GetHeight( ABS(iFrame)-1 );
h = GROUNDZERO - ( h + roPlayer.m_iY ); // Distance of feet from ground
if ( h < 0 ) h = 0;
if ( h > 500 ) h = 500;
h = 500 - h;
int h2 = 15 * h / 500;
h = ( w / 2 ) * h / 500;
sge_FilledEllipse( gamescreen,
g_oBackend.m_aoPlayers[i].m_iX + w/2, GROUNDZERO,
h, h2, C_BLACK );
}
- for ( int i=0; i<2; ++i )
+ for ( i=0; i<2; ++i )
{
Backend::SPlayer& roPlayer = g_oBackend.m_aoPlayers[i];
int iFrame = roPlayer.m_iFrame;
if ( iFrame == 0 )
continue;
RlePack* poPack = g_oPlayerSelect.GetPlayerInfo(i).m_poPack;
poPack->Draw( ABS(iFrame)-1, roPlayer.m_iX, roPlayer.m_iY, iFrame<0 );
}
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", 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 = 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 );
g_oBackend.PlaySounds();
return;
}
}
// OK. The trick here is to collect the sound information, and then
// inject it into the last frame to send it to the remote side.
static std::string sFrameDesc;
int iSounds = 0;
std::string asSounds[ MAXSOUNDS ];
int i;
while ( a_iNumFrames > 0 )
{
-- a_iNumFrames;
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() );
for ( i=0;
IsNetworkGame() && i<g_oBackend.m_iNumSounds && iSounds<MAXSOUNDS;
++i, ++iSounds )
{
asSounds[iSounds] = g_oBackend.m_asSounds[i];
}
}
if ( IsNetworkGame() && sFrameDesc.size() )
{
g_oBackend.m_iNumSounds = iSounds;
for ( i=0; i<iSounds; ++i )
{
g_oBackend.m_asSounds[i] = asSounds[i];
}
g_oBackend.WriteToString( sFrameDesc );
g_poNetwork->SendGameData( sFrameDesc.c_str() );
}
}
/** 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 && !IsNetworkGame() )
{
SState::TGameMode enMode = g_oState.m_enGameMode;
::DoMenu();
return g_oState.m_enGameMode == enMode ? 0 : 1;
}
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;
}
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 (START, NORMAL, KO, TIMEUP)
while ( dGameTime >= 0 )
{
if ( m_enInitialGameMode != g_oState.m_enGameMode )
{
return;
}
// 1. Wait for the next tick (on extremely fast machines..)
while (iThisTick == iLastTick)
{
iThisTick = SDL_GetTicks() / iGameSpeed;
if ( iThisTick==iLastTick ) SDL_Delay(1);
}
// 2. Advance as many ticks as necessary..
int iNumTicks = iThisTick - iLastTick;
if ( iNumTicks > MAXFRAMESKIP ) iNumTicks = MAXFRAMESKIP;
Advance( iNumTicks );
dGameTime -= iNumTicks * iGameSpeed;
// 3. Check for state transitions and game time.
// START -> NORMAL
// NORMAL -> KO
// NORMAL -> TIMEUP
// bHurryUp flag can be set during NORMAL phase
if ( IsMaster() )
{
if ( Ph_START == m_enGamePhase ) // Check for the end of the START phase
{
if ( dGameTime <= 0 )
{
m_enGamePhase = Ph_NORMAL;
dGameTime = g_oState.m_iGameTime * 1000;
}
}
else if ( Ph_NORMAL == m_enGamePhase ) // Check for the end of the NORMAL phase
{
if ( dGameTime < 10 * 1000
&& !bHurryUp )
{
bHurryUp = true;
g_poNetwork->SendHurryup( 1 );
HurryUp();
iGameSpeed = iGameSpeed * 3 / 4;
}
if ( g_oBackend.m_bKO )
{
m_enGamePhase = Ph_KO;
dGameTime = 10 * 1000;
iKoFrame = m_aReplayOffsets.size();
}
else if ( dGameTime <= 0 )
{
m_enGamePhase = Ph_TIMEUP;
g_poNetwork->SendHurryup( 2 );
TimeUp();
break;
}
}
m_iGameTime = (int) ((dGameTime + 500.0) / 1000.0);
if ( IsNetworkGame() )
{
g_poNetwork->SendGameTime( m_iGameTime, m_enGamePhase );
}
}
else
{
m_iGameTime = g_poNetwork->GetGameTime();
m_enGamePhase = (TGamePhaseEnum) g_poNetwork->GetGamePhase();
dGameTime = 1000.0; // ignored.
switch (g_poNetwork->GetHurryup() )
{
case 1: HurryUp(); break;
case 2: TimeUp(); break;
}
}
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();
// 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 && !IsNetworkGame() )
{
InstantReplay( iKoFrame );
}
// 4. END OF ROUND
debug( "Game over; p1h = %d; p2h = %d\n", p1h, p2h );
if ( IsMaster() )
{
int iWhoWon = -1;
if ( p1h > p2h ) { ++m_aiRoundsWonByPlayer[0]; iWhoWon = 0; }
if ( p2h > p1h ) { ++m_aiRoundsWonByPlayer[1]; iWhoWon = 1; }
if ( IsNetworkGame() )
{
g_poNetwork->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/MortalNetworkImpl.cpp b/src/MortalNetworkImpl.cpp
index febc320..c0c2cf3 100644
--- a/src/MortalNetworkImpl.cpp
+++ b/src/MortalNetworkImpl.cpp
@@ -1,920 +1,933 @@
/***************************************************************************
MortalNetworkImpl.cpp - description
-------------------
begin : Sun Jan 25 2004
copyright : (C) 2004 by upi
email : upi@feel
***************************************************************************/
#include "MortalNetworkImpl.h"
#include "State.h"
#include "PlayerSelect.h"
#include "common.h"
#include "config.h"
#define MORTALNETWORKPORT 0x3A22
#define MAXSTRINGLENGTH 900
// Some graphics routines, defined in menu.cpp
void MortalNetworkResetMessages( bool a_bClear );
void MortalNetworkMessage( const char* format, ... );
bool MortalNetworkCheckKey();
const char* GetGameTimeString( int a_iValue );
const char* GetGameSpeedString( int a_iValue );
const char* GetHitPointsString( int a_iValue );
CMortalNetwork* g_poNetwork = NULL;
void CMortalNetwork::Create() // static
{
if ( NULL == g_poNetwork )
{
g_poNetwork = new CMortalNetworkImpl;
}
}
CMortalNetworkImpl::CMortalNetworkImpl()
{
m_enState = NS_DISCONNECTED;
m_bServer = false;
m_bMaster = false;
m_poSocket = NULL;
m_enRemoteFighter = UNKNOWN;
m_bRemoteReady = false;
m_bRoundOver = false;
m_iWhoWon = -1;
m_bGameOver = false;
m_iGameTime = 0;
m_iGamePhase = 0;
m_iHurryupCode = 0;
m_iIncomingBufferSize = 0;
if(SDLNet_Init()==-1)
{
m_bNetworkAvailable = false;
m_sLastError = SDLNet_GetError();
debug ( "Error opening SDLNet: %s\n", m_sLastError.c_str() );
}
m_bNetworkAvailable = true;
}
CMortalNetworkImpl::~CMortalNetworkImpl()
{
Stop();
}
bool CMortalNetworkImpl::Start( const char* a_pcServerName )
{
#define RETURNNOERROR { \
debug( "%s\n", m_sLastError.c_str() ); \
return false; }
#define RETURNWITHERROR { \
m_sLastError = SDLNet_GetError(); \
debug( "%s\n", m_sLastError.c_str() ); \
return false; }
#define RETURNWITHADDITIONALERROR { \
m_sLastError += SDLNet_GetError(); \
debug( "%s\n", m_sLastError.c_str() ); \
return false; }
if ( !m_bNetworkAvailable )
{
return false;
}
debug( "CMortalNetworkImpl::Start( %s )\n", a_pcServerName ? a_pcServerName : "NULL" );
IPaddress oAddress;
if ( a_pcServerName )
{
MortalNetworkMessage( Translate("Resolving hostname (%s)..."), a_pcServerName );
}
int iResult = SDLNet_ResolveHost( &oAddress, 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 );
if ( NULL == m_poSocket ) RETURNWITHERROR;
// 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_iHurryupCode = 0;
m_iIncomingBufferSize = 0;
m_aiAvailableRemoteFighters.clear();
m_oGameParams.iGameTime = m_oGameParams.iGameSpeed = m_oGameParams.iHitPoints = 0;
m_sRemoteUserName = "HIM";
m_poSocketSet = SDLNet_AllocSocketSet( 1 );
SDLNet_TCP_AddSocket( m_poSocketSet, m_poSocket ); // Check for errors?
SendRawData( 'U', g_oState.m_acNick, strlen(g_oState.m_acNick)+1 );
return true;
}
void CMortalNetworkImpl::Stop()
{
if ( NS_DISCONNECTED == m_enState )
{
return;
}
g_oState.m_enGameMode = SState::IN_DEMO;
m_enState = NS_DISCONNECTED;
SDLNet_FreeSocketSet( m_poSocketSet );
SDLNet_TCP_Close( m_poSocket );
}
bool CMortalNetworkImpl::IsConnectionAlive()
{
return ( NS_DISCONNECTED != m_enState );
}
#define DISCONNECTONCOMMUNICATIONERROR { \
m_sLastError = Translate("Communication error. Disconnecting."); \
Stop(); \
return; }
#define DISCONNECTWITH(A) { \
m_sLastError = Translate("Communication error. Disconnecting."); \
Stop(); \
return(A); }
#define CHECKCONNECTION if ( NS_DISCONNECTED == m_enState ) return;
void CMortalNetworkImpl::Update()
{
CHECKCONNECTION;
// 1. CHECK FOR STUFF TO READ
int iRetval = SDLNet_CheckSockets( m_poSocketSet, 0 );
if ( iRetval <= 0 )
{
return;
}
// 2. APPEND AT MOST 1024 bytes TO THE END OF THE INCOMING BUFFER
iRetval = SDLNet_TCP_Recv( m_poSocket, m_acIncomingBuffer + m_iIncomingBufferSize, 1024 );
if ( iRetval <= 0 )
{
m_sLastError = SDLNet_GetError();
Stop();
return;
}
m_iIncomingBufferSize += iRetval;
// 3. CONSUME THE INCOMING BUFFER.
// We always make sure the incoming buffer starts with a package header.
int iOffset = 0;
while ( iOffset < m_iIncomingBufferSize )
{
// 3.1. Check if we have enough data to receive the package.
if ( m_iIncomingBufferSize - iOffset < 4 )
{
// Not enough space left for a full header.
debug( "Not enough space left for a full header (%d).\n", m_iIncomingBufferSize-iOffset );
break;
}
unsigned int iLengthOfPackage = SDL_SwapBE16(*((Uint16*)(m_acIncomingBuffer + iOffset + 1)));
if ( iLengthOfPackage > 1000 )
{
debug( "Maximum package size exceeded.\n" );
DISCONNECTONCOMMUNICATIONERROR;
}
debug( "Receiving stuff.. %c type, %d package length, offset %d in buffer, %d bytes in buffer\n",
m_acIncomingBuffer[iOffset], iLengthOfPackage, iOffset, m_iIncomingBufferSize );
if ( iOffset + 4 + (int)iLengthOfPackage > m_iIncomingBufferSize )
{
// Not enough space left for the actual package.
debug( "Not enough space left for the actual package.\n" );
break;
}
// 3.2. Receive the data.
switch ( m_acIncomingBuffer[iOffset] )
{
case 'M': ReceiveMsg( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'F': ReceiveFighter( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'R': ReceiveReady( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'S': m_bSynchQueryResponse=true; break;
case 'G': ReceiveGameData( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'K': ReceiveKeystroke( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'O': ReceiveRoundOver( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'T': ReceiveGameTime( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'H': ReceiveHurryup( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'A': ReceiveRemoteFighterAvailable( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'Q': ReceiveRemoteFighterQuery( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'P': ReceiveGameParams( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
case 'U': ReceiveRemoteUserName( m_acIncomingBuffer+iOffset+4, iLengthOfPackage ); break;
default:
{
debug( "Bad ID: %c (%d)\n", m_acIncomingBuffer[iOffset], m_acIncomingBuffer[iOffset] );
DISCONNECTONCOMMUNICATIONERROR;
}
}
if ( !IsConnectionAlive() )
{
return;
}
iOffset += iLengthOfPackage + 4;
}
// 4. MOVE LEFTOVER DATA TO THE BEGINNING OF THE INCOMING BUFFER
// The leftover data starts at iOffset, and is (m_iIncomingBufferSize-iOffset) long.
memmove( m_acIncomingBuffer, m_acIncomingBuffer + iOffset, m_iIncomingBufferSize-iOffset );
m_iIncomingBufferSize -= iOffset;
}
const char* CMortalNetworkImpl::GetLastError()
{
return m_sLastError.c_str();
}
bool CMortalNetworkImpl::IsMaster()
{
return m_bMaster;
}
void CMortalNetworkImpl::ReceiveRemoteUserName( void* a_pData, int a_iLength )
{
if ( a_iLength < 1 || a_iLength > MAXSTRINGLENGTH ) DISCONNECTONCOMMUNICATIONERROR;
char* acData = (char*) a_pData;
acData[ a_iLength-1 ] = 0; // Last char should be 0, just making sure..
m_sRemoteUserName = acData;
}
const char* CMortalNetworkImpl::GetRemoteUsername()
{
return m_sRemoteUserName.c_str();
}
/*************************************************************************
MSG RELATED METHODS
*************************************************************************/
/** All sent data must go through this method. It ensures the well-formed
header for the data.
The header itself looks like this:
ID char
Length Uint16
Reserved char
This is followed by as many bytes as the Length is.
*/
void CMortalNetworkImpl::SendRawData( char a_cID, const void* a_pData, int a_iLength )
{
CHECKCONNECTION;
char acBuffer[4];
acBuffer[0] = a_cID;
*((Uint16*)(acBuffer+1)) = SDL_SwapBE16( a_iLength );
acBuffer[3] = 0;
int iRetval = SDLNet_TCP_Send( m_poSocket, &acBuffer, 4 );
if ( iRetval != 4 ) DISCONNECTONCOMMUNICATIONERROR;
if ( a_iLength )
{
iRetval = SDLNet_TCP_Send( m_poSocket, (void*) a_pData, a_iLength );
if (iRetval != a_iLength ) DISCONNECTONCOMMUNICATIONERROR;
}
}
void CMortalNetworkImpl::SendMsg( const char* a_pcMsg )
{
CHECKCONNECTION;
int iMsgLen = strlen( a_pcMsg ) + 1;
if ( iMsgLen > MAXSTRINGLENGTH )
{
// Will not be 0 terminated if exceeds length!
iMsgLen = MAXSTRINGLENGTH;
}
SendRawData( 'M', a_pcMsg, iMsgLen );
}
void CMortalNetworkImpl::ReceiveMsg( void* a_pData, int a_iLength )
{
if ( a_iLength < 1 || a_iLength > MAXSTRINGLENGTH ) DISCONNECTONCOMMUNICATIONERROR;
char* pcData = (char*) a_pData;
pcData[ a_iLength-1 ] = 0; // Last char should be 0, just making sure..
std::string sMsg = "<" + m_sRemoteUserName + "> " + pcData;
m_asMsgs.push_back( sMsg );
}
bool CMortalNetworkImpl::IsMsgAvailable()
{
return m_asMsgs.size() > 0;
}
const char* CMortalNetworkImpl::GetMsg()
{
static std::string sLastMsg;
if ( IsMsgAvailable() )
{
sLastMsg = m_asMsgs.front();
m_asMsgs.pop_front();
return sLastMsg.c_str();
}
return NULL;
}
/*************************************************************************
CHARACTER SELECTION RELATED METHODS
*************************************************************************/
+/** Unfortunately not all STL are created equal... Some do not have the
+find algorithm. So, here is our feeble implementation... */
+template<class InputIterator, class EqualityComparable>
+InputIterator MszFind(InputIterator first, InputIterator last, const EqualityComparable& value )
+{
+ while ( first != last
+ && *first != value )
+ {
+ ++first;
+ }
+ return first;
+}
+
bool CMortalNetworkImpl::IsRemoteFighterAvailable( FighterEnum a_enFighter )
{
if ( 0 == a_enFighter )
{
return false;
}
// Check if we already have it cached.
- if ( std::find( m_aiAvailableRemoteFighters.begin(), m_aiAvailableRemoteFighters.end(), a_enFighter ) != m_aiAvailableRemoteFighters.end() )
+ if ( MszFind( m_aiAvailableRemoteFighters.begin(), m_aiAvailableRemoteFighters.end(), a_enFighter ) != m_aiAvailableRemoteFighters.end() )
{
return true;
}
- if ( std::find( m_aiAvailableRemoteFighters.begin(), m_aiAvailableRemoteFighters.end(), -a_enFighter ) != m_aiAvailableRemoteFighters.end() )
+ if ( MszFind( m_aiAvailableRemoteFighters.begin(), m_aiAvailableRemoteFighters.end(), -a_enFighter ) != m_aiAvailableRemoteFighters.end() )
{
return false;
}
Uint32 iFighter = SDL_SwapBE32( a_enFighter );
SendRawData( 'Q', &iFighter, sizeof(Uint32) );
for ( int i=0; i<100; ++i )
{
SDL_Delay(10);
Update();
- if ( std::find( m_aiAvailableRemoteFighters.begin(), m_aiAvailableRemoteFighters.end(), a_enFighter ) != m_aiAvailableRemoteFighters.end() )
+ if ( MszFind( m_aiAvailableRemoteFighters.begin(), m_aiAvailableRemoteFighters.end(), a_enFighter ) != m_aiAvailableRemoteFighters.end() )
{
return true;
}
- if ( std::find( m_aiAvailableRemoteFighters.begin(), m_aiAvailableRemoteFighters.end(), -a_enFighter ) != m_aiAvailableRemoteFighters.end() )
+ if ( MszFind( m_aiAvailableRemoteFighters.begin(), m_aiAvailableRemoteFighters.end(), -a_enFighter ) != m_aiAvailableRemoteFighters.end() )
{
return false;
}
}
m_aiAvailableRemoteFighters.push_front( -a_enFighter );
return false;
}
void CMortalNetworkImpl::ReceiveRemoteFighterQuery( void* a_pData, int a_iLength )
{
if ( a_iLength != sizeof(Uint32) ) DISCONNECTONCOMMUNICATIONERROR;
FighterEnum iFighter = (FighterEnum) SDL_SwapBE32( *((Uint32*)a_pData) );
bool bAvailable = g_oPlayerSelect.IsLocalFighterAvailable( iFighter );
Uint32 iResponse = bAvailable ? iFighter : iFighter + 100000;
debug( "ReceiveRemoteFighterQuery: %d -> %d\n", iFighter, iResponse );
iResponse = SDL_SwapBE32( iResponse );
SendRawData( 'A', &iResponse, sizeof(Uint32) );
}
void CMortalNetworkImpl::ReceiveRemoteFighterAvailable( void* a_pData, int a_iLength )
{
if ( a_iLength != sizeof(Uint32) ) DISCONNECTONCOMMUNICATIONERROR;
Uint32 iFighter = SDL_SwapBE32( *((Uint32*)a_pData) );
debug( "ReceiveRemoteFighterAvailable: %d\n", iFighter );
if ( iFighter >= 100000 )
{
m_aiAvailableRemoteFighters.push_front( -(iFighter-100000) );
}
else
{
m_aiAvailableRemoteFighters.push_front( iFighter );
}
}
void CMortalNetworkImpl::SendFighter( FighterEnum a_enFighter )
{
CHECKCONNECTION;
Uint32 iFighter = SDL_SwapBE32( a_enFighter );
SendRawData( 'F', &iFighter, sizeof (iFighter) );
}
void CMortalNetworkImpl::ReceiveFighter( void* a_pcData, int a_iLength )
{
Uint32 iFighter;
if ( a_iLength != sizeof(iFighter) ) DISCONNECTONCOMMUNICATIONERROR;
iFighter = *((Uint32*)a_pcData);
m_enRemoteFighter = (FighterEnum) SDL_SwapBE32( iFighter );
debug( "ReceiveFighter: %d\n", m_enRemoteFighter );
}
FighterEnum CMortalNetworkImpl::GetRemoteFighter()
{
return m_enRemoteFighter;
}
void CMortalNetworkImpl::SendReady()
{
CHECKCONNECTION;
SendRawData( 'R', NULL, 0 );
}
void CMortalNetworkImpl::ReceiveReady( void* a_pData, int a_iLength )
{
if ( a_iLength != 0 ) DISCONNECTONCOMMUNICATIONERROR;
m_bRemoteReady = true;
}
bool CMortalNetworkImpl::IsRemoteSideReady()
{
return m_bRemoteReady;
}
void CMortalNetworkImpl::SendGameParams( int a_iGameSpeed, int a_iGameTime, int a_iHitPoints )
{
CHECKCONNECTION;
if ( (int)m_oGameParams.iGameSpeed == a_iGameSpeed
&& (int)m_oGameParams.iGameTime == a_iGameTime
&& (int)m_oGameParams.iHitPoints == a_iHitPoints )
{
// Nothing to update.
return;
}
m_oGameParams.iGameSpeed = a_iGameSpeed;
m_oGameParams.iGameTime = a_iGameTime;
m_oGameParams.iHitPoints = a_iHitPoints;
SGameParams oPackage;
oPackage.iGameSpeed = SDL_SwapBE32( a_iGameSpeed );
oPackage.iGameTime = SDL_SwapBE32( a_iGameTime );
oPackage.iHitPoints = SDL_SwapBE32( a_iHitPoints );
SendRawData( 'P', &oPackage, sizeof(SGameParams) );
}
void CMortalNetworkImpl::ReceiveGameParams( void* a_pData, int a_iLength )
{
if ( a_iLength != sizeof(SGameParams) ) DISCONNECTONCOMMUNICATIONERROR;
SGameParams* poPackage = (SGameParams*) a_pData;
if ( m_oGameParams.iGameSpeed != SDL_SwapBE32( poPackage->iGameSpeed ) )
{
m_oGameParams.iGameSpeed = SDL_SwapBE32( poPackage->iGameSpeed );
m_asMsgs.push_back( std::string("*** ") + GetGameSpeedString( m_oGameParams.iGameSpeed ) );
}
if ( m_oGameParams.iGameTime != SDL_SwapBE32( poPackage->iGameTime ) )
{
m_oGameParams.iGameTime = SDL_SwapBE32( poPackage->iGameTime );
m_asMsgs.push_back( std::string("*** ") + GetGameTimeString( m_oGameParams.iGameTime) );
}
if ( m_oGameParams.iHitPoints != SDL_SwapBE32( poPackage->iHitPoints ) )
{
m_oGameParams.iHitPoints = SDL_SwapBE32( poPackage->iHitPoints );
m_asMsgs.push_back( std::string("*** ") + GetHitPointsString( m_oGameParams.iHitPoints ) );
}
}
/*************************************************************************
GAME RELATED METHODS
*************************************************************************/
bool CMortalNetworkImpl::SynchStartRound()
{
debug( "SynchStartRound STARTED.\n" );
m_bSynchQueryResponse = false;
// run until both sides manage to get a SYNCH
int i=0;
while ( !m_bSynchQueryResponse )
{
SendRawData('S', NULL, 0);
if ( !IsConnectionAlive() ) break;
Update();
SDL_Delay(100);
if ( !IsConnectionAlive() ) break;
++i;
if ( i == 10 )
{
debug( "Synch is slow...\n" );
MortalNetworkResetMessages( true );
MortalNetworkMessage( "Synching with remote side..." );
MortalNetworkMessage( "Press any key to disconnect." );
}
if ( i > 10
&& MortalNetworkCheckKey() )
{
Stop();
return false;
}
}
if ( IsConnectionAlive() )
{
m_enState = NS_IN_GAME;
m_bRoundOver = false;
m_bGameOver = false;
m_bRemoteReady = false;
m_bSynchQueryResponse = false;
m_iHurryupCode = -1;
}
return IsConnectionAlive();
debug( "SynchStartRound FINISHED.\n" );
}
void CMortalNetworkImpl::SendGameData( const char* a_pcGameData )
{
CHECKCONNECTION;
int iMsgLen = strlen( a_pcGameData ) + 1;
if ( iMsgLen > MAXSTRINGLENGTH )
{
// Will not be 0 terminated if exceeds length!
iMsgLen = MAXSTRINGLENGTH;
}
SendRawData( 'G', a_pcGameData, iMsgLen );
}
void CMortalNetworkImpl::ReceiveGameData( void* a_pData, int a_iLength )
{
if ( a_iLength < 1 || a_iLength > MAXSTRINGLENGTH ) DISCONNECTONCOMMUNICATIONERROR;
char* pcData = (char*) a_pData;
pcData[ a_iLength-1 ] = 0; // Last char should be 0, just making sure..
if ( pcData[0] )
{
m_sLatestGameData = pcData;
}
}
const char* CMortalNetworkImpl::GetLatestGameData()
{
return m_sLatestGameData.c_str();
}
struct SKeystrokePackage
{
char cKey;
bool bPressed;
};
void CMortalNetworkImpl::SendKeystroke( int a_iKey, bool a_bPressed )
{
CHECKCONNECTION;
SKeystrokePackage oPackage;
oPackage.cKey = a_iKey;
oPackage.bPressed = a_bPressed;
SendRawData( 'K', &oPackage, sizeof( oPackage) );
}
void CMortalNetworkImpl::ReceiveKeystroke( void* a_pData, int a_iLength )
{
if ( a_iLength != (int)sizeof(SKeystrokePackage) ) DISCONNECTONCOMMUNICATIONERROR;
SKeystrokePackage* poPackage = (SKeystrokePackage*) a_pData;
m_aiKeystrokes.push_back( poPackage->cKey );
m_abKeystrokes.push_back( poPackage->bPressed );
}
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;
}
struct SGameTimePackage
{
int iGameTime;
int iGamePhase;
};
void CMortalNetworkImpl::SendGameTime( int a_iGameTime, int a_iGamePhase )
{
CHECKCONNECTION;
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::GetGameTime()
{
return m_iGameTime;
}
int CMortalNetworkImpl::GetGamePhase()
{
return m_iGamePhase;
}
void CMortalNetworkImpl::SendHurryup( int a_iHurryUpCode )
{
CHECKCONNECTION;
int iPackage = SDL_SwapBE32( a_iHurryUpCode );
SendRawData( 'H', &iPackage, sizeof(int) );
}
void CMortalNetworkImpl::ReceiveHurryup( void* a_pData, int a_iLength )
{
if ( a_iLength != sizeof(int) ) DISCONNECTONCOMMUNICATIONERROR;
m_iHurryupCode = SDL_SwapBE32( *((int*)a_pData) );
}
int CMortalNetworkImpl::GetHurryup()
{
int iRetval = m_iHurryupCode;
m_iHurryupCode = -1;
return iRetval;
}
struct SRoundOrder
{
int iWhoWon;
bool bGameOver;
};
void CMortalNetworkImpl::SendRoundOver( int a_iWhoWon, bool a_bGameOver )
{
CHECKCONNECTION;
SRoundOrder oPackage;
oPackage.iWhoWon = a_iWhoWon;
oPackage.bGameOver = a_bGameOver;
SendRawData( 'O', &oPackage, sizeof(SRoundOrder) );
if ( a_bGameOver )
{
m_enState = NS_CHARACTER_SELECTION;
}
}
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 m_bGameOver;
}
int CMortalNetworkImpl::GetWhoWon()
{
return m_iWhoWon;
}
diff --git a/src/State.cpp b/src/State.cpp
index 4749812..09d165a 100644
--- a/src/State.cpp
+++ b/src/State.cpp
@@ -1,269 +1,278 @@
/***************************************************************************
State.cpp - description
-------------------
begin : Mon Aug 12 2003
copyright : (C) 2003 by upi
email : upi@apocalypse.rulez.org
***************************************************************************/
#include "../config.h"
#include "SDL.h"
#include "common.h"
#include "State.h"
#include <string>
#include <fstream>
+#ifndef _WINDOWS
#include <unistd.h>
+#endif
#include "Backend.h"
#include "MszPerl.h"
extern PerlInterpreter* my_perl;
SState g_oState;
std::string GetConfigHeader()
{
std::string sHeader( "Simple config file " );
sHeader += PACKAGE " " VERSION;
return sHeader;
}
std::string GetConfigFilename()
{
#ifdef _WINDOWS
if ( NULL != g_oState.m_pcArgv0 )
{
return std::string(g_oState.m_pcArgv0) + ".ini";
}
return "c:\\openmortal.ini";
#else
return std::string(getenv("HOME")) + "/.openmortalrc";
#endif
}
SState::SState()
{
m_enGameMode = IN_DEMO;
m_bQuitFlag = false;
m_pcArgv0 = NULL;
m_iGameTime = 60;
m_iHitPoints = 100;
m_iGameSpeed = 12;
#ifdef _WINDOWS
#ifdef _DEBUG
m_bFullscreen = false;
#else
m_bFullscreen = true;
#endif
#else
m_bFullscreen = false;
#endif
m_iChannels = 2;
m_iMixingRate = MIX_DEFAULT_FREQUENCY;
m_iMixingBits = 2;
m_iMusicVolume = 50;
m_iSoundVolume = 100;
static const int aiDefaultKeys[2][9] = {
{ SDLK_UP, SDLK_DOWN, SDLK_LEFT, SDLK_RIGHT, SDLK_PAGEDOWN,
SDLK_DELETE, SDLK_INSERT, SDLK_END, SDLK_HOME },
{ SDLK_w, SDLK_s, SDLK_a, SDLK_d, SDLK_x,
SDLK_f, SDLK_r, SDLK_g, SDLK_t }
};
for ( int i=0; i<2; ++i )
for ( int j=0; j<9; ++j )
m_aiPlayerKeys[i][j] = aiDefaultKeys[i][j];
// Read the locale from the operating system
char* pcLocale = setlocale( LC_CTYPE, NULL );
debug( "The locale returned by the operating system is '%s'\n", pcLocale ? pcLocale : "NULL" );
if ( NULL == pcLocale
|| strcmp( pcLocale, "C") == 0 )
{
// Try the 'GETENV' method
pcLocale = getenv( "LANG" );
debug( "The LANG envvar is '%s'\n", pcLocale ? pcLocale : "NULL" );
}
if ( NULL != pcLocale )
{
strncpy( m_acLanguage, pcLocale, 2 );
m_acLanguage[2] = 0;
}
else
{
strcpy( m_acLanguage, "en" );
}
strcpy( m_acLatestServer, "apocalypse.rulez.org" );
+
+#ifdef _WINDOWS
+ m_acNick[0] = 0;
+ DWORD iLen = 127;
+ GetUserName( m_acNick, &iLen )
+#else
int iResult = getlogin_r( m_acNick, 127 );
if ( iResult )
{
debug( "getlogin_r failed: %d\n", iResult );
strcpy( m_acNick, getenv("USER") );
- if ( !m_acNick[0] )
- {
- strcpy( m_acNick, "Mortal"); // Last-ditch default..
- }
+ }
+#endif
+ if ( !m_acNick[0] )
+ {
+ strcpy( m_acNick, "Mortal"); // Last-ditch default..
}
m_bServer = false;
};
void SState::ToggleFullscreen()
{
m_bFullscreen = !m_bFullscreen;
bool bPaletted = ( gamescreen->format->BitsPerPixel <= 8 );
SDL_Color aoPalette[256];
int iNumColors = 0;
if ( bPaletted )
{
iNumColors = gamescreen->format->palette->ncolors;
if ( iNumColors > 256 ) iNumColors = 256;
for ( int i=0; i<iNumColors; ++i )
{
aoPalette[i].r = gamescreen->format->palette->colors[i].r;
aoPalette[i].g = gamescreen->format->palette->colors[i].g;
aoPalette[i].b = gamescreen->format->palette->colors[i].b;
aoPalette[i].unused = 0;
}
}
gamescreen = SDL_SetVideoMode( gamescreen->w, gamescreen->h,
gamescreen->format->BitsPerPixel,
m_bFullscreen ? SDL_FULLSCREEN : SDL_SWSURFACE );
if ( bPaletted )
{
SDL_SetPalette( gamescreen, SDL_LOGPAL | SDL_PHYSPAL, aoPalette, 0, iNumColors );
}
}
void SState::SetLanguage( const char* a_pcLanguage )
{
if ( m_acLanguage != a_pcLanguage )
{
strncpy( m_acLanguage, a_pcLanguage, 9 );
m_acLanguage[9] = 0;
}
g_oBackend.PerlEvalF( "SetLanguage('%s');", m_acLanguage );
SV* poSv = get_sv("LanguageNumber", FALSE);
if (poSv)
{
m_iLanguageCode = SvIV( poSv );
}
else
{
m_iLanguageCode = 0;
}
}
void SState::SetServer( const char* a_pcServer )
{
if ( a_pcServer )
{
strncpy( m_acLatestServer, a_pcServer, 255 );
m_acLatestServer[255] = 0;
m_bServer = false;
}
else
{
m_bServer = true;
}
}
void SState::Load()
{
std::string sFilename = GetConfigFilename();
g_oBackend.PerlEvalF( "ParseConfig('%s');", sFilename.c_str() );
SV* poSv;
poSv = get_sv("GAMETIME", FALSE); if (poSv) m_iGameTime = SvIV( poSv );
poSv = get_sv("HITPOINTS", FALSE); if (poSv) m_iHitPoints = SvIV( poSv );
poSv = get_sv("GAMESPEED", FALSE); if (poSv) m_iGameSpeed = SvIV( poSv );
poSv = get_sv("FULLSCREEN", FALSE); if (poSv) m_bFullscreen = SvIV( poSv );
poSv = get_sv("CHANNELS", FALSE); if (poSv) m_iChannels = SvIV( poSv );
poSv = get_sv("MIXINGRATE", FALSE); if (poSv) m_iMixingRate = SvIV( poSv );
poSv = get_sv("MIXINGBITS", FALSE); if (poSv) m_iMixingBits = SvIV( poSv );
poSv = get_sv("MUSICVOLUME", FALSE); if (poSv) m_iMusicVolume = SvIV( poSv );
poSv = get_sv("SOUNDVOLUME", FALSE); if (poSv) m_iSoundVolume = SvIV( poSv );
poSv = get_sv("LANGUAGE", FALSE); if (poSv) { strncpy( m_acLanguage, SvPV_nolen( poSv ), 9 ); m_acLanguage[9] = 0; }
poSv = get_sv("LATESTSERVER", FALSE); if (poSv) { strncpy( m_acLatestServer, SvPV_nolen( poSv ), 255 ); m_acLatestServer[255] = 0; }
poSv = get_sv("SERVER", FALSE); if (poSv) m_bServer = SvIV( poSv );
poSv = get_sv("NICK", FALSE); if (poSv) { strncpy( m_acNick, SvPV_nolen( poSv ), 127 ); m_acNick[127] = 0; }
char pcBuffer[1024];
for ( int i=0; i<2; ++i )
{
for ( int j=0; j<9; ++j )
{
sprintf( pcBuffer, "PLAYER%dKEY%d", i, j );
poSv = get_sv(pcBuffer, FALSE); if (poSv) m_aiPlayerKeys[i][j] = SvIV( poSv );
}
}
}
void SState::Save()
{
std::string sFilename = GetConfigFilename();
std::ofstream oStream( sFilename.c_str(), std::ios_base::out | std::ios_base::trunc );
if ( oStream.rdstate() & std::ios::failbit )
{
debug( "Unable to open config file: %s\n", sFilename.c_str() );
return;
}
oStream << GetConfigHeader() << '\n';
oStream << "GAMETIME=" << m_iGameTime << '\n';
oStream << "HITPOINTS=" << m_iHitPoints << '\n';
oStream << "GAMESPEED=" << m_iGameSpeed << '\n';
oStream << "FULLSCREEN=" << m_bFullscreen << '\n';
oStream << "CHANNELS=" << m_iChannels << '\n';
oStream << "MIXINGRATE=" << m_iMixingRate << '\n';
oStream << "MIXINGBITS=" << m_iMixingBits << '\n';
oStream << "MUSICVOLUME=" << m_iMusicVolume << '\n';
oStream << "SOUNDVOLUME=" << m_iSoundVolume << '\n';
oStream << "LANGUAGE=" << m_acLanguage << '\n';
oStream << "LATESTSERVER=" << m_acLatestServer << '\n';
oStream << "SERVER=" << m_bServer << '\n';
oStream << "NICK=" << m_acNick << '\n';
for ( int i=0; i<2; ++i )
{
for ( int j=0; j<9; ++j )
{
oStream << "PLAYER" <<i<< "KEY" <<j<< '=' << m_aiPlayerKeys[i][j] << '\n';
}
}
oStream.flush();
oStream.close();
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jun 17, 9:30 PM (1 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
69865
Default Alt Text
(57 KB)

Event Timeline