Skip to content
Snippets Groups Projects
fov.c 13.12 KiB
/*
    TE4 - T-Engine 4
    Copyright (C) 2009, 2010, 2011 Nicolas Casalini

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    Nicolas Casalini "DarkGod"
    darkgod@te4.org
*/
#include "display.h"
#include "fov/fov.h"
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "auxiliar.h"
#include "types.h"
#include "script.h"
#include "display.h"
#include "physfs.h"
#include "physfsrwops.h"
#include "SFMT.h"
#include "mzip.h"
#include "main.h"
#include "useshader.h"
#include <math.h>
#include <time.h>

/******************************************************************
 ******************************************************************
 *                              FOV                               *
 ******************************************************************
 ******************************************************************/
struct lua_fovcache
{
	bool *cache;
	int w, h;
};

struct lua_fov
{
	lua_State *L;
	fov_settings_type fov_settings;
	int w, h;
	int apply_ref;
	int opaque_ref;
	int cache_ref;
	struct lua_fovcache *cache;
};

static void map_seen(void *m, int x, int y, int dx, int dy, int radius, void *src)
{
	struct lua_fov *fov = (struct lua_fov *)m;
	radius--;
	if (x < 0 || y < 0 || x >= fov->w || y >= fov->h) return;
	if (dx*dx + dy*dy <= radius*radius + 1)
	{
		// circular view - can be changed if you like
		lua_rawgeti(fov->L, LUA_REGISTRYINDEX, fov->apply_ref);
		if (fov->cache) lua_rawgeti(fov->L, LUA_REGISTRYINDEX, fov->cache_ref);
		else lua_pushnil(fov->L);
		lua_pushnumber(fov->L, x);
		lua_pushnumber(fov->L, y);
		lua_pushnumber(fov->L, dx);
		lua_pushnumber(fov->L, dy);
		lua_pushnumber(fov->L, dx*dx + dy*dy);
		lua_call(fov->L, 6, 0);
	}
}

static bool map_opaque(void *m, int x, int y)
{
	struct lua_fov *fov = (struct lua_fov *)m;
	if (x < 0 || y < 0 || x >= fov->w || y >= fov->h) return TRUE;

	if (fov->cache)
	{
		return fov->cache->cache[x + y * fov->cache->w];
	}
	else
	{
		lua_rawgeti(fov->L, LUA_REGISTRYINDEX, fov->opaque_ref);
		if (fov->cache) lua_rawgeti(fov->L, LUA_REGISTRYINDEX, fov->cache_ref);
		else lua_pushnil(fov->L);
		lua_pushnumber(fov->L, x);
		lua_pushnumber(fov->L, y);
		lua_call(fov->L, 3, 1);
		bool res = lua_toboolean(fov->L, -1);
		lua_pop(fov->L, 1);
		return res;
	}
}

static int lua_fov_calc_circle(lua_State *L)
{
	int x = luaL_checknumber(L, 1);
	int y = luaL_checknumber(L, 2);
	int w = luaL_checknumber(L, 3);
	int h = luaL_checknumber(L, 4);
	int radius = luaL_checknumber(L, 5);
	struct lua_fov fov;
	if (lua_isuserdata(L, 8))
	{
		fov.cache = (struct lua_fovcache*)auxiliar_checkclass(L, "fov{cache}", 8);
		fov.cache_ref = luaL_ref(L, LUA_REGISTRYINDEX);
	}
	else
	{
		lua_pop(L, 1);
		fov.cache_ref = LUA_NOREF;
		fov.cache = NULL;
	}
	fov.L = L;
	fov.apply_ref = luaL_ref(L, LUA_REGISTRYINDEX);
	fov.opaque_ref = luaL_ref(L, LUA_REGISTRYINDEX);
	fov.w = w;
	fov.h = h;

	fov_settings_init(&(fov.fov_settings));
	fov_settings_set_opacity_test_function(&(fov.fov_settings), map_opaque);
	fov_settings_set_apply_lighting_function(&(fov.fov_settings), map_seen);
	fov_circle(&(fov.fov_settings), &fov, NULL, x, y, radius+1);
	map_seen(&fov, x, y, 0, 0, radius, NULL);
	fov_settings_free(&(fov.fov_settings));

	luaL_unref(L, LUA_REGISTRYINDEX, fov.apply_ref);
	luaL_unref(L, LUA_REGISTRYINDEX, fov.opaque_ref);
	if (fov.cache_ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, fov.cache_ref);

	return 0;
}

