gamefic 0.0.1

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