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

Revision 809, 19.4 KB checked in by aspidites, 8 years ago (diff)

Patch by Aspidites:

  • Modified settings api slightly to prepare for other changes. Settings.filenames is replaced by Settings.paths as it will be more useful, IMHO.
  • Added copyright notice to settings module
  • Renamed DataDirectory? to SystemDataDirectory? and added UserDataDirectory? to [fife] section of default config file.
  • Implemented proper command line interface for settings.py

+ If multiple system names are given, multiple settings files

are generated, with the system name prepended to the filename

+ Both system and user data directories may be specified

+ It is possible to change the filename
+ not speficying anything will assume the system to be the one that

the script was run on and a filename of system.cfg

  • saves directory is now configurable (located in user's data directory by default
  • removed hard coded paths to open and save file browsers
  • removed default argument for FileBrowser?'s gui xml since it didnt' exist
  • screenshot directory now resides in user data directory
  • updated help screen
  • screenshots directory is now created only when its needed
  • when saving of games is implemented, the same should be done for the saves directory
  • 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            fife_ = self.model.settings.fife
138            parpg_ = self.model.settings.parpg
139            screenshot_directory = os.path.join(fife_.UserDataDirectory,
140                                           parpg_.ScreenshotsDirectory)
141            # try to create the screenshots directory
142            try:
143                os.mkdir(screenshot_directory)
144            #TODO: distinguish between already existing permissions error
145            except OSError:
146                logger.warning("screenshot directory wasn't created.")
147
148            screenshot_file = os.path.join(screenshot_directory,
149                                           'screen-{0}.png'.format(
150                                           datetime.now().strftime(
151                                           '%Y-%m-%d-%H-%M-%S')))
152            self.engine.getRenderBackend().captureScreen(screenshot_file)
153            logger.info("PARPG: Saved: {0}".format(screenshot_file))
154        if(key_val == key.F10):
155            # F10 shows/hides the console
156            self.engine.getGuiManager().getConsole().toggleShowHide()
157        if(key_val == key.C):
158            # C opens and closes the character screen.
159            self.view.hud.toggleCharacterScreen()
160        if(key_val == key.I):
161            # I opens and closes the inventory
162            self.view.hud.toggleInventory()
163        if(key_val == key.A):
164            # A adds a test action to the action box
165            # The test actions will follow this format: Action 1,
166            # Action 2, etc.
167            self.view.hud.addAction("Action " + str(self.action_number))
168            self.action_number += 1
169        if(key_val == key.ESCAPE):
170            # Escape brings up the main menu
171            self.view.hud.displayMenu()
172            # Hide the quit menu
173            self.view.hud.quit_window.hide()
174        if(key_val == key.M):
175            self.view.sounds.toggleMusic()
176        if(key_val == key.PAUSE):
177            # Pause pause/unpause the game
178            self.model.togglePause()
179            self.pause(False)
180        if(key_val == key.SPACE):
181            self.model.active_map.centerCameraOnPlayer() 
182   
183    def mouseReleased(self, evt):
184        """If a mouse button is released, fife calls this routine.
185           We want to wait until the button is released, because otherwise
186           pychan captures the release if a menu is opened.
187           @type evt: fife.event
188           @param evt: The event that fife caught
189           @return: None"""
190        self.view.hud.hideContextMenu()
191        scr_point = fife.ScreenPoint(evt.getX(), evt.getY())
192        if(evt.getButton() == fife.MouseEvent.LEFT):
193            if(data_drag.dragging):
194                coord = self.model.getCoords(scr_point)\
195                                    .getExactLayerCoordinates()
196                commands = ({"Command": "ResetMouseCursor"}, 
197                            {"Command": "StopDragging"})
198                self.model.game_state.player_character.approach([coord.x, 
199                                                                 coord.y],
200                                    DropItemAction(self, 
201                                                   data_drag.dragged_item, 
202                                                   commands))
203            else:
204                self.model.movePlayer(self.model.getCoords(scr_point))
205        elif(evt.getButton() == fife.MouseEvent.RIGHT):
206            # is there an object here?
207            tmp_active_map = self.model.active_map
208            instances = tmp_active_map.cameras[tmp_active_map.my_cam_id].\
209                            getMatchingInstances(scr_point,
210                                                 tmp_active_map.agent_layer)
211            info = None
212            for inst in instances:
213                # check to see if this is an active item
214                if(self.model.objectActive(inst.getId())):
215                    # yes, get the model
216                    info = self.getItemActions(inst.getId())
217                    break
218
219            # take the menu items returned by the engine or show a
220            # default menu if no items
221            data = info or \
222                [["Walk", "Walk here", self.view.onWalk, 
223                  self.model.getCoords(scr_point)]]
224            # show the menu
225            self.view.hud.showContextMenu(data, (scr_point.x, scr_point.y))
226   
227       
228    def updateMouse(self):
229        """Updates the mouse values"""
230        if self.paused:
231            return
232        cursor = self.engine.getCursor()
233        #this can be helpful for IDEs code analysis
234        if False:
235            assert(isinstance(cursor, fife.Cursor))
236        self.last_mousecoords = fife.ScreenPoint(cursor.getX(), 
237                                                 cursor.getY())       
238        self.view.highlightFrontObject(self.last_mousecoords)       
239       
240        #set the trigger area in pixles
241        pixle_edge = 20
242       
243        mouse_x = self.last_mousecoords.x
244        screen_width = self.model.engine.getSettings().getScreenWidth()
245        mouse_y = self.last_mousecoords.y
246        screen_height = self.model.engine.getSettings().getScreenHeight()
247       
248        image = None
249        settings = self.model.settings
250       
251       
252        #edge logic
253        self.scroll_direction = [0, 0]
254        if self.has_mouse_focus:
255            direction = self.scroll_direction
256            #up
257            if mouse_y <= pixle_edge: 
258                direction[0] += 1
259                direction[1] -= 1
260                image = os.path.join(settings.fife.SystemDataDirectory,
261                                     settings.parpg.GuiDirectory,
262                                     settings.parpg.CursorDirectory,
263                                     settings.parpg.CursorUp)
264               
265            #right
266            if mouse_x >= screen_width - pixle_edge:
267                direction[0] += 1
268                direction[1] += 1
269                image = os.path.join(settings.fife.SystemDataDirectory,
270                                     settings.parpg.GuiDirectory,
271                                     settings.parpg.CursorDirectory,
272                                     settings.parpg.CursorRight)
273               
274            #down
275            if mouse_y >= screen_height - pixle_edge:
276                direction[0] -= 1
277                direction[1] += 1
278                image = os.path.join(settings.fife.SystemDataDirectory,
279                                     settings.parpg.GuiDirectory,
280                                     settings.parpg.CursorDirectory,
281                                     settings.parpg.CursorDown)
282               
283            #left
284            if mouse_x <= pixle_edge:
285                direction[0] -= 1
286                direction[1] -= 1
287                image = os.path.join(settings.fife.SystemDataDirectory,
288                                     settings.parpg.GuiDirectory,
289                                     settings.parpg.CursorDirectory,
290                                     settings.parpg.CursorLeft)
291           
292            if image is not None and not data_drag.dragging:
293                self.setMouseCursor(image, image)
294       
295
296    def handleCommands(self):
297        """Check if a command is to be executed
298        """
299        if self.model.map_change:
300            self.pause(True)
301            if self.model.active_map:
302                player_char = self.model.game_state.player_character
303                self.model.game_state.player_character = None
304                pc_agent = self.model.agents[self.model.ALL_AGENTS_KEY]\
305                                                ["PlayerCharacter"]
306                pc_agent.update(player_char.getStateForSaving())
307                pc_agent["Map"] = self.model.target_map_name
308                pc_agent["Position"] = self.model.target_position
309                pc_agent["Inventory"] = \
310                        player_char.inventory.serializeInventory()
311                player_agent = self.model.active_map.\
312                                    agent_layer.getInstance("PlayerCharacter")
313                self.model.active_map.agent_layer.deleteInstance(player_agent)
314            self.model.loadMap(self.model.target_map_name)
315            self.model.setActiveMap(self.model.target_map_name)         
316            self.model.readAgentsOfMap(self.model.target_map_name)
317            self.model.placeAgents()
318            self.model.placePC()
319            self.model.map_change = False
320            # The PlayerCharacter has an inventory, and also some
321            # filling of the ready slots in the HUD.
322            # At this point we sync the contents of the ready slots
323            # with the contents of the inventory.
324            self.view.hud.inventory = None
325            self.view.hud.initializeInventory()         
326            self.pause(False)
327
328    def nullFunc(self, userdata):
329        """Sample callback for the context menus."""
330        logger.info(userdata)
331
332    def initTalk(self, npc_info):
333        """ Starts the PlayerCharacter talking to an NPC. """
334        # TODO: work more on this when we get NPCData and HeroData straightened
335        # out
336        npc = self.model.game_state.getObjectById(npc_info.ID,
337                                            self.model.game_state.\
338                                                current_map_name)
339        self.model.game_state.player_character.approach([npc.getLocation().\
340                                     getLayerCoordinates().x,
341                                     npc.getLocation().\
342                                     getLayerCoordinates().y],
343                                    TalkAction(self, npc))
344
345    def getItemActions(self, obj_id):
346        """Given the objects ID, return the text strings and callbacks.
347           @type obj_id: string
348           @param obj_id: ID of object
349           @rtype: list
350           @return: List of text and callbacks"""
351        actions = []
352        # note: ALWAYS check NPC's first!
353        obj = self.model.game_state.\
354                        getObjectById(obj_id,
355                                      self.model.game_state.current_map_name)
356       
357        if obj is not None:
358            if obj.trueAttr("NPC"):
359                # keep it simple for now, None to be replaced by callbacks
360                actions.append(["Talk", "Talk", self.initTalk, obj])
361                actions.append(["Attack", "Attack", self.nullFunc, obj])
362            else:
363                actions.append(["Examine", "Examine",
364                                self.model.game_state.\
365                                player_character.approach, 
366                                [obj.X, obj.Y],
367                                ExamineAction(self, 
368                                              obj_id, obj.name, 
369                                              obj.text)])
370                # is it a Door?
371                if obj.trueAttr("door"):
372                    actions.append(["Change Map", "Change Map",
373                       self.model.game_state.player_character.approach, 
374                       [obj.X, obj.Y],
375                       ChangeMapAction(self, obj.target_map_name,
376                                       obj.target_pos)])
377                # is it a container?
378                if obj.trueAttr("container"):
379                    actions.append(["Open", "Open", 
380                                    self.model.game_state.\
381                                        player_character.approach,
382                                    [obj.X, obj.Y],
383                                    OpenBoxAction(self, obj)])
384                    actions.append(["Unlock", "Unlock", 
385                                    self.model.game_state.\
386                                        player_character.approach,
387                                    [obj.X, obj.Y],
388                                    UnlockBoxAction(self, obj)])
389                    actions.append(["Lock", "Lock", 
390                                    self.model.game_state.\
391                                        player_character.approach,
392                                    [obj.X, obj.Y],
393                                    LockBoxAction(self, obj)])
394                # can you pick it up?
395                if obj.trueAttr("carryable"):
396                    actions.append(["Pick Up", "Pick Up", 
397                                    self.model.game_state.\
398                                        player_character.approach,
399                                    [obj.X, obj.Y],
400                                    PickUpAction(self, obj)])
401
402        return actions
403   
404    def saveGame(self, *args, **kwargs):
405        """Saves the game state, delegates call to engine.Engine
406           @return: None"""
407        self.model.pause(False)
408        self.pause(False)
409        self.view.hud.enabled = True
410        self.model.save(*args, **kwargs)
411
412    def loadGame(self, *args, **kwargs):
413        """Loads the game state, delegates call to engine.Engine
414           @return: None"""
415        # Remove all currently loaded maps so we can start fresh
416        self.model.pause(False)
417        self.pause(False)
418        self.view.hud.enabled = True
419        self.model.deleteMaps()
420        self.view.hud.inventory = None
421
422        self.model.load(*args, **kwargs)
423        self.view.hud.initializeInventory()         
424
425    def quitGame(self):
426        """Quits the game
427           @return: None"""
428        self.application.listener.quitGame()
429   
430    def pause(self, paused):
431        """Pauses the controller"""
432        super(GameSceneController, self).pause(paused)
433        self.paused = paused
434        if paused:
435            self.scroll_timer.stop()
436            self.resetMouseCursor()
437   
438    def onCommand(self, command):
439        if(command.getCommandType() == fife.CMD_MOUSE_FOCUS_GAINED):
440            self.has_mouse_focus = True
441        elif(command.getCommandType() == fife.CMD_MOUSE_FOCUS_LOST):
442            self.has_mouse_focus = False
443     
444    def pump(self):
445        """Routine called during each frame. Our main loop is in ./run.py"""
446        # uncomment to instrument
447        # t0 = time.time()
448        if self.paused: 
449            return
450        self.updateMouse()
451        if self.model.active_map:
452            self.view.highlightFrontObject(self.last_mousecoords)
453            self.view.refreshTopLayerTransparencies()
454            if self.scroll_direction != [0, 0]:
455                self.scroll_timer.start()
456            else: 
457                self.scroll_timer.stop()
458                if not data_drag.dragging:
459                    self.resetMouseCursor()
460               
461        self.handleCommands()
462        # print "%05f" % (time.time()-t0,)
Note: See TracBrowser for help on using the repository browser.