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

Revision 310, 10.5 KB checked in by eliedebrauwer, 10 years ago (diff)

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