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