1 /*
2  *  This program is free software; you can redistribute it and/or modify
3  *  it under the terms of the GNU General Public License as published by
4  *  the Free Software Foundation; either version 2 of the License, or
5  *  (at your option) any later version.
6  *
7  *  This program is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *  GNU General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program; if not, write to the Free Software
14  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15  */
16 
17 #include "genall.h"
18 #include "../planets.h"
19 #include "../lander.h"
20 #include "../../encount.h"
21 #include "../../gamestr.h"
22 #include "../../globdata.h"
23 #include "../../grpinfo.h"
24 #include "../../races.h"
25 #include "../../state.h"
26 #include "../../sounds.h"
27 #include "libs/mathlib.h"
28 
29 
30 static void GeneratePlanets (SOLARSYS_STATE *system);
31 static void check_yehat_rebellion (void);
32 
33 
34 const GenerateFunctions generateDefaultFunctions = {
35 	/* .initNpcs         = */ GenerateDefault_initNpcs,
36 	/* .reinitNpcs       = */ GenerateDefault_reinitNpcs,
37 	/* .uninitNpcs       = */ GenerateDefault_uninitNpcs,
38 	/* .generatePlanets  = */ GenerateDefault_generatePlanets,
39 	/* .generateMoons    = */ GenerateDefault_generateMoons,
40 	/* .generateName     = */ GenerateDefault_generateName,
41 	/* .generateOrbital  = */ GenerateDefault_generateOrbital,
42 	/* .generateMinerals = */ GenerateDefault_generateMinerals,
43 	/* .generateEnergy   = */ GenerateDefault_generateEnergy,
44 	/* .generateLife     = */ GenerateDefault_generateLife,
45 	/* .pickupMinerals   = */ GenerateDefault_pickupMinerals,
46 	/* .pickupEnergy     = */ GenerateDefault_pickupEnergy,
47 	/* .pickupLife       = */ GenerateDefault_pickupLife,
48 };
49 
50 
51 bool
GenerateDefault_initNpcs(SOLARSYS_STATE * solarSys)52 GenerateDefault_initNpcs (SOLARSYS_STATE *solarSys)
53 {
54 	if (!GetGroupInfo (GLOBAL (BattleGroupRef), GROUP_INIT_IP))
55 	{
56 		GLOBAL (BattleGroupRef) = 0;
57 		BuildGroups ();
58 	}
59 
60 	(void) solarSys;
61 	return true;
62 }
63 
64 bool
GenerateDefault_reinitNpcs(SOLARSYS_STATE * solarSys)65 GenerateDefault_reinitNpcs (SOLARSYS_STATE *solarSys)
66 {
67 	GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
68 	// This is not a great place to do the Yehat rebellion check, but
69 	// since you can start the rebellion in any star system (not just
70 	// the Homeworld), I could not find a better place for it.
71 	// At least it is better than where it was originally.
72 	check_yehat_rebellion ();
73 
74 	(void) solarSys;
75 	return true;
76 }
77 
78 bool
GenerateDefault_uninitNpcs(SOLARSYS_STATE * solarSys)79 GenerateDefault_uninitNpcs (SOLARSYS_STATE *solarSys)
80 {
81 	PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
82 	ReinitQueue (&GLOBAL (npc_built_ship_q));
83 	ReinitQueue (&GLOBAL (ip_group_q));
84 
85 	(void) solarSys;
86 	return true;
87 }
88 
89 bool
GenerateDefault_generatePlanets(SOLARSYS_STATE * solarSys)90 GenerateDefault_generatePlanets (SOLARSYS_STATE *solarSys)
91 {
92 	FillOrbits (solarSys, (BYTE)~0, solarSys->PlanetDesc, FALSE);
93 	GeneratePlanets (solarSys);
94 	return true;
95 }
96 
97 bool
GenerateDefault_generateMoons(SOLARSYS_STATE * solarSys,PLANET_DESC * planet)98 GenerateDefault_generateMoons (SOLARSYS_STATE *solarSys, PLANET_DESC *planet)
99 {
100 	FillOrbits (solarSys, planet->NumPlanets, solarSys->MoonDesc, FALSE);
101 	return true;
102 }
103 
104 bool
GenerateDefault_generateName(const SOLARSYS_STATE * solarSys,const PLANET_DESC * world)105 GenerateDefault_generateName (const SOLARSYS_STATE *solarSys,
106 		const PLANET_DESC *world)
107 {
108 	COUNT i = planetIndex (solarSys, world);
109 	utf8StringCopy (GLOBAL_SIS (PlanetName), sizeof (GLOBAL_SIS (PlanetName)),
110 			GAME_STRING (PLANET_NUMBER_BASE + (9 + 7) + i));
111 	SET_GAME_STATE (BATTLE_PLANET, world->data_index);
112 
113 	return true;
114 }
115 
116 bool
GenerateDefault_generateOrbital(SOLARSYS_STATE * solarSys,PLANET_DESC * world)117 GenerateDefault_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
118 {
119 	DWORD rand_val;
120 	SYSTEM_INFO *sysInfo;
121 
122 #ifdef DEBUG_SOLARSYS
123 	if (worldIsPlanet (solarSys, world))
124 	{
125 		log_add (log_Debug, "Planet index = %d",
126 				planetIndex (solarSys, world));
127 	}
128 	else
129 	{
130 		log_add (log_Debug, "Planet index = %d, Moon index = %d",
131 				planetIndex (solarSys, world),
132 				moonIndex (solarSys, world));
133 	}
134 #endif /* DEBUG_SOLARSYS */
135 
136 	sysInfo = &solarSys->SysInfo;
137 
138 	DoPlanetaryAnalysis (sysInfo, world);
139 	rand_val = RandomContext_GetSeed (SysGenRNG);
140 
141 	sysInfo->PlanetInfo.ScanSeed[BIOLOGICAL_SCAN] = rand_val;
142 	GenerateLifeForms (sysInfo, GENERATE_ALL, NULL);
143 	rand_val = RandomContext_GetSeed (SysGenRNG);
144 
145 	sysInfo->PlanetInfo.ScanSeed[MINERAL_SCAN] = rand_val;
146 	GenerateMineralDeposits (sysInfo, GENERATE_ALL, NULL);
147 
148 	sysInfo->PlanetInfo.ScanSeed[ENERGY_SCAN] = rand_val;
149 	LoadPlanet (NULL);
150 
151 	return true;
152 }
153 
154 COUNT
GenerateDefault_generateMinerals(const SOLARSYS_STATE * solarSys,const PLANET_DESC * world,COUNT whichNode,NODE_INFO * info)155 GenerateDefault_generateMinerals (const SOLARSYS_STATE *solarSys,
156 		const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
157 {
158 	return GenerateMineralDeposits (&solarSys->SysInfo, whichNode, info);
159 	(void) world;
160 }
161 
162 bool
GenerateDefault_pickupMinerals(SOLARSYS_STATE * solarSys,PLANET_DESC * world,COUNT whichNode)163 GenerateDefault_pickupMinerals (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
164 		COUNT whichNode)
165 {
166 	// Minerals do not need any extra handling as of now
167 	(void) solarSys;
168 	(void) world;
169 	(void) whichNode;
170 	return true;
171 }
172 
173 COUNT
GenerateDefault_generateEnergy(const SOLARSYS_STATE * solarSys,const PLANET_DESC * world,COUNT whichNode,NODE_INFO * info)174 GenerateDefault_generateEnergy (const SOLARSYS_STATE *solarSys,
175 		const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
176 {
177 	(void) whichNode;
178 	(void) solarSys;
179 	(void) world;
180 	(void) info;
181 	return 0;
182 }
183 
184 bool
GenerateDefault_pickupEnergy(SOLARSYS_STATE * solarSys,PLANET_DESC * world,COUNT whichNode)185 GenerateDefault_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
186 		COUNT whichNode)
187 {
188 	// This should never be called since every energy node needs
189 	// special handling and the function should be overridden
190 	assert (false);
191 	(void) solarSys;
192 	(void) world;
193 	(void) whichNode;
194 	return false;
195 }
196 
197 COUNT
GenerateDefault_generateLife(const SOLARSYS_STATE * solarSys,const PLANET_DESC * world,COUNT whichNode,NODE_INFO * info)198 GenerateDefault_generateLife (const SOLARSYS_STATE *solarSys,
199 		const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
200 {
201 	return GenerateLifeForms (&solarSys->SysInfo, whichNode, info);
202 	(void) world;
203 }
204 
205 bool
GenerateDefault_pickupLife(SOLARSYS_STATE * solarSys,PLANET_DESC * world,COUNT whichNode)206 GenerateDefault_pickupLife (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
207 		COUNT whichNode)
208 {
209 	// Bio does not need any extra handling as of now
210 	(void) solarSys;
211 	(void) world;
212 	(void) whichNode;
213 	return true;
214 }
215 
216 COUNT
GenerateDefault_generateArtifact(const SOLARSYS_STATE * solarSys,COUNT whichNode,NODE_INFO * info)217 GenerateDefault_generateArtifact (const SOLARSYS_STATE *solarSys,
218 		COUNT whichNode, NODE_INFO *info)
219 {
220 	// Generate an energy node at a random location
221 	return GenerateRandomNodes (&solarSys->SysInfo, ENERGY_SCAN, 1, 0,
222 			whichNode, info);
223 }
224 
225 COUNT
GenerateDefault_generateRuins(const SOLARSYS_STATE * solarSys,COUNT whichNode,NODE_INFO * info)226 GenerateDefault_generateRuins (const SOLARSYS_STATE *solarSys,
227 		COUNT whichNode, NODE_INFO *info)
228 {
229 	// Generate a standard spread of city ruins of a destroyed civilization
230 	return GenerateRandomNodes (&solarSys->SysInfo, ENERGY_SCAN, NUM_RACE_RUINS,
231 			0, whichNode, info);
232 }
233 
234 static inline void
runLanderReport(void)235 runLanderReport (void)
236 {
237 	UnbatchGraphics ();
238 	DoDiscoveryReport (MenuSounds);
239 	BatchGraphics ();
240 }
241 
242 bool
GenerateDefault_landerReport(SOLARSYS_STATE * solarSys)243 GenerateDefault_landerReport (SOLARSYS_STATE *solarSys)
244 {
245 	PLANET_INFO *planetInfo = &solarSys->SysInfo.PlanetInfo;
246 
247 	if (!planetInfo->DiscoveryString)
248 		return false;
249 
250 	runLanderReport ();
251 
252 	// XXX: A non-cycling report is given only once and has to be deleted
253 	//   in some circumstances (like the Syreen Vault). It does not
254 	//   hurt to simply delete it in all cases. Nothing should rely on
255 	//   the presence of DiscoveryString, but the Syreen Vault and the
256 	//   Mycon Egg Cases rely on its absence.
257 	DestroyStringTable (ReleaseStringTable (planetInfo->DiscoveryString));
258 	planetInfo->DiscoveryString = 0;
259 
260 	return true;
261 }
262 
263 bool
GenerateDefault_landerReportCycle(SOLARSYS_STATE * solarSys)264 GenerateDefault_landerReportCycle (SOLARSYS_STATE *solarSys)
265 {
266 	PLANET_INFO *planetInfo = &solarSys->SysInfo.PlanetInfo;
267 
268 	if (!planetInfo->DiscoveryString)
269 		return false;
270 
271 	runLanderReport ();
272 	// Advance to the next report
273 	planetInfo->DiscoveryString = SetRelStringTableIndex (
274 			planetInfo->DiscoveryString, 1);
275 
276 	// If our discovery strings have cycled, we're done
277 	if (GetStringTableIndex (planetInfo->DiscoveryString) == 0)
278 	{
279 		DestroyStringTable (ReleaseStringTable (planetInfo->DiscoveryString));
280 		planetInfo->DiscoveryString = 0;
281 	}
282 
283 	return true;
284 }
285 
286 // NB. This function modifies the RNG state.
287 static void
GeneratePlanets(SOLARSYS_STATE * solarSys)288 GeneratePlanets (SOLARSYS_STATE *solarSys)
289 {
290 	COUNT i;
291 	PLANET_DESC *planet;
292 
293 	for (i = solarSys->SunDesc[0].NumPlanets,
294 			planet = &solarSys->PlanetDesc[0]; i; --i, ++planet)
295 	{
296 		DWORD rand_val;
297 		BYTE byte_val;
298 		BYTE num_moons;
299 		BYTE type;
300 
301 		rand_val = RandomContext_Random (SysGenRNG);
302 		byte_val = LOBYTE (rand_val);
303 
304 		num_moons = 0;
305 		type = PlanData[planet->data_index & ~PLANET_SHIELDED].Type;
306 		switch (PLANSIZE (type))
307 		{
308 			case LARGE_ROCKY_WORLD:
309 				if (byte_val < 0x00FF * 25 / 100)
310 				{
311 					if (byte_val < 0x00FF * 5 / 100)
312 						++num_moons;
313 					++num_moons;
314 				}
315 				break;
316 			case GAS_GIANT:
317 				if (byte_val < 0x00FF * 90 / 100)
318 				{
319 					if (byte_val < 0x00FF * 75 / 100)
320 					{
321 						if (byte_val < 0x00FF * 50 / 100)
322 						{
323 							if (byte_val < 0x00FF * 25 / 100)
324 								++num_moons;
325 							++num_moons;
326 						}
327 						++num_moons;
328 					}
329 					++num_moons;
330 				}
331 				break;
332 		}
333 		planet->NumPlanets = num_moons;
334 	}
335 }
336 
337 static void
check_yehat_rebellion(void)338 check_yehat_rebellion (void)
339 {
340 	HIPGROUP hGroup, hNextGroup;
341 
342 	// XXX: Is there a better way to do this? I could not find one.
343 	//   When you talk to a Yehat ship (YEHAT_SHIP) and start the rebellion,
344 	//   there is no battle following the comm. There is *never* a battle in
345 	//   an encounter with Rebels, but the group race_id (YEHAT_REBEL_SHIP)
346 	//   is different from Royalists (YEHAT_SHIP). There is *always* a battle
347 	//   in an encounter with Royalists.
348 	// TRANSLATION: "If the civil war has not started yet, or the player
349 	//   battled a ship -- bail."
350 	if (!GET_GAME_STATE (YEHAT_CIVIL_WAR) || EncounterRace >= 0)
351 		return; // not this time
352 
353 	// Send Yehat groups to flee the system, but only if the player
354 	// has actually talked to a ship.
355 	for (hGroup = GetHeadLink (&GLOBAL (ip_group_q)); hGroup;
356 			hGroup = hNextGroup)
357 	{
358 		IP_GROUP *GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
359 		hNextGroup = _GetSuccLink (GroupPtr);
360 		// IGNORE_FLAGSHIP was set in ipdisp.c:ip_group_collision()
361 		// during a collision with the flagship.
362 		if (GroupPtr->race_id == YEHAT_SHIP
363 				&& (GroupPtr->task & IGNORE_FLAGSHIP))
364 		{
365 			GroupPtr->task &= REFORM_GROUP;
366 			GroupPtr->task |= FLEE | IGNORE_FLAGSHIP;
367 			GroupPtr->dest_loc = 0;
368 		}
369 		UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
370 	}
371 }
372 
373 
374