diff --git a/SRB2.cbp b/SRB2.cbp
index 5aa623fa8..2a1eb87b8 100644
--- a/SRB2.cbp
+++ b/SRB2.cbp
@@ -1549,6 +1549,9 @@ HW3SOUND for 3D hardware sound support
+
+
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 99f2beebe..4a9ef5ba8 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -237,6 +237,7 @@ if(${SRB2_CONFIG_HAVE_BLUA})
add_definitions(-DHAVE_BLUA)
set(SRB2_LUA_SOURCES
lua_baselib.c
+ lua_blockmaplib.c
lua_consolelib.c
lua_hooklib.c
lua_hudlib.c
diff --git a/src/blua/Makefile.cfg b/src/blua/Makefile.cfg
index e3fb3df46..b1131eaca 100644
--- a/src/blua/Makefile.cfg
+++ b/src/blua/Makefile.cfg
@@ -39,6 +39,7 @@ OBJS:=$(OBJS) \
$(OBJDIR)/lvm.o \
$(OBJDIR)/lua_script.o \
$(OBJDIR)/lua_baselib.o \
+ $(OBJDIR)/lua_blockmaplib.o \
$(OBJDIR)/lua_mathlib.o \
$(OBJDIR)/lua_hooklib.o \
$(OBJDIR)/lua_consolelib.o \
diff --git a/src/lua_blockmaplib.c b/src/lua_blockmaplib.c
new file mode 100644
index 000000000..dabbdd9f6
--- /dev/null
+++ b/src/lua_blockmaplib.c
@@ -0,0 +1,266 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 2016 by Iestyn "Monster Iestyn" Jealous.
+// Copyright (C) 2016 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_blockmaplib.c
+/// \brief blockmap library for Lua scripting
+
+#include "doomdef.h"
+#ifdef HAVE_BLUA
+#include "p_local.h"
+#include "r_main.h" // validcount
+#include "lua_script.h"
+#include "lua_libs.h"
+//#include "lua_hud.h" // hud_running errors
+
+static const char *const search_opt[] = {
+ "objects",
+ "lines",
+ NULL};
+
+// a quickly-made function pointer typedef used by lib_searchBlockmap...
+// return values:
+// 0 - normal, no interruptions
+// 1 - stop search through current block
+// 2 - stop search completely
+typedef UINT8 (*blockmap_func)(lua_State *, INT32, INT32, mobj_t *);
+
+static boolean blockfuncerror = false; // errors should only print once per search blockmap call
+
+// Helper function for "objects" search
+static UINT8 lib_searchBlockmap_Objects(lua_State *L, INT32 x, INT32 y, mobj_t *thing)
+{
+ mobj_t *mobj, *bnext = NULL;
+
+ if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
+ return 0;
+
+ // Check interaction with the objects in the blockmap.
+ for (mobj = blocklinks[y*bmapwidth + x]; mobj; mobj = bnext)
+ {
+ P_SetTarget(&bnext, mobj->bnext); // We want to note our reference to bnext here incase it is MF_NOTHINK and gets removed!
+ if (mobj == thing)
+ continue; // our thing just found itself, so move on
+ lua_pushvalue(L, 1); // push function
+ LUA_PushUserdata(L, thing, META_MOBJ);
+ LUA_PushUserdata(L, mobj, META_MOBJ);
+ if (lua_pcall(gL, 2, 1, 0)) {
+ if (!blockfuncerror || cv_debug & DBG_LUA)
+ CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+ lua_pop(gL, 1);
+ blockfuncerror = true;
+ return 0; // *shrugs*
+ }
+ if (!lua_isnil(gL, -1))
+ { // if nil, continue
+ if (lua_toboolean(gL, -1))
+ return 2; // stop whole search
+ else
+ return 1; // stop block search
+ }
+ lua_pop(gL, 1);
+ if (P_MobjWasRemoved(thing) // func just popped our thing, cannot continue.
+ || (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue.
+ {
+ P_SetTarget(&bnext, NULL);
+ return (P_MobjWasRemoved(thing)) ? 2 : 1;
+ }
+ }
+ return 0;
+}
+
+// Helper function for "lines" search
+static UINT8 lib_searchBlockmap_Lines(lua_State *L, INT32 x, INT32 y, mobj_t *thing)
+{
+ INT32 offset;
+ const INT32 *list; // Big blockmap
+#ifdef POLYOBJECTS
+ polymaplink_t *plink; // haleyjd 02/22/06
+#endif
+ line_t *ld;
+
+ if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
+ return 0;
+
+ offset = y*bmapwidth + x;
+
+#ifdef POLYOBJECTS
+ // haleyjd 02/22/06: consider polyobject lines
+ plink = polyblocklinks[offset];
+
+ while (plink)
+ {
+ polyobj_t *po = plink->po;
+
+ if (po->validcount != validcount) // if polyobj hasn't been checked
+ {
+ size_t i;
+ po->validcount = validcount;
+
+ for (i = 0; i < po->numLines; ++i)
+ {
+ if (po->lines[i]->validcount == validcount) // line has been checked
+ continue;
+ po->lines[i]->validcount = validcount;
+
+ lua_pushvalue(L, 1);
+ LUA_PushUserdata(L, thing, META_MOBJ);
+ LUA_PushUserdata(L, po->lines[i], META_LINE);
+ if (lua_pcall(gL, 2, 1, 0)) {
+ if (!blockfuncerror || cv_debug & DBG_LUA)
+ CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+ lua_pop(gL, 1);
+ blockfuncerror = true;
+ return 0; // *shrugs*
+ }
+ if (!lua_isnil(gL, -1))
+ { // if nil, continue
+ if (lua_toboolean(gL, -1))
+ return 2; // stop whole search
+ else
+ return 1; // stop block search
+ }
+ lua_pop(gL, 1);
+ if (P_MobjWasRemoved(thing))
+ return 2;
+ }
+ }
+ plink = (polymaplink_t *)(plink->link.next);
+ }
+#endif
+
+ offset = *(blockmap + offset); // offset = blockmap[y*bmapwidth+x];
+
+ // First index is really empty, so +1 it.
+ for (list = blockmaplump + offset + 1; *list != -1; list++)
+ {
+ ld = &lines[*list];
+
+ if (ld->validcount == validcount)
+ continue; // Line has already been checked.
+
+ ld->validcount = validcount;
+
+ lua_pushvalue(L, 1);
+ LUA_PushUserdata(L, thing, META_MOBJ);
+ LUA_PushUserdata(L, ld, META_LINE);
+ if (lua_pcall(gL, 2, 1, 0)) {
+ if (!blockfuncerror || cv_debug & DBG_LUA)
+ CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+ lua_pop(gL, 1);
+ blockfuncerror = true;
+ return 0; // *shrugs*
+ }
+ if (!lua_isnil(gL, -1))
+ { // if nil, continue
+ if (lua_toboolean(gL, -1))
+ return 2; // stop whole search
+ else
+ return 1; // stop block search
+ }
+ lua_pop(gL, 1);
+ if (P_MobjWasRemoved(thing))
+ return 2;
+ }
+ return 0; // Everything was checked.
+}
+
+// The searchBlockmap function
+// arguments: searchBlockmap(searchtype, function, mobj, [x1, x2, y1, y2])
+// return value:
+// true = search completely uninteruppted,
+// false = searching of at least one block stopped mid-way (including if the whole search was stopped)
+static int lib_searchBlockmap(lua_State *L)
+{
+ int searchtype = luaL_checkoption(L, 1, "objects", search_opt);
+ int n;
+ mobj_t *mobj;
+ INT32 xl, xh, yl, yh, bx, by;
+ fixed_t x1, x2, y1, y2;
+ boolean retval = true;
+ UINT8 funcret = 0;
+ blockmap_func searchFunc;
+
+ lua_remove(L, 1); // remove searchtype, stack is now function, mobj, [x1, x2, y1, y2]
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+
+ switch (searchtype)
+ {
+ case 0: // "objects"
+ default:
+ searchFunc = lib_searchBlockmap_Objects;
+ break;
+ case 1: // "lines"
+ searchFunc = lib_searchBlockmap_Lines;
+ break;
+ }
+
+ // the mobj we are searching around, the "calling" mobj we could say
+ mobj = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
+ if (!mobj)
+ return LUA_ErrInvalid(L, "mobj_t");
+
+ n = lua_gettop(L);
+
+ if (n > 2) // specific x/y ranges have been supplied
+ {
+ if (n < 6)
+ return luaL_error(L, "arguments 4 to 6 not all given (expected 4 fixed-point integers)");
+
+ x1 = luaL_checkfixed(L, 3);
+ x2 = luaL_checkfixed(L, 4);
+ y1 = luaL_checkfixed(L, 5);
+ y2 = luaL_checkfixed(L, 6);
+ }
+ else // mobj and function only - search around mobj's radius by default
+ {
+ fixed_t radius = mobj->radius + MAXRADIUS;
+ x1 = mobj->x - radius;
+ x2 = mobj->x + radius;
+ y1 = mobj->y - radius;
+ y2 = mobj->y + radius;
+ }
+ lua_settop(L, 2); // pop everything except function, mobj
+
+ xl = (unsigned)(x1 - bmaporgx)>>MAPBLOCKSHIFT;
+ xh = (unsigned)(x2 - bmaporgx)>>MAPBLOCKSHIFT;
+ yl = (unsigned)(y1 - bmaporgy)>>MAPBLOCKSHIFT;
+ yh = (unsigned)(y2 - bmaporgy)>>MAPBLOCKSHIFT;
+
+ BMBOUNDFIX(xl, xh, yl, yh);
+
+ blockfuncerror = false; // reset
+ validcount++;
+ for (bx = xl; bx <= xh; bx++)
+ for (by = yl; by <= yh; by++)
+ {
+ funcret = searchFunc(L, bx, by, mobj);
+ // return value of searchFunc determines searchFunc's return value and/or when to stop
+ if (funcret == 2){ // stop whole search
+ lua_pushboolean(L, false); // return false
+ return 1;
+ }
+ else if (funcret == 1) // search was interrupted for this block
+ retval = false; // this changes the return value, but doesn't stop the whole search
+ // else don't do anything, continue as normal
+ if (P_MobjWasRemoved(mobj)){ // ...unless the original object was removed
+ lua_pushboolean(L, false); // in which case we have to stop now regardless
+ return 1;
+ }
+ }
+ lua_pushboolean(L, retval);
+ return 1;
+}
+
+int LUA_BlockmapLib(lua_State *L)
+{
+ lua_register(L, "searchBlockmap", lib_searchBlockmap);
+ return 0;
+}
+
+#endif
diff --git a/src/lua_libs.h b/src/lua_libs.h
index 1817dc456..9c6050bea 100644
--- a/src/lua_libs.h
+++ b/src/lua_libs.h
@@ -69,6 +69,7 @@ int LUA_PlayerLib(lua_State *L);
int LUA_SkinLib(lua_State *L);
int LUA_ThinkerLib(lua_State *L);
int LUA_MapLib(lua_State *L);
+int LUA_BlockmapLib(lua_State *L);
int LUA_HudLib(lua_State *L);
#endif
diff --git a/src/lua_script.c b/src/lua_script.c
index a079f57f7..edf8ac3c5 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -51,6 +51,7 @@ static lua_CFunction liblist[] = {
LUA_SkinLib, // skin_t, skins[]
LUA_ThinkerLib, // thinker_t
LUA_MapLib, // line_t, side_t, sector_t, subsector_t
+ LUA_BlockmapLib, // blockmap stuff
LUA_HudLib, // HUD stuff
NULL
};
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj
index 467d2829b..3e4a9ebb1 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj
+++ b/src/sdl/Srb2SDL-vc10.vcxproj
@@ -297,6 +297,7 @@
+
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters
index 364deb497..ac0b03177 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj.filters
+++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters
@@ -639,6 +639,9 @@
LUA
+
+ LUA
+
LUA
diff --git a/src/win32/Srb2win-vc10.vcxproj b/src/win32/Srb2win-vc10.vcxproj
index 0722c0b68..5bfa29b8c 100644
--- a/src/win32/Srb2win-vc10.vcxproj
+++ b/src/win32/Srb2win-vc10.vcxproj
@@ -132,6 +132,7 @@
+
diff --git a/src/win32/Srb2win-vc10.vcxproj.filters b/src/win32/Srb2win-vc10.vcxproj.filters
index 95e79cab1..d20dd672b 100644
--- a/src/win32/Srb2win-vc10.vcxproj.filters
+++ b/src/win32/Srb2win-vc10.vcxproj.filters
@@ -228,6 +228,9 @@
LUA
+
+ LUA
+
LUA