source: branches/active/character_customization/game/scripts/gui/inventorygui.py @ 726

Revision 726, 14.9 KB checked in by technomage, 9 years ago (diff)

Patch by Technomage

  • Merged in my inventory_view git branch which implements my inventory gui prototype;
  • 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 fife.extensions.pychan.tools import callbackWithArguments as cbwa
19from fife.extensions import pychan
20from fife.extensions.pychan.attrs import UnicodeAttr
21
22from scripts.gui import drag_drop_data as data_drag
23from scripts.objects.base import Container
24from scripts.gui.containergui_base import ContainerGUIBase
25from scripts.objects.action import ACTIONS
26
27class EquipmentSlot(pychan.VBox):
28    ATTRIBUTES = pychan.VBox.ATTRIBUTES + [UnicodeAttr('label_text')]
29   
30    def _setLabelText(self, text):
31        label = self.findChild()
32        label.text = unicode(text)
33        label.resizeToContent()
34        self.margins = (
35            int((self.width - label.width) / 2.0),
36            int((self.height - label.height) / 2.0)
37        )
38   
39    def _getLabelText(self):
40        label = self.findChild()
41        return label.text
42   
43    label_text = property(fget=_getLabelText, fset=_setLabelText)
44   
45    def __init__(self, label_text=u'equipment', min_size=(50, 50),
46                 max_size=(50, 50), margins=None,
47                 background_image="gui/inv_images/inv_background.png",
48                 **kwargs):
49        pychan.VBox.__init__(self, min_size=min_size, max_size=max_size,
50                             **kwargs)
51        self.background_image = background_image
52        label = pychan.Label(text=unicode(label_text))
53        self.addChild(label)
54        self.label_text = label_text
55        self.adaptLayout()
56        if self.parent is not None:
57            self.beforeShow()
58
59
60class InventoryGrid(pychan.VBox):
61    ATTRIBUTES = pychan.VBox.ATTRIBUTES + [pychan.attrs.PointAttr('grid_size')]
62   
63    def _setNColumns(self, n_columns):
64        n_rows = self.grid_size[1]
65        self.grid_size = (n_columns, n_rows)
66   
67    def _getNColumns(self):
68        n_columns = self.grid_size[0]
69        return n_columns
70    n_columns = property(fget=_getNColumns, fset=_setNColumns)
71   
72    def _setNRows(self, n_rows):
73        n_columns = self.grid_size[0]
74        self.grid_size = (n_columns, n_rows)
75   
76    def _getNRows(self):
77        n_rows = self.grid_size[1]
78        return n_rows
79    n_rows = property(fget=_getNRows, fset=_getNColumns)
80   
81    def _setGridSize(self, grid_size):
82        n_columns, n_rows = grid_size
83        self.removeAllChildren()
84        for row_n in range(n_rows):
85            row_size = (n_columns * 50, 50)
86            row = pychan.HBox(min_size=row_size, max_size=row_size,
87                              padding=self.padding)
88            row.border_size = 1
89            row.opaque = 0
90            for column_n in range(n_columns):
91                slot = pychan.Icon(min_size=(50, 50), max_size=(50, 50))
92                slot.border_size = 1
93                row.addChild(slot)
94            self.addChild(row)
95        self.min_size = ((n_columns * 50) + 2, (n_rows * 50) + 2)
96        self.max_size = self.min_size
97   
98    def _getGridSize(self):
99        n_rows = len(self.children)
100        n_columns = len(self.children[0].children)
101        return (n_rows, n_columns)
102    grid_size = property(fget=_getGridSize, fset=_setGridSize)
103   
104    def __init__(self, grid_size=(2, 2), padding=0, **kwargs):
105        pychan.VBox.__init__(self, padding=padding, **kwargs)
106        self.opaque = 0
107        self.grid_size = grid_size
108        self.border_size = 1
109
110
111class InventoryGUI(ContainerGUIBase):
112    def __init__(self, controller, inventory, callbacks):
113        super(InventoryGUI, self).__init__(controller, "gui/inventory.xml")
114        self.engine = controller.engine
115        self.inventory_shown = False
116        render_backend = self.engine.getRenderBackend()
117        screen_mode = render_backend.getCurrentScreenMode()
118        screen_width, screen_height = (screen_mode.getWidth(),
119                                       screen_mode.getHeight())
120        widget_width, widget_height = self.gui.size
121        self.gui.position = ((screen_width - widget_width) / 2,
122                             (screen_height - widget_height) / 2)
123   
124    def toggleInventory(self, toggleImage=True):
125        """Pause the game and enter the inventory screen, or close the
126           inventory screen and resume the game.
127           @type toggleImage: bool
128           @param toggleImage:
129               Call toggleInventoryCallback if True. Toggling via a
130               keypress requires that we toggle the Hud inventory image
131               explicitly. Clicking on the Hud inventory button toggles the
132               image implicitly, so we don't change it.
133           @return: None"""
134        if not self.inventory_shown:
135            self.showInventory()
136            self.inventory_shown = True
137        else:
138            self.closeInventory()
139            self.inventory_shown = False
140   
141    def showInventory(self):
142        self.gui.show()
143   
144    def closeInventory(self):
145        self.gui.hide()
146
147
148class _InventoryGUI(ContainerGUIBase):
149    """Inventory GUI class"""
150    def __init__(self, controller, inventory, callbacks):
151        """Initialise the instance.
152           @param controller: Current Controller
153           @type controller: Class derived from ControllerBase
154           @type inventory: Inventory
155           @param inventory: An inventory object to be displayed and manipulated
156           @type callbacks: dict
157           @param callbacks: a dict of callbacks
158               refreshReadyImages:
159                   Function that will make the ready slots on the HUD
160                   reflect those within the inventory
161               toggleInventoryButton:
162                   Function that will toggle the state of the inventory button
163           @return: None"""
164        super(InventoryGUI, self).__init__(controller, "gui/inventory.xml")
165        self.engine = controller.engine
166        self.readyCallback = callbacks['refreshReadyImages']
167        self.toggleInventoryButtonCallback = callbacks['toggleInventoryButton']
168        self.original_cursor_id = self.engine.getCursor().getId()
169
170        self.inventory_shown = False
171        events_to_map = {}
172        self.inventory_storage = inventory
173       
174        # Buttons of inventory arranged by slots
175
176        self.slot_buttons = {'head': ('Head',), 'chest': ('Body',),
177                             'left_arm': ('LeftHand',),
178                             'right_arm': ('RightHand',),
179                             'hips' : ('Belt',), 'left_leg': ('LeftFoot',),
180                             'right_leg': ('RightFoot',),
181                             'left_hand': ('LeftHeld',),
182                             'right_hand': ('RightHeld',),
183                             'backpack': ('A1', 'A2', 'A3', 'A4', 'A5',
184                                          'B1', 'B2', 'B3', 'B4', 'B5',
185                                          'C1', 'C2', 'C3', 'C4', 'C5',
186                                          'D1', 'D2', 'D3', 'D4', 'D5'),
187                             'ready': ('Ready1', 'Ready2', 'Ready3', 'Ready4')
188        }
189        # the images that should be used for the buttons when they are "empty"
190        self.slot_empty_images = {'head':'gui/inv_images/inv_head.png',
191                                  'chest':'gui/inv_images/inv_torso.png',
192                                  'left_arm':'gui/inv_images/inv_lhand.png',
193                                  'right_arm':'gui/inv_images/inv_rhand.png',
194                                  'hips':'gui/inv_images/inv_belt.png',
195                                  'left_leg':'gui/inv_images/inv_lfoot.png',
196                                  'right_leg':'gui/inv_images/inv_rfoot.png',
197                                  'left_hand':'gui/inv_images/inv_litem.png',
198                                  'right_hand':'gui/inv_images/inv_ritem.png',
199                                  'backpack':'gui/inv_images/inv_backpack.png',
200                                  'ready':'gui/inv_images/inv_belt_pouches.png',
201                                  }
202        self.updateInventoryButtons()
203
204        for slot in self.slot_buttons:
205            for _, button in enumerate(self.slot_buttons[slot]):
206                events_to_map[button] = cbwa(self.dragDrop, button)
207                events_to_map[button + "/mouseReleased"] = \
208                                                self.showContextMenu
209        events_to_map['close_button'] = self.closeInventoryAndToggle
210        self.gui.mapEvents(events_to_map)
211        # TODO: Why the commented out code?
212        # self.resetMouseCursor()
213
214    def updateImages(self):
215        self.updateInventoryButtons()
216   
217    def updateInventoryButtons (self):
218        for slot in self.slot_buttons:
219            for index, button in enumerate(self.slot_buttons[slot]):
220                widget = self.gui.findChild(name=button)
221                widget.slot = slot
222                widget.index = index
223                widget.item = self.inventory_storage.getItemsInSlot(widget.slot,
224                                                                   widget.index)
225                self.updateImage(widget)
226               
227    def updateImage(self, button):
228        if (button.item == None):
229            image = self.slot_empty_images[button.slot]
230        else:
231            image = button.item.getInventoryThumbnail()
232        button.up_image = image
233        button.down_image = image
234        button.hover_image = image
235
236    def closeInventory(self):
237        """Close the inventory.
238           @return: None"""
239        self.gui.hide()
240
241    def closeInventoryAndToggle(self):
242        """Close the inventory screen.
243           @return: None"""
244        self.closeInventory()
245        self.toggleInventoryButtonCallback()
246        self.inventory_shown = False
247
248    def toggleInventory(self, toggleImage=True):
249        """Pause the game and enter the inventory screen, or close the
250           inventory screen and resume the game.
251           @type toggleImage: bool
252           @param toggleImage:
253               Call toggleInventoryCallback if True. Toggling via a
254               keypress requires that we toggle the Hud inventory image
255               explicitly. Clicking on the Hud inventory button toggles the
256               image implicitly, so we don't change it.
257           @return: None"""
258        if not self.inventory_shown:
259            self.showInventory()
260            self.inventory_shown = True
261        else:
262            self.closeInventory()
263            self.inventory_shown = False
264
265        if toggleImage:
266            self.toggleInventoryButtonCallback()
267
268    def showInventory(self):
269        """Show the inventory.
270           @return: None"""
271        self.updateInventoryButtons()
272        self.gui.show()               
273               
274    def dragObject(self, obj):
275        """Drag the selected object.
276           @type obj: string
277           @param obj: The name of the object within
278                       the dictionary 'self.buttons'
279           @return: None"""
280        # get the widget from the inventory with the name obj
281        drag_widget = self.gui.findChild(name = obj)
282        drag_item = drag_widget.item
283        # only drag if the widget is not empty
284        if (drag_item != None):
285            # get the item that the widget is 'storing'
286            data_drag.dragged_item = drag_widget.item
287            # get the up and down images of the widget
288            up_image = drag_widget.up_image
289            down_image = drag_widget.down_image
290            # set the mouse cursor to be the widget's image
291            self.controller.setMouseCursor(up_image.source,down_image.source)
292            data_drag.dragged_image = up_image.source
293            data_drag.dragging = True
294            data_drag.dragged_widget = drag_widget
295            data_drag.source_container = self.inventory_storage
296           
297            self.inventory_storage.takeItem(drag_widget.item)
298            # after dragging the 'item', set the widgets' images
299            # so that it has it's default 'empty' images
300            drag_widget.item = None
301            self.updateImage(drag_widget)
302           
303           
304    def dropObject(self, obj):
305        """Drops the object being dropped
306           @type obj: string
307           @param obj: The name of the object within
308                       the dictionary 'self.buttons'
309           @return: None"""
310        drop_widget = self.gui.findChild(name = obj)
311        drop_slot, drop_index = drop_widget.slot, drop_widget.index
312        replace_item = None
313        try :
314            if data_drag.dragging:
315                inventory = self.inventory_storage
316                drag_item = data_drag.dragged_item
317                #this will get the replacement item and data for drag_drop if
318                ## there is an item All ready occupying the slot
319                if not inventory.isSlotEmpty(drop_slot, drop_index):
320                    #get the item and then remove it from the inventory
321                    replace_item = inventory.getItemsInSlot \
322                                                (drop_slot, drop_index)
323                    self.dragObject(obj)
324                self.inventory_storage.moveItemToSlot(drag_item,
325                                                      drop_slot,
326                                                      drop_index)
327                   
328            if drop_widget.slot == 'ready':
329                self.readyCallback()
330           
331            if replace_item == None:
332                self.controller.resetMouseCursor()
333                data_drag.dragging = False
334        except Container.TooBig :
335            print("%s too big to fit into %s" % (data_drag.dragged_item,
336                                                 drop_widget.slot))
337        except (Container.SlotBusy, Container.ItemSelf):
338            pass
339        self.updateInventoryButtons()
340             
341    def createMenuItems(self, item, actions):
342        """Creates context menu items for the InventoryGUI"""
343        menu_actions = super(InventoryGUI, self).createMenuItems(item, actions)
344        param_dict = {}
345        param_dict["controller"] = self.controller
346        param_dict["commands"] = {}
347        param_dict["item"] = item
348        param_dict["container_gui"] = self
349        menu_actions.append(["Drop",
350                             "Drop", 
351                             self.executeMenuItem, 
352                             ACTIONS["DropFromInventory"](**param_dict)])       
353        return menu_actions
354   
355    def getImage(self, name):
356        """Return a current image from the inventory
357           @type name: string
358           @param name: name of image to get
359           @return: None"""
360        return self.gui.findChild(name = name)
Note: See TracBrowser for help on using the repository browser.