all repos — Legends-RPG @ 680235e09a98538dcc6f7165b3f8186889ed6ba5

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