Reorganise game into modpacks

This commit is contained in:
rubenwardy 2019-01-11 15:45:27 +00:00
parent 86a5266bb5
commit b38a89c2fe
762 changed files with 9 additions and 8 deletions

View file

@ -0,0 +1,2 @@
ctf
lib_chatcmdbuilder

View file

@ -0,0 +1,84 @@
local storage = minetest.get_mod_storage()
local data = minetest.parse_json(storage:get_string("locktoteam")) or {}
local ctf_autoalloc = ctf.autoalloc
function ctf.autoalloc(name, alloc_mode)
if data[name] then
return data[name]
end
return ctf_autoalloc(name, alloc_mode)
end
ChatCmdBuilder.new("ctf_lockpt", function(cmd)
cmd:sub(":name :team", function(name, pname, team)
if team == "!" then
data[pname] = nil
storage:set_string("locktoteam", minetest.write_json(data))
return true, "Unlocked " .. pname
else
data[pname] = team
storage:set_string("locktoteam", minetest.write_json(data))
return true, "Locked " .. pname .. " to " .. team
end
end)
end, {
description = "Lock a player to a team",
privs = {
ctf_admin = true,
}
})
function table.map_inplace(t, f) -- luacheck: ignore
for key, value in pairs(t) do
t[key] = f(value)
end
return t
end
ctf_alloc = {}
function ctf_alloc.set_all()
local players = minetest.get_connected_players()
table.map_inplace(players, function(a)
local stats, _ = ctf_stats.player(a:get_player_name())
return {
player = a,
score = stats.score,
}
end)
table.sort(players, function(a, b)
return a.score > b.score
end)
minetest.log("warning", dump(players))
local to_red = math.random(2) == 2
for _, spair in pairs(players) do
local player = spair.player
local name = player:get_player_name()
local alloc_mode = tonumber(ctf.setting("allocate_mode"))
local team
if to_red then
team = "red"
else
team = "blue"
end
to_red = not to_red
if alloc_mode ~= 0 and team then
ctf.log("autoalloc", name .. " was allocated to " .. team)
ctf.join(name, team)
end
ctf.move_to_spawn(name)
if ctf.setting("match.clear_inv") then
local inv = player:get_inventory()
inv:set_list("main", {})
inv:set_list("craft", {})
give_initial_stuff(player)
end
player:set_hp(20)
end
end

View file

@ -0,0 +1,3 @@
ctf
ctf_stats
irc?

View file

