OSDN Git Service

日本語版
[nazghul-jp/nazghul-jp.git] / src / combat.c
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 "combat.h"
23 #include "dice.h"
24 #include "node.h"
25 #include "Party.h"
26 #include "place.h"
27 #include "player.h"
28 #include "object.h"
29 #include "common.h"
30 #include "map.h"
31 #include "hash.h"
32 #include "wq.h"
33 #include "sound.h"
34 #include "status.h"
35 #include "cursor.h"
36 #include "Container.h"
37 #include "terrain.h"
38 #include "Field.h"
39 #include "event.h"
40 #include "play.h"
41 #include "foogod.h"
42 #include "wind.h"
43 #include "dup_constants.h"
44 #include "cmdwin.h"
45 #include "terrain_map.h"
46 #include "vehicle.h"
47 #include "formation.h"
48 #include "pinfo.h"
49 #include "cmd.h"
50 #include "formation.h"
51 #include "session.h"
52 #include "log.h"
53 #include "vmask.h"
54 #include "factions.h"
55 #include "blender.h"
56
57 #include <assert.h>
58 #include <stdlib.h>
59 #include <time.h>
60 #include <unistd.h>
61 #include <math.h>
62
63 #define FORMATION_H     (formation[array_sz(formation) - 1].y)
64 #define N_MAX_NPCS      256      /* arbitrary limit */
65 #define MAX_DEPTH       64
66 #define MAX_PLACEMENT_RECTANGLE_W 32
67 #define MAX_PLACEMENT_RECTANGLE_H 16
68 #define SEARCH_QUEUE_SZ 100
69
70 /* Formation pattern -- party facing north (dx=0,dy=-1), origin at the leader's
71  * position */
72
73 enum combat_faction_status {
74         COMBAT_FACTION_EXISTS,
75         COMBAT_FACTION_GONE,
76         COMBAT_FACTION_CHARMED,
77         COMBAT_FACTION_CAMPING
78 };
79
80 static struct {
81         struct place *place;
82         void *session_handle;
83         enum combat_state state;
84         char vmap[7 * 7];       // visited map (used to search for positions)
85         class Vehicle *enemy_vehicle;
86         sound_t *sound_enter;
87         sound_t *sound_defeat;
88         sound_t *sound_victory;
89         int round;
90 } Combat;
91
92 struct v2 {
93         struct place *place;
94         int dx, dy;
95 };
96
97 // Search alg data
98 static char rmap[MAX_PLACEMENT_RECTANGLE_W * MAX_PLACEMENT_RECTANGLE_H];
99 static int x_q[SEARCH_QUEUE_SZ];
100 static int y_q[SEARCH_QUEUE_SZ];
101 static int q_head;
102 static int q_tail;
103
104 enum combat_state combat_get_state(void)
105 {
106         return Combat.state;
107 }
108
109 void combat_set_state(enum combat_state new_state)
110 {
111         // --------------------------------------------------------------------
112         // Interesting state transitions:
113         //
114         // ====================================================================
115         // old state              | new state              | result
116         // ====================================================================
117         // COMBAT_STATE_DONE      | COMBAT_STATE_FIGHTING  | entry to combat
118         // COMBAT_STATE_DONE      | COMBAT_STATE_LOOTING   | non-hostile
119         // COMBAT_STATE_DONE      | COMBAT_STATE_CAMPING   | entry to camping
120         // COMBAT_STATE_FIGHTING  | COMBAT_STATE_DONE      | defeat
121         // COMBAT_STATE_FIGHTING  | COMBAT_STATE_LOOTING   | victory
122         // COMBAT_STATE_LOOTING   | COMBAT_STATE_FIGHTING  | hostiles entered
123         // COMBAT_STATE_LOOTING   | COMBAT_STATE_DONE      | exit normally
124         // COMBAT_STATE_CAMPING   | COMBAT_STATE_FIGHTING  | ambush
125         // COMBAT_STATE_CAMPING   | COMBAT_STATE_DONE      | exit camping
126         // ====================================================================
127         //
128         // --------------------------------------------------------------------
129
130         if (Combat.state == new_state)
131                 return;
132
133
134         switch (Combat.state) {
135
136         case COMBAT_STATE_DONE:
137                 switch (new_state) {
138                 case COMBAT_STATE_FIGHTING:
139                         log_banner("^c+mÀïÆ®^c-");
140                         sound_play(Combat.sound_enter, SOUND_MAX_VOLUME);
141                         break;
142                 case COMBAT_STATE_LOOTING:
143                         break;
144                 case COMBAT_STATE_CAMPING:
145                         log_banner("µÙ©");
146                         break;
147                 default:
148                         assert(false);
149                         break;
150                 }
151                 break;
152
153         case COMBAT_STATE_FIGHTING:
154                 switch (new_state) {
155                 case COMBAT_STATE_LOOTING:
156                         log_banner("^c+g¾¡Íø^c-");
157                         sound_play(Combat.sound_victory, SOUND_MAX_VOLUME);
158                         player_party->addExperience(COMBAT_VICTORY_XP);
159                         break;
160                 case COMBAT_STATE_DONE:
161                         log_banner("^c+rÇÔËÌ^c-");
162                         sound_play(Combat.sound_defeat, SOUND_MAX_VOLUME);
163                         break;
164                 default:
165                         assert(false);
166                         break;
167                 }
168                 break;
169
170         case COMBAT_STATE_LOOTING:
171                 switch (new_state) {
172                 case COMBAT_STATE_FIGHTING:
173                         log_banner("^c+mÀïÆ®^c-");
174                         sound_play(Combat.sound_enter, SOUND_MAX_VOLUME);
175                         break;
176                 case COMBAT_STATE_DONE:
177                         break;
178                 default:
179                         assert(false);
180                         break;
181                 }
182                 break;
183
184         case COMBAT_STATE_CAMPING:
185                 switch (new_state) {
186                 case COMBAT_STATE_FIGHTING:
187                         log_banner("^c+mÀïÆ®^c-");
188                         sound_play(Combat.sound_enter, SOUND_MAX_VOLUME);
189                         break;
190                 case COMBAT_STATE_LOOTING:
191                 case COMBAT_STATE_DONE:
192                         break;
193                 default:
194                         assert(false);
195                         break;
196                 }
197                 break;
198
199         default:
200                 assert(false);
201                 break;
202         }
203
204         Combat.state = new_state;
205         session_run_hook(Session, combat_change_hook, "p", Session->player);
206 }
207
208 // returns 0 for ok position, -1 for no position, or a PFLAG type for fallback positions with problems
209 // input includes current best case problemness
210 static int location_is_safe(struct position_info *info, int current)
211 {
212         struct astar_node *path;
213         struct astar_search_info as_info;
214         struct terrain *terrain;
215         int returntype=0;
216
217         // Is it passable?
218         if (!place_is_passable(info->place, info->px, info->py, 
219                                info->subject, 0)) {
220                 dbg("impassable\n");
221                 return -1;
222         }
223         // Is it occupied?
224         if (place_is_occupied(info->place, info->px, info->py)) {
225                 dbg("occupied\n");
226                 returntype= PFLAG_IGNOREBEINGS;
227                 if (current & (PFLAG_IGNOREHAZARDS || PFLAG_IGNOREBEINGS || PFLAG_IGNOREFIELDS))
228                         return -1;
229         }
230
231         // I added the next two checks because a character was getting
232         // positioned over the firepit while camping, and I thought it was
233         // damaging him. Turns out firepits weren't setup to cause fire damage
234         // (oddly), and the character was just starving. I'll leave this here
235         // for now anyway.
236
237         // Is it dangerous? Hack: check for a field and dangerous terrain
238         if (place_get_object(info->place, info->px, info->py, field_layer)) {
239                 dbg("possibly dangerous field\n");
240                 returntype= PFLAG_IGNOREFIELDS;
241                 if (current & (PFLAG_IGNOREHAZARDS || PFLAG_IGNOREFIELDS))
242                         return -1;
243         }
244         terrain = place_get_terrain(info->place, info->px, info->py);
245         if (terrain->effect) {
246                 dbg("possibly dangerous terrain\n");
247                 returntype= PFLAG_IGNOREHAZARDS;
248                 if (current & (PFLAG_IGNOREHAZARDS ))
249                         return -1;
250         }
251
252         memset(&as_info, 0, sizeof (as_info));
253         if (info->find_party) {
254                 // Each member should be able to find a path back to the
255                 // party's originating location on the map.
256                 dbg("searching for path to party [%d %d]...",
257                        info->x, info->y);
258
259                 as_info.x0 = info->px;
260                 as_info.y0 = info->py;
261                 as_info.x1 = info->x;
262                 as_info.y1 = info->y;
263                 as_info.flags = PFLAG_IGNOREBEINGS;
264                 as_info.limit_depth = true;
265                 as_info.max_depth = 5;
266
267                 path = place_find_path(info->place, &as_info, info->subject);
268
269                 if (!path)
270                         dbg("no path back to party\n");
271         }
272         else {
273                 // skip the pathfinding check
274                 return returntype;
275         }
276
277         if (path) {
278                 astar_path_destroy(path);
279                 return returntype;
280         }
281
282         return -1;
283 }
284
285 // returns 0 for ok position, -1 for no position, or a PFLAG type for fallback positions with problems
286 // input includes current best case problemness
287 static int combat_search_for_safe_position(struct position_info *info, int currentsafety)
288 {
289         unsigned int i;
290         int index;
291         static int x_offsets[] = { -1, 1, 0, 0 };
292         static int y_offsets[] = { 0, 0, -1, 1 };
293         int locationsafety=-1;
294
295         dbg("checking [%d %d]...", info->px, info->py);
296
297         // translate the map coords into an rmap index
298         index = info->py - info->ry;
299         index *= info->rw;
300         index += (info->px - info->rx);
301
302         // If the current location is off-map, outside of the placement
303         // rectangle or already visited then discontinue the search.  
304         if (rmap[index]) {
305                 dbg("already visited [%d]\n", index);
306                 return -1;      // already visited
307         }
308         if (info->px < info->rx || info->px >= info->rx + info->rw ||
309             info->py < info->ry || info->py >= info->ry + info->rh) {
310                 dbg("outside the placement area\n");
311                 return -1;      // outside the placement rect
312         }
313         if (place_off_map(info->place, info->px, info->py)) {
314                 dbg("off-map\n");
315                 // return -1; // off map
316                 goto enqueue_neighbors;
317         }
318         // Mark this location as visited.
319         rmap[index] = 1;
320
321         // If the current location is safe then the search succeeded.
322         locationsafety = location_is_safe(info,currentsafety);
323         if (locationsafety == 0) {
324                 dbg("OK!\n");
325                 return 0;
326         }
327
328       enqueue_neighbors:
329         // Enqueue the adjacent neighbors onto the search queue.
330         for (i = 0; i < array_sz(x_offsets) && q_tail < SEARCH_QUEUE_SZ; i++) {
331                 assert(q_tail < SEARCH_QUEUE_SZ);
332                 x_q[q_tail] = info->px + x_offsets[i];
333                 y_q[q_tail] = info->py + y_offsets[i];
334                 q_tail++;
335         }
336
337         // Return still not found.
338         return locationsafety;
339 }
340
341 // returns 0 for ok position, -1 for no position, or a PFLAG type for fallback positions with problems
342 static int combat_find_safe_position(struct position_info *info)
343 {
344         // Here's my new definition of a safe place: a safe place is a tile
345         // within the placement rectangle which is passable to the character in
346         // question and from which the character in question can pathfind to
347         // the edge from which the party entered.
348
349           // store data on a fallback not-so-safe position
350           int currentsafety = 0;
351           int cur_x = 0, cur_y = 0;
352           int newsafety = -1;
353           
354         // Clear the search queue.
355         q_head = q_tail = 0;
356
357         // Push the preferred position onto the search queue.
358         x_q[q_tail] = info->px;
359         y_q[q_tail] = info->py;
360         q_tail++;
361
362         // Run through the search queue until it is exhausted or a safe
363         // position has been found.
364         while (q_head != q_tail) {
365
366                 // Dequeue the next location to check.
367                 info->px = x_q[q_head];
368                 info->py = y_q[q_head];
369                 q_head++;
370
371                 // If it is ok then we're done.
372                 newsafety = combat_search_for_safe_position(info, currentsafety);
373                 if (newsafety == 0)
374                         return 0;
375                 else if (newsafety != -1)
376                 {
377                         currentsafety = newsafety;
378                         cur_x = info->px;
379                         cur_y = info->py;
380                 }
381         }
382
383         if (currentsafety != 0)
384         {
385                 info->px = cur_x;
386                 info->py = cur_y;  
387                 return currentsafety;
388         }
389         
390         return -1;
391 }
392
393 static bool myPutNpc(class Character * pm, void *data)
394 {
395         int tmp;
396         struct position_info *info;
397
398
399         info = (struct position_info *) data;
400
401         if (pm->isDead())
402                 return false;
403
404         // In the case where there is more than one NPC party entering combat
405         // this might be called more than once for an NPC. I want to ignore all
406         // but the first call, so check if the NPC is already on the map.
407         if (pm->isOnMap())
408                 return false;
409
410         // Make sure we don't index beyond the end of the formation array.
411         if (pm->getOrder() >= info->formation->n)
412                 return false;
413
414         pm->setCombat(true);
415
416         pm->setX(info->formation->entry[pm->getOrder()].x);
417         pm->setY(info->formation->entry[pm->getOrder()].y);
418
419         /* Counterclockwise rotations: x = x * cos - y * sin y = x * sin + y *
420          * cos */
421         if (info->dx < 0) {
422                 /* Rotate +90 degrees */
423                 tmp = pm->getX();
424                 pm->setX(pm->getY());
425                 pm->setY(tmp);
426         }
427         else if (info->dx > 0) {
428                 /* Rotate -90 degrees */
429                 tmp = pm->getX();
430                 pm->setX(-pm->getY());
431                 pm->setY(tmp);
432         }
433         else if (info->dy > 0) {
434                 /* Rotate 180 degrees */
435                 pm->setX(-pm->getX());
436                 pm->setY(-pm->getY());
437         }
438
439         /* If dy > 1 then the formation is ok as-is. */
440         pm->changeX(info->x);
441         pm->changeY(info->y);
442
443         // Check if that location will really work. If not then do a DFS
444         // starting from the desired location and see if we can find someplace
445         // that WILL work.
446
447         // initialize the position info for a new search
448         info->subject = pm;
449         memset(rmap, 0, sizeof (rmap));
450
451         // set the preferred location
452         info->px = pm->getX();
453         info->py = pm->getY();
454
455         dbg("Placing %s\n", pm->getName());
456
457         if (combat_find_safe_position(info) == -1) {
458                 // If I can't place a member then I can't place it.
459                 dbg("*** Can't place %s ***\n", pm->getName());
460                 return false;
461         }
462
463         pm->setX(info->px);
464         pm->setY(info->py);
465         dbg("Put '%s' at [%d %d]\n", pm->getName(), info->px, info->py);
466         pm->setPlace(Place);
467         place_add_object(Place, pm);
468         pm->setOnMap(true);
469         info->placed++;
470
471         /* Check if we need to go back to fighting */
472         if (combat_get_state() != COMBAT_STATE_FIGHTING &&
473             are_hostile(pm, player_party)) {
474                 combat_set_state(COMBAT_STATE_FIGHTING);
475         }
476
477         return false;
478 }
479
480 static void set_party_initial_position(struct position_info *pinfo, int x, int y)
481 {
482         pinfo->x = x;
483         pinfo->y = y;
484
485         // Set the bounds of the placement rectangle. For now I don't care if
486         // it overlaps the edge of the map because the search algorithm will
487         // check for off-map locations.
488         pinfo->rw = MAX_PLACEMENT_RECTANGLE_W;
489         pinfo->rh = MAX_PLACEMENT_RECTANGLE_H;
490         pinfo->rx = pinfo->x - pinfo->rw / 2;
491         pinfo->ry = pinfo->y - pinfo->rh / 2;
492
493         dbg("Moved party start position to [%d %d]\n", pinfo->x, pinfo->y);;
494
495 }
496
497 void combat_fill_position_info(struct position_info *info, struct place *place, int x, int y, int dx, int dy, bool defend)
498 {
499         // 
500         // This function will:
501         // * determine the party's coordinates upon entry to combat
502         // * turn the defending party to face the attacker
503         // * specify the placement rectangle for the party members
504         // * set the flags for the placement algorithm
505         // 
506
507         info->place = place;
508         info->dx = dx;
509         info->dy = dy;
510
511         if (info->place != Combat.place) {
512                 // Occupy the same location and face the same way
513                 info->x = x;
514                 info->y = y;
515
516         }
517         else {
518
519                 if (defend) {
520                         // Reverse facing
521                         info->dx = -dx;
522                         info->dy = -dy;
523                         dx = -dx;
524                         dy = -dy;
525                 }
526                 // Occupy an edge facing the opponent
527                 if (dx < 0) {
528                         // facing west, occupy east half
529                         info->x = place_w(info->place) - place_w(info->place) / 4;
530                 }
531                 else if (dx > 0) {
532                         // facing east, occupy west half
533                         info->x = place_w(info->place) / 4;
534                 }
535                 else {
536                         // facing north or south, center on east-west
537                         info->x = place_w(info->place) / 2;
538                 }
539
540                 if (dy < 0) {
541                         // facing north, occupy south
542                         info->y = place_h(info->place) - place_h(info->place) / 4;
543                 }
544                 else if (dy > 0) {
545                         // facing south, occupy north
546                         info->y = place_h(info->place) / 4;
547                 }
548                 else {
549                         // facing east or west, center on north-south
550                         info->y = place_h(info->place) / 2;
551                 }
552         }
553
554         set_party_initial_position(info, info->x, info->y);
555
556         // clear the pmask and search map before first use
557         info->subject = NULL;
558         memset(rmap, 0, sizeof (rmap));
559
560         info->placed = 0;
561 }
562
563 bool combat_place_character(class Character * pm, void *data)
564 {
565         // Put a party member on the combat map
566
567         int tmp;
568         struct position_info *info;
569
570         info = (struct position_info *) data;
571
572         if (pm->isDead())
573                 return false;
574
575         if (pm->getOrder() >= info->formation->n)
576                 return false;
577
578         pm->setX(info->formation->entry[pm->getOrder()].x);
579         pm->setY(info->formation->entry[pm->getOrder()].y);
580
581         /* Counterclockwise rotations: x = x * cos - y * sin y = x * sin + y *
582          * cos */
583         if (info->dx < 0) {
584                 /* Rotate +90 degrees */
585                 tmp = pm->getX();
586                 pm->setX(pm->getY());
587                 pm->setY(tmp);
588         }
589         else if (info->dx > 0) {
590                 /* Rotate -90 degrees */
591                 tmp = pm->getX();
592                 pm->setX(-pm->getY());
593                 pm->setY(tmp);
594         }
595         else if (info->dy > 0) {
596                 /* Rotate 180 degrees */
597                 pm->setX(-pm->getX());
598                 pm->setY(-pm->getY());
599         }
600
601         /* If dy > 1 then the formation is ok as-is. */
602         pm->changeX(info->x);
603         pm->changeY(info->y);
604
605         // Check if that location will really work. If not then do a DFS
606         // starting from the desired location and see if we can find someplace
607         // that WILL work.
608
609         // init the position info for a new search
610         info->subject = pm;
611         memset(rmap, 0, sizeof (rmap));
612         info->px = pm->getX();
613         info->py = pm->getY();
614         dbg("Placing %s\n", pm->getName());
615
616         if (combat_find_safe_position(info) != 0) {
617
618                 // Ok, so that didn't work. This can happen when the party
619                 // leader is right on the map border facing towards the map
620                 // center. In this case the find-safe-place alg won't handle
621                 // followers that are too deep off the map. If this IS the
622                 // party leader, or if upon retry we STILL can't find a safe
623                 // place, then screw it - we'll place this character on the
624                 // start location (even if we have to end up stacking the whole
625                 // party there!).
626
627                 // Corner case: a portal leads from one lake to another. A
628                 // hostile npc party is sitting right on the destination. The
629                 // party enters the portal on foot, upon arrival the npc party
630                 // attacks. Party members which are on foot get stacked on the
631                 // entry point and cannot move or flee. This is an unfriendly
632                 // situation, but something of a corner case. Map hackers can
633                 // skirt the issue and player's can expect that entering
634                 // portals involves an element of danger :). The engine won't
635                 // crash and the player can usually get out of the fix by
636                 // defeating the npc's.
637
638                 class Character *leader = player_party->get_leader();
639
640                 if (!leader) {
641                         dbg("Putting %s on start location [%d %d]\n",
642                                pm->getName(), info->x, info->y);
643                         info->px = info->x;
644                         info->py = info->y;
645                 }
646                 else {
647                         // init the position info to search again
648                         memset(rmap, 0, sizeof (rmap));
649                         info->px = leader->getX();
650                         info->py = leader->getY();
651                         dbg("Retrying %s\n", pm->getName());
652
653                         if (combat_find_safe_position(info) != 0) {
654                                 dbg("Putting %s on start location "
655                                        "[%d %d]\n",
656                                        pm->getName(), info->x, info->y);
657                                 info->px = info->x;
658                                 info->py = info->y;
659                         }
660                 }
661         }
662
663         pm->relocate(info->place, info->px, info->py);
664         
665         return false;
666 }
667
668 static bool mySetInitialCameraPosition(class Character * pm, void *data)
669 {
670         if (pm->isOnMap()) {
671                 mapCenterCamera(pm->getX(), pm->getY());
672                 return true;
673         }
674         return false;
675 }
676
677 static void combat_overlay_vehicle(Vehicle *vehicle, int dx, int dy,
678                                    struct position_info *pinfo)
679 {
680
681         assert(pinfo->dx || pinfo->dy);
682         assert(!pinfo->dx || !pinfo->dy);       
683         
684         int dst_x = 0, dst_y = 0;
685                 
686         if (dx < 0) {
687                 // facing west, fill east half
688                 dst_x = COMBAT_MAP_W / 2;
689                 set_party_initial_position(pinfo, (COMBAT_MAP_W*3)/4,COMBAT_MAP_H/2);
690         }
691         else if (dx > 0) {
692                 // facing east, fill west half
693                 dst_x = COMBAT_MAP_W / 2 - COMBAT_MAP_W;
694                 set_party_initial_position(pinfo, COMBAT_MAP_W/4,COMBAT_MAP_H/2);
695         }
696         else if (dy < 0) {
697                 // facing north, fill south half
698                 dst_y = COMBAT_MAP_H / 2;
699                 set_party_initial_position(pinfo, COMBAT_MAP_W/2,(COMBAT_MAP_H*3)/4);
700         }
701         else if (dy > 0) {
702                 // facing south, fill north half
703                 dst_y = COMBAT_MAP_H / 2 - COMBAT_MAP_H;
704                 set_party_initial_position(pinfo, COMBAT_MAP_W/2,COMBAT_MAP_H/4);
705         }
706         else
707         {
708                 set_party_initial_position(pinfo, COMBAT_MAP_W/2,COMBAT_MAP_H/2);
709         }
710         
711         closure_exec(vehicle->getObjectType()->renderCombat, "ppdd", 
712         Place, vehicle, dst_x, dst_y);
713       
714 }
715                                    
716
717 static void combat_overlay_map(struct terrain_map *map, 
718                                struct position_info *pinfo, int broadside)
719 {
720         int x = 0, y = 0;
721
722         assert(pinfo->dx || pinfo->dy);
723         assert(!pinfo->dx || !pinfo->dy);
724
725         // Clone the map so we can make a rotated copy.
726         map = terrain_map_clone(map, "combat_overlay_map");
727         if (!map) {
728                 err("Failed to allocate temporary terrain map");
729                 return;
730         }
731         // Rotate the map so that north faces the opponent.
732         if (broadside) {
733                 terrain_map_rotate(map, vector_to_rotation(pinfo->dy, pinfo->dx));
734
735                 // Position the map against the boundary dividing the map.
736                 if (pinfo->dx < 0) {
737                         // facing west, shift map west toward edge
738                         x = (place_w(Place)) / 2;
739                         y = (place_h(Place) - map->h) / 2;
740                 }
741                 else if (pinfo->dx > 0) {
742                         // facing east, shift map east toward edge
743                         x = (place_w(Place)) / 2 - map->w;
744                         y = (place_h(Place) - map->h) / 2;
745                 }
746                 else if (pinfo->dy < 0) {
747                         // facing north, shift map north toward edge
748                         x = (place_w(Place) - map->w) / 2;
749                         y = (place_h(Place)) / 2;
750                 }
751                 else if (pinfo->dy > 0) {
752                         // facing south, shift map south toward edge
753                         x = (place_w(Place) - map->w) / 2;
754                         y = (place_h(Place)) / 2 - map->h;
755                 }
756         }
757         else {
758                 terrain_map_rotate(map, vector_to_rotation(pinfo->dx, 
759                                                            pinfo->dy));
760                 // center the overlayed map
761                 x = (place_w(Place) - map->w) / 2;
762                 y = (place_h(Place) - map->h) / 2;
763         }
764
765         assert(x >= 0);
766         assert(y >= 0);
767
768         // Adjust the party's starting position to be centered on the overlap
769         // map.
770         set_party_initial_position(pinfo, 
771                                    x + (map->w /*+ 1*/) / 2, 
772                                    y + (map->h /*+ 1*/) / 2);
773
774         // Blit the rotated map centered on the given coordinates.
775         terrain_map_blit(Place->terrain_map, x, y, map, 0, 0, map->w, map->h);
776         // terrain_map_print(stdout, INITIAL_INDENTATION, Place->terrain_map);
777
778         // Cleanup.
779         terrain_map_unref(map);
780 }
781
782 static void myPutEnemy(class Party * foe, struct position_info *pinfo)
783 {
784         foe->forEachMember(myPutNpc, pinfo);
785 }
786
787 /*
788  * combat_position_enemy - put an NPC party on the combat map
789  */
790 static int combat_position_enemy(class Party * foe, int dx, int dy, 
791                                  int defend, 
792                                  struct place *place)
793 {
794         int positioned = 0;
795
796         assert(foe->getSize());
797
798         combat_fill_position_info(&foe->pinfo, place, foe->getX(), foe->getY(),
799                                   dx, dy, defend);
800         foe->pinfo.formation = foe->get_formation();
801         if (!foe->pinfo.formation)
802                 foe->pinfo.formation = formation_get_default();
803
804         Combat.enemy_vehicle = foe->getVehicle();
805
806         /* Bugfix SF1412060 "NPC attacks while on (but not aboard) ship" */
807         /* Addendum: the foe may not have a place if it is being introduced
808          * into combat "from scratch" (ie, it did not exist on the wilderness
809          * as a party before being imported into combat). */
810         if (! Combat.enemy_vehicle && foe->getPlace()) {
811                 Combat.enemy_vehicle = place_get_vehicle(foe->getPlace(),
812                                                          foe->getX(),
813                                                          foe->getY());
814         }
815         /* Check for a map overlay. */
816         if (Combat.enemy_vehicle
817             && Combat.enemy_vehicle->getObjectType()->renderCombat 
818             && Place == Combat.place) {
819                 combat_overlay_vehicle(Combat.enemy_vehicle, 
820                                        defend ? -dx : dx, 
821                                        defend ? -dy : dy,
822                                        &foe->pinfo);
823           }
824
825         foe->disembark();
826         obj_inc_ref(foe);
827         foe->remove();
828         myPutEnemy(foe, &foe->pinfo);
829         positioned = foe->pinfo.placed;
830
831         obj_dec_ref(foe);
832
833         return positioned;
834 }
835
836
837 /*****************************************************************************
838  * combat_npc_status_visitor - determine the status of the npc faction(s)
839  *
840  * This is a "visitor" function applied to each object in a list of nodes. It's
841  * meant to be applied to every object in a place. It checks for any npc
842  * party members and whether or not they're charmed, and sets the combat status
843  * of the npc faction.
844  *
845  * This routine does not distinguish between different NPC factions, it just
846  * looks for anybody hostile to the player.
847  *
848  *****************************************************************************/
849 static void combat_hostile_status_visitor(struct node *node, void *data)
850 {
851         enum combat_faction_status *status;
852         class Object *obj;
853
854         /* Extract the typed variables from the generic parms */
855         status = (enum combat_faction_status *)data;
856         obj = (class Object*)node->ptr;
857
858         /* If we already know a hostile faction exists then skip the rest */
859         if (*status == COMBAT_FACTION_EXISTS)
860                 return;
861
862         /* Skip non-beings */
863         if (! obj_is_being(obj))
864                 return;
865
866         /* Skip player party members */
867         if (obj->isPlayerPartyMember())
868                 return;
869
870         /* A hostile npc means a hostile faction still exists */
871         if (are_hostile((Being*)obj, player_party)) {
872                 *status = COMBAT_FACTION_EXISTS;
873                 return;
874         }
875
876         /* Check for a charmed hostile */
877         if (are_natively_hostile((Being*)obj, player_party)) {
878                 *status = COMBAT_FACTION_CHARMED;
879         }
880 }
881
882 /*****************************************************************************
883  * combat_get_hostile_faction_status - check hostile combat status
884  *
885  * This is literally a dupe of combat_get_player_faction_status() below.
886  *
887  *****************************************************************************/
888 static enum combat_faction_status combat_get_hostile_faction_status(void)
889 {
890         enum combat_faction_status stat;
891
892         /* Assume until proven otherwise that the player faction is gone. */
893         stat = COMBAT_FACTION_GONE;
894
895         /* Check each object in the current place to determine the status of
896          * the player faction. */
897         node_foldr(place_get_all_objects(Place),
898                    combat_hostile_status_visitor,
899                    &stat);
900
901         /* Return the discovered status. */
902         return stat;
903 }
904
905 /*****************************************************************************
906  * combat_player_status_visitor - determine the status of the player faction
907  *
908  * This is a "visitor" function applied to each object in a list of nodes. It's
909  * meant to be applied to every object in a place. It checks for any player
910  * party members and whether or not they're charmed, and sets the combat status
911  * of the player party based on the cumulative results.
912  *
913  *****************************************************************************/
914 static void combat_player_status_visitor(struct node *node, void *data)
915 {
916         enum combat_faction_status *status;
917         class Object *obj;
918
919         /* Extract the typed variables from the generic parms */
920         status = (enum combat_faction_status *)data;
921         obj = (class Object*)node->ptr;
922
923         /* If we already know the player is still fighting then skip the rest
924          * of this. */
925         if (*status == COMBAT_FACTION_EXISTS)
926                 return;
927
928         /* Skip non-beings */
929         if (! obj_is_being(obj))
930                 return;
931
932         /* Skip non-party-members */
933         if (! obj->isPlayerPartyMember())
934                 return;
935
936         /* A non-hostile party member means the player is still fighting. */
937         if (! are_hostile((Being*)obj, player_party)) {
938                 *status = COMBAT_FACTION_EXISTS;
939                 return;
940         }
941
942         /* Check if a player party members has been charmed */
943         if (are_natively_hostile((Being*)obj, player_party)) {
944                 *status = COMBAT_FACTION_CHARMED;
945         }
946 }
947
948 /*****************************************************************************
949  * combat_get_player_faction_status - check player combat status
950  *
951  * Loops over all objects to check if any player party members are still around
952  * and whether or not they're charmed.
953  *
954  * FIXME: why not just check the player party directly? Or at least merge this
955  * with the combat_get_hostile_faction_status() function above?
956  *
957  *****************************************************************************/
958 static enum combat_faction_status combat_get_player_faction_status(void)
959 {
960         enum combat_faction_status stat;
961
962         /* Assume until proven otherwise that the player faction is gone. */
963         stat = COMBAT_FACTION_GONE;
964
965         /* Check each object in the current place to determine the status of
966          * the player faction. */
967         node_foldr(place_get_all_objects(Place),
968                    combat_player_status_visitor,
969                    &stat);
970
971         /* Return the discovered status. */
972         return stat;
973 }
974
975 void combat_analyze_results_of_last_turn()
976 {
977
978         enum combat_faction_status hostile_faction_status;
979         enum combat_faction_status player_faction_status;
980
981         // ---------------------------------------------------------------------
982         // Now check for changes in the combat state as a result of the last
983         // turn. Check the status of the hostile party or parties and the
984         // player party. The following table shows the outcome with all
985         // possible combinations of status:
986         //
987         // =====================================================
988         // hostiles | player party | result
989         // ===================================================== 
990         // exist    | exist        | continue combat
991         // exist    | gone         | exit combat
992         // exist    | charmed      | uncharm, continue combat
993         // gone     | exist        | looting
994         // gone     | gone         | exit combat
995         // gone     | charmed      | uncharm, looting
996         // charmed  | exist        | continue combat
997         // charmed  | gone         | exit combat
998         // charmed  | charmed      | uncharm, continue combat
999         // =====================================================
1000         //
1001         // ---------------------------------------------------------------------
1002         
1003         hostile_faction_status = combat_get_hostile_faction_status();
1004         player_faction_status  = combat_get_player_faction_status();
1005                         
1006         switch (player_faction_status) {
1007                 
1008         case COMBAT_FACTION_EXISTS:
1009                 
1010                 switch (hostile_faction_status) {
1011                         
1012                 case COMBAT_FACTION_EXISTS:
1013                         // -----------------------------------------------------
1014                         // Both factions exist. Continue or restart fighting.
1015                         // -----------------------------------------------------                        
1016                         combat_set_state(COMBAT_STATE_FIGHTING);
1017                         break;
1018
1019                 case COMBAT_FACTION_CHARMED:
1020                         // -----------------------------------------------------
1021                         // The hostile faction are all charmed. Tough luck for
1022                         // them. Make sure we are fighting.  Addendum: can't
1023                         // attack them without degrading diplomacy with own
1024                         // faction, so declare combat over. When charm wears
1025                         // off we'll go back to fighting.
1026                         // -----------------------------------------------------
1027                         //combat_set_state(COMBAT_STATE_FIGHTING);
1028                         combat_set_state(COMBAT_STATE_LOOTING);
1029                         break;
1030
1031                 case COMBAT_FACTION_GONE:
1032                         // -----------------------------------------------------
1033                         // No hostiles around. Loot at will.
1034                         // -----------------------------------------------------
1035                         if (Combat.state != COMBAT_STATE_DONE)
1036                         {
1037                                 combat_set_state(COMBAT_STATE_LOOTING);
1038                         }
1039                         break;
1040
1041                 default:
1042                         assert(false);
1043                         break;
1044                 }
1045                 
1046                 break;
1047
1048         case COMBAT_FACTION_CHARMED:
1049
1050                 // -------------------------------------------------------------
1051                 // In all of these cases I uncharm the party members. If I
1052                 // don't, then I'm risking a deadlock situation where the
1053                 // hostiles can't or won't finish off the charmed members, in
1054                 // which case the game gets stuck running all the npc's forever
1055                 // while the player helplessly watches.
1056                 // -------------------------------------------------------------
1057
1058                 player_party->unCharmMembers();
1059
1060                 switch (hostile_faction_status) {
1061                         
1062                 case COMBAT_FACTION_EXISTS:
1063                         combat_set_state(COMBAT_STATE_FIGHTING);
1064                         break;
1065
1066                 case COMBAT_FACTION_GONE:
1067                         // ----------------------------------------------------
1068                         // No hostiles around. Loot at will. Uncharm or we'll
1069                         // definitely deadlock.
1070                         // ----------------------------------------------------
1071                         combat_set_state(COMBAT_STATE_LOOTING);
1072                         break;
1073
1074                 default:
1075                         assert(false);
1076                         break;
1077                 }
1078
1079                 break;
1080
1081         case COMBAT_FACTION_GONE:
1082
1083                 // ------------------------------------------------------------
1084                 // In all of these cases combat is over. Simple. If combat is
1085                 // ocurring in the special combat place then we need to clean
1086                 // it up by calling combat_exit().
1087                 // ------------------------------------------------------------
1088                 
1089                 combat_set_state(COMBAT_STATE_DONE);
1090
1091 //                if (Place == Combat.place)
1092                 combat_exit();
1093
1094                 break;
1095                 
1096         case COMBAT_FACTION_CAMPING:
1097
1098                 switch (hostile_faction_status) {
1099                         
1100                 case COMBAT_FACTION_EXISTS:
1101                         // ----------------------------------------------------
1102                         // Ambush!
1103                         // ----------------------------------------------------                        
1104                         combat_set_state(COMBAT_STATE_FIGHTING);
1105                         break;
1106
1107                 case COMBAT_FACTION_GONE:
1108                         // ----------------------------------------------------
1109                         // No hostiles around. Change nothing.
1110                         // ----------------------------------------------------
1111                         break;
1112
1113                 default:
1114                         assert(false);
1115                         break;
1116                 }
1117
1118                 break;
1119
1120         }
1121         
1122
1123 }
1124
1125 /*
1126  * combat_find_and_position_enemy - if this object is a hostile NPC party then
1127  * place it on the combat map
1128  */
1129 static void combat_find_and_position_enemy(class Object * obj, void *data)
1130 {
1131         struct v2 *info;
1132
1133         if (!obj->isType(PARTY_ID))
1134                 return;
1135
1136         info = (struct v2 *) data;
1137         assert(obj_is_being(obj));
1138         if (are_hostile((Being*)obj, player_party))
1139                 combat_set_state(COMBAT_STATE_FIGHTING);        
1140         combat_position_enemy((class Party *) obj, info->dx, info->dy, false, 
1141                         info->place);
1142 }
1143
1144 int combatInit(void)
1145 {
1146         // This is called once at the beginning of the game
1147         memset(&Combat, 0, sizeof (Combat));
1148
1149         /* Initialize the place to safe defaults */
1150         Combat.state = COMBAT_STATE_DONE;
1151
1152         return 0;
1153 }
1154
1155 void combat_reset_state(void)
1156 {
1157         /* Initialize the place to safe defaults */
1158         fprintf(stderr,"combatreset\n");
1159         Combat.state = COMBAT_STATE_DONE;
1160 }
1161
1162 static void fill_map_half(struct terrain_map *map, int dx, int dy,
1163                           struct terrain *terrain)
1164 {
1165         assert(dx || dy);
1166
1167         if (dx < 0) {
1168                 // facing west, fill east half
1169                 terrain_map_fill(map, map->w / 2, 0, (map->w+1) / 2, map->h,
1170                                  terrain);
1171         }
1172         else if (dx > 0) {
1173                 // facing east, fill west half
1174                 terrain_map_fill(map, 0, 0, map->w / 2, map->h, terrain);
1175         }
1176         else if (dy < 0) {
1177                 // facing north, fill south half
1178                 terrain_map_fill(map, 0, map->h / 2, map->w, (map->h+1) / 2,
1179                                  terrain);
1180         }
1181         else if (dy > 0) {
1182                 // facing south, fill north half
1183                 terrain_map_fill(map, 0, 0, map->w, map->h / 2, terrain);
1184         }
1185 }
1186
1187
1188 static void fill_temporary_terrain_map(struct terrain_map *map,
1189                                        struct place *place, int x, int y,
1190                                        int dx, int dy)
1191 {
1192         struct terrain_map *tile_map;
1193         struct terrain *terrain;
1194         int dst_x = 0, dst_y = 0, src_x = 0, src_y = 0, src_w = 0, src_h = 0;
1195
1196         assert(dx || dy);
1197         assert(!(dx && dy));
1198
1199         if (dx < 0) {
1200
1201                 // facing west, fill east half
1202                 dst_x = map->w / 2;
1203                 dst_y = 0;
1204                 src_x = 0;
1205                 src_y = 0;
1206                 src_w = (map->w + 1) / 2;
1207                 src_h = map->h;
1208
1209         }
1210         else if (dx > 0) {
1211
1212                 // facing east, fill west half
1213                 dst_x = 0;
1214                 dst_y = 0;
1215                 src_x = map->w / 2;
1216                 src_y = 0;
1217                 src_w = (map->w) / 2;
1218                 src_h = map->h;
1219
1220         }
1221         else if (dy < 0) {
1222
1223                 // facing north, fill south half
1224                 dst_x = 0;
1225                 dst_y = map->h / 2;
1226                 src_x = 0;
1227                 src_y = 0;
1228                 src_w = map->w;
1229                 src_h = (map->h + 1) / 2;
1230
1231         }
1232         else if (dy > 0) {
1233
1234                 // facing south, fill north half
1235                 dst_x = 0;
1236                 dst_y = 0;
1237                 src_x = 0;
1238                 src_y = map->h / 2;
1239                 src_w = map->w;
1240                 src_h = (map->h)/ 2;
1241         }
1242
1243         tile_map = place_get_combat_terrain_map(place, x, y);
1244
1245         if (tile_map) {
1246
1247                 // fixme -- instead of crashing at runtime, check for properly
1248                 // sized combat maps at load time (this will require the combat
1249                 // map dimensions to be also specified at load time or at least
1250                 // well-documented for map developers)
1251                 assert(tile_map->w >= (src_x + src_w));
1252                 assert(tile_map->h >= (src_y + src_h));
1253
1254                 // Use the combat map associated with the terrain type.
1255                 terrain_map_blit(map, dst_x, dst_y, tile_map, src_x, src_y,
1256                                  src_w, src_h);
1257         }
1258         else {
1259                 // Fill with the terrain type.
1260                 terrain = place_get_terrain(place, x, y);
1261                 terrain_map_fill(map, dst_x, dst_y, src_w, src_h, terrain);
1262         }
1263 }
1264
1265
1266 static void setup_combat_place_part(struct place *place, 
1267                 struct terrain* our_terrain, struct terrain* other_terrain,
1268                 int dx, int dy, int mapx, int mapy)
1269 {
1270         int dst_x = 0, dst_y = 0;
1271         
1272         if (our_terrain->renderCombat)
1273         {       
1274                 fprintf(stderr,"rc\n");
1275                 if (dx < 0) {
1276                         // facing west, fill east half
1277                         dst_x = COMBAT_MAP_W / 2;
1278                 }
1279                 else if (dx > 0) {
1280                         // facing east, fill west half
1281                         dst_x = COMBAT_MAP_W / 2 - COMBAT_MAP_W;
1282                 }
1283                 else if (dy < 0) {
1284                         // facing north, fill south half
1285                         dst_y = COMBAT_MAP_H / 2;
1286                 }
1287                 else if (dy > 0) {
1288                         // facing south, fill north half
1289                         dst_y = COMBAT_MAP_H / 2 - COMBAT_MAP_H;
1290                 }
1291                 
1292                 closure_exec(our_terrain->renderCombat, "pppdddd", 
1293                                                         place, our_terrain, other_terrain,
1294                                                         dst_x, dst_y, mapx, mapy);
1295         }
1296         else if (dx || dy)
1297         {
1298                 fill_map_half(place->terrain_map, dx, dy, our_terrain); 
1299         }
1300         else
1301         {
1302                 terrain_map_fill(place->terrain_map, 0, 0, COMBAT_MAP_W, COMBAT_MAP_H, our_terrain);
1303         }
1304 }
1305         
1306
1307 static void setup_combat_place(struct place *place, struct combat_info
1308                                                         *info)
1309 {
1310         struct terrain *player_terrain;
1311         struct terrain *npc_terrain;
1312         
1313         // Determine orientation for both parties
1314         // Also get true locations (after parties have moved to go from diagonal to adjacent)
1315         if (!info->move->npc_party)
1316         {
1317                 info->pc_dx = 0;
1318                 info->pc_dy = 0;
1319                 info->pc_x = player_party->getX();
1320                 info->pc_y = player_party->getY();
1321         }
1322         else if (info->defend)
1323         {
1324                 info->pc_dx = -info->move->dx;
1325                 info->pc_dy = -info->move->dy;
1326                 info->pc_x = player_party->getX();
1327                 info->pc_y = player_party->getY();
1328                 info->npc_x = info->pc_x - info->move->dx;
1329                 info->npc_y = info->pc_y - info->move->dy;
1330         }
1331         else
1332         {
1333                 info->pc_dx = info->move->dx;
1334                 info->pc_dy = info->move->dy;
1335                 info->npc_x = info->move->npc_party->getX();
1336                 info->npc_y = info->move->npc_party->getY();
1337                 info->pc_x = info->npc_x - info->move->dx;
1338                 info->pc_y = info->npc_y - info->move->dy;
1339         }
1340         
1341         // get terrains for each
1342         player_terrain=place_get_terrain(player_party->getPlace(), info->pc_x, info->pc_y);
1343         
1344         if (info->move->npc_party)      
1345         {
1346                 npc_terrain=place_get_terrain(info->move->npc_party->getPlace(), info->npc_x, info->npc_y);
1347         }
1348         else
1349         {
1350                 npc_terrain=player_terrain; // some sensible definition makes like easier...
1351         }
1352         
1353         setup_combat_place_part(place, player_terrain, npc_terrain, info->pc_dx, info->pc_dy, info->pc_x, info->pc_y);
1354         if (info->move->npc_party)      
1355         {
1356                 setup_combat_place_part(place, npc_terrain, player_terrain, -info->pc_dx, -info->pc_dy, info->npc_x, info->npc_y);
1357         }
1358         
1359 }
1360
1361
1362 static struct terrain_map *create_temporary_terrain_map(struct combat_info
1363                                                         *info)
1364 {
1365         struct terrain_map *map;
1366         int player_dx, player_dy, npc_dx, npc_dy,pcmap_x,pcmap_y,npcmap_x,npcmap_y;
1367         struct list *elem;
1368
1369         // Create a map derived partially from the enemy's tile and
1370         // partially from the player's tile.
1371
1372         map = terrain_map_new("tmp_combat_map", COMBAT_MAP_W, COMBAT_MAP_H,
1373                         player_party->getPlace()->terrain_map->palette);
1374         assert(map);
1375         
1376         terrain_map_fill(map, 0, 0, COMBAT_MAP_W, COMBAT_MAP_H, 
1377                         place_get_terrain(player_party->getPlace(),player_party->getX(),player_party->getY()));
1378         return map;
1379         
1380         // Determine orientation for both parties.
1381
1382         
1383         if (info->defend) {
1384                 player_dx = -info->move->dx;
1385                 player_dy = -info->move->dy;
1386                 npc_dx = info->move->dx;
1387                 npc_dy = info->move->dy;
1388                 pcmap_x = player_party->getX();
1389                 pcmap_y = player_party->getY();
1390                 npcmap_x = pcmap_x - info->move->dx;
1391                 npcmap_y = pcmap_y - info->move->dy;
1392         }
1393         else {
1394                 player_dx = info->move->dx;
1395                 player_dy = info->move->dy;
1396                 npc_dx = -info->move->dx;
1397                 npc_dy = -info->move->dy;
1398                 npcmap_x = info->move->npc_party->getX();
1399                 npcmap_y = info->move->npc_party->getY();
1400                 pcmap_x = npcmap_x - info->move->dx;
1401                 pcmap_y = npcmap_y - info->move->dy;
1402         }
1403
1404         // Fill the player's half of the combat map
1405         fill_temporary_terrain_map(map,
1406                                    player_party->getPlace(),
1407                                    pcmap_x, pcmap_y,
1408                                    player_dx, player_dy);
1409
1410         // Fill the npc party's half of the combat map
1411         fill_temporary_terrain_map(map,
1412                                    info->move->npc_party->getPlace(),
1413                                    npcmap_x, npcmap_y,
1414                                    npc_dx, npc_dy);
1415
1416         struct terrain_map *party_map =
1417             place_get_combat_terrain_map(player_party->getPlace(),
1418                                          player_party->getX(),
1419                                          player_party->getY());
1420         struct terrain_map *npc_party_map =
1421             place_get_combat_terrain_map(info->move->npc_party->getPlace(),
1422                                          info->move->npc_party->getX(),
1423                                          info->move->npc_party->getY());
1424
1425         // SAM: It seems that, until we can think of something 
1426         //      more clever, (palette merging code?  yech...)
1427         //      that all combat maps will have to have a palette
1428         //      in common.  It may be more restrictive than I have
1429         //      stated, but I would need to research the question...
1430         // 
1431         // SAM:
1432         // Hmmm...ship-to-shore blits a map with a different palette 
1433         // onto a map with 'pal_standard', and this code has no way of seeing that.
1434         // Possibly the map blitting should merge the palettes after all...
1435         if (party_map && npc_party_map) {
1436                 dbg("maps '%s' '%s', palettes '%s' '%s'\n",
1437                        party_map->tag, npc_party_map->tag,
1438                        party_map->palette->tag, npc_party_map->palette->tag);
1439                 int palette_tags_match = !strcmp(party_map->palette->tag,
1440                                                  npc_party_map->palette->tag);
1441                 if (!palette_tags_match) {
1442                         dbg("create_temporary_terrain_map() warning: \n"
1443                                "  Two combat maps (tags '%s' and '%s') \n"
1444                                "  merging with dissimilar palettes (tags '%s' and '%s').\n"
1445                                "  (This should work OK now, but be aware...)\n",
1446                                party_map->tag, npc_party_map->tag,
1447                                party_map->palette->tag,
1448                                npc_party_map->palette->tag);
1449                         // SAM: New code in palette_print() should enable us to carry on.
1450                         // assert(0);
1451                 }
1452         }
1453         struct terrain_map *the_map;
1454         if (party_map)
1455                 the_map = party_map;
1456         else if (npc_party_map)
1457                 the_map = npc_party_map;
1458         else {
1459                 // No combat map for either location?
1460                 // Use the parent map (wilderness, or whatever) to get a palette.
1461                 // Since the terrain fill for each half is based on 
1462                 // some terrain in the parent map, that palette should be appropriate.
1463                 assert(player_party->getPlace()->terrain_map);
1464                 the_map = player_party->getPlace()->terrain_map;
1465         }
1466         map->palette = the_map->palette;
1467         //terrain_map_print(stdout, INITIAL_INDENTATION, map);
1468
1469
1470         /* run all registered terrain blenders on the new map */
1471         list_for_each(&Session->blenders, elem) {
1472                 blender_t *blender=outcast(elem, blender_t, list);
1473                 terrain_map_blend(map, blender->inf, blender->n_nonsup,
1474                                   blender->nonsup, blender->range);
1475         }
1476
1477         return map;
1478 }
1479
1480 static bool position_player_party(struct combat_info *cinfo)
1481 {
1482         class Vehicle *vehicle;
1483
1484         combat_fill_position_info(&player_party->pinfo, Place,
1485                                   cinfo->move->x, cinfo->move->y,
1486                                   cinfo->move->dx, cinfo->move->dy, 
1487                                   cinfo->defend);
1488
1489         player_party->pinfo.formation = player_party->get_formation();
1490         if (!player_party->pinfo.formation)
1491                 player_party->pinfo.formation = formation_get_default();
1492
1493         // Check for map overlays. First check if the player is in a vehicle
1494         // with a map.
1495         vehicle = player_party->getVehicle();
1496         if (vehicle &&
1497             vehicle->getObjectType()->renderCombat &&
1498             Place == Combat.place) {
1499                 combat_overlay_vehicle(vehicle, cinfo->pc_dx, cinfo->pc_dy,
1500                                    &player_party->pinfo);
1501         }
1502
1503         // Next check if the player is OVER (on the map) but not in a vehicle
1504         // on the map. Note: this only applies to non-dungeon combat, and in a
1505         // series of dungeon combats the player party may not have a place
1506         // (because we remove it just below and the calling code does not
1507         // relocate the player party until it returns to a town or wilderness.
1508         else if (player_party->getPlace() &&
1509                  (vehicle = place_get_vehicle(player_party->getPlace(),
1510                                               player_party->getX(),
1511                                               player_party->getY())) &&
1512                  vehicle->getObjectType()->renderCombat) {
1513                 // dbg("party overlay, party over vehicle\n");
1514                combat_overlay_vehicle(vehicle, cinfo->pc_dx, cinfo->pc_dy,
1515                                    &player_party->pinfo);
1516         }
1517         // Finally, since there is no vehicle map check for a camping map.
1518         else if (cinfo->camping && player_party->campsite_map) {
1519                 // dbg("party overlay, party is camping\n");
1520                 combat_overlay_map(player_party->campsite_map, 
1521                                    &player_party->pinfo, 0);
1522         }
1523
1524         if (player_party->isLoitering())
1525         {
1526                         player_party->forceAbortLoitering();   
1527           }
1528         player_party->remove();
1529         player_party->forEachReverseMember(combat_place_character, 
1530                                            &player_party->pinfo);
1531                                            
1532         return true;
1533 }
1534
1535 bool combat_enter(struct combat_info * info)
1536 {
1537         struct location loc;
1538
1539         if (player_party->allDead()) {
1540                 /* Yes, this can happen in some rare circumstances... */
1541                 return false;
1542         }
1543
1544         /* Our map-building code assumes 4-neighbor adjacency. If attacking on
1545          * a diagonal, randomly choose a cardinal direction. */
1546         if (info->move->dx && info->move->dy)
1547         {
1548                 if (rand() % 2) {
1549                         info->move->dx = 0;
1550                 } else {
1551                         info->move->dy = 0;
1552                 }
1553         }
1554
1555         // --------------------------------------------------------------------
1556         // Default to the entry point as the combat exit location for the
1557         // player party.
1558         // --------------------------------------------------------------------
1559
1560         loc.place = info->move->place;
1561         loc.x     = info->move->x;
1562         loc.y     = info->move->y;
1563
1564
1565         // *** Initialize Combat Globals ***
1566
1567         Combat.enemy_vehicle = NULL;
1568         Combat.round = 0;
1569         Session->crosshair->remove();
1570
1571         if (! info->move->place->wilderness) {
1572
1573                 // ------------------------------------------------------------
1574                 // When not in the wilderness use the current place for combat.
1575                 // ------------------------------------------------------------
1576                 Place = info->move->place;
1577         }
1578         else {
1579
1580                 // ------------------------------------------------------------
1581                 // Create a temporary place for combat in the wilderness. It's
1582                 // parent will be the wilderness. We have to set a special flag
1583                 // to indicate that it's wilderness combat (used for things
1584                 // like exit policy).
1585                 // ------------------------------------------------------------
1586
1587                 Combat.place = place_new("p_wilderness_combat", 
1588                                          "Wilderness Combat",
1589                                          0, // sprite
1590                                          create_temporary_terrain_map(info),
1591                                          0, // ! wrapping
1592                                          info->move->place->underground, 
1593                                          0, // ! wilderness
1594                                          1  // wilderness combat
1595                                          );
1596
1597                 Combat.place->is_wilderness_combat = 1;
1598                 Combat.place->location.place = info->move->place;
1599                 Combat.place->location.x = info->move->px;
1600                 Combat.place->location.y = info->move->py;
1601
1602                 setup_combat_place(Combat.place,info);
1603                 
1604                 place_add_subplace(info->move->place, Combat.place, 
1605                                    info->move->px, info->move->py);
1606
1607                 Place = Combat.place;
1608         }
1609
1610         mapSetPlace(Place);
1611
1612         // *** Position the Player Companions ***
1613
1614         // This is where the map overlays on the player side of the map get
1615         // placed, if any.
1616         if (!position_player_party(info))
1617                 return false;
1618                
1619         // *** Position the Enemy Party Members ***
1620
1621         if (info->move->npc_party) {
1622                 
1623
1624                 /* combat_position_enemy() will decrement most of the refcounts
1625                  * ont he party, keep it alive until we're done */
1626                 obj_inc_ref(info->move->npc_party);
1627
1628                 if (!combat_position_enemy(info->move->npc_party, 
1629                                            info->move->dx, info->move->dy, 
1630                                            !info->defend, Place)) {
1631                         log_begin("*** ÁÓ¼º ***");
1632                         log_msg("Áê¼ê¤ò¸«¼º¤Ã¤¿¡ª");
1633                         log_end(NULL);
1634                         combat_set_state(COMBAT_STATE_LOOTING);
1635                 }
1636                 else
1637                 {
1638                         combat_set_state(COMBAT_STATE_FIGHTING);   
1639                  }
1640
1641                 /* done with it now */
1642                 obj_dec_ref(info->move->npc_party);
1643                 info->move->npc_party = NULL;
1644
1645         }
1646         else if (info->camping) {
1647
1648                 combat_set_state(COMBAT_STATE_CAMPING);
1649                 log_msg("Zzzz...");
1650         }
1651         else {
1652                 struct v2 v2;
1653                 v2.dx = info->move->dx;
1654                 v2.dy = info->move->dy;
1655                 v2.place = Place;
1656                 combat_set_state(COMBAT_STATE_DONE);
1657                 place_for_each_object(Place, combat_find_and_position_enemy,
1658                                       &v2);
1659         }
1660
1661         player_party->forEachMember(mySetInitialCameraPosition, 0);
1662
1663         if (combat_get_state() == COMBAT_STATE_FIGHTING) {
1664                 player_party->enableRoundRobinMode();
1665                 sound_play(Combat.sound_enter, SOUND_MAX_VOLUME);
1666         }
1667         else if (combat_get_state() != COMBAT_STATE_CAMPING) {
1668                 player_party->enableFollowMode();
1669         }
1670
1671         // ---------------------------------------------------------------------
1672         // Force a map update. If an npc initiates combat then the event
1673         // handler will not run and do a repaint until the next event.
1674         // ---------------------------------------------------------------------
1675
1676         mapUpdate(0);
1677         foogodRepaint();
1678
1679         // ---------------------------------------------------------------------
1680         // Return to the main loop. Combat will continue from there.
1681         // ---------------------------------------------------------------------
1682         return true;
1683 }
1684
1685 char combatGetState(void)
1686 {
1687         switch (Combat.state) {
1688         case COMBAT_STATE_FIGHTING:
1689                 return 'Y';
1690                 break;
1691         case COMBAT_STATE_DONE:
1692                 return 'N';
1693                 break;
1694         case COMBAT_STATE_LOOTING:
1695                 return 'V';
1696                 break;
1697         case COMBAT_STATE_CAMPING:
1698                 return 'K';
1699                 break;
1700         }
1701
1702         return 'N';
1703 }
1704
1705 int combat_add_party(class Party * party, int dx, int dy, int located,
1706                      struct place *place, int x, int y)
1707 {
1708         int added = 0;
1709
1710         obj_inc_ref(party);
1711
1712         if (!located) {
1713                 // Caller has not specified a location so use the normal
1714                 // procedure.
1715                 added = combat_position_enemy(party, dx, dy, false, place);
1716                 obj_dec_ref(party);
1717                 return added;
1718         }
1719
1720         // Special case: caller wants to put the party at (x, y). Duplicate the
1721         // code in combat_position_enemy except fill out the position info
1722         // based on caller's request.
1723         party->disembark();
1724         party->remove();
1725
1726         memset(&party->pinfo, 0, sizeof (party->pinfo));
1727         party->pinfo.place = place;
1728         party->pinfo.x = x;
1729         party->pinfo.y = y;
1730         party->pinfo.dx = dx;
1731         party->pinfo.dy = dy;
1732         party->pinfo.formation = party->get_formation();
1733         party->pinfo.find_party = true;
1734         if (!party->pinfo.formation)
1735                 party->pinfo.formation = formation_get_default();
1736         set_party_initial_position(&party->pinfo, x, y);
1737         myPutEnemy(party, &party->pinfo);
1738
1739         added = party->pinfo.placed;
1740
1741         obj_dec_ref(party);
1742
1743         return added;
1744
1745 }
1746
1747 void combat_exit(void)
1748 {
1749         struct place *parent = 0;
1750         int x, y;
1751
1752         // Clean up the temporary combat place, if we used one. If we started
1753         // off in combat when we loaded this session then we need to remove
1754         // the temp place and temp map from the session's list of objects
1755         // to save. We can tell that we need to remove them if they have
1756         // non-null handles (those handles are only set by the loader, if
1757         // we create the tmp place during normal game play we don't set them).
1758         //
1759         // Before destroying the place I have to memorize it's location for the
1760         // next step.
1761         if (Place->is_wilderness_combat) {
1762
1763                 //assert(! Place->handle); // should not be top-level
1764                 assert(place_get_parent(Place));
1765
1766                 if (Place->terrain_map->handle)
1767                         session_rm(Session, Place->terrain_map->handle);
1768
1769                 place_remove_and_destroy_all_objects(Place);
1770                 parent = place_get_parent(Place);
1771                 x = place_get_x(Place);
1772                 y = place_get_y(Place);
1773                 place_remove_subplace(parent, Place);
1774
1775                 // Bugfix: Invalidate the entire map from the vmask cache. If
1776                 // you don't do this, then the next time the player enters
1777                 // combat and we start looking up vmasks we will find old,
1778                 // stale ones from this place, resulting in LOS bugs. That's
1779                 // because the keys used by the vmask are built from the name
1780                 // of the place, and for the combat map it is always the same
1781                 // name.
1782                 vmask_invalidate(Place, 0, 0, place_w(Place), place_h(Place));
1783
1784                 // If this place has a handle it's in the session list and
1785                 // needs to be removed before we destroy it, otherwise on
1786                 // session reload it will be deleted again.
1787                 if (Place->handle)
1788                         session_rm(Session, Place->handle);
1789
1790                 place_del(Place); // map deleted in here
1791                 Combat.place = 0;
1792
1793                 // ------------------------------------------------------------
1794                 // Relocate the player party back to the wilderness. This will
1795                 // handle the place switch implicitly by setting the global
1796                 // 'Place' pointer to the wilderness.
1797                 // ------------------------------------------------------------
1798
1799                 player_party->relocate(parent, x, y);                
1800
1801         }
1802
1803         assert(NULL != player_party->getPlace());
1804
1805         // --------------------------------------------------------------------
1806         // Force a map update. Although the map has been marked dirty by now,
1807         // we will not see a repaint until the next event if we do not act
1808         // now. This routine is called from the context of the main play loop,
1809         // not the event loop.
1810         // --------------------------------------------------------------------
1811
1812         //mapUpdate(0);
1813         
1814 }