Large update (#11)

* Add a lot of new stuff

* Add even more new stuff

* Fix /start...

* Revert "Fix /start..."

This reverts commit 00b55b666a.

* Fix syntax error.. (how did this get here)

* Fix countdown persisting after aborting

* Add xpanes for the snow map

* Change the 'your' in the loading tips

why english why -_-

Co-authored-by: CrazyladMT <247920740+CrazyladMT@users.noreply.github.com>

* Another loading tips change...

Co-authored-by: CrazyladMT <247920740+CrazyladMT@users.noreply.github.com>

* Fix '/stop' crashing...

* Fix another crash with /stop...

* Update mods/game/functions/init.lua

Co-authored-by: CrazyladMT <247920740+CrazyladMT@users.noreply.github.com>

* Remove snow map for now

* Remove a loading tip...

* Rename /list_maps to /maps

* Update map making README

* Hopefully fix whitespaces...

---------

Co-authored-by: CrazyladMT <247920740+CrazyladMT@users.noreply.github.com>
This commit is contained in:
IonicCheese 2026-02-19 20:41:32 -08:00 committed by GitHub
commit 46ce669100
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
48 changed files with 677 additions and 188 deletions

View file

@ -28,7 +28,7 @@ core.register_chatcommand("stop", {
privs = {match_manager = true},
description = "Terminate the match",
func = function()
if match_state ~= "pre_match" and match_state ~= "post_match" and match_state ~= "not_started" then
if match_state ~= "post_match" and match_state ~= "not_started" then
core.chat_send_all(core.colorize("red", "Match terminated."))
end_match()
@ -38,3 +38,16 @@ core.register_chatcommand("stop", {
return false, "Match cannot be terminated at the moment."
end
})
core.register_chatcommand("maps", {
params = "",
privs = {match_manager = true},
description = "List all maps",
func = function()
local list_string = "Available maps:\n"
for _, map in pairs(map_list) do
list_string = list_string .. map .. "\n"
end
return true, list_string .. "\nUse /start <map> to start a match."
end
})

View file

@ -1,3 +1,17 @@
match_start_jobs = {}
loading_tips = {
"Open the inventory to change class!",
"Short-range is good for small/dense maps!",
"Mid-range is good on maps with long tunnels or open areas!",
"Long-range is good for maps with really open and large areas!",
"Ambushing can be powerful assuming you're not found!",
"Players may sneak up on you from behind!",
"You *can't* be shot under water!",
"The Short-range class can one-shot from 6 to 7 nodes away!",
"Using torches may allow enemies to sneak up on you in the dark!",
"e s p i o n a g e",
}
-- Functions for SSG
function make_player_invisible(player) -- Hide a player (pre-match and spectator)
save_player_data(player)
@ -116,52 +130,68 @@ function start_match(map) -- Start the match
return
end
map_data = place_map(map or "forest") -- default to forest if no map is specified
place_map(map or "forest") -- default to forest if no map is specified
if not map_data then
return nil
end
set_match_state("pre_match")
local map_loading_images = {}
for _, player in pairs(core.get_connected_players()) do
set_player_mode(player, "pre_match")
map_loading_images[player:get_player_name()] = player:hud_add({
type = "image",
position = {x=0.5, y=0.5},
image_scale = 100,
text = "map_loading.png",
scale = {x=-100, y=-100},
z_index = 1000,
})
map_loading_images[player:get_player_name()] = {
loading = player:hud_add({
type = "image",
position = {x=0.5, y=0.5},
image_scale = 100,
text = "map_loading.png",
scale = {x=-100, y=-100},
z_index = 1000,
}),
info = player:hud_add({
type = "text",
position = {x=0.5, y=0.7},
text = loading_tips[math.random(1, #loading_tips)],
number = 0xFFFFFF,
z_index = 1000,
})
}
give_player_items(player)
player:set_pos({x = map_data.spawn_x, y = map_data.spawn_y, z = map_data.spawn_z})
player:set_pos(map_data.spawn)
player:set_hp(20)
end
core.after(3, function()
set_match_state("pre_match")
for _, player in pairs(core.get_connected_players()) do
player:set_pos({x = map_data.spawn_x, y = map_data.spawn_y, z = map_data.spawn_z})
player:hud_remove(map_loading_images[player:get_player_name()])
player:set_pos(map_data.spawn)
for _, id in pairs(map_loading_images[player:get_player_name()]) do
player:hud_remove(id)
end
end
assert(loadstring(map_data.scripts.on_start or ""))()
if map_data.on_start then
map_data.on_start()
end
core.chat_send_all(core.colorize("#b011f9", string.format("Match about to start in %d seconds!\nOpen inventory to change class!", map_data.start_time)))
match_start_jobs = {countdown = {}, map = nil}
for i = 10, 1, -1 do -- count down from 10 to 1 (yes you are free to set me on fire for this horrible solution)
core.after(map_data.start_time - 10 + i, function()
table.insert(match_start_jobs.countdown, core.after(map_data.start_time - 10 + i, function()
core.chat_send_all(core.colorize("green", string.format("Match starts in %d second%s.", 11 - i, 11 - i == 1 and "" or "s"))) -- <- RIP readability
end)
end))
end
core.after(map_data.start_time, function()
match_start_jobs.map = core.after(map_data.start_time, function()
match_start_jobs = nil
set_match_state("in_progress")
core.chat_send_all(core.colorize("green", "Match started!"))
@ -191,6 +221,20 @@ end
function end_match() -- End the match
set_match_state("not_started")
if match_start_jobs then
match_start_jobs.map:cancel()
for _, job in pairs(match_start_jobs.countdown) do
job:cancel()
end
match_start_jobs = nil
end
if map_data.on_end then
map_data.on_end()
end
for _, player in pairs(core.get_connected_players()) do
player:set_pos(spawn_pos)
player:get_inventory():set_list("main", {})
@ -251,8 +295,6 @@ function kill_player(player, reason) -- Handle killed/disconnected players prope
local winner_name = alive_player_names[1]
core.chat_send_all(core.colorize("green", winner_name .. " is the winner!"))
assert(loadstring(map_data.scripts.on_end or ""))()
set_match_state("post_match")
core.after(5, end_match)

View file

@ -76,7 +76,7 @@ core.register_on_respawnplayer(function(player)
if match_state == "in_progress" or match_state == "post_match" then
set_player_mode(player, "spectator")
player:set_pos({x = map_data.spawn_x, y = map_data.spawn_y, z = map_data.spawn_z})
player:set_pos(map_data.spawn)
player:get_inventory():set_list("main", {})
player:set_properties({pointable = false})

View file

@ -34,23 +34,17 @@ Step 8: Open the `map.lua` file in a text editor and put the following content i
```lua
local map_data = {
name = "(Your map name here)",
size_x = (Size in the X direction of your map),
size_y = (Size in the Y direction of your map),
size_z = (Size in the Z direction of your map),
size = vector.new(Your map size)
barrier_level = (Distance from the bottom of the map to the barrier),
spawn_x = nil,
spawn_y = nil,
spawn_z = nil,
spawn = vector.new(Your spawn coordinates relative to map position, can be set to nil)
start_time = (Amount of time in seconds before the barrier is removed),
scripts = {
on_start = "(Lua script to be run after /start is run, leave blank unless you know what you are doing!)",
on_barrier_remove = "(Lua script to be run after the barrier is removed, leave blank unless you know what you are doing!)",
on_end = "(Lua script to be run after the match has ended, leave blank unless you know what you are doing!)"
},
on_start = function(),
on_end = function(),
on_barrier_remove = function(),
classes = {
class_1 = {
@ -69,8 +63,6 @@ local map_data = {
}
}
}
return map_data
```
Step 9: Open Minetest/Luanti and create a new world with Simple Shooter Game.

View file

@ -1,23 +1,31 @@
-- Maps mod for SSG
local map_path = core.get_modpath("maps") .. "/maps/"
map_data = {}
map_list = core.get_dir_list(map_path, true)
table.sort(map_list)
function place_map(map)
local map_path = core.get_modpath("maps") .. "/maps/"
local map_list = core.get_dir_list(map_path, true)
local map_pos = vector.new(0, 0, 0)
for i = 1, #map_list do
if map_list[i] == map then
map_pos = vector.new(1000 * (i - 1), 0, 0)
break
elseif i == #map_list then
return nil
end
end
local map_data = dofile(map_path .. map .. "/map.lua")
core.place_schematic({x=0, y=0, z=0}, map_path .. map .. "/map.mts", 0, nil, true)
if map_data.spawn_x == nil or map_data.spawn_y == nil or map_data.spawn_z == nil then -- set a default spawnpoint if not set
map_data.spawn_x = map_data.size_x / 2
map_data.spawn_y = map_data.barrier_level + 1
map_data.spawn_z = map_data.size_z / 2
map_data = dofile(map_path .. map .. "/map.lua")
map_data.pos = map_pos
core.place_schematic(map_pos, map_path .. map .. "/map.mts", 0, nil, true)
if not map_data.spawn then -- set a default spawnpoint if not set
map_data.spawn = vector.new(map_data.size.x / 2, map_data.barrier_level + 1, map_data.size.z / 2) + map_pos
else
map_data.spawn = map_data.spawn + map_pos
end
if map_data.start_time == nil or map_data.start_time <= 0 then
@ -45,8 +53,6 @@ function place_map(map)
map_data.classes.class_3.initial_items = {"ctf_ranged:benelli_loaded", "ctf_ranged:glock17_loaded", "ctf_ranged:ammo 99"}
map_data.classes.class_3.name = "Short-range"
end
return map_data
end
function remove_barrier()
@ -54,6 +60,8 @@ function remove_barrier()
local pos = player:get_pos()
player:set_pos({x=pos.x, y=map_data.barrier_level - 3.5, z=pos.z})
end
assert(loadstring(map_data.scripts.on_barrier_remove or ""))()
return ""
if map_data.on_barrier_remove then
map_data.on_barrier_remove()
end
end

View file

@ -1,22 +1,14 @@
local map_data = {
return {
name = "1v1",
size_x = 41,
size_y = 31,
size_z = 38,
size = vector.new(41, 31, 38),
barrier_level = 27,
spawn_x = nil,
spawn_y = nil,
spawn_z = nil,
spawn = nil,
start_time = 15,
scripts = {
on_start = "for x=0, 40 do\nfor y=0, 17 do\nfor z=0, 37 do\ncore.set_node({x=x,y=31+y,z=z}, {name=\"default:glass\"})\nend\nend\nend",
on_barrier_remove = "",
on_end = ""
}
on_start = nil,
on_end = nil,
on_barrier_remove = nil,
}
return map_data

View file

@ -1,22 +1,14 @@
local map_data = {
return {
name = "forest-2",
size_x = 189,
size_y = 71,
size_z = 102,
size = vector.new(189, 71, 102),
barrier_level = 67, -- <- Y level of the barrier
spawn_x = nil,
spawn_y = nil,
spawn_z = nil,
spawn = nil,
start_time = 45,
scripts = { -- "temporary" hack to ensure there's nothing on top of the map
on_start = "for x=0, 188 do\nfor y=0, 4 do\nfor z=0, 101 do\ncore.set_node({x=x,y=71+y,z=z}, {name=\"air\"})\nend\nend\nend",
on_barrier_remove = "",
on_end = ""
}
on_start = nil,
on_end = nil,
on_barrier_remove = nil,
}
return map_data

View file

@ -1,22 +1,14 @@
local map_data = {
return {
name = "forest-3",
size_x = 537,
size_y = 117,
size_z = 244,
size = vector.new(537, 117, 244),
barrier_level = 113, -- <- Y level of the barrier
spawn_x = nil,
spawn_y = nil,
spawn_z = nil,
spawn = nil,
start_time = 60,
scripts = {
on_start = "",
on_barrier_remove = "",
on_end = ""
}
on_start = nil,
on_end = nil,
on_barrier_remove = nil,
}
return map_data

View file

@ -1,22 +1,16 @@
local map_data = {
name = "forest-4",
size_x = 190,
size_y = 69,
size_z = 155,
return {
name = "forest-4",
size = vector.new(190, 69, 155),
barrier_level = 65,
spawn_x = nil,
spawn_y = nil,
spawn_z = nil,
spawn = nil,
start_time = 30,
scripts = {
on_start = "for x=0, 189 do\nfor y=0, 10 do\nfor z=0, 154 do\ncore.set_node({x=x,y=69+y,z=z}, {name=\"air\"})\nend\nend\nend",
on_barrier_remove = "",
on_end = ""
},
on_start = nil,
on_end = nil,
on_barrier_remove = nil,
classes = {
class_1 = {
@ -35,5 +29,3 @@ local map_data = {
}
}
}
return map_data

View file

@ -1,22 +1,16 @@
local map_data = {
return {
name = "forest",
size_x = 155,
size_y = 53,
size_z = 147,
size = vector.new(155, 53, 147),
barrier_level = 49, -- <- Y level of the barrier
spawn_x = nil,
spawn_y = nil,
spawn_z = nil,
spawn = nil,
start_time = 30,
scripts = { -- "temporary" hack to ensure there's nothing on top of the map
on_start = "for x=0, 154 do\nfor y=0, 16 do\nfor z=0, 146 do\ncore.set_node({x=x,y=53+y,z=z}, {name=\"air\"})\nend\nend\nend",
on_barrier_remove = "",
on_end = ""
},
on_start = nil,
on_end = nil,
on_barrier_remove = nil,
classes = {
class_1 = {
@ -35,5 +29,3 @@ local map_data = {
}
}
}
return map_data

View file

@ -1,22 +1,24 @@
local map_data = {
return {
name = "mini-map",
size_x = 8,
size_y = 19,
size_z = 8,
barrier_level = 15,
spawn_x = nil,
spawn_y = nil,
spawn_z = nil,
size = vector.new(8, 19, 8),
-- This is a ridiculous hack to prevent players from teleporting into the ground..
barrier_level = 19,
spawn = vector.new(4, 15, 4),
start_time = 15,
scripts = {
on_start = "",
on_barrier_remove = "",
on_end = ""
}
on_start = nil,
on_end = nil,
on_barrier_remove = function()
local pos = map_data.pos
local size = map_data.size + pos
for x = pos.x + 1, size.x - 2 do
for z = pos.z + 1, size.z - 2 do
core.set_node(vector.new(x, 14, z), {name = "air"})
end
end
end,
}
return map_data

View file

@ -1,22 +1,14 @@
local map_data = {
return {
name = "pine",
size_x = 111,
size_y = 64,
size_z = 107,
size = vector.new(111, 64, 107),
barrier_level = 60, -- <- Y level of the barrier
spawn_x = nil,
spawn_y = nil,
spawn_z = nil,
spawn = nil,
start_time = 30,
scripts = {
on_start = "",
on_barrier_remove = "",
on_end = ""
}
on_start = nil,
on_end = nil,
on_barrier_remove = nil,
}
return map_data

View file

@ -1,22 +1,14 @@
local map_data = {
return {
name = "savanna",
size_x = 341,
size_y = 83,
size_z = 188,
size = vector.new(341, 83, 188),
barrier_level = 79, -- <- Y level of the barrier
spawn_x = nil,
spawn_y = nil,
spawn_z = nil,
spawn = nil,
start_time = 45,
scripts = {
on_start = "",
on_barrier_remove = "",
on_end = ""
}
on_start = nil,
on_end = nil,
on_barrier_remove = nil,
}
return map_data