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