diff --git a/houdini/handlers/redemption/__init__.py b/houdini/handlers/redemption/__init__.py index 3db918d..872dec8 100644 --- a/houdini/handlers/redemption/__init__.py +++ b/houdini/handlers/redemption/__init__.py @@ -1,21 +1,12 @@ from houdini import handlers from houdini.constants import ClientType -from houdini.data.item import Item -from houdini.data.igloo import Furniture, Igloo -from houdini.data import db -from houdini.data.redemption import PenguinRedemptionBook, PenguinRedemptionCode, RedemptionAwardCard, \ - RedemptionAwardFlooring, RedemptionAwardFurniture, RedemptionAwardIgloo, RedemptionAwardItem, \ - RedemptionAwardLocation, RedemptionAwardPuffle, RedemptionAwardPuffleItem, RedemptionCode +from houdini.data.redemption import PenguinRedemptionBook from houdini.handlers import XTPacket -from houdini.handlers.games.ninja.card import ninja_rank_up - -import random -from datetime import datetime @handlers.handler(XTPacket('rjs', ext='red'), pre_login=True, client=ClientType.Vanilla) @handlers.allow_once -async def handle_join_redemption_server_vanilla(p, credentials: str, confirmation_hash: str, lang: str): +async def handle_join_redemption_server_vanilla(p, credentials: str, confirmation_hash: str): pid, _, username, login_key, rdnk, approved, rejected = credentials.split('|') if login_key != p.login_key: @@ -33,7 +24,7 @@ async def handle_join_redemption_server_vanilla(p, credentials: str, confirmatio @handlers.handler(XTPacket('rjs', ext='red'), pre_login=True, client=ClientType.Legacy) @handlers.allow_once -async def handle_join_redemption_server_legacy(p, penguin_id: int, login_key: str, lang: str): +async def handle_join_redemption_server_legacy(p, _, login_key: str): if login_key != p.login_key: return await p.close() @@ -41,372 +32,6 @@ async def handle_join_redemption_server_legacy(p, penguin_id: int, login_key: st tr.setex(f'{p.username}.lkey', p.server.config.auth_ttl, login_key) await tr.execute() - await p.send_xt('rjs', '', 1) - - -@handlers.handler(XTPacket('rsc', ext='red'), pre_login=True, client=ClientType.Legacy) -@handlers.depends_on_packet(XTPacket('rjs', ext='red')) -async def handle_code_legacy(p, redemption_code: str): - query = RedemptionCode.distinct(RedemptionCode.id) \ - .load(cards=RedemptionAwardCard.distinct(RedemptionAwardCard.card_id), - items=RedemptionAwardItem.distinct(RedemptionAwardItem.item_id), - flooring=RedemptionAwardFlooring.distinct(RedemptionAwardFlooring.flooring_id), - furniture=RedemptionAwardFurniture.distinct(RedemptionAwardFurniture.furniture_id), - igloos=RedemptionAwardIgloo.distinct(RedemptionAwardIgloo.igloo_id), - locations=RedemptionAwardLocation.distinct(RedemptionAwardLocation.location_id), - puffles=RedemptionAwardPuffle.distinct(RedemptionAwardPuffle.puffle_id), - puffle_items=RedemptionAwardPuffleItem.distinct(RedemptionAwardPuffleItem.puffle_item_id) - ) \ - .query.where(RedemptionCode.code == redemption_code) - codes = await query.gino.all() - awards = [] - - if not codes: - return await p.send_error(720) - - code = codes[0] - - if code.uses is not None: - redeemed_count = await db.select([db.func.count(PenguinRedemptionCode.code_id)]).where( - PenguinRedemptionCode.code_id == code.id).gino.scalar() - - if redeemed_count >= code.uses: - return await p.send_error(721) - - penguin_redeemed = await PenguinRedemptionCode.query.where((PenguinRedemptionCode.code_id == code.id) & - (PenguinRedemptionCode.penguin_id == p.id)).gino.scalar() - if penguin_redeemed: - return await p.send_error(721) - - if code.expires is not None and code.expires < datetime.now(): - return await p.send_error(726) - - if code.type == 'GOLDEN': - return await p.send_xt('rsc', 'GOLDEN', p.ninja_rank, p.fire_ninja_rank, p.water_ninja_rank, - int(p.fire_ninja_rank > 0), int(p.water_ninja_rank > 0)) - - if code.type == 'CARD': - for award in code.cards: - await p.add_card(p.server.cards[award.card_id]) - - else: - if code.items: - for award in code.items: - awards.append(str(award.item_id)) - await p.add_inventory(p.server.items[award.item_id], notify=False) - - await PenguinRedemptionCode.create(penguin_id=p.id, code_id=code.id) - await p.update(coins=p.coins + code.coins).apply() - return await p.send_xt('rsc', code.type, ','.join(map(str, awards)), code.coins) - - -@handlers.handler(XTPacket('rsc', ext='red'), pre_login=True, client=ClientType.Vanilla) -@handlers.depends_on_packet(XTPacket('rjs', ext='red')) -async def handle_code_vanilla(p, redemption_code: str): - query = RedemptionCode.distinct(RedemptionCode.id)\ - .load(cards=RedemptionAwardCard.distinct(RedemptionAwardCard.card_id), - items=RedemptionAwardItem.distinct(RedemptionAwardItem.item_id), - flooring = RedemptionAwardFlooring.distinct(RedemptionAwardFlooring.flooring_id), - furniture=RedemptionAwardFurniture.distinct(RedemptionAwardFurniture.furniture_id), - igloos=RedemptionAwardIgloo.distinct(RedemptionAwardIgloo.igloo_id), - locations=RedemptionAwardLocation.distinct(RedemptionAwardLocation.location_id), - puffles=RedemptionAwardPuffle.distinct(RedemptionAwardPuffle.puffle_id), - puffle_items=RedemptionAwardPuffleItem.distinct(RedemptionAwardPuffleItem.puffle_item_id) - )\ - .query.where(RedemptionCode.code == redemption_code) - codes = await query.gino.all() - awards = [] - - if not codes: - return await p.send_error(720) - - code = codes[0] - - if code.uses is not None: - redeemed_count = await db.select([db.func.count(PenguinRedemptionCode.code_id)]).where( - PenguinRedemptionCode.code_id == code.id).gino.scalar() - - if redeemed_count >= code.uses: - return await p.send_error(721) - - penguin_redeemed = await PenguinRedemptionCode.query.where((PenguinRedemptionCode.code_id == code.id) & - (PenguinRedemptionCode.penguin_id == p.id)).gino.scalar() - if penguin_redeemed: - return await p.send_error(721) - - if code.expires is not None and code.expires < datetime.now(): - return await p.send_error(726) - - if code.type == 'CATALOG': - num_redeemed_codes = await PenguinRedemptionCode.join(RedemptionCode).count().where( - (PenguinRedemptionCode.penguin_id == p.id) & (RedemptionCode.type == 'CATALOG') - ).gino.scalar() - owned_ids = ','.join((str(item) for item in p.server.items.treasure if item in p.inventory)) - return await p.send_xt('rsc', 'treasurebook', 3, owned_ids, num_redeemed_codes) - - if code.type == 'INNOCENT': - innocent_redeemed_items = { item for item in p.server.items.innocent if item.id in p.inventory } - innocent_redeemed_furniture = { item for item in p.server.furniture.innocent if item.id in p.furniture } - innocent_redeemed = innocent_redeemed_items.union(innocent_redeemed_furniture) - innocent_items = set(p.server.items.innocent + p.server.furniture.innocent) - - innocent_remaining = innocent_items - innocent_redeemed - - choices = random.sample(innocent_remaining, min(len(innocent_remaining), 3)) - if len(innocent_redeemed) + 3 == len(innocent_items): - choices.append(p.server.igloos[53]) - for item in choices: - if type(item) is Item: - awards.append(str(item.id)) - await p.add_inventory(item, notify=False) - elif type(item) is Igloo: - awards.append('g' + str(item.id)) - await p.add_igloo(item, notify=False) - elif type(item) is Furniture: - awards.append('f' + str(item.id)) - await p.add_furniture(item, notify=False) - - await PenguinRedemptionCode.create(penguin_id=p.id, code_id=code[0].id) - - return await p.send_xt('rsc', 'INNOCENT', ','.join(map(str, awards)), - len(innocent_redeemed) + len(choices), - len(innocent_items)) - - if code.type == 'GOLDEN': - return await p.send_xt('rsc', 'GOLDEN', p.ninja_rank, p.fire_ninja_rank, p.water_ninja_rank, 0, - int(p.fire_ninja_rank > 0), int(p.water_ninja_rank > 0), 0) - - if code.type == 'CARD': - for award in code.cards: - awards.append(str(award.card_id)) - await p.add_card(p.server.cards[award.card_id]) - - else: - if code.items: - for award in code.items: - awards.append(str(award.item_id)) - await p.add_inventory(p.server.items[award.item_id], notify=False) - - if code.furniture: - for award in code.furniture: - awards.append('f'+str(award.furniture_id)) - await p.add_furniture(p.server.furniture[award.furniture_id], notify=False) - - if code.igloos: - for award in code.igloos: - awards.append('g'+str(award.igloo_id)) - await p.add_igloo(p.server.igloos[award.igloo_id], notify=False) - - if code.locations: - for award in code.locations: - awards.append('loc'+str(award.location_id)) - await p.add_location(p.server.locations[award.location_id], notify=False) - - if code.flooring: - for award in code.flooring: - awards.append('flr'+str(award.flooring_id)) - await p.add_flooring(p.server.flooring[award.flooring_id], notify=False) - - if code.puffles: - for award in code[0].puffles: - awards.append('p'+str(award.puffle_id)) - - if code.puffle_items: - for award in code.puffle_items: - awards.append('pi'+str(award.puffle_item_id)) - await p.add_puffle_item(p.server.puffle_items[award.puffle_item_id], notify=False) - - - await PenguinRedemptionCode.create(penguin_id=p.id, code_id=code.id) - await p.update(coins=p.coins + code.coins).apply() - coins = "" if code.coins == 0 else code.coins - return await p.send_xt('rsc', code.type, ','.join(map(str, awards)), coins) - - -@handlers.handler(XTPacket('rsgc', ext='red'), pre_login=True) -@handlers.depends_on_packet(XTPacket('rsc', ext='red')) -async def handle_golden_choice(p, redemption_code: str, choice: int): - query = RedemptionCode.distinct(RedemptionCode.id)\ - .load(cards=RedemptionAwardCard.distinct(RedemptionAwardCard.card_id) - )\ - .query.where(RedemptionCode.code == redemption_code) - codes = await query.gino.all() - - if not codes: - return await p.close() - - code = codes[0] - - if len(code.cards) < 6: - return await p.close() - - penguin_redeemed = await PenguinRedemptionCode.query.where((PenguinRedemptionCode.code_id == code.id) & (PenguinRedemptionCode.penguin_id == p.id)).gino.scalar() - - if penguin_redeemed: - return await p.close() - - card_ids = [card.card_id for card in code.cards] - - if choice == 1: - await ninja_rank_up(p) - await p.send_xt('rsgc', ','.join(map(str, card_ids[:4])) + '|' + str(p.ninja_rank)) - - elif choice == 2: - await p.send_xt('rsgc', ','.join(map(str, card_ids[:4])) + '|' + ','.join(map(str, card_ids[-2:]))) - for card in code.cards: - await p.add_card(p.server.cards[card.card_id]) - - if code.uses is None: - await PenguinRedemptionCode.create(penguin_id=p.id, code_id=code.id) - - -@handlers.handler(XTPacket('rscrt', ext='red'), pre_login=True) -@handlers.depends_on_packet(XTPacket('rsc', ext='red')) -async def handle_send_cart(p, redemption_code: str, choice: str, super_exclusive: int): - query = RedemptionCode.distinct(RedemptionCode.id) \ - .load(cards=RedemptionAwardCard.distinct(RedemptionAwardCard.card_id), - items=RedemptionAwardItem.distinct(RedemptionAwardItem.item_id), - flooring=RedemptionAwardFlooring.distinct(RedemptionAwardFlooring.flooring_id), - furniture=RedemptionAwardFurniture.distinct(RedemptionAwardFurniture.furniture_id), - igloos=RedemptionAwardIgloo.distinct(RedemptionAwardIgloo.igloo_id), - locations=RedemptionAwardLocation.distinct(RedemptionAwardLocation.location_id), - puffles=RedemptionAwardPuffle.distinct(RedemptionAwardPuffle.puffle_id), - puffle_items=RedemptionAwardPuffleItem.distinct(RedemptionAwardPuffleItem.puffle_item_id) - ) \ - .query.where(RedemptionCode.code == redemption_code) - codes = await query.gino.all() - code = codes[0] - coins = 0 - if choice is None: - return await p.close() - - choices = choice.split(',') - print(choices) - for choice in choices: - if choice[:2] == 'c0': - coins += 500 - - elif choice[:1] != 'p': - if int(choice) not in p.server.items: - return await p.close() - else: - print('Adding', int(choice)) - await p.add_inventory(p.server.items[int(choice)], notify=False) - - # if int(choice) not in p.server.items.treasure: - # return await p.close() - # else: - # await p.add_inventory(p.server.items[choice], notify=False) - - while 'c0' in choices: - choices.remove('c0') - - if code.uses is None: - await PenguinRedemptionCode.create(penguin_id=p.id, code_id=code.id) - - if coins == 0: - coins = '' - else: - await p.update(coins=p.coins + coins).apply() - - await p.send_xt('rscrt', ','.join(choices), coins) - - - -@handlers.handler(XTPacket('rsp', ext='red'), pre_login=True) -@handlers.depends_on_packet(XTPacket('rsc', ext='red')) -async def handle_redeem_puffle(p, name: str, id: int): - if name is None or id is None: - return await p.close() - - if id not in p.server.puffles: - return await p.close() - - if not 16 > len(name) >= 3: - await p.send_xt('rsp', 0) - - if len(p.puffles) >= 75: - return await p.send_error(440) - - puffle = await p.puffles.insert(puffle_id=id, name=name) - await p.add_puffle_item(p.server.puffle_items[3], quantity=5, cost=0) - await p.add_puffle_item(p.server.puffle_items[79], cost=0) - await p.add_puffle_item(p.server.puffle_items[p.server.puffles[puffle.id].favourite_toy]) - await p.add_inbox(p.server.postcards[111], details=puffle.name) - await p.send_xt('rsp', 1) - - -@handlers.handler(XTPacket('rgbq', ext='red'), pre_login=True) -@handlers.depends_on_packet(XTPacket('rjs', ext='red')) -async def handle_get_book_question(p, book: int): - book_exist = await RedemptionBook.query.where(RedemptionBook.id == book).gino.scalar() - book_redeemed = await PenguinRedemptionBook.query.where((PenguinRedemptionBook.book_id == book) & - (PenguinRedemptionBook.penguin_id == p.id)).gino.scalar() - if not book_exist: - return await p.close() - - if book_redeemed: - return await p.close() - - question = await RedemptionBookWord.query.where(RedemptionBookWord.book_id == book).order_by(db.func.random()).gino.first() - await p.send_xt('rgbq', question.question_id, question.book_id, question.page, question.line, question.word_number) - - -@handlers.handler(XTPacket('rsba', ext='red'), pre_login=True) -@handlers.depends_on_packet(XTPacket('rgbq', ext='red')) -async def handle_send_book_answer(p, book: int, question_id: int, answer: str): - book_exist = await RedemptionBook.query.where(RedemptionBook.id == book).gino.scalar() - book_redeemed = await PenguinRedemptionBook.query.where((PenguinRedemptionBook.book_id == book) & - (PenguinRedemptionBook.penguin_id == p.id)).gino.scalar() - if not book_exist: - return await p.close() - - if book_redeemed: - return await p.close() - - redemption_attempts_key = f'{p.id}.redemption_attempts' - - if await p.server.redis.exists(redemption_attempts_key): - tr = p.server.redis.multi_exec() - tr.incr(redemption_attempts_key) - tr.expire(redemption_attempts_key, p.server.config.login_failure_timer) - failure_count, _ = await tr.execute() - - if failure_count >= 5: - return await p.send_error(713) - else: - await p.server.redis.setex(redemption_attempts_key, p.server.config.login_failure_timer, 1) - - - redemption_book_answer = await RedemptionBookWord.select('answer').where((RedemptionBookWord.book_id == book) & - (RedemptionBookWord.question_id == question_id)).gino.scalar() - - if answer == redemption_book_answer: - if book == 23: - item = 14608 - await p.add_inventory(p.server.items[14608], notify=False) - - elif book == 24: - item = 13054 - await p.add_inventory(p.server.items[13054], notify=False) - - else: - if 15007 not in p.inventory: - item = 15007 - await p.add_inventory(p.server.items[15007], notify=False) - else: - item = '' - - await p.update(coins=p.coins + 1500).apply() - - await p.send_xt('rsba', item, 1500) - await PenguinRedemptionBook.create(penguin_id=p.id, book_id=book) - - else: - return await p.send_error(712) - - - - - - + redeemed_books = await PenguinRedemptionBook.query.where(PenguinRedemptionBook.penguin_id == p.id).gino.all() + await p.send_xt('rjs', ','.join(str(redeemed_book.book_id) for redeemed_book in redeemed_books), 'houdini', + int(p.is_member)) diff --git a/houdini/handlers/redemption/book.py b/houdini/handlers/redemption/book.py new file mode 100644 index 0000000..76ee32d --- /dev/null +++ b/houdini/handlers/redemption/book.py @@ -0,0 +1,71 @@ +from houdini import handlers +from houdini.data import db +from houdini.data.redemption import PenguinRedemptionBook, RedemptionBook, RedemptionBookWord +from houdini.handlers import XTPacket + + +@handlers.handler(XTPacket('rgbq', ext='red'), pre_login=True) +@handlers.depends_on_packet(XTPacket('rjs', ext='red')) +async def handle_get_book_question(p, book: int): + book_exist = await RedemptionBook.query.where(RedemptionBook.id == book).gino.scalar() + book_redeemed = await PenguinRedemptionBook.query.\ + where((PenguinRedemptionBook.book_id == book) & + (PenguinRedemptionBook.penguin_id == p.id)).gino.scalar() + if not book_exist: + return await p.close() + + if book_redeemed: + return await p.close() + + question = await RedemptionBookWord.query.where(RedemptionBookWord.book_id == book)\ + .order_by(db.func.random()).gino.first() + await p.send_xt('rgbq', question.question_id, question.book_id, question.page, question.line, question.word_number) + + +@handlers.handler(XTPacket('rsba', ext='red'), pre_login=True) +@handlers.depends_on_packet(XTPacket('rgbq', ext='red')) +async def handle_send_book_answer(p, book: int, question_id: int, answer: str): + book_exist = await RedemptionBook.query.where(RedemptionBook.id == book).gino.scalar() + book_redeemed = await PenguinRedemptionBook.query.where((PenguinRedemptionBook.book_id == book) & + (PenguinRedemptionBook.penguin_id == p.id)).gino.scalar() + if not book_exist: + return await p.close() + + if book_redeemed: + return await p.close() + + redemption_attempts_key = f'{p.id}.redemption_attempts' + + if await p.server.redis.exists(redemption_attempts_key): + tr = p.server.redis.multi_exec() + tr.incr(redemption_attempts_key) + tr.expire(redemption_attempts_key, p.server.config.login_failure_timer) + failure_count, _ = await tr.execute() + + if failure_count >= 5: + return await p.send_error(713) + else: + await p.server.redis.setex(redemption_attempts_key, p.server.config.login_failure_timer, 1) + + redemption_book_answer = await RedemptionBookWord.select('answer').\ + where((RedemptionBookWord.book_id == book) & + (RedemptionBookWord.question_id == question_id)).gino.scalar() + + if answer == redemption_book_answer: + item = None + if book == 23: + item = 14608 + await p.add_inventory(p.server.items[item], notify=False) + elif book == 24: + item = 13054 + await p.add_inventory(p.server.items[item], notify=False) + elif 15007 not in p.inventory: + item = 15007 + await p.add_inventory(p.server.items[item], notify=False) + + await p.update(coins=p.coins + 1500).apply() + + await p.send_xt('rsba', item or '', 1500) + await PenguinRedemptionBook.create(penguin_id=p.id, book_id=book) + else: + return await p.send_error(712) diff --git a/houdini/handlers/redemption/code.py b/houdini/handlers/redemption/code.py new file mode 100644 index 0000000..655e415 --- /dev/null +++ b/houdini/handlers/redemption/code.py @@ -0,0 +1,267 @@ +import random +from datetime import datetime + +from houdini import handlers +from houdini.constants import ClientType +from houdini.data import db +from houdini.data.igloo import Furniture, Igloo +from houdini.data.item import Item +from houdini.data.redemption import PenguinRedemptionCode, RedemptionAwardCard, \ + RedemptionAwardFlooring, RedemptionAwardFurniture, RedemptionAwardIgloo, RedemptionAwardItem, \ + RedemptionAwardLocation, RedemptionAwardPuffle, RedemptionAwardPuffleItem, RedemptionCode +from houdini.handlers import XTPacket +from houdini.handlers.games.ninja.card import ninja_rank_up + + +TreasureUnlockCount = 3 +NinjaRankUpChoice = 1 +FireNinjaRankUpChoice = 3 +WaterNinjaRankUpChoice = 4 +SnowNinjaRankUpChoice = 5 + + +@handlers.handler(XTPacket('rsc', ext='red'), pre_login=True, client=ClientType.Legacy) +@handlers.depends_on_packet(XTPacket('rjs', ext='red')) +async def handle_code_legacy(p, redemption_code: str): + query = RedemptionCode.distinct(RedemptionCode.id)\ + .load(cards=RedemptionAwardCard.distinct(RedemptionAwardCard.card_id), + items=RedemptionAwardItem.distinct(RedemptionAwardItem.item_id))\ + .query.where(RedemptionCode.code == redemption_code) + codes = await query.gino.all() + if not codes: + return await p.send_error(720) + + code = codes[0] + awards = [] + + if code.uses is not None: + redeemed_count = await db.select([db.func.count(PenguinRedemptionCode.code_id)]).where( + PenguinRedemptionCode.code_id == code.id).gino.scalar() + + if redeemed_count >= code.uses: + return await p.send_error(721) + + penguin_redeemed = await PenguinRedemptionCode.query.\ + where((PenguinRedemptionCode.code_id == code.id) & + (PenguinRedemptionCode.penguin_id == p.id)).gino.scalar() + if penguin_redeemed: + return await p.send_error(721) + + if code.expires is not None and code.expires < datetime.now(): + return await p.send_error(726) + + if code.type == 'GOLDEN': + p.server.cache.set(f'{p.id}.{code.code}.golden_code', code) + return await p.send_xt('rsc', 'GOLDEN', p.ninja_rank, p.fire_ninja_rank, p.water_ninja_rank, + int(p.fire_ninja_rank > 0), int(p.water_ninja_rank > 0)) + + if code.type == 'CARD': + for award in code.cards: + awards.append(str(award.card_id)) + await p.add_card(p.server.cards[award.card_id]) + else: + if code.items: + for award in code.items: + awards.append(str(award.item_id)) + await p.add_inventory(p.server.items[award.item_id], notify=False) + + await PenguinRedemptionCode.create(penguin_id=p.id, code_id=code.id) + await p.update(coins=p.coins + code.coins).apply() + return await p.send_xt('rsc', code.type, ','.join(map(str, awards)), code.coins) + + +@handlers.handler(XTPacket('rsc', ext='red'), pre_login=True, client=ClientType.Vanilla) +@handlers.depends_on_packet(XTPacket('rjs', ext='red')) +async def handle_code_vanilla(p, redemption_code: str): + query = RedemptionCode.distinct(RedemptionCode.id) \ + .load(cards=RedemptionAwardCard.distinct(RedemptionAwardCard.card_id), + items=RedemptionAwardItem.distinct(RedemptionAwardItem.item_id), + flooring=RedemptionAwardFlooring.distinct(RedemptionAwardFlooring.flooring_id), + furniture=RedemptionAwardFurniture.distinct(RedemptionAwardFurniture.furniture_id), + igloos=RedemptionAwardIgloo.distinct(RedemptionAwardIgloo.igloo_id), + locations=RedemptionAwardLocation.distinct(RedemptionAwardLocation.location_id), + puffles=RedemptionAwardPuffle.distinct(RedemptionAwardPuffle.puffle_id), + puffle_items=RedemptionAwardPuffleItem.distinct(RedemptionAwardPuffleItem.puffle_item_id))\ + .query.where(RedemptionCode.code == redemption_code) + codes = await query.gino.all() + if not codes: + return await p.send_error(720) + + code = codes[0] + awards = [] + + if code.uses is not None: + redeemed_count = await db.select([db.func.count(PenguinRedemptionCode.code_id)]).where( + PenguinRedemptionCode.code_id == code.id).gino.scalar() + + if redeemed_count >= code.uses: + return await p.send_error(721) + + penguin_redeemed = await PenguinRedemptionCode.query.where((PenguinRedemptionCode.code_id == code.id) & + (PenguinRedemptionCode.penguin_id == p.id)).gino.scalar() + if penguin_redeemed: + return await p.send_error(721) + + if code.expires is not None and code.expires < datetime.now(): + return await p.send_error(726) + + if code.type == 'CATALOG': + num_redeemed_codes = await PenguinRedemptionCode.join(RedemptionCode).count().where( + (PenguinRedemptionCode.penguin_id == p.id) & (RedemptionCode.type == 'CATALOG')).gino.scalar() + owned_ids = ','.join((str(item.id) for item in p.server.items.treasure if item.id in p.inventory)) + + p.server.cache.set(f'{p.id}.{code.code}.treasure_code', code) + return await p.send_xt('rsc', 'treasurebook', TreasureUnlockCount, owned_ids, num_redeemed_codes) + + if code.type == 'GOLDEN': + p.server.cache.set(f'{p.id}.{code.code}.golden_code', code) + return await p.send_xt('rsc', 'GOLDEN', p.ninja_rank, p.fire_ninja_rank, p.water_ninja_rank, 0, + int(p.fire_ninja_rank > 0), int(p.water_ninja_rank > 0), 0) + + if code.type == 'INNOCENT': + innocent_redeemed_items = {item for item in p.server.items.innocent if item.id in p.inventory} + innocent_redeemed_furniture = {item for item in p.server.furniture.innocent if item.id in p.furniture} + innocent_redeemed = innocent_redeemed_items.union(innocent_redeemed_furniture) + innocent_items = set(p.server.items.innocent + p.server.furniture.innocent) + + innocent_remaining = innocent_items - innocent_redeemed + + choices = random.sample(innocent_remaining, min(len(innocent_remaining), 3)) + if len(innocent_redeemed) + 3 == len(innocent_items): + choices.append(p.server.igloos[53]) + for item in choices: + if type(item) is Item: + awards.append(str(item.id)) + await p.add_inventory(item, notify=False) + elif type(item) is Igloo: + awards.append(f'g{item.id}') + await p.add_igloo(item, notify=False) + elif type(item) is Furniture: + awards.append(f'f{item.id}') + await p.add_furniture(item, notify=False) + + await PenguinRedemptionCode.create(penguin_id=p.id, code_id=code[0].id) + + return await p.send_xt('rsc', 'INNOCENT', ','.join(map(str, awards)), + len(innocent_redeemed) + len(choices), + len(innocent_items)) + + if code.type == 'CARD': + for award in code.cards: + awards.append(str(award.card_id)) + await p.add_card(p.server.cards[award.card_id]) + else: + if code.items: + for award in code.items: + awards.append(str(award.item_id)) + await p.add_inventory(p.server.items[award.item_id], notify=False) + if code.furniture: + for award in code.furniture: + awards.append(f'f{award.furniture_id}') + await p.add_furniture(p.server.furniture[award.furniture_id], notify=False) + if code.igloos: + for award in code.igloos: + awards.append(f'g{award.igloo_id}') + await p.add_igloo(p.server.igloos[award.igloo_id], notify=False) + if code.locations: + for award in code.locations: + awards.append(f'loc{award.location_id}') + await p.add_location(p.server.locations[award.location_id], notify=False) + if code.flooring: + for award in code.flooring: + awards.append(f'flr{award.flooring_id}') + await p.add_flooring(p.server.flooring[award.flooring_id], notify=False) + if code.puffles: + for award in code.puffles: + awards.append(f'p{award.puffle_id}') + if code.puffle_items: + for award in code.puffle_items: + awards.append(f'pi{award.puffle_item_id}') + await p.add_puffle_item(p.server.puffle_items[award.puffle_item_id], notify=False) + + await PenguinRedemptionCode.create(penguin_id=p.id, code_id=code.id) + await p.update(coins=p.coins + code.coins).apply() + return await p.send_xt('rsc', code.type, ','.join(map(str, awards)), code.coins or '') + + +@handlers.handler(XTPacket('rsgc', ext='red'), pre_login=True) +@handlers.depends_on_packet(XTPacket('rsc', ext='red')) +async def handle_golden_choice(p, redemption_code: str, choice: int): + code_key = f'{p.id}.{redemption_code}.golden_code' + code = p.server.cache.get(code_key) + p.server.cache.delete(code_key) + if not code: + return await p.close() + + if len(code.cards) < 6: + return await p.close() + + cards = list(code.cards) + card_ids = [str(card.card_id) for card in cards] + + if choice == NinjaRankUpChoice: + await ninja_rank_up(p) + cards = cards[:4] + await p.send_xt('rsgc', ','.join(card_ids[:4]) + '|' + str(p.ninja_rank)) + else: + cards = cards[:4] + cards[-2:] + await p.send_xt('rsgc', ','.join(card_ids[:4]) + '|' + ','.join(card_ids[-2:])) + + for card in cards: + await p.add_card(p.server.cards[card.card_id]) + + if code.uses is None: + await PenguinRedemptionCode.create(penguin_id=p.id, code_id=code.id) + + +@handlers.handler(XTPacket('rscrt', ext='red'), pre_login=True) +@handlers.depends_on_packet(XTPacket('rsc', ext='red')) +async def handle_send_cart(p, redemption_code: str, choice: str): + code_key = f'{p.id}.{redemption_code}.treasure_code' + code = p.server.cache.get(code_key) + p.server.cache.delete(code_key) + + if code is None: + return await p.close() + + coins = 0 + awards = [] + choices = choice.split(',') + + if len(choices) > TreasureUnlockCount: + return await p.close() + + for choice in choices: + if choice.startswith('c'): + coins += 500 + elif choice.startswith('p'): + awards.append(choice) + elif choice.isdigit(): + awards.append(choice) + await p.add_inventory(p.server.items[int(choice)], notify=False) + + if code.uses is None: + await PenguinRedemptionCode.create(penguin_id=p.id, code_id=code.id) + + await p.update(coins=p.coins + coins).apply() + await p.send_xt('rscrt', ','.join(awards), coins or '') + + +@handlers.handler(XTPacket('rsp', ext='red'), pre_login=True) +@handlers.depends_on_packet(XTPacket('rsc', ext='red')) +async def handle_redeem_puffle(p, name: str, puffle_type: int): + if puffle_type not in p.server.puffles: + return await p.close() + + if not 16 > len(name) >= 3: + await p.send_xt('rsp', 0) + + if len(p.puffles) >= 75: + return await p.send_error(440) + + puffle = await p.puffles.insert(puffle_id=puffle_type, name=name) + await p.add_puffle_item(p.server.puffle_items[3], quantity=5, cost=0) + await p.add_puffle_item(p.server.puffle_items[79], cost=0) + await p.add_puffle_item(p.server.puffle_items[p.server.puffles[puffle.id].favourite_toy]) + await p.add_inbox(p.server.postcards[111], details=puffle.name) + await p.send_xt('rsp', 1)