gamefic-standard 3.2.3 → 3.3.0

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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/Rakefile +5 -2
  4. data/gamefic-standard.gemspec +13 -11
  5. data/lib/gamefic-standard/actions/attack.rb +23 -19
  6. data/lib/gamefic-standard/actions/close.rb +16 -10
  7. data/lib/gamefic-standard/actions/drop.rb +31 -16
  8. data/lib/gamefic-standard/actions/enter.rb +32 -26
  9. data/lib/gamefic-standard/actions/give.rb +41 -0
  10. data/lib/gamefic-standard/actions/go.rb +47 -43
  11. data/lib/gamefic-standard/actions/insert.rb +42 -28
  12. data/lib/gamefic-standard/actions/inventory.rb +13 -8
  13. data/lib/gamefic-standard/actions/leave.rb +44 -37
  14. data/lib/gamefic-standard/actions/lock.rb +22 -16
  15. data/lib/gamefic-standard/actions/look.rb +123 -104
  16. data/lib/gamefic-standard/actions/move.rb +18 -14
  17. data/lib/gamefic-standard/actions/nil.rb +62 -52
  18. data/lib/gamefic-standard/actions/open.rb +34 -24
  19. data/lib/gamefic-standard/actions/place.rb +35 -20
  20. data/lib/gamefic-standard/actions/pronouns.rb +26 -18
  21. data/lib/gamefic-standard/actions/quit.rb +17 -7
  22. data/lib/gamefic-standard/actions/repeat.rb +17 -9
  23. data/lib/gamefic-standard/actions/save-restore-undo.rb +19 -11
  24. data/lib/gamefic-standard/actions/search.rb +31 -21
  25. data/lib/gamefic-standard/actions/take.rb +47 -34
  26. data/lib/gamefic-standard/actions/talk.rb +45 -31
  27. data/lib/gamefic-standard/actions/unlock.rb +31 -21
  28. data/lib/gamefic-standard/actions/wait.rb +15 -5
  29. data/lib/gamefic-standard/actions.rb +33 -0
  30. data/lib/gamefic-standard/articles.rb +45 -40
  31. data/lib/gamefic-standard/enterable.rb +13 -0
  32. data/lib/gamefic-standard/entities/character.rb +3 -0
  33. data/lib/gamefic-standard/entities/container.rb +2 -2
  34. data/lib/gamefic-standard/entities/door.rb +13 -13
  35. data/lib/gamefic-standard/entities/portal.rb +1 -1
  36. data/lib/gamefic-standard/entities/receptacle.rb +1 -1
  37. data/lib/gamefic-standard/entities/room.rb +20 -5
  38. data/lib/gamefic-standard/entities/supporter.rb +1 -1
  39. data/lib/gamefic-standard/entities/thing.rb +1 -5
  40. data/lib/gamefic-standard/introduction.rb +14 -4
  41. data/lib/gamefic-standard/lockable.rb +36 -0
  42. data/lib/gamefic-standard/openable.rb +33 -0
  43. data/lib/gamefic-standard/pathfinder.rb +75 -55
  44. data/lib/gamefic-standard/standardized.rb +65 -0
  45. data/lib/gamefic-standard/version.rb +1 -1
  46. data/lib/gamefic-standard.rb +12 -3
  47. metadata +36 -13
  48. data/lib/gamefic-standard/give.rb +0 -21
  49. data/lib/gamefic-standard/grammar/attributes.rb +0 -37
  50. data/lib/gamefic-standard/grammar/pronoun.rb +0 -101
  51. data/lib/gamefic-standard/grammar.rb +0 -2
  52. data/lib/gamefic-standard/modules/enterable.rb +0 -9
  53. data/lib/gamefic-standard/modules/lockable.rb +0 -34
  54. data/lib/gamefic-standard/modules/openable.rb +0 -19
  55. data/lib/gamefic-standard/modules/standardized.rb +0 -57
  56. data/lib/gamefic-standard/modules.rb +0 -6
  57. data/spec-opal/spec_helper.rb +0 -32
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gamefic
4
+ module Standard
5
+ module Enterable
6
+ attr_writer :enterable
7
+
8
+ def enterable?
9
+ @enterable ||= false
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,2 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Character = Gamefic::Actor
2
4
  Character.set_default gender: :other
5
+ Character.include Gamefic::What::Askable
@@ -3,6 +3,6 @@
3
3
  # An openable and lockable receptacle.
4
4
  #
