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

Revision 313, 10.9 KB checked in by Kaydeth_parpg, 10 years ago (diff)

Ticket #2: Patch by Kaydeth. Updated the rest of the classes in the scripts directory to correct some obvious discrepancies with our coding standard. Still need to go through the common and objects sub directories. comment[s:trac, t:2]

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