Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

merged the gui back

File size: 15.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
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
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));
94}
95
96/**
97 * decontsructor
98 */
99MultiplayerTeamDeathmatch::~MultiplayerTeamDeathmatch()
100{
101  if( this->deathScreen)
102    delete this->deathScreen;
103
104  unsubscribeEvent( ES_GAME, SDLK_o );
105  unsubscribeEvent( ES_GAME, SDLK_TAB );
106
107  if ( this->notifier )
108  {
109    delete this->notifier;
110    this->notifier = NULL;
111  }
112
113  if ( this->input )
114  {
115    delete this->input;
116    this->input = NULL;
117  }
118}
119
120
121
122void MultiplayerTeamDeathmatch::loadParams(const TiXmlElement* root)
123{
124  GameRules::loadParams(root) ;
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");
131
132  LoadParam(root, "death-screen-image", this, MultiplayerTeamDeathmatch, setDeathScreen)
133      .describe("sets the death screen image");
134
135  LoadParam(root, "num-teams", this, MultiplayerTeamDeathmatch, setNumTeams)
136      .describe("sets number of teams");
137
138}
139
140
141
142void MultiplayerTeamDeathmatch::setDeathScreen(const std::string& imageName)
143{
144  if( this->deathScreen)
145    this->deathScreen->setTexture(imageName);
146}
147
148
149
150/**
151 * called when the player enters the game
152 * @param player the spawned player
153 */
154void MultiplayerTeamDeathmatch::onPlayerSpawn()
155{
156  this->bLocalPlayerDead = false;
157  this->deathScreen->setVisibility(false);
158}
159
160
161/**
162 * when the player is killed
163 * @param player the killed player
164 */
165void MultiplayerTeamDeathmatch::onPlayerDeath()
166{
167  this->bLocalPlayerDead = true;
168  this->deathScreen->setVisibility(true);
169}
170
171
172/**
173 * time tick
174 * @param dt time
175 */
176void MultiplayerTeamDeathmatch::tick(float dt)
177{
178  //on client side hostId is -1 until hanshake finished
179  if ( SharedNetworkData::getInstance()->getHostID() < 0 )
180    return;
181
182  if ( currentGameState == GAMESTATE_PRE_GAME || currentGameState == GAMESTATE_GAME )
183  {
184    if ( PlayerStats::getStats( SharedNetworkData::getInstance()->getHostID() )
185         && box == NULL
186         &&  (PlayerStats::getStats( SharedNetworkData::getInstance()->getHostID() )->getPreferedTeamId() == TEAM_NOTEAM
187         || bShowTeamChange )
188
189       )
190    {
191      EventHandler::getInstance()->pushState( ES_MENU );
192
193      OrxGui::GLGuiHandler::getInstance()->activateCursor();
194
195      box = new OrxGui::GLGuiBox();
196      box->setAbsCoor2D( 300, 100 );
197
198      OrxGui::GLGuiPushButton * buttonSpectator = new OrxGui::GLGuiPushButton("Spectator");
199      box->pack( buttonSpectator );
200      buttonSpectator->connect(SIGNAL(buttonSpectator, released), this, SLOT(MultiplayerTeamDeathmatch, onButtonSpectator));
201
202      OrxGui::GLGuiPushButton * buttonRandom = new OrxGui::GLGuiPushButton("Random");
203      box->pack( buttonRandom );
204      buttonRandom->connect(SIGNAL(buttonRandom, released), this, SLOT(MultiplayerTeamDeathmatch, onButtonRandom));
205
206      OrxGui::GLGuiPushButton * buttonTeam0 = new OrxGui::GLGuiPushButton("Blue Team");
207      box->pack( buttonTeam0 );
208      buttonTeam0->connect(SIGNAL(buttonTeam0, released), this, SLOT(MultiplayerTeamDeathmatch, onButtonTeam0));
209
210      OrxGui::GLGuiPushButton * buttonTeam1 = new OrxGui::GLGuiPushButton("Red Team");
211      box->pack( buttonTeam1 );
212      buttonTeam1->connect(SIGNAL(buttonTeam1, released), this, SLOT(MultiplayerTeamDeathmatch, onButtonTeam1));
213
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      }
220
221      OrxGui::GLGuiPushButton * buttonExit = new OrxGui::GLGuiPushButton("Exit");
222      box->pack( buttonExit );
223      buttonExit->connect(SIGNAL(buttonExit, released), this, SLOT(MultiplayerTeamDeathmatch, onButtonExit));
224
225      box->showAll();
226    }
227  }
228
229  if ( box != NULL
230       && PlayerStats::getStats( SharedNetworkData::getInstance()->getHostID() )
231       && PlayerStats::getStats( SharedNetworkData::getInstance()->getHostID() )->getPreferedTeamId() != TEAM_NOTEAM
232       && !bShowTeamChange
233     )
234  {
235    delete box;
236    box = NULL;
237
238    OrxGui::GLGuiHandler::getInstance()->deactivateCursor( true );
239
240    EventHandler::getInstance()->popState();
241  }
242
243  if ( box != NULL )
244  {
245    OrxGui::GLGuiHandler::getInstance()->tick( dt );
246  }
247
248  assignPlayable();
249
250  if ( !SharedNetworkData::getInstance()->isGameServer() )
251    return;
252
253  gameStateTimer -= dt;
254  //PRINTF(0)("TICK %f\n", gameStateTimer);
255
256  if ( currentGameState != GAMESTATE_GAME && gameStateTimer < 0 )
257    nextGameState();
258
259  this->currentGameState = NetworkGameManager::getInstance()->getGameState();
260
261  if ( currentGameState == GAMESTATE_GAME )
262  {
263    handleTeamChanges();
264  }
265
266  this->calculateTeamScore();
267
268  this->checkGameRules();
269
270  // is the local player dead and inactive
271  if( unlikely(this->bLocalPlayerDead))
272  {
273    this->timeout += dt;
274    PRINTF(0)("TICK DEATH: %f of %f\n", this->timeout, this->deathTimeout);
275    // long enough dead?
276    if( this->timeout >= this->deathTimeout)
277    {
278      this->timeout = 0.0f;
279      // respawn
280      PRINTF(0)("RESPAWN\n");
281      (State::getPlayer())->getPlayable()->respawn();
282    }
283  }
284}
285
286
287/**
288 * draws the stuff
289 */
290void MultiplayerTeamDeathmatch::draw()
291{
292  if( unlikely( this->bLocalPlayerDead))
293  {
294
295  }
296}
297
298
299/**
300 * check the game rules for consistency
301 */
302void MultiplayerTeamDeathmatch::checkGameRules()
303{
304  if ( !SharedNetworkData::getInstance()->isGameServer() )
305    return;
306
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}
317
318/**
319 * find group for new player
320 * @return group id
321 */
322int MultiplayerTeamDeathmatch::getTeamForNewUser()
323{
324  return TEAM_NOTEAM;
325}
326
327ClassID MultiplayerTeamDeathmatch::getPlayableClassId( int userId, int team )
328{
329  if ( team == TEAM_NOTEAM || team == TEAM_SPECTATOR )
330    return CL_SPECTATOR;
331
332  if ( team == 0 || team == 1 )
333    return CL_SPACE_SHIP;
334
335  assert( false );
336}
337
338std::string MultiplayerTeamDeathmatch::getPlayableModelFileName( int userId, int team, ClassID classId )
339{
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 "";
346}
347
348/**
349 * calculate team score
350 */
351void MultiplayerTeamDeathmatch::calculateTeamScore( )
352{
353  teamScore.clear();
354
355  for ( int i = 0; i<numTeams; i++ )
356    teamScore[i] = 0;
357
358
359  const std::list<BaseObject*> * list = ClassList::getList( CL_PLAYER_STATS );
360
361  if ( !list )
362    return;
363
364  for ( std::list<BaseObject*>::const_iterator it = list->begin(); it != list->end(); it++ )
365  {
366    PlayerStats & stats = *dynamic_cast<PlayerStats*>(*it);
367
368    if ( stats.getTeamId() >= 0 )
369    {
370      teamScore[stats.getTeamId()] += stats.getScore();
371    }
372  }
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;
382
383  for ( int i = 0; i<numTeams; i++ )
384    playersInTeam[i] = 0;
385
386  const std::list<BaseObject*> * list = ClassList::getList( CL_PLAYER_STATS );
387
388  if ( !list )
389    return 0;
390
391  for ( std::list<BaseObject*>::const_iterator it = list->begin(); it != list->end(); it++ )
392  {
393    PlayerStats & stats = *dynamic_cast<PlayerStats*>(*it);
394
395    if ( stats.getTeamId() >= 0 )
396    {
397      playersInTeam[stats.getTeamId()]++;
398    }
399  }
400
401
402  int minPlayers = 0xFFFF;
403  int minTeam = -1;
404
405  for ( int i = 0; i<numTeams; i++ )
406  {
407    if ( playersInTeam[i] < minPlayers )
408    {
409      minTeam = i;
410      minPlayers = playersInTeam[i];
411    }
412  }
413
414  assert( minTeam != -1 );
415
416  return minTeam;
417}
418
419void MultiplayerTeamDeathmatch::nextGameState( )
420{
421  if ( currentGameState == GAMESTATE_PRE_GAME )
422  {
423    NetworkGameManager::getInstance()->setGameState( GAMESTATE_GAME );
424
425    return;
426  }
427
428  if ( currentGameState == GAMESTATE_GAME )
429  {
430    NetworkGameManager::getInstance()->setGameState( GAMESTATE_POST_GAME );
431
432    return;
433  }
434
435  if ( currentGameState == GAMESTATE_POST_GAME )
436  {
437    //TODO end game
438
439    return;
440  }
441}
442
443void MultiplayerTeamDeathmatch::handleTeamChanges( )
444{
445  const std::list<BaseObject*> * list = ClassList::getList( CL_PLAYER_STATS );
446
447  if ( !list )
448    return;
449
450  //first server players with choices
451  for ( std::list<BaseObject*>::const_iterator it = list->begin(); it != list->end(); it++ )
452  {
453    PlayerStats & stats = *dynamic_cast<PlayerStats*>(*it);
454
455    if ( stats.getTeamId() != stats.getPreferedTeamId() )
456    {
457      if ( stats.getPreferedTeamId() == TEAM_SPECTATOR || ( stats.getPreferedTeamId() >= 0 && stats.getPreferedTeamId() < numTeams ) )
458      {
459        teamChange( stats.getUserId() );
460      }
461    }
462  }
463
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);
468
469    if ( stats.getTeamId() != stats.getPreferedTeamId() )
470    {
471      if ( stats.getPreferedTeamId() == TEAM_RANDOM )
472      {
473        stats.setPreferedTeamId( getRandomTeam() );
474        teamChange( stats.getUserId() );
475      }
476    }
477  }
478}
479
480void MultiplayerTeamDeathmatch::teamChange( int userId )
481{
482  assert( PlayerStats::getStats( userId ) );
483  PlayerStats & stats = *(PlayerStats::getStats( userId ));
484
485  stats.setTeamId( stats.getPreferedTeamId() );
486
487  Playable * oldPlayable = stats.getPlayable();
488
489
490  ClassID playableClassId = getPlayableClassId( userId, stats.getPreferedTeamId() );
491  std::string playableModel = getPlayableModelFileName( userId, stats.getPreferedTeamId(), playableClassId );
492
493  BaseObject * bo = Factory::fabricate( playableClassId );
494
495  assert( bo != NULL );
496  assert( bo->isA( CL_PLAYABLE ) );
497
498  Playable & playable = *(dynamic_cast<Playable*>(bo));
499
500  playable.loadModel( playableModel );
501  playable.setOwner( userId );
502  playable.setUniqueID( SharedNetworkData::getInstance()->getNewUniqueID() );
503  playable.setSynchronized( true );
504
505  stats.setTeamId( stats.getPreferedTeamId() );
506  stats.setPlayableClassId( playableClassId );
507  stats.setPlayableUniqueId( playable.getUniqueID() );
508  stats.setModelFileName( playableModel );
509
510  if ( oldPlayable )
511  {
512    //if ( userId == SharedNetworkData::getInstance()->getHostID() )
513    //  State::getPlayer()->setPlayable( NULL );
514    delete oldPlayable;
515  }
516}
517
518void MultiplayerTeamDeathmatch::onButtonExit( )
519{
520  State::getCurrentStoryEntity()->stop();
521  this->bShowTeamChange = false;
522}
523
524void MultiplayerTeamDeathmatch::onButtonRandom( )
525{
526  NetworkGameManager::getInstance()->prefereTeam( TEAM_RANDOM );
527  this->bShowTeamChange = false;
528}
529
530void MultiplayerTeamDeathmatch::onButtonTeam0( )
531{
532  NetworkGameManager::getInstance()->prefereTeam( 0 );
533  this->bShowTeamChange = false;
534}
535
536void MultiplayerTeamDeathmatch::onButtonTeam1( )
537{
538  NetworkGameManager::getInstance()->prefereTeam( 1 );
539  this->bShowTeamChange = false;
540}
541
542void MultiplayerTeamDeathmatch::onButtonSpectator( )
543{
544  NetworkGameManager::getInstance()->prefereTeam( TEAM_SPECTATOR );
545  this->bShowTeamChange = false;
546}
547
548void MultiplayerTeamDeathmatch::assignPlayable( )
549{
550  if ( PlayerStats::getStats( SharedNetworkData::getInstance()->getHostID() ) )
551    PlayerStats::getStats( SharedNetworkData::getInstance()->getHostID() )->getPlayable();
552}
553
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  }
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();
574      input->giveMouseFocus();
575      input->setText("say ");
576    }
577  }
578}
579
580void MultiplayerTeamDeathmatch::onButtonCancel( )
581{
582  this->bShowTeamChange = false;
583}
584
585
586
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";
596
597  if ( PlayerStats::getStats( userId ) )
598  {
599    name = PlayerStats::getStats( userId )->getNickName();
600  }
601
602  PRINTF(0)("CHATMESSAGE %s (%d): %s\n", name.c_str(), userId, message.c_str() );
603  notifier->pushNotifyMessage(name + ": " + message);
604}
605
606void MultiplayerTeamDeathmatch::onInputEnter( const std::string & text )
607{
608  EventHandler::getInstance()->popState();
609  input->breakMouseFocus();
610  input->hide();
611  input->setText("");
612
613  std::string command = text;
614
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  }
621
622  OrxShell::ShellCommand::execute( command );
623}
624
625
626
627
Note: See TracBrowser for help on using the repository browser.