1 """
2 The basic building blocks of myst games.
3 Start here if you're not sure where to go
4 """
5 import pygame, os
6 from pygame.sprite import Group
7 from pygame.rect import Rect
8 from pygame.surface import Surface
9 from RelativeRect import RelativeRect
10 from Hotspot import Hotspot
11 from Panel import Panel
12 from DB import Table,Row
13 import pyzzle
14 import standard
15 import Text
16 import media
17
18
19
20 -class Slide(Panel):
21 """The basic building blocks of myst games.
22
23 A typical slide represents a location the player can visit in the game world.
24 Some slides may also represent items or switches that the player can interact with.
25
26 Every Slide has its own image file in the game world.
27 When the player encounters the Slide, he is presented with this image.
28 The player clicks on parts of the slide in order to perform certain actions.
29 These parts of the slide are called Hotspots.
30
31 As a subclass of Panel, the Slide class resembles both the Sprite and Group
32 classes of Pygame. As Sprites, they can blit an image to the screen, but as
33 Groups, they can manage the behavior of other Sprites that are nested within them.
34 Typically, Hotspots are the only Sprites nested within Slides, but it is possible
35 to store other types of Sprites, such as Text, Movies, and even other Slides."""
36
37 __metaclass__=Table
38
40 panWidth=.2
41 rectRels=\
42 {'left': Rect(0, rect.top,
43 screen.width*panWidth,rect.height),
44 'right': Rect(screen.width*(1-panWidth),rect.top,
45 screen.width*(panWidth),rect.height) }
46 highlighting={'left':standard.panLeft,
47 'right':standard.panRight}
48 hotspot=Hotspot(self, None, cursor=direction+'.png',
49 onHighlight=highlighting[direction],
50 layer=.1, _template=True)
51 hotspot.rect=highlighting[direction]
52 return hotspot
54 width=.025
55 rectRels=\
56 {'left': (0,0,width,1.),
57 'right': (1.-width,0,width,1.),
58 'forward': (width,width,1-width,1-width),
59 'up': (0,0,1.,width),
60 'down': (0,1.-width,1.,width) }
61 transitions=\
62 {'left': standard.scrollLeft,
63 'right': standard.scrollRight,
64 'forward': standard.transition,
65 'up': standard.scrollUp,
66 'down': standard.scrollDown }
67 hotspot=Hotspot(self, link, None,
68 rectRel=RelativeRect(rectRels[direction]),
69 cursor=direction+'.png', onTransition=transitions[direction],
70 layer=-1., _template=True)
71 if direction=='forward':
72 hotspot.cursor='fwd.png'
73 hotspot.soundfile = self._movementSoundfile
74 if self._refs['left'] == self._refs['right'] and direction in ('left','right'):
75 hotspot.cursor=cursor=direction+'180.png'
76 return hotspot
77 @staticmethod
79 row=Row(cells)
80 stage=pyzzle.stages[row.stage]
81 slide=Slide(row.id, row.image, stage, ambiencefile=row.ambientSound,
82 rectRel=RelativeRect((row.rectleft, row.recttop,
83 row.rectheight, row.rectwidth)),
84 layer=row.layer)
85 slide._movementSoundfile=row.movementSound
86 slide._refs={}
87 if stage and stage.movementSound and not row.movementSound:
88 slide._movementSoundfile=stage.movementSound
89 for ref in 'forward', 'up', 'down', 'right', 'left':
90 slide._refs[ref]=row[ref]
91 return slide
93 for direction in self._refs:
94 linkname=self._refs[direction]
95 link=Slide[linkname] if linkname else None
96 if pyzzle.design or linkname:
97 hotspot=self.templateHotspots(link, direction)
98 setattr(self,direction,hotspot)
99 self.add(hotspot)
101 cells= \
102 {'id':self.id,
103 'stage':self.stage.id if self.stage else None,
104 'image':self.file,
105 'ambientSound' :self._ambiencefile,
106 'movementSound':self._movementSoundfile,
107 'layer' :self._layer}
108 if self.rectRel:
109 for attr in 'left','top','width','height':
110 cells['rect'+attr]=getattr(self.rectRel, attr)
111 for ref in 'forward', 'up', 'down', 'right', 'left':
112 hotspot=getattr(self,ref)
113 if hotspot and hotspot.link:
114 cells[ref]=hotspot.link.id
115 return cells
116
117
118
119
120
121 - def __init__(self, id, file, stage=None, parent=None,
122 ambiencefile=None, rectRel=None, layer=0,
123 cursor='', visible=True, enabled=True):
124 """Creates a slide.
125 Parameters:
126 @param id: A unique identifier for the Slide. Used to refer to the Slide
127 in scripts and the database
128 @param file: The name of the image file drawn by the slide.
129 @type stage: Stage
130 @param stage: An area of the game in which the slide occurs. Used to determine
131 folder paths and default ambience/movement sounds.
132 @type parent: Panel
133 @param parent: The panel that the slide is nested within.
134 The default is pyzzle.panel.
135 Note: This will not add the slide to the panel. You still need to add it
136 using Slide.enter() or one of the transition functions
137 @param ambiencefile: The name of the sound file that is played in a loop when
138 the player reaches the slide.
139 @type rectRel: RelativeRect
140 @param rectRel: The rectangle occupied by the Slide.
141 Currently, rect width and height do not resize the slide image.
142 @type layer: float
143 @param layer: The layer of the Slide. Larger numbers represent upper layers.
144 When the player clicks on an area of the Slide where two Sprites overlap,
145 the sprite with the topmost layer is the only one that's activated.
146 @param cursor: The name of the cursor file that is displayed when no hotspots are
147 highlighted. The default is defined by Panel.cursorDefault. None displays no cursor.
148 """
149 if id: Slide.rows[id]=self
150 if not parent: parent=pyzzle.panel
151 Panel.__init__(self)
152 self.id = id
153 self._file = file
154 self.stage=stage
155 self.parent=parent
156 self._ambiencefile=ambiencefile
157 self.rectRel=rectRel
158 self._layer=layer
159 self.cursor=Panel.cursorDefault if cursor =='' else cursor
160
161 self._movementSoundfile=None
162 self._rect=None
163 self.loaded=False
164 """Whether the player has loaded the slide's image file
165 in the current game session."""
166 self.links=Group()
167 """All Hotspots that link to this slide when clicked."""
168 self.visited=False
169 """Whether the player has previously visited the slide."""
170
171 for ref in 'forward', 'up', 'down', 'right', 'left':
172 setattr(self, ref, None)
173
174
198 image=property(_getImage)
199
201 """The portion of the screen occupied by the slide.
202 rect coordinates are determined by rectRel.
203 If a coordinate in rectRel is None, the coordinate is
204 determined by the slide's image size.
205 """
206 self._getImage()
207 return self._rect
208 rect=property(_getRect)
209
210
212 self._file=file
213 self.loaded=False
215 """The name of the image file displayed by the Slide.
216 Resets self.loaded to False"""
217 return self._file
218 file=property(_getFile, _setFile)
219
220
222 """The name of the sound file that is played in a loop when
223 the player reaches the slide. If none is specified, it returns
224 the default for the slide's stage."""
225 if self._ambiencefile:
226 return self._ambiencefile
227 if self.stage:
228 return self.stage.ambientSound
230 self._ambiencefile=value
231 ambiencefile=property(_getAmbiencefile, _setAmbiencefile)
232
233 - def draw(self, screen):
241 - def enter(self, oldslide=None, delay=.1):
242 """Called when the user enters the Slide.
243 @type oldslide: Panel
244 @param oldslide: The Panel that was previously presented to the user,
245 to be replaced by self
246 @param delay: The time it should take for oldslide to transition to self
247 """
248 Panel.enter(self, oldslide, delay)
249 self.visited=True
250 if self.ambiencefile:
251 ambience=media.sounds.load(self.ambiencefile)
252 if ambience.get_num_channels() == 0:
253 ambience.play(-1, fade_ms=int(delay*1000))
254 - def exit(self, newslide=None, delay=.1):
255 """Called when the user exits the Slide.
256 @type newslide: Panel
257 @param newslide: The Panel that was previously presented to the user,
258 to be replaced by self
259 @param delay: The time it should take for oldslide to transition to self,
260 in seconds
261 """
262 Panel.exit(self, newslide, delay)
263 if self.ambiencefile and \
264 ((not hasattr(newslide, 'ambiencefile')) or self.ambiencefile!=newslide.ambiencefile):
265 ambience=media.sounds.load(self.ambiencefile)
266 ambience.fadeout(int(delay*1000))
267