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

Revision 319, 11.5 KB checked in by eliedebrauwer, 10 years ago (diff)

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