Initial working release with code based from skinsdb
|
@ -0,0 +1,88 @@
|
|||
# Change Player Model (chchar) Application Programming Interface (API)
|
||||
|
||||
## chchar.get_player_model(player)
|
||||
Return the model object assigned to the player. Returns default if nothing assigned.
|
||||
|
||||
## chchar.assign_player_model(player, model)
|
||||
Check if allowed and assign the model for the player without visual updates. The "model" parameter could be the model key or the model object.
|
||||
Returns false if model is not valid or applicable to player.
|
||||
|
||||
## chchar.update_player_model(player)
|
||||
Update selected model visuals on player.
|
||||
|
||||
## chchar.set_player_model(player, model)
|
||||
Function for external usage on model selection. This function assigns the model, calls the model:set_model(player) hook to update dynamic models, then update the visuals.
|
||||
|
||||
## chchar.get_modellist_for_player(playername)
|
||||
Get all allowed models for player. All public and all player's private models. If playername not given only public models returned.
|
||||
|
||||
## chchar.get_modellist_with_meta(key, value)
|
||||
Get all models with metadata key is set to value. Example:
|
||||
chchar.get_modellist_with_meta("playername", playername) - Get all private models (w.o. public) for playername.
|
||||
|
||||
|
||||
## chchar.new(key, object)
|
||||
Create and register a new model object for given key.
|
||||
- key: Unique models key, like "character_1"
|
||||
- object: Optional. Could be a prepared object with redefinitions
|
||||
|
||||
## chchar.get(key)
|
||||
Get existing model object.
|
||||
|
||||
HINT: During build-up phase maybe the next statement is useful.
|
||||
```
|
||||
local model = chchar.get(name) or chchar.new(name)
|
||||
```
|
||||
|
||||
|
||||
# Model object
|
||||
|
||||
## model:get_key()
|
||||
Get the unique model key.
|
||||
|
||||
## model:set_texture(texture)
|
||||
Set the list of textures used by the 3D model - usually at the init time only.
|
||||
|
||||
## model:get_texture()
|
||||
Get the list of textures used by the 3D model for any reason. Note to apply them the model:set_model() should be used.
|
||||
|
||||
## model:set_icon(texture)
|
||||
Set the model icon - usually at the init time only.
|
||||
|
||||
## model:get_icon()
|
||||
Get the model icon.
|
||||
|
||||
It is currently not possible to draw arbitrary 3D models in formspecs, except for when drawing nodes in inventory slots. An icon is the best alternative to this.
|
||||
|
||||
## model:set_model(player)
|
||||
Hook for dynamic models updates on select. Is called in chchar.set_player_model()
|
||||
In chchar the default implementation for this function is empty.
|
||||
|
||||
model:apply_model_to_player(player)
|
||||
Apply the model to the player. Called in chchar.update_player_model() to update visuals.
|
||||
|
||||
## model:set_meta(key, value)
|
||||
Add a meta information to the model object.
|
||||
|
||||
Note: the information is not stored, therefore should be filled each time during models registration.
|
||||
|
||||
## model:get_meta(key)
|
||||
The next metadata keys are filled or/and used interally in chchar framework, some of which are optional. These optional items are denoted with '*'.
|
||||
- name - A name for the 3D model
|
||||
- author* - The model author
|
||||
- license* - The model license
|
||||
- animation_speed* - How many frames per second the animations play. Default: 30
|
||||
- textures - A list of all textures that the model uses. It is possible to leave this field blank, but the model will then not have an optimal appearance.
|
||||
- animations* - A list of animations that the model will use on certain actions.
|
||||
- collisionbox* - A list of 2 XYZ coordinates (6 points) in meters relative from the origin of the model.
|
||||
- stepheight* - How fast this model moves on the ground. Default: 0.6
|
||||
- eye_height* - How far up the Y-axis in meters the camera will be, in 1st-person view. Default: 1.47
|
||||
- playername* - Player assignment for private model. Set false for models not usable by all players (like NPC-models), true or nothing for all player models
|
||||
- in_inventory_list* - If set to false the model is not visible in inventory models selection but can be still applied to the player
|
||||
- _sort_id - The model lists are sorted by this field for output (internal key)
|
||||
|
||||
## model:get_meta_string(key)
|
||||
Same as get_meta() but does return "" instead of nil if the meta key does not exists
|
||||
|
||||
## model:is_applicable_for_player(playername)
|
||||
Returns whether this model is applicable for player "playername" or not, like private models
|
|
@ -0,0 +1,22 @@
|
|||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Pre-release] - Starting from 2019-12-28
|
||||
* Initial working version based on code from the [skinsdb](https://github.com/minetest-mods/skinsdb) mod. This includes the code and directory structure. All previous code is gone with this change.
|
||||
* License changed to GPLv3 from LGPLv3.
|
||||
* Out-of-the-box support for the inventory mods *sfinv* and *unified_inventory*, with the former already shipping with Minetest.
|
||||
* Other mods that alter the player model may come into conflict with *chchar*, and should be disabled before using this mod.
|
||||
* Assigning models to specific players is currently untested. All models loaded are assumed to be public for all players.
|
||||
* Translation support with *intllib* included, but there are no translations yet.
|
||||
* Additional chat command `/chchar reset` allows to quickly go back to the default model.
|
||||
|
||||
### Changes from *skinsdb*
|
||||
* Drop support for Minetest 0.4 versions.
|
||||
* Drop optional support for the *3d_armor* and *clothing* mods. This support can be reintroduced at a later time.
|
||||
* Disable the online downloader, as there are currently no places to download pre-made 3D models for use with this mod. If such a place appears, it can be re-enabled.
|
||||
* Rename some files to be more relevant for 3D models and not for skins.
|
||||
* Alter the meta file format to include more fields, including those that can changed with *player_api*.
|
||||
* Add more information to be displayed in formspecs, and make images and model buttons square sizes.
|
67
README.md
|
@ -1,45 +1,34 @@
|
|||
# Change Player Model, a mod for Minetest
|
||||
# Change Player Model (chchar), a mod for Minetest
|
||||
Allows players to change the default character model for another one, optionally with different animations. While using this mod, skins for the default player model cannot be used unless it is switched back to. Basically a frontend to the built-in player_api mod shipping with Minetest Game.
|
||||
Right now, this mod is in the planning and drafting stage. No code has been implemented yet.
|
||||
Minetest 5.0.0 or later is required in order to use this mod. No backwards compatibility with older Minetest versions 0.4.16 and 0.4.17 is currently planned.
|
||||
As a side bonus, it is possible to create models with collision boxes that are small enough to fit in one node wide tunnels or crevices, which the default player model cannot do.
|
||||
|
||||
## Directory structure of this mod
|
||||
Note that the media formats represented here can be replaced by any format supported by the Irrlicht engine.
|
||||
## Installing models
|
||||
|
||||
chchar
|
||||
```
|
||||
|-- init.lua (required) - Runs when the game loads.
|
||||
|-- mod.conf (recommended) - Contains description and dependencies.
|
||||
|-- models
|
||||
| |-- <model name> - code name of a model.
|
||||
| |-- model.ini - Information about this model, including name, costumes available, where to play animations, etc.
|
||||
| `-- costumes - Different versions of a model. Can contain alternate versions with different models, textures, and/or sounds.
|
||||
| |-- default - The default costume to load if no alternate versions of a model are available.
|
||||
| |-- textures (optional)
|
||||
| | `-- ... any textures or images for the default model.
|
||||
| |-- sounds (optional)
|
||||
| | `-- ... any sounds for the default model.
|
||||
| |-- model.b3d/model.obj - Model file that will be rendered in-game by default.
|
||||
| `-- icon.png - Image representing this model overall.
|
||||
| |-- <costume name> - Name of an optional costume. It can also be represented by a number, where the ordering is based alphabetically \(a - 0 to z - 25\).
|
||||
| |-- textures (optional)
|
||||
| | `-- ... any textures or images for the alternate version of this model.
|
||||
| |-- sounds (optional)
|
||||
| | `-- ... any sounds for the alternate version of this model.
|
||||
| |-- model.b3d/model.obj - Model file that will be rendered in-game for this particular version.
|
||||
| `-- icon.png - Image representing the alternate version of this model.
|
||||
| |-- ... any other models can be added here
|
||||
|-- textures - other images used in places such as formspecs. Not part of any model.
|
||||
`-- ... any other files or directories
|
||||
```
|
||||
## Command Help
|
||||
* /chchar \[model name\] \[costume name\] - If "default" is specified in place of <model name>, or /chchar is used by itself, it will load the default player model. If nothing is specified for <costume name>, the default costume for the specified model will load.
|
||||
What happens during errors:
|
||||
* If no models are found in the models/ directory: Prints a message in its GUI and chat commands about this.
|
||||
* If an invalid model name or costume name is specified (chat command only): Prints an error telling the user about this.
|
||||
### Manual addition
|
||||
|
||||
* /chchargui - This command brings up the GUI. Useful in case if an inventory mod is not supported.
|
||||
|
||||
* /help chchar - Pulls up usage information on how to use /chchar.
|
||||
1) Copy your 3D model files to `models`. Only four formats will be accepted by Minetest:
|
||||
* Blitz3D (.b3d)
|
||||
* Quake2 models (.md2)
|
||||
* Maya/Wavefront (.obj)
|
||||
* Microsoft DirectX (.x)
|
||||
2) Copy all the textures that each 3D model uses to `textures`.
|
||||
3) Create `meta/<name>.txt` with the minimum following fields (separated by new lines):
|
||||
* Model Name
|
||||
* Textures
|
||||
4) It is recommended to fill out the rest of these fields for optimal performance:
|
||||
* Animations
|
||||
* Collision Box
|
||||
* Eye Height
|
||||
|
||||
## License
|
||||
Change Player Model code is licensed under the GNU LGPL, version 3 or later.
|
||||
Change Player Model code is licensed under the GNU GPL, version 3 (it is not yet known whether only under this version or including later ones, so assume only version 3 for now).
|
||||
|
||||
### Credits
|
||||
|
||||
* Most of this mod's source code is based from [skinsdb](https://github.com/minetest-mods/skinsdb), commit [8054293](https://github.com/minetest-mods/skinsdb/commit/8054293c2c7617c503fc1c15005f74f063e0e71f). As this code is licensed under the GNU GPLv3, the code here is also under that license.
|
||||
|
||||
* Some icons used originate from GTK 3.13.2, latest tagged versions at https://gitlab.gnome.org/GNOME/gtk/tree/3.13.2/gtk/stock-icons. As with the rest of that software, the icons used are licensed under the GNU LGPLv2+. They are:
|
||||
* 16/gtk-convert.*
|
||||
* 24/gtk-convert.svg
|
||||
* 24/image-missing.svg
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
-- get current model
|
||||
local storage = minetest.get_mod_storage()
|
||||
|
||||
function chchar.get_player_model(player)
|
||||
if player:get_attribute("chchar:model_key") then
|
||||
storage:set_string(player:get_player_name(), player:get_attribute("chchar:model_key"))
|
||||
player:set_attribute("chchar:model_key", nil)
|
||||
end
|
||||
local model = storage:get_string(player:get_player_name())
|
||||
return chchar.get(model) or chchar.get(chchar.default)
|
||||
end
|
||||
|
||||
-- Assign model to player
|
||||
function chchar.assign_player_model(player, model)
|
||||
local model_obj
|
||||
if type(model) == "string" then
|
||||
model_obj = chchar.get(model)
|
||||
else
|
||||
model_obj = model
|
||||
end
|
||||
|
||||
if not model_obj then
|
||||
return false
|
||||
end
|
||||
|
||||
if model_obj:is_applicable_for_player(player:get_player_name()) then
|
||||
local model_key = model_obj:get_key()
|
||||
if model_key == chchar.default then
|
||||
model_key = ""
|
||||
end
|
||||
storage:set_string(player:get_player_name(), model_key)
|
||||
else
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- update visuals
|
||||
function chchar.update_player_model(player)
|
||||
chchar.get_player_model(player):apply_model_to_player(player)
|
||||
--[[
|
||||
if skins.armor_loaded then
|
||||
-- all needed is wrapped and implemented in 3d_armor mod
|
||||
armor:set_player_armor(player)
|
||||
else
|
||||
-- do updates manually without 3d_armor
|
||||
chchar.get_player_model(player):apply_model_to_player(player)
|
||||
if minetest.global_exists("sfinv") and sfinv.enabled then
|
||||
sfinv.set_player_inventory_formspec(player)
|
||||
end
|
||||
end--]]
|
||||
end
|
||||
|
||||
-- Assign and update - should be used on selection externally
|
||||
function chchar.set_player_model(player, model)
|
||||
local success = chchar.assign_player_model(player, model)
|
||||
if success then
|
||||
chchar.get_player_model(player):set_model(player)
|
||||
chchar.update_player_model(player)
|
||||
end
|
||||
return success
|
||||
end
|
|
@ -0,0 +1,103 @@
|
|||
local S = chchar.S
|
||||
|
||||
local function show_selection_formspec(player)
|
||||
local context = chchar.get_formspec_context(player)
|
||||
local name = player:get_player_name()
|
||||
local model = chchar.get_player_model(player)
|
||||
local formspec = "size[8,8]"..chchar.get_model_info_formspec(model)
|
||||
formspec = formspec..chchar.get_model_selection_formspec(player, context, 3.5)
|
||||
minetest.show_formspec(name, 'chchar_show_ui', formspec)
|
||||
end
|
||||
|
||||
|
||||
minetest.register_chatcommand("chchar", {
|
||||
params = "[set] <model key> | show [<model key>] | list | list private | list public | reset | [ui]",
|
||||
description = S("Show, list or set player's model"),
|
||||
func = function(name, param)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if not player then
|
||||
return false, S("The player").." "..name.." "..S("was not found.")
|
||||
end
|
||||
|
||||
-- parse command line
|
||||
local command, parameter
|
||||
local words = param:split(" ")
|
||||
local word = words[1]
|
||||
if word == 'set' or word == 'list' or word == 'reset' or word == 'show' or word == 'ui' then
|
||||
command = word
|
||||
parameter = words[2]
|
||||
elseif chchar.get(word) then
|
||||
command = 'set'
|
||||
parameter = word
|
||||
elseif not word then
|
||||
command = 'ui'
|
||||
else
|
||||
return false, S("Unknown command").." "..word..", "..S("see /help chchar for supported parameters.")
|
||||
end
|
||||
|
||||
if command == "set" then
|
||||
local success = chchar.set_player_model(player, parameter)
|
||||
if success then
|
||||
return true, S("Player model for").." "..name.." "..S("set to").." "..parameter
|
||||
else
|
||||
return false, S("Invalid model").." "..parameter
|
||||
end
|
||||
elseif command == "reset" then
|
||||
chchar.set_player_model(player, "character")
|
||||
return true, S("Player model reset to the default.")
|
||||
elseif command == "list" then
|
||||
local list
|
||||
if parameter == "private" then
|
||||
list = chchar.get_modellist_with_meta("playername", name)
|
||||
elseif parameter == "public" then
|
||||
list = chchar.get_modellist_for_player()
|
||||
elseif not parameter then
|
||||
list = chchar.get_modellist_for_player(name)
|
||||
else
|
||||
return false, S("Unknown parameter"), parameter
|
||||
end
|
||||
|
||||
local current_model_key = chchar.get_player_model(player):get_key()
|
||||
for _, model in ipairs(list) do
|
||||
local info = model:get_key().." - "
|
||||
..S("Name").."="..model:get_meta_string("name").." "
|
||||
..S("Author").."="..model:get_meta_string("author").." "
|
||||
..S("License").."="..model:get_meta_string("license")
|
||||
if model:get_key() == current_model_key then
|
||||
info = minetest.colorize("#00FFFF", info)
|
||||
end
|
||||
minetest.chat_send_player(name, info)
|
||||
end
|
||||
elseif command == "show" then
|
||||
local model
|
||||
if parameter then
|
||||
model = models.get(parameter)
|
||||
else
|
||||
model = chchar.get_player_model(player)
|
||||
end
|
||||
if not model then
|
||||
return false, S("Invalid model")
|
||||
end
|
||||
local formspec = "size[8,3]"..chchar.get_model_info_formspec(model)
|
||||
minetest.show_formspec(name, 'chchar_show_model', formspec)
|
||||
elseif command == "ui" then
|
||||
show_selection_formspec(player)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname ~= "chchar_show_ui" then
|
||||
return
|
||||
end
|
||||
|
||||
local context = chchar.get_formspec_context(player)
|
||||
|
||||
local action = chchar.on_model_selection_receive_fields(player, context, fields)
|
||||
if action == 'set' then
|
||||
minetest.close_formspec(player:get_player_name(), formname)
|
||||
elseif action == 'page' then
|
||||
show_selection_formspec(player)
|
||||
end
|
||||
end)
|
|
@ -1 +1,4 @@
|
|||
player_api
|
||||
intllib?
|
||||
unified_inventory?
|
||||
sfinv?
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
local S = chchar.S
|
||||
|
||||
function chchar.get_formspec_context(player)
|
||||
if player then
|
||||
local playername = player:get_player_name()
|
||||
chchar.ui_context[playername] = chchar.ui_context[playername] or {}
|
||||
return chchar.ui_context[playername]
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
-- Show model info in a graphical window
|
||||
function chchar.get_model_info_formspec(model)
|
||||
--local texture = model:get_texture()
|
||||
local icon = model:get_icon()
|
||||
local m_codename = model:get_meta_string("codename")
|
||||
local m_filename = model:get_meta_string("filename")
|
||||
local m_name = model:get_meta_string("name")
|
||||
local m_author = model:get_meta_string("author")
|
||||
local m_license = model:get_meta_string("license")
|
||||
local m_format = model:get_meta_string("format")
|
||||
-- overview page
|
||||
local formspec = "image[0,.75;2,2;"..model:get_icon().."]"
|
||||
--local formspec = "image[0,.75;1,2;"..model:get_preview().."]"
|
||||
--if texture then
|
||||
-- formspec = formspec.."label[6,.5;"..S("Raw texture")..":]"
|
||||
-- .."image[6,1;2,1;"..model:get_texture().."]"
|
||||
--end
|
||||
if m_name ~= "" then
|
||||
formspec = formspec.."label[2,.5;"..S("Name")..": "..minetest.formspec_escape(m_name).."]"
|
||||
end
|
||||
if m_codename ~= "" then
|
||||
formspec = formspec.."label[2,1;"..S("Technical Name")..": "..minetest.formspec_escape(m_codename).."]"
|
||||
end
|
||||
if m_filename ~= "" then
|
||||
formspec = formspec.."label[2,1.5;"..S("File Name")..": "..minetest.formspec_escape(m_filename).."]"
|
||||
end
|
||||
if m_author ~= "" then
|
||||
formspec = formspec.."label[2,2;"..S("Author")..": "..minetest.formspec_escape(m_author).."]"
|
||||
end
|
||||
if m_license ~= "" then
|
||||
formspec = formspec.."label[2,2.5;"..S("License")..": "..minetest.formspec_escape(m_license).."]"
|
||||
end
|
||||
if m_format ~= "" then
|
||||
formspec = formspec.."label[2,3;"..S("Format")..": "..minetest.formspec_escape(m_format:upper()).."]"
|
||||
end
|
||||
return formspec
|
||||
end
|
||||
|
||||
function chchar.get_model_selection_formspec(player, context, y_delta)
|
||||
context.chchar_list = chchar.get_modellist_for_player(player:get_player_name())
|
||||
context.total_pages = 1
|
||||
for i, model in ipairs(context.chchar_list) do
|
||||
local page = math.floor((i-1) / 16)+1
|
||||
model:set_meta("inv_page", page)
|
||||
model:set_meta("inv_page_index", (i-1)%16+1)
|
||||
context.total_pages = page
|
||||
end
|
||||
context.chchar_page = context.chchar_page or chchar.get_player_model(player):get_meta("inv_page") or 1
|
||||
context.dropdown_values = nil
|
||||
|
||||
local page = context.chchar_page
|
||||
local formspec = ""
|
||||
for i = (page-1)*16+1, page*16 do
|
||||
local model = context.chchar_list[i]
|
||||
if not model then
|
||||
break
|
||||
end
|
||||
|
||||
local index_p = model:get_meta("inv_page_index")
|
||||
local x = (index_p-1) % 8
|
||||
local y
|
||||
if index_p > 8 then
|
||||
y = y_delta + 1.9
|
||||
else
|
||||
y = y_delta
|
||||
end
|
||||
formspec = formspec.."image_button["..x..","..y..";1,1;"..
|
||||
model:get_icon()..";chchar_set$"..i..";]"..
|
||||
"tooltip[chchar_set$"..i..";"..minetest.formspec_escape(model:get_meta_string("name")).."]"
|
||||
end
|
||||
|
||||
if context.total_pages > 1 then
|
||||
local page_prev = page - 1
|
||||
local page_next = page + 1
|
||||
if page_prev < 1 then
|
||||
page_prev = context.total_pages
|
||||
end
|
||||
if page_next > context.total_pages then
|
||||
page_next = 1
|
||||
end
|
||||
local page_list = ""
|
||||
context.dropdown_values = {}
|
||||
for pg=1, context.total_pages do
|
||||
local pagename = S("Page").." "..pg.."/"..context.total_pages
|
||||
context.dropdown_values[pagename] = pg
|
||||
if pg > 1 then page_list = page_list.."," end
|
||||
page_list = page_list..pagename
|
||||
end
|
||||
formspec = formspec
|
||||
.."button[0,"..(y_delta+4.0)..";1,.5;chchar_page$"..page_prev..";<<]"
|
||||
.."dropdown[0.9,"..(y_delta+3.88)..";6.5,.5;chchar_selpg;"..page_list..";"..page.."]"
|
||||
.."button[7,"..(y_delta+4.0)..";1,.5;chchar_page$"..page_next..";>>]"
|
||||
end
|
||||
return formspec
|
||||
end
|
||||
|
||||
function chchar.on_model_selection_receive_fields(player, context, fields)
|
||||
for field, _ in pairs(fields) do
|
||||
local current = string.split(field, "$", 2)
|
||||
if current[1] == "chchar_set" then
|
||||
chchar.set_player_model(player, context.chchar_list[tonumber(current[2])])
|
||||
return 'set'
|
||||
elseif current[1] == "chchar_page" then
|
||||
context.chchar_page = tonumber(current[2])
|
||||
return 'page'
|
||||
end
|
||||
end
|
||||
if fields.chchar_selpg then
|
||||
context.chchar_page = tonumber(context.dropdown_values[fields.chchar_selpg])
|
||||
return 'page'
|
||||
end
|
||||
end
|
128
init.lua
|
@ -1,11 +1,117 @@
|
|||
--print("This file will be run at load time!")
|
||||
--minetest.register_chatcommand("chchar"
|
||||
--{
|
||||
-- params = "[<model_name>] [<costume_name>]",
|
||||
-- description = "Set the model and optionally costume name for the current player",
|
||||
-- func = function(name, param)
|
||||
-- local name = player:get_player_name()
|
||||
-- player_api.set_model(player, model_name)
|
||||
-- end,
|
||||
--}
|
||||
--)
|
||||
--[[
|
||||
Change Player Model (chchar), based on Unified Skins for Minetest
|
||||
|
||||
Copyright (c) 2012 cornernote, Dean Montgomery
|
||||
Rework 2017 by bell07
|
||||
Copyright (c) 2019, 2020 Richard Qian
|
||||
License: GPLv3
|
||||
Boilerplate to support localized strings if intllib mod is installed.
|
||||
--]]
|
||||
chchar = {}
|
||||
chchar.modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||
chchar.default = "character"
|
||||
|
||||
local S
|
||||
if minetest.get_modpath("intllib") then
|
||||
chchar.S = intllib.Getter()
|
||||
else
|
||||
chchar.S = function(s) return s end
|
||||
end
|
||||
|
||||
dofile(chchar.modpath.."/model_meta_api.lua")
|
||||
dofile(chchar.modpath.."/api.lua")
|
||||
dofile(chchar.modpath.."/model_list.lua")
|
||||
dofile(chchar.modpath.."/formspecs.lua")
|
||||
dofile(chchar.modpath.."/chatcommands.lua")
|
||||
-- Unified inventory page/integration
|
||||
if minetest.get_modpath("unified_inventory") then
|
||||
dofile(chchar.modpath.."/unified_inventory_page.lua")
|
||||
end
|
||||
|
||||
if minetest.get_modpath("sfinv") then
|
||||
dofile(chchar.modpath.."/sfinv_page.lua")
|
||||
end
|
||||
--[[
|
||||
-- ie.loadfile does not exist?
|
||||
chchar.ie = minetest.request_insecure_environment()
|
||||
chchar.http = minetest.request_http_api()
|
||||
dofile(chchar.modpath.."/model_downloader.lua")
|
||||
chchar.ie = nil
|
||||
chchar.http = nil
|
||||
|
||||
-- 3d_armor compatibility
|
||||
if minetest.global_exists("armor") then
|
||||
skins.armor_loaded = true
|
||||
armor.get_player_skin = function(self, name)
|
||||
local skin = skins.get_player_skin(minetest.get_player_by_name(name))
|
||||
return skin:get_texture()
|
||||
end
|
||||
armor.get_preview = function(self, name)
|
||||
local skin = skins.get_player_skin(minetest.get_player_by_name(name))
|
||||
return skin:get_preview()
|
||||
end
|
||||
armor.update_player_visuals = function(self, player)
|
||||
if not player then
|
||||
return
|
||||
end
|
||||
local skin = skins.get_player_skin(player)
|
||||
skin:apply_skin_to_player(player)
|
||||
armor:run_callbacks("on_update", player)
|
||||
end
|
||||
end
|
||||
|
||||
if minetest.global_exists("clothing") and clothing.player_textures then
|
||||
skins.clothing_loaded = true
|
||||
clothing:register_on_update(skins.update_player_skin)
|
||||
end--]]
|
||||
|
||||
-- Update model on join
|
||||
chchar.ui_context = {}
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
chchar.update_player_model(player)
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
chchar.ui_context[player:get_player_name()] = nil
|
||||
end)
|
||||
--[[
|
||||
if minetest.global_exists("player_api") then
|
||||
-- Minetest-5 and above compatible
|
||||
player_api.register_model("skinsdb_3d_armor_character_5.b3d", {
|
||||
animation_speed = 30,
|
||||
textures = {
|
||||
"blank.png",
|
||||
"blank.png",
|
||||
"blank.png",
|
||||
"blank.png"
|
||||
},
|
||||
animations = {
|
||||
stand = {x=0, y=79},
|
||||
lay = {x=162, y=166},
|
||||
walk = {x=168, y=187},
|
||||
mine = {x=189, y=198},
|
||||
walk_mine = {x=200, y=219},
|
||||
sit = {x=81, y=160},
|
||||
},
|
||||
})
|
||||
else
|
||||
-- Minetest-0.4 compatible
|
||||
default.player_register_model("skinsdb_3d_armor_character.b3d", {
|
||||
animation_speed = 30,
|
||||
textures = {
|
||||
"blank.png",
|
||||
"blank.png",
|
||||
"blank.png",
|
||||
"blank.png",
|
||||
},
|
||||
animations = {
|
||||
stand = {x=0, y=79},
|
||||
lay = {x=162, y=166},
|
||||
walk = {x=168, y=187},
|
||||
mine = {x=189, y=198},
|
||||
walk_mine = {x=200, y=219},
|
||||
sit = {x=81, y=160},
|
||||
},
|
||||
})
|
||||
end--]]
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# Template
|
||||
|
||||
Raw texture =
|
||||
Name =
|
||||
Author =
|
||||
Change =
|
||||
Page =
|
||||
License =
|
||||
Description =
|
||||
Show, list or set player's model =
|
||||
The player =
|
||||
was not found =
|
||||
Unknown command =
|
||||
see /help chchar for supported parameters =
|
||||
Player model for =
|
||||
set to =
|
||||
Player model reset to the default. =
|
||||
Invalid model =
|
||||
Unknown parameter =
|
||||
Unknown model =
|
|
@ -0,0 +1,30 @@
|
|||
For each model in "/models" folder a metadata file can be applied with "txt" suffix in "/meta". See character.txt for model character.b3d for reference. A '*' means an optional item.
|
||||
|
||||
The file contains:
|
||||
|
||||
- Model name
|
||||
|
||||
- Author*
|
||||
|
||||
- License*
|
||||
|
||||
- Animation Speed* - How many frames per second the animations play. Default: 30
|
||||
|
||||
- Textures - A list of all textures that the model uses. It is possible to leave this field blank, but the model will then not have an optimal appearance.
|
||||
|
||||
- Animations* - A list of animations that the model will use on certain actions. Each X and Y point are the starting and ending frames as set in the Blender timeline, respectively. Format for each item is {animation_name:start_frame,end_frame}; (without braces)
|
||||
|
||||
Standard animations:
|
||||
stand = {x = 0, y = 79}
|
||||
lay = {x = 162, y = 166}
|
||||
walk = {x = 168, y = 187}
|
||||
mine = {x = 189, y = 198}
|
||||
walk_mine = {x = 200, y = 219}
|
||||
sit = {x = 81, y = 160}
|
||||
Formatted version would be: stand:0,79;lay:162,166;walk:168,187;mine:189,198;walk_mine:200,219;sit:81,160
|
||||
|
||||
- Collision Box* - A list of 2 XYZ coordinates (6 points) in meters relative from the origin of the model. It creates a cube or prism depending on the location of the coordinates. Format is {X1,Y1,Z1,X2,Y2,Z2} Default: {-0.3,0.0,-0.3,0.3,1.7,0.3} (without braces)
|
||||
|
||||
- Stepheight* - How fast this model moves on the ground. Default: 0.6
|
||||
|
||||
- Eye height* - How far up the Y-axis in meters the camera will be, in 1st-person view. Default: 1.47
|
|
@ -0,0 +1,9 @@
|
|||
name = Sam 0
|
||||
author = Jordach
|
||||
license = CC BY-SA 3.0
|
||||
animation_speed = 30
|
||||
textures = character.png,character_eyes.png
|
||||
animations => stand:0,79;lay:162,166;walk:168,187;mine:189,198;walk_mine:200,219;sit:81,160
|
||||
collisionbox = -0.3,0.0,-0.3,0.3,1.7,0.3
|
||||
stepheight = 0.6
|
||||
eye_height = 1.47
|
1
mod.conf
|
@ -2,4 +2,5 @@ name = chchar
|
|||
author = Worldblender
|
||||
description = Allows players to change the default character model for another one, optionally with different animations.
|
||||
depends = player_api
|
||||
optional_depends = intllib,unified_inventory,sfinv
|
||||
title = Change Player Model
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
-- 3D model downloader script - rewrite currently incomplete and disabled
|
||||
|
||||
local S = chchar.S
|
||||
local _ID_ = "Lua 3D Model Downloader"
|
||||
|
||||
local internal = {}
|
||||
internal.errors = {}
|
||||
|
||||
-- Binary downloads are required
|
||||
if not core.features.httpfetch_binary_data then
|
||||
internal.errors[#internal.errors + 1] =
|
||||
"Feature 'httpfetch_binary_data' is missing. Update Minetest."
|
||||
end
|
||||
|
||||
-- Insecure environment for saving textures and meta
|
||||
local ie, http = chchar.ie, chchar.http
|
||||
if not ie or not http then
|
||||
internal.errors[#internal.errors + 1] = "Insecure environment is required. " ..
|
||||
"Please add chchar to `secure.trusted_mods` in minetest.conf"
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("skinsdb_download_skins", {
|
||||
params = "<skindb start page> <amount of pages>",
|
||||
description = S("Downloads the specified range of skins and shuts down the server"),
|
||||
privs = {server=true},
|
||||
func = function(name, param)
|
||||
if #internal.errors > 0 then
|
||||
return false, "Cannot run " .. _ID_ .. ":\n\t" ..
|
||||
table.concat(internal.errors, "\n\t")
|
||||
end
|
||||
|
||||
local parts = string.split(param, " ")
|
||||
local start = tonumber(parts[1])
|
||||
local len = tonumber(parts[2])
|
||||
if not (start and len and len > 0) then
|
||||
return false, "Invalid page number or amount of pages"
|
||||
end
|
||||
|
||||
internal.get_pages_count(internal.fetch_function, start, len)
|
||||
return true, "Started downloading..."
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
if #internal.errors > 0 then
|
||||
return -- Nonsense to load something that's not working
|
||||
end
|
||||
|
||||
-- http://minetest.fensta.bplaced.net/api/apidoku.md
|
||||
local root_url = "http://minetest.fensta.bplaced.net"
|
||||
local page_url = root_url .. "/api/v2/get.json.php?getlist&page=%i&outformat=base64" -- [1] = Page#
|
||||
local preview_url = root_url .. "/skins/1/%i.png" -- [1] = ID
|
||||
|
||||
local mod_path = skins.modpath
|
||||
local meta_path = mod_path .. "/meta/"
|
||||
local skins_path = mod_path .. "/textures/"
|
||||
|
||||
-- Fancy debug wrapper to download an URL
|
||||
local function fetch_url(url, callback)
|
||||
http.fetch({
|
||||
url = url,
|
||||
user_agent = _ID_
|
||||
}, function(result)
|
||||
if result.succeeded then
|
||||
if result.code ~= 200 then
|
||||
core.log("warning", ("%s: STATUS=%i URL=%s"):format(
|
||||
_ID_, result.code, url))
|
||||
end
|
||||
return callback(result.data)
|
||||
end
|
||||
core.log("warning", ("%s: Failed to download URL=%s"):format(
|
||||
_ID_, url))
|
||||
end)
|
||||
end
|
||||
|
||||
-- Insecure workaround since meta/ and textures/ cannot be written to
|
||||
local function unsafe_file_write(path, contents)
|
||||
local f = ie.io.open(path, "w")
|
||||
f:write(contents)
|
||||
f:close()
|
||||
end
|
||||
|
||||
-- Takes a valid skin table from the Skins Database and saves it
|
||||
local function safe_single_skin(skin)
|
||||
local meta = {
|
||||
skin.name,
|
||||
skin.author,
|
||||
skin.license
|
||||
}
|
||||
|
||||
local name = "character_" .. skin.id
|
||||
|
||||
-- core.safe_file_write does not work here
|
||||
unsafe_file_write(
|
||||
meta_path .. name .. ".txt",
|
||||
table.concat(meta, "\n")
|
||||
)
|
||||
|
||||
unsafe_file_write(
|
||||
skins_path .. name .. ".png",
|
||||
core.decode_base64(skin.img)
|
||||
)
|
||||
fetch_url(preview_url:format(skin.id), function(preview)
|
||||
unsafe_file_write(skins_path .. name .. "_preview.png", preview)
|
||||
end)
|
||||
core.log("action", ("%s: Completed skin %s"):format(_ID_, name))
|
||||
end
|
||||
|
||||
-- Get total pages since it'll just return the last page all over again
|
||||
internal.get_pages_count = function(callback, ...)
|
||||
local vars = {...}
|
||||
fetch_url(page_url:format(1) .. "&per_page=1", function(data)
|
||||
local list = core.parse_json(data)
|
||||
-- "per_page" defaults to 20 if left away (docs say something else, though)
|
||||
callback(math.ceil(list.pages / 20), unpack(vars))
|
||||
end)
|
||||
end
|
||||
|
||||
-- Function to fetch a range of pages
|
||||
internal.fetch_function = function(pages_total, start_page, len)
|
||||
start_page = math.max(start_page, 1)
|
||||
local end_page = math.min(start_page + len - 1, pages_total)
|
||||
|
||||
for page_n = start_page, end_page do
|
||||
local page_cpy = page_n
|
||||
fetch_url(page_url:format(page_n), function(data)
|
||||
core.log("action", ("%s: Page %i"):format(_ID_, page_cpy))
|
||||
|
||||
local list = core.parse_json(data)
|
||||
for i, skin in pairs(list.skins) do
|
||||
assert(skin.type == "image/png")
|
||||
assert(skin.id ~= "")
|
||||
|
||||
if skin.id ~= 1 then -- Skin 1 is bundled with skinsdb
|
||||
safe_single_skin(skin)
|
||||
end
|
||||
end
|
||||
|
||||
if page_cpy == end_page then
|
||||
local log = _ID_ .. " finished downloading all skins. " ..
|
||||
"Shutting down server to reload media cache"
|
||||
core.log("action", log)
|
||||
core.request_shutdown(log, true, 3 --[[give some time for pending requests]])
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,182 @@
|
|||
local models_dir_list = minetest.get_dir_list(chchar.modpath.."/models")
|
||||
local icons_dir_list = minetest.get_dir_list(chchar.modpath.."/textures")
|
||||
table.insert(models_dir_list, 1, "character.b3d") -- Add the default model from player_api as the first model in this list
|
||||
|
||||
-- Load only the formats that are allowed to be loaded
|
||||
local file_ext_2d = {"png", "jpg", "bmp", "tga", "pcx", "ppm", "psd", "wal", "rgb"}
|
||||
local file_ext_3d = {"x", "b3d", "md2", "obj"}
|
||||
|
||||
for _, fn in pairs(models_dir_list) do
|
||||
local name, sort_id, assignment, is_preview, playername
|
||||
local nameparts = string.gsub(fn, "[.]", "_"):split("_")
|
||||
local filetype = nameparts[#nameparts]:lower()
|
||||
|
||||
-- check allowed file extension (3D model)
|
||||
if table.indexof(file_ext_3d, filetype) > 0 then
|
||||
print("Found model file "..fn)
|
||||
-- cut filename extension
|
||||
table.remove(nameparts, #nameparts)
|
||||
|
||||
-- check if optional icon exists
|
||||
has_icon = false
|
||||
iconname = ""
|
||||
for iconfile = 1, #icons_dir_list, 1 do
|
||||
runname = icons_dir_list[iconfile]
|
||||
iconparts = string.gsub(runname, "[.]", "_"):split("_")
|
||||
if iconparts[1] == nameparts[1] and iconparts[2] == 'icon' and table.indexof(file_ext_2d, iconparts[3]) > 0 then
|
||||
print("Found icon file "..runname)
|
||||
has_icon = true
|
||||
iconname = runname
|
||||
end
|
||||
end
|
||||
|
||||
-- Build technical model name
|
||||
name = table.concat(nameparts, '_')
|
||||
|
||||
-- Get player name
|
||||
if nameparts[1] == "player" then
|
||||
playername = nameparts[2]
|
||||
table.remove(nameparts, 1)
|
||||
sort_id = 0
|
||||
else
|
||||
sort_id = 5000
|
||||
end
|
||||
|
||||
-- Get sort index
|
||||
if tonumber(nameparts[#nameparts]) then
|
||||
sort_id = sort_id + nameparts[#nameparts]
|
||||
end
|
||||
|
||||
local model_obj = chchar.get(name) or chchar.new(name)
|
||||
|
||||
model_obj:set_meta("codename", name)
|
||||
model_obj:set_meta("filename", fn)
|
||||
model_obj:set_meta("format", filetype)
|
||||
model_obj:set_icon(iconname)
|
||||
model_obj:set_meta("_sort_id", sort_id)
|
||||
if playername then
|
||||
model_obj:set_meta("assignment", "player:"..playername)
|
||||
model_obj:set_meta("playername", playername)
|
||||
end
|
||||
|
||||
-- How to parse config files from https://stackoverflow.com/q/55523750
|
||||
local file = io.open(chchar.modpath.."/meta/"..name..".txt", "r")
|
||||
if file then
|
||||
print("Found meta file for "..fn)
|
||||
for line in file:lines() do
|
||||
if line:find("name = ") ~= nil then
|
||||
local data = line:split(" = ")
|
||||
model_obj:set_meta("name", data[2])
|
||||
elseif line:find("author = ") ~= nil then
|
||||
local data = line:split(" = ")
|
||||
model_obj:set_meta("author", data[2])
|
||||
elseif line:find("license = ") ~= nil then
|
||||
local data = line:split(" = ")
|
||||
model_obj:set_meta("license", data[2])
|
||||
elseif line:find("animation_speed = ") ~= nil then
|
||||
local data = line:split(" = ")
|
||||
model_obj:set_meta("animation_speed", data[2])
|
||||
elseif line:find("stepheight = ") ~= nil then
|
||||
local data = line:split(" = ")
|
||||
model_obj:set_meta("stepheight", data[2])
|
||||
elseif line:find("eye_height = ") ~= nil then
|
||||
local data = line:split(" = ")
|
||||
model_obj:set_meta("eye_height", data[2])
|
||||
elseif line:find("collisionbox = ") ~= nil then
|
||||
local data = line:split(" = ")
|
||||
model_obj:set_meta("collisionbox", data[2]:split())
|
||||
elseif line:find("textures = ") ~= nil then
|
||||
local data = line:split(" = ")[2]:split()
|
||||
if #data < 2 then
|
||||
model_obj:set_meta("textures", {data})
|
||||
else
|
||||
model_obj:set_meta("textures", data)
|
||||
end
|
||||
elseif line:find("animations => ") ~= nil then
|
||||
local data = line:split(" => ")[2]:split(";")
|
||||
-- Inserting nested tables from https://stackoverflow.com/a/22598242
|
||||
local animtable = {}
|
||||
for item = 1, #data, 1 do
|
||||
local subdata = data[item]:split(":")
|
||||
local subnum = subdata[2]:split()
|
||||
animname = subdata[1]
|
||||
animtable[animname] = subnum
|
||||
end
|
||||
model_obj:set_meta("animations", animtable)
|
||||
end
|
||||
end
|
||||
file:close()
|
||||
else
|
||||
-- remove player / character prefix if further naming given
|
||||
if nameparts[2] and not tonumber(nameparts[2]) then
|
||||
table.remove(nameparts, 1)
|
||||
end
|
||||
model_obj:set_meta("name", table.concat(nameparts, ' '))
|
||||
end
|
||||
|
||||
-- Register model with player_api, excluding the default model
|
||||
if fn ~= "character.b3d" then
|
||||
player_api.register_model(fn, {
|
||||
animation_speed = model_obj:get_meta("animation_speed") or 30,
|
||||
textures = model_obj:get_meta("textures") or {"blank.png"},
|
||||
animations = model_obj:get_meta("animations") or {},
|
||||
collisionbox = model_obj:get_meta("collisionbox") or {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3},
|
||||
stepheight = model_obj:get_meta("stepheight") or 0.6,
|
||||
eye_height = model_obj:get_meta("eye_height") or 1.47,
|
||||
})
|
||||
print("registered model "..fn)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function models_sort(modelslist)
|
||||
table.sort(modelslist, function(a,b)
|
||||
local a_id = a:get_meta("_sort_id") or 10000
|
||||
local b_id = b:get_meta("_sort_id") or 10000
|
||||
if a_id ~= b_id then
|
||||
return a:get_meta("_sort_id") < b:get_meta("_sort_id")
|
||||
else
|
||||
return a:get_meta("name") < b:get_meta("name")
|
||||
end
|
||||
end)
|
||||
end
|
||||
--[[
|
||||
-- (obsolete) get skinlist. If assignment given ("mod:wardrobe" or "player:bell07") select skins matches the assignment. select_unassigned selects the skins without any assignment too
|
||||
function skins.get_skinlist(assignment, select_unassigned)
|
||||
minetest.log("deprecated", "skins.get_skinlist() is deprecated. Use skins.get_skinlist_for_player() instead")
|
||||
local skinslist = {}
|
||||
for _, skin in pairs(skins.meta) do
|
||||
if not assignment or
|
||||
assignment == skin:get_meta("assignment") or
|
||||
(select_unassigned and skin:get_meta("assignment") == nil) then
|
||||
table.insert(skinslist, skin)
|
||||
end
|
||||
end
|
||||
skins_sort(skinslist)
|
||||
return skinslist
|
||||
end
|
||||
--]]
|
||||
-- Get modellist for player. If no player given, public models only selected
|
||||
function chchar.get_modellist_for_player(playername)
|
||||
local modelslist = {}
|
||||
for _, model in pairs(chchar.meta) do
|
||||
if model:is_applicable_for_player(playername) and model:get_meta("in_inventory_list") ~= false then
|
||||
table.insert(modelslist, model)
|
||||
end
|
||||
end
|
||||
models_sort(modelslist)
|
||||
return modelslist
|
||||
end
|
||||
|
||||
-- Get modellist selected by metadata
|
||||
function chchar.get_modellist_with_meta(key, value)
|
||||
assert(key, "key parameter for chchar.get_modellist_with_meta() missed")
|
||||
local modelslist = {}
|
||||
for _, model in pairs(chchar.meta) do
|
||||
if model:get_meta(key) == value then
|
||||
table.insert(modelslist, model)
|
||||
end
|
||||
end
|
||||
models_sort(modelslist)
|
||||
return modelslist
|
||||
end
|
|
@ -0,0 +1,155 @@
|
|||
chchar.meta = {}
|
||||
|
||||
local model_class = {}
|
||||
model_class.__index = model_class
|
||||
chchar.model_class = model_class
|
||||
-----------------------
|
||||
-- Class methods
|
||||
-----------------------
|
||||
-- constructor
|
||||
function chchar.new(key, object)
|
||||
assert(key, 'Unique models key required, like "character_1"')
|
||||
local self = object or {}
|
||||
setmetatable(self, model_class)
|
||||
self.__index = model_class
|
||||
|
||||
self._key = key
|
||||
self._sort_id = 0
|
||||
chchar.meta[key] = self
|
||||
return self
|
||||
end
|
||||
|
||||
-- getter
|
||||
function chchar.get(key)
|
||||
return chchar.meta[key]
|
||||
end
|
||||
|
||||
-- Model methods
|
||||
-- In this implementation it is just access to attrubutes wrapped
|
||||
-- but this way allow to redefine the functionality for more complex models provider
|
||||
function model_class:get_key()
|
||||
return self._key
|
||||
end
|
||||
|
||||
function model_class:set_meta(key, value)
|
||||
self[key] = value
|
||||
end
|
||||
|
||||
function model_class:get_meta(key)
|
||||
return self[key]
|
||||
end
|
||||
|
||||
function model_class:get_meta_string(key)
|
||||
return tostring(self:get_meta(key) or "")
|
||||
end
|
||||
|
||||
function model_class:set_texture(value)
|
||||
self._texture = value
|
||||
end
|
||||
|
||||
function model_class:get_texture()
|
||||
return self._texture
|
||||
end
|
||||
|
||||
function model_class:set_icon(value)
|
||||
self._icon = value
|
||||
end
|
||||
|
||||
function model_class:get_icon()
|
||||
return self._icon or "image-missing.png"
|
||||
end
|
||||
|
||||
function model_class:apply_model_to_player(player)
|
||||
--[[
|
||||
local function concat_texture(base, ext)
|
||||
if base == "blank.png" then
|
||||
return ext
|
||||
elseif ext == "blank.png" then
|
||||
return base
|
||||
else
|
||||
return base .. "^" .. ext
|
||||
end
|
||||
end
|
||||
--]]
|
||||
local playername = player:get_player_name()
|
||||
player_api.set_model(player, self:get_meta("filename"))
|
||||
--[[
|
||||
local ver = self:get_meta("format") or "1.0"
|
||||
if minetest.global_exists("player_api") then
|
||||
-- Minetest-5 compatible
|
||||
player_api.set_model(player, "skinsdb_3d_armor_character_5.b3d")
|
||||
else
|
||||
-- Minetest-0.4 compatible
|
||||
default.player_set_model(player, "skinsdb_3d_armor_character.b3d")
|
||||
end
|
||||
local v10_texture = "blank.png"
|
||||
local v18_texture = "blank.png"
|
||||
local armor_texture = "blank.png"
|
||||
local wielditem_texture = "blank.png"
|
||||
|
||||
if ver == "1.8" then
|
||||
v18_texture = self:get_texture()
|
||||
else
|
||||
v10_texture = self:get_texture()
|
||||
end
|
||||
--[[
|
||||
-- Support for clothing
|
||||
if skins.clothing_loaded and clothing.player_textures[playername] then
|
||||
local cape = clothing.player_textures[playername].cape
|
||||
local layers = {}
|
||||
for k, v in pairs(clothing.player_textures[playername]) do
|
||||
if k ~= "skin" and k ~= "cape" then
|
||||
table.insert(layers, v)
|
||||
end
|
||||
end
|
||||
local overlay = table.concat(layers, "^")
|
||||
v10_texture = concat_texture(v10_texture, cape)
|
||||
v18_texture = concat_texture(v18_texture, overlay)
|
||||
end
|
||||
|
||||
-- Support for armor
|
||||
if skins.armor_loaded then
|
||||
local armor_textures = armor.textures[playername]
|
||||
if armor_textures then
|
||||
armor_texture = concat_texture(armor_texture, armor_textures.armor)
|
||||
wielditem_texture = concat_texture(wielditem_texture, armor_textures.wielditem)
|
||||
end
|
||||
end
|
||||
|
||||
if minetest.global_exists("player_api") then
|
||||
-- Minetest-5 compatible
|
||||
player_api.set_textures(player, {
|
||||
v10_texture,
|
||||
v18_texture,
|
||||
armor_texture,
|
||||
wielditem_texture,
|
||||
})
|
||||
else
|
||||
-- Minetest-0.4 compatible
|
||||
default.player_set_textures(player, {
|
||||
v10_texture,
|
||||
v18_texture,
|
||||
armor_texture,
|
||||
wielditem_texture,
|
||||
})
|
||||
end
|
||||
--]]
|
||||
player:set_properties({
|
||||
visual_size = {
|
||||
x = self:get_meta("visual_size_x") or 1,
|
||||
y = self:get_meta("visual_size_y") or 1
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
function model_class:set_model(player)
|
||||
-- The set_skin is used on skins selection
|
||||
-- This means the method could be redefined to start an furmslec
|
||||
-- See character_creator for example
|
||||
end
|
||||
|
||||
function model_class:is_applicable_for_player(playername)
|
||||
local assigned_player = self:get_meta("playername")
|
||||
return assigned_player == nil or assigned_player == true or
|
||||
(assigned_player:lower() == playername:lower())
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
Copy your 3D model files here. Only four formats will be accepted by Minetest:
|
||||
* Blitz3D (.b3d)
|
||||
* Quake2 models (.md2)
|
||||
* Maya/Wavefront (.obj)
|
||||
* Microsoft DirectX (.x)
|
|
@ -0,0 +1,21 @@
|
|||
local S = chchar.S
|
||||
|
||||
-- generate the current formspec
|
||||
local function get_formspec(player, context)
|
||||
local model = chchar.get_player_model(player)
|
||||
local formspec = chchar.get_model_info_formspec(model)
|
||||
formspec = formspec..chchar.get_model_selection_formspec(player, context, 4)
|
||||
return formspec
|
||||
end
|
||||
|
||||
sfinv.register_page("chchar:overview", {
|
||||
title = "Models",
|
||||
get = function(self, player, context)
|
||||
-- collect models data
|
||||
return sfinv.make_formspec(player, context, get_formspec(player, context))
|
||||
end,
|
||||
on_player_receive_fields = function(self, player, context, fields)
|
||||
chchar.on_model_selection_receive_fields(player, context, fields)
|
||||
sfinv.set_player_inventory_formspec(player)
|
||||
end
|
||||
})
|
After Width: | Height: | Size: 604 B |
After Width: | Height: | Size: 3.5 KiB |
|
@ -0,0 +1,133 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://web.resource.org/cc/"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="16px"
|
||||
height="16px"
|
||||
id="svg4908"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.44+devel"
|
||||
sodipodi:docbase="/home/andreas/project/gtk/16"
|
||||
sodipodi:docname="gtk-convert.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
inkscape:export-filename="/home/andreas/project/gtk-convert.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90"
|
||||
sodipodi:modified="true">
|
||||
<defs
|
||||
id="defs4910">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient5899">
|
||||
<stop
|
||||
style="stop-color:#f66d6d;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop5901" />
|
||||
<stop
|
||||
style="stop-color:#d00e0e;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop5903" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5899"
|
||||
id="radialGradient5905"
|
||||
cx="10.628995"
|
||||
cy="11.018212"
|
||||
fx="10.628995"
|
||||
fy="11.018212"
|
||||
r="2.8514368"
|
||||
gradientTransform="matrix(0.9387545,-1.1022182e-2,1.2105798e-2,0.917556,0.6110392,1.1229117)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="22.197802"
|
||||
inkscape:cx="12.98387"
|
||||
inkscape:cy="9.5174568"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:window-width="1674"
|
||||
inkscape:window-height="969"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26" />
|
||||
<metadata
|
||||
id="metadata4913">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:1;fill:url(#radialGradient5905);fill-opacity:1.0;stroke:#790000;stroke-width:0.92762601;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="path4926"
|
||||
sodipodi:cx="11.352475"
|
||||
sodipodi:cy="11.630198"
|
||||
sodipodi:rx="2.3876238"
|
||||
sodipodi:ry="2.2524753"
|
||||
d="M 13.740099 11.630198 A 2.3876238 2.2524753 0 1 1 8.9648514,11.630198 A 2.3876238 2.2524753 0 1 1 13.740099 11.630198 z"
|
||||
transform="matrix(1.0470662,0,0,1.1098901,0.1132071,-0.9082411)" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:0.58695652;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="path4928"
|
||||
sodipodi:cx="11.712872"
|
||||
sodipodi:cy="12.125743"
|
||||
sodipodi:rx="1.1262376"
|
||||
sodipodi:ry="1.1262376"
|
||||
d="M 12.839109 12.125743 A 1.1262376 1.1262376 0 1 1 10.586634,12.125743 A 1.1262376 1.1262376 0 1 1 12.839109 12.125743 z"
|
||||
transform="matrix(0.8879121,0,0,0.8879121,0.5999996,0.2334061)" />
|
||||
<path
|
||||
style="fill:#3465a4;fill-rule:evenodd;stroke:#132c52;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:1"
|
||||
d="M 0.5,5.8999999 L 3.5387914,0.47747524 L 8.5000002,5.5391088 L 4.2213588,9.4999998 L 0.5,5.8999999 z "
|
||||
id="path5907"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="opacity:0.54891304;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 3,2.4641089 L 3.680198,1.3266089 L 4,1.6757426 L 4,8.6170792 L 3,7.6034653 L 3,2.4641089 z "
|
||||
id="rect5909"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:0.43478261"
|
||||
d="M 1.1351485,5.8486987 L 3,2.5076731 L 3,7.5495049 L 1.1351485,5.8486987 z "
|
||||
id="path5911"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:0.32608696"
|
||||
d="M 1.729703,5.7522277 L 3.7747525,2.090099 L 7.0675743,5.5034652 L 4.2121287,8.1351485 L 1.729703,5.7522277 z "
|
||||
id="path5914"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 9.044769,2.4999999 L 11.480646,2.4999999 C 12.01156,2.4192207 12.478763,3.0711021 12.5,3.4999999 L 12.5,5.4999998"
|
||||
id="path5916"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 10,5 L 15,5 L 12.487086,8 L 10,5 z "
|
||||
id="path5918" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.6 KiB |
|
@ -0,0 +1,172 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://web.resource.org/cc/"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24"
|
||||
height="24"
|
||||
id="svg4908"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.44+devel"
|
||||
version="1.0"
|
||||
sodipodi:docbase="/home/andreas/project/gtk/24"
|
||||
sodipodi:docname="gtk-convert.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
inkscape:export-filename="/home/andreas/project/gtk/24/gtk-convert.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90"
|
||||
sodipodi:modified="true">
|
||||
<defs
|
||||
id="defs4910">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient5948">
|
||||
<stop
|
||||
style="stop-color:#eeeeec;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5950" />
|
||||
<stop
|
||||
style="stop-color:#eeeeec;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop5952" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient5904">
|
||||
<stop
|
||||
style="stop-color:#f20000;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop5906" />
|
||||
<stop
|
||||
style="stop-color:#b00000;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop5908" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5904"
|
||||
id="radialGradient5910"
|
||||
cx="16.60972"
|
||||
cy="17.044081"
|
||||
fx="16.60972"
|
||||
fy="17.044081"
|
||||
r="4.9853533"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.8665774,0.3622763,-0.3153673,0.754369,7.4985738,-1.772133)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5948"
|
||||
id="linearGradient5954"
|
||||
x1="16.231808"
|
||||
y1="16.568331"
|
||||
x2="17.704367"
|
||||
y2="18.141708"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="16"
|
||||
inkscape:cx="18.711881"
|
||||
inkscape:cy="10.748197"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
width="24px"
|
||||
height="24px"
|
||||
inkscape:window-width="1674"
|
||||
inkscape:window-height="969"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26" />
|
||||
<metadata
|
||||
id="metadata4913">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#15315b;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 1.4999999,11.58685 L 6.0833329,15.516192 L 12.499999,11.542198 L 5.6178032,4.4425904 L 1.4999999,11.58685 z "
|
||||
id="path4923" />
|
||||
<path
|
||||
style="fill:#204a87;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 6,15 L 6,5.531158 L 12,11.330226 L 6,15 z "
|
||||
id="path5894"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:#729fcf;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 5.0306677,14 L 5.0306677,6.473504 L 2,11.424191 L 5.0306677,14 z "
|
||||
id="path5896"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:#b6cee7;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 6,5.6160448 L 5.6791892,5.2784591 L 5.0225843,6.4560847 L 5,13.995216 L 5.9812162,14.884752 L 6,5.6160448 z "
|
||||
id="path5898"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:0.23369565"
|
||||
d="M 2.6344456,11.290236 L 6.1116291,14.334887 L 11.223258,11.245584 L 5.8437193,6.0669774 L 2.6344456,11.290236 z "
|
||||
id="path5900"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:1;fill:url(#radialGradient5910);fill-opacity:1;stroke:#8d0000;stroke-width:0.90642792;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="path5902"
|
||||
sodipodi:cx="17.525761"
|
||||
sodipodi:cy="17.905054"
|
||||
sodipodi:rx="4.5321393"
|
||||
sodipodi:ry="4.5321393"
|
||||
d="M 22.0579 17.905054 A 4.5321393 4.5321393 0 1 1 12.993621,17.905054 A 4.5321393 4.5321393 0 1 1 22.0579 17.905054 z"
|
||||
transform="matrix(1.1032317,0,0,1.1032318,-1.8349745,-1.2534248)" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:0.20652173;fill:url(#linearGradient5954);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="path5912"
|
||||
sodipodi:cx="16.699705"
|
||||
sodipodi:cy="16.855742"
|
||||
sodipodi:rx="1.6074584"
|
||||
sodipodi:ry="1.6074584"
|
||||
d="M 18.307163 16.855742 A 1.6074584 1.6074584 0 1 1 15.092247,16.855742 A 1.6074584 1.6074584 0 1 1 18.307163 16.855742 z"
|
||||
transform="matrix(1.5552502,0,0,1.5552506,-9.4722197,-9.7149015)" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:0.30434783;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.96299076;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="path5922"
|
||||
sodipodi:cx="17.870581"
|
||||
sodipodi:cy="18.285204"
|
||||
sodipodi:rx="3.8519626"
|
||||
sodipodi:ry="3.8519626"
|
||||
d="M 21.722543 18.285204 A 3.8519626 3.8519626 0 1 1 14.018618,18.285204 A 3.8519626 3.8519626 0 1 1 21.722543 18.285204 z"
|
||||
transform="matrix(1.0384317,0,0,1.0384316,-1.0573766,-0.4879334)" />
|
||||
<path
|
||||
style="fill:#edd400;fill-rule:evenodd;stroke:#a48600;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 15.508502,6.4991071 L 13,6.4991071 L 17.5625,11.499107 L 22,6.4991071 L 19.489631,6.4991071 C 19.489631,6.4991071 19.993728,0.52600781 15,0.4757553 C 9.9620777,0.42550279 8.5303301,3.3812816 8.5303301,3.3812816 C 8.5303301,3.3812816 8.3091456,4.5552427 8.7950679,5.0883883 C 9.7644658,4.2049764 10.167099,3.4891404 12.96875,3.4572916 C 15.739151,3.4254428 15.508502,6.4991071 15.508502,6.4991071 z "
|
||||
id="path5966"
|
||||
sodipodi:nodetypes="ccccczcczc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:0.65217391"
|
||||
d="M 15.25,7.5 L 17.5,10 L 19.75,7.5 L 18.5,7.5 C 18.510417,3.8020833 17.895833,1.5729167 15.03125,1.53125 C 13.03125,1.5416666 11.5625,1.8645833 10.3125,2.8749999 C 14.177083,1.5520833 16.885417,3.0729166 16.5,7.5 L 15.25,7.5 z "
|
||||
id="path5968"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 7.5 KiB |
After Width: | Height: | Size: 2.0 KiB |
|
@ -0,0 +1,239 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://web.resource.org/cc/"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24"
|
||||
height="24"
|
||||
id="svg2"
|
||||
inkscape:label="Pozadí"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.45.1"
|
||||
version="1.0"
|
||||
sodipodi:docbase="/home/cornelius/GFX/ikony/GTK/my-stuff/24x24"
|
||||
sodipodi:docname="gtk-missing-image.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape">
|
||||
<defs
|
||||
id="defs3">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4916">
|
||||
<stop
|
||||
style="stop-color:#888a85;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop4918" />
|
||||
<stop
|
||||
style="stop-color:#5d5f5b;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop4920" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient6108">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop6110" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop6112" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient6076">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop6078" />
|
||||
<stop
|
||||
style="stop-color:#cbcbcb;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop6080" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient6056">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop6058" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop6060" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
x="-0.14148472"
|
||||
width="1.2829694"
|
||||
y="-0.70742352"
|
||||
height="2.414847"
|
||||
id="filter6052">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="1.1790392"
|
||||
id="feGaussianBlur6054" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6056"
|
||||
id="linearGradient6062"
|
||||
x1="9.7099533"
|
||||
y1="14.716651"
|
||||
x2="24.285765"
|
||||
y2="-5.9535513"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.117647,0,0,1.1,-1.4117645,-1.15)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6076"
|
||||
id="linearGradient6082"
|
||||
x1="17.68836"
|
||||
y1="4.5279388"
|
||||
x2="18.561535"
|
||||
y2="3.7493312"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.3472897,0,0,1.3356241,-6.7302791,-1.5076969)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6108"
|
||||
id="linearGradient6114"
|
||||
x1="16.552481"
|
||||
y1="2.9626985"
|
||||
x2="16.552481"
|
||||
y2="7.8355665"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.9803921,0,0,1.1,1.2941182,-0.8000001)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4916"
|
||||
id="linearGradient4922"
|
||||
x1="18.963779"
|
||||
y1="6.504528"
|
||||
x2="14.04664"
|
||||
y2="3.6734154"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="20.182764"
|
||||
inkscape:cx="19.575284"
|
||||
inkscape:cy="14.566885"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
width="24px"
|
||||
height="24px"
|
||||
inkscape:showpageshadow="false"
|
||||
showgrid="true"
|
||||
inkscape:window-width="967"
|
||||
inkscape:window-height="792"
|
||||
inkscape:window-x="256"
|
||||
inkscape:window-y="29" />
|
||||
<metadata
|
||||
id="metadata6">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>gtk Missing image</dc:title>
|
||||
<dc:date>29.3.2007</dc:date>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Josef Vybíral</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:subject>
|
||||
<rdf:Bag>
|
||||
<rdf:li>missing image</rdf:li>
|
||||
</rdf:Bag>
|
||||
</dc:subject>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/LGPL/2.1/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/LGPL/2.1/">
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/Notice" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/ShareAlike" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/SourceCode" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Vrstva 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
ry="1.6350584"
|
||||
rx="1.6350582"
|
||||
y="19"
|
||||
x="2.0000019"
|
||||
height="4"
|
||||
width="19.999998"
|
||||
id="rect5870"
|
||||
style="opacity:0.12663755;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.9999997px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;filter:url(#filter6052)" />
|
||||
<path
|
||||
style="color:#000000;fill:url(#linearGradient6062);fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:0.9999997px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
d="M 4.3274179,0.49999959 L 15.796562,0.49999959 L 21.078809,5.8278139 C 21.360792,6.1872781 21.511555,6.5467422 21.5,6.9062064 L 21.5,20.701435 C 21.5,21.69784 20.684972,22.5 19.672582,22.5 L 4.3274179,22.5 C 3.3150284,22.5 2.4999999,21.69784 2.4999999,20.701435 L 2.4999999,2.2985637 C 2.4999999,1.3021592 3.3150284,0.49999959 4.3274179,0.49999959 z "
|
||||
id="rect4899"
|
||||
sodipodi:nodetypes="cccccccccc" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccccccc"
|
||||
id="path6084"
|
||||
d="M 5.3489939,1.4999999 L 15.221123,1.4999999 L 20.5,7.2473524 L 20.462646,19.683268 C 20.462646,20.689737 19.637994,21.499999 18.613652,21.499999 L 5.3489939,21.499999 C 4.3246511,21.499999 3.4999997,20.689737 3.4999997,19.683268 L 3.4999997,3.3167312 C 3.4999997,2.310262 4.3246511,1.4999999 5.3489939,1.4999999 z "
|
||||
style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.99999964px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
<path
|
||||
style="opacity:1;color:#000000;fill:#d3d7cf;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999982px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
d="M 6 5 L 6 6 L 6 17 L 6 18 L 18 18 L 18 17 L 18 6 L 17 6 L 17 17 L 7 17 L 7 6 L 15 6 L 15 5 L 7 5 L 6 5 z "
|
||||
id="rect5899" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccc"
|
||||
id="path6104"
|
||||
d="M 16,2.0708482 L 20.981955,6.91941 L 21,8 L 16.036229,7.9999999 L 16,2.0708482 z "
|
||||
style="fill:url(#linearGradient6114);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:url(#linearGradient6082);fill-opacity:1.0;fill-rule:evenodd;stroke:url(#linearGradient4922);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 21.004529,6.5000002 L 15.536602,6.5 L 15.500001,0.84683065"
|
||||
id="path6074"
|
||||
sodipodi:nodetypes="ccc" />
|
||||
<g
|
||||
id="g5906"
|
||||
transform="translate(-0.5,-0.5)">
|
||||
<path
|
||||
id="path5902"
|
||||
d="M 10,9 L 15,14"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#ef2929;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#ef2929;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 15,9 L 10,14"
|
||||
id="path5904" />
|
||||
</g>
|
||||
<path
|
||||
id="path5910"
|
||||
d="M 7,6 L 7,7 L 7,17 L 8,17 L 8,7 L 15,7 L 15,6 L 8,6 L 7,6 z "
|
||||
style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999982px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
sodipodi:nodetypes="ccccccccc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 9.8 KiB |
After Width: | Height: | Size: 5.3 KiB |
|
@ -0,0 +1,48 @@
|
|||
local S = chchar.S
|
||||
|
||||
unified_inventory.register_page("chchar", {
|
||||
get_formspec = function(player)
|
||||
local model = chchar.get_player_model(player)
|
||||
local formspec = "background[0.06,0.99;7.92,7.52;ui_misc_form.png]"..chchar.get_model_info_formspec(model)..
|
||||
"button[.75,3.7;6.5,.5;chchar_page;"..S("Change").."]"
|
||||
return {formspec=formspec}
|
||||
end,
|
||||
})
|
||||
|
||||
unified_inventory.register_button("chchar", {
|
||||
type = "image",
|
||||
image = "chchar_button.png",
|
||||
})
|
||||
|
||||
local function get_formspec(player)
|
||||
local context = chchar.get_formspec_context(player)
|
||||
local formspec = "background[0.06,0.99;7.92,7.52;ui_misc_form.png]"..
|
||||
chchar.get_model_selection_formspec(player, context, -0.2)
|
||||
return formspec
|
||||
end
|
||||
|
||||
unified_inventory.register_page("chchar_page", {
|
||||
get_formspec = function(player)
|
||||
return {formspec=get_formspec(player)}
|
||||
end
|
||||
})
|
||||
|
||||
-- click button handlers
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if fields.chchar then
|
||||
unified_inventory.set_inventory_formspec(player, "craft")
|
||||
return
|
||||
end
|
||||
|
||||
if formname ~= "" then
|
||||
return
|
||||
end
|
||||
|
||||
local context = chchar.get_formspec_context(player)
|
||||
local action = chchar.on_model_selection_receive_fields(player, context, fields)
|
||||
if action == 'set' then
|
||||
unified_inventory.set_inventory_formspec(player, "chchar")
|
||||
elseif action == 'page' then
|
||||
unified_inventory.set_inventory_formspec(player, "chchar_page")
|
||||
end
|
||||
end)
|