diff --git a/binaries/data/mods/official/scripts/TriggerSpecs.xml b/binaries/data/mods/official/scripts/TriggerSpecs.xml index b096583a01..969d420bc2 100644 --- a/binaries/data/mods/official/scripts/TriggerSpecs.xml +++ b/binaries/data/mods/official/scripts/TriggerSpecs.xml @@ -1,6 +1,6 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/binaries/data/mods/official/scripts/trigger_functions.js b/binaries/data/mods/official/scripts/trigger_functions.js index 516454fd09..646e1957c9 100644 --- a/binaries/data/mods/official/scripts/trigger_functions.js +++ b/binaries/data/mods/official/scripts/trigger_functions.js @@ -19,12 +19,13 @@ function trigPlayerUnitCount(player, unit) function trigObjectTask(subjects, target, task) { for ( var i = 0; i < subjects.length; ++i ) - - getEntityByHandle(subjects[i]).order(ORDER_GENERIC, getEntityByHandle(target), task); + getEntityByHandle(subjects[i]).orderFromTriggers( + ORDER_GENERIC, getEntityByHandle(target[0]), task); } function trigObjectGoto(subjects, destination) { for ( var i = 0; i < subjects.length; ++i ) - getEntityByHandle(subjects[i]).order(ORDER_GOTO, destination.x, destination.y); -} \ No newline at end of file + getEntityByHandle(subjects[i]).orderFromTriggers( + ORDER_GOTO, destination.x, destination.y); +} diff --git a/source/graphics/MapReader.cpp b/source/graphics/MapReader.cpp index e1f672f037..647b673de6 100644 --- a/source/graphics/MapReader.cpp +++ b/source/graphics/MapReader.cpp @@ -697,7 +697,8 @@ void CXMLReader::ReadTriggerGroup(XMBElement parent, MapTriggerGroup& group) EL(display); AT(name); - + AT(function); + AT(display); AT(not); #undef EL @@ -747,6 +748,8 @@ void CXMLReader::ReadTriggerGroup(XMBElement parent, MapTriggerGroup& group) { MapTriggerCondition mapCondition; mapCondition.name = condition.getAttributes().getNamedItem(at_name); + mapCondition.functionName = condition.getAttributes().getNamedItem(at_function); + mapCondition.displayName = condition.getAttributes().getNamedItem(at_display); CStr notAtt(condition.getAttributes().getNamedItem(at_not)); if ( notAtt == CStr("true") ) @@ -849,8 +852,8 @@ int CXMLReader::ReadEntities(XMBElement parent, double end_time) XMBAttributeList attrs = entity.getAttributes(); utf16string uid = attrs.getNamedItem(at_uid); - int UnitID = uid.empty() ? -1 : CStr(uid).ToInt(); - maxUnitID = std::max(maxUnitID, UnitID); + int unitId = uid.empty() ? -1 : CStr(uid).ToInt(); + maxUnitID = std::max(maxUnitID, unitId); } m_MapReader.pUnitMan->SetNextID(maxUnitID + 1); @@ -866,7 +869,7 @@ int CXMLReader::ReadEntities(XMBElement parent, double end_time) XMBAttributeList attrs = entity.getAttributes(); utf16string uid = attrs.getNamedItem(at_uid); - int UnitID = uid.empty() ? -1 : CStr(uid).ToInt(); + int unitId = uid.empty() ? -1 : CStr(uid).ToInt(); CStrW TemplateName; int PlayerID = 0; @@ -913,7 +916,7 @@ int CXMLReader::ReadEntities(XMBElement parent, double end_time) { std::set selections; // TODO: read from file - HEntity ent = g_EntityManager.create(base, Position, Orientation, selections); + HEntity ent = g_EntityManager.create(base, Position, Orientation, selections, 0, unitId); if (! ent) LOG(ERROR, LOG_CATEGORY, "Failed to create entity of type '%ls'", TemplateName.c_str()); @@ -922,10 +925,10 @@ int CXMLReader::ReadEntities(XMBElement parent, double end_time) ent->m_actor->SetPlayerID(PlayerID); g_EntityManager.AddEntityClassData(ent); - if (UnitID < 0) + if (unitId < 0) ent->m_actor->SetID(m_MapReader.pUnitMan->GetNewID()); else - ent->m_actor->SetID(UnitID); + ent->m_actor->SetID(unitId); } } diff --git a/source/main.cpp b/source/main.cpp index 0471e383f1..170edc139c 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -33,7 +33,6 @@ that of Atlas depending on commandline parameters. #include "ps/Globals.h" #include "ps/Interact.h" #include "network/SessionManager.h" -#include "simulation/TriggerManager.h" #include "graphics/Camera.h" #include "graphics/GameView.h" #include "simulation/Scheduler.h" @@ -267,10 +266,6 @@ static void Frame() PROFILE( "camera update" ); g_Game->GetView()->Update(float(TimeSinceLastFrame)); } - - PROFILE_START("trigger update"); - g_TriggerManager.Update( (float)TimeSinceLastFrame ); - PROFILE_END("trigger udpate"); PROFILE_START( "selection and interaction ui" ); // TODO Where does GameView end and other things begin? diff --git a/source/simulation/Entity.h b/source/simulation/Entity.h index c195c76895..355ba02442 100644 --- a/source/simulation/Entity.h +++ b/source/simulation/Entity.h @@ -439,14 +439,20 @@ public: return ( m_formation != 0 ? true : false ); } - bool Order( JSContext* cx, uintN argc, jsval* argv, bool Queued ); + bool Order( JSContext* cx, uintN argc, jsval* argv, CEntityOrder::EOrderSource source, bool Queued ); + + // TODO: Replace these variants of order() with a single function, and update scripts accordingly. inline bool OrderSingle( JSContext* cx, uintN argc, jsval* argv ) { - return( Order( cx, argc, argv, false ) ); + return( Order( cx, argc, argv, CEntityOrder::SOURCE_PLAYER, false ) ); } inline bool OrderQueued( JSContext* cx, uintN argc, jsval* argv ) { - return( Order( cx, argc, argv, true ) ); + return( Order( cx, argc, argv, CEntityOrder::SOURCE_PLAYER, true ) ); + } + inline bool OrderFromTriggers( JSContext* cx, uintN argc, jsval* argv ) + { + return( Order( cx, argc, argv, CEntityOrder::SOURCE_TRIGGERS, true ) ); } bool IsIdle( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) diff --git a/source/simulation/EntityManager.cpp b/source/simulation/EntityManager.cpp index 4ef54812a2..10f2859b9f 100644 --- a/source/simulation/EntityManager.cpp +++ b/source/simulation/EntityManager.cpp @@ -85,28 +85,47 @@ void CEntityManager::deleteAll() HEntity CEntityManager::create(CEntityTemplate* base, CVector3D position, float orientation, const std::set& actorSelections, - const CStrW* building) + const CStrW* building, int desiredHandle) { debug_assert( base ); if( !base ) return HEntity(); - while( m_entities[m_nextalloc].m_refcount ) + int pos = 0; + + if(desiredHandle >= 0) { - m_nextalloc++; - if(m_nextalloc >= MAX_HANDLES) + if( m_entities[desiredHandle].m_refcount ) { - debug_warn("Ran out of entity handles!"); + debug_warn("Tried to create an entity at index %d, which is already taken.", desiredHandle); return HEntity(); } + else + { + pos = desiredHandle; + } + } + else + { + while( m_entities[m_nextalloc].m_refcount ) + { + m_nextalloc++; + if(m_nextalloc >= MAX_HANDLES) + { + debug_warn("Ran out of entity handles!"); + return HEntity(); + } + } + pos = m_nextalloc; + m_nextalloc++; } - m_entities[m_nextalloc].m_entity = new CEntity( base, position, orientation, actorSelections, building ); + m_entities[pos].m_entity = new CEntity( base, position, orientation, actorSelections, building ); if( m_collisionPatches) - m_entities[m_nextalloc].m_entity->updateCollisionPatch(); - m_entities[m_nextalloc].m_entity->me = HEntity( m_nextalloc ); + m_entities[pos].m_entity->updateCollisionPatch(); + m_entities[pos].m_entity->me = HEntity( pos ); - return( HEntity( m_nextalloc++ ) ); + return( HEntity( pos ) ); } void CEntityManager::AddEntityClassData(const HEntity& handle) diff --git a/source/simulation/EntityManager.h b/source/simulation/EntityManager.h index ed150cb658..cc4c5c125b 100644 --- a/source/simulation/EntityManager.h +++ b/source/simulation/EntityManager.h @@ -69,7 +69,7 @@ public: ~CEntityManager(); HEntity create( CEntityTemplate* base, CVector3D position, float orientation, - const std::set& actorSelections, const CStrW* building = 0 ); + const std::set& actorSelections, const CStrW* building = 0, int desiredHandle = -1 ); HEntity create( const CStrW& templateName, CPlayer* player, CVector3D position, float orientation, const CStrW* building = 0 ); diff --git a/source/simulation/EntityOrders.h b/source/simulation/EntityOrders.h index 93005d8da2..328f321f37 100644 --- a/source/simulation/EntityOrders.h +++ b/source/simulation/EntityOrders.h @@ -23,7 +23,8 @@ // order queue after it's executed. In this way, the entity will // circle round a list of patrol points. // Create this order when a standard patrol order is required. -// ORDER_ATTACK_MELEE: Move towards target entity; start bashing it when close enough. +// ORDER_GENERIC: Generic ranged action. Move towards target entity, then start +// performing an action (call a JS event handler every few seconds). // If we collide with something (=> line-of-sight tracking no longer // sufficient) spawns a ORDER_GOTO to target's location and pushes it // immediately in front of this order. @@ -105,7 +106,8 @@ public: enum EOrderSource { SOURCE_PLAYER, - SOURCE_UNIT_AI + SOURCE_UNIT_AI, + SOURCE_TRIGGERS }; EOrderSource m_source; diff --git a/source/simulation/EntityScriptInterface.cpp b/source/simulation/EntityScriptInterface.cpp index 03ea5c0498..f467186cfd 100644 --- a/source/simulation/EntityScriptInterface.cpp +++ b/source/simulation/EntityScriptInterface.cpp @@ -49,6 +49,7 @@ void CEntity::ScriptingInit() AddMethod( "toString", 0 ); AddMethod( "order", 1 ); AddMethod( "orderQueued", 1 ); + AddMethod( "orderFromTriggers", 1 ); AddMethod( "terminateOrder", 1 ); AddMethod( "kill", 0 ); AddMethod( "isIdle", 0 ); @@ -244,7 +245,7 @@ void CEntity::JSI_SetPlayer( jsval val ) SetPlayer(newPlayer); } -bool CEntity::Order( JSContext* cx, uintN argc, jsval* argv, bool Queued ) +bool CEntity::Order( JSContext* cx, uintN argc, jsval* argv, CEntityOrder::EOrderSource source, bool Queued ) { // This needs to be sorted (uses Scheduler rather than network messaging) @@ -261,7 +262,9 @@ bool CEntity::Order( JSContext* cx, uintN argc, jsval* argv, bool Queued ) } CEntityOrder newOrder; - newOrder.m_source = CEntityOrder::SOURCE_PLAYER; + + newOrder.m_source = source; + CEntity* target; (int&)newOrder.m_type = orderCode; diff --git a/source/simulation/EntityStateProcessing.cpp b/source/simulation/EntityStateProcessing.cpp index 28973e8b4c..cb09f971a6 100644 --- a/source/simulation/EntityStateProcessing.cpp +++ b/source/simulation/EntityStateProcessing.cpp @@ -375,7 +375,8 @@ bool CEntity::processContactAction( CEntityOrder* current, size_t UNUSED(timeste return false; } - if( g_Game->GetWorld()->GetLOSManager()->GetUnitStatus( target, m_player ) == UNIT_HIDDEN ) + if( current->m_source != CEntityOrder::SOURCE_TRIGGERS && + g_Game->GetWorld()->GetLOSManager()->GetUnitStatus( target, m_player ) == UNIT_HIDDEN ) { popOrder(); return false; diff --git a/source/simulation/Simulation.cpp b/source/simulation/Simulation.cpp index 03a608f876..726426367a 100644 --- a/source/simulation/Simulation.cpp +++ b/source/simulation/Simulation.cpp @@ -26,6 +26,7 @@ #include "simulation/Simulation.h" #include "simulation/TerritoryManager.h" #include "simulation/TurnManager.h" +#include "simulation/TriggerManager.h" CSimulation::CSimulation(CGame *pGame): m_pGame(pGame), @@ -146,6 +147,10 @@ void CSimulation::Simulate() m_pWorld->GetLOSManager()->Update(); PROFILE_END( "los update" ); + PROFILE_START("trigger update"); + g_TriggerManager.Update( m_pTurnManager->GetTurnLength() ); + PROFILE_END("trigger udpate"); + PROFILE_START( "turn manager update" ); m_pTurnManager->NewTurn(); m_pTurnManager->IterateBatch(0, TranslateMessage, this); diff --git a/source/simulation/TriggerManager.cpp b/source/simulation/TriggerManager.cpp index 858c9f50b9..363f7b1689 100644 --- a/source/simulation/TriggerManager.cpp +++ b/source/simulation/TriggerManager.cpp @@ -210,72 +210,79 @@ void CTriggerManager::SetAllGroups(const std::list& groups) void CTriggerManager::AddTrigger(MapTriggerGroup& group, const MapTrigger& trigger) { - CStrW conditionBody(L"if ( "); + CStrW conditionBody; CStrW linkLogic[] = { CStrW(L""), CStrW(L" && "), CStrW(L" || ") }; size_t i=0; bool allParameters = true; - for ( std::list::const_iterator it = trigger.conditions.begin(); - it != trigger.conditions.end(); ++it, ++i ) - { - //Opening parenthesis here? - std::set::const_iterator blockIt; - if ( ( blockIt = trigger.logicBlocks.find(MapTriggerLogicBlock(i)) ) != trigger.logicBlocks.end() ) + if(trigger.conditions.size() == 0) { + conditionBody = CStrW(L"return ( true );"); + } + else { + conditionBody = CStrW(L"return ( "); + + for ( std::list::const_iterator it = trigger.conditions.begin(); + it != trigger.conditions.end(); ++it, ++i ) { - if ( blockIt->negated ) - conditionBody += CStrW(L"!"); - conditionBody += CStrW(L" ("); - } - - if ( it->negated ) - conditionBody += CStrW(L"!"); - conditionBody += it->functionName; - conditionBody += CStrW(L"("); - - for ( std::list::const_iterator it2 = it->parameters.begin(); it2 != - it->parameters.end(); ++it2 ) - { - size_t params = (size_t)std::find(m_ConditionSpecs.begin(), m_ConditionSpecs.end(), it->displayName)->funcParameters; - size_t distance = std::distance(it->parameters.begin(), it2); - - //Parameters end here, additional "parameters" are used directly as script - if ( distance == params ) + //Opening parenthesis here? + std::set::const_iterator blockIt; + if ( ( blockIt = trigger.logicBlocks.find(MapTriggerLogicBlock(i)) ) != trigger.logicBlocks.end() ) { - conditionBody += CStrW(L") "); - allParameters = false; + if ( blockIt->negated ) + conditionBody += CStrW(L"!"); + conditionBody += CStrW(L" ("); } - - //Take display parameter and translate into JS usable code...evilness - CTriggerSpec spec = *std::find( m_ConditionSpecs.begin(), m_ConditionSpecs.end(), it->displayName ); - const std::set& specParameters = spec.GetParameters(); - //Don't use specialized find, since we're searching for a different member - std::set::const_iterator specParam = std::find( - specParameters.begin(), specParameters.end(), (int)distance); - std::wstring combined = std::wstring( it->functionName + specParam->name ); - size_t translatedIndex = std::distance( m_TriggerChoices[combined].begin(), - std::find(m_TriggerChoices[combined].begin(), m_TriggerChoices[combined].end(), std::wstring(*it2)) ); + if ( it->negated ) + conditionBody += CStrW(L"!"); + conditionBody += it->functionName; + conditionBody += CStrW(L"("); + + for ( std::list::const_iterator it2 = it->parameters.begin(); it2 != + it->parameters.end(); ++it2 ) + { + size_t params = (size_t)std::find(m_ConditionSpecs.begin(), m_ConditionSpecs.end(), it->displayName)->funcParameters; + size_t distance = std::distance(it->parameters.begin(), it2); + + //Parameters end here, additional "parameters" are used directly as script + if ( distance == params ) + { + conditionBody += CStrW(L") "); + allParameters = false; + } - if ( m_TriggerTranslations[combined].empty() ) - conditionBody += *it2; - else - conditionBody += m_TriggerTranslations[combined][translatedIndex]; + //Take display parameter and translate into JS usable code...evilness + CTriggerSpec spec = *std::find( m_ConditionSpecs.begin(), m_ConditionSpecs.end(), it->displayName ); + const std::set& specParameters = spec.GetParameters(); + + //Don't use specialized find, since we're searching for a different member + std::set::const_iterator specParam = std::find( + specParameters.begin(), specParameters.end(), (int)distance); + std::wstring combined = std::wstring( it->functionName + specParam->name ); + size_t translatedIndex = std::distance( m_TriggerChoices[combined].begin(), + std::find(m_TriggerChoices[combined].begin(), m_TriggerChoices[combined].end(), std::wstring(*it2)) ); - if ( distance + 1 < params ) - conditionBody += CStrW(L", "); + if ( m_TriggerTranslations[combined].empty() ) + conditionBody += *it2; + else + conditionBody += m_TriggerTranslations[combined][translatedIndex]; + + if ( distance + 1 < params ) + conditionBody += CStrW(L", "); + } + + if ( allParameters ) //Otherwise, closed inside loop + conditionBody += CStrW(L")"); + if ( trigger.logicBlockEnds.find(i) != trigger.logicBlockEnds.end() ) + conditionBody += CStrW(L" )"); + + if ( std::distance(it, trigger.conditions.end()) != 1 ) + conditionBody += linkLogic[it->linkLogic]; } - - if ( allParameters ) //Otherwise, closed inside loop - conditionBody += CStrW(L")"); - if ( trigger.logicBlockEnds.find(i) != trigger.logicBlockEnds.end() ) - conditionBody += CStrW(L" )"); - if ( std::distance(it, trigger.conditions.end()) != 1 ) - conditionBody += linkLogic[it->linkLogic]; + conditionBody += CStrW(L" );"); } - conditionBody += CStrW(L" )"); //closing if - conditionBody += CStrW(L" { return true; } "); CStrW effectBody; for ( std::list::const_iterator it = trigger.effects.begin();