1
0
mirror of https://github.com/demodude4u/Factorio-FBSR.git synced 2025-02-19 19:59:54 +02:00

Almost everything but trains and other needless things

This commit is contained in:
Weston Rye (Demod) 2017-02-27 01:51:04 -05:00
commit 04c4c4a394
63 changed files with 2971 additions and 0 deletions

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@ -0,0 +1,4 @@
/bin/
/target/
/blueprint-string_4.0.0.zip
/test.png

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>FactorioBlueprintStringRenderer</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@ -0,0 +1,12 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.8

View File

@ -0,0 +1,59 @@
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
sp_cleanup.add_default_serial_version_id=true
sp_cleanup.add_generated_serial_version_id=false
sp_cleanup.add_missing_annotations=true
sp_cleanup.add_missing_deprecated_annotations=true
sp_cleanup.add_missing_methods=false
sp_cleanup.add_missing_nls_tags=false
sp_cleanup.add_missing_override_annotations=true
sp_cleanup.add_missing_override_annotations_interface_methods=true
sp_cleanup.add_serial_version_id=false
sp_cleanup.always_use_blocks=true
sp_cleanup.always_use_parentheses_in_expressions=false
sp_cleanup.always_use_this_for_non_static_field_access=false
sp_cleanup.always_use_this_for_non_static_method_access=false
sp_cleanup.convert_functional_interfaces=false
sp_cleanup.convert_to_enhanced_for_loop=false
sp_cleanup.correct_indentation=false
sp_cleanup.format_source_code=true
sp_cleanup.format_source_code_changes_only=false
sp_cleanup.insert_inferred_type_arguments=false
sp_cleanup.make_local_variable_final=false
sp_cleanup.make_parameters_final=false
sp_cleanup.make_private_fields_final=true
sp_cleanup.make_type_abstract_if_missing_method=false
sp_cleanup.make_variable_declarations_final=true
sp_cleanup.never_use_blocks=false
sp_cleanup.never_use_parentheses_in_expressions=true
sp_cleanup.on_save_use_additional_actions=true
sp_cleanup.organize_imports=true
sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
sp_cleanup.remove_private_constructors=true
sp_cleanup.remove_redundant_type_arguments=false
sp_cleanup.remove_trailing_whitespaces=false
sp_cleanup.remove_trailing_whitespaces_all=true
sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
sp_cleanup.remove_unnecessary_casts=true
sp_cleanup.remove_unnecessary_nls_tags=false
sp_cleanup.remove_unused_imports=false
sp_cleanup.remove_unused_local_variables=false
sp_cleanup.remove_unused_private_fields=true
sp_cleanup.remove_unused_private_members=false
sp_cleanup.remove_unused_private_methods=true
sp_cleanup.remove_unused_private_types=true
sp_cleanup.sort_members=true
sp_cleanup.sort_members_all=false
sp_cleanup.use_anonymous_class_creation=false
sp_cleanup.use_blocks=false
sp_cleanup.use_blocks_only_for_return_and_throw=false
sp_cleanup.use_lambda=true
sp_cleanup.use_parentheses_in_expressions=false
sp_cleanup.use_this_for_non_static_field_access=false
sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
sp_cleanup.use_this_for_non_static_method_access=false
sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true

View File

@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

View File

@ -0,0 +1,3 @@
To setup the lua:
1. download blueprint string mod (4.0.0 at time of writing), extract into lua folder.
2. download numberlua.lua from github/lua-bit-numberlua and put it in a folder named "bit" in the lua folder.

View File

@ -0,0 +1,2 @@
/blueprint-string_4.0.0/
/bit/

View File

@ -0,0 +1,13 @@
require "fixmodule"
require "demod.fbsr.FBSRBridge"
local inspect = require "inspect"
local BlueprintString = require "blueprintstring"
require "dataloader"
require "core.data"
require "base.data"
local blueprint_table = BlueprintString.fromString(fbsr.decoded())
--print(inspect(blueprint_table))
fbsr.result(blueprint_table)

View File

@ -0,0 +1,45 @@
if not module then
function module(modname,...)
local function findtable(tbl,fname)
for key in string.gmatch(fname,"([%w_]+)") do
if key and key~="" then
local val = rawget(tbl,key)
if not val then
local field = {}
tbl[key]=field
tbl = field
elseif type(val)~="table" then
return nil
else
tbl = val
end
end
end
return tbl
end
assert(type(modname)=="string")
local value,modul = package.loaded[modname]
if type(value)~="table" then
modul = findtable(_G,modname)
assert(modul,"name conflict for module '"..modname.."'" )
package.loaded[modname] = modul
else
modul = value
end
local name = modul._NAME
if not name then
modul._M = modul
modul._NAME = modname
modul._PACKAGE = string.match(modname,"([%w%._]*)%.[%w_]*$")
end
local func = debug.getinfo(2,"f").func
debug.setupvalue(func,1,modul)
for _,f in ipairs{...} do f(modul) end
end
function package.seeall(modul)
setmetatable(modul,{__index=_G})
end
end

View File

@ -0,0 +1,341 @@
local inspect ={
_VERSION = 'inspect.lua 3.1.0',
_URL = 'http://github.com/kikito/inspect.lua',
_DESCRIPTION = 'human-readable representations of tables',
_LICENSE = [[
MIT LICENSE
Copyright (c) 2013 Enrique García Cota
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
]]
}
local tostring = tostring
inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end})
inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end})
-- Apostrophizes the string if it has quotes, but not aphostrophes
-- Otherwise, it returns a regular quoted string
local function smartQuote(str)
if str:match('"') and not str:match("'") then
return "'" .. str .. "'"
end
return '"' .. str:gsub('"', '\\"') .. '"'
end
-- \a => '\\a', \0 => '\\0', 31 => '\31'
local shortControlCharEscapes = {
["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n",
["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v"
}
local longControlCharEscapes = {} -- \a => nil, \0 => \000, 31 => \031
for i=0, 31 do
local ch = string.char(i)
if not shortControlCharEscapes[ch] then
shortControlCharEscapes[ch] = "\\"..i
longControlCharEscapes[ch] = string.format("\\%03d", i)
end
end
local function escape(str)
return (str:gsub("\\", "\\\\")
:gsub("(%c)%f[0-9]", longControlCharEscapes)
:gsub("%c", shortControlCharEscapes))
end
local function isIdentifier(str)
return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" )
end
local function isSequenceKey(k, sequenceLength)
return type(k) == 'number'
and 1 <= k
and k <= sequenceLength
and math.floor(k) == k
end
local defaultTypeOrders = {
['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4,
['function'] = 5, ['userdata'] = 6, ['thread'] = 7
}
local function sortKeys(a, b)
local ta, tb = type(a), type(b)
-- strings and numbers are sorted numerically/alphabetically
if ta == tb and (ta == 'string' or ta == 'number') then return a < b end
local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb]
-- Two default types are compared according to the defaultTypeOrders table
if dta and dtb then return defaultTypeOrders[ta] < defaultTypeOrders[tb]
elseif dta then return true -- default types before custom ones
elseif dtb then return false -- custom types after default ones
end
-- custom types are sorted out alphabetically
return ta < tb
end
-- For implementation reasons, the behavior of rawlen & # is "undefined" when
-- tables aren't pure sequences. So we implement our own # operator.
local function getSequenceLength(t)
local len = 1
local v = rawget(t,len)
while v ~= nil do
len = len + 1
v = rawget(t,len)
end
return len - 1
end
local function getNonSequentialKeys(t)
local keys = {}
local sequenceLength = getSequenceLength(t)
for k,_ in pairs(t) do
if not isSequenceKey(k, sequenceLength) then table.insert(keys, k) end
end
table.sort(keys, sortKeys)
return keys, sequenceLength
end
local function getToStringResultSafely(t, mt)
local __tostring = type(mt) == 'table' and rawget(mt, '__tostring')
local str, ok
if type(__tostring) == 'function' then
ok, str = pcall(__tostring, t)
str = ok and str or 'error: ' .. tostring(str)
end
if type(str) == 'string' and #str > 0 then return str end
end
local function countTableAppearances(t, tableAppearances)
tableAppearances = tableAppearances or {}
if type(t) == 'table' then
if not tableAppearances[t] then
tableAppearances[t] = 1
for k,v in pairs(t) do
countTableAppearances(k, tableAppearances)
countTableAppearances(v, tableAppearances)
end
countTableAppearances(getmetatable(t), tableAppearances)
else
tableAppearances[t] = tableAppearances[t] + 1
end
end
return tableAppearances
end
local copySequence = function(s)
local copy, len = {}, #s
for i=1, len do copy[i] = s[i] end
return copy, len
end
local function makePath(path, ...)
local keys = {...}
local newPath, len = copySequence(path)
for i=1, #keys do
newPath[len + i] = keys[i]
end
return newPath
end
local function processRecursive(process, item, path, visited)
if item == nil then return nil end
if visited[item] then return visited[item] end
local processed = process(item, path)
if type(processed) == 'table' then
local processedCopy = {}
visited[item] = processedCopy
local processedKey
for k,v in pairs(processed) do
processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited)
if processedKey ~= nil then
processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited)
end
end
local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited)
setmetatable(processedCopy, mt)
processed = processedCopy
end
return processed
end
-------------------------------------------------------------------
local Inspector = {}
local Inspector_mt = {__index = Inspector}
function Inspector:puts(...)
local args = {...}
local buffer = self.buffer
local len = #buffer
for i=1, #args do
len = len + 1
buffer[len] = args[i]
end
end
function Inspector:down(f)
self.level = self.level + 1
f()
self.level = self.level - 1
end
function Inspector:tabify()
self:puts(self.newline, string.rep(self.indent, self.level))
end
function Inspector:alreadyVisited(v)
return self.ids[v] ~= nil
end
function Inspector:getId(v)
local id = self.ids[v]
if not id then
local tv = type(v)
id = (self.maxIds[tv] or 0) + 1
self.maxIds[tv] = id
self.ids[v] = id
end
return tostring(id)
end
function Inspector:putKey(k)
if isIdentifier(k) then return self:puts(k) end
self:puts("[")
self:putValue(k)
self:puts("]")
end
function Inspector:putTable(t)
if t == inspect.KEY or t == inspect.METATABLE then
self:puts(tostring(t))
elseif self:alreadyVisited(t) then
self:puts('<table ', self:getId(t), '>')
elseif self.level >= self.depth then
self:puts('{...}')
else
if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end
local nonSequentialKeys, sequenceLength = getNonSequentialKeys(t)
local mt = getmetatable(t)
local toStringResult = getToStringResultSafely(t, mt)
self:puts('{')
self:down(function()
if toStringResult then
self:puts(' -- ', escape(toStringResult))
if sequenceLength >= 1 then self:tabify() end
end
local count = 0
for i=1, sequenceLength do
if count > 0 then self:puts(',') end
self:puts(' ')
self:putValue(t[i])
count = count + 1
end
for _,k in ipairs(nonSequentialKeys) do
if count > 0 then self:puts(',') end
self:tabify()
self:putKey(k)
self:puts(' = ')
self:putValue(t[k])
count = count + 1
end
if mt then
if count > 0 then self:puts(',') end
self:tabify()
self:puts('<metatable> = ')
self:putValue(mt)
end
end)
if #nonSequentialKeys > 0 or mt then -- result is multi-lined. Justify closing }
self:tabify()
elseif sequenceLength > 0 then -- array tables have one extra space before closing }
self:puts(' ')
end
self:puts('}')
end
end
function Inspector:putValue(v)
local tv = type(v)
if tv == 'string' then
self:puts(smartQuote(escape(v)))
elseif tv == 'number' or tv == 'boolean' or tv == 'nil' then
self:puts(tostring(v))
elseif tv == 'table' then
self:putTable(v)
else
self:puts('<',tv,' ',self:getId(v),'>')
end
end
-------------------------------------------------------------------
function inspect.inspect(root, options)
options = options or {}
local depth = options.depth or math.huge
local newline = options.newline or '\n'
local indent = options.indent or ' '
local process = options.process
if process then
root = processRecursive(process, root, {}, {})
end
local inspector = setmetatable({
depth = depth,
level = 0,
buffer = {},
ids = {},
maxIds = {},
newline = newline,
indent = indent,
tableAppearances = countTableAppearances(root)
}, Inspector_mt)
inspector:putValue(root)
return table.concat(inspector.buffer)
end
setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end })
return inspect

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

