This repository has been archived by the owner on Jul 1, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
264 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.