-- Copyright © 2013-2016, Omnys of Valefor -- All rights reserved. -- Redistribution and use in source and binary forms, with or without -- modification, are permitted provided that the following conditions are met: -- * Redistributions of source code must retain the above copyright -- notice, this list of conditions and the following disclaimer. -- * Redistributions in binary form must reproduce the above copyright -- notice, this list of conditions and the following disclaimer in the -- documentation and/or other materials provided with the distribution. -- * Neither the name of Gametime nor the -- names of its contributors may be used to endorse or promote products -- derived from this software without specific prior written permission. -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -- DISCLAIMED. IN NO EVENT SHALL OMNYS BE LIABLE FOR ANY -- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. _addon.name = 'gametime' _addon.author = 'Omnys' _addon.version = '0.6' _addon.commands = {'gametime','gt'} require('chat') require('logger') require('strings') require('maths') require('tables') texts = require('texts') config = require('config') res = require('resources') visible = false local gt = {} gt.delimiter = ' ' gt.days = {} gt.days[1] = {} gt.days[1][1] = 'Firesday' gt.days[1][2] = 'Fi ' gt.days[1][10] = '(255, 0, 0)' gt.days[1][3] = 'Fire ' --Mini mode gt.days[1][4] = '° ' gt.days[2] = {} gt.days[2][1] = 'Earthsday' gt.days[2][2] = 'Ea ' gt.days[2][10] = '(255, 225, 0)' gt.days[2][3] = 'Earth ' --Mini mode gt.days[2][4] = '° ' gt.days[3] = {} gt.days[3][1] = 'Watersday' gt.days[3][2] = 'Wa ' gt.days[3][10] = '(100, 100, 255)' gt.days[3][3] = 'Water ' --Mini mode gt.days[3][4] = '° ' gt.days[4] = {} gt.days[4][1] = 'Windsday' gt.days[4][2] = 'Wi ' gt.days[4][10] = '(0, 255, 0)' gt.days[4][3] = 'Wind ' --Mini mode gt.days[4][4] = '° ' gt.days[5] = {} gt.days[5][1] = 'Iceday' gt.days[5][2] = 'Ic ' gt.days[5][10] = '(150, 200, 255)' gt.days[5][3] = 'Ice ' --Mini mode gt.days[5][4] = '° ' gt.days[6] = {} gt.days[6][1] = 'Lightningday' gt.days[6][2] = 'Lg ' gt.days[6][10] = '(255, 128, 128)' gt.days[6][3] = 'Lightning ' --Mini mode gt.days[6][4] = '° ' gt.days[7] = {} gt.days[7][1] = 'Lightsday' gt.days[7][2] = 'Lt ' gt.days[7][10] = '(255, 255, 255)' gt.days[7][3] = 'Light ' --Mini mode gt.days[7][4] = '° ' gt.days[8] = {} gt.days[8][1] = 'Darksday' gt.days[8][2] = 'Dk ' gt.days[8][10] = '(128, 128, 128)' gt.days[8][3] = 'Dark ' --Mini mode gt.days[8][4] = '° ' gt.WeekReport = '' gt.MoonPct = '' gt.MoonPhase = '' defaults = {} defaults.saved = 0 defaults.mode = 1 defaults.zero = false defaults.time = {} defaults.time.pos = {} defaults.time.pos.x = 0 -- left defaults.time.pos.y = 0 -- top defaults.time.text = {} defaults.time.text.alpha = 255 defaults.time.text.red = 255 defaults.time.text.green = 255 defaults.time.text.blue = 255 defaults.time.text.size = 12 defaults.time.text.font = 'Consolas' defaults.time.bg = {} defaults.time.bg.alpha = 25 defaults.time.bg.red = 100 defaults.time.bg.green = 100 defaults.time.bg.blue = 100 defaults.time.bg.visible = true defaults.days = {} defaults.days.pos = {} defaults.days.pos.x = 100 defaults.days.pos.y = 0 -- top defaults.days.text = {} defaults.days.text.alpha = 255 defaults.days.text.size = 12 defaults.days.text.font = 'Consolas' defaults.days.bg = {} defaults.days.bg.alpha = 100 defaults.days.bg.red = 0 defaults.days.bg.green = 0 defaults.days.bg.blue = 0 defaults.days.axis = 'horizontal' defaults.numdays = 8 defaults.moon = {} defaults.alert = true settings = config.load(defaults) Cycles = T{} Cycles.selbina = T{} Cycles.selbina.rname = "Ships between Mhaura and Selbina" Cycles.selbina.route = T{} Cycles.selbina.route[1] = "Arrives in Mhaura and Selbina|22:40" Cycles.selbina.route[2] = "Arrives in Mhaura and Selbina|6:40" Cycles.selbina.route[3] = "Arrives in Mhaura and Selbina|14:40" Cycles.bibiki = T{} Cycles.bibiki.rname = "Ship departing Bibiki Bay for Purgonorgo Isle" Cycles.bibiki.route = T{} Cycles.bibiki.route[1] = "Arrives in Bibiki|4:50" Cycles.bibiki.route[2] = "Arrives in Bibiki|16:50" Cycles.nashmau = T{} Cycles.nashmau.rname = "Aht Urhgan / Nashmau Ship" Cycles.nashmau.route = T{} Cycles.nashmau.route[1] = "Arrives in Whitegate and Nashmau|05:00" Cycles.nashmau.route[2] = "Arrives in Whitegate and Nashmau|13:00" Cycles.nashmau.route[3] = "Arrives in Whitegate and Nashmau|21:00" Cycles.whitegate = T{} Cycles.whitegate.rname = "Aht Urhgan / whitegate Ship" Cycles.whitegate.route = T{} Cycles.whitegate.route[1] = "Arrives in Whitegate and Mhaura|10:40" Cycles.whitegate.route[2] = "Arrives in Whitegate and Mhaura|18:40" Cycles.whitegate.route[3] = "Arrives in Whitegate and Mhaura|2:40" Cycles.windurst = T{} Cycles.windurst.rname = "Ship between Windurst and Jeuno" Cycles.windurst.route = T{} Cycles.windurst.route[1] = "Arrives in Windurst|4:47" Cycles.windurst.route[2] = "Arrives in Jeuno|7:41" Cycles.windurst.route[3] = "Arrives in Windurst|10:47" Cycles.windurst.route[4] = "Arrives in Jeuno|13:41" Cycles.windurst.route[5] = "Arrives in Windurst|16:47" Cycles.windurst.route[6] = "Arrives in Jeuno|19:41" Cycles.windurst.route[7] = "Arrives in Windurst|22:47" Cycles.windurst.route[8] = "Arrives in Jeuno|1:41" Cycles.bastok = T{} Cycles.bastok.rname = "Ship between Bastok and Jeuno" Cycles.bastok.route = T{} Cycles.bastok.route[1] = "Arrives in Bastok|0:13" Cycles.bastok.route[2] = "Arrives in Jeuno|3:11" Cycles.bastok.route[3] = "Arrives in Bastok|6:13" Cycles.bastok.route[4] = "Arrives in Jeuno|9:11" Cycles.bastok.route[5] = "Arrives in Bastok|12:13" Cycles.bastok.route[6] = "Arrives in Jeuno|15:11" Cycles.bastok.route[7] = "Arrives in Bastok|18:13" Cycles.bastok.route[8] = "Arrives in Jeuno|21:41" Cycles.sandy = T{} Cycles.sandy.rname = "Ship between San d'Oria and Jeuno" Cycles.sandy.route = T{} Cycles.sandy.route[1] = "Arrives in San d'Oria|7:10" Cycles.sandy.route[2] = "Arrives in Jeuno|6:11" Cycles.sandy.route[3] = "Arrives in San d'Oria|9:10" Cycles.sandy.route[4] = "Arrives in Jeuno|12:11" Cycles.sandy.route[5] = "Arrives in San d'Oria|15:10" Cycles.sandy.route[6] = "Arrives in Jeuno|18:11" Cycles.sandy.route[7] = "Arrives in San d'Oria|21:10" Cycles.sandy.route[8] = "Arrives in Jeuno|00:41" Cycles.kazham = T{} Cycles.kazham.rname = "Ship between Kazham and Jeuno" Cycles.kazham.route = T{} Cycles.kazham.route[1] = "Arrives in Kazham|1:48" Cycles.kazham.route[2] = "Arrives in Jeuno|4:49" Cycles.kazham.route[3] = "Arrives in Kazham|7:48" Cycles.kazham.route[4] = "Arrives in Jeuno|10:49" Cycles.kazham.route[5] = "Arrives in Kazham|13:48" Cycles.kazham.route[6] = "Arrives in Jeuno|14:49" Cycles.kazham.route[7] = "Arrives in Kazham|19:48" Cycles.kazham.route[8] = "Arrives in Jeuno|20:49" gt.gtt = texts.new('', settings.time) gt.gtd = texts.new('', settings.days) config.register(settings, function() if settings.days.axis == 'horizontal' then gt.delimiter = ' ' else gt.delimiter = '\n' end if settings.zero then gt.gtt:text('${hours|XX|%.2d}:${minutes|XX|%.2d}') else gt.gtt:text('${hours|XX}:${minutes|XX|%.2d}') end gt.gtd:text('${day|} ${MoonPhase|Unknown} (${MoonPct|-}%); ${WeekReport|}') if settings.numdays > 0 and settings.numdays < 9 then gt.numdays = settings.numdays else gt.numdays = 8 end gt.gtd:text('${day|} ${MoonPhase|Unknown} (${MoonPct|-}%); ${WeekReport|}') local info = windower.ffxi.get_info() if info.logged_in then day_change(info.day) gt.gtt:show() gt.gtd:show() end end) function getroutes(route) for ckey, cval in pairs(Cycles) do if route == nil or ckey == route then log('\30\02'..Cycles[ckey].rname..' (shortcode: //gt route '..ckey..')') for ri = 1, #Cycles[ckey].route do ro = Cycles[ckey].route[ri]:split('|') rtime = timeconvert(ro[2]) rdelay = math.round(rtime-gt.dectime,2) if rdelay < 0 then rdelay = rdelay + 24 end rdelay = 2.4 * rdelay log(ro[1]..' @ '..ro[2]..' \30\02Arrival in '..(timeconvert2(rdelay))..'') end end end end function default_settings() settings:save('all') end windower.register_event('time change', function(new, old) gt.hours = (new / 60):floor() gt.minutes = new % 60 gt.gtt:update(gt) end) function timeconvert(basetime) basetable = basetime:split(':') return basetable[1]..'.'..math.round(basetable[2] * (100/60)) end function timeconvert2(basetime) basetable = tostring(basetime):split('.') return basetable[1]..':'..tostring(math.round(tostring(basetable[2]):slice(1,2) / (100/60))):zfill(2) end function moon_change() local info = windower.ffxi.get_info() gt.MoonPhase = res.moon_phases[info.moon_phase].english gt.MoonPct = info.moon gt.gtd:update(gt) end function day_change(day) day = res.days[day].english if (day == 'Firesday') then dlist = {'1','2','3','4','5','6','7','8'} elseif (day == 'Earthsday') then dlist = {'2','3','4','5','6','7','8','1'} elseif (day == 'Watersday') then dlist = {'3','4','5','6','7','8','1','2'} elseif (day == 'Windsday') then dlist = {'4','5','6','7','8','1','2','3'} elseif (day == 'Iceday') then dlist = {'5','6','7','8','1','2','3','4'} elseif (day == 'Lightningsday') then dlist = {'6','7','8','1','2','3','4','5'} elseif (day == 'Lightsday') then dlist = {'7','8','1','2','3','4','5','6'} elseif (day == 'Darksday') then dlist = {'8','1','2','3','4','5','6','7'} end dpos = 0 daystring = '' while dpos < gt.numdays do dpos = dpos + 1 dval = dlist[dpos] daystring = ''..daystring..gt.delimiter..' \\cs'..gt.days[(dval+0)][10]..gt.days[(dval+0)][settings.mode] end gt.day = day gt.WeekReport = daystring gt.gtd:update(gt) moon_change() end function tolog() if settings.alert == true then log('Day: '..gt.day..'; Moon: '..gt.MoonPhase..' ('..gt.MoonPct..'%);') end end windower.register_event('day change', moon_change .. day_change) windower.register_event('moon change', moon_change) windower.register_event('addon command', function (...) local args = T{...}:map(string.lower) if args[1] == nil or args[1] == "help" then log('Use //gametime or //gt as follows:') log('Positioning:') log('//gt [timex/timey/daysx/daysy] :: example: //gt timex 125') log('//gt [time/days] reset :: example: //gt days reset') log('Text features:') log('//gt timeSize :: example: //gt timeSize 10') log('//gt timeFont :: example: //gt timeFont Verdana') log('//gt daySize :: example: //gt daySize 10') log('//gt dayFont :: example: //gt dayFont Verdana') log('Visibility:') log('//gt [time/days] [show/hide] :: example //gt time hide') log('//gt axis [horizontal/vertical] :: week display axis') log('//gt [time/days] alpha 1-255. :: Sets the transparency. Lowest numbers = more transparent.') log('//gt mode 1-4 :: Fullday; Abbreviated; Element names; Compact') log('Routes:') log('//gt route :: Displays route names.') log('//gt route [route name] :: Displays arrival time for route.') log('Misc:') log('//gt zero [on/off] :: Displays the time with leading zeros. 04:05 instead of 4:05') log('//gt days [1-8] :: Limits the number of days displayed') log('//gt alert :: Toggle display in chat log for day and moon changes') log('Remember to //gt save when you\'re happy with your settings.') elseif args[1] == 'routes' or args[1] == 'route' then if args[2] == nil then local ckeys = '' for ckey, cval in pairs(Cycles) do ckeys = ckeys..', '..ckey end ckeys = ckeys:slice(3,#ckeys) log('Use //gt route [shortcode] ('..ckeys..')') else getroutes(args[2]) end ---CLI Arguments for Time font Size elseif args[1] == 'timeSize' then gt.gtt:size(tonumber(args[2])) ---CLI Arguments for Time font type elseif args[1] == 'timeFont' then gt.gtt:font(args[2]) ---CLI Arguments for Day font Size elseif args[1] == 'daySize' then gt.gtd:size(tonumber(args[2])) ---CLI Arguments for Day font type elseif args[1] == 'dayFont' then gt.gtd:font(args[2]) elseif args[1] == 'timex' then gt.gtt:pos_x(tonumber(args[2])) elseif args[1] == 'timey' then gt.gtt:pos_y(tonumber(args[2])) elseif args[1] == 'daysx' then gt.gtd:pos_x(tonumber(args[2])) elseif args[1] == 'daysy' then gt.gtd:pos_y(tonumber(args[2])) elseif args[1] == 'alert' then settings.alert = (settings.alert == false) gt.alert = settings.alert log('Show alerts in log when day / moon-phase changes: '..tostring(settings.alert)) tolog() elseif args[1] == 'time' then if args[2] == 'alpha' then inalpha = tostring(args[3]):zfill(3) inalpha = inalpha+0 if (inalpha > 0 and inalpha < 256) then gt.gtt:alpha(inalpha) log('Time transparency set to '..inalpha..' ('..math.round(100-(inalpha/2.55),0)..'%).') end elseif args[2] == 'x' or args[2] == 'posx' then windower.send_command('gt timex '..args[3]) elseif args[2] == 'y' or args[2] == 'posy' then windower.send_command('gt timey '..args[3]) elseif args[2] == 'hide' then gt.gtt:hide() log('Time display hidden.') elseif args[2] == 'reset' then gt.gtt:pos(0,0) else gt.gtt:show() log('Showing time display.') end elseif args[1] == 'days' then if args[2] == 'alpha' then inalpha = tostring(args[3]):zfill(3) inalpha = inalpha+0 if (inalpha > 0 and inalpha < 256) then gt.gtd:alpha(inalpha) log('Day transparency set to '..inalpha..' ('..math.round(100-(inalpha/2.55),0)..'%).') end elseif tonumber(args[2]) > 0 and tonumber(args[2]) < 9 then gt.numdays = tonumber(args[2]) settings.numdays = tonumber(args[2]) day_change(windower.ffxi.get_info().day) elseif args[2] == 'x' or args[2] == 'posx' then windower.send_command('gt daysx '..args[3]) elseif args[2] == 'y' or args[2] == 'posy' then windower.send_command('gt daysy '..args[3]) elseif args[2] == 'hide' then gt.gtd:hide() log('Day display hidden.') elseif args[2] == 'reset' then gt.gtd:pos(0,0) else gt.gtd:show() log('Showing day display.') end elseif args[1] == 'axis' then if args[2] == 'vertical' then gt.delimiter = "\n" log('Week display axis set to vertical.') else gt.delimiter = " " log('Week display axis set to horizontal.') end day_change(windower.ffxi.get_info().day) elseif args[1] == 'mode' then inmode = args[2]:zfill(1) inmode = inmode+0 if inmode > 4 then return else settings.mode = inmode log('mode updated') end day_change(windower.ffxi.get_info().day) elseif args[1] == 'zero' then if args[2] == 'on' then settings.zero = true config.save(settings) log('zero padding enabled.') elseif args[2] == 'off' then settings.zero = false config.save(settings) log('zero padding disabled.') end elseif args[1] == 'save' then config.save(settings, 'all') log('Settings saved.') end end)