gamefic-standard 4.0.1 → 4.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ca3a0186e345a9afcd439decd174e1e0d25af882a3c12a4fae8c93ce7dc148a9
4
- data.tar.gz: 983d0edf810abcade4ccca1ebcdb45d9052968cea7e614f4dd478d79d4fdc94b
3
+ metadata.gz: bb3b66cc12abfb8e46bd9b9fb042d2aeb34a0cdd1bd13b0c9865bee9493a5f54
4
+ data.tar.gz: c9e3e7f515bcf2b294ace5171fe0f843d8298135338f53950cca0e3468437ffe
5
5
  SHA512:
6
- metadata.gz: 10f9e483b14180248220dec5dfb2d99a331ef54f06f3e2cedd869a2577fdd6bc968457e0675bbc6af16f48f79a4585b5c0215d247053ee089f742a3d5ebe3dc7
7
- data.tar.gz: 5afbf188e3dde7b5850aedc9e7f45bc5dc9b0c46f0cd913a16f60142e8d2d2ddcf184507b4f7c2cb68b72671d2ec1df1307a1171f421428bf68d387961d84c2d
6
+ metadata.gz: fc292cde7a1465c88a64bf17c13e1f9124ed98ed59382258b878d1a56bbafd890f818568cca80f0a72a11a30e45acceb6a1c9982b6847498cc6d12c0d9ae35e4
7
+ data.tar.gz: e4d588d264b1f1ff8a4f189de4736f5d4cf10361d1f145401e20987a1e4eac08a44e46af8a0cd34487421faea7f346846c511f893088237b985d62dacb9aadd9
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 4.1.0 - February 1, 2026
2
+ - In and out directions
3
+ - Room#connect= accepts hashes
4
+ - Portal type names
5
+ - Implicit taking
6
+ - Piped words in syntaxes
7
+ - Look#itemize_parent skips parent rooms
8
+
1
9
  ## 4.0.1 - May 26, 2025
2
10
  - Fix lock/unlock actions
3
11
 
@@ -37,7 +37,7 @@ Gem::Specification.new do |spec|
37
37
 
38
38
  spec.required_ruby_version = '>= 2.7.0'
39
39
 
40
- spec.add_dependency 'gamefic', '~> 4.0'
40
+ spec.add_dependency 'gamefic', '~> 4.2', '>= 4.2.1'
41
41
  spec.add_dependency 'gamefic-grammar', '~> 1.1'
42
42
  spec.add_dependency 'gamefic-what', '~> 2.0'
43
43
 
@@ -23,11 +23,8 @@ module Gamefic
23
23
  actor.tell "#{The container} is closed."
24
24
  end
25
25
 
26
- interpret 'get in :thing', 'enter :thing'
27
- interpret 'get inside :thing', 'enter :thing'
28
- interpret 'sit in :thing', 'enter :thing'
29
- interpret 'lie in :thing', 'enter :thing'
30
- interpret 'stand in :thing', 'enter :thing'
26
+ interpret 'get in|inside :thing', 'enter :thing'
27
+ interpret 'sit|lie|stand in|inside :thing', 'enter :thing'
31
28
  end
32
29
  end
33
30
  end
@@ -16,9 +16,7 @@ module Gamefic
16
16
  end
17
17
 
18
18
  respond :give, available(Character), available do |actor, _character, gift|
19
- actor.execute :take, gift if gift.parent != actor
20
-
21
- actor.proceed if gift.parent == actor
19
+ actor.proceed if actor.have_or_take(gift)
22
20
  end
23
21
 
24
22
  respond :give, Thing do |actor, thing|
@@ -11,8 +11,7 @@ module Gamefic
11
11
  end
12
12
 
13
13
  respond :insert, available, available(Receptacle) do |actor, thing, receptacle|
14
- actor.execute :take, thing unless thing.parent == actor
15
- next unless thing.parent == actor
14
+ next unless actor.have_or_take(thing)
16
15
 
17
16
  thing.parent = receptacle
