diff --git a/logic/service/player/Monster.go b/logic/service/player/Monster.go index ca1c06946..02010635e 100644 --- a/logic/service/player/Monster.go +++ b/logic/service/player/Monster.go @@ -3,7 +3,6 @@ package player import ( "blazing/common/data/xmlres" "blazing/logic/service/common" - "blazing/modules/blazing/model" "strings" "sync/atomic" @@ -49,32 +48,11 @@ func (p *Player) genMonster() { ttt.Id = gconv.Uint32(RandomStringFromSlice(id)) if ttt.Id != 0 { - ttt.ShinyInfo = make([]model.GlowFilter, 1) - // 假设 t 是包含 ShinyInfo 字段的结构体,ShinyInfo 是 GlowFilter 类型的切片 - ttt.ShinyInfo[0] = model.GlowFilter{ - // 光晕颜色:白色(十六进制 0xFFFFFF),符合 uint32 类型 - Color: 65535, - // 透明度:0.8(0.0~1.0 范围内的合理值,float64 类型) - Alpha: 0.8, - // 水平模糊量:10(0~255 范围内,uint8 类型,略高于默认值6) - BlurX: 20, - // 垂直模糊量:10(与 BlurX 对称,uint8 类型) - BlurY: 20, - // 发光强度:8(0~255 范围内,uint8 类型,略高于默认值2) - Strength: 1, - // 滤镜应用次数:2(1~3 范围内,int 类型,非默认值1) - Quality: 2, - // 内侧发光:true(bool 类型,模拟开启内侧发光) - Inner: true, - // 挖空:false(bool 类型,保持默认逻辑) - Knockout: false, - // 颜色矩阵:标准 RGBA 矩阵(20个uint8,符合 [20]uint8 数组类型) - // 矩阵含义:R=100%、G=100%、B=100%、A=100%,无颜色偏移 - ColorMatrixFilter: RandomMatrixDefaultAlphaNoSingleColor(int64(grand.Intn(5000)), 0.8), - } - // g.Dump(ttt.ShinyInfo) - // ttt.Shiny = 0 //待确认是否刷新异色 + ttt.Lv = gconv.Uint32(RandomStringFromSlice(lv)) + if grand.Meet(1, 100) { + ttt.RandSHiny() + } if len(id) == 1 { //说明这里只固定刷一个,概率变尼尔尼奥 diff --git a/logic/service/player/color.go b/logic/service/player/color.go index 86c1594cc..91347a734 100644 --- a/logic/service/player/color.go +++ b/logic/service/player/color.go @@ -1,41 +1,48 @@ package player import ( + "math" grand "math/rand" ) -// RandomMatrixNoSingleColor 生成不会导致“只有一种颜色”的 4x5 uint8 矩阵(共20字节)。 -// 约束与策略: -// - 仍然适用“直接把 uint8 原样当系数使用”的前提,保证不会白化或溢出: -// - 当某行有 multiplier != 0 时,offset 强制为 0;且 multipliers 最大为 1 且每行最多一个 1(L1<=1) -// - 当某行使用常量偏移模式(ModeB)时,四个 multiplier 全为 0,offset 为常量(在安全中间区) -// -// - 为避免“只有一种颜色”的情况,我们强制: -// - 至少有一个行使用 ModeA(依赖输入通道)。若有多个 ModeA,则这些 ModeA 的“pick”尽量互不相同(从 0..3 中抽样不重复), -// 这样 destR/destG/destB 至少有不同的输入来源,从而随输入像素变化产生多色彩输出。 -// - 如果有多个 ModeB(常量行),它们的 offset 不相同,且不同时全为相同值,避免所有通道都变为同一常量。 -// -// - alphaRow (最后5字节) 由调用方指定并固定不变(默认 [0,0,0,1,0])。 -// -// brightChance: 0..1,用于决定每行是否优先使用常量偏移 (ModeB);函数内部会修正以保证多样性。 -func RandomMatrixNoSingleColor(seed int64, alphaRow [5]uint8, brightChance float64) [20]uint8 { +// RandomMatrixNoSingleColorBright 是在 RandomMatrixNoSingleColorBias 基础上的变体, +// 增加 brightnessScale 参数用于控制允许的 offset(亮度提升)的比例。 +// 参数说明: +// - seed: 随机种子 +// - alphaRow: 最后 5 字节(alpha 行)固定值 +// - brightChance: 原有决定 ModeB 的概率(常量行概率) +// - redBias: 保持原有 R 偏好(0..1) +// - brightnessScale: 0..1,0 表示不额外提升亮度(行为近似原函数),1 表示允许最大可用 offset(受每行 sum 限制) +// 返回:20 字节矩阵(直接 uint8 原样使用) +func RandomMatrixNoSingleColorBright(seed int64, alphaRow [5]uint8, brightChance, redBias, brightnessScale float64) [20]uint8 { var out [20]uint8 - // clamp brightChance + // clamp params if brightChance < 0 { brightChance = 0 } if brightChance > 1 { brightChance = 1 } + if redBias < 0 { + redBias = 0 + } + if redBias > 1 { + redBias = 1 + } + if brightnessScale < 0 { + brightnessScale = 0 + } + if brightnessScale > 1 { + brightnessScale = 1 + } - // offset 范围(中间区,避免接近 0 或 255) - const minOffset = 40 - const maxOffset = 215 + const minOffsetBase = 24 // 最小偏移基线(你可以调大以更亮) + const maxOffsetBase = 180 // 最大偏移基线(经验上不宜太接近 255) grand.Seed(seed) - // 初步决定每行的模式(true=ModeB(常量偏移),false=ModeA(拷贝/置换)) + // 1) 先按原逻辑决定 ModeA / ModeB 与 picks(保留 redBias 偏好) modeB := make([]bool, 3) modeBCount := 0 for r := 0; r < 3; r++ { @@ -46,99 +53,171 @@ func RandomMatrixNoSingleColor(seed int64, alphaRow [5]uint8, brightChance float modeB[r] = false } } - - // 强制至少一个 ModeA(依赖输入),以避免全常量图像 if modeBCount == 3 { - // 随机把一行改为 ModeA which := int(grand.Intn(3)) modeB[which] = false modeBCount-- } - // 为 ModeA 行分配不重复的 pick(0..3),尽量保证不同输入来源 - // 先收集需要 pick 的行索引 - pickRows := make([]int, 0, 3) + // assign ModeA picks with red bias (similar于之前实现) + available := []int{0, 1, 2, 3} + picks := make(map[int]int) + used := make(map[int]bool) + modeARows := []int{} for r := 0; r < 3; r++ { if !modeB[r] { - pickRows = append(pickRows, r) + modeARows = append(modeARows, r) } } - // available inputs 0..3 - available := []int{0, 1, 2, 3} - // 随机打乱 available - for i := len(available) - 1; i > 0; i-- { - j := int(grand.Intn(i + 1)) - available[i], available[j] = available[j], available[i] + weightedPick := func(avoidUsed bool) int { + cands := []int{} + ws := []float64{} + for _, c := range available { + if avoidUsed && used[c] { + continue + } + w := 1.0 + if c == 0 { + w += redBias * 3.0 + } // 红色加权 + cands = append(cands, c) + ws = append(ws, w) + } + if len(cands) == 0 { + for _, c := range available { + w := 1.0 + if c == 0 { + w += redBias * 3.0 + } + cands = append(cands, c) + ws = append(ws, w) + } + } + sum := 0.0 + for _, w := range ws { + sum += w + } + rv := grand.Float64() * sum + acc := 0.0 + for i, w := range ws { + acc += w + if rv <= acc { + return cands[i] + } + } + return cands[len(cands)-1] } - // 给每个需要 pick 的行分配不同输入(如果 pickRows 数量 <= 4 则肯定可不重复) - picks := make(map[int]int) // row -> pick - for i, row := range pickRows { - picks[row] = available[i%len(available)] + for _, row := range modeARows { + p := weightedPick(true) + picks[row] = p + used[p] = true } - // 为 ModeB 行分配 offsets,保证多个 ModeB 行的 offset 不相同且不等于边界值 - usedOffsets := map[int]bool{} + // 2) 先把 multipliers 写入 out(ModeA one-hot,ModeB 暂置 multipliers=0) for r := 0; r < 3; r++ { - if modeB[r] { - // 随机取一个不同的 offset - var off int - for tries := 0; tries < 10; tries++ { - off = minOffset + int(grand.Intn(maxOffset-minOffset+1)) - if !usedOffsets[off] { - break + base := r * 5 + if !modeB[r] { + p := picks[r] + for c := 0; c < 4; c++ { + if c == p { + out[base+c] = 1 + } else { + out[base+c] = 0 } } - // 若多次冲突仍然存在,仍使用最后一个 off(collision 可能性低) - usedOffsets[off] = true - // 写入 multipliers 全 0,offset = off - for c := 0; c < 4; c++ { - out[r*5+c] = 0 - } - out[r*5+4] = uint8(off) + out[base+4] = 0 // 暂时保持 0,后面可能根据 sum 注入 offset } else { - // ModeA:拷贝/置换,只有一个 multiplier=1(pick),其他为0,offset=0 - pick := picks[r] for c := 0; c < 4; c++ { - if c == pick { - out[r*5+c] = 1 - } else { - out[r*5+c] = 0 - } + out[base+c] = 0 } - out[r*5+4] = 0 + out[base+4] = 0 // 将在下一步设置合法 offset } } - // 额外保护:防止三行输出仍然完全相同的常量或同一输入通道(极小概率) - // 情况1:如果三行都是 ModeA 且 picks 全相同 -> 强制将其中一行改为拷贝另一个不同的输入(或改为 ModeB) - if !modeB[0] && !modeB[1] && !modeB[2] { - if picks[0] == picks[1] && picks[1] == picks[2] { - // 将第三行改为 pick = (picks[2]+1)%4 保证不同 - newPick := (picks[2] + 1) % 4 + // 3) 基于每行 multipliers 的 sum 计算允许的 offset 上限,并在允许范围内按 brightnessScale 随机选择 offset + // allowedMaxOffset = min(maxOffsetBase, floor(255*(1 - sum))) 然后乘以 brightnessScale(线性缩放) + hadOffsetRow := false + for r := 0; r < 3; r++ { + base := r * 5 + // 计算 multipliers sum(目前为 0/1) + sum := 0 + for c := 0; c < 4; c++ { + sum += int(out[base+c]) + } + // 允许的最大 offset(绝对上限由 sum 决定) + allowedBySum := int(math.Floor(255.0 * (1.0 - float64(sum)))) + if allowedBySum < 0 { + allowedBySum = 0 + } + // 想要的最大基线由 maxOffsetBase 决定,再乘以 brightnessScale + desiredMax := int(float64(maxOffsetBase) * brightnessScale) + // 最终可用最大 offset(至少要大于等于 minOffsetBase 才能选择) + allowedMax := allowedBySum + if desiredMax < allowedMax { + allowedMax = desiredMax + } + if allowedMax < minOffsetBase { + // 不足最小基线,设为 0(不可用 offset) + out[base+4] = 0 + } else { + // 在 [minOffsetBase, allowedMax] 随机取一个 offset + off := minOffsetBase + int(grand.Intn(allowedMax-minOffsetBase+1)) + out[base+4] = uint8(off) + hadOffsetRow = true + } + } + + // 4) 保证至少有一行有 offset(避免整个图都只靠拷贝导致暗) + if !hadOffsetRow { + // 尝试找到 sum < 1 的行 preferentially Rout (row 0) then others + rowChosen := -1 + for r := 0; r < 3; r++ { + base := r * 5 + sum := 0 for c := 0; c < 4; c++ { - if c == newPick { - out[2*5+c] = 1 - } else { - out[2*5+c] = 0 - } + sum += int(out[base+c]) } - out[2*5+4] = 0 + if sum < 1 { + rowChosen = r + break + } + } + if rowChosen == -1 { + // 所有行 sum==1(很可能全部 one-hot),我们需要强制把其中一行的 multiplier 置为 0 并赋 offset + rowChosen = int(grand.Intn(3)) + base := rowChosen * 5 + for c := 0; c < 4; c++ { + out[base+c] = 0 + } + } + // 计算 allowedBySum 并设置 offset 为 min(allowedBySum, desired),至少设为 minOffsetBase + base := rowChosen * 5 + sum := 0 + for c := 0; c < 4; c++ { + sum += int(out[base+c]) + } + allowedBySum := int(math.Floor(255.0 * (1.0 - float64(sum)))) + if allowedBySum < minOffsetBase { + // 若仍不足则设为 min(minOffsetBase, allowedBySum) 或强制给一个较小的值 + if allowedBySum > 0 { + out[base+4] = uint8(allowedBySum) + } else { + out[base+4] = uint8(minOffsetBase) // 最后手段(风险低,因为我们可能已把 sum=0) + } + } else { + desiredMax := int(float64(maxOffsetBase) * brightnessScale) + if desiredMax > allowedBySum { + desiredMax = allowedBySum + } + if desiredMax < minOffsetBase { + desiredMax = minOffsetBase + } + off := minOffsetBase + int(grand.Intn(desiredMax-minOffsetBase+1)) + out[base+4] = uint8(off) } } - // 情况2:如果三行都是 ModeB 且 offsets 有重复(理论上不可能因为我们尽量保证唯一),强制调整 - if modeB[0] && modeB[1] && modeB[2] { - if out[4] == out[9] && out[9] == out[14] { - // adjust third offset by +17 (并截断到范围) - newOff := int(out[14]) + 17 - if newOff > maxOffset { - newOff = minOffset + ((newOff - minOffset) % (maxOffset - minOffset + 1)) - } - out[14] = uint8(newOff) - } - } - - // 最后 5 个字节由 caller 指定(alpha 行)并固定 + // 5) 最后 5 个字节(alpha 行)由 caller 指定并固定 for i := 0; i < 5; i++ { out[15+i] = alphaRow[i] } @@ -146,6 +225,7 @@ func RandomMatrixNoSingleColor(seed int64, alphaRow [5]uint8, brightChance float return out } -func RandomMatrixDefaultAlphaNoSingleColor(seed int64, brightChance float64) [20]uint8 { - return RandomMatrixNoSingleColor(seed, [5]uint8{0, 0, 0, 1, 0}, brightChance) +// 便利 wrapper:默认 redBias=0.6,brightnessScale=0.5 +func RandomMatrixNoSingleColorBrightDefault(seed int64, alphaRow [5]uint8, brightChance float64) [20]uint8 { + return RandomMatrixNoSingleColorBright(seed, alphaRow, brightChance, 0.6, 0.5) } diff --git a/logic/service/player/player.go b/logic/service/player/player.go index 17644100d..8512a326e 100644 --- a/logic/service/player/player.go +++ b/logic/service/player/player.go @@ -9,6 +9,7 @@ import ( "blazing/logic/service/fight/info" "blazing/logic/service/space" "sync/atomic" + "time" "blazing/modules/base/service" @@ -46,6 +47,36 @@ type OgrePetInfo struct { Ext uint32 `struc:"skip"` //是否变尼尔尼奥 } +func (o *OgrePetInfo) RandSHiny() { + o.ShinyInfo = make([]model.GlowFilter, 1) + // 假设 t 是包含 ShinyInfo 字段的结构体,ShinyInfo 是 GlowFilter 类型的切片 + o.ShinyInfo[0] = model.GlowFilter{ + // 光晕颜色:白色(十六进制 0xFFFFFF),符合 uint32 类型 + Color: 65535, + // 透明度:0.8(0.0~1.0 范围内的合理值,float64 类型) + Alpha: 0.3, + // 水平模糊量:10(0~255 范围内,uint8 类型,略高于默认值6) + BlurX: 20, + // 垂直模糊量:10(与 BlurX 对称,uint8 类型) + BlurY: 20, + // 发光强度:8(0~255 范围内,uint8 类型,略高于默认值2) + Strength: 1, + // 滤镜应用次数:2(1~3 范围内,int 类型,非默认值1) + Quality: 2, + // 内侧发光:true(bool 类型,模拟开启内侧发光) + Inner: true, + // 挖空:false(bool 类型,保持默认逻辑) + Knockout: false, + // 颜色矩阵:标准 RGBA 矩阵(20个uint8,符合 [20]uint8 数组类型) + // 矩阵含义:R=100%、G=100%、B=100%、A=100%,无颜色偏移 + + } + + o.ShinyInfo[0].ColorMatrixFilter = RandomMatrixNoSingleColorBrightDefault(time.Now().Unix(), [5]uint8{0, 0, 0, 1, 0}, 0.6) + //g.Dump(ttt.ShinyInfo) + // ttt.Shiny = 0 //待确认是否刷新异色 +} + type Player struct { MainConn gnet.Conn baseplayer