@ -0,0 +1,124 @@
local bountied_player = nil
local bounty_score = 0
local function announce(name)
local _, tcolor = ctf_colors.get_color(ctf.player(bountied_player))
tcolor = tcolor:gsub("0x", "#")
minetest.chat_send_player(name,
minetest.colorize("#fff326", "The next person to kill ") ..
minetest.colorize(tcolor, bountied_player) ..
minetest.colorize("#fff326", " will receive " .. bounty_score .. " points!"))
end
local function announce_all()
if bountied_player then
for _, player in pairs(minetest.get_connected_players()) do
if bountied_player ~= player:get_player_name() then
announce(player:get_player_name())
end
end
end
end
local function bounty_player(target)
local prev = bountied_player
bountied_player = target
-- Score * K/D
-- bounty_score = -----------, or 500 (whichever is lesser)
-- 5000
local pstat, _ = ctf_stats.player(target)
if pstat.deaths == 0 then
pstat.deaths = 1
end
bounty_score = (pstat.score * (pstat.kills / pstat.deaths)) / 10000
if bounty_score > 500 then
bounty_score = 500
end
if bounty_score < 50 then
bounty_score = 50
end
bounty_score = math.floor(bounty_score)
if prev then
for _, player in pairs(minetest.get_connected_players()) do
local name = player:get_player_name()
if bountied_player ~= name then
local _, prev_color = ctf_colors.get_color(prev, ctf.player(prev))
minetest.chat_send_player(player:get_player_name(),
minetest.colorize("#fff326", "Player ") ..
minetest.colorize(prev_color:gsub("0x", "#"), prev) ..
minetest.colorize("#fff326", " no longer has a bounty on their head!"))
end
end
end
minetest.after(0.1, announce_all)
end
local function bounty_find_new_target()
local players = {}
for _, player in pairs(minetest.get_connected_players()) do
local name = player:get_player_name()
local pstat, _ = ctf_stats.player(name)
pstat.name = name
pstat.color = nil
if pstat.score > 1000 and pstat.kills > pstat.deaths * 1.5 then
table.insert(players, pstat)
end
end
if #players > 0 then
bounty_player(players[math.random(1, #players)].name)
end
minetest.after(math.random(500, 1000), bounty_find_new_target)
end
minetest.after(math.random(500, 1000), bounty_find_new_target)
minetest.register_on_joinplayer(function(player)
local name = player:get_player_name()
if bountied_player and
bountied_player ~= name then
announce(name)
end
end)
ctf.register_on_killedplayer(function(victim, killer)
-- Suicide is not encouraged here at CTF
if victim == killer then
return
end
if victim == bountied_player then
local main, match = ctf_stats.player(killer)
if main and match then
main.score = main.score + bounty_score
match.score = match.score + bounty_score
ctf.needs_save = true
end
bountied_player = nil
local msg = killer .. " has killed " .. victim .. " and received the prize!"
minetest.chat_send_all(msg)
local pstats, mstats = ctf_stats.player(killer)
pstats.bounty_kills = pstats.bounty_kills + 1
mstats.bounty_kills = mstats.bounty_kills + 1
end
end)
minetest.register_privilege("bounty_admin")
minetest.register_chatcommand("place_bounty", {
privs = { bounty_admin = true },
func = function(name, target)
target = target:trim()
if not minetest.get_player_by_name(target) then
return false, target .. " is not online"
end
bounty_player(target)
return true, "Put bounty on " .. target
end
})

View file

@ -0,0 +1 @@
crafting

View file

@ -0,0 +1,204 @@
local full_ores = {
{"diamond", "default:diamond"},
{"mese", "default:mese_crystal"},
{"bronze", "default:bronze_ingot"},
{"steel", "default:steel_ingot"},
{"stone", "default:cobble"},
}
-- Swords
for _, orex in pairs(full_ores) do
crafting.register_recipe({
type = "inv",
output = "default:sword_" .. orex[1],
items = { "default:stick", orex[2] .. " 2" },
always_known = true,
level = 1,
})
end
-- Pickaxes
for _, orex in pairs(full_ores) do
crafting.register_recipe({
type = "inv",
output = "default:pick_" .. orex[1],
items = { "default:stick 2", orex[2] .. " 3" },
always_known = true,
level = 1,
})
end
-- Bronze ingot x9 <== Copper x9 + Tin x9
crafting.register_recipe({
type = "inv",
output = "default:bronze_ingot 9",
items = { "default:copper_ingot 8", "default:tin_ingot"},
always_known = true,
level = 1,
})
-- Mese crystal x9 <== Mese block
crafting.register_recipe({
type = "inv",
output = "default:mese_crystal 9",
items = { "default:mese"},
always_known = true,
level = 1,
})
-- Furnace <== Cobble x8
crafting.register_recipe({
type = "inv",
output = "default:furnace",
items = { "default:cobble 8" },
always_known = true,
level = 1,
})
-- Furnace <== Desert cobble x8
crafting.register_recipe({
type = "inv",
output = "default:furnace",
items = { "default:desert_cobble 8" },
always_known = true,
level = 1,
})
-- Team door
crafting.register_recipe({
type = "inv",
output = "doors:door_steel",
items = { "default:steel_ingot 6" },
always_known = true,
level = 1,
})
-- Wooden plank x4
crafting.register_recipe({
type = "inv",
output = "default:wood 4",
items = { "group:tree" },
always_known = true,
level = 1,
})
-- Stick x4
crafting.register_recipe({
type = "inv",
output = "default:stick 4",
items = { "default:wood" },
always_known = true,
level = 1,
})
-- Torch x5
crafting.register_recipe({
type = "inv",
output = "default:torch 5",
items = { "default:stick", "default:coal_lump" },
always_known = true,
level = 1,
})
-- Ammo <== Tin ingot x3 + Coal lump x2
crafting.register_recipe({
type = "inv",
output = "shooter:ammo",
items = { "default:tin_ingot 3", "default:coal_lump 2" },
always_known = true,
level = 1,
})
-- Ammo <== Steel ingot x3 + Coal lump x2
crafting.register_recipe({
type = "inv",
output = "shooter:ammo",
items = { "default:steel_ingot 3", "default:coal_lump 2" },
always_known = true,
level = 1,
})
-- Arrow x5
crafting.register_recipe({
type = "inv",
output = "shooter:arrow_white 5",
items = { "default:stick 5", "default:cobble" },
always_known = true,
level = 1,
})
-- Wooden ladder x4
crafting.register_recipe({
type = "inv",
output = "default:ladder 4",
items = { "default:stick 8" },
always_known = true,
level = 1,
})
-- Stick x2 <== Wooden ladder
crafting.register_recipe({
type = "inv",
output = "default:stick 2",
items = { "default:ladder" },
always_known = true,
level = 1,
})
-- Shovels
for _, orex in pairs(full_ores) do
crafting.register_recipe({
type = "inv",
output = "default:shovel_" .. orex[1],
items = { "default:stick 2", orex[2] },
always_known = true,
level = 1,
})
end
-- Axes
for _, orex in pairs(full_ores) do
crafting.register_recipe({
type = "inv",
output = "default:axe_" .. orex[1],
items = { "default:stick 2", orex[2] .. " 3" },
always_known = true,
level = 1,
})
end
-- Wooden plank x3 <== Wooden pickaxe
crafting.register_recipe({
type = "inv",
output = "default:wood 3",
items = { "default:pick_wood" },
always_known = true,
level = 1,
})
-- Wooden plank x2 <== Wooden sword
crafting.register_recipe({
type = "inv",
output = "default:wood 2",
items = { "default:sword_wood" },
always_known = true,
level = 1,
})
-- Wooden plank x3 <== Wooden axe
crafting.register_recipe({
type = "inv",
output = "default:wood 3",
items = { "default:axe_wood" },
always_known = true,
level = 1,
})
-- Wooden plank <== Wooden shovel
crafting.register_recipe({
type = "inv",
output = "default:wood 1",
items = { "default:shovel_wood" },
always_known = true,
level = 1,
})

View file

@ -0,0 +1 @@
ctf

View file

@ -0,0 +1,5 @@
ctf.save = function()
for i = 1, #ctf.registered_on_save do
ctf.registered_on_save[i]()
end
end

View file

@ -0,0 +1,3 @@
ctf
ctf_match
hudkit

View file

@ -0,0 +1,148 @@
local hud = hudkit()
minetest.register_on_leaveplayer(function(player)
hud.players[player:get_player_name()] = nil
end)
local NUM_EVT = 6
ctf_events = {
events = {}
}
function ctf_events.post(action, one, two)
table.insert(ctf_events.events, 1, {
action = action,
one = one,
two = two
})
while #ctf_events.events > NUM_EVT do
table.remove(ctf_events.events, #ctf_events.events)
end
end
function ctf_events.update_row(i, player, name, tplayer, evt)
local idx = "ctf_events:" .. i .. "_one"
local idxa = "ctf_events:" .. i .. "_action"
local idx2 = "ctf_events:" .. i .. "_two"
if not evt then
hud:remove(player, idx)
hud:remove(player, idxa)
hud:remove(player, idx2)
return
end
local y_pos = i * 20
-- One
if evt.one then
local _, tone_hex = ctf_colors.get_color(ctf.player(evt.one))
if hud:exists(player, idx) then
hud:change(player, idx, "text", evt.one)
hud:change(player, idx, "number", tone_hex)
else
local tmp = {
hud_elem_type = "text",
position = {x = 0, y = 0.8},
scale = {x = 200, y = 100},
text = evt.one,
number = tone_hex,
offset = {x = 145, y = -y_pos},
alignment = {x = -1, y = 0}
}
hud:add(player, idx, tmp)
end
else
hud:remove(player, idx)
end
-- Two
if evt.two then
local _, ttwo_hex = ctf_colors.get_color(ctf.player(evt.two))
if hud:exists(player, idx2) then
hud:change(player, idx2, "text", evt.two)
hud:change(player, idx2, "number", ttwo_hex)
else
local tmp = {
hud_elem_type = "text",
position = {x = 0, y = 0.8},
scale = {x = 200, y = 100},
text = evt.two,
number = ttwo_hex,
offset = {x = 175, y = -y_pos},
alignment = {x = 1, y = 0}
}
hud:add(player, idx2, tmp)
end
else
hud:remove(player, idx2)
end
-- Action
if evt.action then
if hud:exists(player, idxa) then
hud:change(player, idxa, "text", "ctf_events_" .. evt.action .. ".png")
else
local tmp = {
hud_elem_type = "image",
position = {x = 0, y = 0.8},
scale = {x = 1, y = 1},
text = "ctf_events_" .. evt.action .. ".png",
offset = {x = 160, y = -y_pos},
alignment = {x = 0, y = 0}
}
hud:add(player, idxa, tmp)
end
else
hud:remove(player, idxa)
end
end
function ctf_events.update(player)
local name = player:get_player_name()
local tplayer = ctf.player_or_nil(name)
if tplayer then
for i=1, NUM_EVT do
local evt = nil
if #ctf_events.events >= i then
evt = ctf_events.events[i]
end
ctf_events.update_row(i, player, name, tplayer, evt)
end
end
end
function ctf_events.update_all()
for _, player in pairs(minetest.get_connected_players()) do
ctf_events.update(player)
end
end
ctf.register_on_killedplayer(function(victim, killer, stack)
local sname = stack:get_name()
local tool_caps = stack:get_tool_capabilities()
local type = "sword"
if sname:sub(1, 8) == "shooter:" then
if sname == "shooter:grenade" then
type = "grenade"
else
type = "bullet"
end
end
if tool_caps.damage_groups.grenade then
type = "grenade"
end
ctf_events.post("kill_" .. type, killer, victim)
ctf_events.update_all()
end)
minetest.register_on_joinplayer(function(player)
ctf_events.update(player)
end)
ctf_match.register_on_new_match(function()
ctf_events.events = {}
ctf_events.update_all()
end)

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

View file

@ -0,0 +1,2 @@
ctf
sfinv

View file

@ -0,0 +1,46 @@
local items = {
minetest.colorize("#66a0ff", "Game Play"),
"",
"* Capture an enemy's flag by walking up to it, punching it, then",
" running and punching your team's flag.",
"* Look for weapons and other resources in chests, or mine and use furnaces to make swords.",
"* Good swords do more damage than guns, but need to be used at close range.",
"* Use apples to replenish health quickly.",
"* Gain more score by killing more than you die, or by capturing the flag.",
"* Players are immune for 10 seconds after they respawn.",
"* Access the pro section of the chest by achieving a 2k+ score and",
" killing 2 people for every death.",
"",
minetest.colorize("#66a0ff", "Team Co-op"),
"",
"* Your team has a chest near your flag.",
"* Your team name is displayed in the top left.",
" to talk with only your team, type: /t message",
"",
minetest.colorize("#66a0ff", "Player Rankings"),
"",
"* See the league table by typing /rankings",
"* See your position in it by typing /rankings me",
"* Get to the top by capturing lots of flags, and having a high K/D ratio.",
"",
minetest.colorize("#66a0ff", "Contact Moderators"),
"",
"* Report people who sabotage using /report."
}
for i = 1, #items do
items[i] = minetest.formspec_escape(items[i])
end
local fs = [[
textlist[0,0;7.85,8.5;help;
]] .. table.concat(items, ",") .. "]"
sfinv.register_page("ctf_inventory:help", {
title = "Help",
get = function(self, player, context)
return sfinv.make_formspec(player, context, fs, false)
end
})

View file

@ -0,0 +1,51 @@
# CTF Map
This mod handles multiple maps.
# Creating a new map
## 1. Dependencies
* Minetest 0.4.16 or later.
* Mods
* ctf_map (by copying the folder from this game to `minetest/mods`)
* worldedit and worldedit_commands.
## 2. Find an area
* Can use Minetest Game and any mapgen.
* It must be a cube, and the barrier will be in the exact center.
* It should be around 230x230 in surface area, but this can vary.
* Feel free to modify the area to your needs.
## 3. Select the area
There are multiple ways do this, this is the simplist in most cases.
* If you haven't modified the map at all, do the following to speed up barrier placement:
* Stop Minetest.
* Open up the world's world.mt
* Set backend to "dummy".
* Save.
* Using worldedit, select the area.
* Type /gui, and click "From WE" then "To WE".
* Check that the center location is the right place for the barrier to go.
* Check that the bounds extend far enough.
## 4. Place barriers
* Set the middle barrier direction. The barrier is a plane defined by a co-ordinate = 0.
If the barrier is X=0, then it will placed with every node of the barrier having X=0.
If the barrier is Z=0, then it will placed with every node of the barrier having Z=0.
* Click "place barrier". Note that this command does not have an undo.
## 5. Meta data
* Set the meta data
## 6. Export
* Click export, and wait until completion.
* Copy the two files from `worlddir/schemes/` to `ctf_map/maps/`.
* Rename the files so the two prefixed numbers are consistent to existing maps.
* Profit!

View file

@ -0,0 +1,270 @@
local c_stone = minetest.get_content_id("ctf_map:ind_stone")
local c_stone_red = minetest.get_content_id("ctf_map:ind_stone_red")
local c_glass = minetest.get_content_id("ctf_map:ind_glass")
local c_glass_red = minetest.get_content_id("ctf_map:ind_glass_red")
local c_map_ignore = minetest.get_content_id("ctf_map:ignore")
local c_actual_st = minetest.get_content_id("default:stone")
local c_water = minetest.get_content_id("default:water_source")
-- local c_water_f = minetest.get_content_id("default:water_flowing")
local c_air = minetest.get_content_id("air")
function ctf_map.remove_middle_barrier()
local r = ctf_map.map.r
local h = ctf_map.map.h
local min = vector.add(ctf_map.map.offset, {
x = -r + 1,
y = -h / 2,
z = -1
})
local max = vector.add(ctf_map.map.offset, {
x = r - 1,
y = h / 2,
z = 1
})
local vm = minetest.get_voxel_manip()
local emin, emax = vm:read_from_map(min, max)
local a = VoxelArea:new{
MinEdge = emin,
MaxEdge = emax
}
local data = vm:get_data()
for x = min.x, max.x do
for y = min.y, max.y do
local vi = a:index(x, y, 0)
local adj1 = a:index(x, y, 1)
local adj2 = a:index(x, y, -1)
if data[vi] == c_glass_red then
-- If surrounding nodes are water, replace node with water
if data[adj1] == c_water and data[adj2] == c_water then
data[vi] = c_water
-- Else replace with air
else
data[vi] = c_air
end
elseif data[vi] == c_stone_red then
data[vi] = c_actual_st
end
end
end
vm:set_data(data)
vm:write_to_map(data)
vm:update_map()
end
function ctf_map.place_middle_barrier(center, r, h, direction)
assert(direction == "x" or direction == "z")
local min = {
x = -r + 1,
y = -h / 2 + 1,
z = -r + 1,
}
local max = {
x = r - 1,
y = h / 2 - 1,
z = r - 1,
}
local other = "z"
if direction == "z" then
other = "x"
end
min[direction] = -1
max[direction] = 1
min = vector.add(center, min)
max = vector.add(center, max)
local vm = minetest.get_voxel_manip()
local emin, emax = vm:read_from_map(min, max)
local a = VoxelArea:new{
MinEdge = emin,
MaxEdge = emax
}
local data = vm:get_data()
for x = min[other], max[other] do
for y = min.y, max.y do
local vi
if other == "x" then
vi = a:index(x, y, center.z)
else
vi = a:index(center.x, y, x)
end
if data[vi] == c_air or data[vi] == c_water then
data[vi] = c_glass_red
end
end
end
vm:set_data(data)
vm:write_to_map(data)
vm:update_map()
end
function ctf_map.place_outer_barrier(center, r, h)
local minp = vector.subtract(center, r)
local maxp = vector.add(center, r)
minp.y = center.y - h / 2
maxp.y = center.y + h / 2
print("Loading data into LVM")
local vm = minetest.get_voxel_manip()
local emin, emax = vm:read_from_map(minp, maxp)
local a = VoxelArea:new{
MinEdge = emin,
MaxEdge = emax
}
local data = vm:get_data()
print("Placing left wall")
-- Left
do
local x = center.x - r
for z = minp.z, maxp.z do
for y = minp.y, maxp.y do
local vi = a:index(x, y, z)
if data[vi] == c_air or data[vi] == c_glass or data[vi] == c_map_ignore then
data[vi] = c_glass
else
data[vi] = c_stone
end
end
end
end
print("Placing right wall")
-- Right
do
local x = center.x + r
for z = minp.z, maxp.z do
for y = minp.y, maxp.y do
local vi = a:index(x, y, z)
if data[vi] == c_air or data[vi] == c_glass or data[vi] == c_map_ignore then
data[vi] = c_glass
else
data[vi] = c_stone
end
end
end
end
print("Placing front wall")
-- Front
do
local z = center.z - r
for x = minp.x, maxp.x do
for y = minp.y, maxp.y do
local vi = a:index(x, y, z)
if data[vi] == c_air or data[vi] == c_glass or data[vi] == c_map_ignore then
data[vi] = c_glass
else
data[vi] = c_stone
end
end
end
end
print("Placing back wall")
-- Back
do
local z = center.z + r
for x = minp.x, maxp.x do
for y = minp.y, maxp.y do
local vi = a:index(x, y, z)
if data[vi] == c_air or data[vi] == c_glass or data[vi] == c_map_ignore then
data[vi] = c_glass
else
data[vi] = c_stone
end
end
end
end
print("Placing bedrock")
-- Bedrock
do
local y = minp.y
for x = minp.x, maxp.x do
for z = minp.z, maxp.z do
data[a:index(x, y, z)] = c_stone
end
end
end
print("Placing ceiling")
-- Ceiling
do
local y = maxp.y
for x = minp.x, maxp.x do
for z = minp.z, maxp.z do
data[a:index(x, y, z)] = c_glass
end
end
end
print("Writing to engine!")
vm:set_data(data)
vm:write_to_map(data)
vm:update_map()
end
if minetest.get_modpath("ctf") then
local old_is_protected = minetest.is_protected
function minetest.is_protected(pos, name)
if ctf_match.build_timer <= 0 then
return old_is_protected(pos, name)
end
local tname = ctf.player(name).team
if tname and
(tname == "blue" and pos.z >= 0) or (tname == "red" and pos.z <= 0) then
minetest.chat_send_player(name, "Can't dig beyond the barrier!")
return true
else
return old_is_protected(pos, name)
end
end
local function pos_check()
if ctf_match.build_timer <= 0 then
return
end
for _, player in pairs(minetest.get_connected_players()) do
local name = player:get_player_name()
local tname = ctf.player(name).team
local pos = player:get_pos()
local privs = minetest.get_player_privs(name)
if tname and not privs.fly and privs.interact then
if (tname == "blue" and pos.z >= 0) or
(tname == "red" and pos.z <= 0) then
minetest.chat_send_player(name, "Match hasn't started yet!")
ctf.move_to_spawn(name)
end
end
end
if ctf_match.build_timer > 0.2 then
minetest.after(0.2, pos_check)
end
end
ctf_match.register_on_build_time_start(function()
minetest.after(0.2, pos_check)
end)
end

View file

@ -0,0 +1,6 @@
default
ctf_team_base?
ctf?
ctf_match?
worldedit?
irc?

View file

@ -0,0 +1,33 @@
local function emergeblocks_callback(pos, action, num_calls_remaining, ctx)
if ctx.total_blocks == 0 then
ctx.total_blocks = num_calls_remaining + 1
ctx.current_blocks = 0
end
ctx.current_blocks = ctx.current_blocks + 1
if ctx.current_blocks == ctx.total_blocks then
if ctx.name then
minetest.chat_send_player(ctx.name,
string.format("Finished emerging %d blocks in %.2fms.",
ctx.total_blocks, (os.clock() - ctx.start_time) * 1000))
end
ctx:callback()
elseif ctx.progress then
ctx:progress()
end
end
function ctf_map.emerge_with_callbacks(name, pos1, pos2, callback, progress)
local context = {
current_blocks = 0,
total_blocks = 0,
start_time = os.clock(),
name = name,
callback = callback,
progress = progress
}
minetest.emerge_area(pos1, pos2, emergeblocks_callback, context)
end

View file

@ -0,0 +1,34 @@
give_initial_stuff = {}
setmetatable(give_initial_stuff, {
__call = function(self, player)
minetest.log("action", "Giving initial stuff to player "..player:get_player_name())
local inv = player:get_inventory()
inv:set_list("main", {})
inv:set_list("craft", {})
inv:set_size("craft", 1)
inv:set_size("craftresult", 0)
inv:set_size("hand", 0)
local items = give_initial_stuff.get_stuff()
for _, item in pairs(items) do
inv:add_item("main", item)
end
end
})
function give_initial_stuff.get_stuff()
return ctf_map.map and ctf_map.map.initial_stuff or {
"default:pick_wood",
"default:sword_wood",
"default:torch 3",
}
end
minetest.register_on_joinplayer(function(player)
player:set_hp(20)
give_initial_stuff(player)
end)
minetest.register_on_respawnplayer(give_initial_stuff)

16
mods/ctf/ctf_map/init.lua Normal file
View file

@ -0,0 +1,16 @@
ctf_map = {}
dofile(minetest.get_modpath("ctf_map") .. "/nodes.lua")
dofile(minetest.get_modpath("ctf_map") .. "/emerge.lua")
dofile(minetest.get_modpath("ctf_map") .. "/barrier.lua")
if minetest.get_modpath("ctf") then
dofile(minetest.get_modpath("ctf_map") .. "/schem_map.lua")
dofile(minetest.get_modpath("ctf_map") .. "/give_initial_stuff.lua")
assert(ctf_match)
ctf_match.register_on_build_time_end(ctf_map.remove_middle_barrier)
else
dofile(minetest.get_modpath("ctf_map") .. "/map_maker.lua")
end

View file

@ -0,0 +1,409 @@
local randint = math.random(100)
-- Reload mapmaker context from mod_storage if it exists
local storage = minetest.get_mod_storage()
local mapname = storage:get_string("mapname")
local maptitle = storage:get_string("maptitle")
local mapauthor = storage:get_string("mapauthor")
local mapinitial = storage:get_string("mapinitial")
local center = storage:get_string("center")
local flag_positions = storage:get_string("flags")
local barrier_r = storage:get_int("barrier_r")
local center_barrier_rot = storage:get_int("center_barrier_rot")
if mapname == "" then
mapname = "ctf_" .. randint
end
if mapauthor == "" then
mapauthor = nil
end
if maptitle == "" then
maptitle = "Untitled Map " .. randint
end
if barrier_r == 0 then
barrier_r = 110
end
if center == "" then
center = { x = 0, y = 0, z = 0, r = 115, h = 140 }
else
center = minetest.parse_json(storage:get("center"))
end
if flag_positions == "" then
flag_positions = {}
else
flag_positions = minetest.parse_json(storage:get("flags"))
end
--------------------------------------------------------------------------------
minetest.register_on_joinplayer(function(player)
minetest.after(1, function(name)
minetest.chat_send_player(name, "*** CTF_MAP IS IN MAP MAKER MODE ***")
end, player:get_player_name())
end)
assert(minetest.get_modpath("worldedit") and
minetest.get_modpath("worldedit_commands"),
"worldedit and worldedit_commands are required!")
local function check_step()
for _, pos in pairs(flag_positions) do
if minetest.get_node(pos).name ~= "ctf_map:flag" then
minetest.set_node(pos, { name = "ctf_map:flag" })
end
end
minetest.after(1, check_step)
end
minetest.after(1, check_step)
minetest.register_node("ctf_map:flag", {
description = "Flag",
drawtype="nodebox",
paramtype = "light",
walkable = false,
tiles = {
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png",
"flag_grey2.png",
"flag_grey.png"
},
node_box = {
type = "fixed",
fixed = {
{ 0.250000,-0.500000,0.000000,0.312500,0.500000,0.062500},
{ -0.5,0,0.000000,0.250000,0.500000,0.062500}
}
},
groups = {oddly_breakable_by_hand=1,snappy=3},
on_place = function(_, _, pthing)
local node = minetest.get_node(pthing.under).name
local ndef = minetest.registered_nodes[node]
local pos = (ndef and ndef.buildable_to) and pthing.under or pthing.above
table.insert(flag_positions, vector.new(pos))
storage:set_string("flags", minetest.write_json(flag_positions))
end,
on_destruct = function(pos)
for i, v in pairs(flag_positions) do
if vector.equals(pos, v) then
flag_positions[i] = nil
return
end
end
end,
})
local function to_2pos()
return {
x = center.x - center.r,
y = center.y - center.h / 2,
z = center.z - center.r,
}, {
x = center.x + center.r,
y = center.y + center.h / 2,
z = center.z + center.r,
}
end
local function max(a, b)
if a > b then
return a
else
return b
end
end
local function we_select(name)
local pos1, pos2 = to_2pos()
worldedit.pos1[name] = pos1
worldedit.mark_pos1(name)
worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos1))
worldedit.pos2[name] = pos2
worldedit.mark_pos2(name)
worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos2))
end
local function we_import(name)
local pos1 = worldedit.pos1[name]
local pos2 = worldedit.pos2[name]
if pos1 and pos2 then
local size = vector.subtract(pos2, pos1)
local r = max(size.x, size.z) / 2
center = vector.divide(vector.add(pos1, pos2), 2)
center.r = r
center.h = size.y
storage:set_string("center", minetest.write_json(center))
end
end
local function get_flags()
local negative = nil
local positive = nil
for _, pos in pairs(flag_positions) do
pos = vector.subtract(pos, center)
if center_barrier_rot == 0 and pos.x < 0 or pos.z < 0 then
negative = pos
end
if center_barrier_rot == 0 and pos.x > 0 or pos.z > 0 then
positive = pos
end
end
return negative, positive
end
local function get_flag_status()
if #flag_positions > 2 then
return "Too many flags! (" .. #flag_positions .. "/2)"
elseif #flag_positions < 2 then
return "Place more flags (" .. #flag_positions .. "/2)"
else
local negative, positive = get_flags()
if positive and negative then
return "Flags placed (" .. #flag_positions .. "/2)"
else
return "Place one flag on each side of the barrier."
end
end
end
local function show_gui(name)
if not mapauthor then
mapauthor = name
storage:set_string("mapauthor", mapauthor)
end
local formspec = {
"size[9,9.5]",
"bgcolor[#080808BB;true]",
default.gui_bg,
default.gui_bg_img,
"label[0,0;1. Select Area]",
"field[0.4,1;1,1;posx;X;", center.x, "]",
"field[1.4,1;1,1;posy;Y;", center.y, "]",
"field[2.4,1;1,1;posz;Z;", center.z, "]",
"field[0.4,2;1.5,1;posr;R;", center.r, "]",
"field[1.9,2;1.5,1;posh;H;", center.h, "]",
"button[4.3,0.7;1.75,1;set_center;Player Pos]",
"button[6.05,0.7;1.5,1;towe;To WE]",
"button[7.55,0.7;1.5,1;fromwe;From WE]",
"button[4.3,1.7;4.75,1;emerge;Emerge Area]",
"box[0,2.65;8.85,0.05;#111111BB]",
"label[0,2.8;2. Place Barriers]",
"label[0.1,3.3;This may take a few minutes.]",
"field[0.4,4.3;1,1;barrier_r;R;", barrier_r, "]",
"dropdown[1.15,4.05;1,1;center_barrier_rot;X=0,Z=0;", center_barrier_rot + 1, "]",
"button[2.3,4;2,1;place_barrier;Place Barriers]",
"box[4.4,2.8;0.05,2.2;#111111BB]",
"label[4.8,2.8;3. Place Flags]",
"label[4.8,3.3;", minetest.formspec_escape(get_flag_status()), "]",
"button[4.8,4;3.5,1;giveme;Giveme Flags]",
"box[0,5.06;8.85,0.05;#111111BB]",
"label[0,5.15;4. Meta Data]",
"field[0.4,6.2;8.5,1;title;Title;", minetest.formspec_escape(maptitle), "]",
"field[0.4,7.3;8.5,1;initial;Stuff to give on (re)spawn, comma-separated itemstrings;",
minetest.formspec_escape(mapinitial), "]",
"field[0.4,8.4;4.25,1;name;File Name;" , minetest.formspec_escape(mapname), "]",
"field[4.625,8.4;4.25,1;author;Author;", minetest.formspec_escape(mapauthor), "]",
"button_exit[1.3,9;3,1;close;Close]",
"button_exit[4.3,9;3,1;export;Export]",
}
formspec = table.concat(formspec, "")
minetest.show_formspec(name, "ctf_map:tool", formspec)
end
local function show_progress_formspec(name, text)
minetest.show_formspec(name, "ctf_map:progress",
"size[6,1]bgcolor[#080808BB;true]" ..
default.gui_bg ..
default.gui_bg_img .. "label[0,0;" ..
minetest.formspec_escape(text) .. "]")
end
local function emerge_progress(ctx)
show_progress_formspec(ctx.name, string.format("Emerging Area - %d/%d blocks emerged (%.1f%%)",
ctx.current_blocks, ctx.total_blocks,
(ctx.current_blocks / ctx.total_blocks) * 100))
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= "ctf_map:tool" then
return
end
if fields.posx then
center.x = tonumber(fields.posx)
center.y = tonumber(fields.posy)
center.z = tonumber(fields.posz)
center.r = tonumber(fields.posr)
center.h = tonumber(fields.posh)
storage:set_string("center", minetest.write_json(center))
end
fields.barrier_r = tonumber(fields.barrier_r)
if fields.barrier_r and fields.barrier_r ~= barrier_r then
barrier_r = fields.barrier_r
storage:set_int("barrier_r", barrier_r)
end
if fields.title and fields.title ~= maptitle then
maptitle = fields.title
storage:set_string("maptitle", maptitle)
end
if fields.author and fields.author ~= mapauthor then
mapauthor = fields.author
storage:set_string("mapauthor", mapauthor)
end
if fields.name and fields.name ~= mapname then
mapname = fields.name
storage:set_string("mapname", mapname)
end
if fields.initial and fields.initial ~= mapinitial then
mapinitial = fields.initial
storage:set_string("mapinitial", mapinitial)
end
if fields.center_barrier_rot and fields.center_barrier_rot ~= "" then
center_barrier_rot = fields.center_barrier_rot == "X=0" and 0 or 1
storage:set_int("center_barrier_rot", center_barrier_rot)
end
if fields.set_center then
local r = center.r
local h = center.h
center = vector.floor(player:get_pos())
center.r = r
center.h = h
storage:set_string("center", minetest.write_json(center))
end
if fields.giveme then
player:get_inventory():add_item("main", "ctf_map:flag 2")
end
local player_name = player:get_player_name()
if fields.emerge then
local pos1, pos2 = to_2pos()
show_progress_formspec(player_name, "Emerging area...")
ctf_map.emerge_with_callbacks(player_name, pos1, pos2, function()
show_gui(player_name)
end, emerge_progress)
return true
end
if fields.place_barrier then
local pos1, pos2 = to_2pos()
show_progress_formspec(player_name, "Emerging area...")
ctf_map.emerge_with_callbacks(player_name, pos1, pos2, function()
show_progress_formspec(player_name, "Placing center barrier, this may take a while...")
minetest.after(0.1, function()
ctf_map.place_middle_barrier(center, barrier_r, center.h, (center_barrier_rot == 0) and "x" or "z")
show_progress_formspec(player_name, "Placing outer barriers, this may take a while...")
minetest.after(0.1, function()
ctf_map.place_outer_barrier(center, barrier_r, center.h)
show_gui(player_name)
end)
end)
end, emerge_progress)
return true
end
if fields.towe then
we_select(player_name)
end
if fields.fromwe then
we_import(player_name)
end
if fields.export then
if #flag_positions ~= 2 then
minetest.chat_send_all("You need to place two flags!")
return
end
we_select(player_name)
show_progress_formspec(player_name, "Exporting...")
local path = minetest.get_worldpath() .. "/schems/"
minetest.mkdir(path)
-- Reset mod_storage
storage:set_string("center", "")
storage:set_string("maptitle", "")
storage:set_string("mapauthor", "")
storage:set_string("mapname", "")
storage:set_string("mapinitial", "")
storage:set_string("center_barrier_rot", "")
storage:set_string("barrier_r", "")
-- Write to .conf
local meta = Settings(path .. mapname .. ".conf")
meta:set("name", maptitle)
meta:set("author", mapauthor)
meta:set("initial_stuff", mapinitial)
meta:set("rotation", center_barrier_rot == 0 and "x" or "z")
meta:set("r", center.r)
meta:set("h", center.h)
for _, flags in pairs(flag_positions) do
local pos = vector.subtract(flags, center)
if center_barrier_rot == 0 then
local old = vector.new(pos)
pos.x = old.z
pos.z = -old.x
end
local idx = pos.z > 0 and 1 or 2
meta:set("team." .. idx, pos.z > 0 and "red" or "blue")
meta:set("team." .. idx .. ".color", pos.z > 0 and "red" or "blue")
meta:set("team." .. idx .. ".pos", minetest.pos_to_string(pos))
end
meta:write()
minetest.after(0.1, function()
local filepath = path .. mapname .. ".mts"
if minetest.create_schematic(worldedit.pos1[player_name],
worldedit.pos2[player_name], worldedit.prob_list[player_name],
filepath) then
minetest.chat_send_all("Exported " .. mapname .. " to " .. path)
minetest.close_formspec(player_name, "")
else
minetest.chat_send_all("Failed!")
show_gui(player_name)
end
end)
return
end
if not fields.quit then
show_gui(player_name)
end
end)
minetest.register_chatcommand("gui", {
func = function(name)
show_gui(name)
return true
end
})

