source: branches/active/character_customization/game/parpg/gamescenecontroller.py @ 796

Revision 796, 18.8 KB checked in by aspidites, 8 years ago (diff)

Patch by Aspdites:

  • changed PlaySounds? references to EnableSound? for consistency
  • changed optionButton references to settingsButton for consistency
  • changed hud pause menu "Options" to "Settings" for consistency
  • Property svn:eol-style set to native
Line 
1#   This file is part of PARPG.
2
3#   PARPG is free software: you can redistribute it and/or modify
4#   it under the terms of the GNU General Public License as published by
5#   the Free Software Foundation, either version 3 of the License, or
6#   (at your option) any later version.
7
8#   PARPG is distributed in the hope that it will be useful,
9#   but WITHOUT ANY WARRANTY; without even the implied warranty of
10#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11#   GNU General Public License for more details.
12
13#   You should have received a copy of the GNU General Public License
14#   along with PARPG.  If not, see <http://www.gnu.org/licenses/>.
15"""This file contains the GameSceneController that handles input when the game
16   is exploring a scene"""
17
18
19from datetime import datetime
20import random
21import glob
22import os
23
24from fife import fife
25from fife import extensions
26
27from controllerbase import ControllerBase
28from parpg.gui.hud import Hud
29from parpg.gui import drag_drop_data as data_drag
30from objects.action import ChangeMapAction, ExamineAction, OpenBoxAction, \
31                           UnlockBoxAction, LockBoxAction, TalkAction, \
32                           PickUpAction, DropItemAction
33
34#For debugging/code analysis
35if False:
36    from gamesceneview import GameSceneView
37    from gamemodel import GameModel
38    from parpg import PARPGApplication
39
40class GameSceneController(ControllerBase):
41    '''
42    This controller handles inputs when the game is in "scene" state.
43    "Scene" state is when the player can move around and interact
44    with objects. Like, talking to a npc or examining the contents of a box.
45    '''
46
47
48    def __init__(self, engine, view, model, application):
49        '''
50        Constructor
51        @param engine: Instance of the active fife engine
52        @type engine: fife.Engine
53        @param view: Instance of a GameSceneView
54        @param type: parpg.GameSceneView
55        @param model: The model that has the current gamestate
56        @type model: parpg.GameModel
57        @param application: The application that created this controller
58        @type application: parpg.PARPGApplication
59        @param settings: The current settings of the application
60        @type settings: fife.extensions.fife_settings.Setting
61        '''
62        ControllerBase.__init__(self,
63                                engine,
64                                view,
65                                model,
66                                application)
67        #this can be helpful for IDEs code analysis
68        if False:
69            assert(isinstance(self.engine, fife.Engine))
70            assert(isinstance(self.view, GameSceneView))
71            assert(isinstance(self.view, GameModel))
72            assert(isinstance(self.application, PARPGApplication))
73            assert(isinstance(self.event_manager, fife.EventManager))
74       
75        # Last saved mouse coords       
76        self.action_number = 1
77
78        self.has_mouse_focus = True
79        self.last_mousecoords = None
80        self.mouse_callback = None
81        self.original_cursor_id = self.engine.getCursor().getId()       
82        self.scroll_direction = [0, 0]
83        self.scroll_timer = extensions.fife_timer.Timer(100,
84                                          lambda: self.view.moveCamera \
85                                                   (self.scroll_direction))   
86       
87        #this is temporary until we can set the native cursor
88        self.resetMouseCursor()
89        self.paused = False
90
91        if model.settings.fife.EnableSound:
92            if not self.view.sounds.music_init:
93                music_file = random.choice(glob.glob(os.path.join(
94                                                                  "music", 
95                                                                  "*.ogg")))
96                self.view.sounds.playMusic(music_file) 
97        self.initHud()
98               
99
100    def initHud(self):
101        """Initialize the hud member
102        @return: None"""
103        hud_callbacks = {
104            'saveGame': self.saveGame,
105            'loadGame': self.loadGame,
106            'quitGame': self.quitGame,
107        }
108        self.view.hud = Hud(self, 
109                            self.model.settings, 
110                            hud_callbacks)
111
112    def keyPressed(self, evt):
113        """Whenever a key is pressed, fife calls this routine.
114           @type evt: fife.event
115           @param evt: The event that fife caught
116           @return: None"""
117        key = evt.getKey()
118        key_val = key.getValue()
119
120        if(key_val == key.Q):
121            # we need to quit the game
122            self.view.hud.quitGame()
123        if(key_val == key.T):
124            self.model.active_map.toggleRenderer('GridRenderer')
125        if(key_val == key.F1):
126            # display the help screen and pause the game
127            self.view.hud.displayHelp()
128        if(key_val == key.F5):
129            self.model.active_map.toggleRenderer('CoordinateRenderer')
130        if(key_val == key.F7):
131            # F7 saves a screenshot to fife/clients/parpg/screenshots
132           
133            screenshotdir = model.settings.parpg.ScreenshotsDirectory
134            screenshot_file = os.path.join(screenshotdir, 
135                                           'screen-{0}.png'.format(
136                                           datetime.now().strftime(
137                                           '%Y-%m-%d-%H-%M-%S')))
138            print "PARPG: Saved:", screenshot_file
139            self.engine.getRenderBackend().captureScreen(screenshot_file)
140        if(key_val == key.F10):
141            # F10 shows/hides the console
142            self.engine.getGuiManager().getConsole().toggleShowHide()
143        if(key_val == key.C):
144            # C opens and closes the character screen.
145            self.view.hud.toggleCharacterScreen()
146        if(key_val == key.I):
147            # I opens and closes the inventory
148            self.view.hud.toggleInventory()
149        if(key_val == key.A):
150            # A adds a test action to the action box
151            # The test actions will follow this format: Action 1,
152            # Action 2, etc.
153            self.view.hud.addAction("Action " + str(self.action_number))
154            self.action_number += 1
155        if(key_val == key.ESCAPE):
156            # Escape brings up the main menu
157            self.view.hud.displayMenu()
158            # Hide the quit menu
159            self.view.hud.quit_window.hide()
160        if(key_val == key.M):
161            self.view.sounds.toggleMusic()
162        if(key_val == key.PAUSE):
163            # Pause pause/unpause the game
164            self.model.togglePause()
165            self.pause(False)
166        if(key_val == key.SPACE):
167            self.model.active_map.centerCameraOnPlayer() 
168   
169    def mouseReleased(self, evt):
170        """If a mouse button is released, fife calls this routine.
171           We want to wait until the button is released, because otherwise
172           pychan captures the release if a menu is opened.
173           @type evt: fife.event
174           @param evt: The event that fife caught
175           @return: None"""
176        self.view.hud.hideContextMenu()
177        scr_point = fife.ScreenPoint(evt.getX(), evt.getY())
178        if(evt.getButton() == fife.MouseEvent.LEFT):
179            if(data_drag.dragging):
180                coord = self.model.getCoords(scr_point)\
181                                    .getExactLayerCoordinates()
182                commands = ({"Command": "ResetMouseCursor"}, 
183                            {"Command": "StopDragging"})
184                self.model.game_state.player_character.approach([coord.x, 
185                                                                 coord.y],
186                                    DropItemAction(self, 
187                                                   data_drag.dragged_item, 
188                                                   commands))
189            else:
190                self.model.movePlayer(self.model.getCoords(scr_point))
191        elif(evt.getButton() == fife.MouseEvent.RIGHT):
192            # is there an object here?
193            tmp_active_map = self.model.active_map
194            instances = tmp_active_map.cameras[tmp_active_map.my_cam_id].\
195                            getMatchingInstances(scr_point,
196                                                 tmp_active_map.agent_layer)
197            info = None
198            for inst in instances:
199                # check to see if this is an active item
200                if(self.model.objectActive(inst.getId())):
201                    # yes, get the model
202                    info = self.getItemActions(inst.getId())
203                    break
204
205            # take the menu items returned by the engine or show a
206            # default menu if no items
207            data = info or \
208                [["Walk", "Walk here", self.view.onWalk, 
209                  self.model.getCoords(scr_point)]]
210            # show the menu
211            self.view.hud.showContextMenu(data, (scr_point.x, scr_point.y))
212   
213       
214    def updateMouse(self):
215        """Updates the mouse values"""
216        if self.paused:
217            return
218        cursor = self.engine.getCursor()
219        #this can be helpful for IDEs code analysis
220        if False:
221            assert(isinstance(cursor, fife.Cursor))
222        self.last_mousecoords = fife.ScreenPoint(cursor.getX(), 
223                                                 cursor.getY())       
224        self.view.highlightFrontObject(self.last_mousecoords)       
225       
226        #set the trigger area in pixles
227        pixle_edge = 20
228       
229        mouse_x = self.last_mousecoords.x
230        screen_width = self.model.engine.getSettings().getScreenWidth()
231        mouse_y = self.last_mousecoords.y
232        screen_height = self.model.engine.getSettings().getScreenHeight()
233       
234        image = None
235        settings = self.model.settings
236       
237       
238        #edge logic
239        self.scroll_direction = [0, 0]
240        if self.has_mouse_focus:
241            direction = self.scroll_direction
242            #up
243            if mouse_y <= pixle_edge: 
244                direction[0] += 1
245                direction[1] -= 1
246                image = os.path.join(settings.fife.DataDirectory,
247                                     settings.parpg.GuiDirectory,
248                                     settings.parpg.CursorDirectory,
249                                     settings.parpg.CursorUp)
250               
251            #right
252            if mouse_x >= screen_width - pixle_edge:
253                direction[0] += 1
254                direction[1] += 1
255                image = os.path.join(settings.fife.DataDirectory,
256                                     settings.parpg.GuiDirectory,
257                                     settings.parpg.CursorDirectory,
258                                     settings.parpg.CursorRight)
259               
260            #down
261            if mouse_y >= screen_height - pixle_edge:
262                direction[0] -= 1
263                direction[1] += 1
264                image = os.path.join(settings.fife.DataDirectory,
265                                     settings.parpg.GuiDirectory,
266                                     settings.parpg.CursorDirectory,
267                                     settings.parpg.CursorDown)
268               
269            #left
270            if mouse_x <= pixle_edge:
271                direction[0] -= 1
272                direction[1] -= 1
273                image = os.path.join(settings.fife.DataDirectory,
274                                     settings.parpg.GuiDirectory,
275                                     settings.parpg.CursorDirectory,
276                                     settings.parpg.CursorLeft)
277           
278            if image is not None and not data_drag.dragging:
279                self.setMouseCursor(image, image)
280       
281
282    def handleCommands(self):
283        """Check if a command is to be executed
284        """
285        if self.model.map_change:
286            self.pause(True)
287            if self.model.active_map:
288                player_char = self.model.game_state.player_character
289                self.model.game_state.player_character = None
290                pc_agent = self.model.agents[self.model.ALL_AGENTS_KEY]\
291                                                ["PlayerCharacter"]
292                pc_agent.update(player_char.getStateForSaving())
293                pc_agent["Map"] = self.model.target_map_name
294                pc_agent["Position"] = self.model.target_position
295                pc_agent["Inventory"] = \
296                        player_char.inventory.serializeInventory()
297                player_agent = self.model.active_map.\
298                                    agent_layer.getInstance("PlayerCharacter")
299                self.model.active_map.agent_layer.deleteInstance(player_agent)
300            self.model.loadMap(self.model.target_map_name)
301            self.model.setActiveMap(self.model.target_map_name)         
302            self.model.readAgentsOfMap(self.model.target_map_name)
303            self.model.placeAgents()
304            self.model.placePC()
305            self.model.map_change = False
306            # The PlayerCharacter has an inventory, and also some
307            # filling of the ready slots in the HUD.
308            # At this point we sync the contents of the ready slots
309            # with the contents of the inventory.
310            self.view.hud.inventory = None
311            self.view.hud.initializeInventory()         
312            self.pause(False)
313
314    def nullFunc(self, userdata):
315        """Sample callback for the context menus."""
316        print userdata   
317
318    def initTalk(self, npc_info):
319        """ Starts the PlayerCharacter talking to an NPC. """
320        # TODO: work more on this when we get NPCData and HeroData straightened
321        # out
322        npc = self.model.game_state.getObjectById(npc_info.ID,
323                                            self.model.game_state.\
324                                                current_map_name)
325        self.model.game_state.player_character.approach([npc.getLocation().\
326                                     getLayerCoordinates().x,
327                                     npc.getLocation().\
328                                     getLayerCoordinates().y],
329                                    TalkAction(self, npc))
330
331    def getItemActions(self, obj_id):
332        """Given the objects ID, return the text strings and callbacks.
333           @type obj_id: string
334           @param obj_id: ID of object
335           @rtype: list
336           @return: List of text and callbacks"""
337        actions = []
338        # note: ALWAYS check NPC's first!
339        obj = self.model.game_state.\
340                        getObjectById(obj_id,
341                                      self.model.game_state.current_map_name)
342       
343        if obj is not None:
344            if obj.trueAttr("NPC"):
345                # keep it simple for now, None to be replaced by callbacks
346                actions.append(["Talk", "Talk", self.initTalk, obj])
347                actions.append(["Attack", "Attack", self.nullFunc, obj])
348            else:
349                actions.append(["Examine", "Examine",
350                                self.model.game_state.\
351                                player_character.approach, 
352                                [obj.X, obj.Y],
353                                ExamineAction(self, 
354                                              obj_id, obj.name, 
355                                              obj.text)])
356                # is it a Door?
357                if obj.trueAttr("door"):
358                    actions.append(["Change Map", "Change Map",
359                       self.model.game_state.player_character.approach, 
360                       [obj.X, obj.Y],
361                       ChangeMapAction(self, obj.target_map_name,
362                                       obj.target_pos)])
363                # is it a container?
364                if obj.trueAttr("container"):
365                    actions.append(["Open", "Open", 
366                                    self.model.game_state.\
367                                        player_character.approach,
368                                    [obj.X, obj.Y],
369                                    OpenBoxAction(self, obj)])
370                    actions.append(["Unlock", "Unlock", 
371                                    self.model.game_state.\
372                                        player_character.approach,
373                                    [obj.X, obj.Y],
374                                    UnlockBoxAction(self, obj)])
375                    actions.append(["Lock", "Lock", 
376                                    self.model.game_state.\
377                                        player_character.approach,
378                                    [obj.X, obj.Y],
379                                    LockBoxAction(self, obj)])
380                # can you pick it up?
381                if obj.trueAttr("carryable"):
382                    actions.append(["Pick Up", "Pick Up", 
383                                    self.model.game_state.\
384                                        player_character.approach,
385                                    [obj.X, obj.Y],
386                                    PickUpAction(self, obj)])
387
388        return actions
389   
390    def saveGame(self, *args, **kwargs):
391        """Saves the game state, delegates call to engine.Engine
392           @return: None"""
393        self.model.pause(False)
394        self.pause(False)
395        self.view.hud.enabled = True
396        self.model.save(*args, **kwargs)
397
398    def loadGame(self, *args, **kwargs):
399        """Loads the game state, delegates call to engine.Engine
400           @return: None"""
401        # Remove all currently loaded maps so we can start fresh
402        self.model.pause(False)
403        self.pause(False)
404        self.view.hud.enabled = True
405        self.model.deleteMaps()
406        self.view.hud.inventory = None
407
408        self.model.load(*args, **kwargs)
409        self.view.hud.initializeInventory()         
410
411    def quitGame(self):
412        """Quits the game
413           @return: None"""
414        self.application.listener.quitGame()
415   
416    def pause(self, paused):
417        """Pauses the controller"""
418        super(GameSceneController, self).pause(paused)
419        self.paused = paused
420        if paused:
421            self.scroll_timer.stop()
422            self.resetMouseCursor()
423   
424    def onCommand(self, command):
425        if(command.getCommandType() == fife.CMD_MOUSE_FOCUS_GAINED):
426            self.has_mouse_focus = True
427        elif(command.getCommandType() == fife.CMD_MOUSE_FOCUS_LOST):
428            self.has_mouse_focus = False
429     
430    def pump(self):
431        """Routine called during each frame. Our main loop is in ./run.py"""
432        # uncomment to instrument
433        # t0 = time.time()
434        if self.paused: 
435            return
436        self.updateMouse()
437        if self.model.active_map:
438            self.view.highlightFrontObject(self.last_mousecoords)
439            self.view.refreshTopLayerTransparencies()
440            if self.scroll_direction != [0, 0]:
441                self.scroll_timer.start()
442            else: 
443                self.scroll_timer.stop()
444                if not data_drag.dragging:
445                    self.resetMouseCursor()
446               
447        self.handleCommands()
448        # print "%05f" % (time.time()-t0,)
Note: See TracBrowser for help on using the repository browser.