gamefic-standard 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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