View file

@ -0,0 +1,81 @@
minetest.register_node("ctf_map:ignore", {
description = "Artificial Ignore", -- this may need to be given a more appropriate name
drawtype = "airlike",
paramtype = "light",
sunlight_propagates = true,
walkable = true,
pointable = false,
diggable = false,
buildable_to = false,
air_equivalent = true,
drop = "",
groups = {not_in_creative_inventory=1}
})
minetest.register_node("ctf_map:ind_glass", {
description = "Indestructible Glass",
drawtype = "glasslike_framed_optional",
tiles = {"default_glass.png", "default_glass_detail.png"},
inventory_image = minetest.inventorycube("default_glass.png"),
paramtype = "light",
sunlight_propagates = true,
is_ground_content = false,
walkable = true,
buildable_to = false,
pointable = false,
groups = {immortal = 1},
sounds = default.node_sound_glass_defaults()
})
minetest.register_node("ctf_map:ind_stone", {
description = "Indestructible Stone",
groups = {immortal = 1},
tiles = {"default_stone.png"},
is_ground_content = false
})
minetest.register_node("ctf_map:ind_dirt", {
description = "Indestructible Dirt",
groups = {immortal = 1},
tiles = {"default_dirt.png"},
is_ground_content = false,
sounds = default.node_sound_dirt_defaults({
footstep = {name = "default_grass_footstep", gain = 0.25}
}),
})
minetest.register_node("ctf_map:ind_dirt_with_grass", {
description = "Indestructible Dirt with Grass",
groups = {immortal = 1},
tiles = {"default_grass.png", "default_dirt.png",
{name = "default_dirt.png^default_grass_side.png",
tileable_vertical = false}},
is_ground_content = false,
sounds = default.node_sound_dirt_defaults({
footstep = {name = "default_grass_footstep", gain = 0.25},
}),
})
minetest.register_node("ctf_map:ind_stone_red", {
description = "Indestructible Red Stone",
groups = {immortal = 1},
tiles = {"ctf_map_stone_red.png"},
is_ground_content = false
})
minetest.register_node("ctf_map:ind_glass_red", {
description = "Indestructible Red Glass",
drawtype = "glasslike",
tiles = {"ctf_map_glass_red.png"},
inventory_image = minetest.inventorycube("default_glass.png"),
paramtype = "light",
sunlight_propagates = true,
is_ground_content = false,
walkable = true,
buildable_to = false,
use_texture_alpha = false,
alpha = 0,
pointable = false,
groups = {immortal = 1},
sounds = default.node_sound_glass_defaults()
})

