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

Last change on this file since 497 was 497, checked in by Sam Hocevar, 10 years ago

lisp: implement LSymbol::EvalFunction? and ensure all local pointers are
protected against collection.

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