gamefic-standard 2.0.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 (70) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +7 -0
  5. data/.vscode/launch.json +20 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +60 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/gamefic-standard.gemspec +44 -0
  13. data/lib/gamefic-standard.rb +10 -0
  14. data/lib/gamefic-standard/actions.rb +13 -0
  15. data/lib/gamefic-standard/actions/drop.rb +21 -0
  16. data/lib/gamefic-standard/actions/enter.rb +21 -0
  17. data/lib/gamefic-standard/actions/go.rb +56 -0
  18. data/lib/gamefic-standard/actions/insert.rb +38 -0
  19. data/lib/gamefic-standard/actions/inventory.rb +11 -0
  20. data/lib/gamefic-standard/actions/leave.rb +32 -0
  21. data/lib/gamefic-standard/actions/look.rb +117 -0
  22. data/lib/gamefic-standard/actions/nil.rb +38 -0
  23. data/lib/gamefic-standard/actions/place.rb +43 -0
  24. data/lib/gamefic-standard/actions/quit.rb +14 -0
  25. data/lib/gamefic-standard/actions/take.rb +33 -0
  26. data/lib/gamefic-standard/actions/talk.rb +29 -0
  27. data/lib/gamefic-standard/actions/wait.rb +5 -0
  28. data/lib/gamefic-standard/articles.rb +50 -0
  29. data/lib/gamefic-standard/clothing.rb +4 -0
  30. data/lib/gamefic-standard/clothing/actions.rb +4 -0
  31. data/lib/gamefic-standard/clothing/actions/doff.rb +14 -0
  32. data/lib/gamefic-standard/clothing/actions/drop.rb +10 -0
  33. data/lib/gamefic-standard/clothing/actions/inventory.rb +16 -0
  34. data/lib/gamefic-standard/clothing/actions/wear.rb +22 -0
  35. data/lib/gamefic-standard/clothing/entities.rb +7 -0
  36. data/lib/gamefic-standard/clothing/entities/clothing.rb +5 -0
  37. data/lib/gamefic-standard/clothing/entities/coat.rb +3 -0
  38. data/lib/gamefic-standard/clothing/entities/gloves.rb +3 -0
  39. data/lib/gamefic-standard/clothing/entities/hat.rb +3 -0
  40. data/lib/gamefic-standard/clothing/entities/pants.rb +3 -0
  41. data/lib/gamefic-standard/clothing/entities/shirt.rb +3 -0
  42. data/lib/gamefic-standard/clothing/entities/shoes.rb +3 -0
  43. data/lib/gamefic-standard/container.rb +27 -0
  44. data/lib/gamefic-standard/direction.rb +55 -0
  45. data/lib/gamefic-standard/edible.rb +23 -0
  46. data/lib/gamefic-standard/entities.rb +10 -0
  47. data/lib/gamefic-standard/entities/character.rb +11 -0
  48. data/lib/gamefic-standard/entities/fixture.rb +3 -0
  49. data/lib/gamefic-standard/entities/item.rb +5 -0
  50. data/lib/gamefic-standard/entities/portal.rb +44 -0
  51. data/lib/gamefic-standard/entities/receptacle.rb +3 -0
  52. data/lib/gamefic-standard/entities/room.rb +79 -0
  53. data/lib/gamefic-standard/entities/rubble.rb +11 -0
  54. data/lib/gamefic-standard/entities/scenery.rb +7 -0
  55. data/lib/gamefic-standard/entities/supporter.rb +7 -0
  56. data/lib/gamefic-standard/entities/thing.rb +72 -0
  57. data/lib/gamefic-standard/give.rb +27 -0
  58. data/lib/gamefic-standard/grammar.rb +2 -0
  59. data/lib/gamefic-standard/grammar/attributes.rb +37 -0
  60. data/lib/gamefic-standard/grammar/pronoun.rb +101 -0
  61. data/lib/gamefic-standard/lockable.rb +93 -0
  62. data/lib/gamefic-standard/modules.rb +7 -0
  63. data/lib/gamefic-standard/modules/enterable.rb +19 -0
  64. data/lib/gamefic-standard/modules/use.rb +45 -0
  65. data/lib/gamefic-standard/openable.rb +49 -0
  66. data/lib/gamefic-standard/pathfinder.rb +65 -0
  67. data/lib/gamefic-standard/queries.rb +25 -0
  68. data/lib/gamefic-standard/test.rb +36 -0
  69. data/lib/gamefic-standard/version.rb +5 -0
  70. metadata +182 -0
