# Fixed some trigger bugs.

- Conditions were not properly converted to JavaScript (not read from
the XML file correctly either).
- Added an OrderFromTriggers function so that triggers can give orders
which ignore fog of war.
- Fixed some spelling mistakes in TriggerSpecs.xml (parameter vs.
parameters).
- Moved trigger update to CSimulation update, which is more correct.

This was SVN commit r4821.
This commit is contained in:
Matei 2007-01-29 22:21:25 +00:00
parent 41cf5c6139
commit 465c0a037b
12 changed files with 130 additions and 88 deletions

View File

@ -1,6 +1,6 @@
<Definitions>
<Condition name ="DisplayName" function = "something" funcParameter = "0">
<Condition name ="DisplayName" function = "something" funcParameters = "0">
<WindowRow>
<Parameter name = "Random">
<Window type="text" position = "0,0" size = "80,20"/>
@ -20,7 +20,7 @@
</WindowRow>
</Condition>
<Condition name = "Always" function = "trigGetAlwaysTrue" funcParameter = "0"/>
<Condition name = "Always" function = "trigGetAlwaysTrue" funcParameters = "0"/>
<Condition name = "Player resource" function = "trigPlayerResourceCount" funcParameters = "2">
<WindowRow>

View File

@ -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);
}
getEntityByHandle(subjects[i]).orderFromTriggers(
ORDER_GOTO, destination.x, destination.y);
}

View File

@ -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<CStr> 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);
}
}

View File

@ -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?

View File

@ -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) )

View File

@ -85,28 +85,47 @@ void CEntityManager::deleteAll()
HEntity CEntityManager::create(CEntityTemplate* base, CVector3D position, float orientation,
const std::set<CStr>& 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)

View File

@ -69,7 +69,7 @@ public:
~CEntityManager();
HEntity create( CEntityTemplate* base, CVector3D position, float orientation,
const std::set<CStr>& actorSelections, const CStrW* building = 0 );
const std::set<CStr>& actorSelections, const CStrW* building = 0, int desiredHandle = -1 );
HEntity create( const CStrW& templateName, CPlayer* player, CVector3D position,
float orientation, const CStrW* building = 0 );

View File

@ -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;

View File

@ -49,6 +49,7 @@ void CEntity::ScriptingInit()
AddMethod<jsval, &CEntity::ToString>( "toString", 0 );
AddMethod<bool, &CEntity::OrderSingle>( "order", 1 );
AddMethod<bool, &CEntity::OrderQueued>( "orderQueued", 1 );
AddMethod<bool, &CEntity::OrderFromTriggers>( "orderFromTriggers", 1 );
AddMethod<jsval, &CEntity::TerminateOrder>( "terminateOrder", 1 );
AddMethod<bool, &CEntity::Kill>( "kill", 0 );
AddMethod<bool, &CEntity::IsIdle>( "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;

View File

@ -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;

View File

@ -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);

View File

@ -210,72 +210,79 @@ void CTriggerManager::SetAllGroups(const std::list<MapTriggerGroup>& 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<MapTriggerCondition>::const_iterator it = trigger.conditions.begin();
it != trigger.conditions.end(); ++it, ++i )
{
//Opening parenthesis here?
std::set<MapTriggerLogicBlock>::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<MapTriggerCondition>::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<CStrW>::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<MapTriggerLogicBlock>::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<TriggerParameter>& specParameters = spec.GetParameters();
//Don't use specialized find, since we're searching for a different member
std::set<TriggerParameter>::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<CStrW>::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<TriggerParameter>& specParameters = spec.GetParameters();
//Don't use specialized find, since we're searching for a different member
std::set<TriggerParameter>::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<MapTriggerEffect>::const_iterator it = trigger.effects.begin();