Package scripts :: Package objects :: Script actors
[hide private]
[frames] | no frames]

Source Code for Script scripts.objects.actors

  1  #!/usr/bin/env 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  from random import randrange 
 19   
 20   
 21  from fife import fife 
 22   
 23  from base import GameObject, Living, Scriptable, CharStats 
 24  from composed import CarryableItem 
 25  from scripts.inventory import Inventory 
 26  from scripts.common.utils import loadSettings 
 27   
 28  """All actors go here. Concrete classes only.""" 
 29   
 30  __all__ = ["PlayerCharacter", "NonPlayerCharacter",] 
 31   
 32  Settings = loadSettings() 
 33   
 34  _AGENT_STATE_NONE, _AGENT_STATE_IDLE, _AGENT_STATE_APPROACH, _AGENT_STATE_RUN, _AGENT_STATE_WANDER, _AGENT_STATE_TALK = xrange(6) 
 35   
36 -class ActorBehaviour (fife.InstanceActionListener):
37 """Fife agent listener"""
38 - def __init__(self, layer):
39 fife.InstanceActionListener.__init__(self) 40 self.layer = layer 41 self.agent = None 42 self.state = None 43 self.speed = 0 44 self.idle_counter = 1
45
46 - def attachToLayer(self, agent_ID):
47 """Attaches to a certain layer 48 @type agent_ID: String 49 @param agent_ID: ID of the layer to attach to. 50 @return: None""" 51 self.agent = self.layer.getInstance(agent_ID) 52 self.agent.addActionListener(self) 53 self.state = _AGENT_STATE_NONE 54 # TODO: rework/improve 55 self.speed = Settings.get("PARPG", "PCSpeed")-1
56
57 - def getX(self):
58 """Get the NPC's x position on the map. 59 @rtype: integer" 60 @return: the x coordinate of the NPC's location""" 61 return self.agent.getLocation().getLayerCoordinates().x
62
63 - def getY(self):
64 """Get the NPC's y position on the map. 65 @rtype: integer 66 @return: the y coordinate of the NPC's location""" 67 return self.agent.getLocation().getLayerCoordinates().y
68
69 - def onNewMap(self, layer):
70 """Sets the agent onto the new layer.""" 71 if self.agent is not None: 72 self.agent.removeActionListener(self) 73 74 self.agent = layer.getInstance(self.parent.ID) 75 self.agent.addActionListener(self) 76 self.state = _AGENT_STATE_NONE 77 self.idle_counter = 1
78
79 - def idle(self):
80 """@return: None""" 81 self.state = _AGENT_STATE_IDLE 82 self.agent.act('stand', self.agent.getFacingLocation())
83
84 - def onInstanceActionFinished(self, instance, action):
85 pass
86
87 -class PCBehaviour (ActorBehaviour):
88 - def __init__(self, parent = None, layer = None):
89 super(PCBehaviour, self).__init__(layer) 90 self.parent = parent 91 self.idle_counter = 1 92 # TODO: rework/improve 93 self.speed = Settings.get("PARPG", "PCSpeed") 94 self.nextAction = None 95 self.agent = None
96
97 - def onInstanceActionFinished(self, instance, action):
98 """@type instance: ??? 99 @param instance: ??? 100 @type action: ??? 101 @param action: ??? 102 @return: None""" 103 # First we reset the next behavior 104 act = self.nextAction 105 self.nextAction = None 106 self.idle() 107 108 if act: 109 act.execute() 110 111 if(action.getId() != 'stand'): 112 self.idle_counter = 1 113 else: 114 self.idle_counter += 1
115 116
117 -class NPCBehaviour(ActorBehaviour):
118 - def __init__(self, Parent = None, Layer = None):
119 super(NPCBehaviour, self).__init__(Layer) 120 121 self.parent = Parent 122 self.state = _AGENT_STATE_NONE 123 self.pc = None 124 self.target_loc = None 125 self.nextAction = None 126 127 # hard code these for now 128 self.distRange = (2, 4) 129 # these are parameters to lower the rate of wandering 130 # wander rate is the number of "IDLEs" before a wander step 131 # this could be set for individual NPCs at load time 132 # or thrown out altogether. 133 self.wanderCounter = 0 134 self.wanderRate = 9
135
136 - def getTargetLocation(self):
137 """@rtype: fife.Location 138 @return: NPC's position""" 139 x = self.getX() 140 y = self.getY() 141 if self.state == _AGENT_STATE_WANDER: 142 """ Random Target Location """ 143 l = [0, 0] 144 for i in range(len(l)): 145 sign = randrange(0, 2) 146 dist = randrange(self.distRange[0], self.distRange[1]) 147 if sign == 0: 148 dist *= -1 149 l[i] = dist 150 x += l[0] 151 y += l[1] 152 # Random walk is 153 # rl = randint(-1, 1);ud = randint(-1, 1);x += rl;y += ud 154 l = fife.Location(self.agent.getLocation()) 155 l.setLayerCoordinates(fife.ModelCoordinate(x, y)) 156 return l
157
158 - def onInstanceActionFinished(self, instance, action):
159 """What the NPC does when it has finished an action. 160 Called by the engine and required for InstanceActionListeners. 161 @type instance: fife.Instance 162 @param instance: self.agent (the NPC listener is listening for this 163 instance) 164 @type action: ??? 165 @param action: ??? 166 @return: None""" 167 if self.state == _AGENT_STATE_WANDER: 168 self.target_loc = self.getTargetLocation() 169 self.idle()
170 171
172 - def idle(self):
173 """Controls the NPC when it is idling. Different actions 174 based on the NPC's state. 175 @return: None""" 176 if self.state == _AGENT_STATE_NONE: 177 self.state = _AGENT_STATE_IDLE 178 self.agent.act('stand', self.agent.getFacingLocation()) 179 elif self.state == _AGENT_STATE_IDLE: 180 if self.wanderCounter > self.wanderRate: 181 self.wanderCounter = 0 182 self.state = _AGENT_STATE_WANDER 183 else: 184 self.wanderCounter += 1 185 self.state = _AGENT_STATE_NONE 186 187 self.target_loc = self.getTargetLocation() 188 self.agent.act('stand', self.agent.getFacingLocation()) 189 elif self.state == _AGENT_STATE_WANDER: 190 self.parent.wander(self.target_loc) 191 self.state = _AGENT_STATE_NONE 192 elif self.state == _AGENT_STATE_TALK: 193 self.agent.act('stand', self.pc.getLocation())
194
195 -class CharacterBase(GameObject, Living, CharStats):
196 """Base class for Characters"""
197 - def __init__(self, ID, agent_layer = None, inventory = None, 198 text = "", **kwargs):
199 GameObject.__init__( self, ID, text = text, **kwargs ) 200 Living.__init__( self, **kwargs ) 201 CharStats.__init__( self, **kwargs ) 202 203 self.behaviour = None 204 205 if inventory == None: 206 self.inventory = Inventory() 207 else: 208 self.inventory = inventory 209 210 self.state = _AGENT_STATE_NONE 211 self.layer_id = agent_layer.getId() 212 self.createBehaviour(agent_layer)
213
214 - def createBehaviour(self, layer):
215 """Creates the behaviour for this actor. 216 @return: None""" 217 pass
218
219 - def setup(self):
220 """@return: None""" 221 self.behaviour.attachToLayer(self.ID)
222
223 - def start(self):
224 """@return: None""" 225 self.behaviour.idle()
226
227 - def teleport(self, location):
228 """Teleports a Character instantly to the given location. 229 @type location: fife.Location 230 @param location: Target coordinates for Character. 231 @return: None""" 232 self.state = _AGENT_STATE_IDLE 233 self.behaviour.nextAction = None 234 self.behaviour.agent.setLocation(location)
235
236 - def give (self, item, actor):
237 """Gives the specified item to the different actor. Raises an exception if the item was invalid or not found 238 @type item: Carryable 239 @param item: The item object to give 240 @param actor: Person to give item to""" 241 if item == None: 242 raise ValueError("I don't have %s" % item.name) 243 self.inventory.takeItem(item) 244 actor.inventory.placeItem(item)
245
246 - def hasItem(self, item_type):
247 """Returns wether an item is present in the players inventory or not 248 @param item_type: ID of the item 249 @type item_type: str 250 @return: True when the item is present, False when not""" 251 return self.inventory.findItem(item_type = item_type)
252
253 - def itemCount(self, item_type = ""):
254 """Returns number of all items or items specified by item_type 255 the player has. 256 @param item_type: ID of the item, can be empty 257 @type item_type: str 258 @return: Number of items""" 259 return self.inventory.count(item_type)
260
261 - def getLocation(self):
262 """Get the NPC's position as a fife.Location object. Basically a 263 wrapper. 264 @rtype: fife.Location 265 @return: the location of the NPC""" 266 return self.behaviour.agent.getLocation()
267
268 - def run(self, location):
269 """Makes the PC run to a certain location 270 @type location: fife.ScreenPoint 271 @param location: Screen position to run to. 272 @return: None""" 273 self.state = _AGENT_STATE_RUN 274 self.behaviour.nextAction = None 275 self.behaviour.agent.move('run', location, self.behaviour.speed+1)
276
277 - def walk(self, location):
278 """Makes the PC walk to a certain location. 279 @type location: fife.ScreenPoint 280 @param location: Screen position to walk to. 281 @return: None""" 282 self.state = _AGENT_STATE_RUN 283 self.behaviour.nextAction = None 284 self.behaviour.agent.move('walk', location, self.behaviour.speed-1)
285
286 - def getStateForSaving(self):
287 """Returns state for saving 288 """ 289 ret_dict = GameObject.getStateForSaving(self) 290 ret_dict["Inventory"] = self.inventory.serializeInventory() 291 return ret_dict
292
293 - def _getCoords(self):
294 """Get-er property function""" 295 return (self.getLocation().getMapCoordinates().x, 296 self.getLocation().getMapCoordinates().y)
297
298 - def _setCoords(self, coords):
299 """Set-er property function""" 300 map_coords = self.getLocation().getMapCoordinates() 301 map_coords.X, map_coords.Y = float(coords[0]), float (coords[1]) 302 self.teleport(map_coords)
303 304 coords = property (_getCoords, _setCoords, 305 doc = "Property allowing you to get and set the object's \ 306 coordinates via tuples")
307
308 -class PlayerCharacter (CharacterBase):
309 """PC class"""
310 - def __init__ (self, ID, agent_layer = None, inventory = None, 311 text = "Its you. Who would've thought that?", **kwargs):
312 if inventory == None: 313 inventory = Inventory() 314 inventory.placeItem(CarryableItem(ID=456, name="Dagger123")) 315 inventory.placeItem(CarryableItem(ID=555, name="Beer")) 316 inventory.placeItem(CarryableItem(ID = 616, 317 name = "Pamphlet", 318 image = "/gui/inv_images/inv_pamphlet.png")) 319 CharacterBase.__init__(self, ID, agent_layer, inventory, text, **kwargs) 320 self.people_i_know = set() 321 self.attributes.append("PC")
322
323 - def getStateForSaving(self):
324 """Returns state for saving 325 """ 326 ret_dict = super(PlayerCharacter, self).getStateForSaving() 327 ret_dict["PeopleKnown"] = self.people_i_know 328 return ret_dict
329
330 - def meet(self, npc):
331 """Record that the PC has met a certain NPC 332 @type npc: str 333 @param npc: The NPC's name or id""" 334 if npc in self.people_i_know: 335 # we could raise an error here, but should probably be a warn 336 # raise RuntimeError("I already know %s" % npc) 337 return 338 self.people_i_know.add(npc)
339
340 - def met(self, npc):
341 """Indicate whether the PC has met this npc before 342 @type npc: str 343 @param npc: The NPC's name or id 344 @return: None""" 345 return npc in self.people_i_know
346
347 - def createBehaviour(self, layer):
348 """Creates the behaviour for this actor. 349 @return: None""" 350 self.behaviour = PCBehaviour(self, layer)
351
352 - def approach(self, location, action = None):
353 """Approaches a location and then perform an action (if set). 354 @type loc: fife.Location 355 @param loc: the location to approach 356 @type action: Action 357 @param action: The action to schedule for execution after the approach. 358 @return: None""" 359 self.state = _AGENT_STATE_APPROACH 360 self.behaviour.nextAction = action 361 boxLocation = tuple([int(float(i)) for i in location]) 362 l = fife.Location(self.behaviour.agent.getLocation()) 363 l.setLayerCoordinates(fife.ModelCoordinate(*boxLocation)) 364 self.behaviour.agent.move('run', l, self.behaviour.speed+1)
365
366 -class NonPlayerCharacter(CharacterBase, Scriptable):
367 """NPC class"""
368 - def __init__(self, ID, agent_layer=None, name='NPC', \ 369 text = 'A nonplayer character', inventory = None, 370 real_name = 'NPC', dialogue = None, **kwargs):
371 # init game object 372 CharacterBase.__init__(self, ID, agent_layer = agent_layer, 373 inventory = inventory, name=name, 374 real_name=real_name, text = text, **kwargs) 375 Scriptable.__init__(self, **kwargs) 376 377 self.attributes.append("NPC") 378 self.dialogue = dialogue
379
380 - def prepareStateForSaving(self, state):
381 """Prepares state for saving 382 @type state: dictionary 383 @param state: State of the object 384 """ 385 CharacterBase.prepareStateForSaving(self, state) 386 del state["behaviour"]
387
388 - def getStateForSaving(self):
389 """Returns state for saving 390 """ 391 ret_dict = CharacterBase.getStateForSaving(self) 392 ret_dict["Lives"] = self.lives 393 ret_dict["State"] = self.behaviour.state 394 return ret_dict
395
396 - def createBehaviour(self, layer):
397 """Creates the behaviour for this actor. 398 @return None """ 399 self.behaviour = NPCBehaviour(self, layer)
400
401 - def wander(self, location):
402 """Nice slow movement for random walking. 403 @type location: fife.Location 404 @param location: Where the NPC will walk to. 405 @return: None""" 406 self.behaviour.agent.move('walk', location, self.behaviour.speed-1)
407
408 - def talk(self, pc):
409 """Makes the NPC ready to talk to the PC 410 @return: None""" 411 self.behaviour.state = _AGENT_STATE_TALK 412 self.behaviour.pc = pc.behaviour.agent 413 self.behaviour.idle()
414