source: trunk/PARPG/scripts/objects/base.py @ 187

Revision 187, 11.2 KB checked in by tZee_parpg, 10 years ago (diff)

Moved code to the new object classes and the new objectLoader.py.
(N)PC and (N)PCBehaviour coupling is still a bit clumsy: Too many dereferences between those two. Needs to be improved.
Doors are missing for now and need to be added. Except that all functionality should have been preserved.

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                  **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: ???
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        self.ID = ID
79        self.gfx = gfx
80        self.X = xpos
81        self.Y = ypos
82        self.map_id = map_id
83        self.blocking = True
84        self.name = name
85        self.text = text
86        super(GameObject,self).__init__ (**kwargs)
87       
88    def trueAttr(self, attr):
89        """Shortcut function to check if the current object has a member named
90           is_%attr and if that attribute evaluates to True"""
91        return hasattr(self,'is_%s' % attr) and getattr(self, 'is_%s' % attr)
92
93    def _getCoords(self):
94        """Get-er property function"""
95        return (self.X, self.Y)
96   
97    def _setCoords(self, coords):
98        """Set-er property function"""
99        self.X, self.Y = float(coords[0]), float (coords[1])
100       
101    coords = property (_getCoords, _setCoords, 
102        doc = "Property allowing you to get and set the obejct's coordinates via tuples")
103   
104    def __repr__(self):
105        """A debugging string representation of the object"""
106        return "<%s:%s>" % (self.name, self.ID)
107
108class Openable(object):
109    """Adds open() and .close() capabilities to game objects
110    The current state is tracked by the .is_open variable"""
111    def __init__(self, is_open = True, **kwargs):
112        """Init operation for openable objects
113        @type is_open: Boolean
114        @param is_open: Keyword boolean argument sets the initial state."""
115        self.is_openable = True
116        self.is_open = is_open
117        super(Openable,self).__init__ (**kwargs)
118   
119    def open(self):
120        """Opens the object, and runs an 'onOpen' script, if present"""
121        self.is_open = True
122        if self.trueAttr ('scriptable'):
123            self.runScript('onOpen')
124           
125    def close(self):
126        """Opens the object, and runs an 'onClose' script, if present"""
127        self.is_open = False
128        if self.trueAttr ('scriptable'):
129            self.runScript('onClose')             
130       
131class Lockable (Openable):
132    """Allows objects to be locked"""
133    def __init__ (self, locked = True, **kwargs):
134        """Init operation for lockable objects
135        @type locked: Boolean
136        @param locked: Keyword boolen argument to set the initial locked state.
137        """
138        self.is_lockable = True
139        self.locked = locked
140        super(Lockable,self).__init__ (**kwargs)
141       
142    def unlock (self):
143        """Handles unlocking functionality"""
144        self.locked = False     
145       
146    def lock (self):
147        """Handles  locking functionality"""
148        self.close()
149        self.locked = True
150       
151    def open (self, *args, **kwargs):
152        """Adds a check to see if the object is unlocked before running the
153           .open() function of the parent class"""
154        if self.locked:
155            raise ValueError ("Open failed: object locked")
156        super (Lockable,self).open(*args,**kwargs)
157       
158class Carryable (object):
159    """Allows objects to be stored in containers"""
160    def __init__ (self, **kwargs):
161        self.is_carryable = True
162        self.in_container = None
163        self.weight = 1.0
164        super(Carryable,self).__init__ (**kwargs)
165   
166class Container (object):
167    """Gives objects the capability to hold other objects"""
168    def __init__ (self, **kwargs):
169        self.is_container = True
170        self.items = []
171        super(Container,self).__init__ (**kwargs)
172       
173    def placeItem (self, item):
174        """Adds the provided carriable item to the inventory.
175           Runs an 'onStoreItem' script, if present"""   
176        if not item.trueAttr ('carryable'):
177            raise ValueError ('% is not carriable!' % item)
178        item.in_container = self
179        self.items.append (item)
180        # Run any scripts associated with storing an item in the container
181        if self.trueAttr ('scriptable'):
182            self.runScript('onPlaceItem')
183       
184    def takeItem (self, item):
185        """Takes the listed item out of the inventory.
186           Runs an 'ontakeItem' script"""       
187        if not item in self.items:
188            raise ValueError ('I do not contain this item: %s' % item)
189        self.items.remove (item)
190        # Run any scripts associated with popping an item out of the container
191        if self.trueAttr ('scriptable'):
192            self.runScript('ontakeItem')
193       
194class Inventory (object):
195    """Aggregate class for things that have multiple Containers"""
196    def __init__ (self, **kwargs):
197        self.is_inventory = True
198        self.containers = []
199        super(Inventory,self).__init__ (**kwargs)
200   
201class Living (object):
202    def __init__ (self, **kwargs):
203        self.is_living = True
204        super(Living,self).__init__ (**kwargs)
205    def die(self):
206        self.is_living = False
207       
208class Scriptable (object):
209    """Allows objects to have predefined scripts executed on certain events"""
210    def __init__ (self, scripts = {}, **kwargs):
211        """Init operation for scriptable objects
212           @type scripts: Dictionary
213           @param scripts: Dictionary where the event strings are keys. The
214           values are 3-item tuples (function, positional_args, keyword_args)"""
215        self.is_scriptable = True
216        self.scripts = scripts
217        super(Scriptable,self).__init__ (**kwargs)
218       
219    def runScript (self, event):
220        """Runs the script for the given event"""
221        if event in self.scripts and self.scrpits[event]:
222            func, args, kwargs = self.scrpits[event]
223            func (*args, **kwargs)
224           
225    def setScript (self, event, func, args = [] , kwargs={}):
226        """Sets a script to be executed for the given event."""
227        self.scripts[event] = (func, args, kwargs)
228
229class CharStats (object):
230    """Provides the object with character statistics"""
231    def __init__ (self, **kwargs):
232        self.is_charstats = True
233        super(CharStats,self).__init__ (**kwargs)
234       
235class Wearable (object):
236    def __init__ (self, **kwargs):
237        """Allows the object to be weared somewhere on the body (e.g. pants)"""
238        self.is_wearable = True
239        super(Wearable,self).__init__ (**kwargs)
240   
241class Usable (object):
242    """Allows the object to be used in some way (e.g. a Zippo lighter
243       to make a fire)"""
244    def __init__ (self, **kwargs):
245        self.is_usable = True
246        super(Usable,self).__init__ (**kwargs)
247       
248class Weapon (object):
249    """Allows the object to be used as a weapon"""
250    def __init__ (self, **kwargs):
251        self.is_weapon = True
252        super(Weapon,self).__init__ (**kwargs)
253       
254class Destructable (object):
255    """Allows the object to be destroyed"""
256    def __init__ (self, **kwargs):
257        self.is_destructable = True
258        super(Destructable,self).__init__ (**kwargs)
259       
260class Trappable (object):
261    """Provides trap slots to the object"""
262    def __init__ (self, **kwargs):
263        self.is_trappable = True
264        super(Trappable,self).__init__ (**kwargs)
265       
266if __name__=="__main__":
267    """This will be turned into a test suite"""
268    class Wildcard (GameObject, Lockable, Container, Living, Scriptable, 
269                    CharStats, Wearable, Usable, Weapon, Destructable,
270                    Trappable, Carryable, ):
271        def __init__ (self, ID, *args, **kwargs):
272            super(Wildcard,self).__init__ (ID, *args, **kwargs)
273            self.name = 'All-purpose carry-all'
274            self.text = 'What is this? I dont know'   
275   
276    test = GameObject (1, {'map':'img/test.png'}, (1,1), None, 'Test object','Description')
277    print test
278    assert test.X == 1
279    assert test.Y == 1
280    assert test.coords == (1,1)
281    test.coords = (2,2)
282    assert test.X == 2.0
283    assert test.Y == 2.0
284   
285    wc = Wildcard (2)
286    print wc
287    print wc.is_carryable
288
Note: See TracBrowser for help on using the repository browser.