Ignore:
Timestamp:
10/24/09 06:17:27 (10 years ago)
Author:
b0rland_parpg
Message:

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/game/scripts/inventory.py

    r313 r346  
    1 #/usr/bin/python 
     1# This file is part of PARPG. 
     2#  
     3# PARPG is free software: you can redistribute it and/or modify 
     4# it under the terms of the GNU General Public License as published by 
     5# the Free Software Foundation, either version 3 of the License, or 
     6# (at your option) any later version. 
     7#  
     8# PARPG is distributed in the hope that it will be useful, 
     9# but WITHOUT ANY WARRANTY; without even the implied warranty of 
     10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     11# GNU General Public License for more details. 
     12#  
     13# You should have received a copy of the GNU General Public License 
     14# along with PARPG.  If not, see <http://www.gnu.org/licenses/>. 
    215 
    3 #   This file is part of PARPG. 
     16from scripts.objects.base import GameObject, Container, Carryable 
     17from scripts.objects.composed import CarryableContainer, SingleItemContainer as Slot 
     18import copy 
    419 
    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. 
     20class Inventory(CarryableContainer): 
     21    """The class to represent inventory 'model': allow operations with 
     22    inventory contents, perform weight/bulk calculations, etc""" 
     23    def __init__(self, ID, **kwargs): 
     24        """Initialise instance""" 
     25        CarryableContainer.__init__(self, ID=ID, **kwargs) 
     26        self.items = {"head": Slot(ID), "neck": Slot(ID), "shoulders": Slot(ID), 
     27                      "chest": Slot(ID), "abdomen": Slot(ID), "left_arm": Slot(ID), 
     28                      "right_arm": Slot(ID),"groin": Slot(ID), "hips": Slot(ID), 
     29                      "left_leg": Slot(ID), "right_leg": Slot(ID), 
     30                      "backpack": CarryableContainer(ID)} 
     31        for key,item in self.items.iteritems(): 
     32            item.name = key 
     33        self.item_lookup = {} 
    934 
    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. 
     35    def placeItem(self,item): 
     36        self.items["backpack"].placeItem(item) 
     37        self.item_lookup[item.ID] = "backpack" 
     38         
     39    def takeItem(self,item): 
     40        if not item.ID in self.item_lookup: 
     41            raise ValueError ('I do not contain this item: %s' % item) 
     42        self.items[self.item_lookup[item.ID]].takeItem(item) 
     43        self.item_lookup[item.ID] = None 
    1444 
    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/>. 
     45    def getWeight(self): 
     46        """Total weight of all items in container + container's own weight""" 
     47        return sum((item.weight for item in self.items.values()), self.own_weight) 
    1748 
    18 import sys 
    19 import os 
    20 import fife 
    21 import fifelog 
    22 import pychan 
    23 from scripts import drag_drop_data as data_drag 
    24 from pychan.tools import callbackWithArguments as cbwa 
    25 from scripts.items import item_image_dict 
     49    def setWeightDummy(self, weight): 
     50        pass 
    2651 
    27 class Inventory(object): 
    28     """Main inventory class""" 
    29     def __init__(self, engine, items, callbacks): 
    30         """Initialise the instance. 
    31            @type engine: fife.Engine 
    32            @param engine: An instance of the fife engine 
    33            @type items: dict 
    34            @param items: A dictionary for every slot that goes '{slot:item,  
    35                          slot:item}' if a slot is not included in the dict, 
    36                          it is assumed to be empty 
    37            @type callbacks: dict 
    38            @param callbacks: a dict of callbacks 
    39                refreshReadyImages: 
    40                    Function that will make the ready slots on the HUD 
    41                    reflect those within the inventory 
    42                toggleInventoryButton: 
    43                    Function that will toggle the state of the inventory button 
    44            @return: None""" 
    45         pychan.init(engine, debug = True) 
    46         self.engine = engine 
    47         self.readyCallback = callbacks['refreshReadyImages'] 
    48         self.toggleInventoryButtonCallback = callbacks['toggleInventoryButton'] 
    49         self.original_cursor_id = self.engine.getCursor().getId() 
    50         # TODO: remove hard-coded string? 
    51         self.inventory = pychan.loadXML("gui/inventory.xml") 
    52         self.inventory_shown = False  
    53         self.events_to_map = {} 
    54         # the images that should be used for the buttons when they are "empty" 
    55         self.empty_images = {'A1':'gui/inv_images/inv_backpack.png', 
    56                              'A2':'gui/inv_images/inv_backpack.png', 
    57                              'A3':'gui/inv_images/inv_backpack.png', 
    58                              'A4':'gui/inv_images/inv_backpack.png', 
    59                              'A5':'gui/inv_images/inv_backpack.png', 
    60                              'B1':'gui/inv_images/inv_backpack.png', 
    61                              'B2':'gui/inv_images/inv_backpack.png', 
    62                              'B3':'gui/inv_images/inv_backpack.png', 
    63                              'B4':'gui/inv_images/inv_backpack.png', 
    64                              'B5':'gui/inv_images/inv_backpack.png', 
    65                              'C1':'gui/inv_images/inv_backpack.png', 
    66                              'C2':'gui/inv_images/inv_backpack.png', 
    67                              'C3':'gui/inv_images/inv_backpack.png', 
    68                              'C4':'gui/inv_images/inv_backpack.png', 
    69                              'C5':'gui/inv_images/inv_backpack.png', 
    70                              'D1':'gui/inv_images/inv_backpack.png', 
    71                              'D2':'gui/inv_images/inv_backpack.png', 
    72                              'D3':'gui/inv_images/inv_backpack.png', 
    73                              'D4':'gui/inv_images/inv_backpack.png', 
    74                              'D5':'gui/inv_images/inv_backpack.png', 
    75                              'Head':'gui/inv_images/inv_head.png', 
    76                              'LeftHeld':'gui/inv_images/inv_litem.png', 
    77                              'RightHeld':'gui/inv_images/inv_ritem.png', 
    78                              'LeftHand':'gui/inv_images/inv_lhand.png', 
    79                              'RightHand':'gui/inv_images/inv_rhand.png', 
    80                              'Body':'gui/inv_images/inv_torso.png', 
    81                              'Belt':'gui/inv_images/inv_belt.png', 
    82                              'Ready1':'gui/inv_images/inv_belt_pouches.png', 
    83                              'Ready2':'gui/inv_images/inv_belt_pouches.png', 
    84                              'Ready3':'gui/inv_images/inv_belt_pouches.png', 
    85                              'Ready4':'gui/inv_images/inv_belt_pouches.png', 
    86                              'LeftFoot':'gui/inv_images/inv_lfoot.png', 
    87                              'RightFoot':'gui/inv_images/inv_rfoot.png'} 
    88         # every button on the inventory and its category 
    89         self.buttons = {'A1':'main_inv', 'A2':'main_inv', 'A3':'main_inv', 
    90                         'A4':'main_inv', 'A5':'main_inv', 'B1':'main_inv', 
    91                         'B2':'main_inv', 'B3':'main_inv', 'B4':'main_inv', 
    92                         'B5':'main_inv', 'C1':'main_inv', 'C2':'main_inv', 
    93                         'C3':'main_inv', 'C4':'main_inv', 'C5':'main_inv', 
    94                         'D1':'main_inv', 'D2':'main_inv', 'D3':'main_inv', 
    95                         'D4':'main_inv', 'D5':'main_inv', 
    96                         'LeftFoot':'foot', 'RightFoot':'foot', 
    97                         'LeftHand':'hand', 'RightHand':'hand', 
    98                         'Head':'head', 'Ready1':'ready',  
    99                         'Ready2':'ready', 'Ready3':'ready',  
    100                         'Ready4':'ready', 'Belt':'belt', 'LeftHeld':'held', 
    101                         'RightHeld':'held', 'Body':'body'} 
    102         # all possible categories 
    103         self.locations = ['ready', 'head', 'foot', 'hand', 
    104                           'belt', 'held', 'body'] 
     52    weight = property(getWeight, setWeightDummy, "Total weight of container") 
    10553 
    106         for key in items: 
    107             widget = self.inventory.findChild(name=key) 
    108             item = items[key] 
    109             image = item_image_dict[item] 
    11054 
    111             widget.item = item 
    112             widget.up_image = image 
    113             widget.down_image = image 
    114             widget.hover_image = image             
     55    def count(self): 
     56        return sum(item.count() for item in self.items.values()) 
     57     
     58    def takeOff(self, item): 
     59        return self.moveItemToSlot(item, "backpack") 
    11560 
    116         for button in self.buttons: 
    117             # make every button's callback be self.dragDrop 
    118             self.events_to_map[button] = cbwa(self.dragDrop, button) 
    119             ch = self.inventory.findChild(name=button) 
    120             # make every slot's item be none if it has not already been set 
    121             if button not in items: 
    122                 ch.item = "" 
     61    def moveItemToSlot(self,item,slot): 
     62        if not slot in self.items: 
     63            raise(ValueError,"%s: No such slot" % slot) 
    12364 
    124         self.events_to_map['close_button'] = self.closeInventoryAndToggle 
    125         self.inventory.mapEvents(self.events_to_map)    
    126         self.resetMouseCursor() 
     65        if item.ID in self.item_lookup: 
     66            self.items[self.item_lookup[item.ID]].takeItem(item) 
     67        try: 
     68            self.items[slot].placeItem(item) 
     69        except ValueError: 
     70            self.takeOff(self.items[slot].items[0]) 
     71            self.items[slot].placeItem(item) 
     72        self.item_lookup[item.ID] = slot 
     73      
     74    def getItemsInSlot(self, slot): 
     75        if not slot in self.items: 
     76            raise(ValueError,"%s: No such slot" % slot) 
     77        return copy.copy(self.items[slot].items) 
    12778 
    128     def closeInventory(self): 
    129         """Close the inventory. 
    130            @return: None""" 
    131         self.inventory.hide() 
     79    def isSlotEmpty(self, slot): 
     80        if not slot in self.items: 
     81            raise(ValueError,"%s: No such slot" % slot) 
     82        return self.items[slot].count() == 0 
    13283 
    133     def closeInventoryAndToggle(self): 
    134         """Close the inventory screen. 
    135            @return: None""" 
    136         self.closeInventory() 
    137         self.toggleInventoryButtonCallback() 
    138         self.inventory_shown = False 
    139  
    140     def toggleInventory(self, toggleImage=True): 
    141         """Pause the game and enter the inventory screen, or close the 
    142            inventory screen and resume the game. 
    143            @type toggleImage: bool 
    144            @param toggleImage: 
    145                Call toggleInventoryCallback if True. Toggling via a 
    146                keypress requires that we toggle the Hud inventory image 
    147                explicitly. Clicking on the Hud inventory button toggles the 
    148                image implicitly, so we don't change it. 
    149            @return: None""" 
    150         if not self.inventory_shown: 
    151             self.showInventory() 
    152             self.inventory_shown = True 
    153         else: 
    154             self.closeInventory() 
    155             self.inventory_shown = False 
    156  
    157         if toggleImage: 
    158             self.toggleInventoryButtonCallback() 
    159  
    160     def showInventory(self): 
    161         """Show the inventory. 
    162            @return: None""" 
    163         self.inventory.show() 
    164  
    165     def setMouseCursor(self, image, dummy_image, type="native"):  
    166         """Set the mouse cursor to an image. 
    167            @type image: string 
    168            @param image: The image you want to set the cursor to 
    169            @type dummy_image: string 
    170            @param dummy_image: ??? 
    171            @type type: string 
    172            @param type: ??? 
    173            @return: None""" 
    174         cursor = self.engine.getCursor() 
    175         cursor_type = fife.CURSOR_IMAGE 
    176         img_pool = self.engine.getImagePool() 
    177         if(type == "target"): 
    178             target_cursor_id = img_pool.addResourceFromFile(image)   
    179             dummy_cursor_id = img_pool.addResourceFromFile(dummy_image) 
    180             cursor.set(cursor_type,target_dummy_cursor_id) 
    181             cursor.setDrag(cursor_type,target_cursor_id,-16,-16) 
    182         else: 
    183             cursor_type = fife.CURSOR_IMAGE 
    184             zero_cursor_id = img_pool.addResourceFromFile(image) 
    185             cursor.set(cursor_type,zero_cursor_id) 
    186             cursor.setDrag(cursor_type,zero_cursor_id) 
    187              
    188     def resetMouseCursor(self): 
    189         """Reset cursor to default image. 
    190            @return: None""" 
    191         c = self.engine.getCursor() 
    192         img_pool = self.engine.getImagePool() 
    193         cursor_type = fife.CURSOR_NATIVE 
    194         # this is the path to the default image 
    195         cursor_id = self.original_cursor_id 
    196         c.setDrag(cursor_type, cursor_id) 
    197         c.set(cursor_type, cursor_id) 
    198          
    199     def dragDrop(self, obj): 
    200         """Decide whether to drag or drop the image. 
    201            @type obj: string 
    202            @param obj: The name of the object within  
    203                        the dictionary 'self.buttons' 
    204            @return: None""" 
    205         if(data_drag.dragging == True): 
    206             self.dropObject(obj) 
    207         elif(data_drag.dragging == False): 
    208             self.dragObject(obj) 
    209                  
    210     def dragObject(self, obj): 
    211         """Drag the selected object. 
    212            @type obj: string 
    213            @param obj: The name of the object within 
    214                        the dictionary 'self.buttons' 
    215            @return: None""" 
    216         # get the widget from the inventory with the name obj 
    217         drag_widget = self.inventory.findChild(name = obj) 
    218         # only drag if the widget is not empty 
    219         if (drag_widget.up_image != self.empty_images[obj]): 
    220             # get it's type (e.g. main_inv) 
    221             data_drag.dragged_type = self.buttons[obj] 
    222             # get the item that the widget is 'storing' 
    223             data_drag.dragged_item = drag_widget.item 
    224             # get the up and down images of the widget 
    225             up_image = drag_widget.up_image 
    226             down_image = drag_widget.down_image 
    227             # set the mouse cursor to be the widget's image 
    228             self.setMouseCursor(up_image.source,down_image.source) 
    229             data_drag.dragged_image = up_image.source 
    230             data_drag.dragging = True 
    231             # after dragging the 'item', set the widgets' images 
    232             # so that it has it's default 'empty' images 
    233             drag_widget.up_image=(self.empty_images[obj]) 
    234             drag_widget.down_image=(self.empty_images[obj]) 
    235             drag_widget.hover_image=(self.empty_images[obj]) 
    236             # then set it's item to nothing 
    237             drag_widget.item = "" 
    238              
    239     def dropObject(self, obj): 
    240         """Drops the object being dropped 
    241            @type obj: string 
    242            @param obj: The name of the object within 
    243                        the dictionary 'self.buttons'  
    244            @return: None""" 
    245         # find the type of the place that the object 
    246         # is being dropped onto 
    247         data_drag.dropped_type  =  self.buttons[obj] 
    248         # if the dragged obj or the place it is being dropped is 
    249         # in the main inventory, drop the object 
    250         if((data_drag.dragged_type == 'main_inv') or 
    251            (data_drag.dropped_type == 'main_inv')): 
    252             drag_widget = self.inventory.findChild(name = obj) 
    253             drag_widget.up_image = data_drag.dragged_image 
    254             drag_widget.hover_image = data_drag.dragged_image 
    255             drag_widget.down_image = data_drag.dragged_image 
    256             drag_widget.item = data_drag.dragged_item 
    257             print 'Item: ' + drag_widget.item 
    258             data_drag.dragging = False 
    259             #reset the mouse cursor to the normal cursor 
    260             self.resetMouseCursor() 
    261             # if the object was dropped onto a ready slot, then 
    262             # update the hud 
    263             if (data_drag.dropped_type == 'ready'): 
    264                 self.readyCallback() 
    265          
    266         # if the dragged object's type is the same as the location to 
    267         # to drop it at's, and the dragged object's type is in 
    268         # self.locations, then drop the object 
    269         elif((data_drag.dragged_type == data_drag.dropped_type) and 
    270              (data_drag.dragged_type in self.locations)): 
    271             drag_widget = self.inventory.findChild(name = obj) 
    272             drag_widget.up_image = data_drag.dragged_image 
    273             drag_widget.hover_image = data_drag.dragged_image 
    274             drag_widget.down_image = data_drag.dragged_image 
    275             drag_widget.item = data_drag.dragged_item 
    276             print 'Item: ' + drag_widget.item 
    277             data_drag.dragging = False 
    278             # reset the mouse cursor 
    279             self.resetMouseCursor() 
    280             # if the object was dropped onto a ready slot, then 
    281             # update the hud 
    282             if(data_drag.dropped_type == 'ready'): 
    283                 self.readyCallback() 
    284         # otherwise, we assume that the player is trying to 
    285         # drop an object onto an incompatible slot 
    286         else: 
    287             # reset the mouse cursor 
    288             self.resetMouseCursor() 
    289             data_drag.dragging = False 
    290  
    291     def getItems(self): 
    292         """ 
    293         Get the items in the inventory slots. If there is no item in the slot, 
    294         it is skipped 
    295          
    296         @rtype: dict 
    297         @return: The items in the inventory 
    298         """ 
    299         items = {} 
    300  
    301         for button in self.buttons: 
    302             widget = self.inventory.findChild(name=button) 
    303             if (widget.item != ""): 
    304                 items[button] = widget.item 
    305  
    306         return items 
    307                  
    308     def getImage(self, name): 
    309         """Return a current image from the inventory 
    310            @type name: string 
    311            @param name: name of image to get 
    312            @return: None""" 
    313         return self.inventory.findChild(name=name) 
    314  
     84    def __repr__(self): 
     85        return "[%s:%s "%(self.name, self.ID)+reduce((lambda a,b: str(a) +', '+str(b)), self.items.values())+" ]" 
Note: See TracChangeset for help on using the changeset viewer.