207 lines
11 KiB
Python
207 lines
11 KiB
Python
|
from objects.rance import automate
|
||
|
import random as ra
|
||
|
import asyncio
|
||
|
import discord
|
||
|
|
||
|
excluded=False
|
||
|
commands={'rance':[], 'rancequit':[], 'rancehack':[], 'rancerank':[], 'azul':[]}
|
||
|
|
||
|
rancegames={}
|
||
|
|
||
|
async def rance(args, channel, message):
|
||
|
if channel.id in rancegames and not type(rancegames[channel.id])==tuple:
|
||
|
await channel.send("Game already in progress, just watch or pick another channel")
|
||
|
return
|
||
|
p1=message.author
|
||
|
if len(args)==1:
|
||
|
try:
|
||
|
num=int(args[0])
|
||
|
except ValueError: num=None
|
||
|
if num is not None and 0<num<7: rancegames[channel.id]=(p1,num); await channel.send("Game is open! Awaiting other party!")
|
||
|
return
|
||
|
elif len(args)==0 and type(rancegames[channel.id])==tuple:
|
||
|
p1=rancegames[channel.id][0]; p2=message.author; num=rancegames[channel.id][1]
|
||
|
else:
|
||
|
p2=u.findobj(args[0],channel.guild.members)
|
||
|
if p2 is None:
|
||
|
await channel.send("Challenging players directly doesn't work anymore, thanks to intents.")
|
||
|
#await channel.send("First argument should name another player (mention, username, or id)")
|
||
|
return
|
||
|
try:
|
||
|
num=int(args[1])
|
||
|
except ValueError:
|
||
|
await channel.send("Second argument should be number of units (1-6)")
|
||
|
return
|
||
|
if not 1<=num<=6:
|
||
|
await channel.send("Number of units should be between 1 and 6, inclusive")
|
||
|
return
|
||
|
await rancesetup(p1,p2,channel,num)
|
||
|
await playrance(channel)
|
||
|
|
||
|
async def rancerank(args,channel,message):
|
||
|
if channel.id in rancegames: await channel.send("Game already in progress, just watch or pick another channel"); return
|
||
|
if len(args)==0: await channel.send("You need to challenge an opponent with this."); return
|
||
|
p2=u.findobj(' '.join(args),channel.guild.members)
|
||
|
if p2 is None: await channel.send("Couldn't find opponent; try mentioning them."); return
|
||
|
p1=message.author
|
||
|
rancegames[channel.id]=True
|
||
|
def validateunits(x):
|
||
|
n=x.split('\n')
|
||
|
for m in n[1:]:
|
||
|
if len(m.split(','))!=9: return f"line {m} is invalid somehow, please redo it"
|
||
|
return True
|
||
|
units=await getnp("Please enter your name, then the units you wish to bring to battle, in the following format:\npos,class,atk,def,int,spd,troops,flags,name\nPlease enter one unit per line, but the first line should be your name.\nAlso note that this will be logged; this function is intended for tournament play, where you have an existing roster. If cheating is suspected, the logs will be reviewed. Try not to use it frivolously during tournaments.",[p1,p2],[[],[]],[validateunits])
|
||
|
u.log('',*units) # That just makes the logs look a bit neater.
|
||
|
units=units[0].split('\n'),units[1].split('\n')
|
||
|
names=units[0][0],units[1][0]
|
||
|
units=[n.split(',') for n in units[0][1:]],[n.split(',') for n in units[1][1:]]
|
||
|
rancegames[channel.id]=board=automate.Board(names[0],names[1],p1.id,p2.id,{int(m[0])-1:automate.unit.BattleUnit(m[1],*[int(n) for n in m[2:-1]],name=m[-1]) for m in units[0]},{int(m[0])-1:automate.unit.BattleUnit(m[1],*[int(n) for n in m[2:-1]],name=m[-1]) for m in units[1]})
|
||
|
await p1.send(f"Game's ready in <#{channel.id}>")
|
||
|
await p2.send(f"Game's ready in <#{channel.id}>")
|
||
|
await playrance(channel)
|
||
|
|
||
|
async def rancesetup(p1,p2,channel,num):
|
||
|
# All checks finally out of the way zzzz
|
||
|
rancegames[channel.id]=seed=ra.random()
|
||
|
await channel.send("ゲーム・スタート!")
|
||
|
# Fire off the first set of listeners; the player names.
|
||
|
try:
|
||
|
names=await getnp("Please enter player name.",[p1,p2],[[],[]],[])
|
||
|
except (discord.Forbidden,discord.HTTPException) as ex:
|
||
|
await channel.send("Couldn't DM one of the players (can't tell who yet), maybe their DM settings are wrong; perhaps try DMing me first.")
|
||
|
rancegames.pop(channel.id)
|
||
|
return
|
||
|
if not rancestatus(channel.id,seed): return
|
||
|
berd=automate.blindstart(num,*names)
|
||
|
classlist=next(berd)
|
||
|
# https://stackoverflow.com/a/44780467
|
||
|
nl='\n'
|
||
|
longth=lambda x:len(x.split(' '))==num or "Wrong number of units provided"
|
||
|
def incls(x):
|
||
|
out=[]
|
||
|
for n in x.split(' '):
|
||
|
if n not in automate.classlist: out.append(n)
|
||
|
if out: return f"Invalid class names: {', '.join(out)}. Please note they're case sensitive."
|
||
|
return True
|
||
|
classes=await getnp(f"What classes would you like for your **{num}** units? (Space separated)\nList of melee classes (work best in front row):\n{nl.join(classlist[0])}\n\nRanged (work best in back row):\n{nl.join(classlist[1])}",[p1,p2],[[],[]],[longth,incls])
|
||
|
if not rancestatus(channel.id,seed): return
|
||
|
units=berd.send(tuple([m.lower() for m in n.split(' ')] for n in classes))
|
||
|
def isint(x):
|
||
|
try: int(x); return True
|
||
|
except ValueError: return f"'{x}' isn't a valid integer"
|
||
|
rerolls=await getnp("Your units:\n{0}\n Please pick a number (from 1 to {1}) to reroll; you can discard the reroll, don't worry",[p1,p2],[['\n'.join(units[0]),num],['\n'.join(units[1]),num]],[isint,lambda x:int(x) in range(1,num+1) or f"{x} isn't a valid unit number"])
|
||
|
rerolls=tuple(int(n)-1 for n in rerolls)
|
||
|
if not rancestatus(channel.id,seed): return
|
||
|
news=berd.send(rerolls)
|
||
|
boolaliases={'new':True,'old':False, 'yes':True,'no':False, 'true':True,'false':False, '1':True,'0':False, 'y':True,'n':False}
|
||
|
confirms=await getnp("Old unit: {0}\nNew unit: {1}\nWhich would you like to keep (old/new)?",[p1,p2],[[units[0][rerolls[0]],news[0]],[units[1][rerolls[1]],news[1]]],[lambda x:x in boolaliases or "Please specify either `old` or `new`"])
|
||
|
if not rancestatus(channel.id,seed): return
|
||
|
_=berd.send(tuple(boolaliases[n.lower()] for n in confirms))
|
||
|
def checknames(x):
|
||
|
if x in ('-','`-`'): return True
|
||
|
try: a={int(n.split(':')[0]):n.split(':')[1] for n in x.split(' ')}
|
||
|
except: return 'The format was weird in some way, please try again.'
|
||
|
if min(list(a))<1 or max(list(a))>num+1: return f"Numbers should be between 1 and {num} inclusive."
|
||
|
return True
|
||
|
unames=await getnp('You may provide names for your units, if you wish. Please provide them as num:name pairs, eg. `2:Ran 3:Rin`\nUnnamed units will be assigned a number as their name. Say `-` if you don\'t want to name any units.',[p1,p2],[[],[]],[checknames])
|
||
|
if not rancestatus(channel.id,seed): return
|
||
|
def isints(x):
|
||
|
for n in x.split(' '):
|
||
|
try: int(n)
|
||
|
except ValueError: return f"'{n}' isn't a valid integer"
|
||
|
if int(n) not in range(1,7): return f"{n} isn't a valid position"
|
||
|
return True
|
||
|
units=berd.send(({int(n.split(':')[0])-1:n.split(':')[1] for n in unames[0].split(' ')} if unames[0]!='-' else {},{int(n.split(':')[0])-1:n.split(':')[1] for n in unames[1].split(' ')} if unames[1]!='-' else {}))
|
||
|
positions=await getnp("Final unit list:\n{0}\nPlease enter the positions for these units (1-3 back row (ranged units should go here), 4-6 front row (melee units should go here), space separated, positions should be unique!)",[p1,p2],[['\n'.join(units[0])],['\n'.join(units[1])]],[lambda x:len(x.split(' '))==num or f"You should provide {num} position numbers", isints, lambda x:len(set(x.split(' ')))==len(x.split(' ')) or "Position numbers must be unique"])
|
||
|
if not rancestatus(channel.id,seed): return
|
||
|
_=berd.send(([int(n)-1 for n in positions[0].split(' ')],[int(n)-1 for n in positions[1].split(' ')]))
|
||
|
try: berd.send((p1.id,p2.id))
|
||
|
except StopIteration as ex:
|
||
|
board=ex.value
|
||
|
# We have a board!
|
||
|
rancegames[channel.id]=board
|
||
|
await p1.send(f"Game's ready in <#{channel.id}>")
|
||
|
await p2.send(f"Game's ready in <#{channel.id}>")
|
||
|
|
||
|
async def playrance(channel,board=None):
|
||
|
nl='\n'
|
||
|
if board is None: board=rancegames[channel.id]
|
||
|
controller=board.controller()
|
||
|
await channel.send("If a unit wasn't named, its unit code became its name.\nファイト!")
|
||
|
# No auto quit yet.
|
||
|
while True:
|
||
|
if not rancestatus(channel.id,board): return
|
||
|
# We can discard this during charged actions but if we don't get it shit goes wrong.
|
||
|
try: active=next(controller)
|
||
|
except StopIteration as ex: end=ex; break
|
||
|
if active is None:
|
||
|
# Charged action is happening. Don't wait for action or anything.
|
||
|
action=None
|
||
|
else:
|
||
|
await channel.send(f'```{board}```')
|
||
|
await channel.send(f"Available actions for {active[0][0]}'s unit {active[1]}:\n{nl.join([', '.join(n) for n in active[2]])}")
|
||
|
msg=await client.wait_for('message',check=lambda x:x.author.id==active[0][1] and x.channel.id==channel.id and x.content.split(' ')[0] in [n[0] for n in active[2]])
|
||
|
action=msg.content.split(' ')
|
||
|
try: res=controller.send(action); await channel.send(res)
|
||
|
except Exception as ex: await channel.send(str(ex)+'\nThis might have broken the game state; please confirm and quit if so')
|
||
|
await channel.send(f'```{board}```')
|
||
|
await channel.send(end)
|
||
|
rancegames.pop(channel.id)
|
||
|
|
||
|
async def getnp(question,players,formats,conditions):
|
||
|
# Each condition is a function that will return a string if the content failed validation.
|
||
|
# That string should be fired back to the user, for them to retry.
|
||
|
# The conditions should accept a string.
|
||
|
solos=[get1p(question,players[i],formats[i],conditions) for i in range(len(players))]
|
||
|
return [n.content.replace(' ',' ') for n in await asyncio.gather(*solos)]
|
||
|
|
||
|
async def get1p(question,player,forma,conditions):
|
||
|
# Probably easier to do it like this.
|
||
|
await player.send(question.format(*forma))
|
||
|
valid=False
|
||
|
while not valid:
|
||
|
msg=await client.wait_for('message',check=lambda x:x.author.id==player.id and x.channel==x.author.dm_channel)
|
||
|
content=msg.content.replace(' ',' ')
|
||
|
valid=True
|
||
|
for con in conditions:
|
||
|
res=con(content)
|
||
|
if res!=True: valid=False; await player.send(f"Please try again:\n{res}"); break
|
||
|
return msg
|
||
|
|
||
|
async def rancequit(args, channel, message):
|
||
|
# Get noobed
|
||
|
try: del rancegames[channel.id]; rancegames.pop(channel.id)
|
||
|
except KeyError: pass
|
||
|
|
||
|
# I might make this use the playrance function later.
|
||
|
async def rancehack(args, channel, message):
|
||
|
rancegames[channel.id]=board=automate.Board('a','b','','',{0:automate.unit.BattleUnit('tactician',1,9,3,9,400,4,{},'Ram'),2:automate.unit.BattleUnit('tactician',1,9,3,3,400,4,{},'Rem')},{4:automate.unit.BattleUnit('tactician',1,9,3,3,400,4,{},'Rom'),
|
||
|
2:automate.unit.BattleUnit('tactician',1,9,3,3,400,4,{},'Rim')})
|
||
|
board.turn=2
|
||
|
controller=board.controller()
|
||
|
nl='\n'
|
||
|
while True:
|
||
|
if not rancestatus(channel.id,board): return
|
||
|
# We can discard this during charged actions but if we don't get it shit goes wrong.
|
||
|
try: active=next(controller)
|
||
|
except StopIteration as ex: end=ex; break
|
||
|
if active is None:
|
||
|
# Charged action is happening. Don't wait for action or anything.
|
||
|
action=None
|
||
|
else:
|
||
|
await channel.send(f'```{board}```')
|
||
|
await channel.send(f"Available actions for {active[0][0]}'s unit {active[1]}:\n{nl.join([', '.join(n) for n in active[2]])}")
|
||
|
msg=await client.wait_for('message',check=lambda x:x.author!=client.user and x.channel.id==channel.id and x.content.split(' ')[0] in [n[0] for n in active[2]])
|
||
|
action=msg.content.split(' ')
|
||
|
res=controller.send(action); await channel.send(res)
|
||
|
#except Exception as ex: await channel.send(str(ex)+'\nThis might have broken the game state; please confirm and quit if so')
|
||
|
await channel.send(f'```{board}```')
|
||
|
await channel.send(end)
|
||
|
rancegames.pop(channel.id)
|
||
|
|
||
|
def rancestatus(cid,seed):
|
||
|
return cid in rancegames and rancegames[cid]==seed
|
||
|
|
||
|
async def azul(args, channel, message):
|
||
|
pass
|