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

Revision 323, 12.0 KB checked in by eliedebrauwer, 10 years ago (diff)

Ticket #73: Patch by eliedebrauwer, removed obj_hashes from world, and from the maps, added logic to restore PC and NPC behaviour based on the layer ID, added logic to trigger the map load using within the pump(). Load still not functional. comment[s:trac, t:73]

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