View File

@ -0,0 +1,44 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>demod.fbsr</groupId>
<artifactId>FactorioBlueprintStringRenderer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
</repositories>
<dependencies>
<dependency>
<groupId>org.luaj</groupId>
<artifactId>luaj-jse</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>net.sf.jopt-simple</groupId>
<artifactId>jopt-simple</artifactId>
<version>6.0-alpha-1</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20160810</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,95 @@
package demod.fbsr;
import java.awt.geom.Point2D;
import java.awt.geom.Point2D.Double;
import java.awt.geom.Rectangle2D;
import org.luaj.vm2.LuaTable;
public class BlueprintEntity {
public static enum Direction {
NORTH(0, -1), //
NORTHEAST(1, -1), //
EAST(1, 0), //
SOUTHEAST(1, 1), //
SOUTH(0, 1), //
SOUTHWEST(-1, 1), //
WEST(-1, 0), //
NORTHWEST(-1, -1);
public static Direction fromCardinal(int cardinal) {
return values()[cardinal * 2];
}
private final int dx;
private final int dy;
private Direction(int dx, int dy) {
this.dx = dx;
this.dy = dy;
}
public Direction back() {
return rotate(4);
}
public int cardinal() {
return ordinal() / 2;
}
public Direction left() {
return rotate(-2);
}
public Point2D.Double offset(Point2D.Double pos) {
return new Point2D.Double(pos.x + dx, pos.y + dy);
}
public Double offset(Point2D.Double pos, double distance) {
return new Point2D.Double(pos.x + distance * dx, pos.y + distance * dy);
}
public Rectangle2D.Double offset(Rectangle2D.Double rect, double distance) {
return new Rectangle2D.Double(rect.x + distance * dx, rect.y + distance * dy, rect.width, rect.height);
}
public Direction right() {
return rotate(2);
}
public Direction rotate(int deltaIndex) {
Direction[] values = values();
return values[(((ordinal() + deltaIndex) % values.length) + values.length) % values.length];
}
}
private final LuaTable lua;
private final String name;
private final Double position;
private final Direction direction;
public BlueprintEntity(LuaTable entityLua) {
this.lua = entityLua;
LuaTable positionLua = entityLua.get("position").checktable();
this.name = entityLua.get("name").toString();
this.position = new Point2D.Double(positionLua.get("x").todouble(), positionLua.get("y").todouble());
this.direction = Direction.values()[entityLua.get("direction").optint(0)];
}
public Direction getDirection() {
return direction;
}
public String getName() {
return name;
}
public Double getPosition() {
return position;
}
public LuaTable lua() {
return lua;
}
}

View File