18
17
  actor.tell "You put #{the thing} in #{the receptacle}."
@@ -30,23 +29,16 @@ module Gamefic
30
29
  actor.execute :drop, thing
31
30
  end
32
31
 
33
- interpret 'drop :item in :container', 'insert :item :container'
34
- interpret 'put :item in :container', 'insert :item :container'
35
- interpret 'place :item in :container', 'insert :item :container'
36
- interpret 'set :item in :container', 'insert :item :container'
37
- interpret 'insert :item in :container', 'insert :item :container'
38
-
39
- interpret 'drop :item inside :container', 'insert :item :container'
40
- interpret 'put :item inside :container', 'insert :item :container'
41
- interpret 'place :item inside :container', 'insert :item :container'
42
- interpret 'set :item inside :container', 'insert :item :container'
43
- interpret 'insert :item inside :container', 'insert :item :container'
44
-
45
- interpret 'drop :item into :container', 'insert :item :container'
46
- interpret 'put :item into :container', 'insert :item :container'
47
- interpret 'place :item into :container', 'insert :item :container'
48
- interpret 'set :item into :container', 'insert :item :container'
49
- interpret 'insert :item into :container', 'insert :item :container'
32
+ interpret 'drop :item :container', 'insert :item :container'
33
+ interpret 'put :item :container', 'insert :item :container'
34
+ interpret 'place :item :container', 'insert :item :container'
35
+ interpret 'set :item :container', 'insert :item :container'
36
+
37
+ interpret 'insert :item in|inside|into :container', 'insert :item :container'
38
+ interpret 'drop :item in|inside|into :container', 'insert :item :container'
39
+ interpret 'put :item in|inside|into :container', 'insert :item :container'
40
+ interpret 'place :item in|inside|into :container', 'insert :item :container'
41
+ interpret 'set :item in|inside|into :container', 'insert :item :container'
50
42
  end
51
43
  end
52
44
  end
@@ -47,9 +47,7 @@ module Gamefic
47
47
  interpret 'exit', 'leave'
48
48
  interpret 'exit :supporter', 'leave :supporter'
49
49
  interpret 'get on :supporter', 'enter :supporter'
50
- interpret 'get off :supporter', 'leave :supporter'
51
- interpret 'get out :container', 'leave :container'
52
- interpret 'get out of :container', 'leave :container'
50
+ interpret 'get (off|out|out of) :supporter', 'leave :supporter'
53
51
  interpret 'out', 'leave'
54
52
  end
55
53
  end
@@ -31,12 +31,11 @@ module Gamefic
31
31
  end
32
32
  end
33
33
 
34
- respond :lock, available(Lockable, proc(&:has_lock_key?)), available do |actor, thing, key|
35
- actor.execute :take, key if key.parent != actor
36
- actor.proceed if key.parent == actor
34
+ respond :lock, available(Lockable, proc(&:has_lock_key?)), available do |actor, _thing, key|
35
+ actor.proceed if actor.have_or_take(key)
37
36
  end
38
37
 
39
- interpret "lock :container with :key", "lock :container :key"
38
+ interpret 'lock :container with :key', 'lock :container :key'
40
39
  end
41
40
  end
42
41
  end
@@ -32,7 +32,7 @@ module Gamefic
32
32
  .each { |thing| actor.tell thing.locale_description }
33
33
 
34
34
  itemize_explicit_portals(actor)
35
- itemize_parent(actor) unless actor.parent == actor.room
35
+ itemize_parent(actor)
36
36
  end
37
37
 
38
38
  def itemize_explicit_portals(actor)
@@ -58,7 +58,8 @@ module Gamefic
58
58
  end
59
59
 
60
60
  def itemize_parent(actor)
61
- return unless (parent = actor.parent)
61
+ parent = actor.parent if parent != actor.room
62
+ return unless parent
62
63
 
63
64
  siblings = parent.children.that_are_not(actor)
64
65
  if siblings.empty?