5
5
  class Container < Receptacle
6
- include Openable
7
- include Lockable
6
+ include Gamefic::Standard::Openable
7
+ include Gamefic::Standard::Lockable
8
8
  end
@@ -1,42 +1,42 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # An openable portal.
3
+ # An openable and lockable portal.
4
4
  #
5
5
  class Door < Portal
6
- include Openable
7
- include Lockable
6
+ include Gamefic::Standard::Openable
7
+ include Gamefic::Standard::Lockable
8
8
 
9
9
  def post_initialize
10
10
  update_reverse_open
11
+ update_reverse_lock
11
12
  end
12
13
 
13
- def open= bool
14
+ def open=(bool)
14
15
  super
16
+ reverse&.lock_key = lock_key
15
17
  update_reverse_open
18
+ update_reverse_lock
16
19
  end
17
20
 
18
- def locked= bool
21
+ def locked=(bool)
19
22
  super
20
23
  update_reverse_lock
21
24
  end
22
25
 
23
- def two_way_lock_key= key
24
- lock_key = key
25
- return if reverse.nil?
26
- reverse.lock_key = key
26
+ def two_way_lock_key=(key)
27
+ self.lock_key = key
28
+ reverse&.lock_key = key
27
29
  end
28
30
 
29
31
  private
30
32
 
31
33
  def update_reverse_open
32
34
  rev = find_reverse
33
- return if rev.nil? || rev.open? == open?
34
- rev.open = open?
35
+ rev&.open = open? unless rev&.open? == open?
35
36
  end
36
37
 
37
38
  def update_reverse_lock
38
39
  rev = find_reverse
39
- return if rev.nil? || rev.locked? == locked?
40
- rev.locked = locked?
40
+ rev&.locked = locked? unless rev&.locked? == locked?
41
41
  end
42
42
  end
@@ -28,7 +28,7 @@ class Portal < Thing
28
28
  end
29
29
  alias find_reverse reverse
30
30
 
31
- def direction= dir
31
+ def direction=(dir)
32
32
  @direction = Direction.find(dir)
33
33
  end
34
34
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Receptacle < Thing
4
- include Enterable
4
+ include Gamefic::Standard::Enterable
5
5
  end
@@ -23,14 +23,12 @@ class Room < Thing
23
23
  # @param type [Class<Portal>]
24
24
  # @param two_way [Boolean]
25
25
  # @return [Portal, Array<Portal>]
26
- def connect destination, direction: nil, type: Portal, two_way: true
26
+ def connect(destination, direction: nil, type: Portal, two_way: true, **opts)
27
27
  direction = Direction.find(direction)
28
- here = type.new(parent: self,
29
- destination: destination,
30
- direction: Direction.find(direction))
28
+ here = type.new parent: self, destination: destination, direction: Direction.find(direction), **opts
31
29
  return here unless two_way
32
30
 
33
- there = type.new(parent: destination, destination: self, direction: direction&.reverse)
31
+ there = type.new parent: destination, destination: self, direction: direction&.reverse, **opts
34
32
  [here, there]
35
33
  end
36
34
 
@@ -43,4 +41,21 @@ class Room < Thing
43
41
  set_default explicit_exits: bool
44
42
  end
45
43
  end
44
+
45
+ protected
46
+
47
+ %w[north south west east northeast southeast southwest northwest up down].each do |direction|
48
+ define_method "#{direction}=" do |destination|
49
+ connect destination, direction: direction
50
+ end
51
+ end
52
+
53
+ def connect=(destinations)
54
+ all = [destinations].flatten
55
+ until all.empty?
56
+ destination = all.shift
57
+ direction = (all.first.is_a?(String) ? all.shift : nil)
58
+ connect destination, direction: direction
59
+ end
60
+ end
46
61
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Supporter < Thing
4
- include Enterable
4
+ include Gamefic::Standard::Enterable
5
5
  end
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Gamefic::Entity
4
- include Grammar::Attributes
5
- include Standardized
6
- end
7
-
8
3
  Thing = Gamefic::Entity
9
4
  Thing.set_default itemized: true, portable: false
