2 // nazghul - an old-school RPG engine
3 // Copyright (C) 2002, 2003 Gordon McNutt
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License as published by the Free
7 // Software Foundation; either version 2 of the License, or (at your option)
10 // This program is distributed in the hope that it will be useful, but WITHOUT
11 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 // You should have received a copy of the GNU General Public License along with
16 // this program; if not, write to the Free Foundation, Inc., 59 Temple Place,
17 // Suite 330, Boston, MA 02111-1307 USA
20 // gmcnutt@users.sourceforge.net
35 #include "dup_constants.h"
39 #include "cmd.h" // for ui_get_direction()
42 #include "formation.h"
52 #define CAMPING_TIME_ACCELERATION (TURNS_PER_HOUR)
55 #define DIRLOC(dir,place,coord) { \
57 (coord) = place_w((place)) - 1; \
59 (coord) = place_w((place)) / 2; \
64 extern void play_update_place_viewer(void); /* hack */
67 static bool apply_damage(class Character * pm, void *amount)
69 pm->damage(*((int *)amount));
73 void PlayerParty::damage(int amount)
75 /* First apply damage to the vehicle. If the vehicle is destroyed then
76 destroy the party, too. */
77 if (vehicle && vehicle->isVulnerable()) {
79 // Subtle: save a pointer to the vehicle. If vehicle->damage()
80 // actually destroys the vehicle, then in a roundabout way by
81 // the time that call returns we will have disembarked (meaning
82 // our usual vehicle pointrer will be NULL). But we still bear
83 // responsibility hear for destroying the vehicle. Messy, but
84 // there you go. It works and I don't care to fool with it.
85 class Vehicle *vehptr = vehicle;
88 vehicle->damage(amount);
92 /* If the vehicle is destroyed then we are dead. */
93 if (vehptr->isDestroyed()) {
102 /* Apply damage to all party members. If they all die then the party is
104 forEachMember(apply_damage, &amount);
107 static bool pc_get_first_living(class Character * pm, void *data)
109 class Character **pc = (class Character**)data;
117 static bool pc_check_if_alive(class Character * pm, void *data)
119 int *count = (int *) data;
127 static bool pc_check_if_not_immobilized(class Character * pm, void *data)
129 int *count = (int *) data;
130 if (!pm->isAsleep()) {
137 static bool pc_eat_food(class Character * pm, void *data)
143 if (player_party->food) {
144 player_party->food--;
148 log_msg("µ²¤¨»à¤Ë¤·¤½¤¦¤À¡ª");
150 /* Partial fix for SF BUG [ 1526910 ] "starvation is lame". Instead of
151 * a fixed amount of damage, make damage proportional to existing
152 * health. The player can starve for a while before dying, but the loss
153 * of HP is a serious threat now even at high levels. */
154 int damage = max(pm->getHp()/2, 1);
155 apply_damage(pm, &damage);
159 void PlayerParty::changePlaceHook()
164 log_banner("%s¤Ë¤òƧ¤ßÆþ¤ì¤¿", Place->name);
166 // --------------------------------------------------------------------
167 // If the party is relocating to a non-wilderness place then I have to
169 // --------------------------------------------------------------------
171 if (! place_is_wilderness(place)) {
172 distributeMembers(place, x, y, dx, dy);
175 foogod_set_title("¹ÓÌî¤Îι");
180 enum move_result PlayerParty::check_move_to(struct move_info *info)
182 class Party *npc_party;
186 return move_null_place;
188 info->x = place_wrap_x(info->place, info->x);
189 info->y = place_wrap_y(info->place, info->y);
192 if (place_off_map(info->place, info->x, info->y)) {
195 // occupied (this handles occupied vehicles, too)
196 if ((npc_party = place_get_Party(info->place, info->x, info->y))) {
197 if (are_hostile(this, npc_party)) {
198 if (info->place->wilderness) {
199 info->npc_party = npc_party;
201 return move_enter_combat;
203 return move_occupied;
206 if (vehicle && place_get_vehicle(info->place, info->x, info->y))
207 return move_occupied;
210 // * t t exit vehicle
214 info->subplace = place_get_subplace(info->place, info->x, info->y);
215 if (info->subplace) {
217 return move_not_in_vehicle;
218 return move_enter_subplace;
221 // passable? I moved this check to be before a test for portals. A
222 // portal is accessible iff the player can move across the terrain it
223 // is placed on. The "ladder in the lake to the ladder on land" test
224 // case just plain looks counterintuitive without it.
225 if (!place_move_is_passable(info->place, info->x - info->dx, info->y-info->dy, info->x, info->y, this,
227 return move_impassable;
232 bool PlayerParty::turn_vehicle(void)
236 // Three possible outcomes:
238 // 1. We do NOT turn any vehicle, therefore we want to continue
239 // processing the move() command which calls us and we do NOT want to
240 // consume any turns (e.g., no vehicle, or horse turning north/south).
242 // 2. We DO turn a vehicle, but it does not cost us anything (e.g.,
243 // horse turning east/west).
245 // 3. We do turn a vehicle and it cost us a turn (e.g., ship).
247 if (!vehicle || !vehicle->turn(dx, dy, &cost) || !vehicle->mustTurn())
254 /* Note it is not safe to modify the member list within the block of the below
256 #define FOR_EACH_MEMBER(e,c) \
257 for ((e) = node_next(&members), (c) = (class Character *)(e)->ptr; \
259 (e) = node_next(e), (c) = (class Character *)(e)->ptr)
261 #define FOR_EACH_REVERSE_MEMBER(e,c) \
262 for ((e) = node_prev(&members), (c) = (class Character *)(e)->ptr; \
264 (e) = node_prev(e), (c) = (class Character *)(e)->ptr)
267 void PlayerParty::distributeMembers(struct place *new_place, int new_x,
268 int new_y, int new_dx, int new_dy)
270 // --------------------------------------------------------------------
271 // Remove the player party from the current place and remove its view
272 // from the map viewer's consideration (instead we'll use the character
273 // views, which are added as each character is placed on the map
275 // --------------------------------------------------------------------
279 // --------------------------------------------------------------------
280 // Switch the current place to the portal destination place.
281 // --------------------------------------------------------------------
285 // --------------------------------------------------------------------
286 // Set the new destination place to be the subject of the map
287 // viewer. Center the camera on the portal destination.
288 // --------------------------------------------------------------------
290 mapSetPlace(new_place);
291 mapCenterCamera(new_x, new_y);
294 // --------------------------------------------------------------------
295 // Distribute all the party members centered on the portal destination
296 // point. I assume this always succeeds, which means I'm relying on map
297 // editors to be nice and make nice, clear areas around their portal
300 // For now, I'll reuse the existing combat placement algorithm. Compare
301 // the code below with position_player_party() in combat.c:
302 // --------------------------------------------------------------------
305 class Character *member;
307 FOR_EACH_REVERSE_MEMBER(entry, member) {
308 if (!member->isDead()) {
312 if (member->putOnMap(new_place, new_x, new_y,
316 // Try again, ignoring other objects.
317 flags |= PFLAG_IGNOREMECHS;
318 flags |= PFLAG_IGNOREBEINGS;
319 if (member->putOnMap(new_place, new_x, new_y,
323 // Try again, ignoring hazards.
324 flags |= PFLAG_IGNOREHAZARDS;
325 flags |= PFLAG_IGNOREFIELDS;
326 if (member->putOnMap(new_place, new_x, new_y,
330 // Try again, ignoring terrain.
331 flags |= PFLAG_IGNORETERRAIN;
332 if (member->putOnMap(new_place, new_x, new_y,
336 // Ok, now that should have worked!
341 // I've messed with this a bit. I think it's confusing (even to me!) to have
342 // the party mode switched automatically. If it felt natural during play it
343 // would be ok, but it's quite the opposite.
344 #undef CONFIG_ENABLE_ROUND_ROBIN_ON_ENTRANCE_TO_HOSTILE_PLACE
345 #ifdef CONFIG_ENABLE_ROUND_ROBIN_ON_ENTRANCE_TO_HOSTILE_PLACE
346 // --------------------------------------------------------------------
347 // Set the party mode to "follow" by default, but if hostiles are in
348 // this place then set to "character" mode.
349 // --------------------------------------------------------------------
351 if (place_contains_hostiles(new_place, this)) {
352 enableRoundRobinMode();
353 combat_set_state(COMBAT_STATE_FIGHTING);
356 combat_set_state(COMBAT_STATE_LOOTING);
359 // --------------------------------------------------------------------
360 // Set the party mode to "follow" by default. If hostiles are present
361 // the user can opt to engage them manually. If hostiles are present
362 // but not visible it's confusing/annoying when the camera does not
363 // follow the party leader. This is especially true when there is only
365 // --------------------------------------------------------------------
367 if (place_contains_hostiles(new_place, this)) {
368 combat_set_state(COMBAT_STATE_FIGHTING);
375 enum MoveResult PlayerParty::try_to_enter_subplace_from_edge(
376 struct place *subplace, int dx, int dy)
383 // --------------------------------------------------------------------
384 // There should be no way to get here unless we are already in party
385 // mode, which means we are in the wilderness. And the destination
386 // should never be another wilderness.
387 // --------------------------------------------------------------------
389 assert(place_is_wilderness(getPlace()));
390 assert(!place_is_wilderness(subplace));
392 // --------------------------------------------------------------------
393 // Entering a subplace REQUIRES a direction so I know which edge of the
394 // subplace the party should enter from. If we don't have one then
395 // prompt the user to get one.
396 // --------------------------------------------------------------------
398 if (dx == 0 && dy == 0) {
399 cmdwin_spush("Enter");
400 dir = ui_get_direction();
402 dir = vector_to_8facing(dx, dy);
405 // --------------------------------------------------------------------
406 // Get the entry coordinates from the subplace.
407 // --------------------------------------------------------------------
413 return NoDestination;
415 if (place_get_edge_entrance(subplace, dir, &new_x, &new_y))
416 return NoDestination;
420 dx = directionToDx(dir);
421 dy = directionToDy(dir);
423 relocate(subplace, new_x, new_y);
428 enum MoveResult PlayerParty::try_to_move_off_map(struct move_info * info)
431 // Yes, I'm making the following unconditional with no further checks.
432 // The parent tile is _always_ and _unconditionally_ passable.
433 // Consider everything that could make that tile impassable:
434 // --Terrain on parent map... don't care
435 // --Autoportals leading elsewhere... ignore them
436 // --Npc's... they aren't allowed on town tiles in the wilderness,
437 // and if one sneaks through ignore it
438 // --Fields... unlikely, and don't care
439 // --Vehicles (when player is already in one)... ignore them (and
440 // don't allow player to abandon a vehicle over a town, otherwise
441 // we can leak vehicles since consecutive abandonments will
442 // clobber previous ones)
443 // See notes on the ship problem in discussion #1 of doc/GAME_RULES
445 if (! info->place->location.place) {
449 relocate(info->place->location.place,
450 info->place->location.x,
451 info->place->location.y);
456 MoveResult PlayerParty::move(int newdx, int newdy)
458 struct move_info info;
459 struct combat_info cinfo;
460 bool teleport = (abs(newdx) > 1 || abs(newdy) > 1);
465 // Cache the requested direction. Can't remember what we use this for
466 // or if it's still necessary.
470 // Change vehicle facing. This might consume a turn in which case
471 // that's all we'll do.
472 if (!teleport && turn_vehicle())
473 return ChangedFacing;
475 // Check the consequences of moving to the target tile.
476 memset(&info, 0, sizeof(info));
477 info.place = getPlace();
482 switch (check_move_to(&info)) {
488 // No complications. Update the turn counter based on player
489 // speed and terrain difficulties then move the player.
490 mv_cost = place_get_diagonal_movement_cost(info.place, x, y, info.x, info.y, this,0);
491 relocate(info.place, info.x, info.y);
493 mv_cost *= vehicle->getMovementCostMultiplier();
494 decActionPoints(mv_cost);
496 extern int DeveloperMode;
498 log_msg("mv_cost=%d\n", mv_cost);
501 // none of this is used atm
503 // // reload mv_cost because diagonal movement shouldnt affect 'very slow' etc
504 //mv_cost = place_get_movement_cost(info.place, info.x, info.y, this,0);
506 // mv_cost *= vehicle->getMovementCostMultiplier();
507 //if (mv_cost < getSpeed()) {
509 //} else if (mv_cost < (getSpeed() * 2)) {
510 // progress = "-slow";
512 // progress = "-very slow";
515 //consolePrint("Move %s%s [%d AP]\n",
516 // directionToString(vector_to_8facing(dx, dy)),
517 // progress, mv_cost);
523 return try_to_move_off_map(&info);
527 // Occupied by a friendly npc party. If they were unfriendly
528 // we'd be going into combat.
531 case move_impassable:
532 // Impassable terrain.
533 return WasImpassable;
535 case move_not_in_vehicle:
538 case move_enter_combat:
539 // Handle combat (possible multiple combats) until we're all
541 memset(&cinfo, 0, sizeof(cinfo));
544 // ------------------------------------------------------------
545 // Enter combat on the current square, not the square we're
546 // trying to move onto.
547 // ------------------------------------------------------------
554 combat_enter(&cinfo);
559 case move_enter_subplace:
561 #if OLD_PRE_ENTRY_INVOCATION
562 // Going to try and move this call to place_enter()... as I
563 // recall I put this here to allow an on-entry hook to prompt
564 // the player to see if he really wants to enter.
566 // run the place's pre-entry hook, if applicable
567 if (info.subplace->pre_entry_hook &&
568 ! closure_exec(info.subplace->pre_entry_hook, "pp",
569 info.subplace, this))
572 return try_to_enter_subplace_from_edge(info.subplace, info.dx,
576 // no other results expected
578 return NotApplicable;
582 struct sprite *PlayerParty::getSprite(void)
585 return vehicle->getSprite();
590 char *PlayerParty::get_movement_description(void)
594 return vehicle->getMvDesc();
597 sound_t *PlayerParty::get_movement_sound(void)
600 return vehicle->get_movement_sound();
605 bool PlayerParty::add(class ObjectType * type, int quantity)
611 type->describeType(quantity);
612 log_end("¤ò¼ê¤ËÆþ¤ì¤¿¡£");
614 return inventory->add(type, quantity);
617 bool PlayerParty::takeOut(ObjectType *type, int q)
619 // Some types can be in more than one category, so remove from all.
621 type->describeType(q);
622 log_end("¤ò¼º¤Ã¤¿¡£");
623 return inventory->takeOut(type, q);
626 static bool player_member_rest_one_hour(class Character * pm, void *data)
633 void PlayerParty::exec()
639 // NOTE: by running startTurn() after this we are skipping the party
640 // members' start-of-party-turn hooks when resting. That's probably not
645 /* After each hour of rest heal/restore party members */
646 if (clock_alarm_is_expired(&rest_alarm)) {
647 forEachMember(player_member_rest_one_hour, NULL);
648 clock_alarm_set(&rest_alarm, 60);
651 if (clock_alarm_is_expired(&wakeup_alarm)) {
660 absorbMemberAPDebt();
665 if (clock_alarm_is_expired(&wakeup_alarm))
672 if (action_points > 0 && ! isDestroyed()) {
674 /* In party mode increment the turn count whenever the player
675 * moves. I'm assuming he'll move at most once per turn. */
676 session_inc_turn_count();
681 if (Session->reloaded)
682 /* Hack: this object has been destroyed with the old
683 * session. Leave now! */
690 bool PlayerParty::allDead(void)
693 forEachMember(pc_check_if_alive, &count);
697 bool PlayerParty::immobilized(void)
700 forEachMember(pc_check_if_not_immobilized, &count);
709 bool myGetBestVisionRadius(class Character * c, void *data)
711 struct VradInfo *info = (struct VradInfo *) data;
712 info->vrad = max(info->vrad, c->getVisionRadius());
713 info->light = max(info->light, c->getLight());
717 int PlayerParty::getLight()
720 class Character *member;
724 FOR_EACH_MEMBER(entry, member) {
725 light += member->getLight();
731 int PlayerParty::getVisionRadius()
733 struct VradInfo info;
735 // Scan all party members to find the best light source and the best
736 // eyes. They needn't be from the same member since I assume they are
737 // traveling close enough to share their light sources with each other.
740 forEachMember(myGetBestVisionRadius, &info);
742 // hack -- should replace this with a routine for getting the ambient
743 // light from the local environment (place?)
745 if (!getPlace()->underground)
746 info.light += sky_get_ambient_light(&Session->sky);
748 // Set the vision radius to the minimum of the best available light
749 // radius or the best available vision radius of all party members
750 light = max(info.light, MIN_PLAYER_LIGHT);
755 return min(info.vrad, MAX_VISION_RADIUS);
758 PlayerParty::PlayerParty()
767 mv_sound = NULL_SOUND;
776 campsite_formation = 0;
782 control_mode = PARTY_CONTROL_ROUND_ROBIN;
783 ctrl = ctrl_party_ui;
786 setTurnsToNextMeal(TURNS_PER_FOOD);
787 setBaseFaction(PLAYER_PARTY_FACTION);
788 view = mapCreateView();
791 PlayerParty::PlayerParty(char *_tag,
792 struct sprite *sprite,
793 char *movement_desc, sound_t *movement_sound,
794 int _food, int _gold,
795 struct formation *_formation,
796 struct terrain_map *_camping_map,
797 struct formation *_camping_formation)
799 /* Do standard initialization first. */
810 formation = _formation;
811 campsite_map = _camping_map;
812 campsite_formation = _camping_formation;
818 control_mode = PARTY_CONTROL_ROUND_ROBIN;
819 ctrl = ctrl_party_ui;
823 // --------------------------------------------------------------------
824 // Location (if any) will be set after construction by the loading
825 // code. Initially the party is off-map and on the orphan list.
826 // --------------------------------------------------------------------
830 setTurnsToNextMeal(TURNS_PER_FOOD);
831 setBaseFaction(PLAYER_PARTY_FACTION);
832 view = mapCreateView();
834 /* Now use the arguments */
841 this->sprite = sprite;
843 this->mv_desc = strdup(movement_desc);
844 assert(this->mv_desc);
849 this->mv_sound = movement_sound;
852 PlayerParty::~PlayerParty()
857 obj_dec_ref_safe(inventory);
859 // Bugfix: when quitting while on board a vehicle, force the vehicle to
860 // give up its reference to the player (otherwise this causes an
861 // assert in session_del()).
863 vehicle->setOccupant(0);
864 vehicle->relocate(getPlace(), getX(), getY());
865 obj_dec_ref(vehicle);
869 /* fixme: need to somehow cancel those wq jobs setup by player_init? */
872 void PlayerParty::board_vehicle(void)
876 // already in a vehicle so exit
880 log_end("¤«¤é¹ß¤ê¤¿¡£");
881 vehicle->setOccupant(0);
882 vehicle->relocate(getPlace(), getX(), getY());
883 obj_dec_ref(vehicle);
887 /* Erase vehicle hull stats. */
892 vehicle = place_get_vehicle(Place, x, y);
897 obj_inc_ref(vehicle);
901 log_end("¤Ë¾è¤Ã¤¿¡£");
903 vehicle->setOccupant(this);
907 /* Show vehicle hull stats. */
910 /* Prompt the player to name the vehicle so it isn't
911 * garbage-collected. */
912 if (! vehicle->isNamed()) {
913 ui_name_vehicle(vehicle);
917 class Character *PlayerParty::get_leader(void)
919 if (leader == NULL) {
925 void PlayerParty::removeMember(class Character *c)
928 class Character *next_member;
931 assert(c->party == (class Party*)this);
933 // Remove all its readied arms from party inventory.
935 for (class ArmsType * weapon = c->enumerateArms(&armsIndex); weapon != NULL;
936 weapon = c->getNextArms(&armsIndex)) {
938 struct inv_entry *ie;
940 ie = inventory->search(weapon);
946 // Re-order the indices of the remaining party members
947 for (entry = node_next(c->plnode), index = c->getOrder();
949 entry = node_next(entry), index++) {
950 next_member = (class Character *)entry->ptr;
951 next_member->setOrder(index);
954 // Relinquish control.
955 c->setPlayerControlled(false);
956 c->setControlMode(CONTROL_MODE_AUTO);
961 // Unhook it from the party
962 Party::removeMember(c);
965 bool PlayerParty::addMember(class Character * c)
967 // Note: this is called so early in startup that I can't touch the
968 // paint routines in here. Callers will have to update the map and
969 // status if necessary.
971 assert(!c->isPlayerControlled());
973 // Hook it to the party
976 // Make sure it's not going to try and run a schedule
977 c->setSchedule(NULL);
979 // Set special player-controlled flag
980 c->setPlayerControlled(true);
982 // gmcnutt: added this as a hack to support quickly determining if a
983 // character belongs to the player party.
984 c->party = (Party*)this;
986 // Can't think of any reason why a char should be on the orphan list
989 if (NULL == c->getView())
990 c->setView(mapCreateView());
992 // Loop over all readied weapons and add them to player inventory. Also
993 // must set the refcount once they are in inventory.
996 for (class ArmsType * weapon = c->enumerateArms(&armsIndex);
997 weapon != NULL; weapon = c->getNextArms(&armsIndex)) {
999 struct inv_entry *ie;
1002 ie = inventory->search(weapon);
1008 // Note: leave the readied arms as-is and character inventory as-is for
1011 // --------------------------------------------------------------------
1012 // Reevaluate leadership with the new member added to the group.
1013 // --------------------------------------------------------------------
1014 if (! c->engagedInTask()) {
1015 switch (getPartyControlMode()) {
1016 case PARTY_CONTROL_FOLLOW:
1017 if (c != get_leader()) {
1018 c->setControlMode(CONTROL_MODE_FOLLOW);
1022 case PARTY_CONTROL_SOLO:
1023 c->setControlMode(CONTROL_MODE_IDLE);
1026 case PARTY_CONTROL_ROUND_ROBIN:
1027 c->setControlMode(CONTROL_MODE_PLAYER);
1036 // Call statusSetMode() to force it to re-evaluate the necessary screen
1037 // size for showing the party.
1038 statusSetMode(ShowParty);
1040 // Force a repaint on the status window.
1047 void PlayerParty::paint(int sx, int sy)
1050 vehicle->paint(sx, sy);
1052 Object::paint(sx, sy);
1055 const char *PlayerParty::getName()
1060 bool PlayerParty::isVisible()
1065 void PlayerParty::describe()
1067 log_continue("%s", getName());
1070 struct formation *PlayerParty::get_formation()
1072 if (vehicle && vehicle->get_formation())
1073 return vehicle->get_formation();
1074 if (camping && campsite_formation)
1075 return campsite_formation;
1079 int PlayerParty::get_num_living_members(void)
1082 forEachMember(pc_check_if_alive, &count);
1086 class Character *PlayerParty::get_first_living_member(void)
1088 class Character *pc = NULL;
1089 forEachMember(pc_get_first_living, &pc);
1093 void PlayerParty::beginLoitering(int hours)
1095 struct node *entry = 0;
1096 class Character *member = 0;
1101 log_msg("¤¦¤í¤¦¤í¤·¤Æ¤¤¤ë¡Ä");
1102 FOR_EACH_MEMBER(entry, member) {
1103 member->beginLoitering(hours);
1107 clock_alarm_set(&wakeup_alarm, hours * 60);
1110 /* Bugfix for [ 1629974 ] "loitering in wilderness uses too much
1111 * food". We want the acceleration to be one hour per turn at the
1112 * current map scale. CAMPING_TIME_ACCELERATION is appropriate for a
1113 * town-scale wilderness combat map (where camping typically occurs),
1114 * but party loitering is done at wilderness scale. */
1115 session_set_time_accel((float)CAMPING_TIME_ACCELERATION
1116 / (float)place_get_scale(getPlace()));
1119 bool PlayerParty::isLoitering()
1124 void PlayerParty::endLoitering()
1127 class Character *member;
1130 session_set_time_accel(1);
1132 FOR_EACH_MEMBER(entry, member) {
1133 member->endLoitering();
1140 void PlayerParty::forceAbortLoitering()
1145 class Character *member;
1148 FOR_EACH_MEMBER(entry, member) {
1149 member->resetActionPoints();
1155 static bool member_begin_resting(class Character *member, void *data)
1157 member->beginResting(*((int*)data));
1161 void PlayerParty::beginResting(int hours)
1166 log_msg("µÙ©¤ò»Ï¤á¤¿¡Ä");
1167 forEachMember(member_begin_resting, &hours);
1172 clock_alarm_set(&wakeup_alarm, hours * 60);
1173 clock_alarm_set(&rest_alarm, 60);
1179 // Accelerate time while resting.
1180 session_set_time_accel(CAMPING_TIME_ACCELERATION);
1183 bool PlayerParty::isResting()
1185 return (resting || camping);
1188 void PlayerParty::throw_out_of_bed()
1190 assert(isResting());
1194 class Character *PlayerParty::getMemberAtIndex(int index)
1197 class Character *member;
1199 FOR_EACH_MEMBER(entry, member) {
1208 static bool member_uncharm(class Character *member, void *data)
1214 void PlayerParty::unCharmMembers()
1216 forEachMember(member_uncharm, NULL);
1219 bool PlayerParty::addToInventory(class Object *object)
1221 // ---------------------------------------------------------------------
1222 // This is the overloaded generic Object method. For now it is just a
1223 // wrapper for the pre-existing, historical add_to_inventory() method.
1225 // Probably about the time I convert the player inventory to use
1226 // objects instead of types I'll remove add_to_inventory() and roll the
1227 // implementation into here.
1228 // ---------------------------------------------------------------------
1230 add(object->getObjectType(), object->getCount());
1231 obj_dec_ref(object);
1235 bool PlayerParty::isCamping()
1240 void PlayerParty::beginCamping(class Character *guard, int hours)
1243 class Character *member;
1249 && ! camp_guard->isDead()
1250 && ! camp_guard->isAsleep())
1251 camp_guard->beginGuarding(hours);
1253 FOR_EACH_MEMBER(entry, member) {
1254 if (member != camp_guard)
1255 member->beginCamping(hours);
1258 // Accelerate time while camping.
1259 session_set_time_accel(CAMPING_TIME_ACCELERATION);
1262 void PlayerParty::endCamping()
1265 class Character *member;
1272 // Un-accelerate time when done camping.
1273 session_set_time_accel(1);
1275 if (NULL != camp_guard) {
1276 camp_guard->endGuarding();
1280 FOR_EACH_MEMBER(entry, member) {
1281 if (member != camp_guard)
1282 member->endCamping();
1287 void PlayerParty::ambushWhileCamping()
1290 class Character *member;
1294 // Un-accelerate time when done camping.
1295 session_set_time_accel(1);
1297 FOR_EACH_MEMBER(entry, member) {
1299 // ------------------------------------------------------------
1300 // If there's a guard then he/she/it will wake everybody else
1302 // ------------------------------------------------------------
1304 if (camp_guard != NULL) {
1305 member->endResting();
1308 // ------------------------------------------------------------
1309 // Since there is no guard each member will roll to wake up.
1310 // ------------------------------------------------------------
1313 member->ambushWhileCamping();
1318 camp_guard->endGuarding();
1321 void PlayerParty::endResting()
1324 class Character *member;
1328 // Un-accelerate time when done resting.
1329 session_set_time_accel(1);
1332 FOR_EACH_MEMBER(entry, member) {
1333 member->endResting();
1342 // -----------------------------------------------------------------------------
1344 // Party control mode functions
1346 // -----------------------------------------------------------------------------
1348 static bool member_set_control_mode(class Character *member, void *data)
1350 if (member->isPlayerControlled()
1351 && ! member->engagedInTask()
1353 member->setControlMode(*((enum control_mode*)data));
1358 enum party_control PlayerParty::getPartyControlMode()
1360 return control_mode;
1363 void PlayerParty::disableCurrentMode()
1366 solo_member->setSolo(false);
1371 leader->setLeader(false);
1376 void PlayerParty::enableFollowMode()
1378 enum control_mode mode = CONTROL_MODE_FOLLOW;
1380 disableCurrentMode();
1381 forEachMember(member_set_control_mode, &mode);
1383 if (NULL == leader) {
1384 enableRoundRobinMode();
1386 control_mode = PARTY_CONTROL_FOLLOW;
1387 foogod_set_title("ÄÉÀ×: %s", leader->getName());
1393 void PlayerParty::enableRoundRobinMode()
1395 enum control_mode mode = CONTROL_MODE_PLAYER;
1397 disableCurrentMode();
1398 forEachMember(member_set_control_mode, &mode);
1399 control_mode = PARTY_CONTROL_ROUND_ROBIN;
1400 foogod_set_title("ÄÉÀ×: <̤Äê>");
1404 void PlayerParty::enableSoloMode(class Character *solo)
1406 enum control_mode mode = CONTROL_MODE_IDLE;
1408 assert(solo->party == (class Party*)this);
1410 disableCurrentMode();
1411 forEachMember(member_set_control_mode, &mode);
1412 solo->setSolo(true);
1414 control_mode = PARTY_CONTROL_SOLO;
1415 foogod_set_title("ñÆÈ: %s", solo->getName());
1419 void PlayerParty::chooseNewLeader()
1421 if (NULL != leader) {
1422 leader->setLeader(false);
1427 class Character *member;
1429 FOR_EACH_MEMBER(entry, member) {
1430 if (member->canBeLeader()) {
1436 if (NULL != leader) {
1437 leader->setLeader(true);
1438 if (PARTY_CONTROL_FOLLOW == control_mode) {
1439 foogod_set_title("ÄÉÀ×: %s", leader->getName());
1445 void PlayerParty::setLeader(class Character *character)
1450 bool PlayerParty::rendezvous(struct place *place, int rx, int ry)
1456 class Character *member;
1458 assert(NULL != leader);
1460 // --------------------------------------------------------------------
1461 // If any member cannot find a path at least this short than rendezvous
1463 // --------------------------------------------------------------------
1467 // --------------------------------------------------------------------
1468 // Center the camera on the rendezvous point.
1469 // --------------------------------------------------------------------
1471 mapCenterCamera(rx, ry);
1474 // --------------------------------------------------------------------
1475 // Have each party member find and store a path to the rendezvous
1477 // --------------------------------------------------------------------
1479 FOR_EACH_MEMBER(entry, member) {
1481 struct astar_search_info as_info;
1483 // ------------------------------------------------------------
1484 // If the member already has a cached path then clean it up.
1485 // ------------------------------------------------------------
1486 if (member->cachedPath) {
1487 astar_path_destroy(member->cachedPath);
1488 member->cachedPathPlace = NULL;
1489 member->cachedPath = NULL;
1494 || !member->isOnMap()
1496 || (member->getX() == rx
1497 && member->getY() == ry))
1500 /* The following works for sleeping, but in general is not a
1501 * good solution. What about paralyzation, or other effects
1502 * unknown to the kernel? What will happen is these effects
1503 * will be ignored in the code below. Although technically not
1504 * correct behavior, it shouldn't crash or make the game
1506 if (member->isAsleep()
1507 || member->engagedInTask()
1508 || ! member->isPlayerControlled()
1514 memset(&as_info, 0, sizeof (as_info));
1515 as_info.x0 = member->getX();
1516 as_info.y0 = member->getY();
1519 as_info.flags = PFLAG_IGNOREBEINGS;
1520 member->cachedPath = place_find_path(place, &as_info,
1523 if (!member->cachedPath) {
1524 log_msg("%s¤Ï½¸¹ç¤Ç¤¤Ê¤¤¡ª",
1528 else if (max_path_len > 0 &&
1529 member->cachedPath->len > max_path_len) {
1530 log_msg("%s¤Ï±ó¤¹¤®¤ë¡ª",
1536 // --------------------------------------------------------------------
1537 // If anyone could not find a path then abort.
1538 // --------------------------------------------------------------------
1541 FOR_EACH_MEMBER(entry, member) {
1542 if (member->cachedPath) {
1543 astar_path_destroy(member->cachedPath);
1544 member->cachedPath = 0;
1550 // --------------------------------------------------------------------
1551 // Otherwise everyone has a path, so have them each take turns
1552 // following their own path to the rendezvous point.
1553 // --------------------------------------------------------------------
1558 //consolePrint(".");
1559 FOR_EACH_MEMBER(entry, member) {
1561 struct astar_node *tmp;
1563 // already arrived in an earlier iteration
1564 if (!member->cachedPath)
1567 // should always be at least two nodes
1568 assert(member->cachedPath->next);
1571 if (member->cachedPath->next->x == rx &&
1572 member->cachedPath->next->y == ry) {
1573 astar_node_destroy(member->cachedPath);
1574 member->cachedPath = 0;
1575 //member->remove(); // try this... seems ok
1583 member->cachedPath->next->x - member->getX(),
1584 member->cachedPath->next->y - member->getY());
1586 // clean up used path node
1587 tmp = member->cachedPath;
1588 member->cachedPath = member->cachedPath->next;
1589 astar_node_destroy(tmp);
1591 // Bugfix: after rendezvous party members sometimes
1592 // have a heavy action point debt, penalizing them in
1593 // the new place. Zero out the debt during rendezvous
1595 member->resetActionPoints();
1601 //consolePrint("Ok!\n");
1607 int PlayerParty::getContext(void)
1609 return (isOnMap() ? CONTEXT_WILDERNESS : CONTEXT_TOWN);
1612 void PlayerParty::addView()
1615 mapSetPlace(getPlace());
1619 struct place *PlayerParty::getPlaceFromMembers(void)
1622 class Character *member;
1624 member = get_leader();
1627 return member->getPlace();
1629 FOR_EACH_MEMBER(elem, member) {
1630 if (member->isOnMap()) {
1631 return member->getPlace();
1638 void PlayerParty::startSession(void)
1641 class Character *member;
1647 // --------------------------------------------------------------------
1648 // Not on the map, so we must be in a small-scale place. At least one
1649 // of the members needs to be on the map or I can't figure out where
1650 // the starting place is. Once I know, I can setup the map viewer and
1651 // add all the member views.
1652 // --------------------------------------------------------------------
1654 Place = getPlaceFromMembers();
1664 FOR_EACH_MEMBER(elem, member) {
1665 if (member->isOnMap()) {
1670 // --------------------------------------------------------------------
1671 // Set the party mode to "follow" by default, but if hostiles are in
1672 // this place then set to "character" mode.
1673 // --------------------------------------------------------------------
1675 if (place_contains_hostiles(getPlace(), this)) {
1676 enableRoundRobinMode();
1677 combat_set_state(COMBAT_STATE_FIGHTING);
1680 combat_set_state(COMBAT_STATE_DONE);
1685 void PlayerParty::setOnMap(bool val)
1687 if (val == isOnMap())
1690 Object::setOnMap(val);
1692 // --------------------------------------------------------------------
1693 // If the party is going from off-map to on, then remove it from the
1694 // orphan list. If the opposite then add it.
1695 // --------------------------------------------------------------------
1699 session_rm_obj(Session, this);
1703 session_add_obj(Session, this, player_dtor, player_save, NULL);
1707 void player_dtor(void *val)
1709 class PlayerParty *party = (class PlayerParty*)val;
1713 void player_save(save_t *save, void *val)
1715 ((class PlayerParty*)val)->save(save);
1718 void PlayerParty::save(save_t *save)
1720 save->enter(save, "(let ((kplayer ");
1722 save->enter(save, "(kern-mk-player\n");
1724 save->write(save, "'%s\n", tag);
1726 save->write(save, "nil\n");
1727 save->write(save, "%s\n", sprite_get_tag(this->sprite));
1728 save->write(save, "\"%s\"\n", this->mv_desc);
1729 if (NULL_SOUND != mv_sound)
1730 save->write(save, "%s\n", sound_get_tag(this->mv_sound));
1732 save->write(save, "nil\n");
1733 save->write(save, "%d %d\n", food, gold);
1734 save->write(save, "%d ;; turns to next meal\n",
1735 turns_to_next_meal);
1736 save->write(save, "%s\n",
1737 this->formation ? this->formation->tag : "nil");
1738 save->write(save, "%s\n",
1739 this->campsite_map ? this->campsite_map->tag : "nil");
1740 save->write(save, "%s\n",
1741 this->campsite_formation ?
1742 this->campsite_formation->tag : "nil");
1745 vehicle->save(save);
1747 save->write(save, "nil ; player's vehicle\n");
1749 inventory->save(save);
1751 if (list_empty(nodelst(&this->members))) {
1752 save->write(save, "nil\n");
1755 class Character *ch;
1757 save->enter(save, "(list\n");
1758 FOR_EACH_MEMBER(elem,ch) {
1759 char_save(save, ch);
1761 save->exit(save, ") ; list of party members\n");
1764 save->exit(save, ")))\n"); // end ((kplayer (kern-mk-player ...)))
1767 save->enter(save, "(bind kplayer ");
1768 gob_save(getGob(), save);
1769 save->exit(save, ")\n");
1772 save->exit(save, ")\n"); // end (let ...) block
1775 bool PlayerParty::addFood(int amount)
1781 log_msg("%d¤Î¿©ÎÁ¤ò¼ê¤ËÆþ¤ì¤¿¡£", amount);
1783 log_msg("%d¤Î¿©ÎÁ¤ò¼º¤Ã¤¿¡£", amount);
1793 bool PlayerParty::addGold(int amount)
1799 log_msg("%d¤Î¶â¤òÆÀ¤¿¡£", amount);
1801 log_msg("%d¤Î¶â¤ò¼º¤Ã¤¿¡£", -amount);
1811 bool PlayerParty::isPlayerControlled()
1816 void PlayerParty::advanceTurns(int turns)
1818 // Check if its time to eat
1819 turns_to_next_meal -= turns;
1820 if (turns_to_next_meal <= 0) {
1821 forEachMember(pc_eat_food, 0);
1823 turns_to_next_meal += TURNS_PER_FOOD;
1827 void PlayerParty::setTurnsToNextMeal(int turns)
1829 turns_to_next_meal = turns;
1832 bool PlayerParty::hasInInventory(class ObjectType *type)
1834 return inventory->search(type) != NULL;
1837 void PlayerParty::unrefInventoryObject(ObjectType *type)
1839 struct inv_entry *ie = inventory->search(type);
1844 void PlayerParty::refInventoryObject(ObjectType *type)
1846 struct inv_entry *ie = inventory->search(type);
1851 void PlayerParty::setInventoryContainer(Container *val)
1857 obj_inc_ref(inventory);
1860 void PlayerParty::addExperience(int val)
1863 class Character *member;
1865 FOR_EACH_MEMBER(entry, member) {
1866 if (!member->isDead()) {
1867 member->addExperience(val);
1872 bool PlayerParty::canSeeLocation(struct place *place, int x, int y)
1874 // fixme -- ref naz.scm (any-player-party-member-visible?)
1878 /* shifts currently wielded items to the top of the equipment list */
1879 void PlayerParty::sortReadiedItems(class Character * member)
1881 struct inv_entry *ie;
1883 for (class ArmsType * weapon = member->enumerateArms(&armsIndex); weapon != NULL;
1884 weapon = member->getNextArms(&armsIndex)) {
1886 ie = inventory->search(weapon);
1888 inventory->moveToFront(ie);
1892 class Container *PlayerParty::getInventory()
1894 // yes, I know it's public, but this is polymorphic with the Party base class