Compare commits
6 commits
282128382d
...
96d2be9ebd
Author | SHA1 | Date | |
---|---|---|---|
|
96d2be9ebd | ||
|
9712c7999c | ||
|
19ab4c4a70 | ||
|
038dd3c1e5 | ||
|
e2f2ba9fae | ||
|
055be8c9e5 |
7 changed files with 156 additions and 100 deletions
|
@ -91,11 +91,14 @@ end
|
|||
local function set_max_hp(player, max_hp)
|
||||
local cur_hp = player:get_hp()
|
||||
local old_max = player:get_properties().hp_max
|
||||
local new_hp = cur_hp + max_hp - old_max
|
||||
player:set_properties({
|
||||
hp_max = max_hp
|
||||
})
|
||||
|
||||
if old_max == 0 then
|
||||
minetest.log("error", "[ctf_classes] Reviving dead player " .. player:get_player_name())
|
||||
end
|
||||
|
||||
player:set_properties({hp_max = max_hp})
|
||||
|
||||
local new_hp = cur_hp + max_hp - old_max
|
||||
if new_hp > max_hp then
|
||||
minetest.log("error", string.format("New hp %d is larger than new max %d, old max is %d", new_hp, max_hp, old_max))
|
||||
new_hp = max_hp
|
||||
|
|
|
@ -10,8 +10,10 @@ local items = {
|
|||
"* Use medkits to replenish health gradually.",
|
||||
"* Gain more score by killing more than you die, or by capturing the flag.",
|
||||
"* Players are immune for 5 seconds after they respawn.",
|
||||
"* Access the pro section of the chest by achieving 2k+ score,",
|
||||
" killing 3 people for every 2 deaths, and capturing the flag at least 10 times",
|
||||
"* Access the pro section of the chest by achieving 10k+ score and either",
|
||||
" killing 3 people for every 2 deaths and capturing the flag at least 10 times",
|
||||
" or killing as many people as you die, capturing the flag on every 3rd attempt",
|
||||
" and at least 30 times.",
|
||||
"",
|
||||
|
||||
color .. "Team Co-op",
|
||||
|
@ -30,12 +32,12 @@ local items = {
|
|||
|
||||
color .. "Contact Moderators",
|
||||
"",
|
||||
"* Report people using /report or the #reports channel in Discord",
|
||||
"* Report people using /report.",
|
||||
"",
|
||||
|
||||
color .. "Other",
|
||||
"",
|
||||
"* Capture The Flag Discord: https://discord.gg/vcZTRPX",
|
||||
"* Capture The Flag Discord (not related to this server): https://discord.gg/vcZTRPX",
|
||||
}
|
||||
for i = 1, #items do
|
||||
items[i] = minetest.formspec_escape(items[i])
|
||||
|
|
|
@ -1,88 +1,84 @@
|
|||
# CTF Map - Map maker
|
||||
# CTF map-maker mod
|
||||
|
||||
## Creating a new map
|
||||
## Making a new map
|
||||
|
||||
### Youtube tutorial
|
||||
### Youtube Tutorial
|
||||
https://youtu.be/orBsC9wViUw
|
||||
|
||||
### Dependencies
|
||||
- Minetest 5.0.0 or later (https://minetest.net/)
|
||||
- Minetest Game (https://github.com/minetest/minetest_game/) (CTF supports most MTG nodes)
|
||||
- `ctf_map` modpack (copy this folder to `minetest/mods`)
|
||||
- `worldedit` modpack (WE) (https://content.minetest.net/packages/sfan5/worldedit/)
|
||||
|
||||
### 1. Dependencies
|
||||
### Find an area
|
||||
|
||||
* Minetest 5.0.0 or later.
|
||||
* `ctf_map` modpack (by copying the folder from this game to `minetest/mods`)
|
||||
* `worldedit` and `worldedit_commands`.
|
||||
- The area can be maximum 230x230 blocks in surface area, but it can be lesser.
|
||||
- Modify the area to *your* unique ctf_map
|
||||
- you could add
|
||||
- buildings
|
||||
- lakes
|
||||
- hills
|
||||
- etc.
|
||||
- If you haven't modified the map at all, do the following to speed up barrier placement:
|
||||
|
||||
### 2. Find an area
|
||||
- Stop Minetest.
|
||||
- Open up the world's world.mt
|
||||
- Set backend to "dummy".
|
||||
- Save.
|
||||
|
||||
* 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 be lesser.
|
||||
* Feel free to modify the area to your needs.
|
||||
### The `gui`window
|
||||
|
||||
### 3. Select the area
|
||||
![gui-window](./gui.png)
|
||||
|
||||
There are multiple ways do this, this is the simplest in most cases.
|
||||
There are many ways of placing the barrier:
|
||||
|
||||
* 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 `Player pos` then `From WE` and then `To WE`.
|
||||
* Check that the center location is the right place for the barrier to go.
|
||||
* Check that the bounds extend far enough.
|
||||
- Go to the center of the map and click on `Player Pos` and then on `To WE`
|
||||
- set a radius and a height for the map
|
||||
- **Or** select the area of the map via WE
|
||||
- Go to one corner of the map and type `//pos 1` in the chat
|
||||
- Then go to the opposite corner of the cube and type `//pos 2` in the chat
|
||||
- Click on `From WE` to import the positions
|
||||
- **If `h` is negative change it to the positive number** (`-130 -> 130`)
|
||||
- **Both radii must be the same!**
|
||||
- The rotation of the map has to be `z=0` (currently x=0 creates bugs and errors)
|
||||
- Click on `Place Barriers` (Note that this has no undo)
|
||||
- After the barriers are placed, click on `Givme Flags` to get 2 flags and place them at the bases.
|
||||
|
||||
### 4. Place barriers
|
||||
### Meta Data
|
||||
|
||||
* The barrier is a plane defined by co-ordinate (=0).
|
||||
* If you choose `X=0` the barrier will be placed having the X co-ordinate as 0. But from a few months, the `X=0` co-ordinate creates bugs and errors. It's better if you choose `Z=0` for creating your map.
|
||||
* If you choose `Z=0` The barrier will be placed having the Z co-ordinate as 0.
|
||||
* Click "place barrier". Note that this command does not have an undo.
|
||||
* After placing barriers you should place 2 flags where you want bases to be. You get flags in `/gui` --> `Giveme flags`
|
||||
The `gui`window only shows the most important things. You have to add the missing in the `map.conf` later.
|
||||
|
||||
### 5. Meta data
|
||||
### Exporting
|
||||
|
||||
* Set the meta data
|
||||
- Click on `Export` to export the map-files. This may takes some time
|
||||
|
||||
### 6. Export
|
||||
## Map Meta
|
||||
|
||||
* Click export, and wait until completion.
|
||||
* Copy the resultant folder from `worlddir/schems/` into `games/capturetheflag/mods/ctf/ctf_map/ctf_map_core/maps/`.
|
||||
* Profit!
|
||||
The metadata of each map are stored in the `map.conf` file and includes all important information about them:
|
||||
- `name`: Name of the map.
|
||||
- `author`: Author of the map.
|
||||
- `hint` [Optional]: A helpful tip for players to understand unique maps.
|
||||
- `roation`: The rotation of the map. [x|y]
|
||||
- `r`: Radius of the map.
|
||||
- `h`: Heigt of the map (**If it's an odd numer, make h=h+1 `107->108`**).
|
||||
- `team.i`: Name of the team.
|
||||
- `team.i.color`: Color of the team.
|
||||
- `team.i.pos`: Position of team `i`'s flag, relative ot the center of schem. **The y-positions of the flags must be an integer!** `30,-32.5,60 -> 30,-33,60`
|
||||
- `chest.i.from` and `chests.i.to` [Optional]: Positions of diagonal corners of custom chest zone `i`, relative to the center of the schem.
|
||||
- `chests.i.n` [Optional]: Number if chests in zone `i`
|
||||
- `license`: Name of license of the map.
|
||||
- `other` [Optional]: Additional information about the map. This is displayed in the maps catalog.
|
||||
- `base_node` [Optional]: String of the node around the flags.
|
||||
- `initial_stuff` [Optional]: Comma-separated list of itemstacks to be given to the player on join and on respawn.
|
||||
- `treasures` [Optional]: List of treasures to be registered for the map, in a serialized format. Refer to the `treasures` sub-section for more details.
|
||||
- `start_time` [Optional]: Time of day when the match starts. Default to `0.4` [`0 - 1`].
|
||||
- `time_speed` [Optional]: Time speed multiplier. Accepts any valid number. Defaults to 1.
|
||||
- `phys_speed` [Optional]: Player speed multiplier. Accepts any valid number. Defaults to 1.
|
||||
- `phys_jump` [Optional]: Player jump multiplier. Accepts any valid number. Defaults to 1.
|
||||
- `phys_gravity` [Optional]: Player gravity multiplier. Accepts any valid number. Defaults to 1.
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
### Map meta
|
||||
|
||||
Each map's metadata is stored in an accompanying `map.conf` file containing the following data:
|
||||
|
||||
* `name`: Name of map.
|
||||
* `author`: Author of the map.
|
||||
* `hint`: [Optional] Helpful hint or tip for unique maps, to help players understand the map.
|
||||
* `rotation`: Rotation of the schem. [`x`|`z`]
|
||||
* `r`: Radius of the map.
|
||||
* `h`: Height of the map.
|
||||
* `team.i`: Name of team `i`.
|
||||
* `team.i.color`: Color of team `i`.
|
||||
* `team.i.pos`: Position of team `i`'s flag, relative to center of schem.
|
||||
* `chests.i.from`, `chests.i.to`: [Optional] Positions of diagonal corners of custom chest
|
||||
zone `i`, relative to the center of the schem.
|
||||
* `chests.i.n`: [Optional] Number of chests to place in custom chest zone `i`.
|
||||
* `license`: Name of the license of the map.
|
||||
* `other`: [Optional] Misc. information about the map. This is displayed in the maps catalog.
|
||||
* `base_node`: [Optional] Technical name of node to be used for the team base.
|
||||
* `initial_stuff`: [Optional] Comma-separated list of itemstacks to be given to the player
|
||||
on join and on respawn.
|
||||
* `treasures`: [Optional] List of treasures to be registered for the map, in a serialized
|
||||
format. Refer to the `treasures` sub-section for more details.
|
||||
* `start_time`: [Optional] Time at start of match. Defaults to `0.4` [`0` - `1`].
|
||||
* `time_speed`: [Optional] Time speed multiplier. Accepts any valid number. Defaults to 1.
|
||||
* `phys_speed`: [Optional] Player speed multiplier. Accepts any valid number. Defaults to 1.
|
||||
* `phys_jump`: [Optional] Player jump multiplier. Accepts any valid number. Defaults to 1.
|
||||
* `phys_gravity`: [Optional] Player gravity multiplier. Accepts any valid number. Defaults to 1.
|
||||
|
||||
#### `license`
|
||||
|
||||
* Every map must have its own license. Once you've chosen your license, simply add the following line to the `map.conf` file:
|
||||
|
|
BIN
mods/ctf/ctf_map/map_maker/gui.png
Normal file
BIN
mods/ctf/ctf_map/map_maker/gui.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
|
@ -37,13 +37,18 @@ minetest.register_on_mods_loaded(function()
|
|||
table.insert(minetest.registered_on_respawnplayers, 1, function(player)
|
||||
local pname = player:get_player_name()
|
||||
|
||||
if ctf_respawn_delay.players[pname] and ctf_respawn_delay.players[pname].timeleft == "waiting" then
|
||||
if ctf_respawn_delay.players[pname] then
|
||||
-- Since the player is still dead the client can send respawn actions
|
||||
-- https://github.com/minetest/minetest/blob/4152227f17315a9cf9038266d9f9bb06e21e3424/src/network/serverpackethandler.cpp#L895
|
||||
-- We should ignore those
|
||||
if ctf_respawn_delay.players[pname].timeleft == "waiting" then
|
||||
ctf_respawn_delay.players[pname].timeleft = RESPAWN_DELAY
|
||||
local pos = player:get_pos()
|
||||
pos.y = ctf_map.map.h/2 + 10
|
||||
|
||||
player:set_pos(pos) -- Player will be stuck there because CTF 'air' is walkable
|
||||
minetest.after(RESPAWN_INTERVAL, respawnfunc, pname)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
@ -56,15 +61,10 @@ minetest.register_on_mods_loaded(function()
|
|||
end)
|
||||
end)
|
||||
|
||||
function ctf_respawn_delay.respawnplayer(name)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
|
||||
if not player then return end
|
||||
|
||||
player:hud_remove(ctf_respawn_delay.players[name].hudid)
|
||||
player:set_properties({hp_max = ctf_respawn_delay.players[name].old_max})
|
||||
player:set_hp(ctf_respawn_delay.players[name].old_max)
|
||||
ctf_respawn_delay.players[name] = nil
|
||||
function respawnplayer(player, pname)
|
||||
player:hud_remove(ctf_respawn_delay.players[pname].hudid)
|
||||
player:set_properties({hp_max = ctf_respawn_delay.players[pname].old_max})
|
||||
player:set_hp(ctf_respawn_delay.players[pname].old_max)
|
||||
|
||||
for k, func in ipairs(ctf_respawn_delay.registered_on_respawnplayers) do
|
||||
func(player)
|
||||
|
@ -72,16 +72,13 @@ function ctf_respawn_delay.respawnplayer(name)
|
|||
end
|
||||
|
||||
function respawnfunc(pname)
|
||||
local player = minetest.get_player_by_name(pname)
|
||||
|
||||
if not player or not ctf_respawn_delay.players[pname] then
|
||||
ctf_respawn_delay.players[pname] = nil
|
||||
if not ctf_respawn_delay.players[pname] then
|
||||
return
|
||||
end
|
||||
|
||||
if type(ctf_respawn_delay.players[pname].timeleft) == "string" then
|
||||
minetest.after(RESPAWN_INTERVAL, respawnfunc, pname)
|
||||
|
||||
local player = minetest.get_player_by_name(pname)
|
||||
if not player then
|
||||
ctf_respawn_delay.players[pname] = nil
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -93,12 +90,26 @@ function respawnfunc(pname)
|
|||
|
||||
minetest.after(RESPAWN_INTERVAL, respawnfunc, pname)
|
||||
else
|
||||
ctf_respawn_delay.respawnplayer(pname)
|
||||
respawnplayer(player, pname)
|
||||
ctf_respawn_delay.players[pname] = nil
|
||||
end
|
||||
end
|
||||
|
||||
ctf_match.register_on_new_match(function()
|
||||
for name in pairs(ctf_respawn_delay.players) do
|
||||
ctf_respawn_delay.respawnplayer(name)
|
||||
for pname in pairs(ctf_respawn_delay.players) do
|
||||
local player = minetest.get_player_by_name(pname)
|
||||
if player then
|
||||
respawnplayer(player, pname)
|
||||
end
|
||||
end
|
||||
|
||||
ctf_respawn_delay.players = {}
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
local pname = player:get_player_name()
|
||||
if ctf_respawn_delay.players[pname] then
|
||||
player:set_properties({hp_max = ctf_respawn_delay.players[pname].old_max})
|
||||
ctf_respawn_delay.players[pname] = nil
|
||||
end
|
||||
end)
|
||||
|
|
|
@ -259,3 +259,48 @@ minetest.register_chatcommand("makepro", {
|
|||
end
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("makecapturepro", {
|
||||
params = "[player_name]",
|
||||
description = "Make player a 'capture pro'",
|
||||
privs = {ctf_admin = true},
|
||||
func = function(name, param)
|
||||
-- Check if param is specified, else target the caller
|
||||
param = param:trim()
|
||||
if param == "" then
|
||||
param = name
|
||||
end
|
||||
|
||||
local modified = false
|
||||
local stats = ctf_stats.player(param)
|
||||
|
||||
local deaths = math.max(stats.deaths, 1)
|
||||
if stats.kills < 1.0 * deaths then
|
||||
stats.kills = math.ceil(1.01 * deaths)
|
||||
modified = true
|
||||
end
|
||||
|
||||
if stats.score < 10000 then
|
||||
stats.score = 10000
|
||||
modified = true
|
||||
end
|
||||
|
||||
if stats.captures < 30 then
|
||||
stats.captures = 30
|
||||
modified = true
|
||||
end
|
||||
|
||||
local attempts = math.max(stats.attempts, 1)
|
||||
if stats.captures < 0.33 * attempts then
|
||||
stats.captures = math.ceil(0.34 * attempts)
|
||||
modified = true
|
||||
end
|
||||
|
||||
if modified then
|
||||
ctf_stats.request_save()
|
||||
return true, "Made " .. param .. " a pro!"
|
||||
else
|
||||
return false, param .. " is already a pro!"
|
||||
end
|
||||
end
|
||||
})
|
||||
|
|
|
@ -578,7 +578,6 @@ minetest.register_node("default:clay", {
|
|||
description = "Clay",
|
||||
tiles = {"default_clay.png"},
|
||||
groups = {crumbly = 3},
|
||||
drop = 'default:clay_lump 4',
|
||||
sounds = default.node_sound_dirt_defaults(),
|
||||
})
|
||||
|
||||
|
|
Loading…
Reference in a new issue