1 /*
2 * XPilot NG, a multiplayer space war game.
3 *
4 * Copyright (C) 1991-2001 by
5 *
6 * Bj�rn Stabell <bjoern@xpilot.org>
7 * Ken Ronny Schouten <ken@xpilot.org>
8 * Bert Gijsbers <bert@xpilot.org>
9 * Dick Balaska <dick@xpilot.org>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26 #include "xpserver.h"
27
28 /******************************
29 * Functions for ship movement.
30 */
31
Make_thrust_sparks(player_t * pl)32 void Make_thrust_sparks(player_t *pl)
33 {
34 static int min_dir, max_dir;
35 static double max_speed; /* kps - why are these static ? */
36 clpos_t engine = Ship_get_engine_clpos(pl->ship, pl->dir), pos;
37 int afterburners;
38 double max_life, tot_sparks, alt_sparks, power;
39
40 if (pl->fuel.sum > 0.0) {
41 power = pl->power;
42 afterburners = (Player_uses_emergency_thrust(pl)
43 ? MAX_AFTERBURNER
44 : pl->item[ITEM_AFTERBURNER]);
45 }
46 else {
47 /* read comment about this in update.c */
48 power = MIN_PLAYER_POWER * 0.6;
49 afterburners = 0;
50 }
51
52 max_life = 3 + power * 0.35;
53 tot_sparks = (power * 0.15 + 2.5) * timeStep;
54
55 min_dir = (int)(pl->dir + RES/2 - (RES*0.2 + 1) * options.thrustWidth);
56 max_dir = (int)(pl->dir + RES/2 + (RES*0.2 + 1) * options.thrustWidth);
57 max_speed = (1 + (power * 0.14)) * options.sparkSpeed;
58 alt_sparks = tot_sparks * afterburners * (1. / (MAX_AFTERBURNER + 1));
59
60 pos.cx = pl->pos.cx + engine.cx;
61 pos.cy = pl->pos.cy + engine.cy;
62
63 sound_play_sensors(pl->pos, THRUST_SOUND);
64
65 /* floor(tot_sparks + rfrac()) randomly rounds up or down to an integer,
66 * so that the expectation value of the result is tot_sparks */
67 Make_debris(pos,
68 pl->vel,
69 pl->id,
70 pl->team,
71 OBJ_SPARK,
72 options.thrustMass,
73 GRAVITY | OWNERIMMUNE,
74 RED,
75 8,
76 (int)((tot_sparks-alt_sparks) + rfrac()),
77 min_dir, max_dir,
78 1.0, max_speed,
79 3.0, max_life);
80
81 Make_debris(pos,
82 pl->vel,
83 pl->id,
84 pl->team,
85 OBJ_SPARK,
86 options.thrustMass * ALT_SPARK_MASS_FACT,
87 GRAVITY | OWNERIMMUNE,
88 BLUE,
89 8,
90 (int)(alt_sparks + rfrac()),
91 min_dir, max_dir,
92 1.0, max_speed,
93 3.0, max_life);
94 }
95
Record_shove(player_t * pl,player_t * pusher,long shove_time)96 void Record_shove(player_t *pl, player_t *pusher, long shove_time)
97 {
98 shove_t *shove = &pl->shove_record[pl->shove_next];
99
100 if (++pl->shove_next == MAX_RECORDED_SHOVES)
101 pl->shove_next = 0;
102
103 shove->pusher_id = pusher->id;
104 shove->time = shove_time;
105 }
106
107 /* Calculates the effect of a collision between two objects */
108 /* This calculates a completely inelastic collision. Ie. the
109 * objects remain stuck together (same velocity and direction.
110 * Use this function if one of the objects will die in the
111 * collision. */
Delta_mv(object_t * ship,object_t * obj)112 void Delta_mv(object_t *ship, object_t *obj)
113 {
114 double vx, vy, m;
115
116 m = ship->mass + ABS(obj->mass);
117 vx = (ship->vel.x * ship->mass + obj->vel.x * obj->mass) / m;
118 vy = (ship->vel.y * ship->mass + obj->vel.y * obj->mass) / m;
119 if (ship->type == OBJ_PLAYER
120 && obj->id != NO_ID
121 && BIT(obj->obj_status, COLLISIONSHOVE)) {
122 player_t *pl = (player_t *)ship;
123 player_t *pusher = Player_by_id(obj->id);
124 if (pusher != pl)
125 Record_shove(pl, pusher, frame_loops);
126 }
127 ship->vel.x = vx;
128 ship->vel.y = vy;
129 obj->vel.x = vx;
130 obj->vel.y = vy;
131 }
132
133 /* Calculates the effect of a collision between two objects */
134 /* And now for a completely elastic collision. Ie. the objects
135 * will bounce off of eachother. Use this function if both
136 * objects stay alive after the collision. */
Delta_mv_elastic(object_t * obj1,object_t * obj2)137 void Delta_mv_elastic(object_t *obj1, object_t *obj2)
138 {
139 double m1 = (double)obj1->mass,
140 m2 = (double)obj2->mass,
141 ms = m1 + m2;
142 double v1x = obj1->vel.x,
143 v1y = obj1->vel.y,
144 v2x = obj2->vel.x,
145 v2y = obj2->vel.y;
146
147 obj1->vel.x = (m1 - m2) / ms * v1x
148 + 2 * m2 / ms * v2x;
149 obj1->vel.y = (m1 - m2) / ms * v1y
150 + 2 * m2 / ms * v2y;
151 obj2->vel.x = 2 * m1 / ms * v1x
152 + (m2 - m1) / ms * v2x;
153 obj2->vel.y = 2 * m1 / ms * v1y
154 + (m2 - m1) / ms * v2y;
155 if (obj1->type == OBJ_PLAYER
156 && obj2->id != NO_ID
157 && BIT(obj2->obj_status, COLLISIONSHOVE)) {
158 player_t *pl = (player_t *)obj1;
159 player_t *pusher = Player_by_id(obj2->id);
160 if (pusher != pl)
161 Record_shove(pl, pusher, frame_loops);
162 }
163 }
164
Delta_mv_partly_elastic(object_t * obj1,object_t * obj2,double elastic)165 void Delta_mv_partly_elastic(object_t *obj1, object_t *obj2, double elastic)
166 {
167 double m1 = (double)obj1->mass,
168 m2 = (double)obj2->mass,
169 ms = m1 + m2;
170 double v1x = obj1->vel.x,
171 v1y = obj1->vel.y,
172 v2x = obj2->vel.x,
173 v2y = obj2->vel.y;
174 double vx, vy;
175 double vxd, vyd, xd, yd;
176 if(elastic < 0)
177 return;
178
179 vxd = v1x - v2x;
180 vyd = v1y - v2y;
181 xd = WRAP_DCX(obj2->pos.cx - obj1->pos.cx);
182 yd = WRAP_DCY(obj2->pos.cy - obj1->pos.cy);
183
184 /* KHS objects going away from each other, dont do anything */
185 if((vxd * xd + vyd * yd) < 0){
186 /* this sometimes has false positives for fast moves */
187 /* because the objects have already passed each other */
188 /* so lets check with positions from 1 frame back */
189
190 xd = WRAP_DCX(obj2->prevpos.cx - obj1->prevpos.cx);
191 yd = WRAP_DCY(obj2->prevpos.cy - obj1->prevpos.cy);
192
193 if((vxd * xd + vyd * yd) < 0)
194 return;
195 }
196
197 vx = (v1x * m1 + v2x * m2) / ms;
198 vy = (v1y * m1 + v2y * m2) / ms;
199
200
201 obj1->vel.x = ((m1 - m2) / ms * v1x
202 + 2 * m2 / ms * v2x) * elastic
203 + vx * (1 - elastic);
204 obj1->vel.y = ((m1 - m2) / ms * v1y
205 + 2 * m2 / ms * v2y) * elastic
206 + vy * (1 - elastic);
207 obj2->vel.x = (2 * m1 / ms * v1x
208 + (m2 - m1) / ms * v2x) * elastic
209 + vx * (1 - elastic);
210 obj2->vel.y = (2 * m1 / ms * v1y
211 + (m2 - m1) / ms * v2y) * elastic
212 + vy * (1 - elastic);
213
214 if (obj1->type == OBJ_PLAYER
215 && obj2->id != NO_ID
216 && BIT(obj2->obj_status, COLLISIONSHOVE)) {
217 player_t *pl = (player_t *)obj1;
218 player_t *pusher = Player_by_id(obj2->id);
219 if (pusher != pl)
220 Record_shove(pl, pusher, frame_loops);
221 }
222 }
223
224
Obj_repel(object_t * obj1,object_t * obj2,int repel_dist)225 void Obj_repel(object_t *obj1, object_t *obj2, int repel_dist)
226 {
227 double xd, yd, force, dm, dvx1, dvy1, dvx2, dvy2, a;
228 int obj_theta;
229
230 xd = WRAP_DCX(obj2->pos.cx - obj1->pos.cx);
231 yd = WRAP_DCY(obj2->pos.cy - obj1->pos.cy);
232 force = CLICK_TO_PIXEL((int)(repel_dist - LENGTH(xd, yd)));
233
234 if (force <= 0)
235 return;
236
237 force = MIN(force, 10);
238
239 a = findDir(xd, yd);
240 obj_theta = MOD2((int) (a + 0.5), RES);
241
242 dm = obj1->mass / obj2->mass;
243 dvx2 = tcos(obj_theta) * force * dm;
244 dvy2 = tsin(obj_theta) * force * dm;
245
246 dvx1 = -(tcos(obj_theta) * force / dm);
247 dvy1 = -(tsin(obj_theta) * force / dm);
248
249 if (obj1->type == OBJ_PLAYER && obj2->id != NO_ID) {
250 player_t *pl = (player_t *)obj1;
251 player_t *pusher = Player_by_id(obj2->id);
252 if (pusher != pl)
253 Record_shove(pl, pusher, frame_loops);
254 }
255
256 if (obj2->type == OBJ_PLAYER && obj1->id != NO_ID) {
257 player_t *pl = (player_t *)obj2;
258 player_t *pusher = Player_by_id(obj1->id);
259 if (pusher != pl)
260 Record_shove(pl, pusher, frame_loops);
261 }
262
263 obj1->vel.x += dvx1;
264 obj1->vel.y += dvy1;
265
266 obj2->vel.x += dvx2;
267 obj2->vel.y += dvy2;
268 }
269
270
271 /*
272 * Add fuel to fighter's tanks.
273 * Maybe use more than one of tank to store the fuel.
274 */
Add_fuel(pl_fuel_t * ft,double fuel)275 void Add_fuel(pl_fuel_t *ft, double fuel)
276 {
277 if (ft->sum + fuel > ft->max)
278 fuel = ft->max - ft->sum;
279 else if (ft->sum + fuel < 0.0)
280 fuel = -ft->sum;
281 ft->sum += fuel;
282 ft->tank[ft->current] += fuel;
283 }
284
285
286 /*
287 * Move fuel from add-on tanks to main tank,
288 * handle over and underflow of tanks.
289 */
Update_tanks(pl_fuel_t * ft)290 void Update_tanks(pl_fuel_t *ft)
291 {
292 if (ft->num_tanks) {
293 int t, check;
294 double low_level;
295 double fuel;
296 double *f;
297 double frame_refuel = REFUEL_RATE * timeStep;
298
299 /* Set low_level to minimum fuel in each tank */
300 low_level = ft->sum / (ft->num_tanks + 1) - 1;
301 if (low_level < 0.0)
302 low_level = 0.0;
303 if (TANK_REFILL_LIMIT < low_level)
304 low_level = TANK_REFILL_LIMIT;
305
306 t = ft->num_tanks;
307 check = MAX_TANKS << 2;
308 fuel = 0;
309 f = ft->tank + t;
310
311 while (t >= 0 && check--) {
312 double m = TANK_CAP(t);
313
314 /* Add the previous over/underflow and do a new cut */
315 *f += fuel;
316 if (*f > m) {
317 fuel = *f - m;
318 *f = m;
319 } else if (*f < 0) {
320 fuel = *f;
321 *f = 0;
322 } else
323 fuel = 0;
324
325 /* If there is no over/underflow, let the fuel run to main-tank */
326 if (!fuel) {
327 if (t
328 && t != ft->current
329 && *f >= low_level + frame_refuel
330 && *(f-1) <= TANK_CAP(t-1) - frame_refuel) {
331
332 *f -= frame_refuel;
333 fuel = frame_refuel;
334 } else if (t && *f < low_level) {
335 *f += frame_refuel;
336 fuel = frame_refuel;
337 }
338 }
339 if (fuel && t == 0) {
340 t = ft->num_tanks;
341 f = ft->tank + t;
342 } else {
343 t--;
344 f--;
345 }
346 }
347 if (!check) {
348 error("fuel problem");
349 fuel = ft->sum;
350 ft->sum =
351 ft->max = 0;
352 t = 0;
353 while (t <= ft->num_tanks) {
354 if (fuel) {
355 if (fuel>TANK_CAP(t)) {
356 ft->tank[t] = TANK_CAP(t);
357 fuel -= TANK_CAP(t);
358 } else {
359 ft->tank[t] = fuel;
360 fuel = 0;
361 }
362 ft->sum += ft->tank[t];
363 } else
364 ft->tank[t] = 0;
365 ft->max += TANK_CAP(t);
366 t++;
367 }
368 }
369 } else
370 ft->tank[0] = ft->sum;
371 }
372
373
374 /*
375 * Use current tank as dummy target for heat seeking missles.
376 */
Tank_handle_detach(player_t * pl)377 void Tank_handle_detach(player_t *pl)
378 {
379 player_t *tank;
380 int i, ct;
381
382 if (Player_is_phasing(pl))
383 return;
384
385 /* Return, if no more players or no tanks */
386 if (pl->fuel.num_tanks == 0
387 || NumPseudoPlayers == MAX_PSEUDO_PLAYERS
388 || peek_ID() == 0)
389 return;
390
391 /* If current tank is main, use another one */
392 if ((ct = pl->fuel.current) == 0)
393 ct = pl->fuel.num_tanks;
394
395 Update_tanks(&(pl->fuel));
396
397 /* Fork the current player */
398 tank = Player_by_index(NumPlayers);
399
400 /*
401 * MWAAH: this was ... naieve at least:
402 * *tank = *pl;
403 * Player structures contain pointers to dynamic memory...
404 */
405
406 Init_player(NumPlayers,
407 options.allowShipShapes
408 ? Parse_shape_str(options.tankShipShape) : NULL,
409 PL_TYPE_TANK);
410
411 /* Released tanks don't have tanks... */
412 while (tank->fuel.num_tanks > 0)
413 Player_remove_tank(tank, tank->fuel.num_tanks);
414
415 Object_position_init_clpos(OBJ_PTR(tank), pl->pos);
416 tank->vel = pl->vel;
417 tank->acc = pl->acc;
418 tank->dir = pl->dir;
419 tank->turnspeed = pl->turnspeed;
420 tank->velocity = pl->velocity;
421 Player_set_float_dir(tank, pl->float_dir);
422 tank->turnresistance = pl->turnresistance;
423 tank->turnvel = pl->turnvel;
424 tank->oldturnvel = pl->oldturnvel;
425 tank->turnacc = pl->turnacc;
426 tank->power = pl->power;
427
428 strlcpy(tank->name, pl->name, MAX_CHARS);
429 strlcat(tank->name, "'s tank", MAX_CHARS);
430 strlcpy(tank->username, options.tankUserName, MAX_CHARS);
431 strlcpy(tank->hostname, options.tankHostName, MAX_CHARS);
432 tank->home_base = pl->home_base;
433 tank->team = pl->team;
434 tank->pseudo_team = pl->pseudo_team;
435 tank->alliance = ALLIANCE_NOT_SET;
436 tank->invite = NO_ID;
437 tank->score = Get_Score(pl) - options.tankScoreDecrement;
438
439 /* Fuel is the one from chosen tank */
440 tank->fuel.sum =
441 tank->fuel.tank[0] = pl->fuel.tank[ct];
442 tank->fuel.max = TANK_CAP(ct);
443 tank->fuel.current = 0;
444 tank->fuel.num_tanks = 0;
445
446 /* Mass is only tank + fuel */
447 tank->emptymass = options.shipMass;
448 tank->mass = tank->emptymass + FUEL_MASS(tank->fuel.sum);
449 tank->power *= TANK_THRUST_FACT;
450
451 /* Reset visibility. */
452 tank->updateVisibility = true;
453 for (i = 0; i <= NumPlayers; i++) {
454 tank->visibility[i].lastChange = 0;
455 Player_by_index(i)->visibility[NumPlayers].lastChange = 0;
456 }
457
458 /* Remember whose tank this is */
459 tank->lock.pl_id = pl->id;
460
461 request_ID();
462 NumPlayers++;
463 NumPseudoPlayers++;
464 updateScores = true;
465
466 /* Possibly join alliance. */
467 if (pl->alliance != ALLIANCE_NOT_SET)
468 Player_join_alliance(tank, pl);
469
470 sound_play_sensors(pl->pos, TANK_DETACH_SOUND);
471
472 /* The tank uses shield and thrust */
473 tank->obj_status = GRAVITY;
474 Thrust(tank, true);
475 tank->have = DEF_HAVE;
476 tank->used = (DEF_USED & ~USED_KILL & pl->have) | HAS_SHIELD;
477
478 if (!options.allowShields) {
479 tank->shield_time = 30 * 12;
480 tank->have |= HAS_SHIELD;
481 }
482
483 /* Maybe heat-seekers to retarget? */
484 for (i = 0; i < NumObjs; i++) {
485 object_t *obj = Obj[i];
486
487 if (obj->type == OBJ_HEAT_SHOT) {
488 heatobject_t *heat = HEAT_PTR(obj);
489
490 if (heat->heat_lock_id > 0
491 && Player_by_id(heat->heat_lock_id) == pl)
492 /* kps - is this right ? */
493 heat->heat_lock_id = NumPlayers - 1;
494 }
495 }
496
497 /* Remove tank, fuel and mass from myself */
498 Player_remove_tank(pl, ct);
499
500 for (i = 0; i < NumPlayers - 1; i++) {
501 player_t *pl_i = Player_by_index(i);
502
503 if (pl_i->conn != NULL) {
504 Send_player(pl_i->conn, tank->id);
505 Send_score(pl_i->conn, tank->id,
506 tank->score, (int)tank->life,
507 tank->mychar, tank->alliance);
508 }
509 }
510
511 for (i = 0; i < NumSpectators - 1; i++) {
512 player_t *pl_i = Player_by_index(i + spectatorStart);
513
514 Send_player(pl_i->conn, tank->id);
515 Send_score(pl_i->conn, tank->id, tank->score,
516 (int)tank->life, tank->mychar, tank->alliance);
517 }
518 }
519
520
521
522
523
524
525 /****************************
526 * Functions for explosions.
527 */
528
529 /* Create debris particles */
Make_debris(clpos_t pos,vector_t vel,int owner_id,int owner_team,int type,double mass,int status,int color,int radius,int num_debris,int min_dir,int max_dir,double min_speed,double max_speed,double min_life,double max_life)530 void Make_debris(clpos_t pos,
531 vector_t vel,
532 int owner_id,
533 int owner_team,
534 int type,
535 double mass,
536 int status,
537 int color,
538 int radius,
539 int num_debris,
540 int min_dir, int max_dir,
541 double min_speed, double max_speed,
542 double min_life, double max_life)
543 {
544 object_t *debris;
545 int i;
546 double life;
547 modifiers_t mods;
548
549 if (!options.useDebris)
550 return;
551
552 pos = World_wrap_clpos(pos);
553 if (!World_contains_clpos(pos))
554 return;
555
556 if (max_life < min_life)
557 max_life = min_life;
558
559 if (max_speed < min_speed)
560 max_speed = min_speed;
561
562 Mods_clear(&mods);
563
564 if (type == OBJ_SHOT) {
565 Mods_set(&mods, ModsCluster, 1);
566 if (!options.shotsGravity)
567 CLR_BIT(status, GRAVITY);
568 }
569
570 if (num_debris > MAX_TOTAL_SHOTS - NumObjs)
571 num_debris = MAX_TOTAL_SHOTS - NumObjs;
572
573 for (i = 0; i < num_debris; i++) {
574 double speed, dx, dy, diroff;
575 int dir, dirplus;
576
577 if ((debris = Object_allocate()) == NULL)
578 break;
579
580 debris->color = color;
581 debris->id = owner_id;
582 debris->team = owner_team;
583 Object_position_init_clpos(debris, pos);
584 dir = MOD2(min_dir + (int)(rfrac() * (max_dir - min_dir)), RES);
585 dirplus = MOD2(dir + 1, RES);
586 diroff = rfrac();
587 dx = tcos(dir) + (tcos(dirplus) - tcos(dir)) * diroff;
588 dy = tsin(dir) + (tsin(dirplus) - tsin(dir)) * diroff;
589 speed = min_speed + rfrac() * (max_speed - min_speed);
590 debris->vel.x = vel.x + dx * speed;
591 debris->vel.y = vel.y + dy * speed;
592 debris->acc.x = 0;
593 debris->acc.y = 0;
594 if (options.shotHitFuelDrainUsesKineticEnergy
595 && type == OBJ_SHOT) {
596 /* compensate so that m*v^2 is constant */
597 double sp_shotsp = speed / options.shotSpeed;
598
599 debris->mass = mass / (sp_shotsp * sp_shotsp);
600 } else
601 debris->mass = mass;
602 debris->type = type;
603 life = min_life + rfrac() * (max_life - min_life);
604 debris->life = life;
605 debris->fuse = 0;
606 debris->pl_range = radius;
607 debris->pl_radius = radius;
608 debris->obj_status = status;
609 debris->mods = mods;
610 Cell_add_object(debris);
611 }
612 }
613
614
Make_wreckage(clpos_t pos,vector_t vel,int owner_id,int owner_team,double min_mass,double max_mass,double total_mass,int status,int max_wreckage,int min_dir,int max_dir,double min_speed,double max_speed,double min_life,double max_life)615 void Make_wreckage(clpos_t pos,
616 vector_t vel,
617 int owner_id,
618 int owner_team,
619 double min_mass, double max_mass,
620 double total_mass,
621 int status,
622 int max_wreckage,
623 int min_dir, int max_dir,
624 double min_speed, double max_speed,
625 double min_life, double max_life)
626 {
627 wireobject_t *wreckage;
628 int i, size;
629 double life, mass, sum_mass = 0.0;
630 modifiers_t mods;
631
632 if (!options.useWreckage)
633 return;
634
635 pos = World_wrap_clpos(pos);
636 if (!World_contains_clpos(pos))
637 return;
638
639 if (max_life < min_life)
640 max_life = min_life;
641
642 if (max_speed < min_speed)
643 max_speed = min_speed;
644
645 if (max_wreckage > MAX_TOTAL_SHOTS - NumObjs)
646 max_wreckage = MAX_TOTAL_SHOTS - NumObjs;
647
648 Mods_clear(&mods);
649
650 for (i = 0; i < max_wreckage && sum_mass < total_mass; i++) {
651
652 double speed;
653 int dir, radius;
654
655 /* Calculate mass */
656 mass = min_mass + rfrac() * (max_mass - min_mass);
657 if ( sum_mass + mass > total_mass )
658 mass = total_mass - sum_mass;
659
660 if (mass < min_mass)
661 /* not enough mass available. */
662 break;
663
664 /* Allocate object */
665 if ((wreckage = WIRE_PTR(Object_allocate())) == NULL)
666 break;
667
668 wreckage->color = WHITE;
669 wreckage->id = owner_id;
670 wreckage->team = owner_team;
671 wreckage->type = OBJ_WRECKAGE;
672
673 /* Position */
674 Object_position_init_clpos(OBJ_PTR(wreckage), pos);
675
676 /* Direction */
677 dir = MOD2(min_dir + (int)(rfrac() * MOD2(max_dir - min_dir, RES)),
678 RES);
679
680 /* Velocity and acceleration */
681 speed = min_speed + rfrac() * (max_speed - min_speed);
682 wreckage->vel.x = vel.x + tcos(dir) * speed;
683 wreckage->vel.y = vel.y + tsin(dir) * speed;
684 wreckage->acc.x = 0;
685 wreckage->acc.y = 0;
686
687 /* Mass */
688 wreckage->mass = mass;
689 sum_mass += mass;
690
691 /* Lifespan */
692 life = min_life + rfrac() * (max_life - min_life);
693
694 wreckage->life = life;
695 wreckage->fuse = 0;
696
697 /* Wreckage type, rotation, and size */
698 wreckage->wire_turnspeed = 0.02 + rfrac() * 0.35;
699 wreckage->wire_rotation = (int)(rfrac() * RES);
700 size = (int) ( 256.0 * 1.5 * mass / total_mass );
701 if (size > 255)
702 size = 255;
703 wreckage->wire_size = size;
704 wreckage->wire_type = (uint8_t)(rfrac() * 256);
705
706 radius = wreckage->wire_size * 16 / 256;
707 if (radius < 8)
708 radius = 8;
709
710 wreckage->pl_range = radius;
711 wreckage->pl_radius = radius;
712 wreckage->obj_status = status;
713 wreckage->mods = mods;
714 Cell_add_object(OBJ_PTR(wreckage));
715 }
716 }
717
718
Explode_fighter(player_t * pl)719 void Explode_fighter(player_t *pl)
720 {
721 int min_debris;
722 double debris_range;
723
724 sound_play_sensors(pl->pos, PLAYER_EXPLOSION_SOUND);
725
726 min_debris = (int)(1 + (pl->fuel.sum / 8.0));
727 debris_range = pl->mass;
728 /* reduce debris since we also create wreckage objects */
729 min_debris >>= 1; /* Removed *2.0 from range */
730
731 Make_debris(pl->pos,
732 pl->vel,
733 pl->id,
734 pl->team,
735 OBJ_DEBRIS,
736 3.5,
737 GRAVITY,
738 RED,
739 8,
740 (int)(min_debris + debris_range * rfrac()),
741 0, RES-1,
742 20.0, 20.0 + pl->mass * 0.5,
743 5.0, 5.0 + pl->mass * 1.5);
744
745 Make_wreckage(pl->pos,
746 pl->vel,
747 pl->id,
748 pl->team,
749 MAX(pl->mass/8.0, 0.33), pl->mass,
750 2.0 * pl->mass,
751 GRAVITY,
752 10,
753 0, RES-1,
754 10.0, 10.0 + pl->mass * 0.5,
755 5.0, 5.0 + pl->mass * 1.5);
756 }
757