Rev 4766 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
3148 | helixhorne | 1 | /* The Lunatic Interpreter, part of EDuke32. Game-side stuff. */ |
2034 | helixhorne | 2 | |
3 | #include <stdint.h> |
||
3946 | helixhorne | 4 | #include <stdlib.h> // getenv |
5 | #include <errno.h> |
||
6 | #include <string.h> // strerror |
||
2034 | helixhorne | 7 | |
4286 | helixhorne | 8 | #ifdef __cplusplus |
9 | extern "C" { |
||
10 | #endif |
||
11 | |||
4107 | helixhorne | 12 | #ifdef USE_LUAJIT_2_1 |
13 | # include <luajit-2.1/lualib.h> |
||
14 | # include <luajit-2.1/lauxlib.h> |
||
15 | #else |
||
16 | # include <luajit-2.0/lualib.h> |
||
17 | # include <luajit-2.0/lauxlib.h> |
||
18 | #endif |
||
2034 | helixhorne | 19 | |
4286 | helixhorne | 20 | #ifdef __cplusplus |
21 | } |
||
22 | #endif |
||
23 | |||
3319 | helixhorne | 24 | #include "build.h" // printext256 |
25 | |||
3148 | helixhorne | 26 | #include "lunatic_game.h" |
27 | |||
2034 | helixhorne | 28 | #include "osd.h" |
3148 | helixhorne | 29 | #include "gamedef.h" // EventNames[] |
2034 | helixhorne | 30 | |
3056 | helixhorne | 31 | |
3148 | helixhorne | 32 | L_State g_ElState; |
2034 | helixhorne | 33 | |
3148 | helixhorne | 34 | |
2329 | helixhorne | 35 | // this serves two purposes: |
36 | // the values as booleans and the addresses as keys to the Lua registry |
||
2746 | helixhorne | 37 | uint8_t g_elEvents[MAXEVENTS]; |
2034 | helixhorne | 38 | |
2747 | helixhorne | 39 | // same thing for actors: |
3315 | helixhorne | 40 | el_actor_t g_elActors[MAXTILES]; |
2329 | helixhorne | 41 | |
4112 | helixhorne | 42 | // Session variable. Never restored except by 'readgamevar'. |
43 | int32_t g_elSessionVar[8]; // MAXSESSIONVARS, KEEPINSYNC con_lang.lua |
||
44 | |||
3593 | helixhorne | 45 | // Set to 1 on error in event. |
46 | int32_t g_elEventError; |
||
47 | |||
4236 | helixhorne | 48 | // Will be set to 0 after the first time that user Lua modules are run. |
49 | int32_t g_elFirstTime = 1; |
||
50 | |||
3320 | helixhorne | 51 | int32_t g_elCallDepth = 0; |
3601 | helixhorne | 52 | int32_t g_RETURN; |
3320 | helixhorne | 53 | |
2842 | helixhorne | 54 | // for timing events and actors |
3788 | helixhorne | 55 | static int32_t g_timingInited = 0; |
2842 | helixhorne | 56 | uint32_t g_eventCalls[MAXEVENTS], g_actorCalls[MAXTILES]; |
3557 | helixhorne | 57 | double g_eventTotalMs[MAXEVENTS], g_actorTotalMs[MAXTILES], g_actorMinMs[MAXTILES], g_actorMaxMs[MAXTILES]; |
2827 | helixhorne | 58 | |
3535 | helixhorne | 59 | // Used as Lua registry key to the tweak_traceback_msg() function, set to 1 if |
60 | // such a function has been registered. |
||
61 | static uint8_t g_tweakTracebackMsg = 0; |
||
2842 | helixhorne | 62 | |
2329 | helixhorne | 63 | // forward-decls... |
3352 | helixhorne | 64 | static int32_t SetEvent_CF(lua_State *L); |
65 | static int32_t SetActor_CF(lua_State *L); |
||
2329 | helixhorne | 66 | |
4286 | helixhorne | 67 | |
68 | #ifdef __cplusplus |
||
69 | extern "C" { |
||
70 | #endif |
||
71 | |||
2650 | helixhorne | 72 | // in lpeg.o |
73 | extern int luaopen_lpeg(lua_State *L); |
||
2329 | helixhorne | 74 | |
4286 | helixhorne | 75 | #ifdef __cplusplus |
76 | } |
||
77 | #endif |
||
2857 | helixhorne | 78 | |
4286 | helixhorne | 79 | |
2857 | helixhorne | 80 | |
81 | // See: Good Practice in (Pseudo) Random Number Generation for |
||
82 | // Bioinformatics Applications, by David Jones |
||
3341 | helixhorne | 83 | ATTRIBUTE_OPTIMIZE("O2") |
3932 | helixhorne | 84 | LUNATIC_EXTERN uint32_t rand_jkiss_u32(rng_jkiss_t *s) |
2857 | helixhorne | 85 | { |
86 | uint64_t t; |
||
87 | s->x = 314527869 * s->x + 1234567; |
||
88 | s->y ^= s->y << 5; s->y ^= s->y >> 7; s->y ^= s->y << 22; |
||
89 | t = 4294584393ULL * s->z + s->c; s->c = t >> 32; s->z = t; |
||
90 | return s->x + s->y + s->z; |
||
91 | } |
||
92 | |||
3341 | helixhorne | 93 | ATTRIBUTE_OPTIMIZE("O2") |
3932 | helixhorne | 94 | LUNATIC_EXTERN double rand_jkiss_dbl(rng_jkiss_t *s) |
2857 | helixhorne | 95 | { |
96 | double x; |
||
97 | unsigned int a, b; |
||
98 | a = rand_jkiss_u32(s) >> 6; /* Upper 26 bits */ |
||
99 | b = rand_jkiss_u32(s) >> 5; /* Upper 27 bits */ |
||
100 | x = (a * 134217728.0 + b) / 9007199254740992.0; |
||
101 | return x; |
||
102 | } |
||
103 | |||
104 | |||
2842 | helixhorne | 105 | void El_PrintTimes(void) |
106 | { |
||
3946 | helixhorne | 107 | int32_t i; |
3258 | helixhorne | 108 | const char nn = Bstrlen("EVENT_"); |
2650 | helixhorne | 109 | |
3946 | helixhorne | 110 | // Try environment variable specifying the base name (sans ".actors.csv" or |
111 | // ".events.csv") for a CSV file to output, for further processing in e.g. |
||
112 | // GSL shell: http://www.nongnu.org/gsl-shell/ |
||
113 | const char *basefn = getenv("LUNATIC_TIMING_BASEFN"); |
||
114 | |||
115 | if (basefn != NULL) |
||
3258 | helixhorne | 116 | { |
3946 | helixhorne | 117 | const int32_t baselen = Bstrlen(basefn); |
118 | const int32_t addnlen = Bstrlen(".actors.csv"); // MUST equal that of ".events.csv" |
||
3258 | helixhorne | 119 | |
4491 | helixhorne | 120 | char *fullfn = (char *)Xmalloc(baselen + addnlen + 1); |
3946 | helixhorne | 121 | BFILE *outf; |
122 | |||
123 | if (fullfn == NULL) |
||
124 | return; |
||
125 | |||
126 | Bmemcpy(fullfn, basefn, baselen); |
||
127 | |||
128 | // EVENTS |
||
129 | Bmemcpy(fullfn+baselen, ".events.csv", addnlen+1); |
||
130 | outf = Bfopen(fullfn, "w"); |
||
131 | if (outf == NULL) |
||
3258 | helixhorne | 132 | { |
3946 | helixhorne | 133 | OSD_Printf("Couldn't open \"%s\" for writing timing data: %s", fullfn, strerror(errno)); |
134 | goto finish; |
||
135 | } |
||
3258 | helixhorne | 136 | |
3946 | helixhorne | 137 | Bfprintf(outf, "evtname,numcalls,total_ms,mean_us\n"); // times in usecs are per-call |
138 | for (i=0; i<MAXEVENTS; i++) |
||
139 | if (g_eventCalls[i]) |
||
140 | Bfprintf(outf, "%s,%d,%f,%f\n", EventNames[i]+nn, g_eventCalls[i], g_eventTotalMs[i], |
||
141 | 1000*g_eventTotalMs[i]/g_eventCalls[i]); |
||
142 | Bfclose(outf); |
||
3258 | helixhorne | 143 | |
3946 | helixhorne | 144 | // ACTORS |
145 | Bmemcpy(fullfn+baselen, ".actors.csv", addnlen+1); |
||
146 | outf = Bfopen(fullfn, "w"); |
||
147 | if (outf == NULL) |
||
148 | { |
||
149 | OSD_Printf("Couldn't open \"%s\" for writing timing data: %s", fullfn, strerror(errno)); |
||
150 | goto finish; |
||
3258 | helixhorne | 151 | } |
2842 | helixhorne | 152 | |
3946 | helixhorne | 153 | Bfprintf(outf, "tilenum,numcalls,total_ms,min_us,mean_us,max_us\n"); |
154 | for (i=0; i<MAXTILES; i++) |
||
155 | if (g_actorCalls[i]) |
||
156 | Bfprintf(outf, "%d,%d,%f,%f,%f,%f\n", i, g_actorCalls[i], g_actorTotalMs[i], |
||
157 | 1000*g_actorMinMs[i], |
||
158 | 1000*g_actorTotalMs[i]/g_actorCalls[i], |
||
159 | 1000*g_actorMaxMs[i]); |
||
160 | Bfclose(outf); |
||
161 | |||
162 | OSD_Printf("Wrote timing data to \"%s.*.csv\"\n", basefn); |
||
163 | finish: |
||
164 | Bfree(fullfn); |
||
165 | return; |
||
166 | } |
||
167 | else |
||
168 | { |
||
169 | // If not writing out CSV files, print timing data to log instead. |
||
170 | |||
171 | char buf[32]; |
||
172 | int32_t maxlen = 0; |
||
4178 | helixhorne | 173 | int32_t haveev=0, haveac=0; |
3946 | helixhorne | 174 | |
175 | for (i=0; i<MAXEVENTS; i++) |
||
176 | { |
||
177 | int32_t len = Bstrlen(EventNames[i]+nn); |
||
178 | Bassert(len < (int32_t)sizeof(buf)); |
||
179 | maxlen = max(len, maxlen); |
||
180 | } |
||
181 | |||
182 | for (i=0; i<MAXEVENTS; i++) |
||
183 | if (g_eventCalls[i]) |
||
184 | { |
||
185 | int32_t n=Bsprintf(buf, "%s", EventNames[i]+nn); |
||
186 | |||
4178 | helixhorne | 187 | if (!haveev) |
188 | { |
||
189 | haveev = 1; |
||
190 | OSD_Printf("\n -- event times: [event]={ total calls, total time [ms], mean time/call [us] }\n"); |
||
191 | } |
||
192 | |||
3946 | helixhorne | 193 | for (; n<maxlen; n++) |
194 | buf[n] = ' '; |
||
195 | buf[maxlen] = 0; |
||
196 | |||
197 | OSD_Printf(" [%s]={ %8d, %10.3f, %10.3f },\n", |
||
198 | buf, g_eventCalls[i], g_eventTotalMs[i], |
||
199 | 1000*g_eventTotalMs[i]/g_eventCalls[i]); |
||
200 | } |
||
201 | |||
202 | for (i=0; i<MAXTILES; i++) |
||
203 | if (g_actorCalls[i]) |
||
4178 | helixhorne | 204 | { |
205 | if (!haveac) |
||
206 | { |
||
207 | haveac = 1; |
||
208 | OSD_Printf("\n -- actor times: [tile]={ total calls, total time [ms], {min,mean,max} time/call [us] }\n"); |
||
209 | } |
||
210 | |||
3946 | helixhorne | 211 | OSD_Printf(" [%5d]={ %8d, %9.3f, %9.3f, %9.3f, %9.3f },\n", |
212 | i, g_actorCalls[i], g_actorTotalMs[i], |
||
213 | 1000*g_actorMinMs[i], |
||
214 | 1000*g_actorTotalMs[i]/g_actorCalls[i], |
||
215 | 1000*g_actorMaxMs[i]); |
||
4178 | helixhorne | 216 | } |
3946 | helixhorne | 217 | } |
2842 | helixhorne | 218 | } |
219 | |||
3319 | helixhorne | 220 | ////////// ERROR REPORTING ////////// |
3320 | helixhorne | 221 | #define EL_MAXERRORS 20 |
3319 | helixhorne | 222 | static int32_t el_numErrors=0, el_tooMuchErrors; |
223 | static char *el_errorMsgs[EL_MAXERRORS]; |
||
4134 | helixhorne | 224 | int8_t el_addNewErrors = 1; // add new errors to display? |
2827 | helixhorne | 225 | |
3319 | helixhorne | 226 | // Compare against all other error messages. |
227 | // Strictly seen, this is quadratic-time, but EL_MAXERRORS is small and |
||
228 | // errors should be fixed anyway. |
||
229 | static int32_t cmp_against_others(const char *str, int32_t slen) |
||
230 | { |
||
231 | int32_t i; |
||
232 | for (i=0; i<el_numErrors; i++) |
||
233 | if (!Bstrncmp(str, el_errorMsgs[i], slen)) |
||
234 | return 1; |
||
235 | return 0; |
||
236 | } |
||
237 | |||
4118 | helixhorne | 238 | LUNATIC_EXTERN void El_OnError(const char *str) |
3319 | helixhorne | 239 | { |
4134 | helixhorne | 240 | if (el_addNewErrors && !el_tooMuchErrors) |
3319 | helixhorne | 241 | { |
242 | char *errstr = NULL; |
||
243 | const char *nl = Bstrchr(str, '\n'); |
||
244 | |||
245 | // First, check whether the error message matches an already saved one |
||
246 | if (nl) |
||
247 | { |
||
248 | // cut off string after the newline |
||
249 | if (cmp_against_others(str, nl-str)) |
||
250 | return; |
||
251 | } |
||
252 | else |
||
253 | { |
||
254 | // save string fully |
||
255 | if (cmp_against_others(str, Bstrlen(str))) |
||
256 | return; |
||
257 | } |
||
258 | |||
259 | // If the (EL_MAXERRORS+1)'th distinct error appeared, we have too many. |
||
260 | if (el_numErrors==EL_MAXERRORS) |
||
261 | { |
||
262 | el_tooMuchErrors = 1; |
||
263 | return; |
||
264 | } |
||
265 | |||
266 | // Otherwise, allocate storage for the potentially clipped error string... |
||
267 | if (nl) |
||
268 | { |
||
4491 | helixhorne | 269 | errstr = (char *)Xmalloc(nl-str+1); |
270 | Bmemcpy(errstr, str, nl-str); |
||
271 | errstr[nl-str] = 0; |
||
3319 | helixhorne | 272 | } |
273 | else |
||
274 | { |
||
4491 | helixhorne | 275 | errstr = Xstrdup(str); |
3319 | helixhorne | 276 | } |
277 | |||
278 | // ...and save it: |
||
4491 | helixhorne | 279 | el_errorMsgs[el_numErrors++] = errstr; |
3319 | helixhorne | 280 | } |
281 | } |
||
282 | |||
283 | void El_ClearErrors(void) |
||
284 | { |
||
285 | int32_t i; |
||
286 | for (i=0; i<EL_MAXERRORS; i++) |
||
287 | { |
||
288 | Bfree(el_errorMsgs[i]); |
||
289 | el_errorMsgs[i] = NULL; |
||
290 | } |
||
291 | el_numErrors = el_tooMuchErrors = 0; |
||
292 | } |
||
293 | |||
294 | void El_DisplayErrors(void) |
||
295 | { |
||
296 | int32_t i; |
||
297 | for (i=0; i<el_numErrors; i++) |
||
298 | printext256(8, 8+8*i, 242, 0, el_errorMsgs[i], 0); |
||
299 | if (el_tooMuchErrors) |
||
300 | printext256(8, 8+8*EL_MAXERRORS, 242, 0, "(more distinct errors ...)", 0); |
||
301 | } |
||
302 | |||
303 | |||
3148 | helixhorne | 304 | ////////// STATE CREATION/DESTRUCTIION ////////// |
305 | |||
3352 | helixhorne | 306 | static int our_traceback_CF(lua_State *L) |
307 | { |
||
308 | Bassert(lua_gettop(L)==1); |
||
309 | |||
310 | if (lua_type(L, 1)==LUA_TBOOLEAN) |
||
311 | { |
||
312 | lua_pushvalue(L, 1); // duplicate it |
||
313 | return 1; // and tell Lua to return it |
||
314 | } |
||
315 | |||
316 | Bassert(lua_type(L, 1)==LUA_TSTRING); |
||
317 | |||
318 | // call debug.traceback with the string |
||
319 | L_PushDebugTraceback(L); |
||
320 | lua_pushvalue(L, 1); |
||
321 | lua_call(L, 1, 1); |
||
322 | Bassert(lua_gettop(L)==2); // Lua will pop off args |
||
323 | |||
3535 | helixhorne | 324 | if (g_tweakTracebackMsg) |
325 | { |
||
326 | // Get tweak_traceback_msg() onto the stack. |
||
327 | lua_pushlightuserdata(L, &g_tweakTracebackMsg); |
||
328 | lua_gettable(L, LUA_REGISTRYINDEX); |
||
329 | |||
330 | lua_pushvalue(L, -2); // push copy of error message string |
||
331 | Bassert(lua_type(L, -1)==LUA_TSTRING); |
||
332 | |||
333 | // Call tweak_traceback_msg(). CAREFUL, it's unprotected! |
||
334 | lua_call(L, 1, 1); |
||
335 | } |
||
336 | |||
3352 | helixhorne | 337 | return 1; |
338 | } |
||
339 | |||
3535 | helixhorne | 340 | // Registers a function: str = tweak_traceback_msg(str) |
341 | static int32_t SetTweakTracebackMsg_CF(lua_State *L) |
||
342 | { |
||
343 | Bassert(lua_gettop(L)==1); |
||
344 | L_CheckAndRegisterFunction(L, &g_tweakTracebackMsg); |
||
345 | g_tweakTracebackMsg = 1; |
||
346 | return 0; |
||
347 | } |
||
348 | |||
349 | |||
3520 | helixhorne | 350 | ////// Lua C-API interfaces for C game functions that may call events. |
351 | // http://www.freelists.org/post/luajit/intermitten-lua-pcall-crash-on-x86-64-linux,1 |
||
352 | |||
353 | // Some of these are duplicate declarations: |
||
4766 | hendricks2 | 354 | #ifdef __cplusplus |
355 | extern "C" { |
||
356 | #endif |
||
3520 | helixhorne | 357 | extern void P_AddWeaponMaybeSwitchI(int32_t snum, int32_t weap); |
358 | extern void P_CheckWeaponI(int32_t snum); |
||
359 | extern int32_t A_ShootWithZvel(int32_t i, int32_t atwith, int32_t override_zvel); |
||
360 | extern int32_t A_Spawn(int32_t j, int32_t pn); |
||
361 | extern void VM_FallSprite(int32_t i); |
||
5039 | hendricks2 | 362 | extern int32_t VM_ResetPlayer2(int32_t snum, int32_t flags); |
3520 | helixhorne | 363 | extern void A_RadiusDamage(int32_t i, int32_t r, int32_t, int32_t, int32_t, int32_t); |
364 | extern void G_OperateSectors(int32_t sn, int32_t ii); |
||
365 | extern void G_OperateActivators(int32_t low,int32_t snum); |
||
366 | extern int32_t A_InsertSprite(int32_t whatsect,int32_t s_x,int32_t s_y,int32_t s_z,int32_t s_pn,int32_t s_s, |
||
367 | int32_t s_xr,int32_t s_yr,int32_t s_a,int32_t s_ve,int32_t s_zv,int32_t s_ow,int32_t s_ss); |
||
368 | extern void A_AddToDeleteQueue(int32_t i); |
||
369 | extern int32_t A_PlaySound(uint32_t num, int32_t i); |
||
3821 | helixhorne | 370 | extern void A_DeleteSprite(int32_t s); |
3948 | helixhorne | 371 | extern void G_ShowView(int32_t x, int32_t y, int32_t z, int32_t a, int32_t horiz, int32_t sect, |
372 | int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t unbiasedp); |
||
4766 | hendricks2 | 373 | extern void G_GameExit(const char *msg); |
374 | #ifdef __cplusplus |
||
375 | } |
||
376 | #endif |
||
3520 | helixhorne | 377 | |
378 | #define LARG(index) lua_tointeger(L, index) |
||
379 | |||
380 | #define ONE_ARG LARG(1) |
||
381 | #define TWO_ARGS LARG(1), LARG(2) |
||
382 | #define THREE_ARGS LARG(1), LARG(2), LARG(3) |
||
383 | |||
384 | #define CALL_WITH_RET(Name, ...) \ |
||
385 | int32_t ret = Name(__VA_ARGS__); \ |
||
386 | lua_pushinteger(L, ret); \ |
||
387 | return 1 |
||
388 | |||
389 | #define CALL_WITHOUT_RET(Name, ...) \ |
||
390 | Name(__VA_ARGS__); \ |
||
391 | return 0 |
||
392 | |||
393 | #define DEFINE_RET_CFUNC(Name, ...) \ |
||
394 | static int32_t Name##_CF(lua_State *L) \ |
||
395 | { \ |
||
396 | CALL_WITH_RET(Name, __VA_ARGS__); \ |
||
397 | } |
||
398 | |||
399 | #define DEFINE_VOID_CFUNC(Name, ...) \ |
||
400 | static int32_t Name##_CF(lua_State *L) \ |
||
401 | { \ |
||
402 | CALL_WITHOUT_RET(Name, __VA_ARGS__); \ |
||
403 | } |
||
404 | |||
405 | // NOTE: player struct -> player index -> player struct ugliness because |
||
406 | // pointers to FFI cdata apparently can't be reliably passed via lua_getpointer(). |
||
407 | // Not to mention that lua_getpointer() returns _const_ void*. |
||
408 | DEFINE_VOID_CFUNC(P_AddWeaponMaybeSwitchI, TWO_ARGS) |
||
409 | DEFINE_VOID_CFUNC(P_CheckWeaponI, ONE_ARG) |
||
410 | DEFINE_RET_CFUNC(A_ShootWithZvel, THREE_ARGS) |
||
411 | DEFINE_RET_CFUNC(A_Spawn, TWO_ARGS) |
||
412 | DEFINE_VOID_CFUNC(VM_FallSprite, ONE_ARG) |
||
5039 | hendricks2 | 413 | DEFINE_RET_CFUNC(VM_ResetPlayer2, TWO_ARGS) |
3520 | helixhorne | 414 | DEFINE_VOID_CFUNC(A_RadiusDamage, LARG(1), LARG(2), LARG(3), LARG(4), LARG(5), LARG(6)) |
415 | DEFINE_VOID_CFUNC(G_OperateSectors, TWO_ARGS) |
||
416 | DEFINE_VOID_CFUNC(G_OperateActivators, TWO_ARGS) |
||
417 | DEFINE_RET_CFUNC(A_InsertSprite, LARG(1), LARG(2), LARG(3), LARG(4), LARG(5), LARG(6), |
||
418 | LARG(7), LARG(8), LARG(9), LARG(10), LARG(11), LARG(12), LARG(13)) |
||
419 | DEFINE_VOID_CFUNC(A_AddToDeleteQueue, ONE_ARG) |
||
420 | DEFINE_RET_CFUNC(A_PlaySound, TWO_ARGS) |
||
3821 | helixhorne | 421 | DEFINE_VOID_CFUNC(A_DeleteSprite, ONE_ARG) |
3948 | helixhorne | 422 | DEFINE_VOID_CFUNC(G_ShowView, LARG(1), LARG(2), LARG(3), LARG(4), LARG(5), LARG(6), |
423 | LARG(7), LARG(8), LARG(9), LARG(10), LARG(11)) |
||
3520 | helixhorne | 424 | |
425 | #define CFUNC_REG(Name) { #Name, Name##_CF } |
||
426 | |||
4286 | helixhorne | 427 | static struct { const char *name; lua_CFunction func; } cfuncs[] = |
3520 | helixhorne | 428 | { |
429 | CFUNC_REG(P_AddWeaponMaybeSwitchI), |
||
430 | CFUNC_REG(P_CheckWeaponI), |
||
431 | CFUNC_REG(A_ShootWithZvel), |
||
432 | CFUNC_REG(A_Spawn), |
||
433 | CFUNC_REG(VM_FallSprite), |
||
434 | CFUNC_REG(VM_ResetPlayer2), |
||
435 | CFUNC_REG(A_RadiusDamage), |
||
436 | CFUNC_REG(G_OperateSectors), |
||
437 | CFUNC_REG(G_OperateActivators), |
||
438 | CFUNC_REG(A_InsertSprite), |
||
439 | CFUNC_REG(A_Spawn), |
||
440 | CFUNC_REG(A_AddToDeleteQueue), |
||
441 | CFUNC_REG(A_PlaySound), |
||
3821 | helixhorne | 442 | CFUNC_REG(A_DeleteSprite), |
3948 | helixhorne | 443 | CFUNC_REG(G_ShowView), |
3520 | helixhorne | 444 | }; |
445 | |||
446 | // Creates a global table "CF" containing the functions from cfuncs[]. |
||
447 | static void El_PushCFunctions(lua_State *L) |
||
448 | { |
||
449 | int32_t i; |
||
450 | |||
451 | lua_newtable(L); |
||
452 | |||
453 | for (i=0; i<(signed)sizeof(cfuncs)/(signed)sizeof(cfuncs[0]); i++) |
||
454 | { |
||
455 | lua_pushstring(L, cfuncs[i].name); |
||
456 | lua_pushcfunction(L, cfuncs[i].func); |
||
457 | lua_settable(L, -3); |
||
458 | } |
||
459 | |||
460 | lua_setglobal(L, "CF"); |
||
461 | } |
||
462 | |||
463 | ////// |
||
464 | |||
4119 | helixhorne | 465 | LUNATIC_CB int32_t (*El_RestoreGamevars)(const char *savecode); |
466 | |||
3148 | helixhorne | 467 | static void El_StateSetup(lua_State *L) |
2034 | helixhorne | 468 | { |
2827 | helixhorne | 469 | luaopen_lpeg(L); |
470 | lua_pop(L, lua_gettop(L)); // pop off whatever lpeg leaves on the stack |
||
2034 | helixhorne | 471 | |
2329 | helixhorne | 472 | // create misc. global functions in the Lua state |
3352 | helixhorne | 473 | lua_pushcfunction(L, SetEvent_CF); |
3320 | helixhorne | 474 | lua_setglobal(L, "gameevent_internal"); |
3352 | helixhorne | 475 | lua_pushcfunction(L, SetActor_CF); |
3315 | helixhorne | 476 | lua_setglobal(L, "gameactor_internal"); |
3535 | helixhorne | 477 | lua_pushcfunction(L, SetTweakTracebackMsg_CF); |
478 | lua_setglobal(L, "set_tweak_traceback_internal"); |
||
2329 | helixhorne | 479 | |
3520 | helixhorne | 480 | El_PushCFunctions(L); |
481 | |||
2827 | helixhorne | 482 | Bassert(lua_gettop(L)==0); |
3352 | helixhorne | 483 | |
3375 | helixhorne | 484 | // This is for engine-side Lua: |
3352 | helixhorne | 485 | lua_pushcfunction(L, &our_traceback_CF); |
2034 | helixhorne | 486 | } |
487 | |||
4766 | hendricks2 | 488 | static void El_OnOutOfMem(void) |
489 | { |
||
490 | G_GameExit("Out of memory in Lunatic."); |
||
491 | } |
||
492 | |||
3148 | helixhorne | 493 | // 0: success, <0: failure |
494 | int32_t El_CreateState(L_State *estate, const char *name) |
||
2034 | helixhorne | 495 | { |
3557 | helixhorne | 496 | int32_t i; |
497 | |||
3788 | helixhorne | 498 | if (!g_timingInited) |
499 | { |
||
500 | g_timingInited = 1; |
||
501 | for (i=0; i<MAXTILES; i++) |
||
502 | g_actorMinMs[i] = 1e308; |
||
503 | } |
||
3557 | helixhorne | 504 | |
3319 | helixhorne | 505 | L_ErrorFunc = El_OnError; |
3352 | helixhorne | 506 | L_OutOfMemFunc = El_OnOutOfMem; |
3319 | helixhorne | 507 | |
3148 | helixhorne | 508 | return L_CreateState(estate, name, &El_StateSetup); |
2034 | helixhorne | 509 | } |
510 | |||
3148 | helixhorne | 511 | void El_DestroyState(L_State *estate) |
2034 | helixhorne | 512 | { |
3148 | helixhorne | 513 | L_DestroyState(estate); |
3788 | helixhorne | 514 | |
3790 | helixhorne | 515 | g_tweakTracebackMsg = 0; |
516 | |||
3788 | helixhorne | 517 | // XXX: It would be cleaner to also clear stuff like g_elEvents[], but |
518 | // currently, when the game Lua state is recreated, the array should have |
||
519 | // the same values as before, so we're skipping that for now. |
||
2034 | helixhorne | 520 | } |
2329 | helixhorne | 521 | |
2747 | helixhorne | 522 | |
523 | ////////// Lua_CFunctions ////////// |
||
524 | |||
525 | // gameevent(EVENT_..., lua_function) |
||
3352 | helixhorne | 526 | static int32_t SetEvent_CF(lua_State *L) |
2329 | helixhorne | 527 | { |
2827 | helixhorne | 528 | int32_t eventidx; |
2329 | helixhorne | 529 | |
3320 | helixhorne | 530 | Bassert(lua_gettop(L) == 2); |
2827 | helixhorne | 531 | eventidx = luaL_checkint(L, 1); |
3320 | helixhorne | 532 | Bassert((unsigned)eventidx < MAXEVENTS); |
2827 | helixhorne | 533 | |
3148 | helixhorne | 534 | L_CheckAndRegisterFunction(L, &g_elEvents[eventidx]); |
2747 | helixhorne | 535 | g_elEvents[eventidx] = 1; |
2329 | helixhorne | 536 | |
2747 | helixhorne | 537 | return 0; |
538 | } |
||
2329 | helixhorne | 539 | |
3315 | helixhorne | 540 | // gameactor(actortile, strength, act, mov, movflags, lua_function) |
3352 | helixhorne | 541 | static int32_t SetActor_CF(lua_State *L) |
2747 | helixhorne | 542 | { |
3629 | helixhorne | 543 | int32_t actortile; |
3315 | helixhorne | 544 | el_actor_t *a; |
2827 | helixhorne | 545 | |
3315 | helixhorne | 546 | Bassert(lua_gettop(L) == 6); |
547 | |||
2827 | helixhorne | 548 | actortile = luaL_checkint(L, 1); |
3315 | helixhorne | 549 | Bassert((unsigned)actortile < MAXTILES); |
2827 | helixhorne | 550 | |
3315 | helixhorne | 551 | a = &g_elActors[actortile]; |
552 | L_CheckAndRegisterFunction(L, a); |
||
553 | |||
3629 | helixhorne | 554 | // Set actor properties. They can only be nil if we're chaining actor code. |
3315 | helixhorne | 555 | |
3629 | helixhorne | 556 | if (!lua_isnil(L, 2)) |
557 | a->strength = luaL_checkint(L, 2); |
||
558 | if (!lua_isnil(L, 5)) |
||
559 | a->movflags = luaL_checkint(L, 5); |
||
3315 | helixhorne | 560 | |
3629 | helixhorne | 561 | if (!lua_isnil(L, 3)) |
562 | Bmemcpy(&a->act, lua_topointer(L, 3), sizeof(con_action_t)); |
||
563 | if (!lua_isnil(L, 4)) |
||
564 | Bmemcpy(&a->mov, lua_topointer(L, 4), sizeof(con_move_t)); |
||
565 | |||
566 | a->haveit = 1; |
||
567 | |||
2329 | helixhorne | 568 | return 0; |
569 | } |
||
570 | |||
2747 | helixhorne | 571 | ////////////////////////////// |
572 | |||
3148 | helixhorne | 573 | static int32_t call_regd_function3(lua_State *L, void *keyaddr, |
574 | int32_t iActor, int32_t iPlayer, int32_t lDist) |
||
2329 | helixhorne | 575 | { |
3557 | helixhorne | 576 | #if !defined NDEBUG |
3948 | helixhorne | 577 | const int32_t top = lua_gettop(L); |
3557 | helixhorne | 578 | #endif |
3375 | helixhorne | 579 | lua_pushcfunction(L, &our_traceback_CF); |
580 | |||
2827 | helixhorne | 581 | // get the Lua function from the registry |
582 | lua_pushlightuserdata(L, keyaddr); |
||
583 | lua_gettable(L, LUA_REGISTRYINDEX); |
||
2329 | helixhorne | 584 | |
2746 | helixhorne | 585 | lua_pushinteger(L, iActor); |
586 | lua_pushinteger(L, iPlayer); |
||
587 | lua_pushinteger(L, lDist); |
||
588 | |||
2329 | helixhorne | 589 | // -- call it! -- |
3948 | helixhorne | 590 | { |
591 | const int32_t i = lua_pcall(L, 3, 0, -5); |
||
592 | const int32_t haveerr = (i != 0); |
||
3345 | helixhorne | 593 | |
3948 | helixhorne | 594 | Bassert(lua_iscfunction(L, -1-haveerr)); |
595 | lua_remove(L, -1-haveerr); |
||
2329 | helixhorne | 596 | |
3948 | helixhorne | 597 | Bassert(lua_gettop(L) == top+haveerr); |
598 | |||
599 | return i; |
||
600 | } |
||
2747 | helixhorne | 601 | } |
602 | |||
3352 | helixhorne | 603 | static int32_t g_eventIdx = 0; |
604 | static void El_EventErrorPrint(const char *errmsg) |
||
605 | { |
||
606 | OSD_Printf(OSD_ERROR "event \"%s\" runtime error: %s\n", |
||
607 | EventNames[g_eventIdx], errmsg); |
||
608 | } |
||
609 | |||
3526 | helixhorne | 610 | int32_t El_CallEvent(L_State *estate, int32_t eventidx, int32_t iActor, int32_t iPlayer, int32_t lDist, int32_t *iReturn) |
2747 | helixhorne | 611 | { |
612 | // XXX: estate must be the one where the events were registered... |
||
613 | // make a global? |
||
614 | |||
615 | lua_State *const L = estate->L; |
||
3320 | helixhorne | 616 | int32_t i; |
2747 | helixhorne | 617 | |
3601 | helixhorne | 618 | const int32_t o_RETURN = g_RETURN; |
619 | g_RETURN = *iReturn; |
||
3526 | helixhorne | 620 | |
3320 | helixhorne | 621 | g_elCallDepth++; |
622 | i = call_regd_function3(L, &g_elEvents[eventidx], iActor, iPlayer, lDist); |
||
623 | g_elCallDepth--; |
||
2747 | helixhorne | 624 | |
3601 | helixhorne | 625 | *iReturn = g_RETURN; |
626 | g_RETURN = o_RETURN; |
||
3526 | helixhorne | 627 | |
3352 | helixhorne | 628 | if (i != 0) |
2329 | helixhorne | 629 | { |
3593 | helixhorne | 630 | g_elEventError = 1; |
3352 | helixhorne | 631 | g_eventIdx = eventidx; |
632 | return L_HandleError(L, i, &El_EventErrorPrint); |
||
2329 | helixhorne | 633 | } |
634 | |||
635 | return 0; |
||
636 | } |
||
2747 | helixhorne | 637 | |
3352 | helixhorne | 638 | static int32_t g_actorTile, g_iActor; |
639 | static void El_ActorErrorPrint(const char *errmsg) |
||
640 | { |
||
641 | OSD_Printf(OSD_ERROR "actor %d (sprite %d) runtime error: %s\n", |
||
642 | g_actorTile, g_iActor, errmsg); |
||
643 | } |
||
644 | |||
3148 | helixhorne | 645 | int32_t El_CallActor(L_State *estate, int32_t actortile, int32_t iActor, int32_t iPlayer, int32_t lDist) |
2747 | helixhorne | 646 | { |
647 | lua_State *const L = estate->L; |
||
3320 | helixhorne | 648 | int32_t i; |
2747 | helixhorne | 649 | |
3320 | helixhorne | 650 | g_elCallDepth++; |
651 | i = call_regd_function3(L, &g_elActors[actortile], iActor, iPlayer, lDist); |
||
652 | g_elCallDepth--; |
||
2747 | helixhorne | 653 | |
3352 | helixhorne | 654 | if (i != 0) |
2747 | helixhorne | 655 | { |
3352 | helixhorne | 656 | g_actorTile = actortile; |
657 | g_iActor = iActor; |
||
658 | return L_HandleError(L, i, &El_ActorErrorPrint); |
||
2747 | helixhorne | 659 | } |
660 | |||
661 | return 0; |
||
662 | } |