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