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

Revision 736, 17.9 KB checked in by aspidites, 9 years ago (diff)

Patch by Aspidites:

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