def __init__(self): self.active = True self.group = pg.sprite.Group() self.frames = pg.sprite.Group() self.advisor = Advisor(self.group, self.frames) self.advisor.active = True self.images = { 'back': prepare.GFX["advisor_back"], 'front': prepare.GFX["advisor_front"], 'back_dim': prepare.GFX["advisor_back_dim"], 'front_dim': prepare.GFX["advisor_front_dim"], } self.rect = self.get_rect() self.button = Button(self.rect, call=self.toggle_active) # Just testing to ensure it works... self.advisor.queue_text("Welcome to Keno!", dismiss_after=2500)
class KenoAdvisor(object): def __init__(self): self.active = True self.group = pg.sprite.Group() self.frames = pg.sprite.Group() self.advisor = Advisor(self.group, self.frames) self.advisor.active = True self.images = { 'back': prepare.GFX["advisor_back"], 'front': prepare.GFX["advisor_front"], 'back_dim': prepare.GFX["advisor_back_dim"], 'front_dim': prepare.GFX["advisor_front_dim"], } self.rect = self.get_rect() self.button = Button(self.rect, call=self.toggle_active) # Just testing to ensure it works... self.advisor.queue_text("Welcome to Keno!", dismiss_after=2500) def get_rect(self): return self.images['back'].get_rect().union( self.images['front'].get_rect()) def toggle_active(self): self.active = not self.active def update(self, dt): if self.active: self.frames.update(dt) def draw(self, surface): if self.active: surface.blit(self.images['back'], (0, 0)) self.group.draw(surface) surface.blit(self.images['front'], (0, 0)) else: surface.blit(self.images['back_dim'], (0, 0)) surface.blit(self.images['front_dim'], (0, 0))
class KenoAdvisor(object): def __init__(self): self.active = True self.group = pg.sprite.Group() self.frames = pg.sprite.Group() self.advisor= Advisor(self.group, self.frames) self.advisor.active = True self.images = { 'back' : prepare.GFX["advisor_back"], 'front' : prepare.GFX["advisor_front"], 'back_dim' : prepare.GFX["advisor_back_dim"], 'front_dim' : prepare.GFX["advisor_front_dim"], } self.rect = self.get_rect() self.button = Button(self.rect, call=self.toggle_active) # Just testing to ensure it works... self.advisor.queue_text("Welcome to Keno!", dismiss_after=2500) def get_rect(self): return self.images['back'].get_rect().union(self.images['front'].get_rect()) def toggle_active(self): self.active = not self.active def update(self, dt): if self.active: self.frames.update(dt) def draw(self, surface): if self.active: surface.blit(self.images['back'], (0,0)) self.group.draw(surface) surface.blit(self.images['front'], (0,0)) else: surface.blit(self.images['back_dim'], (0,0)) surface.blit(self.images['front_dim'], (0,0))
def __init__(self): self.active = True self.group = pg.sprite.Group() self.frames = pg.sprite.Group() self.advisor= Advisor(self.group, self.frames) self.advisor.active = True self.images = { 'back' : prepare.GFX["advisor_back"], 'front' : prepare.GFX["advisor_front"], 'back_dim' : prepare.GFX["advisor_back_dim"], 'front_dim' : prepare.GFX["advisor_front_dim"], } self.rect = self.get_rect() self.button = Button(self.rect, call=self.toggle_active) # Just testing to ensure it works... self.advisor.queue_text("Welcome to Keno!", dismiss_after=2500)
def startup(self, now, persistent): self.now = now self.persist = persistent self.casino_player = self.persist['casino_player'] self.get_stats_from_casino_player() # declared here to appease pycharm's syntax checking. # will be filled in when configuration is loaded self.betting_areas = dict() self.dealer_hand = None self.player_hand = None self.player_chips = None self.house_chips = None self.shoe = None # baccarat only: move out later self.confirm_button_rect = None self.interested_events = [ ('SNAP_STACK', self.on_snap_stack), ('RETURN_STACK', self.on_return_stack), ('SNAP_STACK_MOTION', self.on_snap_stack_motion), ('PICKUP_STACK', self.on_pickup_stack), ('DROP_STACK', self.on_drop_stack), ('PICKUP_STACK_MOTION', self.on_pickup_stack_motion), ] names = ["cardshove{}".format(x) for x in (1, 3, 4)] self.shove_sounds = [prepare.SFX[name] for name in names] names = ["cardplace{}".format(x) for x in (2, 3, 4)] self.deal_sounds = [prepare.SFX[name] for name in names] names = ["chipsstack{}".format(x) for x in (3, 5, 6)] self.chip_sounds = [prepare.SFX[name] for name in names] self._allow_exit = True self._enable_chips = False self._mouse_tooltip = None self._background = None self._hovered_sprite = None self._clicked_sprite = None self._hovered_chip_area = None self._grabbed_stack = False self._locked_advice = None self.font = pg.font.Font(prepare.FONTS["Saniretro"], 64) self.large_font = pg.font.Font(prepare.FONTS["Saniretro"], 120) self.button_font = pg.font.Font(prepare.FONTS["Saniretro"], 48) self.hud = SpriteGroup() self.bets = MetaGroup() self.metagroup = MetaGroup() self.metagroup.add(self.bets) self.animations = pg.sprite.Group() self._advisor = Advisor(self.hud, self.animations) self._advisor.queue_text('Welcome to Baccarat', 3000) self.hud.add(NeonButton('lobby', (540, 938, 0, 0), self.goto_lobby)) spr = Sprite() spr.image = prepare.GFX['baccarat-menu-front'] spr.rect = spr.image.get_rect() self.hud.add(spr, layer=1) spr = Sprite() spr.image = prepare.GFX['baccarat-menu-back'] spr.rect = spr.image.get_rect() self.hud.add(spr, layer=-100) self.remove_animations = partial(remove_animations_of, self.animations) self.metagroup.add(self.hud) self.reload_config() self.link_events() self.cash_in() self.new_round()
class TableGame(data.state.State): """Supports vegas style card games with chips and cards """ name = 'table game base' def startup(self, now, persistent): self.now = now self.persist = persistent self.casino_player = self.persist['casino_player'] self.get_stats_from_casino_player() # declared here to appease pycharm's syntax checking. # will be filled in when configuration is loaded self.betting_areas = dict() self.dealer_hand = None self.player_hand = None self.player_chips = None self.house_chips = None self.shoe = None # baccarat only: move out later self.confirm_button_rect = None self.interested_events = [ ('SNAP_STACK', self.on_snap_stack), ('RETURN_STACK', self.on_return_stack), ('SNAP_STACK_MOTION', self.on_snap_stack_motion), ('PICKUP_STACK', self.on_pickup_stack), ('DROP_STACK', self.on_drop_stack), ('PICKUP_STACK_MOTION', self.on_pickup_stack_motion), ] names = ["cardshove{}".format(x) for x in (1, 3, 4)] self.shove_sounds = [prepare.SFX[name] for name in names] names = ["cardplace{}".format(x) for x in (2, 3, 4)] self.deal_sounds = [prepare.SFX[name] for name in names] names = ["chipsstack{}".format(x) for x in (3, 5, 6)] self.chip_sounds = [prepare.SFX[name] for name in names] self._allow_exit = True self._enable_chips = False self._mouse_tooltip = None self._background = None self._hovered_sprite = None self._clicked_sprite = None self._hovered_chip_area = None self._grabbed_stack = False self._locked_advice = None self.font = pg.font.Font(prepare.FONTS["Saniretro"], 64) self.large_font = pg.font.Font(prepare.FONTS["Saniretro"], 120) self.button_font = pg.font.Font(prepare.FONTS["Saniretro"], 48) self.hud = SpriteGroup() self.bets = MetaGroup() self.metagroup = MetaGroup() self.metagroup.add(self.bets) self.animations = pg.sprite.Group() self._advisor = Advisor(self.hud, self.animations) self._advisor.queue_text('Welcome to Baccarat', 3000) self.hud.add(NeonButton('lobby', (540, 938, 0, 0), self.goto_lobby)) spr = Sprite() spr.image = prepare.GFX['baccarat-menu-front'] spr.rect = spr.image.get_rect() self.hud.add(spr, layer=1) spr = Sprite() spr.image = prepare.GFX['baccarat-menu-back'] spr.rect = spr.image.get_rect() self.hud.add(spr, layer=-100) self.remove_animations = partial(remove_animations_of, self.animations) self.metagroup.add(self.hud) self.reload_config() self.link_events() self.cash_in() self.new_round() def reload_config(self): raise NotImplementedError def new_round(self): raise NotImplementedError @staticmethod def initialize_stats(): """Return OrderedDict suitable for use in game stats :return: collections.OrderedDict """ raise NotImplementedError def render_background(self, size): """Render the background Subclasses must implement this :param size: (width, height) in pixels :return: pygame.surface.Surface """ raise NotImplementedError def get_stats_from_casino_player(self): """Get stats for game and set them. Will set defaults if needed. """ self.stats = self.casino_player.stats.get(self.name, None) if self.stats is None: raise RuntimeError def set_casino_player_states(self): # to deal with the awkward new stats system for key, value in self.stats.items(): print(key, value) self.casino_player.set(key, value) def cleanup(self): self.unlink_events() self.set_casino_player_states() return super(TableGame, self).cleanup() def link_events(self): for name, f in self.interested_events: B.linkEvent(name, f) def unlink_events(self): for name, f in self.interested_events: B.unlinkEvent(name, f) def on_pickup_stack(self, *args): """When a stack of chips is picked up """ self._grabbed_stack = True self._locked_advice = None self._advisor.empty() self._advisor.queue_text('Place chips into a betting area', 0) def on_drop_stack(self, *args): """When a stack of chips is dropped anywhere """ # this is a hack until i have a proper metagroup def remove(owner, chips): owner.remove(chips) if owner is not self.player_chips: if owner.value == 0: self.bets.remove(owner) # clear the light areas under chips/betting areas if self._hovered_chip_area is not None: self.clear_area_highlight() self._grabbed_stack = False d = args[0] position = d['position'] owner = d['object'] chips = d['chips'] needs_advice = not self.bets.groups() # this value is used to determine where to # return chips if this bet wins if not hasattr(owner, 'origin'): owner.origin = owner if self._hovered_chip_area is not None: self.clear_area_highlight() self._advisor.empty() # check if chip is dropped onto a bet pile or player chips areas = chain(self.bets.groups(), [self.player_chips]) for area in areas: if area is owner: continue if area.rect.collidepoint(position): remove(owner, chips) area.extend(chips) area.ignore_until_away = True self.clear_background() if not self.bets.groups(): self.hide_bet_confirm_button() return True, area # place chips in betting area for area in self.betting_areas.values(): if area.rect.collidepoint(position): remove(owner, chips) bet = self.place_bet(area.hand, owner.origin, chips) bet.origin = owner.origin bet.rect.bottomleft = position # TODO: should not be hardcoded bet.rect.x -= 32 self.clear_background() if bet.result is None: payout = self.options['tie_payout'] msg = 'Ties pay {} to 1'.format(payout) self._advisor.queue_text(msg, 3000) if bet.result is self.dealer_hand: com = int(self.options['commission'] * 100) msg = 'There is a {}% commission on dealer bets'.format( com) self._advisor.push_text(msg, 3000) if needs_advice: # TODO: remove from baseclass self.show_bet_confirm_button() return True, bet # place chips in the house chips if self.house_chips.rect.collidepoint(position): remove(owner, chips) new_chips = list() for chip in chips: for value in make_change(chip.value, break_down=True): chip = Chip(value) chip.rect.center = self.house_chips.rect.center new_chips.append(chip) self.player_chips.extend(new_chips) def on_pickup_stack_motion(self, *args): """When mouse is hovering over a stack or moving it """ chips, position = args[0] self.on_snap_stack_motion(*args) # check if mouse is hovering over betting area areas = chain(self.betting_areas.values(), [self.player_chips]) if self._hovered_chip_area is None: for area in areas: if area.drop_rect.collidepoint(position): self._hovered_chip_area = area sprite = getattr(area, 'sprite', None) if sprite is None: sprite = Sprite() sprite.rect = area.drop_rect.copy() sprite.image = pg.Surface(sprite.rect.size) sprite.image.fill((255, 255, 255)) area.sprite = sprite self.hud.add(sprite) self.remove_animations(sprite.image) ani = Animation(set_alpha=48, initial=0, duration=500, transition='out_quint') ani.start(sprite.image) self.animations.add(ani) # handle when mouse moves outside previously hovered area elif not self._hovered_chip_area.drop_rect.collidepoint(position): self.clear_area_highlight() def on_snap_stack(self, *args): """When chips snap to cursor for first time """ value = TextSprite('', self.large_font) self._mouse_tooltip = value self.hud.add(value, layer=100) # essentially just updates the tool tip value self.on_snap_stack_motion(*args) if self._locked_advice is None: self._advisor.empty() sprite = self._advisor.queue_text('Click to grab chips', 0) self._locked_advice = sprite def on_return_stack(self, *args): """When a stack of chips was previously snapped, but not any longer """ # remove money tooltip self.hud.remove(self._mouse_tooltip) self._mouse_tooltip = None # remove 'click to grab chips' message if self._locked_advice is not None: self._advisor.dismiss(self._locked_advice) self._locked_advice = None def on_snap_stack_motion(self, *args): """When snapped stack is moved around """ chips, position = args[0] amount = str(chips_to_cash(chips)) sprite = self._mouse_tooltip sprite.text = "${}".format(amount) self.update_tooltip(sprite, position) def update_tooltip(self, sprite, position): sprite.rect.midleft = position sprite.rect.x += 30 def clear_area_highlight(self): self.remove_animations(self._hovered_chip_area.sprite.image) ani = Animation(set_alpha=0, initial=self._hovered_chip_area.sprite.image.get_alpha, duration=500, transition='out_quint') ani.callback = self._hovered_chip_area.sprite.kill ani.start(self._hovered_chip_area.sprite.image) self.animations.add(ani) self._hovered_chip_area = None def get_event(self, event, scale=(1, 1)): if event.type == pg.KEYDOWN: if event.key == pg.K_ESCAPE: self.goto_lobby() return if event.type == pg.MOUSEMOTION: pos = tools.scaled_mouse_pos(scale) sprite = self._clicked_sprite if sprite is not None: sprite.pressed = sprite.rect.collidepoint(pos) for sprite in self.hud.sprites(): if self._hovered_sprite is None: if hasattr(sprite, 'on_mouse_enter'): if sprite.rect.collidepoint(pos): self._hovered_sprite = sprite sprite.on_mouse_enter(pos) elif sprite is self._hovered_sprite: if hasattr(sprite, 'on_mouse_leave'): if not sprite.rect.collidepoint(pos): self._hovered_sprite = None sprite.on_mouse_leave(pos) elif event.type == pg.MOUSEBUTTONDOWN: if event.button == 1: pos = tools.scaled_mouse_pos(scale) for sprite in self.hud.sprites(): if hasattr(sprite, 'on_mouse_click'): if sprite.rect.collidepoint(pos): sprite.pressed = True self._clicked_sprite = sprite elif event.type == pg.MOUSEBUTTONUP: pos = tools.scaled_mouse_pos(scale) sprite = self._clicked_sprite if sprite is not None: if sprite.rect.collidepoint(pos): sprite.pressed = False sprite.on_mouse_click(pos) self._clicked_sprite = None if self._enable_chips: self.player_chips.get_event(event, scale) for bet in self.bets.groups(): bet.get_event(event, scale) def delay(self, amount, callback, args=None, kwargs=None): """Convenience function to delay a function call :param amount: milliseconds to wait until callback is called :param callback: function to call :param args: arguments to pass to callback :param kwargs: keywords to pass to callback :return: Task instance """ task = Task(callback, amount, 1, args, kwargs) self.animations.add(task) return task def deal_card(self, hand): """Shortcut to draw card from shoe and add to a hand :param hand: Deck instance :return: Card instance """ sound = choice(self.deal_sounds) sound.set_volume(1) self.delay(100, sound.play) sound = choice(self.shove_sounds) sound.set_volume(.20) sound.play() card = self.shoe.pop() hand.add(card) return card def place_bet_with_amount(self, result, owner, amount): """Shortcut to place a bet with amount of wager :param result: Deck or None :param owner: ChipsPile instance :param amount: amount to wager in cash (not chips) :return: ChipsPile instance """ chips = owner.withdraw_chips(amount) return self.place_bet(result, owner, chips) def place_bet(self, result, owner, chips): """Shortcut to place a bet with chips :param result: Deck or None :param owner: ChipsPile instance :param chips: Chips to wager with :return: ChipsPile instance """ choice(self.chip_sounds).play() bet = ChipPile((600, 800, 200, 200)) bet.extend(chips) bet.owner = owner bet.result = result self.bets.add(bet) return bet def goto_lobby(self, *args): """Try to exit to the lobby Player will be automatically cashed out if ok """ if self._allow_exit: self.quit() def do_quit(self, *args): """Try to exit to the lobby Player will be automatically cashed out if ok """ self.cash_out() self.done = True self.next = 'lobby' def cash_in(self): """Change player's cash to chips """ chips = cash_to_chips(self.casino_player.stats['cash']) self.casino_player.stats['cash'] = 0 self.player_chips.extend(chips) def cash_out(self): """Change player's chips to cash. Includes any bets on table. """ cash = self.player_chips.value for bet in self.bets.groups(): if bet.origin is self.player_chips: bet.kill_me = True cash += bet.value bet.empty() self.casino_player.stats['cash'] = cash self.player_chips.empty() def get_bet_totals(self): """Get totals of all bets on the table :return: Totals of all bets on the table :rtype: Dict """ totals = defaultdict(int) for name, area in self.betting_areas.items(): for bet in self.bets.groups(): if bet.result is area.hand: result = bet.result if result is None: result = 'tie' else: result = name totals[result] += bet.value return totals def clear_background(self): """Force the background to be redrawn """ self._background = None def update(self, surface, keys, current_time, dt, scale): if self._background is None: image = self.render_background(surface.get_size()) surface.blit(image, (0, 0)) self._background = image self.animations.update(dt) self.metagroup.update(dt) self.metagroup.clear(surface, self._background) self.metagroup.draw(surface)
class TableGame(data.state.State): """Supports vegas style card games with chips and cards """ name = 'table game base' def startup(self, now, persistent): self.now = now self.persist = persistent self.casino_player = self.persist['casino_player'] self.get_stats_from_casino_player() # declared here to appease pycharm's syntax checking. # will be filled in when configuration is loaded self.betting_areas = dict() self.dealer_hand = None self.player_hand = None self.player_chips = None self.house_chips = None self.shoe = None # baccarat only: move out later self.confirm_button_rect = None self.interested_events = [ ('SNAP_STACK', self.on_snap_stack), ('RETURN_STACK', self.on_return_stack), ('SNAP_STACK_MOTION', self.on_snap_stack_motion), ('PICKUP_STACK', self.on_pickup_stack), ('DROP_STACK', self.on_drop_stack), ('PICKUP_STACK_MOTION', self.on_pickup_stack_motion), ] names = ["cardshove{}".format(x) for x in (1, 3, 4)] self.shove_sounds = [prepare.SFX[name] for name in names] names = ["cardplace{}".format(x) for x in (2, 3, 4)] self.deal_sounds = [prepare.SFX[name] for name in names] names = ["chipsstack{}".format(x) for x in (3, 5, 6)] self.chip_sounds = [prepare.SFX[name] for name in names] self._allow_exit = True self._enable_chips = False self._mouse_tooltip = None self._background = None self._hovered_sprite = None self._clicked_sprite = None self._hovered_chip_area = None self._grabbed_stack = False self._locked_advice = None self.font = pg.font.Font(prepare.FONTS["Saniretro"], 64) self.large_font = pg.font.Font(prepare.FONTS["Saniretro"], 120) self.button_font = pg.font.Font(prepare.FONTS["Saniretro"], 48) self.hud = SpriteGroup() self.bets = MetaGroup() self.metagroup = MetaGroup() self.metagroup.add(self.bets) self.animations = pg.sprite.Group() self._advisor = Advisor(self.hud, self.animations) self._advisor.queue_text('Welcome to Baccarat', 3000) self.hud.add(NeonButton('lobby', (540, 938, 0, 0), self.goto_lobby)) spr = Sprite() spr.image = prepare.GFX['baccarat-menu-front'] spr.rect = spr.image.get_rect() self.hud.add(spr, layer=1) spr = Sprite() spr.image = prepare.GFX['baccarat-menu-back'] spr.rect = spr.image.get_rect() self.hud.add(spr, layer=-100) self.remove_animations = partial(remove_animations_of, self.animations) self.metagroup.add(self.hud) self.reload_config() self.link_events() self.cash_in() self.new_round() def reload_config(self): raise NotImplementedError def new_round(self): raise NotImplementedError @staticmethod def initialize_stats(): """Return OrderedDict suitable for use in game stats :return: collections.OrderedDict """ raise NotImplementedError def render_background(self, size): """Render the background Subclasses must implement this :param size: (width, height) in pixels :return: pygame.surface.Surface """ raise NotImplementedError def get_stats_from_casino_player(self): """Get stats for game and set them. Will set defaults if needed. """ self.stats = self.casino_player.stats.get(self.name, None) if self.stats is None: raise RuntimeError def set_casino_player_states(self): # to deal with the awkward new stats system for key, value in self.stats.items(): print(key, value) self.casino_player.set(key, value) def cleanup(self): self.unlink_events() self.set_casino_player_states() return super(TableGame, self).cleanup() def link_events(self): for name, f in self.interested_events: B.linkEvent(name, f) def unlink_events(self): for name, f in self.interested_events: B.unlinkEvent(name, f) def on_pickup_stack(self, *args): """When a stack of chips is picked up """ self._grabbed_stack = True self._locked_advice = None self._advisor.empty() self._advisor.queue_text('Place chips into a betting area', 0) def on_drop_stack(self, *args): """When a stack of chips is dropped anywhere """ # this is a hack until i have a proper metagroup def remove(owner, chips): owner.remove(chips) if owner is not self.player_chips: if owner.value == 0: self.bets.remove(owner) # clear the light areas under chips/betting areas if self._hovered_chip_area is not None: self.clear_area_highlight() self._grabbed_stack = False d = args[0] position = d['position'] owner = d['object'] chips = d['chips'] needs_advice = not self.bets.groups() # this value is used to determine where to # return chips if this bet wins if not hasattr(owner, 'origin'): owner.origin = owner if self._hovered_chip_area is not None: self.clear_area_highlight() self._advisor.empty() # check if chip is dropped onto a bet pile or player chips areas = chain(self.bets.groups(), [self.player_chips]) for area in areas: if area is owner: continue if area.rect.collidepoint(position): remove(owner, chips) area.extend(chips) area.ignore_until_away = True self.clear_background() if not self.bets.groups(): self.hide_bet_confirm_button() return True, area # place chips in betting area for area in self.betting_areas.values(): if area.rect.collidepoint(position): remove(owner, chips) bet = self.place_bet(area.hand, owner.origin, chips) bet.origin = owner.origin bet.rect.bottomleft = position # TODO: should not be hardcoded bet.rect.x -= 32 self.clear_background() if bet.result is None: payout = self.options['tie_payout'] msg = 'Ties pay {} to 1'.format(payout) self._advisor.queue_text(msg, 3000) if bet.result is self.dealer_hand: com = int(self.options['commission'] * 100) msg = 'There is a {}% commission on dealer bets'.format(com) self._advisor.push_text(msg, 3000) if needs_advice: # TODO: remove from baseclass self.show_bet_confirm_button() return True, bet # place chips in the house chips if self.house_chips.rect.collidepoint(position): remove(owner, chips) new_chips = list() for chip in chips: for value in make_change(chip.value, break_down=True): chip = Chip(value) chip.rect.center = self.house_chips.rect.center new_chips.append(chip) self.player_chips.extend(new_chips) def on_pickup_stack_motion(self, *args): """When mouse is hovering over a stack or moving it """ chips, position = args[0] self.on_snap_stack_motion(*args) # check if mouse is hovering over betting area areas = chain(self.betting_areas.values(), [self.player_chips]) if self._hovered_chip_area is None: for area in areas: if area.drop_rect.collidepoint(position): self._hovered_chip_area = area sprite = getattr(area, 'sprite', None) if sprite is None: sprite = Sprite() sprite.rect = area.drop_rect.copy() sprite.image = pg.Surface(sprite.rect.size) sprite.image.fill((255, 255, 255)) area.sprite = sprite self.hud.add(sprite) self.remove_animations(sprite.image) ani = Animation(set_alpha=48, initial=0, duration=500, transition='out_quint') ani.start(sprite.image) self.animations.add(ani) # handle when mouse moves outside previously hovered area elif not self._hovered_chip_area.drop_rect.collidepoint(position): self.clear_area_highlight() def on_snap_stack(self, *args): """When chips snap to cursor for first time """ value = TextSprite('', self.large_font) self._mouse_tooltip = value self.hud.add(value, layer=100) # essentially just updates the tool tip value self.on_snap_stack_motion(*args) if self._locked_advice is None: self._advisor.empty() sprite = self._advisor.queue_text('Click to grab chips', 0) self._locked_advice = sprite def on_return_stack(self, *args): """When a stack of chips was previously snapped, but not any longer """ # remove money tooltip self.hud.remove(self._mouse_tooltip) self._mouse_tooltip = None # remove 'click to grab chips' message if self._locked_advice is not None: self._advisor.dismiss(self._locked_advice) self._locked_advice = None def on_snap_stack_motion(self, *args): """When snapped stack is moved around """ chips, position = args[0] amount = str(chips_to_cash(chips)) sprite = self._mouse_tooltip sprite.text = "${}".format(amount) self.update_tooltip(sprite, position) def update_tooltip(self, sprite, position): sprite.rect.midleft = position sprite.rect.x += 30 def clear_area_highlight(self): self.remove_animations(self._hovered_chip_area.sprite.image) ani = Animation( set_alpha=0, initial=self._hovered_chip_area.sprite.image.get_alpha, duration=500, transition='out_quint') ani.callback = self._hovered_chip_area.sprite.kill ani.start(self._hovered_chip_area.sprite.image) self.animations.add(ani) self._hovered_chip_area = None def get_event(self, event, scale=(1, 1)): if event.type == pg.KEYDOWN: if event.key == pg.K_ESCAPE: self.goto_lobby() return if event.type == pg.MOUSEMOTION: pos = tools.scaled_mouse_pos(scale) sprite = self._clicked_sprite if sprite is not None: sprite.pressed = sprite.rect.collidepoint(pos) for sprite in self.hud.sprites(): if self._hovered_sprite is None: if hasattr(sprite, 'on_mouse_enter'): if sprite.rect.collidepoint(pos): self._hovered_sprite = sprite sprite.on_mouse_enter(pos) elif sprite is self._hovered_sprite: if hasattr(sprite, 'on_mouse_leave'): if not sprite.rect.collidepoint(pos): self._hovered_sprite = None sprite.on_mouse_leave(pos) elif event.type == pg.MOUSEBUTTONDOWN: if event.button == 1: pos = tools.scaled_mouse_pos(scale) for sprite in self.hud.sprites(): if hasattr(sprite, 'on_mouse_click'): if sprite.rect.collidepoint(pos): sprite.pressed = True self._clicked_sprite = sprite elif event.type == pg.MOUSEBUTTONUP: pos = tools.scaled_mouse_pos(scale) sprite = self._clicked_sprite if sprite is not None: if sprite.rect.collidepoint(pos): sprite.pressed = False sprite.on_mouse_click(pos) self._clicked_sprite = None if self._enable_chips: self.player_chips.get_event(event, scale) for bet in self.bets.groups(): bet.get_event(event, scale) def delay(self, amount, callback, args=None, kwargs=None): """Convenience function to delay a function call :param amount: milliseconds to wait until callback is called :param callback: function to call :param args: arguments to pass to callback :param kwargs: keywords to pass to callback :return: Task instance """ task = Task(callback, amount, 1, args, kwargs) self.animations.add(task) return task def deal_card(self, hand): """Shortcut to draw card from shoe and add to a hand :param hand: Deck instance :return: Card instance """ sound = choice(self.deal_sounds) sound.set_volume(1) self.delay(100, sound.play) sound = choice(self.shove_sounds) sound.set_volume(.20) sound.play() card = self.shoe.pop() hand.add(card) return card def place_bet_with_amount(self, result, owner, amount): """Shortcut to place a bet with amount of wager :param result: Deck or None :param owner: ChipsPile instance :param amount: amount to wager in cash (not chips) :return: ChipsPile instance """ chips = owner.withdraw_chips(amount) return self.place_bet(result, owner, chips) def place_bet(self, result, owner, chips): """Shortcut to place a bet with chips :param result: Deck or None :param owner: ChipsPile instance :param chips: Chips to wager with :return: ChipsPile instance """ choice(self.chip_sounds).play() bet = ChipPile((600, 800, 200, 200)) bet.extend(chips) bet.owner = owner bet.result = result self.bets.add(bet) return bet def goto_lobby(self, *args): """Try to exit to the lobby Player will be automatically cashed out if ok """ if self._allow_exit: self.quit() def do_quit(self, *args): """Try to exit to the lobby Player will be automatically cashed out if ok """ self.cash_out() self.done = True self.next = 'lobby' def cash_in(self): """Change player's cash to chips """ chips = cash_to_chips(self.casino_player.stats['cash']) self.casino_player.stats['cash'] = 0 self.player_chips.extend(chips) def cash_out(self): """Change player's chips to cash. Includes any bets on table. """ cash = self.player_chips.value for bet in self.bets.groups(): if bet.origin is self.player_chips: bet.kill_me = True cash += bet.value bet.empty() self.casino_player.stats['cash'] = cash self.player_chips.empty() def get_bet_totals(self): """Get totals of all bets on the table :return: Totals of all bets on the table :rtype: Dict """ totals = defaultdict(int) for name, area in self.betting_areas.items(): for bet in self.bets.groups(): if bet.result is area.hand: result = bet.result if result is None: result = 'tie' else: result = name totals[result] += bet.value return totals def clear_background(self): """Force the background to be redrawn """ self._background = None def update(self, surface, keys, current_time, dt, scale): if self._background is None: image = self.render_background(surface.get_size()) surface.blit(image, (0, 0)) self._background = image self.animations.update(dt) self.metagroup.update(dt) self.metagroup.clear(surface, self._background) self.metagroup.draw(surface)
class BlackjackGame(object): """Represents a single game of blackjack.""" 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"] font = prepare.FONTS["Saniretro"] result_font = prepare.FONTS["Saniretro"] deal_sounds = [ prepare.SFX[name] for name in ["cardplace{}".format(x) for x in (2, 3, 4)] ] chip_sounds = [ prepare.SFX[name] for name in ["chipsstack{}".format(x) for x in (3, 5, 6)] ] chip_size = (48, 30) screen_rect = pg.Rect((0, 0), prepare.RENDER_SIZE) advisor_active = True def __init__(self, casino_player, player_cash, chips=None, chip_pile=None): self.deck = Deck((20, 100), prepare.CARD_SIZE, 40) self.dealer = Dealer() self.chip_rack = ChipRack((1100, 130), self.chip_size) self.moving_stacks = [] self.casino_player = casino_player self.player = Player(self.chip_size, player_cash, chips, chip_pile) self.labels = self.make_labels() self.current_player_hand = self.player.hands[0] self.quick_bet = 0 self.last_bet = 0 rect = self.advisor_back.get_rect().union( self.advisor_front.get_rect()) self.advisor_button = Button(rect, call=self.toggle_advisor) def make_labels(self): labels_info = [("Drop chips in chip rack", 36, "antiquewhite", 100, { "midtop": (self.chip_rack.rect.centerx, self.chip_rack.rect.bottom + 5) }), ("to make change", 36, "antiquewhite", 100, { "midtop": (self.chip_rack.rect.centerx, self.chip_rack.rect.bottom + 60) }), ("Blackjack Pays 3 to 2", 64, "gold3", 120, { "midtop": (580, 300) }), ("Dealer must draw to 16 and stand on 17", 48, "antiquewhite", 100, { "midtop": (580, 240) })] labels = [] for info in labels_info: label = Label(self.font, info[1], info[0], info[2], info[4], bg=prepare.FELT_GREEN) label.image.set_alpha(info[3]) labels.append(label) return labels def toggle_advisor(self, *args): BlackjackGame.advisor_active = not BlackjackGame.advisor_active def tally_hands(self): """ Calculate result of each player hand and set appropriate flag for each hand. """ if self.dealer.hand.blackjack: for hand in self.player.hands: hand.loser = True elif self.dealer.hand.busted: for hand in self.player.hands: if not hand.busted and not hand.blackjack: hand.winner = True else: d_score = self.dealer.hand.best_score() for hand in self.player.hands: if not hand.busted: p_score = hand.best_score() if p_score == 21 and len(hand.cards) == 2: hand.blackjack = True elif p_score < d_score: hand.loser = True elif p_score == d_score: hand.push = True else: hand.winner = True def pay_out(self): """ Calculate player win amounts, update stats and return chips totalling total win amount. """ cash = 0 for hand in self.player.hands: bet = hand.bet.get_chip_total() self.casino_player.increase("hands played") self.casino_player.increase("total bets", bet) if hand.busted: self.casino_player.increase("busts") self.casino_player.increase("hands lost") elif hand.loser: self.casino_player.increase("hands lost") elif hand.blackjack: cash += int(bet * 2.5) self.casino_player.increase("blackjacks") self.casino_player.increase("hands won") elif hand.winner: cash += bet * 2 self.casino_player.increase("hands won") elif hand.push: cash += bet self.casino_player.increase("pushes") self.casino_player.increase("total winnings", cash) chips = cash_to_chips(cash, self.chip_size) return chips def get_event(self, event): self.advisor_button.get_event(event) def update(self, dt, mouse_pos): self.advisor_button.update(mouse_pos) total_text = "Chip Total: ${}".format( self.player.chip_pile.get_chip_total()) screen = self.screen_rect self.chip_total_label = Label( self.font, 48, total_text, "gold3", {"bottomleft": (screen.left + 3, screen.bottom - 3)}) self.chip_rack.update() if self.advisor_active: self.move_animations.update(dt)
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()