Added node/item scoreboard criteria

This commit is contained in:
ThePython 2024-05-05 19:24:40 -07:00
parent d714f9a8a5
commit 232534e598
10 changed files with 243 additions and 139 deletions

View File

@ -19,6 +19,7 @@ local api_files = {
"register",
"scoreboard",
"teams",
"scoreboard_criteria",
}
for _, file in ipairs(api_files) do
@ -59,22 +60,3 @@ end)
minetest.register_on_joinplayer(function(player)
better_commands.sidebars[player:get_player_name()] = {}
end)
minetest.register_on_item_pickup(function(itemstack, player)
for _, objective in pairs(better_commands.scoreboard.objectives) do
local score = objective.scores[player:get_player_name()]
if score then -- don't bother with other players' scores
if objective.criterion:sub(1, 10) == "picked_up." then
local criterion_item = objective.criterion:sub(11, -1)
local handled = better_commands.handle_alias(criterion_item)
if handled and handled == itemstack:get_name() then
score.score = score.score + 1
elseif criterion_item:sub(1, 6) == "group:" then
if minetest.get_item_group(itemstack:get_name(), criterion_item:sub(7, -1)) ~= 0 then
score.score = score.score + 1
end
end
end
end
end
end)

View File

@ -34,86 +34,10 @@ function better_commands.deal_damage(target, damage, reason, damage_immortal)
end
end
minetest.register_on_dieplayer(function(player, reason)
local player_name = player:get_player_name()
for _, def in pairs(better_commands.scoreboard.objectives) do
if def.criterion == "deathCount" then
if def.scores[player_name] then
def.scores[player_name].score = def.scores[player_name].score + 1
end
end
end
local killer
if reason._mcl_reason then
killer = reason._mcl_reason.source
else
killer = reason.object
end
if killer and killer:is_player() then
local player_name = player:get_player_name()
local killer_name = killer:get_player_name()
local player_team = better_commands.teams.players[player_name]
local killer_team = better_commands.teams.players[killer_name]
for _, def in pairs(better_commands.scoreboard.objectives) do
if def.criterion == "playerKillCount" or (player_team and def.criterion == "teamkill."..player_team) then
if def.scores[killer_name] then
def.scores[killer_name].score = def.scores[killer_name].score + 1
end
elseif killer_team and def.criterion == "killedByTeam."..killer_team then
if def.scores[player_name] then
def.scores[player_name].score = def.scores[player_name].score + 1
end
elseif def.criterion == "killed_by.player" then
if def.scores[player_name] then
def.scores[player_name].score = def.scores[player_name].score + 1
end
end
end
elseif killer then
local killer_type = killer:get_luaentity().name
for _, def in pairs(better_commands.scoreboard.objectives) do
local killed_by = def.criterion:match("^killed_by%.(.*)$")
if killed_by and (killer_type == killed_by or
(better_commands.entity_aliases[killer_type] and better_commands.entity_aliases[killer_type][killed_by])) then
if def.scores[player_name] then
def.scores[player_name].score = def.scores[player_name].score + 1
end
end
end
end
end)
-- Make sure players always die when /killed, also track hp
-- Make sure players always die when /killed
minetest.register_on_player_hpchange(function(player, hp_change, reason)
if reason.better_commands == "kill" then
return hp_change, true
end
local player_name = player:get_player_name()
for _, def in pairs(better_commands.scoreboard.objectives) do
if def.criterion == "health" then
if def.scores[player_name] then
minetest.after(0, function() def.scores[player_name].score = player:get_hp() end)
end
end
end
if hp_change < 0 then
local attacker
if reason._mcl_reason then
attacker = reason._mcl_reason.source
else
attacker = reason.object
end
if attacker and attacker:is_player() then
local player_name = player:get_player_name()
local attacker_name = attacker:get_player_name()
local player_team = better_commands.teams.players[player_name]
local attacker_team = better_commands.teams.players[attacker_name]
if player_team == attacker_team then
if better_commands.teams.teams[player_team].pvp == false then
return 0, true
end
end
end
end
return hp_change
end, true)

View File

@ -1,42 +1,6 @@
--local bc = better_commands
local S = minetest.get_translator(minetest.get_current_modname())
better_commands.criteria_patterns = {
"^killed_by%..*$", -- killed_by.<entity name>
"^teamkill%..*$", -- teamkill.<team name>
"^killedByTeam%..*$", -- killedByTeam.<team name>
"^picked_up%.[_%w]*:?[_%w]+$" -- picked_up.<itemstring>
--"^distanceTo%.%-?%d*%.?%d+,%-?%d*%.?%d+,%-?%d*%.?%d+$" -- distanceTo.<x>,<y>,<z>
}
better_commands.valid_criteria = {
dummy = true,
trigger = true,
deathCount = true,
playerKillCount = true,
health = true,
--xp = better_commands.mcl and true,
--level = better_commands.mcl and true,
--food = (better_commands.mcl or minetest.get_modpath("stamina") and true),
--air = true,
--armor = (better_commands.mcl or minetest.get_modpath("3d_armor") and true)
}
---Validates a criterion
---@param criterion string
---@return boolean
function better_commands.validate_criterion(criterion)
if better_commands.valid_criteria[criterion] then
return true
end
for _, pattern in ipairs(better_commands.criteria_patterns) do
if criterion:match(pattern) then
return true
end
end
return false
end
---Gets matching names in a scoreboard
---@param selector splitParam
---@param context contextTable

207
API/scoreboard_criteria.lua Normal file
View File

@ -0,0 +1,207 @@
local item_pattern = "[_%w]*:?[_%w]+"
better_commands.criteria_patterns = {
"^killed_by%..*$", -- killed_by.<entity name>
"^teamkill%..*$", -- teamkill.<team name>
"^killedByTeam%..*$", -- killedByTeam.<team name>
"^picked_up%.%*$", -- picked_up.*
"^picked_up%."..item_pattern.."$", -- picked_up.<itemstring>
"^mined%.%*$", -- mined.*
"^mined%."..item_pattern.."$", -- mined.<itemstring>
"^dug%.%*$", -- dug.*
"^dug%."..item_pattern.."$", -- dug.<itemstring>
"^placed%.%*$", -- placed.*
"^placed%."..item_pattern.."$", -- placed.<itemstring>
"^crafted%.%*$", -- crafted.*
"^crafted%."..item_pattern.."$", -- crafted.<itemstring>
--"^distanceTo%.%-?%d*%.?%d+,%-?%d*%.?%d+,%-?%d*%.?%d+$" -- distanceTo.<x>,<y>,<z>
}
better_commands.valid_criteria = {
dummy = true,
trigger = true,
deathCount = true,
playerKillCount = true,
health = true,
--xp = better_commands.mcl and true,
--level = better_commands.mcl and true,
--food = (better_commands.mcl or minetest.get_modpath("stamina") and true),
--air = true,
--armor = (better_commands.mcl or minetest.get_modpath("3d_armor") and true)
}
---Validates a criterion
---@param criterion string
---@return boolean
function better_commands.validate_criterion(criterion)
if better_commands.valid_criteria[criterion] then
return true
end
for _, pattern in ipairs(better_commands.criteria_patterns) do
if criterion:match(pattern) then
return true
end
end
return false
end
local function item_matches(item, criterion_item)
if criterion_item == "*" then
return true
end
item = better_commands.handle_alias(item)
if not item then return end
if criterion_item:sub(1, 6) == "group:" then
return minetest.get_item_group(item, criterion_item:sub(7, -1)) ~= 0
else
return better_commands.handle_alias(criterion_item) == item
end
end
if better_commands.settings.scoreboard_picked_up then
minetest.register_on_item_pickup(function(itemstack, player)
for _, objective in pairs(better_commands.scoreboard.objectives) do
local score = objective.scores[player:get_player_name()]
if not score then return end
if objective.criterion:sub(1, 10) ~= "picked_up." then return end
local criterion_item = objective.criterion:sub(11, -1)
if item_matches(itemstack, criterion_item) then
score.score = score.score + 1
end
end
end)
end
if better_commands.settings.scoreboard_mined then
minetest.register_on_dignode(function(pos, node, player)
for _, objective in pairs(better_commands.scoreboard.objectives) do
local score = objective.scores[player:get_player_name()]
if not score then return end
local offset
if objective.criterion:sub(1, 6) == "mined." then
offset = 6
elseif objective.criterion:sub(1, 4) == "dug." then
offset = 4
else
return
end
local criterion_item = objective.criterion:sub(offset + 1, -1)
if item_matches(node.name, criterion_item) then
score.score = score.score + 1
end
end
end)
end
if better_commands.settings.scoreboard_placed then
minetest.register_on_placenode(function(pos, node, player)
for _, objective in pairs(better_commands.scoreboard.objectives) do
local score = objective.scores[player:get_player_name()]
if not score then return end
if objective.criterion:sub(1, 7) ~= "placed." then return end
local criterion_item = objective.criterion:sub(8, -1)
if item_matches(node.name, criterion_item) then
score.score = score.score + 1
end
end
end)
end
if better_commands.settings.scoreboard_crafted then
minetest.register_on_craft(function(itemstack, player)
for _, objective in pairs(better_commands.scoreboard.objectives) do
local score = objective.scores[player:get_player_name()]
if not score then return end
if objective.criterion:sub(1, 8) ~= "crafted." then return end
local criterion_item = objective.criterion:sub(9, -1)
if item_matches(itemstack, criterion_item) then
score.score = score.score + 1
end
end
end)
end
-- Tracks "health" objectives, also prevents damage if team PVP seting is on
minetest.register_on_player_hpchange(function(player, hp_change, reason)
local player_name = player:get_player_name()
if better_commands.settings.scoreboard_health then
for _, def in pairs(better_commands.scoreboard.objectives) do
if def.criterion == "health" then
if def.scores[player_name] then
-- update *after* hp changed
minetest.after(0, function() def.scores[player_name].score = player:get_hp() end)
end
end
end
end
if hp_change < 0 then
local attacker
if reason._mcl_reason then
attacker = reason._mcl_reason.source
else
attacker = reason.object
end
if attacker and attacker:is_player() then
local attacker_name = attacker:get_player_name()
local player_team = better_commands.teams.players[player_name]
local attacker_team = better_commands.teams.players[attacker_name]
if player_team and player_team == attacker_team then
if better_commands.teams.teams[player_team].pvp == false then
return 0, true
end
end
end
end
return hp_change
end)
if better_commands.settings.scoreboard_death then
minetest.register_on_dieplayer(function(player, reason)
local player_name = player:get_player_name()
for _, def in pairs(better_commands.scoreboard.objectives) do
if def.criterion == "deathCount" then
if def.scores[player_name] then
def.scores[player_name].score = def.scores[player_name].score + 1
end
end
end
local killer
if reason._mcl_reason then
killer = reason._mcl_reason.source
else
killer = reason.object
end
if killer and killer:is_player() then
local player_name = player:get_player_name()
local killer_name = killer:get_player_name()
local player_team = better_commands.teams.players[player_name]
local killer_team = better_commands.teams.players[killer_name]
for _, def in pairs(better_commands.scoreboard.objectives) do
if def.criterion == "playerKillCount" or (player_team and def.criterion == "teamkill."..player_team) then
if def.scores[killer_name] then
def.scores[killer_name].score = def.scores[killer_name].score + 1
end
elseif killer_team and def.criterion == "killedByTeam."..killer_team then
if def.scores[player_name] then
def.scores[player_name].score = def.scores[player_name].score + 1
end
elseif def.criterion == "killed_by.player" then
if def.scores[player_name] then
def.scores[player_name].score = def.scores[player_name].score + 1
end
end
end
elseif killer then
local killer_type = killer:get_luaentity().name
for _, def in pairs(better_commands.scoreboard.objectives) do
local killed_by = def.criterion:match("^killed_by%.(.*)$")
if killed_by and (killer_type == killed_by or
(better_commands.entity_aliases[killer_type] and better_commands.entity_aliases[killer_type][killed_by])) then
if def.scores[player_name] then
def.scores[player_name].score = def.scores[player_name].score + 1
end
end
end
end
end)
end

