Classes Updates (#994)
* Classes Rework * Add ctf_settings * Auto-start respawn timer * cooldowns: provide cooldown start time * Allow squinting with zoom * Restore old scout ability, allow sniper ADS * Knight combat rework rework rework * Fix luacheck warnings * Change classes build timer to a minute * Default to default wielditem display * Update class desc * Fix crash and tweak restart message * Fix potential issue * Undo failure of a crash fix * Fix crash * Fix potential issues * Fix crash? * Roughly restore old knight sword behaviour
This commit is contained in:
parent
78ababcb17
commit
c412c6058b
@ -3,18 +3,18 @@ unused_args = false
|
||||
globals = {
|
||||
"PlayerObj", "PlayerName", "HumanReadable", "RunCallbacks",
|
||||
|
||||
"ctf_gui", "hud_events", "mhud", "physics", "rawf",
|
||||
"ctf_gui", "hud_events", "mhud", "physics", "rawf", "ctf_settings",
|
||||
|
||||
"ctf_api", "ctf_chat", "ctf_combat_mode", "ctf_core", "ctf_cosmetics",
|
||||
"ctf_healing", "ctf_kill_list", "ctf_map", "ctf_melee", "ctf_modebase",
|
||||
"ctf_ranged", "ctf_rankings", "ctf_report", "ctf_teams",
|
||||
"ctf_ranged", "ctf_rankings", "ctf_report", "ctf_teams", "ctf_player",
|
||||
|
||||
"dropondie", "grenades",
|
||||
|
||||
"chatcmdbuilder", "crafting", "hpbar", "playertag", "random_messages",
|
||||
"skybox", "throwable_snow",
|
||||
|
||||
"default", "doors",
|
||||
"default", "doors", "player_api", "sfinv", "binoculars",
|
||||
|
||||
"vector",
|
||||
math = {
|
||||
@ -47,6 +47,7 @@ read_globals = {
|
||||
"ItemStack",
|
||||
"Settings",
|
||||
"unpack",
|
||||
"loadstring",
|
||||
|
||||
table = {
|
||||
fields = {
|
||||
|
@ -2,10 +2,14 @@
|
||||
|
||||
A tool for easily creating basic CTF-themed GUIs
|
||||
|
||||
**There is a new API in the works, use of this is not recommended**
|
||||
|
||||
# API
|
||||
|
||||
```lua
|
||||
ctf_gui.show_formspec(player, "modname:formname", {
|
||||
ctf_gui.old_init()
|
||||
|
||||
ctf_gui.old_show_formspec(player, "modname:formname", {
|
||||
size = {x = <x size>, y = <y size>},
|
||||
title = "Formspec Title",
|
||||
description = "Text below the title",
|
||||
@ -86,4 +90,3 @@ ctf_gui.show_formspec(player, "modname:formname", {
|
||||
-- Called when this element shows up in on_player_recieve_fields
|
||||
end,
|
||||
}
|
||||
|
||||
|
37
mods/apis/ctf_gui/dev.lua
Normal file
37
mods/apis/ctf_gui/dev.lua
Normal file
@ -0,0 +1,37 @@
|
||||
function ctf_gui.show_formspec_dev(player, formname, formspec, formcontext)
|
||||
local filepath = minetest.get_worldpath().."/ctf_gui/"
|
||||
local filename = filepath.."file_edit.txt"
|
||||
|
||||
minetest.mkdir(filepath)
|
||||
|
||||
local file = assert(io.open(filename, "w"))
|
||||
|
||||
file:write(formspec)
|
||||
|
||||
file:close()
|
||||
|
||||
local function interval()
|
||||
if formspec:sub(1, 3) == "[f]" then
|
||||
local result, form = pcall(loadstring(formspec:sub(4)), formcontext)
|
||||
ctf_gui.show_formspec(player, formname, result and form or "")
|
||||
else
|
||||
ctf_gui.show_formspec(player, formname, formspec)
|
||||
end
|
||||
|
||||
minetest.after(1, function()
|
||||
local f = assert(io.open(filename, "r"))
|
||||
|
||||
formspec = f:read("*a")
|
||||
|
||||
f:close()
|
||||
|
||||
if formspec ~= "exit" then
|
||||
interval()
|
||||
else
|
||||
minetest.request_shutdown("Formspec dev requested shutdown", true)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
interval()
|
||||
end
|
@ -5,8 +5,8 @@ ctf_gui = {
|
||||
}
|
||||
|
||||
local context = {}
|
||||
|
||||
local gui_users_initialized = {}
|
||||
|
||||
function ctf_gui.init()
|
||||
local modname = minetest.get_current_modname()
|
||||
|
||||
@ -14,6 +14,45 @@ function ctf_gui.init()
|
||||
|
||||
gui_users_initialized[modname] = true
|
||||
|
||||
ctf_core.register_on_formspec_input(modname..":", function(pname, formname, fields, ...)
|
||||
local ctx = context[pname]
|
||||
if not ctx then return end
|
||||
|
||||
if ctx._formname == formname and ctx._on_formspec_input then
|
||||
if ctx._privs then
|
||||
local playerprivs = minetest.get_player_privs(pname)
|
||||
|
||||
for priv, needed in pairs(ctx._privs) do
|
||||
if needed and not playerprivs[priv] then
|
||||
minetest.log("warning", string.format(
|
||||
"Player '%q' doesn't have the privs needed to access the formspec '%s'",
|
||||
pname, formname
|
||||
))
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if fields.quit and ctx._on_quit then
|
||||
ctx._on_quit(pname, fields)
|
||||
else
|
||||
local action = ctx._on_formspec_input(pname, ctx, fields, ...)
|
||||
|
||||
if action == "refresh" then
|
||||
minetest.show_formspec(pname, ctx._formname, ctx._formspec(ctx))
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function ctf_gui.old_init()
|
||||
local modname = minetest.get_current_modname()
|
||||
|
||||
assert(not gui_users_initialized[modname], "Already initialized for mod: "..modname)
|
||||
|
||||
gui_users_initialized[modname] = true
|
||||
|
||||
ctf_core.register_on_formspec_input(modname..":", function(pname, formname, fields)
|
||||
local ctx = context[pname]
|
||||
if not ctx then return end
|
||||
@ -24,8 +63,10 @@ function ctf_gui.init()
|
||||
|
||||
for priv, needed in pairs(ctx.privs) do
|
||||
if needed and not playerprivs[priv] then
|
||||
minetest.log("warning", "Player " .. dump(pname) ..
|
||||
" doesn't have the privs needed to access the formspec " .. dump(formname))
|
||||
minetest.log("warning", string.format(
|
||||
"Player '%q' doesn't have the privs needed to access the formspec '%s'",
|
||||
pname, formname
|
||||
))
|
||||
return
|
||||
end
|
||||
end
|
||||
@ -44,8 +85,10 @@ function ctf_gui.init()
|
||||
end
|
||||
end
|
||||
if bad then
|
||||
minetest.log("warning", "Player " .. dump(pname) ..
|
||||
" sent unallowed values for formspec " .. dump(formname) .. " : " .. dump(fields))
|
||||
minetest.log("warning", string.format(
|
||||
"Player %s sent unallowed values for formspec %s : %s",
|
||||
pname, formname, dump(fields)
|
||||
))
|
||||
return
|
||||
end
|
||||
end
|
||||
@ -64,7 +107,52 @@ function ctf_gui.init()
|
||||
end)
|
||||
end
|
||||
|
||||
function ctf_gui.show_formspec(player, formname, formdef)
|
||||
function ctf_gui.show_formspec(player, formname, formspec, formcontext)
|
||||
player = PlayerName(player)
|
||||
|
||||
context[player] = formcontext or {}
|
||||
|
||||
context[player]._formname = formname
|
||||
context[player]._formspec = formspec
|
||||
|
||||
if type(formspec) == "function" then
|
||||
minetest.show_formspec(player, formname, formspec(formcontext))
|
||||
else
|
||||
minetest.show_formspec(player, formname, formspec)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local remove = table.remove
|
||||
local concat = table.concat
|
||||
local formspec_escape = minetest.formspec_escape
|
||||
local format = string.format
|
||||
local unpck = unpack
|
||||
|
||||
function ctf_gui.list_to_element(l)
|
||||
local base = remove(l, 1)
|
||||
|
||||
for k, format_var in ipairs(l) do
|
||||
l[k] = formspec_escape(format_var)
|
||||
end
|
||||
|
||||
return format(base, unpck(l))
|
||||
end
|
||||
|
||||
local lte = ctf_gui.list_to_element
|
||||
|
||||
function ctf_gui.list_to_formspec_str(l)
|
||||
for k, v in ipairs(l) do
|
||||
if type(v) == "table" then
|
||||
l[k] = lte(v)
|
||||
end
|
||||
end
|
||||
|
||||
return concat(l, "")
|
||||
end
|
||||
end
|
||||
|
||||
function ctf_gui.old_show_formspec(player, formname, formdef)
|
||||
player = PlayerName(player)
|
||||
|
||||
formdef.formname = formname
|
||||
@ -289,7 +377,10 @@ function ctf_gui.show_formspec(player, formname, formdef)
|
||||
"]"
|
||||
end
|
||||
|
||||
formdef._info = formdef
|
||||
context[player] = formdef
|
||||
|
||||
minetest.show_formspec(player, formdef.formname, formspec)
|
||||
end
|
||||
|
||||
dofile(minetest.get_modpath("ctf_gui").."/dev.lua")
|
||||
|
122
mods/apis/ctf_settings/init.lua
Normal file
122
mods/apis/ctf_settings/init.lua
Normal file
@ -0,0 +1,122 @@
|
||||
ctf_settings = {
|
||||
settings = {},
|
||||
settings_list = {},
|
||||
}
|
||||
|
||||
local FORMSIZE = {x = 8, y = 5}
|
||||
local SCROLLBAR_W = 0.4
|
||||
|
||||
minetest.after(0, function()
|
||||
table.sort(ctf_settings.settings_list, function(a, b) return a < b end)
|
||||
end)
|
||||
|
||||
--[[
|
||||
Settings should only be registered at loadtime
|
||||
|
||||
{
|
||||
type = "bool",
|
||||
label = "Setting name/label",
|
||||
description = "Text in tooltip",
|
||||
default = "default value",
|
||||
on_change = function(player, new_value)
|
||||
<...>
|
||||
end
|
||||
}
|
||||
]]
|
||||
---@param def table
|
||||
function ctf_settings.register(name, def)
|
||||
ctf_settings.settings[name] = def
|
||||
table.insert(ctf_settings.settings_list, name)
|
||||
end
|
||||
|
||||
function ctf_settings.set(player, setting, value)
|
||||
player:get_meta():set_string("ctf_settings:"..setting, value)
|
||||
end
|
||||
|
||||
---@return string Returns the player's chosen setting value, the default given at registration, or if both are unset: ""
|
||||
function ctf_settings.get(player, setting)
|
||||
local value = player:get_meta():get_string("ctf_settings:"..setting)
|
||||
local info = ctf_settings.settings[setting]
|
||||
|
||||
return value == "" and info.default or value
|
||||
end
|
||||
|
||||
minetest.register_on_mods_loaded(function()
|
||||
sfinv.register_page("ctf_settings:settings", {
|
||||
title = "Settings",
|
||||
get = function(self, player, context)
|
||||
local setting_list = {}
|
||||
local lastypos
|
||||
|
||||
for k, setting in ipairs(ctf_settings.settings_list) do
|
||||
local settingdef = ctf_settings.settings[setting]
|
||||
|
||||
if settingdef.type == "bool" then
|
||||
lastypos = (k / 2) - 1
|
||||
setting_list[k] = {
|
||||
"checkbox[0,%f;%s;%s;%s]tooltip[%s;%s]",
|
||||
lastypos,
|
||||
setting,
|
||||
settingdef.label or setting,
|
||||
ctf_settings.get(player, setting),
|
||||
setting,
|
||||
settingdef.description or HumanReadable(setting)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local form = {
|
||||
{"box[-0.1,-0.1;%f,%f;#00000055]", FORMSIZE.x - SCROLLBAR_W, FORMSIZE.y},
|
||||
{"scroll_container[-0.1,0.3;%f,%f;settings_scrollbar;vertical;0.1]",
|
||||
FORMSIZE.x - SCROLLBAR_W,
|
||||
FORMSIZE.y + 0.7
|
||||
},
|
||||
ctf_gui.list_to_formspec_str(setting_list),
|
||||
"scroll_container_end[]",
|
||||
{"scrollbaroptions[max=%d]", math.ceil((lastypos - 3.833) * 11.538)},
|
||||
{"scrollbar[%f,-0.1;%f,%f;vertical;settings_scrollbar;%f]",
|
||||
FORMSIZE.x - SCROLLBAR_W,
|
||||
SCROLLBAR_W,
|
||||
FORMSIZE.y,
|
||||
context and context.settings_scrollbar or 0
|
||||
},
|
||||
}
|
||||
|
||||
return sfinv.make_formspec(player, context, ctf_gui.list_to_formspec_str(form), true)
|
||||
end,
|
||||
on_player_receive_fields = function(self, player, context, fields)
|
||||
local refresh = false
|
||||
|
||||
for field, value in pairs(fields) do
|
||||
local setting = ctf_settings.settings[field]
|
||||
|
||||
if setting then
|
||||
if setting.type == "bool" then
|
||||
local newvalue = value == "true" and "true" or "false"
|
||||
|
||||
ctf_settings.set(player, field, newvalue)
|
||||
|
||||
if setting.on_change then
|
||||
setting.on_change(player, newvalue)
|
||||
end
|
||||
end
|
||||
|
||||
refresh = true
|
||||
end
|
||||
end
|
||||
|
||||
if not refresh then return end
|
||||
|
||||
if fields.settings_scrollbar then
|
||||
local scrollevent = minetest.explode_scrollbar_event(fields.settings_scrollbar)
|
||||
|
||||
if scrollevent.value then
|
||||
context.settings_scrollbar = scrollevent.value
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
sfinv.set_page(player, sfinv.get_page(player))
|
||||
end,
|
||||
})
|
||||
end)
|
2
mods/apis/ctf_settings/mod.conf
Normal file
2
mods/apis/ctf_settings/mod.conf
Normal file
@ -0,0 +1,2 @@
|
||||
name = ctf_settings
|
||||
depends = sfinv, ctf_gui
|
@ -38,4 +38,4 @@ end
|
||||
--- * flag_team
|
||||
function ctf_api.register_on_flag_take(func)
|
||||
table.insert(ctf_api.registered_on_flag_take, func)
|
||||
end
|
||||
end
|
||||
|
@ -29,6 +29,8 @@ local sword_mats = {
|
||||
}
|
||||
}
|
||||
|
||||
local attack_cooldown = ctf_core.init_cooldowns()
|
||||
|
||||
function ctf_melee.simple_register_sword(name, def)
|
||||
local base_def = {
|
||||
description = def.description,
|
||||
@ -74,6 +76,253 @@ function ctf_melee.simple_register_sword(name, def)
|
||||
ctf_melee.registered_swords[name] = base_def
|
||||
end
|
||||
|
||||
local slash_stab_anim_length = ctf_player.animation_time.stab_slash
|
||||
local EXTRA_ANIM_LENGTH = {
|
||||
slash = slash_stab_anim_length * 0.5,
|
||||
stab = 0.1,
|
||||
}
|
||||
|
||||
local SWOOSH_SOUND_DISTANCE = 8
|
||||
local COMBAT_SOUND_DISTANCE = 16
|
||||
local KNOCKBACK = {slash = 6, stab = 0}
|
||||
local HIT_BOOST = 9
|
||||
|
||||
local function dopunch(target, attacker, ignores, attack_capabilities, dir, attack_interval)
|
||||
if target.ref:is_player() and not ignores[target.ref] and
|
||||
ctf_modebase:get_current_mode().can_punchplayer(attacker, target.ref) then
|
||||
ignores[target.ref] = true -- add to the table we were passed
|
||||
|
||||
target.ref:punch(attacker, attack_interval, attack_capabilities, dir)
|
||||
|
||||
minetest.sound_play("player_damage", {
|
||||
object = attacker,
|
||||
exclude_player = target.ref:get_player_name(),
|
||||
pitch = 0.8,
|
||||
gain = 0.4,
|
||||
max_hear_distance = COMBAT_SOUND_DISTANCE,
|
||||
}, true)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function slash_stab_sword_func(keypress, itemstack, user, pointed)
|
||||
local uname = user:get_player_name()
|
||||
local cooldown = attack_cooldown:get(uname)
|
||||
local anim = (keypress == "LMB" and "stab") or "slash"
|
||||
|
||||
if cooldown then
|
||||
-- if the cooldown has <= 0.3 seconds left then let them queue another stab
|
||||
if anim == "stab" and cooldown._time - (os.clock() - cooldown.start_time) <= 0.3 then
|
||||
-- Don't queue a miss
|
||||
if not pointed or pointed.type ~= "object" then
|
||||
return
|
||||
end
|
||||
|
||||
cooldown._on_end = function(self)
|
||||
local player = minetest.get_player_by_name(uname)
|
||||
|
||||
if not player then return end
|
||||
|
||||
local wielded = player:get_wielded_item()
|
||||
|
||||
if wielded:get_name() ~= itemstack:get_name() then return end
|
||||
|
||||
slash_stab_sword_func(keypress, wielded, user, pointed)
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local attack_interval = slash_stab_anim_length + EXTRA_ANIM_LENGTH[anim]
|
||||
|
||||
ctf_player.set_stab_slash_anim(anim, user, EXTRA_ANIM_LENGTH[anim])
|
||||
|
||||
attack_cooldown:set(uname, {
|
||||
_time = attack_interval,
|
||||
_on_end = function(self) -- Repeat attack if player is holding down the button
|
||||
local player = minetest.get_player_by_name(uname)
|
||||
|
||||
if not player then return end
|
||||
|
||||
local controls = player:get_player_control()
|
||||
local wielded = player:get_wielded_item()
|
||||
|
||||
if wielded:get_name() == itemstack:get_name() and (controls.LMB or controls.RMB) then
|
||||
if not controls[keypress] then
|
||||
keypress = (keypress == "LMB") and "RMB" or "LMB"
|
||||
end
|
||||
|
||||
slash_stab_sword_func(keypress, wielded, player, nil)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
local startpos = vector.offset(user:get_pos(), 0, user:get_properties().eye_height, 0)
|
||||
|
||||
local def = itemstack:get_definition()
|
||||
local attack_capabilities = def.tool_capabilities
|
||||
|
||||
local dir = user:get_look_dir()
|
||||
local user_kb_dir = vector.new(dir)
|
||||
|
||||
local ignores = {[user] = true}
|
||||
local section = math.pi/12
|
||||
local hit_player = false
|
||||
local axis
|
||||
local rays
|
||||
|
||||
attack_capabilities.damage_groups.knockback = KNOCKBACK[anim]
|
||||
|
||||
axis = vector.cross(vector.new(dir.z, 0, -dir.x), dir)
|
||||
|
||||
if pointed and pointed.type == "object" then
|
||||
hit_player = dopunch(pointed, user, ignores, attack_capabilities, dir, attack_interval) or hit_player
|
||||
|
||||
pointed = true
|
||||
end
|
||||
|
||||
if anim == "slash" then
|
||||
user_kb_dir = -user_kb_dir
|
||||
rays = {
|
||||
vector.rotate_around_axis(dir, axis, section * 3),
|
||||
vector.rotate_around_axis(dir, axis, section * 2),
|
||||
vector.rotate_around_axis(dir, axis, section ),
|
||||
|
||||
vector.rotate_around_axis(dir, axis, -section * 3),
|
||||
vector.rotate_around_axis(dir, axis, -section * 2),
|
||||
vector.rotate_around_axis(dir, axis, -section ),
|
||||
|
||||
(pointed ~= true) and dir or nil,
|
||||
}
|
||||
|
||||
minetest.sound_play("ctf_melee_whoosh", {
|
||||
object = user,
|
||||
pitch = 1.1,
|
||||
gain = 1.2,
|
||||
max_hear_distance = SWOOSH_SOUND_DISTANCE,
|
||||
}, true)
|
||||
else
|
||||
rays = {
|
||||
(pointed ~= true) and dir or nil,
|
||||
vector.rotate_around_axis(dir, axis, -section),
|
||||
vector.rotate_around_axis(dir, axis, section),
|
||||
}
|
||||
|
||||
minetest.sound_play("ctf_melee_whoosh", {
|
||||
object = user,
|
||||
gain = 1.1,
|
||||
max_hear_distance = SWOOSH_SOUND_DISTANCE,
|
||||
}, true)
|
||||
end
|
||||
|
||||
user_kb_dir.y = math.max(math.min(user_kb_dir.y, 0.4), 0)
|
||||
|
||||
for _, shootdir in ipairs(rays) do
|
||||
local ray = minetest.raycast(startpos, startpos + (shootdir * 4), true, false)
|
||||
|
||||
minetest.add_particle({
|
||||
pos = startpos,
|
||||
velocity = shootdir * 44,
|
||||
expirationtime = 0.1,
|
||||
size = 5,
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
object_collision = false,
|
||||
texture = "ctf_melee_slash.png",
|
||||
glow = 5,
|
||||
})
|
||||
|
||||
for hit in ray do
|
||||
if hit.type ~= "object" then break end
|
||||
|
||||
hit_player = dopunch(hit, user, ignores, attack_capabilities, shootdir, attack_interval) or hit_player
|
||||
end
|
||||
end
|
||||
|
||||
if hit_player then
|
||||
user:add_velocity(user_kb_dir * HIT_BOOST)
|
||||
end
|
||||
end
|
||||
|
||||
function ctf_melee.register_sword(name, def)
|
||||
local base_def = {
|
||||
description = def.description,
|
||||
inventory_image = def.inventory_image,
|
||||
inventory_overlay = def.inventory_overlay,
|
||||
wield_image = def.wield_image,
|
||||
damage_groups = def.damage_groups,
|
||||
disable_mine_anim = true,
|
||||
tool_capabilities = {
|
||||
full_punch_interval = slash_stab_anim_length,
|
||||
max_drop_level=1,
|
||||
groupcaps={
|
||||
snappy={times={[1]=2.5, [2]=1.20, [3]=0.35}, uses=0, maxlevel=3},
|
||||
},
|
||||
punch_attack_uses = 0,
|
||||
},
|
||||
sound = {breaks = "default_tool_breaks"},
|
||||
groups = def.groups or {},
|
||||
}
|
||||
|
||||
local damage_capabilities = base_def.tool_capabilities
|
||||
damage_capabilities.damage_groups = base_def.damage_groups
|
||||
|
||||
base_def.groups.sword = 1
|
||||
|
||||
local function rightclick_func(...)
|
||||
slash_stab_sword_func("RMB", ...)
|
||||
|
||||
if def.rightclick_func then
|
||||
return def.rightclick_func(...)
|
||||
end
|
||||
end
|
||||
|
||||
base_def.on_use = function(itemstack, user, pointed, ...)
|
||||
if pointed then
|
||||
if pointed.type == "object" then
|
||||
if not pointed.ref:is_player() then
|
||||
pointed.ref:punch(user, slash_stab_anim_length, damage_capabilities, vector.new())
|
||||
return
|
||||
end
|
||||
elseif pointed.type == "node" then
|
||||
local node = minetest.get_node(pointed.under)
|
||||
local node_on_punch = minetest.registered_nodes[node.name].on_punch
|
||||
|
||||
if node_on_punch then
|
||||
node_on_punch(pointed.under, node, user, pointed)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
slash_stab_sword_func("LMB", itemstack, user, pointed, ...)
|
||||
end
|
||||
|
||||
base_def.on_place = function(itemstack, user, pointed, ...)
|
||||
local pointed_def = false
|
||||
local node
|
||||
|
||||
if pointed and pointed.under then
|
||||
node = minetest.get_node(pointed.under)
|
||||
pointed_def = minetest.registered_nodes[node.name]
|
||||
end
|
||||
|
||||
if pointed_def and pointed_def.on_rightclick then
|
||||
return minetest.item_place(itemstack, user, pointed)
|
||||
else
|
||||
return rightclick_func(itemstack, user, pointed, ...)
|
||||
end
|
||||
end
|
||||
|
||||
base_def.on_secondary_use = rightclick_func
|
||||
|
||||
minetest.register_tool(name, base_def)
|
||||
ctf_melee.registered_swords[name] = base_def
|
||||
end
|
||||
|
||||
for mat, def in pairs(sword_mats) do
|
||||
ctf_melee.simple_register_sword("ctf_melee:sword_"..mat, def)
|
||||
|
||||
|
@ -1,2 +1,2 @@
|
||||
name = ctf_melee
|
||||
depends = ctf_core
|
||||
depends = ctf_core, ctf_player
|
||||
|
BIN
mods/ctf/ctf_combat/ctf_melee/sounds/ctf_melee_whoosh.ogg
Normal file
BIN
mods/ctf/ctf_combat/ctf_melee/sounds/ctf_melee_whoosh.ogg
Normal file
Binary file not shown.
BIN
mods/ctf/ctf_combat/ctf_melee/textures/ctf_melee_slash.png
Normal file
BIN
mods/ctf/ctf_combat/ctf_melee/textures/ctf_melee_slash.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 589 B |
@ -1,8 +1,11 @@
|
||||
ctf_ranged = {}
|
||||
local hud = mhud.init()
|
||||
local shoot_cooldown = ctf_core.init_cooldowns()
|
||||
|
||||
local scoped = {}
|
||||
ctf_ranged = {
|
||||
scoped = {}
|
||||
}
|
||||
|
||||
local scoped = ctf_ranged.scoped
|
||||
local scale_const = 6
|
||||
local timer = 1
|
||||
|
||||
@ -23,11 +26,17 @@ local function process_ray(ray, user, look_dir, def)
|
||||
|
||||
if hitpoint then
|
||||
if hitpoint.type == "node" then
|
||||
local nodedef = minetest.registered_nodes[minetest.get_node(hitpoint.under).name]
|
||||
local node = minetest.get_node(hitpoint.under)
|
||||
local nodedef = minetest.registered_nodes[node.name]
|
||||
|
||||
if nodedef.groups.snappy or (nodedef.groups.oddly_breakable_by_hand or 0) >= 3 then
|
||||
if nodedef.groups.snappy or nodedef.groups.ranged_breakable or
|
||||
(nodedef.groups.oddly_breakable_by_hand or 0) >= 3 then
|
||||
if not minetest.is_protected(hitpoint.under, user:get_player_name()) then
|
||||
minetest.dig_node(hitpoint.under)
|
||||
if nodedef.groups.ranged_breakable and nodedef.on_dig then
|
||||
nodedef.on_dig(hitpoint.under, node, user)
|
||||
else
|
||||
minetest.dig_node(hitpoint.under)
|
||||
end
|
||||
end
|
||||
else
|
||||
if nodedef.walkable and nodedef.pointable then
|
||||
@ -112,6 +121,7 @@ function ctf_ranged.simple_register_gun(name, def)
|
||||
loaded_def.inventory_overlay = def.texture_overlay
|
||||
loaded_def.wield_image = def.wield_texture or def.texture
|
||||
loaded_def.groups.not_in_creative_inventory = nil
|
||||
loaded_def.on_secondary_use = def.on_secondary_use
|
||||
loaded_def.on_use = function(itemstack, user)
|
||||
if not ctf_ranged.can_use_gun(user, name) then
|
||||
minetest.sound_play("ctf_ranged_click", {pos = user:get_pos()}, true)
|
||||
@ -186,13 +196,17 @@ minetest.register_on_leaveplayer(function(player)
|
||||
scoped[player:get_player_name()] = nil
|
||||
end)
|
||||
|
||||
local function show_scope(name, item_name, fov_mult)
|
||||
function ctf_ranged.show_scope(name, item_name, fov_mult)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if not player then
|
||||
return
|
||||
end
|
||||
|
||||
scoped[name] = item_name
|
||||
scoped[name] = {
|
||||
item_name = item_name,
|
||||
wielditem = player:hud_get_flags().wielditem
|
||||
}
|
||||
|
||||
hud:add(player, "ctf_ranged:scope", {
|
||||
hud_elem_type = "image",
|
||||
position = {x = 0.5, y = 0.5},
|
||||
@ -207,18 +221,17 @@ local function show_scope(name, item_name, fov_mult)
|
||||
|
||||
end
|
||||
|
||||
local function hide_scope(name)
|
||||
function ctf_ranged.hide_scope(name)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if not player then
|
||||
return
|
||||
end
|
||||
|
||||
scoped[name] = nil
|
||||
hud:remove(name, "ctf_ranged:scope")
|
||||
player:set_fov(0)
|
||||
physics.remove(name, "sniper_rifles:scoping")
|
||||
player:hud_set_flags({ wielditem = true })
|
||||
|
||||
player:hud_set_flags({ wielditem = scoped[name].wielditem })
|
||||
scoped[name] = nil
|
||||
end
|
||||
|
||||
ctf_ranged.simple_register_gun("ctf_ranged:pistol", {
|
||||
@ -289,10 +302,10 @@ ctf_ranged.simple_register_gun("ctf_ranged:sniper", {
|
||||
liquid_travel_dist = 10,
|
||||
rightclick_func = function(itemstack, user, pointed, ...)
|
||||
if scoped[user:get_player_name()] then
|
||||
hide_scope(user:get_player_name())
|
||||
ctf_ranged.hide_scope(user:get_player_name())
|
||||
else
|
||||
local item_name = itemstack:get_name():gsub("_loaded", "")
|
||||
show_scope(user:get_player_name(), item_name, 4)
|
||||
ctf_ranged.show_scope(user:get_player_name(), item_name, 4)
|
||||
end
|
||||
end
|
||||
})
|
||||
@ -309,10 +322,10 @@ ctf_ranged.simple_register_gun("ctf_ranged:sniper_magnum", {
|
||||
liquid_travel_dist = 15,
|
||||
rightclick_func = function(itemstack, user, pointed, ...)
|
||||
if scoped[user:get_player_name()] then
|
||||
hide_scope(user:get_player_name())
|
||||
ctf_ranged.hide_scope(user:get_player_name())
|
||||
else
|
||||
local item_name = itemstack:get_name():gsub("_loaded", "")
|
||||
show_scope(user:get_player_name(), item_name, 8)
|
||||
ctf_ranged.show_scope(user:get_player_name(), item_name, 8)
|
||||
end
|
||||
end
|
||||
})
|
||||
@ -332,11 +345,11 @@ minetest.register_globalstep(function(dtime)
|
||||
end
|
||||
|
||||
time = 0
|
||||
for name, original_item in pairs(scoped) do
|
||||
for name, info in pairs(scoped) do
|
||||
local player = minetest.get_player_by_name(name)
|
||||
local wielded_item = player:get_wielded_item():get_name():gsub("_loaded", "")
|
||||
if wielded_item ~= original_item then
|
||||
hide_scope(name)
|
||||
if wielded_item ~= info.item_name then
|
||||
ctf_ranged.hide_scope(name)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
@ -5,7 +5,7 @@ function ctf_core.init_cooldowns()
|
||||
local pname = PlayerName(player)
|
||||
|
||||
if self.players[pname] then
|
||||
self.players[pname]:cancel()
|
||||
self.players[pname]._timer:cancel()
|
||||
|
||||
if not time then
|
||||
self.players[pname] = nil
|
||||
@ -13,7 +13,24 @@ function ctf_core.init_cooldowns()
|
||||
end
|
||||
end
|
||||
|
||||
self.players[pname] = minetest.after(time, function() self.players[pname] = nil end)
|
||||
if type(time) ~= "table" then
|
||||
time = {_time = time}
|
||||
end
|
||||
|
||||
time._timer = minetest.after(time._time, function()
|
||||
if time._on_end then
|
||||
local copy = table.copy(self.players[pname])
|
||||
|
||||
self.players[pname] = nil
|
||||
time._on_end(copy)
|
||||
else
|
||||
self.players[pname] = nil
|
||||
end
|
||||
end)
|
||||
|
||||
time.start_time = os.clock()
|
||||
|
||||
self.players[pname] = time
|
||||
end,
|
||||
get = function(self, player)
|
||||
return self.players[PlayerName(player)]
|
||||
|
@ -43,7 +43,7 @@ minetest.register_node("ctf_map:damage_cobble", {
|
||||
tiles = {"ctf_map_damage_cobble.png"},
|
||||
is_ground_content = false,
|
||||
walkable = true,
|
||||
groups = {cracky=3, stone=2},
|
||||
groups = {cracky=3, stone=2, ranged_breakable=1},
|
||||
on_dig = function(pos, node, digger, extra)
|
||||
if not digger:is_player() then return end
|
||||
|
||||
@ -100,4 +100,4 @@ minetest.register_node("ctf_map:reinforced_cobble", {
|
||||
is_ground_content = false,
|
||||
groups = {cracky = 1, stone = 2},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
})
|
||||
|
@ -87,7 +87,7 @@ minetest.register_chatcommand("ctf_map", {
|
||||
inv:add_item("main", "ctf_map:adminpick")
|
||||
end
|
||||
|
||||
if not ctf_core.settings.server_mode == "mapedit" then
|
||||
if ctf_core.settings.server_mode ~= "mapedit" then
|
||||
minetest.chat_send_player(name,
|
||||
minetest.colorize("red", "It is not recommended to edit maps unless the server is in mapedit mode"))
|
||||
end
|
||||
|
@ -3,7 +3,7 @@ local CURRENT_MAP_VERSION = "2"
|
||||
function ctf_map.skybox_exists(subdir)
|
||||
local list = minetest.get_dir_list(subdir, true)
|
||||
|
||||
return not (table.indexof(list, "skybox") == -1)
|
||||
return table.indexof(list, "skybox") ~= -1
|
||||
end
|
||||
|
||||
function ctf_map.load_map_meta(idx, dirname)
|
||||
|
@ -1,4 +1,4 @@
|
||||
ctf_gui.init()
|
||||
ctf_gui.old_init()
|
||||
|
||||
local context = {}
|
||||
|
||||
@ -40,7 +40,7 @@ function ctf_map.show_map_editor(player)
|
||||
table.sort(dirlist_sorted)
|
||||
|
||||
local selected_map = 1
|
||||
ctf_gui.show_formspec(player, "ctf_map:start", {
|
||||
ctf_gui.old_show_formspec(player, "ctf_map:start", {
|
||||
size = {x = 8, y = 10.2},
|
||||
title = "Capture The Flag Map Editor",
|
||||
description = "Would you like to edit an existing map or create a new one?",
|
||||
@ -92,7 +92,7 @@ function ctf_map.show_map_editor(player)
|
||||
pos = {0.1, 1.8},
|
||||
func = function(pname, fields)
|
||||
minetest.after(0.1, function()
|
||||
ctf_gui.show_formspec(pname, "ctf_map:loading", {
|
||||
ctf_gui.old_show_formspec(pname, "ctf_map:loading", {
|
||||
size = {x = 6, y = 4},
|
||||
title = "Capture The Flag Map Editor",
|
||||
description = "Placing map '"..dirlist_sorted[selected_map].."'. This will take a few seconds..."
|
||||
@ -114,7 +114,7 @@ function ctf_map.show_map_editor(player)
|
||||
pos = {(8-ctf_gui.ELEM_SIZE.x) - 0.3, 1.8},
|
||||
func = function(pname, fields)
|
||||
minetest.after(0.1, function()
|
||||
ctf_gui.show_formspec(pname, "ctf_map:loading", {
|
||||
ctf_gui.old_show_formspec(pname, "ctf_map:loading", {
|
||||
size = {x = 6, y = 4},
|
||||
title = "Capture The Flag Map Editor",
|
||||
description = "Resuming map '"..dirlist_sorted[selected_map]..
|
||||
@ -188,7 +188,7 @@ function ctf_map.show_map_save_form(player, scroll_pos)
|
||||
-- MAP ENABLED
|
||||
elements.enabled = {
|
||||
type = "checkbox", label = "Map Enabled", pos = {0, 2}, default = context[player].enabled,
|
||||
func = function(pname, fields, name) context[pname].enabled = fields[name] == "true" or false end,
|
||||
func = function(pname, fields) context[pname].enabled = fields.enabled == "true" or false end,
|
||||
}
|
||||
|
||||
-- FOLDER NAME, MAP NAME, MAP AUTHOR(S), MAP HINT, MAP LICENSE, OTHER INFO
|
||||
@ -201,8 +201,8 @@ function ctf_map.show_map_save_form(player, scroll_pos)
|
||||
type = "field", label = label,
|
||||
pos = {0, ypos}, size = {6, 0.7},
|
||||
default = context[player][name],
|
||||
func = function(pname, fields, fname)
|
||||
context[pname][name] = fields[fname]
|
||||
func = function(pname, fields)
|
||||
context[pname][name] = fields[name]
|
||||
end,
|
||||
}
|
||||
|
||||
@ -213,8 +213,8 @@ function ctf_map.show_map_save_form(player, scroll_pos)
|
||||
elements.initial_stuff = {
|
||||
type = "field", label = "Map Initial Stuff", pos = {0, ypos}, size = {6, 0.7},
|
||||
default = table.concat(context[player].initial_stuff or {"none"}, ","),
|
||||
func = function(pname, fields, name)
|
||||
context[pname].initial_stuff = string.split(fields[name]:gsub("%s?,%s?", ","), ",")
|
||||
func = function(pname, fields)
|
||||
context[pname].initial_stuff = string.split(fields.initial_stuff:gsub("%s?,%s?", ","), ",")
|
||||
end,
|
||||
}
|
||||
ypos = ypos + 1.4
|
||||
@ -223,8 +223,8 @@ function ctf_map.show_map_save_form(player, scroll_pos)
|
||||
elements.treasures = {
|
||||
type = "textarea", label = "Map Treasures", pos = {0, ypos}, size = {ctf_gui.FORM_SIZE.x-3.6, 2.1},
|
||||
default = context[player].treasures,
|
||||
func = function(pname, fields, name)
|
||||
context[pname].treasures = fields[name]
|
||||
func = function(pname, fields)
|
||||
context[pname].treasures = fields.treasures
|
||||
end,
|
||||
}
|
||||
ypos = ypos + 3.1
|
||||
@ -243,12 +243,12 @@ function ctf_map.show_map_save_form(player, scroll_pos)
|
||||
size = {6, ctf_gui.ELEM_SIZE.y},
|
||||
items = ctf_map.skyboxes,
|
||||
default_idx = table.indexof(ctf_map.skyboxes, context[player].skybox),
|
||||
func = function(pname, fields, name)
|
||||
func = function(pname, fields)
|
||||
local oldval = context[pname].skybox
|
||||
context[pname].skybox = fields[name]
|
||||
context[pname].skybox = fields.skybox
|
||||
|
||||
if context[pname].skybox ~= oldval then
|
||||
skybox.set(PlayerObj(pname), table.indexof(ctf_map.skyboxes, fields[name])-1)
|
||||
skybox.set(PlayerObj(pname), table.indexof(ctf_map.skyboxes, fields.skybox)-1)
|
||||
end
|
||||
end,
|
||||
}
|
||||
@ -279,7 +279,7 @@ function ctf_map.show_map_save_form(player, scroll_pos)
|
||||
pos = {0, ypos},
|
||||
size = {ctf_gui.FORM_SIZE.x/2 - 0.2, 2},
|
||||
items = context[player].game_modes,
|
||||
func = function(pname, fields, fname)
|
||||
func = function(pname, fields)
|
||||
local event = minetest.explode_textlist_event(fields.game_modes)
|
||||
|
||||
if event.type == "DCL" then
|
||||
@ -289,12 +289,12 @@ function ctf_map.show_map_save_form(player, scroll_pos)
|
||||
end
|
||||
end
|
||||
}
|
||||
elements["available_game_modes"] = {
|
||||
elements.available_game_modes = {
|
||||
type = "textlist",
|
||||
pos = {ctf_gui.FORM_SIZE.x/2 + 0.2, ypos},
|
||||
size = {ctf_gui.FORM_SIZE.x/2 - 0.2, 2},
|
||||
items = available_game_modes,
|
||||
func = function(pname, fields, fname)
|
||||
func = function(pname, fields)
|
||||
local event = minetest.explode_textlist_event(fields.available_game_modes)
|
||||
|
||||
if event.type == "DCL" then
|
||||
@ -312,12 +312,12 @@ function ctf_map.show_map_save_form(player, scroll_pos)
|
||||
type = "field", label = label,
|
||||
pos = {0, ypos}, size = {4, 0.7},
|
||||
default = context[player]["phys_"..name] or 1,
|
||||
func = function(pname, fields, fname)
|
||||
func = function(pname, fields)
|
||||
local oldval = context[pname]["phys_"..name]
|
||||
context[pname]["phys_"..name] = tonumber(fields[fname]) or 1
|
||||
context[pname]["phys_"..name] = tonumber(fields[name]) or 1
|
||||
|
||||
if context[pname]["phys_"..name] ~= oldval then
|
||||
physics.set(pname, "ctf_map_editor_"..name, {[name] = tonumber(fields[fname] or 1)})
|
||||
physics.set(pname, "ctf_map_editor_"..name, {[name] = tonumber(fields[name] or 1)})
|
||||
end
|
||||
end,
|
||||
}
|
||||
@ -329,9 +329,9 @@ function ctf_map.show_map_save_form(player, scroll_pos)
|
||||
elements.start_time = {
|
||||
type = "field", label = "Map start_time", pos = {0, ypos}, size = {4, 0.7},
|
||||
default = context[player].start_time or ctf_map.DEFAULT_START_TIME,
|
||||
func = function(pname, fields, name)
|
||||
func = function(pname, fields)
|
||||
local oldval = context[pname].start_time
|
||||
context[pname].start_time = tonumber(fields[name] or ctf_map.DEFAULT_START_TIME)
|
||||
context[pname].start_time = tonumber(fields.start_time or ctf_map.DEFAULT_START_TIME)
|
||||
|
||||
if context[pname].start_time ~= oldval then
|
||||
minetest.registered_chatcommands["time"].func(pname, tostring(context[pname].start_time))
|
||||
@ -344,9 +344,9 @@ function ctf_map.show_map_save_form(player, scroll_pos)
|
||||
elements.time_speed = {
|
||||
type = "field", label = "Map time_speed (Multiplier)", pos = {0, ypos},
|
||||
size = {4, 0.7}, default = context[player].time_speed or "1",
|
||||
func = function(pname, fields, name)
|
||||
func = function(pname, fields)
|
||||
local oldval = context[pname].time_speed
|
||||
context[pname].time_speed = tonumber(fields[name] or "1")
|
||||
context[pname].time_speed = tonumber(fields.time_speed or "1")
|
||||
|
||||
if context[pname].time_speed ~= oldval then
|
||||
minetest.settings:set("time_speed", context[pname].time_speed * 72)
|
||||
@ -363,8 +363,8 @@ function ctf_map.show_map_save_form(player, scroll_pos)
|
||||
label = HumanReadable(teamname) .. " Team",
|
||||
pos = {0, idx},
|
||||
default = def.enabled,
|
||||
func = function(pname, fields, name)
|
||||
context[pname].teams[teamname].enabled = fields[name] == "true" or false
|
||||
func = function(pname, fields)
|
||||
context[pname].teams[teamname].enabled = fields[teamname.."_checkbox"] == "true" or false
|
||||
end,
|
||||
}
|
||||
idx = idx + 1
|
||||
@ -464,10 +464,10 @@ function ctf_map.show_map_save_form(player, scroll_pos)
|
||||
pos = {7.2, idx},
|
||||
size = {1, ctf_gui.ELEM_SIZE.y},
|
||||
default = context[player].chests[id].amount,
|
||||
func = function(pname, fields, name)
|
||||
func = function(pname, fields)
|
||||
if not context[pname].chests[id] then return end
|
||||
|
||||
local newnum = tonumber(fields[name])
|
||||
local newnum = tonumber(fields["chestzone_chests_"..id])
|
||||
if newnum then
|
||||
context[pname].chests[id].amount = newnum
|
||||
end
|
||||
@ -530,7 +530,7 @@ function ctf_map.show_map_save_form(player, scroll_pos)
|
||||
idx = idx + 1
|
||||
|
||||
-- Show formspec
|
||||
ctf_gui.show_formspec(player, "ctf_map:save", {
|
||||
ctf_gui.old_show_formspec(player, "ctf_map:save", {
|
||||
title = "Capture The Flag Map Editor",
|
||||
description = "Save your map or edit the config.\nRemember to press ENTER after writing to a field",
|
||||
scrollheight = 176 + ((idx - 24) * 10) + 4,
|
||||
|
@ -163,6 +163,33 @@ local function end_combat_mode(player, reason, killer, weapon_image)
|
||||
ctf_combat_mode.end_combat(player)
|
||||
end
|
||||
|
||||
local function can_punchplayer(player, hitter)
|
||||
if not ctf_modebase.match_started then
|
||||
return false, "The match hasn't started yet!"
|
||||
end
|
||||
|
||||
local pname, hname = player:get_player_name(), hitter:get_player_name()
|
||||
local pteam, hteam = ctf_teams.get(player), ctf_teams.get(hitter)
|
||||
|
||||
if not ctf_modebase.remove_respawn_immunity(hitter) then
|
||||
return false, "You can't attack while immune"
|
||||
end
|
||||
|
||||
if not pteam then
|
||||
return false, pname .. " is not in a team!"
|
||||
end
|
||||
|
||||
if not hteam then
|
||||
return false, "You are not in a team!"
|
||||
end
|
||||
|
||||
if pteam == hteam and pname ~= hname then
|
||||
return false, pname .. " is on your team!"
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return {
|
||||
on_new_match = function()
|
||||
team_list = {}
|
||||
@ -424,37 +451,21 @@ return {
|
||||
|
||||
return "You need at least 10 score to access this chest", deny_pro
|
||||
end,
|
||||
can_punchplayer = can_punchplayer,
|
||||
on_punchplayer = function(player, hitter, damage, _, tool_capabilities)
|
||||
if not hitter:is_player() or player:get_hp() <= 0 then return false end
|
||||
|
||||
if not ctf_modebase.match_started then
|
||||
return false, "The match hasn't started yet!"
|
||||
end
|
||||
local allowed, message = can_punchplayer(player, hitter)
|
||||
|
||||
local pname, hname = player:get_player_name(), hitter:get_player_name()
|
||||
local pteam, hteam = ctf_teams.get(player), ctf_teams.get(hitter)
|
||||
|
||||
if not ctf_modebase.remove_respawn_immunity(hitter) then
|
||||
return false, "You can't attack while immune"
|
||||
end
|
||||
|
||||
if not pteam then
|
||||
return false, pname .. " is not in a team!"
|
||||
end
|
||||
|
||||
if not hteam then
|
||||
return false, "You are not in a team!"
|
||||
end
|
||||
|
||||
if pteam == hteam and pname ~= hname then
|
||||
return false, pname .. " is on your team!"
|
||||
if not allowed then
|
||||
return false, message
|
||||
end
|
||||
|
||||
local weapon_image = get_weapon_image(hitter, tool_capabilities)
|
||||
|
||||
if player:get_hp() <= damage then
|
||||
end_combat_mode(pname, "punch", hname, weapon_image)
|
||||
elseif pname ~= hname then
|
||||
end_combat_mode(player:get_player_name(), "punch", hitter:get_player_name(), weapon_image)
|
||||
elseif player:get_player_name() ~= hitter:get_player_name() then
|
||||
ctf_combat_mode.add_hitter(player, hitter, weapon_image, 15)
|
||||
end
|
||||
|
||||
|
@ -15,7 +15,7 @@ if ctf_core.settings.server_mode == "play" then
|
||||
end
|
||||
|
||||
local function show_flag_color_form(player, target_pos, param2)
|
||||
ctf_gui.show_formspec(player, "ctf_modebase:flag_color_select", {
|
||||
ctf_gui.old_show_formspec(player, "ctf_modebase:flag_color_select", {
|
||||
title = "Flag Color Selection",
|
||||
description = "Choose a color for this flag",
|
||||
privs = {ctf_map_editor = true},
|
||||
@ -30,7 +30,7 @@ local function show_flag_color_form(player, target_pos, param2)
|
||||
label = "Choose",
|
||||
exit = true,
|
||||
pos = {"center", 1.5},
|
||||
func = function(playername, fields, field_name)
|
||||
func = function(playername, fields)
|
||||
if not target_pos or not fields.teams then return end
|
||||
|
||||
minetest.set_node(target_pos, {name = "ctf_modebase:flag_top_"..fields.teams, param2 = param2})
|
||||
|
@ -38,7 +38,7 @@ ctf_modebase = {
|
||||
flag_captured = {},
|
||||
}
|
||||
|
||||
ctf_gui.init()
|
||||
ctf_gui.old_init()
|
||||
|
||||
function ctf_modebase.announce(msg)
|
||||
end
|
||||
|
@ -21,8 +21,8 @@ local function show_catalog(pname, current_map)
|
||||
},
|
||||
rows = ctf_modebase.map_catalog.map_names,
|
||||
default_idx = current_map,
|
||||
func = function(_, fields, name)
|
||||
local evt = minetest.explode_table_event(fields[name])
|
||||
func = function(_, fields)
|
||||
local evt = minetest.explode_table_event(fields.list)
|
||||
if evt.type == "CHG" then
|
||||
show_catalog(pname, evt.row)
|
||||
end
|
||||
@ -153,7 +153,7 @@ local function show_catalog(pname, current_map)
|
||||
}
|
||||
end
|
||||
|
||||
ctf_gui.show_formspec(pname, "ctf_map:catalog", formspec)
|
||||
ctf_gui.old_show_formspec(pname, "ctf_map:catalog", formspec)
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("maps", {
|
||||
|
@ -1,11 +1,30 @@
|
||||
local hud = mhud.init()
|
||||
local marker_cooldown = ctf_core.init_cooldowns()
|
||||
local markers = {}
|
||||
|
||||
local MARKER_LIFETIME = 20
|
||||
local MARKER_RANGE = 150
|
||||
local MARKER_PLACE_INTERVAL = 5
|
||||
|
||||
ctf_modebase.markers = {}
|
||||
|
||||
-- Code taken from mods/mtg/mtg_binoculars, changed default FOV
|
||||
function binoculars.update_player_property(player)
|
||||
local new_zoom_fov = 84
|
||||
|
||||
if player:get_inventory():contains_item(
|
||||
"main", "binoculars:binoculars") then
|
||||
new_zoom_fov = 10
|
||||
elseif minetest.is_creative_enabled(player:get_player_name()) then
|
||||
new_zoom_fov = 15
|
||||
end
|
||||
|
||||
-- Only set property if necessary to avoid player mesh reload
|
||||
if player:get_properties().zoom_fov ~= new_zoom_fov then
|
||||
player:set_properties({zoom_fov = new_zoom_fov})
|
||||
end
|
||||
end
|
||||
|
||||
local function add_marker(pname, pteam, message, pos, owner)
|
||||
if not hud:get(pname, "marker_" .. owner) then
|
||||
hud:add(pname, "marker_" .. owner, {
|
||||
@ -88,72 +107,80 @@ ctf_api.register_on_match_end(function()
|
||||
hud:remove_all()
|
||||
end)
|
||||
|
||||
local function marker_func(name, param)
|
||||
local pteam = ctf_teams.get(name)
|
||||
|
||||
if marker_cooldown:get(name) then
|
||||
return false, "You can only place a marker every "..MARKER_PLACE_INTERVAL.." seconds"
|
||||
end
|
||||
|
||||
if not pteam then
|
||||
return false, "You need to be in a team to use markers!"
|
||||
end
|
||||
|
||||
local player = minetest.get_player_by_name(name)
|
||||
local pos1 = vector.offset(player:get_pos(), 0, player:get_properties().eye_height, 0)
|
||||
|
||||
if param == "" then
|
||||
param = "Look here!"
|
||||
end
|
||||
|
||||
local ray = minetest.raycast(
|
||||
pos1, vector.add(pos1, vector.multiply(player:get_look_dir(), MARKER_RANGE),
|
||||
true, false
|
||||
))
|
||||
local pointed = ray:next()
|
||||
|
||||
if pointed and pointed.type == "object" and pointed.ref == player then
|
||||
pointed = ray:next()
|
||||
end
|
||||
|
||||
if not pointed then
|
||||
return false, "Can't find anything to mark, too far away!"
|
||||
end
|
||||
|
||||
local message = string.format("m [%s]: %s", name, param)
|
||||
local pos
|
||||
|
||||
if pointed.type == "object" then
|
||||
local concat
|
||||
local obj = pointed.ref
|
||||
local entity = obj:get_luaentity()
|
||||
|
||||
-- If object is a player, append player name to display text
|
||||
-- Else if obj is item entity, append item description and count to str.
|
||||
if obj:is_player() then
|
||||
concat = obj:get_player_name()
|
||||
elseif entity then
|
||||
if entity.name == "__builtin:item" then
|
||||
local stack = ItemStack(entity.itemstring)
|
||||
local itemdef = minetest.registered_items[stack:get_name()]
|
||||
|
||||
-- Fallback to itemstring if description doesn't exist
|
||||
concat = itemdef.description or entity.itemstring
|
||||
concat = concat .. " " .. stack:get_count()
|
||||
end
|
||||
end
|
||||
|
||||
pos = obj:get_pos()
|
||||
if concat then
|
||||
message = message .. " <" .. concat .. ">"
|
||||
end
|
||||
else
|
||||
pos = pointed.under
|
||||
end
|
||||
|
||||
ctf_modebase.markers.add(name, message, pos)
|
||||
|
||||
marker_cooldown:set(name, MARKER_PLACE_INTERVAL)
|
||||
|
||||
return true, "Marker is placed!"
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("m", {
|
||||
description = "Place a marker in your look direction",
|
||||
privs = {interact = true, shout = true},
|
||||
func = function(name, param)
|
||||
local pteam = ctf_teams.get(name)
|
||||
|
||||
if not pteam then
|
||||
return false, "You need to be in a team to use markers!"
|
||||
end
|
||||
|
||||
local player = minetest.get_player_by_name(name)
|
||||
local pos1 = vector.offset(player:get_pos(), 0, player:get_properties().eye_height, 0)
|
||||
|
||||
if param == "" then
|
||||
param = "Look here!"
|
||||
end
|
||||
|
||||
local ray = minetest.raycast(
|
||||
pos1, vector.add(pos1, vector.multiply(player:get_look_dir(), MARKER_RANGE),
|
||||
true, false
|
||||
))
|
||||
local pointed = ray:next()
|
||||
|
||||
if pointed and pointed.type == "object" and pointed.ref == player then
|
||||
pointed = ray:next()
|
||||
end
|
||||
|
||||
if not pointed then
|
||||
return false, "Can't find anything to mark, too far away!"
|
||||
end
|
||||
|
||||
local message = string.format("m [%s]: %s", name, param)
|
||||
local pos
|
||||
|
||||
if pointed.type == "object" then
|
||||
local concat
|
||||
local obj = pointed.ref
|
||||
local entity = obj:get_luaentity()
|
||||
|
||||
-- If object is a player, append player name to display text
|
||||
-- Else if obj is item entity, append item description and count to str.
|
||||
if obj:is_player() then
|
||||
concat = obj:get_player_name()
|
||||
elseif entity then
|
||||
if entity.name == "__builtin:item" then
|
||||
local stack = ItemStack(entity.itemstring)
|
||||
local itemdef = minetest.registered_items[stack:get_name()]
|
||||
|
||||
-- Fallback to itemstring if description doesn't exist
|
||||
concat = itemdef.description or entity.itemstring
|
||||
concat = concat .. " " .. stack:get_count()
|
||||
end
|
||||
end
|
||||
|
||||
pos = obj:get_pos()
|
||||
if concat then
|
||||
message = message .. " <" .. concat .. ">"
|
||||
end
|
||||
else
|
||||
pos = pointed.under
|
||||
end
|
||||
|
||||
ctf_modebase.markers.add(name, message, pos)
|
||||
|
||||
return true, "Marker is placed!"
|
||||
end
|
||||
func = marker_func
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("mr", {
|
||||
@ -163,3 +190,38 @@ minetest.register_chatcommand("mr", {
|
||||
return true, "Marker is removed!"
|
||||
end
|
||||
})
|
||||
|
||||
local check_interval = 0.3
|
||||
local timer = 0
|
||||
minetest.register_globalstep(function(dtime)
|
||||
timer = timer + dtime
|
||||
|
||||
if timer < check_interval then return end
|
||||
timer = 0
|
||||
|
||||
for _, player in pairs(minetest.get_connected_players()) do
|
||||
local controls = player:get_player_control()
|
||||
|
||||
if controls.zoom then
|
||||
local marker_text = false
|
||||
|
||||
if controls.LMB then
|
||||
marker_text = ""
|
||||
elseif controls.RMB then
|
||||
marker_text = "Defend!"
|
||||
end
|
||||
|
||||
if marker_text then
|
||||
local success, msg = marker_func(player:get_player_name(), marker_text)
|
||||
|
||||
if not success and msg then
|
||||
hud_events.new(player, {
|
||||
text = msg,
|
||||
color = "warning",
|
||||
quick = true,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
@ -45,7 +45,7 @@ local function start_new_match()
|
||||
end
|
||||
|
||||
if restart_on_next_match then
|
||||
minetest.request_shutdown("Restarting server at imperator request.", true)
|
||||
minetest.request_shutdown("Restarting server at imperator request.\nTip: Count to 7 before clicking reconnect", true)
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -1,2 +1,2 @@
|
||||
name = ctf_modebase
|
||||
depends = ctf_api, ctf_core, ctf_teams, ctf_gui, ctf_map, ctf_healing, crafting, mhud, default
|
||||
depends = ctf_api, ctf_core, ctf_teams, ctf_gui, ctf_map, ctf_healing, crafting, mhud, default, binoculars
|
||||
|
@ -32,6 +32,7 @@ local function show_modechoose_form(player)
|
||||
label = i,
|
||||
exit = true,
|
||||
pos = {"center", i},
|
||||
size = {1.4, 0.7},
|
||||
func = function()
|
||||
if votes then
|
||||
player_vote(player, i)
|
||||
@ -40,8 +41,8 @@ local function show_modechoose_form(player)
|
||||
}
|
||||
end
|
||||
|
||||
ctf_gui.show_formspec(player, "ctf_modebase:mode_select", {
|
||||
size = {x = 8, y = 8},
|
||||
ctf_gui.old_show_formspec(player, "ctf_modebase:mode_select", {
|
||||
size = {x = 8, y = MAX_ROUNDS + 3},
|
||||
title = "Mode: "..HumanReadable(new_mode),
|
||||
description = "Please vote on how many matches you would like to play",
|
||||
elements = elements,
|
||||
|
@ -1,4 +1,5 @@
|
||||
local RESPAWN_SECONDS = 7
|
||||
local AUTO_RESPAWN_TIME = 0.4
|
||||
local respawn_delay = {}
|
||||
local hud = mhud.init()
|
||||
|
||||
@ -75,6 +76,23 @@ local function respawn(player, time)
|
||||
run_respawn_timer(pname)
|
||||
end
|
||||
|
||||
local function trigger_respawn(pname)
|
||||
if respawn_delay[pname] then
|
||||
if respawn_delay[pname].autorespawn then
|
||||
respawn_delay[pname].autorespawn:cancel()
|
||||
respawn_delay[pname].autorespawn = nil
|
||||
end
|
||||
|
||||
respawn(minetest.get_player_by_name(pname), RESPAWN_SECONDS)
|
||||
else
|
||||
local player = minetest.get_player_by_name(pname)
|
||||
|
||||
if player then
|
||||
ctf_modebase.on_respawnplayer(player)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ctf_modebase.prepare_respawn_delay(player)
|
||||
local pname = player:get_player_name()
|
||||
if respawn_delay[pname] then return end
|
||||
@ -90,6 +108,11 @@ function ctf_modebase.prepare_respawn_delay(player)
|
||||
player:set_attach(obj)
|
||||
respawn_delay[pname].obj = obj
|
||||
end
|
||||
|
||||
respawn_delay[pname].autorespawn = minetest.after(AUTO_RESPAWN_TIME, function()
|
||||
minetest.close_formspec(pname, "") -- This is the only way to close clientside formspecs
|
||||
trigger_respawn(pname)
|
||||
end)
|
||||
end
|
||||
|
||||
ctf_api.register_on_match_end(function()
|
||||
@ -117,11 +140,9 @@ minetest.register_on_leaveplayer(function(player)
|
||||
end)
|
||||
|
||||
minetest.register_on_respawnplayer(function(player)
|
||||
if respawn_delay[player:get_player_name()] then
|
||||
respawn(player, RESPAWN_SECONDS)
|
||||
else
|
||||
ctf_modebase.on_respawnplayer(player)
|
||||
end
|
||||
local pname = player:get_player_name()
|
||||
|
||||
trigger_respawn(pname)
|
||||
|
||||
return true
|
||||
end)
|
||||
|
@ -237,7 +237,7 @@ function ctf_modebase.summary.show_gui_sorted(name, rankings, special_rankings,
|
||||
}
|
||||
end
|
||||
|
||||
ctf_gui.show_formspec(name, "ctf_modebase:summary", formspec)
|
||||
ctf_gui.old_show_formspec(name, "ctf_modebase:summary", formspec)
|
||||
end
|
||||
|
||||
ctf_core.register_chatcommand_alias("summary", "s", {
|
||||
|
BIN
mods/ctf/ctf_modebase/textures/ctf_modebase_group.png
Normal file
BIN
mods/ctf/ctf_modebase/textures/ctf_modebase_group.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 630 B |
@ -1,14 +1,16 @@
|
||||
ctf_gui.init()
|
||||
|
||||
local cooldowns = ctf_core.init_cooldowns()
|
||||
local CLASS_SWITCH_COOLDOWN = 30
|
||||
|
||||
local readable_class_list = {"Knight", "Ranged", "Support"}
|
||||
local classes = {}
|
||||
|
||||
local class_list = {"knight", "ranged", "support"}
|
||||
local class_props = {
|
||||
knight = {
|
||||
name = "Knight",
|
||||
description = "High HP class with a sword capable of strong damage bursts, +50% health points",
|
||||
hp_max = 28,
|
||||
description = "High HP class with a sword capable of short damage bursts",
|
||||
hp_max = 30,
|
||||
visual_size = vector.new(1.1, 1.05, 1.1),
|
||||
items = {
|
||||
"ctf_mode_classes:knight_sword",
|
||||
@ -20,7 +22,7 @@ local class_props = {
|
||||
},
|
||||
support = {
|
||||
name = "Support",
|
||||
description = "Normal HP class with healing bandages, an immunity ability, and building tools, +10% speed",
|
||||
description = "Helper class with healing bandages, an immunity ability, and building gear",
|
||||
physics = {speed = 1.1},
|
||||
items = {
|
||||
"ctf_mode_classes:support_bandage",
|
||||
@ -33,12 +35,14 @@ local class_props = {
|
||||
"ctf_ranged:shotgun",
|
||||
"ctf_melee:",
|
||||
},
|
||||
disallowed_items_markup = {
|
||||
["ctf_melee:"] = "default_tool_steelsword.png^ctf_modebase_group.png",
|
||||
},
|
||||
},
|
||||
ranged = {
|
||||
name = "Ranged",
|
||||
description = "Low HP ranged class with a rifle/grenade launcher gun, and a scaling ladder for reaching high places",
|
||||
hp_max = 14,
|
||||
visual_size = vector.new(0.9, 0.95, 0.9),
|
||||
name = "Scout",
|
||||
description = "Ranged class with a scoped rifle/grenade launcher and a scaling ladder for reaching high places",
|
||||
visual_size = vector.new(0.9, 1, 0.9),
|
||||
items = {
|
||||
"ctf_mode_classes:ranged_rifle_loaded",
|
||||
"ctf_mode_classes:scaling_ladder"
|
||||
@ -46,9 +50,54 @@ local class_props = {
|
||||
disallowed_items = {
|
||||
"ctf_melee:",
|
||||
},
|
||||
disallowed_items_markup = {
|
||||
["ctf_melee:"] = "default_tool_steelsword.png^ctf_modebase_group.png",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
minetest.register_on_mods_loaded(function()
|
||||
for k, class_prop in pairs(class_props) do
|
||||
local items_markup = ""
|
||||
local disallowed_items_markup = ""
|
||||
|
||||
for _, iname in ipairs(class_prop.items or {}) do
|
||||
local item = ItemStack(iname)
|
||||
local count = item:get_count()
|
||||
|
||||
if count <= 1 then
|
||||
count = nil
|
||||
else
|
||||
count = " x"..count
|
||||
end
|
||||
|
||||
local desc = string.split(item:get_description(), "\n", false, 1)
|
||||
items_markup = string.format("%s%s\n<item name=%s float=left width=48>\n\n\n",
|
||||
items_markup,
|
||||
minetest.formspec_escape(desc[1]) .. (count and count or ""),
|
||||
item:get_name()
|
||||
)
|
||||
end
|
||||
|
||||
for _, iname in ipairs(class_prop.disallowed_items or {}) do
|
||||
if minetest.registered_items[iname] then
|
||||
disallowed_items_markup = string.format("%s<item name=%s width=48>",
|
||||
disallowed_items_markup,
|
||||
iname
|
||||
)
|
||||
else
|
||||
disallowed_items_markup = string.format("%s<img name=%s width=48>",
|
||||
disallowed_items_markup,
|
||||
class_prop.disallowed_items_markup[iname]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
class_props[k].items_markup = items_markup:sub(1, -2) -- Remove \n at the end of str
|
||||
class_props[k].disallowed_items_markup = disallowed_items_markup
|
||||
end
|
||||
end)
|
||||
|
||||
local function dist_from_flag(player)
|
||||
local tname = ctf_teams.get(player)
|
||||
if not tname then return 0 end
|
||||
@ -60,8 +109,21 @@ end
|
||||
--- Knight Sword
|
||||
--
|
||||
|
||||
local KNIGHT_COOLDOWN_TIME = 42
|
||||
local KNIGHT_USAGE_TIME = 12
|
||||
-- ctf_melee.register_sword("ctf_mode_classes:knight_sword", {
|
||||
-- description = "Knight Sword",
|
||||
-- inventory_image = "default_tool_bronzesword.png",
|
||||
-- damage_groups = {fleshy = 5},
|
||||
-- })
|
||||
|
||||
local KNIGHT_COOLDOWN_TIME = 26
|
||||
local KNIGHT_USAGE_TIME = 8
|
||||
|
||||
ctf_settings.register("ctf_mode_classes:simple_knight_activate", {
|
||||
type = "bool",
|
||||
label = "[Classes] Simple Knight sword activation",
|
||||
description = "If enabled you don't need to hold Sneak/Run to activate the rage ability",
|
||||
default = "false",
|
||||
})
|
||||
|
||||
ctf_melee.simple_register_sword("ctf_mode_classes:knight_sword", {
|
||||
description = "Knight Sword\n" .. minetest.colorize("gold",
|
||||
@ -73,8 +135,10 @@ ctf_melee.simple_register_sword("ctf_mode_classes:knight_sword", {
|
||||
damage_groups = {fleshy = 7},
|
||||
full_punch_interval = 0.7,
|
||||
rightclick_func = function(itemstack, user, pointed)
|
||||
local ctl = user:get_player_control()
|
||||
if not ctl.sneak and not ctl.aux1 then return end
|
||||
if ctf_settings.get(user, "ctf_mode_classes:simple_knight_activate") ~= "true" then
|
||||
local ctl = user:get_player_control()
|
||||
if not ctl.sneak and not ctl.aux1 then return end
|
||||
end
|
||||
|
||||
local pname = user:get_player_name()
|
||||
|
||||
@ -110,16 +174,19 @@ ctf_melee.simple_register_sword("ctf_mode_classes:knight_sword", {
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
--
|
||||
--- Ranged Gun
|
||||
--
|
||||
|
||||
local RANGED_COOLDOWN_TIME = 36
|
||||
local RANGED_COOLDOWN_TIME = 31
|
||||
local RANGED_ZOOM_MULT = 3
|
||||
|
||||
local scoped = ctf_ranged.scoped
|
||||
ctf_ranged.simple_register_gun("ctf_mode_classes:ranged_rifle", {
|
||||
type = "classes_rifle",
|
||||
description = "Rifle\n" .. minetest.colorize("gold",
|
||||
"(Sneak/Run) + Rightclick to launch grenade ("..RANGED_COOLDOWN_TIME.."s cooldown)"),
|
||||
description = "Scout Rifle\n" .. minetest.colorize("gold",
|
||||
"Rightclick + (Sneak/Run) to launch grenade ("..RANGED_COOLDOWN_TIME.."s cooldown), otherwise will toggle scope"),
|
||||
texture = "ctf_mode_classes_ranged_rifle.png",
|
||||
texture_overlay = "ctf_modebase_special_item.png^[transformFX",
|
||||
wield_texture = "ctf_mode_classes_ranged_rifle.png",
|
||||
@ -131,7 +198,20 @@ ctf_ranged.simple_register_gun("ctf_mode_classes:ranged_rifle", {
|
||||
liquid_travel_dist = 4,
|
||||
rightclick_func = function(itemstack, user, pointed)
|
||||
local ctl = user:get_player_control()
|
||||
if not ctl.sneak and not ctl.aux1 then return end
|
||||
|
||||
if not ctl.sneak and not ctl.aux1 then
|
||||
local uname = user:get_player_name()
|
||||
|
||||
if not ctl.zoom then
|
||||
if scoped[uname] then
|
||||
ctf_ranged.hide_scope(uname)
|
||||
else
|
||||
ctf_ranged.show_scope(uname, "ctf_mode_classes:ranged_rifle", RANGED_ZOOM_MULT)
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if itemstack:get_wear() == 0 then
|
||||
grenades.throw_grenade("grenades:frag", 24, user)
|
||||
@ -243,8 +323,6 @@ ctf_healing.register_bandage("ctf_mode_classes:support_bandage", {
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
local classes = {}
|
||||
function classes.get_name(player)
|
||||
local meta = player:get_meta()
|
||||
|
||||
@ -261,6 +339,10 @@ function classes.get(player)
|
||||
return class_props[classes.get_name(player)]
|
||||
end
|
||||
|
||||
function classes.get_skin_overlay(player_or_class, class)
|
||||
return "^ctf_mode_classes_" .. (class and player_or_class or classes.get_name(player_or_class)) .. "_overlay.png"
|
||||
end
|
||||
|
||||
function classes.update(player)
|
||||
local class = classes.get(player)
|
||||
|
||||
@ -309,14 +391,10 @@ local function select_class(player, classname)
|
||||
end
|
||||
end
|
||||
|
||||
function classes.show_class_formspec(player, selected)
|
||||
function classes.show_class_formspec(player)
|
||||
player = PlayerObj(player)
|
||||
if not player then return end
|
||||
|
||||
if not selected then
|
||||
selected = table.indexof(class_list, classes.get_name(player))
|
||||
end
|
||||
|
||||
if not cooldowns:get(player) then
|
||||
if dist_from_flag(player) > 5 then
|
||||
hud_events.new(player, {
|
||||
@ -327,38 +405,96 @@ function classes.show_class_formspec(player, selected)
|
||||
return
|
||||
end
|
||||
|
||||
local elements = {}
|
||||
local pteam = ctf_teams.get(player)
|
||||
|
||||
elements.class_select = {
|
||||
type = "dropdown",
|
||||
items = readable_class_list,
|
||||
default_idx = selected,
|
||||
pos = {x = 0, y = 0.5},
|
||||
func = function(playername, fields, field_name)
|
||||
local new_idx = table.indexof(readable_class_list, fields[field_name])
|
||||
ctf_gui.show_formspec(player, "ctf_mode_classes:class_form", function(context)
|
||||
local form_x, form_y = 12, 10
|
||||
|
||||
if new_idx ~= selected then
|
||||
classes.show_class_formspec(playername, new_idx)
|
||||
local bar_h = 2.2
|
||||
local bw = 5
|
||||
|
||||
local class = context.class
|
||||
local class_prop = class_props[class]
|
||||
|
||||
return ctf_gui.list_to_formspec_str({
|
||||
"formspec_version[4]",
|
||||
{"size[%f,%f]", form_x, form_y+1.1},
|
||||
"real_coordinates[true]",
|
||||
{"hypertext[0,0.2;%f,1.3;title;<bigger><center><b>Class Selection</b></center></bigger>]", form_x},
|
||||
|
||||
{"hypertext[0,%f;%f,1;classname;<bigger><center><style color=#0DD>%s</style></center></bigger>]",
|
||||
bar_h-0.9,
|
||||
form_x,
|
||||
class_prop.name
|
||||
},
|
||||
{"box[0,%f;%f,0.8;#00000022]", bar_h-0.9, form_x},
|
||||
{"image_button[0.1,%f;0.8,0.8;creative_prev_icon.png;prev_class;]", bar_h-0.9},
|
||||
{"image_button[%f,%f;0.8,0.8;creative_next_icon.png;next_class;]", form_x-0.9, bar_h-0.9},
|
||||
|
||||
{"box[0.1,2.3;%f,%f;#00000077]", (form_x/2)-0.8, form_y-2.4},
|
||||
{"model[0.1,2.3;%f,%f;classpreview;character.b3d;%s;{0,160};;;]",
|
||||
(form_x/2)-0.8,
|
||||
form_y-2.4,
|
||||
ctf_cosmetics.get_colored_skin(player, pteam and ctf_teams.team[pteam].color) ..
|
||||
classes.get_skin_overlay(class, true) or ""
|
||||
},
|
||||
{[[hypertext[%f,2.3;%f,%f;info;<global font=mono background=#00000044>
|
||||
<center>%s</center>
|
||||
<img name=heart.png width=20 float=left> %d HP
|
||||
%s
|
||||
%s
|
||||
Disallowed Items
|
||||
%s
|
||||
] ]],
|
||||
(form_x/2)-0.6,
|
||||
(form_x/2)+0.5,
|
||||
form_y-2.4,
|
||||
class_prop.description,
|
||||
class_prop.hp_max or minetest.PLAYER_MAX_HP_DEFAULT,
|
||||
class_prop.physics and class_prop.physics.speed and
|
||||
"<img name=sprint_stamina_icon.png width=20 float=left> "..class_prop.physics.speed.."x Speed\n" or "",
|
||||
class_prop.items_markup,
|
||||
class_prop.disallowed_items_markup
|
||||
},
|
||||
"style[select;font_size=*1.5]",
|
||||
{"button_exit[%f,%f;%f,1;select;Choose Class]", (form_x/2) - (bw/2), form_y, bw},
|
||||
})
|
||||
end, {
|
||||
class = classes.get_name(player) or "knight",
|
||||
_on_formspec_input = function(pname, context, fields)
|
||||
if fields.prev_class then
|
||||
local classidx = table.indexof(class_list, context.class) - 1
|
||||
|
||||
if classidx < 1 then
|
||||
classidx = #class_list
|
||||
end
|
||||
|
||||
context.class = class_list[classidx]
|
||||
|
||||
return "refresh"
|
||||
elseif fields.next_class then
|
||||
local classidx = table.indexof(class_list, context.class) + 1
|
||||
|
||||
if classidx > #class_list then
|
||||
classidx = 1
|
||||
end
|
||||
|
||||
context.class = class_list[classidx]
|
||||
|
||||
return "refresh"
|
||||
elseif fields.select and classes.get_name(player) ~= context.class then
|
||||
if dist_from_flag(player) > 5 then
|
||||
hud_events.new(player, {
|
||||
quick = true,
|
||||
text = "You can only change class at your flag!",
|
||||
color = "warning",
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
select_class(pname, context.class)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
elements.select_class = {
|
||||
type = "button",
|
||||
exit = true,
|
||||
label = "Choose Class",
|
||||
pos = {x = ctf_gui.ELEM_SIZE.x + 0.5, y = 0.5},
|
||||
func = function(playername, fields, field_name)
|
||||
select_class(playername, class_list[selected])
|
||||
end,
|
||||
}
|
||||
|
||||
ctf_gui.show_formspec(player, "ctf_mode_classes:class_form", {
|
||||
size = {x = (ctf_gui.ELEM_SIZE.x * 2) + 1, y = 3.5},
|
||||
title = class_props[class_list[selected]].name,
|
||||
description = class_props[class_list[selected]].description,
|
||||
privs = {interact = true},
|
||||
elements = elements,
|
||||
})
|
||||
else
|
||||
hud_events.new(player, {
|
||||
|
@ -60,8 +60,7 @@ ctf_modebase.register_mode("classes", {
|
||||
"deaths",
|
||||
"hp_healed"
|
||||
},
|
||||
build_timer = 60 * 1.5,
|
||||
|
||||
build_timer = 60,
|
||||
is_bound_item = function(_, name)
|
||||
if name:match("ctf_mode_classes:") or name:match("ctf_melee:") or name == "ctf_healing:bandage" then
|
||||
return true
|
||||
@ -78,7 +77,7 @@ ctf_modebase.register_mode("classes", {
|
||||
ctf_modebase.bounties.get_next_bounty = ctf_modebase.bounty_algo.kd.get_next_bounty
|
||||
|
||||
ctf_cosmetics.get_skin = function(player)
|
||||
return old_get_skin(player) .. "^ctf_mode_classes_" .. classes.get_name(player) .. "_overlay.png"
|
||||
return old_get_skin(player) .. classes.get_skin_overlay(player)
|
||||
end
|
||||
end,
|
||||
on_mode_end = function()
|
||||
@ -107,8 +106,13 @@ ctf_modebase.register_mode("classes", {
|
||||
end,
|
||||
get_chest_access = features.get_chest_access,
|
||||
on_punchplayer = features.on_punchplayer,
|
||||
can_punchplayer = features.can_punchplayer,
|
||||
on_healplayer = features.on_healplayer,
|
||||
calculate_knockback = function()
|
||||
return 0
|
||||
calculate_knockback = function(player, hitter, time_from_last_punch, tool_capabilities, dir, distance, damage)
|
||||
if features.can_punchplayer(player, hitter) then
|
||||
return 2 * (tool_capabilities.damage_groups.knockback or 1)
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
@ -1,2 +1,2 @@
|
||||
name = ctf_mode_classes
|
||||
depends = ctf_modebase, ctf_melee, ctf_gui, ctf_cosmetics, ctf_api, hud_events
|
||||
depends = ctf_modebase, ctf_melee, ctf_gui, ctf_cosmetics, ctf_api, hud_events, ctf_settings
|
||||
|
@ -68,6 +68,7 @@ ctf_modebase.register_mode("classic", {
|
||||
on_flag_capture = features.on_flag_capture,
|
||||
on_flag_rightclick = function() end,
|
||||
get_chest_access = features.get_chest_access,
|
||||
can_punchplayer = features.can_punchplayer,
|
||||
on_punchplayer = features.on_punchplayer,
|
||||
on_healplayer = features.on_healplayer,
|
||||
calculate_knockback = function()
|
||||
|
@ -102,6 +102,7 @@ ctf_modebase.register_mode("nade_fight", {
|
||||
on_flag_capture = features.on_flag_capture,
|
||||
on_flag_rightclick = function() end,
|
||||
get_chest_access = features.get_chest_access,
|
||||
can_punchplayer = features.can_punchplayer,
|
||||
on_punchplayer = function(player, hitter, damage, unneeded, tool_capabilities, ...)
|
||||
if tool.holed[player:get_player_name()] then
|
||||
if tool_capabilities.grenade then
|
||||
|
1
mods/ctf/ctf_player/LICENSE.txt
Normal file
1
mods/ctf/ctf_player/LICENSE.txt
Normal file
@ -0,0 +1 @@
|
||||
Player Model taken from Minetest Game and modified for CTF, Licensed CC BY-SA 3.0 (mods/mtg/mtg_player_api/)
|
118
mods/ctf/ctf_player/init.lua
Normal file
118
mods/ctf/ctf_player/init.lua
Normal file
@ -0,0 +1,118 @@
|
||||
local stab_slash_time = 20/60 - 0.2
|
||||
local stab_slash_cooldown_after = 0.2
|
||||
|
||||
ctf_player = {
|
||||
animation_time = {
|
||||
-- Animation Frames / Animation Framerate + Cooldown Time - 0.1
|
||||
stab_slash = stab_slash_time + stab_slash_cooldown_after,
|
||||
},
|
||||
}
|
||||
|
||||
-- Override player_api model
|
||||
player_api.registered_models["character.b3d"] = nil
|
||||
|
||||
player_api.register_model("character.b3d", {
|
||||
animation_speed = 30,
|
||||
textures = {"character.png"},
|
||||
animations = {
|
||||
-- Standard animations.
|
||||
stand = {x = 0, y = 79},
|
||||
lay = {x = 162, y = 166, eye_height = 0.3,
|
||||
collisionbox = {-0.6, 0.0, -0.6, 0.6, 0.3, 0.6}},
|
||||
walk = {x = 168, y = 187},
|
||||
mine = {x = 189, y = 198},
|
||||
walk_mine = {x = 200, y = 219},
|
||||
sit = {x = 81, y = 160, eye_height = 0.8,
|
||||
collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.0, 0.3}},
|
||||
stab = {x = 221, y = 241, frame_loop = false},
|
||||
slash = {x = 242, y = 262, frame_loop = false},
|
||||
},
|
||||
collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3},
|
||||
stepheight = 0.6,
|
||||
eye_height = 1.47,
|
||||
})
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
player:set_local_animation(nil, nil, nil, nil, 0)
|
||||
end)
|
||||
|
||||
-- Override player_api globalstep
|
||||
|
||||
-- Localize for better performance.
|
||||
local player_set_animation = player_api.set_animation
|
||||
local get_animation = player_api.get_animation
|
||||
local player_attached = player_api.player_attached
|
||||
local models = player_api.registered_models
|
||||
|
||||
local stab_slash_timer = {}
|
||||
minetest.register_globalstep(function(dtime)
|
||||
for p, timer in pairs(stab_slash_timer) do
|
||||
timer.timeleft = timer.timeleft - dtime
|
||||
|
||||
if timer.timeleft <= 0 then
|
||||
if timer.state == "anim" then
|
||||
timer.state = "cooldown"
|
||||
timer.timeleft = stab_slash_cooldown_after + (timer.extra_time or 0)
|
||||
else
|
||||
stab_slash_timer[p] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
function ctf_player.set_stab_slash_anim(anim_type, player, extra_time)
|
||||
stab_slash_timer[player:get_player_name()] = {
|
||||
timeleft = stab_slash_time,
|
||||
extra_time = extra_time,
|
||||
state = "anim"
|
||||
}
|
||||
|
||||
player_set_animation(player, anim_type, 60)
|
||||
end
|
||||
|
||||
function player_api.globalstep()
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
local name = player:get_player_name()
|
||||
local player_data = get_animation(player)
|
||||
local model = models[player_data.model]
|
||||
|
||||
if model and not player_attached[name] then
|
||||
local controls = player:get_player_control()
|
||||
local animation_speed_mod = model.animation_speed or 30
|
||||
|
||||
-- Determine if the player is sneaking, and reduce animation speed if so
|
||||
if controls.sneak then
|
||||
animation_speed_mod = animation_speed_mod / 2
|
||||
end
|
||||
|
||||
-- Apply animations based on what the player is doing
|
||||
if player:get_hp() == 0 then
|
||||
player_set_animation(player, "lay")
|
||||
elseif not stab_slash_timer[name] or stab_slash_timer[name].state == "cooldown" then
|
||||
if controls.up or controls.down or controls.left or controls.right then
|
||||
if controls.LMB or controls.RMB then
|
||||
local wielded = player:get_wielded_item()
|
||||
|
||||
if not wielded or not wielded:get_definition().disable_mine_anim then
|
||||
player_set_animation(player, "walk_mine", animation_speed_mod)
|
||||
else
|
||||
player_set_animation(player, "walk", animation_speed_mod)
|
||||
end
|
||||
else
|
||||
player_set_animation(player, "walk", animation_speed_mod)
|
||||
end
|
||||
elseif controls.LMB or controls.RMB then
|
||||
local wielded = player:get_wielded_item()
|
||||
|
||||
if not wielded or not wielded:get_definition().disable_mine_anim then
|
||||
player_set_animation(player, "mine", animation_speed_mod)
|
||||
else
|
||||
player_set_animation(player, "stand", animation_speed_mod)
|
||||
end
|
||||
else
|
||||
player_set_animation(player, "stand", animation_speed_mod)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
2
mods/ctf/ctf_player/mod.conf
Normal file
2
mods/ctf/ctf_player/mod.conf
Normal file
@ -0,0 +1,2 @@
|
||||
name = ctf_player
|
||||
depends = player_api, ctf_core
|
BIN
mods/ctf/ctf_player/models/character.b3d
Normal file
BIN
mods/ctf/ctf_player/models/character.b3d
Normal file
Binary file not shown.
BIN
mods/ctf/ctf_player/models/character.blend
Normal file
BIN
mods/ctf/ctf_player/models/character.blend
Normal file
Binary file not shown.
@ -142,7 +142,7 @@ function player_api.set_animation(player, anim_name, speed)
|
||||
end
|
||||
end
|
||||
-- Set the animation seen by everyone else
|
||||
player:set_animation(anim, speed, animation_blend)
|
||||
player:set_animation(anim, speed, animation_blend, anim.frame_loop)
|
||||
-- Update related properties if they changed
|
||||
if anim._equals ~= previous_anim._equals then
|
||||
player:set_properties({
|
||||
@ -178,8 +178,8 @@ function minetest.calculate_knockback(player, ...)
|
||||
end
|
||||
|
||||
-- Check each player and apply animations
|
||||
minetest.register_globalstep(function()
|
||||
for _, player in pairs(minetest.get_connected_players()) do
|
||||
function player_api.globalstep()
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
local name = player:get_player_name()
|
||||
local player_data = players[name]
|
||||
local model = models[player_data.model]
|
||||
@ -208,6 +208,11 @@ minetest.register_globalstep(function()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Mods can modify the globalstep by overriding player_api.globalstep
|
||||
minetest.register_globalstep(function(...)
|
||||
player_api.globalstep(...)
|
||||
end)
|
||||
|
||||
for _, api_function in pairs({"get_animation", "set_animation", "set_model", "set_textures"}) do
|
||||
|
@ -2,7 +2,7 @@ local location = {
|
||||
"Arm_Right", -- default bone
|
||||
{x=0, y=5.5, z=3}, -- default position
|
||||
{x=-90, y=225, z=90}, -- default rotation
|
||||
{x=0.25, y=0.25}, -- default scale
|
||||
{x=0.3, y=0.3, z=0.25}, -- default scale
|
||||
}
|
||||
|
||||
local players = {}
|
||||
@ -21,6 +21,7 @@ minetest.register_entity("wield3d:entity", {
|
||||
backface_culling = false,
|
||||
static_save = false,
|
||||
pointable = false,
|
||||
glow = 7,
|
||||
on_punch = function() return true end,
|
||||
})
|
||||
|
||||
@ -43,7 +44,7 @@ end
|
||||
local globalstep_timer = 0
|
||||
minetest.register_globalstep(function(dtime)
|
||||
globalstep_timer = globalstep_timer + dtime
|
||||
if globalstep_timer < 1 then return end
|
||||
if globalstep_timer < 0.5 then return end
|
||||
|
||||
globalstep_timer = 0
|
||||
|
||||
@ -54,18 +55,41 @@ minetest.register_globalstep(function(dtime)
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local function add_wielditem(player)
|
||||
local entity = minetest.add_entity(player:get_pos(), "wield3d:entity")
|
||||
entity:set_attach(player, location[1], location[2], location[3])
|
||||
local setting = ctf_settings.get(player, "use_old_wielditem_display")
|
||||
|
||||
entity:set_attach(
|
||||
player,
|
||||
location[1], location[2], location[3],
|
||||
setting == "false"
|
||||
)
|
||||
players[player:get_player_name()] = {entity=entity, item="wield3d:hand"}
|
||||
|
||||
player:hud_set_flags({wielditem = (setting == "true")})
|
||||
update_entity(player)
|
||||
end)
|
||||
end
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
local function remove_wielditem(player)
|
||||
local pname = player:get_player_name()
|
||||
if players[pname] ~= nil then
|
||||
players[pname].entity:remove()
|
||||
players[pname] = nil
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
minetest.register_on_joinplayer(add_wielditem)
|
||||
minetest.register_on_leaveplayer(remove_wielditem)
|
||||
|
||||
|
||||
ctf_settings.register("use_old_wielditem_display", {
|
||||
label = "Use old wielditem display",
|
||||
type = "bool",
|
||||
default = "true",
|
||||
description = "Will use Minetest's default method of showing the wielded item.\n" ..
|
||||
"This won't show custom animations, but might be less jarring",
|
||||
on_change = function(player, new_value)
|
||||
remove_wielditem(player)
|
||||
add_wielditem(player)
|
||||
end,
|
||||
})
|
||||
|
@ -1 +1,2 @@
|
||||
name = wield3d
|
||||
depends = ctf_settings
|
||||
|
Loading…
x
Reference in New Issue
Block a user