728x90
반응형

처음 유저 데이터를 만들었을 때 레벨도 추가했었지만 거의 더미데이터 수준으로 쓰이지 않았다

일단 레벨업부터 만들어보자

아직 레벨업 수단에 대해서 생각해둔 게 없기 때문에 임의로 경험치를 추가 할 수 있는 명령어도 만들어야 한다

 

레벨 관련 코드 추가

먼저 userDB.xlsx를 조금 수정한다

레벨업에 필요한 경험치(exp)를 추가하고 column의 위치도 조금 바꾸었다

 

다음으로 user.py에서 필요한 코드를 수정한다

column의 위치를 다시 정해주고 회원가입과 정보를 가져오는 함수에 exp를 추가했다

#user.py
...
c_name = 1
c_id = 2
c_lvl = 3
c_exp = 4
c_money = 5
c_loss = 6
...
def Signup(_name, _id):
    loadFile()

    _row = checkFirstRow()

    ws.cell(row=_row, column=c_name, value=_name)
    ws.cell(row=_row, column=c_id, value =hex(_id))

    ws.cell(row=_row, column=c_lvl, value = 1)
    ws.cell(row=_row, column=c_exp, value = 0)

    ws.cell(row=_row, column=c_money, value = default_money)
    ws.cell(row=_row, column=c_loss, value = 0)

    saveFile()
...
def userInfo(_row):
    loadFile()

    _lvl = ws.cell(_row,c_lvl).value
    _exp = wsl.cell(_row,c_exp).value
    _money = ws.cell(_row,c_money).value
    _loss = ws.cell(_row,c_loss).value

    saveFile()

    return _lvl, _exp, _money, _loss
...
def addExp(_row, _amount):
    loadFile()

    ws.cell(_row, c_exp).value += _amount

    saveFile()

 

main.py에서도 정보관련 함수를 수정한다

정보를 가져올 때 exp를 추가하고 embed에도 exp 정보를 보내도록 수정한다

#main.py
...
@bot.command()
async def 내정보(ctx):
    userExistance, userRow = checkUser(ctx.author.name, ctx.author.id)

    if not userExistance:
        await ctx.send("회원가입 후 자신의 정보를 확인할 수 있습니다.")
    else:
        level, exp, money, loss = userInfo(userRow)

        embed = discord.Embed(title="유저 정보", description = ctx.author.name, color = 0x62D0F6)
        embed.add_field(name = "레벨", value = level)
        embed.add_field(name = "경험치", value = exp)
        embed.add_field(name = "보유 자산", value = money, inline = False)
        embed.add_field(name = "도박으로 날린 돈", value = loss, inline = False)

        await ctx.send(embed=embed)

@bot.command()
async def 정보(ctx, user: discord.User):
    userExistance, userRow = checkUser(user.name, user.id)

    if not userExistance:
        await ctx.send(user.name  + " 은(는) 등록되지 않은 사용자입니다.")
    else:
        level, exp, money, loss = userInfo(userRow)
        
        embed = discord.Embed(title="유저 정보", description = user.name, color = 0x62D0F6)
        embed.add_field(name = "레벨", value = level)
        embed.add_field(name = "경험치", value = exp)
        embed.add_field(name = "보유 자산", value = money, inline = False)
        embed.add_field(name = "도박으로 날린 돈", value = loss, inline = False)

        await ctx.send(embed=embed)
...

정보 확인도 문제 없이 되었다

 

레벨업 시스템

일단은 채팅을 치면 경험치를 얻는 것으로 하고 코딩을 하려고 했으나

봇이 돌아가는 동안 어떻게 레벨업을 확인할지 그림이 안그려졌다

 

일단 discord.py에서는 on_message(msg)라는 event를 가지고 있는데 이걸 그대로 사용하면

계속 이 함수에서만 돌기 때문에 지금까지 만든 모든 명령어를 이쪽으로 옮기는 작업을 해야한다...

