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

Revision 813, 19.3 KB checked in by aspidites, 8 years ago (diff)

Patch by Aspidites:

-updated all calls to settings.paths to the more readable settings.system_path and settings.user_path properties

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