]> git.lizzy.rs Git - Crafter.git/blob - mods/player_api/init.lua
Add in player dying animation
[Crafter.git] / mods / player_api / init.lua
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
8 api.pairs             = pairs
9 api.ipairs            = ipairs
10 api.name              = nil
11 api.item              = nil
12 api.item_string       = nil
13 api.object            = nil
14 api.object_string     = nil
15 api.entity            = nil
16 api.data_index        = nil
17 api.current_animation = nil
18 api.animations        = nil
19 api.opacity           = nil
20 api.pitch             = nil
21 api.control_table     = nil
22 api.controls          = nil
23 api.old_controls      = nil
24 api.update            = nil
25 api.player_data       = nil
26 api.state             = nil
27 api.mouse             = nil
28 api.translated        = nil
29 api.swimming          = nil
30 api.force_update      = nil
31 api.get_connected     = minetest.get_connected_players
32 -- player physical data constant
33 api.player = {
34         visual               = "mesh"       ,
35         mesh                 = "player.b3d" ,
36         animation_speed      = 24           ,
37         visual_size          = {x = 1, y = 1, z = 1},
38         textures             = {
39                                                         "player.png"    ,
40                                                         "blank_skin.png",
41                                                    },
42
43         animations           = {
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 },
57
58                                                    },
59
60         current_animation    = "stand",
61         swimming             = false,
62         collisionbox         = {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3},
63         old_controls         = {},
64         stepheight           = 0.6  ,
65         eye_height           = 1.47 ,
66         attached             = false,
67         wield_item           = nil  ,
68 }
69
70 -- allows other mods to register models
71 player_pointer.register_model = function(name, def)
72         models[name] = def
73 end
74
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] = {}
80         end
81
82         for key,data in api.pairs(api.player) do
83                 player_pool[api.name][key] = data
84         end     
85 end
86
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] = {}
92         end
93
94         for index,i_data in api.pairs(data) do
95                 player_pool[api.name][index] = i_data
96         end
97 end
98
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] = {}
104         end
105
106         for index,i_data in api.pairs(data) do
107                 player_pool[api.name][index] = i_data
108         end
109
110         player:set_properties(data)
111 end
112
113 -- removes data
114 api.terminate = function(player)
115         api.name = player:get_player_name()
116         if player_pool[name] then
117                 player_pool[name] = nil
118         end
119 end
120
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
125                 local data_list = {}
126                 local count     = 0
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]
130                                 count = count + 1
131                         end
132                 end
133                 if count > 0 then
134                         return(data_list)
135                 else
136                         return(nil)
137                 end
138         end
139         return(nil)
140 end
141
142
143 -- set player wield item
144 api.update_wield_item = function(player)
145         api.name = player:get_player_name()
146         
147         api.item = api.get_data(player,{"wield_item"})
148         if api.item then
149                 api.item = api.item.item
150         end
151
152         api.item_string = player:get_wielded_item():get_name()
153
154         if api.item or (api.item and not api.item:get_luaentity()) then
155                 
156                 api.object = minetest.add_entity(player:get_pos(),"player_api:item")
157
158                 api.entity = api.object:get_luaentity()
159
160                 if api.entity then
161
162                         api.entity:set_item(api.item_string)
163                         
164                         api.entity.wielder = api.name
165                         
166                         api.object:set_attach(player, "Right_Hand", vector.new(0,0,0), vector.new(0,0,0))
167                         
168                         api.set_data(player,{
169                                 wield_item = api.object
170                         })
171                 end
172                 return
173         end
174         
175         api.entity = api.item:get_luaentity()
176
177         api.object_string = api.entity.itemstring
178
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)
182         end
183 end
184
185
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)
191         else
192                 return(nil)
193         end
194 end
195
196 -- easy way to allocate new players
197 api.set_all_properties = function(player)
198         api.player_data = api.get_data(player,{
199                 "visual",
200                 "mesh",
201                 "textures",
202                 "collisionbox",
203                 "eye_height",
204                 "stepheight",
205                 "visual_size"
206         })
207         player:set_properties(api.player_data)
208 end
209
210 -- easy way to set textures
211 api.set_textures = function(player, textures)
212         api.set_data(player,{
213                 texture = textures,
214         })
215         player:set_properties({textures = textures})
216 end
217
218 -- easy way for other mods to set textures
219 player_pointer.set_textures = function(player,textures)
220         api.set_textures(player,textures)
221 end
222
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
228         end
229
230         if api.current_animation == animation_name then
231                 return
232         end
233
234         api.animations = api.get_data(player,{"animations"})
235         if api.animations then
236                 api.animations = api.animations.animations
237         end
238
239         print(loop)
240
241         api.animations = api.animations[animation_name]
242         
243         player:set_animation(api.animations, speed, 0, loop)
244
245         api.set_data(player,{
246                 current_animation = animation_name
247         })
248 end
249
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)
253 end
254
255 -- allows mods to force update animation
256 player_pointer.force_update = function(player)
257         api.set_data(player,{
258                 force_update = true
259         })
260 end
261
262 -- force updates the player
263 api.create_force_update = function(player)
264         api.set_data(player,{
265                 force_update = true
266         })
267 end
268
269
270 -- toggles nametag visibility
271 api.show_nametag = function(player,boolean)
272         if boolean then
273                 api.opacity = 255
274         else
275                 api.opacity = 0
276         end
277         
278         player:set_nametag_attributes({
279                 color = {
280                         r = 255,
281                         b = 255,
282                         a = api.opacity,
283                         g = 255
284                 }
285         })
286 end
287
288 -- remove all player data
289 minetest.register_on_leaveplayer(function(player)
290         api.terminate(player)
291 end)
292
293
294 -- converts yaw to degrees
295 api.degrees = function(yaw)
296         return(yaw*180.0/math.pi)
297 end
298
299 -- controls head bone
300 api.pitch_look = function(player,sneak)
301         api.state = movement_pointer.get_data(player,{"swimming"})
302         if api.state then
303                 api.state = api.state.swimming
304         end
305
306         api.pitch = api.degrees(player:get_look_vertical()) * -1
307         if api.swimming then
308                 api.pitch = api.pitch + 90
309         elseif sneak then
310                 api.pitch = api.pitch + 15
311         end
312         player:set_bone_position("Head", vector.new(0,6.3,0), vector.new(api.pitch,0,0))
313 end
314
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
320         end
321
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
325         end
326
327         if api.force_update then
328                 api.set_data(player,{
329                         old_controls = control_table,
330                         force_update = nil          ,
331                 })
332                 return(true)
333         end
334
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
339                         })
340                         return(true)
341                 end
342         end
343         api.set_data(player,{
344                 old_controls = control_table
345         })
346         return(false)
347 end
348
349 -- movement to animation translations
350 api.translation_table = {
351         ["walk"] = {
352                 ["keys"]    = { -- required keys
353                         up      = true,
354                         down    = true,
355                         left    = true,
356                         right   = true,
357                 },
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},
363                         },
364                         [true ] = {
365                                 [0] = {animation = "walk_mine", speed = 24},
366                                 [1] = {animation = "walk_mine", speed = 36},
367                                 [2] = {animation = "walk_mine", speed = 42},
368                         }
369                 }
370         },
371
372         ["sneak"] = {
373                 ["keys"]    = {
374                         up      = true,
375                         down    = true,
376                         left    = true,
377                         right   = true,
378                 },
379                 ["states" ] = {
380                         [true ] = { -- moving
381                                 --mouse input
382                                 [false] = {animation = "sneak_walk"     , speed = 24},
383                                 [true ] = {animation = "sneak_mine_walk", speed = 24},
384                         },
385                         [false] = { -- moving
386                                 --mouse input
387                                 [false] = {animation = "sneak"           , speed = 0 },
388                                 [true ] = {animation = "sneak_mine_stand", speed = 24},
389                         }
390                 }
391         },
392         
393         ["stand"]   = {
394                 [true ] = {animation = "mine" , speed = 24},
395                 [false] = {animation = "stand", speed = 0 },
396         },
397
398         ["swim"] = {
399                 ["keys"]    = { -- required keys
400                         up      = true,
401                         down    = true,
402                         left    = true,
403                         right   = true,
404                 },
405                 ["states"]  = {
406                         [true ] = {animation = "swim"      , speed = 24},
407                         [false] = {animation = "swim_still", speed = 0 },
408                 }
409         }
410 }
411
412 -- translate input and combine with state
413 api.control_translation = function(player,control)
414         api.state = movement_pointer.get_data(player,{"state","swimming"})
415
416         if api.state then
417                 api.swimming = api.state.swimming
418                 api.state    = api.state.state
419         end
420
421         api.mouse = (control.LMB or control.RMB)
422
423         if api.swimming then
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)
428                                 return
429                         end
430                 end
431                 api.translated = api.translation_table.swim.states[false]
432                 api.set_animation(player, api.translated.animation, api.translated.speed)
433                 return
434         else
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)
440                                         return
441                                 end
442                         end
443                         api.translated = api.translation_table.sneak.states[false][api.mouse]
444                         api.set_animation(player, api.translated.animation, api.translated.speed)
445                         return
446                 else
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)
452                                                 return
453                                         end
454                                 end
455                         end
456                 end
457
458                 api.translated = api.translation_table.stand[api.mouse]
459                 api.set_animation(player, api.translated.animation, api.translated.speed)
460         end
461 end
462
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)
468
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)
473         end
474 end
475
476
477
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)
482 end)
483
484 minetest.register_on_respawnplayer(function(player)
485         api.create_force_update(player)
486 end)
487
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)
492         end
493 end)
494
495
496
497
498 minetest.register_entity("player_api:item", {
499         initial_properties = {
500                 hp_max = 1,
501                 physical = true,
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},
506                 textures = {""},
507                 spritediv = {x = 1, y = 1},
508                 initial_sprite_basepos = {x = 0, y = 0},
509                 is_visible = true,
510                 pointable = false,
511         },
512
513         itemstring = "",
514
515         set_item = function(self, item)
516                 local stack = ItemStack(item or self.itemstring)
517                 
518                 self.itemstring = stack:to_string()
519                 
520
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"
524
525                 local max_count = stack:get_stack_max()
526                 local count = math.min(stack:get_count(), max_count)
527
528                 local size = 0.21
529                 local coll_height = size * 0.75
530                 local def = minetest.registered_nodes[itemname]
531                 local glow = def and def.light_source
532
533                 local is_visible = true
534                 if self.itemstring == "" then
535                         -- item not yet known
536                         is_visible = false
537                 end
538
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,
549                         glow = glow,
550                 })
551         end,
552
553         on_step = function(self, dtime)
554                 if not self.wielder then
555                         self.object:remove()
556                 end
557         end,
558 })