@@ -0,0 +1,10 @@
1
+ require 'gamefic-standard/entities/thing'
2
+ require 'gamefic-standard/entities/character'
3
+ require 'gamefic-standard/entities/fixture'
4
+ require 'gamefic-standard/entities/item'
5
+ require 'gamefic-standard/entities/portal'
6
+ require 'gamefic-standard/entities/receptacle'
7
+ require 'gamefic-standard/entities/room'
8
+ require 'gamefic-standard/entities/scenery'
9
+ require 'gamefic-standard/entities/rubble'
10
+ require 'gamefic-standard/entities/supporter'
@@ -0,0 +1,11 @@
1
+ class Character < Thing
2
+ include Gamefic::Active
3
+
4
+ def gender
5
+ @gender ||= :other
6
+ end
7
+ end
8
+
9
+ Gamefic.script do
10
+ player_class Character
11
+ end
@@ -0,0 +1,3 @@
1
+ class Fixture < Thing
2
+
3
+ end
@@ -0,0 +1,5 @@
1
+ # An entity that is portable by default.
2
+ #
3
+ class Item < Thing
4
+ set_default portable: true
5
+ end
@@ -0,0 +1,44 @@
1
+ class Portal < Thing
2
+ attr_accessor :destination
3
+
4
+ # Find the portal in the destination that returns to this portal's parent
5
+ #
6
+ # @return [Room]
7
+ def find_reverse
8
+ return nil if destination.nil?
9
+ rev = direction.reverse
10
+ if rev != nil
11
+ destination.children.that_are(Portal).each { |c|
12
+ if c.direction == rev
13
+ return c
14
+ end
15
+ }
16
+ end
17
+ nil
18
+ end
19
+
20
+ # Get the ordinal direction of this Portal
21
+ # Portals have distinct direction and name properties so games can display a
22
+ # bare compass direction for exits, e.g., "south" vs. "the southern door."
23
+ #
24
+ # @return [Direction]
25
+ def direction
26
+ @direction
27
+ end
28
+
29
+ def direction= d
30
+ @direction = Direction.find(d)
31
+ end
32
+
33
+ def name
34
+ @name || (direction.nil? ? destination.name : direction.name)
35
+ end
36
+
37
+ def instruction
38
+ direction || (destination ? "to #{destination.definitely}" : name)
39
+ end
40
+
41
+ def synonyms
42
+ "#{super} #{@destination} #{@direction} #{!direction.nil? ? direction.synonyms : ''}"
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ class Receptacle < Thing
2
+ include Enterable
3
+ end
@@ -0,0 +1,79 @@
1
+ class Room < Thing
2
+ attr_writer :explicit_exits
3
+
4
+ set_default explicit_exits: true
5
+
6
+ def explicit_exits?
7
+ @explicit_exits
8
+ end
9
+
10
+ def synonyms
11
+ @synonyms.to_s + " around here room"
12
+ end
13
+
14
+ def tell(message)
15
+ children.each { |c|
16
+ c.tell message
17
+ }
18
+ end
19
+
20
+ def find_portal(direction)
21
+ d = direction.to_s
22
+ portals = children.that_are(Portal).delete_if { |p| p.direction.to_s != d }
23
+ portals[0]
24
+ end
25
+
26
+ class << self
27
+ def explicit_exits?
28
+ default_attributes[:explicit_exits]
29
+ end
30
+
31
+ def explicit_exits=(bool)
32
+ set_default explicit_exits: bool
33
+ end
34
+ end
35
+ end
36
+
37
+ # @todo Monkey patching might not be the best way to handle this. It's only
38
+ # necessary because of specs that make Plot#connect calls. Consider
39
+ # changing the specs instead.
40
+ module Gamefic::World
41
+ # Create portals between rooms.
42
+ #
43
+ # @return [Portal]
44
+ def connect origin, destination, direction = nil, type: Portal, two_way: true
45
+ if direction.nil?
46
+ portal = make type, :parent => origin, :destination => destination
47
+ if two_way == true
48
+ portal2 = make type, :parent => destination, :destination => origin
49
+ end
50
+ else
51
+ if direction.kind_of?(String)
52
+ direction = Direction.find(direction)
53
+ end
54
+ portal = make type, :direction => direction, :parent => origin, :destination => destination
55
+ portal.proper_named = true if type == Portal
56
+ if two_way == true
57
+ reverse = direction.reverse
58
+ if reverse == nil
59
+ raise "#{direction.name.cap_first} does not have an opposite direction"
60
+ end
61
+ portal2 = make type, :direction => reverse, :parent => destination, :destination => origin
62
+ portal2.proper_named = true if type == Portal
63
+ end
64
+ end
65
+ portal
66
+ end
67
+ end
68
+
69
+ Room.module_exec self do |plot|
70
+ # Define the connect method dynamically so the plot is available
71
+ define_method :connect do |destination, direction = nil, type: Portal, two_way: true|
72
+ plot.connect self, destination, direction, type: Portal, two_way: true
73
+ end
74
+ end
75
+ class Room
76
+ # @!method connect destination, direction = nil, type: Portal, two_way: true
77
+ # Create a portal to connect this room to a destination.
78
+ # @return [Portal]
79
+ end
@@ -0,0 +1,11 @@
1
+ # script 'standard/entities/scenery'
2
+ require 'gamefic-standard/entities/scenery'
3
+
4
+ # Rubble is Scenery with slightly modified action responses.
5
+ # Intended for things that might be portable but are useless.
6
+ # Rule of thumb: Scenery is something that can't be carried,
7
+ # like a table or the sky; and Rubble is something that might
8
+ # be portable but is otherwise useless, like trash or debris.
9
+ #
10
+ class Rubble < Scenery
11
+ end
@@ -0,0 +1,7 @@
1
+ # Scenery is an entity that is not itemized by default. They're typically used
2
+ # to provide a description for objects that can be observed but do not respond
3
+ # to any other interactions.
4
+ #
5
+ class Scenery < Thing
6
+ set_default itemized: false
7
+ end
@@ -0,0 +1,7 @@
1
+ class Supporter < Thing
2
+ include Enterable
3
+
4
+ set_default enter_verb: 'get on'
5
+ set_default leave_verb: 'get off'
6
+ set_default inside_verb: 'be on'
7
+ end
@@ -0,0 +1,72 @@
1
+ class Thing < Gamefic::Entity
2
+ include Grammar::Attributes
3
+
4
+ attr_writer :itemized
5
+
6
+ attr_writer :sticky
7
+
8
+ attr_writer :portable
9
+
10
+ # An optional description to use when itemizing entities in room
11
+ # descriptions. The locale_description will be used instead of adding
12
+ # the entity's name to a list.
13
+ #
14
+ attr_accessor :locale_description
15
+
16
+ # A message to be displayed in response to DROP actions when the entity is
17
+ # sticky.
18
+ #
19
+ attr_accessor :sticky_message
20
+
21
+ set_default itemized: true
22
+ set_default sticky: false
23
+ set_default portable: false
24
+
25
+ # Itemized entities are automatically listed in room descriptions.
26
+ #
27
+ # @return [Boolean]
28
+ def itemized?
29
+ @itemized
30
+ end
31
+
32
+ # Sticky entities cannot be dropped with DROP actions
33
+ #
34
+ # @return [Boolean]
35
+ def sticky?
36
+ @sticky
37
+ end
38
+
39
+ # Portable entities can be taken with TAKE actions.
40
+ #
41
+ # @return [Boolean]
42
+ def portable?
43
+ @portable
44
+ end
45
+
46
+ # @return [Boolean]
47
+ def attached?
48
+ @attached ||= false
49
+ end
50
+
51
+ # @param [Boolean]
52
+ def attached= bool
53
+ bool = false if parent.nil?
54
+ @attached = bool
55
+ end
56
+
57
+ def parent= p
58
+ self.attached = false unless p == parent
59
+ super
60
+ end
61
+
62
+ # The entity's parent room (i.e., the closest ascendant that is a Room).
63
+ #
64
+ # @return [Room]
65
+ def room
66
+ p = parent
67
+ until p.is_a?(Room) or p.nil?
68
+ p = p.parent
69
+ end
70
+ p
71
+ end
72
+ end
@@ -0,0 +1,27 @@
1
+ # @gamefic.script standard/give
2
+
3
+ respond :give, Use.available, Gamefic::Query::Children.new do |actor, _character, gift|
4
+ actor.tell "Nothing happens."
5
+ end
6
+
7
+ respond :give, Use.available(Character), Use.available do |actor, character, gift|
8
+ if gift.sticky?
9
+ actor.tell gift.sticky_message || "You need to keep #{the gift} for now."
10
+ else
11
+ actor.tell "#{The character} doesn't want #{the gift}."
12
+ end
13
+ end
14
+
15
+ respond :give, Use.available(Character), Use.available do |actor, _character, gift|
16
+ if gift.parent == actor
17
+ actor.proceed
18
+ else
19
+ actor.tell "You don't have #{the gift}."
20
+ end
21
+ end
22
+
23
+ respond :give, Use.text, Use.available do |actor, character, _gift|
24
+ actor.tell "You don't see any \"#{character}\" here."
25
+ end
26
+
27
+ interpret "give :gift to :character", "give :character :gift"
@@ -0,0 +1,2 @@
1
+ require 'gamefic-standard/grammar/attributes'
2
+ require 'gamefic-standard/grammar/pronoun'
@@ -0,0 +1,37 @@
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
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grammar
4
+ # Functions to select pronouns based on an entity's attributes, such as
5
+ # gender.
6
+ #
7
+ module Pronoun
8
+ class << self
9
+ # @param entity [#person, #plural?, #gender]
10
+ # @return [String]
11
+ def subjective(entity)
12
+ map(entity)[:subjective]
13
+ end
14
+ alias they subjective
15
+ alias he subjective
16
+ alias she subjective
17
+
18
+ # @param entity [#person, #plural?, #gender]
19
+ # @return [String]
20
+ def subjective_(entity)
21
+ subjective(entity).cap_first
22
+ end
23
+ alias they_ subjective_
24
+ alias he_ subjective_
25
+ alias she_ subjective_
26
+
27
+ # @param entity [#person, #plural?, #gender]
28
+ # @return [String]
29
+ def objective(entity)
30
+ map(entity)[:objective]
31
+ end
32
+ alias them objective
33
+
34
+ # @param entity [#person, #plural?, #gender]
35
+ # @return [String]
36
+ def objective_(entity)
37
+ objective(entity).cap_first
38
+ end
39
+ alias them_ objective_
40
+
41
+ # @param entity [#person, #plural?, #gender]
42
+ # @return [String]
43
+ def possessive(entity)
44
+ map(entity)[:possessive]
45
+ end
46
+ alias their possessive
47
+
48
+ # @param entity [#person, #plural?, #gender]
49
+ # @return [String]
50
+ def possessive_(entity)
51
+ possessive(entity).cap_first
52
+ end
53
+ alias their_ possessive_
54
+
55
+ # @param entity [#person, #plural?, #gender]
56
+ # @return [String]
57
+ def reflexive(entity)
58
+ map(entity)[:reflexive]
59
+ end
60
+ alias themselves reflexive
61
+ alias themself reflexive
62
+
63
+ # @param entity [#person, #plural?, #gender]
64
+ # @return [String]
65
+ def reflexive_(entity)
66
+ reflexive(entity).cap_first
67
+ end
68
+ alias themselves_ reflexive_
69
+ alias themself_ reflexive_
70
+
71
+ # @param entity [#person, #plural?, #gender]
72
+ # @return [Hash]
73
+ def map(entity)
74
+ plurality = (entity.plural? ? :plural : :singular)
75
+ maps[[(entity.person || 3), plurality, entity.gender]] ||
76
+ maps[[(entity.person || 3), plurality]]
77
+ end
78
+
79
+ private
80
+
81
+ def maps
82
+ @maps ||= {
83
+ [1, :singular] => Hash[map_keys.zip(%w[I me my myself])],
84
+ [2, :singular] => Hash[map_keys.zip(%w[you you your yourself])],
85
+ [3, :singular] => Hash[map_keys.zip(%w[it it its itself])],
86
+ [3, :singular, :male] => Hash[map_keys.zip(%w[he him his himself])],
87
+ [3, :singular, :female] => Hash[map_keys.zip(%w[she her her herself])],
88
+ [3, :singular, :other] => Hash[map_keys.zip(%w[they them their themselves])],
89
+ [3, :singular, :neutral] => Hash[map_keys.zip(%w[it it its itself])],
90
+ [1, :plural] => Hash[map_keys.zip(%w[we us our ourselves])],
91
+ [2, :plural] => Hash[map_keys.zip(%w[you you your yourselves])],
92
+ [3, :plural] => Hash[map_keys.zip(%w[they them their themselves])]
93
+ }
94
+ end
95
+
96
+ def map_keys
97
+ @map_keys ||= %i[subjective objective possessive reflexive]
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,93 @@
1
+ # @gamefic.script standard/lockable
2
+
3
+ require 'gamefic-standard/openable'
4
+
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
+ if @locked == true
15
+ self.open = false
16
+ end
17
+ end
18
+
19
+ def open=(bool)
20
+ @open = bool
21
+ @locked = false if @open == true
22
+ end
23
+
24
+ def locked?
25
+ @locked ||= false
26
+ end
27
+
28
+ def has_lock_key?
29
+ !@lock_key.nil?
30
+ end
31
+ end
32
+
33
+ Gamefic.script do
34
+ respond :lock, Use.available do |actor, thing|
35
+ actor.tell "You can't lock #{the thing}."
36
+ end
37
+
38
+ respond :_toggle_lock, Use.available(Lockable, :has_lock_key?) do |actor, thing|
39
+ verb = thing.locked? ? 'unlock' : 'lock'
40
+ key = nil
41
+ if thing.lock_key.parent == actor
42
+ key = thing.lock_key
43
+ end
44
+ if key.nil?
45
+ actor.tell "You don't have any way to #{verb} #{the thing}."
46
+ else
47
+ actor.tell "You #{verb} #{the thing} with #{the key}."
48
+ thing.locked = !thing.locked?
49
+ end
50
+ end
51
+
52
+ respond :lock, Use.available(Lockable, :has_lock_key?), Use.children do |actor, thing, key|
53
+ if thing.lock_key == key
54
+ actor.perform :_toggle_lock, thing
55
+ else
56
+ actor.tell "You can't unlock #{the thing} with #{the key}."
57
+ end
58
+ end
59
+
60
+ respond :lock, Use.available(Lockable, :has_lock_key?), Use.available do |actor, thing, key|
61
+ actor.perform :take, key if key.parent != actor
62
+ actor.proceed if key.parent == actor
63
+ end
64
+
65
+ respond :unlock, Use.available do |actor, thing|
66
+ actor.tell "You can't unlock #{the thing}."
67
+ end
68
+
69
+ respond :unlock, Use.available(Lockable, :has_lock_key?), Use.children do |actor, thing, key|
70
+ if thing.lock_key == key
71
+ actor.perform :_toggle_lock, thing
72
+ else
73
+ actor.tell "You can't unlock #{the thing} with #{the key}."
74
+ end
75
+ end
76
+
77
+ respond :unlock, Use.available(Lockable, :has_lock_key?), Use.available do |actor, thing, key|
78
+ actor.perform :take, key if key.parent != actor
79
+ actor.proceed if key.parent == actor
80
+ end
81
+
82
+ respond :open, Use.available(Lockable) do |actor, thing|
83
+ if thing.locked?
84
+ actor.tell "#{The thing} is locked."
85
+ else
86
+ actor.proceed
87
+ end
88
+ end
89
+
90
+ interpret "lock :container with :key", "lock :container :key"
91
+ interpret "unlock :container with :key", "unlock :container :key"
92
+ interpret "open :container with :key", "unlock :container :key"
93
+ end