5
+ Thing.include Gamefic::Standard::Standardized
@@ -1,8 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- Gamefic::Standard.script do
4
- introduction do |actor|
5
- actor.name = 'myself'
6
- actor.synonyms = 'self me yourself you'
3
+ module Gamefic
4
+ module Standard
5
+ # Add a simple introduction that sets an appropriate name and synonyms for
6
+ # the player character.
7
+ #
8
+ module Introduction
9
+ extend Gamefic::Scriptable
10
+
11
+ introduction do |actor|
12
+ actor.name = 'you'
13
+ actor.synonyms = 'self me yourself myself'
14
+ actor.proper_named = true
15
+ end
16
+ end
7
17
  end
8
18
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gamefic
4
+ module Standard
5
+ # A module for entities that are both openable and lockable.
6
+ #
7
+ module Lockable
8
+ include Openable
9
+
10
+ attr_accessor :lock_key
11
+
12
+ def locked=(bool)
13
+ @locked = bool
14
+ @open = false if @locked
15
+ end
16
+
17
+ def open=(bool)
18
+ @open = bool
19
+ @locked = false if @open
20
+ end
21
+
22
+ def locked?
23
+ @locked ||= false
24
+ end
25
+
26
+ def unlocked?
27
+ !locked?
28
+ end
29
+
30
+ def lock_key?
31
+ !@lock_key.nil?
32
+ end
33
+ alias has_lock_key? lock_key?
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gamefic
4
+ module Standard
5
+ # A module for entities that are openable.
6
+ #
7
+ module Openable
8
+ def open
9
+ self.open = true
10
+ end
11
+
12
+ def close
13
+ self.open = false
14
+ end
15
+
16
+ def open=(bool)
17
+ @open = bool
18
+ end
19
+
20
+ def open?
21
+ @open ||= false
22
+ end
23
+
24
+ def closed?
25
+ !open?
26
+ end
27
+
28
+ def accessible?
29
+ open?
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,62 +1,82 @@
1
- # Pathfinders provide the shortest route between two locations. The
2
- # destination needs to be accessible from the origin through portals. Note
3
- # that Pathfinders do not take into account portals that characters should
4
- # not be able to traverse, such as locked doors.
5
- class Pathfinder
6
- # @return [Room]
7
- attr_reader :origin
8
-
9
- # @return [Room]
10
- attr_reader :destination
11
-
12
- def initialize origin, destination
13
- @origin = origin
14
- @destination = destination
15
- @path = nil
16
- @paths = [[@origin]]
17
- @visited = []
18
- if @origin == @destination
19
- @path = []
20
- else
21
- embark while @path.nil? && @paths.length > 0
22
- end
23
- end
1
+ # frozen_string_literal: true
24
2
 
25
- # @return [Array<Room>]
26
- def path
27
- # @path is nil if the path is invalid, but #path should return an empty
28
- # array instead.
29
- @path || []
30
- end
3
+ module Gamefic
4
+ module Standard
5
+ # Pathfinders provide the shortest route between two rooms. The destination
6
+ # needs to be accessible from the origin through portals. Note that
7
+ # Pathfinder does not take into account portals that characters should not
8
+ # be able to traverse, such as locked doors.
9
+ #
10
+ class Pathfinder
11
+ # @return [Room]
12
+ attr_reader :origin
31
13
 
32
- # @return [Boolean]
33
- def valid?
34
- path.length > 0 || origin == destination
35
- end
14
+ # @return [Room]
15
+ attr_reader :destination
16
+
17
+ # @param origin [Room]
18
+ # @param destination [Room]
19
+ def initialize(origin, destination)
20
+ @origin = origin
21
+ @destination = destination
22
+ @visited = []
23
+ if origin == destination
24
+ @path = []
25
+ else
26
+ embark [[origin]]
27
+ end
28
+ end
29
+
30
+ # An array of rooms starting with the first room after the origin and
31
+ # ending with the destination.
32
+ #
33
+ # The path will be empty if a path could not be found or the origin and
34
+ # destination are the same room.
35
+ #
36
+ # @return [Array<Room>]
37
+ def path
38
+ @path || []
39
+ end
40
+
41
+ # True if there is a valid path from the origin to the destination (or
42
+ # the origin and destination are the same room).
43
+ #
44
+ def valid?
45
+ !!@path
46
+ end
47
+
48
+ private
49
+
50
+ def embark(paths)
51
+ return if paths.empty?
52
+
53
+ new_paths = paths.each_with_object([]) do |path, acc|
54
+ updates = traverse(path)
55
+ acc.concat updates
56
+ end
57
+
58
+ if new_paths.last&.last == destination
59
+ @path = new_paths.last
60
+ else
61
+ embark new_paths
62
+ end
63
+ end
64
+
65
+ def traverse(path)
66
+ portals_with_destination(path).map do |portal|
67
+ next if @visited.include?(portal.destination)
68
+
69
+ return [path[1..] + [portal.destination]] if portal.destination == destination
36
70
 
