Increase arrow damage smooth dmg/speed increase
[MineClone/MineClone2.git] / mods / ITEMS / mcl_throwing / bow.lua
blobfdad089c74cffd2889589b14c6a44841e1df1532
1 mcl_throwing = {}
3 local arrows = {
4 ["mcl_throwing:arrow"] = "mcl_throwing:arrow_entity",
7 local GRAVITY = 9.81
8 local BOW_DURABILITY = 385
9 local BOW_CHARGE_TIME_HALF = 500000
10 local BOW_CHARGE_TIME_FULL = 1000000
12 -- TODO: Use Minecraft speed (ca. 53 m/s)
13 -- Currently nerfed because at full speed the arrow would easily get out of the range of the loaded map.
14 local BOW_MAX_SPEED = 26
16 local bow_load = {}
18 mcl_throwing.shoot_arrow = function(arrow_item, pos, dir, yaw, shooter, power, damage)
19 local obj = minetest.add_entity({x=pos.x,y=pos.y,z=pos.z}, arrows[arrow_item])
20 if power == nil then
21 power = 19
22 end
23 if damage == nil then
24 damage = 3
25 end
26 obj:setvelocity({x=dir.x*power, y=dir.y*power, z=dir.z*power})
27 obj:setacceleration({x=dir.x*-3, y=-GRAVITY, z=dir.z*-3})
28 obj:setyaw(yaw-math.pi/2)
29 local le = obj:get_luaentity()
30 le._shooter = shooter
31 le._damage = damage
32 le._startpos = pos
33 minetest.sound_play("mcl_throwing_bow_shoot", {pos=pos})
34 if shooter ~= nil then
35 if obj:get_luaentity().player == "" then
36 obj:get_luaentity().player = shooter
37 end
38 obj:get_luaentity().node = shooter:get_inventory():get_stack("main", 1):get_name()
39 end
40 return obj
41 end
43 local get_arrow = function(player)
44 local inv = player:get_inventory()
45 local arrow_stack, arrow_stack_id
46 for i=1, inv:get_size("main") do
47 local it = inv:get_stack("main", i)
48 if not it:is_empty() and minetest.get_item_group(it:get_name(), "ammo_bow") ~= 0 then
49 arrow_stack = it
50 arrow_stack_id = i
51 break
52 end
53 end
54 return arrow_stack, arrow_stack_id
55 end
57 local player_shoot_arrow = function(itemstack, player, power, damage)
58 local arrow_stack, arrow_stack_id = get_arrow(player)
59 local arrow_itemstring
60 if not minetest.settings:get_bool("creative_mode") then
61 if not arrow_stack then
62 return false
63 end
64 arrow_itemstring = arrow_stack:get_name()
65 arrow_stack:take_item()
66 local inv = player:get_inventory()
67 inv:set_stack("main", arrow_stack_id, arrow_stack)
68 end
69 local playerpos = player:getpos()
70 local dir = player:get_look_dir()
71 local yaw = player:get_look_horizontal()
73 if not arrow_itemstring then
74 arrow_itemstring = "mcl_throwing:arrow"
75 end
76 mcl_throwing.shoot_arrow(arrow_itemstring, {x=playerpos.x,y=playerpos.y+1.5,z=playerpos.z}, dir, yaw, player, power, damage)
77 return true
78 end
80 minetest.register_tool("mcl_throwing:bow", {
81 description = "Bow",
82 _doc_items_longdesc = "Bows are ranged weapons to shoot arrows at your foes.",
83 _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.
85 The arrow speed and damage increase the longer you charge. The minimum damage is 1. At full charge, the damage is 9 with a 20% chance of a critical hit dealing 10 damage instead.]],
86 _doc_items_durability = BOW_DURABILITY,
87 inventory_image = "mcl_throwing_bow.png",
88 stack_max = 1,
89 -- Trick to disable melee damage to entities.
90 -- Range not set to 0 (unlike the others) so it can be placed into item frames
91 range = 1,
92 -- Trick to disable digging as well
93 on_use = function() end,
94 groups = {weapon=1,weapon_ranged=1},
97 minetest.register_tool("mcl_throwing:bow_0", {
98 description = "Bow",
99 _doc_items_create_entry = false,
100 inventory_image = "mcl_throwing_bow_0.png",
101 stack_max = 1,
102 range = 0, -- Pointing range to 0 to prevent punching with bow :D
103 groups = {not_in_creative_inventory=1, not_in_craft_guide=1},
106 minetest.register_tool("mcl_throwing:bow_1", {
107 description = "Bow",
108 _doc_items_create_entry = false,
109 inventory_image = "mcl_throwing_bow_1.png",
110 stack_max = 1,
111 range = 0,
112 groups = {not_in_creative_inventory=1, not_in_craft_guide=1},
115 minetest.register_tool("mcl_throwing:bow_2", {
116 description = "Bow",
117 _doc_items_create_entry = false,
118 inventory_image = "mcl_throwing_bow_2.png",
119 stack_max = 1,
120 range = 0,
121 groups = {not_in_creative_inventory=1, not_in_craft_guide=1},
124 controls.register_on_release(function(player, key, time)
125 if key~="RMB" then return end
126 local inv = minetest.get_inventory({type="player", name=player:get_player_name()})
127 local wielditem = player:get_wielded_item()
128 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
129 local has_shot = false
131 local speed, damage
132 local charge = minetest.get_us_time() - bow_load[player:get_player_name()]
133 charge = math.max(math.min(charge, BOW_CHARGE_TIME_FULL), 0)
134 local charge_ratio = charge / BOW_CHARGE_TIME_FULL
135 charge_ratio = math.max(math.min(charge_ratio, 1), 0)
137 -- Calculate damage and speed
138 -- Fully charged
139 if charge >= BOW_CHARGE_TIME_FULL then
140 speed = BOW_MAX_SPEED
141 local r = math.random(1,5)
142 -- Damage and range have been nerfed because the arrow charges very quickly
143 if r == 1 then
144 -- 20% chance for critical hit
145 damage = 10
146 else
147 damage = 9
149 -- Partially charged
150 else
151 -- Linear speed and damage increase
152 speed = math.max(4, BOW_MAX_SPEED * charge_ratio)
153 damage = math.max(1, math.floor(9 * charge_ratio))
156 has_shot = player_shoot_arrow(wielditem, player, speed, damage)
158 wielditem:set_name("mcl_throwing:bow")
159 if has_shot and minetest.settings:get_bool("creative_mode") == false then
160 wielditem:add_wear(65535/BOW_DURABILITY)
162 player:set_wielded_item(wielditem)
163 bow_load[player:get_player_name()] = false
165 end)
167 controls.register_on_hold(function(player, key, time)
168 if key ~= "RMB" then
169 return
171 local name = player:get_player_name()
172 local inv = minetest.get_inventory({type="player", name=name})
173 local wielditem = player:get_wielded_item()
174 if wielditem:get_name()=="mcl_throwing:bow" and (minetest.settings:get_bool("creative_mode") or inv:contains_item("main", "mcl_throwing:arrow")) then
175 wielditem:set_name("mcl_throwing:bow_0")
176 bow_load[name] = minetest.get_us_time()
177 else
178 if type(bow_load[name]) == "number" then
179 if wielditem:get_name() == "mcl_throwing:bow_0" and minetest.get_us_time() - bow_load[name] >= BOW_CHARGE_TIME_HALF then
180 wielditem:set_name("mcl_throwing:bow_1")
181 elseif wielditem:get_name() == "mcl_throwing:bow_1" and minetest.get_us_time() - bow_load[name] >= BOW_CHARGE_TIME_FULL then
182 wielditem:set_name("mcl_throwing:bow_2")
184 else
185 wielditem:set_name("mcl_throwing:bow")
188 player:set_wielded_item(wielditem)
189 end)
191 minetest.register_globalstep(function(dtime)
192 for _, player in pairs(minetest.get_connected_players()) do
193 local wielditem = player:get_wielded_item()
194 local controls = player:get_player_control()
195 local inv = minetest.get_inventory({type="player", name = player:get_player_name()})
196 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
197 local list = inv:get_list("main")
198 for place, stack in pairs(list) do
199 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
200 stack:set_name("mcl_throwing:bow")
201 list[place] = stack
202 break
205 inv:set_list("main", list)
206 bow_load[player:get_player_name()] = false
209 end)
211 if minetest.get_modpath("mcl_core") and minetest.get_modpath("mcl_mobitems") then
212 minetest.register_craft({
213 output = 'mcl_throwing:bow',
214 recipe = {
215 {'', 'mcl_core:stick', 'mcl_mobitems:string'},
216 {'mcl_core:stick', '', 'mcl_mobitems:string'},
217 {'', 'mcl_core:stick', 'mcl_mobitems:string'},
220 minetest.register_craft({
221 output = 'mcl_throwing:bow',
222 recipe = {
223 {'mcl_mobitems:string', 'mcl_core:stick', ''},
224 {'mcl_mobitems:string', '', 'mcl_core:stick'},
225 {'mcl_mobitems:string', 'mcl_core:stick', ''},
230 minetest.register_craft({
231 type = "fuel",
232 recipe = "mcl_throwing:bow",
233 burntime = 15,
236 -- Add entry aliases for the Help
237 if minetest.get_modpath("doc") then
238 doc.add_entry_alias("tools", "mcl_throwing:bow", "tools", "mcl_throwing:bow_0")
239 doc.add_entry_alias("tools", "mcl_throwing:bow", "tools", "mcl_throwing:bow_1")
240 doc.add_entry_alias("tools", "mcl_throwing:bow", "tools", "mcl_throwing:bow_2")