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