/* ** 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: BridgeBehavior.cpp /////////////////////////////////////////////////////////////////////// // Author: Colin Day, July 2002 // Desc: Behavior module for bridges /////////////////////////////////////////////////////////////////////////////////////////////////// // INCLUDES /////////////////////////////////////////////////////////////////////////////////////// #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include "Common/Radar.h" #include "Common/ThingFactory.h" #include "Common/ThingTemplate.h" #include "Common/Xfer.h" #include "GameClient/InGameUI.h" #include "GameClient/FXList.h" #include "GameClient/Line2D.h" #include "GameClient/TerrainRoads.h" #include "GameLogic/AI.h" #include "GameLogic/AIPathfind.h" #include "GameLogic/Object.h" #include "GameLogic/GameLogic.h" #include "GameLogic/ObjectCreationList.h" #include "GameLogic/Module/BodyModule.h" #include "GameLogic/Module/BridgeBehavior.h" #include "GameLogic/Module/BridgeScaffoldBehavior.h" #include "GameLogic/Module/PhysicsUpdate.h" #include "GameLogic/PartitionManager.h" #include "GameLogic/TerrainLogic.h" /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ BridgeBehaviorModuleData::BridgeBehaviorModuleData( void ) { m_lateralScaffoldSpeed = 1.0f; m_verticalScaffoldSpeed = 1.0f; } // end BridgeBehaviorModuleData // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ BridgeBehaviorModuleData::~BridgeBehaviorModuleData( void ) { // clear the fx list m_fx.clear(); // clear the ocl list m_ocl.clear(); } // end ~BridgeBehaviorModuleData // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ /*static*/ void BridgeBehaviorModuleData::buildFieldParse( MultiIniFieldParse &p ) { BehaviorModuleData::buildFieldParse( p ); static const FieldParse dataFieldParse[] = { { "LateralScaffoldSpeed", INI::parseVelocityReal, NULL, offsetof( BridgeBehaviorModuleData, m_lateralScaffoldSpeed ) }, { "VerticalScaffoldSpeed", INI::parseVelocityReal, NULL, offsetof( BridgeBehaviorModuleData, m_verticalScaffoldSpeed ) }, { "BridgeDieFX", parseFX, NULL, offsetof( BridgeBehaviorModuleData, m_fx ) }, { "BridgeDieOCL", parseOCL, NULL, offsetof( BridgeBehaviorModuleData, m_ocl ) }, { 0, 0, 0, 0 } }; p.add( dataFieldParse ); } // end buildFieldParse // ------------------------------------------------------------------------------------------------ /** Parse time and location info in the form of: * Delay:#### */ // ------------------------------------------------------------------------------------------------ static void parseTimeAndLocationInfo( INI *ini, void *instance, TimeAndLocationInfo *timeAndLocationInfo ) { // delay label const char *token = ini->getNextToken( ini->getSepsColon() ); if( stricmp( token, "Delay" ) != 0 ) { DEBUG_CRASH(( "Expected 'Delay' token, found '%s'\n", token )); throw INI_INVALID_DATA; } // end if // delay value ini->parseDurationUnsignedInt( ini, instance, &timeAndLocationInfo->delay, NULL ); // get optional bone label token = ini->getNextTokenOrNull( ini->getSepsColon() ); if( token ) { // token must be a label for bone location if( stricmp( token, "Bone" ) != 0 ) { DEBUG_CRASH(( "Expected 'Bone' token, found '%s'\n", token )); throw INI_INVALID_DATA; } // end if // read bone name and store timeAndLocationInfo->boneName = ini->getNextAsciiString(); } // end if } // end parseTimeAndLocationInfo //------------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------------ /*static*/ void BridgeBehaviorModuleData::parseFX( INI *ini, void *instance, void *store, const void *userData ) { const char *token; // create item we will read into and push on list BridgeFXInfo item; item.fx = NULL; // get list to store at BridgeFXList *bridgeFXList = (BridgeFXList *)store; // fx list label token = ini->getNextToken( ini->getSepsColon() ); if( stricmp( token, "FX" ) != 0 ) { DEBUG_CRASH(( "Expected 'FX' token, found '%s'\n", token )); throw INI_INVALID_DATA; } // end if // fx list name and store as pointer FXList *fx; ini->parseFXList( ini, instance, &fx, NULL ); // store fx list to item item.fx = fx; // parse the timing and location info parseTimeAndLocationInfo( ini, instance, &item.timeAndLocationInfo ); // put on list bridgeFXList->push_back( item ); } // end parseFX // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ /*static*/ void BridgeBehaviorModuleData::parseOCL( INI *ini, void *instance, void *store, const void *userData ) { const char *token; // create item we will read into and push on list BridgeOCLInfo item; item.ocl = NULL; // get list to store at BridgeOCLList *bridgeOCLList = (BridgeOCLList *)store; // fx list label token = ini->getNextToken( ini->getSepsColon() ); if( stricmp( token, "OCL" ) != 0 ) { DEBUG_CRASH(( "Expected 'OCL' token, found '%s'\n", token )); throw INI_INVALID_DATA; } // end if // fx list name and store as pointer ObjectCreationList *ocl; ini->parseObjectCreationList( ini, instance, &ocl, NULL ); // store ocl list to item item.ocl = ocl; // parse the timing and location info parseTimeAndLocationInfo( ini, instance, &item.timeAndLocationInfo ); // put on list bridgeOCLList->push_back( item ); } // end parseOCL /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ BridgeBehavior::BridgeBehavior( Thing *thing, const ModuleData *moduleData ) : UpdateModule( thing, moduleData ) { Int i; m_scaffoldObjectIDList.clear(); m_scaffoldPresent = FALSE; for( i = 0; i < BRIDGE_MAX_TOWERS; ++i ) m_towerID[ i ] = INVALID_ID; m_fxResolved = FALSE; for( Int bodyState = BODY_PRISTINE; bodyState < BODYDAMAGETYPE_COUNT; ++bodyState ) { // initialize the fx and ocl lists for( i = 0; i < MAX_BRIDGE_BODY_FX; ++i ) { m_damageToOCL[ bodyState ][ i ] = NULL; m_damageToFX[ bodyState ][ i ] = NULL; m_repairToOCL[ bodyState ][ i ] = NULL; m_repairToFX[ bodyState ][ i ] = NULL; } // end for i // these don't need initialization, they have constructors // m_damageToSound[ bodyState ] = ??? // m_repairToSound[ bodyState ] = ??? } // end for, bodyState m_deathFrame = 0; } // end BridgeBehavior // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ BridgeBehavior::~BridgeBehavior( void ) { // // destroy our four towers if they are still present in the world (they wouldn't be if we // are resetting the engine, and those towers were earlier in the destruction list than // we were) // for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i ) { Object *tower; // get the tower and destroy tower = TheGameLogic->findObjectByID( getTowerID( (BridgeTowerType)i ) ); if( tower ) TheGameLogic->destroyObject( tower ); } // end for i } // end ~BridgeBehavior // ------------------------------------------------------------------------------------------------ /** Get bridge behavior interface */ // ------------------------------------------------------------------------------------------------ /*static */BridgeBehaviorInterface *BridgeBehavior::getBridgeBehaviorInterfaceFromObject( Object *obj ) { // sanity if( obj == NULL ) return NULL; BehaviorModule **bmi; BridgeBehaviorInterface *bbi = NULL; for( bmi = obj->getBehaviorModules(); *bmi; ++bmi ) { bbi = (*bmi)->getBridgeBehaviorInterface(); if( bbi ) return bbi; } // end for, bmi // interface not found return NULL; } // end getBridgeBehaviorInterfaceFromObject // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ void BridgeBehavior::onDelete( void ) { // clear the list of scaffold objects m_scaffoldObjectIDList.clear(); } // end onDelete // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ void BridgeBehavior::resolveFX( void ) { Object *us = getObject(); Bridge *bridge = TheTerrainLogic->findBridgeAt( us->getPosition() ); // sanity if( bridge == NULL ) return; // get the bridge template name AsciiString bridgeTemplateName = bridge->getBridgeTemplateName(); // find the bridge template TerrainRoadType *bridgeTemplate = TheTerrainRoads->findBridge( bridgeTemplateName ); // sanity if( bridgeTemplate == NULL ) return; AsciiString name; for( Int bodyState = BODY_PRISTINE; bodyState < BODYDAMAGETYPE_COUNT; ++bodyState ) { // initialize the fx and ocl lists for( Int i = 0; i < MAX_BRIDGE_BODY_FX; ++i ) { name = bridgeTemplate->getDamageToOCLString( (BodyDamageType)bodyState, i ); m_damageToOCL[ bodyState ][ i ] = TheObjectCreationListStore->findObjectCreationList( name.str() ); if( name.isEmpty() == FALSE && m_damageToOCL[ bodyState ][ i ] == NULL ) DEBUG_CRASH(( "OCL list '%s' not found\n", name.str() )); name = bridgeTemplate->getDamageToFXString( (BodyDamageType)bodyState, i ); m_damageToFX[ bodyState ][ i ] = TheFXListStore->findFXList( name.str() ); if( name.isEmpty() == FALSE && m_damageToFX[ bodyState ][ i ] == NULL ) DEBUG_CRASH(( "FX list '%s' not found\n", name.str() )); name = bridgeTemplate->getRepairedToOCLString( (BodyDamageType)bodyState, i ); m_repairToOCL[ bodyState ][ i ] = TheObjectCreationListStore->findObjectCreationList( name.str() ); if( name.isEmpty() == FALSE && m_repairToOCL[ bodyState ][ i ] == NULL ) DEBUG_CRASH(( "OCL list '%s' not found\n", name.str() )); name = bridgeTemplate->getRepairedToFXString( (BodyDamageType)bodyState, i ); m_repairToFX[ bodyState ][ i ] = TheFXListStore->findFXList( name.str() );; if( name.isEmpty() == FALSE && m_repairToFX[ bodyState ][ i ] == NULL ) DEBUG_CRASH(( "FX list '%s' not found\n", name.str() )); } // end for i // audio sounds name = bridgeTemplate->getDamageToSoundString( (BodyDamageType)bodyState ); m_damageToSound[ bodyState ].setEventName( name ); m_damageToSound[ bodyState ].setObjectID( us->getID() ); name = bridgeTemplate->getRepairedToSoundString( (BodyDamageType)bodyState ); m_repairToSound[ bodyState ].setEventName( name ); m_repairToSound[ bodyState ].setObjectID( us->getID() ); } // end for, bodyState // fx are now "resolved" m_fxResolved = TRUE; } // end resolveFX // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ void BridgeBehavior::setTower( BridgeTowerType towerType, Object *tower ) { // sanity if( towerType < 0 || towerType >= BRIDGE_MAX_TOWERS ) { DEBUG_CRASH(( "BridgeBehavior::setTower - Invalid tower type index '%d'\n", towerType )); return; } // end if // store it if( tower ) m_towerID[ towerType ] = tower->getID(); else m_towerID[ towerType ] = INVALID_ID; } // end setTower // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ ObjectID BridgeBehavior::getTowerID( BridgeTowerType towerType ) { // sanity if( towerType < 0 || towerType >= BRIDGE_MAX_TOWERS ) { DEBUG_CRASH(( "BridgeBehavior::setTower - Invalid tower type index '%d'\n", towerType )); return INVALID_ID; } // end if // return the stored ID return m_towerID[ towerType ]; } // end getTowerID // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ void BridgeBehavior::onDamage( DamageInfo *damageInfo ) { // // get our body info so we now how much damage percent is being done to us ... we need this // so that we can propagate the same damage percentage amont the towers and the bridge // BodyModuleInterface *body = getObject()->getBodyModule(); Real damagePercentage = damageInfo->in.m_amount / body->getMaxHealth(); // // if the damage didn't come from a bridge tower, then we must propagate this damage // to all our towers // Object *source = TheGameLogic->findObjectByID( damageInfo->in.m_sourceID ); if( source == NULL || source->isKindOf( KINDOF_BRIDGE_TOWER ) == FALSE ) { Object *tower; for( Int i = 0; i < BRIDGE_MAX_TOWERS; i++ ) { tower = TheGameLogic->findObjectByID( getTowerID( (BridgeTowerType)i ) ); if( tower ) { BodyModuleInterface *towerBody = tower->getBodyModule(); DamageInfo towerDamage; towerDamage.in.m_amount = damagePercentage * towerBody->getMaxHealth(); towerDamage.in.m_sourceID = getObject()->getID(); // we're now the source towerDamage.in.m_damageType = damageInfo->in.m_damageType; towerDamage.in.m_deathType = damageInfo->in.m_deathType; tower->attemptDamage( &towerDamage ); } // end if } // end for i } // end if } // end onDamage // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ void BridgeBehavior::onHealing( DamageInfo *damageInfo ) { // // get our body info so we now how much healing percent is being done to us ... we need this // so that we can propagate the same healing percentage amont the towers and the bridge // BodyModuleInterface *body = getObject()->getBodyModule(); Real healingPercentage = damageInfo->in.m_amount / body->getMaxHealth(); // // if the healing didn't come from a bridge tower, then we must propagate this healing // to all our towers // Object *source = TheGameLogic->findObjectByID( damageInfo->in.m_sourceID ); if( source == NULL || source->isKindOf( KINDOF_BRIDGE_TOWER ) == FALSE ) { Object *tower; for( Int i = 0; i < BRIDGE_MAX_TOWERS; i++ ) { tower = TheGameLogic->findObjectByID( getTowerID( (BridgeTowerType)i ) ); if( tower ) { BodyModuleInterface *towerBody = tower->getBodyModule(); tower->attemptHealing(healingPercentage * towerBody->getMaxHealth(), getObject()); } // end if } // end for i } // end if } // end onHealing // ------------------------------------------------------------------------------------------------ /** Pick a random surface spot on the bridge surface */ // ------------------------------------------------------------------------------------------------ void BridgeBehavior::getRandomSurfacePosition( TerrainRoadType *bridgeTemplate, const BridgeInfo *bridgeInfo, Coord3D *pos ) { // sanity if( bridgeInfo == NULL || pos == NULL ) return; // // pick the spot by finding vectors along the edge of the bridge region, scaling // them and then adding them together // Real scale; Coord3D v1; v1.x = bridgeInfo->toLeft.x - bridgeInfo->fromLeft.x; v1.y = bridgeInfo->toLeft.y - bridgeInfo->fromLeft.y; v1.z = bridgeInfo->toLeft.z - bridgeInfo->fromLeft.z; scale = GameLogicRandomValueReal( 0.0f, 1.0f ); v1.x *= scale; v1.y *= scale; v1.z *= scale; Coord3D v2; v2.x = bridgeInfo->fromRight.x - bridgeInfo->fromLeft.x; v2.y = bridgeInfo->fromRight.y - bridgeInfo->fromLeft.y; v2.z = bridgeInfo->fromRight.z - bridgeInfo->fromLeft.z; scale = GameLogicRandomValueReal( 0.0f, 1.0f ); v2.x *= scale; v2.y *= scale; v2.z *= scale; // set the position pos->x = v1.x + v2.x + bridgeInfo->fromLeft.x; pos->y = v1.y + v2.y + bridgeInfo->fromLeft.y; pos->z = v1.z + v2.z + bridgeInfo->fromLeft.z; // // we now have a position picked, the last thing to do is add in an additional // Z component so that effects can be created in a "cube" area on and above the bridge // pos->z += GameLogicRandomValueReal( 0.0f, bridgeTemplate->getTransitionEffectsHeight() ); } // end getRandomSurfacePosition // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ void BridgeBehavior::doAreaEffects( TerrainRoadType *bridgeTemplate, Bridge *bridge, const ObjectCreationList *ocl, const FXList *fx ) { // sanity if( bridge == NULL ) return; // if no effects, don't bother if( ocl == NULL && fx == NULL ) return; // get bridge info const BridgeInfo *bridgeInfo = bridge->peekBridgeInfo(); // play effects in the bridge area const Int maxEffects = bridgeTemplate->getNumFXPerType(); Coord3D pos; for( Int i = 0; i < maxEffects; ++i ) { // pick spot in the bridge area and do FX if( fx ) { getRandomSurfacePosition( bridgeTemplate, bridgeInfo, &pos ); FXList::doFXPos( fx, &pos ); } // end if // pick spot in the bridge area and do OCL if( ocl ) { getRandomSurfacePosition( bridgeTemplate, bridgeInfo, &pos ); ObjectCreationList::create( ocl, getObject(), &pos, NULL, INVALID_ANGLE ); } // end if } // end for i } // end doAreaEffects // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ void BridgeBehavior::onBodyDamageStateChange( const DamageInfo* damageInfo, BodyDamageType oldState, BodyDamageType newState ) { // // check for coming back from the dead, if our new state is not the rubble state, we can't // possibly be dead // if( newState != BODY_RUBBLE ) m_deathFrame = 0; // first resolve any fx stuff if we need to if( m_fxResolved == FALSE ) resolveFX(); // sanity if( m_fxResolved == FALSE ) return; // sanity DEBUG_ASSERTCRASH( oldState != newState, ("BridgeBehavior::onBodyDamageStateChange - oldState and newState should be different if this is getting called\n") ); Object *us = getObject(); Bridge *bridge = TheTerrainLogic->findBridgeAt( us->getPosition() ); // sanity if( bridge == NULL ) { DEBUG_CRASH(( "BridgeBehavior - Unable to find bridge\n" )); return; } // end if // get the bridge template name AsciiString bridgeTemplateName = bridge->getBridgeTemplateName(); // find the bridge template TerrainRoadType *bridgeTemplate = TheTerrainRoads->findBridge( bridgeTemplateName ); // sanity DEBUG_ASSERTCRASH( bridgeTemplate, ("BridgeBehavior: Unable to find bridge template '%s' in bridge object '%s'\n", bridgeTemplateName, us->getTemplate()->getName().str()) ); // // given the old state and the new state, did we get worse (damaged) or did // we get better (repaired)? // Bool gotRepaired = IS_CONDITION_WORSE( oldState, newState ); // get the effect data AsciiString soundString; AsciiString oclString[ MAX_BRIDGE_BODY_FX ]; AsciiString fxString[ MAX_BRIDGE_BODY_FX ]; if( gotRepaired ) { // play the sound TheAudio->addAudioEvent( &m_repairToSound[ newState ] ); for( Int i = 0; i < MAX_BRIDGE_BODY_FX; i++ ) doAreaEffects( bridgeTemplate, bridge, m_repairToOCL[ newState ][ i ], m_repairToFX[ newState ][ i ] ); } // end if else { // play the sound TheAudio->addAudioEvent( &m_damageToSound[ newState ] ); for( Int i = 0; i < MAX_BRIDGE_BODY_FX; i++ ) doAreaEffects( bridgeTemplate, bridge, m_damageToOCL[ newState ][ i ], m_damageToFX[ newState ][ i ] ); } // end else // update bridge damage states ///@todo this should be re-written, there no need for this looping bridge examination TheTerrainLogic->updateBridgeDamageStates(); // // for the local player, if this bridge has switched from rubble, to usable, or from // usable to rubble, we should reflect the change on the radar. note that we // request that the radar queue a refresh sometime in the future because it keeps // track of how often we makes requests to do a refresh and doesn't do them too // often because it's expensive to refresh the terrain // if( oldState == BODY_RUBBLE || newState == BODY_RUBBLE ) TheRadar->queueTerrainRefresh(); } // end onBodyDamageStateChange // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ UpdateSleepTime BridgeBehavior::update( void ) { // if we're dead, we need to possibly throw off some effects if( m_deathFrame != 0 ) { AsciiString boneName; // get object Object *us = getObject(); // get module data const BridgeBehaviorModuleData *modData = getBridgeBehaviorModuleData(); // get bridge information Bridge *bridge = TheTerrainLogic->findBridgeAt( us->getPosition() ); const BridgeInfo *bridgeInfo = NULL; TerrainRoadType *bridgeTemplate = NULL; if ( bridge ) { DEBUG_ASSERTCRASH( bridge, ("BridgeBehavior::update - Unable to find bridge\n") ); // get bridge info bridgeInfo = bridge->peekBridgeInfo(); // get the bridge template info AsciiString bridgeTemplateName = bridge->getBridgeTemplateName(); bridgeTemplate = TheTerrainRoads->findBridge( bridgeTemplateName ); DEBUG_ASSERTCRASH( bridgeTemplate, ("BridgeBeahvior::getRandomSurfacePosition - Encountered a bridge with no template!\n") ); } // how much time has passed between now and our destruction frame UnsignedInt deathTime = TheGameLogic->getFrame() - m_deathFrame; // see if there are any fx visuals we need to execute BridgeFXList::const_iterator fxIt; for( fxIt = modData->m_fx.begin(); fxIt != modData->m_fx.end(); ++fxIt ) { // // we'll launch an fx list if our death time is equal to exactly the delay // we're waiting for to launch the list // if( deathTime == (*fxIt).timeAndLocationInfo.delay ) { Coord3D pos; // // if a bone name is present, we'll use the bone position, otherwise we'll pick a // spot somewhere on the bridge surface // boneName = (*fxIt).timeAndLocationInfo.boneName; if( boneName.isEmpty() == FALSE ) us->getSingleLogicalBonePosition( boneName.str(), &pos, NULL ); else if ( bridge && bridgeTemplate && bridgeInfo)//we have valid Terrain data for the bridge getRandomSurfacePosition( bridgeTemplate, bridgeInfo, &pos ); else pos.set( getObject()->getPosition() ); // launch the fx list FXList::doFXPos( (*fxIt).fx, &pos ); } // end if } // end for, fxIt // see if there are any ocl visuals we need to execute BridgeOCLList::const_iterator oclIt; for( oclIt = modData->m_ocl.begin(); oclIt != modData->m_ocl.end(); ++oclIt ) { // // we'll launch an ocl list if our death time is equal to exactly the delay // we're waiting for to launch the list // if( deathTime == (*oclIt).timeAndLocationInfo.delay ) { Coord3D pos; // // if a bone name is present, we'll use the bone position, otherwise we'll pick a // spot somewhere on the bridge surface // boneName = (*oclIt).timeAndLocationInfo.boneName; if( boneName.isEmpty() == FALSE ) { // // special case for creating an OCL for using the bridge object parent center location // why do we have this ... well, apparently the OCL ignores the obj parameter // passed in if you give it a position parameter, and we need it to pay attention // to that object parameteter so the OCL stuff can inherit the LIKE_EXISTING // properties from its parent // if( boneName.compare( "ParentObject" ) == 0 ) { // launch the effects just using the parent object for location info ObjectCreationList::create( (*oclIt).ocl, us, NULL ); } // endif else { // get bone position us->getSingleLogicalBonePosition( boneName.str(), &pos, NULL ); // launch the fx list ObjectCreationList::create( (*oclIt).ocl, us, &pos, NULL, INVALID_ANGLE ); } // end else } // end if, bone name not empty else { // get random place on bridge if ( bridge && bridgeTemplate && bridgeInfo )//we have valid Terrain data for the bridge getRandomSurfacePosition( bridgeTemplate, bridgeInfo, &pos ); else pos.set( getObject()->getPosition() ); // launch the fx list ObjectCreationList::create( (*oclIt).ocl, us, &pos, NULL, INVALID_ANGLE ); } // end else } // end if } // end for, oclIt } // end if return UPDATE_SLEEP_NONE; } // end update // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ void BridgeBehavior::onDie( const DamageInfo *damageInfo ) { // kill the towers associated with us Object *tower; for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i ) { tower = TheGameLogic->findObjectByID( getTowerID( (BridgeTowerType)i ) ); if( tower ) tower->kill(); } // end for, i // we need to handle anything that was on top of us now that we've been destroyed handleObjectsOnBridgeOnDie(); // we have now died, record the death frame m_deathFrame = TheGameLogic->getFrame(); } // end onDie // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ void BridgeBehavior::handleObjectsOnBridgeOnDie( void ) { const Object *bridge = getObject(); const Coord3D *bridgePos = bridge->getPosition(); Bridge *terrainBridge = TheTerrainLogic->findBridgeAt( getObject()->getPosition() ); if( terrainBridge ) { PathfindLayerEnum bridgeLayer = terrainBridge->getLayer(); BridgeInfo bridgeInfo; // get the bridge info terrainBridge->getBridgeInfo( &bridgeInfo ); // setup a polygon area using the bridge extents Coord3D bridgePolygon[ 4 ]; bridgePolygon[ 0 ] = bridgeInfo.fromLeft; bridgePolygon[ 1 ] = bridgeInfo.fromRight; bridgePolygon[ 2 ] = bridgeInfo.toRight; bridgePolygon[ 3 ] = bridgeInfo.toLeft; // // find the lowest Z point of the bridge area ... we will use this to figure out of // objects in the bridge area are "on top" of the bridge // Real lowBridgeZ = bridgePolygon[ 0 ].z; for( Int i = 0; i < 4; ++i ) if( bridgePolygon[ i ].z < lowBridgeZ ) lowBridgeZ = bridgePolygon[ i ].z; // // given the polygon area, how big is the radius that we need to scan in the world // to cover from the center of the bridge (the bridge object position) to the edge // of the bridge // Coord2D v; v.x = bridgeInfo.toLeft.x - bridgePos->x; v.y = bridgeInfo.toLeft.y - bridgePos->y; Real radius = v.length(); // scan the objects in the radius ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( bridgePos, radius, FROM_CENTER_2D ); MemoryPoolObjectHolder hold( iter ); Object *other; for( other = iter->first(); other; other = iter->next() ) { // ingnore some kind of objects if( other->isKindOf( KINDOF_BRIDGE ) || other->isKindOf( KINDOF_BRIDGE_TOWER ) ) continue; // ignore airborne objects if( other->isAboveTerrain() ) continue; // ignore objects that were not actually on the bridge if( other->getPosition()->z < lowBridgeZ ) continue; // ignore objects that are not inside the bridge polygon if( PointInsideArea2D( other->getPosition(), bridgePolygon, 4 ) == FALSE ) continue; // if object not on same layer as bridge do nothing if( bridgeLayer != other->getLayer() ) continue; if (other->getLayer() == bridgeLayer) other->setLayer(LAYER_GROUND); // if they have physics, let 'em fall, otherwise just kill 'em PhysicsBehavior* physics = other->getPhysics(); if (physics) physics->setAllowToFall(true); else other->kill(); } // end for, other } // end if, terrainBridge } // end handleObjectsOnBridgeDie // ------------------------------------------------------------------------------------------------ /** Set all the position, angle, and speed data we need to for a single scaffold object */ // ------------------------------------------------------------------------------------------------ void BridgeBehavior::setScaffoldData( Object *obj, Real *angle, Real *sunkenHeight, const Coord3D *riseToPos, const Coord3D *buildPos, const Coord3D *bridgeCenter ) { // sanity if( obj == NULL || angle == NULL || riseToPos == NULL || buildPos == NULL ) return; const BridgeBehaviorModuleData *modData = getBridgeBehaviorModuleData(); // get the scaffold behavior interface BridgeScaffoldBehaviorInterface *scaffoldBehavior; scaffoldBehavior = BridgeScaffoldBehavior::getBridgeScaffoldBehaviorInterfaceFromObject( obj ); DEBUG_ASSERTCRASH( scaffoldBehavior, ("Unable to find bridge scaffold behavior interface\n") ); // compute the sunken position that the object will initially start at Real fudge = 8.0f; Coord3D sunkenPos = *riseToPos; sunkenPos.z = sunkenPos.z - *sunkenHeight - fudge; // set object initial position obj->setPosition( &sunkenPos ); // set all the destination points for all scaffold motion scaffoldBehavior->setPositions( &sunkenPos, riseToPos, buildPos ); // set the scaffold object in motion rising up out of the ground scaffoldBehavior->setMotion( STM_RISE ); // set object angle obj->setOrientation( *angle ); // // set the speed of the scaffold "animation" which is based on how big of a distance // all the scaffold objects have to traverse in order to meet up and be complete in // the center of the bridge in an interesting way // Real lateralSpeed = modData->m_lateralScaffoldSpeed; Coord3D buildUpPosToBridgeCenter, riseToPosToBridgeCenter; buildUpPosToBridgeCenter.x = buildPos->x - riseToPos->x; buildUpPosToBridgeCenter.y = buildPos->y - riseToPos->y; buildUpPosToBridgeCenter.z = buildPos->z - riseToPos->z; riseToPosToBridgeCenter.x = bridgeCenter->x - riseToPos->x; riseToPosToBridgeCenter.y = bridgeCenter->y - riseToPos->y; riseToPosToBridgeCenter.z = bridgeCenter->z - riseToPos->z; Real distBuildUpPosToBridgeCenter = buildUpPosToBridgeCenter.length(); Real distRiseToPosToBridgeCenter = riseToPosToBridgeCenter.length(); scaffoldBehavior->setLateralSpeed( lateralSpeed * (distBuildUpPosToBridgeCenter / distRiseToPosToBridgeCenter) ); // rising speed is always the same for all objects Real verticalSpeed = modData->m_verticalScaffoldSpeed; scaffoldBehavior->setVerticalSpeed( verticalSpeed ); } // end setScaffoldData // ------------------------------------------------------------------------------------------------ /** Start the bridge repair scaffolding. If we already have scaffolding this call * is ignored */ // ------------------------------------------------------------------------------------------------ void BridgeBehavior::createScaffolding( void ) { // if we have scaffolding up already do nothing if( m_scaffoldPresent == TRUE ) return; // get the bridge world object Object *us = getObject(); const Coord3D *center = us->getPosition(); // get our bridge object Bridge *bridge = TheTerrainLogic->findBridgeAt( us->getPosition() ); // get the bridge template AsciiString bridgeTemplateName = bridge->getBridgeTemplateName(); TerrainRoadType *bridgeTemplate = TheTerrainRoads->findBridge( bridgeTemplateName ); DEBUG_ASSERTCRASH( bridgeTemplate, ("Unable to find bridge template to create scaffolding\n") ); // get the thing template for the scaffold object we're going to use AsciiString scaffoldObjectName = bridgeTemplate->getScaffoldObjectName(); const ThingTemplate *scaffoldTemplate = TheThingFactory->findTemplate( scaffoldObjectName ); if( scaffoldTemplate == NULL ) { DEBUG_CRASH(( "Unable to find bridge scaffold template\n" )); return; } // end if // get thing template for scaffold support object AsciiString scaffoldSupportObjectName = bridgeTemplate->getScaffoldSupportObjectName(); const ThingTemplate *scaffoldSupportTemplate = TheThingFactory->findTemplate( scaffoldSupportObjectName ); if( scaffoldSupportTemplate == NULL ) { DEBUG_CRASH(( "Unable to find bridge support scaffold template\n" )); return; } // end if // how much space is going to be between each of the scaffold objects at their final positions Real spacing = scaffoldTemplate->getTemplateGeometryInfo().getMajorRadius() * 2.0f; // how tall are the scaffold objects Real scaffoldHeight = scaffoldTemplate->getTemplateGeometryInfo().getMaxHeightAbovePosition() + scaffoldTemplate->getTemplateGeometryInfo().getMaxHeightBelowPosition(); Real scaffoldSupportHeight = scaffoldSupportTemplate->getTemplateGeometryInfo().getMaxHeightAbovePosition() + scaffoldSupportTemplate->getTemplateGeometryInfo().getMaxHeightBelowPosition(); // get the bridge info BridgeInfo bridgeInfo; bridge->getBridgeInfo( &bridgeInfo ); // // given the area of the bridge, figure out what the start and end points are to create // all the scaffold objects at (just in the 2D bridge plane, not thinking about // rising the objects up through the ground yet) // Coord3D leftStart; leftStart.x = ((bridgeInfo.fromLeft.x - bridgeInfo.fromRight.x) / 2.0f) + bridgeInfo.fromRight.x; leftStart.y = ((bridgeInfo.fromLeft.y - bridgeInfo.fromRight.y) / 2.0f) + bridgeInfo.fromRight.y; leftStart.z = ((bridgeInfo.fromLeft.z - bridgeInfo.fromRight.z) / 2.0f) + bridgeInfo.fromRight.z; Coord3D rightStart; rightStart.x = ((bridgeInfo.toLeft.x - bridgeInfo.toRight.x) / 2.0f) + bridgeInfo.toRight.x; rightStart.y = ((bridgeInfo.toLeft.y - bridgeInfo.toRight.y) / 2.0f) + bridgeInfo.toRight.y; rightStart.z = ((bridgeInfo.toLeft.z - bridgeInfo.toRight.z) / 2.0f) + bridgeInfo.toRight.z; // // now that we have the left and right start points, we will compute two angles to // use for all the objects that we will create ... objects on the left side of the // bridge will be given a 'leftAngle' pointing from 'leftStart' to 'rightStart' and // the opposite will be given to objects on the right side of the bridge // Coord2D angleV; angleV.x = rightStart.x - leftStart.x; angleV.y = rightStart.y - leftStart.y; Real leftAngle = angleV.toAngle(); Real rightAngle = leftAngle + TWO_PI; // compute vector from left to right across bridge and the reverse Coord3D leftVector; leftVector.x = rightStart.x - leftStart.x; leftVector.y = rightStart.y - leftStart.y; leftVector.z = rightStart.z - leftStart.z; Coord3D rightVector; rightVector.x = leftStart.x - rightStart.x; rightVector.y = leftStart.y - rightStart.y; rightVector.z = leftStart.z - rightStart.z; // // how many of these scaffold objects will take to tile from each of the endpoints // to the center area of the bridge // Real tileDistance = leftVector.length(); Int numObjects = REAL_TO_INT_CEIL( tileDistance / spacing ) + 1; // // given the number of objects that we need to tile across the whole bridge, we will // go through the creation loop ceil( numObjects / 2.0f ) times, and each // time through the loop we'll create an object to move from each side of the // bridge, except the last object if the number of objects is odd is dead in the // center // Int numIterations = REAL_TO_INT_CEIL( INT_TO_REAL( numObjects ) / 2.0f ); // // normalize left and right vectors as it is a vector that goes from our left start // position to the destination right start position ... we will multiply this vector by a // spacing amount and add to the left start position to find a destination position // for a particular scaffold object along the bridge surface // leftVector.normalize(); rightVector.normalize(); // create the scaffold objects for now Int scaffoldObjectsCreated = 0; Coord3D destinationPos, *riseToPos; Real *angle; Object *obj; for( Int i = 0; i < numIterations; ++i ) { // sanity DEBUG_ASSERTCRASH( scaffoldObjectsCreated < numObjects, ("Creating too many scaffold objects\n") ); // create object obj = TheThingFactory->newObject( scaffoldTemplate, us->getTeam() ); // this object is from the "left" side of the bridge riseToPos = &leftStart; angle = &leftAngle; // // compute position for object moving from the left side, we're adding 0.1 here // so that all scaffold objects *have* to move some distance ... that way we // can just assign the speeds so they line up perfectly and not have to worry // about any object reaching a destination before any other object // destinationPos.x = leftVector.x * (spacing * i) + riseToPos->x + 0.1f; destinationPos.y = leftVector.y * (spacing * i) + riseToPos->y; destinationPos.z = leftVector.z * (spacing * i) + riseToPos->z; // // now that they key positions are calculated, set the rest of the position data // and movement speeds for the object // setScaffoldData( obj, angle, &scaffoldHeight, riseToPos, &destinationPos, center ); // keeping track of objects created scaffoldObjectsCreated++; m_scaffoldObjectIDList.push_back( obj->getID() ); // // create support object layers under the scaffold object, this object mirrors the scaffold // object except is on a lower layer // Real offset = riseToPos->z; Coord3D supportRiseToPos = *riseToPos; Coord3D supportDestinationPos = destinationPos; Coord3D supportBridgeCenter = *center; while( offset >= 0.0f ) { supportRiseToPos.z -= scaffoldSupportHeight; supportDestinationPos.z -= scaffoldSupportHeight; supportBridgeCenter.z -= scaffoldSupportHeight; obj = TheThingFactory->newObject( scaffoldSupportTemplate, us->getTeam() ); setScaffoldData( obj, angle, &scaffoldSupportHeight, &supportRiseToPos, &supportDestinationPos, &supportBridgeCenter ); m_scaffoldObjectIDList.push_back( obj->getID() ); // off to the next layer offset -= scaffoldSupportHeight; } // end while // // now create the object from the "right" side of the bridge ... but note that // we don't do this on the last iteration through this loop when we have an odd // number of objects we're tiling because all the space is already perfectly used up) // if( scaffoldObjectsCreated < numObjects ) { // sanity DEBUG_ASSERTCRASH( scaffoldObjectsCreated < numObjects, ("Creating too many scaffold objects\n") ); // create new object obj = TheThingFactory->newObject( scaffoldTemplate, us->getTeam() ); // this object is on the "right" side of the bridge riseToPos = &rightStart; angle = &rightAngle; // // compute position for object moving from the left side, we're adding 0.1 here // so that all scaffold objects *have* to move some distance ... that way we // can just assign the speeds so they line up perfectly and not have to worry // about any object reaching a destination before any other object // destinationPos.x = rightVector.x * (spacing * i) + riseToPos->x + 0.1f; destinationPos.y = rightVector.y * (spacing * i) + riseToPos->y; destinationPos.z = rightVector.z * (spacing * i) + riseToPos->z; // set the rest scaffold data again setScaffoldData( obj, angle, &scaffoldHeight, riseToPos, &destinationPos, center ); // keeping track of objects created scaffoldObjectsCreated++; m_scaffoldObjectIDList.push_back( obj->getID() ); // // create support object layers under the scaffold object, this object mirrors the scaffold // object except is on a lower layer // Real offset = riseToPos->z; Coord3D supportRiseToPos = *riseToPos; Coord3D supportDestinationPos = destinationPos; Coord3D supportBridgeCenter = *center; while( offset >= 0.0f ) { supportRiseToPos.z -= scaffoldSupportHeight; supportDestinationPos.z -= scaffoldSupportHeight; supportBridgeCenter.z -= scaffoldSupportHeight; obj = TheThingFactory->newObject( scaffoldSupportTemplate, us->getTeam() ); setScaffoldData( obj, angle, &scaffoldSupportHeight, &supportRiseToPos, &supportDestinationPos, &supportBridgeCenter ); m_scaffoldObjectIDList.push_back( obj->getID() ); // off to the next layer offset -= scaffoldSupportHeight; } // end while } // end if } // end for i // scaffolding is now present m_scaffoldPresent = TRUE; // when scaffolding is present, a bridge cannot be used TheAI->pathfinder()->changeBridgeState( bridge->getLayer(), FALSE ); } // end createScaffolding // ------------------------------------------------------------------------------------------------ /** Remove the bridge scaffolding. If we don't have any then this call is ignored */ // ------------------------------------------------------------------------------------------------ void BridgeBehavior::removeScaffolding( void ) { // if we have no scaffolding, do nothing if( m_scaffoldPresent == FALSE ) return; // go through each object and tell them to reverse course Object *obj; ObjectIDListIterator it; BridgeScaffoldBehaviorInterface *scaffoldBehavior; for( it = m_scaffoldObjectIDList.begin(); it != m_scaffoldObjectIDList.end(); ++it ) { // get the object obj = TheGameLogic->findObjectByID( (*it) ); if( obj == NULL ) continue; // get the scaffold behavior scaffoldBehavior = BridgeScaffoldBehavior::getBridgeScaffoldBehaviorInterfaceFromObject( obj ); DEBUG_ASSERTCRASH( scaffoldBehavior, ("Unable to find bridge scaffold behavior interface\n") ); // reverse the motion scaffoldBehavior->reverseMotion(); } // end for, it // clear our scaffold object list m_scaffoldObjectIDList.clear(); // scaffolding is no longer present m_scaffoldPresent = FALSE; // when scaffolding is gone, a bridge can be used again if we're not in a rubble state Object *us = getObject(); BodyModuleInterface *body = us->getBodyModule(); if( body->getDamageState() != BODY_RUBBLE ) { Bridge *bridge = TheTerrainLogic->findBridgeAt( us->getPosition() ); if( bridge ) TheAI->pathfinder()->changeBridgeState( bridge->getLayer(), TRUE ); } // end if } // end removeScaffolding // ------------------------------------------------------------------------------------------------ /** Is any of the scaffolding in motion */ // ------------------------------------------------------------------------------------------------ Bool BridgeBehavior::isScaffoldInMotion( void ) { Object *obj; // go through the scaffold objects, if any of them are in motion the scaffold is in motion ObjectIDListIterator it; for( it = m_scaffoldObjectIDList.begin(); it != m_scaffoldObjectIDList.end(); ++it ) { // get object obj = TheGameLogic->findObjectByID( (*it) ); if( obj == NULL ) continue; // get scaffold interface BridgeScaffoldBehaviorInterface *bsbi = BridgeScaffoldBehavior::getBridgeScaffoldBehaviorInterfaceFromObject( obj ); if( bsbi == NULL ) continue; // check in motion if( bsbi->getCurrentMotion() != STM_STILL ) return TRUE; } // end for // not in motion return FALSE; } // end isScaffoldInMotion // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void BridgeBehavior::crc( Xfer *xfer ) { // extend base class UpdateModule::crc( xfer ); } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: * 1: Initial version */ // ------------------------------------------------------------------------------------------------ void BridgeBehavior::xfer( Xfer *xfer ) { Object *us = getObject(); // version XferVersion currentVersion = 1; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); // extend base class UpdateModule::xfer( xfer ); // set us as the bridge object in the bridge info if( xfer->getXferMode() == XFER_LOAD ) { Bridge *bridge = TheTerrainLogic->findBridgeAt( us->getPosition() ); // sanity DEBUG_ASSERTCRASH( bridge, ("BridgeBehavior::xfer - Unable to find bridge\n" )); // set new object ID in bridge info to us bridge->setBridgeObjectID( us->getID() ); } // end if // xfer the tower object ids for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i ) xfer->xferObjectID( &m_towerID[ i ] ); // set the tower object ids in the bridge info if( xfer->getXferMode() == XFER_LOAD ) { Object *us = getObject(); Bridge *bridge = TheTerrainLogic->findBridgeAt( us->getPosition() ); // sanity DEBUG_ASSERTCRASH( bridge, ("BridgeBehavior::xfer - Unable to find bridge\n" )); // set new object ID in bridge info to us for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i ) bridge->setTowerObjectID( m_towerID[ i ], (BridgeTowerType)i ); } // end if // scaffold present flag xfer->xferBool( &m_scaffoldPresent ); // scaffold object id list UnsignedShort scaffoldObjectCount = 0; scaffoldObjectCount = m_scaffoldObjectIDList.size(); xfer->xferUnsignedShort( &scaffoldObjectCount ); ObjectID scaffoldObjectID; if( xfer->getXferMode() == XFER_SAVE ) { // write out all object IDs ObjectIDListIterator it; for( it = m_scaffoldObjectIDList.begin(); it != m_scaffoldObjectIDList.end(); ++it ) { scaffoldObjectID = *it; xfer->xferObjectID( &scaffoldObjectID ); } // end for } // end if, save else { // read all object IDs DEBUG_ASSERTCRASH( m_scaffoldObjectIDList.size() == 0, ("BridgeBehavior::xfer - scaffold object list should be empty\n") ); for( Int i = 0; i < scaffoldObjectCount; ++i ) { // read id xfer->xferObjectID( &scaffoldObjectID ); // put on list m_scaffoldObjectIDList.push_back( scaffoldObjectID ); } // end for i } // end load // death frame xfer->xferUnsignedInt( &m_deathFrame ); } // end xfer // ------------------------------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------------------------------ void BridgeBehavior::loadPostProcess( void ) { // extend base class UpdateModule::loadPostProcess(); } // end loadPostProcess