Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

mini shell works

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