-- ### -- Fashion -- ### -- some meshes use more than one texture, and which texture is the main skin -- texture can only be derived from the name of the mesh yl_speak_up.get_mesh = function(pname) if(not(pname)) then return "error" end local obj = yl_speak_up.speak_to[pname].obj if(not(obj)) then return "error" end local entity = obj:get_luaentity() if(not(entity)) then return "error" end return entity.mesh end -- diffrent mobs (distinguished by self.name) may want to wear diffrent skins -- even if they share the same model; find out which mob we're dealing with yl_speak_up.get_mob_type = function(pname) if(not(pname)) then return "error" end local obj = yl_speak_up.speak_to[pname].obj if(not(obj)) then return "error" end local entity = obj:get_luaentity() if(not(entity)) then return "error" end return entity.name end local function cape2texture(t) return "yl_speak_up_mask_cape.png^[combine:32x64:56,20=" .. t end local function shield2texture(t) return "yl_speak_up_mask_shield.png^[combine:32x64:0,0=(" .. t .. ")" end local function textures2skin(textures) local temp = {} -- Cape local cape = cape2texture(textures[1]) -- Main local main = textures[2] -- left (Shield) local left = shield2texture(textures[3]) -- right (Sword) local right = textures[4] temp = {cape, main, left, right} return temp end local function set_textures(obj, textures) -- this function takes the base name of the textures, converts them to usable textures and stores those on the NPC local skins = textures2skin(textures) local ent = obj:get_luaentity() ent.yl_speak_up.skin = skins obj:set_properties({textures = skins}) end local function get_npc_skins(skin) local retstring = "yl_speak_up_main_default.png" local rettable = {"yl_speak_up_main_default.png"} local temp = {} -- get the files out of modpath local mp_list = minetest.get_dir_list(yl_speak_up.modpath .. DIR_DELIM .. "textures", false) -- get the files out of worlddir local wp_list = minetest.get_dir_list( yl_speak_up.worldpath .. DIR_DELIM .. "worldmods" .. DIR_DELIM .. "yl_npc" .. DIR_DELIM .. "textures", false ) -- Let's join both lists. table.insert_all(temp, mp_list) table.insert_all(temp, wp_list) --[[ Let's see if the files are the ones we want. Format is yl_npc_main_name.png <-- Those are the ones we want yl_npc_cape_name.png yl_npc_item_name.png ]]-- local index = 1 local retindex = 1 for _, v in pairs(temp) do local s = string.split(v, "_") if s[1] == "yl" and s[2] == "npc" and s[3] == "main" then index = index + 1 retstring = retstring .. "," .. v table.insert(rettable, v) if v == skin then retindex = index end end end return rettable, retstring, retindex end local function get_npc_capes(cape) local retstring = "yl_npc_cape_default.png" local rettable = {"yl_npc_cape_default.png"} local temp = {} -- get the files out of modpath local mp_list = minetest.get_dir_list(yl_speak_up.modpath .. DIR_DELIM .. "textures", false) -- get the files out of worlddir local wp_list = minetest.get_dir_list( yl_speak_up.worldpath .. DIR_DELIM .. "worldmods" .. DIR_DELIM .. "yl_npc" .. DIR_DELIM .. "textures", false ) -- Let's join both lists. table.insert_all(temp, mp_list) table.insert_all(temp, wp_list) --[[ Let's see if the files are the ones we want. Format is yl_npc_main_name.png yl_npc_cape_name.png <-- Those are the ones we want yl_npc_item_name.png ]]-- local index = 1 local retindex = 1 for _, v in pairs(temp) do local s = string.split(v, "_") if s[1] == "yl" and s[2] == "npc" and s[3] == "cape" then index = index + 1 retstring = retstring .. "," .. v table.insert(rettable, v) if cape ~= "" and v == cape then retindex = index end end end return rettable, retstring, retindex end -- TODO: this function is now kind of obsolete local function create_preview(main_skin) if main_skin == nil or main_skin == "" then main_skin = "default_greyscale.png" end local player_skin = "(" .. main_skin .. ")" local skin = "" -- Consistent on both sizes: --Chest skin = skin .. "([combine:16x32:-16,-12=" .. player_skin .. "^[mask:yl_speak_up_mask_chest.png)^" --Head skin = skin .. "([combine:16x32:-4,-8=" .. player_skin .. "^[mask:yl_speak_up_mask_head.png)^" --Hat skin = skin .. "([combine:16x32:-36,-8=" .. player_skin .. "^[mask:yl_speak_up_mask_head.png)^" --Right Arm skin = skin .. "([combine:16x32:-44,-12=" .. player_skin .. "^[mask:yl_speak_up_mask_rarm.png)^" --Right Leg skin = skin .. "([combine:16x32:0,0=" .. player_skin .. "^[mask:yl_speak_up_mask_rleg.png)^" -- Left Arm skin = skin .. "([combine:16x32:-24,-44=" .. player_skin .. "^[mask:(yl_speak_up_mask_rarm.png^[transformFX))^" --Left Leg skin = skin .. "([combine:16x32:-12,-32=" .. player_skin .. "^[mask:(yl_speak_up_mask_rleg.png^[transformFX))^" -- Add overlays for 64x skins. these wont appear if skin is 32x because it will be cropped out --Chest Overlay skin = skin .. "([combine:16x32:-16,-28=" .. player_skin .. "^[mask:yl_speak_up_mask_chest.png)^" --Right Arm Overlay skin = skin .. "([combine:16x32:-44,-28=" .. player_skin .. "^[mask:yl_speak_up_mask_rarm.png)^" --Right Leg Overlay skin = skin .. "([combine:16x32:0,-16=" .. player_skin .. "^[mask:yl_speak_up_mask_rleg.png)^" --Left Arm Overlay skin = skin .. "([combine:16x32:-40,-44=" .. player_skin .. "^[mask:(yl_speak_up_mask_rarm.png^[transformFX))^" --Left Leg Overlay skin = skin .. "([combine:16x32:4,-32=" .. player_skin .. "^[mask:(yl_speak_up_mask_rleg.png^[transformFX))" -- Full Preview skin = "(((" .. skin .. ")^[resize:64x128)^[mask:yl_speak_up_transform.png)" return skin end -- TODO: link that somehow for NPC that support it yl_speak_up.get_fs_fashion_extended = function(pname) local textures = yl_speak_up.speak_to[pname].textures local maintable, mainlist, mainindex = get_npc_skins(textures[2]) local capetable, capelist, capeindex = get_npc_capes(textures[1]) local preview = create_preview(textures[2]) local button_cancel = "Cancel" -- is this player editing this particular NPC? then rename the button if( yl_speak_up.edit_mode[pname] and yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id) then button_cancel = "Back" end local formspec = { "size[13.4,15]", "dropdown[0.3,0.2;4,0.75;set_skin;", mainlist or "", ";", mainindex or "", "]", "label[4.6,0.45;", yl_speak_up.speak_to[pname].n_id, "]", "label[6,0.45;", (yl_speak_up.speak_to[pname].n_npc or "- nameless -"), "]", "dropdown[9.1,0.2;4,0.75;set_cape;", capelist or "", ";", capeindex or "", "]", "label[0.3,3.2;", textures[4] or "", "]", "label[9.1,3.2;", textures[3] or "", "]", "field_close_on_enter[set_sword;false]", "field_close_on_enter[set_shield;false]", "image[0.3,1;4,2;", textures[2] or "", "]", -- Main "image[9.1,1;4,2;", textures[1] or "", "]", -- Cape "image[0.3,4.2;4,4;", textures[4] or "", "]", -- Sword "image[9.1,4.2;4,4;", textures[3] or "", "]", --textures[3],"]", -- Shield "image[4.7,1;4,8;", preview or "", "]", "button[0.3,8.4;3,0.75;button_cancel;"..button_cancel.."]", "button[10.1,8.4;3,0.75;button_save;Save]", "list[current_player;main;1.8,10;8,4;]", -- set wielded items "label[0.3,9.7;Wield\nleft:]", "label[12.0,9.7;Wield\nright:]", "list[detached:yl_speak_up_player_"..tostring(pname)..";wield;0.3,10.5;1,1;]", "list[detached:yl_speak_up_player_"..tostring(pname)..";wield;12.0,10.5;1,1;1]", "button[0.3,11.7;1,0.6;button_wield_left;Set]", "button[12.0,11.7;1,0.6;button_wield_right;Set]", "tooltip[button_wield_left;Set and store what your NPC shall wield in its left hand.]", "tooltip[button_wield_right;Set and store what your NPC shall wield in its right hand.]", } return table.concat(formspec, "") end yl_speak_up.fashion_wield_give_items_back = function(player, pname) -- move the item back to the player's inventory (if possible) local trade_inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname}) local player_inv = player:get_inventory() local left_stack = trade_inv:get_stack("wield", 1) local right_stack = trade_inv:get_stack("wield", 2) if(left_stack and not(left_stack:is_empty()) and player_inv:add_item("main", left_stack)) then trade_inv:set_stack("wield", 1, "") end if(right_stack and not(right_stack:is_empty()) and player_inv:add_item("main", right_stack)) then trade_inv:set_stack("wield", 2, "") end end -- normal skins for NPC - without wielded items or capes etc. yl_speak_up.input_fashion = function(player, formname, fields) if formname ~= "yl_speak_up:fashion" then return end local pname = player:get_player_name() local n_id = yl_speak_up.speak_to[pname].n_id -- is the player editing this npc? if not: abort if(not(yl_speak_up.edit_mode[pname]) or (yl_speak_up.edit_mode[pname] ~= n_id)) then return "" end -- catch ESC as well if(not(fields) or (fields.quit or fields.button_cancel or fields.button_exit)) then yl_speak_up.show_fs(player, "talk", {n_id = yl_speak_up.speak_to[pname].n_id, d_id = yl_speak_up.speak_to[pname].d_id}) return end -- which texture from the textures list are we talking about? -- this depends on the model! local mesh = yl_speak_up.get_mesh(pname) local texture_index = yl_speak_up.mesh_data[mesh].texture_index -- show extra formspec with wielded item configuration and cape setup if(fields.button_config_wielded_items and yl_speak_up.mesh_data[mesh].can_show_wielded_items) then yl_speak_up.show_fs(player, "fashion_extended") return end -- which skins are available? this depends on mob_type local mob_type = yl_speak_up.get_mob_type(pname) local skins = yl_speak_up.mob_skins[mob_type] local textures = yl_speak_up.speak_to[pname].textures -- fallback if something went wrong (i.e. unkown NPC) if(not(skins)) then skins = {textures[texture_index]} end local skin = (textures[texture_index] or "") local skin_index = table.indexof(skins, skin) if(skin_index == -1) then skin_index = 1 end local new_skin = skin -- swithc back to the stored old skin if(fields.button_old_skin) then local old_texture = yl_speak_up.speak_to[pname].old_texture if(old_texture) then new_skin = old_texture end -- store the new skin elseif(fields.button_store_new_skin) then yl_speak_up.speak_to[pname].old_texture = skin -- show previous skin elseif(fields.button_prev_skin) then if(skin_index > 1) then new_skin = skins[skin_index - 1] else new_skin = skins[#skins] end -- show next skin elseif(fields.button_next_skin) then if(skin_index < #skins) then new_skin = skins[skin_index + 1] else new_skin = skins[1] end -- set directly via list elseif(fields.set_skin_normal) then local new_index = table.indexof(skins, fields.set_skin_normal) if(new_index ~= -1) then new_skin = skins[new_index] end end -- if there is a new skin to consider if(textures[texture_index] ~= new_skin) then textures[texture_index] = new_skin -- actually make sure that the NPC updates its texture local obj = yl_speak_up.speak_to[pname].obj if(obj) then obj:set_properties({ textures = textures }) end -- scrolling through the diffrent skins updates the skin; avoid spam in the log -- yl_speak_up.log_change(pname, n_id, -- "(fashion) skin changed to "..tostring(new_skin)..".") end if(fields.button_old_skin or fields.button_store_new_skin) then yl_speak_up.speak_to[pname].old_texture = nil yl_speak_up.show_fs(player, "talk", {n_id = yl_speak_up.speak_to[pname].n_id, d_id = yl_speak_up.speak_to[pname].d_id}) return end yl_speak_up.show_fs(player, "fashion") end -- set what the NPC shall wield and which cape to wear yl_speak_up.input_fashion_extended = function(player, formname, fields) if formname ~= "yl_speak_up:fashion_extended" then return end local pname = player:get_player_name() local textures = yl_speak_up.speak_to[pname].textures local n_id = yl_speak_up.speak_to[pname].n_id -- is the player editing this npc? if not: abort if(not(yl_speak_up.edit_mode[pname]) or (yl_speak_up.edit_mode[pname] ~= n_id)) then return "" end -- catch ESC as well if(not(fields) or (fields.quit or fields.button_cancel or fields.button_exit)) then yl_speak_up.fashion_wield_give_items_back(player, pname) yl_speak_up.show_fs(player, "fashion") return elseif(fields.button_wield_left or fields.button_wield_right) then local trade_inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname}) local player_inv = player:get_inventory() local left_stack = trade_inv:get_stack("wield", 1) local right_stack = trade_inv:get_stack("wield", 2) if(left_stack and left_stack:get_name() and fields.button_wield_left) then textures[4] = yl_speak_up.get_wield_texture(left_stack:get_name()) yl_speak_up.log_change(pname, n_id, "(fashion) sword changed to "..tostring(fields.set_sword)..".") end if(right_stack and right_stack:get_name() and fields.button_wield_right) then textures[3] = yl_speak_up.get_wield_texture(right_stack:get_name()) yl_speak_up.log_change(pname, n_id, "(fashion) shield changed to "..tostring(fields.set_shield)..".") end yl_speak_up.fashion_wield_give_items_back(player, pname) -- only change cape if there really is a diffrent one selected elseif(fields.set_cape and fields.set_cape ~= textures[1]) then -- only set the cape if it is part of the list of allowed capes local capetable, capelist, capeindex = get_npc_capes(textures[1]) for _, v in pairs(capetable) do if(v == fields.set_cape) then textures[1] = fields.set_cape yl_speak_up.log_change(pname, n_id, "(fashion) cape changed to "..tostring(fields.set_cape)..".") -- we have found it break end end -- only change the skin if there really is a diffrent new one selected elseif(fields.set_skin and fields.set_skin ~= textures[2]) then -- only set the skin if it is part of the list of allowed skins local maintable, mainlist, mainindex = get_npc_skins(textures[2]) for _, v in pairs(maintable) do if(v == fields.set_skin) then textures[2] = fields.set_skin yl_speak_up.log_change(pname, n_id, "(fashion) skin changed to "..tostring(fields.set_skin)..".") -- we have found it break end end elseif fields.button_save then local obj = yl_speak_up.speak_to[pname].obj if obj ~= nil and obj:get_luaentity() ~= nil then -- save textures yl_speak_up.speak_to[pname].skins = textures2skin(textures) set_textures(obj, textures) yl_speak_up.log_change(pname, n_id, "(fashion) saved changes.") end yl_speak_up.fashion_wield_give_items_back(player, pname) end yl_speak_up.show_fs(player, "fashion") end function yl_speak_up.fashion(player, obj) local luaentity = obj:get_luaentity() local pname = player:get_player_name() local npc_id = luaentity.yl_speak_up.id local skins = luaentity.yl_speak_up.skins local n_id = "n_" .. npc_id local t = luaentity.textures yl_speak_up.speak_to[pname] = {} yl_speak_up.speak_to[pname].n_id = n_id yl_speak_up.speak_to[pname].obj = obj yl_speak_up.speak_to[pname].textures = t yl_speak_up.speak_to[pname].skins = textures2skin(t) local dialog = yl_speak_up.load_dialog(n_id, false) if next(dialog) then yl_speak_up.speak_to[pname].n_npc = dialog.n_npc else yl_speak_up.speak_to[pname].n_npc = "Unknown" end yl_speak_up.show_fs(player, "fashion") end yl_speak_up.update_nametag = function(self) if self.yl_speak_up.npc_name then -- the nametag is normal (green) if(self.yl_speak_up.talk) then self.object:set_nametag_attributes({color="#00ff00", text=self.yl_speak_up.npc_name}) -- the nametag has the addition "[muted]" and is magenta when muted else self.object:set_nametag_attributes({color="#ff00ff", text=self.yl_speak_up.npc_name.." [muted]"}) end end end -- inspired/derived from the wieldview mod in 3darmor yl_speak_up.get_wield_texture = function(item) if(not(item) or not(minetest.registered_items[ item ])) then return "3d_armor_trans.png" end local def = minetest.registered_items[ item ] if(def.inventory_image ~= "") then return def.inventory_image elseif(def.tiles and type(def.tiles[1]) == "string" and def.tiles[1] ~= "") then return minetest.inventorycube(def.tiles[1]) end return "3d_armor_trans.png" end -- this only sets the *skin*, depending on the mesh of the NPC; -- capes and wielded items are supported by an extended formspec for those -- NPC that can handle them yl_speak_up.get_fs_fashion = function(pname) -- which texture from the textures list are we talking about? -- this depends on the model! local mesh = yl_speak_up.get_mesh(pname) local texture_index = yl_speak_up.mesh_data[mesh].texture_index -- which skins are available? this depends on mob_type local mob_type = yl_speak_up.get_mob_type(pname) local skins = yl_speak_up.mob_skins[mob_type] local textures = yl_speak_up.speak_to[pname].textures local skin = (textures[texture_index] or "") -- store the old texture so that we can go back to it local old_texture = yl_speak_up.speak_to[pname].old_texture if(not(old_texture)) then yl_speak_up.speak_to[pname].old_texture = skin old_texture = skin end -- fallback if something went wrong if(not(skins)) then skins = {old_texture} end local button_cancel = "Cancel" -- is this player editing this particular NPC? then rename the button if( yl_speak_up.edit_mode[pname] and yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id) then button_cancel = "Back" end local skin_list = table.concat(skins, ",") local skin_index = table.indexof(skins, skin) if(skin_index == -1) then skin_index = "" end local preview = yl_speak_up.mesh_data[mesh].skin_preview(skin) local formspec = { "container[0.5,4.0]", "dropdown[0.75,14.1;16.25,1.5;set_skin_normal;", skin_list or "", ";", tostring(skin_index) or "", "]", "label[0.75,13.6;The name of this skin is:]", "button[0.75,0.75;1.2,12;button_prev_skin;<]", "button[15.75,0.75;1.2,12;button_next_skin;>]", "tooltip[button_prev_skin;Select previous skin in list.]", "tooltip[button_next_skin;Select next skin in list.]", "tooltip[set_skin_normal;Select a skin from the list.]", preview, -- we add a special button for setting the skin in the player answer/reply window "container_end[]", } local left_window = table.concat(formspec, "") formspec = {} local h = -0.8 local button_text = "This shall be your new skin. Wear it proudly!" if(skin == old_texture) then button_text = "This is your old skin. It is fine. Keep it!" end h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h, "button_store_new_skin", "The NPC will wear the currently selected skin.", button_text, true, nil, nil, nil) h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h, "button_old_skin", "The NPC will wear the skin he wore before you started changing it.", "On a second throught - Keep your old skin. It was fine.", (skin ~= old_texture), nil, nil, nil) h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h, "button_config_wielded_items", "What shall the NPC wield, and which cape shall he wear?", "I'll tell you what you shall wield.", (yl_speak_up.mesh_data[mesh].can_show_wielded_items), "You don't know how to show wielded items. Thus, we can't configure them.", nil, nil) return yl_speak_up.show_fs_decorated(pname, true, h, "", left_window, table.concat(formspec, ""), nil, h) end