1 local minetest,math = minetest,math
2 local api = {} -- api class
3 local wield = {} -- wield item class
4 local player_pool = {} -- holds data about the players
5 player_pointer = {} -- allows other mods to modify player attributes
6 api.registered_models = {}
7 api.animation_blend = 0
14 api.object_string = nil
17 api.current_animation = nil
21 api.control_table = nil
23 api.old_controls = nil
30 api.force_update = nil
31 api.get_connected = minetest.get_connected_players
32 -- player physical data constant
36 animation_speed = 24 ,
37 visual_size = {x = 1, y = 1, z = 1},
44 stand = { x = 5 , y = 5 },
45 lay = { x = 162, y = 162 },
46 walk = { x = 168, y = 187 },
47 mine = { x = 189, y = 198 },
48 walk_mine = { x = 200, y = 219 },
49 sit = { x = 81 , y = 160 },
50 sneak = { x = 60 , y = 60 },
51 sneak_mine_stand = { x = 20 , y = 30 },
52 sneak_walk = { x = 60 , y = 80 },
53 sneak_mine_walk = { x = 40 , y = 59 },
54 swim = { x = 221, y = 241 },
55 swim_still = { x = 226, y = 226 },
56 die = { x = 242, y = 253 },
60 current_animation = "stand",
62 collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3},
70 -- allows other mods to register models
71 player_pointer.register_model = function(name, def)
75 -- creates default data for players
76 api.create_data = function(player)
77 api.name = player:get_player_name()
78 if not player_pool[api.name] then
79 player_pool[api.name] = {}
82 for key,data in api.pairs(api.player) do
83 player_pool[api.name][key] = data
87 -- creates volitile data for the game to use
88 api.set_data = function(player,data)
89 api.name = player:get_player_name()
90 if not player_pool[api.name] then
91 player_pool[api.name] = {}
94 for index,i_data in api.pairs(data) do
95 player_pool[api.name][index] = i_data
99 -- allows other mods to modify the player
100 player_pointer.set_data = function(player,data)
101 api.name = player:get_player_name()
102 if not player_pool[api.name] then
103 player_pool[api.name] = {}
106 for index,i_data in api.pairs(data) do
107 player_pool[api.name][index] = i_data
110 player:set_properties(data)
114 api.terminate = function(player)
115 api.name = player:get_player_name()
116 if player_pool[name] then
117 player_pool[name] = nil
121 -- indexes and returns data
122 api.get_data = function(player,requested_data)
123 api.name = player:get_player_name()
124 if player_pool[api.name] then
127 for index,i_data in api.pairs(requested_data) do
128 if player_pool[api.name][i_data] then
129 data_list[i_data] = player_pool[api.name][i_data]
143 -- set player wield item
144 api.update_wield_item = function(player)
145 api.name = player:get_player_name()
147 api.item = api.get_data(player,{"wield_item"})
149 api.item = api.item.item
152 api.item_string = player:get_wielded_item():get_name()
154 if api.item or (api.item and not api.item:get_luaentity()) then
156 api.object = minetest.add_entity(player:get_pos(),"player_api:item")
158 api.entity = api.object:get_luaentity()
162 api.entity:set_item(api.item_string)
164 api.entity.wielder = api.name
166 api.object:set_attach(player, "Right_Hand", vector.new(0,0,0), vector.new(0,0,0))
168 api.set_data(player,{
169 wield_item = api.object
175 api.entity = api.item:get_luaentity()
177 api.object_string = api.entity.itemstring
179 if api.entity and api.object_string ~= api.item_string then
180 api.entity.itemstring = player_wield_item
181 api.entity:set_item(player_wield_item)
186 -- easier way to index animation
187 api.get_animation = function(player)
188 api.data_index = api.get_data(player,{"current_animation"})
189 if api.data_index and api.data_index.current_animation then
190 return(api.data_index.current_animation)
196 -- easy way to allocate new players
197 api.set_all_properties = function(player)
198 api.player_data = api.get_data(player,{
207 player:set_properties(api.player_data)
210 -- easy way to set textures
211 api.set_textures = function(player, textures)
212 api.set_data(player,{
215 player:set_properties({textures = textures})
218 -- easy way for other mods to set textures
219 player_pointer.set_textures = function(player,textures)
220 api.set_textures(player,textures)
223 -- easy way to set animation
224 api.set_animation = function(player, animation_name, speed, loop)
225 api.current_animation = api.get_data(player,{"current_animation"})
226 if api.current_animation then
227 api.current_animation = api.current_animation.current_animation
230 if api.current_animation == animation_name then
234 api.animations = api.get_data(player,{"animations"})
235 if api.animations then
236 api.animations = api.animations.animations
241 api.animations = api.animations[animation_name]
243 player:set_animation(api.animations, speed, 0, loop)
245 api.set_data(player,{
246 current_animation = animation_name
250 -- allows other mods to set player animation
251 player_pointer.set_animation = function(player,animation_name,speed)
252 api.set_animation(player,animation_name,speed)
255 -- allows mods to force update animation
256 player_pointer.force_update = function(player)
257 api.set_data(player,{
262 -- force updates the player
263 api.create_force_update = function(player)
264 api.set_data(player,{
270 -- toggles nametag visibility
271 api.show_nametag = function(player,boolean)
278 player:set_nametag_attributes({
288 -- remove all player data
289 minetest.register_on_leaveplayer(function(player)
290 api.terminate(player)
294 -- converts yaw to degrees
295 api.degrees = function(yaw)
296 return(yaw*180.0/math.pi)
299 -- controls head bone
300 api.pitch_look = function(player,sneak)
301 api.state = movement_pointer.get_data(player,{"swimming"})
303 api.state = api.state.swimming
306 api.pitch = api.degrees(player:get_look_vertical()) * -1
308 api.pitch = api.pitch + 90
310 api.pitch = api.pitch + 15
312 player:set_bone_position("Head", vector.new(0,6.3,0), vector.new(api.pitch,0,0))
315 -- checks if the player has done anything with their keyboard/mouse
316 api.control_check = function(player,control_table)
317 api.old_controls = api.get_data(player,{"old_controls"})
318 if api.old_controls then
319 api.old_controls = api.old_controls.old_controls
322 api.force_update = api.get_data(player,{"force_update"})
323 if api.force_update then
324 api.force_update = api.force_update.force_update
327 if api.force_update then
328 api.set_data(player,{
329 old_controls = control_table,
335 for i,k in api.pairs(api.old_controls) do
336 if control_table[i] ~= k then
337 api.set_data(player,{
338 old_controls = control_table
343 api.set_data(player,{
344 old_controls = control_table
349 -- movement to animation translations
350 api.translation_table = {
352 ["keys"] = { -- required keys
358 ["states" ] = { -- states
359 [false] = { -- mouse input
360 [0] = {animation = "walk", speed = 24},
361 [1] = {animation = "walk", speed = 36},
362 [2] = {animation = "walk", speed = 42},
365 [0] = {animation = "walk_mine", speed = 24},
366 [1] = {animation = "walk_mine", speed = 36},
367 [2] = {animation = "walk_mine", speed = 42},
380 [true ] = { -- moving
382 [false] = {animation = "sneak_walk" , speed = 24},
383 [true ] = {animation = "sneak_mine_walk", speed = 24},
385 [false] = { -- moving
387 [false] = {animation = "sneak" , speed = 0 },
388 [true ] = {animation = "sneak_mine_stand", speed = 24},
394 [true ] = {animation = "mine" , speed = 24},
395 [false] = {animation = "stand", speed = 0 },
399 ["keys"] = { -- required keys
406 [true ] = {animation = "swim" , speed = 24},
407 [false] = {animation = "swim_still", speed = 0 },
412 -- translate input and combine with state
413 api.control_translation = function(player,control)
414 api.state = movement_pointer.get_data(player,{"state","swimming"})
417 api.swimming = api.state.swimming
418 api.state = api.state.state
421 api.mouse = (control.LMB or control.RMB)
424 for k,i in api.pairs(control) do
425 if i and api.translation_table.swim.keys[k] then
426 api.translated = api.translation_table.swim.states[true]
427 api.set_animation(player, api.translated.animation, api.translated.speed)
431 api.translated = api.translation_table.swim.states[false]
432 api.set_animation(player, api.translated.animation, api.translated.speed)
435 if control.sneak then
436 for k,i in api.pairs(control) do
437 if i and api.translation_table.sneak.keys[k] then
438 api.translated = api.translation_table.sneak.states[true][api.mouse]
439 api.set_animation(player, api.translated.animation, api.translated.speed)
443 api.translated = api.translation_table.sneak.states[false][api.mouse]
444 api.set_animation(player, api.translated.animation, api.translated.speed)
447 for k,i in api.pairs(control) do
448 if i and api.translation_table.walk.keys[k] then
449 api.translated = api.translation_table.walk.states[api.mouse][api.state]
450 if api.translated then
451 api.set_animation(player, api.translated.animation, api.translated.speed)
458 api.translated = api.translation_table.stand[api.mouse]
459 api.set_animation(player, api.translated.animation, api.translated.speed)
463 -- translates player movement to animation
464 api.do_animations = function(player)
465 api.control_table = player:get_player_control()
466 api.update = api.control_check(player,api.control_table)
467 api.pitch_look(player,api.control_table.sneak)
469 if api.update and player:get_hp() > 0 then
470 api.control_translation(player,api.control_table)
471 elseif player:get_hp() <= 0 then
472 api.set_animation(player,"die",40,false)
478 -- Update appearance when the player joins
479 minetest.register_on_joinplayer(function(player)
480 api.create_data(player)
481 api.set_all_properties(player)
484 minetest.register_on_respawnplayer(function(player)
485 api.create_force_update(player)
488 -- inject into global loop
489 minetest.register_globalstep(function()
490 for _,player in api.ipairs(api.get_connected()) do
491 api.do_animations(player)
498 minetest.register_entity("player_api:item", {
499 initial_properties = {
502 collide_with_objects = false,
503 collisionbox = {0, 0, 0, 0, 0, 0},
504 visual = "wielditem",
505 visual_size = {x = 0.21, y = 0.21},
507 spritediv = {x = 1, y = 1},
508 initial_sprite_basepos = {x = 0, y = 0},
515 set_item = function(self, item)
516 local stack = ItemStack(item or self.itemstring)
518 self.itemstring = stack:to_string()
521 -- Backwards compatibility: old clients use the texture
522 -- to get the type of the item
523 local itemname = stack:is_known() and stack:get_name() or "unknown"
525 local max_count = stack:get_stack_max()
526 local count = math.min(stack:get_count(), max_count)
529 local coll_height = size * 0.75
530 local def = minetest.registered_nodes[itemname]
531 local glow = def and def.light_source
533 local is_visible = true
534 if self.itemstring == "" then
535 -- item not yet known
539 self.object:set_properties({
540 is_visible = is_visible,
541 visual = "wielditem",
542 textures = {itemname},
543 visual_size = {x = size, y = size},
544 collisionbox = {-size, -0.21, -size,
545 size, coll_height, size},
546 selectionbox = {-size, -size, -size, size, size, size},
547 --automatic_rotate = math.pi * 0.5 * 0.2 / size,
548 wield_item = self.itemstring,
553 on_step = function(self, dtime)
554 if not self.wielder then