mirror of
https://github.com/solero/houdini.git
synced 2024-11-21 21:17:21 +00:00
Improve stamps end screen (#101)
* properly handle players exiting cardjitsu water * make end screens only show stamps from current session * add card-jitsu end game screen * fix oversight with how water players get removed * fix card jitsu fire stamp awards * change session stamps to use redis
This commit is contained in:
parent
f6f9f39c22
commit
e23a7ecf2e
@ -78,30 +78,34 @@ 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 await determine_coins_overdose(p, coins_earned):
|
|
||||||
return await cheat_ban(p, p.id, comment='Coins overdose')
|
|
||||||
|
|
||||||
collected_stamps_string, total_collected_stamps, total_game_stamps, total_stamps = '', 0, 0, 0
|
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:
|
if p.room.stamp_group:
|
||||||
game_stamps = [stamp for stamp in p.server.stamps.values() if stamp.group_id == p.room.stamp_group]
|
stamp_info = await p.get_game_end_stamps_info(True)
|
||||||
collected_stamps = [stamp for stamp in game_stamps if stamp.id in p.stamps]
|
# has all stamps in game
|
||||||
|
if stamp_info[1] == stamp_info[2]:
|
||||||
total_stamps = len([stamp for stamp in p.stamps.values() if p.server.stamps[stamp.stamp_id].group_id])
|
|
||||||
total_collected_stamps = len(collected_stamps)
|
|
||||||
total_game_stamps = len(game_stamps)
|
|
||||||
collected_stamps_string = '|'.join(str(stamp.id) for stamp in collected_stamps)
|
|
||||||
|
|
||||||
if total_collected_stamps == total_game_stamps:
|
|
||||||
coins_earned *= 2
|
coins_earned *= 2
|
||||||
|
|
||||||
await p.update(coins=min(p.coins + coins_earned, p.server.config.max_coins)).apply()
|
if not is_card_jitsu:
|
||||||
await p.send_xt('zo', p.coins,
|
await p.update(
|
||||||
collected_stamps_string,
|
coins=min(p.coins + coins_earned, p.server.config.max_coins)
|
||||||
total_collected_stamps,
|
).apply()
|
||||||
total_game_stamps,
|
await p.send_xt("zo", p.coins, *stamp_info)
|
||||||
total_stamps)
|
|
||||||
|
|
||||||
|
|
||||||
@handlers.handler(XTPacket('ggd', ext='z'), client=ClientType.Vanilla)
|
@handlers.handler(XTPacket('ggd', ext='z'), client=ClientType.Vanilla)
|
||||||
@ -146,3 +150,10 @@ async def handle_game_complete(p, medals: int):
|
|||||||
medals = min(6, medals)
|
medals = min(6, medals)
|
||||||
await p.update(career_medals=p.career_medals + medals,
|
await p.update(career_medals=p.career_medals + medals,
|
||||||
agent_medals=p.agent_medals + medals).apply()
|
agent_medals=p.agent_medals + medals).apply()
|
||||||
|
|
||||||
|
|
||||||
|
@handlers.disconnected
|
||||||
|
@handlers.player_attribute(joined_world=True)
|
||||||
|
async def clear_stamp_sessions(p):
|
||||||
|
"""When disconnected, clear stamps in case any were obtained and not properly handled"""
|
||||||
|
await p.clear_stamps_session()
|
||||||
|
@ -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
|
||||||
|
@ -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 = {1: FireStamp.FIRE_MIDWAY, 3: 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()
|
||||||
|
@ -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
|
||||||
@ -469,6 +491,13 @@ class CardJitsuWaterLogic(IWaddle):
|
|||||||
# original)
|
# original)
|
||||||
self.board = Board(columns=5 if len(waddle.penguins) <= 2 else 7)
|
self.board = Board(columns=5 if len(waddle.penguins) <= 2 else 7)
|
||||||
|
|
||||||
|
async def remove_penguin(self, p: Penguin):
|
||||||
|
player = self.get_player_by_penguin(p)
|
||||||
|
# this may run twice for the same player
|
||||||
|
if player is not None:
|
||||||
|
self.players[player.seat_id].left = True
|
||||||
|
await super().remove_penguin(p)
|
||||||
|
|
||||||
async def send_zm(self, *args):
|
async def send_zm(self, *args):
|
||||||
"""Send a "zm" packet, used for various commands, to the clients"""
|
"""Send a "zm" packet, used for various commands, to the clients"""
|
||||||
await self.send_xt("zm", "&".join(map(str, args)))
|
await self.send_xt("zm", "&".join(map(str, args)))
|
||||||
@ -477,9 +506,11 @@ class CardJitsuWaterLogic(IWaddle):
|
|||||||
"""Send a "zm" packet, used for various commands, to a specific client"""
|
"""Send a "zm" packet, used for various commands, to a specific client"""
|
||||||
await player.penguin.send_xt("zm", "&".join(map(str, args)))
|
await player.penguin.send_xt("zm", "&".join(map(str, args)))
|
||||||
|
|
||||||
def get_player_by_penguin(self, penguin: Penguin) -> WaterPlayer:
|
def get_player_by_penguin(self, penguin: Penguin) -> Union[WaterPlayer, None]:
|
||||||
"""Get the player instance associated with a penguin"""
|
"""Get the player instance associated with a penguin"""
|
||||||
return next(player for player in self.players if player.penguin == penguin)
|
return next(
|
||||||
|
(player for player in self.players if player.penguin == penguin), None
|
||||||
|
)
|
||||||
|
|
||||||
def get_card_generator(self, p: Penguin) -> Generator[WaterCard, None, None]:
|
def get_card_generator(self, p: Penguin) -> Generator[WaterCard, None, None]:
|
||||||
"""Get a generator for a player's cards"""
|
"""Get a generator for a player's cards"""
|
||||||
@ -634,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:
|
||||||
@ -650,7 +678,7 @@ class CardJitsuWaterLogic(IWaddle):
|
|||||||
break
|
break
|
||||||
players_in_row = self.get_players_in_row(row)
|
players_in_row = self.get_players_in_row(row)
|
||||||
for player in players_in_row:
|
for player in players_in_row:
|
||||||
if isinstance(player, WaterSensei):
|
if isinstance(player, WaterSensei) or player.left:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# because winner has already been removed
|
# because winner has already been removed
|
||||||
@ -684,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
|
||||||
@ -700,20 +728,28 @@ class CardJitsuWaterLogic(IWaddle):
|
|||||||
players_in_row = self.get_players_in_row(drop_row)
|
players_in_row = self.get_players_in_row(drop_row)
|
||||||
position = len(self.players)
|
position = len(self.players)
|
||||||
for player in players_in_row:
|
for player in players_in_row:
|
||||||
if player.penguin is not None:
|
if not player.left:
|
||||||
# Watery Fall stamp
|
# Watery Fall stamp
|
||||||
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:
|
||||||
amulet = await self.update_player_progress(
|
amulet = None
|
||||||
player, fell=True, position=position
|
if not player.left:
|
||||||
)
|
amulet = await self.update_player_progress(
|
||||||
|
player, fell=True, position=position
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
amulet = Amulet(None, False)
|
||||||
player_kill_data.append(
|
player_kill_data.append(
|
||||||
f"pk&{player.seat_id}&{position}&{amulet.serialize()}&false"
|
f"pk&{player.seat_id}&{position}&{amulet.serialize()}&false"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
await self.send_zm(":".join(player_kill_data))
|
await self.send_zm(":".join(player_kill_data))
|
||||||
|
|
||||||
# for Two Close stamp
|
# for Two Close stamp
|
||||||
@ -985,12 +1021,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)
|
||||||
|
|
||||||
@ -1109,8 +1148,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(
|
||||||
|
@ -244,6 +244,8 @@ class Penguin(Spheniscidae, penguin.Penguin):
|
|||||||
if stamp.id in self.stamps:
|
if stamp.id in self.stamps:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
await self.server.redis.set(self.get_recent_stamp_key(stamp.id), 1)
|
||||||
|
|
||||||
await self.stamps.insert(stamp_id=stamp.id)
|
await self.stamps.insert(stamp_id=stamp.id)
|
||||||
|
|
||||||
if notify:
|
if notify:
|
||||||
@ -254,6 +256,10 @@ class Penguin(Spheniscidae, penguin.Penguin):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def get_recent_stamp_key(self, stamp_id):
|
||||||
|
"""Get redis key that locates the session recency of a stamp"""
|
||||||
|
return f'{self.id}.{stamp_id}.recentstamp'
|
||||||
|
|
||||||
async def add_inbox(self, postcard, sender_name="sys", sender_id=None, details=""):
|
async def add_inbox(self, postcard, sender_name="sys", sender_id=None, details=""):
|
||||||
penguin_postcard = await PenguinPostcard.create(penguin_id=self.id, sender_id=sender_id,
|
penguin_postcard = await PenguinPostcard.create(penguin_id=self.id, sender_id=sender_id,
|
||||||
postcard_id=postcard.id, details=details)
|
postcard_id=postcard.id, details=details)
|
||||||
@ -381,6 +387,89 @@ 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(
|
||||||
|
self, clear_session: bool
|
||||||
|
) -> tuple[str, int, int, int]:
|
||||||
|
"""
|
||||||
|
Get the info of the stamps at the end of a game that the client requires
|
||||||
|
|
||||||
|
If clear_session is True, the stamps will be marked off and will no longer
|
||||||
|
show up as new stamps at the end of minigames
|
||||||
|
"""
|
||||||
|
(
|
||||||
|
collected_game_stamps_string,
|
||||||
|
total_collected_game_stamps,
|
||||||
|
total_game_stamps,
|
||||||
|
total_stamps,
|
||||||
|
) = ("", 0, 0, 0)
|
||||||
|
|
||||||
|
game_stamps = [
|
||||||
|
stamp
|
||||||
|
for stamp in self.server.stamps.values()
|
||||||
|
if stamp.group_id == self.room.stamp_group
|
||||||
|
]
|
||||||
|
|
||||||
|
game_stamps_ids = [stamp.id for stamp in game_stamps]
|
||||||
|
|
||||||
|
recently_collected_game_stamps = []
|
||||||
|
|
||||||
|
for stamp in self.stamps.values():
|
||||||
|
if stamp.stamp_id in game_stamps_ids:
|
||||||
|
is_recent = await self.server.redis.get(
|
||||||
|
self.get_recent_stamp_key(stamp.stamp_id)
|
||||||
|
)
|
||||||
|
if is_recent:
|
||||||
|
recently_collected_game_stamps.append(stamp)
|
||||||
|
|
||||||
|
collected_game_stamps = [
|
||||||
|
stamp for stamp in game_stamps if (stamp.id in self.stamps and stamp)
|
||||||
|
]
|
||||||
|
|
||||||
|
collected_game_stamps_string = "|".join(
|
||||||
|
str(stamp.stamp_id) for stamp in recently_collected_game_stamps
|
||||||
|
)
|
||||||
|
|
||||||
|
total_collected_game_stamps = len(collected_game_stamps)
|
||||||
|
|
||||||
|
total_game_stamps = len(game_stamps)
|
||||||
|
|
||||||
|
total_stamps = len(
|
||||||
|
[
|
||||||
|
stamp
|
||||||
|
for stamp in self.stamps.values()
|
||||||
|
if self.server.stamps[stamp.stamp_id].group_id
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
if clear_session:
|
||||||
|
await self.clear_stamps_session()
|
||||||
|
|
||||||
|
return (
|
||||||
|
collected_game_stamps_string,
|
||||||
|
total_collected_game_stamps,
|
||||||
|
total_game_stamps,
|
||||||
|
total_stamps,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def clear_stamps_session(self):
|
||||||
|
"""
|
||||||
|
Exits a game session and unmarks all stamps since we are no longer in their session
|
||||||
|
"""
|
||||||
|
async for key in self.server.redis.scan_iter(self.get_recent_stamp_key("*")):
|
||||||
|
await self.server.redis.delete(key)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if self.id is not None:
|
if self.id is not None:
|
||||||
return f'<Penguin ID=\'{self.id}\' Username=\'{self.username}\'>'
|
return f'<Penguin ID=\'{self.id}\' Username=\'{self.username}\'>'
|
||||||
|
Loading…
Reference in New Issue
Block a user