biogasmachines: gasifier machine added.

This machine is designed to extract Biogas from highly compressed
dry organic matter, such as fossil fuels (coal block) and straw.
It requires no fuel to avoid complex processing loops.
Its recipe base is expandable via API function.
All functionality and Tubelib integration is in place.

Signed-off-by: Michal Cieslakiewicz <michal.cieslakiewicz@wp.pl>
master
Michal Cieslakiewicz 2018-11-19 11:40:56 +01:00
parent d1f39d9582
commit 485b578e33
7 changed files with 648 additions and 27 deletions

View File

@ -1,5 +1,6 @@
default default
bucket bucket
farming
tubelib tubelib
tubelib_addons1 tubelib_addons1
pipeworks? pipeworks?

View File

@ -1,53 +1,673 @@
--[[ --[[
Tubelib Biogas Burn =======================================================================
=================== Tubelib Biogas Machines Mod
by Micu (c) 2018
Copyright (C) 2018 Michal Cieslakiewicz Copyright (C) 2018 Michal Cieslakiewicz
LGPLv2.1+ Gasifier is a machine designed to slowly extract Biogas from fossil
See LICENSE.txt for more information fuels and other compressed dry organic materials.
Basic recipe is conversion of Coal Block into Biogas (heavier leftover
fractions form Biofuel). Another default recipe is clean and complete
transformation of Straw Block into some Biogas units.
Custom recipes can be added via simple API function.
gasifier.lua Operational info:
* machine requires no extra fuel
Converts Coal Blocks to Biogas * if there is nothing to process, machine enters standby mode; it
will automatically pick up work as soon as any valid item is loaded
into input (source) tray
* there is 1 tick gap between processing items to perform machinery
cleaning and reload working tray; this is a design choice
* working tray can only be emptied when machine is stopped; this tray
is auto-loaded, source material should always go into input container
* machine cannot be recovered unless input and output trays are all
empty
* when active, due to high temperature inside, machine becomes a light
source of level 5
License: LGPLv2.1+
=======================================================================
]]-- ]]--
--[[
---------
Variables
---------
]]--
-- Biogas recipe table (key - source item name)
local biogas_recipes = {}
-- Biogas source table (indexed by numbers, used by formspec recipe hint bar)
local biogas_sources = {}
-- timing
local TIMER_TICK_SEC = 1 -- Node timer tick
local TICKS_TO_SLEEP = 5 -- Tubelib standby
-- machine inventory
local INV_H = 3 -- Inventory height (do not change)
local INV_IN_W = 2 -- Input inventory width
local INV_OUT_W = (6 - INV_IN_W) -- Output inventory width
--[[
----------------
Public functions
----------------
]]--
-- Add Biogas recipe
-- Returns true if recipe added successfully, false if error or already present
-- biogas_recipe = {
-- input = "itemstring", -- source item, always 1 (req)
-- count = number, -- biogas units produced (opt, def: 1)
-- time = number, -- production time in ticks (req, max: 99)
-- extra = "itemstring" } -- additional product (opt, def: nil)
function biogasmachines.add_gasifier_recipe(biogas_recipe)
if not biogas_recipe then return false end
if not biogas_recipe.input or not biogas_recipe.time or
biogas_recipe.time < 1 or biogas_recipe.time > 99 then
return false
end
local input_item = ItemStack(biogas_recipe.input)
if not input_item or input_item:get_count() > 1 then
return false
end
local input_name = input_item:get_name()
if not minetest.registered_items[input_name] then
return false
end
if biogas_recipes[input_name] then return false end
local extra_item = nil
if biogas_recipe.extra then
extra_item = ItemStack(biogas_recipe.extra)
if not minetest.registered_items[extra_item:get_name()] then
extra_item = nil
end
end
local count = 1
if biogas_recipe.count and biogas_recipe.count > 1 and
biogas_recipe.count < 100 then
count = biogas_recipe.count
end
biogas_recipes[input_name] = {
count = count,
time = biogas_recipe.time,
extra = extra_item,
}
biogas_sources[#biogas_sources + 1] = input_name
return true
end
--[[
--------
Formspec
--------
]]--
-- static data for formspec
local fmxy = { inv_h = tostring(INV_H),
inv_in_w = tostring(INV_IN_W),
mid_x = tostring(INV_IN_W + 0.5),
inv_out_w = tostring(INV_OUT_W),
inv_out_x = tostring(INV_IN_W + 2),
}
-- recipe hint
local function formspec_recipe_hint_bar(recipe_idx)
if #biogas_sources == 0 or recipe_idx > #biogas_sources then
return ""
end
local input_item = biogas_sources[recipe_idx]
local input_desc = minetest.registered_nodes[input_item].description
local recipe = biogas_recipes[input_item]
return "label[0.5,3.25;Recipe]" ..
"image_button[1.5,3.3;0.5,0.5;;left;<]" ..
"label[2,3.25;" ..
string.format("%3d / %3d", recipe_idx, #biogas_sources) ..
"]" ..
"image_button[2.8,3.3;0.5,0.5;;right;>]" ..
"item_image[3.6,3.25;0.5,0.5;" .. input_item .. "]" ..
--"tooltip[3.6,3.25;0.5,0.5;" .. -- not supported in 0.4.x
-- minetest.registered_nodes[input_item].description ..
-- ";;]" ..
"image[4,3.25;0.5,0.5;tubelib_gui_arrow.png^[resize:16x16]" ..
"label[4.4,3.25;" ..
string.format("%2d sec", recipe.time * TIMER_TICK_SEC) ..
"]" ..
"image[5,3.25;0.5,0.5;tubelib_gui_arrow.png^[resize:16x16]" ..
"item_image[5.5,3.25;0.5,0.5;tubelib_addons1:biogas]" ..
"label[6,3.25;x " .. tostring(recipe.count) .. "]" ..
(recipe.extra and "item_image[6.5,3.25;0.5,0.5;" ..
recipe.extra:get_name() .. "]label[7,3.25;x " ..
tostring(recipe.extra:get_count()) .. "]" or "")
end
-- Parameters:
-- state - tubelib state
-- item_percent - item completion
-- recipe_idx - index of recipe shown at hint bar
-- show_icons - show image hints (bool)
local function formspec(state, item_percent, recipe_idx, show_icons)
local inv_hint = show_icons and #biogas_sources > 0
local inv_key = inv_hint and biogas_sources[1] or ""
return "size[8,8.25]" ..
default.gui_bg ..
default.gui_bg_img ..
default.gui_slots ..
"list[context;src;0,0;" .. fmxy.inv_in_w .. "," .. fmxy.inv_h .. ";]" ..
(inv_hint and "item_image[0,0;1,1;" .. inv_key .. "]" or "") ..
"list[context;cur;" .. fmxy.mid_x .. ",0;1,1;]" ..
"image[" .. fmxy.mid_x .. ",1;1,1;gui_furnace_arrow_bg.png^[lowpart:" ..
tostring(item_percent) ..
":gui_furnace_arrow_fg.png^[transformR270]" ..
"image_button[" .. fmxy.mid_x .. ",2;1,1;" ..
tubelib.state_button(state) .. ";button;]" ..
formspec_recipe_hint_bar(recipe_idx) ..
(inv_hint and "item_image[" .. fmxy.inv_out_x ..
",0;1,1;tubelib_addons1:biogas]" or "") ..
"list[context;dst;" .. fmxy.inv_out_x .. ",0;" .. fmxy.inv_out_w ..
"," .. fmxy.inv_h .. ";]" ..
"list[current_player;main;0,4;8,1;]" ..
"list[current_player;main;0,5.25;8,3;8]" ..
"listring[context;dst]" ..
"listring[current_player;main]" ..
(state == tubelib.RUNNING and
"box[" .. fmxy.mid_x .. ",0;0.82,0.9;#9F3F1F]" or
"listring[context;cur]listring[current_player;main]") ..
"listring[context;src]" ..
"listring[current_player;main]" ..
default.get_hotbar_bg(0, 4)
end
--[[
-------
Helpers
-------
]]--
-- check if item is valid biogas source (bool)
local function is_input_item(stack)
local stackname = stack:get_name()
return biogas_recipes[stackname] and true or false
end
-- get one source item (itemstack)
local function get_input_item(inv, listname)
local stack = ItemStack({})
for i, _ in pairs(biogas_recipes) do
stack = inv:remove_item(listname, ItemStack(i .. " 1"))
if not stack:is_empty() then break end
end
return stack
end
local function gasifier_start(pos)
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local number = meta:get_string("number")
local recipe_idx = meta:get_int("recipe_idx")
local label = minetest.registered_nodes[node.name].description
meta:set_int("item_ticks", -1)
meta:set_int("running", TICKS_TO_SLEEP)
meta:set_string("infotext", label .. " " .. number .. ": running")
meta:set_string("formspec", formspec(tubelib.RUNNING, 0, recipe_idx, true))
node.name = "biogasmachines:gasifier_active"
minetest.swap_node(pos, node)
minetest.get_node_timer(pos):start(TIMER_TICK_SEC)
return false
end
local function gasifier_stop(pos)
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local number = meta:get_string("number")
local recipe_idx = meta:get_int("recipe_idx")
local label = minetest.registered_nodes[node.name].description
meta:set_int("item_ticks", -1)
meta:set_int("running", tubelib.STATE_STOPPED)
meta:set_string("infotext", label .. " " .. number .. ": stopped")
meta:set_string("formspec", formspec(tubelib.STOPPED, 0, recipe_idx, true))
node.name = "biogasmachines:gasifier"
minetest.swap_node(pos, node)
minetest.get_node_timer(pos):stop()
return false
end
local function gasifier_idle(pos)
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local number = meta:get_string("number")
local recipe_idx = meta:get_int("recipe_idx")
local label = minetest.registered_nodes[node.name].description
meta:set_int("item_ticks", -1)
meta:set_int("running", tubelib.STATE_STANDBY)
meta:set_string("infotext", label .. " " .. number .. ": standby")
meta:set_string("formspec", formspec(tubelib.STANDBY, 0, recipe_idx, true))
node.name = "biogasmachines:gasifier"
minetest.swap_node(pos, node)
minetest.get_node_timer(pos):start(TIMER_TICK_SEC * TICKS_TO_SLEEP)
return false
end
local function gasifier_fault(pos)
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local number = meta:get_string("number")
local recipe_idx = meta:get_int("recipe_idx")
local label = minetest.registered_nodes[node.name].description
meta:set_int("item_ticks", -1)
meta:set_int("running", tubelib.STATE_FAULT)
meta:set_string("infotext", label .. " " .. number .. ": fault")
meta:set_string("formspec", formspec(tubelib.FAULT, 0, recipe_idx, true))
node.name = "biogasmachines:gasifier"
minetest.swap_node(pos, node)
minetest.get_node_timer(pos):stop()
return false
end
local function update_recipe_hint_bar(pos)
local meta = minetest.get_meta(pos)
local recipe_idx = meta:get_int("recipe_idx")
local state = tubelib.state(meta:get_int("running"))
local item_name = meta:get_string("item_name")
local item_ticks = meta:get_int("item_ticks")
local item_pct = 0
if item_name ~= "" and item_ticks >= 0 then
local tot_ticks = biogas_recipes[item_name].time
item_pct = 100 * (tot_ticks - item_ticks) / tot_ticks
end
meta:set_string("formspec", formspec(state, item_pct, recipe_idx, true))
end
--[[
---------
Callbacks
---------
]]--
-- do not allow to dig non-empty machine
local function can_dig(pos, player)
local meta = minetest.get_meta(pos);
local inv = meta:get_inventory()
return inv:is_empty("src") and inv:is_empty("dst")
end
-- cleanup after digging
local function after_dig_node(pos, oldnode, oldmetadata, digger)
tubelib.remove_node(pos)
end
-- validate incoming items
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
if listname == "src" then
if is_input_item(stack) then
return stack:get_count()
else
return 0
end
elseif listname == "cur" or listname == "dst" then
return 0
end
return 0
end
-- validate items move
local function allow_metadata_inventory_move(pos, from_list, from_index,
to_list, to_index, count, player)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local stack = inv:get_stack(from_list, from_index)
return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
end
-- validate items retrieval
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
if listname == "cur" then
local meta = minetest.get_meta(pos)
if meta:get_int("running") > 0 then
return 0
end
end
return stack:get_count()
end
-- punch machine to see status info
local function on_punch(pos, node, puncher, pointed_thing)
local meta = minetest.get_meta(pos)
local player_name = puncher:get_player_name()
if meta:get_string("owner") ~= player_name then
return false
end
local msgclr = { ["fault"] = "#FFBFBF",
["standby"] = "#BFFFFF",
["stopped"] = "#BFBFFF",
["running"] = "#BFFFBF"
}
local state = tubelib.statestring(meta:get_int("running"))
local pipe = tostring(biogasmachines.is_pipe_with_water(pos, node))
minetest.chat_send_player(player_name,
minetest.colorize("#FFFF00", "[Gasifier:" ..
meta:get_string("number") .. "]") .. " Status is " ..
minetest.colorize(msgclr[state], "\"" .. state .. "\""))
return true
end
-- formspec callback
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local meta = minetest.get_meta(pos)
local running = meta:get_int("running")
if fields and fields.button then
if running > 0 or running == tubelib.STATE_FAULT then
gasifier_stop(pos)
else
gasifier_start(pos)
end
end
if fields and (fields.left or fields.right) then
local recipe_idx = meta:get_int("recipe_idx")
if fields.left then
recipe_idx = math.max(recipe_idx - 1, 1)
end
if fields.right then
recipe_idx = math.min(recipe_idx + 1, #biogas_sources)
end
meta:set_int("recipe_idx", recipe_idx)
update_recipe_hint_bar(pos)
end
end
-- tick-based item production
local function on_timer(pos, elapsed)
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local label = minetest.registered_nodes[node.name].description
local number = meta:get_string("number")
local running = meta:get_int("running")
local itemcnt = meta:get_int("item_ticks")
local itemname = meta:get_string("item_name")
local prodtime = -1
if itemcnt < 0 or itemname == "" then
-- idle and ready, check for something to work with
if inv:is_empty("src") then
if running > 0 then
-- no source item, count towards standby
running = running - 1
meta:set_int("running", running)
if running == 0 then
return gasifier_idle(pos)
end
end
return true
end
if running == tubelib.STATE_STANDBY then
-- something to do, wake up and re-entry
return gasifier_start(pos)
end
-- choose item
local inputname = nil
for i, r in pairs(biogas_recipes) do
if inv:contains_item("src", ItemStack(i .. " 1")) and
inv:room_for_item("dst",
ItemStack("tubelib_addons1:biogas " ..
tostring(r.count))) and
(not r.extra or inv:room_for_item("dst", r.extra))
then
inputname = i
prodtime = r.time
break
end
end
if not inputname then
return true
end
-- move item to working tray, next tick will start processing
local inp = inv:remove_item("src", ItemStack(inputname .. " 1"))
if inp:is_empty() then
return gasifier_fault(pos) -- oops
end
inv:add_item("cur", inp)
meta:set_string("item_name", inputname)
itemcnt = prodtime
else
-- production tick
itemcnt = itemcnt - 1
if itemcnt == 0 then
inv:add_item("dst",
ItemStack("tubelib_addons1:biogas " ..
tostring(biogas_recipes[itemname].count)))
if biogas_recipes[itemname].extra then
inv:add_item("dst",
biogas_recipes[itemname].extra)
end
inv:set_stack("cur", 1, ItemStack({}))
meta:set_string("item_name", "")
itemcnt = -1
else
prodtime = biogas_recipes[itemname].time
end
end
meta:set_int("item_ticks", itemcnt)
meta:set_int("running", TICKS_TO_SLEEP)
meta:set_string("infotext", label .. " " .. number .. ": running")
meta:set_string("formspec", formspec(tubelib.RUNNING,
100 * (prodtime - itemcnt) / prodtime,
meta:get_int("recipe_idx"), true))
return true
end
--[[
-----------------
Node registration
-----------------
]]--
minetest.register_node("biogasmachines:gasifier", { minetest.register_node("biogasmachines:gasifier", {
description = "Tubelib Coal Gasifier", description = "Tubelib Gasifier",
tiles = { tiles = {
-- up, down, right, left, back, front -- up, down, right, left, back, front
"biogasmachines_gasifier_top.png", "biogasmachines_gasifier_top.png",
"tubelib_front.png", "biogasmachines_bottom.png",
"tubelib_front.png", "biogasmachines_gasifier_side.png",
"tubelib_front.png", "biogasmachines_gasifier_side.png",
"tubelib_front.png", "biogasmachines_gasifier_side.png",
"tubelib_front.png", "biogasmachines_gasifier_side.png"
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -0.5, -0.5, -0.5, 0.5, 0.375, 0.5 },
{ -0.375, 0.375, -0.375, 0.375, 0.5, 0.375 },
},
},
selection_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, 0.375, 0.5 },
},
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = { choppy = 2, cracky = 2, crumbly = 2 },
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
can_dig = can_dig,
after_dig_node = after_dig_node,
on_punch = on_punch,
on_rotate = screwdriver.disallow,
on_timer = on_timer,
on_receive_fields = on_receive_fields,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_move = allow_metadata_inventory_move,
allow_metadata_inventory_take = allow_metadata_inventory_take,
after_place_node = function(pos, placer, itemstack, pointed_thing)
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local number = tubelib.add_node(pos, "biogasmachines:gasifier")
local inv = meta:get_inventory()
inv:set_size('src', INV_H * INV_IN_W)
inv:set_size('cur', 1)
inv:set_size('dst', INV_H * INV_OUT_W)
local label = minetest.registered_nodes[node.name].description
meta:set_string("number", number)
meta:set_string("owner", placer:get_player_name())
meta:set_int("running", tubelib.STATE_STOPPED)
meta:set_string("item_name", "")
meta:set_int("item_ticks", -1)
meta:set_int("recipe_idx", 1)
meta:set_string("infotext", label .. " " .. number .. ": stopped")
meta:set_string("formspec", formspec(tubelib.STOPPED, 0, 1, true))
end,
})
minetest.register_node("biogasmachines:gasifier_active", {
description = "Tubelib Gasifier",
tiles = {
-- up, down, right, left, back, front
{
image = "biogasmachines_gasifier_active_top.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 4.0,
},
},
"biogasmachines_bottom.png",
"biogasmachines_gasifier_side.png",
"biogasmachines_gasifier_side.png",
"biogasmachines_gasifier_side.png",
"biogasmachines_gasifier_side.png"
}, },
drawtype = "nodebox", drawtype = "nodebox",
node_box = { node_box = {
type = "fixed", type = "fixed",
fixed = { fixed = {
{-0.5, -0.5, -0.5, 0.5, 0.5, -0.375}, { -0.5, -0.5, -0.5, 0.5, 0.375, 0.5 },
{-0.5, -0.5, 0.375, 0.5, 0.5, 0.5}, { -0.375, 0.375, -0.375, 0.375, 0.5, 0.375 },
{-0.5, -0.5, -0.5, -0.375, 0.5, 0.5},
{0.375, -0.5, -0.5, 0.5, 0.5, 0.5},
{-0.375, -0.5, -0.375, 0.375, 0.375, 0.375},
}, },
},
selection_box = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
}, },
selection_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, 0.375, 0.5 },
},
paramtype = "light", paramtype = "light",
sunlight_propagates = true, sunlight_propagates = true,
paramtype2 = "facedir", paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2}, groups = { crumbly = 0, not_in_creative_inventory = 1 },
is_ground_content = false, is_ground_content = false,
sounds = default.node_sound_wood_defaults(), light_source = 5,
sounds = default.node_sound_metal_defaults(),
can_dig = can_dig,
after_dig_node = after_dig_node,
on_punch = on_punch,
on_rotate = screwdriver.disallow,
on_timer = on_timer,
on_receive_fields = on_receive_fields,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_move = allow_metadata_inventory_move,
allow_metadata_inventory_take = allow_metadata_inventory_take,
drop = "biogasmachines:gasifier",
}) })
tubelib.register_node("biogasmachines:gasifier", { "biogasmachines:gasifier_active" }, {
on_push_item = function(pos, side, item)
local meta = minetest.get_meta(pos)
if is_input_item(item) then
return tubelib.put_item(meta, "src", item)
end
return false
end,
on_pull_item = function(pos, side)
local meta = minetest.get_meta(pos)
return tubelib.get_item(meta, "dst")
end,
on_unpull_item = function(pos, side, item)
local meta = minetest.get_meta(pos)
return tubelib.put_item(meta, "dst", item)
end,
on_recv_message = function(pos, topic, payload)
local meta = minetest.get_meta(pos)
if topic == "on" then
gasifier_start(pos)
elseif topic == "off" then
gasifier_stop(pos)
elseif topic == "state" then
return tubelib.statestring(meta:get_int("running"))
else
return "unsupported"
end
end,
})
--[[
--------
Crafting
--------
]]--
minetest.register_craft({
output = "biogasmachines:gasifier",
recipe = {
{ "default:steelblock", "default:glass", "default:steelblock" },
{ "default:mese_crystal", "default:gold_ingot", "tubelib:tube1" },
{ "group:wood", "default:gold_ingot", "group:wood" },
},
})
--[[
-------
Recipes
-------
]]--
biogasmachines.add_gasifier_recipe({
input = "default:coalblock",
count = 8,
time = 12,
extra = "tubelib_addons1:biofuel 1",
})
biogasmachines.add_gasifier_recipe({
input = "farming:straw",
count = 3,
time = 8,
})
-- Unified Inventory hints
if minetest.get_modpath("unified_inventory") then
unified_inventory.register_craft_type("gasifier", {
description = "Gasifier",
icon = 'biogasmachines_gasifier_top.png',
width = 1,
height = 1,
})
for i, r in pairs(biogas_recipes) do
unified_inventory.register_craft({
type = "gasifier",
items = { i },
output = "tubelib_addons1:biogas " .. tostring(r.count),
})
end
end

View File

@ -20,5 +20,5 @@ dofile(minetest.get_modpath("biogasmachines").."/functions.lua")
-- machines -- machines
dofile(minetest.get_modpath("biogasmachines").."/freezer.lua") dofile(minetest.get_modpath("biogasmachines").."/freezer.lua")
dofile(minetest.get_modpath("biogasmachines").."/gasifier.lua") dofile(minetest.get_modpath("biogasmachines").."/gasifier.lua")
dofile(minetest.get_modpath("biogasmachines").."/gasfurnace.lua") --dofile(minetest.get_modpath("biogasmachines").."/gasfurnace.lua")
dofile(minetest.get_modpath("biogasmachines").."/htcompressor.lua") --dofile(minetest.get_modpath("biogasmachines").."/htcompressor.lua")

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 585 B

After

Width:  |  Height:  |  Size: 897 B