Lua scripts pack. It is a part of PvPGN now.

First large implementation in Lua is Trivia Quiz Game (/quiz command) http://i.imgur.com/8QV3blt.png
This commit is contained in:
HarpyWar 2014-05-26 16:17:51 +04:00
parent 4d862593b4
commit ee04fdd23d
36 changed files with 7534 additions and 22 deletions

View file

@ -30,5 +30,9 @@ option(WITH_PGSQL "include PostgreSQL user accounts support" OFF)
option(WITH_ODBC "include ODBC user accounts support" OFF)
include(ConfigureChecks.cmake)
subdirs(src conf man files lua)
subdirs(src conf man files)
if(WITH_LUA)
add_subdirectory(lua)
endif(WITH_LUA)
ENABLE_TESTING()

View file

@ -162,7 +162,7 @@
--------------------------------------------------------
/games all
Displays a list of all games.
/games [l]obby
/games l[obby]
Displays a list of games in lobby.
%channels chs
@ -383,11 +383,11 @@
--------------------------------------------------------
/mail <command> [options]
--------------------------------------------------------
/mail [s]end <receiver> <message>
/mail s[end] <receiver> <message>
Sends mail to <receiver> with <message>.
/mail [r]ead [index]
/mail r[ead] [index]
Reads mail [index]
/mail [del]ete {all|<index>}
/mail del[ete] {all|<index>}
Deletes mail <index> or [all] mail.
%flag
@ -411,7 +411,7 @@
--------------------------------------------------------
/ipban <command> [option] [time]
--------------------------------------------------------
/ipban [l[ist]]
/ipban l[ist]
Displays a list of banned IP addresses
/ipban c[heck] <IP>
Checks if IP address <IP> is banned or not.
@ -460,25 +460,25 @@
Create a new clan (max <clantag> length = 4; spaces are allowed in <clanname>)
Commands for clan members:
/clan [m]sg <message> (alias [w]hisper)
/clan m[sg] <message> (alias [w]hisper)
Whispers a message to all your fellow clan members
/clan [inv]ite <username>
/clan inv[ite] <username>
Invite <username> to your clan.
/clan [inv]ite get
/clan inv[ite] get
Show clanname which you have been invited
/clan [inv]ite accept
/clan inv[ite] accept
Accept invitation to clan
/clan [inv]ite decline
/clan inv[ite] decline
Decline invitation to clan
Commands for clan chieftain:
/clan motd <message>
Update the clan's Message of the Day to <message>.
/clan [pub]lic (alias: pub)
/clan pub[lic] (alias: pub)
Opens the clan channel up to the public so that anyone may enter.
/clan [priv]ate (alias: priv)
/clan priv[ate] (alias: priv)
Closes the clan channel such that only members of the clan may enter.
/clan [dis]band
/clan dis[band]
Disband your clan.
%ping p latency
@ -589,13 +589,27 @@ Commands for clan chieftain:
Use /icon without [name] to display list of available icons in your stash.
--------------------------------------------------------
Syntax for operator/admin:
/icon [a]dd <username> <icon>
/icon a[dd] <username> <icon>
Add icon into user stash
/icon [d]el <username> <icon>
/icon d[el] <username> <icon>
Remove icon from user stash
/icon [s]et <username> <icon>
/icon s[et] <username> <icon>
Set custom icon to user without adding it in user stash
/icon [l]ist <username>
/icon l[ist] <username>
Display icons in user's stash
/icon [l]ist
/icon l[ist]
Display availaible icons in server stash that can be assigned to users
%quiz
--------------------------------------------------------
/quiz <command> [option]
Trivia Quiz Game
--------------------------------------------------------
/quiz start <name>
Start game with given dictionary name in current channel
/quiz stop
Finish game by force
/quiz stats [username]
Display record statistics for user
/quiz stats
Display Top records

View file

@ -1,4 +1,17 @@
# TODO: add lua files here
# copy all files from lua directory
file(GLOB DEPLOY_FILES_AND_DIRS "${PROJECT_SOURCE_DIR}/lua/*")
foreach(ITEM ${DEPLOY_FILES_AND_DIRS})
IF( IS_DIRECTORY "${ITEM}" )
LIST( APPEND DIRS_TO_DEPLOY "${ITEM}" )
ELSE()
IF(NOT ${ITEM} MATCHES "CMakeLists.txt")
LIST( APPEND FILES_TO_DEPLOY "${ITEM}" )
ENDIF(NOT ${ITEM} MATCHES "CMakeLists.txt")
ENDIF()
endforeach()
INSTALL( FILES ${FILES_TO_DEPLOY} DESTINATION ${LOCALSTATEDIR}/lua )
INSTALL( DIRECTORY ${DIRS_TO_DEPLOY} DESTINATION ${LOCALSTATEDIR}/lua )
install(FILES
DESTINATION ${LOCALSTATEDIR}/lua)

32
lua/command/redirect.lua Normal file
View file

@ -0,0 +1,32 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- Send text to user from server. Works like /announce,
-- but directly to user and message text is not red.
-- /redirect <username> <message>
function command_redirect(account, text)
local args = split_command(text, 2)
if not args[1] or not args[2] then
api.describe_command(account.name, args[0])
return 1
end
-- get destination account
local dest = api.account_get_by_name(args[1])
if next(dest) == nil or dest.online == "false" then
api.message_send_text(account.name, message_type_error, account.name, "User '" ..args[1].. "' is offline")
return 1
end
api.message_send_text(dest.name, message_type_info, dest.name, args[2])
return 1
end

29
lua/command/w3motd.lua Normal file
View file

@ -0,0 +1,29 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
--
-- Read file w3motd.txt line by line and send text to user
--
local username = nil
function command_w3motd(account, text)
-- allow warcraft 3 client only
if not (account.clienttag == "W3XP" or account.clienttag == "WAR3") then
return 0
end
username = account.name
local data = file_load(config.motdw3file, w3motd_sendline_callback)
return 1
end
function w3motd_sendline_callback(line)
api.message_send_text(username, message_type_info, nil, line)
end

23
lua/config.lua Normal file
View file

@ -0,0 +1,23 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- Config table can be extended here with your own variables
-- values are preloaded from bnetd.conf
config = {
-- Quiz settings
quiz = true,
quiz_filelist = "misc, dota, warcraft", -- display available files in "/quiz start"
quiz_competitive_mode = true, -- top players loses half of points which last player received; at the end top of records loses half of points which players received in current game
quiz_max_questions = 100, -- from start to end
quiz_question_delay = 5, -- delay before send next question
quiz_hint_delay = 20, -- delay between prompts
quiz_users_in_top = 15, -- how many users display in TOP list
quiz_channel = nil, -- (do not modify!) channel when quiz has started (it assigned with start)
}

26
lua/extend/account.lua Normal file
View file

@ -0,0 +1,26 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- Get count of all online users
function users_get_count()
local count = 0
for id,username in pairs(api.server_get_users()) do
count = count + 1
end
return count
end
-- Get count of all server account
function accounts_get_count()
local count = 0
for id,username in pairs(api.server_get_users(true)) do
count = count + 1
end
return count
end

516
lua/extend/account_wrap.lua Normal file
View file

