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 "blackurq.h"
21 #include "resinst.h"
22
23 #include "uqm/globdata.h"
24
25 // Core characteristics
26 #define MAX_CREW MAX_CREW_SIZE
27 #define MAX_ENERGY MAX_ENERGY_SIZE
28 #define ENERGY_REGENERATION 1
29 #define ENERGY_WAIT 4
30 #define MAX_THRUST 30
31 #define THRUST_INCREMENT 6
32 #define TURN_WAIT 4
33 #define THRUST_WAIT 6
34 #define SHIP_MASS 10
35
36 // Buzzsaw
37 #define WEAPON_ENERGY_COST 6
38 #define WEAPON_WAIT 6
39 #define MISSILE_OFFSET 9
40 #define KOHR_AH_OFFSET 28
41 #define MISSILE_SPEED 64
42 #define MISSILE_LIFE 64
43 /* actually, it's as long as you hold the button down.*/
44 #define MISSILE_HITS 10
45 #define MISSILE_DAMAGE 4
46 #define SAW_RATE 0
47 #define MAX_SAWS 8
48 #define ACTIVATE_RANGE 224
49 /* Originally SPACE_WIDTH - the distance within which
50 * stationary sawblades will home */
51 #define TRACK_WAIT 4
52 #define FRAGMENT_SPEED MISSILE_SPEED
53 #define FRAGMENT_LIFE 10
54 #define FRAGMENT_RANGE (FRAGMENT_LIFE * FRAGMENT_SPEED)
55
56 // F.R.I.E.D.
57 #define SPECIAL_ENERGY_COST (MAX_ENERGY_SIZE / 2)
58 #define SPECIAL_WAIT 9
59 #define GAS_OFFSET 2
60 #define GAS_SPEED 16
61 #define GAS_RATE 2 /* Controls animation of the gas cloud decay - the decay
62 * animation advances one frame every GAS_RATE frames. */
63 #define GAS_HITS 100
64 #define GAS_DAMAGE 3
65 #define GAS_ALT_DAMAGE 50
66 #define NUM_GAS_CLOUDS 16
67
68 static RACE_DESC black_urquan_desc =
69 {
70 { /* SHIP_INFO */
71 FIRES_FORE,
72 30, /* Super Melee cost */
73 MAX_CREW, MAX_CREW,
74 MAX_ENERGY, MAX_ENERGY,
75 KOHR_AH_RACE_STRINGS,
76 KOHR_AH_ICON_MASK_PMAP_ANIM,
77 KOHR_AH_MICON_MASK_PMAP_ANIM,
78 NULL, NULL, NULL
79 },
80 { /* FLEET_STUFF */
81 2666 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
82 { /* Known location (center of SoI) */
83 6000, 6250,
84 },
85 },
86 {
87 MAX_THRUST,
88 THRUST_INCREMENT,
89 ENERGY_REGENERATION,
90 WEAPON_ENERGY_COST,
91 SPECIAL_ENERGY_COST,
92 ENERGY_WAIT,
93 TURN_WAIT,
94 THRUST_WAIT,
95 WEAPON_WAIT,
96 SPECIAL_WAIT,
97 SHIP_MASS,
98 },
99 {
100 {
101 KOHR_AH_BIG_MASK_PMAP_ANIM,
102 KOHR_AH_MED_MASK_PMAP_ANIM,
103 KOHR_AH_SML_MASK_PMAP_ANIM,
104 },
105 {
106 BUZZSAW_BIG_MASK_PMAP_ANIM,
107 BUZZSAW_MED_MASK_PMAP_ANIM,
108 BUZZSAW_SML_MASK_PMAP_ANIM,
109 },
110 {
111 GAS_BIG_MASK_PMAP_ANIM,
112 GAS_MED_MASK_PMAP_ANIM,
113 GAS_SML_MASK_PMAP_ANIM,
114 },
115 {
116 KOHR_AH_CAPTAIN_MASK_PMAP_ANIM,
117 NULL, NULL, NULL, NULL, NULL
118 },
119 KOHR_AH_VICTORY_SONG,
120 KOHR_AH_SHIP_SOUNDS,
121 { NULL, NULL, NULL },
122 { NULL, NULL, NULL },
123 { NULL, NULL, NULL },
124 NULL, NULL
125 },
126 {
127 0,
128 CLOSE_RANGE_WEAPON,
129 NULL,
130 },
131 (UNINIT_FUNC *) NULL,
132 (PREPROCESS_FUNC *) NULL,
133 (POSTPROCESS_FUNC *) NULL,
134 (INIT_WEAPON_FUNC *) NULL,
135 0,
136 0, /* CodeRef */
137 };
138
139 static void
spin_preprocess(ELEMENT * ElementPtr)140 spin_preprocess (ELEMENT *ElementPtr)
141 {
142 ELEMENT *ShipPtr;
143 STARSHIP *StarShipPtr;
144
145 GetElementStarShip (ElementPtr, &StarShipPtr);
146 LockElement (StarShipPtr->hShip, &ShipPtr);
147 if (ShipPtr->crew_level
148 && ++StarShipPtr->RaceDescPtr->characteristics.special_wait > MAX_SAWS)
149 {
150 ElementPtr->life_span = 1;
151 ElementPtr->state_flags |= DISAPPEARING;
152 }
153 else
154 {
155 ++ElementPtr->life_span;
156 if (ElementPtr->turn_wait)
157 --ElementPtr->turn_wait;
158 else
159 {
160 #define LAST_SPIN_INDEX 1
161 if (GetFrameIndex (
162 ElementPtr->current.image.frame
163 ) < LAST_SPIN_INDEX)
164 ElementPtr->next.image.frame =
165 IncFrameIndex (ElementPtr->current.image.frame);
166 else
167 ElementPtr->next.image.frame =
168 SetAbsFrameIndex (ElementPtr->current.image.frame, 0);
169 ElementPtr->state_flags |= CHANGING;
170
171 ElementPtr->turn_wait = SAW_RATE;
172 }
173 }
174 UnlockElement (StarShipPtr->hShip);
175 }
176
177 static void
buzztrack_preprocess(ELEMENT * ElementPtr)178 buzztrack_preprocess (ELEMENT *ElementPtr)
179 {
180 if (ElementPtr->thrust_wait)
181 --ElementPtr->thrust_wait;
182 else
183 {
184 COUNT facing = 0;
185
186 if (ElementPtr->hTarget == 0
187 && TrackShip (ElementPtr, &facing) < 0)
188 {
189 ZeroVelocityComponents (&ElementPtr->velocity);
190 }
191 else
192 {
193 SIZE delta_x, delta_y;
194 ELEMENT *eptr;
195
196 LockElement (ElementPtr->hTarget, &eptr);
197 delta_x = eptr->current.location.x
198 - ElementPtr->current.location.x;
199 delta_y = eptr->current.location.y
200 - ElementPtr->current.location.y;
201 UnlockElement (ElementPtr->hTarget);
202 delta_x = WRAP_DELTA_X (delta_x);
203 delta_y = WRAP_DELTA_Y (delta_y);
204 facing = NORMALIZE_FACING (
205 ANGLE_TO_FACING (ARCTAN (delta_x, delta_y))
206 );
207
208 if (delta_x < 0)
209 delta_x = -delta_x;
210 if (delta_y < 0)
211 delta_y = -delta_y;
212 delta_x = WORLD_TO_DISPLAY (delta_x);
213 delta_y = WORLD_TO_DISPLAY (delta_y);
214 if (delta_x >= ACTIVATE_RANGE
215 || delta_y >= ACTIVATE_RANGE
216 || (DWORD)((UWORD)delta_x * delta_x)
217 + (DWORD)((UWORD)delta_y * delta_y) >=
218 (DWORD)ACTIVATE_RANGE * ACTIVATE_RANGE)
219 {
220 ZeroVelocityComponents (&ElementPtr->velocity);
221 }
222 else
223 {
224 ElementPtr->thrust_wait = TRACK_WAIT;
225 SetVelocityVector (&ElementPtr->velocity,
226 DISPLAY_TO_WORLD (2), facing);
227 }
228 }
229 }
230
231 spin_preprocess (ElementPtr);
232 }
233
234 static void
decelerate_preprocess(ELEMENT * ElementPtr)235 decelerate_preprocess (ELEMENT *ElementPtr)
236 {
237 SIZE dx, dy;
238
239 GetCurrentVelocityComponents (&ElementPtr->velocity, &dx, &dy);
240 dx /= 2;
241 dy /= 2;
242 SetVelocityComponents (&ElementPtr->velocity, dx, dy);
243 if (dx == 0 && dy == 0)
244 {
245 ElementPtr->preprocess_func = buzztrack_preprocess;
246 }
247
248 spin_preprocess (ElementPtr);
249 }
250
251 static void
splinter_preprocess(ELEMENT * ElementPtr)252 splinter_preprocess (ELEMENT *ElementPtr)
253 {
254 ElementPtr->next.image.frame =
255 IncFrameIndex (ElementPtr->current.image.frame);
256 ElementPtr->state_flags |= CHANGING;
257 }
258
259 static void
buzzsaw_collision(ELEMENT * ElementPtr0,POINT * pPt0,ELEMENT * ElementPtr1,POINT * pPt1)260 buzzsaw_collision (ELEMENT *ElementPtr0, POINT *pPt0,
261 ELEMENT *ElementPtr1, POINT *pPt1)
262 {
263 weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
264
265 if (ElementPtr0->state_flags & DISAPPEARING)
266 {
267 ElementPtr0->state_flags &= ~DISAPPEARING;
268 ElementPtr0->state_flags |= NONSOLID | CHANGING;
269 ElementPtr0->life_span = 5;
270 ElementPtr0->next.image.frame =
271 SetAbsFrameIndex (ElementPtr0->current.image.frame, 2);
272
273 ElementPtr0->preprocess_func = splinter_preprocess;
274 }
275 }
276
277 static void
buzzsaw_preprocess(ELEMENT * ElementPtr)278 buzzsaw_preprocess (ELEMENT *ElementPtr)
279 {
280 STARSHIP *StarShipPtr;
281
282 GetElementStarShip (ElementPtr, &StarShipPtr);
283 if (!(StarShipPtr->cur_status_flags & WEAPON))
284 {
285 ElementPtr->life_span >>= 1;
286 ElementPtr->preprocess_func = decelerate_preprocess;
287 }
288
289 spin_preprocess (ElementPtr);
290 }
291
292 static void
buzzsaw_postprocess(ELEMENT * ElementPtr)293 buzzsaw_postprocess (ELEMENT *ElementPtr)
294 {
295 HELEMENT hElement;
296
297 ElementPtr->postprocess_func = 0;
298 hElement = AllocElement ();
299 if (hElement)
300 {
301 COUNT primIndex;
302 ELEMENT *ListElementPtr;
303 STARSHIP *StarShipPtr;
304
305 LockElement (hElement, &ListElementPtr);
306 primIndex = ListElementPtr->PrimIndex;
307 *ListElementPtr = *ElementPtr;
308 ListElementPtr->PrimIndex = primIndex;
309 (GLOBAL (DisplayArray))[primIndex] =
310 (GLOBAL (DisplayArray))[ElementPtr->PrimIndex];
311 ListElementPtr->current = ListElementPtr->next;
312 InitIntersectStartPoint (ListElementPtr);
313 InitIntersectEndPoint (ListElementPtr);
314 ListElementPtr->state_flags = (ListElementPtr->state_flags
315 & ~(PRE_PROCESS | CHANGING | APPEARING))
316 | POST_PROCESS;
317 UnlockElement (hElement);
318
319 GetElementStarShip (ElementPtr, &StarShipPtr);
320 LockElement (StarShipPtr->hShip, &ListElementPtr);
321 InsertElement (hElement, GetSuccElement (ListElementPtr));
322 UnlockElement (StarShipPtr->hShip);
323
324 ElementPtr->life_span = 0;
325 }
326 }
327
328 static COUNT
initialize_buzzsaw(ELEMENT * ShipPtr,HELEMENT SawArray[])329 initialize_buzzsaw (ELEMENT *ShipPtr, HELEMENT SawArray[])
330 {
331 STARSHIP *StarShipPtr;
332 MISSILE_BLOCK MissileBlock;
333
334 GetElementStarShip (ShipPtr, &StarShipPtr);
335 MissileBlock.cx = ShipPtr->next.location.x;
336 MissileBlock.cy = ShipPtr->next.location.y;
337 MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
338 MissileBlock.face = StarShipPtr->ShipFacing;
339 MissileBlock.index = 0;
340 MissileBlock.sender = ShipPtr->playerNr;
341 MissileBlock.flags = IGNORE_SIMILAR;
342 MissileBlock.pixoffs = KOHR_AH_OFFSET;
343 MissileBlock.speed = MISSILE_SPEED;
344 MissileBlock.hit_points = MISSILE_HITS;
345 MissileBlock.damage = MISSILE_DAMAGE;
346 MissileBlock.life = MISSILE_LIFE;
347 MissileBlock.preprocess_func = buzzsaw_preprocess;
348 MissileBlock.blast_offs = MISSILE_OFFSET;
349 SawArray[0] = initialize_missile (&MissileBlock);
350
351 if (SawArray[0])
352 {
353 ELEMENT *SawPtr;
354
355 LockElement (SawArray[0], &SawPtr);
356 SawPtr->turn_wait = SAW_RATE;
357 SawPtr->thrust_wait = 0;
358 SawPtr->postprocess_func = buzzsaw_postprocess;
359 SawPtr->collision_func = buzzsaw_collision;
360 UnlockElement (SawArray[0]);
361 }
362
363 return (1);
364 }
365
366 static void
black_urquan_intelligence(ELEMENT * ShipPtr,EVALUATE_DESC * ObjectsOfConcern,COUNT ConcernCounter)367 black_urquan_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
368 COUNT ConcernCounter)
369 {
370 EVALUATE_DESC *lpEvalDesc;
371 STARSHIP *StarShipPtr;
372
373 lpEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX];
374 if (lpEvalDesc->ObjectPtr
375 && lpEvalDesc->MoveState == ENTICE
376 && (lpEvalDesc->ObjectPtr->state_flags & CREW_OBJECT)
377 && lpEvalDesc->which_turn <= 8)
378 lpEvalDesc->MoveState = PURSUE;
379
380 ship_intelligence (ShipPtr,
381 ObjectsOfConcern, ConcernCounter);
382
383 GetElementStarShip (ShipPtr, &StarShipPtr);
384 StarShipPtr->ship_input_state &= ~SPECIAL;
385
386 if (StarShipPtr->special_counter == 0
387 && StarShipPtr->RaceDescPtr->ship_info.energy_level >= SPECIAL_ENERGY_COST
388 && lpEvalDesc->ObjectPtr
389 && lpEvalDesc->which_turn <= 8)
390 StarShipPtr->ship_input_state |= SPECIAL;
391
392 lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
393 if (lpEvalDesc->ObjectPtr)
394 {
395 HELEMENT h, hNext;
396 ELEMENT *BuzzSawPtr;
397
398 h = (StarShipPtr->old_status_flags & WEAPON) ?
399 GetSuccElement (ShipPtr) : (HELEMENT)0;
400 for (; h; h = hNext)
401 {
402 LockElement (h, &BuzzSawPtr);
403 hNext = GetSuccElement (BuzzSawPtr);
404 if (!(BuzzSawPtr->state_flags & NONSOLID)
405 && BuzzSawPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.weapon
406 && BuzzSawPtr->life_span > MISSILE_LIFE * 3 / 4
407 && elementsOfSamePlayer (BuzzSawPtr, ShipPtr))
408 {
409 {
410 //COUNT which_turn;
411
412 if (!PlotIntercept (BuzzSawPtr,
413 lpEvalDesc->ObjectPtr, BuzzSawPtr->life_span,
414 FRAGMENT_RANGE / 2))
415 StarShipPtr->ship_input_state &= ~WEAPON;
416 else if (StarShipPtr->weapon_counter == 0)
417 StarShipPtr->ship_input_state |= WEAPON;
418
419 UnlockElement (h);
420 break;
421 }
422 hNext = 0;
423 }
424 UnlockElement (h);
425 }
426
427 if (h == 0)
428 {
429 if (StarShipPtr->old_status_flags & WEAPON)
430 StarShipPtr->ship_input_state &= ~WEAPON;
431 else if (StarShipPtr->weapon_counter == 0
432 && ship_weapons (ShipPtr, lpEvalDesc->ObjectPtr, FRAGMENT_RANGE / 2))
433 StarShipPtr->ship_input_state |= WEAPON;
434
435 if (StarShipPtr->special_counter == 0
436 && !(StarShipPtr->ship_input_state & WEAPON)
437 && lpEvalDesc->which_turn <= 8
438 && (StarShipPtr->ship_input_state & (LEFT | RIGHT))
439 && StarShipPtr->RaceDescPtr->ship_info.energy_level >=
440 SPECIAL_ENERGY_COST)
441 StarShipPtr->ship_input_state |= SPECIAL;
442 }
443 }
444 }
445
446 static void
gas_cloud_preprocess(ELEMENT * ElementPtr)447 gas_cloud_preprocess (ELEMENT *ElementPtr)
448 {
449 if (ElementPtr->turn_wait)
450 --ElementPtr->turn_wait;
451 else
452 {
453 ElementPtr->next.image.frame =
454 IncFrameIndex (ElementPtr->current.image.frame);
455 ElementPtr->state_flags |= CHANGING;
456
457 ElementPtr->turn_wait = GAS_RATE;
458 }
459 }
460
461 static void
gas_cloud_collision(ELEMENT * ElementPtr0,POINT * pPt0,ELEMENT * ElementPtr1,POINT * pPt1)462 gas_cloud_collision (ELEMENT *ElementPtr0, POINT *pPt0,
463 ELEMENT *ElementPtr1, POINT *pPt1)
464 {
465 if (ElementPtr1->state_flags & PLAYER_SHIP)
466 ElementPtr0->mass_points = GAS_DAMAGE;
467 else
468 ElementPtr0->mass_points = GAS_ALT_DAMAGE;
469
470 weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
471 }
472
473 static void
spawn_gas_cloud(ELEMENT * ElementPtr)474 spawn_gas_cloud (ELEMENT *ElementPtr)
475 {
476 SIZE dx, dy;
477 STARSHIP *StarShipPtr;
478 MISSILE_BLOCK MissileBlock;
479
480 GetElementStarShip (ElementPtr, &StarShipPtr);
481 MissileBlock.cx = ElementPtr->next.location.x;
482 MissileBlock.cy = ElementPtr->next.location.y;
483 MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.special;
484 MissileBlock.index = 0;
485 MissileBlock.sender = ElementPtr->playerNr;
486 MissileBlock.flags = IGNORE_SIMILAR;
487 MissileBlock.pixoffs = 20;
488 MissileBlock.speed = GAS_SPEED;
489 MissileBlock.hit_points = GAS_HITS;
490 MissileBlock.damage = GAS_DAMAGE;
491 MissileBlock.life =
492 GetFrameCount (MissileBlock.farray[0]) * (GAS_RATE + 1) - 1;
493 MissileBlock.preprocess_func = gas_cloud_preprocess;
494 MissileBlock.blast_offs = GAS_OFFSET;
495
496 GetCurrentVelocityComponents (&ElementPtr->velocity, &dx, &dy);
497 for (MissileBlock.face = 0;
498 MissileBlock.face < ANGLE_TO_FACING (FULL_CIRCLE);
499 MissileBlock.face +=
500 (ANGLE_TO_FACING (FULL_CIRCLE) / NUM_GAS_CLOUDS))
501 {
502 HELEMENT hGasCloud;
503
504 hGasCloud = initialize_missile (&MissileBlock);
505 if (hGasCloud)
506 {
507 ELEMENT *GasCloudPtr;
508
509 LockElement (hGasCloud, &GasCloudPtr);
510 SetElementStarShip (GasCloudPtr, StarShipPtr);
511 GasCloudPtr->hTarget = 0;
512 GasCloudPtr->turn_wait = GAS_RATE - 1;
513 GasCloudPtr->collision_func = gas_cloud_collision;
514 DeltaVelocityComponents (&GasCloudPtr->velocity, dx, dy);
515 UnlockElement (hGasCloud);
516 PutElement (hGasCloud);
517 }
518 }
519 }
520
521 static void
black_urquan_postprocess(ELEMENT * ElementPtr)522 black_urquan_postprocess (ELEMENT *ElementPtr)
523 {
524 STARSHIP *StarShipPtr;
525
526 GetElementStarShip (ElementPtr, &StarShipPtr);
527 if ((StarShipPtr->cur_status_flags & SPECIAL)
528 && StarShipPtr->special_counter == 0
529 && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
530 {
531 spawn_gas_cloud (ElementPtr);
532
533 ProcessSound (SetAbsSoundIndex (
534 StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
535
536 StarShipPtr->special_counter = SPECIAL_WAIT;
537 }
538 }
539
540 static void
black_urquan_preprocess(ELEMENT * ElementPtr)541 black_urquan_preprocess (ELEMENT *ElementPtr)
542 {
543 STARSHIP *StarShipPtr;
544
545 GetElementStarShip (ElementPtr, &StarShipPtr);
546 /* no spinning disks */
547 StarShipPtr->RaceDescPtr->characteristics.special_wait = 0;
548 if (StarShipPtr->weapon_counter == 0
549 && (StarShipPtr->cur_status_flags
550 & StarShipPtr->old_status_flags & WEAPON))
551 ++StarShipPtr->weapon_counter;
552 }
553
554 RACE_DESC*
init_black_urquan(void)555 init_black_urquan (void)
556 {
557 RACE_DESC *RaceDescPtr;
558
559 black_urquan_desc.preprocess_func = black_urquan_preprocess;
560 black_urquan_desc.postprocess_func = black_urquan_postprocess;
561 black_urquan_desc.init_weapon_func = initialize_buzzsaw;
562 black_urquan_desc.cyborg_control.intelligence_func = black_urquan_intelligence;
563
564 RaceDescPtr = &black_urquan_desc;
565
566 return (RaceDescPtr);
567 }
568