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