Переглянути джерело

Exposed EventListener, sending events to any EventListener (not just entities or components) and subscribing to sender specific events to script.
Fixed and simplified script function to ScriptFile mapping.
Renamed methods to get sub-elements (Console, LineEdit)
TextFinished event also sends the text like TextChanged.

Lasse Öörni 15 роки тому
батько
коміт
553b5055ad

+ 1 - 1
Bin/Data/Scripts/GraphicsTest.as

@@ -306,7 +306,7 @@ void initConsole()
     Console@ console = engine.createConsole();
     Console@ console = engine.createConsole();
     console.setNumRows(16);
     console.setNumRows(16);
     console.setFont(cache.getResource("Font", "cour.ttf"), 12);
     console.setFont(cache.getResource("Font", "cour.ttf"), 12);
-    BorderImage@ cursor = console.getLineEditElement().getCursorElement();
+    BorderImage@ cursor = console.getLineEdit().getCursor();
     cursor.setWidth(4);
     cursor.setWidth(4);
     cursor.setTexture(cache.getResource("Texture2D", "Textures/UI.png"));
     cursor.setTexture(cache.getResource("Texture2D", "Textures/UI.png"));
     cursor.setImageRect(112, 0, 116, 16);
     cursor.setImageRect(112, 0, 116, 16);

+ 1 - 1
Bin/Data/Scripts/NinjaSnowWar.as

@@ -83,7 +83,7 @@ void initConsole()
     Console@ console = engine.createConsole();
     Console@ console = engine.createConsole();
     console.setNumRows(16);
     console.setNumRows(16);
     console.setFont(cache.getResource("Font", "cour.ttf"), 12);
     console.setFont(cache.getResource("Font", "cour.ttf"), 12);
-    BorderImage@ cursor = console.getLineEditElement().getCursorElement();
+    BorderImage@ cursor = console.getLineEdit().getCursor();
     cursor.setWidth(4);
     cursor.setWidth(4);
     cursor.setTexture(cache.getResource("Texture2D", "Textures/UI.png"));
     cursor.setTexture(cache.getResource("Texture2D", "Textures/UI.png"));
     cursor.setImageRect(112, 0, 116, 16);
     cursor.setImageRect(112, 0, 116, 16);

+ 16 - 0
Engine/Common/EventListener.cpp

@@ -36,6 +36,8 @@ static VariantMap noEventData;
 std::map<StringHash, std::vector<EventListener*> > EventListener::sEventListeners;
 std::map<StringHash, std::vector<EventListener*> > EventListener::sEventListeners;
 std::map<std::pair<EventListener*, StringHash>, std::vector<EventListener*> > EventListener::sSpecificEventListeners;
 std::map<std::pair<EventListener*, StringHash>, std::vector<EventListener*> > EventListener::sSpecificEventListeners;
 
 
+EventListener* eventSender = 0;
+
 EventListener::EventListener()
 EventListener::EventListener()
 {
 {
 }
 }
@@ -199,6 +201,7 @@ void EventListener::unsubscribeFromAllEvents()
         removeEventListener(i->first.first, i->first.second);
         removeEventListener(i->first.first, i->first.second);
         delete i->second;
         delete i->second;
     }
     }
+    
     mSpecificEventHandlers.clear();
     mSpecificEventHandlers.clear();
     
     
     for (std::map<StringHash, EventHandlerInvoker*>::iterator i = mEventHandlers.begin(); i != mEventHandlers.end(); ++i)
     for (std::map<StringHash, EventHandlerInvoker*>::iterator i = mEventHandlers.begin(); i != mEventHandlers.end(); ++i)
@@ -206,6 +209,7 @@ void EventListener::unsubscribeFromAllEvents()
         removeEventListener(i->first);
         removeEventListener(i->first);
         delete i->second;
         delete i->second;
     }
     }
+    
     mEventHandlers.clear();
     mEventHandlers.clear();
 }
 }
 
 
@@ -216,6 +220,8 @@ void EventListener::sendEvent(StringHash eventType)
 
 
 void EventListener::sendEvent(StringHash eventType, VariantMap& eventData)
 void EventListener::sendEvent(StringHash eventType, VariantMap& eventData)
 {
 {
+    eventSender = this;
+    
     std::set<EventListener*> processed;
     std::set<EventListener*> processed;
     
     
     // Check first the specific event listeners
     // Check first the specific event listeners
@@ -235,7 +241,10 @@ void EventListener::sendEvent(StringHash eventType, VariantMap& eventData)
     // Then the non-specific listeners
     // Then the non-specific listeners
     std::map<StringHash, std::vector<EventListener*> >::const_iterator j = sEventListeners.find(eventType);
     std::map<StringHash, std::vector<EventListener*> >::const_iterator j = sEventListeners.find(eventType);
     if (j == sEventListeners.end())
     if (j == sEventListeners.end())
+    {
+        eventSender = 0;
         return;
         return;
+    }
     const std::vector<EventListener*>& listeners = j->second;
     const std::vector<EventListener*>& listeners = j->second;
     if (processed.empty())
     if (processed.empty())
     {
     {
@@ -251,6 +260,8 @@ void EventListener::sendEvent(StringHash eventType, VariantMap& eventData)
                 listeners[k]->onEvent(this, eventType, eventData);
                 listeners[k]->onEvent(this, eventType, eventData);
         }
         }
     }
     }
+    
+    eventSender = 0;
 }
 }
 
 
 void EventListener::sendEvent(EventListener* receiver, StringHash eventType)
 void EventListener::sendEvent(EventListener* receiver, StringHash eventType)
@@ -315,3 +326,8 @@ void EventListener::removeEventListener(EventListener* sender, StringHash eventT
         }
         }
     }
     }
 }
 }
+
+EventListener* getEventSender()
+{
+    return eventSender;
+}

+ 6 - 0
Engine/Common/EventListener.h

@@ -141,6 +141,9 @@ protected:
     //! Event handlers for specific senders' events
     //! Event handlers for specific senders' events
     std::map<std::pair<EventListener*, StringHash>, EventHandlerInvoker*> mSpecificEventHandlers;
     std::map<std::pair<EventListener*, StringHash>, EventHandlerInvoker*> mSpecificEventHandlers;
     
     
+    //! Event sender. Only non-null during event handling
+    static EventListener* sSender;
+    
 private:
 private:
     //! Prevent copy construction
     //! Prevent copy construction
     EventListener(const EventListener& rhs);
     EventListener(const EventListener& rhs);
@@ -153,4 +156,7 @@ private:
     static std::map<std::pair<EventListener*, StringHash>, std::vector<EventListener*> > sSpecificEventListeners;
     static std::map<std::pair<EventListener*, StringHash>, std::vector<EventListener*> > sSpecificEventListeners;
 };
 };
 
 
+//! Return event sender. Only non-null during the event handling
+EventListener* getEventSender();
+
 #endif // EVENT_EVENTLISTENER_H
 #endif // EVENT_EVENTLISTENER_H

+ 4 - 4
Engine/Engine/Console.h

