nc_ctf/init.lua

447 lines
13 KiB
Lua

-- LUALOCALS < ---------------------------------------------------------
local include, nodecore, minetest
= include, nodecore, minetest
-- LUALOCALS > ---------------------------------------------------------
local modname = minetest.get_current_modname()
ctf = {}
local cfg = include("config")
local teams = cfg.teams
local CRYSTALS_PER_TEAM = cfg.CRYSTALS_PER_TEAM
local PLAYERS_PER_TEAM = cfg.PLAYERS_PER_TEAM
local SPAWNPOINT = cfg.SPAWNPOINT
local SPAWN_OFFSET = cfg.SPAWN_OFFSET
local FIELD_RADIUS = cfg.FIELD_RADIUS
local BORDER_RADIUS = cfg.BORDER_RADIUS
local SPAWN_DIST = cfg.SPAWN_DIST
local TEAM_DIST = cfg.TEAM_DIST
local PLAYER_DIST = cfg.PLAYER_DIST
local SPAWNFALL_LIMIT = cfg.SPAWNFALL_LIMIT
local TELEPORT_ROOM = cfg.TELEPORT_ROOM
ctf.player_count = 0
ctf.voted_count = 0
ctf.players = {}
ctf.teams = {}
ctf.started = false
ctf.starting = false
ctf.ended = false
do
local f = math.floor
local function sign(x)
return x > 0 and 1 or (x < 0 and -1 or 0)
end
function ctf.spawn(name,pos,call)
local ref = minetest.get_player_by_name(name)
ref:set_pos(TELEPORT_ROOM)
pos = {x=f(pos.x),y=f(pos.y),z=f(pos.z)}
call = call or function()end
local limit = SPAWNFALL_LIMIT
minetest.chat_send_player(name,"<TELEPORT> Configuring transportation processor...")
minetest.emerge_area({x=pos.x,y=pos.y-limit,z=pos.z},{x=pos.x,y=pos.y+limit,z=pos.z},function(bp,act,crem)
if crem > 0 then return end
minetest.chat_send_player(name,"<TELEPORT> Done. Sending transport coordinates...")
local off = 0
while off > -limit do
if minetest.get_node({x=pos.x,y=pos.y+off,z=pos.z}).name ~= "air" then
off = off + 1
break
end
off = off - 1
end
while off < limit do
if minetest.get_node({x=pos.x,y=pos.y+off,z=pos.z}).name == "air" then
break
end
off = off + 1
end
local apos = {x=pos.x,y=pos.y+off,z=pos.z}
local ref = minetest.get_player_by_name(name)
if ref then
ref:set_pos(apos)
call(ref,apos)
end
end)
end
end
minetest.register_on_chat_message(function(name, message)
local team = ctf.players[name].team
if team and ctf.teams[team] and ctf.started and not ctf.ended then
if message:sub(1,1) ~= "." then
local msg = minetest.format_chat_message(name,message)
minetest.chat_send_all(msg)
else
local msg = minetest.colorize(teams[team].colorstr, minetest.format_chat_message(name,message:sub(2)))
for k,v in pairs(ctf.teams[team].players) do
minetest.chat_send_player(k,msg)
end
end
return true
end
end)
minetest.register_chatcommand("vote",{
desc = "Vote to end the game",
privs = {interact = true},
func = function(name,param)
local pl = ctf.players[name]
pl.voted = not pl.voted
ctf.voted_count = pl.voted and (ctf.voted_count+1) or (ctf.voted_count-1)
if pl.voted then
minetest.chat_send_player(name,"You vote to end the game")
else
minetest.chat_send_player(name,"You vote to NOT end the game")
end
end
})
include("node")
ctf.crystals(teams)
local bar_c = minetest.get_content_id(modname..":barrier")
local air_c = minetest.get_content_id("air")
local abs = math.abs
local function distance(a,b)
return math.max(abs(a.x-b.x),abs(a.z-b.z))
end
minetest.register_on_generated(function(minp,maxp,seed)
if vector.distance(TELEPORT_ROOM,vector.divide(vector.add(minp,maxp),2)) < 2000 then
local vm,mip,map = minetest.get_mapgen_object("voxelmanip")
local ar = VoxelArea:new{MinEdge=mip,MaxEdge=map}
local d = vm:get_data()
for x=minp.x,maxp.x do
for y=minp.y,maxp.y do
for z=minp.z,maxp.z do
local i = ar:index(x,y,z)
if vector.distance({x=x,y=y,z=z},TELEPORT_ROOM) > math.random(8,10) then
d[i] = bar_c
else
d[i] = air_c
end
end
end
end
vm:set_data(d)
vm:write_to_map()
else
if distance(vector.divide(vector.add(minp,maxp),2),ctf.spawnpoint) > FIELD_RADIUS then
local vm,mip,map = minetest.get_mapgen_object("voxelmanip")
local ar = VoxelArea:new{MinEdge=mip,MaxEdge=map}
local d = vm:get_data()
for x=minp.x,maxp.x do
for y=minp.y,maxp.y do
for z=minp.z,maxp.z do
local i = ar:index(x,y,z)
d[i] = bar_c
end
end
end
vm:set_data(d)
vm:write_to_map()
end
end
end)
function ctf.set_team_text(name)
local pl = ctf.players[name]
assert(pl)
local team = teams[pl.team]
print(pl.team,team)
ctf.set_text(name,"You're member of "..tostring(team.desc).." team\n",team.colornum)
end
function ctf.join(team,name)
if ctf.players[name].team and ctf.teams[ctf.players[name].team].players[name] then
ctf.teams[ctf.players[name].team].players[name] = nil
end
ctf.teams[team].players[name] = true
ctf.players[name].team = team
end
function ctf.dist_player(name)
local m,t = math.huge,nil
for team,teamd in pairs(ctf.teams) do
local c = 0
for namee,bool in pairs(teamd.players) do
if bool and namee ~= name then
c=c+1
end
end
if c < m or (c == m and math.random()>0.5) then
m = c
t = team
end
end
ctf.join(t,name)
end
minetest.register_on_joinplayer(function(ref)
local name = ref:get_player_name()
local id = ref:hud_add{
name = "stat",
text = "Game is not started yet\nWaiting for more players to begin",
number = 0xFFFFFF,
alignment = {x=-1,y=1},
offset = {x=-20,y=20},
position = {x=1,y=0},
}
ctf.players[name] = {}
ctf.players[name].hud = {stat = id}
ctf.player_count = ctf.player_count + 1
ctf.explode_inv(ref)
minetest.after(1,function()
if ctf.started then
ctf.dist_player(name)
ctf.set_team_text(name)
local pls = {}
for k,v in pairs(ctf.teams[ctf.players[name].team].players) do
if v and k ~= name then
table.insert(pls,minetest.get_player_by_name(k):get_pos())
end
end
if #pls > 0 then
ctf.spawn(name,pls[math.random(1,#pls)])
else
ctf.spawn(name,vector.add(ctf.spawnpoint,SPAWN_OFFSET))
end
else
ctf.spawn(name,vector.add(ctf.spawnpoint,SPAWN_OFFSET))
end
end)
end)
function ctf.explode_inv(ref)
local inv = ref:get_inventory()
local pos = ref:get_pos()
local list = inv:get_list("main")
inv:set_list("main",{})
for k,v in pairs(list) do
if not nodecore.item_is_virtual(v) and not v:is_empty() then
nodecore.item_eject(pos,v,5)
end
end
end
function ctf.set_text(name,text,colornum)
local pl = ctf.players[name]
if pl and pl.hud then
local ref = minetest.get_player_by_name(name)
ref:hud_change(pl.hud.stat,"text",text)
ref:hud_change(pl.hud.stat,"number",colornum or 0xFFFFFF)
end
end
minetest.register_on_leaveplayer(function(ref)
local name = ref:get_player_name()
if ctf.players[name].team and ctf.teams[ctf.players[name].team] then
ctf.teams[ctf.players[name].team].players[name] = nil
end
if ctf.players[name].voted then
ctf.voted_count = ctf.voted_count-1
end
if ctf.starting == math.huge then
ctf.starting = -math.huge
end
ctf.players[name] = nil
ctf.explode_inv(ref)
ctf.player_count = ctf.player_count - 1
end)
function ur(n)
return (math.random()-0.5)*n
end
do
local z = SPAWN_DIST
local pos = vector.add(SPAWNPOINT,{x=ur(z),y=0,z=ur(z)})
ctf.spawnpoint = pos
end
function ctf.start()
if ctf.starting == math.huge or ctf.starting == -math.huge then
return
end
if ctf.started or ctf.ended then
return
end
for k,v in pairs(ctf.players) do
if v.voted then
v.voted = false
ctf.voted_count = ctf.voted_count - 1
end
end
ctf.ended = false
local tc = math.max(2,math.floor(ctf.player_count/PLAYERS_PER_TEAM))
if ctf.player_count >= 2 and not ctf.starting then
ctf.starting = minetest.get_server_uptime()+10
minetest.chat_send_all("<INFO> Enough players are here! Starting in 10 seconds")
elseif ctf.starting and not (ctf.player_count >= 2) then
ctf.starting = false
minetest.chat_send_all("<INFO> Not enough players now. Aborting.")
end
if ctf.starting and minetest.get_server_uptime() >= ctf.starting then
ctf.starting = math.huge
minetest.chat_send_all("<TELEPORT + INFO> CONFIGURING TRANSPORTATION PROCCESSORS FOR EVERYONE . . .")
local c = 0
ctf.teams = {}
for k,v in pairs(teams) do
c=c+1
ctf.teams[k] = {players = {},score = CRYSTALS_PER_TEAM}
if c >= tc then
break
end
end
for name,pl in pairs(ctf.players) do
ctf.explode_inv(minetest.get_player_by_name(name))
ctf.dist_player(name)
ctf.set_team_text(name)
end
local z = SPAWN_DIST
local pos = vector.add(SPAWNPOINT,{x=ur(z),y=0,z=ur(z)})
ctf.spawnpoint = pos
pos = vector.add(pos,SPAWN_OFFSET)
local pc = 0
local call = function(ref,pos)
pc = pc + 1
if pc < ctf.player_count then return end
if ctf.starting == -math.huge then
minetest.chat_send_all("<TELEPORT + INFO> SOMEONE LEFT DURING CONFIGURATION. ABORTING")
ctf.teams = {}
for k,v in pairs(ctf.players) do
v.team = nil
end
ctf.starting = false
ctf.started = false
ctf.ended = false
return
end
ctf.starting = false
ctf.started = true
minetest.chat_send_all("<INFO> The game begins!")
for tname,team in pairs(ctf.teams) do
local crystals = CRYSTALS_PER_TEAM
while crystals > 0 do
local b = false
for name in pairs(team.players) do
local ref = minetest.get_player_by_name(name)
if crystals > 0 then
crystals = crystals - 1
b=true
ref:get_inventory():add_item("main",modname..":crystal_"..tname)
else
break
end
end
if not b then
break
end
end
end
end
for tname,team in pairs(ctf.teams) do
local x = TEAM_DIST
local pos = vector.add(pos,{x=ur(x),y=0,z=ur(x)})
for name in pairs(team.players) do
local y = PLAYER_DIST
local pos = vector.add(pos,{x=ur(y),y=0,z=ur(y)})
local ref = minetest.get_player_by_name(name)
ctf.spawn(name,pos,call)
end
end
end
end
do
local f
function f()
minetest.emerge_area(vector.subtract(TELEPORT_ROOM,{x=20,y=20,z=20}),vector.add(TELEPORT_ROOM,{x=20,y=20,z=20}))
minetest.after(5,f)
end
f()
end
do
local f
function f()
ctf.start()
for name,pl in pairs(ctf.players) do
if not minetest.check_player_privs(name,{server = true}) then
if not pl.team then
ctf.explode_inv(minetest.get_player_by_name(name))
local privs = minetest.get_player_privs(name)
privs.fast = true
privs.fly = true
privs.interact = nil
minetest.set_player_privs(name,privs)
else
local privs = minetest.get_player_privs(name)
privs.fast = nil
privs.fly = nil
privs.interact = true
minetest.set_player_privs(name,privs)
end
end
end
if ctf.started and not ctf.ended then
ctf.check_win()
elseif ctf.voted_count > 0 then
for k,v in pairs(ctf.players) do
if v.voted then
v.voted = false
ctf.voted_count = ctf.voted_count - 1
end
end
end
minetest.after(1,f)
end
minetest.after(1,f)
end
function ctf.check_win()
for name,team in pairs(ctf.teams) do
if team.score <= 0 then
ctf.teams[name] = nil
for pname,pl in pairs(team.players) do
ctf.set_text(pname,":(\nYour team lost.",teams[name].colornum)
end
minetest.chat_send_all("<INFO> "..teams[name].desc.." team lost!")
end
end
local c = 0
for k,v in pairs(ctf.teams) do if v then c=c+1 end end
local vout = (ctf.voted_count/ctf.player_count) >= 0.5
if c == 1 or vout then
ctf.ended = true
if c == 1 then
local name,team = next(ctf.teams)
minetest.chat_send_all("<INFO> "..teams[name].desc.." team won!")
for pname,pl in pairs(team.players) do
ctf.set_text(pname,":D\nYour team won!",teams[name].colornum)
end
elseif vout then
minetest.chat_send_all("<INFO> Majority of players voted to end the game")
for k,v in pairs(ctf.players) do
ctf.set_text(k,"Game is not started yet\nWaiting for more players to begin")
end
end
minetest.chat_send_all("<INFO> The game is finished")
ctf.teams = {}
for k,v in pairs(ctf.players) do
v.team = nil
end
minetest.after(3,function()ctf.started = false ctf.ended = false end)
end
end