From 3de983a33879c1e0ae46f229c45d4ce85048c1ed Mon Sep 17 00:00:00 2001 From: Petr Ohlidal Date: Sun, 23 Jun 2024 23:15:56 +0200 Subject: [PATCH] :bug: Fixed hook/tie/rope glitches. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PROBLEMS: * when you release a load from a hook (be it by pressing L or Hard-resetting), the skeletonview and physics paused states don't get reset. In 2022.12 it's the same except physicspause does get reset when unhooking by L (not by hardreset tho.). * when you release a load from ties using O key, skeleton view gets reset, but physics-pause does not. Also when you hard reset, everything remains dirty. 2022.12 behaves the same. * When you Hard-reset or delete a vehicle with hooks/ties attached, they are not removed correctly, causing a crash if re-attached. The `DisjoinInterActorBeams()` helper (used by SyncReset() and Actor destructor) was leaving data in inconsistent state - specifically, it properly removed the inter-beams themselves and linkage records (both the per-actor and global lists), but it didn't update the `tie_t/hook_t/rope_t` objects itself - these kept pointing to the deleted/reset actor. SOLUTIONS: * Remake the actor-linking code to track when 2 actors become linked/unlinked - and sync the sekeletonview & physicspause there. * Instead of employing custom code to do Hard-reset, extend the existing (old!) `hookToggle()/tieToggle()/ropeToggle()` funcs (used for regular locking/unlocking of those elements) to also do forced unlocking upon removing/SyncReset-ing an actor, and then modify `DisjoinInterActorBeams()` to use them. DEV NOTE: This is quite a significant remake of actor-linking code; a direct follow-up to d36c4ecdd72d751f934b3a76b256578563bfb9d6 which introduced ✉✉ MSG_SIM_ACTOR_LINKING_REQUESTED. A remake was needed because there was no single spot to correctly reset the skeleton/physicspause on actor unlink - there was simply no concept of "actors were just linked/unlinked". While researching how to add it, I realized scripts may want to know when that happens, so I added a script event `SE_GENERIC_TRUCK_LINKING_CHANGED` for it. Keep in mind there can be multiple interlinking beams at the same time, so not every added/removed beam means linking changes. The new event also helps navigate the codebase and documents how stuff works. CODECHANGES: * SimData.h: the ActorLinkingRequestType enum was merged with HookAction enum and got new fields: HOOK_RESET, TIE_RESET, ROPE_RESET - the `DisjoinInterActorBeams()` function now uses these. * Actor.h: `hookToggle()/tieToggle()/ropeToggle()` funcs got new parameter `forceunlock_filter` and all now accept ActorLinkingRequestType param; * Actor.cpp: `SyncReset()` no longer manipulates beams/hooks/ties/ropes directly - that's all done by the extended `DisjoinInterActorBeams()`. * ScriptEvents.h - added SE_GENERIC_TRUCK_LINKING_CHANGED * All other files are just fallout from changes above. --- doc/angelscript/Script2Game/globals.h | 4 +- source/main/GameContext.cpp | 7 +- source/main/gameplay/SceneMouse.cpp | 5 +- source/main/gameplay/ScriptEvents.h | 2 + source/main/gui/panels/GUI_VehicleButtons.cpp | 5 +- source/main/main.cpp | 19 +- source/main/physics/Actor.cpp | 332 ++++++++---------- source/main/physics/Actor.h | 18 +- source/main/physics/ActorForcesEuler.cpp | 6 +- source/main/physics/ActorManager.cpp | 22 +- source/main/physics/ActorManager.h | 2 +- source/main/physics/Savegame.cpp | 2 +- source/main/physics/SimData.h | 32 +- .../bindings/ScriptEventsAngelscript.cpp | 2 + 14 files changed, 212 insertions(+), 246 deletions(-) diff --git a/doc/angelscript/Script2Game/globals.h b/doc/angelscript/Script2Game/globals.h index 37ba26aa55..908631c2ae 100644 --- a/doc/angelscript/Script2Game/globals.h +++ b/doc/angelscript/Script2Game/globals.h @@ -75,7 +75,9 @@ void print(const string message); SE_GENERIC_MESSAGEBOX_CLICK //!< triggered when the user clicks on a message box button, the argument refers to the button pressed SE_GENERIC_EXCEPTION_CAUGHT //!< Triggered when C++ exception (usually Ogre::Exception) is thrown; #1 ScriptUnitID, #5 originFuncName, #6 type, #7 message. - SE_GENERIC_MODCACHE_ACTIVITY //!< Triggered when status of modcache changes, args: #1 type, #2 entry number, for other args see `RoR::modCacheActivityType` + SE_GENERIC_MODCACHE_ACTIVITY //!< Triggered when status of modcache changes, args: #1 type, #2 entry number, for other args see `RoR::modCacheActivityType` + + SE_GENERIC_TRUCK_LINKING_CHANGED //!< Triggered when 2 actors become linked or unlinked via ties/hooks/ropes/slidenodes; args: #1 state (1=linked, 0=unlinked), #2 action `ActorLinkingRequestType` #3 master ActorInstanceID_t, #4 slave ActorInstanceID_t SE_ALL_EVENTS = 0xffffffff, SE_NO_EVENTS = 0 diff --git a/source/main/GameContext.cpp b/source/main/GameContext.cpp index 89f4d911c9..076e5993a9 100644 --- a/source/main/GameContext.cpp +++ b/source/main/GameContext.cpp @@ -1367,21 +1367,20 @@ void GameContext::UpdateCommonInputEvents(float dt) { //m_player_actor->hookToggle(-1, HOOK_TOGGLE, -1); ActorLinkingRequest* hook_rq = new ActorLinkingRequest(); - hook_rq->alr_type = ActorLinkingRequestType::HOOK_ACTION; + hook_rq->alr_type = ActorLinkingRequestType::HOOK_TOGGLE; hook_rq->alr_actor_instance_id = m_player_actor->ar_instance_id; - hook_rq->alr_hook_action = HOOK_TOGGLE; App::GetGameContext()->PushMessage(Message(MSG_SIM_ACTOR_LINKING_REQUESTED, hook_rq)); //m_player_actor->toggleSlideNodeLock(); ActorLinkingRequest* slidenode_rq = new ActorLinkingRequest(); - slidenode_rq->alr_type = ActorLinkingRequestType::SLIDENODE_ACTION; + slidenode_rq->alr_type = ActorLinkingRequestType::SLIDENODE_TOGGLE; hook_rq->alr_actor_instance_id = m_player_actor->ar_instance_id; App::GetGameContext()->PushMessage(Message(MSG_SIM_ACTOR_LINKING_REQUESTED, slidenode_rq)); } if (App::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_AUTOLOCK)) { - m_player_actor->hookToggle(-2, HOOK_UNLOCK, -1); //unlock all autolocks + m_player_actor->hookToggle(-2, ActorLinkingRequestType::HOOK_UNLOCK, -1); //unlock all autolocks } //strap diff --git a/source/main/gameplay/SceneMouse.cpp b/source/main/gameplay/SceneMouse.cpp index c1a67c3879..7de5f86790 100644 --- a/source/main/gameplay/SceneMouse.cpp +++ b/source/main/gameplay/SceneMouse.cpp @@ -165,11 +165,10 @@ bool SceneMouse::mouseMoved(const OIS::MouseEvent& _arg) { if (it->hk_hook_node->pos == minnode) { - //grab_truck->hookToggle(it->hk_group, MOUSE_HOOK_TOGGLE, minnode); + //grab_truck->hookToggle(it->hk_group, HOOK_MOUSE_TOGGLE, minnode); ActorLinkingRequest* rq = new ActorLinkingRequest(); - rq->alr_type = ActorLinkingRequestType::HOOK_ACTION; + rq->alr_type = ActorLinkingRequestType::HOOK_MOUSE_TOGGLE; rq->alr_actor_instance_id = grab_truck->ar_instance_id; - rq->alr_hook_action = MOUSE_HOOK_TOGGLE; rq->alr_hook_group = it->hk_group; rq->alr_hook_mousenode = minnode; App::GetGameContext()->PushMessage(Message(MSG_SIM_ACTOR_LINKING_REQUESTED, rq)); diff --git a/source/main/gameplay/ScriptEvents.h b/source/main/gameplay/ScriptEvents.h index f454673e31..6ac2d6af98 100644 --- a/source/main/gameplay/ScriptEvents.h +++ b/source/main/gameplay/ScriptEvents.h @@ -61,6 +61,8 @@ enum scriptEvents SE_GENERIC_EXCEPTION_CAUGHT = BITMASK(24), //!< Triggered when C++ exception (usually Ogre::Exception) is thrown; #1 ScriptUnitID, #5 originFuncName, #6 type, #7 message. SE_GENERIC_MODCACHE_ACTIVITY = BITMASK(25), //!< Triggered when status of modcache changes, args: #1 type, #2 entry number, for other args see `RoR::modCacheActivityType` + SE_GENERIC_TRUCK_LINKING_CHANGED = BITMASK(26), //!< Triggered when 2 actors become linked or unlinked via ties/hooks/ropes/slidenodes; args: #1 state (1=linked, 0=unlinked), #2 action `ActorLinkingRequestType` #3 master ActorInstanceID_t, #4 slave ActorInstanceID_t + SE_ALL_EVENTS = 0xffffffff, SE_NO_EVENTS = 0 diff --git a/source/main/gui/panels/GUI_VehicleButtons.cpp b/source/main/gui/panels/GUI_VehicleButtons.cpp index e2b30a3552..8f0790910b 100644 --- a/source/main/gui/panels/GUI_VehicleButtons.cpp +++ b/source/main/gui/panels/GUI_VehicleButtons.cpp @@ -1183,14 +1183,13 @@ void VehicleButtons::DrawLockButton(RoR::GfxActor* actorx) { //actorx->GetActor()->hookToggle(-1, HOOK_TOGGLE, -1); ActorLinkingRequest* hook_rq = new ActorLinkingRequest(); - hook_rq->alr_type = ActorLinkingRequestType::HOOK_ACTION; + hook_rq->alr_type = ActorLinkingRequestType::HOOK_TOGGLE; hook_rq->alr_actor_instance_id = actorx->GetActor()->ar_instance_id; - hook_rq->alr_hook_action = HOOK_TOGGLE; App::GetGameContext()->PushMessage(Message(MSG_SIM_ACTOR_LINKING_REQUESTED, hook_rq)); //actorx->GetActor()->toggleSlideNodeLock(); ActorLinkingRequest* slidenode_rq = new ActorLinkingRequest(); - slidenode_rq->alr_type = ActorLinkingRequestType::SLIDENODE_ACTION; + slidenode_rq->alr_type = ActorLinkingRequestType::SLIDENODE_TOGGLE; hook_rq->alr_actor_instance_id = actorx->GetActor()->ar_instance_id; App::GetGameContext()->PushMessage(Message(MSG_SIM_ACTOR_LINKING_REQUESTED, slidenode_rq)); } diff --git a/source/main/main.cpp b/source/main/main.cpp index 588d2674b9..383b38748b 100644 --- a/source/main/main.cpp +++ b/source/main/main.cpp @@ -1142,23 +1142,26 @@ int main(int argc, char *argv[]) { switch (request->alr_type) { - case ActorLinkingRequestType::HOOK_ACTION: - actor->hookToggle(request->alr_hook_group, request->alr_hook_action, request->alr_hook_mousenode); - if (request->alr_hook_action == MOUSE_HOOK_TOGGLE) - { + case ActorLinkingRequestType::HOOK_LOCK: + case ActorLinkingRequestType::HOOK_UNLOCK: + case ActorLinkingRequestType::HOOK_TOGGLE: + actor->hookToggle(request->alr_hook_group, request->alr_type); + break; + + case ActorLinkingRequestType::HOOK_MOUSE_TOGGLE: + actor->hookToggle(request->alr_hook_group, request->alr_type, request->alr_hook_mousenode); TRIGGER_EVENT_ASYNC(SE_TRUCK_MOUSE_GRAB, request->alr_actor_instance_id); - } break; - case ActorLinkingRequestType::TIE_ACTION: + case ActorLinkingRequestType::TIE_TOGGLE: actor->tieToggle(request->alr_tie_group); break; - case ActorLinkingRequestType::ROPE_ACTION: + case ActorLinkingRequestType::ROPE_TOGGLE: actor->ropeToggle(request->alr_rope_group); break; - case ActorLinkingRequestType::SLIDENODE_ACTION: + case ActorLinkingRequestType::SLIDENODE_TOGGLE: actor->toggleSlideNodeLock(); break; } diff --git a/source/main/physics/Actor.cpp b/source/main/physics/Actor.cpp index c6109c7db4..2343b15d5d 100644 --- a/source/main/physics/Actor.cpp +++ b/source/main/physics/Actor.cpp @@ -87,9 +87,12 @@ Actor::~Actor() void Actor::dispose() { + // Handler for `MSG_SIM_DELETE_ACTOR_REQUESTED` message - should not be invoked otherwise. + // -------------------------------------------------------------------------------------- + ROR_ASSERT(ar_state != ActorState::DISPOSED); - this->DisjoinInterActorBeams(); + this->DisjoinInterActorBeams(); // OK to be invoked here - processing `MSG_SIM_DELETE_ACTOR_REQUESTED`. ar_hooks.clear(); ar_ties.clear(); ar_node_to_beam_connections.clear(); @@ -824,8 +827,9 @@ float Actor::getTotalMass(bool withLocked) void Actor::DetermineLinkedActors() { - // This updates `ar_linked_actors` by searching (iteratively) through global `inter_actor_links` list. + // BEWARE: `ar_linked_actors` includes both direct and indirect links! // -------------------------------------------------------------------------------------------------- + ar_linked_actors.clear(); bool found = true; @@ -1621,6 +1625,8 @@ void Actor::SyncReset(bool reset_position) float cur_rot = getRotation(); Vector3 cur_position = ar_nodes[0].AbsPosition; + this->DisjoinInterActorBeams(); // OK to be invoked here - SyncReset() - `processing MSG_SIM_MODIFY_ACTOR_REQUESTED` + for (int i = 0; i < ar_num_nodes; i++) { ar_nodes[i].AbsPosition = ar_initial_node_positions[i]; @@ -1643,40 +1649,21 @@ void Actor::SyncReset(bool reset_position) this->applyNodeBeamScales(); - this->DisjoinInterActorBeams(); + // Extra cleanup for inter-actor beams (until the above `ar_beams` loop is fixed) for (auto& h : ar_hooks) { - h.hk_locked = UNLOCKED; - h.hk_lock_node = nullptr; - h.hk_locked_actor = nullptr; - h.hk_beam->p2 = &ar_nodes[0]; - h.hk_beam->bm_disabled = true; - h.hk_beam->bm_inter_actor = false; - h.hk_beam->L = (ar_nodes[0].AbsPosition - h.hk_hook_node->AbsPosition).length(); - this->RemoveInterActorBeam(h.hk_beam); - } - - for (auto& r : ar_ropes) - { - r.rp_locked = UNLOCKED; - r.rp_locked_ropable = nullptr; - r.rp_locked_actor = nullptr; - this->RemoveInterActorBeam(r.rp_beam); + h.hk_beam->bm_disabled = true; // should only be active if the hook is locked } for (auto& t : ar_ties) { - t.ti_tied = false; - t.ti_tying = false; - t.ti_locked_actor = nullptr; - t.ti_locked_ropable = nullptr; - t.ti_beam->p2 = &ar_nodes[0]; - t.ti_beam->bm_disabled = true; - t.ti_beam->bm_inter_actor = false; - this->RemoveInterActorBeam(t.ti_beam); + t.ti_locked_ropable = nullptr; // `tieToggle()` doesn't do this - bug or feature? ~ ohlidalp, 06/2024 + t.ti_beam->bm_disabled = true; // should only be active if the tie is tied } + // End extra cleanup + for (auto& r : ar_ropables) { r.attached_ties = 0; @@ -2756,9 +2743,8 @@ void Actor::CalcTriggers(int i, Real difftoBeamL, bool trigger_hooks) //autolock hooktoggle unlock //hookToggle(ar_beams[i].shock->trigger_cmdlong, HOOK_UNLOCK, NODENUM_INVALID); ActorLinkingRequest* rq = new ActorLinkingRequest(); - rq->alr_type = ActorLinkingRequestType::HOOK_ACTION; + rq->alr_type = ActorLinkingRequestType::HOOK_UNLOCK; rq->alr_actor_instance_id = ar_instance_id; - rq->alr_hook_action = HOOK_UNLOCK; rq->alr_hook_group = ar_beams[i].shock->trigger_cmdlong; App::GetGameContext()->PushMessage(Message(MSG_SIM_ACTOR_LINKING_REQUESTED, rq)); } @@ -2770,9 +2756,8 @@ void Actor::CalcTriggers(int i, Real difftoBeamL, bool trigger_hooks) //autolock hooktoggle lock //hookToggle(ar_beams[i].shock->trigger_cmdlong, HOOK_LOCK, NODENUM_INVALID); ActorLinkingRequest* rq = new ActorLinkingRequest(); - rq->alr_type = ActorLinkingRequestType::HOOK_ACTION; + rq->alr_type = ActorLinkingRequestType::HOOK_LOCK; rq->alr_actor_instance_id = ar_instance_id; - rq->alr_hook_action = HOOK_LOCK; rq->alr_hook_group = ar_beams[i].shock->trigger_cmdlong; App::GetGameContext()->PushMessage(Message(MSG_SIM_ACTOR_LINKING_REQUESTED, rq)); } @@ -2807,9 +2792,8 @@ void Actor::CalcTriggers(int i, Real difftoBeamL, bool trigger_hooks) //autolock hooktoggle unlock //hookToggle(ar_beams[i].shock->trigger_cmdshort, HOOK_UNLOCK, NODENUM_INVALID); ActorLinkingRequest* rq = new ActorLinkingRequest(); - rq->alr_type = ActorLinkingRequestType::HOOK_ACTION; + rq->alr_type = ActorLinkingRequestType::HOOK_UNLOCK; rq->alr_actor_instance_id = ar_instance_id; - rq->alr_hook_action = HOOK_UNLOCK; rq->alr_hook_group = ar_beams[i].shock->trigger_cmdshort; App::GetGameContext()->PushMessage(Message(MSG_SIM_ACTOR_LINKING_REQUESTED, rq)); } @@ -2821,9 +2805,8 @@ void Actor::CalcTriggers(int i, Real difftoBeamL, bool trigger_hooks) //autolock hooktoggle lock //hookToggle(ar_beams[i].shock->trigger_cmdshort, HOOK_LOCK, NODENUM_INVALID); ActorLinkingRequest* rq = new ActorLinkingRequest(); - rq->alr_type = ActorLinkingRequestType::HOOK_ACTION; + rq->alr_type = ActorLinkingRequestType::HOOK_LOCK; rq->alr_actor_instance_id = ar_instance_id; - rq->alr_hook_action = HOOK_LOCK; rq->alr_hook_group = ar_beams[i].shock->trigger_cmdshort; App::GetGameContext()->PushMessage(Message(MSG_SIM_ACTOR_LINKING_REQUESTED, rq)); } @@ -3331,82 +3314,120 @@ void Actor::updateVisual(float dt) ar_hydro_elevator_command = autoelevator; } -void Actor::AddInterActorBeam(beam_t* beam, ActorPtr a, ActorPtr b) +void Actor::AddInterActorBeam(beam_t* beam, ActorPtr other, ActorLinkingRequestType type) { - beam->bm_locked_actor = b; + // We can't assert the beam setup here because ropes do it differently (not actually using inter-beams, just exhibiting the same gamelogic). + beam->bm_locked_actor = other; // This isn't entirely valid for 'ropes' either, but for compatibility I won't touch it now ~ ohlidalp, 2024 auto pos = std::find(ar_inter_beams.begin(), ar_inter_beams.end(), beam); + ROR_ASSERT(pos == ar_inter_beams.end()); if (pos == ar_inter_beams.end()) { ar_inter_beams.push_back(beam); } - std::pair actor_pair(a, b); + const bool linked_before = App::GetGameContext()->GetActorManager()->AreActorsDirectlyLinked(this, other); + ROR_ASSERT(App::GetGameContext()->GetActorManager()->inter_actor_links.find(beam) == App::GetGameContext()->GetActorManager()->inter_actor_links.end()); + std::pair actor_pair(this, other); App::GetGameContext()->GetActorManager()->inter_actor_links[beam] = actor_pair; + const bool linked_now = App::GetGameContext()->GetActorManager()->AreActorsDirectlyLinked(this, other); + + if (linked_before != linked_now) + { + // Update lists of directly/indirectly linked actors. + this->DetermineLinkedActors(); + for (ActorPtr& actor : this->ar_linked_actors) + actor->DetermineLinkedActors(); - a->DetermineLinkedActors(); - for (ActorPtr& actor : a->ar_linked_actors) - actor->DetermineLinkedActors(); + other->DetermineLinkedActors(); + for (ActorPtr& actor : other->ar_linked_actors) + actor->DetermineLinkedActors(); - b->DetermineLinkedActors(); - for (ActorPtr& actor : b->ar_linked_actors) - actor->DetermineLinkedActors(); + // Forward toggled states. + for (ActorPtr& actor : this->ar_linked_actors) + { + actor->ar_physics_paused = this->ar_physics_paused; + actor->GetGfxActor()->SetDebugView(this->GetGfxActor()->GetDebugView()); + } + + // Let scripts know. + TRIGGER_EVENT_ASYNC(SE_GENERIC_TRUCK_LINKING_CHANGED, 1, (int)type, this->ar_instance_id, other->ar_instance_id); + } } -void Actor::RemoveInterActorBeam(beam_t* beam) +void Actor::RemoveInterActorBeam(beam_t* beam, ActorLinkingRequestType type) { + ROR_ASSERT(beam->bm_locked_actor); + ActorPtr other = beam->bm_locked_actor; + beam->bm_locked_actor = nullptr; + auto pos = std::find(ar_inter_beams.begin(), ar_inter_beams.end(), beam); + ROR_ASSERT(pos != ar_inter_beams.end()); if (pos != ar_inter_beams.end()) { ar_inter_beams.erase(pos); } + const bool linked_before = App::GetGameContext()->GetActorManager()->AreActorsDirectlyLinked(this, other); auto it = App::GetGameContext()->GetActorManager()->inter_actor_links.find(beam); + ROR_ASSERT(it != App::GetGameContext()->GetActorManager()->inter_actor_links.end()); if (it != App::GetGameContext()->GetActorManager()->inter_actor_links.end()) { - auto actor_pair = it->second; App::GetGameContext()->GetActorManager()->inter_actor_links.erase(it); + } + const bool linked_now = App::GetGameContext()->GetActorManager()->AreActorsDirectlyLinked(this, other); - actor_pair.first->DetermineLinkedActors(); - for (ActorPtr& actor : actor_pair.first->ar_linked_actors) + if (linked_before != linked_now) + { + // Update lists of directly/indirectly linked actors. + this->DetermineLinkedActors(); + for (ActorPtr& actor : this->ar_linked_actors) actor->DetermineLinkedActors(); - actor_pair.second->DetermineLinkedActors(); - for (ActorPtr& actor : actor_pair.second->ar_linked_actors) + other->DetermineLinkedActors(); + for (ActorPtr& actor : other->ar_linked_actors) actor->DetermineLinkedActors(); + + // Reset toggled states. + other->ar_physics_paused = false; + other->GetGfxActor()->SetDebugView(DebugViewType::DEBUGVIEW_NONE); + for (ActorPtr& actor : other->ar_linked_actors) + { + actor->ar_physics_paused = false; + actor->GetGfxActor()->SetDebugView(DebugViewType::DEBUGVIEW_NONE); + } + + // Let scripts know. + TRIGGER_EVENT_ASYNC(SE_GENERIC_TRUCK_LINKING_CHANGED, 0, (int)type, this->ar_instance_id, other->ar_instance_id); } } void Actor::DisjoinInterActorBeams() { - ar_inter_beams.clear(); - auto inter_actor_links = &App::GetGameContext()->GetActorManager()->inter_actor_links; - for (auto it = inter_actor_links->begin(); it != inter_actor_links->end();) - { - auto actor_pair = it->second; - if (this == actor_pair.first.GetRef() || this == actor_pair.second.GetRef()) - { - it->first->bm_locked_actor = nullptr; - it->first->bm_inter_actor = false; - it->first->bm_disabled = true; - inter_actor_links->erase(it++); + // Helper for `MSG_SIM_MODIFY/DELETE_ACTOR_REQUESTED`, do not invoke otherwise! + // Removes all (both ways) inter-actor connections from this actor. + // Note the repetitive 'OK to be invoked...' comments are for fulltext search results. + // ------------------------------------------------------------------ - actor_pair.first->DetermineLinkedActors(); - for (ActorPtr& actor : actor_pair.first->ar_linked_actors) - actor->DetermineLinkedActors(); + // Remove all inter-linking beams which belong to this actor. + this->hookToggle(-1, ActorLinkingRequestType::HOOK_RESET); // OK to be invoked here - DisjoinInterActorBeams() - `processing MSG_SIM_MODIFY/DELETE_ACTOR_REQUESTED` + this->ropeToggle(-1, ActorLinkingRequestType::ROPE_RESET); // OK to be invoked here - DisjoinInterActorBeams() - `processing MSG_SIM_MODIFY/DELETE_ACTOR_REQUESTED` + this->tieToggle(-1, ActorLinkingRequestType::TIE_RESET); // OK to be invoked here - DisjoinInterActorBeams() - `processing MSG_SIM_MODIFY/DELETE_ACTOR_REQUESTED` - actor_pair.second->DetermineLinkedActors(); - for (ActorPtr& actor : actor_pair.second->ar_linked_actors) - actor->DetermineLinkedActors(); - } - else - { - ++it; - } + // Remove any possible links from other actors to this actor. + for (ActorPtr& other_actor : App::GetGameContext()->GetActorManager()->GetActors()) + { + if (other_actor->ar_state != ActorState::LOCAL_SIMULATED) + continue; + + // Use the new `unlock_filter` param to only unlock the links to this actor (brute force but safe approach). + other_actor->hookToggle(-1, ActorLinkingRequestType::HOOK_RESET, NODENUM_INVALID, /*unlock_filter:*/ar_instance_id); // OK to be invoked here - DisjoinInterActorBeams() - `processing MSG_SIM_MODIFY/DELETE_ACTOR_REQUESTED` + other_actor->tieToggle(-1, ActorLinkingRequestType::TIE_RESET, /*unlock_filter:*/ar_instance_id); // OK to be invoked here - DisjoinInterActorBeams() - `processing MSG_SIM_MODIFY/DELETE_ACTOR_REQUESTED` + other_actor->ropeToggle(-1, ActorLinkingRequestType::ROPE_RESET, /*unlock_filter:*/ar_instance_id); // OK to be invoked here - DisjoinInterActorBeams() - `processing MSG_SIM_MODIFY/DELETE_ACTOR_REQUESTED` } } -void Actor::tieToggle(int group) +void Actor::tieToggle(int group, ActorLinkingRequestType mode, ActorInstanceID_t forceunlock_filter) { ActorPtr player_actor = App::GetGameContext()->GetPlayerActor(); @@ -3419,7 +3440,14 @@ void Actor::tieToggle(int group) if (group != -1 && (it->ti_group != -1 && it->ti_group != group)) continue; - // if tied, untie it. And the other way round + // When RESET-ing, filter by the locked actor, if specified. + if (mode == ActorLinkingRequestType::TIE_RESET + && forceunlock_filter != ACTORINSTANCEID_INVALID && it->ti_locked_actor && it->ti_locked_actor->ar_instance_id != forceunlock_filter) + { + continue; + } + + // if tied, untie it. if (it->ti_tied) { istied = !it->ti_beam->bm_disabled; @@ -3435,35 +3463,14 @@ void Actor::tieToggle(int group) it->ti_beam->bm_disabled = true; if (it->ti_locked_actor != this) { - this->RemoveInterActorBeam(it->ti_beam); - // update skeletonview on the untied actors - auto linked_actors = it->ti_locked_actor->ar_linked_actors; - if (!(std::find(linked_actors.begin(), linked_actors.end(), this) != linked_actors.end())) - { - if (this == player_actor.GetRef()) - { - it->ti_locked_actor->GetGfxActor()->SetDebugView(DebugViewType::DEBUGVIEW_NONE); - for (ActorPtr& actor : it->ti_locked_actor->ar_linked_actors) - { - actor->GetGfxActor()->SetDebugView(DebugViewType::DEBUGVIEW_NONE); - } - } - else if (it->ti_locked_actor == player_actor) - { - m_gfx_actor->SetDebugView(DebugViewType::DEBUGVIEW_NONE); - for (ActorPtr& actor : this->ar_linked_actors) - { - actor->GetGfxActor()->SetDebugView(DebugViewType::DEBUGVIEW_NONE); - } - } - } + this->RemoveInterActorBeam(it->ti_beam, mode); // OK to invoke here - tieToggle() - processing `MSG_SIM_ACTOR_LINKING_REQUESTED` } it->ti_locked_actor = nullptr; } } // iterate over all ties - if (!istied) + if (!istied && mode == ActorLinkingRequestType::TIE_TOGGLE) { for (std::vector::iterator it = ar_ties.begin(); it != ar_ties.end(); it++) { @@ -3526,24 +3533,7 @@ void Actor::tieToggle(int group) it->ti_locked_ropable->attached_ties++; if (it->ti_beam->bm_inter_actor) { - AddInterActorBeam(it->ti_beam, this, nearest_actor); - // update skeletonview on the tied actors - if (this == player_actor.GetRef()) - { - nearest_actor->GetGfxActor()->SetDebugView(m_gfx_actor->GetDebugView()); - for (ActorPtr& actor : nearest_actor->ar_linked_actors) - { - actor->GetGfxActor()->SetDebugView(m_gfx_actor->GetDebugView()); - } - } - else if (nearest_actor == player_actor) - { - m_gfx_actor->SetDebugView(player_actor->GetGfxActor()->GetDebugView()); - for (ActorPtr& actor : this->ar_linked_actors) - { - actor->GetGfxActor()->SetDebugView(player_actor->GetGfxActor()->GetDebugView()); - } - } + this->AddInterActorBeam(it->ti_beam, nearest_actor, mode); // OK to invoke here - tieToggle() - processing `MSG_SIM_ACTOR_LINKING_REQUESTED` } } } @@ -3554,7 +3544,7 @@ void Actor::tieToggle(int group) TRIGGER_EVENT_ASYNC(SE_TRUCK_TIE_TOGGLE, ar_instance_id); } -void Actor::ropeToggle(int group) +void Actor::ropeToggle(int group, ActorLinkingRequestType mode, ActorInstanceID_t forceunlock_filter) { ActorPtr player_actor = App::GetGameContext()->GetPlayerActor(); @@ -3565,7 +3555,14 @@ void Actor::ropeToggle(int group) if (group != -1 && (it->rp_group != -1 && it->rp_group != group)) continue; - if (it->rp_locked == LOCKED || it->rp_locked == PRELOCK) + // When RESET-ing, filter by the locked actor, if specified. + if (mode == ActorLinkingRequestType::ROPE_RESET + && forceunlock_filter != ACTORINSTANCEID_INVALID && it->rp_locked_actor && it->rp_locked_actor->ar_instance_id != forceunlock_filter) + { + continue; + } + + if (it->rp_locked == LOCKED || it->rp_locked == PRELOCK) // Do this for both `ROPE_TOGGLE` and `ROPE_RESET` { // we unlock ropes it->rp_locked = UNLOCKED; @@ -3574,33 +3571,12 @@ void Actor::ropeToggle(int group) it->rp_locked_ropable->attached_ropes--; if (it->rp_locked_actor != this) { - this->RemoveInterActorBeam(it->rp_beam); - // update skeletonview on the unroped actors - auto linked_actors = it->rp_locked_actor->ar_linked_actors; - if (!(std::find(linked_actors.begin(), linked_actors.end(), this) != linked_actors.end())) - { - if (this == player_actor.GetRef()) - { - it->rp_locked_actor->GetGfxActor()->SetDebugView(DebugViewType::DEBUGVIEW_NONE); - for (ActorPtr& actor : it->rp_locked_actor->ar_linked_actors) - { - actor->GetGfxActor()->SetDebugView(DebugViewType::DEBUGVIEW_NONE); - } - } - else if (it->rp_locked_actor == player_actor) - { - m_gfx_actor->SetDebugView(DebugViewType::DEBUGVIEW_NONE); - for (ActorPtr& actor : this->ar_linked_actors) - { - actor->GetGfxActor()->SetDebugView(DebugViewType::DEBUGVIEW_NONE); - } - } - } + this->RemoveInterActorBeam(it->rp_beam, mode); // OK to invoke here - ropeToggle() - processing `MSG_SIM_ACTOR_LINKING_REQUESTED` } it->rp_locked_actor = nullptr; it->rp_locked_ropable = nullptr; } - else + else if (mode == ActorLinkingRequestType::ROPE_TOGGLE) // Do this only for `ROPE_TOGGLE` { //we lock ropes // search new remote ropable to lock to @@ -3633,84 +3609,77 @@ void Actor::ropeToggle(int group) if (nearest_actor) { //okay, we have found a rope to tie - it->rp_locked_actor = nearest_actor; it->rp_locked = LOCKED; it->rp_locked_ropable = rop; it->rp_locked_ropable->attached_ropes++; if (nearest_actor != this) { - AddInterActorBeam(it->rp_beam, this, nearest_actor); - // update skeletonview on the roped up actors - if (this == player_actor.GetRef()) - { - nearest_actor->GetGfxActor()->SetDebugView(m_gfx_actor->GetDebugView()); - for (ActorPtr& actor : nearest_actor->ar_linked_actors) - { - actor->GetGfxActor()->SetDebugView(m_gfx_actor->GetDebugView()); - } - } - else if (nearest_actor == player_actor) - { - m_gfx_actor->SetDebugView(player_actor->GetGfxActor()->GetDebugView()); - for (ActorPtr& actor : this->ar_linked_actors) - { - actor->GetGfxActor()->SetDebugView(player_actor->GetGfxActor()->GetDebugView()); - } - } + this->AddInterActorBeam(it->rp_beam, nearest_actor, mode); // OK to invoke here - ropeToggle() - processing `MSG_SIM_ACTOR_LINKING_REQUESTED` } } } } } -void Actor::hookToggle(int group, HookAction mode, NodeNum_t mousenode /*=NODENUM_INVALID*/) +void Actor::hookToggle(int group, ActorLinkingRequestType mode, NodeNum_t mousenode /*=NODENUM_INVALID*/, ActorInstanceID_t forceunlock_filter) { + ROR_ASSERT(mode == ActorLinkingRequestType::HOOK_LOCK || mode == ActorLinkingRequestType::HOOK_UNLOCK + || mode == ActorLinkingRequestType::HOOK_TOGGLE || mode == ActorLinkingRequestType::HOOK_MOUSE_TOGGLE + || mode == ActorLinkingRequestType::HOOK_RESET); + // iterate over all hooks for (std::vector::iterator it = ar_hooks.begin(); it != ar_hooks.end(); it++) { - if (mode == MOUSE_HOOK_TOGGLE && it->hk_hook_node->pos != mousenode) + if (mode == ActorLinkingRequestType::HOOK_MOUSE_TOGGLE && it->hk_hook_node->pos != mousenode) { //skip all other nodes except the one manually toggled by mouse continue; } - if (mode == HOOK_TOGGLE && group == -1) + if (mode == ActorLinkingRequestType::HOOK_TOGGLE && group == -1) { //manually triggerd (EV_COMMON_LOCK). Toggle all hooks groups with group#: -1, 0, 1 ++ if (it->hk_group <= -2) continue; } - if (mode == HOOK_LOCK && group == -2) + if (mode == ActorLinkingRequestType::HOOK_LOCK && group == -2) { //automatic lock attempt (cyclic with doupdate). Toggle all hooks groups with group#: -2, -3, -4 --, skip the ones which are not autolock (triggered only) if (it->hk_group >= -1 || !it->hk_autolock) continue; } - if (mode == HOOK_UNLOCK && group == -2) + if (mode == ActorLinkingRequestType::HOOK_UNLOCK && group == -2) { //manual unlock ALL autolock and triggerlock, do not unlock standard hooks (EV_COMMON_AUTOLOCK) if (it->hk_group >= -1 || !it->hk_autolock) continue; } - if ((mode == HOOK_LOCK || mode == HOOK_UNLOCK) && group <= -3) + if ((mode == ActorLinkingRequestType::HOOK_LOCK || mode == ActorLinkingRequestType::HOOK_UNLOCK) && group <= -3) { //trigger beam lock or unlock. Toggle one hook group with group#: group if (it->hk_group != group) continue; } - if ((mode == HOOK_LOCK || mode == HOOK_UNLOCK) && group >= -1) + if ((mode == ActorLinkingRequestType::HOOK_LOCK || mode == ActorLinkingRequestType::HOOK_UNLOCK) && group >= -1) { continue; } - if (mode == HOOK_LOCK && it->hk_timer > 0.0f) + if (mode == ActorLinkingRequestType::HOOK_LOCK && it->hk_timer > 0.0f) { //check relock delay timer for autolock nodes and skip if not 0 continue; } + // When RESET-ing, filter by the locked actor, if specified. + if (mode == ActorLinkingRequestType::HOOK_RESET + && forceunlock_filter != ACTORINSTANCEID_INVALID && it->hk_locked_actor && it->hk_locked_actor->ar_instance_id != forceunlock_filter) + { + continue; + } + ActorPtr prev_locked_actor = it->hk_locked_actor; // memorize current value // do this only for toggle or lock attempts, skip prelocked or locked nodes for performance - if (mode != HOOK_UNLOCK && it->hk_locked == UNLOCKED) + if ((mode != ActorLinkingRequestType::HOOK_UNLOCK && mode != ActorLinkingRequestType::HOOK_RESET) && it->hk_locked == UNLOCKED) { // we lock hooks // search new remote ropable to lock to @@ -3764,17 +3733,17 @@ void Actor::hookToggle(int group, HookAction mode, NodeNum_t mousenode /*=NODENU it->hk_beam->bm_inter_actor = (it->hk_locked_actor != nullptr); it->hk_beam->L = (it->hk_hook_node->AbsPosition - it->hk_lock_node->AbsPosition).length(); it->hk_beam->bm_disabled = false; - this->AddInterActorBeam(it->hk_beam, this, it->hk_locked_actor); + this->AddInterActorBeam(it->hk_beam, it->hk_locked_actor, mode); // OK to invoke here - hookToggle() - processing `MSG_SIM_ACTOR_LINKING_REQUESTED` } } } } // this is a locked or prelocked hook and its not a locking attempt or the locked actor was removed (bm_inter_actor == false) - else if ((it->hk_locked == LOCKED || it->hk_locked == PRELOCK) && (mode != HOOK_LOCK || !it->hk_beam->bm_inter_actor)) + else if ((it->hk_locked == LOCKED || it->hk_locked == PRELOCK) && (mode != ActorLinkingRequestType::HOOK_LOCK || !it->hk_beam->bm_inter_actor)) { // we unlock ropes immediatelly it->hk_locked = UNLOCKED; - this->RemoveInterActorBeam(it->hk_beam); + this->RemoveInterActorBeam(it->hk_beam, mode); // OK to invoke here - hookToggle() - processing `MSG_SIM_ACTOR_LINKING_REQUESTED` if (it->hk_group <= -2) { it->hk_timer = it->hk_timer_preset; //timer reset for autolock nodes @@ -3787,27 +3756,6 @@ void Actor::hookToggle(int group, HookAction mode, NodeNum_t mousenode /*=NODENU it->hk_beam->L = (ar_nodes[0].AbsPosition - it->hk_hook_node->AbsPosition).length(); it->hk_beam->bm_disabled = true; } - - // update skeletonview on the (un)hooked actor - if (it->hk_locked_actor != prev_locked_actor) - { - if (it->hk_locked_actor) - { - it->hk_locked_actor->GetGfxActor()->SetDebugView(m_gfx_actor->GetDebugView()); - for (ActorPtr& actor : it->hk_locked_actor->ar_linked_actors) - { - actor->GetGfxActor()->SetDebugView(m_gfx_actor->GetDebugView()); - } - } - else if (prev_locked_actor != this) - { - prev_locked_actor->GetGfxActor()->SetDebugView(m_gfx_actor->GetDebugView()); - for (ActorPtr& actor : prev_locked_actor->ar_linked_actors) - { - actor->GetGfxActor()->SetDebugView(m_gfx_actor->GetDebugView()); - } - } - } } } diff --git a/source/main/physics/Actor.h b/source/main/physics/Actor.h index 7bed48f850..1899b58e09 100644 --- a/source/main/physics/Actor.h +++ b/source/main/physics/Actor.h @@ -43,7 +43,9 @@ namespace RoR { /// @{ /// Softbody object; can be anything from soda can to a space shuttle -/// Former name: `Beam` (that's why scripting uses `BeamClass`) +/// Constructed from a truck definition file, see https://docs.rigsofrods.org/vehicle-creation/fileformat-truck/ +/// To spawn in-game, use `MSG_SIM_SPAWN_ACTOR_REQUESTED`, see `GameContext::PushMessage()`, in AngelScript use `game.pushMessage();` +/// Gameplay states are described by `enum ActorState`. For additional state vars see "Gameplay state" section below. class Actor : public RefCountingObject { friend class ActorSpawner; @@ -132,11 +134,11 @@ class Actor : public RefCountingObject bool getCustomParticleMode(); // not exported to scripting: void mouseMove(NodeNum_t node, Ogre::Vector3 pos, float force); - void tieToggle(int group=-1); + void tieToggle(int group=-1, ActorLinkingRequestType mode=ActorLinkingRequestType::TIE_TOGGLE, ActorInstanceID_t forceunlock_filter=ACTORINSTANCEID_INVALID); bool isTied(); - void hookToggle(int group=-1, HookAction mode=HOOK_TOGGLE, NodeNum_t mousenode=NODENUM_INVALID); + void hookToggle(int group=-1, ActorLinkingRequestType mode=ActorLinkingRequestType::HOOK_TOGGLE,NodeNum_t mousenode=NODENUM_INVALID, ActorInstanceID_t forceunlock_filter=ACTORINSTANCEID_INVALID); bool isLocked(); //!< Are hooks locked? - void ropeToggle(int group=-1); + void ropeToggle(int group=-1, ActorLinkingRequestType mode=ActorLinkingRequestType::ROPE_TOGGLE, ActorInstanceID_t forceunlock_filter=ACTORINSTANCEID_INVALID); void engineTriggerHelper(int engineNumber, EngineTriggerType type, float triggerValue); void toggleSlideNodeLock(); bool getParkingBrake() { return ar_parking_brake; } @@ -303,7 +305,6 @@ class Actor : public RefCountingObject std::vector ar_initial_node_positions; std::vector> ar_initial_beam_defaults; std::vector ar_wheeldetachers; - ActorPtrVec ar_linked_actors; //!< Sim state; other actors linked using 'hooks' std::vector> ar_node_to_node_connections; std::vector> ar_node_to_beam_connections; std::vector ar_collision_bounding_boxes; //!< smart bounding boxes, used for determining the state of an actor (every box surrounds only a subset of nodes) @@ -437,6 +438,7 @@ class Actor : public RefCountingObject // Gameplay state ActorState ar_state = ActorState::LOCAL_SIMULATED; + ActorPtrVec ar_linked_actors; //!< BEWARE: Includes indirect links, see `DetermineLinkedActors()`; Other actors linked using 'hooks/ties/ropes/slidenodes'; use `MSG_SIM_ACTOR_LINKING_REQUESTED` // Repair state Ogre::Vector3 m_rotation_request_center = Ogre::Vector3::ZERO; @@ -507,9 +509,9 @@ class Actor : public RefCountingObject void DetermineLinkedActors(); void RecalculateNodeMasses(Ogre::Real total); //!< Previously 'calc_masses2()' void calcNodeConnectivityGraph(); - void AddInterActorBeam(beam_t* beam, ActorPtr a, ActorPtr b); - void RemoveInterActorBeam(beam_t* beam); - void DisjoinInterActorBeams(); //!< Destroys all inter-actor beams which are connected with this actor + void AddInterActorBeam(beam_t* beam, ActorPtr other, ActorLinkingRequestType type); //!< Do not call directly - use `MSG_SIM_ACTOR_LINKING_REQUESTED` + void RemoveInterActorBeam(beam_t* beam, ActorLinkingRequestType type); //!< Do not call directly - use `MSG_SIM_ACTOR_LINKING_REQUESTED` + void DisjoinInterActorBeams(); //!< Helper for `MSG_` handlers, do not invoke by hand. void autoBlinkReset(); //!< Resets the turn signal when the steering wheel is turned back. void ResetAngle(float rot); void calculateLocalGForces(); //!< Derive the truck local g-forces from the global ones diff --git a/source/main/physics/ActorForcesEuler.cpp b/source/main/physics/ActorForcesEuler.cpp index 9dc6bbf12a..d4dc5b3e99 100644 --- a/source/main/physics/ActorForcesEuler.cpp +++ b/source/main/physics/ActorForcesEuler.cpp @@ -1109,9 +1109,8 @@ bool Actor::CalcForcesEulerPrepare(bool doUpdate) { //this->hookToggle(-2, HOOK_LOCK, -1); ActorLinkingRequest* rq = new ActorLinkingRequest(); - rq->alr_type = ActorLinkingRequestType::HOOK_ACTION; + rq->alr_type = ActorLinkingRequestType::HOOK_LOCK; rq->alr_actor_instance_id = ar_instance_id; - rq->alr_hook_action = HOOK_LOCK; rq->alr_hook_group = -2; App::GetGameContext()->PushMessage(Message(MSG_SIM_ACTOR_LINKING_REQUESTED, rq)); } @@ -1748,8 +1747,7 @@ void Actor::CalcHooks() //force exceeded, reset the hook node ActorLinkingRequest* rq = new ActorLinkingRequest(); rq->alr_actor_instance_id = ar_instance_id; - rq->alr_type = ActorLinkingRequestType::HOOK_ACTION; - rq->alr_hook_action = HOOK_UNLOCK; + rq->alr_type = ActorLinkingRequestType::HOOK_UNLOCK; App::GetGameContext()->PushMessage(Message(MSG_SIM_ACTOR_LINKING_REQUESTED, rq)); } } diff --git a/source/main/physics/ActorManager.cpp b/source/main/physics/ActorManager.cpp index b1922a05e4..1347c06228 100644 --- a/source/main/physics/ActorManager.cpp +++ b/source/main/physics/ActorManager.cpp @@ -685,7 +685,7 @@ void ActorManager::ForwardCommands(ActorPtr source_actor) { //actor->tieToggle(); ActorLinkingRequest* rq = new ActorLinkingRequest(); - rq->alr_type = ActorLinkingRequestType::TIE_ACTION; + rq->alr_type = ActorLinkingRequestType::TIE_TOGGLE; rq->alr_actor_instance_id = actor->ar_instance_id; rq->alr_tie_group = -1; App::GetGameContext()->PushMessage(Message(MSG_SIM_ACTOR_LINKING_REQUESTED, rq)); @@ -695,7 +695,7 @@ void ActorManager::ForwardCommands(ActorPtr source_actor) { //actor->ropeToggle(-1); ActorLinkingRequest* rq = new ActorLinkingRequest(); - rq->alr_type = ActorLinkingRequestType::ROPE_ACTION; + rq->alr_type = ActorLinkingRequestType::ROPE_TOGGLE; rq->alr_actor_instance_id = actor->ar_instance_id; rq->alr_rope_group = -1; App::GetGameContext()->PushMessage(Message(MSG_SIM_ACTOR_LINKING_REQUESTED, rq)); @@ -721,6 +721,20 @@ void ActorManager::ForwardCommands(ActorPtr source_actor) } } +bool ActorManager::AreActorsDirectlyLinked(const ActorPtr& a1, const ActorPtr& a2) +{ + for (auto& entry: inter_actor_links) + { + auto& actor_pair = entry.second; + if ((actor_pair.first == a1 && actor_pair.second == a2) || + (actor_pair.first == a2 && actor_pair.second == a1)) + { + return true; + } + } + return false; +} + void ActorManager::UpdateSleepingState(ActorPtr player_actor, float dt) { if (!m_forced_awake) @@ -1070,7 +1084,7 @@ void ActorManager::UpdateActors(ActorPtr player_actor) { //player_actor->tieToggle(); ActorLinkingRequest* rq = new ActorLinkingRequest(); - rq->alr_type = ActorLinkingRequestType::TIE_ACTION; + rq->alr_type = ActorLinkingRequestType::TIE_TOGGLE; rq->alr_actor_instance_id = player_actor->ar_instance_id; rq->alr_tie_group = -1; App::GetGameContext()->PushMessage(Message(MSG_SIM_ACTOR_LINKING_REQUESTED, rq)); @@ -1081,7 +1095,7 @@ void ActorManager::UpdateActors(ActorPtr player_actor) { //player_actor->ropeToggle(-1); ActorLinkingRequest* rq = new ActorLinkingRequest(); - rq->alr_type = ActorLinkingRequestType::ROPE_ACTION; + rq->alr_type = ActorLinkingRequestType::ROPE_TOGGLE; rq->alr_actor_instance_id = player_actor->ar_instance_id; rq->alr_rope_group = -1; App::GetGameContext()->PushMessage(Message(MSG_SIM_ACTOR_LINKING_REQUESTED, rq)); diff --git a/source/main/physics/ActorManager.h b/source/main/physics/ActorManager.h index e18b38944f..349d809886 100644 --- a/source/main/physics/ActorManager.h +++ b/source/main/physics/ActorManager.h @@ -109,8 +109,8 @@ class ActorManager std::pair GetNearestActor(Ogre::Vector3 position); - // A list of all beams interconnecting two actors std::map> inter_actor_links; + bool AreActorsDirectlyLinked(const ActorPtr& a1, const ActorPtr& a2); static const ActorPtr ACTORPTR_NULL; // Dummy value to be returned as const reference. diff --git a/source/main/physics/Savegame.cpp b/source/main/physics/Savegame.cpp index 8ef53219a8..2c0dd03b4f 100644 --- a/source/main/physics/Savegame.cpp +++ b/source/main/physics/Savegame.cpp @@ -959,7 +959,7 @@ void ActorManager::RestoreSavedState(ActorPtr actor, rapidjson::Value const& j_e locked_actor < (int)actors.size() && actors[locked_actor] != nullptr) { - actor->AddInterActorBeam(&actor->ar_beams[i], actor, actors[locked_actor]); + actor->AddInterActorBeam(&actor->ar_beams[i], actors[locked_actor], ActorLinkingRequestType::LOAD_SAVEGAME); // OK to be invoked here - RestoreSavedState() - processing MSG_SIM_MODIFY_ACTOR_REQUESTED } } diff --git a/source/main/physics/SimData.h b/source/main/physics/SimData.h index b29f3ac494..3817b9456d 100644 --- a/source/main/physics/SimData.h +++ b/source/main/physics/SimData.h @@ -61,18 +61,6 @@ enum class ExtCameraMode NODE = 2, }; -/// @addtogroup Gameplay -/// @{ - -enum HookAction -{ - HOOK_LOCK=0, - HOOK_UNLOCK, - HOOK_TOGGLE, - MOUSE_HOOK_TOGGLE, -}; - -/// @} /// @addtogroup Physics /// @{ @@ -861,10 +849,21 @@ struct ActorModifyRequest enum class ActorLinkingRequestType { INVALID, - HOOK_ACTION, - TIE_ACTION, - ROPE_ACTION, - SLIDENODE_ACTION + LOAD_SAVEGAME, + // hookToggle() + HOOK_LOCK, + HOOK_UNLOCK, + HOOK_TOGGLE, + HOOK_MOUSE_TOGGLE, + HOOK_RESET, + // tieToggle() + TIE_TOGGLE, + TIE_RESET, + // ropeToggle() + ROPE_TOGGLE, + ROPE_RESET, + // toggleSlideNodeLock() + SLIDENODE_TOGGLE }; /// Estabilishing a physics linkage between 2 actors modifies a global linkage table @@ -876,7 +875,6 @@ struct ActorLinkingRequest ActorLinkingRequestType alr_type = ActorLinkingRequestType::INVALID; // hookToggle() int alr_hook_group = -1; - HookAction alr_hook_action; NodeNum_t alr_hook_mousenode = NODENUM_INVALID; // tieToggle() int alr_tie_group = -1; diff --git a/source/main/scripting/bindings/ScriptEventsAngelscript.cpp b/source/main/scripting/bindings/ScriptEventsAngelscript.cpp index db7ee6691e..117b22976d 100644 --- a/source/main/scripting/bindings/ScriptEventsAngelscript.cpp +++ b/source/main/scripting/bindings/ScriptEventsAngelscript.cpp @@ -65,6 +65,8 @@ void RoR::RegisterScriptEvents(asIScriptEngine *engine) result = engine->RegisterEnumValue("scriptEvents", "SE_GENERIC_EXCEPTION_CAUGHT", SE_GENERIC_EXCEPTION_CAUGHT); ROR_ASSERT(result>=0); result = engine->RegisterEnumValue("scriptEvents", "SE_GENERIC_MODCACHE_ACTIVITY", SE_GENERIC_MODCACHE_ACTIVITY); ROR_ASSERT(result>=0); + result = engine->RegisterEnumValue("scriptEvents", "SE_GENERIC_TRUCK_LINKING_CHANGED", SE_GENERIC_TRUCK_LINKING_CHANGED); ROR_ASSERT(result>=0); + result = engine->RegisterEnumValue("scriptEvents", "SE_ALL_EVENTS", SE_ALL_EVENTS); ROR_ASSERT(result>=0); result = engine->RegisterEnumValue("scriptEvents", "SE_NO_EVENTS", SE_NO_EVENTS); ROR_ASSERT(result>=0);