Add standalone API
This commit is contained in:
parent
6db582f912
commit
2659d195a1
62
README.md
Normal file
62
README.md
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# Gunslinger
|
||||||
|
|
||||||
|
This mod provides an API to add a variety of realistic and enjoyable guns to Minetest. A variety of different guns are provided with the mod, and can be disabled if required.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
- **Code**: MIT
|
||||||
|
- **Media**: CC0
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Gunslinger makes use of gun _types_ in order to ease registration of similar guns. A `type` is made up of a name and a table of default values to be applied to all guns registered with that type. At least one type needs to be registered in order to register guns.
|
||||||
|
|
||||||
|
`Raycast` is used to find target in line-of-sight, and all objects take damage. Damage is calculated as detailed in [Damage calculation](###Damage-calculation)
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### Damage calculation
|
||||||
|
|
||||||
|
Weapon damage = `def.type.base_dmg * def.dmg_mult`
|
||||||
|
|
||||||
|
If headshot, damage is increased by 50%
|
||||||
|
|
||||||
|
If shooter was looking through scope, damage is increased by 20%
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
|
||||||
|
#### `gunslinger.register_type(name, def)`
|
||||||
|
|
||||||
|
- Registers a def table for the type `name`.
|
||||||
|
- The def table contains the default values for all the guns registered with this type. e.g. `style_of_fire`, `enable_scope`, etc.
|
||||||
|
|
||||||
|
#### `gunslinger.register_gun(name, def)`
|
||||||
|
|
||||||
|
- Registers a gun with the type `def.type`, and assigns the type defaults to the gun.
|
||||||
|
- The def table contains gun-specific values like `clip_size`, `wield_image`, `scope_image`, etc.
|
||||||
|
- Default type values can also be overridden per gun by just including that value in the def table.
|
||||||
|
|
||||||
|
### Definition table fields used by API methods
|
||||||
|
|
||||||
|
#### Fields passed to `gunslinger.register_type`
|
||||||
|
|
||||||
|
- `type` [string]: Name of a valid type (i.e. type registered by `gunslinger.register_type`)
|
||||||
|
- `style_of_fire` [string]: Sets style of fire
|
||||||
|
- `"manual"`: One shot per-click.
|
||||||
|
- `"burst"`: Three shots per-click.
|
||||||
|
- `"splash"`: Shotgun-style pellets; one burst per-click.
|
||||||
|
- `"automatic"`: Fully automatic; shoots as long as primary button is held down.
|
||||||
|
- `"semi-automatic"`: Same as `"automatic"`, but switches to `"burst"` when scope view is toggled.
|
||||||
|
- `scope` [string]: Sets style of scope.
|
||||||
|
- `"none"`: Default. No scope functionality.
|
||||||
|
- `"ironsight"`: Ironsight, without zoom. Unrestricted peripheral vision
|
||||||
|
- `"scope"`: Proper scope, with zoom. Restricted peripheral vision.
|
||||||
|
- `base_dmg` [number]: Base amount of damage dealt in HP.
|
||||||
|
|
||||||
|
#### Fields passed to `gunslinger.register_gun`
|
||||||
|
|
||||||
|
- `itemdef` [table]: Item definition table passed to `minetest.register_item`.
|
||||||
|
- `scope_overlay` [string]: Name of scope overlay texture. Must be provided if `scope` is defined. Overlay texture would be stretched across the screen.
|
||||||
|
- `clip_size` [number]: Number of bullets per-clip.
|
||||||
|
- `fire_rate` [number]: Number of shots per-second.
|
||||||
|
- `dmg_mult` [number]: Damage multiplier value. Final damage is calculated by multiplying `dmg_mult` with the type's `base_dmg`.
|
191
api.lua
Normal file
191
api.lua
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
gunslinger = {}
|
||||||
|
local guns = {}
|
||||||
|
local types = {}
|
||||||
|
local automatic = {}
|
||||||
|
local scope_overlay = {}
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Internal API functions
|
||||||
|
--
|
||||||
|
|
||||||
|
local function get_def(name)
|
||||||
|
return guns[name]
|
||||||
|
end
|
||||||
|
|
||||||
|
local function play_sound(sound, player)
|
||||||
|
minetest.sound_play(sound, {
|
||||||
|
object = player,
|
||||||
|
loop = false
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function show_scope(player, def)
|
||||||
|
if not player then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Create HUD image
|
||||||
|
scope_overlay[player:get_player_name()] = player:hud_add({
|
||||||
|
hud_elem_type = "image",
|
||||||
|
position = {x = 0.5, y = 0.5},
|
||||||
|
alignment = {x = 0, y = 0},
|
||||||
|
text = def.scope_overlay
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function hide_scope(player)
|
||||||
|
if not player then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local name = player:get_player_name()
|
||||||
|
player:hud_remove(scope_overlay[name])
|
||||||
|
scope_overlay[name] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function fire(stack, player)
|
||||||
|
-- Take aim
|
||||||
|
local def = get_def(stack:get_name())
|
||||||
|
local eye_offset = player:get_eye_offset().offset_first
|
||||||
|
local p1 = vector.add(player:get_pos(), eye_offset)
|
||||||
|
p1 = vector.add(p1, player:get_look_dir())
|
||||||
|
local p2 = vector.add(p1, vector.multiply(player:get_look_dir(), max_dist))
|
||||||
|
local ray = minetest.raycast(p1, p2)
|
||||||
|
local pointed = ray:next()
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
|
||||||
|
-- Update wear
|
||||||
|
wear = wear + def.wear_step
|
||||||
|
stack:set_wear(wear)
|
||||||
|
|
||||||
|
return stack
|
||||||
|
end
|
||||||
|
|
||||||
|
local function reload(stack, player)
|
||||||
|
-- Check for ammo
|
||||||
|
local inv = player:get_inventory()
|
||||||
|
if inv:contains_item("main", "gunslinger:ammo") then
|
||||||
|
-- Ammo exists, reload and reset wear
|
||||||
|
inv:remove_item("main", "gunslinger:ammo")
|
||||||
|
stack:set_wear(0)
|
||||||
|
else
|
||||||
|
-- No ammo, play click sound
|
||||||
|
play_sound("gunslinger_ooa", player)
|
||||||
|
end
|
||||||
|
|
||||||
|
return stack
|
||||||
|
end
|
||||||
|
|
||||||
|
local function on_lclick(stack, player)
|
||||||
|
local wear = stack:get_wear()
|
||||||
|
local def = get_def(stack:get_name())
|
||||||
|
if wear >= 65535 then
|
||||||
|
--Reload
|
||||||
|
stack = reload(stack, player)
|
||||||
|
else
|
||||||
|
if def.style_of_fire == "automatic" then
|
||||||
|
automatic[player:get_player_name()] = {
|
||||||
|
stack = stack,
|
||||||
|
def = def
|
||||||
|
}
|
||||||
|
else
|
||||||
|
stack = fire(stack, player)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return stack
|
||||||
|
end
|
||||||
|
|
||||||
|
local function on_rclick(stack, player)
|
||||||
|
local def = get_def(stack:get_name())
|
||||||
|
local hud = scope_overlay[player:get_player_name()]
|
||||||
|
if hud then
|
||||||
|
hide_scope(player)
|
||||||
|
else
|
||||||
|
show_scope(player, def)
|
||||||
|
end
|
||||||
|
|
||||||
|
return stack
|
||||||
|
end
|
||||||
|
|
||||||
|
local function verify_def(def, is_gun)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Globalstep to handle firing of automatic guns
|
||||||
|
minetest.register_globalstep(function(dtime)
|
||||||
|
for name, info in pairs(automatic) do
|
||||||
|
local player = minetest.get_player_by_name(name)
|
||||||
|
if player:get_player_control().LMB then
|
||||||
|
-- If LMB pressed, fire
|
||||||
|
info.stack = fire(info.stack, player)
|
||||||
|
else
|
||||||
|
-- If LMB not pressed, remove player from list
|
||||||
|
automatic[name] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Ammo
|
||||||
|
--
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Gun registration
|
||||||
|
--
|
||||||
|
|
||||||
|
function gunslinger.register_type(name, def)
|
||||||
|
assert(type(name) == "string" and type(def) == "table",
|
||||||
|
"gunslinger.register_type: Invalid params!")
|
||||||
|
assert(not types[name], "gunslinger.register_type:"
|
||||||
|
.. " Attempt to register a type with an existing name!")
|
||||||
|
|
||||||
|
types[name] = def
|
||||||
|
end
|
||||||
|
|
||||||
|
function gunslinger.register_gun(name, def)
|
||||||
|
assert(type(name) == "string" and type(def) == "table",
|
||||||
|
"gunslinger.register_type: Invalid params!")
|
||||||
|
assert(not guns[name], "gunslinger.register_gun:"
|
||||||
|
.. " Attempt to register a gun with an existing name!")
|
||||||
|
assert(types[def.type], "gunslinger.register_gun: Attempt to"
|
||||||
|
.. " register gun of non-existent type (" .. def.type .. ")!")
|
||||||
|
|
||||||
|
def.itemdef.on_use = on_lclick
|
||||||
|
def.itemdef.on_secondary_use = on_rclick
|
||||||
|
def.itemdef.on_place = function(stack, player, pointed)
|
||||||
|
if pointed.type == "node" then
|
||||||
|
local node = minetest.get_node_or_nil(pointed.under)
|
||||||
|
local nodedef = minetest.registered_items[node.name]
|
||||||
|
return nodedef.on_rightclick or on_rclick(stack, player)
|
||||||
|
elseif pointed.type == "object" then
|
||||||
|
local entity = pointed.ref:get_luaentity()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def.style_of_fire = def.style_of_fire or def.type.style_of_fire
|
||||||
|
|
||||||
|
def.wear = math.ceil(65534 / def.clip_size)
|
||||||
|
guns[name] = def
|
||||||
|
minetest.register_tool(name, def.itemdef)
|
||||||
|
end
|
11
init.lua
Normal file
11
init.lua
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
local modpath = minetest.get_modpath("gunslinger") .. "/"
|
||||||
|
|
||||||
|
-- Import API
|
||||||
|
dofile(modpath .. "api.lua")
|
||||||
|
|
||||||
|
if not minetest.settings:get_bool("gunslinger.disable_builtin") then
|
||||||
|
dofile(modpath .. "assault_rifle.lua")
|
||||||
|
dofile(modpath .. "shotgun.lua")
|
||||||
|
dofile(modpath .. "sniper_rifle.lua")
|
||||||
|
dofile(modpath .. "handgun.lua")
|
||||||
|
end
|
BIN
sounds/gunslinger_fire1.ogg
Normal file
BIN
sounds/gunslinger_fire1.ogg
Normal file
Binary file not shown.
BIN
sounds/gunslinger_fire2.ogg
Normal file
BIN
sounds/gunslinger_fire2.ogg
Normal file
Binary file not shown.
BIN
textures/gunslinger_cheetah.png
Normal file
BIN
textures/gunslinger_cheetah.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 262 KiB |
Loading…
x
Reference in New Issue
Block a user