og-pvpgn-server/lua/quiz/quiz.lua
2016-08-18 09:40:05 +03:00

281 lines
7.7 KiB
Lua

--[[
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
-- is question unswered?
local q_unswered = true
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 tonumber(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 quiz_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)
if q_unswered then return end
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_unswered = true
_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)
q_unswered = false
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