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