@ -0,0 +1,516 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- All attribute get/set actions must have a wrapper to avoid write a wrong type into database
--
-- Profile
--
function account_get_acct_email(username)
return api.account_get_attr(username, "BNET\\acct\\email", attr_type_str)
end
function account_set_acct_email(username, value)
return api.account_set_attr(username, "BNET\\acct\\email", attr_type_str, value)
end
function account_get_auth_admin(username, channelname)
if channelname then
return api.account_get_attr(username, "BNET\\auth\\admin\\" .. channelname, attr_type_bool)
else
return api.account_get_attr(username, "BNET\\auth\\admin", attr_type_bool)
end
end
function account_set_auth_admin(username, channelname, value)
if channelname then
return api.account_set_attr(username, "BNET\\auth\\admin\\" .. channelname, attr_type_bool, value)
else
return api.account_set_attr(username, "BNET\\auth\\admin", attr_type_bool, value)
end
end
function account_get_auth_operator(username, channelname)
if channelname then
return api.account_get_attr(username, "BNET\\auth\\operator\\" .. channelname, attr_type_bool)
else
return api.account_get_attr(username, "BNET\\auth\\operator", attr_type_bool)
end
end
function account_set_auth_operator(username, channelname, value)
if channelname then
return api.account_set_attr(username, "BNET\\auth\\operator\\" .. channelname, attr_type_bool, value)
else
return api.account_set_attr(username, "BNET\\auth\\operator", attr_type_bool, value)
end
end
function account_get_auth_voice(username, channelname)
if channelname then
return api.account_get_attr(username, "BNET\\auth\\voice\\" .. channelname, attr_type_bool)
else
return api.account_get_attr(username, "BNET\\auth\\voice", attr_type_bool)
end
end
function account_set_auth_voice(username, channelname, value)
if channelname then
return api.account_set_attr(username, "BNET\\auth\\voice\\" .. channelname, attr_type_bool, value)
else
return api.account_set_attr(username, "BNET\\auth\\voice", attr_type_bool, value)
end
end
function account_is_operator_or_admin(username, channelname)
return account_get_auth_operator(username, channelname) or account_get_auth_admin(username, channelname) or account_get_auth_operator(username, nil) or account_get_auth_admin(username, nil)
end
function account_get_auth_announce(username)
return api.account_get_attr(username, "BNET\\auth\\announce", attr_type_bool)
end
function account_set_auth_announce(username, value)
return api.account_set_attr(username, "BNET\\auth\\announce", attr_type_bool, value)
end
function account_get_auth_botlogin(username)
return api.account_get_attr(username, "BNET\\auth\\botlogin", attr_type_bool)
end
function account_set_auth_botlogin(username, value)
return api.account_set_attr(username, "BNET\\auth\\botlogin", attr_type_bool, value)
end
function account_get_auth_lock(username)
return api.account_get_attr(username, "BNET\\auth\\lockk", attr_type_bool)
end
function account_set_auth_lock(username, value)
return api.account_set_attr(username, "BNET\\auth\\lockk", attr_type_bool, value)
end
function account_get_auth_locktime(username)
return api.account_get_attr(username, "BNET\\auth\\locktime", attr_type_int)
end
function account_set_auth_locktime(username, value)
return api.account_set_attr(username, "BNET\\auth\\locktime", attr_type_int, value)
end
function account_get_auth_lockreason(username)
return api.account_get_attr(username, "BNET\\auth\\lockreason", attr_type_str)
end
function account_set_auth_lockreason(username, value)
return api.account_set_attr(username, "BNET\\auth\\lockreason", attr_type_str, value)
end
function account_get_auth_lockby(username)
return api.account_get_attr(username, "BNET\\auth\\lockby", attr_type_str)
end
function account_set_auth_lockby(username, value)
return api.account_set_attr(username, "BNET\\auth\\lockby", attr_type_str, value)
end
function account_get_auth_mute(username)
return api.account_get_attr(username, "BNET\\auth\\mute", attr_type_bool)
end
function account_set_auth_mute(username, value)
return api.account_set_attr(username, "BNET\\auth\\mute", attr_type_bool, value)
end
function account_get_auth_mutetime(username)
return api.account_get_attr(username, "BNET\\auth\\mutetime", attr_type_int)
end
function account_set_auth_mutetime(username, value)
return api.account_set_attr(username, "BNET\\auth\\mutetime", attr_type_int, value)
end
function account_get_auth_mutereason(username)
return api.account_get_attr(username, "BNET\\auth\\mutereason", attr_type_str)
end
function account_set_auth_mutereason(username, value)
return api.account_set_attr(username, "BNET\\auth\\mutereason", attr_type_str, value)
end
function account_get_auth_muteby(username)
return api.account_get_attr(username, "BNET\\auth\\muteby", attr_type_str)
end
function account_set_auth_muteby(username, value)
return api.account_set_attr(username, "BNET\\auth\\muteby", attr_type_str, value)
end
function account_get_auth_command_groups(username)
return api.account_get_attr(username, "BNET\\auth\\command_groups", attr_type_int)
end
function account_set_auth_command_groups(username, value)
return api.account_set_attr(username, "BNET\\auth\\command_groups", attr_type_int, value)
end
function account_get_acct_lastlogin_time(username)
return api.account_get_attr(username, "BNET\\acct\\lastlogin_time", attr_type_int)
end
function account_set_acct_lastlogin_time(username, value)
return api.account_set_attr(username, "BNET\\acct\\lastlogin_time", attr_type_int, value)
end
function account_get_acct_lastlogin_owner(username)
return api.account_get_attr(username, "BNET\\acct\\lastlogin_owner", attr_type_str)
end
function account_set_acct_lastlogin_owner(username, value)
return api.account_set_attr(username, "BNET\\acct\\lastlogin_owner", attr_type_str, value)
end
function account_get_acct_createtime(username)
return api.account_get_attr(username, "BNET\\acct\\ctime", attr_type_int)
end
function account_set_acct_createtime(username, value)
return api.account_set_attr(username, "BNET\\acct\\ctime", attr_type_int, value)
end
function account_get_acct_lastlogin_clienttag(username)
return api.account_get_attr(username, "BNET\\acct\\lastlogin_clienttag", attr_type_str)
end
function account_set_acct_lastlogin_clienttag(username, value)
return api.account_set_attr(username, "BNET\\acct\\lastlogin_clienttag", attr_type_str, value)
end
function account_get_acct_lastlogin_ip(username)
return api.account_get_attr(username, "BNET\\acct\\lastlogin_ip", attr_type_str)
end
function account_set_acct_lastlogin_ip(username, value)
return api.account_set_attr(username, "BNET\\acct\\lastlogin_ip", attr_type_str, value)
end
function account_get_acct_passhash(username)
return api.account_get_attr(username, "BNET\\acct\\passhash1", attr_type_str)
end
function account_set_acct_passhash(username, value)
return api.account_set_attr(username, "BNET\\acct\\passhash1", attr_type_str, value)
end
function account_get_acct_verifier(username)
return api.account_get_attr(username, "BNET\\acct\\verifier", attr_type_raw)
end
function account_set_acct_verifier(username, value)
return api.account_set_attr(username, "BNET\\acct\\verifier", attr_type_raw, value)
end
function account_get_acct_salt(username)
return api.account_get_attr(username, "BNET\\acct\\salt", attr_type_raw)
end
function account_set_acct_salt(username, value)
return api.account_set_attr(username, "BNET\\acct\\salt", attr_type_raw, value)
end
--
-- Profile
--
function account_get_auth_adminnormallogin(username)
return api.account_get_attr(username, "BNET\\auth\\adminnormallogin", attr_type_bool)
end
function account_set_auth_adminnormallogin(username, value)
return api.account_set_attr(username, "BNET\\auth\\adminnormallogin", attr_type_bool, value)
end
function account_get_auth_changepass(username)
return api.account_get_attr(username, "BNET\\auth\\changepass", attr_type_bool)
end
function account_set_auth_changepass(username, value)
return api.account_set_attr(username, "BNET\\auth\\changepass", attr_type_bool, value)
end
function account_get_auth_changeprofile(username)
return api.account_get_attr(username, "BNET\\auth\\changeprofile", attr_type_bool)
end
function account_set_auth_changeprofile(username, value)
return api.account_set_attr(username, "BNET\\auth\\changeprofile", attr_type_bool, value)
end
function account_get_auth_createnormalgame(username)
return api.account_get_attr(username, "BNET\\auth\\createnormalgame", attr_type_bool)
end
function account_set_auth_createnormalgame(username, value)
return api.account_set_attr(username, "BNET\\auth\\createnormalgame", attr_type_bool, value)
end
function account_get_auth_joinnormalgame(username)
return api.account_get_attr(username, "BNET\\auth\\joinnormalgame", attr_type_bool)
end
function account_set_auth_joinnormalgame(username, value)
return api.account_set_attr(username, "BNET\\auth\\joinnormalgame", attr_type_bool, value)
end
function account_get_auth_createladdergame(username)
return api.account_get_attr(username, "BNET\\auth\\createladdergame", attr_type_bool)
end
function account_set_auth_createladdergame(username, value)
return api.account_set_attr(username, "BNET\\auth\\createladdergame", attr_type_bool, value)
end
function account_get_auth_joinladdergame(username)
return api.account_get_attr(username, "BNET\\auth\\joinladdergame", attr_type_bool)
end
function account_set_auth_joinladdergame(username, value)
return api.account_set_attr(username, "BNET\\auth\\joinladdergame", attr_type_bool, value)
end
--
-- Profile
--
function account_get_sex(username)
return api.account_get_attr(username, "profile\\sex", attr_type_str)
end
function account_set_sex(username, value)
return api.account_set_attr(username, "profile\\sex", attr_type_str, value)
end
function account_get_age(username)
return api.account_get_attr(username, "profile\\age", attr_type_str)
end
function account_set_age(username, value)
return api.account_set_attr(username, "profile\\age", attr_type_str, value)
end
function account_get_location(username)
return api.account_get_attr(username, "profile\\location", attr_type_str)
end
function account_set_location(username, value)
return api.account_set_attr(username, "profile\\location", attr_type_str, value)
end
function account_get_description(username)
return api.account_get_attr(username, "profile\\description", attr_type_str)
end
function account_set_description(username, value)
return api.account_set_attr(username, "profile\\description", attr_type_str, value)
end
--
-- Warcraft 3
--
function account_get_soloxp(username)
return api.account_get_attr(username, "Record\\W3XP\\solo_xp", attr_type_num)
end
function account_set_soloxp(username, value)
return api.account_set_attr(username, "Record\\W3XP\\solo_xp", attr_type_num, value)
end
function account_get_sololevel(username)
return api.account_get_attr(username, "Record\\W3XP\\solo_level", attr_type_num)
end
function account_set_sololevel(username, value)
return api.account_set_attr(username, "Record\\W3XP\\solo_level", attr_type_num, value)
end
function account_get_solowins(username)
return api.account_get_attr(username, "Record\\W3XP\\solo_wins", attr_type_num)
end
function account_set_solowins(username, value)
return api.account_set_attr(username, "Record\\W3XP\\solo_wins", attr_type_num, value)
end
function account_get_sololosses(username)
return api.account_get_attr(username, "Record\\W3XP\\solo_losses", attr_type_num)
end
function account_set_sololosses(username, value)
return api.account_set_attr(username, "Record\\W3XP\\solo_losses", attr_type_num, value)
end
function account_get_solorank(username)
return api.account_get_attr(username, "Record\\W3XP\\solo_rank", attr_type_num)
end
function account_set_solorank(username, value)
return api.account_set_attr(username, "Record\\W3XP\\solo_rank", attr_type_num, value)
end
--
-- Starcraft
--
function account_get_normal_wins(username)
return api.account_get_attr(username, "Record\\SEXP\\0\\wins", attr_type_num)
end
function account_set_normal_wins(username, value)
return api.account_set_attr(username, "Record\\SEXP\\0\\wins", attr_type_num, value)
end
function account_get_normal_losses(username)
return api.account_get_attr(username, "Record\\SEXP\\0\\losses", attr_type_num)
end
function account_set_normal_losses(username, value)
return api.account_set_attr(username, "Record\\SEXP\\0\\losses", attr_type_num, value)
end
function account_get_normal_draws(username)
return api.account_get_attr(username, "Record\\SEXP\\0\\draws", attr_type_num)
end
function account_set_normal_draws(username, value)
return api.account_set_attr(username, "Record\\SEXP\\0\\draws", attr_type_num, value)
end
function account_get_normal_disconnects(username)
return api.account_get_attr(username, "Record\\SEXP\\0\\disconnects", attr_type_num)
end
function account_set_normal_disconnects(username, value)
return api.account_set_attr(username, "Record\\SEXP\\0\\disconnects", attr_type_num, value)
end
function account_get_normal_last_time(username)
return api.account_get_attr(username, "Record\\SEXP\\0\\last game", attr_type_num)
end
function account_set_normal_last_time(username, value)
return api.account_set_attr(username, "Record\\SEXP\\0\\last game", attr_type_num, value)
end
function account_get_normal_last_result(username)
return api.account_get_attr(username, "Record\\SEXP\\0\\last game result", attr_type_num)
end
function account_set_normal_last_result(username, value)
return api.account_set_attr(username, "Record\\SEXP\\0\\last game result", attr_type_num, value)
end
function account_get_ladder_wins(username)
return api.account_get_attr(username, "Record\\SEXP\\1\\wins", attr_type_num)
end
function account_set_ladder_wins(username, value)
return api.account_set_attr(username, "Record\\SEXP\\1\\wins", attr_type_num, value)
end
function account_get_ladder_losses(username)
return api.account_get_attr(username, "Record\\SEXP\\1\\losses", attr_type_num)
end
function account_set_ladder_losses(username, value)
return api.account_set_attr(username, "Record\\SEXP\\1\\losses", attr_type_num, value)
end
function account_get_ladder_draws(username)
return api.account_get_attr(username, "Record\\SEXP\\1\\draws", attr_type_num)
end
function account_set_ladder_draws(username, value)
return api.account_set_attr(username, "Record\\SEXP\\1\\draws", attr_type_num, value)
end
function account_get_ladder_disconnects(username)
return api.account_get_attr(username, "Record\\SEXP\\1\\disconnects", attr_type_num)
end
function account_set_ladder_disconnects(username, value)
return api.account_set_attr(username, "Record\\SEXP\\1\\disconnects", attr_type_num, value)
end
function account_get_ladder_last_time(username)
return api.account_get_attr(username, "Record\\SEXP\\1\\last game", attr_type_num)
end
function account_set_ladder_last_time(username, value)
return api.account_set_attr(username, "Record\\SEXP\\1\\last game", attr_type_num, value)
end
function account_get_ladder_last_result(username)
return api.account_get_attr(username, "Record\\SEXP\\1\\last game result", attr_type_num)
end
function account_set_ladder_last_result(username, value)
return api.account_set_attr(username, "Record\\SEXP\\1\\last game result", attr_type_num, value)
end
function account_get_ladder_rating(username)
return api.account_get_attr(username, "Record\\SEXP\\1\\rating", attr_type_num)
end
function account_set_ladder_rating(username, value)
return api.account_set_attr(username, "Record\\SEXP\\1\\rating", attr_type_num, value)
end
function account_get_ladder_rank(username)
return api.account_get_attr(username, "Record\\SEXP\\1\\rank", attr_type_num)
end
function account_set_ladder_rank(username, value)
return api.account_set_attr(username, "Record\\SEXP\\1\\rank", attr_type_num, value)
end
-- TODO: wrappers for 2x2
--
-- Warcraft 2
--
--[[ Warcraft 2 has the same functions as Starcraft, but "W2BN" instead of "SEXP" in path. Replace it if you have a single 2 server. ]]--
--
-- Diablo
--
function account_get_normal_level(username)
return api.account_get_attr(username, "Record\\DRTL\\0\\level", attr_type_num)
end
function account_set_normal_level(username, value)
return api.account_set_attr(username, "Record\\DRTL\\0\\level", attr_type_num, value)
end
function account_get_normal_class(username)
return api.account_get_attr(username, "Record\\DRTL\\0\\class", attr_type_num)
end
function account_set_normal_class(username, value)
return api.account_set_attr(username, "Record\\DRTL\\0\\class", attr_type_num, value)
end
function account_get_normal_diablo_kills(username)
return api.account_get_attr(username, "Record\\DRTL\\0\\diablo kills", attr_type_num)
end
function account_set_normal_diablo_kills(username, value)
return api.account_set_attr(username, "Record\\DRTL\\0\\diablo kills", attr_type_num, value)
end
function account_get_normal_strength(username)
return api.account_get_attr(username, "Record\\DRTL\\0\\strength", attr_type_num)
end
function account_set_normal_strength(username, value)
return api.account_set_attr(username, "Record\\DRTL\\0\\strength", attr_type_num, value)
end
function account_get_normal_dexterity(username)
return api.account_get_attr(username, "Record\\DRTL\\0\\dexterity", attr_type_num)
end
function account_set_normal_dexterity(username, value)
return api.account_set_attr(username, "Record\\DRTL\\0\\dexterity", attr_type_num, value)
end
function account_get_normal_vitality(username)
return api.account_get_attr(username, "Record\\DRTL\\0\\vitality", attr_type_num)
end
function account_set_normal_vitality(username, value)
return api.account_set_attr(username, "Record\\DRTL\\0\\vitality", attr_type_num, value)
end
function account_get_normal_gold(username)
return api.account_get_attr(username, "Record\\DRTL\\0\\gold", attr_type_num)
end
function account_set_normal_gold(username, value)
return api.account_set_attr(username, "Record\\DRTL\\0\\gold", attr_type_num, value)
end
--
-- Westwood Online
--
function account_get_wol_apgar(username)
return api.account_get_attr(username, "Record\\WOL\\auth\\apgar", attr_type_str)
end
function account_set_wol_apgar(username, value)
return api.account_set_attr(username, "Record\\WOL\\auth\\apgar", attr_type_str, value)
end
function account_get_locale(username)
return api.account_get_attr(username, "Record\\WOL\\auth\\locale", attr_type_str)
end
function account_set_locale(username, value)
return api.account_set_attr(username, "Record\\WOL\\auth\\locale", attr_type_str, value)
end

