From a19923e6aac17115d8d1ea622644f72672b41331 Mon Sep 17 00:00:00 2001 From: stujones11 Date: Mon, 2 Jun 2014 19:19:22 +0100 Subject: [PATCH] Version 0.4.0 (rework) --- README.txt | 37 ++-- depends.txt | 2 - init.lua | 57 +++++- shooter.conf.example | 36 +++- shooter.lua | 334 ++++++++++++++++++++++++------------ textures/shooter_bullet.png | Bin 0 -> 178 bytes textures/shooter_cap.png | Bin 0 -> 157 bytes textures/shooter_smgun.png | Bin 0 -> 289 bytes 8 files changed, 325 insertions(+), 141 deletions(-) create mode 100644 textures/shooter_bullet.png create mode 100644 textures/shooter_cap.png create mode 100644 textures/shooter_smgun.png diff --git a/README.txt b/README.txt index b017051..8c114a3 100644 --- a/README.txt +++ b/README.txt @@ -1,28 +1,21 @@ Minetest Mod - Simple Shooter [shooter] ======================================= -Mod Version: 0.3.0 +Mod Version: 0.4.0 -Minetest Version: 0.4.8-dev d9ef072305 +Minetest Version: 0.4.9 -Depends: default - -An experimental first person shooter mod using simple vector mathematics -in an effort to find a more server-firendly method of hit detection from -that which is currently being used by the firearms mod. - -For the most part I think I have achieved this for straight pvp, however, -the jury is still out as to whether it is any faster against entities (mobs) +An experimental first person shooter mod that uses simple vector mathematics +to produce an accurate and server-firendly method of hit detection. By default this mod is configured to work only against other players in -multiplayer (server) mode. This is overridden in singleplayer mode to work -against all registered entities instead. +multiplayer mode and against Simple Mobs [mobs] in singleplayer mode. -Default configuration can be customised by adding a shooter.conf file to the -mod's main directory, see shooter.conf.example for more details. +Default configuration can be customised by adding a shooter.conf file to +the mod's main directory, see shooter.conf.example for more details. -This is still very much a work in progress which I plan to eventually use as -the base for a 'Spades' style FPS game using the minetest engine. +This is still very much a work in progress which I eventually plan to use +as the base for a 'Spades' style FPS game using the minetest engine. Crafting ======== @@ -34,7 +27,7 @@ M = Mese Crystal [default:mese_crysytal] Pistol: [shooter:pistol] +---+---+ -| S | B | +| S | S | +---+---+ | | M | +---+---+ @@ -59,3 +52,13 @@ Shotgun: [shooter:shotgun] | | M | B | +---+---+---+ +Sub Machine Gun: [shooter:machine_gun] + ++---+---+---+ +| S | S | S | ++---+---+---+ +| | B | M | ++---+---+---+ +| | B | | ++---+---+---+ + diff --git a/depends.txt b/depends.txt index 3a7daa1..e69de29 100644 --- a/depends.txt +++ b/depends.txt @@ -1,2 +0,0 @@ -default - diff --git a/init.lua b/init.lua index 427cd44..f1eb1a7 100644 --- a/init.lua +++ b/init.lua @@ -4,12 +4,15 @@ minetest.register_tool("shooter:pistol", { description = "Pistol", inventory_image = "shooter_pistol.png", on_use = function(itemstack, user, pointed_thing) + local name = user:get_player_name() shooter:fire_weapon(user, pointed_thing, { - range = 30, - tool_caps = {full_punch_interval=0.5, damage_groups={fleshy=1}}, + name = name, + 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 = "default_obsidian.png", + particle = "shooter_cap.png", }) itemstack:add_wear(328) -- 200 Rounds return itemstack @@ -20,12 +23,15 @@ minetest.register_tool("shooter:riffle", { description = "Riffle", inventory_image = "shooter_riffle.png", on_use = function(itemstack, user, pointed_thing) + local name = user:get_player_name() shooter:fire_weapon(user, pointed_thing, { - range = 80, - tool_caps = {full_punch_interval=1.0, damage_groups={fleshy=2}}, + name = name, + 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_riffle", - particle = "default_gold_block.png", + particle = "shooter_bullet.png", }) itemstack:add_wear(656) -- 100 Rounds return itemstack @@ -36,8 +42,11 @@ minetest.register_tool("shooter:shotgun", { description = "Shotgun", inventory_image = "shooter_shotgun.png", on_use = function(itemstack, user, pointed_thing) + local name = user:get_player_name() shooter:fire_weapon(user, pointed_thing, { - range = 15, + name = name, + 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", @@ -48,10 +57,33 @@ minetest.register_tool("shooter:shotgun", { end, }) +minetest.register_tool("shooter:machine_gun", { + description = "Sub Machine Gun", + inventory_image = "shooter_smgun.png", + on_use = function(itemstack, user, pointed_thing) + local name = user:get_player_name() + for i = 0, 0.45, 0.15 do + minetest.after(i, function() + shooter:fire_weapon(user, pointed_thing, { + name = name, + range = 100, + step = 20, + tool_caps = {full_punch_interval=0.1, damage_groups={fleshy=2}}, + groups = {snappy=3, fleshy=3, oddly_breakable_by_hand=3}, + sound = "shooter_pistol", + particle = "shooter_cap.png", + }) + end) + end + itemstack:add_wear(328) -- 4 x 200 Rounds + return itemstack + end, +}) + minetest.register_craft({ output = "shooter:pistol", recipe = { - {"default:steel_ingot", "default:bronze_ingot"}, + {"default:steel_ingot", "default:steel_ingot"}, {"", "default:mese_crystal"}, }, }) @@ -74,3 +106,12 @@ minetest.register_craft({ }, }) +minetest.register_craft({ + output = "shooter:machine_gun", + recipe = { + {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, + {"", "default:bronze_ingot", "default:mese_crystal"}, + {"", "default:bronze_ingot", ""}, + }, +}) + diff --git a/shooter.conf.example b/shooter.conf.example index 82e96ab..f91078b 100644 --- a/shooter.conf.example +++ b/shooter.conf.example @@ -2,18 +2,42 @@ -- Global Constants (defaults) --- Particle texture used when target it hit +-- Particle texture used when a player or entity it hit SHOOTER_EXPLOSION_TEXTURE = "shooter_hit.png" -- Allow node destruction SHOOTER_ALLOW_NODES = true --- Maximum node rage, applies only if nodes are allowed -SHOOTER_NODE_RANGE = 50 - -- Allow entities in multiplayer mode SHOOTER_ALLOW_ENTITIES = false --- Maximum object range, applies only if entities are allowed -SHOOTER_OBJECT_RANGE = 50 +-- Allow players in multiplayer mode +SHOOTER_ALLOW_PLAYERS = true + +-- How often objects are fully reloaded +SHOOTER_OBJECT_RELOAD_TIME = 1 + +-- How often object positions are updated +SHOOTER_OBJECT_UPDATE_TIME = 0.25 + +-- How often rounds are processed +SHOOTER_ROUNDS_UPDATE_TIME = 0.4 + +-- Player collision box offset (may require adjustment for some games) +SHOOTER_PLAYER_OFFSET = {x=0, y=1, z=0} + +-- Entity collision box offset (may require adjustment for other mobs) +SHOOTER_ENTITY_OFFSET = {x=0, y=0, z=0} + +-- Shootable entities (default support for Simple Mobs) +SHOOTER_ENTITIES = { + "mobs:dirt_monster", + "mobs:stone_monster", + "mobs:sand_monster", + "mobs:tree_monster", + "mobs:sheep", + "mobs:rat", + "mobs:oerkki", + "mobs:dungeon_master", +} diff --git a/shooter.lua b/shooter.lua index 2fbaa4a..d16e2af 100644 --- a/shooter.lua +++ b/shooter.lua @@ -1,10 +1,39 @@ -shooter = {} +shooter = { + time = 0, + objects = {}, + rounds = {}, + shots = {}, +} SHOOTER_EXPLOSION_TEXTURE = "shooter_hit.png" SHOOTER_ALLOW_NODES = true SHOOTER_ALLOW_ENTITIES = false -SHOOTER_NODE_RANGE = 50 -SHOOTER_OBJECT_RANGE = 50 +SHOOTER_ALLOW_PLAYERS = true +SHOOTER_OBJECT_RELOAD_TIME = 1 +SHOOTER_OBJECT_UPDATE_TIME = 0.25 +SHOOTER_ROUNDS_UPDATE_TIME = 0.4 +SHOOTER_PLAYER_OFFSET = {x=0, y=1, z=0} +SHOOTER_ENTITY_OFFSET = {x=0, y=0, z=0} +SHOOTER_ENTITIES = { + "mobs:dirt_monster", + "mobs:stone_monster", + "mobs:sand_monster", + "mobs:tree_monster", + "mobs:sheep", + "mobs:rat", + "mobs:oerkki", + "mobs:dungeon_master", +} + +if minetest.is_singleplayer() == true then + SHOOTER_ALLOW_ENTITIES = true + SHOOTER_ALLOW_PLAYERS = false +end + +local allowed_entities = {} +for _,v in ipairs(SHOOTER_ENTITIES) do + allowed_entities[v] = 1 +end local modpath = minetest.get_modpath(minetest.get_current_modname()) local input = io.open(modpath.."/shooter.conf", "r") @@ -13,36 +42,45 @@ if input then input:close() input = nil end -if minetest.is_singleplayer() == true then - SHOOTER_ALLOW_ENTITIES = true + +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 -local timer = 0 -local shots = {} local function spawn_particles(p, v, d, texture) - if type(texture) ~= "string" then - texture = SHOOTER_EXPLOSION_TEXTURE + if p and v and d then + if type(texture) ~= "string" then + texture = SHOOTER_EXPLOSION_TEXTURE + end + local pos = vector.add(p, vector.multiply(v, {x=d, y=d, z=d})) + pos.y = pos.y + 0.75 + local spread = {x=0.1, y=0.1, z=0.1} + minetest.add_particlespawner(15, 0.3, + vector.subtract(pos, spread), vector.add(pos, spread), + {x=-1, y=1, z=-1}, {x=1, y=2, z=1}, + {x=-2, y=-2, z=-2}, {x=2, y=-2, z=2}, + 0.1, 0.75, 1, 2, false, texture + ) end - local pos = vector.add(p, vector.multiply(v, {x=d, y=d, z=d})) - pos.y = pos.y + 0.75 - local spread = {x=0.1, y=0.1, z=0.1} - minetest.add_particlespawner(15, 0.3, - vector.subtract(pos, spread), vector.add(pos, spread), - {x=-1, y=1, z=-1}, {x=1, y=2, z=1}, - {x=-2, y=-2, z=-2}, {x=2, y=-2, z=2}, - 0.1, 0.75, 1, 2, false, texture - ) end local function is_valid_object(object) - if object:is_player() == true then - return true - end - if SHOOTER_ALLOW_ENTITIES == true then - local luaentity = object:get_luaentity() - if luaentity then - if minetest.registered_entities[luaentity.name] then - return true + if object then + if object:is_player() == true then + return true + end + if SHOOTER_ALLOW_ENTITIES == true then + local luaentity = object:get_luaentity() + if luaentity then + if luaentity.name then + if allowed_entities[luaentity.name] then + return true + end + end end end end @@ -64,16 +102,15 @@ local function punch_node(pos, def) if level >= v then minetest.remove_node(pos) local sounds = item.sounds - if sounds then - local soundspec = sounds.dug - if soundspec then - soundspec.pos = pos - minetest.sound_play(soundspec.name, soundspec) + if item.sounds then + local spec = item.sounds.dug + if spec then + spec.pos = pos + minetest.sound_play(spec.name, spec) end end - local tiles = item.tiles - if tiles then - return tiles[1] + if item.tiles then + return item.tiles[1] end break end @@ -81,17 +118,81 @@ local function punch_node(pos, def) end end +local function process_round(round) + local target = {object=nil, distance=10000} + local p1 = round.pos + local v1 = round.ray + for _,ref in ipairs(shooter.objects) do + local p2 = vector.add(ref.pos, ref.offset) + if p1 and p2 and ref.name ~= round.name then + local x = vector.distance(p1, p2) + if x < round.def.step then + local n = vector.multiply(v1, {x=-1, y=0, z=-1}) + local v2 = vector.subtract(p1, p2) + local r1 = get_dot_product(n, v2) + local r2 = get_dot_product(n, v1) + if r2 ~= 0 then + local t = -(r1 / r2) + local td = vector.multiply(v1, {x=t, y=t, z=t}) + local pt = vector.add(p1, td) + local pd = vector.subtract(pt, p2) + if math.abs(pd.x) < ref.collisionbox[4] and + math.abs(pd.y) < ref.collisionbox[5] and + math.abs(pd.z) < ref.collisionbox[6] then + target.object = ref.object + target.pos = pt + target.distance = x + end + end + end + end + end + if target.object and target.pos then + local success, pos = minetest.line_of_sight(p1, target.pos, 1) + if success then + local user = minetest.get_player_by_name(round.name) + if user then + target.object:punch(user, nil, round.def.tool_caps, v1) + spawn_particles({x=p1.x, y=p1.y - 1, z=p1.z}, v1, + target.distance, SHOOTER_EXPLOSION_TEXTURE) + end + return 1 + elseif pos and SHOOTER_ALLOW_NODES == true then + local texture = punch_node(pos, round.def) + if texture then + spawn_particles({x=p1.x, y=p1.y - 1, z=p1.z}, + v1, vector.distance(p1, pos), texture) + end + return 1 + end + elseif SHOOTER_ALLOW_NODES == true then + local d = round.def.step + local p2 = vector.add(p1, vector.multiply(v1, {x=d, y=d, z=d})) + local success, pos = minetest.line_of_sight(p1, p2, 1) + if pos then + local texture = punch_node(pos, round.def) + if texture then + spawn_particles({x=p1.x, y=p1.y - 1, z=p1.z}, + v1, vector.distance(p1, pos), texture) + end + return 1 + end + end +end + function shooter:fire_weapon(user, pointed_thing, def) - local name = user:get_player_name() - if shots[name] then - if timer < shots[name] then + if shooter.shots[def.name] then + if shooter.time < shooter.shots[def.name] then return end end - shots[name] = timer + def.tool_caps.full_punch_interval + shooter.shots[def.name] = shooter.time + def.tool_caps.full_punch_interval minetest.sound_play(def.sound, {object=user}) local v1 = user:get_look_dir() local p1 = user:getpos() + if not v1 or not p1 then + return + end minetest.add_particle({x=p1.x, y=p1.y + 1.6, z=p1.z}, vector.multiply(v1, {x=30, y=30, z=30}), {x=0, y=0, z=0}, 0.5, 0.25, @@ -102,92 +203,90 @@ function shooter:fire_weapon(user, pointed_thing, def) local texture = punch_node(pos, def) if texture then spawn_particles({x=p1.x, y=p1.y + 0.75, z=p1.z}, - v1, vector.distance(p1, pos), texture) + v1, vector.distance(p1, pos), texture) end - return elseif pointed_thing.type == "object" then local object = pointed_thing.ref if is_valid_object(object) == true then object:punch(user, nil, def.tool_caps, v1) local p2 = object:getpos() spawn_particles({x=p1.x, y=p1.y + 0.75, z=p1.z}, v1, - vector.distance(p1, p2), SHOOTER_EXPLOSION_TEXTURE) - return + vector.distance(p1, p2), SHOOTER_EXPLOSION_TEXTURE) end - end - if def.range > 100 then - def.range = 100 - end - local target = {object=nil, distance=100} - local objects = {} - if SHOOTER_ALLOW_ENTITIES == true then - local range = def.range - if range > SHOOTER_OBJECT_RANGE then - range = SHOOTER_OBJECT_RANGE - end - local r = math.ceil(range * 0.5) - local p = vector.add(p1, vector.multiply(v1, {x=r, y=r, z=r})) - objects = minetest.get_objects_inside_radius(p, r) else - objects = minetest.get_connected_players() + shooter:update_objects() + table.insert(shooter.rounds, { + name = def.name, + pos = vector.add(p1, {x=0, y=1.75, z=0}), + ray = v1, + dist = 0, + def = def, + }) end - for _,object in ipairs(objects) do - if is_valid_object(object) == true then - local p2 = object:getpos() - if p1 and p2 then - local x = vector.distance(p1, p2) - p2.y = p2.y - 0.75 - if x > 0 and x < target.distance and x < def.range then - local yx = 0 - if x > 30 then - yx = 0.001 * (10 - x * 0.1) - else - yx = 0.00002 * (x * x) - 0.002 * x + 0.05 - end - local yy = yx * 3 - local v2 = vector.normalize(vector.direction(p1, p2)) - local vd = vector.subtract(v1, v2) - if math.abs(vd.x) < yx and - math.abs(vd.z) < yx and - math.abs(vd.y) < yy then - target = { - object = object, - distance = x, - pos = {x=p2.x, z=p2.z, y=p2.y+1.75}, - } +end + +function shooter:load_objects() + local objects = {} + if SHOOTER_ALLOW_PLAYERS == true then + local players = minetest.get_connected_players() + for _,player in ipairs(players) do + local pos = player:getpos() + local name = player:get_player_name() + local hp = player:get_hp() or 0 + if pos and name and hp > 0 then + table.insert(objects, { + name = name, + object = player, + pos = pos, + collisionbox = {-0.25,-1.0,-0.25, 0.25, 0.8, 0.25}, + offset = SHOOTER_PLAYER_OFFSET, + }) + end + end + end + if SHOOTER_ALLOW_ENTITIES == true then + for _,ref in pairs(minetest.luaentities) do + if ref.object and ref.name then + if allowed_entities[ref.name] then + local pos = ref.object:getpos() + local hp = ref.object:get_hp() or 0 + if pos and hp > 0 then + local def = minetest.registered_entities[ref.name] + table.insert(objects, { + name = ref.name, + object = ref.object, + pos = pos, + collisionbox = def.collisionbox, + offset = SHOOTER_ENTITY_OFFSET, + }) end end end end end - local view_pos = {x=p1.x, y=p1.y + 1.75, z=p1.z} - if target.object then - local success, pos = minetest.line_of_sight(view_pos, target.pos, 1) - if success then - target.object:punch(user, nil, def.tool_caps, v1) - spawn_particles({x=p1.x, y=p1.y + 0.75, z=p1.z}, v1, - target.distance, SHOOTER_EXPLOSION_TEXTURE) - elseif pos and SHOOTER_ALLOW_NODES == true then - local texture = punch_node(pos, def) - if texture then - spawn_particles({x=p1.x, y=p1.y + 0.75, z=p1.z}, - v1, vector.distance(p1, pos), texture) - end - end - elseif SHOOTER_ALLOW_NODES == true then - local d = def.range - if d > SHOOTER_NODE_RANGE then - d = SHOOTER_NODE_RANGE - end - local p2 = vector.add(view_pos, vector.multiply(v1, {x=d, y=d, z=d})) - local success, pos = minetest.line_of_sight(view_pos, p2, 1) - if pos then - local texture = punch_node(pos, def) - if texture then - spawn_particles({x=p1.x, y=p1.y + 0.75, z=p1.z}, - v1, vector.distance(p1, pos), texture) + object_reload_time = shooter.time + object_update_time = shooter.time + shooter.objects = {} + for _,v in ipairs(objects) do + table.insert(shooter.objects, v) + end +end + +function shooter:update_objects() + if shooter.time - object_reload_time > SHOOTER_OBJECT_RELOAD_TIME then + shooter:load_objects() + elseif shooter.time - object_update_time > SHOOTER_OBJECT_UPDATE_TIME then + for i, ref in ipairs(shooter.objects) do + if ref.object then + local pos = ref.object:getpos() + if pos then + shooter.objects[i].pos = pos + end + else + table.remove(shooter.objects, i) end end + object_update_time = shooter.time end end @@ -196,6 +295,25 @@ minetest.register_on_joinplayer(function(player) end) minetest.register_globalstep(function(dtime) - timer = timer + 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/textures/shooter_bullet.png b/textures/shooter_bullet.png new file mode 100644 index 0000000000000000000000000000000000000000..c8b8e30696bd212cecd19fd7bdd5499497e9d3f2 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPggK7BN8`xm_ooeFF*^dAc};Se#Ct zb4Tmb{6?k&hYlP#uwdyiw`tF^x7QgO7zS{-@k*E`Twz#pr{e83{Y|_K3{`^cKfC9< RI|Fqyc)I$ztaD0e0svCOHhlm9 literal 0 HcmV?d00001 diff --git a/textures/shooter_cap.png b/textures/shooter_cap.png new file mode 100644 index 0000000000000000000000000000000000000000..692c1d59d5bfa333157bcbed3b2ea83dbd201bc6 GIT binary patch literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPggK7BN9B6LZauWk4ZWPZ!4!i_^&| u2?;;WJ1{0BEfDCDGRQixg0cB11B0&vqx`1pvr~Zz89ZJ6T-G@yGywo9b13Ej literal 0 HcmV?d00001 diff --git a/textures/shooter_smgun.png b/textures/shooter_smgun.png new file mode 100644 index 0000000000000000000000000000000000000000..98886e91390ada358fc2c411c6e2bd413bb5e3c9 GIT binary patch literal 289 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85sBugD~Uq{1qucLG}_)Usv|KJj}u}>MwMJ76OG9db&7CPSv2JnV^oyl&9@U|)~t9XS(GoQ=pP!h zs;X&@^lhySOXp?NrCK-s5njT`roGwX&Xzs3SI^9?*l^{D;DkepTO6Z!B+HnmF8jYh d^nd;yM((3kQ~UlrEC4!|!PC{xWt~$(69CkNXhi@3 literal 0 HcmV?d00001