static int lua_fov_calc_beam(lua_State *L)
{
	int x = luaL_checknumber(L, 1);
	int y = luaL_checknumber(L, 2);
	int w = luaL_checknumber(L, 3);
	int h = luaL_checknumber(L, 4);
	int radius = luaL_checknumber(L, 5);
	int direction = luaL_checknumber(L, 6);
	float angle = luaL_checknumber(L, 7);
	struct lua_fov fov;
	if (lua_isuserdata(L, 10))
	{
		fov.cache = (struct lua_fovcache*)auxiliar_checkclass(L, "fov{cache}", 10);
		fov.cache_ref = luaL_ref(L, LUA_REGISTRYINDEX);
	}
	else
	{
		lua_pop(L, 1);
		fov.cache_ref = LUA_NOREF;
		fov.cache = NULL;
	}
	fov.L = L;
	fov.apply_ref = luaL_ref(L, LUA_REGISTRYINDEX);
	fov.opaque_ref = luaL_ref(L, LUA_REGISTRYINDEX);
	fov.cache = NULL;
	fov.w = w;
	fov.h = h;
	int dir = 0;

	switch (direction)
	{
	case 1: dir = FOV_SOUTHWEST; break;
	case 2: dir = FOV_SOUTH; break;
	case 3: dir = FOV_SOUTHEAST; break;
	case 4: dir = FOV_WEST; break;
	case 6: dir = FOV_EAST; break;
	case 7: dir = FOV_NORTHWEST; break;
	case 8: dir = FOV_NORTH; break;
	case 9: dir = FOV_NORTHEAST; break;
	}

	fov_settings_init(&(fov.fov_settings));
	fov_settings_set_opacity_test_function(&(fov.fov_settings), map_opaque);
	fov_settings_set_apply_lighting_function(&(fov.fov_settings), map_seen);
	fov_beam(&(fov.fov_settings), &fov, NULL, x, y, radius+1, dir, angle);
	map_seen(&fov, x, y, 0, 0, radius, NULL);
	fov_settings_free(&(fov.fov_settings));

	luaL_unref(L, LUA_REGISTRYINDEX, fov.apply_ref);
	luaL_unref(L, LUA_REGISTRYINDEX, fov.opaque_ref);
	if (fov.cache_ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, fov.cache_ref);

	return 0;
}

static int lua_fov_calc_beam_any_angle(lua_State *L)
{
	int x = luaL_checknumber(L, 1);
	int y = luaL_checknumber(L, 2);
	int w = luaL_checknumber(L, 3);
	int h = luaL_checknumber(L, 4);
	int radius = luaL_checknumber(L, 5);
	float dir_angle = luaL_checknumber(L, 6);
	float beam_angle = luaL_checknumber(L, 7);
	struct lua_fov fov;
	if (lua_isuserdata(L, 10))
	{
		fov.cache = (struct lua_fovcache*)auxiliar_checkclass(L, "fov{cache}", 10);
		fov.cache_ref = luaL_ref(L, LUA_REGISTRYINDEX);
	}
	else
	{
		lua_pop(L, 1);
		fov.cache_ref = LUA_NOREF;
		fov.cache = NULL;
	}
	fov.L = L;
	fov.apply_ref = luaL_ref(L, LUA_REGISTRYINDEX);
	fov.opaque_ref = luaL_ref(L, LUA_REGISTRYINDEX);
	fov.cache = NULL;
	fov.w = w;
	fov.h = h;
	
	fov_settings_init(&(fov.fov_settings));
	fov_settings_set_opacity_test_function(&(fov.fov_settings), map_opaque);
	fov_settings_set_apply_lighting_function(&(fov.fov_settings), map_seen);
	fov_beam_any_angle(&(fov.fov_settings), &fov, NULL, x, y, radius+1, dir_angle, beam_angle);
	map_seen(&fov, x, y, 0, 0, radius, NULL);
	fov_settings_free(&(fov.fov_settings));
	
	luaL_unref(L, LUA_REGISTRYINDEX, fov.apply_ref);
	luaL_unref(L, LUA_REGISTRYINDEX, fov.opaque_ref);
	if (fov.cache_ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, fov.cache_ref);
	
	return 0;
}

static int lua_distance(lua_State *L)
{
	double x1 = luaL_checknumber(L, 1);
	double y1 = luaL_checknumber(L, 2);
	double x2 = luaL_checknumber(L, 3);
	double y2 = luaL_checknumber(L, 4);

	lua_pushnumber(L, sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)));
	return 1;
}

