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