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 "common.h"
16 
17 #include "lisp.h"
18 #include "lisp_gc.h"
19 #include "compiled.h"
20 #include "objects.h"
21 #include "level.h"
22 #include "game.h"
23 #include "jrand.h"
24 #include "clisp.h"
25 #include "ant.h"
26 #include "dev.h"
27 
28 enum { point_angle, fire_delay1 };
29 
30 #define SHOTGUN  10
31 #define GRENADE  2
32 #define ROCKET   3
33 #define PLASMA   4
34 #define FIREBOMB 5
35 #define DFRIS    6
36 #define LSABER   7
37 
38 signed char small_fire_off[24*2]=  // x & y offset from character to end of gun.
39   { 17,20,     // 1
40     17,23,     // 2
41     17,28,
42     15,33,
43     11,39,     // 5
44     7,43,
45     -3,44,     // 7
46     -10,42,
47     -16,39,
48     -20,34,
49     -20,28,
50     -20,25,
51     -19,20,
52     -19,16,
53     -16,14,
54     -14,11,
55     -11,9,
56     -7,8,
57     -3,8,
58     2,8,
59     6,9,
60     10,10,
61     14,13,
62     16,15 };
63 
64 signed char large_fire_off[24*2]=
65   { 18,25,
66     17,30,
67     15,34,
68     14,36,
69     10,39,
70     7,41,
71     4,42,
72     -3,41,
73     -8,39,
74     -11,37,
75     -14,33,
76     -16,30,
77     -18,25,
78     -17,21,
79     -14,17,
80     -11,15,
81     -7,13,
82     -4,12,
83     3,12,
84     9,12,
85     12,15,
86     14,16,
87     15,18,
88     16,21 };
89 
90 
91 enum { in_climbing_area,
92     disable_top_draw,
93     just_hit,
94     ship_pan_x,
95     special_power,
96     used_special_power,
97     last1_x, last1_y,
98     last2_x, last2_y,
99     has_saved_this_level,
100     r_ramp, g_ramp, b_ramp,
101         is_teleporting,
102        just_fired};
103 
104 
105 enum { sgb_speed,
106        sgb_angle,
107        sgb_lastx,
108        sgb_lasty,
109        sgb_bright_color,
110        sgb_medium_color,
111        sgb_lifetime };
112 
113 enum { NO_POWER,
114        FAST_POWER,
115        FLY_POWER,
116        SNEAKY_POWER,
117        HEALTH_POWER } ;
118 
119 enum { top_point_angle,
120        top_fire_delay1,
121        top_just_fired };
122 
angle_diff(int a1,int a2)123 inline int angle_diff(int a1, int a2)
124 {
125   if (a1<a2)
126   { if ((a2-a1)<180)
127       return a2-a1;
128     else return 360-a2+a1;
129   } else
130   {
131     if ((a1-a2)<180)
132       return a1-a2;
133     else return 360-a1+a2;
134   }
135   return 0;
136 }
137 
top_ai()138 void *top_ai()
139 {
140   game_object *o=current_object;
141   if (o->total_objects())            // make sure we are linked to the main character
142   {
143     game_object *q=o->get_object(0);
144 
145     view *v=q->controller();
146     if (v)
147     {
148       if (!v->freeze_time)
149       {
150     o->direction=1;                 // always face right
151 
152     if (q->direction<0)
153           q->x+=4;
154     int i;
155     signed char *fire_off=o->otype==S_DFRIS_TOP ? large_fire_off :
156                                     (o->otype==S_ROCKET_TOP ? large_fire_off :
157                      (o->otype==S_BFG_TOP ? large_fire_off : small_fire_off));
158     signed char *f=fire_off,*fb=NULL;
159     int best_diff=200,best_num=0;
160     int iy=f[1],ix=f[6*2];
161 
162     int best_angle=lisp_atan2(q->y-iy-v->pointer_y,v->pointer_x-q->x-ix);
163     for (i=0; i<24; i++,f+=2)             // check all the angles to see which would best fit animation wise
164     {
165       int this_angle=lisp_atan2(f[1]-iy,f[0]-ix);
166       int this_diff=angle_diff(this_angle,best_angle);
167       if (this_diff<best_diff)
168       {
169         best_diff=this_diff;
170         best_num=i;
171         fb=f;
172       }
173     }
174 
175 
176     // if the pointer is too close to the player go with the angle shown, not the angle through the pointer
177     if (abs(q->y-fb[1]-v->pointer_y)<45 && abs(v->pointer_x-q->x+fb[0])<40)
178       o->lvars[point_angle]=lisp_atan2(fb[1]-iy,fb[0]-ix);
179     else
180       o->lvars[point_angle]=lisp_atan2(q->y-fb[1]-v->pointer_y,v->pointer_x-(q->x+fb[0]));
181 
182 
183     if (q->direction<0)
184           q->x-=4;
185 
186     o->x=q->x;
187     o->y=q->y+29-q->picture()->Size().y;
188 
189     rand_on+=o->lvars[point_angle];
190     o->current_frame=best_num;
191 
192 
193     if (o->lvars[fire_delay1])
194       o->lvars[fire_delay1]--;
195 
196     o->otype=weapon_types[v->current_weapon];  // switch to correct top part
197       }
198     }
199   }
200   return true_symbol;
201 }
202 
203 
player_fire_weapon(game_object * o,int type,game_object * target,int angle,signed char * fire_off)204 static int player_fire_weapon(game_object *o, int type, game_object *target, int angle, signed char *fire_off)
205 {
206 
207   if (!o->total_objects()) return 0;
208   game_object *other=o->get_object(0);
209   int ox=other->x,oy=other->y;
210   if (other->direction<0) other->x+=4;
211 
212   int firex=other->x+fire_off[o->current_frame*2];
213   int firey=other->y-fire_off[o->current_frame*2+1];
214 
215 
216 
217   // fire try to move up to gun level
218 
219   int32_t x2=o->x,y2=firey;
220 //  current_level->foreground_intersect(other->x,other->y,x2,y2);      // find first location we can actuall "see"
221 //  current_level->all_boundary_setback(o,other->x,other->y,x2,y2);       // to make we don't fire through walls
222   other->y=y2;
223 
224   if (other->y==firey)             // now try to move out to end of gun if we were not blocked above
225   {
226     x2=firex;
227     current_level->foreground_intersect(other->x,other->y,x2,y2);      // find first location we can actuall "see"
228     current_level->all_boundary_setback(other,other->x,other->y,x2,y2);       // to make we don't fire through walls
229     o->x=x2;
230   }
231 
232   void *list=NULL;
233   PtrRef r1(list);
234   push_onto_list(LPointer::Create(target),list);
235   push_onto_list(LNumber::Create(angle),list);
236   push_onto_list(LNumber::Create(y2),list);
237   push_onto_list(LNumber::Create(x2),list);
238   push_onto_list(LNumber::Create(type),list);
239   push_onto_list(LPointer::Create(o->get_object(0)),list);
240   ((LSymbol *)l_fire_object)->EvalFunction(list);
241   o->lvars[top_just_fired]=1;
242   other->lvars[just_fired]=1;
243   other->x=ox;
244   other->y=oy;
245 
246   return 1;
247 }
248 
249 
250 
laser_ufun(void * args)251 void *laser_ufun(void *args)
252 {
253   game_object *o=current_object;
254   PtrRef r1(args);
255   void *signal=CAR(args);  args=CDR(args);
256   void *ret=NULL;
257 
258   if (signal==l_FIRE)
259   {
260     if (!o->lvars[fire_delay1])                   // make sur we are not waiting of previous fire
261     {
262       int32_t value=lnumber_value(CAR(args)->Eval());
263       if (value)                                   // do we have ammo ?
264       {
265     o->lvars[fire_delay1]=3;
266     if (player_fire_weapon(o,SHOTGUN,NULL,o->lvars[point_angle],small_fire_off))
267           ret=LNumber::Create(-1);
268     else ret=LNumber::Create(0);
269       } else
270       {
271     o->lvars[fire_delay1]=5;                  // no ammo, set large fire delay for next shot
272     player_fire_weapon(o,SHOTGUN,NULL,o->lvars[point_angle],small_fire_off);
273     ret=LNumber::Create(0);
274       }
275     } else ret=LNumber::Create(0);                // can't fire yet, return 0 ammo subtract
276   }
277   return ret;
278 }
279 
280 
ammo_type(int otype)281 static int ammo_type(int otype)
282 {
283   if (otype==S_GRENADE_TOP)
284     return GRENADE;
285   else if (otype==S_FIREBOMB_TOP)
286     return FIREBOMB;
287   else if (otype==S_DFRIS_TOP)
288     return DFRIS;
289   else return SHOTGUN;
290 }
291 
292 
top_ufun(void * args)293 void *top_ufun(void *args)                       // generic top character ai GRENADE && FIREBOMB
294 {
295   game_object *o=current_object;
296   PtrRef r1(args);
297   void *signal=CAR(args);  args=CDR(args);
298   void *ret=NULL;
299 
300   if (signal==l_FIRE)
301   {
302     if (!o->lvars[fire_delay1])                   // make sur we are not waiting of previous fire
303     {
304       int32_t value=lnumber_value(CAR(args)->Eval());
305       if (value)                                   // do we have ammo ?
306       {
307     o->lvars[fire_delay1]=6;
308     if (player_fire_weapon(o,ammo_type(o->otype),NULL,o->lvars[point_angle],
309                    o->otype==DFRIS ? large_fire_off : small_fire_off ))
310           ret=LNumber::Create(-1);
311     else ret=LNumber::Create(0);
312       } else ret=LNumber::Create(0);
313     } else ret=LNumber::Create(0);                // can't fire yet, return 0 ammo subtract
314   }
315   return ret;
316 }
317 
318 static int climb_handler(game_object *, int xm, int ym, int but);
319 
plaser_ufun(void * args)320 void *plaser_ufun(void *args)
321 {
322   game_object *o=current_object;
323   PtrRef r1(args);
324   void *signal=CAR(args);  args=CDR(args);
325   void *ret=NULL;
326 
327   if (signal==l_FIRE)
328   {
329     if (!o->lvars[fire_delay1])                   // make sur we are not waiting of previous fire
330     {
331       int32_t value=lnumber_value(CAR(args)->Eval());
332       if (value)                                   // do we have ammo ?
333       {
334     o->lvars[fire_delay1]=2;
335     if (player_fire_weapon(o,PLASMA,NULL,o->lvars[point_angle],small_fire_off))
336           ret=LNumber::Create(-1);
337     else ret=LNumber::Create(0);
338       } else ret=LNumber::Create(0);
339     } else ret=LNumber::Create(0);                // can't fire yet, return 0 ammo subtract
340   }
341   return ret;
342 }
343 
lsaber_ufun(void * args)344 void *lsaber_ufun(void *args)
345 {
346   game_object *o=current_object;
347   PtrRef r1(args);
348   void *signal=CAR(args);  args=CDR(args);
349   void *ret=NULL;
350 
351   if (signal==l_FIRE)
352   {
353     if (!o->lvars[fire_delay1])                   // make sur we are not waiting of previous fire
354     {
355       int32_t value=lnumber_value(CAR(args)->Eval());
356       if (value)                                   // do we have ammo ?
357       {
358     o->lvars[fire_delay1]=1;
359     if (player_fire_weapon(o,LSABER,NULL,o->lvars[point_angle]+(current_level->tick_counter()&7)-8,
360                    small_fire_off))
361           ret=LNumber::Create(-1);
362     else ret=LNumber::Create(0);
363       } else ret=LNumber::Create(0);
364     } else ret=LNumber::Create(0);                // can't fire yet, return 0 ammo subtract
365   }
366   return ret;
367 }
368 
369 
370 
player_rocket_ufun(void * args)371 void *player_rocket_ufun(void *args)
372 {
373   game_object *o=current_object;
374   PtrRef r1(args);
375   void *signal=CAR(args);  args=CDR(args);
376   void *ret=NULL;
377   int xd,yd,cl=0xfffffff,d;
378   if (signal==l_FIRE)
379   {
380     if (!o->lvars[fire_delay1])                   // make sur we are not waiting of previous fire
381     {
382       int32_t value=lnumber_value(CAR(args)->Eval());
383       if (value)                                   // do we have ammo ?
384       {
385     o->lvars[fire_delay1]=6;
386     game_object *target=NULL,*p,*bot=o->total_objects() ? o->get_object(0) : 0;
387     if (bad_guy_array)
388     {
389       game_object *other=current_object->total_objects() ? current_object->get_object(0) : 0;
390       for (p=current_level->first_active_object(); p; p=p->next_active)
391       {
392         xd=abs(p->x-o->x);
393         yd=abs(p->y-o->y);
394         if (xd<160 && yd<130 && bad_guy_array[p->otype] && p!=other)
395         {
396           if (p->targetable() &&
397           !(p->otype==S_ROCKET && p->total_objects() && p->get_object(0)==bot))  // don't track onto own missles
398           {
399         d=xd*xd+yd*yd;
400         if (d<cl)
401         {
402           cl=d;
403           target=p;
404         }
405           }
406         }
407       }
408     }
409     if (player_fire_weapon(o,ROCKET,target,o->lvars[point_angle],large_fire_off))
410           ret=LNumber::Create(-1);
411     else ret=LNumber::Create(0);
412 
413       } else ret=LNumber::Create(0);
414     } else ret=LNumber::Create(0);                // can't fire yet, return 0 ammo subtract
415   }
416   return ret;
417 }
418 
player_move(game_object * o,int xm,int ym,int but)419 static int player_move(game_object *o, int xm, int ym, int but)
420 {
421   if (!o->lvars[in_climbing_area])
422   {
423     if (o->state==S_climbing)
424     {
425       o->set_gravity(1);
426       o->set_state(run_jump_fall);
427     }
428     o->next_picture();
429     return o->mover(xm,ym,but);
430   } else return climb_handler(o,xm,ym,but);
431 
432 }
433 
434 
undo_special_power(game_object * o)435 static void undo_special_power(game_object *o)
436 {
437   switch (o->lvars[special_power])
438   {
439     case SNEAKY_POWER :
440     {
441       if (o->lvars[used_special_power]>0)
442         o->lvars[used_special_power]--;
443     } break;
444     case FAST_POWER :
445     {
446       o->lvars[used_special_power]=0;
447     } break;
448   }
449 }
450 
do_special_power(game_object * o,int xm,int ym,int but,game_object * top)451 static void do_special_power(game_object *o, int xm, int ym, int but, game_object *top)
452 {
453   switch (o->lvars[special_power])
454   {
455     case FLY_POWER :
456     {
457       game_object *cloud=create(S_CLOUD,o->x+o->direction*-10,o->y+jrand()%5);
458       if (current_level)
459         current_level->add_object(cloud);
460       o->set_state(run_jump);
461       o->set_gravity(1);
462       o->set_yacel(0);
463       if (o->yvel()>0) o->set_yvel(o->yvel()/2);
464       if (ym<0)
465         o->set_yvel(o->yvel()-3);
466       else
467         o->set_yvel(o->yvel()-2);
468       the_game->play_sound(S_FLY_SND,32,o->x,o->y);
469     } break;
470     case FAST_POWER :
471     {
472       if ((current_level->tick_counter()%16)==0)
473       the_game->play_sound(S_SPEED_SND,100,o->x,o->y);
474 
475       o->lvars[used_special_power]=1;
476       o->lvars[last1_x]=o->x;
477       o->lvars[last1_y]=o->y;
478       int32_t oyvel=o->yvel();
479       int in=o->lvars[in_climbing_area];
480 
481       player_move(o,xm,ym,but);
482       if (ym<0 && !oyvel && o->yvel()<0)             // if they just jumped, make them go higher
483       o->set_yvel(o->yvel()+o->yvel()/3);
484       o->lvars[in_climbing_area]=in;
485 
486       o->lvars[last2_x]=o->x;
487       o->lvars[last2_x]=o->y;
488     } break;
489     case SNEAKY_POWER :
490     {
491       if (o->lvars[used_special_power]<15)
492         o->lvars[used_special_power]++;
493     } break;
494   }
495 }
496 
climb_off_handler(game_object * o)497 static int climb_off_handler(game_object *o)
498 {
499   if (o->next_picture())
500     o->controller()->pan_y-=4;
501   else
502   {
503     o->y-=28;
504     o->controller()->pan_y+=28;
505     o->controller()->last_y-=28;
506     o->set_state(stopped);
507   }
508   return 0;
509 }
510 
511 
climb_on_handler(game_object * o)512 static int climb_on_handler(game_object *o)
513 {
514   if (o->next_picture())
515     o->controller()->pan_y+=4;
516   else
517     o->set_state((character_state)S_climbing);
518   return 0;
519 }
520 
climb_handler(game_object * o,int xm,int ym,int but)521 static int climb_handler(game_object *o, int xm, int ym, int but)
522 {
523   int yd=o->lvars[in_climbing_area];  // see how from the top we are
524   o->lvars[in_climbing_area]=0;          // set 0, ladders will set back to proper if still in area
525   if (o->state==S_climb_off)
526     climb_off_handler(o);
527   else if (o->state==S_climb_on)
528     climb_on_handler(o);
529   else
530   {
531     if (o->state==S_climbing)
532     {
533       if (ym>0)                       // going down
534       {
535 
536     if (o->current_frame==0) o->current_frame=9;
537       o->current_frame--;
538 
539 /*    if (o->lvars[special_power]==FAST_POWER)
540     {
541       int32_t xv=0,yv=4;
542       o->try_move(o->x,o->y,xv,yv,1);
543       if (yv==4)
544         o->y+=3;
545       else
546       {
547         o->set_gravity(1);
548           o->set_state(run_jump_fall);
549       }
550     }
551     else */ o->y+=3;
552 
553 
554       } else if (ym<0)
555       {
556     if (yd<32)
557       o->set_state((character_state)S_climb_off);
558     else
559     {
560       if (!o->next_picture()) o->set_state((character_state)S_climbing);
561       o->y-=3;
562     }
563       }
564       if (xm)                     // trying to get off the ladder, check to see if that's ok
565       {
566     int32_t x2=0,y2=-20;
567     o->try_move(o->x,o->y,x2,y2,3);
568     if (y2==-20)
569     {
570       o->set_gravity(1);
571       if (ym>=0)
572         o->set_state(run_jump_fall);
573       else
574       { o->set_state(run_jump);
575         o->set_yvel(get_ability(o->otype,jump_yvel));
576       }
577     }
578       }
579     }  else if (ym>0 && yd<10)
580     {
581       o->y+=28;
582       o->controller()->pan_y-=28;
583       o->controller()->last_y+=28;
584       o->set_state((character_state)S_climb_on);
585     }
586     else if (o->yvel()>=0 && (ym>0 || (ym<0 && yd>8)))
587     {
588       o->set_state((character_state)S_climbing);
589       o->set_gravity(0);
590       o->set_xvel(0);
591       o->set_yvel(0);
592       o->set_xacel(0);
593       o->set_yacel(0);
594       return 0;
595     } else
596     {
597       o->next_picture();
598       return o->mover(xm,ym,but);
599     }
600   }
601   return 0;
602 }
603 
604 
cop_mover(int xm,int ym,int but)605 void *cop_mover(int xm, int ym, int but)
606 {
607 
608   int ret=0;
609   game_object *o=current_object,*top;
610   if (o->controller() && o->controller()->freeze_time)
611   {
612     o->controller()->freeze_time--;
613     if (but || o->controller()->key_down(JK_SPACE) || o->controller()->key_down(JK_ENTER))
614       o->controller()->freeze_time=0;
615   }
616   else
617   {
618     if (!o->total_objects())                  // if no top create one
619     {
620       top=create(S_MGUN_TOP,o->x,o->y,0,0);
621       current_level->add_object_after(top,o);
622       o->add_object(top);
623       top->add_object(o);
624     } else top=o->get_object(0);
625 
626     if (o->yvel()>10)
627     {
628       o->set_yacel(0);
629       o->set_yvel(o->yvel()-1);            // terminal velocity
630     }
631 
632     if (o->aistate()==0)  // just started, wait for button
633     {
634       o->set_aistate(1);
635     } else if (o->aistate()==1)         // normal play
636     {
637       if (o->hp()==0)
638       {
639     o->set_aistate(2);                // go to deing state
640     o->set_state(dead);
641       }
642       else
643       {
644     if (o->hp()<40 && (current_level->tick_counter()%16)==0) // if low on health play heart beat
645       the_game->play_sound(S_LOW_HEALTH_SND,127,o->x,o->y);
646     else if (o->hp()<15 && (current_level->tick_counter()%8)==0) // if low on health play heart beat
647       the_game->play_sound(S_LOW_HEALTH_SND,127,o->x,o->y);
648     else if (o->hp()<7 && (current_level->tick_counter()%4)==0) // if low on health play heart beat
649       the_game->play_sound(S_LOW_HEALTH_SND,127,o->x,o->y);
650 
651     if (but&1)
652         do_special_power(o,xm,ym,but,top);
653     else
654     undo_special_power(o);
655     ret=player_move(o,xm,ym,but);
656     top->x=o->x;
657     top->y=o->y+29-top->picture()->Size().y;
658 
659     if ((but&2) && !o->lvars[is_teleporting] && o->state!=S_climbing && o->state!=S_climb_off)
660     {
661       void *args=NULL;
662       PtrRef r1(args);
663       view *v=o->controller();
664 
665       push_onto_list(LNumber::Create(v->weapon_total(v->current_weapon)),args);
666       push_onto_list(l_FIRE,args);
667 
668       current_object=top;
669       void *ret = ((LSymbol *)figures[top->otype]->get_fun(OFUN_USER_FUN))->EvalFunction(args);
670       current_object=o;
671       v->add_ammo(v->current_weapon,lnumber_value(ret));
672     }
673       }
674     } else if (o->aistate()==3)
675     {
676       if (!o->controller() || o->controller()->key_down(JK_SPACE))
677       {
678         // call the user function to reset the player
679     ((LSymbol *)l_restart_player)->EvalFunction(NULL);
680     o->controller()->reset_player();
681     o->set_aistate(0);
682       } else if (o->controller() && o->controller()->local_player())
683         the_game->show_help(symbol_str("space_cont"));
684 
685     } else o->set_aistate(o->aistate()+1);
686   }
687 
688   return LNumber::Create(ret);
689 }
690 
691 
692 
ladder_ai()693 void *ladder_ai()
694 {
695   view *f=player_list;
696   game_object *o=current_object;
697   if (o->total_objects())
698   {
699     game_object *other=o->get_object(0);
700     for (; f; f=f->next)
701     {
702       int mex=f->focus->x;
703       int mey=f->focus->y;
704 
705       if (o->x<=mex && o->y<=mey && other->x>=mex && other->y>=mey)
706       {
707     if (f->focus->state==S_climbing)
708       f->focus->x=(o->x+other->x)/2;
709         f->focus->lvars[in_climbing_area]=mey-o->y;
710       }
711     }
712   }
713   return true_symbol;
714 }
715 
716 
717 
player_draw(int just_fired_var,int num)718 void *player_draw(int just_fired_var, int num)
719 {
720   game_object *o=current_object;
721   if (num==0)
722   {
723     if (o->lvars[just_fired_var])
724     {
725       o->draw_tint(S_bright_tint);
726       o->lvars[just_fired_var]=0;
727     } else
728       o->drawer();
729   }
730   else
731   {
732     if (o->lvars[just_fired_var])
733     {
734       o->draw_double_tint(lnumber_value(((LArray *)((LSymbol *)l_player_tints)->GetValue())->Get(num)), S_bright_tint);
735       o->lvars[just_fired_var]=0;
736     } else
737       o->draw_tint(lnumber_value(((LArray *)((LSymbol *)l_player_tints)->GetValue())->Get(num)));
738   }
739   return NULL;
740 }
741 
742 
top_draw()743 void *top_draw()
744 {
745   game_object *o=current_object;
746   if (o->total_objects())
747   {
748     game_object *bot=o->get_object(0);
749     if (bot->state==stopped  || bot->state==running ||
750     bot->state==run_jump || bot->state==run_jump_fall ||
751     bot->state==end_run_jump)
752     {
753       int oldy=o->y;
754       o->x=bot->x;
755       if (bot->direction<0)
756         o->x+=4;
757       o->y=bot->y+29-bot->picture()->Size().y;
758 
759       void *ret=NULL;
760       PtrRef r1(ret);
761 
762       push_onto_list(LNumber::Create(bot->get_tint()),ret);
763 
764       if (bot->lvars[special_power]==SNEAKY_POWER)
765       {
766     if (bot->lvars[used_special_power]==0)
767       player_draw(top_just_fired,bot->get_tint());
768     else if (bot->lvars[used_special_power]<15)
769       o->draw_trans(bot->lvars[used_special_power],16);
770     else
771       o->draw_predator();
772       } else
773         ((LSymbol *)l_player_draw)->EvalFunction(ret);
774 
775       o->y=oldy;
776       if (bot->direction<0)
777         o->x-=4;
778     }
779   }
780   return NULL;
781 }
782 
783 
784 
bottom_draw()785 void *bottom_draw()
786 {
787   game_object *o=current_object;
788 
789   if (o->lvars[r_ramp] || o->lvars[g_ramp] || o->lvars[b_ramp])
790   {
791     int r=o->lvars[r_ramp];
792     if (r>7) r-=7;
793     else r=0;
794     o->lvars[r_ramp]=r;
795 
796     int g=o->lvars[g_ramp];
797     if (g>7) g-=7;
798     else g=0;
799     o->lvars[g_ramp]=g;
800 
801     int b=o->lvars[b_ramp];
802     if (b>7) b-=7;
803     else b=0;
804     o->lvars[b_ramp]=b;
805 
806     palette *p=pal->copy();
807     uint8_t *addr=(uint8_t *)p->addr();
808     int ra,ga,ba;
809 
810     for (int i=0; i<256; i++)
811     {
812       ra=(int)*addr+r; if (ra>255) ra=255; else if (ra<0) r=0; *addr=(uint8_t)ra; addr++;
813       ga=(int)*addr+g; if (ga>255) ga=255; else if (ga<0) g=0; *addr=(uint8_t)ga; addr++;
814       ba=(int)*addr+b; if (ba>255) ba=255; else if (ba<0) b=0; *addr=(uint8_t)ba; addr++;
815     }
816     p->load();
817     delete p;
818   }
819 
820   if (o->aistate()>0)
821   {
822     switch (o->lvars[special_power])
823     {
824       case NO_POWER :
825       { player_draw(just_fired,o->get_tint()); } break;
826 
827       case HEALTH_POWER :
828       {
829     player_draw(just_fired,o->get_tint());
830     if (o->controller() && o->controller()->local_player())
831       cache.img(S_health_image)->put_image(screen,o->controller()->cx2-20,
832                         o->controller()->cy1+5,1);
833       } break;
834       case FAST_POWER :
835       {
836     ((LSymbol *)l_draw_fast)->EvalFunction(NULL);
837     int old_state=o->state;
838     switch (o->state)
839     {
840       case stopped : o->state=(character_state)S_fast_stopped; break;
841       case running : o->state=(character_state)S_fast_running; break;
842       case start_run_jump : o->state=(character_state)S_fast_start_run_jump; break;
843       case run_jump : o->state=(character_state)S_fast_run_jump; break;
844       case run_jump_fall : o->state=(character_state)S_fast_run_jump_fall; break;
845       case end_run_jump : o->state=(character_state)S_fast_end_run_jump; break;
846       default: break;
847     }
848 
849     player_draw(just_fired,o->get_tint());
850     o->state=(character_state)old_state;
851     if (o->controller() && o->controller()->local_player())
852       cache.img(S_fast_image)->put_image(screen,o->controller()->cx2-20,
853                         o->controller()->cy1+5,1);
854       } break;
855       case FLY_POWER :
856       {
857     int old_state=o->state;
858     switch (o->state)
859     {
860       case stopped : o->state=(character_state)S_fly_stopped; break;
861       case running : o->state=(character_state)S_fly_running; break;
862       case start_run_jump : o->state=(character_state)S_fly_start_run_jump; break;
863       case run_jump : o->state=(character_state)S_fly_run_jump; break;
864       case run_jump_fall : o->state=(character_state)S_fly_run_jump_fall; break;
865       case end_run_jump : o->state=(character_state)S_fly_end_run_jump; break;
866       default: break;
867     }
868 
869     player_draw(just_fired,o->get_tint());
870     o->state=(character_state)old_state;
871 
872     if (o->controller() && o->controller()->local_player())
873       cache.img(S_fly_image)->put_image(screen,o->controller()->cx2-20,
874                         o->controller()->cy1+5,1);
875       } break;
876       case SNEAKY_POWER :
877       {
878     if (o->lvars[used_special_power]==0)
879       player_draw(just_fired,o->get_tint());
880     else if (o->lvars[used_special_power]<15)
881       o->draw_trans(o->lvars[used_special_power],16);
882     else
883       o->draw_predator();
884 
885     if (o->controller() && o->controller()->local_player())
886       cache.img(S_sneaky_image)->put_image(screen,o->controller()->cx2-20,
887                         o->controller()->cy1+5,1);
888       } break;
889     }
890   }
891   return NULL;
892 }
893 
894 
895 
sgun_ai()896 void *sgun_ai()
897 {
898   game_object *o=current_object;
899 
900   if (o->lvars[sgb_lifetime]==0)
901     return NULL;
902   o->lvars[sgb_lifetime]--;
903 
904   o->lvars[sgb_lastx]=o->x;
905   o->lvars[sgb_lasty]=o->y;
906   o->lvars[sgb_speed]=o->lvars[sgb_speed]*6/5;
907 
908   int32_t ang=o->lvars[sgb_angle];
909   int32_t mag=o->lvars[sgb_speed];
910 
911   int32_t xvel=(lisp_cos(ang))*(mag);
912   current_object->set_xvel(xvel>>16);
913   current_object->set_fxvel((xvel&0xffff)>>8);
914   int32_t yvel=-(lisp_sin(ang))*(mag);
915   current_object->set_yvel(yvel>>16);
916   current_object->set_fyvel((yvel&0xffff)>>8);
917 
918 
919   int whit=0;
920   game_object *who=o->bmove(whit, o->total_objects() ? o->get_object(0) : 0);
921 
922   if (whit || (who && figures[who->otype]->get_cflag(CFLAG_UNACTIVE_SHIELD) && who->total_objects() &&
923            who->get_object(0)->aistate()==0))
924   {
925     o->lvars[sgb_lifetime]=0;
926     game_object *n=create(S_EXPLODE5,o->x+jrand()%4,o->y+jrand()%4);
927     current_level->add_object(n);
928   } else if (who && figures[who->otype]->get_cflag(CFLAG_HURTABLE))
929   {
930     o->lvars[sgb_lifetime]=0;
931     game_object *n=create(S_EXPLODE3,o->x+jrand()%4,o->y+jrand()%4);
932     current_level->add_object(n);
933      who->do_damage(5,o,o->x,o->y,(lisp_cos(ang)*10)>>16,(lisp_sin(ang)*10)>>16);
934   }
935   return true_symbol;
936 }
937 
938 
939 
mover_ai()940 void *mover_ai()
941 {
942   game_object *o=current_object;
943   if (o->total_objects()==2)
944   {
945     if (o->aistate()<2)
946     {
947       game_object *obj=o->get_object(1);
948       o->remove_object(obj);
949       game_object *d=o->get_object(0);
950       d->add_object(obj);
951       d->set_aistate(d->aitype());
952     } else
953     {
954       o->set_aistate(o->aistate()-1);
955       game_object *d=o->get_object(0);
956       game_object *obj=o->get_object(1);
957 
958       obj->x=d->x-(d->x-o->x)*o->aistate()/o->aitype();
959       obj->y=d->y-(d->y-o->y)*o->aistate()/o->aitype();
960     }
961   }
962   return true_symbol;
963 }
964 
965 
respawn_ai()966 void *respawn_ai()
967 {
968  game_object *o=current_object;
969  int x=o->total_objects();
970  if (x)
971  {
972    game_object *last=o->get_object(x-1);
973    if (last->x==o->x && last->y==o->y)
974    {
975      if (last->fade_count())
976        last->set_fade_count(last->fade_count()-1);
977      o->set_aistate_time(0);
978    } else if (o->aistate_time()>o->xvel())
979    {
980      int type=o->get_object(jrandom(x))->otype;
981      game_object *n=create(type,o->x,o->y);
982      current_level->add_object(n);
983      o->add_object(n);
984      n->set_fade_count(15);
985      o->set_aistate_time(0);
986    }
987  }
988  return true_symbol;
989 }
990 
compare_players(const void * a,const void * b)991 static int compare_players(const void *a, const void *b)
992 {
993   if  ( ((view **)a)[0]->kills > ((view **)b)[0]->kills)
994     return -1;
995   else if  ( ((view **)a)[0]->kills < ((view **)b)[0]->kills)
996     return 1;
997   else if (((view **)a)[0]->player_number > ((view **)b)[0]->player_number)
998     return -1;
999   else if (((view **)a)[0]->player_number < ((view **)b)[0]->player_number)
1000     return 1;
1001   else return 0;
1002 }
1003 
score_draw()1004 void *score_draw()
1005 {
1006   view *sorted_players[16],*local=NULL;
1007   int tp=0;
1008   view *f=player_list;
1009   for (; f; f=f->next)
1010   {
1011     sorted_players[tp]=f;
1012     tp++;
1013     if (f->local_player()) local=f;
1014   }
1015 
1016   JCFont *fnt=wm->font();
1017   if (local)
1018   {
1019     qsort(sorted_players,tp,sizeof(view *),compare_players);
1020 
1021     int x=local->cx1;
1022     int y=local->cy1;
1023     char msg[100];
1024 
1025     int i;
1026     for (i=0; i<tp; i++)
1027     {
1028       int color=lnumber_value(((LArray *)((LSymbol *)l_player_text_color)->GetValue())->Get(sorted_players[i]->get_tint()));
1029       sprintf(msg,"%3ld %s",(long)sorted_players[i]->kills,sorted_players[i]->name);
1030       if (sorted_players[i]==local)
1031         strcat(msg," <<");
1032 
1033       fnt->put_string(screen,x,y,msg,color);
1034       y+=fnt->height();
1035     }
1036   }
1037   return NULL;
1038 }
1039 
1040 
1041 extern void fade_in(image *im, int steps);
1042 extern void fade_out(int steps);
1043 
show_kills()1044 void *show_kills()
1045 {
1046   fade_out(8);
1047   wm->set_mouse_position(0,0);
1048   screen->clear();
1049   image *im=cache.img(cache.reg("art/frame.spe","end_level_screen",SPEC_IMAGE,1));
1050   im->put_image(screen,0,0);
1051   int x1=im->Size().x+1,y1=0,y2=screen->Size().y;
1052   JCFont *fnt=wm->font();
1053 
1054   view *v=player_list; int tp=0,i;
1055   for (v=player_list; v; v=v->next) tp++;
1056 
1057   int y=(y1+y2)/2-(tp+2)*fnt->height()/2,x=x1+10;
1058   char const *header_str = symbol_str("score_header");
1059   fnt->put_string(screen,x,y,header_str,wm->bright_color());
1060   y+=fnt->height();
1061 
1062   screen->widget_bar(x,y+2,x+strlen(header_str)*fnt->width(),y+fnt->height()-3,
1063              wm->bright_color(),wm->medium_color(),wm->dark_color());
1064   y+=fnt->height();
1065   v=player_list;
1066   for (i=0; i<tp; i++)
1067   {
1068     enum { NAME_LEN=18 } ;
1069     int color=lnumber_value(((LArray *)((LSymbol *)l_player_text_color)->GetValue())->Get(v->get_tint()));
1070     char max_name[NAME_LEN];
1071     strncpy(max_name,v->name,NAME_LEN-1);
1072     max_name[NAME_LEN-1]=0;
1073     char msg[100];
1074 
1075 
1076     sprintf(msg,"%-17s %3ld  %3ld",max_name,(long)v->kills,(long)(v->tkills+v->kills));
1077     fnt->put_string(screen,x,y,msg,color);
1078 
1079     y+=fnt->height();
1080     v=v->next;
1081   }
1082 
1083   wm->flush_screen();
1084   Timer now; now.WaitMs(4000);   // wait 4 seconds
1085 
1086   return NULL;
1087 }
1088 
1089 
1090