그런 미친짓을 할 수 없기에 일단 열심히 검색을 해봤다

그저 빛

누가 말했지

코딩을 하면서 뭔가 안된다면 검색을 해보라고...

어딘가 사는 누군가가 이미 그걸 경험했고 다른 누군가가 해결책을 제시했다고...

https://tmrtkr.tistory.com/83

이렇게 하면 된다고 한다

 

확인을 위해 main.py에 코드를 살짝 추가했다

...
@bot.event
async def on_message(message):
	if message.author == bot.user:
    	return
    print("levelup!")
    await bot.process_commands(message)
...

성공했다!

 

이제 레벨업을 확인하는 함수를 만들어야겠다

레벨업 당 필요한 경험치는 마인크래프트 위키를 참고했다

뭔가 많지만 귀찮으니 첫번째줄만 사용한다

#main.py
...
@bot.event
async def on_message(message):
    if message.author == bot.user:
        return
        
    userExistance, userRow = checkUser(message.author.name, message.author.id)
    channel = message.channel
    if userExistance:
        levelUp, lvl = levelupCheck(userRow)
        
        if levelUp:
            embed = discord.Embed(title = "레벨업", description = None, color = 0x00A260)
            embed.set_footer(text = message.author.name + "이 " + str(lvl) + "레벨 달성!")
            await channel.send(embed=embed)
        else:
            addExp(userRow, 1)

    await bot.process_commands(message)
...
#user.py
...
def levelupCheck(_row):
    print("user.py - levelupCheck")
    loadFile()

    name = ws.cell(_row, c_name).value
    exp = ws.cell(_row, c_exp).value
    lvl = ws.cell(_row, c_lvl).value
    amount_to_up = lvl*lvl + 6*lvl

    if exp >= amount_to_up:
        ws.cell(_row, c_lvl).value += 1
        ws.cell(_row, c_exp).value -= amount_to_up
        return True, lvl+1
    else:
        return False, lvl
...
def addExp(_row, _amount):
    loadFile()

    ws.cell(_row, c_exp).value += _amount

    saveFile()
...

메세지 보낸게 봇이면 무시하고 levelupCheck를 통해 레벨업 여부 판단하여 메세지를 보낸다

 

 

다만 아직 문제가 좀 있는게 갑자기 많은 경험치를 주었을 때 한번에 레벨업하지 않는다

아마 반복문으로 해결 가능할 듯 하다

#user.py
...
def levelupCheck(_row):
    loadFile()

    name = ws.cell(_row, c_name).value
    exp = ws.cell(_row, c_exp).value
    lvl = ws.cell(_row, c_lvl).value
    amount_to_up = lvl*lvl + 6*lvl
    count = 0

    if exp >= amount_to_up:
        while(exp >= amount_to_up and exp >= 0):
            ws.cell(_row, c_lvl).value += 1
            count += 1
            ws.cell(_row, c_exp).value -= amount_to_up

            lvl = ws.cell(_row, c_lvl).value
            exp = ws.cell(_row, c_exp).value
            amount_to_up = lvl*lvl + 6*lvl
        return True, lvl+count
    else:
        return False, lvl

 반복문으로 수정한 결과 한번에 많은 경험치가 들어와도 정상적으로 레벨업 처리가 되었다

!exp는 테스트를 위한 명령어

 

랭킹 시스템

이제 랭킹을 만들어보자

먼저 내정보에서 자신의 순위를 볼 수 있게 하자

#user.py
...
def ranking():
    loadFile()

    userRanking =  {}
    userNum = checkUserNum()

    for row in range(2, 2+userNum):
        _name = ws.cell(row, c_name).value
        _lvl = ws.cell(row, c_lvl).value
        userRanking[_name] = _lvl
        
    a = sorted(userRanking.items(), reverse=True, key=lambda item:item[1])
    result = []
    for items in a:
        result.append(items[0])
        result.append(items[1])
    return result

