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 "build.h"
20
21 #include "races.h"
22 #include "master.h"
23 #include "sis.h"
24 #include "setup.h"
25 #include "libs/compiler.h"
26 #include "libs/mathlib.h"
27
28
29 // Allocate a new STARSHIP or SHIP_FRAGMENT and put it in the queue
30 HLINK
Build(QUEUE * pQueue,SPECIES_ID SpeciesID)31 Build (QUEUE *pQueue, SPECIES_ID SpeciesID)
32 {
33 HLINK hNewShip;
34 SHIP_BASE *ShipPtr;
35
36 assert (GetLinkSize (pQueue) == sizeof (STARSHIP) ||
37 GetLinkSize (pQueue) == sizeof (SHIP_FRAGMENT));
38
39 hNewShip = AllocLink (pQueue);
40 if (!hNewShip)
41 return 0;
42
43 ShipPtr = (SHIP_BASE *) LockLink (pQueue, hNewShip);
44 memset (ShipPtr, 0, GetLinkSize (pQueue));
45 ShipPtr->SpeciesID = SpeciesID;
46
47 UnlockLink (pQueue, hNewShip);
48 PutQueue (pQueue, hNewShip);
49
50 return hNewShip;
51 }
52
53 HLINK
GetStarShipFromIndex(QUEUE * pShipQ,COUNT Index)54 GetStarShipFromIndex (QUEUE *pShipQ, COUNT Index)
55 {
56 HLINK hStarShip, hNextShip;
57
58 for (hStarShip = GetHeadLink (pShipQ);
59 Index > 0 && hStarShip; hStarShip = hNextShip, --Index)
60 {
61 LINK *StarShipPtr;
62
63 StarShipPtr = LockLink (pShipQ, hStarShip);
64 hNextShip = _GetSuccLink (StarShipPtr);
65 UnlockLink (pShipQ, hStarShip);
66 }
67
68 return (hStarShip);
69 }
70
71 HSHIPFRAG
GetEscortByStarShipIndex(COUNT index)72 GetEscortByStarShipIndex (COUNT index)
73 {
74 HSHIPFRAG hStarShip;
75 HSHIPFRAG hNextShip;
76 SHIP_FRAGMENT *StarShipPtr;
77
78 for (hStarShip = GetHeadLink (&GLOBAL (built_ship_q));
79 hStarShip; hStarShip = hNextShip)
80 {
81 StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
82
83 if (StarShipPtr->index == index)
84 {
85 UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
86 break;
87 }
88
89 hNextShip = _GetSuccLink (StarShipPtr);
90 UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
91 }
92
93 return hStarShip;
94 }
95
96 /*
97 * Give the player 'count' ships of the specified race,
98 * limited by the number of free slots.
99 * Returns the number of ships added.
100 */
101 COUNT
AddEscortShips(COUNT race,SIZE count)102 AddEscortShips (COUNT race, SIZE count)
103 {
104 HFLEETINFO hFleet;
105 BYTE which_window;
106 COUNT i;
107
108 hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), race);
109 if (!hFleet)
110 return 0;
111
112 assert (count > 0);
113
114 which_window = 0;
115 for (i = 0; i < (COUNT) count; i++)
116 {
117 HSHIPFRAG hStarShip;
118 HSHIPFRAG hOldShip;
119 SHIP_FRAGMENT *StarShipPtr;
120
121 hStarShip = CloneShipFragment (race, &GLOBAL (built_ship_q), 0);
122 if (!hStarShip)
123 break;
124
125 RemoveQueue (&GLOBAL (built_ship_q), hStarShip);
126
127 /* Find first available escort window */
128 while ((hOldShip = GetStarShipFromIndex (
129 &GLOBAL (built_ship_q), which_window++)))
130 {
131 BYTE win_loc;
132
133 StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hOldShip);
134 win_loc = StarShipPtr->index;
135 UnlockShipFrag (&GLOBAL (built_ship_q), hOldShip);
136 if (which_window <= win_loc)
137 break;
138 }
139
140 StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
141 StarShipPtr->index = which_window - 1;
142 UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
143
144 InsertQueue (&GLOBAL (built_ship_q), hStarShip, hOldShip);
145 }
146
147 DeltaSISGauges (UNDEFINED_DELTA, UNDEFINED_DELTA, UNDEFINED_DELTA);
148 return i;
149 }
150
151 /*
152 * Returns the total value of all the ships escorting the SIS.
153 */
154 COUNT
CalculateEscortsWorth(void)155 CalculateEscortsWorth (void)
156 {
157 COUNT ShipCost[] =
158 {
159 RACE_SHIP_COST
160 };
161 COUNT total = 0;
162 HSHIPFRAG hStarShip, hNextShip;
163
164 for (hStarShip = GetHeadLink (&GLOBAL (built_ship_q));
165 hStarShip; hStarShip = hNextShip)
166 {
167 SHIP_FRAGMENT *StarShipPtr;
168
169 StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
170 hNextShip = _GetSuccLink (StarShipPtr);
171 total += ShipCost[StarShipPtr->race_id];
172 UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
173 }
174 return total;
175 }
176
177 #if 0
178 /*
179 * Returns the size of the fleet of the specified race when the starmap was
180 * last checked. If the race has no SoI, 0 is returned.
181 */
182 COUNT
183 GetRaceKnownSize (COUNT race)
184 {
185 HFLEETINFO hFleet;
186 FLEET_INFO *FleetPtr;
187 COUNT result;
188
189 hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), race);
190 if (!hFleet)
191 return 0;
192
193 FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hFleet);
194
195 result = FleetPtr->known_strength;
196
197 UnlockFleetInfo (&GLOBAL (avail_race_q), hFleet);
198 return result;
199 }
200 #endif
201
202 /*
203 * Start or end an alliance with the specified race.
204 * Being in an alliance with a race makes their ships available for building
205 * in the shipyard.
206 * flag == TRUE: start an alliance
207 * flag == TRUE: end an alliance
208 */
209 COUNT
SetRaceAllied(COUNT race,BOOLEAN flag)210 SetRaceAllied (COUNT race, BOOLEAN flag) {
211 HFLEETINFO hFleet;
212 FLEET_INFO *FleetPtr;
213
214 hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), race);
215 if (!hFleet)
216 return 0;
217
218 FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hFleet);
219
220 if (FleetPtr->allied_state == DEAD_GUY)
221 {
222 /* Strange request, silently ignore it */
223 }
224 else
225 {
226 FleetPtr->allied_state = (flag ? GOOD_GUY : BAD_GUY);
227 }
228
229 UnlockFleetInfo (&GLOBAL (avail_race_q), hFleet);
230 return 1;
231 }
232
233 /*
234 * Make the sphere of influence for the specified race shown on the starmap
235 * in the future.
236 * The value returned is 'race', unless the type of ship is only available
237 * in SuperMelee, in which case 0 is returned.
238 */
239 COUNT
StartSphereTracking(COUNT race)240 StartSphereTracking (COUNT race)
241 {
242 HFLEETINFO hFleet;
243 FLEET_INFO *FleetPtr;
244
245 hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), race);
246 if (!hFleet)
247 return 0;
248
249 FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hFleet);
250
251 if (FleetPtr->actual_strength == 0)
252 {
253 if (FleetPtr->allied_state == DEAD_GUY)
254 {
255 // Race is extinct.
256 UnlockFleetInfo (&GLOBAL (avail_race_q), hFleet);
257 return 0;
258 }
259 }
260 else if (FleetPtr->known_strength == 0
261 && FleetPtr->actual_strength != INFINITE_RADIUS)
262 {
263 FleetPtr->known_strength = 1;
264 FleetPtr->known_loc = FleetPtr->loc;
265 }
266
267 UnlockFleetInfo (&GLOBAL (avail_race_q), hFleet);
268 return race;
269 }
270
271 /*
272 * Returns the number of ships of the specified race among the
273 * escort ships.
274 */
275 COUNT
CountEscortShips(COUNT race)276 CountEscortShips (COUNT race)
277 {
278 HFLEETINFO hFleet;
279 HSHIPFRAG hStarShip, hNextShip;
280 COUNT result = 0;
281
282 hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), race);
283 if (!hFleet)
284 return 0;
285
286 for (hStarShip = GetHeadLink (&GLOBAL (built_ship_q)); hStarShip;
287 hStarShip = hNextShip)
288 {
289 BYTE ship_type;
290 SHIP_FRAGMENT *StarShipPtr;
291
292 StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
293 hNextShip = _GetSuccLink (StarShipPtr);
294 ship_type = StarShipPtr->race_id;
295 UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
296
297 if (ship_type == race)
298 result++;
299 }
300 return result;
301 }
302
303 /*
304 * Returns true if and only if a ship of the specified race is among the
305 * escort ships.
306 */
307 BOOLEAN
HaveEscortShip(COUNT race)308 HaveEscortShip (COUNT race)
309 {
310 return (CountEscortShips (race) > 0);
311 }
312
313 /*
314 * Test if the SIS can have an escort of the specified race.
315 * Returns 0 if 'race' is not available.
316 * Otherwise, returns the number of ships that can be added.
317 */
318 COUNT
EscortFeasibilityStudy(COUNT race)319 EscortFeasibilityStudy (COUNT race)
320 {
321 HFLEETINFO hFleet;
322
323 hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), race);
324 if (!hFleet)
325 return 0;
326
327 return (MAX_BUILT_SHIPS - CountLinks (&GLOBAL (built_ship_q)));
328 }
329
330 /*
331 * Test the alliance status of the specified race.
332 * Either DEAD_GUY (extinct), GOOD_GUY (allied), or BAD_GUY (not allied) is
333 * returned.
334 */
335 COUNT
CheckAlliance(COUNT race)336 CheckAlliance (COUNT race)
337 {
338 HFLEETINFO hFleet;
339 UWORD flags;
340 FLEET_INFO *FleetPtr;
341
342 hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), race);
343 if (!hFleet)
344 return 0;
345
346 FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hFleet);
347 flags = FleetPtr->allied_state;
348 UnlockFleetInfo (&GLOBAL (avail_race_q), hFleet);
349
350 return flags;
351 }
352
353 /*
354 * Remove a number of escort ships of the specified race (if present).
355 * Returns the number of escort ships removed.
356 */
357 COUNT
RemoveSomeEscortShips(COUNT race,COUNT count)358 RemoveSomeEscortShips (COUNT race, COUNT count)
359 {
360 HSHIPFRAG hStarShip;
361 HSHIPFRAG hNextShip;
362
363 if (count == 0)
364 return 0;
365
366 for (hStarShip = GetHeadLink (&GLOBAL (built_ship_q)); hStarShip;
367 hStarShip = hNextShip)
368 {
369 BOOLEAN RemoveShip;
370 SHIP_FRAGMENT *StarShipPtr;
371
372 StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
373 hNextShip = _GetSuccLink (StarShipPtr);
374 RemoveShip = (StarShipPtr->race_id == race);
375 UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
376
377 if (RemoveShip)
378 {
379 RemoveQueue (&GLOBAL (built_ship_q), hStarShip);
380 FreeShipFrag (&GLOBAL (built_ship_q), hStarShip);
381 count--;
382 if (count == 0)
383 break;
384 }
385 }
386
387 if (count > 0)
388 {
389 // Update the display.
390 DeltaSISGauges (UNDEFINED_DELTA, UNDEFINED_DELTA, UNDEFINED_DELTA);
391 }
392
393 return count;
394 }
395
396 /*
397 * Remove all escort ships of the specified race.
398 */
399 void
RemoveEscortShips(COUNT race)400 RemoveEscortShips (COUNT race)
401 {
402 RemoveSomeEscortShips (race, (COUNT) -1);
403 }
404
405 COUNT
GetIndexFromStarShip(QUEUE * pShipQ,HLINK hStarShip)406 GetIndexFromStarShip (QUEUE *pShipQ, HLINK hStarShip)
407 {
408 COUNT Index;
409
410 Index = 0;
411 while (hStarShip != GetHeadLink (pShipQ))
412 {
413 HLINK hNextShip;
414 LINK *StarShipPtr;
415
416 StarShipPtr = LockLink (pShipQ, hStarShip);
417 hNextShip = _GetPredLink (StarShipPtr);
418 UnlockLink (pShipQ, hStarShip);
419
420 hStarShip = hNextShip;
421 ++Index;
422 }
423
424 return Index;
425 }
426
427 BYTE
NameCaptain(QUEUE * pQueue,SPECIES_ID SpeciesID)428 NameCaptain (QUEUE *pQueue, SPECIES_ID SpeciesID)
429 {
430 BYTE name_index;
431 HLINK hStarShip;
432
433 assert (GetLinkSize (pQueue) == sizeof (STARSHIP) ||
434 GetLinkSize (pQueue) == sizeof (SHIP_FRAGMENT));
435
436 do
437 {
438 HLINK hNextShip;
439
440 name_index = PickCaptainName ();
441 for (hStarShip = GetHeadLink (pQueue); hStarShip;
442 hStarShip = hNextShip)
443 {
444 SHIP_BASE *ShipPtr;
445 BYTE test_name_index = -1;
446
447 ShipPtr = (SHIP_BASE *) LockLink (pQueue, hStarShip);
448 hNextShip = _GetSuccLink (ShipPtr);
449 if (ShipPtr->SpeciesID == SpeciesID)
450 test_name_index = ShipPtr->captains_name_index;
451 UnlockLink (pQueue, hStarShip);
452
453 if (name_index == test_name_index)
454 break;
455 }
456 } while (hStarShip /* name matched another ship */);
457
458 return name_index;
459 }
460
461 // crew_level can be set to INFINITE_FLEET for a ship which is to
462 // represent an infinite number of ships.
463 HSHIPFRAG
CloneShipFragment(COUNT shipIndex,QUEUE * pDstQueue,COUNT crew_level)464 CloneShipFragment (COUNT shipIndex, QUEUE *pDstQueue, COUNT crew_level)
465 {
466 HFLEETINFO hFleet;
467 HSHIPFRAG hBuiltShip;
468 FLEET_INFO *TemplatePtr;
469 BYTE captains_name_index;
470
471 assert (GetLinkSize (pDstQueue) == sizeof (SHIP_FRAGMENT));
472
473 hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), shipIndex);
474 if (!hFleet)
475 return 0;
476
477 TemplatePtr = LockFleetInfo (&GLOBAL (avail_race_q), hFleet);
478 if (shipIndex == SAMATRA_SHIP)
479 captains_name_index = 0;
480 else
481 captains_name_index = NameCaptain (pDstQueue,
482 TemplatePtr->SpeciesID);
483 hBuiltShip = Build (pDstQueue, TemplatePtr->SpeciesID);
484 if (hBuiltShip)
485 {
486 SHIP_FRAGMENT *ShipFragPtr;
487
488 ShipFragPtr = LockShipFrag (pDstQueue, hBuiltShip);
489 ShipFragPtr->captains_name_index = captains_name_index;
490 ShipFragPtr->race_strings = TemplatePtr->race_strings;
491 ShipFragPtr->icons = TemplatePtr->icons;
492 ShipFragPtr->melee_icon = TemplatePtr->melee_icon;
493 if (crew_level)
494 ShipFragPtr->crew_level = crew_level;
495 else
496 ShipFragPtr->crew_level = TemplatePtr->crew_level;
497 ShipFragPtr->max_crew = TemplatePtr->max_crew;
498 ShipFragPtr->energy_level = 0;
499 ShipFragPtr->max_energy = TemplatePtr->max_energy;
500 ShipFragPtr->race_id = (BYTE)shipIndex;
501 ShipFragPtr->index = 0;
502 UnlockShipFrag (pDstQueue, hBuiltShip);
503 }
504 UnlockFleetInfo (&GLOBAL (avail_race_q), hFleet);
505
506 return hBuiltShip;
507 }
508
509 /* Set the crew and captain's name on the first fully-crewed escort
510 * ship of race 'which_ship' */
511 int
SetEscortCrewComplement(COUNT which_ship,COUNT crew_level,BYTE captain)512 SetEscortCrewComplement (COUNT which_ship, COUNT crew_level, BYTE captain)
513 {
514 HFLEETINFO hFleet;
515 FLEET_INFO *TemplatePtr;
516 HSHIPFRAG hStarShip, hNextShip;
517 SHIP_FRAGMENT *StarShipPtr = 0;
518 int Index;
519
520 hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), which_ship);
521 if (!hFleet)
522 return -1;
523 TemplatePtr = LockFleetInfo (&GLOBAL (avail_race_q), hFleet);
524
525 /* Find first ship of which_ship race */
526 for (hStarShip = GetHeadLink (&GLOBAL (built_ship_q)), Index = 0;
527 hStarShip; hStarShip = hNextShip, ++Index)
528 {
529 StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
530 hNextShip = _GetSuccLink (StarShipPtr);
531 if (which_ship == StarShipPtr->race_id &&
532 StarShipPtr->crew_level == TemplatePtr->crew_level)
533 break; /* found one */
534 UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
535 }
536 if (hStarShip)
537 {
538 StarShipPtr->crew_level = crew_level;
539 StarShipPtr->captains_name_index = captain;
540 UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
541 }
542 else
543 Index = -1;
544
545 UnlockFleetInfo (&GLOBAL (avail_race_q), hFleet);
546 return Index;
547 }
548