source: abuse/branches/lol/src/objects.cpp @ 732

Last change on this file since 732 was 732, checked in by Sam Hocevar, 8 years ago

build: SDL2 compilation fixes.

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