43
lua/extend/channel.lua Normal file
View file

@ -0,0 +1,43 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- Return channel id by name (if channel not found then return -1)
function channel_get_id_by_name(channel_name)
for id,name in pairs(api.server_get_channels()) do
if name == channel_name then
return id
end
end
return -1
end
-- Send message in channel
-- message_type: message_type_info | message_type_error
function channel_send_message(channel_name, text, message_type)
channel_id = channel_get_id_by_name(channel_name)
if (channel_id == -1) then
return nil
end
channel = api.channel_get_by_id(channel_id)
for username in string.split(channel.memberlist,",") do
api.message_send_text(username, message_type, nil, text)
end
end
-- Get count of all channels
function channels_get_count()
local count = 0
for id,channelname in pairs(api.server_get_channels()) do
count = count + 1
end
return count
end

13
lua/extend/enum/attr.lua Normal file
View file

@ -0,0 +1,13 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
attr_type_str,
attr_type_num,
attr_type_bool,
attr_type_raw
= 0,1,2,3

View file

@ -0,0 +1,17 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
eventlog_level_none,
eventlog_level_trace,
eventlog_level_debug,
eventlog_level_info,
eventlog_level_warn,
eventlog_level_error,
eventlog_level_fatal,
eventlog_level_gui -- Win32 GUI
= 0,1,2,4,8,16,32,64

133
lua/extend/enum/game.lua Normal file
View file