@@ -130,15 +131,10 @@ module Gamefic
130
131
  itemize_room(actor)
131
132
  end
132
133
 
133
- interpret 'look around', 'look'
134
- interpret 'look here', 'look'
134
+ interpret 'look around|here', 'look'
135
135
  interpret 'l', 'look'
136
136
 
137
- interpret 'look at :thing', 'look :thing'
138
- interpret 'look on :thing', 'look :thing'
139
- interpret 'look under :thing', 'look :thing'
140
- interpret 'look beneath :thing', 'look :thing'
141
- interpret 'look around :thing', 'look :thing'
137
+ interpret 'look at|on|under|beneath|around :thing', 'look :thing'
142
138
  interpret 'l :thing', 'look :thing'
143
139
  interpret 'examine :thing', 'look :thing'
144
140
  interpret 'x :thing', 'look :thing'
@@ -11,8 +11,7 @@ module Gamefic
11
11
  end
12
12
 
13
13
  respond :place, available, available(Supporter) do |actor, thing, supporter|
14
- actor.execute :take, thing unless thing.parent == actor
15
- next unless thing.parent == actor
14
+ next unless actor.have_or_take(thing)
16
15
 
17
16
  thing.put supporter, :on
18
17
  actor.tell "You put #{the thing} on #{the supporter}."
@@ -27,12 +26,10 @@ module Gamefic
27
26
  actor.execute :drop, thing
28
27
  end
29
28
 
30
- interpret 'put :thing on :supporter', 'place :thing :supporter'
31
- interpret 'put :thing down on :supporter', 'place :thing :supporter'
32
- interpret 'set :thing on :supporter', 'place :thing :supporter'
33
- interpret 'set :thing down on :supporter', 'place :thing :supporter'
34
- interpret 'drop :thing on :supporter', 'place :thing :supporter'
35
- interpret 'place :thing on :supporter', 'place :thing :supporter'
29
+ interpret 'put :thing (on|down on) :supporter', 'place :thing :supporter'
30
+ interpret 'set :thing (on|down on) :supporter', 'place :thing :supporter'
31
+ interpret 'drop :thing (on|down on) :supporter', 'place :thing :supporter'
32
+ interpret 'place :thing (on|down on) :supporter', 'place :thing :supporter'
36
33
  end
37
34
  end
38
35
  end
@@ -28,8 +28,7 @@ module Gamefic
28
28
  end
29
29
  end
30
30
 
31
- interpret 'look inside :thing', 'search :thing'
32
- interpret 'look in :thing', 'search :thing'
31
+ interpret 'look in|inside :thing', 'search :thing'
33
32
  end
34
33
  end
35
34
  end
@@ -28,8 +28,7 @@ module Gamefic
28
28
  end
29
29
 
30
30
  respond :unlock, available(Lockable, proc(&:has_lock_key?)), available do |actor, _thing, key|
31
- actor.execute :take, key if key.parent != actor
32
- actor.proceed if key.parent == actor
31
+ actor.proceed if actor.have_or_take(key)
33
32
  end
34
33
 
35
34
  interpret 'unlock :container with :key', 'unlock :container :key'
@@ -1,60 +1,88 @@
1
- class Direction
2
- attr_writer :adjective, :adverb, :reverse
1
+ module Gamefic
2
+ module Standard
3
+ # Descriptions of the geographical relationships between entities. Portals
4
+ # can use directions to identify the path to their destination, such as
5
+ # north, southwest, up, etc.
6
+ #
7
+ class Direction
8
+ attr_writer :adjective, :adverb, :reverse
3
9
 
4
- # @return [String]
5
- attr_accessor :name
10
+ # @return [String]
11
+ attr_accessor :name
6
12
 
7
- def initialize **args
8
- args.each { |key, value|
9
- send "#{key}=", value
10
- }
11
- end
13
+ def initialize **args
14
+ args.each { |key, value|
15
+ send "#{key}=", value
16
+ }
17
+ end
12
18
 
