diff --git a/game_api.txt b/game_api.txt index b47b0c5d..6a013224 100644 --- a/game_api.txt +++ b/game_api.txt @@ -449,13 +449,13 @@ The player API can register player models and update the player's appearance. * `player_api.set_model(player, model_name)` * Change a player's model * `player`: PlayerRef - * `model_name`: model registered with player_api.register_model() + * `model_name`: model registered with `player_api.register_model` * `player_api.set_animation(player, anim_name, speed)` - * Applies an animation to a player + * Applies an animation to a player if speed or anim_name differ from the currently playing animation * `player`: PlayerRef * `anim_name`: name of the animation - * `speed`: frames per second. If nil, the default from the model def is used + * `speed`: keyframes per second. If nil, the default from the model def is used * `player_api.set_textures(player, textures)` * Sets player textures @@ -477,18 +477,18 @@ The player API can register player models and update the player's appearance. ### Model Definition { - animation_speed = 30, -- Default animation speed, in FPS - textures = {"character.png", }, -- Default array of textures - visual_size = {x = 1, y = 1}, -- Used to scale the model + animation_speed = 30, -- Default animation speed, in keyframes per second + textures = {"character.png"}, -- Default array of textures animations = { - -- = {x = , y = }, - foo = {x = 0, y = 19}, - bar = {x = 20, y = 39}, - -- ... + -- [anim_name] = {x = , y = , collisionbox = model collisionbox, eye_height = model eye height}, + stand = ..., lay = ..., walk = ..., mine = ..., walk_mine = ..., -- required animations + sit = ... -- used by boats and other MTG mods }, - collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3}, -- In nodes from feet position - stepheight = 0.6, -- In nodes - eye_height = 1.47, -- In nodes above feet position + -- Default object properties, see lua_api.txt + visual_size = {x = 1, y = 1}, + collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3}, + stepheight = 0.6, + eye_height = 1.47 } diff --git a/mods/beds/functions.lua b/mods/beds/functions.lua index ddac52be..409aa376 100644 --- a/mods/beds/functions.lua +++ b/mods/beds/functions.lua @@ -116,8 +116,6 @@ local function lay_down(player, pos, bed_pos, state, skip) beds.bed_position[name] = bed_pos beds.player[name] = {physics_override = player:get_physics_override()} - -- physics, eye_offset, etc - player:set_eye_offset({x = 0, y = -13, z = 0}, {x = 0, y = 0, z = 0}) local yaw, param2 = get_look_yaw(bed_pos) player:set_look_horizontal(yaw) local dir = minetest.facedir_to_dir(param2) diff --git a/mods/player_api/api.lua b/mods/player_api/api.lua index 0aee048e..f341e6ea 100644 --- a/mods/player_api/api.lua +++ b/mods/player_api/api.lua @@ -1,54 +1,88 @@ --- Minetest 0.4 mod: player --- See README.txt for licensing and other information. - player_api = {} -- Player animation blending -- Note: This is currently broken due to a bug in Irrlicht, leave at 0 local animation_blend = 0 -player_api.registered_models = { } +player_api.registered_models = {} -- Local for speed. local models = player_api.registered_models +local function collisionbox_equals(collisionbox, other_collisionbox) + if collisionbox == other_collisionbox then + return true + end + for index = 1, 6 do + if collisionbox[index] ~= other_collisionbox[index] then + return false + end + end + return true +end + function player_api.register_model(name, def) models[name] = def + def.visual_size = def.visual_size or {x = 1, y = 1} + def.collisionbox = def.collisionbox or {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3} + def.stepheight = def.stepheight or 0.6 + def.eye_height = def.eye_height or 1.47 + + -- Sort animations into property classes: + -- Animations with same properties have the same _equals value + for animation_name, animation in pairs(def.animations) do + animation.eye_height = animation.eye_height or def.eye_height + animation.collisionbox = animation.collisionbox or def.collisionbox + for _, other_animation in pairs(def.animations) do + if other_animation._equals then + if collisionbox_equals(animation.collisionbox, other_animation.collisionbox) + and animation.eye_height == other_animation.eye_height then + animation._equals = other_animation._equals + break + end + end + end + animation._equals = animation._equals or animation_name + end end -- Player stats and animations -local player_model = {} -local player_textures = {} -local player_anim = {} -local player_sneak = {} +-- model, textures, animation +local players = {} player_api.player_attached = {} +local function get_player_data(player) + return assert(players[player:get_player_name()]) +end + function player_api.get_animation(player) - local name = player:get_player_name() - return { - model = player_model[name], - textures = player_textures[name], - animation = player_anim[name], - } + return get_player_data(player) end -- Called when a player's appearance needs to be updated function player_api.set_model(player, model_name) - local name = player:get_player_name() + local player_data = get_player_data(player) + if player_data.model == model_name then + return + end local model = models[model_name] if model then - if player_model[name] == model_name then - return - end player:set_properties({ mesh = model_name, - textures = player_textures[name] or model.textures, + textures = player_data.textures or model.textures, visual = "mesh", - visual_size = model.visual_size or {x = 1, y = 1}, - collisionbox = model.collisionbox or {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3}, - stepheight = model.stepheight or 0.6, - eye_height = model.eye_height or 1.47, + visual_size = model.visual_size, + stepheight = model.stepheight }) + local animations = model.animations + player:set_local_animation( + animations.stand, + animations.walk, + animations.mine, + animations.walk_mine, + model.animation_speed or 30 + ) + -- sets collisionbox & eye_height player_api.set_animation(player, "stand") else player:set_properties({ @@ -60,37 +94,61 @@ function player_api.set_model(player, model_name) eye_height = 1.625, }) end - player_model[name] = model_name + player_data.model = model_name +end + +function player_api.get_textures(player) + local player_data = get_player_data(player) + local model = models[player_data.model] + return assert(player_data.textures or (model and model.textures)) end function player_api.set_textures(player, textures) - local name = player:get_player_name() - local model = models[player_model[name]] - local model_textures = model and model.textures or nil - player_textures[name] = textures or model_textures - player:set_properties({textures = textures or model_textures}) + local player_data = get_player_data(player) + local model = models[player_data.model] + local new_textures = assert(textures or (model and model.textures)) + player_data.textures = new_textures + player:set_properties({textures = new_textures}) +end + +function player_api.set_texture(player, index, texture) + local textures = table.copy(player_api.get_textures(player)) + textures[index] = texture + player_api.set_textures(player, textures) end function player_api.set_animation(player, anim_name, speed) - local name = player:get_player_name() - if player_anim[name] == anim_name then - return - end - local model = player_model[name] and models[player_model[name]] + local player_data = get_player_data(player) + local model = models[player_data.model] if not (model and model.animations[anim_name]) then return end + speed = speed or model.animation_speed + if player_data.animation == anim_name and player_data.animation_speed == speed then + return + end + local previous_anim_equals = (model.animations[player_data.animation] or {})._equals local anim = model.animations[anim_name] - player_anim[name] = anim_name - player:set_animation(anim, speed or model.animation_speed, animation_blend) + player_data.animation = anim_name + player_data.animation_speed = speed + player:set_animation(anim, speed, animation_blend) + if anim._equals ~= previous_anim_equals then + player:set_properties({ + collisionbox = anim.collisionbox, + eye_height = anim.eye_height + }) + end end +minetest.register_on_joinplayer(function(player) + local name = player:get_player_name() + players[name] = {} + player_api.player_attached[name] = false +end) + minetest.register_on_leaveplayer(function(player) local name = player:get_player_name() - player_model[name] = nil - player_anim[name] = nil - player_textures[name] = nil - player_sneak[name] = nil + players[name] = nil player_api.player_attached[name] = nil end) @@ -111,8 +169,8 @@ end minetest.register_globalstep(function() for _, player in pairs(minetest.get_connected_players()) do local name = player:get_player_name() - local model_name = player_model[name] - local model = model_name and models[model_name] + local player_data = players[name] + local model = models[player_data.model] if model and not player_attached[name] then local controls = player:get_player_control() local animation_speed_mod = model.animation_speed or 30 @@ -125,12 +183,7 @@ minetest.register_globalstep(function() -- Apply animations based on what the player is doing if player:get_hp() == 0 then player_set_animation(player, "lay") - -- Determine if the player is walking elseif controls.up or controls.down or controls.left or controls.right then - if player_sneak[name] ~= controls.sneak then - player_anim[name] = nil - player_sneak[name] = controls.sneak - end if controls.LMB or controls.RMB then player_set_animation(player, "walk_mine", animation_speed_mod) else @@ -144,3 +197,15 @@ minetest.register_globalstep(function() end end end) + +for _, api_function in pairs({"get_animation", "set_animation", "set_model", "set_textures"}) do + local original_function = player_api[api_function] + player_api[api_function] = function(player, ...) + if not players[player:get_player_name()] then + -- HACK for keeping backwards compatibility + minetest.log("warning", api_function .. " called on offline player") + return + end + original_function(player, ...) + end +end diff --git a/mods/player_api/init.lua b/mods/player_api/init.lua index 1176b0ea..6bc3c821 100644 --- a/mods/player_api/init.lua +++ b/mods/player_api/init.lua @@ -1,5 +1,3 @@ --- player/init.lua - dofile(minetest.get_modpath("player_api") .. "/api.lua") -- Default player appearance @@ -9,11 +7,11 @@ player_api.register_model("character.b3d", { animations = { -- Standard animations. stand = {x = 0, y = 79}, - lay = {x = 162, y = 166}, + lay = {x = 162, y = 166, collisionbox = {-0.6, 0.0, -0.6, 0.6, 0.3, 0.6}, eye_height = 0.3}, walk = {x = 168, y = 187}, mine = {x = 189, y = 198}, walk_mine = {x = 200, y = 219}, - sit = {x = 81, y = 160}, + sit = {x = 81, y = 160, collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.0, 0.3}, eye_height = 0.8} }, collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3}, stepheight = 0.6, @@ -22,13 +20,5 @@ player_api.register_model("character.b3d", { -- Update appearance when the player joins minetest.register_on_joinplayer(function(player) - player_api.player_attached[player:get_player_name()] = false player_api.set_model(player, "character.b3d") - player:set_local_animation( - {x = 0, y = 79}, - {x = 168, y = 187}, - {x = 189, y = 198}, - {x = 200, y = 219}, - 30 - ) end) diff --git a/mods/player_api/mod.conf b/mods/player_api/mod.conf index 198b86dc..bf62327b 100644 --- a/mods/player_api/mod.conf +++ b/mods/player_api/mod.conf @@ -1,2 +1,2 @@ name = player_api -description = Minetest Game mod: player_api +description = Minetest Game mod: Manages player visuals