source: trunk/PARPG/scripts/engine.py @ 189

Revision 189, 11.5 KB checked in by bretzel_parpg, 10 years ago (diff)

The examine function now works. Added a 'desc' attribute to all GameObject?'s that contains the detailed
description

  • 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 agents.hero import Hero
21from agents.npc import NPC
22from saver import Saver
23from gamestate import GameState
24from objects import *
25from objectLoader import ObjectXMLParser
26
27# design note:
28# there is a map file that FIFE reads. We use that file for half the map
29# format because the map editor in FIFE uses it, and secondly because it
30# save us writing a bunch of new code.
31# However, the objects and characters on a map are liable to change
32# whilst the game is being run, so when we change the map, we need to
33# to grab the objects and npc data EITHER from the engine state, or grab
34# from another file if in their initial state
35# This other file has the name AAA_objects.xml where AAA.xml is the name
36# of the original mapfile.
37
38class Engine:
39    """Engine holds the logic for the game.
40       Since some data (object position and so forth) is held in the
41       fife, and would be pointless to replicate, we hold a instance of
42       the fife view here. This also prevents us from just having a
43       function heavy controller."""
44   
45    def __init__(self, view):
46        """Initialise the instance.
47           @type view: world
48           @param view: A world instance
49           @return: None"""
50        # a World object (the fife stuff, essentially)
51        self.view = view
52        self.doors = {}
53        self.npcs = []
54        self.PC = None
55        self.mapchange = False
56        self.gameState = GameState()
57
58    def reset(self):
59        """Clears the data on a map reload so we don't have objects/npcs from
60           other maps hanging around.
61           @return: None"""
62        self.PC = None
63        self.npcs = []
64        self.doors = {}
65
66    def save(self, path, filename):
67        """Writes the saver to a file.
68           @type filename: string
69           @param filename: the name of the file to write to
70           @return: None"""
71        self.updateGameState()
72        fname = '/'.join([path,filename])
73        try:
74            f = open(fname, 'w')
75        except(IOError):
76            sys.stderr.write("Error: Can't find save game: " + fname + "\n")
77            return
78        pickle.dump(self.gameState, f)
79        f.close()
80
81    def load(self, path, filename):
82        """Loads a saver from a file.
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.currentMap)
96           
97    def updateGameState(self):
98        """Stores the current pc and npcs positons in the game state."""
99        # save the PC position
100        self.gameState.PC.posx = self.PC.getX()
101        self.gameState.PC.posy = self.PC.getY()
102        #save npc positions
103        for i in self.npcs:
104            if str(i.id) in self.gameState.objects:
105                self.gameState.getObjectById(str(i.id)).posx = i.getX()
106                self.gameState.getObjectById(str(i.id)).posy = i.getY()
107
108    def loadObjects(self, filename):
109        """Load objects from the XML file
110           Returns True if it worked, False otherwise.
111           @type filename: string
112           @param filename: The XML file to read.
113           @rtype: boolean
114           @return: Status of result (True/False)"""
115        try:
116            objects_file = open(filename, 'rt')
117        except(IOError):
118            sys.stderr.write("Error: Can't find objects file\n")
119            return False
120        # now open and read the XML file
121        other_handler = ObjectXMLParser()
122        other_handler.getObjects(objects_file, self.view.agent_layer)
123        objects_file.close()
124           
125        # now add to the map and the engine
126        self.addGameObjs(other_handler.local_info)
127        return True
128
129    def addGameObjs(self, objList):
130        """Add all found game objects to the world
131           @type objList: list
132           @param objList: a list of the objects found in the xml file
133           @return: None"""
134
135        self.addPC(*[obj for obj in objList if obj.trueAttr("PC")])
136        self.addNPCs([obj for obj in objList if obj.trueAttr("NPC")])
137        self.addObjects([obj for obj in objList if (not obj.trueAttr("PC") and not obj.trueAttr("NPC"))])
138
139    def addPC(self,pc):
140        """Add the PC to the world
141           @type pc: list
142           @param pc: List of data for PC attributes
143           @return: None"""
144        # add to view data   
145        self.view.addObject(pc.X, pc.X, pc.gfx, pc.ID)
146       
147        # sync with game data
148        if not self.gameState.PC:
149            self.gameState.PC = pc
150           
151        self.gameState.PC.setup()
152        self.view.addPC(self.gameState.PC.behaviour.agent)
153           
154        # create the PC agent
155        self.gameState.PC.start()
156
157    def addObjects(self,objects):
158        """Add all of the objects we found into the fife map
159           and into our class. An NPC is just an object to FIFE
160           @type objects: list
161           @param objects: List of objects to add
162           @return: None"""
163        for i in objects:
164            # already in game data?
165            ref = self.gameState.getObjectById(i.ID)
166            if ref is None:
167                # no, add it to the game state
168                i.map_id = self.gameState.currentMap
169                self.gameState.objects[i.ID] = i
170            else:
171                # yes, use the current game state data
172                i.X = ref.X
173                i.Y = ref.Y
174                i.gfx = ref.gfx       
175           
176            self.view.addObject(i.X, i.Y, i.gfx, i.ID)
177
178    def addNPCs(self,npcs):
179        """Add all of the NPCs we found into the fife map to FIFE.
180           @type npcs: list
181           @param npcs: List of NPC's to add
182           @return: None"""
183        for i in npcs:
184            # already in the game data?
185            ref = self.gameState.getObjectById(i.ID) 
186            if ref is None:
187                # no, add it to the game state
188                i.map_id = self.gameState.currentMap
189                self.gameState.objects[i.ID] = i
190            else:
191                # yes, use the current game state data
192                i.X = ref.X
193                i.Y = ref.Y
194                i.gfx = ref.gfx 
195               
196            # add it to the view
197            self.view.addObject(i.X, i.Y, i.gfx, i.ID)         
198           
199            # create the agent
200            i.setup()
201           
202            # create the PC agent
203            i.start()
204
205    def addDoors(self, doors):
206        """Add all the doors to the map as well.
207           As an object they will have already been added.
208           @type doors: list
209           @param doors: List of doors
210           @return: None"""
211        for i in doors:
212            self.doors[str(i.id)] = MapDoor(i.id, i.destmap, (i.destx, i.desty))
213
214    def objectActive(self, ident):
215        """Given the objects ID, pass back the object if it is active,
216           False if it doesn't exist or not displayed
217           @type ident: string
218           @param ident: ID of object
219           @rtype: boolean
220           @return: Status of result (True/False)"""
221        for i in self.gameState.getObjectsFromMap(self.gameState.currentMap):
222            if (i.ID == ident):
223                # we found a match
224                return i         
225        # no match
226        return False
227
228    def getItemActions(self, obj_id):
229        """Given the objects ID, return the text strings and callbacks.
230           @type obj_id: string
231           @param obj_id: ID of object
232           @rtype: list
233           @return: List of text and callbacks"""
234        actions=[]
235        # note: ALWAYS check NPC's first!
236        obj = self.gameState.getObjectById(obj_id)
237       
238        if obj:
239            if obj.trueAttr("NPC"):
240                # keep it simple for now, None to be replaced by callbacks
241                actions.append(["Talk", "Talk", self.initTalk, obj])
242                actions.append(["Attack", "Attack", self.nullFunc, obj]) 
243            elif obj.trueAttr("Door"):
244                #actions.append(["Change Map", "Change Map", \
245                #       self.gameState.PC.approachDoor, [obj.X, obj.Y], \
246                #        self.doors[str(i.ID)].map, [i.destx, i.desty]])
247                pass
248            else:
249                actions.append(["Examine", "Examine", self.view.createExamineBox, obj.name, obj.desc])
250                # is it a container?
251                if obj.trueAttr("container"):
252                    actions.append(["Open", "Open", self.gameState.PC.approachBox, [obj.X, obj.Y]])
253                # can you pick it up?
254                if obj.trueAttr("carryable"):
255                    actions.append(["Pick Up", "Pick Up", self.nullFunc, obj])       
256                   
257        return actions
258   
259    def nullFunc(self, userdata):
260        """Sample callback for the context menus."""
261        print userdata
262   
263    def initTalk(self, npcInfo):
264        """ Starts the PC talking to an NPC. """
265        # TODO: work more on this when we get NPCData and HeroData straightened
266        # out
267        npc = self.gameState.getObjectById(npcInfo.ID)
268        if npc:
269            npc.talk()
270        self.gameState.PC.approachNPC(npc.getLocation())
271
272    def loadMap(self, map_file):
273        """Load a new map. TODO: needs some error checking
274           @type map_file: string
275           @param map_file: Name of map file to load
276           @return: None"""
277        # then we let FIFE load the rest of the map
278        self.view.load(str(map_file))
279        # then we update FIFE with the PC, NPC and object details
280        self.reset()
281        self.gameState.currentMap = map_file
282        self.loadObjects(map_file[:-4] + "_objects.xml")
283
284    def handleMouseClick(self,position):
285        """Code called when user left clicks the screen.
286           @type position: fife.ScreenPoint
287           @param position: Screen position of click
288           @return: None"""
289        self.gameState.PC.run(position)
290       
291    def changeMap(self, map, targetPosition):
292        """Registers for a mapchange on the next pump().
293           @type map: ???
294           @param map: Name of the target map.
295           @type targetPosition: ???
296           @param targetPosition: Position of PC on target map.
297           @return: None"""
298        # save the postions
299        self.updateGameState()
300        # set the PC position
301        self.gameState.PC.posx = targetPosition[0]
302        self.gameState.PC.posy = targetPosition[1]
303        # set the parameters for the mapchange
304        self.targetMap = map
305        # issue the mapchange
306        self.mapchange = True
307
308    def handleCommands(self):
309        if self.mapchange:
310            self.loadMap(self.targetMap)
311            self.mapchange = False
312
313    def pump(self):
314        """Main loop in the engine."""
315        self.handleCommands()
316
Note: See TracBrowser for help on using the repository browser.