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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d39e0e4c0bdd08258174c87bfe75b1a44ab60643
4
+ data.tar.gz: 201eb4941dc0d59a784992c35068a379287d9e0e
5
+ SHA512:
6
+ metadata.gz: 4939cd6592c098ce8d407b654c38c1ba75583b95b0581c8c834c3d973e9e415d36095317f837888b937e0ceb2231cfc80e003b3c59e9ff0c75b8547af4c8eb98
7
+ data.tar.gz: eece7938f58fdbf2746061eb85c2eb932fe0773afd966f544c3cb9c7ac87131da403b339cedd46bdc2ffb07fc4dda5a7626c675ed7b2dc569aa05698ea2a30f0
@@ -0,0 +1,61 @@
1
+ module Gamefic
2
+
3
+ class Action
4
+ attr_reader :command
5
+ @@defaults = Array.new
6
+ def initialize(story, command, *queries, &proc)
7
+ if (command.kind_of?(Symbol) == false)
8
+ raise "Action commands must be symbols"
9
+ end
10
+ if (queries.length + 1 != proc.arity) and (queries.length == 0 and proc.arity != -1)
11
+ raise "Number of contexts is not compatible with proc arguments"
12
+ end
13
+ @command = command
14
+ @queries = queries
15
+ @proc = proc
16
+ if story == nil
17
+ @@defaults.push self
18
+ else
19
+ story.send :add_action, self
20
+ end
21
+ end
22
+ def self.defaults
23
+ @@defaults.clone
24
+ end
25
+ def specificity
26
+ spec = 0
27
+ magnitude = 1
28
+ @queries.each { |q|
29
+ if q.kind_of?(Query)
30
+ spec += (q.specificity * magnitude)
31
+ else
32
+ spec += magnitude
33
+ end
34
+ magnitude = magnitude * 10
35
+ }
36
+ return spec
37
+ end
38
+ def key
39
+ @key
40
+ end
41
+ def queries
42
+ @queries
43
+ end
44
+ def proc
45
+ @proc
46
+ end
47
+ private
48
+ def self.explode(entity)
49
+ arr = Array.new
50
+ arr.push entity
51
+ cls = entity.class
52
+ while cls != Object
53
+ arr.push cls
54
+ cls = cls.superclass
55
+ end
56
+ arr.push String
57
+ arr.push nil
58
+ end
59
+ end
60
+
61
+ end
@@ -0,0 +1,36 @@
1
+ module Gamefic
2
+
3
+ Action.new nil, :look_inside, Query.new(:family, Container) do |actor, container|
4
+ if container.children.length == 0
5
+ actor.tell "You don't find anything."
6
+ else
7
+ actor.tell "#{container.longname} contains: #{container.children.join(', ')}"
8
+ end
9
+ end
10
+ Syntax.new nil, "look inside :container", :look_inside, :container
11
+ Syntax.new nil, "search :container", :look_inside, :container
12
+ Syntax.new nil, "look in :container", :look_inside, :container
13
+
14
+ Action.new nil, :look_in_at, Query.new(:family, Container), Subquery.new(:children, Entity) do |actor, container, item|
15
+ actor.tell item.description
16
+ end
17
+ Syntax.new nil, "look at :item in :container", :look_in_at, :container, :item
18
+ Syntax.new nil, "look :item in :container", :look_in_at, :container, :item
19
+
20
+ Action.new nil, :take_from, Query.new(:family, Container), Subquery.new(:children, Item) do |actor, container, item|
21
+ item.parent = actor
22
+ actor.tell "You take #{item.longname} from #{container.longname}."
23
+ end
24
+ Syntax.new nil, "take :item from :container", :take_from, :container, :item
25
+ Syntax.new nil, "get :item from :container", :take_from, :container, :item
26
+ Syntax.new nil, "remove :item from :container", :take_from, :container, :item
27
+
28
+ Action.new nil, :drop_in, Query.new(:family, Container), Query.new(:children) do |actor, container, item|
29
+ item.parent = container
30
+ actor.tell "You put #{item.longname} in #{container.longname}."
31
+ end
32
+ Syntax.new nil, "drop :item in :container", :drop_in, :container, :item
33
+ Syntax.new nil, "put :item in :container", :drop_in, :container, :item
34
+ Syntax.new nil, "place :item in :container", :drop_in, :container, :item
35
+
36
+ end
@@ -0,0 +1,37 @@
1
+ module Gamefic
2
+
3
+ Action.new nil, :inventory do |actor|
4
+ if actor.children.length > 0
5
+ actor.tell actor.children.join(', ')
6
+ else
7
+ actor.tell "You aren't carrying anything."
8
+ end
9
+ end
10
+ Syntax.new nil, "i", :inventory
11
+
12
+ Action.new nil, :take, Query.new(:siblings, Portable) do |actor, thing|
13
+ thing.parent = actor
14
+ actor.tell "You take #{thing.longname}.", true
15
+ end
16
+
17
+ Action.new nil, :take, Query.new(:siblings) do |actor, thing|
18
+ actor.tell "You can't carry #{thing.longname}."
19
+ end
20
+
21
+ Action.new nil, :take, String do |actor, thing|
22
+ actor.tell "You don't see anything called \"#{thing}\" here."
23
+ end
24
+
25
+ Action.new nil, :drop, Query.new(:children) do |actor, thing|
26
+ thing.parent = actor.parent
27
+ actor.tell "You drop #{thing.longname}.", true
28
+ end
29
+
30
+ Syntax.new nil, "get :thing", :take, :thing
31
+ Syntax.new nil, "pick :thing up", :take, :thing
32
+ Syntax.new nil, "pick up :thing", :take, :thing
33
+
34
+ Syntax.new nil, "put down :thing", :drop, :thing
35
+ Syntax.new nil, "put :thing down", :drop, :thing
36
+
37
+ end
@@ -0,0 +1,52 @@
1
+ module Gamefic
2
+
3
+ Action.new nil, :look do |actor|
4
+ actor.perform "itemize room full"
5
+ end
6
+
7
+ Action.new nil, :look_around do |actor|
8
+ actor.perform "look"
9
+ end
10
+
11
+ Action.new nil, :itemize_room, Query.new(:string) do |actor, option|
12
+ actor.tell "#{actor.parent.longname.cap_first}"
13
+ if option == "full"
14
+ actor.tell actor.parent.description
15
+ end
16
+ chars = actor.parent.children.that_are(Character) - [actor]
17
+ if chars.length > 0
18
+ actor.tell "Others here: #{chars.join(", ")}"
19
+ end
20
+ #items = actor.parent.children.that_are(Itemized) - [chars] - [actor] - actor.parent.children.that_are(Portal)
21
+ items = actor.parent.children.that_are(Itemized)
22
+ if items.length > 0
23
+ actor.tell "Visible items: #{items.join(", ")}"
24
+ end
25
+ portals = actor.parent.children.that_are(Portal)
26
+ if portals.length > 0
27
+ actor.tell "Obvious exits: #{portals.join(', ')}"
28
+ else
29
+ actor.tell "Obvious exits: none"
30
+ end
31
+ end
32
+ Syntax.new nil, "itemize room", :itemize_room, "short"
33
+ Syntax.new nil, "itemize room :option", :itemize_room, :option
34
+
35
+ Action.new nil, :look, Query.new(:family) do |actor, thing|
36
+ actor.tell thing.description
37
+ end
38
+
39
+ Action.new nil, :look, Query.new(:parent) do |actor, thing|
40
+ actor.perform "look"
41
+ end
42
+
43
+ Action.new nil, :look, String do |actor, string|
44
+ actor.tell "You don't see any \"#{string}\" here."
45
+ end
46
+
47
+ Syntax.new nil, "look at :thing", :look, :thing
48
+ Syntax.new nil, "examine :thing", :look, :thing
49
+ Syntax.new nil, "exam :thing", :look, :thing
50
+ Syntax.new nil, "x :thing", :look, :thing
51
+
52
+ end
@@ -0,0 +1,10 @@
1
+ module Gamefic
2
+
3
+ Action.new nil, :quit do |actor|
4
+ actor.destroy
5
+ end
6
+ Action.new nil, :commands do |actor|
7
+ actor.tell actor.plot.commandwords.sort.join(", ")
8
+ end
9
+
10
+ end
@@ -0,0 +1,39 @@
1
+ module Gamefic
2
+
3
+ Action.new nil, :go, Query.new(:siblings, Portal) do |actor, portal|
4
+ actor.parent = portal.destination
5
+ actor.tell "You go #{portal.name}."
6
+ actor.perform "itemize room"
7
+ end
8
+
9
+ Action.new nil, :go, String do |actor, string|
10
+ actor.tell "You don't see any exit \"#{string}\" from here."
11
+ end
12
+
13
+ Action.new nil, :go do |actor|
14
+ actor.tell "Where do you want to go?"
15
+ end
16
+
17
+ Syntax.new nil, "north", :go, "north"
18
+ Syntax.new nil, "south", :go, "south"
19
+ Syntax.new nil, "west", :go, "west"
20
+ Syntax.new nil, "east", :go, "east"
21
+ Syntax.new nil, "up", :go, "up"
22
+ Syntax.new nil, "down", :go, "down"
23
+ Syntax.new nil, "northwest", :go, "northwest"
24
+ Syntax.new nil, "northeast", :go, "northeast"
25
+ Syntax.new nil, "southwest", :go, "southwest"
26
+ Syntax.new nil, "southeast", :go, "southeast"
27
+
28
+ Syntax.new nil, "n", :go, "north"
29
+ Syntax.new nil, "s", :go, "south"
30
+ Syntax.new nil, "w", :go, "west"
31
+ Syntax.new nil, "e", :go, "east"
32
+ Syntax.new nil, "u", :go, "up"
33
+ Syntax.new nil, "d", :go, "down"
34
+ Syntax.new nil, "nw", :go, "northwest"
35
+ Syntax.new nil, "ne", :go, "northeast"
36
+ Syntax.new nil, "sw", :go, "southwest"
37
+ Syntax.new nil, "se", :go, "southeast"
38
+
39
+ end
@@ -0,0 +1,10 @@
1
+ module Gamefic
2
+
3
+ Dir[File.dirname(__FILE__) + '/entity_ext/*.rb'].each do |file|
4
+ require file
5
+ end
6
+ Dir[File.dirname(__FILE__) + '/action_ext/*.rb'].each do |file|
7
+ require file
8
+ end
9
+
10
+ end
@@ -0,0 +1,72 @@
1
+ module Gamefic
2
+
3
+ class Character < Entity
4
+ attr_reader :state, :queue, :user
5
+ def post_initialize
6
+ @state = CharacterState.new(self)
7
+ @queue = Array.new
8
+ end
9
+ def connect(user)
10
+ @user = user
11
+ end
12
+ def disconnect
13
+ # TODO: We might need some cleanup here. Like, move the character out of the game, or set a timeout to allow dropped users to reconnect... figure it out.
14
+ @user = nil
15
+ end
16
+ def perform(command)
17
+ #if command != nil
18
+ # @queue.push command
19
+ #end
20
+ if state.busy? == false
21
+ Director.dispatch(self, command)
22
+ else
23
+ @queue.push command
24
+ end
25
+ end
26
+ #def inject(command)
27
+ # Director.dispatch(self, command)
28
+ #end
29
+ def tell(message, refresh = false)
30
+ if user != nil and message.to_s != ''
31
+ user.stream.send "#{message}\n"
32
+ if (refresh == true)
33
+ user.refresh
34
+ end
35
+ end
36
+ end
37
+ def state=(new_state)
38
+ @state = new_state
39
+ end
40
+ def destroy
41
+ if @user != nil
42
+ @user.quit
43
+ end
44
+ super
45
+ end
46
+ def update
47
+ super
48
+ @state.update
49
+ end
50
+ end
51
+ class CharacterState
52
+ def initialize(character)
53
+ @character = character
54
+ post_initialize
55
+ end
56
+ def post_initialize
57
+ # TODO: Required by subclasses?
58
+ end
59
+ def busy?
60
+ false
61
+ end
62
+ def update
63
+ while (line = @character.queue.shift)
64
+ @character.perform line
65
+ if @character.state != self
66
+ break
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ end
@@ -0,0 +1,40 @@
1
+ class Array
2
+ def that_are(cls)
3
+ if (cls.kind_of?(Class) or cls.kind_of?(Module))
4
+ return self.clone.delete_if { |i| i.kind_of?(cls) == false }
5
+ else
6
+ if self.include?(cls)
7
+ return [cls]
8
+ end
9
+ return Array.new
10
+ end
11
+ end
12
+ def that_are_not(cls)
13
+ if (cls.kind_of?(Class) or cls.kind_of?(Module))
14
+ return self.clone.delete_if { |i| i.kind_of?(cls) == true }
15
+ else
16
+ return self.clone - [cls]
17
+ end
18
+ end
19
+ def random
20
+ return self[rand(self.length)]
21
+ end
22
+ def shuffle
23
+ self.sort { |a, b|
24
+ rand(3) <=> rand(3)
25
+ }
26
+ end
27
+ def shuffle!
28
+ self.sort! { |a, b|
29
+ rand(3) <=> rand(3)
30
+ }
31
+ end
32
+ def join_and(sep = ', ', andSep = ' and ', serial = true)
33
+ if self.length < 3
34
+ self.join(andSep)
35
+ else
36
+ start = self - [self.last]
37
+ start.join(sep) + "#{serial ? sep.strip : ''}#{andSep}#{self.last}"
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,42 @@
1
+ class String
2
+ def capitalize_first
3
+ "#{self[0,1].upcase}#{self[1,self.length]}"
4
+ end
5
+ def cap_first
6
+ self.capitalize_first
7
+ end
8
+ def specify
9
+ if self[0,2] == 'a ' or self[0,3] == 'an '
10
+ "the #{self.split(' ').shift.join(' ')}"
11
+ end
12
+ if self[0,2] == 'A ' or self[0,3] == 'An '
13
+ "The #{self.split(' ').shift.join(' ')}"
14
+ end
15
+ "#{self}"
16
+ end
17
+ def terminalize
18
+ output = ''
19
+ lines = self.split("\n")
20
+ lines.each { |line|
21
+ if line.size > 79
22
+ while (line.size > 79)
23
+ offset = line.rindex(/[\s\W]/, 79)
24
+ if (offset == 0 or offset == nil)
25
+ output = output + line + "\n"
26
+ line = ''
27
+ else
28
+ output = output + line[0,offset + 1] + "\n"
29
+ line = line[offset + 1, line.size - offset]
30
+ end
31
+ end
32
+ output = output + line + "\n"
33
+ else
34
+ output = output + line + "\n"
35
+ end
36
+ }
37
+ return output.strip
38
+ end
39
+ def split_words
40
+ self.gsub(/ +/, ' ').strip.split
41
+ end
42
+ end
@@ -0,0 +1,27 @@
1
+ require "gamefic/keywords"
2
+
3
+ module Gamefic
4
+
5
+ module Describable
6
+ attr_accessor :name, :longname, :synonyms
7
+ def keywords
8
+ Keywords.new "#{name} #{longname} #{synonyms}"
9
+ end
10
+ def keywords=(value)
11
+ @keywords = value
12
+ end
13
+ def longname
14
+ @longname.to_s != '' ? @longname : name
15
+ end
16
+ def longname=(value)
17
+ @longname = value
18
+ end
19
+ def description
20
+ @description.to_s != '' ? @description : "Nothing special."
21
+ end
22
+ def description=(value)
23
+ @description = value
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,133 @@
1
+ module Gamefic
2
+
3
+ class Director
4
+ def self.dispatch(actor, command)
5
+ command.strip!
6
+ handlers = Syntax.match(command, actor.plot.syntaxes)
7
+ options = Array.new
8
+ handlers.each { |handler|
9
+ actions = actor.plot.commands[handler.command]
10
+ if actions != nil
11
+ actions.each { |action|
12
+ orders = bind_contexts_in_result(actor, handler, action)
13
+ orders.each { |order|
14
+ args = Array.new
15
+ args.push actor
16
+ order.arguments.each { |a|
17
+ if a.length > 1
18
+ longnames = Array.new
19
+ a.each { |b|
20
+ longnames.push b.longname
21
+ }
22
+ actor.tell "I don't know which you mean: #{longnames.join(', ')}"
23
+ return
24
+ end
25
+ args.push a[0]
26
+ }
27
+ options.push [order.action.proc, args]
28
+ }
29
+ }
30
+ end
31
+ }
32
+ options.push([
33
+ Proc.new { |actor|
34
+ actor.tell "I don't know what you mean by '#{command}.'"
35
+ }, [actor], -1
36
+ ])
37
+ del = Delegate.new(options)
38
+ del.execute
39
+ end
40
+ private
41
+ def self.bind_contexts_in_result(actor, handler, action)
42
+ objects = Array.new
43
+ valid = true
44
+ prepared = Array.new
45
+ arguments = handler.arguments.clone
46
+ action.queries.each { |context|
47
+ arg = arguments.shift
48
+ if arg == nil or arg == ''
49
+ valid = false
50
+ next
51
+ end
52
+ if context == String
53
+ prepared.push [arg]
54
+ elsif context.kind_of?(Query)
55
+ if context.kind_of?(Subquery)
56
+ last = prepared.last
57
+ if last == nil or last.length > 1
58
+ valid = false
59
+ next
60
+ end
61
+ result = context.execute(last[0], arg)
62
+ else
63
+ result = context.execute(actor, arg)
64
+ end
65
+ if result.objects.length == 0
66
+ valid = false
67
+ next
68
+ else
69
+ prepared.push result.objects
70
+ if result.remainder
71
+ arguments.push result.remainder
72
+ end
73
+ end
74
+ else
75
+ # TODO: Better message
76
+ raise "Invalid object"
77
+ end
78
+ }
79
+ if valid == true
80
+ prepared.each { |p|
81
+ p.uniq!
82
+ }
83
+ objects.push Order.new(action, prepared)
84
+ end
85
+ return objects
86
+ end
87
+ end
88
+
89
+ class Director
90
+ class Delegate
91
+ @@delegation_stack = Array.new
92
+ def initialize(options)
93
+ @options = options
94
+ end
95
+ def execute
96
+ @@delegation_stack.push @options
97
+ if @options.length > 0
98
+ opt = @options.shift
99
+ if opt[1].length == 1
100
+ opt[0].call(opt[1][0])
101
+ else
102
+ opt[0].call(opt[1])
103
+ end
104
+ end
105
+ @@delegation_stack.pop
106
+ end
107
+ def self.passthru
108
+ if @@delegation_stack.last != nil
109
+ if @@delegation_stack.last.length > 0
110
+ opt = @@delegation_stack.last.shift
111
+ if opt[1].length == 1
112
+ opt[0].call(opt[1][0])
113
+ else
114
+ opt[0].call(opt[1])
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ class Order
121
+ attr_reader :action, :arguments
122
+ def initialize(action, arguments)
123
+ @action = action
124
+ @arguments = arguments
125
+ end
126
+ end
127
+ end
128
+
129
+ def self.passthru
130
+ Director::Delegate.passthru
131
+ end
132
+
133
+ end
@@ -0,0 +1,18 @@
1
+ module Gamefic
2
+
3
+ class Engine
4
+ def initialize(plot)
5
+ @plot = plot
6
+ end
7
+ def run
8
+ user = User.new @plot
9
+ @plot.introduce user.character
10
+ while true
11
+ user.stream.select
12
+ user.state.update
13
+ @plot.update
14
+ end
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,76 @@
1
+ require "digest/md5"
2
+ require "gamefic/node"
3
+ require "gamefic/describable"
4
+ require "gamefic/plot"
5
+
6
+ module Gamefic
7
+
8
+ class Entity
9
+ include Branch
10
+ include Describable
11
+ attr_reader :session, :plot
12
+ def initialize(plot, args = {})
13
+ if (plot.kind_of?(Plot) == false)
14
+ raise "First argument must be a Plot"
15
+ end
16
+ pre_initialize
17
+ #self.state = State
18
+ #@story = Subplot.current
19
+ @plot = plot
20
+ @plot.send :add_entity, self
21
+ #@story.add_entity self
22
+ args.each { |key, value|
23
+ send "#{key}=", value
24
+ }
25
+ @update_procs = Array.new
26
+ @session = Hash.new
27
+ post_initialize
28
+ end
29
+ #def self.present(args = {})
30
+ # story = Plot.Loading
31
+ # if story == nil
32
+ # raise "No plot loading"
33
+ # end
34
+ # return self.new(story, args)
35
+ #end
36
+ def uid
37
+ if @uid == nil
38
+ @uid = Digest::MD5.hexdigest(self.object_id.to_s)[0,8]
39
+ end
40
+ @uid
41
+ end
42
+ def pre_initialize
43
+ # raise NotImplementedError, "#{self.class} must implement post_initialize"
44
+ end
45
+ def post_initialize
46
+ # raise NotImplementedError, "#{self.class} must implement post_initialize"
47
+ end
48
+ def tell(message, refresh = false)
49
+ #TODO: Should this even be here? In all likelihood, only Characters receive tells, right?
50
+ #TODO: On second thought, it might be interesting to see logs from an npc point of view.
51
+ end
52
+ def to_s
53
+ name
54
+ end
55
+ def update
56
+ @update_procs.each { |p|
57
+ p.call self
58
+ }
59
+ end
60
+ def on_update(&block)
61
+ @update_procs.push block
62
+ end
63
+ def parent=(node)
64
+ if node != nil and node.kind_of?(Entity) == false and node.kind_of?(Zone) == false
65
+ raise "Entity's parent must be an Entity or a Zone"
66
+ end
67
+ super
68
+ end
69
+ def destroy
70
+ self.parent = nil
71
+ # TODO: Need to call this private method here?
72
+ @plot.send(:rem_entity, self)
73
+ end
74
+ end
75
+
76
+ end
@@ -0,0 +1,9 @@
1
+ require "gamefic/entity_ext/itemized"
2
+
3
+ module Gamefic
4
+
5
+ class Container < Entity
6
+ include Itemized
7
+ end
8
+
9
+ end
@@ -0,0 +1,9 @@
1
+ require "gamefic/entity_ext/itemized"
2
+
3
+ module Gamefic
4
+
5
+ class Fixture < Entity
6
+ include Itemized
7
+ end
8
+
9
+ end