diff --git a/src/object/pushbutton.cpp b/src/object/pushbutton.cpp index d575a43bf45..5335f4a0091 100644 --- a/src/object/pushbutton.cpp +++ b/src/object/pushbutton.cpp @@ -125,7 +125,14 @@ PushButton::collision(GameObject& other, const CollisionHit& hit) SoundManager::current()->play(BUTTON_SOUND, get_pos()); // run script - Sector::get().run_script(m_script, "PushButton"); + if (player) + { + Sector::get().run_script(m_script, "PushButton", *this, { + { player->get_name(), "Tux" } // Create trigger reference to the player + }); + } + else + Sector::get().run_script(m_script, "PushButton", *this, {}); return FORCE_MOVE; } diff --git a/src/object/scripted_object.cpp b/src/object/scripted_object.cpp index 42b31b50ab8..99e3054ecc1 100644 --- a/src/object/scripted_object.cpp +++ b/src/object/scripted_object.cpp @@ -71,7 +71,7 @@ ScriptedObject::get_settings() result.add_bool(_("Solid"), &solid, "solid", true); result.add_bool(_("Physics enabled"), &physic_enabled, "physic-enabled", true); result.add_bool(_("Visible"), &visible, "visible", true); - result.add_text(_("Hit script"), &hit_script, "hit-script"); + result.add_script(_("Hit script"), &hit_script, "hit-script"); result.reorder({"z-pos", "visible", "physic-enabled", "solid", "name", "sprite", "script", "button", "x", "y"}); @@ -200,7 +200,9 @@ ScriptedObject::collision(GameObject& other, const CollisionHit& ) { auto player = dynamic_cast (&other); if (player && !hit_script.empty()) { - Sector::get().run_script(hit_script, "hit-script"); + Sector::get().run_script(hit_script, "hit-script", *this, { + { player->get_name(), "Tux" } // Create trigger reference to the player + }); } return FORCE_MOVE; diff --git a/src/squirrel/squirrel_environment.cpp b/src/squirrel/squirrel_environment.cpp index 7f2a1f23f02..c0ecdf0548c 100644 --- a/src/squirrel/squirrel_environment.cpp +++ b/src/squirrel/squirrel_environment.cpp @@ -128,6 +128,46 @@ SquirrelEnvironment::run_script(const std::string& script, const std::string& so run_script(stream, sourcename); } +void +SquirrelEnvironment::create_reference(const std::string& name, const std::string& ref_name, + const std::string& ref_table, const std::string& ref_sub_table) +{ + const SQInteger old_top = sq_gettop(m_vm.get_vm()); + + sq_pushobject(m_vm.get_vm(), m_table); + sq_pushstring(m_vm.get_vm(), name.c_str(), -1); + if (SQ_FAILED(sq_get(m_vm.get_vm(), -2))) + { + log_warning << "Failed to get entry '" << name << "' from '" << m_name << "'. " + << "Cannot create reference." << std::endl; + } + else + { + const SQInteger entry_top = sq_gettop(m_vm.get_vm()); + + sq_pushobject(m_vm.get_vm(), m_table); + m_vm.get_or_create_table_entry(ref_table); + if (!ref_sub_table.empty()) + m_vm.get_or_create_table_entry(ref_sub_table); + + sq_pushstring(m_vm.get_vm(), ref_name.c_str(), -1); + sq_weakref(m_vm.get_vm(), entry_top); + if (SQ_FAILED(sq_createslot(m_vm.get_vm(), -3))) + log_warning << "Unable to create reference to entry '" << name << "' from '" << m_name << "'." << std::endl; + } + + sq_settop(m_vm.get_vm(), old_top); +} + +void +SquirrelEnvironment::modify_table(const std::function& function) +{ + const SQInteger old_top = sq_gettop(m_vm.get_vm()); + sq_pushobject(m_vm.get_vm(), m_table); + function(m_vm); + sq_settop(m_vm.get_vm(), old_top); +} + void SquirrelEnvironment::garbage_collect() { diff --git a/src/squirrel/squirrel_environment.hpp b/src/squirrel/squirrel_environment.hpp index 29d8e3160d6..05ec5d720a8 100644 --- a/src/squirrel/squirrel_environment.hpp +++ b/src/squirrel/squirrel_environment.hpp @@ -20,6 +20,7 @@ #include #include +#include #include @@ -71,6 +72,13 @@ class SquirrelEnvironment destroyed). */ void run_script(std::istream& in, const std::string& sourcename); + /** Pushes a reference to an entry in the environment table, if it exists, + to a sub-table in the environment table. */ + void create_reference(const std::string& name, const std::string& ref_name, + const std::string& ref_table, const std::string& ref_sub_table); + + void modify_table(const std::function& function); + void update(float dt_sec); void wait_for_seconds(HSQUIRRELVM vm, float seconds); void skippable_wait_for_seconds(HSQUIRRELVM vm, float seconds); diff --git a/src/supertux/sector_base.cpp b/src/supertux/sector_base.cpp index bcecc606ccc..b2a90821c6f 100644 --- a/src/supertux/sector_base.cpp +++ b/src/supertux/sector_base.cpp @@ -16,8 +16,6 @@ #include "supertux/sector_base.hpp" -#include "util/log.hpp" - namespace Base { Sector::Sector(const std::string& type) : @@ -33,6 +31,29 @@ Sector::run_script(const std::string& script, const std::string& sourcename) m_squirrel_environment->run_script(script, sourcename); } +void +Sector::run_script(const std::string& script, const std::string& sourcename, + const GameObject& object, std::map triggers) +{ + if (!object.get_name().empty()) + { + // Delete any existing trigger references + m_squirrel_environment->modify_table([&object](SquirrelVM& vm) { + try + { + vm.get_table_entry("triggers"); + vm.delete_table_entry(object.get_name().c_str()); + } + catch (...) {} + }); + + for (const auto& [name, ref_name] : triggers) + m_squirrel_environment->create_reference(name, ref_name, "triggers", object.get_name()); + } + + m_squirrel_environment->run_script(script, sourcename); +} + bool Sector::before_object_add(GameObject& object) { diff --git a/src/supertux/sector_base.hpp b/src/supertux/sector_base.hpp index 7535118a138..f7306129cef 100644 --- a/src/supertux/sector_base.hpp +++ b/src/supertux/sector_base.hpp @@ -19,6 +19,8 @@ #include "supertux/game_object_manager.hpp" +#include + #include "squirrel/squirrel_environment.hpp" class Level; @@ -47,6 +49,8 @@ class Sector : public GameObjectManager void set_init_script(const std::string& init_script) { m_init_script = init_script; } void run_script(const std::string& script, const std::string& sourcename); + void run_script(const std::string& script, const std::string& sourcename, + const GameObject& object, std::map triggers); protected: virtual bool before_object_add(GameObject& object) override; diff --git a/src/trigger/scripttrigger.cpp b/src/trigger/scripttrigger.cpp index 98683307a4c..141c7d6629b 100644 --- a/src/trigger/scripttrigger.cpp +++ b/src/trigger/scripttrigger.cpp @@ -17,6 +17,7 @@ #include "trigger/scripttrigger.hpp" #include "editor/editor.hpp" +#include "object/player.hpp" #include "supertux/debug.hpp" #include "supertux/sector.hpp" #include "util/log.hpp" @@ -58,12 +59,14 @@ ScriptTrigger::get_settings() } void -ScriptTrigger::event(Player& , EventType type) +ScriptTrigger::event(Player& player, EventType type) { if (type != triggerevent || (oneshot && runcount >= 1)) return; - Sector::get().run_script(script, "ScriptTrigger"); + Sector::get().run_script(script, "ScriptTrigger", *this, { + { player.get_name(), "Tux" } // Create trigger reference to the player + }); runcount++; } diff --git a/src/trigger/switch.cpp b/src/trigger/switch.cpp index 53250d1ee19..d9f12f2713e 100644 --- a/src/trigger/switch.cpp +++ b/src/trigger/switch.cpp @@ -19,6 +19,7 @@ #include #include "audio/sound_manager.hpp" +#include "object/player.hpp" #include "supertux/flip_level_transformer.hpp" #include "supertux/sector.hpp" #include "util/log.hpp" @@ -33,6 +34,7 @@ Switch::Switch(const ReaderMapping& reader) : m_script(), m_off_script(), m_state(OFF), + m_player_name(), m_bistable(), m_dir(Direction::NONE) { @@ -78,9 +80,13 @@ Switch::update(float ) break; case TURN_ON: if (m_sprite->animation_done()) { + assert(!m_player_name.empty()); + std::ostringstream location; location << "switch" << m_col.m_bbox.p1(); - Sector::get().run_script(m_script, location.str()); + Sector::get().run_script(m_script, location.str(), *this, { + { m_player_name, "Tux" } // Create trigger reference to the player + }); set_action("on", m_dir, 1); m_state = ON; @@ -95,9 +101,13 @@ Switch::update(float ) case TURN_OFF: if (m_sprite->animation_done()) { if (m_bistable) { + assert(!m_player_name.empty()); + std::ostringstream location; location << "switch" << m_col.m_bbox.p1(); - Sector::get().run_script(m_off_script, location.str()); + Sector::get().run_script(m_off_script, location.str(), *this, { + { m_player_name, "Tux" } // Create trigger reference to the player + }); } set_action("off", m_dir); @@ -108,7 +118,7 @@ Switch::update(float ) } void -Switch::event(Player& , EventType type) +Switch::event(Player& player, EventType type) { if (type != EVENT_ACTIVATE) return; @@ -117,6 +127,7 @@ Switch::event(Player& , EventType type) set_action("turnon", m_dir, 1); SoundManager::current()->play(SWITCH_SOUND, get_pos()); m_state = TURN_ON; + m_player_name = player.get_name(); break; case TURN_ON: break; @@ -125,6 +136,7 @@ Switch::event(Player& , EventType type) set_action("turnoff", m_dir, 1); SoundManager::current()->play(SWITCH_SOUND, get_pos()); m_state = TURN_OFF; + m_player_name = player.get_name(); } break; case TURN_OFF: diff --git a/src/trigger/switch.hpp b/src/trigger/switch.hpp index 7ad70c5bf1b..eeb2ecec2be 100644 --- a/src/trigger/switch.hpp +++ b/src/trigger/switch.hpp @@ -50,6 +50,7 @@ class Switch final : public SpritedTrigger std::string m_script; std::string m_off_script; SwitchState m_state; + std::string m_player_name; bool m_bistable; Direction m_dir;