add card-jitsu end game screen

This commit is contained in:
nhaar 2024-09-16 14:36:42 -03:00
parent ed38f66923
commit 0146804287
5 changed files with 124 additions and 58 deletions

View File

@ -78,8 +78,21 @@ async def handle_get_game_over(p, score: int):
if p.room.id == 996: if p.room.id == 996:
return return
if p.room.game and not p.waddle and not p.table: # card-jitsus except snow have special handling
card_jitsu_rooms = [995, 998, 997]
is_card_jitsu = p.room.id in card_jitsu_rooms
# Waddle minigames don't normally need the end screen
if p.waddle and not is_card_jitsu:
return
if p.room.game and not p.table:
coins_earned = determine_coins_earned(p, score) coins_earned = determine_coins_earned(p, score)
if not is_card_jitsu:
if await determine_coins_overdose(p, coins_earned):
return await cheat_ban(p, p.id, comment="Coins overdose")
stamp_info = "", 0, 0, 0 stamp_info = "", 0, 0, 0
if p.room.stamp_group: if p.room.stamp_group:
@ -88,6 +101,10 @@ async def handle_get_game_over(p, score: int):
if stamp_info[1] == stamp_info[2]: if stamp_info[1] == stamp_info[2]:
coins_earned *= 2 coins_earned *= 2
if not is_card_jitsu:
await p.update(
coins=min(p.coins + coins_earned, p.server.config.max_coins)
).apply()
await p.send_xt("zo", p.coins, *stamp_info) await p.send_xt("zo", p.coins, *stamp_info)

View File