@ -0,0 +1,27 @@
package demod.fbsr;
import org.luaj.vm2.LuaTable;
public class DataPrototype {
private final LuaTable lua;
private final String name;
private final String type;
public DataPrototype(LuaTable lua, String name, String type) {
this.lua = lua;
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public String getType() {
return type;
}
public LuaTable lua() {
return lua;
}
}

View File

@ -0,0 +1,51 @@
package demod.fbsr;
import java.util.HashMap;
import java.util.Map;
import org.luaj.vm2.LuaTable;
public class DataTable {
private final Map<String, DataPrototype> entities = new HashMap<>();
private final Map<String, DataPrototype> items = new HashMap<>();
private final Map<String, DataPrototype> recipes = new HashMap<>();
private final Map<String, DataPrototype> fluids = new HashMap<>();
public DataTable(TypeHiearchy typeHiearchy, LuaTable dataLua) {
LuaTable rawLua = dataLua.get("raw").checktable();
Utils.forEach(rawLua, v -> {
Utils.forEach(v.checktable(), protoLua -> {
// LuaUtils.debugPrintTableStructure("", protoLua);
// System.exit(1);
String type = protoLua.get("type").toString();
String name = protoLua.get("name").toString();
DataPrototype prototype = new DataPrototype(protoLua.checktable(), name, type);
if (typeHiearchy.isAssignable("item", type)) {
items.put(name, prototype);
} else if (typeHiearchy.isAssignable("recipe", type)) {
recipes.put(name, prototype);
} else if (typeHiearchy.isAssignable("entity", type)) {
entities.put(name, prototype);
} else if (typeHiearchy.isAssignable("fluid", type)) {
fluids.put(name, prototype);
}
});
});
}
public Map<String, DataPrototype> getEntities() {
return entities;
}
public Map<String, DataPrototype> getFluids() {
return fluids;
}
public Map<String, DataPrototype> getItems() {
return items;
}
public Map<String, DataPrototype> getRecipes() {
return recipes;
}
}

View File

@ -0,0 +1,43 @@
package demod.fbsr;
import java.util.function.Consumer;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.OneArgFunction;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.ZeroArgFunction;
public class FBSRBridge extends TwoArgFunction {
private static String blueprintDecoded;
private static Consumer<LuaTable> blueprintListener;
public static void setBlueprintDecoded(String blueprintDecoded) {
FBSRBridge.blueprintDecoded = blueprintDecoded;
}
public static void setBlueprintListener(Consumer<LuaTable> blueprintListener) {
FBSRBridge.blueprintListener = blueprintListener;
}
@Override
public LuaValue call(LuaValue modname, LuaValue env) {
LuaValue library = tableOf();
library.set("decoded", new ZeroArgFunction() {
@Override
public LuaValue call() {
return valueOf(blueprintDecoded);
}
});
library.set("result", new OneArgFunction() {
@Override
public LuaValue call(LuaValue arg) {
blueprintListener.accept(arg.checktable());
return LuaValue.NIL;
}
});
env.set("fbsr", library);
return library;
}
}

View File

@ -0,0 +1,314 @@
package demod.fbsr;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Desktop;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.GZIPInputStream;
import javax.imageio.ImageIO;
import javax.swing.JOptionPane;
import org.json.JSONException;
import org.json.JSONObject;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.BaseLib;
import org.luaj.vm2.lib.DebugLib;
import org.luaj.vm2.lib.ResourceFinder;
import org.luaj.vm2.lib.jse.JsePlatform;
import demod.fbsr.render.Renderer;
import demod.fbsr.render.TypeRendererFactory;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
public class FBSRMain {
public static final String DEFAULT_FACTORIO = "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Factorio";
public static final String DEFAULT_LUA = "lua";
private static final String TYPE_SCHEMA = "https://raw.githubusercontent.com/jcranmer/factorio-tools/master/schema.json";
private static Map<String, BufferedImage> modImageCache = new HashMap<>();
private static File factorio;
private static List<Point2D.Double> debugPoints = new ArrayList<>();
public static synchronized void debugPoint(Point2D.Double point) {
debugPoints.add(point);
}
private static String decode(String blueprint) throws IOException {
byte[] bin = Base64.getDecoder().decode(blueprint);
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new GZIPInputStream(new ByteArrayInputStream(bin))))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
return sb.toString();
}
}
public static BufferedImage getModImage(LuaValue value) {
String path = value.toString();
return modImageCache.computeIfAbsent(path, p -> {
String firstSegment = path.split("\\/")[0];
String mod = firstSegment.substring(2, firstSegment.length() - 2);
File modFolder = new File(factorio, "data/" + mod);
try {
return ImageIO.read(new File(modFolder, path.replace(firstSegment, "").substring(1)));
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
});
}
public static void main(String[] args) throws InterruptedException, IOException {
setupWorkingDirectory();
OptionParser optionParser = new OptionParser();
optionParser.accepts("?", "Shows help.").forHelp();
OptionSpec<File> factorioOption = optionParser.accepts("factorio", "Location of the factorio folder.")
.withRequiredArg().ofType(File.class).defaultsTo(new File(DEFAULT_FACTORIO));
OptionSpec<File> luaOption = optionParser.accepts("lua", "Location of the needed lua files.").withRequiredArg()
.ofType(File.class).defaultsTo(new File(DEFAULT_LUA));
OptionSpec<File> inputOption = optionParser.accepts("input", "Location of the blueprint file.")
.withRequiredArg().ofType(File.class);
OptionSpec<File> outputOption = optionParser.accepts("output", "Location to save the output PNG file.")
.withRequiredArg().ofType(File.class).defaultsTo(new File("output.png"));
optionParser.accepts("show", "Open image immediately after being saved.");
OptionSet options = null;
try {
options = optionParser.parse(args);
} catch (UnsupportedOperationException e1) {
System.err.println(e1.getMessage());
optionParser.printHelpOn(System.err);
System.exit(0);
}
if (options.has("?")) {
optionParser.printHelpOn(System.out);
System.exit(0);
}
factorio = options.valueOf(factorioOption);
File lua = options.valueOf(luaOption);
boolean hasInput = options.has(inputOption);
File inputBlueprint = hasInput ? options.valueOf(inputOption) : null;
File outputImage = options.valueOf(outputOption);
boolean showImage = options.has("show");
File[] luaFolders = new File[] { //
lua, //
new File(factorio, "data/core/luaLib"), //
new File(factorio, "data"), //
new File(factorio, "data/core"), //
new File(factorio, "data/base"), //
new File(lua, "blueprint-string_4.0.0"), //
new File(lua, "blueprint-string_4.0.0/blueprintstring"),//
};
String luaPath = Arrays.stream(luaFolders).map(f -> f.getAbsolutePath() + File.separator + "?.lua")
.collect(Collectors.joining(";")).replace('\\', '/');
// System.out.println("LUA_PATH: " + luaPath);
Globals globals = JsePlatform.standardGlobals();
globals.load(new BaseLib());
globals.load(new DebugLib());
globals.load(new StringReader("package.path = package.path .. ';" + luaPath + "'"), "initLuaPath").call();
globals.finder = new ResourceFinder() {
@Override
public InputStream findResource(String filename) {
File file = new File(filename);
// System.out.println(filename + "? " + file.exists());
try {
return file.exists() ? new FileInputStream(file) : null;
} catch (FileNotFoundException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
};
File blueprintFile = inputBlueprint;
if (!hasInput) {
String[] testBlueprintFiles = Stream.of(new File(".").listFiles()).map(File::getName)
.filter(n -> n.endsWith(".blueprint")).toArray(String[]::new);
if (testBlueprintFiles.length == 0) {
System.err.println(
"You have not specified an input file, and there aren't any test files in this folder!");
System.exit(-1);
}
Object choice = JOptionPane.showInputDialog(null, "Test file:", "FBSR", JOptionPane.QUESTION_MESSAGE, null,
testBlueprintFiles, testBlueprintFiles[0]);
if (choice == null) {
return;
}
blueprintFile = new File(choice.toString());
}
String blueprintEncoded = Files.readAllLines(blueprintFile.toPath()).stream().collect(Collectors.joining());
String blueprintDecoded = decode(blueprintEncoded);
// System.out.println(blueprintDecoded);
TypeHiearchy typeHiearchy = new TypeHiearchy(readJsonFromStream(new URL(TYPE_SCHEMA).openStream()));
FBSRBridge.setBlueprintDecoded(blueprintDecoded);
FBSRBridge.setBlueprintListener(blueprint -> {
List<BlueprintEntity> blueprintEntities = new ArrayList<>();
Utils.forEach(blueprint.get("entities").checktable(),
v -> blueprintEntities.add(new BlueprintEntity(v.checktable())));
DataTable dataTable = new DataTable(typeHiearchy, globals.get("data").checktable());
Map<String, DataPrototype> entities = dataTable.getEntities();
class RenderingTuple {
BlueprintEntity entity;
DataPrototype prototype;
TypeRendererFactory factory;
}
List<RenderingTuple> renderingTuples = new ArrayList<RenderingTuple>();
for (BlueprintEntity entity : blueprintEntities) {
RenderingTuple tuple = new RenderingTuple();
tuple.entity = entity;
tuple.prototype = entities.get(entity.getName());
if (tuple.prototype == null) {
System.err.println("Cant find prototype for " + entity.getName());
continue;
}
tuple.factory = TypeRendererFactory.forType(tuple.prototype.getType());
renderingTuples.add(tuple);
}
WorldMap worldState = new WorldMap();
renderingTuples.forEach(t -> t.factory.populateWorldMap(worldState, dataTable, t.entity, t.prototype));
List<Renderer> renderers = new ArrayList<>();
renderingTuples.forEach(
t -> t.factory.createRenderers(renderers::add, worldState, dataTable, t.entity, t.prototype));
BufferedImage render = renderImage((int) Math.round(TypeRendererFactory.tileSize), renderers);
try {
ImageIO.write(render, "PNG", outputImage);
System.out.println(outputImage.getAbsolutePath());
if (showImage) {
Desktop.getDesktop().open(outputImage);
}
} catch (IOException e) {
e.printStackTrace();
}
});
globals.load(new FileReader(new File(lua, "fbsr.lua")), "fbsr").call();
}
@SuppressWarnings("resource")
private static JSONObject readJsonFromStream(InputStream in) throws JSONException, IOException {
return new JSONObject(new Scanner(in, "UTF-8").useDelimiter("\\A").next());
}
private static BufferedImage renderImage(int tileSize, List<Renderer> renderers) {
Area area = new Area();
renderers.forEach(r -> area.add(new Area(r.getBounds())));
Rectangle2D bounds = area.getBounds2D();
bounds.setFrameFromDiagonal(Math.floor(bounds.getMinX()) - 1, Math.floor(bounds.getMinY()) - 1,
Math.ceil(bounds.getMaxX()) + 1, Math.ceil(bounds.getMaxY()) + 1);
BufferedImage image = new BufferedImage((int) (bounds.getWidth() * tileSize),
(int) (bounds.getHeight() * tileSize), BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
g.scale(image.getWidth() / bounds.getWidth(), image.getHeight() / bounds.getHeight());
g.translate(-bounds.getX(), -bounds.getY());
g.setColor(new Color(40, 40, 40));
g.fill(bounds);
g.setStroke(new BasicStroke((float) (3 / TypeRendererFactory.tileSize)));
g.setColor(new Color(60, 60, 60));
g.setFont(new Font("Courier New", Font.PLAIN, 1).deriveFont(0.3f));
for (double x = Math.round(bounds.getMinX()); x <= bounds.getMaxX(); x++) {
g.draw(new Line2D.Double(x, bounds.getMinY(), x, bounds.getMaxY()));
g.drawString("" + (int) x, (float) x + 0.1f, (float) (bounds.getMinY() + 0.35f));
}
for (double y = Math.round(bounds.getMinY()); y <= bounds.getMaxY(); y++) {
g.draw(new Line2D.Double(bounds.getMinX(), y, bounds.getMaxX(), y));
g.drawString("" + (int) y, (float) (bounds.getMinX() + 0.1f), (float) y + 0.35f);
}
renderers.stream().sorted((r1, r2) -> {
int ret;
Rectangle2D.Double b1 = r1.getBounds();
Rectangle2D.Double b2 = r2.getBounds();
ret = Double.compare(b1.getMinY(), b2.getMinY());
if (ret != 0) {
return ret;
}
ret = Double.compare(b1.getMinX(), b2.getMinX());
if (ret != 0) {
return ret;
}
ret = r1.getLayer().compareTo(r2.getLayer());
return ret;
}).forEach(r -> r.render(g));
g.setColor(Color.magenta);
for (Point2D.Double debugPoint : debugPoints) {
g.draw(new Ellipse2D.Double(debugPoint.x - 0.25, debugPoint.y - 0.25, 0.5, 0.5));
}
g.dispose();
return image;
}
private static void setupWorkingDirectory() {
String className = FBSRMain.class.getName().replace('.', '/');
String classJar = FBSRMain.class.getResource("/" + className + ".class").toString();
if (classJar.startsWith("jar:")) {
try {
File jarFolder = new File(
FBSRMain.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath())
.getParentFile();
// System.out.println("Jar Folder: " +
// jarFolder.getAbsolutePath());
System.setProperty("user.dir", jarFolder.getAbsolutePath());
} catch (URISyntaxException e) {
e.printStackTrace();
System.exit(-1);
}
}
}
}

View File

@ -0,0 +1,28 @@
package demod.fbsr;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONObject;
public class TypeHiearchy {
private final Map<String, String> parents = new HashMap<>();
public TypeHiearchy(JSONObject json) {
for (String key : json.keySet()) {
JSONObject typeJson = json.getJSONObject(key);
parents.put(key, typeJson.optString("parent"));
}
}
public boolean isAssignable(String type, String subType) {
String checkType = subType;
while (checkType != null) {
if (type.equals(checkType)) {
return true;
}
checkType = parents.get(checkType);
}
return false;
}
}

View File

@ -0,0 +1,120 @@
package demod.fbsr;
import java.awt.geom.Point2D;
import java.awt.geom.Point2D.Double;
import java.awt.geom.Rectangle2D;
import java.util.Comparator;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
public final class Utils {
public static int compareRange(double min1, double max1, double min2, double max2) {
if (max1 <= min2) {
return -1;
}
if (max2 <= min1) {
return 1;
}
return 0;
}
public static void debugPrintTable(LuaValue table) {
debugPrintTable("", table);
}
private static void debugPrintTable(String prefix, LuaValue table) {
forEach(table, (k, v) -> {
if (v.istable()) {
debugPrintTable(prefix + k + ".", v.checktable());
} else {
System.out.println(prefix + k + " = " + v);
}
});
}
public static void forEach(LuaValue table, BiConsumer<LuaValue, LuaValue> consumer) {
LuaValue k = LuaValue.NIL;
while (true) {
Varargs n = table.next(k);
if ((k = n.arg1()).isnil())
break;
LuaValue v = n.arg(2);
consumer.accept(k, v);
}
}
public static void forEach(LuaValue table, Consumer<LuaValue> consumer) {
LuaValue k = LuaValue.NIL;
while (true) {
Varargs n = table.next(k);
if ((k = n.arg1()).isnil())
break;
LuaValue v = n.arg(2);
consumer.accept(v);
}
}
public static Double parsePoint2D(LuaValue luaValue) {
if (luaValue.isnil()) {
return new Point2D.Double();
}
return new Point2D.Double(luaValue.get(1).checkdouble(), luaValue.get(2).checkdouble());
}
public static Rectangle2D.Double parseRectangle(LuaValue value) {
LuaTable table = value.checktable();
LuaValue p1 = table.get(1);
LuaValue p2 = table.get(2);
double x1 = p1.get(1).checkdouble();
double y1 = p1.get(2).checkdouble();
double x2 = p2.get(1).checkdouble();
double y2 = p2.get(2).checkdouble();
return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
}
public static <T> void sortWithNonTransitiveComparator(T[] array, Comparator<T> comparator) {
@SuppressWarnings("unchecked")
T[] tmp = (T[]) new Object[array.length];
sortWithNonTransitiveComparator_MergeSort(array, comparator, tmp, 0, array.length - 1);
}
private static <T> void sortWithNonTransitiveComparator_Merge(T[] a, Comparator<T> comparator, T[] tmp, int left,
int right, int rightEnd) {
int leftEnd = right - 1;
int k = left;
int num = rightEnd - left + 1;
while (left <= leftEnd && right <= rightEnd)
if (comparator.compare(a[left], a[right]) <= 0)
tmp[k++] = a[left++];
else
tmp[k++] = a[right++];
while (left <= leftEnd) // Copy rest of first half
tmp[k++] = a[left++];
while (right <= rightEnd) // Copy rest of right half
tmp[k++] = a[right++];
// Copy tmp back
for (int i = 0; i < num; i++, rightEnd--)
a[rightEnd] = tmp[rightEnd];
}
private static <T> void sortWithNonTransitiveComparator_MergeSort(T[] a, Comparator<T> comparator, T[] tmp,
int left, int right) {
if (left < right) {
int center = (left + right) / 2;
sortWithNonTransitiveComparator_MergeSort(a, comparator, tmp, left, center);
sortWithNonTransitiveComparator_MergeSort(a, comparator, tmp, center + 1, right);
sortWithNonTransitiveComparator_Merge(a, comparator, tmp, left, center + 1, right);
}
}
private Utils() {
}
}

View File

@ -0,0 +1,93 @@
package demod.fbsr;
import java.awt.geom.Point2D;
import java.util.Optional;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import demod.fbsr.BlueprintEntity.Direction;
public class WorldMap {
private final Table<Integer, Integer, Direction> belts = HashBasedTable.create();
private final Table<Integer, Integer, Integer> pipes = HashBasedTable.create();
private final Table<Integer, Integer, Object> walls = HashBasedTable.create();
private final Table<Integer, Integer, Boolean> gates = HashBasedTable.create();
private int flag(Direction facing) {
return 1 << facing.cardinal();
}
public Optional<Direction> getBelt(Point2D.Double pos) {
int kr = keyOf(pos.x);
int kc = keyOf(pos.y);
return Optional.ofNullable(belts.get(kr, kc));
}
public boolean isHorizontalGate(Point2D.Double pos) {
int kr = keyOf(pos.x);
int kc = keyOf(pos.y);
return gates.contains(kr, kc) && (gates.get(kr, kc) == false);
}
public boolean isPipe(Point2D.Double pos, Direction facing) {
int kr = keyOf(pos.x);
int kc = keyOf(pos.y);
return pipes.contains(kr, kc) && (pipes.get(kr, kc) & flag(facing)) > 0;
}
public boolean isVerticalGate(Point2D.Double pos) {
int kr = keyOf(pos.x);
int kc = keyOf(pos.y);
return gates.contains(kr, kc) && (gates.get(kr, kc) == true);
}
public boolean isWall(Point2D.Double pos) {
int kr = keyOf(pos.x);
int kc = keyOf(pos.y);
return walls.contains(kr, kc);
}
private int keyOf(double pos) {
return (int) Math.round(pos - 0.5);
}
public void setBelt(Point2D.Double pos, Direction direction) {
int kr = keyOf(pos.x);
int kc = keyOf(pos.y);
belts.put(kr, kc, direction);
}
public void setHorizontalGate(Point2D.Double pos) {
int kr = keyOf(pos.x);
int kc = keyOf(pos.y);
gates.put(kr, kc, false);
}
public void setPipe(Point2D.Double pos, Direction... facings) {
int kr = keyOf(pos.x);
int kc = keyOf(pos.y);
int flags = 0;
if (facings.length == 0) {
flags = 0b1111;
} else {
for (Direction facing : facings) {
flags |= flag(facing);
}
}
pipes.put(kr, kc, flags);
}
public void setVerticalGate(Point2D.Double pos) {
int kr = keyOf(pos.x);
int kc = keyOf(pos.y);
gates.put(kr, kc, true);
}
public void setWall(Point2D.Double pos) {
int kr = keyOf(pos.x);
int kc = keyOf(pos.y);
walls.put(kr, kc, pos);
}
}

View File

@ -0,0 +1,17 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class AccumulatorRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
Sprite sprite = getSpriteFromAnimation(prototype.lua().get("picture"));
register.accept(spriteRenderer(sprite, entity, prototype));
}
}

View File

@ -0,0 +1,23 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import org.luaj.vm2.LuaValue;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class AmmoTurretRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
Sprite baseSprite = getSpriteFromAnimation(prototype.lua().get("base_picture").get("layers").get(1));
register.accept(spriteRenderer(baseSprite, entity, prototype));
LuaValue turretLayers = prototype.lua().get("folded_animation").get("layers");
Sprite turretSprite = getSpriteFromAnimation(turretLayers.get(1));
turretSprite.source.y += turretSprite.source.height * entity.getDirection().cardinal();
register.accept(spriteRenderer(turretSprite, entity, prototype));
}
}

