Forked from
tome / Tales of MajEyal
13802 commits behind the upstream repository.
-
dg authored
Right click popup will not allow talents to be used on the world map Static map generator will not clone terrain that does not requests cloning git-svn-id: http://svn.net-core.org/repos/t-engine4@1256 51575b47-30f0-44d4-a5cc-537603b46e54
dg authoredRight click popup will not allow talents to be used on the world map Static map generator will not clone terrain that does not requests cloning git-svn-id: http://svn.net-core.org/repos/t-engine4@1256 51575b47-30f0-44d4-a5cc-537603b46e54
Entity.lua 12.26 KiB
-- TE4 - T-Engine 4
-- Copyright (C) 2009, 2010 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
--- A game entity
-- An entity is anything that goes on a map, terrain features, objects, monsters, player, ...
-- Usually there is no need to use it directly, and it is betetr to use specific engine.Grid, engine.Actor or engine.Object
-- classes. Most modules will want to subclass those anyway to add new comportments
local Shader = require "engine.Shader"
module(..., package.seeall, class.make)
local next_uid = 1
local entities_load_functions = {}
_M.__mo_repo = {}
_M.__mo_final_repo = {}
_M._no_save_fields = {}
-- Setup the uids & MO repository as a weak value table, when the entities are no more used anywhere else they disappear from there too
setmetatable(__uids, {__mode="v"})
setmetatable(_M.__mo_repo, {__mode="k"})
setmetatable(_M.__mo_final_repo, {__mode="k"})
--- Invalidates the whole MO repository
function _M:invalidateAllMO()
for mo, _ in pairs(_M.__mo_repo) do
mo:invalidate()
end
_M.__mo_repo = {}
setmetatable(_M.__mo_repo, {__mode="k"})
setmetatable(_M.__mo_final_repo, {__mode="k"})
end
local function copy_recurs(dst, src, deep)
for k, e in pairs(src) do
if type(e) == "table" and e.__CLASSNAME then
dst[k] = e
elseif not dst[k] then
if deep then
dst[k] = {}
copy_recurs(dst[k], e, deep)
else
dst[k] = e
end
elseif type(dst[k]) == "table" and type(e) == "table" and not e.__CLASSNAME then
copy_recurs(dst[k], e, deep)
end
end
end
--- Initialize an entity
-- Any subclass MUST call this constructor
-- @param t a table defining the basic properties of the entity
-- @usage Entity.new{display='#', color_r=255, color_g=255, color_b=255}
function _M:init(t, no_default)
t = t or {}
self.uid = next_uid
__uids[self.uid] = self
for k, e in pairs(t) do
if k ~= "__CLASSNAME" then
local ee = e
if type(e) == "table" and not e.__CLASSNAME then ee = table.clone(e, true) end
self[k] = ee
end
end
if self.color then
self.color_r = self.color.r
self.color_g = self.color.g
self.color_b = self.color.b
self.color = nil
end
if self.back_color then
self.color_br = self.back_color.r
self.color_bg = self.back_color.g
self.color_bb = self.back_color.b
self.back_color = nil
end
if self.tint then
self.tint_r = self.tint.r / 255
self.tint_g = self.tint.g / 255
self.tint_b = self.tint.b / 255
self.tint = nil
end
if not no_default then
self.image = self.image or nil
self.display = self.display or '.'
self.color_r = self.color_r or 0
self.color_g = self.color_g or 0
self.color_b = self.color_b or 0
self.color_br = self.color_br or -1
self.color_bg = self.color_bg or -1
self.color_bb = self.color_bb or -1
self.tint_r = self.tint_r or 1
self.tint_g = self.tint_g or 1
self.tint_b = self.tint_b or 1
end
if self.unique and type(self.unique) ~= "string" then self.unique = self.name end
next_uid = next_uid + 1
self.changed = true
self.__particles = self.__particles or {}
end
--- If we are cloned we need a new uid
function _M:cloned()
self.uid = next_uid
__uids[self.uid] = self
next_uid = next_uid + 1
self.changed = true
end
_M.__autoload = {}
_M.loadNoDelay = true
--- If we are loaded we need a new uid
function _M:loaded()
local ouid = self.uid
self.uid = next_uid
__uids[self.uid] = self
next_uid = next_uid + 1
self.changed = true
-- hackish :/
if self.autoLoadedAI then self:autoLoadedAI() end
end
--- Change the entity's uid
-- <strong>*WARNING*</strong>: ONLY DO THIS IF YOU KNOW WHAT YOU ARE DOING!. YOU DO NOT !
function _M:changeUid(newuid)
__uids[self.uid] = nil
self.uid = newuid
__uids[self.uid] = self
end
--- Create the "map object" representing this entity
-- Do not touch unless you *KNOW* what you are doing.<br/>
-- You do *NOT* need this, this is used by the engine.Map class automatically.<br/>
-- *DO NOT TOUCH!!!*
function _M:makeMapObject(tiles, idx)
if idx > 1 and not tiles.use_images then return nil end
if idx > 1 then
if not self.add_displays or not self.add_displays[idx-1] then return nil end
return self.add_displays[idx-1]:makeMapObject(tiles, 1)
else
if self._mo and self._mo:isValid() then return self._mo, self.z end
end
-- Create the map object with 1 + additional textures
self._mo = core.map.newObject(self.uid,
1 + (tiles.use_images and self.textures and #self.textures or 0),
self:check("display_on_seen"),
self:check("display_on_remember"),
self:check("display_on_unknown"),
self:check("display_x") or 0,
self:check("display_y") or 0,
self:check("display_scale") or 1
)
_M.__mo_repo[self._mo] = true
-- Setup tint
self._mo:tint(self.tint_r, self.tint_g, self.tint_b)
-- Texture 0 is always the normal image/ascii tile
self._mo:texture(0, tiles:get(self.display, self.color_r, self.color_g, self.color_b, self.color_br, self.color_bg, self.color_bb, self.image, self._noalpha and 255, self.ascii_outline))
-- Setup additional textures
if tiles.use_images and self.textures then
for i = 1, #self.textures do
local t = self.textures[i]
if type(t) == "function" then local tex, is3d = t(self, tiles); if tex then self._mo:texture(i, tex, is3d) tiles.texture_store[tex] = true end
elseif type(t) == "table" then
if t[1] == "image" then local tex = tiles:get('', 0, 0, 0, 0, 0, 0, t[2]); self._mo:texture(i, tex, false) tiles.texture_store[tex] = true
end
end
end
end
-- Setup shader
if tiles.use_images and core.shader.active() and self.shader then
local shad = Shader.new(self.shader, self.shader_args)
if shad.shad then self._mo:shader(shad.shad) end
end
return self._mo, self.z
end
--- Get all "map objects" representing this entity
-- Do not touch unless you *KNOW* what you are doing.<br/>
-- You do *NOT* need this, this is used by the engine.Map class automatically.<br/>
-- *DO NOT TOUCH!!!*
function _M:getMapObjects(tiles, mos, z)
local i = -1
local mo, dz
repeat
i = i + 1
mo, dz = self:makeMapObject(tiles, 1+i)
if mo then
mos[dz or z+i] = mo
end
until not mo
end
--- Get the entity image as an sdl surface and texture for the given tiles and size
-- @param tiles a Tiles instance that will handle the tiles (usualy pass it the current Map.tiles)
-- @param w the width
-- @param h the height
-- @return the sdl surface and the texture
function _M:getEntityFinalSurface(tiles, w, h)
local id = w.."x"..h
if _M.__mo_final_repo[self] and _M.__mo_final_repo[self][id] then return _M.__mo_final_repo[self][id].surface, _M.__mo_final_repo[self][id].tex end
local Map = require "engine.Map"
local mos = {}
local list = {}
self:getMapObjects(tiles, mos, 1)
for i = 1, Map.zdepth do
if mos[i] then list[#list+1] = mos[i] end
end
local tex = core.map.mapObjectsToTexture(w, h, unpack(list))
if not tex then return nil end
_M.__mo_final_repo[self] = _M.__mo_final_repo[self] or {}
_M.__mo_final_repo[self][id] = {surface=tex:toSurface(), tex=tex}
return _M.__mo_final_repo[self][id].surface, _M.__mo_final_repo[self][id].tex
end
--- Get a string that will display in text the texture of this entity
function _M:getDisplayString()
if core.display.FBOActive() then
return "#UID:"..self.uid..":0#"
else
return ""
end
end
--- Resolves an entity
-- This is called when generatingthe final clones of an entity for use in a level.<br/>
-- This can be used to make random enchants on objects, random properties on actors, ...<br/>
-- by default this only looks for properties with a table value containing a __resolver field
function _M:resolve(t, last, on_entity)
t = t or self
for k, e in pairs(t) do
if type(e) == "table" and e.__resolver and (not e.__resolve_last or last) then
t[k] = resolvers.calc[e.__resolver](e, on_entity or self, self)
elseif type(e) == "table" and not e.__CLASSNAME then
self:resolve(e, last, on_entity)
end
end
-- Finish resolving stuff
if on_entity then return end
if t == self then
if last then
if self.resolveLevel then self:resolveLevel() end
if self.unique and type(self.unique) == "boolean" then
self.unique = self.name
end
else
-- Handle ided if possible
if self.resolveIdentify then self:resolveIdentify() end
end
end
end
--- Call when the entity is actually added to a level/whatever
-- This helps ensuring uniqueness of uniques
function _M:added()
if self.unique then
game.uniques[self.__CLASSNAME.."/"..self.unique] = true
print("Added unique", self.__CLASSNAME.."/"..self.unique)
end
end
--- Call when the entity is actually removed from existance
-- This helps ensuring uniqueness of uniques.
-- This recursively remvoes inventories too, if you need anythign special, overload this
function _M:removed()
if self.inven then
for _, inven in pairs(self.inven) do
for i, o in ipairs(inven) do
o:removed()
end
end
end
if self.unique then
game.uniques[self.__CLASSNAME.."/"..self.unique] = nil
print("Removed unique", self.__CLASSNAME.."/"..self.unique)
end
end
--- Check for an entity's property
-- If not a function it returns it directly, otherwise it calls the function
-- with the extra parameters
-- @param prop the property name to check
function _M:check(prop, ...)
if type(self[prop]) == "function" then return self[prop](self, ...)
else return self[prop]
end
end
--- Loads a list of entities from a definition file
-- @param file the file to load from
-- @param no_default if true then no default values will be assigned
-- @param res the table to load into, defaults to a new one
-- @param mod an optional function to which will be passed each entity as they are created. Can be used to adjust some values on the fly
-- @usage MyEntityClass:loadList("/data/my_entities_def.lua")
function _M:loadList(file, no_default, res, mod, loaded)
if type(file) == "table" then
res = res or {}
for i, f in ipairs(file) do
self:loadList(f, no_default, res, mod)
end
return res
end
no_default = no_default and true or false
res = res or {}
local f, err = nil, nil
if entities_load_functions[file] and entities_load_functions[file][no_default] then
print("Loading entities file from memory", file)
f = entities_load_functions[file][no_default]
elseif fs.exists(file) then
f, err = loadfile(file)
print("Loading entities file from file", file)
entities_load_functions[file] = entities_load_functions[file] or {}
entities_load_functions[file][no_default] = f
else
-- No data
f = function() end
end
if err then error(err) end
loaded = loaded or {}
loaded[file] = true
setfenv(f, setmetatable({
class = self,
loaded = loaded,
resolvers = resolvers,
DamageType = require "engine.DamageType",
entity_mod = mod,
rarity = function(add, mult) add = add or 0; mult = mult or 1; return function(e) if e.rarity then e.rarity = math.ceil(e.rarity * mult + add) end end end,
newEntity = function(t)
-- Do we inherit things ?
if t.base then
-- Append array part
for i = 1, #res[t.base] do
local b = res[t.base][i]
if type(b) == "table" and not b.__CLASSNAME then b = table.clone(b, true)
elseif type(b) == "table" and b.__CLASSNAME then b = b:clone()
end
table.insert(t, b)
end
for k, e in pairs(res[t.base]) do
if k ~= "define_as" and type(k) ~= "number" then
if type(t[k]) == "table" and type(e) == "table" then
copy_recurs(t[k], e)
elseif not t[k] and type(t[k]) ~= "boolean" then
t[k] = e
end
end
end
t.base = nil
end
local e = self.new(t, no_default)
if mod then mod(e) end
res[#res+1] = e
if t.define_as then res[t.define_as] = e end
end,
load = function(f, new_mod)
self:loadList(f, no_default, res, new_mod or mod, loaded)
end,
loadList = function(f, new_mod)
return self:loadList(f, no_default, nil, new_mod or mod, loaded)
end,
}, {__index=_G}))
f()
return res
end