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

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

lisp: miscellaneous work on type safety.

File size: 36.9 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.h"
13#include "objects.h"
14#include "chars.h"
15
16#include "game.h"
17#include "intsect.h"
18#include "ability.h"
19#include "lisp.h"
20#include "jrand.h"
21#include "light.h"
22#include "dprint.h"
23#include "clisp.h"
24#include "lisp_gc.h"
25#include "profile.h"
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(((LispSymbol *)figures[otype]->vars[i])->GetName()),name))
108    {
109      return lvars[figures[otype]->var_index[i]];
110/*      LispObjectVar *cobj=(LispObjectVar *)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(((LispSymbol *)figures[otype]->vars[i])->GetName()),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((LispSymbol *)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( (LispSymbol *)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) free(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((LispSymbol *)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
444// collision checking will ask first to see if you
445int game_object::can_hurt(game_object *who)
446{
447    int is_attacker = current_level->is_attacker(this);
448
449    // it's you against them!  Damage only if it you are attacking or they are
450    // attacking you, ie. don't let them hurt themselves. This can change if
451    // you override this virtual function
452
453    if(who->hurtable()
454        && ((_team == -1) || (_team != who->get_team()))
455        && (is_attacker || current_level->is_attacker(who) || hurt_all()))
456        return 1;
457
458    return 0;
459}
460
461void game_object::note_attack(game_object *whom)
462{
463    return; // nevermind
464}
465
466void game_object::do_flinch(game_object *from)
467{
468  if (jrandom(2) && has_sequence(flinch_down))
469    set_state(flinch_down);
470  else if (has_sequence(flinch_up))
471    set_state(flinch_up);
472}
473
474
475void game_object::do_damage(int amount, game_object *from, int32_t hitx, int32_t hity,
476                int32_t push_xvel, int32_t push_yvel)
477{
478  // No friendly fire
479  if((_team != -1) && (_team == from->get_team()))
480    return;
481
482  void *d=figures[otype]->get_fun(OFUN_DAMAGE);
483  if (d)
484  {
485    LispList *am, *frm, *hx, *hy, *px, *py;
486    game_object *o = current_object;
487    current_object = this;
488
489    void *m = mark_heap(TMP_SPACE);
490
491    am = LispList::Create();
492    PtrRef r1(am);
493    am->car = LispNumber::Create(amount);
494
495    frm = LispList::Create();
496    PtrRef r2(frm);
497    frm->car = new_lisp_pointer(from);
498
499    hx = LispList::Create();
500    PtrRef r3(hx);
501    hx->car = LispNumber::Create(hitx);
502
503    hy = LispList::Create();
504    PtrRef r4(hy);
505    hy->car = LispNumber::Create(hity);
506
507    px = LispList::Create();
508    PtrRef r5(px);
509    px->car = LispNumber::Create(push_xvel);
510
511    py = LispList::Create();
512    PtrRef r6(py);
513    py->car = LispNumber::Create(push_yvel);
514
515    px->cdr = py;
516    hy->cdr = px;
517    hx->cdr = hy;
518    frm->cdr = hx;
519    am->cdr = frm;
520
521    time_marker *prof1 = NULL;
522    if (profiling())
523      prof1 = new time_marker;
524
525    eval_user_fun((LispSymbol *)d, am);
526    if (profiling())
527    {
528      time_marker now;
529      profile_add_time(this->otype, now.diff_time(prof1));
530      delete prof1;
531    }
532
533    restore_heap(m, TMP_SPACE);
534
535    current_object = o;
536  } else damage_fun(amount,from,hitx,hity,push_xvel,push_yvel);
537#ifdef SCADALISP
538  ENDLOCAL();
539#endif
540}
541
542void game_object::damage_fun(int amount, game_object *from, int32_t hitx, int32_t hity,
543                int32_t push_xvel, int32_t push_yvel)
544{
545  if (!hurtable() || !alive()) return ;
546
547  add_hp(-amount);
548  set_flags(flags()|FLAG_JUST_HIT);
549  do_flinch(from);
550
551
552  set_xvel(xvel()+push_xvel);
553  if (push_yvel<0 && !gravity())
554    set_gravity(1);
555  set_yvel(yvel()+push_yvel);
556
557  view *c=controller();
558  if (c && hp()<=0)
559  {
560    view *v=from->controller();
561    if (v) v->kills++;                       // attack from another player?
562    else if (from->total_objects()>0)
563    {
564      v=from->get_object(0)->controller();   // weapon from another player?
565      if (v && v!=c) v->kills++;
566      else
567      {
568    v=c;                                 // suicide
569    if (v) v->kills--;
570      }
571    }
572  }
573}
574
575
576
577int game_object::facing_attacker(int attackerx)
578{
579  return ((attackerx<x && direction<0) || (attackerx>=x && direction>0));
580
581}
582
583
584void game_object::picture_space(int32_t &x1, int32_t &y1,int32_t &x2, int32_t &y2)
585{
586  int xc=x_center(),w=picture()->width(),h=picture()->height();
587  if (direction>0)
588    x1=x-xc;
589  else x1=x-(w-xc-1);
590  x2=x1+w-1;
591  y1=y-h+1;
592  y2=y;
593}
594
595
596int game_object::next_picture()
597{
598  int ret=1;
599  if (frame_dir()>0)
600  {
601    if (!current_sequence()->next_frame(current_frame))
602    {
603      next_sequence();
604      ret=0;
605    }
606  }
607  else
608  {
609    if (!current_sequence()->last_frame(current_frame))
610    {
611      next_sequence();
612      ret=0;
613    }
614  }
615  frame_advance();
616  return ret;
617}
618
619
620int32_t game_object::x_center()
621{
622  return current_sequence()->x_center(current_frame);
623}
624
625
626void game_object::draw()
627{
628  if (figures[otype]->get_fun(OFUN_DRAW))
629  {
630    current_object=this;
631
632    void *m=mark_heap(TMP_SPACE);
633    time_marker *prof1=NULL;
634    if (profiling())
635      prof1=new time_marker;
636
637    eval_function((LispSymbol *)figures[otype]->get_fun(OFUN_DRAW),NULL);
638    if (profiling())
639    {
640      time_marker now;
641      profile_add_time(this->otype,now.diff_time(prof1));
642      delete prof1;
643    }
644
645
646
647    restore_heap(m,TMP_SPACE);
648
649  } else drawer();
650}
651
652
653void game_object::map_draw()
654{
655  if (figures[otype]->get_fun(OFUN_MAP_DRAW))
656  {
657    current_object=this;
658
659    void *m=mark_heap(TMP_SPACE);
660    time_marker *prof1=NULL;
661    if (profiling())
662      prof1=new time_marker;
663
664    eval_function((LispSymbol *)figures[otype]->get_fun(OFUN_MAP_DRAW),NULL);
665    if (profiling())
666    {
667      time_marker now;
668      profile_add_time(this->otype,now.diff_time(prof1));
669      delete prof1;
670    }
671
672    restore_heap(m,TMP_SPACE);
673
674  }
675}
676
677void game_object::draw_trans(int count, int max)
678{
679  trans_image *cpict=picture();
680  cpict->put_fade(screen,
681          (direction<0 ? x-(cpict->width()-x_center()-1) : x-x_center())-current_vxadd,
682          y-cpict->height()+1-current_vyadd,
683          count,max,
684          color_table,the_game->current_palette());
685}
686
687
688void game_object::draw_tint(int tint_id)
689{
690  trans_image *cpict=picture();
691  if (fade_count())
692    cpict->put_fade_tint(screen,
693               (direction<0 ? x-(cpict->width()-x_center()-1) : x-x_center())-current_vxadd,
694               y-cpict->height()+1-current_vyadd,
695               fade_count(),fade_max(),
696               cache.ctint(tint_id)->data,
697               color_table,the_game->current_palette());
698
699
700  else
701    cpict->put_remaped(screen,
702               (direction<0 ? x-(cpict->width()-x_center()-1) : x-x_center())-current_vxadd,
703               y-cpict->height()+1-current_vyadd,
704               cache.ctint(tint_id)->data);
705}
706
707
708void game_object::draw_double_tint(int tint_id, int tint2)
709{
710  trans_image *cpict=picture();
711  if (fade_count())
712    cpict->put_fade_tint(screen,
713               (direction<0 ? x-(cpict->width()-x_center()-1) : x-x_center())-current_vxadd,
714               y-cpict->height()+1-current_vyadd,
715               fade_count(),fade_max(),
716               cache.ctint(tint_id)->data,
717               color_table,the_game->current_palette());
718
719
720  else
721    cpict->put_double_remaped(screen,
722               (direction<0 ? x-(cpict->width()-x_center()-1) : x-x_center())-current_vxadd,
723               y-cpict->height()+1-current_vyadd,
724               cache.ctint(tint_id)->data,
725               cache.ctint(tint2)->data);
726}
727
728
729
730void game_object::draw_predator()
731{
732  trans_image *cpict=picture();
733  cpict->put_predator(screen,
734             (direction<0 ? x-(cpict->width()-x_center()-1) : x-x_center())-current_vxadd,
735             y-cpict->height()+1-current_vyadd);
736       
737}
738
739void game_object::drawer()
740{
741  if (morph_status())
742  {
743    morph_status()->draw(this,current_view);
744    if (morph_status()->frames_left()<1)
745      set_morph_status(NULL);
746  }
747  else
748  {
749    //view *v=controller();
750
751    if (fade_count())
752      draw_trans(fade_count(),fade_max());
753    else
754    {
755      trans_image *cpict=picture();
756      cpict->put_image(screen,
757               (direction<0 ? x-(cpict->width()-x_center()-1) : x-x_center())-current_vxadd,
758               y-cpict->height()+1-current_vyadd);
759    }
760  }
761}
762
763game_object *game_object::try_move(int32_t x, int32_t y, int32_t &xv, int32_t &yv, int checks)
764{
765  if (xv || yv)  // make sure they are suggesting movement
766  {
767    game_object *who1=NULL,*who2=NULL;      // who did we intersect?
768    int32_t x2,y2,h;
769
770    if (checks&1)
771    {
772      x2=x+xv;
773      y2=y+yv;
774      current_level->foreground_intersect(x,y,x2,y2);
775      if (!stoppable())
776        who1=current_level->boundary_setback(this,x,y,x2,y2);
777      else
778        who1=current_level->all_boundary_setback(this,x,y,x2,y2);
779      xv=x2-x;
780      yv=y2-y;
781    }
782
783
784    if (checks&2)
785    {
786      h=picture()->height();
787      x2=x+xv;
788      y2=y-h+1+yv;
789      current_level->foreground_intersect(x,y-h+1,x2,y2);
790      if (!stoppable())
791        who2=current_level->all_boundary_setback(this,x,y-h+1,x2,y2);
792      else
793        who2=current_level->boundary_setback(this,x,y-h+1,x2,y2);
794      xv=x2-x;
795      yv=y2-y+h-1;
796    }
797
798    if (who2) return who2;
799    else return who1;
800  }
801  else return NULL;
802}
803
804void *game_object::float_tick()  // returns 1 if you hit something, 0 otherwise
805{
806  int32_t ret=0;
807  if (hp()<=0)
808  {
809    if (state!=dead)
810    {
811      set_xacel(0);
812      set_fxacel(0);
813
814      if (has_sequence(dieing))
815      {
816    if (state!=dieing)
817    {
818      set_state(dieing);
819      set_xvel(0);
820    }
821      } else
822      { set_xvel(0);
823    set_fxvel(0);
824    if (has_sequence(dead))
825          set_state(dead);
826    else return 0;
827      }
828    }
829  }
830
831  int32_t fxv=sfxvel()+sfxacel(),fyv=sfyvel()+sfyacel();
832  int32_t xv=xvel()+xacel()+(fxv>>8),yv=yvel()+yacel()+(fyv>>8);
833
834  if (xv!=xvel() || yv!=yvel())   // only store vel's if changed so we don't increase object size
835  {
836    set_xvel(xv);
837    set_yvel(yv);
838  }
839
840  if (fxv!=sfxvel() || fyv!=sfyvel())
841  {
842    set_fxvel(fxv&0xff);
843    set_fyvel(fyv&0xff);
844  }
845
846
847  if (fxv || fyv || xv || yv)   // don't even try if there is no velocity
848  {
849    int32_t ffx=fx()+sfxvel(),ffy=fy()+sfyvel();
850    int32_t nxv=xvel()+(ffx>>8);
851    int32_t nyv=yvel()+(ffy>>8);
852    set_fx(ffx&0xff);
853    set_fy(ffy&0xff);
854
855    int32_t old_nxv=nxv,old_nyv=nyv;
856    game_object *hit_object=try_move(x,y,nxv,nyv,3);   // now find out what velocity is safe to use
857
858/*    if (get_cflag(CFLAG_STOPPABLE))
859    {
860      game_object *r=current_level->boundary_setback(exclude,x,y,nxv,nyv,1);
861      if (r) hit_object=r;
862    }*/
863
864    x+=nxv;
865    y+=nyv;
866    if (old_nxv!=nxv || old_nyv!=nyv)
867    {
868      int32_t lx=last_tile_hit_x,ly=last_tile_hit_y;
869      stop();
870      if (old_nxv==0)
871      {
872    if (old_nyv>0) ret|=BLOCKED_DOWN;
873    else if (old_nyv<0) ret|=BLOCKED_UP;
874      } else if (old_nyv==0)
875      {
876    if (old_nxv>0) ret|=BLOCKED_RIGHT;
877    else if (old_nxv<0) ret|=BLOCKED_LEFT;
878      } else
879      {
880    int32_t tx=(old_nxv>0 ? 1 : -1),ty=0;
881    try_move(x,y,tx,ty,3);
882    if (!tx)     
883      ret|=(old_nxv>0 ? BLOCKED_RIGHT : BLOCKED_LEFT);   
884    else tx=0;
885
886    ty=(old_nyv>0 ? 1 : -1);
887    try_move(x,y,tx,ty,3);
888    if (!ty)     
889      ret|=(old_nyv>0 ? BLOCKED_DOWN : BLOCKED_UP);
890   
891    if (!ret)
892      ret|=(old_nyv>0 ? BLOCKED_DOWN : BLOCKED_UP) | (old_nxv>0 ? BLOCKED_RIGHT : BLOCKED_LEFT);
893
894      }
895
896      void *rlist=NULL;   // return list
897      PtrRef r1(rlist);
898
899      if (hit_object)
900      {
901    push_onto_list(new_lisp_pointer(hit_object),rlist);
902    push_onto_list(l_object,rlist);
903      } else
904      {
905    push_onto_list(LispNumber::Create(ly),rlist);
906    push_onto_list(LispNumber::Create(lx),rlist);
907    push_onto_list(l_tile,rlist);
908      }
909      push_onto_list(LispNumber::Create(ret),rlist);
910
911      return rlist;
912    } else return true_symbol;
913  }
914  return true_symbol;
915}
916
917int game_object::tick()      // returns blocked status
918{
919  int blocked=0;
920
921  int32_t xt=0,yt=2;
922  try_move(x,y-2,xt,yt,1);    // make sure we are not falling through the floor
923  y=y-2+yt;
924
925  if (flags()&FLAG_JUST_BLOCKED)
926    set_flags(flags()-FLAG_JUST_BLOCKED);
927
928  if (gravity() && !floating())
929  {
930    int fya;
931    if (yacel()>=0)
932      fya=sfyacel()+200;
933    else
934      fya=sfyacel()-200;
935
936    set_yacel(yacel()+(fya>>8));
937    set_fyacel(fya&255);
938  }
939
940  // first let's move the guy acording to his physics
941  int32_t xa=xacel(),ya=yacel(),fxa=sfxacel(),fya=sfyacel();
942  if (xa || ya || fxa || fya)
943  {
944    int fxv=sfxvel(),fyv=sfyvel();
945    fxv+=fxa;  fyv+=fya;
946    //int32_t xv=xvel()+xa+(fxv>>8);
947    set_xvel(xvel()+xa+(fxv>>8));
948    set_yvel(yvel()+ya+(fyv>>8));
949    set_fxvel(fxv&0xff);
950    set_fyvel(fyv&0xff);
951  }
952
953  // check to see if this advancement causes him to collide with objects
954  int32_t old_vy=yvel(),old_vx=xvel();  // save the correct veloicties
955
956  if (old_vx || old_vy)
957  {
958    int up=0;
959    if (yvel()<=0)  // if we are going up or a strait across check up and down
960    up=2;
961    int32_t xv=xvel(),yv=yvel();
962    game_object *h=try_move(x,y,xv,yv,1|up);       // now find out what velocity is safe to use
963    set_xvel(xv);
964    set_yvel(yv);
965    x+=xv;
966    y+=yv;
967
968    if (h && stoppable()) return BLOCKED_LEFT|BLOCKED_RIGHT;
969
970    if (xv!=old_vx || yv!=old_vy)     // he collided with something
971    {
972      if (gravity())                         // was he going up or down?
973      {   
974    int32_t fall_xv=0,old_fall_vy,fall_vy;
975    old_fall_vy=fall_vy=old_vy-yvel();             // make sure he gets all of his yvel
976    try_move(x,y,fall_xv,fall_vy,1|up);
977    if (old_vy>0 && fall_vy<old_fall_vy)       // he was trying to fall, but he hit the ground
978    {
979      if (old_vy>0)   
980      {
981        blocked|=BLOCKED_DOWN;
982   
983        if (!xvel() && has_sequence(end_run_jump))   
984        {
985          set_xvel(old_vx);
986          set_state(end_run_jump);
987        }
988        else set_state(stopped);   
989      }
990      else blocked|=BLOCKED_UP;
991
992
993      if (state==run_jump_fall)
994      {
995        if (has_sequence(running))
996        set_state(running);
997        else
998        {
999          stop_x();
1000          set_state(stopped);
1001        }
1002      }
1003      else
1004      {
1005        set_yacel(0);
1006        set_fyacel(0);
1007        set_yvel(0);
1008        set_fyvel(0);
1009        set_gravity(0);
1010      }
1011
1012    } else
1013    {
1014      if (old_vy!=0)
1015      {
1016        int32_t testx=old_vx<0 ? -1 : 1,testy=0;    // see if we were stopped left/right
1017                                                     // or just up down
1018        try_move(x,y,testx,testy,1|up);
1019        if (testx==0)                           // blocked left/right, set flag
1020        {
1021          if (old_vx<0)
1022            blocked|=BLOCKED_LEFT;
1023          else
1024            blocked|=BLOCKED_RIGHT;
1025        }
1026        else if (old_vy<0)
1027          blocked|=BLOCKED_UP;
1028        else if (old_vy>0)
1029          blocked|=BLOCKED_DOWN;
1030
1031      } else if (old_vx<0)   // we can skip left/right test because player wasn't moving up/down
1032            blocked|=BLOCKED_LEFT;
1033      else
1034            blocked|=BLOCKED_RIGHT;
1035
1036      set_xvel(0);
1037      set_fxvel(0);
1038      if (old_vy<0 && fall_vy>0)
1039      {
1040        set_yvel(yvel()+fall_vy);
1041      } else set_yvel(yvel()+fall_vy);
1042    }
1043    y+=fall_vy;
1044      }
1045      else                  // see if we can make him 'climb' the hill
1046      {
1047    int32_t ox=x,oy=y;       // rember orginal position in case climb doesn't work
1048
1049    int32_t climb_xvel=0,climb_yvel=-5;        // try to move up one pixel to step over the
1050    try_move(x,y,climb_xvel,climb_yvel,3);  // jutting polygon line
1051    y+=climb_yvel;
1052
1053    climb_xvel=old_vx-xvel();
1054    climb_yvel=-(abs(climb_xvel));        // now try 45 degree slope
1055    try_move(x,y,climb_xvel,climb_yvel,3);
1056
1057    if (abs(climb_xvel)>0)  // see if he got further by climbing
1058    {
1059      blocked=blocked&(~(BLOCKED_LEFT|BLOCKED_RIGHT));
1060      x+=climb_xvel;
1061      y+=climb_yvel;
1062
1063      set_xvel(xvel()+climb_xvel);
1064      set_yvel(0);
1065      set_fyvel(0);
1066   
1067      // now put him back on the ground
1068      climb_yvel=abs(climb_xvel)+5;               // plus one to put him back on the ground
1069      climb_xvel=0;
1070      try_move(x,y,climb_xvel,climb_yvel,1);
1071      if (climb_yvel)
1072          y+=climb_yvel;
1073    }
1074    else   
1075    {   
1076      if (old_vx<0)
1077          blocked|=BLOCKED_LEFT;
1078      else
1079          blocked|=BLOCKED_RIGHT;
1080      set_state(stopped);      // nope, musta hit a wall, stop the poor fella
1081      x=ox;
1082      y=oy;   
1083    }
1084   
1085      }
1086
1087      if (xvel()!=old_vx && state!=run_jump_fall && state!=end_run_jump)
1088      {
1089    set_xacel(0);
1090    set_fxacel(0);
1091      }
1092    }
1093  }
1094
1095  if (yacel()==0 && !gravity())       // he is not falling, make sure he can't
1096  {
1097    int32_t nvx=0,nvy=yvel()+12;  // check three pixels below for ground
1098    try_move(x,y,nvx,nvy,1);
1099    if (nvy>11)                    // if he falls more than 2 pixels, then he falls
1100    {
1101      if (state!=run_jump_fall)      // make him fall
1102      {
1103        if (has_sequence(run_jump_fall))
1104          set_state(run_jump_fall);
1105        set_gravity(1);
1106      }
1107    } else if (nvy)   // if he fells less than 3 pixels then let him descend 'hills'
1108    {
1109      y+=nvy;
1110      blocked|=BLOCKED_DOWN;
1111    }
1112  }
1113  return blocked;
1114}
1115
1116void game_object::defaults()
1117{
1118  set_state(state);
1119  if (otype!=0xffff)
1120  {
1121    int s=get_ability(otype,start_hp);
1122    if (s!=default_simple.hp())
1123      set_hp(s);
1124  }
1125}
1126
1127void game_object::frame_advance()
1128{
1129  int ad=current_sequence()->get_advance(current_frame);
1130  if (ad && current_level)
1131  {
1132    int32_t xv;
1133    if (direction>0) xv=ad; else xv=-ad;
1134    int32_t yv=0;
1135    try_move(x,y,xv,yv,3);
1136    x+=xv;
1137  }
1138}
1139
1140void game_object::set_state(character_state s, int frame_direction)
1141{
1142  if (has_sequence(s))
1143    state=s;
1144  else state=stopped;
1145
1146  current_frame=0;
1147  if (frame_direction!=1)
1148    set_frame_dir(frame_direction);
1149
1150  frame_advance();
1151}
1152
1153
1154game_object *create(int type, int32_t x, int32_t y, int skip_constructor, int aitype)
1155{
1156  game_object *g=new game_object(type,skip_constructor);
1157  g->x=x; g->y=y; g->last_x=x; g->last_y=y;
1158  if (aitype)
1159    g->set_aitype(aitype);
1160  if (figures[type]->get_fun(OFUN_CONSTRUCTOR) && !skip_constructor)
1161  {
1162    game_object *o=current_object;
1163    current_object=g;
1164
1165    void *m=mark_heap(TMP_SPACE);
1166
1167    time_marker *prof1=NULL;
1168    if (profiling())
1169      prof1=new time_marker;
1170
1171    eval_function((LispSymbol *)figures[type]->get_fun(OFUN_CONSTRUCTOR),NULL);
1172    if (profiling())
1173    {
1174      time_marker now;
1175      profile_add_time(type,now.diff_time(prof1));
1176      delete prof1;
1177    }
1178
1179
1180
1181    restore_heap(m,TMP_SPACE);
1182
1183    current_object=o;
1184  }
1185  return g;
1186}
1187
1188int base_size()
1189{
1190  return 1+
1191         1*8+
1192     2*5+
1193     4*7;
1194}
1195
1196int game_object::size()
1197{
1198  return base_size();
1199}
1200
1201
1202
1203int game_object::move(int cx, int cy, int button)
1204{
1205  int ret=0;
1206
1207  if (figures[otype]->get_fun(OFUN_MOVER))      // is a lisp move function defined?
1208  {
1209    LispList *lcx, *lcy, *lb;
1210
1211    game_object *o=current_object;
1212    current_object=this;
1213
1214    // make a list of the parameters, and call the lisp function
1215    lcx = LispList::Create();
1216    PtrRef r1(lcx);
1217    lcx->car = LispNumber::Create(cx);
1218
1219    lcy = LispList::Create();
1220    PtrRef r2(lcy);
1221    lcy->car = LispNumber::Create(cy);
1222
1223    lb = LispList::Create();
1224    PtrRef r3(lb);
1225    lb->car = LispNumber::Create(button);
1226
1227    lcx->cdr = lcy;
1228    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((LispSymbol *)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    if (item_type(r)!=L_NUMBER)
1248    {
1249      lprint(r);
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((LispSymbol *)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    eval_function((LispSymbol *)figures[new_type]->get_fun(OFUN_CONSTRUCTOR),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.