Adonthell  0.4
mapcharacter.cc
Go to the documentation of this file.
1 /*
2  $Id: mapcharacter.cc,v 1.49 2003/02/17 19:31:21 ksterker Exp $
3 
4  Copyright (C) 1999/2000/2001/2002 Alexandre Courbot
5  Part of the Adonthell Project http://adonthell.linuxgames.com
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file mapcharacter.cc
17  *
18  * @author Alexandre Courbot <alexandrecourbot@linuxgames.com>
19  * @brief Defines the mapcharacter class.
20  */
21 
22 #include "mapcharacter.h"
23 #include "map_event.h"
24 #include "time_event.h"
25 #include "event_handler.h"
26 #include "landmap.h"
27 #include "win_manager.h"
28 
29 using namespace std;
30 
31 
32 // Public methods
34 {
35  submap_ = posx_ = posy_ = offx_ = offy_ = 0;
36  refmap = NULL;
37  anim.resize (NBR_MOVES);
38  for (u_int16 i = 0; i < NBR_MOVES; i++)
39  anim[i] = new animation;
40  current_move = STAND_NORTH;
41  previous_move = NO_MOVE;
42 
43  saying = NULL;
44 
45  schedule_activated = true;
46  action_activated = true;
47  goal_reached_ = true;
48 
49  schedule_args = NULL;
50  action_args = NULL;
51 
52  callback = NULL;
53 }
54 
56 {
57  clear ();
58  for (u_int16 i = 0; i < anim.size (); i++)
59  delete anim[i];
60  anim.clear ();
61 }
62 
64 {
66 
67  if (saying) delete saying;
68 
69  for (u_int16 i = 0; i < anim.size (); i++)
70  anim[i]->clear ();
71 
73 
74  schedule.clear ();
75  action.clear ();
76 
77  filename_ = "";
78 
79  Py_XDECREF (schedule_args);
80  schedule_args = NULL;
81 
82  Py_XDECREF (action_args);
83  action_args = NULL;
84  schedule_file_ = "";
85  action_file_ = "";
86 
87  if (callback) delete callback;
88 }
89 
91 {
92  int i;
93 
94  for (i = 0; i < NBR_MOVES; i++)
95  {
96  anim[i]->get (file);
97  anim[i]->stop ();
98  }
99 
101 
102  return 0;
103 }
104 
106 {
107  string s = MAPCHAR_DIR;
108 
109  s += fname;
110  igzstream file (s);
111  if (!file.is_open ())
112  return -1;
113 
114  s_int8 retvalue;
115  if (fileops::get_version (file, 1, 1, s))
116  retvalue = get (file);
117  file.close ();
118  filename_ = fname;
119 
120  return 0;
121 }
122 
124 {
125  int i;
126 
127  for (i = 0; i < NBR_MOVES; i++)
128  {
129  anim[i]->put (file);
130  }
131 
133 
134  return 0;
135 }
136 
137 s_int8 mapcharacter::save (string fname) const
138 {
139  string s = MAPCHAR_DIR;
140 
141  s += fname;
142  ogzstream file (s);
143  if (!file.is_open ())
144  return -1;
145 
146  s_int8 retvalue;
147  fileops::put_version (file, 1);
148  retvalue = put (file);
149  file.close ();
150 
151  return 0;
152 }
153 
155 {
156  string t;
157  bool b;
158  u_int16 current_move__;
159  s_int8 offx__, offy__;
160 
161  remove_from_pos ();
162 
163  t << file;
164  load (t);
165 
166  // Reads the data members
167  current_move__ << file;
168  previous_move << file;
169  submap_ << file;
170  posx_ << file;
171  posy_ << file;
172  offx__ << file;
173  offy__ << file;
174 
175  jump_to (submap (), posx (), posy ());
176  set_offset (offx__, offy__);
177 
178  current_move = current_move__;
179 
180  // Get the path state
181  mypath.get_state (file);
182  // The map must be attached manually for now! :(
183  mypath.refmap = refmap;
184 
185  pathindex << file;
186  goal_reached_ << file;
187 
188  // Schedule state
189  PyObject * args;
190  t << file;
191  b << file;
192  args = NULL;
193  if (b) args = python::get_tuple (file);
194  set_schedule (t, args);
195  Py_XDECREF (args);
196  b << file;
198 
199  // Action state
200  t << file;
201  b << file;
202  args = NULL;
203  if (b) args = python::get_tuple (file);
204  set_action (t, args);
205  Py_XDECREF (args);
206  b << file;
207  set_action_active (b);
208 
209  // get the events
210  py_callback::instance = schedule.get_instance (false);
211  return event_list::get_state (file);
212 }
213 
215 {
216  // Write the mapcharacter's file name
217  filename_ >> file;
218 
219  // Write the data members
220  current_move >> file;
221  previous_move >> file;
222  submap_ >> file;
223  posx_ >> file;
224  posy_ >> file;
225  offx_ >> file;
226  offy_ >> file;
227 
228  // Save the path state
229  mypath.put_state (file);
230  pathindex >> file;
231  goal_reached_ >> file;
232 
233  // Save the schedule script state
234  schedule_file () >> file;
235  if (schedule_args)
236  {
237  true >> file;
238  python::put_tuple (schedule_args, file);
239  }
240  else false >> file;
241  is_schedule_activated () >> file;
242 
243  // Save the action script state
244  action_file () >> file;
245  if (action_args)
246  {
247  true >> file;
248  python::put_tuple (action_args, file);
249  }
250  else false >> file;
251  is_action_activated () >> file;
252 
253  // save the events
254  event_list::put_state (file);
255 
256  return 0;
257 }
258 
260 {
261  if (mymap ()) return;
262 
263  m->mapchar.push_back (this);
264 
265  refmap = m;
266 }
267 
269 {
270  if (!mymap ()) return;
271 
272  leave_position ();
273 
274  vector <mapcharacter *>::iterator i;
275  for (i = mymap ()->mapchar.begin (); (*i) != this; i++);
276  mymap ()->mapchar.erase (i);
277 
278  refmap = NULL;
279 }
280 
282 {
283  leave_position ();
284 }
285 
287  u_int16 pos)
288 {
289  leave_position ();
290  set_pos (smap, x, y);
291  set_offset (0, 0);
292 
293  switch (pos)
294  {
295  case STAND_NORTH:
296  stand_north ();
297  break;
298  case STAND_SOUTH:
299  stand_south ();
300  break;
301  case STAND_WEST:
302  stand_west ();
303  break;
304  case STAND_EAST:
305  stand_east ();
306  break;
307  default:
308  stand ();
309  break;
310  }
311 
312  enter_event evt;
313  evt.submap = submap ();
314  evt.x = posx ();
315  evt.y = posy ();
316  evt.c = this;
317  evt.dir = pos;
319 }
320 
322 {
323  if (current_move >= WALK_NORTH && current_move != NO_MOVE)
324  {
325  previous_move = current_move;
326  current_move -= WALK_NORTH;
327  }
328 }
329 
331 {
332  previous_move = current_move;
333  current_move = STAND_NORTH;
334 }
335 
337 {
338  previous_move = current_move;
339  current_move = STAND_SOUTH;
340 }
341 
343 {
344  previous_move = current_move;
345  current_move = STAND_EAST;
346 }
347 
349 {
350  previous_move = current_move;
351  current_move = STAND_WEST;
352 }
353 
355 {
356  if (!posy ())
357  return false;
358  u_int16 i, j;
359  u_int16 sx = (posx () - base_x () < 0) ? 0 : posx () - base_x ();
360  u_int16 sy = (posy () - base_y () < 0) ? 0 : posy () - base_y ();
361  s_int16 ax = sx - (posx () - base_x ());
362  s_int16 ay = sy - (posy () - base_y ());
363  u_int16 ex =
364  (posx () - base_x () + area_length () >
365  refmap->submap[submap ()]->area_length ()) ? refmap->submap[submap ()]->area_length ()
366  : posx () - base_x () + area_length ();
367  u_int16 ey =
368  (posy () - base_y () + area_height () >
369  refmap->submap[submap ()]->area_height ()) ? refmap->submap[submap ()]->area_height ()
370  : posy () - base_y () + area_height ();
371 
372  for (j = sy; j < ey; j++)
373  for (i = sx; i < ex; i++)
374  {
375  if (get_square (i - sx + ax, j - sy + ay)->get_walkable ())
376  continue;
377  if (!j)
378  continue;
379  if (!(refmap->submap[submap ()]->area[i][j].is_walkable_north () &&
380  refmap->submap[submap ()]->area[i][j - 1].is_walkable_south ()
381  && refmap->submap[submap ()]->area[i][j - 1].is_free ()))
382  return false;
383  }
384  return true;
385 }
386 
388 {
389  if (posy () == refmap->submap[submap ()]->area_height () - 1)
390  return false;
391  u_int16 i, j;
392  u_int16 sx = (posx () - base_x () < 0) ? 0 : posx () - base_x ();
393  u_int16 sy = (posy () - base_y () < 0) ? 0 : posy () - base_y ();
394  s_int16 ax = sx - (posx () - base_x ());
395  s_int16 ay = sy - (posy () - base_y ());
396  u_int16 ex =
397  (posx () - base_x () + area_length () >=
398  refmap->submap[submap ()]->area_length ()) ? refmap->submap[submap ()]->area_length ()
399  : posx () - base_x () + area_length ();
400  u_int16 ey =
401  (posy () - base_y () + area_height () >=
402  refmap->submap[submap ()]->area_height ()) ? refmap->submap[submap ()]->area_height ()
403  : posy () - base_y () + area_height ();
404 
405  for (j = sy; j < ey; j++)
406  for (i = sx; i < ex; i++)
407  {
408  if (get_square (i - sx + ax, j - sy + ay)->get_walkable ())
409  continue;
410  if (j == refmap->submap[submap ()]->area_height () - 1)
411  continue;
412  if (!(refmap->submap[submap ()]->area[i][j].is_walkable_south () &&
413  refmap->submap[submap ()]->area[i][j +
414  1].is_walkable_north ()
415  && refmap->submap[submap ()]->area[i][j + 1].is_free ()))
416  return false;
417  }
418  return true;
419 }
420 
422 {
423  if (posx () == refmap->submap[submap ()]->area_length () - 1)
424  return false;
425  u_int16 i, j;
426  u_int16 sx = (posx () - base_x () < 0) ? 0 : posx () - base_x ();
427  u_int16 sy = (posy () - base_y () < 0) ? 0 : posy () - base_y ();
428  s_int16 ax = sx - (posx () - base_x ());
429  s_int16 ay = sy - (posy () - base_y ());
430  u_int16 ex =
431  (posx () - base_x () + area_length () >=
432  refmap->submap[submap ()]->area_length ()) ? refmap->submap[submap ()]->area_length ()
433  : posx () - base_x () + area_length ();
434  u_int16 ey =
435  (posy () - base_y () + area_height () >=
436  refmap->submap[submap ()]->area_height ()) ? refmap->submap[submap ()]->area_height ()
437  : posy () - base_y () + area_height ();
438 
439  for (j = sy; j < ey; j++)
440  for (i = sx; i < ex; i++)
441  {
442  if (get_square (i - sx + ax, j - sy + ay)->get_walkable ())
443  continue;
444  if (i == refmap->submap[submap ()]->area_length () - 1)
445  continue;
446  if (!(refmap->submap[submap ()]->area[i][j].is_walkable_east () &&
447  refmap->submap[submap ()]->area[i + 1][j].is_walkable_west ()
448  && refmap->submap[submap ()]->area[i + 1][j].is_free ()))
449  return false;
450  }
451  return true;
452 }
453 
455 {
456  if (!posx ())
457  return false;
458  u_int16 i, j;
459  u_int16 sx = (posx () - base_x () < 0) ? 0 : posx () - base_x ();
460  u_int16 sy = (posy () - base_y () < 0) ? 0 : posy () - base_y ();
461  s_int16 ax = sx - (posx () - base_x ());
462  s_int16 ay = sy - (posy () - base_y ());
463  u_int16 ex =
464  (posx () - base_x () + area_length () >
465  refmap->submap[submap ()]->area_length ()) ? refmap->submap[submap ()]->area_length ()
466  : posx () - base_x () + area_length ();
467  u_int16 ey =
468  (posy () - base_y () + area_height () >
469  refmap->submap[submap ()]->area_height ()) ? refmap->submap[submap ()]->area_height ()
470  : posy () - base_y () + area_height ();
471 
472  for (j = sy; j < ey; j++)
473  for (i = sx; i < ex; i++)
474  {
475  if (get_square (i - sx + ax, j - sy + ay)->get_walkable ())
476  continue;
477  if (!i)
478  continue;
479  if (!(refmap->submap[submap ()]->area[i][j].is_walkable_west () &&
480  refmap->submap[submap ()]->area[i - 1][j].is_walkable_east ()
481  && refmap->submap[submap ()]->area[i - 1][j].is_free ()))
482  return false;
483  }
484  return true;
485 }
486 
488 {
489  if (current_move < WALK_NORTH)
490  {
491  bool ret = can_go_north ();
492  previous_move = current_move;
493  if (ret)
494  current_move = WALK_NORTH;
495  else current_move = STAND_NORTH;
496  return ret;
497  }
498  return false;
499 }
500 
502 {
503  if (current_move < WALK_NORTH)
504  {
505  bool ret = can_go_south ();
506  previous_move = current_move;
507  if (ret)
508  current_move = WALK_SOUTH;
509  else current_move = STAND_SOUTH;
510  return ret;
511  }
512  return false;
513 }
514 
516 {
517  if (current_move < WALK_NORTH)
518  {
519  bool ret = can_go_east ();
520  previous_move = current_move;
521  if (ret)
522  current_move = WALK_EAST;
523  else current_move = STAND_EAST;
524  return ret;
525  }
526  return false;
527 }
528 
530 {
531  if (current_move < WALK_NORTH)
532  {
533  bool ret = can_go_west ();
534  previous_move = current_move;
535  if (ret)
536  current_move = WALK_WEST;
537  else current_move = STAND_WEST;
538  return ret;
539  }
540  return false;
541 }
542 
543 bool mapcharacter::set_goal (u_int16 x, u_int16 y, u_int16 dir)
544 {
545  mypath.refmap = mymap ();
546  mypath.submap = submap ();
547  mypath.start.x = posx ();
548  mypath.start.y = posy ();
549  mypath.goal.x = x;
550  mypath.goal.y = y;
551  mypath.dir = dir;
552  pathindex = 0;
553  goal_reached_ = false;
554 
555  return mypath.calculate ();
556 }
557 
558 void mapcharacter::set_callback (PyObject *cb, PyObject *args)
559 {
560  if (callback) delete callback;
561  callback = new py_callback (cb, args);
562 }
563 
564 void mapcharacter::time_callback (string delay, PyObject *cb, PyObject *args)
565 {
566  time_event *ev = new time_event (delay);
567  ev->set_callback (cb, args);
568  add_event (ev);
569 }
570 
571 void mapcharacter::time_callback_string (string delay, string cb, PyObject *args)
572 {
573  PyObject *instance = schedule.get_instance (false);
574 
575  // check that we have a valid instance that contains our callback
576  if (instance == NULL)
577  {
578  fprintf (stderr, "*** error: mapcharacter::time_callback: Invalid instance!");
579  return;
580  }
581 
582  PyObject *callback = PyObject_GetAttrString (instance, (char *) cb.c_str ());
583 
584  if (!PyCallable_Check (callback))
585  {
586  fprintf (stderr, "*** error: mapcharacter::time_callback: Setting callback ' %s' failed!", cb.c_str ());
587  }
588  else
589  {
590  time_event *ev = new time_event (delay);
591  ev->set_callback (callback, args);
592  add_event (ev);
593  }
594 
595  Py_XDECREF (callback);
596 }
597 
598 bool mapcharacter::follow_path ()
599 {
600  // If a movment is engaged, let it finish first.
601  if (offx () || offy ())
602  return false;
603 
604  // If the goal isn't reached yet.
605  if (pathindex < mypath.nbr_moves ())
606  {
607  u_int16 dir = mypath.get_move (pathindex);
608  u_int8 success = 0;
609 
610  // Try to follow the direction
611  switch (dir)
612  {
613  case WALK_NORTH:
614  if (go_north ()) success = 1;
615  break;
616 
617  case WALK_SOUTH:
618  if (go_south ()) success = 1;
619  break;
620 
621  case WALK_WEST:
622  if (go_west ()) success = 1;
623  break;
624 
625  case WALK_EAST:
626  if (go_east ()) success = 1;
627  break;
628  }
629 
630  // Who the fuck is on my way!!?@! I have to find a new path now!
631  if (!success)
632  {
633  mypath.start.x = posx ();
634  mypath.start.y = posy ();
635  mypath.submap = submap ();
636  mypath.calculate ();
637  pathindex = 0;
638  }
639  else pathindex++;
640  }
641  else
642  {
643  switch (mypath.dir)
644  {
645  case STAND_NORTH:
646  stand_north ();
647  break;
648  case STAND_SOUTH:
649  stand_south ();
650  break;
651  case STAND_WEST:
652  stand_west ();
653  break;
654  case STAND_EAST:
655  stand_east ();
656  break;
657  }
658 
659  // goal reached -> notify script (as the script might immediately
660  // set the next goal, we gotta set goal_reached_ before that)
661  goal_reached_ = true;
662  if (callback) callback->callback_func0 ();
663  return true;
664  }
665  return false;
666 }
667 
668 void mapcharacter::stop_moving ()
669 {
670  set_goal (posx (), posy ());
671 }
672 
673 bool mapcharacter::goal_reached ()
674 {
675  return goal_reached_;
676 }
677 
679 {
680  switch (p)
681  {
682  case STAND_NORTH:
683  stand_south ();
684  break;
685  case STAND_SOUTH:
686  stand_north ();
687  break;
688  case STAND_EAST:
689  stand_west ();
690  break;
691  case STAND_WEST:
692  stand_east ();
693  break;
694  }
695 }
696 
698 {
699  switch (current_move)
700  {
701  case STAND_NORTH:
702  if (posy () == 0)
703  return NULL;
704  return refmap->submap[submap ()]->area[posx ()][posy () - 1].whoshere ();
705  break;
706  case STAND_SOUTH:
707  if (posy () == refmap->submap[submap ()]->area_height () - 1)
708  return NULL;
709  return refmap->submap[submap ()]->area[posx ()][posy () + 1].whoshere ();
710  break;
711  case STAND_WEST:
712  if (posx () == 0)
713  return NULL;
714  return refmap->submap[submap ()]->area[posx () - 1][posy ()].whoshere ();
715  break;
716  case STAND_EAST:
717  if (posx () == refmap->submap[submap ()]->area_length () - 1)
718  return NULL;
719  return refmap->submap[submap ()]->area[posx () + 1][posy ()].whoshere ();
720  break;
721  }
722  return NULL;
723 }
724 
725 bool mapcharacter::do_stuff (string method, PyObject *args)
726 {
727  if (!schedule.has_attribute (method)) return false;
728  else schedule.call_method (method, args);
729 
730  return true;
731 }
732 
733 void mapcharacter::set_schedule (string file, PyObject * args)
734 {
735  // Clears the schedule
736  schedule.clear ();
737  Py_XDECREF (schedule_args);
738  schedule_args = NULL;
739 
740  // Set new schedule
741  if (file != "")
742  {
743  Py_XINCREF (args);
744  schedule_args = args;
745  u_int16 argssize = args == NULL ? 1 : PyTuple_Size (args) + 1;
746  PyObject * theargs;
747 
748  theargs = PyTuple_New (argssize);
749 
750  // We can pass_instance directly 'cause PyTuple_SetItem steals a
751  // reference to the result of pass_instance.
752  PyTuple_SetItem (theargs, 0, python::pass_instance (this, "mapcharacter"));
753  for (u_int16 i = 1; i < argssize; i++)
754  {
755  PyObject * intref = PyTuple_GetItem (args, i - 1);
756  Py_INCREF (intref);
757  PyTuple_SetItem (theargs, i, intref);
758  }
759  schedule.create_instance ("schedules.mapcharacters." + file, file, theargs);
760  Py_DECREF (theargs);
761 
762  if (!schedule.has_attribute ("run"))
763  set_schedule_active (false);
764  }
765  schedule_file_ = file;
766 }
767 
768 void mapcharacter::set_action (string file, PyObject * args)
769 {
770  // Clears the action script
771  action.clear ();
772  Py_XDECREF (action_args);
773  action_args = NULL;
774 
775  if (file != "")
776  {
777  Py_XINCREF (args);
778  action_args = args;
779  u_int16 argssize = args == NULL ? 1 : PyTuple_Size (args) + 1;
780  PyObject * theargs;
781 
782  theargs = PyTuple_New (argssize);
783 
784  // We can pass_instance directly 'cause PyTuple_SetItem steals a
785  // reference to the result of pass_instance.
786  PyTuple_SetItem (theargs, 0, python::pass_instance (this, "mapcharacter"));
787  for (u_int16 i = 1; i < argssize; i++)
788  {
789  PyObject * intref = PyTuple_GetItem (args, i - 1);
790  Py_INCREF (intref);
791  PyTuple_SetItem (theargs, i, intref);
792  }
793  action.create_instance ("actions." + file, file, theargs);
794  Py_DECREF (theargs);
795  }
796  action_file_ = file;
797 }
798 
800 {
801  update_move ();
802 
803  if (is_schedule_activated ())
804  schedule.run ();
805 
806  // if we have a goal, then go there!
807  if (!goal_reached ())
808  follow_path ();
809 
810  if (previous_move != NO_MOVE && previous_move != current_move)
811  {
812  anim[previous_move]->stop ();
813  anim[previous_move]->rewind ();
814  anim[current_move]->play ();
815  }
816 
817  if (saying && !saying->update ())
818  {
819  delete saying;
820  saying = NULL;
821  }
822 
823  return true;
824 }
825 
827 {
828  PyObject *args = PyTuple_New (1);
829  PyTuple_SetItem (args, 0, python::pass_instance (requester, "mapcharacter"));
830  if (is_action_activated ()) action.run (args);
831  Py_DECREF (args);
832 }
833 
834 void mapcharacter::draw (s_int16 x, s_int16 y, const drawing_area * da_opt, surface * target) const
835 {
836  anim[current_move]->draw (x, y, da_opt, target);
837 }
838 
839 void mapcharacter::draw_bubble (s_int16 x, s_int16 y, const drawing_area * da_opt,
840  surface * target) const
841 {
842  if (saying)
843  {
844  s_int16 dx = x - (saying->drawing_area::length () >> 1) + (anim[current_move]->length () >> 1);
845  s_int16 dy = y - (saying->drawing_area::height ()) + 5;
846 
847  if (dx < 4) dx = 4;
848  else if (dx + saying->drawing_area::length () > da_opt->x () + da_opt->length () - 4)
849  dx = da_opt->x () + da_opt->length () - saying->drawing_area::length () - 4;
850 
851  saying->move (dx, dy);
852  saying->assign_drawing_area (da_opt);
853  saying->draw ();
854  saying->detach_drawing_area ();
855  }
856 }
857 
859 {
860  u_int16 i;
861 
862  clear ();
863 
865  (character_base&) (*this) = (character_base&) src;
866 
867  for (i = 0; i < NBR_MOVES; i++)
868  (*anim[i]) = (*src.anim[i]);
869 
870  schedule = src.schedule;
871 
872  action = src.action;
873 
874  current_move = src.currentmove ();
875  if (src.mymap ())
876  {
877  set_map (src.mymap ());
878  set_pos (src.submap (), src.posx (), src.posy ());
879  set_offset (src.offx (), src.offy ());
880  }
881 
882  filename_ = src.filename_;
883 
884  return *this;
885 }
886 
887 
888 
889 // Private methods
890 
891 
892 void mapcharacter::occupy (u_int16 smap, u_int16 px, u_int16 py)
893 {
894  mapsquare_char mschar;
895 
896  list <mapsquare_char>::iterator it;
897  u_int16 sx = (px - base_x () < 0) ? 0 : px - base_x ();
898  u_int16 sy = (py - base_y () < 0) ? 0 : py - base_y ();
899  u_int16 ex = (sx + area_length () > refmap->submap[smap]->area_length ()) ?
900  refmap->submap[smap]->area_length () : sx + area_length ();
901  u_int16 ey = (sy + area_height () > refmap->submap[smap]->area_height ()) ?
902  refmap->submap[smap]->area_height () : sy + area_height ();
903  u_int16 i, j;
904 
905  // Placing the base tile first
906  mschar.mchar = this;
907  mschar.is_base = true;
908  mschar.x = px;
909  mschar.y = py;
910  mschar.walkable =
912 
913  refmap->submap[smap]->area[px][py].mapchars.push_back (mschar);
914  it = --refmap->submap[smap]->area[px][py].mapchars.end ();
915  it->base_tile = it;
916  mschar.base_tile = it;
917  mschar.is_base = false;
918 
919  // Ready to place the rest now
920  for (i = sx; i < ex; i++)
921  for (j = sy; j < ey; j++)
922  if (i != px || j != py)
923  {
924  mschar.x = i;
925  mschar.y = j;
926  mschar.walkable =
927  get_square (sx + base_x () - px, sy + base_y () - py)->
928  get_walkable () == ALL_WALKABLE;
929  refmap->submap[smap]->area[i][j].mapchars.push_back (mschar);
930  }
931 }
932 
933 void mapcharacter::leave (u_int16 smap, u_int16 px, u_int16 py)
934 {
935  list <mapsquare_char>::iterator it;
936  list <mapsquare_char>::iterator e;
937 
938  u_int16 sx = (px - base_x () < 0) ? 0 : px - base_x ();
939  u_int16 sy = (py - base_y () < 0) ? 0 : py - base_y ();
940  u_int16 ex = (sx + area_length () > refmap->submap[smap]->area_length ()) ?
941  refmap->submap[smap]->area_length () : sx + area_length ();
942  u_int16 ey = (sy + area_height () > refmap->submap[smap]->area_height ()) ?
943  refmap->submap[smap]->area_height () : sy + area_height ();
944  u_int16 i, j;
945 
946 
947  for (i = sx; i < ex; i++)
948  for (j = sy; j < ey; j++)
949  {
950  it = refmap->submap[smap]->area[i][j].mapchars.begin ();
951  e = refmap->submap[smap]->area[i][j].mapchars.end ();
952 
953  while (it != e && it->mchar != this)
954  it++;
955  if (it != e)
956  refmap->submap[smap]->area[px][py].mapchars.erase (it);
957  }
958 }
959 
960 void mapcharacter::leave_position ()
961 {
962  leave (submap (), posx (), posy ());
963  switch (current_move)
964  {
965  case WALK_NORTH:
966  case WALK_SOUTH:
967  leave (submap (), posx (), posy () - 1);
968  break;
969 
970  case WALK_WEST:
971  case WALK_EAST:
972  leave (submap (), posx () - 1, posy ());
973  break;
974  }
975 }
976 
977 void mapcharacter::set_pos (u_int16 smap, u_int16 x, u_int16 y)
978 {
979  // update character position
980  submap_ = smap;
981  posx_ = x;
982  posy_ = y;
983 
984  // mark the character's place as occupied
985  occupy (submap (), posx (), posy ());
986 }
987 
988 void mapcharacter::update_move ()
989 {
990  if (refmap)
991  switch (currentmove ())
992  {
993  case WALK_NORTH:
994  if (!offy ())
995  {
996  if (!can_go_north ())
997  {
998  stand_north ();
999  break;
1000  }
1001  leave_event evt;
1002 
1003  evt.submap = submap ();
1004  evt.x = posx ();
1005  evt.y = posy ();
1006  evt.c = this;
1007  evt.dir = WALK_NORTH;
1009 
1010  occupy (submap (), posx (), posy () - 1);
1011  set_offset (offx (), offy () - 1);
1012  }
1013 
1014  set_offset (offx (), offy () - 1);
1015 
1016  if (offy () == -MAPSQUARE_SIZE)
1017  {
1018  leave (submap (), posx (), posy ());
1019  leave (submap (), posx (), posy () - 1);
1020  set_pos (submap (), posx (), posy () - 1);
1021  set_offset (offx (), 0);
1022  stand_north ();
1023 
1024  enter_event evt;
1025 
1026  evt.submap = submap ();
1027  evt.x = posx ();
1028  evt.y = posy ();
1029  evt.c = this;
1030  evt.dir = WALK_NORTH;
1032  }
1033  break;
1034  case WALK_SOUTH:
1035  if (!offy ())
1036  {
1037  if (!can_go_south ())
1038  {
1039  stand_south ();
1040  break;
1041  }
1042  leave_event evt;
1043 
1044  evt.submap = submap ();
1045  evt.x = posx ();
1046  evt.y = posy ();
1047  evt.c = this;
1048  evt.dir = WALK_SOUTH;
1050 
1051  leave (submap (), posx (), posy ());
1052  occupy (submap (), posx (), posy ());
1053  set_pos (submap (), posx (), posy () + 1);
1054  set_offset (0, -(MAPSQUARE_SIZE - 1));
1055  }
1056  else
1057  {
1058  set_offset (offx (), offy () + 1);
1059 
1060  if (!offy ())
1061  {
1062  leave (submap (), posx (), posy () - 1);
1063  stand_south ();
1064 
1065  enter_event evt;
1066  evt.submap = submap ();
1067  evt.x = posx ();
1068  evt.y = posy ();
1069  evt.c = this;
1070  evt.dir = WALK_SOUTH;
1072  }
1073  }
1074  break;
1075  case WALK_WEST:
1076  if (!offx ())
1077  {
1078  if (!can_go_west ())
1079  {
1080  stand_west ();
1081  break;
1082  }
1083  leave_event evt;
1084 
1085  evt.submap = submap ();
1086  evt.x = posx ();
1087  evt.y = posy ();
1088  evt.c = this;
1089  evt.dir = WALK_WEST;
1091 
1092  occupy (submap (), posx () - 1, posy ());
1093  }
1094  set_offset (offx () -1, offy ());
1095  if (offx () == -MAPSQUARE_SIZE)
1096  {
1097  leave (submap (), posx (), posy ());
1098  leave (submap (), posx () - 1, posy ());
1099  set_pos (submap (), posx () - 1, posy ());
1100  set_offset (0, offy ());
1101  stand_west ();
1102 
1103  enter_event evt;
1104  evt.submap = submap ();
1105  evt.x = posx ();
1106  evt.y = posy ();
1107  evt.c = this;
1108  evt.dir = WALK_WEST;
1110  }
1111  break;
1112  case WALK_EAST:
1113  if (!offx ())
1114  {
1115  if (!can_go_east ())
1116  {
1117  stand_east ();
1118  break;
1119  }
1120  leave_event evt;
1121 
1122  evt.submap = submap ();
1123  evt.x = posx ();
1124  evt.y = posy ();
1125  evt.c = this;
1126  evt.dir = WALK_EAST;
1128 
1129  leave (submap (), posx (), posy ());
1130  occupy (submap (), posx (), posy ());
1131  set_pos (submap (), posx () + 1, posy ());
1132  set_offset (-(MAPSQUARE_SIZE - 1), 0);
1133  }
1134  else
1135  {
1136  set_offset (offx () + 1, offy ());
1137  if (!offx ())
1138  {
1139  leave (submap (), posx () - 1, posy ());
1140  stand_east ();
1141 
1142  enter_event evt;
1143  evt.submap = submap ();
1144  evt.x = posx ();
1145  evt.y = posy ();
1146  evt.c = this;
1147  evt.dir = WALK_EAST;
1149  }
1150  }
1151  break;
1152  }
1153  anim[current_move]->update ();
1154 }
1155 
1156 void mapcharacter::speak (const string & text)
1157 {
1158  if (saying)
1159  delete saying;
1160 
1161  string col;
1162  switch (get_color ())
1163  {
1164  case 1: col = "yellow"; break;
1165  case 2: col = "red"; break;
1166  case 3: col = "violet"; break;
1167  case 4: col = "blue"; break;
1168  case 5: col = "green"; break;
1169  default: col = "white"; break;
1170  }
1171 
1172  saying = new text_bubble (text, col, "original");
1173 }