1 //Copyright Paul Reiche, Fred Ford. 1992-2002
2
3 /*
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 #include "../ship.h"
20 #include "mmrnmhrm.h"
21 #include "resinst.h"
22
23 // Core characteristics
24 #define MAX_CREW 20
25 #define MAX_ENERGY 10
26 #define SHIP_MASS 3
27
28 // X-Wing characteristics
29 #define ENERGY_REGENERATION 2
30 #define ENERGY_WAIT 6
31 #define MAX_THRUST 20
32 #define THRUST_INCREMENT 5
33 #define THRUST_WAIT 1
34 #define TURN_WAIT 2
35
36 // Y-Wing characteristics
37 #define YWING_ENERGY_REGENERATION 1
38 #define YWING_SPECIAL_ENERGY_COST MAX_ENERGY
39 #define YWING_ENERGY_WAIT 6
40 #define YWING_MAX_THRUST 50
41 #define YWING_THRUST_INCREMENT 10
42 #define YWING_THRUST_WAIT 0
43 #define YWING_TURN_WAIT 14
44
45 // X-Wing Lasers
46 #define MMRNMHRM_OFFSET 16
47 #define WEAPON_ENERGY_COST 1
48 #define WEAPON_WAIT 0
49 #define CENTER_OFFS DISPLAY_TO_WORLD (4)
50 #define WING_OFFS DISPLAY_TO_WORLD (10)
51 #define LASER_RANGE DISPLAY_TO_WORLD (125 + MMRNMHRM_OFFSET)
52
53 // Y-Wing Missiles
54 #define YWING_WEAPON_ENERGY_COST 1
55 #define YWING_WEAPON_WAIT 20
56 #define LAUNCH_OFFS DISPLAY_TO_WORLD (4)
57 #define MISSILE_OFFSET 0
58 #define MISSILE_SPEED DISPLAY_TO_WORLD (20)
59 #define MISSILE_LIFE 40
60 #define MISSILE_HITS 1
61 #define MISSILE_DAMAGE 1
62 #define TRACK_WAIT 5
63
64 // Transform
65 #define SPECIAL_ENERGY_COST MAX_ENERGY
66 #define SPECIAL_WAIT 0
67 #define YWING_SPECIAL_WAIT 0
68
69 static RACE_DESC mmrnmhrm_desc =
70 {
71 { /* SHIP_INFO */
72 FIRES_FORE | IMMEDIATE_WEAPON,
73 19, /* Super Melee cost */
74 MAX_CREW, MAX_CREW,
75 MAX_ENERGY, MAX_ENERGY,
76 MMRNMHRM_RACE_STRINGS,
77 MMRNMHRM_ICON_MASK_PMAP_ANIM,
78 MMRNMHRM_MICON_MASK_PMAP_ANIM,
79 NULL, NULL, NULL
80 },
81 { /* FLEET_STUFF */
82 0, /* Initial sphere of influence radius */
83 { /* Known location (center of SoI) */
84 0, 0,
85 },
86 },
87 {
88 MAX_THRUST,
89 THRUST_INCREMENT,
90 ENERGY_REGENERATION,
91 WEAPON_ENERGY_COST,
92 SPECIAL_ENERGY_COST,
93 ENERGY_WAIT,
94 TURN_WAIT,
95 THRUST_WAIT,
96 WEAPON_WAIT,
97 SPECIAL_WAIT,
98 SHIP_MASS,
99 },
100 {
101 {
102 MMRNMHRM_BIG_MASK_PMAP_ANIM,
103 MMRNMHRM_MED_MASK_PMAP_ANIM,
104 MMRNMHRM_SML_MASK_PMAP_ANIM,
105 },
106 {
107 TORP_BIG_MASK_PMAP_ANIM,
108 TORP_MED_MASK_PMAP_ANIM,
109 TORP_SML_MASK_PMAP_ANIM,
110 },
111 {
112 YWING_BIG_MASK_PMAP_ANIM,
113 YWING_MED_MASK_PMAP_ANIM,
114 YWING_SML_MASK_PMAP_ANIM,
115 },
116 {
117 MMRNMHRM_CAPTAIN_MASK_PMAP_ANIM,
118 NULL, NULL, NULL, NULL, NULL
119 },
120 MMRNMHRM_VICTORY_SONG,
121 MMRNMHRM_SHIP_SOUNDS,
122 { NULL, NULL, NULL },
123 { NULL, NULL, NULL },
124 { NULL, NULL, NULL },
125 NULL, NULL
126 },
127 {
128 0,
129 CLOSE_RANGE_WEAPON,
130 NULL,
131 },
132 (UNINIT_FUNC *) NULL,
133 (PREPROCESS_FUNC *) NULL,
134 (POSTPROCESS_FUNC *) NULL,
135 (INIT_WEAPON_FUNC *) NULL,
136 0,
137 0, /* CodeRef */
138 };
139
140 // Private per-instance ship data
141 typedef CHARACTERISTIC_STUFF MMRNMHRM_DATA;
142
143 // Local typedef
144 typedef MMRNMHRM_DATA CustomShipData_t;
145
146 // Retrieve race-specific ship data from a race desc
147 static CustomShipData_t *
GetCustomShipData(RACE_DESC * pRaceDesc)148 GetCustomShipData (RACE_DESC *pRaceDesc)
149 {
150 return pRaceDesc->data;
151 }
152
153 // Set the race-specific data in a race desc
154 // (Re)Allocates its own storage for the data.
155 static void
SetCustomShipData(RACE_DESC * pRaceDesc,const CustomShipData_t * data)156 SetCustomShipData (RACE_DESC *pRaceDesc, const CustomShipData_t *data)
157 {
158 if (pRaceDesc->data == data)
159 return; // no-op
160
161 if (pRaceDesc->data) // Out with the old
162 {
163 HFree (pRaceDesc->data);
164 pRaceDesc->data = NULL;
165 }
166
167 if (data) // In with the new
168 {
169 CustomShipData_t* newData = HMalloc (sizeof (*data));
170 *newData = *data;
171 pRaceDesc->data = newData;
172 }
173 }
174
175 static void
missile_preprocess(ELEMENT * ElementPtr)176 missile_preprocess (ELEMENT *ElementPtr)
177 {
178 if (ElementPtr->turn_wait > 0)
179 --ElementPtr->turn_wait;
180 else
181 {
182 COUNT facing;
183
184 facing = GetFrameIndex (ElementPtr->next.image.frame);
185 if (TrackShip (ElementPtr, &facing) > 0)
186 {
187 ElementPtr->next.image.frame =
188 SetAbsFrameIndex (ElementPtr->next.image.frame,
189 facing);
190 ElementPtr->state_flags |= CHANGING;
191
192 SetVelocityVector (&ElementPtr->velocity,
193 MISSILE_SPEED, facing);
194 }
195
196 ElementPtr->turn_wait = TRACK_WAIT;
197 }
198 }
199
200 static void
mmrnmhrm_intelligence(ELEMENT * ShipPtr,EVALUATE_DESC * ObjectsOfConcern,COUNT ConcernCounter)201 mmrnmhrm_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
202 COUNT ConcernCounter)
203 {
204 BOOLEAN CanTransform;
205 EVALUATE_DESC *lpEvalDesc;
206 STARSHIP *StarShipPtr;
207 STARSHIP *EnemyStarShipPtr = NULL;
208
209 GetElementStarShip (ShipPtr, &StarShipPtr);
210 CanTransform = (BOOLEAN)(StarShipPtr->special_counter == 0
211 && StarShipPtr->RaceDescPtr->ship_info.energy_level >=
212 StarShipPtr->RaceDescPtr->characteristics.special_energy_cost);
213
214 lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
215 if (lpEvalDesc->ObjectPtr)
216 {
217 GetElementStarShip (lpEvalDesc->ObjectPtr, &EnemyStarShipPtr);
218 }
219
220 ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
221
222 StarShipPtr->ship_input_state &= ~SPECIAL;
223 if (CanTransform
224 && lpEvalDesc->ObjectPtr
225 && !(StarShipPtr->ship_input_state & WEAPON))
226 {
227 SIZE delta_x, delta_y;
228 COUNT travel_angle, direction_angle;
229
230 GetCurrentVelocityComponents (&lpEvalDesc->ObjectPtr->velocity,
231 &delta_x, &delta_y);
232 if (delta_x == 0 && delta_y == 0)
233 direction_angle = travel_angle = 0;
234 else
235 {
236 delta_x = lpEvalDesc->ObjectPtr->current.location.x
237 - ShipPtr->current.location.x;
238 delta_y = lpEvalDesc->ObjectPtr->current.location.y
239 - ShipPtr->current.location.y;
240 direction_angle = ARCTAN (-delta_x, -delta_y);
241 travel_angle = GetVelocityTravelAngle (
242 &lpEvalDesc->ObjectPtr->velocity
243 );
244 }
245
246 if (ShipPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.ship)
247 {
248 if (lpEvalDesc->which_turn > 8)
249 {
250 if (MANEUVERABILITY (&EnemyStarShipPtr->RaceDescPtr->cyborg_control) <= SLOW_SHIP
251 || NORMALIZE_ANGLE (
252 direction_angle - travel_angle + QUADRANT
253 ) > HALF_CIRCLE)
254 StarShipPtr->ship_input_state |= SPECIAL;
255 }
256 }
257 else
258 {
259 SIZE ship_delta_x, ship_delta_y;
260
261 GetCurrentVelocityComponents (&ShipPtr->velocity,
262 &ship_delta_x, &ship_delta_y);
263 delta_x -= ship_delta_x;
264 delta_y -= ship_delta_y;
265 travel_angle = ARCTAN (delta_x, delta_y);
266 if (lpEvalDesc->which_turn < 16)
267 {
268 if (lpEvalDesc->which_turn <= 8
269 || NORMALIZE_ANGLE (
270 direction_angle - travel_angle + OCTANT
271 ) <= QUADRANT)
272 StarShipPtr->ship_input_state |= SPECIAL;
273 }
274 else if (lpEvalDesc->which_turn > 32
275 && NORMALIZE_ANGLE (
276 direction_angle - travel_angle + QUADRANT
277 ) > HALF_CIRCLE)
278 StarShipPtr->ship_input_state |= SPECIAL;
279 }
280 }
281
282 if (ShipPtr->current.image.farray == StarShipPtr->RaceDescPtr->ship_data.special)
283 {
284 if (!(StarShipPtr->ship_input_state & SPECIAL)
285 && lpEvalDesc->ObjectPtr)
286 StarShipPtr->ship_input_state |= WEAPON;
287 else
288 StarShipPtr->ship_input_state &= ~WEAPON;
289 }
290 }
291
292 static void
twin_laser_collision(ELEMENT * ElementPtr0,POINT * pPt0,ELEMENT * ElementPtr1,POINT * pPt1)293 twin_laser_collision (ELEMENT *ElementPtr0, POINT *pPt0,
294 ELEMENT *ElementPtr1, POINT *pPt1)
295 {
296 if (!(ElementPtr1->state_flags & PLAYER_SHIP)
297 || !elementsOfSamePlayer (ElementPtr0, ElementPtr1))
298 weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
299 }
300
301 static COUNT
initialize_dual_weapons(ELEMENT * ShipPtr,HELEMENT WeaponArray[])302 initialize_dual_weapons (ELEMENT *ShipPtr, HELEMENT WeaponArray[])
303 {
304 COORD cx, cy;
305 COUNT facing, angle;
306 SIZE offs_x, offs_y;
307 STARSHIP *StarShipPtr;
308
309 GetElementStarShip (ShipPtr, &StarShipPtr);
310 facing = StarShipPtr->ShipFacing;
311 angle = FACING_TO_ANGLE (facing);
312 cx = ShipPtr->next.location.x + COSINE (angle, CENTER_OFFS);
313 cy = ShipPtr->next.location.y + SINE (angle, CENTER_OFFS);
314
315 if (ShipPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.ship)
316 {
317 COORD ex, ey;
318 LASER_BLOCK LaserBlock;
319 ELEMENT *LaserPtr;
320
321 LaserBlock.sender = ShipPtr->playerNr;
322 LaserBlock.flags = 0;
323 LaserBlock.pixoffs = 0;
324 LaserBlock.color = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0A, 0x0A), 0x0C);
325 LaserBlock.face = facing;
326
327 ex = cx + COSINE (angle, LASER_RANGE);
328 ey = cy + SINE (angle, LASER_RANGE);
329 offs_x = -SINE (angle, WING_OFFS);
330 offs_y = COSINE (angle, WING_OFFS);
331
332 LaserBlock.cx = cx + offs_x;
333 LaserBlock.cy = cy + offs_y;
334 LaserBlock.ex = ex - LaserBlock.cx;
335 LaserBlock.ey = ey - LaserBlock.cy;
336 if ((WeaponArray[0] = initialize_laser (&LaserBlock)))
337 {
338 LockElement (WeaponArray[0], &LaserPtr);
339 LaserPtr->collision_func = twin_laser_collision;
340 UnlockElement (WeaponArray[0]);
341 }
342
343 LaserBlock.cx = cx - offs_x;
344 LaserBlock.cy = cy - offs_y;
345 LaserBlock.ex = ex - LaserBlock.cx;
346 LaserBlock.ey = ey - LaserBlock.cy;
347 if ((WeaponArray[1] = initialize_laser (&LaserBlock)))
348 {
349 LockElement (WeaponArray[1], &LaserPtr);
350 LaserPtr->collision_func = twin_laser_collision;
351 UnlockElement (WeaponArray[1]);
352 }
353 }
354 else
355 {
356 MISSILE_BLOCK TorpBlock;
357 ELEMENT *TorpPtr;
358
359 TorpBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
360 TorpBlock.sender = ShipPtr->playerNr;
361 TorpBlock.flags = IGNORE_SIMILAR;
362 TorpBlock.pixoffs = 0;
363 TorpBlock.speed = MISSILE_SPEED;
364 TorpBlock.hit_points = MISSILE_HITS;
365 TorpBlock.damage = MISSILE_DAMAGE;
366 TorpBlock.life = MISSILE_LIFE;
367 TorpBlock.preprocess_func = missile_preprocess;
368 TorpBlock.blast_offs = MISSILE_OFFSET;
369
370 TorpBlock.face = TorpBlock.index = NORMALIZE_FACING (facing - 1);
371 offs_x = -SINE (FACING_TO_ANGLE (TorpBlock.face), LAUNCH_OFFS);
372 offs_y = COSINE (FACING_TO_ANGLE (TorpBlock.face), LAUNCH_OFFS);
373
374 TorpBlock.cx = cx + offs_x;
375 TorpBlock.cy = cy + offs_y;
376 if ((WeaponArray[0] = initialize_missile (&TorpBlock)))
377 {
378 LockElement (WeaponArray[0], &TorpPtr);
379 TorpPtr->turn_wait = TRACK_WAIT;
380 UnlockElement (WeaponArray[0]);
381 }
382
383 TorpBlock.face = TorpBlock.index = NORMALIZE_FACING (facing + 1);
384
385 TorpBlock.cx = cx - offs_x;
386 TorpBlock.cy = cy - offs_y;
387 if ((WeaponArray[1] = initialize_missile (&TorpBlock)))
388 {
389 LockElement (WeaponArray[1], &TorpPtr);
390 TorpPtr->turn_wait = TRACK_WAIT;
391 UnlockElement (WeaponArray[1]);
392 }
393 }
394
395 return (2);
396 }
397
398 static void
mmrnmhrm_postprocess(ELEMENT * ElementPtr)399 mmrnmhrm_postprocess (ELEMENT *ElementPtr)
400 {
401 STARSHIP *StarShipPtr;
402
403 GetElementStarShip (ElementPtr, &StarShipPtr);
404 /* take care of transform effect */
405 if (ElementPtr->next.image.farray != ElementPtr->current.image.farray)
406 {
407 MMRNMHRM_DATA tempShipData;
408 MMRNMHRM_DATA *otherwing_desc;
409
410 ProcessSound (SetAbsSoundIndex (
411 /* TRANSFORM */
412 StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
413
414 StarShipPtr->weapon_counter = 0;
415
416 /* Swap characteristics descriptors around */
417 otherwing_desc = GetCustomShipData (StarShipPtr->RaceDescPtr);
418 if (!otherwing_desc)
419 return; // No ship data (?!)
420
421 tempShipData = *otherwing_desc;
422 SetCustomShipData (StarShipPtr->RaceDescPtr, &StarShipPtr->RaceDescPtr->characteristics);
423 StarShipPtr->RaceDescPtr->characteristics = tempShipData;
424 StarShipPtr->RaceDescPtr->cyborg_control.ManeuverabilityIndex = 0;
425
426 if (ElementPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.special)
427 {
428 StarShipPtr->RaceDescPtr->cyborg_control.WeaponRange = LONG_RANGE_WEAPON - 1;
429 StarShipPtr->RaceDescPtr->ship_info.ship_flags &= ~IMMEDIATE_WEAPON;
430 StarShipPtr->RaceDescPtr->ship_info.ship_flags |= SEEKING_WEAPON;
431 StarShipPtr->RaceDescPtr->ship_data.ship_sounds =
432 SetAbsSoundIndex (StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 2);
433
434 StarShipPtr->cur_status_flags &=
435 ~(SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED);
436 }
437 else
438 {
439 StarShipPtr->RaceDescPtr->cyborg_control.WeaponRange = CLOSE_RANGE_WEAPON;
440 StarShipPtr->RaceDescPtr->ship_info.ship_flags &= ~SEEKING_WEAPON;
441 StarShipPtr->RaceDescPtr->ship_info.ship_flags |= IMMEDIATE_WEAPON;
442 StarShipPtr->RaceDescPtr->ship_data.ship_sounds =
443 SetAbsSoundIndex (StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 0);
444
445 if (StarShipPtr->cur_status_flags
446 & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED))
447 StarShipPtr->cur_status_flags |=
448 SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED;
449 }
450 }
451 }
452
453 static void
mmrnmhrm_preprocess(ELEMENT * ElementPtr)454 mmrnmhrm_preprocess (ELEMENT *ElementPtr)
455 {
456 STARSHIP *StarShipPtr;
457
458 GetElementStarShip (ElementPtr, &StarShipPtr);
459
460 if (!(ElementPtr->state_flags & APPEARING))
461 {
462 if ((StarShipPtr->cur_status_flags & SPECIAL)
463 && StarShipPtr->special_counter == 0)
464 {
465 /* Either we transform or text will flash */
466 if (DeltaEnergy (ElementPtr,
467 -StarShipPtr->RaceDescPtr->characteristics.special_energy_cost))
468 {
469 if (ElementPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.ship)
470 ElementPtr->next.image.farray =
471 StarShipPtr->RaceDescPtr->ship_data.special;
472 else
473 ElementPtr->next.image.farray =
474 StarShipPtr->RaceDescPtr->ship_data.ship;
475 ElementPtr->next.image.frame =
476 SetEquFrameIndex (ElementPtr->next.image.farray[0],
477 ElementPtr->next.image.frame);
478 ElementPtr->state_flags |= CHANGING;
479
480 StarShipPtr->special_counter =
481 StarShipPtr->RaceDescPtr->characteristics.special_wait;
482 }
483 }
484 }
485 }
486
487 static void
uninit_mmrnmhrm(RACE_DESC * pRaceDesc)488 uninit_mmrnmhrm (RACE_DESC *pRaceDesc)
489 {
490 SetCustomShipData (pRaceDesc, NULL);
491 }
492
493 RACE_DESC*
init_mmrnmhrm(void)494 init_mmrnmhrm (void)
495 {
496 RACE_DESC *RaceDescPtr;
497 // The caller of this func will copy the struct
498 static RACE_DESC new_mmrnmhrm_desc;
499 MMRNMHRM_DATA otherwing_desc;
500
501 mmrnmhrm_desc.uninit_func = uninit_mmrnmhrm;
502 mmrnmhrm_desc.preprocess_func = mmrnmhrm_preprocess;
503 mmrnmhrm_desc.postprocess_func = mmrnmhrm_postprocess;
504 mmrnmhrm_desc.init_weapon_func = initialize_dual_weapons;
505 mmrnmhrm_desc.cyborg_control.intelligence_func = mmrnmhrm_intelligence;
506
507 new_mmrnmhrm_desc = mmrnmhrm_desc;
508
509 otherwing_desc.max_thrust = YWING_MAX_THRUST;
510 otherwing_desc.thrust_increment = YWING_THRUST_INCREMENT;
511 otherwing_desc.energy_regeneration = YWING_ENERGY_REGENERATION;
512 otherwing_desc.weapon_energy_cost = YWING_WEAPON_ENERGY_COST;
513 otherwing_desc.special_energy_cost = YWING_SPECIAL_ENERGY_COST;
514 otherwing_desc.energy_wait = YWING_ENERGY_WAIT;
515 otherwing_desc.turn_wait = YWING_TURN_WAIT;
516 otherwing_desc.thrust_wait = YWING_THRUST_WAIT;
517 otherwing_desc.weapon_wait = YWING_WEAPON_WAIT;
518 otherwing_desc.special_wait = YWING_SPECIAL_WAIT;
519 otherwing_desc.ship_mass = SHIP_MASS;
520
521 SetCustomShipData (&new_mmrnmhrm_desc, &otherwing_desc);
522
523 RaceDescPtr = &new_mmrnmhrm_desc;
524
525 return (RaceDescPtr);
526 }
527
528