@ -0,0 +1,133 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
game_type_none,
game_type_all,
game_type_topvbot,
game_type_melee,
game_type_ffa,
game_type_oneonone,
game_type_ctf,
game_type_greed,
game_type_slaughter,
game_type_sdeath,
game_type_ladder,
game_type_ironman,
game_type_mapset,
game_type_teammelee,
game_type_teamffa,
game_type_teamctf,
game_type_pgl,
game_type_diablo,
game_type_diablo2open,
game_type_diablo2closed,
game_type_anongame
= 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
game_status_started,
game_status_full,
game_status_open,
game_status_loaded,
game_status_done
= 0,1,2,3,4
game_result_none,
game_result_win,
game_result_loss,
game_result_draw,
game_result_disconnect,
game_result_observer,
game_result_playing
= 0,1,2,3,4,5,6
game_option_none,
game_option_melee_normal,
game_option_ffa_normal,
game_option_oneonone_normal,
game_option_ctf_normal,
game_option_greed_10000,
game_option_greed_7500,
game_option_greed_5000,
game_option_greed_2500,
game_option_slaughter_60,
game_option_slaughter_45,
game_option_slaughter_30,
game_option_slaughter_15,
game_option_sdeath_normal,
game_option_ladder_countasloss,
game_option_ladder_nopenalty,
game_option_mapset_normal,
game_option_teammelee_4,
game_option_teammelee_3,
game_option_teammelee_2,
game_option_teamffa_4,
game_option_teamffa_3,
game_option_teamffa_2,
game_option_teamctf_4,
game_option_teamctf_3,
game_option_teamctf_2,
game_option_topvbot_7,
game_option_topvbot_6,
game_option_topvbot_5,
game_option_topvbot_4,
game_option_topvbot_3,
game_option_topvbot_2,
game_option_topvbot_1
= 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32
game_maptype_none,
game_maptype_selfmade,
game_maptype_blizzard,
game_maptype_ladder,
game_maptype_pgl,
game_maptype_kbk,
game_maptype_compusa
= 0,1,2,3,4,5,6
game_tileset_none,
game_tileset_badlands,
game_tileset_space,
game_tileset_installation,
game_tileset_ashworld,
game_tileset_jungle,
game_tileset_desert,
game_tileset_ice,
game_tileset_twilight
= 0,1,2,3,4,5,6,7,8,9
game_speed_none,
game_speed_slowest,
game_speed_slower,
game_speed_slow,
game_speed_normal,
game_speed_fast,
game_speed_faster,
game_speed_fastest
= 0,1,2,3,4,5,6,7
game_difficulty_none,
game_difficulty_normal,
game_difficulty_nightmare,
game_difficulty_hell,
game_difficulty_hardcore_normal,
game_difficulty_hardcore_nightmare,
game_difficulty_hardcore_hell
= 0,1,2,3,4,5,6
game_flag_none,
game_flag_private
= 0,1

View file

@ -0,0 +1,46 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
message_type_adduser,
message_type_join,
message_type_part,
message_type_whisper,
message_type_talk,
message_type_broadcast,
message_type_channel,
message_type_userflags,
message_type_whisperack,
message_type_friendwhisperack,
message_type_channelfull,
message_type_channeldoesnotexist,
message_type_channelrestricted,
message_type_info,
message_type_error,
message_type_emote,
message_type_uniqueid, -- bad message type?
message_type_mode, -- bad message type?
message_type_kick,
message_type_quit,
-- IRC specific messages
message_type_nick,
message_type_notice,
message_type_namreply,
message_type_topic,
-- Westwood Online Extensions
message_type_host,
message_type_invmsg,
message_type_page,
message_type_wol_joingame,
message_type_gameopt_talk,
message_type_gameopt_whisper,
message_type_wol_start_game,
message_type_wol_advertr,
message_type_wol_chanchk,
message_type_wol_userip,
message_type_null
= 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34

View file

@ -0,0 +1,51 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- https://github.com/HarpyWar/pvpgn/issues/15
-- http://msdn.microsoft.com/en-us/library/windows/desktop/ms645505(v=vs.85).aspx
--
-- use math_or(mb_type1, mb_type2) to combine two messagebox types
--[[ Example code to display Windows MessageBox:
local mb_type = math_or(MB_DEFBUTTON2, math_or(MB_ABORTRETRYIGNORE, MB_ICONEXCLAMATION) )
api.messagebox_show(account.name, "aaaaaaaaaaaaaaaaaaaaaaaaawwadwwadawdawdawdwadawdaaw", "MessageBox from " .. config.servername, mb_type)
]]--
MB_ABORTRETRYIGNORE,
MB_CANCELTRYCONTINUE,
MB_HELP,
MB_OK,
MB_OKCANCEL,
MB_RETRYCANCEL,
MB_YESNO,
MB_YESNOCANCEL,
MB_ICONEXCLAMATION,
MB_ICONWARNING,
MB_ICONINFORMATION,
MB_ICONASTERISK,
MB_ICONQUESTION,
MB_ICONSTOP,
MB_ICONERROR,
MB_ICONHAND,
MB_DEFBUTTON1,
MB_DEFBUTTON2,
MB_DEFBUTTON3,
MB_DEFBUTTON4,
MB_APPLMODAL,
MB_SYSTEMMODAL,
MB_TASKMODAL,
MB_DEFAULT_DESKTOP_ONLY,
MB_RIGHT,
MB_RTLREADING,
MB_SETFOREGROUND,
MB_TOPMOST,
MB_SERVICE_NOTIFICATION
= 0x00000002,0x00000006,0x00004000,0x00000000,0x00000001,0x00000005,0x00000004,0x00000003,0x00000030,0x00000030,0x00000040,0x00000040,0x00000020,0x00000010,0x00000010,0x00000010,0x00000000,0x00000100,0x00000200,0x00000300,0x00000000,0x00001000,0x00002000,0x00020000,0x00080000,0x00100000,0x00010000,0x00040000,0x00200000

17
lua/extend/game.lua Normal file
View file

@ -0,0 +1,17 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- Get count of all games
function games_get_count()
local count = 0
for id,gamename in pairs(api.server_get_games()) do
count = count + 1
end
return count
end

15
lua/extend/message.lua Normal file
View file

@ -0,0 +1,15 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- Send announce to all connected users
function message_send_all(text)
for id,username in pairs(api.server_get_users()) do
api.message_send_text(username, message_type_broadcast, nil, text)
end
end

22
lua/handle_channel.lua Normal file
View file

@ -0,0 +1,22 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
function handle_channel_message(channel, account, text, message_type)
if config.quiz and channel.name == config.quiz_channel then
quiz_handle_message(account.name, text)
end
--api.eventlog(eventlog_level_gui, __FUNCTION__, text)
--return 1
end
function handle_channel_userjoin(channel, account)
--api.eventlog(eventlog_level_gui, __FUNCTION__, account.name.." joined "..channel.name)
end
function handle_channel_userleft(channel, account)
--api.eventlog(eventlog_level_gui, __FUNCTION__, account.name.." left "..channel.name)
end

83
lua/handle_command.lua Normal file
View file

@ -0,0 +1,83 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- List of available lua commands
-- (To create a new command - create a new file in directory "commands")
local lua_command_table = {
[1] = {
["/w3motd"] = command_w3motd,
-- Quiz
["/quiz"] = command_quiz,
},
[8] = {
["/redirect"] = command_redirect,
},
}
-- Global function to handle commands
-- ("return 1" from a command will break next C++ code execution)
function handle_command(account, text)
-- find command in table
for cg,cmdlist in pairs(lua_command_table) do
for cmd,func in pairs(cmdlist) do
if string.starts(text, cmd) then
-- check if command group is in account.commandgroups
if not math_and(account.commandgroups, cg) then
api.message_send_text(account.name, message_type_error, account.name, "This command is reserved for admins.")
return 1
end
-- FIXME: we can use _G[func] if func is a text but not a function,
-- like ["/dotastats"] = "command_dotastats"
-- and function command_dotastats can be defined below, not only before
return func(account, text)
end
end
end
return 0
end
-- Split command to arguments,
-- index 0 is always a command name without a slash
-- return table with arguments
function split_command(text, args_count)
local count = args_count
local result = {}
local tmp = ""
-- remove slash from the command
if not string:empty(text) then
text = string.sub(text, 2)
end
i = 0
-- split by space
for token in string.split(text) do
if not string:empty(token) then
if (i < count) then
result[i] = token
i = i + 1
else
if not string:empty(tmp) then
tmp = tmp .. " "
end
tmp = tmp .. token
end
end
end
-- push remaining text at the end
if not string:empty(tmp) then
result[count] = tmp
end
return result
end

63
lua/handle_game.lua Normal file
View file

