/*
** Command & Conquer Generals Zero Hour(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 .
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: OpenContain.h ////////////////////////////////////////////////////////////////////////////
// Author: Colin Day, November 2001
// Desc: The OpenContainer ContainModule allows objects to be contained inside of other
// objects. There is a set of functionality that will be common to
// all container modules that provides the actual containment
// implementations, those implementations are found here
///////////////////////////////////////////////////////////////////////////////////////////////////
#pragma once
#ifndef __OPENCONTAIN_H_
#define __OPENCONTAIN_H_
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "GameLogic/Module/BehaviorModule.h"
#include "GameLogic/Module/CollideModule.h"
#include "GameLogic/Module/ContainModule.h"
#include "GameLogic/Module/UpdateModule.h"
#include "GameLogic/Module/DieModule.h"
#include "GameLogic/Module/DamageModule.h"
#include "Common/AudioEventRTS.h"
#include "Common/KindOf.h"
#include "Common/GameMemory.h"
#include "Common/ModelState.h"
// ------------------------------------------------------------------------------------------------
enum { CONTAIN_MAX_UNKNOWN = -1 }; // means we don't care, infinite, unassigned, whatever
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
class OpenContainModuleData : public UpdateModuleData
{
public:
DieMuxData m_dieMuxData;
Int m_containMax; ///< how many things we can have inside (-1 = "I don't care")
AudioEventRTS m_enterSound; ///< sound to play on entering
AudioEventRTS m_exitSound; ///< sound to play on exiting
Bool m_passengersAllowedToFire; ///< Can the passengers shoot out of us?
Bool m_passengersInTurret; ///< The Firepoint bones are in our turret, not our chassis
Int m_numberOfExitPaths; ///< Will alternate through ExitStart/End paths as we exit people.
Real m_damagePercentageToUnits;
Bool m_isBurnedDeathToUnits; ///< Turn off the hardcoded burn death when killing guys in transport
UnsignedInt m_doorOpenTime;
KindOfMaskType m_allowInsideKindOf; ///< objects must have at least one of these kind of bits set to be contained by us
KindOfMaskType m_forbidInsideKindOf; ///< objects must have NONE of these kind of bits set to be contained by us
Bool m_weaponBonusPassedToPassengers; ///< Do our passengers get to use our weapon bonuses?
Bool m_allowAlliesInside; ///< allow allies inside us
Bool m_allowEnemiesInside; ///< allow enemies inside us
Bool m_allowNeutralInside; ///< allow neutral inside us
OpenContainModuleData( void );
static void buildFieldParse(MultiIniFieldParse& p);
};
//-------------------------------------------------------------------------------------------------
/** An open container can actually contain other objects */
//-------------------------------------------------------------------------------------------------
class OpenContain : public UpdateModule,
public ContainModuleInterface,
public CollideModuleInterface,
public DieModuleInterface,
public DamageModuleInterface,
public ExitInterface
{
MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( OpenContain, "OpenContain" )
MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA( OpenContain, OpenContainModuleData )
public:
OpenContain( Thing *thing, const ModuleData* moduleData );
// virtual destructor prototype provided by memory pool declaration
virtual ContainModuleInterface* getContain() { return this; }
virtual CollideModuleInterface* getCollide() { return this; }
virtual DieModuleInterface* getDie() { return this; }
virtual DamageModuleInterface* getDamage() { return this; }
static Int getInterfaceMask() { return UpdateModule::getInterfaceMask() | (MODULEINTERFACE_CONTAIN) | (MODULEINTERFACE_COLLIDE) | (MODULEINTERFACE_DIE) | (MODULEINTERFACE_DAMAGE); }
virtual void onDie( const DamageInfo *damageInfo ); ///< the die callback
virtual void onDelete( void ); ///< Last possible moment cleanup
virtual void onCapture( Player *oldOwner, Player *newOwner ){}
// CollideModuleInterface
virtual void onCollide( Object *other, const Coord3D *loc, const Coord3D *normal );
virtual Bool wouldLikeToCollideWith(const Object* other) const { return false; }
virtual Bool isCarBombCrateCollide() const { return false; }
virtual Bool isHijackedVehicleCrateCollide() const { return false; }
virtual Bool isRailroad() const { return false;}
virtual Bool isSalvageCrateCollide() const { return false; }
virtual Bool isSabotageBuildingCrateCollide() const { return FALSE; }
// UpdateModule
virtual UpdateSleepTime update(); ///< called once per frame
// ContainModuleInterface
virtual OpenContain *asOpenContain() { return this; } ///< treat as open container
// DamageModuleInterface
virtual void onDamage( DamageInfo *damageInfo ){}; ///< damage callback
virtual void onHealing( DamageInfo *damageInfo ){}; ///< healing callback
virtual void onBodyDamageStateChange( const DamageInfo* damageInfo,
BodyDamageType oldState,
BodyDamageType newState){}; ///< state change callback
// our object changed position... react as appropriate.
virtual void containReactToTransformChange();
virtual Bool calcBestGarrisonPosition( Coord3D *sourcePos, const Coord3D *targetPos ) { return FALSE; }
virtual Bool attemptBestFirePointPosition( Object *source, Weapon *weapon, Object *victim ) { return FALSE; }
virtual Bool attemptBestFirePointPosition( Object *source, Weapon *weapon, const Coord3D *targetPos ) { return FALSE; }
///< if my object gets selected, then my visible passengers should, too
///< this gets called from
virtual void clientVisibleContainedFlashAsSelected() {};
virtual const Player* getApparentControllingPlayer(const Player* observingPlayer) const { return NULL; }
virtual void recalcApparentControllingPlayer() { }
virtual void onContaining( Object *obj, Bool wasSelected ); ///< object now contains 'obj'
virtual void onRemoving( Object *obj ); ///< object no longer contains 'obj'
virtual void onSelling();///< Container is being sold. Open responds by kicking people out
virtual void orderAllPassengersToExit( CommandSourceType commandSource, Bool instantly ); ///< All of the smarts of exiting are in the passenger's AIExit. removeAllFrommContain is a last ditch system call, this is the game Evacuate
virtual void orderAllPassengersToIdle( CommandSourceType commandSource ); ///< Just like it sounds
virtual void orderAllPassengersToHackInternet( CommandSourceType ); ///< Just like it sounds
virtual void markAllPassengersDetected(); ///< Cool game stuff got added to the system calls since this layer didn't exist, so this regains that functionality
// default OpenContain has unlimited capacity...!
virtual Bool isValidContainerFor(const Object* obj, Bool checkCapacity) const;
virtual void addToContain( Object *obj ); ///< add 'obj' to contain list
virtual void addToContainList( Object *obj ); ///< The part of AddToContain that inheritors can override (Can't do whole thing because of all the private stuff involved)
virtual void removeFromContain( Object *obj, Bool exposeStealthUnits = FALSE ); ///< remove 'obj' from contain list
virtual void removeAllContained( Bool exposeStealthUnits = FALSE ); ///< remove all objects on contain list
virtual void killAllContained( void ); ///< kill all objects on contain list
virtual void harmAndForceExitAllContained( DamageInfo *info ); // apply canned damage against those containes
virtual Bool isEnclosingContainerFor( const Object *obj ) const; ///< Does this type of Contain Visibly enclose its contents?
virtual Bool isPassengerAllowedToFire( ObjectID id = INVALID_ID ) const; ///< Hey, can I shoot out of this container?
virtual void setPassengerAllowedToFire( Bool permission = TRUE ) { m_passengerAllowedToFire = permission; } ///< Hey, can I shoot out of this container?
virtual void setOverrideDestination( const Coord3D * ){} ///< Instead of falling peacefully towards a clear spot, I will now aim here
virtual Bool isDisplayedOnControlBar() const {return FALSE;}///< Does this container display its contents on the ControlBar?
virtual Int getExtraSlotsInUse( void ) { return 0; }
virtual Bool isKickOutOnCapture(){ return TRUE; }///< By default, yes, all contain modules kick passengers out on capture
// contain list access
virtual void iterateContained( ContainIterateFunc func, void *userData, Bool reverse );
virtual UnsignedInt getContainCount() const { return m_containListSize; }
virtual const ContainedItemsList* getContainedItemsList() const { return &m_containList; }
virtual const Object *friend_getRider() const{return NULL;} ///< Damn. The draw order dependency bug for riders means that our draw module needs to cheat to get around it.
virtual Real getContainedItemsMass() const;
virtual UnsignedInt getStealthUnitsContained() const { return m_stealthUnitsContained; }
virtual PlayerMaskType getPlayerWhoEntered(void) const { return m_playerEnteredMask; }
virtual Int getContainMax() const;
// ExitInterface
virtual Bool isExitBusy() const {return FALSE;} ///< Contain style exiters are getting the ability to space out exits, so ask this before reserveDoor as a kind of no-commitment check.
virtual ExitDoorType reserveDoorForExit( const ThingTemplate* objType, Object *specificObject ) { return DOOR_1; }
virtual void exitObjectViaDoor( Object *newObj, ExitDoorType exitDoor );
virtual void exitObjectInAHurry( Object *newObj );
virtual void unreserveDoorForExit( ExitDoorType exitDoor ) { /*nothing*/ }
virtual void exitObjectByBudding( Object *newObj, Object *budHost ) { return; };
virtual void setRallyPoint( const Coord3D *pos ); ///< define a "rally point" for units to move towards
virtual const Coord3D *getRallyPoint( void ) const; ///< define a "rally point" for units to move towards
virtual Bool getExitPosition(Coord3D& exitPosition ) const { return FALSE; }; ///< access to the "Door" position of the production object
virtual Bool getNaturalRallyPoint( Coord3D& rallyPoint, Bool offset = TRUE ) const; ///< get the natural "rally point" for units to move towards
virtual ExitInterface* getContainExitInterface() { return this; }
virtual Bool isGarrisonable() const { return false; } ///< can this unit be Garrisoned? (ick)
virtual Bool isBustable() const { return false; } ///< can this container get busted by a bunkerbuster
virtual Bool isHealContain() const { return false; } ///< true when container only contains units while healing (not a transport!)
virtual Bool isTunnelContain() const { return FALSE; }
virtual Bool isRiderChangeContain() const { return FALSE; }
virtual Bool isSpecialZeroSlotContainer() const { return false; }
virtual Bool isImmuneToClearBuildingAttacks() const { return true; }
virtual Bool isSpecialOverlordStyleContainer() const { return false; }
virtual Bool isAnyRiderAttacking( void ) const;
/**
this is used for containers that must do something to allow people to enter or exit...
eg, land (for Chinook), open door (whatever)... it's called with wants=WANTS_TO_ENTER
when something is in the enter state, and wants=ENTS_NOTHING when the unit has
either entered, or given up...
*/
virtual void onObjectWantsToEnterOrExit(Object* obj, ObjectEnterExitType wants);
// returns true iff there are objects currently waiting to enter.
virtual Bool hasObjectsWantingToEnterOrExit() const;
virtual void processDamageToContained(Real percentDamage); ///< Do our % damage to units now.
virtual Bool isWeaponBonusPassedToPassengers() const;
virtual WeaponBonusConditionFlags getWeaponBonusPassedToPassengers() const;
virtual void enableLoadSounds( Bool enable ) { m_loadSoundsEnabled = enable; }
Real getDamagePercentageToUnits( void );
virtual Object* getClosestRider ( const Coord3D *pos );
virtual void setEvacDisposition( EvacDisposition disp ) {};
protected:
virtual void monitorConditionChanges( void ); ///< check to see if we need to update our occupant postions from a model change or anything else
virtual void putObjAtNextFirePoint( Object *obj ); ///< place object at position of the next fire point to use
virtual void redeployOccupants( void ); ///< redeploy any objects at firepoints due to a model condition change
const ContainedItemsList& getContainList() const { return m_containList; }
void scatterToNearbyPosition(Object* obj);
void removeFromContainViaIterator( ContainedItemsList::iterator it, Bool exposeStealthUnits = FALSE ); ///< remove item from contain list
void removeFromPassengerViaIterator( ContainedItemsList::iterator it );///< remove item from passenger list
virtual void doLoadSound();
virtual void doUnloadSound();
virtual void positionContainedObjectsRelativeToContainer(){}
virtual void addOrRemoveObjFromWorld(Object* obj, Bool add);
// exists primarily for TransportContain to override
virtual void killRidersWhoAreNotFreeToExit() { }
void pruneDeadWanters();
ContainedItemsList m_containList; ///< the list of contained objects
UnsignedInt m_containListSize; ///< size of contained list
private:
typedef std::map< ObjectID, ObjectEnterExitType, std::less > ObjectEnterExitMap;
ObjectEnterExitMap m_objectEnterExitInfo;
UnsignedInt m_stealthUnitsContained; ///< number of stealth units that can't be seen by enemy players.
Int m_whichExitPath; ///< Cycles from 1 to n and is used only in modules whose data has numberOfExitPaths > 1.
UnsignedInt m_doorCloseCountdown; ///< When should I shut my door.
std::list m_xferContainIDList; ///< for loading m_containList from a save game
PlayerMaskType m_playerEnteredMask; ///< Mask of player that entered last, if any.
UnsignedInt m_lastUnloadSoundFrame; ///< last frame we did an un-loading sound
UnsignedInt m_lastLoadSoundFrame; ///< last frame we did a loading sound
/// @todo srj -- move this to a lazily-allocated subobject
enum { MAX_FIRE_POINTS = 32 };
ModelConditionFlags m_conditionState; ///< The Drawables current behavior state
Matrix3D m_firePoints[ MAX_FIRE_POINTS ];
Int m_firePointStart; ///< start firepoint index to use when building becomes occupied
Int m_firePointNext; ///< next index to place objects at
Int m_firePointSize; ///< how many entries in m_firePoint are valid
Bool m_noFirePointsInArt; ///< TRUE when no fire point bones exist in the art
Coord3D m_rallyPoint; ///< Where units should move to after they have reached the "natural" rally point
Bool m_rallyPointExists; ///< Only move to the rally point if this is true
Bool m_loadSoundsEnabled; ///< Don't serialize -- used for disabling sounds during payload creation.
Bool m_passengerAllowedToFire; ///< Newly promoted from the template data to the module for upgrade overriding access
};
#endif // end __OPENCONTAIN_H_