all repos — Legends-RPG @ 0802f998231bfac166df0e8174a45a00e71776da

A fantasy mini-RPG built with Python and Pygame.

data/shopgui.py (view raw)

  1"""
  2This class controls the textbox GUI for any shop state.
  3A Gui object is created and updated by the shop state.
  4"""
  5import sys
  6import pickle
  7import pygame as pg
  8from . import setup, observer
  9from . components import textbox
 10from . import constants as c
 11
 12
 13#Python 2/3 compatibility.
 14if sys.version_info[0] == 2:
 15    import cPickle
 16    pickle = cPickle 
 17
 18
 19class Gui(object):
 20    """Class that controls the GUI of the shop state"""
 21    def __init__(self, level):
 22        self.level = level
 23        self.game_data = self.level.game_data
 24        self.level.game_data['last direction'] = 'down'
 25        self.SFX_observer = observer.SoundEffects()
 26        self.observers = [self.SFX_observer]
 27        self.sellable_items = level.sell_items
 28        self.player_inventory = level.game_data['player inventory']
 29        self.name = level.name
 30        self.state = 'dialogue'
 31        self.no_selling = ['Inn', 'Magic Shop']
 32        self.weapon_list = ['Long Sword', 'Rapier']
 33        self.armor_list = ['Chain Mail', 'Wooden Shield']
 34        self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22)
 35        self.index = 0
 36        self.timer = 0.0
 37        self.allow_input = False
 38        self.items = level.items
 39        self.item_to_be_sold = None
 40        self.item_to_be_purchased = None
 41        self.dialogue = level.dialogue
 42        self.accept_dialogue = level.accept_dialogue
 43        self.accept_sale_dialogue = level.accept_sale_dialogue
 44        self.arrow = textbox.NextArrow()
 45        self.selection_arrow = textbox.NextArrow()
 46        self.arrow_pos1 = (50, 475)
 47        self.arrow_pos2 = (50, 515)
 48        self.arrow_pos3 = (50, 555)
 49        self.arrow_pos4 = (50, 495)
 50        self.arrow_pos5 = (50, 535)
 51        self.arrow_pos_list = [self.arrow_pos1, self.arrow_pos2, self.arrow_pos3]
 52        self.two_arrow_pos_list = [self.arrow_pos4, self.arrow_pos5]
 53        self.arrow_index = 0
 54        self.selection_arrow.rect.topleft = self.arrow_pos1
 55        self.dialogue_box = self.make_dialogue_box(self.dialogue, self.index)
 56        self.gold_box = self.make_gold_box()
 57        if self.name in self.no_selling:
 58            choices = self.items[0]['dialogue']
 59        else:
 60            choices = ['Buy', 'Sell', 'Leave']
 61        self.selection_box = self.make_selection_box(choices)
 62        self.state_dict = self.make_state_dict()
 63
 64    def notify(self, event):
 65        """
 66        Notify all observers of event.
 67        """
 68        for observer in self.observers:
 69            observer.on_notify(event)
 70
 71    def make_dialogue_box(self, dialogue_list, index):
 72        """
 73        Make the sprite that controls the dialogue.
 74        """
 75        image = setup.GFX['dialoguebox']
 76        rect = image.get_rect()
 77        surface = pg.Surface(rect.size)
 78        surface.set_colorkey(c.BLACK)
 79        surface.blit(image, rect)
 80        dialogue = self.font.render(dialogue_list[index],
 81                                    True,
 82                                    c.NEAR_BLACK)
 83        dialogue_rect = dialogue.get_rect(left=50, top=50)
 84        surface.blit(dialogue, dialogue_rect)
 85        sprite = pg.sprite.Sprite()
 86        sprite.image = surface
 87        sprite.rect = rect
 88        self.check_to_draw_arrow(sprite)
 89
 90        return sprite
 91
 92    def check_to_draw_arrow(self, sprite):
 93        """
 94        Blink arrow if more text needs to be read.
 95        """
 96        if self.index < len(self.dialogue) - 1:
 97            sprite.image.blit(self.arrow.image, self.arrow.rect)
 98
 99    def make_gold_box(self):