37
- private
38
-
39
- def embark
40
- new_paths = []
41
- @paths.each { |path|
42
- last = path.last
43
- portals = last.children.that_are(Portal)
44
- portals.each { |portal|
45
- new_path = path.clone
46
- if !@visited.include?(portal.destination)
47
- new_path.push portal.destination
48
71
  @visited.push portal.destination
49
- if portal.destination == @destination
50
- @path = new_path
51
- @path.shift
52
- break
53
- end
54
- new_paths.push new_path
72
+ path + [portal.destination]
55
73
  end
56
- }
57
- path.push nil
58
- }
59
- @paths += new_paths
60
- @paths.delete_if{|path| path.last.nil?}
74
+ .compact
75
+ end
76
+
77
+ def portals_with_destination(path)
78
+ path.last.children.that_are(Portal).select(&:destination)
79
+ end
80
+ end
61
81
  end
62
82
  end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gamefic
4
+ module Standard
5
+ # Common features of standard entities.
6
+ #
7
+ module Standardized
8
+ # @return [Boolean]
9
+ attr_writer :itemized
10
+
11
+ # @return [Boolean]
12
+ attr_writer :portable
13
+
14
+ # An optional description to use when itemizing entities in room
15
+ # descriptions. The locale_description will be used instead of adding
16
+ # the entity's name to a list.
17
+ #
18
+ # @return [String, nil]
19
+ attr_accessor :locale_description
20
+
21
+ # Itemized entities are automatically listed in room descriptions.
22
+ #
23
+ # @return [Boolean]
24
+ def itemized?
25
+ @itemized
26
+ end
27
+
28
+ # Portable entities can be taken with TAKE actions.
29
+ #
30
+ # @return [Boolean]
31
+ def portable?
32
+ @portable
33
+ end
34
+
35
+ # @return [Boolean]
36
+ def attached?
37
+ @attached ||= false
38
+ end
39
+
40
+ # @param bool [Boolean]
41
+ def attached=(bool)
42
+ @attached = if parent.nil?
43
+ # @todo Log attachment failure
44
+ false
45
+ else
46
+ bool
47
+ end
48
+ end
49
+
50
+ def parent=(new_parent)
51
+ self.attached = false unless new_parent == parent
52
+ super
53
+ end
54
+
55
+ # The entity's parent room (i.e., the closest ascendant that is a Room).
56
+ #
57
+ # @return [Room]
58
+ def room
59
+ ascendant = parent
60
+ ascendant = ascendant.parent until ascendant.is_a?(Room) || ascendant.nil?
61
+ ascendant
62
+ end
63
+ end
64
+ end
65
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Gamefic
4
4
  module Standard
5
- VERSION = '3.2.3'
5
+ VERSION = '3.3.0'
6
6
  end
7
7
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'gamefic'
4
+ require 'gamefic-grammar'
5
+ require 'gamefic-what'
4
6
 
5
7
  module Gamefic
6
8
  # The Gamefic standard library provides a base collection of entities and
@@ -10,15 +12,22 @@ module Gamefic
10
12
  extend Gamefic::Scriptable
11
13
 
12
14
  require 'gamefic-standard/version'
13
- require 'gamefic-standard/grammar'
14
15
  require 'gamefic-standard/articles'
15
16
  require 'gamefic-standard/queries'
16
- require 'gamefic-standard/modules'
17
+ require 'gamefic-standard/standardized'
18
+ require 'gamefic-standard/enterable'
19
+ require 'gamefic-standard/openable'
20
+ require 'gamefic-standard/lockable'
17
21
  require 'gamefic-standard/direction'
18
22
  require 'gamefic-standard/entities'
19
23
  require 'gamefic-standard/actions'
20
24
  require 'gamefic-standard/introduction'
21
- require 'gamefic-standard/give'
25
+ require 'gamefic-standard/pathfinder'
26
+
27
+ include Articles
28
+ include Grammar::Pronoun
29
+ include Actions
30
+ include Introduction
22
31
 
23
32
  def connect(origin, destination, direction = nil, type: Portal, two_way: true)