@ -0,0 +1,63 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- Global function to handle game create
function handle_game_create(game)
--for i,j in pairs(game) do
-- api.message_send_text(game.owner, message_type_info, game.owner, i.." = "..j)
--end
end
-- Global function to handle user join to game
function handle_game_userjoin(game, account)
--for i,j in pairs(game) do
-- message_send_text(account.name, message_type_info, account.name, i.." = "..j)
--end
end
-- Global function to handle user left from game
function handle_game_userleft(game, account)
--for username in string.split(str,",") do
-- if (account.name ~= username) then
-- api.message_send_text(username, message_type_whisper, nil, "Bye ".. account.name)
-- end
--end
end
-- Global function to handle game end
function handle_game_end(game)
--api.message_send_text(game.owner, message_type_whisper, nil, "End game")
end
-- Global function to handle game report
function handle_game_report(game)
--for i,j in pairs(game) do
-- api.message_send_text("harpywar", message_type_info, game.owner, i.." = "..j)
-- api.message_send_text(game.owner, message_type_info, game.owner, i.." = "..j)
--end
--api.eventlog(eventlog_level_gui, __FUNCTION__, game.last_access)
end
-- Global function to handle game destroy
function handle_game_destroy(game)
--api.message_send_text(game.owner, message_type_whisper, nil, "Destroy game")
end
-- Global function to handle game status
function handle_game_changestatus(game)
--api.message_send_text(game.owner, message_type_info, nil, "Change status of the game to ".. game.status)
end

17
lua/handle_server.lua Normal file
View file

@ -0,0 +1,17 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- Loop each second
function handle_server_mainloop()
-- Tick all timers
for t in pairs(__timers) do
__timers[t]:tick()
end
-- api.eventlog(eventlog_level_gui, __FUNCTION__, os.time())
end

20
lua/handle_user.lua Normal file
View file

@ -0,0 +1,20 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
function handle_user_whisper(account_src, account_dst, text)
--api.eventlog(eventlog_level_gui, __FUNCTION__, account_src.name.."->"..account_dst.name.. ": ".. text)
--return 1;
end
function handle_user_login(account)
--api.eventlog(eventlog_level_gui, __FUNCTION__, account.name.." logged in")
--return 1;
end
function handle_user_disconnect(account)
--api.eventlog(eventlog_level_gui, __FUNCTION__, account.name.." disconnected")
end

33
lua/include/bitwise.lua Normal file
View file

@ -0,0 +1,33 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
--
-- Bitwise functions:
-- math_not, math_and, math_or, math_xor
--
local function nand(x,y,z)
z=z or 2^16
if z<2 then
return 1-x*y
else
return nand((x-x%z)/z,(y-y%z)/z,math.sqrt(z))*z+nand(x%z,y%z,math.sqrt(z))
end
end
function math_not(y,z)
return nand(nand(0,0,z),y,z)
end
function math_and(x,y,z)
return nand(math_not(0,z),nand(x,y,z),z)
end
function math_or(x,y,z)
return nand(math_not(x,z),math_not(y,z),z)
end
function math_xor(x,y,z)
return math_and(nand(x,y,z),math_or(x,y,z),z)
end

19
lua/include/common.lua Normal file
View file

@ -0,0 +1,19 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- Return the script path and line of the function where it is executed
__FUNCTION__ = nil
setmetatable(_G, {__index =
function(t, k)
if k == '__FUNCTION__' then
local w = debug.getinfo(2, "S")
return w.short_src..":"..w.linedefined
end
end
})

76
lua/include/file.lua Normal file
View file

@ -0,0 +1,76 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- Read file line by line
-- callback 2 is optional
-- You can process line with function callback1(line, callback2)
function file_load(filename, callback1, callback2)
local file = io.open(filename, "r")
if file then
for line in file:lines() do
if callback2 then
callback1(line, callback2)
else
callback1(line)
end
end
file.close(file)
api.eventlog(eventlog_level_trace, __FUNCTION__, "File readed " .. filename)
else
api.eventlog(eventlog_level_error, __FUNCTION__, "Could not open file " .. filename)
return false
end
return true
end
-- (callback) for "file_load" to load file with each line format like "key = value"
function file_load_dictionary_callback(line, callback)
if string:empty(line) then return 0 end
local idx = 0
local a, b
for v in string.split(line, "=") do
if idx == 0 then
a = string:trim(v)
else
b = string:trim(v)
end
idx = idx + 1
end
if not string:empty(b) and not string:empty(a) then
callback(a, b)
end
end
-- Save raw text "data" into a filename
function file_save(data, filename)
local file = io.open(filename, "w")
file:write(data)
file:close()
end
-- Save file using callback
function file_save2(filename, callback)
local file = io.open(filename, "w")
callback(file)
file:close()
end
-- Check file for exist
function file_exists(filename)
local f=io.open(filename, "r")
if f~=nil then io.close(f) return true else return false end
end

59
lua/include/string.lua Normal file
View file

@ -0,0 +1,59 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- Split text into table by delimeter
-- Usage example: string.split("one,two",",")
function string:split(str)
str = str or '%s+'
local st, g = 1, self:gmatch("()("..str..")")
local function getter(segs, seps, sep, cap1, ...)
st = sep and seps + #sep
return self:sub(segs, (seps or 0) - 1), cap1 or sep, ...
end
return function() if st then return getter(st, g()) end end
end
-- Check string is nil or empty
-- bool
function string:empty(str)
return str == nil or str == ''
end
-- bool
function string.starts(str, starts)
if string:empty(str) then return false end
return string.sub(str,1,string.len(starts))==starts
end
-- bool
function string.ends(str, ends)
if string:empty(str) then return false end
return ends=='' or string.sub(str,-string.len(ends))==ends
end
-- Replace string
function string.replace(str, pattern, replacement)
if string:empty(str) then return str end
local s, n = string.gsub(str, pattern, replacement)
return s
end
function string:trim(str)
if string:empty(str) then return str end
return (str:gsub("^%s*(.-)%s*$", "%1"))
end
-- Replace char in specified position of string
function replace_char(pos, str, replacement)
if string:empty(str) then return str end
return str:sub(1, pos-1) .. replacement .. str:sub(pos+1)
end

160
lua/include/table.lua Normal file
View file

@ -0,0 +1,160 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- Clear table
function table.clear(_table)
for k in pairs(_table) do
_table[k] = nil
end
end
-- Get table size
function table.count(T)
if not T or not next(t) then return 0 end
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
--[[
Save Table to File
Load Table from File
v 1.0
Lua 5.2 compatible
Only Saves Tables, Numbers and Strings
Insides Table References are saved
Does not save Userdata, Metatables, Functions and indices of these
----------------------------------------------------
table.save( table , filename )
on failure: returns an error msg
----------------------------------------------------
table.load( filename or stringtable )
Loads a table that has been saved via the table.save function
on success: returns a previously saved table
on failure: returns as second argument an error msg
----------------------------------------------------
Licensed under the same terms as Lua itself.
]]--
do
-- declare local variables
--// exportstring( string )
--// returns a "Lua" portable version of the string
local function exportstring( s )
return string.format("%q", s)
end
--// The Save Function
function table.save( tbl,filename )
local charS,charE = " ","\n"
local file,err = io.open( filename, "wb" )
if err then return err end
-- initiate variables for save procedure
local tables,lookup = { tbl },{ [tbl] = 1 }
file:write( "return {"..charE )
for idx,t in ipairs( tables ) do
file:write( "-- Table: {"..idx.."}"..charE )
file:write( "{"..charE )
local thandled = {}
for i,v in ipairs( t ) do
thandled[i] = true
local stype = type( v )
-- only handle value
if stype == "table" then
if not lookup[v] then
table.insert( tables, v )
lookup[v] = #tables
end
file:write( charS.."{"..lookup[v].."},"..charE )
elseif stype == "string" then
file:write( charS..exportstring( v )..","..charE )
elseif stype == "number" then
file:write( charS..tostring( v )..","..charE )
end
end
for i,v in pairs( t ) do
-- escape handled values
if (not thandled[i]) then
local str = ""
local stype = type( i )
-- handle index
if stype == "table" then
if not lookup[i] then
table.insert( tables,i )
lookup[i] = #tables
end
str = charS.."[{"..lookup[i].."}]="
elseif stype == "string" then
str = charS.."["..exportstring( i ).."]="
elseif stype == "number" then
str = charS.."["..tostring( i ).."]="
end
if str ~= "" then
stype = type( v )
-- handle value
if stype == "table" then
if not lookup[v] then
table.insert( tables,v )
lookup[v] = #tables
end
file:write( str.."{"..lookup[v].."},"..charE )
elseif stype == "string" then
file:write( str..exportstring( v )..","..charE )
elseif stype == "number" then
file:write( str..tostring( v )..","..charE )
end
end
end
end
file:write( "},"..charE )
end
file:write( "}" )
file:close()
end
--// The Load Function
function table.load( sfile )
local ftables,err = loadfile( sfile )
if err or not ftables() then return _,err end
local tables = ftables()
for idx = 1,#tables do
local tolinki = {}
for i,v in pairs( tables[idx] ) do
if type( v ) == "table" then
tables[idx][i] = tables[v[1]]
end
if type( i ) == "table" and tables[i[1]] then
table.insert( tolinki,{ i,tables[i[1]] } )
end
end
-- link indices
for _,v in ipairs( tolinki ) do
tables[idx][v[2]],tables[idx][v[1]] = tables[idx][v[1]],nil
end
end
return tables[1]
end
-- close do
end
-- ChillCode