100        """Make the box to display total gold"""
101        image = setup.GFX['goldbox']
102        rect = image.get_rect(bottom=608, right=800)
103
104        surface = pg.Surface(rect.size)
105        surface.set_colorkey(c.BLACK)
106        surface.blit(image, (0, 0))
107        gold = self.player_inventory['GOLD']['quantity']
108        text = 'Gold: ' + str(gold)
109        text_render = self.font.render(text, True, c.NEAR_BLACK)
110        text_rect = text_render.get_rect(x=80, y=60)
111
112        surface.blit(text_render, text_rect)
113
114        sprite = pg.sprite.Sprite()
115        sprite.image = surface
116        sprite.rect = rect
117
118        return sprite
119
120    def make_selection_box(self, choices):
121        """Make the box for the player to select options"""
122        image = setup.GFX['shopbox']
123        rect = image.get_rect(bottom=608)
124
125        surface = pg.Surface(rect.size)
126        surface.set_colorkey(c.BLACK)
127        surface.blit(image, (0, 0))
128
129        if len(choices) == 2:
130            choice1 = self.font.render(choices[0], True, c.NEAR_BLACK)
131            choice1_rect = choice1.get_rect(x=200, y=35)
132            choice2 = self.font.render(choices[1], True, c.NEAR_BLACK)
133            choice2_rect = choice2.get_rect(x=200, y=75)
134
135            surface.blit(choice1, choice1_rect)
136            surface.blit(choice2, choice2_rect)
137
138        elif len(choices) == 3:
139            choice1 = self.font.render(choices[0], True, c.NEAR_BLACK)
140            choice1_rect = choice1.get_rect(x=200, y=15)
141            choice2 = self.font.render(choices[1], True, c.NEAR_BLACK)
142            choice2_rect = choice2.get_rect(x=200, y=55)
143            choice3 = self.font.render(choices[2], True, c.NEAR_BLACK)
144            choice3_rect = choice3.get_rect(x=200, y=95)
145
146            surface.blit(choice1, choice1_rect)
147            surface.blit(choice2, choice2_rect)
148            surface.blit(choice3, choice3_rect)
149
150        sprite = pg.sprite.Sprite()
151        sprite.image = surface
152        sprite.rect = rect
153
154        return sprite
155
156
157    def make_state_dict(self):
158        """Make the state dictionary for the GUI behavior"""
159        state_dict = {'dialogue': self.control_dialogue,
160                      'select': self.make_selection,
161                      'confirmpurchase': self.confirm_purchase,
162                      'confirmsell': self.confirm_sell,
163                      'reject': self.reject_insufficient_gold,
164                      'accept': self.accept_purchase,
165                      'acceptsell': self.accept_sale,
166                      'hasitem': self.has_item,
167                      'buysell': self.buy_sell,
168                      'sell': self.sell_items,
169                      'cantsell': self.cant_sell,
170                      'cantsellequippedweapon': self.cant_sell_equipped_weapon,
171                      'cantsellequippedarmor': self.cant_sell_equipped_armor}
172
173        return state_dict
174
175    def control_dialogue(self, keys, current_time):
176        """Control the dialogue boxes"""
177        self.dialogue_box = self.make_dialogue_box(self.dialogue, self.index)
178
179        if self.index < (len(self.dialogue) - 1) and self.allow_input:
180            if keys[pg.K_SPACE]:
181                self.index += 1
182                self.allow_input = False
183
184                if self.index == (len(self.dialogue) - 1):
185                    self.state = self.begin_new_transaction()
186                self.notify(c.CLICK2)
187
188        if not keys[pg.K_SPACE]:
189            self.allow_input = True
190
191    def begin_new_transaction(self):
192        """Set state to buysell or select, depending if the shop
193        is a Inn/Magic shop or not"""
194        if self.level.name in self.no_selling:
195            state = 'select'
196        else:
197            state = 'buysell'
198
199        return state
200
201    def make_selection(self, keys, current_time):
202        """Control the selection"""
203        choices = []
204        for item in self.items:
205            choices.append(item['dialogue'])
206        if self.name in self.no_selling:
207            choices.append('Leave')
208        else:
209            choices.append('Cancel')
210        self.dialogue_box = self.make_dialogue_box(self.dialogue, self.index)
211        self.selection_box = self.make_selection_box(choices)
212        self.gold_box = self.make_gold_box()
213
214        if len(choices) == 2:
215            arrow_list = self.two_arrow_pos_list
216        elif len(choices) == 3:
217            arrow_list = self.arrow_pos_list
218        else:
219            arrow_list = None
220            AssertionError('Only two items supported')
221
222        self.selection_arrow.rect.topleft = arrow_list[self.arrow_index]
223
224
225        if keys[pg.K_DOWN] and self.allow_input:
226            if self.arrow_index < (len(choices) - 1):
227                self.arrow_index += 1
228                self.allow_input = False
229                self.notify(c.CLICK)
230        elif keys[pg.K_UP] and self.allow_input:
231            if self.arrow_index > 0:
232                self.arrow_index -= 1
233                self.allow_input = False
234                self.notify(c.CLICK)
235        elif keys[pg.K_SPACE] and self.allow_input:
236            if self.arrow_index == 0:
237                self.state = 'confirmpurchase'
238                self.item_to_be_purchased = self.items[0]
239
240            elif self.arrow_index == 1 and len(choices) == 3:
241                self.state = 'confirmpurchase'
242                self.item_to_be_purchased = self.items[1]
243
244            else:
245                if self.level.name in self.no_selling:
246                    self.level.state = 'transition out'
247                    self.game_data['last state'] = self.level.name
248                else:
249                    self.state = 'buysell'
250
251            self.notify(c.CLICK2)
252            self.arrow_index = 0
253            self.allow_input = False
254
255        if not keys[pg.K_SPACE] and not keys[pg.K_UP] and not keys[pg.K_DOWN]:
256            self.allow_input = True
257
258
259
260    def confirm_purchase(self, keys, current_time):
261        """Confirm selection state for GUI"""
262        dialogue = ['Are you sure?']
263        choices = ['Yes', 'No']
264        self.selection_box = self.make_selection_box(choices)
265        self.gold_box = self.make_gold_box()
266        self.dialogue_box = self.make_dialogue_box(dialogue, 0)
267        self.selection_arrow.rect.topleft = self.two_arrow_pos_list[self.arrow_index]
268
269        if keys[pg.K_DOWN] and self.allow_input:
270            if self.arrow_index < (len(choices) - 1):
271                self.arrow_index += 1
272                self.allow_input = False
273                self.notify(c.CLICK)
274        elif keys[pg.K_UP] and self.allow_input:
275            if self.arrow_index > 0:
276                self.arrow_index -= 1
277                self.allow_input = False
278                self.notify(c.CLICK)
279        elif keys[pg.K_SPACE] and self.allow_input:
280            if self.arrow_index == 0:
281                self.buy_item()
282            elif self.arrow_index == 1:
283                self.state = self.begin_new_transaction()
284            self.notify(c.CLICK2)
285            self.arrow_index = 0
286            self.allow_input = False
287
288        if not keys[pg.K_SPACE] and not keys[pg.K_DOWN] and not keys[pg.K_UP]:
289            self.allow_input = True
290
291
292    def buy_item(self):
293        """Attempt to allow player to purchase item"""
294        item = self.item_to_be_purchased
295
296        self.player_inventory['GOLD']['quantity'] -= item['price']
297
298        if self.player_inventory['GOLD']['quantity'] < 0:
299            self.player_inventory['GOLD']['quantity'] += item['price']
300            self.state = 'reject'
301        else:
302            if (item['type'] in self.player_inventory and
303                        not self.name == c.POTION_SHOP):
304                self.state = 'hasitem'
305                self.player_inventory['GOLD']['quantity'] += item['price']
306            else:
307                self.notify(c.CLOTH_BELT)
308                self.state = 'accept'
309                self.add_player_item(item)
310
311
312    def add_player_item(self, item):
313        """
314        Add item to player's inventory.
315        """
316        item_type = item['type']
317        quantity = item['quantity']
318        value = item['price']
319        power = item['power']
320        magic_list = ['Cure', 'Fire Blast']
321        player_armor = ['Chain Mail', 'Wooden Shield']
322        player_weapons = ['Rapier', 'Long Sword']
323        player_items = self.level.game_data['player inventory']
324        player_health = self.level.game_data['player stats']['health']
325        player_magic = self.level.game_data['player stats']['magic']
326        equipped_armor = self.level.game_data['player inventory']['equipped armor']
327
328        item_to_add = {'quantity': quantity,
329                       'value': value,
330                       'power': power}
331
332        if item_type in magic_list:
333            item_to_add = {'magic points': item['magic points'],
334                           'power': item['power']}
335            player_items[item_type] = item_to_add
336        if item_type in player_armor:
337            equipped_armor.append(item_type)
338        if item_type in player_weapons:
339            player_items['equipped weapon'] = item_type
340        if item_type in player_items and item_type not in magic_list:
341            player_items[item_type]['quantity'] += quantity
342        elif quantity > 0:
343            player_items[item_type] = item_to_add
344        elif item_type == 'room':
345            player_health['current'] = player_health['maximum']
346            player_magic['current'] = player_magic['maximum']
347            pickle.dump(self.game_data, open( "save.p", "wb"))
348
349    def confirm_sell(self, keys, current_time):
350        """
351        Confirm player wants to sell item.
352        """
353        dialogue = ['Are you sure?']
354        choices = ['Yes', 'No']
355        self.dialogue_box = self.make_dialogue_box(dialogue, 0)
356        self.selection_box = self.make_selection_box(choices)
357        self.selection_arrow.rect.topleft = self.two_arrow_pos_list[self.arrow_index]
358
359        if keys[pg.K_DOWN] and self.allow_input:
360            if self.arrow_index < (len(choices) - 1):
361                self.arrow_index += 1
362                self.allow_input = False
363                self.notify(c.CLICK)
364        elif keys[pg.K_UP] and self.allow_input:
365            if self.arrow_index > 0:
366                self.arrow_index -= 1
367                self.allow_input = False
368                self.notify(c.CLICK)
369        elif keys[pg.K_SPACE] and self.allow_input:
370            if self.arrow_index == 0:
371                self.sell_item_from_inventory()
372            elif self.arrow_index == 1:
373                self.state = self.begin_new_transaction()
374                self.notify(c.CLICK2)
375            self.allow_input = False
376            self.arrow_index = 0
377
378        if not keys[pg.K_SPACE] and not keys[pg.K_UP] and not keys[pg.K_DOWN]:
379            self.allow_input = True
380
381
382    def sell_item_from_inventory(self):
383        """
384        Allow player to sell item to shop.
385        """
386        item_price = self.item_to_be_sold['price']
387        item_name = self.item_to_be_sold['type']
388
389        if item_name in self.weapon_list:
390            if item_name == self.game_data['player inventory']['equipped weapon']:
391                self.state = 'cantsellequippedweapon'
392            else:
393                self.notify(c.CLOTH_BELT)
394                self.sell_inventory_data_adjust(item_price, item_name)
395
396        elif item_name in self.armor_list:
397            if item_name in self.game_data['player inventory']['equipped armor']:
398                self.state = 'cantsellequippedarmor'
399            else:
400                self.notify(c.CLOTH_BELT)
401                self.sell_inventory_data_adjust(item_price, item_name)
402        else:
403            self.notify(c.CLOTH_BELT)
404            self.sell_inventory_data_adjust(item_price, item_name)
405
406    def sell_inventory_data_adjust(self, item_price, item_name):
407        """
408        Add gold and subtract item during sale.
409        """
410        self.player_inventory['GOLD']['quantity'] += (item_price / 2)
411        self.state = 'acceptsell'
412        if self.player_inventory[item_name]['quantity'] > 1:
413            self.player_inventory[item_name]['quantity'] -= 1
414        else:
415            del self.player_inventory[self.item_to_be_sold['type']]
416
417    def reject_insufficient_gold(self, keys, current_time):
418        """Reject player selection if they do not have enough gold"""
419        dialogue = ["You don't have enough gold!"]
420        self.dialogue_box = self.make_dialogue_box(dialogue, 0)
421
422        if keys[pg.K_SPACE] and self.allow_input:
423            self.notify(c.CLICK2)
424            self.state = self.begin_new_transaction()
425            self.selection_arrow.rect.topleft = self.arrow_pos1
426            self.allow_input = False
427
428        if not keys[pg.K_SPACE]:
429            self.allow_input = True
430
431    def accept_purchase(self, keys, current_time):
432        """Accept purchase and confirm with message"""
433        self.dialogue_box = self.make_dialogue_box(self.accept_dialogue, 0)
434        self.gold_box = self.make_gold_box()
435
436        if keys[pg.K_SPACE] and self.allow_input:
437            self.notify(c.CLICK2)
438            self.state = self.begin_new_transaction()
439            self.selection_arrow.rect.topleft = self.arrow_pos1
440            self.allow_input = False
441
442        if not keys[pg.K_SPACE]:
443            self.allow_input = True
444
445    def accept_sale(self, keys, current_time):
446        """Confirm to player that item was sold"""
447        self.dialogue_box = self.make_dialogue_box(self.accept_sale_dialogue, 0)
448        self.gold_box = self.make_gold_box()
449
450        if keys[pg.K_SPACE] and self.allow_input:
451            self.notify(c.CLICK2)
452            self.state = self.begin_new_transaction()
453            self.selection_arrow.rect.topleft = self.arrow_pos1
454            self.allow_input = False
455
456        if not keys[pg.K_SPACE]:
457            self.allow_input = True
458
459
460    def has_item(self, keys, current_time):
461        """Tell player he has item already"""
462        dialogue = ["You have that item already."]
463        self.dialogue_box = self.make_dialogue_box(dialogue, 0)
464
465        if keys[pg.K_SPACE] and self.allow_input:
466            self.state = self.begin_new_transaction()
467            self.selection_arrow.rect.topleft = self.arrow_pos1
468            self.allow_input = False
469            self.notify(c.CLICK2)
470
471        if not keys[pg.K_SPACE]:
472            self.allow_input = True
473
474
475    def buy_sell(self, keys, current_time):
476        """Ask player if they want to buy or sell something"""
477        dialogue = ["Would you like to buy or sell an item?"]
478        choices = ['Buy', 'Sell', 'Leave']
479        self.dialogue_box = self.make_dialogue_box(dialogue, 0)
480        self.selection_box = self.make_selection_box(choices)
481        self.selection_arrow.rect.topleft = self.arrow_pos_list[self.arrow_index]
482
483        if keys[pg.K_DOWN] and self.allow_input:
484            if self.arrow_index < (len(self.arrow_pos_list) - 1):
485                self.arrow_index += 1
486                self.allow_input = False
487                self.notify(c.CLICK)
488
489        elif keys[pg.K_UP] and self.allow_input:
490            if self.arrow_index > 0:
491                self.arrow_index -= 1
492                self.allow_input = False
493                self.notify(c.CLICK)
494        elif keys[pg.K_SPACE] and self.allow_input:
495            if self.arrow_index == 0:
496                self.state = 'select'
497                self.allow_input = False
498                self.arrow_index = 0
499            elif self.arrow_index == 1:
500                if self.check_for_sellable_items():
501                    self.state = 'sell'
502                    self.allow_input = False
503                    self.arrow_index = 0
504                else:
505                    self.state = 'cantsell'
506                    self.allow_input = False
507                    self.arrow_index = 0
508            else:
509                self.level.state = 'transition out'
510                self.game_data['last state'] = self.level.name
511
512            self.arrow_index = 0
513            self.notify(c.CLICK2)
514
515        if not keys[pg.K_SPACE] and not keys[pg.K_DOWN] and not keys[pg.K_UP]:
516            self.allow_input = True
517
518    def check_for_sellable_items(self):
519        """Check for sellable items"""
520        for item in self.player_inventory:
521            if item in self.sellable_items:
522                return True
523        else:
524            return False
525
526    def sell_items(self, keys, current_time):
527        """Have player select items to sell"""
528        dialogue = ["What would you like to sell?"]
529        choices = []
530        item_list = []
531        for item in self.items:
532            if item['type'] in self.player_inventory:
533                name = item['type']
534                price = " (" + str(item['price'] / 2) + " gold)"
535                choices.append(name + price)
536                item_list.append(name)
537        choices.append('Cancel')
538        self.dialogue_box = self.make_dialogue_box(dialogue, 0)
539        self.selection_box = self.make_selection_box(choices)
540
541        if len(choices) == 2:
542            self.selection_arrow.rect.topleft = self.two_arrow_pos_list[self.arrow_index]
543        elif len(choices) == 3:
544            self.selection_arrow.rect.topleft = self.arrow_pos_list[self.arrow_index]
545
546        if keys[pg.K_DOWN] and self.allow_input:
547            if self.arrow_index < (len(self.arrow_pos_list) - 1):
548                self.arrow_index += 1
549                self.allow_input = False
550                self.notify(c.CLICK)
551        elif keys[pg.K_UP] and self.allow_input:
552            if self.arrow_index > 0:
553                self.arrow_index -= 1
554                self.allow_input = False
555                self.notify(c.CLICK)
556        elif keys[pg.K_SPACE] and self.allow_input:
557            if self.arrow_index == 0:
558                self.state = 'confirmsell'
559                self.allow_input = False
560                for item in self.items:
561                    if item['type'] == item_list[0]:
562                        self.item_to_be_sold = item
563
564            elif self.arrow_index == 1 and len(choices) == 3:
565                self.state = 'confirmsell'
566                self.allow_input = False
567                for item in self.items:
568                    if item['type'] == item_list[1]:
569                        self.item_to_be_sold = item
570            else:
571                self.state = 'buysell'
572                self.allow_input = False
573            self.arrow_index = 0
574            self.notify(c.CLICK2)
575
576        if not keys[pg.K_SPACE] and not keys[pg.K_DOWN] and not keys[pg.K_UP]:
577            self.allow_input = True
578
579
580    def cant_sell(self, keys, current_time):
581        """Do not allow player to sell anything"""
582        dialogue = ["You don't have anything to sell!"]
583        self.dialogue_box = self.make_dialogue_box(dialogue, 0)
584
585        if keys[pg.K_SPACE] and self.allow_input:
586            self.state = 'buysell'
587            self.allow_input = False
588            self.notify(c.CLICK2)
589
590
591        if not keys[pg.K_SPACE]:
592            self.allow_input = True
593
594    def cant_sell_equipped_weapon(self, keys, *args):
595        """
596        Do not sell weapon the player has equipped.
597        """
598        dialogue = ["You can't sell an equipped weapon."]
599        self.dialogue_box = self.make_dialogue_box(dialogue, 0)
600
601        if keys[pg.K_SPACE] and self.allow_input:
602            self.state = 'buysell'
603            self.allow_input = False
604            self.notify(c.CLICK2)
605
606        if not keys[pg.K_SPACE]:
607            self.allow_input = True
608
609    def cant_sell_equipped_armor(self, keys, *args):
610        """
611        Do not sell armor the player has equipped.
612        """
613        dialogue = ["You can't sell equipped armor."]
614        self.dialogue_box = self.make_dialogue_box(dialogue, 0)
615
616        if keys[pg.K_SPACE] and self.allow_input:
617            self.state = 'buysell'
618            self.allow_input = False
619
620        if not keys[pg.K_SPACE]:
621            self.allow_input = True
622
623
624
625    def update(self, keys, current_time):
626        """Updates the shop GUI"""
627        state_function = self.state_dict[self.state]
628        state_function(keys, current_time)
629
630
631    def draw(self, surface):
632        """Draw GUI to level surface"""
633        state_list1 = ['dialogue', 'reject', 'accept', 'hasitem']
634        state_list2 = ['select', 'confirmpurchase', 'buysell', 'sell', 'confirmsell']
635
636        surface.blit(self.dialogue_box.image, self.dialogue_box.rect)
637        surface.blit(self.gold_box.image, self.gold_box.rect)
638        if self.state in state_list2:
639            surface.blit(self.selection_box.image, self.selection_box.rect)
640            surface.blit(self.selection_arrow.image, self.selection_arrow.rect)
641