From 015b64541484179851a4303dd0766cc512b126ab Mon Sep 17 00:00:00 2001 From: nhaar <38634785+nhaar@users.noreply.github.com> Date: Thu, 29 Aug 2024 22:28:15 -0300 Subject: [PATCH] Update Card-Jitsu belt progression system to be accurate to the original Club Penguin (#95) * change ninja progress system * make it so ranking up does not reset your progress * make losing against sensei give you experience * remove rank speed from card-jitsu * move get_percentage_to_next_belt * add autocorrection to card-jitsu exp if it is not proper to the rank * fix typo * change the correction for card-jitsu exp --- houdini/handlers/games/ninja/card.py | 40 ++++++++++++++++++---------- houdini/handlers/play/ninja.py | 6 ++++- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/houdini/handlers/games/ninja/card.py b/houdini/handlers/games/ninja/card.py index 32bb747..42f1d09 100644 --- a/houdini/handlers/games/ninja/card.py +++ b/houdini/handlers/games/ninja/card.py @@ -47,8 +47,6 @@ class CardJitsuLogic(IWaddle): StampAwards = {0: 230, 4: 232, 8: 234, 9: 236} StampGroupId = 38 - RankSpeed = 1 - def __init__(self, waddle): super().__init__(waddle) @@ -188,7 +186,7 @@ class CardJitsuLogic(IWaddle): class CardJitsuMatLogic(CardJitsuLogic): - RankSpeed = 0.5 + pass class SenseiLogic(CardJitsuLogic): @@ -238,24 +236,36 @@ async def ninja_rank_up(p, ranks=1): 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.update(ninja_rank=p.ninja_rank + ranks, ninja_progress=0).apply() + await p.update(ninja_rank=p.ninja_rank + ranks).apply() return True +def get_exp_difference_to_next_rank(cur_rank: int) -> int: + return (cur_rank + 1) * 5 + +def get_threshold_for_rank(rank: int) -> int: + # using arithmetic progression sum because the exp structure allows + return (rank + 1) * rank // 2 * 5 async def ninja_progress(p, won=False): - if p.ninja_rank == 0: - await p.update(ninja_progress=100).apply() - elif p.ninja_rank < 9: - speed = type(p.waddle).RankSpeed - if not won: - speed *= 0.5 - points = math.floor((100 / p.ninja_rank) * speed) - await p.update(ninja_progress=p.ninja_progress+points).apply() - if p.ninja_progress >= 100: + # black belts don't need exp, otherwise it could overflow + if p.ninja_rank >= 9: + return + gained_exp = 5 if won else 1 + + previous_progress = p.ninja_progress + cur_rank_threshold = get_threshold_for_rank(p.ninja_rank) + next_rank_threshold = get_threshold_for_rank(p.ninja_rank + 1) + # this is for correcting old versions, where the exp might not be in the proper threshold. + if previous_progress < cur_rank_threshold or previous_progress > next_rank_threshold: + # in this case, ninja_progress is the percentage to next belt + previous_progress = int(cur_rank_threshold + previous_progress * get_exp_difference_to_next_rank(p.ninja_rank) / 100) + + new_progress = previous_progress + gained_exp + await p.update(ninja_progress=new_progress).apply() + if new_progress >= next_rank_threshold: 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] @@ -526,6 +536,8 @@ async def handle_send_sensei_pick(p, action: str, card_id: int): can_rank_up = await ninja_rank_up(p) if can_rank_up: await p.send_xt('cza', p.ninja_rank) + else: + await ninja_progress(p, False) else: for seat_id, ninja in enumerate(p.waddle.ninjas): if not p.waddle.has_cards_to_play(seat_id): diff --git a/houdini/handlers/play/ninja.py b/houdini/handlers/play/ninja.py index 8b7473e..3821cde 100644 --- a/houdini/handlers/play/ninja.py +++ b/houdini/handlers/play/ninja.py @@ -1,7 +1,11 @@ from houdini import handlers from houdini.handlers import XTPacket from houdini.data.penguin import Penguin +from houdini.handlers.games.ninja.card import get_threshold_for_rank, get_exp_difference_to_next_rank +# rank doesn't need to be known, but requiring it since it is always known and is simpler/faster to compute +def get_percentage_to_next_belt(xp: int, rank: int) -> int: + return int(((xp - get_threshold_for_rank(rank)) / get_exp_difference_to_next_rank(rank)) * 100) @handlers.handler(XTPacket('ni', 'gnr')) @handlers.cooldown(2) @@ -13,7 +17,7 @@ async def handle_get_ninja_ranks(p, penguin_id: int): @handlers.handler(XTPacket('ni', 'gnl')) async def handle_get_ninja_level(p): - await p.send_xt('gnl', p.ninja_rank, p.ninja_progress, 10) + await p.send_xt('gnl', p.ninja_rank, get_percentage_to_next_belt(p.ninja_progress, p.ninja_rank), 10) @handlers.handler(XTPacket('ni', 'gfl'))