Implement smooth bow charging, based on [bows]
[MineClone/MineClone2.git] / mods / ITEMS / mcl_throwing / init.lua
blob4e24a42d43ed71f153e8149e725510e0ba259fa6
1 mcl_throwing = {}
3 dofile(minetest.get_modpath("mcl_throwing").."/arrow.lua")
4 dofile(minetest.get_modpath("mcl_throwing").."/throwable.lua")
6 local arrows = {
7 ["mcl_throwing:arrow"] = "mcl_throwing:arrow_entity",
10 local GRAVITY = 9.81
11 local BOW_DURABILITY = 385
12 local CHARGE_SPEED = 1
14 local bow_load = {}
16 mcl_throwing.shoot_arrow = function(arrow_item, pos, dir, yaw, shooter, power, damage)
17 local obj = minetest.add_entity({x=pos.x,y=pos.y,z=pos.z}, arrows[arrow_item])
18 if power == nil then
19 power = 19
20 end
21 if damage == nil then
22 damage = 3
23 end
24 obj:setvelocity({x=dir.x*power, y=dir.y*power, z=dir.z*power})
25 obj:setacceleration({x=dir.x*-3, y=-GRAVITY, z=dir.z*-3})
26 obj:setyaw(yaw-math.pi/2)
27 local le = obj:get_luaentity()
28 le._shooter = shooter
29 le._damage = damage
30 le._startpos = pos
31 minetest.sound_play("mcl_throwing_bow_shoot", {pos=pos})
32 if shooter ~= nil then
33 if obj:get_luaentity().player == "" then
34 obj:get_luaentity().player = shooter
35 end
36 obj:get_luaentity().node = shooter:get_inventory():get_stack("main", 1):get_name()
37 end
38 return obj
39 end
41 local get_arrow = function(player)
42 local inv = player:get_inventory()
43 local arrow_stack, arrow_stack_id
44 for i=1, inv:get_size("main") do
45 local it = inv:get_stack("main", i)
46 if not it:is_empty() and minetest.get_item_group(it:get_name(), "ammo_bow") ~= 0 then
47 arrow_stack = it
48 arrow_stack_id = i
49 break
50 end
51 end
52 return arrow_stack, arrow_stack_id
53 end
55 local player_shoot_arrow = function(itemstack, player, power, damage)
56 local arrow_stack, arrow_stack_id = get_arrow(player)
57 local arrow_itemstring
58 if not minetest.settings:get_bool("creative_mode") then
59 if not arrow_stack then
60 return false
61 end
62 arrow_itemstring = arrow_stack:get_name()
63 arrow_stack:take_item()
64 local inv = player:get_inventory()
65 inv:set_stack("main", arrow_stack_id, arrow_stack)
66 end
67 local playerpos = player:getpos()
68 local dir = player:get_look_dir()
69 local yaw = player:get_look_horizontal()
71 if not arrow_itemstring then
72 arrow_itemstring = "mcl_throwing:arrow"
73 end
74 mcl_throwing.shoot_arrow(arrow_itemstring, {x=playerpos.x,y=playerpos.y+1.5,z=playerpos.z}, dir, yaw, player, power, damage)
75 return true
76 end
78 local powerup_function = function(nextbow)
79 return function(itemstack, placer, pointed_thing)
80 -- Use pointed node's on_rightclick function first, if present
81 if pointed_thing.type == "node" then
82 local node = minetest.get_node(pointed_thing.under)
83 if placer and not placer:get_player_control().sneak then
84 if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
85 return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) or itemstack
86 end
87 end
88 end
90 -- Check for arrow or Creative Mode
91 if minetest.settings:get_bool("creative_mode") or get_arrow(placer) ~= nil then
92 local wear = itemstack:get_wear()
93 itemstack:replace(nextbow)
94 itemstack:set_wear(wear)
95 end
96 return itemstack
97 end
98 end
100 minetest.register_tool("mcl_throwing:bow", {
101 description = "Bow",
102 _doc_items_longdesc = "Bows are ranged weapons to shoot arrows at your foes.",
103 _doc_items_usagehelp = [[To use the bow, you first need to have at least one arrow anywhere in your inventory (unless in Creative Mode). Hold down the right mouse button to charge, release to shoot.
105 The arrow speed and damage increase with the charge level:
106 • Low charge: 1 damage
107 • Medium charge: 2 damage
108 • High charge: 4-5 damage (20% chance for 5 damage)]],
109 _doc_items_durability = BOW_DURABILITY,
110 inventory_image = "mcl_throwing_bow.png",
111 stack_max = 1,
112 -- Trick to disable melee damage to entities.
113 -- Range not set to 0 (unlike the others) so it can be placed into item frames
114 range = 1,
115 -- Trick to disable digging as well
116 on_use = function() end,
117 groups = {weapon=1,weapon_ranged=1},
120 minetest.register_tool("mcl_throwing:bow_0", {
121 description = "Bow",
122 _doc_items_create_entry = false,
123 inventory_image = "mcl_throwing_bow_0.png",
124 stack_max = 1,
125 range = 0, -- Pointing range to 0 to prevent punching with bow :D
126 groups = {not_in_creative_inventory=1, not_in_craft_guide=1},
129 minetest.register_tool("mcl_throwing:bow_1", {
130 description = "Bow",
131 _doc_items_create_entry = false,
132 inventory_image = "mcl_throwing_bow_1.png",
133 stack_max = 1,
134 range = 0,
135 groups = {not_in_creative_inventory=1, not_in_craft_guide=1},
138 minetest.register_tool("mcl_throwing:bow_2", {
139 description = "Bow",
140 _doc_items_create_entry = false,
141 inventory_image = "mcl_throwing_bow_2.png",
142 stack_max = 1,
143 range = 0,
144 groups = {not_in_creative_inventory=1, not_in_craft_guide=1},
147 controls.register_on_release(function(player, key, time)
148 if key~="RMB" then return end
149 local inv = minetest.get_inventory({type="player", name=player:get_player_name()})
150 local wielditem = player:get_wielded_item()
151 if (wielditem:get_name()=="mcl_throwing:bow_0" or wielditem:get_name()=="mcl_throwing:bow_1" or wielditem:get_name()=="mcl_throwing:bow_2") then
152 local shot = false
153 if wielditem:get_name()=="mcl_throwing:bow_0" then
154 shot = player_shoot_arrow(wielditem, player, 4, 1)
155 elseif wielditem:get_name()=="mcl_throwing:bow_1" then
156 shot = player_shoot_arrow(wielditem, player, 16, 2)
157 elseif wielditem:get_name()=="mcl_throwing:bow_2" then
158 local r = math.random(1,5)
159 local damage
160 -- Damage and range have been nerfed because the arrow charges very quickly
161 -- TODO: Use Minecraft damage and range (9-10 @ ca. 53 m/s)
162 if r == 1 then
163 -- 20% chance for critical hit
164 damage = 5
165 else
166 damage = 4
168 shot = player_shoot_arrow(wielditem, player, 26, damage)
170 wielditem:set_name("mcl_throwing:bow")
171 if shot and minetest.settings:get_bool("creative_mode") == false then
172 wielditem:add_wear(65535/BOW_DURABILITY)
174 player:set_wielded_item(wielditem)
175 bow_load[player:get_player_name()] = false
177 end)
179 controls.register_on_hold(function(player, key, time)
180 if key ~= "RMB" then
181 return
183 local name = player:get_player_name()
184 local inv = minetest.get_inventory({type="player", name=name})
185 local wielditem = player:get_wielded_item()
186 if wielditem:get_name()=="mcl_throwing:bow" and (minetest.settings:get_bool("creative_mode") or inv:contains_item("main", "mcl_throwing:arrow")) then
187 wielditem:set_name("mcl_throwing:bow_0")
188 bow_load[name] = os.time()
189 else
190 if type(bow_load[name]) == "number" then
191 if wielditem:get_name() == "mcl_throwing:bow_0" and os.time() - bow_load[name] > CHARGE_SPEED then
192 wielditem:set_name("mcl_throwing:bow_1")
193 elseif wielditem:get_name() == "mcl_throwing:bow_1" and os.time() - bow_load[name] > CHARGE_SPEED*2 then
194 wielditem:set_name("mcl_throwing:bow_2")
196 else
197 wielditem:set_name("mcl_throwing:bow")
200 player:set_wielded_item(wielditem)
201 end)
203 minetest.register_globalstep(function(dtime)
204 for _, player in pairs(minetest.get_connected_players()) do
205 local wielditem = player:get_wielded_item()
206 --if wielditem:get_name()=="bow:bow_dropped" then
207 -- wielditem:set_name("bow:bow")
208 -- player:set_wielded_item(wielditem)
209 --end
210 local controls = player:get_player_control()
211 local inv = minetest.get_inventory({type="player", name = player:get_player_name()})
212 if bow_load[player:get_player_name()] and (wielditem:get_name()~="mcl_throwing:bow_0" and wielditem:get_name()~="mcl_throwing:bow_1" and wielditem:get_name()~="mcl_throwing:bow_2") then
213 local list = inv:get_list("main")
214 for place, stack in pairs(list) do
215 if stack:get_name()=="mcl_throwing:bow_0" or stack:get_name()=="mcl_throwing:bow_1" or stack:get_name()=="mcl_throwing:bow_2" then
216 stack:set_name("mcl_throwing:bow")
217 list[place] = stack
218 break
221 inv:set_list("main", list)
222 bow_load[player:get_player_name()] = false
225 end)
227 if minetest.get_modpath("mcl_core") and minetest.get_modpath("mcl_mobitems") then
228 minetest.register_craft({
229 output = 'mcl_throwing:bow',
230 recipe = {
231 {'', 'mcl_core:stick', 'mcl_mobitems:string'},
232 {'mcl_core:stick', '', 'mcl_mobitems:string'},
233 {'', 'mcl_core:stick', 'mcl_mobitems:string'},
236 minetest.register_craft({
237 output = 'mcl_throwing:bow',
238 recipe = {
239 {'mcl_mobitems:string', 'mcl_core:stick', ''},
240 {'mcl_mobitems:string', '', 'mcl_core:stick'},
241 {'mcl_mobitems:string', 'mcl_core:stick', ''},
246 minetest.register_craft({
247 type = "fuel",
248 recipe = "mcl_throwing:bow",
249 burntime = 15,
252 -- Add entry aliases for the Help
253 if minetest.get_modpath("doc") then
254 doc.add_entry_alias("tools", "mcl_throwing:bow", "tools", "mcl_throwing:bow_0")
255 doc.add_entry_alias("tools", "mcl_throwing:bow", "tools", "mcl_throwing:bow_1")
256 doc.add_entry_alias("tools", "mcl_throwing:bow", "tools", "mcl_throwing:bow_2")