-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathrkill.lic
871 lines (785 loc) · 25.9 KB
/
rkill.lic
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
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
=begin
rkill.lic: Hunting with target prioritization and online adjustable attack profiles.
This script allows setting up sequences of commands known as profiles, and having characters execute those sequences during hunting. Creatures are targetted in a semi-stable fashion, which allows for multiple characters operating this script at the same time to focus-fire creatures. Includes some ideas in bigshot.lic.
Author: Ericos
Contributors: Ineum
Game: Gemstone IV
tags: hunting
Version: 2.0
changelog:
2.1:
Make the core group a configurable variable
Protected profiles from being erased due to invalid commands
Improved intelligence and reliability of poaching logic
Adding checks for common movement scripts to reduce targetting while running
2.0 (2017-09-23):
Added target priorities for focusing on certain enemies type before others
Improved group join/leave detection
Wide ranging improvements to logic and robustness.
1.5:
Added logic for modifying attack profiles
1.0:
Initial release with attack profiles
=end
@script_name = script.name
def name
#Function to get active character name
Char.name
end
def get_current_group
#Get members of current group
group = [name]
clear
fput "group"
while line = get
case line
when /(?<name>\w+) is following|(?<name>\w+) is also a member|(?<name>\w+) is the leader/
group.push($~[:name])
when /Your group status/
break
end
end
group
end
@group_mutex = Mutex.new
@vars_mutex = Mutex.new
@room_mutex = Mutex.new
@group_mutex.synchronize {
@current_group = get_current_group
}
@current_room_counter = nil
@new_room = true
@poaching = true
@is_peer = false
@update_group = false
@check_retarget = false
def poaching?
#Test if someone in the room is unexpected, to avoid poaching.
#This respects room order, e.g. who was in the room first has hunting rights
#If that person(group) leaves, it is no longer considered poaching
@poach_targets ||= []
if @update_group
@groups_mutex.synchronize { @current_group = get_current_group }
@update_group = false
end
if !@current_room_counter || XMLData.room_count != @current_room_counter
@poach_targets = []
pcs = Set.new
@vars_mutex.synchronize { pcs.merge Vars[@script_name]['core_group'] }
@group_mutex.synchronize {
pcs.merge @current_group
}
party_members = Regexp.new pcs.to_a.join("|"), Regexp::IGNORECASE
#Build list of PC's not recognized, and also check disk names against party members
strange_pcs = GameObj.pcs.select {|pc| pc.name !~ party_members} || []
disk_pcs = GameObj.loot.select {|obj| obj.noun == "disk" && obj.name !~ party_members} || []
#We need nouns for comparison
@poach_targets = Set.new(strange_pcs.collect {|pc| pc.noun} + disk_pcs.collect {|disk|
disk.name =~ /(?<name>\w+) disk/
$~[:name]
}).to_a
respond "Room #{Room.current.id} Poaching: #{@poach_targets}"
@poaching = !@poach_targets.empty?
@current_room_counter = XMLData.room_count
else
old_strangers = Set.new @poach_targets
strange_pcs = GameObj.pcs.select {|pc| pc.name !~ party_members} || []
disk_pcs = GameObj.loot.select {|obj| obj.noun == "disk" && obj.name !~ party_members} || []
current_strangers = Set.new strange_pcs.collect {|pc| pc.noun} + disk_pcs.collect {|disk|
disk.name =~ /(?<name>\w+) disk/
$~[:name]
}
@poach_targets = (current_strangers & old_strangers).to_a
#Use set intersection as a fast way to tell if people left
if @poach_targets.empty?
if @poaching
respond "No longer poaching #{@poach_targets}"
end
@poaching = false
end
end
@poaching
end
def change_stance new_stance, force: true
#return unless new_stance =~ /off/ # pookahs/bog
return if Spell[1617].active? || Spell[216].active? || dead?
return if checkstance(new_stance)
return if checkcastrt > 0 && new_stance =~ /def/ && checkstance("guarded")
if force
result = dothistimeout "stance #{new_stance}", 3, /You are now in an?|Cast Round Time in effect|You are unable to change/
else
put "stance #{new_stance}"
end
end
def stand
#Goes defensive, then stands
until(standing?)
change_stance('defensive')
fput 'stand'
end
end
def valid_target? npc, invalid_types:nil, invalid_status: ["dead", "gone"], valid_types: nil
#Function which determines if an NPC is a valid target for combat, regardless of name
#Must not be of invalid_types or invalid_status, and must be of valid_types, if each one is set
valid_types_re = nil
invalid_types_re = nil
invalid_status_re = nil
if invalid_types && invalid_types.is_a?(Array) && !invalid_types.empty?
invalid_types_re = Regexp.new invalid_types.join("|")
end
if valid_types && valid_types.is_a?(Array) && !valid_types.empty?
valid_types_re = Regexp.new valid_types.join("|")
end
if invalid_status && invalid_status.is_a?(Array) && !invalid_status.empty?
invalid_status_re = Regexp.new invalid_status.join("|")
end
(valid_types_re.nil? || npc.type =~ valid_types_re) && (invalid_status_re.nil? || npc.status !~ invalid_status_re) && (invalid_types_re.nil? || npc.type !~ invalid_types_re)
end
def maintain_spells spells, min_mana: -1, max_mana: -1
#Ensures that all given spells are active, and casts to refresh them, assuming spells are affordable.
return if spells.all? {|spell| spell.active?}
return if min_mana >= 0 and checkmana <= min_mana
return if max_mana >= 0 and checkmana >= max_mana
res = []
spells.each { |spell|
if spell.known? and spell.affordable? and !spell.active?
waitcastrt?
clear
res.push spell.cast
waitcastrt?
end
}
res
end
def cmd_gos
sigils = [Spell[9707], Spell[9708], Spell[9710]]
if Spell[9715].known?
sigils.push Spell[9715]
elsif Spell[9705].known?
sigils.push Spell[9705]
end
maintain_spells sigils, min_mana: 15
end
def cmd_power
power = [Spell[9718]]
maintain_spells power, max_mana: 100
# Pause to ensure lich can track stamina/mana
pause 0.5
end
def cmd_col
signs = [Spell[9904], Spell[9907], Spell[9908], Spell[9912], Spell[9913]]
maintain_spells signs, min_mana: 5
end
def cmd_voln
symbols = [Spell[9805], Spell[9806]]
maintain_spells symbols
end
def cmd_smastery
#Check if cooldown is in effect
return if Spell[9604].active? || Spell[9603].active?
if @smastery_cooldown
respond Time.now - @smastery_cooldown
end
if !@smastery_cooldown || Time.now - @smastery_cooldown >= 180
respond "Shadow Mastery should cost #{Spell[9603].stamina_cost}, #{name} has #{checkstamina}, thus the affordable flag states #{Spell[9603].affordable?}"
res = maintain_spells [Spell[9603]]
unless res.empty?
@smastery_cooldown = Time.now
unless Spell[9603].active?
fput "spell active"
end
end
else
respond "Shadow Mastery is not detected as active, but it's been less than 3 minutes since the last firing. Rejecting update. This message indicates that active detection is likely buggy."
end
end
def cmd_tonis
return unless Spell[1035].known? and Spell[1035].affordable?
return if checkmana <= 85
waitcastrt?
Spell[1035].cast
waitcastrt?
end
def cmd_fury target
return unless Spell[635].known? and Spell[635].affordable?
return if checkmana <= 45
return if(hiding?)
return if GameObj.npcs.count { |npc| valid_target? npc, target_types: ["aggressive npc"]} < 5
waitcastrt?
Spell[635].cast "##{target.id}"
change_stance('guarded')
waitcastrt?
end
def cmd_spike target
return if target.status =~ /dead|gone/
return if checkmana <= 32
return if(hiding?)
return unless Spell[616].known? and Spell[616].affordable?
waitcastrt?
Spell[616].cast "##{target.id}"
change_stance('guarded')
waitcastrt?
end
def cmd_swarm target
return if target.status =~ /dead|gone/
return if target.status =~ /kneel|sit|lying|stunned/
return if GameObj.loot.find { |loot| loot.name =~ /swarm$|vine$/ }
return unless Spell[615].known? and Spell[615].affordable?
return if target.name =~ /lava golem|skayl|banshee|stone troll|stone giant/i
return if checkmana <= 140
return if(hiding?)
waitcastrt?
change_stance('offensive')
Spell[615].cast "##{target.id}"
change_stance('guarded')
waitcastrt?
end
def cmd_weed target
return if target.status =~ /dead|gone/
return if target.status =~ /kneel|sit|lying|stunned/
#TODO: Make this more intelligent about detecting enemy vines
return if GameObj.loot.find { |loot| loot.name =~ /swarm$|vine$/ }
return unless Spell[610].known? and Spell[610].affordable?
return if target.name =~ /lava golem|skayl|banshee/i
return if checkmana <= 140
return if(hiding?)
waitcastrt?
change_stance('offensive')
Spell[610].cast "##{target.id}"
change_stance('guarded')
waitcastrt?
end
def cmd_sounds target
return if target.status =~ /dead|gone/
return unless Spell[607].known? and Spell[607].affordable?
waitcastrt?
Spell[607].cast "##{target.id}"
waitcastrt?
end
def cmd_camo target
return if(hiding?)
return if target.status =~ /dead|gone/
maintain_spells [Spell[608]], min_mana: 40
end
def cmd_hide target
return if(hiding?)
return if target.status =~ /dead|gone/
fput "hide"
waitrt?
if matchtimeout 1, "You attempt to blend with the surroundings"
sleep 0.1
end
end
def cmd_ambush target
return if target.status =~ /dead|gone/
change_stance('offensive')
if target.status =~ /kneel|sit|lying|stunned/
result = dothistimeout( "ambush ##{target.id} head", 2, /You leap|You cannot|does not have a|already missing|Could not find|seconds/ )
if( result =~ /You cannot|already missing/ )
result = dothistimeout( "ambush ##{target.id} right leg", 2, /You leap|You cannot|does not have a|already missing|Could not find|seconds/ )
if( result =~ /does not have a|already missing/ )
result = dothistimeout( "ambush ##{target.id} left leg", 2, /You leap|You cannot|does not have a|already missing|Could not find|seconds/ )
if( result =~ /does not have a|already missing/ )
fput "ambush ##{target.id} head"
end
end
end
else
result = dothistimeout( "ambush ##{target.id} right leg", 2, /You leap|You cannot|does not have a|already missing|Could not find|seconds/ )
if( result =~ /does not have a|already missing/ )
result = dothistimeout( "ambush ##{target.id} left leg", 2, /You leap|You cannot|does not have a|already missing|Could not find|seconds/ )
if( result =~ /does not have a|already missing/ )
fput "kill ##{target.id}"
end
end
end
change_stance('defensive')
end
def cmd_kill target
return if target.status =~ /dead|gone/
change_stance('offensive')
fput "kill ##{target.id}"
change_stance('defensive')
end
def cmd_mstrike
if( !Spell[9005].active? && Skills.multiopponentcombat >= 15 && GameObj.npcs.all? { |i| i.noun !~ /nest/i } )
if( GameObj.npcs.size > 1 )
change_stance('offensive')
fput "mstrike"
change_stance('defensive')
else
change_stance('offensive')
fput "kill"
change_stance('defensive')
end
else
kill
end
end
def cmd_vibration target
return if target.status =~ /dead|gone/
return unless Spell[1002].known? and Spell[1002].affordable?
return if checkmana <= 45
waitcastrt?
Spell[1002].cast "##{target.id}"
waitcastrt?
end
def cmd_bone target
return if target.status =~ /dead|gone/
return unless Spell[1106].known? and Spell[1106].affordable?
waitcastrt?
fput "incant 1106"
change_stance "defensive"
waitcastrt?
end
def cmd_empathiclink target
return if target.status =~ /dead|gone/
return unless Spell[1117].known? and Spell[1117].affordable?
return if checkmana <= 45
return if GameObj.npcs.count { |npc| valid_target? npc, valid_types: ["bandit"]} < 4
waitcastrt?
Spell[1117].cast "##{target.id}"
change_stance('guarded')
waitcastrt?
end
def cmd_bind target
#Attempts to bind all monsters which are alive and unbound, while maintaining 50 mana
return unless Spell[214].known?
GameObj.npcs.each { |npc|
if checkmana > 50 && valid_target?(npc, invalid_status: ["frozen", "dead", "gone"], invalid_types: ["passive npc", "pet", "familiar", "companion"])
Spell[214].cast "##{npc.id}"
end
}
end
def find_priority_target valid_targets
target = nil
pri = nil
targets = []
GameObj.npcs.each {|npc|
valid_targets.each_pair {|key, val|
if npc.name =~ /#{key}/ && valid_target?(npc, invalid_types: ["passive npc", "pet", "familiar", "companion"])
targets.push [npc, val]
end
}
}
if !targets.empty?
target, pri = targets.min_by { |pair|
pair[1] }
end
[target, pri]
end
def find_any_target
target = GameObj.npcs.find { |npc|
if npc.type =~ /passive npc|pet|familiar|companion/ || Vars[@script_name]['untargettable'].include?(npc.name)
next
end
clear
fput "target ##{npc.id}"
line = get until line =~ /^Usage|^You are now (?<success>targeting)|^You do not have a target\.|You can only target creatures and players\.|You can't target|^Could not find a valid target\./
if !$~[:success]
Vars[@script_name]['untargettable'].push npc.name
end
$~[:success]
}
end
def cmd_sympathy
#Cast sympathy if enough creatures are present
sympathy = Spell[1120]
return unless sympathy.known? and sympathy.affordable?
return if GameObj.npcs.count { |npc| is_valid_target npc, target_types: ["bandit"] } < 6
sympathy.cast
end
def cmd_sweep target
return if target.status =~ /dead|gone/
return if target.status =~ /kneel|sit|lying|stunned/
return if(hiding?)
return if checkstamina <= 45
change_stance('offensive')
fput "cman sweep ##{target.id}"
change_stance('defensive')
end
def cmd_feint target
return if target.status =~ /dead|gone/
return if target.status =~ /kneel|sit|lying|stunned/
return if(hiding?)
return if checkstamina <= 45
change_stance('offensive')
fput "cman feint ##{target.id}"
change_stance('defensive')
end
def cmd_loot
#Loot dead bodies
nouns = nil
@vars_mutex.synchronize {
nouns = Vars[@script_name]['targets'].keys
}
safe_loot = true
@vars_mutex.synchronize {safe_loot = Vars[@script_name]['safe_loot']}
if safe_loot
targets = GameObj.npcs.select { |npc| !Vars[@script_name]['untargettable'].include?(npc.name) && valid_target?(npc, invalid_types: ["passive npc", "pet", "familiar", "companion"], invalid_status: nil)}
#Must have at least one creature present
return if targets.empty?
#All NPCs must be non-aggressive, or dead
return unless targets.all? {|npc| npc.status =~ /dead|gone/i}
else
return unless GameObj.npcs.any? {|npc| npc.status =~ /dead/ }
end
return if poaching?
waitrt?
start_script "sloot"
wait_while{running?('sloot')}
end
def print_target_priority priority
#Prints out a single priority level
targets = nil
@vars_mutex.synchronize {
targets = Vars[@script_name]['targets'].select { |npc, pri| pri == priority}
}
if !targets
respond "Priority #{priority} not found!"
return
end
respond "Targets at priority #{priority}: #{targets.keys}"
end
def print_all_targets
@vars_mutex.synchronize {
if Vars[@script_name]['targets'].empty?
respond "No targets defined!"
else
priorities = Hash.new
Vars[@script_name]['targets'].each_pair { |target, pri|
priorities[pri] ||= []
priorities[pri].push target
}
sorted_pris = priorities.keys.sort
sorted_pris.each{ |pri|
respond "#{pri} #{priorities[pri]}"
}
end
}
end
def moving?
#Simple wrapper around logic to test if character is in motion
running?("go2") || running?("wander")
end
def retarget? target, valid_targets
@check_retarget && valid_targets.none? {|noun, pri| /#{noun}/ =~ target.name}
end
def execute_commands
#This defines the core hunting loop for all characters, executing a sequence of functions from a specified profile
target = nil
target_pri = nil
last_profile = nil
select_target = false
cmds = []
loop {
#Rebuild list of commands each loop, for on-the-fly profile switches
profile = []
valid_targets = nil
use_target_list = true
@vars_mutex.synchronize {
current = Vars[@script_name]['current']
profile = Vars[@script_name]['profiles'][current].dup
valid_targets = Vars[@script_name]['targets'].dup
use_target_list = Vars[@script_name]['use_target_lists']
}
if profile != last_profile
select_target = false
cmds = profile.collect { |cmd|
m = method(cmd)
select_target = select_target || m.arity == 1
m
}
last_profile = profile
end
deltaT = 0.0
#Not poaching, valid commands, not in motion
if !dead? && !moving? && !poaching? && cmds
#Only look for a target if we need it
if target && (target.status =~ /dead|gone/ || retarget?(target, valid_targets))
target = nil
target_pri = nil
@check_retarget = false
end
if select_target && use_target_list
new_target, new_pri = find_priority_target valid_targets
if new_target && (!target || target.status =~ /dead|gone/ || !target_pri || new_pri < target_pri)
target = new_target
target_pri = new_pri
fput "target ##{target.id}"
end
elsif select_target && (!target || target.status =~ /dead|gone/)
target = find_any_target
end
startTime = Time.now
#Executes each command in order, providing target if requested
cmds.each{ |cmd|
#This means we need to retarget and restart command loop
if moving? || poaching? || retarget?(target, valid_targets)
break
end
if cmd.parameters[0].include?(:target)
if target
cmd.call target
end
else
cmd.call
end
}
deltaT = (Time.now - startTime)
end
if deltaT < 0.5
# Cap at 0.5s to not spam commands too fast
sleep 0.5 - deltaT
end
}
end
#This is the list of all commands which can be used in profiles. This is to ensure that incorrectly named or unexpected functions don't get injected
@all_cmds = ['cmd_gos', 'cmd_power', 'cmd_loot', 'cmd_voln', 'cmd_vibration', 'cmd_kill', 'cmd_smastery', 'cmd_camo', 'cmd_hide', 'cmd_ambush', 'cmd_swarm', 'cmd_weed', 'cmd_col', 'stand', 'cmd_bone', 'cmd_empathiclink', 'cmd_bind', 'cmd_sympathy', 'cmd_mstrike', 'cmd_sweep', 'cmd_feint', 'cmd_spike', 'cmd_tonis']
#NOTE: At this point, only one thread is messing with these vars, so there's no need to mutex.
#Sets up basic configuration structures
Vars[@script_name] ||= Hash.new
Vars[@script_name]['profiles'] ||= Hash.new
Vars[@script_name]['current'] ||= 'default'
Vars[@script_name]['targets'] ||= Hash.new
Vars[@script_name]['untargettable'] ||= []
Vars[@script_name]['core_group'] ||= []
if !Vars[@script_name].include? 'use_target_lists'
Vars[@script_name]['use_target_lists'] = true
end
if !Vars[@script_name].include? 'safe_loot'
Vars[@script_name]['safe_loot'] = true
end
def parse_names names
#Parse string of names into an array
names = Set.new (names.split(",") || []).map {|pc| pc.strip.delete('"').downcase}
names.to_a
end
@cmd_string = "#{$lich_char}#{@script_name}"
def handle_profile_cmds line
#Processes profile commands
case line
when /profile list/
#List profiles for the current character
respond "Profiles available for #{name}:"
@vars_mutex.synchronize {
Vars[@script_name]['profiles'].keys.each {|profile|
respond "* #{profile}"
}
}
when /profile set (?<pname>\w+)\s*=\s*(?<cmds>[\w, ]+)/
#Sets a profile to a string of commands
cmds = []
pname = $~[:pname]
$~[:cmds].split(",").each { |cmd|
cmd = cmd.strip
if !@all_cmds.include? cmd
respond "#{cmd} not a recognized command"
cmds = nil
break
end
cmds.push(cmd)
}
unless cmds.nil?
@vars_mutex.synchronize { Vars[@script_name]['profiles'][pname] = cmds }
respond "#{pname} = #{cmds.join(', ')}"
else
respond "Did not update #{pname} with invalid commands."
end
when /profile rm (?<pname>\w+)/
#Removes a profile
pname = $~[:pname]
@vars_mutex.synchronize { Vars[@script_name]['profiles'].delete pname }
respond "Removed profile #{pname} for #{name}"
when /profile info (?<pname>\w+)/
pname = $~[:pname]
@vars_mutex.synchronize {
if Vars[@script_name]['profiles'].include? pname
respond "#{pname}=#{Vars[@script_name]['profiles'][pname].join(",")}"
else
respond "#{pname} not a valid profile!"
end
}
when /profile current\s?(?<set>\w+)?/
@vars_mutex.synchronize {
if Vars[@script_name]['profiles'].include?($~[:set])
Vars[@script_name]['current'] = $~[:set]
elsif !$~[:set].nil?
respond "#{$~[:set]} is not a valid profile!"
end
}
respond "#{name} is using profile #{Vars[@script_name]['current']}"
when /profile help/
respond "Commands for editing attack sequence profiles"
respond "Usage: #{@cmd_string} profile [options]"
respond ""
respond "Options:"
respond " list: Lists all known profiles for the current character"
respond " set [profile=cmds]: Sets the named profile to the comma separated list of commands given"
respond " rm profile_name: Deletes named profile"
respond " info profile_name: Lists the command sequence of a given profile"
respond " current [profile_name]: Lists the current profile, or sets it to the given name if specified"
respond " help: Prints this help message"
end
end
def handle_target_cmds line
#Process commands based around priority target lists
case line
when /targets list\s?(?<pri>\d+)?/
if $~[:pri]
print_target_priority $~[:pri].to_i
else
print_all_targets
end
when /targets rm\s+(?<name>[\w "',]+)/
names = parse_names $~[:name]
@vars_mutex.synchronize {
names.each {|delname|
found = Vars[@script_name]['targets'].reject! { |tgt, pri| tgt =~ /^#{delname}$/i }
if found
respond "Removed #{delname} from target list"
@check_retarget = true
else
respond "Could not find #{delname} in target list"
end
}
}
when /targets add (?<pri>\d+)\s+[\[]?(?<name>[,\w "']+)[\]]?/
pri = $~[:pri].to_i
names = $~[:name].split(",")
@vars_mutex.synchronize {
names.each{ |name|
Vars[@script_name]['targets'][name.strip.delete '"'] = pri
}
}
respond "Added #{$~[:name]} as target priority #{pri}"
when /targets all=?(?<flag>on|off)?/
if $~[:flag]
flag = $~[:flag] =~ /off/i
@vars_mutex.synchronize { Vars[@script_name]['use_target_lists'] = flag }
end
if Vars[@script_name]['use_target_lists']
respond "Using targetting lists"
else
respond "Targetting any enemy in sight"
end
when /targets help/
respond "Control prioritized list of targets to hunt"
respond "Usage: #{@cmd_string} targets [options]"
respond " Handles the list of valid targets and priorities for hunting"
respond ""
respond "Options: "
respond " list [$priority]: Prints all creatures, or all creatures of a specified priority level"
respond " add $priority $name: Adds $name to $priority level"
respond " rm $priority $name: Removes $name from $priority level"
end
end
#Procedure for accepting core group commands during operation
def handle_group_cmds line
case line
when /group list/
@vars_mutex.synchronize {
respond "Core group: #{Vars[@script_name]['core_group'].map(&:capitalize)}"
}
when /group add (?<name>[\w ,'"]+)/
names = parse_names $~[:name]
@vars_mutex.synchronize {
pcs = Set.new(Vars[@script_name]['core_group'].map(&:downcase) + names)
Vars[@script_name]['core_group'] = pcs.to_a
}
respond "Added #{names.map {|name| name.capitalize}} to core group"
when /group rm (?<name>[\w' ,"]+)/
names = parse_names $~[:name]
@vars_mutex.synchronize {
names.each {|name|
pc = Vars[@script_name]['core_group'].delete name
if pc
respond "Removed #{name.capitalize} from core group"
else
respond "Could not find #{name.capitalize} to remove from core group"
end
}
}
when /group set (?<name>[\w, ]+)?/
pcs = Set.new parse_names($~[:name])
@vars_mutex.synchronize { Vars[@script_name]['core_group'] = pcs.to_a }
respond "Core group set to #{pcs.map(&:capitalize)}"
else
respond "Commands for adjusting the PC's always considered part of the group for poaching"
respond "Usage: #{@cmd_string} group [options]"
respond " list: Lists the current core group"
respond " add [name1,name2,...nameN]: Adds player name(s) to core group"
respond " rm [name]: Removes player name from core group"
respond " set [name1,name2,...nameN]: Overwrites the core group with the given list of names, with an empty list clearing the list"
end
end
#Thread for accepting commands during operation
command_hook = proc { |line|
case line
when /#{@cmd_string} update_group/
#Updates group for poaching tests
@update_group = true
nil
when /#{@cmd_string} commands/
respond "Available commands: #{@all_cmds}"
nil
when /#{@cmd_string} group/
handle_group_cmds line
nil
when /#{@cmd_string} profile/
handle_profile_cmds line
nil
when /#{@cmd_string} targets/
handle_target_cmds line
nil
when /#{@cmd_string} safe_loot\s+(?<flag>off|no|0|yes|on|1|true|false)?/i
if $~[:flag]
flag = $~[:flag] =~ /yes|on|1|true/i
@vars_mutex.synchronize {Vars[@script_name]['safe_loot'] = flag}
end
if flag
respond "Safe looting is on"
else
respond "Safe looting is off"
end
when /#{@cmd_string}.*/
respond "Script for automating attack patterns and target selection"
respond "Usage: #{@cmd_string} [command] [options]"
respond ""
respond "Commands:"
respond " update_group: Forces upating the group for poaching determination"
respond " commands: Lists available commands for profiles"
respond " profile: Commands for configuring and setting attack profiles"
respond " targets: Commands for setting target priorities"
respond " group: Commands for modifying the list of people who are always considered part of your group for poaching detection."
respond " safe_loot (on|off): Sets flag indicating if creatures should be looted in rooms with no living enemies, or whenever a dead creature is found."
else
line
end
}
#Procedure for processing people joining/leaving the group organically
group_hook = proc {|line|
case line
when /^(?<name>\w+) joins (your|\w+'s) group.$/
#Add person to group
@group_mutex.synchronize {
@current_group.push $~[:name]
}
when /^(?<name>\w+) leaves (your|\w+'s) group.$/
@group_mutex.synchronize {
@current_group.delete $~[:name]
}
end
line
}
UpstreamHook.add @cmd_string, command_hook
DownstreamHook.add "#{script.name} update_group", group_hook
before_dying {
UpstreamHook.remove @cmd_string
DownstreamHook.remove "#{script.name} update_group"
}
#If launched with commands, process them
unless script.vars.empty?
command_hook.call script.vars[0]
end
execute_commands