View File

@ -0,0 +1,34 @@
package demod.fbsr.render;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class ArithmeticCombinatorRendering extends TypeRendererFactory {
public static final Map<String, String> operationSprites = new HashMap<>();
static {
operationSprites.put("+", "plus_symbol_sprites");
operationSprites.put("-", "minus_symbol_sprites");
operationSprites.put("*", "multiply_symbol_sprites");
operationSprites.put("/", "divide_symbol_sprites");
}
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
Sprite sprite = getSpriteFromAnimation(
prototype.lua().get("sprites").get(entity.getDirection().name().toLowerCase()));
Sprite operatorSprite = getSpriteFromAnimation(prototype.lua()
.get(operationSprites.get(
entity.lua().get("control_behavior").get("arithmetic_conditions").get("operation").toString()))
.get(entity.getDirection().name().toLowerCase()));
register.accept(spriteRenderer(sprite, entity, prototype));
register.accept(spriteRenderer(operatorSprite, entity, prototype));
}
}

View File

@ -0,0 +1,110 @@
package demod.fbsr.render;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import org.luaj.vm2.LuaValue;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.BlueprintEntity.Direction;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.FBSRMain;
import demod.fbsr.Utils;
import demod.fbsr.WorldMap;
import demod.fbsr.render.Renderer.Layer;
public class AssemblingMachineRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
super.createRenderers(register, map, dataTable, entity, prototype);
Sprite sprite = new Sprite();
LuaValue recipe = entity.lua().get("recipe");
if (!recipe.isnil()) {
DataPrototype protoRecipe = dataTable.getRecipes().get(recipe.toString());
if (protoRecipe.lua().get("icon") != LuaValue.NIL) {
sprite.image = FBSRMain.getModImage(protoRecipe.lua().get("icon"));
} else {
String name;
if (protoRecipe.lua().get("results") != LuaValue.NIL) {
name = protoRecipe.lua().get("results").get(1).get("name").toString();
} else {
name = protoRecipe.lua().get("result").toString();
}
DataPrototype protoProduct = dataTable.getItems().get(name);
if (protoProduct == null) {
protoProduct = dataTable.getFluids().get(name);
}
sprite.image = FBSRMain.getModImage(protoProduct.lua().get("icon"));
}
sprite.source = new Rectangle(0, 0, sprite.image.getWidth(), sprite.image.getHeight());
sprite.bounds = new Rectangle2D.Double(-0.7, -1.0, 1.4, 1.4);
Renderer delegate = spriteRenderer(sprite, entity, prototype);
register.accept(new Renderer(Layer.OVERLAY2, delegate.getBounds()) {
@Override
public void render(Graphics2D g) {
g.setColor(new Color(0, 0, 0, 180));
g.fill(sprite.bounds);
delegate.render(g);
}
});
}
}
@Override
public void populateWorldMap(WorldMap map, DataTable dataTable, BlueprintEntity entity, DataPrototype prototype) {
LuaValue recipe = entity.lua().get("recipe");
boolean hasFluid = false;
if (!recipe.isnil()) {
DataPrototype protoRecipe = dataTable.getRecipes().get(recipe.toString());
List<LuaValue> items = new ArrayList<>();
Utils.forEach(protoRecipe.lua().get("ingredients"), (Consumer<LuaValue>) items::add);
LuaValue resultsLua = protoRecipe.lua().get("results");
if (resultsLua != LuaValue.NIL) {
items.add(resultsLua);
}
hasFluid = items.stream().anyMatch(lua -> {
LuaValue typeLua = lua.get("type");
return typeLua != LuaValue.NIL && typeLua.toString().equals("fluid");
});
}
LuaValue fluidBoxesLua = prototype.lua().get("fluid_boxes");
boolean offWhenNoFluidRecipe = fluidBoxesLua.isnil() ? true
: fluidBoxesLua.get("off_when_no_fluid_recipe").optboolean(false);
if (!fluidBoxesLua.isnil() && (!offWhenNoFluidRecipe || hasFluid)) {
Utils.forEach(fluidBoxesLua, fluidBoxLua -> {
if (!fluidBoxLua.istable()) {
return;
}
Utils.forEach(fluidBoxLua.get("pipe_connections"), pipeConnectionLua -> {
Point2D.Double offset = Utils.parsePoint2D(pipeConnectionLua.get("position"));
if (Math.abs(offset.y) > Math.abs(offset.x)) {
offset.y += -Math.signum(offset.y);
} else {
offset.x += -Math.signum(offset.x);
}
Point2D.Double pos = entity.getDirection().left()
.offset(entity.getDirection().back().offset(entity.getPosition(), offset.y), offset.x);
Direction direction = offset.y > 0 ? entity.getDirection().back() : entity.getDirection();
map.setPipe(pos, direction);
});
});
}
}
}

View File

@ -0,0 +1,21 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class BeaconRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
Sprite baseSprite = getSpriteFromAnimation(prototype.lua().get("base_picture"));
Sprite antennaSpriteShadow = getSpriteFromAnimation(prototype.lua().get("animation_shadow"));
Sprite antennaSprite = getSpriteFromAnimation(prototype.lua().get("animation"));
register.accept(spriteRenderer(baseSprite, entity, prototype));
register.accept(spriteRenderer(antennaSpriteShadow, entity, prototype));
register.accept(spriteRenderer(antennaSprite, entity, prototype));
}
}

View File

@ -0,0 +1,57 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.BlueprintEntity.Direction;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class BoilerRendering extends TypeRendererFactory {
public static final String[] pipeSpriteNameMapping = //
new String[/* bits WSEN */] { //
"down", // ....
"down", // ...N
"left", // ..E.
"right_up", // ..EN
"down", // .S..
"down", // .S.N
"right_down", // .SE.
"right_down", // .SEN
"left", // W...
"left_up", // W..N
"left", // W.E.
"t_up", // W.EN
"left_down", // WS..
"t_down", // WS.N
"t_down", // WSE.
"t_down",// WSEN
};
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
int adjCode = 0;
adjCode |= ((pipeFacingMeFrom(Direction.NORTH, map, entity) ? 1 : 0) << 0);
adjCode |= ((pipeFacingMeFrom(Direction.EAST, map, entity) ? 1 : 0) << 1);
adjCode |= ((pipeFacingMeFrom(Direction.SOUTH, map, entity) ? 1 : 0) << 2);
adjCode |= ((pipeFacingMeFrom(Direction.WEST, map, entity) ? 1 : 0) << 3);
String spriteName = pipeSpriteNameMapping[adjCode];
Sprite sprite = getSpriteFromAnimation(prototype.lua().get("structure").get(spriteName));
register.accept(spriteRenderer(sprite, entity, prototype));
}
public boolean pipeFacingMeFrom(Direction direction, WorldMap map, BlueprintEntity entity) {
return map.isPipe(direction.offset(entity.getPosition()), direction.back());
}
@Override
public void populateWorldMap(WorldMap map, DataTable dataTable, BlueprintEntity entity, DataPrototype prototype) {
map.setPipe(entity.getPosition());
}
}