def getRank(_row):
    print("user.py - getRank")
    user = ws.cell(_row, c_name).value
    rank = ranking()

    result = int(rank.index(user)/2)+1

    return result

...

내림차순 정렬에 엄청난 시간을 들였다...

더보기

간단히 설명하면 Dictionary로 이름(key), 레벨(value)를 저장하고

레벨(value)에 따라 내림차순 정렬한다

이때 저장되는 건 tuple로 된 list이다

tuple된건 써먹기 귀찮기 때문에 이걸 list로 다시 바꾸어준다

이걸 출력하면 이렇게 나온다

이걸 반환값으로 넣고 getRank함수에서 user의 index를 찾아 순위로 만들고 최종적으로 반환한다

 

이제 정보, 내정보 함수의 embed에 순위만 추가하면 된다

#main.py
...
@bot.command()
async def 내정보(ctx):
    userExistance, userRow = checkUser(ctx.author.name, ctx.author.id)

    if not userExistance:
        await ctx.send("회원가입 후 자신의 정보를 확인할 수 있습니다.")
    else:
        level, exp, money, loss = userInfo(userRow)
        rank = getRank(userRow)
        userNum = checkUserNum()
        embed = discord.Embed(title="유저 정보", description = ctx.author.name, color = 0x62D0F6)
        embed.add_field(name = "레벨", value = level)
        embed.add_field(name = "경험치", value = str(exp) + "/" + str(level*level + 6*level))
        embed.add_field(name = "순위", value = str(rank) + "/" + str(userNum))
        embed.add_field(name = "보유 자산", value = money, inline = False)
        embed.add_field(name = "도박으로 날린 돈", value = loss, inline = False)

        await ctx.send(embed=embed)

@bot.command()
async def 정보(ctx, user: discord.User):
    userExistance, userRow = checkUser(user.name, user.id)

    if not userExistance:
        await ctx.send(user.name  + " 은(는) 등록되지 않은 사용자입니다.")
    else:
        level, exp, money, loss = userInfo(userRow)
        rank = getRank(userRow)
        userNum = chekcUserNum()
        embed = discord.Embed(title="유저 정보", description = user.name, color = 0x62D0F6)
        embed.add_field(name = "레벨", value = level)
        embed.add_field(name = "경험치", value = str(exp) + "/" + str(level*level + 6*level))
        embed.add_field(name = "순위", value = str(rank) + "/" + str(userNum))
        embed.add_field(name = "보유 자산", value = money, inline = False)
        embed.add_field(name = "도박으로 날린 돈", value = loss, inline = False)

        await ctx.send(embed=embed)
...

위의 두 부분만 추가하면 된다

 

내정보를 통해 이름, 레벨, 경험치, 순위, 보유자산, 도박으로 날린 돈까지 볼 수 있게 되었다

 

이제 마지막으로 랭킹을 집계해보자

#main.py
...
@bot.command()
async def 랭킹(ctx):
    rank = ranking()
    embed = discord.Embed(title = "레벨 랭킹", description = None, color = 0x4A44FF)

    for i in range(0,len(rank)):
        if i%2 == 0:
            name = rank[i]
            lvl = rank[i+1]
            embed.add_field(name = str(int(i/2+1))+"위 "+name, value ="레벨: "+str(lvl), inline=False)

    await ctx.send(embed=embed) 
...

랭킹을 만드는건 그렇게 어렵지 않았다

ranking함수로 list를 받아와서 반복문으로 돌리면 되니...

더보기

얘도 간단히 설명하면

이 list에서 2로 나누어 떨어지는 index, 즉 이름이 들어가 있는 위치를 찾아서

name에 그 이름을, lvl에는 그 이름 바로 뒤에 있는 레벨을 할당하고

embed.add_field를 해주면 끝!

 

다음에는 묵혀두었던 음악봇을 다시 만들어 보자