@@ -59,10 +59,10 @@ public:
     bool isVisible() const;
     bool isVisible() const;
     //! Return number of rows
     //! Return number of rows
     unsigned getNumRows() const { return mRows.size(); }
     unsigned getNumRows() const { return mRows.size(); }
-    //! Return background element
-    BorderImage* getBackgroundElement() const { return mBackground; }
-    //! Return line edit element
-    LineEdit* getLineEditElement() const { return mLineEdit; }
+    //! Return the background element
+    BorderImage* getBackground() const { return mBackground; }
+    //! Return the line edit element
+    LineEdit* getLineEdit() const { return mLineEdit; }
     
     
 private:
 private:
     //! Update layout
     //! Update layout

+ 40 - 2
Engine/Engine/RegisterCommon.cpp

@@ -643,6 +643,13 @@ static void SendEvent(const std::string& eventType, VariantMap& parameters)
         sender->sendEvent(StringHash(eventType), parameters);
         sender->sendEvent(StringHash(eventType), parameters);
 }
 }
 
 
+static void SendTargetedEvent(EventListener* receiver, const std::string& eventType, VariantMap& parameters)
+{
+    ScriptEventListener* sender = getScriptContextEventListener();
+    if (sender)
+        sender->sendEvent(receiver, StringHash(eventType), parameters);
+}
+
 static void SubscribeToEvent(const std::string& eventType, const std::string& handlerName)
 static void SubscribeToEvent(const std::string& eventType, const std::string& handlerName)
 {
 {
     ScriptEventListener* listener = getScriptContextEventListener();
     ScriptEventListener* listener = getScriptContextEventListener();
@@ -650,6 +657,13 @@ static void SubscribeToEvent(const std::string& eventType, const std::string& ha
         listener->addEventHandler(StringHash(eventType), handlerName);
         listener->addEventHandler(StringHash(eventType), handlerName);
 }
 }
 
 
+static void SubscribeToSenderEvent(EventListener* sender, const std::string& eventType, const std::string& handlerName)
+{
+    ScriptEventListener* listener = getScriptContextEventListener();
+    if (listener)
+        listener->addEventHandler(sender, StringHash(eventType), handlerName);
+}
+
 static void UnsubscribeFromEvent(const std::string& eventType)
 static void UnsubscribeFromEvent(const std::string& eventType)
 {
 {
     ScriptEventListener* listener = getScriptContextEventListener();
     ScriptEventListener* listener = getScriptContextEventListener();
@@ -657,6 +671,20 @@ static void UnsubscribeFromEvent(const std::string& eventType)
         listener->removeEventHandler(StringHash(eventType));
         listener->removeEventHandler(StringHash(eventType));
 }
 }
 
 
+static void UnsubscribeFromSenderEvent(EventListener* sender, const std::string& eventType)
+{
+    ScriptEventListener* listener = getScriptContextEventListener();
+    if (listener)
+        listener->removeEventHandler(sender, StringHash(eventType));
+}
+
+static void UnsubscribeFromSenderEvents(EventListener* sender)
+{
+    ScriptEventListener* listener = getScriptContextEventListener();
+    if (listener)
+        listener->removeEventHandlers(sender);
+}
+
 static void UnsubscribeFromAllEvents()
 static void UnsubscribeFromAllEvents()
 {
 {
     ScriptEventListener* listener = getScriptContextEventListener();
     ScriptEventListener* listener = getScriptContextEventListener();
@@ -664,12 +692,22 @@ static void UnsubscribeFromAllEvents()
         listener->removeAllEventHandlers();
         listener->removeAllEventHandlers();
 }
 }
 
 
-void registerEvents(asIScriptEngine* engine)
+void registerEventListener(asIScriptEngine* engine)
 {
 {
+    engine->RegisterObjectType("EventListener", 0, asOBJ_REF);
+    engine->RegisterObjectBehaviour("EventListener", asBEHAVE_ADDREF, "void f()", asFUNCTION(optionalAddRef<EventListener>), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectBehaviour("EventListener", asBEHAVE_RELEASE, "void f()", asFUNCTION(optionalReleaseRef<EventListener>), asCALL_CDECL_OBJLAST);
+    
     engine->RegisterGlobalFunction("void sendEvent(const string& in, VariantMap&)", asFUNCTION(SendEvent), asCALL_CDECL);
     engine->RegisterGlobalFunction("void sendEvent(const string& in, VariantMap&)", asFUNCTION(SendEvent), asCALL_CDECL);
+    engine->RegisterGlobalFunction("void sendEvent(EventListener@+, const string& in, VariantMap&)", asFUNCTION(SendTargetedEvent), asCALL_CDECL);
     engine->RegisterGlobalFunction("void subscribeToEvent(const string& in, const string& in)", asFUNCTION(SubscribeToEvent), asCALL_CDECL);
     engine->RegisterGlobalFunction("void subscribeToEvent(const string& in, const string& in)", asFUNCTION(SubscribeToEvent), asCALL_CDECL);
+    engine->RegisterGlobalFunction("void subscribeToEvent(EventListener@+, const string& in, const string& in)", asFUNCTION(SubscribeToSenderEvent), asCALL_CDECL);
     engine->RegisterGlobalFunction("void unsubscribeFromEvent(const string& in)", asFUNCTION(UnsubscribeFromEvent), asCALL_CDECL);
     engine->RegisterGlobalFunction("void unsubscribeFromEvent(const string& in)", asFUNCTION(UnsubscribeFromEvent), asCALL_CDECL);
+    engine->RegisterGlobalFunction("void unsubscribeFromEvent(EventListener@+, const string& in)", asFUNCTION(UnsubscribeFromSenderEvent), asCALL_CDECL);
+    engine->RegisterGlobalFunction("void unsubscribeFromEvents(EventListener@+)", asFUNCTION(UnsubscribeFromSenderEvents), asCALL_CDECL);
     engine->RegisterGlobalFunction("void unsubscribeFromAllEvents()", asFUNCTION(UnsubscribeFromAllEvents), asCALL_CDECL);
     engine->RegisterGlobalFunction("void unsubscribeFromAllEvents()", asFUNCTION(UnsubscribeFromAllEvents), asCALL_CDECL);
+    
+    engine->RegisterGlobalFunction("EventListener@+ getEventSender()", asFUNCTION(getEventSender), asCALL_CDECL);
 }
 }
 
 
 void registerCommonLibrary(asIScriptEngine* engine)
 void registerCommonLibrary(asIScriptEngine* engine)
@@ -683,5 +721,5 @@ void registerCommonLibrary(asIScriptEngine* engine)
     registerPackageFile(engine);
     registerPackageFile(engine);
     registerTimer(engine);
     registerTimer(engine);
     registerProcessUtils(engine);
     registerProcessUtils(engine);
-    registerEvents(engine);
+    registerEventListener(engine);
 }
 }

+ 6 - 2
Engine/Engine/RegisterEngine.cpp

