class GameWin(_State): def __init__(self, controller): super(GameWin, self).__init__(controller) font = prog_constants.FONTS["Fixedsys500c"] color = constants.LOW_LIGHT_GREEN sr = constants.SCREEN_RECT self.labels = [ Label(font, 48, "Congratulations", color, {"midtop": (sr.centerx, 200)}), Label(font, 48, "Your cities are saved", color, {"midtop": (sr.centerx, 300)}) ] self.buttons = ButtonGroup() NeonButton((373, 630), "OK", 32, self.to_title, None, self.buttons) def to_title(self, *args): self.done = True self.next = "TITLE" self.player.clear_save() def startup(self, persistent): self.persist = persistent self.player = self.persist["player"] def get_event(self, event, scale): self.buttons.get_event(event) def update(self, surface, keys, current_time, dt, scale): self.buttons.update(scaled_mouse_pos(scale)) self.draw(surface) def draw(self, surface): surface.fill(constants.BACKGROUND_BASE) for label in self.labels: label.draw(surface) self.buttons.draw(surface)
class PlayerTurn(BlackjackState): def __init__(self): super(PlayerTurn, self).__init__() pos = (self.screen_rect.right - (NeonButton.width + 10), self.screen_rect.bottom - (NeonButton.height + 15)) self.make_buttons() self.lobby_button = NeonButton(pos, "Lobby", self.back_to_lobby, None, self.buttons, bindings=[pg.K_ESCAPE]) self.last_click = 0 def play_deal_sound(self): choice(self.game.deal_sounds).play() def make_buttons(self): side_margin = 10 vert_space = 12 left = self.screen_rect.right - (NeonButton.width + side_margin) top = self.screen_rect.bottom - ( (NeonButton.height * 5) + vert_space * 5) self.hit_button = NeonButton((left, top), "Hit", self.hit_click) top += NeonButton.height + vert_space self.stand_button = NeonButton((left, top), "Stand", self.stand) top += NeonButton.height + vert_space self.double_down_button = NeonButton((left, top), "Double", self.double_down) top += NeonButton.height + vert_space self.split_button = NeonButton((left, top), "Split", self.split_hand) self.buttons = ButtonGroup(self.hit_button, self.stand_button, self.double_down_button, self.split_button) def hit_click(self, *args): self.hit(self.game.current_player_hand) def hit(self, hand): """Draw a card from deck and add to hand.""" self.play_deal_sound() self.game.player.add_slot(hand) card = self.game.deck.draw_card() card.face_up = True destination = hand.slots[-1] dest_x, dest_y = destination.topleft hand.cards.append(card) dur = get_distance(destination.center, card.pos) * 20 // card.speed ani = Animation(x=dest_x, y=dest_y, duration=dur, round_values=True) ani.start(card.rect) self.animations.add(ani) def stand(self, *args): """Player is done with this hand.""" self.game.current_player_hand.final = True def double_down(self, *args): """ Double player's bet on the hand, deal one more card and finalize hand. """ hand = self.game.current_player_hand bet = hand.bet.get_chip_total() bet_chips = self.game.player.chip_pile.withdraw_chips(bet) hand.bet.add_chips(bet_chips) self.hit(hand) hand.final = True def split_hand(self, *args): """ Split player's hand into two hands, adjust hand locations and deal a new card to both hands. """ player = self.game.player hand = self.game.current_player_hand bet = hand.bet.get_chip_total() hand.slots = hand.slots[:-1] move = ((self.screen_rect.left + 50) - hand.slots[0].left, 0) player.move_hands(move) p_slot = player.hands[-1].slots[0] hand_slot = p_slot.move(int(prepare.CARD_SIZE[0] * 3.5), 0) card = hand.cards.pop() new_hand = Hand(hand_slot.topleft, [card], player.chip_pile.withdraw_chips(bet)) new_hand.slots = [hand_slot] card.rect.topleft = hand_slot.topleft player.hands.append(new_hand) player.add_slot(new_hand) self.play_deal_sound() self.game.player.add_slot(hand) card1 = self.game.deck.draw_card() dest = hand.slots[-1] dest_x, dest_y = dest.topleft card1.face_up = True hand.cards.append(card1) ani = Animation(x=dest_x, y=dest_y, duration=1000, round_values=True) ani.start(card1.rect) card2 = self.game.deck.draw_card() dest = new_hand.slots[-1] dest_x, dest_y = dest.topleft card2.face_up = True new_hand.cards.append(card2) ani2 = Animation(x=dest_x, y=dest_y, duration=1000, delay=500, round_values=True) ani2.start(card2.rect) ani3 = Task(self.play_deal_sound, 1500) self.animations.add(ani, ani2, ani3) def startup(self, game): self.game = game self.animations = pg.sprite.Group() def get_event(self, event, scale): now = pg.time.get_ticks() span = now - self.last_click if event.type == pg.QUIT: self.back_to_lobby() if event.type == pg.MOUSEBUTTONUP: self.last_click = now self.game.get_event(event) if span > 300: if self.window: self.window.get_event(event) else: self.buttons.get_event(event) def update(self, surface, keys, current_time, dt, scale): mouse_pos = tools.scaled_mouse_pos(pg.mouse.get_pos(), scale) g = self.game g.update(dt, mouse_pos) self.split_button.active = False self.split_button.visible = False self.double_down_button.active = False self.double_down_button.visible = False hand = g.current_player_hand hand_score = hand.best_score() if hand_score is None: hand.busted = True hand.final = True chip_total = g.player.chip_pile.get_chip_total() bet = hand.bet.get_chip_total() if len(hand.cards) == 2 and len(g.player.hands) < 2: c1 = hand.card_values[hand.cards[0].value] c2 = hand.card_values[hand.cards[1].value] if c1 == c2 and chip_total >= bet: self.split_button.active = True self.split_button.visible = True if len(hand.cards) == 2: if hand_score == 21: hand.blackjack = True hand.final = True elif chip_total >= bet: self.double_down_button.active = True self.double_down_button.visible = True if hand.final: if all([hand.final for hand in g.player.hands]): if not self.animations: g.dealer.hand.cards[0].face_up = True self.next = "Dealer Turn" self.done = True else: next_hand = [x for x in g.player.hands if not x.final][0] g.current_player_hand = next_hand if self.window: self.window.update(mouse_pos) if self.window.done: self.window = None else: self.buttons.update(mouse_pos) self.animations.update(dt) def draw_hand_frame(self, surface): hand = self.game.current_player_hand rects = [x for x in hand.slots] pg.draw.rect(surface, pg.Color("gold3"), hand.slots[0].unionall(rects).inflate(8, 8), 3) def draw(self, surface): g = self.game surface.fill(prepare.FELT_GREEN) for label in g.labels: label.draw(surface) g.player.draw_hand_bets(surface) g.player.chip_pile.draw(surface) g.chip_total_label.draw(surface) g.dealer.draw_hand(surface) g.deck.draw(surface) g.chip_rack.draw(surface) self.buttons.draw(surface) self.lobby_button.draw(surface) g.player.draw_hands(surface) self.draw_advisor(surface) self.draw_hand_frame(surface) self.game.player.chip_pile.draw(surface) self.window and self.window.draw(surface)
class TitleScreen(data.state.State): """ Initial state of the game. Introduces the game and lets user load a saved game if there's one present. """ name = "title_screen" def __init__(self): super(TitleScreen, self).__init__() self.screen_rect = pg.Rect((0, 0), prepare.RENDER_SIZE) self.marquees = [] self.scrollers, self.marquees = self.make_titles() self.buttons = ButtonGroup() self.new_game, self.load_game = self.make_buttons() self.lights = self.make_spotlights() self.use_music_handler = False def make_spotlights(self): lights = pg.sprite.LayeredDirty() y = self.screen_rect.bottom + 20 for i in range(1, 5): start = 0 if i % 2 else 0.5 pos = (i * self.screen_rect.w / 5, y) spotlight.SpotLight(pos, 4, 140, start, lights) return lights def make_titles(self): font = prepare.FONTS["Saniretro"] scrollers = pg.sprite.Group() marquees = pg.sprite.Group() pos = {"right": 0, "centery": self.screen_rect.y + 123} Scroller(pos, prepare.GFX["pyrollers_shiny"], 0.6, scrollers) pos = {"center": (self.screen_rect.centerx, pos["centery"])} MarqueeFrame(pos, prepare.GFX["pyrollers_shiny"], 20, 120, marquees) pos = {"left": self.screen_rect.w, "centery": 370} Scroller(pos, prepare.GFX["casino_shiny"], -0.6, scrollers) pos = {"center": (self.screen_rect.centerx, pos["centery"])} MarqueeFrame(pos, prepare.GFX["casino_shiny"], 20, 120, marquees) return scrollers, marquees def make_buttons(self): x = self.screen_rect.centerx - (NeonButton.width // 2) y = 600 new_game = NeonButton((x, y), "New", self.load_or_new, False, self.buttons, visible=False) y = new_game.rect.bottom + 50 load_game = NeonButton((x, y), "Load", self.load_or_new, True, self.buttons, visible=False) return new_game, load_game def load_or_new(self, try_to_load_data): if try_to_load_data: self.persist = self.controller.load_persist_from_disk() self.next = "lobby" self.done = True def get_event(self, event, scale): if event.type == pg.QUIT: self.done = True self.quit = True elif event.type == pg.MOUSEBUTTONDOWN and event.button == 1: for scroller in self.scrollers: scroller.done = True elif event.type == pg.KEYUP: if event.key == pg.K_ESCAPE: self.done = True self.quit = True self.buttons.get_event(event) def cleanup(self): spotlight.SpotLight.clear_cache() # Clear cache to reclaim memory. return super(TitleScreen, self).cleanup() def update(self, surface, keys, current_time, dt, scale): mouse_pos = tools.scaled_mouse_pos(scale) self.buttons.update(mouse_pos) self.scrollers.update(self.screen_rect, dt) if self.scrollers: if all(scroller.done for scroller in self.scrollers): self.scrollers.empty() else: self.marquees.update(dt) self.new_game.visible = True self.load_game.visible = self.controller.saved_stats_are_available self.lights.update(dt) self.draw(surface, dt) def draw(self, surface, dt): surface.fill(prepare.BACKGROUND_BASE) self.scrollers.draw(surface) if not self.scrollers: self.marquees.draw(surface) self.lights.draw(surface) self.buttons.draw(surface)
class StatsMenu(data.state.State): """ This state allows the player to choose which game's stats they want to view or return to the lobby. """ name = "stats_menu" def __init__(self): super(StatsMenu, self).__init__() self.font = prepare.FONTS["Saniretro"] self.title = None self.buttons = ButtonGroup() self.labels = [] self.lines = [] self.use_music_handler = False def collect_games_with_stats(self): for name, scene in self.controller.query_all_states().items(): if hasattr(scene, 'initialize_stats'): yield name def make_labels(self): self.labels = [] cash = self.player.cash balance = self.player.account_balance assets = cash + balance starting_cash = prepare.MONEY profit = assets - starting_cash label_info = [("Cash", cash, 110), ("Account", balance, 150), ("Assets", assets, 198), ("Starting Cash", -starting_cash, 238), ("Profit", profit, 283)] left = 500 right = 900 for name, value, topy in label_info: label1 = Label(self.font, 36, name, "white", {"topleft": (left, topy)}) color = "darkgreen" if value >= 0 else "darkred" label2 = Label(self.font, 36, "{:.2f}".format(value), color, {"topright": (right, topy)}) self.labels.extend([label1, label2]) self.lines = [((left, 193), (right, 193)), ((left, 280), (right, 280))] def make_buttons(self, games, screen_rect, col=2): spacer_x = 20 spacer_y = 20 start_y = 410 start_x = (screen_rect.w-NeonButton.width*col-spacer_x*(col-1))//2 buttons = ButtonGroup() for i,game in enumerate(games): y,x = divmod(i, col) pos = (start_x+x*(NeonButton.width+spacer_x), start_y+y*(NeonButton.height+spacer_y)) button = NeonButton(pos, game, self.view_game_stats, game, buttons) pos = (screen_rect.centerx-(NeonButton.width//2), screen_rect.bottom-(NeonButton.height+10)) NeonButton(pos, "Lobby", self.back_to_lobby, None, buttons, bindings=[pg.K_ESCAPE]) return buttons def back_to_lobby(self, *args): self.done = True self.next = "lobby" def view_game_stats(self, game): self.persist["current_game_stats"] = game self.next = "stats_screen" self.done = True def startup(self, current_time, persistent): screen_rect = pg.Rect((0, 0), prepare.RENDER_SIZE) self.title = Label(self.font, 64, "Statistics", "darkred", {"midtop":(screen_rect.centerx, screen_rect.top+10)}) self.start_time = current_time self.persist = persistent self.player = self.persist["casino_player"] games = self.collect_games_with_stats() self.buttons = self.make_buttons(games, screen_rect, 3) self.make_labels() def get_event(self, event, scale=(1,1)): if event.type == pg.QUIT: self.done = True self.next = "lobby" self.buttons.get_event(event) def draw(self, surface): surface.fill(prepare.BACKGROUND_BASE) self.title.draw(surface) self.buttons.draw(surface) for label in self.labels: label.draw(surface) for line in self.lines: pg.draw.line(surface, pg.Color("white"), *line) def update(self, surface, keys, current_time, dt, scale): mouse_pos = tools.scaled_mouse_pos(scale) self.buttons.update(mouse_pos) self.draw(surface)
class Keno(data.state.State): """Class to represent a casino game.""" show_in_lobby = True name = 'keno' def __init__(self): super(Keno, self).__init__() self.screen_rect = pg.Rect((0, 0), prepare.RENDER_SIZE) self.game_started = False self.font = prepare.FONTS["Saniretro"] self.advisor = KenoAdvisor() self.mock_label = Label(self.font, 64, 'KENO [WIP]', 'gold3', {'center': (672, 102)}) b_width = 360 b_height = 90 side_margin = 10 w = self.screen_rect.right - (b_width + side_margin) h = self.screen_rect.bottom - (b_height + 15) self.buttons = ButtonGroup() NeonButton((w, h), "Lobby", self.back_to_lobby, None, self.buttons) self.turns = 16 self.play_max_active = False ball_path = os.path.join('resources', 'keno', 'balls', '64x64', 'sheet.png') ball_sheet = pg.image.load(ball_path).convert_alpha() self.balls = tools.strip_from_sheet(ball_sheet, (0, 0), (64, 64), 10, 8) self.keno_card = KenoCard(self.balls) #self.keno_card = KenoCard() -- no ball graphics self.prev_spot_count = 0 self.pay_table = PayTable(self.keno_card) self.pay_table.update(0) self.round_history = RoundHistory(self.keno_card) self.alert = None self.quick_picking = Action( pg.Rect(370, 760, 150, 75), Label(self.font, 32, 'QUICK PICK', 'gold3', {'center': (0, 0)}), self.activate_quick_pick) self.betting = Action( pg.Rect(682, 760, 150, 75), Label(self.font, 32, 'BET 1', 'gold3', {'center': (0, 0)}), self.activate_bet) self.clearing = Action( pg.Rect(526, 760, 150, 75), Label(self.font, 32, 'CLEAR', 'gold3', {'center': (0, 0)}), self.activate_clear) self.playing = Action( pg.Rect(838, 760, 156, 75), Label(self.font, 32, 'PLAY', 'gold3', {'center': (0, 0)}), self.activate_play) self.playing_max = Action( pg.Rect(838, 840, 156, 75), Label(self.font, 32, 'PLAY MAX', 'gold3', {'center': (0, 0)}), self.activate_playmax) self.actions = { 'quick pick': self.quick_picking, 'betting': self.betting, 'clearing': self.clearing, 'playing': self.playing, 'playing max': self.playing_max, } self.gui_widgets = { 'title': self.mock_label, 'card': self.keno_card, 'quick_pick': self.quick_picking, 'play': self.playing, 'play_max': self.playing_max, 'pay_table': self.pay_table, 'round_history': self.round_history, 'balance': None, 'bet_action': None, 'clear': None, 'bet': None, 'won': None, 'spot': None, } @staticmethod def initialize_stats(): """Return OrderedDict suitable for use in game stats :return: collections.OrderedDict """ stats = OrderedDict([("games played", 0)]) return stats def activate_quick_pick(self): self.keno_card.reset() numbers = pick_numbers(10) for number in numbers: self.keno_card.toggle_owned(number) def activate_bet(self): log.debug("betting activated") try: self.make_bet(1) except InsufficientFundsException: self.handle_insufficient_funds() spot_count = self.keno_card.spot_count self.pay_table.update(spot_count, self.pot._balance) def activate_clear(self): log.debug("clear activated") self.keno_card.ready_play(clear_all=True) self.clear_bet() self.round_history.clear() def validate_configuration(self): if not self.pot.paid and self.pot._balance > 0: try: self.pot.repeat_bet() return True except InsufficientFundsException: self.handle_insufficient_funds() return False elif not self.pot.paid: self.alert = NoticeWindow(self.screen_rect.center, "Please place your bet.") return False spot_count = self.keno_card.spot_count if spot_count <= 0: self.alert = NoticeWindow(self.screen_rect.center, "Please pick your spots.") return False return True def handle_insufficient_funds(self): self.pot.clear_bet(with_payout=False) self.play_max_active = False self.turns = 0 self.alert_insufficient_funds() def alert_insufficient_funds(self): self.alert = NoticeWindow(self.screen_rect.center, "You cannot afford that bet.") def activate_play(self): if not self.validate_configuration(): return numbers = pick_numbers(20) log.debug("pick: {}".format(numbers)) self.keno_card.ready_play() self.keno_card.current_pick = numbers for number in numbers: self.keno_card.toggle_hit(number) self.play_game() def activate_playmax(self): self.round_history.clear() if not self.validate_configuration(): return self.continue_playmax() self.play_game() def continue_playmax(self): log.debug("turns={0}".format(self.turns)) try: self.pot.repeat_bet() self.play_max_active = True numbers = pick_numbers(20) self.keno_card.ready_play() self.keno_card.current_pick = numbers for number in numbers: self.keno_card.toggle_hit(number) self.turns -= 1 if self.turns <= 0: self.play_max_active = False self.turns = 16 except InsufficientFundsException: self.turns = 0 self.handle_insufficient_funds() self.play_max_active = False def make_bet(self, amount): try: self.pot.change_bet(amount) except InsufficientFundsException: self.handle_insufficient_funds() def clear_bet(self): self.pot.clear_bet() def result(self, spot, hit): paytable = PAYTABLE[spot] payment = 0.0 for entry in paytable: if entry[0] == hit: payment = entry[1] if payment > 0.0: break self.pot.payout(payment) self.casino_player.cash = self.wallet.balance def back_to_lobby(self, *args): self.game_started = False self.done = True self.next = "lobby" def startup(self, current_time, persistent): """This method will be called each time the state resumes.""" self.persist = persistent #This is the object that represents the user. self.casino_player = self.persist["casino_player"] self.casino_player.current_game = self.name self.gui_widgets['bet_action'] = self.betting self.gui_widgets['clear'] = self.clearing self.wallet = Wallet(self.casino_player.cash) self.pot = Pot(self.wallet) self.casino_player.increase("games played", 1) def play_game(self): spot_count = self.keno_card.spot_count hit_count = self.keno_card.hit_count self.result(spot_count, hit_count) self.round_history.update(spot_count, hit_count) def get_event(self, event, scale=(1, 1)): """This method will be called for each event in the event queue while the state is active. """ if event.type == pg.QUIT and not self.alert: self.done = True self.next = "lobby" elif event.type == pg.MOUSEBUTTONDOWN and not self.alert: #Use tools.scaled_mouse_pos(scale, event.pos) for correct mouse #position relative to the pygame window size. event_pos = tools.scaled_mouse_pos(scale, event.pos) log.info(event_pos) #[for debugging positional items] for action in self.actions.values(): action.execute(event_pos) self.keno_card.update(event_pos) spot_count = self.keno_card.spot_count if spot_count != self.prev_spot_count: self.pay_table.update(spot_count, self.pot._balance) self.prev_spot_count = spot_count if not self.alert: self.buttons.get_event(event) self.alert and self.alert.get_event(event) def draw(self, surface): """This method handles drawing/blitting the state each frame.""" surface.fill(prepare.FELT_GREEN) self.buttons.draw(surface) self.advisor.draw(surface) for widget in self.gui_widgets.values(): if widget: widget.draw(surface) if self.alert and not self.alert.done: self.alert.draw(surface) def update(self, surface, keys, current_time, dt, scale): """ This method will be called once each frame while the state is active. Surface is a reference to the rendering surface which will be scaled to pygame's display surface, keys is the return value of the last call to pygame.key.get_pressed. current_time is the number of milliseconds since pygame was initialized. dt is the number of milliseconds since the last frame. """ self.advisor.update(dt) if self.play_max_active: self.continue_playmax() self.play_game() total_text = "Balance: ${}".format(self.wallet.balance) self.gui_widgets['balance'] = Label(self.font, 48, total_text, "gold3", {"topleft": (24, 760)}) bet_text = "Bet: ${}".format(self.pot._balance) self.gui_widgets['bet'] = Label(self.font, 48, bet_text, "gold3", {"topleft": (24, 760 + 48)}) won_text = "Won: ${}".format(self.pot.won) self.gui_widgets['won'] = Label(self.font, 48, won_text, "gold3", {"topleft": (24, 760 + 48 + 48)}) spot_count = self.keno_card.spot_count spot_text = "Spot: {}".format(spot_count) self.gui_widgets['spot'] = Label(self.font, 48, spot_text, "gold3", {"topleft": (1036, 760)}) mouse_pos = tools.scaled_mouse_pos(scale) self.buttons.update(mouse_pos) if self.alert: self.alert.update(mouse_pos) if self.alert.done: self.alert = None self.draw(surface)
class HighScores(_State): def __init__(self, controller): super(HighScores, self).__init__(controller) def startup(self, persistent): self.persist = persistent self.labels = [] score = self.persist["player"].cash current_highs = self.load_high_scores() if len(current_highs) < 10: current_highs.append(("", score)) elif current_highs[0][1] < score: current_highs[0] = ("", score) current_highs = sorted(current_highs, key=lambda x: x[1], reverse=True) self.make_high_scores_table(current_highs, score) self.labels.append( Label(prog_constants.FONTS["Fixedsys500c"], 64, "High Scores", constants.LOW_LIGHT_GREEN, {"midtop": (464, 20)})) self.buttons = ButtonGroup() NeonButton((373, 630), "OK", 32, self.to_title, None, self.buttons) def make_high_scores_table(self, high_scores, score): font = prog_constants.FONTS["Fixedsys500c"] color = constants.LOW_LIGHT_GREEN left1, left2, top = 350, 500, 100 placed = False self.name_label = None self.flasher = None self.textbox = None for i, info in enumerate(high_scores): name, high_score = info name_label = Label(font, 32, name, color, {"topleft": (left1, top)}) if high_score == score and not placed and not name: placed = True label = FlashingText(font, 32, "{}".format(high_score), color, {"topleft": (left2, top)}, 500) self.flasher = label self.name_label = name_label self.name_index = i validator = lambda x: len(x) == 3 box_size = (96, 32) self.textbox = Textbox2({"topleft": (left1 - 4, top + 2)}, call=self.enter_name, box_size=box_size, validator=validator, font_path=font) else: label = Label(font, 32, "{}".format(high_score), color, {"topleft": (left2, top)}) self.labels.extend([label, name_label]) top += 40 self.high_scores = high_scores def load_high_scores(self): p = os.path.join(".", "data", "games", "missiles", "resources", "high_scores.json") try: with open(p, "r") as f: return sorted(json.load(f), key=lambda x: x[1]) except IOError: return [] def save_scores(self, scores): p = os.path.join(".", "data", "games", "missiles", "resources", "high_scores.json") with open(p, "w") as f: json.dump(scores, f) def to_title(self, *args): if self.textbox is None: self.save_scores(self.high_scores) self.done = True self.next = "TITLE" def enter_name(self, name): self.name_label.set_text(name) score = self.high_scores[self.name_index][1] self.high_scores[self.name_index] = name, score self.textbox = None def get_event(self, event, scale): if self.textbox is not None: self.textbox.get_event(event) self.buttons.get_event(event) def update(self, surface, keys, current_time, dt, scale): if self.flasher is not None: self.flasher.update(current_time) if self.textbox is not None: self.textbox.buffer = "".join( (x.upper() for x in self.textbox.buffer)) self.textbox.update(dt) self.buttons.update(scaled_mouse_pos(scale)) self.draw(surface) def draw(self, surface): surface.fill(constants.BACKGROUND_BASE) for label in self.labels: label.draw(surface) if self.textbox is not None: self.textbox.draw(surface) self.buttons.draw(surface)
class PlayerTurn(GutsState): def __init__(self): super(PlayerTurn, self).__init__() def startup(self, game): self.game = game self.current_player = self.game.deal_queue[self.game.current_player_index] self.player_buttons = ButtonGroup() self.animations = pg.sprite.Group() self.timer = 0 self.time_limit = 600 if self.current_player is self.game.dealer: self.next = "Show Cards" else: self.next = "Player Turn" if self.current_player is self.game.player: x, y = self.screen_rect.centerx -120, 695 NeonButton((x, y - 55), "Stay", self.stay, None, self.player_buttons) NeonButton((x, y + 55), "Pass", self.stay_out, None, self.player_buttons) if self.current_player is self.game.dealer: stays = [x for x in self.game.players if x.stayed] if not stays: self.stay() else: self.current_player.play_hand(self.game) def toggle_player_buttons(self, boolean): for b in self.player_buttons: b.active = boolean b.visible = boolean def end_player_turn(self): self.done = True self.game.current_player_index += 1 def stay(self, *args): self.current_player.stay() self.game.casino_player.increase("stays") self.animations.add(Task(self.end_player_turn, 1000)) self.toggle_player_buttons(False) def stay_out(self, *args): self.current_player.stay_out() self.game.casino_player.increase("passes") self.animations.add(Task(self.end_player_turn, 1000)) self.toggle_player_buttons(False) def update(self, dt, scale): mouse_pos = tools.scaled_mouse_pos(scale) self.general_update(dt, mouse_pos) if not self.window: self.timer += dt if self.current_player is not self.game.player: if self.timer > self.time_limit: self.done = True self.player_buttons.update(mouse_pos) def draw(self, surface): surface.fill(prepare.FELT_GREEN) for player in self.game.players: player.draw(surface) self.game.draw(surface) self.buttons.draw(surface) self.player_buttons.draw(surface) self.money_icon.draw(surface) self.draw_advisor(surface) if self.window: self.window.draw(surface) def get_event(self, event): if self.window: self.window.get_event(event) else: self.buttons.get_event(event) self.player_buttons.get_event(event) self.advisor_button.get_event(event)
class StartGame(GutsState): """Choose between starting a new game or view tutorial.""" def __init__(self): super(StartGame, self).__init__() def startup(self, game): self.game = game sr = self.screen_rect title = Label(self.font, 128, "Two-Card Guts", "gold3", {"midtop": (sr.centerx, 5)}, bg=prepare.FELT_GREEN) title.image.set_alpha(160) title2 = Label(self.font, 96, "${} Ante".format(self.game.bet), "darkred", {"midtop": (sr.centerx, title.rect.bottom)}, bg=prepare.FELT_GREEN) title2.image.set_alpha(140) self.titles = [title, title2] self.player_buttons = ButtonGroup() w, h = NeonButton.width, NeonButton.height pos = sr.centerx - (w//2), sr.centery - (h//2) NeonButton(pos, "Ante Up", self.start_game, None, self.player_buttons) pos2 = sr.centerx - (w//2), sr.centery + (h//2) + 50 self.tutorial_button = NeonButton(pos2, "Tutorial", self.to_tutorial, None, self.player_buttons) self.advisor.queue_text("Ante Up ${} to play".format(self.game.bet), dismiss_after=2500) self.advisor.queue_text("Press Tutorial to learn how to play", dismiss_after=2500) self.advisor.queue_text("Press the Lobby button to exit", dismiss_after=2500) def start_game(self, *args): self.done = True self.next = "Betting" self.advisor.empty() def to_tutorial(self, *args): self.done = True self.next = "Tutorial" self.advisor.empty() def get_event(self, event): if self.window: self.window.get_event(event) else: self.buttons.get_event(event) self.player_buttons.get_event(event) self.advisor_button.get_event(event) def update(self, dt, scale): mouse_pos = tools.scaled_mouse_pos(scale) self.general_update(dt, mouse_pos) if not self.window: self.player_buttons.update(mouse_pos) def draw(self, surface): surface.fill(prepare.FELT_GREEN) for title in self.titles: title.draw(surface) self.buttons.draw(surface) self.player_buttons.draw(surface) self.money_icon.draw(surface) self.draw_advisor(surface) if self.window: self.window.draw(surface)
class Betting(BlackjackState): def __init__(self): super(Betting, self).__init__() self.make_buttons() pos = (self.screen_rect.right-(NeonButton.width+10), self.screen_rect.bottom-(NeonButton.height+15)) self.lobby_button = NeonButton(pos, "Lobby", self.back_to_lobby, None, self.buttons, bindings=[pg.K_ESCAPE]) def back_to_lobby(self, *args): player = self.game.player for hand in player.hands: player.chip_pile.add_chips(cash_to_chips(hand.bet.get_chip_total())) self.leave_state() def startup(self, game): self.game = game if self.game.quick_bet and (self.game.quick_bet <= self.game.player.chip_pile.get_chip_total()): chips = self.game.player.chip_pile.withdraw_chips(self.game.quick_bet) self.game.current_player_hand.bet.add_chips(chips) self.deal() elif self.game.advisor_active: self.game.advisor.queue_text("Click chips in your chip pile to place bet", dismiss_after=3000) self.game.advisor.queue_text("Click chips in pot to remove from bet", dismiss_after=3000) def make_buttons(self): self.buttons = ButtonGroup() side_margin = 10 vert_space = 15 left = self.screen_rect.right-(NeonButton.width + side_margin) top = self.screen_rect.bottom-((NeonButton.height*5)+vert_space*4) self.deal_button = NeonButton((left, top), "Deal", self.deal, None, self.buttons) def deal(self, *args): bets = [x.bet.get_chip_total() for x in self.game.player.hands] self.game.last_bet = max(bets) self.game.quick_bet = 0 self.next = "Dealing" self.game.casino_player.increase("games played") self.game.advisor.empty() self.done = True def get_event(self, event, scale): self.game.get_event(event) if event.type == pg.QUIT: self.back_to_lobby() elif event.type == pg.MOUSEBUTTONDOWN: pos = tools.scaled_mouse_pos(scale, event.pos) if not self.game.moving_stacks and event.button == 1: new_movers = self.game.player.chip_pile.grab_chips(pos) self.last_click = pg.time.get_ticks() if new_movers: self.play_chip_sound() self.game.moving_stacks.append(new_movers) for hand in self.game.player.hands: unbet_stack = hand.bet.grab_chips(pos) if unbet_stack: self.play_chip_sound() self.game.player.chip_pile.add_chips(unbet_stack.chips) elif event.type == pg.MOUSEBUTTONUP: now = pg.time.get_ticks() span = now - self.last_click pos = tools.scaled_mouse_pos(scale, event.pos) if self.game.moving_stacks and event.button == 1: for stack in self.game.moving_stacks: stack.rect.bottomleft = pos if self.game.chip_rack.rect.collidepoint(pos): self.play_chip_sound() self.game.player.chip_pile.add_chips(self.game.chip_rack.break_chips(stack.chips)) elif span > 300 and self.game.player.chip_pile.rect.collidepoint(pos): self.play_chip_sound() self.game.player.chip_pile.add_chips(stack.chips) else: self.play_chip_sound() self.game.current_player_hand.bet.add_chips(stack.chips) self.game.moving_stacks = [] if self.window: self.window.get_event(event) else: self.lobby_button.get_event(event) self.deal_button.get_event(event) def update(self, surface, keys, current_time, dt, scale): mouse_pos = tools.scaled_mouse_pos(pg.mouse.get_pos(), scale) self.game.update(dt, mouse_pos) bets = [x.bet.get_chip_total() for x in self.game.player.hands] self.deal_button.visible = any(bets) and not self.game.moving_stacks if self.window: self.window.update(mouse_pos) if self.window.done: self.window = None else: self.lobby_button.update(mouse_pos) self.deal_button.update(mouse_pos) for stack in self.game.moving_stacks: x, y = mouse_pos stack.rect.bottomleft = (x - (self.game.chip_size[0] // 2), y + 6) def draw(self, surface): g = self.game surface.fill(prepare.FELT_GREEN) for label in g.labels: label.draw(surface) g.player.draw_hands(surface) g.player.draw_hand_bets(surface) g.player.chip_pile.draw(surface) g.chip_total_label.draw(surface) g.dealer.draw_hand(surface) g.deck.draw(surface) g.chip_rack.draw(surface) for stack in g.moving_stacks: stack.draw(surface) self.buttons.draw(surface) self.draw_advisor(surface) self.window and self.window.draw(surface)
class PlayerTurn(BlackjackState): def __init__(self): super(PlayerTurn, self).__init__() pos = (self.screen_rect.right-(NeonButton.width+10), self.screen_rect.bottom-(NeonButton.height+15)) self.make_buttons() self.lobby_button = NeonButton(pos, "Lobby", self.back_to_lobby, None, self.buttons, bindings=[pg.K_ESCAPE]) self.last_click = 0 def play_deal_sound(self): choice(self.game.deal_sounds).play() def make_buttons(self): side_margin = 10 vert_space = 12 left = self.screen_rect.right-(NeonButton.width + side_margin) top = self.screen_rect.bottom-((NeonButton.height*5)+vert_space*5) self.hit_button = NeonButton((left,top), "Hit", self.hit_click) top += NeonButton.height + vert_space self.stand_button = NeonButton((left, top), "Stand", self.stand) top += NeonButton.height + vert_space self.double_down_button = NeonButton((left,top), "Double", self.double_down) top += NeonButton.height + vert_space self.split_button = NeonButton((left, top), "Split", self.split_hand) self.buttons = ButtonGroup(self.hit_button, self.stand_button, self.double_down_button, self.split_button) def hit_click(self, *args): self.hit(self.game.current_player_hand) def hit(self, hand): """Draw a card from deck and add to hand.""" self.play_deal_sound() self.game.player.add_slot(hand) card = self.game.deck.draw_card() card.face_up = True destination = hand.slots[-1] dest_x, dest_y = destination.topleft hand.cards.append(card) dur = get_distance(destination.center, card.pos) * 20 // card.speed ani = Animation(x=dest_x, y=dest_y, duration=dur, round_values=True) ani.start(card.rect) self.animations.add(ani) def stand(self, *args): """Player is done with this hand.""" self.game.current_player_hand.final = True def double_down(self, *args): """ Double player's bet on the hand, deal one more card and finalize hand. """ hand = self.game.current_player_hand bet = hand.bet.get_chip_total() bet_chips = self.game.player.chip_pile.withdraw_chips(bet) hand.bet.add_chips(bet_chips) self.hit(hand) hand.final = True def split_hand(self, *args): """ Split player's hand into two hands, adjust hand locations and deal a new card to both hands. """ player = self.game.player hand = self.game.current_player_hand bet = hand.bet.get_chip_total() hand.slots = hand.slots[:-1] move = ((self.screen_rect.left+50)-hand.slots[0].left, 0) player.move_hands(move) p_slot = player.hands[-1].slots[0] hand_slot = p_slot.move(int(prepare.CARD_SIZE[0] * 3.5), 0) card = hand.cards.pop() new_hand = Hand(hand_slot.topleft, [card], player.chip_pile.withdraw_chips(bet)) new_hand.slots = [hand_slot] card.rect.topleft = hand_slot.topleft player.hands.append(new_hand) player.add_slot(new_hand) self.play_deal_sound() self.game.player.add_slot(hand) card1 = self.game.deck.draw_card() dest = hand.slots[-1] dest_x, dest_y = dest.topleft card1.face_up = True hand.cards.append(card1) ani = Animation(x=dest_x, y=dest_y, duration=1000, round_values=True) ani.start(card1.rect) card2 = self.game.deck.draw_card() dest = new_hand.slots[-1] dest_x, dest_y = dest.topleft card2.face_up = True new_hand.cards.append(card2) ani2 = Animation(x=dest_x, y=dest_y, duration=1000, delay=500, round_values=True) ani2.start(card2.rect) ani3 = Task(self.play_deal_sound, 1500) self.animations.add(ani, ani2, ani3) def startup(self, game): self.game = game self.animations = pg.sprite.Group() def get_event(self, event, scale): now = pg.time.get_ticks() span = now - self.last_click if event.type == pg.QUIT: self.back_to_lobby() if event.type == pg.MOUSEBUTTONUP: self.last_click = now self.game.get_event(event) if span > 300: if self.window: self.window.get_event(event) else: self.buttons.get_event(event) def update(self, surface, keys, current_time, dt, scale): mouse_pos = tools.scaled_mouse_pos(pg.mouse.get_pos(), scale) g = self.game g.update(dt, mouse_pos) self.split_button.active = False self.split_button.visible = False self.double_down_button.active = False self.double_down_button.visible = False hand = g.current_player_hand hand_score = hand.best_score() if hand_score is None: hand.busted = True hand.final = True chip_total = g.player.chip_pile.get_chip_total() bet = hand.bet.get_chip_total() if len(hand.cards) == 2 and len(g.player.hands) < 2: c1 = hand.card_values[hand.cards[0].value] c2 = hand.card_values[hand.cards[1].value] if c1 == c2 and chip_total >= bet: self.split_button.active = True self.split_button.visible = True if len(hand.cards) == 2: if hand_score == 21: hand.blackjack = True hand.final = True elif chip_total >= bet: self.double_down_button.active = True self.double_down_button.visible = True if hand.final: if all([hand.final for hand in g.player.hands]): if not self.animations: g.dealer.hand.cards[0].face_up = True self.next = "Dealer Turn" self.done = True else: next_hand = [x for x in g.player.hands if not x.final][0] g.current_player_hand = next_hand if self.window: self.window.update(mouse_pos) if self.window.done: self.window = None else: self.buttons.update(mouse_pos) self.animations.update(dt) def draw_hand_frame(self, surface): hand = self.game.current_player_hand rects = [x for x in hand.slots] pg.draw.rect(surface, pg.Color("gold3"), hand.slots[0].unionall(rects).inflate(8, 8), 3) def draw(self, surface): g = self.game surface.fill(prepare.FELT_GREEN) for label in g.labels: label.draw(surface) g.player.draw_hand_bets(surface) g.player.chip_pile.draw(surface) g.chip_total_label.draw(surface) g.dealer.draw_hand(surface) g.deck.draw(surface) g.chip_rack.draw(surface) self.buttons.draw(surface) self.lobby_button.draw(surface) g.player.draw_hands(surface) self.draw_advisor(surface) self.draw_hand_frame(surface) self.game.player.chip_pile.draw(surface) self.window and self.window.draw(surface)
class LobbyScreen(data.state.State): """ This state represents the casino lobby where the player can choose which game they want to play or view their game statistics. This is also the exit point for the game. """ name = "lobby" per_page = 6 def __init__(self): super(LobbyScreen, self).__init__() self.game = None self.chip_curtain = None # Created on startup self.animations = pg.sprite.Group() def collect_game_scenes(self): all_scenes = self.controller.query_all_states() games = OrderedDict() for name, scene in all_scenes.items(): if hasattr(scene, 'show_in_lobby'): games[name] = scene return games def update_screen_buttons(self, games): screen_rect = pg.Rect((0, 0), prepare.RENDER_SIZE) number_of_pages = int(math.ceil(len(games) / float(self.per_page))) self.loop_length = prepare.RENDER_SIZE[0] * number_of_pages self.game_buttons = self.make_game_pages(games, screen_rect, self.per_page) nav_buttons = self.make_navigation_buttons(screen_rect) main_buttons = self.make_main_buttons(screen_rect) self.buttons = ButtonGroup(nav_buttons, main_buttons) def make_game_pages(self, games, screen_rect, per): games = list(games.keys()) groups = (games[i:i + per] for i in range(0, len(games), per)) columns = 3 width, height = GameButton.width, GameButton.height spacer_x, spacer_y = 50, 80 start_x = (screen_rect.w - width * columns - spacer_x * (columns - 1)) // 2 start_y = screen_rect.top + 105 step_x, step_y = width + spacer_x, height + spacer_y buttons = ButtonGroup() for offset, group in enumerate(groups): offset *= prepare.RENDER_SIZE[0] for i, game in enumerate(group): y, x = divmod(i, columns) pos = (start_x + step_x * x + offset, start_y + step_y * y) GameButton(pos, game, self.change_state, buttons) return buttons def make_navigation_buttons(self, screen_rect): sheet = prepare.GFX["nav_buttons"] size = (106, 101) y = 790 from_center = 15 icons = tools.strip_from_sheet(sheet, (0, 0), size, 4) buttons = ButtonGroup() l_kwargs = { "idle_image": icons[0], "hover_image": icons[1], "call": self.scroll_page, "args": 1, "bindings": [pg.K_LEFT, pg.K_KP4] } r_kwargs = { "idle_image": icons[2], "hover_image": icons[3], "call": self.scroll_page, "args": -1, "bindings": [pg.K_RIGHT, pg.K_KP6] } left = Button(((0, y), size), buttons, **l_kwargs) left.rect.right = screen_rect.centerx - from_center right = Button(((0, y), size), buttons, **r_kwargs) right.rect.x = screen_rect.centerx + from_center return buttons def make_main_buttons(self, screen_rect): buttons = ButtonGroup() pos = (9, screen_rect.bottom - (NeonButton.height + 11)) NeonButton(pos, "Credits", self.change_state, "credits", buttons) pos = (screen_rect.right - (NeonButton.width + 10), screen_rect.bottom - (NeonButton.height + 11)) NeonButton(pos, "Stats", self.change_state, "stats_menu", buttons) pos = (screen_rect.centerx - (NeonButton.width // 2), screen_rect.bottom - (NeonButton.height + 11)) NeonButton(pos, "Exit", self.exit_game, None, buttons, bindings=[pg.K_ESCAPE]) rect_style = (screen_rect.left, screen_rect.top, 150, 95) Button(rect_style, buttons, idle_image=prepare.GFX["atm_dim"], hover_image=prepare.GFX["atm_bright"], call=self.change_state, args="atm") return buttons def scroll_page(self, mag): if not self.animations: for game in self.game_buttons: self.normalize_scroll(game, mag) fx, fy = game.rect.x + prepare.RENDER_SIZE[0] * mag, game.rect.y ani = Animation(x=fx, y=fy, duration=350.0, transition='in_out_quint', round_values=True) ani.start(game.rect) self.animations.add(ani) prepare.SFX["cardplace4"].play() def normalize_scroll(self, game, mag): if game.rect.x < 0 and mag == -1: game.rect.x += self.loop_length elif game.rect.x >= prepare.RENDER_SIZE[0] and mag == 1: game.rect.x -= self.loop_length def startup(self, current_time, persistent): self.persist = persistent self.chip_curtain = ChipCurtain(None, **CURTAIN_SETTINGS) games = self.collect_game_scenes() self.update_screen_buttons(games) def exit_game(self, *args): with open(os.path.join("resources", "save_game.json"), "w") as f: json.dump(self.persist["casino_player"].stats, f) self.done = True self.quit = True def change_state(self, next_state): self.done = True self.next = next_state def get_event(self, event, scale=(1, 1)): if self.game: self.game.get_event(event, scale) elif event.type == pg.QUIT: self.exit_game() else: self.buttons.get_event(event) self.game_buttons.get_event(event) def update(self, surface, keys, current_time, dt, scale): if self.game: self.game.update(surface, keys, current_time, dt, scale) if self.game.done: self.game.cleanup() self.game = None else: mouse_pos = tools.scaled_mouse_pos(scale) self.chip_curtain.update(dt) self.buttons.update(mouse_pos) self.game_buttons.update(mouse_pos) self.animations.update(dt) self.draw(surface) def draw(self, surface): rect = surface.get_rect() surface.fill(prepare.BACKGROUND_BASE) self.chip_curtain.draw(surface) self.buttons.draw(surface) for button in self.game_buttons: if button.rect.colliderect(rect): button.draw(surface)
class ShowResults(GutsState): def __init__(self): super(ShowResults, self).__init__() self.buttons = ButtonGroup() pos = (self.screen_rect.right - (NeonButton.width + 10), self.screen_rect.bottom - (NeonButton.height + 10)) self.lobby_button = NeonButton(pos, "Lobby", self.warn, None, self.buttons, bindings=[pg.K_ESCAPE]) def make_player_buttons(self, free_ride): self.player_buttons = ButtonGroup() w, h = NeonButton.width, NeonButton.height sr = self.screen_rect pos = sr.centerx - (w // 2), sr.centery + 120 text = "OK" if free_ride else "Ante Up" NeonButton(pos, text, self.new_game, None, self.player_buttons) def new_game(self, *args): self.game.game_over = True self.done = True self.next = "Betting" self.advisor.empty() def add_player_cash(self, amount): self.game.player.cash += amount def add_to_pot(self, amount): self.game.pot += amount def toggle_buttons(self, boolean): for button in self.player_buttons: button.active = boolean button.visible = boolean def cash_advance(self, amount): acct = self.game.casino_player.account acct.cash_advance(amount) self.add_player_cash(amount) def startup(self, game): self.game = game self.lobby_button.call = self.warn self.toggled = False self.alpha = 255 self.labels = [] self.blinkers = [] self.calculated = False self.animations = pg.sprite.Group() stayers = [x for x in self.game.players if x.stayed] winners = self.game.get_winners() share = self.game.pot // len(winners) ani_duration = 2000 self.free_ride = False for stayer in stayers: pos = stayer.name_label.rect.center dest = pos[0], pos[1] - 120 cards_center = stayer.cards[0].rect.union( stayer.cards[1].rect).center if stayer not in winners: text = "${}".format(self.game.pot) color = "darkred" stayer.lost = self.game.pot self.free_ride = True dest = self.game.pot_label.rect.center if stayer is self.game.player: pos = self.money_icon.rect.center if self.game.player.cash < stayer.lost: amount = stayer.lost - self.game.player.cash self.cash_advance(amount) msg = "You were forced to take a cash advance to cover your loss. Visit the ATM to view your account." self.notice(msg) self.add_player_cash(-stayer.lost) task = Task(self.add_to_pot, ani_duration, args=[stayer.lost]) self.animations.add(task) lose_label = Blinker(self.font, 96, "Loser", color, {"center": cards_center}, 500) self.animations.add( Task(self.blinkers.append, ani_duration, args=[lose_label])) else: text = "${}".format(share) color = "darkgreen" stayer.won = share pos = self.game.pot_label.rect.center if stayer is self.game.player: self.cha_ching.play() dest = self.money_icon.rect.center task = Task(self.add_player_cash, ani_duration, args=[share]) self.animations.add(task) win_label = Blinker(self.font, 96, "Winner", color, {"center": cards_center}, 500) self.animations.add( Task(self.blinkers.append, ani_duration, args=[win_label])) label = Label(self.font, 128, text, color, {"center": pos}, bg=prepare.FELT_GREEN) label.image.set_colorkey(prepare.FELT_GREEN) self.labels.append(label) move = Animation(centerx=dest[0], centery=dest[1], duration=ani_duration, round_values=True, transition="in_quart") move.start(label.rect) self.animations.add(move) fader = Animation(alpha=0, duration=ani_duration + 200, round_values=True) fader.start(self) self.animations.add(fader) self.game.pot = 0 self.make_player_buttons(self.free_ride) self.toggle_buttons(False) if self.free_ride: self.advice_texts = ["Press OK to play the next round"] else: self.advice_texts = [ "Ante Up ${} to play again".format(self.game.bet) ] self.advice_texts.append("Press the Lobby button to exit") def fade_labels(self): for label in self.labels: label.image.set_alpha(self.alpha) def update(self, dt, scale): mouse_pos = tools.scaled_mouse_pos(scale) self.general_update(dt, mouse_pos) if not self.window: for blinker in self.blinkers: blinker.update(dt) self.player_buttons.update(mouse_pos) self.fade_labels() if self.alpha < 1 and not self.toggled: self.toggle_buttons(True) self.toggled = True for text in self.advice_texts: self.advisor.queue_text(text, dismiss_after=3500) if not self.free_ride: self.lobby_button.call = self.back_to_lobby def get_event(self, event): if self.window: self.window.get_event(event) else: self.buttons.get_event(event) self.player_buttons.get_event(event) self.advisor_button.get_event(event) def draw(self, surface): surface.fill(prepare.FELT_GREEN) self.game.draw(surface) for p in self.game.players: p.draw(surface) self.money_icon.draw(surface) self.buttons.draw(surface) self.player_buttons.draw(surface) for label in self.labels: label.draw(surface) for blinker in self.blinkers: blinker.draw(surface) self.draw_advisor(surface) if self.window: self.window.draw(surface)
class PlayerTurn(GutsState): def __init__(self): super(PlayerTurn, self).__init__() def startup(self, game): self.game = game self.current_player = self.game.deal_queue[ self.game.current_player_index] self.player_buttons = ButtonGroup() self.animations = pg.sprite.Group() self.timer = 0 self.time_limit = 600 if self.current_player is self.game.dealer: self.next = "Show Cards" else: self.next = "Player Turn" if self.current_player is self.game.player: x, y = self.screen_rect.centerx - 120, 695 NeonButton((x, y - 55), "Stay", self.stay, None, self.player_buttons) NeonButton((x, y + 55), "Pass", self.stay_out, None, self.player_buttons) if self.current_player is self.game.dealer: stays = [x for x in self.game.players if x.stayed] if not stays: self.stay() else: self.current_player.play_hand(self.game) def toggle_player_buttons(self, boolean): for b in self.player_buttons: b.active = boolean b.visible = boolean def end_player_turn(self): self.done = True self.game.current_player_index += 1 def stay(self, *args): self.current_player.stay() self.game.casino_player.increase("stays") self.animations.add(Task(self.end_player_turn, 1000)) self.toggle_player_buttons(False) def stay_out(self, *args): self.current_player.stay_out() self.game.casino_player.increase("passes") self.animations.add(Task(self.end_player_turn, 1000)) self.toggle_player_buttons(False) def update(self, dt, scale): mouse_pos = tools.scaled_mouse_pos(scale) self.general_update(dt, mouse_pos) if not self.window: self.timer += dt if self.current_player is not self.game.player: if self.timer > self.time_limit: self.done = True self.player_buttons.update(mouse_pos) def draw(self, surface): surface.fill(prepare.FELT_GREEN) for player in self.game.players: player.draw(surface) self.game.draw(surface) self.buttons.draw(surface) self.player_buttons.draw(surface) self.money_icon.draw(surface) self.draw_advisor(surface) if self.window: self.window.draw(surface) def get_event(self, event): if self.window: self.window.get_event(event) else: self.buttons.get_event(event) self.player_buttons.get_event(event) self.advisor_button.get_event(event)
class Betting(GutsState): def __init__(self): super(Betting, self).__init__() self.buttons = ButtonGroup() pos = (self.screen_rect.right - (NeonButton.width + 10), self.screen_rect.bottom - (NeonButton.height + 10)) lobby_button = NeonButton(pos, "Lobby", self.warn, None, self.buttons, bindings=[pg.K_ESCAPE]) def add_bet_to_pot(self): self.game.pot += self.game.bet def make_labels(self): sr = self.screen_rect self.labels = [] self.alpha = 255 self.big_alpha = 255 self.animations = pg.sprite.Group() text = "Free Ride" if self.game.free_ride else "Ante Up" self.big_label = Label(self.font, 320, text, "gold3", {"center": sr.center}, bg=prepare.FELT_GREEN) self.big_label.image.set_colorkey(prepare.FELT_GREEN) left, top = self.big_label.rect.topleft ani = Animation(x=left, y=top - 500, duration=2000, round_values=True) fade = Animation(alpha=0, duration=2000, round_values=True) big_fader = Animation(big_alpha=0, duration=1500, delay=500, round_values=True) fade.start(self) ani.start(self.big_label.rect) big_fader.start(self) self.animations.add(ani, fade, big_fader) if not self.game.free_ride: dest = self.game.pot_label.rect.center for p in self.game.players: pos = p.name_label.rect.center label = Label(self.font, 96, "${}".format(self.game.bet), "darkred", {"center": pos}, bg=prepare.FELT_GREEN) label.image.set_colorkey(prepare.FELT_GREEN) left, top = label.rect.topleft self.labels.append(label) ani = Animation(centerx=dest[0], centery=dest[1], duration=2000, delay=50, round_values=True, transition="in_quart") ani.callback = self.add_bet_to_pot ani.start(label.rect) self.animations.add(ani) fader = Animation(alpha=0, duration=2100, round_values=True) fader.start(self) self.animations.add(fader) def startup(self, game): self.game = game if not self.game.free_ride: if self.game.player.cash < self.game.bet: self.next = "Bankrupt Screen" self.done = True else: self.game.player.cash -= self.game.bet self.game.casino_player.increase("games played") self.game.casino_player.increase("total bets", self.game.bet) self.game.casino_player.increase("hands played") self.make_labels() def get_event(self, event): if self.window: self.window.get_event(event) else: self.buttons.get_event(event) self.advisor_button.get_event(event) def update(self, dt, scale): mouse_pos = tools.scaled_mouse_pos(scale) self.general_update(dt, mouse_pos) if not self.animations: self.done = True self.next = "Dealing" for label in self.labels: label.image.set_alpha(self.alpha) self.big_label.image.set_alpha(self.big_alpha) def draw(self, surface): if not self.done: surface.fill(prepare.FELT_GREEN) self.game.draw(surface) for p in self.game.players: p.draw(surface) for label in self.labels: label.draw(surface) self.big_label.draw(surface) self.buttons.draw(surface) self.money_icon.draw(surface) self.draw_advisor(surface) if self.window: self.window.draw(surface)
class StartGame(GutsState): """Choose between starting a new game or view tutorial.""" def __init__(self): super(StartGame, self).__init__() def startup(self, game): self.game = game sr = self.screen_rect title = Label(self.font, 128, "Two-Card Guts", "gold3", {"midtop": (sr.centerx, 5)}, bg=prepare.FELT_GREEN) title.image.set_alpha(160) title2 = Label(self.font, 96, "${} Ante".format(self.game.bet), "darkred", {"midtop": (sr.centerx, title.rect.bottom)}, bg=prepare.FELT_GREEN) title2.image.set_alpha(140) self.titles = [title, title2] self.player_buttons = ButtonGroup() w, h = NeonButton.width, NeonButton.height pos = sr.centerx - (w // 2), sr.centery - (h // 2) NeonButton(pos, "Ante Up", self.start_game, None, self.player_buttons) pos2 = sr.centerx - (w // 2), sr.centery + (h // 2) + 50 self.tutorial_button = NeonButton(pos2, "Tutorial", self.to_tutorial, None, self.player_buttons) self.advisor.queue_text("Ante Up ${} to play".format(self.game.bet), dismiss_after=2500) self.advisor.queue_text("Press Tutorial to learn how to play", dismiss_after=2500) self.advisor.queue_text("Press the Lobby button to exit", dismiss_after=2500) def start_game(self, *args): self.done = True self.next = "Betting" self.advisor.empty() def to_tutorial(self, *args): self.done = True self.next = "Tutorial" self.advisor.empty() def get_event(self, event): if self.window: self.window.get_event(event) else: self.buttons.get_event(event) self.player_buttons.get_event(event) self.advisor_button.get_event(event) def update(self, dt, scale): mouse_pos = tools.scaled_mouse_pos(scale) self.general_update(dt, mouse_pos) if not self.window: self.player_buttons.update(mouse_pos) def draw(self, surface): surface.fill(prepare.FELT_GREEN) for title in self.titles: title.draw(surface) self.buttons.draw(surface) self.player_buttons.draw(surface) self.money_icon.draw(surface) self.draw_advisor(surface) if self.window: self.window.draw(surface)
class Betting(BlackjackState): def __init__(self): super(Betting, self).__init__() self.make_buttons() pos = (self.screen_rect.right - (NeonButton.width + 10), self.screen_rect.bottom - (NeonButton.height + 15)) self.lobby_button = NeonButton(pos, "Lobby", self.back_to_lobby, None, self.buttons, bindings=[pg.K_ESCAPE]) def back_to_lobby(self, *args): player = self.game.player for hand in player.hands: player.chip_pile.add_chips(cash_to_chips( hand.bet.get_chip_total())) self.leave_state() def startup(self, game): self.game = game if self.game.quick_bet and ( self.game.quick_bet <= self.game.player.chip_pile.get_chip_total()): chips = self.game.player.chip_pile.withdraw_chips( self.game.quick_bet) self.game.current_player_hand.bet.add_chips(chips) self.deal() elif self.game.advisor_active: self.game.advisor.queue_text( "Click chips in your chip pile to place bet", dismiss_after=3000) self.game.advisor.queue_text( "Click chips in pot to remove from bet", dismiss_after=3000) def make_buttons(self): self.buttons = ButtonGroup() side_margin = 10 vert_space = 15 left = self.screen_rect.right - (NeonButton.width + side_margin) top = self.screen_rect.bottom - ( (NeonButton.height * 5) + vert_space * 4) self.deal_button = NeonButton((left, top), "Deal", self.deal, None, self.buttons) def deal(self, *args): bets = [x.bet.get_chip_total() for x in self.game.player.hands] self.game.last_bet = max(bets) self.game.quick_bet = 0 self.next = "Dealing" self.game.casino_player.increase("games played") self.game.advisor.empty() self.done = True def get_event(self, event, scale): self.game.get_event(event) if event.type == pg.QUIT: self.back_to_lobby() elif event.type == pg.MOUSEBUTTONDOWN: pos = tools.scaled_mouse_pos(scale, event.pos) if not self.game.moving_stacks and event.button == 1: new_movers = self.game.player.chip_pile.grab_chips(pos) self.last_click = pg.time.get_ticks() if new_movers: self.play_chip_sound() self.game.moving_stacks.append(new_movers) for hand in self.game.player.hands: unbet_stack = hand.bet.grab_chips(pos) if unbet_stack: self.play_chip_sound() self.game.player.chip_pile.add_chips(unbet_stack.chips) elif event.type == pg.MOUSEBUTTONUP: now = pg.time.get_ticks() span = now - self.last_click pos = tools.scaled_mouse_pos(scale, event.pos) if self.game.moving_stacks and event.button == 1: for stack in self.game.moving_stacks: stack.rect.bottomleft = pos if self.game.chip_rack.rect.collidepoint(pos): self.play_chip_sound() self.game.player.chip_pile.add_chips( self.game.chip_rack.break_chips(stack.chips)) elif span > 300 and self.game.player.chip_pile.rect.collidepoint( pos): self.play_chip_sound() self.game.player.chip_pile.add_chips(stack.chips) else: self.play_chip_sound() self.game.current_player_hand.bet.add_chips( stack.chips) self.game.moving_stacks = [] if self.window: self.window.get_event(event) else: self.lobby_button.get_event(event) self.deal_button.get_event(event) def update(self, surface, keys, current_time, dt, scale): mouse_pos = tools.scaled_mouse_pos(pg.mouse.get_pos(), scale) self.game.update(dt, mouse_pos) bets = [x.bet.get_chip_total() for x in self.game.player.hands] self.deal_button.visible = any(bets) and not self.game.moving_stacks if self.window: self.window.update(mouse_pos) if self.window.done: self.window = None else: self.lobby_button.update(mouse_pos) self.deal_button.update(mouse_pos) for stack in self.game.moving_stacks: x, y = mouse_pos stack.rect.bottomleft = (x - (self.game.chip_size[0] // 2), y + 6) def draw(self, surface): g = self.game surface.fill(prepare.FELT_GREEN) for label in g.labels: label.draw(surface) g.player.draw_hands(surface) g.player.draw_hand_bets(surface) g.player.chip_pile.draw(surface) g.chip_total_label.draw(surface) g.dealer.draw_hand(surface) g.deck.draw(surface) g.chip_rack.draw(surface) for stack in g.moving_stacks: stack.draw(surface) self.buttons.draw(surface) self.draw_advisor(surface) self.window and self.window.draw(surface)
class EndRound(BlackjackState): def __init__(self): super(EndRound, self).__init__() pos = (self.screen_rect.right - (NeonButton.width + 10), self.screen_rect.bottom - (NeonButton.height + 15)) self.lobby_button = NeonButton(pos, "Lobby", self.back_to_lobby, bindings=[pg.K_ESCAPE]) self.make_buttons() def make_buttons(self): self.buttons = ButtonGroup() self.new_game_button = NeonButton( (0, 0), "Change", self.new_game_click, None, self.buttons) self.quick_bet_button = NeonButton( (0, 0), "Again", self.quick_bet_click, None, self.buttons) self.quick_bet_button.rect.midbottom = (self.screen_rect.centerx, self.screen_rect.centery - 30) self.new_game_button.rect.center = (self.screen_rect.centerx, self.screen_rect.centery + 30) def new_game_click(self, *args): self.game.advisor.empty() self.next = "Betting" self.done = True def quick_bet_click(self, *args): self.game.quick_bet = self.game.last_bet self.new_game_click() def startup(self, game): self.game = game if game.advisor_active: text = "The current bet amount is ${}".format(self.game.last_bet) self.game.advisor.queue_text(text, dismiss_after=3000) text2 = "Press Change Bet for a different amount" self.game.advisor.queue_text(text2, dismiss_after=3000) text3 = "Press the Lobby button to exit" self.game.advisor.queue_text(text3, dismiss_after=3000) def get_event(self, event, scale): if event.type == pg.QUIT: self.back_to_lobby() self.game.get_event(event) if self.window: self.window.get_event(event) else: self.buttons.get_event(event) self.lobby_button.get_event(event) def update(self, surface, keys, current_time, dt, scale): mouse_pos = tools.scaled_mouse_pos(pg.mouse.get_pos(), scale) self.game.update(dt, mouse_pos) for label in self.game.result_labels: label.update(dt) if self.window: self.window.update(mouse_pos) if self.window.done: self.window = None else: self.buttons.update(mouse_pos) self.lobby_button.update(mouse_pos) def draw(self, surface): g = self.game surface.fill(prepare.FELT_GREEN) for label in g.labels: label.draw(surface) g.player.draw_hand_bets(surface) g.player.chip_pile.draw(surface) g.chip_total_label.draw(surface) g.dealer.draw_hand(surface) g.deck.draw(surface) g.chip_rack.draw(surface) self.lobby_button.draw(surface) g.player.draw_hands(surface) for blinker in self.game.result_labels: blinker.draw(surface) self.buttons.draw(surface) self.draw_advisor(surface) self.game.player.chip_pile.draw(surface) self.window and self.window.draw(surface)
class EndRound(BlackjackState): def __init__(self): super(EndRound, self).__init__() pos = (self.screen_rect.right-(NeonButton.width+10), self.screen_rect.bottom-(NeonButton.height+15)) self.lobby_button = NeonButton(pos, "Lobby", self.back_to_lobby, bindings=[pg.K_ESCAPE]) self.make_buttons() def make_buttons(self): self.buttons = ButtonGroup() self.new_game_button = NeonButton((0,0), "Change", self.new_game_click, None, self.buttons) self.quick_bet_button = NeonButton((0, 0), "Again", self.quick_bet_click, None, self.buttons) self.quick_bet_button.rect.midbottom = (self.screen_rect.centerx, self.screen_rect.centery-30) self.new_game_button.rect.center = (self.screen_rect.centerx, self.screen_rect.centery+30) def new_game_click(self, *args): self.game.advisor.empty() self.next = "Betting" self.done = True def quick_bet_click(self, *args): self.game.quick_bet = self.game.last_bet self.new_game_click() def startup(self, game): self.game = game if game.advisor_active: text = "The current bet amount is ${}".format(self.game.last_bet) self.game.advisor.queue_text(text, dismiss_after=3000) text2 = "Press Change Bet for a different amount" self.game.advisor.queue_text(text2, dismiss_after=3000) text3 = "Press the Lobby button to exit" self.game.advisor.queue_text(text3, dismiss_after=3000) def get_event(self, event, scale): if event.type == pg.QUIT: self.back_to_lobby() self.game.get_event(event) if self.window: self.window.get_event(event) else: self.buttons.get_event(event) self.lobby_button.get_event(event) def update(self, surface, keys, current_time, dt, scale): mouse_pos = tools.scaled_mouse_pos(pg.mouse.get_pos(), scale) self.game.update(dt, mouse_pos) for label in self.game.result_labels: label.update(dt) if self.window: self.window.update(mouse_pos) if self.window.done: self.window = None else: self.buttons.update(mouse_pos) self.lobby_button.update(mouse_pos) def draw(self, surface): g = self.game surface.fill(prepare.FELT_GREEN) for label in g.labels: label.draw(surface) g.player.draw_hand_bets(surface) g.player.chip_pile.draw(surface) g.chip_total_label.draw(surface) g.dealer.draw_hand(surface) g.deck.draw(surface) g.chip_rack.draw(surface) self.lobby_button.draw(surface) g.player.draw_hands(surface) for blinker in self.game.result_labels: blinker.draw(surface) self.buttons.draw(surface) self.draw_advisor(surface) self.game.player.chip_pile.draw(surface) self.window and self.window.draw(surface)
class Betting(GutsState): def __init__(self): super(Betting, self).__init__() self.buttons = ButtonGroup() pos = (self.screen_rect.right-(NeonButton.width+10), self.screen_rect.bottom-(NeonButton.height+10)) lobby_button = NeonButton(pos, "Lobby", self.warn, None, self.buttons, bindings=[pg.K_ESCAPE]) def add_bet_to_pot(self): self.game.pot += self.game.bet def make_labels(self): sr = self.screen_rect self.labels = [] self.alpha = 255 self.big_alpha = 255 self.animations = pg.sprite.Group() text = "Free Ride" if self.game.free_ride else "Ante Up" self.big_label = Label(self.font, 320, text, "gold3", {"center": sr.center}, bg=prepare.FELT_GREEN) self.big_label.image.set_colorkey(prepare.FELT_GREEN) left, top = self.big_label.rect.topleft ani = Animation(x=left, y=top-500, duration=2000, round_values=True) fade = Animation(alpha=0, duration=2000, round_values=True) big_fader = Animation(big_alpha=0, duration=1500, delay=500, round_values=True) fade.start(self) ani.start(self.big_label.rect) big_fader.start(self) self.animations.add(ani, fade, big_fader) if not self.game.free_ride: dest = self.game.pot_label.rect.center for p in self.game.players: pos = p.name_label.rect.center label = Label(self.font, 96, "${}".format(self.game.bet), "darkred", {"center": pos}, bg=prepare.FELT_GREEN) label.image.set_colorkey(prepare.FELT_GREEN) left, top = label.rect.topleft self.labels.append(label) ani = Animation(centerx=dest[0], centery=dest[1], duration=2000, delay=50, round_values=True, transition="in_quart") ani.callback = self.add_bet_to_pot ani.start(label.rect) self.animations.add(ani) fader = Animation(alpha=0, duration=2100, round_values=True) fader.start(self) self.animations.add(fader) def startup(self, game): self.game = game if not self.game.free_ride: if self.game.player.cash < self.game.bet: self.next = "Bankrupt Screen" self.done = True else: self.game.player.cash -= self.game.bet self.game.casino_player.increase("games played") self.game.casino_player.increase("total bets", self.game.bet) self.game.casino_player.increase("hands played") self.make_labels() def get_event(self, event): if self.window: self.window.get_event(event) else: self.buttons.get_event(event) self.advisor_button.get_event(event) def update(self, dt, scale): mouse_pos = tools.scaled_mouse_pos(scale) self.general_update(dt, mouse_pos) if not self.animations: self.done = True self.next = "Dealing" for label in self.labels: label.image.set_alpha(self.alpha) self.big_label.image.set_alpha(self.big_alpha) def draw(self, surface): if not self.done: surface.fill(prepare.FELT_GREEN) self.game.draw(surface) for p in self.game.players: p.draw(surface) for label in self.labels: label.draw(surface) self.big_label.draw(surface) self.buttons.draw(surface) self.money_icon.draw(surface) self.draw_advisor(surface) if self.window: self.window.draw(surface)
class TitleScreen(data.state.State): """ Initial state of the game. Introduces the game and lets user load a saved game if there's one present. """ name = "title_screen" def __init__(self): super(TitleScreen, self).__init__() self.screen_rect = pg.Rect((0, 0), prepare.RENDER_SIZE) self.marquees = [] self.scrollers, self.marquees = self.make_titles() self.buttons = ButtonGroup() self.new_game, self.load_game = self.make_buttons() self.lights = self.make_spotlights() self.use_music_handler = False def make_spotlights(self): lights = pg.sprite.LayeredDirty() y = self.screen_rect.bottom+20 for i in range(1,5): start = 0 if i%2 else 0.5 pos = (i*self.screen_rect.w/5, y) spotlight.SpotLight(pos, 4, 140, start, lights) return lights def make_titles(self): font = prepare.FONTS["Saniretro"] scrollers = pg.sprite.Group() marquees = pg.sprite.Group() pos = {"right" : 0, "centery": self.screen_rect.y+123} Scroller(pos, prepare.GFX["pyrollers_shiny"], 0.6, scrollers) pos = {"center": (self.screen_rect.centerx, pos["centery"])} MarqueeFrame(pos, prepare.GFX["pyrollers_shiny"], 20, 120, marquees) pos = {"left" : self.screen_rect.w, "centery": 370} Scroller(pos, prepare.GFX["casino_shiny"], -0.6, scrollers) pos = {"center": (self.screen_rect.centerx, pos["centery"])} MarqueeFrame(pos, prepare.GFX["casino_shiny"], 20, 120, marquees) return scrollers, marquees def make_buttons(self): x = self.screen_rect.centerx-(NeonButton.width//2) y = 600 new_game = NeonButton((x, y), "New", self.load_or_new, False, self.buttons, visible=False) y = new_game.rect.bottom + 50 load_game = NeonButton((x, y), "Load", self.load_or_new, True, self.buttons, visible=False) return new_game, load_game def load_or_new(self, try_to_load_data): if try_to_load_data: self.persist = self.controller.load_persist_from_disk() self.next = "lobby" self.done = True def get_event(self, event, scale): if event.type == pg.QUIT: self.done = True self.quit = True elif event.type == pg.MOUSEBUTTONDOWN and event.button == 1: for scroller in self.scrollers: scroller.done = True elif event.type == pg.KEYUP: if event.key == pg.K_ESCAPE: self.done = True self.quit = True self.buttons.get_event(event) def cleanup(self): spotlight.SpotLight.clear_cache() # Clear cache to reclaim memory. return super(TitleScreen, self).cleanup() def update(self, surface, keys, current_time, dt, scale): mouse_pos = tools.scaled_mouse_pos(scale) self.buttons.update(mouse_pos) self.scrollers.update(self.screen_rect, dt) if self.scrollers: if all(scroller.done for scroller in self.scrollers): self.scrollers.empty() else: self.marquees.update(dt) self.new_game.visible = True self.load_game.visible = self.controller.saved_stats_are_available self.lights.update(dt) self.draw(surface, dt) def draw(self, surface, dt): surface.fill(prepare.BACKGROUND_BASE) self.scrollers.draw(surface) if not self.scrollers: self.marquees.draw(surface) self.lights.draw(surface) self.buttons.draw(surface)
class ShowResults(GutsState): def __init__(self): super(ShowResults, self).__init__() self.buttons = ButtonGroup() pos = (self.screen_rect.right-(NeonButton.width+10), self.screen_rect.bottom-(NeonButton.height+10)) self.lobby_button = NeonButton(pos, "Lobby", self.warn, None, self.buttons, bindings=[pg.K_ESCAPE]) def make_player_buttons(self, free_ride): self.player_buttons = ButtonGroup() w, h = NeonButton.width, NeonButton.height sr = self.screen_rect pos = sr.centerx - (w//2), sr.centery + 120 text = "OK" if free_ride else "Ante Up" NeonButton(pos, text, self.new_game, None, self.player_buttons) def new_game(self, *args): self.game.game_over = True self.done = True self.next = "Betting" self.advisor.empty() def add_player_cash(self, amount): self.game.player.cash += amount def add_to_pot(self, amount): self.game.pot += amount def toggle_buttons(self, boolean): for button in self.player_buttons: button.active = boolean button.visible = boolean def cash_advance(self, amount): acct = self.game.casino_player.account acct.cash_advance(amount) self.add_player_cash(amount) def startup(self, game): self.game = game self.lobby_button.call = self.warn self.toggled = False self.alpha = 255 self.labels = [] self.blinkers = [] self.calculated = False self.animations = pg.sprite.Group() stayers = [x for x in self.game.players if x.stayed] winners = self.game.get_winners() share = self.game.pot // len(winners) ani_duration = 2000 self.free_ride = False for stayer in stayers: pos = stayer.name_label.rect.center dest = pos[0], pos[1] - 120 cards_center = stayer.cards[0].rect.union(stayer.cards[1].rect).center if stayer not in winners: text = "${}".format(self.game.pot) color = "darkred" stayer.lost = self.game.pot self.free_ride = True dest = self.game.pot_label.rect.center if stayer is self.game.player: pos = self.money_icon.rect.center if self.game.player.cash < stayer.lost: amount = stayer.lost - self.game.player.cash self.cash_advance(amount) msg = "You were forced to take a cash advance to cover your loss. Visit the ATM to view your account." self.notice(msg) self.add_player_cash(-stayer.lost) task = Task(self.add_to_pot, ani_duration, args=[stayer.lost]) self.animations.add(task) lose_label = Blinker(self.font, 96, "Loser", color, {"center": cards_center}, 500) self.animations.add(Task(self.blinkers.append, ani_duration, args=[lose_label])) else: text = "${}".format(share) color = "darkgreen" stayer.won = share pos = self.game.pot_label.rect.center if stayer is self.game.player: self.cha_ching.play() dest = self.money_icon.rect.center task = Task(self.add_player_cash, ani_duration, args=[share]) self.animations.add(task) win_label = Blinker(self.font, 96, "Winner", color, {"center": cards_center}, 500) self.animations.add(Task(self.blinkers.append, ani_duration, args=[win_label])) label = Label(self.font, 128, text, color, {"center": pos}, bg=prepare.FELT_GREEN) label.image.set_colorkey(prepare.FELT_GREEN) self.labels.append(label) move = Animation(centerx=dest[0], centery=dest[1], duration=ani_duration, round_values=True, transition="in_quart") move.start(label.rect) self.animations.add(move) fader = Animation(alpha=0, duration=ani_duration + 200, round_values = True) fader.start(self) self.animations.add(fader) self.game.pot = 0 self.make_player_buttons(self.free_ride) self.toggle_buttons(False) if self.free_ride: self.advice_texts = ["Press OK to play the next round"] else: self.advice_texts = ["Ante Up ${} to play again".format(self.game.bet)] self.advice_texts.append("Press the Lobby button to exit") def fade_labels(self): for label in self.labels: label.image.set_alpha(self.alpha) def update(self, dt, scale): mouse_pos = tools.scaled_mouse_pos(scale) self.general_update(dt, mouse_pos) if not self.window: for blinker in self.blinkers: blinker.update(dt) self.player_buttons.update(mouse_pos) self.fade_labels() if self.alpha < 1 and not self.toggled: self.toggle_buttons(True) self.toggled = True for text in self.advice_texts: self.advisor.queue_text(text, dismiss_after=3500) if not self.free_ride: self.lobby_button.call = self.back_to_lobby def get_event(self, event): if self.window: self.window.get_event(event) else: self.buttons.get_event(event) self.player_buttons.get_event(event) self.advisor_button.get_event(event) def draw(self, surface): surface.fill(prepare.FELT_GREEN) self.game.draw(surface) for p in self.game.players: p.draw(surface) self.money_icon.draw(surface) self.buttons.draw(surface) self.player_buttons.draw(surface) for label in self.labels: label.draw(surface) for blinker in self.blinkers: blinker.draw(surface) self.draw_advisor(surface) if self.window: self.window.draw(surface)
class Keno(data.state.State): """Class to represent a casino game.""" show_in_lobby = True name = 'keno' def __init__(self): super(Keno, self).__init__() self.screen_rect = pg.Rect((0, 0), prepare.RENDER_SIZE) self.game_started = False self.font = prepare.FONTS["Saniretro"] self.advisor = KenoAdvisor() self.mock_label = Label(self.font, 64, 'KENO [WIP]', 'gold3', {'center':(672,102)}) b_width = 360 b_height = 90 side_margin = 10 w = self.screen_rect.right - (b_width + side_margin) h = self.screen_rect.bottom - (b_height+15) self.buttons = ButtonGroup() NeonButton((w, h), "Lobby", self.back_to_lobby, None, self.buttons) self.turns = 16 self.play_max_active = False ball_path = os.path.join('resources', 'keno', 'balls', '64x64', 'sheet.png') ball_sheet = pg.image.load(ball_path).convert_alpha() self.balls = tools.strip_from_sheet(ball_sheet, (0,0), (64,64), 10, 8) self.keno_card = KenoCard(self.balls) #self.keno_card = KenoCard() -- no ball graphics self.prev_spot_count = 0 self.pay_table = PayTable(self.keno_card) self.pay_table.update(0) self.round_history = RoundHistory(self.keno_card) self.alert = None self.quick_picking = Action(pg.Rect(370, 760, 150, 75), Label(self.font, 32, 'QUICK PICK', 'gold3', {'center':(0,0)}), self.activate_quick_pick) self.betting = Action(pg.Rect(682, 760, 150, 75), Label(self.font, 32, 'BET 1', 'gold3', {'center':(0,0)}), self.activate_bet) self.clearing = Action(pg.Rect(526, 760, 150, 75), Label(self.font, 32, 'CLEAR', 'gold3', {'center':(0,0)}), self.activate_clear) self.playing = Action(pg.Rect(838, 760, 156, 75), Label(self.font, 32, 'PLAY', 'gold3', {'center':(0,0)}), self.activate_play) self.playing_max = Action(pg.Rect(838, 840, 156, 75), Label(self.font, 32, 'PLAY MAX', 'gold3', {'center':(0,0)}), self.activate_playmax) self.actions = { 'quick pick' : self.quick_picking, 'betting' : self.betting, 'clearing' : self.clearing, 'playing' : self.playing, 'playing max' : self.playing_max, } self.gui_widgets = { 'title' : self.mock_label, 'card' : self.keno_card, 'quick_pick' : self.quick_picking, 'play' : self.playing, 'play_max' : self.playing_max, 'pay_table' : self.pay_table, 'round_history' : self.round_history, 'balance' : None, 'bet_action' : None, 'clear' : None, 'bet' : None, 'won' : None, 'spot' : None, } @staticmethod def initialize_stats(): """Return OrderedDict suitable for use in game stats :return: collections.OrderedDict """ stats = OrderedDict([("games played", 0)]) return stats def activate_quick_pick(self): self.keno_card.reset() numbers = pick_numbers(10) for number in numbers: self.keno_card.toggle_owned(number) def activate_bet(self): log.debug("betting activated") try: self.make_bet(1) except InsufficientFundsException: self.handle_insufficient_funds() spot_count = self.keno_card.spot_count self.pay_table.update(spot_count, self.pot._balance) def activate_clear(self): log.debug("clear activated") self.keno_card.ready_play(clear_all=True) self.clear_bet() self.round_history.clear() def validate_configuration(self): if not self.pot.paid and self.pot._balance > 0: try: self.pot.repeat_bet() return True except InsufficientFundsException: self.handle_insufficient_funds() return False elif not self.pot.paid: self.alert = NoticeWindow(self.screen_rect.center, "Please place your bet.") return False spot_count = self.keno_card.spot_count if spot_count <= 0: self.alert = NoticeWindow(self.screen_rect.center, "Please pick your spots.") return False return True def handle_insufficient_funds(self): self.pot.clear_bet(with_payout=False) self.play_max_active = False self.turns = 0 self.alert_insufficient_funds() def alert_insufficient_funds(self): self.alert = NoticeWindow(self.screen_rect.center, "You cannot afford that bet.") def activate_play(self): if not self.validate_configuration(): return numbers = pick_numbers(20) log.debug("pick: {}".format(numbers)) self.keno_card.ready_play() self.keno_card.current_pick = numbers for number in numbers: self.keno_card.toggle_hit(number) self.play_game() def activate_playmax(self): self.round_history.clear() if not self.validate_configuration(): return self.continue_playmax() self.play_game() def continue_playmax(self): log.debug("turns={0}".format(self.turns)) try: self.pot.repeat_bet() self.play_max_active = True numbers = pick_numbers(20) self.keno_card.ready_play() self.keno_card.current_pick = numbers for number in numbers: self.keno_card.toggle_hit(number) self.turns -= 1 if self.turns <= 0: self.play_max_active = False self.turns = 16 except InsufficientFundsException: self.turns = 0 self.handle_insufficient_funds() self.play_max_active = False def make_bet(self, amount): try: self.pot.change_bet(amount) except InsufficientFundsException: self.handle_insufficient_funds() def clear_bet(self): self.pot.clear_bet() def result(self, spot, hit): paytable = PAYTABLE[spot] payment = 0.0 for entry in paytable: if entry[0] == hit: payment = entry[1] if payment > 0.0: break self.pot.payout(payment) self.casino_player.cash = self.wallet.balance def back_to_lobby(self, *args): self.game_started = False self.done = True self.next = "lobby" def startup(self, current_time, persistent): """This method will be called each time the state resumes.""" self.persist = persistent #This is the object that represents the user. self.casino_player = self.persist["casino_player"] self.casino_player.current_game = self.name self.gui_widgets['bet_action'] = self.betting self.gui_widgets['clear'] = self.clearing self.wallet = Wallet(self.casino_player.cash) self.pot = Pot(self.wallet) self.casino_player.increase("games played", 1) def play_game(self): spot_count = self.keno_card.spot_count hit_count = self.keno_card.hit_count self.result(spot_count, hit_count) self.round_history.update(spot_count, hit_count) def get_event(self, event, scale=(1,1)): """This method will be called for each event in the event queue while the state is active. """ if event.type == pg.QUIT and not self.alert: self.done = True self.next = "lobby" elif event.type == pg.MOUSEBUTTONDOWN and not self.alert: #Use tools.scaled_mouse_pos(scale, event.pos) for correct mouse #position relative to the pygame window size. event_pos = tools.scaled_mouse_pos(scale, event.pos) log.info(event_pos) #[for debugging positional items] for action in self.actions.values(): action.execute(event_pos) self.keno_card.update(event_pos) spot_count = self.keno_card.spot_count if spot_count != self.prev_spot_count: self.pay_table.update(spot_count, self.pot._balance) self.prev_spot_count = spot_count if not self.alert: self.buttons.get_event(event) self.alert and self.alert.get_event(event) def draw(self, surface): """This method handles drawing/blitting the state each frame.""" surface.fill(prepare.FELT_GREEN) self.buttons.draw(surface) self.advisor.draw(surface) for widget in self.gui_widgets.values(): if widget: widget.draw(surface) if self.alert and not self.alert.done: self.alert.draw(surface) def update(self, surface, keys, current_time, dt, scale): """ This method will be called once each frame while the state is active. Surface is a reference to the rendering surface which will be scaled to pygame's display surface, keys is the return value of the last call to pygame.key.get_pressed. current_time is the number of milliseconds since pygame was initialized. dt is the number of milliseconds since the last frame. """ self.advisor.update(dt) if self.play_max_active: self.continue_playmax() self.play_game() total_text = "Balance: ${}".format(self.wallet.balance) self.gui_widgets['balance'] = Label(self.font, 48, total_text, "gold3", {"topleft": (24, 760)}) bet_text = "Bet: ${}".format(self.pot._balance) self.gui_widgets['bet'] = Label(self.font, 48, bet_text, "gold3", {"topleft": (24, 760+48)}) won_text = "Won: ${}".format(self.pot.won) self.gui_widgets['won'] = Label(self.font, 48, won_text, "gold3", {"topleft": (24, 760+48+48)}) spot_count = self.keno_card.spot_count spot_text = "Spot: {}".format(spot_count) self.gui_widgets['spot'] = Label(self.font, 48, spot_text, "gold3", {"topleft": (1036, 760)}) mouse_pos = tools.scaled_mouse_pos(scale) self.buttons.update(mouse_pos) if self.alert: self.alert.update(mouse_pos) if self.alert.done: self.alert = None self.draw(surface)
class LevelWin(_State): def __init__(self, controller): super(LevelWin, self).__init__(controller) def startup(self, persistent): self.persist = persistent self.animations = pg.sprite.Group() self.buttons = ButtonGroup() font = prog_constants.FONTS["Fixedsys500c"] color = constants.LOW_LIGHT_GREEN sr = constants.SCREEN_RECT NeonButton((373, 630), "Next Level", 32, self.next_level, None, self.buttons) self.make_city_icons() total = sum((icon.current_points for icon in self.city_icons)) points_text = "${}".format(total) self.points_label = Label(font, 32, points_text, color, {"center": (sr.centerx, 200)}) level_num = self.persist["player"].level_num self.title = Label(font, 48, "Level {} Complete".format(level_num), color, {"midtop": (sr.centerx, 5)}) def make_city_icons(self): level = self.persist["level"] player = self.persist["player"] self.city_icons = [] for city in player.cities: points = 0 if not city.damaged: points = city.population * 100 cx = city.rect.centerx icon = CityIcon((cx, 300), points, city.image) self.city_icons.append(icon) if points: time_scale = points / 1000. dur = min(1000 + (points * time_scale), 5000) ani = Animation(current_points=points, duration=dur, round_values=True) ani.start(icon) self.animations.add(ani) def next_level(self, *args): player = self.persist["player"] total = sum((icon.points for icon in self.city_icons)) player.cash += total player.level_num += 1 for city in player.cities: if city.damaged: city.kill() else: city.population += 1 player.cities = [c for c in player.cities if not c.damaged] player.save() if player.level_num > 42: self.next = "GAME_WIN" else: self.next = "LEVEL_START" level = Level(player) self.persist["level"] = level self.done = True def get_event(self, event, scale): self.buttons.get_event(event) def update(self, surface, keys, current_time, dt, scale): self.animations.update(dt) total = sum((icon.current_points for icon in self.city_icons)) points_text = "${}".format(total) if self.points_label.text != points_text: self.points_label.set_text(points_text) for icon in self.city_icons: icon.update() self.buttons.update(scaled_mouse_pos(scale)) self.draw(surface) def draw(self, surface): surface.fill(constants.BACKGROUND_BASE) for icon in self.city_icons: icon.draw(surface) self.buttons.draw(surface) self.points_label.draw(surface) self.title.draw(surface)
class LobbyScreen(data.state.State): """ This state represents the casino lobby where the player can choose which game they want to play or view their game statistics. This is also the exit point for the game. """ name = "lobby" per_page = 6 def __init__(self): super(LobbyScreen, self).__init__() self.game = None self.chip_curtain = None # Created on startup self.animations = pg.sprite.Group() def collect_game_scenes(self): all_scenes = self.controller.query_all_states() games = OrderedDict() for name, scene in all_scenes.items(): if hasattr(scene, 'show_in_lobby'): games[name] = scene return games def update_screen_buttons(self, games): screen_rect = pg.Rect((0, 0), prepare.RENDER_SIZE) number_of_pages = int(math.ceil(len(games)/float(self.per_page))) self.loop_length = prepare.RENDER_SIZE[0] * number_of_pages self.game_buttons = self.make_game_pages(games, screen_rect, self.per_page) nav_buttons = self.make_navigation_buttons(screen_rect) main_buttons = self.make_main_buttons(screen_rect) self.buttons = ButtonGroup(nav_buttons, main_buttons) def make_game_pages(self, games, screen_rect, per): games = list(games.keys()) groups = (games[i:i+per] for i in range(0,len(games),per)) columns = 3 width, height = GameButton.width, GameButton.height spacer_x, spacer_y = 50, 80 start_x = (screen_rect.w-width*columns-spacer_x*(columns-1))//2 start_y = screen_rect.top+105 step_x, step_y = width+spacer_x, height+spacer_y buttons = ButtonGroup() for offset,group in enumerate(groups): offset *= prepare.RENDER_SIZE[0] for i,game in enumerate(group): y,x = divmod(i, columns) pos = (start_x+step_x*x+offset, start_y+step_y*y) GameButton(pos, game, self.change_state, buttons) return buttons def make_navigation_buttons(self, screen_rect): sheet = prepare.GFX["nav_buttons"] size = (106,101) y = 790 from_center = 15 icons = tools.strip_from_sheet(sheet, (0,0), size, 4) buttons = ButtonGroup() l_kwargs = {"idle_image" : icons[0], "hover_image" : icons[1], "call" : self.scroll_page, "args" : 1, "bindings" : [pg.K_LEFT, pg.K_KP4]} r_kwargs = {"idle_image" : icons[2], "hover_image" : icons[3], "call" : self.scroll_page, "args" : -1, "bindings" : [pg.K_RIGHT, pg.K_KP6]} left = Button(((0,y),size), buttons, **l_kwargs) left.rect.right = screen_rect.centerx-from_center right = Button(((0,y),size), buttons, **r_kwargs) right.rect.x = screen_rect.centerx+from_center return buttons def make_main_buttons(self, screen_rect): buttons = ButtonGroup() pos = (9, screen_rect.bottom-(NeonButton.height+11)) NeonButton(pos, "Credits", self.change_state, "credits", buttons) pos = (screen_rect.right-(NeonButton.width+10), screen_rect.bottom-(NeonButton.height+11)) NeonButton(pos, "Stats", self.change_state, "stats_menu", buttons) pos = (screen_rect.centerx-(NeonButton.width//2), screen_rect.bottom-(NeonButton.height+11)) NeonButton(pos, "Exit", self.exit_game, None, buttons, bindings=[pg.K_ESCAPE]) rect_style = (screen_rect.left, screen_rect.top, 150, 95) Button(rect_style, buttons, idle_image=prepare.GFX["atm_dim"], hover_image=prepare.GFX["atm_bright"], call=self.change_state, args="atm") return buttons def scroll_page(self, mag): if not self.animations: for game in self.game_buttons: self.normalize_scroll(game, mag) fx, fy = game.rect.x+prepare.RENDER_SIZE[0]*mag, game.rect.y ani = Animation(x=fx, y=fy, duration=350.0, transition='in_out_quint', round_values=True) ani.start(game.rect) self.animations.add(ani) prepare.SFX["cardplace4"].play() def normalize_scroll(self, game, mag): if game.rect.x < 0 and mag == -1: game.rect.x += self.loop_length elif game.rect.x >= prepare.RENDER_SIZE[0] and mag == 1: game.rect.x -= self.loop_length def startup(self, current_time, persistent): self.persist = persistent self.chip_curtain = ChipCurtain(None, **CURTAIN_SETTINGS) games = self.collect_game_scenes() self.update_screen_buttons(games) def exit_game(self, *args): with open(os.path.join("resources", "save_game.json"), "w") as f: json.dump(self.persist["casino_player"].stats, f) self.done = True self.quit = True def change_state(self, next_state): self.done = True self.next = next_state def get_event(self, event, scale=(1,1)): if self.game: self.game.get_event(event, scale) elif event.type == pg.QUIT: self.exit_game() else: self.buttons.get_event(event) self.game_buttons.get_event(event) def update(self, surface, keys, current_time, dt, scale): if self.game: self.game.update(surface, keys, current_time, dt, scale) if self.game.done: self.game.cleanup() self.game = None else: mouse_pos = tools.scaled_mouse_pos(scale) self.chip_curtain.update(dt) self.buttons.update(mouse_pos) self.game_buttons.update(mouse_pos) self.animations.update(dt) self.draw(surface) def draw(self, surface): rect = surface.get_rect() surface.fill(prepare.BACKGROUND_BASE) self.chip_curtain.draw(surface) self.buttons.draw(surface) for button in self.game_buttons: if button.rect.colliderect(rect): button.draw(surface)
class LobbyScreen(_State): """ This state represents the lobby where the player can choose which game they want to play or view their high scores. This is also the exit point for the game. """ per_page = 6 def __init__(self, controller): super(LobbyScreen, self).__init__(controller) self.animations = pg.sprite.Group() def update_screen_buttons(self, games): screen_rect = pg.Rect((0, 0), prepare.RENDER_SIZE) number_of_pages = int(math.ceil(len(games) / float(self.per_page))) self.loop_length = prepare.RENDER_SIZE[0] * number_of_pages self.game_buttons = self.make_game_pages(games, screen_rect, self.per_page) nav_buttons = self.make_navigation_buttons(screen_rect) main_buttons = self.make_main_buttons(screen_rect) self.buttons = ButtonGroup(nav_buttons, main_buttons) def make_game_pages(self, games, screen_rect, per): games_list = list(games.keys()) groups = (games_list[i:i+per] for i in range(0, len(games), per)) columns = 3 width, height = GameButton.width, GameButton.height spacer_x, spacer_y = 50, 80 start_x = (screen_rect.w - width * columns - spacer_x * (columns-1))//2 start_y = screen_rect.top + 105 step_x, step_y = width + spacer_x, height + spacer_y buttons = ButtonGroup() for offset,group in enumerate(groups): offset *= prepare.RENDER_SIZE[0] for i,game in enumerate(group): y, x = divmod(i, columns) pos = (start_x + step_x * x + offset, start_y + step_y * y) GameButton(pos, game, games[game], self.change_state, buttons) return buttons def make_navigation_buttons(self, screen_rect): sheet = prepare.GFX["nav_buttons"] size = (53, 50) y = 530 from_center = 15 icons = tools.strip_from_sheet(sheet, (0, 0), size, 4) buttons = ButtonGroup() l_kwargs = {"idle_image" : icons[0], "hover_image" : icons[1], "call" : self.scroll_page, "args" : 1, "bindings" : [pg.K_LEFT, pg.K_KP4]} r_kwargs = {"idle_image" : icons[2], "hover_image" : icons[3], "call" : self.scroll_page, "args" : -1, "bindings" : [pg.K_RIGHT, pg.K_KP6]} left = Button(((0, y), size), buttons, **l_kwargs) left.rect.right = screen_rect.centerx - from_center right = Button(((0, y), size), buttons, **r_kwargs) right.rect.x = screen_rect.centerx + from_center return buttons def make_main_buttons(self, screen_rect): buttons = ButtonGroup() pos = (9, screen_rect.bottom-(NeonButton.height+11)) NeonButton(pos, "Credits", 32, self.change_state, "credits", buttons) pos = (screen_rect.right-(NeonButton.width+10), screen_rect.bottom-(NeonButton.height+11)) NeonButton(pos, "High Scores", 28, self.change_state, "high_scores", buttons) pos = (screen_rect.centerx-(NeonButton.width//2), screen_rect.bottom-(NeonButton.height+11)) NeonButton(pos, "Exit", 32, self.exit_game, None, buttons, bindings=[pg.K_ESCAPE]) rect_style = (screen_rect.left, screen_rect.top, 150, 95) return buttons def scroll_page(self, mag): if not self.animations and len(self.game_buttons) > self.per_page: for game in self.game_buttons: self.normalize_scroll(game, mag) fx, fy = game.rect.x+prepare.RENDER_SIZE[0]*mag, game.rect.y ani = Animation(x=fx, y=fy, duration=350.0, transition='in_out_quint', round_values=True) ani.start(game.rect) self.animations.add(ani) prepare.SFX["cardplace4"].play() def normalize_scroll(self, game, mag): if game.rect.x < 0 and mag == -1: game.rect.x += self.loop_length elif game.rect.x >= prepare.RENDER_SIZE[0] and mag == 1: game.rect.x -= self.loop_length def startup(self, persistent): super(LobbyScreen, self).startup(persistent) games = self.controller.game_thumbs self.update_screen_buttons(games) def exit_game(self, *args): self.done = True self.quit = True def change_state(self, next_state): self.done = True self.next = next_state def get_event(self, event, scale=(1,1)): if event.type == pg.QUIT: self.exit_game() else: self.buttons.get_event(event) self.game_buttons.get_event(event) def update(self, surface, keys, current_time, dt, scale): mouse_pos = tools.scaled_mouse_pos(scale) self.buttons.update(mouse_pos) self.game_buttons.update(mouse_pos) self.animations.update(dt) self.draw(surface) def draw(self, surface): rect = surface.get_rect() surface.fill(prepare.BACKGROUND_BASE) self.buttons.draw(surface) for button in self.game_buttons: if button.rect.colliderect(rect): button.draw(surface)
class StatsMenu(data.state.State): """ This state allows the player to choose which game's stats they want to view or return to the lobby. """ name = "stats_menu" def __init__(self): super(StatsMenu, self).__init__() self.font = prepare.FONTS["Saniretro"] self.title = None self.buttons = ButtonGroup() self.labels = [] self.lines = [] self.use_music_handler = False def collect_games_with_stats(self): for name, scene in self.controller.query_all_states().items(): if hasattr(scene, 'initialize_stats'): yield name def make_labels(self): self.labels = [] cash = self.player.cash balance = self.player.account_balance assets = cash + balance starting_cash = prepare.MONEY profit = assets - starting_cash label_info = [("Cash", cash, 110), ("Account", balance, 150), ("Assets", assets, 198), ("Starting Cash", -starting_cash, 238), ("Profit", profit, 283)] left = 500 right = 900 for name, value, topy in label_info: label1 = Label(self.font, 36, name, "white", {"topleft": (left, topy)}) color = "darkgreen" if value >= 0 else "darkred" label2 = Label(self.font, 36, "{:.2f}".format(value), color, {"topright": (right, topy)}) self.labels.extend([label1, label2]) self.lines = [((left, 193), (right, 193)), ((left, 280), (right, 280))] def make_buttons(self, games, screen_rect, col=2): spacer_x = 20 spacer_y = 20 start_y = 410 start_x = (screen_rect.w - NeonButton.width * col - spacer_x * (col - 1)) // 2 buttons = ButtonGroup() for i, game in enumerate(games): y, x = divmod(i, col) pos = (start_x + x * (NeonButton.width + spacer_x), start_y + y * (NeonButton.height + spacer_y)) button = NeonButton(pos, game, self.view_game_stats, game, buttons) pos = (screen_rect.centerx - (NeonButton.width // 2), screen_rect.bottom - (NeonButton.height + 10)) NeonButton(pos, "Lobby", self.back_to_lobby, None, buttons, bindings=[pg.K_ESCAPE]) return buttons def back_to_lobby(self, *args): self.done = True self.next = "lobby" def view_game_stats(self, game): self.persist["current_game_stats"] = game self.next = "stats_screen" self.done = True def startup(self, current_time, persistent): screen_rect = pg.Rect((0, 0), prepare.RENDER_SIZE) self.title = Label( self.font, 64, "Statistics", "darkred", {"midtop": (screen_rect.centerx, screen_rect.top + 10)}) self.start_time = current_time self.persist = persistent self.player = self.persist["casino_player"] games = self.collect_games_with_stats() self.buttons = self.make_buttons(games, screen_rect, 3) self.make_labels() def get_event(self, event, scale=(1, 1)): if event.type == pg.QUIT: self.done = True self.next = "lobby" self.buttons.get_event(event) def draw(self, surface): surface.fill(prepare.BACKGROUND_BASE) self.title.draw(surface) self.buttons.draw(surface) for label in self.labels: label.draw(surface) for line in self.lines: pg.draw.line(surface, pg.Color("white"), *line) def update(self, surface, keys, current_time, dt, scale): mouse_pos = tools.scaled_mouse_pos(scale) self.buttons.update(mouse_pos) self.draw(surface)