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

121
mods/other/afkkick/COPYING Normal file
View file

@ -0,0 +1,121 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

View file

@ -0,0 +1,23 @@
Afk Kick mod for Minetest by GunshipPenguin
Kicks players after they are Afk for an amount of time. By default,
players are kicked after five minutes, although this can be configured.
Licence: CC0 (see COPYING file)
This mod can be configured by changing the variables declared in the
start of init.lua. The following is a brief explanation of each one.
MAX_INACTIVE_TIME (default 300)
Maximum amount of time that a player may remain AFK (in seconds)
before being kicked.
CHECK_INTERVAL (default 1)
Time between checks for inactivity (In seconds)
WARN_TIME (default 20)
Number of seconds remaining before being kicked that a player will
start to be warned via chat message.

View file

@ -0,0 +1,72 @@
--[[
Afk Kick mod for Minetest by GunshipPenguin
To the extent possible under law, the author(s)
have dedicated all copyright and related and neighboring rights
to this software to the public domain worldwide. This software is
distributed without any warranty.
]]
local MAX_INACTIVE_TIME = 120
local CHECK_INTERVAL = 1
local WARN_TIME = 20
local players = {}
local checkTimer = 0
minetest.register_privilege("canafk")
minetest.register_on_joinplayer(function(player)
local playerName = player:get_player_name()
players[playerName] = {
lastAction = minetest.get_gametime()
}
end)
minetest.register_on_leaveplayer(function(player)
local playerName = player:get_player_name()
players[playerName] = nil
end)
minetest.register_on_chat_message(function(playerName, message)
players[playerName]["lastAction"] = minetest.get_gametime()
end)
minetest.register_globalstep(function(dtime)
local currGameTime = minetest.get_gametime()
--Check for inactivity once every CHECK_INTERVAL seconds
checkTimer = checkTimer + dtime
local checkNow = checkTimer >= CHECK_INTERVAL
if checkNow then
checkTimer = checkTimer - CHECK_INTERVAL
end
--Loop through each player in players
for playerName,_ in pairs(players) do
local player = minetest.get_player_by_name(playerName)
if player then
--Check if this player is doing an action
for _,keyPressed in pairs(player:get_player_control()) do
if keyPressed then
players[playerName]["lastAction"] = currGameTime
end
end
if checkNow and not minetest.check_player_privs(player, { canafk = true }) then
--Kick player if he/she has been inactive for longer than MAX_INACTIVE_TIME seconds
if players[playerName]["lastAction"] + MAX_INACTIVE_TIME < currGameTime then
minetest.kick_player(playerName, "Kicked for inactivity")
end
--Warn player if he/she has less than WARN_TIME seconds to move or be kicked
if players[playerName]["lastAction"] + MAX_INACTIVE_TIME - WARN_TIME < currGameTime then
minetest.chat_send_player(playerName, minetest.colorize("#FF8C00", "Warning, you have " ..
tostring(players[playerName]["lastAction"] + MAX_INACTIVE_TIME - currGameTime + 1) ..
" seconds to move or be kicked"))
end
end
end
end
end)

View file

