1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "ultima/nuvie/core/nuvie_defs.h"
24 #include "ultima/nuvie/misc/u6_misc.h"
25 #include "ultima/nuvie/files/nuvie_io.h"
26 #include "ultima/nuvie/core/game.h"
27 #include "ultima/nuvie/core/converse.h"
28 #include "ultima/nuvie/core/timed_event.h"
29 #include "ultima/nuvie/conf/configuration.h"
30 #include "ultima/nuvie/actors/actor_manager.h"
31 #include "ultima/nuvie/sound/sound_manager.h"
32 #include "ultima/nuvie/views/view_manager.h"
33 #include "ultima/nuvie/core/player.h"
34 #include "ultima/nuvie/core/map.h"
35 #include "ultima/nuvie/gui/widgets/map_window.h"
36 #include "ultima/nuvie/usecode/u6_usecode.h"
37 #include "ultima/nuvie/gui/widgets/command_bar.h"
38 #include "ultima/nuvie/pathfinder/party_path_finder.h"
39 #include "ultima/nuvie/core/party.h"
40 #include "ultima/nuvie/views/view.h"
41 #include "ultima/nuvie/save/obj_list.h"
42 #include "ultima/nuvie/core/events.h"
43
44 namespace Ultima {
45 namespace Nuvie {
46
Party(Configuration * cfg)47 Party::Party(Configuration *cfg) {
48 config = cfg;
49 game = NULL;
50 actor_manager = NULL;
51 map = NULL;
52 pathfinder = NULL;
53 rest_campfire = NULL;
54
55 formation = PARTY_FORM_STANDARD;
56 num_in_party = 0;
57 prev_leader_x = prev_leader_y = 0;
58 defer_removing_dead_members = false;
59 autowalk = false;
60 in_vehicle = false;
61 in_combat_mode = false;
62 lightsources = 0;
63
64 memset(&member, 0, sizeof member);
65 }
66
~Party()67 Party::~Party() {
68 if (pathfinder)
69 delete pathfinder;
70 }
71
init(Game * g,ActorManager * am)72 bool Party::init(Game *g, ActorManager *am) {
73 Std::string formation_string;
74
75 game = g;
76 actor_manager = am;
77 map = g->get_game_map();
78 if (!pathfinder)
79 pathfinder = new PartyPathFinder(this);
80
81 autowalk = false;
82 in_vehicle = false;
83
84 config->value("config/general/party_formation", formation_string, "");
85 if (formation_string == "row")
86 formation = PARTY_FORM_ROW;
87 else if (formation_string == "column")
88 formation = PARTY_FORM_COLUMN;
89 else if (formation_string == "delta")
90 formation = PARTY_FORM_DELTA;
91 else
92 formation = PARTY_FORM_STANDARD;
93
94 config->value("config/audio/combat_changes_music", combat_changes_music, true);
95 config->value("config/audio/vehicles_change_music", vehicles_change_music, true);
96
97 return true;
98 }
99
load(NuvieIO * objlist)100 bool Party::load(NuvieIO *objlist) {
101 uint8 actor_num;
102 uint16 i;
103
104 autowalk = false;
105 in_vehicle = false;
106
107 objlist->seek(OBJLIST_OFFSET_NUM_IN_PARTY);
108 num_in_party = objlist->read1();
109
110
111 objlist->seek(OBJLIST_OFFSET_PARTY_NAMES);
112 for (i = 0; i < num_in_party; i++) {
113 objlist->readToBuf((unsigned char *)member[i].name, PARTY_NAME_MAX_LENGTH + 1); // read in Player name.
114 }
115 objlist->seek(OBJLIST_OFFSET_PARTY_ROSTER);
116 for (i = 0; i < num_in_party; i++) {
117 actor_num = objlist->read1();
118 member[i].actor = actor_manager->get_actor(actor_num);
119 member[i].actor->set_in_party(true);
120 //member[i].inactive = false; // false unless actor is asleep, or paralyzed (is_immobile)
121 }
122
123 objlist->seek(OBJLIST_OFFSET_U6_COMBAT_MODE); // combat mode flag. NOTE! this offset is probably U6 specifix FIXME
124 in_combat_mode = (bool)objlist->read1();
125
126 MapCoord leader_loc = get_leader_location(); // previous leader location
127 prev_leader_x = leader_loc.x;
128 prev_leader_y = leader_loc.y;
129
130 reform_party();
131
132 autowalk = false;
133
134 if (actor_manager->get_actor(ACTOR_VEHICLE_ID_N)->get_worktype() == ACTOR_WT_PLAYER) { // WT_U6_PLAYER
135 set_in_vehicle(true);
136 hide();
137 }
138
139 for (i = 0; i < PARTY_MAX_MEMBERS; i++) {
140 clear_combat_target(i);
141 }
142
143 update_light_sources();
144 update_music();
145
146 return true;
147 }
148
save(NuvieIO * objlist)149 bool Party::save(NuvieIO *objlist) {
150 uint16 i;
151
152 objlist->seek(OBJLIST_OFFSET_NUM_IN_PARTY);
153 objlist->write1(num_in_party);
154
155
156 objlist->seek(OBJLIST_OFFSET_PARTY_NAMES);
157 for (i = 0; i < num_in_party; i++) {
158 objlist->writeBuf((unsigned char *)member[i].name, PARTY_NAME_MAX_LENGTH + 1);
159 }
160
161 objlist->seek(OBJLIST_OFFSET_PARTY_ROSTER);
162 for (i = 0; i < num_in_party; i++) {
163 objlist->write1(member[i].actor->get_actor_num());
164 }
165
166 objlist->seek(OBJLIST_OFFSET_U6_COMBAT_MODE); // combat mode flag. NOTE! this offset is probably U6 specifix FIXME
167 objlist->write1((uint8)in_combat_mode);
168
169 return true;
170 }
171
add_actor(Actor * actor)172 bool Party::add_actor(Actor *actor) {
173 Converse *converse = game->get_converse();
174
175 if (num_in_party < PARTY_MAX_MEMBERS) {
176 actor->set_in_party(true);
177 member[num_in_party].actor = actor;
178 //member[num_in_party].inactive = false;
179 strncpy(member[num_in_party].name, converse->npc_name(actor->id_n), PARTY_NAME_MAX_LENGTH + 1);
180 member[num_in_party].name[PARTY_NAME_MAX_LENGTH] = '\0'; // make sure name is terminated
181 member[num_in_party].combat_position = 0;
182 // member[num_in_party].leader_x = member[0].actor->get_location().x;
183 // member[num_in_party].leader_y = member[0].actor->get_location().y;
184
185 num_in_party++;
186 reform_party();
187 return true;
188 }
189
190 return false;
191 }
192
193
194 // remove actor from member array shuffle remaining actors down if required.
remove_actor(Actor * actor,bool keep_party_flag)195 bool Party::remove_actor(Actor *actor, bool keep_party_flag) {
196 if (defer_removing_dead_members) //we don't want to remove member while inside the Party::follow() method.
197 return true;
198 Game::get_game()->get_event()->set_control_cheat(false);
199 uint8 i;
200
201 for (i = 0; i < num_in_party; i++) {
202 if (member[i].actor->id_n == actor->id_n) {
203 if (keep_party_flag == false) {
204 for (int j = 0; j < member[i].actor->get_num_light_sources(); j++)
205 subtract_light_source();
206 member[i].actor->set_in_party(false);
207 }
208 if (i != (num_in_party - 1)) {
209 for (; i + 1 < num_in_party; i++)
210 member[i] = member[i + 1];
211 }
212 num_in_party--;
213
214 reform_party();
215 if (game->is_new_style()) {
216 Game::get_game()->get_event()->close_gumps();
217 return true;
218 }
219 //FIXME this is a bit hacky we need a better way to refresh views when things change
220 //maybe using callbacks.
221 //If the last actor dies and was being displayed in a view then we need to set the
222 //view to the new last party member.
223 View *cur_view = Game::get_game()->get_view_manager()->get_current_view();
224 if (cur_view && cur_view->get_party_member_num() >= num_in_party)
225 cur_view->set_party_member(num_in_party - 1);
226 else if (cur_view)
227 cur_view->set_party_member(cur_view->get_party_member_num()); // needed if a middle party member dies
228 return true;
229 }
230 }
231
232 return false;
233 }
234
remove_dead_actor(Actor * actor)235 bool Party::remove_dead_actor(Actor *actor) {
236 return remove_actor(actor, PARTY_KEEP_PARTY_FLAG);
237 }
238
resurrect_dead_members()239 bool Party::resurrect_dead_members() {
240 uint16 i;
241 Actor *actor;
242 MapCoord new_pos = get_leader_location();
243 if (Game::get_game()->get_event()->using_control_cheat()) {
244 Game::get_game()->get_event()->set_control_cheat(false);
245 if (!Game::get_game()->is_new_style()) {
246 Game::get_game()->get_view_manager()->set_inventory_mode();
247 Game::get_game()->get_view_manager()->get_current_view()->set_party_member(0);
248 }
249 }
250
251 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
252 actor = actor_manager->get_actor(i);
253 if (actor->is_in_party() && actor->is_alive() == false)
254 actor->resurrect(new_pos);
255 }
256 update_light_sources(); // should only be needed for control cheat but it won't hurt to recheck
257 return true;
258 }
259
260
split_gold()261 void Party::split_gold() {
262 }
263
gather_gold()264 void Party::gather_gold() {
265 }
266
get_party_size()267 uint8 Party::get_party_size() {
268 return num_in_party;
269 }
270
get_leader_actor()271 Actor *Party::get_leader_actor() {
272 sint8 leader = get_leader();
273 if (leader < 0) {
274 return NULL;
275 }
276
277 return get_actor(leader);
278 }
279
get_actor(uint8 member_num)280 Actor *Party::get_actor(uint8 member_num) {
281 if (num_in_party <= member_num)
282 return NULL;
283
284 return member[member_num].actor;
285 }
286
get_actor_name(uint8 member_num)287 char *Party::get_actor_name(uint8 member_num) {
288 if (num_in_party <= member_num)
289 return NULL;
290
291 return member[member_num].name;
292 }
293
294
295 /* Returns position of actor in party or -1.
296 */
get_member_num(Actor * actor)297 sint8 Party::get_member_num(Actor *actor) {
298 for (int i = 0; i < num_in_party; i++) {
299 if (member[i].actor->id_n == actor->id_n)
300 return (i);
301 }
302 return (-1);
303 }
304
get_member_num(uint8 a)305 sint8 Party::get_member_num(uint8 a) {
306 return (get_member_num(actor_manager->get_actor(a)));
307 }
308
get_actor_num(uint8 member_num)309 uint8 Party::get_actor_num(uint8 member_num) {
310 if (num_in_party <= member_num)
311 return 0; // hmm how should we handle this error.
312
313 return member[member_num].actor->id_n;
314 }
315
316 /* Rearrange member slot positions based on the number of members and the
317 * selected formation. Used only when adding or removing actors.
318 */
reform_party()319 void Party::reform_party() {
320 uint32 n;
321 sint32 x, y, max_x;
322 bool even_row;
323 sint8 leader = get_leader();
324 if (leader < 0 || num_in_party == 1)
325 return;
326 member[leader].form_x = 0;
327 member[leader].form_y = 0;
328 switch (formation) {
329 case PARTY_FORM_COLUMN: // line up behind Avatar
330 x = 0;
331 y = 1;
332 for (n = leader + 1; n < num_in_party; n++) {
333 member[n].form_x = x;
334 member[n].form_y = y++;
335 if (y == 5) {
336 x += 1;
337 y = 0;
338 }
339 }
340 break;
341 case PARTY_FORM_ROW: // line up left of Avatar
342 x = -1;
343 y = 0;
344 for (n = leader + 1; n < num_in_party; n++) {
345 member[n].form_x = x--;
346 member[n].form_y = y;
347 if (x == -5) {
348 y += 1;
349 x = 0;
350 }
351 }
352 break;
353 case PARTY_FORM_DELTA: // open triangle formation with Avatar at front
354 x = -1;
355 y = 1;
356 for (n = leader + 1; n < num_in_party; n++) {
357 member[n].form_x = x;
358 member[n].form_y = y;
359 // alternate X once, then move down
360 x = -x;
361 if (x == 0 || x < 0) {
362 x -= 1;
363 ++y;
364 }
365 if (y == 5) { // start at top of triangle
366 y -= ((-x) - 1);
367 x = 0;
368 }
369 }
370 break;
371 // case PARTY_FORM_COMBAT: // positions determined by COMBAT mode
372 // break;
373 case PARTY_FORM_REST: // special formation used while Resting
374 member[1].form_x = 0;
375 member[1].form_y = -2;
376 member[2].form_x = 1;
377 member[2].form_y = -1;
378 member[3].form_x = -1;
379 member[3].form_y = -1;
380 member[4].form_x = 1;
381 member[4].form_y = 0;
382 member[5].form_x = -1;
383 member[5].form_y = -2;
384 member[6].form_x = 1;
385 member[6].form_y = -2;
386 member[7].form_x = -1;
387 member[7].form_y = 0;
388 break;
389 case PARTY_FORM_STANDARD: // U6
390 default:
391 // put first follower behind or behind and to the left of Avatar
392 member[leader + 1].form_x = (num_in_party >= 3) ? -1 : 0;
393 member[leader + 1].form_y = 1;
394 x = y = 1;
395 even_row = false;
396 for (n = leader + 2, max_x = 1; n < num_in_party; n++) {
397 member[n].form_x = x;
398 member[n].form_y = y;
399 // alternate & move X left/right by 2
400 x = (x == 0) ? x - 2 : (x < 0) ? -x : -x - 2;
401 if (x > max_x || (x < 0 && -x > max_x)) { // reached row max.
402 ++y;
403 even_row = !even_row; // next row
404 ++max_x; // increase row max.
405 x = even_row ? 0 : -1; // next guy starts at center or left by 2
406 }
407 }
408 }
409 }
410
411 /* Returns number of person leading the party (the first active member), or -1
412 * if the party has no leader and can't move. */
get_leader()413 sint8 Party::get_leader() {
414 for (int m = 0; m < num_in_party; m++)
415 if (member[m].actor->is_immobile() == false && member[m].actor->is_charmed() == false)
416 return m;
417 return -1;
418 }
419
420 /* Get map location of a party member. */
get_location(uint8 m)421 MapCoord Party::get_location(uint8 m) {
422 return (member[m].actor->get_location());
423 }
424
425 /* Get map location of the first active party member. */
get_leader_location()426 MapCoord Party::get_leader_location() {
427 sint8 m = get_leader();
428 MapCoord loc;
429 if (m >= 0)
430 loc = member[m].actor->get_location();
431 return (loc);
432 }
433
434 /* Returns absolute location where party member `m' SHOULD be (based on party
435 * formation and leader location.
436 */
get_formation_coords(uint8 m)437 MapCoord Party::get_formation_coords(uint8 m) {
438 MapCoord a = get_location(m); // my location
439 MapCoord l = get_leader_location(); // leader location
440 sint8 leader = get_leader();
441 if (leader < 0)
442 return (a);
443 uint8 ldir = member[leader].actor->get_direction(); // leader direction
444 // intended location
445 uint16 x = (ldir == NUVIE_DIR_N) ? l.x + member[m].form_x : // X
446 (ldir == NUVIE_DIR_E) ? l.x - member[m].form_y :
447 (ldir == NUVIE_DIR_S) ? l.x - member[m].form_x :
448 (ldir == NUVIE_DIR_W) ? l.x + member[m].form_y : a.x;
449 uint16 y = (ldir == NUVIE_DIR_N) ? l.y + member[m].form_y : // Y
450 (ldir == NUVIE_DIR_E) ? l.y + member[m].form_x :
451 (ldir == NUVIE_DIR_S) ? l.y - member[m].form_y :
452 (ldir == NUVIE_DIR_W) ? l.y - member[m].form_x : a.y;
453 return (MapCoord(WRAPPED_COORD(x, a.z),
454 WRAPPED_COORD(y, a.z),
455 a.z // Z
456 ));
457 }
458
459
460 /* Update the actual locations of the party actors on the map, so that they are
461 * in the proper formation. */
follow(sint8 rel_x,sint8 rel_y)462 void Party::follow(sint8 rel_x, sint8 rel_y) {
463 Common::Array<bool> try_again;
464 try_again.resize(get_party_max());
465
466 sint8 leader = get_leader();
467 if (leader <= -1)
468 return;
469
470 if (is_in_combat_mode()) { // just update everyone's combat mode
471 for (int p = 0; p < get_party_size(); p++)
472 get_actor(p)->set_worktype(get_actor(p)->get_combat_mode());
473 return;
474 }
475
476 defer_removing_dead_members = true;
477
478 // set previous leader location first, just in case the leader changed
479 prev_leader_x = WRAPPED_COORD(member[leader].actor->x - rel_x, member[leader].actor->z);
480 prev_leader_y = member[leader].actor->y - rel_y;
481 // PASS 1: Keep actors chained together.
482 for (uint32 p = (leader + 1); p < num_in_party; p++) {
483 if (member[p].actor->is_immobile()) continue;
484
485 try_again[p] = false;
486 if (!pathfinder->follow_passA(p))
487 try_again[p] = true;
488 }
489 // PASS 2: Catch up to party.
490 for (uint32 p = (leader + 1); p < num_in_party; p++) {
491 if (member[p].actor->is_immobile()) continue;
492
493 if (try_again[p])
494 pathfinder->follow_passA(p);
495 pathfinder->follow_passB(p);
496 if (!pathfinder->is_contiguous(p)) {
497 sint8 l = get_leader();
498 if (l >= 0) {
499 DEBUG(0, LEVEL_DEBUGGING, "%s is looking for %s.\n", get_actor_name(p), get_actor_name(l));
500 }
501 pathfinder->seek_leader(p); // enter/update seek mode
502 } else if (member[p].actor->get_pathfinder())
503 pathfinder->end_seek(p);
504
505 get_actor(p)->set_moves_left(get_actor(p)->get_moves_left() - 10);
506 get_actor(p)->set_worktype(0x01); // revert to normal worktype
507 }
508
509 defer_removing_dead_members = false;
510
511 //remove party members that died during follow routine.
512 for (int p = get_party_size() - 1; p >= 0; p--) {
513 Actor *a = get_actor(p);
514 if (a->is_alive() == false)
515 remove_actor(a, PARTY_KEEP_PARTY_FLAG);
516 }
517 }
518
519 // Returns true if anyone in the party has a matching object.
has_obj(uint16 obj_n,uint8 quality,bool match_zero_qual)520 bool Party::has_obj(uint16 obj_n, uint8 quality, bool match_zero_qual) {
521 uint16 i;
522
523 for (i = 0; i < num_in_party; i++) {
524 if (member[i].actor->inventory_get_object(obj_n, quality, match_zero_qual) != NULL) // we got a match
525 return true;
526 }
527
528 return false;
529 }
530
531 // Removes the first occurence of an object in the party.
remove_obj(uint16 obj_n,uint8 quality)532 bool Party::remove_obj(uint16 obj_n, uint8 quality) {
533 uint16 i;
534 Obj *obj;
535
536 for (i = 0; i < num_in_party; i++) {
537 obj = member[i].actor->inventory_get_object(obj_n, quality);
538 if (obj != NULL) {
539 if (member[i].actor->inventory_remove_obj(obj)) {
540 delete_obj(obj);
541 return true;
542 }
543 }
544 }
545
546 return false;
547 }
548
549 // Returns the actor id of the first person in the party to have a matching object.
who_has_obj(uint16 obj_n,uint8 quality,bool match_qual_zero)550 Actor *Party::who_has_obj(uint16 obj_n, uint8 quality, bool match_qual_zero) {
551 uint16 i;
552 for (i = 0; i < num_in_party; i++) {
553 if (member[i].actor->inventory_get_object(obj_n, quality, match_qual_zero) != NULL)
554 return (member[i].actor);
555 }
556 return NULL;
557 }
558
get_obj(uint16 obj_n,uint8 quality,bool match_qual_zero,uint8 frame_n,bool match_frame_n)559 Obj *Party::get_obj(uint16 obj_n, uint8 quality, bool match_qual_zero, uint8 frame_n, bool match_frame_n) {
560 Obj *obj;
561 for (uint16 i = 0; i < num_in_party; i++) {
562 obj = member[i].actor->inventory_get_object(obj_n, quality, match_qual_zero, frame_n, match_frame_n);
563 if (obj)
564 return obj;
565 }
566 return NULL;
567 }
568
569 /* Is EVERYONE in the party at or near the coordinates?
570 */
is_at(uint16 x,uint16 y,uint8 z,uint32 threshold)571 bool Party::is_at(uint16 x, uint16 y, uint8 z, uint32 threshold) {
572 for (uint32 p = 0; p < num_in_party; p++) {
573 MapCoord loc(x, y, z);
574 if (!member[p].actor->is_nearby(loc, threshold))
575 return (false);
576 }
577 return (true);
578 }
579
is_at(MapCoord & xyz,uint32 threshold)580 bool Party::is_at(MapCoord &xyz, uint32 threshold) {
581 return (is_at(xyz.x, xyz.y, xyz.z, threshold));
582 }
583
584 /* Is ANYONE in the party at or near the coordinates? */
is_anyone_at(uint16 x,uint16 y,uint8 z,uint32 threshold)585 bool Party::is_anyone_at(uint16 x, uint16 y, uint8 z, uint32 threshold) {
586 for (uint32 p = 0; p < num_in_party; p++) {
587 MapCoord loc(x, y, z);
588 if (member[p].actor->is_nearby(loc, threshold))
589 return (true);
590 }
591 return (false);
592 }
593
is_anyone_at(MapCoord & xyz,uint32 threshold)594 bool Party::is_anyone_at(MapCoord &xyz, uint32 threshold) {
595 return (is_anyone_at(xyz.x, xyz.y, xyz.z, threshold));
596 }
597
contains_actor(Actor * actor)598 bool Party::contains_actor(Actor *actor) {
599 if (get_member_num(actor) >= 0)
600 return (true);
601
602 return (false);
603 }
604
contains_actor(uint8 actor_num)605 bool Party::contains_actor(uint8 actor_num) {
606 return (contains_actor(actor_manager->get_actor(actor_num)));
607 }
608
set_in_combat_mode(bool value)609 void Party::set_in_combat_mode(bool value) {
610 in_combat_mode = value;
611 actor_manager->set_combat_movement(value);
612
613 if (in_combat_mode) {
614 for (int p = 0; p < get_party_size(); p++)
615 get_actor(p)->set_worktype(get_actor(p)->get_combat_mode()); //set combat worktype
616 } else {
617 for (int p = 0; p < get_party_size(); p++)
618 get_actor(p)->set_worktype(ACTOR_WT_FOLLOW); //set back to follow party leader.
619 }
620 // if(combat_changes_music)
621 update_music();
622 if (game->get_command_bar() != NULL) {
623 game->get_command_bar()->set_combat_mode(in_combat_mode);
624 }
625 }
626
update_music()627 void Party::update_music() {
628 SoundManager *s = Game::get_game()->get_sound_manager();
629 MapCoord pos;
630
631 if (in_vehicle && vehicles_change_music) {
632 s->musicPlayFrom("boat");
633 return;
634 } else if (in_combat_mode && combat_changes_music) {
635 s->musicPlayFrom("combat");
636 return;
637 }
638
639 pos = get_leader_location();
640
641 switch (pos.z) {
642 case 0 :
643 s->musicPlayFrom("random");
644 break;
645 case 5 :
646 s->musicPlayFrom("gargoyle");
647 break;
648 default :
649 s->musicPlayFrom("dungeon");
650 break;
651 }
652
653 return;
654 }
655
heal()656 void Party::heal() {
657 uint16 i;
658
659 for (i = 0; i < num_in_party; i++) {
660 member[i].actor->heal();
661 }
662
663 return;
664
665 }
666
cure()667 void Party::cure() {
668 for (uint16 i = 0; i < num_in_party; i++) {
669 member[i].actor->cure();
670 }
671 }
672
set_ethereal(bool ethereal)673 void Party::set_ethereal(bool ethereal) {
674 for (uint16 i = 0; i < num_in_party; i++) {
675 member[i].actor->set_ethereal(ethereal);
676 }
677 }
678
show()679 void Party::show() {
680 uint16 i;
681
682 for (i = 0; i < num_in_party; i++) {
683 member[i].actor->show();
684 }
685
686 return;
687 }
688
hide()689 void Party::hide() {
690 uint16 i;
691
692 for (i = 0; i < num_in_party; i++) {
693 member[i].actor->hide();
694 }
695
696 return;
697 }
698
699 /* Move and center everyone in the party to one location.
700 */
move(uint16 dx,uint16 dy,uint8 dz)701 bool Party::move(uint16 dx, uint16 dy, uint8 dz) {
702 for (sint32 m = 0; m < num_in_party; m++)
703 if (!member[m].actor->move(dx, dy, dz, ACTOR_FORCE_MOVE))
704 return (false);
705 return (true);
706 }
707
708
709 /* Automatically walk (timed) to a destination, and then teleport to new
710 * location (optional). Used to enter/exit dungeons.
711 * (step_delay 0 = default speed)
712 */
walk(MapCoord * walkto,MapCoord * teleport,uint32 step_delay)713 void Party::walk(MapCoord *walkto, MapCoord *teleport, uint32 step_delay) {
714 if (step_delay)
715 new TimedPartyMove(walkto, teleport, step_delay);
716 else
717 new TimedPartyMove(walkto, teleport);
718
719 game->pause_world(); // other actors won't move
720 game->pause_user(); // don't allow input
721 // view will snap back to player after everyone has moved
722 game->get_player()->set_mapwindow_centered(false);
723 autowalk = true;
724 }
725
726
727 /* Enter a moongate and teleport to a new location.
728 * (step_delay 0 = default speed)
729 */
walk(Obj * moongate,MapCoord * teleport,uint32 step_delay)730 void Party::walk(Obj *moongate, MapCoord *teleport, uint32 step_delay) {
731 MapCoord walkto(moongate->x, moongate->y, moongate->z);
732 if (step_delay)
733 new TimedPartyMove(&walkto, teleport, moongate, step_delay);
734 else
735 new TimedPartyMove(&walkto, teleport, moongate);
736
737 game->pause_world(); // other actors won't move
738 game->pause_user(); // don't allow input
739 // view will snap back to player after everyone has moved
740 game->get_player()->set_mapwindow_centered(false);
741 autowalk = true;
742 }
743
744
745
746 /* Automatically walk (timed) to vehicle. (step_delay 0 = default speed)
747 */
enter_vehicle(Obj * ship_obj,uint32 step_delay)748 void Party::enter_vehicle(Obj *ship_obj, uint32 step_delay) {
749 MapCoord walkto(ship_obj->x, ship_obj->y, ship_obj->z);
750
751 dismount_from_horses();
752
753 if (step_delay)
754 new TimedPartyMoveToVehicle(&walkto, ship_obj, step_delay);
755 else
756 new TimedPartyMoveToVehicle(&walkto, ship_obj);
757
758 game->pause_world(); // other actors won't move
759 game->pause_user(); // don't allow input
760 // view will snap back to player after everyone has moved
761 game->get_player()->set_mapwindow_centered(false);
762 autowalk = true;
763 }
764
exit_vehicle(uint16 x,uint16 y,uint16 z)765 void Party::exit_vehicle(uint16 x, uint16 y, uint16 z) {
766 if (is_in_vehicle() == false)
767 return;
768
769 Actor *vehicle_actor = actor_manager->get_actor(0);
770
771 show();
772 vehicle_actor->unlink_surrounding_objects();
773 vehicle_actor->hide();
774 vehicle_actor->set_worktype(0);
775
776 Player *player = game->get_player();
777
778 player->set_actor(get_actor(0));
779 player->move(x, y, z, false);
780 vehicle_actor->obj_n = 0;//OBJ_U6_NO_VEHICLE;
781 vehicle_actor->frame_n = 0;
782 vehicle_actor->init();
783 vehicle_actor->move(0, 0, 0, ACTOR_FORCE_MOVE);
784
785 set_in_vehicle(false);
786 }
787
set_in_vehicle(bool value)788 void Party::set_in_vehicle(bool value) {
789 in_vehicle = value;
790 if (vehicles_change_music)
791 update_music();
792 if (value) {
793 if (in_combat_mode == true)
794 set_in_combat_mode(false); // break off combat when boarding a vehicle
795 }
796
797 return;
798 }
799
800 /* Done automatically walking, return view to player character.
801 */
stop_walking(bool force_music_change)802 void Party::stop_walking(bool force_music_change) {
803 game->get_player()->set_mapwindow_centered(true);
804 game->unpause_world(); // allow user input, unfreeze actors
805 game->unpause_user();
806 autowalk = false;
807 if (force_music_change || vehicles_change_music)
808 update_music();
809 }
810
dismount_from_horses()811 void Party::dismount_from_horses() {
812 UseCode *usecode = Game::get_game()->get_usecode();
813
814 for (uint32 m = 0; m < num_in_party; m++) {
815 if (member[m].actor->obj_n == OBJ_U6_HORSE_WITH_RIDER) {
816 Obj *my_obj = member[m].actor->make_obj();
817 usecode->use_obj(my_obj, member[m].actor);
818 delete_obj(my_obj);
819 }
820 }
821
822 return;
823 }
824
get_slowest_actor()825 Actor *Party::get_slowest_actor() {
826 Actor *actor = 0;
827 sint8 begin = get_leader();
828 if (begin >= 0) {
829 actor = member[begin].actor;
830 sint8 moves = actor->get_moves_left();
831 for (uint32 m = begin + 1; m < num_in_party; m++) {
832 sint8 select_moves = member[m].actor->get_moves_left();
833 if (member[m].actor->is_immobile() == false && (select_moves < moves)) {
834 moves = select_moves;
835 actor = member[m].actor;
836 }
837 }
838 }
839 return actor;
840 }
841
842 /* Gather everyone around a campfire to Rest. */
rest_gather()843 void Party::rest_gather() {
844 Actor *player_actor = get_leader_actor();
845 if (player_actor) {
846 MapCoord player_loc = player_actor->get_location();
847 rest_campfire = new_obj(OBJ_U6_CAMPFIRE, 1, player_loc.x, player_loc.y, player_loc.z);
848 rest_campfire->set_temporary();
849 rest_campfire->qty = 1; //this is set so the campfire may be destroyed by being attacked.
850 game->get_obj_manager()->add_obj(rest_campfire, true); // addOnTop
851
852 game->get_player()->set_mapwindow_centered(false);
853 game->pause_user();
854 new TimedRestGather(player_loc.x, player_loc.y);
855 }
856 }
857
858 /* Start Resting for the specified number of hours, optionally with a party
859 * member standing guard. */
rest_sleep(uint8 hours,sint16 guard)860 void Party::rest_sleep(uint8 hours, sint16 guard) {
861 // FIXME: change music to Stones when asking "How many hours?", change to
862 // a random song when finished camping (or if cancelled)
863 new TimedRest(hours, guard >= 0 ? member[guard].actor : 0, rest_campfire);
864 }
865
can_rest(Std::string & err_str)866 bool Party::can_rest(Std::string &err_str) {
867 Map *map_ = game->get_game_map();
868 Player *player = game->get_player();
869 Actor *pActor = player->get_actor();
870 MapCoord loc = pActor->get_location();
871
872 ActorList *enemies = 0;
873 ActorList *all_actors = 0;
874
875 if (is_in_combat_mode()) {
876 if (Game::get_game()->get_game_type() == NUVIE_GAME_SE)
877 err_str = "\nNot while in Combat mode!";
878 else if (Game::get_game()->get_game_type() == NUVIE_GAME_MD)
879 err_str = "- Not while in Combat!";
880 else
881 err_str = "-Not while in Combat!";
882 } else if (is_in_vehicle()
883 && pActor->get_obj_n() != OBJ_U6_SHIP) // player is a vehicle
884 err_str = "-Can not be repaired!";
885 else if (Game::get_game()->get_game_type() == NUVIE_GAME_U6
886 && game->get_map_window()->in_town())
887 err_str = "-Only in the wilderness!";
888 else if ((enemies = pActor->find_enemies())) {
889 if (Game::get_game()->get_game_type() == NUVIE_GAME_MD)
890 err_str = "\nNot while foes are near!";
891 if (Game::get_game()->get_game_type() == NUVIE_GAME_SE)
892 err_str = "- Not while foes are near!";
893 else
894 err_str = "-Not while foes are near!";
895 } else if ((all_actors = actor_manager->filter_party(actor_manager->filter_distance(actor_manager->get_actor_list(),
896 loc.x, loc.y, loc.z, 5)))
897 && !all_actors->empty() && !is_in_vehicle()) {
898 if (Game::get_game()->get_game_type() == NUVIE_GAME_U6)
899 err_str = "-Not while others are near!";
900 else
901 err_str = "\nIt's too noisy to sleep here!";
902 delete all_actors;
903 } else if (!player->in_party_mode())
904 err_str = "-Not in solo mode!";
905 else if (!is_in_vehicle() && !map_->is_passable(loc.x - 1, loc.y - 1, loc.x + 1, loc.y + 1, loc.z)
906 && Game::get_game()->get_game_type() != NUVIE_GAME_SE)
907 err_str = "-Not enough room!"; // FIXME: for ships the original checks all squares around the ship. Do we really need this?
908 else if (is_horsed())
909 err_str = "-Dismount first!";
910 else
911 return true;
912 delete enemies;
913 return false;
914 }
915
is_horsed()916 bool Party::is_horsed() {
917 for (int p = 0; p < num_in_party; p++)
918 if (member[p].actor->get_obj_n() == OBJ_U6_HORSE_WITH_RIDER)
919 return true;
920 return false;
921 }
922
is_everyone_horsed()923 bool Party::is_everyone_horsed() {
924 for (int p = 0; p < num_in_party; p++)
925 if (member[p].actor->get_obj_n() != OBJ_U6_HORSE_WITH_RIDER)
926 return false;
927 return true;
928 }
929
get_food()930 Obj *Party::get_food() {
931 for (int p = 0; p < num_in_party; p++) {
932 Obj *food = member[p].actor->inventory_get_food();
933 if (food)
934 return food;
935 }
936 return 0;
937 }
938
set_combat_target(uint8 member_num,Actor * target)939 void Party::set_combat_target(uint8 member_num, Actor *target) {
940 if (num_in_party <= member_num)
941 return;
942
943 member[member_num].target.type = TARGET_ACTOR;
944 member[member_num].target.actor_num = target->get_actor_num();
945 }
946
set_combat_target(uint8 member_num,MapCoord target)947 void Party::set_combat_target(uint8 member_num, MapCoord target) {
948 if (num_in_party <= member_num)
949 return;
950
951 member[member_num].target.type = TARGET_LOCATION;
952 member[member_num].target.loc = target;
953 }
954
clear_combat_target(uint8 member_num)955 void Party::clear_combat_target(uint8 member_num) {
956 if (member_num >= PARTY_MAX_MEMBERS)
957 return;
958
959 member[member_num].target.type = TARGET_NONE;
960 member[member_num].target.loc = MapCoord();
961 member[member_num].target.actor_num = 0;
962 }
963
get_combat_target(uint8 member_num)964 CombatTarget Party::get_combat_target(uint8 member_num) {
965 if (num_in_party <= member_num) {
966 CombatTarget noTarget;
967 noTarget.type = TARGET_NONE;
968 noTarget.loc = MapCoord();
969 noTarget.actor_num = 0;
970 return noTarget;
971 }
972
973 return member[member_num].target;
974 }
975
update_light_sources()976 void Party::update_light_sources() {
977 lightsources = 0;
978 for (int i = 0; i < num_in_party; i++) {
979 for (int j = 0; j < member[i].actor->get_num_light_sources(); j++)
980 add_light_source();
981 }
982 if (game->get_event()->using_control_cheat()) {
983 for (int i = 0; i < game->get_player()->get_actor()->get_num_light_sources(); i++)
984 add_light_source();
985 }
986 game->get_map_window()->updateAmbience();
987 }
988
has_light_source()989 bool Party::has_light_source() {
990 if (!game->get_player()->get_actor())
991 return false;
992 if (lightsources > 0) { // the original engine didn't care about distance
993 if (game->get_event()->using_control_cheat()) {
994 if (game->get_player()->get_actor()->get_num_light_sources() > 0)
995 return true;
996 else
997 return false;
998 }
999 for (int i = 0; i < num_in_party; i++) {
1000 if (member[i].actor->get_num_light_sources() > 0) {
1001 if (!game->get_map_window()->tile_is_black(member[i].actor->x, member[i].actor->y)
1002 && member[i].actor->is_nearby(game->get_player()->get_actor())) // within 5 tiles of player
1003 return true;
1004 }
1005 }
1006 }
1007 return false;
1008 }
1009
1010 } // End of namespace Nuvie
1011 } // End of namespace Ultima
1012