View File

@ -0,0 +1,18 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class ConstantCombinatorRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
Sprite sprite = getSpriteFromAnimation(
prototype.lua().get("sprites").get(entity.getDirection().name().toLowerCase()));
register.accept(spriteRenderer(sprite, entity, prototype));
}
}

View File

@ -0,0 +1,4 @@
package demod.fbsr.render;
public class ContainerRendering extends TypeRendererFactory {
}

View File

@ -0,0 +1,33 @@
package demod.fbsr.render;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class DeciderCombinatorRendering extends TypeRendererFactory {
public static final Map<String, String> operationSprites = new HashMap<>();
static {
operationSprites.put("=", "equal_symbol_sprites");
operationSprites.put(">", "greater_symbol_sprites");
operationSprites.put("<", "less_symbol_sprites");
}
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
Sprite sprite = getSpriteFromAnimation(
prototype.lua().get("sprites").get(entity.getDirection().name().toLowerCase()));
Sprite operatorSprite = getSpriteFromAnimation(prototype.lua()
.get(operationSprites.get(
entity.lua().get("control_behavior").get("decider_conditions").get("comparator").toString()))
.get(entity.getDirection().name().toLowerCase()));
register.accept(spriteRenderer(sprite, entity, prototype));
register.accept(spriteRenderer(operatorSprite, entity, prototype));
}
}

View File

@ -0,0 +1,23 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
import demod.fbsr.render.Renderer.Layer;
public class ElectricPoleRendering extends TypeRendererFactory {
private static final int SpriteIndex = 2;
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
super.createRenderers(register, map, dataTable, entity, prototype);
Sprite sprite = getSpriteFromAnimation(prototype.lua().get("pictures"));
sprite.source.x = sprite.source.width * SpriteIndex;
register.accept(spriteRenderer(Layer.ENTITY3, sprite, entity, prototype));
}
}

View File

@ -0,0 +1,23 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import org.luaj.vm2.LuaValue;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class ElectricTurretRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
Sprite baseSprite = getSpriteFromAnimation(prototype.lua().get("base_picture").get("layers").get(1));
register.accept(spriteRenderer(baseSprite, entity, prototype));
LuaValue turretLayers = prototype.lua().get("folded_animation").get("layers");
Sprite turretSprite = getSpriteFromAnimation(turretLayers.get(1));
turretSprite.source.y += turretSprite.source.height * entity.getDirection().cardinal();
register.accept(spriteRenderer(turretSprite, entity, prototype));
}
}

View File

@ -0,0 +1,32 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import org.luaj.vm2.LuaValue;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.BlueprintEntity.Direction;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class FluidTurretRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
Sprite baseSprite = getSpriteFromAnimation(prototype.lua().get("base_picture")
.get(entity.getDirection().name().toLowerCase()).get("layers").get(1));
register.accept(spriteRenderer(baseSprite, entity, prototype));
LuaValue turretLayers = prototype.lua().get("folded_animation").get(entity.getDirection().name().toLowerCase())
.get("layers");
Sprite turretSprite = getSpriteFromAnimation(turretLayers.get(1));
register.accept(spriteRenderer(turretSprite, entity, prototype));
}
@Override
public void populateWorldMap(WorldMap map, DataTable dataTable, BlueprintEntity entity, DataPrototype prototype) {
Direction dir = entity.getDirection();
map.setPipe(dir.right().offset(dir.back().offset(entity.getPosition()), 0.5), dir.right());
map.setPipe(dir.left().offset(dir.back().offset(entity.getPosition()), 0.5), dir.left());
}
}

View File

@ -0,0 +1,4 @@
package demod.fbsr.render;
public class FurnaceRendering extends TypeRendererFactory {
}

View File

@ -0,0 +1,114 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import org.luaj.vm2.LuaValue;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class GateRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
boolean vertical = isVerticalGate(entity);
// Point2D.Double pos = entity.getPosition();
// boolean northPatch = vertical &&
// map.isWall(Direction.NORTH.offset(pos));
// boolean eastPatch = !vertical &&
// map.isWall(Direction.EAST.offset(pos));
// boolean southPatch = vertical &&
// map.isWall(Direction.SOUTH.offset(pos));
// boolean westPatch = !vertical &&
// map.isWall(Direction.WEST.offset(pos));
String orientation = vertical ? "vertical" : "horizontal";
LuaValue spriteLayersLua = prototype.lua().get(orientation + "_animation").get("layers");
// LuaValue wallPatchLua = prototype.lua().get("wall_patch");
// LuaValue northPatchLayersLua = null, eastPatchLayersLua = null,
// westPatchLayersLua = null,
// southPatchLayersLua = null;
Sprite spriteShadow = getSpriteFromAnimation(spriteLayersLua.get(2));
register.accept(spriteRenderer(spriteShadow, entity, prototype));
Sprite baseSprite = getSpriteFromAnimation(prototype.lua().get(orientation + "_base").get("layers").get(1));
register.accept(spriteRenderer(baseSprite, entity, prototype));
// TODO Patches need to be rendered on the wall pieces!
// if (northPatch) {
// northPatchLayersLua = wallPatchLua.get("north").get("layers");
// Sprite patchSpriteShadow =
// getSpriteFromAnimation(northPatchLayersLua.get(2));
// register.accept(spriteRenderer(patchSpriteShadow, entity,
// prototype));
// }
// if (eastPatch) {
// eastPatchLayersLua = wallPatchLua.get("east").get("layers");
// Sprite patchSpriteShadow =
// getSpriteFromAnimation(eastPatchLayersLua.get(2));
// register.accept(spriteRenderer(patchSpriteShadow, entity,
// prototype));
// }
// if (southPatch) {
// southPatchLayersLua = wallPatchLua.get("south").get("layers");
// Sprite patchSpriteShadow =
// getSpriteFromAnimation(southPatchLayersLua.get(2));
// register.accept(spriteRenderer(patchSpriteShadow, entity,
// prototype));
// }
// if (westPatch) {
// westPatchLayersLua = wallPatchLua.get("west").get("layers");
// Sprite patchSpriteShadow =
// getSpriteFromAnimation(westPatchLayersLua.get(2));
// register.accept(spriteRenderer(patchSpriteShadow, entity,
// prototype));
// }
//
// if (northPatch) {
// Sprite patchSprite =
// getSpriteFromAnimation(northPatchLayersLua.get(1));
// register.accept(spriteRenderer(patchSprite, entity, prototype));
// }
Sprite sprite = getSpriteFromAnimation(spriteLayersLua.get(1));
register.accept(spriteRenderer(sprite, entity, prototype));
// if (eastPatch) {
// Sprite patchSprite =
// getSpriteFromAnimation(eastPatchLayersLua.get(1));
// register.accept(spriteRenderer(patchSprite, entity, prototype));
// }
// if (southPatch) {
// Sprite patchSprite =
// getSpriteFromAnimation(southPatchLayersLua.get(1));
// register.accept(spriteRenderer(patchSprite, entity, prototype));
// }
// if (westPatch) {
// Sprite patchSprite =
// getSpriteFromAnimation(westPatchLayersLua.get(1));
// register.accept(spriteRenderer(patchSprite, entity, prototype));
// }
// TODO Rail Base if over rail
}
private boolean isVerticalGate(BlueprintEntity entity) {
return entity.getDirection().cardinal() % 2 == 0;
}
@Override
public void populateWorldMap(WorldMap map, DataTable dataTable, BlueprintEntity entity, DataPrototype prototype) {
if (isVerticalGate(entity)) {
map.setVerticalGate(entity.getPosition());
} else {
map.setHorizontalGate(entity.getPosition());
}
}
}

View File

@ -0,0 +1,30 @@
package demod.fbsr.render;
import java.awt.geom.Point2D;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.BlueprintEntity.Direction;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class GeneratorRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
debugPrintContext(entity, prototype);
Sprite sprite = getSpriteFromAnimation(prototype.lua()
.get((entity.getDirection().cardinal() % 2) == 0 ? "vertical_animation" : "horizontal_animation"));
register.accept(spriteRenderer(sprite, entity, prototype));
}
@Override
public void populateWorldMap(WorldMap map, DataTable dataTable, BlueprintEntity entity, DataPrototype prototype) {
Direction dir = entity.getDirection();
Point2D.Double position = entity.getPosition();
map.setPipe(dir.offset(position, 2), dir);
map.setPipe(dir.back().offset(position, 2), dir.back());
}
}

View File

@ -0,0 +1,54 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class InserterRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
// Utils.debugPrintTable(entity.lua());
// System.exit(1);
Sprite sprite = getSpriteFromAnimation(prototype.lua().get("platform_picture").get("sheet"));
sprite.source.x += sprite.source.width * (entity.getDirection().back().cardinal());
register.accept(spriteRenderer(sprite, entity, prototype));
// register.accept(new Renderer(Layer.OVERLAY, entity.getPosition()) {
// @Override
// public void render(Graphics2D g) {
// Point2D.Double pos = entity.getPosition();
// Direction forward = entity.getDirection().back();
// Direction left = forward.left();
//
// Point2D.Double pickupOffset =
// LuaUtils.parsePoint2D(prototype.lua().get("pickup_position"));
// Point2D.Double insertOffset =
// LuaUtils.parsePoint2D(prototype.lua().get("insert_position"));
//
// Point2D.Double pickupPosition = left.offset(forward.offset(pos,
// pickupOffset.y), pickupOffset.x);
// Point2D.Double insertPosition = left.offset(forward.offset(pos,
// insertOffset.y), insertOffset.x);
//
// Ellipse2D.Double pickupShape = new Ellipse2D.Double(pickupPosition.x
// - 0.3, pickupPosition.y - 0.3, 0.6,
// 0.6);
// Ellipse2D.Double insertShape = new Ellipse2D.Double(insertPosition.x
// - 0.2, insertPosition.y - 0.2, 0.4,
// 0.4);
//
// g.setColor(new Color(128, 128, 0, 100));
// g.fill(pickupShape);
// g.fill(insertShape);
// }
// });
}
}

