Big Update
api.lua: 58: made reload work for mutliple types of ammunition. 76: made fire function take spread and pellet value. 89: fixed bullets falling being slightly above the crosshair. 100: fire bullets for every pellet, and do spread. doing mutiple bullets here instead of mutiple fire() keeps the sound from sounding multiple times. 165: fixed burst guns getting destroyed when empty. 159: remove splash_fire, use mutliple pellets in gun def. 198-212: pass pellet and spread defs. 205: not sure what this was, it isn't an actual method and causes crash. works fine without. 236: non-automatic weapons would only shoot once, as their interval[name] would not increase with time. remaining lines: passing defs and giving default values. I think the other files are fairily readable, so I won't document those changes unless needed.
This commit is contained in:
parent
12daaffc35
commit
6ddb4a2fab
8
API.md
8
API.md
@ -71,10 +71,6 @@ This file aims to document all the internal and external methods of the `gunslin
|
|||||||
|
|
||||||
- Helper method to fire in burst mode. Takes the same arguments as `fire`.
|
- Helper method to fire in burst mode. Takes the same arguments as `fire`.
|
||||||
|
|
||||||
### `splash_fire(stack, player)`
|
|
||||||
|
|
||||||
- Helper method to fire shotgun-style. Takes the same arguments as `fire`.
|
|
||||||
|
|
||||||
### `on_step(dtime)`
|
### `on_step(dtime)`
|
||||||
|
|
||||||
- This is the globalstep callback that's responsible for firing automatic guns.
|
- This is the globalstep callback that's responsible for firing automatic guns.
|
||||||
@ -85,15 +81,17 @@ This file aims to document all the internal and external methods of the `gunslin
|
|||||||
|
|
||||||
- `itemdef` [table]: Item definition table passed to `minetest.register_item`.
|
- `itemdef` [table]: Item definition table passed to `minetest.register_item`.
|
||||||
- Note that `on_use`, `on_place`, and `on_secondary_use` will be overridden.
|
- Note that `on_use`, `on_place`, and `on_secondary_use` will be overridden.
|
||||||
|
- `ammo` [itemstring]: What type of ammo to use when reloading.
|
||||||
- `clip_size` [number]: Number of bullets per-clip.
|
- `clip_size` [number]: Number of bullets per-clip.
|
||||||
- `fire_rate` [number]: Number of shots per-second.
|
- `fire_rate` [number]: Number of shots per-second.
|
||||||
- `range` [number]: Range of fire in number of nodes.
|
- `range` [number]: Range of fire in number of nodes.
|
||||||
|
- `spread` [number]: How much bullets will spread away from the cursor.(0 is nothing, 1000 is anywhere in front of the player (I think))
|
||||||
- `base_dmg` [number]: Base amount of damage dealt in HP.
|
- `base_dmg` [number]: Base amount of damage dealt in HP.
|
||||||
|
- `pellets` [number]: Number of bullets per shot, used for shotguns.
|
||||||
- `mode` [string]: Firing mode.
|
- `mode` [string]: Firing mode.
|
||||||
- `"manual"`: One shot per-click, but requires manual loading for every round; aka Bolt-action.
|
- `"manual"`: One shot per-click, but requires manual loading for every round; aka Bolt-action.
|
||||||
- `"semi-automatic"`: One shot per-click.
|
- `"semi-automatic"`: One shot per-click.
|
||||||
- `"burst"`: Multiple rounds per-click. Can be set by defining `burst` field. Defaults to 3.
|
- `"burst"`: Multiple rounds per-click. Can be set by defining `burst` field. Defaults to 3.
|
||||||
- `"splash"`: **(WARNING: Unimplemented)** Shotgun-style pellets; one burst per-click.
|
|
||||||
- `"automatic"`: Fully automatic; shoots as long as primary button is held down.
|
- `"automatic"`: Fully automatic; shoots as long as primary button is held down.
|
||||||
- `"hybrid"`: Same as `"automatic"`, but switches to `"burst"` mode when scope view is toggled.
|
- `"hybrid"`: Same as `"automatic"`, but switches to `"burst"` mode when scope view is toggled.
|
||||||
|
|
||||||
|
133
api.lua
133
api.lua
@ -55,12 +55,15 @@ end
|
|||||||
|
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
local function reload(stack, player)
|
local function reload(stack, player, ammo)
|
||||||
-- Check for ammo
|
-- Check for ammo
|
||||||
|
if not ammo then
|
||||||
|
return stack
|
||||||
|
end
|
||||||
local inv = player:get_inventory()
|
local inv = player:get_inventory()
|
||||||
if inv:contains_item("main", "gunslinger:ammo") then
|
if inv:contains_item("main", ammo) then
|
||||||
-- Ammo exists, reload and reset wear
|
-- Ammo exists, reload and reset wear
|
||||||
inv:remove_item("main", "gunslinger:ammo")
|
inv:remove_item("main", ammo)
|
||||||
stack:set_wear(0)
|
stack:set_wear(0)
|
||||||
else
|
else
|
||||||
-- No ammo, play click sound
|
-- No ammo, play click sound
|
||||||
@ -70,7 +73,7 @@ local function reload(stack, player)
|
|||||||
return stack
|
return stack
|
||||||
end
|
end
|
||||||
|
|
||||||
local function fire(stack, player)
|
local function fire(stack, player, base_spread, pellets)
|
||||||
-- Workaround to prevent function from running if stack is nil
|
-- Workaround to prevent function from running if stack is nil
|
||||||
if not stack then
|
if not stack then
|
||||||
return
|
return
|
||||||
@ -83,51 +86,59 @@ local function fire(stack, player)
|
|||||||
|
|
||||||
local wear = stack:get_wear()
|
local wear = stack:get_wear()
|
||||||
if wear == max_wear then
|
if wear == max_wear then
|
||||||
return reload(stack, player)
|
return reload(stack, player, def.ammo)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Play gunshot sound
|
-- Play gunshot sound
|
||||||
play_sound(def.fire_sound)
|
play_sound(def.fire_sound)
|
||||||
|
|
||||||
-- Take aim
|
-- Take aim
|
||||||
local eye_offset = {x = 0, y = 1.625, z = 0} --player:get_eye_offset().offset_first
|
local eye_offset = {x = 0, y = 1.45, z = 0} --player:get_eye_offset().offset_first
|
||||||
local dir = player:get_look_dir()
|
local dir = player:get_look_dir()
|
||||||
local p1 = vector.add(player:get_pos(), eye_offset)
|
local p1 = vector.add(player:get_pos(), eye_offset)
|
||||||
p1 = vector.add(p1, dir)
|
p1 = vector.add(p1, dir)
|
||||||
local p2 = vector.add(p1, vector.multiply(dir, def.range))
|
if not pellets then pellets = 1 end
|
||||||
local ray = minetest.raycast(p1, p2)
|
for i = 1, pellets do
|
||||||
local pointed = ray:next()
|
if base_spread then
|
||||||
|
dir.x = dir.x + (math.random(-1*base_spread, base_spread))*.001
|
||||||
-- Projectile particle
|
dir.y = dir.y + (math.random(-1*base_spread, base_spread))*.001
|
||||||
minetest.add_particle({
|
dir.z = dir.z + (math.random(-1*base_spread, base_spread))*.001
|
||||||
pos = p1,
|
|
||||||
velocity = vector.multiply(dir, 400),
|
|
||||||
acceleration = {x = 0, y = 0, z = 0},
|
|
||||||
expirationtime = 2,
|
|
||||||
size = 1,
|
|
||||||
collisiondetection = true,
|
|
||||||
collision_removal = true,
|
|
||||||
object_collision = true,
|
|
||||||
glow = 5
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Fire!
|
|
||||||
if pointed and pointed.type == "object" then
|
|
||||||
local target = pointed.ref
|
|
||||||
local point = pointed.intersection_point
|
|
||||||
local dmg = base_dmg * def.dmg_mult
|
|
||||||
|
|
||||||
-- Add 50% damage if headshot
|
|
||||||
if point.y > target:get_pos().y + 1.5 then
|
|
||||||
dmg = dmg * 1.5
|
|
||||||
end
|
end
|
||||||
|
local p2 = vector.add(p1, vector.multiply(dir, def.range))
|
||||||
|
local ray = minetest.raycast(p1, p2)
|
||||||
|
local pointed = ray:next()
|
||||||
|
|
||||||
-- Add 20% more damage if player using scope
|
-- Projectile particle
|
||||||
if scope_overlay[player:get_player_name()] then
|
minetest.add_particle({
|
||||||
dmg = dmg * 1.2
|
pos = p1,
|
||||||
|
velocity = vector.multiply(dir, 400),
|
||||||
|
acceleration = {x = 0, y = 0, z = 0},
|
||||||
|
expirationtime = 2,
|
||||||
|
size = 1,
|
||||||
|
collisiondetection = true,
|
||||||
|
collision_removal = true,
|
||||||
|
object_collision = true,
|
||||||
|
glow = 5
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Fire!
|
||||||
|
if pointed and pointed.type == "object" then
|
||||||
|
local target = pointed.ref
|
||||||
|
local point = pointed.intersection_point
|
||||||
|
local dmg = def.base_dmg * def.dmg_mult
|
||||||
|
|
||||||
|
-- Add 50% damage if headshot
|
||||||
|
if point.y > target:get_pos().y + 1.5 then
|
||||||
|
dmg = dmg * 1.5
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add 20% more damage if player using scope
|
||||||
|
if scope_overlay[player:get_player_name()] then
|
||||||
|
dmg = dmg * 1.2
|
||||||
|
end
|
||||||
|
|
||||||
|
target:set_hp(target:get_hp() - dmg)
|
||||||
end
|
end
|
||||||
|
|
||||||
target:set_hp(target:get_hp() - dmg)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Update wear
|
-- Update wear
|
||||||
@ -141,25 +152,26 @@ local function fire(stack, player)
|
|||||||
return stack
|
return stack
|
||||||
end
|
end
|
||||||
|
|
||||||
local function burst_fire(stack, player)
|
local function burst_fire(stack, player, base_spread, pellets)
|
||||||
local def = gunslinger.get_def(stack:get_name())
|
local def = gunslinger.get_def(stack:get_name())
|
||||||
local burst = def.burst or 3
|
local burst = def.burst or 3
|
||||||
for i = 1, burst do
|
for i = 1, burst do
|
||||||
minetest.after(i / def.fire_rate, function(st)
|
minetest.after(i / def.fire_rate, function(st)
|
||||||
fire(st, player)
|
fire(st, player, base_spread, pellets)
|
||||||
end, stack)
|
end, stack)
|
||||||
end
|
end
|
||||||
-- Manually add wear to stack, as functions can't return
|
-- Manually add wear to stack, as functions can't return
|
||||||
-- values from within minetest.after
|
-- values from within minetest.after
|
||||||
stack:add_wear(def.unit_wear * burst)
|
local wear = stack:get_wear()
|
||||||
|
wear = wear + def.unit_wear*3
|
||||||
|
if wear > max_wear then
|
||||||
|
wear = max_wear
|
||||||
|
end
|
||||||
|
stack:set_wear(wear)
|
||||||
|
|
||||||
return stack
|
return stack
|
||||||
end
|
end
|
||||||
|
|
||||||
local function splash_fire(stack, player)
|
|
||||||
-- TODO
|
|
||||||
end
|
|
||||||
|
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
local function on_lclick(stack, player)
|
local function on_lclick(stack, player)
|
||||||
@ -183,26 +195,23 @@ local function on_lclick(stack, player)
|
|||||||
elseif def.mode == "hybrid"
|
elseif def.mode == "hybrid"
|
||||||
and not automatic[name] then
|
and not automatic[name] then
|
||||||
if scope_overlay[name] then
|
if scope_overlay[name] then
|
||||||
stack = burst_fire(stack, player)
|
stack = burst_fire(stack, player, def.base_spread, def.pellets)
|
||||||
else
|
else
|
||||||
add_auto(name, def)
|
add_auto(name, def)
|
||||||
end
|
end
|
||||||
elseif def.mode == "burst" then
|
elseif def.mode == "burst" then
|
||||||
stack = burst_fire(stack, player)
|
stack = burst_fire(stack, player, def.base_spread, def.pellets)
|
||||||
elseif def.mode == "splash" then
|
|
||||||
stack = splash_fire(stack, player)
|
|
||||||
elseif def.mode == "semi-automatic" then
|
elseif def.mode == "semi-automatic" then
|
||||||
stack = fire(stack, player)
|
stack = fire(stack, player, def.base_spread, def.pellets)
|
||||||
elseif def.mode == "manual" then
|
elseif def.mode == "manual" then
|
||||||
local meta = stack:get_meta()
|
local meta = stack:get_meta()
|
||||||
if meta:contains("loaded") then
|
if meta:contains("loaded") then
|
||||||
stack = fire(stack, player)
|
stack = fire(stack, player, def.base_spread, def.pellets)
|
||||||
meta:set_string("loaded", "")
|
meta:set_string("loaded", "")
|
||||||
else
|
else
|
||||||
stack = reload(stack, player)
|
stack = reload(stack, player, def.ammo)
|
||||||
meta:set_string("loaded", "true")
|
meta:set_string("loaded", "true")
|
||||||
end
|
end
|
||||||
stack:set_meta(meta)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return stack
|
return stack
|
||||||
@ -224,21 +233,21 @@ end
|
|||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
local function on_step(dtime)
|
local function on_step(dtime)
|
||||||
|
for name in pairs(interval) do
|
||||||
|
interval[name] = interval[name] + dtime
|
||||||
|
end
|
||||||
for name, info in pairs(automatic) do
|
for name, info in pairs(automatic) do
|
||||||
local player = minetest.get_player_by_name(name)
|
local player = minetest.get_player_by_name(name)
|
||||||
if not player then
|
if not player then
|
||||||
automatic[name] = nil
|
automatic[name] = nil
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
interval[name] = interval[name] + dtime
|
|
||||||
if interval[name] < info.def.unit_time then
|
if interval[name] < info.def.unit_time then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if player:get_player_control().LMB then
|
if player:get_player_control().LMB then
|
||||||
-- If LMB pressed, fire
|
-- If LMB pressed, fire
|
||||||
info.stack = fire(info.stack, player)
|
info.stack = fire(info.stack, player, info.def.base_spread, info.def.pellets)
|
||||||
player:set_wielded_item(info.stack)
|
player:set_wielded_item(info.stack)
|
||||||
automatic[name].stack = info.stack
|
automatic[name].stack = info.stack
|
||||||
interval[name] = 0
|
interval[name] = 0
|
||||||
@ -284,9 +293,8 @@ function gunslinger.register_gun(name, def)
|
|||||||
def[name] = val
|
def[name] = val
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Abort when making use of unimplemented features
|
-- Abort when making use of unimplemented features
|
||||||
if def.mode == "splash" or def.zoom then
|
if def.zoom then
|
||||||
error("register_gun: Unimplemented feature!")
|
error("register_gun: Unimplemented feature!")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -308,9 +316,14 @@ function gunslinger.register_gun(name, def)
|
|||||||
return entity:on_rightclick(player) or on_rclick(stack, player)
|
return entity:on_rightclick(player) or on_rclick(stack, player)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if not def.pellets then
|
||||||
|
def.pellets = 1
|
||||||
|
end
|
||||||
|
if not def.dmg_mult then
|
||||||
|
def.dmg_mult = 1
|
||||||
|
end
|
||||||
if not def.fire_sound then
|
if not def.fire_sound then
|
||||||
def.fire_sound = (def.mode ~= "splash")
|
def.fire_sound = (def.pellets == 1)
|
||||||
and "gunslinger_fire1" or "gunslinger_fire2"
|
and "gunslinger_fire1" or "gunslinger_fire2"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
6
guns.lua
6
guns.lua
@ -10,5 +10,7 @@ gunslinger.register_gun("gunslinger:cheetah", {
|
|||||||
base_dmg = 1,
|
base_dmg = 1,
|
||||||
fire_rate = 6,
|
fire_rate = 6,
|
||||||
clip_size = 50,
|
clip_size = 50,
|
||||||
range = 80
|
range = 80,
|
||||||
})
|
base_spread = 20,
|
||||||
|
ammo = "gunslinger:ammo"
|
||||||
|
})
|
5
init.lua
5
init.lua
@ -6,3 +6,8 @@ dofile(modpath .. "api.lua")
|
|||||||
if not minetest.settings:get_bool("gunslinger.disable_builtin") then
|
if not minetest.settings:get_bool("gunslinger.disable_builtin") then
|
||||||
dofile(modpath .. "guns.lua")
|
dofile(modpath .. "guns.lua")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
minetest.register_craftitem("gunslinger:ammo", {
|
||||||
|
description = "Ammo",
|
||||||
|
inventory_image = "gunslinger_ammo.png",
|
||||||
|
})
|
BIN
textures/gunslinger_ammo.png
Normal file
BIN
textures/gunslinger_ammo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 257 B |
Loading…
x
Reference in New Issue
Block a user