-
Notifications
You must be signed in to change notification settings - Fork 1
/
WorldApp.py
executable file
·437 lines (344 loc) · 12.6 KB
/
WorldApp.py
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
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
"""
Nat's World
Ned Batchelder, 11/2001
"""
import pygame
import pygame.cursors
import pygame.display
import pygame.draw
import pygame.font
import pygame.image
import pygame.mixer
import pygame.time
import pygame.transform
from pygame.locals import *
import Node
import Transition
# Constants
black = (0,0,0)
white = (255, 255, 255)
red = (255,0,0)
yellow = (255,255,0)
class WorldApp:
def __init__(self, parent = None):
if not pygame.image.get_extended():
raise "Must have pygame extended image support!"
#print "Constructing a WorldApp!"
if parent != None:
self.screen = parent.screen
self.screenSize = parent.screenSize
self.debug = parent.debug
self.cursors = parent.cursors
self.homeNode = parent.homeNode
self.gameTitle = parent.gameTitle
self.creditRoll = parent.creditRoll
self.creditSong = parent.creditSong
else:
self.screenSize = (800, 600)
self.homeNode = Node.NullNode()
self.node = Node.NullNode()
self.viewSize = None
# Properties
gameTitle = None
screenSize = None
fullScreen = 0
debug = 0
caption = 0
validateNodes = 0
transitions = 1
creditRoll = None
creditSong = None
recursive = 0
#
# Members
#
screen = None
view = None
viewRect = None
homeNode = None
node = None
cursors = None
#
# Property setters and getters
#
def setTitle(self, gameTitle):
self.gameTitle = gameTitle
def getTitle(self):
return self.gameTitle
def setScreenSize(self, screenSize):
self.screenSize = screenSize
def getViewSize(self):
return self.viewSize
def getViewRect(self):
return self.viewRect
def setFullScreen(self, fullScreen):
self.fullScreen = fullScreen
def setDebug(self, debug):
self.debug = debug
def setCaption(self, caption):
self.caption = caption
def setValidateNodes(self, validateNodes):
self.validateNodes = validateNodes
def setTransitions(self, transitions):
self.transitions = transitions
def setHomeNode(self, homeNode):
self.homeNode = homeNode
def getHomeNode(self):
return self.homeNode
def setCreditRoll(self, creditRoll):
self.creditRoll = creditRoll
def setCreditSong(self, creditSong):
self.creditSong = creditSong
def getView(self):
"""Get a reference to the current view"""
return self.view
def getNode(self):
"Get the current node"
return self.node
def setCursors(self, file, names):
from MakeCursors import MakeCursors
self.cursors = MakeCursors(file, names)
def getCursor(self, name):
try:
return self.cursors[name]
except KeyError:
try:
return self.cursors['arrow']
except KeyError:
return pygame.cursors.arrow
def setRecursive(self, viewRect):
"""
This game engine is recursive.
viewRect is the rect on the screen to work in.
"""
self.recursive = 1
self.viewRect = viewRect
self.viewSize = viewRect.size
#
# Methods
#
def safeApply(self, fn, *args):
"""Protect ourself from errors in a function call."""
try:
return fn(*args)
except:
if self.debug:
print "safeApply Traceback:"
import traceback
traceback.print_exc(99)
return None
def doInit(self):
"""Initialize everything."""
#pygame.mixer.pre_init(44100, 16, 1)
pygame.init()
#print pygame.display.Info()
flags = 0
if self.fullScreen:
flags = flags | FULLSCREEN
depth = pygame.display.mode_ok(self.screenSize, flags, 16)
#print depth
self.screen = pygame.display.set_mode(self.screenSize, flags, depth)
#print "screen bits = ", self.screen.get_bitsize()
#print self.screen.get_palette()
if self.gameTitle != None:
pygame.display.set_caption(self.gameTitle)
pygame.mouse.set_visible(1)
def createView(self):
# Create the view surface.
if self.viewSize == None:
self.viewSize = self.screen.get_size()
self.viewRect = Rect(0, 0, self.viewSize[0], self.viewSize[1])
self.view = pygame.Surface(self.viewSize, 0, 16)
#print "view bits = ", self.view.get_bitsize()
self.view.fill(black)
#print "View pos is %s" % (self.viewRect,)
def doTerm(self):
pygame.quit()
def doTitle(self):
"""Display the title."""
if self.gameTitle == None:
return
# Create centered text.
font = pygame.font.Font(None, self.viewRect.height/8)
text = font.render(self.gameTitle, 1, red)
textpos = text.get_rect()
textpos.center = self.view.get_rect().center
self.updateView(text, textpos)
# Display the view for one second.
pygame.time.delay(1000)
def doCredits(self):
"""Display credits."""
if self.creditRoll == None:
return
viewback = pygame.Surface(self.view.get_size(), 0, 16)
viewback.blit(self.view, (0,0))
self.view.fill(black)
roll = self.creditRoll
font = pygame.font.Font(None, self.viewRect.height/20)
lineskip = self.viewRect.height/15
rollsurf = pygame.Surface((self.viewRect.width, len(roll)*lineskip), 0, 16)
ypos = 0
for line in roll:
color = yellow
if type(line) == type(()):
line, color = line
linesurf = font.render(line, 1, color)
leftmar = (rollsurf.get_width() - linesurf.get_width())/2
rollsurf.blit(linesurf, (leftmar, ypos))
ypos += lineskip
# Play the song if we have one
if self.creditSong:
pygame.mixer.music.load(self.creditSong)
pygame.mixer.music.play()
pygame.mixer.music.set_endevent(QUIT)
else:
# After some time, quit
pygame.time.set_timer(QUIT, 10000)
# Compute parameters for scrolling credits
rollsurf.set_colorkey((0,0,0))
topy = (self.view.get_height() - rollsurf.get_height())/2
xpos = (self.view.get_width() - rollsurf.get_width())/2
vbalpha = 255
finaldelay = 1
ydelta = (self.view.get_height() - topy) / 200
if ydelta == 0:
ydelta = 1
nsteps = (self.view.get_height() - topy) / ydelta
tdelta = 2000 / nsteps
adelta = (255 / nsteps) + 1
if adelta == 0:
adelta = 1
# Run the scroll
for ypos in range(self.view.get_height(), topy, -ydelta):
viewback.set_alpha(vbalpha)
if vbalpha >= adelta:
vbalpha -= adelta
self.view.fill(black)
self.view.blit(viewback, (0,0))
self.view.blit(rollsurf, (xpos, ypos))
self.updateView()
event = pygame.event.poll()
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
finaldelay = 0
break
pygame.time.delay(tdelta)
if finaldelay:
pygame.event.wait()
# clear the quit timer.
pygame.time.set_timer(QUIT, 0)
pygame.mixer.music.set_endevent(NOEVENT)
def enterNode(self, node):
"""Handle the logic of entering a node"""
if self.debug:
print "Entering %s" % (node)
self.safeApply(node.onEnter, self)
# Get the image to display
img = self.safeApply(node.getImage, self)
# Let the old node specify a transition
if self.transitions:
tc = self.safeApply(self.node.getTransitionClass, node)
if tc:
if self.debug:
print "Using transition", tc
t = tc(self)
t.setImages(self.view, img)
t.run()
self.view.fill(self.safeApply(node.getBackColor, self))
vx, vy = self.view.get_size()
pos = (
(vx-img.get_width())/2,
(vy-img.get_height())/2
)
#if self.debug:
# print "Positioning at %d, %d" % pos
self.updateView(img, pos)
if self.caption:
font = pygame.font.Font(None, self.viewRect.height/20)
text = font.render(str(node), 1, white, black)
textpos = text.get_rect()
textpos.midbottom = self.view.get_rect().midbottom
self.updateView(text, textpos)
self.node = node
# Fake a mousemotion event to get the cursor right.
ev = pygame.event.Event(MOUSEMOTION, pos = pygame.mouse.get_pos())
self.doEvent(ev)
def leaveNode(self, node):
"""Handle the logic of leaving a node"""
if self.debug:
print "Leaving %s" % (node)
self.safeApply(node.onLeave, self)
def updateView(self, img = None, pos = None):
if 0:
print "updateView,",
print "screen = ", self.screen,
print "img = ", img,
print "pos = ", pos
if img:
#print "blitting at ", pos
self.view.blit(img, pos or (0,0))
self.screen.blit(self.view, self.viewRect)
pygame.display.update(self.viewRect)
def drawGrid(self):
view2 = pygame.Surface(self.view.get_size(), 0, 16)
view2.blit(self.view, (0,0))
w, h = view2.get_size()
for f in range(10):
pygame.draw.line(view2, white, (0, f*h/10), (w, f*h/10))
pygame.draw.line(view2, white, (f*w/10, 0), (f*w/10, h))
self.screen.blit(view2, self.viewRect)
pygame.display.update(self.viewRect)
def doEvent(self, event):
# Need to translate the mouse position to view coords, but
# the event is readonly (why?).
if hasattr(event, 'pos'):
xpos = (
event.pos[0] - self.viewRect.left,
event.pos[1] - self.viewRect.top
)
else:
xpos = (0,0)
# Let the node handle the event
return self.safeApply(self.node.onEvent, event, xpos, self)
def run(self):
if not self.recursive:
self.doInit()
self.createView()
self.doTitle()
if self.validateNodes:
Node.validateAllNodes()
# This made it look worse!
#pal = pygame.image.load(r'c:\ned\nat\world\common.pal')
#self.screen.set_palette(pal.get_palette())
# Enter the first node
self.node = self.homeNode
if type(self.node) == type(""):
self.node = Node.findNode(self.node)
self.enterNode(self.node)
# Main event loop
while 1:
event = pygame.event.wait()
#if self.debug:
# print "Event: ", event
# Quitting the game is always the same regardless of node.
if event.type == QUIT:
break
newnode = self.doEvent(event)
# What did the node ask us to do?
if type(newnode) == type(""):
newnode = Node.findNode(newnode)
if newnode != None:
# Changing nodes
self.leaveNode(self.node)
self.enterNode(newnode)
# default handling if the node didn't want it.
if newnode == None:
if event.type == KEYDOWN and event.key == K_ESCAPE:
# Quit the game
break
if self.debug and event.type == KEYDOWN and event.key == K_3:
self.drawGrid()
self.leaveNode(self.node)
# Clean up
self.doCredits()
if not self.recursive:
self.doTerm()