Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

showing/hiding frags works now

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