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

Revision 796, 18.8 KB checked in by aspidites, 9 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
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
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
[736]54        @param type: parpg.GameSceneView
[642]55        @param model: The model that has the current gamestate
[736]56        @type model: parpg.GameModel
[642]57        @param application: The application that created this controller
[736]58        @type application: parpg.PARPGApplication
[642]59        @param settings: The current settings of the application
60        @type settings: fife.extensions.fife_settings.Setting
61        '''
[671]62        ControllerBase.__init__(self,
63                                engine,
64                                view,
65                                model,
66                                application)
[642]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
[671]78        self.has_mouse_focus = True
79        self.last_mousecoords = None
[642]80        self.mouse_callback = None
[671]81        self.original_cursor_id = self.engine.getCursor().getId()       
[642]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       
[671]87        #this is temporary until we can set the native cursor
[642]88        self.resetMouseCursor()
89        self.paused = False
90
[796]91        if model.settings.fife.EnableSound:
[642]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) 
[648]97        self.initHud()
98               
[642]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           
[790]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')))
[642]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()
[693]143        if(key_val == key.C):
144            # C opens and closes the character screen.
145            self.view.hud.toggleCharacterScreen()
[642]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   
[671]213       
[642]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]
[671]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
[794]246                image = os.path.join(settings.fife.DataDirectory,
247                                     settings.parpg.GuiDirectory,
248                                     settings.parpg.CursorDirectory,
249                                     settings.parpg.CursorUp)
[671]250               
251            #right
252            if mouse_x >= screen_width - pixle_edge:
253                direction[0] += 1
254                direction[1] += 1
[794]255                image = os.path.join(settings.fife.DataDirectory,
256                                     settings.parpg.GuiDirectory,
257                                     settings.parpg.CursorDirectory,
258                                     settings.parpg.CursorRight)
[671]259               
260            #down
261            if mouse_y >= screen_height - pixle_edge:
262                direction[0] -= 1
263                direction[1] += 1
[794]264                image = os.path.join(settings.fife.DataDirectory,
265                                     settings.parpg.GuiDirectory,
266                                     settings.parpg.CursorDirectory,
267                                     settings.parpg.CursorDown)
[671]268               
269            #left
270            if mouse_x <= pixle_edge:
271                direction[0] -= 1
272                direction[1] -= 1
[794]273                image = os.path.join(settings.fife.DataDirectory,
274                                     settings.parpg.GuiDirectory,
275                                     settings.parpg.CursorDirectory,
276                                     settings.parpg.CursorLeft)
[642]277           
[794]278            if image is not None and not data_drag.dragging:
[671]279                self.setMouseCursor(image, image)
[642]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()
[671]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     
[642]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.