777 lines
35 KiB
C
777 lines
35 KiB
C
![]() |
/*
|
||
|
** Command & Conquer Generals(tm)
|
||
|
** Copyright 2025 Electronic Arts Inc.
|
||
|
**
|
||
|
** This program is free software: you can redistribute it and/or modify
|
||
|
** it under the terms of the GNU General Public License as published by
|
||
|
** the Free Software Foundation, either version 3 of the License, or
|
||
|
** (at your option) any later version.
|
||
|
**
|
||
|
** This program is distributed in the hope that it will be useful,
|
||
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
** GNU General Public License for more details.
|
||
|
**
|
||
|
** You should have received a copy of the GNU General Public License
|
||
|
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// //
|
||
|
// (c) 2001-2003 Electronic Arts Inc. //
|
||
|
// //
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// Object.h ///////////////////////////////////////////////////////////////////
|
||
|
// Simple base object
|
||
|
// Author: Michael S. Booth, October 2000
|
||
|
|
||
|
#pragma once
|
||
|
#ifndef _OBJECT_H_
|
||
|
#define _OBJECT_H_
|
||
|
|
||
|
#include "Lib/BaseType.h"
|
||
|
|
||
|
#include "Common/Geometry.h"
|
||
|
#include "Common/Snapshot.h"
|
||
|
#include "Common/SpecialPowerMaskType.h"
|
||
|
#include "Common/DisabledTypes.h"
|
||
|
#include "Common/Thing.h"
|
||
|
|
||
|
#include "GameClient/Color.h"
|
||
|
|
||
|
#include "GameLogic/WeaponBonusConditionFlags.h"
|
||
|
#include "GameLogic/WeaponSet.h"
|
||
|
#include "GameLogic/WeaponSetFlags.h"
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Forward References
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
class AIGroup;
|
||
|
class AIUpdateInterface;
|
||
|
class Anim2DTemplate;
|
||
|
class BehaviorModule;
|
||
|
class BehaviorModuleInterface;
|
||
|
class BodyModuleInterface;
|
||
|
class CollideModule;
|
||
|
class CollideModuleInterface;
|
||
|
class CommandButton;
|
||
|
class ContainModuleInterface;
|
||
|
class CreateModuleInterface;
|
||
|
class DamageInfo;
|
||
|
class DamageInfoInput;
|
||
|
class DamageModule;
|
||
|
class DamageModuleInterface;
|
||
|
class DestroyModuleInterface;
|
||
|
class DockUpdateInterface;
|
||
|
class Dict;
|
||
|
class DieModule;
|
||
|
class DieModuleInterface;
|
||
|
class ExitInterface;
|
||
|
class ExperienceTracker;
|
||
|
class FiringTracker;
|
||
|
class Module;
|
||
|
class PartitionData;
|
||
|
class PhysicsBehavior;
|
||
|
class PhysicsUpdate;
|
||
|
class Player;
|
||
|
class PolygonTrigger;
|
||
|
class ProductionUpdateInterface;
|
||
|
class RadarObject;
|
||
|
class SightingInfo;
|
||
|
class SpawnBehaviorInterface;
|
||
|
class SpecialAbilityUpdate;
|
||
|
class SpecialPowerCompletionDie;
|
||
|
class SpecialPowerModuleInterface;
|
||
|
class SpecialPowerTemplate;
|
||
|
class SpecialPowerUpdateInterface;
|
||
|
class Team;
|
||
|
class UpdateModule;
|
||
|
class UpdateModuleInterface;
|
||
|
class UpgradeModule;
|
||
|
class UpgradeModuleInterface;
|
||
|
class UpgradeTemplate;
|
||
|
|
||
|
class ObjectHeldHelper;
|
||
|
class ObjectDisabledHelper;
|
||
|
class ObjectSMCHelper;
|
||
|
class ObjectRepulsorHelper;
|
||
|
class ObjectWeaponStatusHelper;
|
||
|
class ObjectDefectionHelper;
|
||
|
|
||
|
enum CommandSourceType;
|
||
|
enum DamageType;
|
||
|
enum HackerAttackMode;
|
||
|
enum NameKeyType;
|
||
|
enum SpecialPowerType;
|
||
|
enum WeaponBonusConditionType;
|
||
|
enum WeaponChoiceCriteria;
|
||
|
enum WeaponSetConditionType;
|
||
|
enum WeaponSetType;
|
||
|
enum WeaponStatus;
|
||
|
enum RadarPriorityType;
|
||
|
enum CanAttackResult;
|
||
|
|
||
|
// For ObjectStatusBits and TheObjectStatusBitNames
|
||
|
#include "GameLogic/ObjectStatusBits.h"
|
||
|
|
||
|
// For ObjectScriptStatusBit
|
||
|
#include "GameLogic/ObjectScriptStatusBits.h"
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Type Defines
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
struct TTriggerInfo
|
||
|
{
|
||
|
const PolygonTrigger* pTrigger; ///< The trigger area that the object is inside.
|
||
|
Byte entered; ///< True if the object entered this trigger area this frame.
|
||
|
Byte exited; ///< True if the object entered this trigger area this frame.
|
||
|
Byte isInside; ///< True if the object is inside this trigger area this frame.
|
||
|
Byte padding; ///< unused.
|
||
|
|
||
|
TTriggerInfo() : entered(false), exited(false), isInside(false), padding(false), pTrigger(NULL) { }
|
||
|
|
||
|
};
|
||
|
|
||
|
//----------------------------------------------------
|
||
|
|
||
|
|
||
|
enum CrushSquishTestType
|
||
|
{
|
||
|
TEST_CRUSH_ONLY,
|
||
|
TEST_SQUISH_ONLY,
|
||
|
TEST_CRUSH_OR_SQUISH
|
||
|
};
|
||
|
|
||
|
|
||
|
// ---------------------------------------------------
|
||
|
/**
|
||
|
* Object definition. Objects are manipulated via TheGameLogic singleton.
|
||
|
* @todo Create an ObjectInterface class.
|
||
|
*/
|
||
|
class Object : public Thing, public Snapshot
|
||
|
{
|
||
|
|
||
|
MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(Object, "ObjectPool" )
|
||
|
/// destructor is non-public in order to require the use of TheGameLogic->destroyObject()
|
||
|
MEMORY_POOL_DELETEINSTANCE_VISIBILITY(protected)
|
||
|
|
||
|
public:
|
||
|
|
||
|
/// Object constructor automatically attaches all objects to "TheGameLogic"
|
||
|
Object(const ThingTemplate *thing, ObjectStatusBits statusBits, Team *team);
|
||
|
|
||
|
void initObject();
|
||
|
|
||
|
void onDestroy(); ///< run during TheGameLogic::destroyObject
|
||
|
|
||
|
Object* getNextObject() { return m_next; }
|
||
|
const Object* getNextObject() const { return m_next; }
|
||
|
|
||
|
void updateObjValuesFromMapProperties(Dict* properties); ///< Brings in properties set in the editor.
|
||
|
|
||
|
// ids and binding
|
||
|
ObjectID getID() const { return m_id; } ///< this object's unique ID
|
||
|
void friend_bindToDrawable( Drawable *draw ); ///< set drawable association. for use ONLY by GameLogic!
|
||
|
Drawable* getDrawable() const { return m_drawable; } ///< drawable (if any) bound to obj
|
||
|
|
||
|
ObjectID getProducerID() const { return m_producerID; }
|
||
|
void setProducer(const Object* obj);
|
||
|
|
||
|
ObjectID getBuilderID() const { return m_builderID; }
|
||
|
void setBuilder( const Object *obj );
|
||
|
|
||
|
void enterGroup( AIGroup *group ); ///< become a member of the AIGroup
|
||
|
void leaveGroup( void ); ///< leave our current AIGroup
|
||
|
AIGroup *getGroup(void);
|
||
|
|
||
|
// physical properties
|
||
|
Bool isMobile() const; ///< returns true if object is currently able to move
|
||
|
Bool isAbleToAttack() const; ///< returns true if object currently has some kind of attack capability
|
||
|
|
||
|
void maskObject( Bool mask ); ///< mask/unmask object
|
||
|
|
||
|
// cannot set velocity, since this is calculated from position every frame
|
||
|
Bool isDestroyed() const { return (m_status & OBJECT_STATUS_DESTROYED) != 0; } ///< Returns TRUE if object has been destroyed
|
||
|
Bool isAirborneTarget() const { return (m_status & OBJECT_STATUS_AIRBORNE_TARGET) != 0; } ///< Our locomotor will control marking us as a valid target for anti air weapons or not
|
||
|
Bool isUsingAirborneLocomotor( void ) const; ///< returns true if the current locomotor is an airborne one
|
||
|
|
||
|
/// central place for us to put any additional capture logic
|
||
|
void onCapture( Player *oldOwner, Player *newOwner );
|
||
|
|
||
|
/// And game death logic. Destroy is deletion of object as code
|
||
|
void onDie( DamageInfo *damageInfo );
|
||
|
|
||
|
// health and damage
|
||
|
void attemptDamage( DamageInfo *damageInfo ); ///< damage object as specified by the info
|
||
|
void attemptHealing(Real amount, const Object* source); ///< heal object as specified by the info
|
||
|
Bool attemptHealingFromSoleBenefactor ( Real amount, const Object* source, UnsignedInt duration );///< for the non-stacking healers like ambulance and propaganda
|
||
|
ObjectID getSoleHealingBenefactor( void ) const;
|
||
|
|
||
|
Real estimateDamage( DamageInfoInput& damageInfo ) const;
|
||
|
void kill(); ///< do max health amount of kill damage to object
|
||
|
void healCompletely(); ///< Restore max health to this Object
|
||
|
|
||
|
void scoreTheKill( const Object *victim ); ///< I just killed this object.
|
||
|
void onVeterancyLevelChanged( VeterancyLevel oldLevel, VeterancyLevel newLevel ); ///< I just achieved this level right this moment
|
||
|
ExperienceTracker* getExperienceTracker() {return m_experienceTracker;}
|
||
|
const ExperienceTracker* getExperienceTracker() const {return m_experienceTracker;}
|
||
|
VeterancyLevel getVeterancyLevel() const;
|
||
|
|
||
|
inline const AsciiString& getName() const { return m_name; }
|
||
|
inline void setName( const AsciiString& newName ) { m_name = newName; }
|
||
|
|
||
|
inline Team* getTeam() { return m_team; }
|
||
|
inline const Team *getTeam() const { return m_team; }
|
||
|
|
||
|
void restoreOriginalTeam();
|
||
|
|
||
|
void setTeam( Team* team ); ///< sets the unit's team AND original team
|
||
|
void setTemporaryTeam( Team* team ); ///< sets the unit's team BUT NOT its original team
|
||
|
|
||
|
Player* getControllingPlayer() const;
|
||
|
Relationship getRelationship(const Object *that) const;
|
||
|
|
||
|
Color getIndicatorColor() const;
|
||
|
Color getNightIndicatorColor() const;
|
||
|
Bool hasCustomIndicatorColor() const { return m_indicatorColor != 0; }
|
||
|
void setCustomIndicatorColor(Color c);
|
||
|
void removeCustomIndicatorColor();
|
||
|
|
||
|
Bool isLocallyControlled() const;
|
||
|
Bool isNeutralControlled() const;
|
||
|
|
||
|
Bool getIsUndetectedDefector(void) const { return BitTest(m_privateStatus, UNDETECTED_DEFECTOR); }
|
||
|
void friend_setUndetectedDefector(Bool status);
|
||
|
|
||
|
inline Bool isOffMap() const { return BitTest(m_privateStatus, OFF_MAP); }
|
||
|
|
||
|
inline Bool isCaptured() const { return BitTest(m_privateStatus, CAPTURED); }
|
||
|
void setCaptured(Bool isCaptured);
|
||
|
|
||
|
inline const GeometryInfo& getGeometryInfo() const { return m_geometryInfo; }
|
||
|
void setGeometryInfo(const GeometryInfo& geom);
|
||
|
void setGeometryInfoZ( Real newZ );
|
||
|
|
||
|
void onCollide( Object *other, const Coord3D *loc, const Coord3D *normal );
|
||
|
|
||
|
// access to modules
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
//This is a good creation inspector. There's been multitudes of issues with conflicts of
|
||
|
//Objects getting constructed causing crashes either because the modules aren't created
|
||
|
//yet, and there's stuff being done inside of setTeam() that cares.
|
||
|
Bool areModulesReady() const { return m_modulesReady; }
|
||
|
|
||
|
BehaviorModule** getBehaviorModules() const { return m_behaviors; }
|
||
|
|
||
|
BodyModuleInterface* getBodyModule() const { return m_body; }
|
||
|
ContainModuleInterface* getContain() const { return m_contain; }
|
||
|
SpawnBehaviorInterface* getSpawnBehaviorInterface() const;
|
||
|
|
||
|
|
||
|
// special case for the AIUpdateInterface, since it will be referred to a great deal
|
||
|
inline AIUpdateInterface *getAIUpdateInterface() { return m_ai; }
|
||
|
inline const AIUpdateInterface* getAIUpdateInterface() const { return m_ai; }
|
||
|
|
||
|
inline AIUpdateInterface *getAI() { return m_ai; }
|
||
|
inline const AIUpdateInterface* getAI() const { return m_ai; }
|
||
|
|
||
|
inline PhysicsBehavior* getPhysics() { return m_physics; }
|
||
|
inline const PhysicsBehavior* getPhysics() const { return m_physics; }
|
||
|
void topple( const Coord3D *toppleDirection, Real toppleSpeed, UnsignedInt options );
|
||
|
|
||
|
UpdateModule* findUpdateModule(NameKeyType key) const { return (UpdateModule*)findModule(key); }
|
||
|
DamageModule* findDamageModule(NameKeyType key) const { return (DamageModule*)findModule(key); }
|
||
|
|
||
|
Bool isSalvageCrate() const;
|
||
|
|
||
|
//
|
||
|
// Find us our production update interface if we have one. This method exists simply
|
||
|
// because we do this in a lot of places in the code and I want a convenient way to get thsi (CBD)
|
||
|
//
|
||
|
ProductionUpdateInterface* getProductionUpdateInterface( void );
|
||
|
|
||
|
//
|
||
|
// Find us our dock update interface if we have one. Again, this method exists simple
|
||
|
// because we want to do this in a lot of places throughout the code
|
||
|
//
|
||
|
DockUpdateInterface *getDockUpdateInterface( void );
|
||
|
|
||
|
// Ditto for special powers -- Kris
|
||
|
SpecialPowerModuleInterface* findSpecialPowerModuleInterface( SpecialPowerType type ) const;
|
||
|
SpecialAbilityUpdate* findSpecialAbilityUpdate( SpecialPowerType type ) const;
|
||
|
SpecialPowerCompletionDie* findSpecialPowerCompletionDie() const;
|
||
|
SpecialPowerUpdateInterface* findSpecialPowerWithOverridableDestinationActive( SpecialPowerType type = SPECIAL_INVALID ) const;
|
||
|
|
||
|
inline UnsignedInt getStatusBits() const { return m_status; }
|
||
|
inline Bool testStatus(ObjectStatusBits bit) const { return (m_status & bit) != 0; }
|
||
|
void setStatus( ObjectStatusBits bits, Bool set = true );
|
||
|
inline void clearStatus( ObjectStatusBits bits ) { setStatus(bits, false); }
|
||
|
void updateUpgradeModules(); ///< We need to go through our Upgrade Modules and see which should be activated
|
||
|
Int64 getObjectCompletedUpgradeMask() const { return m_objectUpgradesCompleted; } ///< Upgrades I complete locally
|
||
|
|
||
|
//This function sucks.
|
||
|
//It was added for objects that can disguise as other objects and contain upgraded subobject overrides.
|
||
|
//A concrete example is the bomb truck. Different payloads are displayed based on which upgrades have been
|
||
|
//made. When the bomb truck disguises as something else, these subobjects are lost because the vector is
|
||
|
//stored in W3DDrawModule. When we revert back to the original bomb truck, we call this function to
|
||
|
//recalculate those upgraded subobjects.
|
||
|
void forceRefreshSubObjectUpgradeStatus();
|
||
|
|
||
|
// Useful for status bits that can be set by the scripting system
|
||
|
inline Bool testScriptStatusBit(ObjectScriptStatusBit b) const { return BitTest(m_scriptStatus, b); }
|
||
|
void setScriptStatus( ObjectScriptStatusBit bit, Bool set = true );
|
||
|
inline void clearScriptStatus( ObjectScriptStatusBit bit ) { setScriptStatus(bit, false); }
|
||
|
|
||
|
// Selectable is individually controlled on an object by object basis for design now.
|
||
|
// It defaults to the thingTemplate->isKindof(KINDOF_SELECTABLE), however, it can be overridden on an
|
||
|
// object by object basis. Finally, it can be temporarily overriden by the OBJECT_STATUS_UNSELECTABLE.
|
||
|
// jba.
|
||
|
void setSelectable(Bool selectable);
|
||
|
Bool isSelectable() const;
|
||
|
|
||
|
Bool isMassSelectable() const;
|
||
|
|
||
|
// User specified formation.
|
||
|
void setFormationID(enum FormationID id) {m_formationID = id;}
|
||
|
enum FormationID getFormationID(void) const {return m_formationID;}
|
||
|
void setFormationOffset(const Coord2D& offset) {m_formationOffset = offset;}
|
||
|
void getFormationOffset(Coord2D* offset) const {*offset = m_formationOffset;}
|
||
|
|
||
|
|
||
|
//THIS FUNCTION BELONGS AT THE OBJECT LEVEL BECAUSE THERE IS AT LEAST ONE SPECIAL UNIT
|
||
|
//(ANGRY MOB) WHICH NEEDS LOGIC-SIDE POSITION CALC'S...
|
||
|
//IT WOULD PROBABLY BE WISE TO MOVE ALL THE HARD-CODED DEFAULTS BELOW
|
||
|
//INTO A new Drawable::getHealthBox..() WHICH USES GEOM0INFO, MODEL DATA, INI DATA, ETC.
|
||
|
void getHealthBoxPosition(Coord3D& pos) const;
|
||
|
Bool getHealthBoxDimensions(Real &healthBoxHeight, Real &healthBoxWidth) const;
|
||
|
|
||
|
inline Bool isEffectivelyDead() const { return (m_privateStatus & EFFECTIVELY_DEAD) != 0; }
|
||
|
void setEffectivelyDead(Bool dead);
|
||
|
|
||
|
void markSingleUseCommandUsed() { m_singleUseCommandUsed = true; }
|
||
|
Bool hasSingleUseCommandBeenUsed() const { return m_singleUseCommandUsed; }
|
||
|
|
||
|
/// returns true iff the object can run over the other object.
|
||
|
Bool canCrushOrSquish(Object *otherObj, CrushSquishTestType testType = TEST_CRUSH_OR_SQUISH) const;
|
||
|
UnsignedByte getCrusherLevel() const;
|
||
|
UnsignedByte getCrushableLevel() const;
|
||
|
|
||
|
Bool hasUpgrade( const UpgradeTemplate *upgradeT ) const ; ///< does this object already have this upgrade
|
||
|
Bool affectedByUpgrade( const UpgradeTemplate *upgradeT ) const ; ///< can the object even "have" this upgrade, will it do something?
|
||
|
void giveUpgrade( const UpgradeTemplate *upgradeT ); ///< give upgrade to this object
|
||
|
void removeUpgrade( const UpgradeTemplate *upgradeT ); ///< remove upgrade from this object
|
||
|
|
||
|
void calcNaturalRallyPoint(Coord2D *pt); ///< calc the "natural" starting rally point
|
||
|
void setConstructionPercent( Real percent ) { m_constructionPercent = percent; }
|
||
|
Real getConstructionPercent() const { return m_constructionPercent; }
|
||
|
|
||
|
void setLayer( PathfindLayerEnum layer );
|
||
|
PathfindLayerEnum getLayer() const { return m_layer; }
|
||
|
|
||
|
void setDestinationLayer( PathfindLayerEnum layer );
|
||
|
PathfindLayerEnum getDestinationLayer() const { return m_destinationLayer; }
|
||
|
|
||
|
void prependToList(Object **pListHead);
|
||
|
void removeFromList(Object **pListHead);
|
||
|
Bool isInList(Object **pListHead) const;
|
||
|
|
||
|
// this is intended for use ONLY by GameLogic.
|
||
|
void friend_deleteInstance() { deleteInstance(); }
|
||
|
|
||
|
/// cache the partition module (should be called only by PartitionData)
|
||
|
void friend_setPartitionData(PartitionData *pd) { m_partitionData = pd; }
|
||
|
PartitionData *friend_getPartitionData() const { return m_partitionData; }
|
||
|
const PartitionData *friend_getConstPartitionData() const { return m_partitionData; }
|
||
|
|
||
|
void onPartitionCellChange();///< We have moved a 'significant' amount, so do maintenence that can be considered 'cell-based'
|
||
|
void handlePartitionCellMaintenance(); ///< Undo and redo all shroud actions. Call when something has changed, like position or ownership or Death
|
||
|
|
||
|
Real getVisionRange() const; ///< How far can you see? This is dynamic so it is in Object.
|
||
|
void setVisionRange( Real newVisionRange ); ///< Access to setting someone's Vision distance
|
||
|
Real getShroudRange() const; ///< How far can you shroud? Even more dynamic since it'll start at zero for everyone.
|
||
|
void setShroudRange( Real newShroudRange ); ///< Access to setting someone's shrouding distance
|
||
|
Real getShroudClearingRange() const; ///< How far do you clear shroud?
|
||
|
void setShroudClearingRange( Real newShroudClearingRange ); ///< Access to setting someone's clear shroud distance
|
||
|
|
||
|
|
||
|
// Both of these calls are intended to only be used by TerrainLogic, specifically setActiveBoundary()
|
||
|
void friend_prepareForMapBoundaryAdjust(void);
|
||
|
void friend_notifyOfNewMapBoundary(void);
|
||
|
|
||
|
// data for the radar
|
||
|
void friend_setRadarData( RadarObject *rd ) { m_radarData = rd; }
|
||
|
RadarObject *friend_getRadarData() { return m_radarData; }
|
||
|
RadarPriorityType getRadarPriority() const;
|
||
|
|
||
|
// contained-by
|
||
|
inline Object *getContainedBy() { return m_containedBy; }
|
||
|
inline const Object *getContainedBy() const { return m_containedBy; }
|
||
|
inline UnsignedInt getContainedByFrame() const { return m_containedByFrame; }
|
||
|
inline Bool isContained() const { return m_containedBy != NULL; }
|
||
|
void onContainedBy( Object *containedBy );
|
||
|
void onRemovedFrom( Object *removedFrom );
|
||
|
Int getTransportSlotCount() const;
|
||
|
void friend_setContainedBy( Object *containedBy ) { m_containedBy = containedBy; }
|
||
|
|
||
|
// Special Powers -------------------------------------------------------------------------------
|
||
|
SpecialPowerModuleInterface *getSpecialPowerModule( const SpecialPowerTemplate *specialPowerTemplate ) const;
|
||
|
void doSpecialPower( const SpecialPowerTemplate *specialPowerTemplate, UnsignedInt commandOptions, Bool forced = false ); ///< execute power
|
||
|
void doSpecialPowerAtObject( const SpecialPowerTemplate *specialPowerTemplate, Object *obj, UnsignedInt commandOptions, Bool forced = false ); ///< execute power
|
||
|
void doSpecialPowerAtLocation( const SpecialPowerTemplate *specialPowerTemplate, const Coord3D *loc, UnsignedInt commandOptions, Bool forced = false ); ///< execute power
|
||
|
void doSpecialPowerAtMultipleLocations( const SpecialPowerTemplate *specialPowerTemplate,
|
||
|
const Coord3D *locations, Int locCount, UnsignedInt commandOptions, Bool forced = false ); ///< execute power
|
||
|
void doCommandButton( const CommandButton *commandButton, CommandSourceType cmdSource );
|
||
|
void doCommandButtonAtObject( const CommandButton *commandButton, Object *obj, CommandSourceType cmdSource );
|
||
|
void doCommandButtonAtPosition( const CommandButton *commandButton, const Coord3D *pos, CommandSourceType cmdSource );
|
||
|
|
||
|
/**
|
||
|
For Object specific dynamic command sets. Different from the Science specific ones handled in ThingTemplate
|
||
|
*/
|
||
|
const AsciiString& getCommandSetString() const;
|
||
|
void setCommandSetStringOverride( AsciiString newCommandSetString ) { m_commandSetStringOverride = newCommandSetString; }
|
||
|
|
||
|
/// People are faking their commandsets, and, Suprise!, they are authoritative. Challenge everything.
|
||
|
Bool Object::canProduceUpgrade( const UpgradeTemplate *upgrade );
|
||
|
|
||
|
// Weapons & Damage -------------------------------------------------------------------------------------------------
|
||
|
void reloadAllAmmo(Bool now);
|
||
|
Bool isOutOfAmmo() const;
|
||
|
Bool hasAnyWeapon() const;
|
||
|
Bool hasAnyDamageWeapon() const; //Kris: a should be used for real weapons that directly inflict damage... not deploy, hack, etc.
|
||
|
Bool hasWeaponToDealDamageType(DamageType typeToDeal) const;
|
||
|
Real getLargestWeaponRange() const;
|
||
|
|
||
|
Weapon* getWeaponInWeaponSlot(WeaponSlotType wslot) const { return m_weaponSet.getWeaponInWeaponSlot(wslot); }
|
||
|
|
||
|
// see if this current weapon set's weapons has shared reload times
|
||
|
const Bool isReloadTimeShared() const { return m_weaponSet.isSharedReloadTime(); }
|
||
|
|
||
|
Weapon* getCurrentWeapon(WeaponSlotType* wslot = NULL);
|
||
|
const Weapon* getCurrentWeapon(WeaponSlotType* wslot = NULL) const;
|
||
|
void setFiringConditionForCurrentWeapon() const;
|
||
|
void adjustModelConditionForWeaponStatus(); ///< Check to see if I should change my model condition.
|
||
|
void fireCurrentWeapon(Object *target);
|
||
|
void fireCurrentWeapon(const Coord3D* pos);
|
||
|
void preFireCurrentWeapon( const Object *victim );
|
||
|
UnsignedInt getLastShotFiredFrame() const; ///< Get the frame a shot was last fired on
|
||
|
ObjectID getLastVictimID() const; ///< Get the last victim we shot at
|
||
|
Weapon* findWaypointFollowingCapableWeapon();
|
||
|
Bool getAmmoPipShowingInfo(Int& numTotal, Int& numFull) const;
|
||
|
|
||
|
/**
|
||
|
Determines if the unit has any weapon that could conceivably
|
||
|
harm the victim. this does not take range, ammo, etc. into
|
||
|
account, but immutable weapon properties, such as "can you
|
||
|
target airborne victims".
|
||
|
*/
|
||
|
/*
|
||
|
NOTE: getAbleToAttackSpecificObject NO LONGER internally calls isAbleToAttack(),
|
||
|
since that isn't an incredibly fast call, and this is called repeatedly in some inner loops
|
||
|
where we already know that isAbleToAttack() == true. so you should always
|
||
|
call isAbleToAttack prior to calling this! (srj)
|
||
|
*/
|
||
|
CanAttackResult getAbleToAttackSpecificObject( AbleToAttackType t, const Object* target, CommandSourceType commandSource ) const;
|
||
|
|
||
|
//Used for base defenses and otherwise stationary units to see if you can attack a position potentially out of range.
|
||
|
CanAttackResult getAbleToUseWeaponAgainstTarget( AbleToAttackType attackType, const Object *victim, const Coord3D *pos, CommandSourceType commandSource ) const;
|
||
|
|
||
|
/**
|
||
|
Selects the best weapon for the given target, and sets it as the current weapon.
|
||
|
If there is no weapon that can damage the target, false is returned (and the current-weapon is unchanged).
|
||
|
Note that this DOES take weapon attack range into account.
|
||
|
*/
|
||
|
Bool chooseBestWeaponForTarget(const Object* target, WeaponChoiceCriteria criteria, CommandSourceType cmdSource);
|
||
|
|
||
|
// set and/or clear a single modelcondition flag
|
||
|
void setModelConditionState( ModelConditionFlagType a );
|
||
|
void clearModelConditionState( ModelConditionFlagType a );
|
||
|
void clearAndSetModelConditionState( ModelConditionFlagType clr, ModelConditionFlagType set );
|
||
|
|
||
|
//Special model states are states that are turned on for a period of time, and turned off
|
||
|
//automatically -- used for cheer, and scripted special moment animations. Setting a special
|
||
|
//state will automatically clear any other special states that may be turned on so you can only
|
||
|
//have one at a time.
|
||
|
void setSpecialModelConditionState( ModelConditionFlagType set, UnsignedInt frames = 0 );
|
||
|
void clearSpecialModelConditionStates();
|
||
|
|
||
|
// set and/or clear multiple modelcondition flags
|
||
|
void clearModelConditionFlags( const ModelConditionFlags& clr );
|
||
|
void setModelConditionFlags( const ModelConditionFlags& set );
|
||
|
void clearAndSetModelConditionFlags( const ModelConditionFlags& clr, const ModelConditionFlags& set );
|
||
|
|
||
|
void setWeaponSetFlag(WeaponSetType wst);
|
||
|
void clearWeaponSetFlag(WeaponSetType wst);
|
||
|
inline Bool testWeaponSetFlag(WeaponSetType wst) const { return m_curWeaponSetFlags.test(wst); }
|
||
|
inline const WeaponSetFlags& getWeaponSetFlags() const { return m_curWeaponSetFlags; }
|
||
|
Bool setWeaponLock( WeaponSlotType weaponSlot, WeaponLockType lockType ){ return m_weaponSet.setWeaponLock( weaponSlot, lockType ); }
|
||
|
void releaseWeaponLock(WeaponLockType lockType){ m_weaponSet.releaseWeaponLock(lockType); }
|
||
|
Bool isCurWeaponLocked() const { return m_weaponSet.isCurWeaponLocked(); }
|
||
|
|
||
|
/// return true if the template has the specified special power flag set
|
||
|
// @todo: inline
|
||
|
Bool hasSpecialPower( SpecialPowerType type ) const;
|
||
|
|
||
|
void setWeaponBonusCondition(WeaponBonusConditionType wst) { m_weaponBonusCondition |= (1 << wst); }
|
||
|
void clearWeaponBonusCondition(WeaponBonusConditionType wst) { m_weaponBonusCondition &= ~(1 << wst); }
|
||
|
// note, the !=0 at the end is important, to convert this into a boolean type! (srj)
|
||
|
Bool testWeaponBonusCondition(WeaponBonusConditionType wst) const { return (m_weaponBonusCondition & (1 << wst)) != 0; }
|
||
|
inline WeaponBonusConditionFlags getWeaponBonusCondition() const { return m_weaponBonusCondition; }
|
||
|
|
||
|
Bool getSingleLogicalBonePosition(const char* boneName, Coord3D* position, Matrix3D* transform) const;
|
||
|
Bool getSingleLogicalBonePositionOnTurret(WhichTurretType whichTurret, const char* boneName, Coord3D* position, Matrix3D* transform) const;
|
||
|
Int getMultiLogicalBonePosition(const char* boneNamePrefix, Int maxBones, Coord3D* positions, Matrix3D* transforms, Bool convertToWorld = TRUE ) const;
|
||
|
|
||
|
// Entered & exited.
|
||
|
Bool didEnter(const PolygonTrigger *pTrigger) const;
|
||
|
Bool didExit(const PolygonTrigger *pTrigger) const;
|
||
|
Bool isInside(const PolygonTrigger *pTrigger) const;
|
||
|
|
||
|
// exiting of any kind
|
||
|
ExitInterface *getObjectExitInterface() const; ///< get exit interface is present
|
||
|
Bool hasExitInterface() const { return getObjectExitInterface() != 0; }
|
||
|
|
||
|
ObjectShroudStatus getShroudedStatus(Int playerIndex) const;
|
||
|
|
||
|
DisabledMaskType getDisabledFlags() const { return m_disabledMask; }
|
||
|
Bool isDisabled() const { return m_disabledMask.any(); }
|
||
|
Bool clearDisabled( DisabledType type );
|
||
|
|
||
|
void setDisabled( DisabledType type );
|
||
|
void setDisabledUntil( DisabledType type, UnsignedInt frame );
|
||
|
Bool isDisabledByType( DisabledType type ) const { return TEST_DISABLEDMASK( m_disabledMask, type ); }
|
||
|
|
||
|
void pauseAllSpecialPowers( const Bool disabling ) const;
|
||
|
|
||
|
//Checks any timers and clears disabled statii that have expired.
|
||
|
void checkDisabledStatus();
|
||
|
|
||
|
//When an AIAttackState is over, it needs to clean up any weapons that might be in leech range mode
|
||
|
//or else those weapons will have unlimited range!
|
||
|
void clearLeechRangeModeForAllWeapons();
|
||
|
|
||
|
Int getNumConsecutiveShotsFiredAtTarget( const Object *victim) const;
|
||
|
|
||
|
void setHealthBoxOffset( const Coord3D& offset ) { m_healthBoxOffset = offset; } ///< for special amorphous like angry mob
|
||
|
|
||
|
void defect( Team *newTeam, UnsignedInt detectionTime );
|
||
|
void goInvulnerable( UnsignedInt time );
|
||
|
|
||
|
// This is public, since there is no Thing level master setting of Turret stuff. It is all done in a sleepy hamlet
|
||
|
// of a module called TurretAI.
|
||
|
virtual void reactToTurretChange( WhichTurretType turret, Real oldRotation, Real oldPitch );
|
||
|
|
||
|
// Convenience function for checking certain kindof bits
|
||
|
Bool isStructure(void) const;
|
||
|
|
||
|
// Convenience function for checking certain kindof bits
|
||
|
Bool isFactionStructure(void) const;
|
||
|
|
||
|
// Convenience function for checking certain kindof bits
|
||
|
Bool isNonFactionStructure(void) const;
|
||
|
|
||
|
Bool getReceivingDifficultyBonus() const { return m_isReceivingDifficultyBonus; }
|
||
|
void setReceivingDifficultyBonus(Bool receive);
|
||
|
|
||
|
inline UnsignedInt getSafeOcclusionFrame(void) { return m_safeOcclusionFrame; } //< this is an object specific frame at which it's safe to enable building occlusion.
|
||
|
inline void setSafeOcclusionFrame(UnsignedInt frame) { m_safeOcclusionFrame = frame;}
|
||
|
|
||
|
// All of our cheating for radars and power go here.
|
||
|
// This is the function that we now call in becomingTeamMember to adjust our power.
|
||
|
// If incoming is true, we're working on the incoming player, if its false, we're on the outgoing
|
||
|
// player. These are friend_s for player.
|
||
|
void friend_adjustPowerForPlayer( Bool incoming );
|
||
|
|
||
|
Bool isHero() const;
|
||
|
|
||
|
protected:
|
||
|
|
||
|
void setOrRestoreTeam( Team* team, Bool restoring );
|
||
|
|
||
|
void onDisabledEdge(Bool becomingDisabled);
|
||
|
// All of our cheating for radars and power go here.
|
||
|
|
||
|
|
||
|
// snapshot methods
|
||
|
void crc( Xfer *xfer );
|
||
|
void xfer( Xfer *xfer );
|
||
|
void loadPostProcess();
|
||
|
|
||
|
void handleShroud();
|
||
|
void handleValueMap();
|
||
|
void handleThreatMap();
|
||
|
|
||
|
// NOTE NOTE NOTE -- this is a private method. Do Not Ever Make It Public.
|
||
|
// If you think you need to make it public, you are wrong. Don't do it.
|
||
|
// It will go away someday. Yeah, right. Just like GlobalData.
|
||
|
Module* findModule(NameKeyType key) const;
|
||
|
|
||
|
Bool didEnterOrExit() const;
|
||
|
|
||
|
void setID( ObjectID id );
|
||
|
virtual Object *asObjectMeth() { return this; }
|
||
|
virtual const Object *asObjectMeth() const { return this; }
|
||
|
|
||
|
virtual Real calculateHeightAboveTerrain(void) const; // Calculates the actual height above terrain. Doesn't use cache.
|
||
|
|
||
|
void updateTriggerAreaFlags(void);
|
||
|
void setTriggerAreaFlagsForChangeInPosition(void);
|
||
|
|
||
|
/// Look and unlook are protected. They should be called from Object::reasonToLook. Like Capture, or death.
|
||
|
void look();
|
||
|
void unlook();
|
||
|
void shroud();
|
||
|
void unshroud();
|
||
|
|
||
|
/// value and threat functions are protected, and should only be called from handleValueMap
|
||
|
void addValue();
|
||
|
void removeValue();
|
||
|
|
||
|
void addThreat();
|
||
|
void removeThreat();
|
||
|
|
||
|
virtual void reactToTransformChange(const Matrix3D* oldMtx, const Coord3D* oldPos, Real oldAngle);
|
||
|
|
||
|
private:
|
||
|
|
||
|
// yes, private. No, really. Private. Don't expose.
|
||
|
enum ObjectPrivateStatusBits
|
||
|
{
|
||
|
EFFECTIVELY_DEAD = (1 << 0), ///< Object is effectively dead
|
||
|
UNDETECTED_DEFECTOR = (1 << 1), ///< set to true when I defect from my team; set to false when I attack anything or when time runs out
|
||
|
CAPTURED = (1 << 2), ///< set to true if I've been captured, otherwise, its false. (Note: Never becomes false once it's true)
|
||
|
OFF_MAP = (1 << 3) ///< set to true if I am known to be OFF the current map.
|
||
|
// NOTE: Object currently only uses a Byte for this, so if you add status bits, you may need to enlarge that field.
|
||
|
};
|
||
|
|
||
|
ObjectID m_id; ///< this object's unique ID
|
||
|
ObjectID m_producerID; ///< object that produced us, if any
|
||
|
ObjectID m_builderID; ///< object that is building or has built us (dozers or workers are builders)
|
||
|
Drawable* m_drawable; ///< drawable (if any) for this object
|
||
|
AsciiString m_name; ///< internal name
|
||
|
|
||
|
Object * m_next;
|
||
|
Object * m_prev;
|
||
|
UnsignedInt m_status; ///< status bits (see ObjectStatusBits enum)
|
||
|
|
||
|
GeometryInfo m_geometryInfo;
|
||
|
|
||
|
AIGroup* m_group; ///< if non-NULL, we are part of this group of agents
|
||
|
|
||
|
// These will last for my lifetime. I will reuse them and reset them. The truly dynamic ones are in PartitionManager
|
||
|
SightingInfo *m_partitionLastLook; ///< Where and for whom I last looked, so I can undo its effects when I stop
|
||
|
SightingInfo *m_partitionLastShroud; ///< Where and for whom I last shrouded, so I can undo its effects when I stop
|
||
|
SightingInfo *m_partitionLastThreat; ///< Where and for whom I last delt with threat, so I can undo its effects when I stop
|
||
|
SightingInfo *m_partitionLastValue; ///< Where and for whom I last delt with value, so I can undo its effects when I stop
|
||
|
|
||
|
Real m_visionRange; ///< looking range
|
||
|
Real m_shroudClearingRange; ///< looking range for shroud ONLY
|
||
|
Real m_shroudRange; ///< like looking range, this is how far I shroud others' looks
|
||
|
|
||
|
DisabledMaskType m_disabledMask;
|
||
|
UnsignedInt m_disabledTillFrame[ DISABLED_COUNT ];
|
||
|
|
||
|
UnsignedInt m_smcUntil;
|
||
|
|
||
|
enum { NUM_SLEEP_HELPERS = 5 };
|
||
|
ObjectRepulsorHelper* m_repulsorHelper;
|
||
|
ObjectSMCHelper* m_smcHelper;
|
||
|
ObjectWeaponStatusHelper* m_wsHelper;
|
||
|
ObjectDefectionHelper* m_defectionHelper;
|
||
|
FiringTracker* m_firingTracker; ///< Tracker is really a "helper" and is included NUM_SLEEP_HELPERS
|
||
|
|
||
|
// modules
|
||
|
BehaviorModule** m_behaviors; // BehaviorModule, not BehaviorModuleInterface
|
||
|
|
||
|
// cache these, for convenience
|
||
|
ContainModuleInterface* m_contain;
|
||
|
BodyModuleInterface* m_body;
|
||
|
|
||
|
AIUpdateInterface* m_ai; ///< ai interface (if any), cached for handy access. (duplicate of entry in the module array!)
|
||
|
PhysicsBehavior* m_physics; ///< physics interface (if any), cached for handy access. (duplicate of entry in the module array!)
|
||
|
|
||
|
PartitionData* m_partitionData; ///< our PartitionData
|
||
|
RadarObject* m_radarData; ///< radar data
|
||
|
ExperienceTracker* m_experienceTracker; ///< Manages experience, gaining levels, and value when killed
|
||
|
|
||
|
Object* m_containedBy; /**< an object can only be contained by at most one
|
||
|
other object, this is that object (if present) */
|
||
|
ObjectID m_xferContainedByID; ///< xfer uses IDs to store pointers and looks them up after
|
||
|
UnsignedInt m_containedByFrame; ///< frame we were contained by m_containedBy
|
||
|
|
||
|
Real m_constructionPercent; ///< for objects being built ... this is the amount completed (0.0 to 100.0)
|
||
|
Int64 m_objectUpgradesCompleted; ///< Bit field of upgrades locally completed.
|
||
|
|
||
|
Team* m_team; ///< team that is current owner of this guy
|
||
|
AsciiString m_originalTeamName; ///< team that was the original ("birth") team of this guy
|
||
|
Color m_indicatorColor; ///< if nonzero, use this instead of controlling player's color
|
||
|
|
||
|
Coord3D m_healthBoxOffset; ///< generally zero, except for special amorphous ones like angry mob
|
||
|
|
||
|
/// @todo srj -- convert to non-DLINK list, after it is once again possible to test the change
|
||
|
MAKE_DLINK(Object, TeamMemberList) ///< other Things that are members of the same team
|
||
|
|
||
|
// Weapons & Damage -------------------------------------------------------------------------------------------------
|
||
|
WeaponSet m_weaponSet;
|
||
|
WeaponSetFlags m_curWeaponSetFlags;
|
||
|
WeaponBonusConditionFlags m_weaponBonusCondition;
|
||
|
Byte m_lastWeaponCondition[WEAPONSLOT_COUNT];
|
||
|
|
||
|
SpecialPowerMaskType m_specialPowerBits; ///< bits determining what kind of special abilities this object has access to.
|
||
|
|
||
|
//////////////////////////////////////< for the non-stacking healers like ambulance and propaganda
|
||
|
ObjectID m_soleHealingBenefactorID; ///< who is the only other object that can give me this non-stacking heal benefit?
|
||
|
UnsignedInt m_soleHealingBenefactorExpirationFrame; ///< on what frame can I accept healing (thus to switch) from a new benefactor
|
||
|
///////////////////////////////////
|
||
|
|
||
|
// Entered & exited housekeeping.
|
||
|
enum { MAX_TRIGGER_AREA_INFOS = 5 };
|
||
|
TTriggerInfo m_triggerInfo[MAX_TRIGGER_AREA_INFOS];
|
||
|
UnsignedInt m_enteredOrExitedFrame;
|
||
|
ICoord3D m_iPos;
|
||
|
|
||
|
PathfindLayerEnum m_layer; // Layer object is pathing on.
|
||
|
PathfindLayerEnum m_destinationLayer; // Layer of current path goal.
|
||
|
|
||
|
// User formations.
|
||
|
FormationID m_formationID;
|
||
|
Coord2D m_formationOffset;
|
||
|
|
||
|
AsciiString m_commandSetStringOverride;///< To allow specific object to switch command sets
|
||
|
|
||
|
UnsignedInt m_safeOcclusionFrame; ///<flag used by occlusion renderer so it knows when objects have exited their production building.
|
||
|
|
||
|
// --------- BYTE-SIZED THINGS GO HERE
|
||
|
Bool m_isSelectable;
|
||
|
Bool m_modulesReady;
|
||
|
#if defined(_DEBUG) || defined(_INTERNAL)
|
||
|
Bool m_hasDiedAlready;
|
||
|
#endif
|
||
|
UnsignedByte m_scriptStatus; ///< status as set by scripting, corresponds to ORed ObjectScriptStatusBits
|
||
|
UnsignedByte m_privateStatus; ///< status bits that are never directly accessible to outside world
|
||
|
Byte m_numTriggerAreasActive;
|
||
|
Bool m_singleUseCommandUsed;
|
||
|
Bool m_isReceivingDifficultyBonus;
|
||
|
|
||
|
}; // end class Object
|
||
|
|
||
|
#ifdef DEBUG_LOGGING
|
||
|
// describe an object as an AsciiString: e.g. "Object 102 (KillerBuggy) [GLARocketBuggy, owned by player 2 (GLAIntroPlayer)]"
|
||
|
AsciiString DescribeObject(const Object *obj);
|
||
|
#endif // DEBUG_LOGGING
|
||
|
|
||
|
#if defined(_DEBUG) || defined(_INTERNAL)
|
||
|
#define DEBUG_OBJECT_ID_EXISTS
|
||
|
#else
|
||
|
#undef DEBUG_OBJECT_ID_EXISTS
|
||
|
#endif
|
||
|
|
||
|
#ifdef DEBUG_OBJECT_ID_EXISTS
|
||
|
extern ObjectID TheObjectIDToDebug;
|
||
|
#endif
|
||
|
|
||
|
#endif // _OBJECT_H_
|