Olib 2.0.0.pre.rc.2 → 2.0.0.pre.rc.7
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.
- checksums.yaml +4 -4
- data/Olib.gemspec +1 -1
- data/lib/Olib/bounty.rb +20 -68
- data/lib/Olib/character/group.rb +233 -225
- data/lib/Olib/combat/attack.rb +60 -0
- data/lib/Olib/combat/creature.rb +44 -54
- data/lib/Olib/combat/creatures.json +1770 -0
- data/lib/Olib/combat/creatures.rb +8 -22
- data/lib/Olib/combat/metadata.rb +17 -0
- data/lib/Olib/core/action.rb +6 -0
- data/lib/Olib/core/container.rb +5 -1
- data/lib/Olib/core/containers.rb +37 -18
- data/lib/Olib/core/exist.rb +4 -12
- data/lib/Olib/core/item.rb +7 -0
- data/lib/Olib/core/scroll.rb +37 -0
- data/lib/Olib/core/transaction.rb +22 -6
- data/lib/Olib/ext/matchdata.rb +1 -1
- data/lib/Olib/ext/string.rb +1 -1
- data/lib/Olib/go2.rb +14 -6
- data/lib/Olib/log.rb +42 -0
- data/lib/Olib/loot.rb +2 -1
- data/lib/Olib/opts.rb +42 -0
- data/lib/Olib/pattern_matching/rill.rb +4 -1
- data/lib/Olib/version.rb +1 -1
- metadata +8 -4
- data/lib/Olib/objects/scroll.rb +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b6389e96091c8dc8e79ea4bb25b9c30805c91d47f09addab1a1e2075976a9dc
|
4
|
+
data.tar.gz: 6008e2a6431503d0c72b17c7ec5e27a940ae7a2a4d4af807f6c11670f346794f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b19fd1ecffec4ddd4a0e8a587b8db4ccb9089c28ffc1d74c4bab90626a2e43e52efcdec7c2abbb8ae943331d7b5c3deac78e1a2674cfdd47dd6f6410294a0fdf
|
7
|
+
data.tar.gz: bf7d1ddb8a5d621f5a9c98656127a051c93842942c0079d061d415fbefd937450e57866ece27d978652af98881ba620b599575b65735aab76f5daf732d0a0b00
|
data/Olib.gemspec
CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.authors = ["Ondreian Shamsiel"]
|
12
12
|
s.email = "ondreian.shamsiel@gmail.com"
|
13
13
|
s.homepage = "https://github.com/ondreian/Olib"
|
14
|
-
s.files = %w[Olib.gemspec] + Dir["*.md", "lib/**/*.rb"]
|
14
|
+
s.files = %w[Olib.gemspec] + Dir["*.md", "lib/**/*.rb", "lib/**/*.json"]
|
15
15
|
#s.require_paths = %w[lib]
|
16
16
|
# s.add_runtime_dependency "shoes"
|
17
17
|
s.license = "MIT"
|
data/lib/Olib/bounty.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
require "ostruct"
|
2
2
|
|
3
3
|
class Bounty
|
4
|
-
NPCS = /guard|sergeant|Felinium|clerk|purser|taskmaster|gemcutter|jeweler|akrash|kris|Ghaerdish|Furryback|healer|dealer|Ragnoz|Maraene|Kelph|Areacne|Jhiseth|Gaedrein/i
|
4
|
+
NPCS = /guard|sergeant|Luthrek|Felinium|clerk|purser|taskmaster|gemcutter|jeweler|akrash|kris|Ghaerdish|Furryback|healer|dealer|Ragnoz|Maraene|Kelph|Areacne|Jhiseth|Gaedrein/i
|
5
5
|
HERBALIST_AREAS = /illistim|vaalor|legendary rest|solhaven/i
|
6
|
-
|
6
|
+
|
7
7
|
# this should be refactored to use CONST
|
8
8
|
REGEX = OpenStruct.new(
|
9
|
-
creature_problem: /It appears they have a creature problem they\'d like you to solve/,
|
9
|
+
creature_problem: /"Hmm, I've got a task here from (?<town>.*?)\. It appears they have a creature problem they\'d like you to solve/,
|
10
10
|
report_to_guard: /^You succeeded in your task and should report back to/,
|
11
11
|
get_skin_bounty: /The local furrier/,
|
12
|
-
heirloom_found: /^You have located
|
12
|
+
heirloom_found: /^You have located (?:a|an|some) (?<heirloom>.*?) and should bring it back to one of the (?<town>.*?) gate guards\./,
|
13
13
|
cooldown: /^You are not currently assigned a task. You will be eligible for new task assignment in about (?<minutes>.*?) minute(s)./,
|
14
14
|
|
15
15
|
dangerous: /You have been tasked to hunt down and kill a particularly dangerous (?<creature>.*) that has established a territory (?:in|on) (?:the )?(?<area>.*?)(?: near| between| under|\.)/,
|
@@ -17,25 +17,26 @@ class Bounty
|
|
17
17
|
heirloom: /^You have been tasked to recover (a|an|some) (?<heirloom>.*?) that an unfortunate citizen lost after being attacked by (a|an|some) (?<creature>.*?) (?:in|on|around|near|by) (?<area>.*?)(| near (?<realm>.*?))\./,
|
18
18
|
|
19
19
|
|
20
|
-
get_rescue: /It appears that a local resident urgently needs our help in some matter/,
|
21
|
-
get_bandits: /It appears they have a bandit problem they'd like you to solve./,
|
22
|
-
get_heirloom: /It appears they need your help in tracking down some kind of lost heirloom/,
|
20
|
+
get_rescue: /"Hmm, I've got a task here from (?<town>.*?)\. It appears that a local resident urgently needs our help in some matter/,
|
21
|
+
get_bandits: /"Hmm, I've got a task here from (?<town>.*?)\. It appears they have a bandit problem they'd like you to solve./,
|
22
|
+
get_heirloom: /"Hmm, I've got a task here from (?<town>.*?)\. It appears they need your help in tracking down some kind of lost heirloom/,
|
23
23
|
get_herb_bounty: /local herbalist|local healer|local alchemist/,
|
24
|
-
get_gem_bounty: /The local gem dealer, (?<npc>[a-zA-Z ]+), has an order to fill and wants our help/,
|
24
|
+
get_gem_bounty: /"Hmm, I've got a task here from (?<town>.*?)\. The local gem dealer, (?<npc>[a-zA-Z ]+), has an order to fill and wants our help/,
|
25
25
|
|
26
26
|
herb: /requires (?:a |an |)(?<herb>.*?) found (?:in|on|around|near) (?<area>.*?)(| (near|between) (?<realm>.*?)). These samples must be in pristine condition. You have been tasked to retrieve (?<number>[\d]+)/,
|
27
27
|
escort: /Go to the (.*?) and WAIT for (?:him|her|them) to meet you there. You must guarantee (?:his|her|their) safety to (?<destination>.*?) as soon as/,
|
28
|
-
gem: /has received orders from multiple customers requesting (?:a|an|some) (?<gem>[a-zA-Z '-]+). You have been tasked to retrieve (?<number>[0-9]+)/,
|
29
|
-
|
28
|
+
gem: /The gem dealer in (?<town>.*?), (?<npc>.*?), has received orders from multiple customers requesting (?:a|an|some) (?<gem>[a-zA-Z '-]+). You have been tasked to retrieve (?<number>[0-9]+)/,
|
30
29
|
cull: /^You have been tasked to suppress (?<creature>(?!bandit).*) activity (?:in|on|around) (?<area>.*?)(| (near|between) (?<realm>.*?)). You need to kill (?<number>[0-9]+)/,
|
31
|
-
bandits: /^You have been tasked to suppress bandit activity (?:in|on|around) (?<area>.*?) (?:near|between|under) (?<realm>.*?). You need to kill (?<number>[0-9]+)/,
|
30
|
+
bandits: /^You have been tasked to suppress bandit activity (?:in|on|around|near) (?<area>.*?) (?:near|between|under) (?<realm>.*?). You need to kill (?<number>[0-9]+)/,
|
32
31
|
|
33
32
|
rescue: /A local divinist has had visions of the child fleeing from (?:a|an) (?<creature>.*) (?:in|on) (?:the )?(?<area>.*?)(?: near| between| under|\.)/,
|
34
33
|
failed: /You have failed in your task/,
|
35
34
|
none: /You are not currently assigned a task/,
|
36
35
|
skin: /^You have been tasked to retrieve (?<number>\d+) (?<skin>.*?) of at least (?<quality>.*?) quality for (?<buyer>.*?) in (?<realm>.*?)\.\s+You can SKIN them off the corpse of (a|an|some) (?<creature>.*?) or/,
|
37
36
|
|
38
|
-
help_bandits: /You have been tasked to help (?<partner>.*?) suppress bandit activity (?:in|on|around) (?<area>.*?) (?:near|between|under) (?<realm>.*?). You need to kill (?<number>[0-9]+)
|
37
|
+
help_bandits: /You have been tasked to help (?<partner>.*?) suppress bandit activity (?:in|on|around|near) (?<area>.*?) (?:near|between|under) (?<realm>.*?). You need to kill (?<number>[0-9]+)/,
|
38
|
+
help_creatures: /You have been tasked to help (?<partner>.*?) kill a dangerous creature by suppressing (?<creature>.*) activity (?:in|on|around|near) (?<area>.*?) (?:near|between|under) (?<realm>.*?) during the hunt. You need to kill (?<number>[0-9]+)/,
|
39
|
+
help_cull: /You have been tasked to help (?<partner>.*?) suppress (?<creature>.*) activity (?:in|on|around|near) (?<area>.*?) (?:near|between|under) (?<realm>.*?). You need to kill (?<number>[0-9]+)/,
|
39
40
|
)
|
40
41
|
|
41
42
|
# convenience list to get all types of bounties
|
@@ -115,23 +116,15 @@ class Bounty
|
|
115
116
|
Bounty.parse(checkbounty)
|
116
117
|
end
|
117
118
|
|
118
|
-
def Bounty.ask_for_bounty
|
119
|
-
if invisible?
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
fput "unhide"
|
125
|
-
end
|
126
|
-
|
127
|
-
if Bounty.npc
|
128
|
-
fput "ask ##{Bounty.npc.id} for bounty"
|
129
|
-
Bounty
|
119
|
+
def Bounty.ask_for_bounty(expedite: false)
|
120
|
+
fput "unhide" if invisible?
|
121
|
+
fput "unhide" if hidden?
|
122
|
+
raise Exception, "could not find Bounty.npc here" unless Bounty.npc
|
123
|
+
if expedite
|
124
|
+
fput "ask ##{Bounty.npc.id} for expedite"
|
130
125
|
else
|
131
|
-
|
126
|
+
fput "ask ##{Bounty.npc.id} for bounty"
|
132
127
|
end
|
133
|
-
# give the XML parser time to update
|
134
|
-
sleep 0.2
|
135
128
|
end
|
136
129
|
|
137
130
|
def Bounty.herbalist
|
@@ -149,15 +142,6 @@ class Bounty
|
|
149
142
|
Bounty
|
150
143
|
end
|
151
144
|
|
152
|
-
def Bounty.on(namespace, &block)
|
153
|
-
@@listeners[namespace] = block
|
154
|
-
Bounty
|
155
|
-
end
|
156
|
-
|
157
|
-
def Bounty.listeners
|
158
|
-
@@listeners
|
159
|
-
end
|
160
|
-
|
161
145
|
def Bounty.cooldown?
|
162
146
|
not XMLData.active_spells["Next Bounty"].nil?
|
163
147
|
end
|
@@ -170,38 +154,6 @@ class Bounty
|
|
170
154
|
Bounty
|
171
155
|
end
|
172
156
|
|
173
|
-
def Bounty.throw_missing_listener
|
174
|
-
msg = "\n"
|
175
|
-
msg.concat "\nBounty.dispatch called for `:#{Bounty.type}` without a defined listener\n\n"
|
176
|
-
msg.concat "define a listener with:\n"
|
177
|
-
msg.concat " \n"
|
178
|
-
msg.concat " Bounty.on(:#{Bounty.type}) {\n"
|
179
|
-
msg.concat " # do something\n"
|
180
|
-
msg.concat " }\n"
|
181
|
-
msg.concat " \n"
|
182
|
-
msg.concat "or rescue this error (Errors::Fatal) gracefully\n"
|
183
|
-
msg.concat " \n"
|
184
|
-
raise Errors::Fatal.new msg
|
185
|
-
end
|
186
|
-
|
187
|
-
def Bounty.dispatch(listener=nil)
|
188
|
-
if listener
|
189
|
-
if @@listeners[listener]
|
190
|
-
@@listeners[listener].call
|
191
|
-
return Bounty
|
192
|
-
else
|
193
|
-
Bounty.throw_missing_listener
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
if @@listeners[Bounty.type]
|
198
|
-
@@listeners[Bounty.type].call
|
199
|
-
return Bounty
|
200
|
-
else
|
201
|
-
Bounty.throw_missing_listener
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
157
|
def Bounty.find_guard
|
206
158
|
Go2.advguard
|
207
159
|
if Bounty.npc.nil? then Go2.advguard2 end
|
data/lib/Olib/character/group.rb
CHANGED
@@ -1,258 +1,99 @@
|
|
1
1
|
require "ostruct"
|
2
|
+
require "benchmark"
|
2
3
|
require "Olib/character/disk"
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
@birth = Time.now
|
10
|
-
@members = []
|
11
|
-
end
|
12
|
-
|
13
|
-
def clear!
|
14
|
-
@members = []
|
15
|
-
@birth = Time.now
|
16
|
-
@leader = nil
|
17
|
-
end
|
18
|
-
|
19
|
-
def size
|
20
|
-
@members.size
|
21
|
-
end
|
22
|
-
|
23
|
-
def empty?
|
24
|
-
@members.empty?
|
25
|
-
end
|
26
|
-
|
27
|
-
def add(pc, leader = false)
|
28
|
-
member = Member.new pc, leader
|
29
|
-
if leader
|
30
|
-
@leader = member
|
31
|
-
end
|
32
|
-
@members << member
|
33
|
-
self
|
34
|
-
end
|
35
|
-
|
36
|
-
def each(&block)
|
37
|
-
@members.each do |char| yield char end
|
38
|
-
self
|
39
|
-
end
|
5
|
+
class Group
|
6
|
+
@@members ||= []
|
7
|
+
@@leader ||= nil
|
8
|
+
@@checked ||= false
|
9
|
+
@@status ||= :closed
|
40
10
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
return true if pc.respond_to?(:name) and Char.name.eql?(pc.name)
|
45
|
-
!find do |char|
|
46
|
-
if pc.is_a?(String)
|
47
|
-
char.noun.eql?(pc)
|
48
|
-
else
|
49
|
-
char.noun.eql?(pc.noun) || char.noun.eql?(pc.name)
|
50
|
-
end
|
51
|
-
end.nil?
|
52
|
-
end
|
53
|
-
|
54
|
-
def nonmembers
|
55
|
-
((GameObj.pcs || []) + Disk.all()).reject do |pc|
|
56
|
-
include?(pc)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def to_s
|
61
|
-
"<Members: [#{@members.join(" ")}]>"
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
class Member
|
66
|
-
attr_reader :id, :leader, :name, :noun
|
67
|
-
def initialize(pc, leader = false)
|
68
|
-
@id = pc.id
|
69
|
-
@leader = leader
|
70
|
-
@name = pc.name
|
71
|
-
@noun = pc.noun
|
72
|
-
end
|
73
|
-
|
74
|
-
def ref
|
75
|
-
GameObj[@id]
|
76
|
-
end
|
77
|
-
|
78
|
-
def leader?
|
79
|
-
@leader
|
80
|
-
end
|
81
|
-
|
82
|
-
def status
|
83
|
-
(ref.status.split(" ") || []).map(&:to_sym)
|
84
|
-
end
|
85
|
-
|
86
|
-
def is(state)
|
87
|
-
status =~ state
|
88
|
-
end
|
89
|
-
|
90
|
-
def ==(other)
|
91
|
-
@id == other.id
|
92
|
-
end
|
93
|
-
|
94
|
-
def to_s
|
95
|
-
"<#{name}: @leader=#{leader?} @status=#{status}>"
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
MEMBERS = Members.new
|
100
|
-
OPEN = :open
|
101
|
-
CLOSED = :closed
|
102
|
-
CHECK_HOOK = self.name.to_s
|
103
|
-
NO_GROUP = /You are not currently in a group/
|
104
|
-
MEMBER = /<a exist="(?<id>.*?)" noun="(?<name>.*?)">(.*?)<\/a> is (?<type>(the leader|also a member) of your group|following you)\./
|
105
|
-
STATE = /^Your group status is currently (?<state>open|closed)\./
|
106
|
-
END_GROUP = /list of other options\./
|
107
|
-
|
108
|
-
PARSER = Proc.new do |line|
|
109
|
-
if line.strip.empty? || line =~ NO_GROUP
|
110
|
-
nil
|
111
|
-
elsif line =~ STATE
|
112
|
-
nil
|
113
|
-
elsif line =~ END_GROUP
|
114
|
-
Group.checked!
|
115
|
-
DownstreamHook.remove(CHECK_HOOK)
|
116
|
-
nil
|
117
|
-
elsif line =~ MEMBER
|
118
|
-
begin
|
119
|
-
pc = line.match(MEMBER).to_struct
|
120
|
-
Group::MEMBERS.add GameObj[pc.name], (line =~ /leader/ ? true : false)
|
121
|
-
if line =~ /following/
|
122
|
-
Group::MEMBERS.leader = OpenStruct.new(name: Char.name, leader: true)
|
123
|
-
end
|
124
|
-
nil
|
125
|
-
rescue Exception => e
|
126
|
-
respond e
|
127
|
-
respond e.backtrace
|
128
|
-
end
|
129
|
-
else
|
130
|
-
line
|
131
|
-
end
|
11
|
+
def self.clear()
|
12
|
+
@@members = []
|
13
|
+
@@checked = false
|
132
14
|
end
|
133
15
|
|
134
|
-
|
135
|
-
|
136
|
-
def Group.checked?
|
16
|
+
def self.checked?
|
137
17
|
@@checked
|
138
18
|
end
|
139
19
|
|
140
|
-
def
|
141
|
-
|
142
|
-
|
20
|
+
def self.push(*members)
|
21
|
+
members.each do |member|
|
22
|
+
@@members.push(member) unless include?(member)
|
23
|
+
end
|
143
24
|
end
|
144
25
|
|
145
|
-
def
|
146
|
-
|
26
|
+
def self.delete(*members)
|
27
|
+
gone = members.map(&:id)
|
28
|
+
@@members.reject! do |m| gone.include?(m.id) end
|
147
29
|
end
|
148
30
|
|
149
|
-
def
|
150
|
-
|
31
|
+
def self.members
|
32
|
+
maybe_check
|
33
|
+
@@members.dup
|
151
34
|
end
|
152
35
|
|
153
|
-
def
|
154
|
-
|
155
|
-
MEMBERS
|
36
|
+
def self._members
|
37
|
+
@@members
|
156
38
|
end
|
157
39
|
|
158
|
-
def
|
40
|
+
def self.disks
|
159
41
|
return [Disk.find_by_name(Char.name)].compact unless Group.leader?
|
160
|
-
members.map(&:
|
42
|
+
members.map(&:noun).map do |noun| Disk.find_by_name(noun) end.compact
|
161
43
|
end
|
162
44
|
|
163
|
-
def
|
164
|
-
|
45
|
+
def self.to_s
|
46
|
+
@@members.to_s
|
165
47
|
end
|
166
48
|
|
167
|
-
|
168
|
-
|
169
|
-
Group.unobserve()
|
170
|
-
@@checked = false
|
171
|
-
MEMBERS.clear!
|
172
|
-
DownstreamHook.add(CHECK_HOOK, PARSER)
|
173
|
-
Game._puts "<c>group\r\n"
|
174
|
-
wait_until { Group.checked? }
|
175
|
-
Group.observe()
|
176
|
-
MEMBERS
|
49
|
+
def self.checked=(flag)
|
50
|
+
@@checked = flag
|
177
51
|
end
|
178
52
|
|
179
|
-
def
|
180
|
-
|
53
|
+
def self.status=(state)
|
54
|
+
@@status = state
|
181
55
|
end
|
182
56
|
|
183
|
-
def
|
184
|
-
|
57
|
+
def self.status()
|
58
|
+
@@status
|
185
59
|
end
|
186
60
|
|
187
|
-
def
|
188
|
-
|
61
|
+
def self.open?
|
62
|
+
maybe_check
|
63
|
+
@@status.eql?(:open)
|
189
64
|
end
|
190
65
|
|
191
|
-
def
|
192
|
-
|
66
|
+
def self.closed?
|
67
|
+
not open?
|
193
68
|
end
|
194
69
|
|
70
|
+
# ran at the initialization of a script
|
71
|
+
def self.check
|
72
|
+
Group.clear()
|
73
|
+
ttl = Time.now + 3
|
74
|
+
Game._puts "<c>group\r\n"
|
75
|
+
wait_until { Group.checked? or Time.now > ttl }
|
76
|
+
@@members.dup
|
77
|
+
end
|
195
78
|
|
196
|
-
|
197
|
-
|
198
|
-
# <a exist="-10467645" noun="Oreh">Oreh</a> joins your group.
|
199
|
-
# You add <a exist="-10467645" noun="Oreh">Oreh</a> to your group.
|
200
|
-
# You remove <a exist="-10467645" noun="Oreh">Oreh</a> from the group.
|
201
|
-
# You disband your group.
|
202
|
-
JOIN = %r{^<a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> joins your group.$}
|
203
|
-
LEAVE = %r{^<a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> leaves your group.$}
|
204
|
-
ADD = %r{^You add <a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> to your group.$}
|
205
|
-
REMOVE = %r{^You remove <a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> from the group.$}
|
206
|
-
NOOP = %r{^But <a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> is already a member of your group!$}
|
207
|
-
EXIST = %r{<a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a>}
|
208
|
-
DISBAND = %r{^You disband your group}
|
209
|
-
ANY = Regexp.union(JOIN, LEAVE, ADD, REMOVE, NOOP)
|
79
|
+
def self.maybe_check
|
80
|
+
Group.check unless checked?
|
210
81
|
end
|
211
82
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
Group.consume(line.strip)
|
216
|
-
rescue => exception
|
217
|
-
respond exception
|
218
|
-
respond exception.backtrace
|
219
|
-
end
|
220
|
-
line
|
221
|
-
}
|
83
|
+
def self.nonmembers
|
84
|
+
GameObj.pcs.to_a.reject {|pc| ids.include?(pc.id) }
|
85
|
+
end
|
222
86
|
|
223
|
-
def self.
|
224
|
-
|
225
|
-
DownstreamHook.add("__group_observer", GROUP_OBSERVER)
|
87
|
+
def self.leader=(char)
|
88
|
+
@@leader = char
|
226
89
|
end
|
227
90
|
|
228
|
-
def self.
|
229
|
-
|
91
|
+
def self.leader
|
92
|
+
@@leader
|
230
93
|
end
|
231
94
|
|
232
|
-
|
233
|
-
|
234
|
-
def self.consume(line)
|
235
|
-
return unless line.match(Group::Term::ANY)
|
236
|
-
person = GameObj[Term::EXIST.match(line)[:id]]
|
237
|
-
case line
|
238
|
-
when Term::JOIN
|
239
|
-
Group.members.add(person) unless Group.members.include?(person)
|
240
|
-
when Term::ADD
|
241
|
-
Group.members.add(person) unless Group.members.include?(person)
|
242
|
-
when Term::NOOP
|
243
|
-
Group.members.add(person) unless Group.members.include?(person)
|
244
|
-
when Term::LEAVE
|
245
|
-
Group.members.members.delete(Group.members.find do |member|
|
246
|
-
member.id.eql?(person.id)
|
247
|
-
end)
|
248
|
-
when Term::REMOVE
|
249
|
-
Group.members.members.delete(Group.members.find do |member|
|
250
|
-
member.id.eql?(person.id)
|
251
|
-
end)
|
252
|
-
else
|
253
|
-
# silence is golden
|
254
|
-
end
|
255
|
-
Group.persist()
|
95
|
+
def self.leader?
|
96
|
+
@@leader.eql?(:self)
|
256
97
|
end
|
257
98
|
|
258
99
|
def self.add(*members)
|
@@ -260,34 +101,201 @@ module Group
|
|
260
101
|
if member.is_a?(Array)
|
261
102
|
Group.add(*member)
|
262
103
|
else
|
104
|
+
member = GameObj.pcs.find {|pc| pc.noun.eql?(member)} if member.is_a?(String)
|
105
|
+
|
106
|
+
return if member.nil?
|
107
|
+
|
263
108
|
result = dothistimeout("group ##{member.id}", 3, Regexp.union(
|
264
109
|
%r{You add #{member.noun} to your group},
|
265
110
|
%r{#{member.noun}'s group status is closed},
|
266
111
|
%r{But #{member.noun} is already a member of your group}))
|
267
112
|
|
268
113
|
case result
|
269
|
-
when %r{You add}
|
270
|
-
Group.
|
271
|
-
|
272
|
-
when %r{already a member}
|
273
|
-
[:noop, member]
|
114
|
+
when %r{You add}, %r{already a member}
|
115
|
+
Group.push(member)
|
116
|
+
{ok: member}
|
274
117
|
when %r{closed}
|
275
|
-
|
118
|
+
Group.delete(member)
|
119
|
+
{err: member}
|
276
120
|
else
|
277
121
|
end
|
278
122
|
end
|
279
123
|
end
|
280
124
|
end
|
281
125
|
|
282
|
-
def self.
|
283
|
-
|
126
|
+
def self.ids
|
127
|
+
@@members.map(&:id)
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.include?(*members)
|
131
|
+
members.all? { |m| ids.include?(m.id) }
|
284
132
|
end
|
285
133
|
|
286
134
|
def self.broken?
|
287
135
|
if Group.leader?
|
288
|
-
(GameObj.pcs.map(&:noun) &
|
136
|
+
(GameObj.pcs.map(&:noun) & @@members.map(&:noun)).size < @@members.size
|
289
137
|
else
|
290
138
|
GameObj.pcs.find do |pc| pc.noun.eql?(Group.leader.noun) end.nil?
|
291
139
|
end
|
292
140
|
end
|
293
|
-
|
141
|
+
|
142
|
+
def self.method_missing(method, *args, &block)
|
143
|
+
@@members.send(method, *args, &block)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class Group
|
148
|
+
module Observer
|
149
|
+
module Term
|
150
|
+
##
|
151
|
+
## passive messages
|
152
|
+
##
|
153
|
+
# <a exist="-10467645" noun="Oreh">Oreh</a> joins your group.
|
154
|
+
JOIN = %r{^<a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> joins your group.$}
|
155
|
+
# <a exist="-10467645" noun="Oreh">Oreh</a> leaves your group
|
156
|
+
LEAVE = %r{^<a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> leaves your group.$}
|
157
|
+
# You add <a exist="-10467645" noun="Oreh">Oreh</a> to your group.
|
158
|
+
ADD = %r{^You add <a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> to your group.$}
|
159
|
+
# You remove <a exist="-10467645" noun="Oreh">Oreh</a> from the group.
|
160
|
+
REMOVE = %r{^You remove <a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> from the group.$}
|
161
|
+
NOOP = %r{^But <a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> is already a member of your group!$}
|
162
|
+
# <a exist="-10488845" noun="Etanamir">Etanamir</a> designates you as the new leader of the group.
|
163
|
+
HAS_LEADER = %r{<a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> designates you as the new leader of the group\.$}
|
164
|
+
SWAP_LEADER = %r{<a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> designates <a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> as the new leader of the group.}
|
165
|
+
|
166
|
+
# You designate <a exist="-10778599" noun="Ondreian">Ondreian</a> as the new leader of the group.
|
167
|
+
GAVE_LEADER_AWAY = %r{You designate <a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> as the new leader of the group\.$}
|
168
|
+
# You disband your group.
|
169
|
+
DISBAND = %r{^You disband your group}
|
170
|
+
# <a exist="-10488845" noun="Etanamir">Etanamir</a> adds you to <a exist="-10488845" noun="Etanamir">his</a> group.
|
171
|
+
ADDED_TO_NEW_GROUP = %r{<a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> adds you to <a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> group.}
|
172
|
+
# You join <a exist="-10488845" noun="Etanamir">Etanamir</a>.
|
173
|
+
JOINED_NEW_GROUP = %r{You join <a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a>\.$}
|
174
|
+
# <a exist="-10488845" noun="Etanamir">Etanamir</a> adds <a exist="-10974229" noun="Szan">Szan</a> to <a exist="-10488845" noun="Etanamir">his</a> group.
|
175
|
+
LEADER_ADDED_MEMBER = %r{<a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> adds <a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> to <a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> group\.$}
|
176
|
+
# <a exist="-10488845" noun="Etanamir">Etanamir</a> removes <a exist="-10974229" noun="Szan">Szan</a> from the group.
|
177
|
+
LEADER_REMOVED_MEMBER = %r{<a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> removes <a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a> from the group\.$}
|
178
|
+
##
|
179
|
+
## active messages
|
180
|
+
##
|
181
|
+
NO_GROUP = /You are not currently in a group/
|
182
|
+
MEMBER = /<a exist="(?<id>.*?)" noun="(?<name>.*?)">(.*?)<\/a> is (?<type>(the leader|also a member) of your group|following you)\./
|
183
|
+
STATUS = /^Your group status is currently (?<status>open|closed)\./
|
184
|
+
|
185
|
+
GROUP_EMPTIED = %[<indicator id='IconJOINED' visible='n'/>]
|
186
|
+
GROUP_EXISTS = %[<indicator id='IconJOINED' visible='y'/>]
|
187
|
+
GIVEN_LEADERSHIP = %[designates you as the new leader of the group.]
|
188
|
+
|
189
|
+
ANY = Regexp.union(
|
190
|
+
JOIN,
|
191
|
+
LEAVE,
|
192
|
+
ADD,
|
193
|
+
REMOVE,
|
194
|
+
DISBAND,
|
195
|
+
NOOP,
|
196
|
+
STATUS,
|
197
|
+
NO_GROUP,
|
198
|
+
MEMBER,
|
199
|
+
|
200
|
+
HAS_LEADER,
|
201
|
+
SWAP_LEADER,
|
202
|
+
|
203
|
+
LEADER_ADDED_MEMBER,
|
204
|
+
LEADER_REMOVED_MEMBER,
|
205
|
+
|
206
|
+
ADDED_TO_NEW_GROUP,
|
207
|
+
JOINED_NEW_GROUP,
|
208
|
+
GAVE_LEADER_AWAY,
|
209
|
+
)
|
210
|
+
|
211
|
+
EXIST = %r{<a exist="(?<id>[\d-]+)" noun="(?<noun>[A-Za-z]+)">(?<name>\w+?)</a>}
|
212
|
+
end
|
213
|
+
|
214
|
+
CALLBACK = -> line {
|
215
|
+
begin
|
216
|
+
# fast first-pass
|
217
|
+
if line.include?("group") or line.include?("following you") or line.include?("IconJOINED")
|
218
|
+
# more detailed pass
|
219
|
+
if match_data = Observer.wants?(line)
|
220
|
+
Observer.consume(line.strip, match_data)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
rescue => exception
|
224
|
+
respond(exception)
|
225
|
+
respond(exception.backtrace)
|
226
|
+
ensure
|
227
|
+
return line
|
228
|
+
end
|
229
|
+
}
|
230
|
+
|
231
|
+
def self.exist(xml)
|
232
|
+
xml.scan(Group::Observer::Term::EXIST).map { |id, noun, name| GameObj[id] }
|
233
|
+
end
|
234
|
+
|
235
|
+
def self.wants?(line)
|
236
|
+
line.strip.match(Term::ANY) or
|
237
|
+
line.include?(Term::GROUP_EMPTIED)
|
238
|
+
end
|
239
|
+
|
240
|
+
def self.consume(line, match_data)
|
241
|
+
if line.include?(Term::GIVEN_LEADERSHIP)
|
242
|
+
return Group.leader = :self
|
243
|
+
end
|
244
|
+
|
245
|
+
## Group indicator changed!
|
246
|
+
if line.include?(Term::GROUP_EMPTIED)
|
247
|
+
Group.leader = :self
|
248
|
+
return Group._members.clear
|
249
|
+
end
|
250
|
+
|
251
|
+
people = exist(line)
|
252
|
+
|
253
|
+
if line.include?("is following you")
|
254
|
+
Group.leader = :self
|
255
|
+
elsif line.include?("is the leader of your group")
|
256
|
+
Group.leader = people.first
|
257
|
+
end
|
258
|
+
|
259
|
+
case line
|
260
|
+
when Term::NO_GROUP, Term::DISBAND
|
261
|
+
Group.leader = :self
|
262
|
+
return Group._members.clear
|
263
|
+
when Term::STATUS
|
264
|
+
Group.status = match_data[:status].to_sym
|
265
|
+
return Group.checked = true
|
266
|
+
when Term::GAVE_LEADER_AWAY
|
267
|
+
Group.push(people.first)
|
268
|
+
return Group.leader = people.first
|
269
|
+
when Term::ADDED_TO_NEW_GROUP, Term::JOINED_NEW_GROUP
|
270
|
+
Group.push(people.first)
|
271
|
+
return Group.leader = people.first
|
272
|
+
when Term::SWAP_LEADER
|
273
|
+
(old_leader, new_leader) = people
|
274
|
+
Group.push(*people) if Group.include?(old_leader) or Group.include?(new_leader)
|
275
|
+
return Group.leader = new_leader
|
276
|
+
when Term::LEADER_ADDED_MEMBER
|
277
|
+
(leader, added) = people
|
278
|
+
Group.push(added) if Group.include?(leader)
|
279
|
+
when Term::LEADER_REMOVED_MEMBER
|
280
|
+
(leader, removed) = people
|
281
|
+
return Group.delete(removed) if Group.include?(leader)
|
282
|
+
when Term::JOIN, Term::ADD, Term::NOOP, Term::MEMBER
|
283
|
+
return Group.push(*people)
|
284
|
+
when Term::LEAVE, Term::REMOVE
|
285
|
+
return Group.delete(*people)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
|
290
|
+
def self.attach()
|
291
|
+
remove() if DownstreamHook.list.include?(self.name)
|
292
|
+
DownstreamHook.add(self.name, CALLBACK)
|
293
|
+
end
|
294
|
+
|
295
|
+
def self.remove()
|
296
|
+
DownstreamHook.remove(self.name)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
Observer.attach()
|
301
|
+
end
|