aboutsummaryrefslogtreecommitdiff
path: root/engines/sword25/script/luacallback.cpp
blob: 6af1c17e92ef472e012a19d174a77d55b6ac5914 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// -----------------------------------------------------------------------------
// This file is part of Broken Sword 2.5
// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsd�rfer
//
// Broken Sword 2.5 is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// Broken Sword 2.5 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Broken Sword 2.5; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// Includes
// -----------------------------------------------------------------------------

#include "sword25/script/luacallback.h"
#include "sword25/script/luabindhelper.h"

extern "C"
{
	#include "sword25/util/lua/lua.h"
	#include "sword25/util/lua/lauxlib.h"
}

#define BS_LOG_PREFIX "LUA"

// -----------------------------------------------------------------------------

namespace
{
	const char * CALLBACKTABLE_NAME = "__CALLBACKS";
}

// -----------------------------------------------------------------------------

BS_LuaCallback::BS_LuaCallback(lua_State * L)
{
	// Callbacktabelle erstellen.
	lua_newtable(L);
	lua_setglobal(L, CALLBACKTABLE_NAME);
}

// -----------------------------------------------------------------------------

BS_LuaCallback::~BS_LuaCallback()
{
}

// -----------------------------------------------------------------------------

void BS_LuaCallback::RegisterCallbackFunction(lua_State * L, unsigned int ObjectHandle)
{
	BS_ASSERT(lua_isfunction(L, -1));
	EnsureObjectCallbackTableExists(L, ObjectHandle);

	// Funktion in der Objekt-Callbacktabelle speichern.
	lua_pushvalue(L, -2);
	luaL_ref(L, -2);

	// Funktion und Objekt-Callbacktabelle vom Stack poppen.
	lua_pop(L, 2);
}

// -----------------------------------------------------------------------------

void BS_LuaCallback::UnregisterCallbackFunction(lua_State * L, unsigned int ObjectHandle)
{
	BS_ASSERT(lua_isfunction(L, -1));
	EnsureObjectCallbackTableExists(L,ObjectHandle);

	// �ber alle Elemente der Objekt-Callbacktabelle iterieren und die Funktion daraus entfernen.
	lua_pushnil(L);
	while (lua_next(L, -2) != 0)
	{
		// Der Wert des aktuellen Elementes liegt oben auf dem Stack, darunter der Index.

		// Falls der Wert identisch mit dem Funktionsparameter ist, wird sie aus der Tabelle entfernt.
		if (lua_equal(L, -1, -4))
		{
			lua_pushvalue(L, -2);
			lua_pushnil(L);
			lua_settable(L, -5);

			// Die Funktion wurde gefunden, die Iteration kann abgebrochen werden.
			lua_pop(L, 2);
			break;
		}
		else
		{
			// Wert vom Stack poppen. Der Index liegt dann oben f�r den n�chsten Aufruf von lua_next().
			lua_pop(L, 1);
		}
	}

	// Funktion und Objekt-Callbacktabelle vom Stack poppen.
	lua_pop(L, 2);
}

// -----------------------------------------------------------------------------

void BS_LuaCallback::RemoveAllObjectCallbacks(lua_State * L, unsigned int ObjectHandle)
{
	PushCallbackTable(L);

	// Objekt-Callbacktabelle aus der Callbacktabelle entfernen.
	lua_pushnumber(L, ObjectHandle);
	lua_pushnil(L);
	lua_settable(L, -3);

	lua_pop(L, 1);
}

// -----------------------------------------------------------------------------

void BS_LuaCallback::InvokeCallbackFunctions(lua_State * L, unsigned int ObjectHandle)
{
	EnsureObjectCallbackTableExists(L, ObjectHandle);

	// �ber die Tabelle iterieren und alle Callbacks ausf�hren.
	lua_pushnil(L);
	while (lua_next(L, -2) != 0)
	{
		// Der Wert des aktuellen Elementes liegt oben auf dem Stack, darunter der Index.

		// Falls der Wert eine Funktion ist, wird sie ausgef�hrt.
		if (lua_type(L, -1) == LUA_TFUNCTION)
		{
			// Pre-Funktion aufrufen.
			// Abgeleitete Klassen k�nnten in dieser Funktion Parameter auf den Stack schieben.
			// Der R�ckgabewert gibt die Anzahl der Parameter zur�ck.
			int ArgumentCount = PreFunctionInvokation(L);

			// lua_pcall poppt die Funktion und die Parameter selber vom Stack.
			if (lua_pcall(L, ArgumentCount, 0, 0) != 0)
			{
				// Ein Fehler ist aufgetreten.
				BS_LOG_ERRORLN("An error occured executing a callback function: %s", lua_tostring(L, -1));

				// Fehlernachricht vom Stack poppen.
				lua_pop(L, 1);
			}
		}
		else
		{
			// Wert vom Stack poppen. Der Index liegt dann oben f�r den n�chsten Aufruf von lua_next().
			lua_pop(L, 1);
		}
	}
}

// -----------------------------------------------------------------------------

void BS_LuaCallback::EnsureObjectCallbackTableExists(lua_State * L, unsigned int ObjectHandle)
{
	PushObjectCallbackTable(L, ObjectHandle);

	// Falls die Tabelle nil ist, muss sie zun�chst erstellt werden.
	if (lua_isnil(L, -1))
	{
		// Nil vom Stack poppen.
		lua_pop(L, 1);

		PushCallbackTable(L);

		// Neue Tabelle unter dem Index ObjectHandle in der Callbacktabelle ablegen.
		lua_newtable(L);
		lua_pushnumber(L, ObjectHandle);
		lua_pushvalue(L, -2);
		lua_settable(L, -4);

		// Callbacktabelle vom Stack entfernen, Objekt-Callbacktabelle aber dort lassen.
		lua_remove(L, -2);
	}
}

// -----------------------------------------------------------------------------

void BS_LuaCallback::PushCallbackTable(lua_State * L)
{
	lua_getglobal(L, CALLBACKTABLE_NAME);
}

// -----------------------------------------------------------------------------

void BS_LuaCallback::PushObjectCallbackTable(lua_State * L, unsigned int ObjectHandle)
{
	PushCallbackTable(L);

	// Objekt-Callbacktabelle auf den Stack legen.
	lua_pushnumber(L, ObjectHandle);
	lua_gettable(L, -2);

	// Callbacktabelle vom Stack entfernen, Objekt-Callbacktabelle aber dort lassen.
	lua_remove(L, -2);
}