/
buildtemplate.go
135 lines (115 loc) · 3.58 KB
/
buildtemplate.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package chatgen
import (
"encoding/base64"
)
// IDs for all professions
const (
BuildTemplateProfessionGuardian = iota + 1
BuildTemplateProfessionWarrior
BuildTemplateProfessionEngineer
BuildTemplateProfessionRanger
BuildTemplateProfessionThief
BuildTemplateProfessionElementalist
BuildTemplateProfessionMesmer
BuildTemplateProfessionNecromancer
BuildTemplateProfessionRevenant
)
// Revenant Legends
const (
RevenantLegendDragonStance = iota + 14
RevenantLegendAssassinStance
RevenantLegendDwarfStance
RevenantLegendDemonStance
RevenantLegendRenegadeStance
RevenantLegendCentaurStance
)
// BuildTemplate contains data contained in a buildtemplate chatlink
type BuildTemplate struct {
Profession uint8
Specializations [3]BuildTemplateSpecialization
Skills [5]int
AquaticSkills [5]int
RangerPets [4]uint8 // Terrestrial Pet 1, Terrestrial Pet 2, Aquatic Pet 1, Aquatic Pet 2
RevenantLegends [4]uint8 // Terrestrial Legend 1, Terrestrial Legend 2, Aquatic Legend 1, Aquatic Legend 2
RevenantInactiveLegendSkills [6]int // Terrestrial Skill 1, 2, 3, Aquatic Skill 1, 2, 3
}
// BuildTemplateSpecialization contains information about one traitline
type BuildTemplateSpecialization struct {
ID int
Traits [3]int
}
// EncodeBuildTemplate encodes a buildtemplate into a chatlink
func EncodeBuildTemplate(data BuildTemplate) string {
bytes := []byte{headerByteBuildTemplate, byte(data.Profession)}
// Specializations
// 2 bytes per spec, the first contains the id
// the second contains 2 bits for each trait choice in reverse order
for _, spec := range data.Specializations {
bytes = append(bytes, byte(spec.ID))
var b byte
for i, t := range spec.Traits {
b += byte(t << (2 * i))
}
bytes = append(bytes, b)
}
// Skills
// 2 bytes per skill, terrestrial and aquatic skill alternating
for i := range data.Skills {
for j := 0; j < 2; j++ {
bytes = append(bytes, byte(data.Skills[i]>>(8*j)))
}
for j := 0; j < 2; j++ {
bytes = append(bytes, byte(data.AquaticSkills[i]>>(8*j)))
}
}
// Special profession data
if data.Profession == BuildTemplateProfessionRanger {
// Ranger pets as single byte
for _, p := range data.RangerPets {
bytes = append(bytes, byte(p))
}
// 12 bytes unused
for i := 0; i < 12; i++ {
bytes = append(bytes, 0)
}
} else if data.Profession == BuildTemplateProfessionRevenant {
// Revenant legends as single byte
for _, l := range data.RevenantLegends {
bytes = append(bytes, byte(l))
}
// Revenant inactive legend skills as 2 bytes
// 3 Terrestrial and 3 aquatic in this order
for _, skill := range data.RevenantInactiveLegendSkills {
for i := 0; i < 2; i++ {
bytes = append(bytes, byte(skill>>(8*i)))
}
}
} else {
// 16 bytes unused for all other professions
for i := 0; i < 16; i++ {
bytes = append(bytes, 0)
}
}
return addLinkEnclosing(base64.StdEncoding.EncodeToString(bytes))
}
// DecodeBuildTemplate decodes a chatlink into the contained buildtemplate
func DecodeBuildTemplate(link string) (*BuildTemplate, error) {
bytes, err := base64.StdEncoding.DecodeString(removeLinkEnclosing(link))
if err != nil {
return nil, err
}
if len(bytes) < 44 || bytes[0] != headerByteBuildTemplate {
return nil, ErrInvalidHeader
}
data := &BuildTemplate{
Profession: uint8(bytes[1]),
}
for i := 0; i < 3; i++ {
data.Specializations[i].ID = int(bytes[(2*i)+3])
for j := 0; j < 3; j++ {
data.Specializations[i].Traits[j] = int(bytes[(2*i)+4]) & (3 << (j * 2))
}
}
// TODO: Skills
return data, nil
}