@ -1,6 +1,7 @@
import itertools import itertools
import math import math
import random import random
import enum
from collections import Counter from collections import Counter
from dataclasses import dataclass from dataclasses import dataclass
from typing import Dict, List, Union from typing import Dict, List, Union
@ -10,6 +11,19 @@ from houdini.data.ninja import Card
from houdini.handlers import XTPacket from houdini.handlers import XTPacket
from houdini.penguin import Penguin from houdini.penguin import Penguin
class CardStamp(enum.IntEnum):
"""ID of Card-Jitsu stamps"""
GRASSHOPPER = 230
ELEMENTAL_WIN = 242
FINE_STUDENT = 232
FLAWLESS_VICTORY = 238
ONE_ELEMENT = 244
TRUE_NINJA = 234
MATCH_MASTER = 240
NINJA_MASTER = 236
SENSEI_CARD = 246
FULL_DOJO = 248
@dataclass @dataclass
class Played: class Played:
@ -44,7 +58,12 @@ class CardJitsuLogic(IWaddle):
ItemAwards = [4025, 4026, 4027, 4028, 4029, 4030, 4031, 4032, 4033, 104] ItemAwards = [4025, 4026, 4027, 4028, 4029, 4030, 4031, 4032, 4033, 104]
PostcardAwards = {0: 177, 4: 178, 8: 179} PostcardAwards = {0: 177, 4: 178, 8: 179}
StampAwards = {0: 230, 4: 232, 8: 234, 9: 236} StampAwards = {
0: CardStamp.GRASSHOPPER,
4: CardStamp.FINE_STUDENT,
8: CardStamp.TRUE_NINJA,
9: CardStamp.NINJA_MASTER,
}
StampGroupId = 38 StampGroupId = 38
def __init__(self, waddle): def __init__(self, waddle):
@ -235,7 +254,7 @@ async def ninja_rank_up(p, ranks=1):
if rank in CardJitsuLogic.PostcardAwards: if rank in CardJitsuLogic.PostcardAwards:
await p.add_inbox(p.server.postcards[CardJitsuLogic.PostcardAwards[rank]]) await p.add_inbox(p.server.postcards[CardJitsuLogic.PostcardAwards[rank]])
if rank in CardJitsuLogic.StampAwards: if rank in CardJitsuLogic.StampAwards:
await p.add_stamp(p.server.stamps[CardJitsuLogic.StampAwards[rank]]) await p.add_card_jitsu_stamp(CardJitsuLogic.StampAwards[rank])
await p.update(ninja_rank=p.ninja_rank + ranks).apply() await p.update(ninja_rank=p.ninja_rank + ranks).apply()
return True return True
@ -266,20 +285,10 @@ async def ninja_progress(p, won=False):
await ninja_rank_up(p) await ninja_rank_up(p)
await p.send_xt('cza', p.ninja_rank) await p.send_xt('cza', p.ninja_rank)
async def ninja_stamps_earned(p):
game_stamps = [stamp for stamp in p.server.stamps.values() if stamp.group_id == p.room.stamp_group]
collected_stamps = [stamp for stamp in game_stamps if stamp.id in p.stamps]
total_collected_stamps = len(collected_stamps)
total_game_stamps = len(game_stamps)
collected_stamps_string = '|'.join(str(stamp.id) for stamp in collected_stamps)
await p.send_xt('cjsi', collected_stamps_string, total_collected_stamps, total_game_stamps)
async def ninja_win(winner, loser): async def ninja_win(winner, loser):
await ninja_progress(winner.penguin, won=True) await ninja_progress(winner.penguin, won=True)
await ninja_progress(loser.penguin, won=False) await ninja_progress(loser.penguin, won=False)
await ninja_stamps_earned(winner.penguin)
await ninja_stamps_earned(loser.penguin)
await winner.penguin.waddle.remove_penguin(winner.penguin) await winner.penguin.waddle.remove_penguin(winner.penguin)
await loser.penguin.waddle.remove_penguin(loser.penguin) await loser.penguin.waddle.remove_penguin(loser.penguin)
@ -304,6 +313,8 @@ async def handle_update_game(p):
@handlers.handler(XTPacket('lz', ext='z')) @handlers.handler(XTPacket('lz', ext='z'))
@handlers.waddle(CardJitsuLogic, CardJitsuMatLogic, SenseiLogic) @handlers.waddle(CardJitsuLogic, CardJitsuMatLogic, SenseiLogic)
async def handle_leave_game(p): async def handle_leave_game(p):
# sending stamp info fixes the game taking a bit to close when quitting
await p.send_card_jitsu_stamp_info()
seat_id = p.waddle.get_seat_id(p) seat_id = p.waddle.get_seat_id(p)
await p.waddle.send_xt('cz', p.safe_name, f=lambda penguin: penguin is not p) await p.waddle.send_xt('cz', p.safe_name, f=lambda penguin: penguin is not p)
await p.waddle.send_xt('lz', seat_id, f=lambda penguin: penguin is not p) await p.waddle.send_xt('lz', seat_id, f=lambda penguin: penguin is not p)
@ -357,9 +368,8 @@ async def handle_send_pick(p, action: str, card_id: int):
winner_seat_id = p.waddle.get_round_winner() winner_seat_id = p.waddle.get_round_winner()
if me.chosen.card.id == 256 or opponent.chosen.card.id == 256: if me.chosen.card.id == 256 or opponent.chosen.card.id == 256:
stamp = p.server.stamps[246] await me.penguin.add_card_jitsu_stamp(CardStamp.SENSEI_CARD)
await me.penguin.add_stamp(stamp, notify=True) await opponent.penguin.add_card_jitsu_stamp(CardStamp.SENSEI_CARD)
await opponent.penguin.add_stamp(stamp, notify=True)
if me.chosen.card.power_id and me.chosen.card.power_id in CardJitsuLogic.OnPlayed: if me.chosen.card.power_id and me.chosen.card.power_id in CardJitsuLogic.OnPlayed:
await p.waddle.send_xt('zm', 'power', seat_id, opponent_seat_id, me.chosen.card.power_id) await p.waddle.send_xt('zm', 'power', seat_id, opponent_seat_id, me.chosen.card.power_id)
@ -383,19 +393,18 @@ async def handle_send_pick(p, action: str, card_id: int):
if winning_cards: if winning_cards:
await p.waddle.send_xt('czo', 0, winner_seat_id, *(card.id for card in winning_cards)) await p.waddle.send_xt('czo', 0, winner_seat_id, *(card.id for card in winning_cards))
stamp = p.server.stamps[[244, 242][win_method]] stamp = [CardStamp.ONE_ELEMENT, CardStamp.ELEMENTAL_WIN][win_method]
await winner.penguin.add_stamp(stamp, notify=True) await winner.penguin.add_card_jitsu_stamp(stamp)
if all(not cards for cards in loser.bank.values()): if all(not cards for cards in loser.bank.values()):
stamp = p.server.stamps[238] await winner.penguin.add_card_jitsu_stamp(
await winner.penguin.add_stamp(stamp, notify=True) CardStamp.FLAWLESS_VICTORY
)
if sum(1 for cards in winner.bank.values() for _ in cards) >= 9: if sum(1 for cards in winner.bank.values() for _ in cards) >= 9:
stamp = p.server.stamps[248] await winner.penguin.add_card_jitsu_stamp(CardStamp.FULL_DOJO)
await winner.penguin.add_stamp(stamp, notify=True)
await winner.penguin.update(ninja_matches_won=winner.penguin.ninja_matches_won+1).apply() await winner.penguin.update(ninja_matches_won=winner.penguin.ninja_matches_won+1).apply()
if winner.penguin.ninja_matches_won == 25: if winner.penguin.ninja_matches_won == 25:
stamp = p.server.stamps[240] await winner.penguin.add_card_jitsu_stamp(CardStamp.MATCH_MASTER)
await winner.penguin.add_stamp(stamp, notify=True)
await ninja_win(winner, loser) await ninja_win(winner, loser)
else: else:
@ -493,8 +502,7 @@ async def handle_send_sensei_pick(p, action: str, card_id: int):
winner_seat_id = p.waddle.get_round_winner() winner_seat_id = p.waddle.get_round_winner()
if me.chosen.card.id == 256 or sensei.chosen.card.id == 256: if me.chosen.card.id == 256 or sensei.chosen.card.id == 256:
stamp = p.server.stamps[246] await me.penguin.add_card_jitsu_stamp(CardStamp.SENSEI_CARD)
await p.add_stamp(stamp, notify=True)
if me.chosen.card.power_id and me.chosen.card.power_id in CardJitsuLogic.OnPlayed: if me.chosen.card.power_id and me.chosen.card.power_id in CardJitsuLogic.OnPlayed:
await p.send_xt('zm', 'power', 1, 0, me.chosen.card.power_id) await p.send_xt('zm', 'power', 1, 0, me.chosen.card.power_id)
@ -518,21 +526,17 @@ async def handle_send_sensei_pick(p, action: str, card_id: int):
await p.waddle.send_xt('czo', 0, winner_seat_id, *(card.id for card in winning_cards)) await p.waddle.send_xt('czo', 0, winner_seat_id, *(card.id for card in winning_cards))
if winner == me: if winner == me:
stamp = p.server.stamps[[244, 242][win_method]] stamp = [CardStamp.ONE_ELEMENT, CardStamp.ELEMENTAL_WIN][win_method]
await p.add_stamp(stamp, notify=True) await me.penguin.add_card_jitsu_stamp(stamp)
if all(not cards for cards in sensei.bank.values()): if all(not cards for cards in sensei.bank.values()):
stamp = p.server.stamps[238] await me.penguin.add_card_jitsu_stamp(CardStamp.FLAWLESS_VICTORY)
await p.add_stamp(stamp, notify=True)
if sum(1 for cards in me.bank.values() for _ in cards) >= 9: if sum(1 for cards in me.bank.values() for _ in cards) >= 9:
stamp = p.server.stamps[248] await me.penguin.add_card_jitsu_stamp(CardStamp.FULL_DOJO)
await p.add_stamp(stamp, notify=True)
await p.update(ninja_matches_won=p.ninja_matches_won + 1).apply() await p.update(ninja_matches_won=p.ninja_matches_won + 1).apply()
if p.ninja_matches_won == 25: if p.ninja_matches_won == 25:
stamp = p.server.stamps[240] await me.penguin.add_card_jitsu_stamp(CardStamp.MATCH_MASTER)
await p.add_stamp(stamp, notify=True)
await ninja_stamps_earned(p)
can_rank_up = await ninja_rank_up(p) can_rank_up = await ninja_rank_up(p)
if can_rank_up: if can_rank_up:
await p.send_xt('cza', p.ninja_rank) await p.send_xt('cza', p.ninja_rank)
@ -548,7 +552,6 @@ async def handle_send_sensei_pick(p, action: str, card_id: int):
if can_rank_up: if can_rank_up:
await p.send_xt('cza', p.ninja_rank) await p.send_xt('cza', p.ninja_rank)
await p.waddle.send_xt('czo', 0, winner_seat_id) await p.waddle.send_xt('czo', 0, winner_seat_id)
await ninja_stamps_earned(p)
await p.send_xt('zm', 'judge', winner_seat_id) await p.send_xt('zm', 'judge', winner_seat_id)
me.chosen = None me.chosen = None

