This commit is contained in:
2026-10-06 19:20:35 +08:00
parent af88568932
commit 95ac31285f
19 changed files with 1349 additions and 35 deletions
@@ -7,7 +7,7 @@ TextureImporter:
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
@@ -7,7 +7,7 @@ TextureImporter:
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
@@ -7,7 +7,7 @@ TextureImporter:
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
@@ -7,7 +7,7 @@ TextureImporter:
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
@@ -61,10 +61,10 @@ namespace Assets.MyGame.Scripts.Net
// ---------------- Lottery ----------------
/// <summary>抽奖配置(GET /api/Lottery/Config)</summary>
public static void GetLotteryConfig(Action<LotteryActivityConfig> callback)
/// <summary>抽奖配置(GET /api/Lottery/Config,响应为 LotteryConfigResponse:config + drawnPrizeIds)</summary>
public static void GetLotteryConfig(Action<LotteryConfigResponse> callback)
{
APIManager.Response<LotteryActivityConfig>(APIType.Lottery_Config, callback);
APIManager.Response<LotteryConfigResponse>(APIType.Lottery_Config, callback);
}
/// <summary>抽奖(GET /api/Lottery/Draw)</summary>
@@ -90,5 +90,30 @@ namespace Assets.MyGame.Scripts.Net.API
/// 机器人AI配置(后端下发,useRemoteBotConfig 开关开启时使用)
/// </summary>
public BotConfig botConfig;
/// <summary>
/// 鱼雷配置(本轮新增)
/// </summary>
public List<TorpedoItemConfig> torpedoConfigs;
/// <summary>
/// 刮刮卡配置
/// </summary>
public List<ScratchCardConfig> scratchCardConfig;
/// <summary>
/// Boss 配置(后端聚合配置,前端暂无消费;与 killConfig 一致用 object 兜底)
/// </summary>
public object bossConfigs;
/// <summary>
/// Boss 掉落配置
/// </summary>
public BossDropConfig bossDropConfig;
/// <summary>
/// 机器人击杀播报配置
/// </summary>
public RobotKillConfig robotKillConfig;
}
}
@@ -0,0 +1,94 @@
using Assets.MyGame.Scripts.Net.API.UserData_SubClass;
using System;
using System.Collections.Generic;
namespace Assets.MyGame.Scripts.Net.API.GameConfig_SubClass
{
/// <summary>
/// 刮刮卡配置(GameConfig.scratchCardConfig)
/// </summary>
[Serializable]
public class ScratchCardConfig
{
/// <summary>奖励类型:1=金币,2=钻石,3=道具</summary>
public int type;
/// <summary>道具详情</summary>
public BaseProduct baseProduct;
/// <summary>奖励数量</summary>
public int count;
/// <summary>权重</summary>
public int weight;
}
/// <summary>
/// Boss 掉落配置(GameConfig.bossDropConfig)
/// </summary>
[Serializable]
public class BossDropConfig
{
/// <summary>不掉落权重(概率 = noDropWeight / 总权重和)</summary>
public int noDropWeight;
/// <summary>掉落道具列表</summary>
public List<BossDropItemConfig> dropItems;
}
/// <summary>
/// Boss 掉落道具配置
/// </summary>
[Serializable]
public class BossDropItemConfig
{
/// <summary>道具编号</summary>
public string propNumber;
/// <summary>道具名称</summary>
public string propName;
/// <summary>掉落数量</summary>
public int count;
/// <summary>掉落权重(概率 = weight / 10000)</summary>
public int weight;
}
/// <summary>
/// 机器人击杀播报配置(GameConfig.robotKillConfig)
/// </summary>
[Serializable]
public class RobotKillConfig
{
/// <summary>总开关</summary>
public bool isEnabled;
/// <summary>播报间隔(秒),默认 5</summary>
public int intervalSeconds;
/// <summary>播报场景配置</summary>
public List<RobotKillScene> scenes;
}
/// <summary>
/// 机器人击杀播报场景配置
/// </summary>
[Serializable]
public class RobotKillScene
{
/// <summary>场次名称(如:风雷之渊)</summary>
public string gameName;
/// <summary>等级名称(如:初级场)</summary>
public string levelName;
/// <summary>游戏类型</summary>
public int gameType;
/// <summary>房间等级</summary>
public int roomLevel;
/// <summary>出现权重</summary>
public int weight;
/// <summary>鱼种名称</summary>
public string fishName;
/// <summary>鱼种编号</summary>
public string fishNumber;
/// <summary>倍数最小值</summary>
public int multiplierMin;
/// <summary>倍数最大值</summary>
public int multiplierMax;
/// <summary>爆奖金额最小值</summary>
public long jackpotMin;
/// <summary>爆奖金额最大值</summary>
public long jackpotMax;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cea80cf2d728fed4ca31613ada9f1163
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -11,7 +11,8 @@ namespace Assets.MyGame.Scripts.Net.API.GameConfig_SubClass
public long startTime;
public long endTime;
public List<LotteryPoolLevel> pools;
public List<long> costs;
/// <summary>分奖池价格阶梯。Key:"1"=炮台,"2"=战神,"3"=翅膀;每个奖池独立计费,不再使用公共价格</summary>
public Dictionary<string, List<long>> costsByPool;
public int userMaxDraw;
}
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
namespace Assets.MyGame.Scripts.Net.API.GameConfig_SubClass
{
/// <summary>
/// 鱼雷道具配置(GameConfig.torpedoConfigs,本轮新增)
/// </summary>
[Serializable]
public class TorpedoItemConfig
{
/// <summary>鱼雷编号</summary>
public string number;
/// <summary>鱼雷名称</summary>
public string name;
/// <summary>鱼雷颜色</summary>
public string color;
/// <summary>奖励范围列表(按权重随机取金币区间)</summary>
public List<TorpedoRewardConfig> rewardRanges;
}
/// <summary>
/// 鱼雷奖励范围配置
/// </summary>
[Serializable]
public class TorpedoRewardConfig
{
/// <summary>最小金币</summary>
public int minGold;
/// <summary>最大金币</summary>
public int maxGold;
/// <summary>权重</summary>
public int weight;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 256fe00634345604995f7e0a61127998
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -84,6 +84,25 @@ namespace Assets.MyGame.Scripts.Net.APIResponses
public int totalDrawCount;
/// <summary>用户活动是否结束(已抽完所有奖项)</summary>
public bool isFinished;
/// <summary>当前抽奖活动周期标识。活动时间变化时自动开始新一期</summary>
public string activityCycleKey;
/// <summary>活动开始时间(Unix 秒)</summary>
public int activityStartTime;
/// <summary>活动结束时间(Unix 秒)</summary>
public int activityEndTime;
/// <summary>各奖池独立抽奖次数。Key:"1"=炮台,"2"=战神,"3"=翅膀。旧数据没有该字段时自动从空字典开始</summary>
public Dictionary<string, int> drawCountByPool;
}
// ---------------- /api/Lottery/Config ----------------
/// <summary>抽奖配置响应(config + 用户已抽中奖项ID)</summary>
[Serializable]
public class LotteryConfigResponse
{
/// <summary>抽奖活动配置</summary>
public LotteryActivityConfig config;
/// <summary>用户已抽中的奖项ID列表</summary>
public List<string> drawnPrizeIds;
}
// ---------------- /api/SummonCard/use ----------------
@@ -1,6 +1,7 @@
using Spine.Unity;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public enum TurretAnimType
{
@@ -158,6 +159,37 @@ public class TurretData : MonoBehaviour
private void OnEnable()
{
PlayState(TurretState.Idle);
BindAddDownButtons(); // ⭐ XJMH 加减炮倍:炮台上的 +/- 绑定点击(每次激活重绑,幂等)
}
// ==================== XJMH 加减炮倍按钮绑定 ====================
// 炮台上的 obj_Add / obj_Down(物体名 Up / Down)在预制体里没有 Button 组件、也没有任何代码绑定
// → 点击无反应。这里在炮台激活时统一补 Button + 绑定:
// + → 座位 ChangeTurretAddConsumeNum(发 ChangeGunMultiple,等服务端确认后应用新炮倍)
// - → 座位 ChangeTurretMinusConsumeNum
// 未拖引用跳过(可要可不要);非本地座位由 SitPosData.HideTurretAddMinusUI 隐藏。
private SitPosData ResolveSit()
{
return setPosData != null ? setPosData : GetComponentInParent<SitPosData>();
}
private void OnClickTurretAdd() { ResolveSit()?.ChangeTurretAddConsumeNum(); }
private void OnClickTurretMinus() { ResolveSit()?.ChangeTurretMinusConsumeNum(); }
private void BindAddDownButtons()
{
BindAddDownButton(obj_Add, OnClickTurretAdd);
BindAddDownButton(obj_Down, OnClickTurretMinus);
}
private static void BindAddDownButton(GameObject go, UnityEngine.Events.UnityAction action)
{
if (go == null) return; // 未拖引用:跳过,不报错
var btn = go.GetComponent<Button>();
if (btn == null) btn = go.GetComponentInChildren<Button>(true);
if (btn == null) btn = go.AddComponent<Button>(); // 没有 Button 就程序补一个(物体自带的 Image 提供射线检测)
btn.onClick.RemoveAllListeners(); // 重绑前清掉,避免重复触发
btn.onClick.AddListener(action);
}
//public Vector2 GetBulletStartWorldPos()
//{
@@ -379,6 +379,7 @@ MonoBehaviour:
forwordXYZ:
fishenum: 0
moveTime: 0
yzGroupTimeOffset: 0
isyz: 0
yzID:
colliderList: []
@@ -389,6 +390,7 @@ MonoBehaviour:
createVideoClip: {fileID: 0}
rotationPY: 60
useMirrorCompensate: 1
useRollSignFlip: 0
useRouteFlipX: 1
baseMmove: 0
isRotate: 0
@@ -397,8 +399,11 @@ MonoBehaviour:
depthOffset: -60
is2DFish: 0
sortOrder2D: 0
drawMoveLine: 0
drawLineSegments: 60
drawLineColor: {r: 1, g: 0.92156863, b: 0.015686275, a: 1}
bossName: "\u5C0F\u4E11\u6251\u514B"
isToUI: 1
isToUI: 0
tMP_CurrBossScore: {fileID: 0}
currBossScoreValue: 0
bossScoreValue: 0
File diff suppressed because it is too large Load Diff
@@ -1540,7 +1540,7 @@ MonoBehaviour:
- {fileID: 8860668919099931237}
btn_Draw: {fileID: 4229500081248706113}
tmp_ActivityName: {fileID: 0}
tmp_Cost: {fileID: 0}
tmp_Cost: {fileID: 2528847139089705305}
tmp_DrawCount: {fileID: 0}
tmp_EndTime: {fileID: 0}
costPayType: 1
@@ -4215,7 +4215,7 @@ MonoBehaviour:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3294295088569546030}
m_Enabled: 1
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
@@ -5214,7 +5214,7 @@ MonoBehaviour:
m_EditorClassIdentifier:
poolType: 2
items_A:
- {fileID: 0}
- {fileID: 807886942732608504}
- {fileID: 2983635097552066583}
- {fileID: 1958402909111257225}
items_B:
@@ -9744,7 +9744,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
m_IsActive: 0
--- !u!224 &6813680584297815965
RectTransform:
m_ObjectHideFlags: 0
@@ -11017,7 +11017,7 @@ MonoBehaviour:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7083499653794423705}
m_Enabled: 1
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
@@ -13147,7 +13147,7 @@ MonoBehaviour:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8246481127441661318}
m_Enabled: 1
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
@@ -1,4 +1,5 @@
using Assets.Framework.Tool;
using Assets.MyGame.Scripts.Net;
using Assets.MyGame.Scripts.Net.API.GameConfig_SubClass;
using TMPro;
using UnityEngine;
@@ -189,6 +190,8 @@ public class ChouJiangItem : MonoBehaviour
/// <summary>
/// 设置奖励图标:1=金币 / 2=钻石 用 Resources 本地图,
/// 3=道具 / 4=道具碎片 下载 icon(为空时用 product.imageUrl)。
/// 新版数据 icon 是相对路径(如 /uploads/11028.png),需拼 baseURL;
/// 下载失败时用固定格式 /uploads/编号.png 兜底(编号优先 otherId,其次 product.number)。
/// </summary>
private void SetPrizeIcon(Image img, LotteryPrize prize)
{
@@ -210,6 +213,14 @@ public class ChouJiangItem : MonoBehaviour
string url = prize.icon;
if (string.IsNullOrEmpty(url) && prize.product != null)
url = prize.product.imageUrl;
url = ToFullUrl(url);
// 兜底地址:格式固定 /uploads/编号.png
string num = !string.IsNullOrEmpty(prize.otherId) ? prize.otherId
: (prize.product != null ? prize.product.number : "");
string fallback = string.IsNullOrEmpty(num) ? null
: APIManager.baseURL + "/uploads/" + num + ".png";
if (!string.IsNullOrEmpty(url))
{
WebTool.DownImage(url, tex =>
@@ -217,14 +228,45 @@ public class ChouJiangItem : MonoBehaviour
// 回调触发时本物体可能已被销毁(关闭面板清空父节点),需判空
if (this == null || img == null) return;
if (tex != null)
{
img.sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height),
Vector2.one * 0.5f);
return;
}
// 下载失败 → 固定格式 /uploads/编号.png 重试一次
if (!string.IsNullOrEmpty(fallback) && fallback != url)
DownloadTo(fallback, img);
});
}
else if (!string.IsNullOrEmpty(fallback))
{
// icon 为空直接走兜底
DownloadTo(fallback, img);
}
break;
}
}
/// <summary>下载图片并贴到 img(回调时判销毁)</summary>
private static void DownloadTo(string url, Image img)
{
WebTool.DownImage(url, tex =>
{
if (img == null) return; // 静态回调不能判 this,只判 img
if (tex != null)
img.sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height),
Vector2.one * 0.5f);
});
}
/// <summary>相对路径(/uploads/xx.png)拼 baseURL,完整 http 地址原样返回</summary>
private static string ToFullUrl(string url)
{
if (string.IsNullOrEmpty(url)) return url;
if (url.StartsWith("http")) return url;
return APIManager.baseURL + url;
}
private void OnDestroy()
{
ClearUI();
@@ -83,9 +83,20 @@ public class ChouJiangPanel : SubUIBase
{
if (toggles_PoolType[i] == null) continue;
LotteryPoolType type = (LotteryPoolType)(i + 1);
toggles_PoolType[i].onValueChanged.AddListener(isOn =>
Toggle toggle = toggles_PoolType[i];
toggle.onValueChanged.AddListener(isOn =>
{
if (isOn) ShowPoolType(type);
if (!isOn) return;
if (isDrawing)
{
// 抽奖过程中禁止切换:勾选拔回当前分组,保持按钮与面板一致
toggle.SetIsOnWithoutNotify(false);
int curIdx = (int)currPoolType - 1;
if (curIdx >= 0 && curIdx < toggles_PoolType.Count && toggles_PoolType[curIdx] != null)
toggles_PoolType[curIdx].SetIsOnWithoutNotify(true);
return;
}
ShowPoolType(type);
});
}
}
@@ -177,9 +188,11 @@ public class ChouJiangPanel : SubUIBase
});
}
/// <summary>切到指定分组的 toggle 面板</summary>
/// <summary>切到指定分组的 toggle 面板(抽奖过程中禁止切换:跑马灯期间切池会看不到结果)</summary>
public void ShowPoolType(LotteryPoolType type)
{
if (isDrawing) return;
currPoolType = type;
if (toggles != null)
@@ -204,7 +217,8 @@ public class ChouJiangPanel : SubUIBase
toggles_PoolType[idx].SetIsOnWithoutNotify(true);
}
UpdateDrawButtonState();
// 切池后花费阶梯跟着变,刷新花费/次数/按钮
UpdateDrawUI();
}
/// <summary>取当前激活的 toggle 面板(都没有激活时取第一个)</summary>
@@ -269,13 +283,37 @@ public class ChouJiangPanel : SubUIBase
btn_Draw.interactable = CanDraw() && !isDrawing;
}
/// <summary>下一次抽奖花费:第 N+1 次取 costs[N],超出长度取最后一个(独立计费非累加)</summary>
/// <summary>下一次抽奖花费:当前分组池的价格阶梯第 N+1 次价格,超出长度取最后一个(独立计费非累加);当前池未配置时取第一个池兜底</summary>
private long GetNextCost()
{
if (config == null || config.costs == null || config.costs.Count == 0) return 0;
int drawn = userStatus != null ? userStatus.totalDrawCount : 0;
int idx = Mathf.Min(drawn, config.costs.Count - 1);
return config.costs[idx];
if (config == null || config.costsByPool == null || config.costsByPool.Count == 0) return 0;
// poolType:"1"=炮台,"2"=战神,"3"=翅膀;按当前展示的分组取该池的价格阶梯
string key = ((int)currPoolType).ToString();
if (!config.costsByPool.TryGetValue(key, out var costs) || costs == null || costs.Count == 0)
{
foreach (var kv in config.costsByPool)
{
costs = kv.Value;
break;
}
if (costs == null || costs.Count == 0) return 0;
}
int drawn = GetPoolDrawCount(key);
int idx = Mathf.Min(drawn, costs.Count - 1);
return costs[idx];
}
/// <summary>当前池已抽次数(价格阶梯索引:少一个奖励价格升一档):
/// drawCountByPool[key](该池独立计数;字典里没有该 key = 该池还没抽过 = 0,
/// 不回退总次数,否则在别的池抽的次数会污染本池价格);
/// 字段整个为 null 才是旧数据,回退总次数</summary>
private int GetPoolDrawCount(string key)
{
if (userStatus == null) return 0;
if (userStatus.drawCountByPool == null) return userStatus.totalDrawCount; // 旧数据无分池计数
return userStatus.drawCountByPool.TryGetValue(key, out var c) ? c : 0;
}
/// <summary>能否再抽:活动开启、在时间内、未抽完、未达次数上限</summary>
+30 -9
View File
@@ -555,10 +555,10 @@ public class ZhanLing_Panel : SubUIBase
if (gifts == null || gifts.Count == 0) return;
var data = gifts.Select(g => new BackPackPropItemData
{
url = g.type == 3 ? APIManager.baseURL + (g.product?.imageUrl ?? "") : "",
url = g.type == 3 ? APIManager.baseURL + ResolveGiftImageUrl(g) : "",
count = g.isRandom ? g.minCount : g.count,
giftType = g.type,
propEnum = g.type == 3 ? (PropEnum)(g.product?.type ?? 0) : PropEnum.none,
propEnum = g.type == 3 ? (PropEnum)ResolveGiftPropType(g) : PropEnum.none,
}).ToList();
UIManager.Instance.ShowHuoDe(data);
}
@@ -570,21 +570,42 @@ public class ZhanLing_Panel : SubUIBase
}
/// <summary>
/// 通过 otherId 在 propConfig 中查找道具图片地址。
/// 新版 GiftConfig 已移除 product,道具图标需用 otherId 查道具表。
/// 解析礼物(道具)图片地址:优先用 product/道具表下发的 imageUrl;
/// 后端部分道具未下发,按约定格式补全 /uploads/{编号}.png。
/// 返回相对地址,调用方自行拼接 APIManager.baseURL。
/// </summary>
private static string ResolveGiftImageUrl(GiftConfig gift)
{
if (gift == null || string.IsNullOrEmpty(gift.otherId)) return "";
var match = GameManager.Instance?.gameConfig?.propConfig?.Find(p => p.number == gift.otherId);
return match?.imageUrl ?? "";
if (gift == null) return "";
// 商品编号:优先取 product.number,其次取 otherId
string number = !string.IsNullOrEmpty(gift.product?.number) ? gift.product.number : gift.otherId;
var match = string.IsNullOrEmpty(number)
? null
: GameManager.Instance?.gameConfig?.propConfig?.Find(p => p.number == number);
// 道具表里配置了下发地址则直接使用
if (match != null && !string.IsNullOrEmpty(match.imageUrl))
return match.imageUrl;
// product 自带地址(后端直接下发)
if (!string.IsNullOrEmpty(gift.product?.imageUrl))
return gift.product.imageUrl;
// 后端部分道具未下发 imageUrl,按约定格式补全
if (!string.IsNullOrEmpty(number))
return $"/uploads/{number}.png";
return "";
}
/// <summary>通过 otherId 在 propConfig 中查找道具类型</summary>
private static int ResolveGiftPropType(GiftConfig gift)
{
if (gift == null || string.IsNullOrEmpty(gift.otherId)) return 0;
var match = GameManager.Instance?.gameConfig?.propConfig?.Find(p => p.number == gift.otherId);
if (gift == null) return 0;
string number = !string.IsNullOrEmpty(gift.product?.number) ? gift.product.number : gift.otherId;
if (string.IsNullOrEmpty(number)) return 0;
var match = GameManager.Instance?.gameConfig?.propConfig?.Find(p => p.number == number);
return match?.type ?? 0;
}