ZeroBot-Plugin/plugin/mcfish/main.go
方柳煜 a9bb078079
修复钓鱼插件的逻辑错误 (#724)
* Update fish.go

* Update main.go

* Update fish.go

* Update fish.go

* Update main.go

* Update fish.go

* Update fish.go

* Update main.go
2023-09-03 23:04:26 +08:00

591 lines
16 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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.AutoRegister(&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- 当前装备概率明细\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" || info.Name == "美西螈":
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))
if fishTypes[randNumber].Number <= 0 {
i++
continue
}
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)
}