Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make some fields on objects/map objects immutable #164

Merged
merged 13 commits into from
Mar 2, 2021
2 changes: 1 addition & 1 deletion src/api/Fs.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function Fs.open(filepath, mode)
-- love.File:open() does not support "rb"
mode = mode:gsub("b$", "")

if not fs.is_absolute(filepath) then
if fs.get_global_working_directory() and not fs.is_absolute(filepath) then
filepath = fs.join(Fs.get_working_directory(), filepath)
end

Expand Down
10 changes: 6 additions & 4 deletions src/api/IMapObject.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,19 @@ local Log = require("api.Log")
local IMapObject = class.interface("IMapObject",
{
uid = "number",
x = "number",
y = "number",
produce_memory = "function"
},
{IOwned, IObject})

function IMapObject:init()
IObject.init(self)

self.x = 0
self.y = 0
-- We make `x` and `y` immutable using `object.__newindex`, to force their
-- update through IMapObject:set_pos(x, y). This is so positional indexing
-- structures can get updated properly.
local mt = getmetatable(self)
mt.x = 0
mt.y = 0
end

function IMapObject:refresh_cell_on_map()
Expand Down
2 changes: 1 addition & 1 deletion src/api/IOwned.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ local ILocation = require("api.ILocation")

local IOwned = class.interface("IOwned",
{
location = { type = ILocation, optional = true },
-- location = { type = ILocation, optional = true },
_parent = { type = ILocation, optional = true }
})

Expand Down
53 changes: 39 additions & 14 deletions src/api/MapObject.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ local object = require("internal.object")
local ILocation = require("api.ILocation")
local pool = require("internal.pool")
local IOwned = require("api.IOwned")
local IMapObject = require("api.IMapObject")

local Event = require("api.Event")
local Object = require("api.Object")
Expand All @@ -17,7 +18,9 @@ function MapObject.generate_from(_type, id, uid_tracker)
-- params.no_pre_build = true
local obj = Object.generate_from(_type, id)

rawset(obj, "uid", uid)
local mt = getmetatable(obj)
assert(mt.__id == "object")
rawset(mt, "uid", uid)

-- class.assert_is_an(IMapObject, data)

Expand All @@ -34,7 +37,9 @@ function MapObject.generate(proto, uid_tracker)
-- params.no_pre_build = true
local obj = Object.generate(proto)

rawset(obj, "uid", uid)
local mt = getmetatable(obj)
assert(mt.__id == "object")
rawset(mt, "uid", uid)

-- class.assert_is_an(IMapObject, obj)

Expand Down Expand Up @@ -95,12 +100,7 @@ function MapObject.clone_base(obj, owned)
end

function MapObject.is_map_object(t)
if type(t) ~= "table" then
return false
end

local mt = getmetatable(t)
return mt and mt.__index == object.__index and type(t.x) == "number" and type(t.y) == "number" or false
return class.is_an(IMapObject, t) or false
end

-- NOTE: We could have an interface for classes that need special cloning logic.
Expand Down Expand Up @@ -159,7 +159,32 @@ local function cycle_aware_copy(t, cache, uids, first, opts)
if t.__class and class.is_class_or_interface(mt) then
res.__class = mt
end
setmetatable(res,mt)
if mt then
local is_object = mt.__id == "object"
if is_object then
local new_mt = {}

-- see `object.deserialize()`
new_mt.__id = mt.__id
new_mt.__index = object.__index
new_mt.__newindex = object.__newindex
new_mt.__iface = mt.__iface
new_mt.__tostring = object.__tostring
new_mt.__inspect = object.__inspect

-- immutable state
new_mt.uid = nil
new_mt.x = mt.x
new_mt.y = mt.y
new_mt.location = nil
new_mt._id = mt._id
new_mt._type = mt._type

setmetatable(res,new_mt)
else
setmetatable(res,mt)
end
end

return res
end
Expand All @@ -179,13 +204,13 @@ function MapObject.clone(obj, owned, uid_tracker, cache, opts)

local new_object = cycle_aware_copy(obj, cache or {}, uid_tracker, true, opts)

if not preserve_uid then
new_object.uid = uid_tracker:get_next_and_increment()
local mt = getmetatable(new_object)
if preserve_uid then
mt.uid = obj.uid
else
mt.uid = uid_tracker:get_next_and_increment()
end

local mt = getmetatable(obj)
setmetatable(new_object, mt)

local location = obj:get_location()
if owned and class.is_an("api.IMapObject", obj) and class.is_an("api.ILocation", location) then
assert(not preserve_uid, "Cannot preserve UID for owned object")
Expand Down
12 changes: 8 additions & 4 deletions src/api/chara/IChara.lua
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,14 @@ function IChara:build()
self.target = nil

-- HACK fallback now to prevent errors. Races should always declare an image,
-- but they might not in error. However, `image` should still be nil during
-- creation and not have any fallback, in order to potentially override it
-- with the male/female image defaults from the character's race.
self.image = self.image or "elona.chara_race_slime"
-- but they might not in error. However, `image` should still be some special
-- fallback (base.default) during creation, in order to potentially override
-- it with the male/female image defaults from the character's race.
--
-- And we can't set a fallback inside `data:add_type {}` because the race's
-- properties will not overwrite any properties that already exist on the
-- object (even if image = "base.default"), so it has to have a nil fallback.
self.image = self.image or "base.default"

self:reset_ai()

Expand Down
2 changes: 1 addition & 1 deletion src/api/test/Assert.lua
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function Assert.lt(expected, actual)
end
end

function Assert.error(f, err_match, ...)
function Assert.throws_error(f, err_match, ...)
local ok, err = pcall(f, ...)
if ok then
error("expected error, but was successful")
Expand Down
4 changes: 2 additions & 2 deletions src/game/field_logic.lua
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ function field_logic.quickstart()
-- We will want to act as if the character making GUI is active, because that
-- will force the player's equipment to be generated uncursed.
chara_make.set_is_active_override(true)
local ok, err = pcall(function()
local ok, err = xpcall(function()
local me = Chara.create(config.base.quickstart_chara_id, nil, nil, {ownerless=true})
me:emit("base.on_finalize_player")
me:emit("base.on_initialize_player")
Expand All @@ -65,7 +65,7 @@ function field_logic.quickstart()
Skill.apply_class_params(me, me.class)

field_logic.setup_new_game(me)
end)
end, debug.traceback)
chara_make.set_is_active_override(false)

if not ok then
Expand Down
7 changes: 7 additions & 0 deletions src/internal/data/fallbacks.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ data:add {
image = "mod/base/graphic/floor.png"
}

data:add {
_type = "base.chip",
_id = "default",

image = "mod/base/graphic/blank.png"
}

data:add {
_type = "base.chara",
_id = "player",
Expand Down
Loading