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 "human.h"
21 #include "resinst.h"
22 
23 #include "uqm/colors.h"
24 #include "uqm/globdata.h"
25 
26 // Core characteristics
27 #define MAX_CREW 18
28 #define MAX_ENERGY 18
29 #define ENERGY_REGENERATION 1
30 #define ENERGY_WAIT 8
31 #define MAX_THRUST /* DISPLAY_TO_WORLD (6) */ 24
32 #define THRUST_INCREMENT /* DISPLAY_TO_WORLD (2) */ 3
33 #define THRUST_WAIT 4
34 #define TURN_WAIT 1
35 #define SHIP_MASS 6
36 
37 // Nuke
38 #define WEAPON_ENERGY_COST 9
39 #define WEAPON_WAIT 10
40 #define HUMAN_OFFSET 42
41 #define NUKE_OFFSET 8
42 #define MIN_MISSILE_SPEED DISPLAY_TO_WORLD (10)
43 #define MAX_MISSILE_SPEED DISPLAY_TO_WORLD (20)
44 #define MISSILE_SPEED (MAX_THRUST >= MIN_MISSILE_SPEED ? \
45 		MAX_THRUST : MIN_MISSILE_SPEED)
46 #define THRUST_SCALE DISPLAY_TO_WORLD (1)
47 #define MISSILE_LIFE 60
48 #define MISSILE_HITS 1
49 #define MISSILE_DAMAGE 4
50 #define TRACK_WAIT 3
51 
52 // Point-Defense Laser
53 #define SPECIAL_ENERGY_COST 4
54 #define SPECIAL_WAIT 9
55 #define LASER_RANGE (UWORD)100
56 
57 static RACE_DESC human_desc =
58 {
59 	{ /* SHIP_INFO */
60 		FIRES_FORE | SEEKING_WEAPON | POINT_DEFENSE,
61 		11, /* Super Melee cost */
62 		MAX_CREW, MAX_CREW,
63 		MAX_ENERGY, MAX_ENERGY,
64 		HUMAN_RACE_STRINGS,
65 		HUMAN_ICON_MASK_PMAP_ANIM,
66 		HUMAN_MICON_MASK_PMAP_ANIM,
67 		NULL, NULL, NULL
68 	},
69 	{ /* FLEET_STUFF */
70 		0, /* Initial sphere of influence radius */
71 		{ /* Known location (center of SoI) */
72 			1752, 1450,
73 		},
74 	},
75 	{
76 		MAX_THRUST,
77 		THRUST_INCREMENT,
78 		ENERGY_REGENERATION,
79 		WEAPON_ENERGY_COST,
80 		SPECIAL_ENERGY_COST,
81 		ENERGY_WAIT,
82 		TURN_WAIT,
83 		THRUST_WAIT,
84 		WEAPON_WAIT,
85 		SPECIAL_WAIT,
86 		SHIP_MASS,
87 	},
88 	{
89 		{
90 			HUMAN_BIG_MASK_PMAP_ANIM,
91 			HUMAN_MED_MASK_PMAP_ANIM,
92 			HUMAN_SML_MASK_PMAP_ANIM,
93 		},
94 		{
95 			SATURN_BIG_MASK_PMAP_ANIM,
96 			SATURN_MED_MASK_PMAP_ANIM,
97 			SATURN_SML_MASK_PMAP_ANIM,
98 		},
99 		{
100 			NULL_RESOURCE,
101 			NULL_RESOURCE,
102 			NULL_RESOURCE,
103 		},
104 		{
105 			HUMAN_CAPTAIN_MASK_PMAP_ANIM,
106 			NULL, NULL, NULL, NULL, NULL
107 		},
108 		HUMAN_VICTORY_SONG,
109 		HUMAN_SHIP_SOUNDS,
110 		{ NULL, NULL, NULL },
111 		{ NULL, NULL, NULL },
112 		{ NULL, NULL, NULL },
113 		NULL, NULL
114 	},
115 	{
116 		0,
117 		LONG_RANGE_WEAPON,
118 		NULL,
119 	},
120 	(UNINIT_FUNC *) NULL,
121 	(PREPROCESS_FUNC *) NULL,
122 	(POSTPROCESS_FUNC *) NULL,
123 	(INIT_WEAPON_FUNC *) NULL,
124 	0,
125 	0, /* CodeRef */
126 };
127 
128 static void
nuke_preprocess(ELEMENT * ElementPtr)129 nuke_preprocess (ELEMENT *ElementPtr)
130 {
131 	COUNT facing;
132 
133 	facing = GetFrameIndex (ElementPtr->next.image.frame);
134 	if (ElementPtr->turn_wait > 0)
135 		--ElementPtr->turn_wait;
136 	else
137 	{
138 		if (TrackShip (ElementPtr, &facing) > 0)
139 		{
140 			ElementPtr->next.image.frame =
141 					SetAbsFrameIndex (ElementPtr->next.image.frame,
142 					facing);
143 			ElementPtr->state_flags |= CHANGING;
144 		}
145 
146 		ElementPtr->turn_wait = TRACK_WAIT;
147 	}
148 
149 	{
150 		SIZE speed;
151 
152 		if ((speed = MISSILE_SPEED +
153 				((MISSILE_LIFE - ElementPtr->life_span) *
154 				THRUST_SCALE)) > MAX_MISSILE_SPEED)
155 			speed = MAX_MISSILE_SPEED;
156 		SetVelocityVector (&ElementPtr->velocity,
157 				speed, facing);
158 	}
159 }
160 
161 static void
spawn_point_defense(ELEMENT * ElementPtr)162 spawn_point_defense (ELEMENT *ElementPtr)
163 {
164 	STARSHIP *StarShipPtr;
165 
166 	GetElementStarShip (ElementPtr, &StarShipPtr);
167 	if (ElementPtr->state_flags & PLAYER_SHIP)
168 	{
169 		HELEMENT hDefense;
170 
171 		hDefense = AllocElement ();
172 		if (hDefense)
173 		{
174 			ELEMENT *DefensePtr;
175 
176 			LockElement (hDefense, &DefensePtr);
177 			DefensePtr->playerNr = ElementPtr->playerNr;
178 			DefensePtr->state_flags = APPEARING | NONSOLID | FINITE_LIFE;
179 			DefensePtr->death_func = spawn_point_defense;
180 
181 			GetElementStarShip (ElementPtr, &StarShipPtr);
182 			SetElementStarShip (DefensePtr, StarShipPtr);
183 			UnlockElement (hDefense);
184 
185 			PutElement (hDefense);
186 		}
187 	}
188 	else
189 	{
190 		BOOLEAN PaidFor;
191 		HELEMENT hObject, hNextObject;
192 		ELEMENT *ShipPtr;
193 
194 		PaidFor = FALSE;
195 
196 		LockElement (StarShipPtr->hShip, &ShipPtr);
197 		for (hObject = GetTailElement (); hObject; hObject = hNextObject)
198 		{
199 			ELEMENT *ObjectPtr;
200 
201 			LockElement (hObject, &ObjectPtr);
202 			hNextObject = GetPredElement (ObjectPtr);
203 			if (ObjectPtr != ShipPtr && CollidingElement (ObjectPtr) &&
204 					!OBJECT_CLOAKED (ObjectPtr))
205 			{
206 				SIZE delta_x, delta_y;
207 
208 				delta_x = ObjectPtr->next.location.x -
209 						ShipPtr->next.location.x;
210 				delta_y = ObjectPtr->next.location.y -
211 						ShipPtr->next.location.y;
212 				if (delta_x < 0)
213 					delta_x = -delta_x;
214 				if (delta_y < 0)
215 					delta_y = -delta_y;
216 				delta_x = WORLD_TO_DISPLAY (delta_x);
217 				delta_y = WORLD_TO_DISPLAY (delta_y);
218 				if ((UWORD)delta_x <= LASER_RANGE &&
219 						(UWORD)delta_y <= LASER_RANGE &&
220 						(UWORD)delta_x * (UWORD)delta_x +
221 						(UWORD)delta_y * (UWORD)delta_y <=
222 						LASER_RANGE * LASER_RANGE)
223 				{
224 					HELEMENT hPointDefense;
225 					LASER_BLOCK LaserBlock;
226 
227 					if (!PaidFor)
228 					{
229 						if (!DeltaEnergy (ShipPtr, -SPECIAL_ENERGY_COST))
230 							break;
231 
232 						ProcessSound (SetAbsSoundIndex (
233 										/* POINT_DEFENSE_LASER */
234 								StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
235 						StarShipPtr->special_counter =
236 								StarShipPtr->RaceDescPtr->characteristics.special_wait;
237 						PaidFor = TRUE;
238 					}
239 
240 					LaserBlock.cx = ShipPtr->next.location.x;
241 					LaserBlock.cy = ShipPtr->next.location.y;
242 					LaserBlock.face = 0;
243 					LaserBlock.ex = ObjectPtr->next.location.x
244 							- ShipPtr->next.location.x;
245 					LaserBlock.ey = ObjectPtr->next.location.y
246 							- ShipPtr->next.location.y;
247 					LaserBlock.sender = ShipPtr->playerNr;
248 					LaserBlock.flags = IGNORE_SIMILAR;
249 					LaserBlock.pixoffs = 0;
250 					LaserBlock.color = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F);
251 					hPointDefense = initialize_laser (&LaserBlock);
252 					if (hPointDefense)
253 					{
254 						ELEMENT *PDPtr;
255 
256 						LockElement (hPointDefense, &PDPtr);
257 						SetElementStarShip (PDPtr, StarShipPtr);
258 						PDPtr->hTarget = 0;
259 						UnlockElement (hPointDefense);
260 
261 						PutElement (hPointDefense);
262 					}
263 				}
264 			}
265 			UnlockElement (hObject);
266 		}
267 		UnlockElement (StarShipPtr->hShip);
268 	}
269 }
270 
271 static COUNT
initialize_nuke(ELEMENT * ShipPtr,HELEMENT NukeArray[])272 initialize_nuke (ELEMENT *ShipPtr, HELEMENT NukeArray[])
273 {
274 	STARSHIP *StarShipPtr;
275 	MISSILE_BLOCK MissileBlock;
276 
277 	GetElementStarShip (ShipPtr, &StarShipPtr);
278 	MissileBlock.cx = ShipPtr->next.location.x;
279 	MissileBlock.cy = ShipPtr->next.location.y;
280 	MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
281 	MissileBlock.face = MissileBlock.index = StarShipPtr->ShipFacing;
282 	MissileBlock.sender = ShipPtr->playerNr;
283 	MissileBlock.flags = 0;
284 	MissileBlock.pixoffs = HUMAN_OFFSET;
285 	MissileBlock.speed = MISSILE_SPEED;
286 	MissileBlock.hit_points = MISSILE_HITS;
287 	MissileBlock.damage = MISSILE_DAMAGE;
288 	MissileBlock.life = MISSILE_LIFE;
289 	MissileBlock.preprocess_func = nuke_preprocess;
290 	MissileBlock.blast_offs = NUKE_OFFSET;
291 	NukeArray[0] = initialize_missile (&MissileBlock);
292 
293 	if (NukeArray[0])
294 	{
295 		ELEMENT *NukePtr;
296 
297 		LockElement (NukeArray[0], &NukePtr);
298 		NukePtr->turn_wait = TRACK_WAIT;
299 		UnlockElement (NukeArray[0]);
300 	}
301 
302 	return (1);
303 }
304 
305 static void
human_intelligence(ELEMENT * ShipPtr,EVALUATE_DESC * ObjectsOfConcern,COUNT ConcernCounter)306 human_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
307 		COUNT ConcernCounter)
308 {
309 	STARSHIP *StarShipPtr;
310 
311 	GetElementStarShip (ShipPtr, &StarShipPtr);
312 	if (StarShipPtr->special_counter == 0
313 			&& ((ObjectsOfConcern[ENEMY_WEAPON_INDEX].ObjectPtr != NULL
314 			&& ObjectsOfConcern[ENEMY_WEAPON_INDEX].which_turn <= 2)
315 			|| (ObjectsOfConcern[ENEMY_SHIP_INDEX].ObjectPtr != NULL
316 			&& ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn <= 4)))
317 		StarShipPtr->ship_input_state |= SPECIAL;
318 	else
319 		StarShipPtr->ship_input_state &= ~SPECIAL;
320 	ObjectsOfConcern[ENEMY_WEAPON_INDEX].ObjectPtr = NULL;
321 
322 	ship_intelligence (ShipPtr,
323 			ObjectsOfConcern, ConcernCounter);
324 
325 	if (StarShipPtr->weapon_counter == 0)
326 	{
327 		if (ObjectsOfConcern[ENEMY_SHIP_INDEX].ObjectPtr
328 				&& (!(StarShipPtr->ship_input_state & (LEFT | RIGHT /* | THRUST */))
329 				|| ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn <= 12))
330 			StarShipPtr->ship_input_state |= WEAPON;
331 	}
332 }
333 
334 static void
human_postprocess(ELEMENT * ElementPtr)335 human_postprocess (ELEMENT *ElementPtr)
336 {
337 	STARSHIP *StarShipPtr;
338 
339 	GetElementStarShip (ElementPtr, &StarShipPtr);
340 	if ((StarShipPtr->cur_status_flags & SPECIAL)
341 			&& StarShipPtr->special_counter == 0)
342 	{
343 		spawn_point_defense (ElementPtr);
344 	}
345 }
346 
347 RACE_DESC*
init_human(void)348 init_human (void)
349 {
350 	RACE_DESC *RaceDescPtr;
351 
352 	human_desc.postprocess_func = human_postprocess;
353 	human_desc.init_weapon_func = initialize_nuke;
354 	human_desc.cyborg_control.intelligence_func = human_intelligence;
355 
356 	RaceDescPtr = &human_desc;
357 
358 	return (RaceDescPtr);
359 }
360 
361