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

Revision 797, 18.9 KB checked in by aspidites, 8 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
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 fife/clients/parpg/screenshots
136           
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')))
142            self.engine.getRenderBackend().captureScreen(screenshot_file)
143            logger.info("PARPG: Saved: {0}".format(screenshot_file))
144        if(key_val == key.F10):
145            # F10 shows/hides the console
146            self.engine.getGuiManager().getConsole().toggleShowHide()
147        if(key_val == key.C):
148            # C opens and closes the character screen.
149            self.view.hud.toggleCharacterScreen()
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   
217       
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]
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
250                image = os.path.join(settings.fife.DataDirectory,
251                                     settings.parpg.GuiDirectory,
252                                     settings.parpg.CursorDirectory,
253                                     settings.parpg.CursorUp)
254               
255            #right
256            if mouse_x >= screen_width - pixle_edge:
257                direction[0] += 1
258                direction[1] += 1
259                image = os.path.join(settings.fife.DataDirectory,
260                                     settings.parpg.GuiDirectory,
261                                     settings.parpg.CursorDirectory,
262                                     settings.parpg.CursorRight)
263               
264            #down
265            if mouse_y >= screen_height - pixle_edge:
266                direction[0] -= 1
267                direction[1] += 1
268                image = os.path.join(settings.fife.DataDirectory,
269                                     settings.parpg.GuiDirectory,
270                                     settings.parpg.CursorDirectory,
271                                     settings.parpg.CursorDown)
272               
273            #left
274            if mouse_x <= pixle_edge:
275                direction[0] -= 1
276                direction[1] -= 1
277                image = os.path.join(settings.fife.DataDirectory,
278                                     settings.parpg.GuiDirectory,
279                                     settings.parpg.CursorDirectory,
280                                     settings.parpg.CursorLeft)
281           
282            if image is not None and not data_drag.dragging:
283                self.setMouseCursor(image, image)
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."""
320        logger.info(userdata)
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()
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     
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.