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, by
8  *  Jonathan Clark, or by Sam Hocevar.
9  */
10 
11 #if defined 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.h"
21 #include "lisp_gc.h"
22 #include "compiled.h"
23 #include "objects.h"
24 #include "level.h"
25 #include "game.h"
26 #include "jrand.h"
27 #include "clisp.h"
28 #include "dev.h"
29 
30 enum {  ANT_need_to_dodge,     // ant vars
31     ANT_no_see_time,
32     ANT_hide_flag };
33 
can_see(game_object * o,int32_t x1,int32_t y1,int32_t x2,int32_t y2)34 int can_see(game_object *o, int32_t x1, int32_t y1, int32_t x2, int32_t y2)
35 {
36   int32_t nx2=x2,ny2=y2;
37   current_level->foreground_intersect(x1,y1,x2,y2);
38   if (x2!=nx2 || y2!=ny2) return 0;
39 
40   current_level->boundary_setback(o,x1,y1,x2,y2);
41   return (x2==nx2 && y2==ny2);
42 }
43 
44 
45 // if we first saw the player or it's been a while since we've seen the player then do a scream
scream_check(game_object * o,game_object * b)46 static void scream_check(game_object *o, game_object *b)
47 {
48   if (can_see(o,o->x,o->y,b->x,b->y))
49   {
50     if (o->lvars[ANT_no_see_time]==0 || o->lvars[ANT_no_see_time]>20)
51       the_game->play_sound(S_ASCREAM_SND,127,o->x,o->y);
52     o->lvars[ANT_no_see_time]=1;
53   } else o->lvars[ANT_no_see_time]++;
54 }
55 
ant_congestion(game_object * o)56 static int ant_congestion(game_object *o)
57 {
58   for (game_object *d=current_level->first_active_object(); d; d=d->next_active)
59   {
60     if (d->otype==o->otype && abs(o->x-d->x)<30 && abs(o->x-d->y)<20) return 1;
61   }
62   return 0;
63 }
64 
ant_dodge(game_object * o)65 static int ant_dodge(game_object *o)
66 {
67   if (o->lvars[ANT_need_to_dodge]==1)
68   {
69     o->lvars[ANT_need_to_dodge]=0;
70     if ((jrand()%2)==0)
71     {
72       o->set_state(stopped);
73       o->set_aistate(ANT_JUMP);
74       if (!can_see(o,o->x,o->y,o->x,o->y-120))   // is there a roof above?
75       {
76     o->set_yvel(-17);
77     o->set_xvel(0);
78     o->set_aistate(ANT_JUMP_ROOF);
79     ant_ai();
80       } else
81       {
82     o->set_yvel(-12);
83     if (o->direction>0)
84       o->set_xvel(22);
85     else o->set_xvel(-22);
86     o->set_aistate(ANT_JUMP);
87       }
88     }
89     return 1;
90   } else return 0;
91 }
92 
alien_wait_time()93 static int alien_wait_time()
94 {
95   void *v=symbol_value(l_difficulty);
96   if (v==l_easy)
97     return 6;
98   else if (v==l_medium)
99    return 4;
100   else if (v==l_hard)
101     return 2;
102   else return 1;
103 }
104 
can_hit_player(game_object * o,game_object * b)105 static int can_hit_player(game_object *o, game_object *b)
106 {
107   return can_see(o,o->x+(o->direction>0?15:-15),o->y-15,b->x,b->y-15);
108 }
109 
fire_at_player(game_object * o,game_object * b)110 static void fire_at_player(game_object *o, game_object *b)
111 {
112   int32_t firex=o->x+(o->direction>0?15:-15),firey=o->y-15,
113           playerx=b->x+b->xvel()*8,playery=b->y-15+b->yvel()*2;
114   if (can_see(o,o->x,o->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 
ant_ai()130 void *ant_ai()
131 {
132   game_object *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 ((jrand()%128)==0) the_game->play_sound(S_SCARE_SND,127,o->x,o->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=current_level->attacker(current_object);
175     else b=player_list->focus;
176     if (abs(b->x-o->x)<130 && (o->y<b->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 ((jrand()%128)==0) the_game->play_sound(S_SCARE_SND,127,o->x,o->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=current_level->attacker(current_object);
207       else b=player_list->focus;
208       if (abs(b->x-o->x)<130 && (o->y<b->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=current_level->attacker(current_object);
226       else b=player_list->focus;
227 
228       scream_check(o,b);
229       int ret=o->mover(0,0,0);
230       if ((ret&BLOCKED_DOWN) || !can_see(o,o->x,o->y,o->x,o->y+1))
231       {
232     o->set_state((character_state)S_landing);
233     the_game->play_sound(S_ALAND_SND,127,o->x,o->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->x,o->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=current_level->attacker(current_object);
260       else b=player_list->focus;
261       scream_check(o,b);
262 
263 
264       if ((jrand()%16)==0)
265       o->lvars[ANT_need_to_dodge]=1;
266       if (!ant_dodge(o))
267       {
268     if ((o->x>b->x && o->direction==-1) || (o->x<b->x && o->direction==1))
269     {
270       o->next_picture();
271       if ((jrand()%4)==0 && abs(o->x-b->x)<180 && abs(o->y-b->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 (abs(o->x-b->x)<100 && abs(o->y-b->y)<10 && (jrand()%4)==0)
276         o->set_aistate(ANT_POUNCE_WAIT);
277       else if (abs(o->x-b->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->x,o->y,new_xm,ym,3);
286         if (new_xm!=xm)    // blocked, see if we can climb ramp
287         {
288           new_xm=xm;
289           ym=-abs(xm);
290           o->try_move(o->x,o->y,new_xm,ym,3);
291           if (new_xm==xm)
292           {
293         o->x+=new_xm;
294         o->y+=ym;
295         new_xm=0;
296         ym=abs(xm);      // now get back on the ground
297         o->try_move(o->x,o->y,new_xm,ym,3);
298         o->x+=new_xm;
299         o->y+=ym;
300           } else
301           {
302         o->direction=0-o->direction;
303         o->set_aistate(ANT_JUMP);
304           }
305         } else
306           o->x+=new_xm;
307         new_xm=0;
308         ym=10;       // see if we should fall
309         o->try_move(o->x,o->y,new_xm,ym,3);
310         if (ym==10)
311           o->set_aistate(ANT_FALL_DOWN);
312         else o->y+=ym;
313       }
314     } else
315     {
316       o->direction=o->x>b->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->x,o->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=current_level->attacker(current_object);
352         else b=player_list->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->set_yvel(o->yvel()+1);
365       o->set_xacel(0);
366       int32_t xv=0,yv=o->yvel();
367       o->y-=31;
368       o->try_move(o->x,o->y,xv,yv,1);
369       o->y+=31+yv;
370       if (yv!=o->yvel())
371       {
372     if (o->yvel()>0)
373     {
374       o->set_state(stopped);
375       o->set_aistate(ANT_RUNNING);
376     } else
377     {
378       o->set_state((character_state)S_top_walk);
379       o->set_aistate(ANT_ROOF_WALK);
380     }
381     o->set_yvel(0);
382       }
383     } break;
384     case ANT_ROOF_WALK :
385     {
386       if (player_list->next)
387       b=current_level->attacker(current_object);
388       else b=player_list->focus;
389       scream_check(o,b);
390       if (((jrand()%8)==0 && abs(o->x-b->x)<10 && o->y<b->y) ||
391       o->lvars[ANT_need_to_dodge]==1)
392       {
393     o->set_gravity(1);
394     o->set_state(run_jump);
395     o->set_aistate(ANT_JUMP);
396     ant_ai();
397       }
398       else
399       {
400     if ((o->x>b->x && o->direction>0) || (o->x<b->x && o->direction<0))
401     o->direction=-o->direction;
402     else if (abs(o->x-b->x)<120 && (jrand()%4)==0)
403     {
404       o->set_state((character_state)S_ceil_fire);
405       o->set_aistate(ANT_CEIL_SHOOT);
406       ant_ai();
407     } else
408     {
409       int speed=o->direction>0 ? get_ability(o->otype,run_top_speed) :
410                 -get_ability(o->otype,run_top_speed);
411       if (can_see(o,o->x,o->y-31,o->x+speed,o->y-31) &&
412           !can_see(o,o->x+speed,o->y-31,o->x+speed,o->y-32))
413       {
414         o->x+=speed;
415         if (!o->next_picture()) o->set_state((character_state)S_top_walk);
416 
417       } else o->set_aistate(ANT_FALL_DOWN);
418     }
419       }
420     } break;
421     case ANT_CEIL_SHOOT :
422     {
423       if (!o->next_picture())
424       {
425     if (player_list->next)
426       b=current_level->attacker(current_object);
427     else b=player_list->focus;
428     fire_at_player(o,b);
429     o->set_state((character_state)S_top_walk);
430     o->set_aistate(ANT_ROOF_WALK);
431       }
432     } break;
433   }
434 
435 
436 
437   return true_symbol;
438 }
439 
440 
441 
442 void fade_in(image *im, int steps);
443 void fade_out(int steps);
444 
show_stats()445 void show_stats()
446 {
447   if (current_level)
448   {
449     fade_out(8);
450     wm->set_mouse_position(0,0);
451     screen->clear();
452     image *im=cache.img(cache.reg("art/frame.spe","end_level_screen",SPEC_IMAGE,1));
453     im->put_image(screen,0,0);
454 
455 
456     int x1=im->Size().x+1,y1=0,x2=xres,y2=screen->Size().y;
457     fade_in(NULL,16);
458 
459     char name[50];
460     strcpy(name,current_level->original_name());
461     char dig1=name[strlen(name)-strlen(".spe")-2];
462     char dig2=name[strlen(name)-strlen(".spe")-1];
463 
464 
465     char msg[50];
466 
467     if (isdigit(dig1) && isdigit(dig2))
468     {
469       if (dig1!='0')
470         sprintf(msg,"%s : %c%c",symbol_str("lev_complete"),dig1,dig2);
471       else
472         sprintf(msg,"%s : %c",symbol_str("lev_complete"),dig2);
473     } else sprintf(msg,"%s : %s",symbol_str("lev_complete"),current_level->original_name());
474 
475     int w=wm->font()->width()*strlen(msg),h=wm->font()->height();
476     int x=(x1+x2)/2-w/2,y=(y1+y2)/2-h/2;
477     screen->bar(x-10,y-10,x+w+10,y+h+10,wm->bright_color());
478     screen->bar(x-9,y-9,x+w+9,y+h+9,wm->medium_color());
479 
480     wm->font()->put_string(screen,x+1,y+1,msg,wm->dark_color());
481     wm->font()->put_string(screen,x,y,msg,wm->bright_color());
482     wm->flush_screen();
483     Timer now; now.WaitMs(500);
484   }
485 }
486 
487