diff --git a/binaries/data/mods/official/maps/scenarios/triggerTest.pmp b/binaries/data/mods/official/maps/scenarios/triggerTest.pmp new file mode 100644 index 0000000000..2fd5ed9f12 --- /dev/null +++ b/binaries/data/mods/official/maps/scenarios/triggerTest.pmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d81cc43c837cb427881906d36d12a2c3e299885d9b8c3d6f2714106b3ee9dd59 +size 207975 diff --git a/binaries/data/mods/official/maps/scenarios/triggerTest.xml b/binaries/data/mods/official/maps/scenarios/triggerTest.xml new file mode 100644 index 0000000000..bbb4751929 --- /dev/null +++ b/binaries/data/mods/official/maps/scenarios/triggerTest.xml @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab2a5f92f97c6d84f9ee40681e280d9076eb4c1135f17cf036229c08ce61a90c +size 1474 diff --git a/binaries/data/mods/official/scripts/TriggerSpecs.xml b/binaries/data/mods/official/scripts/TriggerSpecs.xml new file mode 100644 index 0000000000..8323379e32 --- /dev/null +++ b/binaries/data/mods/official/scripts/TriggerSpecs.xml @@ -0,0 +1,35 @@ + + + + + + + int + 0 + + + + ATLAS_CINEMA_LIST + 0 + + + + holy, crap, comma, seperated, choices + 0 + + + + + + + + + + + string + 0 + + + + + \ No newline at end of file diff --git a/binaries/data/mods/official/scripts/entity_functions.js b/binaries/data/mods/official/scripts/entity_functions.js index bd66c85c20..1743b49d48 100644 --- a/binaries/data/mods/official/scripts/entity_functions.js +++ b/binaries/data/mods/official/scripts/entity_functions.js @@ -791,9 +791,16 @@ function performBuild( evt ) var hp = t.traits.health; var points = parseFloat( b.rate ) * parseFloat( b.speed ) / 1000.0; + if ( bp.curr == 0 ) + { + t.flattenTerrain(); //make the terrain stable for the building + //t.placeBuildingFootprint(false); //false means display regular footprint, not rubble + } bp.curr += points; hp.curr = Math.min( hp.max, hp.curr + (points/bp.max)*hp.max ); + + if( bp.curr >= bp.max ) { // We've finished building this object; convert the foundation to a building diff --git a/binaries/data/mods/official/scripts/trigger_functions.js b/binaries/data/mods/official/scripts/trigger_functions.js new file mode 100644 index 0000000000..c9f337dee2 --- /dev/null +++ b/binaries/data/mods/official/scripts/trigger_functions.js @@ -0,0 +1,4 @@ +function getAlwaysTrue() +{ +return true; +} diff --git a/source/graphics/CinemaTrack.h b/source/graphics/CinemaTrack.h index edc6a5a2c0..e3df65b4a2 100644 --- a/source/graphics/CinemaTrack.h +++ b/source/graphics/CinemaTrack.h @@ -8,7 +8,7 @@ #include "maths/NUSpline.h" /* - Andrew (aka pyrolink) + Andrew Decker (aka pyrolink) Contact: ajdecker1022@msn.com desc: contains various functions used for cinematic camera tracks See also: CinemaHandler.cpp, Cinematic.h/.cpp diff --git a/source/graphics/MapReader.cpp b/source/graphics/MapReader.cpp index 44adf92ecb..0740636af3 100644 --- a/source/graphics/MapReader.cpp +++ b/source/graphics/MapReader.cpp @@ -24,6 +24,7 @@ #include "renderer/WaterManager.h" #include "simulation/Entity.h" #include "simulation/EntityManager.h" +#include "simulation/TriggerManager.h" #include "simulation/EntityTemplate.h" #include "simulation/EntityTemplateCollection.h" @@ -334,6 +335,8 @@ private: void ReadEnvironment(XMBElement parent); void ReadCamera(XMBElement parent); void ReadCinema(XMBElement parent); + void ReadTriggers(XMBElement parent); + void ReadTriggerGroup(XMBElement parent, MapTriggerGroup& group); int ReadEntities(XMBElement parent, double end_time); int ReadNonEntities(XMBElement parent, double end_time); @@ -663,6 +666,164 @@ void CXMLReader::ReadCinema(XMBElement parent) g_Game->GetView()->GetCinema()->SetAllTracks(trackList); } +void CXMLReader::ReadTriggers(XMBElement parent) +{ + MapTriggerGroup rootGroup( CStrW(L"Triggers"), CStrW(L"") ); + g_TriggerManager.DestroyEngineTriggers(); + ReadTriggerGroup(parent, rootGroup); +} + +void CXMLReader::ReadTriggerGroup(XMBElement parent, MapTriggerGroup& group) +{ + #define EL(x) int el_##x = xmb_file.getElementID(#x) + #define AT(x) int at_##x = xmb_file.getAttributeID(#x) + + EL(group); + EL(trigger); + EL(active); + EL(delay); + EL(maxruncount); + EL(conditions); + EL(logicblock); + EL(logicblockend); + EL(condition); + EL(parameter); + EL(linklogic); + EL(effects); + EL(effect); + + AT(name); + AT(function); + AT(display); + AT(not); + + #undef EL + #undef AT + + CStrW name = parent.getAttributes().getNamedItem(at_name), parentName = group.parentName; + if ( group.name == L"Triggers" ) + name = group.name; + + MapTriggerGroup mapGroup(name, parentName); + + XERO_ITER_EL(parent, groupChild) + { + int elementName = groupChild.getNodeName(); + if ( elementName == el_group ) + ReadTriggerGroup(groupChild, mapGroup); + + else if ( elementName == el_trigger ) + { + MapTrigger mapTrigger; + mapTrigger.name = CStrW( groupChild.getAttributes().getNamedItem(at_name) ); + + //Read everything in this trigger + XERO_ITER_EL(groupChild, triggerChild) + { + elementName = triggerChild.getNodeName(); + if ( elementName == el_active ) + { + if ( CStr("false") == CStr( triggerChild.getText() ) ) + mapTrigger.active = false; + else + mapTrigger.active = true; + } + + else if ( elementName == el_maxruncount ) + mapTrigger.maxRunCount = CStr( triggerChild.getText() ).ToInt(); + else if ( elementName == el_delay ) + mapTrigger.timeValue = CStr( triggerChild.getText() ).ToFloat(); + + else if ( elementName == el_conditions ) + { + //Read in all conditions for this trigger + XERO_ITER_EL(triggerChild, condition) + { + elementName = condition.getNodeName(); + if ( elementName == el_condition ) + { + 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") ) + mapCondition.not = true; + + //Read in each condition child + XERO_ITER_EL(condition, conditionChild) + { + elementName = conditionChild.getNodeName(); + + if ( elementName == el_parameter ) + mapCondition.parameters.push_back( conditionChild.getText() ); + else if ( elementName == el_linklogic ) + { + CStr logic = conditionChild.getText(); + if ( logic == CStr("AND") ) + mapCondition.linkLogic = 1; + else + mapCondition.linkLogic = 2; + } + } + mapTrigger.conditions.push_back(mapCondition); + } //Read all conditions + + else if ( elementName == el_logicblock) + { + if ( CStr(condition.getAttributes().getNamedItem(at_not)) == CStr("true") ) + mapTrigger.AddLogicBlock(true); + else + mapTrigger.AddLogicBlock(false); + } + else if ( elementName == el_logicblockend) + mapTrigger.AddLogicBlockEnd(); + + } //Read all conditions + } + + else if ( elementName == el_effects ) + { + //Read all effects + XERO_ITER_EL(triggerChild, effect) + { + if ( effect.getNodeName() != el_effect ) + { + debug_warn("Invalid effect tag in trigger XML file"); + return; + } + MapTriggerEffect mapEffect; + mapEffect.name = effect.getAttributes().getNamedItem(at_name); + mapEffect.functionName = effect.getAttributes().getNamedItem(at_function); + mapEffect.displayName = effect.getAttributes().getNamedItem(at_display); + + //Read parameters + XERO_ITER_EL(effect, effectChild) + { + if ( effectChild.getNodeName() != el_parameter ) + { + debug_warn("Invalid parameter tag in trigger XML file"); + return; + } + mapEffect.parameters.push_back( effectChild.getText() ); + } + mapTrigger.effects.push_back(mapEffect); + } + } + else + debug_warn("Invalid trigger node child in trigger XML file"); + + } //Read trigger children + g_TriggerManager.AddTrigger(mapGroup, mapTrigger); + } + else + debug_warn("Invalid group node child in XML file"); + } //Read group children + + g_TriggerManager.AddGroup(mapGroup); +} + int CXMLReader::ReadEntities(XMBElement parent, double end_time) { XMBElementList entities = parent.getChildNodes(); @@ -848,6 +1009,10 @@ int CXMLReader::ProgressiveRead() { ReadCinema(node); } + else if (name == "Triggers") + { + ReadTriggers(node); + } else debug_warn("Invalid map XML data"); diff --git a/source/graphics/MapReader.h b/source/graphics/MapReader.h index fc89e6f31c..0d5d8f922b 100644 --- a/source/graphics/MapReader.h +++ b/source/graphics/MapReader.h @@ -16,6 +16,7 @@ class SkyManager; class CLightEnv; class CCamera; class CCinemaManager; +class CTriggerManager; class CXMLReader; @@ -28,7 +29,8 @@ public: CMapReader(); // LoadMap: try to load the map from given file; reinitialise the scene to new data if successful void LoadMap(const char* filename, CTerrain *pTerrain, CUnitManager *pUnitMan, - WaterManager* pWaterMan, SkyManager* pSkyMan, CLightEnv *pLightEnv, CCamera *pCamera, CCinemaManager* pCinema); + WaterManager* pWaterMan, SkyManager* pSkyMan, CLightEnv *pLightEnv, CCamera *pCamera, + CCinemaManager* pCinema); private: // UnpackTerrain: unpack the terrain from the input stream @@ -78,6 +80,7 @@ private: CLightEnv* pLightEnv; CCamera* pCamera; CCinemaManager* pCinema; + CTriggerManager* pTrigMan; CStr filename_xml; // UnpackTerrain generator state diff --git a/source/graphics/MapWriter.cpp b/source/graphics/MapWriter.cpp index 99013d31dd..c3896ea194 100644 --- a/source/graphics/MapWriter.cpp +++ b/source/graphics/MapWriter.cpp @@ -28,6 +28,7 @@ #include "renderer/WaterManager.h" #include "simulation/EntityTemplate.h" #include "simulation/EntityTemplateCollection.h" +#include "simulation/TriggerManager.h" #include "simulation/Entity.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -395,7 +396,27 @@ void CMapWriter::WriteXML(const char* filename, } } //track data } - } //(Did this really happen) - I blame XML + } //(Did this really happen) - I blame XML :p + + const std::list& groups = g_TriggerManager.GetAllTriggerGroups(); + std::list rootChildren; + std::list::const_iterator root = std::find( groups.begin(), groups.end(), + CStrW(L"Triggers") ); + std::for_each(rootChildren.begin(), rootChildren.end(), copyIfRootChild(rootChildren)); + + { + XML_Element("Triggers"); + for ( std::list::const_iterator it = rootChildren.begin(); + it != rootChildren.end(); ++it ) + { + WriteTriggerGroup(xml_file_, *it, groups); + } + for ( std::list::const_iterator it = root->triggers.begin(); + it != root->triggers.end(); ++it ) + { + WriteTrigger(xml_file_, *it); + } + } } if (! XML_StoreVFS(h)) @@ -405,6 +426,112 @@ void CMapWriter::WriteXML(const char* filename, (void)vfs_close(h); } +void CMapWriter::WriteTriggerGroup(XMLWriter_File& xml_file_, const MapTriggerGroup& group, const std::list& groupList) +{ + XML_Element("Group"); + XML_Attribute("name", group.name); + + for ( std::list::const_iterator it = group.childGroups.begin(); + it != group.childGroups.end(); ++it ) + { + //Not very efficient... + std::list::const_iterator it2 = std::find(groupList.begin(), groupList.end(), *it); + if ( it2 != groupList.end() ) + WriteTriggerGroup(xml_file_, *it2, groupList); + else + debug_warn("Invalid trigger group ID while writing map"); + } + + for ( std::list::const_iterator it = group.triggers.begin(); it != group.triggers.end(); ++it ) + { + WriteTrigger(xml_file_, *it); + } +} + +void CMapWriter::WriteTrigger(XMLWriter_File& xml_file_, const MapTrigger& trigger) +{ + XML_Element("Trigger"); + XML_Attribute("name", trigger.name); + + { + if ( trigger.active ) + XML_Setting("Active", "true"); + else + XML_Setting("Active", "false"); + XML_Setting("MaxRunCount", trigger.maxRunCount); + XML_Setting("Delay", trigger.timeValue); + } + + { + XML_Element("Conditions"); + for ( std::list::const_iterator it2 = trigger.conditions.begin(); + it2 != trigger.conditions.end(); ++it2 ) + { + size_t distance = std::distance( trigger.conditions.begin(), it2 ); + std::set::const_iterator logicIter; + + if ( ( logicIter = trigger.logicBlocks.find(MapTriggerLogicBlock(distance)) ) + != trigger.logicBlocks.end() ) + { + XML_Element("LogicBlock"); + if ( logicIter->not ) + XML_Attribute("not", "true"); + else + XML_Attribute("not", "false"); + } + + { + XML_Element("Condition"); + XML_Attribute("name", it2->name); + XML_Attribute("function", it2->functionName); + XML_Attribute("display", it2->displayName); + + if ( it2->not ) + XML_Attribute("not", "true"); + else + XML_Attribute("not", "false"); + + for ( std::list::const_iterator paramIter = it2->parameters.begin(); + paramIter != it2->parameters.end(); ++paramIter ) + { + XML_Setting("Parameter", *paramIter); + } + + if ( it2->linkLogic == 1 ) + { + XML_Setting("LinkLogic", "AND"); + } + else if ( it2->linkLogic == 2 ) + { + XML_Setting("LinkLogic", "OR"); + } + + if ( trigger.logicBlockEnds.find(distance) != trigger.logicBlockEnds.end() ) + { + XML_Element("LogicBlockEnd"); + } + } + } //Read all conditions + } //Conditions' scope + + { + XML_Element("Effects"); + for ( std::list::const_iterator it2 = trigger.effects.begin(); + it2 != trigger.effects.end(); ++it2 ) + { + XML_Element("Effect"); + XML_Attribute("name", it2->name); + XML_Attribute("function", it2->functionName); + XML_Attribute("display", it2->displayName); + + for ( std::list::const_iterator paramIter = it2->parameters.begin(); + paramIter != it2->parameters.end(); ++paramIter ) + { + XML_Setting("parameter", *paramIter); + } + } + } //Effects' scope +} /////////////////////////////////////////////////////////////////////////////////////////////////// // RewriteAllMaps void CMapWriter::RewriteAllMaps(CTerrain* pTerrain, CUnitManager* pUnitMan, @@ -428,3 +555,5 @@ void CMapWriter::RewriteAllMaps(CTerrain* pTerrain, CUnitManager* pUnitMan, writer.SaveMap(n, pTerrain, pUnitMan, pWaterMan, pSkyMan, pLightEnv, pCamera, pCinema); } } + + \ No newline at end of file diff --git a/source/graphics/MapWriter.h b/source/graphics/MapWriter.h index 67dd511ccf..6152e1e878 100644 --- a/source/graphics/MapWriter.h +++ b/source/graphics/MapWriter.h @@ -6,13 +6,18 @@ #include "ps/CStr.h" #include "ps/FilePacker.h" + class CLightEnv; class CTerrain; class CUnitManager; class CCamera; class CCinemaManager; +class CTriggerManager; class WaterManager; class SkyManager; +struct MapTrigger; +struct MapTriggerGroup; +class XMLWriter_File; class CMapWriter : public CMapIO { @@ -20,11 +25,16 @@ public: // constructor CMapWriter(); // SaveMap: try to save the current map to the given file - void SaveMap(const char* filename, CTerrain* pTerr, CUnitManager* pUnitMan, WaterManager* pWaterMan, SkyManager* pSkyMan, CLightEnv* pLightEnv, CCamera* pCamera, CCinemaManager* pCinema); + void SaveMap(const char* filename, CTerrain* pTerr, CUnitManager* pUnitMan, + WaterManager* pWaterMan, SkyManager* pSkyMan, + CLightEnv* pLightEnv, CCamera* pCamera, + CCinemaManager* pCinema); // RewriteAllMaps: for use during development: load/save all maps, to // update them to the newest format. - static void RewriteAllMaps(CTerrain* pTerrain, CUnitManager* pUnitMan, WaterManager* pWaterMan, SkyManager* pSkyMan, CLightEnv* pLightEnv, CCamera* pCamera, CCinemaManager* pCinema); + static void RewriteAllMaps(CTerrain* pTerrain, CUnitManager* pUnitMan, WaterManager* pWaterMan, + SkyManager* pSkyMan, CLightEnv* pLightEnv, CCamera* pCamera, + CCinemaManager* pCinema); private: // PackMap: pack the current world into a raw data stream @@ -38,7 +48,12 @@ private: std::vector& tileIndices); // WriteXML: output some other data (entities, etc) in XML format - void WriteXML(const char* filename, CUnitManager* pUnitMan, WaterManager* pWaterMan, SkyManager* pSkyMan, CLightEnv* pLightEnv, CCamera* pCamera, CCinemaManager* pCinema); + void WriteXML(const char* filename, CUnitManager* pUnitMan, WaterManager* pWaterMan, + SkyManager* pSkyMan, CLightEnv* pLightEnv, CCamera* pCamera, + CCinemaManager* pCinema); + void WriteTriggerGroup(XMLWriter_File& xml_file_, const MapTriggerGroup& group, + const std::list& groupList); + void WriteTrigger(XMLWriter_File& xml_file_, const MapTrigger& trigger); }; #endif diff --git a/source/main.cpp b/source/main.cpp index 66ba6cd214..eea3be632c 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -32,6 +32,7 @@ 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/GameView.h" #include "simulation/Scheduler.h" #include "sound/CMusicPlayer.h" @@ -264,6 +265,10 @@ 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/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index 4a8362bd9c..80ae2e1357 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -58,6 +58,7 @@ #include "simulation/FormationCollection.h" #include "simulation/FormationManager.h" #include "simulation/TerritoryManager.h" +#include "simulation/TriggerManager.h" #include "simulation/PathfindEngine.h" #include "simulation/Projectile.h" #include "simulation/Scheduler.h" @@ -480,7 +481,9 @@ static void InitScripting() // Register the JavaScript interfaces with the runtime SColour::ScriptingInit(); CEntity::ScriptingInit(); + CTrigger::ScriptingInit(); CEntityTemplate::ScriptingInit(); + JSI_Sound::ScriptingInit(); CProfileNode::ScriptingInit(); @@ -789,7 +792,8 @@ void Shutdown(uint flags) TIMER_BEGIN("shutdown game scripting stuff"); delete &g_GameAttributes; - + + delete &g_TriggerManager; delete &g_FormationManager; delete &g_TechnologyCollection; delete &g_EntityFormationCollection; @@ -1035,6 +1039,9 @@ void Init(int argc, char* argv[], uint flags) g_EntityFormationCollection.loadTemplates(); g_TechnologyCollection.loadTechnologies(); new CFormationManager; + new CTriggerManager; + g_TriggerManager.LoadXML(CStr("scripts/TriggerSpecs.xml")); + g_ScriptingHost.RunScript("scripts/trigger_functions.js"); // CEntityManager is managed by CWorld //new CEntityManager; diff --git a/source/scripting/ScriptGlue.cpp b/source/scripting/ScriptGlue.cpp index f99ccabb9e..ab9f645f12 100644 --- a/source/scripting/ScriptGlue.cpp +++ b/source/scripting/ScriptGlue.cpp @@ -42,6 +42,7 @@ #include "simulation/EntityManager.h" #include "simulation/EntityTemplate.h" #include "simulation/FormationManager.h" +#include "simulation/TriggerManager.h" #include "simulation/LOSManager.h" #include "simulation/Scheduler.h" #include "simulation/Simulation.h" @@ -1265,6 +1266,33 @@ JSBool getGameTime( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, js return JS_TRUE; } +JSBool registerTrigger( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, + jsval* argv, jsval* rval ) +{ + JSU_REQUIRE_PARAMS_CPP(1); + CTrigger* trigger = ToNative( argv[0] ); + debug_assert( trigger ); + + g_TriggerManager.AddTrigger( trigger ); + *rval = JSVAL_NULL; + return JS_TRUE; +} +JSBool getTrigger( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, + jsval* argv, jsval* rval ) +{ + JSU_REQUIRE_PARAMS_CPP(1); + CStrW name = ToPrimitive( argv[0] ); + + if ( g_TriggerManager.m_TriggerMap.find(name) != g_TriggerManager.m_TriggerMap.end() ) + *rval = ToJSVal( g_TriggerManager.m_TriggerMap[name] ); + else + { + debug_printf("Invalid trigger name %ws", name); + *rval = JSVAL_NULL; + } + return JS_TRUE; +} + // Reveal map JSBool revealMap( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval ) { diff --git a/source/simulation/Entity.h b/source/simulation/Entity.h index c305c5c4a0..f54bca37cd 100644 --- a/source/simulation/Entity.h +++ b/source/simulation/Entity.h @@ -366,6 +366,7 @@ public: void DestroyAllNotifiers(); int findSector( int divs, float angle, float maxAngle, bool negative=true ); + jsval FlattenTerrain( JSContext* cx, uintN argc, jsval* argv ); CEntityFormation* GetFormation(); jsval GetFormationPenalty( JSContext* cx, uintN argc, jsval* argv ); diff --git a/source/simulation/EntityScriptInterface.cpp b/source/simulation/EntityScriptInterface.cpp index 0a3acfd855..0e5cc43d75 100644 --- a/source/simulation/EntityScriptInterface.cpp +++ b/source/simulation/EntityScriptInterface.cpp @@ -83,6 +83,7 @@ void CEntity::ScriptingInit() AddMethod( "onDamaged", 1 ); AddMethod( "getVisibleEntities", 0 ); AddMethod( "getDistance", 1 ); + AddMethod( "flattenTerrain", 0 ); AddClassProperty( L"traits.id.classes", (GetFn)&CEntity::getClassSet, (SetFn)&CEntity::setClassSet ); AddClassProperty( L"template", (CEntityTemplate* CEntity::*)&CEntity::m_base, false, (NotifyFn)&CEntity::loadBase ); @@ -922,3 +923,33 @@ int CEntity::GetAttackAction( HEntity target ) // Default return value is an invalid action ID return 0; } + +jsval CEntity::FlattenTerrain( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) +{ + float xDiff, yDiff; + CVector3D pos = m_position; + CVector2D pos2D(m_position.X, m_position.Z); + + if ( m_bounds->m_type == CBoundingObject::BOUND_CIRCLE ) + xDiff = yDiff = m_bounds->m_radius; + else + { + CBoundingBox* box = static_cast( m_bounds ); + xDiff = box->m_w; + yDiff = box->m_d; + } + //If we ever need to take rotated bounding boxes into account... + //CVector2D points[4] = { CVector2D(xDiff, yDiff), CVector2D(-xDiff, -yDiff), + //CVector2D(-xDiff, yDiff), CVector2D(xDiff, yDiff) }; + //CVector3D circle = points[0].FindCircumCircle(points[1], points[2]); + //float cos = cosf(m_graphics_orientation.Y), sin = sinf(m_graphics_orientation.Y); + + //for ( size_t i = 0; i<4; ++i ) + //{ + // points[i].x *= cos*circle.Z; + // points[i].y *= sin*circle.Z; + //} + g_Game->GetWorld()->GetTerrain()->FlattenArea(pos.X-xDiff, pos.X+xDiff, pos.Z-yDiff, pos.Z+yDiff); + snapToGround(); + return JS_TRUE; +} \ No newline at end of file diff --git a/source/simulation/TriggerManager.cpp b/source/simulation/TriggerManager.cpp new file mode 100644 index 0000000000..b1e2103712 --- /dev/null +++ b/source/simulation/TriggerManager.cpp @@ -0,0 +1,406 @@ +#include "precompiled.h" + +#include "TriggerManager.h" +#include "scripting/ScriptingHost.h" +#include "ps/XML/Xeromyces.h" +#include "ps/XML/XeroXMB.h" +#include "ps/CLogger.h" + +#define LOG_CATEGORY "Triggers" + + +CTrigger::CTrigger() +{ + m_timeLeft = m_timeDelay = m_maxRunCount = m_runCount = m_active = 0; +} +CTrigger::~CTrigger() +{ + +} +CTrigger::CTrigger(const CStrW& name, bool active, float delay, int maxRuns, const CStrW& condFunc, + const CStrW& effectFunc) +{ + m_name = name; + m_active = active; + m_timeLeft = m_timeDelay = delay; + m_runCount = 0; + m_maxRunCount = maxRuns; + + m_conditionFuncString = condFunc; + m_effectFuncString = effectFunc; + m_conditionFunction.Compile( L"TriggerCondition::" + m_name, condFunc ); + m_effectFunction.Compile( L"TriggerEffect::" + m_name, effectFunc ); + +} +CTrigger::CTrigger(const CStrW& name, bool active, float delay, int maxRuns, + CScriptObject& condFunc, CScriptObject& effectFunc) +{ + m_name = name; + m_active = active; + m_timeLeft = m_timeDelay = delay; + m_runCount = 0; + m_maxRunCount = maxRuns; + m_conditionFunction = condFunc; + m_effectFunction = effectFunc; +} + +JSBool CTrigger::Construct( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval ) +{ + JSU_REQUIRE_PARAMS_CPP(6); + + CScriptObject condFunc( JS_ValueToFunction(cx, argv[4]) ); + CScriptObject effectFunc( JS_ValueToFunction(cx, argv[5]) ); + CTrigger* newTrigger = new CTrigger( ToPrimitive( argv[0] ), + ToPrimitive( argv[1] ), + ToPrimitive( argv[2] ), + ToPrimitive( argv[3] ), + condFunc, effectFunc ); + + g_TriggerManager.AddTrigger(newTrigger); + *rval = OBJECT_TO_JSVAL( newTrigger->GetScript() ); + return JS_TRUE; +} + +CTrigger& CTrigger::operator= (const CTrigger& trigger) +{ + m_name = trigger.m_name; + m_active = trigger.m_active; + m_timeLeft = trigger.m_timeLeft; + m_timeDelay = trigger.m_timeDelay; + m_maxRunCount = trigger.m_maxRunCount; + m_conditionFunction = trigger.m_conditionFunction; + m_effectFunction = trigger.m_effectFunction; + + return *this; +} + +void CTrigger::ScriptingInit() +{ + AddProperty(L"runCount", &CTrigger::m_runCount, true); + AddProperty(L"name", &CTrigger::m_name, true); + AddProperty(L"maxRunCount", &CTrigger::m_maxRunCount); + AddProperty(L"timeDelay", &CTrigger::m_timeDelay); + + AddMethod( "activate", 0 ); + AddMethod( "deactivate", 0 ); + + CJSObject::ScriptingInit("Trigger", CTrigger::Construct, 6); +} + +bool CTrigger::ShouldFire() +{ + if ( !m_active ) + return false; + return m_conditionFunction.Run( this->GetScript() ); +} +bool CTrigger::Fire() +{ + m_effectFunction.Run( this->GetScript() ); + ++m_runCount; + m_timeLeft = m_timeDelay; + return (m_runCount < m_maxRunCount); +} + +jsval CTrigger::Activate(JSContext* UNUSED(cx), uint UNUSED(argc), jsval* UNUSED(argv)) +{ + m_active = true; + return JS_TRUE; +} +jsval CTrigger::Deactivate(JSContext* UNUSED(cx), uint UNUSED(argc), jsval* UNUSED(argv)) +{ + m_active = false; + return JS_TRUE; +} + + +void TriggerParameter::SetWindowData(const CStrW& _windowType, CStrW& windowPosition, CStrW& windowSize) +{ + windowPosition.Remove( CStr(L" ") ); + windowSize.Remove( CStr(L" ") ); + + xPos = windowPosition.BeforeFirst( CStrW(L",") ).ToInt(); + yPos = windowPosition.AfterFirst( CStrW(L",") ).ToInt(); + xSize = windowSize.BeforeFirst( CStrW(L",") ).ToInt(); + ySize = windowSize.AfterFirst( CStrW(L",") ).ToInt(); + windowType = _windowType; +} + +bool TriggerParameter::operator< ( const TriggerParameter& rhs ) const +{ + if ( row < rhs.row ) + return true; + else if ( row == rhs.row) + return ( column < rhs.column ); + + return false; +} +//===========Trigger Manager=========== + +CTriggerManager::CTriggerManager() +{ +} + +CTriggerManager::~CTriggerManager() +{ + DestroyEngineTriggers(); +} + +void CTriggerManager::DestroyEngineTriggers() +{ + for ( TriggerIter it = m_TriggerMap.begin(); it != m_TriggerMap.end(); ++it ) + { + SAFE_DELETE(it->second); + } + m_TriggerMap.clear(); +} +std::vector CTriggerManager::GetTriggerChoices(const std::wstring& name) +{ + return m_TriggerChoices[name]; +} + +void CTriggerManager::Update(float delta) +{ + std::list expired; + for ( TriggerIter it = m_TriggerMap.begin(); it != m_TriggerMap.end(); ++it ) + { + if ( it->second->ShouldFire() ) + { + it->second->m_timeLeft -= delta; + if ( it->second->m_timeLeft < 0 ) + { + if ( !it->second->Fire() ) + expired.push_back(it); + } + } + } + + //Remove all expired triggers + for ( std::list::iterator it = expired.begin(); it != expired.end(); ++it ) + { + delete (*it)->second;; + m_TriggerMap.erase(*it); + } +} + +void CTriggerManager::AddTrigger(CTrigger* trigger) +{ + m_TriggerMap[trigger->GetName()] = trigger; +} +void CTriggerManager::AddGroup(const MapTriggerGroup& group) +{ + m_GroupList.push_back(group); +} +void CTriggerManager::SetAllGroups(const std::list& groups) +{ + m_GroupList = groups; +} + +void CTriggerManager::AddTrigger(MapTriggerGroup& group, const MapTrigger& trigger) +{ + CStrW conditionBody(L"if ( "); + CStrW linkLogic[] = { CStrW(L""), CStrW(L" && "), CStrW(L" || ") }; + size_t i=0; + + 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 ( blockIt->not ) + conditionBody += CStrW(L"!"); + conditionBody += CStrW(L" ("); + } + + if ( it->not ) + conditionBody += CStrW(L"!"); + conditionBody += it->functionName; + conditionBody += CStrW(L"("); + for ( std::list::const_iterator it2 = it->parameters.begin(); it2 != + it->parameters.end(); ++it2 ) + { + conditionBody += *it2; + if ( std::distance(it2, it->parameters.end()) != 1 ) + conditionBody += CStrW(L", "); + } + + 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" )"); //closing if + conditionBody += CStrW(L" { return true; } "); + CStrW effectBody; + + for ( std::list::const_iterator it = trigger.effects.begin(); + it != trigger.effects.end(); ++it ) + { + effectBody += it->functionName; + effectBody += CStrW(L"("); + for ( std::list::const_iterator it2 = it->parameters.begin(); it2 != + it->parameters.end(); ++it2 ) + { + effectBody += *it2; + std::list::const_iterator endIt = it->parameters.end(); + if ( std::distance(it2, endIt) != 1 ) + effectBody += CStrW(L", "); + } + + effectBody += CStrW(L");"); + } + group.triggers.push_back(trigger); + CTrigger* newTrigger = new CTrigger(trigger.name, trigger.active, trigger.timeValue, + trigger.maxRunCount, conditionBody, effectBody); + AddTrigger(newTrigger); +} + + +//XML stuff + +bool CTriggerManager::LoadXML( const CStr& filename ) +{ + CXeromyces XeroFile; + + if ( XeroFile.Load( filename.c_str() ) != PSRETURN_OK ) + return false; + + #define EL(x) int el_##x = XeroFile.getElementID(#x) + #define AT(x) int at_##x = XeroFile.getAttributeID(#x) + + EL(condition); + EL(effect); + EL(definitions); + + #undef EL + #undef AT + + //Return false on any error to be extra sure the point gets across (FIX IT) + XMBElement root = XeroFile.getRoot(); + + if ( root.getNodeName() != el_definitions ) + return false; + + XERO_ITER_EL(root, rootChild) + { + int name = rootChild.getNodeName(); + if ( name == el_condition ) + { + if ( !loadTriggerSpec(rootChild, XeroFile, true) ) + { + LOG(ERROR, LOG_CATEGORY, "Error detected in Trigger XML tag. File: %s", filename); + return false; + } + } + else if ( name == el_effect ) + { + if ( !loadTriggerSpec(rootChild, XeroFile, false) ) + { + LOG(ERROR, LOG_CATEGORY, "Error detected in Trigger XML tag. File: %s", filename); + return false; + } + } + else + { + LOG(ERROR, LOG_CATEGORY, "Invalid tag in trigger XML. File: ws", filename); + return false; + } + } + return true; +} + +bool CTriggerManager::loadTriggerSpec( XMBElement condition, CXeromyces& XeroFile, bool isCondition ) +{ + #define EL(x) int el_##x = XeroFile.getElementID(#x) + #define AT(x) int at_##x = XeroFile.getAttributeID(#x) + + EL(parameter); + EL(window); + EL(inputtype); + EL(parameterorder); + EL(windowrow); + EL(choices); + + AT(type); + AT(position); + AT(name); + AT(size); + AT(function); + + #undef EL + #undef AT + + CTriggerSpec specStore; + specStore.functionName = CStrW( condition.getAttributes().getNamedItem(at_function) ); + specStore.displayName = CStrW( condition.getAttributes().getNamedItem(at_name) ); + int row = -1, column = -1; + + XERO_ITER_EL(condition, child) + { + int childID = child.getNodeName(); + + if ( childID == el_windowrow ) + { + ++row; + column = -1; + + XERO_ITER_EL(child, windowChild) + { + ++column; + TriggerParameter specParam(row, column); + specParam.name = windowChild.getAttributes().getNamedItem(at_name); + childID = windowChild.getNodeName(); + + if ( childID != el_parameter) + return false; + + XERO_ITER_EL(windowChild, parameterChild) + { + childID = parameterChild.getNodeName(); + + if ( childID == el_window ) + { + CStrW type( parameterChild.getAttributes().getNamedItem(at_type) ); + CStrW pos( parameterChild.getAttributes().getNamedItem(at_position) ); + CStrW size( parameterChild.getAttributes().getNamedItem(at_size) ); + specParam.SetWindowData(type, pos, size); + } + + else if ( childID == el_inputtype ) + specParam.inputType = CStrW( parameterChild.getText() ); + else if ( childID == el_parameterorder ) + specParam.parameterOrder = CStrW( parameterChild.getText() ).ToInt(); + else if ( childID == el_choices ) + { + std::vector choices; + CStrW comma(L","), input(parameterChild.getText()); + CStrW substr; + while ( (substr = input.BeforeFirst(comma)) != input ) + { + choices.push_back(substr); + input = input.AfterFirst(comma); + } + + choices.push_back(substr); //Last element has no comma + m_TriggerChoices[specStore.functionName + specParam.name] = choices; + } + else + return false; + } + specStore.AddParameter(specParam); + } + } + else + return false; + } + + if ( isCondition ) + m_ConditionSpecs.push_back(specStore); + else + m_EffectSpecs.push_back(specStore); + + return true; +} diff --git a/source/simulation/TriggerManager.h b/source/simulation/TriggerManager.h new file mode 100644 index 0000000000..5d5a55e135 --- /dev/null +++ b/source/simulation/TriggerManager.h @@ -0,0 +1,210 @@ +//======================================================== +//Andrew Decker aka pyrolink +//ajdecker1022@msn.com +//Description: Manages loading trigger specs (format in Atlas) and updating trigger objects +//======================================================= + +#ifndef TRIGGERMANAGER_INCLUDED +#define TRIGGERMANAGER_INCLUDED + +#include "ps/Singleton.h" +#include "scripting/ScriptableObject.h" +#include "simulation/ScriptObject.h" + +class CXeromyces; +class XMBElement; + +//Trigger storage - used be Atlas to keep track of different triggers + +struct MapTriggerCondition +{ + MapTriggerCondition() : linkLogic(0), not(false) { } + CStrW name, functionName, displayName; + std::list parameters; + int linkLogic; //0 = NONE, 1 = AND, 2 = OR + bool not; +}; + +struct MapTriggerEffect +{ + CStrW name, functionName, displayName; + std::list parameters; +}; + +struct MapTriggerLogicBlock +{ + MapTriggerLogicBlock(size_t i, bool _not=false) : index(i), not(_not) { } + size_t index; + bool not; + + bool operator< (const MapTriggerLogicBlock& block) const { return (index < block.index); } + bool operator== (const MapTriggerLogicBlock& block) const { return (index == block.index); } +}; + +struct MapTrigger +{ + MapTrigger() : active(false), maxRunCount(0), timeValue(0) { } + bool active; + int maxRunCount; + float timeValue; + CStrW name, groupName; + + std::set logicBlocks; //Indices of where "(" 's go before + std::set logicBlockEnds; //Indices of where ")" 's come after + std::list conditions; + std::list effects; + + void AddLogicBlock(bool not) { logicBlocks.insert( MapTriggerLogicBlock(conditions.size(), not) ); } + void AddLogicBlockEnd() { logicBlockEnds.insert( effects.size() ); } +}; + +struct MapTriggerGroup +{ + MapTriggerGroup() { } + MapTriggerGroup(CStrW _name, CStrW _parentName) : name(_name), parentName(_parentName) {} + + std::list triggers; + std::list childGroups; //Indices of children + CStrW name, parentName; + + bool operator== (const CStrW& _name) const { return (name == _name); } + bool operator== (const MapTriggerGroup& group) const { return (name == group.name); } +}; + + +struct copyIfRootChild +{ + copyIfRootChild(std::list& groupList) : m_groupList(groupList) {} + void operator() ( const MapTriggerGroup& group ) + { + if ( group.parentName == L"Triggers" ) + m_groupList.push_back(group); + } +private: + void operator= (const copyIfRootChild& UNUSED(group)) const {} + std::list& m_groupList; +}; + +//Triggers used by engine + +class CTrigger : public CJSObject +{ + int m_runCount; + CStrW m_conditionFuncString, m_effectFuncString, m_name, m_groupName; + CScriptObject m_effectFunction; + CScriptObject m_conditionFunction; + +public: + CTrigger(); + CTrigger(const CStrW& name, bool active, float delay, int maxRuns, const CStrW& condFunc, + const CStrW& effectFunc); + CTrigger(const CStrW& name, bool active, float delay, int maxRuns, + CScriptObject& condFunc, CScriptObject& effectFunc); + + CTrigger& operator= (const CTrigger& trigger); + ~CTrigger(); + + void SetFunctionBody(const CStrW& body); + const CStrW& GetConditionString() { return m_conditionFuncString; } + const CStrW& GetEffectString() { return m_effectFuncString; } + const CStrW& GetName() { return m_name; } + const CStrW& GetGroupName() { return m_groupName; } + + bool ShouldFire(); + //Returns false if trigger exceeds run count + bool Fire(); + + jsval Activate(JSContext* cx, uint argc, jsval* argv); + jsval Deactivate(JSContext* cx, uint argc, jsval* argv); + + static JSBool Construct( JSContext* cx, JSObject* obj, uint argc, jsval* argv, jsval* rval ); + static void ScriptingInit(); + + JSContext* m_cx; + + bool m_active; + //Changeable by Atlas or needed by manager, no additional affects required + int m_maxRunCount; + float m_timeLeft, m_timeDelay; + +}; + + +//*****Holds information specifying how conditions and effects should look like in atlas****** + +struct TriggerParameter +{ + TriggerParameter() {} + TriggerParameter(int _row, int _column) : row(_row), column(_column) {} + void SetWindowData(const CStrW& _windowType, CStrW& windowPosition, CStrW& windowSize); + //Sort parameters in the order they will be added to a sizer in atlas + bool operator< ( const TriggerParameter& rhs ) const; + + CStrW name, windowType, inputType; + int row, column, xPos, yPos, xSize, ySize, parameterOrder; +}; + +class CTriggerSpec +{ +public: + CTriggerSpec() {} + ~CTriggerSpec() {} + + void AddParameter(const TriggerParameter& param) { parameters.insert(param); } + const std::set& GetParameters() const { return parameters; } + + CStrW displayName, functionName; + +private: + + //Sorted by parameter order to make things easy for Atlas + std::set parameters; +}; +typedef CTriggerSpec CTriggerEffect; +typedef CTriggerSpec CTriggerCondition; + + +/******************************Trigger manager******************************/ + +class CTriggerManager : public Singleton +{ +public: + typedef std::map::iterator TriggerIter ; + + CTriggerManager(); + ~CTriggerManager(); + + //Returns false on detection of error + bool LoadXML( const CStr& filename ); + void Update(float delta); + + //Add simulation trigger (probably only called from JS) + void AddTrigger(CTrigger* trigger); + void AddTrigger(MapTriggerGroup& group, const MapTrigger& trigger); + void AddGroup(const MapTriggerGroup& group); + void SetAllGroups(const std::list& groups); + void DestroyEngineTriggers(); + + const std::list& GetAllConditions() const { return m_ConditionSpecs; } + const std::list& GetAllEffects() const { return m_EffectSpecs; } + const std::list& GetAllTriggerGroups() const { return m_GroupList; } + std::vector GetTriggerChoices(const std::wstring& name); + + std::map m_TriggerMap; //Simulation triggers - used in engine + +private: + bool loadTriggerSpec( XMBElement condition, CXeromyces& XeroFile, bool isCondition ); + + //Contains choices for trigger choice box parameters, with key = spec.funcName+paramName + std::map > m_TriggerChoices; + + //Holds information which descibes trigger layout in atlas + std::list m_GroupList; + std::list m_ConditionSpecs; + std::list m_EffectSpecs; + +}; + +#define g_TriggerManager CTriggerManager::GetSingleton() + +#endif //ifndef TRIGGERMANAGER_INCLUDED diff --git a/source/tools/atlas/AtlasUI/ArchiveViewer/ArchiveViewer.cpp b/source/tools/atlas/AtlasUI/ArchiveViewer/ArchiveViewer.cpp index b0055baf23..78da2eea3b 100644 --- a/source/tools/atlas/AtlasUI/ArchiveViewer/ArchiveViewer.cpp +++ b/source/tools/atlas/AtlasUI/ArchiveViewer/ArchiveViewer.cpp @@ -306,7 +306,7 @@ void ArchiveViewer::UpdateFileList() for (size_t i = 0; i < files.size(); ++i) { - m_CachedFileData[i].Name = utf16tow(files[i].filename); + m_CachedFileData[i].Name = wxString( utf16tow(files[i].filename).c_str() ); m_CachedFileData[i].NameLower = m_CachedFileData[i].Name.Lower(); m_CachedFileData[i].Size = wxString::Format(_T("%d"), files[i].filesize); @@ -441,7 +441,7 @@ void ArchiveViewer::ExtractFiles(bool onlySelected) for (size_t sel = 0; sel < selection.size(); ++sel) { const BAREntry& file = files[selection[sel]]; - wxString filename = utf16tow(file.filename); + wxString filename = wxString( utf16tow(file.filename).c_str() ); int lastSlash = filename.Find(_T('\\'), true); if (lastSlash != -1) dirs.insert(filename.Mid(0, lastSlash+1).c_str()); @@ -480,7 +480,7 @@ void ArchiveViewer::ExtractFiles(bool onlySelected) // Append name-in-archive to target root directory, one dir // at a time, calling mkdir at each step if necessary - wxFileName filePath (utf16tow(file.filename), wxPATH_WIN); + wxFileName filePath (wxString( utf16tow(file.filename).c_str() ), wxPATH_WIN); filePath.MakeAbsolute(rootDir.GetPath()); // Output to disk @@ -551,7 +551,7 @@ void ArchiveViewer::UpdatePreview(long item) const BAREntry& file = m_BARReader->GetFileList()[ m_FileListCtrl->GetItemData(item) ]; SeekableInputStream* str = m_BARReader->GetFile(file); - m_PreviewWindow->PreviewFile(utf16tow(file.filename), *str); + m_PreviewWindow->PreviewFile(wxString(utf16tow(file.filename).c_str()), *str); delete str; } diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/SectionLayout.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/SectionLayout.cpp index eae5b6accc..dc25ea13ca 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/SectionLayout.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/SectionLayout.cpp @@ -12,6 +12,7 @@ #include "Sections/Object/Object.h" #include "Sections/Environment/Environment.h" #include "Sections/Cinematic/Cinematic.h" +#include "Sections/Trigger/Trigger.h" #include "General/Datafile.h" @@ -270,7 +271,8 @@ void SectionLayout::Build() ADD_SIDEBAR(TerrainSidebar, _T("terrain.png"), _("Terrain")); ADD_SIDEBAR(ObjectSidebar, _T("object.png"), _("Object")); ADD_SIDEBAR(EnvironmentSidebar, _T("environment.png"), _("Environment")); - ADD_SIDEBAR(CinematicSidebar, _T("cinematic.png"), _("Cinematics")); + ADD_SIDEBAR(CinematicSidebar, _T("cinematic.png"), _("Cinema")); + ADD_SIDEBAR(TriggerSidebar, _T("trigger.png"), _("Trigger")); #undef ADD_SIDEBAR diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Cinematic/Cinematic.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Cinematic/Cinematic.cpp index f61365cced..a805960239 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Cinematic/Cinematic.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Cinematic/Cinematic.cpp @@ -90,8 +90,7 @@ public: qGetCameraInfo qry; qry.Post(); sCameraInfo info = qry.info; - m_Sidebar->AddTrack(info.rX, info.rY, info.rZ, msgString, - GetItemCount()-1); + m_Sidebar->AddTrack(info.rX, info.rY, info.rZ, msgString, GetItemCount()-1); } void DeleteTrack() { diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/LightControl.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/LightControl.cpp index 24e3229766..e8ddf58ca5 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/LightControl.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/LightControl.cpp @@ -55,7 +55,7 @@ public: float rdotl = rx*lx + ry*ly + rz*lz; int diffuse = (int)std::max(0.f, ndotl*128.f); - int specular = (int)std::min(255., 64.f*pow(std::max(0.f, rdotl), 16.f)); + int specular = (int)std::min(255.f, 64.f*pow(std::max(0.f, rdotl), 16.f)); imgData[0] = std::min(64+diffuse+specular, 255); imgData[1] = std::min(48+diffuse+specular, 255); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Trigger/Trigger.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Trigger/Trigger.cpp new file mode 100644 index 0000000000..6f1726d99e --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Trigger/Trigger.cpp @@ -0,0 +1,1426 @@ +#include "stdafx.h" + +#include "Trigger.h" +#include "GameInterface/Messages.h" +#include "CustomControls/Buttons/ActionButton.h" +#include "ScenarioEditor/Tools/Common/Tools.h" +#include + +using namespace AtlasMessage; + +BEGIN_EVENT_TABLE(TriggerSidebar, Sidebar) +EVT_TREE_BEGIN_DRAG(wxID_ANY, TriggerSidebar::onTreeDrag) +EVT_TREE_END_LABEL_EDIT(wxID_ANY, TriggerSidebar::onTreeNameChange) +EVT_TREE_SEL_CHANGED(wxID_ANY, TriggerSidebar::onTreeSelChange) +EVT_LIST_ITEM_SELECTED(TriggerSidebar::ID_CondList, TriggerSidebar::onCondSelect) +EVT_LIST_ITEM_SELECTED(TriggerSidebar::ID_EffectList, TriggerSidebar::onEffectSelect) +EVT_NOTEBOOK_PAGE_CHANGED(wxID_ANY, TriggerSidebar::onPageChange) +END_EVENT_TABLE() + + +class TriggerTreeCtrl : public wxTreeCtrl +{ +public: + TriggerTreeCtrl(TriggerSidebar* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, long style = wxTR_HAS_BUTTONS ) + : wxTreeCtrl(parent, id, pos, size, style), m_Sidebar(parent) { } + + void onClick(wxMouseEvent& evt); + +private: + TriggerSidebar* m_Sidebar; + DECLARE_EVENT_TABLE(); +}; + +BEGIN_EVENT_TABLE(TriggerTreeCtrl, wxTreeCtrl) +EVT_LEFT_DOWN(TriggerTreeCtrl::onClick) +END_EVENT_TABLE() + + +struct LogicBlockHelper +{ + LogicBlockHelper() { index = -1; end = false; } + LogicBlockHelper(int _index, bool _end) : index(_index), end(_end) {} + int index; + bool end; + + bool operator< ( const LogicBlockHelper& rhs ) const + { + return index < rhs.index; + } + bool operator== ( const LogicBlockHelper& rhs ) const + { + return index == rhs.index; + } +}; +class TriggerSpecText : public wxTextCtrl +{ + typedef void (*callback)(void* data, std::wstring input, int parameter); + +public: + TriggerSpecText(wxWindow* parent, std::wstring label, const wxPoint& pos, const wxSize& size, + int parameter, std::wstring dataType, callback func, void* data) + : wxTextCtrl(parent, wxID_ANY, wxString( label.c_str() ), pos, size, wxTE_PROCESS_ENTER), + m_DataType(dataType), m_Parameter(parameter), m_Data(data), m_Callback(func) + { + } + void onTextEnter(wxCommandEvent& WXUNUSED(evt)); + + //Disallow invalid input + bool VerifyInput(std::wstring& input) + { + std::wstringstream stream(input); + if ( m_DataType == L"int" ) + { + int test; + stream >> test; + return !stream.fail(); + } + else if ( m_DataType == L"real" ) + { + float test; + stream >> test; + return !stream.fail(); + } + else if ( m_DataType == L"bool" ) + { + bool test; + stream >> test; + return !stream.fail(); + } + else if ( m_DataType == L"string" ) + { + //Make strings appear as strings to javascript + std::wstring quote(L"\""); + input.insert(0, quote); + input.append(quote); + return true; + } + else + { + wxFAIL_MSG(L"Invalid input type for trigger specification"); + return false; + } + } +private: + void* m_Data; + int m_Parameter; + std::wstring m_DataType; + callback m_Callback; + + DECLARE_EVENT_TABLE(); + +}; +BEGIN_EVENT_TABLE(TriggerSpecText, wxTextCtrl) +EVT_TEXT_ENTER(wxID_ANY, TriggerSpecText::onTextEnter) +END_EVENT_TABLE() + +class TriggerSpecChoice : public wxChoice +{ + typedef void (*callback)(void* data, std::wstring input, int parameter); + +public: + TriggerSpecChoice(wxWindow* parent, std::wstring label, const wxPoint& pos, + const wxSize& size, const wxArrayString& strings, int parameter, callback func, void* data) + : wxChoice(parent, wxID_ANY, pos, size, strings), m_Callback(func), m_Data(data), m_Parameter(parameter) + { + } + + void onChoice(wxCommandEvent& evt) + { + (*m_Callback)(m_Data, std::wstring( evt.GetString().wc_str() ), m_Parameter); + } + +private: + callback m_Callback; + int m_Parameter; + void* m_Data; + + DECLARE_EVENT_TABLE(); +}; + +BEGIN_EVENT_TABLE(TriggerSpecChoice, wxChoice) +EVT_CHOICE(wxID_ANY, TriggerSpecChoice::onChoice) +END_EVENT_TABLE() + +class TriggerListCtrl : public wxListCtrl +{ +public: + TriggerListCtrl(wxWindow* parent, TriggerSidebar* sidebar, bool condition, wxWindowID id, const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, long style = wxLC_ICON) + : wxListCtrl(parent, id, pos, size, style), m_Sidebar(sidebar), m_Condition(condition) + { + } + void onClick(wxMouseEvent& evt); + + TriggerSidebar* m_Sidebar; + bool m_Condition; +private: + DECLARE_EVENT_TABLE(); +}; +BEGIN_EVENT_TABLE(TriggerListCtrl, wxListCtrl) +EVT_LEFT_DOWN(TriggerListCtrl::onClick) +END_EVENT_TABLE() + + +class TriggerPage : public wxPanel +{ +public: + TriggerPage(wxWindow* parent, TriggerSidebar* sidebar, long ID, wxString title, bool condition) + : wxPanel(parent), m_Sidebar(sidebar), m_Condition(condition) + + { + wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); + + m_List = new TriggerListCtrl(this, sidebar, condition, ID, wxDefaultPosition, + wxSize(132, 210), wxLC_REPORT | wxLC_SINGLE_SEL); + m_List->InsertColumn(0, title, wxLIST_FORMAT_LEFT, 100); + + sizer->Add(m_List); + SetSizer(sizer); + } + + wxListCtrl* m_List; + TriggerSidebar* m_Sidebar; + bool m_Condition; +}; + + + +class TriggerItemData : public wxTreeItemData, public sTrigger +{ +public: + TriggerItemData(TriggerSidebar* sidebar, const std::wstring& name, bool group) + : m_Sidebar(sidebar), sTrigger(name), m_Group(group), m_CondCount(0), m_EffectCount(0) {} + TriggerItemData(TriggerSidebar* sidebar, const sTrigger& trigger, bool group) + : m_Sidebar(sidebar), sTrigger(trigger), m_Group(group), m_CondCount(0), m_EffectCount(0) {} + + TriggerSidebar* m_Sidebar; + size_t m_CondCount, m_EffectCount; + bool m_Group; + std::list m_BlockIndices, m_BlockEndIndices; + + void AddBlock(const int block, const int index) + { + std::vector copy = *logicBlocks; + copy.push_back(block); + logicBlocks = copy; + m_BlockIndices.push_back(index); + } + void AddBlockEnd(const int block, const int index) + { + std::vector copy = *logicBlockEnds; + copy.push_back(block); + logicBlockEnds = copy; + m_BlockEndIndices.push_back(index); + } + + void ResetBlockIndices() + { + std::vector newLogicBlocks, newLogicBlockEnds; + m_BlockIndices.clear(); + m_BlockEndIndices.clear(); + int conditionCount = 0; + + for (int i=0; im_ConditionPage->m_List->GetItemCount(); ++i) + { + if ( m_Sidebar->m_ConditionPage->m_List->GetItemText(i) == m_Sidebar->m_LogicBlockString ) + { + newLogicBlocks.push_back(conditionCount); + m_BlockIndices.push_back(i); + } + + //Block ends belong to current condition, hence conditionCount-1 + else if ( m_Sidebar->m_ConditionPage->m_List->GetItemText(i) == m_Sidebar->m_LogicBlockEndString ) + { + if ( conditionCount == 0 ) + newLogicBlockEnds.push_back(0); + else + newLogicBlockEnds.push_back(conditionCount-1); + m_BlockEndIndices.push_back(i); + } + else + ++conditionCount; + } + logicBlocks = newLogicBlocks; + logicBlockEnds = newLogicBlockEnds; + } +}; + + +void onTriggerParameter(void* data, std::wstring paramString, int parameter); + + +class TriggerBottomBar : public wxPanel +{ + enum { ID_TimeEdit, ID_CondNameEdit, ID_EffectNameEdit, ID_TriggerNameEdit, ID_RunsEdit, + ID_EffectChoice, ID_CondChoice, + ID_TimeRadio, ID_LogicRadio, + ID_NotCheck, ID_ActiveCheck }; + +public: + enum { NO_VIEW, TRIGGER_VIEW, CONDITION_VIEW, EFFECT_VIEW }; + + TriggerBottomBar(TriggerSidebar* sidebar, wxWindow* parent) + : wxPanel(parent), m_Sidebar(sidebar) + { + m_Sizer = new wxBoxSizer(wxHORIZONTAL); + SetSizer(m_Sizer); + m_DependentStatus = NO_VIEW; + } + + int GetDependentStatus() + { + return m_DependentStatus; + } + void SetSpecs(std::vector conditions, std::vector effects) + { + m_ConditionSpecs = conditions; + m_EffectSpecs = effects; + } + + wxArrayString GetConditionNames() + { + wxArrayString ret; + for ( size_t i = 0; i < m_ConditionSpecs.size(); ++i ) + ret.Add( wxString(m_ConditionSpecs[i].displayName.c_str()) ); + return ret; + } + wxArrayString GetEffectNames() + { + wxArrayString ret; + for ( size_t i = 0; i < m_EffectSpecs.size(); ++i ) + ret.Add( wxString(m_EffectSpecs[i].displayName.c_str()) ); + return ret; + } + + void onEffectChoice(wxCommandEvent& evt) + { + //Retrieve specification corresponding to selection + if ( m_Sidebar->m_SelectedEffect != -1 ) + { + std::vector::iterator it = std::find( m_EffectSpecs.begin(), + m_EffectSpecs.end(), std::wstring(evt.GetString()) ); + DisplayTriggerSpec(*it); + } + } + + void onCondChoice(wxCommandEvent& evt) + { + if ( m_Sidebar->m_SelectedCond != -1 ) + { + std::vector::iterator it = std::find( m_ConditionSpecs.begin(), + m_ConditionSpecs.end(), std::wstring(evt.GetString()) ); + DisplayTriggerSpec(*it); + } + } + + void onTimeEnter(wxCommandEvent& WXUNUSED(evt)) + { + float fValue; + std::wstringstream stream( std::wstring(m_TimeEdit->GetValue()) ); + stream >> fValue; + + if ( stream.fail() ) + { + wxBell(); + return; + } + + m_Sidebar->GetSelectedItemData()->timeValue = fValue; + wxString value = wxString::Format(L"%.2f", fValue); + m_TimeEdit->SetValue(value); + m_Sidebar->UpdateEngineData(); + } + + void onConditionEnter(wxCommandEvent& WXUNUSED(evt)) + { + if ( m_Sidebar->m_SelectedCond == -1 ) + return; + + std::vector conditions = *m_Sidebar->GetSelectedItemData()->conditions; + int condition = m_Sidebar->GetConditionCount(m_Sidebar->m_SelectedCond); + + if ( condition == 0 ) + conditions[0].name = std::wstring( m_ConditionEdit->GetValue() ); + else + conditions[condition-1] = std::wstring( m_ConditionEdit->GetValue() ); + + m_Sidebar->GetSelectedItemData()->conditions = conditions; + m_Sidebar->UpdateLists(); + m_Sidebar->UpdateEngineData(); + } + void onEffectEnter(wxCommandEvent& WXUNUSED(evt)) + { + if ( m_Sidebar->m_SelectedEffect== -1 ) + return; + + std::vector effects = *m_Sidebar->GetSelectedItemData()->effects; + effects[m_Sidebar->m_SelectedEffect].name = std::wstring( m_EffectEdit->GetValue() ); + m_Sidebar->GetSelectedItemData()->effects = effects; + m_Sidebar->UpdateLists(); + m_Sidebar->UpdateEngineData(); + } + void onTriggerEnter(wxCommandEvent& WXUNUSED(evt)) + { + TriggerItemData* data = m_Sidebar->GetSelectedItemData(); + if ( data == NULL || m_Sidebar->m_TriggerTree->GetSelection() == m_Sidebar->m_TriggerTree->GetRootItem() ) + return; + + wxString name = m_TriggerEdit->GetValue(); + data->name = std::wstring(name); + m_Sidebar->m_TriggerTree->SetItemText(m_Sidebar->m_TriggerTree->GetSelection(), name); + m_Sidebar->UpdateEngineData(); + } + + void onRunsEnter(wxCommandEvent& WXUNUSED(evt)) + { + int iValue; + std::wstringstream stream( std::wstring(m_RunsEdit->GetValue()) ); + stream >> iValue; + + if ( stream.fail() ) + { + wxBell(); + return; + } + + m_Sidebar->GetSelectedItemData()->maxRuns = iValue; + m_Sidebar->UpdateEngineData(); + } + void onLogicRadio(wxCommandEvent& evt) + { + if ( m_Sidebar->m_SelectedCond == -1 ) + return; + std::vector conditions = *m_Sidebar->GetSelectedItemData()->conditions; + int condition = m_Sidebar->GetConditionCount(m_Sidebar->m_SelectedCond); + conditions[condition-1].linkLogic = evt.GetInt() + 1; + + m_Sidebar->GetSelectedItemData()->conditions = conditions; + m_Sidebar->UpdateLists(); + m_Sidebar->UpdateEngineData(); + } + void onActiveCheck(wxCommandEvent& evt) + { + m_Sidebar->GetSelectedItemData()->active = (evt.GetInt() == 1); + m_Sidebar->UpdateEngineData(); + } + void onNotCheck(wxCommandEvent& evt) + { + if ( m_Sidebar->m_SelectedCond == -1 ) + return; + std::vector conditions = *m_Sidebar->GetSelectedItemData()->conditions; + int condition = m_Sidebar->GetConditionCount(m_Sidebar->m_SelectedCond); + bool value = (evt.GetInt() == 1); + conditions[condition-1].not = value; + + m_Sidebar->GetSelectedItemData()->conditions = conditions; + m_Sidebar->UpdateLists(); + m_Sidebar->UpdateEngineData(); + } + + void DisplayTriggerSpec(const sTriggerSpec& spec) + { + if ( m_Sizer->Detach(m_ParameterSizer) ) + { + delete m_ParameterSizer; + m_Sizer->Layout(); + Layout(); + } + + //m_ParameterSizer = new wxStaticBoxSizer(wxVERTICAL, this, L"Parameters"); + m_ParameterSizer = new wxBoxSizer(wxVERTICAL); + wxBoxSizer* hRow = NULL; + std::vector parameters = *spec.parameters; + std::vector stringParameters; + int row = -1; + + //Set parameter data to new data if change is needed + if ( m_Sidebar->m_Notebook->GetCurrentPage() == m_Sidebar->m_ConditionPage ) + { + std::vector conditions = *m_Sidebar->GetSelectedItemData()->conditions; + int condition = m_Sidebar->GetConditionCount(m_Sidebar->m_SelectedCond) - 1 ; + if ( *conditions[condition].displayName != *spec.displayName ) + { + std::vector newParameters(parameters.size()); + conditions[condition].parameters = newParameters; + conditions[condition].displayName = *spec.displayName; + conditions[condition].functionName = *spec.functionName; + m_Sidebar->GetSelectedItemData()->conditions = conditions; + m_Sidebar->UpdateEngineData(); + } + stringParameters = *(*m_Sidebar->GetSelectedItemData()->conditions) + [m_Sidebar->m_SelectedCond].parameters; + } + else + { + std::vector effects = *m_Sidebar->GetSelectedItemData()->effects; + if ( *effects[m_Sidebar->m_SelectedEffect].displayName != *spec.displayName ) + { + std::vector newParameters(parameters.size()); + effects[m_Sidebar->m_SelectedEffect].parameters = newParameters; + effects[m_Sidebar->m_SelectedEffect].displayName = *spec.displayName; + effects[m_Sidebar->m_SelectedEffect].functionName = *spec.functionName; + m_Sidebar->GetSelectedItemData()->effects = effects; + m_Sidebar->UpdateEngineData(); + } + stringParameters = *(*m_Sidebar->GetSelectedItemData()->effects) + [m_Sidebar->m_SelectedEffect].parameters; + } + + //Add all parameters to sizer + for ( std::vector::iterator it=parameters.begin(); it!=parameters.end(); ++it ) + { + if ( it->row != row ) + { + row = it->row; + hRow = new wxBoxSizer(wxHORIZONTAL); + m_ParameterSizer->Add(hRow); + } + + if ( *it->windowType == std::wstring(L"text") ) + { + hRow->Add( new wxStaticText(this, wxID_ANY, wxString( (*it->name).c_str() ), + wxPoint(it->xPos, it->yPos)) ); + wxTextCtrl* text = new TriggerSpecText(this, L"", wxDefaultPosition, wxSize(it->xSize, it->ySize), + it->parameterOrder, *it->inputType, &onTriggerParameter, this); + + hRow->Add( text, 0, wxLEFT, 5 ); + wxString fill( stringParameters[it->parameterOrder].c_str() ); + + //Trim quotes + if ( *it->inputType == L"string" && fill.size() > 0 ) + { + fill.erase(0, 1); + fill.erase(fill.size()-1, 1); + } + text->SetValue(fill); + } + + else if ( *it->windowType == std::wstring(L"choice") ) + { + qGetTriggerChoices qChoices(*spec.functionName + *it->name); + qChoices.Post(); + std::vector choices = *qChoices.choices; + wxArrayString strings; + + for ( size_t i = 0; i < choices.size(); ++i ) + strings.Add( wxString(choices[i].c_str()) ); + + hRow->Add( new wxStaticText(this, wxID_ANY, wxString( (*it->name).c_str() ), + wxPoint(it->xPos, it->yPos)) ); + wxChoice* choice = new TriggerSpecChoice( this, L"", wxDefaultPosition, wxSize(it->xSize, it->ySize), + strings, it->parameterOrder, &onTriggerParameter, this ); + + hRow->Add(choice); + choice->SetStringSelection( wxString(stringParameters[it->parameterOrder].c_str()) ); + } + else + { + wxFAIL_MSG(L"Invalid window type for trigger specification"); + //do something else... + } + } + + + //(If nothing was added, it won't be automatically delted) + if ( row < 0 ) + { + delete hRow; + delete m_ParameterSizer; + } + else + m_Sizer->Add(m_ParameterSizer, 0, wxLEFT, 5); + + m_Sizer->Layout(); + Layout(); + } + + void FillConditionData() + { + if ( m_Sidebar->m_SelectedCond== -1 ) + return; + + TriggerItemData* itemData = m_Sidebar->GetSelectedItemData(); + int iCondition = m_Sidebar->GetConditionCount(m_Sidebar->m_SelectedCond); + sTriggerCondition condition = (*itemData->conditions)[iCondition-1]; + wxString display( (*condition.displayName).c_str() ); + m_ConditionEdit->SetValue( wxString(condition.name.c_str()) ); + + if ( display != L"" ) + { + std::vector::iterator it = std::find( m_ConditionSpecs.begin(), + m_ConditionSpecs.end(), std::wstring(display) ); + if ( it != m_ConditionSpecs.end() ) + { + m_ConditionChoice->SetStringSelection(display); + DisplayTriggerSpec(*it); + } + } + else + m_ConditionChoice->SetStringSelection(display); + + if ( condition.linkLogic == 0 || condition.linkLogic == 1 ) + m_LogicRadio->SetSelection(0); + else + m_LogicRadio->SetSelection(1); + + m_NotCheck->SetValue(condition.not); + } + + void FillEffectData() + { + TriggerItemData* itemData = m_Sidebar->GetSelectedItemData(); + sTriggerEffect effect = (*itemData->effects)[m_Sidebar->m_SelectedEffect]; + wxString display( (*effect.displayName).c_str() ); + + m_EffectEdit->SetValue( wxString(effect.name.c_str()) ); + if ( display != L"" ) + { + std::vector::iterator it = std::find( m_EffectSpecs.begin(), + m_EffectSpecs.end(), std::wstring(display) ); + if ( it != m_EffectSpecs.end() ) + { + m_EffectChoice->SetStringSelection(display); + DisplayTriggerSpec(*it); + } + } + //m_TimeRadio->SetSelection(effect.loop); + + float timeVal = itemData->timeValue; + wxString value = wxString::Format(L"%.2f", timeVal); + m_TimeEdit->SetValue(value); + } + + void FillTriggerData() + { + if ( m_DependentStatus != TRIGGER_VIEW ) + return; + TriggerItemData* itemData = m_Sidebar->GetSelectedItemData(); + m_TriggerEdit->SetValue( wxString( (*itemData->name).c_str()) ); + m_ActiveCheck->SetValue(itemData->active); + int runs = itemData->maxRuns; + m_RunsEdit->SetValue( wxString( wxString::Format(L"%d", runs)) ); + + } + + void ToEffectView() + { + DestroyChildren(); + m_Sizer = new wxBoxSizer(wxHORIZONTAL); + m_DependentSizer = new wxStaticBoxSizer(wxVERTICAL, this, wxString(L"Trigger Editor")); + SetSizer(m_Sizer, true); + + m_DependentSizer = new wxStaticBoxSizer(wxVERTICAL, this, wxString(L"Trigger Editor")); + wxStaticText* name = new wxStaticText(this, wxID_ANY, wxString(L"Name:")); + wxStaticText* effect = new wxStaticText(this, wxID_ANY, wxString(L"Effect:")); + m_EffectEdit = new wxTextCtrl(this, ID_EffectNameEdit, _T(""), wxDefaultPosition, + wxSize(100, 18), wxTE_PROCESS_ENTER); + + wxArrayString effectNames = GetEffectNames(); + wxString radioChoice[] = { wxString(L"Delay"), wxString(L"Loop") }; + m_EffectChoice = new wxChoice(this, ID_EffectChoice, wxDefaultPosition, wxSize(100, 13), effectNames); + + m_TimeRadio = new wxRadioBox(this, ID_TimeRadio, _T("Execution type"), + wxDefaultPosition, wxDefaultSize, 2, radioChoice, 2, wxRA_SPECIFY_COLS); + + wxStaticText* time = new wxStaticText(this, wxID_ANY, wxString(L"Time:")); + m_TimeEdit = new wxTextCtrl(this, ID_TimeEdit, _T(""), wxDefaultPosition, + wxSize(100, 18), wxTE_PROCESS_ENTER); + + wxBoxSizer* hNameHolder = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer* hEffectHolder = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer* hTimeHolder = new wxBoxSizer(wxHORIZONTAL); + + hNameHolder->Add(name); + hNameHolder->Add(m_EffectEdit, 0, wxLEFT, 5); + hEffectHolder->Add(effect); + hEffectHolder->Add(m_EffectChoice, 0, wxLEFT, 5); + hTimeHolder->Add(time); + hTimeHolder->Add(m_TimeEdit, 0, wxLEFT, 5); + + m_DependentSizer->Add(hNameHolder, 0, wxTOP, 5); + m_DependentSizer->Add(hEffectHolder, 0, wxTOP, 5); + m_DependentSizer->Add(m_TimeRadio, 0, wxTOP | wxALIGN_CENTER, 10); + m_DependentSizer->Add(hTimeHolder, 0, wxTOP | wxALIGN_CENTER, 5); + + m_Sizer->Add(m_DependentSizer); + m_Sizer->Layout(); + Layout(); + m_DependentStatus = EFFECT_VIEW; + } + + void ToConditionView() + { + DestroyChildren(); + m_Sizer = new wxBoxSizer(wxHORIZONTAL); + m_DependentSizer = new wxStaticBoxSizer(wxVERTICAL, this, wxString(L"Trigger Editor")); + SetSizer(m_Sizer, true); + + wxStaticText* name = new wxStaticText(this, wxID_ANY, wxString(L"Name:")); + wxStaticText* condition = new wxStaticText(this, wxID_ANY, wxString(L"Condition:")); + m_ConditionEdit = new wxTextCtrl(this, ID_CondNameEdit, _T(""), wxDefaultPosition, + wxSize(100, 18), wxTE_PROCESS_ENTER); + + wxArrayString conditionNames = GetConditionNames(); + wxString radioChoice[] = { wxString(L"And"), wxString(L"Or") }; + m_ConditionChoice = new wxChoice(this, ID_CondChoice, wxDefaultPosition, + wxSize(100, 13), conditionNames); + + m_LogicRadio = new wxRadioBox(this, ID_LogicRadio, _T("Link logic:"), + wxDefaultPosition, wxDefaultSize, 2, radioChoice, 2, wxRA_SPECIFY_COLS); + + m_NotCheck = new wxCheckBox(this, ID_NotCheck, wxString(L"Not: ")); + + wxBoxSizer* hNameHolder = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer* hConditionHolder = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer* hLogicHolder = new wxBoxSizer(wxHORIZONTAL); + + hNameHolder->Add(name); + hNameHolder->Add(m_ConditionEdit, 0, wxLEFT | wxALIGN_CENTER, 5); + hConditionHolder->Add(condition); + hConditionHolder->Add(m_ConditionChoice, 0, wxLEFT | wxALIGN_CENTER, 5); + hLogicHolder->Add(m_LogicRadio, 0, 0, 5); + hLogicHolder->Add(m_NotCheck, 0, wxLEFT | wxALIGN_CENTER, 5); + + m_DependentSizer->Add(hNameHolder, 0, wxTOP, 5); + m_DependentSizer->Add(hConditionHolder, 0, wxTOP, 5); + m_DependentSizer->Add(hLogicHolder, 0, wxALIGN_CENTER | wxTOP, 10); + + m_Sizer->Add(m_DependentSizer); + m_Sizer->Layout(); + Layout(); + m_DependentStatus = CONDITION_VIEW; + } + + void ToTriggerView() + { + DestroyChildren(); + m_Sizer = new wxBoxSizer(wxHORIZONTAL); + m_DependentSizer = new wxStaticBoxSizer(wxVERTICAL, this, wxString(L"Trigger Editor")); + SetSizer(m_Sizer, true); + + wxStaticText* name = new wxStaticText(this, wxID_ANY, wxString(L"Name:")); + m_TriggerEdit = new wxTextCtrl(this, ID_TriggerNameEdit, _T(""), wxDefaultPosition, + wxSize(100, 18), wxTE_PROCESS_ENTER); + + wxStaticText* runs = new wxStaticText(this, wxID_ANY, wxString(L"Maximum runs:")); + m_RunsEdit = new wxTextCtrl(this, ID_RunsEdit, wxString(L"-1"), wxDefaultPosition, wxSize(100, 18)); + m_ActiveCheck = new wxCheckBox(this, ID_ActiveCheck, wxString(L"Active: ")); + + wxBoxSizer* nameHolder = new wxBoxSizer(wxHORIZONTAL), *runsHolder = new wxBoxSizer(wxHORIZONTAL); + + nameHolder->Add(name); + nameHolder->Add(m_TriggerEdit, 0, wxLEFT, 5); + runsHolder->Add(runs); + runsHolder->Add(m_RunsEdit, 0, wxLEFT, 5); + + m_DependentSizer->Add(nameHolder); + m_DependentSizer->Add(runsHolder, 0, wxTOP, 5); + m_DependentSizer->Add(m_ActiveCheck, 0, wxTOP, 5); + + m_Sizer->Add(m_DependentSizer); + m_Sizer->Layout(); + Layout(); + m_DependentStatus = TRIGGER_VIEW; + } + void ToNoView() + { + if ( m_DependentStatus == NO_VIEW ) + return; + DestroyChildren(); + m_DependentStatus = NO_VIEW; + } + TriggerSidebar* m_Sidebar; + +private: + + wxBoxSizer* m_Sizer, *m_ParameterSizer; + wxStaticBoxSizer* m_DependentSizer; //dependent = effect/condition + + wxTextCtrl* m_TimeEdit, *m_ConditionEdit, *m_EffectEdit, *m_TriggerEdit, *m_RunsEdit; + wxCheckBox* m_ActiveCheck, *m_NotCheck; + wxChoice* m_ConditionChoice, *m_EffectChoice; + wxRadioBox* m_LogicRadio, *m_TimeRadio; + + std::vector m_ConditionSpecs, m_EffectSpecs; + int m_DependentStatus; + + DECLARE_EVENT_TABLE(); +}; + + +BEGIN_EVENT_TABLE(TriggerBottomBar, wxPanel) +EVT_TEXT_ENTER(TriggerBottomBar::ID_TimeEdit, TriggerBottomBar::onTimeEnter) +EVT_TEXT_ENTER(TriggerBottomBar::ID_CondNameEdit, TriggerBottomBar::onConditionEnter) +EVT_TEXT_ENTER(TriggerBottomBar::ID_EffectNameEdit, TriggerBottomBar::onEffectEnter) +EVT_TEXT_ENTER(TriggerBottomBar::ID_TriggerNameEdit, TriggerBottomBar::onTriggerEnter) +EVT_TEXT_ENTER(TriggerBottomBar::ID_RunsEdit, TriggerBottomBar::onRunsEnter) +EVT_CHOICE(TriggerBottomBar::ID_EffectChoice, TriggerBottomBar::onEffectChoice) +EVT_CHOICE(TriggerBottomBar::ID_CondChoice, TriggerBottomBar::onCondChoice) + +EVT_RADIOBOX(TriggerBottomBar::ID_LogicRadio, TriggerBottomBar::onLogicRadio) +EVT_CHECKBOX(TriggerBottomBar::ID_ActiveCheck, TriggerBottomBar::onActiveCheck) +EVT_CHECKBOX(TriggerBottomBar::ID_NotCheck, TriggerBottomBar::onNotCheck) +//EVT_RADIOBOX(TriggerBotomBar::ID_TimeRadio, TriggerBottomBar::onTimeRadio) +END_EVENT_TABLE() + + +void TriggerTreeCtrl::onClick(wxMouseEvent& evt) +{ + if ( m_Sidebar->m_TriggerTree->GetSelection() == m_Sidebar->m_TriggerTree->GetRootItem() || + !m_Sidebar->m_TriggerTree->GetSelection() ) + { + m_Sidebar->m_TriggerBottom->ToNoView(); + } + else + { + m_Sidebar->m_TriggerBottom->ToTriggerView(); + m_Sidebar->m_TriggerBottom->FillTriggerData(); + } + evt.Skip(); +} + +void TriggerListCtrl::onClick(wxMouseEvent& evt) +{ + if ( m_Condition ) + { + //if ( m_Sidebar->m_TriggerBottom->GetDependentStatus() != TriggerBottomBar::CONDITION_VIEW ) + m_Sidebar->m_TriggerBottom->ToConditionView(); + if ( m_Sidebar->m_SelectedCond != -1 ) + m_Sidebar->m_TriggerBottom->FillConditionData(); + } + else + { + //if ( m_Sidebar->m_TriggerBottom->GetDependentStatus() != TriggerBottomBar::EFFECT_VIEW ) + m_Sidebar->m_TriggerBottom->ToEffectView(); + if ( m_Sidebar->m_SelectedEffect != -1 ) + m_Sidebar->m_TriggerBottom->FillEffectData(); + } + evt.Skip(); +} + +void TriggerSpecText::onTextEnter(wxCommandEvent& WXUNUSED(evt)) +{ + std::wstring text( GetValue().wc_str() ); + + if ( VerifyInput(text) ) + (*m_Callback)(m_Data, text, m_Parameter ); + else + wxBell(); +} + + +void onTriggerParameter(void* data, std::wstring paramString, int parameter) +{ + TriggerBottomBar* bottomBar = static_cast(data); + + if ( bottomBar->m_Sidebar->m_Notebook->GetSelection() == 0 ) + { + if ( bottomBar->m_Sidebar->m_SelectedCond == -1 ) + return; + std::vector conditions = *bottomBar->m_Sidebar->GetSelectedItemData()->conditions; + std::vector parameters = *conditions[bottomBar->m_Sidebar->m_SelectedCond].parameters; + + parameters[parameter] = paramString; + conditions[bottomBar->m_Sidebar->m_SelectedCond].parameters = parameters; + bottomBar->m_Sidebar->GetSelectedItemData()->conditions = conditions; + } + else + { + if ( bottomBar->m_Sidebar->m_SelectedEffect == -1 ) + return; + std::vector effects = *bottomBar->m_Sidebar->GetSelectedItemData()->effects; + std::vector parameters = *effects[bottomBar->m_Sidebar->m_SelectedEffect].parameters; + + parameters[parameter] = paramString; + effects[bottomBar->m_Sidebar->m_SelectedEffect].parameters = parameters; + bottomBar->m_Sidebar->GetSelectedItemData()->effects = effects; + } + bottomBar->m_Sidebar->UpdateEngineData(); +} + +void onGroupPush(void* data) +{ + TriggerSidebar* sidebar = static_cast(data); + if ( !sidebar->m_TriggerTree->GetSelection()) + return; + if ( !sidebar->IsGroupSelected() ) + return; + + wxString name = wxString::Format(L"Group %d", sidebar->m_GroupCount); + wxTreeItemId ID = sidebar->m_TriggerTree->AppendItem( sidebar->m_TriggerTree->GetSelection(), + name, -1, -1, new TriggerItemData(sidebar, std::wstring(name), true) ); + sidebar->m_TriggerTree->EnsureVisible(ID); + ++sidebar->m_GroupCount; + + sidebar->UpdateEngineData(); +} + +void onTriggerPush(void* data) +{ + TriggerSidebar* sidebar = static_cast(data); + wxTreeItemId ID = sidebar->m_TriggerTree->GetSelection(); + + if ( !sidebar->IsGroupSelected() ) + ID = sidebar->m_TriggerTree->GetItemParent(ID); + + wxString name = wxString::Format(L"Trigger %d", sidebar->m_TriggerCount); + TriggerItemData* itemData = new TriggerItemData(sidebar, std::wstring(name), false); + itemData->group = std::wstring( sidebar->m_TriggerTree->GetItemText(ID) ); + + ID = sidebar->m_TriggerTree->AppendItem(ID, name, -1, -1, itemData); + sidebar->m_TriggerTree->Expand( sidebar->m_TriggerTree->GetRootItem() ); + + ++sidebar->m_TriggerCount; + sidebar->m_TriggerTree->SelectItem(ID); + sidebar->UpdateEngineData(); +} + +void onDeleteTreePush(void* data) +{ + TriggerSidebar* sidebar = static_cast(data); + if ( sidebar->m_TriggerTree->GetSelection() == sidebar->m_TriggerTree->GetRootItem() ) + return; + + if ( wxMessageBox( wxString(L"Are you sure you want to delete this item?"), + wxString(L"Caution"), wxYES_NO ) == wxYES ) + { + sidebar->m_TriggerTree->Delete(sidebar->m_TriggerTree->GetSelection()); + sidebar->m_TriggerTree->EnsureVisible( sidebar->m_TriggerTree->GetRootItem() ); + sidebar->m_TriggerBottom->FillTriggerData(); + } + + sidebar->UpdateEngineData(); +} + +void onConditionPush(void* data) +{ + TriggerSidebar* sidebar = static_cast(data); + if ( sidebar->IsGroupSelected() ) + return; + + sidebar->m_Notebook->SetSelection(0); + TriggerItemData* itemData = sidebar->GetSelectedItemData(); + std::wstring name = std::wstring( wxString::Format(L"Condition %d", itemData->m_CondCount).wc_str() ); + + if ( itemData->m_CondCount == 0 ) + sidebar->m_SelectedCond = 0; + + ++itemData->m_CondCount; + + std::vector conditions = *itemData->conditions; + conditions.push_back( sTriggerCondition(name) ); + itemData->conditions = conditions; + + long count = sidebar->m_ConditionPage->m_List->GetItemCount(); + + sidebar->m_ConditionPage->m_List->InsertItem(count, wxString(name.c_str()) ); + sidebar->m_ConditionPage->m_List->EnsureVisible(count); + sidebar->m_ConditionPage->m_List->SetItemState(count, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); + + sidebar->m_TriggerBottom->ToConditionView(); //Some data is not valid, so reset + sidebar->m_TriggerBottom->FillConditionData(); + + sidebar->UpdateEngineData(); +} + +void onEffectPush(void* data) +{ + TriggerSidebar* sidebar = static_cast(data); + if ( sidebar->IsGroupSelected() ) + return; + + sidebar->m_Notebook->SetSelection(1); + TriggerItemData* itemData = sidebar->GetSelectedItemData(); + std::wstring name = std::wstring( wxString::Format(L"Effect %d", itemData->m_EffectCount).wc_str() ); + + if ( itemData->m_EffectCount == 0 ) + sidebar->m_SelectedEffect = 0; + ++itemData->m_EffectCount; + + std::vector effects = *itemData->effects; + effects.push_back( sTriggerEffect(name) ); + itemData->effects = effects; + + long count = sidebar->m_EffectPage->m_List->GetItemCount(); + sidebar->m_EffectPage->m_List->InsertItem(count, wxString(name.c_str()) ); + sidebar->m_EffectPage->m_List->EnsureVisible(count); + sidebar->m_EffectPage->m_List->SetItemState(count, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); + + sidebar->m_TriggerBottom->ToEffectView(); + sidebar->m_TriggerBottom->FillEffectData(); + + sidebar->UpdateEngineData(); +} + +void onDeleteBookPush(void* data) +{ + TriggerSidebar* sidebar = static_cast(data); + wxListCtrl* list = static_cast(sidebar->m_Notebook->GetCurrentPage())->m_List; + if ( !list ) + return; + + TriggerItemData* itemData = sidebar->GetSelectedItemData(); + //Is condition? -- valid selection? + if ( list == sidebar->m_ConditionPage->m_List && sidebar->m_SelectedCond != -1 ) + { + std::wstring text(sidebar->m_ConditionPage->m_List->GetItemText(sidebar->m_SelectedCond) ); + int conditionCount = sidebar->GetConditionCount(sidebar->m_SelectedCond); + + if ( text == std::wstring(sidebar->m_LogicBlockString.wc_str()) ) + { + std::vector blocks = *itemData->logicBlocks; + if ( conditionCount == 0 ) + { + blocks.erase( std::find(blocks.begin(), blocks.end(), 0) ); + itemData->m_BlockIndices.erase( std::find( itemData->m_BlockIndices.begin(), + itemData->m_BlockIndices.end(), sidebar->m_SelectedCond ) ); + } + else + { + blocks.erase( std::find(blocks.begin(), blocks.end(), conditionCount) ); + itemData->m_BlockIndices.erase( std::find( itemData->m_BlockIndices.begin(), + itemData->m_BlockIndices.end(), sidebar->m_SelectedCond ) ); + } + itemData->logicBlocks = blocks; + } + + else if ( text == std::wstring(sidebar->m_LogicBlockEndString.wc_str()) ) + { + std::vector blockEnds = *itemData->logicBlockEnds; + if ( conditionCount == 0 ) + { + blockEnds.erase( std::find(blockEnds.begin(), blockEnds.end(), 0) ); + itemData->m_BlockEndIndices.erase( std::find( itemData->m_BlockEndIndices.begin(), + itemData->m_BlockEndIndices.end(), sidebar->m_SelectedCond ) ); + } + else + { + blockEnds.erase( std::find(blockEnds.begin(), blockEnds.end(), conditionCount-1) ); + itemData->m_BlockEndIndices.erase( std::find( itemData->m_BlockEndIndices.begin(), + itemData->m_BlockEndIndices.end(), sidebar->m_SelectedCond ) ); + } + itemData->logicBlockEnds = blockEnds; + } + + else + { + std::vector conditions = *itemData->conditions; + conditions.erase( std::find(conditions.begin(), conditions.end(), text) ); + itemData->conditions = conditions; + } + + list->DeleteItem( sidebar->m_SelectedCond ); + itemData->ResetBlockIndices(); + + if ( sidebar->m_SelectedCond == list->GetItemCount() ) + sidebar->m_SelectedCond = -1; + else + sidebar->m_TriggerBottom->FillConditionData(); + } + + else if ( list == sidebar->m_EffectPage->m_List && sidebar->m_SelectedEffect != -1) + { + std::vector effects = *itemData->effects; + effects.erase( std::find( effects.begin(), effects.end(), std::wstring( + list->GetItemText(sidebar->m_SelectedEffect)) ) ); + itemData->effects = effects; + + list->DeleteItem( sidebar->m_SelectedEffect ); + + if ( itemData->effects.GetSize() == 0 || sidebar->m_SelectedEffect == list->GetItemCount() ) + sidebar->m_SelectedEffect = -1; + else + sidebar->m_TriggerBottom->FillEffectData(); + } + sidebar->UpdateLists(); + sidebar->UpdateEngineData(); +} + +void onLogicBlockPush(void* data) +{ + TriggerSidebar* sidebar = static_cast(data); + sidebar->m_Notebook->SetSelection(0); + + if ( sidebar->IsGroupSelected() ) + return; + + int limit = sidebar->m_SelectedCond; + if ( sidebar->m_SelectedCond == -1 ) + limit = sidebar->m_ConditionPage->m_List->GetItemCount()-1; + + int conditionCount = sidebar->GetConditionCount(limit); + if ( conditionCount == 0 ) + { + sidebar->GetSelectedItemData()->AddBlock(0, 0); + sidebar->UpdateLists(); + return; + } + + sidebar->GetSelectedItemData()->AddBlock(conditionCount, limit); + sidebar->UpdateLists(); + sidebar->UpdateEngineData(); +} + +void onBlockEndPush(void* data) +{ + TriggerSidebar* sidebar = static_cast(data); + sidebar->m_Notebook->SetSelection(0); + + if ( sidebar->IsGroupSelected() ) + return; + + int limit = sidebar->m_SelectedCond; + if ( sidebar->m_SelectedCond == -1 ) + limit = sidebar->m_ConditionPage->m_List->GetItemCount()-1; + + int conditionCount = sidebar->GetConditionCount(limit); + if ( conditionCount == 0 ) + { + sidebar->GetSelectedItemData()->AddBlockEnd(0, 0); + sidebar->UpdateLists(); + return; + } + + sidebar->GetSelectedItemData()->AddBlockEnd(conditionCount-1, limit); + sidebar->UpdateLists(); +} + + + +TriggerSidebar::TriggerSidebar(wxWindow* sidebarContainer, wxWindow* bottomBarContainer) +: Sidebar(sidebarContainer, bottomBarContainer), m_GroupCount(0), m_TriggerCount(0), + m_SelectedCond(-1), m_SelectedEffect(-1) +{ + m_TriggerBottom = new TriggerBottomBar(this, bottomBarContainer); + m_BottomBar = m_TriggerBottom; + + m_TriggerTree = new TriggerTreeCtrl(this, wxID_ANY, wxDefaultPosition, + wxSize(140, 220), wxTR_HAS_BUTTONS | wxTR_EDIT_LABELS); + m_TriggerTree->AddRoot(L"Triggers", -1, -1, new TriggerItemData(this, L"Triggers", true)); + m_TriggerTree->SelectItem( m_TriggerTree->GetRootItem() ); + m_TriggerTree->Expand( m_TriggerTree->GetRootItem() ); + + + wxBoxSizer* hHolder = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer* vHolder = new wxBoxSizer(wxVERTICAL); + wxBoxSizer* vButtons = new wxBoxSizer(wxVERTICAL); + + ActionButton* trigButton = new ActionButton( this, L"Trigger", &onTriggerPush, + this, wxSize(50, 20) ); + ActionButton* groupButton = new ActionButton( this, L"Group", &onGroupPush, + this, wxSize(50, 20) ); + ActionButton* deleteButton = new ActionButton( this, L"Delete", &onDeleteTreePush, + this, wxSize(50, 20) ); + + ActionButton* conditionButton = new ActionButton( this, L"Condition", + &onConditionPush, this, wxSize(54, 20) ); + ActionButton* effectButton = new ActionButton( this, L"Effect", + &onEffectPush, this, wxSize(50, 20) ); + ActionButton* bookDelete = new ActionButton( this, L"Delete", + &onDeleteBookPush, this, wxSize(50, 20) ); + ActionButton* logicBlock = new ActionButton( this, L"Block", + &onLogicBlockPush, this, wxSize(50, 20) ); + ActionButton* logicBlockEnd = new ActionButton( this, L"Block End", + &onBlockEndPush, this, wxSize(50, 20) ); + + + m_LogicBlockString = wxString(L"--------------------"); + m_LogicBlockEndString = wxString(L"==========="); + wxStaticText* bottomTitle = new wxStaticText( this, wxID_ANY, _T("Conditions and Effects") ); + m_Notebook = new wxNotebook(this, wxID_ANY, wxDefaultPosition, + wxSize(132, 210), wxNB_TOP); + + m_ConditionPage = new TriggerPage(m_Notebook, this, ID_CondList, wxString(L"Conditions"), true); + m_EffectPage = new TriggerPage(m_Notebook, this, ID_EffectList, wxString(L"Effects"), false); + + m_Notebook->AddPage( m_ConditionPage, _T("Conditions") ); + m_Notebook->AddPage( m_EffectPage, _T("Effects") ); + m_Notebook->SetPageSize(wxSize(130, 240)); + + vButtons->Add(trigButton); + vButtons->Add(groupButton); + vButtons->Add(deleteButton); + vButtons->Add(conditionButton, 0, wxTOP, 190); + vButtons->Add(effectButton); + vButtons->Add(bookDelete); + vButtons->Add(logicBlock); + vButtons->Add(logicBlockEnd); + + vHolder->Add(m_TriggerTree, 0, wxTOP, 15); + vHolder->Add(bottomTitle, 0, wxALIGN_CENTER | wxTOP, 5); + vHolder->Add(m_Notebook, 0, wxTOP, 5 ); + + hHolder->Add(vHolder); + hHolder->Add(vButtons, 0, wxALIGN_CENTER); + m_MainSizer->Add(hHolder); +} + +int TriggerSidebar::GetConditionCount(int limit) +{ + int conditionCount = 0; + wxListCtrl* list = m_ConditionPage->m_List; + for ( int i = 0; i <= limit; ++i ) + { + if ( list->GetItemText(i) != m_LogicBlockString && list->GetItemText(i) != m_LogicBlockEndString) + { + ++conditionCount; + } + } + return conditionCount; +} +void TriggerSidebar::OnFirstDisplay() +{ + qGetTriggerData dataQuery; + dataQuery.Post(); + m_TriggerBottom->SetSpecs(*dataQuery.conditions, *dataQuery.effects); + + //Add all loaded triggers to the tree + const std::vector triggerGroups = *dataQuery.groups; + std::vector::const_iterator it = std::find( triggerGroups.begin(), + triggerGroups.end(), std::wstring(L"Triggers") ); + + if ( it != triggerGroups.end() ) + { + wxTreeItemId invalid; + AddGroupTree(*it, invalid); + m_TriggerTree->Expand( m_TriggerTree->GetRootItem() ); + } +} + +void TriggerSidebar::AddGroupTree(const sTriggerGroup& group, wxTreeItemId parent) +{ + wxTreeItemId newID; + wxString text( (*group.name).c_str() ); + + //Make sure root item doesn't already exist + if ( !parent && !m_TriggerTree->GetRootItem() ) + newID = m_TriggerTree->AddRoot(text, -1, -1, new TriggerItemData(this, *group.name, true)); + else if ( parent ) + newID = m_TriggerTree->AppendItem(parent, text); + else + newID = m_TriggerTree->GetRootItem(); + + const std::vector triggerBuf = *group.triggers; + const std::vector groupBuf = *group.children; + + for ( size_t i = 0; i < group.children.GetSize(); ++i ) + AddGroupTree( *std::find(m_TriggerGroups.begin(), m_TriggerGroups.end(), groupBuf[i]), newID ); + for ( size_t i = 0; i < group.triggers.GetSize(); ++i ) + { + std::wstring trigName = *triggerBuf[i].name; + + //Make triggers start where user last left off + if ( trigName.find(L"Trigger ") == 0 ) + { + trigName.erase(0, 8); //remove "Trigger " + std::wstringstream toInt(trigName); + size_t convert; + toInt >> convert; + ++convert; + + if ( !toInt.fail() ) + { + if ( convert > m_TriggerCount ) + m_TriggerCount = convert; + } + } + + m_TriggerTree->AppendItem( newID, wxString( triggerBuf[i].name.c_str() ), -1, -1, + new TriggerItemData(this, triggerBuf[i], false) ); + } +} + + +void TriggerSidebar::onPageChange(wxNotebookEvent& evt) +{ + if ( evt.GetSelection() == 0 ) + { + m_TriggerBottom->ToConditionView(); + if ( m_SelectedCond != -1 ) + m_TriggerBottom->FillConditionData(); + return; + } + m_TriggerBottom->ToEffectView(); + if ( m_SelectedEffect != -1 ) + m_TriggerBottom->FillEffectData(); +} + +void TriggerSidebar::onTreeDrag(wxTreeEvent& evt) +{ + //evt.Allow(); +} + +void TriggerSidebar::onTreeNameChange(wxTreeEvent& evt) +{ + ToDerived( m_TriggerTree->GetItemData(evt.GetItem()) )->name = std::wstring( + evt.GetLabel().wc_str()); + UpdateEngineData(); +} + +void TriggerSidebar::onTreeSelChange(wxTreeEvent& evt) +{ + //Prevent other triggers from trying to use previous data + m_SelectedCond = -1; + m_SelectedEffect = -1; + + if ( evt.GetItem() == m_TriggerTree->GetRootItem() ) + { + m_TriggerBottom->ToNoView(); + return; + } + + if ( m_TriggerBottom->GetDependentStatus() != TriggerBottomBar::TRIGGER_VIEW ) + m_TriggerBottom->ToTriggerView(); + m_TriggerBottom->FillTriggerData(); + + UpdateLists(); +} +void TriggerSidebar::UpdateLists() +{ + TriggerItemData* data = GetSelectedItemData(); + m_ConditionPage->m_List->Freeze(); + m_ConditionPage->m_List->DeleteAllItems(); + m_EffectPage->m_List->Freeze(); + m_EffectPage->m_List->DeleteAllItems(); + + const Shareable* conditions = data->conditions.GetBuffer(); + for ( size_t i = 0; i < data->conditions.GetSize(); ++i ) + { + m_ConditionPage->m_List->InsertItem( m_ConditionPage->m_List-> + GetItemCount(), wxString(conditions[i]->name.c_str()) ); + } + + const Shareable* effects = data->effects.GetBuffer(); + for ( size_t i = 0; i < data->effects.GetSize(); ++i ) + { + m_EffectPage->m_List->InsertItem(m_EffectPage->m_List->GetItemCount(), + wxString(effects[i]->name.c_str()) ); + } + + //These must be merged and sorted because adding them out-of-order screws up the list + std::list sortedBlocks; + std::list blocks = data->m_BlockIndices, blockEnds = data->m_BlockEndIndices; + + for ( std::list::iterator it = blocks.begin(); it != blocks.end(); ++it ) + sortedBlocks.push_back( LogicBlockHelper(*it, false) ); + for ( std::list::iterator it = blockEnds.begin(); it != blockEnds.end(); ++it ) + sortedBlocks.push_back( LogicBlockHelper(*it, true) ); + + sortedBlocks.sort(); + for ( std::list::iterator it = sortedBlocks.begin(); it != sortedBlocks.end(); ++it ) + { + if ( it->end ) + m_ConditionPage->m_List->InsertItem(it->index, m_LogicBlockEndString); + else + m_ConditionPage->m_List->InsertItem(it->index, m_LogicBlockString); + } + + m_ConditionPage->m_List->Thaw(); + m_EffectPage->m_List->Thaw(); +} + +void TriggerSidebar::onCondSelect(wxListEvent& evt) +{ + m_SelectedCond = evt.GetIndex(); + //if ( m_TriggerBottom->GetDependentStatus() != TriggerBottomBar::CONDITION_VIEW ) + m_TriggerBottom->ToConditionView(); + m_TriggerBottom->FillConditionData(); +} +void TriggerSidebar::onEffectSelect(wxListEvent& evt) +{ + m_SelectedEffect = evt.GetIndex(); + //if ( m_TriggerBottom->GetDependentStatus() != TriggerBottomBar::EFFECT_VIEW ) + m_TriggerBottom->ToEffectView(); + m_TriggerBottom->FillEffectData(); +} + +bool TriggerSidebar::IsGroupSelected() +{ + if ( ToDerived( m_TriggerTree->GetItemData(m_TriggerTree->GetSelection()) )->m_Group ) + return true; + return false; +} + +sTrigger TriggerSidebar::CreateTrigger(TriggerItemData* data) +{ + sTrigger trigger; + + trigger.active = data->active; + trigger.group = data->group; + trigger.maxRuns = data->maxRuns; + trigger.name = data->name; + trigger.timeValue = data->timeValue; + + trigger.logicBlockEnds = data->logicBlockEnds; + trigger.logicBlocks = data->logicBlocks; + trigger.conditions = data->conditions; + trigger.effects = data->effects; + trigger.logicNots = data->logicNots; + + return trigger; +} + +void TriggerSidebar::CreateGroup(std::vector& groupList, sTriggerGroup& parent, wxTreeItemId index) +{ + wxTreeItemIdValue cookie; + std::vector triggers; + sTriggerGroup group( std::wstring(m_TriggerTree->GetItemText(index)) ); + group.parentName = parent.name; + + //Add this group to parent's child group + std::vector parentChildren = *parent.children; + parentChildren.push_back(*group.parentName); + parent.children = parentChildren; + + for ( wxTreeItemId ID = m_TriggerTree->GetFirstChild(index, cookie); ID.IsOk(); + ID = m_TriggerTree->GetNextChild(index, cookie) ) + { + TriggerItemData* itemData = ToDerived( m_TriggerTree->GetItemData(ID) ); + if ( itemData->m_Group ) + CreateGroup(groupList, group, ID); + else + triggers.push_back( CreateTrigger(itemData) ); + } + + group.triggers = triggers; + groupList.push_back(group); +} + +void TriggerSidebar::UpdateEngineData() +{ + wxTreeItemIdValue cookie; + wxTreeItemId root = m_TriggerTree->GetRootItem(); + + //Find all root groups + std::vector groups; + std::vector triggers; + sTriggerGroup rootGroup(L"Triggers"); + + for ( wxTreeItemId ID = m_TriggerTree->GetFirstChild(root, cookie); ID.IsOk(); + ID = m_TriggerTree->GetNextChild(root, cookie) ) + { + TriggerItemData* itemData = ToDerived( m_TriggerTree->GetItemData(ID) ); + if ( itemData->m_Group ) + CreateGroup(groups, rootGroup, ID); + else + triggers.push_back( CreateTrigger(itemData) ); + } + + rootGroup.triggers = triggers; + groups.push_back(rootGroup); + POST_COMMAND( SetAllTriggers, (groups) ); +} +TriggerItemData* TriggerSidebar::ToDerived(wxTreeItemData* data) +{ + return ( static_cast(data) ); +} +TriggerItemData* TriggerSidebar::GetSelectedItemData() +{ + if ( !m_TriggerTree->GetSelection() ) + m_TriggerTree->SelectItem(m_TriggerTree->GetRootItem()); + return ToDerived( m_TriggerTree->GetItemData(m_TriggerTree->GetSelection()) ); + +} + diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Trigger/Trigger.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Trigger/Trigger.h new file mode 100644 index 0000000000..e49b5e4c78 --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Trigger/Trigger.h @@ -0,0 +1,80 @@ +/* Andrew Decker, a.k.a pyrolink + Contact ajdecker1022@msn.com + Desc: interface for creation and editing of triggers. Interprets + XML specifications then uses this to construct layout. +*/ + +#include "../Common/Sidebar.h" +#include "GameInterface/Messages.h" + +class TriggerItemData; +class TriggerTreeCtrl; +class TriggerBottomBar; +class TriggerPage; +class wxTreeItemId; + +class TriggerSidebar : public Sidebar +{ +public: + enum { ID_CondList, ID_EffectList }; + + TriggerSidebar(wxWindow* sidebarContainer, wxWindow* bottomBarContainer); + + void onTreeDrag(wxTreeEvent& evt); + void onTreeNameChange(wxTreeEvent& evt); + //void onTreeDelete(wxTreeEvent& evt); + void onTreeSelChange(wxTreeEvent& evt); + void onTreeCollapse(wxTreeEvent& evt); + void onListSelect(wxListEvent& evt); + void onEffectSelect(wxListEvent& evt); + void onCondSelect(wxListEvent& evt); + void onPageChange(wxNotebookEvent& evt); + + bool IsGroupSelected(); + TriggerItemData* ToDerived(wxTreeItemData* data); + TriggerItemData* GetSelectedItemData(); + + //Finds condition number (index+1) of m_SelectedCond [needed because of logic blocks] + int GetConditionCount(int limit); + void UpdateLists(); + void UpdateEngineData(); + + //AtlasMessage::sTriggerList m_TriggerList; + + TriggerBottomBar* m_TriggerBottom; + TriggerTreeCtrl* m_TriggerTree; + TriggerPage* m_ConditionPage, *m_EffectPage; + wxNotebook* m_Notebook; + + size_t m_TriggerCount, m_GroupCount; + long m_SelectedCond, m_SelectedEffect, m_SelectedCondIndex, m_SelectedEffectIndex; + wxString m_LogicBlockString, m_LogicBlockEndString; + +protected: + virtual void OnFirstDisplay(); + +private: + struct copyIfRootChild + { + copyIfRootChild(std::vector& groupList) : m_groupList(groupList) {} + void operator() ( const AtlasMessage::sTriggerGroup& group ) + { + if ( *group.parentName == std::wstring(L"Triggers") ) + m_groupList.push_back(group); + } + private: + std::vector& m_groupList; + }; + + void AddGroupTree(const AtlasMessage::sTriggerGroup& group, wxTreeItemId parent); + + void CreateGroup(std::vector& groupList, + AtlasMessage::sTriggerGroup& parent, wxTreeItemId index); + + AtlasMessage::sTrigger CreateTrigger(TriggerItemData* data); + + std::vector m_TriggerGroups; + + DECLARE_EVENT_TABLE(); +}; + diff --git a/source/tools/atlas/DatafileIO/Util.h b/source/tools/atlas/DatafileIO/Util.h index f1e9452fa4..17cefb3a0d 100644 --- a/source/tools/atlas/DatafileIO/Util.h +++ b/source/tools/atlas/DatafileIO/Util.h @@ -1,5 +1,6 @@ #include #include + #if OS_UNIX #include "ps/utf16string.h" #include @@ -11,7 +12,7 @@ namespace DatafileIO { -#if OS_WIN +#ifdef _WIN32 // TODO: proper portability typedef int int32_t; typedef unsigned int uint32_t; diff --git a/source/tools/atlas/GameInterface/Handlers/TriggerHandler.cpp b/source/tools/atlas/GameInterface/Handlers/TriggerHandler.cpp new file mode 100644 index 0000000000..8f654f90c1 --- /dev/null +++ b/source/tools/atlas/GameInterface/Handlers/TriggerHandler.cpp @@ -0,0 +1,321 @@ +#include "precompiled.h" + +#include "MessageHandler.h" +#include "../CommandProc.h" +#include "../Shareable.h" + +#include "simulation/TriggerManager.h" +#include "ps/Game.h" +//#include "../CommandProc.h" + + +namespace AtlasMessage { + +sTriggerSpec TriggerSpecToAtlas(const CTriggerSpec& spec) +{ + sTriggerSpec atlasSpec; + std::vector< sTriggerParameter > atlasParameters; + const std::set& parameters = spec.GetParameters(); + + for ( std::set::const_iterator it=parameters.begin(); it!=parameters.end(); ++it ) + { + sTriggerParameter atlasParam; + atlasParam.column = it->column; + atlasParam.inputType = it->inputType; + atlasParam.name = it->name; + atlasParam.parameterOrder = it->parameterOrder; + atlasParam.row = it->row; + + atlasParam.windowType = it->windowType; + atlasParam.xPos = it->xPos; + atlasParam.yPos = it->yPos; + atlasParam.xSize = it->xSize; + atlasParam.ySize = it->ySize; + atlasParameters.push_back(atlasParam); + } + atlasSpec.functionName = spec.functionName; + atlasSpec.displayName = spec.displayName; + atlasSpec.parameters = atlasParameters; + return atlasSpec; +} + +sTrigger TriggerToAtlas(const MapTrigger& trigger) +{ + sTrigger atlasTrigger; + atlasTrigger.active = trigger.active; + atlasTrigger.timeValue = trigger.timeValue; + atlasTrigger.maxRuns = trigger.maxRunCount; + atlasTrigger.group = trigger.groupName; + atlasTrigger.name = trigger.name; + + std::vector atlasNots; + std::vector atlasBlocks, atlasBlockEnds; + + for ( std::set::const_iterator it = trigger.logicBlocks.begin(); + it != trigger.logicBlocks.end(); ++it ) + { + atlasBlocks.push_back( (int)it->index ); + atlasNots.push_back( it->not ); + } + for ( std::set::const_iterator it = trigger.logicBlockEnds.begin(); + it != trigger.logicBlockEnds.end(); ++it ) + { + atlasBlockEnds.push_back( (int)*it ); + } + atlasTrigger.logicNots = atlasNots; + atlasTrigger.logicBlocks = atlasBlocks; + atlasTrigger.logicBlockEnds = atlasBlockEnds; + + std::vector atlasConditions; + std::vector atlasEffects; + + for ( std::list::const_iterator it = trigger.conditions.begin(); + it != trigger.conditions.end(); ++it ) + { + sTriggerCondition atlasCondition; + atlasCondition.linkLogic = it->linkLogic; + atlasCondition.name = it->name; + atlasCondition.functionName = it->functionName; + atlasCondition.displayName = it->displayName; + atlasCondition.not = it->not; + std::vector parameters; + + for ( std::list::const_iterator it2=it->parameters.begin(); + it2 != it->parameters.end(); ++it2 ) + { + parameters.push_back( std::wstring(it2->c_str()) ); + } + + atlasCondition.parameters = parameters; + atlasConditions.push_back(atlasCondition); + } + + for ( std::list::const_iterator it = trigger.effects.begin(); + it != trigger.effects.end(); ++it ) + { + sTriggerEffect atlasEffect; + std::vector parameters; + atlasEffect.name = it->name; + atlasEffect.functionName = it->functionName; + atlasEffect.displayName = it->displayName; + + for ( std::list::const_iterator it2 = it->parameters.begin(); + it2 != it->parameters.end(); ++it2 ) + { + parameters.push_back( std::wstring(it2->c_str()) ); + } + atlasEffect.parameters = parameters; + atlasEffects.push_back( atlasEffect ); + } + + atlasTrigger.conditions = atlasConditions; + atlasTrigger.effects = atlasEffects; + return atlasTrigger; +} + +sTriggerGroup GroupToAtlas(const MapTriggerGroup& group) +{ + sTriggerGroup atlasGroup; + atlasGroup.parentName = group.parentName; + atlasGroup.name = group.name; + + std::vector atlasTriggers; + std::vector atlasChildren; + for ( std::list::const_iterator it = group.triggers.begin(); + it != group.triggers.end(); ++it ) + { + atlasTriggers.push_back( TriggerToAtlas(*it) ); + } + for ( std::list::const_iterator it = group.childGroups.begin(); + it != group.childGroups.end(); ++it ) + { + atlasChildren.push_back( std::wstring(it->c_str()) ); + } + + atlasGroup.triggers = atlasTriggers; + atlasGroup.children = atlasChildren; + return atlasGroup; +} + +MapTrigger AtlasToTrigger(const sTrigger& trigger) +{ + MapTrigger engineTrigger; + + engineTrigger.active = trigger.active; + engineTrigger.groupName = *trigger.group; + + std::vector blockEnds = *trigger.logicBlockEnds, blocks = *trigger.logicBlocks; + std::copy( blockEnds.begin(), blockEnds.end(), engineTrigger.logicBlockEnds.begin() ); + std::copy( blocks.begin(), blocks.end(), engineTrigger.logicBlocks.begin() ); + + engineTrigger.maxRunCount = trigger.maxRuns; + engineTrigger.name = *trigger.name; + engineTrigger.timeValue = trigger.timeValue; + + std::vector conditions = *trigger.conditions; + std::vector effects = *trigger.effects; + + for ( std::vector::const_iterator it = conditions.begin(); it != conditions.end(); ++it ) + { + engineTrigger.conditions.push_back( MapTriggerCondition() ); + MapTriggerCondition* cond = &engineTrigger.conditions.back(); + + cond->functionName = *it->functionName; + cond->displayName = *it->displayName; + cond->linkLogic = it->linkLogic; + cond->name = *it->name; + cond->not = it->not; + + std::vector parameters = *it->parameters; + for ( std::vector::const_iterator it2 = parameters.begin(); it2 != parameters.end(); ++it2 ) + { + cond->parameters.push_back( CStrW(*it2) ); + } + } + + for ( std::vector::const_iterator it = effects.begin(); it != effects.end(); ++it ) + { + engineTrigger.effects.push_back( MapTriggerEffect() ); + MapTriggerEffect* effect = &engineTrigger.effects.back(); + + effect->functionName = *it->functionName; + effect->displayName = *it->displayName; + effect->name = *it->name; + std::vector parameters = *it->parameters; + + for ( std::vector::const_iterator it2 = parameters.begin(); it2 != parameters.end(); ++it2 ) + { + effect->parameters.push_back( CStrW(*it2) ); + } + } + + return engineTrigger; +} + + +MapTriggerGroup AtlasToGroup(const sTriggerGroup& group) +{ + MapTriggerGroup engineGroup; + engineGroup.parentName = *group.parentName; + engineGroup.name = *group.name; + + std::list engineTriggers; + std::vector atlasChildren = *group.children; + std::vector atlasTriggers = *group.triggers; + + for ( std::vector::const_iterator it = atlasTriggers.begin(); + it != atlasTriggers.end(); ++it ) + { + engineTriggers.push_back( AtlasToTrigger(*it) ); + } + for ( std::vector::const_iterator it = atlasChildren.begin(); it != atlasChildren.end(); ++it ) + engineGroup.childGroups.push_back(*it); + + engineGroup.triggers = engineTriggers; + return engineGroup; +} + +std::vector GetCurrentTriggers() +{ + const std::list& groups = g_TriggerManager.GetAllTriggerGroups(); + std::vector atlasGroups; + + for ( std::list::const_iterator it = groups.begin(); it != groups.end(); ++it ) + atlasGroups.push_back( GroupToAtlas(*it) ); + return atlasGroups; +} + +void SetCurrentTriggers(const std::vector& groups) +{ + std::list engineGroups; + for ( std::vector::const_iterator it = groups.begin(); it != groups.end(); ++it ) + engineGroups.push_back( AtlasToGroup(*it) ); + g_TriggerManager.SetAllGroups(engineGroups); +} + +QUERYHANDLER(GetTriggerData) +{ + const std::list& conditions = g_TriggerManager.GetAllConditions(); + const std::list& effects = g_TriggerManager.GetAllEffects(); + std::vector atlasConditions; + std::vector atlasEffects; + + for ( std::list::const_iterator it=conditions.begin(); it!=conditions.end(); ++it ) + atlasConditions.push_back( TriggerSpecToAtlas(*it) ); + for ( std::list::const_iterator it=effects.begin(); it!=effects.end(); ++it ) + atlasEffects.push_back( TriggerSpecToAtlas(*it) ); + + msg->conditions = atlasConditions; + msg->effects = atlasEffects; + msg->groups = GetCurrentTriggers(); +} + +QUERYHANDLER(GetTriggerChoices) +{ + std::vector choices = g_TriggerManager.GetTriggerChoices( CStrW(*msg->name) ); + if ( choices.empty() ) + return; + + //If a special list (i.e. uses engine data) + if ( choices.size() == 1 ) + { + + if ( choices[0] == std::wstring(L"ATLAS_CINEMA_LIST") ) + { + choices.clear(); + const std::map& tracks = g_Game->GetView()->GetCinema()->GetAllTracks(); + for ( std::map::const_iterator it = tracks.begin(); it != tracks.end(); ++it ) + choices.push_back(it->first); + } + else if ( choices[0] == std::wstring(L"ATLAS_TRIGGER_LIST") ) + { + choices.clear(); + const std::list& groups = g_TriggerManager.GetAllTriggerGroups(); + for ( std::list::const_iterator it = groups.begin(); + it != groups.end(); ++it ) + { + for ( std::list::const_iterator it2 = it->triggers.begin(); + it2 != it->triggers.end(); ++it2 ) + { + choices.push_back(it2->name); + } + } + } + else if ( choices[0] == std::wstring(L"ATLAS_TRIG_GROUP_LIST") ) + { + choices.clear(); + const std::list& groups = g_TriggerManager.GetAllTriggerGroups(); + for ( std::list::const_iterator it = groups.begin(); + it != groups.end(); ++it ) + { + choices.push_back(it->name); + } + } + else + debug_warn("Invalid choice list for trigger specification parameter"); + } + msg->choices = choices; +} + +BEGIN_COMMAND(SetAllTriggers) +{ + std::vector m_oldGroups, m_newGroups; + + void Do() + { + m_oldGroups = GetCurrentTriggers(); + m_newGroups = *msg->groups; + Redo(); + } + void Redo() + { + SetCurrentTriggers(m_newGroups); + } + void Undo() + { + SetCurrentTriggers(m_oldGroups); + } +}; +END_COMMAND(SetAllTriggers); + +} \ No newline at end of file diff --git a/source/tools/atlas/GameInterface/Messages.h b/source/tools/atlas/GameInterface/Messages.h index 87c8dc6b95..49758ab98a 100644 --- a/source/tools/atlas/GameInterface/Messages.h +++ b/source/tools/atlas/GameInterface/Messages.h @@ -344,6 +344,32 @@ MESSAGE(CinemaEvent, ////////////////////////////////////////////////////////////////////////// +enum eTriggerListType +{ + CINEMA_LIST, + TRIGGER_LIST, + TRIG_GROUP_LIST //list of trigger groups + // [Eventually include things like entities and areas as the editor progresses...] +}; + + +QUERY(GetTriggerData, + , //no inputs + ((std::vector, groups)) + ((std::vector, conditions)) + ((std::vector, effects)) + ); + +QUERY(GetTriggerChoices, + ((std::wstring, name)), + ((std::vector, choices)) + ); + +COMMAND(SetAllTriggers, NOMERGE, + ((std::vector, groups)) + ); + + #ifndef MESSAGES_SKIP_SETUP #include "MessagesSetup.h" #endif diff --git a/source/tools/atlas/GameInterface/Shareable.h b/source/tools/atlas/GameInterface/Shareable.h index bb6401456e..fef5de7791 100644 --- a/source/tools/atlas/GameInterface/Shareable.h +++ b/source/tools/atlas/GameInterface/Shareable.h @@ -82,6 +82,7 @@ public: SHAREABLE_PRIMITIVE(unsigned char); SHAREABLE_PRIMITIVE(int); +SHAREABLE_PRIMITIVE(long); SHAREABLE_PRIMITIVE(bool); SHAREABLE_PRIMITIVE(float); SHAREABLE_PRIMITIVE(void*); diff --git a/source/tools/atlas/GameInterface/SharedTypes.h b/source/tools/atlas/GameInterface/SharedTypes.h index 1d130e67bc..73fd4e7719 100644 --- a/source/tools/atlas/GameInterface/SharedTypes.h +++ b/source/tools/atlas/GameInterface/SharedTypes.h @@ -121,6 +121,119 @@ struct sCameraInfo SHAREABLE_STRUCT(sCameraInfo); +//******Triggers***** + + +struct sTriggerParameter +{ + Shareable row, column, xPos, yPos, xSize, ySize, parameterOrder; + Shareable name, inputType, windowType; +}; +SHAREABLE_STRUCT(sTriggerParameter); + +struct sTriggerSpec +{ + Shareable > parameters; + Shareable displayName, functionName; + + bool operator== ( const std::wstring& name) const + { + return ( *displayName == name ); + } +}; +SHAREABLE_STRUCT(sTriggerSpec); + + +struct sTriggerCondition +{ + sTriggerCondition() : linkLogic(1), not(false) {} + sTriggerCondition(const std::wstring& _name) : linkLogic(1), not(false), name(_name) {} + + //displayName is used for selecting choice items in Atlas + Shareable name, functionName, displayName; + Shareable< std::vector > parameters; + + //0 = none, 1 = and, 2 = or + Shareable linkLogic; + Shareable not; + + bool operator== ( const std::wstring& _name ) const + { + return (*name == _name); + } +}; + +SHAREABLE_STRUCT(sTriggerCondition); + +struct sTriggerEffect +{ + sTriggerEffect() {} + sTriggerEffect(const std::wstring& _name) : name(_name) {} + + Shareable name, functionName, displayName; + Shareable< std::vector > parameters; + //Shareable loop; + + bool operator== ( const std::wstring& _name ) + { + return (*name == _name); + } +}; +SHAREABLE_STRUCT(sTriggerEffect); + +struct sTrigger +{ + Shareable name, group; + Shareable< std::vector > conditions; + Shareable< std::vector > effects; + + //For beginnings, the index of the term it comes before. For ends, the index it comes after + Shareable< std::vector > logicBlocks, logicBlockEnds; + Shareable< std::vector > logicNots; //true if logicBlocks are not-ed + Shareable timeValue; + Shareable maxRuns; + Shareable active; + + sTrigger() : timeValue(0), maxRuns(-1), active(0) {} + sTrigger(std::wstring _name) : name(_name), timeValue(0), maxRuns(0), active(0) {} + bool operator== (const sTrigger& trigger) + { + return (*name == *trigger.name); + } + bool operator!= (const sTrigger& trigger) + { + return (*name != *trigger.name); + } +}; +SHAREABLE_STRUCT(sTrigger); + + +struct sTriggerGroup +{ + Shareable name, parentName; + Shareable< std::vector > children; + Shareable< std::vector > triggers; + + sTriggerGroup() { } + sTriggerGroup(std::wstring _name) : name(_name) { } + + bool operator== (const sTriggerGroup& group) const + { + return (*name == *group.name); + } + bool operator== (const std::wstring& _name) const + { + return (*name == _name); + } + /*bool operator!= (const sTriggerGroup& group) + { + return (*name != *group.name); + }*/ +}; + +SHAREABLE_STRUCT(sTriggerGroup); + + } #endif // SHAREDTYPES_H__