Reorganise game into modpacks
202
mods/pvp/dropondie/LICENSE
Normal file
|
@ -0,0 +1,202 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
4
mods/pvp/dropondie/README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
minetest-dropondie
|
||||
==================
|
||||
|
||||
Minecraft-like drop-all-items-on-die mod
|
1
mods/pvp/dropondie/depends.txt
Normal file
|
@ -0,0 +1 @@
|
|||
ctf_map
|
1
mods/pvp/dropondie/description.txt
Normal file
|
@ -0,0 +1 @@
|
|||
With this mod, players will drop all their items in their inventory on the ground when they die.
|
52
mods/pvp/dropondie/init.lua
Normal file
|
@ -0,0 +1,52 @@
|
|||
local blacklist_drop = {
|
||||
"default:pick_wood",
|
||||
"default:sword_wood",
|
||||
"default:ax_wood"
|
||||
}
|
||||
|
||||
local function drop(pos, itemstack)
|
||||
local it = itemstack:take_item(itemstack:get_count())
|
||||
local sname = it:get_name()
|
||||
|
||||
for _, item in pairs(blacklist_drop) do
|
||||
if sname == item then
|
||||
minetest.log("error", "Not dropping " .. item)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local obj = minetest.add_item(pos, it)
|
||||
|
||||
if obj then
|
||||
obj:setvelocity({x=math.random(-1,1), y=5, z=math.random(-1,1)})
|
||||
|
||||
local remi = minetest.settings:get("remove_items")
|
||||
if minetest.is_yes(remi) then
|
||||
obj:remove()
|
||||
end
|
||||
|
||||
end
|
||||
return itemstack
|
||||
end
|
||||
|
||||
local function drop_list(pos, inv, list)
|
||||
for i = 1, inv:get_size(list) do
|
||||
drop(pos, inv:get_stack(list, i))
|
||||
inv:set_stack(list, i, nil)
|
||||
end
|
||||
end
|
||||
|
||||
local function drop_all(player)
|
||||
local pos = player:getpos()
|
||||
pos.y = math.floor(pos.y + 0.5)
|
||||
|
||||
local inv = player:get_inventory()
|
||||
for _, item in pairs(give_initial_stuff.get_stuff()) do
|
||||
inv:remove_item("main", ItemStack(item))
|
||||
end
|
||||
drop_list(pos, inv, "main")
|
||||
drop_list(pos, inv, "craft")
|
||||
end
|
||||
|
||||
minetest.register_on_dieplayer(drop_all)
|
||||
minetest.register_on_leaveplayer(drop_all)
|
88
mods/pvp/gauges/init.lua
Normal file
|
@ -0,0 +1,88 @@
|
|||
-- Adds health bars above players.
|
||||
-- Code by 4aiman, textures by Calinou. Licensed under CC0.
|
||||
|
||||
gauges = {}
|
||||
|
||||
local hp_bar = {
|
||||
physical = false,
|
||||
collisionbox = {x = 0, y = 0, z = 0},
|
||||
visual = "sprite",
|
||||
textures = {"20.png"}, -- The texture is changed later in the code.
|
||||
visual_size = {x = 1.5, y = 0.09375, z = 1.5}, -- Y value is (1 / 16) * 1.5.
|
||||
wielder = nil,
|
||||
}
|
||||
|
||||
function vector.sqdist(a, b)
|
||||
local dx = a.x - b.x
|
||||
local dy = a.y - b.y
|
||||
local dz = a.z - b.z
|
||||
return dx*dx + dy*dy + dz*dz
|
||||
end
|
||||
|
||||
function hp_bar:on_step(dtime)
|
||||
local wielder = self.wielder and minetest.get_player_by_name(self.wielder)
|
||||
if wielder == nil then
|
||||
print("[gauges] Gauge removed as null wielder! " .. dump(self.wielder))
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
if vector.sqdist(wielder:get_pos(), self.object:get_pos()) > 3 then
|
||||
print("[gauges] Gauge removed as not attached! " .. dump(self.wielder))
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
local hp = wielder:get_hp()
|
||||
local breath = wielder:get_breath()
|
||||
self.object:set_properties({
|
||||
textures = {
|
||||
"health_" .. tostring(hp) .. ".png^breath_" .. tostring(breath) .. ".png",
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_entity("gauges:hp_bar", hp_bar)
|
||||
|
||||
function gauges.add_HP_gauge(name)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if player then
|
||||
local pos = player:get_pos()
|
||||
local ent = minetest.add_entity(pos, "gauges:hp_bar")
|
||||
if ent ~= nil then
|
||||
ent:set_attach(player, "", {x = 0, y = 9, z = 0}, {x = 0, y = 0, z = 0})
|
||||
ent = ent:get_luaentity()
|
||||
ent.wielder = player:get_player_name()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- If health_bars not defined or set to true
|
||||
if minetest.settings:get_bool("health_bars") ~= false and
|
||||
minetest.settings:get_bool("enable_damage") then
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
minetest.after(2, gauges.add_HP_gauge, player:get_player_name())
|
||||
end)
|
||||
end
|
||||
|
||||
function gauges.check_gauges()
|
||||
for _, player in pairs(minetest.get_connected_players()) do
|
||||
local pname = player:get_player_name()
|
||||
local found = false
|
||||
local objects = minetest.get_objects_inside_radius(player:get_pos(), 1)
|
||||
for _, object in pairs(objects) do
|
||||
local le = object:get_luaentity()
|
||||
if le and le.wielder == pname then
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not found then
|
||||
print("Gauge not found!")
|
||||
gauges.add_HP_gauge(pname)
|
||||
end
|
||||
end
|
||||
minetest.after(9.3, gauges.check_gauges)
|
||||
end
|
||||
minetest.after(2, gauges.check_gauges)
|
BIN
mods/pvp/gauges/textures/breath_0.png
Normal file
After Width: | Height: | Size: 131 B |
BIN
mods/pvp/gauges/textures/breath_1.png
Normal file
After Width: | Height: | Size: 170 B |
BIN
mods/pvp/gauges/textures/breath_10.png
Normal file
After Width: | Height: | Size: 124 B |
BIN
mods/pvp/gauges/textures/breath_11.png
Normal file
After Width: | Height: | Size: 68 B |
BIN
mods/pvp/gauges/textures/breath_2.png
Normal file
After Width: | Height: | Size: 171 B |
BIN
mods/pvp/gauges/textures/breath_3.png
Normal file
After Width: | Height: | Size: 167 B |
BIN
mods/pvp/gauges/textures/breath_4.png
Normal file
After Width: | Height: | Size: 162 B |
BIN
mods/pvp/gauges/textures/breath_5.png
Normal file
After Width: | Height: | Size: 155 B |
BIN
mods/pvp/gauges/textures/breath_6.png
Normal file
After Width: | Height: | Size: 151 B |
BIN
mods/pvp/gauges/textures/breath_65535.png
Normal file
After Width: | Height: | Size: 68 B |
BIN
mods/pvp/gauges/textures/breath_7.png
Normal file
After Width: | Height: | Size: 146 B |
BIN
mods/pvp/gauges/textures/breath_8.png
Normal file
After Width: | Height: | Size: 140 B |
BIN
mods/pvp/gauges/textures/breath_9.png
Normal file
After Width: | Height: | Size: 130 B |
BIN
mods/pvp/gauges/textures/health_0.png
Normal file
After Width: | Height: | Size: 68 B |
BIN
mods/pvp/gauges/textures/health_1.png
Normal file
After Width: | Height: | Size: 100 B |
BIN
mods/pvp/gauges/textures/health_10.png
Normal file
After Width: | Height: | Size: 108 B |
BIN
mods/pvp/gauges/textures/health_11.png
Normal file
After Width: | Height: | Size: 108 B |
BIN
mods/pvp/gauges/textures/health_12.png
Normal file
After Width: | Height: | Size: 108 B |
BIN
mods/pvp/gauges/textures/health_13.png
Normal file
After Width: | Height: | Size: 108 B |
BIN
mods/pvp/gauges/textures/health_14.png
Normal file
After Width: | Height: | Size: 108 B |
BIN
mods/pvp/gauges/textures/health_15.png
Normal file
After Width: | Height: | Size: 108 B |
BIN
mods/pvp/gauges/textures/health_16.png
Normal file
After Width: | Height: | Size: 108 B |
BIN
mods/pvp/gauges/textures/health_17.png
Normal file
After Width: | Height: | Size: 108 B |
BIN
mods/pvp/gauges/textures/health_18.png
Normal file
After Width: | Height: | Size: 108 B |
BIN
mods/pvp/gauges/textures/health_19.png
Normal file
After Width: | Height: | Size: 106 B |
BIN
mods/pvp/gauges/textures/health_2.png
Normal file
After Width: | Height: | Size: 105 B |
BIN
mods/pvp/gauges/textures/health_20.png
Normal file
After Width: | Height: | Size: 104 B |
BIN
mods/pvp/gauges/textures/health_3.png
Normal file
After Width: | Height: | Size: 108 B |
BIN
mods/pvp/gauges/textures/health_4.png
Normal file
After Width: | Height: | Size: 108 B |
BIN
mods/pvp/gauges/textures/health_5.png
Normal file
After Width: | Height: | Size: 108 B |
BIN
mods/pvp/gauges/textures/health_6.png
Normal file
After Width: | Height: | Size: 108 B |
BIN
mods/pvp/gauges/textures/health_7.png
Normal file
After Width: | Height: | Size: 108 B |
BIN
mods/pvp/gauges/textures/health_8.png
Normal file
After Width: | Height: | Size: 108 B |
BIN
mods/pvp/gauges/textures/health_9.png
Normal file
After Width: | Height: | Size: 108 B |
23
mods/pvp/hpregen/init.lua
Normal file
|
@ -0,0 +1,23 @@
|
|||
local regen_interval = tonumber(minetest.settings:get("regen_interval"))
|
||||
if regen_interval <= 0 then
|
||||
regen_interval = 6
|
||||
end
|
||||
local regen_amount = tonumber(minetest.settings:get("regen_amount"))
|
||||
if regen_amount <= 0 then
|
||||
regen_amount = 1
|
||||
end
|
||||
|
||||
local function regen_all()
|
||||
for _, player in pairs(minetest.get_connected_players()) do
|
||||
local oldhp = player:get_hp()
|
||||
if oldhp > 0 then
|
||||
local newhp = oldhp + regen_amount
|
||||
if newhp > 20 then
|
||||
newhp = 20
|
||||
end
|
||||
player:set_hp(newhp)
|
||||
end
|
||||
end
|
||||
minetest.after(regen_interval, regen_all)
|
||||
end
|
||||
minetest.after(regen_interval, regen_all)
|
0
mods/pvp/modpack.txt
Normal file
1
mods/pvp/respawn_immunity/depends.txt
Normal file
|
@ -0,0 +1 @@
|
|||
ctf
|
56
mods/pvp/respawn_immunity/init.lua
Normal file
|
@ -0,0 +1,56 @@
|
|||
respawn_immunity = {}
|
||||
|
||||
local IMMUNE_TIME = 15
|
||||
local immune_serial = 0
|
||||
local immune_players = {}
|
||||
|
||||
function respawn_immunity.is_immune(player)
|
||||
return immune_players[player:get_player_name()]
|
||||
end
|
||||
|
||||
function respawn_immunity.set_immune(player)
|
||||
immune_serial = immune_serial + 1
|
||||
immune_players[player:get_player_name()] = immune_serial
|
||||
minetest.after(1, function()
|
||||
respawn_immunity.update_effects(player)
|
||||
end)
|
||||
|
||||
-- Set time out
|
||||
minetest.after(IMMUNE_TIME, function(name, id)
|
||||
if immune_players[name] == id then
|
||||
immune_players[name] = nil
|
||||
respawn_immunity.update_effects(minetest.get_player_by_name(name))
|
||||
end
|
||||
end, player:get_player_name(), immune_serial)
|
||||
end
|
||||
|
||||
function respawn_immunity.update_effects(player)
|
||||
-- TODO: transparent player when immune
|
||||
--
|
||||
-- if immune_players[player:get_player_name()] then
|
||||
-- player:set_texture_mod("[multiply:#1f1")
|
||||
-- else
|
||||
-- player:set_texture_mod(nil)
|
||||
-- end
|
||||
end
|
||||
|
||||
minetest.register_on_punchplayer(function(player, hitter,
|
||||
time_from_last_punch, tool_capabilities, dir, damage)
|
||||
if player and respawn_immunity.is_immune(player) then
|
||||
minetest.chat_send_player(hitter:get_player_name(),
|
||||
player:get_player_name() ..
|
||||
" just respawned or joined, and is immune to attacks!")
|
||||
return true
|
||||
end
|
||||
|
||||
if hitter and respawn_immunity.is_immune(hitter) then
|
||||
minetest.chat_send_player(hitter:get_player_name(),
|
||||
minetest.colorize("#FF8C00",
|
||||
"Your immunity has ended because you attacked a player"))
|
||||
immune_players[hitter:get_player_name()] = nil
|
||||
respawn_immunity.update_effects(hitter)
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_joinplayer(respawn_immunity.set_immune)
|
||||
minetest.register_on_respawnplayer(respawn_immunity.set_immune)
|
8
mods/pvp/shooter/.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
## Generic ignorable patterns and files
|
||||
*~
|
||||
.*.swp
|
||||
*bak*
|
||||
tags
|
||||
*.vim
|
||||
shooter.conf
|
||||
crosshair.png
|
32
mods/pvp/shooter/LICENSE.txt
Normal file
|
@ -0,0 +1,32 @@
|
|||
Minetest Mod - Simple Shooter [shooter]
|
||||
=======================================
|
||||
|
||||
License Source Code: 2013 Stuart Jones - LGPL v2.1
|
||||
|
||||
Additional credit for code ideas taken from other mods.
|
||||
|
||||
PilzAdam [throwing] for the throwing physics
|
||||
ShadowNinja [nuke] for the vm explosion routine
|
||||
|
||||
License Textures: Stuart Jones - WTFPL
|
||||
|
||||
Licence Models: Stuart Jones - CC-BY-SA 3.0
|
||||
|
||||
License Sounds: freesound.org
|
||||
|
||||
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)
|
||||
|
||||
winchester-rifle-cock-reload.wav by MentalSanityOff - CC0 1.0 Universal (CC0 1.0)
|
||||
|
||||
trigger-with-hammer-fall.wav by Nanashi - CC0 1.0 Universal (CC0 1.0)
|
||||
|
||||
woosh.wav by ReadeOnly - CC0 1.0 Universal (CC0 1.0)
|
||||
|
||||
AGM-114 Hellfire Rocket Missile Launch.flac by qubodup - CC0 1.0 Universal (CC0 1.0)
|
||||
|
||||
Sparkler.aif by Ned Bouhalassa - CC0 1.0 Universal (CC0 1.0)
|
||||
|
169
mods/pvp/shooter/README.txt
Normal file
|
@ -0,0 +1,169 @@
|
|||
Minetest Mod - Simple Shooter [shooter]
|
||||
=======================================
|
||||
|
||||
Mod Version: 0.5.3
|
||||
|
||||
Minetest Version: 0.4.9, 0.4.10, 0.4.11
|
||||
|
||||
Depends: default, dye, tnt, wool
|
||||
|
||||
An experimental first person shooter mod that uses simple vector mathematics
|
||||
to produce an accurate and server-firendly method of hit detection.
|
||||
|
||||
By default this mod is configured to work only against other players in
|
||||
multiplayer mode and against Simple Mobs [mobs] in singleplayer mode.
|
||||
|
||||
Default configuration can be customised by adding a shooter.conf file to
|
||||
the mod's main directory, see shooter.conf.example for more details.
|
||||
|
||||
This is still very much a work in progress which I eventually plan to use
|
||||
as the base for a 'Spades' style FPS game using the minetest engine.
|
||||
|
||||
Crafting
|
||||
========
|
||||
|
||||
<color> = grey, black, red, yellow, green, cyan, blue, magenta
|
||||
|
||||
A = Arrow [shooter:arrow_white]
|
||||
C = Color Dye [dye:<color>]
|
||||
W = Wooden Stick [default:stick]
|
||||
P = Paper [default:paper]
|
||||
S = Steel Ingot [default:steel_ingot]
|
||||
B = Bronze Ingot [default:bronze_ingot]
|
||||
M = Mese Crystal [default:mese_crysytal]
|
||||
D = Diamond [default:diamond]
|
||||
R = Red Wool [wool:red]
|
||||
G = Gun Powder [tnt:gunpowder]
|
||||
|
||||
Crossbow: [shooter:crossbow]
|
||||
|
||||
+---+---+---+
|
||||
| W | W | W |
|
||||
+---+---+---+
|
||||
| W | W | |
|
||||
+---+---+---+
|
||||
| W | | B |
|
||||
+---+---+---+
|
||||
|
||||
White Arrow: [shooter:arrow_white]
|
||||
|
||||
+---+---+---+
|
||||
| S | | |
|
||||
+---+---+---+
|
||||
| | W | P |
|
||||
+---+---+---+
|
||||
| | P | W |
|
||||
+---+---+---+
|
||||
|
||||
Coloured Arrow: [shooter:arrow_<color>]
|
||||
|
||||
+---+---+
|
||||
| C | A |
|
||||
+---+---+
|
||||
|
||||
Pistol: [shooter:pistol]
|
||||
|
||||
+---+---+
|
||||
| S | S |
|
||||
+---+---+
|
||||
| | M |
|
||||
+---+---+
|
||||
|
||||
Rifle: [shooter:rifle]
|
||||
|
||||
+---+---+---+
|
||||
| S | | |
|
||||
+---+---+---+
|
||||
| | B | |
|
||||
+---+---+---+
|
||||
| | M | B |
|
||||
+---+---+---+
|
||||
|
||||
Shotgun: [shooter:shotgun]
|
||||
|
||||
+---+---+---+
|
||||
| S | | |
|
||||
+---+---+---+
|
||||
| | S | |
|
||||
+---+---+---+
|
||||
| | M | B |
|
||||
+---+---+---+
|
||||
|
||||
Sub Machine Gun: [shooter:machine_gun]
|
||||
|
||||
+---+---+---+
|
||||
| S | S | S |
|
||||
+---+---+---+
|
||||
| | B | M |
|
||||
+---+---+---+
|
||||
| | B | |
|
||||
+---+---+---+
|
||||
|
||||
Ammo Pack: [shooter:ammo]
|
||||
|
||||
+---+---+
|
||||
| G | B |
|
||||
+---+---+
|
||||
|
||||
Grappling Hook: [shooter:grapple_hook]
|
||||
|
||||
+---+---+---+
|
||||
| S | S | D |
|
||||
+---+---+---+
|
||||
| S | S | |
|
||||
+---+---+---+
|
||||
| D | | S |
|
||||
+---+---+---+
|
||||
|
||||
Grappling Hook Gun: [shooter:grapple_gun]
|
||||
|
||||
+---+---+
|
||||
| S | S |
|
||||
+---+---+
|
||||
| | D |
|
||||
+---+---+
|
||||
|
||||
Flare: [shooter:flare]
|
||||
|
||||
+---+---+
|
||||
| G | R |
|
||||
+---+---+
|
||||
|
||||
Flare Gun: [shooter:flaregun]
|
||||
|
||||
+---+---+---+
|
||||
| R | R | R |
|
||||
+---+---+---+
|
||||
| | | S |
|
||||
+---+---+---+
|
||||
|
||||
Grenade: [shooter:grenade]
|
||||
|
||||
+---+---+
|
||||
| G | S |
|
||||
+---+---+
|
||||
|
||||
Flare Gun: [shooter:rocket_gun]
|
||||
|
||||
+---+---+---+
|
||||
| B | S | S |
|
||||
+---+---+---+
|
||||
| | | D |
|
||||
+---+---+---+
|
||||
|
||||
Rocket: [shooter:rocket]
|
||||
|
||||
+---+---+---+
|
||||
| B | G | B |
|
||||
+---+---+---+
|
||||
|
||||
Turret: [shooter:turret]
|
||||
|
||||
+---+---+---+
|
||||
| B | B | S |
|
||||
+---+---+---+
|
||||
| | B | S |
|
||||
+---+---+---+
|
||||
| | D | |
|
||||
+---+---+---+
|
||||
|
292
mods/pvp/shooter/crossbow.lua
Normal file
|
@ -0,0 +1,292 @@
|
|||
SHOOTER_CROSSBOW_USES = 50
|
||||
SHOOTER_ARROW_TOOL_CAPS = {damage_groups={fleshy=2}}
|
||||
SHOOTER_ARROW_LIFETIME = 180 -- 3 minutes
|
||||
|
||||
minetest.register_alias("shooter:arrow", "shooter:arrow_white")
|
||||
minetest.register_alias("shooter:crossbow_loaded", "shooter:crossbow_loaded_white")
|
||||
|
||||
local dye_basecolors = (dye and dye.basecolors) or
|
||||
{"white", "grey", "black", "red", "yellow", "green", "cyan", "blue", "magenta"}
|
||||
|
||||
local function get_animation_frame(dir)
|
||||
local angle = math.atan(dir.y)
|
||||
local frame = 90 - math.floor(angle * 360 / math.pi)
|
||||
if frame < 1 then
|
||||
frame = 1
|
||||
elseif frame > 180 then
|
||||
frame = 180
|
||||
end
|
||||
return frame
|
||||
end
|
||||
|
||||
local function get_target_pos(p1, p2, dir, offset)
|
||||
local d = vector.distance(p1, p2) - offset
|
||||
local td = vector.multiply(dir, {x=d, y=d, z=d})
|
||||
return vector.add(p1, td)
|
||||
end
|
||||
|
||||
-- name is the overlay texture name, colour is used to select the wool texture
|
||||
local function get_texture(name, colour)
|
||||
return "shooter_"..name..".png^wool_"..colour..".png^shooter_"..name..".png^[makealpha:255,126,126"
|
||||
end
|
||||
|
||||
minetest.register_entity("shooter:arrow_entity", {
|
||||
physical = false,
|
||||
visual = "mesh",
|
||||
mesh = "shooter_arrow.b3d",
|
||||
visual_size = {x=1, y=1},
|
||||
textures = {
|
||||
get_texture("arrow_uv", "white"),
|
||||
},
|
||||
color = "white",
|
||||
timer = 0,
|
||||
lifetime = SHOOTER_ARROW_LIFETIME,
|
||||
player = nil,
|
||||
state = "init",
|
||||
node_pos = nil,
|
||||
collisionbox = {0,0,0, 0,0,0},
|
||||
stop = function(self, pos)
|
||||
local acceleration = {x=0, y=-10, z=0}
|
||||
if self.state == "stuck" then
|
||||
pos = pos or self.object:getpos()
|
||||
acceleration = {x=0, y=0, z=0}
|
||||
end
|
||||
if pos then
|
||||
self.object:moveto(pos)
|
||||
end
|
||||
self.object:set_properties({
|
||||
physical = true,
|
||||
collisionbox = {-1/8,-1/8,-1/8, 1/8,1/8,1/8},
|
||||
})
|
||||
self.object:setvelocity({x=0, y=0, z=0})
|
||||
self.object:setacceleration(acceleration)
|
||||
end,
|
||||
strike = function(self, object)
|
||||
local puncher = self.player
|
||||
if puncher and shooter:is_valid_object(object) then
|
||||
if puncher ~= object then
|
||||
local dir = puncher:get_look_dir()
|
||||
local p1 = puncher:getpos()
|
||||
local p2 = object:getpos()
|
||||
local tpos = get_target_pos(p1, p2, dir, 0)
|
||||
shooter:spawn_particles(tpos, SHOOTER_EXPLOSION_TEXTURE)
|
||||
object:punch(puncher, nil, SHOOTER_ARROW_TOOL_CAPS, dir)
|
||||
end
|
||||
end
|
||||
self:stop(object:getpos())
|
||||
end,
|
||||
on_activate = function(self, staticdata)
|
||||
self.object:set_armor_groups({immortal=1})
|
||||
if staticdata == "expired" then
|
||||
self.object:remove()
|
||||
end
|
||||
end,
|
||||
on_punch = function(self, puncher)
|
||||
if puncher then
|
||||
if puncher:is_player() then
|
||||
local stack = "shooter:arrow_"..self.color
|
||||
local inv = puncher:get_inventory()
|
||||
if inv:room_for_item("main", stack) then
|
||||
inv:add_item("main", stack)
|
||||
self.object:remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
on_step = function(self, dtime)
|
||||
if self.state == "init" then
|
||||
return
|
||||
end
|
||||
self.timer = self.timer + dtime
|
||||
self.lifetime = self.lifetime - dtime
|
||||
if self.lifetime < 0 then
|
||||
self.object:remove()
|
||||
return
|
||||
elseif self.state == "dropped" then
|
||||
return
|
||||
elseif self.state == "stuck" then
|
||||
if self.timer > 1 then
|
||||
if self.node_pos then
|
||||
local node = minetest.get_node(self.node_pos)
|
||||
if node.name then
|
||||
local item = minetest.registered_items[node.name]
|
||||
if item then
|
||||
if not item.walkable then
|
||||
self.state = "dropped"
|
||||
self:stop()
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self.timer = 0
|
||||
end
|
||||
return
|
||||
end
|
||||
if self.timer > 0.2 then
|
||||
local pos = self.object:getpos()
|
||||
local dir = vector.normalize(self.object:getvelocity())
|
||||
local frame = get_animation_frame(dir)
|
||||
self.object:set_animation({x=frame, y=frame}, 0)
|
||||
local objects = minetest.get_objects_inside_radius(pos, 5)
|
||||
for _,obj in ipairs(objects) do
|
||||
if shooter:is_valid_object(obj) and obj ~= self.player then
|
||||
local collisionbox = {-0.25,-1.0,-0.25, 0.25,0.8,0.25}
|
||||
local offset = SHOOTER_PLAYER_OFFSET
|
||||
if not obj:is_player() then
|
||||
offset = SHOOTER_ENTITY_OFFSET
|
||||
local ent = obj:get_luaentity()
|
||||
if ent then
|
||||
local def = minetest.registered_entities[ent.name]
|
||||
collisionbox = def.collisionbox or collisionbox
|
||||
end
|
||||
end
|
||||
local opos = vector.add(obj:getpos(), offset)
|
||||
local ray = {pos=pos, dir=dir}
|
||||
local plane = {pos=opos, normal={x=-1, y=0, z=-1}}
|
||||
local ipos = shooter:get_intersect_pos(ray, plane, collisionbox)
|
||||
if ipos then
|
||||
self:strike(obj)
|
||||
end
|
||||
end
|
||||
end
|
||||
local p = vector.add(pos, vector.multiply(dir, {x=5, y=5, z=5}))
|
||||
local _, npos = minetest.line_of_sight(pos, p, 1)
|
||||
if npos then
|
||||
local node = minetest.get_node(npos)
|
||||
local tpos = get_target_pos(pos, npos, dir, 0.66)
|
||||
self.node_pos = npos
|
||||
self.state = "stuck"
|
||||
self:stop(tpos)
|
||||
shooter:play_node_sound(node, npos)
|
||||
end
|
||||
self.timer = 0
|
||||
end
|
||||
end,
|
||||
get_staticdata = function(self)
|
||||
return "expired"
|
||||
end,
|
||||
})
|
||||
|
||||
for _, color in pairs(dye_basecolors) do
|
||||
minetest.register_craftitem("shooter:arrow_"..color, {
|
||||
description = color:gsub("%a", string.upper, 1).." Arrow",
|
||||
inventory_image = get_texture("arrow_inv", color),
|
||||
})
|
||||
minetest.register_tool("shooter:crossbow_loaded_"..color, {
|
||||
description = "Crossbow",
|
||||
inventory_image = get_texture("crossbow_loaded", color),
|
||||
groups = {not_in_creative_inventory=1},
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
minetest.sound_play("shooter_click", {object=user})
|
||||
if not minetest.settings:get_bool("creative_mode") then
|
||||
itemstack:add_wear(65535/SHOOTER_CROSSBOW_USES)
|
||||
end
|
||||
itemstack = "shooter:crossbow 1 "..itemstack:get_wear()
|
||||
local pos = user:getpos()
|
||||
local dir = user:get_look_dir()
|
||||
local yaw = user:get_look_yaw()
|
||||
if pos and dir and yaw then
|
||||
pos.y = pos.y + 1.5
|
||||
local obj = minetest.add_entity(pos, "shooter:arrow_entity")
|
||||
local ent = nil
|
||||
if obj then
|
||||
ent = obj:get_luaentity()
|
||||
end
|
||||
if ent then
|
||||
ent.player = ent.player or user
|
||||
ent.state = "flight"
|
||||
ent.color = color
|
||||
obj:set_properties({
|
||||
textures = {get_texture("arrow_uv", color)}
|
||||
})
|
||||
minetest.sound_play("shooter_throw", {object=obj})
|
||||
local frame = get_animation_frame(dir)
|
||||
obj:setyaw(yaw + math.pi)
|
||||
obj:set_animation({x=frame, y=frame}, 0)
|
||||
obj:setvelocity({x=dir.x * 14, y=dir.y * 14, z=dir.z * 14})
|
||||
if pointed_thing.type ~= "nothing" then
|
||||
local ppos = minetest.get_pointed_thing_position(pointed_thing, false)
|
||||
local _, npos = minetest.line_of_sight(pos, ppos, 1)
|
||||
if npos then
|
||||
ppos = npos
|
||||
pointed_thing.type = "node"
|
||||
end
|
||||
if pointed_thing.type == "object" then
|
||||
ent:strike(pointed_thing.ref)
|
||||
return itemstack
|
||||
elseif pointed_thing.type == "node" then
|
||||
local node = minetest.get_node(ppos)
|
||||
local tpos = get_target_pos(pos, ppos, dir, 0.66)
|
||||
minetest.after(0.2, function(object, pos, npos)
|
||||
ent.node_pos = npos
|
||||
ent.state = "stuck"
|
||||
ent:stop(pos)
|
||||
shooter:play_node_sound(node, npos)
|
||||
end, obj, tpos, ppos)
|
||||
return itemstack
|
||||
end
|
||||
end
|
||||
obj:setacceleration({x=dir.x * -3, y=-5, z=dir.z * -3})
|
||||
end
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_tool("shooter:crossbow", {
|
||||
description = "Crossbow",
|
||||
inventory_image = "shooter_crossbow.png",
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
local inv = user:get_inventory()
|
||||
local stack = inv:get_stack("main", user:get_wield_index() + 1)
|
||||
local color = string.match(stack:get_name(), "shooter:arrow_(%a+)")
|
||||
if color then
|
||||
minetest.sound_play("shooter_reload", {object=user})
|
||||
if not minetest.settings:get_bool("creative_mode") then
|
||||
inv:remove_item("main", "shooter:arrow_"..color.." 1")
|
||||
end
|
||||
return "shooter:crossbow_loaded_"..color.." 1 "..itemstack:get_wear()
|
||||
end
|
||||
for _, color in pairs(dye_basecolors) do
|
||||
if inv:contains_item("main", "shooter:arrow_"..color) then
|
||||
minetest.sound_play("shooter_reload", {object=user})
|
||||
if not minetest.settings:get_bool("creative_mode") then
|
||||
inv:remove_item("main", "shooter:arrow_"..color.." 1")
|
||||
end
|
||||
return "shooter:crossbow_loaded_"..color.." 1 "..itemstack:get_wear()
|
||||
end
|
||||
end
|
||||
minetest.sound_play("shooter_click", {object=user})
|
||||
end,
|
||||
})
|
||||
|
||||
if SHOOTER_ENABLE_CRAFTING == true then
|
||||
minetest.register_craft({
|
||||
output = "shooter:crossbow",
|
||||
recipe = {
|
||||
{"default:stick", "default:stick", "default:stick"},
|
||||
{"default:stick", "default:stick", ""},
|
||||
{"default:stick", "", "default:bronze_ingot"},
|
||||
},
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = "shooter:arrow_white",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "", ""},
|
||||
{"", "default:stick", "default:paper"},
|
||||
{"", "default:paper", "default:stick"},
|
||||
},
|
||||
})
|
||||
for _, color in pairs(dye_basecolors) do
|
||||
if color ~= "white" then
|
||||
minetest.register_craft({
|
||||
output = "shooter:arrow_"..color,
|
||||
recipe = {
|
||||
{"", "dye:"..color, "shooter:arrow_white"},
|
||||
},
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
1
mods/pvp/shooter/depends.txt
Normal file
|
@ -0,0 +1 @@
|
|||
default
|
2
mods/pvp/shooter/description.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
First person shooter mod.
|
||||
Adds firearms plus a few other fun items.
|
146
mods/pvp/shooter/flaregun.lua
Normal file
|
@ -0,0 +1,146 @@
|
|||
minetest.register_craftitem("shooter:flare", {
|
||||
description = "Flare",
|
||||
inventory_image = "shooter_flare_inv.png",
|
||||
})
|
||||
|
||||
minetest.register_node("shooter:flare_light", {
|
||||
drawtype = "glasslike",
|
||||
tiles = {"shooter_flare_light.png"},
|
||||
paramtype = "light",
|
||||
groups = {not_in_creative_inventory=1},
|
||||
drop = "",
|
||||
walkable = false,
|
||||
buildable_to = true,
|
||||
sunlight_propagates = true,
|
||||
light_source = LIGHT_MAX,
|
||||
pointable = false,
|
||||
})
|
||||
|
||||
minetest.register_abm({
|
||||
nodenames = "shooter:flare_light",
|
||||
interval = 5,
|
||||
chance = 1,
|
||||
action = function(pos, node)
|
||||
local time = os.time()
|
||||
local meta = minetest.get_meta(pos)
|
||||
local init_time = meta:get_int("init_time") or 0
|
||||
if time > init_time + 30 then
|
||||
local id = meta:get_int("particle_id")
|
||||
if id then
|
||||
minetest.delete_particlespawner(id)
|
||||
end
|
||||
minetest.remove_node(pos)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_entity("shooter:flare_entity", {
|
||||
physical = true,
|
||||
timer = 0,
|
||||
visual = "cube",
|
||||
visual_size = {x=1/8, y=1/8},
|
||||
textures = {
|
||||
"shooter_flare.png",
|
||||
"shooter_flare.png",
|
||||
"shooter_flare.png",
|
||||
"shooter_flare.png",
|
||||
"shooter_flare.png",
|
||||
"shooter_flare.png",
|
||||
},
|
||||
player = nil,
|
||||
collisionbox = {-1/16,-1/16,-1/16, 1/16,1/16,1/16},
|
||||
on_activate = function(self, staticdata)
|
||||
if staticdata == "expired" then
|
||||
self.object:remove()
|
||||
end
|
||||
end,
|
||||
on_step = function(self, dtime)
|
||||
self.timer = self.timer + dtime
|
||||
if self.timer > 0.2 then
|
||||
local pos = self.object:getpos()
|
||||
local below = {x=pos.x, y=pos.y - 1, z=pos.z}
|
||||
local node = minetest.get_node(below)
|
||||
if node.name ~= "air" then
|
||||
self.object:setvelocity({x=0, y=-10, z=0})
|
||||
self.object:setacceleration({x=0, y=0, z=0})
|
||||
if minetest.get_node(pos).name == "air" and
|
||||
node.name ~= "default:water_source" and
|
||||
node.name ~= "default:water_flowing" then
|
||||
minetest.place_node(pos, {name="shooter:flare_light"})
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_int("particle_id", id)
|
||||
meta:set_int("init_time", os.time())
|
||||
pos.y = pos.y - 0.1
|
||||
local id = minetest.add_particlespawner(
|
||||
1000, 30, pos, pos,
|
||||
{x=-1, y=1, z=-1}, {x=1, y=1, z=1},
|
||||
{x=2, y=-2, z=-2}, {x=2, y=-2, z=2},
|
||||
0.1, 0.75, 1, 8, false, "shooter_flare_particle.png"
|
||||
)
|
||||
local sound = minetest.sound_play("shooter_flare_burn", {
|
||||
object = self.player,
|
||||
loop = true,
|
||||
})
|
||||
minetest.after(30, function(sound)
|
||||
minetest.sound_stop(sound)
|
||||
end, sound)
|
||||
end
|
||||
self.object:remove()
|
||||
end
|
||||
self.timer = 0
|
||||
end
|
||||
end,
|
||||
get_staticdata = function(self)
|
||||
return "expired"
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_tool("shooter:flaregun", {
|
||||
description = "Flare Gun",
|
||||
inventory_image = "shooter_flaregun.png",
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
local inv = user:get_inventory()
|
||||
if not inv:contains_item("main", "shooter:flare") then
|
||||
minetest.sound_play("shooter_click", {object=user})
|
||||
return itemstack
|
||||
end
|
||||
if not minetest.settings:get_bool("creative_mode") then
|
||||
inv:remove_item("main", "shooter:flare 1")
|
||||
itemstack:add_wear(65535/100)
|
||||
end
|
||||
local pos = user:getpos()
|
||||
local dir = user:get_look_dir()
|
||||
local yaw = user:get_look_yaw()
|
||||
if pos and dir and yaw then
|
||||
pos.y = pos.y + 1.5
|
||||
local obj = minetest.add_entity(pos, "shooter:flare_entity")
|
||||
if obj then
|
||||
minetest.sound_play("shooter_flare_fire", {object=obj})
|
||||
obj:setvelocity({x=dir.x * 16, y=dir.y * 16, z=dir.z * 16})
|
||||
obj:setacceleration({x=dir.x * -3, y=-10, z=dir.z * -3})
|
||||
obj:setyaw(yaw + math.pi)
|
||||
local ent = obj:get_luaentity()
|
||||
if ent then
|
||||
ent.player = ent.player or user
|
||||
end
|
||||
end
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
})
|
||||
|
||||
if SHOOTER_ENABLE_CRAFTING == true then
|
||||
minetest.register_craft({
|
||||
output = "shooter:flare",
|
||||
recipe = {
|
||||
{"tnt:gunpowder", "wool:red"},
|
||||
},
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = "shooter:flaregun",
|
||||
recipe = {
|
||||
{"wool:red", "wool:red", "wool:red"},
|
||||
{"", "", "default:steel_ingot"}
|
||||
},
|
||||
})
|
||||
end
|
131
mods/pvp/shooter/grapple.lua
Normal file
|
@ -0,0 +1,131 @@
|
|||
local function throw_hook(itemstack, user, vel)
|
||||
local inv = user:get_inventory()
|
||||
local pos = user:getpos()
|
||||
local dir = user:get_look_dir()
|
||||
local yaw = user:get_look_yaw()
|
||||
if pos and dir and yaw then
|
||||
if not minetest.settings:get_bool("creative_mode") then
|
||||
itemstack:add_wear(65535/100)
|
||||
end
|
||||
pos.y = pos.y + 1.5
|
||||
local obj = minetest.add_entity(pos, "shooter:hook")
|
||||
if obj then
|
||||
minetest.sound_play("shooter_throw", {object=obj})
|
||||
obj:setvelocity({x=dir.x * vel, y=dir.y * vel, z=dir.z * vel})
|
||||
obj:setacceleration({x=dir.x * -3, y=-10, z=dir.z * -3})
|
||||
obj:setyaw(yaw + math.pi)
|
||||
local ent = obj:get_luaentity()
|
||||
if ent then
|
||||
ent.player = ent.player or user
|
||||
ent.itemstack = itemstack
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_entity("shooter:hook", {
|
||||
physical = true,
|
||||
timer = 0,
|
||||
visual = "wielditem",
|
||||
visual_size = {x=1/2, y=1/2},
|
||||
textures = {"shooter:grapple_hook"},
|
||||
player = nil,
|
||||
itemstack = "",
|
||||
collisionbox = {-1/4,-1/4,-1/4, 1/4,1/4,1/4},
|
||||
on_activate = function(self, staticdata)
|
||||
self.object:set_armor_groups({fleshy=0})
|
||||
if staticdata == "expired" then
|
||||
self.object:remove()
|
||||
end
|
||||
end,
|
||||
on_step = function(self, dtime)
|
||||
if not self.player then
|
||||
return
|
||||
end
|
||||
self.timer = self.timer + dtime
|
||||
if self.timer > 0.25 then
|
||||
local pos = self.object:getpos()
|
||||
local below = {x=pos.x, y=pos.y - 1, z=pos.z}
|
||||
local node = minetest.get_node(below)
|
||||
if node.name ~= "air" then
|
||||
self.object:setvelocity({x=0, y=-10, z=0})
|
||||
self.object:setacceleration({x=0, y=0, z=0})
|
||||
if minetest.get_item_group(node.name, "liquid") == 0 and
|
||||
minetest.get_node(pos).name == "air" then
|
||||
self.player:moveto(pos)
|
||||
end
|
||||
if minetest.get_item_group(node.name, "lava") == 0 then
|
||||
minetest.add_item(pos, self.itemstack)
|
||||
end
|
||||
self.object:remove()
|
||||
end
|
||||
self.timer = 0
|
||||
end
|
||||
end,
|
||||
get_staticdata = function(self)
|
||||
return "expired"
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_tool("shooter:grapple_hook", {
|
||||
description = "Grappling Hook",
|
||||
inventory_image = "shooter_hook.png",
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if pointed_thing.type ~= "nothing" then
|
||||
return itemstack
|
||||
end
|
||||
throw_hook(itemstack, user, 14)
|
||||
return ""
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_tool("shooter:grapple_gun", {
|
||||
description = "Grappling Gun",
|
||||
inventory_image = "shooter_hook_gun.png",
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
local inv = user:get_inventory()
|
||||
if inv:contains_item("main", "shooter:grapple_hook") and
|
||||
inv:contains_item("main", "tnt:gunpowder") then
|
||||
inv:remove_item("main", "tnt:gunpowder")
|
||||
minetest.sound_play("shooter_reload", {object=user})
|
||||
local stack = inv:remove_item("main", "shooter:grapple_hook")
|
||||
itemstack = "shooter:grapple_gun_loaded 1 "..stack:get_wear()
|
||||
else
|
||||
minetest.sound_play("shooter_click", {object=user})
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_tool("shooter:grapple_gun_loaded", {
|
||||
description = "Grappling Gun",
|
||||
inventory_image = "shooter_hook_gun_loaded.png",
|
||||
groups = {not_in_creative_inventory=1},
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if pointed_thing.type ~= "nothing" then
|
||||
return itemstack
|
||||
end
|
||||
minetest.sound_play("shooter_pistol", {object=user})
|
||||
itemstack = ItemStack("shooter:grapple_hook 1 "..itemstack:get_wear())
|
||||
throw_hook(itemstack, user, 20)
|
||||
return "shooter:grapple_gun"
|
||||
end,
|
||||
})
|
||||
|
||||
if SHOOTER_ENABLE_CRAFTING == true then
|
||||
minetest.register_craft({
|
||||
output = "shooter:grapple_hook",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "default:steel_ingot", "default:diamond"},
|
||||
{"default:steel_ingot", "default:steel_ingot", ""},
|
||||
{"default:diamond", "", "default:steel_ingot"},
|
||||
},
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = "shooter:grapple_gun",
|
||||
recipe = {
|
||||
{"", "default:steel_ingot", "default:steel_ingot"},
|
||||
{"", "", "default:diamond"},
|
||||
},
|
||||
})
|
||||
end
|
82
mods/pvp/shooter/grenade.lua
Normal file
|
@ -0,0 +1,82 @@
|
|||
minetest.register_entity("shooter:grenade_entity", {
|
||||
physical = false,
|
||||
timer = 0,
|
||||
visual = "cube",
|
||||
visual_size = {x=1/8, y=1/8},
|
||||
textures = {
|
||||
"shooter_grenade.png",
|
||||
"shooter_grenade.png",
|
||||
"shooter_grenade.png",
|
||||
"shooter_grenade.png",
|
||||
"shooter_grenade.png",
|
||||
"shooter_grenade.png",
|
||||
},
|
||||
player = nil,
|
||||
player_name = nil,
|
||||
collisionbox = {0,0,0, 0,0,0},
|
||||
on_activate = function(self, staticdata)
|
||||
if staticdata == "expired" then
|
||||
self.object:remove()
|
||||
end
|
||||
end,
|
||||
on_step = function(self, dtime)
|
||||
self.timer = self.timer + dtime
|
||||
if self.timer > 0.2 then
|
||||
local pos = self.object:getpos()
|
||||
local above = {x=pos.x, y=pos.y + 1, z=pos.z}
|
||||
if minetest.get_node(pos).name ~= "air" then
|
||||
self.object:remove()
|
||||
shooter:blast(above, 2, 25, 5, self.player)
|
||||
end
|
||||
self.timer = 0
|
||||
end
|
||||
end,
|
||||
get_staticdata = function(self)
|
||||
return "expired"
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_tool("shooter:grenade", {
|
||||
description = "Grenade",
|
||||
inventory_image = "shooter_hand_grenade.png",
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if not minetest.settings:get_bool("creative_mode") then
|
||||
itemstack = ""
|
||||
end
|
||||
-- clarification for future readers: grenade can be used only if player points at nothing (line 47)
|
||||
if pointed_thing.type ~= "nothing" then
|
||||
local pointed = minetest.get_pointed_thing_position(pointed_thing)
|
||||
if vector.distance(user:getpos(), pointed) < 10 then
|
||||
shooter:blast(pointed, 2, 25, 5)
|
||||
return
|
||||
end
|
||||
end
|
||||
local pos = user:getpos()
|
||||
local dir = user:get_look_dir()
|
||||
local yaw = user:get_look_yaw()
|
||||
if pos and dir then
|
||||
pos.y = pos.y + 1.5
|
||||
local obj = minetest.add_entity(pos, "shooter:grenade_entity")
|
||||
if obj then
|
||||
obj:setvelocity({x=dir.x * 15, y=dir.y * 15, z=dir.z * 15})
|
||||
obj:setacceleration({x=dir.x * -3, y=-10, z=dir.z * -3})
|
||||
obj:setyaw(yaw + math.pi)
|
||||
local ent = obj:get_luaentity()
|
||||
if ent then
|
||||
ent.player = ent.player or user
|
||||
ent.player_name = user:get_player_name()
|
||||
end
|
||||
end
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
})
|
||||
|
||||
if SHOOTER_ENABLE_CRAFTING == true then
|
||||
minetest.register_craft({
|
||||
output = "shooter:grenade",
|
||||
recipe = {
|
||||
{"tnt:gunpowder", "default:steel_ingot"},
|
||||
},
|
||||
})
|
||||
end
|
126
mods/pvp/shooter/guns.lua
Normal file
|
@ -0,0 +1,126 @@
|
|||
shooter:register_weapon("shooter:pistol", {
|
||||
description = "Pistol",
|
||||
inventory_image = "shooter_pistol.png",
|
||||
rounds = 200,
|
||||
spec = {
|
||||
range = 100,
|
||||
step = 20,
|
||||
tool_caps = {full_punch_interval=0.5, damage_groups={fleshy=2}},
|
||||
groups = {snappy=3, fleshy=3, oddly_breakable_by_hand=3},
|
||||
sound = "shooter_pistol",
|
||||
particle = "shooter_cap.png",
|
||||
},
|
||||
})
|
||||
|
||||
shooter:register_weapon("shooter:rifle", {
|
||||
description = "Rifle",
|
||||
inventory_image = "shooter_rifle.png",
|
||||
rounds = 100,
|
||||
spec = {
|
||||
range = 200,
|
||||
step = 30,
|
||||
tool_caps = {full_punch_interval=1.0, damage_groups={fleshy=3}},
|
||||
groups = {snappy=3, crumbly=3, choppy=3, fleshy=2, oddly_breakable_by_hand=2},
|
||||
sound = "shooter_rifle",
|
||||
particle = "shooter_bullet.png",
|
||||
},
|
||||
})
|
||||
|
||||
shooter:register_weapon("shooter:shotgun", {
|
||||
description = "Shotgun",
|
||||
inventory_image = "shooter_shotgun.png",
|
||||
rounds = 50,
|
||||
spec = {
|
||||
range = 50,
|
||||
step = 15,
|
||||
tool_caps = {full_punch_interval=1.5, damage_groups={fleshy=6}},
|
||||
groups = {cracky=3, snappy=2, crumbly=2, choppy=2, fleshy=1, oddly_breakable_by_hand=1},
|
||||
sound = "shooter_shotgun",
|
||||
particle = "smoke_puff.png",
|
||||
},
|
||||
})
|
||||
|
||||
shooter:register_weapon("shooter:machine_gun", {
|
||||
description = "Sub Machine Gun",
|
||||
inventory_image = "shooter_smgun.png",
|
||||
rounds = 50,
|
||||
shots = 4,
|
||||
spec = {
|
||||
range = 100,
|
||||
step = 20,
|
||||
tool_caps = {full_punch_interval=0.125, damage_groups={fleshy=2}},
|
||||
groups = {snappy=3, fleshy=3, oddly_breakable_by_hand=3},
|
||||
sound = "shooter_pistol",
|
||||
particle = "shooter_cap.png",
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craftitem("shooter:ammo", {
|
||||
description = "Ammo pack",
|
||||
inventory_image = "shooter_ammo.png",
|
||||
})
|
||||
|
||||
if SHOOTER_ENABLE_CRAFTING == true then
|
||||
minetest.register_craft({
|
||||
output = "shooter:pistol 1 65535",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "default:steel_ingot"},
|
||||
{"", "default:mese_crystal"},
|
||||
},
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = "shooter:rifle 1 65535",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "", ""},
|
||||
{"", "default:bronze_ingot", ""},
|
||||
{"", "default:mese_crystal", "default:bronze_ingot"},
|
||||
},
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = "shooter:shotgun 1 65535",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "", ""},
|
||||
{"", "default:steel_ingot", ""},
|
||||
{"", "default:mese_crystal", "default:bronze_ingot"},
|
||||
},
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = "shooter:machine_gun 1 65535",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
|
||||
{"", "default:bronze_ingot", "default:mese_crystal"},
|
||||
{"", "default:bronze_ingot", ""},
|
||||
},
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = "shooter:ammo",
|
||||
recipe = {
|
||||
{"tnt:gunpowder", "default:bronze_ingot"},
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
local rounds_update_time = 0
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
shooter.time = shooter.time + dtime
|
||||
if shooter.time - rounds_update_time > SHOOTER_ROUNDS_UPDATE_TIME then
|
||||
for i, round in ipairs(shooter.rounds) do
|
||||
if shooter:process_round(round) or round.dist > round.def.range then
|
||||
table.remove(shooter.rounds, i)
|
||||
else
|
||||
local v = vector.multiply(round.ray, round.def.step)
|
||||
shooter.rounds[i].pos = vector.add(round.pos, v)
|
||||
shooter.rounds[i].dist = round.dist + round.def.step
|
||||
end
|
||||
end
|
||||
rounds_update_time = shooter.time
|
||||
end
|
||||
if shooter.time > 100000 then
|
||||
shooter.shots = {}
|
||||
rounds_update_time = 0
|
||||
shooter.reload_time = 0
|
||||
shooter.update_time = 0
|
||||
shooter.time = 0
|
||||
end
|
||||
end)
|
26
mods/pvp/shooter/init.lua
Normal file
|
@ -0,0 +1,26 @@
|
|||
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||
|
||||
dofile(modpath.."/shooter.lua")
|
||||
|
||||
if SHOOTER_ENABLE_CROSSBOW == true then
|
||||
dofile(modpath.."/crossbow.lua")
|
||||
end
|
||||
if SHOOTER_ENABLE_GUNS == true then
|
||||
dofile(modpath.."/guns.lua")
|
||||
end
|
||||
if SHOOTER_ENABLE_FLARES == true then
|
||||
dofile(modpath.."/flaregun.lua")
|
||||
end
|
||||
if SHOOTER_ENABLE_HOOK == true then
|
||||
dofile(modpath.."/grapple.lua")
|
||||
end
|
||||
if SHOOTER_ENABLE_GRENADES == true then
|
||||
dofile(modpath.."/grenade.lua")
|
||||
end
|
||||
if SHOOTER_ENABLE_ROCKETS == true then
|
||||
dofile(modpath.."/rocket.lua")
|
||||
end
|
||||
if SHOOTER_ENABLE_TURRETS == true then
|
||||
dofile(modpath.."/turret.lua")
|
||||
end
|
||||
|
1
mods/pvp/shooter/mod.conf
Normal file
|
@ -0,0 +1 @@
|
|||
name = shooter
|
BIN
mods/pvp/shooter/models/shooter_arrow.b3d
Normal file
BIN
mods/pvp/shooter/models/shooter_arrow.blend
Normal file
BIN
mods/pvp/shooter/models/shooter_turret.b3d
Normal file
BIN
mods/pvp/shooter/models/shooter_turret.blend
Normal file
113
mods/pvp/shooter/rocket.lua
Normal file
|
@ -0,0 +1,113 @@
|
|||
minetest.register_craftitem("shooter:rocket", {
|
||||
description = "Rocket",
|
||||
stack_max = 1,
|
||||
inventory_image = "shooter_rocket_inv.png",
|
||||
})
|
||||
|
||||
minetest.register_entity("shooter:rocket_entity", {
|
||||
physical = false,
|
||||
timer = 0,
|
||||
visual = "cube",
|
||||
visual_size = {x=1/8, y=1/8},
|
||||
textures = {
|
||||
"shooter_bullet.png",
|
||||
"shooter_bullet.png",
|
||||
"shooter_bullet.png",
|
||||
"shooter_bullet.png",
|
||||
"shooter_bullet.png",
|
||||
"shooter_bullet.png",
|
||||
},
|
||||
player = nil,
|
||||
collisionbox = {0,0,0, 0,0,0},
|
||||
on_activate = function(self, staticdata)
|
||||
if staticdata == "expired" then
|
||||
self.object:remove()
|
||||
end
|
||||
end,
|
||||
on_step = function(self, dtime)
|
||||
self.timer = self.timer + dtime
|
||||
if self.timer > 0.2 then
|
||||
local pos = self.object:getpos()
|
||||
local above = {x=pos.x, y=pos.y + 1, z=pos.z}
|
||||
if minetest.get_node(pos).name ~= "air" then
|
||||
self.object:remove()
|
||||
shooter:blast(above, 4, 50, 8, self.player)
|
||||
end
|
||||
self.timer = 0
|
||||
end
|
||||
end,
|
||||
get_staticdata = function(self)
|
||||
return "expired"
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_tool("shooter:rocket_gun_loaded", {
|
||||
description = "Rocket Gun",
|
||||
inventory_image = "shooter_rocket_gun_loaded.png",
|
||||
groups = {not_in_creative_inventory=1},
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if not minetest.settings:get_bool("creative_mode") then
|
||||
itemstack:add_wear(65535/50)
|
||||
end
|
||||
itemstack = "shooter:rocket_gun 1 "..itemstack:get_wear()
|
||||
if pointed_thing.type ~= "nothing" then
|
||||
local pointed = minetest.get_pointed_thing_position(pointed_thing)
|
||||
if vector.distance(user:getpos(), pointed) < 8 then
|
||||
shooter:blast(pointed, 2, 50, 7)
|
||||
return itemstack
|
||||
end
|
||||
end
|
||||
local pos = user:getpos()
|
||||
local dir = user:get_look_dir()
|
||||
local yaw = user:get_look_yaw()
|
||||
if pos and dir and yaw then
|
||||
pos.y = pos.y + 1.5
|
||||
local obj = minetest.add_entity(pos, "shooter:rocket_entity")
|
||||
if obj then
|
||||
minetest.sound_play("shooter_rocket_fire", {object=obj})
|
||||
obj:setvelocity({x=dir.x * 20, y=dir.y * 20, z=dir.z * 20})
|
||||
obj:setacceleration({x=dir.x * -3, y=-10, z=dir.z * -3})
|
||||
obj:setyaw(yaw + math.pi)
|
||||
local ent = obj:get_luaentity()
|
||||
if ent then
|
||||
ent.player = ent.player or user
|
||||
end
|
||||
end
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_tool("shooter:rocket_gun", {
|
||||
description = "Rocket Gun",
|
||||
inventory_image = "shooter_rocket_gun.png",
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
local inv = user:get_inventory()
|
||||
if inv:contains_item("main", "shooter:rocket") then
|
||||
minetest.sound_play("shooter_reload", {object=user})
|
||||
if not minetest.settings:get_bool("creative_mode") then
|
||||
inv:remove_item("main", "shooter:rocket 1")
|
||||
end
|
||||
itemstack = "shooter:rocket_gun_loaded 1 "..itemstack:get_wear()
|
||||
else
|
||||
minetest.sound_play("shooter_click", {object=user})
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
})
|
||||
|
||||
if SHOOTER_ENABLE_CRAFTING == true then
|
||||
minetest.register_craft({
|
||||
output = "shooter:rocket_gun",
|
||||
recipe = {
|
||||
{"default:bronze_ingot", "default:steel_ingot", "default:steel_ingot"},
|
||||
{"", "", "default:diamond"},
|
||||
},
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = "shooter:rocket",
|
||||
recipe = {
|
||||
{"default:bronze_ingot", "tnt:gunpowder", "default:bronze_ingot"},
|
||||
},
|
||||
})
|
||||
end
|
BIN
mods/pvp/shooter/screenshot.png
Normal file
After Width: | Height: | Size: 175 KiB |
81
mods/pvp/shooter/shooter.conf.example
Normal file
|
@ -0,0 +1,81 @@
|
|||
-- Simple Shooter config example
|
||||
|
||||
-- Global Constants (defaults)
|
||||
|
||||
-- Enable admin super weapons
|
||||
-- This lets admins shoot guns automatically after 2 seconds without munition.
|
||||
SHOOTER_ADMIN_WEAPONS = false
|
||||
|
||||
-- Enable node destruction with explosives
|
||||
SHOOTER_ENABLE_BLASTING = true
|
||||
|
||||
-- Enable Crossbow
|
||||
SHOOTER_ENABLE_CROSSBOW = true
|
||||
|
||||
-- Enable basic guns (Pistol, Rifle, Shotgun, Machine Gun)
|
||||
SHOOTER_ENABLE_GUNS = true
|
||||
|
||||
-- Enable Flare Gun
|
||||
SHOOTER_ENABLE_FLARES = true
|
||||
|
||||
-- Enable Grappling Hook
|
||||
SHOOTER_ENABLE_HOOK = true
|
||||
|
||||
-- Enable Grenades
|
||||
SHOOTER_ENABLE_GRENADES = true
|
||||
|
||||
-- Enable Rocket Gun
|
||||
SHOOTER_ENABLE_ROCKETS = true
|
||||
|
||||
-- Enable Turrret Gun
|
||||
SHOOTER_ENABLE_TURRETS = true
|
||||
|
||||
-- Enable Crafting
|
||||
SHOOTER_ENABLE_CRAFTING = true
|
||||
|
||||
-- Enable particle effects
|
||||
SHOOTER_ENABLE_PARTICLE_FX = true
|
||||
|
||||
-- Enable protection mod support, requires a protection mod that utilizes
|
||||
-- minetest.is_protected(), tested with TenPlus1's version of [protector]
|
||||
SHOOTER_ENABLE_PROTECTION = false
|
||||
|
||||
-- Particle texture used when a player or entity is hit
|
||||
SHOOTER_EXPLOSION_TEXTURE = "shooter_hit.png"
|
||||
|
||||
-- Allow node destruction
|
||||
SHOOTER_ALLOW_NODES = true
|
||||
|
||||
-- Allow entities in multiplayer mode
|
||||
SHOOTER_ALLOW_ENTITIES = false
|
||||
|
||||
-- Allow players in multiplayer mode
|
||||
SHOOTER_ALLOW_PLAYERS = true
|
||||
|
||||
-- How often objects are fully reloaded
|
||||
SHOOTER_OBJECT_RELOAD_TIME = 1
|
||||
|
||||
-- How often object positions are updated
|
||||
SHOOTER_OBJECT_UPDATE_TIME = 0.25
|
||||
|
||||
-- How often rounds are processed
|
||||
SHOOTER_ROUNDS_UPDATE_TIME = 0.4
|
||||
|
||||
-- Player collision box offset (may require adjustment for some games)
|
||||
SHOOTER_PLAYER_OFFSET = {x=0, y=1, z=0}
|
||||
|
||||
-- Entity collision box offset (may require adjustment for other mobs)
|
||||
SHOOTER_ENTITY_OFFSET = {x=0, y=0, z=0}
|
||||
|
||||
-- Shootable entities (default support for Simple Mobs)
|
||||
SHOOTER_ENTITIES = {
|
||||
"mobs:dirt_monster",
|
||||
"mobs:stone_monster",
|
||||
"mobs:sand_monster",
|
||||
"mobs:tree_monster",
|
||||
"mobs:sheep",
|
||||
"mobs:rat",
|
||||
"mobs:oerkki",
|
||||
"mobs:dungeon_master",
|
||||
}
|
||||
|
474
mods/pvp/shooter/shooter.lua
Normal file
|
@ -0,0 +1,474 @@
|
|||
shooter = {
|
||||
time = 0,
|
||||
objects = {},
|
||||
rounds = {},
|
||||
shots = {},
|
||||
update_time = 0,
|
||||
reload_time = 0,
|
||||
}
|
||||
|
||||
SHOOTER_ADMIN_WEAPONS = false
|
||||
SHOOTER_ENABLE_BLASTING = false
|
||||
SHOOTER_ENABLE_CROSSBOW = true
|
||||
SHOOTER_ENABLE_GUNS = true
|
||||
SHOOTER_ENABLE_FLARES = false
|
||||
SHOOTER_ENABLE_HOOK = true
|
||||
SHOOTER_ENABLE_GRENADES = true
|
||||
SHOOTER_ENABLE_ROCKETS = true
|
||||
SHOOTER_ENABLE_TURRETS = true
|
||||
SHOOTER_ENABLE_CRAFTING = false
|
||||
SHOOTER_ENABLE_PARTICLE_FX = true
|
||||
SHOOTER_ENABLE_PROTECTION = false
|
||||
SHOOTER_EXPLOSION_TEXTURE = "shooter_hit.png"
|
||||
SHOOTER_ALLOW_NODES = true
|
||||
SHOOTER_ALLOW_ENTITIES = false
|
||||
SHOOTER_ALLOW_PLAYERS = true
|
||||
SHOOTER_OBJECT_RELOAD_TIME = 1
|
||||
SHOOTER_OBJECT_UPDATE_TIME = 0.25
|
||||
SHOOTER_ROUNDS_UPDATE_TIME = 0.4
|
||||
SHOOTER_PLAYER_OFFSET = {x=0, y=1, z=0}
|
||||
SHOOTER_ENTITY_OFFSET = {x=0, y=0, z=0}
|
||||
SHOOTER_ENTITIES = {}
|
||||
for k, v in pairs(minetest.registered_entities) do
|
||||
if string.find(k, "^mobs") then
|
||||
table.insert(SHOOTER_ENTITIES, k)
|
||||
end
|
||||
end
|
||||
|
||||
local singleplayer = minetest.is_singleplayer()
|
||||
if singleplayer then
|
||||
SHOOTER_ENABLE_BLASTING = true
|
||||
SHOOTER_ALLOW_ENTITIES = true
|
||||
SHOOTER_ALLOW_PLAYERS = false
|
||||
end
|
||||
|
||||
if minetest.global_exists("ctf_match") then
|
||||
ctf_match.register_on_build_time_start(function()
|
||||
SHOOTER_ALLOW_PLAYERS = false
|
||||
end)
|
||||
ctf_match.register_on_build_time_end(function()
|
||||
SHOOTER_ALLOW_PLAYERS = true
|
||||
end)
|
||||
end
|
||||
|
||||
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||
local worldpath = minetest.get_worldpath()
|
||||
local input = io.open(modpath.."/shooter.conf", "r")
|
||||
if input then
|
||||
dofile(modpath.."/shooter.conf")
|
||||
input:close()
|
||||
input = nil
|
||||
end
|
||||
input = io.open(worldpath.."/shooter.conf", "r")
|
||||
if input then
|
||||
dofile(worldpath.."/shooter.conf")
|
||||
input:close()
|
||||
input = nil
|
||||
end
|
||||
|
||||
local allowed_entities = {}
|
||||
for _,v in ipairs(SHOOTER_ENTITIES) do
|
||||
allowed_entities[v] = 1
|
||||
end
|
||||
|
||||
local function get_dot_product(v1, v2)
|
||||
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z
|
||||
end
|
||||
|
||||
local function get_particle_pos(p, v, d)
|
||||
return vector.add(p, vector.multiply(v, {x=d, y=d, z=d}))
|
||||
end
|
||||
|
||||
function shooter:spawn_particles(pos, texture)
|
||||
if SHOOTER_ENABLE_PARTICLE_FX == true then
|
||||
if type(texture) ~= "string" then
|
||||
texture = SHOOTER_EXPLOSION_TEXTURE
|
||||
end
|
||||
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
|
||||
end
|
||||
|
||||
function shooter:play_node_sound(node, pos)
|
||||
local item = minetest.registered_items[node.name]
|
||||
if item then
|
||||
if item.sounds then
|
||||
local spec = item.sounds.dug
|
||||
if spec then
|
||||
spec.pos = pos
|
||||
minetest.sound_play(spec.name, spec)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function shooter:punch_node(pos, def)
|
||||
local node = minetest.get_node(pos)
|
||||
if not node then
|
||||
return
|
||||
end
|
||||
local item = minetest.registered_items[node.name]
|
||||
if not item then
|
||||
return
|
||||
end
|
||||
if SHOOTER_ENABLE_PROTECTION then
|
||||
if minetest.is_protected(pos, def.name) then
|
||||
return
|
||||
end
|
||||
end
|
||||
if item.groups then
|
||||
for k, v in pairs(def.groups) do
|
||||
local level = item.groups[k] or 0
|
||||
if level >= v then
|
||||
minetest.remove_node(pos)
|
||||
shooter:play_node_sound(node, pos)
|
||||
if item.tiles then
|
||||
if item.tiles[1] then
|
||||
shooter:spawn_particles(pos, item.tiles[1])
|
||||
end
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function shooter:is_valid_object(object)
|
||||
if object then
|
||||
if object:is_player() == true then
|
||||
return SHOOTER_ALLOW_PLAYERS
|
||||
end
|
||||
if SHOOTER_ALLOW_ENTITIES == true then
|
||||
local luaentity = object:get_luaentity()
|
||||
if luaentity then
|
||||
if luaentity.name then
|
||||
if allowed_entities[luaentity.name] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function shooter:get_intersect_pos(ray, plane, collisionbox)
|
||||
local v = vector.subtract(ray.pos, plane.pos)
|
||||
local r1 = get_dot_product(v, plane.normal)
|
||||
local r2 = get_dot_product(ray.dir, plane.normal)
|
||||
if r2 ~= 0 then
|
||||
local t = -(r1 / r2)
|
||||
local td = vector.multiply(ray.dir, {x=t, y=t, z=t})
|
||||
local pt = vector.add(ray.pos, td)
|
||||
local pd = vector.subtract(pt, plane.pos)
|
||||
if math.abs(pd.x) < collisionbox[4] and
|
||||
math.abs(pd.y) < collisionbox[5] and
|
||||
math.abs(pd.z) < collisionbox[6] then
|
||||
return pt
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function shooter:process_round(round)
|
||||
local target = {object=nil, distance=10000}
|
||||
local p1 = round.pos
|
||||
local v1 = round.ray
|
||||
for _,ref in ipairs(shooter.objects) do
|
||||
local p2 = vector.add(ref.pos, ref.offset)
|
||||
if p1 and p2 and ref.name ~= round.name then
|
||||
local d = vector.distance(p1, p2)
|
||||
if d < round.def.step and d < target.distance then
|
||||
local ray = {pos=p1, dir=v1}
|
||||
local plane = {pos=p2, normal={x=-1, y=0, z=-1}}
|
||||
local pos = shooter:get_intersect_pos(ray, plane, ref.collisionbox)
|
||||
if pos then
|
||||
target.object = ref.object
|
||||
target.pos = pos
|
||||
target.distance = d
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if target.object and target.pos then
|
||||
local success, pos = minetest.line_of_sight(p1, target.pos, 1)
|
||||
if success then
|
||||
local user = minetest.get_player_by_name(round.name)
|
||||
if user then
|
||||
target.object:punch(user, nil, round.def.tool_caps, v1)
|
||||
shooter:spawn_particles(target.pos, SHOOTER_EXPLOSION_TEXTURE)
|
||||
end
|
||||
return 1
|
||||
elseif pos and SHOOTER_ALLOW_NODES == true then
|
||||
shooter:punch_node(pos, round.def)
|
||||
return 1
|
||||
end
|
||||
elseif SHOOTER_ALLOW_NODES == true then
|
||||
local d = round.def.step
|
||||
local p2 = vector.add(p1, vector.multiply(v1, {x=d, y=d, z=d}))
|
||||
local success, pos = minetest.line_of_sight(p1, p2, 1)
|
||||
if pos then
|
||||
shooter:punch_node(pos, round.def)
|
||||
return 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shooter.registered_weapons = shooter.registered_weapons or {}
|
||||
function shooter:register_weapon(name, def)
|
||||
shooter.registered_weapons[name] = def
|
||||
local shots = def.shots or 1
|
||||
local wear = math.ceil(65534 / def.rounds)
|
||||
local max_wear = (def.rounds - 1) * wear
|
||||
minetest.register_tool(name, {
|
||||
description = def.description,
|
||||
inventory_image = def.inventory_image,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if itemstack:get_wear() < max_wear then
|
||||
def.spec.name = user:get_player_name()
|
||||
if shots > 1 then
|
||||
local step = def.spec.tool_caps.full_punch_interval
|
||||
for i = 0, step * shots, step do
|
||||
minetest.after(i, function()
|
||||
shooter:fire_weapon(user, pointed_thing, def.spec)
|
||||
end)
|
||||
end
|
||||
else
|
||||
shooter:fire_weapon(user, pointed_thing, def.spec)
|
||||
end
|
||||
itemstack:add_wear(wear)
|
||||
else
|
||||
local inv = user:get_inventory()
|
||||
if inv then
|
||||
local stack = "shooter:ammo 1"
|
||||
if inv:contains_item("main", stack) then
|
||||
minetest.sound_play("shooter_reload", {object=user})
|
||||
inv:remove_item("main", stack)
|
||||
itemstack:replace(name.." 1 1")
|
||||
else
|
||||
minetest.sound_play("shooter_click", {object=user})
|
||||
end
|
||||
end
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
function shooter:fire_weapon(user, pointed_thing, def)
|
||||
if shooter.shots[def.name] then
|
||||
if shooter.time < shooter.shots[def.name] then
|
||||
return
|
||||
end
|
||||
end
|
||||
shooter.shots[def.name] = shooter.time + def.tool_caps.full_punch_interval
|
||||
minetest.sound_play(def.sound, {object=user})
|
||||
local v1 = user:get_look_dir()
|
||||
local p1 = user:getpos()
|
||||
if not v1 or not p1 then
|
||||
return
|
||||
end
|
||||
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" and SHOOTER_ALLOW_NODES == true then
|
||||
local pos = minetest.get_pointed_thing_position(pointed_thing, false)
|
||||
shooter:punch_node(pos, def)
|
||||
elseif pointed_thing.type == "object" then
|
||||
local object = pointed_thing.ref
|
||||
if shooter:is_valid_object(object) == true then
|
||||
object:punch(user, nil, def.tool_caps, v1)
|
||||
local p2 = object:getpos()
|
||||
local pp = get_particle_pos(p1, v1, vector.distance(p1, p2))
|
||||
pp.y = pp.y + 1.75
|
||||
shooter:spawn_particles(pp, SHOOTER_EXPLOSION_TEXTURE)
|
||||
end
|
||||
else
|
||||
shooter:update_objects()
|
||||
table.insert(shooter.rounds, {
|
||||
name = def.name,
|
||||
pos = vector.add(p1, {x=0, y=1.75, z=0}),
|
||||
ray = v1,
|
||||
dist = 0,
|
||||
def = def,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function shooter:load_objects()
|
||||
local objects = {}
|
||||
if SHOOTER_ALLOW_PLAYERS == true then
|
||||
local players = minetest.get_connected_players()
|
||||
for _,player in ipairs(players) do
|
||||
local pos = player:getpos()
|
||||
local name = player:get_player_name()
|
||||
local hp = player:get_hp() or 0
|
||||
if pos and name and hp > 0 then
|
||||
table.insert(objects, {
|
||||
name = name,
|
||||
object = player,
|
||||
pos = pos,
|
||||
collisionbox = {-0.25,-1.0,-0.25, 0.25,0.8,0.25},
|
||||
offset = SHOOTER_PLAYER_OFFSET,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
if SHOOTER_ALLOW_ENTITIES == true then
|
||||
for _,ref in pairs(minetest.luaentities) do
|
||||
if ref.object and ref.name then
|
||||
if allowed_entities[ref.name] then
|
||||
local pos = ref.object:getpos()
|
||||
local hp = ref.object:get_hp() or 0
|
||||
if pos and hp > 0 then
|
||||
local def = minetest.registered_entities[ref.name]
|
||||
table.insert(objects, {
|
||||
name = ref.name,
|
||||
object = ref.object,
|
||||
pos = pos,
|
||||
collisionbox = def.collisionbox or {0,0,0, 0,0,0},
|
||||
offset = SHOOTER_ENTITY_OFFSET,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
shooter.reload_time = shooter.time
|
||||
shooter.update_time = shooter.time
|
||||
shooter.objects = {}
|
||||
for _,v in ipairs(objects) do
|
||||
table.insert(shooter.objects, v)
|
||||
end
|
||||
end
|
||||
|
||||
function shooter:update_objects()
|
||||
if shooter.time - shooter.reload_time > SHOOTER_OBJECT_RELOAD_TIME then
|
||||
shooter:load_objects()
|
||||
elseif shooter.time - shooter.update_time > SHOOTER_OBJECT_UPDATE_TIME then
|
||||
for i, ref in ipairs(shooter.objects) do
|
||||
if ref.object then
|
||||
local pos = ref.object:getpos()
|
||||
if pos then
|
||||
shooter.objects[i].pos = pos
|
||||
end
|
||||
else
|
||||
table.remove(shooter.objects, i)
|
||||
end
|
||||
end
|
||||
shooter.update_time = shooter.time
|
||||
end
|
||||
end
|
||||
|
||||
function shooter:blast(pos, radius, fleshy, distance, user)
|
||||
if not user then
|
||||
return
|
||||
end
|
||||
local name = user:get_player_name()
|
||||
local pos = vector.round(pos)
|
||||
local p1 = vector.subtract(pos, radius)
|
||||
local p2 = vector.add(pos, radius)
|
||||
minetest.sound_play("shooter_explode", {pos=pos, gain=1})
|
||||
if SHOOTER_ALLOW_NODES and SHOOTER_ENABLE_BLASTING then
|
||||
if SHOOTER_ENABLE_PROTECTION then
|
||||
if not minetest.is_protected(pos, name) then
|
||||
minetest.set_node(pos, {name="tnt:boom"})
|
||||
end
|
||||
else
|
||||
minetest.set_node(pos, {name="tnt:boom"})
|
||||
end
|
||||
end
|
||||
if SHOOTER_ENABLE_PARTICLE_FX == true then
|
||||
minetest.add_particlespawner(50, 0.1,
|
||||
p1, p2, {x=-0, y=-0, z=-0}, {x=0, y=0, z=0},
|
||||
{x=-0.5, y=5, z=-0.5}, {x=0.5, y=5, z=0.5},
|
||||
0.1, 1, 8, 15, false, "tnt_smoke.png"
|
||||
)
|
||||
end
|
||||
local objects = minetest.get_objects_inside_radius(pos, distance)
|
||||
for _,obj in ipairs(objects) do
|
||||
if (obj:is_player() and SHOOTER_ALLOW_PLAYERS == true) or
|
||||
(obj:get_luaentity() and SHOOTER_ALLOW_ENTITIES == true and
|
||||
obj:get_luaentity().name ~= "__builtin:item") then
|
||||
local obj_pos = obj:getpos()
|
||||
local dist = vector.distance(obj_pos, pos)
|
||||
local damage = (fleshy * 0.5 ^ dist) * 3
|
||||
if dist ~= 0 then
|
||||
obj_pos.y = obj_pos.y + 1.7
|
||||
blast_pos = {x=pos.x, y=pos.y + 4, z=pos.z}
|
||||
if minetest.line_of_sight(obj_pos, blast_pos, 1) then
|
||||
obj:punch(user or obj, 2.0, {
|
||||
full_punch_interval = 1.0,
|
||||
damage_groups = {fleshy=damage, grenade=damage},
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if SHOOTER_ALLOW_NODES and SHOOTER_ENABLE_BLASTING then
|
||||
local pr = PseudoRandom(os.time())
|
||||
local vm = VoxelManip()
|
||||
local min, max = vm:read_from_map(p1, p2)
|
||||
local area = VoxelArea:new({MinEdge=min, MaxEdge=max})
|
||||
local data = vm:get_data()
|
||||
local c_air = minetest.get_content_id("air")
|
||||
for z = -radius, radius do
|
||||
for y = -radius, radius do
|
||||
local vp = {x=pos.x - radius, y=pos.y + y, z=pos.z + z}
|
||||
local vi = area:index(vp.x, vp.y, vp.z)
|
||||
for x = -radius, radius do
|
||||
if (x * x) + (y * y) + (z * z) <=
|
||||
(radius * radius) + pr:next(-radius, radius) then
|
||||
if SHOOTER_ENABLE_PROTECTION then
|
||||
if not minetest.is_protected(vp, name) then
|
||||
data[vi] = c_air
|
||||
end
|
||||
else
|
||||
data[vi] = c_air
|
||||
end
|
||||
end
|
||||
vi = vi + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
vm:set_data(data)
|
||||
vm:update_liquids()
|
||||
vm:write_to_map()
|
||||
vm:update_map()
|
||||
end
|
||||
end
|
||||
|
||||
if not singleplayer and SHOOTER_ADMIN_WEAPONS then
|
||||
local timer = 0
|
||||
local shooting = false
|
||||
minetest.register_globalstep(function(dtime)
|
||||
if not shooting then
|
||||
timer = timer+dtime
|
||||
if timer < 2 then
|
||||
return
|
||||
end
|
||||
timer = 0
|
||||
end
|
||||
shooting = false
|
||||
for _,player in pairs(minetest.get_connected_players()) do
|
||||
if player:get_player_control().LMB then
|
||||
local name = player:get_player_name()
|
||||
if minetest.check_player_privs(name, {server=true}) then
|
||||
local spec = shooter.registered_weapons[player:get_wielded_item():get_name()]
|
||||
if spec then
|
||||
spec = spec.spec
|
||||
shooter.shots[name] = false
|
||||
spec.name = name
|
||||
shooter:fire_weapon(player, {}, spec)
|
||||
shooting = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
BIN
mods/pvp/shooter/sounds/shooter_click.ogg
Normal file
BIN
mods/pvp/shooter/sounds/shooter_explode.ogg
Normal file
BIN
mods/pvp/shooter/sounds/shooter_flare_burn.ogg
Normal file
BIN
mods/pvp/shooter/sounds/shooter_flare_fire.ogg
Normal file
BIN
mods/pvp/shooter/sounds/shooter_pistol.ogg
Normal file
BIN
mods/pvp/shooter/sounds/shooter_reload.ogg
Normal file
BIN
mods/pvp/shooter/sounds/shooter_rifle.ogg
Normal file
BIN
mods/pvp/shooter/sounds/shooter_rocket_fire.ogg
Normal file
BIN
mods/pvp/shooter/sounds/shooter_shotgun.ogg
Normal file
BIN
mods/pvp/shooter/sounds/shooter_throw.ogg
Normal file
BIN
mods/pvp/shooter/textures/shooter_ammo.png
Normal file
After Width: | Height: | Size: 268 B |
BIN
mods/pvp/shooter/textures/shooter_arrow_inv.png
Normal file
After Width: | Height: | Size: 234 B |
BIN
mods/pvp/shooter/textures/shooter_arrow_uv.png
Normal file
After Width: | Height: | Size: 290 B |
BIN
mods/pvp/shooter/textures/shooter_bullet.png
Normal file
After Width: | Height: | Size: 178 B |
BIN
mods/pvp/shooter/textures/shooter_cap.png
Normal file
After Width: | Height: | Size: 157 B |
BIN
mods/pvp/shooter/textures/shooter_crossbow.png
Normal file
After Width: | Height: | Size: 500 B |
BIN
mods/pvp/shooter/textures/shooter_crossbow_loaded.png
Normal file
After Width: | Height: | Size: 389 B |
BIN
mods/pvp/shooter/textures/shooter_flare.png
Normal file
After Width: | Height: | Size: 173 B |
BIN
mods/pvp/shooter/textures/shooter_flare_inv.png
Normal file
After Width: | Height: | Size: 278 B |
BIN
mods/pvp/shooter/textures/shooter_flare_light.png
Normal file
After Width: | Height: | Size: 291 B |
BIN
mods/pvp/shooter/textures/shooter_flare_particle.png
Normal file
After Width: | Height: | Size: 310 B |
BIN
mods/pvp/shooter/textures/shooter_flaregun.png
Normal file
After Width: | Height: | Size: 498 B |
BIN
mods/pvp/shooter/textures/shooter_grenade.png
Normal file
After Width: | Height: | Size: 178 B |
BIN
mods/pvp/shooter/textures/shooter_hand_grenade.png
Normal file
After Width: | Height: | Size: 381 B |
BIN
mods/pvp/shooter/textures/shooter_hit.png
Normal file
After Width: | Height: | Size: 621 B |
BIN
mods/pvp/shooter/textures/shooter_hook.png
Normal file
After Width: | Height: | Size: 567 B |
BIN
mods/pvp/shooter/textures/shooter_hook_gun.png
Normal file
After Width: | Height: | Size: 537 B |
BIN
mods/pvp/shooter/textures/shooter_hook_gun_loaded.png
Normal file
After Width: | Height: | Size: 569 B |
BIN
mods/pvp/shooter/textures/shooter_pistol.png
Normal file
After Width: | Height: | Size: 274 B |
BIN
mods/pvp/shooter/textures/shooter_rifle.png
Normal file
After Width: | Height: | Size: 420 B |
BIN
mods/pvp/shooter/textures/shooter_rocket_gun.png
Normal file
After Width: | Height: | Size: 690 B |
BIN
mods/pvp/shooter/textures/shooter_rocket_gun_loaded.png
Normal file
After Width: | Height: | Size: 679 B |
BIN
mods/pvp/shooter/textures/shooter_rocket_inv.png
Normal file
After Width: | Height: | Size: 376 B |
BIN
mods/pvp/shooter/textures/shooter_shotgun.png
Normal file
After Width: | Height: | Size: 402 B |
BIN
mods/pvp/shooter/textures/shooter_smgun.png
Normal file
After Width: | Height: | Size: 289 B |
BIN
mods/pvp/shooter/textures/shooter_turret_base.png
Normal file
After Width: | Height: | Size: 178 B |