View File

@ -0,0 +1,4 @@
package demod.fbsr.render;
public class LabRendering extends TypeRendererFactory {
}

View File

@ -0,0 +1,19 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class LampRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
Sprite sprite = getSpriteFromAnimation(prototype.lua().get("picture_off"));
register.accept(spriteRenderer(sprite, entity, prototype));
}
}

View File

@ -0,0 +1,4 @@
package demod.fbsr.render;
public class LandMineRendering extends TypeRendererFactory {
}

View File

@ -0,0 +1,4 @@
package demod.fbsr.render;
public class LogisticContainerRendering extends TypeRendererFactory {
}

View File

@ -0,0 +1,51 @@
package demod.fbsr.render;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.Utils;
import demod.fbsr.WorldMap;
public class MiningDrillRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
if (entity.getName().equals("pumpjack")) {
Sprite baseSprite = getSpriteFromAnimation(prototype.lua().get("base_picture").get("sheet"));
baseSprite.source.x = baseSprite.source.width * entity.getDirection().cardinal();
Sprite jackSprite = getSpriteFromAnimation(prototype.lua().get("animations").get("north"));
register.accept(spriteRenderer(baseSprite, entity, prototype));
register.accept(spriteRenderer(jackSprite, entity, prototype));
} else {
Sprite sprite = getSpriteFromAnimation(
prototype.lua().get("animations").get(entity.getDirection().name().toLowerCase()));
register.accept(spriteRenderer(sprite, entity, prototype));
}
}
@Override
public void populateWorldMap(WorldMap map, DataTable dataTable, BlueprintEntity entity, DataPrototype prototype) {
if (entity.getName().equals("pumpjack")) {
List<Point2D.Double> positions = new ArrayList<>();
Utils.forEach(prototype.lua().get("fluid_box").get("pipe_connections").get(1).get("positions"), l -> {
positions.add(Utils.parsePoint2D(l));
});
Point2D.Double entityPos = entity.getPosition();
Point2D.Double pipePos = entity.getDirection().back()
.offset(positions.get(entity.getDirection().cardinal()));
pipePos.x += entityPos.x;
pipePos.y += entityPos.y;
map.setPipe(pipePos, entity.getDirection());
}
}
}

View File

@ -0,0 +1,24 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class OffshorePumpRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
Sprite sprite = getSpriteFromAnimation(
prototype.lua().get("picture").get(entity.getDirection().name().toLowerCase()));
register.accept(spriteRenderer(sprite, entity, prototype));
}
@Override
public void populateWorldMap(WorldMap map, DataTable dataTable, BlueprintEntity entity, DataPrototype prototype) {
map.setPipe(entity.getPosition(), entity.getDirection().back());
}
}

View File

@ -0,0 +1,57 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.BlueprintEntity.Direction;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class PipeRendering extends TypeRendererFactory {
public static final String[] pipeSpriteNameMapping = //
new String[/* bits WSEN */] { //
"straight_horizontal", // ....
"ending_up", // ...N
"ending_right", // ..E.
"corner_up_right", // ..EN
"ending_down", // .S..
"straight_vertical", // .S.N
"corner_down_right", // .SE.
"t_right", // .SEN
"ending_left", // W...
"corner_up_left", // W..N
"straight_horizontal", // W.E.
"t_up", // W.EN
"corner_down_left", // WS..
"t_left", // WS.N
"t_down", // WSE.
"cross",// WSEN
};
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
int adjCode = 0;
adjCode |= ((pipeFacingMeFrom(Direction.NORTH, map, entity) ? 1 : 0) << 0);
adjCode |= ((pipeFacingMeFrom(Direction.EAST, map, entity) ? 1 : 0) << 1);
adjCode |= ((pipeFacingMeFrom(Direction.SOUTH, map, entity) ? 1 : 0) << 2);
adjCode |= ((pipeFacingMeFrom(Direction.WEST, map, entity) ? 1 : 0) << 3);
String spriteName = pipeSpriteNameMapping[adjCode];
Sprite sprite = getSpriteFromAnimation(prototype.lua().get("pictures").get(spriteName));
register.accept(spriteRenderer(sprite, entity, prototype));
}
public boolean pipeFacingMeFrom(Direction direction, WorldMap map, BlueprintEntity entity) {
return map.isPipe(direction.offset(entity.getPosition()), direction.back());
}
@Override
public void populateWorldMap(WorldMap map, DataTable dataTable, BlueprintEntity entity, DataPrototype prototype) {
map.setPipe(entity.getPosition());
}
}

View File

@ -0,0 +1,29 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class PipeToGroundRendering extends TypeRendererFactory {
public static String[] pipeToGroundCardinalNaming = { //
"up", "right", "down", "left"//
};
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
Sprite sprite = getSpriteFromAnimation(
prototype.lua().get("pictures").get(pipeToGroundCardinalNaming[entity.getDirection().cardinal()]));
register.accept(spriteRenderer(sprite, entity, prototype));
}
@Override
public void populateWorldMap(WorldMap map, DataTable dataTable, BlueprintEntity entity, DataPrototype prototype) {
map.setPipe(entity.getPosition(), entity.getDirection());
}
}

View File

@ -0,0 +1,17 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class PowerSwitchRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
Sprite sprite = getSpriteFromAnimation(prototype.lua().get("power_on_animation"));
register.accept(spriteRenderer(sprite, entity, prototype));
}
}

View File

@ -0,0 +1,23 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class PumpRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
Sprite sprite = getSpriteFromAnimation(
prototype.lua().get("animations").get(entity.getDirection().name().toLowerCase()));
register.accept(spriteRenderer(sprite, entity, prototype));
}
@Override
public void populateWorldMap(WorldMap map, DataTable dataTable, BlueprintEntity entity, DataPrototype prototype) {
map.setPipe(entity.getPosition(), entity.getDirection(), entity.getDirection().back());
}
}

View File

@ -0,0 +1,17 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class RadarRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
Sprite sprite = getSpriteFromAnimation(prototype.lua().get("pictures"));
register.accept(spriteRenderer(sprite, entity, prototype));
}
}

View File

@ -0,0 +1,33 @@
package demod.fbsr.render;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
public abstract class Renderer {
public static enum Layer {
ENTITY, ENTITY2, ENTITY3, OVERLAY, OVERLAY2;
}
protected final Rectangle2D.Double bounds;
protected final Layer layer;
public Renderer(Layer layer, Point2D.Double position) {
this(layer, new Rectangle2D.Double(position.x, position.y, 0, 0));
}
public Renderer(Layer layer, Rectangle2D.Double bounds) {
this.layer = layer;
this.bounds = bounds;
}
public Rectangle2D.Double getBounds() {
return bounds;
}
public Layer getLayer() {
return layer;
}
public abstract void render(Graphics2D g);
}

View File

@ -0,0 +1,20 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class RoboportRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
register.accept(spriteRenderer(getSpriteFromAnimation(prototype.lua().get("base")), entity, prototype));
register.accept(
spriteRenderer(getSpriteFromAnimation(prototype.lua().get("door_animation_down")), entity, prototype));
register.accept(
spriteRenderer(getSpriteFromAnimation(prototype.lua().get("door_animation_up")), entity, prototype));
}
}

View File

@ -0,0 +1,26 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
import demod.fbsr.render.Renderer.Layer;
public class RocketSiloRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
Sprite baseSprite = getSpriteFromAnimation(prototype.lua().get("base_day_sprite"));
Sprite shadowSprite = getSpriteFromAnimation(prototype.lua().get("shadow_sprite"));
Sprite doorFrontSprite = getSpriteFromAnimation(prototype.lua().get("door_front_sprite"));
Sprite doorBackSprite = getSpriteFromAnimation(prototype.lua().get("door_back_sprite"));
register.accept(spriteRenderer(Layer.ENTITY, shadowSprite, entity, prototype));
register.accept(spriteRenderer(Layer.ENTITY, doorFrontSprite, entity, prototype));
register.accept(spriteRenderer(Layer.ENTITY, doorBackSprite, entity, prototype));
register.accept(spriteRenderer(Layer.ENTITY2, baseSprite, entity, prototype));
}
}

View File

@ -0,0 +1,17 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class SolarPanelRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
Sprite sprite = getSpriteFromAnimation(prototype.lua().get("picture"));
register.accept(spriteRenderer(sprite, entity, prototype));
}
}

View File

@ -0,0 +1,47 @@
package demod.fbsr.render;
import java.awt.geom.Point2D;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.BlueprintEntity.Direction;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
import demod.fbsr.render.Renderer.Layer;
public class SplitterRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
int[] beltSpriteMapping = TransportBeltRendering.transportBeltSpriteMapping[entity.getDirection()
.cardinal()][1];
Sprite belt1Sprite = getSpriteFromAnimation(prototype.lua().get("belt_horizontal"));
belt1Sprite.source.y = belt1Sprite.source.height * beltSpriteMapping[0];
Sprite belt2Sprite = new Sprite(belt1Sprite);
Point2D.Double beltShift = entity.getDirection().left().offset(new Point2D.Double(), 0.5);
belt1Sprite.bounds.x += beltShift.x;
belt1Sprite.bounds.y += beltShift.y;
belt2Sprite.bounds.x -= beltShift.x;
belt2Sprite.bounds.y -= beltShift.y;
Sprite sprite = getSpriteFromAnimation(
prototype.lua().get("structure").get(entity.getDirection().toString().toLowerCase()));
register.accept(spriteRenderer(Layer.ENTITY, belt1Sprite, entity, prototype));
register.accept(spriteRenderer(Layer.ENTITY, belt2Sprite, entity, prototype));
register.accept(spriteRenderer(Layer.ENTITY2, sprite, entity, prototype));
}
@Override
public void populateWorldMap(WorldMap map, DataTable dataTable, BlueprintEntity entity, DataPrototype prototype) {
Direction direction = entity.getDirection();
Point2D.Double belt1Pos = direction.left().offset(entity.getPosition(), 0.5);
Point2D.Double belt2Pos = direction.right().offset(entity.getPosition(), 0.5);
map.setBelt(belt1Pos, direction);
map.setBelt(belt2Pos, direction);
}
}

View File