static int lua_new_fovcache(lua_State *L)
{
	int x = luaL_checknumber(L, 1);
	int y = luaL_checknumber(L, 2);
	int i, j;

	struct lua_fovcache *cache = (struct lua_fovcache*)lua_newuserdata(L, sizeof(struct lua_fovcache));
	auxiliar_setclass(L, "fov{cache}", -1);
	cache->w = x;
	cache->h = y;
	cache->cache = calloc(x * y, sizeof(bool));
	for (i = 0; i < x; i++)
		for (j = 0; j < y; j++)
			cache->cache[i + j * x] = FALSE;

	return 1;
}

static int lua_fovcache_set(lua_State *L)
{
	struct lua_fovcache *cache = (struct lua_fovcache*)auxiliar_checkclass(L, "fov{cache}", 1);
	int x = luaL_checknumber(L, 2);
	int y = luaL_checknumber(L, 3);
	bool opaque = lua_toboolean(L, 4);

	if (x < 0 || y < 0 || x >= cache->w || y >= cache->h) return 0;
	cache->cache[x + y * cache->w] = opaque;

	return 0;
}

static int lua_fovcache_get(lua_State *L)
{
	struct lua_fovcache *cache = (struct lua_fovcache*)auxiliar_checkclass(L, "fov{cache}", 1);
	int x = luaL_checknumber(L, 2);
	int y = luaL_checknumber(L, 3);

	lua_pushboolean(L, cache->cache[x + y * cache->w]);
	return 1;
}


/****************************************************************
 ** Default FOV
 ****************************************************************/
#define STACK_X 1
#define STACK_Y 2
#define STACK_RAD 3
#define STACK_BLOCK 4
#define STACK_FOV 5
#define STACK_ACTOR 6
#define STACK_DIST 7
#define STACK_MAP 8
#define STACK_MAP_W 9
#define STACK_MAP_H 10
#define STACK_ENTITY 11
#define STACK_DMAP 12
#define STACK_TURN 13
#define STACK_SELF 14
#define STACK_APPLY 15

typedef struct {
	int dist_idx;
	int w, h;
	int entity;
	float turn;
	bool do_dmap;
	bool do_apply;
} default_fov;

static void map_default_seen(void *m, int x, int y, int dx, int dy, int radius, void *src)
{
	default_fov *def = (default_fov*)src;
	struct lua_fov *fov = (struct lua_fov *)m;
	radius--;
	float sqdist = dx*dx + dy*dy;
	float dist = sqrtf(sqdist);
	if (sqdist <= radius*radius + 1)
	{
		// Distance Map
		if (def->do_dmap)
		{
			lua_pushnumber(fov->L, x + y * def->w);
			lua_pushnumber(fov->L, def->turn + radius - dist);
			lua_rawset(fov->L, STACK_DMAP);
		}

		// Apply
		if (def->do_apply)
		{
			lua_pushvalue(fov->L, STACK_APPLY);
			lua_pushnumber(fov->L, x);
			lua_pushnumber(fov->L, y);
			lua_pushnumber(fov->L, dx);
			lua_pushnumber(fov->L, dy);
			lua_pushnumber(fov->L, sqdist);
			lua_call(fov->L, 5, 0);
		}

		// Get entity
		lua_pushnumber(fov->L, x + y * def->w);
		lua_rawget(fov->L, STACK_MAP);
		if (!lua_istable(fov->L, -1)) { lua_pop(fov->L, 1); return; }
		lua_pushnumber(fov->L, def->entity);
		lua_rawget(fov->L, -2);
		if (!lua_istable(fov->L, -1)) { lua_pop(fov->L, 2); return; }

		// Check if dead
		lua_pushstring(fov->L, "dead");
		lua_gettable(fov->L, -2);
		if (lua_toboolean(fov->L, -1)) { lua_pop(fov->L, 3); return; }
		lua_pop(fov->L, 1);

		// Set sqdist in the actor for faster sorting
		lua_pushstring(fov->L, "__sqdist");
		lua_pushnumber(fov->L, sqdist);
		lua_rawset(fov->L, -3);

		// Make a table to hold data
		lua_newtable(fov->L);
		lua_pushstring(fov->L, "x");
		lua_pushnumber(fov->L, x);
		lua_rawset(fov->L, -3);
		lua_pushstring(fov->L, "y");
		lua_pushnumber(fov->L, y);
		lua_rawset(fov->L, -3);
		lua_pushstring(fov->L, "dx");
		lua_pushnumber(fov->L, dx);
		lua_rawset(fov->L, -3);
		lua_pushstring(fov->L, "dy");
		lua_pushnumber(fov->L, dy);
		lua_rawset(fov->L, -3);
		lua_pushstring(fov->L, "sqdist");
		lua_pushnumber(fov->L, sqdist);
		lua_rawset(fov->L, -3);

		// Set the actor table
		lua_pushvalue(fov->L, -2);
		lua_pushvalue(fov->L, -2);
		lua_rawset(fov->L, STACK_ACTOR);

		// Set the dist table
		def->dist_idx++;
		lua_pushnumber(fov->L, def->dist_idx);
		lua_pushvalue(fov->L, -3);
		lua_rawset(fov->L, STACK_DIST);

		// Call seen_by, if possible
		lua_pushstring(fov->L, "updateFOV");
		lua_gettable(fov->L, -3);
		lua_pushvalue(fov->L, -3);
		lua_pushvalue(fov->L, STACK_SELF);
		lua_pushnumber(fov->L, sqdist);
		lua_call(fov->L, 3, 0);

		// Call seen_by, if possible
		lua_pushstring(fov->L, "seen_by");
		lua_gettable(fov->L, -3);
		if (lua_isfunction(fov->L, -1))
		{
			lua_pushvalue(fov->L, -3);
			lua_pushvalue(fov->L, STACK_SELF);
			lua_call(fov->L, 2, 0);
		}
		else lua_pop(fov->L, 1);

		lua_pop(fov->L, 3);
	}
}
static bool map_default_opaque(void *m, int x, int y)
{
	struct lua_fov *fov = (struct lua_fov *)m;

	if (x < 0 || y < 0 || x >= fov->cache->w || y >= fov->cache->h) return TRUE;
	return fov->cache->cache[x + y * fov->cache->w];
}

