gamefic-standard 3.2.4 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -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 -37
  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 +10 -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.4'
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,17 +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'
22
26
 
23
27
  include Articles
28
+ include Grammar::Pronoun
29
+ include Actions
30
+ include Introduction
24
31
 
25
32
  def connect(origin, destination, direction = nil, type: Portal, two_way: true)
26
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.4
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-10 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