source: trunk/game/scripts/engine.py @ 340

Revision 340, 11.8 KB checked in by eliedebrauwer, 10 years ago (diff)

Ticket #66: Patch by Saritor, This patch implements Kaydeth's camera fix as well as a fix for letting us be able to remove the PC entry on individual map files. However the very first map a player starts the game on will need such an entry. fixes[s:trac, t:66]

  • Property svn:eol-style set to native
Line 
1#!/usr/bin/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
18# there should be NO references to FIFE here!
19import pickle
20import sys
21from gamestate import GameState
22from objects import *
23from objects.action import *
24
25
26class Engine:
27    """Engine holds the logic for the game.
28       Since some data (object position and so forth) is held in the
29       fife, and would be pointless to replicate, we hold a instance of
30       the fife view here. This also prevents us from just having a
31       function heavy controller."""
32   
33    def __init__(self, view):
34        """Initialize the instance.
35           @type view: world
36           @param view: A world instance
37           @return: None"""
38        # a World object (the fife stuff, essentially)
39        self.view = view
40        self.map_change = False
41        self.load_saver = False
42        self.savegame = None
43        self.game_state = GameState()
44        self.pc_run = 1
45        self.target_position = None
46        self.target_map_name = None
47        self.target_map_file = None
48
49    def save(self, path, filename):
50        """Writes the saver to a file.
51           @type filename: string
52           @param filename: the name of the file to write to
53           @return: None"""
54        fname = '/'.join([path,filename])
55        try:
56            f = open(fname, 'w')
57        except(IOError):
58            sys.stderr.write("Error: Can't find save game: " + fname + "\n")
59            return
60       
61        # can't pickle SwigPyObjects
62        behaviours = {}
63        behaviour_player = self.game_state.PC.behaviour;
64        self.game_state.PC.behaviour = None;
65       
66        # Backup the behaviours
67        for map_id in self.game_state.objects:
68            behaviours[map_id] = {}
69            for (object_id, npc) in self.game_state.objects[map_id].items():
70                if npc.trueAttr("NPC"):
71                    behaviours[map_id][object_id] = npc.behaviour
72                    npc.behaviour = None;
73       
74        # Pickle it
75        pickle.dump(self.game_state, f)
76        f.close()
77       
78        # Restore behaviours
79        for map_id in behaviours:
80            for (object_id,behaviour) in behaviours[map_id].items():
81                self.game_state.objects[map_id][object_id].behaviour = \
82                    behaviours[map_id][object_id]
83               
84        self.game_state.PC.behaviour = behaviour_player
85
86    def load(self, filename):
87        """Loads a saver from a file.
88           @type filename: string
89           @param filename: the name of the file (including path) to load from
90           @return: None"""
91       
92        # TODO
93        # Remove what we are currently working with
94        print "TODO: load"
95        self.view.active_map.view.clearCameras()
96        self.view.active_map.model.deleteMaps()
97       
98        try:
99            f = open(filename, 'r')
100        except(IOError):
101            sys.stderr.write("Error: Can't find save game file\n")
102            return
103        self.game_state = pickle.load(f)
104        f.close()
105       
106        # Make sure we're on the right map
107        if self.game_state.current_map_file:
108            self.loadMap(self.game_state.current_map_name, \
109                         self.game_state.current_map_file) 
110
111    def createObject (self, layer, attributes, instance):
112        """Create an object and add it to the current map.
113           @type layer: fife.Layer
114           @param layer: FIFE layer object exists in
115           @type attributes: Dictionary
116           @param attributes: Dictionary of all object attributes
117           @type instance: fife.Instance
118           @param instance: FIFE instance corresponding to the object
119           @return: None
120        """
121        # create the extra data
122        extra = {}
123        extra['agent_layer'] = layer
124        extra['engine'] = self
125       
126        obj = createObject(attributes, extra)
127       
128        if obj.trueAttr("PC"):
129            self.addPC(layer, obj, instance)
130        else:
131            self.addObject(layer, obj, instance)
132
133       
134
135    def addPC(self, layer, pc, instance):
136        """Add the PC to the map
137           @type layer: fife.Layer
138           @param layer: FIFE layer object exists in
139           @type pc: PlayerCharacter
140           @param pc: PlayerCharacter object
141           @type instance: fife.Instance
142           @param instance: FIFE instance of PC
143           @return: None
144        """
145        # For now we copy the PC, in the future we will need to copy
146        # PC specifics between the different PC's
147        self.game_state.PC = pc
148        self.game_state.PC.setup()
149
150    def addObject(self, layer, obj, instance):
151        """Adds an object to the map.
152           @type layer: fife.Layer
153           @param layer: FIFE layer object exists in
154           @type obj: GameObject
155           @param obj: corresponding object class
156           @type instance: fife.Instance
157           @param instance: FIFE instance of object
158           @return: None
159        """
160
161        ref = self.game_state.getObjectById(obj.ID, \
162                                            self.game_state.current_map_name) 
163        if ref is None:
164            # no, add it to the game state
165            self.game_state.objects[self.game_state.current_map_name][obj.ID] = obj
166        else:
167            # yes, use the current game state data
168            obj.X = ref.X
169            obj.Y = ref.Y
170            obj.gfx = ref.gfx 
171             
172        if obj.trueAttr("NPC"):
173            # create the agent
174            obj.setup()
175           
176            # create the PC agent
177            obj.start()
178
179    def objectActive(self, ident):
180        """Given the objects ID, pass back the object if it is active,
181           False if it doesn't exist or not displayed
182           @type ident: string
183           @param ident: ID of object
184           @rtype: boolean
185           @return: Status of result (True/False)"""
186        for i in self.game_state.getObjectsFromMap(self.game_state.current_map_name):
187            if (i.ID == ident):
188                # we found a match
189                return i
190        # no match
191        return False
192
193    def getItemActions(self, obj_id):
194        """Given the objects ID, return the text strings and callbacks.
195           @type obj_id: string
196           @param obj_id: ID of object
197           @rtype: list
198           @return: List of text and callbacks"""
199        actions=[]
200        # note: ALWAYS check NPC's first!
201        obj = self.game_state.getObjectById(obj_id, \
202                                            self.game_state.current_map_name)
203       
204        if obj is not None:
205            if obj.trueAttr("NPC"):
206                # keep it simple for now, None to be replaced by callbacks
207                actions.append(["Talk", "Talk", self.initTalk, obj])
208                actions.append(["Attack", "Attack", self.nullFunc, obj])
209            else:
210                actions.append(["Examine", "Examine", \
211                                self.game_state.PC.approach, [obj.X, obj.Y], \
212                                ExamineBoxAction(self, obj.name, obj.text)])
213                # is it a Door?
214                if obj.trueAttr("door"):
215                    actions.append(["Change Map", "Change Map", \
216                       self.game_state.PC.approach, [obj.X, obj.Y], \
217                            ChangeMapAction(self, obj.target_map_name, \
218                                obj.target_map, obj.target_pos)])
219                # is it a container?
220                if obj.trueAttr("container"):
221                    actions.append(["Open", "Open", 
222                                    self.game_state.PC.approach, \
223                                    [obj.X, obj.Y], \
224                                    OpenBoxAction(self, "Box")])
225                # can you pick it up?
226                if obj.trueAttr("carryable"):
227                    actions.append(["Pick Up", "Pick Up", self.nullFunc, obj])
228
229        return actions
230   
231    def nullFunc(self, userdata):
232        """Sample callback for the context menus."""
233        print userdata
234   
235    def initTalk(self, npcInfo):
236        """ Starts the PC talking to an NPC. """
237        # TODO: work more on this when we get NPCData and HeroData straightened
238        # out
239        npc = self.game_state.getObjectById(npcInfo.ID, \
240                                            self.game_state.current_map_name)
241        self.game_state.PC.approach([npc.getLocation().\
242                                     getLayerCoordinates().x, \
243                                     npc.getLocation().\
244                                     getLayerCoordinates().y], \
245                                     TalkAction(self, npc))
246
247    def loadMap(self, map_name, map_file):
248        """Load a new map.
249           @type map_name: string
250           @param map_name: Name of the map to load
251           @type map_file: string
252           @param map_file: Filename of map file to load
253           @return: None"""
254        self.game_state.current_map_file = map_file
255        self.game_state.current_map_name = map_name
256        self.view.loadMap(map_name, str(map_file))
257
258        # If the map has been loaded, we might need to add some
259        # agents to the PC and NPS
260        for map_id in self.game_state.objects:
261            for (object_id, npc) in self.game_state.objects[map_id].items():
262                if npc.trueAttr("NPC") and npc.behaviour == None:
263                    npc.createBehaviour(self.view.active_map.agent_layer)
264
265        # Fix the player behaviour
266        if self.game_state.PC.behaviour == None:
267            self.game_state.PC.createBehaviour(self.view.active_map.agent_layer)
268
269        # create the PC agent
270        self.view.active_map.addPC()
271        self.game_state.PC.start()
272
273    def handleMouseClick(self,position):
274        """Code called when user left clicks the screen.
275           @type position: fife.ScreenPoint
276           @param position: Screen position of click
277           @return: None"""
278        if(self.pc_run == 1):
279            self.game_state.PC.run(position)
280        else:
281            self.game_state.PC.walk(position)
282
283    def changeMap(self, map_name, map_file, target_position):
284        """Registers for a map change on the next pump().
285           @type name_name: String
286           @param map_name: Id of the map to teleport to
287           @type map_file: String
288           @param map_file: Filename of the map to teleport to
289           @type target_position: Tuple
290           @param target_position: Position of PC on target map.
291           @return None"""
292        # set the parameters for the map change if moving to a new map
293        if map_name != self.game_state.current_map_name:
294            self.target_map_name = map_name
295            self.target_map_file = map_file
296            self.target_position = target_position
297            # issue the map change
298            self.map_change = True
299        else:
300            #set the player position on the current map
301            self.view.teleport(target_position)
302
303    def handleCommands(self):
304        if self.map_change:
305            self.loadMap(self.target_map_name, self.target_map_file)
306            self.view.teleport(self.target_position)
307            self.map_change = False
308       
309        if self.load_saver:
310            self.load(self.savegame)
311            self.load_saver = False
312
313    def pump(self):
314        """Main loop in the engine."""
315        self.handleCommands()
Note: See TracBrowser for help on using the repository browser.