source: branches/active/character_customization/game/parpg/objects/actors.py @ 736

Revision 736, 15.5 KB checked in by aspidites, 8 years ago (diff)

Patch by Aspidites:

  • renamed scripts package to parpg
  • renamed parpg module to application
  • removed packaging and other related files (kept locally for reference, will reintroduce similar scripts to resolve bug #275
  • updated all import statements to respect changes above
  • 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 parpg.inventory import Inventory
26from parpg.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 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           
195class CharacterBase(GameObject, CharStats, Living):
196    """Base class for Characters"""
197    def __init__(self, ID, agent_layer=None, inventory=None, text="",
198                 primary_stats=None, secondary_stats=None, **kwargs):
199        GameObject.__init__(self, ID, text=text, **kwargs)
200        CharStats.__init__(self, **kwargs)
201        Living.__init__(self, **kwargs)
202        self.statistics = {}
203        if primary_stats is not None:
204            for primary_stat in primary_stats:
205                name = primary_stat.name
206                self.statistics[name] = primary_stat
207        if secondary_stats is not None:
208            for secondary_stat in primary_stats:
209                long_name = secondary_stat.long_name
210                self.statistics[long_name] = secondary_stat
211                short_name = secondary_stat.short_name
212                self.statistics[short_name] = secondary_stat
213                secondary_stat.attach(self)
214        self.behaviour = None
215        if inventory == None:
216            self.inventory = Inventory()
217        else:
218            self.inventory = inventory
219        self.state = _AGENT_STATE_NONE
220        self.layer_id = agent_layer.getId()
221        self.createBehaviour(agent_layer)
222   
223    def createBehaviour(self, layer):
224        """Creates the behaviour for this actor.
225           @return: None"""
226        pass
227   
228    def setup(self):
229        """@return: None"""
230        self.behaviour.attachToLayer(self.ID)
231
232    def start(self):
233        """@return: None"""
234        self.behaviour.idle()
235
236    def teleport(self, location):
237        """Teleports a Character instantly to the given location.
238           @type location: fife.Location
239           @param location: Target coordinates for Character.
240           @return: None"""
241        self.state = _AGENT_STATE_IDLE
242        self.behaviour.nextAction = None 
243        self.behaviour.agent.setLocation(location)
244
245    def give (self, item, actor):
246        """Gives the specified item to the different actor. Raises an exception if the item was invalid or not found
247           @type item: Carryable
248           @param item: The item object to give
249           @param actor: Person to give item to"""
250        if item == None: 
251            raise ValueError("I don't have %s" % item.name)
252        self.inventory.takeItem(item)
253        actor.inventory.placeItem(item)           
254       
255    def hasItem(self, item_type):
256        """Returns wether an item is present in the players inventory or not
257        @param item_type: ID of the item
258        @type item_type: str
259        @return: True when the item is present, False when not"""
260        return self.inventory.findItem(item_type=item_type)
261
262    def itemCount(self, item_type=""):
263        """Returns number of all items or items specified by item_type
264        the player has.
265        @param item_type: ID of the item, can be empty
266        @type item_type: str
267        @return: Number of items"""
268        return self.inventory.count(item_type)
269
270    def getLocation(self):
271        """Get the NPC's position as a fife.Location object. Basically a
272           wrapper.
273           @rtype: fife.Location
274           @return: the location of the NPC"""
275        return self.behaviour.agent.getLocation()
276   
277    def run(self, location):
278        """Makes the PC run to a certain location
279           @type location: fife.ScreenPoint
280           @param location: Screen position to run to.
281           @return: None"""
282        self.state = _AGENT_STATE_RUN
283        self.behaviour.nextAction = None
284        self.behaviour.agent.move('run', location, self.behaviour.speed + 1)
285
286    def walk(self, location):
287        """Makes the PC walk to a certain location.
288           @type location: fife.ScreenPoint
289           @param location: Screen position to walk to.
290           @return: None"""
291        self.state = _AGENT_STATE_RUN
292        self.behaviour.nextAction = None 
293        self.behaviour.agent.move('walk', location, self.behaviour.speed - 1)
294
295    def getStateForSaving(self):
296        """Returns state for saving
297        """
298        ret_dict = GameObject.getStateForSaving(self)
299        ret_dict["Inventory"] = self.inventory.serializeInventory()
300        return ret_dict
301
302    def _getCoords(self):
303        """Get-er property function"""
304        return (self.getLocation().getMapCoordinates().x,
305                self.getLocation().getMapCoordinates().y)
306   
307    def _setCoords(self, coords):
308        """Set-er property function"""
309        map_coords = self.getLocation().getMapCoordinates()
310        map_coords.X, map_coords.Y = float(coords[0]), float (coords[1])
311        self.teleport(map_coords)
312   
313    coords = property (_getCoords, _setCoords,
314        doc="Property allowing you to get and set the object's \
315                coordinates via tuples")
316           
317class PlayerCharacter (CharacterBase):
318    """PC class"""
319    def __init__ (self, ID, agent_layer=None, inventory=None,
320                  text="Its you. Who would've thought that?", **kwargs):
321        if inventory == None:
322            inventory = Inventory()
323            inventory.placeItem(CarryableItem(ID=456, name="Dagger123"))
324            inventory.placeItem(CarryableItem(ID=555, name="Beer"))
325            inventory.placeItem(CarryableItem(ID=616,
326                                    name="Pamphlet",
327                                    image="/gui/inv_images/inv_pamphlet.png"))
328        CharacterBase.__init__(self, ID, agent_layer, inventory, text, **kwargs)
329        self.people_i_know = set()
330        self.attributes.append("PC")
331 
332    def getStateForSaving(self):
333        """Returns state for saving
334        """
335        ret_dict = super(PlayerCharacter, self).getStateForSaving()
336        ret_dict["PeopleKnown"] = self.people_i_know
337        return ret_dict
338   
339    def meet(self, npc):
340        """Record that the PC has met a certain NPC
341           @type npc: str
342           @param npc: The NPC's name or id"""
343        if npc in self.people_i_know:
344            # we could raise an error here, but should probably be a warn
345            # raise RuntimeError("I already know %s" % npc)
346            return
347        self.people_i_know.add(npc)
348
349    def met(self, npc):
350        """Indicate whether the PC has met this npc before
351           @type npc: str
352           @param npc: The NPC's name or id
353           @return: None"""
354        return npc in self.people_i_know
355
356    def createBehaviour(self, layer):
357        """Creates the behaviour for this actor.
358           @return: None"""
359        self.behaviour = PCBehaviour(self, layer)
360 
361    def approach(self, location, action=None):
362        """Approaches a location and then perform an action (if set).
363           @type loc: fife.Location
364           @param loc: the location to approach
365           @type action: Action
366           @param action: The action to schedule for execution after the approach.
367           @return: None"""
368        self.state = _AGENT_STATE_APPROACH
369        self.behaviour.nextAction = action
370        boxLocation = tuple([int(float(i)) for i in location])
371        l = fife.Location(self.behaviour.agent.getLocation())
372        l.setLayerCoordinates(fife.ModelCoordinate(*boxLocation))
373        self.behaviour.agent.move('run', l, self.behaviour.speed + 1)
374   
375class NonPlayerCharacter(CharacterBase, Scriptable):
376    """NPC class"""
377    def __init__(self, ID, agent_layer=None, name='NPC', \
378                 text='A nonplayer character', inventory=None,
379                 real_name='NPC', dialogue=None, **kwargs):
380        # init game object
381        CharacterBase.__init__(self, ID, agent_layer=agent_layer,
382                               inventory=inventory, name=name,
383                               real_name=real_name, text=text, **kwargs)
384        Scriptable.__init__(self, **kwargs)
385
386        self.attributes.append("NPC")
387        self.dialogue = dialogue
388
389    def prepareStateForSaving(self, state):
390        """Prepares state for saving
391        @type state: dictionary
392        @param state: State of the object 
393        """
394        CharacterBase.prepareStateForSaving(self, state)
395        del state["behaviour"]
396
397    def getStateForSaving(self):
398        """Returns state for saving
399        """
400        ret_dict = CharacterBase.getStateForSaving(self)
401        ret_dict["Lives"] = self.lives
402        ret_dict["State"] = self.behaviour.state
403        return ret_dict
404
405    def createBehaviour(self, layer):
406        """Creates the behaviour for this actor.
407           @return None """
408        self.behaviour = NPCBehaviour(self, layer)
409
410    def wander(self, location):
411        """Nice slow movement for random walking.
412           @type location: fife.Location
413           @param location: Where the NPC will walk to.
414           @return: None"""
415        self.behaviour.agent.move('walk', location, self.behaviour.speed - 1)
416
417    def talk(self, pc):
418        """Makes the NPC ready to talk to the PC
419           @return: None"""
420        self.behaviour.state = _AGENT_STATE_TALK
421        self.behaviour.pc = pc.behaviour.agent
422        self.behaviour.idle()
Note: See TracBrowser for help on using the repository browser.