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 /** @file
21 * Definitions for the structures.
22 */
23
24 #ifndef __INCLUDED_SRC_STRUCTURE_H__
25 #define __INCLUDED_SRC_STRUCTURE_H__
26
27 #include "lib/framework/string_ext.h"
28 #include "lib/framework/wzconfig.h"
29
30 #include "objectdef.h"
31 #include "structuredef.h"
32 #include "visibility.h"
33 #include "baseobject.h"
34
35 // how long to wait between CALL_STRUCT_ATTACKED's - plus how long to flash on radar for
36 #define ATTACK_CB_PAUSE 5000
37
38 /// Extra z padding for assembly points
39 #define ASSEMBLY_POINT_Z_PADDING 10
40
41 #define STRUCTURE_DAMAGE_SCALING 400
42
43 //production loop max
44 #define INFINITE_PRODUCTION 9//10
45
46 /*This should correspond to the structLimits! */
47 #define MAX_FACTORY 5
48
49 //used to flag when the Factory is ready to start building
50 #define ACTION_START_TIME 0
51
52 extern std::vector<ProductionRun> asProductionRun[NUM_FACTORY_TYPES];
53
54 //Value is stored for easy access to this structure stat
55 extern UDWORD factoryModuleStat;
56 extern UDWORD powerModuleStat;
57 extern UDWORD researchModuleStat;
58
59 // the structure that was last hit
60 extern STRUCTURE *psLastStructHit;
61
62 //stores which player the production list has been set up for
63 extern SBYTE productionPlayer;
64
65 //holder for all StructureStats
66 extern STRUCTURE_STATS *asStructureStats;
67 extern UDWORD numStructureStats;
68
69 //used to hold the modifiers cross refd by weapon effect and structureStrength
70 extern STRUCTSTRENGTH_MODIFIER asStructStrengthModifier[WE_NUMEFFECTS][NUM_STRUCT_STRENGTH];
71
72 void handleAbandonedStructures();
73
74 int getMaxDroids(int player);
75 int getMaxCommanders(int player);
76 int getMaxConstructors(int player);
77 void setMaxDroids(int player, int value);
78 void setMaxCommanders(int player, int value);
79 void setMaxConstructors(int player, int value);
80
81 bool structureExists(int player, STRUCTURE_TYPE type, bool built, bool isMission);
82
83 bool IsPlayerDroidLimitReached(int player);
84
85 bool loadStructureStats(WzConfig &ini);
86 /*Load the Structure Strength Modifiers from the file exported from Access*/
87 bool loadStructureStrengthModifiers(WzConfig &ini);
88
89 bool structureStatsShutDown();
90
91 int requestOpenGate(STRUCTURE *psStructure);
92 int gateCurrentOpenHeight(const STRUCTURE *psStructure, uint32_t time, int minimumStub); ///< Returns how far open the gate is, or 0 if the structure is not a gate.
93
94 int32_t structureDamage(STRUCTURE *psStructure, unsigned damage, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, unsigned impactTime, bool isDamagePerSecond, int minDamage);
95 void structureBuild(STRUCTURE *psStructure, DROID *psDroid, int buildPoints, int buildRate = 1);
96 void structureDemolish(STRUCTURE *psStructure, DROID *psDroid, int buildPoints);
97 void structureRepair(STRUCTURE *psStruct, DROID *psDroid, int buildRate);
98 /* Set the type of droid for a factory to build */
99 bool structSetManufacture(STRUCTURE *psStruct, DROID_TEMPLATE *psTempl, QUEUE_MODE mode);
100 uint32_t structureBuildPointsToCompletion(const STRUCTURE & structure);
101 float structureCompletionProgress(const STRUCTURE & structure);
102
103 //builds a specified structure at a given location
104 STRUCTURE *buildStructure(STRUCTURE_STATS *pStructureType, UDWORD x, UDWORD y, UDWORD player, bool FromSave);
105 STRUCTURE *buildStructureDir(STRUCTURE_STATS *pStructureType, UDWORD x, UDWORD y, uint16_t direction, UDWORD player, bool FromSave);
106 /// Create a blueprint structure, with just enough information to render it
107 STRUCTURE *buildBlueprint(STRUCTURE_STATS const *psStats, Vector3i xy, uint16_t direction, unsigned moduleIndex, STRUCT_STATES state);
108 /* The main update routine for all Structures */
109 void structureUpdate(STRUCTURE *psBuilding, bool bMission);
110
111 /* Remove a structure and free it's memory */
112 bool destroyStruct(STRUCTURE *psDel, unsigned impactTime);
113
114 // remove a structure from a game without any visible effects
115 // bDestroy = true if the object is to be destroyed
116 // (for example used to change the type of wall at a location)
117 bool removeStruct(STRUCTURE *psDel, bool bDestroy);
118
119 //fills the list with Structures that can be built
120 std::vector<STRUCTURE_STATS *> fillStructureList(UDWORD selectedPlayer, UDWORD limit, bool showFavorites);
121
122 /// Checks if the two structures would be too close to build together.
123 bool isBlueprintTooClose(STRUCTURE_STATS const *stats1, Vector2i pos1, uint16_t dir1, STRUCTURE_STATS const *stats2, Vector2i pos2, uint16_t dir2);
124
125 /// Checks that the location is valid to build on.
126 /// pos in world coords
127 bool validLocation(BASE_STATS *psStats, Vector2i pos, uint16_t direction, unsigned player, bool bCheckBuildQueue);
128
129 bool isWall(STRUCTURE_TYPE type); ///< Structure is a wall. Not completely sure it handles all cases.
130 bool isBuildableOnWalls(STRUCTURE_TYPE type); ///< Structure can be built on walls. Not completely sure it handles all cases.
131
132 void alignStructure(STRUCTURE *psBuilding);
133
134 /* set the current number of structures of each type built */
135 void setCurrentStructQuantity(bool displayError);
136 /* get a stat inc based on the name */
137 int32_t getStructStatFromName(const WzString &name);
138 /*check to see if the structure is 'doing' anything - return true if idle*/
139 bool structureIdle(const STRUCTURE *psBuilding);
140 /*sets the point new droids go to - x/y in world coords for a Factory*/
141 void setAssemblyPoint(FLAG_POSITION *psAssemblyPoint, UDWORD x, UDWORD y, UDWORD player, bool bCheck);
142
143 /*initialises the flag before a new data set is loaded up*/
144 void initFactoryNumFlag();
145
146 //called at start of missions
147 void resetFactoryNumFlag();
148
149 /* get demolish stat */
150 STRUCTURE_STATS *structGetDemolishStat();
151
152 /*find a location near to the factory to start the droid of*/
153 bool placeDroid(STRUCTURE *psStructure, UDWORD *droidX, UDWORD *droidY);
154
155 //Set the factory secondary orders to a droid
156 void setFactorySecondaryState(DROID *psDroid, STRUCTURE *psStructure);
157
158 /* is this a lassat structure? */
isLasSat(STRUCTURE_STATS * pStructureType)159 static inline bool isLasSat(STRUCTURE_STATS *pStructureType)
160 {
161 ASSERT_OR_RETURN(false, pStructureType != nullptr, "LasSat is invalid?");
162
163 return (pStructureType->psWeapStat[0]
164 && pStructureType->psWeapStat[0]->weaponSubClass == WSC_LAS_SAT);
165 }
166
167 /*sets the flag to indicate a SatUplink Exists - so draw everything!*/
168 void setSatUplinkExists(bool state, UDWORD player);
169
170 /*returns the status of the flag*/
171 bool getSatUplinkExists(UDWORD player);
172
173 /*sets the flag to indicate a Las Sat Exists - ONLY EVER WANT ONE*/
174 void setLasSatExists(bool state, UDWORD player);
175
176 /* added int weapon_slot to fix the alway slot 0 hack */
177 bool calcStructureMuzzleLocation(const STRUCTURE *psStructure, Vector3i *muzzle, int weapon_slot);
178 bool calcStructureMuzzleBaseLocation(const STRUCTURE *psStructure, Vector3i *muzzle, int weapon_slot);
179
180 /*this is called whenever a structure has finished building*/
181 void buildingComplete(STRUCTURE *psBuilding);
182
183 // these functions are used in game.c inplace of building complete
184 void checkForResExtractors(STRUCTURE *psPowerGen);
185 void checkForPowerGen(STRUCTURE *psPowerGen);
186
187 uint16_t countPlayerUnusedDerricks();
188
189 // Set the command droid that factory production should go to struct _command_droid;
190 void assignFactoryCommandDroid(STRUCTURE *psStruct, struct DROID *psCommander);
191
192 // remove all factories from a command droid
193 void clearCommandDroidFactory(DROID *psDroid);
194
195 /*for a given structure, return a pointer to its module stat */
196 STRUCTURE_STATS *getModuleStat(const STRUCTURE *psStruct);
197
198 /*called when a Res extractor is destroyed or runs out of power or is disconnected
199 adjusts the owning Power Gen so that it can link to a different Res Extractor if one
200 is available*/
201 void releaseResExtractor(STRUCTURE *psRelease);
202
203 /*called when a Power Gen is destroyed or is disconnected
204 adjusts the associated Res Extractors so that they can link to different Power
205 Gens if any are available*/
206 void releasePowerGen(STRUCTURE *psRelease);
207
208 //print some info at the top of the screen dependent on the structure
209 void printStructureInfo(STRUCTURE *psStructure);
210
211 /*Checks the template type against the factory type - returns false
212 if not a good combination!*/
213 bool validTemplateForFactory(const DROID_TEMPLATE *psTemplate, STRUCTURE *psFactory, bool complain);
214
215 /*calculates the damage caused to the resistance levels of structures*/
216 bool electronicDamage(BASE_OBJECT *psTarget, UDWORD damage, UBYTE attackPlayer);
217
218 /* EW works differently in multiplayer mode compared with single player.*/
219 bool validStructResistance(const STRUCTURE *psStruct);
220
221 /*checks to see if a specific structure type exists -as opposed to a structure
222 stat type*/
223 bool checkSpecificStructExists(UDWORD structInc, UDWORD player);
224
225 int32_t getStructureDamage(const STRUCTURE *psStructure);
226
227 unsigned structureBodyBuilt(const STRUCTURE *psStruct); ///< Returns the maximum body points of a structure with the current number of build points.
228 UDWORD structureBody(const STRUCTURE *psStruct);
229 UDWORD structureResistance(const STRUCTURE_STATS *psStats, UBYTE player);
230
231 void hqReward(UBYTE losingPlayer, UBYTE rewardPlayer);
232
233 // Is a structure a factory of somekind?
234 bool StructIsFactory(const STRUCTURE *Struct);
235
236 // Is a flag a factory delivery point?
237 bool FlagIsFactory(const FLAG_POSITION *psCurrFlag);
238
239 // Find a factories corresonding delivery point.
240 FLAG_POSITION *FindFactoryDelivery(const STRUCTURE *Struct);
241
242 //Find the factory associated with the delivery point - returns NULL if none exist
243 STRUCTURE *findDeliveryFactory(FLAG_POSITION *psDelPoint);
244
245 /*this is called when a factory produces a droid. The Template returned is the next
246 one to build - if any*/
247 DROID_TEMPLATE *factoryProdUpdate(STRUCTURE *psStructure, DROID_TEMPLATE *psTemplate);
248
249 //increment the production run for this type
250 void factoryProdAdjust(STRUCTURE *psStructure, DROID_TEMPLATE *psTemplate, bool add);
251
252 //returns the quantity of a specific template in the production list
253 ProductionRunEntry getProduction(STRUCTURE *psStructure, DROID_TEMPLATE *psTemplate);
254
255 //looks through a players production list to see if a command droid is being built
256 UBYTE checkProductionForCommand(UBYTE player);
257
258 //check that delivery points haven't been put down in invalid location
259 void checkDeliveryPoints(UDWORD version);
260
261 //adjust the loop quantity for this factory
262 void factoryLoopAdjust(STRUCTURE *psStruct, bool add);
263
264 /*cancels the production run for the factory and returns any power that was
265 accrued but not used*/
266 void cancelProduction(STRUCTURE *psBuilding, QUEUE_MODE mode, bool mayClearProductionRun = true);
267
268 /*set a factory's production run to hold*/
269 void holdProduction(STRUCTURE *psBuilding, QUEUE_MODE mode);
270
271 /*release a factory's production run from hold*/
272 void releaseProduction(STRUCTURE *psBuilding, QUEUE_MODE mode);
273
274 /// Does the next item in the production list.
275 void doNextProduction(STRUCTURE *psStructure, DROID_TEMPLATE *current, QUEUE_MODE mode);
276
277 // Count number of factories assignable to a command droid.
278 UWORD countAssignableFactories(UBYTE player, UWORD FactoryType);
279
280 /*Used for determining how much of the structure to draw as being built or demolished*/
281 float structHeightScale(const STRUCTURE *psStruct);
282
283 /*compares the structure sensor type with the droid weapon type to see if the
284 FIRE_SUPPORT order can be assigned*/
285 bool structSensorDroidWeapon(const STRUCTURE *psStruct, const DROID *psDroid);
286
287 /*checks if the structure has a Counter Battery sensor attached - returns
288 true if it has*/
289 bool structCBSensor(const STRUCTURE *psStruct);
290 /*checks if the structure has a Standard Turret sensor attached - returns
291 true if it has*/
292 bool structStandardSensor(const STRUCTURE *psStruct);
293
294 /*checks if the structure has a VTOL Intercept sensor attached - returns
295 true if it has*/
296 bool structVTOLSensor(const STRUCTURE *psStruct);
297
298 /*checks if the structure has a VTOL Counter Battery sensor attached - returns
299 true if it has*/
300 bool structVTOLCBSensor(const STRUCTURE *psStruct);
301
302 // return the nearest rearm pad
303 // psTarget can be NULL
304 STRUCTURE *findNearestReArmPad(DROID *psDroid, STRUCTURE *psTarget, bool bClear);
305
306 // check whether a rearm pad is clear
307 bool clearRearmPad(const STRUCTURE *psStruct);
308
309 // clear a rearm pad for a vtol to land on it
310 void ensureRearmPadClear(STRUCTURE *psStruct, DROID *psDroid);
311
312 // return whether a rearm pad has a vtol on it
313 bool vtolOnRearmPad(STRUCTURE *psStruct, DROID *psDroid);
314
315 /* Just returns true if the structure's present body points aren't as high as the original*/
316 bool structIsDamaged(STRUCTURE *psStruct);
317
318 // give a structure from one player to another - used in Electronic Warfare
319 STRUCTURE *giftSingleStructure(STRUCTURE *psStructure, UBYTE attackPlayer, bool electronic_warfare = true);
320
321 /*Initialise the production list and set up the production player*/
322 void changeProductionPlayer(UBYTE player);
323
324 // La!
325 bool IsStatExpansionModule(const STRUCTURE_STATS *psStats);
326
327 /// is this a blueprint and not a real structure?
328 bool structureIsBlueprint(const STRUCTURE *psStructure);
329 bool isBlueprint(const BASE_OBJECT *psObject);
330
331 /*returns the power cost to build this structure, or to add its next module */
332 UDWORD structPowerToBuildOrAddNextModule(const STRUCTURE *psStruct);
333
334 // check whether a factory of a certain number and type exists
335 bool checkFactoryExists(UDWORD player, UDWORD factoryType, UDWORD inc);
336
337 /*checks the structure passed in is a Las Sat structure which is currently
338 selected - returns true if valid*/
339 bool lasSatStructSelected(const STRUCTURE *psStruct);
340
341 void cbNewDroid(STRUCTURE *psFactory, DROID *psDroid);
342
343 StructureBounds getStructureBounds(const STRUCTURE *object);
344 StructureBounds getStructureBounds(const STRUCTURE_STATS *stats, Vector2i pos, uint16_t direction);
345
346 bool canStructureHaveAModuleAdded(const STRUCTURE* const structure);
347
structSensorRange(const STRUCTURE * psObj)348 static inline int structSensorRange(const STRUCTURE *psObj)
349 {
350 return objSensorRange((const BASE_OBJECT *)psObj);
351 }
352
structJammerPower(const STRUCTURE * psObj)353 static inline int structJammerPower(const STRUCTURE *psObj)
354 {
355 return objJammerPower((const BASE_OBJECT *)psObj);
356 }
357
structureGetInterpolatedWeaponRotation(STRUCTURE * psStructure,int weaponSlot,uint32_t time)358 static inline Rotation structureGetInterpolatedWeaponRotation(STRUCTURE *psStructure, int weaponSlot, uint32_t time)
359 {
360 return interpolateRot(psStructure->asWeaps[weaponSlot].prevRot, psStructure->asWeaps[weaponSlot].rot, psStructure->prevTime, psStructure->time, time);
361 }
362
363 #define setStructureTarget(_psBuilding, _psNewTarget, _idx, _targetOrigin) _setStructureTarget(_psBuilding, _psNewTarget, _idx, _targetOrigin, __LINE__, __FUNCTION__)
_setStructureTarget(STRUCTURE * psBuilding,BASE_OBJECT * psNewTarget,UWORD idx,TARGET_ORIGIN targetOrigin,int line,const char * func)364 static inline void _setStructureTarget(STRUCTURE *psBuilding, BASE_OBJECT *psNewTarget, UWORD idx, TARGET_ORIGIN targetOrigin, int line, const char *func)
365 {
366 ASSERT_OR_RETURN(, idx < MAX_WEAPONS, "Bad index");
367 ASSERT_OR_RETURN(, psNewTarget == nullptr || !psNewTarget->died, "setStructureTarget set dead target");
368 psBuilding->psTarget[idx] = psNewTarget;
369 psBuilding->asWeaps[idx].origin = targetOrigin;
370 #ifdef DEBUG
371 psBuilding->targetLine[idx] = line;
372 sstrcpy(psBuilding->targetFunc[idx], func);
373 #else
374 // Prevent warnings about unused parameters
375 (void)line;
376 (void)func;
377 #endif
378 }
379
380 // Functions for the GUI to know what's pending, before it's synchronised.
381 template<typename Functionality, typename Subject>
setStatusPendingStart(Functionality & functionality,Subject * subject)382 static inline void setStatusPendingStart(Functionality &functionality, Subject *subject)
383 {
384 functionality.psSubjectPending = subject;
385 functionality.statusPending = FACTORY_START_PENDING;
386 ++functionality.pendingCount;
387 }
388
389 template<typename Functionality>
setStatusPendingCancel(Functionality & functionality)390 static inline void setStatusPendingCancel(Functionality &functionality)
391 {
392 functionality.psSubjectPending = nullptr;
393 functionality.statusPending = FACTORY_CANCEL_PENDING;
394 ++functionality.pendingCount;
395 }
396
397 template<typename Functionality>
setStatusPendingHold(Functionality & functionality)398 static inline void setStatusPendingHold(Functionality &functionality)
399 {
400 if (functionality.psSubjectPending == nullptr)
401 {
402 functionality.psSubjectPending = functionality.psSubject;
403 }
404 functionality.statusPending = FACTORY_HOLD_PENDING;
405 ++functionality.pendingCount;
406 }
407
408 template<typename Functionality>
setStatusPendingRelease(Functionality & functionality)409 static inline void setStatusPendingRelease(Functionality &functionality)
410 {
411 if (functionality.psSubjectPending == nullptr && functionality.statusPending != FACTORY_CANCEL_PENDING)
412 {
413 functionality.psSubjectPending = functionality.psSubject;
414 }
415 if (functionality.psSubjectPending != nullptr)
416 {
417 functionality.statusPending = FACTORY_START_PENDING;
418 }
419 ++functionality.pendingCount;
420 }
421
422 template<typename Functionality>
popStatusPending(Functionality & functionality)423 static inline void popStatusPending(Functionality &functionality)
424 {
425 if (functionality.pendingCount == 0)
426 {
427 ++functionality.pendingCount;
428 }
429 if (--functionality.pendingCount == 0)
430 {
431 // Subject is now synchronised, remove pending.
432 functionality.psSubjectPending = nullptr;
433 functionality.statusPending = FACTORY_NOTHING_PENDING;
434 }
435 }
436
437 void checkStructure(const STRUCTURE *psStructure, const char *const location_description, const char *function, const int recurse);
438
439 #define CHECK_STRUCTURE(object) checkStructure((object), AT_MACRO, __FUNCTION__, max_check_object_recursion)
440
441 void structureInitVars();
442 void initStructLimits();
443
444 #define syncDebugStructure(psStruct, ch) _syncDebugStructure(__FUNCTION__, psStruct, ch)
445 void _syncDebugStructure(const char *function, STRUCTURE const *psStruct, char ch);
446
447
448 // True iff object is a structure.
isStructure(SIMPLE_OBJECT const * psObject)449 static inline bool isStructure(SIMPLE_OBJECT const *psObject)
450 {
451 return psObject != nullptr && psObject->type == OBJ_STRUCTURE;
452 }
453 // Returns STRUCTURE * if structure or NULL if not.
castStructure(SIMPLE_OBJECT * psObject)454 static inline STRUCTURE *castStructure(SIMPLE_OBJECT *psObject)
455 {
456 return isStructure(psObject) ? (STRUCTURE *)psObject : (STRUCTURE *)nullptr;
457 }
458 // Returns STRUCTURE const * if structure or NULL if not.
castStructure(SIMPLE_OBJECT const * psObject)459 static inline STRUCTURE const *castStructure(SIMPLE_OBJECT const *psObject)
460 {
461 return isStructure(psObject) ? (STRUCTURE const *)psObject : (STRUCTURE const *)nullptr;
462 }
463
getBuildingResearchPoints(STRUCTURE * psStruct)464 static inline int getBuildingResearchPoints(STRUCTURE *psStruct)
465 {
466 auto &upgrade = psStruct->pStructureType->upgrade[psStruct->player];
467 return upgrade.research + upgrade.moduleResearch * psStruct->capacity;
468 }
469
getBuildingProductionPoints(STRUCTURE * psStruct)470 static inline int getBuildingProductionPoints(STRUCTURE *psStruct)
471 {
472 auto &upgrade = psStruct->pStructureType->upgrade[psStruct->player];
473 return upgrade.production + upgrade.moduleProduction * psStruct->capacity;
474 }
475
getBuildingPowerPoints(STRUCTURE * psStruct)476 static inline int getBuildingPowerPoints(STRUCTURE *psStruct)
477 {
478 auto &upgrade = psStruct->pStructureType->upgrade[psStruct->player];
479 return upgrade.power + upgrade.modulePower * psStruct->capacity;
480 }
481
getBuildingRepairPoints(STRUCTURE * psStruct)482 static inline int getBuildingRepairPoints(STRUCTURE *psStruct)
483 {
484 return psStruct->pStructureType->upgrade[psStruct->player].repair;
485 }
486
getBuildingRearmPoints(STRUCTURE * psStruct)487 static inline int getBuildingRearmPoints(STRUCTURE *psStruct)
488 {
489 return psStruct->pStructureType->upgrade[psStruct->player].rearm;
490 }
491
492 WzString getFavoriteStructs();
493 void setFavoriteStructs(WzString list);
494
495 struct LineBuild
496 {
backLineBuild497 Vector2i back() const { return (*this)[count - 1]; }
498 Vector2i operator [](int i) const { return begin + i*step; }
499
500 Vector2i begin = {0, 0};
501 Vector2i step = {0, 0};
502 int count = 0;
503 };
504
505 LineBuild calcLineBuild(Vector2i size, STRUCTURE_TYPE type, Vector2i pos, Vector2i pos2);
506 LineBuild calcLineBuild(STRUCTURE_STATS const *stats, uint16_t direction, Vector2i pos, Vector2i pos2);
507
508 #endif // __INCLUDED_SRC_STRUCTURE_H__
509