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:
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)
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
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]:
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)

View File

@ -1,6 +1,7 @@
import itertools
import math
import random
import enum
from collections import Counter
from dataclasses import dataclass
from typing import Dict, List, Union
@ -10,6 +11,19 @@ from houdini.data.ninja import Card
from houdini.handlers import XTPacket
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
class Played:
@ -44,7 +58,12 @@ class CardJitsuLogic(IWaddle):
ItemAwards = [4025, 4026, 4027, 4028, 4029, 4030, 4031, 4032, 4033, 104]
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
def __init__(self, waddle):
@ -235,7 +254,7 @@ async def ninja_rank_up(p, ranks=1):
if rank in CardJitsuLogic.PostcardAwards:
await p.add_inbox(p.server.postcards[CardJitsuLogic.PostcardAwards[rank]])
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()
return True
@ -266,20 +285,10 @@ async def ninja_progress(p, won=False):
await ninja_rank_up(p)
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):
await ninja_progress(winner.penguin, won=True)
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 loser.penguin.waddle.remove_penguin(loser.penguin)
@ -304,6 +313,8 @@ async def handle_update_game(p):
@handlers.handler(XTPacket('lz', ext='z'))
@handlers.waddle(CardJitsuLogic, CardJitsuMatLogic, SenseiLogic)
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)
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)
@ -357,9 +368,8 @@ async def handle_send_pick(p, action: str, card_id: int):
winner_seat_id = p.waddle.get_round_winner()
if me.chosen.card.id == 256 or opponent.chosen.card.id == 256:
stamp = p.server.stamps[246]
await me.penguin.add_stamp(stamp, notify=True)
await opponent.penguin.add_stamp(stamp, notify=True)
await me.penguin.add_card_jitsu_stamp(CardStamp.SENSEI_CARD)
await opponent.penguin.add_card_jitsu_stamp(CardStamp.SENSEI_CARD)
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)
@ -383,19 +393,18 @@ async def handle_send_pick(p, action: str, card_id: int):
if 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]]
await winner.penguin.add_stamp(stamp, notify=True)
stamp = [CardStamp.ONE_ELEMENT, CardStamp.ELEMENTAL_WIN][win_method]
await winner.penguin.add_card_jitsu_stamp(stamp)
if all(not cards for cards in loser.bank.values()):
stamp = p.server.stamps[238]
await winner.penguin.add_stamp(stamp, notify=True)
await winner.penguin.add_card_jitsu_stamp(
CardStamp.FLAWLESS_VICTORY
)
if sum(1 for cards in winner.bank.values() for _ in cards) >= 9:
stamp = p.server.stamps[248]
await winner.penguin.add_stamp(stamp, notify=True)
await winner.penguin.add_card_jitsu_stamp(CardStamp.FULL_DOJO)
await winner.penguin.update(ninja_matches_won=winner.penguin.ninja_matches_won+1).apply()
if winner.penguin.ninja_matches_won == 25:
stamp = p.server.stamps[240]
await winner.penguin.add_stamp(stamp, notify=True)
await winner.penguin.add_card_jitsu_stamp(CardStamp.MATCH_MASTER)
await ninja_win(winner, loser)
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()
if me.chosen.card.id == 256 or sensei.chosen.card.id == 256:
stamp = p.server.stamps[246]
await p.add_stamp(stamp, notify=True)
await me.penguin.add_card_jitsu_stamp(CardStamp.SENSEI_CARD)
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)
@ -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))
if winner == me:
stamp = p.server.stamps[[244, 242][win_method]]
await p.add_stamp(stamp, notify=True)
stamp = [CardStamp.ONE_ELEMENT, CardStamp.ELEMENTAL_WIN][win_method]
await me.penguin.add_card_jitsu_stamp(stamp)
if all(not cards for cards in sensei.bank.values()):
stamp = p.server.stamps[238]
await p.add_stamp(stamp, notify=True)
await me.penguin.add_card_jitsu_stamp(CardStamp.FLAWLESS_VICTORY)
if sum(1 for cards in me.bank.values() for _ in cards) >= 9:
stamp = p.server.stamps[248]
await p.add_stamp(stamp, notify=True)
await me.penguin.add_card_jitsu_stamp(CardStamp.FULL_DOJO)
await p.update(ninja_matches_won=p.ninja_matches_won + 1).apply()
if p.ninja_matches_won == 25:
stamp = p.server.stamps[240]
await p.add_stamp(stamp, notify=True)
await me.penguin.add_card_jitsu_stamp(CardStamp.MATCH_MASTER)
await ninja_stamps_earned(p)
can_rank_up = await ninja_rank_up(p)
if can_rank_up:
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:
await p.send_xt('cza', p.ninja_rank)
await p.waddle.send_xt('czo', 0, winner_seat_id)
await ninja_stamps_earned(p)
await p.send_xt('zm', 'judge', winner_seat_id)
me.chosen = None