@ -0,0 +1,24 @@
# Email
![screenshot](https://cdn.pbrd.co/images/8fn5I3u.png)
Created by rubenwardy.
License: WTFPL.
## Usage
Send an email:
* /mail username message to send
View inbox:
* /inbox
* /inbox text
No formspec commands (good for IRC):
* /mail username message to send
* /inbox text
* /inbox clear

View file

@ -0,0 +1,2 @@
chatplus?
sfinv?

View file

@ -0,0 +1 @@
Adds email style inboxes to Minetest.

44
mods/other/email/hud.lua Normal file
View file

@ -0,0 +1,44 @@
local hudkit = dofile(minetest.get_modpath("email") .. "/hudkit.lua")
email.hud = hudkit()
function email.update_hud(player)
local name = player:get_player_name()
local inbox = email.get_inbox(name)
if inbox and #inbox > 0 then
if email.hud:exists(player, "email:text") then
email.hud:change(player, "email:text", "text", #inbox .. " /inbox")
else
email.hud:add(player, "email:icon", {
hud_elem_type = "image",
name = "MailIcon",
position = {x=0.52, y=0.52},
text="email_mail.png",
scale = {x=1,y=1},
alignment = {x=0.5, y=0.5},
})
email.hud:add(player, "email:text", {
hud_elem_type = "text",
name = "MailText",
position = {x=0.55, y=0.52},
text= #inbox .. " /inbox",
scale = {x=1,y=1},
alignment = {x=0.5, y=0.5},
})
end
else
email.hud:remove(player, "email:icon")
email.hud:remove(player, "email:text")
end
end
minetest.register_on_leaveplayer(function(player)
email.hud.players[player:get_player_name()] = nil
end)
function email.update_all_hud()
local players = minetest.get_connected_players()
for _, player in pairs(players) do
email.update_hud(player)
end
minetest.after(5, email.update_all_hud)
end
minetest.after(5, email.update_all_hud)

View file

@ -0,0 +1,48 @@
-- HudKit, by rubenwardy
-- License: Either WTFPL or CC0, you can choose.
local function hudkit()
return {
players = {},
add = function(self, player, id, def)
local name = player:get_player_name()
local elements = self.players[name]
if not elements then
self.players[name] = {}
elements = self.players[name]
end
elements[id] = player:hud_add(def)
end,
exists = function(self, player, id)
local elements = self.players[player:get_player_name()]
return elements and elements[id]
end,
change = function(self, player, id, stat, value)
local elements = self.players[player:get_player_name()]
if not elements or not elements[id] then
return false
end
player:hud_change(elements[id], stat, value)
return true
end,
remove = function(self, player, id)
local elements = self.players[player:get_player_name()]
if not elements or not elements[id] then
return false
end
player:hud_remove(elements[id])
elements[id] = nil
return true
end
}
end
return hudkit

231
mods/other/email/init.lua Normal file
View file

@ -0,0 +1,231 @@
email = {
log = function(msg) end
}
local _loading = true
local send_as = {}
local forward_to = {}
local storage = minetest.get_mod_storage()
if minetest.global_exists("chatplus") then
email.log = chatplus.log
function chatplus.on_old_inbox(inbox)
if _loading then
return
end
email.inboxes = inbox
end
end
function email.init()
email.inboxes = {}
email.load()
_loading = false
if minetest.global_exists("chatplus") and chatplus.old_inbox then
chatplus.on_old_inbox(chatplus.old_inbox)
end
end
function email.load()
local file = io.open(minetest.get_worldpath() .. "/email.txt", "r")
if file then
local from_file = minetest.deserialize(file:read("*all"))
file:close()
if type(from_file) == "table" then
if from_file.mod == "email" and tonumber(from_file.ver_min) <= 1 then
email.inboxes = from_file.inboxes
else
error("[Email] Attempt to read incompatible email.txt file.")
end
end
end
forward_to = minetest.parse_json(storage:get_string("forward_to")) or {}
send_as = minetest.parse_json(storage:get_string("send_as")) or {}
end
function email.save()
local file = io.open(minetest.get_worldpath() .. "/email.txt", "w")
if file then
file:write(minetest.serialize({
mod = "email", version = 1, ver_min = 1,
inboxes = email.inboxes }))
file:close()
end
end
minetest.register_on_shutdown(email.save)
function email.get_inbox(name)
return email.inboxes[name] or {}
end
function email.clear_inbox(name)
email.inboxes[name] = nil
email.save()
end
minetest.register_on_joinplayer(function(player)
local inbox = email.get_inbox(player:get_player_name())
if #inbox > 0 then
minetest.after(10, function()
minetest.chat_send_player(player:get_player_name(),
minetest.colorize("#00FF00",
"(" .. #inbox .. ") You have mail! Type /inbox to recieve"))
end)
end
end)
function email.get_formspec(name)
local inbox = email.get_inbox(name)
local function row(fs, c1, date, from, msg)
date = minetest.formspec_escape(date)
from = minetest.formspec_escape(from)
msg = minetest.formspec_escape(msg)
return fs .. ",#d0d0d0," .. table.concat({date, c1, from, msg}, ",")
end
local fs = "vertlabel[0,0;Your Inbox]"
fs = fs .. "tablecolumns[color;text;color;text;text]"
fs = fs .. "tableoptions[highlight=#ffffff33]"
fs = fs .. "table[0.5,0;11.25,7;inbox;"
fs = fs .. "#ffffff,Date,,From,Message"
if #inbox == 0 then
fs = row(fs, "#d0d0d0", "", ":)", "Well done! Your inbox is empty!")
else
for i = 1, #inbox do
local color = "#ffffff"
if minetest.check_player_privs(inbox[i].from, {kick = true, ban = true}) then
color = "#FFD700"
end
local msg = inbox[i].msg
fs = row(fs, color, inbox[i].date, inbox[i].from, msg:sub(1, 44))
while #msg > 45 do
msg = msg:sub(45, #msg)
fs = row(fs, color, "", "", msg:sub(1, 44))
end
end
end
fs = fs .. "]"
fs = fs .. "button[0,7.25;2,1;clear;Delete All]"
--fs = fs .. "button[0,7.25;2,1;clear;Mark as read]"
fs = fs .. "button_exit[10.1,7.25;2,1;close;Close]"
fs = fs .. "label[2,7.4;Exit then type /mail username message to reply]"
return fs
end
function email.show_inbox(name, text_mode)
if text_mode then
local inbox = email.get_inbox(name)
if #inbox == 0 then
return true, "Your inbox is empty!"
else
minetest.chat_send_player(name, #inbox .. " items in your inbox:")
for i = 1, #inbox do
local item = inbox[i]
minetest.chat_send_player(name, i .. ") " ..item.date ..
" <" .. item.from .. "> " .. item.msg)
end
return true, "End of mail (" .. #inbox .. " items)"
end
else
local fs = "size[12,8]" .. email.get_formspec(name)
minetest.show_formspec(name, "email:inbox", fs)
return true, "Opened inbox!"
end
end
if minetest.global_exists("sfinv") then
sfinv.register_page("email:inbox", {
title = "Inbox",
get = function(self, player, context)
local name = player:get_player_name()
return sfinv.make_formspec(player, context, email.get_formspec(name), false, "size[12,8]")
end
})
end
minetest.register_on_player_receive_fields(function(player,formname,fields)
if fields.clear then
local name = player:get_player_name()
email.clear_inbox(name)
minetest.chat_send_player(name, "Inbox cleared!")
email.show_inbox(name)
end
--[[if fields.mark_all_read then
if player and player.inbox and #player.inbox > 0 then
for i = 1, #player.inbox do
player.inbox[i].read = true
end
minetest.chat_send_player(name, "Marked all as read!")
email.show_inbox(name)
end
end]]--
end)
function email.send_mail(aname, ato, msg)
local name = send_as[aname] or aname
local to = forward_to[ato] or ato
minetest.log("action", "Email - To: "..to..", From: "..name..", MSG: "..msg)
email.log("Email - To: "..to..", From: "..name..", MSG: "..msg)
if not minetest.player_exists(to) then
return false, "Player '" .. to .. "' does not exist"
end
local mail = {
date = os.date("%Y-%m-%d %H:%M:%S"),
from = name,
msg = msg}
if email.inboxes[to] then
table.insert(email.inboxes[to], mail)
else
email.inboxes[to] = { mail }
end
email.save()
minetest.chat_send_player(to, "Mail from " .. name .. ": " .. msg)
return true, "Message sent to " .. ato
end
minetest.register_chatcommand("inbox", {
params = "[/clear/text]",
description = "Inbox: Blank to see inbox. Use 'clear' to empty inbox, 'text' for text only.",
func = function(name, param)
if param == "clear" then
email.clear_inbox(name)
return true, "Inbox cleared"
elseif param == "text" or param == "txt" or param == "t" or not minetest.get_player_by_name(name) then
return email.show_inbox(name, true)
else
return email.show_inbox(name, false)
end
end
})
minetest.register_chatcommand("mail", {
params = "name msg",
description = "mail: add a message to a player's inbox",
func = function(name, param)
local to, msg = string.match(param, "^([%a%d_-]+) (.+)")
if to and msg then
return email.send_mail(name, to, msg)
else
return false, "Usage: mail <playername> <some message>"
end
end
})
dofile(minetest.get_modpath("email") .. "/hud.lua")
email.init()

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

0
mods/other/modpack.txt Normal file
View file

View file

@ -0,0 +1,5 @@
minetest.register_on_joinplayer(function(player)
player:hud_set_flags({
minimap = false,
})
end)

View file

@ -0,0 +1,17 @@
RandomMessages mod by arsdragonfly.
Put your messages in (world directory)/random_messages,1 message per line.
Messages can be all kinds of hints, mod usage, etc.
Add/Remove messages on the fly:
/rmessages viewmessages
to see all the messages.
/rmessages addmessage blah blah blah
to add the random message blah blah blah.
/rmessages removemessage 2
to remove the 2nd random message in /rmessages viewmessages .
In minetest.conf, random_messages_interval decides how often a message is sent.
Released under CC0.
Special thanks to:
Michael Rasmussen (michael@jamhome.us)
Enjoy it! ^_^
arsdragonfly@gmail.com
6/19/2013

View file

@ -0,0 +1 @@
default

View file

@ -0,0 +1,152 @@
--[[
RandomMessages mod by arsdragonfly.
arsdragonfly@gmail.com
6/19/2013
--]]
--Time between two subsequent messages.
local MESSAGE_INTERVAL = 0
math.randomseed(os.time())
random_messages = {}
random_messages.messages = {} --This table contains all messages.
function table.count( t ) -- luacheck: ignore
local i = 0
for k in pairs( t ) do i = i + 1 end
return i
end
function table.random( t ) -- luacheck: ignore
local rk = math.random( 1, table.count( t ) )
local i = 1
for k, v in pairs( t ) do
if ( i == rk ) then return v, k end
i = i + 1
end
end
function random_messages.initialize() --Set the interval in minetest.conf.
minetest.settings:set("random_messages_interval",60)
minetest.settings:write();
return 60
end
function random_messages.set_interval() --Read the interval from minetest.conf and set it if it doesn't exist
MESSAGE_INTERVAL = tonumber(minetest.settings:get("random_messages_interval")) or random_messages.initialize()
end
function random_messages.check_params(name,func,params)
local stat,msg = func(params)
if not stat then
minetest.chat_send_player(name,msg)
return false
end
return true
end
function random_messages.read_messages()
random_messages.messages = {
"To talk to only your team, start your messages with /t. For example, /t Hello team!",
"Eat apples to restore health quickly.",
"Steel swords do more damage than guns, but you need to be up close.",
"Gain more score by killing more than you die, or by capturing the flag.",
"You gain more score the better the opponent you defeat.",
"Find weapons in chests or mine and use furnaces to make stronger swords.",
"Players are immune to attack for 15 seconds after they respawn.",
"Access the pro section of the chest by achieving a 1k+ score and killing 3 people for every 2 deaths.",
"Use team doors (steel) to stop the enemy walking into your base.",
"Sprint by pressing the fast key (E) when you have stamina.",
"Like CTF? Give feedback using /report, and consider donating at rubenwardy.com/donate",
"Map makers needed! Visit ctf.rubenwardy.com to get involved.",
"Using limited resources for building structures that don't strengthen your base's defences is discouraged.",
"To report misbehaving players to moderators, please use /report <name> <action>",
"Swearing, trolling and being rude will not be tolerated and strict action will be taken.",
"Trapping team mates on purpose is strictly against the rules and you will be kicked immediately.",
"Help your team claim victory by storing extra weapons in the team chest, and never taking more than you need.",
"Excessive spawn-killing is a direct violation of the rules - appropriate punishments will be given.",
"Use /r to check your score and rank, and /rankings to see the league tables."
}
end
function random_messages.display_message(message_number)
local msg = random_messages.messages[message_number] or message_number
if msg then
minetest.chat_send_all(msg)
end
end
function random_messages.show_message()
random_messages.display_message(table.random(random_messages.messages))
end
function random_messages.list_messages()
local str = ""
for k,v in pairs(random_messages.messages) do
str = str .. k .. " | " .. v .. "\n"
end
return str
end
function random_messages.remove_message(k)
table.remove(random_messages.messages,k)
random_messages.save_messages()
end
function random_messages.add_message(t)
table.insert(random_messages.messages,table.concat(t," ",2))
random_messages.save_messages()
end
function random_messages.save_messages()
local output = io.open(minetest.get_worldpath().."/random_messages","w")
for k,v in pairs(random_messages.messages) do
output:write(v .. "\n")
end
io.close(output)
end
--When server starts:
random_messages.set_interval()
random_messages.read_messages()
local function step(dtime)
random_messages.show_message()
minetest.after(MESSAGE_INTERVAL, step)
end
minetest.after(MESSAGE_INTERVAL, step)
local register_chatcommand_table = {
params = "viewmessages | removemessage <number> | addmessage <number>",
privs = {server = true},
description = "View and/or alter the server's random messages",
func = function(name,param)
local t = string.split(param, " ")
if t[1] == "viewmessages" then
minetest.chat_send_player(name,random_messages.list_messages())
elseif t[1] == "removemessage" then
if not random_messages.check_params(
name,
function (params)
if not tonumber(params[2]) or
random_messages.messages[tonumber(params[2])] == nil then
return false,"ERROR: No such message."
end
return true
end,
t) then return end
random_messages.remove_message(t[2])
elseif t[1] == "addmessage" then
if not t[2] then
minetest.chat_send_player(name,"ERROR: No message.")
else
random_messages.add_message(t)
end
else
minetest.chat_send_player(name,"ERROR: Invalid command.")
end
end
}
minetest.register_chatcommand("random_messages", register_chatcommand_table)
minetest.register_chatcommand("rmessages", register_chatcommand_table)

View file

@ -0,0 +1,12 @@
# Report
Allows players to report things to admins and online moderators.
Suppose `player`, `griefer` and `moderator` are online players.
`player` runs this command: `/report griefer is griefing`
`moderator` sees: `-!- player reported: griefer is griefing`
The Admin (named in "name") is mailed via chatplus: `<player1> Report: player2 is griefing (mods online: player3)`
License: WTFPL
Depends: email.

View file

@ -0,0 +1,2 @@
email
irc?

View file

@ -0,0 +1 @@
Allows players to report misconduct or bugs using /report.

View file

@ -0,0 +1,90 @@
local storage = minetest.get_mod_storage()
local function get_irc_mods()
return storage:get_string("irc_mods"):split(",")
end
local function add_irc_mod(name)
local mods = get_irc_mods()
if table.indexof(mods, name) > 0 then
return false
end
mods[#mods + 1] = name
storage:set_string("irc_mods", table.concat(mods, ","))
return true
end
local function remove_irc_mod(name)
local mods = get_irc_mods()
local idx = table.indexof(mods, name)
if idx > 0 then
table.remove(mods, idx)
storage:set_string("irc_mods", table.concat(mods, ","))
return true
end
return false
end
minetest.register_chatcommand("report_sub", {
privs = { kick = true },
func = function(name, param)
if param:lower():trim() == "remove" then
if remove_irc_mod(name) then
return true, "Successfully removed!"
else
return false, "Unable to remove, are you even subscribed?"
end
else
if add_irc_mod(name) then
return true, "Successfully added!"
else
return false, "Unable to add, are you already subscribed?"
end
end
end
})
minetest.register_chatcommand("report", {
func = function(name, param)
param = param:trim()
if param == "" then
return false, "Please add a message to your report. " ..
"If it's about (a) particular player(s), please also include their name(s)."
end
local _, count = string.gsub(param, " ", "")
if count == 0 then
minetest.chat_send_player(name, "If you're reporting a player, " ..
"you should also include a reason why. (Eg: swearing, sabotage)")
end
-- Send to online moderators / admins
-- Get comma separated list of online moderators and admins
local mods = {}
for _, player in pairs(minetest.get_connected_players()) do
local toname = player:get_player_name()
if minetest.check_player_privs(toname, {kick = true, ban = true}) then
table.insert(mods, toname)
minetest.chat_send_player(toname, minetest.colorize("#FFFF00",
"-!- " .. name .. " reported: " .. param))
end
end
-- Build message for offline listeners
local msg
if #mods == 0 then
msg = "Report from " ..name .. ": " .. param .. " (no mods online)"
else
msg = "Report from " ..name .. ": " .. param .. " (mods online: " ..
table.concat(mods, ", ") .. ")"
end
-- Send to IRC moderators
for _, toname in pairs(get_irc_mods()) do
if not minetest.get_player_by_name(toname) then
minetest.chat_send_player(toname, msg)
end
end
return true, "Reported. We'll get back to you."
end
})

View file

@ -0,0 +1,7 @@
Copyright 2018 by rubenwardy and GunshipPenguin
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,31 @@
# Sprint Mod For minetest
Allows players to sprint by holding E, by default increasing their travel speed
by 80% and their jump speed by 10%. Also adds a stamina bar that goes down when
the player sprints and goes up they're not sprinting.
This mod is compatible with the HUD bars [hudbars] mod, but does not depend on
it. In this case, a green HUD bar will be displayed, also showing a number.
If this mod is not present, a standard statbar with 0-20 “half-arrows” is shown,
which is a bit more coarse than the HUD bar version.
This mod is by rubenwardy, and is based on a mod by GunshipPenguin but heavily
rewritten to be more performant.
License: MIT (see license.txt)
## Settings
1.0 represents normal speed so 1.5 would mean that a sprinting player would
travel 50% faster than a walking player and 2.4 would mean that a sprinting
player would travel 140% faster than a walking player.
* `sprint_speed`- 1 is normal walking speed, defaults to 1.8.
* `sprint_jump` - 1 is normal jump speed, defaults to 1.1.
* `sprint_stamina` - How long the sprint lasts in seconds, defaults to 20.
* `sprint_heal_rate` - Multiply this by the stamina to get how long it takes to recharge, defaults to 0.5.
* `sprint_min` - The minimum value at which you can start sprinting, defaults to 0.5.
## Recharging when the sprint key is down
Follow the instructions in comments marked by `##number##`.

View file

@ -0,0 +1 @@
hudbars?

123
mods/other/sprint/init.lua Normal file
View file

@ -0,0 +1,123 @@
-- Sprint mod by rubenwardy. License: MIT.
-- Heavily modified from a mod by GunshipPenguin
-- Config, see README.md
local MOD_WALK = tonumber(minetest.settings:get("sprint_speed") or 1.8)
local MOD_JUMP = tonumber(minetest.settings:get("sprint_jump") or 1.1)
local STAMINA_MAX = tonumber(minetest.settings:get("sprint_stamina") or 20)
local HEAL_RATE = tonumber(minetest.settings:get("sprint_heal_rate") or 0.5)
local MIN_SPRINT = tonumber(minetest.settings:get("sprint_min") or 0.5)
local SPRINT_MODIFIERS = {
[true] = { speed = MOD_WALK, jump = MOD_JUMP },
[false] = { speed = 1.0, jump = 1.0 },
}
if minetest.get_modpath("hudbars") ~= nil then
hb.register_hudbar("sprint", 0xFFFFFF, "Stamina",
{ bar = "sprint_stamina_bar.png", icon = "sprint_stamina_icon.png" },
STAMINA_MAX, STAMINA_MAX,
false, "%s: %.1f/%.1f")
SPRINT_HUDBARS_USED = true
else
SPRINT_HUDBARS_USED = false
end
local players = {}
local function setSprinting(player, info, sprinting)
if info.sprinting ~= sprinting then
player:set_physics_override(SPRINT_MODIFIERS[sprinting])
end
info.sprinting = sprinting
end
minetest.register_globalstep(function(dtime)
for name, info in pairs(players) do
local player = minetest.get_player_by_name(name)
if not player then
players[name] = nil
else
-- ##?## You can enable recharging when the E key is pressed by
-- following these instructions.
--Check if the player should be sprinting
local controls = player:get_player_control()
local sprintRequested = controls.aux1 and controls.up
-- ##1## replace info.sprintRequested with info.sprinting
if sprintRequested ~= info.sprintRequested then
if sprintRequested and info.stamina > MIN_SPRINT then
setSprinting(player, info, true)
end
if not sprintRequested then
setSprinting(player, info, false)
end
end
info.sprintRequested = sprintRequested
local staminaChanged = false
if info.sprinting then
info.stamina = info.stamina - dtime
staminaChanged = true
if info.stamina <= 0 then
info.stamina = 0
setSprinting(player, info, false)
end
-- ##2## remove `not info.sprintRequested and`
elseif not info.sprintRequested and info.stamina < STAMINA_MAX then
info.stamina = info.stamina + dtime * HEAL_RATE
staminaChanged = true
if info.stamina > STAMINA_MAX then
info.stamina = STAMINA_MAX
end
end
if staminaChanged then
if SPRINT_HUDBARS_USED then
hb.change_hudbar(player, "sprint", info.stamina)
else
local numBars = math.floor(20 * info.stamina / STAMINA_MAX)
if info.lastHudSendValue ~= numBars then
info.lastHudSendValue = numBars
player:hud_change(info.hud, "number", numBars)
print("Sending hud value")
end
end
end
end
end
end)
minetest.register_on_joinplayer(function(player)
local info = {
sprinting = false, -- Is the player actually sprinting?
stamina = STAMINA_MAX, -- integer, the stamina we have left
sprintRequested = false, -- is the sprint key down / etc?
}
if SPRINT_HUDBARS_USED then
hb.init_hudbar(player, "sprint")
else
info.hud = player:hud_add({
hud_elem_type = "statbar",
position = {x=0.5, y=1},
size = {x=24, y=24},
text = "sprint_stamina_icon.png",
number = 20,
alignment = {x=0, y=1},
offset = {x=-263, y=-110},
})
end
players[player:get_player_name()] = info
end)
minetest.register_on_respawnplayer(function(player)
players[player:get_player_name()].stamina = STAMINA_MAX
end)
minetest.register_on_leaveplayer(function(player)
players[player:get_player_name()] = nil
end)

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

View file

@ -0,0 +1,83 @@
If you wish to register treasures to Treasurer, it is recommended to assign the treasurer to a Treasurer group.
It is not possible to assign a treasure to multiple groups. If you think a treasure fits into two groups, create
two seperate treasure definitions instead. But try to keep it as an exception.
Think of treasurer groups as categories for treasurers to put into.
This file contains some guidelines for Treasurer groups and there are even guidelines for preciousness levels.
Keep in mind these are only guidelines.
Treasurer suggests to use the following standard Treasurer groups:
crafting_component:
Generic group for components in crafting recipes. If it is primarily a component for food, use raw_food instead.
Preciousness is based roughly on the preciousness of items it can create.
fuel:
Fuel for furnaces.
Preciousness is based on burning time.
food:
Can be eaten and restores health.
Preciousness should equal the number of hearts restored (restored HP divided by 2), but not higher than 7.
raw_food:
Food which is not fully processed and is not (quite) ready to be eaten.
melee_weapon:
Primarily used to damage close foes, i.e. a sword.
Preciousness is based on attack speed and damage.
ranged_weapon:
Primarily used to damage far away foes,
Preciousness is based on attack speed, damage and range.
tool:
A tool for other uses.
Preciousness is hard to determine; at least is should base on the number of uses.
minetool:
A tool to destroy blocks. Includes pickaxes, axes, shovels, …
Preciousness is based on power, number of uses and speed.
deco:
Primarily just a decorational thing to place.
Preciousness is based on beauty, highly subjective.
light:
Is a light source.
Preciousness is based on the brightness. For the maximum brightness (before sun brightness), preciousness should be 3.
building_block:
A block for buildings. Includes stairs, slabs, fences and similar things.
Excludes all natural blocks.
Preciousness should be roughly based on the “cost” to craft the block.
seed:
Seeds and saplings.
Preciousness is based on the percieved “usefulness” of what can grow from the seed.
transport_vehicle
A vehicle to transport players and stuff, i.e. a cart or a boat.
Preciousness is hard to determine, maybe speed?
transport_structure
A fixed structure which is neccessary for a transport vehicle to operate, i.e. rails.
Preciousness is hard to dertermine …
ladder
A ladder.
default:
This is the group your treasure get assigned to if you dont specify a group.
Do you miss a group? Feel free to invent your own!
Note that Treasurer groups differ from the groups as defined by the Minetest API. These groups are handled differently.

View file

@ -0,0 +1,246 @@
= Treasurers README file for Treasurer version 0.2.0 =
== Overview ==
* Name: Treasurer
* Technical name: `treasurer`
* Purpose: To provide an interface for mods which want to spawn ItemStacks randomly and an interface for mods which create new items.
* Version: 0.2.0
* Dependencies: none
* License: WTFPL
== Introduction ==
Problem:
There are a bunch of mods which have cool items but they wont appear in the world by
themselves.
There are some mods which randomly distribute treasures into the world. Sadly, these only
distribute the items they know—which are just the items of the mod “default” most of the
time. The items of the other mods are completely missed.
The reason for this is that the distributing mods cant know what sort of items are available
unless they explicitly depend on the mods that defines these. Viewed the other way round,
the item-defining mods that also distribute these items into the world are limited in the
sense that they only know one means of distributing items.
There is a gap between defining items and distributing them. Every time a mod does both,
flexibility is limited and expansion becomes difficult.
To bridge this gap, Treasurer has been written. Treasurer makes it possible a) for mods to define
treasures without bothering _how_ these are distributed into the world and b) for mods to distribute
treasures around the world without knowledge about _what_ treasures exactly are distributed.
== Technical side of Treasurer ==
=== technical overview ===
To get a working Treasurer architecture and actually get some treasures into the world,
you need:
* Treasurer
* at least one treasure registration mod
* at least one treasure spawning mod
=== treasurer registration mod ===
Firstly, there are the treasure registration mods (TRMs). The task of TRMs is to tell
Treasurer which items does it have to offer, which relative appearance probabilities these
treasures should have, how “precious” the treasure is considered (on a scale from 0 to 10)
, optionally how big the stack of items should be and optionally how much worn out it is.
TRMs must depend on at least two mods: On treasurer and on the mod or mods
where the items are defined. Technically, a TRM consists of nothing more than a long
list of “registration” function calls. While this seems trivial, the task of balancing
out probabilties and balancing out preciousness levels of treasures is not trivial
and it may take a long time to get right.
It is strongly recommended that a TRM really does nothing
more than registering treasures (and not defining items, for example). If you want
to make your mod compatible to Treasurer, dont change your mod, write a TRM for
it instead.
There is an example TRM, called “`trm_default_example`”. It registers some items
of the default as treasures. Unsurprisingly, it depends on `treasurer` and `default`.
=== treasurer spawning mods ===
Secondly, there are the treasure spawning mods (TSMs). The task of a TSM is to somehow
distribute the available treasures into the world. This is also called “treasure
spawning”. How exactly the TSM spawns the treasures is completely up the TSM. But a
TSM has to request Treasurer to get some random treasures to distribute. A TSM may
optionally request to filter out treasures outside certain preciousness levels
and groups, so the result is a bit controllable and not completely random.
Treasurer can not guarantee to return the requestet amount of treasures, it may
return an empty table, for two reasons:
* There is no TRM activated. There must be at least one to work.
* The filters filtered out everything, leaving Treasurer with an empty treasure pool
to choose from. This problem can be fixed by installing more TRMs or by balancing the
existing TRMs to cover as many preciousness levels as possible. It also may be that
the range specified by the TSM was too small. It is recommended to keep the
requested range at least of a size of 1. Treasurer does, however, guarantee that
the returned treasures are always in the requested bounds.
A TSM has to at least depend on Treasurer.
Unlike for TRMs, it may not be a problem to also do some other stuff than just
spawning treasures if it seems feasible. You may choose to make your TSM fully
dependant on Treasure, then it wont work without Treasurer. You may also choose
to only add an optional dependency on Treasurer. For this to work, your mod must
now select its own treasures, which of course will only come from a rather limited
pool.
To check if the Treasurer mod is installed, you can use something like this in
your code:
```
if(minetest.get_modpath("treasurer")~=nil) then
-- Treasurer is installed.
-- You can call Treasurers functions here.
else
-- Treasurer is not installed.
-- You may write your replacement code here.
-- You can not call Treasurers funcitons here.
end
```
There are two example TSMs. The first one is a very basic one and called “`tsm_gift_example`”.
It gives a “welcome gift” (1 random treasure) to players who just joined the server
or who respawn. The preciousness and group filters are not used. It does only depend on
Treasurer. The second one is called “`tsm_chests_example`” and pretty advanced for an example.
It places chests of the mod “default” between 20 and 200 node lenghts below the water
surface and puts 1-6 random treasures into these. The lower the chest, the higher
the preciousness. It depends on treasurer and default (for the chests, of course).
=== Recap ===
TRMs define treasures, TSMs spawn them. Treasurer manages the pool of available treasures.
TRMs and TSMs do not have to know anything from each other.
TRMs and TSMs do not neccessarily have to change any line of code of other mods to function.
Treasurer depends on nothing.
Important: It should always only be neccessary for TRMs and TSMs to depend on Treasurer.
All other mods do NOT HAVE TO and SHOULD NOT depend on Treasurer.
=== Treasure attributes ===
This section explains the various attributes a treasure can have.
==== Rarity ====
Rarity in Treasurer works in a pretty primitive way: The relative rarities of all
treasures from the treasure pool are simply all added up. The probabilitiy of one
certain treasure is then simply the rarity value divided by the sum.
==== Preciousness ====
How “precious” an item is, is highly subjective and also not always easy to categorize.
Preciousness in Treasurers terms should be therefore viewed as “utility” or as
“reward level” or “strength” or even “beauty” or whatever positive attributes you can
think of for items. See the text file `GROUPS_AND_PRECIOUSNESS` for a rough
guideline.
So, if you create a TRM and your treasure is something you want the player work
hard for, assign it a high preciousness. Everyday items that are already easy to
obtain in normal gameplay certainly deserve a lower precious than items that are
expensive to craft.
If your treasure consists of a powerful
item, assign it a high preciousness. When in doubt, try to value gameplay over
personal taste. Remember that TSMs can (and will!) filter out treasures based
on their preciousness.
For TSM authors, consider preciousness this way: If the trouble the player has
to get through to in order to obtain the treasure is high, better filter
out unprecious treasures. If your TSM distributes many treasures all over the world and these
are easy to obtain, filter out precious treasures.
TSMs also can just completely ignore preciousness, then the given treasures base
on sheer luck.
==== Treasurer groups ====
Every treasure can be assigned to a group. These groups are specific to Treasurer only.
The idea is that treasures which share a common property are member of the same group.
All groups have a name by which they are identified.
For example, if there are apples, plums, pears and oranges and those items can be
eaten for health, all those treasures would be members of the group “food”.
The group system can be used to further narrow down the treasure pool from which you
want Treasurer to return treasures. This makes it more interesting than just using
an one-dimensional preciousness scale.
Using the groups system is entirely optional. If your TRM does not specify any group,
your treasure will be assigned to the group “default”. It is not possible for a treasure
to not belong to any group. If your TSM does not specify a group parameter, Treasurer
will use all groups.
While not using groups as a TSM may be perfectly okay, not using groups as a TRM is
not recommended, because TSM which filter by groups may “overlook” your treasure,
even if it would actually fit, simply because you didnt assign it to a specific group.
Note that Treasurer groups are completely distinct from Minetests group system.
You can basically invent your own groups on the fly, but it is strongly recommended that you
use the groups suggested in the text file `GROUPS_AND_PRECIOUSNESS` whenever possible, for
maximum portability of your TSM. The text file also has a rough guideline for finding
appropriate values for the preciousness.
==== Recap ====
Rarity determines the chance of a treasure, whereas preciousness determines
the difficulty to obtain it. Group
== Overview of examples ==
- `trm_default_example` - registers items of default mod
- `tsm_chests_example` - spawns chests (from the default mod)
- `tsm_gift_example` - gives one treasure as a “welcome gift” to joined or respawned players
== Treasurer API documentation ==
=== API documentation for treasure registration mods ===
The API consists of one function, which is called “`treasurer.register_treasure`”.
==== `treasurer.register_treasure` ====
Registers a new treasure (this means the treasure will be ready to be spawned by treasure spawning mods).
This function does some basic parameter checking to catch the most obvious
mistakes. If invalid parameters have been passed, the input is rejected and
the function returns false. However, it does not cover every possible
mistake, so some invalid treasures may slip through.
Rarity does not imply preciousness. A rare treasure may not neccessarily a
very precious one. A treasure chest with scorched stuff inside may be very
rare, but its certainly also very unprecious.
===== Parameters =====
* `name`: name of resulting `ItemStack`, e.g. “`mymod:item`
* `rarity`: rarity of treasure on a scale from 0 to 1 (inclusive). lower = rarer
* `preciousness` : subjective preciousness on a scale from 0 to 10 (inclusive). higher = more precious.
* `count`: optional value which specifies the multiplicity of the item. Default is 1. See `count` syntax help in this file.
* `wear`: optional value which specifies the wear of the item. Default is 0, which disables the wear. See `wear` syntax help in this file.
* `treasurer_groups`: an optional table of group names to assign this treasure to. If omitted, the treasure is added to the default group.
===== Return value =====
`true` on success, `false` on failure.
=== data formats ===
format of count type:
==== `count` ====
A `count` can be a number or a table
* `number`: its always so many times
* `{min, max}`: its pseudorandomly between `min` and `max` times, `math.random` will be used to chose the value
* `{min, max, prob_func}`: its between `min` and `max` times, and the value is given by `prob_func` (see below)
==== `wear` ====
Completely analogous to `count`.
==== Format of `prob_func` function ====
There are no parameters.
It returns a random or pseudorandom number between 0 (inclusive) and 1 (exclusive).
`prob_func` is entirely optional, if its not used, treasurer will
default to using `math.random`. You can use `prob_func` to define your own
“randomness” function, in case you dont wish your values to be evenly
distributed.
=== API documentation for treasure spawning mods ===
The API consists of one function, called “`treasurer.select_random_treasures`”.
==== `treasurer.select_random_treasures` ====
Request some treasures from treasurer.
===== Parameters =====
* `count`: (optional) amount of treasures. If this value is `nil`, Treasurer assumes a default of 1.
* `minimal_preciousness`: (optional) dont consider treasures with a lower preciousness. If `nil`, theres no lower bound.
* `maximum_preciousness`: (optional) dont consider treasures with a higher preciousness. If `nil`, theres no upper bound.
* `treasurer_group`: (optional): Only consider treasures which are members of at least one of the members of the provided Treasurer group table. `nil` = consider all groups
===== Return value =====
A table of `ItemStacks` (the requested treasures). It may be empty.

View file

@ -0,0 +1,48 @@
== Treasurer [treasurer] ==
With Treasurer I want to introduce a new way to define and distribute random treasures into the world. Treasurer itself does neither define any items nor does it attempt to distribute them.
Instead Treasurer provides two interfaces.
To actually get a game with treasures using Treasurer running, you need at least the following:
[list]
[*]Treasurer
[*]at least one treasure registration mod
[*]at least one treasure spawning mod
[/list]
A treasure is basically just a fancy ItemStack. It consists of:
- name of item
- relative rarity
- a preciousness
- the amount of item (can be randomized)
- a wear (can be randomized)
A TRM does nothing more than telling Treasurer a bunch of treasures it has to offer. Note, however, that a TRM should really do nothing more than just that. The actual mods where the items originate from does most likely NOT have to be changed in order to register its items as treasures. TRMs are simple,
A TSM distributes (“spawns”) treasures, where it has to ask Treasurer for some treasures. Treasurer then chooses some treasures out of the pool of available treasures and returns a list of ItemStacks. The TSM also may ask Treasurer to filter out treasures which have an either too high or too low preciousness (“preciousness” is defined by the TRMs). Be warned: Treasurer may return no treasures at all, either because no TRM is activated or no treasure that matches the filter exists.
“Spawning” here means to put the treasures into the world somehow. How a TSM does the task of spawning is up to the TSM. Treasurer
Recap: The TSMs will spawn treasures into the world out of a pool of treasures which is offered by TRMs. Treasurer manages the pool.
Examples for TRMs are:
- registering bronze, gold, iron ingots and possibly other items of default (see trm_default_example)
- registering bread, wheat, seeds, etc. of farming
- registering super cool item X from awesome mod Y, where X could normally only obtained by “/give” or with creative mode.
- registering the … bla bla bla, you get the idea
Examples for TSMs are:
- let a mob drop a not mob-specific, random treasure on death
- give a random treasure as a “welcome gift” to joining and respawning players (see tsm_gift_example)
- placing chests around the world and spawning treasures into them (see tsm_chests_example)
Some of the above example have been implemented into already working mods, which are distributed with Treasurer
as seperate mods.
Dependencies of Treasurer: None!
Minimal dependencies of all TRMs: treasurer and all mods that define the registered items
Minimal dependency of all TSMs: treasurer
License: WTFPL

View file

View file

@ -0,0 +1 @@
A framework which manages treasures which helps other mods to register and distribute treasures around the world.

View file

@ -0,0 +1,363 @@
--[==[
Treasurer
- A mod for Minetest
version 0.2.0
]==]
--[=[
TABLE OF CONTENTS
part 1: Initialization
part 2: Treasure API
part 3: Treasure spawning mod handling
part 4: Internal functions
]=]
--[=[
part 1: Initialization
]=]
-- This creates the main table; all functions of this mod are stored in this table
treasurer = {}
-- Table which stores all the treasures
treasurer.treasures = {}
-- This table stores the treasures again, but this time sorted by groups
treasurer.groups = {}
-- Groups defined by the Treasurer API
treasurer.groups.treasurer = {}
treasurer.groups.treasurer.default = {}
-- Groups defined by the Minetest API
treasurer.groups.minetest = {}
--[[
format of treasure table:
treasure = {
name, -- treasure name, e.g. mymod:item
rarity, -- relative rarity on a scale from 0 to 1 (inclusive).
-- a rare treasure must not neccessarily be a precious treasure
count, -- count (see below)
preciousness, -- preciousness or “worth” of the treasure.
-- ranges from 0 (“scorched stuff”) to 10 (“diamond block”)
wear, -- wear (see below)
metadata, -- unused at the moment
}
treasures can be nodes or items
format of count type:
count = number -- its always number times
count = {min, max} -- its pseudorandomly between min and max times, math.random() will be used to chose the value
count = {min, max, prob_func} -- its between min and max times, and the value is given by prob_func (which is not neccessarily random [in the strictly mathematical sense])
format of wear type:
completely analogous to count type
format of prob_func function:
prob_func = function()
--> returns a random or pseudorandom number between 0 (inclusive) and 1 (exclusive)
prob_func is entirely optional, if its not used, treasurer will default to math.random.
You can use prob_func to define your own random function, in case you dont like an even
distribution
format of treasurer_groups:
This is just a table of strings, each string stands for a group name.
]]
--[=[
part 2: Treasurer API
]=]
--[[
treasurer.register_treasure - registers a new treasure
(this means the treasure will be ready to be spawned by treasure spawning mods.
name: name of resulting ItemStack, e.g. mymod:item
rarity: rarity of treasure on a scale from 0 to 1 (inclusive). lower = rarer
preciousness: preciousness of treasure on a scale from 0 (scorched stuff) to 10 (diamond block).
count: optional value which specifies the multiplicity of the item. Default is 1. See count syntax help in this file.
wear: optional value which specifies the wear of the item. Default is 0, which disables the wear. See wear syntax help in this file.
treasurer_groups: (optional) a table of group names to assign this treasure to. If omitted, the treasure is added to the default group.
This function does some basic parameter checking to catch the most obvious mistakes. If invalid parameters have been passed, the input is rejected and the function returns false. However, it does not cover every possible mistake, so some invalid treasures may slip through.
returns: true on success, false on failure
]]
function treasurer.register_treasure(name, rarity, preciousness, count, wear, treasurer_groups )
--[[ We dont trust our input, so we first check if the parameters
have the correct types and refuse to add the treasure if a
parameter is malformed.
What follows is a bunch of parameter checks.
]]
-- check wheather name is a string
if type(name) ~= "string" then
minetest.log("error","[treasure] I rejected a treasure because the name was of type \""..type(name).."\" instead of \"string\".")
return false
end
-- first check if rarity is even a number
if type(rarity) == "number" then
-- then check wheather the rarity lies in the allowed range
if rarity < 0 or rarity > 1 then
minetest.log("error", "[treasurer] I rejected the treasure \""..tostring(name).."\" because its rarity value is out of bounds. (it was "..tostring(rarity)..".)")
return false
end
else
minetest.log("error","[treasurer] I rejected the treasure \""..tostring(name).."\" because it had an illegal type of rarity. Given type was \""..type(rarity).."\".")
return false
end
-- check if preciousness is even a number
if type(preciousness) == "number" then
-- then check wheather the preciousness lies in the allowed range
if preciousness < 0 or preciousness > 10 then
minetest.log("error", "[treasurer] I rejected the treasure \""..tostring(name).."\" because its preciousness value is out of bounds. (it was "..tostring(preciousness)..".)")
return false
end
else
minetest.log("error","[treasurer] I rejected the treasure \""..tostring(name).."\" because it had an illegal type of preciousness. Given type was \""..type(preciousness).."\".")
return false
end
-- first check if count is of a correct type
if type(count) ~= "number" and type(count) ~= "nil" and type(count) ~= "table" then
minetest.log("error", "[treasurer] I rejected the treasure \""..tostring(name).."\" because it had an illegal type of “count”. Given type was \""..type(count).."\".")
return false
end
-- if counts a table, check if its format is correct
if type(count) == "table" then
if(not (type(count[1]) == "number" and type(count[2]) == "number" and (type(count[3]) == "function" or type(count[3]) == "nil"))) then
minetest.log("error","[treasurer] I rejected the treasure \""..tostring(name).."\" because it had a malformed table for the count parameter.")
return false
end
end
-- now do the same for wear:
-- first check if wear is of a correct type
if type(wear) ~= "number" and type(wear) ~= "nil" and type(wear) ~= "table" then
minetest.log("error","[treasurer] I rejected the treasure \""..tostring(name).."\" because it had an illegal type of “wear”. Given type was \""..type(wear).."\".")
return false
end
-- if wears a table, check if its format is correct
if type(wear) == "table" then
if(not (type(wear[1]) == "number" and type(wear[2]) == "number" and (type(wear[3]) == "function" or type(wear[3]) == "nil"))) then
minetest.log("error","[treasurer] I rejected the treasure \""..tostring(name).."\" because it had a malformed table for the wear parameter.")
return false
end
end
-- check type of treasurer_group
if type(treasurer_groups) ~= "table" and type(treasurer_groups) ~= "nil" and type(treasurer_groups) ~= "string" then
minetest.log("error","[treasurer] I rejected the treasure \""..tostring(name).."\" because the treasure_group parameter is of type "..tosting(type(treasurer_groups)).." (expected: nil, string or table).")
return false
end
--[[ End of checks. If we reached this point of the code, all checks have been passed
and we finally register the treasure.]]
-- default count is 1
if count == nil then count = 1 end
-- default wear is 0
if wear == nil then wear = 0 end
local treasure = {
name = name,
rarity = rarity,
count = count,
wear = wear,
preciousness = preciousness,
metadata = "",
}
table.insert(treasurer.treasures, treasure)
--[[ Assign treasure to Treasurer group(s) or default if not provided ]]
-- default Treasurer group is default
if treasurer_groups == nil then treasurer_groups = "default" end
if(type(treasurer_groups) == "string") then
if(treasurer.groups.treasurer[treasurer_groups] == nil) then
treasurer.groups.treasurer[treasurer_groups] = {}
end
table.insert(treasurer.groups.treasurer[treasurer_groups], treasure)
elseif(type(treasurer_groups) == "table") then
for i=1,#treasurer_groups do
-- assign to Treasurer group (create table if it does not exist yet)
if(treasurer.groups.treasurer[treasurer_groups[i]] == nil) then
treasurer.groups.treasurer[treasurer_groups[i]] = {}
end
table.insert(treasurer.groups.treasurer[treasurer_groups[i]], treasure)
end
end
minetest.log("info","[treasurer] Treasure successfully registered: "..name)
return true
end
--[=[
part 3: Treasure spawning mod (TSM) handling
]=]
--[[
treasurer.select_random_treasures - request some treasures from treasurer
parameters:
count: (optional) amount of items in the treasure. If this value is nil, treasurer assumes a default of 1.
min_preciousness: (optional) dont consider treasures with a lower preciousness. nil = no lower limit
max_preciousness: (optional) dont consider treasures with a higher preciousness. nil = no lower limit
treasurer_groups: (optional): Only consider treasures which are members of at least one of the members of the provided Treasurer group table. nil = consider all groups
returns:
a table of ItemStacks (the requested treasures) - may be empty
on error, it returns false
]]
function treasurer.select_random_treasures(count, min_preciousness, max_preciousness, treasurer_groups)
if #treasurer.treasures == 0 and count >= 1 then
minetest.log("info","[treasurer] I was asked to return "..count.." treasure(s) but I cant return any because no treasure was registered to me.")
return {}
end
if count == nil then count = 1 end
local sum = 0
local cumulate = {}
local randoms = {}
-- copy treasures into helper table
local p_treasures = {}
if(treasurer_groups == nil) then
-- if the group filter is not used (defaul behaviour), copy all treasures
for i=1,#treasurer.treasures do
table.insert(p_treasures, treasurer.treasures[i])
end
-- if the group filter IS used, copy only the treasures from the said groups
elseif(type(treasurer_groups) == "string") then
if(treasurer.groups.treasurer[treasurer_groups] ~= nil) then
for i=1,#treasurer.groups.treasurer[treasurer_groups] do
table.insert(p_treasures, treasurer.groups.treasurer[treasurer_groups][i])
end
else
minetest.log("info","[treasurer] I was asked to return "..count.." treasure(s) but I cant return any because no treasure which fits to the given Treasurer group “"..treasurer_groups.."”.")
return {}
end
elseif(type(treasurer_groups) == "table") then
for t=1,#treasurer_groups do
if(treasurer.groups.treasurer[treasurer_groups[t]] ~= nil) then
for i=1,#treasurer.groups.treasurer[treasurer_groups[t]] do
table.insert(p_treasures, treasurer.groups.treasurer[treasurer_groups[t]][i])
end
end
end
else
minetest.log("error","[treasurer] treasurer.select_random_treasures was called with a malformed treasurer_groups parameter!")
return false
end
if(min_preciousness ~= nil) then
-- filter out too unprecious treasures
for t=#p_treasures,1,-1 do
if((p_treasures[t].preciousness) < min_preciousness) then
table.remove(p_treasures,t)
end
end
end
if(max_preciousness ~= nil) then
-- filter out too precious treasures
for t=#p_treasures,1,-1 do
if(p_treasures[t].preciousness > max_preciousness) then
table.remove(p_treasures,t)
end
end
end
for t=1,#p_treasures do
sum = sum + p_treasures[t].rarity
cumulate[t] = sum
end
for c=1,count do
randoms[c] = math.random() * sum
end
local treasures = {}
for c=1,count do
for t=1,#p_treasures do
if randoms[c] < cumulate[t] then
table.insert(treasures, p_treasures[t])
break
end
end
end
local itemstacks = {}
for i=1,#treasures do
itemstacks[i] = treasurer.treasure_to_itemstack(treasures[i])
end
if #itemstacks < count then
minetest.log("info","[treasurer] I was asked to return "..count.." treasure(s) but I could only return "..(#itemstacks)..".")
end
return itemstacks
end
--[=[
Part 4: internal functions
]=]
--[[ treasurer.treasure_to_itemstack - converts a treasure table to an
ItemStack
parameter:
treasure: a treasure (see format in the head of this file)
returns:
an ItemStack
]]
function treasurer.treasure_to_itemstack(treasure)
local itemstack = {}
itemstack.name = treasure.name
itemstack.count = treasurer.determine_count(treasure)
itemstack.wear = treasurer.determine_wear(treasure)
itemstack.metadata = treasure.metadata
return ItemStack(itemstack)
end
--[[
This determines the count of a treasure by taking the various different
possible types of the count value into account
This function assumes that the treasure table is valid.
returns: the count
]]
function treasurer.determine_count(treasure)
if(type(treasure.count)=="number") then
return treasure.count
else
local min,max,prob = treasure.count[1], treasure.count[2], treasure.count[3]
if(prob == nil) then
return(math.floor(min + math.random() * (max-min)))
else
return(math.floor(min + prob() * (max-min)))
end
end
end
--[[
This determines the wear of a treasure by taking the various different
possible types of the wear value into account.
This function assumes that the treasure table is valid.
returns: the count
]]
function treasurer.determine_wear(treasure)
if(type(treasure.wear)=="number") then
return treasure.wear
else
local min,max,prob = treasure.wear[1], treasure.wear[2], treasure.wear[3]
if(prob == nil) then
return(math.floor(min + math.random() * (max-min)))
else
return(math.floor(min + prob() * (max-min)))
end
end
end

View file

@ -0,0 +1,2 @@
treasurer
default

View file

@ -0,0 +1 @@
Generates some chests in the underground with random treasures.

216
mods/other/tsm_chests/init.lua Executable file
View file

@ -0,0 +1,216 @@
--[[
This is an example treasure spawning mod (TSM) for the default mod.
It needs the mods treasurer and default to work.
For an example, it is kinda advanced.
A TSMs task is to somehow bring treasures (which are ItemStacks) into the world.
This is also called spawning treasures.
How it does this task is completely free to the programmer of the TSM.
This TSM spawns the treasures by placing chests (tsm_chests:chest) between 20 and 200 node lengths below the water surface. This cau
The chests are provided by the default mod, therefore this TSM depends on the default mod.
The treasures are requested from the treasurer mod. The TSM asks the treasurer mod for some treasures.
However, the treasurer mod comes itself with no treasures whatsoever. You need another mod which tells the treasurer what treasures to add. These mods are called treasure registration mods (TRMs).
For this, there is another example mod, called trm_default_example, which registers a bunch of items of the default mod, like default:gold_ingot.
]]
local chest_formspec =
"size[8,9]" ..
default.gui_bg ..
default.gui_bg_img ..
default.gui_slots ..
"list[current_name;main;0,0.3;8,4;]" ..
"list[current_player;main;0,4.85;8,1;]" ..
"list[current_player;main;0,6.08;8,3;8]" ..
"listring[current_name;main]" ..
"listring[current_player;main]" ..
default.get_hotbar_bg(0,4.85)
minetest.register_node("tsm_chests:chest", {
description = "Chest",
tiles = {"default_chest_top.png", "default_chest_top.png", "default_chest_side.png",
"default_chest_side.png", "default_chest_side.png", "default_chest_front.png"},
paramtype2 = "facedir",
groups = {choppy = 2, oddly_breakable_by_hand = 2},
legacy_facedir_simple = true,
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
if player then
minetest.chat_send_player(player:get_player_name(),
"You're not allowed to put things in treasure chests!")
return 0
end
end,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("formspec", chest_formspec)
meta:set_string("infotext", "Chest")
local inv = meta:get_inventory()
inv:set_size("main", 8*4)
end,
can_dig = function(pos,player)
local meta = minetest.get_meta(pos);
local inv = meta:get_inventory()
return inv:is_empty("main")
end,
on_metadata_inventory_move = function(pos, from_list, from_index,
to_list, to_index, count, player)
minetest.log("action", player:get_player_name() ..
" moves stuff in chest at " .. minetest.pos_to_string(pos))
end,
on_metadata_inventory_put = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name() ..
" moves stuff to chest at " .. minetest.pos_to_string(pos))
end,
on_metadata_inventory_take = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name() ..
" takes stuff from chest at " .. minetest.pos_to_string(pos))
local inv = minetest.get_inventory({type = "node", pos=pos})
if not inv or inv:is_empty("main") then
minetest.set_node(pos, {name="air"})
minetest.show_formspec(player:get_player_name(), "", player:get_inventory_formspec())
end
end,
})
--[[ here are some configuration variables ]]
local h_min = -65 -- minimum chest spawning height, relative to water_level
local h_max = 40 -- maximum chest spawning height, relative to water_level
local t_min = 4 -- minimum amount of treasures found in a chest
local t_max = 7 -- maximum amount of treasures found in a chest
local water_level = tonumber(minetest.settings:get("water_level"))
local get_node = minetest.get_node
local function findGroundLevel(pos, y_min, y_max)
local ground = nil
local top = y_max
for y = y_max, y_min, -1 do
local p = {x=pos.x,y=y,z=pos.z}
local name = get_node(p).name
if name == "air" or name == "default:water_source" or name == "default:lava_source" then
top = y
break
end
end
for y=top,y_min,-1 do
local p = {x=pos.x,y=y,z=pos.z}
local name = get_node(p).name
if name ~= "air" and name ~= "default:water_source" and name ~= "default:lava_source" then
ground = y
break
end
end
return ground
end
local function getFacedirs(pos, ground)
-- secondly: rotate the chest
-- find possible faces
local xp, xm, zp, zm
xp = minetest.get_node({x=pos.x+1, y=ground+1, z=pos.z})
xm = minetest.get_node({x=pos.x-1, y=ground+1, z=pos.z})
zp = minetest.get_node({x=pos.x, y=ground+1, z=pos.z+1})
zm = minetest.get_node({x=pos.x, y=ground+1, z=pos.z-1})
-- Set Facedirs
local facedirs = {}
if xp.name=="air" or xp.name=="default:water_source" then
table.insert(facedirs, minetest.dir_to_facedir({x=-1,y=0,z=0}))
end
if xm.name=="air" or xm.name=="default:water_source" then
table.insert(facedirs, minetest.dir_to_facedir({x=1,y=0,z=0}))
end
if zp.name=="air" or zp.name=="default:water_source" then
table.insert(facedirs, minetest.dir_to_facedir({x=0,y=0,z=-1}))
end
if zm.name=="air" or zm.name=="default:water_source" then
table.insert(facedirs, minetest.dir_to_facedir({x=0,y=0,z=1}))
end
return facedirs
end
local function placeChest(pos, chest_pos, ground, nn)
local chest = {name = "tsm_chests:chest"}
local facedirs = getFacedirs(pos, ground)
-- choose a random face (if possible)
if #facedirs == 0 then
minetest.set_node({x=pos.x,y=ground+1, z=pos.z+1}, {name=nn})
chest.param2 = minetest.dir_to_facedir({x=0,y=0,z=1})
else
chest.param2 = facedirs[math.floor(math.random(#facedirs))]
end
-- Lastly: place the chest
minetest.set_node(chest_pos, chest)
-- Get treasure
local treasure_amount = math.ceil(math.random(t_min, t_max))
-- local height = math.abs(h_min) - math.abs(h_max)
-- local y_norm = (ground+1) - h_min
-- local scale = 1 - (y_norm/height)
local minp = 0 --scale*4 -- minimal preciousness: 0..4
local maxp = 10 --scale*4+2.1 -- maximum preciousness: 2.1..6.1
local treasures = treasurer.select_random_treasures(treasure_amount, minp, maxp)
-- Add Treasure to Chst
local meta = minetest.get_meta(chest_pos)
local inv = meta:get_inventory()
for i=1, #treasures do
inv:set_stack("main", i, treasures[i])
end
end
--[[ here comes the generation code
the interesting part which involes treasurer comes way below
]]
function place_chests(minp, maxp, seed, number_chests)
minp = {x=minp.x, y=minp.y, z=minp.z}
maxp = {x=maxp.x, y=maxp.y, z=maxp.z}
-- chests minimum and maximum spawn height
local height_min = water_level + h_min
local height_max = water_level + h_max
if(maxp.y < height_min or minp.y > height_max) then
return
end
local y_min = math.max(minp.y, height_min)
local y_max = math.min(maxp.y, height_max)
local attempts = 0
local chests_placed = 0
while chests_placed < number_chests and attempts < number_chests + 12 do
attempts = attempts + 1
local pos = {
x = math.random(minp.x, maxp.x),
y = minp.y,
z = math.random(minp.z, maxp.z)
}
-- Find ground level
local ground = findGroundLevel(pos, y_min, y_max)
if ground ~= nil then
local chest_pos = {x = pos.x, y = ground + 1, z = pos.z}
-- Don't spawn at barrier
if chest_pos.z == 0 then
chest_pos.z = -1
end
local nn = minetest.get_node(chest_pos).name -- chest node name (before it becomes a chest)
if nn == "air" or nn == "default:water_source" then
placeChest(pos, chest_pos, ground, nn)
chests_placed = chests_placed + 1
end
end
end
minetest.log("warning", "Spawned " .. chests_placed .. "/" .. number_chests .. " chests after " .. attempts .. " attempts!")
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 B

96
mods/other/vote/README.md Normal file
View file

@ -0,0 +1,96 @@
# Vote
A mod for Minetest adding an API to allow voting on servers.
Version 0.1
Created by [rubenwardy](http://rubenwardy.com)
Copyright (c) 2015, no rights reserved
Licensed under WTFPL or CC0 (you choose)
# Settings
* vote.maximum_active - maximum votes running at a time, votes are queued if it
reaches this. Defaults to 1.
# Example
```lua
minetest.register_chatcommand("vote_kick", {
privs = {
interact = true
},
func = function(name, param)
if not minetest.get_player_by_name(param) then
minetest.chat_send_player(name, "There is no player called '" ..
param .. "'")
end
vote.new_vote(name, {
description = "Kick player " .. param,
help = "/yes, /no or /abstain",
name = param,
duration = 60,
on_result = function(self, result, results)
if result == "yes" then
minetest.chat_send_all("Vote passed, " ..
#results.yes .. " to " .. #results.no .. ", " ..
self.name .. " will be kicked.")
minetest.kick_player(self.name, "The vote to kick you passed")
else
minetest.chat_send_all("Vote failed, " ..
#results.yes .. " to " .. #results.no .. ", " ..
self.name .. " remains ingame.")
end
end,
on_vote = function(self, name, value)
minetest.chat_send_all(name .. " voted " .. value .. " to '" ..
self.description .. "'")
end
})
end
})
```
# API
## Results
* voted - a key-value table. voted[name] = true if a player called name voted.
* abstain - a list of the names of players who abstained.
* <option> - a list of the names of players who voted for this option.
For example:
```lua
results = {
voted = {
one = true,
two = true,
three = true,
four = true
}
yes = {"one", "three"},
no = {"two"}
abstain = {"four"}
}
```
## Values
* description - required.
* help - recommended. How to respond to the vote.
* duration - the duration of the vote, before it expires.
* perc_needed - if yes/no, this is the percentage needed to pass.
* options - a list of possible options. (not fully supported yet)
## Methods
* can_vote(self, name) - return true if player `name` can vote on this issue.
* on_start(self) - called when vote starts. Return false to cancel.
* on_decide(self, results) - see results section. Return the winning result.
* on_result(self, result, results) - when vote ends, result is the winning result
* on_vote(self, name, value) - called when a player casts a vote
* on_abstain(self, name) - called when a player abstains

View file

@ -0,0 +1,48 @@
-- HudKit, by rubenwardy
-- License: Either WTFPL or CC0, you can choose.
local function hudkit()
return {
players = {},
add = function(self, player, id, def)
local name = player:get_player_name()
local elements = self.players[name]
if not elements then
self.players[name] = {}
elements = self.players[name]
end
elements[id] = player:hud_add(def)
end,
exists = function(self, player, id)
local elements = self.players[player:get_player_name()]
return elements and elements[id]
end,
change = function(self, player, id, stat, value)
local elements = self.players[player:get_player_name()]
if not elements or not elements[id] then
return false
end
player:hud_change(elements[id], stat, value)
return true
end,
remove = function(self, player, id)
local elements = self.players[player:get_player_name()]
if not elements or not elements[id] then
return false
end
player:hud_remove(elements[id])
elements[id] = nil
return true
end
}
end
return hudkit

314
mods/other/vote/init.lua Normal file
View file

@ -0,0 +1,314 @@
vote = {
active = {},
queue = {},
}
function vote.new_vote(creator, voteset)
local max_votes = tonumber(minetest.settings:get("vote.maximum_active")) or 1
local max_queue = tonumber(minetest.settings:get("vote.maximum_active")) or 0
if #vote.active < max_votes then
vote.start_vote(voteset)
return true, "Vote Started. You still need to vote: " .. voteset.help
elseif max_queue == 0 then
return false, "A vote is already running, please try again later."
elseif #vote.queue < max_queue then
table.insert(vote.queue, voteset)
return true, "Vote queued until there is less then " .. max_votes ..
" votes active."
else
return false, "The queue of votes waiting to run is full. Please try again later."
end
end
function vote.start_vote(voteset)
minetest.log("action", "Vote started: " .. voteset.description)
table.insert(vote.active, voteset)
-- Build results table
voteset.results = {
abstain = {},
voted = {}
}
if voteset.options then
for _, option in pairs(voteset.options) do
voteset.results[option] = {}
print(" - " .. option)
end
else
voteset.results.yes = {}
voteset.results.no = {}
end
-- Run start callback
if voteset.on_start then
voteset:on_start()
end
-- Timer for end
if voteset.duration or voteset.time then
minetest.after(voteset.duration + 0.1, function()
vote.end_vote(voteset)
end)
end
-- Show HUD a.s.a.p.
vote.update_all_hud()
end
function vote.end_vote(voteset)
local removed = false
for i, voteset2 in pairs(vote.active) do
if voteset == voteset2 then
table.remove(vote.active, i, 1)
removed = true
end
end
if not removed then
return
end
local result = nil
if voteset.on_decide then
result = voteset:on_decide(voteset.results)
elseif voteset.results.yes and voteset.results.no then
local total = #voteset.results.yes + #voteset.results.no
local perc_needed = voteset.perc_needed or 0.5
if #voteset.results.yes / total > perc_needed then
result = "yes"
else
result = "no"
end
end
minetest.log("action", "Vote '" .. voteset.description ..
"' ended with result '" .. result .. "'.")
if voteset.on_result then
voteset:on_result(result, voteset.results)
end
local max_votes = tonumber(minetest.settings:get("vote.maximum_active")) or 1
if #vote.active < max_votes and #vote.queue > 0 then
local nextvote = table.remove(vote.queue, 1)
vote.start_vote(nextvote)
else
-- Update HUD a.s.a.p.
vote.update_all_hud()
end
end
function vote.get_next_vote(name)
for _, voteset in pairs(vote.active) do
if not voteset.results.voted[name] then
return voteset
end
end
return nil
end
function vote.check_vote(voteset)
local all_players_voted = true
local players = minetest.get_connected_players()
for _, player in pairs(players) do
local name = player:get_player_name()
if not voteset.results.voted[name] then
all_players_voted = false
break
end
end
if all_players_voted then
vote.end_vote(voteset)
end
end
function vote.vote(voteset, name, value)
if not voteset.results[value] then
return
end
minetest.log("action", name .. " voted '" .. value .. "' to '"
.. voteset.description .. "'")
table.insert(voteset.results[value], name)
voteset.results.voted[name] = true
if voteset.on_vote then
voteset:on_vote(name, value)
end
vote.check_vote(voteset)
end
minetest.register_privilege("vote_admin", {
description = "Allows a player to manage running votes."
})
function vote.clear()
vote.active = {}
vote.queue = {}
vote.update_all_hud()
end
minetest.register_chatcommand("vote_clear", {
privs = {
vote_admin = true,
},
func = vote.clear
})
local hudkit = dofile(minetest.get_modpath("vote") .. "/hudkit.lua")
vote.hud = hudkit()
function vote.update_hud(player)
local name = player:get_player_name()
local voteset = vote.get_next_vote(name)
if not voteset or not minetest.check_player_privs(name,
{interact=true, vote=true}) then
vote.hud:remove(player, "vote:desc")
vote.hud:remove(player, "vote:bg")
vote.hud:remove(player, "vote:help")
return
end
if not vote.hud:exists(player, "vote:bg") then
vote.hud:add(player, "vote:bg", {
hud_elem_type = "image",
position = {x = 1, y = 0.5},
scale = {x = 1, y = 1},
text = "vote_background.png",
offset = {x=-100, y = 10},
number = 0xFFFFFF
})
end
if vote.hud:exists(player, "vote:desc") then
vote.hud:change(player, "vote:desc", "text", voteset.description .. "?")
else
vote.hud:add(player, "vote:desc", {
hud_elem_type = "text",
position = {x = 1, y = 0.5},
scale = {x = 100, y = 100},
text = voteset.description .. "?",
offset = {x=-100, y = 0},
number = 0xFFFFFF
})
end
if voteset.help then
if vote.hud:exists(player, "vote:help") then
vote.hud:change(player, "vote:help", "text", voteset.help)
else
vote.hud:add(player, "vote:help", {
hud_elem_type = "text",
position = {x = 1, y = 0.5},
scale = {x = 100, y = 100},
text = voteset.help,
offset = {x=-100, y = 20},
number = 0xFFFFFF
})
end
else
vote.hud:remove(player, "vote:help")
end
end
minetest.register_on_leaveplayer(function(player)
vote.hud.players[player:get_player_name()] = nil
end)
function vote.update_all_hud()
local players = minetest.get_connected_players()
for _, player in pairs(players) do
vote.update_hud(player)
end
minetest.after(5, vote.update_all_hud)
end
minetest.after(5, vote.update_all_hud)
minetest.register_privilege("vote", {
description = "Can vote on issues",
})
minetest.register_privilege("vote_starter", {
description = "Can start votes on issues",
})
minetest.register_chatcommand("yes", {
privs = {
interact = true,
vote = true
},
func = function(name, params)
local voteset = vote.get_next_vote(name)
if not voteset then
minetest.chat_send_player(name,
"There is no vote currently running!")
return
elseif not voteset.results.yes then
minetest.chat_send_player(name, "The vote is not a yes/no one.")
return
elseif voteset.can_vote and not voteset:can_vote(name) then
minetest.chat_send_player(name,
"You can't vote in the currently active vote!")
return
end
vote.vote(voteset, name, "yes")
end
})
minetest.register_chatcommand("no", {
privs = {
interact = true,
vote = true
},
func = function(name, params)
local voteset = vote.get_next_vote(name)
if not voteset then
minetest.chat_send_player(name,
"There is no vote currently running!")
return
elseif not voteset.results.no then
minetest.chat_send_player(name, "The vote is not a yes/no one.")
return
elseif voteset.can_vote and not voteset:can_vote(name) then
minetest.chat_send_player(name,
"You can't vote in the currently active vote!")
return
end
vote.vote(voteset, name, "no")
end
})
minetest.register_chatcommand("abstain", {
privs = {
interact = true,
vote = true
},
func = function(name, params)
local voteset = vote.get_next_vote(name)
if not voteset then
minetest.chat_send_player(name,
"There is no vote currently running!")
return
elseif voteset.can_vote and not voteset:can_vote(name) then
minetest.chat_send_player(name,
"You can't vote in the currently active vote!")
return
end
table.insert(voteset.results.abstain, name)
voteset.results.voted[name] = true
if voteset.on_abstain then
voteset:on_abstain(name)
end
vote.check_vote(voteset)
end
})
local set = minetest.settings:get("vote.kick_vote")
if set == nil or minetest.is_yes(set) then
dofile(minetest.get_modpath("vote") .. "/vote_kick.lua")
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

View file

@ -0,0 +1,125 @@
vote.kick_cooldown = 600
local vlist = {} -- table storing player name, ip & lock status
minetest.register_privilege("vote_kick", {
description = "Can (start) vote to kick a player",
on_grant = function(name, granter)
minetest.log("warning", "Player '" .. name .. "' has been" ..
" granted 'vote_kick' by '" .. granter .. "'")
end,
on_revoke = function(name, revoker)
minetest.log("warning", "Player '" .. name .. "' has been" ..
" revoked of 'vote_kick' by '" .. revoker .. "'")
end
})
minetest.register_chatcommand("vote_kick", {
params = "<name>",
description = "Start a vote to kick a player",
privs = {
interact = true,
vote_kick = true,
},
func = function(name, param)
param = param:trim()
if param == "" then
return false, "Please specify a player name to be vote-kicked!"
end
if not minetest.get_player_by_name(param) then
return false, "There is no player called '" ..
param .. "'"
end
if minetest.check_player_privs(param, {kick = true, ban = true}) then
return false, param .. " is a moderator, and can't be kicked!"
end
minetest.log("warning", "Player '" .. name .. "' started a vote" ..
" to kick '" .. param .. "'")
return vote.new_vote(name, {
description = "Kick " .. param,
help = "/yes, /no or /abstain",
name = param,
duration = 60,
perc_needed = 0.8,
on_result = function(self, result, results)
if result == "yes" then
minetest.chat_send_all("Vote passed, " ..
#results.yes .. " to " .. #results.no .. ", " ..
self.name .. " will be kicked.")
minetest.kick_player(self.name,
("The vote to kick you passed.\n You have been temporarily banned" ..
" for %s minutes."):format(tostring(vote.kick_cooldown / 60)))
vlist[self.name].locked = true
minetest.after(vote.kick_cooldown, function()
vlist[self.name] = nil
end)
else
minetest.chat_send_all("Vote failed, " ..
#results.yes .. " to " .. #results.no .. ", " ..
self.name .. " remains ingame.")
end
end,
on_vote = function(self, voter, value)
minetest.chat_send_all(voter .. " voted " .. value .. " to '" ..
self.description .. "'")
end
})
end
})
minetest.register_chatcommand("unblock", {
params = "<name>",
description = "Unblock a vote-kicked player before the cooldown expires",
privs = {kick = true, ban = true},
func = function(name, param)
param = param:trim()
if param == "" then
return false, "Please specify a player name to be unblocked!"
end
if not minetest.get_player_by_name(param) then
return false, "Can't find player '" .. param .. "'"
end
if not vlist[name].locked then
return false, "Failed! " .. param .. " is not blocked"
end
vlist[name].locked = false
return true, param .. " has been successfully unblocked!"
end
})
minetest.register_on_joinplayer(function(player)
local name = player:get_player_name()
if not vlist[name] then
vlist[name] = {
ip = minetest.get_player_ip(name),
locked = false
}
end
end)
minetest.register_on_leaveplayer(function(player)
local name = player:get_player_name()
if not vlist[name].locked then
vlist[name] = nil
end
end)
minetest.register_on_prejoinplayer(function(name, ip)
if vlist[name] and vlist[name].locked then
return "Please wait until the vote cool down period has elapsed before rejoining!"
else
for k, v in pairs(vlist) do
if v.ip == ip and v.locked then
return "This IP has been temporarily blocked."..
" Please wait until the cool-down period has elapsed before rejoining."
end
end
end
end)

7
mods/other/wield3d/.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
## Generic ignorable patterns and files
*~
.*.swp
*bak*
tags
*.vim

View file

@ -0,0 +1,4 @@
[mod] 3d wielded items [wield3d]
================================
Copyright (C) 2013 Stuart Jones - WTFPL

View file

@ -0,0 +1,15 @@
[mod] 3d wielded items [wield3d]
================================
Mod Version: 0.3.0
Minetest Version: 0.4.12 or later
Decription: Visible 3d wielded items for Minetest
Depends: default
Makes hand wielded items visible to other players.
See wield3d.conf.example for config options (save as wield3d.conf)

View file

@ -0,0 +1,2 @@
default

131
mods/other/wield3d/init.lua Normal file
View file

@ -0,0 +1,131 @@
WIELD3D_INIT_DELAY = 2
WIELD3D_RETRY_TIME = 10
WIELD3D_UPDATE_TIME = 1
local modpath = minetest.get_modpath(minetest.get_current_modname())
local input = io.open(modpath.."/wield3d.conf", "r")
if input then
dofile(modpath.."/wield3d.conf")
input:close()
input = nil
end
dofile(modpath.."/location.lua")
local location = {
"Arm_Right", -- default bone
{x=0.2, y=6.5, z=3}, -- default position
{x=-100, y=225, z=90}, -- default rotation
}
local player_wielding = {}
local timer = 0
local function add_wield_entity(player)
local name = player:get_player_name()
local pos = player:getpos()
local inv = player:get_inventory()
if name and pos and inv then
local offset = {x=pos.x, y=pos.y + 0.5, z=pos.z}
local object = minetest.add_entity(offset, "wield3d:wield_entity")
if object then
object:set_properties({collisionbox={0,0,0, 0,0,0}})
object:set_attach(player, location[1], location[2], location[3])
local entity = object:get_luaentity()
if entity then
entity.player = player
player_wielding[name] = 1
else
object:remove()
end
end
end
end
minetest.register_item("wield3d:hand", {
type = "none",
wield_image = "wield3d_trans.png",
})
minetest.register_entity("wield3d:wield_entity", {
physical = false,
collisionbox = {-0.125,-0.125,-0.125, 0.125,0.125,0.125},
visual = "wielditem",
visual_size = {x=0.25, y=0.25},
textures = {"wield3d:hand"},
player = nil,
item = nil,
timer = 0,
location = {location[1], location[2], location[3]},
on_step = function(self, dtime)
self.timer = self.timer + dtime
if self.timer < WIELD3D_UPDATE_TIME then
return
end
self.timer = 0
local player = self.player
if player then
local name = player:get_player_name()
local p1 = player:getpos()
local p2 = self.object:getpos()
if p1 and p2 then
if vector.equals(p1, p2) then
local stack = player:get_wielded_item()
local item = stack:get_name() or ""
if item == self.item then
return
end
if minetest.get_modpath("wieldview") then
local def = minetest.registered_items[item] or {}
if def.inventory_image ~= "" then
item = ""
end
end
self.item = item
if item == "" then
item = "wield3d:hand"
end
local loc = wield3d_location[item] or location
if loc[1] ~= self.location[1]
or vector.equals(loc[2], self.location[2]) == false
or vector.equals(loc[3], self.location[3]) == false then
self.object:set_detach()
self.object:set_attach(player, loc[1], loc[2], loc[3])
self.location = {loc[1], loc[2], loc[3]}
end
self.object:set_properties({textures={item}})
return
end
end
player_wielding[name] = 0
end
self.object:remove()
end,
})
minetest.register_globalstep(function(dtime)
timer = timer + dtime
if timer > WIELD3D_RETRY_TIME then
for name, state in pairs(player_wielding) do
if state == 0 then
local player = minetest.get_player_by_name(name)
if player then
add_wield_entity(player)
else
player_wielding[name] = nil
end
end
end
timer = 0
end
end)
minetest.register_on_leaveplayer(function(player)
local name = player:get_player_name()
if name then
player_wielding[name] = nil
end
end)
minetest.register_on_joinplayer(function(player)
player_wielding[player:get_player_name()] = 0
minetest.after(WIELD3D_INIT_DELAY, add_wield_entity, player)
end)

View file

@ -0,0 +1,32 @@
-- Wielded Item Location Overrides - [item_name] = {bone, position, rotation}
local bone = "Arm_Right"
wield3d_location = {
["default:torch"] = {bone, {x=0.2, y=6.5, z=3}, {x=-100, y=182, z=83}},
["default:sapling"] = {bone, {x=0.2, y=6.5, z=3}, {x=-100, y=182, z=83}},
["flowers:dandelion_white"] = {bone, {x=0.2, y=6.5, z=3}, {x=-100, y=182, z=83}},
["flowers:dandelion_yellow"] = {bone, {x=0.2, y=6.5, z=3}, {x=-100, y=182, z=83}},
["flowers:geranium"] = {bone, {x=0.2, y=6.5, z=3}, {x=-100, y=182, z=83}},
["flowers:rose"] = {bone, {x=0.2, y=6.5, z=3}, {x=-100, y=182, z=83}},
["flowers:tulip"] = {bone, {x=0.2, y=6.5, z=3}, {x=-100, y=182, z=83}},
["flowers:viola"] = {bone, {x=0.2, y=6.5, z=3}, {x=-100, y=182, z=83}},
["default:shovel_wood"] = {bone, {x=0, y=6.5, z=3}, {x=-90, y=137, z=83}},
["default:shovel_stone"] = {bone, {x=0, y=6.5, z=3}, {x=-90, y=137, z=83}},
["default:shovel_steel"] = {bone, {x=0, y=6.5, z=3}, {x=-90, y=137, z=83}},
["default:shovel_bronze"] = {bone, {x=0, y=6.5, z=3}, {x=-90, y=137, z=83}},
["default:shovel_mese"] = {bone, {x=0, y=6.5, z=3}, {x=-90, y=137, z=83}},
["default:shovel_diamond"] = {bone, {x=0, y=6.5, z=3}, {x=-90, y=137, z=83}},
["bucket:bucket_empty"] = {bone, {x=0, y=6.5, z=3}, {x=-90, y=137, z=83}},
["bucket:bucket_water"] = {bone, {x=0, y=6.5, z=3}, {x=-90, y=137, z=83}},
["bucket:bucket_lava"] = {bone, {x=0, y=6.5, z=3}, {x=-90, y=137, z=83}},
["screwdriver:screwdriver"] = {bone, {x=0, y=6.5, z=3}, {x=-90, y=137, z=83}},
["screwdriver:screwdriver1"] = {bone, {x=0, y=6.5, z=3}, {x=-90, y=137, z=83}},
["screwdriver:screwdriver2"] = {bone, {x=0, y=6.5, z=3}, {x=-90, y=137, z=83}},
["screwdriver:screwdriver3"] = {bone, {x=0, y=6.5, z=3}, {x=-90, y=137, z=83}},
["screwdriver:screwdriver4"] = {bone, {x=0, y=6.5, z=3}, {x=-90, y=137, z=83}},
["vessels:glass_bottle"] = {bone, {x=0, y=6.5, z=3}, {x=-90, y=137, z=83}},
["vessels:drinking_glass"] = {bone, {x=0, y=6.5, z=3}, {x=-90, y=137, z=83}},
["vessels:steel_bottle"] = {bone, {x=0, y=6.5, z=3}, {x=-90, y=137, z=83}},
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

View file

@ -0,0 +1,11 @@
-- Wield3d Configuration (defaults)
-- Increase this if you get non-attachment glitches when a player first joins.
WIELD3D_INIT_DELAY = 1
-- Number of seconds before retry if initialization fails.
WIELD3D_RETRY_TIME = 10
-- How often player wield items are updated.
WIELD3D_UPDATE_TIME = 1