diff --git a/.luacheckrc b/.luacheckrc index e164b23..fbe0728 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -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 = { diff --git a/mods/apis/ctf_gui/api.md b/mods/apis/ctf_gui/api.md index 9814a50..32bfa77 100644 --- a/mods/apis/ctf_gui/api.md +++ b/mods/apis/ctf_gui/api.md @@ -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 = , y = }, 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, } - diff --git a/mods/apis/ctf_gui/dev.lua b/mods/apis/ctf_gui/dev.lua new file mode 100644 index 0000000..e752890 --- /dev/null +++ b/mods/apis/ctf_gui/dev.lua @@ -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 diff --git a/mods/apis/ctf_gui/init.lua b/mods/apis/ctf_gui/init.lua index 511e8ed..045908e 100644 --- a/mods/apis/ctf_gui/init.lua +++ b/mods/apis/ctf_gui/init.lua @@ -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") diff --git a/mods/apis/ctf_settings/init.lua b/mods/apis/ctf_settings/init.lua new file mode 100644 index 0000000..db0c81c --- /dev/null +++ b/mods/apis/ctf_settings/init.lua @@ -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) diff --git a/mods/apis/ctf_settings/mod.conf b/mods/apis/ctf_settings/mod.conf new file mode 100644 index 0000000..9a73d42 --- /dev/null +++ b/mods/apis/ctf_settings/mod.conf @@ -0,0 +1,2 @@ +name = ctf_settings +depends = sfinv, ctf_gui diff --git a/mods/ctf/ctf_api/init.lua b/mods/ctf/ctf_api/init.lua index b032ae5..abd4115 100644 --- a/mods/ctf/ctf_api/init.lua +++ b/mods/ctf/ctf_api/init.lua @@ -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 \ No newline at end of file +end diff --git a/mods/ctf/ctf_combat/ctf_melee/init.lua b/mods/ctf/ctf_combat/ctf_melee/init.lua index 7dc55a2..6355032 100644 --- a/mods/ctf/ctf_combat/ctf_melee/init.lua +++ b/mods/ctf/ctf_combat/ctf_melee/init.lua @@ -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) diff --git a/mods/ctf/ctf_combat/ctf_melee/mod.conf b/mods/ctf/ctf_combat/ctf_melee/mod.conf index 9462315..0318586 100644 --- a/mods/ctf/ctf_combat/ctf_melee/mod.conf +++ b/mods/ctf/ctf_combat/ctf_melee/mod.conf @@ -1,2 +1,2 @@ name = ctf_melee -depends = ctf_core +depends = ctf_core, ctf_player diff --git a/mods/ctf/ctf_combat/ctf_melee/sounds/ctf_melee_whoosh.ogg b/mods/ctf/ctf_combat/ctf_melee/sounds/ctf_melee_whoosh.ogg new file mode 100644 index 0000000..cf98113 Binary files /dev/null and b/mods/ctf/ctf_combat/ctf_melee/sounds/ctf_melee_whoosh.ogg differ diff --git a/mods/ctf/ctf_combat/ctf_melee/textures/ctf_melee_slash.png b/mods/ctf/ctf_combat/ctf_melee/textures/ctf_melee_slash.png new file mode 100644 index 0000000..f73fe91 Binary files /dev/null and b/mods/ctf/ctf_combat/ctf_melee/textures/ctf_melee_slash.png differ diff --git a/mods/ctf/ctf_combat/ctf_ranged/init.lua b/mods/ctf/ctf_combat/ctf_ranged/init.lua index 47c92a0..1e521af 100644 --- a/mods/ctf/ctf_combat/ctf_ranged/init.lua +++ b/mods/ctf/ctf_combat/ctf_ranged/init.lua @@ -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) diff --git a/mods/ctf/ctf_core/cooldowns.lua b/mods/ctf/ctf_core/cooldowns.lua index e4f8519..e8fceb7 100644 --- a/mods/ctf/ctf_core/cooldowns.lua +++ b/mods/ctf/ctf_core/cooldowns.lua @@ -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)] diff --git a/mods/ctf/ctf_map/ctf_traps.lua b/mods/ctf/ctf_map/ctf_traps.lua index ecd9d93..b16fab5 100644 --- a/mods/ctf/ctf_map/ctf_traps.lua +++ b/mods/ctf/ctf_map/ctf_traps.lua @@ -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(), -}) \ No newline at end of file +}) diff --git a/mods/ctf/ctf_map/init.lua b/mods/ctf/ctf_map/init.lua index f002273..095fd19 100644 --- a/mods/ctf/ctf_map/init.lua +++ b/mods/ctf/ctf_map/init.lua @@ -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 diff --git a/mods/ctf/ctf_map/map_meta.lua b/mods/ctf/ctf_map/map_meta.lua index 8a90adf..aa4c915 100644 --- a/mods/ctf/ctf_map/map_meta.lua +++ b/mods/ctf/ctf_map/map_meta.lua @@ -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) diff --git a/mods/ctf/ctf_map/mapedit_gui.lua b/mods/ctf/ctf_map/mapedit_gui.lua index d19b88e..a122b40 100644 --- a/mods/ctf/ctf_map/mapedit_gui.lua +++ b/mods/ctf/ctf_map/mapedit_gui.lua @@ -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, diff --git a/mods/ctf/ctf_modebase/features.lua b/mods/ctf/ctf_modebase/features.lua index 35d0b98..b71ebd1 100644 --- a/mods/ctf/ctf_modebase/features.lua +++ b/mods/ctf/ctf_modebase/features.lua @@ -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 diff --git a/mods/ctf/ctf_modebase/flags/nodes.lua b/mods/ctf/ctf_modebase/flags/nodes.lua index e74d546..6b69ec6 100644 --- a/mods/ctf/ctf_modebase/flags/nodes.lua +++ b/mods/ctf/ctf_modebase/flags/nodes.lua @@ -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}) diff --git a/mods/ctf/ctf_modebase/init.lua b/mods/ctf/ctf_modebase/init.lua index fcba365..edb78b6 100644 --- a/mods/ctf/ctf_modebase/init.lua +++ b/mods/ctf/ctf_modebase/init.lua @@ -38,7 +38,7 @@ ctf_modebase = { flag_captured = {}, } -ctf_gui.init() +ctf_gui.old_init() function ctf_modebase.announce(msg) end diff --git a/mods/ctf/ctf_modebase/map_catalog_show.lua b/mods/ctf/ctf_modebase/map_catalog_show.lua index 04e9aef..5f4d238 100644 --- a/mods/ctf/ctf_modebase/map_catalog_show.lua +++ b/mods/ctf/ctf_modebase/map_catalog_show.lua @@ -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", { diff --git a/mods/ctf/ctf_modebase/markers.lua b/mods/ctf/ctf_modebase/markers.lua index b674913..fc83735 100644 --- a/mods/ctf/ctf_modebase/markers.lua +++ b/mods/ctf/ctf_modebase/markers.lua @@ -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) diff --git a/mods/ctf/ctf_modebase/match.lua b/mods/ctf/ctf_modebase/match.lua index f2cc0fa..cc60180 100644 --- a/mods/ctf/ctf_modebase/match.lua +++ b/mods/ctf/ctf_modebase/match.lua @@ -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 diff --git a/mods/ctf/ctf_modebase/mod.conf b/mods/ctf/ctf_modebase/mod.conf index 786f420..f585cb4 100644 --- a/mods/ctf/ctf_modebase/mod.conf +++ b/mods/ctf/ctf_modebase/mod.conf @@ -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 diff --git a/mods/ctf/ctf_modebase/mode_vote.lua b/mods/ctf/ctf_modebase/mode_vote.lua index a171f3c..190dfe4 100644 --- a/mods/ctf/ctf_modebase/mode_vote.lua +++ b/mods/ctf/ctf_modebase/mode_vote.lua @@ -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, diff --git a/mods/ctf/ctf_modebase/respawn_delay.lua b/mods/ctf/ctf_modebase/respawn_delay.lua index 0f3ed03..7043900 100644 --- a/mods/ctf/ctf_modebase/respawn_delay.lua +++ b/mods/ctf/ctf_modebase/respawn_delay.lua @@ -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) diff --git a/mods/ctf/ctf_modebase/summary.lua b/mods/ctf/ctf_modebase/summary.lua index 51f9689..9e6b661 100644 --- a/mods/ctf/ctf_modebase/summary.lua +++ b/mods/ctf/ctf_modebase/summary.lua @@ -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", { diff --git a/mods/ctf/ctf_modebase/textures/ctf_modebase_group.png b/mods/ctf/ctf_modebase/textures/ctf_modebase_group.png new file mode 100644 index 0000000..9d531cc Binary files /dev/null and b/mods/ctf/ctf_modebase/textures/ctf_modebase_group.png differ diff --git a/mods/ctf/ctf_modes/ctf_mode_classes/classes.lua b/mods/ctf/ctf_modes/ctf_mode_classes/classes.lua index a12524f..ee1d9b9 100644 --- a/mods/ctf/ctf_modes/ctf_mode_classes/classes.lua +++ b/mods/ctf/ctf_modes/ctf_mode_classes/classes.lua @@ -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\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", + disallowed_items_markup, + iname + ) + else + disallowed_items_markup = string.format("%s", + 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;
Class Selection
]", form_x}, + + {"hypertext[0,%f;%f,1;classname;
]", + 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; +
%s
+ %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 + " "..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, { diff --git a/mods/ctf/ctf_modes/ctf_mode_classes/init.lua b/mods/ctf/ctf_modes/ctf_mode_classes/init.lua index 9628b2c..708173d 100644 --- a/mods/ctf/ctf_modes/ctf_mode_classes/init.lua +++ b/mods/ctf/ctf_modes/ctf_mode_classes/init.lua @@ -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, }) diff --git a/mods/ctf/ctf_modes/ctf_mode_classes/mod.conf b/mods/ctf/ctf_modes/ctf_mode_classes/mod.conf index 0248fa0..a56dfae 100644 --- a/mods/ctf/ctf_modes/ctf_mode_classes/mod.conf +++ b/mods/ctf/ctf_modes/ctf_mode_classes/mod.conf @@ -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 diff --git a/mods/ctf/ctf_modes/ctf_mode_classic/init.lua b/mods/ctf/ctf_modes/ctf_mode_classic/init.lua index 7de71e4..39e5c26 100644 --- a/mods/ctf/ctf_modes/ctf_mode_classic/init.lua +++ b/mods/ctf/ctf_modes/ctf_mode_classic/init.lua @@ -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() diff --git a/mods/ctf/ctf_modes/ctf_mode_nade_fight/init.lua b/mods/ctf/ctf_modes/ctf_mode_nade_fight/init.lua index 8196377..1cd5db4 100644 --- a/mods/ctf/ctf_modes/ctf_mode_nade_fight/init.lua +++ b/mods/ctf/ctf_modes/ctf_mode_nade_fight/init.lua @@ -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 diff --git a/mods/ctf/ctf_player/LICENSE.txt b/mods/ctf/ctf_player/LICENSE.txt new file mode 100644 index 0000000..d54e57e --- /dev/null +++ b/mods/ctf/ctf_player/LICENSE.txt @@ -0,0 +1 @@ +Player Model taken from Minetest Game and modified for CTF, Licensed CC BY-SA 3.0 (mods/mtg/mtg_player_api/) diff --git a/mods/ctf/ctf_player/init.lua b/mods/ctf/ctf_player/init.lua new file mode 100644 index 0000000..df3b5e1 --- /dev/null +++ b/mods/ctf/ctf_player/init.lua @@ -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 diff --git a/mods/ctf/ctf_player/mod.conf b/mods/ctf/ctf_player/mod.conf new file mode 100644 index 0000000..b18d4a7 --- /dev/null +++ b/mods/ctf/ctf_player/mod.conf @@ -0,0 +1,2 @@ +name = ctf_player +depends = player_api, ctf_core diff --git a/mods/ctf/ctf_player/models/character.b3d b/mods/ctf/ctf_player/models/character.b3d new file mode 100644 index 0000000..50ab454 Binary files /dev/null and b/mods/ctf/ctf_player/models/character.b3d differ diff --git a/mods/ctf/ctf_player/models/character.blend b/mods/ctf/ctf_player/models/character.blend new file mode 100644 index 0000000..c323402 Binary files /dev/null and b/mods/ctf/ctf_player/models/character.blend differ diff --git a/mods/mtg/mtg_player_api/api.lua b/mods/mtg/mtg_player_api/api.lua index b3e3f5e..9e3559d 100644 --- a/mods/mtg/mtg_player_api/api.lua +++ b/mods/mtg/mtg_player_api/api.lua @@ -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 diff --git a/mods/other/wield3d/init.lua b/mods/other/wield3d/init.lua index c14fa0e..afcbb52 100644 --- a/mods/other/wield3d/init.lua +++ b/mods/other/wield3d/init.lua @@ -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, +}) diff --git a/mods/other/wield3d/mod.conf b/mods/other/wield3d/mod.conf index 7ab29fb..0a64beb 100644 --- a/mods/other/wield3d/mod.conf +++ b/mods/other/wield3d/mod.conf @@ -1 +1,2 @@ name = wield3d +depends = ctf_settings