24
33
  origin.connect destination, direction: direction, type: type, two_way: two_way
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gamefic-standard
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.3
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Snyder
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-09-01 00:00:00.000000000 Z
11
+ date: 2024-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: gamefic
@@ -24,6 +24,34 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '3.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: gamefic-grammar
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: gamefic-what
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: opal
29
57
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +150,7 @@ files:
122
150
  - lib/gamefic-standard/actions/close.rb
123
151
  - lib/gamefic-standard/actions/drop.rb
124
152
  - lib/gamefic-standard/actions/enter.rb
153
+ - lib/gamefic-standard/actions/give.rb
125
154
  - lib/gamefic-standard/actions/go.rb
126
155
  - lib/gamefic-standard/actions/insert.rb
127
156
  - lib/gamefic-standard/actions/inventory.rb
@@ -143,6 +172,7 @@ files:
143
172
  - lib/gamefic-standard/actions/wait.rb
144
173
  - lib/gamefic-standard/articles.rb
145
174
  - lib/gamefic-standard/direction.rb
175
+ - lib/gamefic-standard/enterable.rb
146
176
  - lib/gamefic-standard/entities.rb
147
177
  - lib/gamefic-standard/entities/character.rb
148
178
  - lib/gamefic-standard/entities/container.rb
@@ -156,21 +186,14 @@ files:
156
186
  - lib/gamefic-standard/entities/scenery.rb
157
187
  - lib/gamefic-standard/entities/supporter.rb
158
188
  - lib/gamefic-standard/entities/thing.rb
159
- - lib/gamefic-standard/give.rb
160
- - lib/gamefic-standard/grammar.rb
161
- - lib/gamefic-standard/grammar/attributes.rb
162
- - lib/gamefic-standard/grammar/pronoun.rb
163
189
  - lib/gamefic-standard/introduction.rb
164
- - lib/gamefic-standard/modules.rb
165
- - lib/gamefic-standard/modules/enterable.rb
166
- - lib/gamefic-standard/modules/lockable.rb
167
- - lib/gamefic-standard/modules/openable.rb
168
- - lib/gamefic-standard/modules/standardized.rb
190
+ - lib/gamefic-standard/lockable.rb
191
+ - lib/gamefic-standard/openable.rb
169
192
  - lib/gamefic-standard/pathfinder.rb
170
193
  - lib/gamefic-standard/queries.rb
194
+ - lib/gamefic-standard/standardized.rb
171
195
  - lib/gamefic-standard/version.rb
172
- - spec-opal/spec_helper.rb
173
- homepage: http://gamefic.com
196
+ homepage: https://gamefic.com
174
197
  licenses:
175
198
  - MIT
176
199
  metadata: {}
@@ -1,21 +0,0 @@
1
- module Gamefic::Standard::Give
2
- extend Gamefic::Scriptable
3
-
4
- script do
5
- respond :give, available, children do |actor, _character, _gift|
6
- actor.tell 'Nothing happens.'
7
- end
8
-
9
- respond :give, available(Character), available do |actor, character, gift|
10
- actor.tell "#{The character} doesn't want #{the gift}."
11
- end
12
-
13
- respond :give, available(Character), available do |actor, _character, gift|
14
- actor.execute :take, gift if gift.parent != actor
15
-
16
- actor.proceed if gift.parent == actor
17
- end
18
-
19
- interpret 'give :gift to :character', 'give :character :gift'
20
- end
21
- end
@@ -1,37 +0,0 @@
1
- module Grammar
2
- # A collection of attributes that enable grammar features for entities, such
3
- # as selecting their correct pronouns.
4
- #
5
- module Attributes
6
- # @see #gender
7
- attr_writer :gender
8
-
9
- # @see #plural?
10
- attr_writer :plural
11
-
12
- # The gender of the object. Supported values are :male, :female, :neutral,
13
- # and :other. Use :neutral for objects that don't have a gender (i.e.,
14
- # "it"). Use :other for people or characters that have an unspecified or
15
- # non-binary gender (i.e., "they").
16
- #
17
- # @return [Symbol]
18
- def gender
19
- @gender ||= :neutral
20
- end
21
-
22
- # True if the object should be referred to in the plural, e.g., "they"
23
- # instead of "it."
24
- # @return [Boolean]
25
- #
26
- def plural?
27
- @plural ||= false
28
- end
29
-
30
- # For now, the object's person is always assumed to be third
31
- # (he/she/it/they). A future version of this library might support first
32
- # (I/me) and second (you).
33
- def person
34
- 3
35
- end
36
- end
37
- end