root/src/menu.py @ 267

Revision 267, 18.1 KB (checked in by andefodahl, 23 months ago)

Hopefully revamped hover system

Line 
1"""
2    Containts the hierarchy of menu classes
3"""
4
5import pygame, cPickle
6from gameobject import *
7from constants import BBUILDER
8import globalVars
9from bubbles import *
10from constants import BBUILDER
11from transition import *
12from math import sin, radians
13
14class MenuHandler:
15    """Highest level class for Menu mode. Contains all MenuPages.
16        There is only a single global MenuHandler"""
17    def __init__(self, currentPage = "page0"):
18        """initializes the menuHandler"""
19        try:
20            self.highScores = cPickle.load( open( "scores.txt", "rb" ) )
21        except:
22            self.highScores = HighScores()
23        self.pageLibrary = {}
24        self.currentPage = currentPage
25        self.nextPage = None
26        self.unlock = 0
27        self.menuInitialize()
28        self.background = BubbleBackground(globalVars.screen, 0, 0)
29        self.transition = Transition()
30       
31    def menuInitialize(self):
32        """initializes all menu elements using constants.py"""
33        page0Buttons = [MenuButton(BBUILDER.BUTTON_0, (400,245),
34                                  (200,55), "Play", "page1"),
35                        MenuButton(BBUILDER.BUTTON_0, (400,310),
36                                  (200,55), "Scores", "scorePage"),
37                        MenuButton(BBUILDER.BUTTON_0, (400,375),
38                                  (200,55), "Quit", "exit")]
39                                 
40        clickableObject00 = ClickableObject("secret.png", (215, 91),
41                                  (30, 30), (self.updateUnlock, 0,0, True))
42                                  # Unlock with highest difficulty and high score
43       
44        page0cursor = "cursor0.png"
45        page0 = MenuPage(page0cursor, page0Buttons, [], "bgtext0.png", [clickableObject00])
46        #testPage = MenuPage(page0cursor, [menuButton00, menuButton01, menuButton02], [], [clickableObject00])
47
48        page1buttons = [MenuButton(BBUILDER.BUTTON_0, (400,245),
49                                (200,50), "Puzzle", "page2"),
50                        MenuButton(BBUILDER.BUTTON_0, (400,310),
51                                (200,50), "Survival", "page3", 1, disabled=True),
52                        MenuButton(BBUILDER.BUTTON_0, (400,375),
53                                (200,50), "Back", "page0")]
54       
55        page1 = MenuPage(page0cursor, page1buttons, [], "bgtext1.png",)
56       
57        page2buttons = [MenuButton(BBUILDER.BUTTON_0, (400,245),
58                                (200,50), "Easy", "playGameEasy"),
59                        MenuButton(BBUILDER.BUTTON_0, (400,310),
60                                (200,50), "Medium", "playGameMedium", 1, disabled=True),
61                        MenuButton(BBUILDER.BUTTON_0, (400,375),
62                                (200,50), "Hard", "playGameHard", 2, disabled=True),
63                        MenuButton(BBUILDER.BUTTON_0, (400,440),
64                                (200,50), "Back", "page1")]
65                               
66        page2 = MenuPage(page0cursor, page2buttons, [], "bgtext2.png")
67                               
68        page3buttons = [MenuButton(BBUILDER.BUTTON_0, (400,245),
69                                (200,50), "Easy", "playSurvivalEasy", 1),
70                        MenuButton(BBUILDER.BUTTON_0, (400,310),
71                                (200,50), "Medium", "playSurvivalMedium", 2, disabled=True),
72                        MenuButton(BBUILDER.BUTTON_0, (400,375),
73                                (200,50), "Hard", "playSurvivalHard", 3, disabled=True),
74                        MenuButton(BBUILDER.BUTTON_0, (400,440),
75                                (200,50), "Back", "page1")]
76                               
77        page3 = MenuPage(page0cursor, page3buttons, [], "bgtext2.png")
78
79        scorePageButtons = [MenuButton(BBUILDER.BUTTON_0, (400, 500),
80                                  (200,50), "Back", "page0")]
81        scorePageTxt = self.highScores.getTextItems()
82       
83        scorePage = MenuPage(page0cursor, scorePageButtons, scorePageTxt, "bgtext3.png")
84       
85        submitButton = MenuButton(BBUILDER.BUTTON_0, (400,400),
86                                (200,50), "Submit", "scorePage")
87        cancelButton = MenuButton(BBUILDER.BUTTON_0, (400,465),
88                                (200,50), "Cancel", "page0")
89        enterName = InputScorePage(page0cursor, [submitButton, cancelButton], [])
90       
91        #self.pageLibrary['testPage'] = testPage
92        self.pageLibrary['page0'] = page0
93        self.pageLibrary['page1'] = page1
94        self.pageLibrary['page2'] = page2
95        self.pageLibrary['page3'] = page3
96        self.pageLibrary['scorePage'] = scorePage
97        self.pageLibrary['enterName'] = enterName
98   
99   
100    def updateUnlock(self, difficulty, score, secret = False):
101        """updates unlock"""
102        if secret == True:
103            self.unlock += 3
104        if (self.unlock <= difficulty): # check if it's already unlocked
105            if (score >= BBUILDER.SCORETOUNLOCK[difficulty]):
106               self.unlock+=1               
107   
108    def updateScores(self, name, score, mode):
109        """updates the high score table"""
110        self.highScores.addScore(score, name, mode)
111        cPickle.dump( self.highScores, open( "scores.txt", "wb" ) )
112               
113    def updateScorePage(self):
114        """updates the high score page with a new table"""
115        scorePage = self.pageLibrary['scorePage']
116        highScoreTxt = self.highScores.getTextItems()
117        scorePage.textList = [scorePage.textList[0]] + highScoreTxt
118
119    def update(self, destination, current):
120        """ Properly displays a transition between pages from the menu """
121        if self.transition.isInactive():
122            self.transition.setTransition(BubbleBackground(globalVars.screen, 0, 1))
123            self.transition.incrementTimer()
124            return False
125        if not self.transition.isInactive() and self.transition.timer == 30:
126            self.currentPage = destination
127            self.transition.drawBackground()
128            self.background.update(False)
129            self.pageLibrary[self.currentPage].draw(self.unlock)
130            self.transition.update(False)
131            self.transition.incrementTimer()
132            pygame.event.clear()
133            return False
134        elif not self.transition.isInactive() and self.transition.timer == 60:
135            self.transition.resetTimer()
136            self.transition.resetTransition()
137            return True
138        elif not self.transition.isInactive() and self.transition.timer != 60:
139            self.transition.drawBackground()
140            self.background.update(False)
141            self.pageLibrary[self.currentPage].draw(self.unlock)
142            self.transition.update(False)   
143            self.transition.incrementTimer()
144            pygame.event.clear()
145            return False
146
147    def draw(self):
148        """draws the current background and MenuPage."""
149        if self.transition.isInactive():
150            self.background.update()
151            self.pageLibrary[self.currentPage].draw(self.unlock)
152       
153
154class MenuPage:
155    """Class containing data for MenuPages. Held by MenuHandler."""
156    def __init__(self, cursor, menuButtonList, textList, background = None, clickableObjects = []):
157        """initializes a MenuPage"""
158        self.cursor = cursor
159        self.cursorTimer = 0
160        self.textList = textList
161        self.menuButtonList = menuButtonList
162        self.selectedButton = menuButtonList[0]
163        self.clickableObjects = clickableObjects
164        self.background = background
165       
166    def selectButton(self, intNextButton):
167        """changes location of cursor. Called by EventHandler."""
168        if self.selectedButton == None:
169            newButtonNum = 0
170        else:
171            self.selectedButton.hover = False
172            self.selectedButton.play = False
173            newButtonNum = (self.menuButtonList.index(self.selectedButton) + intNextButton) % \
174                                        len(self.menuButtonList)
175   
176       
177        # Loop through to find a not-locked button
178        unlock = globalVars.menuHandler.unlock
179        for i in range(len(self.menuButtonList)):
180            candidateButton = self.menuButtonList[newButtonNum]
181            if candidateButton.unlock <= unlock:
182                self.selectedButton = candidateButton
183                self.selectedButton.hover = True
184                self.selectedButton.play = True
185                return
186            else:
187                newButtonNum = (newButtonNum + intNextButton) % len(self.menuButtonList)
188
189                       
190        # All the buttons are locked... :/
191        self.selectedButton = None
192
193    def changeButton(self, index):
194        self.selectedButton.hover = False
195        self.selectedButton.play = False
196        self.selectedButton = self.menuButtonList[index]
197        self.selectedButton.hover = True
198        self.selectedButton.play = True
199       
200    def draw(self, unlock):
201        """draws a MenuPage and all associated elements"""
202        for clickableObject in self.clickableObjects:
203            clickableObject.draw()
204        for button in self.menuButtonList:
205            button.draw(unlock)
206            if button == self.selectedButton:
207                cursorOffset = 7*sin(radians(self.cursorTimer))
208                globalVars.graphics.drawRotatedImage(self.cursor,
209                (button.x-20-cursorOffset, button.y), -90)
210                globalVars.graphics.drawRotatedImage(self.cursor,
211                (button.x+button.size[0]+20-46+cursorOffset, button.y), 90)
212                # NOTE: 46 in the second equation is an offset for the width
213                # of the rotated triangle. It needs to be changed if the cursor
214                # size is changed
215       
216        if self.background != None:
217            globalVars.graphics.drawImage(self.background, (globalVars.screen.get_width() / 2, 120), "center")
218
219
220        self.cursorTimer += 10
221        self.cursorTimer = self.cursorTimer % 360
222       
223        for textItem in self.textList:
224            textItem.draw()
225
226
227
228class HighScores():
229    '''A class to hold the user's high scores'''
230    def __init__(self):
231        self.story = []
232        self.survival = []
233
234    def addScore(self, score, name, mode):
235        """adds a score to the score list"""
236        if mode == "Story":
237            self.story.append((score, name))
238            self.story.sort(reverse=True)
239            if len(self.story) > 10:
240                self.story = self.story[:10]
241        elif mode == "Survival":
242            self.survival.append((score, name))
243            self.survival.sort(reverse=True)
244            if len(self.survival) > 10:
245                self.survival = self.survival[:10]
246
247    def getTextItems(self):
248        '''Gets the text items to display based of of scores'''
249        storyScoresTxt = []
250        survivalScoresTxt = []
251
252        headers = [MenuText(BBUILDER.SALMON_COLOR, (180, 180),
253                                20,"Campaign Mode"),
254                   MenuText(BBUILDER.SALMON_COLOR, (580, 180),
255                                20,"Survival Mode")]
256
257        for (scoreList, xx, result) in [(self.story, 100, storyScoresTxt),
258                                        (self.survival, 500, survivalScoresTxt)]:
259
260            if scoreList == []:
261                noScore = MenuText(BBUILDER.SALMON_COLOR, (xx+70, 220),
262                                20,"No Scores to List!")
263                result.append(noScore)
264            else:
265                name = MenuText(BBUILDER.SALMON_COLOR, (xx, 200),30,"Name")
266                score = MenuText(BBUILDER.SALMON_COLOR, (xx+150, 200),30,"Score")
267                result.append(name)
268                result.append(score)
269
270                for scorePairNum in range(len(scoreList)):
271                    scorePair = scoreList[scorePairNum]
272               
273                    (x, y) = (xx, 230 + (20 * scorePairNum))
274               
275                    name = MenuText(BBUILDER.SALMON_COLOR,
276                                (x,y), 20, str(scorePair[1]))
277               
278                    score = MenuText(BBUILDER.SALMON_COLOR,
279                                 (x+150,y),20,
280                                 str(scorePair[0]))
281
282                    result += [name,score]
283       
284        return headers + storyScoresTxt + survivalScoresTxt
285   
286
287class InputScorePage(MenuPage):
288    '''MenuPage that responds to typing inputs to register score'''
289    def __init__(self, cursor, menuButtonList, textList):
290        """Initializes an InputScorePage"""
291        MenuPage.__init__(self, cursor, menuButtonList, textList)
292        self.inputStr = ""
293        self.score = 0
294        self.modeStr = ""
295        self.selectedButton = None #Cursor starts on button
296        self.textList.append(MenuText(pygame.Color("green"), (400, 160),
297                                60,"Complete!"))
298        self.textList.append(MenuText(BBUILDER.SALMON_COLOR, (400, 220),
299                        40,"Your score is "+str(self.score)))
300        self.textList.append(MenuText(BBUILDER.SALMON_COLOR, (400, 270),
301                        40,"Enter name:"))
302        self.textList.append(MenuText(pygame.Color("orange"), (400, 325),
303                        50," " + self.inputStr, True)) #Reflects input
304        self.textList.append(MenuText(pygame.Color("red"), (650, 400),
305                        30,self.inputStr)) #Error message for empty inputStr
306       
307    def registerScore(self):
308        """registers score with name and mode"""
309        globalVars.menuHandler.updateScores(self.inputStr, self.score,
310                                            self.modeStr)
311        globalVars.menuHandler.updateScorePage()
312
313    def updateInput(self, inputKey, caps):
314        """updates page based on inputKey"""
315        if inputKey == "backspace":
316            if len(self.inputStr) != 0:
317                self.inputStr = self.inputStr[:-1]
318            else:
319                self.inputStr = ''
320        elif len(self.inputStr) < 16:
321            if inputKey == "space" and self.inputStr != "":
322                self.inputStr += ' '
323            elif caps and inputKey != "space":
324                self.inputStr += self.caps(inputKey)
325            elif inputKey != "space":
326                self.inputStr += inputKey.lower()
327        if self.inputStr == '':
328            self.inputStr = ' '
329        self.textList[3].text = self.inputStr
330
331    def caps(self, inputKey):
332        """returns the version of inputKey when shift is held"""
333        if inputKey in "abcdefghijklmnopqrstuvwxyz":
334            return inputKey.upper()
335        elif inputKey in "0123456789":
336            return [')', '!', '@', '#', '$', '%', '^', '&', '*', '('][int(inputKey)]
337        else:
338            try:
339                return BBUILDER.CAPSDICT[inputKey]
340            except:
341                return inputKey
342
343    def draw(self, unlock):
344        """draws a MenuPage and all associated elements"""
345        MenuPage.draw(self, unlock)
346
347class MenuText:
348    """ used to display non-clickable text in the menu"""
349    def __init__(self, textColor, pos, font, text, cursor = False):
350        self.active = False
351        self.text = text
352        self.textSize = font
353        self.textColor = textColor
354        self.x = pos[0]
355        self.y = pos[1]
356        self.cursor = cursor
357        self.activated = cursor
358        self.timer = 0
359    def draw(self):
360        """Draws the text in the menu"""
361        self.timer += 1
362        if self.timer % 20 == 0:
363            self.activated = 1 - self.activated
364        if self.activated and self.cursor:
365            globalVars.graphics.drawText(self.text, self.x, self.y,
366                                         self.textSize, self.textColor, "center", cursor = self.cursor)
367        else:
368            globalVars.graphics.drawText(self.text, self.x, self.y,
369                                         self.textSize, self.textColor, "center", cursor = False)   
370class MenuButton(GameObject):
371    """used by MenuPages. Represents a button that can be clicked."""
372    def __init__(self, image, pos, size, text, dest, unlock=0, disabled=False):
373        """initializes a MenuButton"""
374        GameObject.__init__(self, image, pos, size)
375        self.pos = pos
376        self.originalImage = [self.image[0], self.image[1], self.image[2]]
377        self.active = False
378        self.text = text
379        self.textSize = BBUILDER.BUTTON_TEXT_SIZE
380        self.textColor = BBUILDER.BUTTON_TEXT_COLOR
381        self.dest = dest
382        self.unlock = unlock
383        self.disabled = disabled
384        self.hover = False
385       
386        self.inactive_hover_anim = text+"_hover.png"
387        self.active_hover_anim = text+"_hover.png"
388        self.hover_anim_frames = 16
389        self.hover_anim_size = (1920, 80)
390        self.hover_xoffset = (self.imageRect.width - self.hover_anim_size[0]/self.hover_anim_frames)/2
391        self.hover_yoffset = (self.imageRect.height - self.hover_anim_size[1])/2
392       
393    def draw(self, unlock):
394        """draws a MenuButton"""
395        # if (self.unlock <= unlock):
396            # self.image[0] = self.originalImage[0]
397        # else:
398            # self.image[0] = self.originalImage[2]
399           
400        if globalVars.menuHandler.unlock >= self.unlock:
401            self.disabled = False
402           
403        if (not self.disabled):
404            self.image[0] = self.originalImage[0]
405            globalVars.graphics.drawObject(self, source="anim")
406        else:
407            self.image[0] = self.originalImage[2]
408            globalVars.graphics.drawObject(self, source="anim")
409            globalVars.graphics.drawText(self.text, self.pos[0], self.pos[1], 20, color=pygame.Color("white"), drawpos="center")
410       
411class ClickableObject(GameObject):
412    """clickable object on MenuPages (besides buttons)"""
413    def __init__(self, image, pos, size, action):
414        """initializes a ClickableObject"""
415        GameObject.__init__(self, image, pos, size)
416        self.action = action
417    def clicked(self):
418        self.action[0](*self.action[1:])
419    def draw(self):
420        globalVars.graphics.drawImage(self.image, (self.x, self.y))
Note: See TracBrowser for help on using the browser.