@ -0,0 +1,41 @@
package demod.fbsr.render;
import java.awt.geom.Point2D;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class StorageTankRendering extends TypeRendererFactory {
public static final int[][][] storageTankPipes = //
new int[/* NESW */][/* Points */][/* XY */] { //
{ { 1, 1 }, { -1, -1 } }, // North
{ { 1, -1 }, { -1, 1 } }, // East
{ { 1, 1 }, { -1, -1 } }, // South
{ { 1, -1 }, { -1, 1 } },// West
};
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
Sprite sprite = getSpriteFromAnimation(prototype.lua().get("pictures").get("picture").get("sheet"));
sprite.source.x = sprite.source.width * (entity.getDirection().cardinal() % 2);
register.accept(spriteRenderer(sprite, entity, prototype));
}
@Override
public void populateWorldMap(WorldMap map, DataTable dataTable, BlueprintEntity entity, DataPrototype prototype) {
// FIXME maybe should use the fluid box
Point2D.Double position = entity.getPosition();
int[][] pipePoints = storageTankPipes[entity.getDirection().cardinal()];
for (int[] point : pipePoints) {
map.setPipe(new Point2D.Double(position.x + point[0], position.y + point[1]));
}
}
}

View File

@ -0,0 +1,69 @@
package demod.fbsr.render;
import java.awt.geom.Point2D;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.UnaryOperator;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.BlueprintEntity.Direction;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
public class TransportBeltRendering extends TypeRendererFactory {
public static final int[][][] transportBeltSpriteMapping = //
new int[/* NESW */][/* LNR */][/* SXY */] { //
{ { 8, 1, 0 }, { 1, 0, 0 }, { 8, 0, 0 } }, // North
{ { 9, 0, 0 }, { 0, 0, 0 }, { 11, 0, 0 } }, // East
{ { 10, 1, 0 }, { 1, 0, 1 }, { 10, 0, 0 } }, // South
{ { 11, 1, 0 }, { 0, 1, 0 }, { 9, 1, 0 } }, // West
};
public boolean beltFacingMeFrom(UnaryOperator<Direction> rotateFunction, WorldMap map, BlueprintEntity entity) {
Point2D.Double adjacentPosition = rotateFunction.apply(entity.getDirection()).offset(entity.getPosition());
Optional<Direction> adjacentDirection = map.getBelt(adjacentPosition);
return adjacentDirection.map(d -> entity.getPosition().distance(d.offset(adjacentPosition)) < 0.1)
.orElse(false);
}
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
boolean left = beltFacingMeFrom(Direction::left, map, entity);
boolean right = beltFacingMeFrom(Direction::right, map, entity);
boolean back = beltFacingMeFrom(Direction::back, map, entity);
int bend;
if (back || (left && right)) {
bend = 1; // none
} else if (left) {
bend = 0; // from the left
} else if (right) {
bend = 2; // from the right
} else {
bend = 1; // none
}
int[] spriteMapping = transportBeltSpriteMapping[entity.getDirection().cardinal()][bend];
Sprite sprite = getSpriteFromAnimation(prototype.lua().get("animations"));
sprite.source.y = sprite.source.height * spriteMapping[0];
if (spriteMapping[1] == 1) {
sprite.source.x += sprite.source.width;
sprite.source.width *= -1;
}
if (spriteMapping[2] == 1) {
sprite.source.y += sprite.source.height;
sprite.source.height *= -1;
}
register.accept(spriteRenderer(sprite, entity, prototype));
}
@Override
public void populateWorldMap(WorldMap map, DataTable dataTable, BlueprintEntity entity, DataPrototype prototype) {
map.setBelt(entity.getPosition(), entity.getDirection());
}
}

View File

@ -0,0 +1,216 @@
package demod.fbsr.render;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import org.luaj.vm2.LuaValue;
import com.google.common.collect.ImmutableList;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.FBSRMain;
import demod.fbsr.Utils;
import demod.fbsr.WorldMap;
import demod.fbsr.render.Renderer.Layer;
public class TypeRendererFactory {
protected static class Sprite {
public BufferedImage image;
public Rectangle source;
public Rectangle2D.Double bounds;
public Sprite() {
}
public Sprite(Sprite other) {
image = other.image;
source = new Rectangle(other.source);
bounds = new Rectangle2D.Double(other.bounds.x, other.bounds.y, other.bounds.width, other.bounds.height);
}
}
private static List<String> defaultProperties = ImmutableList.of("animation", "off_animation", "structure");
protected static final TypeRendererFactory DEFAULT = new TypeRendererFactory() {
Set<String> defaultedTypes = new HashSet<>();
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable,
BlueprintEntity entity, DataPrototype prototype) {
super.createRenderers(register, map, dataTable, entity, prototype);
register.accept(new Renderer(Layer.OVERLAY2, entity.getPosition()) {
@Override
public void render(Graphics2D g) {
g.setColor(new Color(255, 0, 255, 128));
g.fill(new Rectangle2D.Double(bounds.x - 0.5, bounds.y - 0.5, 1, 1));
if (defaultedTypes.add(prototype.getType())) {
g.setFont(g.getFont().deriveFont(0.5f));
g.setColor(Color.white);
g.drawString(prototype.getType(), (float) bounds.x, (float) bounds.y);
}
}
});
}
};
private static Map<String, TypeRendererFactory> byType = new HashMap<>();
static {
byType.put("", DEFAULT);
byType.put("accumulator", new AccumulatorRendering());
byType.put("ammo-turret", new AmmoTurretRendering());
byType.put("arithmetic-combinator", new ArithmeticCombinatorRendering());
byType.put("assembling-machine", new AssemblingMachineRendering());
byType.put("beacon", new BeaconRendering());
byType.put("boiler", new BoilerRendering());
byType.put("constant-combinator", new ConstantCombinatorRendering());
byType.put("container", new ContainerRendering());
byType.put("decider-combinator", new DeciderCombinatorRendering());
byType.put("electric-pole", new ElectricPoleRendering());
byType.put("electric-turret", new ElectricTurretRendering());
byType.put("fluid-turret", new FluidTurretRendering());
byType.put("furnace", new FurnaceRendering());
byType.put("gate", new GateRendering());
byType.put("generator", new GeneratorRendering());
byType.put("inserter", new InserterRendering());
byType.put("lab", new LabRendering());
byType.put("lamp", new LampRendering());
byType.put("land-mine", new LandMineRendering());
byType.put("logistic-container", new LogisticContainerRendering());
byType.put("mining-drill", new MiningDrillRendering());
byType.put("offshore-pump", new OffshorePumpRendering());
byType.put("pipe", new PipeRendering());
byType.put("pipe-to-ground", new PipeToGroundRendering());
byType.put("power-switch", new PowerSwitchRendering());
byType.put("pump", new PumpRendering());
byType.put("radar", new RadarRendering());
byType.put("roboport", new RoboportRendering());
byType.put("rocket-silo", new RocketSiloRendering());
byType.put("solar-panel", new SolarPanelRendering());
byType.put("splitter", new SplitterRendering());
byType.put("storage-tank", new StorageTankRendering());
byType.put("transport-belt", new TransportBeltRendering());
byType.put("underground-belt", new UndergroundBeltRendering());
byType.put("wall", new WallRendering());
}
public static final double tileSize = 32.0;
protected static void drawImageInBounds(BufferedImage image, Rectangle source, Rectangle2D.Double bounds,
Graphics2D g) {
AffineTransform pat = g.getTransform();
g.translate(bounds.x, bounds.y);
g.scale(bounds.width, bounds.height);
g.drawImage(image, 0, 0, 1, 1, source.x, source.y, source.x + source.width, source.y + source.height, null);
g.setTransform(pat);
}
protected static void drawSprite(Sprite sprite, Graphics2D g) {
drawImageInBounds(sprite.image, sprite.source, sprite.bounds, g);
}
public static TypeRendererFactory forType(String type) {
return byType.getOrDefault(type, DEFAULT);
}
protected static Sprite getSpriteFromAnimation(LuaValue lua) {
Sprite ret = new Sprite();
ret.image = FBSRMain.getModImage(lua.get("filename"));
int srcX = lua.get("x").optint(0);
int srcY = lua.get("y").optint(0);
int srcWidth = lua.get("width").checkint();
double width = srcWidth / tileSize;
int srcHeight = lua.get("height").checkint();
double height = srcHeight / tileSize;
Point2D.Double shift = Utils.parsePoint2D(lua.get("shift"));
ret.source = new Rectangle(srcX, srcY, srcWidth, srcHeight);
ret.bounds = new Rectangle2D.Double(shift.x - width / 2.0, shift.y - height / 2.0, width, height);
return ret;
}
protected static Renderer spriteRenderer(Layer layer, Sprite sprite, BlueprintEntity entity,
DataPrototype prototype) {
Point2D.Double pos = entity.getPosition();
sprite.bounds.x += pos.x;
sprite.bounds.y += pos.y;
Rectangle2D.Double groundBounds = Utils.parseRectangle(prototype.lua().get("collision_box"));
groundBounds.x += pos.x;
groundBounds.y += pos.y;
return new Renderer(layer, groundBounds) {
@Override
public void render(Graphics2D g) {
drawSprite(sprite, g);
}
};
}
protected static Renderer spriteRenderer(Sprite sprite, BlueprintEntity entity, DataPrototype prototype) {
return spriteRenderer(Layer.ENTITY, sprite, entity, prototype);
}
// protected final String type;
// protected TypeRendererFactory(String type) {
// this.type = type;
// }
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
try {
Sprite sprite;
Optional<LuaValue> findSpriteLua = defaultProperties.stream().map(p -> prototype.lua().get(p))
.filter(l -> l != LuaValue.NIL).findAny();
if (findSpriteLua.isPresent()) {
LuaValue spriteLua = findSpriteLua.get();
boolean hasDir = spriteLua.get(entity.getDirection().name().toLowerCase()) != LuaValue.NIL;
if (hasDir) {
spriteLua = spriteLua.get(entity.getDirection().name().toLowerCase());
}
sprite = getSpriteFromAnimation(spriteLua);
} else {
sprite = new Sprite();
sprite.image = FBSRMain.getModImage(prototype.lua().get("icon"));
sprite.source = new Rectangle(0, 0, sprite.image.getWidth(), sprite.image.getHeight());
sprite.bounds = Utils.parseRectangle(prototype.lua().get("selection_box"));
}
register.accept(spriteRenderer(sprite, entity, prototype));
} catch (RuntimeException e) {
debugPrintContext(entity, prototype);
throw e;
}
}
protected void debugPrintContext(BlueprintEntity entity, DataPrototype prototype) {
System.out.println("=================================================================");
System.out.println("=========================== PROTOTYPE ===========================");
System.out.println("=================================================================");
Utils.debugPrintTable(prototype.lua());
System.out.println("=================================================================");
System.out.println("============================ ENTITY =============================");
System.out.println("=================================================================");
Utils.debugPrintTable(entity.lua());
}
public void populateWorldMap(WorldMap map, DataTable dataTable, BlueprintEntity entity, DataPrototype prototype) {
// default do nothing
}
}

