Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

started implementing frag table

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