Move all WML and variable manipulator functions into new wml table
(The old functions in wesnoth and helper are still available, but they are now deprecated.)
This commit is contained in:
parent
b1e4b83014
commit
c0f926e2ed
2 changed files with 309 additions and 235 deletions
|
@ -15,3 +15,256 @@ function wesnoth.confirm(title, msg)
|
|||
end
|
||||
return wesnoth.show_message_box(title, msg, "yes_no", true)
|
||||
end
|
||||
|
||||
wml = {}
|
||||
wml.variable = {}
|
||||
|
||||
--[========[Config Manipulation Functions]========]
|
||||
|
||||
wml.tovconfig = wesnoth.tovconfig
|
||||
wml.tostring = wesnoth.debug
|
||||
|
||||
local function ensure_config(cfg)
|
||||
if type(cfg) == 'table' then
|
||||
return true
|
||||
end
|
||||
if type(cfg) == 'userdata' then
|
||||
if getmetatable(cfg) == 'wml object' then return true end
|
||||
error("Expected a table or wml object but got " .. getmetatable(cfg), 3)
|
||||
else
|
||||
error("Expected a table or wml object but got " .. type(cfg), 3)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--! Returns the first subtag of @a cfg with the given @a name.
|
||||
--! If @a id is not nil, the "id" attribute of the subtag has to match too.
|
||||
--! The function also returns the index of the subtag in the array.
|
||||
function wml.get_child(cfg, name, id)
|
||||
ensure_config(cfg)
|
||||
for i,v in ipairs(cfg) do
|
||||
if v[1] == name then
|
||||
local w = v[2]
|
||||
if not id or w.id == id then return w, i end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--! Returns the nth subtag of @a cfg with the given @a name.
|
||||
--! (Indices start at 1, as always with Lua.)
|
||||
--! The function also returns the index of the subtag in the array.
|
||||
function wml.get_nth_child(cfg, name, n)
|
||||
ensure_config(cfg)
|
||||
for i,v in ipairs(cfg) do
|
||||
if v[1] == name then
|
||||
n = n - 1
|
||||
if n == 0 then return v[2], i end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--! Returns the number of subtags of @a with the given @a name.
|
||||
function wml.child_count(cfg, name)
|
||||
ensure_config(cfg)
|
||||
local n = 0
|
||||
for i,v in ipairs(cfg) do
|
||||
if v[1] == name then
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
return n
|
||||
end
|
||||
|
||||
--! Returns an iterator over all the subtags of @a cfg with the given @a name.
|
||||
function wml.child_range(cfg, tag)
|
||||
ensure_config(cfg)
|
||||
local iter, state, i = ipairs(cfg)
|
||||
local function f(s)
|
||||
local c
|
||||
repeat
|
||||
i,c = iter(s,i)
|
||||
if not c then return end
|
||||
until c[1] == tag
|
||||
return c[2]
|
||||
end
|
||||
return f, state
|
||||
end
|
||||
|
||||
--! Returns an array from the subtags of @a cfg with the given @a name
|
||||
function wml.child_array(cfg, tag)
|
||||
ensure_config(cfg)
|
||||
local result = {}
|
||||
for val in helper.child_range(cfg, tag) do
|
||||
table.insert(result, val)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
--[========[WML Tag Creation Table]========]
|
||||
|
||||
local create_tag_mt = {
|
||||
__metatable = "WML tag builder",
|
||||
__index = function(self, n)
|
||||
return function(cfg) return { n, cfg } end
|
||||
end
|
||||
}
|
||||
|
||||
wml.tag = setmetatable({}, create_tag_mt)
|
||||
|
||||
--[========[Config / Vconfig Unified Handling]========]
|
||||
|
||||
function wml.literal(cfg)
|
||||
if type(cfg) == "userdata" then
|
||||
return cfg.__literal
|
||||
else
|
||||
return cfg or {}
|
||||
end
|
||||
end
|
||||
|
||||
function wml.parsed(cfg)
|
||||
if type(cfg) == "userdata" then
|
||||
return cfg.__parsed
|
||||
else
|
||||
return cfg or {}
|
||||
end
|
||||
end
|
||||
|
||||
function wml.shallow_literal(cfg)
|
||||
if type(cfg) == "userdata" then
|
||||
return cfg.__shallow_literal
|
||||
else
|
||||
return cfg or {}
|
||||
end
|
||||
end
|
||||
|
||||
function wml.shallow_parsed(cfg)
|
||||
if type(cfg) == "userdata" then
|
||||
return cfg.__shallow_parsed
|
||||
else
|
||||
return cfg or {}
|
||||
end
|
||||
end
|
||||
|
||||
--[========[Basic variable access]========]
|
||||
|
||||
wml.variable.get = wesnoth.get_variable
|
||||
wml.variable.set = wesnoth.set_variable
|
||||
wml.variable.get_all = wesnoth.get_all_vars
|
||||
|
||||
--[========[Variable Proxy Table]========]
|
||||
|
||||
local variable_mt = {
|
||||
__metatable = "WML variable proxy"
|
||||
}
|
||||
|
||||
local function get_variable_proxy(k)
|
||||
local v = wesnoth.get_variable(k, true)
|
||||
if type(v) == "table" then
|
||||
v = setmetatable({ __varname = k }, variable_mt)
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
local function set_variable_proxy(k, v)
|
||||
if getmetatable(v) == "WML variable proxy" then
|
||||
v = wesnoth.get_variable(v.__varname)
|
||||
end
|
||||
wesnoth.set_variable(k, v)
|
||||
end
|
||||
|
||||
function variable_mt.__index(t, k)
|
||||
local i = tonumber(k)
|
||||
if i then
|
||||
k = t.__varname .. '[' .. i .. ']'
|
||||
else
|
||||
k = t.__varname .. '.' .. k
|
||||
end
|
||||
return get_variable_proxy(k)
|
||||
end
|
||||
|
||||
function variable_mt.__newindex(t, k, v)
|
||||
local i = tonumber(k)
|
||||
if i then
|
||||
k = t.__varname .. '[' .. i .. ']'
|
||||
else
|
||||
k = t.__varname .. '.' .. k
|
||||
end
|
||||
set_variable_proxy(k, v)
|
||||
end
|
||||
|
||||
local root_variable_mt = {
|
||||
__metatable = "WML variables",
|
||||
__index = function(t, k) return get_variable_proxy(k) end,
|
||||
__newindex = function(t, k, v)
|
||||
if type(v) == "function" then
|
||||
-- User-friendliness when _G is overloaded early.
|
||||
-- FIXME: It should be disabled outside the "preload" event.
|
||||
rawset(t, k, v)
|
||||
else
|
||||
set_variable_proxy(k, v)
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
wml.variable.proxy = setmetatable({}, root_variable_mt)
|
||||
|
||||
--[========[Variable Array Access]========]
|
||||
|
||||
local function resolve_variable_context(ctx, err_hint)
|
||||
if ctx == nil then
|
||||
return {get = wesnoth.get_variable, set = wesnoth.set_variable}
|
||||
elseif type(ctx) == 'number' and ctx > 0 and ctx <= #wesnoth.sides then
|
||||
return resolve_variable_context(wesnoth.sides[ctx])
|
||||
elseif type(ctx) == 'string' then
|
||||
-- TODO: Treat it as a namespace for a global (persistent) variable
|
||||
-- (Need Lua API for accessing them first, though.)
|
||||
elseif getmetatable(ctx) == "unit" then
|
||||
return {
|
||||
get = function(path) return ctx.variables[path] end,
|
||||
set = function(path, val) ctx.variables[path] = val end,
|
||||
}
|
||||
elseif getmetatable(ctx) == "side" then
|
||||
return {
|
||||
get = function(path) return wesnoth.get_side_variable(ctx.side, path) end,
|
||||
set = function(path, val) wesnoth.set_side_variable(ctx.side, path, val) end,
|
||||
}
|
||||
elseif getmetatable(ctx) == "unit variables" or getmetatable(ctx) == "side variables" then
|
||||
return {
|
||||
get = function(path) return ctx[path] end,
|
||||
set = function(path, val) ctx[path] = val end,
|
||||
}
|
||||
end
|
||||
error(string.format("Invalid context for %s: expected nil, side, or unit", err_hint), 3)
|
||||
end
|
||||
|
||||
--! Fetches all the WML container variables with name @a var.
|
||||
--! @returns a table containing all the variables (starting at index 1).
|
||||
function wml.variable.get_array(var, context)
|
||||
context = resolve_variable_context(context, "get_variable_array")
|
||||
local result = {}
|
||||
for i = 1, context.get(var .. ".length") do
|
||||
result[i] = context.get(string.format("%s[%d]", var, i - 1))
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
--! Puts all the elements of table @a t inside a WML container with name @a var.
|
||||
function wml.variable.set_array(var, t, context)
|
||||
context = resolve_variable_context(context, "set_variable_array")
|
||||
context.set(var)
|
||||
for i, v in ipairs(t) do
|
||||
context.set(string.format("%s[%d]", var, i - 1), v)
|
||||
end
|
||||
end
|
||||
|
||||
--! Creates proxies for all the WML container variables with name @a var.
|
||||
--! This is similar to helper.get_variable_array, except that the elements
|
||||
--! can be used for writing too.
|
||||
--! @returns a table containing all the variable proxies (starting at index 1).
|
||||
function wml.get_proxy_array(var)
|
||||
local result = {}
|
||||
for i = 1, wesnoth.get_variable(var .. ".length") do
|
||||
result[i] = get_variable_proxy(string.format("%s[%d]", var, i - 1))
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
|
|
@ -35,82 +35,6 @@ function helper.all_teams()
|
|||
return f, { i = 1 }
|
||||
end
|
||||
|
||||
local function ensure_config(cfg)
|
||||
if type(cfg) == 'table' then
|
||||
return true
|
||||
end
|
||||
if type(cfg) == 'userdata' then
|
||||
if getmetatable(cfg) == 'wml object' then return true end
|
||||
error("Expected a table or wml object but got " .. getmetatable(cfg), 3)
|
||||
else
|
||||
error("Expected a table or wml object but got " .. type(cfg), 3)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--! Returns the first subtag of @a cfg with the given @a name.
|
||||
--! If @a id is not nil, the "id" attribute of the subtag has to match too.
|
||||
--! The function also returns the index of the subtag in the array.
|
||||
function helper.get_child(cfg, name, id)
|
||||
ensure_config(cfg)
|
||||
for i,v in ipairs(cfg) do
|
||||
if v[1] == name then
|
||||
local w = v[2]
|
||||
if not id or w.id == id then return w, i end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--! Returns the nth subtag of @a cfg with the given @a name.
|
||||
--! (Indices start at 1, as always with Lua.)
|
||||
--! The function also returns the index of the subtag in the array.
|
||||
function helper.get_nth_child(cfg, name, n)
|
||||
ensure_config(cfg)
|
||||
for i,v in ipairs(cfg) do
|
||||
if v[1] == name then
|
||||
n = n - 1
|
||||
if n == 0 then return v[2], i end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--! Returns the number of subtags of @a with the given @a name.
|
||||
function helper.child_count(cfg, name)
|
||||
ensure_config(cfg)
|
||||
local n = 0
|
||||
for i,v in ipairs(cfg) do
|
||||
if v[1] == name then
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
return n
|
||||
end
|
||||
|
||||
--! Returns an iterator over all the subtags of @a cfg with the given @a name.
|
||||
function helper.child_range(cfg, tag)
|
||||
ensure_config(cfg)
|
||||
local iter, state, i = ipairs(cfg)
|
||||
local function f(s)
|
||||
local c
|
||||
repeat
|
||||
i,c = iter(s,i)
|
||||
if not c then return end
|
||||
until c[1] == tag
|
||||
return c[2]
|
||||
end
|
||||
return f, state
|
||||
end
|
||||
|
||||
--! Returns an array from the subtags of @a cfg with the given @a name
|
||||
function helper.child_array(cfg, tag)
|
||||
ensure_config(cfg)
|
||||
local result = {}
|
||||
for val in helper.child_range(cfg, tag) do
|
||||
table.insert(result, val)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
--! Modifies all the units satisfying the given @a filter.
|
||||
--! @param vars key/value pairs that need changing.
|
||||
--! @note Usable only during WML actions.
|
||||
|
@ -175,67 +99,15 @@ function helper.move_unit_fake(filter, to_x, to_y)
|
|||
wesnoth.set_variable("LUA_move_unit")
|
||||
end
|
||||
|
||||
local variable_mt = {
|
||||
__metatable = "WML variable proxy"
|
||||
}
|
||||
|
||||
local function get_variable_proxy(k)
|
||||
local v = wesnoth.get_variable(k, true)
|
||||
if type(v) == "table" then
|
||||
v = setmetatable({ __varname = k }, variable_mt)
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
local function set_variable_proxy(k, v)
|
||||
if getmetatable(v) == variable_mt then
|
||||
v = wesnoth.get_variable(v.__varname)
|
||||
end
|
||||
wesnoth.set_variable(k, v)
|
||||
end
|
||||
|
||||
function variable_mt.__index(t, k)
|
||||
local i = tonumber(k)
|
||||
if i then
|
||||
k = t.__varname .. '[' .. i .. ']'
|
||||
else
|
||||
k = t.__varname .. '.' .. k
|
||||
end
|
||||
return get_variable_proxy(k)
|
||||
end
|
||||
|
||||
function variable_mt.__newindex(t, k, v)
|
||||
local i = tonumber(k)
|
||||
if i then
|
||||
k = t.__varname .. '[' .. i .. ']'
|
||||
else
|
||||
k = t.__varname .. '.' .. k
|
||||
end
|
||||
set_variable_proxy(k, v)
|
||||
end
|
||||
|
||||
local root_variable_mt = {
|
||||
-- Metatable that redirects access to wml.variable.proxy
|
||||
local proxy_var_mt = {
|
||||
__metatable = "WML variables",
|
||||
__index = function(t, k) return get_variable_proxy(k) end,
|
||||
__newindex = function(t, k, v)
|
||||
if type(v) == "function" then
|
||||
-- User-friendliness when _G is overloaded early.
|
||||
-- FIXME: It should be disabled outside the "preload" event.
|
||||
rawset(t, k, v)
|
||||
else
|
||||
set_variable_proxy(k, v)
|
||||
end
|
||||
end
|
||||
__index = function(t, k) return wml.variable.proxy[k] end,
|
||||
__newindex = function(t, k, v) wml.variable.proxy[k] = v end,
|
||||
}
|
||||
|
||||
--! Sets the metatable of @a t so that it can be used to access WML variables.
|
||||
--! @return @a t.
|
||||
--! @code
|
||||
--! helper.set_wml_var_metatable(_G)
|
||||
--! my_persistent_variable = 42
|
||||
--! @endcode
|
||||
function helper.set_wml_var_metatable(t)
|
||||
return setmetatable(t, root_variable_mt)
|
||||
return setmetatable(t, proxy_var_mt)
|
||||
end
|
||||
|
||||
local fire_action_mt = {
|
||||
|
@ -255,80 +127,14 @@ function helper.set_wml_action_metatable(t)
|
|||
return setmetatable(t, fire_action_mt)
|
||||
end
|
||||
|
||||
local create_tag_mt = {
|
||||
-- Metatable that redirects to wml.tag
|
||||
local proxy_tag_mt = {
|
||||
__metatable = "WML tag builder",
|
||||
__index = function(t, n)
|
||||
return function(cfg) return { n, cfg } end
|
||||
end
|
||||
__index = function(t, n) return wml.tag[n] end
|
||||
}
|
||||
|
||||
--! Sets the metatable of @a t so that it can be used to create subtags with less brackets.
|
||||
--! @return @a t.
|
||||
--! @code
|
||||
--! T = helper.set_wml_tag_metatable {}
|
||||
--! W.event { name = "new turn", T.message { speaker = "narrator", message = "?" } }
|
||||
--! @endcode
|
||||
function helper.set_wml_tag_metatable(t)
|
||||
return setmetatable(t, create_tag_mt)
|
||||
end
|
||||
|
||||
local function resolve_variable_context(ctx, err_hint)
|
||||
if ctx == nil then
|
||||
return {get = wesnoth.get_variable, set = wesnoth.set_variable}
|
||||
elseif type(ctx) == 'number' and ctx > 0 and ctx <= #wesnoth.sides then
|
||||
return resolve_variable_context(wesnoth.sides[ctx])
|
||||
elseif type(ctx) == 'string' then
|
||||
-- TODO: Treat it as a namespace for a global (persistent) variable
|
||||
-- (Need Lua API for accessing them first, though.)
|
||||
elseif getmetatable(ctx) == "unit" then
|
||||
return {
|
||||
get = function(path) return ctx.variables[path] end,
|
||||
set = function(path, val) ctx.variables[path] = val end,
|
||||
}
|
||||
elseif getmetatable(ctx) == "side" then
|
||||
return {
|
||||
get = function(path) return wesnoth.get_side_variable(ctx.side, path) end,
|
||||
set = function(path, val) wesnoth.set_side_variable(ctx.side, path, val) end,
|
||||
}
|
||||
elseif getmetatable(ctx) == "unit variables" or getmetatable(ctx) == "side variables" then
|
||||
return {
|
||||
get = function(path) return ctx[path] end,
|
||||
set = function(path, val) ctx[path] = val end,
|
||||
}
|
||||
end
|
||||
error(string.format("Invalid context for %s: expected nil, side, or unit", err_hint), 3)
|
||||
end
|
||||
|
||||
--! Fetches all the WML container variables with name @a var.
|
||||
--! @returns a table containing all the variables (starting at index 1).
|
||||
function helper.get_variable_array(var, context)
|
||||
context = resolve_variable_context(context, "get_variable_array")
|
||||
local result = {}
|
||||
for i = 1, context.get(var .. ".length") do
|
||||
result[i] = context.get(string.format("%s[%d]", var, i - 1))
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
--! Puts all the elements of table @a t inside a WML container with name @a var.
|
||||
function helper.set_variable_array(var, t, context)
|
||||
context = resolve_variable_context(context, "set_variable_array")
|
||||
context.set(var)
|
||||
for i, v in ipairs(t) do
|
||||
context.set(string.format("%s[%d]", var, i - 1), v)
|
||||
end
|
||||
end
|
||||
|
||||
--! Creates proxies for all the WML container variables with name @a var.
|
||||
--! This is similar to helper.get_variable_array, except that the elements
|
||||
--! can be used for writing too.
|
||||
--! @returns a table containing all the variable proxies (starting at index 1).
|
||||
function helper.get_variable_proxy_array(var)
|
||||
local result = {}
|
||||
for i = 1, wesnoth.get_variable(var .. ".length") do
|
||||
result[i] = get_variable_proxy(string.format("%s[%d]", var, i - 1))
|
||||
end
|
||||
return result
|
||||
return setmetatable(t, proxy_tag_mt)
|
||||
end
|
||||
|
||||
--! Displays a WML message box with attributes from table @attr and options
|
||||
|
@ -394,38 +200,6 @@ function helper.adjacent_tiles(x, y, with_borders)
|
|||
end
|
||||
end
|
||||
|
||||
function helper.literal(cfg)
|
||||
if type(cfg) == "userdata" then
|
||||
return cfg.__literal
|
||||
else
|
||||
return cfg or {}
|
||||
end
|
||||
end
|
||||
|
||||
function helper.parsed(cfg)
|
||||
if type(cfg) == "userdata" then
|
||||
return cfg.__parsed
|
||||
else
|
||||
return cfg or {}
|
||||
end
|
||||
end
|
||||
|
||||
function helper.shallow_literal(cfg)
|
||||
if type(cfg) == "userdata" then
|
||||
return cfg.__shallow_literal
|
||||
else
|
||||
return cfg or {}
|
||||
end
|
||||
end
|
||||
|
||||
function helper.shallow_parsed(cfg)
|
||||
if type(cfg) == "userdata" then
|
||||
return cfg.__shallow_parsed
|
||||
else
|
||||
return cfg or {}
|
||||
end
|
||||
end
|
||||
|
||||
function helper.rand (possible_values, random_func)
|
||||
random_func = random_func or wesnoth.random
|
||||
assert(type(possible_values) == "table" or type(possible_values) == "string", string.format("helper.rand expects a string or table as parameter, got %s instead", type(possible_values)))
|
||||
|
@ -541,4 +315,51 @@ function helper.shuffle( t, random_func)
|
|||
end
|
||||
end
|
||||
|
||||
-- Compatibility and deprecations
|
||||
|
||||
helper.get_child = helper.deprecate(
|
||||
"helper.get_child is deprecated; use wml.get_child instead", wml.get_child)
|
||||
helper.get_nth_child = helper.deprecate(
|
||||
"helper.get_nth_child is deprecated; use wml.get_nth_child instead", wml.get_nth_child)
|
||||
helper.child_count = helper.deprecate(
|
||||
"helper.child_count is deprecated; use wml.child_count instead", wml.child_count)
|
||||
helper.child_range = helper.deprecate(
|
||||
"helper.child_range is deprecated; use wml.child_range instead", wml.child_range)
|
||||
helper.child_array = helper.deprecate(
|
||||
"helper.child_array is deprecated; use wml.child_array instead", wml.child_array)
|
||||
helper.get_variable_array = helper.deprecate(
|
||||
"helper.get_variable_array is deprecated; use wml.variable.get_array instead",
|
||||
wml.variable.get_array)
|
||||
helper.set_variable_array = helper.deprecate(
|
||||
"helper.set_variable_array is deprecated; use wml.variable.set_array instead",
|
||||
wml.variable.set_array)
|
||||
helper.get_variable_proxy_array = helper.deprecate(
|
||||
"helper.get_variable_proxy_array is deprecated; use wml.variable.get_proxy_array instead",
|
||||
wml.variable.get_proxy_array)
|
||||
helper.literal = helper.deprecate(
|
||||
"helper.literal is deprecated; use wml.literal instead", wml.literal)
|
||||
helper.parsed = helper.deprecate(
|
||||
"helper.parsed is deprecated; use wml.parsed instead", wml.parsed)
|
||||
helper.shallow_literal = helper.deprecate(
|
||||
"helper.shallow_literal is deprecated; use wml.shallow_literal instead", wml.shallow_literal)
|
||||
helper.shallow_parsed = helper.deprecate(
|
||||
"helper.shallow_parsed is deprecated; use wml.shallow_parsed instead", wml.shallow_parsed)
|
||||
helper.set_wml_var_metatable = helper.deprecate(
|
||||
"helper.set_wml_var_metatable is deprecated; use wml.variable.proxy instead " ..
|
||||
"which has the metatable already set", helper.set_wml_var_metatable)
|
||||
helper.set_wml_tag_metatable = helper.deprecate(
|
||||
"helper.set_wml_tag_metatable is deprecated; use wml.tag instead " ..
|
||||
"which has the metatable already set", helper.set_wml_tag_metatable)
|
||||
|
||||
wesnoth.get_variable = helper.deprecate(
|
||||
"wesnoth.get_variable is deprecated; use wml.variable.get instead", wesnoth.get_variable)
|
||||
wesnoth.set_variable = helper.deprecate(
|
||||
"wesnoth.set_variable is deprecated; use wml.variable.set instead", wesnoth.set_variable)
|
||||
wesnoth.get_all_vars = helper.deprecate(
|
||||
"wesnoth.get_all_vars is deprecated; use wml.variable.get_all instead", wesnoth.get_all_vars)
|
||||
wesnoth.tovconfig = helper.deprecate(
|
||||
"wesnoth.tovconfig is deprecated; use wml.tovconfig instead", wesnoth.tovconfig)
|
||||
wesnoth.debug = helper.deprecate(
|
||||
"wesnoth.debug is deprecated; use wml.tostring instead", wesnoth.debug)
|
||||
|
||||
return helper
|
||||
|
|
Loading…
Add table
Reference in a new issue