-
dg authored
git-svn-id: http://svn.net-core.org/repos/t-engine4@3087 51575b47-30f0-44d4-a5cc-537603b46e54
dg authoredgit-svn-id: http://svn.net-core.org/repos/t-engine4@3087 51575b47-30f0-44d4-a5cc-537603b46e54
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;
}