View file

@ -0,0 +1,287 @@
assert(minetest.get_mapgen_setting("mg_name") == "singlenode", "singlenode mapgen is required.")
minetest.register_alias("mapgen_singlenode", "ctf_map:ignore")
minetest.register_alias("ctf_map:flag", "air")
minetest.register_alias("flowers:mushroom_red", "air")
minetest.register_alias("flowers:mushroom_brown", "air")
minetest.register_alias("flowers:waterlily", "air")
minetest.register_alias("flowers:rose", "air")
minetest.register_alias("flowers:tulip", "air")
minetest.register_alias("flowers:dandelion_yellow", "air")
minetest.register_alias("flowers:geranium", "air")
minetest.register_alias("flowers:viola", "air")
minetest.register_alias("flowers:dandelion_white", "air")
minetest.register_alias("flowers:chrysanthemum_green", "air")
minetest.register_alias("default:grass_1", "air")
minetest.register_alias("default:grass_2", "air")
minetest.register_alias("default:grass_3", "air")
minetest.register_alias("default:grass_4", "air")
minetest.register_alias("default:sand_with_kelp", "default:sand")
minetest.register_alias("default:grass_5", "air")
minetest.register_alias("default:bush_leaves", "air")
minetest.register_alias("default:bush_stem", "air")
minetest.register_alias("default:stone_with_gold", "default:stone")
local max_r = 120
local mapdir = minetest.get_modpath("ctf_map") .. "/maps/"
ctf_map.map = nil
local next_idx
minetest.register_chatcommand("set_next", {
privs = { ctf_admin = true },
func = function(name, param)
for i, mname in pairs(ctf_map.available_maps) do
if mname:lower():find(param, 1, true) then
next_idx = i
return true, "Selected " .. mname
end
end
end,
})
local function search_for_maps()
local files_hash = {}
local dirs = minetest.get_dir_list(mapdir, true)
table.insert(dirs, ".")
for _, dir in pairs(dirs) do
if dir ~= ".git" then
local files = minetest.get_dir_list(mapdir .. dir, false)
for i=1, #files do
local parts = files[i]:split(".")
local filename = parts[1]
local extension = parts[2]
if extension == "mts" then
files_hash[dir .. "/" .. filename] = true
else
if extension ~= "conf" and extension ~= "md"
and files[i] ~= ".git" then
error("Map extension is not '.mts': " .. files[i])
end
end
end
end
end
ctf_map.available_maps = {}
for key, _ in pairs(files_hash) do
local conf = Settings(mapdir .. "/" .. key .. ".conf")
minetest.log("error", "ctf.maps." .. key:gsub("%./", ""):gsub("/", "."))
local val = minetest.settings:get("ctf.maps." .. key:gsub("%./", ""):gsub("/", "."))
if not conf:get_bool("disabled", false) and val ~= "false" then
table.insert(ctf_map.available_maps, key)
end
end
if next(ctf_map.available_maps) == nil then
error("No maps found in directory " .. mapdir)
end
return ctf_map.available_maps
end
search_for_maps()
minetest.register_chatcommand("maps_reload", {
privs = { ctf_admin = true },
func = function(name, param)
local maps = search_for_maps()
next_idx = nil
return true, #maps .. " maps found: " .. table.concat(maps, ", ")
end,
})
function ctf_map.place_map(map)
ctf_map.emerge_with_callbacks(nil, map.pos1, map.pos2, function()
local schempath = mapdir .. map.schematic
local res = minetest.place_schematic(map.pos1, schempath,
map.rotation == "z" and "0" or "90")
assert(res)
for _, value in pairs(ctf_map.map.teams) do
ctf_team_base.place(value.color, value.pos)
end
local seed = minetest.get_mapgen_setting("seed")
for _, chestzone in pairs(ctf_map.map.chests) do
minetest.log("warning", "Placing " .. chestzone.n .. " chests from " ..
minetest.pos_to_string(chestzone.from) .. " to "..
minetest.pos_to_string(chestzone.to))
place_chests(chestzone.from, chestzone.to, seed, chestzone.n)
end
minetest.after(2, function()
local msg = "Map: " .. map.name .. " by " .. map.author
if map.hint then
msg = msg .. "\n" .. map.hint
end
minetest.chat_send_all(msg)
if minetest.global_exists("irc") and irc.connected then
irc:say("Map: " .. map.name)
end
end)
minetest.after(10, function()
minetest.fix_light(ctf_map.map.pos1, ctf_map.map.pos2)
end)
end, nil)
end
function ctf_match.load_map_meta(idx, name)
local offset = vector.new(600 * idx, 0, 0)
local conf_path = mapdir .. name .. ".conf"
local meta = Settings(conf_path)
if meta:get("r") == nil then
error("Map was not properly configured " .. conf_path)
end
local initial_stuff = meta:get("initial_stuff")
local map = {
idx = idx,
name = meta:get("name"),
author = meta:get("author"),
hint = meta:get("hint"),
rotation = meta:get("rotation"),
schematic = name .. ".mts",
initial_stuff = initial_stuff and initial_stuff:split(","),
r = tonumber(meta:get("r")),
h = tonumber(meta:get("h")),
offset = offset,
teams = {},
chests = {},
}
assert(map.r <= max_r)
map.pos1 = vector.add(offset, { x = -map.r, y = -map.h / 2, z = -map.r })
map.pos2 = vector.add(offset, { x = map.r, y = map.h / 2, z = map.r })
-- Read teams from config
local i = 1
while meta:get("team." .. i) do
local tname = meta:get("team." .. i)
local tcolor = meta:get("team." .. i .. ".color")
local tpos = minetest.string_to_pos(meta:get("team." .. i .. ".pos"))
map.teams[tname] = {
color = tcolor,
pos = vector.add(offset, tpos),
}
i = i + 1
end
-- Read custom chest zones from config
i = 1
while meta:get("chests." .. i .. ".from") do
local from = minetest.string_to_pos(meta:get("chests." .. i .. ".from"))
local to = minetest.string_to_pos(meta:get("chests." .. i .. ".to"))
assert(from and to, "Positions needed for chest zone " .. i .. " in map " .. map.name)
map.chests[i] = {
from = vector.add(offset, from),
to = vector.add(offset, to),
n = tonumber(meta:get("chests." .. i .. ".n") or "23"),
}
minetest.log("warning", dump(map.chests[i]))
i = i + 1
end
-- Add default chest zones if none given
if i == 1 then
while meta:get("team." .. i) do
local chests1
if i == 1 then
chests1 = vector.add(offset, { x = -map.r, y = -map.h / 2, z = 0 })
elseif i == 2 then
chests1 = map.pos1
end
local chests2
if i == 1 then
chests2 = map.pos2
elseif i == 2 then
chests2 = vector.add(offset, { x = map.r, y = map.h / 2, z = 0 })
end
map.chests[i] = {
from = chests1,
to = chests2,
n = 23,
}
i = i + 1
end
end
return map
end
ctf_match.register_on_new_match(function()
minetest.clear_objects({ mode = "quick" })
-- Choose next map index, but don't select the same one again
local idx
if next_idx then
idx = next_idx
elseif ctf_map.map then
idx = math.random(#ctf_map.available_maps - 1)
if idx >= ctf_map.map.idx then
idx = idx + 1
end
else
idx = math.random(#ctf_map.available_maps)
end
next_idx = (idx % #ctf_map.available_maps) + 1
-- Load meta data
local name = ctf_map.available_maps[idx]
ctf_map.map = ctf_match.load_map_meta(idx, name)
-- Place map
ctf_map.place_map(ctf_map.map)
end)
function ctf_match.create_teams()
for key, value in pairs(ctf_map.map.teams) do
local name = key
local color = value.color
local flag = value.pos
if name and color and flag then
print(" - creating " .. key)
ctf.team({
name = name,
color = color,
add_team = true
})
ctf_flag.add(name, flag)
minetest.after(0, function()
ctf_flag.assert_flag(flag)
end)
else
minetest.log("error", " - Failed to create " .. key)
end
end
end
minetest.register_on_joinplayer(function(player)
local map = ctf_map.map
if not map then
return
end
local msg = "Map: " .. map.name .. " by " .. map.author
if map.hint then
msg = msg .. "\n" .. map.hint
end
minetest.chat_send_player(player:get_player_name(), msg)
end)

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

View file

@ -0,0 +1,111 @@
ctf.register_on_init(function()
ctf._set("match.build_time", 60)
end)
ctf_match.registered_on_build_time_start = {}
function ctf_match.register_on_build_time_start(func)
if ctf._mt_loaded then
error("You can't register callbacks at game time!")
end
table.insert(ctf_match.registered_on_build_time_start, func)
end
ctf_match.registered_on_build_time_end = {}
function ctf_match.register_on_build_time_end(func)
if ctf._mt_loaded then
error("You can't register callbacks at game time!")
end
table.insert(ctf_match.registered_on_build_time_end, func)
end
ctf_match.build_timer = 0
function ctf_match.is_in_build_time()
return ctf_match.build_timer > 0
end
ctf_match.register_on_new_match(function()
ctf_match.build_timer = ctf.setting("match.build_time")
end)
ctf.register_on_new_game(function()
ctf_match.build_timer = ctf.setting("match.build_time")
if ctf_match.build_timer > 0 then
for i = 1, #ctf_match.registered_on_build_time_start do
ctf_match.registered_on_build_time_start[i]()
end
end
end)
local function get_m_s_from_s(s)
local m = math.floor(s / 60)
s = math.floor(s - m * 60)
return m .. "m " .. s .. "s"
end
local last = 0
minetest.register_globalstep(function(delta)
if ctf_match.build_timer > 0 then
ctf_match.build_timer = ctf_match.build_timer - delta
if ctf_match.build_timer <= 0 then
for i = 1, #ctf_match.registered_on_build_time_end do
ctf_match.registered_on_build_time_end[i]()
end
end
local rbt = math.floor(ctf_match.build_timer)
if last ~= rbt then
local text = get_m_s_from_s(ctf_match.build_timer) .. " until match begins!"
for _, player in pairs(minetest.get_connected_players()) do
ctf.hud:change(player, "ctf_match:countdown", "text", text)
end
end
end
end)
minetest.register_on_punchplayer(function(_, hitter)
if ctf_match.is_in_build_time() then
minetest.chat_send_player(hitter:get_player_name(), "Match hasn't started yet!")
return true
end
end)
ctf_match.register_on_build_time_start(function()
minetest.chat_send_all("Prepare your base! Match starts in " ..
ctf.setting("match.build_time") .. " seconds.")
end)
ctf_match.register_on_build_time_end(function()
if minetest.global_exists("chatplus") then
chatplus.log("Build time over!")
end
minetest.chat_send_all("Build time over! Attack and defend!")
for _, player in pairs(minetest.get_connected_players()) do
ctf.hud:remove(player, "ctf_match:countdown")
end
end)
ctf_flag.register_on_prepick_up(function(name, flag)
if ctf_match.is_in_build_time() then
minetest.chat_send_player(name, "Match hasn't started yet!")
ctf.move_to_spawn(name)
return false
else
return true
end
end)
ctf.hud.register_part(function(player, name, tplayer)
if ctf_match.build_timer <= 0 then
ctf.hud:remove(player, "ctf_match:countdown")
elseif not ctf.hud:exists(player, "ctf_match:countdown") then
ctf.hud:add(player, "ctf_match:countdown", {
hud_elem_type = "text",
position = {x = 0.5, y = 0.5},
scale = {x = 0, y = 70},
text = get_m_s_from_s(ctf_match.build_timer) .. " until match begins!",
number = 0xFFFFFF,
offset = {x = -20, y = 20},
alignment = {x = 0.2, y = 0}
})
end
end)

View file

@ -0,0 +1,81 @@
minetest.register_privilege("ctf_match", {
description = "can skip matches"
})
minetest.register_chatcommand("ctf_next", {
description = "Skip to the next match",
privs = {
ctf_match = true
},
func = function(name, param)
ctf_match.next()
end
})
minetest.register_chatcommand("ctf_start", {
description = "End build time",
privs = {
ctf_match = true
},
func = function(name, param)
ctf_match.build_timer = 0.01
end
})
minetest.register_chatcommand("ctf_respawn", {
description = "Respawn a player (clean inv, send to base)",
privs = {
ctf_team_mgr = true
},
func = function(name, param)
minetest.log("action", name .. " ran /ctf_respawn " .. param)
local tplayer = ctf.player_or_nil(param)
if tplayer then
local player = minetest.get_player_by_name(param)
if player then
ctf.move_to_spawn(param)
give_initial_stuff(player)
minetest.chat_send_player(param,
"You were sent back to base and your inventory wiped (by " .. name .. ")")
return true, "Moved player to spawn and wiped inventory."
else
return false, "Player is not online."
end
else
return false, "Player does not exist or is not in any teams."
end
end
})
local restart_on_next_match = false
local restart_on_next_match_by = nil
minetest.register_chatcommand("ctf_queue_restart", {
description = "Queue server restart",
privs = {
server = true
},
func = function(name, param)
restart_on_next_match = true
restart_on_next_match_by = name
return true, "Restart queued."
end
})
minetest.register_chatcommand("ctf_unqueue_restart", {
description = "Unqueue server restart",
privs = {
server = true
},
func = function(name, param)
restart_on_next_match = false
return true, "Restart cancelled."
end
})
ctf_match.register_on_new_match(function()
if restart_on_next_match then
minetest.chat_send_player(restart_on_next_match_by, "Shutting down now!")
minetest.request_shutdown("Restarting server at operator request.", true)
end
end)

View file

@ -0,0 +1,7 @@
ctf
ctf_flag
ctf_inventory
ctf_alloc
vote
hudkit
irc?

View file

@ -0,0 +1,42 @@
ctf_match = {}
local claimed = ctf_flag.collect_claimed()
for i, flag in pairs(claimed) do
flag.claimed = nil
end
dofile(minetest.get_modpath("ctf_match") .. "/matches.lua")
dofile(minetest.get_modpath("ctf_match") .. "/buildtime.lua")
dofile(minetest.get_modpath("ctf_match") .. "/chat.lua")
dofile(minetest.get_modpath("ctf_match") .. "/vote.lua")
ctf.register_on_init(function()
ctf._set("match.remove_player_on_leave", false)
minetest.settings:set_bool("enable_pvp", true)
end)
ctf_match.register_on_build_time_end(function()
minetest.sound_play({name="ctf_match_attack"}, { gain = 1.0 })
end)
minetest.register_on_leaveplayer(function(player)
if ctf.setting("match.remove_player_on_leave") then
ctf.remove_player(player:get_player_name())
end
end)
if minetest.global_exists("irc") then
ctf_match.register_on_winner(function(winner)
if not irc.connected then return end
irc:say("Team " .. winner .. " won!")
end)
ctf.register_on_new_game(function()
if not irc.connected then return end
irc:say("Next round!")
end)
end
minetest.after(5, function()
ctf_match.next()
end)

View file

@ -0,0 +1,98 @@
ctf.register_on_init(function()
ctf._set("match", false)
ctf._set("match.destroy_team", false)
ctf._set("match.break_alliances", true)
ctf._set("match.teams", 2)
ctf._set("match.team.1", "red")
ctf._set("match.team.1.color", "red")
ctf._set("match.team.1.pos", "7,65,93")
ctf._set("match.team.2", "blue")
ctf._set("match.team.2.color", "blue")
ctf._set("match.team.2.pos", "-22,66,-78")
ctf._set("match.clear_inv", false)
end)
ctf_match.registered_on_new_match = {}
function ctf_match.register_on_new_match(func)
if ctf._mt_loaded then
error("You can't register callbacks at game time!")
end
table.insert(ctf_match.registered_on_new_match, func)
end
ctf_match.registered_on_winner = {}
function ctf_match.register_on_winner(func)
if ctf._mt_loaded then
error("You can't register callbacks at game time!")
end
table.insert(ctf_match.registered_on_winner, func)
end
-- Load next match. May be overrided
function ctf_match.next()
for i = 1, #ctf_match.registered_on_new_match do
ctf_match.registered_on_new_match[i]()
end
ctf.reset()
ctf_match.create_teams()
ctf_alloc.set_all()
minetest.set_timeofday(0.4)
minetest.chat_send_all("Next round!")
if minetest.global_exists("chatplus") then
chatplus.log("Next round!")
end
end
-- Check for winner
local game_won = false
function ctf_match.check_for_winner()
local winner
for name, team in pairs(ctf.teams) do
if winner then
return
end
winner = name
end
-- There is a winner!
if not game_won then
game_won = true
ctf.action("match", winner .. " won!")
minetest.chat_send_all("Team " .. winner .. " won!")
for i = 1, #ctf_match.registered_on_winner do
ctf_match.registered_on_winner[i](winner)
end
minetest.after(2, function()
game_won = false
if ctf.setting("match") then
ctf_match.next()
end
end)
end
end
-- This is overriden by ctf_map
function ctf_match.create_teams()
error("Error! Unimplemented")
end
ctf_flag.register_on_capture(function(attname, flag)
if not ctf.setting("match.destroy_team") then
return
end
local fl_team = ctf.team(flag.team)
if fl_team and #fl_team.flags == 0 then
ctf.action("match", flag.team .. " was defeated.")
ctf.remove_team(flag.team)
minetest.chat_send_all(flag.team .. " has been defeated!")
end
ctf_match.check_for_winner()
end)

Binary file not shown.

View file

@ -0,0 +1,62 @@
ctf_match.registered_on_skip_map = {}
function ctf_match.register_on_skip_map(func)
if ctf._mt_loaded then
error("You can't register callbacks at game time!")
end
table.insert(ctf_match.registered_on_skip_map, func)
end
function ctf_match.vote_next(name, params)
if minetest.global_exists("irc") then
local tname = ctf.player(name).team or "none"
irc:say("Vote started by " .. name .. " (team " .. tname .. ")")
end
return vote.new_vote(name, {
description = "Skip to next match",
help = "/yes, /no or /abstain",
duration = 60,
perc_needed = 0.5,
unanimous = 5,
on_result = function(self, result, results)
if result == "yes" then
minetest.chat_send_all("Vote to skip match passed, " ..
#results.yes .. " to " .. #results.no)
for i = 1, #ctf_match.registered_on_skip_map do
ctf_match.registered_on_skip_map[i]()
end
ctf_match.next()
else
minetest.chat_send_all("Vote to skip match failed, " ..
#results.no .. " to " .. #results.yes)
end
end,
on_vote = function(self, voter, value)
minetest.chat_send_all(voter .. " voted " .. value .. " to '" ..
self.description .. "'")
end
})
end
ctf_match.register_on_new_match(vote.clear_vote)
minetest.register_chatcommand("vote", {
privs = {
interact = true,
vote_starter = true
},
func = ctf_match.vote_next
})
minetest.register_on_chat_message(function(name, msg)
if msg == "/vote_next" and minetest.check_player_privs(name,
{interact=true, vote_starter=true}) then
local _, vmsg = ctf_match.vote_next(name)
if vmsg then
minetest.chat_send_player(name, vmsg)
end
return true
end
end)

View file

@ -0,0 +1,3 @@
ctf
ctf_stats
prometheus?

View file

@ -0,0 +1,29 @@
if not minetest.global_exists("prometheus") then
return
end
local kill_counter = 0
ctf.register_on_killedplayer(function(victim, killer, type)
kill_counter = kill_counter + 1
end)
local function step()
prometheus.post("minetest_kills", kill_counter)
kill_counter = 0
local sum = 0
local avg = 0
if #minetest.get_connected_players() > 0 then
for _, player in pairs(minetest.get_connected_players()) do
local total, _ = ctf_stats.player(player:get_player_name())
sum = sum + total.score
end
avg = sum / #minetest.get_connected_players()
end
prometheus.post("minetest_ctf_score_total", sum)
prometheus.post("minetest_ctf_score_avg", avg)
minetest.after(15, step)
end
minetest.after(15, step)

View file

@ -0,0 +1,5 @@
This mod hides the existing tags, and adds entity based tags that are only as visible as the player.
Some code taken from gauges (CC0 1.0) https://forum.minetest.net/viewtopic.php?t=10250
And also some code and textures from npcf (LGPL for code, WTFPL for textures) https://forum.minetest.net/viewtopic.php?t=7321
My part of the code is WTFPL.

View file

@ -0,0 +1,150 @@
local nametags = {}
local tag_settings = {}
local ATTACH_POSITION = minetest.rgba and {x=0,y=20,z=0} or {x=0,y=10,z=0}
local TYPE_BUILTIN = 0
local TYPE_ENTITY = 1
playertag = {
TYPE_BUILTIN = TYPE_BUILTIN,
TYPE_ENTITY = TYPE_ENTITY,
}
local function add_entity_tag(player)
local ent = minetest.add_entity(player:get_pos(), "ctf_playertag:tag")
-- Build name from font texture
local color = "W"
local texture = "npcf_tag_bg.png"
local x = math.floor(134 - ((player:get_player_name():len() * 11) / 2))
local i = 0
player:get_player_name():gsub(".", function(char)
if char:byte() > 64 and char:byte() < 91 then
char = "U"..char
end
texture = texture.."^[combine:84x14:"..(x+i)..",0="..color.."_"..char..".png"
i = i + 11
end)
ent:set_properties({ textures={texture} })
-- Attach to player
ent:set_attach(player, "", ATTACH_POSITION, {x=0,y=0,z=0})
ent:get_luaentity().wielder = player:get_player_name()
-- Store
nametags[player:get_player_name()] = ent
-- Hide fixed nametag
player:set_nametag_attributes({
color = {a = 0, r = 0, g = 0, b = 0}
})
end
local function remove_entity_tag(player)
tag_settings[player:get_player_name()] = nil
local tag = nametags[player:get_player_name()]
if tag then
tag:remove()
nametags[player:get_player_name()] = nil
end
end
local function update(player, settings)
tag_settings[player:get_player_name()] = settings
if settings.type == TYPE_BUILTIN then
remove_entity_tag(player)
print(dump(settings.color))
player:set_nametag_attributes({
color = settings.color
})
elseif settings.type == TYPE_ENTITY then
add_entity_tag(player)
end
end
function playertag.set(player, type, color)
local oldset = tag_settings[player:get_player_name()]
color = color or { a=255, r=255, g=255, b=255 }
if not oldset or oldset.type ~= type or oldset.color ~= color then
update(player, { type = type, color = color })
end
end
function playertag.get(player)
return tag_settings[player:get_player_by_name()]
end
function playertag.get_all()
return tag_settings
end
local nametag = {
npcf_id = "nametag",
physical = false,
collisionbox = {x=0, y=0, z=0},
visual = "sprite",
textures = {"default_dirt.png"},--{"npcf_tag_bg.png"},
visual_size = {x=2.16, y=0.18, z=2.16},--{x=1.44, y=0.12, z=1.44},
}
function nametag:on_activate(staticdata, dtime_s)
if staticdata == "expired" then
local name = self.wielder and self.wielder:get_player_name()
if name and nametags[name] == self.object then
nametags[name] = nil
end
self.object:remove()
end
end
function nametag:get_staticdata()
return "expired"
end
function nametag:on_step(dtime)
local name = self.wielder
local wielder = name and minetest.get_player_by_name(name)
if not wielder then
self.object:remove()
elseif not tag_settings[name] or tag_settings[name].type ~= TYPE_ENTITY then
if name and nametags[name] == self.object then
nametags[name] = nil
end
self.object:remove()
end
end
minetest.register_entity("ctf_playertag:tag", nametag)
local function step()
for _, player in pairs(minetest.get_connected_players()) do
local settings = tag_settings[player:get_player_name()]
if settings and settings.type == TYPE_ENTITY then
local ent = nametags[player:get_player_name()]
if not ent or ent:get_luaentity() == nil then
add_entity_tag(player)
else
ent:set_attach(player, "", ATTACH_POSITION, {x=0,y=0,z=0})
end
end
end
minetest.after(10, step)
end
minetest.after(10, step)
minetest.register_on_joinplayer(function(player)
playertag.set(player, TYPE_BUILTIN, {a = 0, r = 255, g = 255, b = 255})
minetest.after(2, function(name)
player = minetest.get_player_by_name(name)
if player then
playertag.set(player, TYPE_ENTITY)
end
end, player:get_player_name())
end)
minetest.register_on_leaveplayer(function(player)
remove_entity_tag(player)
end)

View file

@ -0,0 +1,3 @@
ctf_flag
ctf_match
ctf_colors

View file

@ -0,0 +1,24 @@
dofile(minetest.get_modpath("ctf_playertag") .. "/api.lua")
ctf_flag.register_on_pick_up(function(attname, flag)
local _, color = ctf_colors.get_color(ctf.player(attname))
color = color:gsub("0x", "#")
playertag.set(minetest.get_player_by_name(attname), playertag.TYPE_BUILTIN,
color)
end)
ctf_flag.register_on_drop(function(attname, flag)
playertag.set(minetest.get_player_by_name(attname), playertag.TYPE_ENTITY)
end)
ctf_flag.register_on_capture(function(attname, flag)
playertag.set(minetest.get_player_by_name(attname), playertag.TYPE_ENTITY)
end)
ctf_match.register_on_new_match(function()
for name, settings in pairs(playertag.get_all()) do
if settings.type == playertag.TYPE_BUILTIN then
playertag.set(minetest.get_player_by_name(name), playertag.TYPE_ENTITY)
end
end
end)

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 B

Some files were not shown because too many files have changed in this diff Show more