65
lua/include/timer.lua Normal file
View file

@ -0,0 +1,65 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- Global table with timers
__timers = {}
function timer_add(id, interval, callback)
timer_object = timer:new(id, interval, callback)
table.insert(__timers, timer_object)
end
function timer_del(id)
-- safe remove from the timers (we can not just use __timers = nil here)
local i = 0
for k,v in pairs(__timers) do
i = i + 1
if (v.id == id) then
table.remove(__timers, i)
return true
end
end
return false
end
function timer_get(id)
local i = 0
for k,v in pairs(__timers) do
i = i + 1
if (v.id == id) then
return v
end
end
end
--
-- Timer class
--
timer = {}
-- Create a new timer with unique id and given interval
function timer:new(id, interval, callback)
options = { id = id, interval = interval, prev_time = 0, callback = callback }
self.__index = self
return setmetatable(options, self)
end
-- Event when timer executes
function timer:tick()
if os.time() < self.prev_time + self.interval then return 0 end
self.prev_time = os.time()
-- Debug: display time when the timer ticks
-- api.eventlog(eventlog_level_gui, __FUNCTION__, self.interval .. ": " .. os.time())
-- execute callback function
return self.callback(self)
end

13
lua/main.lua Normal file
View file

@ -0,0 +1,13 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- this function executes after preload all the lua scripts
function main()
end

128
lua/quiz/command.lua Normal file
View file

@ -0,0 +1,128 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- /quiz <start|stop|stats>
function command_quiz(account, text)
if not config.quiz then
return 0
end
local args = split_command(text, 2)
if (args[1] == "start") then
return q_command_start(account, args[2])
elseif (args[1] == "stop") then
return q_command_stop(account)
elseif (args[1] == "stats") then
if not args[2] then
return q_command_toplist(account)
else
return q_command_stats(account, args[2])
end
end
api.describe_command(account.name, args[0])
return 1
end
-- Start quiz in current channel
function q_command_start(account, filename)
local channel = api.channel_get_by_id(account.channel_id)
if not channel then
api.message_send_text(account.name, message_type_error, account.name, "This command can only be used inside a channel.")
return 1
end
if not account_is_operator_or_admin(account.name, channel.name) then
api.message_send_text(account.name, message_type_error, account.name, "You must be at least a Channel Operator to use this command.")
return 1
end
if config.quiz_channel then
api.message_send_text(account.name, message_type_error, account.name, 'Quiz has already ran in channel "'..config.quiz_channel..'". Use /qstop to force finish.')
return 1
end
-- check if file exists
if not filename or not file_exists(q_directory() .. "/questions/" .. filename .. ".txt") then
api.message_send_text(account.name, message_type_error, account.name, "Available Quiz dictionaries: ")
api.message_send_text(account.name, message_type_error, account.name, " " .. config.quiz_filelist)
return 1
end
quiz:start(channel.name, filename)
return 1
end
-- Stop quiz
function q_command_stop(account)
if not config.quiz_channel then
api.message_send_text(account.name, message_type_error, account.name, 'Quiz is not running.')
return 1
end
quiz:stop(account.name)
return 1
end
-- Display Quiz Top players record
function q_command_toplist(account)
-- load records (if it was not loaded yet)
if not q_load_records() then
return 0
end
local output = "Top " .. config.quiz_users_in_top .. " Quiz records:"
api.message_send_text(account.name, message_type_info, account.name, output)
-- display TOP of total records
for i,t in pairs(q_records_total) do
if (i > config.quiz_users_in_top) then break end
local output = string.format(" %d. %s [%d points]", i, t.username, t.points)
api.message_send_text(account.name, message_type_info, account.name, output)
end
return 1
end
-- Display single player's record
function q_command_stats(account, username)
-- load records (if it was not loaded yet)
if not q_load_records() then
return 0
end
local found = false
-- find user in records
for i,t in pairs(q_records_total) do
if string.upper(t.username) == string.upper(username) then
api.message_send_text(account.name, message_type_info, account.name, t.username.. "'s Quiz record:")
local output = string.format(" %d. %s [%d points]", i, t.username, t.points)
api.message_send_text(account.name, message_type_info, account.name, output)
found = true
end
end
if not found then
api.message_send_text(account.name, message_type_info, account.name, username .. " has never played Quiz.")
end
return 1
end

54
lua/quiz/helper.lua Normal file
View file