View File

@ -0,0 +1,71 @@
package demod.fbsr.render;
import java.util.function.Consumer;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.BlueprintEntity.Direction;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.WorldMap;
import demod.fbsr.render.Renderer.Layer;
public class UndergroundBeltRendering extends TypeRendererFactory {
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
// LuaUtils.debugPrintTable("", prototype.lua());
// System.exit(1);
boolean input = entity.lua().get("type").toString().equals("input");
Direction structDir = input ? entity.getDirection() : entity.getDirection().back();
int[] beltSpriteMapping = TransportBeltRendering.transportBeltSpriteMapping[entity.getDirection()
.cardinal()][1];
Sprite beltSprite = getSpriteFromAnimation(prototype.lua().get("belt_horizontal"));
beltSprite.source.y = beltSprite.source.height * beltSpriteMapping[0];
if (beltSpriteMapping[1] == 1) {
beltSprite.source.x += beltSprite.source.width;
beltSprite.source.width *= -1;
}
if (beltSpriteMapping[2] == 1) {
beltSprite.source.y += beltSprite.source.height;
beltSprite.source.height *= -1;
}
switch (structDir) {
case NORTH:
beltSprite.source.height /= 2;
beltSprite.source.y += beltSprite.source.height;
beltSprite.bounds.height /= 2;
beltSprite.bounds.y += beltSprite.bounds.height;
break;
case WEST:
beltSprite.source.width /= 2;
beltSprite.source.x += beltSprite.source.width;
beltSprite.bounds.width /= 2;
beltSprite.bounds.x += beltSprite.bounds.width;
break;
case EAST:
beltSprite.source.width /= 2;
beltSprite.bounds.width /= 2;
break;
default:
break;
}
Sprite sprite = getSpriteFromAnimation(
prototype.lua().get("structure").get(input ? "direction_in" : "direction_out").get("sheet"));
sprite.source.x += sprite.source.width * (structDir.cardinal());
register.accept(spriteRenderer(Layer.ENTITY, beltSprite, entity, prototype));
register.accept(spriteRenderer(Layer.ENTITY2, sprite, entity, prototype));
}
@Override
public void populateWorldMap(WorldMap map, DataTable dataTable, BlueprintEntity entity, DataPrototype prototype) {
boolean input = entity.lua().get("type").toString().equals("input");
if (!input) {
map.setBelt(entity.getPosition(), entity.getDirection());
}
}
}

View File

@ -0,0 +1,83 @@
package demod.fbsr.render;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import org.luaj.vm2.LuaValue;
import demod.fbsr.BlueprintEntity;
import demod.fbsr.BlueprintEntity.Direction;
import demod.fbsr.DataPrototype;
import demod.fbsr.DataTable;
import demod.fbsr.Utils;
import demod.fbsr.WorldMap;
public class WallRendering extends TypeRendererFactory {
public static final String[] wallSpriteNameMapping = //
new String[/* bits WSEN */] { //
"single", // ....
"single", // ...N
"ending_right", // ..E.
"ending_right", // ..EN
"straight_vertical", // .S..
"straight_vertical", // .S.N
"corner_right_down", // .SE.
"corner_right_down", // .SEN
"ending_left", // W...
"ending_left", // W..N
"straight_horizontal", // W.E.
"straight_horizontal", // W.EN
"corner_left_down", // WS..
"corner_left_down", // WS.N
"t_up", // WSE.
"t_up",// WSEN
};
@Override
public void createRenderers(Consumer<Renderer> register, WorldMap map, DataTable dataTable, BlueprintEntity entity,
DataPrototype prototype) {
Point2D.Double pos = entity.getPosition();
boolean northGate = map.isVerticalGate(Direction.NORTH.offset(pos));
boolean eastGate = map.isHorizontalGate(Direction.EAST.offset(pos));
boolean southGate = map.isVerticalGate(Direction.SOUTH.offset(pos));
boolean westGate = map.isHorizontalGate(Direction.WEST.offset(pos));
int adjCode = 0;
adjCode |= ((map.isWall(Direction.NORTH.offset(pos)) || northGate ? 1 : 0) << 0);
adjCode |= ((map.isWall(Direction.EAST.offset(pos)) || eastGate ? 1 : 0) << 1);
adjCode |= ((map.isWall(Direction.SOUTH.offset(pos)) || southGate ? 1 : 0) << 2);
adjCode |= ((map.isWall(Direction.WEST.offset(pos)) || westGate ? 1 : 0) << 3);
String spriteName = wallSpriteNameMapping[adjCode];
LuaValue spriteLua = prototype.lua().get("pictures").get(spriteName);
List<LuaValue> layersChoices = new ArrayList<>();
if (spriteLua.get("layers") != LuaValue.NIL) {
layersChoices.add(spriteLua.get("layers"));
} else {
Utils.forEach(spriteLua, l -> layersChoices.add(l.get("layers")));
}
LuaValue layersLua = layersChoices.get(Math.abs((int) pos.x + (int) pos.y) % layersChoices.size());
Sprite sprite = getSpriteFromAnimation(layersLua.get(1));
Sprite spriteShadow = getSpriteFromAnimation(layersLua.get(2));
register.accept(spriteRenderer(spriteShadow, entity, prototype));
register.accept(spriteRenderer(sprite, entity, prototype));
if (northGate || eastGate || southGate || westGate) {
Sprite wallDiodeSprite = getSpriteFromAnimation(prototype.lua().get("wall_diode_red"));
register.accept(spriteRenderer(wallDiodeSprite, entity, prototype));
}
}
@Override
public void populateWorldMap(WorldMap map, DataTable dataTable, BlueprintEntity entity, DataPrototype prototype) {
map.setWall(entity.getPosition());
}
}

View File

@ -0,0 +1,34 @@
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

View File

@ -0,0 +1,65 @@
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
49P/A4luUqQBJQEA

View File

@ -0,0 +1,26 @@
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
p+PvfNhtPnz+1G9u/wEgF0qJcncAAA==

View File

@ -0,0 +1,16 @@
H4sIAAAAAAAA/82c7XKbOhCGbyXj36aDhD+nh2vJEKz0MIOBA6TTjsf3fpTYjkGB1e5KcvurzUQgiX21z+5KyqF+Kus8K5+e05Oq+qIvVJeeTlV2VOniJeuK
PHpRWV5Xi2VTd/rXdZWefqXR6tt6+TuN5P7b+nxeXtu39Uvd1G0/bht/NNzdmx3VoXg7RqpUed/qDpq6VONH5OXtSTx8+/xoJgeDGPxm2N4+qug6LLmlDWvU
TVn/KLpeP5H/q7o+atV/b/pf1c50tdbPLq+Nnl+LUrd8t09RHdSvVCwv7yzauorqVg83r9+qPpVxfL732B2zto+KqlPt137EsJ9D0erJv/9qo99UHT6a6e5u
Y9b/fS1aPZCu+FFlZXrqfzfv3ffquBgOpSmzXi3O7+/o+kwPSMRxrH86Nlmb9XWbLv5ZDAb4+clf39oqy42vHl9HuBp+RT2nsozK7NhMCudjOhQlJA4mSjAm
yuumUe28kYw+m6zrip8qatr6Z3GY7Xr0TV6zzmrm1djMkiaSxJNIrh/ji0z2oEzsCy2hrefJr4gQS8x0G2L4HCz6KJ56BjLSzUbSsLBvG8FLGdCgGMxoKCLi
spPDeT501U2a0S7KGOO3PjUS8x2R2D+GFZd+grFCurNC7KirJva0aqanBPs1C8vEni6JoZocFWH3h2ILDBAHsrHBiLLf+vAEKOFvA/Nvx/WtnyPczfHd6qfG
VkTDc2w5hFhWPHiKNR2e42cQbkBsAsMT9m12eAozTOf5hcs8/wQ8BTIwn1SLDZ7j1kQvgoriPcDTVww9TZrEAzwlddWYSYXfKcFO0QZPRmI3VFN4eAp3eEoH
2YuHwdOM+3371q0zPM3UieCnBA+ekgbPHY+dezo691Qf4CuAnrMu7Njs5DRzFqZPQAXUIcCJLG1OCcWGTYfYGxV6u0PTV9w9DZiVOzM3xOVihul+JwR7Qgsx
oWQOXhy7R/By7YxLh2o8qhjvA5a+qvFz3nTjykozUcK7pjWLlBsaKCUPlAkdlAlx5fuKlecsCzszOyfN9ITnClChcwhMJiRMmpkOXJp1iLIflFsGxeTaHZNm
Kdxalg2KSdgJ2hJLflH2AZiEdhFwlOTvQ3gJkRFyjwMzEtY7gpFmSoT2S8T9xSmLWRVC3Cq92laSATl6woofOUcfu7MQgcEKy4HBKgZP5H2mrgvMpkEzroHQ
yD884aHEhvAUq6BchDGC4SJxmyIJSkVY5zAVGfX3u4JCMxHK+1BMhFJPWOZetlsQQt8ERiI7ubiOb80lIvGg3oS9rPLYs4C4IwNxRwLijg/EbWAgwn6PAURG
HVLeZxoaiGZFHAIi48TG9UlcgdmZiGa53jMS4QIkBonjDRW72PdBmQj7PZiJgpFJ3VUUGorg3iGKiuAGpkXsXs7wYeRu5hV/ze7UbYBm9oJ2S2MDYtE4Nppd
JcSi7c2+KzIdx49Y8WgePSAljL7iaN6GNCnje/wR8ykLWrVoJjIQIwU/nMYdX3KHpK9wmnOuBQXJLQ2S5ukxz5SEnaCFkoxduYGOgmMSik1xmGTsq94eRW2s
+sCkmW38NccfbwM0cxq8ayIenZkymlUl45sB+KJqTMbkl0sFtpXvKz72fnJZ3OfjvO2IuwARAJMSWXufkokFk5IfXuOuyDhj0rxx5BmT8N0JDCYlcY/evKHk
GZOwE4QxKdmbBtLDHrTdAUIFYBQmHS5iSi+HDjCC97Wf4P2K3W2AZmaDd03E2xlTRrOrhHjT5GbfDR2TxHN55i1o75iEnZkdk2ZGwnMFXo6xcTCJPMU1JRMb
JkeN5/8OQRKT9OCrhs65FU8yqYeDuPYjA6QoJ2GdYrj8CYfzssg/PrExgVGf+t2X38rljEvQL/reql7/9PT8XVWH/wEtRs/P7EIAAA==

View File

@ -0,0 +1,20 @@
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
6Z5UAAA=