2020-07-13 00:43:15 +02:00
|
|
|
--[[
|
|
|
|
Research N' Duplication
|
|
|
|
Copyright (C) 2020 Noodlemire
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Lesser General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
|
|
License along with this library; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
--]]
|
|
|
|
|
|
|
|
--Mod-specific global variable
|
|
|
|
projectile = {}
|
|
|
|
|
|
|
|
--A list of registered projectile entites, indexed by the category of weapon that uses them.
|
|
|
|
projectile.registered_projectiles = {}
|
|
|
|
|
|
|
|
--Per-player list of much a projectile weapon has been charged.
|
|
|
|
projectile.charge_levels = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--MP = Mod Path
|
|
|
|
local mp = minetest.get_modpath(minetest.get_current_modname())..'/'
|
|
|
|
|
2020-07-20 02:45:20 +02:00
|
|
|
--In here is every publicly available function that this mod uses.
|
|
|
|
dofile(mp.."api.lua")
|
|
|
|
|
2020-07-13 00:43:15 +02:00
|
|
|
--In here is the registration of ammo items that this mod provides, as well as crafting recipes for weapons and ammo.
|
|
|
|
dofile(mp.."crafts.lua")
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-07-20 02:45:20 +02:00
|
|
|
--A helper function to cancel a player's charge when necessary.
|
|
|
|
local function uncharge_player(player)
|
|
|
|
--Useful shorthand
|
|
|
|
local pname = player:get_player_name()
|
2020-07-13 00:43:15 +02:00
|
|
|
|
2020-07-20 02:45:20 +02:00
|
|
|
--If there is charge data...
|
|
|
|
if projectile.charge_levels[pname] then
|
|
|
|
--Get store the previous slot for possible use after charge data deletion.
|
|
|
|
local old_slot = projectile.charge_levels[pname].slot
|
|
|
|
--Get the projectile weapon. get_wielded_item() can't be used, since the weapon may no longer be held.
|
|
|
|
local wep = player:get_inventory():get_stack("main", old_slot)
|
2020-07-13 00:43:15 +02:00
|
|
|
|
2020-08-16 00:48:21 +02:00
|
|
|
--If the weapon was /pulverized or otherwise deleted without triggering a callback...
|
|
|
|
if wep:is_empty() then
|
|
|
|
--Delete the entry directly, which normally happens inside the weapon's on_use function.
|
|
|
|
projectile.charge_levels[pname] = nil
|
|
|
|
else
|
|
|
|
--Call the weapon's on_use function, which will cancel it.
|
|
|
|
wep:get_definition().on_use(wep, player, true)
|
|
|
|
end
|
2020-07-20 02:45:20 +02:00
|
|
|
|
|
|
|
--Update the player's inventory with any modifications.
|
|
|
|
player:get_inventory():set_stack("main", old_slot, wep)
|
|
|
|
end
|
2020-07-13 00:43:15 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
--Globalsteps are used to either cancel a charge if a player switches weapons, or to update the weapon sprite when charging is complete.
|
|
|
|
minetest.register_globalstep(function(dtime)
|
|
|
|
--For each player on the server...
|
|
|
|
for _, player in pairs(minetest.get_connected_players()) do
|
|
|
|
--Useful shorthand
|
|
|
|
local pname = player:get_player_name()
|
|
|
|
|
|
|
|
--If this player is currently charging a projectile weapon...
|
|
|
|
if projectile.charge_levels[pname] then
|
2020-08-16 00:48:21 +02:00
|
|
|
--If the player's selected hotbar slot changed or their weapon was deleted somehow...
|
|
|
|
if player:get_wield_index() ~= projectile.charge_levels[pname].slot or player:get_wielded_item():is_empty() then
|
2020-07-20 02:45:20 +02:00
|
|
|
--Cancel their charge.
|
|
|
|
uncharge_player(player)
|
2020-07-13 00:43:15 +02:00
|
|
|
|
|
|
|
--Otherwise, as long as the player doesn't change weapon...
|
|
|
|
else
|
|
|
|
--Add a little charge, according the how much time that has passed since the last globalstep.
|
|
|
|
projectile.charge_levels[pname].charge = projectile.charge_levels[pname].charge + dtime
|
|
|
|
|
|
|
|
--Get the charging weapon and its definition
|
|
|
|
local wep = player:get_wielded_item()
|
|
|
|
local def = wep:get_definition()
|
|
|
|
|
|
|
|
--if the weapon has a listed full_charge_name, meaning it hasn't already fully charged,
|
|
|
|
--but now the charge level has reached or exceeded the max...
|
|
|
|
if def.full_charge_name and projectile.charge_levels[pname].charge >= def.charge_time then
|
|
|
|
--Once this happens, replace the weapon with a fully charged sprite version.
|
|
|
|
wep:set_name(def.full_charge_name)
|
|
|
|
player:set_wielded_item(wep)
|
2020-08-16 00:48:21 +02:00
|
|
|
|
|
|
|
--Enable a callback that can occur when the weapon is fully charged
|
|
|
|
if def.on_charge_full then
|
|
|
|
def.on_charge_full(wep, player)
|
|
|
|
end
|
2020-07-13 00:43:15 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
--When a weapon is charging, it's a lot harder to check for when the stack has moved elsewhere, at least in terms of checking it.
|
|
|
|
--So, if a player tries any inventory action related to a charging projectile weapon, prevent it.
|
|
|
|
minetest.register_allow_player_inventory_action(function(player, action, inv, info)
|
|
|
|
if (action == "take" and minetest.get_item_group(info.stack:get_name(), "projectile_weapon") >= 2) or
|
|
|
|
(action == "put" and minetest.get_item_group(inv:get_stack(info.listname, info.index):get_name(), "projectile_weapon") >= 2) or
|
|
|
|
(action == "move" and
|
|
|
|
(minetest.get_item_group(inv:get_stack(info.from_list, info.from_index):get_name(), "projectile_weapon") >= 2 or
|
|
|
|
minetest.get_item_group(inv:get_stack(info.to_list, info.to_index):get_name(), "projectile_weapon") >= 2)) then
|
|
|
|
return 0
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
2020-07-20 02:45:20 +02:00
|
|
|
--If a player leaves, cancel their charge.
|
|
|
|
minetest.register_on_leaveplayer(function(player)
|
|
|
|
uncharge_player(player)
|
|
|
|
end)
|
2020-07-13 00:43:15 +02:00
|
|
|
|
2020-07-20 02:45:20 +02:00
|
|
|
--If a server is shutdown, cancel all charges.
|
|
|
|
minetest.register_on_shutdown(function()
|
|
|
|
for _, player in pairs(minetest.get_connected_players()) do
|
|
|
|
uncharge_player(player)
|
2020-07-13 00:43:15 +02:00
|
|
|
end
|
2020-07-20 02:45:20 +02:00
|
|
|
end)
|
|
|
|
|
2020-07-13 00:43:15 +02:00
|
|
|
|
|
|
|
|
|
|
|
--The basic slingshot. Slingshots are weaker than bows, but the ammunition they use is way easier to find and create.
|
|
|
|
projectile.register_weapon("projectile:slingshot", {
|
|
|
|
description = "Slingshot",
|
|
|
|
inventory_image = "projectile_slingshot.png",
|
|
|
|
inventory_image_2 = "projectile_slingshot_charged.png",
|
|
|
|
inventory_image_3 = "projectile_slingshot_charged_full.png",
|
|
|
|
durability = 75,
|
|
|
|
rw_category = "slingshot",
|
2020-07-20 02:45:20 +02:00
|
|
|
charge = true,
|
2020-08-16 00:48:21 +02:00
|
|
|
fire_while_charging = true,
|
|
|
|
|
|
|
|
on_charge_begin = function(wep, user)
|
|
|
|
projectile.charge_levels[user:get_player_name()].sound = minetest.sound_play("projectile_slingshot_drawn_adam-n", {gain = 1.0, object = user})
|
|
|
|
end,
|
|
|
|
|
|
|
|
after_fire = function(wep, user)
|
|
|
|
minetest.sound_play("projectile_bow_release_porkmuncher", {gain = 1.0, pos = user:get_pos()}, true)
|
|
|
|
end
|
2020-07-13 00:43:15 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
--An upgraded slingshot, which fires faster and harder, but is slightly harder to charge up. Metal wire is stiffer than string, after all.
|
|
|
|
projectile.register_weapon("projectile:steel_slingshot", {
|
|
|
|
description = "Steel Slingshot",
|
|
|
|
inventory_image = "projectile_steel_slingshot.png",
|
|
|
|
inventory_image_2 = "projectile_steel_slingshot_charged.png",
|
|
|
|
inventory_image_3 = "projectile_steel_slingshot_charged_full.png",
|
|
|
|
durability = 150,
|
|
|
|
rw_category = "slingshot",
|
|
|
|
charge = true,
|
2020-07-20 02:45:20 +02:00
|
|
|
fire_while_charging = true,
|
2020-07-13 00:43:15 +02:00
|
|
|
charge_time = 1.1,
|
|
|
|
damage = 1.25,
|
2020-08-16 00:48:21 +02:00
|
|
|
speed = 1.75,
|
|
|
|
|
|
|
|
on_charge_begin = function(wep, user)
|
|
|
|
projectile.charge_levels[user:get_player_name()].sound = minetest.sound_play("projectile_slingshot_drawn_adam-n", {gain = 1.0, object = user})
|
|
|
|
end,
|
|
|
|
|
|
|
|
after_fire = function(wep, user)
|
|
|
|
minetest.sound_play("projectile_bow_release_porkmuncher", {gain = 1.0, pos = user:get_pos()}, true)
|
|
|
|
end
|
2020-07-13 00:43:15 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
--The basic bow. It is more powerful than a slingshot, but it takes way longer to charge and ammunition is harder to get.
|
|
|
|
projectile.register_weapon("projectile:bow", {
|
|
|
|
description = "Bow",
|
|
|
|
inventory_image = "projectile_bow.png",
|
|
|
|
inventory_image_2 = "projectile_bow_charged.png",
|
|
|
|
inventory_image_3 = "projectile_bow_charged_full.png",
|
|
|
|
durability = 100,
|
|
|
|
rw_category = "bow",
|
|
|
|
charge = true,
|
2020-07-20 02:45:20 +02:00
|
|
|
fire_while_charging = true,
|
2020-08-16 00:48:21 +02:00
|
|
|
charge_time = 2,
|
|
|
|
|
|
|
|
on_charge_begin = function(wep, user)
|
|
|
|
projectile.charge_levels.sound = minetest.sound_play("projectile_bow_drawn_paveroux", {gain = 1.0, object = user})
|
|
|
|
end,
|
|
|
|
|
|
|
|
after_fire = function(wep, user)
|
|
|
|
minetest.sound_play("projectile_bow_release_porkmuncher", {gain = 1.0, pos = user:get_pos()}, true)
|
|
|
|
end
|
2020-07-13 00:43:15 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
--An upgraded bow, which fires faster and harder, but is slightly harder to charge up. Metal wire is stiffer than string, after all.
|
|
|
|
projectile.register_weapon("projectile:steel_bow", {
|
|
|
|
description = "Steel Bow",
|
|
|
|
inventory_image = "projectile_steel_bow.png",
|
|
|
|
inventory_image_2 = "projectile_steel_bow_charged.png",
|
|
|
|
inventory_image_3 = "projectile_steel_bow_charged_full.png",
|
|
|
|
durability = 200,
|
|
|
|
rw_category = "bow",
|
|
|
|
charge = true,
|
2020-07-20 02:45:20 +02:00
|
|
|
fire_while_charging = true,
|
2020-07-13 00:43:15 +02:00
|
|
|
charge_time = 2.1,
|
|
|
|
damage = 1.5,
|
2020-08-16 00:48:21 +02:00
|
|
|
speed = 1.9,
|
|
|
|
|
|
|
|
on_charge_begin = function(wep, user)
|
|
|
|
projectile.charge_levels[user:get_player_name()].sound = minetest.sound_play("projectile_bow_drawn_paveroux", {gain = 1.0, object = user})
|
|
|
|
end,
|
|
|
|
|
|
|
|
after_fire = function(wep, user)
|
|
|
|
minetest.sound_play("projectile_bow_release_porkmuncher", {gain = 1.0, pos = user:get_pos()}, true)
|
|
|
|
end
|
2020-07-13 00:43:15 +02:00
|
|
|
})
|
|
|
|
|
2020-07-20 02:45:20 +02:00
|
|
|
--The basic flintlock weapon, which can fire fairly often and has fair damage, but can't be fired before it is fully loaded.
|
|
|
|
projectile.register_weapon("projectile:flintlock_pistol", {
|
|
|
|
description = "Flintlock Pistol",
|
|
|
|
inventory_image = "projectile_flintlock_pistol.png",
|
|
|
|
inventory_image_2 = "projectile_flintlock_pistol.png",
|
|
|
|
inventory_image_3 = "projectile_flintlock_pistol_charged.png",
|
|
|
|
durability = 250,
|
|
|
|
rw_category = "flintlock",
|
|
|
|
charge = true,
|
|
|
|
charge_time = 0.667,
|
2020-07-13 00:43:15 +02:00
|
|
|
|
2020-07-20 02:45:20 +02:00
|
|
|
can_fire = projectile.needs_gunpowder,
|
2020-08-16 00:48:21 +02:00
|
|
|
on_cancel = projectile.return_gunpowder,
|
|
|
|
|
|
|
|
on_charge_begin = function(wep, user)
|
|
|
|
minetest.sound_play("projectile_metal_click_mkoenig", {gain = 1.0, pos = user:get_pos()}, true)
|
|
|
|
end,
|
|
|
|
|
|
|
|
on_charge_full = function(wep, user)
|
|
|
|
minetest.sound_play("projectile_metal_click_mkoenig", {gain = 1.0, pos = user:get_pos()}, true)
|
|
|
|
end,
|
|
|
|
|
|
|
|
after_fire = function(wep, user)
|
|
|
|
minetest.sound_play("projectile_musket_shot_aaronsiler", {gain = 0.75, pos = user:get_pos(), max_hear_distance = 48}, true)
|
|
|
|
end,
|
2020-07-20 02:45:20 +02:00
|
|
|
})
|
2020-07-13 00:43:15 +02:00
|
|
|
|
2020-07-20 02:45:20 +02:00
|
|
|
--A slowler, more powerful flintlock weapon.
|
|
|
|
projectile.register_weapon("projectile:musket", {
|
|
|
|
description = "Musket",
|
|
|
|
inventory_image = "projectile_musket.png",
|
|
|
|
inventory_image_2 = "projectile_musket.png",
|
|
|
|
inventory_image_3 = "projectile_musket_charged.png",
|
|
|
|
durability = 300,
|
|
|
|
rw_category = "flintlock",
|
|
|
|
charge = true,
|
|
|
|
charge_time = 1.333,
|
|
|
|
damage = 1.5,
|
|
|
|
speed = 1.1,
|
2020-07-13 00:43:15 +02:00
|
|
|
|
2020-07-20 02:45:20 +02:00
|
|
|
can_fire = projectile.needs_gunpowder,
|
2020-08-16 00:48:21 +02:00
|
|
|
on_cancel = projectile.return_gunpowder,
|
|
|
|
|
|
|
|
on_charge_begin = function(wep, user)
|
|
|
|
minetest.sound_play("projectile_metal_click_mkoenig", {gain = 1.0, pos = user:get_pos()}, true)
|
|
|
|
end,
|
|
|
|
|
|
|
|
on_charge_full = function(wep, user)
|
|
|
|
minetest.sound_play("projectile_metal_click_mkoenig", {gain = 1.0, pos = user:get_pos()}, true)
|
|
|
|
end,
|
|
|
|
|
|
|
|
after_fire = function(wep, user)
|
|
|
|
minetest.sound_play("projectile_musket_shot_aaronsiler", {gain = 1.0, pos = user:get_pos(), max_hear_distance = 96}, true)
|
|
|
|
end,
|
2020-07-20 02:45:20 +02:00
|
|
|
})
|
2020-07-13 00:43:15 +02:00
|
|
|
|
2020-07-20 02:45:20 +02:00
|
|
|
--A flintlock weapon that fires bursts of shot, rather than individual musket balls.
|
|
|
|
projectile.register_weapon("projectile:blunderbuss", {
|
|
|
|
description = "Blunderbuss",
|
|
|
|
inventory_image = "projectile_blunderbuss.png",
|
|
|
|
inventory_image_2 = "projectile_blunderbuss.png",
|
|
|
|
inventory_image_3 = "projectile_blunderbuss_charged.png",
|
|
|
|
durability = 250,
|
|
|
|
rw_category = "flintlock_shot",
|
|
|
|
charge = true,
|
|
|
|
charge_time = 1,
|
2020-07-13 00:43:15 +02:00
|
|
|
|
2020-07-20 02:45:20 +02:00
|
|
|
can_fire = projectile.needs_gunpowder,
|
2020-08-16 00:48:21 +02:00
|
|
|
on_cancel = projectile.return_gunpowder,
|
|
|
|
|
|
|
|
on_charge_begin = function(wep, user)
|
|
|
|
minetest.sound_play("projectile_metal_click_mkoenig", {gain = 1.0, pos = user:get_pos()}, true)
|
|
|
|
end,
|
|
|
|
|
|
|
|
on_charge_full = function(wep, user)
|
|
|
|
minetest.sound_play("projectile_metal_click_mkoenig", {gain = 1.0, pos = user:get_pos()}, true)
|
|
|
|
end,
|
|
|
|
|
|
|
|
after_fire = function(wep, user)
|
|
|
|
minetest.sound_play("projectile_musket_shot_aaronsiler", {gain = 1.0, pos = user:get_pos(), max_hear_distance = 96}, true)
|
|
|
|
end,
|
2020-07-20 02:45:20 +02:00
|
|
|
})
|
2020-07-13 00:43:15 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
2020-08-06 04:20:34 +02:00
|
|
|
--The basic slingshot projectile: rocks, optionally the ones from hardtrees
|
|
|
|
if hardtrees then
|
|
|
|
projectile.register_projectile("projectile:rock", "slingshot", "hardtrees:rock", {
|
|
|
|
image = "rock_lump.png",
|
|
|
|
damage = 5,
|
|
|
|
speed = 15
|
|
|
|
})
|
|
|
|
else
|
|
|
|
projectile.register_projectile("projectile:rock", "slingshot", "projectile:rock", {
|
|
|
|
image = "projectile_rock.png",
|
|
|
|
damage = 5,
|
|
|
|
speed = 15
|
|
|
|
})
|
|
|
|
end
|
2020-07-13 00:43:15 +02:00
|
|
|
|
|
|
|
--A helper function for mese projectiles, to check if a particular node can be powered.
|
|
|
|
local is_mesecon = function(pos)
|
|
|
|
local def = minetest.registered_nodes[minetest.get_node(pos).name]
|
|
|
|
|
|
|
|
return def and def.mesecons
|
|
|
|
end
|
|
|
|
|
|
|
|
--Mese projectiles for slingshots, which have medium power and can be used to power mesecon effectors.
|
|
|
|
projectile.register_projectile("projectile:mese", "slingshot", "default:mese_crystal_fragment", {
|
|
|
|
image = "default_mese_crystal_fragment.png",
|
|
|
|
damage = 7,
|
|
|
|
speed = 20,
|
|
|
|
|
|
|
|
--When a mese crystal fragment hits something...
|
|
|
|
on_impact = function(self, collisions)
|
|
|
|
--If the mesecon mod is loaded...
|
|
|
|
if mesecon then
|
|
|
|
--For each collided thing...
|
|
|
|
for _, c in pairs(collisions) do
|
|
|
|
--If the thing is a node and can be powered...
|
|
|
|
if c.type == "node" and is_mesecon(c.node_pos) then
|
|
|
|
--Grab data about the node.
|
|
|
|
local node = minetest.get_node(c.node_pos)
|
|
|
|
|
|
|
|
--As long as it isn't already powered...
|
|
|
|
if not mesecon.is_powered(c.node_pos) then
|
|
|
|
--Activate the node.
|
|
|
|
mesecon.activate(c.node_pos, node, nil, 0)
|
|
|
|
--Then, after 1/4 of the second, deactivate it.
|
|
|
|
minetest.after(0.25, function() mesecon.deactivate(c.node_pos, node, nil, 0) end)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
})
|
|
|
|
|
|
|
|
--Obsidian shards are the strongest slingshot projectile.
|
|
|
|
projectile.register_projectile("projectile:obsidian", "slingshot", "default:obsidian_shard", {
|
|
|
|
image = "default_obsidian_shard.png",
|
|
|
|
damage = 9,
|
|
|
|
speed = 25
|
|
|
|
})
|
|
|
|
|
|
|
|
--The basic arrow, which has twice the power of a rock.
|
|
|
|
projectile.register_projectile("projectile:arrow", "bow", "projectile:arrow", {
|
|
|
|
damage = 10,
|
|
|
|
speed = 30,
|
|
|
|
|
|
|
|
initial_properties = {
|
|
|
|
visual = "mesh",
|
|
|
|
mesh = "projectile_arrow.obj",
|
|
|
|
textures = {"projectile_arrow_texture.png"}
|
|
|
|
},
|
|
|
|
|
2020-07-20 02:45:20 +02:00
|
|
|
_on_step = function(self, dtime)
|
|
|
|
projectile.autorotate_arrow(self, dtime)
|
|
|
|
|
|
|
|
if fire then
|
|
|
|
local selfo = self.object
|
|
|
|
local node = minetest.get_node(selfo:get_pos())
|
|
|
|
|
|
|
|
if minetest.get_item_group(node.name, "lava") > 0 or minetest.get_item_group(node.name, "fire") > 0 then
|
|
|
|
local arrow = minetest.add_entity(selfo:get_pos(), "projectile:arrow_fire")
|
|
|
|
local arrowlua = arrow:get_luaentity()
|
|
|
|
|
|
|
|
arrow:set_velocity(selfo:get_velocity())
|
|
|
|
arrow:set_acceleration(selfo:get_acceleration())
|
|
|
|
arrow:set_rotation(selfo:get_rotation())
|
|
|
|
|
|
|
|
arrowlua.level = self.level
|
|
|
|
arrowlua.damage = 12
|
|
|
|
arrowlua.owner = self.owner
|
|
|
|
arrowlua.oldvel = self.oldvel
|
|
|
|
arrowlua.timer = self.timer
|
|
|
|
|
|
|
|
selfo:remove()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2020-07-13 00:43:15 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
--If the fire mod is present...
|
|
|
|
if fire then
|
|
|
|
--Register arrows that can combust flammable terrain.
|
|
|
|
projectile.register_projectile("projectile:arrow_fire", "bow", "projectile:arrow_fire", {
|
|
|
|
damage = 12,
|
|
|
|
speed = 35,
|
|
|
|
|
|
|
|
initial_properties = {
|
|
|
|
visual = "mesh",
|
|
|
|
mesh = "projectile_arrow.obj",
|
|
|
|
textures = {"projectile_arrow_fire_texture.png"}
|
|
|
|
},
|
|
|
|
|
2020-07-20 02:45:20 +02:00
|
|
|
_on_step = function(self, dtime)
|
|
|
|
projectile.autorotate_arrow(self, dtime)
|
|
|
|
|
|
|
|
local selfo = self.object
|
|
|
|
local node = minetest.get_node(selfo:get_pos())
|
|
|
|
|
|
|
|
if minetest.get_item_group(node.name, "water") > 0 then
|
|
|
|
local arrow = minetest.add_entity(selfo:get_pos(), "projectile:arrow")
|
|
|
|
local arrowlua = arrow:get_luaentity()
|
|
|
|
|
|
|
|
arrow:set_velocity(selfo:get_velocity())
|
|
|
|
arrow:set_acceleration(selfo:get_acceleration())
|
|
|
|
arrow:set_rotation(selfo:get_rotation())
|
|
|
|
|
|
|
|
arrowlua.level = self.level
|
|
|
|
arrowlua.damage = 10
|
|
|
|
arrowlua.owner = self.owner
|
|
|
|
arrowlua.oldvel = self.oldvel
|
|
|
|
arrowlua.timer = self.timer
|
|
|
|
|
|
|
|
selfo:remove()
|
|
|
|
end
|
|
|
|
end,
|
2020-07-13 00:43:15 +02:00
|
|
|
|
|
|
|
--On impact...
|
|
|
|
on_impact = function(self, collisions)
|
|
|
|
--For each collision...
|
|
|
|
for _, c in pairs(collisions) do
|
|
|
|
--For each flammable node it hit...
|
|
|
|
if c.type == "node" and minetest.get_item_group(minetest.get_node(c.node_pos).name, "flammable") > 0 then
|
|
|
|
--Replace that node with fire.
|
|
|
|
minetest.set_node(c.node_pos, {name = "fire:basic_flame"})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
--Arrows made from gold, which have super high velocity and good damage.
|
|
|
|
projectile.register_projectile("projectile:arrow_high_velocity", "bow", "projectile:arrow_high_velocity", {
|
|
|
|
damage = 15,
|
|
|
|
speed = 70,
|
|
|
|
|
|
|
|
initial_properties = {
|
|
|
|
visual = "mesh",
|
|
|
|
mesh = "projectile_arrow.obj",
|
|
|
|
textures = {"projectile_arrow_high_velocity_texture.png"}
|
|
|
|
},
|
|
|
|
|
2020-07-20 02:45:20 +02:00
|
|
|
_on_step = projectile.autorotate_arrow
|
2020-07-13 00:43:15 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
--If the tnt mod is present...
|
|
|
|
if tnt then
|
|
|
|
--Register arrows that explode upon contact with anything.
|
|
|
|
projectile.register_projectile("projectile:arrow_bomb", "bow", "projectile:arrow_bomb", {
|
|
|
|
--Instead of dealing direct damage, bomb arrows rely on the explosion to deal damage.
|
|
|
|
damage = 0,
|
|
|
|
--Also, it's not like fat sticks of tnt are particularly aerodynamic.
|
|
|
|
speed = 25,
|
|
|
|
|
|
|
|
initial_properties = {
|
|
|
|
visual = "mesh",
|
|
|
|
mesh = "projectile_arrow_bomb.obj",
|
|
|
|
textures = {"projectile_arrow_bomb_texture.png"}
|
|
|
|
},
|
|
|
|
|
2020-07-20 02:45:20 +02:00
|
|
|
_on_step = projectile.autorotate_arrow,
|
2020-07-13 00:43:15 +02:00
|
|
|
|
|
|
|
--Upon impact, create a small explosion.
|
|
|
|
on_impact = function(self, collisions)
|
2020-07-26 01:05:58 +02:00
|
|
|
local pos = self.object:get_pos()
|
|
|
|
|
2020-08-16 00:51:54 +02:00
|
|
|
tnt.boom(pos, {radius = 2, damage_radius = 2})
|
2020-07-26 01:05:58 +02:00
|
|
|
|
2020-08-16 00:51:54 +02:00
|
|
|
if node_damage then
|
|
|
|
local r = 4
|
2020-07-26 01:05:58 +02:00
|
|
|
|
2020-08-16 00:51:54 +02:00
|
|
|
for a = -r, r do
|
|
|
|
for b = -r, r do
|
|
|
|
for c = -r, r do
|
|
|
|
local npos = vector.add(pos, {x=a, y=b, z=c})
|
|
|
|
local dist = vector.distance(pos, npos)
|
|
|
|
local num = math.random(0, math.ceil(r / math.max(1, dist)))
|
|
|
|
|
|
|
|
if dist <= r and num > 0 then
|
|
|
|
node_damage.damage(npos, nil, nil, num)
|
|
|
|
end
|
2020-07-26 01:05:58 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2020-07-13 00:43:15 +02:00
|
|
|
end
|
|
|
|
})
|
|
|
|
end
|
2020-07-20 02:45:20 +02:00
|
|
|
|
|
|
|
--Basic flintlock ammunition with decent damage, and extremely high speed, and slight spread.
|
|
|
|
projectile.register_projectile("projectile:musket_ball", "flintlock", "projectile:musket_ball", {
|
2020-07-20 02:51:56 +02:00
|
|
|
damage = 15,
|
2020-07-20 02:45:20 +02:00
|
|
|
speed = 250,
|
|
|
|
spread = 2.5,
|
|
|
|
|
|
|
|
image = "projectile_dot.png^[multiply:#AEAEAE"
|
|
|
|
})
|
|
|
|
|
|
|
|
--A weaker flintlock ammunition that bursts on impact, striking all enemies in a small radius.
|
|
|
|
--Note that damage of the initial hit is set to 0 so that the radius damage doesn't cause this bullet to hit the same enemy twice.
|
|
|
|
projectile.register_projectile("projectile:musket_ball_diamond", "flintlock", "projectile:musket_ball_diamond", {
|
|
|
|
damage = 0,
|
|
|
|
speed = 275,
|
|
|
|
spread = 5,
|
|
|
|
|
|
|
|
image = "projectile_dot.png^[multiply:#5AAFE7",
|
|
|
|
|
|
|
|
on_impact = function(self, collisions)
|
|
|
|
--Center the blast on the first node or object that the projectile hit.
|
|
|
|
local pos = collisions[1].node_pos or collisions[1].object:get_pos()
|
|
|
|
|
|
|
|
--For each object in a radius of 2.5 meters/nodes...
|
|
|
|
for _, target in pairs(minetest.get_objects_inside_radius(pos, 2.5)) do
|
|
|
|
--As long as this wouldn't be self-damage or friendly fire...
|
|
|
|
if not target:is_player() or (self.owner ~= target:get_player_name() and projectile.in_same_party(self, target)) then
|
2020-07-20 02:51:56 +02:00
|
|
|
--Punch that target for 9 damage.
|
2020-07-20 02:45:20 +02:00
|
|
|
--The direction is just 0, so affected targets won't be knocked anywhere.
|
|
|
|
--Instead, they'll just freeze in place for a moment.
|
2020-07-20 02:51:56 +02:00
|
|
|
target:punch(self.object, 1, {full_punch_interval = 1, damage_groups = {fleshy = 9}}, {x=0, y=0, z=0})
|
2020-07-20 02:45:20 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--Then, create 16 little particles.
|
|
|
|
for i = 1, 16 do
|
|
|
|
--ps = particle speed.
|
|
|
|
local ps = 25
|
|
|
|
--Make a completely random velocity for each particle.
|
|
|
|
local vel = {x = math.random(-ps, ps), y = math.random(-ps, ps), z = math.random(-ps, ps)}
|
|
|
|
--Multiply and normalize are used so that every particle has exactly 25 velocity
|
|
|
|
vel = vector.multiply(vector.normalize(vel), ps)
|
|
|
|
|
|
|
|
--Add the particle so that it lasts only long enough to travel 2.5 meters. Have it look like a smaller version of the bullet fired.
|
|
|
|
minetest.add_particle(pos, vel, {x=0, y=0, z=0}, 2.5 / ps, 1, false, "projectile_dot.png^[multiply:#5AAFE7")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
})
|
|
|
|
|
2020-07-20 02:51:56 +02:00
|
|
|
--An upgrade to the musket ball that deal a lot more damage and is slightly faster and more accurate.
|
2020-07-20 02:45:20 +02:00
|
|
|
projectile.register_projectile("projectile:musket_ball_mithril", "flintlock", "projectile:musket_ball_mithril", {
|
2020-07-20 02:51:56 +02:00
|
|
|
damage = 24,
|
2020-07-20 02:45:20 +02:00
|
|
|
speed = 300,
|
|
|
|
spread = 2,
|
|
|
|
|
|
|
|
image = "projectile_dot.png^[multiply:#8282D5"
|
|
|
|
})
|
|
|
|
|
|
|
|
--A standard shotgun blast, with smaller than average pellets that basically tickle enemies if only one or two land.
|
|
|
|
projectile.register_projectile("projectile:shot_pile", "flintlock_shot", "projectile:shot_pile", {
|
2020-07-20 02:51:56 +02:00
|
|
|
damage = 4,
|
2020-07-20 02:45:20 +02:00
|
|
|
speed = 250,
|
|
|
|
count = 9,
|
|
|
|
spread = 22.5,
|
|
|
|
collide_self = false,
|
|
|
|
|
|
|
|
initial_properties = {
|
|
|
|
collisionbox = {-.125, 0, -.125, .125, .25, .125},
|
|
|
|
visual_size = {x = 0.25, y = 0.25, z = 0.25}
|
|
|
|
},
|
|
|
|
|
|
|
|
image = "projectile_dot.png^[multiply:#AEAEAE"
|
|
|
|
})
|
|
|
|
|
|
|
|
--A massive burst of shot that deals less damage at range, but much more damage up close. Has a lot wider spread as well.
|
|
|
|
projectile.register_projectile("projectile:shot_pile_diamond", "flintlock_shot", "projectile:shot_pile_diamond", {
|
2020-07-20 02:51:56 +02:00
|
|
|
damage = 3,
|
2020-07-20 02:45:20 +02:00
|
|
|
speed = 275,
|
|
|
|
count = 18,
|
|
|
|
spread = 30,
|
|
|
|
collide_self = false,
|
|
|
|
|
|
|
|
initial_properties = {
|
|
|
|
collisionbox = {-.125, 0, -.125, .125, .25, .125},
|
|
|
|
visual_size = {x = 0.25, y = 0.25, z = 0.25}
|
|
|
|
},
|
|
|
|
|
|
|
|
image = "projectile_dot.png^[multiply:#5AAFE7"
|
|
|
|
})
|
|
|
|
|
|
|
|
--Stronger shot than steel with nearly double the damage and slightly better speed as well.
|
|
|
|
projectile.register_projectile("projectile:shot_pile_mithril", "flintlock_shot", "projectile:shot_pile_mithril", {
|
2020-07-20 02:51:56 +02:00
|
|
|
damage = 6,
|
2020-07-20 02:45:20 +02:00
|
|
|
speed = 300,
|
|
|
|
count = 9,
|
|
|
|
spread = 22.5,
|
|
|
|
collide_self = false,
|
|
|
|
|
|
|
|
initial_properties = {
|
|
|
|
collisionbox = {-.125, 0, -.125, .125, .25, .125},
|
|
|
|
visual_size = {x = 0.25, y = 0.25, z = 0.25}
|
|
|
|
},
|
|
|
|
|
|
|
|
image = "projectile_dot.png^[multiply:#8282D5"
|
|
|
|
})
|