@ -0,0 +1,54 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- Get path to Quiz directory
function q_directory()
return config.scriptdir .. "/quiz"
end
-- Replace each symbol in string with *
-- Example: input = "hello world", return = "***** *****"
function q_hide_unswer(input)
local output = input
for i = 1, #input do
local c = input:sub(i,i)
if not (c == " ") then
output = replace_char(i, output, "*")
end
end
return output
end
-- Open one random symbol in hidden (with *) string from original string
-- Example: hidden = "***** *****", original = "hello world", return = "***l* *****"
function q_show_next_symbol(hidden, original)
local output = hidden
if hidden == original then
return output
end
local replaced = false
while not replaced do
local i = math.random(#hidden)
local c = hidden:sub(i,i)
if c == "*" then
local c2 = original:sub(i,i)
output = replace_char(i, hidden, c2)
replaced = true
end
end
return output
end
-- Callback to sort records table descending
function q_compare_desc(a,b)
return tonumber(a.points) > tonumber(b.points)
end

155
lua/quiz/questions/dota.txt Normal file
View file

@ -0,0 +1,155 @@
**************************************************
Category: DotA
Author: AGOES
**************************************************
One of illegal tools for playing dota? = Maphack
How many kills needed for the title killing spree? = three
How many kills needed for the title Dominating? = four
How many kills needed for the title Megakill? = five
How many kills needed for the title Unstoppable? = six
How many kills needed for the title Whicked Sick? = seven
How many kills needed for the title Monster Kill? = eight
How many kills needed for the title GodLike? = nine
How many kills needed for the title Beyond Godlike(Holy Shit!)? = ten
First kill of the Game? = First Blood
Two Kills simutaneously. = Double Kill
Three Kills simutaneously. = Triple Kill
Hero Nicknames: Vengeful Spirit. = Vs
Hero Nicknames: Leshrac the Malicious Tormented Soul. = Leshrac
Hero Nicknames: Kel'Thuzad the Lich King. = Lich
Hero Nicknames: Krobelus the Death Prophet. = Dp
Hero Nicknames: Lion the Demon Witch. = Lion
Hero Nicknames: Lesale Deathbringer the Venomancer. = Veno
Hero Nicknames: Magnus the Magnataur. = Magnataur
Hero Nicknames: Visage The Necro'lic. = Visage
Hero Nicknames: Nessaj the Chaos Knight. = Ck
Hero Nicknames: Banehallow the Lycanthrope. = Lycan
Hero Nicknames: Black Arachnia the Broodmother. = Brood
Hero Nicknames: Mortred the Phantom Assassin. = Pa
Hero Nicknames: Medusa the Gorgon. = Medusa
Hero Nicknames: Balanar the NightStalker. = Ns
Hero Nicknames: King Leoric the Skeleton King. = Leo
Hero Nicknames: Lucifer the DoomBringer. = Lucy
Hero Nicknames: Pharoh'moth the Nerubian Assassin. = Na
Hero Nicknames: Slardar the Slithereen Guard. = Slardar
Hero Nicknames: Akasha the Queen of Pain. = QoP
Hero Nicknames: Bone Clinkz the Bone Fletcher. = Clinkz
Hero Nicknames: Darkterror the Faceless Void. = Void
Hero Nicknames: Viper the Netherdrake. = Viper
Hero Nicknames: Razor the Lightning Revenant. = Razor
Hero Nicknames: Nai'x the Lifestealer. = Naix
Hero Nicknames: Pugna the Oblivion. = Pugna
Hero Nicknames: Leviathan the Tidehunter. = Levi
Hero Nicknames: Atropos the Bane Elemental. = Bane
Hero Nicknames: Rotund'jere the Necrolyte. = Necro
Hero Nicknames: Pudge the Butcher. = Pudge
Hero Nicknames: Barathrum the Spiritbreaker. = Sb
Hero Nicknames: Anub'seran the Nerubian Weaver. = Weaver
Hero Nicknames: Crixalis the Sandking. = Sk
Hero Nicknames: Mogul Kahn the Axe. = Axe
Hero Nicknames: Strygwyr the BloodSeeker. = seeker
Ultimate: Nether Swap. = Vengeful Spirit
Ultimate: Thunder God's Wrath. = Lord of Olympia
Ultimate: Untouchable. = Enchantress
Ultimate: Replicate. = Morphling
Ultimate: Freezing Field. = Crystal Maiden
Ultimate: God's Strength. = Rogue Knight
Ultimate: Song of the Siren. = Naga Siren
Ultimate: Echo Slam. = Earth Shaker
Ultimate: Permanent Invisibility. = Stealth Assassin
Ultimate: True Form. = Lone Druid
Ultimate: Laguna Blade. = Slayer
Ultimate: Omnislash. = Juggernaught
Ultimate: Global Silence. = Silencer
Ultimate: Overgrowth. = Treant Protector
Ultimate: Blackhole. = Enigma
Ultimate: Ignis Fatuus. = Keeper of the Light
Ultimate: Enrage. = Ursa Warrior
Ultimate: Phantom Edge. = Phantom Lancer
Ultimate: Mutli Cast. = Ogre Magi
Ultimate: Rearm. = Tinker
Ultimate: Wrath of Nature. = Prophet
Ultimate: Grow. = Stone Giant
Ultimate: Remote Mines. = Goblin Techies
Ultimate: Hand of God. = Holy Knight
Ultimate: Eclispe. = Moon rider
Ultimate: Assassinate. = Dwarven Sniper
Ultimate: Rampage. = Troll Warlord
Ultimate: Mass Serpent Wards. = Shadow Shaman
Ultimate: Warpath. = Bristleback
Ultimate: Primal Split. = Pandaren Battlemaster
Ultimate: Great Fortitude. = Centaur Warchief
Ultimate: Track. = Bounty Hunter
Ultimate: Elder Dragon Form. = Dragon Knight
Ultimate: Mana Void. = Anti-Mage
Ultimate: Marksmanship. = Drow Ranger
Ultimate: Guardian Angel. = Omni Knight
Ultimate: Sunder. = Soul Keeper
Ultimate: Pulse Nova. = Tormented Soul
Ultimate: Chain Frost. = Lich
Ultimate: Exorcism. = Death Prophet
Ultimate: Finger of Death. = Demon Witch
Ultimate: Poison Nova. = Venomancer
Ultimate: Reverse Polarity. = Magnataur
Ultimate: Raise Revenants. = Necro'lic
Ultimate: Phantasm. = Chaos Knight
Ultimate: Shapeshift. = Lycanthrope
Ultimate: Insatiable Hunger. = Broodmother
Ultimate: Coupe De Grace. = Phantom Assassin
Ultimate: Purge. = Gorgon
Ultimate: Darkness. = Night Stalker
Ultimate: Reincarnation. = Skeleton King
Ultimate: Doom. = Doom Bringer
Ultimate: Vendetta. = Nerubian Assassin
Ultimate: Amplify Damage. = Slithereen Guard
Ultimate: Sonic Wave. = Queen of Pain
Ultimate: Death Pact. = Bone Fletcher
Ultimate: Chronosphere. = Faceless Void
Ultimate: Viper Strike. = Netherdrake
Ultimate: Rage. = Lifestealer
Ultimate: Life Drain. = Oblivion
Ultimate: Ravage. = Tidehunter
Ultimate: Fiend's Grip. = Bane Elemental
Ultimate: Reaper's Scythe. = Necrolyte
Ultimate: Disember. = Butcher
Ultimate: Nether Strike. = Spiritbreaker
Ultimate: Time Lapse. = Nerubian Weaver
Ultimate: Requim of Souls. = Shadow Fiend
Ultimate: Epicenter. = Sand King
Ultimate: Culling Blade. = Axe
Ultimate: Rupture. = Bloodseeker
Armor Type: Spirit Tower = Fortified
Armor Type: The Frozen Throne = Fortified
Cost: Belt of Giant Strength = 450
Cost: Blades of Alarcrity = 1000
Cost: Blades of Attack in map 6.73c = 450
Cost: Boots of Elvenskin = 450
Cost: Boots of Speed = 500
Cost: Broadsword = 1200
Cost: Claymore = 1400
Cost: Dagger of Escape = 2150
Cost: Demon Edge = 2400
Cost: Eaglehorn = 3300
Cost: Gauntlets of Ogre Strength = 150
Cost: Gem of True Sight = 700
Cost: Helm of Iron Will = 950
Cost: Mantle of Intelligence = 150
Cost: Mask of Death = 900
Cost: Mithril Hammer = 1600
Cost: Ogre Axe = 1000
Cost: Platemail = 1400
Cost: Robe of the Magi = 450
Cost: Sacred Relic = 3800
Cost: Scroll for Black King Bar = 1300
Cost: Scroll for Dagon = 1350
Cost: Scroll for Heart of Tarrasque = 1200
Cost: Scroll for Null Talisman = 170
Cost: Scroll for Radiance = 1325
Cost: Scroll for Stygian Desolator = 1200
Cost: Slippers of Agility = 150
Cost: Staff of Wizardry = 1000
Cost: Ultimate Orb = 2100
Cost: Vitality Booster = 1200
Cost: Void Stone = 875

5150
lua/quiz/questions/misc.txt Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,42 @@
**************************************************
Category: Warcraft
Author: Blizzard
**************************************************
This Horde ship was crafted by goblins. Originally intended to bring Thrall and Aggra to the Maelstrom, the ship was destroyed in a surprise attack by the Alliance. = Draka's Fury
What are undead murlocs called? = Murghouls
What is the name of Tirion Fordring's gray stallion? = Mirador
Which of these is the correct name for King Varian Wrynn's first wife? = Tiffin Ellerlan Wrynn
Who was the first satyr to be created? = Xavius
Before Ripsnarl became a worgen, he had a family. What was his wife's name? = Calissa Harrington
This structure, located in Zangarmarsh was controlled by naga who sought to drain a precious and limited resource: the water of Outland. = Coilfang Reservoir
One name for this loa is "Night's Friend" = Mueh'zala
This defender of the Scarlet Crusade was killed while slaying the dreadlord Beltheris = Holia Sunshield
Brown-skinned orcs first began showing up on Azeroth several years after the Third War, when the Dark Portal was reactivated. What are these orcs called? = Mag'har
Succubus demons revel in causing anguish, and they serve the Legion by conducting nightmarish interrogations. What species is the succubus? = Sayaad
While working as a tutor, Stalvan Mistmantle became obsessed with one of his students, a young woman named Tilloa. What was the name of her younger brother? = Giles
Who was the mighty proto-dragon captured by Loken and tranformed into Razorscale? = Veranus
This emissary of the Horde felt that Silvermoon City was a little too bright and clean. = Tatal
This queen oversaw the evacuation of her people after the Cataclysm struck and the Forsaken attacked her nation. = Mia Greymane
Not long ago, this frail Zandalari troll sought to tame a direhorn. Although he journeyed to the Isle of Giants, he was slain in his quest. What was his name? = Talak
Arthas's death knights were trained in a floating citadel that was taken by force when many of them rebelled against the Lich King. What was the fortress's name? = Acherus
White wolves were once the favored mounts of which orc clan? = Frostwolf Clan
In Taur-ahe, the language of the tauren, what does lar'korwi mean? = Sharp claw
Name the homeworld of the ethereals. = K'aresh
Who was the first death knight to be created on Azeroth? = Teron Gorefiend
In the assault on Icecrown, Horde forces dishonorably attacked Alliance soldiers who were busy fighting Scourge and trying to capture this gate. = Mord'rethar
What evidence drove Prince Arthas to slaughter the people in Stratholme during the Third War? = Grain
Who is the current leader of the gnomish people? = Gelbin Mekkatorque
Malfurion Stormrage helped found this group which is the primary druidic organization of Azeroth = Cenarion Circle
The draenei like to joke that in the language of the naaru, the word Exodar has this meaning = Defective elekk turd
Thane Kurdran Wildhammer recently suffered a tragic loss when his valiant gryphon was killed in a fire. What was this gryphon's name? = Sky'ree
Before she was raised from the dead by Arthas to serve the Scourge, Sindragosa was a part of what dragonflight? = Blue
The Ironforge library features a replica of an unusually large ram's skeleton. What was the name of this legendary ram? = Toothgnasher
Name the titan lore-keeper who was a member of the elite Pantheon = Norgannon
What did the dragon aspects give the night elves after the War of the Ancients? = Nordrassil
Formerly a healthy paladin, this draenei fell ill after fighting the Burning Legion and becoming one of the Broken. He later became a powerful shaman. = Nobundo
Who were the three young twilight drakes guarding the twilight dragon eggs in the Obsidian Sanctum? = Shadron Tenebron Vesperon
What phrase means "Thank you" in Draconic, the language of the dragons? = Belan Shi
Before the original Horde formed, a highly contagious sickness began spreading rapidly among the orcs. What did the orcs call it? = Red pox
What is the highest rank bestowed on a druid? = Archdruid
Whose tomb includes the inscription "May the bloodied crown stay lost and forgotten"? = Terenas Menethil 2

274
lua/quiz/quiz.lua Normal file
View file

@ -0,0 +1,274 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
quiz = {} -- object
q_dictionary = {} -- dictionary word=question
q_records_current = { } -- table with current player records
q_records_total = {} -- table with total player records
q_records_diff = {} -- table with current diffs
-- counter of questions
local _q_question_counter = 0
-- count of current question's hints
local _q_hint_counter = 0
-- count of current question's hints
local _q_current_index = 0
-- current hint
local _q_hint = nil
-- time when question was shown
local _q_time_start = 0
-- last user streaks
local _q_streak = { username = nil, count = 0 }
-- delay flag for next_question timer
local _q_next_question_skip_first = false
function quiz:start(channelname, quizname)
local filename = q_directory() .. "/questions/" .. string.lower(quizname) .. ".txt"
if not file_exists(filename) then
channel_send_message(config.quiz_channel, filename, message_type_error)
return false
end
config.quiz_channel = channelname
-- reset
_q_question_counter = 0
_q_streak.username = nil
_q_streak.count = 0
-- clear tables
table.clear(q_dictionary)
table.clear(q_records_current)
table.clear(q_records_diff)
-- table.clear(q_records_total) -- do not clear it, it should be cached
-- load records (if it was not loaded yet)
q_load_records()
-- fill dictionary from file
if file_load(filename, file_load_dictionary_callback, q_read_dictionary_callback) then
channel_send_message(config.quiz_channel, string.format("Quiz \"%s\" started!", quizname), message_type_error)
q_next_question()
end
end
function q_read_dictionary_callback(a, b)
table.insert(q_dictionary, { question = a, word = b })
end
function quiz:stop(username)
timer_del("q_hint")
timer_del("q_question")
local output = nil
if username then
output = "Quiz stopped by "..username
else
output = "Quiz finished!"
end
channel_send_message(config.quiz_channel, output, message_type_error)
-- display current records
if (q_records_current) and next(q_records_current) then
table.sort(q_records_current, q_compare_desc)
channel_send_message(config.quiz_channel, "Records of this game:", message_type_info)
for i,t in pairs(q_records_current) do
channel_send_message(config.quiz_channel, string.format(" %d. %s [%d points]", i, t.username, t.points), message_type_info)
end
end
-- merge current with total records
if (q_records_total) and next(q_records_total) then
for i,t in pairs(q_records_current) do
-- steal points from players in total table accordingly with half points of current table players
if (config.quiz_competitive_mode) then
if q_records_total[i] and not (q_records_total[i].username == t.username) then
-- remove half points
local points = math.floor(t.points / 2)
q_records_diff[q_records_total[i].username] = points * -1
q_records_total[i].points = q_records_total[i].points - points
-- avoid negative value
if q_records_total[i].points <= 0 then
q_records_total[i].points = 0
end
end
end
-- add non exist users from current to total table
-- and increase total users points
local idx = q_records_total_find(t.username)
-- set diff as winned points
local tusername = q_records_total[idx].username
q_records_diff[tusername] = t.points
-- if user not found (new user with 0 points)
if q_records_total[idx].points == 0 then
q_records_total[idx].points = t.points
else
-- update diff if it was changed to negative
if q_records_diff[tusername] < 0 then
q_records_diff[tusername] = q_records_diff[tusername] + t.points
end
q_records_total[idx].points = q_records_total[idx].points + t.points
end
end
-- sort by points
table.sort(q_records_total, q_compare_desc)
quiz_display_top_players()
else
-- if records.txt is empty then save first records
q_records_total = q_records_current
end
q_save_records()
config.quiz_channel = nil
end
-- handle channel message
function quiz_handle_message(username, text)
local q = q_dictionary[_q_current_index]
if string.upper(q.word) == string.upper(text) then
-- time from question to unswer
local time_diff = os.clock() - _q_time_start
local points, total = 0,0
-- calc points
points = 1+(#q.word - _q_hint_counter) - math.floor(time_diff / config.quiz_hint_delay)
-- avoid negative value
if (points <= 0) then points = 1 end
-- remember previous streak
local prev_streak = _q_streak
-- increase streaks
if (_q_streak.username == username) then
_q_streak.count = _q_streak.count + 1
else
_q_streak.username = username
_q_streak.count = 0
end
local bonus = ""
-- add bonus points for streaks
if (_q_streak.count > 0) then
points = points + _q_streak.count
bonus = string.format(" +%s streak bonus", _q_streak.count)
end
local idx = q_records_current_find(username)
total = q_records_current[idx].points + points
q_records_current[idx].points = total
-- lose half points for previous user
if config.quiz_competitive_mode and not (prev_streak.username == username) then
local lose_points = math.floor(total / 2)
local idx = q_records_current_find(prev_streak.username)
q_records_current[idx].points = q_records_current[idx].points - lose_points
-- avoid negative value
if q_records_current[idx].points <= 0 then
q_records_current[idx].points = 0
end
end
channel_send_message(config.quiz_channel, string.format("%s is correct! The unswer is: %s (+%d points%s, %d total) [%d sec]", username, q.word, (points-_q_streak.count), bonus, total, time_diff), message_type_info)
q_next_question()
end
end
-- go to next question
function q_next_question()
_q_question_counter = _q_question_counter + 1
if (_q_question_counter > config.quiz_max_questions) then
quiz:stop()
return 0
end
timer_del("q_hint")
_q_next_question_skip_first = false
timer_add("q_question", config.quiz_question_delay, q_tick_next_question)
end
-- write random question
function q_tick_next_question(options)
-- skip first tick
if not _q_next_question_skip_first then
_q_next_question_skip_first = true
return 0
end
_q_time_start = os.clock()
_q_current_index = math.random(#q_dictionary)
local q = q_dictionary[_q_current_index]
-- send question
channel_send_message(config.quiz_channel, "--------------------------------------------------------", message_type_info)
channel_send_message(config.quiz_channel, string.format(" %s (%d letters)", q.question, #q.word), message_type_info)
-- hide hint
_q_hint = q_hide_unswer(q.word)
-- stop this timer
timer_del(options.id)
_q_hint_counter = 0
-- start timer to hint unswer
timer_add("q_hint", config.quiz_hint_delay, q_tick_hint_unswer)
end
-- write hint for unswer
function q_tick_hint_unswer(options)
-- skip first tick
if _q_hint_counter == 0 then
_q_hint_counter = _q_hint_counter + 1
return 0
end
_q_hint_counter = _q_hint_counter + 1
local q = q_dictionary[_q_current_index]
-- update hint
_q_hint = q_show_next_symbol(_q_hint, q.word)
if not (_q_hint == q.word) then
-- show hint
channel_send_message(config.quiz_channel, "Hint: ".._q_hint, message_type_info)
else
-- reset counter
_q_hint_counter = 0
-- show unswer
q_nounswer(nil)
end
end
-- nobody unswered
function q_nounswer(username)
channel_send_message(config.quiz_channel, 'Nobody unswered. The unswer was: '.._q_hint, message_type_info)
-- decrease streaks
if (_q_streak.count > 0) then
_q_streak.count = _q_streak.count - 1
end
q_next_question()
end

87
lua/quiz/records.lua Normal file
View file

@ -0,0 +1,87 @@
--[[
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
This file is a part of the PvPGN Project http://pvpgn.pro
Licensed under the same terms as Lua itself.
]]--
-- Load total records from records.txt to table
function q_load_records()
local filename = q_directory() .. "/records.txt"
if not q_records_total or not next(q_records_total) then
-- fill records table
return file_load(filename, file_load_dictionary_callback, q_read_records_callback)
end
return true
end
function q_read_records_callback(a, b)
table.insert(q_records_total, { username = a, points = b })
end
-- Save total records from table to records.txt
function q_save_records()
local filename = q_directory() .. "/records.txt"
file_save2(filename, q_save_records_callback)
end
function q_save_records_callback(file)
if (q_records_total) and next(q_records_total) then
for i,t in pairs(q_records_total) do
file:write(t.username .. " = " .. t.points)
file:write("\n")
end
end
end
-- Print Top X players records
function quiz_display_top_players()
local output = "Top " .. config.quiz_users_in_top .. " Quiz players:"
channel_send_message(config.quiz_channel, output, message_type_info)
-- display TOP of total records
for i,t in pairs(q_records_total) do
if (i > config.quiz_users_in_top) then break end
local diff = ""
if q_records_diff[t.username] then
if q_records_diff[t.username] < 0 then
diff = "(" .. q_records_diff[t.username] .. ")" -- minus points
elseif q_records_diff[t.username] > 0 then
diff = "(+" .. q_records_diff[t.username] .. ")" -- plus points
end
end
local output = string.format(" %d. %s [%d points] %s", i, t.username, t.points, diff)
channel_send_message(config.quiz_channel, output, message_type_info)
end
end
-- find username and return table index
-- preventnew - do not add user in table if it is not found
function q_records_current_find(username, preventnew)
-- find username
for i,t in pairs(q_records_current) do
if (t.username == username) then return i end
end
if preventnew then return nil end
-- if not found then insert
table.insert(q_records_current, {username = username, points = 0})
return #q_records_current
end
-- find username and return table index
-- prevent_new = do not add user in table if it is not found
function q_records_total_find(username, prevent_new)
for i,t in pairs(q_records_total) do
if (t.username == username) then return i end
end
if prevent_new then return nil end
-- if not found then insert
table.insert(q_records_total, {username = username, points = 0})
return #q_records_total
end

0
lua/quiz/records.txt Normal file
View file