369 lines
12 KiB
Lua
369 lines
12 KiB
Lua
morph_api = {
|
|
morphs = {},-- table containing morph data
|
|
-- the below ones are player data. key is username and value is data
|
|
player_scale = {},-- table containing scale data for each player
|
|
player = {},-- table containing current morph of player
|
|
player_parent = {},-- table containing current morph parent of player
|
|
player_properties = {},-- table containing player properties after morphed when not scaled
|
|
--smallest_box = {-0.1, 0.0, -0.1, 0.1, 0.2, 0.1}
|
|
}
|
|
local pads = {}
|
|
|
|
local plapi = minetest.get_modpath("player_api")
|
|
|
|
--[[
|
|
example of morph definition:
|
|
{
|
|
-- model defined in player_api
|
|
player_model = "example.b3d",
|
|
-- model property definition used in set_properties()
|
|
properties = {
|
|
|
|
}
|
|
-- one of player_model or properties MUST be set. both may be set
|
|
-- properties will override anything set by player_api
|
|
|
|
-- optional physics override applied upon morphing
|
|
physics = [override_table],
|
|
|
|
-- optional parent morph, properties from the parent are applied to the child
|
|
-- morph child properties override parent properties
|
|
parent = "Other morph name",
|
|
-- whatever you do, do NOT have recursive parents. i dont know what it will do
|
|
-- probably crash. so avoid that.
|
|
|
|
-- optional. privlage required for a player to use a morph pad with this
|
|
-- morph. nil means anyone can use it.
|
|
use_priv = "VIP_morphs"
|
|
-- this can be single priv as a string or priv table
|
|
|
|
-- optional message shown to user when they don't have use_priv if set.
|
|
-- default is "You are not allowed to morph into this."
|
|
use_deny_msg = "you must be VIP user to use this"
|
|
|
|
-- optional. privlage required for a player to see the morph in the morph
|
|
-- pad menu and set the morph pad to this morph. nil means anyone can set it.
|
|
set_priv = "admin_morphs"
|
|
-- this can be single priv as a string or priv table
|
|
|
|
-- optional custom morph data may be applied for other mods to use.
|
|
-- can be any key as long as it does not conflict with the above ones.
|
|
_custom_data = [whatever you want],
|
|
}
|
|
]]
|
|
|
|
function morph_api.register_morph(name,data)
|
|
morph_api.morphs[name] = data
|
|
--print(dump(morph_api.morphs))
|
|
end
|
|
|
|
function morph_api.morph(obj,name)
|
|
if morph_api.morphs[name] == nil then
|
|
return false
|
|
end
|
|
local player_name = obj:get_player_name()
|
|
local parent = false -- this is gonna be the topmost parent
|
|
local morph_data = morph_api.morphs[name]
|
|
if morph_data.parent then
|
|
morph_api.morph(obj,morph_data.parent)
|
|
else
|
|
parent = name
|
|
end
|
|
if player_name ~= "" and parent then
|
|
morph_api.player_parent[player_name] = parent
|
|
end
|
|
if player_name ~= "" then
|
|
morph_api.player[player_name] = name
|
|
end
|
|
if morph_data.player_model and plapi then
|
|
if player_name == "" then
|
|
-- workaround maybe to set non player to a player_api model
|
|
obj:set_properties({mesh = morph_data.player_model, visual = "mesh"})
|
|
obj:set_properties(player_api.registered_models[morph_data.player_model])
|
|
else
|
|
--this is a work around because player_api doesnt seem to set these 2 values when setting the model.
|
|
local workaround = {}
|
|
if player_api.registered_models[morph_data.player_model].collisionbox then
|
|
workaround.collisionbox = player_api.registered_models[morph_data.player_model].collisionbox
|
|
end
|
|
if player_api.registered_models[morph_data.player_model].eye_height then
|
|
workaround.eye_height = player_api.registered_models[morph_data.player_model].eye_height
|
|
end
|
|
player_api.set_model(obj,"Default")
|
|
if workaround ~= {} then
|
|
obj:set_properties(workaround)
|
|
end
|
|
player_api.set_model(obj,morph_data.player_model)
|
|
end
|
|
end
|
|
if morph_data.properties then
|
|
local props = morph_data.properties
|
|
obj:set_properties(props)
|
|
end
|
|
if morph_data.physics then
|
|
local props = morph_data.physics
|
|
obj:set_physics_override(props)
|
|
end
|
|
morph_api.player_properties[player_name] = obj:get_properties()
|
|
morph_api.scale(obj,1)
|
|
end
|
|
|
|
function morph_api.scale(obj,amount)
|
|
--obj is ObjectRef, anount is float
|
|
-- for now will only work on players
|
|
if obj == nil then return end
|
|
if not obj:is_player() or amount < 0.09 then
|
|
return
|
|
end
|
|
local name = obj:get_player_name()
|
|
if not morph_api.player_properties[name] then
|
|
return
|
|
end
|
|
if morph_api.player_scale[name] == amount then
|
|
return
|
|
end
|
|
local f_props = morph_api.player_properties[name]
|
|
morph_api.player_scale[name] = amount
|
|
local properties = obj:get_properties()
|
|
properties.eye_height = math.max(f_props.eye_height*amount,0.1)
|
|
properties.stepheight = math.max(f_props.stepheight*amount,0.1)
|
|
properties.visual_size = f_props.visual_size*amount
|
|
for index, value in ipairs(properties.collisionbox) do
|
|
properties.collisionbox[index] = f_props.collisionbox[index]*amount
|
|
properties.selectionbox[index] = f_props.collisionbox[index]*amount
|
|
end
|
|
--set
|
|
obj:set_properties(properties)
|
|
end
|
|
|
|
function morph_api.user_allowed(username,morph)
|
|
-- returns "" if user IS allowed, returns deny reason if user is denied
|
|
if morph == "" or morph == nil then
|
|
return "Error: No morph."
|
|
end
|
|
if morph_api.morphs[morph] == nil then
|
|
return "Error: Morph does not exist."
|
|
end
|
|
if morph_api.morphs[morph].use_priv == nil then
|
|
return ""
|
|
end
|
|
local priv = morph_api.morphs[morph].use_priv
|
|
local allow, missing = minetest.check_player_privs(username, priv)
|
|
if allow then
|
|
return ""
|
|
else
|
|
return morph_api.morphs[morph].use_deny_msg or "You are not allowed to morph into this."
|
|
end
|
|
end
|
|
|
|
function morph_api.place_allowed(username,morph)
|
|
-- returns true if user is allowed to place morph. false if denied
|
|
if not morph_api.morphs[morph] then return false end
|
|
if morph_api.morphs[morph].set_priv == nil then
|
|
return true
|
|
end
|
|
local priv = morph_api.morphs[morph].set_priv
|
|
local allow, missing = minetest.check_player_privs(username, priv)
|
|
if allow then
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
local function remove_preview(pos)
|
|
local ents = minetest.get_objects_inside_radius(pos,0.1)
|
|
for index, value in ipairs(ents) do
|
|
if value:get_luaentity()._morph then
|
|
value:remove()
|
|
end
|
|
end
|
|
end
|
|
|
|
local function morph_pad_clicked(pos,clicker)
|
|
-- this is run when player clicks a morph pad or morph preview.
|
|
-- checks the morph permission data and set morph if allowed.
|
|
if minetest.get_node(pos).name ~= "morph_api:morph_pad" then
|
|
return
|
|
end
|
|
local meta = minetest.get_meta(pos)
|
|
local name = clicker:get_player_name()
|
|
local morph = meta:get_string("morph")
|
|
local denymsg = morph_api.user_allowed(name,morph)
|
|
if denymsg == "" then
|
|
morph_api.morph(clicker,morph)
|
|
else
|
|
minetest.chat_send_player(name,denymsg)
|
|
if denymsg == "Error: Morph does not exist." then
|
|
remove_preview(pos)
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
minetest.register_privilege("morph_admin", {
|
|
description = "allowed to break all morph pads",
|
|
give_to_singleplayer = true,
|
|
give_to_admin = true,
|
|
})
|
|
|
|
minetest.register_chatcommand("morph_dump", {
|
|
description = "dumps morph_api data to chat and console",
|
|
privs = {morph_admin=true},
|
|
func = function (name,_)
|
|
print(dump(morph_api))
|
|
minetest.chat_send_player(name,dump(morph_api))
|
|
end
|
|
})
|
|
|
|
local function entity_interacted(self, clicker)
|
|
local pos = self.object:get_pos()
|
|
if clicker == nil or not clicker:is_player() then
|
|
return
|
|
end
|
|
if minetest.get_node(pos).name == "morph_api:morph_pad" then
|
|
morph_pad_clicked(pos,clicker)
|
|
else
|
|
self.object:remove()
|
|
end
|
|
end
|
|
|
|
minetest.register_entity("morph_api:preview", {
|
|
_morph = true, --used for checking if entity is morph
|
|
on_activate = function (self, staticdata, dtime_s)
|
|
self.object:set_armor_groups({fleshy=1, immortal=1})
|
|
local pos = self.object:get_pos()
|
|
local meta = minetest.get_meta(pos)
|
|
if meta:get_string("morph") ~= "" then
|
|
local morph_name = meta:get_string("morph")
|
|
morph_api.morph(self.object,morph_name)
|
|
self.object:set_properties({infotext = morph_name..
|
|
" morph\nRight click or double tap to transform."})
|
|
else
|
|
self.object:remove()
|
|
end
|
|
self.object:set_yaw(meta:get_float("rotate"))
|
|
end,
|
|
on_rightclick = entity_interacted,
|
|
on_punch = entity_interacted,
|
|
})
|
|
|
|
local function pad_placed(pos, placer, itemstack, pointed_thing)
|
|
local name = placer:get_player_name()
|
|
local meta = minetest.get_meta(pos)
|
|
meta:set_string("owner",name)
|
|
meta:set_string("infotext",name.."'s morph")
|
|
local dropdown = ""
|
|
pads[name] = pos
|
|
-- this is to sort them in the menu
|
|
local tkeys = {}
|
|
for k in pairs(morph_api.morphs) do table.insert(tkeys, k) end
|
|
table.sort(tkeys)
|
|
--
|
|
for _, key in ipairs(tkeys) do
|
|
print(key)
|
|
-- only show morph in menu if user is allowed to place it
|
|
if morph_api.place_allowed(name,key) then
|
|
dropdown = dropdown ..",".. key
|
|
end
|
|
|
|
end
|
|
dropdown = string.sub(dropdown,2)
|
|
--meta:set_string(key, value)
|
|
minetest.show_formspec(placer:get_player_name(), "set_morph",
|
|
"formspec_version[4]size[8,4]position[0.5,0.3]label[0.375,0.5;Select morph ok?]"..
|
|
"dropdown[0.5,2.0;7,0.5;morph;"..dropdown..";1]"..
|
|
"button_exit[0.5,3.0;7,0.5;set;Set]")
|
|
end
|
|
|
|
|
|
|
|
local function pad_can_dig(pos, player)
|
|
local meta = minetest.env:get_meta(pos)
|
|
local ownername = meta:get_string("owner")
|
|
local playername = player:get_player_name()
|
|
if ownername == "" or ownername == nil or playername == ownername
|
|
or minetest.get_player_privs(playername).morph_admin then
|
|
remove_preview(pos)
|
|
return true
|
|
end
|
|
minetest.chat_send_player(playername, "You may not break this.")
|
|
return false
|
|
end
|
|
|
|
minetest.register_node("morph_api:morph_pad", {
|
|
description = "Morph Pad",
|
|
drawtype = "nodebox",
|
|
node_box = {
|
|
type = "fixed",
|
|
fixed = {-0.5, -0.5, -0.5, 0.5, -0.25, 0.5},
|
|
},
|
|
groups = {choppy = 1},
|
|
sunlight_propagates = true,
|
|
paramtype = "light",
|
|
after_place_node = pad_placed,
|
|
tiles = {"morph_api_morph_pad.png"},
|
|
can_dig = pad_can_dig,
|
|
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
|
morph_pad_clicked(pos,clicker)
|
|
end,
|
|
})
|
|
|
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|
local name = player:get_player_name()
|
|
local pos
|
|
if formname ~= "set_morph" then
|
|
return
|
|
end
|
|
if not fields.morph then
|
|
return
|
|
end
|
|
if pads[name] then
|
|
pos = pads[name]
|
|
else
|
|
return
|
|
end
|
|
if not minetest.get_node(pos).name == "morph_api:morph_pad" then
|
|
return
|
|
end
|
|
|
|
local morph = fields.morph
|
|
if not morph_api.place_allowed(name,morph) then
|
|
print("!!!!! user ".. name.. " attempted to place denied morph "..morph)
|
|
return
|
|
end
|
|
if morph_api.morphs[fields.morph] and fields.set then
|
|
if minetest.get_node(pos).name == "morph_api:morph_pad" then
|
|
local meta = minetest.get_meta(pos)
|
|
meta:set_string("morph",fields.morph)
|
|
print(dump(player:get_look_horizontal()))
|
|
meta:set_float("rotate", player:get_look_horizontal() - math.rad(180))
|
|
meta:set_string("infotext",name.."'s "..fields.morph.." morph")
|
|
local ent = minetest.add_entity(pos, "morph_api:preview")
|
|
end
|
|
pads[name] = nil
|
|
end
|
|
end)
|
|
|
|
-- register default morph
|
|
if plapi then
|
|
morph_api.register_morph("Default",{
|
|
player_model = "character.b3d",
|
|
physics = {speed = 1, jump = 1, gravity = 1, sneak = true}
|
|
})
|
|
minetest.register_on_joinplayer(function(ObjectRef, last_login)
|
|
local name = ObjectRef:get_player_name()
|
|
morph_api.player[name] = "Default"
|
|
morph_api.player_parent[name] = "Default"
|
|
morph_api.morph(ObjectRef,"Default")
|
|
end)
|
|
end
|
|
|
|
minetest.register_on_leaveplayer(function(ObjectRef, timed_out)
|
|
local name = ObjectRef:get_player_name()
|
|
morph_api.player_scale[name] = nil
|
|
morph_api.player[name] = nil
|
|
morph_api.player_parent[name] = nil
|
|
morph_api.player_properties[name] = nil
|
|
end)
|
|
|