/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Patrick Boenzli */ #define DEBUG_MODULE_GAME_RULES #include #include "multiplayer_team_deathmatch.h" #include "util/loading/load_param.h" #include "util/loading/factory.h" #include "render2D/image_plane.h" #include "state.h" #include "class_list.h" #include "player.h" #include "playable.h" #include "space_ships/space_ship.h" #include "shared_network_data.h" #include "terrain_entity.h" #include "class_list.h" #include "space_ships/space_ship.h" #include "network_game_manager.h" #include "event_handler.h" #include "glgui.h" #include "story_entity.h" using namespace std; CREATE_FACTORY(MultiplayerTeamDeathmatch, CL_MULTIPLAYER_TEAM_DEATHMATCH); /** * constructor */ MultiplayerTeamDeathmatch::MultiplayerTeamDeathmatch(const TiXmlElement* root) : NetworkGameRules(root) { this->setClassID(CL_MULTIPLAYER_TEAM_DEATHMATCH, "MultiplayerTeamDeathmatch"); this->bLocalPlayerDead = false; this->deathTimeout = 10.0f; // 5 seconds this->timeout = 0.0f; this->numTeams = 2; this->currentGameState = GAMESTATE_PRE_GAME; this->gameStateTimer = 10.0f; this->bShowTeamChange = false; this->box = NULL; this->deathScreen = new ImagePlane(); this->deathScreen->setSize(State::getResX()/4.0, State::getResY()/4.0); this->deathScreen->setAbsCoor2D(State::getResX()/2.0f, State::getResY()/2.0f); this->deathScreen->setVisibility(false); this->localPlayer = State::getPlayer(); if( root != NULL) this->loadParams(root); subscribeEvent( ES_GAME, SDLK_o ); } /** * decontsructor */ MultiplayerTeamDeathmatch::~MultiplayerTeamDeathmatch() { if( this->deathScreen) delete this->deathScreen; unsubscribeEvent( ES_GAME, SDLK_o ); } void MultiplayerTeamDeathmatch::loadParams(const TiXmlElement* root) { GameRules::loadParams(root) ; LoadParam(root, "death-penalty-timeout", this, MultiplayerTeamDeathmatch, setDeathPenaltyTimeout) .describe("sets the time in seconds a player has to wait for respawn"); LoadParam(root, "max-kills", this, MultiplayerTeamDeathmatch, setMaxKills) .describe("sets the maximal kills for winning condition"); LoadParam(root, "death-screen-image", this, MultiplayerTeamDeathmatch, setDeathScreen) .describe("sets the death screen image"); LoadParam(root, "num-teams", this, MultiplayerTeamDeathmatch, setNumTeams) .describe("sets number of teams"); } void MultiplayerTeamDeathmatch::setDeathScreen(const std::string& imageName) { if( this->deathScreen) this->deathScreen->setTexture(imageName); } /** * called when the player enters the game * @param player the spawned player */ void MultiplayerTeamDeathmatch::onPlayerSpawn() { this->bLocalPlayerDead = false; this->deathScreen->setVisibility(false); } /** * when the player is killed * @param player the killed player */ void MultiplayerTeamDeathmatch::onPlayerDeath() { this->bLocalPlayerDead = true; this->deathScreen->setVisibility(true); } /** * time tick * @param dt time */ void MultiplayerTeamDeathmatch::tick(float dt) { //on client side hostId is -1 until hanshake finished if ( SharedNetworkData::getInstance()->getHostID() < 0 ) return; if ( currentGameState == GAMESTATE_PRE_GAME || currentGameState == GAMESTATE_GAME ) { if ( PlayerStats::getStats( SharedNetworkData::getInstance()->getHostID() ) && box == NULL && (PlayerStats::getStats( SharedNetworkData::getInstance()->getHostID() )->getPreferedTeamId() == TEAM_NOTEAM || bShowTeamChange ) ) { EventHandler::getInstance()->pushState( ES_MENU ); OrxGui::GLGuiHandler::getInstance()->activateCursor(); box = new OrxGui::GLGuiBox(); box->setAbsCoor2D( 300, 100 ); OrxGui::GLGuiPushButton * buttonSpectator = new OrxGui::GLGuiPushButton("Spectator"); box->pack( buttonSpectator ); buttonSpectator->connect(SIGNAL(buttonSpectator, released), this, SLOT(MultiplayerTeamDeathmatch, onButtonSpectator)); OrxGui::GLGuiPushButton * buttonRandom = new OrxGui::GLGuiPushButton("Random"); box->pack( buttonRandom ); buttonRandom->connect(SIGNAL(buttonRandom, released), this, SLOT(MultiplayerTeamDeathmatch, onButtonRandom)); OrxGui::GLGuiPushButton * buttonTeam0 = new OrxGui::GLGuiPushButton("Blue Team"); box->pack( buttonTeam0 ); buttonTeam0->connect(SIGNAL(buttonTeam0, released), this, SLOT(MultiplayerTeamDeathmatch, onButtonTeam0)); OrxGui::GLGuiPushButton * buttonTeam1 = new OrxGui::GLGuiPushButton("Red Team"); box->pack( buttonTeam1 ); buttonTeam1->connect(SIGNAL(buttonTeam1, released), this, SLOT(MultiplayerTeamDeathmatch, onButtonTeam1)); if ( bShowTeamChange ) { OrxGui::GLGuiPushButton * buttonCancel = new OrxGui::GLGuiPushButton("Cancel"); box->pack( buttonCancel ); buttonCancel->connect(SIGNAL(buttonCancel, released), this, SLOT(MultiplayerTeamDeathmatch, onButtonCancel)); } OrxGui::GLGuiPushButton * buttonExit = new OrxGui::GLGuiPushButton("Exit"); box->pack( buttonExit ); buttonExit->connect(SIGNAL(buttonExit, released), this, SLOT(MultiplayerTeamDeathmatch, onButtonExit)); box->showAll(); } } if ( box != NULL && PlayerStats::getStats( SharedNetworkData::getInstance()->getHostID() ) && PlayerStats::getStats( SharedNetworkData::getInstance()->getHostID() )->getPreferedTeamId() != TEAM_NOTEAM && !bShowTeamChange ) { delete box; box = NULL; OrxGui::GLGuiHandler::getInstance()->deactivateCursor( true ); EventHandler::getInstance()->popState(); } if ( box != NULL ) { OrxGui::GLGuiHandler::getInstance()->tick( dt ); } assignPlayable(); if ( !SharedNetworkData::getInstance()->isGameServer() ) return; gameStateTimer -= dt; //PRINTF(0)("TICK %f\n", gameStateTimer); if ( currentGameState != GAMESTATE_GAME && gameStateTimer < 0 ) nextGameState(); this->currentGameState = NetworkGameManager::getInstance()->getGameState(); if ( currentGameState == GAMESTATE_GAME ) { handleTeamChanges(); } this->calculateTeamScore(); this->checkGameRules(); // is the local player dead and inactive if( unlikely(this->bLocalPlayerDead)) { this->timeout += dt; PRINTF(0)("TICK DEATH: %f of %f\n", this->timeout, this->deathTimeout); // long enough dead? if( this->timeout >= this->deathTimeout) { this->timeout = 0.0f; // respawn PRINTF(0)("RESPAWN\n"); (State::getPlayer())->getPlayable()->respawn(); } } } /** * draws the stuff */ void MultiplayerTeamDeathmatch::draw() { if( unlikely( this->bLocalPlayerDead)) { } } /** * check the game rules for consistency */ void MultiplayerTeamDeathmatch::checkGameRules() { if ( !SharedNetworkData::getInstance()->isGameServer() ) return; // check for max killing count for ( int i = 0; i= maxKills ) { //team i wins //TODO } } } /** * find group for new player * @return group id */ int MultiplayerTeamDeathmatch::getTeamForNewUser() { return TEAM_NOTEAM; } ClassID MultiplayerTeamDeathmatch::getPlayableClassId( int userId, int team ) { if ( team == TEAM_NOTEAM || team == TEAM_SPECTATOR ) return CL_SPECTATOR; if ( team == 0 || team == 1 ) return CL_SPACE_SHIP; assert( false ); } std::string MultiplayerTeamDeathmatch::getPlayableModelFileName( int userId, int team, ClassID classId ) { if ( team == 0 ) return "models/ships/reap_#.obj"; else if ( team == 1 ) return "models/ships/fighter.obj"; else return ""; } /** * calculate team score */ void MultiplayerTeamDeathmatch::calculateTeamScore( ) { teamScore.clear(); for ( int i = 0; i * list = ClassList::getList( CL_PLAYER_STATS ); if ( !list ) return; for ( std::list::const_iterator it = list->begin(); it != list->end(); it++ ) { PlayerStats & stats = *dynamic_cast(*it); if ( stats.getTeamId() >= 0 ) { teamScore[stats.getTeamId()] += stats.getScore(); } } } /** * get team for player who choose to join random team * @return smallest team */ int MultiplayerTeamDeathmatch::getRandomTeam( ) { std::map playersInTeam; for ( int i = 0; i * list = ClassList::getList( CL_PLAYER_STATS ); if ( !list ) return 0; for ( std::list::const_iterator it = list->begin(); it != list->end(); it++ ) { PlayerStats & stats = *dynamic_cast(*it); if ( stats.getTeamId() >= 0 ) { playersInTeam[stats.getTeamId()]++; } } int minPlayers = 0xFFFF; int minTeam = -1; for ( int i = 0; isetGameState( GAMESTATE_GAME ); return; } if ( currentGameState == GAMESTATE_GAME ) { NetworkGameManager::getInstance()->setGameState( GAMESTATE_POST_GAME ); return; } if ( currentGameState == GAMESTATE_POST_GAME ) { //TODO end game return; } } void MultiplayerTeamDeathmatch::handleTeamChanges( ) { const std::list * list = ClassList::getList( CL_PLAYER_STATS ); if ( !list ) return; //first server players with choices for ( std::list::const_iterator it = list->begin(); it != list->end(); it++ ) { PlayerStats & stats = *dynamic_cast(*it); if ( stats.getTeamId() != stats.getPreferedTeamId() ) { if ( stats.getPreferedTeamId() == TEAM_SPECTATOR || ( stats.getPreferedTeamId() >= 0 && stats.getPreferedTeamId() < numTeams ) ) { teamChange( stats.getUserId() ); } } } //now serve player who want join a random team for ( std::list::const_iterator it = list->begin(); it != list->end(); it++ ) { PlayerStats & stats = *dynamic_cast(*it); if ( stats.getTeamId() != stats.getPreferedTeamId() ) { if ( stats.getPreferedTeamId() == TEAM_RANDOM ) { stats.setPreferedTeamId( getRandomTeam() ); teamChange( stats.getUserId() ); } } } } void MultiplayerTeamDeathmatch::teamChange( int userId ) { assert( PlayerStats::getStats( userId ) ); PlayerStats & stats = *(PlayerStats::getStats( userId )); stats.setTeamId( stats.getPreferedTeamId() ); Playable * oldPlayable = stats.getPlayable(); ClassID playableClassId = getPlayableClassId( userId, stats.getPreferedTeamId() ); std::string playableModel = getPlayableModelFileName( userId, stats.getPreferedTeamId(), playableClassId ); BaseObject * bo = Factory::fabricate( playableClassId ); assert( bo != NULL ); assert( bo->isA( CL_PLAYABLE ) ); Playable & playable = *(dynamic_cast(bo)); playable.loadModel( playableModel ); playable.setOwner( userId ); playable.setUniqueID( SharedNetworkData::getInstance()->getNewUniqueID() ); playable.setSynchronized( true ); stats.setTeamId( stats.getPreferedTeamId() ); stats.setPlayableClassId( playableClassId ); stats.setPlayableUniqueId( playable.getUniqueID() ); stats.setModelFileName( playableModel ); if ( oldPlayable ) { //if ( userId == SharedNetworkData::getInstance()->getHostID() ) // State::getPlayer()->setPlayable( NULL ); delete oldPlayable; } } void MultiplayerTeamDeathmatch::onButtonExit( ) { State::getCurrentStoryEntity()->stop(); this->bShowTeamChange = false; } void MultiplayerTeamDeathmatch::onButtonRandom( ) { NetworkGameManager::getInstance()->prefereTeam( TEAM_RANDOM ); this->bShowTeamChange = false; } void MultiplayerTeamDeathmatch::onButtonTeam0( ) { NetworkGameManager::getInstance()->prefereTeam( 0 ); this->bShowTeamChange = false; } void MultiplayerTeamDeathmatch::onButtonTeam1( ) { NetworkGameManager::getInstance()->prefereTeam( 1 ); this->bShowTeamChange = false; } void MultiplayerTeamDeathmatch::onButtonSpectator( ) { NetworkGameManager::getInstance()->prefereTeam( TEAM_SPECTATOR ); this->bShowTeamChange = false; } void MultiplayerTeamDeathmatch::assignPlayable( ) { if ( PlayerStats::getStats( SharedNetworkData::getInstance()->getHostID() ) ) PlayerStats::getStats( SharedNetworkData::getInstance()->getHostID() )->getPlayable(); } /** * function that processes events from the handler * @param event: the event * @todo replace SDLK_o with something from KeyMapper */ void MultiplayerTeamDeathmatch::process( const Event & event ) { if ( event.type == SDLK_o ) { if ( event.bPressed ) this->bShowTeamChange = true; } } void MultiplayerTeamDeathmatch::onButtonCancel( ) { this->bShowTeamChange = false; } /** * this method is called by NetworkGameManger when he recieved a chat message * @param userId senders user id * @param message message string * @param messageType some int */ void MultiplayerTeamDeathmatch::handleChatMessage( int userId, const std::string & message, int messageType ) { std::string name = "unknown"; if ( PlayerStats::getStats( userId ) ) { name = PlayerStats::getStats( userId )->getNickName(); } PRINTF(0)("CHATMESSAGE %s: %s\n", name.c_str(), message.c_str() ); }