From 883e162f7f07fd49f1ddeb641965e52d74096162 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Mon, 17 Oct 2016 04:18:02 +1100 Subject: [PATCH] Partitioned Tortuga's map system into a library --- .gitignore | 24 +++++ README.md | 18 +++- makefile | 31 +++++++ region.cpp | 82 +++++++++++++++++ region.hpp | 62 +++++++++++++ region_api.cpp | 99 ++++++++++++++++++++ region_api.hpp | 27 ++++++ region_pager_api.cpp | 172 ++++++++++++++++++++++++++++++++++ region_pager_api.hpp | 27 ++++++ region_pager_base.cpp | 106 +++++++++++++++++++++ region_pager_base.hpp | 58 ++++++++++++ region_pager_lua.cpp | 208 ++++++++++++++++++++++++++++++++++++++++++ region_pager_lua.hpp | 68 ++++++++++++++ tile_sheet.cpp | 124 +++++++++++++++++++++++++ tile_sheet.hpp | 67 ++++++++++++++ 15 files changed, 1171 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 makefile create mode 100644 region.cpp create mode 100644 region.hpp create mode 100644 region_api.cpp create mode 100644 region_api.hpp create mode 100644 region_pager_api.cpp create mode 100644 region_pager_api.hpp create mode 100644 region_pager_base.cpp create mode 100644 region_pager_base.hpp create mode 100644 region_pager_lua.cpp create mode 100644 region_pager_lua.hpp create mode 100644 tile_sheet.cpp create mode 100644 tile_sheet.hpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..497ac38 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +#Editor generated files +*.sln +*.vcproj +*.suo +*.ncb +*.user + +#Directories +Release/ +Debug/ +Out/ +release/ +debug/ +out/ + +#Project generated files +*.db +*.o +*.a +*.exe + +#Shell files +*.bat +*.sh diff --git a/README.md b/README.md index 2afac7f..3baf0e7 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,16 @@ -# TurtleMap -A generic lua-backed C++ map system +## TurtleMap +A generic lua-backed C++ tiled map system. Formerly part of [Tortuga](https://github.com/Ratstail91/Tortuga). + +TurtleMap requires lua 5.3 or later and [TurtleGUI](https://github.com/krgamestudios/TurtleGUI). + +## Copyright + +Copyright (c) 2013-2016 Kayne Ruse + +This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. diff --git a/makefile b/makefile new file mode 100644 index 0000000..c77f161 --- /dev/null +++ b/makefile @@ -0,0 +1,31 @@ +#config +INCLUDES+=. ../TurtleGUI +LIBS+= +CXXFLAGS+=-std=c++11 $(addprefix -I,$(INCLUDES)) + +#source +CXXSRC=$(wildcard *.cpp) + +#objects +OBJDIR=obj +OBJ+=$(addprefix $(OBJDIR)/,$(CXXSRC:.cpp=.o)) + +OUTDIR=.. +OUT=$(addprefix $(OUTDIR)/,libturtlemap.a) + +#targets +all: $(OBJ) $(OUT) + ar -crs $(OUT) $(OBJ) + +$(OBJ): | $(OBJDIR) + +$(OUT): | $(OUTDIR) + +$(OBJDIR): + mkdir $(OBJDIR) + +$(OUTDIR): + mkdir $(OUTDIR) + +$(OBJDIR)/%.o: %.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< diff --git a/region.cpp b/region.cpp new file mode 100644 index 0000000..0a9631e --- /dev/null +++ b/region.cpp @@ -0,0 +1,82 @@ +/* Copyright: (c) Kayne Ruse 2013-2016 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. +*/ +#include "region.hpp" + +#include +#include +#include + +int snapToBase(int base, int x) { + return floor((double)x / base) * base; +} + +Region::Region(int argX, int argY): x(argX), y(argY) { + if (x != snapToBase(REGION_WIDTH, x) || y != snapToBase(REGION_HEIGHT, y)) { + throw(std::invalid_argument("Region location is off grid")); + } + memset(tiles, 0, REGION_WIDTH*REGION_HEIGHT*REGION_DEPTH*sizeof(type_t)); +} + +Region::Region(Region const& rhs): x(rhs.x), y(rhs.y) { + memcpy(tiles, rhs.tiles, REGION_WIDTH*REGION_HEIGHT*REGION_DEPTH*sizeof(type_t)); + solid = rhs.solid; +} + +Region::type_t Region::SetTile(int x, int y, int z, type_t v) { + if (x < 0 || y < 0 || z < 0 || x >= REGION_WIDTH || y >= REGION_HEIGHT || z >= REGION_DEPTH) { + throw(std::out_of_range("Region::SetTile() argument out of range")); + } + return tiles[x][y][z] = v; +} + +Region::type_t Region::GetTile(int x, int y, int z) const { + if (x < 0 || y < 0 || z < 0 || x >= REGION_WIDTH || y >= REGION_HEIGHT || z >= REGION_DEPTH) { + throw(std::out_of_range("Region::GetTile() argument out of range")); + } + return tiles[x][y][z]; +} + +bool Region::SetSolid(int x, int y, bool b) { + if (x < 0 || y < 0 || x >= REGION_WIDTH || y >= REGION_HEIGHT) { + throw(std::out_of_range("Region::SetSolid() argument out of range")); + } + return solid[x * REGION_WIDTH + y] = b; +} + +bool Region::GetSolid(int x, int y) const { + if (x < 0 || y < 0 || x >= REGION_WIDTH || y >= REGION_HEIGHT) { + throw(std::out_of_range("Region::GetSolid() argument out of range")); + } + return solid[x * REGION_WIDTH + y]; +} + +int Region::GetX() const { + return x; +} + +int Region::GetY() const { + return y; +} + +std::bitset* Region::GetSolidBitset() { + return &solid; +} \ No newline at end of file diff --git a/region.hpp b/region.hpp new file mode 100644 index 0000000..a454a24 --- /dev/null +++ b/region.hpp @@ -0,0 +1,62 @@ +/* Copyright: (c) Kayne Ruse 2013-2016 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. +*/ +#pragma once + +#include + +//the region's storage format +constexpr int REGION_WIDTH = 20; +constexpr int REGION_HEIGHT = 20; +constexpr int REGION_DEPTH = 3; + +//utility function +int snapToBase(int base, int x); + +class Region { +public: + typedef unsigned char type_t; + + Region() = delete; + Region(int x, int y); + Region(Region const&); + ~Region() = default; + + type_t SetTile(int x, int y, int z, type_t v); + type_t GetTile(int x, int y, int z) const; + + bool SetSolid(int x, int y, bool b); + bool GetSolid(int x, int y) const; + + //accessors + int GetX() const; + int GetY() const; + + std::bitset* GetSolidBitset(); +private: + friend class TileSheet; + + const int x; + const int y; + + type_t tiles[REGION_WIDTH][REGION_HEIGHT][REGION_DEPTH]; + std::bitset solid; +}; diff --git a/region_api.cpp b/region_api.cpp new file mode 100644 index 0000000..4fa49c9 --- /dev/null +++ b/region_api.cpp @@ -0,0 +1,99 @@ +/* Copyright: (c) Kayne Ruse 2013-2016 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. +*/ +#include "region_api.hpp" + +#include "region.hpp" + +static int setTile(lua_State* L) { + Region* region = reinterpret_cast(lua_touserdata(L, 1)); + int ret = region->SetTile(lua_tointeger(L, 2)-1, lua_tointeger(L, 3)-1, lua_tointeger(L, 4)-1, lua_tointeger(L, 5)); + lua_pushinteger(L, ret); + return 1; +} + +static int getTile(lua_State* L) { + Region* region = reinterpret_cast(lua_touserdata(L, 1)); + int ret = region->GetTile(lua_tointeger(L, 2)-1, lua_tointeger(L, 3)-1, lua_tointeger(L, 4)-1); + lua_pushinteger(L, ret); + return 1; +} + +static int setSolid(lua_State* L) { + Region* region = reinterpret_cast(lua_touserdata(L, 1)); + bool ret = region->SetSolid(lua_tointeger(L, 2)-1, lua_tointeger(L, 3)-1, lua_toboolean(L, 4)); + lua_pushboolean(L, ret); + return 1; +} + +static int getSolid(lua_State* L) { + Region* region = reinterpret_cast(lua_touserdata(L, 1)); + bool ret = region->GetSolid(lua_tointeger(L, 2)-1, lua_tointeger(L, 3)-1); + lua_pushboolean(L, ret); + return 1; +} + +static int getX(lua_State* L) { + Region* region = reinterpret_cast(lua_touserdata(L, 1)); + lua_pushinteger(L, region->GetX()); + return 1; +} + +static int getY(lua_State* L) { + Region* region = reinterpret_cast(lua_touserdata(L, 1)); + lua_pushinteger(L, region->GetY()); + return 1; +} + +static int getWidth(lua_State* L) { + lua_pushinteger(L, REGION_WIDTH); + return 1; +} + +static int getHeight(lua_State* L) { + lua_pushinteger(L, REGION_HEIGHT); + return 1; +} + +static int getDepth(lua_State* L) { + lua_pushinteger(L, REGION_DEPTH); + return 1; +} + +static const luaL_Reg regionLib[] = { + {"SetTile",setTile}, + {"GetTile",getTile}, + {"SetSolid",setSolid}, + {"GetSolid",getSolid}, + {"GetX",getX}, + {"GetY",getY}, + + //the global macros + {"GetWidth",getWidth}, + {"GetHeight",getHeight}, + {"GetDepth",getDepth}, + {nullptr, nullptr} +}; + +LUAMOD_API int openRegionAPI(lua_State* L) { + luaL_newlib(L, regionLib); + return 1; +} \ No newline at end of file diff --git a/region_api.hpp b/region_api.hpp new file mode 100644 index 0000000..7e16202 --- /dev/null +++ b/region_api.hpp @@ -0,0 +1,27 @@ +/* Copyright: (c) Kayne Ruse 2013-2016 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. +*/ +#pragma once + +#include "lua.hpp" + +#define TORTUGA_REGION_API "region" +LUAMOD_API int openRegionAPI(lua_State* L); diff --git a/region_pager_api.cpp b/region_pager_api.cpp new file mode 100644 index 0000000..1500577 --- /dev/null +++ b/region_pager_api.cpp @@ -0,0 +1,172 @@ +/* Copyright: (c) Kayne Ruse 2013-2016 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. +*/ +#include "region_pager_api.hpp" + +#include "region_pager_lua.hpp" +#include "region.hpp" + +//DOCS: These glue functions simply wrap RegionPagerLua's methods +//NOTE: zero indexing is used here, but not in the region API + +static int setTile(lua_State* L) { + RegionPagerLua* pager = reinterpret_cast(lua_touserdata(L, 1)); + int ret = pager->SetTile(lua_tointeger(L, 2), lua_tointeger(L, 3), lua_tointeger(L, 4), lua_tointeger(L, 5)); + lua_pushinteger(L, ret); + return 1; +} + +static int getTile(lua_State* L) { + RegionPagerLua* pager = reinterpret_cast(lua_touserdata(L, 1)); + int ret = pager->GetTile(lua_tointeger(L, 2), lua_tointeger(L, 3), lua_tointeger(L, 4)); + lua_pushinteger(L, ret); + return 1; +} + +static int setSolid(lua_State* L) { + RegionPagerLua* pager = reinterpret_cast(lua_touserdata(L, 1)); + bool ret = pager->SetSolid(lua_tointeger(L, 2), lua_tointeger(L, 3), lua_toboolean(L, 4)); + lua_pushboolean(L, ret); + return 1; +} + +static int getSolid(lua_State* L) { + RegionPagerLua* pager = reinterpret_cast(lua_touserdata(L, 1)); + bool ret = pager->GetSolid(lua_tointeger(L, 2), lua_tointeger(L, 3)); + lua_pushboolean(L, ret); + return 1; +} + +static int getRegion(lua_State* L) { + RegionPagerLua* pager = reinterpret_cast(lua_touserdata(L, 1)); + Region* region = pager->GetRegion(lua_tointeger(L, 2), lua_tointeger(L, 3)); + lua_pushlightuserdata(L, region); + return 1; +} + +static int loadRegion(lua_State* L) { + RegionPagerLua* pager = reinterpret_cast(lua_touserdata(L, 1)); + Region* region = pager->LoadRegion(lua_tointeger(L, 2), lua_tointeger(L, 3)); + lua_pushlightuserdata(L, region); + return 1; +} + +static int saveRegion(lua_State* L) { + RegionPagerLua* pager = reinterpret_cast(lua_touserdata(L, 1)); + Region* region = pager->SaveRegion(lua_tointeger(L, 2), lua_tointeger(L, 3)); + lua_pushlightuserdata(L, region); + return 1; +} + +static int createRegion(lua_State* L) { + RegionPagerLua* pager = reinterpret_cast(lua_touserdata(L, 1)); + Region* region = pager->CreateRegion(lua_tointeger(L, 2), lua_tointeger(L, 3)); + lua_pushlightuserdata(L, region); + return 1; +} + +static int unloadRegion(lua_State* L) { + RegionPagerLua* pager = reinterpret_cast(lua_touserdata(L, 1)); + + //two argument types: coords & the region itself + switch(lua_type(L, 2)) { + case LUA_TNUMBER: + pager->UnloadIf([&](Region const& region) -> bool { + int x = lua_tointeger(L, 2); + int y = lua_tointeger(L, 3); + return region.GetX() == x && region.GetY() == y; + }); + break; + case LUA_TLIGHTUSERDATA: + pager->UnloadIf([&](Region const& region) -> bool { + return (®ion) == lua_touserdata(L, 2); + }); + break; + } + return 0; +} + +static int setOnLoad(lua_State* L) { + RegionPagerLua* pager = reinterpret_cast(lua_touserdata(L, 1)); + luaL_unref(L, LUA_REGISTRYINDEX, pager->GetLoadReference()); + pager->SetLoadReference(luaL_ref(L, LUA_REGISTRYINDEX)); + return 0; +} + +static int setOnSave(lua_State* L) { + RegionPagerLua* pager = reinterpret_cast(lua_touserdata(L, 1)); + luaL_unref(L, LUA_REGISTRYINDEX, pager->GetSaveReference()); + pager->SetSaveReference(luaL_ref(L, LUA_REGISTRYINDEX)); + return 0; +} + +static int setOnCreate(lua_State* L) { + RegionPagerLua* pager = reinterpret_cast(lua_touserdata(L, 1)); + luaL_unref(L, LUA_REGISTRYINDEX, pager->GetCreateReference()); + pager->SetCreateReference(luaL_ref(L, LUA_REGISTRYINDEX)); + return 0; +} + +static int setOnUnload(lua_State* L) { + RegionPagerLua* pager = reinterpret_cast(lua_touserdata(L, 1)); + luaL_unref(L, LUA_REGISTRYINDEX, pager->GetUnloadReference()); + pager->SetUnloadReference(luaL_ref(L, LUA_REGISTRYINDEX)); + return 0; +} + +//debugging +static int containerSize(lua_State* L) { + RegionPagerLua* pager = static_cast(lua_touserdata(L, 1)); + lua_pushinteger(L, pager->GetContainer()->size()); + return 1; +} + +static const luaL_Reg regionPagerLib[] = { + //curry + {"SetTile", setTile}, + {"GetTile", getTile}, + {"SetSolid", setSolid}, + {"GetSolid", getSolid}, + + //access and control + {"GetRegion", getRegion}, + {"LoadRegion", loadRegion}, + {"SaveRegion", saveRegion}, + {"CreateRegion", createRegion}, + {"UnloadRegion", unloadRegion}, + + //triggers + {"SetOnLoad",setOnLoad}, + {"SetOnSave",setOnSave}, + {"SetOnCreate",setOnCreate}, + {"SetOnUnload",setOnUnload}, + + //debugging + {"ContainerSize", containerSize}, + + //sentinel + {nullptr, nullptr} +}; + +LUAMOD_API int openRegionPagerAPI(lua_State* L) { + luaL_newlib(L, regionPagerLib); + return 1; +} \ No newline at end of file diff --git a/region_pager_api.hpp b/region_pager_api.hpp new file mode 100644 index 0000000..2374b6a --- /dev/null +++ b/region_pager_api.hpp @@ -0,0 +1,27 @@ +/* Copyright: (c) Kayne Ruse 2013-2016 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. +*/ +#pragma once + +#include "lua.hpp" + +#define TORTUGA_REGION_PAGER_API "region_pager" +LUAMOD_API int openRegionPagerAPI(lua_State* L); diff --git a/region_pager_base.cpp b/region_pager_base.cpp new file mode 100644 index 0000000..5446c66 --- /dev/null +++ b/region_pager_base.cpp @@ -0,0 +1,106 @@ +/* Copyright: (c) Kayne Ruse 2013-2016 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. +*/ +#include "region_pager_base.hpp" + +#include +#include + +RegionPagerBase::~RegionPagerBase() { + UnloadAll(); +}; + +Region::type_t RegionPagerBase::SetTile(int x, int y, int z, Region::type_t v) { + Region* ptr = GetRegion(x, y); + return ptr->SetTile(x - ptr->GetX(), y - ptr->GetY(), z, v); +} + +//Bug Origin? +Region::type_t RegionPagerBase::GetTile(int x, int y, int z) { + Region* ptr = GetRegion(x, y); + return ptr->GetTile(x - ptr->GetX(), y - ptr->GetY(), z); +} + +bool RegionPagerBase::SetSolid(int x, int y, int b) { + Region* ptr = GetRegion(x, y); + return ptr->SetSolid(x - ptr->GetX(), y - ptr->GetY(), b); +} + +bool RegionPagerBase::GetSolid(int x, int y) { + Region* ptr = GetRegion(x, y); + return ptr->GetSolid(x - ptr->GetX(), y - ptr->GetY()); +} + +Region* RegionPagerBase::GetRegion(int x, int y) { + x = snapToBase(REGION_WIDTH, x); + y = snapToBase(REGION_HEIGHT, y); + + //get the region by various means + Region* ptr = nullptr; + ptr = FindRegion(x, y); + if (ptr) return ptr; + ptr = LoadRegion(x, y); + if (ptr) return ptr; + return CreateRegion(x, y); +} + +Region* RegionPagerBase::FindRegion(int x, int y) { + //find the region + std::list::iterator it = find_if(regionList.begin(), regionList.end(), [x, y](Region& region) -> bool { + return region.GetX() == x && region.GetY() == y; + }); + return it != regionList.end() ? &(*it) : nullptr; +} + +Region* RegionPagerBase::PushRegion(Region* const ptr) { + regionList.push_front(*ptr); + return ®ionList.front(); +} + +Region* RegionPagerBase::LoadRegion(int x, int y) { + //EMPTY, intended for override + return nullptr; +} + +Region* RegionPagerBase::SaveRegion(int x, int y) { + //EMPTY, intended for override + return nullptr; +} + +Region* RegionPagerBase::CreateRegion(int x, int y) { + if (FindRegion(x, y)) { + throw(std::logic_error("Cannot overwrite an existing region")); + } + regionList.emplace_front(x, y); + return ®ionList.front(); +} + +void RegionPagerBase::UnloadIf(std::function fn) { + regionList.remove_if(fn); +} + +void RegionPagerBase::UnloadAll() { + regionList.clear(); +} + +std::list* RegionPagerBase::GetContainer() { + return ®ionList; +} \ No newline at end of file diff --git a/region_pager_base.hpp b/region_pager_base.hpp new file mode 100644 index 0000000..ef43e52 --- /dev/null +++ b/region_pager_base.hpp @@ -0,0 +1,58 @@ +/* Copyright: (c) Kayne Ruse 2013-2016 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. +*/ +#pragma once + +#include "region.hpp" + +#include +#include + +class RegionPagerBase { +public: + RegionPagerBase() = default; + virtual ~RegionPagerBase(); + + //tile manipulation + virtual Region::type_t SetTile(int x, int y, int z, Region::type_t v); + virtual Region::type_t GetTile(int x, int y, int z); + + //solid manipulation + virtual bool SetSolid(int x, int y, int b); + virtual bool GetSolid(int x, int y); + + //region manipulation + virtual Region* GetRegion(int x, int y); + virtual Region* FindRegion(int x, int y); + virtual Region* PushRegion(Region* const); + + virtual Region* LoadRegion(int x, int y); + virtual Region* SaveRegion(int x, int y); + virtual Region* CreateRegion(int x, int y); + + virtual void UnloadIf(std::function fn); + virtual void UnloadAll(); + + //accessors & mutators + std::list* GetContainer(); +protected: + std::list regionList; +}; diff --git a/region_pager_lua.cpp b/region_pager_lua.cpp new file mode 100644 index 0000000..889781e --- /dev/null +++ b/region_pager_lua.cpp @@ -0,0 +1,208 @@ +/* Copyright: (c) Kayne Ruse 2013-2016 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. +*/ +#include "region_pager_lua.hpp" + +#include + +RegionPagerLua::~RegionPagerLua() { + //unload all regions + UnloadAll(); + //clear any stored functions + luaL_unref(lua, LUA_REGISTRYINDEX, loadRef); + luaL_unref(lua, LUA_REGISTRYINDEX, saveRef); + luaL_unref(lua, LUA_REGISTRYINDEX, createRef); + luaL_unref(lua, LUA_REGISTRYINDEX, unloadRef); +} + +//return the loaded region, or nullptr on failure +Region* RegionPagerLua::LoadRegion(int x, int y) { + //get the pager's function from the registry + lua_rawgeti(lua, LUA_REGISTRYINDEX, loadRef); + + //check if this function is available + if (lua_isnil(lua, -1)) { + lua_pop(lua, 1); + //signal that there is no load function + return nullptr; + } + + //something to work on + Region tmpRegion(x, y); + lua_pushlightuserdata(lua, &tmpRegion); + + //call the funtion, 1 arg, 1 return + if (lua_pcall(lua, 1, 1, 0) != LUA_OK) { + throw(std::runtime_error(std::string() + "Lua error: " + lua_tostring(lua, -1) )); + } + + //check the return value, success or failure + if (lua_isboolean(lua, -1) && lua_toboolean(lua, -1)) { + lua_pop(lua, 1); + //push and return the loaded region + regionList.push_front(tmpRegion); + return ®ionList.front(); + } + else { + lua_pop(lua, 1); + //signal a failure + return nullptr; + } +} + +//NOTE: this return value seems strange; could replace it with a boolean +//return the saved region, or nullptr on failure +Region* RegionPagerLua::SaveRegion(int x, int y) { + //get the pager's function from the registry + lua_rawgeti(lua, LUA_REGISTRYINDEX, saveRef); + + //check if this function is available + if (lua_isnil(lua, -1)) { + lua_pop(lua, 1); + //signal that the region wasn't saved + return nullptr; + } + + //find the specified region + Region* ptr = FindRegion(x, y); + if (!ptr) { + lua_pop(lua, 1); + //signal that there is no save function + return nullptr; + } + lua_pushlightuserdata(lua, ptr); + + //call the function, 1 arg, 1 return + if (lua_pcall(lua, 1, 1, 0) != LUA_OK) { + throw(std::runtime_error(std::string() + "Lua error: " + lua_tostring(lua, -1) )); + } + + //check the return value, success or failure + if (lua_isboolean(lua, -1) && lua_toboolean(lua, -1)) { + lua_pop(lua, 1); + //return the specified region that was saved + return ptr; + } + else { + lua_pop(lua, 1); + //signal a failure + return nullptr; + } +} + +//DOCS: since this method is the last ditch call from GetRegion, it must return a valid region object, even if the create function hasn't been set. +//return a new region, throwing an error on failure +Region* RegionPagerLua::CreateRegion(int x, int y) { + if (FindRegion(x, y)) { + throw(std::logic_error("Cannot overwrite an existing region")); + } + + //get the pager's function from the registry + lua_rawgeti(lua, LUA_REGISTRYINDEX, createRef); + + //check if this function is available + if (lua_isnil(lua, -1)) { + lua_pop(lua, 1); + //return an empty region object + regionList.emplace_front(x, y); + return ®ionList.front(); + } + + //something to work on + Region tmpRegion(x, y); + lua_pushlightuserdata(lua, &tmpRegion); + + //call the function, 1 arg, 0 return + if (lua_pcall(lua, 1, 0, 0) != LUA_OK) { + throw(std::runtime_error(std::string() + "Lua error: " + lua_tostring(lua, -1) )); + } + + //return the new region + regionList.push_front(tmpRegion); + return ®ionList.front(); +} + +//no return +void RegionPagerLua::UnloadIf(std::function fn) { + //get the pager's function from the registry + lua_rawgeti(lua, LUA_REGISTRYINDEX, unloadRef); + + //check if this function is available + if (lua_isnil(lua, -1)) { + lua_pop(lua, 1); + //remove the regions anyway + regionList.remove_if(fn); + return; + } + + //run each region through this lambda + regionList.remove_if([&](Region& region) -> bool { + if (fn(region)) { + + //push a copy of the function onto the stack with the region + lua_pushvalue(lua, -1); + lua_pushlightuserdata(lua, static_cast(®ion)); + + //call the function, 1 arg, 0 return + if (lua_pcall(lua, 1, 0, 0) != LUA_OK) { + throw(std::runtime_error(std::string() + "Lua error: " + lua_tostring(lua, -1) )); + } + + //signal to the container + return true; + } + //signal to the container + return false; + }); + + //pop the base copy of the function + lua_pop(lua, 1); +} + +void RegionPagerLua::UnloadAll() { + //get the pager's function from the registry + lua_rawgeti(lua, LUA_REGISTRYINDEX, unloadRef); + + //check if this function is available + if (lua_isnil(lua, -1)) { + lua_pop(lua, 1); + //remove the regions anyway + regionList.clear(); + return; + } + + for (auto& it : regionList) { + //push a copy of the function onto the stack with the region + lua_pushvalue(lua, -1); + lua_pushlightuserdata(lua, &it); + + //call the function, 1 arg, 0 return + if (lua_pcall(lua, 1, 0, 0) != LUA_OK) { + throw(std::runtime_error(std::string() + "Lua error: " + lua_tostring(lua, -1) )); + } + } + + //pop the base copy of the function + lua_pop(lua, 1); + + //remove from memory + regionList.clear(); +} \ No newline at end of file diff --git a/region_pager_lua.hpp b/region_pager_lua.hpp new file mode 100644 index 0000000..4dc18b2 --- /dev/null +++ b/region_pager_lua.hpp @@ -0,0 +1,68 @@ +/* Copyright: (c) Kayne Ruse 2013-2016 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. +*/ +#pragma once + +#include "region_pager_base.hpp" + +#include "lua.hpp" + +#include +#include + +//DOCS: set the lua hook before use + +class RegionPagerLua : public RegionPagerBase { +public: + RegionPagerLua() = default; + ~RegionPagerLua(); + + //region manipulation + Region* LoadRegion(int x, int y) override; + Region* SaveRegion(int x, int y) override; + Region* CreateRegion(int x, int y) override; + + void UnloadIf(std::function fn) override; + void UnloadAll() override; + + //accessors & mutators + lua_State* SetLuaState(lua_State* L) { return lua = L; } + lua_State* GetLuaState() { return lua; } + + //utilities for the API + int SetLoadReference(int i) { return loadRef = i; } + int SetSaveReference(int i) { return saveRef = i; } + int SetCreateReference(int i) { return createRef = i; } + int SetUnloadReference(int i) { return unloadRef = i; } + + int GetLoadReference() { return loadRef; } + int GetSaveReference() { return saveRef; } + int GetCreateReference() { return createRef; } + int GetUnloadReference() { return unloadRef; } + +protected: + lua_State* lua = nullptr; + + int loadRef = LUA_NOREF; + int saveRef = LUA_NOREF; + int createRef = LUA_NOREF; + int unloadRef = LUA_NOREF; +}; diff --git a/tile_sheet.cpp b/tile_sheet.cpp new file mode 100644 index 0000000..40e9114 --- /dev/null +++ b/tile_sheet.cpp @@ -0,0 +1,124 @@ +/* Copyright: (c) Kayne Ruse 2013-2016 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. +*/ +#include "tile_sheet.hpp" + +#include + +TileSheet& TileSheet::operator=(TileSheet const& rhs) { + //don't screw yourself + if (this == &rhs) { + return *this; + } + + Free(); + + //Copy the other TileSheet's stuff + texture = rhs.texture; + clip = rhs.clip; + local = false; + countX = rhs.countX; + countY = rhs.countY; +} + +TileSheet& TileSheet::operator=(TileSheet&& rhs) { + //don't screw yourself + if (this == &rhs) { + return *this; + } + + Free(); + + //Copy the other TileSheet's stuff + texture = rhs.texture; + clip = rhs.clip; + local = false; + countX = rhs.countX; + countY = rhs.countY; + + rhs.texture = nullptr; + rhs.clip = {0, 0, 0, 0}; + rhs.local = false; + rhs.countX = 0; + rhs.countY = 0; +} + +void TileSheet::Load(SDL_Renderer* renderer, std::string fname, int tileWidth, int tileHeight) { + Image::Load(renderer, fname); + countX = clip.w / tileWidth; + countY = clip.h / tileHeight; + clip.w = tileWidth; + clip.h = tileHeight; +} + +SDL_Texture* TileSheet::SetTexture(SDL_Texture* ptr, int tileWidth, int tileHeight) { + Image::SetTexture(ptr); + countX = clip.w / tileWidth; + countY = clip.h / tileHeight; + clip.w = tileWidth; + clip.h = tileHeight; +} + +void TileSheet::Free() { + Image::Free(); + countX = countY = 0; +} + +void TileSheet::DrawLayerTo(SDL_Renderer* const renderer, Region* const region, int layer, int camX, int camY, double scaleX, double scaleY) { + //TODO: (2) empty +} + +void TileSheet::DrawRegionTo(SDL_Renderer* const renderer, Region* const region, int camX, int camY, double scaleX, double scaleY) { + //NOTE: TileSheet is a friend class of Region + //reimplementing DrawTo() to improve performance (less indirection) + if (!texture) { + throw(std::logic_error("No image texture to draw")); + } + + //the local variables + SDL_Rect sclip = {0, 0, clip.w, clip.h}; + SDL_Rect dclip = {0, 0, Uint16(clip.w * scaleX), Uint16(clip.h * scaleY)}; + Region::type_t tile = 0; + + //for each tile + for (register int i = 0; i < REGION_WIDTH; ++i) { + for (register int j = 0; j < REGION_HEIGHT; ++j) { + for (register int k = 0; k < REGION_DEPTH; ++k) { + //get the value to skip expensive lookups + tile = region->tiles[i][j][k]; + + //0 is invisible + if (tile == 0) continue; + + //set the sclip + sclip.x = (tile-1) % countX * clip.h; + sclip.y = (tile-1) / countX * clip.w; + + //set the dclip + dclip.x = ((region->x + i) * clip.w - camX) * scaleX; + dclip.y = ((region->y + j) * clip.h - camY) * scaleY; + + //draw + SDL_RenderCopy(renderer, texture, &sclip, &dclip); + } + } + } +} diff --git a/tile_sheet.hpp b/tile_sheet.hpp new file mode 100644 index 0000000..145f7a7 --- /dev/null +++ b/tile_sheet.hpp @@ -0,0 +1,67 @@ +/* Copyright: (c) Kayne Ruse 2013-2016 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. +*/ +#pragma once + +#include "region.hpp" + +#include "image.hpp" + +#include + +class TileSheet : public Image { +public: + TileSheet() = default; + TileSheet(TileSheet const& rhs) { *this = rhs; } + TileSheet(TileSheet&& rhs) { *this = std::move(rhs); } + TileSheet(SDL_Renderer* r, std::string fn, int tw, int th) { Load(r, fn, tw, th); } + TileSheet(SDL_Texture* p, int tw, int th) { SetTexture(p, tw, th); } + ~TileSheet() = default; + + TileSheet& operator=(TileSheet const&); + TileSheet& operator=(TileSheet&&); + + void Load(SDL_Renderer*, std::string fname, int tileWidth, int tileHeight); + SDL_Texture* SetTexture(SDL_Texture*, int tileWidth, int tileHeight); + void Free() override; + + void DrawLayerTo(SDL_Renderer* const renderer, Region* const region, int layer, int camX, int camY, double scaleX = 1.0, double scaleY = 1.0); + void DrawRegionTo(SDL_Renderer* const renderer, Region* const region, int camX, int camY, double scaleX = 1.0, double scaleY = 1.0); + + //accessors + //DOCS: Reuse Image::clip for tile sizes + int GetCountX() { return countX; } + int GetCountY() { return countY; } + int GetTileW() { return clip.w; } + int GetTileH() { return clip.h; } + +protected: + int countX = 0, countY = 0; + + using Image::Load; + using Image::Create; + using Image::SetTexture; + using Image::SetClip; + using Image::SetClipX; + using Image::SetClipY; + using Image::SetClipW; + using Image::SetClipH; +};