mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-26 01:23:13 +01:00
Merge pull request #19 from ZeruLight/feature/achievements
feature/achievements
This commit is contained in:
@@ -1,18 +1,18 @@
|
|||||||
package mhfpacket
|
package mhfpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"erupe-ce/network/clientctx"
|
|
||||||
"erupe-ce/network"
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/network"
|
||||||
|
"erupe-ce/network/clientctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfGetAchievement represents the MSG_MHF_GET_ACHIEVEMENT
|
// MsgMhfGetAchievement represents the MSG_MHF_GET_ACHIEVEMENT
|
||||||
type MsgMhfGetAchievement struct{
|
type MsgMhfGetAchievement struct {
|
||||||
AckHandle uint32
|
AckHandle uint32
|
||||||
Unk0 uint32 // id?
|
CharID uint32
|
||||||
Unk1 uint32 // char?
|
Unk1 uint32 // char?
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
@@ -22,8 +22,8 @@ func (m *MsgMhfGetAchievement) Opcode() network.PacketID {
|
|||||||
|
|
||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfGetAchievement) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfGetAchievement) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
m.Unk0 = bf.ReadUint32()
|
m.CharID = bf.ReadUint32()
|
||||||
m.Unk1 = bf.ReadUint32()
|
m.Unk1 = bf.ReadUint32()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
41
patch-schema/achievements.sql
Normal file
41
patch-schema/achievements.sql
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.achievements
|
||||||
|
(
|
||||||
|
id int NOT NULL PRIMARY KEY ,
|
||||||
|
ach0 int DEFAULT 0,
|
||||||
|
ach1 int DEFAULT 0,
|
||||||
|
ach2 int DEFAULT 0,
|
||||||
|
ach3 int DEFAULT 0,
|
||||||
|
ach4 int DEFAULT 0,
|
||||||
|
ach5 int DEFAULT 0,
|
||||||
|
ach6 int DEFAULT 0,
|
||||||
|
ach7 int DEFAULT 0,
|
||||||
|
ach8 int DEFAULT 0,
|
||||||
|
ach9 int DEFAULT 0,
|
||||||
|
ach10 int DEFAULT 0,
|
||||||
|
ach11 int DEFAULT 0,
|
||||||
|
ach12 int DEFAULT 0,
|
||||||
|
ach13 int DEFAULT 0,
|
||||||
|
ach14 int DEFAULT 0,
|
||||||
|
ach15 int DEFAULT 0,
|
||||||
|
ach16 int DEFAULT 0,
|
||||||
|
ach17 int DEFAULT 0,
|
||||||
|
ach18 int DEFAULT 0,
|
||||||
|
ach19 int DEFAULT 0,
|
||||||
|
ach20 int DEFAULT 0,
|
||||||
|
ach21 int DEFAULT 0,
|
||||||
|
ach22 int DEFAULT 0,
|
||||||
|
ach23 int DEFAULT 0,
|
||||||
|
ach24 int DEFAULT 0,
|
||||||
|
ach25 int DEFAULT 0,
|
||||||
|
ach26 int DEFAULT 0,
|
||||||
|
ach27 int DEFAULT 0,
|
||||||
|
ach28 int DEFAULT 0,
|
||||||
|
ach29 int DEFAULT 0,
|
||||||
|
ach30 int DEFAULT 0,
|
||||||
|
ach31 int DEFAULT 0,
|
||||||
|
ach32 int DEFAULT 0
|
||||||
|
);
|
||||||
|
|
||||||
|
END;
|
||||||
@@ -3,86 +3,143 @@ package channelserver
|
|||||||
import (
|
import (
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
"erupe-ce/network/mhfpacket"
|
"erupe-ce/network/mhfpacket"
|
||||||
|
"fmt"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var achievementCurves = [][]int32{
|
||||||
|
// 0: HR weapon use, Class use, Tore dailies
|
||||||
|
{5, 15, 30, 50, 100, 150, 200, 300},
|
||||||
|
// 1: Weapon collector, G wep enhances
|
||||||
|
{1, 5, 10, 15, 30, 50, 75, 100},
|
||||||
|
// 2: Festa wins
|
||||||
|
{1, 2, 3, 4, 5, 6, 7, 8},
|
||||||
|
// 3: GR weapon use, Sigil crafts
|
||||||
|
{10, 50, 100, 200, 350, 500, 750, 999},
|
||||||
|
}
|
||||||
|
|
||||||
|
var achievementCurveMap = map[uint8][]int32{
|
||||||
|
0: achievementCurves[0], 1: achievementCurves[0], 2: achievementCurves[0], 3: achievementCurves[0],
|
||||||
|
4: achievementCurves[0], 5: achievementCurves[0], 6: achievementCurves[0], 7: achievementCurves[1],
|
||||||
|
8: achievementCurves[2], 9: achievementCurves[0], 10: achievementCurves[0], 11: achievementCurves[0],
|
||||||
|
12: achievementCurves[0], 13: achievementCurves[0], 14: achievementCurves[0], 15: achievementCurves[0],
|
||||||
|
16: achievementCurves[3], 17: achievementCurves[3], 18: achievementCurves[3], 19: achievementCurves[3],
|
||||||
|
20: achievementCurves[3], 21: achievementCurves[3], 22: achievementCurves[3], 23: achievementCurves[3],
|
||||||
|
24: achievementCurves[3], 25: achievementCurves[3], 26: achievementCurves[3], 27: achievementCurves[1],
|
||||||
|
28: achievementCurves[1], 29: achievementCurves[3], 30: achievementCurves[3], 31: achievementCurves[3],
|
||||||
|
32: achievementCurves[3],
|
||||||
|
}
|
||||||
|
|
||||||
|
type Achievement struct {
|
||||||
|
Level uint8
|
||||||
|
Value uint32
|
||||||
|
NextValue uint16
|
||||||
|
Required uint32
|
||||||
|
Updated bool
|
||||||
|
Progress uint32
|
||||||
|
Trophy uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAchData(id uint8, score int32) Achievement {
|
||||||
|
curve := achievementCurveMap[id]
|
||||||
|
var ach Achievement
|
||||||
|
for i, v := range curve {
|
||||||
|
temp := score - v
|
||||||
|
if temp < 0 {
|
||||||
|
ach.Progress = uint32(score)
|
||||||
|
ach.Required = uint32(curve[i])
|
||||||
|
switch ach.Level {
|
||||||
|
case 0:
|
||||||
|
ach.NextValue = 5
|
||||||
|
case 1, 2, 3:
|
||||||
|
ach.NextValue = 10
|
||||||
|
case 4, 5:
|
||||||
|
ach.NextValue = 15
|
||||||
|
case 6:
|
||||||
|
ach.NextValue = 15
|
||||||
|
ach.Trophy = 0x40
|
||||||
|
case 7:
|
||||||
|
ach.NextValue = 20
|
||||||
|
ach.Trophy = 0x60
|
||||||
|
}
|
||||||
|
return ach
|
||||||
|
} else {
|
||||||
|
score = temp
|
||||||
|
ach.Level++
|
||||||
|
switch ach.Level {
|
||||||
|
case 1:
|
||||||
|
ach.Value += 5
|
||||||
|
case 2, 3, 4:
|
||||||
|
ach.Value += 10
|
||||||
|
case 5, 6, 7:
|
||||||
|
ach.Value += 15
|
||||||
|
case 8:
|
||||||
|
ach.Value += 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ach.Required = uint32(curve[7])
|
||||||
|
ach.Trophy = 0x7F
|
||||||
|
ach.Progress = ach.Required
|
||||||
|
return ach
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgMhfGetAchievement(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfGetAchievement(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfGetAchievement)
|
pkt := p.(*mhfpacket.MsgMhfGetAchievement)
|
||||||
|
|
||||||
achievementStruct := []struct {
|
row := s.server.db.QueryRow("SELECT id FROM achievements WHERE id=$1", pkt.CharID)
|
||||||
ID uint8 // Main ID
|
if row != nil {
|
||||||
Unk0 uint8 // always FF
|
s.server.db.Exec("INSERT INTO achievements (id) VALUES ($1)", pkt.CharID)
|
||||||
Unk1 uint16 // 0x05 0x00
|
|
||||||
Unk2 uint32 // 0x01 0x0A 0x05 0x00
|
|
||||||
}{
|
|
||||||
{ID: 0, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 1, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 2, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 3, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 4, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 5, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 6, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 7, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 8, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 9, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 10, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 11, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 12, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 13, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 14, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 15, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 16, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 17, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 18, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 19, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 20, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 21, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 22, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 23, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 24, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 25, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 26, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 27, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 28, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 29, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 30, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 31, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 32, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 33, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 34, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 35, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 36, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 37, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 38, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 39, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 40, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 41, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 42, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 43, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 44, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 45, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 46, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 47, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 48, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 49, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 50, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 51, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 52, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 53, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 54, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 55, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 56, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 57, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 58, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 59, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var scores [33]int32
|
||||||
|
row = s.server.db.QueryRow("SELECT * FROM achievements WHERE id=$1", pkt.CharID)
|
||||||
|
if row != nil {
|
||||||
|
err := row.Scan(&scores[0], &scores[0],
|
||||||
|
&scores[1], &scores[2], &scores[3], &scores[4], &scores[5], &scores[6], &scores[7], &scores[8], &scores[9],
|
||||||
|
&scores[10], &scores[11], &scores[12], &scores[13], &scores[14], &scores[15], &scores[16], &scores[17],
|
||||||
|
&scores[18], &scores[19], &scores[20], &scores[21], &scores[22], &scores[23], &scores[24], &scores[25],
|
||||||
|
&scores[26], &scores[27], &scores[28], &scores[29], &scores[30], &scores[31], &scores[32])
|
||||||
|
if err != nil {
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 20))
|
||||||
|
s.logger.Error("ERR@", zap.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resp := byteframe.NewByteFrame()
|
resp := byteframe.NewByteFrame()
|
||||||
resp.WriteUint8(uint8(len(achievementStruct))) // Entry count
|
var points uint32
|
||||||
for _, entry := range achievementStruct {
|
resp.WriteBytes(make([]byte, 16))
|
||||||
resp.WriteUint8(entry.ID)
|
resp.WriteBytes([]byte{0x02, 0x00, 0x00}) // Unk
|
||||||
resp.WriteUint8(entry.Unk0)
|
|
||||||
resp.WriteUint16(entry.Unk1)
|
var id uint8
|
||||||
resp.WriteUint32(entry.Unk2)
|
entries := uint8(33)
|
||||||
|
resp.WriteUint8(entries) // Entry count
|
||||||
|
for id = 0; id < entries; id++ {
|
||||||
|
achData := GetAchData(id, scores[id])
|
||||||
|
points += achData.Value
|
||||||
|
resp.WriteUint8(id)
|
||||||
|
resp.WriteUint8(achData.Level)
|
||||||
|
resp.WriteUint16(achData.NextValue)
|
||||||
|
resp.WriteUint32(achData.Required)
|
||||||
|
resp.WriteBool(false) // TODO: Notify on rank increase since last checked, see MhfDisplayedAchievement
|
||||||
|
resp.WriteUint8(achData.Trophy)
|
||||||
|
/* Trophy bitfield
|
||||||
|
0000 0000
|
||||||
|
abcd efgh
|
||||||
|
B - Bronze (0x40)
|
||||||
|
B-C - Silver (0x60)
|
||||||
|
B-H - Gold (0x7F)
|
||||||
|
*/
|
||||||
|
resp.WriteUint16(0) // Unk
|
||||||
|
resp.WriteUint32(achData.Progress)
|
||||||
}
|
}
|
||||||
|
resp.Seek(0, io.SeekStart)
|
||||||
|
resp.WriteUint32(points)
|
||||||
|
resp.WriteUint32(points)
|
||||||
|
resp.WriteUint32(points)
|
||||||
|
resp.WriteUint32(points)
|
||||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,11 +150,16 @@ func handleMsgMhfSetCaAchievementHist(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
func handleMsgMhfResetAchievement(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfResetAchievement(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
func handleMsgMhfAddAchievement(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfAddAchievement(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfAddAchievement)
|
||||||
|
s.server.db.Exec(fmt.Sprintf("UPDATE achievements SET ach%d=ach%d+1 WHERE id=$1", pkt.AchievementID, pkt.AchievementID), s.charID)
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgMhfPaymentAchievement(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfPaymentAchievement(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
func handleMsgMhfDisplayedAchievement(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfDisplayedAchievement(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
// This is how you would figure out if the rank-up notification needs to occur
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgMhfGetCaAchievementHist(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfGetCaAchievementHist(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user