source: abuse/trunk/src/objects.cpp @ 112

Last change on this file since 112 was 112, checked in by Sam Hocevar, 11 years ago
  • Fix spelling errors all over the place.
File size: 36.2 KB
Line 
1/*
2 *  Abuse - dark 2D side-scrolling platform game
3 *  Copyright (c) 1995 Crack dot Com
4 *
5 *  This software was released into the Public Domain. As with most public
6 *  domain software, no warranty is made or implied by Crack dot Com or
7 *  Jonathan Clark.
8 */
9
10#include "config.h"
11
12#include "timage.hpp"
13#include "objects.hpp"
14#include "chars.hpp"
15
16#include "game.hpp"
17#include "intsect.hpp"
18#include "ability.hpp"
19#include "lisp.hpp"
20#include "jrand.hpp"
21#include "light.hpp"
22#include "dprint.hpp"
23#include "clisp.hpp"
24#include "lisp_gc.hpp"
25#include "profile.hpp"
26
27#ifdef SCADALISP
28#define LCAR(x)         CAR(x)
29#define LCDR(x)         CDR(x)
30#else
31#define LCAR(x)         (x)->car
32#define LCDR(x)         (x)->cdr
33#endif /* SCADALISP */
34
35char **object_names;
36int total_objects;
37game_object *current_object;
38view *current_view;
39
40game_object *game_object::copy()
41{
42  game_object *o=create(otype,x,y);
43  o->state=state;
44  int i=0;
45  for (;i<TOTAL_OBJECT_VARS;i++)
46    o->set_var(i,get_var(i));
47  memcpy(o->lvars,lvars,4*figures[otype]->tv);
48  for (i=0;i<total_objects();i++)
49    o->add_object(get_object(i));
50  for (i=0;i<total_lights();i++)
51    o->add_light(get_light(i));
52  return o;
53}
54
55int simple_object::total_vars() { return TOTAL_OBJECT_VARS; }
56
57
58obj_desc object_descriptions[TOTAL_OBJECT_VARS] =
59{
60    {"fade_dir",      RC_8 },
61    {"frame_dir",     RC_8 },
62    {"direction",     RC_8 },
63    {"gravity_on",    RC_8 },
64    {"fade_count",    RC_8 },
65
66    {"fade_max",      RC_8 },
67    {"active",        RC_8 },
68    {"flags",         RC_8 },
69    {"aitype",        RC_8 },
70    {"xvel",          RC_32 },
71
72    {"fxvel",         RC_8 },
73    {"yvel",          RC_32 },
74    {"fyvel",         RC_8 },
75    {"xacel",         RC_32 },
76    {"fxacel",        RC_8 },
77
78    {"yacel",         RC_32 },
79    {"fyacel",        RC_8 },
80    {"x",             RC_32 },
81    {"fx",            RC_8 },
82    {"y",             RC_32 },
83
84    {"fy",            RC_8 },
85    {"hp",            RC_16 },
86    {"mp",            RC_16 },
87    {"fmp",           RC_16 },
88    {"cur_frame",     RC_16 },
89
90    {"aistate",       RC_16 },
91    {"aistate_time",  RC_16 },
92    {"targetable",    RC_8 }
93};
94 
95int32_t game_object::get_var_by_name(char *name, int &error)
96{
97  error=0;
98  int i=0;
99  for (;i<TOTAL_OBJECT_VARS;i++)
100  {
101    if (!strcmp(object_descriptions[i].name,name))
102      return get_var(i);
103  }
104
105  for (i=0;i<figures[otype]->tiv;i++)
106  {
107    if (!strcmp(lstring_value(symbol_name(figures[otype]->vars[i])),name))
108    {
109      return lvars[figures[otype]->var_index[i]];
110/*      lisp_object_var *cobj=(lisp_object_var *)symbol_value(figures[otype]->vars[i]);
111      character_type *t=figures[otype];
112      int number=cobj->number;
113      if (t->tiv<=number || !t->vars[number])
114      {
115        lbreak("access : variable does not exsist for this class\n");
116        return 0;
117      }
118      return lvars[t->var_index[number]];*/
119    }
120  }
121  error=1;
122  return 0;
123}
124
125int game_object::set_var_by_name(char *name, int32_t value)
126{
127  int i=0;
128  for (;i<TOTAL_OBJECT_VARS;i++)
129  {
130    if (!strcmp(object_descriptions[i].name,name))
131    {
132      set_var(i,value);
133      return 1;
134    }
135  }
136  for (i=0;i<figures[otype]->tiv;i++)
137    if (!strcmp(lstring_value(symbol_name(figures[otype]->vars[i])),name))
138    {
139      lvars[figures[otype]->var_index[i]]=value;
140      return 1;
141    }
142  return 0;
143}
144
145
146char const *simple_object::var_name(int x)
147{
148  return object_descriptions[x].name;
149}
150
151int simple_object::var_type(int x)
152{
153  return object_descriptions[x].type;
154}
155
156
157void simple_object::set_var(int xx, uint32_t v)
158{
159  switch (xx)
160  {
161    case 0 : set_fade_dir(v); break;
162    case 1 : set_frame_dir(v); break;
163    case 2 : direction=v; break;
164    case 3 : set_gravity(v); break;
165    case 4 : set_fade_count(v); break;
166    case 5 : set_fade_max(v); break;
167    case 6 : active=v;break;
168    case 7 : set_flags(v); break;
169    case 8 : set_aitype(v); break;
170    case 9 : set_xvel(v); break;
171
172    case 10 : set_fxvel(v); break;
173    case 11 : set_yvel(v); break;
174    case 12 : set_fyvel(v); break;
175    case 13 : set_xacel(v); break;
176    case 14 : set_fxacel(v); break;
177
178    case 15 : set_yacel(v); break;
179    case 16 : set_fyacel(v); break;
180    case 17 : x=v; break;
181    case 18 : set_fx(v); break;
182    case 19 : y=v; break;
183
184    case 20 : set_fy(v); break;
185    case 21 : set_hp(v); break;
186    case 22 : set_mp(v); break;
187    case 23 : set_fmp(v); break;
188
189    case 24 : current_frame=v;  break;
190    case 25 : set_aistate(v); break;
191
192    case 26 : set_aistate_time(v); break;
193    case 27 : set_targetable(v); break;
194  }
195}
196
197int32_t simple_object::get_var(int xx)
198{
199  switch (xx)
200  {
201    case 0 : return fade_dir(); break;
202    case 1 : return frame_dir(); break;
203    case 2 : return direction; break;
204    case 3 : return gravity(); break;
205    case 4 : return fade_count(); break;
206    case 5 : return fade_max(); break;
207    case 6 : return active; break;
208    case 7 : return flags(); break;
209    case 8 : return aitype(); break;
210    case 9 : return xvel(); break;
211    case 10 : return fxvel(); break;
212
213    case 11 : return yvel(); break;
214    case 12 : return fyvel(); break;
215
216    case 13 : return xacel(); break;
217    case 14 : return fxacel(); break;
218
219    case 15 : return yacel(); break;
220    case 16 : return fyacel(); break;
221
222    case 17 : return x; break;
223    case 18 : return fx(); break;
224
225    case 19 : return y; break;
226    case 20 : return fy(); break;
227
228    case 21 : return hp(); break;
229    case 22 : return mp(); break;
230    case 23 : return fmp(); break;
231
232    case 24 : return current_frame;  break;
233    case 25 : return aistate(); break;
234    case 26 : return aistate_time(); break;
235    case 27 : return targetable();
236  }
237  return 0;
238}
239
240
241
242
243int RC_type_size(int type)
244{
245  switch (type)
246  {
247    case RC_8 :
248    { return 1; } break;
249    case RC_16 :
250    { return 2; } break;
251    case RC_32 :
252    { return 4; } break;
253  }             
254  CHECK(0);
255  return 1;
256}
257
258void game_object::reload_notify()
259{
260  void *ns=figures[otype]->get_fun(OFUN_RELOAD); 
261  if (ns)
262  {
263    game_object *o=current_object;
264    current_object=this;
265
266    void *m=mark_heap(TMP_SPACE);
267    eval_function((lisp_symbol *)ns,NULL);
268    restore_heap(m,TMP_SPACE);
269
270    current_object=o;
271  }
272}
273
274void game_object::next_sequence()
275{
276        void *ns = figures[otype]->get_fun( OFUN_NEXT_STATE );
277        if( ns )
278        {
279                current_object = this;
280                void *m = mark_heap( TMP_SPACE );
281                (void)eval_function( (lisp_symbol *)ns, NULL );
282                restore_heap( m, TMP_SPACE );
283        }
284        else
285        {
286                switch( state )
287                {
288                        case dieing:
289                        {
290                                set_state( dead );
291                        } break;
292                        case end_run_jump:
293                        {
294                                set_state( running );
295                        } break;
296                        case dead:
297                        case run_jump:
298                        case run_jump_fall:
299                        case running:
300                        {
301                                set_state(state);
302                        } break;
303                        case start_run_jump:
304                        {
305                                set_yvel(get_ability(type(),jump_yvel));
306                                if( xvel() > 0 )
307                                        set_xvel(get_ability(type(),jump_xvel));
308                                else if( xvel() < 0 )
309                                        set_xvel(-get_ability(type(),jump_xvel));
310                                set_xacel(0);
311                                set_fxacel(0);
312                                set_gravity(1);
313                                set_state(run_jump);
314                        } break;
315                        case flinch_up:
316                        case flinch_down:
317                        {
318                                if( gravity() )
319                                {
320                                        if( has_sequence( end_run_jump ) )
321                                                set_state(end_run_jump);
322                                        else
323                                                set_state(stopped);
324                                }
325                                else
326                                {
327                                        set_state(stopped);
328                                }
329                        } break;
330
331                        default:
332                        {
333                                set_state(stopped);
334                        } break;
335                }
336        }
337}
338
339
340
341game_object::~game_object()
342{
343  if (lvars) jfree(lvars);
344  clean_up();
345}
346
347void game_object::add_power(int amount) 
348{
349  int max_power=lnumber_value(symbol_value(l_max_power)); 
350  int n=mp()+amount;
351  if (n<0) n=0;
352  if (n>max_power) n=max_power;
353  set_mp(n);
354}
355
356void game_object::add_hp(int amount)
357{
358  if (controller() && controller()->god) return ;
359  int max_hp=lnumber_value(symbol_value(l_max_hp)); 
360  int n=hp()+amount;
361  if (n<0) n=0;
362  if (n>max_hp)
363    n=max_hp;
364  set_hp(n);
365}
366
367int game_object::can_morph_into(int type)
368{
369  if (type!=otype && mp()>=figures[type]->morph_power)
370    return 1;
371  else return 0;
372}
373
374void game_object::morph_into(int type, void (*stat_fun)(int), int anneal, int frames)
375{
376  set_morph_status(new morph_char(this,type,stat_fun,anneal,frames));
377  otype=type;
378  set_state(stopped);
379}
380
381void game_object::draw_above(view *v)
382{
383  int32_t x1,y1,x2,y2,sy1,sy2,sx,i;
384  picture_space(x1,y1,x2,y2);   
385
386  the_game->game_to_mouse(x1,y1,v,sx,sy2);
387  if (sy2>=v->cy1)
388  {
389    int32_t draw_to=y1-(sy2-v->cy1),tmp=x;
390    current_level->foreground_intersect(x,y1,tmp,draw_to);     
391    the_game->game_to_mouse(x1,draw_to,v,i,sy1);     // calculate sy1
392
393    sy1=max(v->cy1,sy1);
394    sy2=min(v->cy2,sy2);
395    trans_image *p=picture();
396   
397    for (i=sy1;i<=sy2;i++)
398      p->put_scan_line(screen,sx,i,0); 
399  } 
400}
401
402int game_object::push_range()
403{
404  return get_ability(otype,push_xrange);
405}
406
407int game_object::decide()
408{
409  if (figures[otype]->get_fun(OFUN_AI))
410  {
411    int old_aistate;
412    old_aistate=aistate();
413
414    current_object=this;
415    void *m=mark_heap(TMP_SPACE);
416
417    time_marker *prof1=NULL;
418    if (profiling())
419      prof1=new time_marker;
420
421    Cell *ret=(Cell *)eval_function((lisp_symbol *)figures[otype]->get_fun(OFUN_AI),NULL);
422    if (profiling())
423    {
424      time_marker now;
425      profile_add_time(this->otype,now.diff_time(prof1));
426      delete prof1;
427    }
428
429    restore_heap(m,TMP_SPACE);
430
431    if (keep_ai_info())
432    {
433      if (aistate()!=old_aistate)
434      set_aistate_time(0);
435      else set_aistate_time(aistate_time()+1);   
436    }
437    if (!NILP(ret)) return 1;
438    else return 0;
439  }
440  else move(0,0,0);
441  return 1;
442}
443
444int game_object::can_hurt(game_object *who)     // collision checking will ask first to see if you
445{
446  int is_attacker=current_level->is_attacker(this); 
447  // it's you against them!  Damage only if it you are attacking or they are attacking you
448  // I.E. don't let them hurt themselves, this can change if you over-ride this virtual function
449
450  if (who->hurtable() && (is_attacker || current_level->is_attacker(who) || hurt_all()))
451    return 1;
452  else return 0; 
453}
454
455void game_object::note_attack(game_object *whom)
456{
457  return ;   // nevermind
458}
459
460void game_object::do_flinch(game_object *from)
461{
462  if (jrandom(2) && has_sequence(flinch_down))
463    set_state(flinch_down);
464  else if (has_sequence(flinch_up))
465    set_state(flinch_up);
466}
467
468   
469void game_object::do_damage(int amount, game_object *from, int32_t hitx, int32_t hity,
470                            int32_t push_xvel, int32_t push_yvel)
471{
472
473  void *d=figures[otype]->get_fun(OFUN_DAMAGE); 
474  if (d)
475  {
476    void        *am, *frm, *hx, *hy, *px, *py; 
477    game_object *o=current_object;
478    current_object=this;
479
480    void *m=mark_heap(TMP_SPACE);
481
482    am=new_cons_cell();
483    l_ptr_stack.push(&am);
484
485    ((cons_cell *)am)->car=new_lisp_number(amount);   
486
487    frm=new_cons_cell();
488    l_ptr_stack.push(&frm);
489
490    ((cons_cell *)frm)->car=new_lisp_pointer(from);
491
492    hx=new_cons_cell();
493    l_ptr_stack.push(&hx);
494
495    ((cons_cell *)hx)->car=new_lisp_number(hitx);
496
497    hy=new_cons_cell();
498    l_ptr_stack.push(&hy);
499    ((cons_cell *)hy)->car=new_lisp_number(hity);
500
501    px=new_cons_cell();
502    l_ptr_stack.push(&px);
503    ((cons_cell *)px)->car=new_lisp_number(push_xvel);
504
505    py=new_cons_cell();
506    l_ptr_stack.push(&py);
507    ((cons_cell *)py)->car=new_lisp_number(push_yvel);
508
509
510    ((cons_cell *)am)->cdr=frm;
511    ((cons_cell *)frm)->cdr=hx;
512    ((cons_cell *)hx)->cdr=hy;
513    ((cons_cell *)hy)->cdr=px;
514    ((cons_cell *)px)->cdr=py;
515
516    time_marker *prof1=NULL;
517    if (profiling())
518      prof1=new time_marker;
519
520    eval_user_fun((lisp_symbol *)d,am);
521    if (profiling())
522    {
523      time_marker now;
524      profile_add_time(this->otype,now.diff_time(prof1));
525      delete prof1;
526    }
527
528
529    l_ptr_stack.pop(6);
530   
531    restore_heap(m,TMP_SPACE);
532
533    current_object=o;
534  } else damage_fun(amount,from,hitx,hity,push_xvel,push_yvel);
535#ifdef SCADALISP
536  ENDLOCAL();
537#endif
538}
539
540void game_object::damage_fun(int amount, game_object *from, int32_t hitx, int32_t hity,
541                            int32_t push_xvel, int32_t push_yvel)
542{
543  if (!hurtable() || !alive()) return ;
544
545  add_hp(-amount);
546  set_flags(flags()|FLAG_JUST_HIT);
547  do_flinch(from);
548
549 
550  set_xvel(xvel()+push_xvel);
551  if (push_yvel<0 && !gravity())
552    set_gravity(1);
553  set_yvel(yvel()+push_yvel);
554
555  view *c=controller();
556  if (c && hp()<=0)
557  {
558    view *v=from->controller();
559    if (v) v->kills++;                       // attack from another player?
560    else if (from->total_objects()>0)
561    {
562      v=from->get_object(0)->controller();   // weapon from another player?
563      if (v && v!=c) v->kills++;
564      else
565      {
566        v=c;                                 // suicide
567        if (v) v->kills--;
568      }
569    }
570  }
571}
572     
573
574
575int game_object::facing_attacker(int attackerx)
576{
577  return ((attackerx<x && direction<0) || (attackerx>=x && direction>0));
578
579}
580
581
582void game_object::picture_space(int32_t &x1, int32_t &y1,int32_t &x2, int32_t &y2)
583{
584  int xc=x_center(),w=picture()->width(),h=picture()->height(); 
585  if (direction>0)
586    x1=x-xc; 
587  else x1=x-(w-xc-1); 
588  x2=x1+w-1;
589  y1=y-h+1;
590  y2=y; 
591}
592
593
594int game_object::next_picture()
595{
596  int ret=1;
597  if (frame_dir()>0)
598  {
599    if (!current_sequence()->next_frame(current_frame))
600    {
601      next_sequence();
602      ret=0;
603    }
604  }
605  else
606  {
607    if (!current_sequence()->last_frame(current_frame))
608    {
609      next_sequence();
610      ret=0;
611    }
612  }
613  frame_advance();
614  return ret;
615}
616
617
618int32_t game_object::x_center()
619{
620  return current_sequence()->x_center(current_frame);   
621}
622
623
624void game_object::draw()
625{
626  if (figures[otype]->get_fun(OFUN_DRAW))
627  {
628    current_object=this;
629
630    void *m=mark_heap(TMP_SPACE);
631    time_marker *prof1=NULL;
632    if (profiling())
633      prof1=new time_marker;
634
635    eval_function((lisp_symbol *)figures[otype]->get_fun(OFUN_DRAW),NULL);
636    if (profiling())
637    {
638      time_marker now;
639      profile_add_time(this->otype,now.diff_time(prof1));
640      delete prof1;
641    }
642
643
644
645    restore_heap(m,TMP_SPACE);
646
647  } else drawer();
648}
649
650
651void game_object::map_draw()
652{
653  if (figures[otype]->get_fun(OFUN_MAP_DRAW))
654  {
655    current_object=this;
656
657    void *m=mark_heap(TMP_SPACE);
658    time_marker *prof1=NULL;
659    if (profiling())
660      prof1=new time_marker;
661
662    eval_function((lisp_symbol *)figures[otype]->get_fun(OFUN_MAP_DRAW),NULL);
663    if (profiling())
664    {
665      time_marker now;
666      profile_add_time(this->otype,now.diff_time(prof1));
667      delete prof1;
668    }
669
670    restore_heap(m,TMP_SPACE);
671
672  }
673}
674
675void game_object::draw_trans(int count, int max)
676{
677  trans_image *cpict=picture();
678  cpict->put_fade(screen,
679                  (direction<0 ? x-(cpict->width()-x_center()-1) : x-x_center())-current_vxadd,
680                  y-cpict->height()+1-current_vyadd,
681                  count,max,
682                  color_table,the_game->current_palette());
683}
684
685
686void game_object::draw_tint(int tint_id)
687{
688  trans_image *cpict=picture();
689  if (fade_count())     
690    cpict->put_fade_tint(screen,
691                       (direction<0 ? x-(cpict->width()-x_center()-1) : x-x_center())-current_vxadd,
692                       y-cpict->height()+1-current_vyadd,
693                       fade_count(),fade_max(),
694                       cash.ctint(tint_id)->data,
695                       color_table,the_game->current_palette());
696
697
698  else
699    cpict->put_remaped(screen,
700                       (direction<0 ? x-(cpict->width()-x_center()-1) : x-x_center())-current_vxadd,
701                       y-cpict->height()+1-current_vyadd,
702                       cash.ctint(tint_id)->data);
703}
704
705
706void game_object::draw_double_tint(int tint_id, int tint2)
707{
708  trans_image *cpict=picture();
709  if (fade_count())     
710    cpict->put_fade_tint(screen,
711                       (direction<0 ? x-(cpict->width()-x_center()-1) : x-x_center())-current_vxadd,
712                       y-cpict->height()+1-current_vyadd,
713                       fade_count(),fade_max(),
714                       cash.ctint(tint_id)->data,
715                       color_table,the_game->current_palette());
716
717
718  else
719    cpict->put_double_remaped(screen,
720                       (direction<0 ? x-(cpict->width()-x_center()-1) : x-x_center())-current_vxadd,
721                       y-cpict->height()+1-current_vyadd,
722                       cash.ctint(tint_id)->data,
723                       cash.ctint(tint2)->data);
724}
725
726
727
728void game_object::draw_predator()
729{
730  trans_image *cpict=picture();
731  cpict->put_predator(screen,
732                     (direction<0 ? x-(cpict->width()-x_center()-1) : x-x_center())-current_vxadd,
733                     y-cpict->height()+1-current_vyadd);
734                   
735}
736
737void game_object::drawer()
738{
739  if (morph_status())
740  {
741    morph_status()->draw(this,current_view);
742    if (morph_status()->frames_left()<1)
743      set_morph_status(NULL);
744  }
745  else
746  {
747    //view *v=controller();
748
749    if (fade_count())     
750      draw_trans(fade_count(),fade_max());
751    else
752    {
753      trans_image *cpict=picture();
754      cpict->put_image(screen,
755                       (direction<0 ? x-(cpict->width()-x_center()-1) : x-x_center())-current_vxadd,
756                       y-cpict->height()+1-current_vyadd);
757    }
758  }
759}
760
761game_object *game_object::try_move(int32_t x, int32_t y, int32_t &xv, int32_t &yv, int checks)
762{
763  if (xv || yv)  // make sure they are suggesting movement
764  {   
765    game_object *who1=NULL,*who2=NULL;      // who did we intersect? 
766    int32_t x2,y2,h; 
767
768    if (checks&1)
769    {     
770      x2=x+xv;
771      y2=y+yv;   
772      current_level->foreground_intersect(x,y,x2,y2);
773      if (!stoppable())
774        who1=current_level->boundary_setback(this,x,y,x2,y2);
775      else
776        who1=current_level->all_boundary_setback(this,x,y,x2,y2); 
777      xv=x2-x;
778      yv=y2-y;
779    }
780   
781
782    if (checks&2)
783    {     
784      h=picture()->height();   
785      x2=x+xv;   
786      y2=y-h+1+yv;
787      current_level->foreground_intersect(x,y-h+1,x2,y2);
788      if (!stoppable())
789        who2=current_level->all_boundary_setback(this,x,y-h+1,x2,y2);   
790      else
791        who2=current_level->boundary_setback(this,x,y-h+1,x2,y2);
792      xv=x2-x;
793      yv=y2-y+h-1; 
794    }
795       
796    if (who2) return who2;
797    else return who1;
798  }
799  else return NULL; 
800}
801
802void *game_object::float_tick()  // returns 1 if you hit something, 0 otherwise
803{
804  int32_t ret=0;
805  if (hp()<=0)
806  {
807    if (state!=dead)
808    {
809      set_xacel(0);
810      set_fxacel(0);
811
812      if (has_sequence(dieing))
813      {     
814        if (state!=dieing)
815        {
816          set_state(dieing);
817          set_xvel(0);
818        }
819      } else
820      { set_xvel(0);
821        set_fxvel(0);
822        if (has_sequence(dead))
823          set_state(dead);
824        else return 0;
825      }
826    }
827  }
828
829  int32_t fxv=sfxvel()+sfxacel(),fyv=sfyvel()+sfyacel();
830  int32_t xv=xvel()+xacel()+(fxv>>8),yv=yvel()+yacel()+(fyv>>8);
831
832  if (xv!=xvel() || yv!=yvel())   // only store vel's if changed so we don't increase object size
833  {
834    set_xvel(xv);
835    set_yvel(yv);
836  }
837
838  if (fxv!=sfxvel() || fyv!=sfyvel())
839  {
840    set_fxvel(fxv&0xff);
841    set_fyvel(fyv&0xff);
842  }
843
844 
845  if (fxv || fyv || xv || yv)   // don't even try if there is no velocity
846  {
847    int32_t ffx=fx()+sfxvel(),ffy=fy()+sfyvel();
848    int32_t nxv=xvel()+(ffx>>8);
849    int32_t nyv=yvel()+(ffy>>8);
850    set_fx(ffx&0xff);
851    set_fy(ffy&0xff);
852   
853    int32_t old_nxv=nxv,old_nyv=nyv;
854    game_object *hit_object=try_move(x,y,nxv,nyv,3);   // now find out what velocity is safe to use
855   
856/*    if (get_cflag(CFLAG_STOPPABLE))
857    {
858      game_object *r=current_level->boundary_setback(exclude,x,y,nxv,nyv,1);
859      if (r) hit_object=r;
860    }*/
861
862    x+=nxv;
863    y+=nyv;
864    if (old_nxv!=nxv || old_nyv!=nyv)
865    {
866      int32_t lx=last_tile_hit_x,ly=last_tile_hit_y;
867      stop();
868      if (old_nxv==0)
869      {
870        if (old_nyv>0) ret|=BLOCKED_DOWN;
871        else if (old_nyv<0) ret|=BLOCKED_UP;
872      } else if (old_nyv==0)
873      {
874        if (old_nxv>0) ret|=BLOCKED_RIGHT;
875        else if (old_nxv<0) ret|=BLOCKED_LEFT;
876      } else
877      {
878        int32_t tx=(old_nxv>0 ? 1 : -1),ty=0;
879        try_move(x,y,tx,ty,3);
880        if (!tx)       
881          ret|=(old_nxv>0 ? BLOCKED_RIGHT : BLOCKED_LEFT);     
882        else tx=0;
883
884        ty=(old_nyv>0 ? 1 : -1);
885        try_move(x,y,tx,ty,3);
886        if (!ty)       
887          ret|=(old_nyv>0 ? BLOCKED_DOWN : BLOCKED_UP);
888       
889        if (!ret)
890          ret|=(old_nyv>0 ? BLOCKED_DOWN : BLOCKED_UP) | (old_nxv>0 ? BLOCKED_RIGHT : BLOCKED_LEFT);
891
892      }
893
894      void *rlist=NULL;   // return list
895      p_ref r1(rlist);
896
897      if (hit_object)
898      {
899        push_onto_list(new_lisp_pointer(hit_object),rlist);
900        push_onto_list(l_object,rlist);
901      } else
902      {
903        push_onto_list(new_lisp_number(ly),rlist);
904        push_onto_list(new_lisp_number(lx),rlist);
905        push_onto_list(l_tile,rlist);
906      }
907      push_onto_list(new_lisp_number(ret),rlist);
908
909      return rlist;
910    } else return true_symbol;
911  }
912  return true_symbol;
913}
914
915int game_object::tick()      // returns blocked status
916{
917  int blocked=0;
918
919  int32_t xt=0,yt=2;
920  try_move(x,y-2,xt,yt,1);    // make sure we are not falling through the floor
921  y=y-2+yt;
922 
923  if (flags()&FLAG_JUST_BLOCKED)
924    set_flags(flags()-FLAG_JUST_BLOCKED);
925
926  if (gravity() && !floating())
927  {
928    int fya;
929    if (yacel()>=0)
930      fya=sfyacel()+200;
931    else
932      fya=sfyacel()-200;
933
934    set_yacel(yacel()+(fya>>8));
935    set_fyacel(fya&255);
936  }
937 
938  // first let's move the guy acording to his physics
939  int32_t xa=xacel(),ya=yacel(),fxa=sfxacel(),fya=sfyacel();
940  if (xa || ya || fxa || fya)
941  {
942    int fxv=sfxvel(),fyv=sfyvel();
943    fxv+=fxa;  fyv+=fya;   
944    //int32_t xv=xvel()+xa+(fxv>>8);
945    set_xvel(xvel()+xa+(fxv>>8));
946    set_yvel(yvel()+ya+(fyv>>8));
947    set_fxvel(fxv&0xff);
948    set_fyvel(fyv&0xff);
949  }
950 
951  // check to see if this advancement causes him to collide with objects
952  int32_t old_vy=yvel(),old_vx=xvel();  // save the correct veloicties
953
954  if (old_vx || old_vy)
955  {
956    int up=0;
957    if (yvel()<=0)  // if we are going up or a strait across check up and down
958    up=2;
959    int32_t xv=xvel(),yv=yvel();
960    game_object *h=try_move(x,y,xv,yv,1|up);       // now find out what velocity is safe to use
961    set_xvel(xv);
962    set_yvel(yv);
963    x+=xv;
964    y+=yv;
965
966    if (h && stoppable()) return BLOCKED_LEFT|BLOCKED_RIGHT;
967
968    if (xv!=old_vx || yv!=old_vy)     // he collided with something
969    {         
970      if (gravity())                         // was he going up or down?
971      {                       
972        int32_t fall_xv=0,old_fall_vy,fall_vy;
973        old_fall_vy=fall_vy=old_vy-yvel();             // make sure he gets all of his yvel
974        try_move(x,y,fall_xv,fall_vy,1|up);
975        if (old_vy>0 && fall_vy<old_fall_vy)       // he was trying to fall, but he hit the ground
976        {
977          if (old_vy>0)
978          {
979            blocked|=BLOCKED_DOWN;
980           
981            if (!xvel() && has_sequence(end_run_jump))   
982            {
983              set_xvel(old_vx);
984              set_state(end_run_jump);
985            }
986            else set_state(stopped);     
987          }
988          else blocked|=BLOCKED_UP;
989
990
991          if (state==run_jump_fall)
992          {
993            if (has_sequence(running))
994            set_state(running);
995            else
996            {
997              stop_x();
998              set_state(stopped);
999            }
1000          }
1001          else
1002          {
1003            set_yacel(0);
1004            set_fyacel(0);
1005            set_yvel(0);
1006            set_fyvel(0);
1007            set_gravity(0);
1008          }
1009
1010        } else
1011        {
1012          if (old_vy!=0)
1013          {
1014            int32_t testx=old_vx<0 ? -1 : 1,testy=0;    // see if we were stopped left/right
1015                                                     // or just up down
1016            try_move(x,y,testx,testy,1|up);
1017            if (testx==0)                           // blocked left/right, set flag
1018            {
1019              if (old_vx<0)
1020                blocked|=BLOCKED_LEFT;
1021              else
1022                blocked|=BLOCKED_RIGHT;
1023            }
1024            else if (old_vy<0)
1025              blocked|=BLOCKED_UP;
1026            else if (old_vy>0)
1027              blocked|=BLOCKED_DOWN;
1028
1029          } else if (old_vx<0)   // we can skip left/right test because player wasn't moving up/down
1030            blocked|=BLOCKED_LEFT;
1031          else
1032            blocked|=BLOCKED_RIGHT;
1033
1034          set_xvel(0);
1035          set_fxvel(0);
1036          if (old_vy<0 && fall_vy>0)       
1037          {
1038            set_yvel(yvel()+fall_vy);
1039          } else set_yvel(yvel()+fall_vy);
1040        }     
1041        y+=fall_vy;           
1042      }   
1043      else                  // see if we can make him 'climb' the hill
1044      {
1045        int32_t ox=x,oy=y;       // rember orginal position in case climb doesn't work
1046
1047        int32_t climb_xvel=0,climb_yvel=-5;         // try to move up one pixel to step over the
1048        try_move(x,y,climb_xvel,climb_yvel,3);  // jutting polygon line
1049        y+=climb_yvel;
1050
1051        climb_xvel=old_vx-xvel();
1052        climb_yvel=-(abs(climb_xvel));      // now try 45 degree slope
1053        try_move(x,y,climb_xvel,climb_yvel,3);
1054
1055        if (abs(climb_xvel)>0)  // see if he got further by climbing
1056        {
1057          blocked=blocked&(~(BLOCKED_LEFT|BLOCKED_RIGHT));
1058          x+=climb_xvel;
1059          y+=climb_yvel;
1060
1061          set_xvel(xvel()+climb_xvel);
1062          set_yvel(0);         
1063          set_fyvel(0);
1064         
1065          // now put him back on the ground         
1066          climb_yvel=abs(climb_xvel)+5;               // plus one to put him back on the ground
1067          climb_xvel=0;
1068          try_move(x,y,climb_xvel,climb_yvel,1);
1069          if (climb_yvel)
1070          y+=climb_yvel;
1071        }
1072        else             
1073        {       
1074          if (old_vx<0)
1075          blocked|=BLOCKED_LEFT;
1076          else
1077          blocked|=BLOCKED_RIGHT;
1078          set_state(stopped);      // nope, musta hit a wall, stop the poor fella
1079          x=ox;
1080          y=oy;
1081        }
1082       
1083      }
1084     
1085      if (xvel()!=old_vx && state!=run_jump_fall && state!=end_run_jump)
1086      {
1087        set_xacel(0);
1088        set_fxacel(0);
1089      }
1090    }
1091  }
1092     
1093  if (yacel()==0 && !gravity())       // he is not falling, make sure he can't
1094  {
1095    int32_t nvx=0,nvy=yvel()+12;  // check three pixels below for ground
1096    try_move(x,y,nvx,nvy,1);
1097    if (nvy>11)                    // if he falls more than 2 pixels, then he falls
1098    {
1099      if (state!=run_jump_fall)      // make him fall
1100      {       
1101        if (has_sequence(run_jump_fall))
1102          set_state(run_jump_fall);
1103        set_gravity(1);
1104      }
1105    } else if (nvy)   // if he fells less than 3 pixels then let him descend 'hills'
1106    {     
1107      y+=nvy;
1108      blocked|=BLOCKED_DOWN;     
1109    }   
1110  }
1111  return blocked;
1112}
1113
1114void game_object::defaults()
1115{
1116  set_state(state);
1117  if (otype!=0xffff)
1118  {
1119    int s=get_ability(otype,start_hp);
1120    if (s!=default_simple.hp())
1121      set_hp(s);
1122  }
1123}
1124
1125void game_object::frame_advance()
1126{
1127  int ad=current_sequence()->get_advance(current_frame);
1128  if (ad && current_level)
1129  {
1130    int32_t xv;
1131    if (direction>0) xv=ad; else xv=-ad;
1132    int32_t yv=0;
1133    try_move(x,y,xv,yv,3);
1134    x+=xv;
1135  }   
1136}
1137
1138void game_object::set_state(character_state s, int frame_direction)
1139{
1140  if (has_sequence(s))
1141    state=s;
1142  else state=stopped;
1143
1144  current_frame=0; 
1145  if (frame_direction!=1)
1146    set_frame_dir(frame_direction);
1147
1148  frame_advance();
1149}
1150
1151
1152game_object *create(int type, int32_t x, int32_t y, int skip_constructor, int aitype)
1153{
1154  game_object *g=new game_object(type,skip_constructor);
1155  g->x=x; g->y=y; g->last_x=x; g->last_y=y;
1156  if (aitype)
1157    g->set_aitype(aitype);
1158  if (figures[type]->get_fun(OFUN_CONSTRUCTOR) && !skip_constructor)
1159  {
1160    game_object *o=current_object;
1161    current_object=g;   
1162
1163    void *m=mark_heap(TMP_SPACE);
1164
1165    time_marker *prof1=NULL;
1166    if (profiling())
1167      prof1=new time_marker;
1168
1169    eval_function((lisp_symbol *)figures[type]->get_fun(OFUN_CONSTRUCTOR),NULL);
1170    if (profiling())
1171    {
1172      time_marker now;
1173      profile_add_time(type,now.diff_time(prof1));
1174      delete prof1;
1175    }
1176
1177
1178
1179    restore_heap(m,TMP_SPACE);
1180
1181    current_object=o;
1182  }
1183  return g;
1184}
1185
1186int base_size()
1187{
1188  return 1+ 
1189         1*8+
1190         2*5+
1191         4*7;
1192}
1193
1194int game_object::size()
1195{
1196  return base_size();
1197}
1198
1199
1200
1201int game_object::move(int cx, int cy, int button)
1202
1203  int ret=0;
1204
1205  if (figures[otype]->get_fun(OFUN_MOVER))      // is a lisp move function defined?
1206  {
1207    void *lcx,*lcy,*lb;
1208
1209    game_object *o=current_object;
1210    current_object=this;
1211
1212
1213    // make a list of the parameters, and call the lisp function
1214    lcx=new_cons_cell();
1215    l_ptr_stack.push(&lcx);
1216    ((cons_cell *)lcx)->car=new_lisp_number(cx);
1217
1218    lcy=new_cons_cell();
1219    l_ptr_stack.push(&lcy);
1220    ((cons_cell *)lcy)->car=new_lisp_number(cy);
1221
1222    lb=new_cons_cell();
1223    l_ptr_stack.push(&lb);
1224    ((cons_cell *)lb)->car=new_lisp_number(button);
1225
1226
1227    ((cons_cell *)lcx)->cdr=lcy;
1228    ((cons_cell *)lcy)->cdr=lb;
1229
1230    void *m=mark_heap(TMP_SPACE);
1231
1232    time_marker *prof1=NULL;
1233    if (profiling())
1234      prof1=new time_marker;
1235
1236    void *r=eval_function((lisp_symbol *)figures[otype]->get_fun(OFUN_MOVER),
1237                          (void *)lcx);
1238    if (profiling())
1239    {
1240      time_marker now;
1241      profile_add_time(this->otype,now.diff_time(prof1));
1242      delete prof1;
1243    }
1244
1245    restore_heap(m,TMP_SPACE);
1246
1247    l_ptr_stack.pop(3);
1248    if (item_type(r)!=L_NUMBER)
1249    {
1250      lprint(r);
1251      lbreak("Object %s did not return a number from it's mover function!\n"
1252             "It should return a number to indicate it's blocked status to the\n"
1253             "ai function.",object_names[otype]);       
1254    }
1255    ret|=lnumber_value(r);
1256    current_object=o;
1257  }
1258  else ret|=mover(cx,cy,button);   
1259
1260  return ret;
1261}
1262
1263int game_object::mover(int cx, int cy, int button)  // return false if the route is blocked
1264{
1265  if (hp()<=0)
1266    return tick();
1267
1268  if (flinch_state(state))                    // flinching? don't move
1269    cx=cy=button=0;
1270   
1271 
1272  if (cx)          // x movement suggested?
1273  {     
1274    if (state==stopped)   // see if started moving
1275    {   
1276      if (has_sequence(running))
1277      {
1278        if (cx>0)
1279        {
1280          direction=1;
1281          set_xvel(get_ability(type(),run_top_speed));
1282        }
1283        else
1284        {
1285          direction=-1;
1286          set_xacel(-get_ability(type(),run_top_speed));
1287        }
1288        set_state(running);
1289      }
1290    } else if (state==run_jump || state==run_jump_fall)
1291    {
1292      if (cx>0)
1293      {
1294        direction=1;
1295        set_xacel(get_ability(type(),start_accel));           // set the appropriate accel
1296      } else
1297      {
1298        direction=-1;
1299        set_xacel(-get_ability(type(),start_accel));           // set the appropriate accel
1300      }   
1301    }
1302    else
1303    {
1304      // turn him around if he pressed the other way, he is not walking so a fast turn
1305      // is needed, don't go through the turn sequence
1306      if ((cx>0  && direction<0) || (cx<0 && direction>0))
1307        direction=-direction;     
1308    }
1309  }         // not pressing left or right, so slow down or stop
1310  else if (!gravity() && state!=start_run_jump)
1311  {   
1312    int32_t stop_acel;
1313    if (xvel()<0)                                    // he was going left
1314    {
1315      stop_acel=get_ability(type(),stop_accel);    // find out how fast he can slow down
1316      if (xvel()+stop_acel>=0)                       // if this acceleration is enough to stop him
1317      {
1318        stop_x();
1319        if (!gravity())
1320          set_state(stopped);     
1321      } else { set_xacel(stop_acel); }
1322    } else if (xvel()>0)
1323    {
1324      stop_acel=-get_ability(type(),stop_accel);
1325      if (xvel()+stop_acel<=0)
1326      {
1327        stop_x();
1328        if (!gravity())
1329          set_state(stopped);
1330      } else set_xacel(stop_acel);
1331    } else if (!gravity())                // don't stop in the air
1332    {
1333      set_xacel(0);
1334      set_fxacel(0);
1335      // Check to see if we should go to stop state
1336      if (state==running)
1337        set_state(stopped);
1338    }   
1339  }   
1340 
1341
1342/*  if (state==still_jump || state==still_jump_fall || state==end_still_jump)
1343  {
1344    set_xacel(0);
1345    set_fxacel(0);
1346    if (xvel()>0) set_xvel(get_ability(type(),jump_top_speed));
1347    else if (xvel()<0) set_xvel(-get_ability(type(),jump_top_speed));
1348  } else if (state==run_jump || state==run_jump_fall || state==end_run_jump)
1349  {
1350    set_xacel(0);
1351    set_fxacel(0);
1352    if (xvel()>0) set_xvel(get_ability(type(),jump_top_speed));
1353    else if (xvel()<0) set_xvel(-get_ability(type(),jump_top_speed));
1354  } */
1355 
1356  // see if the user said to jump
1357  if (cy<0 && !floating() && !gravity())
1358  {
1359    set_gravity(1);     
1360    set_yvel(get_ability(type(),jump_yvel));
1361//    if (cx && has_sequence(run_jump))
1362      set_state(run_jump);
1363    if (xvel()!=0)
1364      if (direction>0)
1365        set_xvel(get_ability(type(),jump_top_speed));
1366      else
1367        set_xvel(-get_ability(type(),jump_top_speed));
1368    set_xacel(0);
1369
1370
1371/*    if (state==stopped) 
1372    {
1373      if (cx && has_sequence(start_run_jump))
1374        set_state(start_run_jump);
1375      else if (has_sequence(start_still_jump))
1376        set_state(start_still_jump);               
1377      else
1378      {
1379
1380      }
1381    }
1382    else if (state==running && has_sequence(run_jump))
1383    {
1384      set_state(run_jump);
1385      set_yvel(get_ability(type(),jump_yvel));
1386      set_gravity(1);     
1387    }   
1388    else if (state==walking || state==turn_around)  // if walking check to see if has a
1389    {
1390      if (has_sequence(start_still_jump))
1391        set_state(start_still_jump);   
1392      else
1393      {
1394        set_yvel(get_ability(type(),jump_yvel));
1395        set_gravity(1);     
1396        if (has_sequence(run_jump))
1397          set_state(run_jump);
1398      }
1399    }    */
1400  }
1401
1402
1403
1404  if (state==run_jump && yvel()>0)
1405    set_state(run_jump_fall);
1406   
1407
1408
1409//  if (state!=end_still_jump && state!=end_run_jump)
1410  {   
1411    if (cx>0)
1412      set_xacel(get_ability(type(),start_accel));
1413    else if (cx<0)
1414      set_xacel(-get_ability(type(),start_accel));
1415  } 
1416 
1417  // make sure they are not going faster than their maximum speed
1418  int top_speed;
1419  if (state==stopped || state==end_run_jump)
1420    top_speed=get_ability(type(),walk_top_speed);
1421  else if (state==running)
1422    top_speed=get_ability(type(),run_top_speed);   
1423  else if (state==run_jump || state==run_jump_fall || state==start_run_jump)
1424  {
1425    top_speed=get_ability(type(),jump_top_speed);     
1426    if (!cx) top_speed=0;
1427  }
1428  else top_speed=1000;
1429   
1430     
1431  if (abs(xvel()+xacel())>top_speed)
1432  {   
1433    if (xacel()<0) set_xacel(-top_speed-xvel());
1434    else set_xacel(top_speed-xvel());
1435  } 
1436   
1437  character_state old_state=state;
1438  int old_frame=current_frame;   
1439  int tick_stat=tick();
1440
1441  // if he started to jump and slammed into a wall then make sure he stays in this state
1442  // so he can finish the jump
1443  if (!tick_stat && (old_state==start_run_jump))
1444  {
1445    set_state(old_state);
1446    current_frame=old_frame;
1447    next_picture();   
1448  }
1449   
1450  return tick_stat;
1451   
1452}
1453
1454
1455
1456
1457game_object *game_object::bmove(int &whit, game_object *exclude)
1458{
1459
1460  // first let's move the guy acording to his physics
1461  int32_t xa=xacel(),ya=yacel(),fxa=sfxacel(),fya=sfyacel();
1462  if (xa || ya || fxa || fya)
1463  {
1464    int fxv=sfxvel(),fyv=sfyvel();
1465    fxv+=fxa;  fyv+=fya;   
1466    //int32_t xv=xvel()+xa+(fxv>>8);
1467    set_xvel(xvel()+xa+(fxv>>8));
1468    set_yvel(yvel()+ya+(fyv>>8));
1469    set_fxvel(fxv&0xff);
1470    set_fyvel(fyv&0xff);
1471  }
1472 
1473  int32_t ox2,oy2;
1474
1475  int32_t nx=x+xvel(),nfx=fx()+fxvel(),ny=y+yvel(),nfy=fy()+fyvel();
1476  nx+=nfx>>8;
1477  ny+=nfy>>8;
1478 
1479
1480  // check to see if this advancement causes him to collide with objects
1481  ox2=nx;
1482  oy2=ny;  // save the correct veloicties
1483 
1484  current_level->foreground_intersect(x,y,nx,ny);  // first see how far we can travel
1485  game_object *ret=current_level->all_boundary_setback(exclude,x,y,nx,ny);
1486  x=nx;
1487  y=ny;
1488  set_fx(nfx&0xff);
1489  set_fy(nfy&0xff);
1490  if (ret)
1491  {
1492    if (!ret->hurtable())   // object is not hurtable, return as if hit wall.
1493    { whit=1;
1494      return NULL;
1495    } else
1496    return ret;
1497  }
1498  else
1499  {
1500    whit=(nx!=ox2 || ny!=oy2);
1501    return NULL;
1502  }
1503}
1504
1505
1506
1507int object_to_number_in_list(game_object *who, object_node *list)
1508{
1509  int x=1;
1510  while (list)
1511  {
1512    if (who==list->me) return x;
1513    else list=list->next;
1514    x++;
1515  }
1516  return 0;
1517}
1518
1519game_object *number_to_object_in_list(int32_t x, object_node *list)
1520{
1521  if (!x) return NULL; x--;
1522  while (x && list) { list=list->next; x--; }
1523  if (list) return list->me;
1524  else return NULL;
1525}
1526
1527
1528void delete_object_list(object_node *first)
1529{
1530  while (first)
1531  {
1532    object_node *n=first;
1533    first=first->next;
1534    delete n;
1535  }
1536}
1537
1538
1539int32_t object_list_length(object_node *list)
1540{
1541  int32_t x=0;
1542  while (list) { list=list->next; x++; }
1543  return x;
1544 
1545}
1546
1547
1548
1549game_object::game_object(int Type, int load)
1550{
1551  if (Type<0xffff)
1552  {
1553    int t=figures[Type]->tv;
1554    if (t)
1555    {
1556      lvars=(int32_t *)jmalloc(t*4,"object vars");
1557      memset(lvars,0,t*4);
1558    }
1559    else lvars=NULL;
1560  } else lvars=NULL;
1561
1562  otype=Type;
1563  if (!load) defaults();
1564}
1565
1566
1567int game_object::reduced_state()
1568{
1569  int32_t x=0;
1570  for (int i=0;i<figures[otype]->ts;i++)
1571  {
1572    if (i==state) return x;
1573      else
1574    if (figures[otype]->seq[i]) x++;
1575  }
1576  return 0;
1577}
1578
1579
1580void game_object::change_aitype(int new_type)
1581{
1582  set_aitype(new_type);
1583  if (otype<0xffff)
1584  {
1585    void *f=figures[otype]->get_fun(OFUN_CHANGE_TYPE); 
1586    if (f)
1587    {
1588      game_object *o=current_object;
1589      current_object=(game_object *)this;
1590
1591      time_marker *prof1=NULL;
1592      if (profiling())
1593        prof1=new time_marker;
1594
1595      eval_user_fun((lisp_symbol *)f,NULL);
1596
1597      if (profiling())
1598      {
1599        time_marker now;
1600        profile_add_time(this->otype,now.diff_time(prof1));
1601        delete prof1;
1602      }
1603
1604
1605      current_object=o;
1606    }
1607  }
1608}
1609
1610
1611void game_object::change_type(int new_type)
1612{
1613  if (lvars) jfree(lvars);     // free old variable
1614
1615  if (otype<0xffff)
1616  {
1617    int t=figures[new_type]->tv;
1618    if (t)
1619    {
1620      lvars=(int32_t *)jmalloc(t*4,"object vars");
1621      memset(lvars,0,t*4);
1622    }
1623    else lvars=NULL;
1624  } else return;
1625  otype=new_type;
1626
1627  if (figures[new_type]->get_fun(OFUN_CONSTRUCTOR))
1628  {
1629    game_object *o=current_object;
1630    current_object=this;   
1631
1632    void *m=mark_heap(TMP_SPACE);
1633
1634    time_marker *prof1=NULL;
1635    if (profiling())
1636      prof1=new time_marker;
1637
1638    eval_function((lisp_symbol *)figures[new_type]->get_fun(OFUN_CONSTRUCTOR),NULL);
1639    if (profiling())
1640    {
1641      time_marker now;
1642      profile_add_time(otype,now.diff_time(prof1));
1643      delete prof1;
1644    }
1645
1646
1647    restore_heap(m,TMP_SPACE);
1648
1649    current_object=o;
1650  } 
1651}
Note: See TracBrowser for help on using the repository browser.