Skip to content

Commit

Permalink
Merge pull request #3 from theRustyKnife/0.2-dev
Browse files Browse the repository at this point in the history
0 2 dev
  • Loading branch information
theRustyKnife committed Apr 24, 2017
2 parents 6a69def + 9f106f5 commit 6d837e9
Show file tree
Hide file tree
Showing 15 changed files with 748 additions and 679 deletions.
67 changes: 67 additions & 0 deletions circuit-network-switch/FML/config.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
local config = {}


-- GENRAL --
-- The name of the mod FML is installed in - will be used for checking on_configuration_changed and saving some data (needs to be unique)
config.MOD_NAME = "circuit-network-switch"

-- The directory FML is installed in - will be used for adressing graphics
config.FML_PATH = "/FML"

-- If set to true modules won't be loaded using pcall, therefore crashing if there are errors - good for debugging
config.FORCE_LOAD_MODULES = false

-- If set to true then the on_load event will be raised after on_init too
config.ON_LOAD_AFTER_INIT = true

-- The module names with paths that init will attempt to load
config.MODULES_TO_LOAD = {
Object = ".modules.Object",
surface = ".modules.surface",
table = ".modules.table",
data = ".modules.data",
blueprint_data = ".modules.blueprint_data",
format = ".modules.format",
}

-- The name of the global table FML will use. You shouldn't need to change this unless you have a global table with my name for some reason...
-- Changing this will likely break backwards compatibility.
config.GLOBAL_NAME = "therustyknife"


-- DATA --
-- Default icon to use where none was specified and it's mandatory
config.DEFAULT_ICON_PATH = "__core__/graphics/clear.png"

-- The default base for auto-generated items
config.ITEM_BASE = {
type = "item",
icon = DEFAULT_ICON_PATH,
flags = {"goes-to-quickbar"},
subgroup = "transport",
order = "unspecified",
stack_size = 50,
}

-- The default base for auto-generated recipes
config.RECIPE_BASE = {
type = "recipe",
enabled = false,
}

-- Default minable values
config.DEFAULT_MINABLE = {hardness = 0.2, mining_time = 0.5}


-- BLUEPRINT_DATA --
-- The path to the settings definition, nil if none
config.BLUEPRINT_DATA_PATH = nil

-- The name to use for the prototype, will be prefixed with mod name and FML
config.BLUEPRINT_DATA_PROTOTYPE_NAME = "blueprint-data-prototype"

-- The size of the proxy - should be the same as the size of your entity
config.BLUEPRINT_PROXY_SIZE = {{-0.35, -0.35}, {0.35, 0.35}}


return config
Binary file added circuit-network-switch/FML/graphics/trans.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
115 changes: 115 additions & 0 deletions circuit-network-switch/FML/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
local FML_VERSION_CODE = 3
local FML_VERSION_NAME = "0.1.0-alpha.3.0"


local _M = {}


-- requires a module without interfering with any other required modules
-- it is recommended that any FML modules use this instead of the regular require to prevent name/path clashes with the actual mod code
function _M.safe_require(path)
-- store the original value
local t = package.loaded[path]
package.loaded[path] = nil
-- try to load
local res
local status, err = pcall(function() res = require(path); end)
package.loaded[path] = t -- set the value back to the original

-- if loading failed, re-raise the error
if (not status) then error(err); end

return res
end


local config = _M.safe_require(".config")


package.loaded["therustyknife.FML"] = _M -- global access to this instance of FML
package.loaded["therustyknife.FML.config"] = config -- global access to the FML general config


-- this only runs when loaded from control
-- a better way to check for that would be good since if there appears a variable named script in the data loading stage it'll fail
if script and script.on_init then
local global_handlers = {init = {}, load = {}, config_change = {}, fml_config_change = {}, mod_config_change = {}, fml_init = {}} -- handlers to be run on load and init