View File

@ -2,6 +2,7 @@ import asyncio
import itertools import itertools
import math import math
import random import random
import enum
from collections import Counter from collections import Counter
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import List, Union from typing import List, Union
@ -9,9 +10,18 @@ from typing import List, Union
from houdini import IWaddle, handlers from houdini import IWaddle, handlers
from houdini.data.ninja import Card from houdini.data.ninja import Card
from houdini.handlers import XTPacket from houdini.handlers import XTPacket
from houdini.handlers.games.ninja.card import ninja_stamps_earned
from houdini.penguin import Penguin from houdini.penguin import Penguin
class FireStamp(enum.IntEnum):
"""IDs of Card-Jitsu Fire stamps"""
WARM_UP = 252
SCORE_FIRE = 254
FIRE_MIDWAY = 256
STRONG_DEFENCE = 260
FIRE_SUIT = 262
FIRE_NINJA = 264
MAX_ENERGY = 266
FIRE_EXPERT = 268
@dataclass @dataclass
class FireNinja: class FireNinja:
@ -37,7 +47,7 @@ class CardJitsuFireLogic(IWaddle):
AutoBattleTimeout = 22 AutoBattleTimeout = 22
ItemAwards = [6025, 4120, 2013, 1086, 3032] ItemAwards = [6025, 4120, 2013, 1086, 3032]
StampAwards = {2: 256, 4: 262} StampAwards = {2: FireStamp.FIRE_MIDWAY, 4: FireStamp.FIRE_SUIT}
def __init__(self, waddle): def __init__(self, waddle):
super().__init__(waddle) super().__init__(waddle)
@ -524,7 +534,7 @@ async def fire_ninja_rank_up(p, ranks=1):
for rank in range(p.fire_ninja_rank, p.fire_ninja_rank+ranks): for rank in range(p.fire_ninja_rank, p.fire_ninja_rank+ranks):
await p.add_inventory(p.server.items[CardJitsuFireLogic.ItemAwards[rank]], notify=False) await p.add_inventory(p.server.items[CardJitsuFireLogic.ItemAwards[rank]], notify=False)
if rank in CardJitsuFireLogic.StampAwards: if rank in CardJitsuFireLogic.StampAwards:
await p.add_stamp(p.server.stamps[CardJitsuFireLogic.StampAwards[rank]]) await p.add_card_jitsu_stamp(CardJitsuFireLogic.StampAwards[rank])
await p.update( await p.update(
fire_ninja_rank=p.fire_ninja_rank + ranks fire_ninja_rank=p.fire_ninja_rank + ranks
).apply() ).apply()
@ -567,18 +577,18 @@ async def end_game_stamps(ninja, finish_position):
if finish_position == 1: if finish_position == 1:
await ninja.penguin.update(fire_matches_won=ninja.penguin.fire_matches_won + 1).apply() await ninja.penguin.update(fire_matches_won=ninja.penguin.fire_matches_won + 1).apply()
if ninja.penguin.fire_matches_won >= 10: if ninja.penguin.fire_matches_won >= 10:
await ninja.penguin.add_stamp(ninja.penguin.server.stamps[252]) await ninja.penguin.add_card_jitsu_stamp(FireStamp.WARM_UP)
if ninja.penguin.fire_matches_won >= 50: if ninja.penguin.fire_matches_won >= 50:
await ninja.penguin.add_stamp(ninja.penguin.server.stamps[268]) await ninja.penguin.add_card_jitsu_stamp(FireStamp.FIRE_EXPERT)
if ninja.energy >= 6: if ninja.energy >= 6:
await ninja.penguin.add_stamp(ninja.penguin.server.stamps[260]) await ninja.penguin.add_card_jitsu_stamp(FireStamp.STRONG_DEFENCE)
if type(ninja.penguin.waddle) == FireSenseiLogic: if type(ninja.penguin.waddle) == FireSenseiLogic:
await ninja.penguin.add_stamp(ninja.penguin.server.stamps[264]) await ninja.penguin.add_card_jitsu_stamp(FireStamp.FIRE_NINJA)
if ninja.energy_won >= 1: if ninja.energy_won >= 1:
await ninja.penguin.add_stamp(ninja.penguin.server.stamps[254]) await ninja.penguin.add_card_jitsu_stamp(FireStamp.SCORE_FIRE)
if ninja.energy_won >= 3: if ninja.energy_won >= 3:
await ninja.penguin.add_stamp(ninja.penguin.server.stamps[266]) await ninja.penguin.add_card_jitsu_stamp(FireStamp.MAX_ENERGY)
@handlers.handler(XTPacket('gz', ext='z')) @handlers.handler(XTPacket('gz', ext='z'))
@ -665,5 +675,5 @@ async def handle_info_ready_sync(p):
@handlers.handler(XTPacket('lz', ext='z')) @handlers.handler(XTPacket('lz', ext='z'))
@handlers.player_in_room(CardJitsuFireLogic.room_id) @handlers.player_in_room(CardJitsuFireLogic.room_id)
async def handle_leave_game(p): async def handle_leave_game(p: Penguin):
await ninja_stamps_earned(p) await p.send_card_jitsu_stamp_info()

