1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
|
/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
#ifndef UNIT_H
#define UNIT_H
#include <vector>
#include "Sim/Objects/SolidObject.h"
#include "Sim/Misc/Resource.h"
#include "Sim/Weapons/WeaponTarget.h"
#include "System/Matrix44f.h"
#include "System/type2.h"
class CPlayer;
class CCommandAI;
class CGroup;
class CMissileProjectile;
class AMoveType;
class CWeapon;
class CUnitScript;
class DamageArray;
class DynDamageArray;
struct SolidObjectDef;
struct UnitDef;
struct UnitLoadParams;
struct SLosInstance;
// LOS state bits
#define LOS_INLOS (1 << 0) // the unit is currently in the los of the allyteam
#define LOS_INRADAR (1 << 1) // the unit is currently in radar from the allyteam
#define LOS_PREVLOS (1 << 2) // the unit has previously been in los from the allyteam
#define LOS_CONTRADAR (1 << 3) // the unit has continuously been in radar since it was last inlos by the allyteam
#define LOS_MASK_SHIFT 4
// LOS mask bits (masked bits are not automatically updated)
#define LOS_INLOS_MASK (LOS_INLOS << LOS_MASK_SHIFT) // do not update LOS_INLOS
#define LOS_INRADAR_MASK (LOS_INRADAR << LOS_MASK_SHIFT) // do not update LOS_INRADAR
#define LOS_PREVLOS_MASK (LOS_PREVLOS << LOS_MASK_SHIFT) // do not update LOS_PREVLOS
#define LOS_CONTRADAR_MASK (LOS_CONTRADAR << LOS_MASK_SHIFT) // do not update LOS_CONTRADAR
#define LOS_ALL_BITS \
(LOS_INLOS | LOS_INRADAR | LOS_PREVLOS | LOS_CONTRADAR)
#define LOS_ALL_MASK_BITS \
(LOS_INLOS_MASK | LOS_INRADAR_MASK | LOS_PREVLOS_MASK | LOS_CONTRADAR_MASK)
class CUnit : public CSolidObject
{
public:
CR_DECLARE_DERIVED(CUnit)
CUnit();
virtual ~CUnit();
static void InitStatic();
void SanityCheck() const;
void PreUpdate() { preFramePos = pos; }
virtual void PreInit(const UnitLoadParams& params);
virtual void PostInit(const CUnit* builder);
virtual void Update();
virtual void SlowUpdate();
const SolidObjectDef* GetDef() const override { return ((const SolidObjectDef*) unitDef); }
virtual void DoDamage(const DamageArray& damages, const float3& impulse, CUnit* attacker, int weaponDefID, int projectileID) override;
virtual void DoWaterDamage();
virtual void FinishedBuilding(bool postInit);
void ApplyDamage(CUnit* attacker, const DamageArray& damages, float& baseDamage, float& experienceMod);
void ApplyImpulse(const float3& impulse) override;
bool AttackUnit(CUnit* unit, bool isUserTarget, bool wantManualFire, bool fpsMode = false);
bool AttackGround(const float3& pos, bool isUserTarget, bool wantManualFire, bool fpsMode = false);
void DropCurrentAttackTarget();
int GetBlockingMapID() const override { return id; }
void ChangeLos(int losRad, int airRad);
// negative amount=reclaim, return= true -> build power was successfully applied
bool AddBuildPower(CUnit* builder, float amount) override;
void Activate();
void Deactivate();
void ForcedMove(const float3& newPos) override;
void DeleteScript();
void EnableScriptMoveType();
void DisableScriptMoveType();
CMatrix44f GetTransformMatrix(bool synced = false, bool fullread = false) const final override;
void DependentDied(CObject* o) override;
bool AllowedReclaim(CUnit* builder) const;
void SetMetalStorage(float newStorage);
void SetEnergyStorage(float newStorage);
bool UseMetal(float metal);
void AddMetal(float metal, bool useIncomeMultiplier = true);
bool UseEnergy(float energy);
void AddEnergy(float energy, bool useIncomeMultiplier = true);
bool AddHarvestedMetal(float metal);
void SetStorage(const SResourcePack& newstorage);
bool HaveResources(const SResourcePack& res) const;
bool UseResources(const SResourcePack& res);
void AddResources(const SResourcePack& res, bool useIncomeMultiplier = true);
bool IssueResourceOrder(SResourceOrder* order);
// push the new wind to the script
void UpdateWind(float x, float z, float strength);
void UpdateTransportees();
void ReleaseTransportees(CUnit* attacker, bool selfDestruct, bool reclaimed);
void TransporteeKilled(const CObject* o);
void AddExperience(float exp);
void SetMass(float newMass) override;
void DoSeismicPing(float pingSize);
void CalculateTerrainType();
void UpdateTerrainType();
void UpdatePhysicalState(float eps) override;
float3 GetErrorVector(int allyteam) const;
float3 GetErrorPos(int allyteam, bool aiming = false) const { return (aiming? aimPos: midPos) + GetErrorVector(allyteam); }
float3 GetObjDrawErrorPos(int allyteam) const { return (GetObjDrawMidPos() + GetErrorVector(allyteam)); }
float3 GetLuaErrorVector(int allyteam, bool fullRead) const { return (fullRead? ZeroVector: GetErrorVector(allyteam)); }
float3 GetLuaErrorPos(int allyteam, bool fullRead) const { return (midPos + GetLuaErrorVector(allyteam, fullRead)); }
float3 GetDrawDeltaPos(float dt) const { return ((pos - preFramePos) * dt); }
void UpdatePosErrorParams(bool updateError, bool updateDelta);
bool UsingScriptMoveType() const { return (prevMoveType != nullptr); }
bool UnderFirstPersonControl() const { return (fpsControlPlayer != nullptr); }
bool FloatOnWater() const;
bool IsNeutral() const { return neutral; }
bool IsCloaked() const { return isCloaked; }
bool IsStunned() const { return stunned; }
bool IsIdle() const;
bool HaveTarget() const { return (curTarget.type != Target_None); }
bool CanUpdateWeapons() const {
return (forceUseWeapons || (allowUseWeapons && !onTempHoldFire && !isDead && !beingBuilt && !IsStunned()));
}
void SetNeutral(bool b);
void SetStunned(bool stun);
bool GetPosErrorBit(int at) const {
return (posErrorMask[at / 32] & (1 << (at % 32)));
}
void SetPosErrorBit(int at, int bit) {
posErrorMask[at / 32] |= ((1 << (at % 32)) * (bit == 1));
posErrorMask[at / 32] &= ~((1 << (at % 32)) * (bit == 0));
}
bool IsInLosForAllyTeam(int allyTeam) const { return ((losStatus[allyTeam] & LOS_INLOS) != 0); }
void SetLosStatus(int allyTeam, unsigned short newStatus);
void UpdateLosStatus(int allyTeam);
unsigned short CalcLosStatus(int allyTeam) const;
void UpdateWeapons();
void SlowUpdateWeapons();
void SlowUpdateKamikaze(bool scanForTargets);
void SlowUpdateCloak(bool stunCheck);
bool ScriptCloak();
bool ScriptDecloak(const CSolidObject* object, const CWeapon* weapon);
bool GetNewCloakState(bool checkStun);
enum ChangeType {
ChangeGiven,
ChangeCaptured
};
virtual bool ChangeTeam(int team, ChangeType type);
virtual void StopAttackingAllyTeam(int ally);
//Transporter stuff
CR_DECLARE_SUB(TransportedUnit)
struct TransportedUnit {
CR_DECLARE_STRUCT(TransportedUnit)
CUnit* unit;
int piece;
};
bool SetSoloBuilder(CUnit* builder, const UnitDef* buildeeDef);
void SetLastAttacker(CUnit* attacker);
void SetTransporter(CUnit* trans) { transporter = trans; }
CUnit* GetTransporter() const { return transporter; }
bool AttachUnit(CUnit* unit, int piece, bool force = false);
bool CanTransport(const CUnit* unit) const;
bool DetachUnit(CUnit* unit);
bool DetachUnitCore(CUnit* unit);
bool DetachUnitFromAir(CUnit* unit, const float3& pos); // orders <unit> to move to <pos> after detach
bool CanLoadUnloadAtPos(const float3& wantedPos, const CUnit* unit, float* wantedHeightPtr = nullptr) const;
float GetTransporteeWantedHeight(const float3& wantedPos, const CUnit* unit, bool* ok = nullptr) const;
short GetTransporteeWantedHeading(const CUnit* unit) const;
public:
void KilledScriptFinished(int wreckLevel) { deathScriptFinished = true; delayedWreckLevel = wreckLevel; }
void ForcedKillUnit(CUnit* attacker, bool selfDestruct, bool reclaimed, bool showDeathSequence = true);
virtual void KillUnit(CUnit* attacker, bool selfDestruct, bool reclaimed, bool showDeathSequence = true);
virtual void IncomingMissile(CMissileProjectile* missile);
void TempHoldFire(int cmdID);
void SetHoldFire(bool b) { onTempHoldFire = b; }
// start this unit in free fall from parent unit
void Drop(const float3& parentPos, const float3& parentDir, CUnit* parent);
void PostLoad();
protected:
void ChangeTeamReset();
void UpdateResources();
float GetFlankingDamageBonus(const float3& attackDir);
public: // unsynced methods
bool SetGroup(CGroup* newGroup, bool fromFactory = false, bool autoSelect = true);
const CGroup* GetGroup() const;
CGroup* GetGroup();
public:
static void SetEmpDeclineRate(float value) { empDeclineRate = value; }
static void SetExpMultiplier(float value) { expMultiplier = value; }
static void SetExpPowerScale(float value) { expPowerScale = value; }
static void SetExpHealthScale(float value) { expHealthScale = value; }
static void SetExpReloadScale(float value) { expReloadScale = value; }
static void SetExpGrade(float value) { expGrade = value; }
static float GetExpMultiplier() { return expMultiplier; }
static float GetExpPowerScale() { return expPowerScale; }
static float GetExpHealthScale() { return expHealthScale; }
static float GetExpReloadScale() { return expReloadScale; }
static float GetExpGrade() { return expGrade; }
static float ExperienceScale(const float limExperience, const float experienceWeight) {
// limExperience ranges from 0.0 to 0.9999..., experienceWeight
// should be in [0, 1] and have no effect on accuracy when zero
return (1.0f - (limExperience * experienceWeight));
}
public:
const UnitDef* unitDef = nullptr;
// Our shield weapon, NULL if we have none
CWeapon* shieldWeapon = nullptr;
// Our weapon with stockpiled ammo, NULL if we have none
CWeapon* stockpileWeapon = nullptr;
const DynDamageArray* selfdExpDamages = nullptr;
const DynDamageArray* deathExpDamages = nullptr;
CUnit* soloBuilder = nullptr;
CUnit* lastAttacker = nullptr;
// transport that the unit is currently in
CUnit* transporter = nullptr;
// player who is currently FPS'ing this unit
CPlayer* fpsControlPlayer = nullptr;
AMoveType* moveType = nullptr;
AMoveType* prevMoveType = nullptr;
CCommandAI* commandAI = nullptr;
CUnitScript* script = nullptr;
// current attackee
SWeaponTarget curTarget;
// sufficient for the largest UnitScript (CLuaUnitScript)
uint8_t usMemBuffer[368];
// sufficient for the largest AMoveType (CGroundMoveType)
// need two buffers since ScriptMoveType might be enabled
uint8_t amtMemBuffer[498];
uint8_t smtMemBuffer[370];
// sufficient for the largest CommandAI type (CBuilderCAI)
// knowing the exact CAI object size here is not required;
// static asserts will catch any overflow
uint8_t caiMemBuffer[700];
std::vector<CWeapon*> weapons;
// which squares the unit can currently observe, per los-type
std::array<SLosInstance*, /*ILosType::LOS_TYPE_COUNT*/ 7> los{{nullptr}};
// indicates the los/radar status each allyteam has on this unit
// should technically be MAX_ALLYTEAMS, but #allyteams <= #teams
std::array<unsigned char, /*MAX_TEAMS*/ 255> losStatus{{0}};
// bit-mask indicating which allyteams see this unit with positional error
std::array<unsigned int, /*MAX_TEAMS/32*/ 8> posErrorMask{{1}};
// quads the unit is part of
std::vector<int> quads;
std::vector<TransportedUnit> transportedUnits;
// incoming projectiles for which flares can cause retargeting
std::array<CMissileProjectile*, /*MAX_INCOMING_MISSILES*/ 8> incomingMissiles{{nullptr}};
// position at start of current simframe; updated by ForcedMove
// used as interpolation reference for drawpos since a unit can
// move along vectors other than its velocity
float3 preFramePos;
float3 lastMuzzleFlameDir = UpVector;
// units take less damage when attacked from this dir (encourage flanking fire)
float3 flankingBonusDir = RgtVector;
// used for radar inaccuracy etc
float3 posErrorVector;
float3 posErrorDelta;
int featureDefID = -1; // FeatureDef id of the wreck we spawn on death
// indicate the relative power of the unit, used for experience calulations etc
float power = 100.0f;
// 0.0-1.0
float buildProgress = 0.0f;
// if (health - this) is negative the unit is stunned
float paralyzeDamage = 0.0f;
// how close this unit is to being captured
float captureProgress = 0.0f;
float experience = 0.0f;
// approaches 1 as experience approaches infinity
float limExperience = 0.0f;
// how much terraforming is left to do
float terraformLeft = 0.0f;
// How much reapir power has been added to this recently
float repairAmount = 0.0f;
// last frame unit was attacked by other unit
int lastAttackFrame = -200;
// last time this unit fired a weapon
int lastFireWeapon = 0;
// if we arent built on for a while start decaying
int lastNanoAdd = 0;
int lastFlareDrop = 0;
// id of transport that the unit is about to be {un}loaded by
int loadingTransportId = -1;
int unloadingTransportId = -1;
int transportCapacityUsed = 0;
float transportMassUsed = 0.0f;
// the wreck level the unit will eventually create when it has died
int delayedWreckLevel = -1;
// how long the unit has been inactive
unsigned int restTime = 0;
unsigned int outOfMapTime = 0;
float reloadSpeed = 1.0f;
float maxRange = 0.0f;
// used to determine muzzle flare size
float lastMuzzleFlameSize = 0.0f;
int armorType = 0;
// what categories the unit is part of (bitfield)
unsigned int category = 0;
int mapSquare = -1;
// set los to this when finished building
int realLosRadius = 0;
int realAirLosRadius = 0;
int losRadius = 0;
int airLosRadius = 0;
int radarRadius = 0;
int sonarRadius = 0;
int jammerRadius = 0;
int sonarJamRadius = 0;
int seismicRadius = 0;
float seismicSignature = 0.0f;
float decloakDistance = 0.0f;
// only when the unit is active
SResourcePack resourcesCondUse;
SResourcePack resourcesCondMake;
// always applied
SResourcePack resourcesUncondUse;
SResourcePack resourcesUncondMake;
// costs per UNIT_SLOWUPDATE_RATE frames
SResourcePack resourcesUse;
// incomes per UNIT_SLOWUPDATE_RATE frames
SResourcePack resourcesMake;
// variables used for calculating unit resource usage
SResourcePack resourcesUseI;
SResourcePack resourcesMakeI;
SResourcePack resourcesUseOld;
SResourcePack resourcesMakeOld;
// the amount of storage the unit contributes to the team
SResourcePack storage;
// per unit metal storage (gets filled on reclaim and needs then to be unloaded at some storage building -> 2nd part is lua's job)
SResourcePack harvestStorage;
SResourcePack harvested;
SResourcePack cost = {100.0f, 0.0f};
// how much metal the unit currently extracts from the ground
float metalExtract = 0.0f;
float buildTime = 100.0f;
// decaying value of how much damage the unit has taken recently (for severity of death)
float recentDamage = 0.0f;
int fireState = 0;
int moveState = 0;
// for units being dropped from transports (parachute drops)
float fallSpeed = 0.2f;
/**
* 0 = no flanking bonus
* 1 = global coords, mobile
* 2 = unit coords, mobile
* 3 = unit coords, locked
*/
int flankingBonusMode = 0;
// how much the lowest damage direction of the flanking bonus can turn upon an attack (zeroed when attacked, slowly increases)
float flankingBonusMobility = 10.0f;
// how much ability of the flanking bonus direction to move builds up each frame
float flankingBonusMobilityAdd = 0.01f;
// average factor to multiply damage by
float flankingBonusAvgDamage = 1.4f;
// (max damage - min damage) / 2
float flankingBonusDifDamage = 0.5f;
float armoredMultiple = 1.0f;
// multiply all damage the unit take with this
float curArmorMultiple = 1.0f;
int nextPosErrorUpdate = 1;
int lastTerrainType = -1;
// Used for calling setSFXoccupy which TA scripts want
int curTerrainType = 0;
int selfDCountdown = 0;
// the damage value passed to CEGs spawned by this unit's script
int cegDamage = 0;
// if the unit is in it's 'on'-state
bool activated = false;
// prevent damage from hitting an already dead unit (causing multi wreck etc)
bool isDead = false;
bool armoredState = false;
bool stealth = false;
bool sonarStealth = false;
// used by constructing units
bool inBuildStance = false;
// tells weapons that support it to try to use a high trajectory
bool useHighTrajectory = false;
// used by landed gunships to block weapon Update()'s, also by builders to
// prevent weapon SlowUpdate()'s and Attack{Unit,Ground}()'s during certain
// commands
bool onTempHoldFire = false;
// Lua overrides for CanUpdateWeapons
bool forceUseWeapons = false;
bool allowUseWeapons = true;
// signals if script has finished executing Killed and the unit can be deleted
bool deathScriptFinished = false;
// if true, unit will not be automatically fired upon unless attacker's fireState is set to > FIREATWILL
bool neutral = false;
// if unit is currently incompletely constructed (implies buildProgress < 1)
bool beingBuilt = true;
// if the updir is straight up or align to the ground vector
bool upright = true;
// whether the ground below this unit has been terraformed
bool groundLevelled = true;
// true if the unit is currently cloaked (has enough energy etc)
bool isCloaked = false;
// true if the unit currently wants to be cloaked
bool wantCloak = false;
// unsynced vars
bool noMinimap = false;
bool leaveTracks = false;
bool isSelected = false;
bool isIcon = false;
float iconRadius = 0.0f;
private:
// if we are stunned by a weapon or for other reason, access via IsStunned/SetStunned(bool)
bool stunned = false;
static float empDeclineRate;
static float expMultiplier;
static float expPowerScale;
static float expHealthScale;
static float expReloadScale;
static float expGrade;
};
#endif // UNIT_H
|