Added reload of levels on F7 (Update levelpack) to ease the test of changes.
[enigmagame.git] / src / player.cc
blob33c7efbf837bbd3a0fc2dead14b3a10dd8156512
1 /*
2 * Copyright (C) 2002,2003,2004,2006 Daniel Heck
3 * Copyright (C) 2007,2008,2009,2010 Ronald Lamprecht
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "player.hh"
21 #include "Inventory.hh"
22 #include "display.hh"
23 #include "errors.hh"
24 #include "SoundEffectManager.hh"
25 #include "client.hh"
26 #include "server.hh"
27 #include "world.hh"
28 #include "main.hh"
29 #include "items/GlassesItem.hh"
31 #include "ecl_util.hh"
33 #include <algorithm>
34 #include <iostream>
35 #include <cassert>
37 using namespace std;
38 using namespace enigma;
39 using enigma::Actor;
40 using enigma::Inventory;
42 namespace
44 class PlayerInfo {
45 public:
46 PlayerInfo();
48 string name;
49 Inventory inventory;
50 vector<Actor*> actors;
51 bool out_of_lives;
52 double dead_dtime; // number of seconds the player is already dead
53 bool inhibit_pickup;
56 typedef vector<PlayerInfo> PlayerList;
58 struct RespawnInfo {
59 RespawnInfo (Actor *a, double t)
60 : actor(a), time_left(t)
63 Actor *actor; // The actor to respawn
64 double time_left; // Time left before respawning
68 struct LevelLocalData {
69 // Functions
70 LevelLocalData() {}
72 void respawn_dead_actors(double dtime);
73 void resurrect_actor (Actor *a);
74 bool remove_extralife (Actor *a);
75 void reset();
77 // Variables
78 vector<RespawnInfo> respawn_list;
82 /* -------------------- PlayerInfo -------------------- */
84 PlayerInfo::PlayerInfo ()
85 : name(),
86 inventory(),
87 actors(),
88 out_of_lives(false),
89 dead_dtime(0),
90 inhibit_pickup(false)
93 /* -------------------- LevelLocalData -------------------- */
96 void LevelLocalData::respawn_dead_actors(double dtime)
98 for (unsigned i=0; i<respawn_list.size(); ) {
99 RespawnInfo &info = respawn_list[i];
101 info.time_left -= dtime;
102 if (info.time_left < 0) {
103 // if (lua::CallFunc (lua::LevelState(), "Respawn", Value()) != 0) {
104 // throw enigma_levels::XLevelRuntime(string("Calling 'Respawn' failed:\n")+lua::LastError(lua::LevelState()));
106 info.actor->respawn();
107 respawn_list.erase(respawn_list.begin()+i);
108 continue; // don't increment i
110 ++i;
114 void LevelLocalData::resurrect_actor (Actor *a)
116 const double RESPAWN_TIME = 1.5;
118 // a->find_respawnpos();
119 SendMessage(a, "_resurrect");
120 if (!server::InfiniteReincarnation)
121 remove_extralife(a);
122 respawn_list.push_back(RespawnInfo(a, RESPAWN_TIME));
125 bool LevelLocalData::remove_extralife (Actor *a)
127 Inventory *inv = player::GetInventory(a);
128 int idx = inv->find("it_extralife");
130 if (idx == -1) // no extralife found
131 return false;
132 else {
133 delete inv->yield_item(idx);
134 player::RedrawInventory (inv);
135 return true;
139 void LevelLocalData::reset() {
140 respawn_list.clear();
143 /* -------------------- Local variables -------------------- */
145 namespace
147 LevelLocalData leveldat;
148 PlayerList players(2); // this currently has always size 2
149 unsigned icurrent_player = 0;
150 std::vector<Actor *> unassignedActors;
154 void player::PlayerShutdown() {
155 players.clear(); // release objects of inventories
158 /* -------------------- Functions -------------------- */
160 void player::NewGame() {
161 int nplayers = 2; // Always prepare for two players
162 std::vector<int> extralives(2);
164 // calculate number of extralives
165 for (int i=0; i<nplayers; ++i) {
166 if (server::IsLevelRestart) {
167 if (server::ProvideExtralifes) {
168 // count existing number of extralives
169 int idxLife = -1;
170 extralives[i] = -1;
171 do {
172 ++extralives[i];
173 idxLife = players[i].inventory.find("it_extralife", ++idxLife);
174 } while (idxLife != -1);
175 } else
176 extralives[i] = 0;
177 } else {
178 // new game provides 2 extralives
179 extralives[i] = (server::ProvideExtralifes) ? 2 : 0;
183 players.clear();
184 players.resize(nplayers);
186 for (int i=0; i<nplayers; ++i) {
187 Inventory *inv = GetInventory(i);
188 inv->assignOwner(i);
189 for (int j = 0 ; j < extralives[i]; j++)
190 inv->add_item(MakeItem("it_extralife"));
193 unassignedActors.clear();
194 leveldat.reset();
197 void player::AddYinYang ()
199 for (unsigned i=0; i<players.size(); ++i) {
200 Inventory *inv = GetInventory (i);
201 if (inv->find("it_yinyang") == -1)
202 inv->add_item(MakeItem("it_yinyang"));
206 void player::LevelLoaded(bool isRestart)
208 if (server::TwoPlayerGame && server::SingleComputerGame)
209 AddYinYang();
210 RedrawInventory ();
213 void player::PrepareLevel()
215 // Clear up the inventories of all players: keep only extra lifes.
216 for (unsigned iplayer=0; iplayer<players.size(); ++iplayer)
218 Inventory *inv = GetInventory(iplayer);
219 int nextralifes=0;
220 for (size_t i=0; i<inv->size(); ++i)
221 if (get_id (inv->get_item(i)) == it_extralife)
222 nextralifes += 1;
223 inv->clear();
224 for (int i=0; i<nextralifes; ++i)
225 inv->add_item(MakeItem ("it_extralife"));
227 players[iplayer].actors.clear();
230 SetCurrentPlayer(0);
231 leveldat = LevelLocalData();
234 void player::LevelFinished(int stage)
236 for (unsigned i=0; i<players.size(); ++i) {
237 for (unsigned j=0; j<players[i].actors.size(); ++j) {
238 Actor *a = players[i].actors[j];
239 if (stage == 0) {
240 SendMessage(a, "_levelfinish");
241 } else {
242 SendMessage(a, "_disappear");
243 SendMessage(a, "disconnect");
249 Inventory * player::GetInventory (int iplayer)
251 return &players[iplayer].inventory;
255 Inventory * player::GetInventory (Actor *a)
257 if (Value v = a->getAttr("owner"))
258 return GetInventory((int)v);
259 return 0;
263 bool player::WieldedItemIs (Actor *a, const string &kind)
265 if (Inventory *inv = GetInventory(a))
266 if (Item *it = inv->get_item(0))
267 return it->isKind(kind);
268 return false;
272 int player::CurrentPlayer() {
273 return icurrent_player;
276 void player::SetCurrentPlayer(unsigned iplayer)
278 if (iplayer >= players.size())
279 Log << ecl::strf("SetCurrentPlayer: no such player %d\n", iplayer);
280 else {
281 icurrent_player = iplayer;
282 RedrawInventory (GetInventory(iplayer));
283 Glasses::updateGlasses();
287 unsigned player::NumberOfRealPlayers() {
288 unsigned real_players = 0;
290 for (unsigned i=0; i<players.size(); ++i) {
291 if (!players[i].actors.empty()) {
292 ++real_players;
296 return real_players;
299 /*! Sets respawn positions for black or white actors. */
300 void player::SetRespawnPositions(GridPos pos, Value color)
302 ecl::V2 center = pos.center();
304 for (unsigned i=0; i<players.size(); ++i) {
305 vector<Actor *> &al = players[i].actors;
307 for (unsigned j=0; j<al.size(); ++j) {
308 if (Value ac = al[j]->getAttr("color")) {
309 if (ac == color)
310 al[j]->set_respawnpos(center);
316 /*! Remove respawn positions for black or white actors */
317 void player::RemoveRespawnPositions(Value color) {
318 for (unsigned i=0; i<players.size(); ++i) {
319 vector<Actor *> &al = players[i].actors;
321 for (unsigned j=0; j<al.size(); ++j) {
322 if (Value ac = al[j]->getAttr("color")) {
323 if (ac == color)
324 al[j]->remove_respawnpos();
330 void player::Suicide()
332 for (unsigned i=0; i<players.size(); ++i) {
333 vector<Actor *> &al = players[i].actors;
334 for (unsigned j=0; j<al.size(); ++j) {
335 SendMessage(al[j], "_suicide");
340 Actor *player::ReplaceActor (unsigned iplayer, Actor *old, Actor *a)
342 if (iplayer >= players.size())
343 server::RaiseError ("Invalid actor number");
345 vector<Actor *> &al = players[iplayer].actors;
346 for (unsigned i=0; i<al.size(); ++i) {
347 if (al[i] == old) {
348 al[i] = a;
349 return old;
352 return 0;
356 void player::AddActor (unsigned iplayer, Actor *a)
358 if (iplayer >= players.size())
359 server::RaiseError ("Invalid actor number");
361 ReleaseActor(a);
362 players[iplayer].actors.push_back(a);
364 if (players[iplayer].actors.size() == 1) {
365 // the ``main actor'' was set
366 client::Msg_PlayerPosition (iplayer, a->get_pos());
370 bool player::HasActor(unsigned iplayer, Actor *a) {
371 if (iplayer >= 0 && iplayer < players.size()) {
372 for (int i = 0; i < players[iplayer].actors.size(); i++) {
373 if (players[iplayer].actors[i] == a)
374 return true;
377 return false;
380 void player::SwapPlayers()
382 if (NumberOfRealPlayers() >= 2) {
383 SetCurrentPlayer(1-icurrent_player);
387 static bool has_extralive(unsigned pl, unsigned num) {
388 size_t idx = 0;
389 for (int i = 0; i < num; i++) {
390 int idxLife = players[pl].inventory.find("it_extralife", idx);
391 if (idxLife == -1)
392 return false;
393 else
394 idx = idxLife + 1;
396 return true;
399 static bool resurrect_actor (unsigned pl, Actor *a)
401 assert(server::ConserveLevel); // no resurrection otherwise!
403 bool has_life = has_extralive(pl, 1);
404 if (has_life)
405 leveldat.resurrect_actor(a); // = resurrect with delay
406 else
407 players[pl].out_of_lives = true;
408 return has_life;
411 void player::AddUnassignedActor (Actor *a) {
412 unassignedActors.push_back(a);
415 void player::CheckDeadActors() {
416 bool toggle_player = false;
417 const unsigned NO_PLAYER = UINT_MAX;
418 unsigned toggle_to_player = NO_PLAYER;
419 bool new_game = false; // complete restart (new lives)
421 // to live means to be not dead and to be able to move
422 for (int pl = -1; pl<(int)players.size(); ++pl) { // -1 are unassigned actors
423 vector<Actor*>& actors = (pl == -1) ? unassignedActors : players[pl].actors;
424 bool has_living_actor = false; // controllable and living
425 std::map<std::string, int> essMap;
426 std::map<std::string, int>::iterator itEss;
428 for (size_t i=0; i<actors.size(); ++i) {
429 Actor *a = actors[i];
430 std::string essId;
431 if (Value v = a->getAttr("essential_id"))
432 essId = v.to_string();
433 else
434 essId = a->get_traits().name;
435 int essential = a->getAttr("essential");
436 // count number of necessary actors per kind
437 if (essential == 2)
438 --essMap[essId];
440 if (!a->is_dead() ||
441 (pl >= 0 && server::ConserveLevel && resurrect_actor(pl, a))) {
442 // actor is still alive
443 if (pl >= 0 && a->controlled_by(pl) && a->isSteerable()) {
444 has_living_actor = true;
446 // count number of alive actors per kind
447 if (essential == 0 || essential == 2)
448 ++essMap[essId];
450 else {
451 // player is dead and could not resurrect
452 if (essential == 1) {
453 // actor is personnally essential but dead
454 new_game = true;
458 // check if for any kind we have less living actors as required
459 for (itEss = essMap.begin(); itEss != essMap.end(); itEss++) {
460 if ((*itEss).second < 0)
461 new_game = true;
464 if (has_living_actor) {
465 if (toggle_to_player == NO_PLAYER)
466 toggle_to_player = pl;
468 else {
469 if (pl == icurrent_player)
470 // check if player has yinyang for single gamer mode
471 if (player::GetInventory(pl)->find("it_yinyang",0) >= 0)
472 toggle_player = true;
473 else
474 new_game = true;
478 // if no_living_player -> toggle_player is true and toggle_to_player is NO_PLAYER
479 // => new_game is set to true below
481 if ((server::ConserveLevel == false) && !server::IsRestartingLevel() &&
482 (new_game || (toggle_player && toggle_to_player == NO_PLAYER))) {
483 // check if we have enough extralives for a restart instead of new game
484 std::vector<int> numDead;
485 bool reset_level = true;
486 for (unsigned pl = 0; pl<players.size(); ++pl) {
487 numDead.push_back(0);
488 vector<Actor*>& actors = players[pl].actors;
489 for (size_t i = 0; i<actors.size(); ++i) {
490 Actor *a = actors[i];
491 if (a->is_dead()) {
492 numDead[pl]++;
495 if (!has_extralive(pl, numDead[pl])) {
496 reset_level = false;
497 break;
500 if (reset_level) {
501 for (unsigned pl = 0; pl<players.size(); ++pl) {
502 Inventory *inv = player::GetInventory(pl);
503 for (int i=0; i<numDead[pl]; i++) {
504 int idx = inv->find("it_extralife");
505 ASSERT (idx != -1, XLevelRuntime, "Missing extralife for restart of level");
506 delete inv->yield_item(idx);
509 server::RestartLevel(); // should restart w/o scrolling
510 return;
514 if (new_game) {
515 server::Msg_RestartGame();
517 else if (toggle_player) {
518 if (toggle_to_player == NO_PLAYER)
519 server::Msg_RestartGame();
520 else
521 player::SetCurrentPlayer(toggle_to_player);
525 Actor *player::GetMainActor (unsigned iplayer)
527 vector<Actor *> &actors = players[iplayer].actors;
528 return actors.empty() ? 0 : actors[0];
531 void player::Tick(double dtime)
533 STATUSBAR->set_counter (server::GetMoveCounter());
536 // Tell clients about position of main actor for stereo sound and
537 // screen position
538 for (unsigned iplayer = 0; iplayer < players.size(); ++iplayer) {
539 if (Actor *ac = GetMainActor(iplayer))
540 client::Msg_PlayerPosition (iplayer, ac->get_pos());
543 // Respawn actors that have been dead for a certain amount of time
544 leveldat.respawn_dead_actors(dtime);
546 // Update the respawn list or restart the game when all actors are
547 // dead and no extra lifes are left.
548 CheckDeadActors();
551 void player::InhibitPickup(bool flag) {
552 players[icurrent_player].inhibit_pickup = flag;
555 /*! Return pointer to inventory if actor may pick up items, 0
556 otherwise. */
557 Inventory *player::MayPickup(Actor *a, Item *it, bool allowFlying)
559 int iplayer=-1;
560 if (Value v = a->getAttr("owner")) iplayer = v;
561 if (iplayer < 0 || (unsigned)iplayer >= players.size()) {
562 // cerr << "PickupItem: illegal 'player' entry\n";
563 return 0;
566 Inventory *inv = GetInventory(iplayer);
567 bool dont_pickup = players[iplayer].inhibit_pickup
568 || (!allowFlying && a->is_flying())
569 || !inv->willAddItem(it)
570 || a->is_dead()
571 || (server::GameCompatibility != GAMET_ENIGMA && a->getClass() != "ac_marble");
573 return dont_pickup ? 0 : inv;
576 void player::PickupItem (Actor *a, GridPos p)
578 if (Inventory *inv = MayPickup(a, GetField(p)->item)) {
579 if (Item *item = YieldItem(p)) {
580 inv->add_item(item);
581 item->on_pickup(a);
582 RedrawInventory (inv);
583 sound::EmitSoundEvent ("pickup", p.center());
588 bool player::PickupAsItem(Actor *a, GridObject *obj, std::string kind) {
589 if (Item *item = MakeItem(kind.c_str())) {
590 if (Inventory *inv = MayPickup(a, item, true)) {
591 inv->add_item(item);
592 obj->transferIdentity(item);
593 player::RedrawInventory(inv);
594 sound::EmitSoundEvent("pickup", a->get_pos());
595 return true;
596 } else {
597 DisposeObject(item);
600 return false;
603 void player::ActivateFirstItem()
605 Inventory &inv = players[icurrent_player].inventory;
608 if (inv.size() > 0) {
609 Item *it = inv.get_item (0);
610 Actor *ac = NULL;
611 GridPos p;
612 bool can_drop_item = false;
613 std::vector<Actor *>::iterator itr;
614 for (itr = players[icurrent_player].actors.begin();
615 itr != players[icurrent_player].actors.end() && ac == NULL ; itr++) {
616 if (!(*itr)->is_dead()) {
617 ac = *itr;
618 p = GridPos(ac->get_pos());
619 can_drop_item = ac->can_drop_items();
623 switch (it->activate(ac, p)) {
624 case ITEM_DROP:
625 // only drop if no item underneath and actor allows it
626 if (it->can_drop_at(p) && can_drop_item) {
627 it = inv.yield_first ();
628 RedrawInventory (&inv);
629 it->drop(ac, p);
631 break;
632 case ITEM_KILL:
633 DisposeObject (inv.yield_first ());
634 RedrawInventory (&inv);
635 break;
636 case ITEM_KEEP:
637 break;
642 void player::RotateInventory(int dir)
644 sound::EmitSoundEvent ("invrotate", ecl::V2());
645 Inventory &inv = players[icurrent_player].inventory;
646 if (dir == 1)
647 inv.rotate_left ();
648 else
649 inv.rotate_right ();
650 RedrawInventory (&inv);
654 /** Update the specified inventory on the screen, provided it is the
655 inventory of the current player. For all other inventories, this
656 function does nothing. */
657 void player::RedrawInventory (Inventory *inv)
659 if (inv == GetInventory (CurrentPlayer()))
660 RedrawInventory();
664 void player::RedrawInventory()
666 Inventory *inv = GetInventory (CurrentPlayer());
667 std::vector<std::string> modelnames;
668 for (size_t i=0; i<inv->size(); ++i) {
669 Item *it = inv->get_item(i);
670 modelnames.push_back(it->get_inventory_model());
672 STATUSBAR->set_inventory(CurrentPlayer() == 0 ? YIN : YANG, modelnames);