-
Notifications
You must be signed in to change notification settings - Fork 46
/
main.go
289 lines (272 loc) · 7.39 KB
/
main.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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
// Package gamesystem 基于zbp的游戏插件
package gamesystem
import (
"encoding/json"
"errors"
"image"
"os"
"strings"
"sync"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/floatbox/math"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/extension"
"github.com/wdvxdr1123/ZeroBot/message"
// 图片输出
"github.com/FloatTech/gg"
"github.com/FloatTech/imgfactory"
"github.com/FloatTech/rendercard"
"github.com/FloatTech/zbputils/img/text"
)
const (
serviceErr = "[gamesystem]error:"
kanbanpath = "data/Control/kanban.png"
)
// GameInfo ...
type GameInfo struct {
Command string `json:"游玩指令"` // 游玩指令
Help string `json:"游戏说明"` // 游戏说明
Rewards string `json:"奖励说明"` // 奖励说明
}
// GameStatus ...
type GameStatus struct {
Name string `json:"游戏名称"`
Info *GameInfo `json:"游戏介绍"`
Sales map[int64]bool `json:"上架情况"`
Rooms []int64 `json:"房间列表"`
}
var (
// 插件主体
gamesystem = control.Register("gamesystem", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "游戏系统",
Help: "- 游戏列表\n- 上架[游戏名]\n- 下架[游戏名]",
PublicDataFolder: "GameSystem",
})
// 游戏控件
cfgFile = gamesystem.DataFolder() + "gamesystem.json"
mu sync.RWMutex
gamelist = make(map[string]GameInfo, 30)
gameManager = make(map[string]*GameStatus, 30)
)
func init() {
gamesystem.OnCommandGroup([]string{"上架", "下架"}, zero.UserOrGrpAdmin).SetBlock(true).Handle(func(ctx *zero.Ctx) {
model := extension.CommandModel{}
_ = ctx.Parse(&model)
gid := ctx.Event.GroupID
if strings.Contains(model.Command, "上架") {
err := gameManager[model.Args].SalesIn(gid)
if err != nil {
ctx.SendChain(message.Text("[ERROR]:", err))
return
}
ctx.SendChain(message.Text(model.Args, "游戏已上架"))
} else {
err := gameManager[model.Args].SalesOut(gid)
if err != nil {
ctx.SendChain(message.Text("[ERROR]:", err))
return
}
ctx.SendChain(message.Text(model.Args, "游戏已下架"))
}
})
gamesystem.OnFullMatch("游戏列表").SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
skrft, err := file.GetLazyData(text.SakuraFontFile, control.Md5File, false)
if err != nil {
ctx.SendChain(message.Text("[ERROR]:", err))
return
}
gid := ctx.Event.GroupID
i := 0
var imgs []image.Image
var yOfLine1 int // 第一列最大高度
var yOfLine2 int // 第二列最大高度
for gameName, info := range gamelist {
img, err := (&rendercard.Card{
TitleFontData: skrft,
TextFontData: skrft,
Title: gameName,
CanTitleShown: true,
TitleAlign: rendercard.AlignCenter,
Text: func() []string {
var infoText []string
if !whichGamePlayIn(gameName, gid) {
infoText = append(infoText, []string{"游戏状态:", " 下架中"}...)
}
infoText = append(infoText, []string{
"游戏指令:", " " + info.Command,
"游戏说明:", strings.ReplaceAll(" "+info.Help, "\n", "\n "),
"游戏奖励:", " " + info.Rewards}...)
return infoText
}(),
IsTextSplitPerElement: true,
}).DrawTextCard()
if err != nil {
ctx.SendChain(message.Text(serviceErr, err))
return
}
if i%2 == 0 { // 第一列
yOfLine1 += img.Bounds().Max.Y + 20
} else { // 第二列
yOfLine2 += img.Bounds().Max.Y + 20
}
imgs = append(imgs, img)
i++
}
lnperpg := math.Ceil(math.Max(yOfLine1, yOfLine2), (256 + 30))
imgback, err := (&rendercard.Title{
Line: lnperpg,
LeftTitle: "游戏系统",
LeftSubtitle: "Game System",
RightTitle: "FloatTech",
RightSubtitle: "ZeroBot-Plugin",
TitleFontData: skrft,
TextFontData: skrft,
ImagePath: kanbanpath,
}).DrawTitle()
if err != nil {
ctx.SendChain(message.Text(serviceErr, err))
return
}
yOfLine := []int{0, 0}
canvas := gg.NewContextForImage(imgback)
// 插入游戏列表卡片
for i, img := range imgs {
canvas.DrawImage(img, 25+620*(i%2), 360+yOfLine[i%2])
yOfLine[i%2] += img.Bounds().Max.Y + 20
}
data, err := imgfactory.ToBytes(canvas.Image())
if err != nil {
ctx.SendChain(message.Text(serviceErr, err))
return
}
if id := ctx.SendChain(message.ImageBytes(data)); id.ID() == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
})
}
// 载入游戏状态
func loadConfig(cfgFile string) error {
if file.IsExist(cfgFile) {
reader, err := os.Open(cfgFile)
if err == nil {
err = json.NewDecoder(reader).Decode(&gameManager)
}
if err != nil {
return err
}
return reader.Close()
}
return saveConfig(cfgFile)
}
// 保存游戏状态
func saveConfig(cfgFile string) error {
reader, err := os.Create(cfgFile)
if err == nil {
err = json.NewEncoder(reader).Encode(&gameManager)
}
return err
}
// Register 注册游戏
func Register(gameName string, gameinfo *GameInfo) (en *control.Engine, manager *GameStatus, err error) {
if len(gameManager) == 0 {
err = loadConfig(cfgFile)
if err != nil {
return
}
}
mu.Lock()
defer mu.Unlock()
status, ok := gameManager[gameName]
if !ok {
gameManager[gameName] = &GameStatus{
Name: gameName,
Info: gameinfo,
Sales: make(map[int64]bool),
Rooms: make([]int64, 0),
}
gamelist[gameName] = *gameinfo
err = saveConfig(cfgFile)
return gamesystem, gameManager[gameName], err
}
gamelist[gameName] = *status.Info
return gamesystem, status, nil
}
// 游戏上架情况
func whichGamePlayIn(gameName string, groupID int64) bool {
if len(gameManager) == 0 {
err := loadConfig(cfgFile)
if err != nil {
panic(err)
}
}
mu.RLock()
status, ok := gameManager[gameName]
mu.RUnlock()
if ok {
return status.PlayIn(groupID)
}
return false
}
// PlayIn 判断游戏是否上架
func (gameManager *GameStatus) PlayIn(groupID int64) bool {
mu.Lock()
defer mu.Unlock()
sales, ok := gameManager.Sales[groupID]
if !ok {
gameManager.Sales[groupID] = true
sales = true
err := saveConfig(cfgFile)
if err != nil {
logrus.Debugln("[gamesystem]ERROR:", err)
}
}
return sales
}
// SalesIn 上架游戏
func (gameManager *GameStatus) SalesIn(groupID int64) error {
mu.Lock()
defer mu.Unlock()
gameManager.Sales[groupID] = true
return saveConfig(cfgFile)
}
// SalesOut 下架游戏
func (gameManager *GameStatus) SalesOut(groupID int64) error {
mu.Lock()
defer mu.Unlock()
gameManager.Sales[groupID] = false
return saveConfig(cfgFile)
}
// CreateRoom 创建房间
func (gameManager *GameStatus) CreateRoom(groupID int64) error {
if !gameManager.PlayIn(groupID) {
return errors.New("游戏已下架,无法游玩")
}
mu.Lock()
defer mu.Unlock()
for _, gid := range gameManager.Rooms {
if gid == groupID {
return errors.New("游戏已创建房间,请等待结束后重试")
}
}
// 创建房间
gameManager.Rooms = append(gameManager.Rooms, groupID)
return nil
}
// CloseRoom 关闭房间
func (gameManager *GameStatus) CloseRoom(groupID int64) {
mu.Lock()
defer mu.Unlock()
index := 0
for i, gid := range gameManager.Rooms {
if gid == groupID {
index = i
}
}
gameManager.Rooms = append(gameManager.Rooms[:index], gameManager.Rooms[index+1:]...)
}