-- the global table before global is accessible
-- contains functions for registering various event handlers
_M.global = {
loaded = false, -- indicate that the global table is not yet loaded
on_init = function(f) table.insert(global_handlers.init, f) end, -- same as script.on_init
on_load = function(f) table.insert(global_handlers.load, f) end, -- same as script.on_load, except it also runs when on_init does
on_config_change = function(f) table.insert(global_handlers.config_change, f) end, -- same as script.on_configuration_changed
on_fml_config_change = function(f) table.insert(global_handlers.fml_config_change, f) end, -- runs when FML version changes, except when FML was first installed - passes a table containing the old and new versions
on_mod_config_change = function(f) table.insert(global_handlers.mod_config_change, f) end, -- runs whenever there's an entry for the current MOD_NAME in on_config_change - passes the same data as on_config_change
on_fml_init = function(f) table.insert(global_handlers.fml_init, f) end, -- called when FML is first installed (after on_init) - FML's global table is passed as argument
}
-- runs all the handlers from a table passing an argument to them
local function run(handlers, arg) for _, handler in ipairs(handlers) do handler(arg); end end

-- create the global table if it doesn't exist - if everything works properly it should be safe to call this from on_load
local function init_global()
global[config.GLOBAL_NAME] = global[config.GLOBAL_NAME] or {}
_M.global = global[config.GLOBAL_NAME] -- get the reference to our global table

-- this will return a table from the global, creating a new one if it's not present
function _M.global.get(name)
_M.global[name] = _M.global[name] or {}
return _M.global[name]
end
end

script.on_init(function()
init_global()

global.loaded = true -- indicate that the global table is loaded and contains the actual global values
global.fml_version = {code = FML_VERSION_CODE, name = FML_VERSION_NAME}

-- call all the respective handlers
run(global_handlers.init)
run(global_handlers.fml_init, _M.global)
if config.ON_LOAD_AFTER_INIT then run(global_handlers.load); end
end)

script.on_load(function()
run(global_handlers.load)
end)

script.on_configuration_changed(function(data)
run(global_handlers.config_change, data)

if not global.fml_version then -- FML was just added to the mod
global.fml_version = {code = FML_VERSION_CODE, name = FML_VERSION_NAME}
run(global_handlers.fml_init, _M.global)
elseif global.fml_version.code ~= FML_VERSION_CODE then -- FML version has changed
local arg = {
old = global.fml_version,
new = {code = FML_VERSION_CODE, name = FML_VERSION_NAME},
}
global.fml_version = arg.new
run(global_handlers.fml_config_change, arg)
end

if data.mod_changes[config.MOD_NAME] then
init_global()
run(global_handlers.mod_config_change, data)
end
end)
end


-- here we load the modules
for name, path in pairs(config.MODULES_TO_LOAD) do
local function t_load() _M[name] = _M.safe_require(path); end

if config.FORCE_LOAD_MODULES then t_load()
else pcall(t_load)
end

if type(_M[name]) ~= "table" then _M[name] = nil; end
end


return _M
136 changes: 136 additions & 0 deletions circuit-network-switch/FML/modules/data.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
if not data then return nil; end -- requires data to load


local config = require "therustyknife.FML.config"
local FML = require "therustyknife.FML"


local function make_item_for(prototype, properties)
properties = properties or {}
local item = FML.table.deep_copy(properties.base or config.ITEM_BASE) -- get the base item

-- try to extract data from the prototype
item.name = prototype.name
item.place_result = prototype.name
item.icon = prototype.icon or item.icon
item.order = prototype.order or item.order
item.subgroup = prototype.subgroup or item.subgroup

if properties.properties then FML.table.insert_all(item, properties.properties, true, true); end -- override with explicitly defined properties

data:extend{item}

prototype.minable = prototype.minable or FML.table.deep_copy(config.DEFAULT_MINABLE) -- make sure there's a minable table
prototype.minable.result = item.name
end

