gamefic 0.0.1

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.
@@ -0,0 +1,11 @@
1
+ require "gamefic/entity_ext/itemized"
2
+ require "gamefic/entity_ext/portable"
3
+
4
+ module Gamefic
5
+
6
+ class Item < Entity
7
+ include Itemized
8
+ include Portable
9
+ end
10
+
11
+ end
@@ -0,0 +1,7 @@
1
+ module Gamefic
2
+
3
+ module Itemized
4
+
5
+ end
6
+
7
+ end
@@ -0,0 +1,33 @@
1
+ module Gamefic
2
+
3
+ module Portable
4
+
5
+ end
6
+
7
+ #Action.new("take", Context::ENVIRONMENT) { |actor, object|
8
+ # if object.parent == actor
9
+ # actor.tell "You're already carrying #{object.longname}."
10
+ # else
11
+ # if object.kind_of? Portable
12
+ # object.parent = actor
13
+ # actor.tell "You take #{object.longname}."
14
+ # else
15
+ # actor.tell "You can't carry #{object.longname}."
16
+ # end
17
+ # end
18
+ #}
19
+ #Parser.translate("get [object]", "take [object]")
20
+
21
+ #Action.new("drop", Context::INVENTORY) { |actor, object|
22
+ # if object.parent != actor
23
+ # actor.tell "You're not carrying it."
24
+ # else
25
+ # object.parent = actor.parent
26
+ # actor.tell "You drop #{object.longname}."
27
+ # end
28
+ #}
29
+
30
+ #Parser.translate "drop [object] floor", "drop [object]"
31
+ #Parser.translate "drop [object] ground", "drop [object]"
32
+
33
+ end
@@ -0,0 +1,47 @@
1
+ module Gamefic
2
+
3
+ class Portal < Entity
4
+ attr_accessor :destination
5
+ #def initialize
6
+ # super
7
+ # @destination = nil
8
+ #end
9
+ def find_reverse
10
+ rev = Portal.reverse(self.name)
11
+ if rev != nil
12
+ destination.children.that_are(Portal).each { |c|
13
+ if c.name == rev
14
+ return c
15
+ end
16
+ }
17
+ end
18
+ end
19
+ def self.reverse(direction)
20
+ case direction.downcase
21
+ when "north"
22
+ "south"
23
+ when "south"
24
+ "north"
25
+ when "west"
26
+ "east"
27
+ when "east"
28
+ "west"
29
+ when "northwest"
30
+ "southeast"
31
+ when "southeast"
32
+ "northwest"
33
+ when "northeast"
34
+ "southwest"
35
+ when "southwest"
36
+ "northeast"
37
+ when "up"
38
+ "down"
39
+ when "down"
40
+ "up"
41
+ else
42
+ nil
43
+ end
44
+ end
45
+ end
46
+
47
+ end
@@ -0,0 +1,31 @@
1
+ require "gamefic/entity_ext/portal"
2
+
3
+ module Gamefic
4
+
5
+ class Room < Entity
6
+ def post_initialize
7
+
8
+ end
9
+ def connect(destination, direction, type = Portal, two_way = true)
10
+ portal = type.new self.plot, :name => direction, :parent => self, :destination => destination
11
+ if two_way == true
12
+ reverse = Portal.reverse(direction)
13
+ if reverse == nil
14
+ raise "\"#{direction.cap_first}\" does not have an opposite direction"
15
+ end
16
+ portal2 = type.new(self.plot, {
17
+ :name => reverse,
18
+ :parent => destination,
19
+ :destination => self
20
+ })
21
+ end
22
+ portal
23
+ end
24
+ def tell(message, refresh = false)
25
+ children.each { |c|
26
+ c.tell message, refresh
27
+ }
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,7 @@
1
+ module Gamefic
2
+
3
+ class Scenery < Entity
4
+
5
+ end
6
+
7
+ end
@@ -0,0 +1,8 @@
1
+ module Gamefic
2
+
3
+ # An optional class for grouping entities.
4
+ class Zone < Entity
5
+
6
+ end
7
+
8
+ end
@@ -0,0 +1,39 @@
1
+ # Arrays of keywords that can be compared for matches.
2
+
3
+ module Gamefic
4
+
5
+ class Keywords < Array
6
+ def initialize(statement = '')
7
+ if statement.kind_of?(Keywords)
8
+ self.concat statement
9
+ else
10
+ self.concat statement.to_s.gsub(/[^a-z0-9]/i, ' ').strip.downcase.split(' ')
11
+ end
12
+ self.delete_if { |w|
13
+ w.length < 2 or w == 'an' or w == 'the'
14
+ }
15
+ self.uniq!
16
+ end
17
+ def found_in(other)
18
+ matches = 0.0
19
+ self.each { |my_word|
20
+ if (other.include?(my_word))
21
+ matches = matches + 1.0
22
+ else
23
+ other.each { |other_word|
24
+ if my_word.length < other_word.length
25
+ if other_word[0, my_word.length] == my_word
26
+ matches = matches + (my_word.length.to_f / other_word.length.to_f)
27
+ end
28
+ end
29
+ }
30
+ end
31
+ }
32
+ return matches
33
+ end
34
+ def to_s
35
+ self.join(' ')
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,62 @@
1
+ module Gamefic
2
+
3
+ module Node
4
+ def children
5
+ if @children == nil
6
+ @children = Array.new
7
+ end
8
+ @children.clone
9
+ end
10
+ def flatten
11
+ array = Array.new
12
+ children.each { |child|
13
+ array = array + recurse_flatten(child)
14
+ }
15
+ return array
16
+ end
17
+ protected
18
+ def add_child(node)
19
+ children
20
+ @children.push(node)
21
+ end
22
+ def rem_child(node)
23
+ children
24
+ @children.delete(node)
25
+ end
26
+ def concat_children(children)
27
+ children
28
+ @children.concat(children)
29
+ end
30
+ private
31
+ def recurse_flatten(node)
32
+ array = Array.new
33
+ array.push(node)
34
+ node.children.each { |child|
35
+ array = array + recurse_flatten(child)
36
+ }
37
+ return array
38
+ end
39
+ end
40
+
41
+ module Branch
42
+ include Node
43
+ def parent
44
+ @parent
45
+ end
46
+ def parent=(node)
47
+ if node == self
48
+ raise "Entity cannot be its own parent"
49
+ end
50
+ if @parent != node
51
+ if @parent != nil
52
+ @parent.send(:rem_child, self)
53
+ end
54
+ @parent = node
55
+ if @parent != nil
56
+ @parent.send(:add_child, self)
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ end
@@ -0,0 +1,188 @@
1
+ module Gamefic
2
+
3
+ class Plot
4
+ attr_reader :scenes, :commands, :conclusions, :declared_scripts
5
+ attr_accessor :story
6
+ def commandwords
7
+ words = Array.new
8
+ @syntaxes.each { |s|
9
+ words.push(s.template.split_words[0])
10
+ }
11
+ words.uniq
12
+ end
13
+ def initialize
14
+ @scenes = Hash.new
15
+ @commands = Hash.new
16
+ @syntaxes = Array.new
17
+ @conclusions = Hash.new
18
+ @update_procs = Array.new
19
+ @declared_scripts = Array.new
20
+ @entities = Array.new
21
+ post_initialize
22
+ end
23
+ def post_initialize
24
+ Action.defaults.each {|a|
25
+ add_action a
26
+ }
27
+ Syntax.defaults.each {|s|
28
+ add_syntax s
29
+ }
30
+ end
31
+ #def require_defaults
32
+ #Dir[File.dirname(__FILE__) + '/entity_ext/*.rb'].each do |file|
33
+ # require file
34
+ #end
35
+ #Dir[File.dirname(__FILE__) + '/action_ext/*.rb'].each do |file|
36
+ # require_script file
37
+ #end
38
+ #end
39
+ def action(command, *queries, &proc)
40
+ act = Action.new(self, command, *queries, &proc)
41
+ end
42
+ def respond(command, *queries, &proc)
43
+ self.action(command, *queries, &proc)
44
+ end
45
+ def entity(cls, args)
46
+ ent = cls.new(self, args)
47
+ if ent.kind_of?(Entity) == false
48
+ raise "Invalid entity class"
49
+ end
50
+ #@entities.push ent
51
+ ent
52
+ end
53
+ def make(cls, args = {})
54
+ self.entity(cls, args)
55
+ end
56
+ def syntax(*args)
57
+ syn = Syntax.new(self, *args)
58
+ @syntaxes.push syn
59
+ syn
60
+ end
61
+ def xlate(*args)
62
+ syntax(*args)
63
+ end
64
+ #def add_entity(entity)
65
+ # if @entities.include?(entity) == false
66
+ # @entities.push entity
67
+ # end
68
+ #end
69
+ def rem_entity(entity)
70
+ @entities.delete(entity)
71
+ end
72
+ def entities
73
+ @entities.clone
74
+ end
75
+ def syntaxes
76
+ @syntaxes.clone
77
+ end
78
+ def on_update(&block)
79
+ @update_procs.push block
80
+ end
81
+ def introduction (&proc)
82
+ @introduction = proc
83
+ end
84
+ def conclusion(key, &proc)
85
+ @conclusions[key] = proc
86
+ end
87
+ def scene(key, &proc)
88
+ @scenes[key] = proc
89
+ end
90
+ def introduce(player)
91
+ if @introduction != nil
92
+ @introduction.call(player)
93
+ end
94
+ end
95
+ def conclude(player, key = nil)
96
+ if key != nil and @conclusions[key]
97
+ @conclusions[key].call(player)
98
+ end
99
+ end
100
+ def cue scene
101
+ @scenes[scene].call
102
+ end
103
+ def passthru
104
+ Director::Delegate.passthru
105
+ end
106
+ def update
107
+ @update_procs.each { |p|
108
+ p.call
109
+ }
110
+ #@entities.flatten.each { |e|
111
+ @entities.each { |e|
112
+ #recursive_update e
113
+ e.update
114
+ }
115
+ end
116
+ def tell entities, message, refresh = false
117
+ entities.each { |entity|
118
+ entity.tell message, refresh
119
+ }
120
+ end
121
+ def load_script filename
122
+ plot = self
123
+ eval File.read(filename), nil, filename, 1
124
+ end
125
+ def require_script filename
126
+ if @declared_scripts.include?(filename) == false
127
+ @declared_scripts.push(filename)
128
+ load_script filename
129
+ end
130
+ end
131
+ private
132
+ def recursive_update(entity)
133
+ entity.update
134
+ entity.children.each { |e|
135
+ recursive_update e
136
+ }
137
+ end
138
+ def add_syntax syntax
139
+ if @commands[syntax.command] == nil
140
+ raise "Action \"#{syntax.command}\" does not exist"
141
+ end
142
+ @syntaxes.unshift syntax
143
+ @syntaxes.sort! { |a, b|
144
+ al = a.template.split_words.length
145
+ bl = b.template.split_words.length
146
+ if al == bl
147
+ # For syntaxes of the same length, creation order takes precedence
148
+ 0
149
+ else
150
+ bl <=> al
151
+ end
152
+ }
153
+ end
154
+ def add_action(action)
155
+ if (@commands[action.command] == nil)
156
+ @commands[action.command] = Array.new
157
+ end
158
+ @commands[action.command].unshift action
159
+ @commands[action.command].sort! { |a, b|
160
+ if a.specificity == b.specificity
161
+ # For actions of the same length, creation order takes precedence
162
+ 0
163
+ else
164
+ b.specificity <=> a.specificity
165
+ end
166
+ }
167
+ user_friendly = action.command.to_s.sub(/_/, ' ')
168
+ args = Array.new
169
+ used_names = Array.new
170
+ action.queries.each { |c|
171
+ num = 1
172
+ new_name = "var"
173
+ while used_names.include? new_name
174
+ num = num + 1
175
+ new_name = "var#{num}"
176
+ end
177
+ used_names.push new_name
178
+ user_friendly += " :#{new_name}"
179
+ args.push new_name.to_sym
180
+ }
181
+ Syntax.new self, *[user_friendly, action.command] + args
182
+ end
183
+ def add_entity(entity)
184
+ @entities.push entity
185
+ end
186
+ end
187
+
188
+ end
@@ -0,0 +1,187 @@
1
+ require "gamefic/keywords"
2
+
3
+ module Gamefic
4
+
5
+ class Query
6
+ FAMILY = :family
7
+ CHILDREN = :children
8
+ SIBLINGS = :siblings
9
+ PARENT = :parent
10
+ SELF = :self
11
+ PLOT = :plot
12
+ STRING = :string
13
+ def initialize(context, *arguments)
14
+ if context != :family and context != :children and context != :siblings and context != :parent and context != :self and context != :plot and context != :string
15
+ raise "Invalid Query context '#{context}'"
16
+ end
17
+ #if context == :string and arguments.length > 0
18
+ # raise "Query with :string context cannot take additional arguments."
19
+ #end
20
+ @context = context
21
+ @arguments = arguments
22
+ end
23
+ def execute(subject, description)
24
+ case context
25
+ when :self
26
+ array = [subject]
27
+ when :parent
28
+ array = [subject.parent]
29
+ when :plot
30
+ array = subject.plot.entities
31
+ when :children
32
+ array = subject.children
33
+ when :siblings
34
+ array = subject.parent.children
35
+ when :family
36
+ array = subject.children + subject.parent.children
37
+ when :string
38
+ if @arguments.length == 0
39
+ return Matches.new([description], description, '')
40
+ end
41
+ keywords = Keywords.new(description)
42
+ args = Keywords.new(@arguments)
43
+ found = Array.new
44
+ remainder = Array.new
45
+ keywords.each { |key|
46
+ if args.include?(key)
47
+ found.push key
48
+ else
49
+ remainder.push key
50
+ end
51
+ }
52
+ if found.length > 0
53
+ return Matches.new([description], found.join(' '), remainder.join(' '))
54
+ else
55
+ return Matches.new([], '', description)
56
+ end
57
+ else
58
+ raise "Unrecognized: #{context}"
59
+ end
60
+ @arguments.each { |arg|
61
+ if arg.kind_of?(Class) or arg.kind_of?(Module)
62
+ array.delete_if { |entity| entity.kind_of?(arg) == false }
63
+ else
64
+ if array.include?(arg)
65
+ array = Array.new
66
+ array.push(arg)
67
+ else
68
+ array.clear
69
+ end
70
+ end
71
+ }
72
+ return Query.match(description, array)
73
+ end
74
+ def context
75
+ @context
76
+ end
77
+ def Query.match(description, array)
78
+ array.each {|e|
79
+ if e.uid == description
80
+ return Matches.new([e], description, '')
81
+ end
82
+ }
83
+ keywords = description.split_words
84
+ results = array
85
+ used = Array.new
86
+ if results.length > 0
87
+ previous_match = false
88
+ while keywords.length > 0
89
+ used.push keywords.shift
90
+ new_results = Array.new
91
+ mostMatches = 0.0
92
+ results.each { |r|
93
+ words = Keywords.new(used.join(' '))
94
+ if words.length > 0
95
+ matches = words.found_in r.keywords
96
+ if matches >= mostMatches and matches > 0
97
+ if matches - mostMatches > 0.5
98
+ new_results = Array.new
99
+ end
100
+ new_results.push r
101
+ mostMatches = matches
102
+ end
103
+ end
104
+ }
105
+ if new_results.length == 0
106
+ if previous_match == true
107
+ keywords.unshift used.pop
108
+ if used.length == 0
109
+ results = new_results
110
+ end
111
+ break
112
+ end
113
+ else
114
+ previous_match = true
115
+ results = new_results
116
+ if results.length == 1
117
+ break
118
+ end
119
+ end
120
+ end
121
+ if previous_match == false
122
+ # Scrolled through every word and not a single thing matched
123
+ results = Array.new
124
+ end
125
+ end
126
+ return Matches.new(results, used.join(' '), keywords.join(' '))
127
+ end
128
+ def Query.match_one(description, array)
129
+ result = self.match(description, array)
130
+ if result.objects.length == 1
131
+ return result.objects[0]
132
+ end
133
+ return nil
134
+ end
135
+ def specificity
136
+ if @specificity == nil
137
+ @specificity = 0
138
+ case @context
139
+ when :children
140
+ @specificity += 50
141
+ when :siblings
142
+ @specificity += 40
143
+ when :family
144
+ @specificity += 30
145
+ when :parent
146
+ @specificity += 20
147
+ when :self
148
+ @specificity += 10
149
+ when :string
150
+ @specificity = 1
151
+ #return @specificity
152
+ end
153
+ magnitude = 1
154
+ @arguments.each { |item|
155
+ if item.kind_of?(Entity)
156
+ @specificity += (magnitude * 10)
157
+ item = item.class
158
+ end
159
+ if item.kind_of?(Class)
160
+ s = item
161
+ while s != nil
162
+ @specificity += magnitude
163
+ s = s.superclass
164
+ end
165
+ else
166
+ @specificity += magnitude
167
+ end
168
+ magnitude = magnitude * 10
169
+ }
170
+ end
171
+ @specificity
172
+ end
173
+ class Matches
174
+ attr_reader :objects, :matching_text, :remainder
175
+ def initialize(objects, matching_text, remainder)
176
+ @objects = objects
177
+ @matching_text = matching_text
178
+ @remainder = remainder
179
+ end
180
+ end
181
+ end
182
+
183
+ class Subquery < Query
184
+
185
+ end
186
+
187
+ end