From 45d789850b16a67da1f3e33d712bbe4285386da6 Mon Sep 17 00:00:00 2001 From: Alexsandro Percy Date: Mon, 2 Jun 2025 18:46:24 -0300 Subject: [PATCH] added plane painter --- init.lua | 1 + painter.lua | 181 ++++++++++++++++++++++++++++++++++ textures/airutils_painter.png | Bin 0 -> 5592 bytes 3 files changed, 182 insertions(+) create mode 100644 painter.lua create mode 100644 textures/airutils_painter.png diff --git a/init.lua b/init.lua index 097d1f8..f1bc8e2 100644 --- a/init.lua +++ b/init.lua @@ -82,6 +82,7 @@ dofile(core.get_modpath("airutils") .. DIR_DELIM .. "lib_planes" .. DIR_DELIM .. dofile(core.get_modpath("airutils") .. DIR_DELIM .. "lib_copter" .. DIR_DELIM .. "init.lua") dofile(core.get_modpath("airutils") .. DIR_DELIM .. "texture_management.lua") dofile(core.get_modpath("airutils") .. DIR_DELIM .. "attach_extern_ent.lua") +dofile(core.get_modpath("airutils") .. DIR_DELIM .. "painter.lua") if airutils._use_signs_api then dofile(core.get_modpath("airutils") .. DIR_DELIM .. "text.lua") end local is_biofuel_installed = false diff --git a/painter.lua b/painter.lua new file mode 100644 index 0000000..1d84b5e --- /dev/null +++ b/painter.lua @@ -0,0 +1,181 @@ +--[[ +Copyright (C) 2022 Alexsandro Percy +Copyright (C) 2018 Hume2 + + 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 S = airutils.S + +local function is_hex(color) + if not color or color:len() ~= 7 then return end + return color:match("#%x%x%x%x%x%x") +end + +-- hex translation +local function hex_to_rgb(hex_value) + hex_value = hex_value:gsub("#","") + local rgb = { + r = tonumber("0x"..hex_value:sub(1,2)), + g = tonumber("0x"..hex_value:sub(3,4)), + b = tonumber("0x"..hex_value:sub(5,6)), + } + return rgb +end + +local function rgb_to_hex(rgb) + return string.format("#%02X%02X%02X", rgb.r, rgb.g, rgb.b) +end + +-- Painter formspec +local function painter_form(player, rgb) + local color = rgb_to_hex(rgb) + minetest.show_formspec(player:get_player_name(), "airutils:painter", + -- Init formspec + "formspec_version[3]" .. -- Minetest 5.2+ + "size[5.6,5.2;true]" .. + "position[0.5,0.45]" .. + + -- Color preview + "image[0.2,0.2;2,2;airutils_painting.png^[colorize:" .. color .. ":255]" .. + "label[0.3,1.2;" .. S("Preview") .. "]" .. + + -- Hex field + "field_close_on_enter[hex;false]" .. + "field[2.4,0.2;3,0.8;hex;;" .. color .. "]" .. + "button[2.4,1;3,0.8;set_hex;" .. S("Set Hex") .. "]" .. + + -- RGB sliders + "container[0,2.4]" .. + "scrollbaroptions[min=0;max=255;smallstep=20]" .. + + "box[0.4,0;4.77,0.38;red]" .. + "label[0.2,0.2;-]" .. + "scrollbar[0.4,0;4.8,0.4;horizontal;r;" .. rgb.r .."]" .. + "label[5.2,0.2;+]" .. + + "box[0.4,0.6;4.77,0.38;green]" .. + "label[0.2,0.8;-]" .. + "scrollbar[0.4,0.6;4.8,0.4;horizontal;g;" .. rgb.g .."]" .. + "label[5.2,0.8;+]" .. + + "box[0.4,1.2;4.77,0.38;blue]" .. + "label[0.2,1.4;-]" .. + "scrollbar[0.4,1.2;4.8,0.4;horizontal;b;" .. rgb.b .. "]" .. + "label[5.2,1.4;+]" .. + + "container_end[]" .. + + -- Bottom buttons + "button_exit[0.2,4.2;2.8,0.8;set_color;" .. S("Set Color") .. "]" .. + "button_exit[3.2,4.2;2.2,0.8;quit;" .. S("Cancel") .. "]" + ) +end + +local airutils_being_painted = {} +local formspec_timers = {} + +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname == "airutils:painter" then + if formspec_timers[player] then + formspec_timers[player]:cancel() + formspec_timers[player] = nil + end + + local function parse_scrollbar_field(value) + value = value or "" + local num = math.floor(tonumber((value:gsub(".*:", ""))) or 0) + if num > 255 or num < 0 then num = 0 end + return num + end + + local rgb = { + r = parse_scrollbar_field(fields.r), + g = parse_scrollbar_field(fields.g), + b = parse_scrollbar_field(fields.b), + } + + if fields.set_hex or fields.key_enter then + if is_hex(fields.hex) then + painter_form(player, hex_to_rgb(fields.hex)) + else + painter_form(player, rgb) + end + elseif fields.set_color then + local object_ref = airutils_being_painted[player] + if object_ref and object_ref:get_pos() then + local luaentity = object_ref:get_luaentity() + luaentity:_change_color(rgb_to_hex(rgb)) + end + airutils_being_painted[player] = nil + elseif fields.r and fields.r:find("^CHG") or + fields.g and fields.g:find("^CHG") or + fields.b and fields.b:find("^CHG") then -- Has a scrollbar changed? + formspec_timers[player] = minetest.after(0.2, function(itemstack, name) + local player = minetest.get_player_by_name(name) + if player then + painter_form(player, rgb) + end + formspec_timers[player] = nil + end, itemstack, player:get_player_name()) + end + + if fields.quit then + airutils_being_painted[player] = nil + end + return true + end +end) + + +-- Make the actual thingy +minetest.register_tool("airutils:painter", { + description = S("Plane Painter"), + inventory_image = "airutils_painter.png", + wield_scale = {x = 2, y = 2, z = 1}, + on_use = function(itemstack, user, pointed_thing) + if pointed_thing.type == "object" and not pointed_thing.ref:is_player() then + local luaentity = pointed_thing.ref:get_luaentity() + if luaentity and type(luaentity._change_color) == "function" then + local player_name = user:get_player_name() + if not luaentity.owner or luaentity.owner == player_name then + airutils_being_painted[user] = pointed_thing.ref + local color = luaentity._color + local rgb = is_hex(color) and hex_to_rgb(color) or {r = 0, g = 0, b = 0} + painter_form(user, rgb) + else + minetest.chat_send_player(player_name, S("Only the owner can paint this entity.")) + end + end + end + end +}) + +minetest.register_on_leaveplayer(function(player, timed_out) + airutils_being_painted[player] = nil +end) + +minetest.register_craft({ + output = "airutils:painter", + recipe = { + {"", "default:steel_ingot", ""}, + {"dye:red", "dye:green", "dye:blue"}, + {"", "default:steel_ingot", "default:steel_ingot"}, + }, +}) diff --git a/textures/airutils_painter.png b/textures/airutils_painter.png new file mode 100644 index 0000000000000000000000000000000000000000..b67670ada03c6383775d62640c2ad43f290ec639 GIT binary patch literal 5592 zcmeHKdpJ~E8z0vkC6$V5VvLTF%zcKL$;fSD+(T~VvS((`n3(%8gQGf0Nu>+A&{do` zQYleKp(06?^wCAS=-`A(oRp$(&*=Jm=bxVEJO4G$vuE$U*6;nj@B3TtT5GROem-sn z`g8SRFqi?;-PIquPg7lby3oJb&<6kp(^;Pw7$Wxv6bP|Y#O23=2zi1SM1V>@7Y0+_ zD+rE}4%tq5e8-~8TlD7Vu7P48(t z`d^!gGU5?)=HO{@i#_h|c$sr(z_I<*)PpQ#gtM@?q{rjQD6y+$@=<*EtDMRY(*k(!ug(S^xMa|%XzW@pjP+?{Rh-XbN!Zvq$_8P4NTEhD1= zE_?=Gz|7WjXv}}0A9?QPl-vv!w=C^ycGNX(o{MI6Rg);PH#t@(&mtnk9NVp7d&}hd zR@C(xy}u29&xIWv8Jl}v-$btUXDMUG+NMTE{BWT*Vbg3AT3L*tf8l)%gmd&pj(ZI( z#6m*4T?0>pakQP%Z-?6OaE{Fq#?DxV{n<_S&RyA#ty%NgccS2RQ@kjHGitnN=!CY> zdQo@u42q0+`(>Rv#^KUubt_(+AGx?Kuh3~7`J#@QbDFu%))@kMOZ_yq*D@oNQ;WRe z=ysL|a~6KqfmxzOQv!*+2HVA%$y2G_S!Wy_>ShQ3^3%_|0?VY?>7&b%4ajYQXO^X8 z+T*Pa`ybc!Mm=9M$X%EGcDrZsLuhNa`5Pash^fj^@++_1_p`?8StiE6V=!H0G&irP4e4T`}rq*Seaw_kBK#h>v z^mq(d9ade@9k(`U)t$@{4ZZQ{daM(6!An=I+|0M}K5@QLw%VqmqIO9q^Ur$PtK5vq zePsyj* zU#!gQ;m266eoF|d5Cl{V+MV3nQu^5PvL}{dXQFd8+`4}KtIW;CY?6HL^b&U@I+Pa7X|_5K*{n_s@a�+2dyXXM_|J%p+ew9)>jx#W z;Jg)hWR;Uw$L6|)QuNwgZKv`>f@dd^7M$w-i^Vmz)+^<7nMK|E5Fl$>E>|=O>h)Xa zMw&L~Z3snFj~0|&f4<{LDDzcwxxC!a#BB4JDPxGfrS3TVa5tu4z^?cO=NLx=p-ezmI^%l_>}RAc9jE|}N- zbLo4I298LUt-F22(`M>H{9t9^fVIV<_6yX?jqjIL3~LTsdG5T+THGdoj>xY%?L8yo zOj&8e19qm1WuKlj3uBN{dM3}ZDDK`ZbU_5t)}zP#9r)~L0qRXe()Ll;F%w)@e=9n6 z#h}Ju?U{)5*3>})e!k)vef8og?0O-AfExpM?uxsUsM;rOrVV4_+T8h z4ApWnuQL}0(^$`Uaq(lixO`fVp;dTa@(#NDWk+~jWWX6@wtoCH$yLAoHfiaURkPCl z%x~+L(DtkxH7D`i{a3Gvi^-cPa^6)`e>Lv+x;XW8G65hi$(Hqd*n$z`* z*{;iq%I*-P-R4tq| z{7{o#93g5--sb?D^UOL<3l7(`U|fqBN>C`0L>~_DUyo=#FOW)oj4GU=eRD1!+8;x_ zy=ZKa00VGDED)mx`MJid^Qd{J4AMDRIuWH8wq=Ph;tqxtTM zQZOLVCy<>O!=`eOOC0p=l{APz0LlS`QV=VY(Uf$gnwJJWtD3P$gc>4`p(8`Q{SYo9 zDTp9pNEjTNq2$LCkPi9?dnt!Y^LJhPkpg<7BctVVF%64VC=?h45hIfFuy`t!ip3GI z1OggDpk)a{IiN%fWfm%m2@Y3K#+LHMa=u82P;mk*QJkEPL_+*KT3gs4GQ*hXy?Zt7@ zSamuaHWrKp1rSsQdBuO@(v9iu_t`=v0go>btF0ihzd_3R+%IB%6Pv0>ozB;RK<1x$ zzd?V>T@8j%-rh7<5j##59@CYMRQacIL~K5Xrha7Gu?Qd=XNx9M@hminMdYFZJRXO( zBY+?a&$ZSfQFMV0C*r)Eaa&>RD#o-{Frnk0fYO};uj0ZxsU-J>BSeuDZg|C@&#ak z98jr=wVOo07@p_CZ<3vU)_a3sLPfHuqT|z zfOwFjb_Cf?bg`oWArFM+_s4?z)Xx8h!NNfUMdgt3Xch^iqDe#?2~CCOBifcrC4(S= zfTsYq-?Ga@T)6^}f=)arQz#p#fYjL_tkhC2{1&Z<230!Ypt+4Bpz(x2Jb{KM(IAI7 zA`OQ_Vm~H~Rn6*88QWw3M-zKB;HzZ-vYTjwmKSKX!hT+^K5C|t@n8IX9E*R^14RA9 z$#?1dL#`ikeU}2?1^!W8Kjiu@1-=XXqq_cYa_N6L@PI<-D@XwyXIw{v=0QgxU6!Yt zE6g621e;;I%|rug`AO^^Ducl=7OG1_S+FD?YMdlzdNU?^C#Ddfu%WR(B5jjT+NvRQ&cKEnaLjo;ow%V2+Nl z@tPE5SpLkN-4`4tBBkWH3OY+E0)6%*q+D?dT zCf9<@Q|#?;5UXBHuDKdFnOfU7T^F#Lr)y|jA1WCh)vjr7h`TaU8GibKP?UChx+(UC zS4m5LTeRk*G8(01>d57k>+<3VY#K4edJlGYs5s%y@>+{_a%o%jyak&_{8Ntn6?ly@ zFN(Ux;=qNOSEFCA99XE;vHyAQi@XOzE8Y&>7}n5jN~_WsCBA~xgE1LCuE(9D*8LYw CvYH