diff --git a/LICENSE.txt b/LICENSE.txt index 55a2b42..072500f 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,15 +1,17 @@ Minetest Mod - Simple Shooter [shooter] ======================================= -License Source: 2013 Stuart Jones - LGPL +License Source Code: 2013 Stuart Jones - LGPL v2.1 License Textures: Stuart Jones - WTFPL +Licence Models: Stuart Jones - CC-BY-SA 3.0 + License Sounds: freesound.org flobert1_20070728.wav by Nonoo - Attribution 3.0 Unported (CC BY 3.0) - shot.wav by Sergenious - Attribution 3.0 Unported (CC BY 3.0) + shot.wav by Sergenious - Attribution 3.0 Unported (CC BY 3.0) GUNSHOT.WAV by erkanozan - CC0 1.0 Universal (CC0 1.0) @@ -17,3 +19,9 @@ License Sounds: freesound.org trigger-with-hammer-fall.wav by Nanashi - CC0 1.0 Universal (CC0 1.0) + woosh.wav by ReadeOnly - CC0 1.0 Universal (CC0 1.0) + + AGM-114 Hellfire Rocket Missile Launch.flac by qubodup - CC0 1.0 Universal (CC0 1.0) + + Sparkler.aif by Ned Bouhalassa - CC0 1.0 Universal (CC0 1.0) + diff --git a/README.txt b/README.txt index 064cd5b..15d8edc 100644 --- a/README.txt +++ b/README.txt @@ -1,10 +1,12 @@ Minetest Mod - Simple Shooter [shooter] ======================================= -Mod Version: 0.4.1 +Mod Version: 0.5.0 Minetest Version: 0.4.9 +Depends: default, wool, tnt + An experimental first person shooter mod that uses simple vector mathematics to produce an accurate and server-firendly method of hit detection. @@ -23,6 +25,8 @@ Crafting S = Steel Ingot [default:steel_ingot] B = Bronze Ingot [default:bronze_ingot] M = Mese Crystal [default:mese_crysytal] +D = Diamond [default:diamond] +R = Red Wool [wool:red] G = Gun Powder [tnt:gunpowder] Pistol: [shooter:pistol] @@ -69,3 +73,65 @@ Ammo Pack: [shooter:ammo] | G | B | +---+---+ +Grappling Hook: [shooter:grapple_hook] + ++---+---+---+ +| S | S | D | ++---+---+---+ +| S | S | | ++---+---+---+ +| D | | S | ++---+---+---+ + +Grappling Hook Gun: [shooter:grapple_gun] + ++---+---+ +| S | S | ++---+---+ +| | D | ++---+---+ + +Flare: [shooter:flare] + ++---+---+ +| G | R | ++---+---+ + +Flare Gun: [shooter:flaregun] + ++---+---+---+ +| R | R | R | ++---+---+---+ +| | | S | ++---+---+---+ + +Grenade: [shooter:grenade] + ++---+---+ +| G | S | ++---+---+ + +Flare Gun: [shooter:rocket_gun] + ++---+---+---+ +| B | S | S | ++---+---+---+ +| | | D | ++---+---+---+ + +Rocket: [shooter:rocket] + ++---+---+---+ +| B | G | B | ++---+---+---+ + +Turret: [shooter:turret] + ++---+---+---+ +| B | B | S | ++---+---+---+ +| | B | S | ++---+---+---+ +| | D | | ++---+---+---+ + diff --git a/depends.txt b/depends.txt index 845d8d2..558536a 100644 --- a/depends.txt +++ b/depends.txt @@ -1,2 +1,3 @@ default +wool tnt diff --git a/flaregun.lua b/flaregun.lua new file mode 100644 index 0000000..6904d3e --- /dev/null +++ b/flaregun.lua @@ -0,0 +1,147 @@ +minetest.register_craftitem("shooter:flare", { + description = "Flare", + inventory_image = "shooter_flare_inv.png", +}) + +minetest.register_node("shooter:flare_light", { + drawtype = "glasslike", + tiles = {"shooter_flare_light.png"}, + paramtype = "light", + groups = {not_in_creative_inventory=1}, + drop = "", + walkable = false, + buildable_to = true, + sunlight_propagates = true, + light_source = LIGHT_MAX, + pointable = false, +}) + +minetest.register_abm({ + nodenames = "shooter:flare_light", + interval = 5, + chance = 1, + action = function(pos, node) + local time = os.time() + local meta = minetest.get_meta(pos) + local init_time = meta:get_int("init_time") or 0 + if time > init_time + 30 then + local id = meta:get_int("particle_id") + if id then + minetest.delete_particlespawner(id) + end + minetest.remove_node(pos) + end + end, +}) + +minetest.register_entity("shooter:flare_entity", { + physical = true, + timer = 0, + visual = "cube", + visual_size = {x=1/8, y=1/8}, + textures = { + "shooter_flare.png", + "shooter_flare.png", + "shooter_flare.png", + "shooter_flare.png", + "shooter_flare.png", + "shooter_flare.png", + }, + player = nil, + collisionbox = {-1/16,-1/16,-1/16, 1/16,1/16,1/16}, + on_activate = function(self, staticdata) + if staticdata == "expired" then + self.object:remove() + end + end, + on_step = function(self, dtime) + self.timer = self.timer + dtime + if self.timer > 0.2 then + local pos = self.object:getpos() + local below = {x=pos.x, y=pos.y - 1, z=pos.z} + local node = minetest.get_node(below) + if node.name ~= "air" then + self.object:setvelocity({x=0, y=-10, z=0}) + self.object:setacceleration({x=0, y=0, z=0}) + if minetest.get_node(pos).name == "air" and + node.name ~= "default:water_source" and + node.name ~= "default:water_flowing" then + minetest.place_node(pos, {name="shooter:flare_light"}) + local meta = minetest.get_meta(pos) + meta:set_int("particle_id", id) + meta:set_int("init_time", os.time()) + pos.y = pos.y - 0.1 + local id = minetest.add_particlespawner( + 1000, 30, pos, pos, + {x=-1, y=1, z=-1}, {x=1, y=1, z=1}, + {x=2, y=-2, z=-2}, {x=2, y=-2, z=2}, + 0.1, 0.75, 1, 8, false, "shooter_flare_particle.png" + ) + local sound = minetest.sound_play("shooter_flare_burn", { + object = self.player, + loop = true, + }) + minetest.after(30, function(sound) + minetest.sound_stop(sound) + end, sound) + end + self.object:remove() + end + self.timer = 0 + end + end, + get_staticdata = function(self) + return "expired" + end, +}) + +minetest.register_tool("shooter:flaregun", { + description = "Flare Gun", + inventory_image = "shooter_flaregun.png", + on_use = function(itemstack, user, pointed_thing) + local inv = user:get_inventory() + if not inv:contains_item("main", "shooter:flare") then + minetest.sound_play("shooter_click", {object=user}) + return itemstack + end + if not minetest.setting_getbool("creative_mode") then + inv:remove_item("main", "shooter:flare 1") + itemstack:add_wear(65535/100) + end + local pos = user:getpos() + local dir = user:get_look_dir() + local yaw = user:get_look_yaw() + if pos and dir and yaw then + pos.y = pos.y + 1.5 + local obj = minetest.add_entity(pos, "shooter:flare_entity") + if obj then + minetest.sound_play("shooter_flare_fire", {object=obj}) + obj:setvelocity({x=dir.x * 16, y=dir.y * 16, z=dir.z * 16}) + obj:setacceleration({x=dir.x * -3, y=-10, z=dir.z * -3}) + obj:setyaw(yaw + math.pi) + local ent = obj:get_luaentity() + if ent then + ent.player = ent.player or user + end + end + end + return itemstack + end, +}) + +if SHOOTER_ENABLE_CRAFTING == true then + minetest.register_craft({ + output = "shooter:flare", + recipe = { + {"tnt:gunpowder", "wool:red"}, + }, + }) + minetest.register_craft({ + output = "shooter:flaregun", + recipe = { + {"wool:red", "wool:red", "wool:red"}, + {"", "", "default:steel_ingot"} + }, + }) +end + diff --git a/grapple.lua b/grapple.lua new file mode 100644 index 0000000..246ec89 --- /dev/null +++ b/grapple.lua @@ -0,0 +1,132 @@ +local function throw_hook(itemstack, user, vel) + local inv = user:get_inventory() + local pos = user:getpos() + local dir = user:get_look_dir() + local yaw = user:get_look_yaw() + if pos and dir and yaw then + if not minetest.setting_getbool("creative_mode") then + itemstack:add_wear(65535/100) + end + pos.y = pos.y + 1.5 + local obj = minetest.add_entity(pos, "shooter:hook") + if obj then + minetest.sound_play("shooter_throw", {object=obj}) + obj:setvelocity({x=dir.x * vel, y=dir.y * vel, z=dir.z * vel}) + obj:setacceleration({x=dir.x * -3, y=-10, z=dir.z * -3}) + obj:setyaw(yaw + math.pi) + local ent = obj:get_luaentity() + if ent then + ent.player = ent.player or user + ent.itemstack = itemstack + end + end + end +end + +minetest.register_entity("shooter:hook", { + physical = true, + timer = 0, + visual = "wielditem", + visual_size = {x=1/2, y=1/2}, + textures = {"shooter:grapple_hook"}, + player = nil, + itemstack = "", + collisionbox = {-1/4,-1/4,-1/4, 1/4,1/4,1/4}, + on_activate = function(self, staticdata) + self.object:set_armor_groups({fleshy=0}) + if staticdata == "expired" then + self.object:remove() + end + end, + on_step = function(self, dtime) + if not self.player then + return + end + self.timer = self.timer + dtime + if self.timer > 0.25 then + local pos = self.object:getpos() + local below = {x=pos.x, y=pos.y - 1, z=pos.z} + local node = minetest.get_node(below) + if node.name ~= "air" then + self.object:setvelocity({x=0, y=-10, z=0}) + self.object:setacceleration({x=0, y=0, z=0}) + if minetest.get_item_group(node.name, "liquid") == 0 and + minetest.get_node(pos).name == "air" then + self.player:setpos(pos) + end + if minetest.get_item_group(node.name, "lava") == 0 then + minetest.add_item(pos, self.itemstack) + end + self.object:remove() + end + self.timer = 0 + end + end, + get_staticdata = function(self) + return "expired" + end, +}) + +minetest.register_tool("shooter:grapple_hook", { + description = "Grappling Hook", + inventory_image = "shooter_hook.png", + on_use = function(itemstack, user, pointed_thing) + if pointed_thing.type ~= "nothing" then + return itemstack + end + throw_hook(itemstack, user, 14) + return "" + end, +}) + +minetest.register_tool("shooter:grapple_gun", { + description = "Grappling Gun", + inventory_image = "shooter_hook_gun.png", + on_use = function(itemstack, user, pointed_thing) + local inv = user:get_inventory() + if inv:contains_item("main", "shooter:grapple_hook") and + inv:contains_item("main", "tnt:gunpowder") then + inv:remove_item("main", "tnt:gunpowder") + minetest.sound_play("shooter_reload", {object=user}) + local stack = inv:remove_item("main", "shooter:grapple_hook") + itemstack = "shooter:grapple_gun_loaded 1 "..stack:get_wear() + else + minetest.sound_play("shooter_click", {object=user}) + end + return itemstack + end, +}) + +minetest.register_tool("shooter:grapple_gun_loaded", { + description = "Grappling Gun", + inventory_image = "shooter_hook_gun_loaded.png", + groups = {not_in_creative_inventory=1}, + on_use = function(itemstack, user, pointed_thing) + if pointed_thing.type ~= "nothing" then + return itemstack + end + minetest.sound_play("shooter_pistol", {object=user}) + itemstack = ItemStack("shooter:grapple_hook 1 "..itemstack:get_wear()) + throw_hook(itemstack, user, 20) + return "shooter:grapple_gun" + end, +}) + +if SHOOTER_ENABLE_CRAFTING == true then + minetest.register_craft({ + output = "shooter:grapple_hook", + recipe = { + {"default:steel_ingot", "default:steel_ingot", "default:diamond"}, + {"default:steel_ingot", "default:steel_ingot", ""}, + {"default:diamond", "", "default:steel_ingot"}, + }, + }) + minetest.register_craft({ + output = "shooter:grapple_gun", + recipe = { + {"", "default:steel_ingot", "default:steel_ingot"}, + {"", "", "default:diamond"}, + }, + }) +end + diff --git a/grenade.lua b/grenade.lua new file mode 100644 index 0000000..9b2b669 --- /dev/null +++ b/grenade.lua @@ -0,0 +1,80 @@ +minetest.register_entity("shooter:grenade_entity", { + physical = false, + timer = 0, + visual = "cube", + visual_size = {x=1/8, y=1/8}, + textures = { + "shooter_grenade.png", + "shooter_grenade.png", + "shooter_grenade.png", + "shooter_grenade.png", + "shooter_grenade.png", + "shooter_grenade.png", + }, + player = nil, + collisionbox = {0,0,0, 0,0,0}, + on_activate = function(self, staticdata) + if staticdata == "expired" then + self.object:remove() + end + end, + on_step = function(self, dtime) + self.timer = self.timer + dtime + if self.timer > 0.2 then + local pos = self.object:getpos() + local below = {x=pos.x, y=pos.y - 1, z=pos.z} + if minetest.get_node(below).name ~= "air" then + self.object:remove() + shooter:blast(pos, 1, 25, 5) + end + self.timer = 0 + end + end, + get_staticdata = function(self) + return "expired" + end, +}) + +minetest.register_tool("shooter:grenade", { + description = "Grenade", + inventory_image = "shooter_hand_grenade.png", + on_use = function(itemstack, user, pointed_thing) + if not minetest.setting_getbool("creative_mode") then + itemstack = "" + end + if pointed_thing.type ~= "nothing" then + local pointed = minetest.get_pointed_thing_position(pointed_thing) + if vector.distance(user:getpos(), pointed) < 8 then + shooter:blast(pointed, 1, 25, 5) + return + end + end + local pos = user:getpos() + local dir = user:get_look_dir() + local yaw = user:get_look_yaw() + if pos and dir then + pos.y = pos.y + 1.5 + local obj = minetest.add_entity(pos, "shooter:grenade_entity") + if obj then + obj:setvelocity({x=dir.x * 15, y=dir.y * 15, z=dir.z * 15}) + obj:setacceleration({x=dir.x * -3, y=-10, z=dir.z * -3}) + obj:setyaw(yaw + math.pi) + local ent = obj:get_luaentity() + if ent then + ent.player = ent.player or user + end + end + end + return itemstack + end, +}) + +if SHOOTER_ENABLE_CRAFTING == true then + minetest.register_craft({ + output = "shooter:grenade", + recipe = { + {"tnt:gunpowder", "default:steel_ingot"}, + }, + }) +end + diff --git a/guns.lua b/guns.lua new file mode 100644 index 0000000..d31a724 --- /dev/null +++ b/guns.lua @@ -0,0 +1,127 @@ +shooter:register_weapon("shooter:pistol", { + description = "Pistol", + inventory_image = "shooter_pistol.png", + rounds = 200, + spec = { + range = 100, + step = 20, + tool_caps = {full_punch_interval=0.5, damage_groups={fleshy=2}}, + groups = {snappy=3, fleshy=3, oddly_breakable_by_hand=3}, + sound = "shooter_pistol", + particle = "shooter_cap.png", + }, +}) + +shooter:register_weapon("shooter:rifle", { + description = "Rifle", + inventory_image = "shooter_rifle.png", + rounds = 100, + spec = { + range = 200, + step = 30, + tool_caps = {full_punch_interval=1.0, damage_groups={fleshy=3}}, + groups = {snappy=3, crumbly=3, choppy=3, fleshy=2, oddly_breakable_by_hand=2}, + sound = "shooter_rifle", + particle = "shooter_bullet.png", + }, +}) + +shooter:register_weapon("shooter:shotgun", { + description = "Shotgun", + inventory_image = "shooter_shotgun.png", + rounds = 50, + spec = { + range = 50, + step = 15, + tool_caps = {full_punch_interval=1.5, damage_groups={fleshy=4}}, + groups = {cracky=3, snappy=2, crumbly=2, choppy=2, fleshy=1, oddly_breakable_by_hand=1}, + sound = "shooter_shotgun", + particle = "smoke_puff.png", + }, +}) + +shooter:register_weapon("shooter:machine_gun", { + description = "Sub Machine Gun", + inventory_image = "shooter_smgun.png", + rounds = 50, + shots = 4, + spec = { + range = 100, + step = 20, + tool_caps = {full_punch_interval=0.125, damage_groups={fleshy=2}}, + groups = {snappy=3, fleshy=3, oddly_breakable_by_hand=3}, + sound = "shooter_pistol", + particle = "shooter_cap.png", + }, +}) + +minetest.register_craftitem("shooter:ammo", { + description = "Ammo pack", + inventory_image = "shooter_ammo.png", +}) + +if SHOOTER_ENABLE_CRAFTING == true then + minetest.register_craft({ + output = "shooter:pistol 1 65535", + recipe = { + {"default:steel_ingot", "default:steel_ingot"}, + {"", "default:mese_crystal"}, + }, + }) + minetest.register_craft({ + output = "shooter:rifle 1 65535", + recipe = { + {"default:steel_ingot", "", ""}, + {"", "default:bronze_ingot", ""}, + {"", "default:mese_crystal", "default:bronze_ingot"}, + }, + }) + minetest.register_craft({ + output = "shooter:shotgun 1 65535", + recipe = { + {"default:steel_ingot", "", ""}, + {"", "default:steel_ingot", ""}, + {"", "default:mese_crystal", "default:bronze_ingot"}, + }, + }) + minetest.register_craft({ + output = "shooter:machine_gun 1 65535", + recipe = { + {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, + {"", "default:bronze_ingot", "default:mese_crystal"}, + {"", "default:bronze_ingot", ""}, + }, + }) + minetest.register_craft({ + output = "shooter:ammo", + recipe = { + {"tnt:gunpowder", "default:bronze_ingot"}, + }, + }) +end + +local rounds_update_time = 0 + +minetest.register_globalstep(function(dtime) + shooter.time = shooter.time + dtime + if shooter.time - rounds_update_time > SHOOTER_ROUNDS_UPDATE_TIME then + for i, round in ipairs(shooter.rounds) do + if shooter:process_round(round) or round.dist > round.def.range then + table.remove(shooter.rounds, i) + else + local v = vector.multiply(round.ray, round.def.step) + shooter.rounds[i].pos = vector.add(round.pos, v) + shooter.rounds[i].dist = round.dist + round.def.step + end + end + rounds_update_time = shooter.time + end + if shooter.time > 100000 then + shooter.shots = {} + rounds_update_time = 0 + shooter.reload_time = 0 + shooter.update_time = 0 + shooter.time = 0 + end +end) + diff --git a/init.lua b/init.lua index aa99b4c..4c0471b 100644 --- a/init.lua +++ b/init.lua @@ -1,107 +1,23 @@ -dofile(minetest.get_modpath(minetest.get_current_modname()).."/shooter.lua") +local modpath = minetest.get_modpath(minetest.get_current_modname()) -shooter:register_weapon("shooter:pistol", { - description = "Pistol", - inventory_image = "shooter_pistol.png", - rounds = 200, - spec = { - range = 100, - step = 20, - tool_caps = {full_punch_interval=0.5, damage_groups={fleshy=2}}, - groups = {snappy=3, fleshy=3, oddly_breakable_by_hand=3}, - sound = "shooter_pistol", - particle = "shooter_cap.png", - }, -}) +dofile(modpath.."/shooter.lua") -shooter:register_weapon("shooter:rifle", { - description = "Rifle", - inventory_image = "shooter_rifle.png", - rounds = 100, - spec = { - range = 200, - step = 30, - tool_caps = {full_punch_interval=1.0, damage_groups={fleshy=3}}, - groups = {snappy=3, crumbly=3, choppy=3, fleshy=2, oddly_breakable_by_hand=2}, - sound = "shooter_rifle", - particle = "shooter_bullet.png", - }, -}) - -shooter:register_weapon("shooter:shotgun", { - description = "Shotgun", - inventory_image = "shooter_shotgun.png", - rounds = 50, - spec = { - range = 50, - step = 15, - tool_caps = {full_punch_interval=1.5, damage_groups={fleshy=4}}, - groups = {cracky=3, snappy=2, crumbly=2, choppy=2, fleshy=1, oddly_breakable_by_hand=1}, - sound = "shooter_shotgun", - particle = "smoke_puff.png", - }, -}) - -shooter:register_weapon("shooter:machine_gun", { - description = "Sub Machine Gun", - inventory_image = "shooter_smgun.png", - rounds = 50, - shots = 4, - spec = { - range = 100, - step = 20, - tool_caps = {full_punch_interval=0.125, damage_groups={fleshy=2}}, - groups = {snappy=3, fleshy=3, oddly_breakable_by_hand=3}, - sound = "shooter_pistol", - particle = "shooter_cap.png", - }, -}) - -minetest.register_craftitem("shooter:ammo", { - description = "Ammo pack", - inventory_image = "shooter_ammo.png", -}) - - -minetest.register_craft({ - output = "shooter:pistol 1 65535", - recipe = { - {"default:steel_ingot", "default:steel_ingot"}, - {"", "default:mese_crystal"}, - }, -}) - -minetest.register_craft({ - output = "shooter:rifle 1 65535", - recipe = { - {"default:steel_ingot", "", ""}, - {"", "default:bronze_ingot", ""}, - {"", "default:mese_crystal", "default:bronze_ingot"}, - }, -}) - -minetest.register_craft({ - output = "shooter:shotgun 1 65535", - recipe = { - {"default:steel_ingot", "", ""}, - {"", "default:steel_ingot", ""}, - {"", "default:mese_crystal", "default:bronze_ingot"}, - }, -}) - -minetest.register_craft({ - output = "shooter:machine_gun 1 65535", - recipe = { - {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, - {"", "default:bronze_ingot", "default:mese_crystal"}, - {"", "default:bronze_ingot", ""}, - }, -}) - -minetest.register_craft({ - output = "shooter:ammo", - recipe = { - {"tnt:gunpowder", "default:bronze_ingot"}, - }, -}) +if SHOOTER_ENABLE_GUNS == true then + dofile(modpath.."/guns.lua") +end +if SHOOTER_ENABLE_FLARES == true then + dofile(modpath.."/flaregun.lua") +end +if SHOOTER_ENABLE_HOOK == true then + dofile(modpath.."/grapple.lua") +end +if SHOOTER_ENABLE_GRENADES == true then + dofile(modpath.."/grenade.lua") +end +if SHOOTER_ENABLE_ROCKETS == true then + dofile(modpath.."/rocket.lua") +end +if SHOOTER_ENABLE_TURRETS == true then + dofile(modpath.."/turret.lua") +end diff --git a/models/shooter_turret.b3d b/models/shooter_turret.b3d new file mode 100644 index 0000000..8aa291c Binary files /dev/null and b/models/shooter_turret.b3d differ diff --git a/models/shooter_turret.blend b/models/shooter_turret.blend new file mode 100644 index 0000000..c9519bd Binary files /dev/null and b/models/shooter_turret.blend differ diff --git a/models/shooter_turret_uv.png b/models/shooter_turret_uv.png new file mode 100644 index 0000000..c6bcde2 Binary files /dev/null and b/models/shooter_turret_uv.png differ diff --git a/rocket.lua b/rocket.lua new file mode 100644 index 0000000..cf1c867 --- /dev/null +++ b/rocket.lua @@ -0,0 +1,113 @@ +minetest.register_craftitem("shooter:rocket", { + description = "Rocket", + stack_max = 1, + inventory_image = "shooter_rocket_inv.png", +}) + +minetest.register_entity("shooter:rocket_entity", { + physical = false, + timer = 0, + visual = "cube", + visual_size = {x=1/8, y=1/8}, + textures = { + "shooter_bullet.png", + "shooter_bullet.png", + "shooter_bullet.png", + "shooter_bullet.png", + "shooter_bullet.png", + "shooter_bullet.png", + }, + player = nil, + collisionbox = {0,0,0, 0,0,0}, + on_activate = function(self, staticdata) + if staticdata == "expired" then + self.object:remove() + end + end, + on_step = function(self, dtime) + self.timer = self.timer + dtime + if self.timer > 0.2 then + local pos = self.object:getpos() + if minetest.get_node(pos).name ~= "air" then + self.object:remove() + shooter:blast(pos, 2, 50, 7) + end + self.timer = 0 + end + end, + get_staticdata = function(self) + return "expired" + end, +}) + +minetest.register_tool("shooter:rocket_gun_loaded", { + description = "Rocket Gun", + inventory_image = "shooter_rocket_gun_loaded.png", + groups = {not_in_creative_inventory=1}, + on_use = function(itemstack, user, pointed_thing) + if not minetest.setting_getbool("creative_mode") then + itemstack:add_wear(65535/50) + end + itemstack = "shooter:rocket_gun 1 "..itemstack:get_wear() + if pointed_thing.type ~= "nothing" then + local pointed = minetest.get_pointed_thing_position(pointed_thing) + if vector.distance(user:getpos(), pointed) < 8 then + shooter:blast(pointed, 2, 50, 7) + return itemstack + end + end + local pos = user:getpos() + local dir = user:get_look_dir() + local yaw = user:get_look_yaw() + if pos and dir and yaw then + pos.y = pos.y + 1.5 + local obj = minetest.add_entity(pos, "shooter:rocket_entity") + if obj then + minetest.sound_play("shooter_rocket_fire", {object=obj}) + obj:setvelocity({x=dir.x * 20, y=dir.y * 20, z=dir.z * 20}) + obj:setacceleration({x=dir.x * -3, y=-10, z=dir.z * -3}) + obj:setyaw(yaw + math.pi) + local ent = obj:get_luaentity() + if ent then + ent.player = ent.player or user + end + end + end + return itemstack + end, +}) + +minetest.register_tool("shooter:rocket_gun", { + description = "Rocket Gun", + inventory_image = "shooter_rocket_gun.png", + on_use = function(itemstack, user, pointed_thing) + local inv = user:get_inventory() + if inv:contains_item("main", "shooter:rocket") then + minetest.sound_play("shooter_reload", {object=user}) + if not minetest.setting_getbool("creative_mode") then + inv:remove_item("main", "shooter:rocket 1") + end + itemstack = "shooter:rocket_gun_loaded 1 "..itemstack:get_wear() + else + minetest.sound_play("shooter_click", {object=user}) + end + return itemstack + end, +}) + +if SHOOTER_ENABLE_CRAFTING == true then + minetest.register_craft({ + output = "shooter:rocket_gun", + recipe = { + {"default:bronze_ingot", "default:steel_ingot", "default:steel_ingot"}, + {"", "", "default:diamond"}, + }, + }) + minetest.register_craft({ + output = "shooter:rocket", + recipe = { + {"default:bronze_ingot", "tnt:gunpowder", "default:bronze_ingot"}, + }, + }) +end + diff --git a/shooter.conf.example b/shooter.conf.example index 9c6f429..954ccca 100644 --- a/shooter.conf.example +++ b/shooter.conf.example @@ -2,6 +2,27 @@ -- Global Constants (defaults) +-- Enable basic guns (Pistol, Rifle, Shotgun, Machine Gun) +SHOOTER_ENABLE_GUNS = true + +-- Enable Flare Gun +SHOOTER_ENABLE_FLARES = true + +-- Enable Grappling Hook +SHOOTER_ENABLE_HOOK = true + +-- Enable Grenades +SHOOTER_ENABLE_GRENADES = true + +-- Enable Rocket Gun +SHOOTER_ENABLE_ROCKETS = true + +-- Enable Turrret Gun +SHOOTER_ENABLE_TURRETS = true + +-- Enable Crafting +SHOOTER_ENABLE_CRAFTING = true + -- Enable particle effects SHOOTER_ENABLE_PARTICLE_FX = true diff --git a/shooter.lua b/shooter.lua index bb991c8..e2497a6 100644 --- a/shooter.lua +++ b/shooter.lua @@ -3,8 +3,17 @@ shooter = { objects = {}, rounds = {}, shots = {}, + update_time = 0, + reload_time = 0, } +SHOOTER_ENABLE_GUNS = true +SHOOTER_ENABLE_FLARES = true +SHOOTER_ENABLE_HOOK = true +SHOOTER_ENABLE_GRENADES = true +SHOOTER_ENABLE_ROCKETS = true +SHOOTER_ENABLE_TURRETS = true +SHOOTER_ENABLE_CRAFTING = true SHOOTER_ENABLE_PARTICLE_FX = true SHOOTER_EXPLOSION_TEXTURE = "shooter_hit.png" SHOOTER_ALLOW_NODES = true @@ -44,10 +53,6 @@ if input then input = nil end -local rounds_update_time = 0 -local object_update_time = 0 -local object_reload_time = 0 - local function get_dot_product(v1, v2) return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z end @@ -121,7 +126,7 @@ local function punch_node(pos, def) end end -local function process_round(round) +function shooter:process_round(round) local target = {object=nil, distance=10000} local p1 = round.pos local v1 = round.ray @@ -307,8 +312,8 @@ function shooter:load_objects() end end end - object_reload_time = shooter.time - object_update_time = shooter.time + shooter.reload_time = shooter.time + shooter.update_time = shooter.time shooter.objects = {} for _,v in ipairs(objects) do table.insert(shooter.objects, v) @@ -316,9 +321,9 @@ function shooter:load_objects() end function shooter:update_objects() - if shooter.time - object_reload_time > SHOOTER_OBJECT_RELOAD_TIME then + if shooter.time - shooter.reload_time > SHOOTER_OBJECT_RELOAD_TIME then shooter:load_objects() - elseif shooter.time - object_update_time > SHOOTER_OBJECT_UPDATE_TIME then + elseif shooter.time - shooter.update_time > SHOOTER_OBJECT_UPDATE_TIME then for i, ref in ipairs(shooter.objects) do if ref.object then local pos = ref.object:getpos() @@ -329,34 +334,68 @@ function shooter:update_objects() table.remove(shooter.objects, i) end end - object_update_time = shooter.time + shooter.update_time = shooter.time end end +function shooter:blast(pos, radius, fleshy, distance) + local pos = vector.round(pos) + local p1 = vector.subtract(pos, radius) + local p2 = vector.add(pos, radius) + minetest.sound_play("tnt_explode", {pos=pos, gain=1}) + minetest.set_node(pos, {name="tnt:boom"}) + if SHOOTER_ENABLE_PARTICLE_FX == true then + minetest.add_particlespawner(50, 0.1, + p1, p2, {x=-0, y=-0, z=-0}, {x=0, y=0, z=0}, + {x=-0.5, y=5, z=-0.5}, {x=0.5, y=5, z=0.5}, + 0.1, 1, 8, 15, false, "tnt_smoke.png" + ) + end + local objects = minetest.get_objects_inside_radius(pos, distance) + for _,obj in ipairs(objects) do + if (obj:is_player() and SHOOTER_ALLOW_PLAYERS == true) or + (obj:get_luaentity() and SHOOTER_ALLOW_ENTITIES == true and + obj:get_luaentity().name ~= "__builtin:item") then + local obj_pos = obj:getpos() + local dist = vector.distance(obj_pos, pos) + local damage = (fleshy * 0.5 ^ dist) * 2 + if dist ~= 0 then + obj_pos.y = obj_pos.y + 1.7 + blast_pos = {x=pos.x, y=pos.y + 4, z=pos.z} + if minetest.line_of_sight(obj_pos, blast_pos, 1) then + obj:punch(obj, 1.0, { + full_punch_interval = 1.0, + damage_groups = {fleshy=damage}, + }) + end + end + end + end + local pr = PseudoRandom(os.time()) + local vm = VoxelManip() + local min, max = vm:read_from_map(p1, p2) + local area = VoxelArea:new({MinEdge=min, MaxEdge=max}) + local data = vm:get_data() + local c_air = minetest.get_content_id("air") + for z = -radius, radius do + for y = -radius, radius do + local vi = area:index(pos.x - radius, pos.y + y, pos.z + z) + for x = -radius, radius do + if (x * x) + (y * y) + (z * z) <= + (radius * radius) + pr:next(-radius, radius) then + data[vi] = c_air + end + vi = vi + 1 + end + end + end + vm:set_data(data) + vm:update_liquids() + vm:write_to_map() + vm:update_map() +end + minetest.register_on_joinplayer(function(player) player:hud_set_flags({crosshair = true}) end) -minetest.register_globalstep(function(dtime) - shooter.time = shooter.time + dtime - if shooter.time - rounds_update_time > SHOOTER_ROUNDS_UPDATE_TIME then - for i, round in ipairs(shooter.rounds) do - if process_round(round) or round.dist > round.def.range then - table.remove(shooter.rounds, i) - else - local v = vector.multiply(round.ray, round.def.step) - shooter.rounds[i].pos = vector.add(round.pos, v) - shooter.rounds[i].dist = round.dist + round.def.step - end - end - rounds_update_time = shooter.time - end - if shooter.time > 100000 then - shooter.shots = {} - rounds_update_time = 0 - object_reload_time = 0 - object_update_time = 0 - shooter.time = 0 - end -end) - diff --git a/sounds/shooter_flare_burn.ogg b/sounds/shooter_flare_burn.ogg new file mode 100644 index 0000000..68d6e10 Binary files /dev/null and b/sounds/shooter_flare_burn.ogg differ diff --git a/sounds/shooter_flare_fire.ogg b/sounds/shooter_flare_fire.ogg new file mode 100644 index 0000000..e0c625b Binary files /dev/null and b/sounds/shooter_flare_fire.ogg differ diff --git a/sounds/shooter_rocket_fire.ogg b/sounds/shooter_rocket_fire.ogg new file mode 100644 index 0000000..57252f3 Binary files /dev/null and b/sounds/shooter_rocket_fire.ogg differ diff --git a/sounds/shooter_throw.ogg b/sounds/shooter_throw.ogg new file mode 100644 index 0000000..9ce9176 Binary files /dev/null and b/sounds/shooter_throw.ogg differ diff --git a/textures/shooter_ammo.png b/textures/shooter_ammo.png index 0b9a662..daa348e 100644 Binary files a/textures/shooter_ammo.png and b/textures/shooter_ammo.png differ diff --git a/textures/shooter_flare.png b/textures/shooter_flare.png new file mode 100644 index 0000000..98fd967 Binary files /dev/null and b/textures/shooter_flare.png differ diff --git a/textures/shooter_flare_inv.png b/textures/shooter_flare_inv.png new file mode 100644 index 0000000..532a7df Binary files /dev/null and b/textures/shooter_flare_inv.png differ diff --git a/textures/shooter_flare_light.png b/textures/shooter_flare_light.png new file mode 100644 index 0000000..afed6ba Binary files /dev/null and b/textures/shooter_flare_light.png differ diff --git a/textures/shooter_flare_particle.png b/textures/shooter_flare_particle.png new file mode 100644 index 0000000..8e38165 Binary files /dev/null and b/textures/shooter_flare_particle.png differ diff --git a/textures/shooter_flaregun.png b/textures/shooter_flaregun.png new file mode 100644 index 0000000..e654ab3 Binary files /dev/null and b/textures/shooter_flaregun.png differ diff --git a/textures/shooter_grenade.png b/textures/shooter_grenade.png new file mode 100644 index 0000000..fd13447 Binary files /dev/null and b/textures/shooter_grenade.png differ diff --git a/textures/shooter_hand_grenade.png b/textures/shooter_hand_grenade.png new file mode 100644 index 0000000..f0ade73 Binary files /dev/null and b/textures/shooter_hand_grenade.png differ diff --git a/textures/shooter_hook.png b/textures/shooter_hook.png new file mode 100644 index 0000000..15311e5 Binary files /dev/null and b/textures/shooter_hook.png differ diff --git a/textures/shooter_hook_gun.png b/textures/shooter_hook_gun.png new file mode 100644 index 0000000..3687ac4 Binary files /dev/null and b/textures/shooter_hook_gun.png differ diff --git a/textures/shooter_hook_gun_loaded.png b/textures/shooter_hook_gun_loaded.png new file mode 100644 index 0000000..28cdb3b Binary files /dev/null and b/textures/shooter_hook_gun_loaded.png differ diff --git a/textures/shooter_rocket_gun.png b/textures/shooter_rocket_gun.png new file mode 100644 index 0000000..84738d4 Binary files /dev/null and b/textures/shooter_rocket_gun.png differ diff --git a/textures/shooter_rocket_gun_loaded.png b/textures/shooter_rocket_gun_loaded.png new file mode 100644 index 0000000..3b2dc4d Binary files /dev/null and b/textures/shooter_rocket_gun_loaded.png differ diff --git a/textures/shooter_rocket_inv.png b/textures/shooter_rocket_inv.png new file mode 100644 index 0000000..a96fa46 Binary files /dev/null and b/textures/shooter_rocket_inv.png differ diff --git a/textures/shooter_turret_base.png b/textures/shooter_turret_base.png new file mode 100644 index 0000000..fd13447 Binary files /dev/null and b/textures/shooter_turret_base.png differ diff --git a/textures/shooter_turret_gun.png b/textures/shooter_turret_gun.png new file mode 100644 index 0000000..97e3e9f Binary files /dev/null and b/textures/shooter_turret_gun.png differ diff --git a/textures/shooter_turret_uv.png b/textures/shooter_turret_uv.png new file mode 100644 index 0000000..c6bcde2 Binary files /dev/null and b/textures/shooter_turret_uv.png differ diff --git a/turret.lua b/turret.lua new file mode 100644 index 0000000..d8ce291 --- /dev/null +++ b/turret.lua @@ -0,0 +1,272 @@ +local function get_turret_entity(pos) + local entity = nil + local objects = minetest.get_objects_inside_radius(pos, 1) + for _, obj in ipairs(objects) do + local ent = obj:get_luaentity() + if ent then + if ent.name == "shooter:turret_entity" then + -- Remove duplicates + if entity then + obj:remove() + else + entity = ent + end + end + end + end + return entity +end + +minetest.register_entity("shooter:tnt_entity", { + physical = false, + timer = 0, + visual = "cube", + visual_size = {x=1/4, y=1/4}, + textures = { + "tnt_top.png", + "tnt_bottom.png", + "tnt_side.png", + "tnt_side.png", + "tnt_side.png", + "tnt_side.png", + }, + player = nil, + collisionbox = {0,0,0, 0,0,0}, + on_activate = function(self, staticdata) + if staticdata == "expired" then + self.object:remove() + end + end, + on_step = function(self, dtime) + self.timer = self.timer + dtime + if self.timer > 0.2 then + local pos = self.object:getpos() + if minetest.get_node(pos).name ~= "air" then + self.object:remove() + shooter:blast(pos, 4, 80, 10) + end + self.timer = 0 + end + end, + get_staticdata = function(self) + return "expired" + end, +}) + +minetest.register_entity("shooter:turret_entity", { + physical = true, + visual = "mesh", + mesh = "shooter_turret.b3d", + visual_size = {x=1, y=1}, + collisionbox = {-0.3, 0.5,-0.3, 0.3,1,0.3}, + textures = { + "shooter_turret_uv.png", + }, + timer = 0, + player = nil, + pitch = 40, + yaw = 0, + firing = false, + on_activate = function(self, staticdata) + self.pos = self.object:getpos() + self.yaw = self.object:getyaw() + if minetest.get_node(self.pos).name ~= "shooter:turret" then + self.object:remove() + return + end + self.object:set_animation({x=self.pitch, y=self.pitch}, 0) + self.object:set_armor_groups({fleshy=0}) + -- Remove duplicates + get_turret_entity(self.pos) + end, + on_rightclick = function(self, clicker) + if self.player == nil then + clicker:set_attach(self.object, "", {x=0,y=5,z=-8}, {x=0,y=0,z=0}) + self.player = clicker + else + self.player:set_detach() + local yaw = self.yaw + math.pi / 2 + local dir = vector.normalize({ + x = math.cos(yaw), + y = 0, + z = math.sin(yaw), + }) + local pos = vector.subtract(self.player:getpos(), dir) + minetest.after(0.2, function(player) + player:setpos(pos) + end, self.player) + self.player = nil + end + end, + on_step = function(self, dtime) + self.timer = self.timer + dtime + if self.timer < 0.1 then + return + end + if self.player then + local pitch = self.pitch + local yaw = self.object:getyaw() + local ctrl = self.player:get_player_control() + local step = 2 + if ctrl then + if ctrl.sneak then + step = 1 + if ctrl.jump then + if self.firing == false then + self:fire() + self.firing = true + end + else + self.firing = false + end + end + if ctrl.down then + pitch = pitch + 1 * step + elseif ctrl.up then + pitch = pitch - 1 * step + end + if ctrl.left then + yaw = yaw + 0.025 * step + elseif ctrl.right then + yaw = yaw - 0.025 * step + end + if pitch < 0 then + pitch = 0 + elseif pitch > 90 then + pitch = 90 + end + if self.pitch ~= pitch then + self.object:set_animation({x=pitch, y=pitch}, 0) + self.pitch = pitch + end + if self.yaw ~= yaw then + self.object:setyaw(yaw) + self.yaw = yaw + end + end + end + self.timer = 0 + end, + fire = function(self) + local meta = minetest.get_meta(self.pos) + local inv = meta:get_inventory() + if not inv then + return + end + if not inv:contains_item("main", "tnt:tnt") then + minetest.sound_play("shooter_click", {object=self.object}) + return + end + minetest.sound_play("shooter_shotgun", {object=self.object}) + if not minetest.setting_getbool("creative_mode") then + inv:remove_item("main", "tnt:tnt") + end + local pitch = math.rad(self.pitch - 40) + local len = math.cos(pitch) + local dir = vector.normalize({ + x = len * math.sin(-self.yaw), + y = math.sin(pitch), + z = len * math.cos(self.yaw), + }) + local pos = {x=self.pos.x, y=self.pos.y + 0.87, z=self.pos.z} + pos = vector.add(pos, {x=dir.x * 1.5, y=dir.y * 1.5, z=dir.z * 1.5}) + local obj = minetest.add_entity(pos, "shooter:tnt_entity") + if obj then + minetest.sound_play("shooter_rocket_fire", {object=obj}) + obj:setyaw(self.yaw) + obj:setvelocity({x=dir.x * 20, y=dir.y * 20, z=dir.z * 20}) + obj:setacceleration({x=dir.x * -3, y=-10, z=dir.z * -3}) + end + if SHOOTER_ENABLE_PARTICLE_FX == true then + minetest.add_particlespawner(10, 0.1, + {x=pos.x - 1, y=pos.y - 1, z=pos.z - 1}, + {x=pos.x + 1, y=pos.y + 1, z=pos.z + 1}, + {x=0, y=0, z=0}, {x=0, y=0, z=0}, + {x=-0.5, y=-0.5, z=-0.5}, {x=0.5, y=0.5, z=0.5}, + 0.1, 1, 8, 15, false, "tnt_smoke.png" + ) + end + end +}) + +minetest.register_node("shooter:turret", { + description = "Turret Gun", + tiles = {"shooter_turret_base.png"}, + inventory_image = "shooter_turret_gun.png", + wield_image = "shooter_turret_gun.png", + drawtype = "nodebox", + paramtype = "light", + paramtype2 = "facedir", + groups = {snappy=2,choppy=2,oddly_breakable_by_hand=3}, + node_box = { + type = "fixed", + fixed = { + {-1/8, 1/8, -1/8, 1/8, 1/2, 1/8}, + {-5/16, 0, -5/16, 5/16, 1/8, 5/16}, + {-3/8, -1/2, -3/8, -1/4, 0, -1/4}, + {1/4, -1/2, 1/4, 3/8, 0, 3/8}, + {1/4, -1/2, -3/8, 3/8, 0, -1/4}, + {-3/8, -1/2, 1/4, -1/4, 0, 3/8}, + }, + }, + on_construct = function(pos) + local meta = minetest.get_meta(pos) + meta:set_string("formspec", "size[8,9]".. + "list[current_name;main;2,0;4,4;]".. + "list[current_player;main;0,5;8,4;]" + ) + meta:set_string("infotext", "Turret Gun") + local inv = meta:get_inventory() + inv:set_size("main", 16) + end, + after_place_node = function(pos, placer) + local node = minetest.get_node({x=pos.x, y=pos.y + 1, z=pos.z}) + if node.name == "air" then + if not get_turret_entity(pos) then + minetest.add_entity(pos, "shooter:turret_entity") + end + end + end, + can_dig = function(pos, player) + local meta = minetest.get_meta(pos); + local inv = meta:get_inventory() + return inv:is_empty("main") + end, + after_destruct = function(pos, oldnode) + local ent = get_turret_entity(pos) + if ent then + ent.object:remove() + end + end, + mesecons = { + effector = { + action_on = function(pos, node) + local ent = get_turret_entity(pos) + if ent then + if ent.firing == false then + ent:fire() + ent.firing = true + end + end + end, + action_off = function(pos, node) + local ent = get_turret_entity(pos) + if ent then + ent.firing = false + end + end, + }, + }, +}) + +if SHOOTER_ENABLE_CRAFTING == true then + minetest.register_craft({ + output = "shooter:turret", + recipe = { + {"default:bronze_ingot", "default:bronze_ingot", "default:steel_ingot"}, + {"", "default:bronze_ingot", "default:steel_ingot"}, + {"", "default:diamond", ""}, + }, + }) +end +