VanaTime 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +6 -0
- data/Manifest +3 -0
- data/README +95 -125
- data/VanaTime.gemspec +17 -17
- data/lib/Vana/common.rb +65 -65
- data/lib/Vana/constants.rb +20 -21
- data/lib/Vana/day.rb +47 -44
- data/lib/Vana/element.rb +45 -40
- data/lib/Vana/moon.rb +131 -139
- data/lib/Vana/moon_countdown.rb +48 -51
- data/lib/Vana/skillup_calculator.rb +142 -0
- data/lib/Vana/time.rb +98 -110
- data/lib/Vana/time_manager.rb +91 -64
- data/lib/util/log_listener.rb +15 -0
- data/lib/util/ruby_bean.rb +39 -0
- data/lib/vana_time.rb +7 -2
- metadata +35 -39
data/lib/Vana/constants.rb
CHANGED
@@ -11,36 +11,35 @@
|
|
11
11
|
# minutes to an hour. I don't know who originally discovered the following, but,
|
12
12
|
# the following information can be used to set an "epoch" for Vana'diel time
|
13
13
|
# compared to Earth time:
|
14
|
-
|
14
|
+
#
|
15
15
|
# Earth time: 2002/06/23 15:00:00 GMT
|
16
16
|
# Vana'diel time: 0898/02/01 00:00:00
|
17
|
-
|
17
|
+
#
|
18
18
|
# Using that, you can take any given Earth time, subtract the starting Earth
|
19
19
|
# date to set time to "0", and then multiply that by 25 to convert to Vana'diel
|
20
20
|
# time.
|
21
|
-
|
22
21
|
module FFXI
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
module Constants
|
23
|
+
MINUTE = 60
|
24
|
+
HOUR = MINUTE * 60
|
25
|
+
DAY = HOUR * 24
|
27
26
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
MS_SECOND = 1000
|
28
|
+
MS_DAY = DAY * MS_SECOND
|
29
|
+
MS_HOUR = HOUR * MS_SECOND
|
30
|
+
MS_MINUTE = MINUTE * MS_SECOND
|
32
31
|
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
# correct for the game time calculations anyways
|
33
|
+
MS_MONTH = 30 * MS_DAY
|
34
|
+
MS_YEAR = 360 * MS_DAY
|
36
35
|
|
37
|
-
|
36
|
+
MS_GAME_DAY = (MS_DAY / 25)
|
38
37
|
|
39
|
-
|
40
|
-
|
38
|
+
BASIS_DATE = Time.utc(2002,6,23,15,0,0,0)
|
39
|
+
MOON_DATE = Time.utc(2004,1,25,2,31,12,0)
|
41
40
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
41
|
+
MS_BASIS_VANA = ((898 * 360 + 30) * MS_DAY).to_f
|
42
|
+
MS_BASIS_DATE = BASIS_DATE.to_f * MS_SECOND
|
43
|
+
MS_MOON_DATE = MOON_DATE.to_f * MS_SECOND
|
44
|
+
end
|
46
45
|
end
|
data/lib/Vana/day.rb
CHANGED
@@ -1,54 +1,57 @@
|
|
1
1
|
module FFXI
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
attr_reader :vana_time
|
25
|
-
|
26
|
-
def to_i
|
27
|
-
@day.to_i
|
28
|
-
end
|
2
|
+
class VanaDay < Util::RubyBean
|
3
|
+
include FFXI::Constants
|
4
|
+
|
5
|
+
VANA_DAYS = ["Firesday", "Earthsday", "Watersday", "Windsday", "Iceday",
|
6
|
+
"Lightningday", "Lightsday", "Darksday"]
|
7
|
+
DAY_ELEMENTS = ["Fire", "Earth", "Water", "Wind", "Ice", "Thunder",
|
8
|
+
"Light", "Darkness"]
|
9
|
+
|
10
|
+
property :day, :day_element, :day_weakness, :day_strength
|
11
|
+
property :day_string, :day_html
|
12
|
+
|
13
|
+
alias element day_element
|
14
|
+
alias weakness day_weakness
|
15
|
+
alias strength day_strength
|
16
|
+
alias to_s day_string
|
17
|
+
alias to_html day_html
|
18
|
+
|
19
|
+
def initialize(opts={})
|
20
|
+
super()
|
21
|
+
add_listener(opts[:listener]) if opts[:listener]
|
22
|
+
update_vana_time opts[:vana_time] if opts[:vana_time]
|
23
|
+
end
|
29
24
|
|
30
|
-
|
31
|
-
|
32
|
-
|
25
|
+
def update_vana_time(vt)
|
26
|
+
@vana_time = vt.to_f
|
27
|
+
set_day
|
28
|
+
end
|
33
29
|
|
34
|
-
|
35
|
-
|
36
|
-
|
30
|
+
def update_earth_time(time)
|
31
|
+
update_vana_time FFXI::Common.earth_to_vana(time)
|
32
|
+
end
|
37
33
|
|
38
|
-
|
34
|
+
attr_reader :vana_time
|
39
35
|
|
40
|
-
|
41
|
-
|
36
|
+
def to_i
|
37
|
+
day.to_i
|
38
|
+
end
|
42
39
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
@weakness = FFXI::ElementFactory.send(@element.weakness)
|
40
|
+
def to_f
|
41
|
+
day.to_f
|
42
|
+
end
|
47
43
|
|
48
|
-
|
49
|
-
notify_observers("day", self)
|
50
|
-
end
|
51
|
-
end
|
44
|
+
private
|
52
45
|
|
46
|
+
def set_day
|
47
|
+
self.day = (@vana_time.to_f % (8 * MS_DAY)) / (MS_DAY)
|
48
|
+
self.day_element = FFXI::ElementFactory.send DAY_ELEMENTS[day]
|
49
|
+
self.day_weakness = FFXI::ElementFactory.send(day_element.weakness)
|
50
|
+
self.day_strength = FFXI::ElementFactory.send(day_element.strength)
|
51
|
+
self.day_string = VANA_DAYS[day]
|
52
|
+
self.day_html =
|
53
|
+
"<b><font color=\"#{day_element.color}\">#{VANA_DAYS[day]}</font></b>"
|
53
54
|
end
|
55
|
+
|
56
|
+
end
|
54
57
|
end
|
data/lib/Vana/element.rb
CHANGED
@@ -1,49 +1,54 @@
|
|
1
1
|
module FFXI
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
module ElementFactory
|
3
|
+
Element = Struct.new(:name, :direction, :strength, :weakness, :color,
|
4
|
+
:enfeeble, :attribute, :ninjutsu, :avatar)
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
ELEMENTS = ["Fire", "Ice", "Wind", "Earth", "Thunder", "Water"]
|
7
|
+
ELEMENT_COLORS = ["red", "aqua", "green", "yellow", "purple", "blue"]
|
8
|
+
ELEMENT_ENFEEBLE = ["Burn", "Frost", "Choke", "Rasp", "Shock", "Drown"]
|
9
|
+
ELEMENT_ATTRIBUTE = ["STR", "INT", "AGI", "VIT", "DEX", "MND"]
|
10
|
+
ELEMENT_NINJUTSU = ["Katon", "Hyoton", "Huton", "Doton", "Raiton", "Suiton"]
|
11
|
+
ELEMENT_AVATAR = ["Ifrit", "Shiva", "Garuda", "Titan", "Ramuh", "Leviathan"]
|
12
|
+
ELEMENT_DIRECTION = ["North-West", "East", "South-East", "South",
|
13
|
+
"South-West", "West"]
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
SPECIAL_ELEMENTS = ["Light", "Darkness"]
|
16
|
+
SPECIAL_ELEMENT_COLORS = ["gray", "black"]
|
17
|
+
SPECIAL_ELEMENT_ENFEEBLE = ["Dia", "Bio"]
|
18
|
+
SPECIAL_ELEMENT_ATTRIBUTE = ["CHR", nil]
|
19
|
+
SPECIAL_ELEMENT_AVATAR = ["Carbuncle", ["Fenrir", "Diabolos"]]
|
20
|
+
SPECIAL_ELEMENT_DIRECTION = ["North-East", "North"]
|
18
21
|
|
19
|
-
|
20
|
-
|
22
|
+
def self.method_missing(name, *args)
|
23
|
+
element = name.to_s.capitalize
|
21
24
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
if SPECIAL_ELEMENTS.include? element
|
26
|
+
index = SPECIAL_ELEMENTS.index element
|
27
|
+
direction = SPECIAL_ELEMENTS_DIRECTION[index]
|
28
|
+
opposed = SPECIAL_ELEMENTS[(index - 1) % 2]
|
29
|
+
color = SPECIAL_ELEMENT_COLORS[index]
|
30
|
+
enfeeble = SPECIAL_ELEMENT_ENFEEBLE[index]
|
31
|
+
attribute = SPECIAL_ELEMENT_ATTRIBUTE[index]
|
32
|
+
avatar = SPECIAL_ELEMENT_AVATAR[index]
|
29
33
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
34
|
+
Element.new(element, direction, opposed, opposed, color, enfeeble,
|
35
|
+
attribute, nil, avatar)
|
36
|
+
elsif ELEMENTS.include? element
|
37
|
+
index = ELEMENTS.index element
|
38
|
+
direction = ELEMENT_DIRECTION[index]
|
39
|
+
weakness = ELEMENTS[(index - 1) % 6]
|
40
|
+
strength = ELEMENTS[(index + 1) % 6]
|
41
|
+
color = ELEMENT_COLORS[index]
|
42
|
+
enfeeble = ELEMENT_ENFEEBLE[index]
|
43
|
+
attribute = ELEMENT_ATTRIBUTE[index]
|
44
|
+
ninjutsu = ELEMENT_NINJUTSU[index]
|
45
|
+
avatar = ELEMENT_AVATAR[index]
|
41
46
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
47
|
+
Element.new(element, direction, strength, weakness, color, enfeeble,
|
48
|
+
attribute, ninjutsu, avatar)
|
49
|
+
else
|
50
|
+
raise "Unknown element"
|
51
|
+
end
|
48
52
|
end
|
53
|
+
end
|
49
54
|
end
|
data/lib/Vana/moon.rb
CHANGED
@@ -1,143 +1,135 @@
|
|
1
1
|
module FFXI
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
@moon_days = (((@earth_time.to_f * 1000).to_i -
|
65
|
-
MS_MOON_DATE.to_i) / MS_GAME_DAY).floor % 84
|
66
|
-
|
67
|
-
if @moon_days != old_moon_days
|
68
|
-
changed
|
69
|
-
notify_observers("moon_days", self)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def set_percent
|
74
|
-
old_moon_percent = @moon_percent
|
75
|
-
|
76
|
-
@moon_percent = - ((42 - @moon_days).to_f / 42 * 100).round
|
77
|
-
|
78
|
-
if @moon_percent != old_moon_percent
|
79
|
-
changed
|
80
|
-
notify_observers("moon_percent", self)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def set_phase
|
85
|
-
old_phase = @moon_phase
|
86
|
-
|
87
|
-
case @moon_percent
|
88
|
-
when (-100..-94)
|
89
|
-
@moon_phase = 0
|
90
|
-
@next_phase_target = 3
|
91
|
-
when (90..100)
|
92
|
-
@moon_phase = 0
|
93
|
-
@next_phase_target = 3 + 84
|
94
|
-
when (-93..-62)
|
95
|
-
@moon_phase = 1
|
96
|
-
@next_phase_target = 17
|
97
|
-
when (-61..-41)
|
98
|
-
@moon_phase = 2
|
99
|
-
@next_phase_target = 25
|
100
|
-
when (-40..-11)
|
101
|
-
@moon_phase = 3
|
102
|
-
@next_phase_target = 38
|
103
|
-
when (-10..6)
|
104
|
-
@moon_phase = 4
|
105
|
-
@next_phase_target = 45
|
106
|
-
when (7..36)
|
107
|
-
@moon_phase = 5
|
108
|
-
@next_phase_target = 58
|
109
|
-
when (37..56)
|
110
|
-
@moon_phase = 6
|
111
|
-
@next_phase_target = 66
|
112
|
-
when (57..89)
|
113
|
-
@moon_phase = 7
|
114
|
-
@next_phase_target = 80
|
115
|
-
end
|
116
|
-
|
117
|
-
if @moon_phase != old_phase
|
118
|
-
changed
|
119
|
-
notify_observers("moon_phase", self)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
def set_optimal
|
124
|
-
old_optimal = @next_optimal_phase
|
125
|
-
|
126
|
-
case @moon_phase
|
127
|
-
when (0..3)
|
128
|
-
@next_optimal_phase = 4
|
129
|
-
@optimal_phase_target = 38
|
130
|
-
@optimal_phase_target += 84 if (90..100).include? @moon_percent
|
131
|
-
when (4..7)
|
132
|
-
@next_optimal_phase = 0
|
133
|
-
@optimal_phase_target = 80
|
134
|
-
end
|
135
|
-
|
136
|
-
if @next_optimal_phase != old_optimal
|
137
|
-
changed
|
138
|
-
notify_observers("next_optimal_phase", self)
|
139
|
-
end
|
140
|
-
end
|
2
|
+
class VanaMoon < Util::RubyBean
|
3
|
+
include FFXI::Constants
|
4
|
+
include FFXI::Common
|
5
|
+
|
6
|
+
MOON_PHASES = ["Full", "Waning Gibbous", "Last Quarter Moon",
|
7
|
+
"Waning Crescent", "New Moon", "Waxing Crescent", "First Quarter Moon",
|
8
|
+
"Waxing Gibbous"]
|
9
|
+
SHORT_MOON_PHASES = ["FM", "WNG", "LQM", "WNC", "NM", "WXC", "FQM", "WXG"]
|
10
|
+
|
11
|
+
property :moon_days, :moon_percent, :moon_percent_string
|
12
|
+
property :moon_phase, :moon_phase_name, :moon_phase_short_name
|
13
|
+
property :moon_string
|
14
|
+
property :next_phase_name, :next_phase_short_name
|
15
|
+
property :optimal_phase_name, :optimal_phase_short_name
|
16
|
+
property :next_phase_target, :optimal_phase_target
|
17
|
+
|
18
|
+
alias to_i moon_percent
|
19
|
+
alias percent moon_percent_string
|
20
|
+
alias phase moon_phase_name
|
21
|
+
alias to_s moon_string
|
22
|
+
alias next_phase next_phase_name
|
23
|
+
alias optimal_phase optimal_phase_name
|
24
|
+
|
25
|
+
def initialize(opts={})
|
26
|
+
super()
|
27
|
+
add_listener(opts[:listener]) if opts[:listener]
|
28
|
+
|
29
|
+
if opts[:vana_time].is_a? VanaTime
|
30
|
+
update_earth_time opts[:vana_time].earth_time
|
31
|
+
elsif opts[:earth_time]
|
32
|
+
update_earth_time opts[:earth_time]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.now(listener=nil)
|
37
|
+
opts = {:earth_time => Time.now}
|
38
|
+
opts[:listener] = listener if listener
|
39
|
+
self.new(opts)
|
40
|
+
end
|
41
|
+
|
42
|
+
def update_earth_time(time)
|
43
|
+
raise "Input needs to be a Time object" if ! time.is_a? Time
|
44
|
+
|
45
|
+
@earth_time = time
|
46
|
+
set_moon_days
|
47
|
+
set_percent
|
48
|
+
set_phase
|
49
|
+
set_moon_string
|
50
|
+
set_optimal
|
51
|
+
end
|
52
|
+
|
53
|
+
def update_vana_time(vt)
|
54
|
+
if vt.is_a? VanaTime
|
55
|
+
update_earth_time(vt.earth_time)
|
56
|
+
else
|
57
|
+
update_earth_time(FFXI::Common.vana_to_earth(vt))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
attr_reader :earth_time
|
62
|
+
|
63
|
+
private
|
141
64
|
|
65
|
+
def set_moon_days
|
66
|
+
# Each lunar cycle takes exactly 84 game days
|
67
|
+
# (3 days, 8 hours, 38 minutes, 24 seconds)
|
68
|
+
self.moon_days = (((@earth_time.to_f * 1000).to_i -
|
69
|
+
MS_MOON_DATE.to_i) / MS_GAME_DAY).floor % 84
|
142
70
|
end
|
71
|
+
|
72
|
+
def set_percent
|
73
|
+
self.moon_percent = - ((42 - moon_days).to_f / 42 * 100).round
|
74
|
+
self.moon_percent_string = "#{moon_percent.abs}%"
|
75
|
+
end
|
76
|
+
|
77
|
+
def set_phase
|
78
|
+
case moon_percent
|
79
|
+
when (-100..-94)
|
80
|
+
self.moon_phase = 0
|
81
|
+
self.next_phase_target = 3
|
82
|
+
when (90..100)
|
83
|
+
self.moon_phase = 0
|
84
|
+
self.next_phase_target = 3 + 84
|
85
|
+
when (-93..-62)
|
86
|
+
self.moon_phase = 1
|
87
|
+
self.next_phase_target = 17
|
88
|
+
when (-61..-41)
|
89
|
+
self.moon_phase = 2
|
90
|
+
self.next_phase_target = 25
|
91
|
+
when (-40..-11)
|
92
|
+
self.moon_phase = 3
|
93
|
+
self.next_phase_target = 38
|
94
|
+
when (-10..6)
|
95
|
+
self.moon_phase = 4
|
96
|
+
self.next_phase_target = 45
|
97
|
+
when (7..36)
|
98
|
+
self.moon_phase = 5
|
99
|
+
self.next_phase_target = 58
|
100
|
+
when (37..56)
|
101
|
+
self.moon_phase = 6
|
102
|
+
self.next_phase_target = 66
|
103
|
+
when (57..89)
|
104
|
+
self.moon_phase = 7
|
105
|
+
self.next_phase_target = 80
|
106
|
+
end
|
107
|
+
|
108
|
+
self.moon_phase_name = MOON_PHASES[moon_phase]
|
109
|
+
self.moon_phase_short_name = SHORT_MOON_PHASES[moon_phase]
|
110
|
+
self.next_phase_name = MOON_PHASES[(moon_phase + 1) % 8]
|
111
|
+
self.next_phase_short_name = MOON_PHASES[(moon_phase + 1) % 8]
|
112
|
+
end
|
113
|
+
|
114
|
+
def set_moon_string
|
115
|
+
self.moon_string = "#{moon_percent_string} #{moon_phase_name}"
|
116
|
+
end
|
117
|
+
|
118
|
+
def set_optimal
|
119
|
+
case moon_phase
|
120
|
+
when (0..3)
|
121
|
+
@next_optimal_phase = 4
|
122
|
+
opt = 38
|
123
|
+
opt += 84 if (90..100).include? moon_percent
|
124
|
+
self.optimal_phase_target = opt
|
125
|
+
when (4..7)
|
126
|
+
@next_optimal_phase = 0
|
127
|
+
self.optimal_phase_target = 80
|
128
|
+
end
|
129
|
+
|
130
|
+
self.optimal_phase_name = MOON_PHASES[@next_optimal_phase]
|
131
|
+
self.optimal_phase_short_name = SHORT_MOON_PHASES[@next_optimal_phase]
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
143
135
|
end
|