diff --git a/README.md b/README.md index 87fdcf1..81837d9 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,14 @@ Mods based on this API: The settings are the following: ``` -# Movement parameters -throwing.velocity_factor = 19 -throwing.horizontal_acceleration_factor = -3 +# Trajectory parameters throwing.vertical_acceleration = -10 +throwing.realistic_trajectory = false +throwing.frictional_coefficient = -.5 + +# How the initial velocity of arrows is computed: simple, strength, or momentum +throwing.velocity_mode = strength +throwing.velocity_factor = 19 # Whether to allow placing an arrow as a node throwing.allow_arrow_placing = false @@ -28,6 +32,22 @@ throwing.bow_cooldown = 0.2 throwing.toolranks = true ``` +### Trajectory parameters + +By default, the trajectory of the arrow is a simple parabola. You can set the vertical acceleration (acceleration of gravity) using `throwing.vertical_acceleration`. + +If you want a more realistic trajectory that uses a first-order modelling of air friction, you can set `throwing.realistic_trajectory` to true. In this mode, `throwing.frictional_coefficient` indicates the ratio between the friction force on the arrow and its velocity. It should be negative. The acceleration of the arrow is subsequently determined by dividing the force by the "mass" of the arrow, the reference mass of 1 being the mass of a simple steel arrow. Generally, the frictional coefficient should be quite small; a value of -1 will already drastically shorten the range of the arrow. The default is -0.5. + +### Initial velocity computation + +The mod provides three modes to compute the initial speed of an arrow: simple, strength or strengthmass. + +In simple mode, the initial velocity of the arrow is always the same. The `throwing.velocity_factor` contains the value of this velocity. + +In strength mode (the default), the initial velocity of the arrow only depends on the bow that is used---the more expensive the bow, the faster the arrow. The arrow strength is multiplied by the velocity factor to compute the speed of the arrow, an arrow strength of 1 is about the strength of the steel bow in `throwing_arrows`. + +Finally, momentum mode is the most realistic. It computes the velocity of the arrow based on the bow strength, as in the strength mode, and on the mass of the arrow: the heavier the arrow, the slower it will be shot. It is called the momentum mode because, in this mode, the strength of a bow indicates the initial momentum of the arrow rather than its initial speed. + ## API There are two available functions in the mod API: diff --git a/init.lua b/init.lua index 26eca8b..4656126 100644 --- a/init.lua +++ b/init.lua @@ -28,6 +28,22 @@ function throwing.spawn_arrow_entity(pos, arrow, player) end end +local function apply_realistic_acceleration(obj, mass) + if not minetest.settings:get_bool("throwing.realistic_trajectory", false) then + return + end + + local vertical_acceleration = tonumber(minetest.settings:get("throwing.vertical_acceleration")) or -10 + local friction_coef = tonumber(minetest.settings:get("throwing.frictional_coefficient")) or -3 + + local velocity = obj:get_velocity() + obj:set_acceleration({ + x = friction_coef * velocity.x / mass, + y = friction_coef * velocity.y / mass + vertical_acceleration, + z = friction_coef * velocity.z / mass + }) +end + local function shoot_arrow(def, toolranks_data, player, bow_index, throw_itself, new_stack) local inventory = player:get_inventory() local arrow_index @@ -65,14 +81,29 @@ local function shoot_arrow(def, toolranks_data, player, bow_index, throw_itself, end local dir = player:get_look_dir() - local velocity_factor = tonumber(minetest.settings:get("throwing.velocity_factor")) or 19 - local horizontal_acceleration_factor = tonumber(minetest.settings:get("throwing.horizontal_acceleration_factor")) or -3 local vertical_acceleration = tonumber(minetest.settings:get("throwing.vertical_acceleration")) or -10 + local velocity_factor = tonumber(minetest.settings:get("throwing.velocity_factor")) or 19 + local velocity_mode = minetest.settings:get("throwing.velocity_mode") or "strength" - obj:set_velocity({x=dir.x*velocity_factor, y=dir.y*velocity_factor, z=dir.z*velocity_factor}) - obj:set_acceleration({x=dir.x*horizontal_acceleration_factor, y=vertical_acceleration, z=dir.z*horizontal_acceleration_factor}) + local velocity + if velocity_mode == "simple" then + velocity = velocity_factor + elseif velocity_mode == "momentum" then + velocity = def.strength * velocity_factor / luaentity.mass + else + velocity = def.strength * velocity_factor + end + + obj:set_velocity({ + x = dir.x * velocity, + y = dir.y * velocity, + z = dir.z * velocity + }) + obj:set_acceleration({x = 0, y = vertical_acceleration, z = 0}) obj:set_yaw(player:get_look_horizontal()-math.pi/2) + apply_realistic_acceleration(obj, luaentity.mass) + if luaentity.on_throw_sound ~= "" then minetest.sound_play(luaentity.on_throw_sound or "throwing_sound", {pos=playerpos, gain = 0.5}) end @@ -224,6 +255,8 @@ function throwing.arrow_step(self, dtime) wielded_light.update_light_by_item(self.item, self.object:get_pos()) end + apply_realistic_acceleration(self.object, self.mass) -- Physics: air friction + self.last_pos = pos -- Used by the build arrow end @@ -256,6 +289,7 @@ function throwing.register_arrow(name, def) if not def.groups.dig_immediate then def.groups.dig_immediate = 3 end + def.inventory_image = def.tiles[1] def.on_place = function(itemstack, placer, pointed_thing) if minetest.settings:get_bool("throwing.allow_arrow_placing") and pointed_thing.above then @@ -313,6 +347,7 @@ function throwing.register_arrow(name, def) on_hit_fails = def.on_hit_fails, on_step = throwing.arrow_step, item = name, + mass = def.mass or 1, }) end @@ -322,6 +357,7 @@ function throwing.register_bow(name, def) local enable_toolranks = use_toolranks and not def.no_toolranks def.name = name + if not def.allow_shot then def.allow_shot = function(player, itemstack, index) if index >= player:get_inventory():get_size("main") and not def.throw_itself then @@ -330,9 +366,15 @@ function throwing.register_bow(name, def) return throwing.is_arrow(itemstack) or def.throw_itself end end + if not def.inventory_image then def.inventory_image = def.texture end + + if not def.strength then + def.strength = 20 + end + def.on_use = function(itemstack, user, pointed_thing) -- Cooldown local meta = itemstack:get_meta() @@ -397,9 +439,11 @@ function throwing.register_bow(name, def) end) return itemstack end + if enable_toolranks then def.original_description = def.original_description or def.description def.description = toolranks.create_description(def.description, 0, 1) end + minetest.register_tool(name, def) end