1 /*
2 This file is part of Warzone 2100.
3 Copyright (C) 1999-2004 Eidos Interactive
4 Copyright (C) 2005-2020 Warzone 2100 Project
5
6 Warzone 2100 is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 Warzone 2100 is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Warzone 2100; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 /**
21 * @file power.c
22 *
23 * Store PlayerPower and other power related stuff!
24 *
25 */
26 #include <string.h>
27 #include "objectdef.h"
28 #include "power.h"
29 #include "lib/gamelib/gtime.h"
30 #include "lib/sound/audio.h"
31 #include "objmem.h"
32 #include "frontend.h"
33 #include "lib/netplay/netplay.h"
34 #include "multiplay.h"
35 #include "multiint.h"
36 #include "feature.h"
37 #include "structure.h"
38 #include "mission.h"
39 #include "intdisplay.h"
40
41 #define EXTRACT_POINTS 1
42 #define MAX_POWER 1000000
43
44 #define FP_ONE ((int64_t)1 << 32)
45
46 //flag used to check for power calculations to be done or not
47 bool powerCalculated;
48
49 /* Updates the current power based on the extracted power and a Power Generator*/
50 static void updateCurrentPower(STRUCTURE *psStruct, UDWORD player, int ticks);
51 static int64_t updateExtractedPower(STRUCTURE *psBuilding);
52
53 //returns the relevant list based on OffWorld or OnWorld
54 static STRUCTURE *powerStructList(int player);
55
56 struct PowerRequest
57 {
58 int64_t amount; ///< Amount of power being requested.
59 unsigned id; ///< Structure which is requesting power.
60 };
61
62 struct PlayerPower
63 {
64 // All fields are 32.32 fixed point.
65 int64_t currentPower; ///< The current amount of power available to the player.
66 std::vector<PowerRequest> powerQueue; ///< Requested power.
67 int powerModifier; ///< Percentage modifier on power from each derrick.
68 int64_t maxStorage; ///< Maximum storage of power, in total.
69 int64_t extractedPower; ///< Total amount of extracted power in this game.
70 int64_t wastedPower; ///< Total amount of wasted power in this game.
71 };
72
73 static PlayerPower asPower[MAX_PLAYERS];
74
setPowerModifier(int player,int modifier)75 void setPowerModifier(int player, int modifier)
76 {
77 asPower[player].powerModifier = modifier;
78 }
79
setPowerMaxStorage(int player,int max)80 void setPowerMaxStorage(int player, int max)
81 {
82 asPower[player].maxStorage = max * FP_ONE;
83 asPower[player].currentPower = std::min<int64_t>(asPower[player].maxStorage, asPower[player].currentPower);
84 }
85
86 /*allocate the space for the playerPower*/
allocPlayerPower()87 bool allocPlayerPower()
88 {
89 clearPlayerPower();
90 powerCalculated = true;
91 return true;
92 }
93
94 /*clear the playerPower */
clearPlayerPower()95 void clearPlayerPower()
96 {
97 for (unsigned player = 0; player < MAX_PLAYERS; player++)
98 {
99 asPower[player].currentPower = 0;
100 asPower[player].extractedPower = 0;
101 asPower[player].wastedPower = 0;
102 asPower[player].powerModifier = 100;
103 asPower[player].powerQueue.clear();
104 asPower[player].maxStorage = MAX_POWER * FP_ONE;
105 }
106 }
107
108 /// Returns true iff the power is available. New requests replace old ones (without losing the position in the queue).
addPowerRequest(unsigned player,unsigned id,int64_t amount)109 static bool addPowerRequest(unsigned player, unsigned id, int64_t amount)
110 {
111 PlayerPower *p = &asPower[player];
112
113 int64_t requiredPower = amount;
114 size_t n;
115 for (n = 0; n < p->powerQueue.size() && p->powerQueue[n].id != id; ++n)
116 {
117 requiredPower += p->powerQueue[n].amount;
118 }
119 if (n == p->powerQueue.size())
120 {
121 p->powerQueue.resize(n + 1);
122 p->powerQueue[n].id = id;
123 }
124 p->powerQueue[n].amount = amount;
125 return requiredPower <= p->currentPower;
126 }
127
delPowerRequest(STRUCTURE * psStruct)128 void delPowerRequest(STRUCTURE *psStruct)
129 {
130 ASSERT_NOT_NULLPTR_OR_RETURN(, psStruct);
131 PlayerPower *p = &asPower[psStruct->player];
132
133 for (size_t n = 0; n < p->powerQueue.size(); ++n)
134 {
135 if (p->powerQueue[n].id == psStruct->id)
136 {
137 p->powerQueue.erase(p->powerQueue.begin() + n);
138 return;
139 }
140 }
141 }
142
checkPrecisePowerRequest(STRUCTURE * psStruct)143 static int64_t checkPrecisePowerRequest(STRUCTURE *psStruct)
144 {
145 ASSERT_NOT_NULLPTR_OR_RETURN(-1, psStruct);
146 PlayerPower const *p = &asPower[psStruct->player];
147
148 int64_t requiredPower = 0;
149 for (size_t n = 0; n < p->powerQueue.size(); ++n)
150 {
151 requiredPower += p->powerQueue[n].amount;
152 if (p->powerQueue[n].id == psStruct->id)
153 {
154 if (requiredPower <= p->currentPower)
155 {
156 return -1; // Have enough power.
157 }
158 return requiredPower - p->currentPower;
159 }
160 }
161
162 return -1;
163 }
164
checkPowerRequest(STRUCTURE * psStruct)165 int32_t checkPowerRequest(STRUCTURE *psStruct)
166 {
167 int64_t power = checkPrecisePowerRequest(psStruct);
168 return power != -1 ? power / FP_ONE : -1;
169 }
170
getPreciseQueuedPower(unsigned player)171 static int64_t getPreciseQueuedPower(unsigned player)
172 {
173 PlayerPower const *p = &asPower[player];
174
175 int64_t requiredPower = 0;
176 for (size_t n = 0; n < p->powerQueue.size(); ++n)
177 {
178 requiredPower += p->powerQueue[n].amount;
179 }
180 return requiredPower;
181 }
182
getQueuedPower(int player)183 int getQueuedPower(int player)
184 {
185 PlayerPower const *p = &asPower[player];
186
187 int64_t requiredPower = 0;
188 for (size_t n = 0; n < p->powerQueue.size(); ++n)
189 {
190 requiredPower += p->powerQueue[n].amount;
191 }
192 return requiredPower / FP_ONE;
193 }
194
syncDebugEconomy(unsigned player,char ch)195 static void syncDebugEconomy(unsigned player, char ch)
196 {
197 ASSERT_OR_RETURN(, player < MAX_PLAYERS, "Bad player (%d)", player);
198
199 syncDebug("%c economy%u = %" PRId64"", ch, player, asPower[player].currentPower);
200 }
201
usePower(int player,uint32_t quantity)202 void usePower(int player, uint32_t quantity)
203 {
204 ASSERT_OR_RETURN(, player < MAX_PLAYERS, "Invalid player (%d)", player);
205 syncDebug("usePower%d %" PRId64"-=%u", player, asPower[player].currentPower, quantity);
206 asPower[player].currentPower = MAX(0, asPower[player].currentPower - quantity * FP_ONE);
207 }
208
addPower(int player,int32_t quantity)209 void addPower(int player, int32_t quantity)
210 {
211 ASSERT_OR_RETURN(, player < MAX_PLAYERS, "Bad player (%d)", player);
212 syncDebug("addPower%d %" PRId64"+=%d", player, asPower[player].currentPower, quantity);
213 asPower[player].currentPower += quantity * FP_ONE;
214 if (asPower[player].currentPower > asPower[player].maxStorage)
215 {
216 asPower[player].wastedPower += asPower[player].currentPower - asPower[player].maxStorage;
217 asPower[player].currentPower = asPower[player].maxStorage;
218 }
219 }
220
221 /*resets the power calc flag for all players*/
powerCalc(bool on)222 void powerCalc(bool on)
223 {
224 powerCalculated = on;
225 }
226
227 /** Each Resource Extractor yields EXTRACT_POINTS per second FOREVER */
updateExtractedPower(STRUCTURE * psBuilding)228 static int64_t updateExtractedPower(STRUCTURE *psBuilding)
229 {
230 RES_EXTRACTOR *pResExtractor;
231 int64_t extractedPoints;
232
233 pResExtractor = (RES_EXTRACTOR *) psBuilding->pFunctionality;
234 extractedPoints = 0;
235
236 //only extracts points whilst its active ie associated with a power gen
237 //and has got some power to extract
238 if (pResExtractor->psPowerGen != nullptr)
239 {
240 // include modifier as a %
241 extractedPoints = asPower[psBuilding->player].powerModifier * EXTRACT_POINTS * FP_ONE / (100 * GAME_UPDATES_PER_SEC);
242 syncDebug("updateExtractedPower%d = %" PRId64"", psBuilding->player, extractedPoints);
243 }
244 ASSERT(extractedPoints >= 0, "extracted negative amount of power");
245 return extractedPoints;
246 }
247
248 //returns the relevant list based on OffWorld or OnWorld
powerStructList(int player)249 STRUCTURE *powerStructList(int player)
250 {
251 ASSERT_OR_RETURN(nullptr, player < MAX_PLAYERS, "Invalid player %d", player);
252 if (offWorldKeepLists)
253 {
254 return (mission.apsStructLists[player]);
255 }
256 else
257 {
258 return (apsStructLists[player]);
259 }
260 }
261
262 /* Update current power based on what Power Generators exist */
updatePlayerPower(int player,int ticks)263 void updatePlayerPower(int player, int ticks)
264 {
265 STRUCTURE *psStruct;//, *psList;
266 int64_t powerBefore = asPower[player].currentPower;
267
268 ASSERT_OR_RETURN(, player < MAX_PLAYERS, "Invalid player %d", player);
269
270 syncDebugEconomy(player, '<');
271
272 for (psStruct = powerStructList(player); psStruct != nullptr; psStruct = psStruct->psNext)
273 {
274 if (psStruct->pStructureType->type == REF_POWER_GEN && psStruct->status == SS_BUILT)
275 {
276 updateCurrentPower(psStruct, player, ticks);
277 }
278 }
279 syncDebug("updatePlayerPower%u %" PRId64"->%" PRId64"", player, powerBefore, asPower[player].currentPower);
280
281 syncDebugEconomy(player, '<');
282 }
283
284 /* Updates the current power based on the extracted power and a Power Generator*/
updateCurrentPower(STRUCTURE * psStruct,UDWORD player,int ticks)285 static void updateCurrentPower(STRUCTURE *psStruct, UDWORD player, int ticks)
286 {
287 POWER_GEN *psPowerGen = (POWER_GEN *)psStruct->pFunctionality;
288
289 ASSERT_OR_RETURN(, player < MAX_PLAYERS, "Invalid player %u", player);
290
291 //each power gen can cope with its associated resource extractors
292 int64_t extractedPower = 0;
293 for (int i = 0; i < NUM_POWER_MODULES; ++i)
294 {
295 auto &extractor = psPowerGen->apResExtractors[i];
296 if (extractor && extractor->died) {
297 syncDebugStructure(extractor, '-');
298 extractor = nullptr; // Clear pointer.
299 }
300 if (extractor)
301 {
302 extractedPower += updateExtractedPower(extractor);
303 }
304 }
305
306 int multiplier = getBuildingPowerPoints(psStruct);
307 syncDebug("updateCurrentPower%d = %" PRId64",%u", player, extractedPower, multiplier);
308
309 asPower[player].currentPower += (extractedPower * multiplier) / 100 * ticks;
310 asPower[player].extractedPower += (extractedPower * multiplier) / 100 * ticks;
311 ASSERT(asPower[player].currentPower >= 0, "negative power");
312 if (asPower[player].currentPower > asPower[player].maxStorage)
313 {
314 asPower[player].wastedPower += asPower[player].currentPower - asPower[player].maxStorage;
315 asPower[player].currentPower = asPower[player].maxStorage;
316 }
317 }
318
setPower(unsigned player,int32_t power)319 void setPower(unsigned player, int32_t power)
320 {
321 ASSERT_OR_RETURN(, player < MAX_PLAYERS, "Invalid player (%u)", player);
322
323 syncDebug("setPower%d %" PRId64"->%d", player, asPower[player].currentPower, power);
324 asPower[player].currentPower = power * FP_ONE;
325 ASSERT(asPower[player].currentPower >= 0, "negative power");
326 }
327
getPower(unsigned player)328 int32_t getPower(unsigned player)
329 {
330 ASSERT_OR_RETURN(0, player < MAX_PLAYERS, "Invalid player (%u)", player);
331
332 return asPower[player].currentPower / FP_ONE;
333 }
334
getPrecisePower(unsigned player)335 int64_t getPrecisePower(unsigned player)
336 {
337 ASSERT_OR_RETURN(0, player < MAX_PLAYERS, "Invalid player (%u)", player);
338
339 return asPower[player].currentPower;
340 }
341
getExtractedPower(unsigned player)342 int64_t getExtractedPower(unsigned player)
343 {
344 return asPower[player].extractedPower / FP_ONE;
345 }
346
getWastedPower(unsigned player)347 int64_t getWastedPower(unsigned player)
348 {
349 return asPower[player].wastedPower / FP_ONE;
350 }
351
getPowerMinusQueued(unsigned player)352 int32_t getPowerMinusQueued(unsigned player)
353 {
354 ASSERT_OR_RETURN(0, player < MAX_PLAYERS, "Invalid player (%u)", player);
355
356 return (asPower[player].currentPower - getPreciseQueuedPower(player)) / FP_ONE;
357 }
358
requestPowerFor(STRUCTURE * psStruct,int32_t amount)359 bool requestPowerFor(STRUCTURE *psStruct, int32_t amount)
360 {
361 return requestPrecisePowerFor(psStruct, amount * FP_ONE);
362 }
363
requestPrecisePowerFor(STRUCTURE * psStruct,int64_t amount)364 bool requestPrecisePowerFor(STRUCTURE *psStruct, int64_t amount)
365 {
366 if (amount <= 0 || !powerCalculated)
367 {
368 return true;
369 }
370
371 bool haveEnoughPower = addPowerRequest(psStruct->player, psStruct->id, amount);
372 if (haveEnoughPower)
373 {
374 // you can have it
375 asPower[psStruct->player].currentPower -= amount;
376 delPowerRequest(psStruct);
377 syncDebug("requestPrecisePowerFor%d,%u amount%" PRId64"", psStruct->player, psStruct->id, amount);
378 return true;
379 }
380 syncDebug("requestPrecisePowerFor%d,%u wait,amount%" PRId64"", psStruct->player, psStruct->id, amount);
381 return false; // Not enough power in the queue.
382 }
383