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

Last change on this file since 481 was 481, checked in by Sam Hocevar, 7 years ago

Fuck the history, I'm renaming all .hpp files to .h for my own sanity.

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