source: trunk/game/scripts/gamescenecontroller.py @ 683

Revision 671, 17.7 KB checked in by beliar, 9 years ago (diff)

Ticket 271 : Patch by Beliar.

  • Added variable to GameSceneController? to store whether the window has the mouse focus or not
  • The map will now only scroll when the window has the mouse focus

fixes[s:trac, t:271]

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