13
- # @return [String]
14
- def adjective
15
- @adjective || @name
16
- end
19
+ # @return [String]
20
+ def adjective
21
+ @adjective || @name
22
+ end
17
23
 
18
- # @return [String]
19
- def adverb
20
- @adverb || @name
21
- end
24
+ # @return [String]
25
+ def adverb
26
+ @adverb || @name
27
+ end
22
28
 
23
- # @return [String]
24
- def synonyms
25
- "#{adjective} #{adverb}"
26
- end
29
+ # @return [String]
30
+ def synonyms
31
+ "#{adjective} #{adverb}"
32
+ end
27
33
 
28
- def reverse
29
- Direction.find @reverse
30
- end
34
+ def reverse
35
+ Direction.find @reverse
36
+ end
31
37
 
32
- def to_s
33
- @name
34
- end
38
+ def to_s
39
+ @name
40
+ end
35
41
 
36
- class << self
37
- def compass
38
- @compass ||= {
39
- north: Direction.new(name: 'north', adjective: 'northern', reverse: :south),
40
- south: Direction.new(name: 'south', adjective: 'southern', reverse: :north),
41
- west: Direction.new(name: 'west', adjective: 'western', reverse: :east),
42
- east: Direction.new(name: 'east', adjective: 'eastern', reverse: :west),
43
- northwest: Direction.new(name: 'northwest', adjective: 'northwestern', reverse: :southeast),
44
- southeast: Direction.new(name: 'southeast', adjective: 'southeastern', reverse: :northwest),
45
- northeast: Direction.new(name: 'northeast', adjective: 'northeastern', reverse: :southwest),
46
- southwest: Direction.new(name: 'southwest', adjective: 'southwestern', reverse: :northeast),
47
- up: Direction.new(name: 'up', adjective: 'upwards', reverse: :down),
48
- down: Direction.new(name: 'down', adjective: 'downwards', reverse: :up)
49
- }
50
- end
42
+ class << self
43
+ # @return [Hash{Symbol => Direction}]
44
+ def compass
45
+ @compass ||= {
46
+ north: Direction.new(name: 'north', adjective: 'northern', reverse: :south),
47
+ south: Direction.new(name: 'south', adjective: 'southern', reverse: :north),
48
+ west: Direction.new(name: 'west', adjective: 'western', reverse: :east),
49
+ east: Direction.new(name: 'east', adjective: 'eastern', reverse: :west),
50
+ northwest: Direction.new(name: 'northwest', adjective: 'northwestern', reverse: :southeast),
51
+ southeast: Direction.new(name: 'southeast', adjective: 'southeastern', reverse: :northwest),
52
+ northeast: Direction.new(name: 'northeast', adjective: 'northeastern', reverse: :southwest),
53
+ southwest: Direction.new(name: 'southwest', adjective: 'southwestern', reverse: :northeast),
54
+ up: Direction.new(name: 'up', adjective: 'upwards', reverse: :down),
55
+ down: Direction.new(name: 'down', adjective: 'downwards', reverse: :up),
56
+ in: Direction.new(name: 'in', adjective: 'inside', reverse: :out),
57
+ out: Direction.new(name: 'in', adjective: 'inside', reverse: :in)
58
+ }
59
+ end
60
+
61
+ def names
62
+ compass.values.map(&:name)
63
+ end
64
+
65
+ # @param dir [Direction, String, Symbol]
66
+ # @return [Direction, nil]
67
+ def find(dir)
68
+ return dir if dir.is_a?(Direction)
51
69
 
52
- # @param dir [Direction, String]
53
- # @return [Direction, nil]
54
- def find(dir)
55
- return dir if dir.is_a?(Direction)
70
+ compass[dir.to_s.downcase.to_sym]
71
+ end
72
+ end
56
73
 
