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

Last change on this file since 541 was 541, checked in by Sam Hocevar, 12 years ago

imlib: rename TImage to TransImage? for readability.

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