async def mods(p: Player, c: Messageable, msg: Sequence[str]) -> str: if isinstance(c, Channel) or c.id != 1: return 'This command can only be used in DM with Aika.' if not p.last_np: return 'Please /np a map first!' msg = ''.join(msg).replace(' ', '') if msg[0] == '+': # remove + msg = msg[1:] mods = Mods.from_str(msg) if mods not in p.last_np.pp_cache: # cach await p.last_np.cache_pp(mods) # Since this is a DM to the bot, we should # send back a list of general PP values. # TODO: !acc and !mods in commands to # modify these values :P _msg = [p.last_np.embed] if mods: _msg.append(f'{mods!r}') msg = f"{' '.join(_msg)}: " + ' | '.join( f'{acc}%: {pp:.2f}pp' for acc, pp in zip( (90, 95, 98, 99, 100), p.last_np.pp_cache[mods] )) return msg
async def _with(p: Player, c: Messageable, msg: Sequence[str]) -> str: """Specify custom accuracy & mod combinations with `/np`.""" if isinstance(c, Channel) or c.id != 1: return 'This command can only be used in DM with Aika.' if not p.last_np: return 'Please /np a map first!' # +?<mods> <acc>%? if 1 < len(msg) > 2: return 'Invalid syntax: !with <mods/acc> ...' mods = acc = None for param in (p.strip('+%') for p in msg): if cmyui._isdecimal(param, _float=True): acc = float(param) elif ~len(param) & 1: # len(param) % 2 == 0 mods = Mods.from_str(param) else: return 'Invalid syntax: !with <mods/acc> ...' _msg = [p.last_np.embed] if not mods: mods = Mods.NOMOD _msg.append(repr(mods)) if acc: # they're requesting pp for specified acc value. async with Owoppai(p.last_np.id, acc=acc, mods=mods) as owo: await owo.calc() pp_values = [(owo.acc, owo.pp)] else: # they're requesting pp for general accuracy values. if mods not in p.last_np.pp_cache: # cache await p.last_np.cache_pp(mods) pp_values = zip( (90, 95, 98, 99, 100), p.last_np.pp_cache[mods] ) pp_msg = ' | '.join(f'{acc:.2f}%: {pp:.2f}pp' for acc, pp in pp_values) return f"{' '.join(_msg)}: {pp_msg}"
async def mp_mods(p: 'Player', m: 'Match', msg: Sequence[str]) -> str: """Set the current match's mods, from string form.""" if len(msg) != 1 or not ~len(msg[0]) & 1: return 'Invalid syntax: !mp mods <mods>' mods = Mods.from_str(msg[0]) if m.freemods: if p is m.host: # allow host to set speed-changing mods. m.mods = mods & Mods.SPEED_CHANGING # set slot mods m.get_slot(p).mods = mods & ~Mods.SPEED_CHANGING else: # not freemods, set match mods. m.mods = mods m.enqueue_state() return 'Match mods updated.'
async def mp_mods(p: Player, m: Match, msg: Sequence[str]) -> str: """Set `m`'s mods, from string form.""" if len(msg) != 1 or not ~len(msg[0]) & 1: # len(msg[0]) % 2 == 0 return 'Invalid syntax: !mp mods <mods>' mods = Mods.from_str(msg[0]) if m.freemods: if p.id == m.host.id: # allow host to set speed-changing mods. m.mods = mods & Mods.SPEED_CHANGING # set slot mods m.get_slot(p).mods = mods & ~Mods.SPEED_CHANGING else: # not freemods, set match mods. m.mods = mods m.enqueue(packets.updateMatch(m)) return 'Match mods updated.'
async def _with(p: 'Player', c: Messageable, msg: Sequence[str]) -> str: """Specify custom accuracy & mod combinations with `/np`.""" if c is not glob.bot: return 'This command can only be used in DM with Aika.' if not p.last_np: return 'Please /np a map first!' # +?<mods> <acc>%? if 1 < len(msg) > 2: return 'Invalid syntax: !with <mods/acc> ...' mods = acc = None for param in (p.strip('+%') for p in msg): if cmyui._isdecimal(param, _float=True): if not 0 <= (acc := float(param)) <= 100: return 'Invalid accuracy.' elif ~len(param) & 1: # len(param) % 2 == 0 mods = Mods.from_str(param)
@mp_commands.add(Privileges.Normal) async def mp_ban(p: 'Player', m: 'Match', msg: Sequence[str]) -> str: """Ban a pick in the currently loaded mappool.""" if len(msg) != 1: return 'Invalid syntax: !mp ban <pick>' if not m.pool: return 'No pool currently selected!' mods_slot = msg[0] # separate mods & slot if not (rgx := regexes.mappool_pick.fullmatch(mods_slot)): return 'Invalid pick syntax; correct example: "HD2".' mods = Mods.from_str(rgx[1]) slot = int(rgx[2]) if (mods, slot) not in m.pool.maps: return f'Found no {mods_slot} pick in the pool.' if (mods, slot) in m.bans: return 'That pick is already banned!' m.bans.add((mods, slot)) return f'{mods_slot} banned.' @mp_commands.add(Privileges.Normal) async def mp_unban(p: 'Player', m: 'Match', msg: Sequence[str]) -> str: """Unban a pick in the currently loaded mappool."""