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

Last change on this file since 528 was 528, checked in by Sam Hocevar, 11 years ago

imlib: merge almost all trans_image blit methods into one large template
function and refactor them for size.

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