57
- compass[dir.to_s.downcase.to_sym]
74
+ NORTH = compass[:north]
75
+ SOUTH = compass[:south]
76
+ WEST = compass[:west]
77
+ EAST = compass[:east]
78
+ NORTHWEST = compass[:northwest]
79
+ SOUTHEAST = compass[:southeast]
80
+ NORTHEAST = compass[:northeast]
81
+ SOUTHWEST = compass[:southwest]
82
+ UP = compass[:up]
83
+ DOWN = compass[:down]
84
+ IN = compass[:in]
85
+ OUT = compass[:out]
58
86
  end
59
87
  end
60
88
  end
@@ -6,6 +6,8 @@ module Gamefic
6
6
  Character.set_default gender: :other
7
7
 
8
8
  class Character
9
+ include ImplicitTaking
10
+
9
11
  def accessible
10
12
  children.select { |child| child.is_a?(Scenery) }
11
13
  end
@@ -30,6 +30,10 @@ module Gamefic
30
30
  reverse&.lock_key = key
31
31
  end
32
32
 
33
+ def type_name
34
+ @type_name ||= 'door'
35
+ end
36
+
33
37
  private
34
38
 
35
39
  def update_reverse_open
@@ -8,6 +8,8 @@ module Gamefic
8
8
  # @return [Gamefic::Entity]
9
9
  attr_accessor :destination
10
10
 
11
+ attr_writer :type_name
12
+
11
13
  # Get the ordinal direction of this Portal
12
14
  # Portals have distinct direction and name properties so games can display a
13
15
  # bare compass direction for exits, e.g., "south" vs. "the southern door."
@@ -35,7 +37,8 @@ module Gamefic
35
37
  end
36
38
 
37
39
  def name
38
- @name || direction&.name || destination.name
40
+ @name ||
41
+ (direction ? "#{type_name} #{direction.name}" : destination.name)
39
42
  end
40
43
 
41
44
  def instruction
@@ -45,6 +48,13 @@ module Gamefic
45
48
  def synonyms
46
49
  "#{super} #{@destination} #{@direction} #{!direction.nil? ? direction.synonyms : ''}"
47
50
  end
51
+
52
+ # A description of the portal type for generating automatic names, e.g.,
53
+ # "the northern exit"
54
+ #
55
+ def type_name
56
+ @type_name ||= 'exit'
57
+ end
48
58
  end
49
59
  end
50
60
  end
@@ -2,6 +2,9 @@
2
2
 
3
3
  module Gamefic
4
4
  module Standard
5
+ # An entity that represents a location in the game world that other
6
+ # entities can occupy.
7
+ #
5
8
  class Room < Thing
6
9
  attr_writer :explicit_exits
7
10
 
@@ -27,7 +30,7 @@ module Gamefic
27
30
  # @return [Portal, Array<Portal>]
28
31
  def connect(destination, direction: nil, type: Portal, two_way: true, **opts)
29
32
  direction = Direction.find(direction)
30
- here = type.new parent: self, destination: destination, direction: Direction.find(direction), **opts
33
+ here = type.new parent: self, destination: destination, direction: direction, **opts
31
34
  return here unless two_way
32
35
 
33
36
  there = type.new parent: destination, destination: self, direction: direction&.reverse, **opts
@@ -36,21 +39,27 @@ module Gamefic
36
39
 
37
40
  protected
38
41
 
39
- %w[north south west east northeast southeast southwest northwest up down].each do |direction|
42
+ Direction.names.each do |direction|
40
43
  define_method "#{direction}=" do |destination|
41
44
  connect destination, direction: direction
42
45
  end
43
46
  end
44
47
 
45
- def connect=(destinations)
46
- all = [destinations].flatten
48
+ def connect=(definitions)
49
+ all = [definitions].flatten
47
50
  until all.empty?
48
- destination = all.shift
49
- direction = (all.first.is_a?(String) ? all.shift : nil)
50
- connect destination, direction: direction
51
+ definition = all.shift
52
+ if definition.is_a?(Hash)
53
+ connect definition[:destination], **definition.except(:destination)
54
+ else
55
+ # @todo Prefer definition hashes
56
+ direction = (all.first.is_a?(String) ? all.shift : nil)
57
+ connect definition, direction: direction
58
+ end
51
59
  end
