source: trunk/game/scripts/objects/base.py @ 346

Revision 346, 10.2 KB checked in by b0rland_parpg, 10 years ago (diff)

Ticket #107: patch by b0rland

  • Wrote initial version of the inventory storage class
  • Added some useful utility functions into container class and its derivatives
  • Created several combined classes for carryable containers
  • Extended tests and of course made new ones
  • Renamed old Inventory class to be InventoryGUI

Note: InventoryGUI still works the old way and doesn't use Inventory

  • 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"""Containes classes defining the base properties of all interactable in-game
19   objects (such as Carryable, Openable, etc. These are generally independent
20   classes, which can be combined in almost any way and order.
21
22   Some rules that should be followed when CREATING base property classes:
23   
24   1. If you want to support some custom initialization arguments, always define
25      them as keyword ones. Only GameObject would use positional arguments.
26   2. In __init__() **ALWAYS** call the parent's __init__(**kwargs), preferably
27      *at the end* of your __init__() (makes it easier to follow)
28   3. There should always be an is_x class member set to True on __init__
29      (where X is the name of the class)
30
31   EXAMPLE:
32
33   class Openable(object):
34       def __init__ (self, is_open = True, **kwargs):
35           self.is_openable = True
36           self.is_open = is_open
37           super(Openable,self).__init__ (**kwargs)
38       
39
40   Some rules are to be followed when USING the base classes to make composed ones:
41
42   1. The first parent should always be the base GameObject class
43   2. Base classes other than GameObject can be inherited in any order
44   3. The __init__ functoin of the composed class should always invoke the
45      parent's __init__() *before* it starts customizing any variables.
46
47   EXAMPLE:
48
49   class TinCan (GameObject, Container, Scriptable, Destructable, Carryable):
50       def __init__ (self, *args, **kwargs):
51           super(TinCan,self).__init__ (*args, **kwargs)
52           self.name = 'Tin Can'"""
53import fife
54from settings import Setting
55from random import randrange
56
57class GameObject (object):
58    """A base class to be inherited by all game objects. This must be the
59       first class (left to right) inherited by any game object."""
60    def __init__ (self, ID, gfx = {}, xpos = 0.0, ypos = 0.0, map_id = None, 
61                  blocking=True, name="Generic object", text="Item description",
62                  desc="Detailed description", **kwargs):
63        """Set the basic values that are shared by all game objects.
64           @type ID: String
65           @param ID: Unique object identifier. Must be present.
66           @type gfx: Dictionary
67           @param gfx: Dictionary with graphics for the different contexts       
68           @type coords 2-item tuple
69           @param coords: Initial coordinates of the object.
70           @type map_id: String
71           @param map_id: Identifier of the map where the object is located
72           @type blocking: Boolean
73           @param blocking: Whether the object blocks character movement
74           @type name: String
75           @param name: The display name of this object (e.g. 'Dirty crate')
76           @type text: String
77           @param text: A longer description of the item
78           @type desc: String
79           @param desc: A long description of the item that is displayed when it is examined
80           """
81       
82        self.ID = ID
83        self.gfx = gfx
84        self.X = xpos
85        self.Y = ypos
86        self.map_id = map_id
87        self.blocking = True
88        self.name = name
89        self.text = text
90        self.desc = desc
91       
92    def trueAttr(self, attr):
93        """Shortcut function to check if the current object has a member named
94           is_%attr and if that attribute evaluates to True"""
95        return hasattr(self,'is_%s' % attr) and getattr(self, 'is_%s' % attr)
96
97    def _getCoords(self):
98        """Get-er property function"""
99        return (self.X, self.Y)
100   
101    def _setCoords(self, coords):
102        """Set-er property function"""
103        self.X, self.Y = float(coords[0]), float (coords[1])
104       
105    coords = property (_getCoords, _setCoords, 
106        doc = "Property allowing you to get and set the obejct's coordinates via tuples")
107   
108    def __repr__(self):
109        """A debugging string representation of the object"""
110        return "<%s:%s>" % (self.name, self.ID)
111
112class Openable(object):
113    """Adds open() and .close() capabilities to game objects
114    The current state is tracked by the .is_open variable"""
115    def __init__(self, is_open = True, **kwargs):
116        """Init operation for openable objects
117        @type is_open: Boolean
118        @param is_open: Keyword boolean argument sets the initial state."""
119        self.is_openable = True
120        self.is_open = is_open
121   
122    def open(self):
123        """Opens the object, and runs an 'onOpen' script, if present"""
124        self.is_open = True
125        try:
126            if self.trueAttr ('scriptable'):
127                self.runScript('onOpen')
128        except AttributeError :
129            pass
130           
131    def close(self):
132        """Opens the object, and runs an 'onClose' script, if present"""
133        self.is_open = False
134        try:
135            if self.trueAttr ('scriptable'):
136                self.runScript('onClose')
137        except AttributeError :
138            pass
139       
140class Lockable (Openable):
141    """Allows objects to be locked"""
142    def __init__ (self, locked = False, is_open=True, **kwargs):
143        """Init operation for lockable objects
144        @type locked: Boolean
145        @param locked: Keyword boolen argument to set the initial locked state.
146        @type is_open: Boolean
147        @param is_open: Keyword boolean argument sets the initial open state. It is ignored if locked is True -- locked objects are always closed.
148        """
149        self.is_lockable = True
150        self.locked = locked
151        if locked :
152            is_open=False
153        Openable.__init__( self, is_open, **kwargs )
154       
155    def unlock (self):
156        """Handles unlocking functionality"""
157        self.locked = False     
158       
159    def lock (self):
160        """Handles  locking functionality"""
161        self.close()
162        self.locked = True
163       
164    def open (self, *args, **kwargs):
165        """Adds a check to see if the object is unlocked before running the
166           .open() function of the parent class"""
167        if self.locked:
168            raise ValueError ("Open failed: object locked")
169        super (Lockable,self).open(*args,**kwargs)
170       
171class Carryable (object):
172    """Allows objects to be stored in containers"""
173    def __init__ (self, **kwargs):
174        self.is_carryable = True
175        self.in_container = None
176        self.weight = 1.0
177   
178class Container (object):
179    """Gives objects the capability to hold other objects"""
180    def __init__ (self, **kwargs):
181        self.is_container = True
182        self.items = []
183       
184    def placeItem (self, item):
185        """Adds the provided carriable item to the inventory.
186           Runs an 'onStoreItem' script, if present"""   
187        if not item.trueAttr ('carryable'):
188            raise ValueError ('% is not carriable!' % item)
189        item.in_container = self
190        self.items.append (item)
191        # Run any scripts associated with storing an item in the container
192        try:
193            if self.trueAttr ('scriptable'):
194                self.runScript('onPlaceItem')
195        except AttributeError :
196            pass
197
198    def takeItem (self, item):
199        """Takes the listed item out of the inventory.
200           Runs an 'ontakeItem' script"""       
201        if not item in self.items:
202            raise ValueError ('I do not contain this item: %s' % item)
203        self.items.remove (item)
204        # Run any scripts associated with popping an item out of the container
205        try:
206            if self.trueAttr ('scriptable'):
207                self.runScript('onTakeItem')
208        except AttributeError :
209            pass
210
211    def count (self):
212        return len(self.items)
213
214       
215class Living (object):
216    def __init__ (self, **kwargs):
217        self.is_living = True
218    def die(self):
219        self.is_living = False
220       
221class Scriptable (object):
222    """Allows objects to have predefined scripts executed on certain events"""
223    def __init__ (self, scripts = {}, **kwargs):
224        """Init operation for scriptable objects
225           @type scripts: Dictionary
226           @param scripts: Dictionary where the event strings are keys. The
227           values are 3-item tuples (function, positional_args, keyword_args)"""
228        self.is_scriptable = True
229        self.scripts = scripts
230       
231    def runScript (self, event):
232        """Runs the script for the given event"""
233        if event in self.scripts and self.scripts[event]:
234            func, args, kwargs = self.scripts[event]
235            func (*args, **kwargs)
236           
237    def setScript (self, event, func, args = [] , kwargs={}):
238        """Sets a script to be executed for the given event."""
239        self.scripts[event] = (func, args, kwargs)
240
241class CharStats (object):
242    """Provides the object with character statistics"""
243    def __init__ (self, **kwargs):
244        self.is_charstats = True
245       
246class Wearable (object):
247    def __init__ (self, slots, **kwargs):
248        """Allows the object to be worn somewhere on the body (e.g. pants)"""
249        self.is_wearable = True
250        if isinstance(slots,tuple) :
251            self.slots = slots
252        else :
253            self.slots = (slots,)
254   
255class Usable (object):
256    """Allows the object to be used in some way (e.g. a Zippo lighter
257       to make a fire)"""
258    def __init__ (self, **kwargs):
259        self.is_usable = True
260       
261class Weapon (object):
262    """Allows the object to be used as a weapon"""
263    def __init__ (self, **kwargs):
264        self.is_weapon = True
265       
266class Destructable (object):
267    """Allows the object to be destroyed"""
268    def __init__ (self, **kwargs):
269        self.is_destructable = True
270       
271class Trappable (object):
272    """Provides trap slots to the object"""
273    def __init__ (self, **kwargs):
274        self.is_trappable = True
Note: See TracBrowser for help on using the repository browser.