diff --git a/README.md b/README.md index dcd797d..0b8fd17 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,26 @@ Where needed, apply buttons are placed. So instead of having to leave the formsp In the timer format configuration formspec shown above a text area is used where pressing enter goes to a new line so a button for applying the changes is shown. +## Custom Timer + +mTimer comes with a freely configurable custom timer that can run in three different modes and shows its current state and time as applicable. The formats for the three states can be configured individually. + +The four time unput fields on the middle left are used for days, hours, minutes, and seconds and the table with the variables shows the set value and not the current value because the current value changes depending on the timer state and mode. + +![Custom Timer](screenshots/custom_timer.png) + +In *Countdown* mode the custom timer counts down from the time that was set using the time input fields. In *Timer* mode the custom timer counts up to the value that was set. And in *Continuous Run* mode the custom timer counts up from when it was started, adding the time that was set to the starting point calculation. + +After the custom timer was set up it needs to be started. Starting (as well as stopping or restarting) it is done using the defined chat commands. + +``` +/mtimer ctstart -- Start the timer if not running +/mtimer ctstop -- Stop the timer if running +/mtimer ctrestart -- Restart the timer if running +``` + +If the custom timer finished the format changes to the “finished” value. Please note that the timer is still running if this message is shown. To stop the timer you need to invoke the corresponding chat command. Only if the “stopped” format is used the timer is actually stopped (i.e. most of it’s logic isn’t running). + ## Default configuration (interesting for server admins) The default configuration is loaded on server start and applied when a new player joins or an already existing player sets any (or all) of the mTimer values to the default value via the main menu or one of the specific dialog formspecs. diff --git a/locale/mtimer.de.tr b/locale/mtimer.de.tr index 321f464..0bd6697 100644 --- a/locale/mtimer.de.tr +++ b/locale/mtimer.de.tr @@ -74,12 +74,22 @@ Host Time=Hostzeit Running=Timer läuft Stopped=Angehalten Finished=Abgeschlossen -Countdown=Herunterzählen -Timer Mode=Heraufzählen +Countdown=Countdown +Timer Mode=Zielzeit Continuous Run=Dauerlauf +Used Value=Benutzter Wert +As configured=Wie konfiguriert The timer is stopped=Der Timer ist angehalten The timer has finished=Der Timer hat sein Ziel erreicht -Used Value=Genutzter Wert +Configure the custom timer=Individuellen Timer konfigurieren +Start the custom timer=Individuellen Timer starten +Stop stop custom timer=Individuellen Timer anhalten +Restart the custom timer=Individuellen Timer neu starten +The custom timer is already running=Der individuelle Timer läuft bereits +The custom timer is not running=Der individuelle Timer läuft nicht +The custom timer was started=Der individuelle Timer wurde gestartet +The custom timer was stopped=Der individuelle Timer wurde angehalten +The custom timer was restarted=Der individuelle timer wurde neu gestartet # Default Timer Format Current Date: @1=Aktuelles Datum: @1 diff --git a/screenshots/custom_timer.png b/screenshots/custom_timer.png new file mode 100644 index 0000000..38f12e0 Binary files /dev/null and b/screenshots/custom_timer.png differ diff --git a/screenshots/main_menu.png b/screenshots/main_menu.png index a61a42f..5219c35 100644 Binary files a/screenshots/main_menu.png and b/screenshots/main_menu.png differ diff --git a/system/chat_command.lua b/system/chat_command.lua index dae68aa..41c3703 100644 --- a/system/chat_command.lua +++ b/system/chat_command.lua @@ -2,6 +2,7 @@ local m = mtimer local S = m.translator local d = m.dialog local cs = minetest.chat_send_player +local ds = minetest.deserialize -- Colorize a command sequence @@ -16,6 +17,46 @@ local command = function (command) end +local custom_timer_handling = function (name, action) + local player = minetest.get_player_by_name(name) + local player_meta = player:get_meta() + local current_timestamp = os.time(os.date('!*t')) + local ctv_key = m.meta.custom_timer_settings.key + local ctv = ds(player_meta:get_string(ctv_key)) + + if action == 'start' then + if ctv.running ~= true then + ctv.running = true + ctv.start_timestamp = current_timestamp + cs(name, S('The custom timer was started')) + else + cs(name, S('The custom timer is already running')) + end + end + + if action == 'stop' then + if ctv.running ~= false then + ctv.running = false + ctv.start_timestamp = 0 + cs(name, S('The custom timer was stopped')) + else + cs(name, S('The custom timer is not running')) + end + end + + if action == 'restart' then + if ctv.running == true then + ctv.start_timestamp = current_timestamp + cs(name, S('The custom timer was restarted')) + else + cs(name, S('The custom timer is not running')) + end + end + + player_meta:set_string(ctv_key, minetest.serialize(ctv)) +end + + -- Chat command -- -- The `/mtimer` chat command opens the main menu and allows to directly open @@ -44,7 +85,7 @@ end -- Providing unknown parameters has no effect. minetest.register_chatcommand('mtimer', { description = S('Configure timer display'), - params = '', + params = '', func = function(name, parameters) local action = parameters:match('%a+') @@ -65,8 +106,13 @@ minetest.register_chatcommand('mtimer', { if action == 'tf' then d.timer_format(name) end if action == 'ct' then d.custom_timer(name) end + if action == 'ctstart' then custom_timer_handling(name,'start') end + if action == 'ctstop' then custom_timer_handling(name,'stop') end + if action == 'ctrestart' then custom_timer_handling(name,'restart') end + if action == 'help' then local message = { + command(' ')..S('Open Main Menu'), command('vi')..S('Visibility'), command('po')..S('Position'), command('co')..S('Color'), @@ -79,8 +125,11 @@ minetest.register_chatcommand('mtimer', { command('hs')..S('HUD Element Size'), command('os')..S('HUD Element Offset'), command('tf')..S('Timer Format'), - command('ct')..S('Custom Timer'), - command(' ')..S('Open Main Menu') + '', + command('ct ')..S('Configure the custom timer'), + command('ctstart ')..S('Start the custom timer'), + command('ctstop ')..S('Stop stop custom timer'), + command('ctrestart')..S('Restart the custom timer') } cs(name, table.concat(message, '\n')) end diff --git a/system/formspec/formspec_creation.lua b/system/formspec/formspec_creation.lua index bbd118c..4a7940f 100644 --- a/system/formspec/formspec_creation.lua +++ b/system/formspec/formspec_creation.lua @@ -305,7 +305,7 @@ mtimer.dialog.timer_format = function (player_name) mtimer.show_formspec('mtimer:timer_format', { title = S('Timer Format'), show_to = player_name, - height = 5.75, + height = 6, width = 8.5, formspec = { 'textarea[0,0;6,2.5;format;;'..fe(timer_data.format)..']', @@ -318,6 +318,7 @@ mtimer.dialog.timer_format = function (player_name) 'label[0,1.3;'..S('Session Start Time')..'] label[2.5,1.3;{st}] label[4,1.3;'..fe(timer_data.session_start_time)..']', 'label[0,1.7;'..S('Session Duration')..'] label[2.5,1.7;{sd}] label[4,1.7;'..fe(timer_data.session_duration)..']', 'label[0,2.1;'..S('Host Time')..'] label[2.5,2.1;{ht}] label[4,2.1;'..fe(timer_data.host_time)..']', + 'label[0,2.5;'..S('Custom Timer')..'] label[2.5,2.5;{ct}] label[4,2.5;'..fe(S('As configured'))..']', 'container_end[]', 'container[6.25,0]', 'button[0,0;2,0.5;apply;'..S('Apply')..']', @@ -340,6 +341,10 @@ mtimer.dialog.custom_timer = function (player_name) local a_timer = ctv.timer_mode == 'timer' and 'x' or '' local a_continuous = ctv.timer_mode == 'continuous' and 'x' or '' + local format_running = ctv.format.running or '' + local format_stopped = ctv.format.stopped or '' + local format_finished = ctv.format.finished or '' + mtimer.show_formspec('mtimer:custom_timer', { title = S('Custom Timer'), show_to = player_name, @@ -354,9 +359,9 @@ mtimer.dialog.custom_timer = function (player_name) 'field_close_on_enter[v_minutes;false]', 'field_close_on_enter[v_seconds;false]', 'container[0,0]', - ' label[0,0.25;'..S('Running')..'] field[2.25,0;6.5,0.5;v_format_running;;'..fe(ctv.format.running)..']', - ' label[0,0.85;'..S('Stopped')..'] field[2.25,0.6;6.5,0.5;v_format_stopped;;'..fe(ctv.format.stopped)..']', - ' label[0,1.45;'..S('Finished')..'] field[2.25,1.2;6.5,0.5;v_format_finished;;'..fe(ctv.format.finished)..']', + ' label[0,0.25;'..S('Running')..'] field[2.25,0;6.5,0.5;v_format_running;;'..fe(format_running)..']', + ' label[0,0.85;'..S('Stopped')..'] field[2.25,0.6;6.5,0.5;v_format_stopped;;'..fe(format_stopped)..']', + ' label[0,1.45;'..S('Finished')..'] field[2.25,1.2;6.5,0.5;v_format_finished;;'..fe(format_finished)..']', ' box[0,2;+linewidth,0.04;#ffffff]', 'container_end[]', 'container[3.75,2.4]', diff --git a/system/get_times.lua b/system/get_times.lua index 281dec9..d962875 100644 --- a/system/get_times.lua +++ b/system/get_times.lua @@ -244,16 +244,92 @@ local get_session_duration = function (player_name) end --- TODO: Implemet custom timer logic +-- Custom timer +-- +-- The timer values are loaded from the meta entry and are calculated on +-- runtime. The timer knows thre modes. +-- +-- 1. continuous run +-- +-- In this mode the time just runs in a continuous way and all that is +-- calculated is the difference between the current timestamp and the +-- timestamp from when the timer was started. +-- +-- 2. countdown +-- +-- For the countdown the given input values and the start timestamp are +-- added together, then the current timestamp is substracted from this. +-- This results in the difference getting smaller and eventually being +-- equal or less than 0. In this case the format is changed to the “timer +-- finished” format. +-- +-- 3. timer +-- +-- The timer mode calculated the difference like the continuous run mode +-- but also calculates a target from the starting timestamp and the input +-- values. When the difference is equal or larger than the target the format +-- is changed to the “timer finished” format. +-- +-- The output is parsed outside the mode-specific calculations. Also every +-- format is parsed with the output time parts. This automatically allows all +-- time parts in all formats – wich makes no sense because the difference +-- calculation is messed up outside the specific boundaries. local get_custom_timer = function (player_name) local player = minetest.get_player_by_name(player_name) local player_meta = player:get_meta() local ctv = ds(player_meta:get_string(m.meta.custom_timer_settings.key)) + local current_timestamp = os.time(os.date('!*t')) - local values = {} - values['formatted'] = 'PLACEHOLDER' + local running = ctv.running + local difference = 0 + local format = '' + local finished = false - return values + if running == false then format = ctv.format.stopped end + if running == true and finished == true then format=ctv.format.finished end + if running == true and finished == false then format=ctv.format.running end + + -- Calculate seconds from the given input values + local value_seconds = 0 + value_seconds = value_seconds + ctv.values.seconds + value_seconds = value_seconds + (ctv.values.minutes * 60) + value_seconds = value_seconds + (ctv.values.hours * 3600) + value_seconds = value_seconds + (ctv.values.days * 86400) + + -- Continuous run + if running == true and ctv.timer_mode == 'continuous' then + difference = current_timestamp - (ctv.start_timestamp - value_seconds) + end + + -- Countdown + if running == true and ctv.timer_mode == 'countdown' then + difference = (ctv.start_timestamp + value_seconds) - current_timestamp + if difference <= 0 then format = ctv.format.finished end + end + + -- Timer + if running == true and ctv.timer_mode == 'timer' then + difference = current_timestamp - ctv.start_timestamp + local target = ctv.start_timestamp + value_seconds + if current_timestamp >= target then format = ctv.format.finished end + end + + -- Parse values into time parts + local result_values = { + days = string.format('%02d', math.floor(difference/86400)), + hours = string.format('%02d', math.floor((difference % 86400)/3600)), + minutes = string.format('%02d', math.floor((difference % 3600)/60)), + seconds = string.format('%02d', math.floor((difference % 60))) + } + + return { + formatted = format:gsub('{[a-z0-9]+}', { + ['{days}'] = result_values.days or 0, + ['{hours}'] = result_values.hours or 0, + ['{minutes}'] = result_values.minutes or 0, + ['{seconds}'] = result_values.seconds or 0 + }) + } end diff --git a/system/load_configuration.lua b/system/load_configuration.lua index 0e31d5d..90fd379 100644 --- a/system/load_configuration.lua +++ b/system/load_configuration.lua @@ -98,6 +98,5 @@ set('timer_format', table.concat({ S('Current Date: @1', '{rd}'), S('Ingame Time: @1', '{it}'), S('Session Start: @1', '{st}'), - S('Session Duration: @1', '{sd}'), - S('Custom Timer: @1', '{ct}') + S('Session Duration: @1', '{sd}') }, '\n'), false) diff --git a/system/on_receive_fields.lua b/system/on_receive_fields.lua index 690ac1d..ead8abe 100644 --- a/system/on_receive_fields.lua +++ b/system/on_receive_fields.lua @@ -218,18 +218,32 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if hours < 0 then hours = 23 days = days - 1 end if days < 0 then days = 0 end - -- set formats + -- set relevant values ctv.format = { running = fields.v_format_running, stopped = fields.v_format_stopped, finished = fields.v_format_finished } + ctv.values = { + days = days, + hours = hours, + minutes = minutes, + seconds = seconds + } - -- (re)set (and quit) - ctv.values = {days=days,hours=hours,minutes=minutes,seconds=seconds} - meta:set_string(attr.key, minetest.serialize(table.copy(ctv))) - if fields.default then meta:set_string(attr.key, attr.default) end - if not fields.quit then d.custom_timer(name) end + -- Set default values if requested + if fields.default then + meta:set_string(attr.key, attr.default) + d.custom_timer(name) + m.update_timer(name) + return + end + + -- Set values if not quitting + if not fields.quit then + meta:set_string(attr.key, minetest.serialize(ctv)) + d.custom_timer(name) + end end