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

Revision 797, 18.9 KB checked in by aspidites, 9 years ago (diff)

Patch by Aspidites:

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