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

Revision 756, 15.4 KB checked in by aspidites, 8 years ago (diff)

Patch by Aspidites:

+ maps load properly once again
+ obsoleted get/set methods in settings.py

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