Added New Mods
Added - MRAA + Blindside Military Animation Pack - Dynamic Zone Transitions - Corpse Twitching - Artifact Inspection - Animated Lead Box
This commit is contained in:
@@ -0,0 +1,482 @@
|
||||
--[[
|
||||
DYNAMIC ZONE
|
||||
|
||||
Original Author(s)
|
||||
Singustromo <singustromo at disroot.org>
|
||||
|
||||
Edited by
|
||||
|
||||
License
|
||||
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)
|
||||
(https://creativecommons.org/licenses/by-nc-sa/4.0)
|
||||
|
||||
This file provides the rough structure for saving transitions and
|
||||
their pairs - called routes in this context.
|
||||
|
||||
Additional info:
|
||||
tables, functions, userdata and threads are passed around by reference,
|
||||
while numbers, booleans, and nil are passed by value
|
||||
Despite this we reference entries by their index because both tables
|
||||
are saved in m_data. Can't reference them.
|
||||
--]]
|
||||
|
||||
parent = _G["dynamic_zone"]
|
||||
if not (parent and parent.VERSION and parent.VERSION >= 20241224) then return end
|
||||
|
||||
--------------------------
|
||||
-- Dependencies --
|
||||
--------------------------
|
||||
|
||||
-- verified in main script
|
||||
local utils = dynamic_zone_utils
|
||||
local debug = dynamic_zone_debug
|
||||
|
||||
CONST_LOGGING_PREFIX = "Routes"
|
||||
local log = debug.log_register("info", CONST_LOGGING_PREFIX)
|
||||
local log_error = debug.log_register("error", CONST_LOGGING_PREFIX)
|
||||
|
||||
---------------------
|
||||
-- Structure --
|
||||
---------------------
|
||||
|
||||
registered_routes = {}
|
||||
registered_transitions = {
|
||||
--[[
|
||||
[size] = <int>,
|
||||
[transition_name] = {
|
||||
route = route_id, -- index in registeres_routes
|
||||
master = true, -- exception for 1 -> n routes (e.g. truck cemetary)
|
||||
spawned_anomalies = {}
|
||||
},
|
||||
...
|
||||
--]]
|
||||
}
|
||||
|
||||
function route_count()
|
||||
return ((utils.is_table_empty(registered_routes))
|
||||
and 0 or size_table(registered_routes))
|
||||
end
|
||||
|
||||
function transition_count()
|
||||
return (registered_transitions.size or 0)
|
||||
end
|
||||
|
||||
function clear()
|
||||
registered_routes = {}
|
||||
registered_transitions = {}
|
||||
end
|
||||
|
||||
-- first initialization of a route
|
||||
--- @returns id of route (index)
|
||||
function route_create()
|
||||
local id = #registered_routes +1
|
||||
|
||||
registered_routes[id] = {
|
||||
id = id, -- Needed in rare cases
|
||||
members = { }, -- transition names
|
||||
connects = { }, -- level names (game_levels.ltx)
|
||||
blacklisted = false,
|
||||
unlocked = true,
|
||||
blocked = false,
|
||||
block_discovered = false,
|
||||
recently_discovered = false, -- set during emission (controls blip)
|
||||
anomaly_theme = 0,
|
||||
}
|
||||
|
||||
return id
|
||||
end
|
||||
|
||||
function transition_register(transition_name, route_id)
|
||||
if not utils.valid_type{ caller = "transition_register", "str", transition_name,
|
||||
"tbl", registered_routes[route_id] } then return end
|
||||
|
||||
local id = get_story_object_id(transition_name)
|
||||
if (utils.assert_failed(id, "%s is not a Story-ID. Not registered.")) then return end
|
||||
|
||||
local route_members = registered_routes[route_id].members
|
||||
if utils.table_has(route_members, transition_name) then return end
|
||||
|
||||
registered_transitions[transition_name] = {
|
||||
route = route_id,
|
||||
master = false,
|
||||
spawned_anomalies = {}, -- holds the object ids
|
||||
}
|
||||
|
||||
table.insert(route_members, transition_name)
|
||||
|
||||
local index = (registered_transitions.size or 0) +1
|
||||
registered_transitions.size = index
|
||||
return index
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function transition_table_register(tbl, route_id)
|
||||
local registered = {}
|
||||
|
||||
if not utils.valid_type{ caller = "transition_table_register",
|
||||
"tbl", tbl, "int", route_id } then return registered end
|
||||
|
||||
for _, transition_name in pairs(tbl) do
|
||||
if not transition_register(transition_name, route_id) then
|
||||
log_error("Transition '%s' has already been registered!", transition_name)
|
||||
else
|
||||
registered[#registered +1] = transition_name
|
||||
end
|
||||
end
|
||||
|
||||
return registered
|
||||
end
|
||||
|
||||
-- @returns mutable reference
|
||||
function get_route(route_id)
|
||||
return registered_routes[route_id]
|
||||
end
|
||||
|
||||
-- @returns mutable reference
|
||||
function get_transition(transition_name)
|
||||
return registered_transitions[transition_name]
|
||||
end
|
||||
|
||||
function route_exists(route_id)
|
||||
return (nil ~= registered_routes[route_id])
|
||||
end
|
||||
|
||||
function transition_exists(transition_name)
|
||||
return (nil ~= registered_transitions[transition_name])
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
-- Wacky implementation
|
||||
-- TODO: Improve condition declaration and parsing
|
||||
-- Conditions are table entries (route flags) which are or'ed
|
||||
-- attributes are negated with an '!' or can be joined via an and '*'
|
||||
-- e.g. `get_routes{"blocked*!discovered", "!blocked*discovered"}`
|
||||
-- Should only be used with route flags (true, false)
|
||||
-- @returns <table> (route_ids)
|
||||
function get_routes(conditions)
|
||||
if not utils.valid_type{ caller = "get_routes", "tbl", conditions } then return end
|
||||
if utils.is_table_empty(conditions) then return end
|
||||
|
||||
local matches = {}
|
||||
|
||||
local func_body = ""
|
||||
for _, condition in pairs(conditions) do
|
||||
local parsed = condition
|
||||
parsed = parsed:gsub('*!', ' and not ref.')
|
||||
parsed = parsed:gsub('*', ' and ref.')
|
||||
parsed = parsed:gsub('!', 'not ref.')
|
||||
parsed = (string.find(parsed, "^not")) and parsed or "ref." .. parsed
|
||||
func_body = func_body .. "(" .. parsed .. ") or "
|
||||
end
|
||||
func_body = func_body:sub(1, -5) -- remove last ' or '
|
||||
|
||||
local to_eval = "return function(ref) return (" .. func_body .. ") end"
|
||||
local func, err = loadstring(to_eval)
|
||||
if (err) then
|
||||
log_error("Unable to evaluate '%s'\n! %s", to_eval, err)
|
||||
return
|
||||
end
|
||||
|
||||
-- loadstring encapsulates input into another function
|
||||
local check_func = func()
|
||||
|
||||
for route_id, route in iterate_routes(true) do
|
||||
local status, retval = pcall(check_func, route)
|
||||
|
||||
if (status and retval) then
|
||||
matches[#matches +1] = route_id
|
||||
end
|
||||
end
|
||||
|
||||
return matches
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
-- Filters locked and blacklisted routes by default
|
||||
-- @returns name, modifiable route reference
|
||||
function iterate_routes(include_inactive)
|
||||
local index = 0
|
||||
|
||||
return function()
|
||||
index = index + 1
|
||||
|
||||
if not (include_inactive) then
|
||||
while (registered_routes[index] and route_inactive(index)) do
|
||||
index = index + 1
|
||||
end
|
||||
end
|
||||
|
||||
if (index <= #registered_routes) then
|
||||
return index, registered_routes[index]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- @returns name, mutable-reference
|
||||
function iterate_transitions(route_id)
|
||||
local route = get_route(route_id)
|
||||
local members = route and route.members
|
||||
if (not members) then return end
|
||||
|
||||
local index = 0
|
||||
return function()
|
||||
index = index + 1
|
||||
if (index > #members) then return end
|
||||
|
||||
return members[index], get_transition(members[index])
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function set_transition_property(name, property, value)
|
||||
local transition = get_transition(name)
|
||||
if not utils.valid_type{ caller = "set_transition_property",
|
||||
"tbl", transition, "str", property} then return end
|
||||
|
||||
if (type(value) == 'nil') then return end
|
||||
if (type(transition[property]) == "nil") then return end
|
||||
|
||||
transition[property] = value
|
||||
return true
|
||||
end
|
||||
|
||||
-- @returns immutable reference
|
||||
function get_transition_property(name, property)
|
||||
local transition = get_transition(name)
|
||||
if (not transition) then return end
|
||||
|
||||
return transition[property]
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function set_route_property(route_id, property, value)
|
||||
local route = get_route(route_id)
|
||||
if not utils.valid_type{ caller = "set_route_property",
|
||||
"tbl", route, "str", property} then return end
|
||||
|
||||
if (type(value) == 'nil') then return end
|
||||
if (type(route[property]) == "nil") then return end
|
||||
|
||||
route[property] = value
|
||||
return true
|
||||
end
|
||||
|
||||
-- @returns immutable reference
|
||||
function get_route_property(route_id, property)
|
||||
local route = get_route(route_id)
|
||||
if (not route) then return end
|
||||
|
||||
return route[property]
|
||||
end
|
||||
|
||||
function get_route_id(transition_name)
|
||||
local transition = get_transition(transition_name)
|
||||
return (transition and transition.route)
|
||||
end
|
||||
|
||||
-- used when we only have the transition name
|
||||
-- @returns modifyable route reference
|
||||
function get_route_by_transition(gizmo)
|
||||
local transition_name = (type(gizmo) == "number")
|
||||
and get_object_story_id(gizmo) or gizmo
|
||||
|
||||
if (utils.assert_failed(transition_name)) then return end
|
||||
|
||||
local transition = get_transition(transition_name)
|
||||
return (transition and get_route(transition.route))
|
||||
end
|
||||
has_route = get_route_by_transition -- alias for readability's sake
|
||||
|
||||
-- @returns immutable reference
|
||||
function get_route_members(route_id)
|
||||
local route = get_route(route_id)
|
||||
return (route and route.members)
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function route_inactive(route_id)
|
||||
local route = get_route(route_id)
|
||||
if (not route) then return true end
|
||||
|
||||
return (route.blacklisted or (not route.unlocked))
|
||||
end
|
||||
|
||||
-- only really need this, if we do not replace accessible zone
|
||||
function route_unlock(route_id)
|
||||
local route = get_route(route_id)
|
||||
if (not route) then return end
|
||||
|
||||
route.unlocked = true
|
||||
return true
|
||||
end
|
||||
|
||||
function route_unlocked(route_id)
|
||||
local route = get_route(route_id)
|
||||
if (not route) then return end
|
||||
|
||||
return (route.unlocked)
|
||||
end
|
||||
|
||||
function route_locked(route_id)
|
||||
return (not route_unlocked(route_id))
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function route_block(route_id)
|
||||
local route = get_route(route_id)
|
||||
if not (route and route.unlocked) then return end
|
||||
|
||||
route.blocked = true
|
||||
return true
|
||||
end
|
||||
|
||||
function route_unblock(route_id)
|
||||
local route = get_route(route_id)
|
||||
if not (route and route.unlocked and route.blocked) then return end
|
||||
|
||||
route.blocked = false
|
||||
return true
|
||||
end
|
||||
|
||||
function route_blocked(route_id)
|
||||
local route = get_route(route_id)
|
||||
if (not route) then return end
|
||||
|
||||
return (route.blocked)
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
-- We also pass an additional functor to be executed here
|
||||
function route_discover(route_id, functor, ...)
|
||||
local vararg = {...}
|
||||
|
||||
local route = get_route(route_id)
|
||||
if not (route and route.blocked) then return end
|
||||
|
||||
route.block_discovered = true
|
||||
|
||||
if (functor and type(functor) == "function") then
|
||||
functor(unpack(vararg))
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function route_discovered(route_id)
|
||||
local route = get_route(route_id)
|
||||
return (route and route.block_discovered)
|
||||
end
|
||||
|
||||
function route_known_state_differs(route_id)
|
||||
local route = get_route(route_id)
|
||||
return route and (route.blocked ~= route.block_discovered)
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function transition_tostring(transition_name, indentation, indent_str)
|
||||
indentation = (indentation) or 0
|
||||
indent_str = (indent_str) or " "
|
||||
local transition = get_transition(transition_name)
|
||||
local string = string.rep(indent_str, indentation)
|
||||
.. "[" .. transition_name .. "] = {\n"
|
||||
|
||||
for key, value in pairs(transition) do
|
||||
if (key == "route") then
|
||||
goto next_attribute
|
||||
end
|
||||
|
||||
string = string .. string.rep(indent_str, indentation +1)
|
||||
.. key .. " = "
|
||||
|
||||
if (type(value) == "table") then
|
||||
string = string .. "{" .. table.concat(value, ",") .. "}"
|
||||
else
|
||||
string = string .. tostring(value)
|
||||
end
|
||||
string = string .. "\n"
|
||||
|
||||
:: next_attribute ::
|
||||
end
|
||||
|
||||
return string .. string.rep(indent_str, indentation) .. "}\n"
|
||||
end
|
||||
|
||||
-- crude and specific string generation of attribute tables
|
||||
function route_tostring(route_id, include_member_attributes)
|
||||
local route = get_route(route_id)
|
||||
if (not route) then
|
||||
log_error("Printinfo No route with ID #%s !", route_id)
|
||||
return
|
||||
end
|
||||
|
||||
local indentation = " "
|
||||
local string = string.format("[%03d]\n", route_id)
|
||||
for key, value in pairs(route) do
|
||||
if (key == 'id') then goto next_attribute end
|
||||
|
||||
string = string .. indentation .. key .. " = "
|
||||
|
||||
if (type(value) ~= "table") then
|
||||
string = string .. tostring(value)
|
||||
goto continue
|
||||
end
|
||||
|
||||
if ((not include_member_attributes) or key ~= "members") then
|
||||
string = string .. "{" .. table.concat(value, ", ") .. "}"
|
||||
goto continue
|
||||
end
|
||||
|
||||
-- lazy way to serialize member attributes
|
||||
string = string .. "{\n"
|
||||
for _, section in pairs(value) do
|
||||
string = string .. transition_tostring(section, 2, indentation)
|
||||
end
|
||||
string = string .. indentation .. "}"
|
||||
|
||||
:: continue ::
|
||||
string = string .. "\n"
|
||||
|
||||
:: next_attribute ::
|
||||
end
|
||||
|
||||
return string:sub(1, -2) -- remove last new line
|
||||
end
|
||||
|
||||
-- Prints all routes with the specified attributes
|
||||
-- @returns amount of all blocked routes
|
||||
function print_routes(conditions)
|
||||
local output_string = string.format("Route Attributes%s: ",
|
||||
(conditions) and " (" .. table.concat(conditions, ", ") .. ")" or "")
|
||||
|
||||
local routes = get_routes(conditions)
|
||||
if (not routes or utils.is_table_empty(routes)) then return end
|
||||
|
||||
for _, route_id in pairs(routes) do
|
||||
output_string = output_string .. "\n" .. route_tostring(route_id, true)
|
||||
end
|
||||
|
||||
log("%s", output_string)
|
||||
end
|
||||
|
||||
-- Prints all the information agnostic to what type the parameter is
|
||||
function printinfo(gizmo)
|
||||
local output_string = "Attributes of"
|
||||
if route_exists(gizmo) then
|
||||
output_string = string.format("%s route #%s:\n%s",
|
||||
output_string, gizmo, route_tostring(gizmo, true))
|
||||
elseif transition_exists(gizmo) then
|
||||
output_string = string.format("%s transition '%s':\n%s",
|
||||
output_string, gizmo, transition_tostring(gizmo))
|
||||
else return end
|
||||
|
||||
log("%s", output_string)
|
||||
end
|
||||
Reference in New Issue
Block a user