Quote:Original post by Telastyn
What you're looking for seems to be tantamount to writing your own programming language and parser. Why, when not a few scripting languages like lua and python exist to handle this problem?
This
really isn't as bad as writing an entire language [smile] It's simple enough that you could implement it yourself. As a matter of fact, it might be more work setting up an existing scripting language than it would be writing everything yourself. Besides, if you've never done so before the learning experience alone is worth it. All you'd need is a simple token-based parser that handles string literals. The reason why I used blocks in my sample syntax is because they're easy to parse and there's a clear semantic closure (if that's an appropriate phrase) for the triggers.
Quote:Original post by Grain
Well that what I am asking, How do I implement it?
Phase 1 would definitely be the parser. Just write something that allows you to read words at a time that are delimited by whitespace or quoted strings. It might also be a good idea to optionally restrict the next token to the current line (so that you can enforce the parameters on the same line as the trigger/event but that's a preference issue). Phase 2 would be designing the internal code structure that implements a script. I'm a sucker for nesting, so it might look like this:
class BaseTrigger{public: virtual void TriggerMe() = 0; // ...}; class Trigger : public BaseTrigger{public: virtual bool ShouldTrigger() = 0; virtual void TriggerMe() { if(ShouldTrigger()) { // Simple loop for(int i = 0; i < children.size(); ++i) children->TriggerMe(); } } // ...private: std::vector<BaseTrigger*> children;}; class TriggerLocation : public Trigger{public: virtual bool ShouldTrigger() { if(object->Location() == location) return true; else return false; } // ... private: GameObject* object; LocationType location;}; class Event : public BaseTrigger{public: virtual void TriggerMe() = 0; // ...}; class EventSpawn : public Event{public: virtual void TriggerMe() { entityInterface->Spawn(monsterType, count); } // ... private: int monsterType; int count;}; // ...
Off the top of my head of course. Adding boolean logic would introduce a few more interesting classes:
class BinaryTrigger : public Trigger{public: virtual bool ShouldTrigger() = 0; private: Trigger* triggerA; Trigger* triggerB;}; class UnaryTrigger : public Trigger{public: virtual bool ShouldTrigger() = 0; private: Trigger* trigger;}; class AndTrigger : public BinaryTrigger{public: virtual bool ShouldTrigger() { return triggerA->ShouldTrigger() && triggerB->ShouldTrigger() } // ...}; class OrTrigger : public BinaryTrigger{public: virtual bool ShouldTrigger() { return triggerA->ShouldTrigger() || triggerB->ShouldTrigger() } // ...}; class NotTrigger : public UnaryTrigger{public: virtual bool ShouldTrigger() { return !trigger->ShouldTrigger(); }};
Explicit boolean logic is more difficult to implement because now you have to deal with parenthetical groups. It might just be better to stick with just unary operations (ex. NOT) and let the nesting handle AND. OR is a pain because you have to write the event code multiple times for each condition. Parsing parenthetical groups is something you have to decide you want to implement, but it's similar to parsing bracket blocks.
The bulk of Phase 3 is then turning the script into this trigger/event structure. But that's the fun part.