Olib 2.0.0.pre.rc.1 → 2.0.0.pre.rc.6

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,13 @@
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
+ end
13
+ 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
@@ -2,7 +2,8 @@
2
2
  require "Olib/core/action"
3
3
 
4
4
  class Exist
5
- GETTER = %r[\w$]
5
+ GETTER = %r[\w$]
6
+ PATTERN = %r(<a exist=(?:'|")(?<id>.*?)(?:'|") noun=(?:'|")(?<noun>.*?)(?:'|")>(?<name>.*?)</a>)
6
7
 
7
8
  def self.fetch(id)
8
9
  [ GameObj.inv, GameObj.containers.values,
@@ -12,8 +13,12 @@ class Exist
12
13
  .find do |item| item.id.to_s.eql?(id.to_s) end
13
14
  end
14
15
 
16
+ def self.scan(str)
17
+ str.scan(PATTERN).map do |matches| Item.new(GameObj.new(*matches)) end
18
+ end
19
+
15
20
  def self.normalize_type_data(type)
16
- (type or "").gsub(",", " ").split(" ")
21
+ (type or "").gsub(",", " ").split(" ").compact
17
22
  end
18
23
 
19
24
  attr_reader :id, :gameobj
@@ -35,6 +40,8 @@ class Exist
35
40
  end
36
41
 
37
42
  def method_missing(method, *args)
43
+ return nil if fetch.nil?
44
+
38
45
  if respond_to_missing?(method)
39
46
  fetch.send(method, *args)
40
47
  else
@@ -51,7 +58,7 @@ class Exist
51
58
  end
52
59
 
53
60
  def tags
54
- Exist.normalize_type_data(type).map(&:to_sym)
61
+ Exist.normalize_type_data("#{type},#{sellable}").map(&:to_sym)
55
62
  end
56
63
 
57
64
  def effects
@@ -72,17 +79,9 @@ class Exist
72
79
  inspect()
73
80
  end
74
81
 
75
- def inspect(depth= 0)
76
- indent = ""
77
- indent = "\n" + (["\t"] * depth).join if depth > 0
78
- body = [:id, :name, :tags].reduce("") do |acc, prop|
79
- val = send(prop)
80
- acc = "#{acc} #{prop}=#{val.inspect}" unless val.nil? or val.empty?
81
- acc
82
- end.strip
83
-
84
- body = "#{body} contents=[#{contents.map {|i| i.inspect(depth + 1)}.join}]" unless contents.to_a.empty?
85
-
86
- %[#{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
87
86
  end
88
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
@@ -53,6 +60,10 @@ class Item < Exist
53
60
  Transaction.new(take, **args)
54
61
  end
55
62
 
63
+ def appraise(**args)
64
+ transaction(**args).appraise()
65
+ end
66
+
56
67
  def sell(**args)
57
68
  transaction(**args).sell()
58
69
  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,6 +5,7 @@ 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[Sorry, #{Char.name}, I'm not buying anything this valuable today\.],
8
9
  %r[(I'll give you|How's|I'll offer you|worth at least) (?<value>\d+)],
9
10
  %r[(?<value>\d+) silvers])
10
11
  )
@@ -14,6 +15,9 @@ class Transaction < Exist
14
15
  close: Regexp.union(
15
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,13 @@ 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, Pixelia, I'm not buying anything this valuable today.])}
43
+ @value = Float::INFINITY
44
+ else
45
+ @value = match[:value].to_i
46
+ end
39
47
  self
40
48
  end
41
49
 
@@ -46,8 +54,12 @@ class Transaction < Exist
46
54
  transaction: self,
47
55
  reason: "Value[#{@value}] is over Threshold[#{@threshold}]"]
48
56
  end
49
- (match, _lines) = Sell.capture(self.to_h,
57
+
58
+ (_, match, _lines) = Sell.capture(self.to_h,
50
59
  "sell \#{{id}}")
60
+
61
+
62
+ match[:value] = 0 unless match[:value].is_a?(Integer)
51
63
  Ok[**match]
52
64
  end
53
65
  end
@@ -3,7 +3,7 @@ require "Olib/ext/string"
3
3
 
4
4
  class MatchData
5
5
  def to_struct
6
- OpenStruct.new to_hash
6
+ OpenStruct.new to_h
7
7
  end
8
8
 
9
9
  def to_h
@@ -32,8 +32,7 @@ class Go2
32
32
  def Go2.room(roomid)
33
33
  unless Room.current.id == roomid || Room.current.tags.include?(roomid)
34
34
  Char.unhide if hidden
35
- start_script "go2", [roomid, "_disable_confirm_"]
36
- wait_while { running? "go2" };
35
+ Script.run("go2", "#{roomid} _disable_confirm_")
37
36
  end
38
37
  Go2
39
38
  end
@@ -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