diff --git a/docs/engine/objects/zCTrigger.md b/docs/engine/objects/zCTrigger.md new file mode 100644 index 00000000..455cc060 --- /dev/null +++ b/docs/engine/objects/zCTrigger.md @@ -0,0 +1,157 @@ +# zCTrigger + +!!! abstract inline end "Quick Infos" + **Type:** Virtual Object
+ **Format Name:** ZenGin Archive
+ **File Extension:** `.ZEN`
+ **Class Name:** `zCTrigger`
+ **Version Identifiers:**
+ — Gothic I: `47105`
+ — Gothic II: `47105`
+ **ZenKit Class:** `VTrigger` + +VObject to process and relay specific filtered events. When a `zCTrigger` receives an `OnTrigger`, `OnDamage`, or +`OnTouch` event it checks whether the event source (an [`oCNpc`](oCNpc.md), the player or another VObject) should +be acknowledged (see [`respondToNPC`](#respondToNPC), [`respondToPC`](#respondToPC) and +[`respondToObject`](#respondToObject) respectively). It then verifies if it should react to the specific event by +checking the [`reactToOnTrigger`](#reactToOnTrigger), [`reactToOnDamage`](#reactToOnDamage) and +[`reactToOnTouch`](#reactToOnTouch) properties. If both checks succeed, an `OnTrigger` event is sent to the +[`triggerTarget`](#triggerTarget). `zCTrigger` objects can be enabled and disabled by firing `OnEnable`, `OnDisable` +and `OnToggleEnabled` events at them. + +!!! note + The `respondTo{Object,PC,NPC,VobName}` properties are logically linked by and "or" expression. + + + +## Properties + + `triggerTarget` + +: The name of VObject to send `OnTrigger` and `OnUntrigger` events to after processing. + + `reactToOnTrigger` + +: Whether this trigger should react to `OnTrigger` events. + + * `TRUE` — Do react to `OnTrigger` events by sending an `OnTrigger` message to the [`triggerTarget`](#triggerTarget) + If [`sendUntrigger`](#sendUntrigger) is set to `TRUE`, also sends an `OnUntrigger` event to the [`triggerTarget`](#triggerTarget) + if the trigger receives an `OnUntrigger` event. + * `FALSE` — Ignore `OnTrigger` events. + + `reactToOnTouch` + +: Whether this trigger should react to `OnTouch` events. + + * `TRUE` — Do react to `OnTouch` events by sending an `OnTrigger` message to the [`triggerTarget`](#triggerTarget) + If [`sendUntrigger`](#sendUntrigger) is set to `TRUE`, also sends an `OnUntrigger` event to the [`triggerTarget`](#triggerTarget) + if the trigger receives an `OnUntouch` event. + * `FALSE` — Ignore `OnTouch` events. + + `reactToOnDamage` + +: Whether this trigger should react to `OnDamage` events. + + * `TRUE` — Do react to `OnDamage` events by sending an `OnTrigger` message to the [`triggerTarget`](#triggerTarget) + * `FALSE` — Ignore `OnDamage` events. + + `respondToObject` + +: Whether this trigger should process events coming from inanimate objects. + + * `TRUE` — Do process events from inanimate objects. + * `FALSE` — Ignore events from inanimate objects. + + `respondToPC` + +: Whether this trigger should process events coming from the player. + + * `TRUE` — Do process events from the player. + * `FALSE` — Ignore events from the player. + + `respondToNPC` + +: Whether this trigger should process events coming from NPCs. + + * `TRUE` — Do process events from NPCs. + * `FALSE` — Ignore events from NPCs. + + `startEnabled` + +: Determines whether the trigger is initially enabled. Enabled triggers will process incoming events and send + outgoing events while disabled triggers do not. Triggers can be activated and deactivated at runtime by sending + them `OnEnable`, `OnDisable` or `OnToggleEnabled` events. + + * `TRUE` — Enable the trigger when the world is loaded. + * `FALSE` — Do not enable the trigger when the world is loaded. + + `respondToVobName` + +: Whether this trigger should process events coming from VObjects with this name. If empty, match no VObject + name directly. + + `numCanBeActivated` + +: The number of times the trigger will process incoming events. If set to `-1` the trigger will process + an infinite number of events. + + `retriggerWaitSec` + +: The number of seconds that have to elapse after processing an event before the trigger will process additional events. + All events received by the trigger during that time are ignored. + + `damageThreshold` + +: The amount of damage which must be dealt for the trigger to react to an `OnDamage` event. + + `fireDelaySec` + +: The number of seconds to wait before emitting the `OnTrigger` event after processing. + + `sendUntrigger` + +: Whether to send and `OnUntrigger` event to the [`triggerTarget`](#triggerTarget) after the trigger receives an + `OnUntrigger` or `OnUntouch` event. Only fires the `OnUntrigger` event if [`reactToOnTrigger`](#reactToOnTrigger) + and [`reactToOnTouch`](#reactToOnTouch) are set to `TRUE` respectively. + + +## Sources + +The help file for the Spacer, found [here](https://wiki.worldofgothic.de/doku.php?id=spacer:hilfedatei) and the +Russian-language knowledge accumulator [gothic-library.ru](http://www.gothic-library.ru/publ/class_zctrigger/1-1-0-529). diff --git a/include/zenkit/vobs/Trigger.hh b/include/zenkit/vobs/Trigger.hh index 030197f1..3bda7dc7 100644 --- a/include/zenkit/vobs/Trigger.hh +++ b/include/zenkit/vobs/Trigger.hh @@ -67,18 +67,95 @@ namespace zenkit { random ZKREM("renamed to TriggerBatchMode::RANDOM") = RANDOM, }; - /// \brief A basic trigger VOb which does something upon the player interacting with it. + /// \brief VObject to process and relay specific filtered events. + /// + /// When a `zCTrigger` receives an `OnTrigger`, `OnDamage`, or `OnTouch` event it checks whether the event + /// source (an VNpc, the player or another VObject) should be acknowledged (see #respond_to_npc, #respond_to_pc + /// and #respond_to_object respectively). It then verifies if it should react to the specific event by checking + /// the #react_to_on_trigger, #react_to_on_touch and #react_to_on_damage properties. If both checks succeed, an + /// `OnTrigger` event is sent to the #target. `zCTrigger` objects can be enabled and disabled by firing `OnEnable`, + /// `OnDisable` and `OnToggleEnabled` events at them. + /// + /// \see https://zk.gothickit.dev/engine/objects/zCTrigger/ struct VTrigger : VirtualObject { ZK_OBJECT(ObjectType::zCTrigger); public: + /// \brief The name of VObject to send `OnTrigger` and `OnUntrigger` events to after processing. + /// \see https://zk.gothickit.dev/engine/objects/zCTrigger/#triggerTarget std::string target; - std::uint8_t flags; - std::uint8_t filter_flags; + + std::uint8_t ZKREM("replaced by `start_enabled` and `send_untrigger`") flags; + + /// \brief Determines whether the trigger is initially enabled. + /// + /// Enabled triggers will process incoming events and send outgoing events while disabled triggers do not. + /// Triggers can be activated and deactivated at runtime by sending them `OnEnable`, `OnDisable` or + /// `OnToggleEnabled` events. + /// + /// \see https://zk.gothickit.dev/engine/objects/zCTrigger/#startEnabled + bool start_enabled; + + /// \brief Whether to send and `OnUntrigger` event to the #target after the trigger receives an + /// `OnUntrigger` or `OnUntouch` event. + /// + /// Only fires the `OnUntrigger` event if #react_to_on_trigger and #react_to_on_touch are set + /// to `TRUE` respectively. + /// + /// \see https://zk.gothickit.dev/engine/objects/zCTrigger/#sendUntrigger + bool send_untrigger; + + std::uint8_t ZKREM("replaced by `react_to_*` and `respond_to_*`") filter_flags; + + /// \brief Whether this trigger should react to `OnTrigger` events. + /// \see https://zk.gothickit.dev/engine/objects/zCTrigger/#reactToOnTrigger + bool react_to_on_trigger; + + /// \brief Whether this trigger should react to `OnTouch` events. + /// \see https://zk.gothickit.dev/engine/objects/zCTrigger/#reactToOnTouch + bool react_to_on_touch; + + /// \brief Whether this trigger should react to `OnDamage` events. + /// \see https://zk.gothickit.dev/engine/objects/zCTrigger/#reactToOnDamage + bool react_to_on_damage; + + /// \brief Whether this trigger should process events coming from inanimate objects. + /// \see https://zk.gothickit.dev/engine/objects/zCTrigger/#respondToObject + bool respond_to_object; + + /// \brief Whether this trigger should process events coming from the player. + /// \see https://zk.gothickit.dev/engine/objects/zCTrigger/#respondToPC + bool respond_to_pc; + + /// \brief Whether this trigger should process events coming from NPCs. + /// \see https://zk.gothickit.dev/engine/objects/zCTrigger/#respondToNPC + bool respond_to_npc; + + /// \brief Whether this trigger should process events coming from VObjects with this name. + /// \see https://zk.gothickit.dev/engine/objects/zCTrigger/#respondToVobName std::string vob_target; + + /// \brief The number of times the trigger will process incoming events. + /// + /// If set to `-1` the trigger will process an infinite number of events. + /// + /// \see https://zk.gothickit.dev/engine/objects/zCTrigger/#numCanBeActivated std::int32_t max_activation_count; + + /// \brief The number of seconds that have to elapse after processing an event before the trigger will + /// process additional events. + /// + /// All events received by the trigger during that time are ignored. + /// + /// \see https://zk.gothickit.dev/engine/objects/zCTrigger/#retriggerWaitSec float retrigger_delay_sec; + + /// \brief The amount of damage which must be dealt for the trigger to react to an `OnDamage` event. + /// \see https://zk.gothickit.dev/engine/objects/zCTrigger/#damageThreshold float damage_threshold; + + /// \brief The number of seconds to wait before emitting the `OnTrigger` event after processing. + /// \see https://zk.gothickit.dev/engine/objects/zCTrigger/#fireDelaySec float fire_delay_sec; // Save-game only variables @@ -87,17 +164,18 @@ namespace zenkit { std::shared_ptr s_other_vob {nullptr}; bool s_is_enabled {true}; - /// \brief Parses a trigger VOb the given *ZenGin* archive. - /// \param[out] obj The object to read. - /// \param[in,out] ctx The archive reader to read from. - /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws ParserError if parsing fails. - /// \see vob::parse - /// \see trigger::parse ZKREM("use ::load()") ZKAPI static void parse(VTrigger& obj, ReadArchive& ctx, GameVersion version); + /// \brief Load this object from the given archive. + /// \param r The archive to read from; + /// \param version The version of the game the object was made for. ZKAPI void load(ReadArchive& r, GameVersion version) override; + + /// \brief Save this object to the given archive. + /// \param w The archive to save to. + /// \param version The version of the game to save for. ZKAPI void save(WriteArchive& w, GameVersion version) const override; + [[nodiscard]] ZKAPI uint16_t get_version_identifier(GameVersion game) const override; }; diff --git a/mkdocs.yml b/mkdocs.yml index 678aeb03..2af40cf8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -64,6 +64,7 @@ nav: - 'zCMoverControler': 'engine/objects/zCMoverController.md' - 'zCPFXControler': 'engine/objects/zCPFXControler.md' - 'zCTouchDamage': 'engine/objects/zCTouchDamage.md' + - 'zCTrigger': 'engine/objects/zCTrigger.md' - 'oCItem': 'engine/objects/oCItem.md' - 'zCVisual': 'engine/objects/zCVisual.md' - 'zCDecal': 'engine/objects/zCDecal.md' diff --git a/src/vobs/Trigger.cc b/src/vobs/Trigger.cc index 33cbe65a..7f635162 100644 --- a/src/vobs/Trigger.cc +++ b/src/vobs/Trigger.cc @@ -19,6 +19,15 @@ namespace zenkit { this->damage_threshold = r.read_float(); // damageThreshold this->fire_delay_sec = r.read_float(); // fireDelaySec + this->start_enabled = this->flags & 1; // startEnabled + this->send_untrigger = (this->flags & 4) >> 2; // sendUntrigger + this->react_to_on_trigger = this->filter_flags & 1; // reactToOnTrigger + this->react_to_on_touch = (this->filter_flags & 2) >> 1; // reactToOnTouch + this->react_to_on_damage = (this->filter_flags & 4) >> 2; // reactToOnDamage + this->respond_to_object = (this->filter_flags & 8) >> 3; // respondToObject + this->respond_to_pc = (this->filter_flags & 16) >> 4; // respondToPC + this->respond_to_npc = (this->filter_flags & 32) >> 5; // respondToNPC + this->s_count_can_be_activated = this->max_activation_count; if (r.is_save_game()) {