@@ -115,6 +115,7 @@ static void registerConnection(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Connection", "float getRoundTripTime() const", asMETHOD(Connection, getRoundTripTime), asCALL_THISCALL);
     engine->RegisterObjectMethod("Connection", "float getRoundTripTime() const", asMETHOD(Connection, getRoundTripTime), asCALL_THISCALL);
     engine->RegisterObjectMethod("Connection", "const Controls& getControls() const", asMETHOD(Connection, getControls), asCALL_THISCALL);
     engine->RegisterObjectMethod("Connection", "const Controls& getControls() const", asMETHOD(Connection, getControls), asCALL_THISCALL);
     engine->RegisterObjectMethod("Connection", "const Vector3& getPosition() const", asMETHOD(Connection, getPosition), asCALL_THISCALL);
     engine->RegisterObjectMethod("Connection", "const Vector3& getPosition() const", asMETHOD(Connection, getPosition), asCALL_THISCALL);
+    registerRefCasts<EventListener, Connection>(engine, "EventListener", "Connection");
     
     
     // Register Variant getPtr() for Connection
     // Register Variant getPtr() for Connection
     engine->RegisterObjectMethod("Variant", "Connection@+ getConnection() const", asFUNCTION(getVariantPtr<Connection>), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Variant", "Connection@+ getConnection() const", asFUNCTION(getVariantPtr<Connection>), asCALL_CDECL_OBJLAST);
@@ -236,6 +237,7 @@ static void registerClient(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Client", "const string& getDownloadDirectory() const", asMETHOD(Client, getDownloadDirectory), asCALL_THISCALL);
     engine->RegisterObjectMethod("Client", "const string& getDownloadDirectory() const", asMETHOD(Client, getDownloadDirectory), asCALL_THISCALL);
     engine->RegisterObjectMethod("Client", "string getFileTransferStatus() const", asMETHOD(Client, getFileTransferStatus), asCALL_THISCALL);
     engine->RegisterObjectMethod("Client", "string getFileTransferStatus() const", asMETHOD(Client, getFileTransferStatus), asCALL_THISCALL);
     engine->RegisterObjectMethod("Client", "const SceneInfo& getSceneInfo() const", asMETHOD(Client, getSceneInfo), asCALL_THISCALL);
     engine->RegisterObjectMethod("Client", "const SceneInfo& getSceneInfo() const", asMETHOD(Client, getSceneInfo), asCALL_THISCALL);
+    registerRefCasts<EventListener, Client>(engine, "EventListener", "Client");
     
     
     engine->RegisterGlobalFunction("Client@+ getClient()", asFUNCTION(GetClient), asCALL_CDECL);
     engine->RegisterGlobalFunction("Client@+ getClient()", asFUNCTION(GetClient), asCALL_CDECL);
     engine->RegisterGlobalFunction("Client@+ get_client()", asFUNCTION(GetClient), asCALL_CDECL);
     engine->RegisterGlobalFunction("Client@+ get_client()", asFUNCTION(GetClient), asCALL_CDECL);
@@ -285,6 +287,7 @@ static void registerServer(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Server", "array<Scene@>@ getScenes() const", asFUNCTION(ServerGetScenes), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Server", "array<Scene@>@ getScenes() const", asFUNCTION(ServerGetScenes), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Server", "array<Connection@>@ getConnections() const", asFUNCTION(ServerGetConnections), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Server", "array<Connection@>@ getConnections() const", asFUNCTION(ServerGetConnections), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Server", "uint getNumUsersInScene(Scene@+) const", asMETHOD(Server, getNumUsersInScene), asCALL_THISCALL);
     engine->RegisterObjectMethod("Server", "uint getNumUsersInScene(Scene@+) const", asMETHOD(Server, getNumUsersInScene), asCALL_THISCALL);
+    registerRefCasts<EventListener, Server>(engine, "EventListener", "Server");
     
     
     engine->RegisterGlobalFunction("Server@+ getServer()", asFUNCTION(GetServer), asCALL_CDECL);
     engine->RegisterGlobalFunction("Server@+ getServer()", asFUNCTION(GetServer), asCALL_CDECL);
     engine->RegisterGlobalFunction("Server@+ get_server()", asFUNCTION(GetServer), asCALL_CDECL);
     engine->RegisterGlobalFunction("Server@+ get_server()", asFUNCTION(GetServer), asCALL_CDECL);
@@ -377,8 +380,8 @@ static void registerConsole(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Console", "void setFont(Font@+, int)", asMETHOD(Console, setFont), asCALL_THISCALL);
     engine->RegisterObjectMethod("Console", "void setFont(Font@+, int)", asMETHOD(Console, setFont), asCALL_THISCALL);
     engine->RegisterObjectMethod("Console", "bool isVisible() const", asMETHOD(Console, isVisible), asCALL_THISCALL);
     engine->RegisterObjectMethod("Console", "bool isVisible() const", asMETHOD(Console, isVisible), asCALL_THISCALL);
     engine->RegisterObjectMethod("Console", "uint getNumRows() const", asMETHOD(Console, getNumRows), asCALL_THISCALL);
     engine->RegisterObjectMethod("Console", "uint getNumRows() const", asMETHOD(Console, getNumRows), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Console", "BorderImage@+ getBackgroundElement() const", asMETHOD(Console, getBackgroundElement), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Console", "LineEdit@+ getLineEditElement() const", asMETHOD(Console, getLineEditElement), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Console", "BorderImage@+ getBackground() const", asMETHOD(Console, getBackground), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Console", "LineEdit@+ getLineEdit() const", asMETHOD(Console, getLineEdit), asCALL_THISCALL);
     
     
     engine->RegisterGlobalFunction("Console@+ getConsole()", asFUNCTION(GetConsole), asCALL_CDECL);
     engine->RegisterGlobalFunction("Console@+ getConsole()", asFUNCTION(GetConsole), asCALL_CDECL);
     engine->RegisterGlobalFunction("Console@+ get_console()", asFUNCTION(GetConsole), asCALL_CDECL);
     engine->RegisterGlobalFunction("Console@+ get_console()", asFUNCTION(GetConsole), asCALL_CDECL);
@@ -460,6 +463,7 @@ static void registerEngine(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Engine", "void render()", asMETHOD(Engine, render), asCALL_THISCALL);
     engine->RegisterObjectMethod("Engine", "void render()", asMETHOD(Engine, render), asCALL_THISCALL);
     engine->RegisterGlobalFunction("Engine@+ getEngine()", asFUNCTION(getEngine), asCALL_CDECL);
     engine->RegisterGlobalFunction("Engine@+ getEngine()", asFUNCTION(getEngine), asCALL_CDECL);
     engine->RegisterGlobalFunction("Engine@+ get_engine()", asFUNCTION(getEngine), asCALL_CDECL);
     engine->RegisterGlobalFunction("Engine@+ get_engine()", asFUNCTION(getEngine), asCALL_CDECL);
+    registerRefCasts<EventListener, Engine>(engine, "EventListener", "Engine");
 }
 }
 
 
 void registerEngineLibrary(asIScriptEngine* engine)
 void registerEngineLibrary(asIScriptEngine* engine)

+ 2 - 0
Engine/Engine/RegisterInput.cpp

@@ -25,6 +25,7 @@
 #include "Controls.h"
 #include "Controls.h"
 #include "Engine.h"
 #include "Engine.h"
 #include "Input.h"
 #include "Input.h"
+#include "RegisterTemplates.h"
 
 
 #include <angelscript.h>
 #include <angelscript.h>
 
 
@@ -210,6 +211,7 @@ static void registerInput(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Input", "bool getToggleFullscreen() const", asMETHOD(Input, getToggleFullscreen), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool getToggleFullscreen() const", asMETHOD(Input, getToggleFullscreen), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool isActive() const", asMETHOD(Input, isActive), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool isActive() const", asMETHOD(Input, isActive), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool isMinimized() const", asMETHOD(Input, isMinimized), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool isMinimized() const", asMETHOD(Input, isMinimized), asCALL_THISCALL);
+    registerRefCasts<EventListener, Input>(engine, "EventListener", "Input");
     
     
     engine->RegisterGlobalFunction("Input@+ getInput()", asFUNCTION(GetInput), asCALL_CDECL);
     engine->RegisterGlobalFunction("Input@+ getInput()", asFUNCTION(GetInput), asCALL_CDECL);
     engine->RegisterGlobalFunction("Input@+ get_input()", asFUNCTION(GetInput), asCALL_CDECL);
     engine->RegisterGlobalFunction("Input@+ get_input()", asFUNCTION(GetInput), asCALL_CDECL);

+ 1 - 0
Engine/Engine/RegisterPhysics.cpp

@@ -102,6 +102,7 @@ static void registerPhysicsWorld(asIScriptEngine* engine)
     engine->RegisterObjectMethod("PhysicsWorld", "float getCFM() const", asMETHOD(PhysicsWorld, getCFM), asCALL_THISCALL);
     engine->RegisterObjectMethod("PhysicsWorld", "float getCFM() const", asMETHOD(PhysicsWorld, getCFM), asCALL_THISCALL);
     engine->RegisterObjectMethod("PhysicsWorld", "float getContactSurfaceLayer() const", asMETHOD(PhysicsWorld, getContactSurfaceLayer), asCALL_THISCALL);
     engine->RegisterObjectMethod("PhysicsWorld", "float getContactSurfaceLayer() const", asMETHOD(PhysicsWorld, getContactSurfaceLayer), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "PhysicsWorld@+ getPhysicsWorld() const", asFUNCTION(SceneGetPhysicsWorld), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Scene", "PhysicsWorld@+ getPhysicsWorld() const", asFUNCTION(SceneGetPhysicsWorld), asCALL_CDECL_OBJLAST);
+    registerRefCasts<EventListener, PhysicsWorld>(engine, "EventListener", "PhysicsWorld");
     
     
     engine->RegisterGlobalFunction("PhysicsWorld@+ getPhysicsWorld()", asFUNCTION(GetPhysicsWorld), asCALL_CDECL);
     engine->RegisterGlobalFunction("PhysicsWorld@+ getPhysicsWorld()", asFUNCTION(GetPhysicsWorld), asCALL_CDECL);
     engine->RegisterGlobalFunction("PhysicsWorld@+ get_physicsWorld()", asFUNCTION(GetPhysicsWorld), asCALL_CDECL);
     engine->RegisterGlobalFunction("PhysicsWorld@+ get_physicsWorld()", asFUNCTION(GetPhysicsWorld), asCALL_CDECL);

+ 2 - 29
Engine/Engine/RegisterScene.cpp

@@ -47,27 +47,6 @@ static void DestructComponentRef(ComponentRef* ptr)
     ptr->~ComponentRef();
     ptr->~ComponentRef();
 }
 }
 
 
-static void SendTargetedEvent(EventListener* listener, const std::string& eventType, VariantMap& parameters)
-{
-    // Try to send through the instance first, then through the script file event if not found
-    ScriptInstance* instance = getScriptContextInstance();
-    if (instance)
-        instance->sendEvent(listener, StringHash(eventType), parameters);
-    else
-    {
-        ScriptFile* file = getScriptContextFile();
-        if (file)
-            file->sendEvent(listener, StringHash(eventType), parameters);
-    }
-}
-
-static void SendComponentEvent(Component* component, const std::string& eventType, VariantMap& eventData)
-{
-    EventListener* listener = dynamic_cast<EventListener*>(component);
-    if (listener)
-        SendTargetedEvent(listener, eventType, eventData);
-}
-
 static void registerComponent(asIScriptEngine* engine)
 static void registerComponent(asIScriptEngine* engine)
 {
 {
     engine->RegisterGlobalProperty("const uint8 NET_NONE", (void*)&NET_NONE);
     engine->RegisterGlobalProperty("const uint8 NET_NONE", (void*)&NET_NONE);
@@ -85,8 +64,6 @@ static void registerComponent(asIScriptEngine* engine)
     
     
     engine->RegisterObjectType("Entity", 0, asOBJ_REF);
     engine->RegisterObjectType("Entity", 0, asOBJ_REF);
     registerComponent<Component>(engine, "Component");
     registerComponent<Component>(engine, "Component");
-    
-    engine->RegisterGlobalFunction("void sendEvent(Component@+, const string& in, VariantMap&)", asFUNCTION(SendComponentEvent), asCALL_CDECL);
 }
 }
 
 
 static void registerComponentRef(asIScriptEngine* engine)
 static void registerComponentRef(asIScriptEngine* engine)
@@ -268,11 +245,6 @@ static Entity* GetEntity()
     return getScriptContextEntity();
     return getScriptContextEntity();
 }
 }
 
 
-static void SendEntityEvent(Entity* entity, const std::string& eventType, VariantMap& eventData)
-{
-    SendTargetedEvent(entity, eventType, eventData);
-}
-
 static void registerEntity(asIScriptEngine* engine)
 static void registerEntity(asIScriptEngine* engine)
 {
 {
     engine->RegisterInterface("ScriptObject");
     engine->RegisterInterface("ScriptObject");
@@ -320,10 +292,10 @@ static void registerEntity(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Entity", "bool isOwnerPredicted() const", asMETHOD(Entity, isOwnerPredicted), asCALL_THISCALL);
     engine->RegisterObjectMethod("Entity", "bool isOwnerPredicted() const", asMETHOD(Entity, isOwnerPredicted), asCALL_THISCALL);
     engine->RegisterObjectMethod("Entity", "bool isTransientPredicted() const", asMETHOD(Entity, isTransientPredicted), asCALL_THISCALL);
     engine->RegisterObjectMethod("Entity", "bool isTransientPredicted() const", asMETHOD(Entity, isTransientPredicted), asCALL_THISCALL);
     engine->RegisterObjectMethod("Entity", "bool isPlayback() const", asMETHOD(Entity, isPlayback), asCALL_THISCALL);
     engine->RegisterObjectMethod("Entity", "bool isPlayback() const", asMETHOD(Entity, isPlayback), asCALL_THISCALL);
+    registerRefCasts<EventListener, Entity>(engine, "EventListener", "Entity");
     
     
     engine->RegisterGlobalFunction("Entity@+ getEntity()", asFUNCTION(GetEntity), asCALL_CDECL);
     engine->RegisterGlobalFunction("Entity@+ getEntity()", asFUNCTION(GetEntity), asCALL_CDECL);
     engine->RegisterGlobalFunction("Entity@+ get_entity()", asFUNCTION(GetEntity), asCALL_CDECL);
     engine->RegisterGlobalFunction("Entity@+ get_entity()", asFUNCTION(GetEntity), asCALL_CDECL);
-    engine->RegisterGlobalFunction("void sendEvent(Entity@+, const string& in, VariantMap&)", asFUNCTION(SendEntityEvent), asCALL_CDECL);
     
     
     // Register Variant getPtr() for Entity
     // Register Variant getPtr() for Entity
     engine->RegisterObjectMethod("Variant", "Entity@+ getEntity() const", asFUNCTION(getVariantPtr<Entity>), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Variant", "Entity@+ getEntity() const", asFUNCTION(getVariantPtr<Entity>), asCALL_CDECL_OBJLAST);
@@ -542,6 +514,7 @@ static void registerScene(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Scene", "bool isProxy() const", asMETHOD(Scene, isProxy), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "bool isProxy() const", asMETHOD(Scene, isProxy), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "bool isPlayback() const", asMETHOD(Scene, isPlayback), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "bool isPlayback() const", asMETHOD(Scene, isPlayback), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "bool isAsyncLoading() const", asMETHOD(Scene, isAsyncLoading), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "bool isAsyncLoading() const", asMETHOD(Scene, isAsyncLoading), asCALL_THISCALL);
+    registerRefCasts<EventListener, Scene>(engine, "EventListener", "Scene");
     
     
     engine->RegisterGlobalFunction("Scene@+ getScene()", asFUNCTION(GetScene), asCALL_CDECL);
     engine->RegisterGlobalFunction("Scene@+ getScene()", asFUNCTION(GetScene), asCALL_CDECL);
     engine->RegisterGlobalFunction("Scene@+ get_scene()", asFUNCTION(GetScene), asCALL_CDECL);
     engine->RegisterGlobalFunction("Scene@+ get_scene()", asFUNCTION(GetScene), asCALL_CDECL);

+ 17 - 0
Engine/Engine/RegisterTemplates.h

@@ -37,6 +37,22 @@
 
 
 #include <angelscript.h>
 #include <angelscript.h>
 
 
+//! Template function for optional addRef, when a derived class might derive from RefCounted
+template <class T> void optionalAddRef(T* t)
+{
+    RefCounted* ptr = dynamic_cast<RefCounted*>(t);
+    if (ptr)
+        ptr->addRef();
+}
+
+//! Template function for optional releaseRef, when a derived class might derive from RefCounted
+template <class T> void optionalReleaseRef(T* t)
+{
+    RefCounted* ptr = dynamic_cast<RefCounted*>(t);
+    if (ptr)
+        ptr->releaseRef();
+}
+
 //! Template function for dynamic cast between two script classes
 //! Template function for dynamic cast between two script classes
 template <class T, class U> U* refCast(T* t)
 template <class T, class U> U* refCast(T* t)
 {
 {
@@ -460,6 +476,7 @@ template <class T> void registerUIElement(asIScriptEngine* engine, const char* c
     engine->RegisterObjectMethod(className, "bool isInside(IntVector2, bool)", asMETHOD(T, isInside), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool isInside(IntVector2, bool)", asMETHOD(T, isInside), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool isInsideCombined(IntVector2, bool)", asMETHOD(T, isInsideCombined), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool isInsideCombined(IntVector2, bool)", asMETHOD(T, isInsideCombined), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "IntRect getCombinedScreenRect()", asMETHOD(T, getCombinedScreenRect), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "IntRect getCombinedScreenRect()", asMETHOD(T, getCombinedScreenRect), asCALL_THISCALL);
+    registerRefCasts<EventListener, T>(engine, "EventListener", className);
 }
 }
 
 
 //! Template function for registering a class derived from BorderImage
 //! Template function for registering a class derived from BorderImage

+ 2 - 1
Engine/Engine/RegisterUI.cpp

@@ -188,7 +188,7 @@ static void registerLineEdit(asIScriptEngine* engine)
     engine->RegisterObjectMethod("LineEdit", "bool isTextSelectable() const", asMETHOD(LineEdit, isTextSelectable), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "bool isTextSelectable() const", asMETHOD(LineEdit, isTextSelectable), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "bool isTextCopyable() const", asMETHOD(LineEdit, isTextCopyable), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "bool isTextCopyable() const", asMETHOD(LineEdit, isTextCopyable), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "Text@+ getTextElement() const", asMETHOD(LineEdit, getTextElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "Text@+ getTextElement() const", asMETHOD(LineEdit, getTextElement), asCALL_THISCALL);
-    engine->RegisterObjectMethod("LineEdit", "BorderImage@+ getCursorElement() const", asMETHOD(LineEdit, getCursorElement), asCALL_THISCALL);
+    engine->RegisterObjectMethod("LineEdit", "BorderImage@+ getCursor() const", asMETHOD(LineEdit, getCursor), asCALL_THISCALL);
     registerRefCasts<UIElement, LineEdit>(engine, "UIElement", "LineEdit");
     registerRefCasts<UIElement, LineEdit>(engine, "UIElement", "LineEdit");
 }
 }
 
 
@@ -303,6 +303,7 @@ static void registerUI(asIScriptEngine* engine)
     engine->RegisterObjectMethod("UI", "UIElement@+ getElementAt(int, int, bool)", asMETHODPR(UI, getElementAt, (int, int, bool), UIElement*), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ getElementAt(int, int, bool)", asMETHODPR(UI, getElementAt, (int, int, bool), UIElement*), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ getFocusElement()", asMETHOD(UI, getFocusElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ getFocusElement()", asMETHOD(UI, getFocusElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "IntVector2 getCursorPosition()", asMETHOD(UI, getCursorPosition), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "IntVector2 getCursorPosition()", asMETHOD(UI, getCursorPosition), asCALL_THISCALL);
+    registerRefCasts<EventListener, UI>(engine, "EventListener", "UI");
     
     
     engine->RegisterGlobalFunction("UI@+ getUI()", asFUNCTION(GetUI), asCALL_CDECL);
     engine->RegisterGlobalFunction("UI@+ getUI()", asFUNCTION(GetUI), asCALL_CDECL);
     engine->RegisterGlobalFunction("UI@+ get_ui()", asFUNCTION(GetUI), asCALL_CDECL);
     engine->RegisterGlobalFunction("UI@+ get_ui()", asFUNCTION(GetUI), asCALL_CDECL);

+ 45 - 2
Engine/Script/ScriptEventListener.cpp

@@ -26,17 +26,60 @@
 
 
 void ScriptEventListener::removeEventHandler(StringHash eventType)
 void ScriptEventListener::removeEventHandler(StringHash eventType)
 {
 {
+    for (std::map<std::pair<EventListener*, StringHash>, asIScriptFunction*>::iterator i = mSpecificEventHandlers.begin(); i !=
+        mSpecificEventHandlers.end();)
+    {
+        std::map<std::pair<EventListener*, StringHash>, asIScriptFunction*>::iterator current = i;
+        ++i;
+        if (current->first.second == eventType)
+            mSpecificEventHandlers.erase(current);
+    }
+    
     std::map<StringHash, asIScriptFunction*>::iterator i = mEventHandlers.find(eventType);
     std::map<StringHash, asIScriptFunction*>::iterator i = mEventHandlers.find(eventType);
     if (i != mEventHandlers.end())
     if (i != mEventHandlers.end())
-    {
-        unsubscribeFromEvent(eventType);
         mEventHandlers.erase(i);
         mEventHandlers.erase(i);
+    
+    unsubscribeFromEvent(eventType);
+}
+
+void ScriptEventListener::removeEventHandler(EventListener* sender, StringHash eventType)
+{
+    std::pair<EventListener*, StringHash> combination = std::make_pair(sender, eventType);
+    
+    std::map<std::pair<EventListener*, StringHash>, asIScriptFunction*>::iterator i = mSpecificEventHandlers.find(combination);
+    if (i != mSpecificEventHandlers.end())
+    {
+        mSpecificEventHandlers.erase(i);
+        unsubscribeFromEvent(sender, eventType);
+    }
+}
+
+void ScriptEventListener::removeEventHandlers(EventListener* sender)
+{
+    for (std::map<std::pair<EventListener*, StringHash>, asIScriptFunction*>::iterator i = mSpecificEventHandlers.begin(); i !=
+        mSpecificEventHandlers.end();)
+    {
+        std::map<std::pair<EventListener*, StringHash>, asIScriptFunction*>::iterator current = i;
+        ++i;
+        if (current->first.first == sender)
+            mSpecificEventHandlers.erase(current);
     }
     }
+    
+    unsubscribeFromEvents(sender);
 }
 }
 
 
 void ScriptEventListener::removeAllEventHandlers()
 void ScriptEventListener::removeAllEventHandlers()
 {
 {
+    // Note: we can not simply call unsubscribeFromAllEvents(), as for example ScriptInstance has its own internal
+    // scene update event handlers, which must not be unsubscribed
+    for (std::map<std::pair<EventListener*, StringHash>, asIScriptFunction*>::iterator i = mSpecificEventHandlers.begin(); i !=
+        mSpecificEventHandlers.end(); ++i)
+        unsubscribeFromEvent(i->first.first, i->first.second);
+    
+    mSpecificEventHandlers.clear();
+    
     for (std::map<StringHash, asIScriptFunction*>::iterator i = mEventHandlers.begin(); i != mEventHandlers.end(); ++i)
     for (std::map<StringHash, asIScriptFunction*>::iterator i = mEventHandlers.begin(); i != mEventHandlers.end(); ++i)
         unsubscribeFromEvent(i->first);
         unsubscribeFromEvent(i->first);
+    
     mEventHandlers.clear();
     mEventHandlers.clear();
 }
 }

+ 9 - 1
Engine/Script/ScriptEventListener.h

@@ -33,14 +33,22 @@ class ScriptEventListener : public EventListener
 public:
 public:
     //! Add an event handler. Called by script exposed version of subscribeToEvent()
     //! Add an event handler. Called by script exposed version of subscribeToEvent()
     virtual void addEventHandler(StringHash eventType, const std::string& handlerName) = 0;
     virtual void addEventHandler(StringHash eventType, const std::string& handlerName) = 0;
+    //! Add an event handler for a specific sender. Called by script exposed version of subscribeToEvent()
+    virtual void addEventHandler(EventListener* sender, StringHash eventType, const std::string& handlerName) = 0;
     //! Remove an event handler. Called by script exposed version of unsubcribeFromEvent()
     //! Remove an event handler. Called by script exposed version of unsubcribeFromEvent()
     void removeEventHandler(StringHash eventType);
     void removeEventHandler(StringHash eventType);
+    //! Remove an event handler for a specific sender. Called by script exposed version of unsubscribeFromEvent();
+    void removeEventHandler(EventListener* sender, StringHash eventType);
+    //! Remove all event handlers for a specific sender. Called by script exposed version of unsubscribeFromEvents();
+    void removeEventHandlers(EventListener* sender);
     //! Remove all event handlers. Called by script exposed version of unsubscribeFromAllEvents()
     //! Remove all event handlers. Called by script exposed version of unsubscribeFromAllEvents()
     void removeAllEventHandlers();
     void removeAllEventHandlers();
     
     
 protected:
 protected:
-    //! Pointers to event handler functions or methods
+    //! Pointers to event handler functions
     std::map<StringHash, asIScriptFunction*> mEventHandlers;
     std::map<StringHash, asIScriptFunction*> mEventHandlers;
+    //! Pointers to event handler functions for specific event senders
+    std::map<std::pair<EventListener*, StringHash>, asIScriptFunction*> mSpecificEventHandlers;
 };
 };
 
 
 #endif // SCRIPT_SCRIPTEVENTLISTENER_H
 #endif // SCRIPT_SCRIPTEVENTLISTENER_H

+ 38 - 16
Engine/Script/ScriptFile.cpp

@@ -41,7 +41,7 @@ static unsigned scriptNestingLevel = 0;
 static unsigned highestScriptNestingLevel = 0;
 static unsigned highestScriptNestingLevel = 0;
 ScriptFile* lastScriptFile = 0;
 ScriptFile* lastScriptFile = 0;
 
 
-std::map<asIScriptFunction*, ScriptFile*> functionToFile;
+std::map<asIScriptModule*, ScriptFile*> moduleToFile;
 
 
 ScriptFile::ScriptFile(ScriptEngine* scriptEngine, const std::string& name) :
 ScriptFile::ScriptFile(ScriptEngine* scriptEngine, const std::string& name) :
     Resource(name),
     Resource(name),
@@ -65,9 +65,8 @@ ScriptFile::~ScriptFile()
         // Perform a full garbage collection cycle, also clean up contexts which might still refer to the module's functions
         // Perform a full garbage collection cycle, also clean up contexts which might still refer to the module's functions
         mScriptEngine->garbageCollect(true);
         mScriptEngine->garbageCollect(true);
         
         
-        clearFunctionToFileMappings();
-        
         // Remove the module
         // Remove the module
+        moduleToFile.erase(mScriptModule);
         asIScriptEngine* engine = mScriptEngine->getAngelScriptEngine();
         asIScriptEngine* engine = mScriptEngine->getAngelScriptEngine();
         engine->DiscardModule(getName().c_str());
         engine->DiscardModule(getName().c_str());
         mScriptModule = 0;
         mScriptModule = 0;
@@ -94,9 +93,10 @@ void ScriptFile::load(Deserializer& source, ResourceCache* cache)
     mMethods.clear();
     mMethods.clear();
     setMemoryUse(0);
     setMemoryUse(0);
     removeAllEventHandlers();
     removeAllEventHandlers();
-    clearFunctionToFileMappings();
     
     
     // Create the module. Discard previous module if there was one
     // Create the module. Discard previous module if there was one
+    if (mScriptModule)
+        moduleToFile.erase(mScriptModule);
     asIScriptEngine* engine = mScriptEngine->getAngelScriptEngine();
     asIScriptEngine* engine = mScriptEngine->getAngelScriptEngine();
     mScriptModule = engine->GetModule(getName().c_str(), asGM_ALWAYS_CREATE);
     mScriptModule = engine->GetModule(getName().c_str(), asGM_ALWAYS_CREATE);
     if (!mScriptModule)
     if (!mScriptModule)
@@ -118,6 +118,7 @@ void ScriptFile::load(Deserializer& source, ResourceCache* cache)
     
     
     LOGINFO("Compiled script module " + getName());
     LOGINFO("Compiled script module " + getName());
     mCompiled = true;
     mCompiled = true;
+    moduleToFile[mScriptModule] = this;
     
     
     // Now let the script instances recreate their objects
     // Now let the script instances recreate their objects
     for (std::vector<ScriptInstance*>::iterator i = instances.begin(); i != instances.end(); ++i)
     for (std::vector<ScriptInstance*>::iterator i = instances.begin(); i != instances.end(); ++i)
@@ -141,6 +142,23 @@ void ScriptFile::addEventHandler(StringHash eventType, const std::string& handle
     mEventHandlers[eventType] = function;
     mEventHandlers[eventType] = function;
 }
 }
 
 
+void ScriptFile::addEventHandler(EventListener* sender, StringHash eventType, const std::string& handlerName)
+{
+    if ((!mCompiled) || (!sender))
+        return;
+    
+    std::string declaration = "void " + handlerName + "(StringHash, VariantMap&)";
+    asIScriptFunction* function = getFunction(declaration);
+    if (!function)
+    {
+        LOGERROR("Event handler function " + declaration + " not found in " + getName());
+        return;
+    }
+    
+    subscribeToEvent(sender, eventType, EVENT_HANDLER(ScriptFile, handleSpecificScriptEvent));
+    mSpecificEventHandlers[std::make_pair(sender, eventType)] = function;
+}
+
 bool ScriptFile::execute(const std::string& declaration, const std::vector<Variant>& parameters)
 bool ScriptFile::execute(const std::string& declaration, const std::vector<Variant>& parameters)
 {
 {
     asIScriptFunction* function = getFunction(declaration);
     asIScriptFunction* function = getFunction(declaration);
@@ -301,7 +319,6 @@ asIScriptFunction* ScriptFile::getFunction(const std::string& declaration)
     
     
     int id = mScriptModule->GetFunctionIdByDecl(declaration.c_str());
     int id = mScriptModule->GetFunctionIdByDecl(declaration.c_str());
     asIScriptFunction* function = mScriptModule->GetFunctionDescriptorById(id);
     asIScriptFunction* function = mScriptModule->GetFunctionDescriptorById(id);
-    functionToFile[function] = this;
     mFunctions[declaration] = function;
     mFunctions[declaration] = function;
     return function;
     return function;
 }
 }
@@ -324,7 +341,6 @@ asIScriptFunction* ScriptFile::getMethod(asIScriptObject* object, const std::str
     
     
     int id = type->GetMethodIdByDecl(declaration.c_str());
     int id = type->GetMethodIdByDecl(declaration.c_str());
     asIScriptFunction* function = mScriptModule->GetFunctionDescriptorById(id);
     asIScriptFunction* function = mScriptModule->GetFunctionDescriptorById(id);
-    functionToFile[function] = this;
     mMethods[type][declaration] = function;
     mMethods[type][declaration] = function;
     return function;
     return function;
 }
 }
@@ -533,22 +549,28 @@ void ScriptFile::handleScriptEvent(StringHash eventType, VariantMap& eventData)
     execute(i->second, parameters);
     execute(i->second, parameters);
 }
 }
 
 
-void ScriptFile::clearFunctionToFileMappings()
+void ScriptFile::handleSpecificScriptEvent(StringHash eventType, VariantMap& eventData)
 {
 {
-    for (std::map<asIScriptFunction*, ScriptFile*>::iterator i = functionToFile.begin(); i != functionToFile.end(); )
-    {
-        std::map<asIScriptFunction*, ScriptFile*>::iterator current = i;
-        ++i;
-        if (current->second == this)
-            functionToFile.erase(current);
-    }
+    if (!mCompiled)
+        return;
+    
+    std::map<std::pair<EventListener*, StringHash>, asIScriptFunction*>::iterator i = mSpecificEventHandlers.find(std::make_pair(
+        getEventSender(), eventType));
+    if (i == mSpecificEventHandlers.end())
+        return;
+    
+    std::vector<Variant> parameters;
+    parameters.push_back(Variant((void*)&eventType));
+    parameters.push_back(Variant((void*)&eventData));
+    execute(i->second, parameters);
 }
 }
 
 
 ScriptFile* getScriptContextFile()
 ScriptFile* getScriptContextFile()
 {
 {
     asIScriptFunction* function = asGetActiveContext()->GetFunction();
     asIScriptFunction* function = asGetActiveContext()->GetFunction();
-    std::map<asIScriptFunction*, ScriptFile*>::const_iterator i = functionToFile.find(function);
-    if (i != functionToFile.end())
+    asIScriptModule* module = function->GetEngine()->GetModule(function->GetModuleName());
+    std::map<asIScriptModule*, ScriptFile*>::const_iterator i = moduleToFile.find(module);
+    if (i != moduleToFile.end())
         return i->second;
         return i->second;
     else
     else
         return 0;
         return 0;

+ 5 - 3
Engine/Script/ScriptFile.h

@@ -57,6 +57,8 @@ public:
     virtual void load(Deserializer& source, ResourceCache* cache = 0);
     virtual void load(Deserializer& source, ResourceCache* cache = 0);
     //! Add an event handler. Called by script exposed version of subscribeToEvent()
     //! Add an event handler. Called by script exposed version of subscribeToEvent()
     virtual void addEventHandler(StringHash eventType, const std::string& handlerName);
     virtual void addEventHandler(StringHash eventType, const std::string& handlerName);
+    //! Add an event handler for a specific sender. Called by script exposed version of subscribeToEvent()
+    virtual void addEventHandler(EventListener* sender, StringHash eventType, const std::string& handlerName);
     
     
     //! Query for a function by declaration and execute if found
     //! Query for a function by declaration and execute if found
     bool execute(const std::string& declaration, const std::vector<Variant>& parameters = std::vector<Variant>());
     bool execute(const std::string& declaration, const std::vector<Variant>& parameters = std::vector<Variant>());
@@ -91,10 +93,10 @@ private:
     void addScriptSection(asIScriptEngine* engine, Deserializer& source, ResourceCache* cache);
     void addScriptSection(asIScriptEngine* engine, Deserializer& source, ResourceCache* cache);
     //! Set parameters for a function or method
     //! Set parameters for a function or method
     void setParameters(asIScriptContext* context, asIScriptFunction* function, const std::vector<Variant>& parameters);
     void setParameters(asIScriptContext* context, asIScriptFunction* function, const std::vector<Variant>& parameters);
-    //! Handle an event with a handler in script
+    //! Handle an event in script
     void handleScriptEvent(StringHash eventType, VariantMap& eventData);
     void handleScriptEvent(StringHash eventType, VariantMap& eventData);
-    //! Clear function-to-file mappings when script module is discarded
-    void clearFunctionToFileMappings();
+    //! Handle a specific sender's event in script
+    void handleSpecificScriptEvent(StringHash eventType, VariantMap& eventData);
     
     
     //! Script engine
     //! Script engine
     SharedPtr<ScriptEngine> mScriptEngine;
     SharedPtr<ScriptEngine> mScriptEngine;

+ 33 - 0
Engine/Script/ScriptInstance.cpp

@@ -435,6 +435,23 @@ void ScriptInstance::addEventHandler(StringHash eventType, const std::string& ha
     mEventHandlers[eventType] = method;
     mEventHandlers[eventType] = method;
 }
 }
 
 
+void ScriptInstance::addEventHandler(EventListener* sender, StringHash eventType, const std::string& handlerName)
+{
+    if ((!mScriptObject) || (!sender))
+        return;
+    
+    std::string declaration = "void " + handlerName + "(StringHash, VariantMap&)";
+    asIScriptFunction* method = mScriptFile->getMethod(mScriptObject, declaration);
+    if (!method)
+    {
+        LOGERROR("Event handler method " + declaration + " not found in " + mScriptFile->getName());
+        return;
+    }
+    
+    subscribeToEvent(sender, eventType, EVENT_HANDLER(ScriptInstance, handleSpecificScriptEvent));
+    mSpecificEventHandlers[std::make_pair(sender, eventType)] = method;
+}
+
 bool ScriptInstance::createObject()
 bool ScriptInstance::createObject()
 {
 {
     if (!mScriptFile)
     if (!mScriptFile)
@@ -641,6 +658,22 @@ void ScriptInstance::handleScriptEvent(StringHash eventType, VariantMap& eventDa
     mScriptFile->execute(mScriptObject, i->second, parameters);
     mScriptFile->execute(mScriptObject, i->second, parameters);
 }
 }
 
 
+void ScriptInstance::handleSpecificScriptEvent(StringHash eventType, VariantMap& eventData)
+{
+    if ((!mEnabled) || (!mScriptFile) || (!mScriptObject))
+        return;
+    
+    std::map<std::pair<EventListener*, StringHash>, asIScriptFunction*>::iterator i = mSpecificEventHandlers.find(std::make_pair(
+        getEventSender(), eventType));
+    if (i == mSpecificEventHandlers.end())
+        return;
+    
+    std::vector<Variant> parameters;
+    parameters.push_back(Variant((void*)&eventType));
+    parameters.push_back(Variant((void*)&eventData));
+    mScriptFile->execute(mScriptObject, i->second, parameters);
+}
+
 ScriptInstance* getScriptContextInstance()
 ScriptInstance* getScriptContextInstance()
 {
 {
     void* object = asGetActiveContext()->GetThisPointer();
     void* object = asGetActiveContext()->GetThisPointer();

+ 5 - 1
Engine/Script/ScriptInstance.h

@@ -107,6 +107,8 @@ public:
     virtual void getResourceRefs(std::vector<Resource*>& dest);
     virtual void getResourceRefs(std::vector<Resource*>& dest);
     //! Add an event handler. Called by script exposed version of subscribeToEvent()
     //! Add an event handler. Called by script exposed version of subscribeToEvent()
     virtual void addEventHandler(StringHash eventType, const std::string& handlerName);
     virtual void addEventHandler(StringHash eventType, const std::string& handlerName);
+    //! Add an event handler for a specific sender. Called by script exposed version of subscribeToEvent()
+    virtual void addEventHandler(EventListener* sender, StringHash eventType, const std::string& handlerName);
     
     
     //! Set script file and class
     //! Set script file and class
     bool setScriptClass(ScriptFile* scriptFile, const std::string& className);
     bool setScriptClass(ScriptFile* scriptFile, const std::string& className);
@@ -154,8 +156,10 @@ private:
     void handlePhysicsPreStep(StringHash eventType, VariantMap& eventData);
     void handlePhysicsPreStep(StringHash eventType, VariantMap& eventData);
     //! Handle physics post-step event
     //! Handle physics post-step event
     void handlePhysicsPostStep(StringHash eventType, VariantMap& eventData);
     void handlePhysicsPostStep(StringHash eventType, VariantMap& eventData);
-    //! Handle an event with a handler in script
+    //! Handle an event in script
     void handleScriptEvent(StringHash eventType, VariantMap& eventData);
     void handleScriptEvent(StringHash eventType, VariantMap& eventData);
+    //! Handle a specific sender's event in script
+    void handleSpecificScriptEvent(StringHash eventType, VariantMap& eventData);
     
     
     //! Script engine
     //! Script engine
     SharedPtr<ScriptEngine> mScriptEngine;
     SharedPtr<ScriptEngine> mScriptEngine;

+ 1 - 0
Engine/UI/LineEdit.cpp

@@ -331,6 +331,7 @@ void LineEdit::onChar(unsigned char c, int buttons, int qualifiers)
         
         
         VariantMap eventData;
         VariantMap eventData;
         eventData[P_ELEMENT] = (void*)this;
         eventData[P_ELEMENT] = (void*)this;
+        eventData[P_TEXT] = mLine;
         sendEvent(EVENT_TEXTFINISHED, eventData);
         sendEvent(EVENT_TEXTFINISHED, eventData);
         
         
         mText->clearSelection();
         mText->clearSelection();

+ 1 - 1
Engine/UI/LineEdit.h

@@ -95,7 +95,7 @@ public:
     //! Return text element
     //! Return text element
     Text* getTextElement() const { return mText; }
     Text* getTextElement() const { return mText; }
     //! Return cursor element
     //! Return cursor element
-    BorderImage* getCursorElement() const { return mCursor; }
+    BorderImage* getCursor() const { return mCursor; }
     
     
 protected:
 protected:
     //! Update displayed text
     //! Update displayed text

+ 1 - 0
Engine/UI/UIEvents.h

@@ -83,6 +83,7 @@ DEFINE_EVENT(EVENT_TEXTCHANGED, TextChanged)
 DEFINE_EVENT(EVENT_TEXTFINISHED, TextFinished)
 DEFINE_EVENT(EVENT_TEXTFINISHED, TextFinished)
 {
 {
     EVENT_PARAM(P_ELEMENT, Element);            // UIElement pointer
     EVENT_PARAM(P_ELEMENT, Element);            // UIElement pointer
+    EVENT_PARAM(P_TEXT, Text);                  // string
 }
 }
 
 
 //! Menu item selected
 //! Menu item selected