Olib 0.0.1
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 +7 -0
- data/lib/Olib.rb +13 -0
- data/lib/Olib/container.rb +89 -0
- data/lib/Olib/creature.rb +158 -0
- data/lib/Olib/creatures.rb +52 -0
- data/lib/Olib/dictionary.rb +124 -0
- data/lib/Olib/extender.rb +19 -0
- data/lib/Olib/group.rb +68 -0
- data/lib/Olib/item.rb +58 -0
- data/lib/Olib/transport.rb +178 -0
- metadata +53 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: dbb317934472d698d73d8f573974f0ab38d3862e
|
4
|
+
data.tar.gz: f766e1b784c79ee939e2b54ff81e0f702711bea4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d9cfc21b44c0d74d5a933dd7539521c84e3c9d0ba4e0f475a7e3ecbcce445a45d2e9ad3238450f86a25ae13992fab8a189ee97411e381ffd00409e8bce3de6e9
|
7
|
+
data.tar.gz: 456fd51c9083eaa54bc77f13ea5f00c0c22cffff1cc9b9108458f3a47b60130febeb042fc1485abe4f6c88e075e073d6ebc82fde86c0bceb824f60b2760920c8
|
data/lib/Olib.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
module Olib
|
2
|
+
def Olib.do(action, re)
|
3
|
+
dothistimeout action, 5, re
|
4
|
+
end
|
5
|
+
require 'Olib/group'
|
6
|
+
require 'Olib/creature'
|
7
|
+
require 'Olib/creatures'
|
8
|
+
require 'Olib/extender'
|
9
|
+
require 'Olib/transport'
|
10
|
+
require 'Olib/item'
|
11
|
+
require 'Olib/dictionary'
|
12
|
+
require 'Olib/container'
|
13
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# for defining containers ala lootsack and using them across scripts
|
2
|
+
module Olib
|
3
|
+
class Container < Gameobj_Extender
|
4
|
+
attr_accessor :ref, :types, :full
|
5
|
+
|
6
|
+
def initialize(id)
|
7
|
+
# set the default value of full being false, we can only know by attempting to add something
|
8
|
+
@full = false
|
9
|
+
|
10
|
+
# extract the class name to attempt to lookup the item by your settings
|
11
|
+
# ex: class Lootsack
|
12
|
+
# ex: class Gemsack
|
13
|
+
setting = self.class.name.downcase
|
14
|
+
|
15
|
+
@ref = GameObj[@id] || (GameObj.inv.find { |obj| obj.name =~ /\b#{Regexp.escape(UserVars.send(setting).strip)}$/i } || GameObj.inv.find { |obj| obj.name =~ /\b#{Regexp.escape(UserVars.send(setting)).sub(' ', ' .*')}$/i } || GameObj.inv.find { |obj| obj.name =~ /\b#{Regexp.escape(UserVars.send(setting)).sub(' ', ' .*')}/i })
|
16
|
+
|
17
|
+
return "error: failed to find your #{setting.to_s}" unless @ref
|
18
|
+
|
19
|
+
fput "look in ##{@ref.id}" unless GameObj[@ref.id].contents
|
20
|
+
self._extend(@ref)._constructor
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def worn?
|
26
|
+
GameObj.inv.collect { |item| item.id }.include? @ref.id
|
27
|
+
end
|
28
|
+
|
29
|
+
def [](query)
|
30
|
+
return contents.select do |item|
|
31
|
+
item if (item.type =~ query || item.noun =~ query || item.name =~ query)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def contents
|
36
|
+
contents = []
|
37
|
+
@types.each do |method, exp| contents.push self.send method.to_sym end
|
38
|
+
return contents.flatten.uniq(&:id)
|
39
|
+
end
|
40
|
+
|
41
|
+
def _constructor
|
42
|
+
singleton = (class << self; self end)
|
43
|
+
@types = { #method => #regex
|
44
|
+
"gems" => /gem/,
|
45
|
+
"boxes" => /box/,
|
46
|
+
"scrolls" => /scroll/,
|
47
|
+
"herbs" => /herb/,
|
48
|
+
"jewelry" => /jewelry/,
|
49
|
+
"magic" => /magic/,
|
50
|
+
"clothing" => /clothing/,
|
51
|
+
"uncommons" => /uncommon/,
|
52
|
+
"unknowns" => nil
|
53
|
+
}
|
54
|
+
@types.each do |method, exp|
|
55
|
+
singleton.send :define_method, method.to_sym do
|
56
|
+
|
57
|
+
matches = exp.nil? ?
|
58
|
+
GameObj[@id].contents.select do |item| item.type.nil? end :
|
59
|
+
GameObj[@id].contents.select do |item| item.type =~ exp end
|
60
|
+
|
61
|
+
matches.map! do |item| eval("Olib::#{method.gsub(/es$/,'').gsub(/s$/, '').capitalize!}").new(item) end
|
62
|
+
|
63
|
+
matches
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def __verbs__
|
69
|
+
@verbs = 'open close analyze inspect weight'.split(' ').map(&:to_sym)
|
70
|
+
singleton = (class << self; self end)
|
71
|
+
@verbs.each do |verb|
|
72
|
+
singleton.send :define_method, verb do
|
73
|
+
fput "#{verb.to_s} ##{@id}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def full?
|
79
|
+
return @full
|
80
|
+
end
|
81
|
+
|
82
|
+
def add(item)
|
83
|
+
result = Olib.do "_drag ##{item.id} ##{@id}", /#{[Olib::Gemstone_Regex.put[:success], Olib::Gemstone_Regex.put[:failure].values].flatten.join('|')}/
|
84
|
+
@full = true if result =~ /won't fit in the/
|
85
|
+
self
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# enables creature specific logic
|
2
|
+
# TODO
|
3
|
+
# - add flying types
|
4
|
+
|
5
|
+
require 'Olib/extender'
|
6
|
+
require 'Olib/dictionary'
|
7
|
+
|
8
|
+
module Olib
|
9
|
+
class Creature < Gameobj_Extender
|
10
|
+
attr_accessor :wounds, :targetable, :can_cast, :type, :data, :legged, :limbed
|
11
|
+
|
12
|
+
def initialize(creature)
|
13
|
+
@wounds = {}
|
14
|
+
@data = {}
|
15
|
+
|
16
|
+
@data[:type] = 'unknown'
|
17
|
+
@data[:trollish] = creature.name =~ /troll|csetari/ ? true : false
|
18
|
+
@data[:incapacitated] = false
|
19
|
+
|
20
|
+
if creature.name =~ Gemstone_Regex.undead then @data[:type] = 'undead'; @targetable = true; end
|
21
|
+
if creature.name =~ Gemstone_Regex.living then @data[:type] = 'living'; @targetable = true; end
|
22
|
+
if creature.name =~ Gemstone_Regex.antimagic then @data[:type] = 'antimagic'; @targetable = true; end
|
23
|
+
if creature.noun =~ Gemstone_Regex.bandits then @data[:type] = 'bandit'; @targetable = true; end
|
24
|
+
if creature.noun =~ Gemstone_Regex.escortees then @data[:type] = 'escortee'; @targetable = false; end
|
25
|
+
if creature.name =~ /taladorian/i then @data[:type] = 'invasion'; @targetable = true; end
|
26
|
+
if creature.name =~ /grimswarm/i then @data[:type] = 'grimswarm'; @targetable = true; end
|
27
|
+
if creature.noun =~ /kobold|rolton|velnalin|urgh/ then @data[:type] = 'ignoreable'; @targetable = true; end
|
28
|
+
heal
|
29
|
+
# call the Gameobj_Extender initialize method that copies the game properties to this class
|
30
|
+
super(creature)
|
31
|
+
end
|
32
|
+
|
33
|
+
def type
|
34
|
+
@data[:type]
|
35
|
+
end
|
36
|
+
|
37
|
+
def is?(t)
|
38
|
+
type == t
|
39
|
+
end
|
40
|
+
|
41
|
+
def heal
|
42
|
+
[:right_leg, :left_leg, :right_arm, :left_arm, :head, :left_eye, :right_eye].each do |location| @wounds[location] = 0 end
|
43
|
+
@wounds
|
44
|
+
end
|
45
|
+
def injuries
|
46
|
+
fput "look ##{@id}"
|
47
|
+
woundinfo = matchtimeout(2, /(he|she|it) (?:has|appears) .*/i)
|
48
|
+
if woundinfo =~ /appears to be in good shape/ then heal; return @wounds; end
|
49
|
+
if woundinfo =~ /severed right leg/ then @wounds[:right_leg] = 3; end
|
50
|
+
if woundinfo =~ /severed left leg/ then @wounds[:left_leg] = 3; end
|
51
|
+
if woundinfo =~ /severed right arm/ then @wounds[:right_arm] = 3; end
|
52
|
+
if woundinfo =~ /severed left arm/ then @wounds[:left_arm] = 3; end
|
53
|
+
if woundinfo =~ /severe head trauma and bleeding from .* ears/ then @wounds[:head] = 3; end
|
54
|
+
if woundinfo =~ /blinded left eye/ then @wounds[:left_eye] = 3; end
|
55
|
+
if woundinfo =~ /blinded right eye/ then @wounds[:right_eye] = 3; end
|
56
|
+
if woundinfo =~ /severed right hand/ then @wounds[:right_hand]= 3; end
|
57
|
+
if woundinfo =~ /severed left hand/ then @wounds[:left_hand] = 3; end
|
58
|
+
@wounds
|
59
|
+
end
|
60
|
+
|
61
|
+
def status
|
62
|
+
GameObj[@id].status
|
63
|
+
end
|
64
|
+
|
65
|
+
def trollish?
|
66
|
+
@data[:trollish]
|
67
|
+
end
|
68
|
+
|
69
|
+
def legged?
|
70
|
+
injuries
|
71
|
+
trollish? ? false : @wounds[:right_leg] == 3 || @wounds[:left_leg] == 3 || dead? || gone?
|
72
|
+
end
|
73
|
+
|
74
|
+
def can_cast?
|
75
|
+
injuries
|
76
|
+
trollish? ? false : @wounds[:right_arm] == 3 || @wounds[:head] == 3 || dead? || gone?
|
77
|
+
end
|
78
|
+
|
79
|
+
def limbed?
|
80
|
+
injuries
|
81
|
+
trollish? ? false : @wounds[:right_leg] == 3 || @wounds[:left_leg] == 3 || @wounds[:right_arm] == 3 || dead? || gone?
|
82
|
+
end
|
83
|
+
|
84
|
+
def dead?
|
85
|
+
status =~ /dead/ ? true : false
|
86
|
+
end
|
87
|
+
|
88
|
+
def active?
|
89
|
+
!stunned?
|
90
|
+
end
|
91
|
+
|
92
|
+
def gone?
|
93
|
+
GameObj[@id].nil? ? true : false
|
94
|
+
end
|
95
|
+
|
96
|
+
def prone?
|
97
|
+
status =~ /lying|prone/ ? true : false
|
98
|
+
end
|
99
|
+
|
100
|
+
def stunned?
|
101
|
+
status =~ /stunned/ ? true : false
|
102
|
+
end
|
103
|
+
|
104
|
+
def kill_shot
|
105
|
+
wounds = injuries
|
106
|
+
location = "left eye"
|
107
|
+
location = "right eye" if @wounds[:left_eye] == 3
|
108
|
+
location = "head" if @wounds[:right_eye] == 3
|
109
|
+
location = "neck" if @wounds[:head] == 3
|
110
|
+
location = "back" if @wounds[:neck] == 3
|
111
|
+
Client.notify "#{@name} >> #{location}"
|
112
|
+
location
|
113
|
+
end
|
114
|
+
|
115
|
+
def target
|
116
|
+
result = dothistimeout "target ##{@id}", 3, /#{Olib::Gemstone_Regex.targetable.values.join('|')}/
|
117
|
+
@targetable = result =~ Olib::Gemstone_Regex.targetable[:yes] ? true : false
|
118
|
+
self
|
119
|
+
end
|
120
|
+
|
121
|
+
def search
|
122
|
+
fput "search ##{@id}" if dead?
|
123
|
+
end
|
124
|
+
|
125
|
+
def ambush(location=nil)
|
126
|
+
until hidden?
|
127
|
+
fput "hide"
|
128
|
+
waitrt?
|
129
|
+
end
|
130
|
+
fput "aim #{location}" if location
|
131
|
+
fput "ambush ##{@id}"
|
132
|
+
waitrt?
|
133
|
+
self
|
134
|
+
end
|
135
|
+
|
136
|
+
def mstrike
|
137
|
+
fput "mstrike ##{@id}"
|
138
|
+
waitrt?
|
139
|
+
self
|
140
|
+
end
|
141
|
+
|
142
|
+
def kill
|
143
|
+
fput "kill ##{@id}"
|
144
|
+
waitrt?
|
145
|
+
self
|
146
|
+
end
|
147
|
+
|
148
|
+
def targetable?
|
149
|
+
target if @targetable.nil?
|
150
|
+
@targetable
|
151
|
+
end
|
152
|
+
|
153
|
+
def search
|
154
|
+
waitrt?
|
155
|
+
fput "search ##{id}"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'Olib/creature'
|
2
|
+
# a collection for managing all of the creatures in a room
|
3
|
+
module Olib
|
4
|
+
class Creatures
|
5
|
+
attr_accessor :untargetables, :ignore, :collection
|
6
|
+
def initialize(ignore=true)
|
7
|
+
@ignore = ignore
|
8
|
+
self
|
9
|
+
end
|
10
|
+
|
11
|
+
def first
|
12
|
+
all.first
|
13
|
+
end
|
14
|
+
|
15
|
+
def all
|
16
|
+
GameObj.npcs.map {|creature| Olib::Creature.new(creature) }.select {|creature| creature.targetable && !creature.is?("ignoreable") && !creature.dead? && !creature.gone? } || []
|
17
|
+
end
|
18
|
+
|
19
|
+
def each(&block)
|
20
|
+
all.each(&block)
|
21
|
+
end
|
22
|
+
|
23
|
+
def [](exp)
|
24
|
+
regexp = exp.class == String ? /#{exp}/ : exp
|
25
|
+
|
26
|
+
all.select { |creature| creature.name =~ regexp || creature.id == exp }
|
27
|
+
end
|
28
|
+
def bandits; all.select { |creature| creature.is?('bandit') } ;end;
|
29
|
+
def flying; all.select { |creature| creature.is?('flying') } ;end;
|
30
|
+
def living; all.select { |creature| creature.is?('living') } ;end;
|
31
|
+
def antimagic; all.select { |creature| creature.is?('antimagic') } ;end;
|
32
|
+
def undead; all.select { |creature| creature.is?('undead') } ;end;
|
33
|
+
|
34
|
+
def grimswarm; all.select { |creature| creature.is?('grimswarm') } ;end;
|
35
|
+
def invasion; all.select { |creature| creature.is?('invasion') } ;end;
|
36
|
+
def escortees; GameObj.npcs.map {|creature| Creature.new(creature) }.select {|creature| creature.is?('escortee') } ;end;
|
37
|
+
|
38
|
+
def stunned; all.select(&:stunned?) ;end;
|
39
|
+
def active; all.select(&:active?) ;end;
|
40
|
+
def dead; all.select(&:dead?) ;end;
|
41
|
+
def prone; all.select(&:prone?) ;end;
|
42
|
+
|
43
|
+
def ambushed?
|
44
|
+
last_line = $_SERVERBUFFER_.reverse.find { |line| line =~ /<pushStream id='room'\/>|An? .*? fearfully exclaims, "It's an ambush!"|#{Olib::Gemstone_Regex.bandit_traps.values.join('|')}/ }
|
45
|
+
echo "detected ambush..." if !last_line.nil? && last_line !~ /pushStream id='room'/
|
46
|
+
|
47
|
+
!last_line.nil? && last_line !~ /pushStream id='room'/
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module Olib
|
2
|
+
|
3
|
+
class Gemstone_Regex
|
4
|
+
def Gemstone_Regex.item
|
5
|
+
re = {}
|
6
|
+
re[:heirloom] = /are the initials ([A-Z]{2})./
|
7
|
+
re[:give] = /Excellent. I'm sure the person who lost this will be quite happy/
|
8
|
+
re
|
9
|
+
end
|
10
|
+
def Gemstone_Regex.targetable
|
11
|
+
re = {}
|
12
|
+
re[:yes] = /^You are now targeting/
|
13
|
+
re[:no] = /^You can't target/
|
14
|
+
re
|
15
|
+
end
|
16
|
+
def Gemstone_Regex.bounty
|
17
|
+
re = {}
|
18
|
+
re[:herb] = /requires (?:a|an|some) ([a-zA-Z '-]+) found (?:in|on|around) ([a-zA-Z '-]+). These samples must be in pristine condition. You have been tasked to retrieve ([0-9]+)/
|
19
|
+
re[:escort] = /Go to the (.*?) and WAIT for (?:him|her|them) to meet you there. You must guarantee (?:his|her|their) safety to ([a-zA-Z '-]+) as soon as/
|
20
|
+
re[:gem] = /has received orders from multiple customers requesting (?:a|an|some) ([a-zA-Z '-]+). You have been tasked to retrieve ([0-9]+)/
|
21
|
+
re[:heirloom] = /You have been tasked to recover ([a-zA-Z '-]+) that an unfortunate citizen lost after being attacked by (a|an|some) ([a-zA-Z '-]+) (in|on|around|near|by) ([a-zA-Z '-]+)./
|
22
|
+
re[:heirloom_found] = /^You have located the heirloom and should bring it back to/
|
23
|
+
re[:turn_in] = /You have succeeded in your task and can return to the Adventurer's Guild to receive your reward/
|
24
|
+
re[:guard_turn_in] = /^You succeeded in your task and should report back to/
|
25
|
+
re[:guard_bounty] = /Go report to ([a-zA-Z ]+) to find out more/
|
26
|
+
re[:cull] = /^You have been tasked to suppress (^((?!bandit).)*$) activity (?:in|on) (?:the )? (.*?)(?: near| between| under|\.) ([a-zA-Z' ]+). You need to kill ([0-9]+)/
|
27
|
+
re[:bandits] = /^You have been tasked to suppress bandit activity (?:in |on )(?:the )(.*?)(?: near| between| under) ([a-zA-Z' ]+). You need to kill ([0-9]+)/
|
28
|
+
re[:dangerous] = /You have been tasked to hunt down and kill a particularly dangerous (.*) that has established a territory (?:in|on) (?:the )?(.*?)(?: near| between| under|\.)/
|
29
|
+
re[:get_skin_bounty] = /The local furrier/
|
30
|
+
re[:get_herb_bounty] = /local herbalist|local healer|local alchemist/
|
31
|
+
re[:get_gem_bounty] = /The local gem dealer, ([a-zA-Z ]+), has an order to fill and wants our help/
|
32
|
+
re[:creature_problem] = /It appears they have a creature problem they\'d like you to solve/
|
33
|
+
re[:rescue] = /A local divinist has had visions of the child fleeing from (?:a|an) (.*) (?:in|on) (?:the )?(.*?)(?: near| between| under|\.)/
|
34
|
+
|
35
|
+
re[:failed_bounty] = /You have failed in your task/
|
36
|
+
re[:get_bounty] = /You are not currently assigned a task/
|
37
|
+
re.each do |type, exp|
|
38
|
+
if checkbounty =~ exp
|
39
|
+
bounty = {}
|
40
|
+
bounty[:type] = type
|
41
|
+
bounty[:args] = checkbounty.scan(exp).flatten
|
42
|
+
return bounty
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
def Gemstone_Regex.bandit_traps
|
47
|
+
re = {}
|
48
|
+
re[:net] = /Suddenly, a carefully concealed net springs up from the ground, completely entangling you/
|
49
|
+
re[:jaws] = /large pair of carefully concealed metal jaws slam shut on your/
|
50
|
+
re[:wire] = /stumbled right into a length of nearly invisible razor wire/
|
51
|
+
re[:pouch] = /of air as you realize you've just stepped on a carefully concealed inflated pouch/
|
52
|
+
re[:rope] = /wrapping around your ankle and tossing you up into the air/
|
53
|
+
re[:spikes] = /from under you as you fall into a shallow pit filled with tiny spikes/
|
54
|
+
re[:net] = /completely entangling you/
|
55
|
+
re[:net_end] = /The net entangling you rips and falls apart/
|
56
|
+
re[:hidden] = /You hear a voice shout|leaps|flies from the shadows toward you/
|
57
|
+
re[:fail] = /You spy/
|
58
|
+
re[:statue] = /A faint silvery light flickers from the shadows/
|
59
|
+
re
|
60
|
+
end
|
61
|
+
def Gemstone_Regex.escortees
|
62
|
+
/^(?:traveller|magistrate|merchant|scribe|dignitary|official)$/
|
63
|
+
end
|
64
|
+
def Gemstone_Regex.bandits
|
65
|
+
/thief|rogue|bandit|mugger|outlaw|highwayman|marauder|brigand|thug|robber/
|
66
|
+
end
|
67
|
+
def Gemstone_Regex.undead
|
68
|
+
/zombie rolton|lesser ghoul|skeleton|lesser frost shade|lesser shade|phantom|moaning phantom|ghost|ice skeleton|greater ghoul|revenant|mist wraith|dark apparition|lesser mummy|firephantom|spectral fisherman|bone golem|snow spectre|death dirge|werebear|darkwoode|spectre|shadowy spectre|wraith|tomb wight|wolfshade|ghoul master|ghost wolf|ghostly warrior|dark assassin|rotting krolvin pirate|elder ghoul master|nedum vereri|arch wight|wood wight|ancient ghoul master|nonomino|zombie|crazed zombie|rotting woodsman|roa'ter wormling|carceris|spectral monk|tree spirit|monastic lich|skeletal lord|moaning spirit|elder tree spirit|krynch|skeletal ice troll|rotting corpse|rotting farmhand|ghostly mara|ghostly pooka|skeletal giant|rock troll zombie|skeletal soldier|spectral warrior|troll wraith|spectral shade|barghest|spectral woodsman|spectral lord|skeletal warhorse|lesser moor wight|shadow mare|shadow steed|vourkha|greater moor wight|forest bendith|spectral miner|bog wraith|phantasma|frozen corpse|baesrukha|night mare|gaunt spectral servant|bog wight|ice wraith|lesser vruul|rotting chimera|dybbuk|necrotic snake|waern|banshee|flesh golem|seeker|ethereal mage apprentice|nightmare steed|eidolon|decaying Citadel guardsman|rotting Citadel arbalester|putrefied Citadel herald|phantasmal bestial swordsman|wind wraith|soul golem|greater vruul|naisirc|shrickhen|seraceris|lich qyn'arj|n'ecare|lost soul|vaespilon|spectral triton defender|ethereal triton sentry/
|
69
|
+
end
|
70
|
+
def Gemstone_Regex.antimagic
|
71
|
+
/lesser construct|Vvrael warlock|Vvrael witch/
|
72
|
+
end
|
73
|
+
def Gemstone_Regex.living
|
74
|
+
/carrion worm|black rolton|black-winged daggerbeak|fanged rodent|kobold|mountain rolton|giant rat|slimy little grub|young grass snake|fire ant|rolton|spotted gnarp|giant ant|cave gnome|rabid squirrel|big ugly kobold|goblin|pale crab|fanged goblin|brown gak|thyril|spotted gak|sea nymph|Mistydeep siren|dark vysan|greater ice spider|fire salamander|cave nipper|kobold shepherd|relnak|striped relnak|cave gnoll|hobgoblin|Bresnahanini rolton|velnalin|spotted velnalin|striped gak|white vysan|mountain snowcat|troglodyte|black urgh|water moccasin|cobra|urgh|ridge orc|whiptail|spotted leaper|fanged viper|mongrel kobold|night golem|mongrel hobgoblin|bobcat|coyote|water witch|nasty little gremlin|monkey|spotted lynx|cockatrice|leaper|lesser orc|snowy cockatrice|blood eagle|lesser red orc|hobgoblin shaman|shelfae soldier|lesser burrow orc|greater kappa|greater spider|thrak|crystal crab|greater orc|greater burrow orc|albino tomb spider|mottled thrak|brown spinner|crocodile|manticore|rabid guard dog|great boar|raider orc|cave worm|gnoll worker|giant marmot|shelfae chieftain|Neartofar orc|wall guardian|crystal golem|dark orc|great stag|plumed cockatrice|tawny brindlecat|gnoll thief|deranged sentry|Agresh troll scout|forest troll|grey orc|silverback orc|great brown bear|brown boar|giant weasel|black boar|swamp troll|panther|ridgeback boar|luminescent arachnid|gnoll ranger|large ogre|puma|arctic puma|Neartofar troll|black leopard|humpbacked puma|black bear|Agresh troll warrior|mongrel wolfhound|plains orc warrior|cave troll|phosphorescent worm|hill troll|wind witch|fire guardian|mountain ogre|Agresh bear|mongrel troll|red bear|fire rat|banded rattlesnake|mountain troll|spiked cavern urchin|gnoll guard|giant veaba|plains ogre|forest ogre|mountain goat|black panther|dark shambler|plains orc scout|krolvin mercenary|cave lizard|war troll|fire cat|mountain lion|bighorn sheep|shelfae warlord|plains orc shaman|greenwing hornet|plains lion|thunder troll|krolvin warrior|steel golem|gnoll priest|ogre warrior|massive grahnk|major spider|Agresh troll chieftain|striped warcat|Arachne servant|cave bear|plains orc chieftain|cougar|warthog|crested basilisk|dark panther|centaur|fenghai|Arachne acolyte|tree viper|burly reiver|reiver|ice hound|wolverine|veteran reiver|arctic wolverine|giant albino scorpion|krolvin warfarer|gnoll jarl|jungle troll|Arachne priest|Arachne priestess|troll chieftain|cyclops|Grutik savage|lesser stone gargoyle|snow leopard|giant hawk-owl|fire ogre|dobrem|ki-lin|darken|pra'eda|Grutik shaman|ice troll|arctic manticore|scaly burgee|hooded figure|hisskra warrior|giant albino tomb spider|hunter troll|jungle troll chieftain|mammoth arachnid|ash hag|wild hound|caribou|wild dog|giant fog beetle|mezic|three-toed tegu|hisskra shaman|maw spore|moor hound|sand beetle|tundra giant|colossus vulture|hisskra chieftain|moor witch|cold guardian|lava troll|moor eagle|bog troll|shimmering fungus|water wyrd|snow crone|undertaker bat|dust beetle|krolvin slaver|fire giant|arctic titan|Sheruvian initiate|tusked ursian|huge mein golem|magru|mud wasp|grizzly bear|frost giant|wood sprite|krolvin corsair|vesperti|greater bog troll|stone gargoyle|storm giant|myklian|kiramon worker|lesser ice giant|Sheruvian monk|roa'ter|siren lizard|shan wizard|shan warrior|minor glacei|dark vortece|shan cleric|swamp hag|shan ranger|wasp nest|dreadnought raptor|forest trali shaman|firethorn shoot|polar bear|mastodonic leopard|lesser faeroth|kiramon defender|forest trali|cinder wasp|greater ice giant|major glacei|bog spectre|sand devil|warrior shade|horned vor'taz|red-scaled thrak|greater faeroth|snow madrinol|tomb troll|wooly mammoth|ice golem|lesser ice elemental|sabre-tooth tiger|stone sentinel|animated slush|skayl|tomb troll necromancer|stone troll|glacial morph|lava golem|stone giant|massive pyrothag|black forest viper|massive black boar|fire elemental|black forest ogre|stone mastiff|Illoke mystic|massive troll king|ice elemental|Sheruvian harbinger|grifflet|fire sprite|emaciated hierophant|red tsark|Illoke shaman|muscular supplicant|yeti|lesser griffin|hunch-backed dogmatist|krag yeti|fire mage|krag dweller|storm griffin|lesser minotaur|moulis|csetairi|minotaur warrior|farlook|raving lunatic|minotaur magus|dhu goleras|earth elemental|gnarled being|caedera|greater krynch|gremlock|Illoke elder|festering taint|aivren|greater earth elemental|Ithzir scout|Illoke jarl|Ithzir initiate|water elemental|Ithzir janissary|Ithzir herald|triton dissembler|greater construct|Ithzir adept|triton executioner|siren|Ithzir seer|triton combatant|triton radical|war griffin|triton magus|greater water elemental/
|
75
|
+
end
|
76
|
+
def Gemstone_Regex.shop
|
77
|
+
db = {}
|
78
|
+
db[:success] = /^You hand over|You place your/
|
79
|
+
db[:failure] = {}
|
80
|
+
db[:failure][:missing] = /^There is nobody here to buy anything from/
|
81
|
+
db[:failure][:silvers] = /^The merchant frowns and says/
|
82
|
+
db[:failure][:full] = /^There's no more room for anything else/
|
83
|
+
db[:failure][:own] = /^Buy your own merchandise?/
|
84
|
+
db
|
85
|
+
end
|
86
|
+
def Gemstone_Regex.gems
|
87
|
+
re = {}
|
88
|
+
# Expressions to match interaction with gems
|
89
|
+
re[:appraise] = {}
|
90
|
+
re[:appraise][:gemshop] = /inspects it carefully before saying, "I'll give you ([0-9]+) for it if you want to sell/
|
91
|
+
re[:appraise][:player] = /You estimate that the ([a-zA-Z '-]+) is of ([a-zA-Z '-]+) quality and worth approximately ([0-9]+) silvers/
|
92
|
+
re[:appraise][:failure] = /As best you can tell, the ([a-zA-Z '-]+) is of average quality/
|
93
|
+
re[:singularize] = proc{ |str| str.gsub(/ies$/, 'y').gsub(/zes$/,'z').gsub(/s$/,'').gsub(/large |medium |containing |small |tiny |some /, '').strip }
|
94
|
+
re
|
95
|
+
end
|
96
|
+
def Gemstone_Regex.get
|
97
|
+
re = {}
|
98
|
+
re[:failure] = {}
|
99
|
+
# Expressions to match `get` verb results
|
100
|
+
re[:failure][:weight] = /You are unable to handle the additional load/
|
101
|
+
re[:failure][:hands_full] = /^You need a free hand to pick that up/
|
102
|
+
re[:failure][:ne] = /^Get what/
|
103
|
+
re[:failure][:buy] = /^A sales clerk rushes/
|
104
|
+
re[:failure][:pshop] = /^Looking closely/
|
105
|
+
re[:success] = /^You pick up|^You remove|^You rummage|^You draw|^You grab|^You reach|^You already/
|
106
|
+
re
|
107
|
+
end
|
108
|
+
def Gemstone_Regex.put
|
109
|
+
re = {}
|
110
|
+
re[:failure] = {}
|
111
|
+
re[:failure][:full] = /^won't fit in the/
|
112
|
+
re[:failure][:ne] = /^I could not find what you were referring to/
|
113
|
+
re[:success] = /^You put a|^You tuck|^You sheathe|^You slip|^You roll up|^You tuck/
|
114
|
+
re
|
115
|
+
end
|
116
|
+
def Gemstone_Regex.jar(name=nil)
|
117
|
+
if name
|
118
|
+
return name.gsub(/large |medium |containing |small |tiny |some /, '').sub 'rubies', 'ruby'
|
119
|
+
else
|
120
|
+
return false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# used to wrap and extend a GameObj item
|
2
|
+
module Olib
|
3
|
+
class Gameobj_Extender
|
4
|
+
#attr_accessor :type
|
5
|
+
def initialize(item)
|
6
|
+
# @type = item.type
|
7
|
+
self.__extend__(item)
|
8
|
+
end
|
9
|
+
|
10
|
+
# This copies GameObj data to attributes so we can employ it for scripting
|
11
|
+
def __extend__(item)
|
12
|
+
item.instance_variables.each do |var|
|
13
|
+
s = var.to_s.sub('@', '')
|
14
|
+
(class << self; self end).class_eval do; attr_accessor "#{s}"; end
|
15
|
+
instance_variable_set "#{var}", item.send(s)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/Olib/group.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# NEW: Stable
|
2
|
+
module Olib
|
3
|
+
class Group
|
4
|
+
@@characters = {}
|
5
|
+
@@checked = false
|
6
|
+
@@characters[Char.name] = true # YOU CAN NEVER ESCAPE YOURSELF (so your own disk won't fuck up)
|
7
|
+
@@leader = Char.name
|
8
|
+
|
9
|
+
# ran at the initialization of a script
|
10
|
+
def Group.check
|
11
|
+
@@characters = {}
|
12
|
+
|
13
|
+
|
14
|
+
fput "group"
|
15
|
+
while line=get
|
16
|
+
break if line =~ /You are not currently in a group/
|
17
|
+
Group.define($1) if line =~ /([a-zA-Z]+) (is following you|is also a member of your group|is the leader of your group)/
|
18
|
+
@@leader = $1 if line =~ /([a-zA-Z]+) is the leader of your group/
|
19
|
+
break if line =~ /^Your group status is/
|
20
|
+
end
|
21
|
+
@@checked = true
|
22
|
+
@@characters
|
23
|
+
end
|
24
|
+
|
25
|
+
def Group.leader
|
26
|
+
@@leader
|
27
|
+
end
|
28
|
+
|
29
|
+
def Group.leader?
|
30
|
+
Group.check unless @@checked
|
31
|
+
@@leader == Char.name
|
32
|
+
end
|
33
|
+
|
34
|
+
def Group.whisper(msg)
|
35
|
+
fput "whisper group #{msg}" unless Group.members.empty?
|
36
|
+
end
|
37
|
+
|
38
|
+
def Group.add(char)
|
39
|
+
fput "group #{char}"
|
40
|
+
Group.define(char)
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def Group.remove(char)
|
45
|
+
fput "remove #{char}"
|
46
|
+
@@characters.delete(char)
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def Group.nonmembers
|
51
|
+
Group.check unless @@checked
|
52
|
+
others = GameObj.pcs.map! {|char| char.noun } || []
|
53
|
+
# find all the disks/hidden players too
|
54
|
+
disk_owners = GameObj.loot.find_all { |obj| (obj.noun == 'disk') }.map{|disk| /([A-Z](?:[a-z]+))/.match(disk.name)[0].strip } || []
|
55
|
+
[others, disk_owners].flatten.reject(&:nil?).uniq - @@characters.keys
|
56
|
+
end
|
57
|
+
|
58
|
+
def Group.members
|
59
|
+
Group.check unless @@checked
|
60
|
+
@@characters.keys
|
61
|
+
end
|
62
|
+
|
63
|
+
def Group.define(name)
|
64
|
+
GameObj.pcs.detect do |pc| @@characters[name] = pc.dup if pc.noun == name end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
data/lib/Olib/item.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
module Olib
|
2
|
+
# special item wrapper class to wrap item types with custom, common interactions becoming syntactic sugar
|
3
|
+
module Item
|
4
|
+
attr_accessor :type, :buyable, :cost
|
5
|
+
def initialize(item)
|
6
|
+
@type = item.type
|
7
|
+
self._extend(item)
|
8
|
+
end
|
9
|
+
|
10
|
+
# This copies GameObj data to attributes so we can employ it for scripting
|
11
|
+
def _extend(item)
|
12
|
+
item.instance_variables.each do |var|
|
13
|
+
s = var.to_s.sub('@', '')
|
14
|
+
(class << self; self end).class_eval do; attr_accessor "#{s}"; end
|
15
|
+
instance_variable_set "#{var}", item.send(s)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def take
|
20
|
+
result = Olib.do "get ##{@id}", /#{[Olib::Gemstone_Regex.get[:success], Olib::Gemstone_Regex.get[:failure].values].flatten.join('|')}/
|
21
|
+
if result =~ Olib::Gemstone_Regex.get[:failure][:buy]
|
22
|
+
while(line=get)
|
23
|
+
if line =~ /However, it'll be ([0-9]+) silvers for someone like you/
|
24
|
+
@cost = $1.to_i
|
25
|
+
@buyable = true
|
26
|
+
break;
|
27
|
+
elsif line =~ /The selling price is ([0-9]+) silvers/
|
28
|
+
@cost = $1.to_i
|
29
|
+
@buyable = true
|
30
|
+
break;
|
31
|
+
end
|
32
|
+
break;
|
33
|
+
end
|
34
|
+
end
|
35
|
+
result
|
36
|
+
end
|
37
|
+
|
38
|
+
def wear
|
39
|
+
result = Olib.do "wear ##{@id}", /You can only wear|^You put|^You slide|^You attach|^You hang/
|
40
|
+
if result =~ /You can only wear/
|
41
|
+
return false
|
42
|
+
else
|
43
|
+
return true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def remove; fput "remove ##{@id}"; self; end
|
48
|
+
|
49
|
+
def buy ; fput "buy ##{@id}"; self; end
|
50
|
+
|
51
|
+
def give(target); fput "give ##{@id} to #{target}"; self; end
|
52
|
+
|
53
|
+
def _drag(target)
|
54
|
+
Olib.do "_drag ##{@id} ##{target.id}", /#{[Olib::Gemstone_Regex.put[:success], Olib::Gemstone_Regex.put[:failure].values].flatten.join("|")}/
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
module Olib
|
2
|
+
|
3
|
+
class Transport
|
4
|
+
|
5
|
+
attr_accessor :origin, :silvers, :teleporter
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
rebase
|
9
|
+
@teleporter = {}
|
10
|
+
self.__constructor__
|
11
|
+
return self
|
12
|
+
end
|
13
|
+
|
14
|
+
def rebase
|
15
|
+
@origin = {}
|
16
|
+
@origin[:roomid] = Room.current.id
|
17
|
+
@origin[:hidden] = hiding?
|
18
|
+
@origin[:location] = Room.current.location
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
# Thanks Tillmen
|
23
|
+
def cost(to)
|
24
|
+
cost = 0
|
25
|
+
Map.findpath(Room.current.id, @to).each { |id|
|
26
|
+
Room[id].tags.each { |tag|
|
27
|
+
if tag =~ /^silver-cost:#{id-1}:(.*)$/
|
28
|
+
cost_string = $1
|
29
|
+
if cost_string =~ /^[0-9]+$/
|
30
|
+
cost += cost_string.to_i
|
31
|
+
else
|
32
|
+
cost = StringProc.new(cost_string).call.to_i
|
33
|
+
end
|
34
|
+
end
|
35
|
+
}
|
36
|
+
}
|
37
|
+
cost
|
38
|
+
end
|
39
|
+
|
40
|
+
# use the teleporter variable to locate your teleporter and teleport
|
41
|
+
# naive of where you are
|
42
|
+
def fwi_teleport
|
43
|
+
unless @teleporter[:item]
|
44
|
+
setting = 'teleporter'
|
45
|
+
if UserVars.send(setting).nil? or UserVars.send(setting).empty?
|
46
|
+
echo "error: #{setting.to_s} is not set. (;vars set #{setting.to_s}=<teleporter>)"
|
47
|
+
else
|
48
|
+
#echo "locating teleporter..."
|
49
|
+
GameObj.containers.map do |container, contents|
|
50
|
+
contents.each do |item|
|
51
|
+
if item.full_name =~ /#{UserVars.send(setting)}/
|
52
|
+
respond @item
|
53
|
+
@teleporter[:container] = container
|
54
|
+
@teleporter[:item] = item
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
multifput "get ##{@teleporter[:item].id}", "turn ##{@teleporter[:item].id}", "_drag ##{@teleporter[:item].id} ##{@teleporter[:container]}" if @teleporter[:item]
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
def wealth
|
65
|
+
fput "info"
|
66
|
+
while(line=get)
|
67
|
+
next if line =~ /^\s*Name\:|^\s*Gender\:|^\s*Normal \(Bonus\)|^\s*Strength \(STR\)\:|^\s*Constitution \(CON\)\:|^\s*Dexterity \(DEX\)\:|^\s*Agility \(AGI\)\:|^\s*Discipline \(DIS\)\:|^\s*Aura \(AUR\)\:|^\s*Logic \(LOG\)\:|^\s*Intuition \(INT\)\:|^\s*Wisdom \(WIS\)\:|^\s*Influence \(INF\)\:/
|
68
|
+
if line =~ /^\s*Mana\:\s+\-?[0-9]+\s+Silver\:\s+([0-9]+)/
|
69
|
+
@silvers= $1.to_i
|
70
|
+
break
|
71
|
+
end
|
72
|
+
sleep 0.1
|
73
|
+
end
|
74
|
+
@silvers
|
75
|
+
end
|
76
|
+
|
77
|
+
def deplete(silvers)
|
78
|
+
@silvers = @silvers - silvers
|
79
|
+
end
|
80
|
+
|
81
|
+
def smart_wealth
|
82
|
+
return @silvers if @silvers
|
83
|
+
@wealth
|
84
|
+
end
|
85
|
+
|
86
|
+
def unhide
|
87
|
+
fput 'unhide' if Spell[9003].active? or hidden?
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
def withdraw(amount)
|
92
|
+
go2_bank
|
93
|
+
result = Olib.do "withdraw #{amount} silvers", /I'm sorry|then hands you/
|
94
|
+
if result =~ /I'm sorry/
|
95
|
+
go2_origin
|
96
|
+
echo "Unable to withdraw the amount requested for this script to run from your bank account"
|
97
|
+
exit
|
98
|
+
end
|
99
|
+
return self
|
100
|
+
end
|
101
|
+
|
102
|
+
def deposit_all
|
103
|
+
self.go2_bank
|
104
|
+
fput "unhide" if invisible? || hidden?
|
105
|
+
fput "deposit all"
|
106
|
+
return self
|
107
|
+
end
|
108
|
+
|
109
|
+
def deposit(amt)
|
110
|
+
self.go2_bank
|
111
|
+
fput "unhide" if invisible? || hidden?
|
112
|
+
fput "deposit #{amt}"
|
113
|
+
return self
|
114
|
+
end
|
115
|
+
|
116
|
+
# naive share
|
117
|
+
# does not check if you're actually in a group or not
|
118
|
+
def share
|
119
|
+
wealth
|
120
|
+
fput "share #{@silvers}"
|
121
|
+
self
|
122
|
+
end
|
123
|
+
|
124
|
+
def go2(roomid)
|
125
|
+
unhide if hidden
|
126
|
+
unless Room.current.id == roomid
|
127
|
+
start_script 'go2', [roomid, '_disable_confirm_']
|
128
|
+
wait_while { running? "go2" };
|
129
|
+
end
|
130
|
+
return self
|
131
|
+
end
|
132
|
+
|
133
|
+
def hide
|
134
|
+
while not hiding?
|
135
|
+
waitrt?
|
136
|
+
fput 'hide'
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def __constructor__
|
141
|
+
singleton = (class << self; self end)
|
142
|
+
[
|
143
|
+
'bank', 'gemshop', 'pawnshop', 'advguild', 'forge', 'inn', 'npchealer',
|
144
|
+
'chronomage', 'town', 'furrier', 'herbalist', 'locksmith', 'alchemist',
|
145
|
+
'fletcher', 'sunfist', 'movers', 'consignment', 'advguard','advguard2',
|
146
|
+
'clericshop', 'warriorguild'
|
147
|
+
].each do |target|
|
148
|
+
singleton.send :define_method, "go2_#{target}".to_sym do
|
149
|
+
|
150
|
+
unhide if hidden
|
151
|
+
unless Room.current.tags.include?(target)
|
152
|
+
start_script 'go2', [target, '_disable_confirm_']
|
153
|
+
wait_while { running? "go2" };
|
154
|
+
end
|
155
|
+
return self
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# TODO
|
161
|
+
# create a dictionary of house lockers and the logic to enter a locker
|
162
|
+
# insure locker is closed before scripting away from it
|
163
|
+
def go2_locker
|
164
|
+
echo "the go2_locker method currently does not function properly..."
|
165
|
+
self
|
166
|
+
end
|
167
|
+
|
168
|
+
def go2_origin
|
169
|
+
if Room.current.id != @origin[:roomid]
|
170
|
+
start_script 'go2', [@origin[:roomid]]
|
171
|
+
wait_while { running? "go2" };
|
172
|
+
end
|
173
|
+
hide if @origin[:hidden]
|
174
|
+
return self
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
end
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: Olib
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ondreian Shamsiel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-09-12 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Useful Lich extensions for Gemstone IV including hostile creature management,
|
14
|
+
group management, syntactically pleasing movement, locker management, etc
|
15
|
+
email: ondreian.shamsiel@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/Olib.rb
|
21
|
+
- lib/Olib/group.rb
|
22
|
+
- lib/Olib/creature.rb
|
23
|
+
- lib/Olib/creatures.rb
|
24
|
+
- lib/Olib/extender.rb
|
25
|
+
- lib/Olib/container.rb
|
26
|
+
- lib/Olib/dictionary.rb
|
27
|
+
- lib/Olib/item.rb
|
28
|
+
- lib/Olib/transport.rb
|
29
|
+
homepage:
|
30
|
+
licenses:
|
31
|
+
- MIT
|
32
|
+
metadata: {}
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
require_paths:
|
36
|
+
- lib
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
requirements: []
|
48
|
+
rubyforge_project:
|
49
|
+
rubygems_version: 2.0.2
|
50
|
+
signing_key:
|
51
|
+
specification_version: 4
|
52
|
+
summary: Useful Lich extensions for Gemstone IV
|
53
|
+
test_files: []
|