Newer
Older
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
// Copyright (C) 2012-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file lua_mobjlib.c
/// \brief mobj/thing library for Lua scripting
#include "doomdef.h"
#ifdef HAVE_BLUA
#include "fastcmp.h"
#include "r_things.h"
#include "p_local.h"
#include "g_game.h"
#include "p_setup.h"
#include "lua_script.h"
#include "lua_libs.h"
#include "lua_hud.h" // hud_running errors

Latapostrophe
committed
#include "lua_hook.h" // cmd errors
static const char *const array_opt[] ={"iterate",NULL};
enum mobj_e {
mobj_valid = 0,
mobj_x,
mobj_y,
mobj_z,
mobj_snext,
mobj_sprev,
mobj_angle,
mobj_sprite,
mobj_frame,
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
mobj_touching_sectorlist,
mobj_subsector,
mobj_floorz,
mobj_ceilingz,
mobj_radius,
mobj_height,
mobj_momx,
mobj_momy,
mobj_momz,
mobj_pmomz,
mobj_tics,
mobj_state,
mobj_flags,
mobj_flags2,
mobj_eflags,
mobj_skin,
mobj_color,
mobj_bnext,
mobj_bprev,
mobj_hnext,
mobj_hprev,
mobj_type,
mobj_info,
mobj_health,
mobj_movedir,
mobj_movecount,
mobj_target,
mobj_reactiontime,
mobj_threshold,
mobj_player,
mobj_lastlook,
mobj_spawnpoint,
mobj_tracer,
mobj_friction,
mobj_movefactor,
mobj_fuse,
mobj_watertop,
mobj_waterbottom,
mobj_mobjnum,
mobj_scale,
mobj_destscale,
mobj_scalespeed,
mobj_extravalue1,
mobj_extravalue2,
mobj_cusval,
};
static const char *const mobj_opt[] = {
"valid",
"x",
"y",
"z",
"snext",
"sprev",
"angle",
"sprite",
"frame",
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
"touching_sectorlist",
"subsector",
"floorz",
"ceilingz",
"radius",
"height",
"momx",
"momy",
"momz",
"pmomz",
"tics",
"state",
"flags",
"flags2",
"eflags",
"skin",
"color",
"bnext",
"bprev",
"hnext",
"hprev",
"type",
"info",
"health",
"movedir",
"movecount",
"target",
"reactiontime",
"threshold",
"player",
"lastlook",
"spawnpoint",
"tracer",
"friction",
"movefactor",
"fuse",
"watertop",
"waterbottom",
"mobjnum",
"scale",
"destscale",
"scalespeed",
"extravalue1",
"extravalue2",
"cusval",
"cvmem",
NULL};
#define UNIMPLEMENTED luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", mobj_opt[field])
static int mobj_get(lua_State *L)
{
mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
enum mobj_e field = Lua_optoption(L, 2, NULL, mobj_opt);
lua_settop(L, 2);
if (!mo) {
if (field == mobj_valid) {
lua_pushboolean(L, 0);
return 1;
}
return LUA_ErrInvalid(L, "mobj_t");
}
switch(field)
{
case mobj_valid:
lua_pushboolean(L, 1);
break;
case mobj_x:
break;
case mobj_snext:
LUA_PushUserdata(L, mo->snext, META_MOBJ);
break;
case mobj_sprev:
// sprev is actually the previous mobj's snext pointer,
// or the subsector->sector->thing_list if there is no previous mobj,
// i.e. it will always ultimately point to THIS mobj -- so that's actually not useful to Lua and won't be included.
return UNIMPLEMENTED;
case mobj_angle:
break;
case mobj_sprite:
lua_pushinteger(L, mo->sprite);
break;
case mobj_frame:
lua_pushinteger(L, mo->frame);
break;
case mobj_anim_duration:
lua_pushinteger(L, mo->anim_duration);
break;
case mobj_touching_sectorlist:
return UNIMPLEMENTED;
case mobj_subsector:
LUA_PushUserdata(L, mo->subsector, META_SUBSECTOR);
break;
case mobj_floorz:
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
break;
case mobj_tics:
lua_pushinteger(L, mo->tics);
break;
case mobj_state: // state number, not struct
lua_pushinteger(L, mo->state-states);
break;
case mobj_flags:
lua_pushinteger(L, mo->flags);
break;
case mobj_flags2:
lua_pushinteger(L, mo->flags2);
break;
case mobj_eflags:
lua_pushinteger(L, mo->eflags);
break;
case mobj_skin: // skin name or nil, not struct
if (!mo->skin)
return 0;
lua_pushstring(L, ((skin_t *)mo->skin)->name);
break;
case mobj_color:
lua_pushinteger(L, mo->color);
break;
case mobj_bnext:
LUA_PushUserdata(L, mo->bnext, META_MOBJ);
break;
case mobj_bprev:
// bprev -- same deal as sprev above, but for the blockmap.
return UNIMPLEMENTED;
case mobj_hnext:
LUA_PushUserdata(L, mo->hnext, META_MOBJ);
break;
case mobj_hprev:
// implimented differently from sprev and bprev because SSNTails.
LUA_PushUserdata(L, mo->hprev, META_MOBJ);
break;
case mobj_type:
lua_pushinteger(L, mo->type);
break;
case mobj_info:
LUA_PushUserdata(L, &mobjinfo[mo->type], META_MOBJINFO);
break;
case mobj_health:
lua_pushinteger(L, mo->health);
break;
case mobj_movedir:
lua_pushinteger(L, mo->movedir);
break;
case mobj_movecount:
lua_pushinteger(L, mo->movecount);
break;
case mobj_target:
if (mo->target && P_MobjWasRemoved(mo->target))
{ // don't put invalid mobj back into Lua.
P_SetTarget(&mo->target, NULL);
return 0;
}
LUA_PushUserdata(L, mo->target, META_MOBJ);
break;
case mobj_reactiontime:
lua_pushinteger(L, mo->reactiontime);
break;
case mobj_threshold:
lua_pushinteger(L, mo->threshold);
break;
case mobj_player:
LUA_PushUserdata(L, mo->player, META_PLAYER);
break;
case mobj_lastlook:
lua_pushinteger(L, mo->lastlook);
break;
case mobj_spawnpoint:
LUA_PushUserdata(L, mo->spawnpoint, META_MAPTHING);
break;
case mobj_tracer:
if (mo->tracer && P_MobjWasRemoved(mo->tracer))
{ // don't put invalid mobj back into Lua.
P_SetTarget(&mo->tracer, NULL);
return 0;
}
LUA_PushUserdata(L, mo->tracer, META_MOBJ);
break;
case mobj_friction:
break;
case mobj_fuse:
lua_pushinteger(L, mo->fuse);
break;
case mobj_watertop:
break;
case mobj_mobjnum:
// mobjnum is a networking thing generated for $$$.sav
// and therefore shouldn't be used by Lua.
return UNIMPLEMENTED;
case mobj_scale:
break;
case mobj_extravalue1:
lua_pushinteger(L, mo->extravalue1);
break;
case mobj_extravalue2:
lua_pushinteger(L, mo->extravalue2);
break;
case mobj_cusval:
lua_pushinteger(L, mo->cusval);
break;
case mobj_cvmem:
lua_pushinteger(L, mo->cvmem);
break;
case mobj_standingslope:
LUA_PushUserdata(L, mo->standingslope, META_SLOPE);
break;
case mobj_colorized:
lua_pushboolean(L, mo->colorized);
break;
default: // extra custom variables in Lua memory
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
I_Assert(lua_istable(L, -1));
lua_pushlightuserdata(L, mo);
lua_rawget(L, -2);
if (!lua_istable(L, -1)) { // no extra values table
CONS_Debug(DBG_LUA, M_GetText("'%s' has no extvars table or field named '%s'; returning nil.\n"), "mobj_t", lua_tostring(L, 2));
return 0;
}
lua_pushvalue(L, 2); // field name
lua_gettable(L, -2);
if (lua_isnil(L, -1)) // no value for this field
CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "mobj_t", lua_tostring(L, 2));
break;
}
return 1;
}
#define NOSET luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " should not be set directly.", mobj_opt[field])
#define NOSETPOS luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " should not be set directly. Use " LUA_QL("P_Move") ", " LUA_QL("P_TryMove") ", or " LUA_QL("P_SetOrigin") ", or " LUA_QL("P_MoveOrigin") " instead.", mobj_opt[field])
static int mobj_set(lua_State *L)
{
mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
enum mobj_e field = Lua_optoption(L, 2, mobj_opt[0], mobj_opt);
lua_settop(L, 3);
if (!mo)
return LUA_ErrInvalid(L, "mobj_t");
if (hud_running)
return luaL_error(L, "Do not alter mobj_t in HUD rendering code!");

Latapostrophe
committed
if (hook_cmd_running)
return luaL_error(L, "Do not alter mobj_t in BuildCMD code!");
switch(field)
{
case mobj_valid:
return NOSET;
case mobj_x:
return NOSETPOS;
case mobj_y:
return NOSETPOS;
case mobj_z:
{
// z doesn't cross sector bounds so it's okay.
mobj_t *ptmthing = tmthing;
P_CheckPosition(mo, mo->x, mo->y);
mo->floorz = tmfloorz;
mo->ceilingz = tmceilingz;
P_SetTarget(&tmthing, ptmthing);
break;
}
case mobj_snext:
return NOSETPOS;
case mobj_sprev:
return UNIMPLEMENTED;
case mobj_angle:
localangle[0] = mo->angle;
else if (mo->player == &players[displayplayers[1]])
localangle[1] = mo->angle;
else if (mo->player == &players[displayplayers[2]])
localangle[2] = mo->angle;
else if (mo->player == &players[displayplayers[3]])
localangle[3] = mo->angle;
break;
case mobj_sprite:
mo->sprite = luaL_checkinteger(L, 3);
break;
case mobj_frame:
mo->frame = (UINT32)luaL_checkinteger(L, 3);
break;
case mobj_anim_duration:
mo->anim_duration = (UINT16)luaL_checkinteger(L, 3);
break;
case mobj_touching_sectorlist:
return UNIMPLEMENTED;
case mobj_subsector:
return NOSETPOS;
case mobj_floorz:
return NOSETPOS;
case mobj_ceilingz:
return NOSETPOS;
case mobj_radius:
{
mobj_t *ptmthing = tmthing;
if (mo->radius < 0)
mo->radius = 0;
P_CheckPosition(mo, mo->x, mo->y);
mo->floorz = tmfloorz;
mo->ceilingz = tmceilingz;
P_SetTarget(&tmthing, ptmthing);
break;
}
case mobj_height:
{
mobj_t *ptmthing = tmthing;
if (mo->height < 0)
mo->height = 0;
P_CheckPosition(mo, mo->x, mo->y);
mo->floorz = tmfloorz;
mo->ceilingz = tmceilingz;
P_SetTarget(&tmthing, ptmthing);
break;
}
case mobj_momx:
mo->pmomz = luaL_checkfixed(L, 3);
mo->eflags |= MFE_APPLYPMOMZ;
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
break;
case mobj_tics:
mo->tics = luaL_checkinteger(L, 3);
break;
case mobj_state: // set state by enum
if (mo->player)
P_SetPlayerMobjState(mo, luaL_checkinteger(L, 3));
else
P_SetMobjState(mo, luaL_checkinteger(L, 3));
break;
case mobj_flags: // special handling for MF_NOBLOCKMAP and MF_NOSECTOR
{
UINT32 flags = luaL_checkinteger(L, 3);
if ((flags & (MF_NOBLOCKMAP|MF_NOSECTOR)) != (mo->flags & (MF_NOBLOCKMAP|MF_NOSECTOR)))
{
P_UnsetThingPosition(mo);
mo->flags = flags;
if (flags & MF_NOSECTOR && sector_list)
{
P_DelSeclist(sector_list);
sector_list = NULL;
}
mo->snext = NULL, mo->sprev = NULL;
mo->bnext = NULL, mo->bprev = NULL;
P_SetThingPosition(mo);
}
else
mo->flags = flags;
break;
}
case mobj_flags2:
mo->flags2 = (UINT32)luaL_checkinteger(L, 3);
break;
case mobj_eflags:
mo->eflags = (UINT32)luaL_checkinteger(L, 3);
break;
case mobj_skin: // set skin by name
{
INT32 i;
char skin[SKINNAMESIZE+1]; // all skin names are limited to this length
strlcpy(skin, luaL_checkstring(L, 3), sizeof skin);
strlwr(skin); // all skin names are lowercase
for (i = 0; i < numskins; i++)
if (fastcmp(skins[i].name, skin))
{
mo->skin = &skins[i];
return 0;
}
return luaL_error(L, "mobj.skin '%s' not found!", skin);
}
case mobj_color:
{
UINT8 newcolor = (UINT8)luaL_checkinteger(L,3);
if (newcolor >= MAXTRANSLATIONS)
return luaL_error(L, "mobj.color %d out of range (0 - %d).", newcolor, MAXTRANSLATIONS-1);
mo->color = newcolor;
case mobj_bnext:
return NOSETPOS;
case mobj_bprev:
return UNIMPLEMENTED;
case mobj_hnext:
if (lua_isnil(L, 3))
P_SetTarget(&mo->hnext, NULL);
else
{
mobj_t *hnext = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
P_SetTarget(&mo->hnext, hnext);
}
if (lua_isnil(L, 3))
P_SetTarget(&mo->hprev, NULL);
else
{
mobj_t *hprev = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
P_SetTarget(&mo->hprev, hprev);
}
break;
case mobj_type: // yeah sure, we'll let you change the mobj's type.
{
mobjtype_t newtype = luaL_checkinteger(L, 3);
if (newtype >= NUMMOBJTYPES)
return luaL_error(L, "mobj.type %d out of range (0 - %d).", newtype, NUMMOBJTYPES-1);
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
mo->type = newtype;
mo->info = &mobjinfo[newtype];
P_SetScale(mo, mo->scale);
break;
}
case mobj_info:
return NOSET;
case mobj_health:
mo->health = luaL_checkinteger(L, 3);
break;
case mobj_movedir:
mo->movedir = (angle_t)luaL_checkinteger(L, 3);
break;
case mobj_movecount:
mo->movecount = luaL_checkinteger(L, 3);
break;
case mobj_target:
if (lua_isnil(L, 3))
P_SetTarget(&mo->target, NULL);
else
{
mobj_t *target = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
P_SetTarget(&mo->target, target);
}
break;
case mobj_reactiontime:
mo->reactiontime = luaL_checkinteger(L, 3);
break;
case mobj_threshold:
mo->threshold = luaL_checkinteger(L, 3);
break;
case mobj_player:
return NOSET;
case mobj_lastlook:
mo->lastlook = luaL_checkinteger(L, 3);
break;
case mobj_spawnpoint:
if (lua_isnil(L, 3))
mo->spawnpoint = NULL;
else
{
mapthing_t *spawnpoint = *((mapthing_t **)luaL_checkudata(L, 3, META_MAPTHING));
mo->spawnpoint = spawnpoint;
}
break;
case mobj_tracer:
if (lua_isnil(L, 3))
P_SetTarget(&mo->tracer, NULL);
else
{
mobj_t *tracer = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
P_SetTarget(&mo->tracer, tracer);
}
break;
case mobj_friction:
break;
case mobj_fuse:
mo->fuse = luaL_checkinteger(L, 3);
break;
case mobj_watertop:
break;
case mobj_mobjnum:
return UNIMPLEMENTED;
case mobj_scale:
{
if (scale < FRACUNIT/100)
scale = FRACUNIT/100;
mo->destscale = scale;
P_SetScale(mo, scale);
if (scale < FRACUNIT/100)
scale = FRACUNIT/100;
mo->destscale = scale;
break;
}
case mobj_scalespeed:
break;
case mobj_extravalue1:
mo->extravalue1 = luaL_checkinteger(L, 3);
break;
case mobj_extravalue2:
mo->extravalue2 = luaL_checkinteger(L, 3);
break;
case mobj_cusval:
mo->cusval = luaL_checkinteger(L, 3);
break;
case mobj_cvmem:
mo->cvmem = luaL_checkinteger(L, 3);
break;
case mobj_standingslope:
return NOSET;
case mobj_colorized:
mo->colorized = luaL_checkboolean(L, 3);
break;
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
default:
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
I_Assert(lua_istable(L, -1));
lua_pushlightuserdata(L, mo);
lua_rawget(L, -2);
if (lua_isnil(L, -1)) {
// This index doesn't have a table for extra values yet, let's make one.
lua_pop(L, 1);
CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; adding it as Lua data.\n"), "mobj_t", lua_tostring(L, 2));
lua_newtable(L);
lua_pushlightuserdata(L, mo);
lua_pushvalue(L, -2); // ext value table
lua_rawset(L, -4); // LREG_EXTVARS table
}
lua_pushvalue(L, 2); // key
lua_pushvalue(L, 3); // value to store
lua_settable(L, -3);
lua_pop(L, 2);
break;
}
return 0;
}
#undef UNIMPLEMENTED
#undef NOSET
#undef NOSETPOS
#undef NOFIELD
static int mapthing_get(lua_State *L)
{
mapthing_t *mt = *((mapthing_t **)luaL_checkudata(L, 1, META_MAPTHING));
const char *field = luaL_checkstring(L, 2);
lua_Integer number;
if (!mt) {
if (fastcmp(field,"valid")) {
lua_pushboolean(L, false);
return 1;
}
if (devparm)
return luaL_error(L, "accessed mapthing_t doesn't exist anymore.");
return 0;
}
if (fastcmp(field,"valid")) {
lua_pushboolean(L, true);
return 1;
} else if(fastcmp(field,"x"))
number = mt->x;
else if(fastcmp(field,"y"))
number = mt->y;
else if(fastcmp(field,"angle"))
number = mt->angle;
else if(fastcmp(field,"type"))
number = mt->type;
else if(fastcmp(field,"options"))
number = mt->options;
else if(fastcmp(field,"z"))
number = mt->z;
else if(fastcmp(field,"extrainfo"))
number = mt->extrainfo;
else if(fastcmp(field,"mobj")) {
LUA_PushUserdata(L, mt->mobj, META_MOBJ);
return 1;
} else if (devparm)
return luaL_error(L, LUA_QL("mapthing_t") " has no field named " LUA_QS, field);
else
return 0;
lua_pushinteger(L, number);
return 1;
}
static int mapthing_set(lua_State *L)
{
mapthing_t *mt = *((mapthing_t **)luaL_checkudata(L, 1, META_MAPTHING));
const char *field = luaL_checkstring(L, 2);
if (!mt)
return luaL_error(L, "accessed mapthing_t doesn't exist anymore.");
if (hud_running)
return luaL_error(L, "Do not alter mapthing_t in HUD rendering code!");

Latapostrophe
committed
if (hook_cmd_running)
return luaL_error(L, "Do not alter mapthing_t in BuildCMD code!");
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
if(fastcmp(field,"x"))
mt->x = (INT16)luaL_checkinteger(L, 3);
else if(fastcmp(field,"y"))
mt->y = (INT16)luaL_checkinteger(L, 3);
else if(fastcmp(field,"angle"))
mt->angle = (INT16)luaL_checkinteger(L, 3);
else if(fastcmp(field,"type"))
mt->type = (UINT16)luaL_checkinteger(L, 3);
else if(fastcmp(field,"options"))
mt->options = (UINT16)luaL_checkinteger(L, 3);
else if(fastcmp(field,"z"))
mt->z = (INT16)luaL_checkinteger(L, 3);
else if(fastcmp(field,"extrainfo"))
mt->extrainfo = (UINT8)luaL_checkinteger(L, 3);
else if(fastcmp(field,"mobj"))
mt->mobj = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
else
return luaL_error(L, LUA_QL("mapthing_t") " has no field named " LUA_QS, field);
return 0;
}
static int lib_iterateMapthings(lua_State *L)
{
size_t i = 0;
if (lua_gettop(L) < 2)
return luaL_error(L, "Don't call mapthings.iterate() directly, use it as 'for mapthing in mapthings.iterate do <block> end'.");
lua_settop(L, 2);
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
lua_remove(L, 1); // state is unused.
if (!lua_isnil(L, 1))
i = (size_t)(*((mapthing_t **)luaL_checkudata(L, 1, META_MAPTHING)) - mapthings) + 1;
if (i < nummapthings)
{
LUA_PushUserdata(L, &mapthings[i], META_MAPTHING);
return 1;
}
return 0;
}
static int lib_getMapthing(lua_State *L)
{
int field;
lua_settop(L, 2);
lua_remove(L, 1); // dummy userdata table is unused.
if (lua_isnumber(L, 1))
{
size_t i = lua_tointeger(L, 1);
if (i >= nummapthings)
return 0;
LUA_PushUserdata(L, &mapthings[i], META_MAPTHING);
return 1;
}
field = luaL_checkoption(L, 1, NULL, array_opt);
switch(field)
{
case 0: // iterate
lua_pushcfunction(L, lib_iterateMapthings);
return 1;
}
return 0;
}
static int lib_nummapthings(lua_State *L)
{
lua_pushinteger(L, nummapthings);
return 1;
}
int LUA_MobjLib(lua_State *L)
{
luaL_newmetatable(L, META_MOBJ);
lua_pushcfunction(L, mobj_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, mobj_set);
lua_setfield(L, -2, "__newindex");
lua_pop(L,1);
luaL_newmetatable(L, META_MAPTHING);
lua_pushcfunction(L, mapthing_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, mapthing_set);
lua_setfield(L, -2, "__newindex");
lua_pop(L,1);
lua_newuserdata(L, 0);
lua_createtable(L, 0, 2);
lua_pushcfunction(L, lib_getMapthing);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, lib_nummapthings);
lua_setfield(L, -2, "__len");
lua_setmetatable(L, -2);
lua_setglobal(L, "mapthings");
return 0;
}
#endif