52
60
  end
53
61
 
62
+ # @todo Candidate for deprecation
54
63
  def one_way=(destinations)
55
64
  [destinations].flatten.each do |destination|
56
65
  connect destination, two_way: false
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gamefic
4
+ module Standard
5
+ # A mixin that provides a #have_or_take method to automate possession.
6
+ # Authors can use it to ensure that an actor is holding an entity as a
7
+ # prerequisite to doing something with it.
8
+ #
9
+ module ImplicitTaking
10
+ # True if the actor is already holding the entity or successfully takes
11
+ # possession of it.
12
+ #
13
+ # @example Actor needs to be holding the item to use it
14
+ # respond :use, Item do |actor, item|
15
+ # if actor.have_or_take(item)
16
+ # actor.tell "You're using #{the item}."
17
+ # else
18
+ # actor.tell "You can't use #{the item}."
19
+ # end
20
+ # end
21
+ #
22
+ # @param entity [Gamefic::Entity]
23
+ # @return [Boolean]
24
+ def have_or_take(entity)
25
+ execute(:take, entity) unless possessions.include?(entity)
26
+
27
+ possessions.include?(entity)
28
+ end
29
+ alias has_or_takes have_or_take
30
+
31
+ private
32
+
33
+ # @param from [Array<Gamefic::Entity>]
34
+ # @return [Array<Gamefic::Entity>]
35
+ def possessions(from = children)
36
+ return [] if from.empty?
37
+
38
+ from + possessions(from.flat_map(&:accessible))
39
+ end
40
+ end
41
+ end
42
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Gamefic
4
4
  module Standard
5
- VERSION = '4.0.1'
5
+ VERSION = '4.1.0'
6
6
  end
7
7
  end
@@ -19,6 +19,7 @@ module Gamefic
19
19
  require 'gamefic/standard/openable'
20
20
  require 'gamefic/standard/lockable'
21
21
  require 'gamefic/standard/direction'
22
+ require 'gamefic/standard/implicit_taking'
22
23
  require 'gamefic/standard/entities'
23
24
  require 'gamefic/standard/actions'
24
25
  require 'gamefic/standard/introduction'
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gamefic-standard
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.1
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Snyder
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-05-26 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: gamefic
@@ -16,14 +15,20 @@ dependencies:
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: '4.0'
18
+ version: '4.2'
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: 4.2.1
20
22
  type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
23
25
  requirements:
24
26
  - - "~>"
25
27
  - !ruby/object:Gem::Version
26
- version: '4.0'
28
+ version: '4.2'
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 4.2.1
27
32
  - !ruby/object:Gem::Dependency
28
33
  name: gamefic-grammar
29
34
  requirement: !ruby/object:Gem::Requirement
@@ -188,6 +193,7 @@ files:
188
193
  - lib/gamefic/standard/entities/scenery.rb
189
194
  - lib/gamefic/standard/entities/supporter.rb
190
195
  - lib/gamefic/standard/entities/thing.rb
196
+ - lib/gamefic/standard/implicit_taking.rb
191
197
  - lib/gamefic/standard/introduction.rb
192
198
  - lib/gamefic/standard/lockable.rb
193
199
  - lib/gamefic/standard/openable.rb
@@ -199,7 +205,6 @@ homepage: https://gamefic.com
199
205
  licenses:
200
206
  - MIT
201
207
  metadata: {}
202
- post_install_message:
203
208
  rdoc_options: []
204
209
  require_paths:
205
210
  - lib
@@ -214,8 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
214
219
  - !ruby/object:Gem::Version
215
220
  version: '0'
216
221
  requirements: []
217
- rubygems_version: 3.5.22
218
- signing_key:
222
+ rubygems_version: 3.6.7
219
223
  specification_version: 4
220
224
  summary: The Gamefic standard script library.
221
225
  test_files: []