View File

@ -12,6 +12,17 @@ from houdini.handlers import XTPacket
from houdini.penguin import Penguin from houdini.penguin import Penguin
from houdini.data.ninja import Card from houdini.data.ninja import Card
class WaterStamp(enum.IntEnum):
"IDs of Card-Jitsu Water stamps"
GONG = 270
WATERY_FALL = 274
WATER_EXPERT = 276
WATER_MIDWAY = 278
WATER_SUIT = 282
WATER_NINJA = 284
TWO_CLOSE = 286
SKIPPING_STONES = 288
@dataclass @dataclass
class WaterCard: class WaterCard:
@ -252,6 +263,13 @@ class WaterPlayer:
cleared: int = 0 cleared: int = 0
"""Number of stones cleared in the match""" """Number of stones cleared in the match"""
left: bool = False
"""
Whether the player has left the game
Penguins remain in the board even if the player leaves
"""
def get_card(self, hand_id: int) -> WaterCard: def get_card(self, hand_id: int) -> WaterCard:
"""Get the card given its hand ID""" """Get the card given its hand ID"""
return next((card for card in self.hand.cards if card.hand_id == hand_id), None) return next((card for card in self.hand.cards if card.hand_id == hand_id), None)
@ -407,7 +425,11 @@ class CardJitsuWaterLogic(IWaddle):
ITEM_AWARDS = [6026, 4121, 2025, 1087, 3032] ITEM_AWARDS = [6026, 4121, 2025, 1087, 3032]
"""All the items gained from ranking, indexed by their rank""" """All the items gained from ranking, indexed by their rank"""
STAMP_AWARDS = {1: 278, 3: 282, 4: 284} STAMP_AWARDS = {
1: WaterStamp.WATER_MIDWAY,
3: WaterStamp.WATER_SUIT,
4: WaterStamp.WATER_NINJA,
}
"""Map rank and the stamp you gain from LEAVING the rank""" """Map rank and the stamp you gain from LEAVING the rank"""
board_cycle_handler: WaterCycleHandler board_cycle_handler: WaterCycleHandler
@ -643,15 +665,12 @@ class CardJitsuWaterLogic(IWaddle):
).apply() ).apply()
if winner.penguin.water_matches_won >= 100: if winner.penguin.water_matches_won >= 100:
# Water Expert stamp await winner.penguin.add_card_jitsu_stamp(WaterStamp.WATER_EXPERT)
await winner.penguin.add_stamp(winner.penguin.server.stamps[276])
# Gong! stamp await winner.penguin.add_card_jitsu_stamp(WaterStamp.GONG)
await winner.penguin.add_stamp(winner.penguin.server.stamps[270])
if winner.two_close >= 2: if winner.two_close >= 2:
# Two Close stamp await winner.penguin.add_card_jitsu_stamp(WaterStamp.TWO_CLOSE)
await winner.penguin.add_stamp(winner.penguin.server.stamps[286])
# iterate over all players that drowned from the last place order # iterate over all players that drowned from the last place order
for row in self.board.rows: for row in self.board.rows:
@ -693,7 +712,7 @@ class CardJitsuWaterLogic(IWaddle):
p.server.items[cls.ITEM_AWARDS[rank]], cost=0, notify=False p.server.items[cls.ITEM_AWARDS[rank]], cost=0, notify=False
) )
if rank in cls.STAMP_AWARDS: if rank in cls.STAMP_AWARDS:
await p.add_stamp(p.server.stamps[cls.STAMP_AWARDS[rank]]) await p.add_card_jitsu_stamp(cls.STAMP_AWARDS[rank])
await p.update(water_ninja_rank=p.water_ninja_rank + ranks).apply() await p.update(water_ninja_rank=p.water_ninja_rank + ranks).apply()
return True return True
@ -714,6 +733,8 @@ class CardJitsuWaterLogic(IWaddle):
await player.penguin.add_stamp(player.penguin.server.stamps[274]) await player.penguin.add_stamp(player.penguin.server.stamps[274])
await player.penguin.add_card_jitsu_stamp(WaterStamp.WATERY_FALL)
# CMD_PLAYER_KILL, meant for players who lose from falling # CMD_PLAYER_KILL, meant for players who lose from falling
player_kill_data = [] player_kill_data = []
for player in players_in_row: for player in players_in_row:
@ -997,12 +1018,15 @@ def get_water_rank_threshold(rank):
async def handle_get_game(p: Penguin): async def handle_get_game(p: Penguin):
"""Handle the client entering the game""" """Handle the client entering the game"""
seat_id = p.waddle.get_seat_id(p) seat_id = p.waddle.get_seat_id(p)
player = p.waddle.get_player_by_penguin(p) player: WaterPlayer = p.waddle.get_player_by_penguin(p)
# needs to send these or the client dies # needs to send these or the client dies
await p.send_xt("gz") await p.send_xt("gz")
await p.send_xt("jz") await p.send_xt("jz")
# needed to fix client taking a bit to exit game
await p.send_card_jitsu_stamp_info()
# CMD_PLAYER_INDEX # CMD_PLAYER_INDEX
await p.waddle.send_zm_client(player, "po", seat_id) await p.waddle.send_zm_client(player, "po", seat_id)
@ -1121,8 +1145,7 @@ async def handle_throw_card(p: Penguin, *, cell_id: str):
) )
if player.cleared >= 28: if player.cleared >= 28:
# Skipping Stones stamp await p.add_card_jitsu_stamp(WaterStamp.SKIPPING_STONES)
await player.penguin.add_stamp(player.penguin.server.stamps[288])
# CMD_PLAYER_THROW # CMD_PLAYER_THROW
await p.waddle.send_zm( await p.waddle.send_zm(

View File

@ -380,6 +380,19 @@ class Penguin(Spheniscidae, penguin.Penguin):
self.logger.info(f'{self.username} updated their background to \'{item.name}\' ' if item else self.logger.info(f'{self.username} updated their background to \'{item.name}\' ' if item else
f'{self.username} removed their background item') f'{self.username} removed their background item')
async def send_card_jitsu_stamp_info(self):
"""
Send information the client requires to properly display the stamp end screen
"""
stamp_info = await self.get_game_end_stamps_info(False)
await self.send_xt("cjsi", *stamp_info)
async def add_card_jitsu_stamp(self, stamp_id):
"""Correct way of adding a card-jitsu (Regular, Fire, Water) stamp"""
await self.add_stamp(self.server.stamps[stamp_id])
await self.send_card_jitsu_stamp_info()
async def get_game_end_stamps_info( async def get_game_end_stamps_info(
self, clear_session: bool self, clear_session: bool
) -> tuple[str, int, int, int]: ) -> tuple[str, int, int, int]: