-
Notifications
You must be signed in to change notification settings - Fork 0
/
world.rb
257 lines (223 loc) · 5.68 KB
/
world.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# Define the different exits available
class Symbol
def complete
case self.to_s.downcase[0]
when ?n; :north
when ?s; :south
when ?e; :east
when ?w; :west
when ?u; :up
when ?d; :down
else; self
end
end
def opposite
case self
when :north; :south
when :south; :north
when :east; :west
when :west; :east
when :up; :down
when :down; :up
when :northeast; :southwest
when :southeast; :northwest
when :southwest; :northeast
when :northwest; :southeast
end
end
def position(x,y)
case self
when :north; [x, y - 1]
when :south; [x, y + 1]
when :east; [x + 1, y]
when :west; [x - 1, y]
when :northeast; [x + 1, y - 1]
when :southeast; [x + 1, y + 1]
when :southwest; [x - 1, y + 1]
when :northwest; [x - 1, y - 1]
end
end
end
# Standard directions
Directions = [:north, :south, :east, :west, :northeast, :southeast, :southwest, :northwest]
All_Directions = [:north, :south, :east, :west, :northeast, :southeast, :southwest, :northwest, :up, :down]
# A standard room
class Room
def initialize (id, title, description = "", exits = {}, tile = " ")
@id, @title, @description = id, title, description
@exits = exits
@tile = tile
@art = {}
@contents = []
end
def join (dir, room)
@exits[dir] = room
room.exits[dir.opposite] = self
end
def border (dir)
return (@art[dir] || ".") unless @exits[dir]
return @exits[dir].tile
end
def players
@contents.find_all { |i| i.is_a? Player }
end
def contents_as_s(exclude = nil)
ret = ""
@contents.each { |i|
next if (i.name == exclude) || i.class == exclude
if i.is_a? Player
ret += "#{i.name} is here.\n"
else
ret += "There is #{i.name} here.\n"
end
}
ret
end
def move_entity (what, dir)
if @exits[dir]
@contents.delete what
@exits[dir].contents.push what
return true
end
false
end
def map
v = WorldVisualizer.new(self)
v.visualize
end
attr_accessor :id, :title, :description, :contents, :exits, :tile, :art
end
class WorldVisualizer
def initialize (start)
@root = start
@min_x, @min_y, @max_x, @max_y = -1, -1, 1, 1
@visual = {}
@done = {}
end
def visualize
map_room @root, [0, 0]
size_x = -@min_x + @max_x
size_y = -@min_y + @max_y
map = []
@min_y.upto(@max_y) do |y|
line = []
@min_x.upto(@max_x) do |x|
line.push @visual[[x, y]] || " "
end
map.push line.join
end
return map.join("\n")
end
def map_room (room, pos, prev = nil)
return if @done[pos]
@done[pos] = true
prev = room if prev == nil
x, y = pos[0], pos[1]
@min_x = x - 1 if x - 1 < @min_x
@min_y = y - 1 if y - 1 < @min_y
@max_x = x + 1 if x + 1 > @max_x
@max_y = y + 1 if y + 1 > @max_y
if room.contents.length > 0
@visual[[x, y]] = room.contents.length
else
@visual[[x, y]] = room.tile
end
Directions.each do |dir|
pos = dir.position(x, y)
@visual[pos] = room.border(dir) unless @visual[pos]
end
Directions.each do |dir|
if room.exits[dir]
map_room room.exits[dir], dir.position(x, y), prev unless prev == room.exits[dir]
end
end
end
end
class World
def initialize
@root = Room.new(0, "Spawning room", "Spawning vat")
@next_id = 1
end
def create_room (title, description = nil, exits = {}, tile = " ")
id = @next_id
@next_id++
Room.new(id, title, description, exits, tile)
end
def map
v = WorldVisualizer.new(@root)
v.visualize
end
attr_accessor :root, :next_id
end
#this is a base class for any Item which a Location can contain:
# any object in our Game world, from players to AIs to swords and gold.
# you differentiate items in the game world based on their class.
# it doesn't have much in the way of attrubutes: a reference to its location,
# a name and description
class Mappable
attr_accessor :name, :description, :location
def initialize(name, description, location = nil)
@name, @description, @location = name, description, location
end
end
#this is the base class for all living creatures: Human players, AIs
# it has attributes like: health, race, strength, dexterity, speed
class Creature < Mappable
attr_reader :title, :race, :health, :armour
def initialize(name, title = nil, race = nil)
@title = title ? title : ""
@race = race ? race : "Human"
@health = 100
@armour = 0
super(name, "A #{race}")
end
end
#this is something which goes in the world which is not a player.
# it has things like: weight (can a player pick it up? should it just be 'carryable'?)
class Item < Mappable
end
# Example subclass:
# swords, knives...
class StabbyItem < Item # < AttackItem ?
end
#this is a base class for events which occur within the world.
# they have a location. Multiple events can be chained into a
# sequence.
# we might want a duration / start time (in ticks) for events or
# a descendant thereof - earthquakes take more than one tick.
# maybe so does an attack, leaving you vulnerable...
class Event
attr_accessor :text, :before, :after
def initialize(text)
@text = text
@before = @after = nil
end
def insert_before(event)
event = Event.new(event) if !event.is_a? Event
@before.after = event if @before
@before = event
event.after = self
end
def insert_after(event)
event = Event.new(event) if !event.is_a? Event
@after.before = event if @after
@after = event
event.before = self
end
def to_s
@text
end
end
class FailureEvent < Event
def to_s
"EPIC FAIL: #{@text}"
end
end
def testworld
w = World.new
w.root.join(:east, Room.new(w.next_id + 1, "East", "East"))
w.root.join(:south, Room.new(w.next_id + 2, "South", "South"))
w.root.join(:north, Room.new(w.next_id + 3, "North", "North"))
w.root.join(:west, Room.new(w.next_id + 4, "West", "West"))
w.root.join(:northeast, Room.new(w.next_id + 5, "Northeast", "NE"))
end