source: trunk/game/scripts/objects/actors.py @ 627

Revision 627, 14.5 KB checked in by beliar, 9 years ago (diff)

Patch by Beliar.

  • Added SettingsVersion? to settings. This contains the version (currently a date of the last change) of the settings. The version will be checked with the version in settings-dist.xml and if the versions don't match the settings.xml file will be renamed to settings-old.xml. The version string has to be set manually when editing settings-dist.xml.
  • Property svn:eol-style set to native
Line 
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
18from random import randrange
19
20
21from fife import fife
22
23from base import GameObject, Living, Scriptable, CharStats
24from composed import CarryableItem
25from scripts.inventory import Inventory
26from scripts.common.utils import loadSettings
27
28"""All actors go here. Concrete classes only."""
29
30__all__ = ["PlayerCharacter", "NonPlayerCharacter",]
31
32Settings = loadSettings()
33
34_AGENT_STATE_NONE, _AGENT_STATE_IDLE, _AGENT_STATE_APPROACH, _AGENT_STATE_RUN, _AGENT_STATE_WANDER, _AGENT_STATE_TALK = xrange(6)
35
36class 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
87class 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
117class 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 this for now
128        self.distRange = (2, 4)
129       
130    def getTargetLocation(self):
131        """@rtype: fife.Location
132           @return: NPC's position"""
133        x = self.getX()
134        y = self.getY()
135        if self.state == _AGENT_STATE_WANDER:
136            """ Random Target Location """
137            l = [0, 0]
138            for i in range(len(l)):
139                sign = randrange(0, 2)
140                dist = randrange(self.distRange[0], self.distRange[1])
141                if sign == 0:
142                    dist *= -1
143                l[i] = dist
144            x += l[0]
145            y += l[1]
146            # Random walk is
147            # rl = randint(-1, 1);ud = randint(-1, 1);x += rl;y += ud
148        l = fife.Location(self.agent.getLocation())
149        l.setLayerCoordinates(fife.ModelCoordinate(x, y))
150        return l
151
152    def onInstanceActionFinished(self, instance, action):
153        """What the NPC does when it has finished an action.
154           Called by the engine and required for InstanceActionListeners.
155           @type instance: fife.Instance
156           @param instance: self.agent (the NPC listener is listening for this
157                                        instance)
158           @type action: ???
159           @param action: ???
160           @return: None"""
161        if self.state == _AGENT_STATE_WANDER:
162            self.target_loc = self.getTargetLocation()
163        self.idle()
164       
165   
166    def idle(self):
167        """Controls the NPC when it is idling. Different actions
168           based on the NPC's state.
169           @return: None"""
170        if self.state == _AGENT_STATE_NONE:
171            self.state = _AGENT_STATE_IDLE
172            self.agent.act('stand', self.agent.getFacingLocation())
173        elif self.state == _AGENT_STATE_IDLE:
174            self.target_loc = self.getTargetLocation()
175            self.state = _AGENT_STATE_WANDER
176            self.agent.act('stand', self.agent.getFacingLocation())
177        elif self.state == _AGENT_STATE_WANDER:
178            self.parent.wander(self.target_loc)
179            self.state = _AGENT_STATE_NONE
180        elif self.state == _AGENT_STATE_TALK:
181            self.agent.act('stand', self.pc.getLocation())
182           
183class CharacterBase(GameObject, Living, CharStats):
184    """Base class for Characters"""
185    def __init__(self, ID, agent_layer = None, inventory = None, 
186                 text = "", **kwargs):
187        GameObject.__init__( self, ID, text = text, **kwargs )
188        Living.__init__( self, **kwargs )
189        CharStats.__init__( self, **kwargs )
190       
191        self.behaviour = None
192       
193        if inventory == None:
194            self.inventory = Inventory()
195        else:
196            self.inventory = inventory
197
198        self.state = _AGENT_STATE_NONE
199        self.layer_id = agent_layer.getId()
200        self.createBehaviour(agent_layer)
201   
202    def createBehaviour(self, layer):
203        """Creates the behaviour for this actor.
204           @return: None"""
205        pass
206   
207    def setup(self):
208        """@return: None"""
209        self.behaviour.attachToLayer(self.ID)
210
211    def start(self):
212        """@return: None"""
213        self.behaviour.idle()
214
215    def teleport(self, location):
216        """Teleports a Character instantly to the given location.
217           @type location: fife.Location
218           @param location: Target coordinates for Character.
219           @return: None"""
220        self.state = _AGENT_STATE_IDLE
221        self.behaviour.nextAction = None 
222        self.behaviour.agent.setLocation(location)
223
224    def give (self, item, actor):
225        """Gives the specified item to the different actor. Raises an exception if the item was invalid or not found
226           @type item: Carryable
227           @param item: The item object to give
228           @param actor: Person to give item to"""
229        if item == None: 
230            raise ValueError("I don't have %s" % item.name)
231        self.inventory.takeItem(item)
232        actor.inventory.placeItem(item)           
233       
234    def hasItem(self, item_type):
235        """Returns wether an item is present in the players inventory or not
236        @param item_type: ID of the item
237        @type item_type: str
238        @return: True when the item is present, False when not"""
239        return self.inventory.findItem(item_type = item_type)
240
241    def itemCount(self, item_type = ""):
242        """Returns number of all items or items specified by item_type
243        the player has.
244        @param item_type: ID of the item, can be empty
245        @type item_type: str
246        @return: Number of items"""
247        return self.inventory.count(item_type)
248
249    def getLocation(self):
250        """Get the NPC's position as a fife.Location object. Basically a
251           wrapper.
252           @rtype: fife.Location
253           @return: the location of the NPC"""
254        return self.behaviour.agent.getLocation()
255   
256    def run(self, location):
257        """Makes the PC run to a certain location
258           @type location: fife.ScreenPoint
259           @param location: Screen position to run to.
260           @return: None"""
261        self.state = _AGENT_STATE_RUN
262        self.behaviour.nextAction = None
263        self.behaviour.agent.move('run', location, self.behaviour.speed+1)
264
265    def walk(self, location):
266        """Makes the PC walk to a certain location.
267           @type location: fife.ScreenPoint
268           @param location: Screen position to walk to.
269           @return: None"""
270        self.state = _AGENT_STATE_RUN
271        self.behaviour.nextAction = None 
272        self.behaviour.agent.move('walk', location, self.behaviour.speed-1)
273
274    def getStateForSaving(self):
275        """Returns state for saving
276        """
277        ret_dict = GameObject.getStateForSaving(self)
278        ret_dict["Inventory"] = self.inventory.serializeInventory()
279        return ret_dict
280
281    def _getCoords(self):
282        """Get-er property function"""
283        return (self.getLocation().getMapCoordinates().x, 
284                self.getLocation().getMapCoordinates().y)
285   
286    def _setCoords(self, coords):
287        """Set-er property function"""
288        map_coords = self.getLocation().getMapCoordinates()
289        map_coords.X, map_coords.Y = float(coords[0]), float (coords[1])
290        self.teleport(map_coords)
291   
292    coords = property (_getCoords, _setCoords, 
293        doc = "Property allowing you to get and set the object's \
294                coordinates via tuples")
295           
296class PlayerCharacter (CharacterBase):
297    """PC class"""
298    def __init__ (self, ID, agent_layer = None, inventory = None, 
299                  text = "Its you. Who would've thought that?", **kwargs):
300        if inventory == None:
301            inventory = Inventory()
302            inventory.placeItem(CarryableItem(ID=456, name="Dagger123"))
303            inventory.placeItem(CarryableItem(ID=555, name="Beer"))
304            inventory.placeItem(CarryableItem(ID = 616,
305                                    name = "Pamphlet",
306                                    image = "/gui/inv_images/inv_pamphlet.png"))
307        CharacterBase.__init__(self, ID, agent_layer, inventory, text, **kwargs)
308        self.people_i_know = set()
309        self.attributes.append("PC")
310 
311    def getStateForSaving(self):
312        """Returns state for saving
313        """
314        ret_dict = super(PlayerCharacter, self).getStateForSaving()
315        ret_dict["PeopleKnown"] = self.people_i_know
316        return ret_dict
317   
318    def meet(self, npc):
319        """Record that the PC has met a certain NPC
320           @type npc: str
321           @param npc: The NPC's name or id"""
322        if npc in self.people_i_know:
323            # we could raise an error here, but should probably be a warn
324            # raise RuntimeError("I already know %s" % npc)
325            return
326        self.people_i_know.add(npc)
327
328    def met(self, npc):
329        """Indicate whether the PC has met this npc before
330           @type npc: str
331           @param npc: The NPC's name or id
332           @return: None"""
333        return npc in self.people_i_know
334
335    def createBehaviour(self, layer):
336        """Creates the behaviour for this actor.
337           @return: None"""
338        self.behaviour = PCBehaviour(self, layer)
339 
340    def approach(self, location, action = None):
341        """Approaches a location and then perform an action (if set).
342           @type loc: fife.Location
343           @param loc: the location to approach
344           @type action: Action
345           @param action: The action to schedule for execution after the approach.
346           @return: None"""
347        self.state = _AGENT_STATE_APPROACH
348        self.behaviour.nextAction = action
349        boxLocation = tuple([int(float(i)) for i in location])
350        l = fife.Location(self.behaviour.agent.getLocation())
351        l.setLayerCoordinates(fife.ModelCoordinate(*boxLocation))
352        self.behaviour.agent.move('run', l, self.behaviour.speed+1)
353   
354class NonPlayerCharacter(CharacterBase, Scriptable):
355    """NPC class"""
356    def __init__(self, ID, agent_layer=None, name='NPC', \
357                 text = 'A nonplayer character', inventory = None, 
358                 real_name = 'NPC', dialogue = None, **kwargs):
359        # init game object
360        CharacterBase.__init__(self, ID, agent_layer = agent_layer, 
361                               inventory = inventory, name=name, 
362                               real_name=real_name, text = text, **kwargs)
363        Scriptable.__init__(self, **kwargs)
364
365        self.attributes.append("NPC")
366        self.dialogue = dialogue
367
368    def prepareStateForSaving(self, state):
369        """Prepares state for saving
370        @type state: dictionary
371        @param state: State of the object 
372        """
373        CharacterBase.prepareStateForSaving(self, state)
374        del state["behaviour"]
375
376    def getStateForSaving(self):
377        """Returns state for saving
378        """
379        ret_dict = CharacterBase.getStateForSaving(self)
380        ret_dict["Lives"] = self.lives
381        ret_dict["State"] = self.behaviour.state
382        return ret_dict
383
384    def createBehaviour(self, layer):
385        """Creates the behaviour for this actor.
386           @return None """
387        self.behaviour = NPCBehaviour(self, layer)
388
389    def wander(self, location):
390        """Nice slow movement for random walking.
391           @type location: fife.Location
392           @param location: Where the NPC will walk to.
393           @return: None"""
394        self.behaviour.agent.move('walk', location, self.behaviour.speed-1)
395
396    def talk(self, pc):
397        """Makes the NPC ready to talk to the PC
398           @return: None"""
399        self.behaviour.state = _AGENT_STATE_TALK
400        self.behaviour.pc = pc.behaviour.agent
401        self.behaviour.idle()
Note: See TracBrowser for help on using the repository browser.