From b7e21fc111d7adbee4de92e5bac09458c854e72c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B9=E6=9F=B3=E7=85=9C?= <101934327+fangliuyu@users.noreply.github.com> Date: Sun, 3 Sep 2023 14:09:37 +0800 Subject: [PATCH] =?UTF-8?q?[=E6=96=B0=E5=A2=9E=E6=8F=92=E4=BB=B6]=E9=92=93?= =?UTF-8?q?=E9=B1=BC=E6=A8=A1=E6=8B=9F=E5=99=A8=20(#721)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add files via upload * Update README.md * Add files via upload * Add files via upload * Update pole.go * Update pole.go * Update main.go * Add files via upload * Add files via upload * Update main.go * Update pack.go * Add files via upload --- README.md | 17 ++ plugin/mcfish/fish.go | 329 +++++++++++++++++++++++ plugin/mcfish/main.go | 586 ++++++++++++++++++++++++++++++++++++++++ plugin/mcfish/pack.go | 454 +++++++++++++++++++++++++++++++ plugin/mcfish/pole.go | 485 +++++++++++++++++++++++++++++++++ plugin/mcfish/store.go | 593 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 2464 insertions(+) create mode 100644 plugin/mcfish/fish.go create mode 100644 plugin/mcfish/main.go create mode 100644 plugin/mcfish/pack.go create mode 100644 plugin/mcfish/pole.go create mode 100644 plugin/mcfish/store.go diff --git a/README.md b/README.md index 1c788dccb3..f015babcf2 100644 --- a/README.md +++ b/README.md @@ -959,6 +959,23 @@ print("run[CQ:image,file="+j["img"]+"]") - [x] 吟唱提示[xxxx] + +
+ 钓鱼模拟器 + + `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/mcfish"` + + - [x] 钓鱼商店 + - [x] 购买xxx [数量] + - [x] 出售xxx [数量] + - [x] 钓鱼背包 + - [x] 装备[xx竿|三叉戟|美西螈] + - [x] 附魔[诱钓|海之眷顾] + - [x] 修复鱼竿 + - [x] 合成[xx竿|三叉戟] + - [x] 进行钓鱼 + - [x] 进行n次钓鱼 +
简易midi音乐制作 diff --git a/plugin/mcfish/fish.go b/plugin/mcfish/fish.go new file mode 100644 index 0000000000..4045b3eea6 --- /dev/null +++ b/plugin/mcfish/fish.go @@ -0,0 +1,329 @@ +// Package mcfish 钓鱼模拟器 +package mcfish + +import ( + "math/rand" + "strconv" + "strings" + "time" + + "github.com/FloatTech/AnimeAPI/wallet" + "github.com/FloatTech/zbputils/ctxext" + "github.com/sirupsen/logrus" + zero "github.com/wdvxdr1123/ZeroBot" + "github.com/wdvxdr1123/ZeroBot/message" +) + +func init() { + engine.OnRegex(`^进行(([1-5]\d|[1-9])次)?钓鱼$`, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + uid := ctx.Event.UserID + fishNumber := 1 + info := ctx.State["regex_matched"].([]string)[2] + if info != "" { + number, err := strconv.Atoi(info) + if err != nil || number > FishLimit { + ctx.SendChain(message.Text("请输入正确的次数")) + return + } + fishNumber = number + } + equipInfo, err := dbdata.getUserEquip(uid) + if err != nil { + ctx.SendChain(message.Text("[ERROR at fish.go.2]:", err)) + return + } + if equipInfo == (equip{}) { + ok, err := dbdata.checkEquipFor(uid) + if err != nil { + ctx.SendChain(message.Text("[ERROR at fish.go.2.1]:", err)) + return + } + if !ok { + ctx.SendChain(message.At(uid), message.Text("请装备鱼竿后钓鱼", err)) + return + } + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("你尚未装备鱼竿,是否花费100购买鱼竿?\n回答\"是\"或\"否\"")) + // 等待用户下一步选择 + recv, cancel := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^(是|否)$`), zero.CheckUser(ctx.Event.UserID)).Repeat() + defer cancel() + buy := false + for { + select { + case <-time.After(time.Second * 120): + ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("等待超时,取消钓鱼"))) + return + case e := <-recv: + nextcmd := e.Event.Message.String() + if nextcmd == "否" { + ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("已取消购买"))) + return + } + money := wallet.GetWalletOf(uid) + if money < 100 { + ctx.SendChain(message.Text("你钱包当前只有", money, "ATRI币,无法完成支付")) + return + } + err = wallet.InsertWalletOf(uid, -100) + if err != nil { + ctx.SendChain(message.Text("[ERROR at fish.go.3]:", err)) + return + } + equipInfo = equip{ + ID: uid, + Equip: "木竿", + Durable: 30, + } + err = dbdata.updateUserEquip(equipInfo) + if err != nil { + ctx.SendChain(message.Text("[ERROR at fish.go.4]:", err)) + return + } + err = dbdata.setEquipFor(uid) + if err != nil { + ctx.SendChain(message.Text("[ERROR at fish.go.4]:", err)) + return + } + buy = true + } + if buy { + break + } + } + } + if equipInfo.Durable < fishNumber { + fishNumber = equipInfo.Durable + } + residue, err := dbdata.updateFishInfo(uid, fishNumber) + if err != nil { + ctx.SendChain(message.Text("[ERROR at fish.go.1]:", err)) + return + } + if residue == 0 { + ctx.SendChain(message.Text("今天你已经进行", FishLimit, "次钓鱼了.\n游戏虽好,但请不要沉迷。")) + return + } + fishNumber = residue + msg := "" + if equipInfo.Equip != "美西螈" { + equipInfo.Durable -= fishNumber + err = dbdata.updateUserEquip(equipInfo) + if err != nil { + ctx.SendChain(message.Text("[ERROR at fish.go.5]:", err)) + return + } + if equipInfo.Durable < 10 || equipInfo.Durable > 0 { + msg = "(你的鱼竿耐久仅剩" + strconv.Itoa(equipInfo.Durable) + ")" + } else if equipInfo.Durable <= 0 { + msg = "(你的鱼竿耐已销毁)" + } + } else { + fishNmaes, err := dbdata.pickFishFor(uid, fishNumber) + if err != nil { + ctx.SendChain(message.Text("[ERROR at fish.go.5.1]:", err)) + return + } + if len(fishNmaes) == 0 { + equipInfo.Durable = 0 + err = dbdata.updateUserEquip(equipInfo) + if err != nil { + ctx.SendChain(message.Text("[ERROR at fish.go.5]:", err)) + } + ctx.SendChain(message.Text("美西螈因为没吃到鱼,钓鱼时一直没回来,你失去了美西螈")) + return + } + msg = "(美西螈吃掉了" + fishNumber = 0 + for name, number := range fishNmaes { + fishNumber += number + msg += strconv.Itoa(number) + name + "、" + } + msg += ")" + } + waitTime := 120 / (equipInfo.Induce + 1) + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("你开始去钓鱼了,请耐心等待鱼上钩(预计要", time.Second*time.Duration(waitTime), ")")) + timer := time.NewTimer(time.Second * time.Duration(rand.Intn(waitTime)+1)) + for { + <-timer.C + timer.Stop() + break + } + // 钓到鱼的范围 + number, err := dbdata.getNumberFor(uid, "鱼") + if err != nil { + ctx.SendChain(message.Text("[ERROR at fish.go.5.1]:", err)) + return + } + if number > 100 || equipInfo.Equip == "美西螈" { //放大概率 + probabilities["treasure"] = probabilityLimit{ + Min: 0, + Max: 2, + } + probabilities["pole"] = probabilityLimit{ + Min: 2, + Max: 10, + } + probabilities["fish"] = probabilityLimit{ + Min: 10, + Max: 45, + } + probabilities["waste"] = probabilityLimit{ + Min: 45, + Max: 90, + } + } + for name, info := range probabilities { + switch name { + case "treasure": + info.Max += equipInfo.Favor + probabilities[name] = info + case "pole": + info.Min += equipInfo.Favor + info.Max += equipInfo.Favor * 2 + probabilities[name] = info + case "fish": + info.Min += equipInfo.Favor * 2 + info.Max += equipInfo.Favor * 3 + probabilities[name] = info + case "waste": + info.Min += equipInfo.Favor * 3 + probabilities[name] = info + } + } + // 钓鱼结算 + picName := "" + thingNameList := make(map[string]int) + for i := fishNumber; i > 0; i-- { + thingName := "" + typeOfThing := "" + number := 1 + dice := rand.Intn(100) + switch { + case dice <= probabilities["waste"].Min && dice < probabilities["waste"].Max: // 垃圾 + typeOfThing = "waste" + thingName = wasteList[rand.Intn(len(wasteList))] + picName = thingName + case dice <= probabilities["treasure"].Min && dice < probabilities["treasure"].Max: // 宝藏 + dice = rand.Intn(100) + switch { + case dice <= probabilities["美西螈"].Min && dice < probabilities["美西螈"].Max: + typeOfThing = "pole" + picName = "美西螈" + thingName = "美西螈" + case dice <= probabilities["唱片"].Min && dice < probabilities["唱片"].Max: + typeOfThing = "article" + picName = "唱片" + thingName = "唱片" + case dice <= probabilities["海之眷顾"].Min && dice < probabilities["海之眷顾"].Max: + typeOfThing = "article" + picName = "book" + thingName = "海之眷顾" + default: + typeOfThing = "article" + picName = "book" + thingName = "诱钓" + } + case dice <= probabilities["pole"].Min && dice < probabilities["pole"].Max: // 宝藏 + typeOfThing = "pole" + dice := rand.Intn(100) + switch { + case dice <= probabilities["铁竿"].Min && dice < probabilities["铁竿"].Max: + thingName = "铁竿" + case dice <= probabilities["金竿"].Min && dice < probabilities["金竿"].Max: + thingName = "金竿" + case dice <= probabilities["钻石竿"].Min && dice < probabilities["钻石竿"].Max: + thingName = "钻石竿" + case dice <= probabilities["下界合金竿竿竿"].Min && dice < probabilities["下界合金竿竿竿"].Max: + thingName = "下界合金竿竿竿" + default: + thingName = "木竿" + } + picName = thingName + case dice <= probabilities["fish"].Min && dice < probabilities["fish"].Max: + typeOfThing = "fish" + dice = rand.Intn(100) + switch { + case dice <= probabilities["墨鱼"].Min && dice < probabilities["墨鱼"].Max: + thingName = "墨鱼" + case dice <= probabilities["鳕鱼"].Min && dice < probabilities["鳕鱼"].Max: + thingName = "鳕鱼" + case dice <= probabilities["鲑鱼"].Min && dice < probabilities["鲑鱼"].Max: + thingName = "鲑鱼" + case dice <= probabilities["热带鱼"].Min && dice < probabilities["热带鱼"].Max: + thingName = "热带鱼" + case dice <= probabilities["河豚"].Min && dice < probabilities["河豚"].Max: + thingName = "河豚" + default: + thingName = "鹦鹉螺" + } + picName = thingName + default: + thingNameList["赛博空气"]++ + } + if thingName != "" { + newThing := article{} + if strings.Contains(thingName, "竿") { + info := strconv.Itoa(rand.Intn(discountList[thingName])+1) + + "/" + strconv.Itoa(rand.Intn(10)) + "/" + + strconv.Itoa(rand.Intn(3)) + "/" + strconv.Itoa(rand.Intn(2)) + newThing = article{ + Duration: time.Now().Unix()*100 + int64(i), + Type: typeOfThing, + Name: thingName, + Number: number, + Other: info, + } + } else { + thingInfo, err := dbdata.getUserThingInfo(uid, thingName) + if err != nil { + ctx.SendChain(message.Text("[ERROR at fish.go.6]:", err)) + return + } + if len(thingInfo) == 0 { + newThing = article{ + Duration: time.Now().Unix()*100 + int64(i), + Type: typeOfThing, + Name: thingName, + } + } else { + newThing = thingInfo[0] + } + if equipInfo.Equip == "美西螈" && thingName != "美西螈" { + number += 2 + } + newThing.Number += number + } + err = dbdata.updateUserThingInfo(uid, newThing) + if err != nil { + ctx.SendChain(message.Text("[ERROR at fish.go.7]:", err)) + return + } + thingNameList[thingName] += number + } + } + if len(thingNameList) == 1 { + thingName := "" + for name := range thingNameList { + thingName = name + } + if picName != "" { + pic, err := engine.GetLazyData(picName+".png", false) + if err != nil { + logrus.Warnln("[mcfish]error:", err) + ctx.SendChain(message.At(uid), message.Text("恭喜你钓到了", thingName, "\n", msg)) + return + } + ctx.SendChain(message.At(uid), message.Text("恭喜你钓到了", thingName, "\n", msg), message.ImageBytes(pic)) + return + } + ctx.SendChain(message.At(uid), message.Text("恭喜你钓到了", thingName, "\n", msg)) + return + } + msgInfo := make(message.Message, 0, 3+len(thingNameList)) + msgInfo = append(msgInfo, message.Reply(ctx.Event.MessageID), message.Text("你进行了", fishNumber, "次钓鱼,结果如下:\n")) + for name, number := range thingNameList { + msgInfo = append(msgInfo, message.Text(name, " : ", number, "\n")) + } + msgInfo = append(msgInfo, message.Text(msg)) + ctx.Send(msgInfo) + }) +} diff --git a/plugin/mcfish/main.go b/plugin/mcfish/main.go new file mode 100644 index 0000000000..726d892ecf --- /dev/null +++ b/plugin/mcfish/main.go @@ -0,0 +1,586 @@ +// Package mcfish 钓鱼模拟器 +package mcfish + +import ( + "encoding/json" + "math/rand" + "os" + "strconv" + "sync" + "time" + + fcext "github.com/FloatTech/floatbox/ctxext" + sql "github.com/FloatTech/sqlite" + ctrl "github.com/FloatTech/zbpctrl" + "github.com/FloatTech/zbputils/control" + "github.com/FloatTech/zbputils/ctxext" + zero "github.com/wdvxdr1123/ZeroBot" + "github.com/wdvxdr1123/ZeroBot/message" +) + +type fishdb struct { + db *sql.Sqlite + sync.RWMutex +} + +// FishLimit 钓鱼次数上限 +const FishLimit = 50 + +// 各物品信息 +type jsonInfo struct { + ZoneInfo []zoneInfo `json:"分类"` // 区域概率 + ArticleInfo []articleInfo `json:"物品"` // 物品信息 +} +type zoneInfo struct { + Name string `json:"类型"` //类型 + Probability int `json:"概率[0-100)"` // 概率 +} +type articleInfo struct { + Name string `json:"名称"` //名称 + Type string `json:"类型"` // 类型 + Probability int `json:"概率[0-100),omitempty"` // 概率 + Durable int `json:"耐久上限,omitempty"` // 耐久 + Price int `json:"价格"` // 价格 +} + +type probabilityLimit struct { + Min int + Max int +} + +type equip struct { + ID int64 // 用户 + Equip string // 装备名称 + Durable int // 耐久 + Maintenance int // 维修次数 + Induce int // 诱钓等级 + Favor int // 眷顾等级 +} + +type article struct { + Duration int64 + Name string + Number int + Other string // 耐久/维修次数/诱钓/眷顾 + Type string +} + +type store struct { + Duration int64 + Name string + Number int + Price int + Other string // 耐久/维修次数/诱钓/眷顾 + Type string +} + +type fishState struct { + ID int64 + Duration int64 + Fish int + Equip int +} + +type storeDiscount struct { + Name string + Discount int +} + +var ( + articlesInfo = jsonInfo{} // 物品信息 + thingList = make([]string, 0, 100) // 竿列表 + poleList = make([]string, 0, 10) // 竿列表 + fishList = make([]string, 0, 10) // 鱼列表 + treasureList = make([]string, 0, 10) // 鱼列表 + wasteList = make([]string, 0, 10) // 垃圾列表 + probabilities = make(map[string]probabilityLimit, 50) // 概率分布 + priceList = make(map[string]int, 50) // 价格分布 + durationList = make(map[string]int, 50) // 装备耐久分布 + discountList = make(map[string]int, 50) // 价格波动信息 + enchantLevel = []string{"0", "Ⅰ", "Ⅱ", "Ⅲ"} + dbdata = &fishdb{ + db: &sql.Sqlite{}, + } +) + +var ( + engine = control.Register("mcfish", &ctrl.Options[*zero.Ctx]{ + DisableOnDefault: false, + Brief: "钓鱼", + Help: "一款钓鱼模拟器\n----------指令----------\n" + + "- 钓鱼看板/钓鱼商店\n- 购买xxx\n- 购买xxx [数量]\n- 出售xxx\n- 出售xxx [数量]\n" + + "- 钓鱼背包\n- 装备[xx竿|三叉戟|美西螈]\n- 附魔[诱钓|海之眷顾]\n- 修复鱼竿\n- 合成[xx竿|三叉戟]\n" + + "- 进行钓鱼\n- 进行n次钓鱼\n" + + "规则:\n1.每日的商店价格是波动的!!如何最大化收益自己考虑一下喔\n" + + "2.装备信息:\n-> 木竿 : 耐久上限:30 均价:100 上钩概率:0.7%\n-> 铁竿 : 耐久上限:50 均价:300 上钩概率:0.2%\n-> 金竿 : 耐久上限:70 均价700 上钩概率:0.06%\n" + + "-> 钻石竿 : 耐久上限:100 均价1500 上钩概率:0.03%\n-> 下界合金竿 : 耐久上限:150 均价3100 上钩概率:0.01%\n-> 三叉戟 : 可使钓的鱼类物品数量变成3 耐久上限:300 均价4000 只能合成和交易\n" + + "3.附魔书信息:\n-> 诱钓 : 减少上钩时间. 均价:1000, 上钩概率:0.59%\n-> 海之眷顾 : 增加宝藏上钩概率. 均价:2500, 上钩概率:0.39%\n" + + "4.稀有物品:\n-> 唱片 : 出售物品时使用该物品使价格翻倍. 均价:3000, 上钩概率:0.01%\n-> 美西螈 : 可装备,获得隐形[钓鱼佬]buff,并让钓到除鱼竿和美西螈外的物品数量变成3,无耐久上限.不可修复/附魔,每次钓鱼消耗任意一鱼类物品. 均价:3000, 上钩概率:0.01%\n" + + "5.鱼类信息:\n-> 鳕鱼 : 均价:10 上钩概率:0.69%\n-> 鲑鱼 : 均价:50 上钩概率:0.2%\n-> 热带鱼 : 均价:100 上钩概率:0.06%\n-> 河豚 : 均价:300 上钩概率:0.03%\n-> 鹦鹉螺 : 均价:500 上钩概率:0.01%\n-> 墨鱼 : 均价:500 上钩概率:0.01%\n" + + "6.垃圾:\n-> 均价:10 上钩概率:30%\n" + + "7.物品BUFF:\n-> 钓鱼佬 : 当背包名字含有'鱼'的物品数量超过100时激活,钓到物品概率提高至90%\n-> 修复大师 : 当背包鱼竿数量超过10时激活,修复物品时耐久百分百继承\n" + + "8.合成:\n-> 铁竿 : 3x木竿\n-> 金竿 : 3x铁竿\n-> 钻石竿 : 3x金竿\n-> 下界合金竿 : 3x钻石竿\n-> 三叉戟 : 3x下界合金竿\n注:合成成功率90%,继承附魔等级合/3的等级\n" + + "9.杂项:\n-> 无装备的情况下,每人最多可以购买3次100块钱的鱼竿\n-> 默认状态钓鱼上钩概率为60%(理论值!!!)\n-> 附魔的鱼竿会因附魔变得昂贵,每个附魔最高3级\n-> 三叉戟不算鱼竿", + PublicDataFolder: "McFish", + }).ApplySingle(ctxext.DefaultSingle) + getdb = fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool { + dbdata.db.DBPath = engine.DataFolder() + "fishdata.db" + err := dbdata.db.Open(time.Hour * 24) + if err != nil { + ctx.SendChain(message.Text("[ERROR at main.go.1]:", err)) + return false + } + return true + }) +) + +func init() { + //go func() { + _, err := engine.GetLazyData("articlesInfo.json", false) + if err != nil { + panic(err) + } + reader, err := os.Open(engine.DataFolder() + "articlesInfo.json") + if err == nil { + err = json.NewDecoder(reader).Decode(&articlesInfo) + } + if err == nil { + err = reader.Close() + } + if err != nil { + panic(err) + } + probableList := make([]int, 4) + for _, info := range articlesInfo.ZoneInfo { + switch info.Name { + case "treasure": + probableList[0] = info.Probability + case "pole": + probableList[1] = info.Probability + case "fish": + probableList[2] = info.Probability + case "waste": + probableList[3] = info.Probability + } + } + probabilities["treasure"] = probabilityLimit{ + Min: 0, + Max: probableList[0], + } + probabilities["pole"] = probabilityLimit{ + Min: probableList[0], + Max: probableList[1], + } + probabilities["fish"] = probabilityLimit{ + Min: probableList[1], + Max: probableList[2], + } + probabilities["waste"] = probabilityLimit{ + Min: probableList[2], + Max: probableList[3], + } + min := make(map[string]int, 4) + for _, info := range articlesInfo.ArticleInfo { + switch { + case info.Type == "pole": + poleList = append(poleList, info.Name) + case info.Type == "fish": + fishList = append(fishList, info.Name) + case info.Type == "waste": + wasteList = append(wasteList, info.Name) + case info.Type == "treasure": + treasureList = append(treasureList, info.Name) + } + thingList = append(thingList, info.Name) + priceList[info.Name] = info.Price + if info.Durable != 0 { + durationList[info.Name] = info.Durable + } + probabilities[info.Name] = probabilityLimit{ + Min: min[info.Type], + Max: min[info.Type] + info.Probability, + } + min[info.Type] += info.Probability + } + //}() +} + +// 更新上限信息 +func (sql *fishdb) updateFishInfo(uid int64, number int) (residue int, err error) { + sql.Lock() + defer sql.Unlock() + userInfo := fishState{ID: uid} + err = sql.db.Create("fishState", &userInfo) + if err != nil { + return 0, err + } + _ = sql.db.Find("fishState", &userInfo, "where ID = "+strconv.FormatInt(uid, 10)) + if time.Unix(userInfo.Duration, 0).Day() != time.Now().Day() { + userInfo.Fish = 0 + userInfo.Duration = time.Now().Unix() + } + if userInfo.Fish >= FishLimit { + return 0, nil + } + residue = number + if userInfo.Fish+number > FishLimit { + residue = FishLimit - userInfo.Fish + number = residue + } + userInfo.Fish += number + err = sql.db.Insert("fishState", &userInfo) + return +} + +/*********************************************************/ +/************************装备相关函数***********************/ +/*********************************************************/ + +func (sql *fishdb) checkEquipFor(uid int64) (ok bool, err error) { + sql.Lock() + defer sql.Unlock() + userInfo := fishState{ID: uid} + err = sql.db.Create("fishState", &userInfo) + if err != nil { + return false, err + } + if !sql.db.CanFind("fishState", "where ID = "+strconv.FormatInt(uid, 10)) { + return true, nil + } + err = sql.db.Find("fishState", &userInfo, "where ID = "+strconv.FormatInt(uid, 10)) + if err != nil { + return false, err + } + if userInfo.Equip > 3 { + return false, nil + } + return true, nil +} + +func (sql *fishdb) setEquipFor(uid int64) (err error) { + sql.Lock() + defer sql.Unlock() + userInfo := fishState{ID: uid} + err = sql.db.Create("fishState", &userInfo) + if err != nil { + return err + } + _ = sql.db.Find("fishState", &userInfo, "where ID = "+strconv.FormatInt(uid, 10)) + if err != nil { + return err + } + userInfo.Equip++ + return sql.db.Insert("fishState", &userInfo) +} + +// 获取装备信息 +func (sql *fishdb) getUserEquip(uid int64) (userInfo equip, err error) { + sql.Lock() + defer sql.Unlock() + err = sql.db.Create("equips", &userInfo) + if err != nil { + return + } + if !sql.db.CanFind("equips", "where ID = "+strconv.FormatInt(uid, 10)) { + return + } + err = sql.db.Find("equips", &userInfo, "where ID = "+strconv.FormatInt(uid, 10)) + return +} + +// 更新装备信息 +func (sql *fishdb) updateUserEquip(userInfo equip) (err error) { + sql.Lock() + defer sql.Unlock() + err = sql.db.Create("equips", &userInfo) + if err != nil { + return + } + if userInfo.Durable == 0 { + return sql.db.Del("equips", "where ID = "+strconv.FormatInt(userInfo.ID, 10)) + } + return sql.db.Insert("equips", &userInfo) +} + +func (sql *fishdb) pickFishFor(uid int64, number int) (fishNames map[string]int, err error) { + fishNames = make(map[string]int, 6) + name := strconv.FormatInt(uid, 10) + "Pack" + sql.Lock() + defer sql.Unlock() + userInfo := article{} + err = sql.db.Create(name, &userInfo) + if err != nil { + return + } + count, err := sql.db.Count(name) + if err != nil { + return + } + if count == 0 { + return + } + if !sql.db.CanFind(name, "where Type is 'fish'") { + return + } + fishTypes := make([]article, 0, count) + fishInfo := article{} + err = sql.db.FindFor(name, &fishInfo, "where Type is 'fish'", func() error { + fishTypes = append(fishTypes, fishInfo) + return nil + }) + if err != nil { + return + } + if len(fishTypes) == 0 { + return + } + max := 0 + for _, info := range fishTypes { + max += info.Number + } + if max < number { + number = max + } + for i := number; i > 0; i-- { + randNumber := rand.Intn(len(fishTypes)) + fishTypes[randNumber].Number-- + err = sql.db.Insert(name, &fishTypes[randNumber]) + if err != nil { + return + } + fishNames[fishTypes[randNumber].Name]++ + } + return +} + +/*********************************************************/ +/************************背包相关函数***********************/ +/*********************************************************/ + +// 获取用户背包信息 +func (sql *fishdb) getUserPack(uid int64) (thingInfos []article, err error) { + sql.Lock() + defer sql.Unlock() + userInfo := article{} + err = sql.db.Create(strconv.FormatInt(uid, 10)+"Pack", &userInfo) + if err != nil { + return + } + count, err := sql.db.Count(strconv.FormatInt(uid, 10) + "Pack") + if err != nil { + return + } + if count == 0 { + return + } + err = sql.db.FindFor(strconv.FormatInt(uid, 10)+"Pack", &userInfo, "ORDER by Type, Name, Other ASC", func() error { + thingInfos = append(thingInfos, userInfo) + return nil + }) + return +} + +// 获取用户物品信息 +func (sql *fishdb) getUserThingInfo(uid int64, thing string) (thingInfos []article, err error) { + name := strconv.FormatInt(uid, 10) + "Pack" + sql.Lock() + defer sql.Unlock() + userInfo := article{} + err = sql.db.Create(name, &userInfo) + if err != nil { + return + } + count, err := sql.db.Count(name) + if err != nil { + return + } + if count == 0 { + return + } + if !sql.db.CanFind(name, "where Name = '"+thing+"'") { + return + } + err = sql.db.FindFor(name, &userInfo, "where Name = '"+thing+"'", func() error { + thingInfos = append(thingInfos, userInfo) + return nil + }) + return +} + +// 更新用户物品信息 +func (sql *fishdb) updateUserThingInfo(uid int64, userInfo article) (err error) { + name := strconv.FormatInt(uid, 10) + "Pack" + sql.Lock() + defer sql.Unlock() + err = sql.db.Create(name, &userInfo) + if err != nil { + return + } + if userInfo.Number == 0 { + return sql.db.Del(name, "where Duration = "+strconv.FormatInt(userInfo.Duration, 10)) + } + return sql.db.Insert(name, &userInfo) +} + +// 获取某关键字的数量 +func (sql *fishdb) getNumberFor(uid int64, thing string) (number int, err error) { + name := strconv.FormatInt(uid, 10) + "Pack" + sql.Lock() + defer sql.Unlock() + userInfo := article{} + err = sql.db.Create(name, &userInfo) + if err != nil { + return + } + count, err := sql.db.Count(name) + if err != nil { + return + } + if count == 0 { + return + } + if !sql.db.CanFind(name, "where Name glob '*"+thing+"*'") { + return + } + info := article{} + err = sql.db.FindFor(name, &info, "where Name glob '*"+thing+"*'", func() error { + number += info.Number + return nil + }) + return +} + +/*********************************************************/ +/************************商店相关函数***********************/ +/*********************************************************/ + +// 刷新商店信息 +func (sql *fishdb) refreshStroeInfo() (ok bool, err error) { + sql.Lock() + defer sql.Unlock() + err = sql.db.Create("stroeDiscount", &storeDiscount{}) + if err != nil { + return false, err + } + lastTime := storeDiscount{} + _ = sql.db.Find("stroeDiscount", &lastTime, "where Name = 'lastTime'") + refresh := false + timeNow := time.Now().Day() + if timeNow != lastTime.Discount { + lastTime = storeDiscount{ + Name: "lastTime", + Discount: timeNow, + } + err = sql.db.Insert("stroeDiscount", &lastTime) + if err != nil { + return false, err + } + refresh = true + } + for name := range priceList { + thing := storeDiscount{} + switch refresh { + case true: + thingDiscount := 50 + rand.Intn(150) + thing = storeDiscount{ + Name: name, + Discount: thingDiscount, + } + err = sql.db.Insert("stroeDiscount", &thing) + if err != nil { + return + } + default: + _ = sql.db.Find("stroeDiscount", &thing, "where Name = '"+name+"'") + } + if thing.Discount != 0 { + discountList[name] = thing.Discount + } else { + discountList[name] = 100 + } + } + if refresh { // 每天调控1种鱼 + thingInfo := store{} + err = sql.db.Create("store", &thingInfo) + if err != nil { + return + } + fish := fishList[rand.Intn(len(fishList))] + _ = sql.db.Find("store", &thingInfo, "where Name = '"+fish+"'") + if thingInfo == (store{}) { + thingInfo.Duration = time.Now().Unix() + thingInfo.Type = "fish" + thingInfo.Name = fish + thingInfo.Price = priceList[fish] * discountList[fish] / 100 + } + thingInfo.Number += (100 - discountList[fish]) + if thingInfo.Number < 1 { + thingInfo.Number = 1 + } + _ = sql.db.Insert("store", &thingInfo) + } + return true, nil +} + +// 获取商店信息 +func (sql *fishdb) getStoreInfo() (thingInfos []store, err error) { + sql.Lock() + defer sql.Unlock() + thingInfo := store{} + err = sql.db.Create("store", &thingInfo) + if err != nil { + return + } + count, err := sql.db.Count("store") + if err != nil { + return + } + if count == 0 { + return + } + err = sql.db.FindFor("store", &thingInfo, "ORDER by Type, Name, Price ASC", func() error { + thingInfos = append(thingInfos, thingInfo) + return nil + }) + return +} + +// 获取商店物品信息 +func (sql *fishdb) getStoreThingInfo(thing string) (thingInfos []store, err error) { + sql.Lock() + defer sql.Unlock() + thingInfo := store{} + err = sql.db.Create("store", &thingInfo) + if err != nil { + return + } + count, err := sql.db.Count("store") + if err != nil { + return + } + if count == 0 { + return + } + if !sql.db.CanFind("store", "where Name = '"+thing+"'") { + return + } + err = sql.db.FindFor("store", &thingInfo, "where Name = '"+thing+"'", func() error { + thingInfos = append(thingInfos, thingInfo) + return nil + }) + return +} + +// 更新商店信息 +func (sql *fishdb) updateStoreInfo(thingInfo store) (err error) { + sql.Lock() + defer sql.Unlock() + err = sql.db.Create("store", &thingInfo) + if err != nil { + return + } + if thingInfo.Number == 0 { + return sql.db.Del("store", "where Duration = "+strconv.FormatInt(thingInfo.Duration, 10)) + } + return sql.db.Insert("store", &thingInfo) +} diff --git a/plugin/mcfish/pack.go b/plugin/mcfish/pack.go new file mode 100644 index 0000000000..053d9a6dc6 --- /dev/null +++ b/plugin/mcfish/pack.go @@ -0,0 +1,454 @@ +// Package mcfish 钓鱼模拟器 +package mcfish + +import ( + "bytes" + "errors" + "image" + "image/color" + "strconv" + "strings" + "sync" + + "github.com/FloatTech/AnimeAPI/wallet" + "github.com/FloatTech/floatbox/file" + "github.com/FloatTech/floatbox/math" + "github.com/FloatTech/gg" + "github.com/FloatTech/imgfactory" + "github.com/FloatTech/zbputils/control" + "github.com/FloatTech/zbputils/ctxext" + "github.com/FloatTech/zbputils/img/text" + zero "github.com/wdvxdr1123/ZeroBot" + "github.com/wdvxdr1123/ZeroBot/message" +) + +func init() { + engine.OnFullMatch("钓鱼背包", getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + uid := ctx.Event.UserID + equipInfo, err := dbdata.getUserEquip(uid) + if err != nil { + ctx.SendChain(message.Text("[ERROR at pack.go.1]:", err)) + return + } + articles, err := dbdata.getUserPack(uid) + if err != nil { + ctx.SendChain(message.Text("[ERROR at pack.go.2]:", err)) + return + } + pic, err := drawPackImage(uid, equipInfo, articles) + if err != nil { + ctx.SendChain(message.Text("[ERROR at pack.go.3]:", err)) + return + } + ctx.SendChain(message.ImageBytes(pic)) + }) + engine.OnFullMatch("当前装备概率明细", getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + uid := ctx.Event.UserID + equipInfo, err := dbdata.getUserEquip(uid) + if err != nil { + ctx.SendChain(message.Text("[ERROR at pack.go.1]:", err)) + return + } + number, err := dbdata.getNumberFor(uid, "鱼") + if err != nil { + ctx.SendChain(message.Text("[ERROR at fish.go.5.1]:", err)) + return + } + msg := make(message.Message, 0, 20+len(thingList)) + msg = append(msg, message.At(uid), message.Text("\n大类概率:\n")) + probableList := make([]int, 4) + for _, info := range articlesInfo.ZoneInfo { + switch info.Name { + case "treasure": + probableList[0] = info.Probability + case "pole": + probableList[1] = info.Probability + case "fish": + probableList[2] = info.Probability + case "waste": + probableList[3] = info.Probability + } + } + if number > 100 || equipInfo.Equip == "美西螈" { //放大概率 + probableList = []int{2, 8, 35, 45} + } + if equipInfo.Favor > 0 { + probableList[0] += equipInfo.Favor + probableList[1] += equipInfo.Favor + probableList[2] += equipInfo.Favor + probableList[3] -= equipInfo.Favor * 3 + } + probable := probableList[0] + msg = append(msg, message.Text("宝藏 : ", probableList[0], "%\n")) + probable += probableList[1] + msg = append(msg, message.Text("鱼竿 : ", probableList[1], "%\n")) + probable += probableList[2] + msg = append(msg, message.Text("鱼类 : ", probableList[2], "%\n")) + probable += probableList[3] + msg = append(msg, message.Text("垃圾 : ", probableList[3], "%\n")) + msg = append(msg, message.Text("合计 : ", probable, "%\n")) + msg = append(msg, message.Text("-----------\n宝藏概率:\n")) + for _, name := range treasureList { + msg = append(msg, message.Text(name, " : ", + strconv.FormatFloat(float64(probabilities[name].Max-probabilities[name].Min)*float64(probableList[0])/100, 'f', 2, 64), + "%\n")) + } + msg = append(msg, message.Text("-----------\n鱼竿概率:\n")) + for _, name := range poleList { + msg = append(msg, message.Text(name, " : ", + strconv.FormatFloat(float64(probabilities[name].Max-probabilities[name].Min)*float64(probableList[1])/100, 'f', 2, 64), + "%\n")) + } + msg = append(msg, message.Text("-----------\n鱼类概率:\n")) + for _, name := range fishList { + msg = append(msg, message.Text(name, " : ", + strconv.FormatFloat(float64(probabilities[name].Max-probabilities[name].Min)*float64(probableList[2])/100, 'f', 2, 64), + "%\n")) + } + msg = append(msg, message.Text("-----------")) + ctx.Send(msg) + }) +} + +func drawPackImage(uid int64, equipInfo equip, articles []article) (imagePicByte []byte, err error) { + fontdata, err := file.GetLazyData(text.BoldFontFile, control.Md5File, true) + if err != nil { + return nil, err + } + var ( + wg sync.WaitGroup + equipBlock image.Image // 装备信息 + packBlock image.Image // 背包信息 + ) + wg.Add(1) + // 绘制ID + go func() { + defer wg.Done() + if equipInfo == (equip{}) { + equipBlock, err = drawEquipEmptyBlock(fontdata) + } else { + equipBlock, err = drawEquipInfoBlock(equipInfo, fontdata) + } + if err != nil { + return + } + }() + wg.Add(1) + // 绘制基本信息 + go func() { + defer wg.Done() + if len(articles) == 0 { + packBlock, err = drawArticleEmptyBlock(fontdata) + } else { + packBlock, err = drawArticleInfoBlock(uid, articles, fontdata) + } + if err != nil { + return + } + }() + wg.Wait() + if equipBlock == nil || packBlock == nil { + err = errors.New("生成图片失败,数据缺失") + return + } + // 计算图片高度 + backDX := 1020 + backDY := 10 + equipBlock.Bounds().Dy() + 10 + packBlock.Bounds().Dy() + 10 + canvas := gg.NewContext(backDX, backDY) + + // 画底色 + canvas.DrawRectangle(0, 0, float64(backDX), float64(backDY)) + canvas.SetRGBA255(150, 150, 150, 255) + canvas.Fill() + canvas.DrawRectangle(10, 10, float64(backDX-20), float64(backDY-20)) + canvas.SetRGBA255(255, 255, 255, 255) + canvas.Fill() + + canvas.DrawImage(equipBlock, 10, 10) + canvas.DrawImage(packBlock, 10, 10+equipBlock.Bounds().Dy()+10) + + return imgfactory.ToBytes(canvas.Image()) +} + +// 绘制装备栏区域 +func drawEquipEmptyBlock(fontdata []byte) (image.Image, error) { + canvas := gg.NewContext(1000, 300) + // 画底色 + canvas.DrawRectangle(0, 0, 1000, 300) + canvas.SetRGBA255(255, 255, 255, 150) + canvas.Fill() + // 边框框 + canvas.DrawRectangle(0, 0, 1000, 300) + canvas.SetLineWidth(3) + canvas.SetRGBA255(0, 0, 0, 255) + canvas.Stroke() + + canvas.SetColor(color.Black) + err := canvas.ParseFontFace(fontdata, 100) + if err != nil { + return nil, err + } + textW, textH := canvas.MeasureString("装备信息") + canvas.DrawString("装备信息", 10, 10+textH) + canvas.DrawLine(10, textH*1.2, textW, textH*1.2) + canvas.SetLineWidth(3) + canvas.SetRGBA255(0, 0, 0, 255) + canvas.Stroke() + if err = canvas.ParseFontFace(fontdata, 50); err != nil { + return nil, err + } + canvas.DrawString("没有装备任何鱼竿", 50, 10+textH*2+50) + return canvas.Image(), nil +} +func drawEquipInfoBlock(equipInfo equip, fontdata []byte) (image.Image, error) { + canvas := gg.NewContext(1, 1) + err := canvas.ParseFontFace(fontdata, 100) + if err != nil { + return nil, err + } + _, titleH := canvas.MeasureString("装备信息") + err = canvas.ParseFontFace(fontdata, 50) + if err != nil { + return nil, err + } + _, textH := canvas.MeasureString("装备信息") + + backDY := math.Max(int(10+titleH*2+(textH*2)*4+10), 300) + + canvas = gg.NewContext(1000, backDY) + // 画底色 + canvas.DrawRectangle(0, 0, 1000, float64(backDY)) + canvas.SetRGBA255(255, 255, 255, 150) + canvas.Fill() + // 边框框 + canvas.DrawRectangle(0, 0, 1000, float64(backDY)) + canvas.SetLineWidth(3) + canvas.SetRGBA255(0, 0, 0, 255) + canvas.Stroke() + getAvatar, err := engine.GetLazyData(equipInfo.Equip+".png", false) + if err != nil { + return nil, err + } + equipPic, _, err := image.Decode(bytes.NewReader(getAvatar)) + if err != nil { + return nil, err + } + picDy := float64(backDY) - 10 - titleH*2 + equipPic = imgfactory.Size(equipPic, int(picDy)-10, int(picDy)-10).Image() + canvas.DrawImage(equipPic, 10, 10+int(titleH)*2) + + // 放字 + canvas.SetColor(color.Black) + if err = canvas.ParseFontFace(fontdata, 100); err != nil { + return nil, err + } + titleW, titleH := canvas.MeasureString("装备信息") + canvas.DrawString("装备信息", 10, 10+titleH*1.2) + canvas.DrawLine(10, titleH*1.6, titleW, titleH*1.6) + canvas.SetLineWidth(3) + canvas.SetRGBA255(0, 0, 0, 255) + canvas.Stroke() + + textDx := picDy + 10 + textDy := 10 + titleH*2 + if err = canvas.ParseFontFace(fontdata, 75); err != nil { + return nil, err + } + textW, textH := canvas.MeasureString(equipInfo.Equip) + canvas.DrawStringAnchored(equipInfo.Equip, textDx+textW/2, textDy+textH/2, 0.5, 0.5) + + textDy += textH * 1.5 + if err = canvas.ParseFontFace(fontdata, 50); err != nil { + return nil, err + } + textW, textH = canvas.MeasureString("维修次数") + durable := strconv.Itoa(equipInfo.Durable) + valueW, _ := canvas.MeasureString("100") + barW := 1000 - textDx - textW - 10 - valueW - 10 + + canvas.DrawStringAnchored("装备耐久", textDx+textW/2, textDy+textH/2, 0.5, 0.5) + canvas.DrawRectangle(textDx+textW+5, textDy, barW, textH*1.2) + canvas.SetRGB255(150, 150, 150) + canvas.Fill() + canvas.SetRGB255(0, 0, 0) + durableW := barW * float64(equipInfo.Durable) / float64(durationList[equipInfo.Equip]) + canvas.DrawRectangle(textDx+textW+5, textDy, durableW, textH*1.2) + canvas.SetRGB255(102, 102, 102) + canvas.Fill() + canvas.SetColor(color.Black) + canvas.DrawStringAnchored(durable, textDx+textW+5+barW+5+valueW/2, textDy+textH/2, 0.5, 0.5) + + textDy += textH * 2 + maintenance := strconv.Itoa(equipInfo.Maintenance) + canvas.DrawStringAnchored("维修次数", textDx+textW/2, textDy+textH/2, 0.5, 0.5) + canvas.DrawRectangle(textDx+textW+5, textDy, barW, textH*1.2) + canvas.SetRGB255(150, 150, 150) + canvas.Fill() + canvas.SetRGB255(0, 0, 0) + canvas.DrawRectangle(textDx+textW+5, textDy, barW*float64(equipInfo.Maintenance)/10, textH*1.2) + canvas.SetRGB255(102, 102, 102) + canvas.Fill() + canvas.SetColor(color.Black) + canvas.DrawStringAnchored(maintenance, textDx+textW+5+barW+5+valueW/2, textDy+textH/2, 0.5, 0.5) + + textDy += textH * 3 + canvas.DrawString(" 附魔: 诱钓"+enchantLevel[equipInfo.Induce]+" 海之眷顾"+enchantLevel[equipInfo.Favor], textDx, textDy) + return canvas.Image(), nil +} + +// 绘制背包信息区域 +func drawArticleEmptyBlock(fontdata []byte) (image.Image, error) { + canvas := gg.NewContext(1000, 300) + // 画底色 + canvas.DrawRectangle(0, 0, 1000, 300) + canvas.SetRGBA255(255, 255, 255, 150) + canvas.Fill() + // 边框框 + canvas.DrawRectangle(0, 0, 1000, 300) + canvas.SetLineWidth(3) + canvas.SetRGBA255(0, 0, 0, 255) + canvas.Stroke() + + canvas.SetColor(color.Black) + err := canvas.ParseFontFace(fontdata, 100) + if err != nil { + return nil, err + } + textW, textH := canvas.MeasureString("背包信息") + canvas.DrawString("背包信息", 10, 10+textH*1.2) + canvas.DrawLine(10, textH*1.6, textW, textH*1.6) + canvas.SetLineWidth(3) + canvas.SetRGBA255(0, 0, 0, 255) + canvas.Stroke() + if err = canvas.ParseFontFace(fontdata, 50); err != nil { + return nil, err + } + canvas.DrawStringAnchored("背包没有存放任何东西", 500, 10+textH*2+50, 0.5, 0) + return canvas.Image(), nil +} +func drawArticleInfoBlock(uid int64, articles []article, fontdata []byte) (image.Image, error) { + canvas := gg.NewContext(1, 1) + err := canvas.ParseFontFace(fontdata, 100) + if err != nil { + return nil, err + } + titleW, titleH := canvas.MeasureString("背包信息") + front := 45.0 + err = canvas.ParseFontFace(fontdata, front) + if err != nil { + return nil, err + } + _, textH := canvas.MeasureString("高度") + + nameWOfFiest := 0.0 + nameWOfSecond := 0.0 + for i, info := range articles { + textW, _ := canvas.MeasureString(info.Name + "(" + info.Other + ")") + if i%2 == 0 && textW > nameWOfFiest { + nameWOfFiest = textW + } else if textW > nameWOfSecond { + nameWOfSecond = textW + } + } + valueW, _ := canvas.MeasureString("10000") + + if (10+nameWOfFiest+10+valueW+10)+(10+nameWOfSecond+10+valueW+10) > 980 { + front = 32.0 + err = canvas.ParseFontFace(fontdata, front) + if err != nil { + return nil, err + } + _, textH = canvas.MeasureString("高度") + + nameWOfFiest = 0 + nameWOfSecond = 0 + for i, info := range articles { + textW, _ := canvas.MeasureString(info.Name + "(" + info.Other + ")") + if i%2 == 0 && textW > nameWOfFiest { + nameWOfFiest = textW + } else if textW > nameWOfSecond { + nameWOfSecond = textW + } + } + valueW, _ = canvas.MeasureString("10000") + } + wallW := (980 - (10 + nameWOfFiest + 10 + valueW + 10) - (10 + nameWOfSecond + 10 + valueW + 10)) / 2 + backY := math.Max(10+int(titleH*1.6)+10+int(textH*2)*(math.Ceil(len(articles), 2)+1), 500) + canvas = gg.NewContext(1000, backY) + // 画底色 + canvas.DrawRectangle(0, 0, 1000, float64(backY)) + canvas.SetRGBA255(255, 255, 255, 150) + canvas.Fill() + // 边框框 + canvas.DrawRectangle(0, 0, 1000, float64(backY)) + canvas.SetLineWidth(3) + canvas.SetRGBA255(0, 0, 0, 255) + canvas.Stroke() + + // 放字 + canvas.SetColor(color.Black) + err = canvas.ParseFontFace(fontdata, 100) + if err != nil { + return nil, err + } + canvas.DrawString("背包信息", 10, 10+titleH*1.2) + canvas.DrawLine(10, titleH*1.6, titleW, titleH*1.6) + canvas.SetLineWidth(3) + canvas.SetRGBA255(0, 0, 0, 255) + canvas.Stroke() + + textDy := 10 + titleH*1.7 + if err = canvas.ParseFontFace(fontdata, front); err != nil { + return nil, err + } + canvas.SetColor(color.Black) + numberOfFish := 0 + numberOfEquip := 0 + canvas.DrawStringAnchored("名称", wallW+20+nameWOfFiest/2, textDy+textH/2, 0.5, 0.5) + canvas.DrawStringAnchored("数量", wallW+20+nameWOfFiest+10+valueW/2, textDy+textH/2, 0.5, 0.5) + canvas.DrawStringAnchored("名称", wallW+20+nameWOfFiest+10+valueW+10+10+nameWOfSecond/2, textDy+textH/2, 0.5, 0.5) + canvas.DrawStringAnchored("数量", wallW+20+nameWOfFiest+10+valueW+10+10+nameWOfSecond+10+valueW/2, textDy+textH/2, 0.5, 0.5) + textDy += textH * 2 + for i, info := range articles { + name := info.Name + if info.Other != "" { + if strings.Contains(info.Name, "竿") { + numberOfEquip++ + } + name += "(" + info.Other + ")" + } else if strings.Contains(name, "鱼") { + numberOfFish += info.Number + } + valueStr := strconv.Itoa(info.Number) + if i%2 == 0 { + if i != 0 { + textDy += textH * 2 + } + canvas.DrawStringAnchored(name, wallW+20+nameWOfFiest/2, textDy+textH/2, 0.5, 0.5) + canvas.DrawStringAnchored(valueStr, wallW+20+nameWOfFiest+10+valueW/2, textDy+textH/2, 0.5, 0.5) + } else { + canvas.DrawStringAnchored(name, wallW+20+nameWOfFiest+10+valueW+10+10+nameWOfSecond/2, textDy+textH/2, 0.5, 0.5) + canvas.DrawStringAnchored(valueStr, wallW+20+nameWOfFiest+10+valueW+10+10+nameWOfSecond+10+valueW/2, textDy+textH/2, 0.5, 0.5) + } + } + if err = canvas.ParseFontFace(fontdata, 30); err != nil { + return nil, err + } + textDy = 10 + text := "钱包余额: " + strconv.Itoa(wallet.GetWalletOf(uid)) + textW, textH := canvas.MeasureString(text) + w, _ := canvas.MeasureString("维修大师[已激活]") + if w > textW { + textW = w + } + canvas.DrawStringAnchored(text, 980-textW/2, textDy+textH/2, 0.5, 0.5) + textDy += textH * 1.5 + if numberOfFish > 100 { + canvas.DrawStringAnchored("钓鱼佬[已激活]", 980-textW/2, textDy+textH/2, 0.5, 0.5) + textDy += textH * 1.5 + } + if numberOfEquip > 10 { + canvas.DrawStringAnchored("维修大师[已激活]", 980-textW/2, textDy+textH/2, 0.5, 0.5) + } + return canvas.Image(), nil +} diff --git a/plugin/mcfish/pole.go b/plugin/mcfish/pole.go new file mode 100644 index 0000000000..3cf1a5fad2 --- /dev/null +++ b/plugin/mcfish/pole.go @@ -0,0 +1,485 @@ +// Package mcfish 钓鱼模拟器 +package mcfish + +import ( + "math/rand" + "strconv" + "strings" + "time" + + "github.com/FloatTech/zbputils/ctxext" + zero "github.com/wdvxdr1123/ZeroBot" + "github.com/wdvxdr1123/ZeroBot/message" +) + +func init() { + engine.OnRegex(`^装备(`+strings.Join(poleList, "|")+`)$`, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + uid := ctx.Event.UserID + equipInfo, err := dbdata.getUserEquip(uid) + if err != nil { + ctx.SendChain(message.Text("[ERROR at pole.go.1]:", err)) + return + } + thingName := ctx.State["regex_matched"].([]string)[1] + articles, err := dbdata.getUserThingInfo(uid, thingName) + if err != nil { + ctx.SendChain(message.Text("[ERROR at pole.go.2]:", err)) + return + } + if len(articles) == 0 { + ctx.SendChain(message.Text("你的背包不存在该物品")) + return + } + poles := make([]equip, 0, len(articles)) + if thingName != "美西螈" { + for _, info := range articles { + poleInfo := strings.Split(info.Other, "/") + durable, _ := strconv.Atoi(poleInfo[0]) + maintenance, _ := strconv.Atoi(poleInfo[1]) + induceLevel, _ := strconv.Atoi(poleInfo[2]) + favorLevel, _ := strconv.Atoi(poleInfo[3]) + poles = append(poles, equip{ + ID: uid, + Equip: info.Name, + Durable: durable, + Maintenance: maintenance, + Induce: induceLevel, + Favor: favorLevel, + }) + } + } else { + poles = append(poles, equip{ + ID: uid, + Equip: thingName, + Durable: 999, + }) + } + check := false + index := 0 + if len(poles) > 1 { + msg := make(message.Message, 0, 3+len(articles)) + msg = append(msg, message.Reply(ctx.Event.MessageID), message.Text("找到以下鱼竿:\n")) + for i, info := range poles { + msg = append(msg, message.Text("[", i, "] ", info.Equip, " : 耐", info.Durable, "/修", info.Maintenance, + "/诱", enchantLevel[info.Induce], "/眷顾", enchantLevel[info.Favor], "\n")) + } + msg = append(msg, message.Text("————————\n输入对应序号进行装备,或回复“取消”取消")) + ctx.Send(msg) + // 等待用户下一步选择 + recv, cancel := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^(取消|\d+)$`), zero.CheckUser(ctx.Event.UserID)).Repeat() + defer cancel() + for { + select { + case <-time.After(time.Second * 120): + ctx.Send( + message.ReplyWithMessage(ctx.Event.MessageID, + message.Text("等待超时,取消装备"), + ), + ) + return + case e := <-recv: + nextcmd := e.Event.Message.String() + if nextcmd == "取消" { + ctx.Send( + message.ReplyWithMessage(ctx.Event.MessageID, + message.Text("已取消装备"), + ), + ) + return + } + index, err = strconv.Atoi(nextcmd) + if err != nil || index > len(articles)-1 { + ctx.SendChain(message.At(ctx.Event.UserID), message.Text("请输入正确的序号")) + continue + } + check = true + } + if check { + break + } + } + } + newEquipInfo := poles[index] + packEquip := articles[index] + packEquip.Number-- + err = dbdata.updateUserThingInfo(uid, packEquip) + if err != nil { + ctx.SendChain(message.Text("[ERROR at pole.go.3]:", err)) + return + } + err = dbdata.updateUserEquip(newEquipInfo) + if err != nil { + ctx.SendChain(message.Text("[ERROR at pole.go.3.1]:", err)) + return + } + oldthing := article{} + if equipInfo != (equip{}) && equipInfo.Equip != "美西螈" { + oldthing = article{ + Duration: time.Now().Unix(), + Type: "pole", + Name: equipInfo.Equip, + Number: 1, + Other: strconv.Itoa(equipInfo.Durable) + "/" + strconv.Itoa(equipInfo.Maintenance) + "/" + strconv.Itoa(equipInfo.Induce) + "/" + strconv.Itoa(equipInfo.Favor), + } + } else if equipInfo.Equip == "美西螈" { + articles, err = dbdata.getUserThingInfo(uid, "美西螈") + if err != nil { + ctx.SendChain(message.Text("[ERROR at pole.go.3.2]:", err)) + return + } + if len(articles) == 0 { + oldthing = article{ + Duration: time.Now().Unix(), + Type: "pole", + Name: equipInfo.Equip, + Number: 1, + } + } else { + oldthing = articles[0] + oldthing.Number++ + } + } + err = dbdata.updateUserThingInfo(uid, oldthing) + if err != nil { + ctx.SendChain(message.Text("[ERROR at pole.go.4]:", err)) + return + } + ctx.Send( + message.ReplyWithMessage(ctx.Event.MessageID, + message.Text("装备成功"), + ), + ) + }) + engine.OnFullMatchGroup([]string{"修复鱼竿", "维修鱼竿"}, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + uid := ctx.Event.UserID + equipInfo, err := dbdata.getUserEquip(uid) + if err != nil { + ctx.SendChain(message.Text("[ERROR at pole.go.5]:", err)) + return + } + if equipInfo.Equip == "" || equipInfo.Equip == "美西螈" || equipInfo.Equip == "三叉戟" { + ctx.SendChain(message.Text("仅能修复装备中的鱼竿")) + return + } + if equipInfo.Maintenance >= 10 { + ctx.SendChain(message.Text("装备的鱼竿已经达到修复上限")) + return + } + articles, err := dbdata.getUserThingInfo(uid, equipInfo.Equip) + if err != nil { + ctx.SendChain(message.Text("[ERROR at pole.go.6]:", err)) + return + } + if len(articles) == 0 { + ctx.SendChain(message.Text("你的背包不存在相同鱼竿进行修复")) + return + } + poles := make([]equip, 0, len(articles)) + for _, info := range articles { + poleInfo := strings.Split(info.Other, "/") + durable, _ := strconv.Atoi(poleInfo[0]) + maintenance, _ := strconv.Atoi(poleInfo[1]) + induceLevel, _ := strconv.Atoi(poleInfo[2]) + favorLevel, _ := strconv.Atoi(poleInfo[3]) + poles = append(poles, equip{ + ID: uid, + Equip: info.Name, + Durable: durable, + Maintenance: maintenance, + Induce: induceLevel, + Favor: favorLevel, + }) + } + index := 0 + check := false + if len(articles) > 1 { + msg := make(message.Message, 0, 3+len(articles)) + msg = append(msg, message.Text("找到以下鱼竿:\n")) + for i, info := range poles { + msg = append(msg, message.Text("[", i, "] ", info.Equip, " : 耐", info.Durable, "/修", info.Maintenance, + "/诱", enchantLevel[info.Induce], "/眷顾", enchantLevel[info.Favor], "\n")) + } + msg = append(msg, message.Text("————————\n输入对应序号进行修复,或回复“取消”取消")) + ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, msg...)) + // 等待用户下一步选择 + recv, cancel := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^(取消|\d+)$`), zero.CheckUser(ctx.Event.UserID)).Repeat() + defer cancel() + for { + select { + case <-time.After(time.Second * 120): + ctx.Send( + message.ReplyWithMessage(ctx.Event.MessageID, + message.Text("等待超时,取消修复"), + ), + ) + return + case e := <-recv: + nextcmd := e.Event.Message.String() + if nextcmd == "取消" { + ctx.Send( + message.ReplyWithMessage(ctx.Event.MessageID, + message.Text("已取消修复"), + ), + ) + return + } + index, err = strconv.Atoi(nextcmd) + if err != nil || index > len(articles)-1 { + ctx.SendChain(message.At(ctx.Event.UserID), message.Text("请输入正确的序号")) + continue + } + check = true + } + if check { + break + } + } + } + newEquipInfo := poles[index] + number, err := dbdata.getNumberFor(uid, "竿") + if err != nil { + ctx.SendChain(message.Text("[ERROR at fish.go.5.1]:", err)) + return + } + if number <= 10 { + number = 8 + } else { + number = 10 + } + equipInfo.Durable += newEquipInfo.Durable * number / 10 + if equipInfo.Durable > durationList[equipInfo.Equip] { + equipInfo.Durable = durationList[equipInfo.Equip] + } + msg := "" + if newEquipInfo.Induce != 0 && rand.Intn(100) < 50 { + equipInfo.Induce += newEquipInfo.Induce + if equipInfo.Induce > 3 { + equipInfo.Induce = 3 + } + msg += ",诱钓等级提升至" + enchantLevel[equipInfo.Induce] + } + if newEquipInfo.Favor != 0 && rand.Intn(100) < 50 { + equipInfo.Favor += newEquipInfo.Favor + if equipInfo.Favor > 3 { + equipInfo.Favor = 3 + } + msg += ",海之眷顾等级提升至" + enchantLevel[equipInfo.Favor] + } + thingInfo := articles[index] + thingInfo.Number = 0 + err = dbdata.updateUserThingInfo(uid, thingInfo) + if err == nil { + equipInfo.Maintenance++ + err = dbdata.updateUserEquip(equipInfo) + } + if err != nil { + ctx.SendChain(message.Text("[ERROR at pole.go.7]:", err)) + return + } + ctx.Send( + message.ReplyWithMessage(ctx.Event.MessageID, + message.Text("鱼竿修复成功,耐久提高至", equipInfo.Durable, msg), + ), + ) + }) + engine.OnRegex(`^附魔(诱钓|海之眷顾)$`, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + uid := ctx.Event.UserID + equipInfo, err := dbdata.getUserEquip(uid) + if err != nil { + ctx.SendChain(message.Text("[ERROR at pole.go.7]:", err)) + return + } + if equipInfo.Equip == "" || equipInfo.Equip == "美西螈" { + ctx.SendChain(message.Text("仅可对装备中的进行附魔")) + return + } + book := ctx.State["regex_matched"].([]string)[1] + books, err := dbdata.getUserThingInfo(uid, book) + if err != nil { + ctx.SendChain(message.Text("[ERROR at pole.go.8]:", err)) + return + } + if len(books) == 0 { + ctx.SendChain(message.Text("你的背包不存在", book, "进行附魔")) + return + } + bookInfo := books[0] + bookInfo.Number-- + err = dbdata.updateUserThingInfo(uid, bookInfo) + number := 0 + if err == nil { + if rand.Intn(100) > 50 { + ctx.SendChain(message.Text("附魔失败了")) + return + } + switch book { + case "诱钓": + equipInfo.Induce++ + if equipInfo.Induce > 3 { + equipInfo.Induce = 3 + } + number = equipInfo.Induce + case "海之眷顾": + equipInfo.Favor++ + if equipInfo.Favor > 3 { + equipInfo.Favor = 3 + } + number = equipInfo.Favor + default: + ctx.SendChain(message.Text("附魔失败了")) + return + } + err = dbdata.updateUserEquip(equipInfo) + } + if err != nil { + ctx.SendChain(message.Text("[ERROR at pole.go.9]:", err)) + return + } + ctx.SendChain(message.Text("附魔成功,", book, "等级提高至", enchantLevel[number])) + }) + engine.OnRegex(`^合成(.+竿|三叉戟)$`, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + uid := ctx.Event.UserID + thingList := []string{"木竿", "铁竿", "金竿", "钻石竿", "下界合金竿", "三叉戟"} + thingName := ctx.State["regex_matched"].([]string)[1] + indexOfMaterial := -1 + for i, name := range thingList { + if thingName == name { + indexOfMaterial = (i - 1) + break + } + } + if indexOfMaterial < 0 { + return + } + articles, err := dbdata.getUserThingInfo(uid, thingList[indexOfMaterial]) + if err != nil { + ctx.SendChain(message.Text("[ERROR at pole.go.10]:", err)) + return + } + max := len(articles) + if max < 3 { + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("你的合成材料不足")) + return + } + poles := make([]equip, 0, max) + for _, info := range articles { + poleInfo := strings.Split(info.Other, "/") + durable, _ := strconv.Atoi(poleInfo[0]) + maintenance, _ := strconv.Atoi(poleInfo[1]) + induceLevel, _ := strconv.Atoi(poleInfo[2]) + favorLevel, _ := strconv.Atoi(poleInfo[3]) + poles = append(poles, equip{ + ID: uid, + Equip: info.Name, + Durable: durable, + Maintenance: maintenance, + Induce: induceLevel, + Favor: favorLevel, + }) + } + list := []int{0, 1, 2} + check := false + if len(articles) > 3 { + msg := make(message.Message, 0, 3+len(articles)) + msg = append(msg, message.Text("找到以下鱼竿:\n")) + for i, info := range poles { + msg = append(msg, message.Text("[", i, "] ", info.Equip, " : 耐", info.Durable, "/修", info.Maintenance, + "/诱", enchantLevel[info.Induce], "/眷顾", enchantLevel[info.Favor], "\n")) + } + msg = append(msg, message.Text("————————\n输入3个序号进行合成(用空格分割),或回复“取消”取消")) + ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, msg...)) + // 等待用户下一步选择 + recv, cancel := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^(取消|\d+ \d+ \d+)$`), zero.CheckUser(ctx.Event.UserID)).Repeat() + defer cancel() + for { + select { + case <-time.After(time.Second * 120): + ctx.Send( + message.ReplyWithMessage(ctx.Event.MessageID, + message.Text("等待超时,取消合成"), + ), + ) + return + case e := <-recv: + nextcmd := e.Event.Message.String() + if nextcmd == "取消" { + ctx.Send( + message.ReplyWithMessage(ctx.Event.MessageID, + message.Text("已取消合成"), + ), + ) + return + } + chooseList := strings.Split(nextcmd, " ") + if list[0] == list[1] || list[0] == list[2] || list[1] == list[2] { + ctx.SendChain(message.At(ctx.Event.UserID), message.Text("[0]请输入正确的序号\n", list)) + continue + } + first, err := strconv.Atoi(chooseList[0]) + if err != nil { + ctx.SendChain(message.Text("[ERROR at pole.go.11.1]:", err)) + return + } + second, err := strconv.Atoi(chooseList[1]) + if err != nil { + ctx.SendChain(message.Text("[ERROR at pole.go.11.2]:", err)) + return + } + third, err := strconv.Atoi(chooseList[2]) + if err != nil { + ctx.SendChain(message.Text("[ERROR at pole.go.11.3]:", err)) + return + } + if first > max || second > max || third > max { + ctx.SendChain(message.At(ctx.Event.UserID), message.Text("[", max, "]请输入正确的序号\n", list)) + continue + } + list = []int{first, second, third} + check = true + } + if check { + break + } + } + } + favorLevel := 0 + induceLevel := 0 + for _, index := range list { + thingInfo := articles[index] + thingInfo.Number = 0 + err = dbdata.updateUserThingInfo(uid, thingInfo) + if err != nil { + ctx.SendChain(message.Text("[ERROR at pole.go.12]:", err)) + return + } + favorLevel += poles[index].Favor + induceLevel += poles[index].Induce + } + if rand.Intn(100) >= 90 { + ctx.Send( + message.ReplyWithMessage(ctx.Event.MessageID, + message.Text("合成失败,材料已销毁"), + ), + ) + return + } + attribute := strconv.Itoa(durationList[thingName]) + "/0/" + strconv.Itoa(induceLevel/3) + "/" + strconv.Itoa(favorLevel/3) + newthing := article{ + Duration: time.Now().Unix(), + Type: "pole", + Name: thingName, + Number: 1, + Other: attribute, + } + err = dbdata.updateUserThingInfo(uid, newthing) + if err != nil { + ctx.SendChain(message.Text("[ERROR at pole.go.12]:", err)) + return + } + ctx.Send( + message.ReplyWithMessage(ctx.Event.MessageID, + message.Text(thingName, "合成成功\n属性: ", attribute), + ), + ) + }) +} diff --git a/plugin/mcfish/store.go b/plugin/mcfish/store.go new file mode 100644 index 0000000000..696240e04b --- /dev/null +++ b/plugin/mcfish/store.go @@ -0,0 +1,593 @@ +// Package mcfish 钓鱼模拟器 +package mcfish + +import ( + "image" + "image/color" + "strconv" + "strings" + "time" + + "github.com/FloatTech/AnimeAPI/wallet" + "github.com/FloatTech/floatbox/file" + "github.com/FloatTech/floatbox/math" + "github.com/FloatTech/gg" + "github.com/FloatTech/imgfactory" + "github.com/FloatTech/zbputils/control" + "github.com/FloatTech/zbputils/ctxext" + "github.com/FloatTech/zbputils/img/text" + zero "github.com/wdvxdr1123/ZeroBot" + "github.com/wdvxdr1123/ZeroBot/message" +) + +var ( + refresh = false + timeNow = 0 + refreshFish = func(ctx *zero.Ctx) bool { + if refresh && timeNow == time.Now().Day() { + return true + } + refresh, err := dbdata.refreshStroeInfo() + if err != nil { + ctx.SendChain(message.Text("[ERROR at store.go.1]:", err)) + return refresh + } + timeNow = time.Now().Day() + return refresh + } +) + +func init() { + engine.OnFullMatchGroup([]string{"钓鱼看板", "钓鱼商店"}, getdb, refreshFish).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + infos, err := dbdata.getStoreInfo() + if err != nil { + ctx.SendChain(message.Text("[ERROR at store.go.2]:", err)) + return + } + var picImage image.Image + if len(infos) == 0 { + picImage, err = drawStroeEmptyImage() + } else { + picImage, err = drawStroeInfoImage(infos) + } + if err != nil { + ctx.SendChain(message.Text("[ERROR at store.go.3]:", err)) + return + } + pic, err := imgfactory.ToBytes(picImage) + if err != nil { + ctx.SendChain(message.Text("[ERROR at store.go.4]:", err)) + return + } + ctx.SendChain(message.ImageBytes(pic)) + }) + engine.OnRegex(`^出售(`+strings.Join(thingList, "|")+`)\s*(\d*)$`, getdb, refreshFish).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + uid := ctx.Event.UserID + thingName := ctx.State["regex_matched"].([]string)[1] + number, _ := strconv.Atoi(ctx.State["regex_matched"].([]string)[2]) + if number == 0 { + number = 1 + } + articles, err := dbdata.getUserThingInfo(uid, thingName) + if err != nil { + ctx.SendChain(message.Text("[ERROR at store.go.5]:", err)) + return + } + if len(articles) == 0 { + ctx.SendChain(message.Text("你的背包不存在该物品")) + return + } + index := 0 + thing := article{} + if len(articles) > 1 { + msg := make(message.Message, 0, 3+len(articles)) + msg = append(msg, message.Reply(ctx.Event.MessageID), message.Text("找到以下物品:\n")) + for i, info := range articles { + if info.Other != "" && info.Name != "美西螈" { + msg = append(msg, message.Text("[", i, "] ", info.Name, "(", info.Other, ")\n")) + } else { + msg = append(msg, message.Text( + "[", i, "]", info.Name, " 数量: ", info.Number, "\n")) + } + + } + msg = append(msg, message.Reply(ctx.Event.MessageID), message.Text("————————\n输入对应序号进行装备,或回复“取消”取消")) + ctx.Send(msg) + // 等待用户下一步选择 + sell := false + recv, cancel := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^(取消|\d+)$`), zero.CheckUser(ctx.Event.UserID)).Repeat() + defer cancel() + for { + select { + case <-time.After(time.Second * 120): + ctx.Send( + message.ReplyWithMessage(ctx.Event.MessageID, + message.Text("等待超时,取消出售"), + ), + ) + return + case e := <-recv: + nextcmd := e.Event.Message.String() + if nextcmd == "取消" { + ctx.Send( + message.ReplyWithMessage(ctx.Event.MessageID, + message.Text("已取消出售"), + ), + ) + return + } + index, err = strconv.Atoi(nextcmd) + if err != nil || index > len(articles)-1 { + ctx.SendChain(message.At(ctx.Event.UserID), message.Text("请输入正确的序号")) + continue + } + sell = true + } + if sell { + break + } + } + } + + thing = articles[index] + if thing.Number < number { + ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("背包数量不足"))) + return + } + + var pice int + if strings.Contains(thingName, "竿") || thingName == "三叉戟" { + poleInfo := strings.Split(articles[index].Other, "/") + durable, _ := strconv.Atoi(poleInfo[0]) + maintenance, _ := strconv.Atoi(poleInfo[1]) + induceLevel, _ := strconv.Atoi(poleInfo[2]) + favorLevel, _ := strconv.Atoi(poleInfo[3]) + pice = (priceList[thingName] - (durationList[thingName] - durable) - maintenance*2 + induceLevel*600 + favorLevel*1800) * discountList[thingName] / 100 + } else { + pice = priceList[thingName] * discountList[thingName] / 100 + } + ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("是否接受商店将以", pice*number*8/10, "收购", number, "个", thingName, "?\n回答\"是\"或\"否\""))) + // 等待用户下一步选择 + recv, cancel1 := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^(是|否)$`), zero.CheckUser(ctx.Event.UserID)).Repeat() + defer cancel1() + buy := false + for { + select { + case <-time.After(time.Second * 60): + ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("等待超时,取消钓鱼"))) + return + case e := <-recv: + nextcmd := e.Event.Message.String() + if nextcmd == "否" { + ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("已取消购买"))) + return + } + buy = true + } + if buy { + break + } + } + + records, err := dbdata.getUserThingInfo(uid, "唱片") + if err != nil { + ctx.SendChain(message.Text("[ERROR at store.go.9.1]:", err)) + return + } + if len(records) != 0 { + recordInfo := records[0] + numberOfRecord := recordInfo.Number + if thingName == "唱片" { + numberOfRecord-- + } + if numberOfRecord > 0 { + ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("是否使用唱片让价格翻倍?\n回答\"是\"或\"否\""))) + // 等待用户下一步选择 + recv, cancel2 := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^(是|否)$`), zero.CheckUser(ctx.Event.UserID)).Repeat() + defer cancel2() + use := false + checkTime := false + for { + select { + case <-time.After(time.Second * 60): + checkTime = true + case e := <-recv: + nextcmd := e.Event.Message.String() + if nextcmd == "是" { + use = true + } + checkTime = true + } + if checkTime { + break + } + } + if use { + pice *= 2 + recordInfo.Number-- + err = dbdata.updateUserThingInfo(uid, recordInfo) + if err != nil { + ctx.SendChain(message.Text("[ERROR at store.go.9.2]:", err)) + return + } + } + } + } + thing.Number -= number + err = dbdata.updateUserThingInfo(uid, thing) + if err != nil { + ctx.SendChain(message.Text("[ERROR at store.go.6]:", err)) + return + } + newCommodity := store{} + if strings.Contains(thingName, "竿") || thingName == "三叉戟" { + if pice >= priceList[thingName]*4/5 { // 不值钱的删了 + newCommodity = store{ + Duration: time.Now().Unix(), + Type: "pole", + Name: thingName, + Number: 1, + Price: pice, + Other: thing.Other, + } + } + } else { + things, err1 := dbdata.getStoreThingInfo(thingName) + if err1 != nil { + ctx.SendChain(message.Text("[ERROR at store.go.8]:", err1)) + return + } + if len(things) == 0 { + things = append(things, store{ + Duration: time.Now().Unix(), + Name: thingName, + Number: 0, + Price: pice, + }) + switch { + case thingName == "海之眷顾" || thingName == "诱钓" || thingName == "唱片": + things[0].Type = "article" + case thingName == "美西螈": + things[0].Type = "pole" + default: + things[0].Type = "fish" + } + } + newCommodity = things[0] + newCommodity.Number += number + } + err = dbdata.updateStoreInfo(newCommodity) + if err != nil { + ctx.SendChain(message.Text("[ERROR at store.go.9]:", err)) + return + } + pice = pice * 8 / 10 + err = wallet.InsertWalletOf(uid, pice*number) + if err != nil { + ctx.SendChain(message.Text("[ERROR at store.go.10]:", err)) + return + } + ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("出售成功,你赚到了", pice*number))) + }) + engine.OnRegex(`^购买(`+strings.Join(thingList, "|")+`)\s*(\d*)$`, getdb, refreshFish).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + uid := ctx.Event.UserID + thingName := ctx.State["regex_matched"].([]string)[1] + number, _ := strconv.Atoi(ctx.State["regex_matched"].([]string)[2]) + if number == 0 { + number = 1 + } + thingInfos, err := dbdata.getStoreThingInfo(thingName) + if err != nil { + ctx.SendChain(message.Text("[ERROR at store.go.11]:", err)) + return + } + if len(thingInfos) == 0 { + ctx.SendChain(message.Text("当前商店并没有上架该物品")) + return + } + index := 0 + pice := make([]int, 0, len(thingInfos)) + for _, info := range thingInfos { + if strings.Contains(thingName, "竿") || thingName == "三叉戟" { + poleInfo := strings.Split(info.Other, "/") + durable, _ := strconv.Atoi(poleInfo[0]) + maintenance, _ := strconv.Atoi(poleInfo[1]) + induceLevel, _ := strconv.Atoi(poleInfo[2]) + favorLevel, _ := strconv.Atoi(poleInfo[3]) + thingPice := (priceList[info.Name] - (durationList[info.Name] - durable) - maintenance*2 + induceLevel*600 + favorLevel*1800) * discountList[info.Name] / 100 + pice = append(pice, thingPice) + } else { + thingPice := priceList[info.Name] * discountList[info.Name] / 100 + pice = append(pice, thingPice) + } + + } + if len(thingInfos) > 1 { + msg := make(message.Message, 0, 3+len(thingInfos)) + msg = append(msg, message.Text("找到以下物品:\n")) + for i, info := range thingInfos { + if strings.Contains(thingName, "竿") || thingName == "三叉戟" { + msg = append(msg, message.Text( + "[", i, "]", info.Name, "(", info.Other, ") 价格:", pice[i], "\n")) + } else { + msg = append(msg, message.Text( + "[", i, "]", info.Name, " 数量:", info.Number, " 价格:", pice[i], "\n")) + } + + } + msg = append(msg, message.Text("————————\n输入对应序号进行装备,或回复“取消”取消")) + ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, msg...)) + // 等待用户下一步选择 + sell := false + recv, cancel := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^(取消|\d+)$`), zero.CheckUser(ctx.Event.UserID)).Repeat() + defer cancel() + for { + select { + case <-time.After(time.Second * 120): + ctx.Send( + message.ReplyWithMessage(ctx.Event.MessageID, + message.Text("等待超时,取消购买"), + ), + ) + return + case e := <-recv: + nextcmd := e.Event.Message.String() + if nextcmd == "取消" { + ctx.Send( + message.ReplyWithMessage(ctx.Event.MessageID, + message.Text("已取消购买"), + ), + ) + return + } + index, err = strconv.Atoi(nextcmd) + if err != nil || index > len(thingInfos)-1 { + ctx.SendChain(message.At(ctx.Event.UserID), message.Text("请输入正确的序号")) + continue + } + sell = true + } + if sell { + break + } + } + } + + thing := thingInfos[index] + if thing.Number < number { + ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("商店数量不足"))) + return + } + + money := wallet.GetWalletOf(uid) + if money < pice[index]*number { + ctx.SendChain(message.Text("你身上的钱(", money, ")不够支付")) + return + } + + ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("你确定花费", pice[index]*number, "购买", number, "个", thingName, "?\n回答\"是\"或\"否\""))) + // 等待用户下一步选择 + recv, cancel1 := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^(是|否)$`), zero.CheckUser(ctx.Event.UserID)).Repeat() + defer cancel1() + buy := false + for { + select { + case <-time.After(time.Second * 60): + ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("等待超时,取消购买"))) + return + case e := <-recv: + nextcmd := e.Event.Message.String() + if nextcmd == "否" { + ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("已取消购买"))) + return + } + buy = true + } + if buy { + break + } + } + + thing.Number -= number + err = dbdata.updateStoreInfo(thing) + if err != nil { + ctx.SendChain(message.Text("[ERROR at store.go.12]:", err)) + return + } + err = wallet.InsertWalletOf(uid, -pice[index]*number) + if err != nil { + ctx.SendChain(message.Text("[ERROR at store.go.13]:", err)) + return + } + newCommodity := article{} + if strings.Contains(thingName, "竿") || thingName == "三叉戟" { + newCommodity = article{ + Duration: time.Now().Unix(), + Type: "pole", + Name: thingName, + Number: 1, + Other: thing.Other, + } + } else { + things, err1 := dbdata.getUserThingInfo(uid, thingName) + if err1 != nil { + ctx.SendChain(message.Text("[ERROR at store.go.15]:", err1)) + return + } + if len(things) == 0 { + things = append(things, article{ + Duration: time.Now().Unix(), + Name: thingName, + Number: 0, + }) + switch { + case thingName == "海之眷顾" || thingName == "诱钓" || thingName == "唱片": + things[0].Type = "article" + case thingName == "美西螈": + things[0].Type = "pole" + default: + things[0].Type = "fish" + } + } + newCommodity = things[0] + newCommodity.Number += number + } + err = dbdata.updateUserThingInfo(uid, newCommodity) + if err != nil { + ctx.SendChain(message.Text("[ERROR at store.go.14]:", err)) + return + } + ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("购买成功"))) + }) +} + +func drawStroeEmptyImage() (picImage image.Image, err error) { + fontdata, err := file.GetLazyData(text.BoldFontFile, control.Md5File, true) + if err != nil { + return nil, err + } + canvas := gg.NewContext(1000, 300) + // 画底色 + canvas.DrawRectangle(0, 0, 1000, 300) + canvas.SetRGBA255(255, 255, 255, 150) + canvas.Fill() + // 边框框 + canvas.DrawRectangle(0, 0, 1000, 300) + canvas.SetLineWidth(3) + canvas.SetRGBA255(0, 0, 0, 255) + canvas.Stroke() + + canvas.SetColor(color.Black) + err = canvas.ParseFontFace(fontdata, 100) + if err != nil { + return nil, err + } + textW, textH := canvas.MeasureString("价格信息") + canvas.DrawString("价格信息", 10, 10+textH*1.2) + canvas.DrawLine(10, textH*1.6, textW, textH*1.6) + canvas.SetLineWidth(3) + canvas.SetRGBA255(0, 0, 0, 255) + canvas.Stroke() + if err = canvas.ParseFontFace(fontdata, 50); err != nil { + return nil, err + } + canvas.DrawStringAnchored("当前商店并没有上架任何物品", 500, 10+textH*2+50, 0.5, 0) + return canvas.Image(), nil +} + +func drawStroeInfoImage(stroeInfo []store) (picImage image.Image, err error) { + fontdata, err := file.GetLazyData(text.BoldFontFile, control.Md5File, true) + if err != nil { + return nil, err + } + canvas := gg.NewContext(1, 1) + err = canvas.ParseFontFace(fontdata, 100) + if err != nil { + return nil, err + } + titleW, titleH := canvas.MeasureString("价格信息") + + err = canvas.ParseFontFace(fontdata, 50) + if err != nil { + return nil, err + } + _, textH := canvas.MeasureString("高度") + nameW, _ := canvas.MeasureString("下界合金竿(100/100/0/0)") + numberW, _ := canvas.MeasureString("10000") + priceW, _ := canvas.MeasureString("10000") + + bolckW := int(10 + nameW + 50 + numberW + 50 + priceW + 10) + backY := 10 + int(titleH*2+10)*2 + 10 + (len(stroeInfo)+len(discountList)/2+2)*int(textH*2) + 10 + canvas = gg.NewContext(bolckW, math.Max(backY, 500)) + // 画底色 + canvas.DrawRectangle(0, 0, float64(bolckW), float64(backY)) + canvas.SetRGBA255(150, 150, 150, 255) + canvas.Fill() + + // 放字 + canvas.SetColor(color.Black) + err = canvas.ParseFontFace(fontdata, 100) + if err != nil { + return nil, err + } + canvas.DrawString("今日波动", 10, 10+titleH*1.2) + canvas.DrawLine(10, titleH*1.6, titleW, titleH*1.6) + canvas.SetLineWidth(3) + canvas.SetRGBA255(0, 0, 0, 255) + canvas.Stroke() + + textDy := 10 + titleH*1.7 + if err = canvas.ParseFontFace(fontdata, 35); err != nil { + return nil, err + } + textDx, textDh := canvas.MeasureString("下界合金竿(均价1000)") + valueDx, _ := canvas.MeasureString("+100%") + i := 0 + for name, info := range discountList { + text := name + "(均价" + strconv.Itoa(priceList[name]) + ") " + + if i == 2 { + i = 0 + textDy += textDh * 2 + } + canvas.SetColor(color.Black) + canvas.DrawStringAnchored(text, 20+(textDx+valueDx+10)*float64(i)+10, textDy+textDh/2, 0, 0.5) + if info-100 > 0 { + canvas.SetRGBA255(200, 50, 50, 255) + text = "+" + strconv.Itoa(info-100) + "%" + } else { + canvas.SetRGBA255(63, 133, 55, 255) + text = strconv.Itoa(info-100) + "%" + } + canvas.DrawStringAnchored(text, 20+(textDx+valueDx+10)*float64(i)+10+textDx+10, textDy+textDh/2, 0, 0.5) + i++ + } + canvas.SetColor(color.Black) + textDy += textDh * 2 + canvas.DrawStringAnchored("注:出售商品将会额外扣除20%的税收,附魔鱼竿请按实际价格", 10, textDy+10+textDh/2, 0, 0.5) + + textDy += textH * 2 + err = canvas.ParseFontFace(fontdata, 100) + if err != nil { + return nil, err + } + canvas.DrawString("上架内容", 10, textDy+titleH*1.2) + canvas.DrawLine(10, textDy+titleH*1.6, titleW, textDy+titleH*1.6) + canvas.SetLineWidth(3) + canvas.SetRGBA255(0, 0, 0, 255) + canvas.Stroke() + + textDy += 10 + titleH*1.7 + if err = canvas.ParseFontFace(fontdata, 50); err != nil { + return nil, err + } + + canvas.DrawStringAnchored("名称", 10+nameW/2, textDy+textH/2, 0.5, 0.5) + canvas.DrawStringAnchored("数量", 10+nameW+10+numberW/2, textDy+textH/2, 0.5, 0.5) + canvas.DrawStringAnchored("价格", 10+nameW+10+numberW+50+priceW/2, textDy+textH/2, 0.5, 0.5) + + for _, info := range stroeInfo { + textDy += textH * 2 + name := info.Name + if info.Other != "" && info.Name != "美西螈" { + name += "(" + info.Other + ")" + } + numberStr := strconv.Itoa(info.Number) + pice := 0 + if strings.Contains(name, "竿") { + poleInfo := strings.Split(info.Other, "/") + durable, _ := strconv.Atoi(poleInfo[0]) + maintenance, _ := strconv.Atoi(poleInfo[1]) + induceLevel, _ := strconv.Atoi(poleInfo[2]) + favorLevel, _ := strconv.Atoi(poleInfo[3]) + pice = (priceList[info.Name] - (durationList[info.Name] - durable) - maintenance*2 + induceLevel*600 + favorLevel*1800) * discountList[info.Name] / 100 + } else { + pice = priceList[info.Name] * discountList[info.Name] / 100 + } + + canvas.DrawStringAnchored(name, 10+nameW/2, textDy+textH/2, 0.5, 0.5) + canvas.DrawStringAnchored(numberStr, 10+nameW+10+numberW/2, textDy+textH/2, 0.5, 0.5) + canvas.DrawStringAnchored(strconv.Itoa(pice), 10+nameW+10+numberW+50+priceW/2, textDy+textH/2, 0.5, 0.5) + } + return canvas.Image(), nil +}