local function make_recipe_for(prototype, properties)
properties = properties or {}
local recipe = FML.table.deep_copy(properties.base or config.RECIPE_BASE) -- get the base recipe

recipe.name = prototype.name
recipe.result = prototype.name

if properties.properties then FML.table.insert_all(recipe, properties.properties, true, true); end -- override with explicitly defined properties

data:extend{recipe}

if properties.unlock_with then -- add the recipe to the appropriate tech
if data.raw["technology"] and data.raw["technology"][properties.unlock_with] then
table.insert(data.raw["technology"][properties.unlock_with].effects, {type = "unlock-recipe", recipe = recipe.name})
else
error("Can't add recipe " .. recipe.name .. " to the " .. tostring(properties.unlock_with) .. " technology because it does not (yet) exist.")
end
end
end

local auto_gen_mapping = {item = make_item_for, recipe = make_recipe_for}
local function handle_auto_gen(prototype, auto_generate)
if type(auto_generate) == "string" and auto_gen_mapping[auto_generate] then
auto_gen_mapping[auto_generate](prototype)

elseif type(auto_generate) == "table" then
for i, v in pairs(auto_generate) do
if type(v) == "string" and auto_gen_mapping[v] then
auto_gen_mapping[v](prototype)
elseif type(v) == "table" and type(i) == "string" and auto_gen_mapping[i] then
auto_gen_mapping[i](prototype, v)
end
end
end
end


local _M = {}


function _M.inherit(base_type, base_name)
if not base_name then base_name = base_type; end -- use type as name if no name is specified
if type(base_type) ~= "string" or type(base_name) ~= "string" or not data.raw[base_type] or not data.raw[base_type][base_name] then -- only inherit from valid entries
error("can't inherit from type: " .. tostring(base_type) .. ", name: " .. tostring(base_name))
end

return FML.table.deep_copy(data.raw[base_type][base_name])
end

--[[
prototypes format:
prototype definition or {
{
base (optional) = {a table with a full or partial prototype definition, can be created from an exisiting prototype with inherit(base_type, base_name)},
properties (optional) = {
name = string, -- at least this should be overriden, otherwise you're just changing the base prototype (unless you defined base as a new prototype with a unique name)
-- if base is not defined, this table should look like the usual prototype definition
-- there's no checking for validity of the definition - the game's data loader will handle that
...
},
auto_generate = one of the strings in the table or {
"item", "recipe",
item = {base = {a table with a full or partial item definition, can be created with inherit}, properties = {these will have the highest priority}},
recipe = {base = {a table with a full or partial recipe definition, can be created with inherit}, properties = {these will have the highest priority}, unlock_with = string},
}
},
...
}
]]

function _M.make_prototypes(prototypes)
for _, p in ipairs(prototypes) do _M.make_prototype(p); end
end

function _M.make_prototype(prototype)
local res
if prototype.type then -- if it's a regular prototype definition, we add it to data right away
data:extend{prototype}
res = prototype
else
res = FML.table.deep_copy(prototype.base) or {} -- get the base prototype

if prototype.properties and type(prototype.properties == "table") then FML.table.insert_all(res, prototype.properties, true, true); end -- override with explicitly defined properties

if prototype.auto_generate then handle_auto_gen(res, prototype.auto_generate); end -- auto-generate item and recipe

data:extend{res}
end

return data.raw[res.type][res.name]
end

function _M.is_result(recipe, item)
if type(recipe) == "string" then recipe = data.raw["recipe"][recipe]; end
if type(item) == "table" then item = item.name; end
assert(type(recipe) == "table", "Invalid recipe: " .. tostring(recipe))
assert(type(item) == "string", "Invalid item: " .. tostring(item))

if recipe.result and recipe.result == item then return true
elseif recipe.results then
for _, v in pairs(recipe.results) do
if v.name == item then return true; end
end
end

return false
end


return _M
Loading

0 comments on commit 6d837e9

Please sign in to comment.