Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/trunk/src/util/multiplayer_team_deathmatch.cc @ 8771

Last change on this file since 8771 was 8717, checked in by bensch, 18 years ago

merged the gui back

File size: 15.2 KB
RevLine 
[7034]1/*
2   orxonox - the future of 3D-vertical-scrollers
3
4   Copyright (C) 2004 orx
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2, or (at your option)
9   any later version.
10
11### File Specific:
12   main-programmer: Patrick Boenzli
13*/
14
15#define DEBUG_MODULE_GAME_RULES
16
[8068]17#include <map>
18
[7034]19#include "multiplayer_team_deathmatch.h"
20
[7193]21#include "util/loading/load_param.h"
22#include "util/loading/factory.h"
[7034]23
[7810]24#include "render2D/image_plane.h"
[7039]25#include "state.h"
[7101]26#include "class_list.h"
[7034]27
[7044]28#include "player.h"
29#include "playable.h"
[7101]30#include "space_ships/space_ship.h"
[7039]31
[7116]32
[7101]33#include "shared_network_data.h"
[7116]34#include "terrain.h"
35#include "class_list.h"
36#include "space_ships/space_ship.h"
[7044]37
[8068]38#include "network_game_manager.h"
[7101]39
[8147]40#include "event_handler.h"
[8068]41
[8147]42#include "glgui.h"
43
44#include "story_entity.h"
45
[8708]46#include "shell_command.h"
[8147]47
[8708]48
[7034]49using namespace std;
50
51
[7035]52CREATE_FACTORY(MultiplayerTeamDeathmatch, CL_MULTIPLAYER_TEAM_DEATHMATCH);
53
54
[7034]55/**
56 * constructor
57 */
[7035]58MultiplayerTeamDeathmatch::MultiplayerTeamDeathmatch(const TiXmlElement* root)
[8068]59  : NetworkGameRules(root)
[7035]60{
61  this->setClassID(CL_MULTIPLAYER_TEAM_DEATHMATCH, "MultiplayerTeamDeathmatch");
[7034]62
[7037]63  this->bLocalPlayerDead = false;
64  this->deathTimeout = 10.0f;     // 5 seconds
[7082]65  this->timeout = 0.0f;
[8068]66  this->numTeams = 2;
67  this->currentGameState = GAMESTATE_PRE_GAME;
68  this->gameStateTimer = 10.0f;
[8623]69  this->bShowTeamChange = false;
[8717]70
[8147]71  this->box = NULL;
[7040]72
[7810]73  this->deathScreen = new ImagePlane();
[7044]74  this->deathScreen->setSize(State::getResX()/4.0, State::getResY()/4.0);
[7040]75  this->deathScreen->setAbsCoor2D(State::getResX()/2.0f, State::getResY()/2.0f);
[7044]76  this->deathScreen->setVisibility(false);
[7037]77
[7044]78  this->localPlayer = State::getPlayer();
79
[7035]80  if( root != NULL)
81    this->loadParams(root);
[8717]82
[8623]83  subscribeEvent( ES_GAME, SDLK_o );
[8708]84  subscribeEvent( ES_GAME, SDLK_TAB );
[8717]85
[8708]86  this->notifier = new OrxGui::GLGuiNotifier();
87  this->notifier->show();
88  this->notifier->setAbsCoor2D(5, 30);
89  this->notifier->setFadeAge( 6.0 );
90  this->notifier->setHideAge( 8.0 );
91  this->input = new OrxGui::GLGuiInputLine();
92  this->input->setAbsCoor2D(180, 5);
93  this->input->connect(SIGNAL(input, enterPushed), this, SLOT(MultiplayerTeamDeathmatch, onInputEnter));
[7035]94}
95
[7034]96/**
97 * decontsructor
98 */
99MultiplayerTeamDeathmatch::~MultiplayerTeamDeathmatch()
[7044]100{
101  if( this->deathScreen)
102    delete this->deathScreen;
[8717]103
[8623]104  unsubscribeEvent( ES_GAME, SDLK_o );
[8708]105  unsubscribeEvent( ES_GAME, SDLK_TAB );
[8717]106
[8708]107  if ( this->notifier )
108  {
109    delete this->notifier;
110    this->notifier = NULL;
111  }
[8717]112
[8708]113  if ( this->input )
114  {
115    delete this->input;
116    this->input = NULL;
117  }
[7044]118}
[7034]119
120
121
122void MultiplayerTeamDeathmatch::loadParams(const TiXmlElement* root)
[7035]123{
[7040]124  GameRules::loadParams(root) ;
[7037]125
126  LoadParam(root, "death-penalty-timeout", this, MultiplayerTeamDeathmatch, setDeathPenaltyTimeout)
127      .describe("sets the time in seconds a player has to wait for respawn");
128
129  LoadParam(root, "max-kills", this, MultiplayerTeamDeathmatch, setMaxKills)
130      .describe("sets the maximal kills for winning condition");
[7039]131
132  LoadParam(root, "death-screen-image", this, MultiplayerTeamDeathmatch, setDeathScreen)
133      .describe("sets the death screen image");
[8717]134
[8068]135  LoadParam(root, "num-teams", this, MultiplayerTeamDeathmatch, setNumTeams)
136      .describe("sets number of teams");
[7039]137
[7035]138}
139
140
[7039]141
[7221]142void MultiplayerTeamDeathmatch::setDeathScreen(const std::string& imageName)
[7039]143{
144  if( this->deathScreen)
145    this->deathScreen->setTexture(imageName);
146}
147
148
149
[7035]150/**
151 * called when the player enters the game
152 * @param player the spawned player
153 */
[7044]154void MultiplayerTeamDeathmatch::onPlayerSpawn()
[7039]155{
[7044]156  this->bLocalPlayerDead = false;
157  this->deathScreen->setVisibility(false);
[7039]158}
[7034]159
[7035]160
161/**
162 * when the player is killed
163 * @param player the killed player
164 */
[7044]165void MultiplayerTeamDeathmatch::onPlayerDeath()
[7039]166{
167  this->bLocalPlayerDead = true;
[7044]168  this->deathScreen->setVisibility(true);
[7039]169}
[7035]170
171
172/**
173 * time tick
174 * @param dt time
175 */
176void MultiplayerTeamDeathmatch::tick(float dt)
[7037]177{
[8147]178  //on client side hostId is -1 until hanshake finished
179  if ( SharedNetworkData::getInstance()->getHostID() < 0 )
180    return;
[8717]181
[8623]182  if ( currentGameState == GAMESTATE_PRE_GAME || currentGameState == GAMESTATE_GAME )
[8147]183  {
184    if ( PlayerStats::getStats( SharedNetworkData::getInstance()->getHostID() )
185         && box == NULL
[8717]186         &&  (PlayerStats::getStats( SharedNetworkData::getInstance()->getHostID() )->getPreferedTeamId() == TEAM_NOTEAM
[8623]187         || bShowTeamChange )
[8717]188
[8147]189       )
190    {
191      EventHandler::getInstance()->pushState( ES_MENU );
[8717]192
[8147]193      OrxGui::GLGuiHandler::getInstance()->activateCursor();
[8717]194
[8147]195      box = new OrxGui::GLGuiBox();
196      box->setAbsCoor2D( 300, 100 );
[8717]197
[8147]198      OrxGui::GLGuiPushButton * buttonSpectator = new OrxGui::GLGuiPushButton("Spectator");
199      box->pack( buttonSpectator );
200      buttonSpectator->connect(SIGNAL(buttonSpectator, released), this, SLOT(MultiplayerTeamDeathmatch, onButtonSpectator));
[8717]201
[8147]202      OrxGui::GLGuiPushButton * buttonRandom = new OrxGui::GLGuiPushButton("Random");
203      box->pack( buttonRandom );
204      buttonRandom->connect(SIGNAL(buttonRandom, released), this, SLOT(MultiplayerTeamDeathmatch, onButtonRandom));
[8717]205
[8147]206      OrxGui::GLGuiPushButton * buttonTeam0 = new OrxGui::GLGuiPushButton("Blue Team");
207      box->pack( buttonTeam0 );
208      buttonTeam0->connect(SIGNAL(buttonTeam0, released), this, SLOT(MultiplayerTeamDeathmatch, onButtonTeam0));
[8717]209
[8147]210      OrxGui::GLGuiPushButton * buttonTeam1 = new OrxGui::GLGuiPushButton("Red Team");
211      box->pack( buttonTeam1 );
212      buttonTeam1->connect(SIGNAL(buttonTeam1, released), this, SLOT(MultiplayerTeamDeathmatch, onButtonTeam1));
[8717]213
[8623]214      if ( bShowTeamChange )
215      {
216        OrxGui::GLGuiPushButton * buttonCancel = new OrxGui::GLGuiPushButton("Cancel");
217        box->pack( buttonCancel );
218        buttonCancel->connect(SIGNAL(buttonCancel, released), this, SLOT(MultiplayerTeamDeathmatch, onButtonCancel));
219      }
[8717]220
[8147]221      OrxGui::GLGuiPushButton * buttonExit = new OrxGui::GLGuiPushButton("Exit");
222      box->pack( buttonExit );
223      buttonExit->connect(SIGNAL(buttonExit, released), this, SLOT(MultiplayerTeamDeathmatch, onButtonExit));
[8717]224
[8147]225      box->showAll();
226    }
227  }
228
229  if ( box != NULL
230       && PlayerStats::getStats( SharedNetworkData::getInstance()->getHostID() )
[8717]231       && PlayerStats::getStats( SharedNetworkData::getInstance()->getHostID() )->getPreferedTeamId() != TEAM_NOTEAM
[8623]232       && !bShowTeamChange
[8147]233     )
234  {
235    delete box;
236    box = NULL;
[8717]237
[8147]238    OrxGui::GLGuiHandler::getInstance()->deactivateCursor( true );
[8717]239
[8147]240    EventHandler::getInstance()->popState();
241  }
[8717]242
[8147]243  if ( box != NULL )
244  {
245    OrxGui::GLGuiHandler::getInstance()->tick( dt );
246  }
[8717]247
[8147]248  assignPlayable();
[8717]249
[8068]250  if ( !SharedNetworkData::getInstance()->isGameServer() )
251    return;
[8717]252
[8068]253  gameStateTimer -= dt;
[8147]254  //PRINTF(0)("TICK %f\n", gameStateTimer);
[8717]255
[8068]256  if ( currentGameState != GAMESTATE_GAME && gameStateTimer < 0 )
257    nextGameState();
[8717]258
[8068]259  this->currentGameState = NetworkGameManager::getInstance()->getGameState();
[8717]260
[8068]261  if ( currentGameState == GAMESTATE_GAME )
262  {
263    handleTeamChanges();
264  }
[8717]265
[8068]266  this->calculateTeamScore();
[8717]267
[7039]268  this->checkGameRules();
[7035]269
[7039]270  // is the local player dead and inactive
271  if( unlikely(this->bLocalPlayerDead))
272  {
273    this->timeout += dt;
[7088]274    PRINTF(0)("TICK DEATH: %f of %f\n", this->timeout, this->deathTimeout);
[7039]275    // long enough dead?
[7044]276    if( this->timeout >= this->deathTimeout)
[7039]277    {
278      this->timeout = 0.0f;
279      // respawn
[7079]280      PRINTF(0)("RESPAWN\n");
[7044]281      (State::getPlayer())->getPlayable()->respawn();
[7039]282    }
283  }
[7037]284}
[7035]285
[7037]286
[7035]287/**
288 * draws the stuff
289 */
290void MultiplayerTeamDeathmatch::draw()
[7039]291{
292  if( unlikely( this->bLocalPlayerDead))
293  {
[7035]294
[7039]295  }
296}
[7035]297
[7039]298
[7035]299/**
300 * check the game rules for consistency
301 */
302void MultiplayerTeamDeathmatch::checkGameRules()
[7039]303{
[8068]304  if ( !SharedNetworkData::getInstance()->isGameServer() )
305    return;
[8717]306
[8068]307  // check for max killing count
308  for ( int i = 0; i<numTeams; i++ )
309  {
310    if ( teamScore[i] >= maxKills )
311    {
312      //team i wins
313      //TODO
314    }
315  }
316}
[7101]317
[8068]318/**
319 * find group for new player
320 * @return group id
321 */
[8147]322int MultiplayerTeamDeathmatch::getTeamForNewUser()
[8068]323{
324  return TEAM_NOTEAM;
325}
[7101]326
[8147]327ClassID MultiplayerTeamDeathmatch::getPlayableClassId( int userId, int team )
[8068]328{
[8147]329  if ( team == TEAM_NOTEAM || team == TEAM_SPECTATOR )
330    return CL_SPECTATOR;
[8717]331
[8147]332  if ( team == 0 || team == 1 )
333    return CL_SPACE_SHIP;
[8717]334
[8147]335  assert( false );
[8068]336}
[7101]337
[8147]338std::string MultiplayerTeamDeathmatch::getPlayableModelFileName( int userId, int team, ClassID classId )
[8068]339{
[8147]340  if ( team == 0 )
341    return "models/ships/reap_#.obj";
342  else if ( team == 1 )
343    return "models/ships/fighter.obj";
344  else
345    return "";
[8068]346}
347
348/**
349 * calculate team score
350 */
351void MultiplayerTeamDeathmatch::calculateTeamScore( )
352{
353  teamScore.clear();
[8717]354
[8068]355  for ( int i = 0; i<numTeams; i++ )
356    teamScore[i] = 0;
[8717]357
358
[8068]359  const std::list<BaseObject*> * list = ClassList::getList( CL_PLAYER_STATS );
[8717]360
[8068]361  if ( !list )
362    return;
[8717]363
[8068]364  for ( std::list<BaseObject*>::const_iterator it = list->begin(); it != list->end(); it++ )
[7039]365  {
[8068]366    PlayerStats & stats = *dynamic_cast<PlayerStats*>(*it);
367
368    if ( stats.getTeamId() >= 0 )
369    {
370      teamScore[stats.getTeamId()] += stats.getScore();
371    }
[7039]372  }
[8068]373}
374
375/**
376 * get team for player who choose to join random team
377 * @return smallest team
378 */
379int MultiplayerTeamDeathmatch::getRandomTeam( )
380{
381  std::map<int,int> playersInTeam;
[8717]382
[8068]383  for ( int i = 0; i<numTeams; i++ )
384    playersInTeam[i] = 0;
[8717]385
[8068]386  const std::list<BaseObject*> * list = ClassList::getList( CL_PLAYER_STATS );
[8717]387
[8068]388  if ( !list )
389    return 0;
[8717]390
[8068]391  for ( std::list<BaseObject*>::const_iterator it = list->begin(); it != list->end(); it++ )
[7039]392  {
[8068]393    PlayerStats & stats = *dynamic_cast<PlayerStats*>(*it);
394
395    if ( stats.getTeamId() >= 0 )
396    {
397      playersInTeam[stats.getTeamId()]++;
398    }
[7039]399  }
[8717]400
401
[8068]402  int minPlayers = 0xFFFF;
403  int minTeam = -1;
[8717]404
[8068]405  for ( int i = 0; i<numTeams; i++ )
406  {
407    if ( playersInTeam[i] < minPlayers )
408    {
409      minTeam = i;
410      minPlayers = playersInTeam[i];
411    }
412  }
[8717]413
[8068]414  assert( minTeam != -1 );
[8717]415
[8068]416  return minTeam;
417}
[7101]418
[8068]419void MultiplayerTeamDeathmatch::nextGameState( )
420{
421  if ( currentGameState == GAMESTATE_PRE_GAME )
422  {
423    NetworkGameManager::getInstance()->setGameState( GAMESTATE_GAME );
[8717]424
[8068]425    return;
426  }
[8717]427
[8068]428  if ( currentGameState == GAMESTATE_GAME )
429  {
430    NetworkGameManager::getInstance()->setGameState( GAMESTATE_POST_GAME );
[8717]431
[8068]432    return;
433  }
[8717]434
[8068]435  if ( currentGameState == GAMESTATE_POST_GAME )
436  {
437    //TODO end game
[8717]438
[8068]439    return;
440  }
441}
[7118]442
[8068]443void MultiplayerTeamDeathmatch::handleTeamChanges( )
444{
445  const std::list<BaseObject*> * list = ClassList::getList( CL_PLAYER_STATS );
[8717]446
[8068]447  if ( !list )
448    return;
[8717]449
[8068]450  //first server players with choices
451  for ( std::list<BaseObject*>::const_iterator it = list->begin(); it != list->end(); it++ )
[7116]452  {
[8068]453    PlayerStats & stats = *dynamic_cast<PlayerStats*>(*it);
[7118]454
[8068]455    if ( stats.getTeamId() != stats.getPreferedTeamId() )
[7116]456    {
[8147]457      if ( stats.getPreferedTeamId() == TEAM_SPECTATOR || ( stats.getPreferedTeamId() >= 0 && stats.getPreferedTeamId() < numTeams ) )
[7116]458      {
[8068]459        teamChange( stats.getUserId() );
[7116]460      }
461    }
462  }
[8717]463
[8068]464  //now serve player who want join a random team
465  for ( std::list<BaseObject*>::const_iterator it = list->begin(); it != list->end(); it++ )
466  {
467    PlayerStats & stats = *dynamic_cast<PlayerStats*>(*it);
[7101]468
[8068]469    if ( stats.getTeamId() != stats.getPreferedTeamId() )
[7101]470    {
[8068]471      if ( stats.getPreferedTeamId() == TEAM_RANDOM )
[7101]472      {
[8147]473        stats.setPreferedTeamId( getRandomTeam() );
[8068]474        teamChange( stats.getUserId() );
[7101]475      }
476    }
477  }
[8068]478}
[7101]479
[8068]480void MultiplayerTeamDeathmatch::teamChange( int userId )
481{
482  assert( PlayerStats::getStats( userId ) );
483  PlayerStats & stats = *(PlayerStats::getStats( userId ));
[8717]484
[8147]485  stats.setTeamId( stats.getPreferedTeamId() );
[8717]486
[8147]487  Playable * oldPlayable = stats.getPlayable();
[8717]488
489
[8147]490  ClassID playableClassId = getPlayableClassId( userId, stats.getPreferedTeamId() );
491  std::string playableModel = getPlayableModelFileName( userId, stats.getPreferedTeamId(), playableClassId );
[8717]492
[8147]493  BaseObject * bo = Factory::fabricate( playableClassId );
[8717]494
[8147]495  assert( bo != NULL );
496  assert( bo->isA( CL_PLAYABLE ) );
[8717]497
[8147]498  Playable & playable = *(dynamic_cast<Playable*>(bo));
[8717]499
[8147]500  playable.loadModel( playableModel );
501  playable.setOwner( userId );
502  playable.setUniqueID( SharedNetworkData::getInstance()->getNewUniqueID() );
503  playable.setSynchronized( true );
[8717]504
[8147]505  stats.setTeamId( stats.getPreferedTeamId() );
506  stats.setPlayableClassId( playableClassId );
507  stats.setPlayableUniqueId( playable.getUniqueID() );
508  stats.setModelFileName( playableModel );
[8717]509
[8147]510  if ( oldPlayable )
511  {
512    //if ( userId == SharedNetworkData::getInstance()->getHostID() )
513    //  State::getPlayer()->setPlayable( NULL );
514    delete oldPlayable;
515  }
[7039]516}
[7035]517
[8147]518void MultiplayerTeamDeathmatch::onButtonExit( )
519{
520  State::getCurrentStoryEntity()->stop();
[8623]521  this->bShowTeamChange = false;
[8147]522}
[7035]523
[8147]524void MultiplayerTeamDeathmatch::onButtonRandom( )
525{
526  NetworkGameManager::getInstance()->prefereTeam( TEAM_RANDOM );
[8623]527  this->bShowTeamChange = false;
[8147]528}
[7035]529
[8147]530void MultiplayerTeamDeathmatch::onButtonTeam0( )
531{
532  NetworkGameManager::getInstance()->prefereTeam( 0 );
[8623]533  this->bShowTeamChange = false;
[8147]534}
[7035]535
[8147]536void MultiplayerTeamDeathmatch::onButtonTeam1( )
537{
538  NetworkGameManager::getInstance()->prefereTeam( 1 );
[8623]539  this->bShowTeamChange = false;
[8147]540}
[7035]541
[8147]542void MultiplayerTeamDeathmatch::onButtonSpectator( )
543{
544  NetworkGameManager::getInstance()->prefereTeam( TEAM_SPECTATOR );
[8623]545  this->bShowTeamChange = false;
[8147]546}
[7035]547
[8147]548void MultiplayerTeamDeathmatch::assignPlayable( )
549{
550  if ( PlayerStats::getStats( SharedNetworkData::getInstance()->getHostID() ) )
551    PlayerStats::getStats( SharedNetworkData::getInstance()->getHostID() )->getPlayable();
552}
553
[8623]554  /**
555   * function that processes events from the handler
556   * @param event: the event
557   * @todo replace SDLK_o with something from KeyMapper
558   */
559void MultiplayerTeamDeathmatch::process( const Event & event )
560{
561  if ( event.type == SDLK_o )
562  {
563    if ( event.bPressed )
564      this->bShowTeamChange = true;
565  }
[8708]566  else if ( event.type == SDLK_TAB )
567  {
568    if ( !event.bPressed )
569    {
570      EventHandler::getInstance()->pushState( ES_MENU );
571      OrxGui::GLGuiHandler::getInstance()->activateCursor();
572      OrxGui::GLGuiHandler::getInstance()->deactivateCursor();
573      input->show();
[8717]574      input->giveMouseFocus();
[8708]575      input->setText("say ");
576    }
577  }
[8623]578}
[8147]579
[8623]580void MultiplayerTeamDeathmatch::onButtonCancel( )
581{
582  this->bShowTeamChange = false;
583}
[8147]584
585
586
[8623]587/**
588 * this method is called by NetworkGameManger when he recieved a chat message
589 * @param userId senders user id
590 * @param message message string
591 * @param messageType some int
592 */
593void MultiplayerTeamDeathmatch::handleChatMessage( int userId, const std::string & message, int messageType )
594{
595  std::string name = "unknown";
[8717]596
[8623]597  if ( PlayerStats::getStats( userId ) )
598  {
599    name = PlayerStats::getStats( userId )->getNickName();
600  }
[8717]601
[8708]602  PRINTF(0)("CHATMESSAGE %s (%d): %s\n", name.c_str(), userId, message.c_str() );
603  notifier->pushNotifyMessage(name + ": " + message);
[8623]604}
[8147]605
[8708]606void MultiplayerTeamDeathmatch::onInputEnter( const std::string & text )
607{
608  EventHandler::getInstance()->popState();
[8717]609  input->breakMouseFocus();
[8708]610  input->hide();
611  input->setText("");
[8623]612
[8708]613  std::string command = text;
[8717]614
[8708]615  //HACK insert " in say commands so user doesn't have to type them
616  if ( command.length() >= 4 && command[0] == 's' && command[1] == 'a' && command[2] == 'y' && command[3] == ' ' )
617  {
618    command.insert( 4, "\"" );
619    command = command + "\"";
620  }
[8623]621
[8708]622  OrxShell::ShellCommand::execute( command );
623}
[8623]624
[8708]625
626
627
Note: See TracBrowser for help on using the repository browser.