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

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

Ticket #103: Patch by Elie De Brauwer, Added possibility to make the player character run, this can be toggled with the run console command, comment[s:trac, t:103]

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