1 //
2 // nazghul - an old-school RPG engine
3 // Copyright (C) 2002, 2003 Gordon McNutt
4 //
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)
8 // any later version.
9 //
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
13 // more details.
14 //
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
18 //
19 // Gordon McNutt
20 // gmcnutt@users.sourceforge.net
21 //
22 #include "character.h"
23 #include "conv.h"
24 #include "dice.h"
25 #include "effect.h"
26 #include "gob.h"
27 #include "map.h"
28 #include "console.h"
29 #include "place.h"
30 #include "Container.h"
31 #include "Arms.h"
32 #include "player.h"
33 #include "status.h"
34 #include "sound.h"
35 #include "common.h"
36 #include "screen.h"
37 #include "knapsack.h"
38 #include "occ.h"
39 #include "species.h"
40 #include "sched.h"
41 #include "combat.h"
42 #include "cmdwin.h"
43 #include "terrain.h"
44 #include "cmd.h"
45 #include "event.h"
46 #include "vehicle.h"
47 #include "foogod.h"
48 #include "ctrl.h"
49 #include "session.h"
50 #include "sprite.h"
51 #include "mmode.h"
52 #include "log.h"
53 #include "factions.h"
54 #include "kern_intvar.h"
55 
56 #include <string.h>
57 #include <stdlib.h>
58 #include <math.h>
59 
60 /**
61  * Used when calculating an evasion vector away from hostiles.
62  */
63 struct evasionVectorInfo {
64         class Character *subj; /**< The character doing the evading. */
65         int dx;          /**< The x component of the evasion vector. */
66         int dy;          /**< The y component of the evasion vector. */
67 };
68 
wrapReady(void * item,void * context)69 static int wrapReady(void *item, void *context)
70 {
71 	class ArmsType *type = (class ArmsType *) item;
72 	class Character *npc = (class Character *) context;
73 	return (npc->hasAmmo(type) && npc->ready(type) == Character::Readied);
74 }
75 
wrapUnready(void * item,void * context)76 static void wrapUnready(void *item, void *context)
77 {
78 	class ArmsType *type = (class ArmsType *) item;
79 	class Character *npc = (class Character *) context;
80 	npc->unready(type);
81 
82 }
83 
myConsiderArms(struct inv_entry * ie,void * data)84 static void myConsiderArms(struct inv_entry *ie, void *data)
85 {
86 	struct knapsack *ks = (struct knapsack *) data;
87 	if (!ie->type->isType(ARMS_TYPE_ID))
88 		return;
89 	class ArmsType *arms = (class ArmsType *) ie->type;
90 	int val = 2 * dice_average(arms->getDamageDice()) * arms->getRange()
91                 + 2 * dice_average(arms->getArmorDice())
92                 + 2 * dice_average(arms->getToDefendDice())
93                 + dice_average(arms->getToHitDice())
94                 ;
95         if (val) {
96                 for (int i = 0;
97                      i < ie->count && ks->n_items < MAX_N_ITEMS;
98                      i++, ks->n_items++) {
99                         ks->item[ks->n_items] = arms;
100                         ks->value[ks->n_items] = val;
101                 }
102         }
103 }
104 
105 /**
106  * Accumulate the sum of the vectors from hostile foes. This is passed as an
107  * argument to place_for_each() to scan for hostiles. It sums up the vectors
108  * from hostile foes to the subject in order to calculate the best vector to
109  * flee.
110  *
111  * @param obj An object in the current place.
112  * @param data The evasion vector struct which refers to the calling object and
113  * accumulates results.
114  */
getEvasionVectorVisitor(Object * obj,void * data)115 static void getEvasionVectorVisitor(Object *obj, void *data)
116 {
117         struct evasionVectorInfo *info;
118         class Character *subj;
119 
120         info = (struct evasionVectorInfo *)data;
121         subj = info->subj;
122 
123         /* Filter out non-beings */
124         if (obj->getLayer() != being_layer)
125                 return;
126 
127         /* Filter out non-hostiles */
128         if (! are_hostile(subj, (class Being*)obj))
129                 return;
130 
131 #if 0
132         /* Cheat a bit here, both for performance and to increase the
133          * effectiveness of townsfolk trying to flee raiders. */
134 
135         /* Filter out objects not in los of the subject */
136         if (! place_in_los(subj->getPlace(),subj->getX(),subj->getY(),
137                                    obj->getPlace(),obj->getX(),obj->getY()))
138                 return;
139 
140         /* Filter out object not in the vision radius of the subject */
141         if (place_flying_distance(subj->getPlace(),subj->getX(),subj->getY(),
142                                   obj->getX(),obj->getY())
143             > subj->getVisionRadius())
144                 return;
145 
146         /* Filter out invisible objects */
147         if (! obj->isVisible())
148                 return;
149 #endif
150         /* Add the vector going from the hostile to the subject to the
151          * cumulative vector. */
152         info->dx += (subj->getX() - obj->getX());
153         info->dy += (subj->getX() - obj->getX());
154 }
155 
Character(const char * tag,const char * name,struct sprite * sprite,struct species * species,struct occ * occ,int str,int intl,int dex,int hpmod,int hpmult,int mpmod,int mpmult,int hp,int xp_,int mp,int AP_per_round,int lvl)156 Character::Character(const char *tag, const char *name,
157                      struct sprite *sprite,
158                      struct species *species, struct occ *occ,
159                      int str, int intl, int dex,
160                      int hpmod, int hpmult,
161                      int mpmod, int mpmult,
162                      int hp, int xp_,
163                      int mp,
164 		     int AP_per_round,  // aka speed
165 		     int lvl
166                      )
167         : hm(0), xp(xp_), order(-1),
168           sleeping(false),
169           ac(0),
170           str(str), intl(intl),
171           dex(dex), mana(mp), lvl(lvl),
172           solo(false), target(NULL),
173           rdyArms(NULL),
174           fleeing(false), burden(0),
175           inCombat(false),
176           container(NULL),
177           //sprite(sprite),
178           sched_chars_node(0),
179           forceContainerDrop(false)
180           , fleePathFound(false)
181           , fleeX(0)
182           , fleeY(0)
183           , fleePathFlags(0)
184           , currentMmode(0)
185           , known(false)
186           , taskname(NULL)
187           , taskproc(NULL)
188           , taskgob(NULL)
189           , taskInterruptOnDamage(false)
190 {
191         if (tag) {
192                 this->tag = strdup(tag);
193                 assert(this->tag);
194         } else {
195                 this->tag = NULL;
196         }
197 
198         setName(name);
199 
200         plnode = NULL;
201 	setPlayerControlled(false);	// by default
202 	this->current_sprite = sprite;
203 	this->light        = MIN_PLAYER_LIGHT;
204 	this->party        = 0;
205 	this->conv         = conv;
206 	this->species      = species;
207 	this->occ          = occ;
208         if (occ)
209                 occ_ref(occ);
210 	this->is_clone     = false;
211 	this->visible      = 1;
212 	this->target       = 0;
213         this->damage_sound = NULL_SOUND;
214         this->charmed      = false;
215         this->resting      = false;
216         this->loitering    = false;
217         this->guarding     = false;
218         this->mp_mod       = mpmod;
219         this->mp_mult      = mpmult;
220         this->hp_mod       = hpmod;
221         this->hp_mult      = hpmult;
222         this->sched        = NULL;
223         this->appt         = 0;
224         this->is_leader    = false;
225         this->hp           = hp;
226         this->defenseBonus = 0;
227         factionSwitch      = 0;
228         tmpFaction         = NIL_FACTION;
229         ambushedWhileCamping = false;
230 
231         setDead(hp <= 0);
232 
233         setActivity(NONE);
234 
235 
236         initCommon();
237 
238 	this->hp = min(this->hp, getMaxHp());
239 	this->mana = min(this->mana, getMaxMana());
240 	this->AP_per_round = AP_per_round;
241 
242         setOnMap(false);
243 	if (xp == 0)
244 		this->xp = getXpForLevel(this->lvl);
245 	if (xp < 0)
246 	{
247 		if (this->lvl == 1)
248 		{
249 			this->xp = rand() % getXpForLevel(this->lvl + 1);
250 		}
251 		else
252 		{
253 			this->xp = getXpForLevel(this->lvl) + (rand() % getXpForLevel(this->lvl));
254 		}
255 	}
256 }
257 
Character()258 Character::Character():hm(0), xp(0), order(-1),
259                        sleeping(false),
260                        ac(0),
261                        str(0), intl(0),
262                        dex(0), mana(0),
263 		       AP_per_round(kern_intvar_get("AP_TOTAL:normal_human")),
264 		       lvl(0),
265                        playerControlled(true), solo(false),
266                        target(NULL),
267                        rdyArms(NULL),
268                        fleeing(false), burden(0),
269                        inCombat(false),
270                        container(NULL),
271                        //sprite(0),
272                        sched_chars_node(0),
273                        forceContainerDrop(false)
274                        , fleePathFound(false)
275                        , fleeX(0)
276                        , fleeY(0)
277                        , fleePathFlags(0)
278                        , currentMmode(0)
279                        , known(false)
280                        , taskname(NULL)
281                        , taskproc(NULL)
282                        , taskgob(NULL)
283 {
284         // This method is probably obsolete now
285 
286         plnode = NULL;
287 	setPlayerControlled(false);	// by default
288         setBaseFaction(NIL_FACTION);
289 
290 	light        = MIN_PLAYER_LIGHT;
291 	tag          = 0;
292 	party        = 0;
293 	conv         = 0;
294 	species      = 0;
295 	occ          = 0;
296 	is_clone     = false;
297 	visible      = 1;
298 	occ          = 0;
299 	target       = 0;
300         damage_sound = NULL_SOUND;
301         charmed      = false;
302         resting      = false;
303         loitering    = false;
304         guarding     = false;
305         mp_mod       = 0;
306         mp_mult      = 0;
307         hp_mod       = 0;
308         hp_mult      = 0;
309         sched        = NULL;
310         appt         = 0;
311         is_leader    = false;
312         factionSwitch= 0;
313         tmpFaction   = NIL_FACTION;
314         ambushedWhileCamping = false;
315 
316 	AP_per_round = AP_per_round;
317 
318         setDead(hp <= 0);
319 
320         //assert(place);
321 
322         setActivity(NONE);
323 
324         // ------------------------------------------------------------------
325         // Initially always off-map. Loader will position us, put us in a
326         // party, or whatever.
327         // ------------------------------------------------------------------
328 
329         setPlace(0);
330         setX(-1);
331         setY(-1);
332         setOnMap(false);
333 }
334 
335 
~Character()336 Character::~Character()
337 {
338         obj_dec_ref_safe(container);
339 
340 	if (rdyArms != NULL) {
341 		free(rdyArms);
342         }
343 
344 
345         if (party)
346                 party->removeMember(this);
347 
348         if (ai)
349                 closure_unref(ai);
350 
351         /* Hack: make sure you deref occ after deleting the
352          * container. Currently the container references traps that are built
353          * into the occ struct. The container unrefs the traps, and if the occ
354          * isn't still holding a ref then you get a double-deallocation and
355          * usually a crash. This will get cleaned up when I rework the way NPCs
356          * are factoried; the traps won't be kept as part of occ any more. */
357         if (occ)
358                 occ_unref(occ);
359 
360         // subtle: use setAttackTarget to unref the target; it will do so
361         // safely even if 'this' is the target
362         if (target)
363                 setAttackTarget(NULL);
364 
365         /* remove self from session's special list for chars with multi-place
366          * schedules */
367         if (sched_chars_node)
368                 session_rm_sched_char(sched_chars_node);
369 }
370 
yes_no_ignore(struct KeyHandler * kh,int key,int keymod)371 static int yes_no_ignore(struct KeyHandler * kh, int key, int keymod)
372 {
373 	int *yesno = (int *) kh->data;
374 
375 	switch (key) {
376 	case 'y':
377 	case 'Y':
378 		*yesno = 'y';
379 		return 1;
380         case 'i':
381         case 'I':
382             *yesno = 'i';
383             return 1;
384 	case 'n':
385 	case 'N':
386 	case CANCEL:
387 		*yesno = 'n';
388 		return 1;
389 	default:
390 		return 0;
391 	}
392 }
393 
taskPromptToAbort()394 void Character::taskPromptToAbort()
395 {
396     cmdwin_clear();
397     log_continue("^c+y%s damaged - abort task?^c-\n", getName());
398     log_flush();
399     cmdwin_spush("Abort");
400     cmdwin_spush("(Y/N/Ignore)");
401     int key;
402     getkey(&key, yes_no_ignore);
403     cmdwin_pop();
404     switch (key) {
405     case 'y':
406         taskAbort();
407         cmdwin_spush("abort!");
408         break;
409     case 'i':
410         taskInterruptOnDamage = false;
411         cmdwin_spush("ignore");
412         break;
413     case 'n':
414     default:
415         cmdwin_spush("no");
416         break;
417     }
418 }
419 
damage(int amount)420 void Character::damage(int amount)
421 {
422         if (hp <= 0)
423                 return;
424 
425         // This will run the "on-damage-hook":
426         Object::damage(amount);
427 
428         // setHP() might call kill(), which calls remove(), which will destroy
429         // most objects
430         obj_inc_ref(this);
431         setHp(hp - amount);
432 
433 	if (isPlayerControlled()) {
434             statusFlash(getOrder(), Red);
435             if (!isDead()
436                    && engagedInTask()
437                 && taskInterruptOnDamage
438                 ) {
439                 taskPromptToAbort();
440             }
441 	} else {
442             if (!isDead() && inCombat && (getHp() < (getMaxHp() / 4))) {
443                 setFleeing(true);
444             }
445 	}
446         obj_dec_ref(this);
447 }
448 
inflictDamage(int amount,class Character * attacker)449 void Character::inflictDamage(int amount, class Character *attacker)
450 {
451 	bool mightkill = false;
452 	if (!isDead())
453 	{
454 		mightkill = true;
455 		harm_relations(attacker, this);
456 	}
457 	obj_inc_ref(this);
458 	damage(amount);
459 	if (mightkill && isDead())
460 	{
461 		attacker->addExperience(getExperienceValue());
462 	}
463 	obj_dec_ref(this);
464 }
465 
ready(class ArmsType * arms)466 enum Character::ReadyResult Character::ready(class ArmsType * arms)
467 {
468 	bool foundSlotType = false;
469 	int slot = arms->getSlotMask();
470 
471 	if (burden + arms->getWeight() > getStrength())
472 		return TooHeavy;
473 
474 	for (int i = 0; i < species->n_slots; i++) {
475 
476 		// Is the slot the right type?
477 		if ((slot & species->slots[i]) == 0)
478 			continue;
479 
480 		foundSlotType = true;
481 
482 		// Is the slot occupied?
483 		if (rdyArms[i] != NULL)
484 			continue;
485 
486 		// At this point we've found an empty slot of the correct
487 		// type. If this is a two-handed item then we also need the
488 		// next slot to be empty.
489 		if (arms->getNumHands() == 2) {
490 
491 			if (i >= species->n_slots - 1)
492 				continue;
493 
494 			// Is the slot occupied?
495 			if (rdyArms[i + 1] != NULL)
496 				continue;
497 
498 			// Is the slot the right type?
499 			if ((slot & species->slots[i + 1]) == 0)
500 				continue;
501 
502 			rdyArms[i + 1] = arms;
503 		}
504 		// Ready the item. Recalculate armour class.
505 		rdyArms[i] = arms;
506 		burden += arms->getWeight();
507 
508                 // Run the "ready" hook, passing in the arms type being readied
509                 // and the (first) changed equipment slot.
510                 runHook(OBJ_HOOK_READY_EQUIP, "pd", arms, i);
511 
512                 // Bugfix: for party members, have to change the party
513                 // inventory ref here. cmdReady() used to do this, but the
514                 // script often calls directly here for things like disarm() or
515                 // acid effects.
516                 if (isPlayerPartyMember()) {
517                         player_party->refInventoryObject(arms);
518                 }
519 
520                 // Bugfix: for others, take the item out of personal
521                 // inventory.
522                 else if (container) {
523                         container->takeOut(arms, 1);
524                 }
525 
526 		return Readied;
527 	}
528 
529 	if (foundSlotType)
530 		return NoAvailableSlot;
531 	return WrongType;
532 }
533 
unready(class ArmsType * arms)534 bool Character::unready(class ArmsType * arms)
535 {
536 	for (int i = 0; i < species->n_slots; i++) {
537 
538 		// Is it in this slot?
539 		if (rdyArms[i] != arms)
540 			continue;
541 
542 		// Is this a 2h item (in which case it should be in the next
543 		// slot, also)?
544 		if (arms->getNumHands() == 2) {
545 
546 			assert(i < species->n_slots - 1);
547 			assert(rdyArms[i + 1] == arms);
548 			rdyArms[i + 1] = NULL;
549 		}
550 		// Unready the item. Recacalculate armour class.
551 		rdyArms[i] = NULL;
552 		burden -= arms->getWeight();
553 
554                 // Run the "uunready" hook, passing in the arms type being
555                 // unreadied and the (first) changed equipment slot.
556                 runHook(OBJ_HOOK_UNREADY_EQUIP, "pd", arms, i);
557 
558                 // Bugfix: for party members, have to change the party
559                 // inventory ref here. cmdReady() used to do this, but the
560                 // script often calls directly here for things like disarm() or
561                 // acid effects.
562                 if (isPlayerPartyMember()) {
563                         player_party->unrefInventoryObject(arms);
564                 }
565 
566                 // Bugfix: for others, put the item back into personal
567                 // inventory. This fixes (e.g.) Haxima's acid effect on NPC's,
568                 // which first unreadies, and then removes, which is correct
569                 // for PC's.
570                 else if (container) {
571                         container->add(arms, 1);
572                 }
573 
574 		return true;
575 	}
576 
577 
578 	return false;
579 }
580 
getWoundDescription()581 const char *Character::getWoundDescription()
582 {
583 	static const char *desc[] = {
584 		"Critical",
585 		"Heavily wounded",
586 		"Moderately wounded",
587 		"Barely wounded",
588 		"Unscathed"
589 	};
590 
591 	if (isDead())
592 		return "Killed";
593 
594 	if (isFleeing())
595 		return "Fleeing";
596 
597 	return desc[(getHp() * 4) / getMaxHp()];
598 	return desc[(getHp() * 4) / getMaxHp()];
599 }
600 
groupExitTo(struct place * dest_place,int dest_x,int dest_y,struct closure * cutscene)601 void Character::groupExitTo(struct place *dest_place, int dest_x, int dest_y,
602                             struct closure *cutscene)
603 {
604         struct place *oldPlace = getPlace();
605 
606         player_party->removeMembers();
607 
608         // --------------------------------------------------------------------
609         // If the party is in a vehicle check if we need to disembark before
610         // exiting. If the destination is not wilderness or it's impassable
611         // wilderness then we'll disembark.
612         //
613         // When we disembark, we want to put the vehicle on the parent place
614         // of the place we're leaving.
615         // --------------------------------------------------------------------
616 
617         class Vehicle *vehicle = party->getVehicle();
618         if (vehicle
619             && (! place_is_wilderness(dest_place)
620                 || ! place_is_passable(dest_place, dest_x, dest_y,
621                                        party, 0))) {
622 
623                 assert(getPlace());
624                 assert(getPlace()->location.place);
625 
626                 vehicle->setOccupant(0);
627                 vehicle->relocate(getPlace()->location.place,
628                                          getPlace()->location.x,
629                                          getPlace()->location.y);
630                 vehicle = NULL;
631         }
632 
633 
634         if (cutscene) {
635                 mapUpdate(0);
636                 closure_exec(cutscene, "");
637         }
638 
639         // --------------------------------------------------------------------
640         // If combat is active then run its state machine after removing
641         // everybody.
642         // --------------------------------------------------------------------
643 
644         //if (combat_get_state() != COMBAT_STATE_DONE) {
645                 combat_analyze_results_of_last_turn();
646         //}
647 
648         place_exit(oldPlace);
649 
650         player_party->relocate(dest_place, dest_x, dest_y, REL_NOSTEP);
651         endTurn();
652 }
653 
move(int dx,int dy)654 enum MoveResult Character::move(int dx, int dy)
655 {
656 	int newx, newy;
657 	class Character *occupant;
658 
659         if (isStationary())
660                 return StationaryObject;
661 
662         this->dx = dx;
663         this->dy = dy;
664 
665         // ------------------------------------------------------------------
666         // Let's give this next a try, in order to make the code for teleport
667         // spells simpler. If a teleport spell says to teleport the caster,
668         // then perhaps it shouldn't have to concern itself with whether the
669         // caster is in party mode or member mode. It just tells the caster to
670         // move, and the caster then checks its context to see if this means
671         // "move the member" or "move the whole party".
672         // ------------------------------------------------------------------
673 
674         if (!isOnMap()) {
675                 if (isPlayerControlled()) {
676                         return (player_party->move(dx, dy));
677                 } else {
678                         // I don't know how I got here, but I did with an NPC
679                         // in wilderness combat, so its party was NULL. Maybe
680                         // it was summoned?
681                         dbg("%s not on map and not player-controlled...WTF?\n");
682                         if (! party)
683                                 return OffMap;
684                         return (party->move(dx, dy) ? MovedOk : WasImpassable);
685                 }
686         }
687 
688 	// Calculate new coordinates.
689 	newx = getX() + dx;
690 	newy = getY() + dy;
691 
692 
693 	// ------------------------------------------------------------------
694 	// Is the character walking off the edge of the map? The same rules as
695 	// entering a portal apply here: the party must be in follow mode, and
696         // all other members must be able to pathfind to this location.
697         //
698         // Addendum: since this is not strictly necessary for wilderness
699         // combat, and it is something of an inconvenience to the user, I skip
700         // the checks for wilderness combat.
701         // ------------------------------------------------------------------
702 
703 	if (place_off_map(getPlace(), newx, newy)) {
704 
705                 // -----------------------------------------------------------
706                 // Npc characters can just step off and will be removed from
707                 // the game.
708                 // -----------------------------------------------------------
709 
710                 // Note: the following used to be || isCharmed(), but since I
711                 // changed charm to be just a temporary faction switch I don't
712                 // think this is good any more. I don't want charmed player
713                 // party members getting destroyed.
714                 if (! isPlayerControlled()) {
715                         remove();
716                         destroy();
717                         endTurn();
718                         return ExitedMap;
719                 }
720 
721                 if (place_is_wilderness_combat(getPlace())) {
722                         remove();
723                         endTurn();
724                         return ExitedMap;
725                 }
726 
727                 if (player_party->get_num_living_members() == 1) {
728                         // Force to follow mode to avoid the annoying case
729                         // where only one member is in the party and the player
730                         // wants to leave a combat map.
731                         player_party->enableFollowMode();
732                 }
733 
734                 /* Let's try switching the order used to look for an exit
735                  * place. Instead of checking the parent first, check for a
736                  * neighbor first. */
737 
738                 // Look for a neighbor in that direction
739                 int dir = place_off_map_dir(getPlace(),newx, newy);
740                 struct place *dest_place = place_get_neighbor(getPlace(), dir);
741                 if (dest_place != NULL) {
742 
743                         int dest_x, dest_y;
744 
745                         // Check if the neighbor has an edge entrance
746                         if (place_get_edge_entrance(dest_place, dir, &dest_x,
747                                                     &dest_y)) {
748                                 return NoDestination;
749                         }
750 
751                         // For parties of size 1, force to follow mode
752                         if (player_party->getSize() == 1) {
753                                 player_party->enableFollowMode();
754                         }
755 
756                         // Ensure in follow mode
757                         if (player_party->getPartyControlMode()
758                             != PARTY_CONTROL_FOLLOW) {
759                                 return NotFollowMode;
760                         }
761 
762                         // Rendezvous other party members or abort
763                         if (!player_party->rendezvous(getPlace(), getX(),
764                                                       getY())) {
765                                 return CantRendezvous;
766                         }
767 
768                         // Goto neighbor
769                         groupExitTo(dest_place, dest_x, dest_y, NULL);
770                         endTurn();
771                         return ExitedMap;
772                 }
773 
774 
775                 if (place_get_parent(getPlace()) != NULL) {
776 
777                         if (player_party->getPartyControlMode()
778                             != PARTY_CONTROL_FOLLOW) {
779                                 return NotFollowMode;
780                         }
781 
782                         if (!player_party->rendezvous(getPlace(), getX(),
783                                                       getY())) {
784                                 return CantRendezvous;
785                         }
786 
787                         groupExitTo(place_get_parent(getPlace()),
788                                     place_get_x(getPlace()) + dx,
789                                     place_get_y(getPlace()) + dy,
790                                     NULL);
791 
792                         endTurn();
793                         return ExitedMap;
794                 }
795 
796                 return OffMap;
797 	}
798 
799         // ------------------------------------------------------------------
800         // Check passability. If commuting then ignore closed doors (and other
801         // blocking mechs).
802         // ------------------------------------------------------------------
803 
804 	if (!place_move_is_passable(
805                     getPlace(), getX(), getY(), newx, newy, this,
806                     PFLAG_MOVEATTEMPT |
807                     (getActivity() == COMMUTING ? PFLAG_IGNOREMECHS : 0))) {
808                 return WasImpassable;
809 	}
810 
811 	// Are the new coordinates already occupied by another character?
812 	if ((occupant = (class Character *) place_get_object(getPlace(),
813                                                              newx, newy,
814                                                              being_layer))) {
815 
816 
817 		// Is the occupant an enemy?
818 		if (are_hostile(this, occupant)) {
819 			return WasImpassable;
820 		}
821 
822 		// *** Switch ***
823 
824 		if (isPlayerControlled()
825 
826                     // don't allow switching with sleeping NPC's; this would
827                     // allow the player to kick THEM out of bed!
828                     && SLEEPING != occupant->getActivity()
829                     //&& occupant->isPlayerControlled()
830                     //&& isSelected()
831                         ) {
832 
833 			// Special case: if both the occupant and this
834 			// character are player-controlled then have them
835 			// switch places if possible.  This prevents the
836 			// situation where the player party is in follow mode
837 			// and the leader gets boxed in by the other members.
838 			// Note that in this case I'm ignoring movement cost
839 			// (pretend that the characters help each other across
840 			// the rough terrain... yeah, that's it!).
841 
842                         // Wait - first have to check if the other character
843                         // can occupy this tile (may have different
844                         // passability). If this fails then go ahead and
845                         // approve the move without switching. Stacking members
846                         // of the same party is permitted in order to resolve
847                         // certain corner cases which could be very unpleasant
848                         // or confusing for the user. (For example: the party
849                         // leader is a gazer, the party enters a dungeon, the
850                         // portal destination is surrounded by water, the gazer
851                         // steps onto the water... oops! Either we allow the
852                         // gazer to stack back onto the party or we require the
853                         // user to figure out how to switch the party order
854                         // just to pick a new leader just to get out of this
855                         // mess...)
856                         if (!place_is_passable(getPlace(), getX(), getY(),
857                                                occupant, 0)) {
858                                 relocate(getPlace(), newx, newy);
859                                 runHook(OBJ_HOOK_MOVE_DONE, "pdd", getPlace(),
860                                         newx, newy);
861                                 decActionPoints(
862                                         place_get_diagonal_movement_cost(
863                                                 getPlace(),
864                                                 getX(), getY(),
865                                                 newx, newy,
866                                                 this, (getActivity() == COMMUTING ? PFLAG_IGNOREMECHS : 0)));
867                                 return MovedOk;
868 
869                         }
870 
871                         // In follow mode only the leader can switch with other
872                         // party members, otherwise two or more followers can
873                         // spin-in-place.
874                         if (occupant->isPlayerControlled()
875                             && CONTROL_MODE_FOLLOW == getControlMode()) {
876                                 return WasOccupied;
877                         }
878 
879                         switchPlaces(occupant);
880 			return SwitchedOccupants;
881 		}
882 
883 		return WasOccupied;
884 	}
885 
886         runHook(OBJ_HOOK_MOVE_DONE, "pdd", getPlace(), getX(), getY());
887         decActionPoints(place_get_diagonal_movement_cost(getPlace(),
888                                                          getX(), getY(),
889                                                          newx,
890                                                          newy, this, (getActivity() == COMMUTING ? PFLAG_IGNOREMECHS : 0)));
891 	relocate(getPlace(), newx, newy);
892 
893         // If this move was visible to the player
894 	if (mapTileIsVisible(getX(), getY())) {
895                 // And this is a party member following the player
896                 if (isPlayerPartyMember()
897                     && (CONTROL_MODE_FOLLOW == getControlMode())) {
898                         // Don't update the map now, just mark it dirty. This
899                         // helps reduce the sluggishness in town maps.
900                         mapSetDirty();
901                 } else {
902                         // If this character is being moved by the player and
903                         // they are not in view of the camera
904                         if (isPlayerControlled()
905                             && ! mapIsInCameraView(getPlace(), getX(), getY())) {
906                                 // Recenter the camera to keep the character in view.
907                                 mapCenterCamera(getX(), getY());
908                         }
909                         // Repaint the map now
910                         mapUpdate(0);
911                 }
912 	}
913 
914 	return MovedOk;
915 }
916 
remove()917 void Character::remove()
918 {
919         obj_inc_ref(this);
920 	Object::remove();
921 	setAttackTarget(this);
922         mapSetDirty();
923 
924         // ------------------------------------------------------------------
925         // Handle changes to party control.
926         // ------------------------------------------------------------------
927 
928         if (isSolo()) {
929                 player_party->enableRoundRobinMode();
930         } else if (isLeader()) {
931                 player_party->enableFollowMode();
932         }
933         obj_dec_ref(this);
934 }
935 
enumerateWeapons(int * armsIndex)936 class ArmsType *Character::enumerateWeapons(int *armsIndex)
937 {
938 	*armsIndex = -1;
939 	class ArmsType *currentArms;
940 	currentArms = getNextWeapon(armsIndex);
941 	if (!currentArms)
942 		currentArms = species->weapon;
943 	return currentArms;
944 }
945 
getNextWeapon(int * armsIndex)946 class ArmsType *Character::getNextWeapon(int *armsIndex)
947 {
948 	class ArmsType *currentArms;
949 	do {
950 		currentArms = getNextArms(armsIndex);
951 	} while (currentArms != NULL
952                  && dice_average(currentArms->getDamageDice()) <= 0);
953 	return currentArms;
954 }
955 
956 /* If target is null, then it cant be in range.
957   If weapon is null, then theres no range limitation, so target is
958   considered 'in range' */
isAttackTargetInRange(class ArmsType * weapon)959 bool Character::isAttackTargetInRange(class ArmsType *weapon)
960 {
961 	int dx, dy, distance;
962 
963 	if (target == NULL)
964 		return false;
965 
966 	if (weapon == NULL)
967 		return true;
968 
969 	dx = target->getX() - getX();
970 	dy = target->getY() - getY();
971 
972 	if (dx < 0)
973 		dx = -dx;
974 	if (dy < 0)
975 		dy = -dy;
976 
977 	distance = (dx > dy) ? (dx + (dy >> 1)) : (dy + (dx >> 1));
978 
979 	return (weapon->getRange() >= distance);
980 }
981 
getAttackTarget(class ArmsType * weapon)982 class Character *Character::getAttackTarget(class ArmsType *weapon)
983 {
984 	// Is the old target still valid?
985 	if (!target || !target->isOnMap() || target->isDead() ||
986 	    !isAttackTargetInRange(weapon) || !target->isVisible()) {
987 		setAttackTarget(this);
988 		return this;
989 	}
990 
991 	return target;
992 }
993 
hasReadied(class ArmsType * arms)994 bool Character::hasReadied(class ArmsType * arms)
995 {
996 	int armsIndex = -1;
997 	class ArmsType *readied = enumerateArms(&armsIndex);
998 	while (readied != NULL && readied != arms)
999 		readied = getNextArms(&armsIndex);
1000 	return readied == arms;
1001 }
1002 
enumerateArms(int * armsIndex)1003 class ArmsType *Character::enumerateArms(int *armsIndex)
1004 {
1005 	*armsIndex = -1;
1006 	return getNextArms(armsIndex);
1007 }
1008 
getNextArms(int * armsIndex)1009 class ArmsType *Character::getNextArms(int *armsIndex)
1010 {
1011 	// Advance to the next slot
1012 	// *armsIndex++ doesnt work for some reason...
1013 	*armsIndex=*armsIndex+1;
1014 
1015 	// Search remaining slots for a weapon
1016 	for (; *armsIndex < species->n_slots; *armsIndex=*armsIndex+1) {
1017 
1018 		// Is anything in this slot?
1019 		if (rdyArms[*armsIndex] == NULL)
1020 			continue;
1021 
1022 		// Is this just another slot for the same weapon (happens in
1023 		// the case of multi-slotted weapons like 2h swords)?
1024 		if (*armsIndex > 0 && rdyArms[*armsIndex-1] == rdyArms[*armsIndex] &&
1025 		    rdyArms[*armsIndex]->getNumHands() == 2)
1026 			continue;
1027 
1028 		return rdyArms[*armsIndex];
1029 	}
1030 
1031 	return 0;
1032 }
1033 
getArmsInSlot(int slot)1034 ArmsType *Character::getArmsInSlot(int slot)
1035 {
1036         if (slot < 0
1037             || slot >= species->n_slots)
1038                 return NULL;
1039         return rdyArms[slot];
1040 }
1041 
hasAmmo(class ArmsType * weapon)1042 int Character::hasAmmo (class ArmsType * weapon)
1043 {
1044         // SAM: Changed this from returning bool to
1045         //      returning int (0 for no ammo, n for amount)
1046 	if (weapon->ammoIsUbiquitous())
1047                 return 1;  // One more available, that is.
1048 
1049 	if (isPlayerControlled()) {
1050 		struct inv_entry *ie;
1051 
1052 		if (weapon->isMissileWeapon()) {
1053 			ie = player_party->inventory->search(weapon->getAmmoType());
1054                         if (ie == NULL)
1055                                 return 0;  // No ammo
1056 			return ie->count;  // 1 or more
1057 		}
1058                 else if (weapon->isThrownWeapon()) {
1059 			ie = player_party->inventory->search(weapon);
1060 			if (ie == NULL) {
1061 				unready(weapon);
1062 				return 0;  // No more
1063 			}
1064 			assert(ie->count > 0);
1065 			return ie->count;  // 1 or more
1066 		}
1067 		return 1;  // Melee weapons are like ubiquitous
1068 	} else {
1069                 // SAM: Not bothering with quantity of NPC ammo for now
1070 		return (!weapon->isMissileWeapon() ||
1071                         (container != NULL &&
1072                          container->search(weapon->getAmmoType())));
1073 	}
1074 } // Character::hasAmmo()
1075 
hasInInventory(class ObjectType * type)1076 bool Character::hasInInventory (class ObjectType *type)
1077 {
1078 	if (isPlayerControlled()) {
1079                 return player_party->hasInInventory(type);
1080 	} else {
1081 		return (container != NULL &&
1082                         container->search(type) != NULL);
1083 	}
1084 }
1085 
setLight(int val)1086 void Character::setLight(int val)
1087 {
1088 	light = max(val, MIN_PLAYER_LIGHT);
1089         mapSetDirty();
1090 }
1091 
addMana(int delta)1092 void Character::addMana(int delta)
1093 {
1094 	mana += delta;
1095 	mana = max(mana, 0);
1096         mana = min(mana, getMaxMana());
1097 }
1098 
changeSleep(bool val)1099 void Character::changeSleep(bool val)
1100 {
1101         if (sleeping == val)
1102                 return;
1103 
1104 	sleeping = val;
1105         statusRepaint();
1106         mapSetDirty();
1107 
1108         if (sleeping) {
1109 
1110                 // -----------------------------------------------------------
1111                 // Going to sleep.
1112                 // -----------------------------------------------------------
1113             if (engagedInTask()) {
1114                 taskAbort();
1115             }
1116 
1117 
1118             if (isLeader()) {
1119                 assert(isPlayerControlled());
1120                 player_party->enableFollowMode();
1121             } else if (isSolo()) {
1122                 assert(isPlayerControlled());
1123                 player_party->enableRoundRobinMode();
1124             }
1125 
1126         } else {
1127 
1128                 if (isPlayerControlled()) {
1129 
1130                         // ----------------------------------------------------
1131                         // Upon waking up, set this character's control mode
1132                         // based on the party's control mode.
1133                         // ----------------------------------------------------
1134 
1135                         assert(! isLeader());
1136                         assert(! isSolo());
1137 
1138                         switch (player_party->getPartyControlMode()) {
1139 
1140                         case PARTY_CONTROL_FOLLOW:
1141                                 if (this != player_party->get_leader()) {
1142                                         setControlMode(CONTROL_MODE_FOLLOW);
1143                                 }
1144                                 break;
1145 
1146                         case PARTY_CONTROL_SOLO:
1147                                 setControlMode(CONTROL_MODE_IDLE);
1148                                 break;
1149 
1150                         case PARTY_CONTROL_ROUND_ROBIN:
1151                                 setControlMode(CONTROL_MODE_PLAYER);
1152                                 break;
1153 
1154                         default:
1155                                 assert(false);
1156                                 break;
1157                         }
1158 
1159                 }
1160 
1161         }
1162 
1163 }
1164 
awaken(void)1165 void Character::awaken(void)
1166 {
1167         if (isAsleep()
1168             && ! isResting()
1169             && ! isDead()) {
1170                 changeSleep(false);
1171                 log_msg("%s wakes up!", getName());
1172         }
1173 }
1174 
setFleeing(bool val)1175 void Character::setFleeing(bool val)
1176 {
1177 	if (fleeing == val)
1178 		return;
1179 
1180 	fleeing = val;
1181         fleePathFound = false;
1182         fleePathFlags = 0;
1183         cachedPath = 0;
1184 }
1185 
1186 /**
1187  * Check if it's a good idea to try and move here in the given place. Checks
1188  * for passability and known hazards.
1189  */
locationIsOk(int x2,int y2)1190 bool Character::locationIsOk(int x2, int y2)
1191 {
1192         return (place_is_passable(getPlace(), x2, y2, this, 0)
1193                 && (! place_is_occupied(getPlace(), x2, y2))
1194                 && (! place_is_hazardous(getPlace(), x2, y2)));
1195 }
1196 
1197 /**
1198  * Compute the optimal vector to move away from hostiles.
1199  *
1200  * @param dx The x component of the resulting evasion vector.
1201  * @param dy The y component of the resulting evasion vector.
1202  */
getEvasionVector(int * dx,int * dy)1203 void Character::getEvasionVector(int *dx, int *dy)
1204 {
1205         struct evasionVectorInfo info;
1206         int x2, y2;
1207 
1208         /* Get the vector away from foes. */
1209         memset(&info, 0, sizeof(info));
1210         info.subj = this;
1211         place_for_each_object(getPlace(), getEvasionVectorVisitor, &info);
1212 
1213         /* Normalize the vector. */
1214         clamp(info.dx, -1, 1);
1215         clamp(info.dy, -1, 1);
1216 
1217         /* Compute the new location. */
1218         x2 = getX() + info.dx;
1219         y2 = getY() + info.dy;
1220 
1221         /* Check if ok. */
1222         if (locationIsOk(x2, y2)) {
1223                 goto done;
1224         }
1225 
1226         /* Not ok, try the vertical. */
1227         if (info.dx) {
1228                 x2 -= info.dx;
1229                 if (locationIsOk(x2, y2)) {
1230                         goto done;
1231                 }
1232                 x2 += info.dx;
1233         }
1234 
1235         /* Not ok, try the horizontal. */
1236         if (info.dy) {
1237                 y2 -= info.dy;
1238                 if (locationIsOk(x2, y2)) {
1239                         goto done;
1240                 }
1241                 y2 += info.dx;
1242         }
1243 
1244         /* No place to go. */
1245         info.dx = 0;
1246         info.dy = 0;
1247 
1248  done:
1249         *dx = info.dx;
1250         *dy = info.dy;
1251 }
1252 
movedOk(enum MoveResult result)1253 static bool movedOk(enum MoveResult result)
1254 {
1255         switch (result) {
1256         case NotApplicable:
1257         case NoDestination:
1258         case WasImpassable:
1259         case WasOccupied:
1260                 return false;
1261         default:
1262                 return true;
1263         }
1264 }
1265 
mapHasEdge()1266 bool Character::mapHasEdge()
1267 {
1268         // This is the simple thing to do, and works for towns and wilderness
1269         // maps. Dungeon rooms could have edges shared with other rooms, but I
1270         // don't want to deal with that right now, mostly because it
1271         // complicates the pathfinding heuristics, and I want to get this case
1272         // right first.
1273         return (place_get_parent(getPlace()));
1274 }
1275 
exitMap()1276 bool Character::exitMap()
1277 {
1278         if (getX() == 0) {
1279                 return movedOk(move(-1, 0));
1280         } else if (getX() == (place_w(getPlace()) - 1)) {
1281                 return movedOk(move(1, 0));
1282         } else if (getY() == 0) {
1283                 return movedOk(move(0, -1));
1284         } else if (getY() == (place_h(getPlace()) - 1)) {
1285                 return movedOk(move(0, 1));
1286         } else {
1287                 return false;
1288         }
1289 }
1290 
followFleePath()1291 bool Character::followFleePath()
1292 {
1293         return (exitMap()
1294                 || pathfindTo(getPlace(), fleeX, fleeY, fleePathFlags));
1295 }
1296 
findFleePath()1297 bool Character::findFleePath()
1298 {
1299 	// Just look for the nearest map edge. Note that this might not be the
1300 	// best choice, because it might not have a path. Get this working
1301 	// first, then get fancy if you want.
1302 	int leftx = getX();
1303 	int rightx = place_w(getPlace()) - getX();
1304 	int boty = getY();
1305 	int topy = place_h(getPlace()) - getY();
1306 	int minx, miny;
1307 
1308 	// Is the left edge nearer than the right edge?
1309 	if (leftx < rightx) {
1310                 fleeX = 0;
1311 		minx = leftx;
1312 	} else {
1313                 fleeX = place_w(getPlace()) - 1;
1314 		minx = leftx;
1315 	}
1316 
1317 	// Is the top edge nearer than the bottom edge?
1318 	if (boty < topy) {
1319                 fleeY = 0;
1320 		miny = topy;
1321 	} else {
1322                 fleeY = place_h(getPlace()) - 1;
1323 		miny = boty;
1324 	}
1325 
1326         // Is the vertical edge nearer than the horizontal edge?
1327         if (minx < miny) {
1328                 fleePathFlags = PFLAG_HORZ;
1329         } else {
1330                 fleePathFlags = PFLAG_VERT;
1331         }
1332 
1333         fleePathFound = true;
1334         return true;
1335 }
1336 
pathfindToMapEdge()1337 bool Character::pathfindToMapEdge()
1338 {
1339         if (fleePathFound
1340             && followFleePath()) {
1341                 return true;
1342         }
1343 
1344         if (! findFleePath()) {
1345                 return false;
1346         }
1347 
1348         return followFleePath();
1349 }
1350 
fleeToMapEdge()1351 bool Character::fleeToMapEdge()
1352 {
1353         return (mapHasEdge()
1354                 && pathfindToMapEdge());
1355 }
1356 
evade()1357 bool Character::evade()
1358 {
1359         int dx = 0, dy = 0;
1360         getEvasionVector(&dx, &dy);
1361         if (!dx && !dy) {
1362                 return false;
1363         }
1364 
1365         return movedOk(move(dx, dy));
1366 }
1367 
flee()1368 bool Character::flee()
1369 {
1370         return (!isStationary()
1371                 && (fleeToMapEdge()
1372                     || evade()));
1373 }
1374 
dropRdyArms()1375 void Character::dropRdyArms()
1376 {
1377 	assert(!isPlayerControlled());
1378 
1379 	for (int i = 0; i < species->n_slots; i++) {
1380 
1381 		// Anything in this slot?
1382 		if (rdyArms[i] == NULL)
1383 			continue;
1384 
1385                 // roll to drop
1386                 if ((rand() % 100) > ITEM_DROP_PROB)
1387                         continue;
1388 
1389 		// Create an object of this type and drop it on the map
1390 		class Object *object = new Object();
1391 		if (!object)
1392 			continue;
1393 		object->init(rdyArms[i]);
1394 		object->relocate(getPlace(), getX(), getY());
1395 
1396 		// Unready it
1397 		unready(rdyArms[i]);
1398 
1399 	}
1400 }
1401 
unreadyAll()1402 void Character::unreadyAll()
1403 {
1404 	for (int i = 0; i < species->n_slots; i++) {
1405 
1406                 class ArmsType *type = rdyArms[i];
1407 
1408 		// Anything in this slot?
1409 		if (!type)
1410 			continue;
1411 
1412 		// Unready it
1413 		unready(type);
1414 	}
1415 }
1416 
dropItems()1417 bool Character::dropItems()
1418 {
1419 	assert(!isPlayerControlled());
1420 
1421 	if (container == NULL)
1422 		return false;
1423 
1424         if (container->isEmpty())
1425                 return true;
1426 
1427         if (! container->isEmpty()
1428             && (forceContainerDrop
1429                 || (rand() % 100) <= CHEST_DROP_PROB))
1430                 container->relocate(getPlace(), getX(), getY());
1431         obj_dec_ref(container);
1432 	container = NULL;
1433 
1434 	return true;
1435 }
1436 
kill()1437 void Character::kill()
1438 {
1439     // Why not turn this on again...? At least dropItems?
1440 // 	if (!isPlayerControlled() && isOnMap()) {
1441 // 		dropRdyArms();
1442 // 		dropItems();
1443 // 	}
1444 
1445         // when a PC dies unready all arms so other party members can use them
1446         if (isPlayerControlled()) {
1447                 unreadyAll();
1448         }
1449 
1450         if (isOnMap()
1451             && container
1452             && forceContainerDrop) {
1453                 container->relocate(getPlace(), getX(), getY());
1454                 obj_dec_ref(container);
1455                 container = NULL;
1456         }
1457 
1458         if (engagedInTask()) {
1459             taskAbort();
1460         }
1461 
1462         if (isPlayerControlled()) {
1463                 log_msg("%s has fallen!!", getName());
1464         }
1465 
1466 	hp = 0;
1467         setDead(true);
1468 
1469         // Similarly, run the on-death hook
1470         runHook(OBJ_HOOK_ON_DEATH, 0);
1471 	remove();
1472 }
1473 
useAmmo(class ArmsType * weapon)1474 void Character::useAmmo(class ArmsType *weapon)
1475 {
1476 	if (weapon->ammoIsUbiquitous())
1477 		return;
1478 
1479 	if (isPlayerControlled()) {
1480 		struct inv_entry *ie;
1481 
1482 		if (weapon->isMissileWeapon()) {
1483 			class ObjectType *ammoType = weapon->getAmmoType();
1484 			takeOut(ammoType, 1);
1485 		} else if (weapon->isThrownWeapon()) {
1486 
1487                         // Handle the case where multiple party members have
1488                         // all readied the same throwable weapon, and there is
1489                         // no longer enough to supply them all, by forcing the
1490                         // character that just threw to unready.
1491 			ie = player_party->inventory->search(weapon);
1492                         assert(ie);
1493                         assert(ie->ref <= ie->count);
1494                         if (ie->ref == ie->count) {
1495                                 unready(weapon);
1496                                 log_msg("%s : %s now out of ammo\n",
1497                                         getName(), weapon->getName());
1498                         }
1499 			takeOut(weapon, 1);
1500 		}
1501 	} else {
1502 		if (weapon->isMissileWeapon()) {
1503 			container->takeOut(weapon->getAmmoType(), 1);
1504 			if (!hasAmmo(weapon)) {
1505 				unready(weapon);
1506 				rearm = true;
1507 			}
1508 		} else if (weapon->isThrownWeapon()) {
1509 			if (container && container->search(weapon)) {
1510 				container->takeOut(weapon, 1);
1511 			} else {
1512 				unready(weapon);
1513 				rearm = true;
1514 			}
1515 		}
1516 	}
1517 }
1518 
1519 /*****************************************************************************/
1520 
armThyself(void)1521 void Character::armThyself(void)
1522 {
1523 	struct knapsack ks;
1524 
1525 	//assert(!isPlayerControlled());
1526 
1527 	if (container == NULL)
1528 		return;
1529 
1530 	// Setup the context to solve this using the knapsack algorithm
1531 	memset(&ks, 0, sizeof(ks));
1532 	ks.item = new void *[MAX_N_ITEMS];
1533 	ks.value = new int[MAX_N_ITEMS];
1534 	ks.solution = new unsigned char[MAX_N_ITEMS];
1535 	if (!ks.item || !ks.value || !ks.solution)
1536 		goto destroy_ks;
1537 	ks.put = wrapReady;
1538 	ks.remove = wrapUnready;
1539 	ks.context = this;
1540 
1541 	container->forEach(myConsiderArms, &ks);
1542 
1543 	knapsack_solve(&ks);
1544 
1545 	// Ready the chosen items
1546 	for (int i = 0; i < ks.n_items; i++) {
1547 		if (!ks.solution[i])
1548 			continue;
1549 		class ArmsType *arms = (class ArmsType *) ks.item[i];
1550 		if (ready(arms) != Character::Readied)
1551 			continue;
1552 	}
1553 
1554       destroy_ks:
1555 	if (ks.item)
1556 		delete [] ks.item;
1557 	if (ks.value)
1558 		delete [] ks.value;
1559 	if (ks.solution)
1560 		delete [] ks.solution;
1561 
1562 	rearm = false;
1563 }
1564 
needToRearm()1565 bool Character::needToRearm()
1566 {
1567         // gmcnutt: I currently don't have any way to tell a charmed player
1568         // party member how to rearm themselves. Player characters pull from
1569         // party inventory. Non-player characters pull from their own personal
1570         // inventory. I really need to merge both types into the same behaviour
1571         // to simplify things.
1572         if (party == (Party*)player_party)
1573                 return false;
1574 
1575 	return rearm;
1576 }
1577 
initCommon(void)1578 bool Character::initCommon(void)
1579 {
1580         if (species && species->n_slots > 0) {
1581                 rdyArms = (class ArmsType**)calloc(species->n_slots, sizeof(class ArmsType*));
1582                 assert(rdyArms);
1583         }
1584 
1585         ai = NULL;
1586 
1587 	return true;
1588 }
1589 
initStock(struct species * species,struct occ * occ,struct sprite * sprite,char * nameStr,int order)1590 bool Character::initStock(struct species * species, struct occ * occ,
1591 			  struct sprite * sprite, char *nameStr, int order)
1592 {
1593 	// This method is now only used to initialize cloned characters.
1594 
1595 	this->species = species;
1596 	this->occ = occ;
1597         if (occ)
1598                 occ_ref(occ);
1599 	this->current_sprite = sprite;
1600 
1601 	if (!initCommon())
1602 		return false;
1603 
1604         setName(nameStr);
1605 	this->order = order;
1606 
1607 	lvl = 1;		// fixme: hardcoded hack!
1608 
1609 	hp   = getMaxHp();
1610 	mana = getMaxMana();
1611 	AP_per_round = species->spd;
1612         setDead(false);
1613 
1614         defenseBonus = 0;
1615 
1616 	return true;
1617 }
1618 
1619 /*****************************************************************************/
1620 
resurrect(void)1621 void Character::resurrect(void)
1622 {
1623 	assert(isPlayerControlled());// shotgun assert put here during refactor
1624 	setHp(min(10, getMaxHp()));
1625         setDead(false);
1626 	statusFlash(getOrder(), Blue);
1627 
1628         // ------------------------------------------------------------------
1629         // If we're in wilderness mode then we're done. Otherwise we need to
1630         // put this character near the other party member's on the map.
1631         // ------------------------------------------------------------------
1632 
1633         if (player_party->isOnMap())
1634                 return;
1635 
1636         assert(player_party->get_leader());
1637 
1638         putOnMap(player_party->get_leader()->getPlace(),
1639                  player_party->get_leader()->getX(),
1640                  player_party->get_leader()->getY(), 4,
1641                  0);
1642 
1643         assert(isOnMap());
1644 
1645 }
1646 
1647 
getSprite()1648 struct sprite *Character::getSprite()
1649 {
1650 	if ((isAsleep() || isDead()) && species->sleep_sprite)
1651 		return species->sleep_sprite;
1652 	return current_sprite;
1653 }
1654 
rest(int hours)1655 void Character::rest(int hours)
1656 {
1657         int healAmount = HP_RECOVERED_PER_HOUR_OF_REST;
1658         int manaAmount = MANA_RECOVERED_PER_HOUR_OF_REST;
1659 
1660         /* Partial fix for SF BUG [ 1526910 ] "starvation is lame". Don't let
1661          * the player completely rest his way out of starvation. */
1662         if (isPlayerPartyMember()) {
1663                 if (!player_party->food) {
1664                         healAmount /= 2;
1665                         manaAmount /= 2;
1666                 }
1667         }
1668 
1669 	while (hours) {
1670                 if (!isDead()) {
1671                         heal(healAmount);
1672                         addMana(manaAmount);
1673                 }
1674 		hours--;
1675 	}
1676 }
1677 
getExperienceValue()1678 int Character::getExperienceValue()
1679 {
1680         int xpval = 0;
1681         if (species)
1682                 xpval += species->xpval;
1683         if (occ)
1684                 xpval += occ->xpval;
1685         return (xpval * lvl);
1686 }
1687 
getXpForLevel(int lvl)1688 int Character::getXpForLevel(int lvl)
1689 {
1690 	if (lvl == 1)
1691 		return 0;
1692     return (int)pow((double)2, (double)lvl+6);
1693 }
1694 
addExperience(int amount)1695 void Character::addExperience(int amount)
1696 {
1697 	xp += amount;
1698 	if (xp >= getXpForLevel(getLevel()+1)) {
1699 		lvl++;
1700                 log_banner("^c+b%s^c- gains level ^c+g%d^c-!", getName(), lvl);
1701                 if (isPlayerControlled()) {
1702                         mapFlash(1000);
1703                 }
1704                 setHp(getMaxHp());
1705                 setMana(getMaxMana());
1706 	}
1707 }
1708 
getMaxHp()1709 int Character::getMaxHp()
1710 {
1711         int base = hp_mod + species->hp_mod;
1712         int mult = hp_mult + species->hp_mult;
1713 
1714         if (occ) {
1715                 base += occ->hp_mod;
1716                 mult += occ->hp_mult;
1717         }
1718 
1719         mult = max(0, mult);
1720 
1721         return (base + getLevel() * mult);
1722 }
1723 
getMaxMana()1724 int Character::getMaxMana()
1725 {
1726         int base = mp_mod + species->mp_mod;
1727         int mult = mp_mult + species->mp_mult;
1728 
1729         if (occ) {
1730                 base += occ->mp_mod;
1731                 mult += occ->mp_mult;
1732         }
1733 
1734         mult = max(0, mult);
1735         return base + getLevel() * mult;
1736 }
1737 
changeArmourClass(int delta)1738 void Character::changeArmourClass(int delta)
1739 {
1740 	ac += delta;
1741 	ac = max(0, ac);
1742 }
1743 
clone()1744 class Object *Character::clone()
1745 {
1746 	char buf[64];
1747 	class Character *clone = new Character();
1748 	if (!clone)
1749 		return NULL;
1750 
1751         if (is_clone)
1752                 snprintf(buf, sizeof(buf), "%s", getName());
1753         else
1754                 snprintf(buf, sizeof(buf), "%s (clone)", getName());
1755 
1756 	clone->initStock(species, occ, current_sprite, buf, 0);
1757 	clone->is_clone = true;
1758 
1759         // clone the readied items
1760 		int armsIndex = 0;
1761         for (ArmsType *arms = enumerateArms(&armsIndex); arms != NULL; arms = getNextArms(&armsIndex)) {
1762                 clone->ready(arms);
1763         }
1764 
1765         // NOTE: effects not cloned... that will require a bit of work and
1766         // testing because effects can have gobs (gobs aren't cloned either)
1767 
1768 	return clone;
1769 }
1770 
isVisible()1771 bool Character::isVisible()
1772 {
1773 	return ((visible > 0) && species->visible);
1774 }
1775 
isShaded()1776 bool Character::isShaded()
1777 {
1778 	// Friendly invisible characters are shaded
1779 	return ((! isVisible() && isPlayerControlled())
1780                 || Object::isShaded());
1781 }
1782 
describe()1783 void Character::describe()
1784 {
1785     if (Session->subject) {
1786         const char *diplstr = diplomacy_string(this, Session->subject);
1787         if (isvowel(diplstr[0]))
1788             log_continue("an");
1789         else
1790             log_continue("a");
1791         log_continue(" %s", diplstr);
1792     } else {
1793         log_continue("an");
1794     }
1795     log_continue(" L%d", getLevel());
1796     if (isKnown()) {
1797         log_continue(" %s", getName());
1798     } else {
1799         if (species && species->name) {
1800             log_continue(" %s", species->name);
1801         }
1802         if (occ && occ->name) {
1803             log_continue(" %s", occ->name);
1804         }
1805     }
1806     if (!isVisible())
1807         log_continue(" (invisible)");
1808     if (isSubmerged()) {
1809         log_continue(" (submerged)");
1810     }
1811 }
1812 
examine()1813 void Character::examine()
1814 {
1815         int i;
1816         int n = 0;
1817 	const char *diplstr = diplomacy_string(this, Session->subject);
1818 
1819         log_continue("%s level %d", diplstr, getLevel());
1820 
1821         if (isKnown()) {
1822                 log_continue(" %s,", getName());
1823         } else {
1824                 if (species && species->name) {
1825                         log_continue(" %s", species->name);
1826                 }
1827                 if (occ && occ->name) {
1828                         log_continue(" %s", occ->name);
1829                 }
1830         }
1831 
1832         log_continue(" %s [", getWoundDescription());
1833 
1834         for (ArmsType *arms = enumerateArms(&i); arms;
1835              arms = getNextArms(&i)) {
1836                 if (n > 0) {
1837                         log_continue(", ");
1838                 }
1839                 log_continue("%s", arms->getName());
1840                 n++;
1841         }
1842 
1843         if (!n) {
1844                 log_continue("no arms");
1845         }
1846 
1847         log_continue("]");
1848 }
1849 
getDamageSound()1850 sound_t *Character::getDamageSound()
1851 {
1852         if (damage_sound)
1853                 return damage_sound;
1854         if (species && species->damage_sound)
1855                 return species->damage_sound;
1856         return NULL_SOUND;
1857 }
1858 
get_movement_sound()1859 sound_t *Character::get_movement_sound()
1860 {
1861         if (species)
1862                 return species->movement_sound;
1863         return NULL_SOUND;
1864 }
1865 
isType(int classID)1866 bool Character::isType(int classID) {
1867         if (classID == CHARACTER_ID)
1868                 return true;
1869         return Object::isType(classID);
1870 }
1871 
getType()1872 int Character::getType() {
1873         return CHARACTER_ID;
1874 }
1875 
getHp()1876 int Character::getHp() {
1877         return hp;
1878 }
1879 
getOrder()1880 int Character::getOrder() {
1881         return order;
1882 }
1883 
getExperience()1884 int Character::getExperience() {
1885         return xp;
1886 }
1887 
getStrength()1888 unsigned char Character::getStrength() {
1889         return (species->str + str);
1890 }
1891 
getIntelligence()1892 unsigned char Character::getIntelligence() {
1893         return (species->intl + intl);
1894 }
1895 
getDexterity()1896 unsigned char Character::getDexterity() {
1897         return (species->dex + dex);
1898 }
1899 
getBaseStrength()1900 unsigned char Character::getBaseStrength() {
1901         return (str);
1902 }
1903 
getBaseIntelligence()1904 unsigned char Character::getBaseIntelligence() {
1905         return (intl);
1906 }
1907 
getBaseDexterity()1908 unsigned char Character::getBaseDexterity() {
1909         return (dex);
1910 }
1911 
setStrength(unsigned char newstat)1912 void Character::setStrength(unsigned char newstat) {
1913         str = newstat;
1914 }
1915 
setIntelligence(unsigned char newstat)1916 void Character::setIntelligence(unsigned char newstat) {
1917        intl = newstat;
1918 }
1919 
setDexterity(unsigned char newstat)1920 void Character::setDexterity(unsigned char newstat) {
1921 	dex = newstat;
1922 }
1923 
getLevel()1924 int Character::getLevel() {
1925         return lvl;
1926 }
1927 
setLevel(int val)1928 void Character::setLevel(int val) {
1929         assert(val>0);
1930 		xp = getXpForLevel(val);
1931         lvl = val;
1932         setHp(getMaxHp());
1933         setMana(getMaxMana());
1934 }
1935 
isDead()1936 bool Character::isDead() {
1937         return dead;
1938 }
1939 
isAsleep()1940 bool Character::isAsleep() {
1941         return sleeping;
1942 }
1943 
isIncapacitated()1944 bool Character::isIncapacitated() {
1945         return (!isOnMap() || isDead() || isAsleep());
1946 }
1947 
getArmourClass()1948 int Character::getArmourClass() {
1949         return ac;
1950 }
1951 
setHp(int val)1952 void Character::setHp(int val)
1953 {
1954         hp = val;
1955         hp = clamp(hp, 0, getMaxHp());
1956         if (hp == 0) {
1957             kill();
1958         }
1959 }
1960 
isPlayerControlled()1961 bool Character::isPlayerControlled() {
1962         return playerControlled;
1963 }
1964 
setPlayerControlled(bool val)1965 void Character::setPlayerControlled(bool val)
1966 {
1967     playerControlled = val;
1968     if (val) {
1969         ctrl = ctrl_character_ui;
1970         if (isPlayerPartyMember()) {
1971             class Character *oldLeader, *newLeader;
1972             switch (player_party->getPartyControlMode()) {
1973             case PARTY_CONTROL_FOLLOW:
1974                 oldLeader = player_party->get_leader();
1975                 player_party->chooseNewLeader();
1976                 newLeader = player_party->get_leader();
1977                 if (oldLeader != newLeader) {
1978                     oldLeader->setControlMode(CONTROL_MODE_FOLLOW);
1979                 }
1980                 if (newLeader != this) {
1981                     setControlMode(CONTROL_MODE_FOLLOW);
1982                 }
1983                 break;
1984             case PARTY_CONTROL_SOLO:
1985                 setControlMode(CONTROL_MODE_IDLE);
1986                 break;
1987             case PARTY_CONTROL_ROUND_ROBIN:
1988                 setControlMode(CONTROL_MODE_PLAYER);
1989                 break;
1990             }
1991         }
1992     } else {
1993         ctrl = ctrl_character_ai;
1994         if (isPlayerPartyMember()) {
1995             if (isLeader()) {
1996                 player_party->chooseNewLeader();
1997                 assert(this != player_party->get_leader());
1998             }
1999             if (isSolo()) {
2000                 setSolo(false);
2001                 player_party->enableFollowMode();
2002             }
2003             if (engagedInTask()) {
2004                 taskAbort();
2005             }
2006         }
2007         setControlMode(CONTROL_MODE_AUTO);
2008     }
2009 }
2010 
setAttackTarget(class Character * newtarget)2011 void Character::setAttackTarget(class Character * newtarget)
2012 {
2013         if (target == newtarget)
2014                 return;
2015 
2016         if (target && target != this)
2017                 obj_dec_ref(target);
2018 
2019         target = newtarget;
2020 
2021         if (target && target != this)
2022                 obj_inc_ref(target);
2023 }
2024 
isSolo()2025 bool Character::isSolo() {
2026         return solo;
2027 }
2028 
getLight()2029 int Character::getLight() {
2030         return light;
2031 }
2032 
getVisionRadius()2033 int Character::getVisionRadius() {
2034         return species->vr;
2035 }
2036 
getSpeed()2037 int Character::getSpeed() {
2038     // Returns the character-specific number of
2039     // Action Points per round for this character.
2040 
2041     int total_AP;
2042     int AP_modifier_from_equipped_items = 0;
2043 
2044     if (AP_per_round > 0)
2045     	total_AP = AP_per_round;
2046     else
2047 	total_AP = species->spd;
2048 
2049     int armsIndex = 0;
2050     for (class ArmsType * arms = enumerateArms(&armsIndex); arms != NULL;
2051 	 arms = getNextArms(&armsIndex)) {
2052 	AP_modifier_from_equipped_items += arms->get_AP_mod();
2053     }
2054     total_AP += AP_modifier_from_equipped_items;
2055 
2056     if (total_AP < 1)
2057 	total_AP = 1; // SAM: perhaps revisit this...
2058 
2059     return total_AP;
2060 }
2061 
setSpeed(int val)2062 int Character::setSpeed(int val) {
2063     AP_per_round = val;
2064     return AP_per_round;
2065 }
2066 
getMana()2067 int Character::getMana() {
2068         return mana;
2069 }
2070 
setMana(int val)2071 void Character::setMana(int val) {
2072         mana = val;
2073 }
2074 
isFleeing()2075 bool Character::isFleeing() {
2076         return fleeing;
2077 }
2078 
setOrder(int order)2079 void Character::setOrder(int order) {
2080         this->order = order;
2081 }
2082 
setCombat(bool val)2083 void Character::setCombat(bool val) {
2084         inCombat = val;
2085 }
2086 
getDefend()2087 int Character::getDefend()
2088 {
2089         int defend = 0;
2090 
2091         if (isAsleep())
2092                 return -3; // hack: hard-coded constant
2093 
2094 		int armsIndex=0;
2095         for (class ArmsType * arms = enumerateArms(&armsIndex); arms != NULL;
2096              arms = getNextArms(&armsIndex)) {
2097                 defend += dice_roll(arms->getToDefendDice());
2098         }
2099 
2100         defend += defenseBonus;
2101 
2102         if (isSubmerged()) {
2103                 defend += kern_intvar_get("submerged_def_bonus");
2104         }
2105 
2106         return defend;
2107 }
2108 
getToHitPenalty()2109 int Character::getToHitPenalty()
2110 {
2111         int penalty = 0;
2112 
2113 		int armsIndex=0;
2114         for (class ArmsType * arms = enumerateArms(&armsIndex);
2115              arms != NULL; arms = getNextArms(&armsIndex)) {
2116                 int roll = dice_roll(arms->getToHitDice());
2117                 if (roll < 0)
2118                         penalty += roll;
2119         }
2120 
2121         return penalty;
2122 }
2123 
getBaseAttackBonus(class ArmsType * weapon)2124 int Character::getBaseAttackBonus(class ArmsType * weapon)
2125 {
2126 	int strbonus = weapon->modifyStrAttack(session_run_query(Session, str_based_attack_query, "p", this));
2127 	int dexbonus = weapon->modifyDexAttack(session_run_query(Session, dex_based_attack_query, "p", this));
2128 	int totalbonus = (strbonus + dexbonus) / (100 * 1000);
2129 	return (1+ totalbonus);
2130 }
2131 
getAttackBonus(class ArmsType * weapon)2132 int Character::getAttackBonus(class ArmsType * weapon)
2133 {
2134 	return (rand() % getBaseAttackBonus(weapon));
2135 }
2136 
getDamageBonus(class ArmsType * weapon)2137 int Character::getDamageBonus(class ArmsType * weapon)
2138 {
2139 	int dambonus = weapon->modifyDamageBonus(session_run_query(Session, damage_bonus_query, "p", this))/(100 * 1000);
2140 	return (rand() % (1+ dambonus));
2141 }
2142 
getAvoidBonus()2143 int Character::getAvoidBonus()
2144 {
2145 	//hack: dont get any bonus here if you're asleep on the job
2146 	if (isAsleep())
2147 		return 0;
2148 
2149 	int avoidBonus = session_run_query(Session, defense_bonus_query, "p", this);
2150 	float avoidMod = 1;
2151 
2152 	//roundabout way of getting data in order to preserve info for stderr
2153 	int armsIndex=0;
2154 	for (class ArmsType * arms = enumerateArms(&armsIndex);
2155 		 arms != NULL; arms = getNextArms(&armsIndex))
2156 	{
2157 		avoidMod = arms->modifyAvoidBonus(avoidMod);
2158 	}
2159 
2160 	int totalbonus = (int)(avoidBonus * (avoidMod/1000));
2161 	return (rand() % (1+ totalbonus));
2162 }
2163 
getArmor()2164 int Character::getArmor()
2165 {
2166         int armor = 0;
2167 
2168 		int armsIndex=0;
2169         for (class ArmsType * arms = enumerateArms(&armsIndex);
2170              arms != NULL; arms = getNextArms(&armsIndex)) {
2171                 armor += dice_roll(arms->getArmorDice());
2172         }
2173 
2174         // the obsolescent 'armor class' is still used by the 'protect' spell
2175         // effect
2176         armor += ac;
2177 
2178         // Some species have an armor bonus
2179         if (species->armor_dice) {
2180                 armor += dice_roll(species->armor_dice);
2181         }
2182 
2183         return armor;
2184 
2185 }
2186 
getParty()2187 class Party *Character::getParty()
2188 {
2189         return party;
2190 }
2191 
burn()2192 void Character::burn()
2193 {
2194         damage(DAMAGE_FIRE);
2195         log_msg("%s burning-%s!", getName(), getWoundDescription());
2196 }
2197 
sleep()2198 void Character::sleep()
2199 {
2200         if (isAsleep())
2201                 return;
2202 
2203         changeSleep(true);
2204         log_msg("%s sleeping!", getName());
2205 }
2206 
canSee(class Object * obj)2207 bool Character::canSee(class Object *obj)
2208 {
2209         return (obj->getPlace() == getPlace() &&
2210                 place_flying_distance(getPlace(), getX(), getY(),
2211                                       obj->getX(), obj->getY()) <=
2212                 getVisionRadius() &&
2213                 obj->isVisible() &&
2214                 place_in_los(getPlace(), getX(), getY(),
2215                              obj->getPlace(), obj->getX(), obj->getY()));
2216 }
2217 
atAppointment()2218 bool Character::atAppointment()
2219 {
2220         struct appt *curAppt = &sched->appts[appt];
2221 
2222         if (getX() >= curAppt->x
2223             && getX() < (curAppt->x + curAppt->w)
2224             && getY() >= curAppt->y
2225             && getY() < (curAppt->y + curAppt->h)) {
2226                 return true;
2227         }
2228         return false;
2229 }
2230 
playerIsInMyBed()2231 bool Character::playerIsInMyBed()
2232 {
2233 	struct appt *curAppt = &sched->appts[appt];
2234 
2235 	if (SLEEPING!=curAppt->act)
2236 	return false;
2237 
2238 	class Character *sleeper =
2239 		(class Character*)place_get_object(getPlace(),
2240 			curAppt->x,
2241 			curAppt->y,
2242 			being_layer);
2243 
2244 	return (sleeper
2245 		&& sleeper->isPlayerControlled()
2246 		&& sleeper->isResting());
2247 }
2248 
2249 //return true if character is next to or on the appointment loc
nextToAppointment()2250 bool Character::nextToAppointment()
2251 {
2252    struct appt *curAppt = &sched->appts[appt];
2253 
2254 	if (abs(curAppt->x - getX())>1)
2255 	{
2256 		return false;
2257 	}
2258 	if (abs(curAppt->y - getY())>1)
2259 	{
2260 		return false;
2261 	}
2262 
2263 	return true;
2264 }
2265 
kickPlayerOutOfMyBed()2266 void Character::kickPlayerOutOfMyBed()
2267 {
2268         struct appt *curAppt = &sched->appts[appt];
2269 
2270         log_msg("Kicked out of bed!");
2271         player_party->throw_out_of_bed();
2272 
2273         // now switch places with whoever is in bed
2274        class Character *sleeper =
2275                (class Character*)place_get_object(getPlace(),
2276                                                   curAppt->x,
2277                                                   curAppt->y,
2278                                                   being_layer);
2279        assert(sleeper);
2280        switchPlaces(sleeper);
2281 
2282        assert(atAppointment());
2283        setActivity(curAppt->act);
2284 }
2285 
switchPlaces(class Being * occupant)2286 void Character::switchPlaces(class Being *occupant)
2287 {
2288         int oldx = getX();
2289         int oldy = getY();
2290         int newx = occupant->getX();
2291         int newy = occupant->getY();
2292 
2293         // Save these before calling remove because remove()
2294         // automatically resets these to defaults (for sane
2295         // reasons... I think)
2296         class Character *oldTarget = target;
2297         struct place *oldPlace = getPlace();
2298         bool wasSolo = isSolo();
2299 
2300         //remove();
2301         occupant->relocate(oldPlace, oldx, oldy);
2302         relocate(oldPlace, newx, newy);
2303         decActionPoints(place_get_diagonal_movement_cost(getPlace(),
2304                                                          oldx, oldy,
2305                                                          newx, newy,
2306                                                          this, 0));
2307         setAttackTarget(oldTarget);
2308         setSolo(wasSolo);
2309 }
2310 
commute()2311 bool Character::commute()
2312 {
2313 	int tx, ty;
2314 
2315         // Note: this could be improved a bit by caching the results of the
2316         // rectangle search below. Since the path is cached I'm not sure how
2317         // big of a gain it would be.
2318 
2319         struct appt *curAppt = &sched->appts[appt];
2320 
2321         // Check if the commute is over
2322         if (atAppointment()) {
2323                 setActivity(curAppt->act);
2324                 return true;
2325         }
2326 
2327         // Else search for an open place in the appointment rectangle
2328         for (ty = curAppt->y; ty < curAppt->y + curAppt->h; ty++) {
2329                 for (tx = curAppt->x; tx < curAppt->x + curAppt->w;  tx++) {
2330 
2331                         if (!place_is_passable(getPlace(), tx, ty, this,
2332                                                PFLAG_IGNOREMECHS) ||
2333                             place_is_hazardous(getPlace(), tx, ty))
2334                                 continue;
2335 
2336                         // Try to go there. Adjacent-to-there is not good
2337                         // enough; we will get stuck repeatedly trying to
2338                         // pathfind if the last step is impassable (Bug
2339                         // 1734069).
2340                         if (!pathfindTo(getPlace(), tx, ty,
2341                                         PFLAG_ADJACENTNOTOK|PFLAG_IGNOREMECHS)) {
2342                                 continue;
2343                         }
2344 
2345                         // Check if the commute is over.
2346                         if (atAppointment()) {
2347                                 setActivity(curAppt->act);
2348                         }
2349 
2350                         return true;
2351                 }
2352         }
2353 
2354         // Special case: if the appointment is the character's bed, and
2355         // pathfinding failed because the player is sleeping in it, then kick
2356         // the player out of bed
2357 			if (playerIsInMyBed())
2358 			{
2359 				if (nextToAppointment())
2360 				{
2361 					//evict the player if we are close enough
2362 					kickPlayerOutOfMyBed();
2363 				}
2364 				else
2365 				{
2366 					//try to reach bed, clambering over other beings as necessary
2367 					pathfindTo(getPlace(), curAppt->x, curAppt->y, PFLAG_IGNOREBEINGS | PFLAG_IGNOREMECHS);
2368 				}
2369 				return true;
2370 			}
2371 
2372         dbg("%s cannot find path to [%d %d %d %d] while commuting\n",
2373                getName(),
2374                sched->appts[appt].x,
2375                sched->appts[appt].y,
2376                sched->appts[appt].w,
2377                sched->appts[appt].h);
2378 
2379         return false;
2380 }
2381 
synchronize()2382 void Character::synchronize()
2383 {
2384         struct appt *cur_appt = 0;
2385 
2386 	if (!sched || sched->n_appts == 0)
2387 		return;
2388 
2389 	if (isDead())
2390 		return;
2391 
2392         cur_appt = sched_get_appointment(sched, Session->clock.hour,
2393                                          Session->clock.min);
2394 
2395         if (getPlace()
2396             && getPlace() != cur_appt->place) {
2397                 introduce();
2398                 return;
2399         }
2400 
2401         /* Iff the character is not already in their appointment rectangle then
2402          * relocate them to the upper left corner of their appointment. The ULC
2403          * is better than the center because it's more obvious to the schedule
2404          * designer that the ULC needs to be passable terrain. If they are
2405          * already in their appointment then don't relocate them, because it
2406          * looks funny when you reload the game.
2407          */
2408         if ((getX() < cur_appt->x)
2409             || (getX() >= (cur_appt->x + cur_appt->w))
2410             || (getY() < cur_appt->y)
2411             || (getY() >= (cur_appt->y + cur_appt->h))) {
2412                 relocate(cur_appt->place, cur_appt->x, cur_appt->y);
2413         }
2414 	setActivity(cur_appt->act);
2415         appt = cur_appt->index;
2416 }
2417 
introduce()2418 void Character::introduce()
2419 {
2420         assert(sched);
2421 
2422 		if (isDead())
2423 			return;
2424 
2425         struct appt *newAppt = sched_get_appointment(sched,
2426                                                      Session->clock.hour,
2427                                                      Session->clock.min);
2428         if (getPlace() != newAppt->place) {
2429 
2430                 // Since this code just "teleports" the character, make sure
2431                 // the player isn't looking.
2432 
2433                 // Introduce a character into the place as part of its
2434                 // schedule. For now, just drop it on its location as in
2435                 // synchronize(). DO NOT try to use object::relocate(). It's
2436                 // seriously overextended and has a bunch of logic that will
2437                 // prevent NPC's from relocating.
2438                 obj_inc_ref(this);
2439                 if (getPlace()) {
2440                         place_remove_object(getPlace(), this);
2441                 }
2442                 setPlace(newAppt->place);
2443                 setX(newAppt->x);
2444                 setY(newAppt->y);
2445                 place_add_object(newAppt->place, this);
2446                 obj_dec_ref(this);
2447                 setActivity(newAppt->act);
2448                 appt = newAppt->index;
2449         }
2450 }
2451 
getAppointment()2452 void Character::getAppointment()
2453 {
2454 
2455         int nextAppt = appt + 1;
2456 
2457         /* Special case: the last appointment of the day is over when the clock
2458            rolls over at midnight. We can detect clock rollover by checking if
2459            the current time is BEFORE the start of the current appt. */
2460         if (nextAppt == sched->n_appts) {
2461                 if (Session->clock.hour < sched->appts[appt].hr) {
2462                         appt = 0;
2463                 }
2464         }
2465 
2466         /* Normal case: check if the clock time exceeds the start time of our
2467            next appt. */
2468         else if (Session->clock.hour >= sched->appts[nextAppt].hr &&
2469                  Session->clock.min >= sched->appts[nextAppt].min) {
2470                 appt = nextAppt;
2471         }
2472 
2473         if (atAppointment()) {
2474                 setActivity(sched->appts[appt].act);
2475         } else {
2476                 setActivity(COMMUTING);
2477         }
2478 
2479 }
2480 
exec()2481 void Character::exec()
2482 {
2483         int points_last_loop;
2484         class Character *leader;
2485         bool noHostiles = false;
2486         bool appointmentChecked = false;
2487 
2488         //printf("exec %s\n", getName());
2489 
2490         startTurn();
2491 
2492         if (isDead() || ! isOnMap() ||action_points <= 0) {
2493                 endTurn();
2494                 return;
2495         }
2496 
2497         if (isResting()) {
2498 
2499                 // -----------------------------------------------------------
2500                 // Every hour until the wakeup alarm goes off have the
2501                 // character rest a little.
2502                 //
2503                 // The first character to wakeup to the alarm clock will wake
2504                 // up the party.
2505                 // -----------------------------------------------------------
2506 
2507                 assert(isAsleep());
2508 
2509                 if (clock_alarm_is_expired(&rest_alarm)) {
2510                         rest(1);
2511                         clock_alarm_set(&rest_alarm, 60);
2512                 }
2513 
2514                 if (clock_alarm_is_expired(&wakeup_alarm)) {
2515 
2516                         if (! isPlayerPartyMember()) {
2517                                 endResting();
2518                         } else {
2519                                 log_begin_group();
2520                                 log_msg("Done resting...");
2521                                 endResting();
2522 
2523                                 if (player_party->isCamping())
2524                                         player_party->endCamping();
2525                                 else if (player_party->isResting())
2526                                         player_party->endResting();
2527 
2528                                 log_end_group();
2529                         }
2530                 }
2531 
2532                 endTurn();
2533                 return;
2534         }
2535 
2536         else if (isGuarding()) {
2537 
2538                 // -----------------------------------------------------------
2539                 // Every hour have the guard repair the vehicle by some amount.
2540                 //
2541                 // When guarding is over the guard will wake up the party.
2542                 // -----------------------------------------------------------
2543 
2544                 class Vehicle *vehicle = player_party->getVehicle();
2545                 if (clock_alarm_is_expired(&rest_alarm)) {
2546                         if (isPlayerControlled() &&
2547                             vehicle &&
2548                             vehicle->getHp() <
2549                             vehicle->getMaxHp()) {
2550                                 vehicle->heal(
2551                                         vehicle->getMaxHp() /
2552                                         10);
2553                                 foogodRepaint();
2554                                 log_begin("%s repairs ", getName());
2555                                 vehicle->describe();
2556                                 log_end(".");
2557                         }
2558                         clock_alarm_set(&rest_alarm, 60);
2559                 }
2560 
2561                 if (clock_alarm_is_expired(&wakeup_alarm)) {
2562                         endGuarding();
2563 
2564                         if (isPlayerPartyMember()) {
2565                                 if (player_party->isCamping())
2566                                         player_party->endCamping();
2567                                 else if (player_party->isResting())
2568                                         player_party->endResting();
2569                         }
2570                 }
2571                 endTurn();
2572                 return;
2573         }
2574 
2575         else if (isLoitering()) {
2576                 if (clock_alarm_is_expired(&wakeup_alarm)) {
2577                         if (! isPlayerPartyMember()) {
2578                                 endLoitering();
2579                         } else {
2580                                 log_begin_group();
2581                                 log_msg("Done loitering...");
2582                                 endLoitering();
2583 
2584                                 if (player_party->isLoitering())
2585                                         player_party->endLoitering();
2586                                 log_end_group();
2587                         }
2588                 }
2589                 endTurn();
2590                 return;
2591         }
2592 
2593         // ------------------------------------------------------------------
2594         // Check for cases that prevent the character from taking a turn. Note
2595         // that if the character is sleeping he will still take a turn iff the
2596         // sleep is part of his schedule.
2597         // ------------------------------------------------------------------
2598 
2599         if (isAsleep() && getActivity() != SLEEPING) {
2600 
2601                 if (ambushedWhileCamping &&
2602                     ((rand() % 100) < PROB_AWAKEN)) {
2603                         endCamping();
2604                 } else {
2605                         endTurn();
2606                         return;
2607                 }
2608         }
2609 
2610         switch (getControlMode()) {
2611 
2612         case CONTROL_MODE_AUTO:
2613 
2614                 // Loop until the turn is over or the character stops using
2615                 // action points.
2616                 points_last_loop = 0;
2617                 while (! isTurnEnded() &&
2618                        getActionPoints() != points_last_loop) {
2619                         points_last_loop = action_points;
2620 
2621                         // Lookup this character's schedule. We only need to do
2622                         // it once per turn; the clock won't change in the
2623                         // loop. But we want to do it inside the loop so that
2624                         // if there's no AP the schedule won't change the
2625                         // character's state. This was specifically added so
2626                         // that An Zu could wake up sleeping NPC's for a few
2627                         // rounds by waking them up and socking them with an AP
2628                         // debt to keep them docile for a bit.
2629                         if (sched && ! appointmentChecked) {
2630                                 getAppointment();
2631                                 appointmentChecked = true;
2632                         }
2633 
2634                         switch (getActivity()) {
2635                         case COMMUTING:
2636                                 // pathfind to next appointment
2637                                 commute();
2638                                 break;
2639                         case EATING:
2640                         case SLEEPING:
2641                                 // do nothing
2642                                 break;
2643                         case FIGHTING:
2644                         default:
2645                                 // call the AI
2646                                 ctrl(this);
2647                                 break;
2648                         }
2649                 }
2650                 break;
2651 
2652         case CONTROL_MODE_PLAYER:
2653 
2654                 /* Highlight the character & prompt the user */
2655                 select(true);
2656 
2657                 // Update name in foogod window
2658                 if (PARTY_CONTROL_ROUND_ROBIN == player_party->getPartyControlMode()) {
2659                     foogod_set_title("Round Robin: %s", getName());
2660                     foogodRepaint();
2661                 }
2662 
2663                 // If the character is out-of-site then change the camera to
2664                 // focus on the character.
2665                 if (! mapIsInCameraView(getPlace(), getX(), getY())) {
2666                         mapCenterCamera(getX(), getY());
2667                         mapUpdate(0);
2668                 }
2669 
2670                 /* Hand control over to the player */
2671                 ctrl(this);
2672 
2673                 /* Increment the turn count once per combat round. */
2674                 if (isSolo() || isLeader()) {
2675                         session_inc_turn_count();
2676                         foogodRepaint();
2677                 }
2678 
2679                 if (Session->reloaded)
2680                         /* Hack: this object has been destroyed. Leave
2681                          * now. Don't touch a thing. */
2682                         return;
2683 
2684                 /* Un-highlight the character */
2685                 select(false);
2686 
2687                 break;
2688 
2689 
2690         case CONTROL_MODE_FOLLOW:
2691 
2692                 // -----------------------------------------------------------
2693                 // Follow the party leader.
2694                 // -----------------------------------------------------------
2695 
2696                 leader = player_party->get_leader();
2697 
2698                 assert(leader);
2699                 assert(this != leader);
2700 
2701                 // -----------------------------------------------------------
2702                 // Loop until the leader is one tile away, we run out of action
2703                 // points, or we stop using action points (this last occurs
2704                 // when we can't find a path)
2705                 // -----------------------------------------------------------
2706 
2707                 points_last_loop = 0;
2708 
2709                 // Since this character is in follow mode it's keystroke hook
2710                 // hasn't been run yet. Do it now so that effects like
2711                 // paralysis work properly on PC's in follow mode.
2712                 runHook(OBJ_HOOK_KEYSTROKE, 0);
2713                 if (isTurnEnded()) {
2714                         break;
2715                 }
2716 
2717                 noHostiles = ! place_contains_hostiles(getPlace(), this);
2718 
2719                 while (1 < place_flying_distance(Place, getX(), getY(),
2720                                                  leader->getX(),
2721                                                  leader->getY())
2722                        && (noHostiles || ! isTurnEnded())
2723                        && getActionPoints() != points_last_loop
2724                         ) {
2725 
2726                         points_last_loop = getActionPoints();
2727 
2728                         // ----------------------------------------------------
2729                         // Take a step toward the leader, recompute
2730                         // line-of-sight and repaint to show the action.
2731                         // ----------------------------------------------------
2732 
2733                         pathfindTo(leader->getPlace(),
2734                                    leader->getX(),
2735                                    leader->getY(),
2736                                    PFLAG_IGNOREMECHS
2737                                    |PFLAG_IGNORECOMPANIONS
2738                                    |PFLAG_IGNORESTEPTRIG
2739                                 );
2740                         mapCenterView(getView(), getX(), getY());
2741                         mapSetDirty();
2742                 }
2743 
2744                 // In follow mode don't accumulate action point depth. This
2745                 // leads to annoying laggardliness in player party members.
2746                 setActionPoints(0);
2747                 break;
2748 
2749         case CONTROL_MODE_IDLE:
2750                 break;
2751 
2752         case CONTROL_MODE_TASK:
2753             assert(taskproc);
2754             if (isPlayerControlled()) {
2755                 log_msg("%s continues %s...", getName(), getTaskName());
2756             }
2757             closure_exec(taskproc, "pl", this, taskgob->p);
2758             break;
2759 
2760         default:
2761                 assert(false);
2762                 break;
2763         }
2764 
2765         endTurn();
2766 }
2767 
setSolo(bool val)2768 void Character::setSolo(bool val)
2769 {
2770         if (solo == val)
2771                 return;
2772 
2773         if (val) {
2774             assert(isPlayerControlled());
2775             if (engagedInTask()) {
2776                 taskAbort();
2777             }
2778             solo = val;
2779             attachCamera(true);
2780             setControlMode(CONTROL_MODE_PLAYER);
2781             log_msg("%s goes solo.", getName());
2782             mapCenterCamera(getX(), getY());
2783             mapSetDirty();
2784         } else {
2785             solo = val;
2786             attachCamera(false);
2787             setControlMode(CONTROL_MODE_IDLE);
2788         }
2789 }
2790 
unCharm()2791 void Character::unCharm()
2792 {
2793         // Check for illegal request
2794         if (0 == factionSwitch) {
2795                 warn("%s:uncharm:factionSwitch 0", getName());
2796                 return;
2797         }
2798 
2799         // Decrement the faction switch.
2800         factionSwitch--;
2801 
2802         // If the faction switch is still on then there are other charms in
2803         // effect and we don't want to disturb them. NOTE: the last faction
2804         // used with charm will remain in effect until the factionSwitch falls
2805         // to zero. This will match most expected behavior related to multiple
2806         // charm effects.
2807         if (factionSwitch)
2808                 return;
2809 
2810         // Is this an NPC or a party member?
2811         if (! isPlayerPartyMember()) {
2812 
2813                 // Revert the NPC to AI-control
2814                 setControlMode(CONTROL_MODE_AUTO);
2815 
2816                 // Remove the NPC's map view, if any
2817                 if (NULL != getView()) {
2818                         rmView();
2819                         mapDestroyView(getView());
2820                         setView(NULL);
2821                 }
2822 
2823                 // Switch the controller back to the AI
2824                 //ctrl = ctrl_character_ai;
2825 
2826         } else {
2827 
2828                 // Set the party member's control mode based on the party's
2829                 // current control mode.
2830                 switch (player_party->getPartyControlMode()) {
2831                 case PARTY_CONTROL_ROUND_ROBIN:
2832                         setControlMode(CONTROL_MODE_PLAYER);
2833                         break;
2834                 case PARTY_CONTROL_SOLO:
2835                         setControlMode(CONTROL_MODE_IDLE);
2836                         break;
2837                 case PARTY_CONTROL_FOLLOW:
2838                         if (isLeader()) {
2839                                 setControlMode(CONTROL_MODE_PLAYER);
2840                         } else {
2841                                 setControlMode(CONTROL_MODE_FOLLOW);
2842                         }
2843                         break;
2844                 }
2845         }
2846 }
2847 
charm(int newFaction)2848 void Character::charm(int newFaction)
2849 {
2850 
2851         if (isDead())
2852                 return;
2853 
2854         // Set the temporary faction (possibly clobbering the previous one).
2855         tmpFaction = newFaction;
2856 
2857         if (isPlayerPartyMember())
2858                 // Switch the party member to be AI-controlled.
2859                 setControlMode(CONTROL_MODE_AUTO);
2860         else {
2861                 // Add a map view for the non-party member
2862                 setView(mapCreateView());
2863                 addView();
2864 
2865                 // Switch the non-party member to be player-controlled
2866                 setControlMode(CONTROL_MODE_PLAYER);
2867 
2868                 // Switch the controller over to the player
2869                 // NOTE: this needs a bit more work to be nicely done
2870                 // ctrl = ctrl_character_ui;
2871         }
2872 
2873         // Increment the faction switch (must do this AFTER calling
2874         // setControlMode())
2875         factionSwitch++;
2876 
2877 }
2878 
isCharmed()2879 bool Character::isCharmed()
2880 {
2881         return getCurrentFaction() != getBaseFaction();
2882 }
2883 
isPlayerPartyMember()2884 bool Character::isPlayerPartyMember()
2885 {
2886         // If player party has not been created yet it's safe to assume that
2887         // for now this is not a party member.
2888         if (!Session || ! player_party) {
2889                 return false;
2890         }
2891         return (class Object*)party == (class Object*)player_party;
2892 }
2893 
setControlMode(enum control_mode mode)2894 void Character::setControlMode(enum control_mode mode)
2895 {
2896         // -------------------------------------------------------------------
2897         // Player party calls in here to switch between follow, round-robin and
2898         // solo modes. Don't want to change the control mode of charmed
2899         // member's in this case. Always need to uncharm before switching
2900         // control modes.
2901         // -------------------------------------------------------------------
2902 
2903         if (isCharmed())
2904                 return;
2905 
2906         control_mode = mode;
2907 
2908         switch (mode) {
2909         case CONTROL_MODE_AUTO:
2910                 ctrl = ctrl_character_ai;
2911                 break;
2912         case CONTROL_MODE_PLAYER:
2913         case CONTROL_MODE_IDLE:
2914         case CONTROL_MODE_FOLLOW:
2915                 ctrl = ctrl_character_ui;
2916                 break;
2917         case CONTROL_MODE_TASK:
2918             break;
2919         }
2920 }
2921 
2922 
add(ObjectType * type,int amount)2923 bool Character::add(ObjectType *type, int amount)
2924 {
2925         if (isPlayerPartyMember()) {
2926                 return player_party->add(type, amount);
2927         } else if (container) {
2928                 container->add(type, amount);
2929                 return true;
2930         }
2931 
2932         return false;
2933 }
2934 
2935 
takeOut(ObjectType * type,int amount)2936 bool Character::takeOut(ObjectType *type, int amount)
2937 {
2938         if (isPlayerPartyMember()) {
2939                 return player_party->takeOut(type, amount);
2940         } else if(container) {
2941                 return container->takeOut(type, amount);
2942         }
2943         return false;
2944 
2945 }
addFood(int quantity)2946 bool Character::addFood(int quantity)
2947 {
2948         if (isPlayerPartyMember()) {
2949                 player_party->addFood(quantity);
2950                 return true;
2951         }
2952         return false;
2953 }
2954 
addGold(int quantity)2955 bool Character::addGold(int quantity)
2956 {
2957         if (isPlayerPartyMember()) {
2958                 player_party->addGold(quantity);
2959                 return true;
2960         }
2961         return false;
2962 }
2963 
beginResting(int hours)2964 void Character::beginResting(int hours)
2965 {
2966         assert(hours > 0);
2967 
2968         if (isDead())
2969                 return;
2970 
2971         clock_alarm_set(&wakeup_alarm, hours * 60);
2972         clock_alarm_set(&rest_alarm, 60);
2973         resting = true;
2974         changeSleep(true);
2975 }
2976 
endResting()2977 void Character::endResting()
2978 {
2979         resting = false;
2980         setControlMode(CONTROL_MODE_PLAYER);
2981         awaken();
2982 }
2983 
isResting()2984 bool Character::isResting()
2985 {
2986         return resting;
2987 }
2988 
beginLoitering(int hours)2989 void Character::beginLoitering(int hours)
2990 {
2991         assert(hours > 0);
2992 
2993         if (isDead())
2994                 return;
2995 
2996         clock_alarm_set(&wakeup_alarm, hours * 60);
2997         loitering = true;
2998 }
2999 
endLoitering()3000 void Character::endLoitering()
3001 {
3002         loitering = false;
3003         if (! engagedInTask()) {
3004             setControlMode(CONTROL_MODE_PLAYER);
3005         }
3006 }
3007 
isLoitering()3008 bool Character::isLoitering()
3009 {
3010         return loitering;
3011 }
3012 
beginCamping(int hours)3013 void Character::beginCamping(int hours)
3014 {
3015         ambushedWhileCamping = false;
3016         beginResting(hours);
3017 }
3018 
endCamping()3019 void Character::endCamping()
3020 {
3021         ambushedWhileCamping = false;
3022         endResting();
3023 }
3024 
isCamping()3025 bool Character::isCamping()
3026 {
3027         return isResting();
3028 }
3029 
ambushWhileCamping()3030 void Character::ambushWhileCamping()
3031 {
3032         resting = false;
3033         ambushedWhileCamping = true;
3034 
3035         if (!isAsleep())
3036                 return;
3037 
3038         if ((rand() % 100) < PROB_AWAKEN) {
3039                 endCamping();
3040         }
3041 
3042 }
3043 
beginGuarding(int hours)3044 void Character::beginGuarding(int hours)
3045 {
3046         assert(hours > 0);
3047         assert(!isAsleep());
3048         assert(!isDead());
3049 
3050         clock_alarm_set(&wakeup_alarm, hours * 60);
3051         clock_alarm_set(&rest_alarm, 60);
3052         setControlMode(CONTROL_MODE_IDLE);
3053         guarding  = true;
3054 }
3055 
endGuarding()3056 void Character::endGuarding()
3057 {
3058         guarding = false;
3059         setControlMode(CONTROL_MODE_PLAYER);
3060 }
3061 
isGuarding()3062 bool Character::isGuarding()
3063 {
3064         return guarding;
3065 }
3066 
heal(int amount)3067 void Character::heal(int amount)
3068 {
3069         amount = min(amount, getMaxHp() - hp);
3070         hp += amount;
3071         if (isPlayerControlled() && amount)
3072                 statusFlash(getOrder(), Blue);
3073         /* fixme: if sufficiently healed should stop fleeing */
3074 }
3075 
setLeader(bool val)3076 void Character::setLeader(bool val)
3077 {
3078         if (is_leader == val)
3079                 return;
3080 
3081         if (is_leader) {
3082                 attachCamera(false);
3083                 if (isPlayerControlled()) {
3084                     setControlMode(CONTROL_MODE_IDLE);
3085                 }
3086         } else {
3087                 attachCamera(true);
3088                 setControlMode(CONTROL_MODE_PLAYER);
3089                 //consolePrint("%s is now party leader.\n", getName());
3090                 mapSetPlace(getPlace());
3091                 mapCenterCamera(getX(), getY());
3092                 mapSetDirty();
3093         }
3094 
3095         is_leader = val;
3096 }
3097 
canBeLeader()3098 bool Character::canBeLeader()
3099 {
3100         return (! isDead() && isOnMap() && ! isAsleep() && ! isCharmed() && isPlayerControlled()
3101                 && (getActionPoints() > -(2*getActionPointsPerTurn()))
3102                 && ! engagedInTask()
3103             );
3104 }
3105 
isLeader()3106 bool Character::isLeader()
3107 {
3108         return is_leader;
3109 }
3110 
isCompanionOf(class Object * other)3111 bool Character::isCompanionOf(class Object *other)
3112 {
3113         // ------------------------------------------------------------------
3114         // Do the simple thing for now. This is only used in
3115         // the context of player party follow mode.
3116         // ------------------------------------------------------------------
3117 
3118         return isPlayerPartyMember() && other->isPlayerPartyMember();
3119 }
3120 
joinPlayer(void)3121 bool Character::joinPlayer(void)
3122 {
3123         class Party *old_party = party;
3124 
3125         if (old_party == player_party)
3126                 return false;
3127 
3128         if (NULL != old_party) {
3129                 old_party->removeMember(this);
3130         }
3131 
3132         if (player_party->addMember(this)) {
3133                 addView();
3134                 // Turn off any prior AI.
3135                 setAI(NULL);
3136                 return true;
3137         }
3138 
3139         if (NULL != old_party) {
3140                 old_party->addMember(this);
3141         }
3142 
3143         setSchedule(NULL);
3144 
3145         return false;
3146 }
3147 
leavePlayer(void)3148 void Character::leavePlayer(void)
3149 {
3150         if (!isPlayerPartyMember())
3151                 return;
3152         if (isSolo()) {
3153                 assert(isPlayerControlled());
3154                 player_party->enableRoundRobinMode();
3155         }
3156 
3157         bool wasLeader = isLeader();
3158 
3159         unreadyAll();
3160 
3161         player_party->removeMember(this);
3162         log_msg("%s leaves the party", getName());
3163         if (wasLeader) {
3164                 player_party->enableFollowMode();
3165         }
3166 }
3167 
getActivity()3168 int Character::getActivity()
3169 {
3170         if (place_contains_hostiles(getPlace(), this))
3171                 return FIGHTING;
3172 
3173         if (! sched)
3174                 return NONE;
3175         getAppointment();
3176         if (atAppointment())
3177                 return sched->appts[appt].act;
3178         return COMMUTING;
3179 }
3180 
setActivity(int val)3181 void Character::setActivity(int val)
3182 {
3183         activity = val;
3184         changeSleep(activity == SLEEPING);
3185 }
3186 
canWanderTo(int newx,int newy)3187 bool Character::canWanderTo(int newx, int newy)
3188 {
3189         // If this party is on a schedule then limit wandering to the
3190         // area specied in the current appt.
3191         if (sched) {
3192                 if (newx < sched->appts[appt].x ||
3193                     newx > (sched->appts[appt].x + sched->appts[appt].w - 1) ||
3194                     newy < sched->appts[appt].y ||
3195                     newy > (sched->appts[appt].y + sched->appts[appt].h) - 1)
3196                         return false;
3197         }
3198 
3199         return true;
3200 }
3201 
char_dtor(void * val)3202 void char_dtor(void *val)
3203 {
3204         delete (class Character*)val;
3205 }
3206 
char_save(save_t * save,void * val)3207 void char_save(save_t *save, void *val)
3208 {
3209         ((class Character*)val)->save(save);
3210 }
3211 
save(struct save * save)3212 void Character::save(struct save *save)
3213 {
3214         class ArmsType *arms;
3215 
3216         if (saved == save->session_id) {
3217                 save->write(save, "%s\n", tag);
3218                 return;
3219         }
3220 
3221         saved = save->session_id;
3222 
3223         // Create it within a 'let' block
3224         save->enter(save, "(let ((kchar ");
3225 
3226         if (getGob()) {
3227                 // wrap the declaration in a call to bind the object to the
3228                 // gob
3229                 save->enter(save, "(bind\n");
3230 
3231                 if (getForceContainerDrop()) {
3232                         save->enter(save, "(kern-char-force-drop");
3233                 }
3234         }
3235 
3236         // Create the object within a 'let' block
3237         save->enter(save, "(let ((kchar ");
3238         save->enter(save, "(kern-mk-char\n");
3239         if (this->tag) {
3240                 save->write(save, "\'%s  ; tag\n", this->tag );
3241         } else {
3242                 save->write(save, "nil  ; tag\n");
3243         }
3244         save->write(save, "\"%s\"  ; name\n", this->getName());
3245         save->write(save, "%s  ; species\n",  this->species->tag);
3246         save->write(save, "%s  ; occ\n", this->occ ? this->occ->tag : "nil");
3247         sprite_save(current_sprite, save);
3248         /*save->write(save, "%s\n", sprite_get_tag(this->sprite));*/
3249         save->write(save, "%d  ; BaseFaction\n", getBaseFaction());
3250         save->write(save, "%d %d %d  ; str, int, dex\n",    str, intl, dex);
3251         save->write(save, "%d %d  ;    hp_mod, hp_mult\n",  this->hp_mod,    this->hp_mult);
3252         save->write(save, "%d %d  ;    mp_mod, mp_mult\n",  this->mp_mod,    this->mp_mult);
3253         save->write(save, "%d %d  ;    HP, XP\n",           this->getHp(),   this->getExperience());
3254         save->write(save, "%d  ;    mana\n",        this->getMana()  );
3255         save->write(save, "%d  ;    AP_per_round\n", this->AP_per_round );
3256         save->write(save, "%d  ;    level\n",       this->getLevel() );
3257         save->write(save, "#%c  ; dead?\n", isDead() ? 't' : 'f');
3258 
3259         if (conv != NULL) {
3260                 conv_save(conv, save);
3261         } else {
3262                 save->write(save, "nil  ; conv\n");
3263         }
3264 
3265         save->write(save, "%s  ; sched\n", sched? sched->tag : "nil");
3266 
3267         if (ai != NULL) {
3268                 closure_save(ai, save);
3269         } else
3270                 save->write(save, "nil  ; ai\n");
3271 
3272         // Items in personal inventory.
3273         if (!container) {
3274                 save->write(save, "nil  ; inventory\n");
3275         } else {
3276                 container->save(save);
3277         }
3278 
3279         // Readied items. Subtle: if this character has a container then these
3280         // items have already been saved.
3281 		int armsIndex=0;
3282 	arms = this->enumerateArms(&armsIndex);
3283         if (! arms) {
3284                 save->write(save, "nil  ; readied arms\n");
3285         } else {
3286                 save->enter(save, "(list\n");
3287                 while (arms != NULL) {
3288                         save->write(save, "%s\n", arms->getTag());
3289                         arms = this->getNextArms(&armsIndex);
3290                 }
3291                 save->exit(save, ")  ; readied arms\n\n");
3292         }
3293 
3294         // Hooks
3295         Object::saveHooks(save);
3296 
3297         // Close the <var-list> part of the 'let' block
3298         save->exit(save, "))) ;; end ((kchar ...)\n");
3299 
3300         if (isSubmerged()) {
3301                 save->write(save, "(kern-obj-set-submerged kchar #t)\n");
3302         }
3303 
3304         // Close the 'let' block
3305         save->exit(save, "kchar) ;; end (let ...)\n");
3306 
3307         if (getForceContainerDrop()) {
3308                 save->exit(save, "#t) ;; kern-char-force-drop\n");
3309         }
3310 
3311         if (getGob()) {
3312 
3313                 // save the gob list
3314                 gob_save(getGob(), save);
3315 
3316                 // end the bind call
3317                 save->exit(save, ") ;; bind\n");
3318         }
3319 
3320         // close the args assignment section of the let block
3321         save->write(save, ")) ");
3322 
3323         if (isKnown()) {
3324                 save->write(save, "(kern-char-set-known kchar #t)\n");
3325         }
3326 
3327         // Only save AP debt, else when the game reloads and the character
3328         // restarts its turn it will carry over what is saved as an unwarranted
3329         // bonus.
3330         if (getActionPoints() < 0) {
3331             save->write(save, "(kern-obj-set-ap kchar %d)\n", getActionPoints());
3332         }
3333 
3334         // save the task, if any
3335         if (taskproc) {
3336             save->write(save, "(kern-char-task-continue kchar \"%s\" ", getTaskName());
3337             closure_save(taskproc, save);
3338             if (taskgob) {
3339                 gob_save(taskgob, save);
3340             } else {
3341                 save->write(save, " nil");
3342             }
3343             save->write(save, ")\n");
3344         }
3345 
3346         // close the 'let' block
3347         save->exit(save, "kchar)\n");
3348 }
3349 
setSchedule(struct sched * val)3350 void Character::setSchedule(struct sched *val)
3351 {
3352 
3353         sched = val;
3354 
3355         if (sched) {
3356                 sched_chars_node = session_add_sched_char(Session, this);
3357         } else {
3358                 // Bugfix: if a character is eating and the schedule is set to
3359                 // NULL then the character will be stuck eating (and doing
3360                 // nothing else!).
3361                 setActivity(NONE);
3362         }
3363 
3364         // Bugfix: if an npc with a schedule is added to the player party
3365         // either during the game or on reload, and it has a schedule, then the
3366         // next time the player enters a town the scheduling code will mess
3367         // with the party member.
3368         if (! sched && sched_chars_node) {
3369                 session_rm_sched_char(sched_chars_node);
3370                 sched_chars_node = 0;
3371         }
3372 }
3373 
tryToRelocateToNewPlace(struct place * newplace,int newx,int newy,struct closure * closure)3374 bool Character::tryToRelocateToNewPlace(struct place *newplace,
3375                                         int newx, int newy,
3376                                         struct closure *closure)
3377 {
3378         // NPCs and charmed PCs are not allowed to change places because I
3379         // don't want them dissappearing into the ether.
3380         //
3381         // Addendum: this needs to work for characters with multi-place
3382         // schedules. If I only forbid entry to the wilderness this should work
3383         // as originally intended.
3384         if (place_is_wilderness(newplace)
3385             && (! isPlayerControlled())) {
3386                 return false;
3387         }
3388 
3389         // -----------------------------------------------------------------
3390         // At this point I know the character is player-controlled, so I can
3391         // print informative messages.
3392         // -----------------------------------------------------------------
3393 
3394         if (player_party->get_num_living_members() == 1) {
3395                 // Force to follow mode to avoid the annoying case
3396                 // where only one member is in the party and the player
3397                 // wants to leave a combat map.
3398                 player_party->enableFollowMode();
3399         }
3400 
3401         if (player_party->getPartyControlMode() != PARTY_CONTROL_FOLLOW) {
3402                 log_msg("Exit - must be in follow mode!");
3403                 return false;
3404         }
3405 
3406         if (player_party->get_leader() != this)
3407                 return false;
3408 
3409         if (!player_party->rendezvous(getPlace(), getX(), getY())) {
3410                 log_msg("Exit - party can't rendezvous!");
3411                 return false;
3412         }
3413 
3414         groupExitTo(newplace, newx, newy, closure);
3415 
3416         return true;
3417 
3418 }
3419 
addDefense(int val)3420 void Character::addDefense(int val)
3421 {
3422         defenseBonus += val;
3423 }
3424 
getMovementMode()3425 struct mmode *Character::getMovementMode()
3426 {
3427         if (currentMmode) {
3428                 return currentMmode;
3429         } else {
3430                 return species->mmode;
3431         }
3432 }
3433 
setMovementMode(struct mmode * mmode)3434 void Character::setMovementMode(struct mmode *mmode)
3435 {
3436         currentMmode = mmode;
3437 }
3438 
setCurrentFaction(int faction)3439 void Character::setCurrentFaction(int faction)
3440 {
3441 #if 1
3442     Being::setCurrentFaction(faction);
3443 
3444     // We need to implement charm differently. This is the wrong place to do
3445     // it.
3446 #else
3447         if (! isPlayerPartyMember()) {
3448                 Being::setCurrentFaction(faction);
3449         } else {
3450                 if (faction != player_party->getBaseFaction()) {
3451                         // player party member charmed
3452 
3453                         // Fix for [ 1425039 ] Solo mode + possession = lockup:
3454                         // if in solo mode, switch party to follow mode
3455                         if (isSolo()) {
3456                                 setSolo(false);
3457                                 player_party->enableFollowMode();
3458                         }
3459 
3460                         setControlMode(CONTROL_MODE_AUTO);
3461 
3462                         // subtle: in this case, do this AFTER calling
3463                         // setControlMode() (charmed character's ignore control
3464                         // mode changes)
3465                         Being::setCurrentFaction(faction);
3466 
3467                 } else {
3468                         // subtle: in this case, do this BEFORE calling
3469                         // setControlMode() (opposite of above)
3470                         Being::setCurrentFaction(faction);
3471 
3472                         // player party member uncharmed
3473                         switch (player_party->getPartyControlMode()) {
3474                         case PARTY_CONTROL_ROUND_ROBIN:
3475                                 setControlMode(CONTROL_MODE_PLAYER);
3476                                 break;
3477                         case PARTY_CONTROL_SOLO:
3478                                 setControlMode(CONTROL_MODE_IDLE);
3479                                 break;
3480                         case PARTY_CONTROL_FOLLOW:
3481                                 if (isLeader()) {
3482                                         setControlMode(CONTROL_MODE_PLAYER);
3483                                 } else {
3484                                         setControlMode(CONTROL_MODE_FOLLOW);
3485                                 }
3486                                 break;
3487                         }
3488                 }
3489         }
3490 #endif
3491 }
3492 
getInventoryContainer()3493 class Container* Character::getInventoryContainer()
3494 {
3495         /* for player-controlled maybe return party inventory? */
3496         if (isPlayerControlled())
3497                 return player_party->inventory;
3498         return container;
3499 }
3500 
setInventoryContainer(class Container * val)3501 void Character::setInventoryContainer(class Container *val)
3502 {
3503         // Blow away the old one
3504         if (container) {
3505                 assert(container->isEmpty());
3506                 obj_dec_ref(container);
3507         }
3508 
3509         container = val;
3510         if (container) {
3511                 obj_inc_ref(container);
3512         }
3513 }
3514 
setAI(struct closure * val)3515 void Character::setAI(struct closure *val)
3516 {
3517         // out with the old
3518         if (ai) {
3519                 closure_unref(ai);
3520                 ai = NULL;
3521         }
3522 
3523         // in with the new
3524         if (val) {
3525                 closure_ref(val);
3526                 ai = val;
3527         }
3528 }
3529 
getAI()3530 struct closure *Character::getAI()
3531 {
3532         return ai;
3533 }
3534 
setDead(bool val)3535 void Character::setDead(bool val)
3536 {
3537         dead = val;
3538 }
3539 
getForceContainerDrop()3540 bool Character::getForceContainerDrop()
3541 {
3542         return forceContainerDrop;
3543 }
3544 
setForceContainerDrop(bool val)3545 void Character::setForceContainerDrop(bool val)
3546 {
3547         forceContainerDrop = val;
3548 }
3549 
isStationary()3550 bool Character::isStationary()
3551 {
3552         return species->stationary;
3553 }
3554 
isKnown()3555 bool Character::isKnown()
3556 {
3557         return known;
3558 }
3559 
setKnown(bool val)3560 void Character::setKnown(bool val)
3561 {
3562         known = val;
3563 }
3564 
getTaskName()3565 const char * Character::getTaskName()
3566 {
3567     return taskname ? taskname : "a nameless task";
3568 }
3569 
taskCleanup()3570 void Character::taskCleanup()
3571 {
3572     if (taskname) {
3573         free(taskname);
3574         taskname = NULL;
3575     }
3576 
3577     if (taskproc) {
3578         closure_unref(taskproc);
3579         taskproc = NULL;
3580     }
3581 
3582     if (taskgob) {
3583         gob_unref(taskgob);
3584         taskgob = NULL;
3585     }
3586 
3587     if (isPlayerPartyMember()) {
3588         class Character *oldLeader, *newLeader;
3589         switch (player_party->getPartyControlMode()) {
3590         case PARTY_CONTROL_FOLLOW:
3591             oldLeader = player_party->get_leader();
3592             player_party->chooseNewLeader();
3593             newLeader = player_party->get_leader();
3594             if (oldLeader != newLeader) {
3595                 oldLeader->setControlMode(CONTROL_MODE_FOLLOW);
3596             }
3597             if (newLeader != this) {
3598                 setControlMode(CONTROL_MODE_FOLLOW);
3599             }
3600             break;
3601         case PARTY_CONTROL_SOLO:
3602             setControlMode(CONTROL_MODE_IDLE);
3603             break;
3604         case PARTY_CONTROL_ROUND_ROBIN:
3605             setControlMode(CONTROL_MODE_PLAYER);
3606             break;
3607         }
3608     }
3609 }
3610 
taskAbort()3611 void Character::taskAbort()
3612 {
3613     log_msg("%s aborts %s!", getName(), getTaskName());
3614     taskCleanup();
3615 }
3616 
taskSetup(char * name_arg,struct closure * proc_arg,struct gob * gob_arg)3617 void Character::taskSetup(char *name_arg, struct closure *proc_arg, struct gob *gob_arg)
3618 {
3619     assert(! engagedInTask());
3620 
3621     if (name_arg) {
3622         taskname = strdup(name_arg);
3623     }
3624 
3625     assert(proc_arg);
3626     taskproc = proc_arg;
3627     closure_ref(taskproc);
3628 
3629     if (gob_arg) {
3630         taskgob = gob_arg;
3631         gob_ref(gob_arg);
3632     }
3633 
3634     if (isPlayerPartyMember()) {
3635         switch (player_party->getPartyControlMode()) {
3636         case PARTY_CONTROL_FOLLOW:
3637             if (isLeader()) {
3638                 player_party->chooseNewLeader();
3639                 assert(this != player_party->get_leader());
3640             }
3641             break;
3642         case PARTY_CONTROL_SOLO:
3643             if (isSolo()) {
3644                 setSolo(false); // sets CONTROL_MODE_IDLE
3645                 player_party->enableFollowMode();
3646             }
3647             break;
3648         case PARTY_CONTROL_ROUND_ROBIN:
3649             break;
3650         }
3651     }
3652 
3653     setControlMode(CONTROL_MODE_TASK);
3654     taskInterruptOnDamage = true;
3655 }
3656 
taskBegin(char * name_arg,struct closure * proc_arg,struct gob * gob_arg)3657 void Character::taskBegin(char *name_arg, struct closure *proc_arg, struct gob *gob_arg)
3658 {
3659     taskSetup(name_arg, proc_arg, gob_arg);
3660     log_msg("%s begins %s.", getName(), getTaskName());
3661     endTurn();
3662 }
3663 
taskContinue(char * name_arg,struct closure * proc_arg,struct gob * gob_arg)3664 void Character::taskContinue(char *name_arg, struct closure *proc_arg, struct gob *gob_arg)
3665 {
3666     if (isPlayerControlled()) {
3667         log_msg("%s continues %s.", getName(), getTaskName());
3668     }
3669 
3670     taskSetup(name_arg, proc_arg, gob_arg);
3671 }
3672 
taskEnd()3673 void Character::taskEnd()
3674 {
3675     log_msg("%s completes %s!", getName(), getTaskName());
3676     taskCleanup();
3677 }
3678 
engagedInTask()3679 bool Character::engagedInTask()
3680 {
3681     return taskproc != NULL;
3682 }
3683