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.
@@ -1,11 +1,8 @@
1
- require 'net/http'
2
1
  require 'json'
3
2
  # a collection for managing all of the creatures in a room
4
3
 
5
4
  class Creatures
6
5
  include Enumerable
7
-
8
- METADATA_URL = "https://cdn.rawgit.com/ondreian/gemstone_data_project/c40a5dfb/creatures.json"
9
6
 
10
7
  ARCHETYPES = %i[
11
8
  undead living weak
@@ -15,7 +12,6 @@ class Creatures
15
12
 
16
13
  STATES = %i[
17
14
  prone sitting kneeling
18
- dead
19
15
  sleeping webbed immobile
20
16
  stunned
21
17
  flying
@@ -23,22 +19,6 @@ class Creatures
23
19
 
24
20
  KINDS = ARCHETYPES + STATES
25
21
 
26
- def self.fetch_metadata()
27
- begin
28
- JSON.parse Net::HTTP.get URI METADATA_URL
29
- rescue
30
- puts $!
31
- puts $!.backtrace[0..1]
32
- []
33
- end
34
- end
35
-
36
- METADATA = fetch_metadata()
37
- BY_NAME = METADATA.reduce(Hash.new) do |by_name, record|
38
- by_name[record["name"]] = record
39
- by_name
40
- end
41
-
42
22
  ALL = -> creature { true }
43
23
 
44
24
  attr_reader :predicate
@@ -48,9 +28,9 @@ class Creatures
48
28
  end
49
29
 
50
30
  def each()
51
- GameObj.npcs.to_a.map do |obj| Creature.new(obj) end
31
+ GameObj.targets.to_a.map do |obj| Creature.new(obj) end
52
32
  .select(&@predicate)
53
- .each do |creature| yield(creature) if GameObj[creature.id] and creature.aggressive? end
33
+ .each do |creature| yield(creature) if GameObj[creature.id] end
54
34
  end
55
35
 
56
36
  def respond_to_missing?(method, include_private = false)
@@ -75,6 +55,12 @@ class Creatures
75
55
  Creatures.new do |creature| creature.name.include?(Bounty.creature) end
76
56
  end
77
57
 
58
+ def dead
59
+ GameObj.npcs.to_a
60
+ .select do |c| c.status.include?("dead") end
61
+ .map do |obj| Creature.new(obj) end
62
+ end
63
+
78
64
  def self.method_missing(method, *args, &block)
79
65
  if respond_to?(method)
80
66
  Creatures.new.send(method, *args, &block)
@@ -0,0 +1,17 @@
1
+ class Creatures
2
+ module Metadata
3
+ @repo = JSON.parse File.read File.join(__dir__, "creatures.json")
4
+
5
+ def self.put(name:, level:, tags: [])
6
+ @repo[name.downcase] = {name: name, level: level, tags: tags}
7
+ end
8
+
9
+ def self.get(name)
10
+ @repo.fetch(name.downcase) do {name: name, level: Char.level, tags: []} end
11
+ end
12
+
13
+ def self.repo()
14
+ @repo.values.map {|creature| OpenStruct.new(creature)}
15
+ end
16
+ end
17
+ end
@@ -1,4 +1,10 @@
1
1
  module Action
2
+ LOCK = Mutex.new
3
+
4
+ def self.lock()
5
+ LOCK.synchronize { yield }
6
+ end
7
+
2
8
  def self.try_or_fail(seconds: 5, command: nil)
3
9
  fput(command)
4
10
  expiry = Time.now + seconds
@@ -23,7 +23,11 @@ class Container < Exist
23
23
  end
24
24
 
25
25
  def contents
26
- GameObj.containers.fetch(id, []).map do |item| Item.new(item, self) end
26
+ GameObj.containers.fetch(id, []).map do |item| Item.of(item, self) end
27
+ end
28
+
29
+ def closed?
30
+ not GameObj.containers[id]
27
31
  end
28
32
 
29
33
  def each(&block)
@@ -1,31 +1,45 @@
1
1
  require "Olib/pattern_matching/pattern_matching"
2
2
 
3
3
  module Containers
4
- @@containers = {}
5
-
6
- def self.find_game_obj!(name)
7
- var = Vars[name.to_s] or fail Exception, "Var[#{name}] is not set\n\t;vars set #{name}=<whatever>"
8
- pattern = %r[#{var}]
9
- GameObj.inv.find(&Where[name: pattern]) or fail Exception, "#{name.capitalize}(#{var}) could not be found in GameObj.inv"
4
+ @@containers ||= {}
5
+
6
+ def self.find_game_obj!(pattern)
7
+ candidates = GameObj.inv.select do |item|
8
+ if pattern.class.is_a?(String)
9
+ item.name.include?(pattern)
10
+ else
11
+ item.name.match(pattern)
12
+ end
13
+ end
14
+ case candidates.size
15
+ when 1
16
+ return Container.new(candidates.first)
17
+ when 0
18
+ fail Exception, <<~ERROR
19
+ Source(GameObj.inv)
20
+
21
+ reason: no matches for Pattern(#{pattern}) found in GameObj.inv
22
+ ERROR
23
+ else
24
+ fail Exception, <<~ERROR
25
+ Source(GameObj.inv)
26
+
27
+ reason: aspecific Container[#{pattern.to_s}] found
28
+ matches: #{candidates.map(&:name)}
29
+ ERROR
30
+ end
10
31
  end
11
32
 
12
33
  def self.define(name)
13
- @@containers[name] = Container.new(
14
- Containers.find_game_obj!(name))
34
+ var = Vars[name.to_s] or fail Exception, "Var[#{name}] is not set\n\t;vars set #{name}=<whatever>"
35
+ pattern = %r[#{var}]
36
+ @@containers[name] = Containers.find_game_obj!(pattern)
15
37
  @@containers[name]
16
38
  end
17
39
 
18
- def self.method_missing(name, *args)
19
- return @@containers[name] if @@containers[name]
20
- return self.define(name)
21
- end
22
-
23
40
  def self.[](name)
24
- begin
25
- self.define(name)
26
- rescue Exception => err
27
- Err(why: err.message, error: err)
28
- end
41
+ return define(name) if name.is_a?(Symbol)
42
+ find_game_obj!(name)
29
43
  end
30
44
 
31
45
  def self.right_hand
@@ -39,4 +53,9 @@ module Containers
39
53
  def self.registry
40
54
  @@containers
41
55
  end
56
+
57
+ def self.method_missing(name, *args)
58
+ return @@containers[name] if @@containers[name]
59
+ return self.define(name)
60
+ end
42
61
  end
@@ -79,17 +79,9 @@ class Exist
79
79
  inspect()
80
80
  end
81
81
 
82
- def inspect(depth= 0)
83
- indent = ""
84
- indent = "\n" + (["\t"] * depth).join if depth > 0
85
- body = [:id, :name, :tags].reduce("") do |acc, prop|
86
- val = send(prop)
87
- acc = "#{acc} #{prop}=#{val.inspect}" unless val.nil? or val.empty?
88
- acc
89
- end.strip
90
-
91
- body = "#{body} contents=[#{contents.map {|i| i.inspect(depth + 1)}.join}]" unless contents.to_a.empty?
92
-
93
- %[#{indent}#{self.class.name}(#{body})]
82
+ def deconstruct_keys(keys)
83
+ keys.each_with_object({}) do |key, acc|
84
+ acc[key] = self.send(key)
85
+ end
94
86
  end
95
87
  end
@@ -2,6 +2,7 @@ require "ostruct"
2
2
  require "Olib/core/exist"
3
3
  require "Olib/core/use"
4
4
  require "Olib/core/transaction"
5
+ require "Olib/core/scroll"
5
6
 
6
7
  class GameObj
7
8
  def to_item
@@ -12,6 +13,12 @@ end
12
13
  # this is the structure for a base Object
13
14
  # wraps an instance of GameObj and adds the ability for tags, queries
14
15
  class Item < Exist
16
+ def self.of(item, container = nil)
17
+ return Scroll.new(item, container) if item.type.include?("scroll")
18
+ return Item.new(item, container)
19
+ end
20
+
21
+
15
22
  def self.fetch(id)
16
23
  new Exist.fetch(id)
17
24
  end
@@ -0,0 +1,37 @@
1
+ class Scroll < Exist
2
+ SPELL = %r[\((?<num>\d+)\)\s(?<name>(\w|\s)+)$]
3
+
4
+ def initialize(obj, container = nil)
5
+ super(obj.id)
6
+ @knowledge = nil
7
+ @container = container
8
+ end
9
+
10
+ def spells()
11
+ @_spells ||= _read()
12
+ end
13
+
14
+ def knowledge?
15
+ spells
16
+ @knowledge.eql?(true)
17
+ end
18
+
19
+ def _read()
20
+ Script.current.want_downstream_xml = true
21
+ dothistimeout("read ##{id}", 5, /It takes you a moment to focus on the/)
22
+ spells = []
23
+ @knowledge = false
24
+ while line = get
25
+ @knowledge = true if line.include?(%[in vibrant ink])
26
+ break if line =~ %r[<prompt]
27
+ _parse_spell(spells, line.strip)
28
+ end
29
+ Script.current.want_downstream_xml = false
30
+ return spells
31
+ end
32
+
33
+ def _parse_spell(spells, line)
34
+ return unless line =~ SPELL
35
+ spells << OpenStruct.new(line.match(SPELL).to_h)
36
+ end
37
+ end
@@ -5,15 +5,19 @@ class Transaction < Exist
5
5
  start: %[to appraise (?:a |an |)<a exist="{{id}}"],
6
6
  close: Regexp.union(
7
7
  %r[I already appraised that],
8
- %r[(I'll give you|How's|I'll offer you|worth at least) (?<value>\d+)],
9
- %r[(?<value>\d+) silvers])
8
+ %r[Sorry, #{Char.name}, I'm not buying anything this valuable today\.],
9
+ %r[(I'll give you|How's|I'll offer you|worth at least) (?<value>[,\d]+)],
10
+ %r[(?<value>[,\d]+) silvers])
10
11
  )
11
12
 
12
13
  Sell = Rill.new(
13
14
  start: %[You offer|You ask (?<merchant>.*?) if (he|she) would like to buy (?:a |an |)<a exist="{{id}}"],
14
15
  close: Regexp.union(
15
- %r[(hands you|for) (?<value>\d+)],
16
+ %r[(hands you|for) (?<value>[,\d]+)],
16
17
  %r[No #{Char.name}, I won't buy that],
18
+ %r[I'm sorry, #{Char.name}, but I have no use for that\.],
19
+ %r[He hands it back to you],
20
+ %r[Nope #{Char.name}, I ain't buying that\.],
17
21
  %r[basically worthless here, #{Char.name}])
18
22
  )
19
23
 
@@ -33,9 +37,14 @@ class Transaction < Exist
33
37
  def appraise()
34
38
  return self unless @value.nil?
35
39
  take
36
- (match, _lines) = Appraise.capture(self.to_h,
40
+ (_, match, lines) = Appraise.capture(self.to_h,
37
41
  "appraise \#{{id}}")
38
- @value = match[:value].to_i
42
+ if lines.any? {|line| line.include?(%[Sorry, #{Char.name}, I'm not buying anything this valuable today.])}
43
+ @value = Float::INFINITY
44
+ else
45
+ Log.out(match[:value])
46
+ @value = match[:value].delete(",").to_i
47
+ end
39
48
  self
40
49
  end
41
50
 
@@ -46,8 +55,15 @@ class Transaction < Exist
46
55
  transaction: self,
47
56
  reason: "Value[#{@value}] is over Threshold[#{@threshold}]"]
48
57
  end
49
- (match, _lines) = Sell.capture(self.to_h,
58
+
59
+ (_, match, _lines) = Sell.capture(self.to_h,
50
60
  "sell \#{{id}}")
61
+
62
+ if match[:value].is_a?(String) && match[:value] =~ /[\d,]+/
63
+ match[:value] = match[:value].delete(",").to_i
64
+ end
65
+
66
+ match[:value] = 0 unless match[:value].is_a?(Integer)
51
67
  Ok[**match]
52
68
  end
53
69
  end
@@ -8,7 +8,7 @@ class MatchData
8
8
 
9
9
  def to_h
10
10
  Hash[self.names.map(&:to_sym).zip(self.captures.map(&:strip).map do |capture|
11
- if capture.is_i? then capture.to_i else capture end
11
+ if capture.is_i? then capture.delete(",").to_i else capture end
12
12
  end)]
13
13
  end
14
14
  end
@@ -1,6 +1,6 @@
1
1
  class String
2
2
  def is_i?
3
- !!(self =~ /\A[-+]?[0-9]+\z/)
3
+ !!(self.delete(",") =~ /\A[-+]?[0-9]+\z/)
4
4
  end
5
5
 
6
6
  def methodize
@@ -29,24 +29,32 @@ class Go2
29
29
  Go2.define_singleton_method(tag.methodize) do Go2.room(tag) end
30
30
  end
31
31
 
32
- def Go2.room(roomid)
33
- unless Room.current.id == roomid || Room.current.tags.include?(roomid)
32
+ def Go2.run(target)
33
+ Script.run "go2", ("%s _disable_confirm_" % target)
34
+ end
35
+
36
+ def Go2.room(target)
37
+ starting_room = Room.current.id
38
+ unless Room.current.id == target || Room.current.tags.include?(target)
34
39
  Char.unhide if hidden
35
- start_script "go2", [roomid, "_disable_confirm_"]
36
- wait_while { running? "go2" };
40
+ Go2.run(target)
41
+ if block_given?
42
+ yield
43
+ starting_room
44
+ end
37
45
  end
38
46
  Go2
39
47
  end
40
48
 
41
49
  def Go2.origin
42
- Go2.room @@origin[:roomid]
50
+ Go2.room @@origin[:target]
43
51
  Char.hide if @@origin[:hidden]
44
52
  Go2
45
53
  end
46
54
 
47
55
  def Go2.rebase
48
56
  @@origin = {}
49
- @@origin[:roomid] = Room.current.id
57
+ @@origin[:target] = Room.current.id
50
58
  @@origin[:hidden] = hiding?
51
59
  @@origin[:location] = Room.current.location
52
60
  Go2
@@ -0,0 +1,42 @@
1
+ module Preset
2
+ def self.as(kind, body)
3
+ %[<preset id="#{kind}">#{body}</preset>\r\n]
4
+ end
5
+ end
6
+ ##
7
+ ## contextual logging
8
+ ##
9
+ module Log
10
+ require "cgi"
11
+ def self.out(msg, label: :debug)
12
+ return _write _view(msg, label) unless msg.is_a?(Exception)
13
+ ## pretty-print exception
14
+ _write _view(msg.message, label)
15
+ msg.backtrace.to_a.slice(0..5).each do |frame| _write _view(frame, label) end
16
+ end
17
+
18
+ def self._write(line)
19
+ if Script.current.vars.include?("--headless") or not defined?(:_respond)
20
+ $stdout.write(line + "\n")
21
+ elsif line.include?("<") and line.include?(">")
22
+ respond(line)
23
+ else
24
+ _respond Preset.as(:debug, CGI.escapeHTML(line))
25
+ end
26
+ end
27
+
28
+ def self._view(msg, label)
29
+ label = [Script.current.name, label].flatten.compact.join(".")
30
+ safe = msg.inspect
31
+ #safe = safe.gsub("<", "&lt;").gsub(">", "&gt;") if safe.include?("<") and safe.include?(">")
32
+ "[#{label}] #{safe}"
33
+ end
34
+
35
+ def self.pp(msg, label = :debug)
36
+ respond _view(msg, label)
37
+ end
38
+
39
+ def self.dump(*args)
40
+ pp(*args)
41
+ end
42
+ end
@@ -10,8 +10,9 @@ class Loot
10
10
  end
11
11
 
12
12
  def each()
13
- GameObj.loot.to_a.map do |obj| Item.new(obj) end
13
+ GameObj.loot.to_a
14
14
  .reject(&Where[noun: "disk"])
15
+ .map do |obj| Item.new(obj) end
15
16
  .select(&@predicate)
16
17
  .each do |item| yield(item) end
17
18
  end
@@ -0,0 +1,42 @@
1
+ ##
2
+ ## minimal options parser
3
+ ##
4
+ module Opts
5
+ FLAG_PREFIX = "--"
6
+
7
+ def self.parse_command(h, c)
8
+ h[c.to_sym] = true
9
+ end
10
+
11
+ def self.parse_flag(h, f)
12
+ (name, val) = f[2..-1].split("=")
13
+ if val.nil?
14
+ h[name.to_sym] = true
15
+ else
16
+ val = val.split(",")
17
+
18
+ h[name.to_sym] = val.size == 1 ? val.first : val
19
+ end
20
+ end
21
+
22
+ def self.parse(args = Script.current.vars[1..-1])
23
+ OpenStruct.new(**args.to_a.reduce(Hash.new) do |opts, v|
24
+ if v.start_with?(FLAG_PREFIX)
25
+ Opts.parse_flag(opts, v)
26
+ else
27
+ Opts.parse_command(opts, v)
28
+ end
29
+ opts
30
+ end)
31
+ end
32
+
33
+ def self.as_list(key)
34
+ val = to_h.fetch(key.to_sym, [])
35
+ val = [val] if val.is_a?(String)
36
+ return val
37
+ end
38
+
39
+ def self.method_missing(method, *args)
40
+ parse.send(method, *args)
41
+ end
42
+ end