static int lua_fov_calc_default_fov(lua_State *L)
{
	int x = luaL_checknumber(L, 1);
	int y = luaL_checknumber(L, 2);
	int radius = luaL_checknumber(L, 3);
	// 4 Block
	struct lua_fov fov;
	fov.L = L;
	fov.cache = (struct lua_fovcache*)auxiliar_checkclass(L, "fov{cache}", 5);
	// 6, 7 = actor, actor_dist
	// 8 map
	// 9 map.w
	// 10 map.h
	// 11 Map.ENTITY
	// 12 DMAP
	// 13 game.turn
	// 14 self
	// 15 apply

	default_fov def;
	def.w = lua_tonumber(L, STACK_MAP_W);
	def.h = lua_tonumber(L, STACK_MAP_H);
	def.turn = lua_tonumber(L, STACK_TURN);
	def.entity = lua_tonumber(L, STACK_ENTITY);
	def.do_dmap = lua_istable(L, STACK_DMAP);
	def.do_apply = lua_isfunction(L, STACK_APPLY);
	def.dist_idx = 0;
	fov.w = def.w;
	fov.h = def.h;

//	printf("<TOP %d\n", lua_gettop(L));

	fov_settings_init(&(fov.fov_settings));
	fov_settings_set_opacity_test_function(&(fov.fov_settings), map_default_opaque);
	fov_settings_set_apply_lighting_function(&(fov.fov_settings), map_default_seen);
	fov_circle(&(fov.fov_settings), &fov, &def, x, y, radius+1);
	map_default_seen(&fov, x, y, 0, 0, radius, &def);
	fov_settings_free(&(fov.fov_settings));

//	printf(">TOP %d\n", lua_gettop(L));

	return 0;
}


static const struct luaL_reg fovlib[] =
{
	{"newCache", lua_new_fovcache},
	{"distance", lua_distance},
	{"calc_default_fov", lua_fov_calc_default_fov},
	{"calc_circle", lua_fov_calc_circle},
	{"calc_beam", lua_fov_calc_beam},
	{"calc_beam_any_angle", lua_fov_calc_beam_any_angle},
	{NULL, NULL},
};

static const struct luaL_reg fovcache_reg[] =
{
	{"set", lua_fovcache_set},
	{"get", lua_fovcache_get},
	{NULL, NULL},
};

int luaopen_fov(lua_State *L)
{
	auxiliar_newclass(L, "fov{cache}", fovcache_reg);
	luaL_openlib(L, "core.fov", fovlib, 0);
	lua_pop(L, 1);
	return 1;
}