-- painting - in-game painting for minetest-c55 -- THIS MOD CODE AND TEXTURES LICENSED -- <3 TO YOU <3 -- UNDER TERMS OF GPL LICENSE -- 2012 obneq aka jin xi -- a picture is drawn using a node(box) to draw the supporting canvas -- and an entity which has the painting as its texture. -- this texture is created by minetest-c55's internal image -- compositing engine (see tile.cpp). dofile(minetest.get_modpath("painting").."/crafts.lua") textures = { white = "white.png", yellow = "yellow.png", orange = "orange.png", red = "red.png", violet = "violet.png", blue = "blue.png", green = "green.png", magenta = "magenta.png", cyan = "cyan.png", grey = "grey.png", darkgrey = "darkgrey.png", black = "black.png", darkgreen = "darkgreen.png", brown="brown.png", pink = "pink.png" } thickness = 0.1 -- picture node picbox = { type = "fixed", fixed = { -0.499, -0.499, 0.499, 0.499, 0.499, 0.499 - thickness } } picnode = { description = "Picture", tiles = { "white.png" }, inventory_image = "painted.png", drawtype = "nodebox", sunlight_propagates = true, paramtype = "light", paramtype2 = "facedir", node_box = picbox, selection_box = picbox, groups = { snappy = 2, choppy = 2, oddly_breakable_by_hand = 2 }, --handle that right below, don't drop anything drop = "", after_dig_node=function(pos, oldnode, oldmetadata, digger) --find and remove the entity local objects = minetest.env:get_objects_inside_radius(pos, 0.5) for _, e in ipairs(objects) do if e:get_luaentity().name == "painting:picent" then e:remove() end end --put picture data back into inventory item local data = oldmetadata.fields["painting:picturedata"] local item = { name = "painting:paintedcanvas", count = 1, metadata = data } digger:get_inventory():add_item("main", item) end } -- picture texture entity picent = { collisionbox = { 0, 0, 0, 0, 0, 0 }, visual = "upright_sprite", textures = { "white.png" }, on_activate = function(self, staticdata) local pos = self.object:getpos() local meta = minetest.env:get_meta(pos) local data = meta:get_string("painting:picturedata") data = minetest.deserialize(data) if not data.grid then return end self.object:set_properties({textures = { to_imagestring(data.grid, data.res) }}) end } paintbox = { [0] = { -0.5,-0.5,0,0.5,0.5,0 }, [1] = { 0,-0.5,-0.5,0,0.5,0.5 } } paintent = { collisionbox = { 0, 0, 0, 0, 0, 0 }, visual = "upright_sprite", textures = { "white.png" }, on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir) --check for brush local name = puncher:get_wielded_item():get_name() name = string.split(name, "_")[2] if not textures[name] then return end --get player eye level local ppos = puncher:getpos() ppos = { x = ppos.x, y = ppos.y+(1.5 + 1/16), z = ppos.z } local pos = self.object:getpos() local l = puncher:get_look_dir() local d = dirs[self.fd] local od = dirs[(self.fd + 1) % 4] local normal = { x = d.x, y = 0, z = d.z } local p = intersect(ppos, l, pos, normal) local off = -0.5 pos = { x = pos.x + off * od.x, y = pos.y + off, z = pos.z + off * od.z } p = sub(p, pos) local x = math.abs(p.x + p.z) local y = p.y x = round(x / (1/self.res)) y = round((1-y) / (1/self.res)) x = clamp(x, self.res) y = clamp(y-1, self.res) self.grid[x][y]=colors[name] self.object:set_properties({textures = { to_imagestring(self.grid, self.res) }}) local wielded = puncher:get_wielded_item() wielded:add_wear(65535/256) puncher:set_wielded_item(wielded) end, on_activate = function(self, staticdata) local data = minetest.deserialize(staticdata) if not data then return end self.fd = data.fd self.res = data.res self.grid = data.grid self.object:set_properties({ textures = { to_imagestring(self.grid, self.res) }}) self.object:set_properties({ collisionbox = paintbox[self.fd%2] }) end, get_staticdata = function(self) local data = { fd = self.fd, res = self.res, grid = self.grid } return minetest.serialize(data) end } -- just pure magic local walltoface = {-1, -1, 1, 3, 0, 2} --paintedcanvas picture inventory item paintedcanvas = { description = "Painted Canvas", inventory_image = "painted.png", stack_max = 1, on_place = function(itemstack, placer, pointed_thing) --place node local pos = pointed_thing.above local under = pointed_thing.under local above = pointed_thing.above local dir = { x = under.x - above.x, y = under.y - above.y, z = under.z - above.z } local wm = minetest.dir_to_wallmounted(dir) local fd = walltoface[wm + 1] if fd == -1 then return itemstack end minetest.env:add_node(pos, { name = "painting:pic", param2 = fd, paramtype2 = "none" }) --save metadata local data = itemstack:get_metadata() local meta = minetest.env:get_meta(pos) meta:set_string("painting:picturedata", data) --add entity dir = dirs[fd] local off = 0.5 - thickness - 0.01 pos = { x = pos.x + dir.x * off, y = pos.y, z = pos.z + dir.z * off } data = minetest.deserialize(data) local p = minetest.env:add_entity(pos, "painting:picent"):get_luaentity() p.object:set_properties({ textures = { to_imagestring(data.grid, data.res) }}) p.object:setyaw(math.pi * fd / -2) return ItemStack("") end } --canvas inventory item canvas = { description = "Canvas", inventory_image = "default_paper.png", stack_max = 99, } --canvas for drawing canvasbox = { type = "fixed", fixed = { -0.5, -0.5, 0.0, 0.5, 0.5, thickness } } canvasnode = { description = "Canvas", tiles = { "white.png" }, inventory_image = "painted.png", drawtype = "nodebox", sunlight_propagates = true, paramtype = "light", paramtype2 = "facedir", node_box = canvasbox, selection_box = canvasbox, groups = { snappy = 2, choppy = 2, oddly_breakable_by_hand = 2 }, drop = "", after_dig_node=function(pos, oldnode, oldmetadata, digger) --get data and remove pixels local data = {} local objects = minetest.env:get_objects_inside_radius(pos, 0.5) for _, e in ipairs(objects) do e = e:get_luaentity() if e.grid then data.grid = e.grid data.res = e.res end e.object:remove() end if data.grid then local item = { name = "painting:paintedcanvas", count = 1, metadata = minetest.serialize(data) } digger:get_inventory():add_item("main", item) end end } -- easel easelbox = { type="fixed", fixed = { --feet {-0.4, -0.5, -0.5, -0.3, -0.4, 0.5 }, { 0.3, -0.5, -0.5, 0.4, -0.4, 0.5 }, --legs {-0.4, -0.4, 0.1, -0.3, 1.5, 0.2 }, { 0.3, -0.4, 0.1, 0.4, 1.5, 0.2 }, --shelf {-0.5, 0.35, -0.3, 0.5, 0.45, 0.1 } } } easel = { description = "Easel", tiles = { "default_wood.png" }, drawtype = "nodebox", sunlight_propagates = true, paramtype = "light", paramtype2 = "facedir", node_box = easelbox, selection_box = easelbox, groups = { snappy = 2, choppy = 2, oddly_breakable_by_hand = 2 }, on_punch = function(pos, node, player) local wielded_raw = player:get_wielded_item():get_name() wielded = string.split(wielded_raw, "_") local name = wielded[1] local res = tonumber(wielded[2]) if name ~= "painting:canvas" then return end local meta = minetest.env:get_meta(pos) local fd = node.param2 pos = { x = pos.x, y = pos.y + 1, z = pos.z } if minetest.env:get_node(pos).name ~= "air" then return end minetest.env:add_node(pos, { name = "painting:canvasnode", param2 = fd, paramtype2 = "none" }) local dir = dirs[fd] pos = { x = pos.x - 0.01 * dir.x, y = pos.y, z = pos.z - 0.01 * dir.z } local p = minetest.env:add_entity(pos, "painting:paintent"):get_luaentity() p.object:set_properties({ collisionbox = paintbox[fd%2] }) p.object:setyaw(math.pi * fd / -2) p.grid = initgrid(res) p.res = res p.fd = fd meta:set_int("has_canvas", 1) local itemstack = ItemStack(wielded_raw) player:get_inventory():remove_item("main", itemstack) end, can_dig = function(pos,player) local meta = minetest.env:get_meta(pos) local inv = meta:get_inventory() if meta:get_int("has_canvas") == 0 then return true end return false end } --brushes local function table_copy(t) local t2 = {} for k,v in pairs(t) do t2[k] = v end return t2 end brush = { description = "brush", inventory_image = "default_tool_steelaxe.png", wield_image = "", wield_scale = { x = 1, y = 1, z = 1 }, stack_max = 99, liquids_pointable = false, tool_capabilities = { full_punch_interval = 1.0, max_drop_level=0, groupcaps = {} } } minetest.register_entity("painting:picent", picent) minetest.register_node("painting:pic", picnode) minetest.register_craftitem("painting:canvas_16", canvas) --minetest.register_craftitem("painting:canvas_32", canvas) --minetest.register_craftitem("painting:canvas_64", canvas) minetest.register_craftitem("painting:paintedcanvas", paintedcanvas) minetest.register_entity("painting:paintent", paintent) minetest.register_node("painting:canvasnode", canvasnode) minetest.register_node("painting:easel", easel) colors = {} revcolors = {} for color, _ in pairs(textures) do table.insert(revcolors, color) local brush_new = table_copy(brush) brush_new.description = color:gsub("^%l", string.upper).." brush" brush_new.inventory_image = "painting_brush_"..color..".png" minetest.register_tool("painting:brush_"..color, brush_new) minetest.register_craft({ output = "painting:brush_"..color, recipe = { {"dye:"..color}, {"default:stick"}, {"default:stick"} } }) end for i, color in ipairs(revcolors) do colors[color] = i end minetest.register_alias("easel", "painting:easel") minetest.register_alias("canvas", "painting:canvas_16") function initgrid(res) local grid, x, y = {} for x = 0, res - 1 do grid[x] = {} for y = 0, res - 1 do grid[x][y] = colors["white"] end end return grid end function to_imagestring(data, res) if not data then return end local imagestring = "[combine:"..res.."x"..res..":" for y = 0, res - 1 do for x = 0, res - 1 do imagestring = imagestring..x..","..y.."="..revcolors[ data[x][y] ]..".png:" end end return imagestring end dirs = { [0] = { x = 0, z = 1 }, [1] = { x = 1, z = 0 }, [2] = { x = 0, z =-1 }, [3] = { x =-1, z = 0 } } function sub(v, w) return { x = v.x - w.x, y = v.y - w.y, z = v.z - w.z } end function dot(v, w) return v.x * w.x + v.y * w.y + v.z * w.z end function intersect(pos, dir, origin, normal) local t = -(dot(sub(pos, origin), normal)) / dot(dir, normal) return { x = pos.x + dir.x * t, y = pos.y + dir.y * t, z = pos.z + dir.z * t } end function round(num) return math.floor(num+0.5) end function clamp(num, res) if num < 0 then return 0 elseif num > res - 1 then return res - 1 else return num end end