async def on_message(self, ctx): if ctx.author.bot: return if all([ ctx.channel.id != cs.Zyanken_room, ctx.channel.id != cs.Test_room ]): return for hand in ["グー", "チョキ", "パー"]: # グー,チョキ,パーの順に文字が含まれているか検索 if hand not in jaconv.hira2kata(jaconv.h2z(ctx.content)): continue # img, hand, msg, emoji1, emoji2 = zf.honda_to_zyanken(hand, ctx.author.id) img, hand, msg, emoji1, emoji2 = zf.honda_to_zyanken_breaktime( hand, ctx.author.id) if str(ctx.author.id) not in zf.No_reply: await ctx.add_reaction(emoji1) await ctx.add_reaction(emoji2) await ctx.channel.send(f"{ctx.author.mention} {hand}\n{msg}", file=discord.File(img), delete_after=5) if cs.Zyanken not in [roles.id for roles in ctx.author.roles]: guild = self.bot.get_guild(ctx.guild.id) await guild.get_member(ctx.author.id ).add_roles(get_role(guild, cs.Zyanken)) """ if emoji2 == "🎉" and len(zf.Former_winner) <= 5: guild = self.bot.get_guild(ctx.guild.id) await guild.get_member(ctx.author.id).add_roles(get_role(guild, cs.Winner)) if ctx.author.id not in zf.Former_winner: zf.Former_winner.append(ctx.author.id) """ break
async def update_roles(guild, winner, loser): role_W, role_L = get_role(guild, cs.Winner), get_role(guild, cs.Loser) if winner != zf.Former_winner: for i in range(len(winner)): if winner[i] not in zf.Former_winner: await guild.get_member(winner[i]).add_roles(role_W) for i in range(len(zf.Former_winner)): if zf.Former_winner[i] not in winner: await guild.get_member(zf.Former_winner[i] ).remove_roles(role_W) zf.Former_winner = winner if loser != zf.Former_loser: for i in range(len(loser)): if loser[i] not in zf.Former_loser: await guild.get_member(loser[i]).add_roles(role_L) for i in range(len(zf.Former_loser)): if zf.Former_loser[i] not in loser: await guild.get_member(zf.Former_loser[i]).remove_roles(role_L) zf.Former_loser = loser
async def run(bot, ctx, role_name): guild = bot.get_guild(ctx.guild.id) if role_name in ["participant", "p"]: rm_role = get_role(guild, cs.Participant) cs.Joiner = [] elif role_name in ["winner", "w"]: rm_role = get_role(guild, cs.Winner) elif role_name in ["loser", "l"]: rm_role = get_role(guild, cs.Loser) elif role_name in ["zyanken", "z"]: rm_role = get_role(guild, cs.Zyanken) elif role_name in ["uno", "u"]: rm_role = get_role(guild, cs.UNO) else: return await ctx.send( f"{ctx.author.mention} RoleNameを入力してください\n" ">>> **_ReSet RoleName**\nRoleName = Participant / Winner / Loser / Zyanken / UNO" ) for member in rm_role.members: await member.remove_roles(rm_role) await ctx.send(f"ロール {rm_role.mention} をリセットしました")
async def uno_end(guild, image_flag=False, new_flag=False): global ALL_DATA, ALL_PLAYER, INITIAL_NUM, JOINING_WAIT if image_flag: os.remove(mi.AREA_COPY_PASS) # 新規プレイヤーにはUNOロール付与 if new_flag: for player in ALL_PLAYER: if cs.UNO not in [ roles.id for roles in guild.get_member(player).roles ]: await guild.get_member(player).add_roles( get_role(guild, cs.UNO)) uf.UNO_start = False ALL_DATA, ALL_PLAYER, INITIAL_NUM, JOINING_WAIT = [], [], 7, True
async def run_remove(bot, ctx, name): if name is None: return await ctx.send("ユーザー名を入力してください", delete_after=10) guild = bot.get_guild(ctx.guild.id) for member in get_role(guild, cs.Visitor).members: if name.lower() == member.display_name.lower(): if str(member.id) in Joiner: Joiner.remove(member.id) return await ctx.send(f"{member.display_name}を削除しました", delete_after=10) else: return await ctx.send(f"{member.display_name}は参加していません", delete_after=5) await ctx.send("ユーザーが見つかりませんでした", delete_after=5)
async def run_stats(bot, ctx, name): # じゃんけん会場のみ反応(モデレーター以外) if ctx.channel.id != cs.Zyanken_room and not role_check_mode(ctx): return guild = bot.get_guild(ctx.guild.id) if name is None: name = guild.get_member(ctx.author.id).display_name elif len(re.sub('[^0-9]', "", name)) == 18: try: name = guild.get_member(int(re.sub('[^0-9]', "", name))).display_name except AttributeError: pass data, user, id = None, None, None for member in get_role(guild, cs.Zyanken).members: if name.lower() == member.display_name.lower(): try: data = zf.stats_output(member.id) user, id = member.display_name, member.id except KeyError: pass break elif name == "ケイスケホンダ": data = zf.stats_output(cs.Honda) user, id = name, cs.Honda break if data is None and id is None: return await ctx.send(f"{ctx.author.mention} データが記録されていません", delete_after=10) embed = discord.Embed(title=user, color=0xFFFF00) embed.set_author(name='Stats', icon_url=bot.get_user(id).avatar_url) embed.set_thumbnail(url=data[6]) embed.add_field( name="勝率", value=f"{data[2]:.02f}% ({data[0] + data[1]}戦 {data[0]}勝{data[1]}敗)", inline=False) embed.add_field(name="グー勝ち", value=f"{data[3][0]}回") embed.add_field(name="チョキ勝ち", value=f"{data[3][1]}回") embed.add_field(name="パー勝ち", value=f"{data[3][2]}回") embed.add_field(name="グー負け", value=f"{data[4][0]}回") embed.add_field(name="チョキ負け", value=f"{data[4][1]}回") embed.add_field(name="パー負け", value=f"{data[4][2]}回") embed.add_field(name="連勝数", value=f"最大{data[5][1]}連勝 (現在{data[5][0]}連勝中)") await ctx.send(embed=embed)
async def on_message(self, ctx): if ctx.channel.id == cs.Gate and not ctx.author.bot: time = datetime.now(timezone('UTC')).astimezone( timezone('Asia/Tokyo')) password = f"_join {time.strftime('%Y/%m/%d')}" if str(ctx.author.id) in REJECT_ID: await ctx.delete() # await ctx.channel.send(f'{ctx.author.mention} コマンド実行権限がありません', delete_after=5.0) elif ctx.content == password: await ctx.delete() await ctx.author.add_roles( get_role(self.bot.get_guild(ctx.guild.id), cs.Visitor)) await ctx.channel.send( f"{ctx.author.mention} 参加しました ({time.strftime('%Y/%m/%d %H:%M')})" ) else: await ctx.delete(delay=5.0) await ctx.channel.send(f'{ctx.author.mention} コマンドが違います', delete_after=5.0)
async def run_noreplycancel(bot, ctx, name): if ctx.channel.id != cs.Zyanken_room and not role_check_mode(ctx): return guild = bot.get_guild(ctx.guild.id) if name is None: name = guild.get_member(ctx.author.id).display_name for member in get_role(guild, cs.Zyanken).members: if name.lower() == member.display_name.lower(): if str(member.id) in zf.No_reply: zf.No_reply.remove(str(member.id)) await ctx.send( f"{guild.get_member(member.id).mention} 返信を有効にしました", delete_after=10) else: await ctx.send(f"{ctx.author.mention} 既に返信は有効になっています", delete_after=10) return await ctx.send(f"{ctx.author.mention} ユーザーが見つかりませんでした", delete_after=10)
async def run_record(bot, guild, ctx, name): # UNO会場のみ反応(モデレーター以外) if ctx.channel.id != cs.UNO_room and not role_check_mode(ctx): return if name is None: name = guild.get_member(ctx.author.id).display_name elif len(re.sub('[^0-9]', "", name)) == 18: try: name = guild.get_member(int(re.sub('[^0-9]', "", name))).display_name except AttributeError: pass msg = await ctx.send(f"{name}のデータを検索中...") data, player, url, rank, user, id = None, None, None, None, None, None for member in get_role(guild, cs.Visitor).members: if name.lower() == member.display_name.lower(): data, player, url, rank = ur.record_output(member.id) user, id = member.display_name, member.id if data is not None: break if data is not None: embed = discord.Embed(title=user, color=0xFF3333) embed.set_author(name='UNO Records', icon_url=bot.get_user(id).avatar_url) embed.set_thumbnail(url=url) embed.add_field(name="順位 (総得点)", value=f"**{data[0]}** /{player}位 ({data[3]:+}点)", inline=False) embed.add_field(name="順位 (勝利人数)", value=f"**{rank}** /{player}位 ({data[4]:+}人)", inline=False) embed.add_field(name="優勝率", value=f"{data[5]} ({data[7]}回/{data[6]}戦)", inline=False) embed.add_field(name="最高獲得点", value=f"{data[9]}点") embed.add_field(name="最低減少点", value=f"{data[10]}点") embed.add_field(name="直近5戦", value=f"{data[11]}点") await ctx.send(embed=embed) else: await ctx.send(f"{ctx.author.mention} データが記録されていません") await msg.delete()
async def run_pickup(bot, ctx, num): global Joiner if ctx.channel.id != cs.Recruit: return elif len(Joiner) < num or num <= 0: return await ctx.send("正しい人数を入力してください", delete_after=10) guild = bot.get_guild(ctx.guild.id) role_P = get_role(guild, cs.Participant) pick_num = sorted(random.sample(list(range(len(Joiner))), num)) # 抽選を行う stc = [ f"{i + 1}. {guild.get_member(Joiner[pick_num[i]]).display_name}\n" for i in range(len(pick_num)) ] for i in pick_num: await guild.get_member(Joiner[i]).add_roles(role_P) await ctx.send( f"参加者リスト 抽選結果\n```{''.join(stc)}```" f"リストのユーザーにロール {role_P.mention} を付与しました\n配信用ボイスチャンネルに接続出来るようになります") Joiner = []
async def run_quizstart(bot, ctx, num): if num <= 0: await ctx.send("問題数を入力してください") elif not 1 <= num <= len(Question): await ctx.send("登録されている問題数に対して入力が間違っています") return guild = bot.get_guild(ctx.guild.id) result, point, mag = {}, [4, 2, 1], [] mag = [ 3 if i + 1 == num else 1 if (i + 1) % 5 != 0 else 2 for i in range(num) ] await ctx.send("クイズを開始します") for i in range(num): await asyncio.sleep(5) await ctx.send( f"問題**{i + 1}**/{num} **(1位 +{point[0] * mag[i]}点, " f"2位 +{point[1] * mag[i]}点, 3位 +{point[2] * mag[i]}点)** (制限時間1分)") await asyncio.sleep(3) await ctx.send(f"{Question[f'Q{i + 1}']}") j, flag, winner, start = 1, False, [], datetime.now() while 0 <= j <= 3: reply = await bot.wait_for('message') elap = (start - datetime.now()).seconds if reply.content == Answer[f'A{i + 1}']: if reply.author.id not in winner: winner.append(reply.author.id) j, flag = j + 1, True elif (reply.content.lower() == "!skip" and role_check_admin(reply)) or elap > 60: await ctx.send( f"問題{i + 1}はスキップされました (正解 : {Answer[f'A{i + 1}']})") j, flag = -1, False elif reply.content.lower() == "!cancel" and role_check_admin( reply): await ctx.send(f"クイズを中断しました") return if flag: await ctx.send( f"正解者が出揃ったので問題{i + 1}を終了します (正解 : {Answer[f'A{i + 1}']})") if len(winner) != 0: stc = [ f"{k + 1}位 : {bot.get_user(winner[k]).display_name} (+{point[k] * mag[i]}点)\n" for k in range(len(winner)) ] await ctx.send(f"**```問題{i + 1} 結果\n{''.join(stc)}```**") for k in range(len(winner)): if winner[k] not in result: result[winner[k]] = point[k] * mag[i] else: new_pts = result[winner[k]] + point[k] * mag[i] result[winner[k]] = new_pts all_user, all_result = list(result.keys()), sorted(list(result.values()), reverse=True) ranker = [] embed = discord.Embed(color=0xFF0000) embed.set_author(name='Ranking', icon_url='https://i.imgur.com/F2oH0Bu.png') embed.set_thumbnail(url='https://i.imgur.com/jrl3EDv.png') i = 0 while i < (len(all_user) if len(all_user) < 5 else 5): k = 0 for j in range(len(all_user)): if result[all_user[j]] == all_result[i]: name = guild.get_member(all_user[j]).display_name embed.add_field(name=f"{i + 1}位", value=f"{name} ({all_result[i]}pts)") ranker.append(all_user[j]) k += 1 i += k role_A, role_W = get_role(guild, cs.Administrator), get_role(guild, cs.Winner) await ctx.send(embed=embed) await guild.get_member(ranker[0]).add_roles(role_W) await ctx.send( f"クイズを終了しました\n{role_W.mention} → {guild.get_member(ranker[0]).mention}" ) await ctx.send( f"{role_A.mention} `!out`で結果を {bot.get_channel(cs.Result).mention} に出力" ) while True: reply = await bot.wait_for('message', check=role_check_mode) if reply.content == "!out": await bot.get_channel(cs.Result).send(embed=embed) break
async def run_uno(bot, ctx, type): # ターンパス時の処理 async def turn_pass(pass_stc=""): if not get_flag and penalty == 0: await ctx.send( f"{bot.get_user(ALL_DATA[i][0]).mention} {pass_stc}山札から1枚引いてパスします", delete_after=5) await send_card(bot, i, 1, True) else: await ctx.send( f"{bot.get_user(ALL_DATA[i][0]).mention} {pass_stc}パスします", delete_after=5) # 入力を受け付けない条件一覧 def ng_check(ctx_wait): return all([ ctx.channel.id == ctx_wait.channel.id, not ctx_wait.author.bot, ctx_wait.content != "" ]) # ng_check + 指定したユーザーしか入力不可 def user_check(ctx_wait): return ng_check(ctx_wait) and ctx_wait.author.id in ALL_PLAYER # 既にUNO実行中の場合はゲームを開始しない if uf.UNO_start: return await ctx.send(f"{ctx.author.mention} 現在プレイ中なので開始出来ません", delete_after=5) global ALL_DATA, ALL_PLAYER, INITIAL_NUM, TURN, DECLARATION_WAIT, JOINING_WAIT normal_flag, special_flag, mode_str = False, False, "" uf.Card = uf.Card_Normal mi.AREA_PASS, mi.BG_PASS = mi.AREA_PASS_temp, mi.BG_PASS_temp if type.lower() in ["n", "normal"]: normal_flag, mode_str = True, "ノーマルモードで" elif type.lower() in ["s", "special"]: special_flag, mode_str = True, "特殊ルールモードで" mi.AREA_PASS, mi.BG_PASS = mi.AREA_SP_PASS, mi.BG_SP_PASS elif type.lower() in ["f", "free"]: special_flag, mode_str = True, "フリープレイモードで" mi.AREA_PASS, mi.BG_PASS = mi.AREA_FREE_PASS, mi.BG_FREE_PASS else: return await ctx.send(f"{ctx.author.mention} そんなモードはありません", delete_after=5) if all([ctx.channel.id != cs.UNO_room, ctx.channel.id != cs.Test_room ]) and not special_flag: return await ctx.send(f"{ctx.author.mention} ここでは実行できません", delete_after=5) uf.UNO_start = True guild = bot.get_guild(ctx.guild.id) await ctx.send( f"{mode_str}UNOを開始します\n※必ずダイレクトメッセージの送信を許可にしてください\n" "参加する方は `!Join` と入力してください ( `!Drop` で参加取り消し, `!End` で締め切り, `!Cancel` で中止)" ) ALL_PLAYER, start = [ctx.author.id], datetime.now() while True: try: reply = await bot.wait_for('message', check=ng_check, timeout=3600 - (datetime.now() - start).seconds) input = jaconv.z2h(reply.content, ascii=True).lower() except asyncio.exceptions.TimeoutError: await ctx.send(f"締め切らずに一定時間経過したので、自動的にUNOを終了しました\n") if normal_flag: await ctx.send(f"{ctx.author.mention} 放置ペナルティーとして-100点が与えられました" ) drop_name = guild.get_member(ctx.author.id).display_name ur.add_penalty(ctx.author.id, drop_name, []) await uno_end(guild, False, False) return if input in ["!j", "!join"]: if reply.author.id not in ALL_PLAYER: ALL_PLAYER.append(reply.author.id) await ctx.send(f"{reply.author.mention} 参加しました", delete_after=5) else: await ctx.send(f"{reply.author.mention} 既に参加済みです", delete_after=5) elif input in ["!d", "!drop"] and reply.author.id in ALL_PLAYER: if reply.author.id == ctx.author.id: await ctx.send(f"{reply.author.mention} 開始者は参加を取り消せません", delete_after=5) elif len(ALL_PLAYER) >= 2: ALL_PLAYER.remove(reply.author.id) await ctx.send(f"{reply.author.mention} 参加を取り消しました", delete_after=5) elif input in ["!l", "!list"]: stc = [ f"{i + 1}. {guild.get_member(ALL_PLAYER[i]).display_name}\n" for i in range(len(ALL_PLAYER)) ] await ctx.send(f"```現在の参加者リスト\n{''.join(stc)}```", delete_after=15) elif input in ["!e", "!end"] and ALL_PLAYER: if ctx.author.id == reply.author.id or role_check_mode(ctx): if len(ALL_PLAYER) >= 2 or role_check_admin( ctx) or special_flag: break else: await ctx.send(f"{reply.author.mention} 2人以上でないと開始出来ません", delete_after=5) else: await ctx.send(f"{reply.author.mention} 開始者以外は締め切れません", delete_after=5) elif reply.content == "!cancel" and reply.author.id in ALL_PLAYER: if ctx.author.id == reply.author.id or role_check_mode(ctx): await uno_end(guild, False, False) await ctx.send("中止しました") return else: await ctx.send(f"{reply.author.mention} 開始を宣言した人以外は実行できません", delete_after=5) stc = [ f"{i + 1}. {guild.get_member(ALL_PLAYER[i]).display_name}\n" for i in range(len(ALL_PLAYER)) ] await ctx.send(f"締め切りました```プレイヤーリスト\n\n{''.join(stc)}```") if not normal_flag: await ctx.send( f"{all_mention(guild)}\n初期手札の枚数を多数決で決定します\n各自希望する枚数を入力してください (制限時間30秒)" ) want_nums, ok_player, ask_start = [], [], datetime.now() while True: try: reply = await bot.wait_for( 'message', check=ng_check, timeout=30 - (datetime.now() - ask_start).seconds) input = jaconv.z2h(reply.content, digit=True) except asyncio.exceptions.TimeoutError: break if reply.author.id not in ALL_PLAYER: continue elif not input.isdecimal(): await ctx.send(f"{reply.author.mention} 数字のみで入力してください", delete_after=5) elif 2 <= int(input) <= 100: if reply.author.id not in ok_player: want_nums.append(int(input)) ok_player.append(reply.author.id) else: await ctx.send(f"{reply.author.mention} 2~100枚以内で指定してください", delete_after=5) if len(ok_player) == len(ALL_PLAYER): break try: INITIAL_NUM = collections.Counter(want_nums).most_common()[0][0] except IndexError: pass await ctx.send(f"初期手札を{INITIAL_NUM}枚に設定しました") await ctx.send( f"カードの確率設定を変更できます\n" f"テンプレをコピーした後、[]内の数字を変更して送信してください\n変更しない場合は `!No` と入力してください") await ctx.send(f"テンプレート↓\n{uf.Card_Template}") while True: reply = await bot.wait_for('message', check=ng_check) input = jaconv.z2h(reply.content, ascii=True, digit=True) if reply.author.id not in ALL_PLAYER: continue elif input.lower() == "!no": await ctx.send("カードの確率はデフォルトのままで進行します") break elif input.count("[") == 0: continue error = uf.template_check(input) if error is None: await ctx.send("カードの確率設定を変更しました") break else: await ctx.send(f"{reply.author.mention} 入力エラー\n{error}", delete_after=10) await ctx.send(f"カードの色の数を1~4色に変更できます\n数字を入力してください (変更しない場合は4)") while True: reply = await bot.wait_for('message', check=ng_check) input = jaconv.z2h(reply.content, digit=True) if reply.author.id not in ALL_PLAYER: continue elif not input.isdecimal(): await ctx.send(f"{reply.author.mention} 数字のみで入力してください", delete_after=5) continue elif not 1 <= int(input) <= 4: await ctx.send(f"{reply.author.mention} 1~4色以内で指定してください", delete_after=5) continue for i in range(4 - int(input)): uf.Card = uf.remove_color_card(uf.Color[-1 - i], uf.Card) await ctx.send(f"カードの色の数を{input}色に設定しました") break else: await ctx.send(f"ノーマルモードで開始したため、初期手札{INITIAL_NUM}枚、カードの確率はデフォルトとなります") random.shuffle(ALL_PLAYER) # ALL_DATA == [id, 手札リスト, DM変数, [UNOフラグ, フラグが立った時間], パスペナルティー回数] × 人数分 ALL_DATA = [[id, [], None, [False, None], 0] for id in ALL_PLAYER] msg = await ctx.send("カード配り中...") for i in range(len(ALL_PLAYER)): try: await send_card(bot, i, INITIAL_NUM, False) except Forbidden: await ctx.send( f"{bot.get_user(ALL_PLAYER[i]).mention} DMの送信を許可していないのでカードが配れません\n" f"許可してから途中参加してください") ALL_PLAYER.pop(i) ALL_DATA.pop(i) await msg.delete() stc = [ f"{i + 1}. {guild.get_member(ALL_PLAYER[i]).display_name}\n" for i in range(len(ALL_PLAYER)) ] await ctx.send( f"カードを配りました、各自BotからのDMを確認してください\nゲームの進行順は以下のようになります```{''.join(stc)}```" ) await ctx.send( f"{all_mention(guild)}\nゲームを始めてもよろしいですか?(1分以上経過 or 全員が `!OK` で開始)") ok_player, ask_start = [], datetime.now() while True: try: reply = await bot.wait_for('message', check=user_check, timeout=60 - (datetime.now() - ask_start).seconds) except asyncio.exceptions.TimeoutError: break if reply.author.id not in ok_player and reply.author.id in ALL_PLAYER: if jaconv.z2h(reply.content, ascii=True).lower() == "!ok": ok_player.append(reply.author.id) if len(ok_player) == len(ALL_PLAYER): break TURN, card, penalty, winner, msg1, msg2, time_cut = 0, [ uf.number_card() ], 0, None, None, None, 0 start_time = datetime.now(timezone('UTC')).astimezone( timezone('Asia/Tokyo')).strftime('%Y/%m/%d %H:%M') msg_watchgame = None shutil.copy(mi.AREA_PASS, mi.AREA_COPY_PASS) mi.make_area(card[-1]) JOINING_WAIT = False while True: # 観戦機能ON時は手札を表示 if WATCH_FLAG is not None: if msg_watchgame is not None: await msg_watchgame.delete() msg = "" for j in range(len(ALL_DATA)): name = f"{j + 1}. {guild.get_member(ALL_DATA[j][0]).display_name}" hand = uf.card_to_string(ALL_DATA[j][1]) if len(name) + len(hand) <= 1970 // len(ALL_PLAYER): msg += f"{name}【{hand}】\n\n" else: msg += f"{name}【文字数制限を超過しているため表示できません】\n\n" msg_watchgame = await bot.get_channel(WATCH_FLAG).send( f"```\n各プレイヤーの現在の手札一覧\n\n{msg}```") # i: ユーザー指定変数, bet_flag: カードを出したか, get_flag: !getでカードを引いたか, drop_flag: 棄権者が出たか i, bet_flag, get_flag, drop_flag, bet_card = TURN % len( ALL_DATA), False, False, False, "" # 参加者のIDリスト ALL_PLAYER = [ALL_DATA[j][0] for j in range(len(ALL_DATA))] # 制限時間設定 time = len(ALL_DATA[i][1]) * 5 + 5 time = 30 if time < 30 else 60 if time > 60 else time time, time_cut = round(time / 4**time_cut, 1), 0 # UNO指摘間違いリセット ALL_DATA[i][4] = int(ALL_DATA[i][4]) # 場札更新の際に以前のメッセージを削除 if msg1 is not None: await msg1.delete() await msg2.delete() stc = [ f"{j + 1}. {guild.get_member(ALL_DATA[j][0]).display_name} : {len(ALL_DATA[j][1])}枚\n" for j in range(len(ALL_DATA)) ] msg1 = await ctx.send( f"```\n各プレイヤーの現在の手札枚数\n\n{''.join(stc)}```" f"__現在の場札のカード : {card[-1]}__", file=discord.File(mi.AREA_COPY_PASS)) msg2 = await ctx.send( f"{bot.get_user(ALL_DATA[i][0]).mention} の番です (制限時間{time:g}秒)") # 手札が100枚を超えたら脱落(特殊ルールモード) if len(ALL_DATA[i][1]) > 100 and special_flag: await ctx.send( f"{all_mention(guild)}\n{bot.get_user(ALL_DATA[i][0]).mention} 手札が100枚を超えたので脱落となります" ) if len(ALL_PLAYER) == 1: await ctx.send("プレイヤーが0人となったのでゲームを終了しました") await uno_end(guild, True, False) return else: ALL_DATA.pop(i) continue # 記号しか無い時は2枚追加(1度のみ) if all([uf.card_to_id(j) % 100 > 9 for j in ALL_DATA[i][1]]): await ctx.send( f"{bot.get_user(ALL_DATA[i][0]).mention} 記号残りなので2枚追加します", delete_after=10) await send_card(bot, i, 2, True) # カード入力処理 start = datetime.now() while True: # パスペナルティーが残っている場合 if ALL_DATA[i][4] >= 1: ALL_DATA[i][4] -= 1 await turn_pass("ペナルティーが課せられているので、このターンは強制的に") break try: reply = await bot.wait_for('message', check=ng_check, timeout=time - (datetime.now() - start).seconds) input = jaconv.z2h(jaconv.h2z(reply.content), kana=False, ascii=True, digit=True).lower() except asyncio.exceptions.TimeoutError: await turn_pass("時間切れとなったので") break # ゲームから棄権する if "!drop" in input and (reply.author.id in ALL_PLAYER or role_check_mode(reply)): # 棄権者を指定 if len(ALL_DATA) > 2 and len(reply.raw_mentions) == 1: if role_check_mode(reply) or special_flag: j = uf.search_player(reply.raw_mentions[0], ALL_DATA) if j is not None: await ctx.send( f"{all_mention(guild)}\n{bot.get_user(ALL_DATA[j][0]).mention} を棄権させました" ) TURN = i - 1 if j < i else i if normal_flag: drop_name = guild.get_member( ALL_DATA[j][0]).display_name ur.add_penalty(ALL_DATA[j][0], drop_name, ALL_DATA[j][1]) ALL_DATA.pop(j) drop_flag = True break else: await ctx.send( f"{reply.author.mention} 棄権させられる権限がありません", delete_after=10) # 自分が棄権する elif len(ALL_DATA) > 2 and len(reply.raw_mentions) == 0: j = uf.search_player(reply.author.id, ALL_DATA) if j is not None: await ctx.send( f"{all_mention(guild)}\n{reply.author.mention} が棄権しました" ) TURN = i - 1 if j < i else i if normal_flag: drop_name = guild.get_member( reply.author.id).display_name ur.add_penalty(reply.author.id, drop_name, ALL_DATA[j][1]) ALL_DATA.pop(j) drop_flag = True break else: await ctx.send(f"{reply.author.mention} 2人以下の状態では棄権出来ません", delete_after=10) # ゲームを強制中止する elif input == "!cancel" and reply.author.id in ALL_PLAYER: await ctx.send( f"{all_mention(guild)}\nゲームを中止しますか?(過半数が `!OK` で中止、`!NG` でキャンセル)" ) turn_cancel, turn_ng, cancel_player, ng_player = 0, 0, [], [] while True: confirm = await bot.wait_for('message', check=user_check) input = jaconv.z2h(confirm.content, ascii=True).lower() if input == "!ok" and confirm.author.id not in cancel_player: turn_cancel += 1 cancel_player.append(confirm.author.id) elif input == "!ng" and confirm.author.id not in ng_player: turn_ng += 1 ng_player.append(confirm.author.id) if turn_cancel >= len(ALL_PLAYER) // 2 + 1: await uno_end(guild, True, False) await ctx.send(f"{all_mention(guild)}\nゲームを中止しました") return elif turn_ng >= len(ALL_PLAYER) // 2 + 1: await ctx.send(f"{all_mention(guild)}\nキャンセルしました") break # 自分のターンでの行動 elif reply.author.id == ALL_DATA[i][0] and "!uno" not in input: # 山札から1枚引く if input in ["!g", "!get"]: if not get_flag: await ctx.send( f"{bot.get_user(ALL_DATA[i][0]).mention} 山札から1枚引きます", delete_after=5) await send_card(bot, i, 1, True) get_flag = True else: await ctx.send( f"{bot.get_user(ALL_DATA[i][0]).mention} 山札から引けるのは1度のみです", delete_after=5) # カードを出さない elif input in ["!p", "!pass"]: await turn_pass() break else: # 出せるカードかチェック bet_card = uf.string_to_card(input) error = uf.check_card(card[-1], bet_card, ALL_DATA[i][1], penalty) if error is None: # 出したカードを山場に追加 card += bet_card # 場札更新 for j in bet_card: # 出したカードを手札から削除 ALL_DATA[i][1].remove(j) # 7が出されたらタイム減少 if uf.card_to_id(j) % 100 == 7: time_cut += 1 # ドロー/ドボンのペナルティー枚数計算 penalty += uf.calculate_penalty(bet_card) bet_flag = True break else: await ctx.send( f"{bot.get_user(ALL_DATA[i][0]).mention} {error}", delete_after=5) # 棄権時は以下の処理を飛ばす if drop_flag: continue DECLARATION_WAIT = True # ワイルドカード処理(色指定) if 500 <= uf.card_to_id(card[-1]) <= 569 and bet_flag: msg = await ctx.send( f"{bot.get_user(ALL_DATA[i][0]).mention} 色を指定してください (制限時間20秒)\n" f"(赤[R] / 青[B] / 緑[G] / 黄[Y] / ランダム[X] と入力)") start = datetime.now() while True: try: color = await bot.wait_for( 'message', check=user_check, timeout=20 - (datetime.now() - start).seconds) input = jaconv.z2h(color.content, ascii=True, kana=False).lower() except asyncio.exceptions.TimeoutError: await ctx.send( f"{bot.get_user(ALL_DATA[i][0]).mention} 時間切れなのでランダムで決めます", delete_after=5) card[-1] = f"{random.choice(uf.Color)}{card[-1]}" break if color.author.id != ALL_DATA[i][0]: continue if uf.translate_input(input) not in uf.Color + ["ランダム", "x"]: await ctx.send( f"{bot.get_user(ALL_DATA[i][0]).mention} そんな色はありません", delete_after=5) continue if uf.translate_input(input) in uf.Color: card[-1] = f"{uf.translate_input(input)}{card[-1]}" else: card[-1] = f"{random.choice(uf.Color)}{card[-1]}" break bet_card[-1] = card[-1] await msg.delete() # ディスカードオール処理 if (uf.card_to_id(card[-1]) % 100 == 13 or 570 <= uf.card_to_id(card[-1]) <= 574) and bet_flag: wild_cnt = 0 for j in range(len(bet_card)): if 570 <= uf.card_to_id(bet_card[j]) <= 574: wild_cnt += 1 if wild_cnt >= 1: msg = await ctx.send( f"{bot.get_user(ALL_DATA[i][0]).mention} " f"消去したい色を{wild_cnt}色指定してください(重複可) (制限時間20秒)\n" f"(赤[R] / 青[B] / 緑[G] / 黄[Y] / ランダム[X] と入力)") start = datetime.now() while True: try: color = await bot.wait_for( 'message', check=user_check, timeout=20 - (datetime.now() - start).seconds) input = uf.string_to_card( jaconv.z2h(color.content, ascii=True, kana=False).lower()) except asyncio.exceptions.TimeoutError: await ctx.send( f"{bot.get_user(ALL_DATA[i][0]).mention} 時間切れなのでランダムで決めます", delete_after=5) for j in range(-len(bet_card), 0): if 570 <= uf.card_to_id(card[j]) <= 574: card[j] = f"{random.choice(uf.Color)}{card[j]}" bet_card[j] = card[j] break if color.author.id != ALL_DATA[i][0]: continue if not all([ uf.translate_input(input[j]) in uf.Color + ["ランダム", "x"] for j in range(len(input)) ]): await ctx.send( f"{bot.get_user(ALL_DATA[i][0]).mention} 間違った色が含まれています", delete_after=5) continue if len(input) != wild_cnt: await ctx.send( f"{bot.get_user(ALL_DATA[i][0]).mention} 色の数が間違っています", delete_after=5) continue k = 0 for j in range(-len(bet_card), 0): if 570 <= uf.card_to_id(card[j]) <= 574: if input[k] in uf.Color: card[ j] = f"{uf.translate_input(input[k])}{card[j]}" else: card[j] = f"{random.choice(uf.Color)}{card[j]}" bet_card[j] = card[j] k += 1 break await msg.delete() colors = set([f"{bet_card[j][0]}色" for j in range(len(bet_card))]) await ctx.send( f"{bot.get_user(ALL_DATA[i][0]).mention} " f"{', '.join(colors)} のカードを全て捨てます", delete_after=5) for j in range(len(bet_card)): ALL_DATA[i][1] = uf.remove_color_card(bet_card[j][0], ALL_DATA[i][1]) # 全部カードが無くなった場合 if not ALL_DATA[i][1]: await ctx.send( f"{bot.get_user(ALL_DATA[i][0]).mention} 記号上がりとなるので2枚追加します", delete_after=10) await send_card(bot, i, 2, True) else: await send_card(bot, i, 0, False) # ディスカードオール以外の場合の手札更新 elif bet_flag: await send_card(bot, i, 0, False) DECLARATION_WAIT = False # ペナルティーを受ける if penalty > 0 and not bet_flag: # ドローの場合 if 540 <= uf.card_to_id( card[-1]) <= 544 or uf.card_to_id(card[-1]) % 100 == 12: await ctx.send( f"{bot.get_user(ALL_DATA[i][0]).mention} ペナルティーで{penalty}枚追加します", delete_after=10) await send_card(bot, i, penalty, True) # ドボンの場合 else: await ctx.send( f"{all_mention(guild)}\n{bot.get_user(ALL_DATA[i - 1][0]).mention}以外の全員に" f"ペナルティーで{penalty}枚追加します", delete_after=10) for j in range(len(ALL_PLAYER)): if ALL_DATA[i - 1][0] != ALL_PLAYER[j]: await send_card(bot, j, penalty, True) penalty, TURN, = 0, TURN - 1 # 手札が0枚となったので上がり if not ALL_DATA[i][1] and not ALL_DATA[i][3][0]: await ctx.send(f"{bot.get_user(ALL_DATA[i][0]).mention} YOU WIN!") winner = i break # 手札は0枚になったがUNOの宣言忘れ elif not ALL_DATA[i][1]: await ctx.send( f"{bot.get_user(ALL_DATA[i][0]).mention} UNO宣言忘れのペナルティーで2枚追加します", delete_after=10) await send_card(bot, i, 2, True) # 上がれる手札になったらUNOフラグを立てる elif uf.check_win(ALL_DATA[i][1]): ALL_DATA[i][3] = [True, datetime.now()] # 上がれない手札だったらUNOフラグを降ろす elif not uf.check_win(ALL_DATA[i][1]): ALL_DATA[i][3] = [False, None] # スキップ処理 if uf.card_to_id(card[-1]) % 100 == 10 and bet_flag: await ctx.send(f"{2 * len(bet_card) - 1}人スキップします", delete_after=10) TURN += 2 * len(bet_card) - 1 # リバース処理 elif uf.card_to_id(card[-1]) % 100 == 11 and bet_flag: await ctx.send(f"{len(bet_card)}回リバースします", delete_after=10) if len(bet_card) % 2 == 1: # リバースを出した人のリバースされた配列中の位置を代入 tmp = copy.copy(ALL_DATA[i][0]) ALL_DATA.reverse() TURN = uf.search_player(tmp, ALL_DATA) # ワイルドカードで順番シャッフル elif 530 <= uf.card_to_id(card[-1]) <= 534 and bet_flag: await ctx.send(f"{all_mention(guild)}\n順番がシャッフルされました", delete_after=10) random.shuffle(ALL_DATA) # 場札更新 if bet_flag: [mi.make_area(j) for j in bet_card] # ターンエンド → 次のプレイヤーへ TURN += 1 # 点数計算 all_name, stc = [], "" for i in range(len(ALL_DATA)): ALL_DATA[i].append(ur.calculate_point(ALL_DATA[i][1])) # 1位には他ユーザーの合計得点をプラス ALL_DATA[winner][5] = sum([ALL_DATA[i][5] for i in range(len(ALL_DATA))]) * -1 # 結果データをソート sort_data = sorted(ALL_DATA, key=lambda x: x[5], reverse=True) for i in range(len(sort_data)): all_name.append(guild.get_member(sort_data[i][0]).display_name) last_card = uf.card_to_string(sort_data[i][1]) if len(last_card) > 1900 // len(ALL_PLAYER) or len(last_card) > 400: last_card = "多すぎるため表示出来ません" stc += f"{i + 1}位 : {all_name[-1]} ({sort_data[i][5]:+}pts)\n残り手札【{last_card}】\n\n" # ゲーム終了処理 (画像やロール削除) if normal_flag: # 7人以上参加時はWinner/Loserロール付与 & 結果出力 if 7 <= len(ALL_DATA): end_time = datetime.now(timezone('UTC')).astimezone( timezone('Asia/Tokyo')).strftime('%m/%d %H:%M') await guild.get_member(sort_data[0][0] ).add_roles(get_role(guild, cs.Winner)) await guild.get_member(sort_data[-1][0] ).add_roles(get_role(guild, cs.Loser)) await bot.get_channel( cs.Result).send(f"__★UNO試合結果 ({start_time} ~ {end_time})__") embed = discord.Embed(color=0xff0000) embed.set_author(name='Results', icon_url='https://i.imgur.com/F2oH0Bu.png') embed.set_thumbnail(url='https://i.imgur.com/JHRshwi.png') embed.add_field(name=f"優勝 ({sort_data[0][5]:+}点)", value=f"{all_name[0]}", inline=False) for i in range(1, len(sort_data) - 1): embed.add_field(name=f"{i + 1}位 ({sort_data[i][5]}点)", value=f"{all_name[i]}") embed.add_field(name=f"最下位 ({sort_data[-1][5]}点)", value=f"{all_name[-1]}") await bot.get_channel(cs.Result).send(embed=embed) role_W, role_L = get_role(guild, cs.Winner), get_role(guild, cs.Loser) await bot.get_channel(cs.Result).send( f"{role_W.mention} : {guild.get_member(sort_data[0][0]).mention}\n" f"{role_L.mention} : {guild.get_member(sort_data[-1][0]).mention}" ) ur.data_save(sort_data, all_name) await ctx.send( f"{all_mention(guild)}```\n★ゲーム結果\n\n{stc}```結果を記録してゲームを終了しました") await uno_end(guild, True, True) else: await ctx.send( f"{all_mention(guild)}```\n★ゲーム結果\n\n{stc}```ゲームを終了しました(結果は反映されません)" ) # フリープレイ時はUNOロールを付与しない if mi.AREA_PASS == mi.AREA_SP_PASS: await uno_end(guild, True, True) else: await uno_end(guild, True, False)