1 /*
2  * Luola - 2D multiplayer cave-flying game
3  * Copyright (C) 2003-2006 Calle Laakkonen
4  *
5  * File        : ship.c
6  * Description : Ship information and animation
7  * Author(s)   : Calle Laakkonen
8  *
9  * Luola is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Luola is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22  */
23 
24 #include <stdlib.h>
25 #include <string.h>
26 #include <math.h>
27 
28 #include "console.h"
29 #include "player.h"
30 #include "level.h"
31 #include "list.h"
32 #include "ldat.h"
33 #include "fs.h"
34 #include "decor.h"
35 #include "animation.h"
36 #include "particle.h"
37 #include "audio.h"
38 #include "special.h"
39 #include "critter.h"
40 #include "levelfile.h"
41 #include "ship.h"
42 #include "weapon.h"
43 
44 #define SHIP_POSES      36
45 #define SHIP_WHITE_DUR	(0.13*GAME_SPEED)   /* After receiving damage, for how long the ship appears white */
46 #define THRUST          (80.0/GAME_SPEED)
47 #define DAMAGE_TRESHOLD 3.0 /* Treshold velocity for collision damage */
48 
49 
50 /* Exported globals */
51 struct dllist *ship_list;
52 
53 /* Internally used globals */
54 static SDL_Surface *ship_gfx[7][SHIP_POSES]; /* 0=grey, 1-4=coloured, 5 = white, 6 =  frozen */
55 static SDL_Surface *ghost_gfx[4][SHIP_POSES];
56 static SDL_Surface *shield_gfx[4];      /* 0-3 coloured */
57 static SDL_Surface **remocon_gfx;
58 static int remocon_frames;
59 
60 /* Internally used functions */
61 static void ship_fire_standard_weapon (struct Ship * ship);
62 static void ship_specials (struct Ship * ship);
63 
64 /* Load ship related datafiles */
init_ships(LDAT * playerfile)65 void init_ships (LDAT *playerfile) {
66     SDL_Surface *tmpsurface;
67     int r, p;
68     /* Load ship graphics */
69     for (p = 0; p < SHIP_POSES; p++) {
70         tmpsurface = load_image_ldat (playerfile, 0, T_ALPHA,"VWING",p);
71         for (r = 0; r < 7; r++) {
72             ship_gfx[r][p] = copy_surface(tmpsurface);
73             switch (r) {
74             case Grey:
75                 recolor (ship_gfx[r][p], 0.6, 0.6, 0.6, 1);
76                 break;
77             case Frozen:
78                 recolor (ship_gfx[r][p], 0, 1, 1, 1);
79                 break;
80             case White:
81                 break;
82             case Blue:
83                 recolor (ship_gfx[r][p], 0, 0.2, 1, 1);
84                 break;
85             case Red:
86                 recolor (ship_gfx[r][p], 1, 0.2, 0, 1);
87                 break;
88             case Green:
89                 recolor (ship_gfx[r][p], 0.2, 1, 0.2, 1);
90                 break;
91             case Yellow:
92                 recolor (ship_gfx[r][p], 1, 1, 0.4, 1);
93                 break;
94             }
95         }
96         SDL_FreeSurface(tmpsurface);
97     }
98     /* Create ghost ship graphics */
99     for (r = 0; r < 4; r++) {
100         for (p = 0; p < SHIP_POSES; p++) {
101             ghost_gfx[r][p] = copy_surface (ship_gfx[r + 1][p]);
102             recolor (ghost_gfx[r][p], 1.0, 1.0, 1.0, 0.5);
103         }
104     }
105     /* Load Shield graphics */
106     tmpsurface = load_image_ldat (playerfile, 0, T_ALPHA, "SHIELD", 0);
107     for (r = 0; r < 4; r++) {
108         shield_gfx[r] = copy_surface (tmpsurface);
109         switch (r + Red) {
110         case Red:
111             recolor (shield_gfx[r], 1, 0.4, 0.4, 1);
112             break;
113         case Blue:
114             recolor (shield_gfx[r], 0.4, 0.4, 1, 1);
115             break;
116         case Green:
117             recolor (shield_gfx[r], 0.4, 1, 0.4, 1);
118             break;
119         case Yellow:
120             recolor (shield_gfx[r], 1, 1, 0.6, 1);
121             break;
122         default:
123             fputs("Unhandled shield color in init_ships()\n",stderr);
124             exit(1);
125         }
126     }
127     SDL_FreeSurface(tmpsurface);
128     /* Load Remote Control graphics */
129     remocon_gfx =
130         load_image_array (playerfile, 0, T_ALPHA, "XMIT", &remocon_frames);
131 }
132 
133 /* Remove ships */
clear_ships(void)134 void clear_ships (void)
135 {
136     dllist_free(ship_list,free);
137     ship_list=NULL;
138 }
139 
140 /* Prepare for a new level */
reinit_ships(struct LevelSettings * settings)141 void reinit_ships (struct LevelSettings * settings)
142 {
143     struct dllist *objects=NULL;
144     int standard,special;
145     PlayerColor color;
146     struct Ship *newship;
147     if (settings)
148         objects = settings->objects;
149     while (objects) {
150         struct LSB_Object *object = objects->data;
151         if (object->type == OBJ_SHIP) {
152             switch (object->value) {
153             case 1:
154                 color = Red;
155                 break;
156             case 2:
157                 color = Blue;
158                 break;
159             case 3:
160                 color = Green;
161                 break;
162             case 4:
163                 color = Yellow;
164                 break;
165             default:
166                 color = Grey;
167                 standard = 0;
168                 special = 0;
169                 break;
170             }
171             if (color != Grey) {
172                 standard = players[color - 1].standardWeapon;
173                 special = players[color - 1].specialWeapon;
174             } else {
175                 standard = 0;
176                 special = 0;
177             }
178             newship = create_ship (color, standard, special);
179             newship->physics.x = object->x;
180             newship->physics.y = object->y;
181         }
182         objects = objects->next;
183     }
184 }
185 
186 /* Create a new ship. It is automatically added to the ship list */
create_ship(PlayerColor color,int weapon,int special)187 struct Ship *create_ship (PlayerColor color, int weapon, int special)
188 {
189     Vector nulv = {0,0};
190     struct Ship *newship = malloc (sizeof (struct Ship));
191     if(!newship) {
192         perror("create_ship");
193         return NULL;
194     }
195     memset (newship, 0, sizeof (struct Ship));
196     newship->ship = ship_gfx[color];
197     newship->shield = shield_gfx[color - Red];
198     init_physobj(&newship->physics,0,0,nulv);
199     newship->physics.sharpness = BOUNCY;
200     newship->physics.mass = 8.0;
201     newship->physics.radius = 6.0;
202     newship->angle = M_PI_2;
203 
204     newship->health = 1.0;
205     newship->energy = 1.0;
206     newship->visible = 1;
207     newship->standard = weapon;
208     newship->special = special;
209     newship->color = color;
210 
211     if(ship_list)
212         dllist_append(ship_list,newship);
213     else
214         ship_list=dllist_append(ship_list,newship);
215     return newship;
216 }
217 
218 /* Draw ships on screen */
draw_ships(void)219 void draw_ships (void)
220 {
221     struct dllist *current=ship_list;
222     SDL_Rect rect, rect2;
223     int plr, pose;
224     struct Ship *ship;
225     while (current) {
226         ship = current->data;
227         if (ship->visible==1) {
228             for (plr = 0; plr < 4; plr++) {
229                 rect.w = ship_gfx[Grey][0]->w;
230                 rect.h = ship_gfx[Grey][0]->h;
231                 if (players[plr].state==ALIVE||players[plr].state==DEAD) {
232                     SDL_Surface *surf;
233                     rect.x =
234                         viewport_rects[plr].x + Round(ship->physics.x) -
235                         cam_rects[plr].x - 8;
236                     rect.y =
237                         viewport_rects[plr].y + Round(ship->physics.y) -
238                         cam_rects[plr].y - 8;
239                     if (rect.x < viewport_rects[plr].x - 16
240                         || rect.y < viewport_rects[plr].y - 16
241                         || rect.x > viewport_rects[plr].x + cam_rects[plr].w
242                         || rect.y > viewport_rects[plr].y + cam_rects[plr].h)
243                         continue;
244                     rect2 =
245                         cliprect (rect.x, rect.y, rect.w, rect.h,
246                                   viewport_rects[plr].x, viewport_rects[plr].y,
247                                   viewport_rects[plr].x + cam_rects[plr].w,
248                                   viewport_rects[plr].y + cam_rects[plr].h);
249                     if (rect.x < viewport_rects[plr].x)
250                         rect.x = viewport_rects[plr].x;
251                     if (rect.y < viewport_rects[plr].y)
252                         rect.y = viewport_rects[plr].y;
253                     if (players[plr].ship == ship && radars_visible)
254                         draw_radar (rect, plr);
255                     pose = Round(ship->angle/(2*M_PI)*SHIP_POSES);
256                     if (pose > 35)
257                         pose = 35;
258                     if (ship->state!=INTACT)
259                         surf = ship_gfx[Grey][pose];
260                     else if (ship->frozen)
261                         surf = ship_gfx[Frozen][pose];
262                     else if (ship->white_ship)
263                         surf = ship_gfx[White][pose];
264                     else
265                         surf = ship->ship[pose];
266                     SDL_BlitSurface (surf, &rect2, screen, &rect);
267                     if (ship->shieldup) {
268                         SDL_Rect sr, tr;
269                         tr.x =
270                             viewport_rects[plr].x + Round(ship->physics.x) -
271                             cam_rects[plr].x
272                             - 16;
273                         tr.y =
274                             viewport_rects[plr].y + Round(ship->physics.y) -
275                             cam_rects[plr].y
276                             - 16;
277                         tr.w = ship->shield->w;
278                         tr.h = ship->shield->h;
279                         sr = cliprect (tr.x, tr.y, tr.w, tr.h,
280                                        viewport_rects[plr].x, viewport_rects[plr].y,
281                                        viewport_rects[plr].x + cam_rects[plr].w,
282                                        viewport_rects[plr].y + cam_rects[plr].h);
283                         if (tr.x < viewport_rects[plr].x)
284                             tr.x = viewport_rects[plr].x;
285                         if (tr.y < viewport_rects[plr].y)
286                             tr.y = viewport_rects[plr].y;
287                         SDL_BlitSurface (ship->shield, &sr, screen, &tr);
288                     }
289                     if (ship->remote_control) {
290                         SDL_Rect sr, tr;
291                         tr.x =
292                             viewport_rects[plr].x + Round(ship->physics.x) -
293                             cam_rects[plr].x - 16;
294                         tr.y =
295                             viewport_rects[plr].y + Round(ship->physics.y) -
296                             cam_rects[plr].y - 16;
297                         tr.w = remocon_gfx[ship->anim]->w;
298                         tr.h = remocon_gfx[ship->anim]->h;
299                         sr = cliprect (tr.x, tr.y, tr.w, tr.h,
300                                        viewport_rects[plr].x, viewport_rects[plr].y,
301                                        viewport_rects[plr].x + cam_rects[plr].w,
302                                        viewport_rects[plr].y + cam_rects[plr].h);
303                         if (tr.x < viewport_rects[plr].x)
304                             tr.x = viewport_rects[plr].x;
305                         if (tr.y < viewport_rects[plr].y)
306                             tr.y = viewport_rects[plr].y;
307                         SDL_BlitSurface (remocon_gfx[ship->anim], &sr, screen,
308                                          &tr);
309                     }
310                     if (ship->darting) {
311                         int cx, cy;
312                         float dx, dy;
313                         cx = rect.x + ship_gfx[Grey][0]->w / 2;
314                         cy = rect.y + ship_gfx[Grey][0]->h / 2;
315                         if (ship->darting == DARTING) {
316                             dx = cos (ship->angle);
317                             dy = sin (ship->angle);
318                             draw_line (screen, cx + dx * 5, cy - dy * 5,
319                                        cx + dx * 10, cy - dy * 10, col_gray);
320                         } else {
321                             double h=hypot(ship->physics.vel.x,ship->physics.vel.y);
322                             dx = ship->physics.vel.x/h;
323                             dy = ship->physics.vel.y/h;
324                             draw_line (screen, cx + dx * 6, cy - dy * 6,
325                                        cx - dx * 6, cy + dy * 6, col_gray);
326                         }
327                     }
328                 }
329             }
330         }
331         current = current->next;
332     }
333 }
334 
335 /** Finish off a dead ship ***/
finalize_ship(struct Ship * ship)336 static void finalize_ship(struct Ship *ship) {
337     /* Find the player who controls this ship */
338     int num = find_player (ship);
339 
340     /* Set ship state to DESTROYED. It will be deleted at the end of
341      * the animation loop in animate_ship()
342      */
343     ship->state = DESTROYED;
344     ship->visible = 0;
345 
346     /* Mark the player as dead if pilot has not been ejected */
347     if (num >= 0)
348         kill_player (num);
349 
350     /* Make sure no pilots have roped this ship */
351 #if 0
352     for(num=0;num<4;num++) {
353         if(players[num].state == ALIVE && players[num].pilot.rope_ship==ship) {
354             pilot_detach_rope(&players[num].pilot);
355         }
356     }
357 #endif
358 
359     /* Explosion and sound effect */
360     spawn_clusters (ship->physics.x, ship->physics.y,5.6, 32, make_bullet);
361     spawn_clusters (ship->physics.x, ship->physics.y,5.6, 16, make_firestarter);
362     playwave(WAV_EXPLOSION2);
363 }
364 
365 /** Stop special weapons **/
ship_stop_special(struct Ship * ship)366 void ship_stop_special(struct Ship *ship) {
367     if (ship->remote_control) {
368         if ((int) ship->remote_control > 1) {
369             remote_control(ship,0);
370         } else {
371             struct dllist *tmp2 = ship_list;
372             while (tmp2) {
373                 if (((struct Ship*)tmp2->data)->remote_control == ship) {
374                     remote_control(tmp2->data,0);
375                     break;
376                 }
377                 tmp2 = tmp2->next;
378             }
379         }
380     }
381     ship->fire_special_weapon = 0;
382     ship->antigrav = 0;
383     if(ship->shieldup) {
384         remove_ga(ship->shieldup);
385         ship->shieldup = NULL;
386     }
387     ship->afterburn = 0;
388     ship->repairing = 0;
389     ship->visible = 1;
390     if (ship->physics.solidity==IMMATERIAL)
391         ghostify (ship, 0);
392 }
393 
394 /* Find nearest ship and activate remote control if found */
remote_control(struct Ship * ship,int activate)395 void remote_control(struct Ship *ship,int activate) {
396     if(activate) {
397         struct Ship *targ;
398         double d;
399         targ = find_nearest_ship (ship->physics.x, ship->physics.y, ship, &d);
400         if (targ && d < 150) {
401             targ->thrust = 0;
402             targ->turn = 0;
403             targ->fire_weapon = 0;
404             ship->thrust = 0;
405             ship->turn = 0;
406             ship->fire_weapon = 0;
407             ship_stop_special(targ);
408             ship->remote_control = targ;
409             targ->remote_control = (struct Ship *) 1; /* To indicate that the ship is being remote controlled */
410         }
411     } else {
412         ship->remote_control->remote_control = NULL;
413         ship->remote_control->thrust = 0;
414         ship->remote_control->turn = 0;
415         ship->remote_control->fire_weapon = 0;
416         ship_stop_special(ship->remote_control);
417         ship->remote_control = NULL;
418     }
419 }
420 
421 /* Ship turns gray and starts tumbling */
kill_ship(struct Ship * ship)422 static void kill_ship (struct Ship * ship)
423 {
424     /* Stop weapons */
425     ship->fire_weapon = 0;
426     ship_stop_special(ship);
427 
428     /* Kill the ship */
429     ship->state = BROKEN;
430     ship->frozen = 0;
431     ship->health = 0;
432     ship->thrust = 0;
433     ship->physics.thrust.x = 0;
434     ship->physics.thrust.y = 0;
435     /* Ship turns heavier so it sinks in water */
436     ship->physics.mass *= 6.0;
437 
438     /* Set the ship spinning */
439     if (ship->angle < M_PI)
440         ship->turn = -0.6;
441     else
442         ship->turn = 0.6;
443 
444     /* Sound effect */
445     playwave(WAV_CRASH);
446 }
447 
448 /* Calculate ship thrust vector */
set_ship_thrust(struct Ship * ship)449 static void set_ship_thrust(struct Ship *ship) {
450     double t;
451     if(ship->darting) {
452         ship->physics.thrust = get_constant_vel(ship->physics.vel,
453                 ship->physics.radius,ship->physics.mass);
454         return;
455     } else if(ship->afterburn) {
456         t = 1.1;
457     } else if(ship->thrust || ((ship->criticals & CRITICAL_FUELCONTROL)
458                 && rand()%6==0))
459     {
460         t = 0.5;
461     } else {
462         ship->physics.thrust.x = 0;
463         ship->physics.thrust.y = 0;
464         return;
465     }
466     ship->physics.thrust.x = cos (ship->angle) * t;
467     ship->physics.thrust.y = -sin (ship->angle) * t;
468 }
469 
470 /* Ship exhaust gas effect */
ship_exhaust(struct Ship * ship)471 static void ship_exhaust (struct Ship * ship)
472 {
473     struct Particle *part;
474     part = make_particle (ship->physics.x, ship->physics.y, 15);
475     part->vector = oppositeVector (ship->physics.thrust);
476     if (ship->physics.underwater) {
477         part->color[0] = 100;
478         part->color[1] = 100;
479         part->color[2] = 255;
480         part->rd = -20;
481         part->bd = -20;
482         part->gd = -31;
483         /*calc_color_deltas(part,0,0,0,255);*/
484         part->vector.x += 0.5 - ((rand () % 100) / 100.0);
485         part->vector.y += 0.5 - ((rand () % 100) / 100.0);
486     }
487 }
488 
489 /* Damage a ship */
damage_ship(struct Ship * ship,float damage,float critical)490 void damage_ship(struct Ship *ship, float damage,float critical) {
491     ship->health -= damage;
492     if(ship->health<0) ship->health=0;
493     ship->white_ship = SHIP_WHITE_DUR;
494 
495     if(game_settings.criticals && critical>0) {
496         if(rand() < (RAND_MAX * critical))
497             ship_critical(ship, 0);
498     }
499 }
500 
501 /* Recharge energy and repair */
service_ship(struct Ship * ship)502 static void service_ship(struct Ship *ship) {
503     if (ship->health < 1 && !ship->shieldup) {
504         struct Particle *spark;
505         ship->health += 0.0012;
506         /* Special effects */
507         spark =
508             make_particle (ship->physics.x + (rand () % 16) - 8,
509                ship->physics.y + (rand () % 16) - 8, 6);
510         spark->vector.x = (rand () % 4) - 2;
511         spark->vector.y = -3;
512         spark->color[0] = 255;
513         spark->color[1] = 255;
514         spark->color[2] = 255;
515         spark->rd = -100;
516         spark->gd = -100;
517         spark->bd = -15;
518     }
519     if (ship->health > 1.0)
520         ship->health = 1.0;
521     if (!ship->shieldup && !ship->repairing
522         && !ship->fire_special_weapon && ship->visible) {
523         if (ship->energy < 1.0)
524             ship->energy += 0.0014;
525         else if (ship->energy > 1.0)
526             ship->energy = 1.0;
527         if (ship->criticals && rand () % 10 == 0)
528             ship_critical (ship, 1);
529     }
530     /* Straighten the ship */
531     if (ship->physics.underwater)
532         ship->angle = 1.5*M_PI;
533     else
534         ship->angle = M_PI_2;
535 }
536 
537 /** Ship animation **/
animate_ships(void)538 void animate_ships (void) {
539     struct dllist *current = ship_list;
540     struct Ship *ship;
541     /* Loop through all ships */
542     while (current) {
543         struct dllist *next=current->next;
544         ship = current->data;
545 
546         /* Turn the ship */
547         ship->angle += ship->turn;
548         if (ship->angle > 2 * M_PI)
549             ship->angle = 0;
550         if (ship->angle < 0)
551             ship->angle = 2 * M_PI;
552 
553         if (ship->state==INTACT) {
554             /* Counters */
555             if (ship->cooloff)
556                 ship->cooloff--;
557             if (ship->special_cooloff)
558                 ship->special_cooloff--;
559             if (ship->eject_cooloff)
560                 ship->eject_cooloff--;
561             if (ship->white_ship > 0)
562                 ship->white_ship--;
563             if (ship->no_power > 0)
564                 ship->no_power--;
565             if (ship->visible > 1)
566                 ship->visible--;
567             if ((ship->criticals & CRITICAL_CARGO) && ship->energy > 0)
568                 ship->energy -= 0.03/GAME_SPEED;
569             if(ship->no_power) {
570                 ship->physics.thrust.x = 0;
571                 ship->physics.thrust.y = 0;
572             } else {
573                 set_ship_thrust(ship);
574 
575                 /* Fire normal weapon */
576                 if (ship->fire_weapon && ship->cooloff == 0)
577                     ship_fire_standard_weapon (ship);
578 
579                 /* Fire special weapons */
580                 if (ship->fire_special_weapon && ship->special_cooloff==0)
581                     ship_fire_special(ship);
582 
583                 if(ship->physics.thrust.x!=0 || ship->physics.thrust.y!=0) {
584                     /* Exhaust effects */
585                     if(ship->visible)
586                         ship_exhaust (ship);
587 
588                     /* Critical engine core */
589                     if (ship->thrust && (ship->criticals & CRITICAL_ENGINE)) {
590                         int s;
591                         ship->health -= 0.03/GAME_SPEED;
592                         if (ship->health <= 0.0) {
593                             kill_ship (ship);
594                             spawn_clusters (ship->physics.x, ship->physics.y,
595                                     5.6, 6, make_napalm);
596                         }
597                         for (s = 0; s < 4; s++) {
598                             struct Particle *smoke;
599                             smoke =
600                                 make_particle (ship->physics.x + 8 - rand ()%16,
601                                                ship->physics.y + 8 - rand ()%16, 15);
602                             smoke->vector.x = weather_wind_vector;
603                             smoke->vector.y = -3.0 * (rand () % 20) / 10.0;
604                         }
605                     }
606                 } else if (ship->afterburn && ship->physics.underwater == 0) {
607                     start_burning (ship->physics.x, ship->physics.y);
608                 }
609             }
610             /* Player specials like cloaking device and such */
611             ship_specials (ship);
612 
613         }
614 
615         /* Do physics simulation */
616 #if 0
617         animate_object(&ship->physics,game_settings.ship_collisions>0,&ship_list);
618 #else
619         animate_object(&ship->physics,0,0); /* TODO ship to ship collisions
620                                                disabled until object impacts
621                                                work properly */
622 #endif
623 
624         /* Ground collisions */
625         if(ship->physics.hitground) {
626             if(ship->state==BROKEN) {
627                 /* Ship is dead, explode it */
628                 finalize_ship(ship);
629             } else {
630                 /* Recharge on base */
631                 if(ship->physics.hitground == TER_BASE)
632                     service_ship(ship);
633 
634                 /* Collision damage */
635                 if(game_settings.coll_damage &&
636                         ship->physics.hitground != TER_SNOW)
637                 {
638                     double vel = hypot(ship->physics.hitvel.x,
639                             ship->physics.hitvel.y);
640                     if(vel>DAMAGE_TRESHOLD) {
641                         damage_ship(ship,0.01,0.01);
642                     }
643                 }
644 
645                 /* Touching ground breaks status ailments */
646                 ship->frozen = 0;
647                 if(ship->darting) {
648                     ship->darting = NODART;
649                     ship->physics.sharpness = BOUNCY;
650                     damage_ship(ship,0.1,0.0);
651                 }
652             }
653         } else if(ship->physics.underwater) {
654             ship->frozen=0;
655         }
656 
657         /* Ship collisions (with Dart) */
658         if(ship->physics.hitobj && ship->darting) {
659             struct Ship *opponent = dllist_find(ship_list,ship->physics.hitobj)->data;
660             damage_ship(opponent,0.4,0.1);
661             ship->darting = NODART;
662             ship->physics.sharpness = BOUNCY;
663         }
664 
665         /* Kill ship if health goes below 0 */
666         if (ship->health <= 0.0 && ship->state==INTACT)
667             kill_ship (ship);
668 
669         /* Delete destroyed ships */
670         if (ship->state == DESTROYED) {
671             int p;
672             p = find_player (ship);
673             if (p >= 0)
674                 players[p].ship = NULL;
675             free(ship);
676             if(current==ship_list)
677                 ship_list=dllist_remove(current);
678             else
679                 dllist_remove(current);
680         }
681         current = next;
682     }
683 }
684 
685 /* Critical hit names */
critical2str(int critical)686 const char *critical2str (int critical)
687 {
688     switch (critical) {
689     case CRITICAL_ENGINE:
690         return "Critical engine core";
691     case CRITICAL_FUELCONTROL:
692         return "Critical fuel control";
693     case CRITICAL_LTHRUSTER:
694         return "Critical left thruster";
695     case CRITICAL_RTHRUSTER:
696         return "Critical right thruster";
697     case CRITICAL_CARGO:
698         return "Critical cargo hold";
699     case CRITICAL_STDWEAPON:
700         return "Critical mainweapon";
701     case CRITICAL_SPECIAL:
702         return "Critical special weapon";
703     case CRITICAL_POWER:
704         return "Temporary power failure";
705     default:
706         return "Unhandled critical";
707     }
708 }
709 
710 /* A random critical hit */
ship_critical(struct Ship * ship,int repair)711 void ship_critical (struct Ship * ship, int repair)
712 {
713     int c;
714 #if CRITICAL_COUNT > 8
715 #error Add more handlers for critical hits!
716 #endif
717     c = rand () % CRITICAL_COUNT;
718     switch (c) {
719     case 0: /* Damaged engine */
720         c = CRITICAL_ENGINE;
721         break;
722     case 1: /* Malfunctioning fuel control */
723         c = CRITICAL_FUELCONTROL;
724         break;
725     case 2: /* Left thruster out of order */
726         c = CRITICAL_LTHRUSTER;
727         break;
728     case 3: /* Right thruster out of order */
729         c = CRITICAL_RTHRUSTER;
730         break;
731     case 4: /* Leaking cargo bay */
732         c = CRITICAL_CARGO;
733         break;
734     case 5: /* Standard weapon out of order */
735         c = CRITICAL_STDWEAPON;
736         ship->fire_weapon = 0;
737         break;
738     case 6: /* Special weapon out of order */
739         c = CRITICAL_SPECIAL;
740         ship_stop_special(ship);
741         break;
742     case 7: /* Temporary power failure */
743         c = CRITICAL_POWER;
744         ship_stop_special(ship);
745         ship->no_power = 60;
746         break;
747     default:
748         fputs("Bug! ship_critical(): unhandled critical hit!\n",stderr);
749         return;
750     }
751     if ((ship->criticals & c) == 0) {
752         int plr = find_player (ship);
753         if (repair)
754             return;
755         if (c == CRITICAL_LTHRUSTER && (ship->criticals & CRITICAL_RTHRUSTER))
756             return; /* We won't leave the ship TOTALLY disabled... */
757         if (c == CRITICAL_RTHRUSTER && (ship->criticals & CRITICAL_LTHRUSTER))
758             return;
759         if(c != CRITICAL_POWER) ship->criticals |= c;
760         if (plr >= 0)
761             set_player_message (plr, Bigfont, font_color_red,
762                     c==CRITICAL_POWER?ship->no_power:25, critical2str (c));
763     } else if (repair) {
764         int plr = find_player (ship);
765         ship->criticals &= ~c;
766         if (plr >= 0)
767             set_player_message (plr, Bigfont, font_color_green, 25,
768                                 critical2str (c));
769     }
770 }
771 
772 /* Player claims a ship */
claim_ship(struct Ship * ship,int plr)773 void claim_ship (struct Ship * ship, int plr)
774 {
775     ship->color = Red + plr;
776     ship->ship = ship_gfx[ship->color];
777     ship->shield = shield_gfx[plr];
778     ship->standard = players[plr].standardWeapon;
779     ship->special = players[plr].specialWeapon;
780 }
781 
782 /* Special weapons that affect the state of the ship */
ship_specials(struct Ship * ship)783 static void ship_specials (struct Ship * ship) {
784     /* Remote control animation */
785     if (ship->remote_control) {
786         if ((int) ship->remote_control > 1) {
787             ship->anim++;
788             if (ship->anim >= remocon_frames)
789                 ship->anim = 0;
790             ship->energy -= 0.004;
791             if (ship->energy <= 0)
792                 remote_control(ship,0);
793         } else {
794             if (ship->anim <= 0)
795                 ship->anim = remocon_frames;
796             ship->anim--;
797         }
798     }
799     /* The rest are mutually exclusive (one player can have only one special weapon */
800     if(special_weapon[ship->special].id == WEAP_AUTOREPAIR &&
801             ship->criticals && rand () % 32 == 0)
802     {
803             ship_critical (ship, 1);
804     }
805     /* Cloaking device */
806     if (ship->visible == 0) {
807             ship->energy -= 0.001;
808         if (ship->energy <= 0) {
809             ship->visible = 1;
810             ship->energy = 0;
811         }
812     }
813     /* Ghost mode */
814     else if (ship->physics.solidity == IMMATERIAL) {
815             ship->energy -= 0.002;
816         if (ship->energy <= 0) {
817             ghostify (ship, 0);
818             ship->energy = 0;
819         }
820     }
821     /* Shield */
822     else if (ship->shieldup) {
823             //ship->energy -= 0.005;
824         if (ship->energy <= 0) {
825             ship_stop_special(ship);
826             ship->energy = 0;
827         }
828     }
829     /* Afterburner */
830     else if (ship->afterburn) {
831         if (ship->repeat_audio == 0) {
832             playwave (WAV_BURN);
833             ship->repeat_audio = 15;
834         } else
835             ship->repeat_audio--;
836             ship->energy -= 0.002;
837         if (ship->energy <= 0) {
838             ship->afterburn = 0;
839             ship->energy = 0;
840         }
841     }
842     /* Gravity polarizer active mode */
843     else if (ship->antigrav) {
844             ship->energy -= 0.0005;
845             /* Counter gravity and lift */
846             ship->physics.vel.y = ship->physics.vel.y - GRAVITY +
847                 (AIR_rho * ship->physics.radius * GRAVITY) / ship->physics.mass;
848         if (ship->energy <= 0) {
849             ship->antigrav = 0;
850             ship->energy = 0;
851         }
852     }
853     /* Autorepair */
854     else if (ship->repairing) {
855         ship->energy -= 0.001;
856         ship->health += 0.0008;
857         if (ship->health >= 1) {
858             ship->health = 1;
859             ship->repairing = 0;
860         }
861         if (ship->energy <= 0) {
862             ship->energy = 0;
863             ship->repairing = 0;
864             ship->energy = 0;
865         }
866         if (ship->anim == 0) {
867             struct Particle *spark;
868             ship->anim = rand () % 15;
869             spark =
870                 make_particle (ship->physics.x + (rand () % 16) - 8,
871                                ship->physics.y + (rand () % 16) - 8, 15);
872             spark->color[0] = 255;
873             spark->color[1] = 255;
874             spark->color[2] = 0;
875             spark->rd = -7;
876             spark->gd = -17;
877             spark->bd = 0;
878             spark->vector.x = (rand () % 6) - 3;
879             spark->vector.y = 2;
880         } else
881             ship->anim = 0;
882     }
883 }
884 
885 /* Enable/disable ghost ship effect */
ghostify(struct Ship * ship,int activate)886 void ghostify (struct Ship * ship, int activate)
887 {
888     if (activate)
889         ship->ship = ghost_gfx[ship->color - 1];
890     else
891         ship->ship = ship_gfx[ship->color];
892 
893     ship->physics.solidity = activate?IMMATERIAL:SOLID;
894 }
895 
896 /* Check if there is a ship in the specified coordinates and bump
897  * it one pixel upwards if there is. This is used to keep ships
898  * from getting stuck in regenerating bases */
bump_ship(int x,int y)899 void bump_ship(int x,int y) {
900     struct dllist *shplst=ship_list;
901     while(shplst) {
902         struct Ship *ship=shplst->data;
903 
904         if(Round(ship->physics.x) == x && Round(ship->physics.y) == y) {
905             ship->physics.y--;
906             break;
907         }
908         shplst=shplst->next;
909     }
910 }
911 
912 /* Cloaking device engage/disengage effect */
cloaking_device(struct Ship * ship,int activate)913 void cloaking_device (struct Ship * ship, int activate)
914 {
915     SDL_Surface *surf;
916     struct Particle *part;
917     Uint32 *src;
918     int x, y, p;
919     p= Round(ship->angle/(2*M_PI)*SHIP_POSES);
920     if (p > 35)
921         p = 35;
922     surf = ship->ship[p];
923     src = surf->pixels;
924     for (y = -surf->h / 2; y < surf->h / 2; y++)
925         for (x = -surf->w / 2; x < surf->w / 2; x++) {
926             if (((*src & surf->format->Amask) >> surf->format->Ashift) > 30) {
927                 if (activate) {
928                     part = make_particle (ship->physics.x + x, ship->physics.y + y, 8);
929                     part->color[0] =
930                         (*src & surf->format->Rmask) >> surf->format->Rshift;
931                     part->color[1] =
932                         (*src & surf->format->Gmask) >> surf->format->Gshift;
933                     part->color[2] =
934                         (*src & surf->format->Bmask) >> surf->format->Bshift;
935                     part->color[3] =
936                         (*src & surf->format->Amask) >> surf->format->Ashift;
937 #ifdef HAVE_LIBSDL_GFX
938                     part->rd = 0;
939                     part->gd = 0;
940                     part->bd = 0;
941                     part->ad = -part->color[3] >> 3;
942 #else
943                     part->rd = -part->color[0] >> 3;
944                     part->gd = -part->color[1] >> 3;
945                     part->bd = -part->color[2] >> 3;
946 #endif
947 
948                     part->vector.x =
949                         ship->physics.vel.x + 1.0 - ((rand () % 20) / 10.0);
950                     part->vector.y =
951                         ship->physics.vel.y + 1.0 - ((rand () % 20) / 10.0);
952                 } else {
953                     Uint8 tr,tg,tb,ta;
954                     part = make_particle (ship->physics.x + x, ship->physics.y + y, 3);
955                     part->color[0]=0; part->color[1]=0;
956                     part->color[2]=0; part->color[3]=0;
957                     tr = (*src & surf->format->Rmask) >> surf->format->Rshift;
958                     tg = (*src & surf->format->Gmask) >> surf->format->Gshift;
959                     tb = (*src & surf->format->Bmask) >> surf->format->Bshift;
960                     ta = (*src & surf->format->Amask) >> surf->format->Ashift;
961                     calc_color_deltas (part,tr,tg,tb,ta);
962                     part->vector = ship->physics.vel;
963                 }
964             }
965             src++;
966         }
967 }
968 
969 /* Fire the primary weapon */
ship_fire_standard_weapon(struct Ship * ship)970 static void ship_fire_standard_weapon (struct Ship * ship)
971 {
972     playwave(WAV_NORMALWEAP);
973     normal_weapon[ship->standard].fire(ship);
974     ship->cooloff = normal_weapon[ship->standard].cooloff;
975 }
976 
977 /* Fire special weapon */
ship_fire_special(struct Ship * ship)978 void ship_fire_special(struct Ship *ship) {
979     Vector mvel;
980     double fx,fy;
981 
982     if((special_weapon[ship->special].not_waterproof&&ship->physics.underwater)
983             || ship->energy < special_weapon[ship->special].energy)
984         return;
985 
986     playwave(special_weapon[ship->special].sfx);
987 
988     fx = ship->physics.x + cos(ship->angle) * 5.0;
989     fy = ship->physics.y + -sin(ship->angle) * 5.0;
990 
991     mvel = addVectors(get_muzzle_vel(ship->angle),ship->physics.vel);
992 
993     if(special_weapon[ship->special].make_bullet) {
994         add_projectile(special_weapon[ship->special].make_bullet(fx,fy,mvel));
995     } else {
996         special_weapon[ship->special].fire(ship,fx,fy,mvel);
997     }
998 
999     ship->energy -= special_weapon[ship->special].energy;
1000     if (ship->energy < 0)
1001         ship->energy = 0;
1002     ship->special_cooloff = special_weapon[ship->special].cooloff;
1003 
1004 }
1005 
1006 /* Find the nearest enemy player in a ship that is visible */
find_nearest_enemy(double myX,double myY,int myplr,double * dist)1007 int find_nearest_enemy (double myX, double myY, int myplr, double *dist) {
1008     int p, plr = -1;
1009     double distance = 9999999, d;
1010     for (p = 0; p < 4; p++) {
1011         if (same_team(p,myplr)) continue;
1012         if (players[p].ship==NULL || players[p].ship->state!=INTACT
1013                 || players[p].ship->visible!=1) continue;
1014         d = hypot (players[p].ship->physics.x - myX,
1015                    players[p].ship->physics.y - myY);
1016         if (d < distance) {
1017             plr = p;
1018             distance = d;
1019         }
1020     }
1021     if (dist)
1022         *dist = distance;
1023     return plr;
1024 }
1025 
1026 /* Note. This is a bit different that find_nearest_player       */
1027 /* First of all, this returns a pointer to the ship directly    */
1028 /* Second, not_this is a pointer to a ship, not a team number */
find_nearest_ship(double myX,double myY,struct Ship * not_this,double * dist)1029 struct Ship *find_nearest_ship (double myX, double myY, struct Ship * not_this, double *dist)
1030 {
1031     struct dllist *current = ship_list;
1032     struct Ship *nearest = NULL;
1033     double distance = 9999999, d;
1034     while (current) {
1035         struct Ship *ship=current->data;
1036         if (ship->state!=INTACT || ship == not_this) {
1037             current = current->next;
1038             continue;
1039         }
1040         d = hypot (ship->physics.x - myX, ship->physics.y - myY);
1041         if (d < distance) {
1042             nearest = ship;
1043             distance = d;
1044         }
1045         current = current->next;
1046     }
1047     if (dist)
1048         *dist = distance;
1049     return nearest;
1050 }
1051 
1052 /* Freeze a ship */
freeze_ship(struct Ship * ship)1053 void freeze_ship(struct Ship *ship) {
1054     ship_stop_special(ship);
1055     ship->frozen = 1;
1056     ship->thrust = 0;
1057     ship->turn = 0;
1058     ship->fire_weapon = 0;
1059 }
1060 
1061 /* Impale a ship */
spear_ship(struct Ship * ship,struct Projectile * spear)1062 void spear_ship(struct Ship *ship,struct Projectile *spear) {
1063     ship->physics.vel = spear->physics.vel;
1064     ship->darting = SPEARED;
1065     ship->physics.sharpness = BLUNT;
1066     ship->fire_weapon = 0;
1067     ship->fire_special_weapon = 0;
1068     ship->turn=0;
1069     ship->physics.vel.x += cos(spear->angle) * 30.0;
1070     ship->physics.vel.y += sin(spear->angle) * 30.0;
1071 
1072 }
1073 
1074