[新增插件]钓鱼模拟器 (#721)

* 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
This commit is contained in:
方柳煜 2023-09-03 14:09:37 +08:00 committed by GitHub
parent 6bff62b91b
commit b7e21fc111
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 2464 additions and 0 deletions

View File

@ -959,6 +959,23 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] 吟唱提示[xxxx]
</details>
<details>
<summary>钓鱼模拟器</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/mcfish"`
- [x] 钓鱼商店
- [x] 购买xxx [数量]
- [x] 出售xxx [数量]
- [x] 钓鱼背包
- [x] 装备[xx竿|三叉戟|美西螈]
- [x] 附魔[诱钓|海之眷顾]
- [x] 修复鱼竿
- [x] 合成[xx竿|三叉戟]
- [x] 进行钓鱼
- [x] 进行n次钓鱼
</details>
<details>
<summary>简易midi音乐制作</summary>

329
plugin/mcfish/fish.go Normal file
View File

@ -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)
})
}

586
plugin/mcfish/main.go Normal file
View File

@ -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)
}

454
plugin/mcfish/pack.go Normal file
View File

@ -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
}

485
plugin/mcfish/pole.go Normal file
View File

@ -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),
),
)
})
}

593
plugin/mcfish/store.go Normal file
View File

@ -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
}