Add particle effects and singleplayer support
This commit is contained in:
parent
aeb1dac34d
commit
b77e50e56f
9 changed files with 219 additions and 49 deletions
|
@ -7,6 +7,9 @@ License Textures: Stuart Jones - WTFPL
|
||||||
|
|
||||||
License Sounds: freesound.org
|
License Sounds: freesound.org
|
||||||
|
|
||||||
flobert1_20070728.wav by Nonoo - Attribution 3.0 Unported (CC BY 3.0)
|
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)
|
||||||
|
|
||||||
GUNSHOT.WAV by erkanozan - CC0 1.0 Universal (CC0 1.0)
|
GUNSHOT.WAV by erkanozan - CC0 1.0 Universal (CC0 1.0)
|
||||||
|
|
||||||
|
|
43
README.txt
43
README.txt
|
@ -1,38 +1,37 @@
|
||||||
Minetest Mod - Simple Shooter [shooter]
|
Minetest Mod - Simple Shooter [shooter]
|
||||||
=======================================
|
=======================================
|
||||||
|
|
||||||
Mod Version: 0.1.0
|
Mod Version: 0.2.0
|
||||||
|
|
||||||
Minetest Version: 0.4.8
|
Minetest Version: 0.4.8
|
||||||
|
|
||||||
Depends: default
|
Depends: default
|
||||||
|
|
||||||
An experimental first person shooter mod using vector mathematics instead of
|
An experimental first person shooter mod using simple vector mathematics
|
||||||
physical projectile entities. This has a number of advantages along with a
|
in an effort to find a more server-firendly method of hit detection from
|
||||||
number disadvantages.
|
that which is currently being used by the firearms mod.
|
||||||
|
|
||||||
Pros:
|
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)
|
||||||
|
One big downside of this method is that it only works against ordinary nodes
|
||||||
|
within pointable range of the player.
|
||||||
|
|
||||||
Fast and responsive
|
By default this mod is configured to work only against other players in
|
||||||
Fairly light weight
|
multiplayer (server) mode. This is overridden in singleplayer mode to work
|
||||||
Not affected by chunk boundaries
|
against all registered entities instead.
|
||||||
|
|
||||||
Cons:
|
Default configuration can be customised by adding a shooter.conf file to the
|
||||||
|
mod's main directory, see shooter.conf.example for more details.
|
||||||
|
|
||||||
Only works against other players
|
This is still very much a work in progress which I plan to eventually use as
|
||||||
Slightly less realistic
|
the base for a 'Spades' style FPS game using the minetest engine.
|
||||||
|
|
||||||
This is still very much a work in progress and currently not much use in a
|
|
||||||
singleplayer game. I plan to eventually use this as a base for a 'Spades' style
|
|
||||||
FPS game using the minetest engine, however, I decided to add a couple of craft
|
|
||||||
recipes and release this simple version for minetest_game.
|
|
||||||
|
|
||||||
Crafting
|
Crafting
|
||||||
========
|
========
|
||||||
|
|
||||||
S = Steel Ingot [default:steel_ingot]
|
S = Steel Ingot [default:steel_ingot]
|
||||||
B = Bronze Ingot [default:bronze_ingot]
|
B = Bronze Ingot [default:bronze_ingot]
|
||||||
M = Mese Crystal [default:mese_crystal]
|
M = Mese Crystal [default:mese_crysytal]
|
||||||
|
|
||||||
Pistol: [shooter:pistol]
|
Pistol: [shooter:pistol]
|
||||||
|
|
||||||
|
@ -52,3 +51,13 @@ Riffle: [shooter:riffle]
|
||||||
| | M | B |
|
| | M | B |
|
||||||
+---+---+---+
|
+---+---+---+
|
||||||
|
|
||||||
|
Shotgun: [shooter:shotgun]
|
||||||
|
|
||||||
|
+---+---+---+
|
||||||
|
| S | | |
|
||||||
|
+---+---+---+
|
||||||
|
| | S | |
|
||||||
|
+---+---+---+
|
||||||
|
| | M | B |
|
||||||
|
+---+---+---+
|
||||||
|
|
||||||
|
|
50
init.lua
50
init.lua
|
@ -1,33 +1,52 @@
|
||||||
dofile(minetest.get_modpath(minetest.get_current_modname()).."/shooter.lua")
|
dofile(minetest.get_modpath(minetest.get_current_modname()).."/shooter.lua")
|
||||||
|
|
||||||
minetest.register_tool("shooter:pistol", {
|
minetest.register_tool("shooter:pistol", {
|
||||||
description = "Pistol",
|
description = "Pistol",
|
||||||
inventory_image = "shooter_pistol.png",
|
inventory_image = "shooter_pistol.png",
|
||||||
on_use = function(itemstack, user, pointed_thing)
|
on_use = function(itemstack, user, pointed_thing)
|
||||||
shooter:fire_weapon(user, {
|
shooter:fire_weapon(user, pointed_thing, {
|
||||||
range_func = {a=-0.1, b=-1.5, c=100},
|
range = 30,
|
||||||
tool_caps = {full_punch_interval=1.0, damage_groups={fleshy=3}},
|
tool_caps = {full_punch_interval=0.5, damage_groups={fleshy=1}},
|
||||||
|
groups = {snappy=3, oddly_breakable_by_hand=3},
|
||||||
sound = "shooter_pistol",
|
sound = "shooter_pistol",
|
||||||
|
particle = "default_obsidian.png",
|
||||||
})
|
})
|
||||||
itemstack:add_wear(328) -- 200 Rounds
|
itemstack:add_wear(328) -- 200 Rounds
|
||||||
return itemstack
|
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_tool("shooter:riffle", {
|
minetest.register_tool("shooter:riffle", {
|
||||||
description = "Riffle",
|
description = "Riffle",
|
||||||
inventory_image = "shooter_riffle.png",
|
inventory_image = "shooter_riffle.png",
|
||||||
on_use = function(itemstack, user, pointed_thing)
|
on_use = function(itemstack, user, pointed_thing)
|
||||||
shooter:fire_weapon(user, {
|
shooter:fire_weapon(user, pointed_thing, {
|
||||||
range_func = {a=-0.02, b=-0.6, c=100},
|
range = 80,
|
||||||
tool_caps = {full_punch_interval=1.0, damage_groups={fleshy=5}},
|
tool_caps = {full_punch_interval=1.0, damage_groups={fleshy=2}},
|
||||||
|
groups = {snappy=3, crumbly=3, choppy=3, oddly_breakable_by_hand=2},
|
||||||
sound = "shooter_riffle",
|
sound = "shooter_riffle",
|
||||||
|
particle = "default_gold_block.png",
|
||||||
})
|
})
|
||||||
itemstack:add_wear(656) -- 100 Rounds
|
itemstack:add_wear(656) -- 100 Rounds
|
||||||
return itemstack
|
return itemstack
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
minetest.register_tool("shooter:shotgun", {
|
||||||
|
description = "Shotgun",
|
||||||
|
inventory_image = "shooter_shotgun.png",
|
||||||
|
on_use = function(itemstack, user, pointed_thing)
|
||||||
|
shooter:fire_weapon(user, pointed_thing, {
|
||||||
|
range = 15,
|
||||||
|
tool_caps = {full_punch_interval=1.5, damage_groups={fleshy=4}},
|
||||||
|
groups = {cracky=3, snappy=2, crumbly=2, choppy=2, oddly_breakable_by_hand=1},
|
||||||
|
sound = "shooter_shotgun",
|
||||||
|
particle = "smoke_puff.png",
|
||||||
|
})
|
||||||
|
itemstack:add_wear(1311) -- 50 Rounds
|
||||||
|
return itemstack
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
output = "shooter:pistol",
|
output = "shooter:pistol",
|
||||||
recipe = {
|
recipe = {
|
||||||
|
@ -45,3 +64,12 @@ minetest.register_craft({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
minetest.register_craft({
|
||||||
|
output = "shooter:shotgun",
|
||||||
|
recipe = {
|
||||||
|
{"default:steel_ingot", "", ""},
|
||||||
|
{"", "default:steel_ingot", ""},
|
||||||
|
{"", "default:mese_crystal", "default:bronze_ingot"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
|
13
shooter.conf.example
Normal file
13
shooter.conf.example
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
-- Simple Shooter config example
|
||||||
|
|
||||||
|
-- Global Constants (defaults)
|
||||||
|
|
||||||
|
-- Particle texture used when target it hit
|
||||||
|
SHOOTER_EXPLOSION_TEXTURE = "shooter_hit.png"
|
||||||
|
|
||||||
|
-- Allow entities in multiplayer mode
|
||||||
|
SHOOTER_ALLOW_ENTITIES = false
|
||||||
|
|
||||||
|
-- Maximum object range, applies only if entities are allowed
|
||||||
|
SHOOTER_OBJECT_RANGE = 50
|
||||||
|
|
157
shooter.lua
157
shooter.lua
|
@ -1,34 +1,145 @@
|
||||||
shooter = {}
|
shooter = {}
|
||||||
|
|
||||||
local function in_range(r, x)
|
SHOOTER_EXPLOSION_TEXTURE = "shooter_hit.png"
|
||||||
local chance = r.a * (x * x) + r.b * x + r.c
|
SHOOTER_ALLOW_ENTITIES = true
|
||||||
if math.random(100) < chance then
|
SHOOTER_OBJECT_RANGE = 50
|
||||||
|
|
||||||
|
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||||
|
local input = io.open(modpath.."/shooter.conf", "r")
|
||||||
|
if input then
|
||||||
|
dofile(modpath.."/shooter.conf")
|
||||||
|
input:close()
|
||||||
|
input = nil
|
||||||
|
end
|
||||||
|
if minetest.is_singleplayer() == true then
|
||||||
|
SHOOTER_ALLOW_ENTITIES = true
|
||||||
|
end
|
||||||
|
local timer = 0
|
||||||
|
local shots = {}
|
||||||
|
|
||||||
|
local function spawn_particles(p, v, d, texture)
|
||||||
|
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
|
return true
|
||||||
end
|
end
|
||||||
|
if SHOOTER_ALLOW_ENTITIES == true then
|
||||||
|
local luaentity = object:get_luaentity()
|
||||||
|
if luaentity then
|
||||||
|
if minetest.registered_entities[luaentity.name] then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function shooter:fire_weapon(user, def)
|
function shooter:fire_weapon(user, pointed_thing, def)
|
||||||
|
local name = user:get_player_name()
|
||||||
|
if shots[name] then
|
||||||
|
if timer < shots[name] then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
shots[name] = timer + def.tool_caps.full_punch_interval
|
||||||
minetest.sound_play(def.sound, {object=user})
|
minetest.sound_play(def.sound, {object=user})
|
||||||
local target = {player=nil, distance=50}
|
local v1 = user:get_look_dir()
|
||||||
local p1 = user:getpos()
|
local p1 = user:getpos()
|
||||||
for _,player in ipairs(minetest.get_connected_players()) do
|
minetest.add_particle({x=p1.x, y=p1.y + 1.6, z=p1.z},
|
||||||
local p2 = player:getpos()
|
vector.multiply(v1, {x=30, y=30, z=30}),
|
||||||
if p1 and p2 then
|
{x=0, y=0, z=0}, 0.5, 0.25,
|
||||||
local x = vector.distance(p1, p2)
|
false, def.particle
|
||||||
p2.y = p2.y - 0.75
|
)
|
||||||
if x > 0 and x < target.distance and x < 50 then
|
if pointed_thing.type == "node" then
|
||||||
if in_range(def.range_func, x) == true then
|
local pos = minetest.get_pointed_thing_position(pointed_thing, false)
|
||||||
local v1 = user:get_look_dir()
|
local node = minetest.get_node(pos)
|
||||||
|
if not node then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local item = minetest.registered_items[node.name]
|
||||||
|
if not item.groups then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for k, v in pairs(def.groups) do
|
||||||
|
local level = item.groups[k] or 0
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local tiles = item.tiles
|
||||||
|
if tiles then
|
||||||
|
if tiles[1] then
|
||||||
|
spawn_particles({x=p1.x, y=p1.y + 0.75, z=p1.z},
|
||||||
|
v1, vector.distance(p1, pos), tiles[1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
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
|
||||||
|
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()
|
||||||
|
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 > 50 then
|
||||||
|
yx = 0.001 * (10 - x * 0.1)
|
||||||
|
else
|
||||||
|
yx = 0.000015 * (x * x) - 0.0015 * x + 0.042
|
||||||
|
end
|
||||||
|
local yy = yx * 3
|
||||||
local v2 = vector.normalize(vector.direction(p1, p2))
|
local v2 = vector.normalize(vector.direction(p1, p2))
|
||||||
local vd = vector.subtract(v1, v2)
|
local vd = vector.subtract(v1, v2)
|
||||||
local yx = 0.00002 * (x * x) - 0.002 * x + 0.05
|
|
||||||
local yy = yx * 3
|
|
||||||
if math.abs(vd.x) < yx and
|
if math.abs(vd.x) < yx and
|
||||||
math.abs(vd.z) < yx and
|
math.abs(vd.z) < yx and
|
||||||
math.abs(vd.y) < yy then
|
math.abs(vd.y) < yy then
|
||||||
target = {
|
target = {
|
||||||
player = player,
|
object = object,
|
||||||
distance = x,
|
distance = x,
|
||||||
direction = v1,
|
direction = v1,
|
||||||
pos1 = {x=p1.x, z=p1.z, y=p1.y+1},
|
pos1 = {x=p1.x, z=p1.z, y=p1.y+1},
|
||||||
|
@ -39,9 +150,11 @@ function shooter:fire_weapon(user, def)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if target.player then
|
if target.object then
|
||||||
if minetest.line_of_sight(target.pos1, target.pos2, 1) then
|
if minetest.line_of_sight(target.pos1, target.pos2, 1) then
|
||||||
target.player:punch(user, nil, def.tool_caps, target.direction)
|
target.object:punch(user, nil, def.tool_caps, target.direction)
|
||||||
|
spawn_particles(target.pos1, target.direction,
|
||||||
|
target.distance, SHOOTER_EXPLOSION_TEXTURE)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -50,3 +163,7 @@ minetest.register_on_joinplayer(function(player)
|
||||||
player:hud_set_flags({crosshair = true})
|
player:hud_set_flags({crosshair = true})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
minetest.register_globalstep(function(dtime)
|
||||||
|
timer = timer + dtime
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
Binary file not shown.
BIN
sounds/shooter_shotgun.ogg
Normal file
BIN
sounds/shooter_shotgun.ogg
Normal file
Binary file not shown.
BIN
textures/shooter_hit.png
Normal file
BIN
textures/shooter_hit.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 659 B |
BIN
textures/shooter_shotgun.png
Normal file
BIN
textures/shooter_shotgun.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 402 B |
Loading…
Reference in a new issue