View File

@ -10,5 +10,9 @@ Initial release. Missing *lots* of commands, several `execute` subcommands, lots
* Removed debug logging when using the `/kill` command
## v2.0
* Added `picked_up.<itemstring>` scoreboard criterion (supports groups!)
*
* Added node/item scoreboard criteria (support itemstrings, `group:groupname` and `\*`)
* `picked_up.<itemstring>`
* `mined.<itemstring>`
* `dug.<itemstring>` (same as `mined`)
* `placed.<itemstring>`
* `crafted.<itemstring>`

View File

@ -40,7 +40,7 @@ better_commands.register_command("scoreboard", {
if not better_commands.validate_criterion(criterion) then
return false, S("Invalid criterion @1", criterion), 0
end
local display_name = (split_param[5] and split_param[5][3]) or objective_name
local display_name = (split_param[5] and param:sub(split_param[5][1], -1)) or objective_name
better_commands.scoreboard.objectives[objective_name] = {
name = objective_name,
criterion = criterion,

View File

@ -11,8 +11,9 @@ A place for me to write out my future plans. Also, I can just copy/paste into a
- [ ] `armor` (MCLA/VL/3D Armor)
- [x] `trigger`
- [x] `picked_up.<itemstring>`
- [ ] `mined.<itemstring>`
- [ ] `crafted.<itemstring>`
- [x] `mined.<itemstring>` (AKA `dug`)
- [x] `placed.<itemstring>`
- [x] `crafted.<itemstring>`
- [ ] `total_world_time`
- [ ] `leave_game`
- [ ] Advancements/awards?

View File

@ -3,8 +3,8 @@ better_commands = {commands = {}, old_commands = {}, players = {}}
better_commands.mcl = minetest.get_modpath("mcl_core")
local modpath = minetest.get_modpath("better_commands")
dofile(modpath.."/entity_aliases.lua")
dofile(modpath.."/settings.lua")
dofile(modpath.."/entity_aliases.lua")
dofile(modpath.."/API/api.lua")
dofile(modpath.."/COMMANDS/commands.lua")

View File

@ -20,6 +20,13 @@ local settings = {
{"acovg_time", false, "bool"},
{"save_interval", 3, "number"},
{"kill_creative_players", false, "bool"},
{"scoreboard_picked_up", true, "bool"},
{"scoreboard_mined", true, "bool"},
{"scoreboard_placed", true, "bool"},
{"scoreboard_crafted", true, "bool"},
{"scoreboard_health", true, "bool"},
{"scoreboard_death", true, "bool"},
}
for _, setting in ipairs(settings) do

View File

@ -9,3 +9,18 @@ better_commands_kill_creative_players (Kill creative players?) bool false
# Frequency of saving scoreboard/team data and updating sidebar (seconds)
better_commands_save_interval (Save interval) float 3
# Check for items picked up by players for scoreboard objectives? If false, "picked_up" scoreboard objectives will not update.
better_commands_scoreboard_picked_up (Update "picked_up" objectives?) bool true
# Check for nodes dug by players for scoreboard objectives? If false, "dug" and "broken" scoreboard objectives will not update.
better_commands_scoreboard_mined (Update "mined"/"dug" objectives?) bool true
# Check for nodes placed by players for scoreboard objectives? If false, "placed" scoreboard objectives will not update.
better_commands_scoreboard_placed (Update "placed" objectives?) bool true
# Check for player HP changes for scoreboard objectives? If false, "health" scoreboard objectives will not update.
better_commands_scoreboard_health (Update "health" objectives?) bool true
# Check player deaths for scoreboard objectives? If false, "deathCount", "teamKill", "playerKillCount", and "killedByTeam" scoreboard objectives will not update.
better_commands_scoreboard_death (Update kill/death objectives?) bool true