Add particle effects and singleplayer support

This commit is contained in:
stujones11 2013-12-05 20:28:32 +00:00
parent aeb1dac34d
commit b77e50e56f
9 changed files with 219 additions and 49 deletions

View file

@ -8,5 +8,8 @@ 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)

View file

@ -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 |
+---+---+---+

View file

@ -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
View 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

View file

@ -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

Binary file not shown.

BIN
textures/shooter_hit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B