forked from matt-lowe/lich-scripts
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpassive-spellbot.lic
More file actions
324 lines (308 loc) · 14.4 KB
/
passive-spellbot.lic
File metadata and controls
324 lines (308 loc) · 14.4 KB
1
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
=begin
This script attempts to cast the right spells on people at the right
time without them asking. In theory, it won't cast a spell at someone
that knows the spell, or if it's a stackable spell and another cast will
put the duration over 4 hours 10 minutes, or if it's a refreshable
spell and they have more than 10 minutes remaining.
Only casts with "extra mana", or any time a noded pulse would put you
over your max mana.
;passive-spellbot help
author: Tillmen (tillmen@lichproject.org
game: Gemstone
tags: magic
version: 0.2
0.2 (2016-09-19):
add support for 1204 (Bitash)
=end
settings = CharSettings.to_hash
settings['mode'] ||= 'exclude'
settings['include'] ||= Array.new
settings['exclude'] ||= Array.new
settings['exclude-on-refuse'] = true if settings['exclude-on-refuse'].nil?
if script.vars[1].downcase == 'add' and script.vars[2]
settings[settings['mode']].push(script.vars[2].capitalize)
echo "Added #{script.vars[2].capitalize} to the #{settings['mode']} list."
exit
elsif (script.vars[1] =~ /^(?:del|delete|remove)$/i) and script.vars[2]
if settings[settings['mode']].include?(script.vars[2].capitalize)
settings[settings['mode']].delete(script.vars[2].capitalize)
echo "Deleted #{script.vars[2].capitalize} from the #{settings['mode']} list."
else
echo "#{script.vars[2].capitalize} was not in the #{settings['mode']} list."
end
exit
elsif script.vars[1].downcase == 'clear'
settings[settings['mode']].clear
echo "#{settings['mode']} list cleared."
exit
elsif script.vars[1].downcase =~ /mode=(include|exclude)/
settings['mode'] = $1
echo "Changed to #{settings['mode']} mode."
exit
elsif script.vars[1].downcase =~ /exclude-on-refuse=(yes|no)/
if $1 == 'yes'
settings['exclude-on-refuse'] = true
echo "People who refuse an LNet spells request will be excluded from spells (if using exclude mode)."
else
settings['exclude-on-refuse'] = false
echo "People who refuse an LNet spells request will get spells anyway."
end
exit
elsif script.vars[1].downcase == 'list'
output = "\n"
output.concat " mode: #{settings['mode']}\n"
output.concat " exclude-on-refuse: #{if settings['exclude-on-refuse']; 'yes'; else; 'no'; end}\n"
output.concat " exclude list: #{if settings['exclude'].empty?; '(empty)'; else; settings['exclude'].join(', '); end}\n"
output.concat " include list: #{if settings['include'].empty?; '(empty)'; else; settings['include'].join(', '); end}\n"
output.concat "\n"
respond output
elsif script.vars[1]
output = "\n"
output.concat " #{$clean_lich_char}#{script.name} mode=<include|exclude> Cast only at people on the include list, or\n"
output.concat " #{''.ljust(script.name.length + 1)} only at people not on the exclude list.\n"
output.concat " #{$clean_lich_char}#{script.name} exclude-on-refuse=<yes|no> Auto exclude people who refuse an LNet spells request.\n"
output.concat " #{$clean_lich_char}#{script.name} add <name> Add a name to the current list.\n"
output.concat " #{$clean_lich_char}#{script.name} delete <name> Delete a name from the current list.\n"
output.concat " #{$clean_lich_char}#{script.name} clear Clear the current list.\n"
output.concat " #{$clean_lich_char}#{script.name} list Shows current settings.\n"
output.concat "\n"
respond output
exit
end
script_name = script.name.dup
hook_proc = proc { |client_string|
begin
if client_string =~ /^(?:<c>)?#{$lich_char}#{Regexp.escape(script_name)}\s+(.+)/
vars = $1.split(/\s+/)
if vars[0].downcase == 'add' and vars[1]
settings[settings['mode']].push(vars[1].capitalize)
respond "[#{script_name}: Added #{vars[1].capitalize} to the #{settings['mode']} list.]"
nil
elsif (vars[0] =~ /^(?:del|delete|remove|clear)$/i) and vars[1]
if settings[settings['mode']].include?(vars[1].capitalize)
settings[settings['mode']].delete(vars[1].capitalize)
respond "[#{script_name}: Deleted #{vars[1].capitalize} from the #{settings['mode']} list.]"
else
respond "[#{script_name}: #{vars[1].capitalize} was not in the #{settings['mode']} list.]"
end
nil
elsif vars[0].downcase =~ /exclude-on-refuse=(yes|no)/
if $1 == 'yes'
settings['exclude-on-refuse'] = true
respond "[#{script_name}: People who refuse an LNet spells request will be excluded from spells (if using exclude mode).]"
else
settings['exclude-on-refuse'] = false
respond "[#{script_name}: People who refuse an LNet spells request will get spells anyway.]"
end
elsif vars[0].downcase == 'clear'
settings[settings['mode']].clear
echo "#{settings['mode']} list cleared."
nil
elsif vars[0].downcase =~ /mode=(include|exclude)/
settings['mode'] = $1
respond "[#{script_name}: Changed to #{settings['mode']} mode.]"
nil
elsif vars[0].downcase == 'list'
output = "\n"
output.concat " mode: #{settings['mode']}\n"
output.concat " exclude-on-refuse: #{if settings['exclude-on-refuse']; 'yes'; else; 'no'; end}\n"
output.concat " exclude list: #{if settings['exclude'].empty?; '(empty)'; else; settings['exclude'].join(', '); end}\n"
output.concat " include list: #{if settings['include'].empty?; '(empty)'; else; settings['include'].join(', '); end}\n"
output.concat "\n"
respond output
nil
elsif vars[0]
output = "\n"
output.concat " #{$clean_lich_char}#{script.name} mode=<include|exclude> Cast only at people on the include list, or\n"
output.concat " #{''.ljust(script.name.length + 1)} only at people not on the exclude list.\n"
output.concat " #{$clean_lich_char}#{script.name} exclude-on-refuse=<yes|no> Auto exclude people who refuse an LNet spells request.\n"
output.concat " #{$clean_lich_char}#{script.name} add <name> Add a name to the current list.\n"
output.concat " #{$clean_lich_char}#{script.name} delete <name> Delete a name from the current list.\n"
output.concat " #{$clean_lich_char}#{script.name} clear Clear the current list.\n"
output.concat " #{$clean_lich_char}#{script.name} list Shows current settings.\n"
output.concat "\n"
respond output
nil
else
client_string
end
else
client_string
end
rescue
UpstreamHook.remove(script_name)
client_string
end
}
before_dying { UpstreamHook.remove(script_name) }
UpstreamHook.add(script_name, hook_proc)
cast_list = [ 101, 103, 107, 202, 401, 406, 414, 503, 509, 602, 618, 1204, 1601 ]
stackable_max = 15000
refreshable_min = 600
disk_frequency = 43200
spell_messages = {
101 => /^A light blue glow surrounds <a.*?>([A-Z][a-z]+)<\/a>\.\r?$/,
103 => /^<a.*?>([A-Z][a-z]+)<\/a> suddenly looks more powerful\.\r?$/,
104 => /^<a.*?>([A-Z][a-z]+)<\/a>'s? body seems to glow with an internal strength\.\r?$/,
105 => /^<a.*?>([A-Z][a-z]+)<\/a>'s? veins stand out briefly\.\r?$/,
107 => /^A deep blue glow surrounds <a.*?>([A-Z][a-z]+)<\/a>\.\r?$/,
112 => /^A misty halo surrounds <a.*?>([A-Z][a-z]+)<\/a>\.\r?$/,
202 => /^(?:A dim aura surrounds|There is already a dim aura around) <a.*?>([A-Z][a-z]+)<\/a>\.\r?$/,
204 => /^<a.*?>([A-Z][a-z]+)<\/a> appears more secure\.\r?$/,
207 => /^<a.*?>([A-Z][a-z]+)<\/a> begins to breathe more deeply\.\r?$/,
401 => /^A silvery luminescence surrounds <a.*?>([A-Z][a-z]+)<\/a>\.\r?$/,
403 => /^A scintillating light surrounds <a.*?>([A-Z][a-z]+)<\/a>'s? hands\.\r?$/,
404 => /^<a.*?>([A-Z][a-z]+)<\/a> becomes calm and focused\.\r?$/,
406 => /^A bright luminescence surrounds <a.*?>([A-Z][a-z]+)<\/a>\.\r?$/,
414 => /^A brilliant luminescence surrounds <a.*?>([A-Z][a-z]+)<\/a>\.\r?$/,
503 => /^Glowing specks of \w+ \w+ energy begin to spin around <a.*?>([A-Z][a-z]+)<\/a>\.\r?$/,
509 => /^<a.*?>([A-Z][a-z]+)<\/a> looks considerably more imposing\.\r?$/,
602 => /^The air about <a.*?>([A-Z][a-z]+)<\/a> shimmers slightly\.\r?$/,
618 => /^<a.*?>([A-Z][a-z]+)<\/a> suddenly looks much more dextrous\.\r?$/,
1204 => /^<a.*?>([A-Z][a-z]+)<\/a> gazes into the distance before adopting a thoughtful expression\.\r?$/,
1601 => /^(?:The dully illuminated mantle surrounding|A whirl of spiritual energy streaks down from above, creating a dully illuminated mantle around) ([A-Z][a-z]+)'s? form(?: seems to gain some cohesion)?\.\r?$/,
}
$passive_spellbot_timers ||= Hash.new
$passive_spellbot_last_check ||= Hash.new
$passive_spellbot_last_disk ||= Hash.new
spell_messages.delete_if { |num,message| not cast_list.include?(num) or Spell[num].nil? or not Spell[num].known? }
cast_disk = (cast_list.include?(511) and Spell[511].known?)
cast_list.delete_if { |num| not spell_messages.keys.include?(num) }
in_room_timer = Hash.new
seen_spell = proc { |num,target,caster|
spell = Spell[num]
time_per = spell.time_per(:caster=>caster, :target=>target)
$passive_spellbot_timers[target] ||= Hash.new
if spell.stackable?(:caster=>caster, :target=>target)
if $passive_spellbot_timers[target][num].nil? or ($passive_spellbot_timers[target][num] < Time.now)
$passive_spellbot_timers[target][num] = Time.now
end
$passive_spellbot_timers[target][num] = [ ($passive_spellbot_timers[target][num] + (time_per * 60)), (Time.now + 250*60) ].min
else
$passive_spellbot_timers[target][num] = Time.now + ([time_per, 250].min * 60)
end
if caster
caster_echo = "(#{caster}) "
else
caster_echo = ''
end
"\r\n[ #{target}: #{spell.name}: +#{time_per.as_time}, #{(($passive_spellbot_timers[target][num] - Time.now)/60.0).as_time} remaining. #{caster_echo}]\r\n"
}
knows_103 = Spell[103].known?
my_target = nil
caster = nil
hook_proc = proc { |server_string|
if server_string =~ /<prompt/
caster = nil
my_target = nil
elsif server_string =~ /^(?:<.*?>)?<a.*?>([A-Z][a-z]+)<\/a> gestures/
caster = $1
elsif server_string =~ /<pushStream id="death"\/> \* <a.*?>(.*?)<\/a> just bit the dust!/
$passive_spellbot_timers.delete($1)
$passive_spellbot_last_disk.delete($1)
elsif knows_103 and (server_string =~ /^(?:<.*?>)?You gesture at <a.*?>([A-Z][a-z]+)<\/a>\.\r$/)
my_target = $1
elsif knows_103 and my_target and (server_string =~ /^Your spell misfires\.\r$/)
seen_spell.call(103, my_target, caster)
else
for num,message in spell_messages
if server_string =~ message
time_msg = seen_spell.call(num, target=$1, caster)
server_string.chomp!.concat(time_msg)
break
end
end
end
server_string
}
knows_spell = proc { |name,num|
circle = num.to_s[0..-3].to_i
rank = num.to_s[-2..-1].to_i
if circle == 1
SpellRanks[name].minorspiritual >= rank
elsif circle == 2
SpellRanks[name].majorspiritual >= rank
elsif circle == 3
SpellRanks[name].cleric >= rank
elsif circle == 4
SpellRanks[name].minorelemental >= rank
elsif circle == 5
SpellRanks[name].majorelemental >= rank
elsif circle == 6
SpellRanks[name].ranger >= rank
elsif circle == 16
SpellRanks[name].paladin >= rank
end
}
begin
DownstreamHook.add('passive-spellbot', hook_proc)
loop {
if cast_disk
in_room_timer.delete_if { |name,time| !checkpcs.include?(name) }
checkpcs.each { |name| in_room_timer[name] ||= Time.now }
end
if (mana > (max_mana - noded_pulse)) and (checkrt == 0) and (checkcastrt == 0)
if cast_disk and (target = GameObj.pcs.find { |pc| !knows_spell.call(pc.noun, 511) and ((settings['mode'] == 'exclude' and not settings['exclude'].include?(pc.noun)) or (settings['mode'] == 'include' and settings['include'].include?(pc.noun))) and ($passive_spellbot_last_disk[pc.noun].nil? or ($passive_spellbot_last_disk[pc.noun] + disk_frequency) < Time.now) and (pc.status !~ /dead|hidden/) and not GameObj.loot.any? { |obj| obj.name =~ /#{pc.noun} (?:disk|coffin)$/ } and (in_room_timer[pc.noun].class == Time) and (in_room_timer[pc.noun] + 20 < Time.now) })
cast 511, target
if GameObj.loot.any? { |obj| obj.name =~ /#{target.noun} (?:disk|coffin)$/ }
$passive_spellbot_last_disk[target.noun] = Time.now
end
else
target_list = GameObj.pcs.find_all { |pc| (pc.status !~ /dead|hidden/) and ((settings['mode'] == 'exclude' and not settings['exclude'].include?(pc.noun)) or (settings['mode'] == 'include' and settings['include'].include?(pc.noun))) }
spell_target_combo = Array.new
cast_list.each { |spell_num|
target_list.each { |target|
unless knows_spell.call(target.noun,spell_num)
if Spell[spell_num].stackable?(:target=>true)
unless ($passive_spellbot_timers[target.noun][spell_num].class == Time) and ($passive_spellbot_timers[target.noun][spell_num] + (Spell[spell_num].time_per(:target=>true) * 60) > Time.now + stackable_max)
spell_target_combo.push [ spell_num, target.noun ]
end
else
unless ($passive_spellbot_timers[target.noun][spell_num].class == Time) and ($passive_spellbot_timers[target.noun][spell_num] > Time.now + refreshable_min)
spell_target_combo.push [ spell_num, target.noun ]
end
end
end
}
}
if spell_target_combo.length > 0
spell_target_combo.sort! { |a,b| $passive_spellbot_timers[a[1]][a[0]].to_i <=> $passive_spellbot_timers[b[1]][b[0]].to_i }
spell_num = spell_target_combo[0][0]
target_name = spell_target_combo[0][1]
if $passive_spellbot_last_check[target_name].nil? or (Time.now - $passive_spellbot_last_check[target_name]) > 3600
data = LNet.get_data(target_name, 'spells')
if data.nil?
if settings['exclude-on-refuse'] and (settings['mode'] == 'exclude')
settings['exclude'].push(target_name)
end
echo "spell data for #{target_name}: refused"
elsif data == false
echo "spell data for #{target_name}: failed"
elsif data.empty?
echo "spell data for #{target_name}: empty"
else
echo "spell data for #{target_name}: #{data.collect { |a,b| "#{a}(#{b.to_s.slice(/[0-9]+(?:\.[0-9])?/)})" }.join(', ')}"
end
$passive_spellbot_last_check[target_name] = Time.now
if data
$passive_spellbot_timers[target_name] = Hash.new
data.each_pair { |spell,duration|
if cast_list.include?(spell.to_i)
$passive_spellbot_timers[target_name][spell.to_i] = Time.now + (duration * 60)
end
}
end
elsif target = GameObj.pcs.find { |pc| pc.noun == target_name }
result = cast(spell_num, "at ##{target.id}")
if result =~ /^Provoking a GameMaster is not such a good idea\./
settings['exclude'].push(target_name)
end
end
end
end
end
sleep 0.2
}
ensure
DownstreamHook.remove('passive-spellbot')
end