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

@ -7,6 +7,9 @@ License Textures: Stuart Jones - WTFPL
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)

View file

@ -1,38 +1,37 @@
Minetest Mod - Simple Shooter [shooter]
=======================================
Mod Version: 0.1.0
Mod Version: 0.2.0
Minetest Version: 0.4.8
Depends: default
An experimental first person shooter mod using vector mathematics instead of
physical projectile entities. This has a number of advantages along with a
number disadvantages.
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.
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
Fairly light weight
Not affected by chunk boundaries
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.
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
Slightly less realistic
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.
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.
Crafting
========
S = Steel Ingot [default:steel_ingot]
B = Bronze Ingot [default:bronze_ingot]
M = Mese Crystal [default:mese_crystal]
M = Mese Crystal [default:mese_crysytal]
Pistol: [shooter:pistol]
@ -52,3 +51,13 @@ Riffle: [shooter:riffle]
| | 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")
minetest.register_tool("shooter:pistol", {
description = "Pistol",
inventory_image = "shooter_pistol.png",
description = "Pistol",
inventory_image = "shooter_pistol.png",
on_use = function(itemstack, user, pointed_thing)
shooter:fire_weapon(user, {
range_func = {a=-0.1, b=-1.5, c=100},
tool_caps = {full_punch_interval=1.0, damage_groups={fleshy=3}},
shooter:fire_weapon(user, pointed_thing, {
range = 30,
tool_caps = {full_punch_interval=0.5, damage_groups={fleshy=1}},
groups = {snappy=3, oddly_breakable_by_hand=3},
sound = "shooter_pistol",
particle = "default_obsidian.png",
})
itemstack:add_wear(328) -- 200 Rounds
return itemstack
end,
})
minetest.register_tool("shooter:riffle", {
description = "Riffle",
inventory_image = "shooter_riffle.png",
description = "Riffle",
inventory_image = "shooter_riffle.png",
on_use = function(itemstack, user, pointed_thing)
shooter:fire_weapon(user, {
range_func = {a=-0.02, b=-0.6, c=100},
tool_caps = {full_punch_interval=1.0, damage_groups={fleshy=5}},
shooter:fire_weapon(user, pointed_thing, {
range = 80,
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",
particle = "default_gold_block.png",
})
itemstack:add_wear(656) -- 100 Rounds
return itemstack
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({
output = "shooter:pistol",
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 = {}
local function in_range(r, x)
local chance = r.a * (x * x) + r.b * x + r.c
if math.random(100) < chance then
SHOOTER_EXPLOSION_TEXTURE = "shooter_hit.png"
SHOOTER_ALLOW_ENTITIES = true
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
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
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})
local target = {player=nil, distance=50}
local v1 = user:get_look_dir()
local p1 = user:getpos()
for _,player in ipairs(minetest.get_connected_players()) do
local p2 = player: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 < 50 then
if in_range(def.range_func, x) == true then
local v1 = user:get_look_dir()
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,
false, def.particle
)
if pointed_thing.type == "node" then
local pos = minetest.get_pointed_thing_position(pointed_thing, false)
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 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
math.abs(vd.z) < yx and
math.abs(vd.y) < yy then
math.abs(vd.z) < yx and
math.abs(vd.y) < yy then
target = {
player = player,
object = object,
distance = x,
direction = v1,
pos1 = {x=p1.x, z=p1.z, y=p1.y+1},
@ -39,9 +150,11 @@ function shooter:fire_weapon(user, def)
end
end
end
if target.player then
if target.object 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
@ -50,3 +163,7 @@ minetest.register_on_joinplayer(function(player)
player:hud_set_flags({crosshair = true})
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