source: abuse/branches/lol/src/ant.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: 13.1 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 <ctype.h>
16
17#include "common.h"
18
19#include "ant.h"
20#include "lisp/lisp.h"
21#include "lisp/lisp_gc.h"
22#include "compiled.h"
23#include "objects.h"
24#include "level.h"
25#include "game.h"
26#include "clisp.h"
27#include "dev.h"
28
29enum {  ANT_need_to_dodge,     // ant vars
30    ANT_no_see_time,
31    ANT_hide_flag };
32
33int can_see(GameObject *o, int32_t x1, int32_t y1, int32_t x2, int32_t y2)
34{
35  int32_t nx2=x2,ny2=y2;
36  g_current_level->foreground_intersect(x1,y1,x2,y2);
37  if (x2!=nx2 || y2!=ny2) return 0;
38
39  g_current_level->boundary_setback(o,x1,y1,x2,y2);
40  return (x2==nx2 && y2==ny2);
41}
42
43
44// if we first saw the player or it's been a while since we've seen the player then do a scream
45static void scream_check(GameObject *o, GameObject *b)
46{
47  if (can_see(o,o->m_pos.x, o->m_pos.y, b->m_pos.x, b->m_pos.y))
48  {
49    if (o->lvars[ANT_no_see_time]==0 || o->lvars[ANT_no_see_time]>20)
50      the_game->play_sound(S_ASCREAM_SND, 127, o->m_pos.x, o->m_pos.y);
51    o->lvars[ANT_no_see_time]=1;
52  } else o->lvars[ANT_no_see_time]++;
53}
54
55static int ant_congestion(GameObject *o)
56{
57  for (GameObject *d=g_current_level->first_active_object(); d; d=d->next_active)
58  {
59    if (d->otype == o->otype && lol::abs(o->m_pos.x - d->m_pos.x) < 30
60         && lol::abs(o->m_pos.x - d->m_pos.y) < 20)
61      return 1;
62  }
63  return 0;
64}
65
66static int ant_dodge(GameObject *o)
67{
68  if (o->lvars[ANT_need_to_dodge]==1)
69  {
70    o->lvars[ANT_need_to_dodge]=0;
71    if (rand(2) == 0)
72    {
73      o->set_state(stopped);
74      o->set_aistate(ANT_JUMP);
75      if (!can_see(o, o->m_pos.x, o->m_pos.y, o->m_pos.x, o->m_pos.y - 120))   // is there a roof above?
76      {
77        o->m_vel = ivec2(0, -17);
78        o->set_aistate(ANT_JUMP_ROOF);
79        ant_ai();
80      }
81      else
82      {
83        o->m_vel = ivec2(o->direction > 0 ? 22 : -22, -12);
84        o->set_aistate(ANT_JUMP);
85      }
86    }
87    return 1;
88  } else return 0;
89}
90
91static int alien_wait_time()
92{
93  void *v=symbol_value(l_difficulty);
94  if (v==l_easy)
95    return 6;
96  else if (v==l_medium)
97   return 4;
98  else if (v==l_hard)
99    return 2;
100  else return 1;
101}
102
103static int can_hit_player(GameObject *o, GameObject *b)
104{
105  return can_see(o, o->m_pos.x + (o->direction > 0 ? 15 : -15), o->m_pos.y - 15, b->m_pos.x, b->m_pos.y - 15);
106}
107
108static void fire_at_player(GameObject *o, GameObject *b)
109{
110  int32_t firex = o->m_pos.x + (o->direction > 0 ? 15 : -15),
111          firey = o->m_pos.y - 15,
112          playerx = b->m_pos.x + b->m_vel.x * 8,
113          playery = b->m_pos.y - 15 + b->m_vel.y * 2;
114  if (can_see(o, o->m_pos.x, o->m_pos.y, firex, firey) && can_see(o, firex, firey, playerx, playery))
115  {
116    int angle=lisp_atan2(firey-playery,playerx-firex);
117    void *call_list=NULL;
118    PtrRef r1(call_list);
119    push_onto_list(LPointer::Create(b),call_list);
120    push_onto_list(LNumber::Create(angle),call_list);
121    push_onto_list(LNumber::Create(firey),call_list);
122    push_onto_list(LNumber::Create(firex),call_list);
123    push_onto_list(LNumber::Create(o->aitype()),call_list);
124    push_onto_list(LPointer::Create(o),call_list);
125    ((LSymbol *)l_fire_object)->EvalUserFunction((LList *)call_list);
126    o->set_state((character_state)S_weapon_fire);
127  }
128}
129
130void *ant_ai()
131{
132  GameObject *o=current_object,*b;
133
134  if (o->hp()==0)    // if we are dead return NULL and get deleted
135  {
136    if (o->state==dead)
137      return NULL;
138    else o->set_state(dead);
139    return true_symbol;
140  }
141
142
143  if (o->state==flinch_up || o->state==flinch_down)
144  {
145    o->next_picture();
146    return true_symbol;
147  }
148
149
150  switch (o->aistate())
151  {
152    case ANT_START :
153    {
154      o->set_state((character_state)S_hanging);
155      if (o->lvars[ANT_hide_flag])
156        o->set_aistate(ANT_HIDING);
157
158      else o->set_aistate(ANT_HANGING);
159    } break;
160    case ANT_HIDING :
161    {
162      if (rand(128) == 0) the_game->play_sound(S_SCARE_SND, 127, o->m_pos.x, o->m_pos.y);
163      if (o->otype!=S_HIDDEN_ANT)
164      {
165    o->change_type(S_HIDDEN_ANT);      // switch types so noone hurts us.
166    o->set_state(stopped);
167    o->set_aistate(ANT_HIDING);
168      }
169
170      int fall=0;
171      if (o->total_objects()==0)
172      {
173    if (player_list->next)
174      b=g_current_level->attacker(current_object);
175    else b=player_list->m_focus;
176    if (lol::abs(b->m_pos.x - o->m_pos.x) < 130 && (o->m_pos.y < b->m_pos.y))
177      fall=1;
178      }
179      else if (o->get_object(0)->aistate()!=0)
180        fall=1;
181
182      if (fall)
183      {
184    o->change_type(S_ANT_ROOF);
185    o->set_state((character_state)S_falling);
186    o->set_aistate(ANT_FALL_DOWN);
187    o->set_targetable(1);
188      } else o->set_targetable(0);
189    } break;
190    case ANT_HANGING :
191    {
192      int fall=0;
193      if (rand(128) == 0) the_game->play_sound(S_SCARE_SND, 127, o->m_pos.x, o->m_pos.y);
194      if (o->lvars[ANT_hide_flag])
195        o->set_aistate(ANT_HIDING);
196      else
197      {
198    o->set_state((character_state)S_hanging);
199    if (o->total_objects())
200    {
201      if (o->get_object(0)->aistate()!=0)
202      fall=1;
203    } else
204    {
205      if (player_list->next)
206        b=g_current_level->attacker(current_object);
207      else b=player_list->m_focus;
208      if (lol::abs(b->m_pos.x - o->m_pos.x) < 130 && (o->m_pos.y < b->m_pos.y))
209      fall=1;
210    }
211    if (fall)
212    {
213      o->set_state((character_state)S_fall_start);
214      o->set_aistate(ANT_FALL_DOWN);
215      o->set_targetable(1);
216    } else o->set_targetable(0);
217      }
218
219    } break;
220    case ANT_FALL_DOWN :
221    {
222      o->set_state((character_state)S_falling);
223
224      if (player_list->next)
225      b=g_current_level->attacker(current_object);
226      else b=player_list->m_focus;
227
228      scream_check(o,b);
229      int ret=o->mover(0,0,0);
230      if ((ret&BLOCKED_DOWN) || !can_see(o, o->m_pos.x, o->m_pos.y, o->m_pos.x, o->m_pos.y + 1))
231      {
232    o->set_state((character_state)S_landing);
233    the_game->play_sound(S_ALAND_SND, 127, o->m_pos.x, o->m_pos.y);
234    o->set_aistate(ANT_LANDING);
235      }
236    } break;
237    case ANT_LANDING :
238    {
239      if (!o->next_picture())
240      {
241    int32_t xv=0,yv=2;
242    o->try_move(o->m_pos.x, o->m_pos.y, xv, yv, 1);
243    if (yv!=0)
244    {
245      o->set_gravity(1);
246      o->set_aistate(ANT_FALL_DOWN);
247    }
248    else
249    {
250      o->set_state(stopped);
251      o->set_aistate(ANT_RUNNING);
252      return (void *)ant_ai;
253    }
254      }
255    } break;
256    case ANT_RUNNING :
257    {
258      if (player_list->next)
259      b=g_current_level->attacker(current_object);
260      else b=player_list->m_focus;
261      scream_check(o,b);
262
263
264      if (rand(16) == 0)
265      o->lvars[ANT_need_to_dodge]=1;
266      if (!ant_dodge(o))
267      {
268    if ((o->m_pos.x > b->m_pos.x && o->direction == -1) || (o->m_pos.x < b->m_pos.x && o->direction == 1))
269    {
270      o->next_picture();
271      if (rand(4) == 0 && lol::abs(o->m_pos.x-b->m_pos.x)<180 && lol::abs(o->m_pos.y-b->m_pos.y)<100 && can_hit_player(o,b))
272      {
273        o->set_state((character_state)S_weapon_fire);
274        o->set_aistate(ANT_FIRE);
275      } else if (lol::abs(o->m_pos.x-b->m_pos.x)<100 && lol::abs(o->m_pos.y-b->m_pos.y)<10 && rand(4) == 0)
276        o->set_aistate(ANT_POUNCE_WAIT);
277      else if (lol::abs(o->m_pos.x-b->m_pos.x)>140 && !ant_congestion(o))
278        o->set_aistate(ANT_JUMP);
279      else
280      {
281        int32_t xm=o->direction>0 ? get_ability(o->otype,run_top_speed) : -get_ability(o->otype,run_top_speed);
282        int32_t ym=0,new_xm=xm;
283        if (o->state!=running) o->set_state(running);
284
285        o->try_move(o->m_pos.x,o->m_pos.y,new_xm,ym,3);
286        if (new_xm!=xm)    // blocked, see if we can climb ramp
287        {
288          new_xm=xm;
289          ym=-lol::abs(xm);
290          o->try_move(o->m_pos.x,o->m_pos.y,new_xm,ym,3);
291          if (new_xm==xm)
292          {
293        o->m_pos.x+=new_xm;
294        o->m_pos.y+=ym;
295        new_xm=0;
296        ym=lol::abs(xm);      // now get back on the ground
297        o->try_move(o->m_pos.x,o->m_pos.y,new_xm,ym,3);
298        o->m_pos.x+=new_xm;
299        o->m_pos.y+=ym;
300          } else
301          {
302        o->direction=0-o->direction;
303        o->set_aistate(ANT_JUMP);
304          }
305        } else
306          o->m_pos.x+=new_xm;
307        new_xm=0;
308        ym=10;       // see if we should fall
309        o->try_move(o->m_pos.x,o->m_pos.y,new_xm,ym,3);
310        if (ym==10)
311          o->set_aistate(ANT_FALL_DOWN);
312        else o->m_pos.y+=ym;
313      }
314    } else
315    {
316      o->direction=o->m_pos.x>b->m_pos.x ? -1 : 1;
317      o->set_aistate(ANT_LANDING);
318    }
319      }
320    } break;
321
322    case ANT_POUNCE_WAIT :
323    {
324      if (!ant_dodge(o))
325      {
326    o->set_state((character_state)S_pounce_wait);
327    if (o->aistate_time()>alien_wait_time())
328    {
329      the_game->play_sound(S_ASLASH_SND,127,o->m_pos.x,o->m_pos.y);
330      o->set_state(stopped);
331      o->set_aistate(ANT_JUMP);
332    }
333      }
334    } break;
335
336    case ANT_JUMP :
337    {
338      o->lvars[ANT_need_to_dodge]=0;
339      if (o->move(o->direction,-1,0)&BLOCKED_DOWN)
340      o->set_aistate(ANT_RUNNING);
341    } break;
342    case ANT_FIRE :
343    {
344      if (!ant_dodge(o))
345      {
346    if (o->state==S_fire_wait)
347    {
348      if (!o->next_picture() || symbol_value(l_difficulty)==l_extreme)
349      {
350        if (player_list->next)
351        b=g_current_level->attacker(current_object);
352        else b=player_list->m_focus;
353        fire_at_player(o,b);
354        o->set_state(stopped);
355        o->set_aistate(ANT_RUNNING);
356      }
357    } else o->set_state((character_state)S_fire_wait);
358      }
359    } break;
360    case ANT_JUMP_ROOF :
361    {
362      o->lvars[ANT_need_to_dodge]=0;
363      o->set_state((character_state)S_jump_up);
364//      o->m_vel.y += 1;
365      o->m_accel.x = 0;
366      int32_t xv = 0, yv = o->m_vel.y;
367      o->m_pos.y-=31;
368      o->try_move(o->m_pos.x,o->m_pos.y,xv,yv,1);
369      o->m_pos.y+=31+yv;
370      if (yv != o->m_vel.y)
371      {
372        if (o->m_vel.y > 0)
373        {
374          o->set_state(stopped);
375          o->set_aistate(ANT_RUNNING);
376        }
377        else
378        {
379          o->set_state((character_state)S_top_walk);
380          o->set_aistate(ANT_ROOF_WALK);
381        }
382        o->m_vel.y = 0;
383      }
384    } break;
385    case ANT_ROOF_WALK :
386    {
387      if (player_list->next)
388      b=g_current_level->attacker(current_object);
389      else b=player_list->m_focus;
390      scream_check(o,b);
391      if ((rand(8) == 0 && lol::abs(o->m_pos.x-b->m_pos.x)<10 && o->m_pos.y<b->m_pos.y) ||
392      o->lvars[ANT_need_to_dodge]==1)
393      {
394    o->set_gravity(1);
395    o->set_state(run_jump);
396    o->set_aistate(ANT_JUMP);
397    ant_ai();
398      }
399      else
400      {
401    if ((o->m_pos.x>b->m_pos.x && o->direction>0) || (o->m_pos.x<b->m_pos.x && o->direction<0))
402    o->direction=-o->direction;
403    else if (lol::abs(o->m_pos.x-b->m_pos.x)<120 && rand(4) == 0)
404    {
405      o->set_state((character_state)S_ceil_fire);
406      o->set_aistate(ANT_CEIL_SHOOT);
407      ant_ai();
408    } else
409    {
410      int speed=o->direction>0 ? get_ability(o->otype,run_top_speed) :
411                -get_ability(o->otype,run_top_speed);
412      if (can_see(o,o->m_pos.x,o->m_pos.y-31,o->m_pos.x+speed,o->m_pos.y-31) &&
413          !can_see(o,o->m_pos.x+speed,o->m_pos.y-31,o->m_pos.x+speed,o->m_pos.y-32))
414      {
415        o->m_pos.x+=speed;
416        if (!o->next_picture()) o->set_state((character_state)S_top_walk);
417
418      } else o->set_aistate(ANT_FALL_DOWN);
419    }
420      }
421    } break;
422    case ANT_CEIL_SHOOT :
423    {
424      if (!o->next_picture())
425      {
426    if (player_list->next)
427      b=g_current_level->attacker(current_object);
428    else b=player_list->m_focus;
429    fire_at_player(o,b);
430    o->set_state((character_state)S_top_walk);
431    o->set_aistate(ANT_ROOF_WALK);
432      }
433    } break;
434  }
435
436
437
438  return true_symbol;
439}
440
441
442
443void fade_in(AImage *im, int steps);
444void fade_out(int steps);
445
446void show_stats()
447{
448  if (g_current_level)
449  {
450    fade_out(8);
451    wm->SetMousePos(ivec2(0, 0));
452    main_screen->clear();
453    AImage *im=cache.img(cache.reg("art/frame.spe","end_level_screen",SPEC_IMAGE,1));
454    main_screen->PutImage(im, ivec2(0, 0));
455
456
457    int x1=im->Size().x+1,y1=0,x2=xres,y2=main_screen->Size().y;
458    fade_in(NULL,16);
459
460    String const &name = g_current_level->GetOriginalName();
461    ASSERT(name.Count() > (int)strlen(".spe") + 2,
462           "invalid level name %s", name.C());
463    String digits = name.Sub(name.Count() - strlen(".spe") - 2, 2);
464
465    String msg = symbol_str("lev_complete");
466    msg += " : ";
467    if (isdigit(digits[0]) && isdigit(digits[1]))
468    {
469        if (digits[0] != '0')
470            msg += digits[0];
471        msg += digits[1];
472    }
473    else
474        msg += name;
475
476    int w = wm->font()->Size().x * msg.Count(),
477        h = wm->font()->Size().y;
478    int x=(x1+x2)/2-w/2,y=(y1+y2)/2-h/2;
479    main_screen->Bar(ivec2(x - 10, y - 10), ivec2(x + w + 10, y + h + 10),
480                     wm->bright_color());
481    main_screen->Bar(ivec2(x - 9, y - 9), ivec2(x + w + 9, y + h + 9),
482                     wm->medium_color());
483
484    wm->font()->PutString(main_screen, ivec2(x + 1, y + 1), msg.C(), wm->dark_color());
485    wm->font()->PutString(main_screen, ivec2(x, y), msg.C(), wm->bright_color());
486    wm->flush_screen();
487    Timer now; now.Wait(0.5);
488  }
489}
490
Note: See TracBrowser for help on using the repository browser.