class ATMMenu(ATMState): def __init__(self): super(ATMMenu, self).__init__() self.make_buttons() def set_next(self, next_state): self.beep() self.next = next_state self.done = True def make_buttons(self): self.buttons = ButtonGroup() self.labels = [] buttons = [ ("Balance Inquiry", "ACCOUNTSCREEN", (34, 450)), ("Deposit", "DEPOSIT", (34, 580)), ("Withdrawal", "WITHDRAWAL", (1278, 450)), ("Cash Advance", "ADVANCE", (1278, 580)), ("Exit", "", (1278, 840)), ] for text, next_state_name, topleft in buttons: callback = self.set_next bound = [] if text == "Exit": callback = self.back_to_lobby bound = [pg.K_ESCAPE] Button((topleft, (88, 65)), self.buttons, args=next_state_name, call=callback, bindings=bound) if topleft[0] == 34: rect_pos = {"midleft": (topleft[0] + 115, topleft[1] + 32)} else: rect_pos = {"midright": (topleft[0] - 27, topleft[1] + 32)} label = Label(self.font, 36, text, "white", rect_pos, bg="blue2") self.labels.append(label) title = Label( self.font, 48, "Select Transaction Type", "white", {"midtop": (self.screen_rect.centerx, self.screen_rect.top + 80)}, ) self.labels.append(title) def back_to_lobby(self, *args): self.beep() self.quit = True def get_event(self, event, scale): self.buttons.get_event(event) if event.type == pg.QUIT: self.back_to_lobby() def update(self, surface, keys, current, dt, scale, player): self.buttons.update(tools.scaled_mouse_pos(scale)) self.draw(surface) def draw(self, surface): surface.fill(pg.Color("blue2")) for label in self.labels: label.draw(surface)
class ATMMenu(ATMState): def __init__(self): super(ATMMenu, self).__init__() self.make_buttons() def set_next(self, next_state): self.beep() self.next = next_state self.done = True def make_buttons(self): self.buttons = ButtonGroup() self.labels = [] buttons = [("Balance Inquiry", "ACCOUNTSCREEN", (34, 450)), ("Deposit", "DEPOSIT", (34, 580)), ("Withdrawal", "WITHDRAWAL", (1278, 450)), ("Cash Advance", "ADVANCE", (1278, 580)), ("Exit", "", (1278, 840))] for text, next_state_name, topleft in buttons: callback = self.set_next bound = [] if text == "Exit": callback = self.back_to_lobby bound = [pg.K_ESCAPE] Button((topleft, (88, 65)), self.buttons, args=next_state_name, call=callback, bindings=bound) if topleft[0] == 34: rect_pos = {"midleft": (topleft[0] + 115, topleft[1] + 32)} else: rect_pos = {"midright": (topleft[0] - 27, topleft[1] + 32)} label = Label(self.font, 36, text, "white", rect_pos, bg="blue2") self.labels.append(label) title = Label( self.font, 48, "Select Transaction Type", "white", {"midtop": (self.screen_rect.centerx, self.screen_rect.top + 80)}) self.labels.append(title) def back_to_lobby(self, *args): self.beep() self.quit = True def get_event(self, event, scale): self.buttons.get_event(event) if event.type == pg.QUIT: self.back_to_lobby() def update(self, surface, keys, current, dt, scale, player): self.buttons.update(tools.scaled_mouse_pos(scale)) self.draw(surface) def draw(self, surface): surface.fill(pg.Color("blue2")) for label in self.labels: label.draw(surface)
class AccountScreen(ATMState): def __init__(self): super(AccountScreen, self).__init__() self.buttons = ButtonGroup() Button(((1278, 840), (88, 65)), self.buttons, call=self.back_to_menu) def back_to_menu(self, *args): self.beep() self.next = "MAINMENU" self.done = True def get_event(self, event, scale): self.buttons.get_event(event) if event.type == pg.QUIT: self.back_to_menu() elif event.type == pg.KEYUP: if event.key == pg.K_ESCAPE: self.back_to_menu() def update(self, surface, keys, current, dt, scale, player): self.buttons.update(tools.scaled_mouse_pos(scale)) self.labels = [] balance = "Account Balance: ${:.2f}".format(player.account.balance) self.labels.append( Label(self.font, 48, balance, "white", {"topleft": (200, 150)})) self.labels.append( Label(self.font, 36, "Menu", "white", {"midright": (1278 - 27, 840 + 32)})) top = 300 for transaction, amount in player.account.transactions[::-1]: label1 = Label(self.font, 36, transaction, "white", {"topleft": (200, top)}) label2 = Label(self.font, 36, "${:.2f}".format(amount), "white", {"topright": (1220, top)}) self.labels.extend([label1, label2]) top += label1.rect.height self.draw(surface) def draw(self, surface): surface.fill(pg.Color("blue2")) for label in self.labels: label.draw(surface)
class AccountScreen(ATMState): def __init__(self): super(AccountScreen, self).__init__() self.buttons = ButtonGroup() Button(((1278, 840), (88, 65)), self.buttons, call=self.back_to_menu) def back_to_menu(self, *args): self.beep() self.next = "MAINMENU" self.done = True def get_event(self, event, scale): self.buttons.get_event(event) if event.type == pg.QUIT: self.back_to_menu() elif event.type == pg.KEYUP: if event.key == pg.K_ESCAPE: self.back_to_menu() def update(self, surface, keys, current, dt, scale, player): self.buttons.update(tools.scaled_mouse_pos(scale)) self.labels = [] balance = "Account Balance: ${:.2f}".format(player.account.balance) self.labels.append(Label(self.font, 48, balance, "white", {"topleft": (200, 150)})) self.labels.append(Label(self.font, 36, "Menu", "white", {"midright": (1278 - 27, 840 + 32)})) top = 300 for transaction, amount in player.account.transactions[::-1]: label1 = Label(self.font, 36, transaction, "white", {"topleft": (200, top)}) label2 = Label(self.font, 36, "${:.2f}".format(amount), "white", {"topright": (1220, top)}) self.labels.extend([label1, label2]) top += label1.rect.height self.draw(surface) def draw(self, surface): surface.fill(pg.Color("blue2")) for label in self.labels: label.draw(surface)
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 MessageScreen(ATMState): """ Displays a message from the previous state to the screen and returns to the ATM Menu. """ def __init__(self): super(MessageScreen, self).__init__() self.buttons = ButtonGroup() Button(((1278, 840), (88, 65)), self.buttons, call=self.back_to_menu) def startup(self, persistent): self.persist = persistent msg = self.persist["message"] self.label = Label(self.font, 36, msg, "white", {"center": self.screen_rect.center}) self.exit_label = Label(self.font, 36, "OK", "white", {"midright": (1251, 872)}) def update(self, surface, keys, current, dt, scale, player): self.buttons.update(tools.scaled_mouse_pos(scale)) self.draw(surface) def back_to_menu(self, *args): self.beep() self.next = "MAINMENU" self.done = True def get_event(self, event, scale): self.buttons.get_event(event) if event.type == pg.KEYDOWN: if event.key == pg.K_RETURN: self.back_to_menu() def draw(self, surface): surface.fill(pg.Color("blue2")) self.label.draw(surface) self.exit_label.draw(surface)
class MessageScreen(ATMState): """ Displays a message from the previous state to the screen and returns to the ATM Menu. """ def __init__(self): super(MessageScreen, self).__init__() self.buttons = ButtonGroup() Button(((1278, 840), (88, 65)), self.buttons, call=self.back_to_menu) def startup(self, persistent): self.persist = persistent msg = self.persist["message"] self.label = Label(self.font, 36, msg, "white", {"center": self.screen_rect.center}) self.exit_label = Label(self.font, 36, "OK", "white", {"midright": (1251, 872)}) def update(self, surface, keys, current, dt, scale, player): self.buttons.update(tools.scaled_mouse_pos(scale)) self.draw(surface) def back_to_menu(self, *args): self.beep() self.next = "MAINMENU" self.done = True def get_event(self, event, scale): self.buttons.get_event(event) if event.type == pg.KEYDOWN: if event.key == pg.K_RETURN: self.back_to_menu() def draw(self, surface): surface.fill(pg.Color("blue2")) self.label.draw(surface) self.exit_label.draw(surface)
class WithdrawalScreen(ATMState): def __init__(self): super(WithdrawalScreen, self).__init__() self.title = Label( self.font, 36, "Enter Withdrawal Amount", "white", {"midtop": (self.screen_rect.centerx, self.screen_rect.top + 300)}, ) self.make_textbox() self.make_enter_button() def make_enter_button(self): self.buttons = ButtonGroup() self.labels = [] Button(((1278, 840), (88, 65)), self.buttons, bindings=[pg.K_RETURN]) rect_pos = {"midright": (1251, 872)} label = Label(self.font, 36, "Enter", "white", rect_pos, bg="blue2") self.labels.append(label) def make_textbox(self): rect = (self.screen_rect.centerx - 200, self.screen_rect.top + 600, 400, 200) self.textbox = TextBox( rect, outline_color=pg.Color("white"), color=pg.Color("blue2"), font=pg.font.Font(self.font, 36), font_color=pg.Color("white"), ) self.textbox.accepted = string.digits self.dollar_sign = Label(self.font, 36, "$", "white", {"midright": (rect[0] - 5, rect[1] + 100)}) # update needed to set textbox.render_area self.textbox.update() def leave_message(self, msg): self.make_textbox() self.persist["message"] = msg self.next = "MESSAGESCREEN" self.done = True def back_to_menu(self): self.beep() self.next = "MAINMENU" self.done = True def get_event(self, event, scale): self.textbox.get_event(event, tools.scaled_mouse_pos(scale)) self.buttons.get_event(event) if event.type == pg.QUIT: self.back_to_menu() elif event.type == pg.KEYUP: if event.key == pg.K_ESCAPE: self.back_to_menu() def update(self, surface, keys, current, dt, scale, player): self.textbox.update() if not self.textbox.active: self.beep() try: amount = int(self.textbox.final) except ValueError: amount = 0 if player.account.balance >= amount: player.account.withdrawal(amount) player.cash += amount self.leave_message("${:.2f} Withdrawn".format(amount)) else: msg = "Insufficient Funds for Withdrawal" self.leave_message(msg) self.buttons.update(tools.scaled_mouse_pos(scale)) text = "You have ${:.2f} available for withdrawal".format(player.account.balance) self.dyna_label = Label( self.font, 36, text, "white", {"midtop": (self.screen_rect.centerx, self.screen_rect.top + 80)} ) self.draw(surface) def draw(self, surface): surface.fill(pg.Color("blue2")) self.title.draw(surface) self.dollar_sign.draw(surface) self.textbox.draw(surface) for label in self.labels: label.draw(surface) self.dyna_label.draw(surface)
class WithdrawalScreen(ATMState): def __init__(self): super(WithdrawalScreen, self).__init__() self.title = Label( self.font, 36, "Enter Withdrawal Amount", "white", {"midtop": (self.screen_rect.centerx, self.screen_rect.top + 300)}) self.make_textbox() self.make_enter_button() def make_enter_button(self): self.buttons = ButtonGroup() self.labels = [] Button(((1278, 840), (88, 65)), self.buttons, bindings=[pg.K_RETURN]) rect_pos = {"midright": (1251, 872)} label = Label(self.font, 36, "Enter", "white", rect_pos, bg="blue2") self.labels.append(label) def make_textbox(self): rect = (self.screen_rect.centerx - 200, self.screen_rect.top + 600, 400, 200) self.textbox = TextBox(rect, outline_color=pg.Color("white"), color=pg.Color("blue2"), font=pg.font.Font(self.font, 36), font_color=pg.Color("white")) self.textbox.accepted = string.digits self.dollar_sign = Label(self.font, 36, "$", "white", {"midright": (rect[0] - 5, rect[1] + 100)}) #update needed to set textbox.render_area self.textbox.update() def leave_message(self, msg): self.make_textbox() self.persist["message"] = msg self.next = "MESSAGESCREEN" self.done = True def back_to_menu(self): self.beep() self.next = "MAINMENU" self.done = True def get_event(self, event, scale): self.textbox.get_event(event, tools.scaled_mouse_pos(scale)) self.buttons.get_event(event) if event.type == pg.QUIT: self.back_to_menu() elif event.type == pg.KEYUP: if event.key == pg.K_ESCAPE: self.back_to_menu() def update(self, surface, keys, current, dt, scale, player): self.textbox.update() if not self.textbox.active: self.beep() try: amount = int(self.textbox.final) except ValueError: amount = 0 if player.account.balance >= amount: player.account.withdrawal(amount) player.cash += amount self.leave_message("${:.2f} Withdrawn".format(amount)) else: msg = "Insufficient Funds for Withdrawal" self.leave_message(msg) self.buttons.update(tools.scaled_mouse_pos(scale)) text = "You have ${:.2f} available for withdrawal".format( player.account.balance) self.dyna_label = Label( self.font, 36, text, "white", {"midtop": (self.screen_rect.centerx, self.screen_rect.top + 80)}) self.draw(surface) def draw(self, surface): surface.fill(pg.Color("blue2")) self.title.draw(surface) self.dollar_sign.draw(surface) self.textbox.draw(surface) for label in self.labels: label.draw(surface) self.dyna_label.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 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 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 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 GutsState(object): font = prepare.FONTS["Saniretro"] deal_sounds = [prepare.SFX["cardshove{}".format(x)] for x in (1, 3, 4)] flip_sounds = [prepare.SFX["cardplace{}".format(x)] for x in (2, 3, 4)] cha_ching = prepare.SFX["coins"] screen_rect = pg.Rect((0, 0), prepare.RENDER_SIZE) money_icon = MoneyIcon((0, screen_rect.bottom - 75)) draw_group = pg.sprite.Group() move_animations = pg.sprite.Group() advisor = Advisor(draw_group, move_animations) advisor.active = True advisor_back = prepare.GFX["advisor_back"] advisor_front = prepare.GFX["advisor_front"] advisor_back_dim = prepare.GFX["advisor_back_dim"] advisor_front_dim = prepare.GFX["advisor_front_dim"] advisor_active = True window = None def __init__(self): self.done = False self.quit = False self.next = None rect = self.advisor_back.get_rect().union( self.advisor_front.get_rect()) self.advisor_button = Button(rect, call=self.toggle_advisor) self.buttons = ButtonGroup() pos = (self.screen_rect.right - (NeonButton.width + 10), self.screen_rect.bottom - (NeonButton.height + 10)) lobby_button = NeonButton(pos, "Lobby", self.back_to_lobby, None, self.buttons, bindings=[pg.K_ESCAPE]) self.animations = pg.sprite.Group() def warn(self, *args): warning = "Exiting the game will abandon the current pot!" GutsState.window = WarningWindow(self.screen_rect.center, warning, self.leave) def notice(self, msg): GutsState.window = NoticeWindow(self.screen_rect.center, msg) def back_to_lobby(self, *args): if self.game.pot: self.warn() else: self.leave() def leave(self): self.quit = True self.advisor.empty() def toggle_advisor(self, *args): GutsState.advisor_active = not GutsState.advisor_active def draw_advisor(self, surface): if self.advisor_active: surface.blit(self.advisor_back, (0, 0)) self.draw_group.draw(surface) surface.blit(self.advisor_front, (0, 0)) else: surface.blit(self.advisor_back_dim, (0, 0)) surface.blit(self.advisor_front_dim, (0, 0)) def general_update(self, dt, mouse_pos): if self.window: self.window.update(mouse_pos) if self.window.done: GutsState.window = None else: self.advisor_button.update(mouse_pos) self.animations.update(dt) self.buttons.update(mouse_pos) self.money_icon.update(self.game.player.cash) self.advisor_button.update(mouse_pos) if self.advisor_active: self.move_animations.update(dt) self.game.update() def startup(self, game): self.game = game def update(self, dt, scale): pass def get_event(self, event): pass def draw(self, surface): pass def play_deal_sound(self): choice(self.deal_sounds).play() def play_flip_sound(self): choice(self.flip_sounds).play() def play_stay_sound(self): choice(self.stay_sounds).play() def play_pass_sound(self): choice(self.fold_sounds).play()
class AdvanceScreen(ATMState): def __init__(self): super(AdvanceScreen, self).__init__() self.make_textbox() self.title = Label( self.font, 36, "Enter Cash Advance Amount", "white", {"midtop": (self.screen_rect.centerx, self.screen_rect.top + 300)}, ) self.make_enter_button() def make_dynamic_labels(self, player): text = "The maximum advance is ${:.2f}".format(player.account.max_advance) dyna1 = Label(self.font, 36, text, "white", {"midtop": (self.screen_rect.centerx, self.screen_rect.top + 80)}) fee_text = "A {}% fee will be added to the advance amount".format(player.account.lending_rate * 100) dyna2 = Label(self.font, 36, fee_text, "white", {"midtop": (self.screen_rect.centerx, dyna1.rect.bottom + 10)}) self.dynamic_labels = [dyna1, dyna2] def make_enter_button(self): self.button_labels = [] self.buttons = ButtonGroup() Button(((1278, 840), (88, 65)), self.buttons, bindings=[pg.K_RETURN]) rect_pos = {"midright": (1251, 872)} label = Label(self.font, 36, "Enter", "white", rect_pos, bg="blue2") self.button_labels.append(label) def make_textbox(self): rect = (self.screen_rect.centerx - 200, self.screen_rect.top + 600, 400, 200) self.textbox = TextBox( rect, outline_color=pg.Color("white"), color=pg.Color("blue2"), font=pg.font.Font(self.font, 36), font_color=pg.Color("white"), ) self.textbox.accepted = string.digits self.dollar_sign = Label(self.font, 36, "$", "white", {"midright": (rect[0] - 5, rect[1] + 100)}) self.textbox.update() def leave_message(self, msg): self.make_textbox() self.beep() self.persist["message"] = msg self.next = "MESSAGESCREEN" self.done = True def back_to_menu(self): self.beep() self.next = "MAINMENU" self.done = True def get_event(self, event, scale): self.textbox.get_event(event, tools.scaled_mouse_pos(scale)) self.buttons.get_event(event) if event.type == pg.QUIT: self.back_to_menu() elif event.type == pg.KEYUP: if event.key == pg.K_ESCAPE: self.back_to_menu() def update(self, surface, keys, current, dt, scale, player): self.textbox.update() if not self.textbox.active: self.beep() try: amount = int(self.textbox.final) except ValueError: amount = 0 if amount > player.account.max_advance: amount = 0 self.leave_message("You are not authorized for this amount") else: player.account.cash_advance(amount) player.cash += amount msg = "${:.2f} Dispensed".format(amount) self.leave_message(msg) self.buttons.update(tools.scaled_mouse_pos(scale)) self.make_dynamic_labels(player) self.draw(surface) def draw(self, surface): surface.fill(pg.Color("blue2")) self.title.draw(surface) self.dollar_sign.draw(surface) self.textbox.draw(surface) for label in self.dynamic_labels: label.draw(surface) for b_label in self.button_labels: b_label.draw(surface)
class GutsState(object): font = prepare.FONTS["Saniretro"] deal_sounds = [prepare.SFX["cardshove{}".format(x)] for x in (1,3,4)] flip_sounds = [prepare.SFX["cardplace{}".format(x)] for x in (2,3,4)] cha_ching = prepare.SFX["coins"] screen_rect = pg.Rect((0,0), prepare.RENDER_SIZE) money_icon = MoneyIcon((0, screen_rect.bottom - 75)) draw_group = pg.sprite.Group() move_animations = pg.sprite.Group() advisor = Advisor(draw_group, move_animations) advisor.active = True advisor_back = prepare.GFX["advisor_back"] advisor_front = prepare.GFX["advisor_front"] advisor_back_dim = prepare.GFX["advisor_back_dim"] advisor_front_dim = prepare.GFX["advisor_front_dim"] advisor_active = True window = None def __init__(self): self.done = False self.quit = False self.next = None rect = self.advisor_back.get_rect().union(self.advisor_front.get_rect()) self.advisor_button = Button(rect, call=self.toggle_advisor) self.buttons = ButtonGroup() pos = (self.screen_rect.right-(NeonButton.width+10), self.screen_rect.bottom-(NeonButton.height+10)) lobby_button = NeonButton(pos, "Lobby", self.back_to_lobby, None, self.buttons, bindings=[pg.K_ESCAPE]) self.animations = pg.sprite.Group() def warn(self, *args): warning = "Exiting the game will abandon the current pot!" GutsState.window = WarningWindow(self.screen_rect.center, warning, self.leave) def notice(self, msg): GutsState.window = NoticeWindow(self.screen_rect.center, msg) def back_to_lobby(self, *args): if self.game.pot: self.warn() else: self.leave() def leave(self): self.quit = True self.advisor.empty() def toggle_advisor(self, *args): GutsState.advisor_active = not GutsState.advisor_active def draw_advisor(self, surface): if self.advisor_active: surface.blit(self.advisor_back, (0, 0)) self.draw_group.draw(surface) surface.blit(self.advisor_front, (0, 0)) else: surface.blit(self.advisor_back_dim, (0, 0)) surface.blit(self.advisor_front_dim, (0, 0)) def general_update(self, dt, mouse_pos): if self.window: self.window.update(mouse_pos) if self.window.done: GutsState.window = None else: self.advisor_button.update(mouse_pos) self.animations.update(dt) self.buttons.update(mouse_pos) self.money_icon.update(self.game.player.cash) self.advisor_button.update(mouse_pos) if self.advisor_active: self.move_animations.update(dt) self.game.update() def startup(self, game): self.game = game def update(self, dt, scale): pass def get_event(self, event): pass def draw(self, surface): pass def play_deal_sound(self): choice(self.deal_sounds).play() def play_flip_sound(self): choice(self.flip_sounds).play() def play_stay_sound(self): choice(self.stay_sounds).play() def play_pass_sound(self): choice(self.fold_sounds).play()
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 AdvanceScreen(ATMState): def __init__(self): super(AdvanceScreen, self).__init__() self.make_textbox() self.title = Label( self.font, 36, "Enter Cash Advance Amount", "white", {"midtop": (self.screen_rect.centerx, self.screen_rect.top + 300)}) self.make_enter_button() def make_dynamic_labels(self, player): text = "The maximum advance is ${:.2f}".format( player.account.max_advance) dyna1 = Label( self.font, 36, text, "white", {"midtop": (self.screen_rect.centerx, self.screen_rect.top + 80)}) fee_text = "A {}% fee will be added to the advance amount".format( player.account.lending_rate * 100) dyna2 = Label( self.font, 36, fee_text, "white", {"midtop": (self.screen_rect.centerx, dyna1.rect.bottom + 10)}) self.dynamic_labels = [dyna1, dyna2] def make_enter_button(self): self.button_labels = [] self.buttons = ButtonGroup() Button(((1278, 840), (88, 65)), self.buttons, bindings=[pg.K_RETURN]) rect_pos = {"midright": (1251, 872)} label = Label(self.font, 36, "Enter", "white", rect_pos, bg="blue2") self.button_labels.append(label) def make_textbox(self): rect = (self.screen_rect.centerx - 200, self.screen_rect.top + 600, 400, 200) self.textbox = TextBox(rect, outline_color=pg.Color("white"), color=pg.Color("blue2"), font=pg.font.Font(self.font, 36), font_color=pg.Color("white")) self.textbox.accepted = string.digits self.dollar_sign = Label(self.font, 36, "$", "white", {"midright": (rect[0] - 5, rect[1] + 100)}) self.textbox.update() def leave_message(self, msg): self.make_textbox() self.beep() self.persist["message"] = msg self.next = "MESSAGESCREEN" self.done = True def back_to_menu(self): self.beep() self.next = "MAINMENU" self.done = True def get_event(self, event, scale): self.textbox.get_event(event, tools.scaled_mouse_pos(scale)) self.buttons.get_event(event) if event.type == pg.QUIT: self.back_to_menu() elif event.type == pg.KEYUP: if event.key == pg.K_ESCAPE: self.back_to_menu() def update(self, surface, keys, current, dt, scale, player): self.textbox.update() if not self.textbox.active: self.beep() try: amount = int(self.textbox.final) except ValueError: amount = 0 if amount > player.account.max_advance: amount = 0 self.leave_message("You are not authorized for this amount") else: player.account.cash_advance(amount) player.cash += amount msg = "${:.2f} Dispensed".format(amount) self.leave_message(msg) self.buttons.update(tools.scaled_mouse_pos(scale)) self.make_dynamic_labels(player) self.draw(surface) def draw(self, surface): surface.fill(pg.Color("blue2")) self.title.draw(surface) self.dollar_sign.draw(surface) self.textbox.draw(surface) for label in self.dynamic_labels: label.draw(surface) for b_label in self.button_labels: b_label.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 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 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 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 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)