View File

@ -2,6 +2,7 @@ import asyncio
import itertools
import math
import random
import enum
from collections import Counter
from dataclasses import dataclass, field
from typing import List, Union
@ -9,9 +10,18 @@ from typing import List, Union
from houdini import IWaddle, handlers
from houdini.data.ninja import Card
from houdini.handlers import XTPacket
from houdini.handlers.games.ninja.card import ninja_stamps_earned
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
class FireNinja:
@ -37,7 +47,7 @@ class CardJitsuFireLogic(IWaddle):
AutoBattleTimeout = 22
ItemAwards = [6025, 4120, 2013, 1086, 3032]
StampAwards = {2: 256, 4: 262}
StampAwards = {2: FireStamp.FIRE_MIDWAY, 4: FireStamp.FIRE_SUIT}
def __init__(self, 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):
await p.add_inventory(p.server.items[CardJitsuFireLogic.ItemAwards[rank]], notify=False)
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(
fire_ninja_rank=p.fire_ninja_rank + ranks
).apply()
@ -567,18 +577,18 @@ async def end_game_stamps(ninja, finish_position):
if finish_position == 1:
await ninja.penguin.update(fire_matches_won=ninja.penguin.fire_matches_won + 1).apply()
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:
await ninja.penguin.add_stamp(ninja.penguin.server.stamps[268])
await ninja.penguin.add_card_jitsu_stamp(FireStamp.FIRE_EXPERT)
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:
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:
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:
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'))
@ -665,5 +675,5 @@ async def handle_info_ready_sync(p):
@handlers.handler(XTPacket('lz', ext='z'))
@handlers.player_in_room(CardJitsuFireLogic.room_id)
async def handle_leave_game(p):
await ninja_stamps_earned(p)
async def handle_leave_game(p: Penguin):
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.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
class WaterCard:
@ -252,6 +263,13 @@ class WaterPlayer:
cleared: int = 0
"""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:
"""Get the card given its hand ID"""
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]
"""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"""
board_cycle_handler: WaterCycleHandler
@ -643,15 +665,12 @@ class CardJitsuWaterLogic(IWaddle):
).apply()
if winner.penguin.water_matches_won >= 100:
# Water Expert stamp
await winner.penguin.add_stamp(winner.penguin.server.stamps[276])
await winner.penguin.add_card_jitsu_stamp(WaterStamp.WATER_EXPERT)
# Gong! stamp
await winner.penguin.add_stamp(winner.penguin.server.stamps[270])
await winner.penguin.add_card_jitsu_stamp(WaterStamp.GONG)
if winner.two_close >= 2:
# Two Close stamp
await winner.penguin.add_stamp(winner.penguin.server.stamps[286])
await winner.penguin.add_card_jitsu_stamp(WaterStamp.TWO_CLOSE)
# iterate over all players that drowned from the last place order
for row in self.board.rows:
@ -693,7 +712,7 @@ class CardJitsuWaterLogic(IWaddle):
p.server.items[cls.ITEM_AWARDS[rank]], cost=0, notify=False
)
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()
return True
@ -714,6 +733,8 @@ class CardJitsuWaterLogic(IWaddle):
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
player_kill_data = []
for player in players_in_row:
@ -997,12 +1018,15 @@ def get_water_rank_threshold(rank):
async def handle_get_game(p: Penguin):
"""Handle the client entering the game"""
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
await p.send_xt("gz")
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
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:
# Skipping Stones stamp
await player.penguin.add_stamp(player.penguin.server.stamps[288])
await p.add_card_jitsu_stamp(WaterStamp.SKIPPING_STONES)
# CMD_PLAYER_THROW
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
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(
self, clear_session: bool
) -> tuple[str, int, int, int]: