Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/network/src/util/multiplayer_team_deathmatch.cc @ 8772

Last change on this file since 8772 was 8772, checked in by rennerc, 18 years ago

removed old deathScreen from MultiplayerTeamDeathmatch

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