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

Typechecker; typecheck data entries #357

Merged
merged 18 commits into from
Sep 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions editor/emacs/open-nefia.el
Original file line number Diff line number Diff line change
Expand Up @@ -607,14 +607,14 @@ removed. Return the new string. If STRING is nil, return nil."
(defun open-nefia--run-lua (file switches)
(with-current-buffer (get-buffer-create (open-nefia--repl-buffer-name))
(erase-buffer)
(compilation-shell-minor-mode 1)
(let ((process (apply 'start-process (buffer-name)
(current-buffer)
(open-nefia--lua-headless-executable-name)
(append (list (open-nefia--repl-file file)) switches))))
(ansi-color-for-comint-mode-on)
(comint-mode)
(set-process-filter process 'comint-output-filter))))
(set-process-filter process 'comint-output-filter)
(compilation-shell-minor-mode 1))))

(defun open-nefia--start-repl-1 (file &rest switches)
(save-some-buffers (not compilation-ask-about-save)
Expand Down
1 change: 1 addition & 0 deletions src/.luacheckrc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ globals = {
"_IS_LOVEJS",
"love",
"class",
"types",
"inspect",
"fun",
"_ppr",
Expand Down
17 changes: 3 additions & 14 deletions src/api/Activity.lua
Original file line number Diff line number Diff line change
@@ -1,23 +1,12 @@
local Object = require("api.Object")
local data = require("internal.data")

local Activity = {}

function Activity.create(id, params)
local obj = Object.generate_from("base.activity", id)
function Activity.create(_id, params)
local obj = Object.generate_from("base.activity", _id)
Object.finalize(obj)

obj.params = {}
local activity = data["base.activity"]:ensure(id)
for property, ty in pairs(activity.params or {}) do
local value = params[property]
-- everything is implicitly optional for now, until we get a better
-- typechecker
if value ~= nil and type(value) ~= ty then
error(("Activity '%s' requires parameter '%s' of type %s, got '%s'"):format(id, property, ty, value))
end
obj.params[property] = value
end
obj.params = Object.copy_params(obj.proto.params, params, "base.activity", _id)

return obj
end
Expand Down
2 changes: 2 additions & 0 deletions src/api/Enum.lua
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ Enum.AiBehavior = Enum.new("AiBehavior", {

-- >>>>>>>> shade2/init.hsp:825 #enum global fltGoblin=1 ..
Enum.CharaCategory = Enum.new("CharaCategory", {
None = 0,
Goblin = 1,
Orc = 2,
Slime = 3,
Expand Down Expand Up @@ -172,6 +173,7 @@ Enum.CharaCategory = Enum.new("CharaCategory", {

-- >>>>>>>> shade2/init.hsp:815 #enum global fltSp=1 ..
Enum.FltSelect = Enum.new("FltSelect", {
None = 0,
Sp = 1,
Unique = 2,
SpUnique = 3,
Expand Down
31 changes: 2 additions & 29 deletions src/api/Feat.lua
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
--- @module Feat
local data = require("internal.data")
local Log = require("api.Log")

local Event = require("api.Event")
local Map = require("api.Map")
local Object = require("api.Object")
local MapObject = require("api.MapObject")
local ILocation = require("api.ILocation")
local field = require("game.field")
Expand Down Expand Up @@ -55,29 +54,6 @@ function Feat.is_alive(feat, map)
return their_map.uid == map.uid
end

local function copy_params(feat, params)
local proto = data["base.feat"]:ensure(feat._id)
local found = table.set{}
for property, ty in pairs(proto.params or {}) do
local value = params[property]
-- everything is implicitly optional for now, until we get a better
-- typechecker
if value ~= nil and type(value) ~= ty then
error(("Feat '%s' requires parameter '%s' of type %s, got '%s'"):format(feat._id, property, ty, value))
found[property] = true
end
feat.params[property] = value
end

if table.count(found) ~= table.count(params) then
for k, v in pairs(params) do
if not proto.params[k] then
error(("Feat '%s' does not accept parameter '%s'"):format(feat._id, k))
end
end
end
end

--- Creates a new feat. Returns the feat on success, or nil if
--- creation failed.
---
Expand Down Expand Up @@ -134,10 +110,7 @@ function Feat.create(id, x, y, params, where)
}
local feat = MapObject.generate_from("base.feat", id)

feat.params = {}
if params.params then
copy_params(feat, params.params)
end
feat.params = Object.copy_params(feat.proto.params, params.params, "base.feat", id)

if where then
feat = where:take_object(feat, x, y)
Expand Down
2 changes: 1 addition & 1 deletion src/api/InstancedArea.lua
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ end

function InstancedArea:starting_floor()
local archetype = self:archetype()
if archetype and archetype.floors then
if archetype and #archetype.floors > 0 then
return fun.iter(table.keys(archetype.floors)):min()
end

Expand Down
2 changes: 1 addition & 1 deletion src/api/InstancedMap.lua
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ local fallbacks = {
appearance = "",
tileset = "elona.dungeon",
tile_type = 2,
default_ai_calm = 1,
default_ai_calm = "base.calm_roam",
crowd_density = 0,
max_crowd_density = 0,
is_user_map = false,
Expand Down
52 changes: 52 additions & 0 deletions src/api/Object.lua
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ function Object.generate(proto)
local obj = object.deserialize(proto)
assert(obj.proto)

local fallbacks = data.fallbacks[obj._type]
obj:mod_base_with(table.deepcopy(fallbacks), "merge")

return obj
end

Expand Down Expand Up @@ -137,4 +140,53 @@ function Object.mock(mt, tbl)
return obj
end

-- Validates a params table against the type definitions in an object's data
-- entry.
--
-- For example, a door feat will be able to have an unlock difficulty set on it.
-- `proto_params` is the `params` table in the `base.feat`'s data definition and
-- looks like { difficulty = types.number }, and `passed_params` would have been
-- passed to `Feat.create()` and looks something like { difficulty = 25 }.
function Object.copy_params(proto_params, passed_params, _type, _id)
proto_params = proto_params or {}
passed_params = passed_params or {}

local result = {}

local found = table.set{}

for property, entry in pairs(proto_params) do
local value = passed_params[property]
local error_msg = "%s '%s' received invalid value for parameter '%s': %s"
local checker
if types.is_type_checker(entry) then
checker = entry
else
checker = entry.type
if not types.is_type_checker(checker) then
error(("%s '%s' has invalid type checker for parameter '%s': %s"):format(_type, _id, property, checker))
end
if value == nil then
value = entry.default
error_msg = "%s '%s' is missing required parameter '%s': %s"
end
end
local ok, err = types.check(value, checker)
if not ok then
error((error_msg):format(_type, _id, property, err))
end
result[property] = value
end

if table.count(found) ~= table.count(passed_params) then
for k, v in pairs(passed_params) do
if not proto_params[k] then
error(("%s '%s' does not accept parameter '%s'"):format(_type, _id, k))
end
end
end

return result
end

return Object
3 changes: 0 additions & 3 deletions src/api/chara/IChara.lua
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,6 @@ end
--- Finishes initializing this character. All characters must run this
--- function sometime after running pre_build() before being used.
function IChara:build()
local fallbacks = data.fallbacks["base.chara"]
self:mod_base_with(table.deepcopy(fallbacks), "merge")

self.state = "Alive"

self.target = nil
Expand Down
25 changes: 0 additions & 25 deletions src/api/feat/ITrap.lua

This file was deleted.

14 changes: 8 additions & 6 deletions src/api/gui/IUiLayer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,14 @@ function IUiLayer:query(z_order)
success, res, canceled = xpcall(
function()
if config.base.update_unfocused_ui_layers then
local layers = draw.get_layers()
for i = 1, #layers-1 do
local layer = layers[i].layer
-- HACK
if not class.is_an(IHud, layer) then
layer:update(dt, false)
if self:is_querying() then
local layers = draw.get_layers()
for i = 1, #layers-1 do
local layer = layers[i].layer
-- HACK
if not class.is_an(IHud, layer) then
layer:update(dt, false)
end
end
end
end
Expand Down
4 changes: 0 additions & 4 deletions src/api/gui/hud/UiStatusEffects.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,11 @@ end
local function make_status_indicators(_, params, result)
local chara = params.chara

-- TODO ordering
for _, v in data["base.ui_indicator"]:iter() do
if v.indicator then
local raw = v.indicator(chara)
if type(raw) == "table" then
raw.text = I18N.get_optional(raw.text) or raw.text
raw.ordering = v.ordering
result[#result+1] = raw
end
end
Expand All @@ -50,12 +48,10 @@ function UiStatusEffects:set_data(player)

for _, ind in ipairs(raw) do
if type(ind) == "table" then
ind.ordering = ind.ordering or 100000
self.indicators[#self.indicators + 1] = ind
end
end

table.sort(raw, function(a, b) return a.ordering < b.ordering end)
if self.t then
self:calc_max_width()
end
Expand Down
5 changes: 3 additions & 2 deletions src/api/gui/menu/ChooseNpcMenu.lua
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ function ChooseNpcMenu.generate_list(charas, topic)
return true
end

local sort = function(a, b) return a.ordering > b.ordering end

local list = fun.iter(charas):filter(filter_)
:map(function(chara)
local gender = I18N.capitalize(I18N.get("ui.sex3." .. chara:calc("gender")))
Expand All @@ -83,10 +85,9 @@ function ChooseNpcMenu.generate_list(charas, topic)
ordering = chara:calc("level")
}
end)
:into_sorted(sort)
:to_list()

table.insertion_sort(list, function(a, b) return a.ordering > b.ordering end)

return list
end

Expand Down
14 changes: 9 additions & 5 deletions src/api/gui/menu/FeatsMenu.lua
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,10 @@ end
function FeatsMenu.generate_list(chara)
local list = {}

local sort = function(a, b) return (a.ordering or a.elona_id or 0) < (b.ordering or b.elona_id or 0) end

if chara.feats_acquirable > 0 then
list[#list+1] = { type = "header", text = I18N.get("trait.window.available_feats") }

for _, trait in data["base.trait"]:iter():filter(function(t) return t.type == "feat" end):into_sorted(sort) do
for _, trait in data["base.trait"]:iter():filter(function(t) return t.type == "feat" end) do
local level = chara:trait_level(trait._id)
if can_acquire_trait(trait, level, chara) then
local delta = 1
Expand All @@ -160,7 +158,7 @@ function FeatsMenu.generate_list(chara)

list[#list+1] = { type = "header", text = I18N.get("trait.window.feats_and_traits") }

for _, trait in data["base.trait"]:iter():into_sorted(sort) do
for _, trait in data["base.trait"]:iter() do
local level = chara:trait_level(trait._id)
if level ~= 0 then
local color = trait_color(level)
Expand All @@ -169,7 +167,7 @@ function FeatsMenu.generate_list(chara)
end
end

for _, trait_ind in data["base.trait_indicator"]:iter():into_sorted(sort) do
for _, trait_ind in data["base.trait_indicator"]:iter() do
if trait_ind.applies_to(chara) then
local indicator = trait_ind.make_indicator(chara)
list[#list+1] = {
Expand Down Expand Up @@ -317,12 +315,18 @@ function FeatsMenu:update()
-- Because most callbacks expect the given character to be built, you cannot call `refresh` during character making.
self.chara:refresh()
end

if self.chara_make and self.chara.feats_acquirable <= 0 then
return true
end

self.data = FeatsMenu.generate_list(self.chara)
self.pages:set_data(self.data)

local filter = function(_, i)
return i.type == "have" and i.trait._id == item.trait._id
end

local index = self.pages:iter_all_pages():enumerate():filter(filter):nth(1)
self.pages:select(index)

Expand Down
Loading