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.
21 #include "Inventory.hh"
24 #include "SoundEffectManager.hh"
29 #include "items/GlassesItem.hh"
31 #include "ecl_util.hh"
38 using namespace enigma
;
40 using enigma::Inventory
;
50 vector
<Actor
*> actors
;
52 double dead_dtime
; // number of seconds the player is already dead
56 typedef vector
<PlayerInfo
> PlayerList
;
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
{
72 void respawn_dead_actors(double dtime
);
73 void resurrect_actor (Actor
*a
);
74 bool remove_extralife (Actor
*a
);
78 vector
<RespawnInfo
> respawn_list
;
82 /* -------------------- PlayerInfo -------------------- */
84 PlayerInfo::PlayerInfo ()
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
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
)
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
133 delete inv
->yield_item(idx
);
134 player::RedrawInventory (inv
);
139 void LevelLocalData::reset() {
140 respawn_list
.clear();
143 /* -------------------- Local variables -------------------- */
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
173 idxLife
= players
[i
].inventory
.find("it_extralife", ++idxLife
);
174 } while (idxLife
!= -1);
178 // new game provides 2 extralives
179 extralives
[i
] = (server::ProvideExtralifes
) ? 2 : 0;
184 players
.resize(nplayers
);
186 for (int i
=0; i
<nplayers
; ++i
) {
187 Inventory
*inv
= GetInventory(i
);
189 for (int j
= 0 ; j
< extralives
[i
]; j
++)
190 inv
->add_item(MakeItem("it_extralife"));
193 unassignedActors
.clear();
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
)
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
);
220 for (size_t i
=0; i
<inv
->size(); ++i
)
221 if (get_id (inv
->get_item(i
)) == it_extralife
)
224 for (int i
=0; i
<nextralifes
; ++i
)
225 inv
->add_item(MakeItem ("it_extralife"));
227 players
[iplayer
].actors
.clear();
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
];
240 SendMessage(a
, "_levelfinish");
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
);
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
);
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
);
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()) {
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")) {
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")) {
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
) {
356 void player::AddActor (unsigned iplayer
, Actor
*a
)
358 if (iplayer
>= players
.size())
359 server::RaiseError ("Invalid actor number");
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
)
380 void player::SwapPlayers()
382 if (NumberOfRealPlayers() >= 2) {
383 SetCurrentPlayer(1-icurrent_player
);
387 static bool has_extralive(unsigned pl
, unsigned num
) {
389 for (int i
= 0; i
< num
; i
++) {
390 int idxLife
= players
[pl
].inventory
.find("it_extralife", idx
);
399 static bool resurrect_actor (unsigned pl
, Actor
*a
)
401 assert(server::ConserveLevel
); // no resurrection otherwise!
403 bool has_life
= has_extralive(pl
, 1);
405 leveldat
.resurrect_actor(a
); // = resurrect with delay
407 players
[pl
].out_of_lives
= true;
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
];
431 if (Value v
= a
->getAttr("essential_id"))
432 essId
= v
.to_string();
434 essId
= a
->get_traits().name
;
435 int essential
= a
->getAttr("essential");
436 // count number of necessary actors per kind
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)
451 // player is dead and could not resurrect
452 if (essential
== 1) {
453 // actor is personnally essential but dead
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)
464 if (has_living_actor
) {
465 if (toggle_to_player
== NO_PLAYER
)
466 toggle_to_player
= pl
;
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;
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
];
495 if (!has_extralive(pl
, numDead
[pl
])) {
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
515 server::Msg_RestartGame();
517 else if (toggle_player
) {
518 if (toggle_to_player
== NO_PLAYER
)
519 server::Msg_RestartGame();
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
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.
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
557 Inventory
*player::MayPickup(Actor
*a
, Item
*it
, bool allowFlying
)
560 if (Value v
= a
->getAttr("owner")) iplayer
= v
;
561 if (iplayer
< 0 || (unsigned)iplayer
>= players
.size()) {
562 // cerr << "PickupItem: illegal 'player' entry\n";
566 Inventory
*inv
= GetInventory(iplayer
);
567 bool dont_pickup
= players
[iplayer
].inhibit_pickup
568 || (!allowFlying
&& a
->is_flying())
569 || !inv
->willAddItem(it
)
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
)) {
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)) {
592 obj
->transferIdentity(item
);
593 player::RedrawInventory(inv
);
594 sound::EmitSoundEvent("pickup", a
->get_pos());
603 void player::ActivateFirstItem()
605 Inventory
&inv
= players
[icurrent_player
].inventory
;
608 if (inv
.size() > 0) {
609 Item
*it
= inv
.get_item (0);
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()) {
618 p
= GridPos(ac
->get_pos());
619 can_drop_item
= ac
->can_drop_items();
623 switch (it
->activate(ac
, p
)) {
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
);
633 DisposeObject (inv
.yield_first ());
634 RedrawInventory (&inv
);
642 void player::RotateInventory(int dir
)
644 sound::EmitSoundEvent ("invrotate", ecl::V2());
645 Inventory
&inv
= players
[icurrent_player
].inventory
;
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()))
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
);