添加忽略文件
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -11,3 +11,6 @@ QWEN.md
|
||||
Client/.claude/
|
||||
dev-ab-svr/debugconfig.json
|
||||
/Client/.uloop/
|
||||
/Client/.codex/
|
||||
/CodexOutput/
|
||||
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
# 101真实玩法与迁移档恢复:完整业务包方案
|
||||
|
||||
2026-09-28,主会话;状态:待一次完整独立方案评审。**目标:101已导入的历史档能真实登录,完成一次普通抛竿、拉杆收鱼、收下奖励,保存并在新Unity进程恢复。** 本方案默认生产代码零改动,复用已关闭4C2的运行入口。不能把102已成功视为101也已通过。
|
||||
|
||||
## 1. 范围与执行方式
|
||||
|
||||
- 一个业务包包含准备、登录、玩法、保存、正常退出、重启恢复及对账;不再把备份、许可、运行准备各自拆成独立评审阶段。
|
||||
- 完整方案评审后由主会话核相关hash、逐条裁决;取得本包一次运行授权后连续执行。一次授权覆盖下述备份、两个游戏进程和最多一次付费抛竿,不逐步重新申请。需用户手动操作或启动CLI时只请求必要协作。
|
||||
- 内部检查点写开发日志。完成后一次汇总交付复核;发现明确超出本方案的业务改动,才提交针对性补充方案。
|
||||
- 固定 `http://s101.local:18080/`,仅Unity CLI Loop,不用UnitySkills、不打包。游戏只用101,102仅现有备份工具附带登录/Get,不在游戏使用或写102。
|
||||
- 不重导、不合并、不补发、不更改引导/活动/随机鱼/装备以绕过门。生产源码、配置、场景、预制体均不改;允许在本业务证据目录保存只读采样脚本、命令结果、许可归档和对账结果,不新增通用框架。
|
||||
|
||||
## 2. 已知事实和前提
|
||||
|
||||
导入已由 [D5裁决](P2AlphaImport101/D5-执行复核裁决与关闭.md) 限定关闭。历史参考是 `P2AlphaImport101/runs/20260928T073630Z-345b3ee1f47a419abf876bf5b5506d89/after.json`,55键逐值等于固定PF源。该文件只用于对比,不直接作为游戏permit的backupPath:现有许可要求备份位于 `KVGameplayLoopTests` 下。
|
||||
|
||||
同run/account.json证明NewlyCreated=false、InternalId=2、CreateTimeUtc=2026-09-16T12:58:05.860603Z;不能当成刚创建的新玩家。历史主档等级62、金币36550472、体力7275、装备20001、地图102、倍率索引3;实际倍率、活动和玩法资格必须在运行时读取,不能根据旧档推定。FishingChallengeData历史值为字符串null;两类历史空态兼容已经通过独立复核。
|
||||
|
||||
`KVGameplayLoopSetup`已有101/102许可支持;v3负责一次钓鱼,v1负责恢复登录。游戏登录仍先执行PF身份登录,再执行s101登录和KV加载,存在PF域名解析依赖;不能宣称本包游戏完全不访问PF。PF存档写入不在本包授权范围,发现即停止并调查。恢复登录可能产生既有登录保存/跨日写,不能称整个恢复进程只读。
|
||||
|
||||
## 3. 备份、许可与数据边界
|
||||
|
||||
复用锁定的 `P2AlphaImport101/D1-bin/Preflight.dll --backup-101-102`,从项目根运行。DLL SHA256=`AAA1276630E8633C131072DDF638639B92F60134A0425E17616A4A5A2A436607`,355328字节。禁止调用任何import分支。每轮备份为101/102各1登录+1Get,无KV Set/Delete;登录自身仍有账号元数据副作用。
|
||||
|
||||
计划四轮完整备份:S0游戏前、S1登录稳定且尚未抛竿、S2收下并正常退出后、S3新进程恢复并正常退出后。成功路径合计8登录+8Get用于备份,另计两次游戏登录、游戏加载Get和所有实际保存Set;分开记录,不能把工具计数当游戏总请求数。失败只补必要的一轮状态取证,不自动重复业务动作。
|
||||
|
||||
S0必须55键与D导入after原值一致,身份/关键档/配置匹配且解码有效;不一致则保留现状,说明差异后停止本次既定运行,不覆盖历史进度。保存S0原件、hash、键表。v3许可使用S0下101.json和它的55键集合;这是允许现有登录链访问/保存已备份键的上限,不是允许点击55种业务。新键写入保持原拒绝规则。
|
||||
|
||||
允许的游戏动作只有登录、一次普通付费钓鱼、收下、退出。钓鱼期间保留现有更严格写门:NewPlayerDataDic、实际地图鱼档Map_5_{area}、BattlePassRecordTask、MiniBPKeyNew、EventLuckGameRecord、EventLuckMagicPlayfabData,且必须在S0键集中。S0→S1的登录规范化/跨日差异单列来源,不混为钓鱼奖励;出现无法解释的键变化或写入则停止,不扩大白名单。未知/未消费键应原样保持。
|
||||
|
||||
用户运行确认时需保持101其他客户端停用直至本包结束;不沿用已结束的导入15分钟窗口。102不要求停用,其附带快照只报告观测值,不能把其他客户端可能产生的变化归于本次101游戏。
|
||||
|
||||
## 4. 连续执行步骤(内部检查点,不是五次评审)
|
||||
|
||||
| 检查点 | 连续执行内容 | 通过证据 |
|
||||
|---|---|---|
|
||||
| 0 开始 | 裁决方案、记录本包授权;核焦点hash、实际API、Unity空闲/无活动permit;保存旧claims清单;读取S0。使用无旧本包claim的新进程,必要时正常关闭旧进程再启动。 | 批准范围、S0、进程身份、原claims、CLI状态齐全。 |
|
||||
| 1 首次登录 | PrepareFishingRewardPermit(101,S0,55键);一次Play,等待登录/受控加载/首页稳定。禁止先操作签到或抛竿。采样游戏状态、准入环境、保存状态,读取S1并完成登录差异归因。 | Ready=true/Error为空、加载成功;登录保存确认;S1与运行时主档相符。 |
|
||||
| 2 一次玩法 | 主会话完成动作前采样后才通知用户:手动抛竿→拉杆→收鱼,成功停在奖励界面;失败不重试。记录实际鱼/装备/地图/倍率、扣费和奖励票据、奖励画面/数据后,再协作点击收下返回首页。 | 仅一次付费动作;原扣费票据确认;fishing-reward-confirmed先于presented;收下回首页;原票据与最终值可关联。 |
|
||||
| 3 保存退出 | 等既有保存确认,经QuitStage正常退出并确认closed/drained;归档v3许可,保留claim。取得S2并对账,再正常关闭Unity、确认旧进程已退出。 | 不靠强制Stop/杀进程模拟退出;未确认票据为0或有明确失败终态;S1→S2业务差异可解释。 |
|
||||
| 4 重启恢复 | 新进程、S2的101备份建立v1许可,登录且不操作玩法/领奖/装备;核对恢复值,再正常退出,取得S3、归档许可和证据,汇总交付。 | 恢复金币/体力/鱼档/任务进度与S2一致;登录附带变化单独归因,最终无活动permit、旧新claims保留。 |
|
||||
|
||||
进程退出有场景保存对话框时请用户处理,不强杀;CLI未连接时请用户Start Server,不无限轮询。这些是执行协作,不追加方案轮次。游戏失败后的安全退出也走已有保存/drain;不能为了凑齐闭环重建许可重扣一次费用。
|
||||
|
||||
## 5. 必须保留的准入与失败处理
|
||||
|
||||
复用 `UserService.KVFishingReward.cs` 的全部实际门:普通付费鱼、非免费/重试/决斗/固定列表/活动类型,无特殊鱼技能;无冲突引导、巡游/收集目标/存钱罐/砍价/进行中挑战,频次与倍率/刺鱼解锁合格;事件配置和Guidance投影匹配。自然鱼只在动作中选择,事先不能保证抽中合格鱼;拦截照常停止,不能修改随机结果。
|
||||
|
||||
PF解析失败、账号不符、加载失败、配置漂移、准入拒绝、并发迹象、Set失败/Unknown或写门拒绝:保存现有日志和快照,停止后续玩法。扣费已确认而奖励失败则如实记账,不自动补发/重试。服务器结果不明不重放Set。快照也失败则明确证据缺口,不当作没写入。恢复失败不把S0重新导入。
|
||||
|
||||
本方案不授权任意故障修复代码。若查明必须改业务逻辑,集中记录实际阻塞及最小差异,完整评审补充后再改;一般CLI参数、证据采样或日志路径修正按原范围处理并记日志,不单独开方案。
|
||||
|
||||
## 6. 验收和交付
|
||||
|
||||
1. 101实际加载既有55键档,关键运行时数据与S1一致;历史null不再抛异常;未回退空档/PF旧进度。
|
||||
2. S1是在动作前冻结;S1→S2核对体力/消费累计、金币、鱼数量/重量、钓鱼次数和实际任务变化,关联扣费/奖励票据与服务器结果。获取到公式输入才做独立重算;缺项如实列明,不能用同一结果自证公式正确。
|
||||
3. 奖励确认后显示,收下真实回首页;S2保存成立,新进程恢复成立,S3差异有归因。登录跨日变化不能掩盖玩法未保存。
|
||||
4. 无第二次付费、无导入/覆盖102、无放宽门/删claim/打包;所有非预期业务操作(如误点签到)记录,不能标成纯净单动作验证。
|
||||
5. 一份交付记录汇总实际命令、进程、4快照、票据/日志/关键截图、逐键对账、授权与边界、缺口;一次独立执行复核后主会话关闭。本包最多证明101这条真实链,不代表26模块外键、全活动、热换号、完整4B3B/C或G4完成。
|
||||
|
||||
证据放在 `CodexOutput/upserver/Gameplay101Validation/`,运行产生的原有permit/run/backup沿用现有目录并在交付索引引用,不复制庞大历史材料。受审hash仅冻结本方案、直接依赖及历史参考,见同目录 `review-hashes.json`;共享状态/日志不纳入冻结清单,更新检查点无需滚动重封全部项目。
|
||||
|
||||
## 7. 交给评审AI的提示词
|
||||
|
||||
请独立审查本完整业务包,先核 `Gameplay101Validation/review-hashes.json`,再核对涉及源码与4C2既有闭环。重点检查:101既有账号与55键备份;v3/v1边界;PF身份依赖;登录写和钓鱼写的区别;动作前S1;一次扣费与奖励/退出确认;新进程恢复;备份工具附带102只读;失败后不重导/重放/绕过准入。
|
||||
|
||||
给出通过/有条件通过/不通过,逐条问题、严重级别、依据与最小修订。只将真实阻断项作为开工条件,不因文档样式或内部检查点另设实施前复核阶段。只读审查,不运行Unity或正式接口,不修改主会话共享文档。请只落盘 `Gameplay101Validation/完整业务包独立方案审查.md`。此次没有新生产代码,方案批准后直接连续运行并统一复核;不得假设未知运行结果已经成立。
|
||||
@@ -1,44 +0,0 @@
|
||||
# 3C补修复终审裁决与4A准入
|
||||
|
||||
日期:2026-09-23。主会话裁决。用户指令:“复核完成,继续下一步”。
|
||||
|
||||
## 1. 3C正式结论
|
||||
|
||||
**接受用户指定外部AI的实施复核,R1–R5关闭,3C通过,阶段3(G3)恢复关闭。**
|
||||
|
||||
依据:[阶段3C补修复独立实施复核](阶段3C补修复独立实施复核-2026-09-23.md),报告SHA256为D39DAE878C48F9987C7BE6F1788927491B010D9CBCF4B188D25CF4ADD7668C19。
|
||||
|
||||
本轮重新计算了repair-final-source-hashes.json内全部10项:测试宿主Program、csproj及8份生产源码均匹配。Program完整SHA256为66C41B8B0A8E0CD4FF13A3BED8212B3F61BF95E4707F5C6373B235E21B20492A。报告首行缩写hash有笔误,以同格完整hash及本轮实际重算为准,不修改外部报告原文。
|
||||
|
||||
| 项目 | 裁决 |
|
||||
|---|---|
|
||||
| R1 实际收尾和自动清理门 | 关闭:linked CTS、等待body/Close、不确定原运行禁止自动cleanup;失败后不等未结束票据挡住Close |
|
||||
| R2 manifest与路径边界 | 关闭:严格schema/重复字段、固定账号/精确key/run绑定、拒绝路径和链接、登录前验证 |
|
||||
| R3 原始结果不可升级/覆盖 | 关闭:持久化原判、CreateNew原产物、补清理独立结果 |
|
||||
| R4 值隔离/Owner重建/Delete实测 | 关闭:精确值、新Cookie/Owner、102后读101、删除前存在 |
|
||||
| R5 组合故障与诊断缺项 | 关闭:真实C子进程Get、Unknown不重发、主档异常策略和真实状态诊断 |
|
||||
| F2 外部实施复核 | 完成:外部复核接受、无新P1/P2、逐项矩阵确认,并独立复跑8/8与42/86/29 |
|
||||
|
||||
接受外部复核关于Unity证据仍有效的裁决:最终变化仅在Unity工程外测试宿主,8份生产源码未变;外部已对最终版本独立复跑生产回归。正式run已清理,无需为了本次文档裁决重复访问正式服。
|
||||
|
||||
三项P3按非阻塞观察保留:
|
||||
|
||||
1. Latest-*.log是可覆盖指针,per-run结果才是不可变证据。
|
||||
2. runs含故意损坏的离线负例,通用扫描必须区分,不能把解析错误当正式遗留。
|
||||
3. Confirm只在成功Flush后等待已终结票据;后续改动不得破坏此前提。
|
||||
|
||||
## 2. 4A准入结果
|
||||
|
||||
**3C前置门已满足;4A原设计审查继续有效。开工检查新发现构建文件白名单遗漏,先做最小差异评审,尚不修改代码。**
|
||||
|
||||
已审设计要求新增独立PlayerKVLocalReadResult.cs,并让PlayerKVSessionOwner引用它。现有3个链接Owner的独立csproj使用显式Compile清单,没有该新增依赖。按原白名单只改生产Owner后,这些测试项目无法解析新结果类型,A12回归不能完成。
|
||||
|
||||
静态证据:[4A开工依赖核对](阶段4A开工依赖核对-2026-09-23.json)。尚未修改源码或制造编译失败;这是构建依赖推导,不是现有3C缺陷,不撤销3C通过。
|
||||
|
||||
任务书明确规定:“发现接口缺口先落盘差异方案并评审,不能临时扩展文件白名单。”本次需要增加3个csproj白名单,故不把用户“继续”解释为免除其规定的设计差异审查。
|
||||
|
||||
下一步只评审 [4A构建依赖最小差异方案](阶段4A构建依赖最小差异方案-2026-09-23.md)。通过并裁决后,本会话直接按4A检查点实施,不重做整套设计评审。
|
||||
|
||||
## 3. 仍然保持的范围
|
||||
|
||||
4A只做会话桥接和Owner安全只读接口,合成传输验证A1–A12;不接UserService/PlayFabMgr/玩法、不改服务器/包、不删SDK、不访问正式业务KV、不打包。正式API仍为http://s101.local:18080/。游戏真实存档迁移仍须4B/4C及后续验收。
|
||||
@@ -1,49 +0,0 @@
|
||||
# 3C补验收与4A方案审查裁决
|
||||
|
||||
日期:2026-09-23。裁决者:主会话。依据:《3C补验收与4A方案独立审查-2026-09-23.md》。本轮确认审查并同步文档,未修改代码、未执行测试或访问正式服。
|
||||
|
||||
## 1. 审查版本核对
|
||||
|
||||
在修改方案文档前核验以下SHA256,与独立审查记录全部一致:
|
||||
|
||||
| 对象 | SHA256 |
|
||||
|---|---|
|
||||
| 3C复核更正与补验收 | `20CEA4BA6B274BBD108E7B931DAE5FCEDF457D0A803A0343622A33D274DFBFD5` |
|
||||
| 阶段4分期与4A方案 | `4A04700D9CA97992C6E0D25B2614DFA7AF6EECD99EFC4C3756FC134704C28D06` |
|
||||
| 4A待审任务书 | `AF2E5217FE1DA1DFFCBC89CF707BE37EFCCEA89358C61768C824976673686F8E` |
|
||||
| 3C Program.cs | `3C89DA087AA3841ADBA79A11BB2BAC724FD8F5F429D2124F826185EBBDDA64DA` |
|
||||
|
||||
本文裁决后的方案修订会改变文档hash,不影响上述已审版本的追溯。独立审查原文保持不变。
|
||||
|
||||
## 2. 双裁决
|
||||
|
||||
- **A:3C修复设计通过,批准按限定范围实施修复。** R1/P1、R2/P1、R3/P2、R4/P2、R5/P2全部确认成立,目前仍未修复,不恢复3C/G3通过。
|
||||
- **B:4A设计通过,编码暂不放行。** 设计无新增P1/P2,必须先完成3C修复、逐行补验收和用户指定外部AI复核,再完成主会话裁决并批准4A任务书。
|
||||
- 原运行、回归和Unity历史证据保留,不等于修复后复验结果;当前无正式数据损坏的实测证据。
|
||||
|
||||
## 3. 六项P3逐条裁决
|
||||
|
||||
| 编号 | 裁决 | 实施说明 |
|
||||
|---|---|---|
|
||||
| A-S1 | 采纳 | 正式编排建立linked CTS,传递到登录/Owner/直接传输,超时后停止接收后续变更、请求取消并等待全部操作与Close真正收尾。取消是请求,不能假设忽略取消的Handler已结束;测试用闸门证明顺序。超时/Unknown的原运行仍禁止自动cleanup,即使本地传输已收尾;只有后续显式cleanup可尝试补救。 |
|
||||
| A-S2 | 采纳 | manifest持久保存原运行断言状态、最终结果和不确定状态;尚未完成也不得按成功处理。原result/log不可覆写。补cleanup生成独立产物,只更新清理事实,不把原失败或未完成运行升级为通过。 |
|
||||
| A-S3 | 采纳 | 用拒绝重复JSON属性的JObject严格解析,再逐字段白名单/类型映射;读取前验证路径,登录前完成全部schema/key/路径校验。合法cleanup输出待删key的SHA256清单,不回显明文,不额外增加用户确认步骤。 |
|
||||
| B-S1 | 采纳 | 在A2/A3增加初始Get/Create失败后重开验证:失败owner完成收尾后清空桥接活动位;后续合法Open可用。若Close失败,保留关闭失败状态,不能无条件清空并继续Open。 |
|
||||
| B-S2 | 采纳 | A8增加ReadLocalSnapshot与在途Flush并发:验证baseline→pending→localOnly合并视图,且读取不改变在途批次或发送次数。 |
|
||||
| B-S3 | 采纳 | 同时存在归属失效/关闭与非Ready时,`StaleOrClosing`优先于`NotReady`;读取前、读取后及处理NotReady时均按该优先级检查。 |
|
||||
|
||||
以上是现有方案澄清,不扩展架构或修改范围;无需为了这些已裁决P3单独再开一轮设计审查。修复实施后的独立复核仍是强制门。
|
||||
|
||||
## 4. 优先级和边界
|
||||
|
||||
R3严重度保留P2,但执行优先级按P1处理,与R1/R2同为正式服重跑前的阻塞项。实施顺序保持R2→R1/R3→R4/R5→离线验证和限定源码审查→新正式正常路径→回归/Unity/逐行证据→外部复核。
|
||||
|
||||
3C仅允许修改`CodexOutput/upserver/KVStage3CTests/Program.cs`、必要时其csproj、新增该目录证据,主会话维护实施记录和入口文档。不得修改生产代码、既有42/86/29测试、服务器、游戏入口或Unity项目配置。旧run证据只读保留。
|
||||
|
||||
正式API固定`http://s101.local:18080/`,账号101/102仅使用本次manifest登记fixture;离线安全门通过后才能显式执行新正式run,正式服不制造故障。
|
||||
|
||||
4A文件白名单仍由其任务书约束,本裁决不提前允许修改Owner或新增生产桥接类。
|
||||
|
||||
## 5. 下一步
|
||||
|
||||
下一步由主会话实施3C R2及R1/R3安全修复,逐个检查点保存开发日志;在补验收和外部复核完成前,状态保持“3C修复进行/待验收,4A设计已通过待前置”。
|
||||
@@ -1,75 +0,0 @@
|
||||
# 3C补验收与4A方案独立审查
|
||||
|
||||
日期:2026-09-23。审查者:用户指定的独立审查AI(ZCode会话)。性质:只读审查,仅新增本文件;未修改代码、共享入口文档,未访问正式服,未使用子Agent。
|
||||
|
||||
## 审查对象SHA256
|
||||
|
||||
| 文件 | SHA256 |
|
||||
|---|---|
|
||||
| `阶段3C复核更正与补验收-2026-09-23.md` | `20CEA4BA6B274BBD108E7B931DAE5FCEDF457D0A803A0343622A33D274DFBFD5` |
|
||||
| `阶段4存档接线分期与4A方案-2026-09-23.md` | `4A04700D9CA97992C6E0D25B2614DFA7AF6EECD99EFC4C3756FC134704C28D06` |
|
||||
| `阶段4A实施任务书-待评审-2026-09-23.md` | `AF2E5217FE1DA1DFFCBC89CF707BE37EFCCEA89358C61768C824976673686F8E` |
|
||||
| `KVStage3CTests/Program.cs` | `3C89DA087AA3841ADBA79A11BB2BAC724FD8F5F429D2124F826185EBBDDA64DA`(与更正文档记录一致,即上一轮终验所验收版本) |
|
||||
|
||||
## 双裁决摘要
|
||||
|
||||
- **裁决A(3C复核更正与修复设计):通过。** R1–R5五项发现全部有源码依据,严重度评定恰当;修复任务书方向足够,附3项P3补充(A-S1~A-S3)。补验收完成前3C不得恢复"通过"。
|
||||
- **裁决B(4A方案可实施性):通过。** 接口租约、桥接所有权、Close屏障、旧句柄失效、取消语义均正确且在现有生产接口内可实现;A1–A12覆盖充分,附3项P3补充(B-S1~B-S3)。**4A编码批准仍被3C补验收前置门阻塞**(任务书前置门已正确列出)。
|
||||
|
||||
无新发现的P1/P2(R1–R5系对既有代码的确认,非本审查新增缺陷)。
|
||||
|
||||
## A:3C复核与补验收——R1–R5逐条确认
|
||||
|
||||
**R1/P1:确认成立。** 源码:`Program.cs:533` `action().WaitAsync(TimeSpan.FromSeconds(20))`仅取消等待不终止任务;`Live()`的L1/L2经`Run`包装(行283/287);`LiveAccount`内`CreateAsync(..., default, ...)`(行310)无生命周期token,无外部取消通路;超时后孤儿任务继续在途变更,而`finally`的`CleanupCore`(行296–301)与其并发;`HadUnknownWrite`由孤儿路径置位(行319–320)可能晚于`WriteArtifacts`。触发:正式网络慢于20s的组合步骤。影响:清理与迟到写重叠、结果文件失真。P1恰当。
|
||||
|
||||
**R2/P1:确认成立。** 源码:`ValidateManifest`(行516–523)——`!k.StartsWith(m.Prefix)`在Prefix为空串时恒真;Prefix无格式约束(任意前缀+以其开头的任意≤128键均可过);keys无唯一性与精确后缀校验;`RunDirectory/CacheRoot`仅要求`InsideRuns`未绑定与manifest同一run;`InsideRuns`(行525–529)不拒绝重解析点/链接;`JsonConvert.DeserializeObject`不拒绝重复JSON属性与未知字段。触发:被篡改/损坏的manifest+操作者运行`--cleanup`。影响:可授权删除非fixture key(含业务key)。P1恰当。
|
||||
|
||||
**R3/P2:确认成立。** 源码:`CleanupCore`行363 `Completed = !HadUnknownWrite && CleanedAccounts==2`,不依据断言失败;`Cleanup`(行332–341)独立进程以`failed=0`起点重跑并`WriteArtifacts`(行340→554–556)**覆盖原result.json与log**。影响:失败运行可被表现为成功且原始失败证据被销毁。P2成立;鉴于证据覆写后果,建议主会话将其修复按P1优先级排序(不改变分级)。
|
||||
|
||||
**R4/P2:确认成立。** 源码:`LiveAccount`行328–329只断言`Count==2`与键存在,不核对值;L2后不重读101;verify用`DirectGet`(裸Client)而非新登录+新Owner重开同缓存(矩阵L4缺失);删除目标`Keys[2]`从未先确认存在(碰撞预检全缺失⇒live删除的是从未写入的键,删除路径未被真实行使)。P2恰当。
|
||||
|
||||
**R5/P2:确认成立。** 源码:`OfflineChild`c分支(行145–150)仅Open+Inspect,无Get/Ready断言(重开未Get状态必为NotLoaded);`ConservativeUnknown`重开Owner使用新RecordingHandler(行230–231)无计数,行233`mutationCount==1`只覆盖首只handler,重开后不重发未证实;`CorruptionPolicy`缺"主档缺失+bak/tmp遗留→RecoveryRequired";live编排Unknown退出替身(矩阵L7)不存在;行102诊断`SnapshotDto("101", d, PlayerKVSyncStatus.Ready)`硬编码Ready,live诊断缺state/ticket/epoch真实字段。P2恰当。
|
||||
|
||||
### 修复设计的三点补充(P3,建议随裁决采纳)
|
||||
|
||||
- **A-S1(R1)**:修复应把owner生命周期token接到编排级linked CTS,使孤儿传输被确定性Cancel并经Close drain收尾,而非仅等待其自行结束;替身用例须用闸门同步证明"超时→停止→传输收尾完成→才允许清理"的顺序(不使用Sleep)。
|
||||
- **A-S2(R3)**:原运行的断言状态须持久进manifest(当前manifest无该字段,补清理无法据原运行判定Completed);补清理只生成独立补救产物,原result/log保持只读。
|
||||
- **A-S3(R2)**:"未知字段/重复属性拒绝"需显式严格解析(JObject白名单字段映射;JsonConvert默认行为不满足);建议`--cleanup`在登录前完成全部校验,并输出将删除key的SHA256清单(不回显明文)供操作者核对。
|
||||
|
||||
## B:4A方案可实施性
|
||||
|
||||
| 审查点 | 结论 |
|
||||
|---|---|
|
||||
| Owner只读接口租约 | 正确。复用Owner准入门(含closing/closed/归属校验);`State.ReadLocal`自带原子状态检查并返回独立拷贝(3A实现已核);读后复查归属;不新建第二State、不改3A状态机 |
|
||||
| 桥接重复拥有session | 无。明确"持有引用不拥有、不Dispose、登录控制器在Close完成后销毁";单一所有权成立 |
|
||||
| Close挡住新Open | 成立。桥接串行屏障+3A文件租约(Busy)双保险;A4覆盖"忽略取消传输"等待场景 |
|
||||
| 旧句柄写新账号 | 被阻止。固定句柄+逐操作验证OwnerInstanceId/epoch(A6);"仅全局epoch不足"判断正确——延后回调必须持固定句柄 |
|
||||
| 取消等待误取消收尾 | 无。Open排队取消仅取消该等待者;Close异常保留失败状态并拒绝后续Open,锁未释放不被误作可重用 |
|
||||
| 文件白名单 | 足够。`internal CreateAsync(handlerFactory)`注入点已存在(3B验证过同程序集编译),无需新增公共传输入口;修改`PlayerKVSessionOwner.cs`属已验收文件最小增量,A12要求3B回归保持通过且未允许文件hash不变 |
|
||||
| 生产现状断言抽查 | 全部属实:`UserService.cs:306`HexNumber、`SurveyService.cs:56`、`FriendService.cs:375`、`FishingBehaviorB.cs:770`(async void)、`FishingTurntablePanel.cs:517/585`无条件`isDataSynced=true`、`DataCenter.PlayerData.cs`LoadPlayerData/NewPlayerDataDic、Coordinator tickets只增不清退 |
|
||||
| 4B/4C约束反噬4A接口 | 未发现。固定句柄、空KV阻塞策略、票据语义均可由现有+4A新增接口承载 |
|
||||
|
||||
### 三点补充(P3)
|
||||
|
||||
- **B-S1**:建议A2/A3扩展——`CreateAsync`失败(如初始Get失败)后桥接活动位必须清空,后续Open不被卡死。
|
||||
- **B-S2**:建议增加"句柄ReadLocalSnapshot与在途Flush并发"用例:读应得到baseline→pending→localOnly合并视图且不干扰在途批次。
|
||||
- **B-S3**:`ReadLocalSnapshot`错误分类中`StaleOrClosing`与`NotReady`并存时的优先级建议明确(建议StaleOrClosing优先,避免旧句柄被误报为数据未就绪)。
|
||||
|
||||
## 证据类型区分
|
||||
|
||||
- **历史运行证据**(保留为执行事实):offline 7/7、live 3/3、42/86/29、Unity编译——不证明矩阵逐项满足;更正文档对此的定性正确。
|
||||
- **静态推导**:R1–R5全部为源码级推导(本审查逐行确认);均为"源码可达风险与覆盖缺口",不是已发生的数据事故;未观察到正式服数据损坏。
|
||||
- **尚未实测**:R1迟到写与清理重叠(合成复现方法:受闸门控制的延迟handler使L1超过20s,断言清理开始前传输已收尾——按更正文档§3.2在修复后执行);L7替身;修复后的全部矩阵与新的正式正常路径run。
|
||||
|
||||
## 需主会话确认的事项
|
||||
|
||||
1. 采纳A-S1~A-S3、B-S1~B-S3为实施说明。
|
||||
2. R3修复优先级建议按P1对待(证据覆写后果)。
|
||||
3. 重申:3C补验收(含修复、逐行矩阵、外部复核)完成前,不得以4A设计通过为由批准编码;4A任务书前置门已正确列出该条件。
|
||||
4. 更正文档§4关于"八文件历史基线"的自我更正(S101AccountSession仅本阶段hash)属实,无需再动。
|
||||
|
||||
## 结论
|
||||
|
||||
- **A:通过**(R1–R5确认,修复设计足够+3项P3补充;3C恢复"通过"须待补验收闭环)。
|
||||
- **B:通过**(4A方案可实施+3项P3补充;编码批准以3C补验收闭环为前置)。
|
||||
- 本审查无新增P1/P2。
|
||||
@@ -1,162 +0,0 @@
|
||||
{
|
||||
"createdUtc": "2026-09-28T10:08:57.6522702Z",
|
||||
"purpose": "B01完整方案独立审查;项目根相对路径;共享状态和可更新执行TODO不冻结;清单不代表修改授权",
|
||||
"count": 30,
|
||||
"items": [
|
||||
{
|
||||
"path": "CodexOutput/upserver/B3BatchMigration/B01-通知提示与已读状态批级方案-待评审.md",
|
||||
"sha256": "3F9B1DAAE6D81546CD8B36ECD5DA1DF359BD27842B91A51F290504179812F2E0",
|
||||
"bytes": 15840
|
||||
},
|
||||
{
|
||||
"path": "CodexOutput/upserver/B3BatchMigration/B01-独立方案审查提示词.md",
|
||||
"sha256": "3B689024ACD52A4733AD9CCC0E17D6C4D14E8FA471938227874A90563E59C0B1",
|
||||
"bytes": 1719
|
||||
},
|
||||
{
|
||||
"path": "CodexOutput/upserver/B3BatchMigration/调用分类与现状映射.json",
|
||||
"sha256": "4537878A379D010104CD8C5A66997794095B02279D9F532BBB0433335EEFE768",
|
||||
"bytes": 155605
|
||||
},
|
||||
{
|
||||
"path": "CodexOutput/upserver/B3BatchMigration/分类统计.json",
|
||||
"sha256": "8E142B41D4A1CB92EF09DFC267777053B97DF9ADCFE2F1741CA651ECFAC848F7",
|
||||
"bytes": 836
|
||||
},
|
||||
{
|
||||
"path": "CodexOutput/upserver/B3BatchMigration/current-scan/calls.json",
|
||||
"sha256": "BA3720B8C802E2B85FB7F7FA1C8DE1D527B3E1C81D48EB068073DD905880C51E",
|
||||
"bytes": 917099
|
||||
},
|
||||
{
|
||||
"path": "CodexOutput/upserver/B3BatchMigration/current-scan/source-hashes.json",
|
||||
"sha256": "C1D533D806AEF33537AAE58BD9A5F850C810891C48F056C49D9499F0E45AD02E",
|
||||
"bytes": 276004
|
||||
},
|
||||
{
|
||||
"path": "CodexOutput/upserver/B3DesignAudit/calls.json",
|
||||
"sha256": "12588092ECE71E12FDFD829B0A65F23EFA53241F49C527067B0ADDE423E47C61",
|
||||
"bytes": 913483
|
||||
},
|
||||
{
|
||||
"path": "CodexOutput/upserver/B3DesignAudit/Program.cs",
|
||||
"sha256": "542C2A6EE08EBC88E71A61C423587FEB9335DF16232FC134F4F22328B4BE865E",
|
||||
"bytes": 5303
|
||||
},
|
||||
{
|
||||
"path": "CodexOutput/upserver/B3DesignAudit/bin/Debug/net9.0/B3DesignAudit.dll",
|
||||
"sha256": "B01AC846E3DD015A36744597872E46861981C4950E7D955C76324EF486D99722",
|
||||
"bytes": 17408
|
||||
},
|
||||
{
|
||||
"path": "CodexOutput/upserver/流程修正主会话意见-审查侧裁决-2026-09-28.md",
|
||||
"sha256": "F77EC297607BEF1D9687F0B5778EC6585D9B083C87D148655ECA722726B0390F",
|
||||
"bytes": 5436
|
||||
},
|
||||
{
|
||||
"path": "CodexOutput/upserver/流程修正主会话意见-待审查裁决-2026-09-28.md",
|
||||
"sha256": "21F7F1F1B853E18B598F4B5817EC051FC92FBC5A06B58D4E25E4EFC7930A61D8",
|
||||
"bytes": 9195
|
||||
},
|
||||
{
|
||||
"path": "CodexOutput/upserver/阶段4B3分期与4B3A固定存档入口方案-待评审-2026-09-24.md",
|
||||
"sha256": "FA5EB445C77C9F11E3EEE71BEE8007A5A25AB76761DD4F75EFF93B23360B7022",
|
||||
"bytes": 21798
|
||||
},
|
||||
{
|
||||
"path": "Client/Assets/Scripts/Services/SettingService.cs",
|
||||
"sha256": "5D139BE63E13A3D3248D866C46A2D07F9D0917F624E0073D9CF7FE5E05C561D8",
|
||||
"bytes": 5337
|
||||
},
|
||||
{
|
||||
"path": "Client/Assets/Scripts/Services/ILocalNotificationService.cs",
|
||||
"sha256": "B5763A9CCB4A0C079916345F7D41371A4B9E73B706D5B45468C18345D7EBFEE5",
|
||||
"bytes": 6672
|
||||
},
|
||||
{
|
||||
"path": "Client/Assets/Scripts/Services/ClubService.cs",
|
||||
"sha256": "DC94626E1F386F17FCCA3D3C12D357FE95CEE8E5F50A943E73770399F3B2CD42",
|
||||
"bytes": 43338
|
||||
},
|
||||
{
|
||||
"path": "Client/Assets/Scripts/UI/Setting/SettingNotificationsGradePopupPanel.cs",
|
||||
"sha256": "177E9F1F6B680CF76A8C0C94653A4C02DAB23DDB56D4A85CEA525C3F01A3F1E0",
|
||||
"bytes": 672
|
||||
},
|
||||
{
|
||||
"path": "Client/Assets/Scripts/UI/Setting/SettingNotificationsPopupPanel.cs",
|
||||
"sha256": "783F3973B240BB8BC6C0EAB6449610079C9B59B0666E73A1D101465DEC75E3E1",
|
||||
"bytes": 634
|
||||
},
|
||||
{
|
||||
"path": "Client/Assets/Scripts/UI/Setting/SettingPopupPanel.cs",
|
||||
"sha256": "AF64C5E1CADAD9C543E98A8406F92576A76574F35B8556BBB295EF5A6429D8CF",
|
||||
"bytes": 8854
|
||||
},
|
||||
{
|
||||
"path": "Client/Assets/Scripts/UI/Shop/GiftNoticePanel.cs",
|
||||
"sha256": "A1737674CAEBDD91F4EA2F18C488CDC62F186D948D3D49B4EA316E459C2D86CC",
|
||||
"bytes": 2440
|
||||
},
|
||||
{
|
||||
"path": "Client/Assets/Scripts/UI/Social/FishingSocialPanel.cs",
|
||||
"sha256": "1E33FBD3CF6EF76C20CF76FA7EF12028A31EE062EF4C1EA26EB2A701AC85DD6E",
|
||||
"bytes": 7122
|
||||
},
|
||||
{
|
||||
"path": "Client/Assets/Scripts/DebugFunc.cs",
|
||||
"sha256": "F7968007D44382D6DF17E1239FDA9FBA37BAEA40F5FD608D9412561BC0368107",
|
||||
"bytes": 36052
|
||||
},
|
||||
{
|
||||
"path": "Client/Assets/Scripts/DataCenter/DataCenter.PlayerData.cs",
|
||||
"sha256": "768C2935FA702ACAA7E6F20FA9E82A883E3863B3104ACFD519DFFEBA3A890C83",
|
||||
"bytes": 65923
|
||||
},
|
||||
{
|
||||
"path": "Client/Assets/Scripts/Core/PlayFabMgr.cs",
|
||||
"sha256": "E75C8BEDE21878286EF4E247BB3BE24CE81B81BFD1EEB3106E7B10468FFA9195",
|
||||
"bytes": 15712
|
||||
},
|
||||
{
|
||||
"path": "Client/Assets/Scripts/SubBoostrap.cs",
|
||||
"sha256": "B62354B0761DCCD445A19C25D7379ACB71FFF2FD47D07BEB2101E5A79889D381",
|
||||
"bytes": 18341
|
||||
},
|
||||
{
|
||||
"path": "Client/Assets/Scripts/Stages/LoginStage.cs",
|
||||
"sha256": "D95641EEB076B75D86F57855F71B4F83894519CC6F3890F1919490FB2FF45C45",
|
||||
"bytes": 6152
|
||||
},
|
||||
{
|
||||
"path": "Client/Assets/Scripts/Services/UserService.KVLoad.cs",
|
||||
"sha256": "45300AA455CF43C07286A442E7A2C8534B3654C4AEB8316CA5EB3A20593AEA0D",
|
||||
"bytes": 6963
|
||||
},
|
||||
{
|
||||
"path": "Client/Assets/Scripts/Services/PlayerData/PlayerKVGameSessionBridge.cs",
|
||||
"sha256": "DD888351BFA4BBA03E953F39E11362AF8EA9F76FB56AC1CB77D9053C81227363",
|
||||
"bytes": 13612
|
||||
},
|
||||
{
|
||||
"path": "Client/Assets/Scripts/Services/PlayerData/PlayerKVKeyReadResult.cs",
|
||||
"sha256": "C9C76A16F61CBC3CC748A0F44B581508B4178E7F322C43C8F05E2FC8707345DC",
|
||||
"bytes": 1471
|
||||
},
|
||||
{
|
||||
"path": "Client/Assets/Scripts/Services/PlayerData/PlayerKVSaveSchema.cs",
|
||||
"sha256": "FBDB253D3415E35D7D30F867DA057A84E91EAA7DE2FE74094707C517CB8B51A1",
|
||||
"bytes": 76183
|
||||
},
|
||||
{
|
||||
"path": "Client/Assets/Scripts/Services/PlayerData/PlayerKVGameplayBindings.cs",
|
||||
"sha256": "710A0050277BE07AC781F976945D8D2DDF7704EA616D0BF83797ABA2FA6530EA",
|
||||
"bytes": 6874
|
||||
}
|
||||
],
|
||||
"expectedAbsent": [
|
||||
"Client/Assets/Editor/KVPromptStateValidation.cs",
|
||||
"Client/Assets/Editor/KVPromptStateValidation.cs.meta",
|
||||
"CodexOutput/upserver/B3BatchMigration/B01Tests"
|
||||
]
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"verifiedUtc": "2026-09-28T10:08:58.0972395Z",
|
||||
"manifestCount": 30,
|
||||
"hashMismatches": 0,
|
||||
"absentCount": 3,
|
||||
"classified": 224,
|
||||
"uniqueStableIds": 224,
|
||||
"sourceFilesChecked": 2108,
|
||||
"sourceFilesChangedSinceScan": [],
|
||||
"selectedStableIds": [
|
||||
"B3-C170",
|
||||
"B3-C177",
|
||||
"B3-C178"
|
||||
]
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
# B01独立批级方案审查
|
||||
|
||||
请只读审查C:\projects\ft的4B3B首批“通知提示与俱乐部已读状态”。先读上级《流程修正主会话意见-审查侧裁决-2026-09-28.md》,本目录《执行TodoList.md》《B01-通知提示与已读状态批级方案-待评审.md》及《调用分类与现状映射.json》《分类统计.json》。以《B01-design-hashes.json》独立核SHA256和长度;当前源码快照在current-scan,旧calls.json不得覆盖。
|
||||
|
||||
这是正式业务代码准备方案,不是新Editor单次许可,不启用正式KV;B→C门保持。本批3处存档调用须连同通知异步及多个UI上游审查,不以“少于30处”判不通过,也不能因末端一行保存就认定简单。8运行文件、1固定Editor测试入口及本批独立测试目录为候选白名单,尚未批准编码。
|
||||
|
||||
重点:固定实例/固定Owner、不在回调后Resolve新服务;合法缺键与拒读区别;普通本地Accepted及可能提交错误;授权await、异常finally、旧UI/红点副作用;B2同实例Reset/Replace;设备通知与账号提示的分工;C未来绑定是否可行;整个升级/社交/奖励链仍未开放,不能越界宣称完成。检查方案§6全部验收能否实现且不靠测试替代实现;对原有非Editor编译问题区分新增回归。
|
||||
|
||||
输出总裁决、hash结果、逐条问题和最小修订、是否可实施及批准边界。只落盘报告到本目录《B01-独立方案审查.md》,不改共享入口/方案/代码,不运行Unity/真实API/测试账号,不生成许可或打包。主会话随后核hash并逐条裁决。流程内实现错误修复无需逐调用补审,合同/范围变化才补审。
|
||||
@@ -1,119 +0,0 @@
|
||||
# B01:通知提示与俱乐部已读状态批级方案
|
||||
|
||||
2026-09-28,主会话;待完整独立方案审查,未批准编码。本批属于4B3B的业务接线准备,**不是4B3C正式启用**。按F01–F12统一流程,一份方案、一轮方案审查、连续实施、一轮批末实施复核。
|
||||
|
||||
## 1. 当前分类与选批
|
||||
|
||||
复用既有B3DesignAudit扫描器,不修改扫描源码,在独立目录current-scan生成当前快照。旧223处全部可映射:B3-C050原PlayerData.SavePlayerData的写已提取到SerializeAndSavePlayerData,源调用保持;新增B3-N001是PlayFabMgr.StartKVGameplayLoopSync中的内部计时调用,源于之前Editor验证接线。本轮没有新增生产调用。
|
||||
|
||||
| 分类 | 当前数量 | 含义 |
|
||||
|---|---:|---|
|
||||
| 首批候选 | 3 | B3-C170、C177、C178,本方案详审 |
|
||||
| 待迁移分类 | 200 | 按业务/读取/生命周期/交易等分派,未完成各自上游完整审计 |
|
||||
| 已有限定处理、正式仍待 | 4 | 复用已有R1/4C/Editor证据,不能宣称正式路径全部完成 |
|
||||
| 保留PF待专项 | 13 | 旧PF兼容/身份/SDK/公共资料,不改成本人KV |
|
||||
| 本次排除 | 4 | GM路径,后续单列,不等于永久允许不迁移 |
|
||||
|
||||
逐项见《调用分类与现状映射.json》,旧ID保持稳定,当前扫描ID另列。方法名候选不是语义调用关系;当前224处也不是全部可机械替换的任务。扫描仍不涵盖所有反射/UnityEvent绑定和新的显式KV调用,不能以数字清零关闭B。
|
||||
|
||||
三个选择比较:
|
||||
|
||||
- 本批选择**通知提示/已读标记**:非消费、固定键;覆盖合法缺键、内存提交顺序、异步系统授权和多个UI入口,适合复用固定句柄的第一组业务合同。
|
||||
- 不按“前30个Update”选:足迹涉及高位保持与社交奖励,问卷涉及网络响应与奖励,不能因末端一行保存就混入低风险批。
|
||||
- 不先做定时器/加载发布:属于C,依赖B操作归属;不绕过原B→C门。
|
||||
|
||||
本批不改通知系统权限含义、经济公式、奖励、身份、俱乐部网络API或其他红点。ShowNotice仍是“已展示/处理过提示”而不是“操作系统已授权”;IsOpenClubRed写"1"仍表示已读,内存IsOpenClubRed=false。
|
||||
|
||||
## 2. B/C边界与固定归属
|
||||
|
||||
本批只实现固定Owner下的业务方法和调用者持有原服务的规则。**不修改SubBoostrap/LoginStage/UserService注册,不开放SaveBackend=kv,不获取4B1私有handle,不从PlayerKVGameplayLoop取句柄,不生成v4许可。** B验收使用既有合成session/Bridge;默认PF构造与已有受控Editor玩法保持。
|
||||
|
||||
复用PlayerKVGameSessionHandle与PlayFabMgr.ReadKV/WriteKV,不再造会话管理器/通用Registry/动态当前账号路由。为DefaultSettingService、ClubService提供internal固定KV构造入口:参数是非null、当前可读的既有handle,存于readonly字段;原无参构造继续表示PF。KV构造失败不能退回PF。LocalNotificationService的KV构造接收该次固定DefaultSettingService及配置依赖,不能在授权回调恢复后重新Resolve当前账号服务。
|
||||
|
||||
这些是生产可用的业务实现,不限UNITY_EDITOR;测试入口必须Editor或独立合成宿主。正式C尚未接入,不能因此宣称玩家现在已能使用此新KV路径。C未来必须为每次登录创建新的这些绑定实例,重新发布到游戏容器;同一实例不允许改绑新handle。旧实例仍只指向旧Owner,旧回调即使持有同一账号ID也失效。C还须撤销/退订旧实例及通知订阅;本批不把设备级通知服务生命周期重构提前完成。
|
||||
|
||||
B2受控加载的无参对象Reset/Replace继续照旧。固定KV实例不参与跨账号原地改绑;即便测试人为对旧实例Reset/Replace成新数据,旧Owner失效后也必须在写业务字段前拒绝。不得以ReferenceEquals(service)或账号ID相同代替Owner有效性检查。
|
||||
|
||||
本批不放行任何真实社交界面,也不声称整个ClubService已迁移。其捐赠、签到、网络回调及身份另批处理;C不得仅因SetOpenClubRed完成就启用所有俱乐部功能。
|
||||
|
||||
## 3. 每调用规则表
|
||||
|
||||
| 稳定ID/当前成员 | 上游与首次副作用 | 固定归属及处理 | 失败/验证 |
|
||||
|---|---|---|---|
|
||||
| B3-C177 DefaultSettingService.ShowNotice getter | CheckRequestPermission、通知面板;原首次修改为_showNotice缓存赋值 | 每次先ReadKV(构造捕获的Owner,"ShowNotice"),即使缓存已有也不可跳过有效性检查。成功且Found=false才返回false;Found=true严格沿用原值=="1"规则,其余原字符串为false,不规范化写回 | Stale/NotReady/LoadGuardActive拒绝,不能返回false冒充未提示;不赋缓存、不调用旧PF |
|
||||
| B3-C178 ShowNotice setter | CheckRequestPermission;等级通知弹窗DestroyUI;LocalNotificationService.OpenSettings授权完成。原首次修改为_showNotice=value | KV先检查Owner有效、计算目标"1"/"0",以固定Owner写;本地Accepted后再改_showNotice。同值只有当前有效快照已是目标值时才可不写,不能只看旧内存缓存 | Validation/Blocked/StorageError不改提示状态,不自动重试;可能提交保留原结果,不清pending、不回写旧值;失败不继续关闭界面/触发后续动作 |
|
||||
| B3-C170 ClubService.SetOpenClubRed | FishingSocialPanel.Start;原先IsOpenClubRed=false及SetClubRed发生在保存前 | KV先用固定Owner核有效并读取该键,按M14既有语义得到当前OpenRed(键缺失=true;键存在且值为任意非null字符串,包括空字符串/"0",均为false;不能改为仅"1"才已读);需要写时先WriteKV("IsOpenClubRed","1"),Accepted后再改字段并执行原SetClubRed。若有效快照已读则同步内存为false,不再次写 | 失败不改IsOpenClubRed、不发红点更新;旧服务在新Owner后不能改共享内存或红点。不能把此结果称远端Confirmed |
|
||||
|
||||
普通状态采用本地Accepted语义,随后由原会话同步器保存,不给这两个标记新增每点击必Flush或票据等待。原票据留在既有系统,不伪造远端已确认。Owner在写已接受后被撤销:保留A已接受的事实;再次检查有效性后才发布UI副作用,若失效停止显示更新,不回滚/重放写入。
|
||||
|
||||
结果表达沿用原PlayerKVKeyReadResult/PlayerKVEditResult,不新增另一套通用错误模型。现有bool属性/void入口在KV拒绝时抛出带错误分类的InvalidOperationException;UI和async void边界捕获、记录并停止本次动作,不能冒充false/成功。原提交不确定时在该绑定服务内记录故障阻断后续修改,保留原EditResult供限定诊断;不实现人工解冻。PF分支业务语义保持。
|
||||
|
||||
## 4. 完整通知上游与UI边界
|
||||
|
||||
存档调用少不等于只改保存方法。本批包括以下必要上游,统一在一份方案中处理:
|
||||
|
||||
| 入口 | 现状 | 本批合同 |
|
||||
|---|---|---|
|
||||
| LocalNotificationService.OpenSettings | initing先置true;await Init后重新Resolve ISettingService写ShowNotice;异常可能不复位initing | 启动前捕获原服务并验证其KV归属;使用async Task内部实现,由原void入口统一观察异常;finally只复位本实例initing。await后核原服务再写,不能Resolve新服务;无效时不安排后续账号相关通知/订阅 |
|
||||
| LocalNotificationService.Init | 非Editor系统授权await后调度通知并订阅EventRankData | KV模式捕获本次设置服务,在请求前及await后核原Owner;过期则不调度/订阅或设置isInit。系统已弹出的权限结果不能撤销,必须明确不代表可写新账号。PF原分支保留 |
|
||||
| SettingNotificationsGradePopupPanel | 点同意先调用DestroyUI(写ShowNotice),再打开系统设置;取消也写已提示 | 初始化时捕获设置和通知服务;回调先校验旧实例/原Owner,提示写被拒则停止,不关闭别人的UI、不打开设置。同意/取消的原业务含义保持;同一处理中的重复点击不重复请求 |
|
||||
| SettingNotificationsPopupPanel | 同意关闭后Resolve通知服务 | 初始化时捕获通知服务;点击前校验,先拒绝陈旧入口,再执行原流程;不在延迟操作中重新取当前服务 |
|
||||
| SettingPopupPanel.OnToggleNotification | 开启时Resolve通知服务;关闭时ShowUI弹窗 | 沿用已有settingService引用,初始化时捕获配套通知服务;点击前核归属,不由旧界面启动新账号操作。新弹窗创建归属由未来C UI生命周期统一保证,本批不声称修复所有UI加载回流 |
|
||||
| GiftNoticePanel.OnClickYES | 关闭后Resolve通知服务 | 仅通知按钮同上;不改礼包展示/领取,也不开放该奖励业务。原OnClickNO的设备PlayerPrefs不迁移 |
|
||||
| DebugFunc.NotifySettingTest | 同步Resolve到局部service再调用 | 原代码已捕获局部引用,不新增存档;保持不改,LocalNotificationService统一校验。不以GM作为真实验收入口 |
|
||||
| PlayerData.LevelChange→CheckRequestPermission | 调用前已有升级、地图和事件副作用 | 本批仅保证CheckRequestPermission内部提示读写拒绝不会变成false或写新Owner;**整个LevelChange的上游归属未完成**,留核心进度批,C前不得用本批声明升级全链安全 |
|
||||
| FishingSocialPanel.Awake/Start→SetOpenClubRed | Awake已捕获ClubService,Start先执行社交UI/API行为,再标记已读 | 固定KV模式在对应UI入口开始处核已捕获ClubService的有效性,至少在本批红点修改前核原Owner;不Resolve替代服务。调用失败记录并停止该段后续,整个社交API链另批,不扩大本批验收 |
|
||||
|
||||
无需修改ISettingService/ILocalNotificationService的公开签名。内部可用具体服务的受限检查方法供同程序集UI使用;无参PF服务检查保持原行为,不检查当前全局SaveBackend来切路。UI原有无关按钮、格式和资产不动。失效旧UI只停止旧回调,不能用类型名销毁可能属于新会话的同名窗口。
|
||||
|
||||
错误日志只记键名、Owner标识和错误类别,不记Cookie或完整存档。系统权限已经改变与游戏提示标记保存失败分别记录;不以存档失败反向重复请求系统权限。
|
||||
|
||||
## 5. 白名单与明确不做
|
||||
|
||||
运行源码限8个既有文件(均在Client/Assets/Scripts下):
|
||||
|
||||
1. Services/SettingService.cs:固定KV构造、ShowNotice读写、检查/错误边界;其他本地设置不改。
|
||||
2. Services/ILocalNotificationService.cs:固定依赖构造、原实例捕获、OpenSettings/Init的恢复校验和finally;调度公式不改。
|
||||
3. Services/ClubService.cs:固定KV构造、已读标记读取/提交/检查;不改捐赠/签到/身份/API实现。
|
||||
4. UI/Setting/SettingNotificationsGradePopupPanel.cs:原服务捕获、点击检查及失败中止。
|
||||
5. UI/Setting/SettingNotificationsPopupPanel.cs:同上。
|
||||
6. UI/Setting/SettingPopupPanel.cs:只处理通知入口。
|
||||
7. UI/Shop/GiftNoticePanel.cs:只处理通知入口,不涉及礼包奖励。
|
||||
8. UI/Social/FishingSocialPanel.cs:固定ClubService的本批提示状态入口校验和错误边界。
|
||||
|
||||
测试允许新增 `CodexOutput/upserver/B3BatchMigration/B01Tests/` 下独立宿主;如需真实Unity对象验证,新增 `Client/Assets/Editor/KVPromptStateValidation.cs` 及其meta,仅暴露固定B01用例入口、空闲Editor运行,使用fake transport。禁止任意反射方法执行器/真实API降级,不改原测试以掩盖回归。
|
||||
|
||||
测试优先编译/调用实际源文件并用最小必要平台shim;Unity验证实际服务/面板,不以重新手写一套算法替代生产入口。平台权限在离线shim或Editor分支模拟,不触发用户系统权限弹窗/推送。若必须新增其他生产文件或改公开接口/注册逻辑,先具体补审,不能借白名单延伸为通用框架。
|
||||
|
||||
不改Core/PlayFabMgr、Owner/Bridge、B2加载器与31模块合同、UserService/注册、通知包、数据格式、PF SDK。这里的默认PF兼容不授权KV失败回落PF。历史Editor KV Loop仍是已关闭验证链,未因此扩展或重新运行。
|
||||
|
||||
## 6. 验收矩阵
|
||||
|
||||
| 项 | 必须验证 |
|
||||
|---|---|
|
||||
| P01 正常读/缺键 | ShowNotice缺键false、"1"true、"0"/空字符串按旧规则false;只有成功快照的缺键才缺省,不自动写 |
|
||||
| P02 拒读 | Stale/Closing/NotReady/加载guard活跃时不赋缓存,不当false,不转PF,即使_showNotice已有值也拒绝 |
|
||||
| P03 普通提交 | 首次写正确原字符串/固定Owner,Accepted后才改内存/红点;同值不重复写;记录原票据但不声称已远端确认 |
|
||||
| P04 故障 | Validation/Blocked/StorageError以及LocalCommitMayHaveSucceeded:不继续UI/系统副作用,不回滚、不自动重试;故障阻断后续修改 |
|
||||
| P05 旧回调 | A通知授权挂起→撤销A→B创建→完成A;B缓存、ShowNotice、红点、网络请求均不变,A不重新Resolve B;同账号新epoch也覆盖 |
|
||||
| P06 同对象污染 | 人为让旧Club实例Reset/Replace装入另一组数据后再触发旧操作,原Owner检查必须在字段/红点变化前拒绝;不以对象引用相等过关 |
|
||||
| P07 权限时序 | 重复点击/授权失败/异常均观察并finally清initing;过期后不设置isInit、不新增通知订阅;系统授权与存档标记语义分离 |
|
||||
| P08 UI入口 | 四个通知UI入口和Social标记调用捕获原服务;旧实例回调不销毁新同名UI,不触发新账号系统/存档操作;失败停止后续段 |
|
||||
| P09 非消费范围 | 礼包展示、领取/捐赠/签到/升级经济数据不改;原无参PF入口及设备本地设置语义保持 |
|
||||
| P10 回归与编译 | 既有42/86/29等适用基础回归、4B3A固定入口、B2 M14/31模块受影响回归;Unity Editor编译及适用SERVICE_HOST构建。平台分支做编译验证,已记录默认非Editor错误单列,不掩盖新增错误 |
|
||||
|
||||
fake transport必须固定.invalid地址且绝不转发。关闭原合成Owner并排空后释放临时缓存。旧样本/真实101、102/permit/claim不变。B01不要求真实账号写入或再钓鱼;真实正式路径和连续操作属于后续C/4D集成范围。
|
||||
|
||||
## 7. 检查点与交付
|
||||
|
||||
0. 方案完整审查、主会话核hash和逐项裁决,批准8文件及固定测试范围。
|
||||
1. 批内实现固定绑定与两键规则、所有列明通知上游;相应故障/迟到场景先建有效失败用例,再修正。每检查点更新日志,不逐调用停下。
|
||||
2. 跑专项与适用回归、Unity编译;合同内失败修复并回归继续,保留首败;合同/范围/架构变化才补审。
|
||||
3. 原文件diff/范围核对,封存源码、原始测试输出、hash,形成一份实施交付与独立复核提示词。
|
||||
4. 一次独立实施复核,主会话裁决关闭B01;3处从“首批候选”改为“B业务实现关闭,待C正式接线”,不声称整个4B3B/C或两条完整用户玩法已启用。
|
||||
|
||||
## 8. 自审与需独立裁定的重点
|
||||
|
||||
作者自审:不按调用数量凑批;所有存档点与实际通知入口列明;复用原Owner而无全局动态句柄;无真实数据操作;B→C门未松动;未称整个升级/社交/礼包链已安全。首批是可单独实现和验证的非消费业务能力,后续正式发布仍须完整B/C依赖。
|
||||
|
||||
请独立审查重点判断:固定实例构造作为B准备是否足够且最小;C创建新实例的前置是否与现有受控加载可衔接;普通Accepted后的内存/红点时序是否正确;通知系统权限是设备级但提示标记是账号级,两者的失效处理是否有遗漏;当前8文件是否遗漏必要上游。若此边界不能成立,指出具体调用链和最小修订,不直接扩大到全部服务或免除批级方案门。
|
||||
@@ -1,35 +0,0 @@
|
||||
import json,hashlib,re
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
m=Path('CodexOutput/upserver/B3BatchMigration/M2')
|
||||
def read(p):return json.loads(p.read_text(encoding='utf-8-sig'))
|
||||
def item(p):
|
||||
p=Path(p);raw=p.read_bytes();return {'path':p.as_posix(),'bytes':len(raw),'sha256':hashlib.sha256(raw).hexdigest().upper()}
|
||||
original={x['path']:x for x in read(m/'M2-合批正式受审基线.json')['items']}
|
||||
c=read(m/'M2-合批-C2c检查点与交付hash.json');prior=dict(original);prior.update({x['path']:x for x in c['items']})
|
||||
current=[item(p) for p in sorted(prior)]
|
||||
changed=[x for x in current if x['path'] not in original or x['sha256']!=original[x['path']]['sha256']]
|
||||
delta=[x['path'] for x in current if x['sha256']!=prior[x['path']]['sha256']]
|
||||
allowed={x['path'] for x in read(m/'M2-合批正式候选白名单.json')['files']}|{'AGENTS.md','Client/Assets/Scripts/Services/UserService.KVFishingValidation.cs','Client/Assets/Scripts/Services/UserService.KVFishingRewardValidation.cs'}
|
||||
unexpected=[x['path'] for x in changed if x['path'] not in allowed and x['path'].endswith(('.cs','.meta','.asmdef','.csproj'))];assert not unexpected,unexpected
|
||||
assert item('Client/Assets/Scripts/UI/Home/HomePanel.cs')['sha256']==original['Client/Assets/Scripts/UI/Home/HomePanel.cs']['sha256']
|
||||
assert item(m/'M2-合批正式受审基线.json')['sha256']=='DBC965EDBF48FADA4C1508F2662562F4FD72C01711D8E2FE2022AC4956896EF3'
|
||||
assert item(m/'M2-合批正式独立方案审查-2026-09-29.md')['sha256']=='BB02441CFFE72AF18CF3B0D78F8E581FC39549C3F77E45257B5ED16CADCF2937'
|
||||
compile=read(m/'M2-C2d-final-compile.json');assert compile['Success'] and compile['ErrorCount']==0
|
||||
results={}
|
||||
for name in ['consume','reward']:
|
||||
d=read(m/f'M2-C2d-final-{name}.json');r=json.loads(d['Result']);assert d['Success'] and r['success'] and r['formalRequests']==0
|
||||
results[name]={k:r[k] for k in ['groups','assertions','formalRequests']}
|
||||
d=read(m/'M2-C2d-final-modules.json');assert d['Success'];r=d['Result']
|
||||
a=json.loads(re.search(r'pureResult = (\{.*?\}), moduleResult',r)[1]);b=json.loads(re.search(r'moduleResult = (\{.*?\}), playing',r)[1]);assert a['success'] and b['success'] and b['restored'] and b['realModules']==31
|
||||
state=json.loads(read(m/'M2-C2d-final-state.json')['Result']);assert not any(state[k] for k in ['playing','enabled','ready','permitExists'])
|
||||
for name in ['NON_ANDROID','ANDROID_AGG']:
|
||||
assert 'groups=18 assertions=254 formalRequests=0' in (m/f'M2-C2d-final-ServiceHost-{name}.log').read_text(encoding='utf-8-sig')
|
||||
recovered=[]
|
||||
for p in [Path('CodexOutput/upserver/KVGameplayLoopTests/fishing-fixed-runs/20260930T032500_c51bfd787cac474983d7863461e379e8/result.json'),Path('CodexOutput/upserver/KVGameplayLoopTests/reward-fixed-runs/20260930T032547_cae307d3ed07409a8a3c363c460357fa/result.json')]:
|
||||
assert read(p)['success'];recovered.append(item(p))
|
||||
(m/'M2-C2d-合并CLI超时核对.json').write_text(json.dumps({'cliSucceeded':False,'cliError':'Request timed out after 180000ms','underlyingResults':recovered,'followup':'Final individual CLI outputs are authoritative.'},ensure_ascii=False,indent=2),encoding='utf-8')
|
||||
evidencePaths=list(m.glob('M2-C2d-*.json'))+list(m.glob('M2-C2d-*.log'))+list(m.glob('C2d-*.py.txt'))+[m/'C2d修改前快照/index.json',m/'M2-纯平移用户审查确认与C2d实施边界-2026-09-30.json',m/'M2-纯平移主会话裁决与C2d实施记录-2026-09-30.md']
|
||||
report={'date':datetime.now().astimezone().isoformat(),'stage':'C2d 纯平移核减与旧验证宿主注入完成;C3真实集成未验','reviewBasis':'用户明确审核完毕并继续,详见用户确认JSON;未声称收到新独立报告','originalReviewReportSha256':item(m/'M2-合批正式独立方案审查-2026-09-29.md')['sha256'],'originalManifestSha256':item(m/'M2-合批正式受审基线.json')['sha256'],'priorCheckpointSha256':item(m/'M2-合批-C2c检查点与交付hash.json')['sha256'],'combinedItemCount':len(current),'changedSinceC2c':delta,'outsideCodeWhitelist':unexpected,'unchangedOriginal':sum(x['path'] in original and x['sha256']==original[x['path']]['sha256'] for x in current),'items':changed,'homePanelUnchanged':True,'compile':{k:compile[k] for k in ['Success','ErrorCount','WarningCount']},'offlineResults':results,'modules':{'M08-M23':a,'B2-20':b},'serviceHost':{'NON_ANDROID':{'groups':18,'assertions':254},'ANDROID+AGG':{'groups':18,'assertions':254}},'evidence':[item(p) for p in sorted(set(evidencePaths)) if p.stat().st_size>0],'runtimeVerified':False,'formalRequests':0}
|
||||
out=m/'M2-合批-C2d检查点与交付hash.json';out.write_text(json.dumps(report,ensure_ascii=False,indent=2),encoding='utf-8')
|
||||
print(json.dumps({'combined':len(current),'cumulativeChanged':len(changed),'unchangedOriginal':report['unchangedOriginal'],'changedSinceC2c':len(delta),'outsideCodeWhitelist':unexpected,'checkpointSha256':item(out)['sha256']}))
|
||||
@@ -1,93 +0,0 @@
|
||||
import hashlib, json, subprocess
|
||||
from pathlib import Path
|
||||
root=Path('Client/Assets/Scripts')
|
||||
m=Path('CodexOutput/upserver/B3BatchMigration/M2')
|
||||
baseline={x['path']:x for x in json.loads((m/'M2-合批正式受审基线.json').read_text(encoding='utf-8-sig'))['items']}
|
||||
def original(suffix, require_match=True):
|
||||
path=root/suffix
|
||||
raw=subprocess.run(['git','show','HEAD:'+path.as_posix()],capture_output=True,check=True).stdout
|
||||
s=raw.decode('utf-8-sig').replace('\r\n','\n')
|
||||
variants=[s.encode('utf-8'),s.encode('utf-8-sig'),s.replace('\n','\r\n').encode('utf-8'),s.replace('\n','\r\n').encode('utf-8-sig')]
|
||||
if require_match:
|
||||
assert any(hashlib.sha256(b).hexdigest().upper()==baseline[path.as_posix()]['sha256'] for b in variants),suffix
|
||||
return s
|
||||
def write(suffix,s):
|
||||
path=root/suffix; raw=path.read_bytes()
|
||||
text=s.replace('\r\n','\n')
|
||||
if b'\r\n' in raw: text=text.replace('\n','\r\n')
|
||||
path.write_bytes(text.encode('utf-8-sig' if raw.startswith(b'\xef\xbb\xbf') else 'utf-8'))
|
||||
def calls(s,old,new,tail):
|
||||
pos=0
|
||||
while True:
|
||||
start=s.find(old+'(',pos)
|
||||
if start<0:return s
|
||||
if s[s.rfind('\n',0,start)+1:start].lstrip().startswith('//'):
|
||||
pos=start+len(old);continue
|
||||
i=start+len(old);depth=0;quote=None;esc=False
|
||||
while i<len(s):
|
||||
c=s[i]
|
||||
if quote:
|
||||
if esc:esc=False
|
||||
elif c=='\\':esc=True
|
||||
elif c==quote:quote=None
|
||||
elif c in ('"',"'"):quote=c
|
||||
elif c=='(':depth+=1
|
||||
elif c==')':
|
||||
depth-=1
|
||||
if depth==0:break
|
||||
i+=1
|
||||
assert depth==0
|
||||
text=new+s[start+len(old):i+1]+tail
|
||||
s=s[:start]+text+s[i+1:];pos=start+len(text)
|
||||
def storage(s,name):
|
||||
s=calls(s,'PlayFabMgr.Instance.GetLocalData',name+'.Read','.ValueOrThrow()')
|
||||
return calls(s,'PlayFabMgr.Instance.UpdateUserDataValue',name+'.Write','.RequireAccepted()')
|
||||
|
||||
# All ten source bodies below were independently matched against the frozen formal baseline.
|
||||
for f in ['Services/ChatService.cs','Services/InGameMailHandler.cs','DataCenter/DataCenter.PlayerItemData.cs','DataCenter/PlayerItemData_Inflation.cs','UI/Setting/SurveyPanel.cs','UI/Friends/EventInviteFriends.cs']:
|
||||
write(f,original(f))
|
||||
s=original('Services/SurveyService.cs')
|
||||
s=s.replace(' IUserService userService;',' readonly game.PlayerDataStorage.IPlayerSaveStore saveStore;\n IUserService userService;',1)
|
||||
s=s.replace('public SurveyService(IUserService userService, cfg.Tables tables)','public SurveyService(IUserService userService, cfg.Tables tables, game.PlayerDataStorage.IPlayerSaveStore saveStore)',1)
|
||||
s=s.replace(' this.userService = userService;',' this.userService = userService;\n this.saveStore = saveStore;',1)
|
||||
s=s.replace('Convert.ToUInt64(userService.UserId, 16)','Convert.ToUInt64(userService.UserId, saveStore is game.PlayerDataStorage.KVPlayerSaveStore ? 10 : 16)',1)
|
||||
write('Services/SurveyService.cs',storage(s,'saveStore'))
|
||||
s=original('Services/FriendService.cs')
|
||||
s=s.replace(' public class FriendService\n {',' public class FriendService\n {\n [Inject] public PlayerDataStorage.IPlayerSaveStore SaveStore { get; set; }',1)
|
||||
write('Services/FriendService.cs',storage(s,'SaveStore'))
|
||||
s=original('DataCenter/DataCenter.PlayerShopData.cs')
|
||||
binding=''' game.PlayerDataStorage.IPlayerSaveStore boundSaveStore;
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore SaveStore
|
||||
{
|
||||
get => boundSaveStore;
|
||||
set { CouponData.BindSaveStore(value); boundSaveStore = value; }
|
||||
}
|
||||
'''
|
||||
s=s.replace(' public partial class PlayerShopData : IDisposable\n {',' public partial class PlayerShopData : IDisposable\n {\n'+binding,1)
|
||||
write('DataCenter/DataCenter.PlayerShopData.cs',storage(s,'SaveStore'))
|
||||
s=original('UI/Turntable/FishingTurntablePanel.cs')
|
||||
s=s.replace(' Tables _tables;',' game.PlayerDataStorage.IPlayerSaveStore saveStore;\n Tables _tables;',1)
|
||||
s=s.replace(' void Awake()\n {',' void Awake()\n {\n saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();',1)
|
||||
s=calls(s,'await PlayFabMgr.Instance.SyncDataImmidiate','(await saveStore.WriteAndConfirmAsync',').Confirmed')
|
||||
write('UI/Turntable/FishingTurntablePanel.cs',s)
|
||||
|
||||
# Preserve all pre-existing uncommitted KV-loop/prompt work: replace only the inspected business methods.
|
||||
f='GampPlay/FishingB/FishingBehaviorB.cs';s=(root/f).read_text(encoding='utf-8-sig');old=original(f,False)
|
||||
start=old.index(' private void DoConsumeBait()');end=old.index(' void LoadFish()',start)
|
||||
body=calls(old[start:end],'await PlayFabMgr.Instance.SyncDataImmidiate','(await saveStore.WriteAndConfirmAsync',').Confirmed')
|
||||
start=s.index(' private async void DoConsumeBait()');end=s.index(' void LoadFish()',start)
|
||||
s=s[:start]+body+s[end:]
|
||||
for line in [' game.IUserService loginUser;\n',' bool consumptionPending;\n',' bool SaveSessionCurrent => this != null && isActiveAndEnabled && saveStore != null && saveStore.IsCurrent && !loginUser.LoginLifetime.IsCancellationRequested;\n',' loginUser = GContext.container.Resolve<game.IUserService>();\n']:
|
||||
assert line in s,line;s=s.replace(line,'',1)
|
||||
write(f,s)
|
||||
f='Services/ClubService.cs';s=(root/f).read_text(encoding='utf-8-sig');old=original(f,False)
|
||||
start=old.index(' private int? signingSession;');end=old.index(' public void SendSystemMessage2',start)
|
||||
body=storage(old[start:end],'SaveStore')
|
||||
start=s.index(' private int? signingSession;');end=s.index(' public void SendSystemMessage2',start)
|
||||
write(f,s[:start]+body+s[end:])
|
||||
# This present wrapper was introduced only by C2c; retain the surrounding original deferred reward queue.
|
||||
f='Services/DeferredRewardStashService.cs';s=(root/f).read_text(encoding='utf-8-sig')
|
||||
s=s.replace('game.PlayerDataStorage.PlayerSaveAction.Present(() => GContext.Publish(new ShowData()));','GContext.Publish(new ShowData());')
|
||||
s=s.replace('PlayerSaveAction.Present(() => GContext.Publish(new ShowData()));','GContext.Publish(new ShowData());')
|
||||
write(f,s)
|
||||
print('Verified baseline + minimum storage/identity substitutions applied; historical partial-file work retained.')
|
||||
@@ -1,52 +0,0 @@
|
||||
from pathlib import Path
|
||||
m=Path('CodexOutput/upserver/B3BatchMigration/M2')
|
||||
# Reuse only the previously audited helper definitions, never replay prior edits.
|
||||
exec((m/'C2d-按核证基线核减.py.txt').read_text(encoding='utf-8-sig').split('# All ten source bodies')[0])
|
||||
def read(f):return (root/f).read_text(encoding='utf-8-sig')
|
||||
def replace(f,a,b):
|
||||
s=read(f); assert s.count(a)==1,(f,a); write(f,s.replace(a,b,1))
|
||||
for f,klass in [('Activity/Data/EventDataRepository.cs','EventDataRepository'),('Activity/Services/EventTokenService.cs','EventTokenService')]:
|
||||
s=original(f)
|
||||
start=s.index('\n {',s.index('public class '+klass))+len('\n {')
|
||||
s=s[:start]+'\n private readonly game.PlayerDataStorage.IPlayerSaveStore store;\n public '+klass+'(game.PlayerDataStorage.IPlayerSaveStore store)\n { this.store = store ?? throw new ArgumentNullException(nameof(store)); }\n'+s[start:]
|
||||
s=s.replace('PlayFabMgr.Instance?.GetLocalData','PlayFabMgr.Instance.GetLocalData').replace('PlayFabMgr.Instance?.UpdateUserDataValue','PlayFabMgr.Instance.UpdateUserDataValue')
|
||||
write(f,storage(s,'store'))
|
||||
# Retain current store bindings and session checks. Restore only original parsing behavior.
|
||||
for f in ['DataCenter/TotalRechargeManager.cs','MatchGame/MatchGameManager.cs','MiniBP/MiniBattlePassDataProvider.cs','EventLuckGame/EventLuckyGameModel.cs','EventWelcomeBack/EventWelcomeBackManager.cs','UI/AdWheel/AdWheelManager.cs','UI/OfferSweep/OfferSweepManager.cs','UI/JigsawPuzzle/JigsawPuzzleManager.cs']:
|
||||
old=original(f); current=read(f)
|
||||
# These files have only storage wiring + the inspected parsing changes; keep the current binding.
|
||||
binding=' readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();\n\n'
|
||||
assert current.count(binding)==1,f
|
||||
prefix=current.split(binding)[0]
|
||||
# Locate class opening in original without relying on line endings/BOM.
|
||||
anchor='\n'.join(prefix.splitlines()[-2:])+'\n'
|
||||
at=old.index(anchor)+len(anchor)
|
||||
s=old[:at]+binding+old[at:]
|
||||
s=storage(s,'saveStore')
|
||||
if f=='MiniBP/MiniBattlePassDataProvider.cs':
|
||||
s=s.replace(' public MiniBattlePassRecord GetMiniBattlePassRecord(string key)\n {',' public MiniBattlePassRecord GetMiniBattlePassRecord(string key)\n {\n if (!saveStore.IsCurrent) throw new System.InvalidOperationException("Expired battle pass session.");')
|
||||
write(f,s)
|
||||
f='Event/AEventDataManager.cs'
|
||||
old=original(f);s=read(f)
|
||||
a=s.index(' var read = SaveStore.Read(typeof(T).Name);')
|
||||
b=s.index('\n }',a)
|
||||
oa=old.index(' var dataStr = PlayFabMgr.Instance.GetLocalData(typeof(T).Name);')
|
||||
ob=old.index('\n }',oa)
|
||||
write(f,s[:a]+storage(old[oa:ob],'SaveStore')+s[b:])
|
||||
# Exact original archive branches, including the old null behavior; retain async session checks.
|
||||
for f in ['DataCenter/FishingEventData_OneOne.cs','DataCenter/FishingEventData_OneTwo.cs']:
|
||||
write(f,storage(original(f),'playerSaveStore'))
|
||||
f='DataCenter/FishingEvent_PVPData.cs';s=read(f);old=original(f)
|
||||
for method in ['public void SoloCheckClaimReward()','public void InitSoloData(FishingEvent t)']:
|
||||
a=s.index(method);b=s.index('\n public ',a+len(method))
|
||||
oa=old.index(method);ob=old.index('\n public ',oa+len(method))
|
||||
s=s[:a]+storage(old[oa:ob],'playerSaveStore')+s[b:]
|
||||
write(f,s)
|
||||
f='DataCenter/DataCenter.FishingEventData.cs';s=read(f)
|
||||
a=s.index(' T ReadEventArchive<T>');b=s.index(' private cfg.Tables _tables;',a)
|
||||
s=s[:a]+s[b:]
|
||||
for line in [' List<Exception> errors = null;\n',' (errors ??= new List<Exception>()).Add(e);\n',' if (errors != null) throw new AggregateException("Activity refresh did not complete.", errors);\n']:
|
||||
assert s.count(line)==1,line
|
||||
s=s.replace(line,'',1)
|
||||
write(f,s)
|
||||
print('Restored original activity flows; retained store wiring and existing session isolation.')
|
||||
@@ -1,68 +0,0 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Activity.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// 使用本次登录绑定的存档提供方,保留活动类型名作为存档键。
|
||||
/// </summary>
|
||||
public class EventDataRepository : IEventDataRepository
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore store;
|
||||
public EventDataRepository(game.PlayerDataStorage.IPlayerSaveStore store)
|
||||
{ this.store = store ?? throw new ArgumentNullException(nameof(store)); }
|
||||
public void Save<T>(T data) where T : IEventData
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
Debug.LogWarning($"[EventDataRepository] Cannot save null data for {typeof(T).Name}");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(data);
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"[EventDataRepository] Saving {typeof(T).Name}: {json}");
|
||||
#endif
|
||||
store.Write(typeof(T).Name, json).RequireAccepted();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventDataRepository] Failed to save {typeof(T).Name}: {e.Message}\n{e.StackTrace}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public T Load<T>() where T : IEventData, new()
|
||||
{
|
||||
try
|
||||
{
|
||||
var read = store.Read(typeof(T).Name);
|
||||
var dataStr = read.ValueOrThrow();
|
||||
if (read.Found == false) return new T();
|
||||
if (string.IsNullOrWhiteSpace(dataStr) || dataStr.Trim() == "null")
|
||||
throw new JsonSerializationException("Stored activity data cannot be empty or null.");
|
||||
// #if UNITY_EDITOR
|
||||
// Debug.Log($"[EventDataRepository] Load {typeof(T).Name}: {dataStr}");
|
||||
// #endif
|
||||
var data = JsonConvert.DeserializeObject<T>(dataStr);
|
||||
if (data == null) throw new JsonSerializationException("Stored activity data cannot be null.");
|
||||
return data;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventDataRepository] Failed to load {typeof(T).Name}: {e.Message}\n{e.StackTrace}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Exists<T>() where T : IEventData
|
||||
{
|
||||
var read = store.Read(typeof(T).Name);
|
||||
read.ValueOrThrow();
|
||||
return read.Found.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,329 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using asap.core;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Activity
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="Services.IEventTokenService"/> 入账/扣账成功后由 <see cref="Services.EventTokenService"/> 发布,
|
||||
/// 供各活动自行判断是否刷新入口合并红点等(避免订阅者与具体玩法耦合)。
|
||||
/// </summary>
|
||||
public readonly struct ActivityEventTokenBalanceChanged
|
||||
{
|
||||
public int EventId { get; }
|
||||
|
||||
public ActivityEventTokenBalanceChanged(int eventId)
|
||||
{
|
||||
EventId = eventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Activity.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 活动代币服务实现
|
||||
/// 负责管理所有活动的代币数据,提供统一的存储接口
|
||||
/// </summary>
|
||||
public class EventTokenService : IEventTokenService
|
||||
{
|
||||
#region 常量与字段
|
||||
|
||||
private const string TransitionDataKey = "TransitionData";
|
||||
|
||||
private Dictionary<int, int> _tokenData = new Dictionary<int, int>();
|
||||
private readonly object _lock = new object();
|
||||
private bool _isInitialized;
|
||||
private readonly game.PlayerDataStorage.IPlayerSaveStore store;
|
||||
public EventTokenService(game.PlayerDataStorage.IPlayerSaveStore store)
|
||||
{ this.store = store ?? throw new ArgumentNullException(nameof(store)); }
|
||||
#endregion
|
||||
|
||||
#region 初始化
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (!store.IsCurrent) throw new InvalidOperationException("Activity token session is no longer current.");
|
||||
if (_isInitialized) return;
|
||||
var read = store.Read(TransitionDataKey);
|
||||
var value = read.ValueOrThrow();
|
||||
Dictionary<int, int> loaded;
|
||||
if (read.Found == false) loaded = new Dictionary<int, int>();
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
throw new Newtonsoft.Json.JsonSerializationException("Stored activity tokens cannot be empty.");
|
||||
loaded = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<int, int>>(value);
|
||||
if (loaded == null) throw new Newtonsoft.Json.JsonSerializationException("Stored activity tokens cannot be null.");
|
||||
}
|
||||
_tokenData = loaded;
|
||||
_isInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Publish the candidate balance only after the current store accepts its exact JSON.
|
||||
// Acceptance remains distinct from cloud confirmation, which belongs to the enclosing action.
|
||||
private void SaveTokens(Dictionary<int, int> candidate)
|
||||
{
|
||||
store.Write(TransitionDataKey, Newtonsoft.Json.JsonConvert.SerializeObject(candidate)).RequireAccepted();
|
||||
_tokenData = candidate;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 基础操作
|
||||
|
||||
public void AddToken(int eventId, int count)
|
||||
{
|
||||
if (eventId <= 0)
|
||||
{
|
||||
Debug.LogWarning($"[EventTokenService] Invalid eventId: {eventId}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
EnsureInitialized();
|
||||
var currentCount = GetTokenCount(eventId);
|
||||
var candidate = new Dictionary<int, int>(_tokenData);
|
||||
candidate[eventId] = currentCount + count;
|
||||
SaveTokens(candidate);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"[EventTokenService] Added {count} tokens to event {eventId}, new total: {_tokenData[eventId]}");
|
||||
#endif
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventTokenService] AddToken failed for event {eventId}: {e.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
PublishActivityEventTokenBalanceChanged(eventId);
|
||||
}
|
||||
|
||||
public int RemoveToken(int eventId, int count)
|
||||
{
|
||||
if (eventId <= 0)
|
||||
{
|
||||
Debug.LogWarning($"[EventTokenService] Invalid eventId: {eventId}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int actualRemoved = 0;
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
EnsureInitialized();
|
||||
var currentCount = GetTokenCount(eventId);
|
||||
actualRemoved = Mathf.Min(count, currentCount);
|
||||
var newCount = currentCount - actualRemoved;
|
||||
|
||||
var candidate = new Dictionary<int, int>(_tokenData);
|
||||
if (newCount <= 0)
|
||||
{
|
||||
candidate.Remove(eventId);
|
||||
}
|
||||
else
|
||||
{
|
||||
candidate[eventId] = newCount;
|
||||
}
|
||||
|
||||
SaveTokens(candidate);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"[EventTokenService] Removed {actualRemoved} tokens from event {eventId}, remaining: {newCount}");
|
||||
#endif
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventTokenService] RemoveToken failed for event {eventId}: {e.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
PublishActivityEventTokenBalanceChanged(eventId);
|
||||
|
||||
return actualRemoved;
|
||||
}
|
||||
|
||||
public int GetTokenCount(int eventId)
|
||||
{
|
||||
if (eventId <= 0) return 0;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
EnsureInitialized();
|
||||
return _tokenData.GetValueOrDefault(eventId, 0);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventTokenService] GetTokenCount failed for event {eventId}: {e.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetToken(int eventId, int count)
|
||||
{
|
||||
if (eventId <= 0)
|
||||
{
|
||||
Debug.LogWarning($"[EventTokenService] Invalid eventId: {eventId}");
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
EnsureInitialized();
|
||||
var candidate = new Dictionary<int, int>(_tokenData);
|
||||
candidate[eventId] = count;
|
||||
SaveTokens(candidate);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"[EventTokenService] Set event {eventId} token count to {count}");
|
||||
#endif
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventTokenService] SetToken failed for event {eventId}: {e.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearToken(int eventId)
|
||||
{
|
||||
if (eventId <= 0) return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
EnsureInitialized();
|
||||
var candidate = new Dictionary<int, int>(_tokenData);
|
||||
if (candidate.Remove(eventId))
|
||||
{
|
||||
SaveTokens(candidate);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventTokenService] ClearToken failed for event {eventId}: {e.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 批量操作
|
||||
|
||||
public Dictionary<int, int> GetAllTokens()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
EnsureInitialized();
|
||||
return new Dictionary<int, int>(_tokenData);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTokens(Dictionary<int, int> tokens)
|
||||
{
|
||||
if (tokens == null) return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
EnsureInitialized();
|
||||
var candidate = new Dictionary<int, int>();
|
||||
foreach (var kvp in tokens)
|
||||
{
|
||||
if (kvp.Key > 0)
|
||||
{
|
||||
candidate[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
SaveTokens(candidate);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"[EventTokenService] Set {tokens.Count} token entries");
|
||||
#endif
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventTokenService] SetTokens failed: {e.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearTokens(IEnumerable<int> eventIds)
|
||||
{
|
||||
if (eventIds == null) return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
EnsureInitialized();
|
||||
var candidate = new Dictionary<int, int>(_tokenData);
|
||||
bool changed = false;
|
||||
foreach (var eventId in eventIds)
|
||||
{
|
||||
if (candidate.Remove(eventId))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
{
|
||||
SaveTokens(candidate);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventTokenService] ClearTokens failed: {e.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 私有辅助方法
|
||||
|
||||
private void EnsureInitialized()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
private static void PublishActivityEventTokenBalanceChanged(int eventId)
|
||||
{
|
||||
try
|
||||
{
|
||||
GContext.Publish(new Activity.ActivityEventTokenBalanceChanged(eventId));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// GContext 未就绪等
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,700 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
public class AquariumManager
|
||||
{
|
||||
public const string AquariumEventDataNewKey = "AquariumEventDataNewKey";
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore;
|
||||
readonly IUserService loginUser;
|
||||
private cfg.Tables _tables;
|
||||
private ICustomServerMgr customServerMgr;
|
||||
public AquariumManagerData data { get; private set; }
|
||||
public int MapId => data.mapId;
|
||||
public int Slot => data.slot;
|
||||
public List<string> eggNames;
|
||||
public List<AquariumSlotData> sellList;
|
||||
public AquariumManager()
|
||||
{
|
||||
saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
loginUser = GContext.container.Resolve<IUserService>();
|
||||
_tables = GContext.container.Resolve<Tables>();
|
||||
customServerMgr = GContext.container.Resolve<ICustomServerMgr>();
|
||||
}
|
||||
public static AquariumManagerData GetAquariumManagerData(game.PlayerDataStorage.IPlayerSaveStore store = null)
|
||||
{
|
||||
var source = store ?? GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
string dataStr = source.Read(AquariumEventDataNewKey).ValueOrThrow();
|
||||
if (!string.IsNullOrEmpty(dataStr))
|
||||
{
|
||||
try
|
||||
{
|
||||
return AquariumManagerData.Deserialize(dataStr);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"GetAquariumManagerData Deserialize Error:{e}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public async Task Synchrodata()
|
||||
{
|
||||
//从服务器拿数据
|
||||
if (!saveStore.IsCurrent) throw new OperationCanceledException();
|
||||
var dataSever = GetAquariumManagerData(saveStore);
|
||||
if (dataSever == null)
|
||||
{
|
||||
//新数据
|
||||
data = new AquariumManagerData();
|
||||
data.startTime = ZZTimeHelper.UtcNow().AddDays(-2);
|
||||
data.refreshCount = 0;
|
||||
data.adCount = 0;
|
||||
data.refreshDay = ZZTimeHelper.UtcNow().UtcNowOffset().Date.DayOfYear;
|
||||
|
||||
data.slot = GContext.container.Resolve<PlayerData>().benefitsAquariumLimit;
|
||||
}
|
||||
else
|
||||
{
|
||||
data = dataSever;
|
||||
if (data.slotDatas == null || data.slotDatas.Count < data.slot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
GetAquariumDataRequest request = new GetAquariumDataRequest();
|
||||
request.playFabId = loginUser.UserId;
|
||||
string result = await customServerMgr.CustomServerPost("aquarium/GetAquariumData", Newtonsoft.Json.JsonConvert.SerializeObject(request));
|
||||
if (!saveStore.IsCurrent) throw new OperationCanceledException();
|
||||
AquariumServerData serverData = null;
|
||||
if (result != null)
|
||||
{
|
||||
var resp = JsonConvert.DeserializeObject<GetAquariumDataResp>(result);
|
||||
if (resp.code == 0)
|
||||
{
|
||||
serverData = resp.data;
|
||||
}
|
||||
else if (resp.code == 1)
|
||||
{
|
||||
Debug.LogWarning("GetAquariumDataRequest Null");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("GetAquariumDataRequest Null");
|
||||
}
|
||||
}
|
||||
if (serverData != null)
|
||||
{
|
||||
data.aquariumHurtDatas = serverData.aquariumHurtDatas ?? new List<AquariumHurtData>();
|
||||
int count = data.slot - data.aquariumHurtDatas.Count;
|
||||
if (count > 0)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
AquariumHurtData aquariumHurtData = new AquariumHurtData();
|
||||
data.aquariumHurtDatas.Add(aquariumHurtData);
|
||||
}
|
||||
}
|
||||
data.aquariumResultDatas = serverData.aquariumResultDatas ?? new List<AquariumResultData>();
|
||||
count = data.slot - data.aquariumResultDatas.Count;
|
||||
if (count > 0)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
AquariumResultData aquariumResultData = new AquariumResultData();
|
||||
data.aquariumResultDatas.Add(aquariumResultData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetJury();
|
||||
//保存 数据
|
||||
SaveData(serverData == null);
|
||||
}
|
||||
}
|
||||
void SetJury()
|
||||
{
|
||||
var dateTime = ZZTimeHelper.UtcNow();
|
||||
AquariumFish aquariumFish = null;
|
||||
for (int i = 0; i < data.slot; i++)
|
||||
{
|
||||
AquariumSlotData slotData = data.slotDatas[i];
|
||||
slotData.index = i;
|
||||
AquariumHurtData aquariumHurtData = data.aquariumHurtDatas[i];
|
||||
//没有受新伤
|
||||
if (aquariumHurtData.injuryTime < slotData.allRecoveryTime + 0.0001f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var HatchTime = slotData.FishHatchTime();
|
||||
//还没孵化
|
||||
if (HatchTime > dateTime)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//检查是否受伤
|
||||
//正常的成长时间 自然时间+加速成长-总恢复时间
|
||||
float normalH = (float)(dateTime - HatchTime).TotalHours + slotData.accelerationGrowth - slotData.allRecoveryTime;
|
||||
//受伤时的成长时间 受伤时的时间+加速成长-受伤时的总恢复时间
|
||||
float injuryH = slotData.injuryGrowthTime;
|
||||
|
||||
aquariumFish = _tables.TbAquariumFish.GetOrDefault(slotData.aquariumFishID);
|
||||
//受伤时的成长进度大于正常的成长进度=还没恢复
|
||||
if (injuryH > normalH)
|
||||
{
|
||||
var recoveryTime = UnityEngine.Random.Range(aquariumFish.RecoveryTime[0], aquariumFish.RecoveryTime[1]);
|
||||
slotData.recoveryTime = recoveryTime;
|
||||
recoveryTime -= injuryH - normalH;
|
||||
//处于受伤状态刷新时间
|
||||
if (recoveryTime > 0)
|
||||
{
|
||||
slotData.allRecoveryTime += recoveryTime;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//不处于受伤状态添加受伤时间
|
||||
//当前正常的成长进度 自然时间+加速成长 - 全部恢复时间
|
||||
//受伤时的成长时间 受伤时的时间+加速成长-受伤时的总恢复时间
|
||||
|
||||
slotData.injuryGrowthTime = (float)(dateTime - HatchTime).TotalHours + slotData.accelerationGrowth - slotData.allRecoveryTime;
|
||||
slotData.recoveryTime = UnityEngine.Random.Range(aquariumFish.RecoveryTime[0], aquariumFish.RecoveryTime[1]);
|
||||
slotData.allRecoveryTime += slotData.recoveryTime;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public void GetRandmMap()
|
||||
{
|
||||
PlayerData playerData = GContext.container.Resolve<PlayerData>();
|
||||
PlayerFishData playerFishData = GContext.container.Resolve<PlayerFishData>();
|
||||
|
||||
data.startTime = ZZTimeHelper.UtcNow();
|
||||
data.slot = playerData.benefitsAquariumLimit;
|
||||
List<AquariumInit> inits = _tables.TbAquariumInit.DataList;
|
||||
if (data.startCount < inits.Count)
|
||||
{
|
||||
NewSlotInitData(inits[data.startCount]);
|
||||
}
|
||||
else
|
||||
{
|
||||
NewSlotData();
|
||||
}
|
||||
data.refreshCount++;
|
||||
data.isHome = false;
|
||||
data.startCount++;
|
||||
//保存 数据
|
||||
SaveData(true);
|
||||
GContext.container.Resolve<TriggerPackData>().RefreshTriggerPackData(4000201, data.mapId);
|
||||
|
||||
#if AGG
|
||||
int fishcard_totallevel = playerFishData.GetFishLevelByMap(data.mapId);
|
||||
string egg_quality_list = data.slotDatas[0].eggId.ToString();
|
||||
for (int i = 1; i < data.slotDatas.Count; i++)
|
||||
{
|
||||
egg_quality_list = $"{egg_quality_list},{data.slotDatas[i].eggId}";
|
||||
}
|
||||
using (var e = GEvent.GameEvent("aquarium_start"))
|
||||
{
|
||||
e.AddContent("map_id", data.mapId)
|
||||
.AddContent("total_eggpots", data.slot)
|
||||
.AddContent("fishcard_totallevel", fishcard_totallevel)
|
||||
.AddContent("egg_quality_list", egg_quality_list);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void NewSlotInitData(AquariumInit init)
|
||||
{
|
||||
data.mapId = init.MapID;
|
||||
List<AquariumSlotData> slotDatas = new List<AquariumSlotData>();
|
||||
List<AquariumHurtData> aquariumHurtDatas = new List<AquariumHurtData>();
|
||||
List<AquariumResultData> aquariumResultDatas = new List<AquariumResultData>();
|
||||
DateTime dateTime = ZZTimeHelper.UtcNow();
|
||||
|
||||
List<AquariumEgg> TankEggList = _tables.TbAquariumEgg.DataList;
|
||||
eggNames = new List<string>();
|
||||
for (int i = 0; i < TankEggList.Count; i++)
|
||||
{
|
||||
eggNames.Add(TankEggList[i].Icon);
|
||||
}
|
||||
|
||||
List<int> Egglist = init.Egglist;
|
||||
List<int> FishQualitylist = init.FishQualitylist;
|
||||
List<int> FishList = init.Fishlist;
|
||||
List<int> FishQuatitylist = init.FishQuatitylist;
|
||||
List<int> FishMutantlist = init.FishMutantlist;
|
||||
|
||||
|
||||
for (int i = 0; i < data.slot; i++)
|
||||
{
|
||||
AquariumSlotData aquariumSlotData = new AquariumSlotData();
|
||||
aquariumSlotData.eggId = Egglist[i];
|
||||
aquariumSlotData.aquariumFishID = FishQualitylist[i];
|
||||
aquariumSlotData.fishMQ = FishQuatitylist[i];
|
||||
aquariumSlotData.fishItemID = FishList[i];
|
||||
|
||||
var tankEgg = _tables.TbAquariumEgg.GetOrDefault(Egglist[i]);
|
||||
|
||||
aquariumSlotData.startTime = dateTime;
|
||||
aquariumSlotData.hatchTime = UnityEngine.Random.Range(tankEgg.HatchTime[0], tankEgg.HatchTime[1]);
|
||||
|
||||
AquariumFish aquariumFish = _tables.TbAquariumFish.GetOrDefault(FishQualitylist[i]);
|
||||
|
||||
float allCash = SetRewardCount(aquariumFish.SoldReward);
|
||||
float soldRewardRand = UnityEngine.Random.Range(0, aquariumFish.SoldRewardRand) - aquariumFish.SoldRewardRand * 0.5f;
|
||||
aquariumSlotData.allCash = allCash * (1 + soldRewardRand);
|
||||
|
||||
aquariumSlotData.growthTime = UnityEngine.Random.Range(aquariumFish.GrowthTime[0], aquariumFish.GrowthTime[1]);
|
||||
aquariumSlotData.mutants = FishMutantlist[i];
|
||||
aquariumSlotData.index = i;
|
||||
slotDatas.Add(aquariumSlotData);
|
||||
|
||||
AquariumHurtData aquariumHurtData = new AquariumHurtData();
|
||||
aquariumHurtDatas.Add(aquariumHurtData);
|
||||
|
||||
AquariumResultData aquariumResultData = new AquariumResultData();
|
||||
aquariumResultDatas.Add(aquariumResultData);
|
||||
}
|
||||
data.slotDatas = slotDatas;
|
||||
data.aquariumHurtDatas = aquariumHurtDatas;
|
||||
data.aquariumResultDatas = aquariumResultDatas;
|
||||
}
|
||||
|
||||
void SetAquariumFish(MapData mapData, AquariumSlotData aquariumSlotData, AquariumEgg tankEgg)
|
||||
{
|
||||
List<FishItemList> fishList = mapData.DropFishList;
|
||||
|
||||
//权重取鱼
|
||||
int allProb = tankEgg.HatchFishQualityProb.Sum();
|
||||
int prob = UnityEngine.Random.Range(0, allProb);
|
||||
int curProb = 0;
|
||||
int fishQuality = tankEgg.HatchFishQuality[^1];
|
||||
for (int k = 0; k < tankEgg.HatchFishQuality.Count; k++)
|
||||
{
|
||||
curProb += tankEgg.HatchFishQualityProb[k];
|
||||
if (curProb > prob)
|
||||
{
|
||||
fishQuality = tankEgg.HatchFishQuality[k];
|
||||
break;
|
||||
}
|
||||
}
|
||||
//品质大于6???
|
||||
if (6 > fishQuality)
|
||||
{
|
||||
List<int> FishIDList = fishList[fishQuality - 1].FishIDList;
|
||||
aquariumSlotData.fishItemID = FishIDList[UnityEngine.Random.Range(0, FishIDList.Count)];
|
||||
}
|
||||
else
|
||||
{
|
||||
List<int> FishIDList = _tables.TbAquariumConfig.AquariumSpFishList;
|
||||
aquariumSlotData.fishItemID = FishIDList[UnityEngine.Random.Range(0, FishIDList.Count)];
|
||||
}
|
||||
aquariumSlotData.aquariumFishID = fishQuality;
|
||||
//取鱼的数量
|
||||
allProb = tankEgg.HatchFishQuantityProb.Sum();
|
||||
prob = UnityEngine.Random.Range(0, allProb);
|
||||
curProb = 0;
|
||||
int fishCount = tankEgg.HatchFishQuantity[0];
|
||||
for (int k = 0; k < tankEgg.HatchFishQuantity.Count; k++)
|
||||
{
|
||||
curProb += tankEgg.HatchFishQuantityProb[k];
|
||||
if (curProb > prob)
|
||||
{
|
||||
fishCount = tankEgg.HatchFishQuantity[k];
|
||||
break;
|
||||
}
|
||||
}
|
||||
aquariumSlotData.fishMQ = fishCount;
|
||||
}
|
||||
|
||||
void SetTime(AquariumSlotData aquariumSlotData, AquariumEgg tankEgg)
|
||||
{
|
||||
aquariumSlotData.hatchTime = UnityEngine.Random.Range(tankEgg.HatchTime[0], tankEgg.HatchTime[1]);
|
||||
|
||||
AquariumFish aquariumFish = _tables.TbAquariumFish.GetOrDefault(aquariumSlotData.aquariumFishID);
|
||||
|
||||
float allCash = SetRewardCount(aquariumFish.SoldReward);
|
||||
float soldRewardRand = UnityEngine.Random.Range(0, aquariumFish.SoldRewardRand) - aquariumFish.SoldRewardRand * 0.5f;
|
||||
aquariumSlotData.allCash = allCash * (1 + soldRewardRand);
|
||||
|
||||
aquariumSlotData.growthTime = UnityEngine.Random.Range(aquariumFish.GrowthTime[0], aquariumFish.GrowthTime[1]);
|
||||
aquariumSlotData.mutants = 0;
|
||||
for (int j = 0; j < aquariumSlotData.fishMQ; j++)
|
||||
{
|
||||
if (UnityEngine.Random.Range(0, 1f) < aquariumFish.MutantProb)
|
||||
{
|
||||
aquariumSlotData.mutants++;
|
||||
}
|
||||
}
|
||||
}
|
||||
void NewSlotData()
|
||||
{
|
||||
int curMapId = MapId;
|
||||
|
||||
var mapDatas = _tables.TbMapData.DataList;
|
||||
var maxMapID = GContext.container.Resolve<PlayerData>().lastMapId;
|
||||
|
||||
//非当前地图
|
||||
var mapIDs = new List<int>();
|
||||
for (int i = 0; i < mapDatas.Count; i++)
|
||||
{
|
||||
if (mapDatas[i].ID <= maxMapID)
|
||||
{
|
||||
if (mapDatas[i].ID != curMapId)
|
||||
{
|
||||
mapIDs.Add(mapDatas[i].ID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mapIDs.Count == 0)
|
||||
{
|
||||
data.mapId = maxMapID;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.mapId = mapIDs[UnityEngine.Random.Range(0, mapIDs.Count)];
|
||||
}
|
||||
|
||||
List<AquariumSlotData> slotDatas = new List<AquariumSlotData>();
|
||||
List<AquariumHurtData> aquariumHurtDatas = new List<AquariumHurtData>();
|
||||
List<AquariumResultData> aquariumResultDatas = new List<AquariumResultData>();
|
||||
|
||||
DateTime dateTime = ZZTimeHelper.UtcNow();
|
||||
MapData mapData = _tables.TbMapData.GetOrDefault(data.mapId);
|
||||
List<AquariumEgg> TankEggList = _tables.TbAquariumEgg.DataList;
|
||||
float eggAllWeight = -0.0001f;
|
||||
eggNames = new List<string>();
|
||||
for (int i = 0; i < TankEggList.Count; i++)
|
||||
{
|
||||
eggNames.Add(TankEggList[i].Icon);
|
||||
eggAllWeight += TankEggList[i].Weight;
|
||||
}
|
||||
for (int i = 0; i < data.slot; i++)
|
||||
{
|
||||
AquariumSlotData aquariumSlotData = new AquariumSlotData();
|
||||
//权重取蛋
|
||||
float eggWeight = UnityEngine.Random.Range(0, eggAllWeight);
|
||||
float curEggWeight = 0;
|
||||
AquariumEgg tankEgg = TankEggList[0];
|
||||
foreach (var item in TankEggList)
|
||||
{
|
||||
curEggWeight += item.Weight;
|
||||
if (curEggWeight > eggWeight)
|
||||
{
|
||||
tankEgg = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
aquariumSlotData.eggId = tankEgg.ID;
|
||||
SetAquariumFish(mapData, aquariumSlotData, tankEgg);
|
||||
aquariumSlotData.startTime = dateTime;
|
||||
SetTime(aquariumSlotData, tankEgg);
|
||||
aquariumSlotData.index = i;
|
||||
slotDatas.Add(aquariumSlotData);
|
||||
|
||||
AquariumHurtData aquariumHurtData = new AquariumHurtData();
|
||||
aquariumHurtDatas.Add(aquariumHurtData);
|
||||
|
||||
AquariumResultData aquariumResultData = new AquariumResultData();
|
||||
aquariumResultDatas.Add(aquariumResultData);
|
||||
}
|
||||
data.slotDatas = slotDatas;
|
||||
data.aquariumHurtDatas = aquariumHurtDatas;
|
||||
data.aquariumResultDatas = aquariumResultDatas;
|
||||
}
|
||||
|
||||
public void Speedup(bool isGuide)
|
||||
{
|
||||
var dateTime = ZZTimeHelper.UtcNow();
|
||||
float AdAccelerate = _tables.TbAquariumConfig.AdAccelerate;
|
||||
if (isGuide)
|
||||
{
|
||||
AdAccelerate = _tables.TbAquariumConfig.AdAccelerateFirst;
|
||||
}
|
||||
List<AquariumSlotData> slotDataList = data.slotDatas;
|
||||
List<AquariumResultData> aquariumReportDatas = data.aquariumResultDatas;
|
||||
for (int i = 0; i < slotDataList.Count; i++)
|
||||
{
|
||||
var slotData = slotDataList[i];
|
||||
|
||||
//售卖或者丢失
|
||||
if (slotData.IsResult() || aquariumReportDatas[slotData.index].IsResult())
|
||||
{ continue; }
|
||||
var HatchTime = slotData.FishHatchTime();
|
||||
if (HatchTime <= dateTime)
|
||||
{
|
||||
//鱼加速
|
||||
slotData.accelerationGrowth += AdAccelerate;
|
||||
}
|
||||
else
|
||||
{
|
||||
float s = (float)(HatchTime - dateTime).TotalHours;
|
||||
if (s <= 0)
|
||||
{
|
||||
slotData.accelerationGrowth += AdAccelerate;
|
||||
}
|
||||
else if (AdAccelerate > s)
|
||||
{
|
||||
slotData.accelerationHatch += s;
|
||||
slotData.accelerationGrowth += AdAccelerate - s;
|
||||
}
|
||||
else
|
||||
{
|
||||
slotData.accelerationHatch += AdAccelerate;
|
||||
}
|
||||
}
|
||||
}
|
||||
data.adCount++;
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public void SellFish(List<AquariumSlotData> slotDataList)
|
||||
{
|
||||
sellList = new List<AquariumSlotData>();
|
||||
var dateTime = ZZTimeHelper.UtcNow();
|
||||
PlayerData playerData = GContext.container.Resolve<PlayerData>();
|
||||
var weight = GetWeightMag();
|
||||
AquariumFish aquariumFish;
|
||||
FishData fishData;
|
||||
DateTime startTime;
|
||||
float injuryH;
|
||||
float weightIncEvent;
|
||||
List<ItemData> itemDatas = new List<ItemData>();
|
||||
PlayerFishData playerFishData = GContext.container.Resolve<PlayerFishData>();
|
||||
float CashMag = (1 + playerData.benefitsCashMag) * (1 + playerData.benefitsAquariumCashMag);
|
||||
for (int i = 0; i < slotDataList.Count; i++)
|
||||
{
|
||||
var slotData = slotDataList[i];
|
||||
sellList.Add(slotData);
|
||||
if (slotData.allCash == 0)
|
||||
{
|
||||
slotData.allCash = 1;
|
||||
}
|
||||
//奖励
|
||||
aquariumFish = _tables.TbAquariumFish.GetOrDefault(slotData.aquariumFishID);
|
||||
startTime = slotData.FishHatchTime();
|
||||
injuryH = slotData.injuryGrowthTime;
|
||||
weightIncEvent = (1 + weight[slotData.aquariumFishID - 1]) * CashMag;
|
||||
|
||||
float normalH = (float)(dateTime - startTime).TotalHours + slotData.accelerationGrowth - slotData.allRecoveryTime;
|
||||
|
||||
float rewardProgress = normalH / slotData.growthTime;
|
||||
|
||||
if (injuryH > normalH && injuryH > slotData.growthTime)
|
||||
{
|
||||
rewardProgress = 1 - ((injuryH - normalH) / slotData.growthTime);
|
||||
}
|
||||
|
||||
if (rewardProgress > 1)
|
||||
{
|
||||
rewardProgress = 1;
|
||||
}
|
||||
|
||||
//金币显示
|
||||
ItemData itemData = new ItemData();
|
||||
|
||||
int fishQ = slotData.fishMQ;
|
||||
bool is_injured = false;
|
||||
if (injuryH > normalH)
|
||||
{
|
||||
fishQ -= data.aquariumHurtDatas[slotData.index].fishLQ;
|
||||
is_injured = true;
|
||||
if (fishQ <= 0)
|
||||
{
|
||||
fishQ = 1;
|
||||
}
|
||||
}
|
||||
if (rewardProgress < 0)
|
||||
{
|
||||
rewardProgress = 0;
|
||||
}
|
||||
for (int j = 0; j < fishQ; j++)
|
||||
{
|
||||
if (j < slotData.mutants)
|
||||
{
|
||||
itemData.count += (int)(slotData.allCash * (1 + aquariumFish.MutantGrowthExtraSoldReward * rewardProgress) * (1 + aquariumFish.MutantExtraSoldReward) * weightIncEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
itemData.count += (int)(slotData.allCash * (1 + aquariumFish.GrowthExtraSoldReward * rewardProgress) * weightIncEvent);
|
||||
}
|
||||
}
|
||||
itemData.id = 1002;
|
||||
|
||||
itemDatas.Add(itemData);
|
||||
slotData.cash = itemData.count;
|
||||
|
||||
//重量
|
||||
Item item = _tables.TbItem.GetOrDefault(slotData.fishItemID);
|
||||
fishData = _tables.TbFishData.GetOrDefault(item.RedirectID);
|
||||
//float weightCount = fishData.BaseWeight * (1 + aquariumFish.GrowthExtraSoldReward * rewardProgress) * weightIncEvent;
|
||||
//playerFishData.SetWeight(fishData.ID, weightCount, false);
|
||||
#if AGG
|
||||
int cardLevel = playerFishData.GetDataLevel(fishData.ID);
|
||||
using (var e = GEvent.GameEvent("aquarium_sell"))
|
||||
{
|
||||
e.AddContent("map_id", data.mapId)
|
||||
.AddContent("total_eggpots", data.slot)
|
||||
.AddContent("fish_eggs_quality", slotData.eggId)
|
||||
.AddContent("mature_percent", rewardProgress)
|
||||
.AddContent("fish_id", fishData.ID)
|
||||
.AddContent("fishcard_level", cardLevel)
|
||||
.AddContent("fish_quality", slotData.aquariumFishID)
|
||||
.AddContent("mutant_count", slotData.mutants)
|
||||
.AddContent("is_injured", is_injured)
|
||||
.AddContent("fish_count", slotData.fishMQ)
|
||||
.AddContent("is_dead", false)
|
||||
.AddContent("reward_cash", slotData.cash);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
//GContext.Publish(new ShowData(itemDatas));
|
||||
GContext.container.Resolve<PlayerItemData>().AddItem(itemDatas, null);
|
||||
//GContext.Publish(new ShowData());
|
||||
CheckInHome();
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public float SetRewardCount(int dropID)
|
||||
{
|
||||
DropPackageList dropPackageList = _tables.TbDrop[dropID].DropList;
|
||||
return dropPackageList.DropCountList[0];
|
||||
}
|
||||
|
||||
public void CheckInHome()
|
||||
{
|
||||
data.isHome = true;
|
||||
List<AquariumResultData> aquariumResultDatas = data.aquariumResultDatas;
|
||||
var slotDatas = data.slotDatas;
|
||||
for (int i = 0; i < data.slot; i++)
|
||||
{
|
||||
if (!slotDatas[i].IsResult() && !aquariumResultDatas[i].IsResult())
|
||||
{
|
||||
data.isHome = false;
|
||||
}
|
||||
}
|
||||
if (data.isHome)
|
||||
{
|
||||
#if AGG
|
||||
PlayerFishData playerFishData = GContext.container.Resolve<PlayerFishData>();
|
||||
for (int i = 0; i < data.slot; i++)
|
||||
{
|
||||
if (!slotDatas[i].IsResult() && aquariumResultDatas[i].IsResult())
|
||||
{
|
||||
var fishItem = _tables.TbItem.GetOrDefault(slotDatas[i].fishItemID);
|
||||
int cardLevel = playerFishData.GetDataLevel(fishItem.RedirectID);
|
||||
using (var e = GEvent.GameEvent("aquarium_sell"))
|
||||
{
|
||||
e.AddContent("map_id", data.mapId)
|
||||
.AddContent("total_eggpots", data.slot)
|
||||
.AddContent("fish_eggs_quality", slotDatas[i].eggId)
|
||||
.AddContent("mature_percent", 0)
|
||||
.AddContent("fish_id", fishItem.RedirectID)
|
||||
.AddContent("fishcard_level", cardLevel)
|
||||
.AddContent("fish_quality", slotDatas[i].aquariumFishID)
|
||||
.AddContent("mutant_count", slotDatas[i].mutants)
|
||||
.AddContent("is_injured", true)
|
||||
.AddContent("is_dead", true)
|
||||
.AddContent("fish_count", 0)
|
||||
.AddContent("reward_cash", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
UpdateUserData();
|
||||
//_ = UIManager.Instance.ShowUI(UITypes.AquariumReportPopupPanel);
|
||||
}
|
||||
}
|
||||
void UpdateUserData()
|
||||
{
|
||||
saveStore.Write(AquariumEventDataNewKey, data.Serialize()).RequireAccepted();
|
||||
}
|
||||
void SaveData(bool all = false)
|
||||
{
|
||||
List<AquariumSlotData> slotDatas = data.slotDatas;
|
||||
//鱼卡等级
|
||||
PlayerFishData playerFishData = GContext.container.Resolve<PlayerFishData>();
|
||||
List<int> cardLv = new List<int>();
|
||||
for (int i = 0; i < slotDatas.Count; i++)
|
||||
{
|
||||
var fishItem = _tables.TbItem.GetOrDefault(slotDatas[i].fishItemID);
|
||||
if (fishItem.Quality < 6)
|
||||
{
|
||||
int level = playerFishData.GetDataLevel(fishItem.RedirectID);
|
||||
cardLv.Add(level);
|
||||
}
|
||||
else
|
||||
{
|
||||
int level = GContext.container.Resolve<PlayerData>().benefitsLevel[4];
|
||||
cardLv.Add(level);
|
||||
}
|
||||
}
|
||||
|
||||
if (all)
|
||||
{
|
||||
UpdateAquariumAllDataRequest request = new UpdateAquariumAllDataRequest();
|
||||
request.playFabId = loginUser.UserId;
|
||||
request.data = new AquariumServerData()
|
||||
{
|
||||
startTime = data.startTime,
|
||||
mapId = data.mapId,
|
||||
slot = data.slot,
|
||||
slotDatas = data.slotDatas,
|
||||
cardLv = string.Join(',', cardLv),
|
||||
aquariumResultDatas = data.aquariumResultDatas,
|
||||
aquariumHurtDatas = data.aquariumHurtDatas,
|
||||
};
|
||||
customServerMgr.CustomServerPost("aquarium/UpdateAquariumData", Newtonsoft.Json.JsonConvert.SerializeObject(request));
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateSlotDatasRequest request = new UpdateSlotDatasRequest();
|
||||
request.playFabId = loginUser.UserId;
|
||||
request.slotDatas = data.slotDatas;
|
||||
request.cardLv = string.Join(',', cardLv);
|
||||
customServerMgr.CustomServerPost("aquarium/UpdateSlotData", Newtonsoft.Json.JsonConvert.SerializeObject(request));
|
||||
}
|
||||
UpdateUserData();
|
||||
}
|
||||
|
||||
public List<float> GetWeightMag()
|
||||
{
|
||||
PlayerFishData playerFishData = GContext.container.Resolve<PlayerFishData>();
|
||||
PlayerData playerData = GContext.container.Resolve<PlayerData>();
|
||||
List<float> weight = new List<float>() { 0, 0, 0, 0, 0, 0 };
|
||||
List<int> fishCount = new List<int>() { 0, 0, 0, 0, 0, 0 };
|
||||
MapData mapData = _tables.TbMapData.GetOrDefault(data.mapId);
|
||||
var fishList = mapData.FishList;
|
||||
for (int j = 0; j < fishList.Count; j++)
|
||||
{
|
||||
var data = _tables.TbFishData.GetOrDefault(fishList[j]);
|
||||
fishCount[data.Quality - 1]++;
|
||||
int level = playerFishData.GetDataLevel(fishList[j]);
|
||||
if (level > 0)
|
||||
{
|
||||
FishCard fishCard = _tables.TbFishCard.GetOrDefault(data.FishCardID);
|
||||
weight[data.Quality - 1] += fishCard.WeightMagList[level - 1];
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < weight.Count; i++)
|
||||
{
|
||||
weight[i] /= fishCount[i];
|
||||
}
|
||||
weight[5] = playerData.benefitsEventFishCashMag;
|
||||
|
||||
return weight;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,790 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using LitJson;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
public class CampDataMM
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore SaveStore { get; set; }
|
||||
|
||||
private cfg.Tables _tables;
|
||||
MMTService _MMService;
|
||||
public CampDataMM(cfg.Tables tables, MMTService mmService)
|
||||
{
|
||||
this._tables = tables;
|
||||
_MMService = mmService;
|
||||
}
|
||||
|
||||
#region SERVER_DATA
|
||||
int _id = 101;
|
||||
public int PreID;
|
||||
public int Id
|
||||
{
|
||||
set { _id = value; }
|
||||
get
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
}
|
||||
public int AreaIndex { get; private set; } = 0;
|
||||
public int AreaStep { get; private set; } = 0;
|
||||
Dictionary<string, int> CampDataDic = new Dictionary<string, int>();
|
||||
#endregion
|
||||
|
||||
public int AreaId { get; private set; } = 0;
|
||||
public int AreaStepCount { get; private set; }
|
||||
public int TimelineId { get; private set; }
|
||||
public string Name { get { return GetMMTString(_tables.TbConstruction.DataMap[Id].Name_l10n_key); } }
|
||||
|
||||
public string AreaIcon { get; private set; }
|
||||
public string BasePrefab => GetBasePrefab(Id);
|
||||
public List<string> AllPrefabs
|
||||
{
|
||||
get
|
||||
{
|
||||
List<int> AreaList = _tables.TbConstruction.DataMap[Id].AreaList;
|
||||
return AreaList.Select(stepid => GetArea(stepid).BuildingPrefeb).Distinct().ToList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public string CurrentPrefab
|
||||
{
|
||||
get
|
||||
{
|
||||
int index = AreaIndex;
|
||||
if (index >= AreaList.Count)
|
||||
index = AreaList.Count - 1;
|
||||
|
||||
return GetArea(AreaList[index]).BuildingPrefeb;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Area GetAreaByIndex(int index)
|
||||
{
|
||||
if (index >= AreaList.Count)
|
||||
index = AreaList.Count - 1;
|
||||
return GetArea(AreaList[index]);
|
||||
}
|
||||
|
||||
|
||||
public string BGM { get { return _tables.TbConstruction.DataMap[Id].BGM; } }
|
||||
public int NextMapID { get { return _tables.TbConstruction.DataMap[Id].NextID; } }
|
||||
public Area constructionArea { get; private set; }
|
||||
public Step StepInfo { get; private set; }
|
||||
public List<int> AreaList
|
||||
{
|
||||
get
|
||||
{
|
||||
return _tables.TbConstruction.DataMap[Id].AreaList;
|
||||
}
|
||||
}
|
||||
|
||||
public List<int> AreaStepList { get; private set; }
|
||||
|
||||
public bool IsUpdataArea = false;
|
||||
string _isOpenSkyscraper = "0";
|
||||
public bool IsOpenSkyscraper => _isOpenSkyscraper == "1" || IsCanSkyscraper;
|
||||
public bool IsCanSkyscraper
|
||||
{
|
||||
get
|
||||
{
|
||||
var dataMap = _tables.TbMapData.DataList[^1];
|
||||
return GContext.container.Resolve<PlayerData>().lv >= dataMap.LevelRequired;
|
||||
}
|
||||
}
|
||||
//public int curQRewardCount = 0;
|
||||
//是否需要修复
|
||||
public bool IsFix => _isFix && AreaIndex < AreaList.Count;
|
||||
bool _isFix;
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVCampState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) => game.PlayerDataStorage.PlayerKVSaveSchema.DecodeCamp(raw,errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
CampDataDic=new Dictionary<string,int>(StringComparer.Ordinal);
|
||||
_isFix=false; _isOpenSkyscraper="0"; _id=101;
|
||||
PreID=AreaIndex=AreaStep=AreaId=AreaStepCount=TimelineId=0;
|
||||
AreaIcon=null; constructionArea=null; StepInfo=null; AreaStepList=null; IsUpdataArea=false;
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVCampState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
CampDataDic=state.Values.ToDictionary(p=>p.Key,p=>p.Value,StringComparer.Ordinal);
|
||||
_isFix=state.IsFix; _isOpenSkyscraper=state.Skyscraper;
|
||||
}
|
||||
internal game.PlayerDataStorage.PlayerKVCampState SnapshotForKVValidation() =>
|
||||
new game.PlayerDataStorage.PlayerKVCampState(_isFix,_isOpenSkyscraper,CampDataDic);
|
||||
public void InitData(Dictionary<string, string> userData)
|
||||
{
|
||||
string value;
|
||||
|
||||
if (userData.TryGetValue("IsFix", out value))
|
||||
{
|
||||
SetIsFix(bool.Parse(value), false);
|
||||
}
|
||||
if (userData.TryGetValue("IsOpenSkyscraper", out value))
|
||||
{
|
||||
_isOpenSkyscraper = value;
|
||||
}
|
||||
|
||||
if (userData.TryGetValue("CampDataDic", out value))
|
||||
{
|
||||
CampDataDic = JsonMapper.ToObject<Dictionary<string, int>>(value);
|
||||
}
|
||||
}
|
||||
public void SetIsFix(bool isFix, bool isSave = true)
|
||||
{
|
||||
_isFix = isFix;
|
||||
if (isSave)
|
||||
{
|
||||
SaveStore.Write("IsFix", _isFix.ToString()).RequireAccepted();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetCampAreaIndex(string id_string)
|
||||
{
|
||||
AreaIndex = GlobalUtils.TryParseInt(id_string);
|
||||
}
|
||||
|
||||
public void SetCampAreaStep(string step_string)
|
||||
{
|
||||
AreaStep = GlobalUtils.TryParseInt(step_string);
|
||||
}
|
||||
void SetCampByLevel()
|
||||
{
|
||||
List<Construction> _dataList = _tables.TbConstruction.DataList;
|
||||
if (!_tables.TbConstruction.DataMap.ContainsKey(Id))
|
||||
{
|
||||
Debug.LogError($"[CampData]UpdateCurrentData: Not found data of id({Id})!");
|
||||
Id = _dataList[^1].ID;
|
||||
}
|
||||
int level = GContext.container.Resolve<PlayerData>().lv;
|
||||
int id = _dataList[0].ID;
|
||||
for (int i = _dataList.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (_dataList[i].InitialLevel <= level)
|
||||
{
|
||||
id = _dataList[i].ID;
|
||||
level -= _dataList[i].InitialLevel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AreaIndex = AreaList.Count;
|
||||
var _area = GetArea(AreaList[^1]);
|
||||
int StepCount = _area.StepCount;
|
||||
AreaStep = StepCount;
|
||||
if (id % 1000 <= Id % 1000)
|
||||
{
|
||||
Id = id;
|
||||
AreaIndex = AreaList.Count;
|
||||
for (int i = 0; i < AreaList.Count; i++)
|
||||
{
|
||||
_area = GetArea(AreaList[i]);
|
||||
StepCount = _area.StepCount;
|
||||
if (StepCount > level)
|
||||
{
|
||||
AreaIndex = i;
|
||||
AreaStep = level;
|
||||
break;
|
||||
}
|
||||
level -= StepCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
public void Init()
|
||||
{
|
||||
if (CampDataDic.Count > 0)
|
||||
{
|
||||
Id = CampDataDic["CampId"];
|
||||
}
|
||||
SetCampByLevel();
|
||||
PreID = Id;
|
||||
if (_tables.TbConstruction.DataMap.ContainsKey(Id))
|
||||
{
|
||||
if (AreaIndex >= AreaList.Count)
|
||||
{
|
||||
CompleteInit();
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateCurrentData();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"[CampData]UpdateCurrentData: Not found data of id({Id})!");
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeNextCampID()
|
||||
{
|
||||
OnChangeCamp(NextMapID);
|
||||
}
|
||||
public void ChangeCampId(int id)
|
||||
{
|
||||
OnChangeCamp(id);
|
||||
}
|
||||
|
||||
void OnChangeCamp(int id)
|
||||
{
|
||||
if (_tables.TbConstruction.DataMap.TryGetValue(id, out var camp_data))
|
||||
{
|
||||
constructionArea = null;
|
||||
Id = id;
|
||||
PreID = Id;
|
||||
SetCampByLevel();
|
||||
if (AreaIndex >= AreaList.Count)
|
||||
{
|
||||
CompleteInit();
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateCurrentData();
|
||||
}
|
||||
CampDataDic["CampId"] = Id;
|
||||
SaveStore.Write("CampDataDic", JsonMapper.ToJson(CampDataDic)).RequireAccepted(); ;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"[CampData]UpdateCurrentData: Not found data of id({id})!");
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowReward()
|
||||
{
|
||||
int lv = GContext.container.Resolve<PlayerData>().lv;
|
||||
if (_tables.TbGlobalConfig.AutoClaimUnlock > 0 && lv > _tables.TbGlobalConfig.AutoClaimUnlock)
|
||||
GContext.Publish(new ShowData(RewardType.AutoOpen));
|
||||
else
|
||||
GContext.Publish(new ShowData());
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void UpdateCurrentData()
|
||||
{
|
||||
if (AreaList != null)
|
||||
{
|
||||
if (AreaIndex < 0 || AreaIndex >= AreaList.Count)
|
||||
{
|
||||
if (NextMapID == -1 || !_tables.TbConstruction.DataMap.ContainsKey(NextMapID))
|
||||
{
|
||||
_isOpenSkyscraper = "1";
|
||||
SaveStore.Write("IsOpenSkyscraper", _isOpenSkyscraper).RequireAccepted();
|
||||
return;
|
||||
}
|
||||
Debug.LogError(
|
||||
$"[CampData]UpdateCurrentData: AreaIndex({AreaIndex}) is out of range[0-{AreaList.Count}]! Will reset to 0");
|
||||
AreaIndex = 0;
|
||||
}
|
||||
|
||||
IsUpdataArea = AreaId != AreaList[AreaIndex];
|
||||
if (IsUpdataArea)
|
||||
{
|
||||
AreaId = AreaList[AreaIndex];
|
||||
SetArea();
|
||||
}
|
||||
|
||||
if (AreaStepList != null)
|
||||
{
|
||||
if (AreaStep < 0 || AreaStep >= AreaStepList.Count)
|
||||
{
|
||||
AreaStep = 0;
|
||||
Debug.LogError(
|
||||
$"[CampData]UpdateCurrentData: AreaStep({AreaStep}) is out of range[0-{AreaStepList.Count}]! Reset it to 0.");
|
||||
}
|
||||
|
||||
var step_id = AreaStepList[AreaStep];
|
||||
SetAreaStep(step_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"[CampData]UpdateCurrentData: AreaStepList is empty!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"[CampData]UpdateCurrentData: AreaList is empty!");
|
||||
}
|
||||
}
|
||||
|
||||
void SetArea()
|
||||
{
|
||||
constructionArea = GetArea(AreaId);
|
||||
if (constructionArea == null)
|
||||
{
|
||||
Debug.LogError($"[CampData]UpdateCurrentData: Not found data of area id({AreaId})!");
|
||||
}
|
||||
AreaIcon = constructionArea.Icon;
|
||||
TimelineId = constructionArea.TimelineID;
|
||||
|
||||
AreaStepCount = constructionArea.StepCount;
|
||||
AreaStepList = constructionArea.StepList;
|
||||
}
|
||||
void SetAreaStep(int step_id)
|
||||
{
|
||||
StepInfo = GetStep(step_id);
|
||||
|
||||
if (StepInfo == null)
|
||||
{
|
||||
Debug.LogError($"[CampData]UpdateCurrentData: Not found step data of id({step_id})!");
|
||||
}
|
||||
}
|
||||
void CompleteInit()
|
||||
{
|
||||
if (AreaList != null)
|
||||
{
|
||||
int curAreaIndex = AreaList.Count - 1;
|
||||
AreaId = AreaList[curAreaIndex];
|
||||
|
||||
SetArea();
|
||||
|
||||
if (AreaStepList != null)
|
||||
{
|
||||
var step_id = AreaStepList[^1];
|
||||
SetAreaStep(step_id);
|
||||
}
|
||||
if (NextMapID == -1 || !_tables.TbConstruction.DataMap.ContainsKey(NextMapID))
|
||||
{
|
||||
_isOpenSkyscraper = "1";
|
||||
SaveStore.Write("IsOpenSkyscraper", _isOpenSkyscraper).RequireAccepted();
|
||||
}
|
||||
}
|
||||
}
|
||||
void SetHome_Skyscraper()
|
||||
{
|
||||
if (IsOpenSkyscraper)
|
||||
{
|
||||
if (IsCanSkyscraper)
|
||||
{
|
||||
ulong curGold = GContext.container.Resolve<PlayerData>().gold;
|
||||
ulong basePrice =
|
||||
(ulong)GContext.container.Resolve<Tables>().TbMapData.DataList[^1].ConstrutionCost;
|
||||
var multiplierTable = GContext.container.Resolve<Tables>().TbConstructionInfiniteConfig.CashMagList;
|
||||
var ibData = new InfiniteBuildingData();
|
||||
ibData.Deserialize(GContext.container.Resolve<PlayerData>().InfiniteBuildingData);
|
||||
int step = ibData.Step;
|
||||
ulong price = (ulong)(basePrice * multiplierTable[step]);
|
||||
int count = 0;
|
||||
while (curGold >= price)
|
||||
{
|
||||
curGold -= price;
|
||||
step++;
|
||||
step %= multiplierTable.Count;
|
||||
price = (ulong)(basePrice * multiplierTable[step]);
|
||||
count++;
|
||||
if (count >= 99)
|
||||
break;
|
||||
}
|
||||
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Skyscraper, count > 0, (int)count);
|
||||
}
|
||||
else
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Skyscraper, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool GoBuilding()
|
||||
{
|
||||
if (AreaStep >= AreaStepList.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
long gold = (long)(ulong)GContext.container.Resolve<PlayerData>().gold;
|
||||
var buffTimeData = GContext.container.Resolve<BuffDataCenter>().GetWeelyBuffTimeData<ConstructionCost>();
|
||||
bool isConstructionCost = buffTimeData != null;
|
||||
long _upgradeCost;
|
||||
var step_id = AreaStepList[AreaStep];
|
||||
var camp_data = _tables.TbConstruction.DataMap[Id];
|
||||
List<int> _areaStepList = AreaStepList;
|
||||
int start;
|
||||
int endIndex;
|
||||
List<int> _areaList;
|
||||
_areaList = camp_data.AreaList;
|
||||
start = _tables.TbConstructionStep.DataList.IndexOf(_tables.TbConstructionStep.DataMap[step_id]);
|
||||
_areaStepList = GetArea(_areaList[^1]).StepList;
|
||||
endIndex = _tables.TbConstructionStep.DataList.IndexOf(_tables.TbConstructionStep.DataMap[_areaStepList[^1]]);
|
||||
List<ConstructionStep> DataList = _tables.TbConstructionStep.DataList;
|
||||
for (int i = start; i <= endIndex; i++)
|
||||
{
|
||||
if (isConstructionCost)
|
||||
{
|
||||
_upgradeCost = (long)(DataList[i].BuildRequired * (1 - buffTimeData.Param));
|
||||
}
|
||||
else
|
||||
{
|
||||
_upgradeCost = DataList[i].BuildRequired;
|
||||
}
|
||||
gold -= _upgradeCost;
|
||||
if (gold < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public bool GoBuilding02(int endId)
|
||||
{
|
||||
if (AreaStep >= AreaStepList.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var step_id = AreaStepList[AreaStep];
|
||||
if (step_id < 101003 || step_id >= endId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
long gold = (long)(ulong)GContext.container.Resolve<PlayerData>().gold;
|
||||
long _upgradeCost;
|
||||
|
||||
int start = _tables.TbConstructionStep.DataList.IndexOf(_tables.TbConstructionStep.DataMap[step_id]);
|
||||
int endIndex = _tables.TbConstructionStep.DataList.IndexOf(_tables.TbConstructionStep.DataMap[endId]);
|
||||
List<ConstructionStep> DataList = _tables.TbConstructionStep.DataList;
|
||||
for (int i = start; i < endIndex; i++)
|
||||
{
|
||||
_upgradeCost = DataList[i].BuildRequired;
|
||||
gold -= _upgradeCost;
|
||||
if (gold < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从当前 <see cref="AreaStep"/> 起,用给定金币连续扣款,返回能支付多少步;含 ConstructionCost buff,与建造红点累计逻辑一致。全程 <see cref="ulong"/>,避免金币超出 <c>long.MaxValue</c> 时强转失真。
|
||||
/// </summary>
|
||||
int CountAffordableConstructionSteps(ulong gold)
|
||||
{
|
||||
if (AreaStepList == null || AreaStep >= AreaStepList.Count)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var step_id = AreaStepList[AreaStep];
|
||||
int nextId = NextMapID;
|
||||
var camp_data = _tables.TbConstruction.DataMap[Id];
|
||||
while (_tables.TbConstruction.DataMap.TryGetValue(nextId, out var _camp_data))
|
||||
{
|
||||
camp_data = _camp_data;
|
||||
nextId = camp_data.NextID;
|
||||
}
|
||||
|
||||
var buffTimeData = GContext.container.Resolve<BuffDataCenter>().GetWeelyBuffTimeData<ConstructionCost>();
|
||||
bool isConstructionCost = buffTimeData != null;
|
||||
|
||||
List<int> _areaList = camp_data.AreaList;
|
||||
int start = _tables.TbConstructionStep.DataList.IndexOf(_tables.TbConstructionStep.DataMap[step_id]);
|
||||
List<int> _areaStepList = GetArea(_areaList[^1]).StepList;
|
||||
int endIndex = _tables.TbConstructionStep.DataList.IndexOf(_tables.TbConstructionStep.DataMap[_areaStepList[^1]]);
|
||||
int length = Math.Min(start + 100, endIndex + 1);
|
||||
List<ConstructionStep> DataList = _tables.TbConstructionStep.DataList;
|
||||
|
||||
int count = 0;
|
||||
for (int i = start; i < length; i++)
|
||||
{
|
||||
ulong stepCost = isConstructionCost
|
||||
? (ulong)(DataList[i].BuildRequired * (1 - buffTimeData.Param))
|
||||
: (ulong)DataList[i].BuildRequired;
|
||||
if (gold < stepCost)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
gold -= stepCost;
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public void SetRedPoint()
|
||||
{
|
||||
SetHome_Skyscraper();
|
||||
int count = 0;
|
||||
bool redPoint = false;
|
||||
var playerData = GContext.container.Resolve<PlayerData>();
|
||||
|
||||
if (GContext.container.Resolve<GuideDataCenter>().GetIsFinish(_tables.TbGlobalConfig.BuildRedPointUnlock)
|
||||
&& playerData.gold >= (ulong)StepInfo.BuildRequired)
|
||||
{
|
||||
if (AreaStepList == null || AreaStep >= AreaStepList.Count)
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Build, redPoint, count);
|
||||
return;
|
||||
}
|
||||
|
||||
count = CountAffordableConstructionSteps(playerData.gold);
|
||||
redPoint = true;
|
||||
}
|
||||
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Build, redPoint, count);
|
||||
}
|
||||
|
||||
|
||||
public int GetNextStepRequired(int allStep)
|
||||
{
|
||||
int price = 0;
|
||||
if (!IsOpenSkyscraper || !IsCanSkyscraper)
|
||||
{
|
||||
if (AreaStep < AreaStepList.Count)
|
||||
{
|
||||
var step_id = AreaStepList[AreaStep];
|
||||
List<int> BuildRequired = new List<int>();
|
||||
List<ConstructionStep> DataList = _tables.TbConstructionStep.DataList;
|
||||
int start = DataList.IndexOf(_tables.TbConstructionStep.DataMap[step_id]);
|
||||
int length = start + allStep;
|
||||
|
||||
length = Math.Min(length, DataList.Count);
|
||||
for (int i = start; i < length; i++)
|
||||
{
|
||||
allStep--;
|
||||
price += DataList[i].BuildRequired;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (allStep > 0)
|
||||
{
|
||||
int basePrice = GContext.container.Resolve<Tables>().TbMapData.DataList[^1].ConstrutionCost;
|
||||
var multiplierTable = GContext.container.Resolve<Tables>().TbConstructionInfiniteConfig.CashMagList;
|
||||
var ibData = new InfiniteBuildingData();
|
||||
ibData.Deserialize(GContext.container.Resolve<PlayerData>().InfiniteBuildingData);
|
||||
int step = ibData.Step;
|
||||
for (int i = 0; i < allStep; i++)
|
||||
{
|
||||
price += (int)(basePrice * multiplierTable[step]);
|
||||
step++;
|
||||
step %= multiplierTable.Count;
|
||||
}
|
||||
}
|
||||
return price;
|
||||
}
|
||||
public int GetStepRequired(int allStep)
|
||||
{
|
||||
int price = 0;
|
||||
var start_id = AreaStepList[AreaStep];
|
||||
var lastAreaStepList = GetArea(_tables.TbConstruction.DataMap[Id].AreaList[^1]).StepList;
|
||||
var end_id = lastAreaStepList[^1];
|
||||
if (end_id - start_id < allStep)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
List<ConstructionStep> DataList = _tables.TbConstructionStep.DataList;
|
||||
int start = DataList.IndexOf(_tables.TbConstructionStep.DataMap[start_id]);
|
||||
int length = start + allStep;
|
||||
for (int i = start; i < length; i++)
|
||||
{
|
||||
price += DataList[i].BuildRequired;
|
||||
}
|
||||
return price;
|
||||
}
|
||||
|
||||
public void NextStep()
|
||||
{
|
||||
if (AreaStepList != null)
|
||||
{
|
||||
AreaStep++;
|
||||
|
||||
if (AreaStep < AreaStepList.Count)
|
||||
{
|
||||
UpdateCurrentData();
|
||||
}
|
||||
else
|
||||
{
|
||||
AreaIndex++;
|
||||
if (AreaIndex < AreaList.Count)
|
||||
{
|
||||
AreaStep = 0;
|
||||
UpdateCurrentData();
|
||||
}
|
||||
else
|
||||
{
|
||||
AreaIndex = AreaList.Count;
|
||||
AreaStep = AreaStepList.Count;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GameDebug.LogError($"[CampData]NextStep: AreaStepList is empty!");
|
||||
}
|
||||
}
|
||||
public Area GetArea(int id)
|
||||
{
|
||||
// if (id < 102000)
|
||||
// {
|
||||
// if (_MMService.MMBuilding)
|
||||
// {
|
||||
// ConstructionMMTArea areaMM = _tables.TbConstructionMMTArea.GetOrDefault(id);
|
||||
// if (areaMM != null)
|
||||
// {
|
||||
// return new Area(areaMM);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
ConstructionArea area = _tables.TbConstructionArea.GetOrDefault(id);
|
||||
if (area != null)
|
||||
return new Area(area);
|
||||
return null;
|
||||
}
|
||||
public Step GetStep(int id)
|
||||
{
|
||||
// if (id < 102000)
|
||||
// {
|
||||
// if (_MMService.MMBuilding)
|
||||
// {
|
||||
// ConstructionMMTStep stepMM = _tables.TbConstructionMMTStep.GetOrDefault(id);
|
||||
// if (stepMM != null)
|
||||
// {
|
||||
// return new Step(stepMM);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
ConstructionStep step = _tables.TbConstructionStep.GetOrDefault(id);
|
||||
if (step != null)
|
||||
return new Step(step);
|
||||
return null;
|
||||
}
|
||||
public string GetBasePrefab(int id)
|
||||
{
|
||||
List<int> AreaList = _tables.TbConstruction.DataMap[id].AreaList;
|
||||
return GetArea(AreaList[0]).BuildingPrefeb;
|
||||
}
|
||||
|
||||
public string GetMMTString(string oldValue)
|
||||
{
|
||||
// if (_MMService.MMBuilding)
|
||||
// {
|
||||
// var mmt = _tables.TbMMTStringData.GetOrDefault(oldValue);
|
||||
// if (mmt != null)
|
||||
// {
|
||||
// return mmt.NewString;
|
||||
// }
|
||||
// }
|
||||
return oldValue;
|
||||
}
|
||||
}
|
||||
public class Area
|
||||
{
|
||||
public int ID { get; private set; }
|
||||
/// <summary>
|
||||
/// Building脚本中的序号,从0开始
|
||||
/// </summary>
|
||||
public int TimelineID { get; private set; }
|
||||
public int StepCount { get; private set; }
|
||||
public string BuildingPrefeb { get; private set; }
|
||||
/// <summary>
|
||||
/// 不同prefeb转换的时候,上一个timeline播放时间
|
||||
/// </summary>
|
||||
public float TimelinePlaytime { get; private set; }
|
||||
/// <summary>
|
||||
/// 多语言key + 中文
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
public string Name_l10n_key { get; }
|
||||
/// <summary>
|
||||
/// 多语言key + 中文
|
||||
/// </summary>
|
||||
public string NameIndex { get; private set; }
|
||||
public string NameIndex_l10n_key { get; }
|
||||
public string Icon { get; private set; }
|
||||
/// <summary>
|
||||
/// 对应Drop表主键
|
||||
/// </summary>
|
||||
public int Reward { get; private set; }
|
||||
public System.Collections.Generic.List<int> StepList { get; private set; }
|
||||
/// <summary>
|
||||
/// 用于室内关闭lookfar和looknear功能<br/>1 关闭
|
||||
/// </summary>
|
||||
public int TurnOffFreeLook { get; private set; }
|
||||
/// <summary>
|
||||
/// 用于室内关闭加速功能<br/>1 关闭
|
||||
/// </summary>
|
||||
public int TurnOffTimeScale { get; private set; }
|
||||
public Area(ConstructionArea area)
|
||||
{
|
||||
this.ID = area.ID;
|
||||
this.TimelineID = area.TimelineID;
|
||||
this.StepCount = area.StepCount;
|
||||
this.BuildingPrefeb = area.BuildingPrefeb;
|
||||
this.TimelinePlaytime = area.TimelinePlaytime;
|
||||
this.Name = area.Name;
|
||||
this.Name_l10n_key = area.Name_l10n_key;
|
||||
this.NameIndex = area.NameIndex;
|
||||
this.NameIndex_l10n_key = area.NameIndex_l10n_key;
|
||||
this.Icon = area.Icon;
|
||||
this.Reward = area.Reward;
|
||||
this.StepList = area.StepList;
|
||||
this.TurnOffFreeLook = area.TurnOffFreeLook;
|
||||
this.TurnOffTimeScale = area.TurnOffTimeScale;
|
||||
|
||||
}
|
||||
public Area(ConstructionMMTArea area)
|
||||
{
|
||||
this.ID = area.ID;
|
||||
this.TimelineID = area.TimelineID;
|
||||
this.StepCount = area.StepCount;
|
||||
this.BuildingPrefeb = area.BuildingPrefeb;
|
||||
this.TimelinePlaytime = area.TimelinePlaytime;
|
||||
this.Name = area.Name;
|
||||
this.Name_l10n_key = area.Name_l10n_key;
|
||||
this.NameIndex = area.NameIndex;
|
||||
this.NameIndex_l10n_key = area.NameIndex_l10n_key;
|
||||
this.Icon = area.Icon;
|
||||
this.Reward = area.Reward;
|
||||
this.StepList = area.StepList;
|
||||
this.TurnOffFreeLook = area.TurnOffFreeLook;
|
||||
this.TurnOffTimeScale = area.TurnOffTimeScale;
|
||||
}
|
||||
}
|
||||
|
||||
public class Step
|
||||
{
|
||||
public int ID { get; private set; }
|
||||
/// <summary>
|
||||
/// timeline中时间
|
||||
/// </summary>
|
||||
public float StartTime { get; private set; }
|
||||
/// <summary>
|
||||
/// 动画播放时间
|
||||
/// </summary>
|
||||
public float PlayTime { get; private set; }
|
||||
/// <summary>
|
||||
/// 建造所需钞票
|
||||
/// </summary>
|
||||
public int BuildRequired { get; private set; }
|
||||
/// <summary>
|
||||
/// 修复所需钞票
|
||||
/// </summary>
|
||||
public int FixRequired { get; private set; }
|
||||
|
||||
public Step(ConstructionStep step)
|
||||
{
|
||||
this.ID = step.ID;
|
||||
this.StartTime = step.StartTime;
|
||||
this.PlayTime = step.PlayTime;
|
||||
this.BuildRequired = step.BuildRequired;
|
||||
this.FixRequired = step.FixRequired;
|
||||
}
|
||||
public Step(ConstructionMMTStep step)
|
||||
{
|
||||
this.ID = step.ID;
|
||||
this.StartTime = step.StartTime;
|
||||
this.PlayTime = step.PlayTime;
|
||||
this.BuildRequired = step.BuildRequired;
|
||||
this.FixRequired = step.FixRequired;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,358 +0,0 @@
|
||||
using asap.core;
|
||||
using PlayFab;
|
||||
using PlayFab.ClientModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using game.PlayerDataStorage;
|
||||
|
||||
public class PlayFabMgr : MonoBehaviour
|
||||
{
|
||||
const int USER_DATA_UPDATE_INTERVAL = 40;
|
||||
const int Buffer_Count = 5;
|
||||
const string Last_Update_Time_Key_Prefix = "LastUpdateTime";
|
||||
const string Local_Data_Key_prefix = "LocalData";
|
||||
private string Last_Update_Time_Key;
|
||||
private string Local_Data_Key;
|
||||
|
||||
private LinkedList<Dictionary<string, string>> dataBuffer;
|
||||
private Dictionary<string, string> swapData;
|
||||
private Dictionary<string, long> lastUpdateTime;
|
||||
private Dictionary<string, string> localData;
|
||||
|
||||
private CancellationTokenSource tokenSource;
|
||||
|
||||
static PlayFabMgr _instance = null;
|
||||
// An inactive, never-awakened component proves that no timer or PF send has ever been started.
|
||||
internal bool IsUnstartedForKVLoad => tokenSource==null && dataBuffer==null && swapData==null && localData==null && lastUpdateTime==null && _instance!=this;
|
||||
internal static bool HasLiveInstanceForKVLoad => _instance!=null;
|
||||
|
||||
public static PlayFabMgr Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
GameDebug.LogError("PlayFabMgr Instance doesn't exist yet!");
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<string, string> CompareDatas(Dictionary<string, UserDataRecord> remoteDatas, string userId)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) throw PlayerKVGameplayLoop.Stop("PF archive comparison is disabled in KV loop.");
|
||||
#endif
|
||||
PlayerKVGameLoadGuard.RequireLegacyAllowed(PlayerKVGuardPoint.Compare);
|
||||
Last_Update_Time_Key = $"{Last_Update_Time_Key_Prefix}_{userId}";
|
||||
Local_Data_Key = $"{Local_Data_Key_prefix}_{userId}";
|
||||
var lastUpdateTimeStr = PlayerPrefs.GetString(Last_Update_Time_Key, string.Empty);
|
||||
var now = ZZTimeHelper.UtcNow().Ticks;
|
||||
localData = new Dictionary<string, string>();
|
||||
lastUpdateTime = new Dictionary<string, long>();
|
||||
|
||||
if (string.IsNullOrEmpty(lastUpdateTimeStr))
|
||||
{
|
||||
if (remoteDatas != null && remoteDatas.Count > 0)
|
||||
{
|
||||
lastUpdateTime = remoteDatas.ToDictionary(x => x.Key, x => x.Value.LastUpdated.Ticks);
|
||||
localData = remoteDatas.ToDictionary(x => x.Key, x => x.Value.Value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var tempLastUpdateTime = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, long>>(lastUpdateTimeStr);
|
||||
var localDataStr = PlayerPrefs.GetString(Local_Data_Key);
|
||||
var tempLocalData = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, string>>(localDataStr);
|
||||
var allKeys = tempLocalData.Keys.Concat(remoteDatas.Keys).Distinct();
|
||||
foreach (var key in allKeys)
|
||||
{
|
||||
var localValue = tempLocalData.ContainsKey(key) ? tempLocalData[key] : null;
|
||||
var remoteValue = remoteDatas.ContainsKey(key) ? remoteDatas[key].Value : null;
|
||||
var localTime = tempLastUpdateTime.ContainsKey(key) ? tempLastUpdateTime[key] : 0;
|
||||
var remoteTime = remoteDatas.ContainsKey(key) ? remoteDatas[key].LastUpdated.Ticks : 0;
|
||||
|
||||
if (localValue == null)
|
||||
{
|
||||
localData[key] = remoteValue;
|
||||
lastUpdateTime[key] = remoteTime;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (remoteValue == null)
|
||||
{
|
||||
UpdateUserDataValue(key, localValue);
|
||||
lastUpdateTime[key] = now;
|
||||
localData[key] = localValue;
|
||||
continue;
|
||||
}
|
||||
|
||||
var timeDelta = localTime - remoteTime;
|
||||
if (Math.Abs(timeDelta) > TimeSpan.TicksPerSecond * 60)
|
||||
{
|
||||
if (localTime > remoteTime)
|
||||
{
|
||||
UpdateUserDataValue(key, localValue);
|
||||
lastUpdateTime[key] = now;
|
||||
localData[key] = localValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastUpdateTime[key] = remoteTime;
|
||||
localData[key] = remoteValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lastUpdateTime[key] = localTime;
|
||||
localData[key] = localValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PlayerPrefs.SetString(Last_Update_Time_Key, LitJson.JsonMapper.ToJson(lastUpdateTime));
|
||||
PlayerPrefs.SetString(Local_Data_Key, LitJson.JsonMapper.ToJson(localData));
|
||||
return localData;
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Lifecycle)) return;
|
||||
_instance = this;
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) return;
|
||||
#endif
|
||||
InitBuffer();
|
||||
tokenSource = new CancellationTokenSource();
|
||||
UpdateUserDataValue(tokenSource.Token);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
internal void StartKVGameplayLoopSync()
|
||||
{
|
||||
if(!PlayerKVGameplayLoop.Ready || tokenSource!=null) return;
|
||||
tokenSource=new CancellationTokenSource();
|
||||
UpdateUserDataValue(tokenSource.Token);
|
||||
}
|
||||
internal void StopKVGameplayLoopSync()
|
||||
{ tokenSource?.Cancel(); tokenSource?.Dispose(); tokenSource=null; }
|
||||
#endif
|
||||
void InitBuffer()
|
||||
{
|
||||
dataBuffer = new LinkedList<Dictionary<string, string>>();
|
||||
for (int i = 0; i < Buffer_Count; i++)
|
||||
{
|
||||
dataBuffer.AddLast(new Dictionary<string, string>());
|
||||
}
|
||||
swapData = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
void OnApplicationPause(bool is_pause)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled)
|
||||
{
|
||||
if(is_pause) { StopKVGameplayLoopSync(); _=PlayerKVGameplayLoop.FlushObservedAsync(); }
|
||||
else StartKVGameplayLoopSync();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Lifecycle)) return;
|
||||
if (is_pause)
|
||||
{
|
||||
tokenSource.Cancel();
|
||||
tokenSource = null;
|
||||
SyncData();
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenSource = new CancellationTokenSource();
|
||||
UpdateUserDataValue(tokenSource.Token);
|
||||
}
|
||||
}
|
||||
|
||||
void OnApplicationQuit()
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) { StopKVGameplayLoopSync(); _=PlayerKVGameplayLoop.CloseAsync(); return; }
|
||||
#endif
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Lifecycle)) return;
|
||||
tokenSource.Cancel();
|
||||
tokenSource = null;
|
||||
SyncData();
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) { StopKVGameplayLoopSync(); _=PlayerKVGameplayLoop.CloseAsync(); return; }
|
||||
#endif
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Lifecycle)) return;
|
||||
tokenSource?.Cancel();
|
||||
tokenSource = null;
|
||||
if(!PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.PrefsSave)) PlayerPrefs.Save();
|
||||
SyncData();
|
||||
}
|
||||
|
||||
private async void UpdateUserDataValue(CancellationToken token)
|
||||
{
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Lifecycle)) return;
|
||||
try
|
||||
{
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
SyncData();
|
||||
await Task.Delay(USER_DATA_UPDATE_INTERVAL * 1000);
|
||||
}
|
||||
}
|
||||
catch (System.Exception) { }
|
||||
}
|
||||
|
||||
public void UpdateUserDataValue(string key, string data, bool remoteStore = true)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) { PlayerKVGameplayLoop.Write(key,data,remoteStore); return; }
|
||||
#endif
|
||||
PlayerKVGameLoadGuard.RequireLegacyAllowed(PlayerKVGuardPoint.Update);
|
||||
if (remoteStore)
|
||||
{
|
||||
var cbuff = dataBuffer.First;
|
||||
while (cbuff != null)
|
||||
{
|
||||
var datas = cbuff.Value;
|
||||
if (!datas.ContainsKey(key) && datas.Count >= 10)
|
||||
{
|
||||
cbuff = cbuff.Next;
|
||||
continue;
|
||||
}
|
||||
cbuff.Value[key] = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
localData[key] = data;
|
||||
lastUpdateTime[key] = ZZTimeHelper.UtcNow().Ticks;
|
||||
//Debug.Log($"[PlayFabMgr]UpdateUserDataValue key:{key} data:{data}");
|
||||
PlayerPrefs.SetString(Last_Update_Time_Key, LitJson.JsonMapper.ToJson(lastUpdateTime));
|
||||
PlayerPrefs.SetString(Local_Data_Key, LitJson.JsonMapper.ToJson(localData));
|
||||
}
|
||||
/// <summary>
|
||||
/// 没有数据返回 null 一定一定一定判空!!!
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public string GetLocalData(string key)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) return PlayerKVGameplayLoop.Read(key);
|
||||
#endif
|
||||
PlayerKVGameLoadGuard.RequireLegacyAllowed(PlayerKVGuardPoint.ReadLocal);
|
||||
if (localData.ContainsKey(key))
|
||||
{
|
||||
return localData[key];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void SyncData()
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) { _=PlayerKVGameplayLoop.FlushObservedAsync(); return; }
|
||||
#endif
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Sync)) return;
|
||||
// Logout clears credentials before RootCtx is destroyed. Keep pending data local.
|
||||
if (!PlayFabClientAPI.IsClientLoggedIn()) return;
|
||||
var buf = dataBuffer.First;
|
||||
if (buf.Value.Count == 0)
|
||||
return;
|
||||
|
||||
var datas = buf.Value;
|
||||
swapData.Clear();
|
||||
buf.Value = swapData;
|
||||
swapData = datas;
|
||||
|
||||
dataBuffer.RemoveFirst();
|
||||
dataBuffer.AddLast(buf);
|
||||
|
||||
var request = new UpdateUserDataRequest()
|
||||
{
|
||||
Data = swapData,
|
||||
Permission = UserDataPermission.Public
|
||||
};
|
||||
|
||||
PlayFabClientAPI.UpdateUserData(request, null, UpdateFaild);
|
||||
#if UNITY_EDITOR
|
||||
//IConfig config = GContext.container.Resolve<IConfig>();
|
||||
//if (config != null && config.Get<string>("DBUG", string.Empty) == "1")
|
||||
//{
|
||||
try
|
||||
{
|
||||
//写入本地文件记录 key 和时间
|
||||
string persistentPath = Application.persistentDataPath;
|
||||
// 拼接文件名,这里使用txt文件记录数据
|
||||
string filePath = System.IO.Path.Combine(persistentPath, "syncData.csv");
|
||||
string currentTime = ZZTimeHelper.UtcNow().ToString("yyyy-MM-dd HH:mm:ss");
|
||||
string dataLine = $"{currentTime},{string.Join(',', swapData.Keys)}";
|
||||
// 使用追加模式写入文件,每次写入一行数据
|
||||
using (System.IO.StreamWriter writer = System.IO.File.AppendText(filePath))
|
||||
{
|
||||
writer.WriteLine(dataLine);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"记录数据时出错: {e.Message}");
|
||||
}
|
||||
//}
|
||||
#endif
|
||||
}
|
||||
|
||||
private void UpdateFaild(PlayFabError error)
|
||||
{
|
||||
GameDebug.LogError("[PlayFabMgr]UpdateUserData Error: {0}", error.ToString());
|
||||
}
|
||||
|
||||
internal void FlushBufferedData() => SyncData();
|
||||
|
||||
public async Task<bool> SyncDataImmidiate(string key, string data)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) throw PlayerKVGameplayLoop.Stop("Immediate/consuming saves are outside the equipment loop.");
|
||||
#endif
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Immediate)) return false;
|
||||
foreach (var buff in dataBuffer)
|
||||
{
|
||||
if (buff.ContainsKey(key))
|
||||
{
|
||||
buff.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
var taskSource = new TaskCompletionSource<bool>();
|
||||
|
||||
var request = new UpdateUserDataRequest()
|
||||
{
|
||||
Data = new Dictionary<string, string>() { { key, data } },
|
||||
Permission = UserDataPermission.Public
|
||||
};
|
||||
|
||||
PlayFabClientAPI.UpdateUserData(request,
|
||||
(r) => { taskSource.SetResult(true); },
|
||||
(e) =>
|
||||
{
|
||||
taskSource.SetResult(false);
|
||||
UnityEngine.Debug.LogError($"[PlayFabMgr]UpdateUserData Error: {e.ToString()}");
|
||||
}
|
||||
);
|
||||
return await taskSource.Task;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,321 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using game;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class BargainPackData
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore SaveStore { get; set; }
|
||||
|
||||
private readonly Tables _tables = GContext.container.Resolve<Tables>();
|
||||
private readonly TbEventPackManager _epm = GContext.container.Resolve<Tables>().TbEventPackManager;
|
||||
private readonly TbSpecialPack _spp = GContext.container.Resolve<Tables>().TbSpecialPack;
|
||||
private GeneralEventPackData _data;
|
||||
|
||||
private bool _kvLoadPresent;
|
||||
#if UNITY_EDITOR
|
||||
internal bool SelectionsAreIndependentForKVValidation()
|
||||
{
|
||||
var source = new List<int> { 7, 9 };
|
||||
var state = new game.PlayerDataStorage.PlayerKVBargainState(true,0,_data.redirectID,0,0,0,0,0,0,0,0,0,false,false,0,source);
|
||||
ReplaceFromKVDecoded(state);
|
||||
source.Clear();
|
||||
if (_data.selections.Count != 2 || ReferenceEquals(_data.selections, source) || ReferenceEquals(_data.selections, state.selections)) return false;
|
||||
var previous = _data.selections;
|
||||
_data.selections[0] = 10;
|
||||
if (state.selections[0] != 7) return false;
|
||||
ResetForKVLoad();
|
||||
return _data.selections.Count == 0 && !ReferenceEquals(previous, _data.selections) && !_kvLoadPresent;
|
||||
}
|
||||
#endif
|
||||
internal bool KVLoadPresent => _kvLoadPresent;
|
||||
internal static game.PlayerDataStorage.PlayerKVBargainState DecodeForKVLoad(
|
||||
IReadOnlyDictionary<string, string> raw, game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,
|
||||
game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodeBargain(raw, snapshot, errors);
|
||||
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
_kvLoadPresent = false;
|
||||
_data = default(GeneralEventPackData);
|
||||
_data.selections = new List<int>();
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVBargainState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
if (!state.IsPresent) { ResetForKVLoad(); return; }
|
||||
var replacement = new GeneralEventPackData
|
||||
{
|
||||
currentEventID = state.currentEventID,
|
||||
redirectID = state.redirectID,
|
||||
VIPLvlWhenEventActivate = state.VIPLvlWhenEventActivate,
|
||||
purchaseCount = state.purchaseCount,
|
||||
savingProgress = state.savingProgress,
|
||||
discountLvl = state.discountLvl,
|
||||
visualProgress = state.visualProgress,
|
||||
visualDiscountLvl = state.visualDiscountLvl,
|
||||
rodID = state.rodID,
|
||||
lastPackIDBought = state.lastPackIDBought,
|
||||
packID = state.packID,
|
||||
doNeedTrigger = state.doNeedTrigger,
|
||||
isTriggered = state.isTriggered,
|
||||
inflation = state.inflation,
|
||||
selections = state.selections == null ? null : new List<int>(state.selections),
|
||||
};
|
||||
_data = replacement;
|
||||
_kvLoadPresent = true;
|
||||
}
|
||||
internal game.PlayerDataStorage.PlayerKVBargainState SnapshotForKVValidation() =>
|
||||
new game.PlayerDataStorage.PlayerKVBargainState(_kvLoadPresent, _data.currentEventID, _data.redirectID, _data.VIPLvlWhenEventActivate, _data.purchaseCount, _data.savingProgress, _data.discountLvl, _data.visualProgress, _data.visualDiscountLvl, _data.rodID, _data.lastPackIDBought, _data.packID, _data.doNeedTrigger, _data.isTriggered, _data.inflation, _data.selections);
|
||||
|
||||
public int CurrentEventID => _data.currentEventID;
|
||||
|
||||
public FishingEvent CurrentEvent => _tables.TbFishingEvent[_data.currentEventID];
|
||||
|
||||
public TimeSpan RemainingTime =>
|
||||
DateTime.Parse((_tables.TbFishingEvent[_data.currentEventID].TimeDefinition as LimitedTime).EndTime) -
|
||||
ZZTimeHelper.UtcNow();
|
||||
|
||||
public bool IsWithinEventTime
|
||||
{
|
||||
get
|
||||
{
|
||||
var fishingEvent = _tables.TbFishingEvent.GetOrDefault(CurrentEventID);
|
||||
if (!(fishingEvent?.TimeDefinition is LimitedTime time)) return false;
|
||||
|
||||
var et = DateTime.Parse(time.EndTime);
|
||||
var st = DateTime.Parse(time.StartTime);
|
||||
return ZZTimeHelper.UtcNow() >= st && ZZTimeHelper.UtcNow() < et;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPackActivated
|
||||
{
|
||||
get
|
||||
{
|
||||
bool res = _tables.TbFishingEvent.DataMap.ContainsKey(_data.currentEventID)
|
||||
&& _epm.DataMap.ContainsKey(_data.redirectID)
|
||||
&& _epm[_data.redirectID].PackType == 3
|
||||
&& IsWithinEventTime
|
||||
&& _data.isTriggered
|
||||
&& _data.purchaseCount < _epm[_data.redirectID].MaxCount;
|
||||
//Debug.Log(_tables.TbFishingEvent.DataMap.ContainsKey(_data.currentEventID));
|
||||
//Debug.Log(_epm.DataMap.ContainsKey(_data.redirectID));
|
||||
//Debug.Log(_epm[_data.redirectID].PackType == 3);
|
||||
//Debug.Log(IsWithinEventTime);
|
||||
//Debug.Log(_data.isTriggered);
|
||||
//Debug.Log(_data.purchaseCount < _epm[_data.redirectID].MaxCount);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
public int RedirectID => _data.redirectID;
|
||||
public bool DoNeedTrigger => _data.doNeedTrigger;
|
||||
public int PackID => _epm[_data.redirectID].VIPPackList[_data.VIPLvlWhenEventActivate][0];
|
||||
public int DiscountLvlCount => _spp[PackID].IAPID.Count;
|
||||
public int DiscountLvl => _data.discountLvl;
|
||||
public int PurchaseCount => _data.purchaseCount;
|
||||
public int Progress => _data.savingProgress;
|
||||
public int VIPLvlWhenEventActivated => _data.VIPLvlWhenEventActivate;
|
||||
public int ActivateItemId => _spp[PackID].ActivateItemId;
|
||||
public string ActiveItemImg => _spp[PackID].ActiveItemImg;
|
||||
public int EventItemId => IsPackActivated ? _spp[PackID].EventItemId : 0;
|
||||
public int RewardDropID => _spp[PackID].DropID[0];
|
||||
public int MaxCount => _epm[_data.redirectID].MaxCount;
|
||||
public float Inflation => _data.inflation;
|
||||
|
||||
public int NextTarget
|
||||
{
|
||||
get
|
||||
{
|
||||
int lvl = _data.discountLvl >= DiscountLvlCount - 1 ? DiscountLvlCount - 1 : _data.discountLvl + 1;
|
||||
int count = _spp[PackID].EventItemRequire[lvl];
|
||||
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
count = playerItemData.IntInflation(count, _data.inflation);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsFull => _data.discountLvl >= DiscountLvlCount - 1 && _data.savingProgress >= NextTarget;
|
||||
public int IAPID => _spp[PackID].IAPID[DiscountLvl];
|
||||
public List<int> FishingScoreList => _spp[PackID].EventItemGet;
|
||||
public int NextDiscount
|
||||
{
|
||||
get
|
||||
{
|
||||
int lvl = _data.discountLvl >= DiscountLvlCount - 1 ? DiscountLvlCount - 1 : _data.discountLvl + 1;
|
||||
return _spp[PackID].DiscountList[lvl];
|
||||
}
|
||||
}
|
||||
public int VisualProgress => _data.visualProgress;
|
||||
public int VisualDiscountLvl => _data.visualDiscountLvl;
|
||||
public int NextVisualTarget
|
||||
{
|
||||
get
|
||||
{
|
||||
int lvl = _data.visualDiscountLvl >= DiscountLvlCount - 1
|
||||
? DiscountLvlCount - 1
|
||||
: _data.visualDiscountLvl + 1;
|
||||
int EventItemRequire = _spp[PackID].EventItemRequire[lvl];
|
||||
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
EventItemRequire = playerItemData.IntInflation(EventItemRequire, _data.inflation);
|
||||
return EventItemRequire;
|
||||
}
|
||||
}
|
||||
|
||||
public int NextVisualDiscount
|
||||
{
|
||||
get
|
||||
{
|
||||
int lvl = _data.visualDiscountLvl >= DiscountLvlCount - 1
|
||||
? DiscountLvlCount - 1
|
||||
: _data.visualDiscountLvl + 1;
|
||||
return _spp[PackID].DiscountList[lvl];
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveData()
|
||||
{
|
||||
//Debug.LogError("Testing, data not saved for debug purpose.");
|
||||
SaveStore.Write("GeneralEventPackData",
|
||||
Newtonsoft.Json.JsonConvert.SerializeObject(_data)).RequireAccepted();
|
||||
}
|
||||
|
||||
public void LoadData(GeneralEventPackData data)
|
||||
{
|
||||
_data = data;
|
||||
if (IsPackActivated && IsFull)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIForce(_data.currentEventID, UITypes.GiftBargainPopupPanel);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateData(FishingEvent e)
|
||||
{
|
||||
if (_epm[_tables.TbFishingEvent[e.ID].RedirectID].PackType != 3)
|
||||
{
|
||||
Debug.LogError($"Fishing event ID {e.ID} is not a Bargain back event");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_data.currentEventID != e.ID)
|
||||
{
|
||||
_data = new GeneralEventPackData(
|
||||
eventID: e.ID,
|
||||
vip: GContext.container.Resolve<PlayerData>().PriceLv,
|
||||
redirectID: e.RedirectID,
|
||||
savingProgress: 0,
|
||||
discountLvl: 0,
|
||||
isTriggered: false,
|
||||
doNeedTrigger: true,
|
||||
visualProgress: 0,
|
||||
visualDiscountLvl: 0,
|
||||
purchaseCount: 0,
|
||||
inflation: GContext.container.Resolve<PlayerData>().InflationRate);
|
||||
}
|
||||
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public void TriggerPack()
|
||||
{
|
||||
_data.doNeedTrigger = false;
|
||||
_data.isTriggered = true;
|
||||
int EventItemCountFirst = _spp[PackID].EventItemCountFirst;
|
||||
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
EventItemCountFirst = playerItemData.IntInflation(EventItemCountFirst, _data.inflation);
|
||||
AddProgress(EventItemCountFirst);
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public void AddProgress(int progress)
|
||||
{
|
||||
if (progress <= 0 || IsFull)
|
||||
return;
|
||||
_data.savingProgress += progress;
|
||||
while (_data.savingProgress >= NextTarget)
|
||||
{
|
||||
//DoUICut = true;
|
||||
_data.savingProgress -= NextTarget;
|
||||
_data.discountLvl++;
|
||||
if (_data.discountLvl >= DiscountLvlCount - 1)
|
||||
{
|
||||
_data.savingProgress = NextTarget;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SaveData();
|
||||
return;
|
||||
}
|
||||
|
||||
public void AddProgress(int fishTier, int magnification)
|
||||
{
|
||||
if (!IsPackActivated || fishTier < 1 || fishTier > 5)
|
||||
{
|
||||
//Debug.LogError($"Fish Tier {fishTier} not defined in TbSpedialPack");
|
||||
return;
|
||||
}
|
||||
|
||||
int progress = _spp[PackID].EventItemGet[fishTier - 1] * magnification;
|
||||
if (progress <= 0 || IsFull)
|
||||
return;
|
||||
|
||||
int curProgress = _data.savingProgress;
|
||||
_data.savingProgress += progress;
|
||||
GContext.Publish(new TargetAddData(EventItemId, curProgress, progress));
|
||||
while (_data.savingProgress >= NextTarget)
|
||||
{
|
||||
//DoUICut = true;
|
||||
_data.savingProgress -= NextTarget;
|
||||
_data.discountLvl++;
|
||||
if (_data.discountLvl >= DiscountLvlCount - 1)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>()
|
||||
.AddGiftFaceUIForce(CurrentEventID, UITypes.GiftBargainPopupPanel);
|
||||
_data.savingProgress = NextTarget;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SaveData();
|
||||
return;
|
||||
}
|
||||
|
||||
public void AddPurchase()
|
||||
{
|
||||
_data.purchaseCount++;
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public void RefreshVisualData()
|
||||
{
|
||||
_data.visualDiscountLvl = _data.discountLvl;
|
||||
_data.visualProgress = _data.savingProgress;
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public int GetNextDiscountTarget(int l)
|
||||
{
|
||||
int lvl = l >= DiscountLvlCount - 1 ? DiscountLvlCount - 1 : l + 1;
|
||||
int count = _spp[PackID].EventItemRequire[lvl];
|
||||
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
count = playerItemData.IntInflation(count, _data.inflation);
|
||||
return count;
|
||||
}
|
||||
|
||||
public int GetNextDiscount(int l)
|
||||
{
|
||||
int lvl = l >= DiscountLvlCount - 1 ? DiscountLvlCount - 1 : l + 1;
|
||||
return _spp[PackID].DiscountList[lvl];
|
||||
}
|
||||
}
|
||||
|
||||
public class BargainPackProgressEvent
|
||||
{
|
||||
public int type; //0: 初始化 1:增加动画
|
||||
public int addProgress;
|
||||
}
|
||||
@@ -1,923 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using tysdk;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
public class GetBattlePassRewardEvent
|
||||
{
|
||||
public int NormalLevel;
|
||||
public int VipLevel;
|
||||
}
|
||||
|
||||
public struct GetAllBPRewards
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public struct BpGetExp
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public struct BpVipUnlock
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class BattlePassData
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore playerSaveStore { get; set; }
|
||||
public BattlePassRecord BattlePassRecord;
|
||||
public BattleTask BattleTask;
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVBattlePassState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodeBattlePass(raw,snapshot,errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
BattlePassRecord=new BattlePassRecord();
|
||||
BattleTask=new BattleTask();
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVBattlePassState state)
|
||||
{
|
||||
if(state==null) throw new ArgumentNullException(nameof(state));
|
||||
var record=new BattlePassRecord {
|
||||
ID=state.Record.ID,Level=state.Record.Level,Exp=state.Record.Exp,BuyVipToastIndex=state.Record.BuyVipToastIndex,
|
||||
IsUnlockVip=state.Record.IsUnlockVip,NormalRewardGet=new List<int>(state.Record.Normal),VipRewardGet=new List<int>(state.Record.Vip),
|
||||
LastDropIDs=new List<int>(state.Record.LastDrops),InflationRate=state.Record.Inflation
|
||||
};
|
||||
var task=new BattleTask {
|
||||
dic_dailyTasks=state.Task.Daily.ToDictionary(p=>p.Key,p=>p.Value),dic_weeklyTasks=state.Task.Weekly.ToDictionary(p=>p.Key,p=>p.Value),
|
||||
loginDays=new List<int>(state.Task.LoginDays),DailyTaskEndTime=state.Task.DailyEnd,WeeklyTaskEndTime=state.Task.WeeklyEnd
|
||||
};
|
||||
BattlePassRecord=record; BattleTask=task;
|
||||
}
|
||||
|
||||
private Tables _tables;
|
||||
|
||||
|
||||
public BattlePassData(Tables tables)
|
||||
{
|
||||
if (BattlePassRecord == null)
|
||||
BattlePassRecord = new BattlePassRecord();
|
||||
if (BattleTask == null)
|
||||
BattleTask = new BattleTask();
|
||||
|
||||
_tables = tables;
|
||||
}
|
||||
|
||||
#region Load&Save
|
||||
public void InitData(Dictionary<string, string> userDatas = null)
|
||||
{
|
||||
if (userDatas.TryGetValue("BattlePassRecordTask", out string battlePassRecordTask))
|
||||
{
|
||||
|
||||
LoadBattlePassRecordTask(battlePassRecordTask);
|
||||
}
|
||||
else
|
||||
{
|
||||
//旧数据兼容
|
||||
if (userDatas.TryGetValue("BattlePassRecord", out string data_value))
|
||||
{
|
||||
LoadBattlePassRecord(data_value);
|
||||
}
|
||||
if (userDatas.TryGetValue("BattleTask", out data_value))
|
||||
{
|
||||
LoadBattleTask(data_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void LoadBattlePassRecordTask(string data)
|
||||
{
|
||||
string[] values = data.Split('#');
|
||||
BattlePassRecord = new BattlePassRecord();
|
||||
BattlePassRecord.DeserializeObject(values[0]);
|
||||
|
||||
BattleTask = new BattleTask();
|
||||
BattleTask.DeserializeObject(values[1]);
|
||||
}
|
||||
public void LoadBattlePassRecord(string data)
|
||||
{
|
||||
BattlePassRecord = JsonConvert.DeserializeObject<BattlePassRecord>(data);
|
||||
}
|
||||
public void LoadBattleTask(string data)
|
||||
{
|
||||
BattleTask = JsonConvert.DeserializeObject<BattleTask>(data);
|
||||
}
|
||||
void SaveBattlePassRecordTask()
|
||||
{
|
||||
string battlePassRecord = BattlePassRecord.SerializeObject();
|
||||
string battleTask = BattleTask.SerializeObject();
|
||||
playerSaveStore.Write("BattlePassRecordTask", battlePassRecord + "#" + battleTask).RequireAccepted();
|
||||
}
|
||||
public void SaveBattlePassRecord()
|
||||
{
|
||||
GContext.container.Resolve<BattlePassDataProvider>().SetRedPoint();
|
||||
SaveBattlePassRecordTask();
|
||||
//PlayFabMgr.Instance.UpdateUserDataValue("BattlePassRecord", JsonConvert.SerializeObject(BattlePassRecord));
|
||||
}
|
||||
|
||||
public void SaveBattleTask()
|
||||
{
|
||||
GContext.container.Resolve<BattlePassDataProvider>().SetRedPoint();
|
||||
SaveBattlePassRecordTask();
|
||||
//PlayFabMgr.Instance.UpdateUserDataValue("BattleTask", JsonConvert.SerializeObject(BattleTask));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region BattlePass
|
||||
|
||||
public void RefreshBattlePassRecord(FishingEvent t = null)
|
||||
{
|
||||
//没有开启的战令
|
||||
if (t == null)
|
||||
{
|
||||
GContext.container.Resolve<BattlePassDataProvider>().RecordLastBPUnGetDropIDs();
|
||||
BattlePassRecord.ID = 0;
|
||||
SaveBattlePassRecord();
|
||||
return;
|
||||
}
|
||||
if (t.ID != BattlePassRecord.ID)
|
||||
{
|
||||
GContext.container.Resolve<BattlePassDataProvider>().RecordLastBPUnGetDropIDs();
|
||||
BattlePassRecord.Init(t.ID);
|
||||
RefreshDaiyTask();
|
||||
RefreshWeeklyTask();
|
||||
SaveBattlePassRecord();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Task
|
||||
|
||||
public void RefreshDaiyTask()
|
||||
{
|
||||
var fishingEvent = _tables.TbFishingEvent.GetOrDefault(BattlePassRecord.ID);
|
||||
if (fishingEvent == null) return;
|
||||
var bpMain = _tables.TbBattlePassMain.GetOrDefault(fishingEvent.RedirectID);
|
||||
if (bpMain == null) return;
|
||||
BattleTask.DailyTaskEndTime = ConvertTools.GetDateTimeYMD(ZZTimeHelper.UtcNow().UtcNowOffset()).AddDays(1);
|
||||
var dailyTasks = bpMain.DailyTaskList;
|
||||
BattleTask.dic_dailyTasks.Clear();
|
||||
foreach (var task in dailyTasks)
|
||||
{
|
||||
BattleTask.dic_dailyTasks.Add(task, 0);
|
||||
}
|
||||
GContext.Publish(new ConditionTypeEvent { type = ConditionType.LoginDays, count = 1 });
|
||||
SaveBattleTask();
|
||||
}
|
||||
|
||||
public void RefreshWeeklyTask()
|
||||
{
|
||||
var fishingEvent = _tables.TbFishingEvent.GetOrDefault(BattlePassRecord.ID);
|
||||
if (fishingEvent == null) return;
|
||||
var bpMain = _tables.TbBattlePassMain.GetOrDefault(fishingEvent.RedirectID);
|
||||
if (bpMain == null) return;
|
||||
var weeklyTasks = bpMain.WeeklyTaskList;
|
||||
BattleTask.dic_weeklyTasks.Clear();
|
||||
foreach (var task in weeklyTasks)
|
||||
{
|
||||
BattleTask.dic_weeklyTasks.Add(task, 0);
|
||||
}
|
||||
BattleTask.WeeklyTaskEndTime = ConvertTools.GetDateTimeSunDay(ZZTimeHelper.UtcNow().UtcNowOffset()).AddDays(1);
|
||||
BattlePassRecord.InflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
|
||||
GContext.Publish(new ConditionTypeEvent { type = ConditionType.LoginDays, count = 1 });
|
||||
BattleTask.loginDays.Clear();
|
||||
SaveBattleTask();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
public class BattlePassDataProvider : IDisposable
|
||||
{
|
||||
#region Fields
|
||||
|
||||
public BattlePassData Data;
|
||||
private PlayerItemData _itemData;
|
||||
|
||||
public int LockTipID;
|
||||
|
||||
public int DefaultBuyLevel
|
||||
{
|
||||
get
|
||||
{
|
||||
return curPassMain.BuyLevel;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private Tables _tables;
|
||||
private Dictionary<int, BattlePassLevel> dic_BPLevel;
|
||||
|
||||
public bool IsActivating => Data.BattlePassRecord.ID != 0 && curPassMain != null;
|
||||
public bool UILockVip = false;
|
||||
public bool IsUnlockVip => Data.BattlePassRecord.IsUnlockVip == 1;
|
||||
|
||||
CompositeDisposable disposables = new CompositeDisposable();
|
||||
|
||||
public BattlePassMain curPassMain
|
||||
{
|
||||
get
|
||||
{
|
||||
var fishingEvent = _tables.TbFishingEvent.GetOrDefault(Data.BattlePassRecord.ID);
|
||||
if (fishingEvent == null) { return null; }
|
||||
|
||||
return _tables.TbBattlePassMain.GetOrDefault(fishingEvent.RedirectID);
|
||||
}
|
||||
}
|
||||
public BattlePassLevel curPassLevel
|
||||
{
|
||||
get
|
||||
{
|
||||
if (curPassMain != null)
|
||||
return dic_BPLevel.GetValueOrDefault(curPassMain.LevelList[Data.BattlePassRecord.Level - 1]);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public BattlePassDataProvider(Tables tables, BattlePassData data, PlayerItemData itemData)
|
||||
{
|
||||
_tables = tables;
|
||||
Data = data;
|
||||
dic_BPLevel = tables.TbBattlePassLevel.DataMap;
|
||||
_itemData = itemData;
|
||||
GContext.OnEvent<ConditionTypeEvent>().Subscribe(UpdateTaskData).AddTo(disposables);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Check Func
|
||||
|
||||
public void CheckIfRefreshBattblePass()
|
||||
{
|
||||
if (!IsActivating) return;
|
||||
DateTime endTime = GContext.container.Resolve<FishingEventData>().GetEventEndTime(Data.BattlePassRecord.ID);
|
||||
|
||||
if (ZZTimeHelper.UtcNow().UtcNowOffset() >= endTime && IsActivating)
|
||||
{
|
||||
RecordLastBPUnGetDropIDs();
|
||||
Data.RefreshBattlePassRecord();
|
||||
//GContext.container.Resolve<FishingEventData>().GetEventAndInit(3, 7);
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckIfRefreshDailyTask()
|
||||
{
|
||||
if (ZZTimeHelper.UtcNow().UtcNowOffset() >= Data.BattleTask.DailyTaskEndTime && IsActivating)
|
||||
{
|
||||
Data.RefreshDaiyTask();
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckIfRefreshWeekTask()
|
||||
{
|
||||
|
||||
if (ZZTimeHelper.UtcNow().UtcNowOffset() >= Data.BattleTask.WeeklyTaskEndTime && IsActivating)
|
||||
{
|
||||
Data.RefreshWeeklyTask();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CheckIfPopBuyVipPanel()
|
||||
{
|
||||
if (Data.BattlePassRecord.BuyVipToastIndex >= curPassMain.RemindLevel.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Data.BattlePassRecord.Level >= curPassMain.RemindLevel[Data.BattlePassRecord.BuyVipToastIndex] && Data.BattlePassRecord.IsUnlockVip == 0;
|
||||
}
|
||||
|
||||
public bool CheckIfGetLastBpRewards()
|
||||
{
|
||||
if (LastBpDropIds.Count != 0)
|
||||
{
|
||||
var playerData = GContext.container.Resolve<PlayerItemData>();
|
||||
ChangeSource changeSource = new ChangeSource(Data.BattlePassRecord.ID, "BattlePass", "BattlePass_Normal28Day", "ExpiredReward");
|
||||
|
||||
var itemDatas = playerData.AddItemByDropListLureInflation(null, LastBpDropIds.GetRange(1, LastBpDropIds.Count - 1), changeSource);
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("bp_level_reward"))
|
||||
{
|
||||
e.AddContent("bp_level", GetBPCurLevel)
|
||||
.AddContent("drop_id_list", JsonConvert.SerializeObject(LastBpDropIds.GetRange(1, LastBpDropIds.Count - 1)));
|
||||
if (itemDatas != null && itemDatas.Count > 0)
|
||||
{
|
||||
int reward_hook = 0;
|
||||
int hook_origin = 0;
|
||||
int reward_cash = 0;
|
||||
for (int i = 0; i < itemDatas.Count; i++)
|
||||
{
|
||||
if (itemDatas[i].id == 1001)
|
||||
{
|
||||
reward_hook += itemDatas[i].count;
|
||||
hook_origin += itemDatas[i].origin;
|
||||
}
|
||||
else if (itemDatas[i].id == 1002)
|
||||
{
|
||||
reward_cash += itemDatas[i].count;
|
||||
}
|
||||
}
|
||||
e.AddContent("reward_hook", reward_hook)
|
||||
.AddContent("hook_origin", hook_origin)
|
||||
.AddContent("reward_cash", reward_cash);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (LastBpDropIds[0] > 0)
|
||||
{
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("bp_cash_reward"))
|
||||
{
|
||||
e.AddContent("bp_level", GetBPCurLevel)
|
||||
.AddContent("cash_num", LastBpDropIds[0]);
|
||||
}
|
||||
#endif
|
||||
|
||||
var cash = new ItemData { id = 1002, count = LastBpDropIds[0] };
|
||||
ChangeSource changeSource1 = new ChangeSource(Data.BattlePassRecord.ID, "BattlePass", "BattlePass_Normal28Day", "OverflowReward");
|
||||
|
||||
playerData.AddItem(cash, changeSource1);
|
||||
GContext.Publish(new ShowData(cash));
|
||||
}
|
||||
GContext.Publish(new ShowData(textValue: LocalizationMgr.GetText("UI_BattlePassPanel_25")));
|
||||
LastBpDropIds.Clear();
|
||||
Data.SaveBattlePassRecord();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Get Func
|
||||
|
||||
public TimeSpan GetDailyTaskDuration()
|
||||
{
|
||||
return Data.BattleTask.DailyTaskEndTime - ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
}
|
||||
|
||||
public TimeSpan GetWeeklyTaskDuration()
|
||||
{
|
||||
return Data.BattleTask.WeeklyTaskEndTime - ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
}
|
||||
|
||||
public int GetBPCurLevel => Data.BattlePassRecord.Level;
|
||||
public int GetBPNextLevel => Mathf.Clamp(Data.BattlePassRecord.Level + 1, 1, BPMaxLevel);
|
||||
|
||||
public int BPMaxLevel => curPassMain.LevelList.Count;
|
||||
public List<int> LastBpDropIds => Data.BattlePassRecord.LastDropIDs;
|
||||
|
||||
public BattlePassLevel GetBPLevel(int level) => dic_BPLevel[curPassMain.LevelList[level - 1]];
|
||||
|
||||
public int GetBPCurExp() => Data.BattlePassRecord.Exp;
|
||||
|
||||
public int GetNeedExpToLevelUp(int upLevel)
|
||||
{
|
||||
var curLevel = Data.BattlePassRecord.Level;
|
||||
return GetTotalExpFromLevelRange(curLevel, curLevel + upLevel) - Data.BattlePassRecord.Exp;
|
||||
}
|
||||
|
||||
public int GetCashBoxCount()
|
||||
{
|
||||
var count = 0;
|
||||
if (GetBPCurLevel == BPMaxLevel)
|
||||
count = GetBPCurExp() / 100 * curPassMain.ExpToCash;
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Level Up From StartLevel To EndLevel
|
||||
/// </summary>
|
||||
/// <param name="startLevel"></param>
|
||||
/// <param name="endLevel"></param>
|
||||
/// <returns></returns>
|
||||
public int GetTotalExpFromLevelRange(int startLevel, int endLevel)
|
||||
{
|
||||
var expNeed = 0;
|
||||
endLevel = endLevel > BPMaxLevel ? BPMaxLevel : endLevel;
|
||||
for (int i = startLevel; i < endLevel; i++)
|
||||
{
|
||||
expNeed += dic_BPLevel[curPassMain.LevelList[i - 1]].Exp;
|
||||
}
|
||||
return expNeed;
|
||||
}
|
||||
|
||||
public BPRewardSlot.ESlotState GetSlotState(int level, bool isNormalReward)
|
||||
{
|
||||
// if ( !IsUnlockVip&&!isNormalReward )
|
||||
// return BPRewardSlot.ESlotState.VipLock;
|
||||
if (Data.BattlePassRecord.Level < level || (!isNormalReward && !IsUnlockVip))
|
||||
{
|
||||
return BPRewardSlot.ESlotState.LevelLock;
|
||||
}
|
||||
if (isNormalReward)
|
||||
{
|
||||
if (Data.BattlePassRecord.NormalRewardGet.Contains(level))
|
||||
return BPRewardSlot.ESlotState.HasGotReward;
|
||||
}
|
||||
else if (Data.BattlePassRecord.VipRewardGet.Contains(level))
|
||||
return BPRewardSlot.ESlotState.HasGotReward;
|
||||
return BPRewardSlot.ESlotState.CanGetReward;
|
||||
}
|
||||
|
||||
public void GetAllBpRewards()
|
||||
{
|
||||
var playItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
var dropIds = new List<int>();
|
||||
var dropIds1 = new List<int>();
|
||||
var dropIds2 = new List<int>();
|
||||
List<ItemData> itemDatas = new List<ItemData>();
|
||||
List<ItemData> itemDatas1 = new List<ItemData>();
|
||||
List<ItemData> itemDatas2 = new List<ItemData>();
|
||||
for (int i = 1; i <= Data.BattlePassRecord.Level; i++)
|
||||
{
|
||||
if (GetSlotState(i, true) == BPRewardSlot.ESlotState.CanGetReward)
|
||||
{
|
||||
dropIds1.Add(GetSlotDropID(i, true));
|
||||
SetLevelRewardGet(i, true);
|
||||
}
|
||||
}
|
||||
if (dropIds1.Count > 0)
|
||||
{
|
||||
dropIds.AddRange(dropIds1);
|
||||
ChangeSource changeSource = new ChangeSource(Data.BattlePassRecord.ID, "BattlePass", "BattlePass_Normal28Day", "FreeLevelReward");
|
||||
itemDatas1 = playItemData.GetItemByDropListLureInflation(null, dropIds1);
|
||||
for (int i = 0; i < itemDatas1.Count; i++)
|
||||
{
|
||||
itemDatas1[i].changeSource = changeSource;
|
||||
}
|
||||
itemDatas.AddRange(itemDatas1);
|
||||
}
|
||||
if (IsUnlockVip)
|
||||
{
|
||||
for (int i = 1; i <= Data.BattlePassRecord.Level; i++)
|
||||
{
|
||||
if (GetSlotState(i, false) == BPRewardSlot.ESlotState.CanGetReward)
|
||||
{
|
||||
dropIds2.Add(GetSlotDropID(i, false));
|
||||
SetLevelRewardGet(i, false);
|
||||
}
|
||||
}
|
||||
if (dropIds2.Count > 0)
|
||||
{
|
||||
dropIds.AddRange(dropIds2);
|
||||
ChangeSource changeSource = new ChangeSource(Data.BattlePassRecord.ID, "BattlePass", "BattlePass_Normal28Day", "PrimeLevelReward");
|
||||
itemDatas2 = playItemData.GetItemByDropListLureInflation(null, dropIds2);
|
||||
for (int i = 0; i < itemDatas2.Count; i++)
|
||||
{
|
||||
itemDatas2[i].changeSource = changeSource;
|
||||
}
|
||||
itemDatas.AddRange(itemDatas2);
|
||||
}
|
||||
}
|
||||
Data.SaveBattlePassRecord();
|
||||
GContext.Publish(new ShowData(itemDatas));
|
||||
playItemData.AddItem(itemDatas, null);
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("bp_level_reward"))
|
||||
{
|
||||
e.AddContent("bp_level", GContext.container.Resolve<BattlePassDataProvider>().GetBPCurLevel)
|
||||
.AddContent("drop_id_list", JsonConvert.SerializeObject(dropIds));
|
||||
if (itemDatas != null && itemDatas.Count > 0)
|
||||
{
|
||||
int reward_hook = 0;
|
||||
int hook_origin = 0;
|
||||
int reward_cash = 0;
|
||||
for (int i = 0; i < itemDatas.Count; i++)
|
||||
{
|
||||
if (itemDatas[i].id == 1001)
|
||||
{
|
||||
reward_hook += itemDatas[i].count;
|
||||
hook_origin += itemDatas[i].origin;
|
||||
}
|
||||
else if (itemDatas[i].id == 1002)
|
||||
{
|
||||
reward_cash += itemDatas[i].count;
|
||||
}
|
||||
}
|
||||
e.AddContent("reward_hook", reward_hook)
|
||||
.AddContent("hook_origin", hook_origin)
|
||||
.AddContent("reward_cash", reward_cash);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public int GetAllToGetRewardCount()
|
||||
{
|
||||
var count = 0;
|
||||
for (int i = 1; i <= Data.BattlePassRecord.Level; i++)
|
||||
{
|
||||
if (GetSlotState(i, true) == BPRewardSlot.ESlotState.CanGetReward)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (IsUnlockVip)
|
||||
{
|
||||
for (int i = 1; i <= Data.BattlePassRecord.Level; i++)
|
||||
{
|
||||
if (GetSlotState(i, false) == BPRewardSlot.ESlotState.CanGetReward)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public int GetSlotDropID(int level, bool isNormalReward)
|
||||
{
|
||||
if (isNormalReward)
|
||||
{
|
||||
return curPassMain.FreeRewardList[level - 1];
|
||||
}
|
||||
return curPassMain.PrimeRewardList[level - 1];
|
||||
}
|
||||
public ItemData GetSlotDropItem(int level, bool isNormalReward)
|
||||
{
|
||||
if (isNormalReward)
|
||||
{
|
||||
return _itemData.GetItemDataOneLureInflation(null, curPassMain.FreeRewardList[level - 1]);
|
||||
}
|
||||
return _itemData.GetItemDataOneLureInflation(null, curPassMain.PrimeRewardList[level - 1]);
|
||||
}
|
||||
|
||||
public int GetDiamondCostByUpLevel(int upLevel)
|
||||
{
|
||||
var needExp = GetNeedExpToLevelUp(upLevel);
|
||||
if (needExp % 100 > 0)
|
||||
{
|
||||
needExp = (needExp / 100 + 1) * 100;
|
||||
}
|
||||
return needExp / 100 * curPassMain.ExpPrice;
|
||||
}
|
||||
|
||||
public List<ItemData> GetRewardsByUpLevel(int upLevel, bool isNormal)
|
||||
{
|
||||
return GetRewardsByLevelRange(GetBPCurLevel + 1, GetBPCurLevel + upLevel, isNormal);
|
||||
}
|
||||
|
||||
public List<ItemData> GetRewardsByLevelRange(int startLevel, int endLevel, bool isNormal)
|
||||
{
|
||||
|
||||
var curLevel = startLevel;
|
||||
var maxLevel = BPMaxLevel;
|
||||
var curPass = curPassMain;
|
||||
|
||||
Dictionary<int, int> dic_allRewards = new();
|
||||
|
||||
var dic_items = GContext.container.Resolve<Tables>().TbItem.DataMap;
|
||||
var playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
var rewardList = isNormal ? curPass.FreeRewardList : curPass.PrimeRewardList;
|
||||
|
||||
for (int i = curLevel - 1; i < endLevel && i < maxLevel; i++)
|
||||
{
|
||||
var curItemData = playerItemData.GetItemDataOneLureInflation(null, rewardList[i]);
|
||||
if (dic_allRewards.ContainsKey(curItemData.id))
|
||||
{
|
||||
dic_allRewards[curItemData.id] += curItemData.count;
|
||||
}
|
||||
else
|
||||
dic_allRewards.Add(curItemData.id, curItemData.count);
|
||||
}
|
||||
|
||||
var rewards = dic_allRewards.OrderByDescending(x => dic_items[x.Key].Quality).Select(x => new ItemData { id = x.Key, count = x.Value }).ToList();
|
||||
return rewards;
|
||||
}
|
||||
|
||||
public int GetBarsDefaultPos()
|
||||
{
|
||||
for (int i = 1; i <= GetBPCurLevel; i++)
|
||||
{
|
||||
if (GetSlotState(i, true) == BPRewardSlot.ESlotState.CanGetReward)
|
||||
return Mathf.Clamp(i - 1, 1, BPMaxLevel - 3);
|
||||
|
||||
if (GetSlotState(i, false) == BPRewardSlot.ESlotState.CanGetReward)
|
||||
return Mathf.Clamp(i - 1, 1, BPMaxLevel - 3);
|
||||
}
|
||||
return Mathf.Clamp(GetBPCurLevel - 1, 1, BPMaxLevel - 3);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Set Func
|
||||
public void RecordLastBPUnGetDropIDs()
|
||||
{
|
||||
if (curPassMain == null)
|
||||
return;
|
||||
var drops = Data.BattlePassRecord.LastDropIDs;
|
||||
if (Data.BattlePassRecord.ID == 0 || drops.Count > 0)
|
||||
return;
|
||||
drops.Add(GContext.container.Resolve<PlayerItemData>().GetExtraCoinMag(GetCashBoxCount()));
|
||||
for (int i = 1; i <= GetBPCurLevel; i++)
|
||||
{
|
||||
if (GetSlotState(i, true) == BPRewardSlot.ESlotState.CanGetReward)
|
||||
drops.Add(curPassMain.FreeRewardList[i - 1]);
|
||||
|
||||
if (GetSlotState(i, false) == BPRewardSlot.ESlotState.CanGetReward)
|
||||
drops.Add(curPassMain.PrimeRewardList[i - 1]);
|
||||
}
|
||||
|
||||
}
|
||||
public void SetRedPoint()
|
||||
{
|
||||
var flag = 0;
|
||||
for (int i = 1; i <= GetBPCurLevel; i++)
|
||||
{
|
||||
if (GetSlotState(i, false) == BPRewardSlot.ESlotState.CanGetReward || GetSlotState(i, true) == BPRewardSlot.ESlotState.CanGetReward)
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_BattlePass_Reward, true);
|
||||
flag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flag == 0)
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_BattlePass_Reward, false);
|
||||
|
||||
foreach (var taskInfo in Data.BattleTask.dic_dailyTasks)
|
||||
{
|
||||
var task = _tables.TbBattlePassTask.Get(taskInfo.Key);
|
||||
int requiredCount = int.Parse(task.Param[0]);
|
||||
if (task.Type == ConditionType.ConsumeEnergy)
|
||||
{
|
||||
requiredCount = GContext.container.Resolve<PlayerItemData>().IntInflation(requiredCount, Data.BattlePassRecord.InflationRate);
|
||||
}
|
||||
if (taskInfo.Value >= requiredCount)
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_BattlePass_Task, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
foreach (var taskInfo in Data.BattleTask.dic_weeklyTasks)
|
||||
{
|
||||
var task = _tables.TbBattlePassTask.Get(taskInfo.Key);
|
||||
int requiredCount = int.Parse(task.Param[0]);
|
||||
if (task.Type == ConditionType.ConsumeEnergy)
|
||||
{
|
||||
requiredCount = GContext.container.Resolve<PlayerItemData>().IntInflation(requiredCount, Data.BattlePassRecord.InflationRate);
|
||||
}
|
||||
if (taskInfo.Value >= requiredCount)
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_BattlePass_Task, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_BattlePass_Task, false);
|
||||
}
|
||||
|
||||
public void SetVipUnlock()
|
||||
{
|
||||
Data.BattlePassRecord.IsUnlockVip = 1;
|
||||
Data.SaveBattlePassRecord();
|
||||
}
|
||||
|
||||
public void SetLevelRewardGet(int level, bool isNormal)
|
||||
{
|
||||
if (isNormal)
|
||||
{
|
||||
if (Data.BattlePassRecord.NormalRewardGet.Contains(level))
|
||||
{
|
||||
Debug.LogError("Has Get Reward" + level);
|
||||
return;
|
||||
}
|
||||
Data.BattlePassRecord.NormalRewardGet.Add(level);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Data.BattlePassRecord.VipRewardGet.Contains(level))
|
||||
{
|
||||
Debug.LogError("Has Get Reward" + level);
|
||||
return;
|
||||
}
|
||||
Data.BattlePassRecord.VipRewardGet.Add(level);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetBPExp(int exp)
|
||||
{
|
||||
var curExp = Data.BattlePassRecord.Exp + exp;
|
||||
//Debug.Log("StartExp:" + Data.BattlePassRecord.Exp);
|
||||
var upLevel = 0;
|
||||
var curLevel = GetBPCurLevel;
|
||||
while (curExp - GetTotalExpFromLevelRange(curLevel, curLevel + upLevel + 1) >= 0 && curLevel + upLevel < BPMaxLevel)
|
||||
{
|
||||
upLevel++;
|
||||
}
|
||||
|
||||
Data.BattlePassRecord.Exp = curExp - GetTotalExpFromLevelRange(curLevel, curLevel + upLevel);
|
||||
Data.BattlePassRecord.Level = curLevel + upLevel;
|
||||
//Debug.Log("EndExp:" + Data.BattlePassRecord.Exp + "Get:" + exp + " Level" + Data.BattlePassRecord.Level);
|
||||
Data.SaveBattlePassRecord();
|
||||
SetRedPoint();
|
||||
}
|
||||
|
||||
public void UpdateVipToastLevel()
|
||||
{
|
||||
Data.BattlePassRecord.BuyVipToastIndex++;
|
||||
Data.SaveBattlePassRecord();
|
||||
}
|
||||
|
||||
void UpdateTaskData(ConditionTypeEvent conditionTypeEvent)
|
||||
{
|
||||
if (!IsActivating)
|
||||
return;
|
||||
bool isUpdata = false;
|
||||
var tasks = _tables.TbBattlePassTask;
|
||||
var curTasks = Data.BattleTask.dic_dailyTasks;
|
||||
var curTasksId = Data.BattleTask.dic_dailyTasks.Keys.ToList();
|
||||
//每日任务
|
||||
foreach (var taskId in curTasksId)
|
||||
{
|
||||
var task = tasks.GetOrDefault(taskId);
|
||||
if (task == null)
|
||||
continue;
|
||||
if (task.Type == conditionTypeEvent.type)
|
||||
{
|
||||
int requiredCount = int.Parse(task.Param[0]);
|
||||
if (task.Type == ConditionType.ConsumeEnergy)
|
||||
{
|
||||
requiredCount = GContext.container.Resolve<PlayerItemData>().IntInflation(requiredCount, Data.BattlePassRecord.InflationRate);
|
||||
}
|
||||
if (curTasks[taskId] == requiredCount || curTasks[taskId] == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (task.Type == ConditionType.LoginDays && Data.BattleTask.loginDays.Contains((int)ZZTimeHelper.UtcNow().UtcNowOffset().DayOfWeek))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
curTasks[taskId] += conditionTypeEvent.count;
|
||||
if (curTasks[taskId] >= requiredCount)
|
||||
{
|
||||
curTasks[taskId] = requiredCount;
|
||||
}
|
||||
|
||||
isUpdata = true;
|
||||
}
|
||||
}
|
||||
curTasks = Data.BattleTask.dic_weeklyTasks;
|
||||
curTasksId = Data.BattleTask.dic_weeklyTasks.Keys.ToList();
|
||||
//每周任务
|
||||
foreach (var taskId in curTasksId)
|
||||
{
|
||||
var task = tasks.GetOrDefault(taskId);
|
||||
if (task == null)
|
||||
continue;
|
||||
if (task.Type == conditionTypeEvent.type)
|
||||
{
|
||||
int requiredCount = int.Parse(task.Param[0]);
|
||||
if (task.Type == ConditionType.ConsumeEnergy)
|
||||
{
|
||||
requiredCount = GContext.container.Resolve<PlayerItemData>().IntInflation(requiredCount, Data.BattlePassRecord.InflationRate);
|
||||
}
|
||||
if (curTasks[taskId] == requiredCount || curTasks[taskId] == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (task.Type == ConditionType.LoginDays && Data.BattleTask.loginDays.Contains((int)ZZTimeHelper.UtcNow().UtcNowOffset().DayOfWeek))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
curTasks[taskId] += conditionTypeEvent.count;
|
||||
if (curTasks[taskId] >= requiredCount)
|
||||
{
|
||||
curTasks[taskId] = requiredCount;
|
||||
}
|
||||
|
||||
isUpdata = true;
|
||||
}
|
||||
}
|
||||
if (isUpdata)
|
||||
{
|
||||
if (conditionTypeEvent.type == ConditionType.LoginDays && Data.BattleTask.loginDays.Contains((int)ZZTimeHelper.UtcNow().UtcNowOffset().DayOfWeek))
|
||||
{
|
||||
Data.BattleTask.loginDays.Add((int)ZZTimeHelper.UtcNow().UtcNowOffset().DayOfWeek);
|
||||
}
|
||||
Data.SaveBattleTask();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void SetTaskDone(int taskID, bool isDailyTask)
|
||||
{
|
||||
var tasks = isDailyTask ? Data.BattleTask.dic_dailyTasks : Data.BattleTask.dic_weeklyTasks;
|
||||
tasks[taskID] = -1;
|
||||
Data.SaveBattleTask();
|
||||
}
|
||||
#endregion
|
||||
public void Dispose()
|
||||
{
|
||||
disposables?.Dispose();
|
||||
disposables = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class BattlePassRecord
|
||||
{
|
||||
public int ID;
|
||||
public int Level;
|
||||
public int Exp;
|
||||
//0:Lock,1:Unlock
|
||||
public int IsUnlockVip;
|
||||
public int BuyVipToastIndex;
|
||||
public List<int> NormalRewardGet;
|
||||
public List<int> VipRewardGet;
|
||||
public List<int> LastDropIDs;
|
||||
public float InflationRate;
|
||||
|
||||
public BattlePassRecord()
|
||||
{
|
||||
ID = 0;
|
||||
BuyVipToastIndex = 0;
|
||||
Level = 1;
|
||||
NormalRewardGet = new List<int>();
|
||||
VipRewardGet = new List<int>();
|
||||
LastDropIDs = new();
|
||||
}
|
||||
public void Init(int id)
|
||||
{
|
||||
ID = id;
|
||||
Level = 1;
|
||||
Exp = 0;
|
||||
BuyVipToastIndex = 0;
|
||||
IsUnlockVip = 0;
|
||||
NormalRewardGet.Clear();
|
||||
VipRewardGet.Clear();
|
||||
}
|
||||
public string SerializeObject()
|
||||
{
|
||||
return $"{ID}|{Level}|{Exp}|{BuyVipToastIndex}|{IsUnlockVip}|{string.Join(',', NormalRewardGet)}|{string.Join(',', VipRewardGet)}";
|
||||
}
|
||||
public void DeserializeObject(string data)
|
||||
{
|
||||
var arr = data.Split('|');
|
||||
ID = int.Parse(arr[0]);
|
||||
Level = int.Parse(arr[1]);
|
||||
Exp = int.Parse(arr[2]);
|
||||
BuyVipToastIndex = int.Parse(arr[3]);
|
||||
IsUnlockVip = int.Parse(arr[4]);
|
||||
NormalRewardGet = arr[5].Split(',').Where(x => !string.IsNullOrEmpty(x)).Select(x => int.Parse(x)).ToList();
|
||||
VipRewardGet = arr[6].Split(',').Where(x => !string.IsNullOrEmpty(x)).Select(x => int.Parse(x)).ToList();
|
||||
}
|
||||
}
|
||||
public class BattleTask
|
||||
{
|
||||
public Dictionary<int, int> dic_dailyTasks;
|
||||
public Dictionary<int, int> dic_weeklyTasks;
|
||||
public List<int> loginDays;
|
||||
public DateTime DailyTaskEndTime;
|
||||
public DateTime WeeklyTaskEndTime;
|
||||
public BattleTask()
|
||||
{
|
||||
dic_dailyTasks = new Dictionary<int, int>();
|
||||
dic_weeklyTasks = new Dictionary<int, int>();
|
||||
loginDays = new List<int>();
|
||||
}
|
||||
public string SerializeObject()
|
||||
{
|
||||
string dailyTasksStr = string.Join(',', dic_dailyTasks.Select(x => $"{x.Key}:{x.Value}"));
|
||||
string weeklyTasksStr = string.Join(',', dic_weeklyTasks.Select(x => $"{x.Key}:{x.Value}"));
|
||||
string loginDaysStr = string.Join(',', loginDays);
|
||||
return $"{dailyTasksStr}|{weeklyTasksStr}|{loginDaysStr}|{DailyTaskEndTime.Ticks}|{WeeklyTaskEndTime.Ticks}";
|
||||
}
|
||||
public void DeserializeObject(string data)
|
||||
{
|
||||
var arr = data.Split('|');
|
||||
dic_dailyTasks = arr[0].Split(',').Where(x => !string.IsNullOrEmpty(x)).Select(x =>
|
||||
{
|
||||
var kv = x.Split(':');
|
||||
return new KeyValuePair<int, int>(int.Parse(kv[0]), int.Parse(kv[1]));
|
||||
}).ToDictionary(x => x.Key, x => x.Value);
|
||||
dic_weeklyTasks = arr[1].Split(',').Where(x => !string.IsNullOrEmpty(x)).Select(x =>
|
||||
{
|
||||
var kv = x.Split(':');
|
||||
return new KeyValuePair<int, int>(int.Parse(kv[0]), int.Parse(kv[1]));
|
||||
}).ToDictionary(x => x.Key, x => x.Value);
|
||||
loginDays = arr[2].Split(',').Where(x => !string.IsNullOrEmpty(x)).Select(x => int.Parse(x)).ToList();
|
||||
DailyTaskEndTime = new DateTime(long.Parse(arr[3]));
|
||||
WeeklyTaskEndTime = new DateTime(long.Parse(arr[4]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,596 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class BuffDataCenter
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore SaveStore { get; set; }
|
||||
|
||||
private cfg.Tables _tables;
|
||||
public BuffDataCenter(cfg.Tables tables)
|
||||
{
|
||||
this._tables = tables;
|
||||
}
|
||||
Dictionary<int, FishBuffTimeData> mapBuffDataList = new Dictionary<int, FishBuffTimeData>();
|
||||
List<FishBuffTimeData> targetBuffDataList = new List<FishBuffTimeData>();
|
||||
Dictionary<int, FishBuffTimeData> globalBuffDataList = new Dictionary<int, FishBuffTimeData>();
|
||||
FishBuffTimeData fishDailyBuff = null;
|
||||
public List<FishBuffTimeData> showFishBuffIds = new List<FishBuffTimeData>();
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVBuffState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodeBuffs(raw,snapshot,errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
mapBuffDataList=new Dictionary<int,FishBuffTimeData>();
|
||||
targetBuffDataList=new List<FishBuffTimeData>();
|
||||
globalBuffDataList=new Dictionary<int,FishBuffTimeData>();
|
||||
showFishBuffIds=new List<FishBuffTimeData>(); fishDailyBuff=null;
|
||||
}
|
||||
static FishBuffTimeData MaterializeKVBuff(game.PlayerDataStorage.PlayerKVBuffValue value) => value==null ? null : new FishBuffTimeData
|
||||
{ SortID=value.SortID,sourceID=value.sourceID,buffID=value.buffID,isEnd=value.isEnd,buffEndTime=value.buffEndTime,isNew=false,addDown=0 };
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVBuffState state)
|
||||
{
|
||||
if(state==null) throw new ArgumentNullException(nameof(state));
|
||||
var map=new Dictionary<int,FishBuffTimeData>(); var global=new Dictionary<int,FishBuffTimeData>(); var target=new List<FishBuffTimeData>();
|
||||
foreach(var item in state.Map) map.Add(item.Key,MaterializeKVBuff(item.Value));
|
||||
foreach(var item in state.Global) global.Add(item.Key,MaterializeKVBuff(item.Value));
|
||||
foreach(var item in state.Target) target.Add(MaterializeKVBuff(item));
|
||||
mapBuffDataList=map; globalBuffDataList=global; targetBuffDataList=target; fishDailyBuff=MaterializeKVBuff(state.Daily);
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
internal FishBuffTimeData BuffForKVValidation(int kind,int key)
|
||||
{
|
||||
if(kind==0) return mapBuffDataList.TryGetValue(key,out var value) ? value : null;
|
||||
if(kind==1) return targetBuffDataList.Count>key ? targetBuffDataList[key] : null;
|
||||
if(kind==2) return fishDailyBuff;
|
||||
return globalBuffDataList.TryGetValue(key,out var global) ? global : null;
|
||||
}
|
||||
internal bool IsEmptyForKVValidation => mapBuffDataList.Count==0 && targetBuffDataList.Count==0 &&
|
||||
globalBuffDataList.Count==0 && showFishBuffIds.Count==0 && fishDailyBuff==null;
|
||||
#endif
|
||||
public void InitData(Dictionary<string, string> userDatas)
|
||||
{
|
||||
if (userDatas.TryGetValue("BuffDataList", out string buffDataStr))
|
||||
{
|
||||
InitBuffData(buffDataStr);
|
||||
}
|
||||
if (userDatas.TryGetValue("TargetBuffDataList", out string targetBuffDataStr))
|
||||
{
|
||||
InitTargetBuffData(targetBuffDataStr);
|
||||
}
|
||||
if (userDatas.TryGetValue("FishDailyBuff", out string dailyBuffDataStr))
|
||||
{
|
||||
InitDailyBuffData(dailyBuffDataStr);
|
||||
}
|
||||
if (userDatas.TryGetValue("GlobalBuffDataList", out string globalBuffDataStr))
|
||||
{
|
||||
InitGlobalBuffData(globalBuffDataStr);
|
||||
}
|
||||
|
||||
}
|
||||
void InitBuffData(string dataStr)
|
||||
{
|
||||
mapBuffDataList = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<int, FishBuffTimeData>>(dataStr);
|
||||
}
|
||||
void InitTargetBuffData(string dataStr)
|
||||
{
|
||||
targetBuffDataList = Newtonsoft.Json.JsonConvert.DeserializeObject<List<FishBuffTimeData>>(dataStr);
|
||||
}
|
||||
void InitDailyBuffData(string dataStr)
|
||||
{
|
||||
fishDailyBuff = Newtonsoft.Json.JsonConvert.DeserializeObject<FishBuffTimeData>(dataStr);
|
||||
}
|
||||
void InitGlobalBuffData(string dataStr)
|
||||
{
|
||||
globalBuffDataList = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<int, FishBuffTimeData>>(dataStr);
|
||||
}
|
||||
#region 地图Buff或目标奖励Buff
|
||||
public FishBuff IsMapBuff(int mapId)
|
||||
{
|
||||
if (mapBuffDataList.TryGetValue(mapId, out FishBuffTimeData mapBuffData) && !mapBuffData.isEnd && mapBuffData.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
FishBuff buff = _tables.TbFishBuff.GetOrDefault(mapBuffData.buffID);
|
||||
return buff;
|
||||
}
|
||||
foreach (var item in targetBuffDataList)
|
||||
{
|
||||
if (!item.isEnd && item.sourceID == mapId && item.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
|
||||
FishBuff buff = _tables.TbFishBuff.GetOrDefault(item.buffID);
|
||||
return buff;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public bool IsDarkness(int mapId)
|
||||
{
|
||||
if (mapBuffDataList.TryGetValue(mapId, out FishBuffTimeData mapBuffData) && !mapBuffData.isEnd && mapBuffData.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(mapBuffData.buffID);
|
||||
if (buff.IsDarkness)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
foreach (var item in targetBuffDataList)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(item.buffID);
|
||||
if (!item.isEnd && (item.sourceID == mapId || item.sourceID == 0) && item.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
if (buff.IsDarkness)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public string GetSceneFx(int mapId)
|
||||
{
|
||||
if (mapBuffDataList.TryGetValue(mapId, out FishBuffTimeData mapBuffData) && !mapBuffData.isEnd && mapBuffData.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(mapBuffData.buffID);
|
||||
if (!string.IsNullOrEmpty(buff.SceneFx))
|
||||
{
|
||||
return buff.SceneFx;
|
||||
}
|
||||
}
|
||||
foreach (var item in targetBuffDataList)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(item.buffID);
|
||||
if (!item.isEnd && (item.sourceID == mapId || item.sourceID == 0) && item.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(buff.SceneFx))
|
||||
{
|
||||
return buff.SceneFx;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
//获取某种Buff
|
||||
public FishBuffTimeData GetBuffData<T>() where T : FishBuffType
|
||||
{
|
||||
int curMapId = GContext.container.Resolve<PlayerData>().currentMapId;
|
||||
foreach (var item in targetBuffDataList)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(item.buffID);
|
||||
if (buff.BuffParam is T && !item.isEnd && (item.sourceID == curMapId || item.sourceID == 0) && item.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
//排行榜活动特殊地图额外积分
|
||||
public float GetSpMapExtraPointBuffValue()
|
||||
{
|
||||
float extraPointValue = 0;
|
||||
foreach (var item in targetBuffDataList)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(item.buffID);
|
||||
if (buff.BuffParam is SpMapExtraPoint && !item.isEnd && item.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
SpMapExtraPoint extraPoint = buff.BuffParam as SpMapExtraPoint;
|
||||
extraPointValue += extraPoint.Param;
|
||||
}
|
||||
}
|
||||
return extraPointValue;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 地图Buff
|
||||
public bool CheckMapBuff(int mapID)
|
||||
{
|
||||
if (mapID != GContext.container.Resolve<PlayerData>().lastMapId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!mapBuffDataList.ContainsKey(mapID))
|
||||
{
|
||||
var mapdata = _tables.TbMapData.GetOrDefault(mapID);
|
||||
if (mapdata != null && mapdata.FishBuff > 0)
|
||||
{
|
||||
var buffData = _tables.TbFishBuff.GetOrDefault(mapdata.FishBuff);
|
||||
var mapBuffData = new FishBuffTimeData();
|
||||
mapBuffData.SortID = buffData.SortID;
|
||||
mapBuffData.sourceID = mapID;
|
||||
mapBuffData.buffID = mapdata.FishBuff;
|
||||
DateTime now = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
mapBuffData.buffEndTime = now.AddSeconds(buffData.CountDown);
|
||||
mapBuffDataList.Add(mapID, mapBuffData);
|
||||
SaveStore.Write("BuffDataList", Newtonsoft.Json.JsonConvert.SerializeObject(mapBuffDataList)).RequireAccepted();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//当前地图的Buff
|
||||
public FishBuffTimeData GetMapBuffTimeData(int mapId)
|
||||
{
|
||||
if (mapBuffDataList.TryGetValue(mapId, out FishBuffTimeData mapBuffData) && !mapBuffData.isEnd && mapBuffData.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
return mapBuffData;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
//钓鱼金币加成
|
||||
public float GetExtraGoldBuffValue(int mapId, int fishQuality)
|
||||
{
|
||||
float extraGoldValue = 0;
|
||||
|
||||
if (mapBuffDataList.TryGetValue(mapId, out FishBuffTimeData mapBuffData) && !mapBuffData.isEnd && mapBuffData.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(mapBuffData.buffID);
|
||||
if (buff.BuffParam is ExtraGold)
|
||||
{
|
||||
ExtraGold extraGold = buff.BuffParam as ExtraGold;
|
||||
extraGoldValue += extraGold.Param;
|
||||
}
|
||||
}
|
||||
MoreFishingCash moreFishingCash = GetWeelyBuffTimeData<MoreFishingCash>();
|
||||
if (moreFishingCash != null)
|
||||
{
|
||||
for (int i = 0; i < moreFishingCash.Quality.Count; i++)
|
||||
{
|
||||
if (fishQuality == moreFishingCash.Quality[i])
|
||||
{
|
||||
extraGoldValue += moreFishingCash.Param[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return extraGoldValue;
|
||||
}
|
||||
#endregion
|
||||
#region 目标奖励活动Buff
|
||||
public void CheckTargetBuff()
|
||||
{
|
||||
FishingEventData fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
var collectingTargetInit = fishingEventData.collectingTargetInit;
|
||||
if (collectingTargetInit != null)
|
||||
{
|
||||
EventTargetExtraDrop eventTargetExtraDrop = collectingTargetInit.FishingExtraDrop;
|
||||
ETKeepsake eTKeepsake = null;
|
||||
if (eventTargetExtraDrop is ETKeepsake)
|
||||
{
|
||||
eTKeepsake = eventTargetExtraDrop as ETKeepsake;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int buffID = eTKeepsake.FishbuffID;
|
||||
FishBuff fishBuff = _tables.TbFishBuff.GetOrDefault(buffID);
|
||||
if (fishBuff != null)
|
||||
{
|
||||
FishBuffTimeData fishBuffTimeData = new FishBuffTimeData();
|
||||
if (fishBuff.IsMapBuff)
|
||||
{
|
||||
fishBuffTimeData.sourceID = GContext.container.Resolve<PlayerData>().currentMapId;
|
||||
}
|
||||
else
|
||||
{
|
||||
fishBuffTimeData.sourceID = 0;
|
||||
}
|
||||
fishBuffTimeData.SortID = fishBuff.SortID;
|
||||
fishBuffTimeData.buffID = fishBuff.ID;
|
||||
if (fishBuff.CountDown > 0)
|
||||
{
|
||||
fishBuffTimeData.buffEndTime = ZZTimeHelper.UtcNow().UtcNowOffset().AddSeconds(fishBuff.CountDown);
|
||||
}
|
||||
else
|
||||
{
|
||||
fishBuffTimeData.buffEndTime = fishingEventData.GetCollectingTargetEndTime();
|
||||
}
|
||||
for (int i = 0; i < targetBuffDataList.Count; i++)
|
||||
{
|
||||
if (targetBuffDataList[i].buffID == fishBuffTimeData.buffID && targetBuffDataList[i].sourceID == fishBuffTimeData.sourceID)
|
||||
{
|
||||
targetBuffDataList[i] = fishBuffTimeData;
|
||||
SaveStore.Write("TargetBuffDataList", Newtonsoft.Json.JsonConvert.SerializeObject(targetBuffDataList)).RequireAccepted();
|
||||
return;
|
||||
}
|
||||
}
|
||||
targetBuffDataList.Add(fishBuffTimeData);
|
||||
SaveStore.Write("TargetBuffDataList", Newtonsoft.Json.JsonConvert.SerializeObject(targetBuffDataList)).RequireAccepted();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearTargetBuff();
|
||||
}
|
||||
}
|
||||
public void ClearTargetBuff()
|
||||
{
|
||||
targetBuffDataList.Clear();
|
||||
SaveStore.Write("TargetBuffDataList", Newtonsoft.Json.JsonConvert.SerializeObject(targetBuffDataList)).RequireAccepted();
|
||||
}
|
||||
/// <summary>
|
||||
/// CollectionTargetWeight的特殊处理
|
||||
/// </summary>
|
||||
/// <param name="fishItemId"></param>
|
||||
public void EndCollectionTargetWeight(int fishItemId)
|
||||
{
|
||||
CollectingTargetInit collectingTargetInit = GContext.container.Resolve<FishingEventData>().collectingTargetInit;
|
||||
if (collectingTargetInit == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
EventTargetExtraDrop eventTargetExtraDrop = collectingTargetInit.FishingExtraDrop;
|
||||
ETKeepsake eTKeepsake = null;
|
||||
if (eventTargetExtraDrop is ETKeepsake)
|
||||
{
|
||||
eTKeepsake = eventTargetExtraDrop as ETKeepsake;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
FishBuff fishBuff;
|
||||
int curMapId = GContext.container.Resolve<PlayerData>().currentMapId;
|
||||
foreach (var item in targetBuffDataList)
|
||||
{
|
||||
fishBuff = _tables.TbFishBuff.GetOrDefault(item.buffID);
|
||||
if (!item.isEnd && (item.sourceID == curMapId || item.sourceID == 0) && fishBuff.BuffParam is CollectionTargetWeight)
|
||||
{
|
||||
CollectionTargetWeight collectionTargetWeight = fishBuff.BuffParam as CollectionTargetWeight;
|
||||
if (collectionTargetWeight.TargetFishIndex < 1 || collectionTargetWeight.TargetFishIndex > eTKeepsake.FishIDList.Count)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (fishItemId == eTKeepsake.FishIDList[collectionTargetWeight.TargetFishIndex - 1])
|
||||
{
|
||||
item.isEnd = true;
|
||||
SaveStore.Write("TargetBuffDataList", Newtonsoft.Json.JsonConvert.SerializeObject(targetBuffDataList)).RequireAccepted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//目标奖励活动额外积分
|
||||
public float GetCollectionTargetExtraPointBuffValue(int mapId)
|
||||
{
|
||||
float extraPointValue = 0;
|
||||
foreach (var item in targetBuffDataList)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(item.buffID);
|
||||
if (buff.BuffParam is CollectionTargetExtraPoint && !item.isEnd && (item.sourceID == mapId || item.sourceID == 0) && item.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
CollectionTargetExtraPoint extraPoint = buff.BuffParam as CollectionTargetExtraPoint;
|
||||
extraPointValue += extraPoint.Param;
|
||||
}
|
||||
}
|
||||
return extraPointValue;
|
||||
}
|
||||
#endregion
|
||||
#region 全局Buff Item投放
|
||||
|
||||
public void SetGlobalBucffNew(int buffID, bool isNew = true)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(buffID);
|
||||
if (buff != null)
|
||||
{
|
||||
if (globalBuffDataList.TryGetValue(buff.GroupId, out FishBuffTimeData fishBuffTimeData))
|
||||
{
|
||||
if (fishBuffTimeData.isNew != isNew)
|
||||
{
|
||||
fishBuffTimeData.isNew = isNew;
|
||||
|
||||
if (isNew)
|
||||
{
|
||||
showFishBuffIds.Add(fishBuffTimeData);
|
||||
}
|
||||
else
|
||||
{
|
||||
showFishBuffIds.Remove(fishBuffTimeData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddGlobalBuff(int buffID)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(buffID);
|
||||
if (buff != null)
|
||||
{
|
||||
var UtcNow = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
bool isNewBuff = true;
|
||||
if (globalBuffDataList.TryGetValue(buff.GroupId, out FishBuffTimeData fishBuffTimeData))
|
||||
{
|
||||
var oldBuff = _tables.TbFishBuff.GetOrDefault(fishBuffTimeData.buffID);
|
||||
if (oldBuff.GroupId != buff.GroupId)
|
||||
{
|
||||
globalBuffDataList.Remove(buff.GroupId);
|
||||
}
|
||||
else
|
||||
{
|
||||
isNewBuff = false;
|
||||
if (fishBuffTimeData.buffEndTime.Ticks > UtcNow.Ticks)
|
||||
{
|
||||
fishBuffTimeData.buffEndTime = fishBuffTimeData.buffEndTime.AddSeconds(buff.CountDown);
|
||||
if (buff.SortID > fishBuffTimeData.SortID)
|
||||
{
|
||||
fishBuffTimeData.SortID = buff.SortID;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fishBuffTimeData.buffEndTime = UtcNow.AddSeconds(buff.CountDown);
|
||||
fishBuffTimeData.SortID = buff.SortID;
|
||||
fishBuffTimeData.isNew = true;
|
||||
}
|
||||
fishBuffTimeData.isEnd = false;
|
||||
showFishBuffIds.Add(fishBuffTimeData);
|
||||
fishBuffTimeData.addDown = buff.CountDown;
|
||||
}
|
||||
}
|
||||
if (isNewBuff)
|
||||
{
|
||||
fishBuffTimeData = new FishBuffTimeData();
|
||||
if (buff.IsMapBuff)
|
||||
{
|
||||
fishBuffTimeData.sourceID = GContext.container.Resolve<PlayerData>().currentMapId;
|
||||
}
|
||||
else
|
||||
{
|
||||
fishBuffTimeData.sourceID = 0;
|
||||
}
|
||||
fishBuffTimeData.buffID = buffID;
|
||||
if (buff.CountDown > 0)
|
||||
{
|
||||
fishBuffTimeData.buffEndTime = ZZTimeHelper.UtcNow().UtcNowOffset().AddSeconds(buff.CountDown);
|
||||
}
|
||||
else
|
||||
{
|
||||
fishBuffTimeData.buffEndTime = ZZTimeHelper.UtcNow().UtcNowOffset().AddDays(1);
|
||||
}
|
||||
globalBuffDataList[buff.GroupId] = fishBuffTimeData;
|
||||
fishBuffTimeData.SortID = buff.SortID;
|
||||
fishBuffTimeData.isNew = true;
|
||||
fishBuffTimeData.addDown = buff.CountDown;
|
||||
showFishBuffIds.Add(fishBuffTimeData);
|
||||
}
|
||||
SaveStore.Write("GlobalBuffDataList", Newtonsoft.Json.JsonConvert.SerializeObject(globalBuffDataList)).RequireAccepted();
|
||||
GContext.Publish(new AddGlobalBuffEvent(buffID));
|
||||
}
|
||||
}
|
||||
|
||||
//GetBuffGroupTimeData
|
||||
public T GetWeelyBuffTimeData<T>(bool isEnd = false) where T : FishBuffType
|
||||
{
|
||||
GetWeelyBuff();
|
||||
foreach (var item in globalBuffDataList)
|
||||
{
|
||||
if (!item.Value.isEnd && item.Value.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
FishBuff buff = _tables.TbFishBuff.GetOrDefault(item.Value.buffID);
|
||||
if (buff != null && buff.BuffParam is T)
|
||||
{
|
||||
//if (isEnd)
|
||||
//{
|
||||
// item.Value.isEnd = true;
|
||||
//}
|
||||
return buff.BuffParam as T;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public FishBuffTimeData GetBuffTimeDataByType<T>() where T : FishBuffType
|
||||
{
|
||||
GetWeelyBuff();
|
||||
foreach (var item in globalBuffDataList)
|
||||
{
|
||||
if (!item.Value.isEnd && item.Value.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
FishBuff buff = _tables.TbFishBuff.GetOrDefault(item.Value.buffID);
|
||||
if (buff.BuffParam.GetType() == typeof(T))
|
||||
{
|
||||
return item.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public List<FishBuffTimeData> GetGroupBuff()
|
||||
{
|
||||
GetWeelyBuff();
|
||||
List<FishBuffTimeData> buffList = new List<FishBuffTimeData>();
|
||||
foreach (var item in globalBuffDataList)
|
||||
{
|
||||
if (!item.Value.isEnd && item.Value.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
buffList.Add(item.Value);
|
||||
}
|
||||
}
|
||||
return buffList;
|
||||
}
|
||||
#endregion
|
||||
#region 每日周期Buff
|
||||
|
||||
FishBuffTimeData GetWeelyBuff()
|
||||
{
|
||||
DateTime now = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
int week = (int)now.DayOfWeek;
|
||||
var weelyBuff = _tables.TbWeelyBuffInit.GetOrDefault(week);
|
||||
if (weelyBuff != null)
|
||||
{
|
||||
List<int> dailyBuffList = weelyBuff.DailyBuffList;
|
||||
int day = now.DayOfYear;
|
||||
if (fishDailyBuff != null && fishDailyBuff.buffEndTime > now/* && fishDailyBuff.buffEndTime.DayOfYear == day*/)
|
||||
{
|
||||
//旧的每日Buff还未结束
|
||||
return fishDailyBuff;
|
||||
}
|
||||
for (int i = 0; i < dailyBuffList.Count; i++)
|
||||
{
|
||||
var buff = _tables.TbDailyBuffTrigger.GetOrDefault(dailyBuffList[i]);
|
||||
if (buff != null)
|
||||
{
|
||||
DateTime startTime = now.Date.AddSeconds(buff.StartTime);
|
||||
DateTime endTime = now.Date.AddSeconds(buff.EndTime);
|
||||
if (now >= startTime && now <= endTime)
|
||||
{
|
||||
//开启新的每日Buff
|
||||
if (buff.LevelRequired > GContext.container.Resolve<PlayerData>().lv)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (fishDailyBuff == null)
|
||||
{
|
||||
fishDailyBuff = new FishBuffTimeData();
|
||||
}
|
||||
if (fishDailyBuff.sourceID == buff.Day/* && fishDailyBuff.buffEndTime.DayOfYear == day*/)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
FishBuff fishBuff = _tables.TbFishBuff.GetOrDefault(buff.BuffID);
|
||||
fishDailyBuff.buffID = buff.BuffID;
|
||||
fishDailyBuff.sourceID = buff.Day;
|
||||
fishDailyBuff.buffEndTime = endTime;
|
||||
if (fishBuff.CountDown > 0)
|
||||
{
|
||||
fishDailyBuff.buffEndTime = now.AddSeconds(fishBuff.CountDown);
|
||||
}
|
||||
AddGlobalBuff(buff.BuffID);
|
||||
fishDailyBuff.isEnd = false;
|
||||
SaveStore.Write("FishDailyBuff", Newtonsoft.Json.JsonConvert.SerializeObject(fishDailyBuff)).RequireAccepted();
|
||||
return fishDailyBuff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
public class FishBuffTimeData
|
||||
{
|
||||
public int SortID;
|
||||
public int sourceID;
|
||||
public int buffID;
|
||||
public bool isEnd = false;
|
||||
[NonSerialized]
|
||||
public bool isNew = false;
|
||||
[NonSerialized]
|
||||
public int addDown;
|
||||
public DateTime buffEndTime;
|
||||
}
|
||||
public class AddGlobalBuffEvent
|
||||
{
|
||||
|
||||
public int buffID;
|
||||
public AddGlobalBuffEvent(int buffID)
|
||||
{
|
||||
this.buffID = buffID;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,740 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using LitJson;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using tysdk;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
|
||||
namespace GameCore
|
||||
{
|
||||
public class GuideNextEvent
|
||||
{
|
||||
|
||||
}
|
||||
public class AddIndexEvent
|
||||
{
|
||||
public bool CompleteGuide;
|
||||
public int guideSubIndex;
|
||||
public bool isAutoNext;
|
||||
}
|
||||
/// <summary>
|
||||
/// 废弃
|
||||
/// </summary>
|
||||
public struct OnEventTriggerGuide
|
||||
{
|
||||
public OnEventTriggerGuide(string panelName)
|
||||
{
|
||||
this.panelName = panelName;
|
||||
}
|
||||
public string panelName;
|
||||
}
|
||||
public class GuideSaveData
|
||||
{
|
||||
public int guideSubIndex = 0;
|
||||
public bool isFinish = false;
|
||||
public string groupName;
|
||||
}
|
||||
public class GuideDataCenter : IDisposable
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore playerSaveStore { get; set; }
|
||||
Tables _tables;
|
||||
PlayerData playerData;
|
||||
public GuideDataCenter(cfg.Tables tables, PlayerData playerData)
|
||||
{
|
||||
this._tables = tables;
|
||||
this.playerData = playerData;
|
||||
}
|
||||
public string CurPanelName;
|
||||
public Dictionary<string, GuideSaveData> GuideSaveDataDic = new Dictionary<string, GuideSaveData>();
|
||||
public GuideSaveData curSaveData;
|
||||
public GuidanceGroupDefine[] guidanceGroupDefines;
|
||||
public GuidanceDefine curDefine;
|
||||
public string popupPanelNode;
|
||||
public GuidanceGroupDefine curGroupDefine;
|
||||
public GuidanceDefineSetting guidanceDefineSetting;
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVGuideState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) => game.PlayerDataStorage.PlayerKVSaveSchema.DecodeGuides(raw,errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
GuideSaveDataDic = new Dictionary<string,GuideSaveData>(StringComparer.Ordinal);
|
||||
curSaveData = null; curDefine = null; curGroupDefine = null; popupPanelNode = null; CurPanelName = null;
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVGuideState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
GuideSaveDataDic = state.Values.ToDictionary(p=>p.Key,
|
||||
p=>new GuideSaveData { guideSubIndex=p.Value.Index, isFinish=p.Value.Finished, groupName=p.Value.Group },StringComparer.Ordinal);
|
||||
}
|
||||
//待释放
|
||||
IDisposable disposable;
|
||||
IDisposable disposable2;
|
||||
IDisposable disposable3;
|
||||
|
||||
public void GMClearGuide()
|
||||
{
|
||||
foreach (var item in guidanceGroupDefines)
|
||||
{
|
||||
if (!GuideSaveDataDic.TryGetValue(item.groupName.ToString(), out GuideSaveData _saveData))
|
||||
{
|
||||
_saveData = new GuideSaveData();
|
||||
_saveData.groupName = item.groupName.ToString();
|
||||
GuideSaveDataDic.Add(item.groupName.ToString(), _saveData);
|
||||
}
|
||||
_saveData.isFinish = true;
|
||||
}
|
||||
GContext.Publish(new ConditionTypeEvent(ConditionType.CompleteGuide, 0));
|
||||
curDefine = null;
|
||||
curGroupDefine = null;
|
||||
Save();
|
||||
}
|
||||
public async void Init()
|
||||
{
|
||||
var store = playerSaveStore;
|
||||
var setting = (GuidanceDefineSetting)await Addressables.LoadAssetAsync<object>("GuidanceConfig");
|
||||
if (disposed || !store.IsCurrent) return;
|
||||
guidanceDefineSetting = setting;
|
||||
if (guidanceDefineSetting != null)
|
||||
{
|
||||
bool isUpdate = false;
|
||||
guidanceGroupDefines = guidanceDefineSetting.guidanceGroupDefines;
|
||||
List<GroupName> Skip = guidanceDefineSetting.SkipGroupNames;
|
||||
bool isSkip = playerData.lv >= guidanceDefineSetting.SkipLevel;
|
||||
if (GuideSaveDataDic == null)
|
||||
{
|
||||
GuideSaveDataDic = new Dictionary<string, GuideSaveData>();
|
||||
}
|
||||
else
|
||||
{
|
||||
GuideSaveData _saveData;
|
||||
GuidanceDefine _define;
|
||||
Dictionary<string, GuideSaveData> keyValuePairs = new Dictionary<string, GuideSaveData>();
|
||||
foreach (var item in guidanceGroupDefines)
|
||||
{
|
||||
|
||||
if (GuideSaveDataDic.TryGetValue(item.groupName.ToString(), out _saveData))
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if (DeferUnavailablePotionGuide(item, _saveData))
|
||||
{
|
||||
keyValuePairs[item.groupName.ToString()] = _saveData;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (!_saveData.isFinish && _saveData.guideSubIndex > 0)
|
||||
{
|
||||
for (int i = _saveData.guideSubIndex; i < item.guidanceDefines.Length; i++)
|
||||
{
|
||||
_define = item.guidanceDefines[i];
|
||||
if (_define.isKey)
|
||||
{
|
||||
_saveData.guideSubIndex = _define.JumpIndex;
|
||||
if (_saveData.guideSubIndex < item.guidanceDefines.Length)
|
||||
{
|
||||
_saveData.isFinish = false;
|
||||
curSaveData = _saveData;
|
||||
curGroupDefine = item;
|
||||
curDefine = curGroupDefine.guidanceDefines[_saveData.guideSubIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
_saveData.isFinish = true;
|
||||
GContext.Publish(new ConditionTypeEvent(ConditionType.CompleteGuide, 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
_saveData.isFinish = true;
|
||||
}
|
||||
}
|
||||
isUpdate = true;
|
||||
}
|
||||
keyValuePairs[item.groupName.ToString()] = _saveData;
|
||||
}
|
||||
else if (isSkip && Skip.Contains(item.groupName))
|
||||
{
|
||||
_saveData = new GuideSaveData();
|
||||
_saveData.groupName = item.groupName.ToString();
|
||||
_saveData.isFinish = true;
|
||||
keyValuePairs[item.groupName.ToString()] = _saveData;
|
||||
isUpdate = true;
|
||||
}
|
||||
}
|
||||
GuideSaveDataDic = keyValuePairs;
|
||||
if (isUpdate)
|
||||
{
|
||||
Save();
|
||||
}
|
||||
}
|
||||
if (disposable2 == null)
|
||||
{
|
||||
disposable2 = GContext.OnEvent<OnEventTriggerGuide>().Subscribe(TriggerGuideNext);
|
||||
}
|
||||
}
|
||||
}
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
private static bool DeferUnavailablePotionGuide(GuidanceGroupDefine group, GuideSaveData saved)
|
||||
{
|
||||
if (!game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled || !game.PlayerDataStorage.PlayerKVGameplayLoop.Ready ||
|
||||
group.groupName != GroupName.EventPotion01 || saved == null || saved.isFinish || saved.guideSubIndex <= 0 ||
|
||||
group.guidanceDefines == null || saved.guideSubIndex >= group.guidanceDefines.Length ||
|
||||
group.guidanceDefines[saved.guideSubIndex] == null ||
|
||||
group.guidanceDefines[saved.guideSubIndex].panelName != "EventPotionPanel")
|
||||
return false;
|
||||
try
|
||||
{
|
||||
// Existing potion activity classification; availability query does not initialize activity data.
|
||||
const int potionEventType = 4;
|
||||
const int potionEventSubType = 5;
|
||||
return GContext.container.Resolve<FishingEventData>().GetEvent(potionEventType, potionEventSubType) == -1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning("Potion guide availability query failed; retaining original guide recovery: " + ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
public bool GetIsFinish(string groupName)
|
||||
{
|
||||
return GuideSaveDataDic.ContainsKey(groupName) && GuideSaveDataDic[groupName].isFinish;
|
||||
}
|
||||
public bool InspectTriggerGuide(string panelName, string prePanelName = "", string curPanelName = "")
|
||||
{
|
||||
if (prePanelName.Contains("FishingRodPanel"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (guidanceDefineSetting == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (curGroupDefine != null
|
||||
&& GuideSaveDataDic.TryGetValue(curGroupDefine.groupName.ToString(), out var guideSaveData)
|
||||
//&& !guideSaveData.isFinish
|
||||
&& curGroupDefine.guidanceDefines.Length > guideSaveData.guideSubIndex
|
||||
&& panelName == curGroupDefine.guidanceDefines[guideSaveData.guideSubIndex].panelName)
|
||||
{
|
||||
CurPanelName = curPanelName;
|
||||
TriggerGuideNext(new OnEventTriggerGuide(panelName));
|
||||
return true;
|
||||
}
|
||||
PlayerFishData playerFishData = GContext.container.Resolve<PlayerFishData>();
|
||||
GuidanceGroupDefine guidanceGroupDefine;
|
||||
for (int i = 0; i < guidanceGroupDefines.Length; i++)
|
||||
{
|
||||
guidanceGroupDefine = guidanceGroupDefines[i];
|
||||
|
||||
bool isFinish = GetIsFinish(guidanceGroupDefine.groupName.ToString());
|
||||
if (isFinish || panelName != guidanceGroupDefine.guidanceDefines[0].panelName)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CheckTriggerGuideFinish(guidanceGroupDefine))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int count;
|
||||
switch (guidanceGroupDefine.TriggerConditionsType)
|
||||
{
|
||||
case GuideTriggerConditions.Next:
|
||||
break;
|
||||
case GuideTriggerConditions.First_login:
|
||||
break;
|
||||
case GuideTriggerConditions.OpenSystem_Shop:
|
||||
if (GContext.container.Resolve<PlayerShopData>().IsShopOpen == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
//case GuideTriggerConditions.UnlockSystem_Album:
|
||||
// if (GContext.container.Resolve<AlbumData>().theme == null)
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// break;
|
||||
case GuideTriggerConditions.Level_2:
|
||||
if (playerData.lv >= 2)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.Group_Finish:
|
||||
if (GuideSaveDataDic.TryGetValue(guidanceGroupDefine.TriggerConditionsParameter, out var _data) && _data.isFinish)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.GetDiffFishCount:
|
||||
if (int.TryParse(guidanceGroupDefine.TriggerConditionsParameter, out count) && playerFishData.GetDiffFishCount() >= count)
|
||||
{
|
||||
if (playerFishData.GetDiffFishCount() > count)
|
||||
{
|
||||
TriggerGuideFinish(guidanceGroupDefine);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.GetFishCount:
|
||||
if (int.TryParse(guidanceGroupDefine.TriggerConditionsParameter, out count) && playerFishData.GetAnglingCount() >= count)
|
||||
{
|
||||
if (playerFishData.GetAnglingCount() > count)
|
||||
{
|
||||
TriggerGuideFinish(guidanceGroupDefine);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.FirstClickMap_AtMap2:
|
||||
if (playerData.lastMapId == _tables.TbMapData.DataList[1].ID)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.AccountLevel:
|
||||
if (int.TryParse(guidanceGroupDefine.TriggerConditionsParameter, out count) && playerData.lv >= count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.UnlockSystem:
|
||||
if (int.TryParse(guidanceGroupDefine.TriggerConditionsParameter, out count) && GContext.container.Resolve<FishingEventData>().GetSystemOpen(count))
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.UnlockEvent:
|
||||
string[] str = guidanceGroupDefine.TriggerConditionsParameter.Split('-');
|
||||
if (str.Length == 2)
|
||||
{
|
||||
if (int.TryParse(str[0], out count) && int.TryParse(str[1], out int count2) && GContext.container.Resolve<FishingEventData>().GetEvent(count, count2) > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (str.Length == 3)
|
||||
{
|
||||
if (int.TryParse(str[0], out count) && int.TryParse(str[1], out int count2) && int.TryParse(str[2], out int count3) && GContext.container.Resolve<FishingEventData>().GetEventRedirectID(count, count2) == count3)
|
||||
{
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
//if (int.TryParse(guidanceGroupDefine.TriggerConditionsParameter, out count) && GContext.container.Resolve<FishingEventData>().GetSystemOpen(count))
|
||||
//{
|
||||
// break;
|
||||
//}
|
||||
continue;
|
||||
case GuideTriggerConditions.MaxMultiplierCount:
|
||||
if (playerData.IsOpenMagnification &&
|
||||
int.TryParse(guidanceGroupDefine.TriggerConditionsParameter, out count) &&
|
||||
playerData.ContainsOrGreaterRatio(count))
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.GetSecondRod:
|
||||
if (!prePanelName.Contains("HomePanel") && playerFishData.IsOpenRod && playerFishData.GetRodAllCount() > 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.RodAscendAvailable:
|
||||
if (!prePanelName.Contains("HomePanel") && playerFishData.IsOpenRod && playerFishData.GetRodCanUp())
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.RodUpgradeAvailable:
|
||||
if (!prePanelName.Contains("HomePanel") && playerFishData.IsOpenRod && playerFishData.GetRodCanUpLevel())
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.BuyPack:
|
||||
if (int.TryParse(guidanceGroupDefine.TriggerConditionsParameter, out count) && GContext.container.Resolve<PlayerShopData>().GetAllPackCount(count) > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.BoxOpenCondition:
|
||||
FishingBoxDataProvier fishingBoxData = GContext.container.Resolve<FishingBoxDataProvier>();
|
||||
if (fishingBoxData.IsUnclocked && fishingBoxData.GetBoxCount(fishingBoxData.GetIDByQuality(3)) > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.GoBuilding:
|
||||
if (GContext.container.Resolve<CampDataMM>().GoBuilding())
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.GoBuilding02:
|
||||
if (playerData.lv >= 12)
|
||||
{
|
||||
TriggerGuideFinish(guidanceGroupDefine);
|
||||
Debug.Log("TriggerGuideFinish(guidanceGroupDefine);");
|
||||
continue;
|
||||
}
|
||||
if (GContext.container.Resolve<CampDataMM>().GoBuilding02(101012))
|
||||
{
|
||||
Debug.Log("TriggerGuideFinish();");
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (curGroupDefine == guidanceGroupDefine)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (panelName == guidanceGroupDefine.guidanceDefines[0].panelName)
|
||||
{
|
||||
TriggerGuide(guidanceGroupDefine, curPanelName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void MarkGuideFinished(GroupName groupName)
|
||||
{
|
||||
string key = groupName.ToString();
|
||||
if (GetIsFinish(key)) return;
|
||||
if (!GuideSaveDataDic.TryGetValue(key, out var saveData))
|
||||
{
|
||||
saveData = new GuideSaveData();
|
||||
saveData.groupName = key;
|
||||
GuideSaveDataDic.Add(key, saveData);
|
||||
}
|
||||
saveData.isFinish = true;
|
||||
Save();
|
||||
}
|
||||
|
||||
public void ClearGuide(string groupName)
|
||||
{
|
||||
if (GuideSaveDataDic.ContainsKey(groupName))
|
||||
{
|
||||
GuideSaveDataDic.Remove(groupName);
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 特殊屏蔽的新手引导
|
||||
/// </summary>
|
||||
/// <param name="group"></param>
|
||||
bool CheckTriggerGuideFinish(GuidanceGroupDefine group)
|
||||
{
|
||||
bool isFinishPass = false;
|
||||
//判断是否直接完成
|
||||
if (group.groupName == GroupName.Chanllenge)
|
||||
{
|
||||
// isFinishPass = !GContext.container.Resolve<FishingChallengeCenter>().IsFirstOpen;
|
||||
isFinishPass = !GContext.container.Resolve<FishingChallengeManager>().IsFirstOpen;
|
||||
}
|
||||
|
||||
if (isFinishPass)
|
||||
{
|
||||
TriggerGuideFinish(group);
|
||||
}
|
||||
return isFinishPass;
|
||||
}
|
||||
|
||||
void TriggerGuideFinish(GuidanceGroupDefine group)
|
||||
{
|
||||
string groupName = group.groupName.ToString();
|
||||
if (!GuideSaveDataDic.TryGetValue(groupName, out var guideSaveData))
|
||||
{
|
||||
guideSaveData = new GuideSaveData();
|
||||
guideSaveData.groupName = groupName;
|
||||
GuideSaveDataDic.Add(groupName, guideSaveData);
|
||||
}
|
||||
guideSaveData.guideSubIndex = group.guidanceDefines.Length;
|
||||
guideSaveData.isFinish = true;
|
||||
Save();
|
||||
}
|
||||
public bool TriggerGuide(string groupName, string panelName, bool loop = true,string curPanelName = "")
|
||||
{
|
||||
if (guidanceDefineSetting == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
GuidanceGroupDefine guidanceGroupDefine;
|
||||
if (!loop)
|
||||
{
|
||||
bool isFinish = GetIsFinish(groupName.ToString());
|
||||
if (isFinish)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < guidanceGroupDefines.Length; i++)
|
||||
{
|
||||
guidanceGroupDefine = guidanceGroupDefines[i];
|
||||
if (guidanceGroupDefine.groupName.ToString() == groupName)
|
||||
{
|
||||
if (panelName != guidanceGroupDefine.guidanceDefines[0].panelName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
TriggerGuide(groupName, curPanelName, guidanceGroupDefine);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
async void TriggerGuide(string groupName, string curPanelName, GuidanceGroupDefine guidanceGroupDefine)
|
||||
{
|
||||
var store = playerSaveStore;
|
||||
await System.Threading.Tasks.Task.Delay(200);
|
||||
if (disposed || !store.IsCurrent) return;
|
||||
curSaveData = new GuideSaveData();
|
||||
curSaveData.groupName = groupName;
|
||||
GuideSaveDataDic[groupName] = curSaveData;
|
||||
curGroupDefine = guidanceGroupDefine;
|
||||
curDefine = curGroupDefine.guidanceDefines[0];
|
||||
ShowPanel(curPanelName);
|
||||
curSaveData.guideSubIndex++;
|
||||
}
|
||||
void TriggerGuide(GuidanceGroupDefine group, string curPanelName)
|
||||
{
|
||||
string groupName = group.groupName.ToString();
|
||||
if (!GuideSaveDataDic.TryGetValue(groupName, out var guideSaveData))
|
||||
{
|
||||
guideSaveData = new GuideSaveData();
|
||||
guideSaveData.groupName = groupName;
|
||||
GuideSaveDataDic.Add(groupName, guideSaveData);
|
||||
}
|
||||
if (guideSaveData.isFinish)
|
||||
{
|
||||
return;
|
||||
}
|
||||
guideSaveData.guideSubIndex = 0;
|
||||
curSaveData = guideSaveData;
|
||||
|
||||
curGroupDefine = group;
|
||||
curDefine = group.guidanceDefines[0];
|
||||
ShowPanel(curPanelName);
|
||||
curSaveData.guideSubIndex++;
|
||||
|
||||
}
|
||||
public void TriggerGuideNext(OnEventTriggerGuide eventTriggerGuide)
|
||||
{
|
||||
if (curGroupDefine != null && GuideSaveDataDic.TryGetValue(curGroupDefine.groupName.ToString(), out var guideSaveData))
|
||||
{
|
||||
if (curGroupDefine.guidanceDefines.Length <= curSaveData.guideSubIndex || eventTriggerGuide.panelName != curGroupDefine.guidanceDefines[guideSaveData.guideSubIndex].panelName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
curDefine = curGroupDefine.guidanceDefines[guideSaveData.guideSubIndex];
|
||||
if (curDefine.completeType == CompleteType.PanelOpen)
|
||||
{
|
||||
string uIType = curDefine.completeTypeParameter;
|
||||
if (!string.IsNullOrEmpty(uIType) && UIManager.Instance.ContainsUI(uIType))
|
||||
{
|
||||
curSaveData.guideSubIndex++;
|
||||
AddIndex();
|
||||
}
|
||||
else
|
||||
{
|
||||
disposable = GContext.OnEvent<GetUIAsyncEvent>().Subscribe(data =>
|
||||
{
|
||||
if (uIType == data.uIType && data.show)
|
||||
{
|
||||
curSaveData.guideSubIndex++;
|
||||
AddIndex();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowPanel(CurPanelName);
|
||||
curSaveData.guideSubIndex++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
InspectTriggerGuide(eventTriggerGuide.panelName);
|
||||
}
|
||||
}
|
||||
public void AddIndex(bool is_pass = false)
|
||||
{
|
||||
if (curDefine == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (disposable != null)
|
||||
{
|
||||
disposable.Dispose();
|
||||
disposable = null;
|
||||
}
|
||||
if (disposable3 != null)
|
||||
{
|
||||
disposable3.Dispose();
|
||||
disposable3 = null;
|
||||
}
|
||||
UIManager.Instance.HideUI(UITypes.GuidancePanel);
|
||||
bool isAutoNext = curDefine.isAutoNext;
|
||||
AddIndexEvent addIndexEvent = new AddIndexEvent();
|
||||
addIndexEvent.isAutoNext = isAutoNext;
|
||||
if (curGroupDefine.guidanceDefines.Length <= curSaveData.guideSubIndex || is_pass)
|
||||
{
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("guide_complete"))
|
||||
{
|
||||
e.AddContent("guide_id", curGroupDefine.groupName.ToString())
|
||||
.AddContent("is_pass", is_pass);
|
||||
}
|
||||
#endif
|
||||
string panelName = curDefine.panelName;
|
||||
curDefine = null;
|
||||
curGroupDefine = null;
|
||||
curSaveData.isFinish = true;
|
||||
Save();
|
||||
if (isAutoNext)
|
||||
{
|
||||
InspectTriggerGuide(panelName, panelName);
|
||||
}
|
||||
GContext.Publish(new ConditionTypeEvent(ConditionType.CompleteGuide, 1));
|
||||
addIndexEvent.CompleteGuide = true;
|
||||
addIndexEvent.guideSubIndex = curSaveData.guideSubIndex;
|
||||
GContext.Publish(addIndexEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
Save();
|
||||
var define = curGroupDefine.guidanceDefines[curSaveData.guideSubIndex];
|
||||
if (isAutoNext && define.panelName == curDefine.panelName)
|
||||
{
|
||||
curDefine = define;
|
||||
ShowPanel(CurPanelName);
|
||||
curSaveData.guideSubIndex++;
|
||||
}
|
||||
else if (!isAutoNext)
|
||||
{
|
||||
for (int i = curSaveData.guideSubIndex; i < curGroupDefine.guidanceDefines.Length; i++)
|
||||
{
|
||||
var _define = curGroupDefine.guidanceDefines[i];
|
||||
if (_define.isKey)
|
||||
{
|
||||
curSaveData.isFinish = false;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
curSaveData.isFinish = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
addIndexEvent.guideSubIndex = curSaveData.guideSubIndex;
|
||||
GContext.Publish(addIndexEvent);
|
||||
}
|
||||
}
|
||||
async void ShowPanel(string PanelName)
|
||||
{
|
||||
if (disposed || !playerSaveStore.IsCurrent) return;
|
||||
if (curDefine.fingerType != FingerType.None)
|
||||
{
|
||||
disposable3 = GContext.OnEvent<GuideNextEvent>().Subscribe(GuideNextEvent);
|
||||
}
|
||||
CurPanelName = PanelName;
|
||||
await UIManager.Instance.ShowUI(UITypes.GuidancePanel);
|
||||
}
|
||||
|
||||
public async void ShowGuidancePopupPanel(string nodeName)
|
||||
{
|
||||
var store = playerSaveStore;
|
||||
if (disposed || !store.IsCurrent) return;
|
||||
popupPanelNode = nodeName;
|
||||
if (!string.IsNullOrEmpty(popupPanelNode))
|
||||
{
|
||||
await UIManager.Instance.ShowUI(UITypes.GuidancePopupPanel);
|
||||
if (disposed || !store.IsCurrent) return;
|
||||
UIManager.Instance.HideUI(UITypes.GuidancePanel);
|
||||
}
|
||||
}
|
||||
|
||||
void GuideNextEvent(GuideNextEvent guideLongEvent)
|
||||
{
|
||||
if (curDefine != null && curDefine.fingerType != FingerType.None)
|
||||
{
|
||||
AddIndex();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsGuide(string panelName)
|
||||
{
|
||||
bool isGuide = curDefine != null;
|
||||
if (!isGuide)
|
||||
{
|
||||
InspectTriggerGuide(panelName);
|
||||
}
|
||||
return curDefine != null && curDefine.panelName == panelName;
|
||||
}
|
||||
|
||||
public bool IsGuide(string panelName, int index, GroupName groupName)
|
||||
{
|
||||
bool isGuide = curDefine != null;
|
||||
|
||||
if (isGuide && curGroupDefine.groupName == groupName && curSaveData.guideSubIndex == index)
|
||||
{
|
||||
InspectTriggerGuide(panelName);
|
||||
return true;
|
||||
}
|
||||
return false;// curDefine != null && curDefine.panelName == panelName;
|
||||
}
|
||||
public void SetData(string value)
|
||||
{
|
||||
string[] groupStrings = value.Split(';');
|
||||
foreach (var groupString in groupStrings)
|
||||
{
|
||||
string[] strings = groupString.Split('|');
|
||||
if (strings.Length == 3)
|
||||
{
|
||||
GuideSaveData saveData = new GuideSaveData();
|
||||
saveData.groupName = strings[0];
|
||||
if (int.TryParse(strings[1], out int guideSubIndex))
|
||||
{
|
||||
saveData.guideSubIndex = guideSubIndex;
|
||||
}
|
||||
saveData.isFinish = strings[2] == "1";
|
||||
GuideSaveDataDic[saveData.groupName] = saveData;
|
||||
}
|
||||
}
|
||||
}
|
||||
public void Save()
|
||||
{
|
||||
List<string> strings = GuideSaveDataDic.Select(kv => kv.Key + "|" + kv.Value.guideSubIndex + "|" + (kv.Value.isFinish ? "1" : "0")).ToList();
|
||||
string value = string.Join(";", strings);
|
||||
playerSaveStore.Write("GuideSaveDataNew", value).RequireAccepted();
|
||||
}
|
||||
|
||||
bool disposed;
|
||||
public void Dispose()
|
||||
{
|
||||
disposed = true;
|
||||
disposable?.Dispose();
|
||||
disposable = null;
|
||||
disposable2?.Dispose();
|
||||
disposable2 = null;
|
||||
disposable3?.Dispose();
|
||||
disposable3 = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,232 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using LitJson;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GameCore
|
||||
{
|
||||
public class DailySignEvent
|
||||
{
|
||||
public int ID;
|
||||
//领取第几个
|
||||
public int index;
|
||||
//签到最后一次是哪一天or链式礼包PackManager.ID
|
||||
public int lastID;
|
||||
//结束时间
|
||||
public string time;
|
||||
}
|
||||
public class MonthlySignEvent
|
||||
{
|
||||
public int index;
|
||||
//结束时间
|
||||
public DailySignEvent dailySignEvent;
|
||||
}
|
||||
public class OnSignEvent
|
||||
{
|
||||
public List<ItemData> itemDataList;
|
||||
}
|
||||
public class SigninData
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore SaveStore { get; set; }
|
||||
|
||||
private cfg.Tables _tables;
|
||||
public SigninData(cfg.Tables tables)
|
||||
{
|
||||
this._tables = tables;
|
||||
}
|
||||
|
||||
MonthlySignEvent _monthlySignEvent = null;
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVMonthlySignState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) => game.PlayerDataStorage.PlayerKVSaveSchema.DecodeMonthlySign(raw,errors);
|
||||
internal void ResetForKVLoad() { _monthlySignEvent = null; }
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVMonthlySignState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
_monthlySignEvent = !state.IsPresent ? null : new MonthlySignEvent
|
||||
{
|
||||
index = state.Index,
|
||||
dailySignEvent = new DailySignEvent { ID=state.DailyId, index=state.DailyIndex, lastID=state.LastId, time=state.Time }
|
||||
};
|
||||
}
|
||||
|
||||
public DailySignEvent dailySignEvent
|
||||
{
|
||||
get => _monthlySignEvent == null ? null : _monthlySignEvent.dailySignEvent;
|
||||
}
|
||||
|
||||
public MonthlySignEvent monthlySignEvent
|
||||
{
|
||||
get => _monthlySignEvent;
|
||||
set => _monthlySignEvent = value;
|
||||
}
|
||||
public void SetDailySignEvent(FishingEvent t)
|
||||
{
|
||||
DateTime dateTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
bool isSave = false;
|
||||
DailySignEvent data = null;
|
||||
if (_monthlySignEvent == null)
|
||||
{
|
||||
_monthlySignEvent = new MonthlySignEvent()
|
||||
{
|
||||
index = 0,
|
||||
};
|
||||
isSave = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<MonthlySign> MonthlySignL = _tables.TbMonthlySign.DataList;
|
||||
|
||||
data = _monthlySignEvent.dailySignEvent;
|
||||
if (_monthlySignEvent.index >= MonthlySignL[^1].SignDayAccount && data.lastID != dateTime.DayOfYear)
|
||||
{
|
||||
_monthlySignEvent = new MonthlySignEvent()
|
||||
{
|
||||
index = 0,
|
||||
dailySignEvent = data,
|
||||
};
|
||||
isSave = true;
|
||||
}
|
||||
}
|
||||
SetTheme();
|
||||
if (data == null || (data.index >= 7 && data.lastID != dateTime.DayOfYear))
|
||||
{
|
||||
_monthlySignEvent.dailySignEvent = new DailySignEvent()
|
||||
{
|
||||
ID = t.RedirectID,
|
||||
lastID = -1,
|
||||
index = 0,
|
||||
time = dateTime.Date.ToString()
|
||||
};
|
||||
isSave = true;
|
||||
}
|
||||
if (isSave)
|
||||
{
|
||||
SaveStore.Write("MonthlySignEvent", JsonMapper.ToJson(_monthlySignEvent)).RequireAccepted();
|
||||
}
|
||||
bool isReward = CanDailySign();
|
||||
if (isReward)
|
||||
{
|
||||
ILoadResourceService loadResourceService = GContext.container.Resolve<ILoadResourceService>();
|
||||
loadResourceService.EnqueueBundleSilently(new List<string>() { UITypes.EventSignupPopupPanel_2.Name });
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUI(t.ID, UITypes.EventSignupPopupPanel_2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//签到
|
||||
public bool OnDailySign(int index, int DropID)
|
||||
{
|
||||
if (_monthlySignEvent == null || _monthlySignEvent.dailySignEvent == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool isSign = SignEvent(_monthlySignEvent.dailySignEvent, index);
|
||||
if (isSign)
|
||||
{
|
||||
_monthlySignEvent.index++;
|
||||
List<ItemData> itemDataList = GContext.container.Resolve<PlayerItemData>().AddItemByDropLureInflation(null, DropID, null, false);
|
||||
#if AGG
|
||||
DateTime tomorrow = GlobalUtils.TryParseDateTime(_monthlySignEvent.dailySignEvent.time, ZZTimeHelper.UtcNow().UtcNowOffset());
|
||||
TimeSpan remaining = ZZTimeHelper.UtcNow().UtcNowOffset() - tomorrow;
|
||||
using (var e = GEvent.GameEvent("user_checkin"))
|
||||
{
|
||||
e.AddContent("day_num", remaining.Days)
|
||||
.AddContent("day_count", _monthlySignEvent.dailySignEvent.index);
|
||||
if (itemDataList != null)
|
||||
{
|
||||
for (int i = 0; i < itemDataList.Count; i++)
|
||||
{
|
||||
e.AddContent($"item_{itemDataList[i].id}", itemDataList[i].count);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
List<MonthlySign> MonthlySignL = _tables.TbMonthlySign.DataList;
|
||||
List<ItemData> monthlySignReward = null;
|
||||
for (int i = 0; i < MonthlySignL.Count; i++)
|
||||
{
|
||||
if (_monthlySignEvent.index == MonthlySignL[i].SignDayAccount)
|
||||
{
|
||||
monthlySignReward = GContext.container.Resolve<PlayerItemData>().AddItemByDropLureInflation(null, MonthlySignL[i].DropID, null, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//GContext.Publish(new ShowData());
|
||||
SaveStore.Write("MonthlySignEvent", JsonMapper.ToJson(_monthlySignEvent)).RequireAccepted();
|
||||
GContext.Publish(new OnSignEvent() { itemDataList = monthlySignReward });
|
||||
}
|
||||
return isSign;
|
||||
}
|
||||
|
||||
|
||||
bool SignEvent(DailySignEvent data, int index)
|
||||
{
|
||||
if (data.lastID == ZZTimeHelper.UtcNow().UtcNowOffset().DayOfYear)
|
||||
{
|
||||
ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_1"));
|
||||
return false;
|
||||
}
|
||||
if (index == data.index)
|
||||
{
|
||||
data.lastID = ZZTimeHelper.UtcNow().UtcNowOffset().DayOfYear;
|
||||
data.index++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public bool DailySignIsOpen()
|
||||
{
|
||||
return _monthlySignEvent != null && _monthlySignEvent.dailySignEvent != null;
|
||||
}
|
||||
|
||||
public bool CanDailySign()
|
||||
{
|
||||
DailySignEvent data = dailySignEvent;
|
||||
return data != null
|
||||
&& data.lastID != ZZTimeHelper.UtcNow().UtcNowOffset().DayOfYear
|
||||
&& data.index < 7;
|
||||
}
|
||||
|
||||
public bool ShouldLoadSignInInterstitial()
|
||||
{
|
||||
return DailySignIsOpen() && FishingEventData.Condition_NoRechargeDay(15);
|
||||
}
|
||||
|
||||
private const string DefaultPanel = "EventSignupPopupPanel_2";
|
||||
private void SetTheme()
|
||||
{
|
||||
var themeList = _tables.TbEventSignSpTheme.DataList;
|
||||
UITypes.EventSignupPopupPanel_2.SetType(DefaultPanel);
|
||||
foreach (var theme in themeList)
|
||||
{
|
||||
try
|
||||
{
|
||||
var startTime = DateTime.Parse((theme.TimeDefinition as LimitedTime).StartTime);
|
||||
var endTime = DateTime.Parse((theme.TimeDefinition as LimitedTime).EndTime);
|
||||
var timeNow = ZZTimeHelper.UtcNow();
|
||||
if (startTime <= timeNow && timeNow <= endTime)
|
||||
{
|
||||
UITypes.EventSignupPopupPanel_2.SetType(theme.Prefab);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"[DataCenter] SetSignInTheme Error:");
|
||||
UnityEngine.Debug.LogError(e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void ResetTheme(TimeSpan duration)
|
||||
{
|
||||
await System.Threading.Tasks.Task.Delay(duration);
|
||||
UITypes.EventSignupPopupPanel_2.SetType(DefaultPanel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,706 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using LitJson;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using static EventPartnerData;
|
||||
|
||||
namespace GameCore
|
||||
{
|
||||
public class ChangeSmallGameTargetEvent
|
||||
{
|
||||
public OpponentItemData opponentItemData;
|
||||
public System.Threading.Tasks.TaskCompletionSource<bool> ts;
|
||||
public bool random;
|
||||
}
|
||||
public class SmallGameData
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore playerSaveStore { get; set; }
|
||||
private cfg.Tables _tables;
|
||||
IUserService userService;
|
||||
ICustomServerMgr customServerMgr;
|
||||
public SmallGameData(cfg.Tables tables, IUserService userService, ICustomServerMgr customServerMgr)
|
||||
{
|
||||
this._tables = tables;
|
||||
this.userService = userService;
|
||||
this.customServerMgr = customServerMgr;
|
||||
BottleCashParamBase = _tables.TbGlobalConfig.BottleCashParamBase;
|
||||
BottleCashParamDiv = _tables.TbGlobalConfig.BottleCashParamDiv;
|
||||
BottleCashParamLevel = _tables.TbGlobalConfig.BottleCashParamLevel;
|
||||
}
|
||||
public void Init()
|
||||
{
|
||||
campData = GContext.container.Resolve<CampDataMM>();
|
||||
|
||||
LoginRandomOpponent();
|
||||
}
|
||||
CampDataMM campData;
|
||||
|
||||
int _heistGameCount = 0;
|
||||
int _bombGameCount = 0;
|
||||
int _fishHuntGameCount = 0;
|
||||
public int HeistGameCount => _heistGameCount;
|
||||
public int FishHuntGameCount => _fishHuntGameCount;
|
||||
public int BombGameCount => _bombGameCount;
|
||||
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
_heistGameCount=_bombGameCount=_fishHuntGameCount=0;
|
||||
opponentItemDatas=new List<OpponentItemData>(); opponentFriendItemDatas=new List<OpponentItemData>();
|
||||
opponentNotFriendItemDatas=new List<OpponentItemData>(); opponentFriend=new List<OpponentItemData>();
|
||||
opponentFriendDic=new Dictionary<string,OpponentItemData>(); opponentEnemy=new Dictionary<EnemyType,List<OpponentItemData>>();
|
||||
opponentItemDatasPre=new List<OpponentItemData>(); opponentFriendItemDatasPre=new List<OpponentItemData>();
|
||||
opponentNotFriendItemDatasPre=new List<OpponentItemData>(); powerData=new Dictionary<string,float>();
|
||||
opponentData=null; nextOpponent=null; curEnemyType=EnemyType.Aquarium; RobotMapId=nextRobotMapId=0;
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVCoreState state)
|
||||
{
|
||||
if(state==null) throw new ArgumentNullException(nameof(state));
|
||||
_heistGameCount=state.Integer(16); _bombGameCount=state.Integer(17); _fishHuntGameCount=state.Integer(18);
|
||||
}
|
||||
internal bool IsOpponentEmptyForKVValidation => opponentItemDatas.Count==0 && opponentFriendItemDatas.Count==0 &&
|
||||
opponentNotFriendItemDatas.Count==0 && opponentFriend.Count==0 && opponentFriendDic.Count==0 && opponentEnemy.Count==0 &&
|
||||
opponentItemDatasPre.Count==0 && opponentFriendItemDatasPre.Count==0 && opponentNotFriendItemDatasPre.Count==0 &&
|
||||
powerData.Count==0 && opponentData==null && nextOpponent==null && RobotMapId==0 && nextRobotMapId==0;
|
||||
|
||||
//public UnityEngine.Vector3 image_extra_pos { set; get; }
|
||||
|
||||
public void SetHeistGameCount(int value)
|
||||
{
|
||||
_heistGameCount = value;
|
||||
}
|
||||
public void SaveHeistGameCount()
|
||||
{
|
||||
_heistGameCount++;
|
||||
playerSaveStore.Write("HeistGameCount", _heistGameCount.ToString()).RequireAccepted();
|
||||
}
|
||||
public void SetBombGameCount(int value)
|
||||
{
|
||||
_bombGameCount = value;
|
||||
}
|
||||
public void SaveBombGameCount()
|
||||
{
|
||||
_bombGameCount++;
|
||||
playerSaveStore.Write("BombGameCount", _bombGameCount.ToString()).RequireAccepted();
|
||||
}
|
||||
public void SetFishHuntGameCount(int value)
|
||||
{
|
||||
_fishHuntGameCount = value;
|
||||
}
|
||||
public void SaveFishHuntGameCount()
|
||||
{
|
||||
_fishHuntGameCount++;
|
||||
playerSaveStore.Write("FishHuntGameCount", _fishHuntGameCount.ToString()).RequireAccepted();
|
||||
}
|
||||
#region 小游戏仇人
|
||||
public List<OpponentItemData> opponentItemDatas = new List<OpponentItemData>();
|
||||
public List<OpponentItemData> opponentFriendItemDatas = new List<OpponentItemData>();
|
||||
public List<OpponentItemData> opponentNotFriendItemDatas = new List<OpponentItemData>();
|
||||
public List<OpponentItemData> opponentFriend = new List<OpponentItemData>();
|
||||
Dictionary<string, OpponentItemData> opponentFriendDic = new Dictionary<string, OpponentItemData>();
|
||||
public Dictionary<EnemyType, List<OpponentItemData>> opponentEnemy = new Dictionary<EnemyType, List<OpponentItemData>>();
|
||||
List<OpponentItemData> opponentItemDatasPre = new List<OpponentItemData>();
|
||||
List<OpponentItemData> opponentFriendItemDatasPre = new List<OpponentItemData>();
|
||||
List<OpponentItemData> opponentNotFriendItemDatasPre = new List<OpponentItemData>();
|
||||
EnemyType curEnemyType = EnemyType.Aquarium;
|
||||
public Dictionary<string, float> powerData;
|
||||
public OpponentItemData opponentData;
|
||||
OpponentItemData nextOpponent;
|
||||
int GMbobmName = 0;
|
||||
int BottleCashParamBase;
|
||||
int BottleCashParamDiv;
|
||||
int BottleCashParamLevel;
|
||||
public int RobotMapId;
|
||||
public int nextRobotMapId;
|
||||
public void GMSetBomb(string bobmName)
|
||||
{
|
||||
GMbobmName = int.Parse(bobmName);
|
||||
}
|
||||
|
||||
public async void LoginRandomOpponent()
|
||||
{
|
||||
GetRobotData();
|
||||
RandomOpponent();
|
||||
await LoadData();
|
||||
RandomOpponent();
|
||||
}
|
||||
|
||||
void RandomOpponent()
|
||||
{
|
||||
Synchrodata();
|
||||
nextOpponent = NextOpponent();
|
||||
LoadNextOpponentResource();
|
||||
}
|
||||
|
||||
public List<OpponentItemData> GetOpponentEnemy()
|
||||
{
|
||||
return opponentEnemy.GetValueOrDefault(curEnemyType);
|
||||
}
|
||||
|
||||
public OpponentItemData GetBankHeistOpponent(EnemyType enemyType)
|
||||
{
|
||||
this.curEnemyType = enemyType;
|
||||
if (opponentItemDatasPre == null || opponentItemDatasPre.Count == 0)
|
||||
{
|
||||
ClearOpponent();
|
||||
GetFriendData();
|
||||
GetRobotData();
|
||||
}
|
||||
opponentData = RandomItem();
|
||||
LoadData();
|
||||
GetEnemyData(curEnemyType);
|
||||
if (GMbobmName > 0)
|
||||
{
|
||||
opponentData.BombID = GMbobmName;
|
||||
}
|
||||
return opponentData;
|
||||
}
|
||||
|
||||
void Synchrodata()
|
||||
{
|
||||
if (opponentItemDatasPre != null && opponentItemDatasPre.Count > 0)
|
||||
{
|
||||
opponentItemDatas = opponentItemDatasPre;
|
||||
}
|
||||
if (opponentFriendItemDatasPre != null && opponentFriendItemDatasPre.Count > 0)
|
||||
{
|
||||
opponentFriendItemDatas = opponentFriendItemDatasPre;
|
||||
}
|
||||
if (opponentNotFriendItemDatasPre != null && opponentNotFriendItemDatasPre.Count > 0)
|
||||
{
|
||||
opponentNotFriendItemDatas = opponentNotFriendItemDatasPre;
|
||||
}
|
||||
}
|
||||
public OpponentItemData RandomItem()
|
||||
{
|
||||
OpponentItemData target = nextOpponent;
|
||||
Synchrodata();
|
||||
nextOpponent = NextOpponent();
|
||||
RobotMapId = nextRobotMapId;
|
||||
LoadNextOpponentResource();
|
||||
return target;
|
||||
}
|
||||
|
||||
async void LoadNextOpponentResource()
|
||||
{
|
||||
if (nextOpponent != null)
|
||||
{
|
||||
ILoadResourceService loadResourceService = GContext.container.Resolve<ILoadResourceService>();
|
||||
RobotSetMap();
|
||||
int mapId;
|
||||
if (nextOpponent.isRobot)
|
||||
{
|
||||
mapId = nextRobotMapId;
|
||||
}
|
||||
else
|
||||
{
|
||||
mapId = await LoadAquariumResource(nextOpponent.playFabID);
|
||||
if (mapId > 0)
|
||||
{
|
||||
nextRobotMapId = mapId;
|
||||
}
|
||||
else
|
||||
{
|
||||
mapId = nextRobotMapId;
|
||||
}
|
||||
}
|
||||
Debug.Log($"nextOpponent Next MapID {mapId}");
|
||||
|
||||
if (mapId > 0)
|
||||
{
|
||||
MapDownloadRes mapData = _tables.TbMapDownloadRes.GetOrDefault(mapId);
|
||||
if (mapData != null)
|
||||
{
|
||||
loadResourceService.EnqueueBundleSilently(mapData.FishRes);
|
||||
}
|
||||
}
|
||||
|
||||
string bombPrefab = _tables.TbConstruction.GetOrDefault(nextOpponent.BombID)?.BombPrefab;
|
||||
if (!string.IsNullOrEmpty(bombPrefab))
|
||||
{
|
||||
loadResourceService.EnqueueBundleSilently(new List<string>() { bombPrefab });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RobotSetMap()
|
||||
{
|
||||
int curMapId = nextRobotMapId;
|
||||
|
||||
var mapDatas = _tables.TbMapData.DataList;
|
||||
var maxMapID = GContext.container.Resolve<PlayerData>().lastMapId;
|
||||
|
||||
//非当前地图
|
||||
var mapIDs = new List<int>();
|
||||
for (int i = 0; i < mapDatas.Count; i++)
|
||||
{
|
||||
if (mapDatas[i].ID <= maxMapID)
|
||||
{
|
||||
if (mapDatas[i].ID != curMapId)
|
||||
{
|
||||
mapIDs.Add(mapDatas[i].ID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mapIDs.Count == 0)
|
||||
{
|
||||
nextRobotMapId = maxMapID;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextRobotMapId = mapIDs[UnityEngine.Random.Range(0, mapIDs.Count)];
|
||||
}
|
||||
}
|
||||
|
||||
async Task<int> LoadAquariumResource(string playFabId)
|
||||
{
|
||||
GetAquariumDataRequest request = new GetAquariumDataRequest();
|
||||
request.playFabId = playFabId;
|
||||
string result = await customServerMgr.CustomServerPost("aquarium/GetAquariumData", Newtonsoft.Json.JsonConvert.SerializeObject(request));
|
||||
if (result != null)
|
||||
{
|
||||
var resp = JsonConvert.DeserializeObject<GetAquariumDataResp>(result);
|
||||
if (resp.code == 0)
|
||||
{
|
||||
var data = resp.data;
|
||||
List<AquariumSlotData> slotDatas = data.slotDatas;
|
||||
if (slotDatas == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var time = ZZTimeHelper.UtcNow();
|
||||
List<AquariumResultData> aquariumResultDatas = data.aquariumResultDatas;
|
||||
for (int i = 0; i < slotDatas.Count; i++)
|
||||
{
|
||||
AquariumSlotData slotData = slotDatas[i];
|
||||
slotData.index = i;
|
||||
var HatchTime = slotData.FishHatchTime();
|
||||
|
||||
if (HatchTime < time)
|
||||
{
|
||||
if (!slotData.IsResult() && aquariumResultDatas.Count > i && !aquariumResultDatas[i].IsResult())
|
||||
{
|
||||
return resp.data.mapId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public OpponentItemData NextOpponent()
|
||||
{
|
||||
int friendW = Math.Min(100, opponentFriendItemDatas.Count * 20);
|
||||
if (opponentFriendItemDatas.Count == 0)
|
||||
{
|
||||
friendW = 0;
|
||||
}
|
||||
int noFriendW = 70;
|
||||
if (opponentNotFriendItemDatas.Count == 0)
|
||||
{
|
||||
noFriendW = 0;
|
||||
}
|
||||
int robotW = 30;
|
||||
int allW = friendW + noFriendW + robotW;
|
||||
int w = UnityEngine.Random.Range(0, allW);
|
||||
OpponentItemData target;
|
||||
if (friendW > 0 && opponentFriendItemDatas.Count > 0 && w < friendW)
|
||||
{
|
||||
target = opponentFriendItemDatas[UnityEngine.Random.Range(0, opponentFriendItemDatas.Count)];
|
||||
target.target_group = 1;
|
||||
}
|
||||
else if (noFriendW > 0 && opponentNotFriendItemDatas.Count > 0 && w < friendW + noFriendW)
|
||||
{
|
||||
target = opponentNotFriendItemDatas[UnityEngine.Random.Range(0, opponentNotFriendItemDatas.Count)];
|
||||
target.target_group = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (opponentItemDatas == null || opponentItemDatas.Count == 0)
|
||||
{
|
||||
GetRobotData();
|
||||
opponentItemDatas = opponentItemDatasPre;
|
||||
Debug.LogError("opponentItemDatas Count == 0");
|
||||
}
|
||||
target = opponentItemDatas[UnityEngine.Random.Range(0, opponentItemDatas.Count)];
|
||||
target.target_group = 3;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
void ClearOpponent()
|
||||
{
|
||||
Synchrodata();
|
||||
opponentFriendItemDatasPre = new List<OpponentItemData>();
|
||||
opponentNotFriendItemDatasPre = new List<OpponentItemData>();
|
||||
opponentItemDatasPre = new List<OpponentItemData>();
|
||||
powerData = new Dictionary<string, float>();
|
||||
opponentFriend = new List<OpponentItemData>();
|
||||
opponentFriendDic = new Dictionary<string, OpponentItemData>();
|
||||
}
|
||||
public async Task LoadData()
|
||||
{
|
||||
ClearOpponent();
|
||||
GetFriendData();
|
||||
try
|
||||
{
|
||||
await GetActiveNotFriendData();
|
||||
await GetLeaderboardData(curEnemyType);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError(ex.Message);
|
||||
}
|
||||
GetRobotData();
|
||||
}
|
||||
|
||||
async Task GetActiveNotFriendData()
|
||||
{
|
||||
var request = new EventBuildRecommendPlayerRequest();
|
||||
int lv = GContext.container.Resolve<PlayerData>().lv;
|
||||
int minLevel = (int)(lv * 0.5f);
|
||||
if (minLevel < 0)
|
||||
{
|
||||
minLevel = 0;
|
||||
}
|
||||
request.MinLevel = minLevel;
|
||||
request.MaxLevel = lv * 2;
|
||||
request.Exclude = new List<string>();
|
||||
|
||||
string json = await customServerMgr.CustomServerPost($"eventpartner/recommend",
|
||||
Newtonsoft.Json.JsonConvert.SerializeObject(request));
|
||||
if (!string.IsNullOrEmpty(json) && json != "[]")
|
||||
{
|
||||
EventBuildRecommendPlayerResp resp = Newtonsoft.Json.JsonConvert.DeserializeObject<EventBuildRecommendPlayerResp>(json);
|
||||
if (resp.State == EEventBuildState.Success && resp.Players != null)
|
||||
{
|
||||
List<Construction> _dataList = _tables.TbConstruction.DataList;
|
||||
for (int i = 0; i < resp.Players.Count; i++)
|
||||
{
|
||||
if (resp.Players[i].PlayFabId == userService.UserId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
OpponentItemData opponentItemData = new OpponentItemData();
|
||||
opponentItemData.playFabID = resp.Players[i].PlayFabId;
|
||||
opponentItemData.target_source = 2;
|
||||
|
||||
userService.SetPlayInfo(resp.Players[i].PlayFabId, resp.Players[i].DisplayName, resp.Players[i].AvatarUrl);
|
||||
userService.SetPlayerLv(resp.Players[i].PlayFabId, resp.Players[i].Level);
|
||||
int level = resp.Players[i].Level;
|
||||
|
||||
opponentItemData.BombID = GetConstructionID(level);
|
||||
opponentItemData.power = (float)Math.Log10(BottleCashParamBase + level / BottleCashParamLevel) / BottleCashParamDiv;
|
||||
powerData[opponentItemData.playFabID] = opponentItemData.power;
|
||||
//opponentItemDatasPre.Add(opponentItemData);
|
||||
opponentNotFriendItemDatasPre.Add(opponentItemData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int GetConstructionID(int level)
|
||||
{
|
||||
List<Construction> _dataList = _tables.TbConstruction.DataList;
|
||||
int id = _dataList[0].ID;
|
||||
for (int j = _dataList.Count - 1; j >= 0; j--)
|
||||
{
|
||||
if (_dataList[j].InitialLevel <= level)
|
||||
{
|
||||
id = _dataList[j].ID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
void GetFriendData()
|
||||
{
|
||||
var FriendList = GContext.container.Resolve<FriendService>().FriendList;
|
||||
opponentFriendItemDatasPre.Clear();
|
||||
DateTime dateTime = ZZTimeHelper.UtcNow().Date.AddDays(-1);
|
||||
if (FriendList != null && FriendList.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < FriendList.Count; i++)
|
||||
{
|
||||
if (FriendList[i].playFabId == userService.UserId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
OpponentItemData opponentItemData = new OpponentItemData();
|
||||
opponentItemData.playFabID = FriendList[i].playFabId;
|
||||
opponentItemData.target_source = 1;
|
||||
|
||||
opponentFriendItemDatasPre.Add(opponentItemData);
|
||||
int level = FriendList[i].value / LeadboardData.LV_MODELING;
|
||||
|
||||
opponentItemData.BombID = GetConstructionID(level);
|
||||
opponentItemData.power = (float)Math.Log10(BottleCashParamBase + level / BottleCashParamLevel) / BottleCashParamDiv;
|
||||
powerData[opponentItemData.playFabID] = opponentItemData.power;
|
||||
//opponentItemDatasPre.Add(opponentItemData);
|
||||
opponentFriend.Add(opponentItemData);
|
||||
opponentFriendDic[opponentItemData.playFabID] = opponentItemData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async void SetEnemy(EnemyType enemyType, long glod)
|
||||
{
|
||||
if (!opponentData.isRobot && opponentData.playFabID.Length > 10)
|
||||
{
|
||||
SetEnemyIDRequest setEnemyCampIDRequest = new SetEnemyIDRequest()
|
||||
{
|
||||
enemyType = enemyType,
|
||||
playerID = userService.UserId,
|
||||
enemyID = opponentData.playFabID,
|
||||
content = glod.ToString()
|
||||
};
|
||||
var customServerMgr = GContext.container.Resolve<ICustomServerMgr>();
|
||||
string json = await customServerMgr.BombRequest("SetEnemyID", setEnemyCampIDRequest);
|
||||
if (!string.IsNullOrEmpty(json) && json != "[]")
|
||||
{
|
||||
SetEnemyIDResponse setCampIDResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<SetEnemyIDResponse>(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
//向服务器请求仇人列表
|
||||
async Task GetEnemyData(EnemyType enemyType)
|
||||
{
|
||||
GetEnemyIDRequest getEnemyCampIDRequest = new GetEnemyIDRequest()
|
||||
{
|
||||
playerID = userService.UserId,
|
||||
enemyType = enemyType,
|
||||
};
|
||||
var customServerMgr = GContext.container.Resolve<ICustomServerMgr>();
|
||||
string json = await customServerMgr.BombRequest("GetEnemyID", getEnemyCampIDRequest);
|
||||
if (!string.IsNullOrEmpty(json) && json != "[]")
|
||||
{
|
||||
GetEnemyIDResponse getCampIDResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<GetEnemyIDResponse>(json);
|
||||
if (getCampIDResponse.enemyIDs == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
List<EventPopupData> eventPopupDatas = GContext.container.Resolve<ClubService>().eventPopupDatas;
|
||||
//DateTime dateTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
//EventPopupData eventPopupData;
|
||||
int count;
|
||||
ReportDataType reportDataType = ReportDataType.Bomb;
|
||||
switch (enemyType)
|
||||
{
|
||||
case EnemyType.Bomb:
|
||||
reportDataType = ReportDataType.Bomb;
|
||||
break;
|
||||
case EnemyType.Heist:
|
||||
reportDataType = ReportDataType.Heist;
|
||||
break;
|
||||
case EnemyType.Aquarium:
|
||||
reportDataType = ReportDataType.Aquarium;
|
||||
break;
|
||||
}
|
||||
List<OpponentItemData> opponentItemDatas = new List<OpponentItemData>();
|
||||
opponentEnemy[enemyType] = opponentItemDatas;
|
||||
eventPopupDatas = eventPopupDatas.Where(x => (int)(x.type) == (int)reportDataType).ToList();
|
||||
for (int i = 0; i < getCampIDResponse.enemyIDs.Count; i++)
|
||||
{
|
||||
if (getCampIDResponse.enemyIDs[i] == userService.UserId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
OpponentItemData opponentItemData = new OpponentItemData();
|
||||
opponentItemData.playFabID = getCampIDResponse.enemyIDs[i];
|
||||
|
||||
if (getCampIDResponse.levels != null)
|
||||
{
|
||||
if (getCampIDResponse.levels.TryGetValue(opponentItemData.playFabID, out int level))
|
||||
{
|
||||
opponentItemData.BombID = GetConstructionID(level);
|
||||
}
|
||||
}
|
||||
|
||||
if (powerData.ContainsKey(opponentItemData.playFabID))
|
||||
{
|
||||
opponentItemData.power = powerData[opponentItemData.playFabID];
|
||||
}
|
||||
else
|
||||
{
|
||||
opponentItemData.power = 1;
|
||||
}
|
||||
count = eventPopupDatas.Where(x => x.playerID == opponentItemData.playFabID).Count();
|
||||
if (reportDataType == ReportDataType.Aquarium)
|
||||
{
|
||||
if (count > 1)
|
||||
{
|
||||
opponentItemData.info = LocalizationMgr.GetFormatTextValue("UI_EventBankHeistOpponentPopupPanel_3", count);
|
||||
}
|
||||
else
|
||||
{
|
||||
opponentItemData.info = LocalizationMgr.GetFormatTextValue("UI_EventBankHeistOpponentPopupPanel_5", 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (count > 1)
|
||||
{
|
||||
opponentItemData.info = LocalizationMgr.GetFormatTextValue("UI_EventBankHeistOpponentPopupPanel_6", count);
|
||||
}
|
||||
else
|
||||
{
|
||||
opponentItemData.info = LocalizationMgr.GetFormatTextValue("UI_EventBankHeistOpponentPopupPanel_7", 1);
|
||||
}
|
||||
}
|
||||
if (opponentFriendDic.TryGetValue(opponentItemData.playFabID, out OpponentItemData friendData))
|
||||
{
|
||||
friendData.info = opponentItemData.info;
|
||||
}
|
||||
opponentItemData.target_source = 4;
|
||||
|
||||
opponentItemDatas.Add(opponentItemData);
|
||||
//opponentNotFriendItemDatasPre.Add(opponentItemData);
|
||||
}
|
||||
}
|
||||
}
|
||||
void GetRobotData()
|
||||
{
|
||||
int count = opponentItemDatasPre.Count;
|
||||
int allCount = 20;
|
||||
if (count >= 20)
|
||||
{
|
||||
allCount = count + 1;
|
||||
}
|
||||
var robots = new List<cfg.Robot>(_tables.TbRobot.DataList);
|
||||
cfg.Robot robot;
|
||||
int myID = campData.Id;
|
||||
cfg.TbConstruction tbConstruction = _tables.TbConstruction;
|
||||
int constructionFirstId = tbConstruction.DataList[0].ID % 1000;
|
||||
int constructionLastId = tbConstruction.DataList[^1].ID;
|
||||
int campId;
|
||||
for (int i = count; i < allCount; i++)
|
||||
{
|
||||
robot = robots[UnityEngine.Random.Range(0, robots.Count)];
|
||||
robots.Remove(robot);
|
||||
OpponentItemData opponentItemData = new OpponentItemData();
|
||||
opponentItemData.playFabID = robot.ID.ToString("X");
|
||||
opponentItemData.target_source = -1;
|
||||
userService.SetPlayInfo(opponentItemData.playFabID, LocalizationMgr.GetText(robot.Name_l10n_key), robot.Avatar);
|
||||
//opponentItemData.playName = robot.Name;
|
||||
//opponentItemData.url = robot.Avatar;
|
||||
opponentItemData.isRobot = true;
|
||||
|
||||
campId = robot.ConstructionOffset + myID;
|
||||
if (campId < constructionFirstId)
|
||||
{
|
||||
campId = constructionFirstId;
|
||||
}
|
||||
if (campId > constructionLastId)
|
||||
{
|
||||
campId = constructionLastId;
|
||||
}
|
||||
opponentItemData.BombID = campId;
|
||||
opponentItemData.power = (float)Math.Log10(BottleCashParamBase - i) / BottleCashParamDiv;
|
||||
opponentItemDatasPre.Add(opponentItemData);
|
||||
}
|
||||
}
|
||||
async Task GetLeaderboardData(EnemyType enemyType)
|
||||
{
|
||||
FishingEventData _fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
if (!_fishingEventData.IsOpenRank())
|
||||
{
|
||||
return;
|
||||
}
|
||||
LeadboardData leadboardData = GContext.container.Resolve<LeadboardData>();
|
||||
if (leadboardData.curEventRank == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Dictionary<int, cfg.Robot> robots = GContext.container.Resolve<cfg.Tables>().TbRobot.DataMap;
|
||||
GetLeaderboardsResp curEventRank = leadboardData.curEventRank;
|
||||
int myID = campData.Id;
|
||||
cfg.TbConstruction tbConstruction = _tables.TbConstruction;
|
||||
int constructionFirstId = tbConstruction.DataList[0].ID % 1000;
|
||||
int constructionLastId = tbConstruction.DataList[^1].ID;
|
||||
int campId = myID;
|
||||
Dictionary<string, OpponentItemData> playerList = new Dictionary<string, OpponentItemData>();
|
||||
for (int i = 0; i < curEventRank.data.Length; i++)
|
||||
{
|
||||
if (curEventRank.data[i].isMe || curEventRank.data[i].id == userService.UserId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (curEventRank.data[i].id == null || powerData.ContainsKey(curEventRank.data[i].id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
OpponentItemData opponentItemData = new OpponentItemData();
|
||||
opponentItemData.playFabID = curEventRank.data[i].id;
|
||||
if (curEventRank.data[i].isBot)
|
||||
{
|
||||
int b = Convert.ToInt32(opponentItemData.playFabID, 16);
|
||||
if (robots.TryGetValue(b, out cfg.Robot robot))
|
||||
{
|
||||
campId = robot.ConstructionOffset + myID;
|
||||
}
|
||||
if (campId < constructionFirstId)
|
||||
{
|
||||
campId = constructionFirstId;
|
||||
}
|
||||
if (campId > constructionLastId)
|
||||
{
|
||||
campId = constructionLastId;
|
||||
}
|
||||
opponentItemData.isRobot = true;
|
||||
opponentItemData.target_source = -1;
|
||||
opponentItemDatasPre.Add(opponentItemData);
|
||||
}
|
||||
else
|
||||
{
|
||||
playerList[opponentItemData.playFabID] = opponentItemData;
|
||||
opponentItemData.target_source = 3;
|
||||
userService.GetPlayInfo(opponentItemData.playFabID);
|
||||
opponentNotFriendItemDatasPre.Add(opponentItemData);
|
||||
}
|
||||
opponentItemData.BombID = campId;
|
||||
opponentItemData.power = (float)Math.Log10(BottleCashParamBase - curEventRank.data[i].rank) / BottleCashParamDiv;
|
||||
powerData[opponentItemData.playFabID] = opponentItemData.power;
|
||||
}
|
||||
if (playerList.Count > 0 /*&& enemyType == EnemyType.Bomb*/)
|
||||
{
|
||||
await GetCampID(playerList);
|
||||
}
|
||||
}
|
||||
async Task GetCampID(Dictionary<string, OpponentItemData> playerList)
|
||||
{
|
||||
List<string> playFabIds = playerList.Values.Select(x => x.playFabID).ToList();
|
||||
|
||||
GetCampIDRequest getCampIDRequest = new GetCampIDRequest()
|
||||
{
|
||||
playerIDs = playFabIds
|
||||
};
|
||||
var customServerMgr = GContext.container.Resolve<ICustomServerMgr>();
|
||||
string json = await customServerMgr.BombRequest("GetCampID", getCampIDRequest);
|
||||
if (!string.IsNullOrEmpty(json) && json != "[]")
|
||||
{
|
||||
GetCampIDResponse getCampIDResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<GetCampIDResponse>(json);
|
||||
if (getCampIDResponse != null && getCampIDResponse.levels != null && getCampIDResponse.levels.Count > 0)
|
||||
{
|
||||
foreach (var item in getCampIDResponse.levels)
|
||||
{
|
||||
playerList[item.Key].BombID = GetConstructionID(item.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,657 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using LitJson;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GameCore
|
||||
{
|
||||
public class TriggerPackBuyData
|
||||
{
|
||||
public int ID;
|
||||
//领取第几个
|
||||
public int index;
|
||||
//礼包类型
|
||||
public int lastID;
|
||||
//结束时间
|
||||
public string time;
|
||||
//触发当时VIP等级
|
||||
public int vipLevel;
|
||||
//触发当时地图ID
|
||||
public int mapID;
|
||||
}
|
||||
public class GiftVIPDData
|
||||
{
|
||||
public int oldVipLevel;
|
||||
public int curVipLevel;
|
||||
//记录数据用于明天时间(天)
|
||||
public int timer;
|
||||
}
|
||||
public class TriggerPackEvent
|
||||
{
|
||||
|
||||
}
|
||||
public class TriggerPackData
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore SaveStore { get; set; }
|
||||
|
||||
Tables _tables;
|
||||
PlayerShopData playerShopData;
|
||||
public TriggerPackData(Tables tables)
|
||||
{
|
||||
_tables = tables;
|
||||
}
|
||||
//固定时机开启的过期时间
|
||||
Dictionary<int, FixedTimeEvent> fixedTimeShopDic = new Dictionary<int, FixedTimeEvent>();
|
||||
|
||||
//触发礼包的数据
|
||||
public List<TriggerPackBuyData> triggerPackList = new List<TriggerPackBuyData>();
|
||||
|
||||
//VIP 等级叠加
|
||||
public Dictionary<string, GiftVIPDData> giftVIPDData = new Dictionary<string, GiftVIPDData>();
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVTriggerState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) => game.PlayerDataStorage.PlayerKVSaveSchema.DecodeTriggers(raw,errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
triggerPackList=new List<TriggerPackBuyData>();
|
||||
giftVIPDData=new Dictionary<string,GiftVIPDData>(StringComparer.Ordinal);
|
||||
fixedTimeShopDic=new Dictionary<int,FixedTimeEvent>();
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVTriggerState state)
|
||||
{
|
||||
if(state==null) throw new ArgumentNullException(nameof(state));
|
||||
var packs=state.Packs.Select(p=>new TriggerPackBuyData {
|
||||
ID=p.ID,index=p.index,lastID=p.lastID,vipLevel=p.vipLevel,mapID=p.mapID,time=p.time }).ToList();
|
||||
var gifts=state.Gifts.ToDictionary(p=>p.Key,p=>new GiftVIPDData {
|
||||
oldVipLevel=p.Value.oldVipLevel,curVipLevel=p.Value.curVipLevel,timer=p.Value.timer },StringComparer.Ordinal);
|
||||
var fixedValues=state.Fixed.ToDictionary(p=>p.Key,p=>new FixedTimeEvent { id=p.Value.id,endTime=p.Value.endTime });
|
||||
triggerPackList=packs; giftVIPDData=gifts; fixedTimeShopDic=fixedValues;
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
internal FixedTimeEvent FixedForKVValidation(int key) => fixedTimeShopDic.TryGetValue(key,out var value) ? value : null;
|
||||
internal bool IsEmptyForKVValidation => triggerPackList.Count==0 && giftVIPDData.Count==0 && fixedTimeShopDic.Count==0;
|
||||
#endif
|
||||
public void Init()
|
||||
{
|
||||
playerShopData = GContext.container.Resolve<PlayerShopData>();
|
||||
GContext.OnEvent<ConditionTypeEvent>().Subscribe(UpdateTriggerPackData);
|
||||
InitPackData();
|
||||
}
|
||||
#region 礼包限制、开启、初始化
|
||||
public void SetFixedTimeShopDic(Dictionary<int, FixedTimeEvent> _limitedTimeShopDic)
|
||||
{
|
||||
fixedTimeShopDic = _limitedTimeShopDic;
|
||||
}
|
||||
public bool FixedTimeEventExpired(int eventId)
|
||||
{
|
||||
if (fixedTimeShopDic.TryGetValue(eventId, out FixedTimeEvent fixedTimeEvent))
|
||||
{
|
||||
var endTime = GlobalUtils.TryParseDateTime(fixedTimeEvent.endTime, ZZTimeHelper.UtcNow().UtcNowOffset());
|
||||
return endTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public void InitPackData()
|
||||
{
|
||||
InitGiftVIPDData();
|
||||
//过期礼包删除
|
||||
DateTime curTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
for (int i = triggerPackList.Count - 1; i >= 0; i--)
|
||||
{
|
||||
TriggerPackBuyData item = triggerPackList[i];
|
||||
DateTime endTime = GlobalUtils.TryParseDateTime(item.time, curTime);
|
||||
if (endTime.Ticks <= curTime.Ticks)
|
||||
{
|
||||
triggerPackList.Remove(item);
|
||||
}
|
||||
}
|
||||
List<TriggerPackManager> _dataList = _tables.TbTriggerPackManager.DataList;
|
||||
foreach (var t in _dataList)
|
||||
{
|
||||
if (t.PackType == 2)
|
||||
{
|
||||
ConditionTypeAdd(t);
|
||||
}
|
||||
}
|
||||
triggerPackList.Sort((a, b) => a.lastID.CompareTo(b.lastID));
|
||||
}
|
||||
bool isInitFace;
|
||||
public void SetInitFace()
|
||||
{
|
||||
if (isInitFace)
|
||||
{
|
||||
return;
|
||||
}
|
||||
isInitFace = true;
|
||||
|
||||
for (int i = 0; i < triggerPackList.Count; i++)
|
||||
{
|
||||
if (playerShopData.GetShopPackBuyCount(triggerPackList[i].lastID) == 0 && triggerPackList[i].lastID != 4)
|
||||
{
|
||||
playerShopData.AddShopPackDailyBuyCount(triggerPackList[i].lastID, 1, false);
|
||||
}
|
||||
if (triggerPackList[i].mapID <= 0)
|
||||
{
|
||||
triggerPackList[i].mapID = GContext.container.Resolve<PlayerData>().currentMapId;
|
||||
}
|
||||
if (triggerPackList[i].lastID == 53)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(triggerPackList[i].ID, UITypes.MapPackPanel, triggerPackList[i].lastID);
|
||||
}
|
||||
else if (triggerPackList[i].lastID == 2)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(triggerPackList[i].ID, UITypes.CommerceNoviceGiftPopupPanel, triggerPackList[i].lastID);
|
||||
}
|
||||
else if (triggerPackList[i].lastID == 1)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(triggerPackList[i].ID, UITypes.GiftPopupPanel_6, triggerPackList[i].lastID);
|
||||
}
|
||||
else if (triggerPackList[i].lastID == 5)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(triggerPackList[i].ID, UITypes.AdsPackPanel, triggerPackList[i].lastID);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public void UpdateTriggerPackData(ConditionTypeEvent typeData)
|
||||
{
|
||||
ConditionType type = typeData.type;
|
||||
List<TriggerPackManager> _dataList = _tables.TbTriggerPackManager.DataList;
|
||||
foreach (var t in _dataList)
|
||||
{
|
||||
if (t.Condition == type)
|
||||
{
|
||||
if (ConditionTypeAdd(t, true, typeData.count))
|
||||
{
|
||||
if (t.PackType == 53)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(t.ID, UITypes.MapPackPanel, t.PackType);
|
||||
GContext.Publish(new TriggerPackEvent());
|
||||
}
|
||||
else if (t.PackType == 2)
|
||||
{
|
||||
//SetPack2Panel(t);
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(t.ID, UITypes.CommerceNoviceGiftPopupPanel, t.PackType);
|
||||
GContext.Publish(new TriggerPackEvent());
|
||||
}
|
||||
else if (t.PackType == 1)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(t.ID, UITypes.GiftPopupPanel_6, t.PackType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 钓鱼失败 看完广告 触发礼包
|
||||
/// </summary>
|
||||
public void TriggerAdsPack()
|
||||
{
|
||||
if (!playerShopData.IsOpenAdPack() || playerShopData.GetAdticketCount() >= 5 ||
|
||||
GContext.container.Resolve<PlayerData>().vip < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TriggerPackManager t = _tables.TbTriggerPackManager.GetOrDefault(4000101);
|
||||
if (t == null || t.PackType != 5)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//特殊的 广告礼包 使用 fixedTimeShopDic 记录CD时间
|
||||
DateTime dateTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
|
||||
if (!FixedTimeEventExpired(t.ID))
|
||||
{
|
||||
fixedTimeShopDic.Remove(t.ID);
|
||||
}
|
||||
if (!fixedTimeShopDic.ContainsKey(t.ID))
|
||||
{
|
||||
FixedTime fixedTime = (FixedTime)t.TimeDefinition;
|
||||
//广告礼包记录的是 cd时间
|
||||
fixedTimeShopDic[t.ID] = new FixedTimeEvent()
|
||||
{
|
||||
id = t.ID,
|
||||
endTime = dateTime.AddSeconds(fixedTime.EndTime).ToString(),
|
||||
};
|
||||
SaveStore.Write("FixedTimeShopDic", JsonMapper.ToJson(fixedTimeShopDic)).RequireAccepted();
|
||||
|
||||
TriggerPackBuyData _newSignEvent = null;
|
||||
for (int i = 0; i < triggerPackList.Count; i++)
|
||||
{
|
||||
if (triggerPackList[i].ID == t.ID)
|
||||
{
|
||||
_newSignEvent = triggerPackList[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
string time = dateTime.AddSeconds(fixedTime.StartTime).ToString();
|
||||
if (_newSignEvent == null)
|
||||
{
|
||||
_newSignEvent = new TriggerPackBuyData()
|
||||
{
|
||||
ID = t.ID,
|
||||
lastID = t.PackType,
|
||||
index = 0,
|
||||
time = time,
|
||||
vipLevel = GetVIPLevel(t.PackType),
|
||||
mapID = GContext.container.Resolve<PlayerData>().lastMapId,
|
||||
};
|
||||
triggerPackList.Insert(0, _newSignEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
_newSignEvent.time = time;
|
||||
_newSignEvent.vipLevel = GetVIPLevel(t.PackType);
|
||||
}
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(_newSignEvent.ID, UITypes.AdsPackPanel, _newSignEvent.lastID);
|
||||
SaveStore.Write("TriggerPackDataList", JsonMapper.ToJson(triggerPackList)).RequireAccepted();
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshTriggerPackData(int id, int mapID)
|
||||
{
|
||||
TriggerPackManager t = _tables.TbTriggerPackManager.GetOrDefault(id);
|
||||
bool isOpen = playerShopData.GetShopPackBuyCount(t.PackType) < t.MaxTrigger;
|
||||
|
||||
if (isOpen)
|
||||
{
|
||||
DateTime dateTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
TriggerPackBuyData _newSignEvent = null;
|
||||
for (int i = 0; i < triggerPackList.Count; i++)
|
||||
{
|
||||
if (triggerPackList[i].ID == t.ID)
|
||||
{
|
||||
_newSignEvent = triggerPackList[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_newSignEvent != null)
|
||||
{
|
||||
triggerPackList.Remove(_newSignEvent);
|
||||
}
|
||||
string time = dateTime.AddDays(10).ToString();
|
||||
if (t.TimeDefinition is FixedTime)
|
||||
{
|
||||
FixedTime fixedTime = (FixedTime)t.TimeDefinition;
|
||||
time = ZZTimeHelper.UtcNow().UtcNowOffset().AddSeconds(fixedTime.StartTime).ToString();
|
||||
FixedTimeEvent fixedTimeEvent = new FixedTimeEvent()
|
||||
{
|
||||
id = t.ID,
|
||||
endTime = time,
|
||||
};
|
||||
fixedTimeShopDic[t.ID] = fixedTimeEvent;
|
||||
SaveStore.Write("FixedTimeShopDic", JsonMapper.ToJson(fixedTimeShopDic)).RequireAccepted();
|
||||
}
|
||||
else if (t.TimeDefinition is LimitedTime)
|
||||
{
|
||||
LimitedTime limitedTime = (LimitedTime)t.TimeDefinition;
|
||||
time = GlobalUtils.TryParseDateTime(limitedTime.EndTime, ZZTimeHelper.UtcNow()).UtcNowOffset().ToString();
|
||||
}
|
||||
|
||||
_newSignEvent = new TriggerPackBuyData()
|
||||
{
|
||||
ID = t.ID,
|
||||
lastID = t.PackType,
|
||||
index = 0,
|
||||
time = time,
|
||||
vipLevel = GetVIPLevel(t.PackType),
|
||||
mapID = mapID,
|
||||
};
|
||||
triggerPackList.Insert(0, _newSignEvent);
|
||||
playerShopData.AddShopPackDailyBuyCount(t.PackType, 1, false);
|
||||
SaveStore.Write("TriggerPackDataList", JsonMapper.ToJson(triggerPackList)).RequireAccepted();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 特殊触发礼包
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
public void AddTriggerPackData(int id)
|
||||
{
|
||||
TriggerPackManager t = _tables.TbTriggerPackManager.GetOrDefault(id);
|
||||
|
||||
bool isOpen = t.MaxTrigger == 0 || ((t.PackType == 1 || t.PackType == 2) && GContext.container.Resolve<PlayerShopData>().GetAllPackCount(t.ID) < t.MaxTrigger)//类型为1.2新手礼包
|
||||
|| (t.PackType != 1 && t.PackType != 2 && playerShopData.GetShopPackBuyCount(t.PackType) < t.MaxTrigger);//每日限购礼包
|
||||
|
||||
if (isOpen)
|
||||
{
|
||||
if (t.TimeDefinition is FixedTime)
|
||||
{
|
||||
if (t.PackType == 53)
|
||||
{
|
||||
fixedTimeShopDic.Remove(t.ID);
|
||||
}
|
||||
if (!fixedTimeShopDic.ContainsKey(t.ID))
|
||||
{
|
||||
FixedTime fixedTime = (FixedTime)t.TimeDefinition;
|
||||
fixedTimeShopDic[t.ID] = new FixedTimeEvent()
|
||||
{
|
||||
id = t.ID,
|
||||
endTime = ZZTimeHelper.UtcNow().UtcNowOffset().AddSeconds(fixedTime.StartTime).ToString(),
|
||||
};
|
||||
|
||||
SaveStore.Write("FixedTimeShopDic", JsonMapper.ToJson(fixedTimeShopDic)).RequireAccepted();
|
||||
}
|
||||
if (FixedTimeEventExpired(t.ID))
|
||||
{
|
||||
AddTriggerPack(t);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddTriggerPack(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool ConditionTypeAdd(TriggerPackManager t, bool trigger = false, int Param = 0)
|
||||
{
|
||||
bool isOpen = false;
|
||||
if (Condition(t, false, Param))
|
||||
{
|
||||
if (t.TimeDefinition is FixedTime)
|
||||
{
|
||||
if (trigger && (t.PackType == 53))
|
||||
{
|
||||
fixedTimeShopDic.Remove(t.ID);
|
||||
}
|
||||
if (!fixedTimeShopDic.ContainsKey(t.ID))
|
||||
{
|
||||
FixedTime fixedTime = (FixedTime)t.TimeDefinition;
|
||||
fixedTimeShopDic[t.ID] = new FixedTimeEvent()
|
||||
{
|
||||
id = t.ID,
|
||||
endTime = ZZTimeHelper.UtcNow().UtcNowOffset().AddSeconds(fixedTime.StartTime).ToString(),
|
||||
};
|
||||
|
||||
SaveStore.Write("FixedTimeShopDic", JsonMapper.ToJson(fixedTimeShopDic)).RequireAccepted();
|
||||
}
|
||||
if (FixedTimeEventExpired(t.ID))
|
||||
{
|
||||
AddTriggerPack(t);
|
||||
isOpen = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddTriggerPack(t);
|
||||
isOpen = true;
|
||||
}
|
||||
}
|
||||
return isOpen;
|
||||
}
|
||||
bool Condition(TriggerPackManager packManager, bool isAdd, int Param)
|
||||
{
|
||||
if (packManager.PackType == 5)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool isOpen = packManager.MaxTrigger == 0 || ((packManager.PackType == 1 || packManager.PackType == 2) && GContext.container.Resolve<PlayerShopData>().GetAllPackCount(packManager.ID) < packManager.MaxTrigger)//类型为1.2新手礼包
|
||||
|| (packManager.PackType != 1 && packManager.PackType != 2 && playerShopData.GetShopPackBuyCount(packManager.PackType) < packManager.MaxTrigger);//每日限购礼包
|
||||
if (!isOpen)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (packManager.TimeDefinition is LimitedTime)
|
||||
{
|
||||
LimitedTime limitedTime = (LimitedTime)packManager.TimeDefinition;
|
||||
var timer = ZZTimeHelper.UtcNow();
|
||||
isOpen = GlobalUtils.TryParseDateTime(limitedTime.StartTime, timer) <= timer && timer < GlobalUtils.TryParseDateTime(limitedTime.EndTime, timer);
|
||||
}
|
||||
|
||||
switch (packManager.Condition)
|
||||
{
|
||||
case ConditionType.AccountLevel:
|
||||
if (isAdd)
|
||||
{
|
||||
isOpen &= GContext.container.Resolve<PlayerData>().lv >= int.Parse(packManager.Param[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
isOpen &= GContext.container.Resolve<PlayerData>().lv == int.Parse(packManager.Param[0]);
|
||||
}
|
||||
break;
|
||||
case ConditionType.GetFishCount:
|
||||
if (isAdd)
|
||||
{
|
||||
isOpen &= GContext.container.Resolve<PlayerFishData>().GetAnglingCount() >= int.Parse(packManager.Param[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
isOpen &= GContext.container.Resolve<PlayerFishData>().GetAnglingCount() == int.Parse(packManager.Param[0]);
|
||||
}
|
||||
break;
|
||||
case ConditionType.CompleteGuide:
|
||||
isOpen &= GContext.container.Resolve<GuideDataCenter>().GetIsFinish(packManager.Param[0]);
|
||||
break;
|
||||
case ConditionType.BuyPack:
|
||||
isOpen &= playerShopData.shopAllPackBuyCount.GetValueOrDefault(int.Parse(packManager.Param[0])) > 0;
|
||||
break;
|
||||
case ConditionType.MapUnlocked:
|
||||
//isOpen &= GContext.container.Resolve<PlayerData>().MapIsUnlock(packManager.Param);
|
||||
isOpen &= packManager.Param.Contains(Param.ToString());
|
||||
break;
|
||||
}
|
||||
// if ( packManager.PackType == 2 )
|
||||
// Debug.Log("??" + packManager.Condition+GContext.container.Resolve<PlayerData>().lv+int.Parse(packManager.Param[0])+ (GContext.container.Resolve<PlayerData>().lv >= int.Parse(packManager.Param[0])) + " " +isOpen);
|
||||
return isOpen;
|
||||
}
|
||||
#endregion
|
||||
void InitGiftVIPDData()
|
||||
{
|
||||
if (giftVIPDData.Count > 0)
|
||||
{
|
||||
var timer = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
int dateTime = timer.AddDays(1).DayOfYear;
|
||||
bool isUpdate = false;
|
||||
foreach (var item in giftVIPDData)
|
||||
{
|
||||
GiftVIPDData giftVIPDData = item.Value;
|
||||
if (giftVIPDData.timer != dateTime)
|
||||
{
|
||||
isUpdate = true;
|
||||
if (giftVIPDData.timer == timer.DayOfYear)
|
||||
{
|
||||
giftVIPDData.oldVipLevel = giftVIPDData.curVipLevel;
|
||||
giftVIPDData.curVipLevel = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
giftVIPDData.oldVipLevel = 0;
|
||||
giftVIPDData.curVipLevel = 0;
|
||||
}
|
||||
giftVIPDData.timer = dateTime;
|
||||
}
|
||||
}
|
||||
if (isUpdate)
|
||||
{
|
||||
SaveStore.Write("GiftVIPDData", JsonMapper.ToJson(giftVIPDData)).RequireAccepted();
|
||||
}
|
||||
}
|
||||
}
|
||||
public int GetVIPLevel(int packType)
|
||||
{
|
||||
string PackType = packType.ToString();
|
||||
return GetVIPLevel(PackType);
|
||||
}
|
||||
public void SetVIPLevel(int packType, int VIPBonusForNextType)
|
||||
{
|
||||
string PackType = packType.ToString();
|
||||
SetVIPLevel(PackType, VIPBonusForNextType);
|
||||
}
|
||||
public int GetVIPLevel(string type)
|
||||
{
|
||||
int vipLevel = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
if (giftVIPDData.TryGetValue(type, out GiftVIPDData giftVIPD))
|
||||
{
|
||||
vipLevel += giftVIPD.oldVipLevel;
|
||||
}
|
||||
if (vipLevel >= _tables.TbVipLevel.DataList.Count)
|
||||
{
|
||||
vipLevel = _tables.TbVipLevel.DataList.Count - 1;
|
||||
}
|
||||
return vipLevel;
|
||||
}
|
||||
public void SetVIPLevel(string PackType, int VIPBonusForNextType)
|
||||
{
|
||||
if (VIPBonusForNextType > 0)
|
||||
{
|
||||
if (giftVIPDData.TryGetValue(PackType, out GiftVIPDData giftVIPD))
|
||||
{
|
||||
giftVIPD.curVipLevel += VIPBonusForNextType;
|
||||
}
|
||||
else
|
||||
{
|
||||
giftVIPD = new GiftVIPDData()
|
||||
{
|
||||
curVipLevel = VIPBonusForNextType,
|
||||
timer = ZZTimeHelper.UtcNow().UtcNowOffset().AddDays(1).DayOfYear,
|
||||
};
|
||||
}
|
||||
giftVIPDData[PackType] = giftVIPD;
|
||||
SaveStore.Write("GiftVIPDData", JsonMapper.ToJson(giftVIPDData)).RequireAccepted();
|
||||
}
|
||||
}
|
||||
#region 触发礼包
|
||||
public void AddTriggerPack(TriggerPackManager t)
|
||||
{
|
||||
DateTime dateTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
TriggerPackBuyData _newSignEvent = null;
|
||||
//if (t.PackType != 53)
|
||||
//{
|
||||
for (int i = 0; i < triggerPackList.Count; i++)
|
||||
{
|
||||
if (triggerPackList[i].ID == t.ID)
|
||||
{
|
||||
_newSignEvent = triggerPackList[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
//}
|
||||
if (_newSignEvent != null &&
|
||||
(GlobalUtils.TryParseDateTime(_newSignEvent.time, ZZTimeHelper.UtcNow()).Ticks < dateTime.Ticks))
|
||||
{
|
||||
triggerPackList.Remove(_newSignEvent);
|
||||
_newSignEvent = null;
|
||||
}
|
||||
if (_newSignEvent == null)
|
||||
{
|
||||
string time = dateTime.AddDays(10).ToString();
|
||||
if (t.TimeDefinition is FixedTime)
|
||||
{
|
||||
if (fixedTimeShopDic.TryGetValue(t.ID, out FixedTimeEvent fixedTimeEvent))
|
||||
{
|
||||
time = fixedTimeEvent.endTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
FixedTime fixedTime = (FixedTime)t.TimeDefinition;
|
||||
time = dateTime.AddSeconds(fixedTime.StartTime).ToString();
|
||||
}
|
||||
}
|
||||
else if (t.TimeDefinition is LimitedTime)
|
||||
{
|
||||
LimitedTime limitedTime = (LimitedTime)t.TimeDefinition;
|
||||
time = GlobalUtils.TryParseDateTime(limitedTime.EndTime, ZZTimeHelper.UtcNow()).UtcNowOffset().ToString();
|
||||
}
|
||||
|
||||
_newSignEvent = new TriggerPackBuyData()
|
||||
{
|
||||
ID = t.ID,
|
||||
lastID = t.PackType,
|
||||
index = 0,
|
||||
time = time,
|
||||
vipLevel = GetVIPLevel(t.PackType),
|
||||
mapID = GContext.container.Resolve<PlayerData>().lastMapId,
|
||||
};
|
||||
triggerPackList.Insert(0, _newSignEvent);
|
||||
playerShopData.AddShopPackDailyBuyCount(t.PackType, 1, false);
|
||||
SaveStore.Write("TriggerPackDataList", JsonMapper.ToJson(triggerPackList)).RequireAccepted();
|
||||
}
|
||||
}
|
||||
public void AddTriggerPackCount(int id)
|
||||
{
|
||||
for (int i = 0; i < triggerPackList.Count; i++)
|
||||
{
|
||||
if (triggerPackList[i].ID == id)
|
||||
{
|
||||
if (++triggerPackList[i].index >= _tables.TbTriggerPackManager.Get(triggerPackList[i].ID).VIPPackList[GetVIPLevel(triggerPackList[i].lastID)].Count)
|
||||
{
|
||||
triggerPackList.Remove(triggerPackList[i]);
|
||||
}
|
||||
SaveStore.Write("TriggerPackDataList", JsonMapper.ToJson(triggerPackList)).RequireAccepted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
playerShopData.AddAllPackCount(id, 1);
|
||||
TriggerPackManager packManager = _tables.TbTriggerPackManager.GetOrDefault(id);
|
||||
GContext.Publish(new TriggerPackEvent());
|
||||
if (packManager.VIPBonus > 0)
|
||||
{
|
||||
SetVIPLevel(packManager.PackType, packManager.VIPBonus);
|
||||
}
|
||||
}
|
||||
public List<TriggerPackBuyData> GetTriggerPackList(int type)
|
||||
{
|
||||
List<TriggerPackBuyData> newTriggerPackList = new List<TriggerPackBuyData>();
|
||||
for (int i = 0; i < triggerPackList.Count; i++)
|
||||
{
|
||||
if (triggerPackList[i].lastID == type && _tables.TbTriggerPackManager.GetOrDefault(triggerPackList[i].ID) != null)
|
||||
{
|
||||
newTriggerPackList.Add(triggerPackList[i]);
|
||||
}
|
||||
}
|
||||
newTriggerPackList.Sort((a, b) => a.ID.CompareTo(b.ID));
|
||||
return newTriggerPackList;
|
||||
}
|
||||
public void ShowTriggerPack(int type, int id)
|
||||
{
|
||||
for (int i = 0; i < triggerPackList.Count; i++)
|
||||
{
|
||||
if (triggerPackList[i].lastID == type && triggerPackList[i].ID == id)
|
||||
{
|
||||
var t = _tables.TbTriggerPackManager.GetOrDefault(id);
|
||||
if (t.PackType == 53)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(t.ID, UITypes.MapPackPanel, t.PackType);
|
||||
}
|
||||
else if (t.PackType == 2)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(t.ID, UITypes.CommerceNoviceGiftPopupPanel, t.PackType);
|
||||
}
|
||||
else if (t.PackType == 1)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(t.ID, UITypes.GiftPopupPanel_6, t.PackType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public TimeSpan GetCurTriggerPackEndTime(TriggerPackBuyData dailySignEvent)
|
||||
{
|
||||
DateTime curTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
DateTime endTime = GlobalUtils.TryParseDateTime(dailySignEvent.time, curTime);
|
||||
if (endTime.Ticks <= curTime.Ticks)
|
||||
{
|
||||
triggerPackList.Remove(dailySignEvent);
|
||||
GContext.Publish(new TriggerPackEvent());
|
||||
SaveStore.Write("TriggerPackDataList", JsonMapper.ToJson(triggerPackList)).RequireAccepted();
|
||||
}
|
||||
return endTime - curTime;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public TriggerPackBuyData GetTriggerPackBuyDataById(int packId)
|
||||
{
|
||||
return triggerPackList.FirstOrDefault(x => x.ID == packId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
using asap.core;
|
||||
using GameCore;
|
||||
using cfg;
|
||||
public class EventPackData
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore playerSaveStore { get; set; }
|
||||
private Tables _tables = GContext.container.Resolve<Tables>();
|
||||
private struct Data
|
||||
{
|
||||
public int currentEventID;
|
||||
public int purchaseCount;
|
||||
public int VIPLvlWhenEventActivate;
|
||||
public int redirectID;
|
||||
public Data(int eventID = 0, int p = 0, int v = 0, int rid = 0)
|
||||
{
|
||||
currentEventID = eventID;
|
||||
purchaseCount = p;
|
||||
VIPLvlWhenEventActivate = v;
|
||||
redirectID = rid;
|
||||
}
|
||||
}
|
||||
private Data _data;
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVEventPackState DecodeForKVLoad(System.Collections.Generic.IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodeEventPack(raw,snapshot,errors);
|
||||
internal void ResetForKVLoad() { _data=default(Data); }
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVEventPackState state)
|
||||
{
|
||||
if (state == null) throw new System.ArgumentNullException(nameof(state));
|
||||
_data=new Data { currentEventID=state.EventId,purchaseCount=state.PurchaseCount,
|
||||
VIPLvlWhenEventActivate=state.VipLevel,redirectID=state.RedirectId };
|
||||
}
|
||||
internal game.PlayerDataStorage.PlayerKVEventPackState SnapshotForKVValidation() =>
|
||||
new game.PlayerDataStorage.PlayerKVEventPackState(_data.currentEventID,_data.purchaseCount,_data.VIPLvlWhenEventActivate,_data.redirectID);
|
||||
public int CurrentEventID { get { return _data.currentEventID; } }
|
||||
public int PurchaseCount { get { return _data.purchaseCount; } }
|
||||
public int VIPLvlWhenEventActivated { get { return _data.VIPLvlWhenEventActivate; } }
|
||||
|
||||
public bool IsActive => _tables.TbFishingEvent.DataMap.ContainsKey(_data.currentEventID) &&
|
||||
(System.DateTime.Parse(
|
||||
(_tables.TbFishingEvent[_data.currentEventID].TimeDefinition as LimitedTime)
|
||||
?.EndTime) -
|
||||
ZZTimeHelper.UtcNow()).TotalSeconds > 0 &&
|
||||
PurchaseCount < _tables.TbEventPackManager[_data.redirectID].MaxCount;
|
||||
public EventPackData()
|
||||
{
|
||||
//Debug.Log("<color=red>construct</color>");
|
||||
_data = new Data();
|
||||
//PlayFabMgr.Instance.UpdateUserDataValue("EventPackData",
|
||||
// Newtonsoft.Json.JsonConvert.SerializeObject(_data));
|
||||
}
|
||||
|
||||
public void AddPurchaceCount()
|
||||
{
|
||||
_data.purchaseCount++;
|
||||
SaveEventPackData();
|
||||
}
|
||||
/// <summary>
|
||||
/// Refresh and update EventPackData if current event is not the event given. Will reset current
|
||||
/// event if the event given is null
|
||||
/// </summary>
|
||||
/// <param name="e">New event</param>
|
||||
public bool UpdateEventData(cfg.FishingEvent e = null)
|
||||
{
|
||||
bool isUpdated = false;
|
||||
if (e == null)
|
||||
{
|
||||
_data = new Data();
|
||||
isUpdated = true;
|
||||
}
|
||||
else if (_data.currentEventID != e.ID)
|
||||
{
|
||||
_data = new Data(e.ID, 0, GContext.container.Resolve<PlayerData>().PriceLv, e.RedirectID);
|
||||
isUpdated = true;
|
||||
}
|
||||
//else
|
||||
// do nothing
|
||||
SaveEventPackData();
|
||||
return isUpdated;
|
||||
}
|
||||
public void LoadEventPackData(string s)
|
||||
{
|
||||
//Debug.Log("<color=red>load epd</color>");
|
||||
_data = Newtonsoft.Json.JsonConvert.DeserializeObject<Data>(s);
|
||||
//Debug.Log($"load: {_data.currentEventID}");
|
||||
if (!_tables.TbFishingEvent.DataMap.ContainsKey(_data.currentEventID) ||
|
||||
(System.DateTime.Parse((_tables.TbFishingEvent[_data.currentEventID].TimeDefinition
|
||||
as LimitedTime).EndTime) - ZZTimeHelper.UtcNow()).TotalSeconds <= 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_tables.TbEventPackManager[_tables.TbFishingEvent[_data.currentEventID].RedirectID].PackType != 1)
|
||||
{
|
||||
_data = new Data();
|
||||
return;
|
||||
}
|
||||
if (_data.redirectID == 0)
|
||||
_data.redirectID = 4054001;
|
||||
TriggerEventPack();
|
||||
}
|
||||
public void SaveEventPackData()
|
||||
{
|
||||
playerSaveStore.Write("EventPackData",
|
||||
Newtonsoft.Json.JsonConvert.SerializeObject(_data)).RequireAccepted();
|
||||
}
|
||||
private void TriggerEventPack()
|
||||
{
|
||||
var tables = GContext.container.Resolve<Tables>();
|
||||
FishingEvent e = tables.TbFishingEvent.DataMap[_data.currentEventID];
|
||||
int packID = _data.redirectID;
|
||||
var packData = tables.TbEventPackManager.DataMap[packID];//pack data read from tables
|
||||
// if (_data.purchaseCount < packData.MaxCount && packData.PackType == 1)
|
||||
//TODO:change panel according to RedirecctID of current event
|
||||
// GContext.container.Resolve<IFaceUIService>().
|
||||
// AddGiftFaceUI(e.ID, UITypes.GiftPopupPanel_9, packData.PackType, true);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,993 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public class EventPinballDataManager
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore playerSaveStore { get; set; }
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private bool m_IsCheckCondition = true;
|
||||
private bool m_IsSyncData = true;
|
||||
private int m_DefaultToken = 100;
|
||||
#else
|
||||
private bool m_IsCheckCondition = true;
|
||||
private bool m_IsSyncData = true;
|
||||
private int m_DefaultToken = 0;
|
||||
#endif
|
||||
|
||||
#region 奖励次数相关
|
||||
private int m_BonusResponseNumber = 10; // 相应次数
|
||||
public int BonusResponseNumber { set => m_BonusResponseNumber = value; }
|
||||
private int m_CumulativeResponseNumber = 0;
|
||||
public int CumulativeResponseNumber
|
||||
{
|
||||
get => m_CumulativeResponseNumber;
|
||||
set
|
||||
{
|
||||
m_CumulativeResponseNumber = value;
|
||||
FishingPinballAct.Publish(new EventPinballUpdateBonusCircularProgress());
|
||||
}
|
||||
}
|
||||
public bool IsAddScoreValid()
|
||||
{
|
||||
return m_CumulativeResponseNumber < m_BonusResponseNumber;
|
||||
}
|
||||
public float GetBonusResponseRatio()
|
||||
{
|
||||
if (m_BonusResponseNumber <= 0) return 0f;
|
||||
|
||||
return (float)m_CumulativeResponseNumber / m_BonusResponseNumber;
|
||||
}
|
||||
|
||||
private int m_AccumulatedTriggerCount = 0; // 埋点用
|
||||
public int AccumulatedTriggerCount
|
||||
{
|
||||
get => m_AccumulatedTriggerCount;
|
||||
set => m_AccumulatedTriggerCount = value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 代币相关
|
||||
public int GetToken()
|
||||
{
|
||||
if (m_PinballData != null)
|
||||
return m_PinballData.Token;
|
||||
return 0;
|
||||
}
|
||||
public void SetRedPoint()
|
||||
{
|
||||
if (m_PinballData != null)
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Pinball, m_PinballData.Token > 0, m_PinballData.Token);
|
||||
}
|
||||
else
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Pinball, false);
|
||||
}
|
||||
}
|
||||
public void AddToken(int count)
|
||||
{
|
||||
if (m_PinballData != null)
|
||||
{
|
||||
m_PinballData.Token += count;
|
||||
}
|
||||
else
|
||||
{
|
||||
GetFreshPinballData();
|
||||
if (m_PinballData == null) return;
|
||||
m_PinballData.Token += count;
|
||||
}
|
||||
|
||||
|
||||
SetRedPoint();
|
||||
FishingPinballAct.Publish<EventPinballTokenUpdateData>(new EventPinballTokenUpdateData());
|
||||
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(m_PinballData.EventID, m_PinballData.Token);
|
||||
|
||||
SyncData();
|
||||
}
|
||||
//public bool ConsumeTokensDefault()
|
||||
//{
|
||||
// EventPinballMain pinballMain = GetCurrentEventPinballMain();
|
||||
|
||||
// int magnification = GetCurMagnification();
|
||||
// bool isEnough = ConsumeTokens(pinballMain.SpinRequire * magnification);
|
||||
// if (isEnough == false)
|
||||
// {
|
||||
// ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_103"));
|
||||
// }
|
||||
// return isEnough;
|
||||
//}
|
||||
public bool ConsumeTokens(int count)
|
||||
{
|
||||
bool isEnough = IsEnough(count);
|
||||
if (isEnough == false) return false;
|
||||
|
||||
m_PinballData.ConsumedToken += count;
|
||||
AddToken(-count);
|
||||
return isEnough;
|
||||
}
|
||||
|
||||
public bool IsEnough(int spend)
|
||||
{
|
||||
if (m_PinballData == null) return false;
|
||||
return (m_PinballData.Token - spend) >= 0;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 每一局的临时数据
|
||||
public void ClearTemporaryData()
|
||||
{
|
||||
m_IsMultipleMax = false;
|
||||
|
||||
CumulativeResponseNumber = 0;
|
||||
AccumulatedTriggerCount = 0;
|
||||
|
||||
ClearTemporaryScore();
|
||||
ClearTemporaryConsumedToken();
|
||||
}
|
||||
|
||||
private int m_TemporaryScore = 0;
|
||||
public int TemporaryScore { get => m_TemporaryScore; }
|
||||
|
||||
public void ClearTemporaryScore()
|
||||
{
|
||||
m_TemporaryScore = 0;
|
||||
}
|
||||
public void AddTemporaryScore(int score)
|
||||
{
|
||||
m_TemporaryScore += score;
|
||||
}
|
||||
public void MultiplyTemporaryScore(int multiple)
|
||||
{
|
||||
m_TemporaryScore *= multiple;
|
||||
}
|
||||
|
||||
public void ReturnConsumedToken()
|
||||
{
|
||||
AddToken(m_TemporaryConsumedToken);
|
||||
}
|
||||
private int m_TemporaryConsumedToken = 0;
|
||||
public int TemporaryConsumedToken { get => m_TemporaryConsumedToken; }
|
||||
public void ClearTemporaryConsumedToken()
|
||||
{
|
||||
m_TemporaryConsumedToken = 0;
|
||||
}
|
||||
public bool ConsumeTemporaryTokensDefault()
|
||||
{
|
||||
EventPinballMain pinballMain = GetCurrentEventPinballMain();
|
||||
|
||||
int magnification = GetCurMagnification();
|
||||
bool isEnough = ConsumeTemporaryTokens(pinballMain.SpinRequire * magnification);
|
||||
//if (isEnough == false)
|
||||
//{
|
||||
// ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_103"));
|
||||
//}
|
||||
return isEnough;
|
||||
}
|
||||
public bool ConsumeTemporaryTokens(int count)
|
||||
{
|
||||
bool isEnough = IsEnough(count);
|
||||
if (isEnough == false) return false;
|
||||
|
||||
ConsumeTokens(count);
|
||||
|
||||
m_TemporaryConsumedToken += count;
|
||||
FishingPinballAct.Publish<EventPinballTokenUpdateData>(new EventPinballTokenUpdateData());
|
||||
return isEnough;
|
||||
}
|
||||
|
||||
private bool m_IsMultipleMax = false;
|
||||
public bool IsMultipleMax { get => m_IsMultipleMax; set => m_IsMultipleMax = value; }
|
||||
#endregion
|
||||
|
||||
#region 分数相关
|
||||
private int m_MagnificationListIndex = 0;
|
||||
public void ResetMagnification()
|
||||
{
|
||||
EventPinballMain pinballMain = GetCurrentEventPinballMain();
|
||||
List<int> MagnificationList = pinballMain.SpinMag;
|
||||
int magnification = GetCurMagnification();
|
||||
bool isEnough = IsEnough(pinballMain.SpinRequire * magnification);
|
||||
|
||||
if (isEnough == false)
|
||||
{
|
||||
int count = MagnificationList.Count;
|
||||
int index = 0;
|
||||
for (int i = count - 1; i >= 0; --i)
|
||||
{
|
||||
int tmpMagnification = MagnificationList[i];
|
||||
if (IsEnough(pinballMain.SpinRequire * tmpMagnification))
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_MagnificationListIndex = index;
|
||||
}
|
||||
|
||||
FishingPinballAct.Publish(new EventPinballResetMagnification());
|
||||
}
|
||||
public int GetCurMagnification()
|
||||
{
|
||||
if (m_PinballData == null) return -1;
|
||||
EventPinballMain pinballMain = GetCurrentEventPinballMain();
|
||||
List<int> MagnificationList = pinballMain.SpinMag;
|
||||
return MagnificationList[m_MagnificationListIndex];
|
||||
}
|
||||
public bool IsMaxMagnification()
|
||||
{
|
||||
if (m_PinballData == null) return false;
|
||||
|
||||
return IsCurMaxMagnification();// m_MagnificationListIndex == (m_PinballData.MagnificationList.Count - 1);
|
||||
}
|
||||
bool IsCurMaxMagnification()
|
||||
{
|
||||
if (m_PinballData == null) return false;
|
||||
EventPinballMain pinballMain = GetCurrentEventPinballMain();
|
||||
List<int> MagnificationList = pinballMain.SpinMag;
|
||||
int count = MagnificationList.Count;
|
||||
|
||||
int tmpIndex = m_MagnificationListIndex + 1;
|
||||
|
||||
if (tmpIndex >= count)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int magnification = MagnificationList[tmpIndex];
|
||||
return !IsEnough(pinballMain.SpinRequire * magnification);
|
||||
}
|
||||
public void ScrollToTheNextMagnification()
|
||||
{
|
||||
if (m_PinballData == null) return;
|
||||
|
||||
EventPinballMain pinballMain = GetCurrentEventPinballMain();
|
||||
List<int> MagnificationList = pinballMain.SpinMag;
|
||||
|
||||
int count = MagnificationList.Count;
|
||||
|
||||
int tmpIndex = m_MagnificationListIndex + 1;
|
||||
|
||||
if (tmpIndex >= count)
|
||||
{
|
||||
tmpIndex = 0;
|
||||
}
|
||||
|
||||
int magnification = MagnificationList[tmpIndex];
|
||||
bool isEnough = IsEnough(pinballMain.SpinRequire * magnification);
|
||||
|
||||
if (isEnough)
|
||||
{
|
||||
m_MagnificationListIndex = tmpIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_MagnificationListIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private List<float> m_RewardList = new List<float>();
|
||||
public List<float> RewardList { get => m_RewardList; }
|
||||
public void InitRewardList()
|
||||
{
|
||||
m_RewardList.Clear();
|
||||
|
||||
int count = m_TbEventPinballReward.DataList.Count;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var item = m_TbEventPinballReward.DataList[i];
|
||||
m_RewardList.Add(item.SpinPoint);
|
||||
}
|
||||
if (m_PinballData != null)
|
||||
{
|
||||
if (m_PinballData.Scores >= m_RewardList[^1])
|
||||
{
|
||||
m_PinballData.Scores = 0;
|
||||
}
|
||||
m_PinballData.RewardIndex = FindInsertionIndex(m_RewardList, m_PinballData.Scores);
|
||||
}
|
||||
}
|
||||
//private int m_RewardIndex, m_RewardMaxIndex;
|
||||
//public int RewardIndex { get => m_RewardIndex; }
|
||||
//public int RewardMaxIndex { get => m_RewardMaxIndex; }
|
||||
//public void CalculateReward()
|
||||
//{
|
||||
// if (m_PinballData == null) return;
|
||||
|
||||
// m_RewardIndex = FindInsertionIndex(m_RewardList, m_PinballData.Scores);
|
||||
// m_RewardMaxIndex = m_TbEventPinballReward.DataList.Count - 1;
|
||||
//}
|
||||
//public void AddScore(int score)
|
||||
//{
|
||||
// if (m_PinballData == null) return;
|
||||
|
||||
// m_PinballData.Scores += score;
|
||||
// m_PinballData.TotalScores += score;
|
||||
//}
|
||||
#endregion
|
||||
|
||||
#region 活动相关
|
||||
public void ActivateEvent(FishingEvent fishingEvent)
|
||||
{
|
||||
if (fishingEvent == null) return;
|
||||
|
||||
if (!Init()) return;
|
||||
|
||||
if (m_PinballData != null) FixPinballData();
|
||||
if (m_PinballData == null) GetFreshPinballData();
|
||||
}
|
||||
|
||||
public void GetDataFromServer(string json)
|
||||
{
|
||||
if (string.IsNullOrEmpty(json))
|
||||
{
|
||||
GameDebug.LogWarning("EventPinball get data from server has encountered empty data.");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
m_PinballData = Newtonsoft.Json.JsonConvert.DeserializeObject<EventPinballData>(json);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
GameDebug.LogWarning("EventPinball deserialize data has failed. Exception: " + e.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Init()) return;
|
||||
}
|
||||
public int FixPinballData()
|
||||
{
|
||||
if (m_PinballData == null) return 0;
|
||||
|
||||
// 获取当前开放并符合解锁条件的弹珠活动ID
|
||||
FishingEventData fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
FishingEvent fishingEvent = fishingEventData.GetEventByTypeAndSubType(4, 6, m_IsCheckCondition);
|
||||
if (fishingEvent == null) return ClearData();
|
||||
int eventID = fishingEvent.ID;
|
||||
int pinballMainID = fishingEvent.RedirectID;
|
||||
if (pinballMainID < 0) return ClearData();
|
||||
|
||||
if (eventID != m_PinballData.EventID || pinballMainID != m_PinballData.PinballMainID) return ClearData();
|
||||
|
||||
return 0;
|
||||
}
|
||||
public TimeSpan RemainingTime
|
||||
{
|
||||
get => DateTime.Parse((m_Tables.TbFishingEvent[m_PinballData.EventID].TimeDefinition as LimitedTime).EndTime)
|
||||
- ZZTimeHelper.UtcNow();
|
||||
}
|
||||
public int ClearData()
|
||||
{
|
||||
if (m_PinballData == null) return 0;
|
||||
m_PinballData = null;
|
||||
return 0;
|
||||
}
|
||||
public void SyncData()
|
||||
{
|
||||
if (!m_IsSyncData) return;
|
||||
playerSaveStore.Write(EventPinballConfig.SAVE_DATA_KEY, Newtonsoft.Json.JsonConvert.SerializeObject(m_PinballData)).RequireAccepted();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 生命周期
|
||||
public bool Init()
|
||||
{
|
||||
InitTables();
|
||||
|
||||
m_IsSimulatorMode = false;
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Table Data
|
||||
private Tables m_Tables;
|
||||
private TbEventPinballMain m_TbEventPinballMain;
|
||||
private TbEventPinballPoint m_TbEventPinballPoint;
|
||||
private TbEventPinballReward m_TbEventPinballReward;
|
||||
private void InitTables()
|
||||
{
|
||||
m_Tables = GContext.container.Resolve<Tables>();
|
||||
if (m_Tables == null) return;
|
||||
m_TbEventPinballMain = m_Tables.TbEventPinballMain;
|
||||
m_TbEventPinballPoint = m_Tables.TbEventPinballPoint;
|
||||
m_TbEventPinballReward = m_Tables.TbEventPinballReward;
|
||||
|
||||
InitRewardList();
|
||||
}
|
||||
public EventPinballReward GetEventPinballReward(int index)
|
||||
{
|
||||
return m_TbEventPinballReward.DataList[index];
|
||||
}
|
||||
|
||||
public int GetTicketItemId()
|
||||
{
|
||||
return m_Tables.TbFishingEventCycleItem[m_Tables.TbFishingEvent[m_PinballData.EventID].RedirectID].ItemId;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region UI 设置相关
|
||||
private UIType m_UIPanel = null, m_InfoPanel = null;
|
||||
public void ClearUIData()
|
||||
{
|
||||
m_UIPanel = null;
|
||||
m_InfoPanel = null;
|
||||
}
|
||||
public void InitUIData()
|
||||
{
|
||||
GetUIPanel();
|
||||
GetInfoPanel();
|
||||
}
|
||||
public UIType GetUIPanel()
|
||||
{
|
||||
if (m_UIPanel == null)
|
||||
{
|
||||
var data = GetCurrentEventPinballMain();
|
||||
m_UIPanel = new UIType(data.UIPanel);
|
||||
}
|
||||
return m_UIPanel;
|
||||
}
|
||||
public UIType GetInfoPanel()
|
||||
{
|
||||
if (m_InfoPanel == null)
|
||||
{
|
||||
var data = GetCurrentEventPinballMain();
|
||||
m_InfoPanel = new UIType(data.InfoPanel);//EventPinballRewardPoupPanel
|
||||
}
|
||||
return m_InfoPanel;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 数据相关
|
||||
private EventPinballData m_PinballData;
|
||||
internal bool IsInactiveForKVValidation => m_Tables==null && m_TbEventPinballMain==null && m_TbEventPinballPoint==null &&
|
||||
m_TbEventPinballReward==null && m_UIPanel==null && m_InfoPanel==null && m_RewardList.Count==0 &&
|
||||
m_PointWeightList.Count==0 && !m_IsSimulatorMode && !m_IsMultipleMax && m_CumulativeResponseNumber==0 &&
|
||||
m_AccumulatedTriggerCount==0 && m_TemporaryScore==0 && m_TemporaryConsumedToken==0 && m_MagnificationListIndex==0 && m_BonusResponseNumber==10;
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVPinballState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodePinball(raw,snapshot,errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
m_PinballData=null; m_BonusResponseNumber=10; m_CumulativeResponseNumber=m_AccumulatedTriggerCount=m_TemporaryScore=m_TemporaryConsumedToken=m_MagnificationListIndex=0;
|
||||
m_IsMultipleMax=false; m_IsSimulatorMode=false; m_RewardList=new List<float>(); m_PointWeightList=new List<float>(); m_UIPanel=m_InfoPanel=null;
|
||||
m_Tables=null; m_TbEventPinballMain=null; m_TbEventPinballPoint=null; m_TbEventPinballReward=null;
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVPinballState state)
|
||||
{
|
||||
if(state==null) throw new ArgumentNullException(nameof(state));
|
||||
m_PinballData=!state.IsPresent ? null : new EventPinballData
|
||||
{
|
||||
EventID=state.EventID,
|
||||
PinballMainID=state.PinballMainID,
|
||||
SpinPoint=state.SpinPoint,
|
||||
Scores=state.Scores,
|
||||
Multiple=state.Multiple,
|
||||
Token=state.Token,
|
||||
ConsumedToken=state.ConsumedToken,
|
||||
RewardIndex=state.RewardIndex,
|
||||
Force=state.Force,
|
||||
LuckyValue=state.LuckyValue,
|
||||
InflationRate=state.InflationRate
|
||||
};
|
||||
}
|
||||
public EventPinballData PinballData { get { return m_PinballData; } }
|
||||
public int RewardMaxIndex
|
||||
{
|
||||
get { return m_TbEventPinballReward.DataList.Count - 1; }
|
||||
}
|
||||
public EventPinballMain GetFreshPinballData()
|
||||
{
|
||||
// 获取当前开放并符合解锁条件的弹珠活动ID
|
||||
FishingEventData fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
FishingEvent fishingEvent = fishingEventData.GetEventByTypeAndSubType(4, 6, m_IsCheckCondition);
|
||||
if (fishingEvent == null) return null;
|
||||
int eventID = fishingEvent.ID;
|
||||
int pinballMainID = fishingEvent.RedirectID;
|
||||
if (pinballMainID < 0) return null;
|
||||
|
||||
// 获取当前弹珠活动数据
|
||||
EventPinballMain pinballMain = m_TbEventPinballMain.GetOrDefault(pinballMainID);
|
||||
if (pinballMain == null) return null;
|
||||
|
||||
int token = m_DefaultToken;
|
||||
m_PinballData = null;
|
||||
|
||||
token = GContext.container.Resolve<FishingEventData>().GetInitWelcomeGift(eventID);
|
||||
|
||||
m_PinballData = new EventPinballData();
|
||||
|
||||
m_PinballData.InflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
Debug.Log("m_PinballData.InflationRate " + GContext.container.Resolve<PlayerData>().InflationRate);
|
||||
m_PinballData.EventID = eventID;
|
||||
m_PinballData.PinballMainID = pinballMainID;
|
||||
m_PinballData.Force = 0f;
|
||||
m_PinballData.SpinPoint = 0;
|
||||
m_PinballData.Scores = 0;
|
||||
m_PinballData.Multiple = 0;
|
||||
|
||||
m_PinballData.Token = token;
|
||||
m_PinballData.ConsumedToken = 0;
|
||||
|
||||
m_PinballData.LuckyValue = (float)pinballMain.InitialLuck;
|
||||
|
||||
m_PinballData.RewardIndex = 0;
|
||||
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(m_PinballData.EventID, m_PinballData.Token);
|
||||
|
||||
|
||||
UpdateLuckyValueAndWeight();
|
||||
|
||||
GContext.container.Resolve<GuideDataCenter>().ClearGuide("");
|
||||
SetRedPoint();
|
||||
|
||||
// 同步数据
|
||||
SyncData();
|
||||
return pinballMain;
|
||||
}
|
||||
public EventPinballMain GetNextPinballData()
|
||||
{
|
||||
// 获取当前开放并符合解锁条件的弹珠活动ID
|
||||
FishingEventData fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
FishingEvent fishingEvent = fishingEventData.GetEventByTypeAndSubType(4, 6, m_IsCheckCondition);
|
||||
if (fishingEvent == null) return null;
|
||||
int eventID = fishingEvent.ID;
|
||||
int pinballMainID = fishingEvent.RedirectID;
|
||||
if (pinballMainID < 0) return null;
|
||||
|
||||
if (eventID != m_PinballData.EventID || pinballMainID != m_PinballData.PinballMainID) return GetFreshPinballData();
|
||||
|
||||
// 获取当前弹珠活动数据
|
||||
EventPinballMain pinballMain = m_TbEventPinballMain.GetOrDefault(pinballMainID);
|
||||
if (pinballMain == null) return null;
|
||||
|
||||
m_PinballData.EventID = eventID;
|
||||
m_PinballData.PinballMainID = pinballMainID;
|
||||
|
||||
m_PinballData.ConsumedToken = 0;
|
||||
|
||||
UpdateLuckyValueAndWeight();
|
||||
|
||||
// 同步数据
|
||||
SyncData();
|
||||
return pinballMain;
|
||||
}
|
||||
public EventPinballMain ContinueGame()
|
||||
{
|
||||
if (m_PinballData == null) return GetFreshPinballData();
|
||||
|
||||
// 获取当前开放并符合解锁条件的弹珠活动ID
|
||||
FishingEventData fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
FishingEvent fishingEvent = fishingEventData.GetEventByTypeAndSubType(4, 6, m_IsCheckCondition);
|
||||
if (fishingEvent == null) return null;
|
||||
int eventID = fishingEvent.ID;
|
||||
int pinballMainID = fishingEvent.RedirectID;
|
||||
if (pinballMainID < 0) return null;
|
||||
|
||||
if (eventID != m_PinballData.EventID || pinballMainID != m_PinballData.PinballMainID) return GetFreshPinballData();
|
||||
|
||||
// 获取当前弹珠活动数据
|
||||
EventPinballMain pinballMain = m_TbEventPinballMain.GetOrDefault(pinballMainID);
|
||||
if (pinballMain == null) return null;
|
||||
|
||||
//UpdateLuckyValueAndWeight();
|
||||
|
||||
return pinballMain;
|
||||
}
|
||||
public List<float> m_PointWeightList = new List<float>();
|
||||
public EventPinballPoint UpdateLuckyValueAndWeight()
|
||||
{
|
||||
if (m_TbEventPinballPoint == null) return null;
|
||||
|
||||
m_PointWeightList.Clear();
|
||||
|
||||
float luckyValue = m_PinballData.LuckyValue;
|
||||
int count = m_TbEventPinballPoint.DataList.Count;
|
||||
|
||||
float total = 0;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
EventPinballPoint item = m_TbEventPinballPoint.DataList[i];
|
||||
|
||||
float weight = item.Weight + luckyValue * item.LuckParameter;
|
||||
total += weight;
|
||||
float tmp = total;
|
||||
m_PointWeightList.Add(tmp);
|
||||
}
|
||||
|
||||
float randomValue = UnityEngine.Random.Range(0f, total);
|
||||
int index = FindInsertionIndex(m_PointWeightList, randomValue);
|
||||
|
||||
EventPinballPoint target = m_TbEventPinballPoint.DataList[index];
|
||||
if (float.MaxValue - target.DeltaLuck <= m_PinballData.LuckyValue)
|
||||
{
|
||||
m_PinballData.LuckyValue = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PinballData.LuckyValue += target.DeltaLuck;
|
||||
if (m_PinballData.LuckyValue < 0f)
|
||||
{
|
||||
m_PinballData.LuckyValue = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
m_PinballData.SpinPoint = target.SpinPoint;
|
||||
|
||||
return target;
|
||||
}
|
||||
public int FindInsertionIndex(List<float> sortedList, float target)
|
||||
{
|
||||
if (sortedList == null || sortedList.Count == 0)
|
||||
{
|
||||
Debug.LogError("---- EventPinballDataManager->FindInsertionIndex 数组为空");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (target < sortedList[0])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (target >= sortedList[sortedList.Count - 1])
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 使用二分查找算法
|
||||
int left = 0;
|
||||
int right = sortedList.Count - 1;
|
||||
int result = -1;
|
||||
|
||||
while (left <= right)
|
||||
{
|
||||
int mid = left + (right - left) / 2;
|
||||
|
||||
if (sortedList[mid] > target)
|
||||
{
|
||||
if (mid > 0 && target > sortedList[mid - 1] && target < sortedList[mid])
|
||||
{
|
||||
result = mid;
|
||||
break;
|
||||
}
|
||||
right = mid - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mid < sortedList.Count - 1 && target >= sortedList[mid] && target < sortedList[mid + 1])
|
||||
{
|
||||
result = mid + 1;
|
||||
break;
|
||||
}
|
||||
left = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public float GetRandomForce(float ratio)
|
||||
{
|
||||
EventPinballPoint target = m_TbEventPinballPoint.GetOrDefault(m_PinballData.SpinPoint);
|
||||
EventPinballMain pinballMain = GetCurrentEventPinballMain();
|
||||
float failMaxDist = pinballMain.FailMaxDist;
|
||||
if (ratio <= failMaxDist)
|
||||
{
|
||||
return pinballMain.FailMaxForce;
|
||||
}
|
||||
int count = target.ForceLv.Count;
|
||||
|
||||
List<float> forceList = target.ForceLv[count - 1];
|
||||
ratio = (ratio - failMaxDist) / 1 - failMaxDist; // 归一化到0-1之间
|
||||
float subRatio = 1f / count; // 每个等级的比例
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
if (ratio <= subRatio * (i + 1))
|
||||
{
|
||||
forceList = target.ForceLv[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
count = forceList.Count;
|
||||
int index = UnityEngine.Random.Range(0, count);
|
||||
return forceList[index];
|
||||
}
|
||||
|
||||
public void InitGameRound()
|
||||
{
|
||||
//SetRewardList();
|
||||
//CalculateReward();
|
||||
ResetMagnification();
|
||||
ClearTemporaryData();
|
||||
|
||||
m_PinballData.Force = 0f;
|
||||
PinballData.Multiple = 0;
|
||||
}
|
||||
|
||||
public EventPinballMain GetCurrentEventPinballMain()
|
||||
{
|
||||
if (m_PinballData == null) return GetFreshPinballData();
|
||||
|
||||
return m_TbEventPinballMain.GetOrDefault(m_PinballData.PinballMainID);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 本地存储数据相关
|
||||
public string GetSaveKey(string key)
|
||||
{
|
||||
if (m_PinballData == null) return key;
|
||||
return m_PinballData.EventID + "-" + m_PinballData.PinballMainID + "-" + key;
|
||||
}
|
||||
public bool HasSaveKey(string key)
|
||||
{
|
||||
return PlayerPrefs.HasKey(GetSaveKey(key));
|
||||
}
|
||||
public void SaveData(string key, string value)
|
||||
{
|
||||
PlayerPrefs.SetString(GetSaveKey(key), value);
|
||||
PlayerPrefs.Save();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 红点相关
|
||||
public bool IsRedDotDisplayed()
|
||||
{
|
||||
if (m_PinballData == null) return false;
|
||||
return m_PinballData.Token > 0;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 运行模式相关(模拟数据模式/正常玩家模式)
|
||||
public bool m_IsSimulatorMode = false;
|
||||
public bool IsSimulatorMode
|
||||
{
|
||||
get => m_IsSimulatorMode;
|
||||
set
|
||||
{
|
||||
if (m_IsSimulatorMode != value)
|
||||
{
|
||||
m_IsSimulatorMode = value;
|
||||
FishingPinballAct.Publish(new EventPinballSyncOperatingModeData()
|
||||
{
|
||||
IsSimulatorMode = value
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IO
|
||||
public string GetSaveFilepath(string filename)
|
||||
{
|
||||
return Path.Combine(Application.persistentDataPath, filename);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class EventPinballData
|
||||
{
|
||||
public int EventID { get; set; }
|
||||
public int PinballMainID { get; set; }
|
||||
public float Force { get; set; }
|
||||
public int SpinPoint { get; set; }
|
||||
public int Scores { get; set; }
|
||||
public int Multiple { get; set; }
|
||||
|
||||
public int Token { get; set; }
|
||||
public int ConsumedToken { get; set; }
|
||||
|
||||
public float LuckyValue { get; set; }
|
||||
|
||||
public int RewardIndex { get; set; }
|
||||
|
||||
public float InflationRate { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public enum EventPinballBonusType
|
||||
{
|
||||
TypeScore,
|
||||
TypeMultiple
|
||||
}
|
||||
|
||||
public enum EventPinballTrackType
|
||||
{
|
||||
TypeEnter,
|
||||
TypeExit
|
||||
}
|
||||
|
||||
public enum EventPinballState
|
||||
{
|
||||
Prepare,
|
||||
Running,
|
||||
Ended
|
||||
}
|
||||
|
||||
public enum EventPinballSwitcherState
|
||||
{
|
||||
Open,
|
||||
Close
|
||||
}
|
||||
|
||||
public class EventPinballSwitcherOpen
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballSwitcherClose
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballEnergyArrowData
|
||||
{
|
||||
public float Energy;
|
||||
}
|
||||
|
||||
public class EventPinballEnergyArrowRestoreData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballUpdateBonusCircularProgress
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballTokenUpdateData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballBasicScore
|
||||
{
|
||||
public int Score;
|
||||
}
|
||||
public class EventPinballBasicScoreHideData
|
||||
{
|
||||
|
||||
}
|
||||
public class EventPinballBasicScoreShowData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballResetMagnification
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballRewardUpdateData
|
||||
{
|
||||
public int Scores;
|
||||
}
|
||||
|
||||
public class EventPinballSpringUpdateData
|
||||
{
|
||||
public float Ratio;
|
||||
}
|
||||
|
||||
public class EventPinballPlayArrowBreathAnimData
|
||||
{
|
||||
|
||||
}
|
||||
public class EventPinballStopArrowBreathAnimData
|
||||
{
|
||||
|
||||
}
|
||||
public class EventPinballPlayLightOnAnimData
|
||||
{
|
||||
|
||||
}
|
||||
public class EventPinballResumePelletData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballResetPelletStatusData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballLaunchPelletData
|
||||
{
|
||||
public float Force;
|
||||
}
|
||||
|
||||
public class EventPinballShowPopupData
|
||||
{
|
||||
public int Scores;
|
||||
public EventPinballBonusType BonusType;
|
||||
public bool IsMultipleMax;
|
||||
}
|
||||
public class EventPinballHidePopupData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballShowMultipleTextData
|
||||
{
|
||||
public EventPinballBonusType BonusType;
|
||||
public Vector3 Position;
|
||||
public string Tips;
|
||||
public int Scores;
|
||||
public bool IsMultipleMax;
|
||||
}
|
||||
|
||||
public class EventPinballRewardDisplayUpdateData
|
||||
{
|
||||
public int RewardIndex;
|
||||
public int TargetScore;
|
||||
public int LevelScore;
|
||||
public float FromRatio;
|
||||
public float TargetRatio;
|
||||
public int DropId;
|
||||
public int RewardDisplayDropId;
|
||||
}
|
||||
|
||||
public class EventPinballSyncOperatingModeData
|
||||
{
|
||||
public bool IsSimulatorMode;
|
||||
}
|
||||
|
||||
public class EventPinballHideWidgetMultipleEffectData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballShowWidgetScoreEffect2Data
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballHideFiringPinData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballShowFiringPinData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballHideCircularRotationalAnim
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballShowCircularRotationalAnim
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballLaunchPelletStatusData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballSetDummyPelletData
|
||||
{
|
||||
public PinballPellet Pellet;
|
||||
}
|
||||
|
||||
public class EventPinballConfig
|
||||
{
|
||||
public const string CSVFileName = "pinball.csv";
|
||||
|
||||
public const string SAVE_DATA_KEY = "epbdk"; // event pinball data key
|
||||
|
||||
public const int MultipleMax = 10;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,957 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.U2D;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public class EventScratchTicketDataManager
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore playerSaveStore { get; set; }
|
||||
#if UNITY_EDITOR
|
||||
private bool m_IsCheckCondition = true;
|
||||
private bool m_IsSyncData = true;
|
||||
private int m_DefaultToken = 100;
|
||||
#else
|
||||
private bool m_IsCheckCondition = true;
|
||||
private bool m_IsSyncData = true;
|
||||
private int m_DefaultToken = 0;
|
||||
#endif
|
||||
|
||||
#region 埋点相关
|
||||
public int TempRecordRewardTaskID { get; set; } = 0;
|
||||
public int TempRecordRewardTaskConsumedToken { get; set; } = 0;
|
||||
public void SyncTempRecordData()
|
||||
{
|
||||
ClearRecordList();
|
||||
|
||||
TempRecordRewardTaskID = m_ScratchTicketData.EventScratchRewardTaskID;
|
||||
TempRecordRewardTaskConsumedToken = m_ScratchTicketData.RewardTaskConsumedToken;
|
||||
}
|
||||
public void RecordStatisticalData()
|
||||
{
|
||||
int fromToken = TempRecordRewardTaskConsumedToken;
|
||||
|
||||
int taskID = TempRecordRewardTaskID;
|
||||
EventScratchReward curScratchReward = GetEventScratchReward(taskID);
|
||||
|
||||
int count = m_RecordList.Count;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var data = m_RecordList[i];
|
||||
fromToken += 1;
|
||||
if (fromToken >= curScratchReward.TokenRequired)
|
||||
{
|
||||
data.MilestoneTask = curScratchReward.TaskID;
|
||||
|
||||
fromToken = 0;
|
||||
|
||||
taskID = curScratchReward.NextTask;
|
||||
curScratchReward = GetEventScratchReward(taskID);
|
||||
}
|
||||
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("event_scratchticket"))
|
||||
{
|
||||
e.AddContent("ticket_id", data.TicketID)
|
||||
.AddContent("ticketfinish_id", data.TicketFinishID)
|
||||
.AddContent("scratch_type", ScratchTicketData.magIndex + 1)
|
||||
.AddContent("milestone_task", data.MilestoneTask);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log("<color=red>--- RecordStatisticalData m_RecordList: </color>" + Newtonsoft.Json.JsonConvert.SerializeObject(m_RecordList));
|
||||
#endif
|
||||
}
|
||||
private List<EventScratchTicketRecordData> m_RecordList = new List<EventScratchTicketRecordData>();
|
||||
public void InitRecordList()
|
||||
{
|
||||
ClearRecordList();
|
||||
}
|
||||
public void ClearRecordList()
|
||||
{
|
||||
m_RecordList?.Clear();
|
||||
TempRecordRewardTaskID = 0;
|
||||
TempRecordRewardTaskConsumedToken = 0;
|
||||
}
|
||||
public EventScratchTicketRecordData AddRecordData()
|
||||
{
|
||||
EventScratchTicketRecordData data = new EventScratchTicketRecordData();
|
||||
m_RecordList.Add(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 数据相关
|
||||
private EventScratchTicketData m_ScratchTicketData;
|
||||
internal bool IsInactiveForKVValidation => m_Tables==null && m_TbEventScratchMain==null && m_TbEventScratchCard==null &&
|
||||
m_TbEventScratchReward==null && m_ChainPackData==null && m_NormalPackData==null && m_SpriteAtlas==null && m_SpriteDict==null &&
|
||||
m_UIPanel==null && m_InfoPanel==null && m_RewardPanel==null && m_ChainPackPanel==null && m_PackPanel==null &&
|
||||
m_RecordList.Count==0 && m_PointWeightList.Count==0 && m_ScratchStampList.Count==0 &&
|
||||
TempRecordRewardTaskID==0 && TempRecordRewardTaskConsumedToken==0;
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVScratchState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodeScratch(raw,snapshot,errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
m_ScratchTicketData=null; TempRecordRewardTaskID=TempRecordRewardTaskConsumedToken=0; m_RecordList=new List<EventScratchTicketRecordData>();
|
||||
m_PointWeightList=new List<float>(); m_ScratchStampList=new List<int>(); m_ChainPackData=null; m_NormalPackData=null;
|
||||
m_UIPanel=m_InfoPanel=m_RewardPanel=m_ChainPackPanel=m_PackPanel=null; m_SpriteAtlas=null; m_SpriteDict=null;
|
||||
m_Tables=null; m_TbEventScratchMain=null; m_TbEventScratchCard=null; m_TbEventScratchReward=null;
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVScratchState state)
|
||||
{
|
||||
if(state==null) throw new ArgumentNullException(nameof(state));
|
||||
m_ScratchTicketData=!state.IsPresent ? null : new EventScratchTicketData
|
||||
{
|
||||
EventID=state.EventID,
|
||||
ScratchMainID=state.ScratchMainID,
|
||||
ChainPackID=state.ChainPackID,
|
||||
Token=state.Token,
|
||||
ConsumedToken=state.ConsumedToken,
|
||||
RewardTaskConsumedToken=state.RewardTaskConsumedToken,
|
||||
EventScratchRewardTaskID=state.EventScratchRewardTaskID,
|
||||
EventScratchCardID=state.EventScratchCardID,
|
||||
TotalConsumedToken=state.TotalConsumedToken,
|
||||
ChainPackProgress=state.ChainPackProgress,
|
||||
magIndex=state.magIndex,
|
||||
IsOver=state.IsOver,
|
||||
LuckyValue=state.LuckyValue,
|
||||
InflationRate=state.InflationRate,
|
||||
StampRewardList=new List<int>(state.StampRewardList),
|
||||
StampRewardDict=state.StampRewardDict.ToDictionary(p=>p.Key,p=>new EventScratchTicketOwnedStampRewardData { EventScratchCardID=p.Value.EventScratchCardID,Count=p.Value.Count })
|
||||
};
|
||||
}
|
||||
public EventScratchTicketData ScratchTicketData { get { return m_ScratchTicketData; } }
|
||||
public EventScratchMain GetFreshScratchTicketData()
|
||||
{
|
||||
// 获取当前开放并符合解锁条件的刮刮乐活动ID
|
||||
FishingEventData fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
FishingEvent fishingEvent = fishingEventData.GetEventByTypeAndSubType(7, 3, m_IsCheckCondition);
|
||||
if (fishingEvent == null) return null;
|
||||
int eventID = fishingEvent.ID;
|
||||
int scratchMainID = fishingEvent.RedirectID;
|
||||
if (scratchMainID < 0) return null;
|
||||
|
||||
// 获取当前刮刮乐活动数据
|
||||
EventScratchMain scratchMain = m_TbEventScratchMain.GetOrDefault(scratchMainID);
|
||||
if (scratchMain == null) return null;
|
||||
|
||||
int token = m_DefaultToken;
|
||||
if (m_ScratchTicketData != null) m_ScratchTicketData.Clear();
|
||||
m_ScratchTicketData = null;
|
||||
|
||||
token = GContext.container.Resolve<FishingEventData>().GetInitWelcomeGift(eventID);
|
||||
|
||||
m_ScratchTicketData = new EventScratchTicketData();
|
||||
m_ScratchTicketData.InflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
Debug.Log("m_PinballData.InflationRate " + GContext.container.Resolve<PlayerData>().InflationRate);
|
||||
m_ScratchTicketData.EventID = eventID;
|
||||
m_ScratchTicketData.ScratchMainID = scratchMainID;
|
||||
|
||||
m_ScratchTicketData.IsOver = false;
|
||||
m_ScratchTicketData.Token = token;
|
||||
m_ScratchTicketData.ConsumedToken = 0;
|
||||
|
||||
m_ScratchTicketData.LuckyValue = 0f;
|
||||
m_ScratchTicketData.EventScratchCardID = 0;
|
||||
|
||||
m_ScratchTicketData.TotalConsumedToken = 0;
|
||||
m_ScratchTicketData.ChainPackProgress = 0;
|
||||
|
||||
m_ScratchTicketData.StampRewardDict = new Dictionary<int, EventScratchTicketOwnedStampRewardData>();
|
||||
|
||||
EventScratchReward eventScratchReward = m_TbEventScratchReward.DataList[0];
|
||||
m_ScratchTicketData.EventScratchRewardTaskID = eventScratchReward.TaskID;
|
||||
m_ScratchTicketData.RewardTaskConsumedToken = 0;
|
||||
if (FishingEventData.Condition_NoRechargeDay())
|
||||
{
|
||||
m_ScratchTicketData.ChainPackID = scratchMain.AdPackId;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ScratchTicketData.ChainPackID = scratchMain.PackId;
|
||||
}
|
||||
m_ScratchTicketData.StampRewardList.Clear();
|
||||
|
||||
ClearSaveData(EventScratchTicketConfig.key_first_in);
|
||||
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(m_ScratchTicketData.EventID, m_ScratchTicketData.Token);
|
||||
|
||||
UpdateLuckyValueAndWeight();
|
||||
|
||||
foreach (var item in scratchMain.GuidanceGroupList)
|
||||
{
|
||||
GContext.container.Resolve<GuideDataCenter>().ClearGuide(item);
|
||||
}
|
||||
|
||||
// 同步数据
|
||||
SyncData();
|
||||
return scratchMain;
|
||||
}
|
||||
|
||||
public EventScratchMain GetNextScratchTicketData()
|
||||
{
|
||||
// 获取当前开放并符合解锁条件的刮刮乐活动ID
|
||||
FishingEventData fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
FishingEvent fishingEvent = fishingEventData.GetEventByTypeAndSubType(7, 3, m_IsCheckCondition);
|
||||
if (fishingEvent == null) return null;
|
||||
int eventID = fishingEvent.ID;
|
||||
int scratchMainID = fishingEvent.RedirectID;
|
||||
if (scratchMainID < 0) return null;
|
||||
|
||||
// 获取当前刮刮乐活动数据
|
||||
EventScratchMain scratchMain = m_TbEventScratchMain.GetOrDefault(scratchMainID);
|
||||
if (scratchMain == null) return null;
|
||||
|
||||
m_ScratchTicketData.EventID = eventID;
|
||||
m_ScratchTicketData.ScratchMainID = scratchMainID;
|
||||
|
||||
m_ScratchTicketData.IsOver = false;
|
||||
m_ScratchTicketData.ConsumedToken = 0;
|
||||
|
||||
UpdateLuckyValueAndWeight();
|
||||
|
||||
// 同步数据
|
||||
SyncData();
|
||||
return scratchMain;
|
||||
}
|
||||
public EventScratchMain ContinueGame()
|
||||
{
|
||||
if (m_ScratchTicketData == null) return GetFreshScratchTicketData();
|
||||
|
||||
// 获取当前开放并符合解锁条件的刮刮乐活动ID
|
||||
FishingEventData fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
FishingEvent fishingEvent = fishingEventData.GetEventByTypeAndSubType(7, 3, m_IsCheckCondition);
|
||||
if (fishingEvent == null) return null;
|
||||
int eventID = fishingEvent.ID;
|
||||
int scratchMainID = fishingEvent.RedirectID;
|
||||
if (scratchMainID < 0) return null;
|
||||
|
||||
if (eventID != m_ScratchTicketData.EventID || scratchMainID != m_ScratchTicketData.ScratchMainID) return GetFreshScratchTicketData();
|
||||
|
||||
// 获取当前刮刮乐活动数据
|
||||
EventScratchMain scratchMain = m_TbEventScratchMain.GetOrDefault(scratchMainID);
|
||||
if (scratchMain == null) return null;
|
||||
|
||||
return scratchMain;
|
||||
}
|
||||
public List<float> m_PointWeightList = new List<float>();
|
||||
public EventScratchCard UpdateLuckyValueAndWeight()
|
||||
{
|
||||
if (m_TbEventScratchCard == null) return null;
|
||||
|
||||
m_PointWeightList.Clear();
|
||||
|
||||
float luckyValue = m_ScratchTicketData.LuckyValue;
|
||||
int count = m_TbEventScratchCard.DataList.Count;
|
||||
|
||||
float total = 0;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
EventScratchCard item = m_TbEventScratchCard.DataList[i];
|
||||
|
||||
float weight = item.Weight + luckyValue * item.LuckParameter;
|
||||
total += weight;
|
||||
float tmp = total;
|
||||
m_PointWeightList.Add(tmp);
|
||||
}
|
||||
|
||||
float randomValue = UnityEngine.Random.Range(0f, total);
|
||||
int index = FindInsertionIndex(m_PointWeightList, randomValue);
|
||||
|
||||
EventScratchCard target = m_TbEventScratchCard.DataList[index];
|
||||
if (float.MaxValue - target.DeltaLuck <= m_ScratchTicketData.LuckyValue)
|
||||
{
|
||||
m_ScratchTicketData.LuckyValue = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ScratchTicketData.LuckyValue += target.DeltaLuck;
|
||||
if (m_ScratchTicketData.LuckyValue < 0f)
|
||||
{
|
||||
m_ScratchTicketData.LuckyValue = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
m_ScratchTicketData.EventScratchCardID = target.ID;
|
||||
|
||||
return target;
|
||||
}
|
||||
public EventScratchCard GetCurrentEventScratchCard()
|
||||
{
|
||||
if (m_ScratchTicketData == null) return null;
|
||||
return GetEventScratchCard(m_ScratchTicketData.EventScratchCardID);
|
||||
}
|
||||
public int FindInsertionIndex(List<float> sortedList, float target)
|
||||
{
|
||||
if (sortedList == null || sortedList.Count == 0)
|
||||
{
|
||||
Debug.LogError("---- EventScratchTicketDataManager->FindInsertionIndex 数组为空");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (target < sortedList[0])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (target >= sortedList[sortedList.Count - 1])
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 使用二分查找算法
|
||||
int left = 0;
|
||||
int right = sortedList.Count - 1;
|
||||
int result = -1;
|
||||
|
||||
while (left <= right)
|
||||
{
|
||||
int mid = left + (right - left) / 2;
|
||||
|
||||
if (sortedList[mid] > target)
|
||||
{
|
||||
if (mid > 0 && target > sortedList[mid - 1] && target < sortedList[mid])
|
||||
{
|
||||
result = mid;
|
||||
break;
|
||||
}
|
||||
right = mid - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mid < sortedList.Count - 1 && target >= sortedList[mid] && target < sortedList[mid + 1])
|
||||
{
|
||||
result = mid + 1;
|
||||
break;
|
||||
}
|
||||
left = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
public EventScratchMain GetCurrentEventScratchMain()
|
||||
{
|
||||
if (m_ScratchTicketData == null) return GetFreshScratchTicketData();
|
||||
|
||||
return m_TbEventScratchMain.GetOrDefault(m_ScratchTicketData.ScratchMainID);
|
||||
}
|
||||
private EventScratchTicketChainPackData m_ChainPackData;
|
||||
public EventScratchTicketChainPackData GetChainPackData()
|
||||
{
|
||||
if (m_ChainPackData == null)
|
||||
{
|
||||
EventScratchMain scratchMain = GetCurrentEventScratchMain();
|
||||
if (scratchMain == null) return null;
|
||||
int packId = m_ScratchTicketData.ChainPackID;
|
||||
if (packId == 0)
|
||||
{
|
||||
packId = scratchMain.PackId;
|
||||
}
|
||||
var chainList = m_Tables.TbEventPackManager[packId].VIPPackList[0];
|
||||
|
||||
var expireTime = GetExpireTime;
|
||||
|
||||
var chainListIdSet = chainList.ToHashSet();
|
||||
var packs = m_Tables.TbPack.DataList.Where(p => chainListIdSet.Contains(p.ID)).ToArray();
|
||||
|
||||
m_ChainPackData = new EventScratchTicketChainPackData(chainList, expireTime, packs, m_ScratchTicketData.EventID, m_ScratchTicketData.ChainPackProgress);
|
||||
RedPointManager.Instance.SetRedPointState(m_ChainPackData.RedPointKey, m_ChainPackData.DoNeedPackRedPoint, m_ChainPackData.RedPointIndex);
|
||||
}
|
||||
return m_ChainPackData;
|
||||
}
|
||||
public void InitChainPackData()
|
||||
{
|
||||
ClearChainPackData();
|
||||
}
|
||||
public void ClearChainPackData()
|
||||
{
|
||||
if (m_ChainPackData != null)
|
||||
{
|
||||
m_ChainPackData = null;
|
||||
}
|
||||
}
|
||||
private EventBreakNormalPackInfo m_NormalPackData;
|
||||
public EventBreakNormalPackInfo GetNormalPackData()
|
||||
{
|
||||
if (m_NormalPackData == null)
|
||||
{
|
||||
EventScratchMain scratchMain = GetCurrentEventScratchMain();
|
||||
if (scratchMain == null) return null;
|
||||
|
||||
var expireTime = GetExpireTime;
|
||||
|
||||
var packList = m_Tables.TbEventPackManager[scratchMain.PackId2].VIPPackList[0];
|
||||
|
||||
m_NormalPackData = new EventBreakNormalPackInfo
|
||||
{
|
||||
EventId = m_ScratchTicketData.EventID,
|
||||
PackLeft = m_Tables.TbPack[packList[0]],
|
||||
PackRight = m_Tables.TbPack[packList[1]],
|
||||
ExpireTime = expireTime,
|
||||
};
|
||||
}
|
||||
|
||||
return m_NormalPackData;
|
||||
}
|
||||
private void InitNormalPackData()
|
||||
{
|
||||
ClearNormalPackDatas();
|
||||
}
|
||||
private void ClearNormalPackDatas()
|
||||
{
|
||||
m_NormalPackData = null;
|
||||
}
|
||||
public void InitRewardBagData()
|
||||
{
|
||||
GContext.container.Resolve<IDeferredRewardStashService>().Reset();
|
||||
}
|
||||
|
||||
public void SetChainProgress(int progress)
|
||||
{
|
||||
m_ScratchTicketData.ChainPackProgress = progress;
|
||||
SyncData();
|
||||
}
|
||||
public int GetChainProgress()
|
||||
{
|
||||
return m_ScratchTicketData.ChainPackProgress;
|
||||
}
|
||||
public void AddStampReward(int cardID)
|
||||
{
|
||||
m_ScratchTicketData.StampRewardList.Add(cardID);
|
||||
SyncData();
|
||||
}
|
||||
public int GetStampRewardNum()
|
||||
{
|
||||
return m_ScratchTicketData.StampRewardList.Count;
|
||||
}
|
||||
public EventScratchCard GetFirstStampReward()
|
||||
{
|
||||
return GetEventScratchCard(m_ScratchTicketData.StampRewardList[0]);
|
||||
}
|
||||
public void RemoveFirstStampReward()
|
||||
{
|
||||
m_ScratchTicketData.StampRewardList.RemoveAt(0);
|
||||
SyncData();
|
||||
}
|
||||
|
||||
private List<int> m_ScratchStampList = new List<int>();
|
||||
public List<int> ScratchStampList { get { return m_ScratchStampList; } }
|
||||
private void InitScratchStampList()
|
||||
{
|
||||
m_ScratchStampList.Clear();
|
||||
}
|
||||
public void ClearScratchStampList()
|
||||
{
|
||||
m_ScratchStampList.Clear();
|
||||
}
|
||||
public void AddScratchStamp(int cardID)
|
||||
{
|
||||
m_ScratchStampList.Add(cardID);
|
||||
}
|
||||
public EventScratchCard GetFirstScratchStamp()
|
||||
{
|
||||
return GetEventScratchCard(m_ScratchStampList[0]);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 代币相关
|
||||
public int GetToken()
|
||||
{
|
||||
if (m_ScratchTicketData != null)
|
||||
return m_ScratchTicketData.Token;
|
||||
return 0;
|
||||
}
|
||||
public int GetRedCount()
|
||||
{
|
||||
if (m_ScratchTicketData != null)
|
||||
{
|
||||
//+礼包红点
|
||||
if (m_ChainPackData != null)
|
||||
{
|
||||
return m_ScratchTicketData.Token + m_ChainPackData.RedPointIndex;
|
||||
|
||||
}
|
||||
return m_ScratchTicketData.Token;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public void AddToken(int count)
|
||||
{
|
||||
if (m_ScratchTicketData != null)
|
||||
{
|
||||
m_ScratchTicketData.Token += count;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_ScratchTicketData == null) return;
|
||||
m_ScratchTicketData.Token += count;
|
||||
}
|
||||
|
||||
if (m_ScratchTicketData.Token > 0) RedPointManager.Instance.SetRedPointState(RedPointName.Home_ScratchTicket, true, GetRedCount());
|
||||
else RedPointManager.Instance.SetRedPointState(RedPointName.Home_ScratchTicket, false);
|
||||
|
||||
FishingScratchTicketAct.Publish<EventScratchTicketTokenUpdateData>(new EventScratchTicketTokenUpdateData());
|
||||
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(m_ScratchTicketData.EventID, m_ScratchTicketData.Token);
|
||||
|
||||
SyncData();
|
||||
}
|
||||
public bool ConsumeTokens(int count)
|
||||
{
|
||||
bool isEnough = IsEnough(count);
|
||||
if (isEnough == false) return false;
|
||||
|
||||
m_ScratchTicketData.ConsumedToken += count;
|
||||
m_ScratchTicketData.TotalConsumedToken += count;
|
||||
AddToken(-count);
|
||||
return isEnough;
|
||||
}
|
||||
|
||||
public bool IsEnough(int spend)
|
||||
{
|
||||
if (m_ScratchTicketData == null) return false;
|
||||
return (m_ScratchTicketData.Token - spend) >= 0;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 活动相关
|
||||
public void ActivateEvent(FishingEvent fishingEvent)
|
||||
{
|
||||
if (fishingEvent == null) return;
|
||||
|
||||
if (!Init()) return;
|
||||
|
||||
if (m_ScratchTicketData == null) GetFreshScratchTicketData();
|
||||
else FixScratchTicketData();
|
||||
}
|
||||
|
||||
public void GetDataFromServer(string json)
|
||||
{
|
||||
if (string.IsNullOrEmpty(json))
|
||||
{
|
||||
GameDebug.LogWarning("EventScratchTicket get data from server has encountered empty data.");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
m_ScratchTicketData = Newtonsoft.Json.JsonConvert.DeserializeObject<EventScratchTicketData>(json);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
GameDebug.LogWarning("EventScratchTicket deserialize data has failed. Exception: " + e.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Init()) return;
|
||||
}
|
||||
public int FixScratchTicketData()
|
||||
{
|
||||
if (m_ScratchTicketData == null) return 0;
|
||||
|
||||
// 获取当前开放并符合解锁条件的刮刮乐活动ID
|
||||
FishingEventData fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
FishingEvent fishingEvent = fishingEventData.GetEventByTypeAndSubType(7, 3, m_IsCheckCondition);
|
||||
if (fishingEvent == null) return ClearData();
|
||||
int eventID = fishingEvent.ID;
|
||||
int scratchMainID = fishingEvent.RedirectID;
|
||||
if (scratchMainID < 0) return ClearData();
|
||||
|
||||
if (eventID != m_ScratchTicketData.EventID || scratchMainID != m_ScratchTicketData.ScratchMainID) return ClearData();
|
||||
|
||||
return 0;
|
||||
}
|
||||
public DateTime GetExpireTime
|
||||
{
|
||||
get => DateTime.Parse((m_Tables.TbFishingEvent[m_ScratchTicketData.EventID].TimeDefinition as LimitedTime).EndTime);
|
||||
}
|
||||
public TimeSpan RemainingTime
|
||||
{
|
||||
get => GetExpireTime - ZZTimeHelper.UtcNow();
|
||||
}
|
||||
public int ClearData()
|
||||
{
|
||||
if (m_ScratchTicketData == null) return 0;
|
||||
|
||||
m_ScratchTicketData.Clear();
|
||||
m_ScratchTicketData = null;
|
||||
return 0;
|
||||
}
|
||||
public void SyncData()
|
||||
{
|
||||
if (!m_IsSyncData) return;
|
||||
playerSaveStore.Write(EventScratchTicketConfig.SAVE_DATA_KEY, Newtonsoft.Json.JsonConvert.SerializeObject(m_ScratchTicketData)).RequireAccepted();
|
||||
}
|
||||
|
||||
List<int> magList = new List<int>() { 1, 5, 10, 20 };
|
||||
|
||||
public int ChangeScratchMag()
|
||||
{
|
||||
ScratchTicketData.magIndex++;
|
||||
if (ScratchTicketData.magIndex >= magList.Count)
|
||||
{
|
||||
ScratchTicketData.magIndex = 0;
|
||||
}
|
||||
SyncData();
|
||||
return magList[ScratchTicketData.magIndex];
|
||||
}
|
||||
public int GetScratchMag()
|
||||
{
|
||||
return magList[ScratchTicketData.magIndex];
|
||||
}
|
||||
public bool IsMaxMag()
|
||||
{
|
||||
return ScratchTicketData.magIndex == magList.Count - 1;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 生命周期
|
||||
public bool Init()
|
||||
{
|
||||
InitTables();
|
||||
return true;
|
||||
}
|
||||
public void StartData()
|
||||
{
|
||||
ClearUIData();
|
||||
InitUIData();
|
||||
InitChainPackData();
|
||||
InitNormalPackData();
|
||||
InitRewardBagData();
|
||||
InitSpriteDict();
|
||||
InitScratchStampList();
|
||||
InitRecordList();
|
||||
}
|
||||
public void StopData()
|
||||
{
|
||||
ClearUIData();
|
||||
ClearChainPackData();
|
||||
ClearNormalPackDatas();
|
||||
ClearSpriteAtlas();
|
||||
ClearSpriteDict();
|
||||
ClearScratchStampList();
|
||||
ClearRecordList();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region UI 设置相关
|
||||
private UIType m_UIPanel = null, m_InfoPanel = null, m_RewardPanel = null, m_ChainPackPanel = null, m_PackPanel = null;
|
||||
public void ClearUIData()
|
||||
{
|
||||
m_UIPanel = null;
|
||||
m_InfoPanel = null;
|
||||
m_RewardPanel = null;
|
||||
m_ChainPackPanel = null;
|
||||
m_PackPanel = null;
|
||||
}
|
||||
public void InitUIData()
|
||||
{
|
||||
GetUIPanel();
|
||||
GetInfoPanel();
|
||||
GetChainPackPanel();
|
||||
GetPackPanel();
|
||||
}
|
||||
public UIType GetUIPanel()
|
||||
{
|
||||
if (m_UIPanel == null)
|
||||
{
|
||||
var data = GetCurrentEventScratchMain();
|
||||
m_UIPanel = new UIType(data.UIPanel);
|
||||
}
|
||||
return m_UIPanel;
|
||||
}
|
||||
public UIType GetRewardPanel()
|
||||
{
|
||||
if (m_RewardPanel == null)
|
||||
{
|
||||
var data = GetCurrentEventScratchMain();
|
||||
m_RewardPanel = new UIType(data.RewardPanel);
|
||||
}
|
||||
return m_RewardPanel;
|
||||
}
|
||||
public UIType GetInfoPanel()
|
||||
{
|
||||
if (m_InfoPanel == null)
|
||||
{
|
||||
var data = GetCurrentEventScratchMain();
|
||||
m_InfoPanel = new UIType(data.InfoPanel);
|
||||
}
|
||||
return m_InfoPanel;
|
||||
}
|
||||
public UIType GetChainPackPanel()
|
||||
{
|
||||
if (m_ChainPackPanel == null)
|
||||
{
|
||||
var data = GetCurrentEventScratchMain();
|
||||
m_ChainPackPanel = new UIType(data.ChainPackPanel);
|
||||
}
|
||||
return m_ChainPackPanel;
|
||||
}
|
||||
public UIType GetPackPanel()
|
||||
{
|
||||
if (m_PackPanel == null)
|
||||
{
|
||||
var data = GetCurrentEventScratchMain();
|
||||
m_PackPanel = new UIType(data.PackPanel);
|
||||
}
|
||||
return m_PackPanel;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 图集等
|
||||
private SpriteAtlas m_SpriteAtlas;
|
||||
public SpriteAtlas SpriteAtlas
|
||||
{
|
||||
get { return m_SpriteAtlas; }
|
||||
set { m_SpriteAtlas = value; }
|
||||
}
|
||||
public void ClearSpriteAtlas()
|
||||
{
|
||||
m_SpriteAtlas = null;
|
||||
}
|
||||
private Dictionary<string, Sprite> m_SpriteDict;
|
||||
private void InitSpriteDict()
|
||||
{
|
||||
if (m_SpriteDict != null)
|
||||
{
|
||||
m_SpriteDict.Clear();
|
||||
m_SpriteDict = null;
|
||||
}
|
||||
m_SpriteDict = new Dictionary<string, Sprite>();
|
||||
}
|
||||
public void AddSprite(string name, Sprite sprite)
|
||||
{
|
||||
m_SpriteDict.Add(name, sprite);
|
||||
}
|
||||
public Sprite GetSprite(string name)
|
||||
{
|
||||
return m_SpriteDict[name];
|
||||
}
|
||||
private void ClearSpriteDict()
|
||||
{
|
||||
if (m_SpriteDict == null) return;
|
||||
|
||||
foreach (var pair in m_SpriteDict)
|
||||
{
|
||||
Addressables.Release(pair.Value);
|
||||
}
|
||||
|
||||
m_SpriteDict.Clear();
|
||||
m_SpriteDict = null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Table Data
|
||||
private Tables m_Tables;
|
||||
private TbEventScratchMain m_TbEventScratchMain;
|
||||
private TbEventScratchCard m_TbEventScratchCard;
|
||||
private TbEventScratchReward m_TbEventScratchReward;
|
||||
|
||||
private void InitTables()
|
||||
{
|
||||
m_Tables = GContext.container.Resolve<Tables>();
|
||||
if (m_Tables == null) return;
|
||||
|
||||
m_TbEventScratchMain = m_Tables.TbEventScratchMain;
|
||||
m_TbEventScratchCard = m_Tables.TbEventScratchCard;
|
||||
m_TbEventScratchReward = m_Tables.TbEventScratchReward;
|
||||
}
|
||||
|
||||
public EventScratchReward GetEventScratchReward(int taskID)
|
||||
{
|
||||
return m_TbEventScratchReward.GetOrDefault(taskID);
|
||||
}
|
||||
public EventScratchReward GetEventScratchFinalReward()
|
||||
{
|
||||
int count = m_TbEventScratchReward.DataList.Count;
|
||||
return m_TbEventScratchReward.DataList[count - 1];
|
||||
}
|
||||
public EventScratchCard GetEventScratchCard(int cardID)
|
||||
{
|
||||
return m_TbEventScratchCard.GetOrDefault(cardID);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 本地存储数据相关
|
||||
public string GetSaveKey(string key)
|
||||
{
|
||||
if (m_ScratchTicketData == null) return key;
|
||||
return m_ScratchTicketData.EventID + "-" + m_ScratchTicketData.ScratchMainID + "-" + key;
|
||||
}
|
||||
public bool HasSaveKey(string key)
|
||||
{
|
||||
return PlayerPrefs.HasKey(GetSaveKey(key));
|
||||
}
|
||||
public void SaveData(string key, string value)
|
||||
{
|
||||
PlayerPrefs.SetString(GetSaveKey(key), value);
|
||||
PlayerPrefs.Save();
|
||||
}
|
||||
public void ClearSaveData(string key)
|
||||
{
|
||||
PlayerPrefs.DeleteKey(GetSaveKey(key));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 红点相关
|
||||
public bool IsRedDotDisplayed()
|
||||
{
|
||||
if (m_ScratchTicketData == null) return false;
|
||||
return m_ScratchTicketData.Token > 0;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
public enum EventScratchTicketScratchType
|
||||
{
|
||||
TypeInvalid,
|
||||
TypeScratch1,
|
||||
TypeScratch5,//5不是代表5次 表示多数
|
||||
TypeTemporary
|
||||
}
|
||||
public class EventScratchTicketData
|
||||
{
|
||||
public int EventID { get; set; }
|
||||
public int ScratchMainID { get; set; }
|
||||
public int ChainPackID { get; set; }
|
||||
public bool IsOver { get; set; }
|
||||
public int Token { get; set; }
|
||||
public int ConsumedToken { get; set; }
|
||||
|
||||
public int RewardTaskConsumedToken { get; set; }
|
||||
public int EventScratchRewardTaskID { get; set; }
|
||||
|
||||
public int EventScratchCardID { get; set; }
|
||||
public float LuckyValue { get; set; }
|
||||
|
||||
public int TotalConsumedToken { get; set; }
|
||||
public int ChainPackProgress { get; set; }
|
||||
|
||||
public List<int> StampRewardList = new List<int>();
|
||||
public float InflationRate { get; set; }
|
||||
|
||||
public Dictionary<int, EventScratchTicketOwnedStampRewardData> StampRewardDict { get; set; }
|
||||
public int magIndex { get; set; }
|
||||
public void Clear()
|
||||
{
|
||||
if (StampRewardDict != null)
|
||||
{
|
||||
StampRewardDict.Clear();
|
||||
}
|
||||
StampRewardList.Clear();
|
||||
}
|
||||
|
||||
}
|
||||
public class EventScratchTicketConfig
|
||||
{
|
||||
public const string SAVE_DATA_KEY = "estdk"; // event scratch ticket data key
|
||||
public const string key_first_in = "scratch_kfcw"; // 本地存储数据key
|
||||
|
||||
public static Vector2 ScreenToGraphicLocalPoint(Graphic graphic, Vector2 screenPoint)
|
||||
{
|
||||
var canvas = graphic.canvas.rootCanvas;
|
||||
var camera = canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera;
|
||||
var rt = graphic.rectTransform;
|
||||
|
||||
return RectTransformUtility.ScreenPointToLocalPointInRectangle(rt, screenPoint, camera, out var result) ? result : default;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventScratchTicketTokenUpdateData
|
||||
{
|
||||
|
||||
}
|
||||
public class EventScratchTicketRewardTaskUpdateData
|
||||
{
|
||||
public EventScratchTicketScratchType ScratchType;
|
||||
public int Token;
|
||||
}
|
||||
public class EventScratchTicketRewardDisplayUpdateData
|
||||
{
|
||||
public int TaskID;
|
||||
public int FromToken;
|
||||
public int TargetToken;
|
||||
public int LevelToken;
|
||||
public float FromRatio;
|
||||
public float TargetRatio;
|
||||
public EventScratchReward Reward;
|
||||
}
|
||||
public class EventScratchTicketChangeScratchTypeData
|
||||
{
|
||||
public EventScratchTicketScratchType ScratchType;
|
||||
}
|
||||
public class EventScratchTicketRevealedAllData
|
||||
{
|
||||
public EventScratchTicketScratchType ScratchType;
|
||||
}
|
||||
public class EventScratchTicketStampRewardUpdateData
|
||||
{
|
||||
|
||||
}
|
||||
public class EventScratchTicketStartStampRewardCountUpdateData
|
||||
{
|
||||
|
||||
}
|
||||
public class EventScratchTicketStopStampRewardCountUpdateData
|
||||
{
|
||||
|
||||
}
|
||||
public class EventScratchTicketOwnedStampRewardData
|
||||
{
|
||||
public int EventScratchCardID;
|
||||
public int Count;
|
||||
}
|
||||
public class EventScratchTicketStampRewardFlyData
|
||||
{
|
||||
public EventScratchTicketScratchType ScratchType;
|
||||
public Vector3 SrcPosition;
|
||||
//public Vector3 DestPosition;
|
||||
public Vector3 LocalScale;
|
||||
public float IconSize;
|
||||
}
|
||||
public class EventScratchTicketRewardEmptyData
|
||||
{
|
||||
public EventScratchTicketScratchType ScratchType;
|
||||
}
|
||||
public class EventScratchTicketUpdateEventScratchCardData
|
||||
{
|
||||
|
||||
}
|
||||
public class EventScratchTicketPanelPlayEndAnimData
|
||||
{
|
||||
public EventScratchTicketScratchType ScratchType;
|
||||
}
|
||||
public class EventScratchTicketPlayScratchType5
|
||||
{
|
||||
|
||||
}
|
||||
public class EventScratchTicketStartMaskData
|
||||
{
|
||||
|
||||
}
|
||||
public class EventScratchTicketOutData
|
||||
{
|
||||
public EventScratchTicketScratchType ScratchType;
|
||||
}
|
||||
public class EventScratchTicketRecordData
|
||||
{
|
||||
public int TicketID;
|
||||
public int TicketFinishID;
|
||||
public int MilestoneTask;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,84 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using LitJson;
|
||||
using System;
|
||||
namespace GameCore
|
||||
{
|
||||
public partial class FishingEventData
|
||||
{
|
||||
string Pack1A1DataKey = "Pack1A1Data";
|
||||
public TriggerPackBuyData Pack1A1Data;
|
||||
void GiftFace1A1(int packType, int eventID)
|
||||
{
|
||||
if (Pack1A1Data == null || Pack1A1Data.index < 2 || Pack1A1Data.lastID != eventID)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUI(eventID, UITypes.GiftPopupPanel_10, packType);
|
||||
}
|
||||
}
|
||||
public void SetPack1A1(FishingEvent t)
|
||||
{
|
||||
if (Pack1A1Data == null)
|
||||
{
|
||||
Pack1A1Data = ReadEventArchive<TriggerPackBuyData>(Pack1A1DataKey);
|
||||
}
|
||||
EventPackManager eventPackDataRead = _tables.TbEventPackManager.DataMap[t.RedirectID];
|
||||
GiftFace1A1(eventPackDataRead.PackType, t.ID);
|
||||
if (Pack1A1Data != null && Pack1A1Data.lastID == t.ID)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Pack1A1Data = new TriggerPackBuyData()
|
||||
{
|
||||
ID = eventPackDataRead.ID,
|
||||
lastID = t.ID,//活动id
|
||||
index = 0,
|
||||
vipLevel = GContext.container.Resolve<PlayerData>().PriceLv,
|
||||
mapID = GContext.container.Resolve<PlayerData>().lastMapId,
|
||||
};
|
||||
RedPointManager.Instance.SetRedPointState(HomeBtnOneOnePack.redKey, false);
|
||||
|
||||
playerSaveStore.Write(Pack1A1DataKey, JsonMapper.ToJson(Pack1A1Data)).RequireAccepted();
|
||||
}
|
||||
public void SetPack1A1Index(int index)
|
||||
{
|
||||
if (Pack1A1Data == null || index < Pack1A1Data.index)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Pack1A1Data.index = index;
|
||||
if (Pack1A1Data.index >= 2)
|
||||
{
|
||||
//购买完 特殊处理
|
||||
GContext.Publish(new TargetEvent(Pack1A1Data.lastID, 3, 11));
|
||||
}
|
||||
RedPointManager.Instance.SetRedPointState(HomeBtnOneOnePack.redKey, Pack1A1Data.index == 1);
|
||||
playerSaveStore.Write(Pack1A1DataKey, JsonMapper.ToJson(Pack1A1Data)).RequireAccepted();
|
||||
}
|
||||
public int GetPack1A1Index()
|
||||
{
|
||||
if (Pack1A1Data == null)
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
return Pack1A1Data.index;
|
||||
}
|
||||
public EventPackManager Get1A1EventPackManager()
|
||||
{
|
||||
if (Pack1A1Data == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return _tables.TbEventPackManager.DataMap[Pack1A1Data.ID];
|
||||
}
|
||||
public DateTime GetEvent1A1EndTime()
|
||||
{
|
||||
if (Pack1A1Data == null)
|
||||
{
|
||||
return ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
}
|
||||
return GetEventEndTime(Pack1A1Data.lastID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using LitJson;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
namespace GameCore
|
||||
{
|
||||
public class Pack1A2Data
|
||||
{
|
||||
public Pack1A2Data(int ID,
|
||||
//活动id
|
||||
int eventId,
|
||||
int vipLevel)
|
||||
{
|
||||
this.ID = ID;
|
||||
this.lastID = eventId;
|
||||
this.index = 0;
|
||||
this.vipLevel = vipLevel;
|
||||
}
|
||||
public int ID;
|
||||
//领取第几个
|
||||
public int index;
|
||||
public int lastID;
|
||||
//触发当时VIP等级
|
||||
public int vipLevel;
|
||||
public int rodDstId;
|
||||
public int rodSrcId;
|
||||
}
|
||||
public partial class FishingEventData
|
||||
{
|
||||
string Pack1A2DataKey = "Pack1A2Data";
|
||||
public Pack1A2Data Pack1A2Data;
|
||||
void GiftFace1A2(EventPackManager packManager, int eventID)
|
||||
{
|
||||
if (Pack1A2Data == null)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUI(eventID, UITypes.GiftPopupPanel_11, packManager.PackType);
|
||||
}
|
||||
else
|
||||
{
|
||||
List<int> packIDs = packManager.VIPPackList[Pack1A2Data.vipLevel];
|
||||
if (Pack1A2Data.lastID != eventID || packIDs.Count > Pack1A2Data.index)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUI(eventID, UITypes.GiftPopupPanel_11, packManager.PackType);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public void SetPack1A2(FishingEvent t)
|
||||
{
|
||||
if (Pack1A2Data == null)
|
||||
{
|
||||
Pack1A2Data = ReadEventArchive<Pack1A2Data>(Pack1A2DataKey);
|
||||
}
|
||||
EventPackManager eventPackDataRead = _tables.TbEventPackManager.DataMap[t.RedirectID];
|
||||
|
||||
GiftFace1A2(eventPackDataRead, t.ID);
|
||||
if (Pack1A2Data != null && Pack1A2Data.lastID == t.ID)
|
||||
{
|
||||
SetType();
|
||||
SetChainPackRed();
|
||||
return;
|
||||
}
|
||||
|
||||
Pack1A2Data = new Pack1A2Data(
|
||||
eventPackDataRead.ID,
|
||||
t.ID,//活动id
|
||||
GContext.container.Resolve<PlayerData>().PriceLv
|
||||
);
|
||||
SetType();
|
||||
SetRodDstId();
|
||||
SetChainPackRed();
|
||||
playerSaveStore.Write(Pack1A2DataKey, JsonMapper.ToJson(Pack1A2Data)).RequireAccepted();
|
||||
}
|
||||
void SetType()
|
||||
{
|
||||
EventPackManager eventPackManager = Get1A2EventPackManager();
|
||||
if (eventPackManager != null)
|
||||
{
|
||||
UITypes.GiftPopupPanel_11.SetType(eventPackManager.Panel);
|
||||
}
|
||||
}
|
||||
void SetRodDstId()
|
||||
{
|
||||
Pack1A2Data.rodSrcId = 330040000;
|
||||
Item item = _tables.TbItem.GetOrDefault(Pack1A2Data.rodSrcId);
|
||||
Drop drop = _tables.TbDrop.GetOrDefault(item.RedirectID);
|
||||
List<int> rodIDs = drop.DropList.DropIDList;
|
||||
int level = -1;
|
||||
PlayerFishData playerFishData = GContext.container.Resolve<PlayerFishData>();
|
||||
for (int i = 0; i < rodIDs.Count; i++)
|
||||
{
|
||||
int curLevel = playerFishData.GetRodLevel(rodIDs[i]);
|
||||
if (curLevel > level)
|
||||
{
|
||||
level = curLevel;
|
||||
Pack1A2Data.rodDstId = rodIDs[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
public void SetPack1A2Index()
|
||||
{
|
||||
if (Pack1A2Data == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Pack1A2Data.index++;
|
||||
playerSaveStore.Write(Pack1A2DataKey, JsonMapper.ToJson(Pack1A2Data)).RequireAccepted();
|
||||
SetChainPackRed();
|
||||
}
|
||||
void SetChainPackRed()
|
||||
{
|
||||
bool isRed = false;
|
||||
EventPackManager eventPackManager = Get1A2EventPackManager();
|
||||
List<int> packIDs = eventPackManager.VIPPackList[Pack1A2Data.vipLevel];
|
||||
if (packIDs.Count > Pack1A2Data.index)
|
||||
{
|
||||
var pack = _tables.TbPack.GetOrDefault(packIDs[Pack1A2Data.index]);
|
||||
isRed = _tables.TbIAPItemList.GetOrDefault(pack.IAPID) == null;
|
||||
}
|
||||
else
|
||||
{
|
||||
//购买完 特殊处理
|
||||
GContext.Publish(new TargetEvent(Pack1A2Data.lastID, 6, 1));
|
||||
}
|
||||
RedPointManager.Instance.SetRedPointState(HomeBtnOneTwoPack.redKey, isRed);
|
||||
}
|
||||
public int GetPack1A2Index()
|
||||
{
|
||||
if (Pack1A2Data == null)
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
return Pack1A2Data.index;
|
||||
}
|
||||
public EventPackManager Get1A2EventPackManager()
|
||||
{
|
||||
if (Pack1A2Data == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return _tables.TbEventPackManager.DataMap[Pack1A2Data.ID];
|
||||
}
|
||||
public DateTime GetEvent1A2EndTime()
|
||||
{
|
||||
if (Pack1A2Data == null)
|
||||
{
|
||||
return ZZTimeHelper.UtcNow().UtcNowOffset(); ;
|
||||
}
|
||||
return GetEventEndTime(Pack1A2Data.lastID);
|
||||
}
|
||||
public List<ItemData> Get1A2ItemDatasBuyPack(int dropID)
|
||||
{
|
||||
List<ItemData> itemDatas = GContext.container.Resolve<PlayerItemData>().GetItemDataByDropIdLureInflation(null, dropID);
|
||||
for (int i = 0; i < itemDatas.Count; i++)
|
||||
{
|
||||
if (itemDatas[i].id == Pack1A2Data.rodSrcId)
|
||||
{
|
||||
itemDatas[i].id = Pack1A2Data.rodDstId;
|
||||
}
|
||||
}
|
||||
return itemDatas;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,282 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
namespace GameCore
|
||||
{
|
||||
public partial class FishingEventData : IDisposable
|
||||
{
|
||||
// void GetTransitionDataPVP(int subType = 0)
|
||||
// {
|
||||
// FishingEvent fishingEvent = null;
|
||||
// if (transitionDataDic != null)
|
||||
// {
|
||||
// foreach (var item in transitionDataDic)
|
||||
// {
|
||||
// FishingEvent fe = _tables.TbFishingEvent.GetOrDefault(item.Key);
|
||||
// if (fe != null && (subType == fe.SubType || subType == 0) && fe.Type == 5 && !Condition(fe))
|
||||
// {
|
||||
// fishingEvent = fe;
|
||||
// int count = transitionDataDic[fishingEvent.ID];
|
||||
// if (count > 0)
|
||||
// {
|
||||
// PvpEventCycleItem cycle = _tables.TbPvpEventCycleItem.
|
||||
// DataList.Find(x => x.Type == fe.Type && x.SubType == fe.SubType);
|
||||
// if (cycle != null)
|
||||
// {
|
||||
// SetTransitionData(fishingEvent.ID, cycle.EventName_l10n_key, cycle.ItemId, cycle.EventEndSettlement);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// transitionDataDic.Remove(fishingEvent.ID);
|
||||
// SaveTransitionData();
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
public int GetInitWelcomeGiftPVP(int eventID)
|
||||
{
|
||||
int welcomeGift = 0;
|
||||
if (eventID > 0)
|
||||
{
|
||||
FishingEvent fe = _tables.TbFishingEvent.GetOrDefault(eventID);
|
||||
if (fe != null)
|
||||
{
|
||||
PvpEventCycleItem cycle = _tables.TbPvpEventCycleItem.
|
||||
DataList.Find(x => x.Type == fe.Type && x.SubType == fe.SubType);
|
||||
if (cycle != null)
|
||||
{
|
||||
welcomeGift = cycle.WelcomeGift;
|
||||
}
|
||||
}
|
||||
}
|
||||
return welcomeGift;
|
||||
}
|
||||
public int GetRedDotPVP(int eventID)
|
||||
{
|
||||
int redDot = 10;
|
||||
if (eventID > 0)
|
||||
{
|
||||
FishingEvent fe = _tables.TbFishingEvent.GetOrDefault(eventID);
|
||||
if (fe != null)
|
||||
{
|
||||
PvpEventCycleItem cycle = _tables.TbPvpEventCycleItem.
|
||||
DataList.Find(x => x.Type == fe.Type && x.SubType == fe.SubType);
|
||||
if (cycle != null)
|
||||
{
|
||||
redDot = cycle.RedDot;
|
||||
}
|
||||
}
|
||||
}
|
||||
return redDot;
|
||||
}
|
||||
#region SoloData
|
||||
public DuelData duelData { get; private set; }
|
||||
int rankTierChangeState = 0;
|
||||
EventSolomain curSolomain;
|
||||
public int PVPToken { private set; get; }//pvp商店代币
|
||||
|
||||
public void SetRankTierChange(EventSolomain beforeSolomain, EventSolomain eventSolomain)
|
||||
{
|
||||
rankTierChangeState = beforeSolomain.RankTier > eventSolomain.RankTier ? 1 : 2;
|
||||
this.curSolomain = beforeSolomain;
|
||||
}
|
||||
public async void ShowRankTierChange()
|
||||
{
|
||||
var store = playerSaveStore;
|
||||
if (!store.IsCurrent) return;
|
||||
if (rankTierChangeState > 0 && curSolomain != null)
|
||||
{
|
||||
GameObject go = await UIManager.Instance.ShowUI(UITypes.EventFishingDuelRankChangePopupPanel);
|
||||
if (!store.IsCurrent) return;
|
||||
EventFishingDuelRankChangePopupPanel changePopupPanel = go.GetComponent<EventFishingDuelRankChangePopupPanel>();
|
||||
changePopupPanel.SetRank(rankTierChangeState == 1, curSolomain);
|
||||
rankTierChangeState = 0;
|
||||
curSolomain = null;
|
||||
}
|
||||
}
|
||||
public void SoloCheckClaimReward()
|
||||
{
|
||||
var loaded = ReadEventArchive<DuelData>(FishingDuelManager.SoloDataKey);
|
||||
if (loaded != null)
|
||||
{
|
||||
duelData = loaded;
|
||||
int fishingEventID = GContext.container.Resolve<FishingEventData>().GetEvent(5, 1);
|
||||
if (duelData.rewardEventID > 0 && duelData.preProgress > 0 && duelData.rewardEventID != fishingEventID)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddFaceCustomUIData(UITypes.EventFishingDuelSettlementPanel, true);
|
||||
//_ = UIManager.Instance.ShowUI(UITypes.EventFishingDuelSettlementPanel);
|
||||
}
|
||||
//GetTransitionDataPVP(1);
|
||||
}
|
||||
}
|
||||
//领取赛季奖励
|
||||
public void ClaimReward()
|
||||
{
|
||||
if (duelData.rewardEventID > 0 && duelData.preProgress > 0)
|
||||
{
|
||||
duelData.soloTimes = 0;
|
||||
duelData.rewardEventID = 0;
|
||||
duelData.preProgress = 0;
|
||||
string json_duelData = Newtonsoft.Json.JsonConvert.SerializeObject(duelData);
|
||||
playerSaveStore.Write(FishingDuelManager.SoloDataKey, json_duelData).RequireAccepted();
|
||||
}
|
||||
}
|
||||
public void GMAddSoloProgress(int count)
|
||||
{
|
||||
if (duelData != null)
|
||||
{
|
||||
duelData.progress = count;
|
||||
SaveDuelData();
|
||||
}
|
||||
}
|
||||
public void InitSoloData(FishingEvent t)
|
||||
{
|
||||
var loaded = ReadEventArchive<DuelData>(FishingDuelManager.SoloDataKey);
|
||||
if (loaded != null)
|
||||
{
|
||||
duelData = loaded;
|
||||
}
|
||||
else
|
||||
{
|
||||
duelData = new DuelData();
|
||||
duelData.progress = _tables.TbEventSoloConfig.InitTrophy;
|
||||
}
|
||||
|
||||
if (t.ID != duelData.eventID)
|
||||
{
|
||||
duelData.eventID = t.ID;
|
||||
int min = GetInitWelcomeGiftPVP(t.ID);
|
||||
if (duelData.tickets < min)
|
||||
{
|
||||
duelData.tickets = min;
|
||||
}
|
||||
duelData.soloTimes = 0;
|
||||
duelData.rePVPToken = 0;
|
||||
duelData.buyItem.Clear();
|
||||
int reset = duelData.progress - _tables.TbEventSoloConfig.MinTrophy;
|
||||
if (reset > 0)
|
||||
{
|
||||
//duelData.progress = _tables.TbEventSoloConfig.SettlementTrophy +
|
||||
// (int)(reset * _tables.TbEventSoloConfig.SettlementTrophyMultiplier);
|
||||
|
||||
var data = _tables.TbEventSolomain.DataList;
|
||||
var solomain = data[0];
|
||||
if (duelData.progress > 0)
|
||||
{
|
||||
for (int i = data.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (duelData.progress >= data[i].TrophyRange[0])
|
||||
{
|
||||
solomain = data[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
duelData.progress -= solomain.SettleDeducTrophy;
|
||||
}
|
||||
//duelData.progress /= _tables.TbEventSoloConfig.TrophyMutiple;
|
||||
//duelData.progress *= _tables.TbEventSoloConfig.TrophyMutiple;
|
||||
if (duelData.progress < _tables.TbEventSoloConfig.MinTrophy)
|
||||
{
|
||||
duelData.progress = _tables.TbEventSoloConfig.MinTrophy;
|
||||
}
|
||||
//GContext.Publish(new TargetEvent(t.ID, 5, 1));
|
||||
SaveDuelData();
|
||||
}
|
||||
}
|
||||
public void AddDuelTickets(int tickets)
|
||||
{
|
||||
if (duelData != null)
|
||||
{
|
||||
if (tickets < 0)
|
||||
{
|
||||
duelData.soloTimes++;
|
||||
if (duelData.soloTimes >= _tables.TbEventSoloConfig.ClaimRewardMatchCount)
|
||||
{
|
||||
duelData.rewardEventID = duelData.eventID;
|
||||
duelData.preProgress = duelData.progress;
|
||||
}
|
||||
}
|
||||
duelData.tickets += tickets;
|
||||
//SaveTransitionData(duelData.eventID, duelData.tickets);
|
||||
SaveDuelData();
|
||||
}
|
||||
}
|
||||
public int GetDuelTickets()
|
||||
{
|
||||
if (duelData != null)
|
||||
{
|
||||
return duelData.tickets;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public bool BuyPVPItem(int id)
|
||||
{
|
||||
if (duelData != null && !duelData.buyItem.ContainsKey(id))
|
||||
{
|
||||
duelData.buyItem[id] = 1;
|
||||
SaveDuelData();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public bool IsBuyPVPItem(int id)
|
||||
{
|
||||
if (duelData != null && duelData.buyItem.ContainsKey(id))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public DateTime SetPVPShopTime()
|
||||
{
|
||||
DateTime dateTime = ZZTimeHelper.UtcNow().UtcNowOffset().Date;
|
||||
int offfset = (int)dateTime.DayOfWeek - 1;
|
||||
|
||||
if (offfset < 0)
|
||||
{
|
||||
offfset = -offfset;
|
||||
}
|
||||
else
|
||||
{
|
||||
offfset = 7 - offfset;
|
||||
}
|
||||
DateTime endTime = dateTime.AddDays(offfset);
|
||||
if (endTime.DayOfYear != duelData.pvpShopTimeDay)
|
||||
{
|
||||
if (duelData.pvpShopTimeDay != -1)
|
||||
{
|
||||
duelData.buyItem.Clear();
|
||||
}
|
||||
duelData.inflation = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
duelData.pvpShopTimeDay = endTime.DayOfYear;
|
||||
SaveDuelData();
|
||||
}
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public void AddPVPToken(int value)
|
||||
{
|
||||
PVPToken += value;
|
||||
playerSaveStore.Write("PVPToken", PVPToken.ToString()).RequireAccepted();
|
||||
if (value < 0 && duelData != null)
|
||||
{
|
||||
duelData.rePVPToken -= value;
|
||||
SaveDuelData();
|
||||
}
|
||||
}
|
||||
public void SaveDuelData()
|
||||
{
|
||||
string json_duelData = Newtonsoft.Json.JsonConvert.SerializeObject(duelData);
|
||||
playerSaveStore.Write(FishingDuelManager.SoloDataKey, json_duelData).RequireAccepted();
|
||||
}
|
||||
#endregion SoloData
|
||||
}
|
||||
}
|
||||
@@ -1,573 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DataCenter
|
||||
{
|
||||
// 锁链礼包
|
||||
|
||||
public class TOfferChainsData
|
||||
{
|
||||
public class TItem
|
||||
{
|
||||
public int Id;
|
||||
public int Item;
|
||||
public int Number;
|
||||
// 0 可选,1 高亮 2 糊了 ,锁链隐藏 应该转换成Enum
|
||||
public int Status;
|
||||
// public int SKUId;
|
||||
}
|
||||
public bool FirstOpen; // 是否首次打开
|
||||
public int EventId; // 默认存一下ID
|
||||
public DateTime StartTime; // 开始时间
|
||||
public DateTime EndTime;// 结束时间
|
||||
public int ItemId = -1;
|
||||
// 当前抽奖次数,必须抽了才会更新
|
||||
public int LotteryNum;
|
||||
public List<TItem> Items;
|
||||
public bool Finished;
|
||||
public int ChestReward;
|
||||
public List<int> RewardList; // 当前获得的奖励Item
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class OfferChainsChestManager
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore playerSaveStore { get; set; }
|
||||
|
||||
public const string SaveKey = "OfferChainsChestData";
|
||||
[Inject] public Tables _tables { get; set; }
|
||||
// 计入当前Event
|
||||
private FishingEvent _fishingEvent;
|
||||
//
|
||||
public EventOfferChainsChestMain OfferChainsChestMain { get; set; }
|
||||
public List<EventOfferChainsChestRewards> OfferChainsChestRewards { get; set; }
|
||||
// 当前可保存数据
|
||||
public TOfferChainsData OfferChainsData { get; private set; }
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVOfferState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodeOffer(raw,false,snapshot,errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
OfferChainsData=null; ValidIds=new List<int>(); _fishingEvent=null; _isInit=false;
|
||||
OfferChainsChestMain=null; OfferChainsChestRewards=null;
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVOfferState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
OfferChainsData=!state.IsPresent ? null : new TOfferChainsData
|
||||
{
|
||||
FirstOpen=state.FirstOpen,EventId=state.EventId,StartTime=state.StartTime,EndTime=state.EndTime,
|
||||
ItemId=state.ItemId,LotteryNum=state.LotteryNum,
|
||||
Items=state.Items.Select(item=>new TOfferChainsData.TItem {
|
||||
Id=item.Id,Item=item.Item,Number=item.Number,Status=item.Status }).ToList(),
|
||||
RewardList=new List<int>(state.RewardList),Finished=state.Finished,ChestReward=state.ChestReward
|
||||
};
|
||||
}
|
||||
internal bool IsInactiveForKVValidation => !_isInit && _fishingEvent == null;
|
||||
public List<int> ValidIds;
|
||||
|
||||
public bool IsFirstOpen
|
||||
{
|
||||
get => OfferChainsData.FirstOpen;
|
||||
set
|
||||
{
|
||||
if (OfferChainsData.FirstOpen)
|
||||
{
|
||||
OfferChainsData.FirstOpen = false;
|
||||
SaveData();
|
||||
}
|
||||
}
|
||||
}
|
||||
public void UpdateEventData(FishingEvent fishingEvent)
|
||||
{
|
||||
ELog($"UpdateEventData - > {fishingEvent}");
|
||||
_fishingEvent = fishingEvent;
|
||||
UpdateLocalData();
|
||||
}
|
||||
private void UpdateLocalData()
|
||||
{
|
||||
//选择当前的 Main
|
||||
var redirectId = _fishingEvent.RedirectID;
|
||||
var configList = _tables.TbEventOfferChainsChestMain.DataList;
|
||||
foreach (var eventChallenge in configList.Where(eventChallenge => eventChallenge.ID == redirectId))
|
||||
{
|
||||
OfferChainsChestMain = eventChallenge;
|
||||
break;
|
||||
}
|
||||
OfferChainsChestRewards = _tables.TbEventOfferChainsChestRewards.DataList;
|
||||
// SkuIds = OfferChainsChestMain.IapList;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private bool _isInit = false;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
ELog("Init-> ");
|
||||
|
||||
if (_fishingEvent == null)
|
||||
return;
|
||||
//-
|
||||
if (_fishingEvent.ID != OfferChainsData?.EventId)
|
||||
{
|
||||
ClearOfferChainsData();
|
||||
}
|
||||
|
||||
if (_isInit)
|
||||
return;
|
||||
|
||||
if (OfferChainsData == null)
|
||||
{
|
||||
if (_fishingEvent == null) // 不存在Event
|
||||
{
|
||||
return;
|
||||
}
|
||||
InitOfferChainsData();
|
||||
// CreateNewData();
|
||||
}
|
||||
UpdateOfferChainsData();
|
||||
//UpdateData()
|
||||
_isInit = true;
|
||||
ELog("初始化完成-> ");
|
||||
}
|
||||
//
|
||||
private void ProcessLastOne()
|
||||
{
|
||||
ELog("ProcessLastOne-> ");
|
||||
if (OfferChainsData != null)
|
||||
{
|
||||
Settle();
|
||||
// if (!IsTimeAllow())
|
||||
// {
|
||||
// Settle();
|
||||
// // ClearOfferChainsData();
|
||||
// }
|
||||
// // else if (IsCanGetFinalReward())
|
||||
// else
|
||||
// {
|
||||
// Settle(); // 补充一下最终大奖
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateOfferChainsData()
|
||||
{
|
||||
ELog("UpdateOfferChainsData");
|
||||
// 更新可用数据
|
||||
UpdateTime();
|
||||
UpdateOfferChainsItems();
|
||||
UpdateValidIds();
|
||||
GetLotteryItemId();
|
||||
//-
|
||||
if (_fishingEvent != null && OfferChainsData != null)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUI(_fishingEvent.ID, new UIType(OfferChainsChestMain.UIPanel), 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateValidIds()
|
||||
{
|
||||
var lotteryNum = OfferChainsData.LotteryNum;
|
||||
ValidIds = (from item in OfferChainsData?.Items where item.Status == 0 select item.Id).ToList();
|
||||
}
|
||||
|
||||
|
||||
private void InitOfferChainsData()
|
||||
{
|
||||
var limitedTime = (LimitedTime)_fishingEvent.TimeDefinition;
|
||||
var startTime = GlobalUtils.TryParseDateTime(limitedTime.StartTime, ZZTimeHelper.UtcNow());
|
||||
var endTime = GlobalUtils.TryParseDateTime(limitedTime.EndTime, ZZTimeHelper.UtcNow());
|
||||
|
||||
|
||||
OfferChainsData = new TOfferChainsData
|
||||
{
|
||||
FirstOpen = true,
|
||||
EventId = _fishingEvent.ID,
|
||||
StartTime = startTime,
|
||||
EndTime = endTime,
|
||||
Items = new List<TOfferChainsData.TItem>(),
|
||||
RewardList = new List<int>(),
|
||||
ChestReward = OfferChainsChestMain.ChestReward, // 最终大奖存储下,方便额外的处理
|
||||
};
|
||||
|
||||
for (var i = 0; i < OfferChainsChestRewards.Count; ++i)
|
||||
{
|
||||
var item = new TOfferChainsData.TItem
|
||||
{
|
||||
Id = OfferChainsChestRewards[i].ID,
|
||||
Item = OfferChainsChestRewards[i].Item,
|
||||
Number = OfferChainsChestRewards[i].Number,
|
||||
Status = 0,
|
||||
// SKUId = OfferChainsChestMain.IapList[i],
|
||||
};
|
||||
OfferChainsData.Items.Add(item);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public bool IsTimeAllow()
|
||||
{
|
||||
var now = ZZTimeHelper.UtcNow();
|
||||
|
||||
var startTime = OfferChainsData.StartTime;
|
||||
var endTime = OfferChainsData.EndTime;
|
||||
#if UNITY_EDITOR
|
||||
var timeOpened = (now - startTime).TotalSeconds;
|
||||
var timeEnded = (endTime - now).TotalSeconds;
|
||||
ELog($"StartTime: {startTime} Now: {now} EndTime:{endTime} TimeOpened:{timeOpened} TimeToEnd:{timeEnded} Other: {1800000 - timeOpened} IsTimeAllow => {now <= endTime}");
|
||||
#endif
|
||||
return now >= startTime && now <= endTime;
|
||||
}
|
||||
public void CheckInit()
|
||||
{
|
||||
ELog("CheckInit -> ");
|
||||
|
||||
ProcessLastOne();
|
||||
Init();
|
||||
}
|
||||
|
||||
// ReSharper disable Unity.PerformanceAnalysis
|
||||
private static void Log(object t)
|
||||
{
|
||||
Debug.Log($"<color=yellow>OfferChainsChestManager -> {t} </color>");
|
||||
}
|
||||
|
||||
private static void ELog(object t)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"<color=yellow>OfferChainsChestManager -> {t} </color>");
|
||||
#endif
|
||||
}
|
||||
|
||||
public bool CheckOpen()
|
||||
{
|
||||
//
|
||||
if (OfferChainsData == null) return false;
|
||||
//
|
||||
if (!IsTimeAllow()) return false;
|
||||
|
||||
if (OfferChainsChestMain == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsFinished())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsFinished()
|
||||
{
|
||||
return OfferChainsData?.Finished ?? false;
|
||||
}
|
||||
|
||||
private void SaveData()
|
||||
{
|
||||
ELog($"SaveData-> {JsonConvert.SerializeObject(OfferChainsData)}");
|
||||
playerSaveStore.Write(SaveKey, JsonConvert.SerializeObject(OfferChainsData)).RequireAccepted();
|
||||
}
|
||||
|
||||
public void LoadData(string dataValue)
|
||||
{
|
||||
ELog($"LoadData -> {dataValue}");
|
||||
OfferChainsData = JsonConvert.DeserializeObject<TOfferChainsData>(dataValue);
|
||||
}
|
||||
|
||||
// 进入游戏舞台
|
||||
public async void OnEnterGameAct()
|
||||
{
|
||||
if (!playerSaveStore.IsCurrent) return;
|
||||
Log("OnEnterGameAct()");
|
||||
IsFirstOpen = false;
|
||||
await UIManager.Instance.ShowUI(new UIType(OfferChainsChestMain.UIPanel));
|
||||
}
|
||||
|
||||
public static T WeightedRandom<T>(Dictionary<T, int> weightTable)
|
||||
{
|
||||
// 1. 计算总权重
|
||||
var totalWeight = weightTable.Values.Sum();
|
||||
// 2. 生成随机数
|
||||
var randomValue = UnityEngine.Random.Range(0, totalWeight);
|
||||
|
||||
// 3. 线性遍历选择
|
||||
var currentWeight = 0;
|
||||
foreach (var (key, value) in weightTable)
|
||||
{
|
||||
currentWeight += value;
|
||||
if (randomValue < currentWeight)
|
||||
return key;
|
||||
}
|
||||
return default; // 理论上不会执行到这里
|
||||
}
|
||||
|
||||
// 活动当前的抽奖索引
|
||||
private int CalcLotteryItemId()
|
||||
{
|
||||
var lotteryNum = OfferChainsData.LotteryNum;
|
||||
if (ValidIds.Count <= 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (OfferChainsChestRewards == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// while (true)
|
||||
// {
|
||||
var dict = OfferChainsChestRewards.Where(
|
||||
item => ValidIds.Contains(item.ID) && item.LimitConfig <= lotteryNum)
|
||||
.ToDictionary(elem => (elem.ID, elem.LimitConfig), elem => elem.Weight);
|
||||
var (elemId, limitConfig) = WeightedRandom(dict);
|
||||
Log($"CalcLotteryItemId -> {lotteryNum} {elemId} {limitConfig}");
|
||||
// if (limitConfig >0 && lotteryNum > limitConfig )
|
||||
return elemId;
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
public int GetLotteryItemId()
|
||||
{
|
||||
ELog($"GetLotteryItemId -> {OfferChainsData.ItemId}");
|
||||
if (OfferChainsData.ItemId == -1 || !ValidIds.Contains(OfferChainsData.ItemId))
|
||||
{
|
||||
UpdateLotteryItemId();
|
||||
}
|
||||
return OfferChainsData.ItemId;
|
||||
}
|
||||
|
||||
private void UpdateOfferChainsItems()
|
||||
{
|
||||
if (OfferChainsData != null && _fishingEvent != null && OfferChainsData.EventId == _fishingEvent.ID)
|
||||
{
|
||||
var items = OfferChainsData.Items;
|
||||
if (OfferChainsChestRewards is { Count: > 0 })
|
||||
{
|
||||
for (int i = 0; i < items.Count; ++i)
|
||||
{
|
||||
EventOfferChainsChestRewards reward = null;
|
||||
if (i < OfferChainsChestRewards.Count)
|
||||
{
|
||||
reward = OfferChainsChestRewards[i];
|
||||
}
|
||||
if (reward != null)
|
||||
{
|
||||
items[i].Id = reward.ID;
|
||||
items[i].Item = reward.Item;
|
||||
items[i].Number = reward.Number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (OfferChainsChestMain != null)
|
||||
{
|
||||
OfferChainsData.ChestReward = OfferChainsChestMain.ChestReward;
|
||||
}
|
||||
}
|
||||
|
||||
// for (var i = 0; i < OfferChainsChestRewards.Count; ++i)
|
||||
// {
|
||||
// var item = new TOfferChainsData.TItem
|
||||
// {
|
||||
// Id = OfferChainsChestRewards[i].ID,
|
||||
// Item = OfferChainsChestRewards[i].Item,
|
||||
// Number = OfferChainsChestRewards[i].Number,
|
||||
// Status = 0,
|
||||
// };
|
||||
// OfferChainsData.Items.Add(item);
|
||||
// }
|
||||
}
|
||||
|
||||
private void UpdateTime()
|
||||
{
|
||||
if (OfferChainsData != null && _fishingEvent != null && OfferChainsData.EventId == _fishingEvent.ID)
|
||||
{
|
||||
var limitedTime = (LimitedTime)_fishingEvent.TimeDefinition;
|
||||
var startTime = GlobalUtils.TryParseDateTime(limitedTime.StartTime, ZZTimeHelper.UtcNow());
|
||||
var endTime = GlobalUtils.TryParseDateTime(limitedTime.EndTime, ZZTimeHelper.UtcNow().AddDays(-1));
|
||||
OfferChainsData.StartTime = startTime;
|
||||
OfferChainsData.EndTime = endTime;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLotteryItemId()
|
||||
{
|
||||
var targetId = CalcLotteryItemId();
|
||||
ELog($"UpdateLotteryItemId -> {OfferChainsData.ItemId} => {targetId}");
|
||||
OfferChainsData.ItemId = targetId;
|
||||
SaveData();
|
||||
}
|
||||
|
||||
//- 结束当前
|
||||
public void FinishLottery(int targetId)
|
||||
{
|
||||
Log($"FinishLottery -> {targetId}");
|
||||
var lastItem = OfferChainsData.Items.SingleOrDefault(it => it.Id == targetId);
|
||||
if (lastItem is { Status: not 2 })
|
||||
{
|
||||
lastItem.Status = 2;
|
||||
}
|
||||
}
|
||||
|
||||
//- 开始下一轮数据
|
||||
public void UpdateForNextLottery()
|
||||
{
|
||||
UpdateValidIds();
|
||||
UpdateLotteryItemId();
|
||||
}
|
||||
|
||||
public bool IsCanGetFinalReward()
|
||||
{
|
||||
var ret = OfferChainsData.LotteryNum >= OfferChainsData.Items.Count
|
||||
&& !OfferChainsData.Finished;
|
||||
|
||||
ELog($"IsCanGetFinalReward {ret} : {OfferChainsData.LotteryNum} {OfferChainsData.Items.Count} {OfferChainsData.Finished}");
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void ClearOfferChainsData()
|
||||
{
|
||||
OfferChainsData = null;
|
||||
_isInit = false;
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public TOfferChainsData.TItem GetTItemById(int targetId)
|
||||
{
|
||||
var lastItem = OfferChainsData.Items.FirstOrDefault(it => it.Id == targetId);
|
||||
return lastItem;
|
||||
}
|
||||
|
||||
public int GetDropIdById(int id)
|
||||
{
|
||||
var item = GetTItemById(id);
|
||||
var itemData = _tables.TbItem.GetOrDefault(item.Item);
|
||||
return itemData.RedirectID;
|
||||
}
|
||||
public void OnGetFinalReward()
|
||||
{
|
||||
ELog("OnGetFinalReward()-> ");
|
||||
|
||||
// var item = _tables.TbItem.GetOrDefault(OfferChainsChestMain.ChestReward);
|
||||
var item = _tables.TbItem.GetOrDefault(OfferChainsData.ChestReward);
|
||||
var itemFinal = GContext.container.Resolve<PlayerItemData>()
|
||||
.GetItemDropItemList(item.RedirectID, 1);
|
||||
GContext.container.Resolve<PlayerItemData>().LureInflation(itemFinal, null);
|
||||
GContext.container.Resolve<PlayerItemData>().AddItem(itemFinal, null);
|
||||
GContext.Publish(new ShowData(itemFinal));
|
||||
GContext.Publish(new ShowData());
|
||||
OfferChainsData.Finished = true;
|
||||
SaveData();
|
||||
GContext.Publish(new OfferChainsRefreshEvent());
|
||||
}
|
||||
public float GetProbability(int id)
|
||||
{
|
||||
var theIdWeight = OfferChainsChestRewards.FirstOrDefault(item => item.ID == id)?.Weight ?? 0;
|
||||
var weightTable = OfferChainsChestRewards
|
||||
.Where(item => !IsItemFinished(item.ID))
|
||||
.Select(item => item.Weight).ToList();
|
||||
var totalWeight = weightTable.Sum();
|
||||
return (float)Math.Round((float)theIdWeight / totalWeight * 100, 2);
|
||||
}
|
||||
|
||||
private bool IsItemFinished(int itemID)
|
||||
{
|
||||
var lastItem = OfferChainsData.Items.SingleOrDefault(it => it.Id == itemID);
|
||||
return lastItem?.Status == 2;
|
||||
}
|
||||
|
||||
public TimeSpan GetTimeRemain()
|
||||
{
|
||||
if (OfferChainsData != null)
|
||||
{
|
||||
return OfferChainsData.EndTime - ZZTimeHelper.UtcNow();
|
||||
}
|
||||
// 活动未开始
|
||||
return ZZTimeHelper.UtcNow().AddDays(-1) - ZZTimeHelper.UtcNow();
|
||||
}
|
||||
public void Settle()
|
||||
{
|
||||
ELog("Settle");
|
||||
if (IsCanGetFinalReward())
|
||||
{
|
||||
OnGetFinalReward();
|
||||
}
|
||||
}
|
||||
|
||||
public bool UIRunning { get; set; } = false;
|
||||
public void OnBuySuccess()
|
||||
{
|
||||
Log("OnBuySuccess -> ");
|
||||
OfferChainsData.RewardList.Add(OfferChainsData.ItemId);
|
||||
OfferChainsData.LotteryNum += 1;
|
||||
// 为了处理打点
|
||||
if (IsCanGetFinalReward())
|
||||
{
|
||||
OfferChainsData.RewardList.Add(OfferChainsData.ChestReward);
|
||||
}
|
||||
FinishLottery(OfferChainsData.ItemId);
|
||||
SaveData(); // -存
|
||||
EventTracking();
|
||||
|
||||
|
||||
//NEXT TURN
|
||||
if (UIRunning)
|
||||
{ // 播放动画逻辑,交给UI
|
||||
return;
|
||||
}
|
||||
UpdateForNextLottery();
|
||||
Settle();
|
||||
}
|
||||
|
||||
public void EventTracking()
|
||||
{
|
||||
//var price = GetPayAmountById(OfferChainsData.ItemId);
|
||||
var price = GetIapItemListThisTime(OfferChainsData.LotteryNum - 1)?.PaymentAmount ?? 0F;
|
||||
var itemEnt = GetTItemById(OfferChainsData.ItemId);
|
||||
var rewardStr = string.Join(',', OfferChainsData.RewardList);
|
||||
var number = OfferChainsData.LotteryNum;
|
||||
#if AGG
|
||||
using (var e = GEvent.TackEvent("offer_chainchest"))
|
||||
{
|
||||
e.AddContent("event_id", OfferChainsData.EventId);
|
||||
e.AddContent("number", number);
|
||||
e.AddContent("price", price);
|
||||
e.AddContent("reward", OfferChainsData.ItemId);
|
||||
e.AddContent("reward_claimed_list", rewardStr);
|
||||
}
|
||||
#endif
|
||||
Log($"EventTracking: event_id: {OfferChainsData.EventId} number: {number} price: {price} " +
|
||||
$"reward :{OfferChainsData.ItemId} reward_claimed_list :{rewardStr}");
|
||||
}
|
||||
|
||||
public IAPItemList GetIapItemListThisTime()
|
||||
{
|
||||
return GetIapItemListThisTime(OfferChainsData.LotteryNum);
|
||||
}
|
||||
|
||||
private IAPItemList GetIapItemListThisTime(int index)
|
||||
{
|
||||
// var index = OfferChainsData.LotteryNum;
|
||||
index = Mathf.Clamp(index, 0, OfferChainsChestMain.IapList.Count - 1);
|
||||
var iapId = OfferChainsChestMain.IapList[index];
|
||||
var iAPItemList = _tables.TbIAPItemList.GetOrDefault(iapId);
|
||||
return iAPItemList;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,588 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DataCenter
|
||||
{
|
||||
// 数据保存
|
||||
public enum TItemStatues
|
||||
{
|
||||
Valid = 0,
|
||||
HighLight = 1,
|
||||
Done = 2,
|
||||
}
|
||||
public class TOfferDiscoPartyData
|
||||
{
|
||||
public class TItem
|
||||
{
|
||||
public int Id;
|
||||
public int Item;
|
||||
public int Number;
|
||||
|
||||
// 0 可选,1 高亮 2 糊了 ,锁链隐藏 应该转换成Enum
|
||||
public TItemStatues Status;
|
||||
}
|
||||
|
||||
public bool FirstOpen; // 是否首次打开
|
||||
public int EventId; // 默认存一下ID
|
||||
public DateTime StartTime; // 开始时间
|
||||
public DateTime EndTime; // 结束时间
|
||||
public int ItemId = -1;
|
||||
public int LotteryNum;
|
||||
// public bool Finished;
|
||||
|
||||
// 这个看看能否删掉,只保留状态
|
||||
public List<TItem> Items;
|
||||
public List<int> RewardList; // 当前获得的奖励Item
|
||||
}
|
||||
|
||||
// Disco Manager
|
||||
public class OfferDiscoPartyManager
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore playerSaveStore { get; set; }
|
||||
public const string SaveKey = "OfferDiscoPartyManager";
|
||||
|
||||
// 免费抽奖次数
|
||||
public int FreeTime = 1;
|
||||
|
||||
[Inject] public Tables _tables { get; set; }
|
||||
|
||||
#region 数据
|
||||
|
||||
private FishingEvent _fishingEvent;
|
||||
|
||||
public EventOfferDiscoParty OfferDiscoParty { get; private set; }
|
||||
|
||||
public List<EventOfferDiscoPartyReward> OfferDiscoPartyRewards { get; private set; }
|
||||
|
||||
public TOfferDiscoPartyData OfferDiscoPartyData { get; private set; }
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVOfferState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodeOffer(raw,true,snapshot,errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
OfferDiscoPartyData=null; ValidIds=new List<int>(); _fishingEvent=null; _isInit=false;
|
||||
OfferDiscoParty=null; OfferDiscoPartyRewards=null; FreeTime=1;
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVOfferState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
OfferDiscoPartyData=!state.IsPresent ? null : new TOfferDiscoPartyData
|
||||
{
|
||||
FirstOpen=state.FirstOpen,EventId=state.EventId,StartTime=state.StartTime,EndTime=state.EndTime,
|
||||
ItemId=state.ItemId,LotteryNum=state.LotteryNum,
|
||||
Items=state.Items.Select(item=>new TOfferDiscoPartyData.TItem {
|
||||
Id=item.Id,Item=item.Item,Number=item.Number,Status=(TItemStatues) item.Status }).ToList(),
|
||||
RewardList=new List<int>(state.RewardList)
|
||||
};
|
||||
}
|
||||
internal bool IsInactiveForKVValidation => !_isInit && _fishingEvent == null;
|
||||
|
||||
|
||||
public List<int> ValidIds; //
|
||||
|
||||
#endregion
|
||||
|
||||
public bool IsFirstOpen
|
||||
{
|
||||
get => OfferDiscoPartyData?.FirstOpen?? false;
|
||||
set
|
||||
{
|
||||
if (OfferDiscoPartyData?.FirstOpen??false)
|
||||
{
|
||||
OfferDiscoPartyData.FirstOpen = false;
|
||||
SaveData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateEventData(FishingEvent fishingEvent)
|
||||
{
|
||||
ELog($"UpdateEventData - > {fishingEvent}");
|
||||
_fishingEvent = fishingEvent;
|
||||
UpdateLocalData();
|
||||
}
|
||||
|
||||
private void UpdateLocalData()
|
||||
{
|
||||
//选择当前的 Main
|
||||
var redirectId = _fishingEvent.RedirectID;
|
||||
var configList = _tables.TbEventOfferDiscoParty.DataList;
|
||||
foreach (var eventChallenge in configList.Where(eventChallenge => eventChallenge.ID == redirectId))
|
||||
{
|
||||
OfferDiscoParty = eventChallenge;
|
||||
break;
|
||||
}
|
||||
|
||||
FreeTime = OfferDiscoParty.Free;
|
||||
|
||||
var rewardList = OfferDiscoParty.RewardList;
|
||||
// OfferDiscoPartyRewards = _tables.TbEventOfferDiscoPartyReward.DataList;
|
||||
var list = _tables.TbEventOfferDiscoPartyReward.DataList;
|
||||
//TODO:LF 这个筛选未应用到程序中,看策划的应用
|
||||
//
|
||||
OfferDiscoPartyRewards = list.Where(item => rewardList.Contains(item.ID)).ToList();
|
||||
OfferDiscoPartyRewards.Sort((a,b) => a.ID.CompareTo(b.ID));
|
||||
|
||||
//添加拍脸
|
||||
|
||||
// _tables.TbEventOfferDiscoParty
|
||||
}
|
||||
|
||||
private bool _isInit = false;
|
||||
public void Init()
|
||||
{
|
||||
ELog("Init-> ");
|
||||
if (_fishingEvent == null)
|
||||
return;
|
||||
|
||||
if (_fishingEvent.ID != OfferDiscoPartyData?.EventId)
|
||||
{
|
||||
ClearOfferDiscoPartyData();
|
||||
}
|
||||
|
||||
if (_isInit)
|
||||
return;
|
||||
|
||||
|
||||
if (OfferDiscoPartyData == null)
|
||||
{
|
||||
if (_fishingEvent == null) // 不存在Event
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InitOfferDiscoPartyData();
|
||||
}
|
||||
UpdateOfferDiscoPartyData();
|
||||
_isInit = true;
|
||||
ELog("初始化完成-> ");
|
||||
}
|
||||
|
||||
private void ProcessLastOne()
|
||||
{
|
||||
if (OfferDiscoPartyData != null)
|
||||
{
|
||||
Settle();
|
||||
// if (!IsTimeAllow())
|
||||
// {
|
||||
// Settle();
|
||||
// ClearOfferDiscoPartyData();
|
||||
// }
|
||||
// else if (IsFinished())
|
||||
// {
|
||||
// Settle(); // 补充一下最终大奖
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void InitOfferDiscoPartyData()
|
||||
{
|
||||
var limitedTime = (LimitedTime)_fishingEvent.TimeDefinition;
|
||||
var startTime = GlobalUtils.TryParseDateTime(limitedTime.StartTime, ZZTimeHelper.UtcNow());
|
||||
var endTime = GlobalUtils.TryParseDateTime(limitedTime.EndTime, ZZTimeHelper.UtcNow());
|
||||
|
||||
|
||||
OfferDiscoPartyData = new TOfferDiscoPartyData
|
||||
{
|
||||
FirstOpen = true,
|
||||
EventId = _fishingEvent.ID,
|
||||
StartTime = startTime,
|
||||
EndTime = endTime,
|
||||
Items = new List<TOfferDiscoPartyData.TItem>(),
|
||||
RewardList = new List<int>()
|
||||
};
|
||||
|
||||
for (var i = 0; i < OfferDiscoPartyRewards.Count; ++i)
|
||||
{
|
||||
var item = new TOfferDiscoPartyData.TItem
|
||||
{
|
||||
Id = OfferDiscoPartyRewards[i].ID,
|
||||
Item = OfferDiscoPartyRewards[i].Item,
|
||||
Number = OfferDiscoPartyRewards[i].Number,
|
||||
Status = 0,
|
||||
};
|
||||
OfferDiscoPartyData.Items.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateOfferDiscoPartyData()
|
||||
{
|
||||
ELog("UpdateOfferDiscoPartyData");
|
||||
// 更新可用数据
|
||||
OfferDiscoPartyData.EventId = _fishingEvent?.ID ?? OfferDiscoPartyData.EventId;
|
||||
|
||||
UpdateTime();
|
||||
UpdateOfferDiscoPartyItems();
|
||||
UpdateValidIds();
|
||||
// UpdateLotteryItemId();
|
||||
GetLotteryItemId();
|
||||
|
||||
if (_fishingEvent != null)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUI(_fishingEvent.ID, new UIType(OfferDiscoParty.UIPanel), 0);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsTimeAllow()
|
||||
{
|
||||
var now = ZZTimeHelper.UtcNow();
|
||||
|
||||
var startTime = OfferDiscoPartyData.StartTime;
|
||||
var endTime = OfferDiscoPartyData.EndTime;
|
||||
#if UNITY_EDITOR
|
||||
var timeOpened = (now - startTime).TotalSeconds;
|
||||
var timeEnded = (endTime - now).TotalSeconds;
|
||||
ELog(
|
||||
$"StartTime: {startTime} Now: {now} EndTime:{endTime} TimeOpened:{timeOpened} TimeToEnd:{timeEnded} Other: {1800000 - timeOpened} IsTimeAllow => {now <= endTime}");
|
||||
#endif
|
||||
return now >= startTime && now <= endTime;
|
||||
}
|
||||
|
||||
public void CheckInit()
|
||||
{
|
||||
ELog("CheckInit-> ");
|
||||
ProcessLastOne();
|
||||
Init();
|
||||
}
|
||||
|
||||
private static void Log(object t)
|
||||
{
|
||||
Debug.Log($"<color=orange>OfferDiscoPartyManager -> {t} </color>");
|
||||
}
|
||||
private static void ELog(object t)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"<color=yellow>OfferDiscoPartyManager -> {t} </color>");
|
||||
#endif
|
||||
}
|
||||
|
||||
public bool CheckOpen()
|
||||
{
|
||||
//
|
||||
if (OfferDiscoPartyData == null) return false;
|
||||
//
|
||||
if (!IsTimeAllow()) return false;
|
||||
|
||||
if (OfferDiscoParty == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsFinished())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsFinished()
|
||||
{
|
||||
// return OfferDiscoPartyData?.Finished ?? false;
|
||||
return OfferDiscoPartyData.LotteryNum >= OfferDiscoPartyData.Items.Count;
|
||||
}
|
||||
|
||||
private void SaveData()
|
||||
{
|
||||
playerSaveStore.Write(SaveKey, JsonConvert.SerializeObject(OfferDiscoPartyData)).RequireAccepted();
|
||||
}
|
||||
|
||||
public void LoadData(string dataValue)
|
||||
{
|
||||
ELog($"LoadData- {dataValue}");
|
||||
|
||||
OfferDiscoPartyData = JsonConvert.DeserializeObject<TOfferDiscoPartyData>(dataValue);
|
||||
//
|
||||
// if (true)
|
||||
// {
|
||||
// OfferDiscoPartyData.EventId = 1001;
|
||||
// SaveData();
|
||||
// }
|
||||
}
|
||||
|
||||
public async void OnEnterGameAct()
|
||||
{
|
||||
if (!playerSaveStore.IsCurrent) return;
|
||||
ELog("OnEnterGameAct()");
|
||||
IsFirstOpen = false;
|
||||
await UIManager.Instance.ShowUI(new UIType(OfferDiscoParty.UIPanel));
|
||||
}
|
||||
|
||||
public static T WeightedRandom<T>(Dictionary<T, int> weightTable)
|
||||
{
|
||||
// 1. 计算总权重
|
||||
var totalWeight = weightTable.Values.Sum();
|
||||
// 2. 生成随机数
|
||||
var randomValue = UnityEngine.Random.Range(0, totalWeight);
|
||||
|
||||
// 3. 线性遍历选择
|
||||
var currentWeight = 0;
|
||||
foreach (var (key, value) in weightTable)
|
||||
{
|
||||
currentWeight += value;
|
||||
if (randomValue < currentWeight)
|
||||
return key;
|
||||
}
|
||||
|
||||
return default; // 理论上不会执行到这里
|
||||
}
|
||||
|
||||
private int CalcLotteryItemId()
|
||||
{
|
||||
// UpdateValidIds();
|
||||
var lotteryNum = OfferDiscoPartyData.LotteryNum;
|
||||
if (ValidIds.Count <= 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (OfferDiscoPartyRewards == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// while (true)
|
||||
// {
|
||||
var dict = OfferDiscoPartyRewards
|
||||
.Where(item => ValidIds.Contains(item.ID) && lotteryNum >= (item.LimitConfig - 1))
|
||||
.ToDictionary(elem => (elem.ID, elem.LimitConfig), elem => elem.Weight);
|
||||
|
||||
if (dict?.Count <= 0)
|
||||
return -1;
|
||||
|
||||
var (elemId, limitConfig) = WeightedRandom(dict);
|
||||
ELog($"CalcLotteryItemId -> {lotteryNum} {elemId} {limitConfig}");
|
||||
// if (limitConfig >0 && lotteryNum > limitConfig )
|
||||
return elemId;
|
||||
// }
|
||||
}
|
||||
|
||||
private void UpdateValidIds()
|
||||
{
|
||||
|
||||
ValidIds = (from item in OfferDiscoPartyData?.Items where item.Status == 0 select item.Id).ToList();
|
||||
// return lotteryNum;
|
||||
}
|
||||
|
||||
public int GetLotteryItemId()
|
||||
{
|
||||
ELog($"GetLotteryItemId -> {OfferDiscoPartyData.ItemId}");
|
||||
if (OfferDiscoPartyData.ItemId == -1 || !ValidIds.Contains(OfferDiscoPartyData.ItemId))
|
||||
{
|
||||
UpdateLotteryItemId();
|
||||
}
|
||||
return OfferDiscoPartyData.ItemId;
|
||||
}
|
||||
|
||||
private void UpdateOfferDiscoPartyItems()
|
||||
{
|
||||
ELog("UpdateOfferDiscoPartyItems-> ");
|
||||
if (OfferDiscoPartyData != null && _fishingEvent != null && OfferDiscoPartyData.EventId == _fishingEvent.ID)
|
||||
{
|
||||
var items = OfferDiscoPartyData.Items;
|
||||
if (OfferDiscoPartyRewards is { Count: > 0 })
|
||||
{
|
||||
for (int i = 0; i < items.Count; ++i)
|
||||
{
|
||||
EventOfferDiscoPartyReward reward = null;
|
||||
if (i < OfferDiscoPartyRewards.Count)
|
||||
{
|
||||
reward = OfferDiscoPartyRewards[i];
|
||||
}
|
||||
|
||||
if (reward != null)
|
||||
{
|
||||
items[i].Id = reward.ID;
|
||||
items[i].Item = reward.Item;
|
||||
items[i].Number = reward.Number;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTime()
|
||||
{
|
||||
if (OfferDiscoPartyData != null && _fishingEvent != null && OfferDiscoPartyData.EventId == _fishingEvent.ID)
|
||||
{
|
||||
var limitedTime = (LimitedTime)_fishingEvent.TimeDefinition;
|
||||
var startTime = GlobalUtils.TryParseDateTime(limitedTime.StartTime, ZZTimeHelper.UtcNow());
|
||||
var endTime = GlobalUtils.TryParseDateTime(limitedTime.EndTime, ZZTimeHelper.UtcNow().AddDays(-1));
|
||||
OfferDiscoPartyData.StartTime = startTime;
|
||||
OfferDiscoPartyData.EndTime = endTime;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLotteryItemId()
|
||||
{
|
||||
var targetId = CalcLotteryItemId();
|
||||
OfferDiscoPartyData.ItemId = targetId;
|
||||
SaveData();
|
||||
|
||||
ELog($"UpdateLotteryItemId -> {targetId}");
|
||||
}
|
||||
|
||||
public void FinishLottery(int targetId)
|
||||
{
|
||||
ELog($"FinishLottery -> {targetId}");
|
||||
var lastItem = OfferDiscoPartyData.Items.SingleOrDefault(it => it.Id == targetId);
|
||||
if (lastItem is { Status: not TItemStatues.Done })
|
||||
{
|
||||
lastItem.Status = TItemStatues.Done;
|
||||
UpdateForNextLottery();
|
||||
}
|
||||
}
|
||||
//- 开始下一轮数据
|
||||
public void UpdateForNextLottery()
|
||||
{
|
||||
UpdateValidIds();
|
||||
UpdateLotteryItemId();
|
||||
}
|
||||
|
||||
|
||||
public void ClearOfferDiscoPartyData()
|
||||
{
|
||||
OfferDiscoPartyData = null;
|
||||
_isInit = false;
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public TOfferDiscoPartyData.TItem GetTItemById(int targetId)
|
||||
{
|
||||
var lastItem = OfferDiscoPartyData.Items.FirstOrDefault(it => it.Id == targetId);
|
||||
return lastItem;
|
||||
}
|
||||
|
||||
public int GetDropIdById(int id)
|
||||
{
|
||||
var item = GetTItemById(id);
|
||||
var itemData = _tables.TbItem.GetOrDefault(item.Item);
|
||||
return itemData.RedirectID;
|
||||
}
|
||||
|
||||
public void OnSettleFinalEvent()
|
||||
{
|
||||
// var item = _tables.TbItem.GetOrDefault(OfferDiscoParty.ChestReward);
|
||||
// var item = _tables.TbItem.GetOrDefault(OfferDiscoPartyData.ChestReward);
|
||||
// var itemFinal = GContext.container.Resolve<PlayerItemData>()
|
||||
// .GetItemDropPackageItemList(item.RedirectID, 1);
|
||||
// GContext.container.Resolve<PlayerItemData>().LureInflation(itemFinal, null);
|
||||
// GContext.container.Resolve<PlayerItemData>().AddItem(itemFinal);
|
||||
// GContext.Publish(new ShowData(itemFinal));
|
||||
// GContext.Publish(new ShowData());
|
||||
// OfferDiscoPartyData.Finished = true;
|
||||
SaveData();
|
||||
GContext.Publish(new OfferDiscoPartyRefreshEvent());
|
||||
}
|
||||
|
||||
public float GetProbability(int id)
|
||||
{
|
||||
var theIdWeight = OfferDiscoPartyRewards.FirstOrDefault(item => item.ID == id)?.Weight ?? 0;
|
||||
var weightTable = OfferDiscoPartyRewards
|
||||
.Where(item => !IsItemFinished(item.ID))
|
||||
.Select(item => item.Weight).ToList();
|
||||
var totalWeight = weightTable.Sum();
|
||||
return (float)Math.Round((float)theIdWeight / totalWeight * 100, 2);
|
||||
}
|
||||
|
||||
private bool IsItemFinished(int itemID)
|
||||
{
|
||||
var lastItem = OfferDiscoPartyData.Items.SingleOrDefault(it => it.Id == itemID);
|
||||
return lastItem?.Status == TItemStatues.Done;
|
||||
}
|
||||
|
||||
public TimeSpan GetTimeRemain()
|
||||
{
|
||||
if (OfferDiscoPartyData != null)
|
||||
{
|
||||
return OfferDiscoPartyData.EndTime - ZZTimeHelper.UtcNow();
|
||||
}
|
||||
|
||||
// 活动未开始
|
||||
return ZZTimeHelper.UtcNow().AddDays(-1) - ZZTimeHelper.UtcNow();
|
||||
}
|
||||
|
||||
public void Settle()
|
||||
{
|
||||
if (IsFinished())
|
||||
{
|
||||
OnSettleFinalEvent();
|
||||
}
|
||||
}
|
||||
|
||||
public bool UIRunning { get; set; } = false;
|
||||
|
||||
// private bool IsCanGetFinalReward()
|
||||
// {
|
||||
// // return OfferDiscoPartyData.LotteryNum >= OfferDiscoPartyData.Items.Count;
|
||||
// // && !OfferDiscoPartyData.Finished;
|
||||
// }
|
||||
|
||||
public void OnBuySuccess()
|
||||
{
|
||||
ELog("OnBuySuccess -> ");
|
||||
OfferDiscoPartyData.RewardList.Add(OfferDiscoPartyData.ItemId);
|
||||
OfferDiscoPartyData.LotteryNum += 1;
|
||||
FinishLottery(OfferDiscoPartyData.ItemId);
|
||||
SaveData();
|
||||
EventTracking();
|
||||
//NEXT TURN
|
||||
if (UIRunning)
|
||||
{
|
||||
// 播放动画逻辑,交给UI
|
||||
return;
|
||||
}
|
||||
|
||||
// FinishLottery(OfferDiscoPartyData.ItemId);
|
||||
UpdateForNextLottery();
|
||||
Settle();
|
||||
}
|
||||
|
||||
public void EventTracking()
|
||||
{
|
||||
var iapItemIdx = OfferDiscoPartyData.LotteryNum - 1 - FreeTime;
|
||||
var price = GetIapItemListThisTime(iapItemIdx)?.PaymentAmount ?? 0F;
|
||||
// var itemEnt = GetTItemById(OfferDiscoPartyData.ItemId);
|
||||
// var rewardStr = string.Join(',', OfferDiscoPartyData.RewardList);
|
||||
var number = OfferDiscoPartyData.LotteryNum;
|
||||
#if AGG
|
||||
using (var e = GEvent.TackEvent("offer_discoparty"))
|
||||
{
|
||||
e.AddContent("event_id", OfferDiscoPartyData.EventId);
|
||||
e.AddContent("number",number);
|
||||
e.AddContent("price", price);
|
||||
e.AddContent("reward",OfferDiscoPartyData.ItemId);
|
||||
}
|
||||
#endif
|
||||
ELog($"EventTracking: event_id: {OfferDiscoPartyData.EventId} number: {number} price: {price} " +
|
||||
$"reward :{OfferDiscoPartyData.ItemId} ");
|
||||
}
|
||||
|
||||
public IAPItemList GetIapItemListThisTime()
|
||||
{
|
||||
return GetIapItemListThisTime(OfferDiscoPartyData.LotteryNum - FreeTime);
|
||||
}
|
||||
|
||||
private IAPItemList GetIapItemListThisTime(int index)
|
||||
{
|
||||
// index -= FreeTime;
|
||||
// index = Mathf.Clamp(index, 0, OfferDiscoParty.IapList.Count - 1);
|
||||
if (index < 0 || index > OfferDiscoParty.IapList.Count - 1)
|
||||
return null;
|
||||
|
||||
var iapId = OfferDiscoParty.IapList[index];
|
||||
var iAPItemList = _tables.TbIAPItemList.GetOrDefault(iapId);
|
||||
return iAPItemList;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,247 +0,0 @@
|
||||
using asap.core;
|
||||
using GameCore;
|
||||
using cfg;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using game;
|
||||
using System.Linq;
|
||||
public class PiggyBankPackData
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore playerSaveStore { get; set; }
|
||||
private readonly Tables _tables = GContext.container.Resolve<Tables>();
|
||||
private readonly TbEventPackManager _epm
|
||||
= GContext.container.Resolve<Tables>().TbEventPackManager;
|
||||
private struct Data
|
||||
{
|
||||
public int currentEventID;
|
||||
public int purchaseCount;
|
||||
public int VIPLvlWhenEventActivate;
|
||||
public bool doNeedTrigger;
|
||||
public bool isTriggered;
|
||||
public int savingProgress;
|
||||
public int redirectID;
|
||||
public float inflation;
|
||||
public Data(int eventID = 0, int v = 0, bool b = false, int rid = 0, float inflation = 0)
|
||||
{
|
||||
currentEventID = eventID;
|
||||
purchaseCount = 0;
|
||||
VIPLvlWhenEventActivate = v;
|
||||
doNeedTrigger = b;
|
||||
savingProgress = 0;
|
||||
isTriggered = false;
|
||||
redirectID = rid;
|
||||
this.inflation = inflation;
|
||||
}
|
||||
}
|
||||
private Data _data;
|
||||
|
||||
private bool _kvLoadPresent;
|
||||
internal bool KVLoadPresent => _kvLoadPresent;
|
||||
internal static game.PlayerDataStorage.PlayerKVPiggyBankState DecodeForKVLoad(
|
||||
IReadOnlyDictionary<string, string> raw, game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,
|
||||
game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodePiggyBank(raw, snapshot, errors);
|
||||
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
_kvLoadPresent = false;
|
||||
_data = default(Data);
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVPiggyBankState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
if (!state.IsPresent) { ResetForKVLoad(); return; }
|
||||
var replacement = new Data
|
||||
{
|
||||
currentEventID = state.currentEventID,
|
||||
purchaseCount = state.purchaseCount,
|
||||
VIPLvlWhenEventActivate = state.VIPLvlWhenEventActivate,
|
||||
savingProgress = state.savingProgress,
|
||||
redirectID = state.redirectID,
|
||||
doNeedTrigger = state.doNeedTrigger,
|
||||
isTriggered = state.isTriggered,
|
||||
inflation = state.inflation,
|
||||
};
|
||||
_data = replacement;
|
||||
_kvLoadPresent = true;
|
||||
}
|
||||
internal game.PlayerDataStorage.PlayerKVPiggyBankState SnapshotForKVValidation() =>
|
||||
new game.PlayerDataStorage.PlayerKVPiggyBankState(_kvLoadPresent, _data.currentEventID, _data.purchaseCount, _data.VIPLvlWhenEventActivate, _data.savingProgress, _data.redirectID, _data.doNeedTrigger, _data.isTriggered, _data.inflation);
|
||||
public bool IsWithinEventTime
|
||||
{
|
||||
get
|
||||
{
|
||||
var fishingEvent = _tables.TbFishingEvent.GetOrDefault(_data.currentEventID);
|
||||
if (!(fishingEvent?.TimeDefinition is LimitedTime time)) return false;
|
||||
var et = DateTime.Parse(time.EndTime);
|
||||
var st = DateTime.Parse(time.StartTime);
|
||||
return ZZTimeHelper.UtcNow() >= st && ZZTimeHelper.UtcNow() < et;
|
||||
}
|
||||
}
|
||||
public bool IsPackActivated { get => _data.isTriggered && _tables.TbFishingEvent.DataMap.Keys.Contains(_data.currentEventID) && IsWithinEventTime && _data.purchaseCount < _epm[RedirectID].MaxCount; }
|
||||
public bool DoNeedUpdate { get => _data.doNeedTrigger; set => _data.doNeedTrigger = value; }
|
||||
public int CurrentEventID { get => _data.currentEventID; }
|
||||
public int PurchaseCount { get => _data.purchaseCount; }
|
||||
public int VIPLvlWhenEventActivated { get => _data.VIPLvlWhenEventActivate; }
|
||||
public int Progress { get => _data.savingProgress; }
|
||||
public float Inflation { get => _data.inflation; }
|
||||
public bool IsFull { get => _data.savingProgress >= Target; }
|
||||
public int ActivateItemId
|
||||
{
|
||||
get => _tables.TbSpecialPack[_epm[RedirectID]
|
||||
.VIPPackList[_data.VIPLvlWhenEventActivate][0]]
|
||||
.ActivateItemId;
|
||||
}
|
||||
public string ActiveItemImg
|
||||
{
|
||||
get => _tables.TbSpecialPack[_epm[RedirectID]
|
||||
.VIPPackList[_data.VIPLvlWhenEventActivate][0]].ActiveItemImg;
|
||||
}
|
||||
|
||||
public int EventItemId
|
||||
{
|
||||
get
|
||||
{
|
||||
SpecialPack specialPack = GetSpecialPack();
|
||||
if (specialPack != null)
|
||||
{
|
||||
return specialPack.EventItemId;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
SpecialPack GetSpecialPack()
|
||||
{
|
||||
var TbEventPackManager = _tables.TbEventPackManager.GetOrDefault(_data.redirectID);
|
||||
if (TbEventPackManager == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return _tables.TbSpecialPack.GetOrDefault(TbEventPackManager.VIPPackList[_data.VIPLvlWhenEventActivate][0]);
|
||||
}
|
||||
public int Target
|
||||
{
|
||||
get
|
||||
{
|
||||
int count = _tables.TbSpecialPack[_epm[RedirectID]
|
||||
.VIPPackList[_data.VIPLvlWhenEventActivate][0]]
|
||||
.EventItemRequire[0];
|
||||
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
count = playerItemData.IntInflation(count, _data.inflation);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
public int CanBuyCount
|
||||
{
|
||||
get
|
||||
{
|
||||
int count = _tables.TbSpecialPack[_epm[RedirectID]
|
||||
.VIPPackList[_data.VIPLvlWhenEventActivate][0]]
|
||||
.CanBuyCount;
|
||||
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
count = playerItemData.IntInflation(count, _data.inflation);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
//public int RedirectID { get => _tables.TbFishingEvent[_data.currentEventID].RedirectID; }
|
||||
public int RedirectID { get => _data.redirectID; }
|
||||
public int RewardDropID
|
||||
{
|
||||
get => _tables.TbSpecialPack[_epm[RedirectID]
|
||||
.VIPPackList[_data.VIPLvlWhenEventActivate][0]]
|
||||
.DropID[0];
|
||||
}
|
||||
public List<int> FishingScoreList
|
||||
{
|
||||
get => _tables.TbSpecialPack[_epm[RedirectID].VIPPackList[_data.VIPLvlWhenEventActivate][0]]
|
||||
.EventItemGet;
|
||||
}
|
||||
public TimeSpan RemainingTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return DateTime.Parse((_tables.TbFishingEvent[_data.currentEventID].TimeDefinition as LimitedTime).EndTime)
|
||||
- ZZTimeHelper.UtcNow();
|
||||
}
|
||||
}
|
||||
public int MaxCount { get => _epm[RedirectID].MaxCount; }
|
||||
public void LoadData(string s)
|
||||
{
|
||||
_data = Newtonsoft.Json.JsonConvert.DeserializeObject<Data>(s);
|
||||
|
||||
//_data.redirectID = 0;
|
||||
if (_data.redirectID == 0)
|
||||
{
|
||||
_data.redirectID = 4055001;
|
||||
}
|
||||
}
|
||||
public void UpdateData(cfg.FishingEvent e)
|
||||
{
|
||||
if (_epm[_tables.TbFishingEvent[e.ID].RedirectID].PackType != 2)
|
||||
{
|
||||
Debug.LogError($"Fishing event ID {e.ID} is not a piggyBank event.");
|
||||
return;
|
||||
}
|
||||
if (_data.currentEventID != e.ID)
|
||||
_data = new Data(e.ID, GContext.container.Resolve<PlayerData>().PriceLv, true, e.RedirectID, GContext.container.Resolve<PlayerData>().InflationRate);
|
||||
if (_data.redirectID != e.RedirectID)
|
||||
{
|
||||
_data.redirectID = e.RedirectID;
|
||||
Debug.Log("working!!!!!!!!!!");
|
||||
}
|
||||
SaveData();
|
||||
}
|
||||
public void SaveData()
|
||||
{
|
||||
playerSaveStore.Write("PiggyBankPackData",
|
||||
Newtonsoft.Json.JsonConvert.SerializeObject(_data)).RequireAccepted();
|
||||
}
|
||||
public bool AddProgress(int fishTier, int magnification)
|
||||
{
|
||||
if (!IsPackActivated || fishTier < 1 || fishTier > 5)
|
||||
{
|
||||
//Debug.LogError($"Fish Tier {fishTier} not defined in TbSpedialPack");
|
||||
return false;
|
||||
}
|
||||
var TbSpecialPack = _tables.TbSpecialPack[_epm[RedirectID].VIPPackList[_data.VIPLvlWhenEventActivate][0]];
|
||||
int progress = TbSpecialPack.EventItemGet[fishTier - 1] * magnification;
|
||||
if (progress > 0 && _data.savingProgress < Target)
|
||||
{
|
||||
int curProgress = _data.savingProgress;
|
||||
_data.savingProgress += progress;
|
||||
GContext.Publish(new TargetAddData(TbSpecialPack.EventItemId, curProgress, progress));
|
||||
if (_data.savingProgress >= Target)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIForce(_data.currentEventID, UITypes.GiftPiggyBankPopupPanel);
|
||||
_data.savingProgress = Target;
|
||||
SaveData();
|
||||
return true;
|
||||
}
|
||||
SaveData();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public void AddPurchase()
|
||||
{
|
||||
_data.purchaseCount++;
|
||||
SaveData();
|
||||
}
|
||||
public void TriggerPack()
|
||||
{
|
||||
DoNeedUpdate = false;
|
||||
_data.isTriggered = true;
|
||||
int EventItemCountFirst = _tables.TbSpecialPack[_epm[RedirectID]
|
||||
.VIPPackList[_data.VIPLvlWhenEventActivate][0]].EventItemCountFirst;
|
||||
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
EventItemCountFirst = playerItemData.IntInflation(EventItemCountFirst, _data.inflation);
|
||||
_data.savingProgress += EventItemCountFirst;
|
||||
SaveData();
|
||||
}
|
||||
}
|
||||
public class PiggyBankProgressEvent
|
||||
{
|
||||
public int type;//0: 初始化 1:增加动画
|
||||
public int addProgress;
|
||||
}
|
||||
|
||||
@@ -1,681 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using LitJson;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace GameCore
|
||||
{
|
||||
public class RodGSAData
|
||||
{
|
||||
public int GloveID;
|
||||
public Dictionary<int, int> RodToSkin = new Dictionary<int, int>();
|
||||
public List<int> GloveIds = new List<int>();
|
||||
public List<int> SkinIds = new List<int>();
|
||||
|
||||
public List<int> GloveNewIds = new List<int>();
|
||||
public List<int> SkinNewIds = new List<int>();
|
||||
}
|
||||
public partial class PlayerFishData
|
||||
{
|
||||
#region 鱼杆皮肤
|
||||
public RodGSAData rodAccessoriesData;
|
||||
string InitRodAccessoriesDataKey = "InitRodAccessoriesData";
|
||||
public GloveData GetGloveData
|
||||
{
|
||||
get
|
||||
{
|
||||
return _tables.TbGloveData.GetOrDefault(rodAccessoriesData.GloveID);
|
||||
}
|
||||
}
|
||||
public void InitRodAccessoriesData()
|
||||
{
|
||||
if (rodAccessoriesData == null || rodAccessoriesData.GloveID == 0)
|
||||
{
|
||||
rodAccessoriesData = new RodGSAData();
|
||||
rodAccessoriesData.GloveID = _tables.TbGlobalConfig.InitGloveID;
|
||||
rodAccessoriesData.GloveIds = new List<int>() { _tables.TbGlobalConfig.InitGloveID };
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取鱼杆对应的皮肤
|
||||
/// </summary>
|
||||
/// <param name="rodId"></param>
|
||||
/// <returns></returns>
|
||||
public int GetRodSkin(int rodId)
|
||||
{
|
||||
if (rodAccessoriesData.RodToSkin.ContainsKey(rodId))
|
||||
{
|
||||
return rodAccessoriesData.RodToSkin[rodId];
|
||||
}
|
||||
return rodId;
|
||||
}
|
||||
/// <summary>
|
||||
/// 设置鱼杆对应的皮肤
|
||||
/// </summary>
|
||||
/// <param name="rodId"></param>
|
||||
/// <param name="skinId"></param>
|
||||
public void SetRodSkin(int rodId, int skinId)
|
||||
{
|
||||
if (skinId <= 0)
|
||||
{
|
||||
skinId = rodId;
|
||||
}
|
||||
rodAccessoriesData.RodToSkin[rodId] = skinId;
|
||||
SaveInitRodAccessoriesData();
|
||||
}
|
||||
public void InitRodAccessoriesData(string value)
|
||||
{
|
||||
rodAccessoriesData = Newtonsoft.Json.JsonConvert.DeserializeObject<RodGSAData>(value);
|
||||
}
|
||||
public void SaveInitRodAccessoriesData()
|
||||
{
|
||||
string value = Newtonsoft.Json.JsonConvert.SerializeObject(rodAccessoriesData);
|
||||
playerSaveStore.Write(InitRodAccessoriesDataKey, value).RequireAccepted();
|
||||
}
|
||||
/// <summary>
|
||||
/// 手是否解锁
|
||||
/// </summary>
|
||||
/// <param name="gloveId"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsGloveUnlocked(int gloveId)
|
||||
{
|
||||
return rodAccessoriesData.GloveIds.Contains(gloveId);
|
||||
}
|
||||
/// <summary>
|
||||
/// 皮肤是否解锁
|
||||
/// </summary>
|
||||
/// <param name="skinId"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsSkinUnlocked(int skinId)
|
||||
{
|
||||
return rodAccessoriesData.SkinIds.Contains(skinId);
|
||||
}
|
||||
/// <summary>
|
||||
/// 解锁手
|
||||
/// </summary>
|
||||
/// <param name="gloveId"></param>
|
||||
public void UnlockGlove(int gloveId)
|
||||
{
|
||||
if (!IsGloveUnlocked(gloveId))
|
||||
{
|
||||
rodAccessoriesData.GloveIds.Add(gloveId);
|
||||
rodAccessoriesData.GloveNewIds.Add(gloveId);
|
||||
SaveInitRodAccessoriesData();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("手投放重复:" + gloveId);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 解锁皮肤
|
||||
/// </summary>
|
||||
/// <param name="skinId"></param>
|
||||
public void UnlockSkin(int skinId)
|
||||
{
|
||||
if (!IsSkinUnlocked(skinId))
|
||||
{
|
||||
rodAccessoriesData.SkinIds.Add(skinId);
|
||||
rodAccessoriesData.SkinNewIds.Add(skinId);
|
||||
SaveInitRodAccessoriesData();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("鱼杆皮肤投放重复:" + skinId);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取是否是没查看过的手
|
||||
/// </summary>
|
||||
/// <param name="skinId"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsNewGlove(int skinId)
|
||||
{
|
||||
return rodAccessoriesData.GloveNewIds.Contains(skinId);
|
||||
}
|
||||
/// <summary>
|
||||
/// 是否是没查看过的皮肤
|
||||
/// </summary>
|
||||
/// <param name="skinId"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsNewSkin(int skinId)
|
||||
{
|
||||
bool isNew = rodAccessoriesData.SkinNewIds.Contains(skinId);
|
||||
if (isNew)
|
||||
{
|
||||
rodAccessoriesData.SkinNewIds.Remove(skinId);
|
||||
}
|
||||
return isNew;
|
||||
}
|
||||
/// <summary>
|
||||
/// 取消new标记 手
|
||||
/// </summary>
|
||||
/// <param name="gloveId"></param>
|
||||
public void RemoveGloveNewId()
|
||||
{
|
||||
if (rodAccessoriesData.GloveNewIds.Count > 0)
|
||||
{
|
||||
rodAccessoriesData.GloveNewIds.Clear();
|
||||
SaveInitRodAccessoriesData();
|
||||
}
|
||||
}
|
||||
#endregion 鱼杆皮肤
|
||||
#region 鱼杆升级
|
||||
//合成或强化鱼竿
|
||||
bool EnhanceConsumed(int quality, int fragmentID, int rquiredFragments)
|
||||
{
|
||||
if (rodData.TryGetValue(fragmentID, out int rodCount) && rodCount >= rquiredFragments)
|
||||
{
|
||||
rodData[fragmentID] -= rquiredFragments;
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("item_change", gaSend: true))
|
||||
{
|
||||
e.AddContent("item_id", fragmentID)
|
||||
.AddContent("state_info", "cost")
|
||||
.AddContent("change_num", rquiredFragments);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
rquiredFragments -= rodCount;
|
||||
RodFragmentExchange rodFragmentExchange = _tables.TbRodFragmentExchange.GetOrDefault(quality);
|
||||
int fragment = 0;
|
||||
if (rodFragmentExchange != null)
|
||||
{
|
||||
rquiredFragments *= rodFragmentExchange.ExchangeRate;
|
||||
fragment = GContext.container.Resolve<PlayerFishData>().GetRodPiece(rodFragmentExchange.GeneralFragmentID);
|
||||
}
|
||||
if (fragment >= rquiredFragments)
|
||||
{
|
||||
if (rodData.ContainsKey(fragmentID))
|
||||
{
|
||||
rodData[fragmentID] = 0;
|
||||
}
|
||||
rodData[rodFragmentExchange.GeneralFragmentID] -= rquiredFragments;
|
||||
#if AGG
|
||||
|
||||
if (rodCount > 0)
|
||||
{
|
||||
using (var e = GEvent.GameEvent("item_change", gaSend: true))
|
||||
{
|
||||
e.AddContent("item_id", fragmentID)
|
||||
.AddContent("state_info", "cost")
|
||||
.AddContent("change_num", rodCount);
|
||||
}
|
||||
}
|
||||
using (var e = GEvent.GameEvent("item_change", gaSend: true))
|
||||
{
|
||||
e.AddContent("item_id", rodFragmentExchange.GeneralFragmentID)
|
||||
.AddContent("state_info", "cost")
|
||||
.AddContent("change_num", rquiredFragments);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public void AscendRod(int id)
|
||||
{
|
||||
RodData _rodData = _tables.TbRodData.GetOrDefault(id);
|
||||
if (_rodData != null)
|
||||
{
|
||||
bool isUp = false;
|
||||
RodAscend rodAscend = _tables.TbRodAscend.GetOrDefault(_rodData.AscendID);
|
||||
int rquiredFragments;
|
||||
int fragmentID = rodAscend.FragmentID;
|
||||
int quality = _rodData.Quality;
|
||||
if (rodData.ContainsKey(id))
|
||||
{
|
||||
rquiredFragments = rodAscend.AscentTransformed[rodData[id] + 1];
|
||||
|
||||
if (rodData[id] < rodAscend.AscentTransformed.Count - 1 && EnhanceConsumed(quality, fragmentID, rquiredFragments))
|
||||
{
|
||||
isUp = true;
|
||||
rodData[id] += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rquiredFragments = rodAscend.AscentTransformed[0];
|
||||
|
||||
if (rodData.TryGetValue(fragmentID, out int rodCount) && rodCount >= rquiredFragments)
|
||||
{
|
||||
rodData[fragmentID] -= rquiredFragments;
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("item_change", gaSend: true))
|
||||
{
|
||||
e.AddContent("item_id", fragmentID)
|
||||
.AddContent("state_info", "cost")
|
||||
.AddContent("change_num", rquiredFragments);
|
||||
}
|
||||
#endif
|
||||
isUp = true;
|
||||
rodData.Add(id, 0);
|
||||
}
|
||||
}
|
||||
if (isUp)
|
||||
{
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("rod_upgrade"))
|
||||
{
|
||||
e.AddContent("rod_id", id)
|
||||
.AddContent("rod_level", GetRodLevel(id))
|
||||
.AddContent("resin_consume", 0)
|
||||
.AddContent("rod_star", rodData[id])
|
||||
.AddContent("rod_chips_consume", rquiredFragments);
|
||||
}
|
||||
#endif
|
||||
playerSaveStore.Write("RodData", JsonMapper.ToJson(rodData)).RequireAccepted();
|
||||
//鱼竿红点数据修改
|
||||
CheckRodRedPointUp();
|
||||
//合成鱼竿
|
||||
CheckAllRodUpLevel();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region 荣耀等级
|
||||
|
||||
Dictionary<int, int> _honorlevel = new Dictionary<int, int>();
|
||||
public int GetHonorLevel(int honorId)
|
||||
{
|
||||
if (_honorlevel.TryGetValue(honorId, out int level))
|
||||
{
|
||||
return level;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public int GetHonorLevelAttribute(int quality, int starAdd = 0)
|
||||
{
|
||||
RodRodLevelPeak rodLevelPeak = _tables.TbRodRodLevelPeak.GetOrDefault(quality);
|
||||
//星级对属性的加成 某几个属性加多少加到多少星
|
||||
int level = GetHonorLevel(quality) + starAdd;
|
||||
if (rodLevelPeak != null && level > 0)
|
||||
{
|
||||
List<int> peakBasicStats = rodLevelPeak.PeakBasicStats;
|
||||
int value;
|
||||
int count = peakBasicStats.Count;
|
||||
if (level > count)
|
||||
{
|
||||
value = peakBasicStats[count - 1];
|
||||
value += (level - count) * (peakBasicStats[count - 1] - peakBasicStats[count - 2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = peakBasicStats[level - 1];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public void HonorLevelUp(RodData rodData)
|
||||
{
|
||||
PlayerData playerData = GContext.container.Resolve<PlayerData>();
|
||||
RodAscend rodAscend = _tables.TbRodAscend.GetOrDefault(rodData.AscendID);
|
||||
int quality = rodData.Quality;
|
||||
int fragmentID = rodAscend.FragmentID;
|
||||
RodRodLevelPeak rodLevelPeak = _tables.TbRodRodLevelPeak.GetOrDefault(rodData.Quality);
|
||||
if (rodLevelPeak == null)
|
||||
{
|
||||
Debug.LogError($"[FishingRodHonorlevelPanel]OnClickUpGray: No RodRodLevelPeak data found for Quality {quality}");
|
||||
return;
|
||||
}
|
||||
int currentHonorLevel = GetHonorLevel(rodData.Quality);
|
||||
if (currentHonorLevel >= rodLevelPeak.ItemConsume.Count)
|
||||
{
|
||||
currentHonorLevel = rodLevelPeak.ItemConsume.Count - 1;
|
||||
}
|
||||
int requiredItems = rodLevelPeak.ItemConsume[currentHonorLevel];
|
||||
int fragmentItems = rodLevelPeak.FragmentConsume[currentHonorLevel];
|
||||
if (playerData.pearl >= requiredItems && EnhanceConsumed(quality, fragmentID, fragmentItems))
|
||||
{
|
||||
playerData.SetPearl(playerData.pearl - requiredItems);
|
||||
if (_honorlevel.ContainsKey(quality))
|
||||
{
|
||||
_honorlevel[quality]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_honorlevel[quality] = 1;
|
||||
}
|
||||
SaveHonorLevelData();
|
||||
}
|
||||
}
|
||||
|
||||
string HonorLevelDataKey = "HonorLevelData";
|
||||
|
||||
public void SaveHonorLevelData()
|
||||
{
|
||||
string value = Newtonsoft.Json.JsonConvert.SerializeObject(_honorlevel);
|
||||
playerSaveStore.Write(HonorLevelDataKey, value).RequireAccepted();
|
||||
}
|
||||
|
||||
#endregion 荣耀等级
|
||||
|
||||
#region 鱼杆词条星级相关
|
||||
|
||||
//计算升级和强化后的属性
|
||||
public List<int> GetRodAttribute(int id, bool isMax = false, int levelAdd = 0, int starAdd = 0)
|
||||
{
|
||||
RodData rodData = _tables.TbRodData.GetOrDefault(id);
|
||||
RodAscend rodAscend = _tables.TbRodAscend.GetOrDefault(rodData.AscendID);
|
||||
RodLevelup rodLevelup = _tables.TbRodLevelup.GetOrDefault(rodData.LevelupID);
|
||||
int star = isMax ? rodAscend.MaxAscent : GetRodPiece(rodData.ID) + starAdd;
|
||||
int level = isMax ? rodLevelup.MaxLevel - 1 : GetRodLevel(rodData.ID) + levelAdd;
|
||||
List<int> list = new List<int>();
|
||||
for (int i = 0; i < rodData.InitialBasicStats.Count; i++)
|
||||
{
|
||||
list.Add(0);
|
||||
}
|
||||
//星级对属性的加成 某几个属性加多少加到多少星
|
||||
var AscentBasicStats = rodAscend.AscentBasicStat;
|
||||
var AscentBasicStatList = rodAscend.AscentBasicStatList;
|
||||
int index;
|
||||
for (int i = 0; i < star; i++)
|
||||
{
|
||||
var AscentBasicStat = AscentBasicStats[i];
|
||||
int count = AscentBasicStat.Count;
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
//InitialBasicStats 中第 AscentBasicStat[j] 个值加 i 星的值 累加
|
||||
index = AscentBasicStat[j] - 1;
|
||||
list[index] = AscentBasicStatList[i][j];
|
||||
}
|
||||
}
|
||||
//等级对属性的加成 每个属性升到多少级
|
||||
var levelupBasicStats = rodLevelup.LevelupBasicStats;
|
||||
for (int j = 0; j < list.Count; j++)
|
||||
{
|
||||
list[j] += levelupBasicStats[j][level] + rodData.InitialBasicStats[j];
|
||||
}
|
||||
list[0] += GetHonorLevelAttribute(rodData.Quality);
|
||||
return list;
|
||||
}
|
||||
#region 工具
|
||||
//获取鱼竿某一星级各个词条的等级
|
||||
public int[] GetPerkLevel(int perkIDListCount, int star, List<int> DefaultPerk, List<int> PerkUnlockOrder)
|
||||
{
|
||||
int[] lv = new int[perkIDListCount];
|
||||
for (int i = 0; i < DefaultPerk.Count; i++)
|
||||
{
|
||||
lv[DefaultPerk[i] - 1]++;
|
||||
}
|
||||
for (int i = 0; i < star; i++)
|
||||
{
|
||||
if (PerkUnlockOrder[i] <= perkIDListCount)
|
||||
{
|
||||
//每一星升级的是第几个特性
|
||||
lv[PerkUnlockOrder[i] - 1]++;
|
||||
}
|
||||
}
|
||||
return lv;
|
||||
}
|
||||
//获取鱼竿某一星级能解锁的词条ID及值
|
||||
public Dictionary<int, string> GetRodDuelPerk(int id, int star)
|
||||
{
|
||||
RodData rodData = _tables.TbRodData.GetOrDefault(id);
|
||||
RodAscend curRodAscend = _tables.TbRodAscend.GetOrDefault(rodData.AscendID);
|
||||
int perkIDListCount = curRodAscend.PerkIDList.Count;
|
||||
int[] lv = GetPerkLevel(perkIDListCount, star, curRodAscend.DefaultPerk, curRodAscend.PerkUnlockOrder);
|
||||
Dictionary<int, string> RodPerkDic = new Dictionary<int, string>();
|
||||
for (int i = 0; i < perkIDListCount; i++)
|
||||
{
|
||||
int level = lv[i];
|
||||
if (level > 0)
|
||||
{
|
||||
var rodEngancePerk = _tables.TbRodAscendPerk.GetOrDefault(curRodAscend.PerkIDList[i]);
|
||||
|
||||
if (rodEngancePerk.DuelPerk)
|
||||
{
|
||||
List<string> PerkDataList = curRodAscend.PerkDataList[i];
|
||||
if (level > PerkDataList.Count)
|
||||
{
|
||||
Debug.LogError($"GetRodPerk: rod {id} perkID {curRodAscend.PerkIDList[i]} level {level} is out of range (1 - {PerkDataList.Count})");
|
||||
}
|
||||
else
|
||||
{
|
||||
RodPerkDic[curRodAscend.PerkIDList[i]] = PerkDataList[level - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return RodPerkDic;
|
||||
}
|
||||
//获取鱼竿某一星级能解锁的词条类型及值
|
||||
public Dictionary<RodPerkType, string> GetRodPerk(int id, int star)
|
||||
{
|
||||
RodData rodData = _tables.TbRodData.GetOrDefault(id);
|
||||
RodAscend curRodAscend = _tables.TbRodAscend.GetOrDefault(rodData.AscendID);
|
||||
int perkIDListCount = curRodAscend.PerkIDList.Count;
|
||||
int[] lv = GetPerkLevel(perkIDListCount, star, curRodAscend.DefaultPerk, curRodAscend.PerkUnlockOrder);
|
||||
Dictionary<RodPerkType, string> RodPerkDic = new Dictionary<RodPerkType, string>();
|
||||
for (int i = 0; i < curRodAscend.PerkDataList.Count; i++)
|
||||
{
|
||||
int level = lv[i];
|
||||
if (level > 0)
|
||||
{
|
||||
var rodEngancePerk = _tables.TbRodAscendPerk.GetOrDefault(curRodAscend.PerkIDList[i]);
|
||||
//第i条第lv[i]级 直接读数值
|
||||
List<string> PerkDataList = curRodAscend.PerkDataList[i];
|
||||
if (level > PerkDataList.Count)
|
||||
{
|
||||
Debug.LogError($"GetRodPerk: rod {id} perkID {curRodAscend.PerkIDList[i]} level {level} is out of range (1 - {PerkDataList.Count})");
|
||||
}
|
||||
else
|
||||
{
|
||||
RodPerkDic[rodEngancePerk.PerkType] = PerkDataList[level - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return RodPerkDic;
|
||||
}
|
||||
//获取鱼竿某一星级能解锁的词条类型及等级
|
||||
public Dictionary<RodPerkType, (int, int)> GetRodPerkLevel(int id, int star)
|
||||
{
|
||||
RodData rodData = _tables.TbRodData.GetOrDefault(id);
|
||||
RodAscend curRodAscend = _tables.TbRodAscend.GetOrDefault(rodData.AscendID);
|
||||
int perkIDListCount = curRodAscend.PerkIDList.Count;
|
||||
int[] lv = GetPerkLevel(perkIDListCount, star, curRodAscend.DefaultPerk, curRodAscend.PerkUnlockOrder);
|
||||
Dictionary<RodPerkType, (int, int)> RodPerkDic = new Dictionary<RodPerkType, (int, int)>();
|
||||
for (int i = 0; i < perkIDListCount; i++)
|
||||
{
|
||||
int level = lv[i];
|
||||
if (level > 0)
|
||||
{
|
||||
var rodEngancePerk = _tables.TbRodAscendPerk.GetOrDefault(curRodAscend.PerkIDList[i]);
|
||||
RodPerkDic[rodEngancePerk.PerkType] = (curRodAscend.PerkIDList[i], level);
|
||||
}
|
||||
}
|
||||
return RodPerkDic;
|
||||
}
|
||||
|
||||
|
||||
//解析词条的值
|
||||
public float GetRodPerkValue(RodPerkType rodPerkType, Dictionary<RodPerkType, string> RodPerkDic)
|
||||
{
|
||||
if (RodPerkDic.TryGetValue(rodPerkType, out string perk))
|
||||
{
|
||||
if (perk.Contains('|'))
|
||||
{
|
||||
Debug.LogError($"GetRodPerkValue: rodPerkType {rodPerkType} perk {perk} cannot convert to float directly");
|
||||
string[] values = perk.Split('|');
|
||||
return float.Parse(values[0]);
|
||||
}
|
||||
return float.Parse(perk);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static string GetPerkDesc(RodPerkType rodPerkType, string des, string value)
|
||||
{
|
||||
var values = value.Split('|');
|
||||
string str = LocalizationMgr.GetText(des);
|
||||
try
|
||||
{
|
||||
switch (rodPerkType)
|
||||
{
|
||||
case RodPerkType.BattlePerk_AllToAtkSpeed:
|
||||
str = LocalizationMgr.GetFormatTextValue(des, float.Parse(values[0]).ToPercentageString(), values[1], float.Parse(values[2]).ToPercentageString());
|
||||
break;
|
||||
case RodPerkType.BattlePerk_ComboToExDmg:
|
||||
str = LocalizationMgr.GetFormatTextValue(des, float.Parse(values[0]).ToPercentageString(), values[1]);
|
||||
break;
|
||||
case RodPerkType.BattlePerk_FishSkillToAtkSpeed:
|
||||
case RodPerkType.BattlePerk_FishEscapeToAtkSpeed:
|
||||
str = LocalizationMgr.GetFormatTextValue(des, values[0], float.Parse(values[1]).ToPercentageString());
|
||||
break;
|
||||
case RodPerkType.BattlePerk_PiercingAddDamage:
|
||||
str = LocalizationMgr.GetFormatTextValue(des, values[1], float.Parse(values[0]).ToPercentageString());
|
||||
break;
|
||||
case RodPerkType.BattlePerk_PiercingToCombo:
|
||||
case RodPerkType.BattlePerk_TensionToCrit:
|
||||
case RodPerkType.BattlePerk_PiercingCritDmg:
|
||||
|
||||
case RodPerkType.FactoryLabel_ScaleG:
|
||||
case RodPerkType.FactoryLabel_Ilmarinen:
|
||||
case RodPerkType.FactoryLabel_ChromaCast:
|
||||
case RodPerkType.FactoryLabel_Victoria:
|
||||
case RodPerkType.FactoryLabel_Sakura:
|
||||
case RodPerkType.FactoryLabel_Titan:
|
||||
str = LocalizationMgr.GetFormatTextValue(des, float.Parse(values[0]).ToPercentageString(), float.Parse(values[1]).ToPercentageString());
|
||||
break;
|
||||
case RodPerkType.BattlePerk_ComboToCombo:
|
||||
str = LocalizationMgr.GetFormatTextValue(des, float.Parse(values[0]).ToPercentageString(), float.Parse(values[1]).ToPercentageString(), float.Parse(values[2]).ToPercentageString());
|
||||
break;
|
||||
case RodPerkType.NewPiercingZone:
|
||||
break;
|
||||
default:
|
||||
if (!value.Contains('|'))
|
||||
{
|
||||
str = LocalizationMgr.GetFormatTextValue(des, float.Parse(value).ToPercentageString());
|
||||
}
|
||||
else
|
||||
{
|
||||
str = LocalizationMgr.GetFormatTextValue(des, float.Parse(values[0]).ToPercentageString(), float.Parse(values[1]).ToPercentageString());
|
||||
Debug.LogError($"GetPerkDesc error: no case for rodPerkType: {rodPerkType}, des: {des}, value: {value}");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"GetPerkDesc error: {e.Message}, rodPerkType: {rodPerkType}, des: {des}, value: {value}");
|
||||
}
|
||||
return str;
|
||||
}
|
||||
/// <summary>
|
||||
/// 设置星级展示
|
||||
/// </summary>
|
||||
/// <param name="star"></param>
|
||||
/// <param name="MaxAscent"></param>
|
||||
/// <param name="star_empty_list"></param>
|
||||
/// <param name="star_empty_image"></param>
|
||||
/// <param name="starList"></param>
|
||||
/// <param name="isUp"></param>
|
||||
/// <returns></returns>
|
||||
public static int SetRodStar(int star,
|
||||
int MaxAscent,
|
||||
List<GameObject> star_empty_list,
|
||||
List<Image> star_empty_image,
|
||||
List<StarItem> starList, bool isUp = false)
|
||||
{
|
||||
if (star > MaxAscent || star < 0)
|
||||
{
|
||||
Debug.LogError($"SetRodStar: star {star} is out of range (0 - {MaxAscent})");
|
||||
star = MaxAscent;
|
||||
}
|
||||
int Quality = MaxAscent / 3;
|
||||
if (MaxAscent > 15)
|
||||
{
|
||||
Debug.LogError("MaxAscent >> show == 15");
|
||||
Quality = MaxAscent / 4;
|
||||
}
|
||||
if (Quality > 5)
|
||||
{
|
||||
Debug.LogError("MaxAscent >> show == 20");
|
||||
Quality = 5;
|
||||
}
|
||||
|
||||
int show = star;
|
||||
int startIndex = 0;
|
||||
if (star > Quality * 3)
|
||||
{
|
||||
show = star - Quality * 3;
|
||||
startIndex = 3;
|
||||
}
|
||||
else if (star > Quality * 2)
|
||||
{
|
||||
show = star - Quality * 2;
|
||||
startIndex = 2;
|
||||
}
|
||||
else if (star > Quality)
|
||||
{
|
||||
show = star - Quality;
|
||||
startIndex = 1;
|
||||
}
|
||||
|
||||
int index = startIndex;
|
||||
for (int i = 0; i < starList.Count; i++)
|
||||
{
|
||||
starList[i].gameObject.SetActive(i < Quality);
|
||||
star_empty_list[i].SetActive(i < Quality);
|
||||
if (i < show)
|
||||
{
|
||||
starList[i].SetStar(startIndex + 1);
|
||||
}
|
||||
else if (i < Quality)
|
||||
{
|
||||
starList[i].SetStar(startIndex);
|
||||
}
|
||||
if (star_empty_image != null)
|
||||
{
|
||||
if (index > 0)
|
||||
{
|
||||
if (index > 3)
|
||||
{
|
||||
Debug.LogError("SetRodStar index > 3, index = " + index);
|
||||
index = 3;
|
||||
}
|
||||
star_empty_image[i].gameObject.SetActive(true);
|
||||
star_empty_image[i].sprite = starList[i].Star[index - 1].sprite;
|
||||
}
|
||||
else
|
||||
{
|
||||
star_empty_image[i].gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isUp)
|
||||
{
|
||||
if (show > 0)
|
||||
{
|
||||
starList[show - 1].PlayAni();
|
||||
if (star_empty_image != null)
|
||||
{
|
||||
index = startIndex;
|
||||
if (index > 0)
|
||||
{
|
||||
star_empty_image[show - 1].gameObject.SetActive(true);
|
||||
star_empty_image[show - 1].sprite = starList[show - 1].Star[index - 1].sprite;
|
||||
}
|
||||
else
|
||||
{
|
||||
star_empty_image[show - 1].gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Quality;
|
||||
}
|
||||
#endregion 工具
|
||||
#endregion 鱼杆词条星级相关
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
namespace GameCore
|
||||
{
|
||||
public partial class PlayerItemData
|
||||
{
|
||||
/// <summary>
|
||||
/// 获得膨胀体力后的物品显示
|
||||
/// </summary>
|
||||
/// <param name="inflate">体力膨胀系数 == null 当前实时系数</param>
|
||||
/// <param name="dropId"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public ItemData GetItemDataOneLureInflation(float? inflate, int dropId, int index = 0)
|
||||
{
|
||||
DropPackageList dropPackageList = _tables.TbDrop.GetOrDefault(dropId)?.DropList;
|
||||
if (dropPackageList == null)
|
||||
{
|
||||
GameDebug.LogError("null dropID" + dropId);
|
||||
return null;
|
||||
}
|
||||
int len = dropPackageList.DropIDList.Count;
|
||||
int len2 = dropPackageList.DropCountList.Count;
|
||||
if (len != len2)
|
||||
{
|
||||
if (len > len2)
|
||||
{
|
||||
len = len2;
|
||||
}
|
||||
Debug.LogError($"[PlayerItemData]GetItemDataByDropIdAndType: DropPackageList count not equal [DropId]=={dropId}");
|
||||
}
|
||||
if (index >= len)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ItemData itemData = new ItemData()
|
||||
{
|
||||
id = dropPackageList.DropIDList[index],
|
||||
count = dropPackageList.DropCountList[index]
|
||||
};
|
||||
|
||||
ItemTransition(itemData);
|
||||
if (inflate == null)
|
||||
{
|
||||
inflate = GContext.container.Resolve<PlayerData>().InflationRate;//等级膨胀系数
|
||||
}
|
||||
itemData.inflate = (float)inflate;
|
||||
if (itemData.id == 1001)
|
||||
{
|
||||
ItemInflation(itemData, inflate);
|
||||
}
|
||||
return itemData;
|
||||
}
|
||||
/// <summary>
|
||||
/// 获得膨胀体力后的物品显示
|
||||
/// </summary>
|
||||
/// <param name="inflate">体力膨胀系数 == null 当前实时系数</param>
|
||||
/// <returns></returns>
|
||||
public List<ItemData> GetItemDataByDropIdAndTypeLureInflation(float? inflate, int dropId, int mapId = -1)
|
||||
{
|
||||
List<ItemData> itemDataList = GetItemDataByDropIdAndType(dropId, mapId);
|
||||
LureInflation(itemDataList, inflate);
|
||||
return itemDataList;
|
||||
}
|
||||
/// <summary>
|
||||
/// 获得膨胀体力后的物品显示 处理有选定地图鱼卡的情况
|
||||
/// </summary>
|
||||
/// <param name="inflate">体力膨胀系数 == null 当前实时系数</param>
|
||||
/// <returns></returns>
|
||||
public List<ItemData> GetItemDataByDropIdLureInflation(float? inflate, int dropId, int mapId = -1, bool afterAdding = false)
|
||||
{
|
||||
List<ItemData> itemDataList = GetItemDataByDropId(dropId, mapId, afterAdding);
|
||||
LureInflation(itemDataList, inflate);
|
||||
return itemDataList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获得膨胀体力后的物品显示
|
||||
/// </summary>
|
||||
/// <param name="inflate">体力膨胀系数 == null 当前实时系数</param>
|
||||
/// <param name="dropIDList"></param>
|
||||
/// <param name="isShow"></param>
|
||||
/// <returns></returns>
|
||||
public List<ItemData> GetItemByDropListLureInflation(float? inflate, List<int> dropIDList)
|
||||
{
|
||||
if (dropIDList == null || dropIDList.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
List<ItemData> itemDataList = new List<ItemData>();
|
||||
foreach (var t in dropIDList)
|
||||
{
|
||||
List<ItemData> itemDatas = GetItemDataByDropIdAndType(t);
|
||||
if (itemDatas != null && itemDatas.Count > 0)
|
||||
{
|
||||
itemDataList.AddRange(itemDatas);
|
||||
}
|
||||
}
|
||||
//itemDataList不需要重复合并
|
||||
if (itemDataList.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
LureInflation(itemDataList, inflate);
|
||||
return itemDataList;
|
||||
}
|
||||
/// <summary>
|
||||
/// 发放物品 并且膨胀体力
|
||||
/// </summary>
|
||||
/// <param name="inflate">体力膨胀系数 == null 当前实时系数</param>
|
||||
/// <param name="dropID"></param>
|
||||
/// <param name="isShow"></param>
|
||||
/// <param name="rewardType"></param>
|
||||
/// <returns></returns>
|
||||
public List<ItemData> AddItemByDropLureInflation(float? inflate, int dropID, ChangeSource changeSource, bool isShow = true, RewardType rewardType = RewardType.Normal)
|
||||
{
|
||||
List<ItemData> itemDataList = GetItemDataByDropIdAndType(dropID);
|
||||
if (itemDataList != null)
|
||||
{
|
||||
LureInflation(itemDataList, inflate);
|
||||
if (isShow)
|
||||
{
|
||||
game.PlayerDataStorage.PlayerSaveAction.Present(() => GContext.Publish(new ShowData(itemDataList, rewardType)));
|
||||
}
|
||||
AddItem(itemDataList, changeSource);
|
||||
}
|
||||
return itemDataList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发放物品 并且膨胀体力
|
||||
/// </summary>
|
||||
/// <param name="inflate">体力膨胀系数 == null 当前实时系数</param>
|
||||
/// <param name="dropIDList"></param>
|
||||
/// <param name="isShow"></param>
|
||||
/// <returns></returns>
|
||||
public List<ItemData> AddItemByDropListLureInflation(float? inflate, List<int> dropIDList, ChangeSource changeSource, bool isShow = true, RewardType rewardType = RewardType.Normal)
|
||||
{
|
||||
if (dropIDList == null || dropIDList.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
List<ItemData> itemDataList = new List<ItemData>();
|
||||
foreach (var t in dropIDList)
|
||||
{
|
||||
List<ItemData> itemDatas = GetItemDataByDropIdAndType(t);
|
||||
if (itemDatas != null && itemDatas.Count > 0)
|
||||
{
|
||||
itemDataList.AddRange(itemDatas);
|
||||
}
|
||||
}
|
||||
//itemDataList不需要重复合并
|
||||
if (itemDataList.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
LureInflation(itemDataList, inflate);
|
||||
if (isShow)
|
||||
{
|
||||
game.PlayerDataStorage.PlayerSaveAction.Present(() => GContext.Publish(new ShowData(itemDataList, rewardType)));
|
||||
}
|
||||
AddItem(itemDataList, changeSource);
|
||||
return itemDataList;
|
||||
}
|
||||
/// <summary>
|
||||
/// 对 itemDataList 中的体力进行膨胀
|
||||
/// </summary>
|
||||
/// <param name="itemDataList"></param>
|
||||
/// <param name="inflate">体力膨胀系数 == null 当前实时系数</param>
|
||||
public void LureInflation(List<ItemData> itemDataList, float? inflate)
|
||||
{
|
||||
if (inflate == null)
|
||||
{
|
||||
inflate = GContext.container.Resolve<PlayerData>().InflationRate;//等级膨胀系数
|
||||
}
|
||||
for (int i = 0; i < itemDataList.Count; i++)
|
||||
{
|
||||
ItemData itemData = itemDataList[i];
|
||||
itemData.inflate = (float)inflate;
|
||||
if (itemData.id == 1001)
|
||||
{
|
||||
itemData.origin = itemData.count;
|
||||
itemData.count = IntInflation(itemData.count, inflate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 智能处理itemData.count的膨胀
|
||||
/// </summary>
|
||||
/// <param name="itemData"></param>
|
||||
/// <param name="inflate"></param>
|
||||
/// <returns>True if it needs inflation.</returns>
|
||||
public bool LureInflation(ItemData itemData, float? inflate)
|
||||
{
|
||||
if (itemData.id != 1001)
|
||||
return false;
|
||||
ItemInflation(itemData, inflate);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 对itemData.count进行膨胀
|
||||
/// </summary>
|
||||
/// <param name="itemData"></param>
|
||||
/// <param name="inflate">体力膨胀系数 == null 当前实时系数</param>
|
||||
public void ItemInflation(ItemData itemData, float? inflate)
|
||||
{
|
||||
itemData.origin = itemData.count;
|
||||
if (inflate == null)
|
||||
{
|
||||
inflate = GContext.container.Resolve<PlayerData>().InflationRate;//等级膨胀系数
|
||||
}
|
||||
itemData.inflate = (float)inflate;
|
||||
itemData.count = IntInflation(itemData.count, inflate);
|
||||
}
|
||||
/// <summary>
|
||||
/// 对数字进行膨胀
|
||||
/// </summary>
|
||||
/// <param name="origin"></param>
|
||||
/// <param name="inflate">体力膨胀系数 == null 当前实时系数</param>
|
||||
/// <returns></returns>
|
||||
public int IntInflation(int origin, float? inflate)
|
||||
{
|
||||
if (inflate == null)
|
||||
{
|
||||
inflate = GContext.container.Resolve<PlayerData>().InflationRate;//等级膨胀系数
|
||||
}
|
||||
if (inflate > 0)
|
||||
{
|
||||
float count = origin * (1 + inflate.Value);
|
||||
var roundingStep = _tables.TbGlobalConfig.LureScaleRoundingStep;
|
||||
int stepCount = roundingStep.Count / 2;
|
||||
int step = roundingStep[stepCount * 2];
|
||||
for (int i = 0; i < stepCount; i++)
|
||||
{
|
||||
if (count <= roundingStep[i])
|
||||
{
|
||||
step = roundingStep[i + stepCount];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float a = count / step;
|
||||
var b = Math.Round(a, MidpointRounding.AwayFromZero);
|
||||
origin = (int)b * step;
|
||||
}
|
||||
return origin;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using LitJson;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GameCore
|
||||
{
|
||||
public class TotalRechargeData
|
||||
{
|
||||
public int eventID;
|
||||
public int progress;
|
||||
public int vip;
|
||||
public int oldProgress;
|
||||
}
|
||||
public class TotalRechargeManager
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public const string TotalRechargeDataKey = "EventTotalRecharge";
|
||||
public TotalRechargeData GetEventData()
|
||||
{
|
||||
var totalRechargeData = new TotalRechargeData();
|
||||
|
||||
string data = saveStore.Read(TotalRechargeDataKey).ValueOrThrow();
|
||||
if (data != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
totalRechargeData = Newtonsoft.Json.JsonConvert.DeserializeObject<TotalRechargeData>(data) ?? throw new Newtonsoft.Json.JsonSerializationException("Null TotalRechargeData save.");
|
||||
if (totalRechargeData.vip > 10)
|
||||
{
|
||||
totalRechargeData.vip = 10;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
FishingEvent tr = GContext.container.Resolve<FishingEventData>().GetEventByTypeAndSubType(12, 1);
|
||||
|
||||
if (tr != null && totalRechargeData.eventID != tr.ID)
|
||||
{
|
||||
totalRechargeData = new TotalRechargeData();
|
||||
totalRechargeData.eventID = tr.ID;
|
||||
totalRechargeData.vip = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
if (totalRechargeData.vip > 10)
|
||||
{
|
||||
totalRechargeData.vip = 10;
|
||||
}
|
||||
SaveTotalRechargeData(totalRechargeData);
|
||||
}
|
||||
return totalRechargeData;
|
||||
}
|
||||
|
||||
public void GetTotalReward(RewardType rewardType, string textValue)
|
||||
{
|
||||
var totalRechargeData = GetEventData();
|
||||
List<int> rewardIDs = new List<int>();
|
||||
var tables = GContext.container.Resolve<Tables>();
|
||||
var eventTotalRecharge = GetEventTotalRecharge(totalRechargeData);
|
||||
var taskIds = eventTotalRecharge.TaskList;
|
||||
var vip = totalRechargeData.vip;
|
||||
int oldProgress = totalRechargeData.oldProgress;
|
||||
int newProgress = totalRechargeData.progress;
|
||||
int taskCount = taskIds.Count;
|
||||
for (int i = 0; i < taskCount; i++)
|
||||
{
|
||||
int taskId = taskIds[i];
|
||||
CommonTask commonTask = tables.TbCommonTask.GetOrDefault(taskId);
|
||||
if (commonTask == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int iapid = commonTask.TaskParam[vip];
|
||||
var iap = tables.TbIAPItemList.GetOrDefault(iapid);
|
||||
if (oldProgress < iap.VIPPoint && newProgress >= iap.VIPPoint)
|
||||
{
|
||||
int rewardID = commonTask.TaskReward[vip];
|
||||
rewardIDs.Add(rewardID);
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("event_totalrecharge"))
|
||||
{
|
||||
e.AddContent("task_id", taskId)
|
||||
.AddContent("reward_id", rewardID);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (rewardIDs.Count > 0)
|
||||
{
|
||||
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
var itemData = playerItemData.GetItemByDropListLureInflation(null, rewardIDs);
|
||||
var showData = new ShowData(itemData, rewardType);
|
||||
showData.textValue = textValue;
|
||||
GContext.Publish(showData);
|
||||
playerItemData.AddItem(itemData, null);
|
||||
}
|
||||
totalRechargeData.oldProgress = totalRechargeData.progress;
|
||||
SaveTotalRechargeData(totalRechargeData);
|
||||
}
|
||||
|
||||
public void AddProgress(int progress)
|
||||
{
|
||||
TotalRechargeData totalRechargeData = GetEventData();
|
||||
if (CheckTotalRechargeOpen(totalRechargeData))
|
||||
{
|
||||
totalRechargeData.progress += progress;
|
||||
SaveTotalRechargeData(totalRechargeData);
|
||||
}
|
||||
}
|
||||
public bool CheckTotalRechargeOpen()
|
||||
{
|
||||
TotalRechargeData totalRechargeData = GetEventData();
|
||||
return CheckTotalRechargeOpen(totalRechargeData);
|
||||
}
|
||||
EventTotalRecharge GetEventTotalRecharge(TotalRechargeData totalRechargeData)
|
||||
{
|
||||
if (totalRechargeData == null || totalRechargeData.eventID == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var _tables = GContext.container.Resolve<Tables>();
|
||||
FishingEvent fishingEvent = _tables.TbFishingEvent.GetOrDefault(totalRechargeData.eventID);
|
||||
if (fishingEvent == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return _tables.TbEventTotalRecharge[fishingEvent.RedirectID];
|
||||
}
|
||||
public bool CheckTotalRechargeOpen(TotalRechargeData totalRechargeData)
|
||||
{
|
||||
if (totalRechargeData == null || totalRechargeData.eventID == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var _tables = GContext.container.Resolve<Tables>();
|
||||
FishingEvent fishingEvent = _tables.TbFishingEvent.GetOrDefault(totalRechargeData.eventID);
|
||||
if (fishingEvent == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LimitedTime limitedTime = fishingEvent.TimeDefinition as LimitedTime;
|
||||
var startTime = DateTime.Parse(limitedTime.StartTime);
|
||||
var endTime = DateTime.Parse(limitedTime.EndTime);
|
||||
var curTime = ZZTimeHelper.UtcNow();
|
||||
if (curTime < startTime || curTime > endTime)
|
||||
{
|
||||
if (totalRechargeData.oldProgress != totalRechargeData.progress)
|
||||
{
|
||||
RewardType rewardType = RewardType.FishBoxFlyRestore;
|
||||
string textValue = LocalizationMgr.GetText("UI_FishingRewardPanel_101042");
|
||||
GetTotalReward(rewardType, textValue);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
var eventTotalRecharge = GetEventTotalRecharge(totalRechargeData);
|
||||
var taskIds = eventTotalRecharge.TaskList;
|
||||
var vip = totalRechargeData.vip;
|
||||
int oldProgress = totalRechargeData.oldProgress;
|
||||
int taskCount = taskIds.Count;
|
||||
int taskId = taskIds[^1];
|
||||
CommonTask commonTask = _tables.TbCommonTask.GetOrDefault(taskId);
|
||||
if (commonTask == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int iapid = commonTask.TaskParam[vip];
|
||||
var iap = _tables.TbIAPItemList.GetOrDefault(iapid);
|
||||
|
||||
return totalRechargeData.oldProgress < iap.VIPPoint;
|
||||
}
|
||||
|
||||
void SaveTotalRechargeData(TotalRechargeData totalRechargeData)
|
||||
{
|
||||
saveStore.Write(TotalRechargeDataKey, JsonMapper.ToJson(totalRechargeData)).RequireAccepted();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,942 +0,0 @@
|
||||
using asap.core;
|
||||
using game;
|
||||
using GameCore;
|
||||
using IngameDebugConsole;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UI.HookWheel;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public class DebugFunc
|
||||
{
|
||||
public static void Init()
|
||||
{
|
||||
var config = GContext.container.Resolve<IConfig>();
|
||||
if (config != null && (config.Get("DEBUG", "0") == "1" || config.Get("GM", "0") == "1"))
|
||||
{
|
||||
DebugLogConsole.AddCommand<string, string>("Go.show", "Shows a GameObject", ShowGO);
|
||||
DebugLogConsole.AddCommand<string, string>("Go.hide", "Shows a GameObject", HideGO);
|
||||
|
||||
DebugLogConsole.AddCommand<int, string>("Render.quality", "change quality", SetQuality);
|
||||
DebugLogConsole.AddCommand<float, string>("Render.scale", "Set render scale [0.1-2]", SetRendererScale);
|
||||
DebugLogConsole.AddCommand<int, string>("Render.aa", "anti aliasing", SetAntiAliasing);
|
||||
DebugLogConsole.AddCommand<string>("Render.ShadowToggle", "Toggle Shadow", ToggleShadow);
|
||||
DebugLogConsole.AddCommand<string, string>("Render.Pipline", "light num", SetRenderPipline);
|
||||
|
||||
DebugLogConsole.AddCommand<string>("info.GPU", "GPU Info", GpuInfo);
|
||||
DebugLogConsole.AddCommand<string>("info.CPU", "CPU Info", CpuInfo);
|
||||
DebugLogConsole.AddCommand<string>("info.Support", "Device Support", SupportInfo);
|
||||
|
||||
DebugLogConsole.AddCommand("FPS", "Toggle FPS", Fps);
|
||||
DebugLogConsole.AddCommand("LTPL", "开关 Leviathan Timeline 性能日志(不影响 FPS 浮层)", ToggleLeviathanTimelinePerf);
|
||||
DebugLogConsole.AddCommand<int>("LTCW", "设置 CatchUp 消费等待(ms) 1~50", SetLeviathanCatchUpWaitMs);
|
||||
DebugLogConsole.AddCommand("LTCW?", "查询 CatchUp 消费等待(ms)", GetLeviathanCatchUpWaitMs);
|
||||
|
||||
DebugLogConsole.AddCommand<int>("Render.antiAliasing", "change antiAliasing", SetRTAntiAliasing);
|
||||
DebugLogConsole.AddCommand<string>("T.hotupdate", "hot update test", HotUpdateTest);
|
||||
DebugLogConsole.AddCommand<string, string>("UI.hide", "Hide UI", ToggleUI);
|
||||
DebugLogConsole.AddCommand<int, string>("Misc.tg", "Hide misc", ToggleMisc);
|
||||
DebugLogConsole.AddCommand("aihelp", "open aihelp", OpenAIHelp);
|
||||
DebugLogConsole.AddCommand("review", "open review", OpenReview);
|
||||
|
||||
DebugLogConsole.AddCommand("Pay.sku", "sku list", GetSKUs);
|
||||
DebugLogConsole.AddCommand("ad.debug", "Active Ad Debug", ActiveAdDebug);
|
||||
DebugLogConsole.AddCommand("ad.media", "Active Ad Media Debug", ActiveAdMedia);
|
||||
|
||||
DebugLogConsole.AddCommand("notify.send", "Local Notify Test", NotifyTest);
|
||||
DebugLogConsole.AddCommand("notify.setting", " Notify set", NotifySettingTest);
|
||||
DebugLogConsole.AddCommand<string>("notify.status", " Notify Status", NotificationStatus);
|
||||
DebugLogConsole.AddCommand<string>("notify.time", "Notify at certain time", NotifyAtTime);
|
||||
|
||||
|
||||
DebugLogConsole.AddCommand("mail.status", "Print cache mail", PrintMail);
|
||||
DebugLogConsole.AddCommand("mail.fetch", "fetch mail to cache", FetchMail);
|
||||
DebugLogConsole.AddCommand("rt.test", "test realtime", RtTest);
|
||||
|
||||
DebugLogConsole.AddCommand<int>("account.loginTime", "login time - 1day", LoginTime);
|
||||
DebugLogConsole.AddCommand("account.info", "account info", AccoutInfo);
|
||||
DebugLogConsole.AddCommand("account.authortest", "author test", AuthorTest);
|
||||
|
||||
DebugLogConsole.AddCommand("aihelp.test", "aihelp test", AIHelpTest);
|
||||
|
||||
DebugLogConsole.AddCommand("open.washing", "Open Washing Activity", OpenWashingActivity);
|
||||
|
||||
DebugLogConsole.AddCommand("deeplink.test", "Test if deeplink connects. If so, returns pid and uid.", TestDeepLink);
|
||||
|
||||
DebugLogConsole.AddCommand<int>("PotionGoTo", "Potion Go To", PotionGoTo);
|
||||
DebugLogConsole.AddCommand("potion.clear", "Potion Go To", PotionClear);
|
||||
|
||||
DebugLogConsole.AddCommand("pinball.clear", "Clear Pinball Data", PinballDataClear);
|
||||
DebugLogConsole.AddCommand<float>("pinball.fire", "Launch Pinball Pellet", PinballFirePellet);
|
||||
|
||||
DebugLogConsole.AddCommand("scratch.clear", "Clear Scratch Ticket Data", ScratchTicketDataClear);
|
||||
|
||||
DebugLogConsole.AddCommand<int>("shooting.go", "Goto", ShootingRangeDebug);
|
||||
DebugLogConsole.AddCommand("eventpartner.activate", "Activate a temp event", EventPartnerDebugActivate);
|
||||
DebugLogConsole.AddCommand("bingo.ts", "OpenTestScene", ShowTestBingoPanel);
|
||||
DebugLogConsole.AddCommand("bingo.seeds", "TestBingoSeedPickingRng", TestBingoSeedPickingRng);
|
||||
DebugLogConsole.AddCommand("bingo.instant", "Instant Bingo.", TestBingoInstantBingo);
|
||||
|
||||
DebugLogConsole.AddCommand("srp.fswave", "toggle fullscreen wave", ToggleFSWave);
|
||||
DebugLogConsole.AddCommand<int>("ib.lvl", "infinitebuilding level", SetInfiniteBuildLvl);
|
||||
DebugLogConsole.AddCommand("srp.fsblur", "toggle fullscreen blur", ToggleFSBlur);
|
||||
|
||||
DebugLogConsole.AddCommand<string>("stash", "test reward stash service", TestRewardStashService);
|
||||
DebugLogConsole.AddCommand("guide.clear", "clear guide data", ClearGuide);
|
||||
DebugLogConsole.AddCommand("pinataAct", "load pinata Act", LoadPinataAct);
|
||||
DebugLogConsole.AddCommand("lastpayment.clear", "Clear Last Payment Time", ClearLastPaymentTime);
|
||||
DebugLogConsole.AddCommand("HookWheelForecActive", "Hook Wheel Forec Active", HookWheelForecActive);
|
||||
|
||||
DebugLogConsole.AddCommand("eps.invitecount", "GetInvitationCount", PrintEventPartnerStatueInvitationCount);
|
||||
DebugLogConsole.AddCommand("eps.sendbot", "Start recommending bots", EventPartnerStatueActivateBotInvitation);
|
||||
DebugLogConsole.AddCommand("eps.bot", "Peek Robot Data", EventPartnerStatuePeekRobot);
|
||||
DebugLogConsole.AddCommand("eps.pool", "Peek Player Info Pool", EventPartnerStatuePeekPlayerInfoPool);
|
||||
DebugLogConsole.AddCommand("eps.score", "Peek Score", EventPartnerStatuePeekScore);
|
||||
DebugLogConsole.AddCommand<int>("eps.rt", "Test rt", EventPartnerStatueTestRt);
|
||||
DebugLogConsole.AddCommand("eps.i", "Peek inflationrate", EventPartnerStatuePeekInflationRate);
|
||||
|
||||
DebugLogConsole.AddCommand<int>("sr.set", "Set Social Rush Progress", SocialRushTest);
|
||||
DebugLogConsole.AddCommand("sr.reset", "Reset Social Rush", SocialRushReset);
|
||||
DebugLogConsole.AddCommand("sr.peek", "Peek Social Rush", SocialRushPeek);
|
||||
DebugLogConsole.AddCommand("mft.guide", "Mow For Treasure Clear Guide", EventMowForTreasureGuideClear);
|
||||
|
||||
DebugLogConsole.AddCommand("osc.peek", "Peek Offer Story Chain", OfferStoryChainPeek);
|
||||
DebugLogConsole.AddCommand("osc.reset", "Reset Offer Story Chain", OfferStoryChainReset);
|
||||
|
||||
DebugLogConsole.AddCommand("ota.peek", "Peek Offer treasure ascent", OfferTreasureAscentPeek);
|
||||
DebugLogConsole.AddCommand("ota.reset", "Reset Offer treasure ascent", OfferTreasureAscentReset);
|
||||
|
||||
DebugLogConsole.AddCommand<int>("pu.bk", "Block Pinball Kick if input is not 0. If input is 0, unblock.", PinballUncountableBlockKick);
|
||||
}
|
||||
}
|
||||
|
||||
private static void TestDeepLink()
|
||||
{
|
||||
var res = AFHelper.Instance.GetDLReferer(out var pid, out var uid);
|
||||
Debug.Log($"DidReceiveDeepLink: {AFHelper.Instance.DidReceivedDeepLink}");
|
||||
Debug.Log($"IsFirstLaunch: {AFHelper.Instance.IsFirstLaunch}");
|
||||
|
||||
if (AFHelper.Instance.DeepLinkParamsDictionary is not null)
|
||||
{
|
||||
Debug.Log($"DeepLinkParamsDic:");
|
||||
foreach (var kvp in AFHelper.Instance.DeepLinkParamsDictionary)
|
||||
{
|
||||
Debug.Log($"{kvp.Key}: {kvp.Value}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"DeepLinkParamsDic: Empty.");
|
||||
}
|
||||
var message = res ? $"Deeplink success with pid: {pid}, uid: {uid}" : "Deeplink fail.";
|
||||
Debug.Log(message);
|
||||
}
|
||||
|
||||
private static void NotifyAtTime(string time)
|
||||
{
|
||||
var now = ZZTimeHelper.UtcNow();
|
||||
var fireTime = DateTime.SpecifyKind(DateTime.Parse(time), DateTimeKind.Utc);
|
||||
fireTime = fireTime.AddDays(now <= fireTime ? 0 : 1);
|
||||
var service = GContext.container.Resolve<ILocalNotificationService>();
|
||||
service.ScheduleOneTimeNotification(434200, fireTime.ToLocalTime(), "Test Note at Time",
|
||||
$"Test Notification @ {fireTime}.");
|
||||
}
|
||||
|
||||
private static void NotifyTest()
|
||||
{
|
||||
ILocalNotificationService service = GContext.container.Resolve<ILocalNotificationService>();
|
||||
service.ScheduleOneTimeNotification(434200, DateTime.Now + TimeSpan.FromSeconds(3), "Notification",
|
||||
"Test Notification ");
|
||||
}
|
||||
|
||||
private static void NotifySettingTest()
|
||||
{
|
||||
ILocalNotificationService service = GContext.container.Resolve<ILocalNotificationService>();
|
||||
service.OpenSettings();
|
||||
}
|
||||
|
||||
private static string NotificationStatus()
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
return Unity.Notifications.Android.AndroidNotificationCenter.UserPermissionToPost.ToString();
|
||||
#else
|
||||
return Unity.Notifications.iOS.iOSNotificationCenter.GetNotificationSettings().AuthorizationStatus.ToString();
|
||||
#endif
|
||||
}
|
||||
|
||||
private static string HotUpdateTest()
|
||||
{
|
||||
return "hot update test Ver 0.6 - 2024-03-26:1434";
|
||||
}
|
||||
|
||||
private static string ToggleUI(string path)
|
||||
{
|
||||
GameObject homePanelGo = UIManager.Instance.gameObject.transform.Find("HomePanel").gameObject;
|
||||
if (homePanelGo == null)
|
||||
{
|
||||
return "UI not found.";
|
||||
}
|
||||
|
||||
GameObject go = homePanelGo.transform.Find(path).gameObject;
|
||||
if (go == null)
|
||||
{
|
||||
return "path not found";
|
||||
}
|
||||
|
||||
go.SetActive(!go.activeSelf);
|
||||
return $"UI {go} toggled to {go.activeSelf}.";
|
||||
}
|
||||
|
||||
private static string ToggleMisc(int idx)
|
||||
{
|
||||
GameObject go = GetGOFromPath("Env_101(Clone)/Misc");
|
||||
int childCount = go.transform.childCount;
|
||||
if (idx >= childCount)
|
||||
return $"index {idx} exceeds children count{childCount}";
|
||||
var child = go.transform.GetChild(idx).gameObject;
|
||||
if (child == null)
|
||||
return $"child {idx} not found";
|
||||
child.SetActive(!child.activeSelf);
|
||||
return $"{child} toggled to {child.activeSelf}.";
|
||||
}
|
||||
|
||||
private static void OpenAIHelp()
|
||||
{
|
||||
AIHelp.AIHelpSupport.Show("E001");
|
||||
}
|
||||
|
||||
private static void OpenReview()
|
||||
{
|
||||
var config = GContext.container.Resolve<IConfig>();
|
||||
tysdk.AppRequestReview.RequestReview(config.Get(GConstant.K_APP_Store_URL, string.Empty));
|
||||
}
|
||||
|
||||
private static string ShowGO(string path)
|
||||
{
|
||||
GameObject go = GetGOFromPath(path);
|
||||
if (go != null)
|
||||
{
|
||||
go.SetActive(true);
|
||||
}
|
||||
|
||||
return $"GameObject not found";
|
||||
}
|
||||
|
||||
private static string HideGO(string path)
|
||||
{
|
||||
GameObject go = GetGOFromPath(path);
|
||||
if (go != null)
|
||||
{
|
||||
go.SetActive(false);
|
||||
return $"{go.name} has been hidden";
|
||||
}
|
||||
|
||||
return $"GameObject not found";
|
||||
}
|
||||
|
||||
private static string SetQuality(int level)
|
||||
{
|
||||
QualityManager.ChangeQuality(level);
|
||||
zzwater.URPHelper.RendererFeatureSetActive(false, "FSBlur");
|
||||
zzwater.URPHelper.RendererFeatureSetActive(false, "FSWave");
|
||||
return $"quality set to {QualitySettings.names[QualitySettings.GetQualityLevel()]}";
|
||||
}
|
||||
|
||||
private static void SetRTAntiAliasing(int level)
|
||||
{
|
||||
var rt = new RenderTexture((int)125, (int)125, 24, RenderTextureFormat.ARGB32);
|
||||
rt.antiAliasing = level;
|
||||
}
|
||||
|
||||
private static string SetRendererScale(float ratio)
|
||||
{
|
||||
if (ratio == 0)
|
||||
{
|
||||
return $"Render scale {CurrentRenderPipeline.renderScale}";
|
||||
}
|
||||
|
||||
ratio = Mathf.Clamp(ratio, 0.1f, 2f);
|
||||
var uprAsset = CurrentRenderPipeline;
|
||||
if (uprAsset == null) return "No UniversalRenderPipelineAsset";
|
||||
var preValue = uprAsset.renderScale;
|
||||
uprAsset.renderScale = ratio;
|
||||
return $"Render scale {preValue} set to {ratio}";
|
||||
}
|
||||
|
||||
private static string SetAntiAliasing(int count)
|
||||
{
|
||||
var uprAsset = CurrentRenderPipeline;
|
||||
if (uprAsset == null) return "No UniversalRenderPipelineAsset";
|
||||
var preValue = uprAsset.msaaSampleCount;
|
||||
uprAsset.msaaSampleCount = count;
|
||||
return $"Render scale {preValue} set to {count}";
|
||||
}
|
||||
|
||||
private static string SetRenderPipline(string name)
|
||||
{
|
||||
var currentPiplineName = GraphicsSettings.currentRenderPipeline.name;
|
||||
if (name == "?")
|
||||
{
|
||||
return $"Pipline set to {currentPiplineName}";
|
||||
}
|
||||
else if (name == "-")
|
||||
{
|
||||
QualitySettings.renderPipeline = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var pipelineAsset = Addressables.LoadAssetAsync<RenderPipelineAsset>(name).WaitForCompletion();
|
||||
QualitySettings.renderPipeline = pipelineAsset;
|
||||
}
|
||||
|
||||
var newPipelineName = GraphicsSettings.currentRenderPipeline.name;
|
||||
return $"{currentPiplineName} set to {newPipelineName}";
|
||||
}
|
||||
|
||||
|
||||
private static string ToggleShadow()
|
||||
{
|
||||
var cam = Camera.main;
|
||||
if (cam != null)
|
||||
{
|
||||
var comp = cam.GetComponent<UniversalAdditionalCameraData>();
|
||||
var preValue = comp.renderShadows;
|
||||
comp.renderShadows = !preValue;
|
||||
return $"Shadow {preValue} set to {comp.renderShadows}";
|
||||
}
|
||||
|
||||
return "Camera not found";
|
||||
}
|
||||
|
||||
private static string CpuInfo()
|
||||
{
|
||||
var props = typeof(SystemInfo).GetProperties()
|
||||
.Where(x => x.Name.StartsWith("processor"));
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine();
|
||||
foreach (var prop in props)
|
||||
{
|
||||
sb.AppendLine($"{prop.Name}: {prop.GetValue(null)}");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string GpuInfo()
|
||||
{
|
||||
var props = typeof(SystemInfo).GetProperties()
|
||||
.Where(x => x.Name.StartsWith("graphics"));
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine();
|
||||
foreach (var prop in props)
|
||||
{
|
||||
sb.AppendLine($"{prop.Name}: {prop.GetValue(null)}");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string SupportInfo()
|
||||
{
|
||||
var props = typeof(SystemInfo).GetProperties()
|
||||
.Where(x => x.Name.StartsWith("support"));
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine();
|
||||
foreach (var prop in props)
|
||||
{
|
||||
sb.AppendLine($"{prop.Name}: {prop.GetValue(null)}");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static void Fps()
|
||||
{
|
||||
game.RootCtx.showFPS = !game.RootCtx.showFPS;
|
||||
}
|
||||
|
||||
private static void ToggleLeviathanTimelinePerf()
|
||||
{
|
||||
var on = !LeviathanLogConfig.EnableTimelineVideoPerfLog;
|
||||
LeviathanLogConfig.EnableTimelineVideoPerfLog = on;
|
||||
PlayerPrefs.SetInt("LeviathanTimelinePerf", on ? 1 : 0);
|
||||
PlayerPrefs.Save();
|
||||
UnityEngine.Debug.Log($"[Debug] Leviathan Timeline 性能日志 = {on}(Logcat 搜 LeviathanTimeline)");
|
||||
}
|
||||
|
||||
private static void SetLeviathanCatchUpWaitMs(int ms)
|
||||
{
|
||||
var decoders = UnityEngine.Object.FindObjectsOfType<LeviathanVideoDecoderBase>(true);
|
||||
if (decoders == null || decoders.Length == 0)
|
||||
{
|
||||
UnityEngine.Debug.Log("[Debug] LTCW: 未找到活跃的 LeviathanVideoDecoderBase 实例");
|
||||
return;
|
||||
}
|
||||
foreach (var d in decoders)
|
||||
{
|
||||
d.CatchUpConsumePerFrameWaitMs = ms;
|
||||
}
|
||||
UnityEngine.Debug.Log($"[Debug] LTCW: 已将 {decoders.Length} 个解码器 CatchUpConsumePerFrameWaitMs 设为 {ms}ms");
|
||||
}
|
||||
|
||||
private static void GetLeviathanCatchUpWaitMs()
|
||||
{
|
||||
var decoders = UnityEngine.Object.FindObjectsOfType<LeviathanVideoDecoderBase>();
|
||||
if (decoders == null || decoders.Length == 0)
|
||||
{
|
||||
UnityEngine.Debug.Log("[Debug] LTCW?: 未找到活跃的 LeviathanVideoDecoderBase 实例");
|
||||
return;
|
||||
}
|
||||
foreach (var d in decoders)
|
||||
{
|
||||
UnityEngine.Debug.Log($"[Debug] LTCW? {d.name}: CatchUpConsumePerFrameWaitMs={d.CatchUpConsumePerFrameWaitMs}ms");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static UniversalRenderPipelineAsset CurrentRenderPipeline
|
||||
{
|
||||
get
|
||||
{
|
||||
return GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset;
|
||||
//return (UniversalRenderPipelineAsset) QualitySettings.renderPipeline;
|
||||
}
|
||||
}
|
||||
|
||||
private static async void GetSKUs()
|
||||
{
|
||||
var iapService = GContext.container.Resolve<game.IIAPService>();
|
||||
if (iapService == null)
|
||||
{
|
||||
UnityEngine.Debug.Log("iapService is null");
|
||||
;
|
||||
return;
|
||||
}
|
||||
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine();
|
||||
var skus = await iapService.GetSKUs();
|
||||
foreach (var sku in skus)
|
||||
{
|
||||
sb.AppendLine();
|
||||
sb.Append($" {sku.price} code {sku.price_currency_code} ");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
UnityEngine.Debug.Log(sb.ToString());
|
||||
}
|
||||
|
||||
private static void ActiveAdDebug()
|
||||
{
|
||||
MaxSdk.SetVerboseLogging(true);
|
||||
MaxSdk.ShowCreativeDebugger();
|
||||
}
|
||||
|
||||
private static void ActiveAdMedia()
|
||||
{
|
||||
MaxSdk.ShowMediationDebugger();
|
||||
}
|
||||
|
||||
private static GameObject GetGOFromPath(string path)
|
||||
{
|
||||
var root = SceneManager.GetActiveScene().GetRootGameObjects();
|
||||
var splitPath = path.Split('/');
|
||||
var rootName = splitPath[0];
|
||||
var rootGo = root.FirstOrDefault(x => x.name == rootName);
|
||||
if (rootGo == null) return null;
|
||||
if (splitPath.Length == 1) return rootGo;
|
||||
var childPath = string.Join("/", splitPath.Skip(1));
|
||||
return rootGo.transform.Find(childPath)?.gameObject;
|
||||
}
|
||||
|
||||
private static void PrintMail()
|
||||
{
|
||||
var mailService = GContext.container.Resolve<IInGameMailService>() as InGameMailService;
|
||||
mailService.PrintCacheMail();
|
||||
}
|
||||
|
||||
private static void FetchMail()
|
||||
{
|
||||
GContext.container.Resolve<IInGameMailService>().FetchMail();
|
||||
}
|
||||
|
||||
private static void RtTest()
|
||||
{
|
||||
var rtService = GContext.container.Resolve<IRTService>() as SignalRService;
|
||||
rtService.Test();
|
||||
}
|
||||
|
||||
private static void LoginTime(int hours)
|
||||
{
|
||||
var userService = GContext.container.Resolve<game.IUserService>() as game.PlayFabUserService;
|
||||
if (userService == null) return;
|
||||
|
||||
var loginTime = userService.LoginTime;
|
||||
var newLoginTime = loginTime.AddHours(hours);
|
||||
var prop = userService.GetType().GetProperty("LoginTime");
|
||||
prop.SetValue(userService, newLoginTime);
|
||||
Debug.Log($"loginTime: {loginTime} newLoginTime: {newLoginTime} prop: {userService.LoginTime}");
|
||||
}
|
||||
|
||||
private static void AccoutInfo()
|
||||
{
|
||||
var userService = GContext.container.Resolve<game.IUserService>() as game.PlayFabUserService;
|
||||
if (userService == null) return;
|
||||
Debug.Log($"pfid: {userService.UserId} tyid: {userService.CustomId} lastLogin: {userService.LoginTime}");
|
||||
|
||||
if (PlayerPrefs.HasKey("KEY_ACCOUNT_INFO"))
|
||||
{
|
||||
Debug.Log("Saved account info: " + PlayerPrefs.GetString("KEY_ACCOUNT_INFO"));
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("No saved account info");
|
||||
}
|
||||
}
|
||||
|
||||
private static async void AuthorTest()
|
||||
{
|
||||
var svr = GContext.container.Resolve<ICustomServerMgr>();
|
||||
var result = await svr.GetStringAsync("account/authortest");
|
||||
Debug.Log($"AuthorTest {result}");
|
||||
}
|
||||
|
||||
private static void AIHelpTest()
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.AIHelp_Msg, true, 1);
|
||||
}
|
||||
|
||||
private static void OpenWashingActivity()
|
||||
{
|
||||
ToggleUI("safearea/RightPanel/layout/btn_washing");
|
||||
}
|
||||
|
||||
private static void PotionClear()
|
||||
{
|
||||
GContext.container.Resolve<EventPotionDataManager>().ClearPotionData();
|
||||
Debug.Log("--- Potion Clear ok");
|
||||
}
|
||||
|
||||
private static void PotionGoTo(int levelID)
|
||||
{
|
||||
GContext.container.Resolve<EventPotionDataManager>().GoToPotionLevel(levelID);
|
||||
Debug.Log("--- PotionGoTo levelID:" + levelID + ", ok");
|
||||
}
|
||||
|
||||
private static void PinballDataClear()
|
||||
{
|
||||
GContext.container.Resolve<EventPinballDataManager>().ClearData();
|
||||
Debug.Log("--- PinballDataClear ok");
|
||||
}
|
||||
|
||||
private static void PinballFirePellet(float force)
|
||||
{
|
||||
FishingPinballAct.Publish(new EventPinballLaunchPelletData()
|
||||
{
|
||||
Force = force
|
||||
});
|
||||
Debug.Log("--- PinballFirePellet ok, force:" + force);
|
||||
}
|
||||
|
||||
private static void ScratchTicketDataClear()
|
||||
{
|
||||
GContext.container.Resolve<EventScratchTicketDataManager>().ClearData();
|
||||
Debug.Log("--- Scratch Ticket Data Clear ok");
|
||||
}
|
||||
|
||||
private static void ShootingRangeDebug(int n)
|
||||
{
|
||||
GContext.Publish(new EventShootingRangeDebug { StageIdx = n });
|
||||
Debug.Log($"Switch to {n}.");
|
||||
}
|
||||
|
||||
private static void EventPartnerDebugActivate()
|
||||
{
|
||||
// var d = GContext.container.Resolve<EventPartnerData>();
|
||||
// if (d is null)
|
||||
// {
|
||||
// Debug.Log( $"<color=#2d7cee>[EventPartner] No data found, cannot activate.</color>");
|
||||
// return;
|
||||
// }
|
||||
// _ = d.LoadDataDebug();
|
||||
Debug.Log($"<color=#2d7cee>[EventPartner] This function is obsoleted for now.</color>");
|
||||
}
|
||||
|
||||
private static void ToggleFSWave()
|
||||
{
|
||||
var feature = zzwater.URPHelper.FindAndCacheRendererFeature("FSWave");
|
||||
if (feature == null)
|
||||
Debug.LogError($"Feature FSWave not found");
|
||||
else
|
||||
feature.SetActive(!feature.isActive);
|
||||
}
|
||||
|
||||
private static void SetInfiniteBuildLvl(int level)
|
||||
{
|
||||
var s = GContext.container.Resolve<PlayerData>().InfiniteBuildingData;
|
||||
if (s == null)
|
||||
{
|
||||
Debug.LogError($"No ib data found.");
|
||||
return;
|
||||
}
|
||||
var data = new InfiniteBuildingData();
|
||||
data.Deserialize(s);
|
||||
var delta = level - data.Lvl;
|
||||
data.Lvl = level;
|
||||
for (int i = 0; i < data.Record.Count; i++)
|
||||
{
|
||||
var r = data.Record[i];
|
||||
r.Pos += Vector2.up * delta;
|
||||
data.Record[i] = r;
|
||||
}
|
||||
data.UploadData();
|
||||
Debug.Log($"Done.");
|
||||
}
|
||||
|
||||
private static void ToggleFSBlur()
|
||||
{
|
||||
var feature = zzwater.URPHelper.FindAndCacheRendererFeature("FSBlur");
|
||||
if (feature == null)
|
||||
Debug.LogError($"Feature FSWave not found");
|
||||
else
|
||||
feature.SetActive(!feature.isActive);
|
||||
}
|
||||
|
||||
private static void ShowTestBingoPanel()
|
||||
{
|
||||
GContext.container.Register<EventBingoModel>().AsSingleton();
|
||||
GContext.container.Resolve<EventBingoModel>().InitTest();
|
||||
GContext.Publish(new UnloadActToNextAct { actId = "EventBingoAct", TransitionPanel = UITypes.CloudTransitionPanel });
|
||||
}
|
||||
|
||||
private static void TestBingoSeedPickingRng()
|
||||
{
|
||||
var dataList = GContext.container.Resolve<cfg.Tables>().TbEventBingoMain.DataList;
|
||||
var tc = new EventBingoTableContext(40701);
|
||||
int[] stat = { 0, 0, 0, 0 };
|
||||
int seed, statCount = 10000;
|
||||
var sb = new System.Text.StringBuilder();
|
||||
for (int i = 0; i < statCount; i++)
|
||||
{
|
||||
seed = tc.PickSeed(roundCount: 10086);
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
if (dataList[j].SeedID.Contains(seed))
|
||||
{
|
||||
stat[j]++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sb.AppendLine($"{seed}, ");
|
||||
}
|
||||
Debug.Log($"[EventBingo] Test Result:\n1Bingo:{stat[0] / (float)statCount * 100}%,\n2Bingo: {stat[1] / (float)statCount * 100}%,\n3Bingo: {stat[2] / (float)statCount * 100}%,\n4Bingo: {stat[3] / (float)statCount * 100}%");
|
||||
Debug.Log("[EventBingo] Seeds: \n" + sb.ToString());
|
||||
}
|
||||
|
||||
private static void TestBingoInstantBingo()
|
||||
{
|
||||
var model = GContext.container.Resolve<EventBingoModel>();
|
||||
if (model == null)
|
||||
{
|
||||
Debug.Log("[EventBingo] Bingo model not found!");
|
||||
return;
|
||||
}
|
||||
model.ActivateInstantBingo();
|
||||
Debug.Log("[EventBingo] Instant Bingo activated.");
|
||||
}
|
||||
|
||||
private static void TestRewardStashService(string cmd)
|
||||
{
|
||||
const string reset = "reset", upload = "upload", load = "load", flush = "flush", add = "add";
|
||||
var service = GContext.container.Resolve<IDeferredRewardStashService>();
|
||||
switch (cmd)
|
||||
{
|
||||
case reset:
|
||||
service.Reset();
|
||||
Debug.Log("Reset Done.");
|
||||
break;
|
||||
case upload:
|
||||
service.UploadData();
|
||||
Debug.Log("Upload Done.");
|
||||
break;
|
||||
case load:
|
||||
service.LoadData();
|
||||
Debug.Log("Load Done.");
|
||||
break;
|
||||
case flush:
|
||||
service.Flush();
|
||||
Debug.Log("Flush Done.");
|
||||
break;
|
||||
case add:
|
||||
GContext.Publish(new DeferredRewardStashService.EventStashItem
|
||||
{
|
||||
Item = new ItemData(1001, 100)
|
||||
});
|
||||
Debug.Log("Add Done.");
|
||||
break;
|
||||
default:
|
||||
Debug.Log("Invalid command. Available commands:\n" +
|
||||
"- reset: Clear all stash data\n" +
|
||||
"- add: Add 1001 * 100\n" +
|
||||
"- upload: Upload stash data to playfab\n" +
|
||||
"- load: Load stash data from playfab\n" +
|
||||
"- flush: Process and clear all pending rewards");
|
||||
break;
|
||||
}
|
||||
}
|
||||
static void ClearGuide()
|
||||
{
|
||||
GContext.container.Resolve<GuideDataCenter>().GMClearGuide();
|
||||
UIManager.Instance.DestroyUI(UITypes.GuidancePanel);
|
||||
}
|
||||
static void LoadPinataAct()
|
||||
{
|
||||
GContext.Publish(new UnloadActToNextAct { actId = "PinataPackAct_Pony" });
|
||||
GContext.OnEvent<ActLoadedEvent>().Subscribe((actLoadedEvent) =>
|
||||
{
|
||||
var Act = GContext.container.ResolveAct("PinataPackAct_Pony");
|
||||
if (Act is PinataPackAct pinataPackAct)
|
||||
{
|
||||
Debug.Log($"jsd Act is PinataPackAct pinataPackAct");
|
||||
var rigidBody = pinataPackAct.GetComponentsInChildren<Rigidbody>();
|
||||
foreach (var r in rigidBody)
|
||||
{
|
||||
r.useGravity = false;
|
||||
}
|
||||
var gameObject = GetGOFromPath("PinataPackAct_Pony(Clone)/Stage1");
|
||||
gameObject.transform.localPosition = new Vector3(0, 0, 0.2f);
|
||||
gameObject.transform.localRotation = Quaternion.Euler(-21, 0, 0);
|
||||
}
|
||||
|
||||
var ui = UIManager.Instance.GetContainedUI(UITypes.PinataPack_PonySettlementPanel);
|
||||
ui.FindChildGameObject("btn_scene")?.SetActive(false);
|
||||
ui.FindChildGameObject("btn_close")?.SetActive(true);
|
||||
});
|
||||
}
|
||||
static void ClearLastPaymentTime()
|
||||
{
|
||||
GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>().Write("LastPaymentTime", "").RequireAccepted();
|
||||
}
|
||||
static void HookWheelForecActive()
|
||||
{
|
||||
GContext.container.Resolve<HookWheelManager>()?.ForceActive();
|
||||
}
|
||||
|
||||
static void PrintEventPartnerStatueInvitationCount()
|
||||
{
|
||||
var arc = GContext.container.Resolve<EventPartnerStatueArc>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("EventPartnerStatueArc not found");
|
||||
return;
|
||||
}
|
||||
Debug.Log($"InvitationCount: {arc.InvitationData.InvitationCount}, DoRecommendBot: {arc.InvitationData.DoRecommendBot}");
|
||||
}
|
||||
|
||||
static void EventPartnerStatueActivateBotInvitation()
|
||||
{
|
||||
var arc = GContext.container.Resolve<EventPartnerStatueArc>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("EventPartnerStatueArc not found");
|
||||
return;
|
||||
}
|
||||
arc.InvitationData.SetInvitationCount(1999);
|
||||
Debug.Log($"Bot invitation count set to 1999");
|
||||
}
|
||||
|
||||
// static void EventPartnerStatueRefreshInvitation()
|
||||
// {
|
||||
// var arc = GContext.container.Resolve<EventPartnerStatueArc>();
|
||||
// if (arc == null)
|
||||
// {
|
||||
// Debug.Log("EventPartnerStatueArc not found");
|
||||
// return;
|
||||
// }
|
||||
// arc.InvitationData.RefreshInvitation();
|
||||
// Debug.Log("RefreshInvitation Done");
|
||||
// }
|
||||
|
||||
static void EventPartnerStatuePeekRobot()
|
||||
{
|
||||
var arc = GContext.container.Resolve<EventPartnerStatueArc>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("EventPartnerStatueArc not found");
|
||||
return;
|
||||
}
|
||||
var str = Newtonsoft.Json.JsonConvert.SerializeObject(arc.RobotData.RobotInvitationBuffer);
|
||||
Debug.Log("RobotBuffer: " + str);
|
||||
str = Newtonsoft.Json.JsonConvert.SerializeObject(arc.RobotData.RobotList.Select(kv => kv.Value.ActionQ));
|
||||
Debug.Log("RobotActionQ: " + str);
|
||||
}
|
||||
|
||||
static void EventPartnerStatuePeekPlayerInfoPool()
|
||||
{
|
||||
var arc = GContext.container.Resolve<EventPartnerStatueArc>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("EventPartnerStatueArc not found");
|
||||
return;
|
||||
}
|
||||
var bufferStr = Newtonsoft.Json.JsonConvert.SerializeObject(arc.PlayerInfoPool.PlayerInfoDic);
|
||||
Debug.Log("PlayerInfoCache:" + bufferStr);
|
||||
}
|
||||
|
||||
static void EventPartnerStatuePeekScore()
|
||||
{
|
||||
var arc = GContext.container.Resolve<EventPartnerStatueArc>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("EventPartnerStatueArc not found");
|
||||
return;
|
||||
}
|
||||
var s = new System.Text.StringBuilder();
|
||||
foreach (var c in arc.Model.ComponentList)
|
||||
{
|
||||
s.AppendLine($"Partner{c.PartnerId}: self display:{c.ScoreSelfDisplay}, self actual:{c.ScoreSelfActual}, partner display:{c.ScorePartnerDisplay}, partner actual:{c.ScorePartnerActual}\n");
|
||||
}
|
||||
Debug.Log(s.ToString());
|
||||
}
|
||||
|
||||
static void EventPartnerStatueTestRt(int scoreAdd)
|
||||
{
|
||||
var arc = GContext.container.Resolve<EventPartnerStatueArc>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("EventPartnerStatueArc not found");
|
||||
return;
|
||||
}
|
||||
var partnerId = arc.Model.ComponentList[0].PartnerId;
|
||||
var newScore = arc.Model.ComponentList[0].ScorePartnerActual + scoreAdd;
|
||||
var se = new EventPartnerGatherRequests.EventPartnerGatherScoreEvt
|
||||
{
|
||||
PartnerId = partnerId,
|
||||
Score = newScore,
|
||||
};
|
||||
var pe = new EventPartnerGatherRequests.EventPartnerGatherPartnerEvt
|
||||
{
|
||||
PartnerInfo = new PlayerInfo
|
||||
{
|
||||
AvatarUrl = "",
|
||||
DisplayName = "TestRt",
|
||||
Level = 1,
|
||||
PlayFabId = "TestRt",
|
||||
},
|
||||
EvtType = 0,
|
||||
SlotId = 1
|
||||
};
|
||||
arc.RtSystem.TestRt(se, pe);
|
||||
}
|
||||
|
||||
static void EventPartnerStatuePeekInflationRate()
|
||||
{
|
||||
var arc = GContext.container.Resolve<EventPartnerStatueArc>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("EventPartnerStatueArc not found");
|
||||
return;
|
||||
}
|
||||
Debug.Log($"InflationRate: {arc.Model.LureInflationRate}");
|
||||
}
|
||||
|
||||
static void SocialRushTest(int progress)
|
||||
{
|
||||
var arc = GContext.container.Resolve<Assets.Scripts.UI.SocialRush.SocialRushArchitecture>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("SocialRushArchitechture not found");
|
||||
return;
|
||||
}
|
||||
arc.Model.SetProgress(progress);
|
||||
Debug.Log($"[SocialRush]Actual progress set to {progress}");
|
||||
}
|
||||
|
||||
static void SocialRushReset()
|
||||
{
|
||||
var arc = GContext.container.Resolve<Assets.Scripts.UI.SocialRush.SocialRushArchitecture>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("SocialRushArchitechture not found");
|
||||
return;
|
||||
}
|
||||
arc.Model.Reset();
|
||||
Debug.Log($"[SocialRush]Reset Done.");
|
||||
}
|
||||
static void SocialRushPeek()
|
||||
{
|
||||
var arc = GContext.container.Resolve<Assets.Scripts.UI.SocialRush.SocialRushArchitecture>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("SocialRushArchitechture not found");
|
||||
return;
|
||||
}
|
||||
Debug.Log($"[SocialRush] Display: {arc.Model.ProgressDisplay}, Actual {arc.Model.ProgressActual}, inflation rate: {arc.Model.InflationRate}, event Id: {arc.EventId}");
|
||||
}
|
||||
static void EventMowForTreasureGuideClear()
|
||||
{
|
||||
GContext.container.Resolve<EventMowForTreasure.EventMowForTreasureManager>()?.ClearGuide();
|
||||
}
|
||||
|
||||
static void OfferStoryChainReset()
|
||||
{
|
||||
var manager = GContext.container.Resolve<OfferStoryChain.Manager>();
|
||||
if (manager == null)
|
||||
{
|
||||
Debug.Log("OfferStoryChain.Manager not found");
|
||||
return;
|
||||
}
|
||||
manager.Reset();
|
||||
}
|
||||
|
||||
static void OfferStoryChainPeek()
|
||||
{
|
||||
var manager = GContext.container.Resolve<OfferStoryChain.Manager>();
|
||||
if (manager == null)
|
||||
{
|
||||
Debug.Log("OfferStoryChain.Manager not found");
|
||||
return;
|
||||
}
|
||||
var str = "EventId: " + manager.EventId + "\n" + "Progress: " + manager.Progress + "\n" + "VipLevel: " + manager.VipLevel + "\n" + "RoundCount: " + manager.RoundCount;
|
||||
Debug.Log("OfferStoryChain.Manager:" + str);
|
||||
}
|
||||
|
||||
static void OfferTreasureAscentPeek()
|
||||
{
|
||||
var manager = GContext.container.Resolve<OfferTreasureAscent.Manager>();
|
||||
if (manager == null)
|
||||
{
|
||||
Debug.Log("OfferTreasureAscent.Manager not found");
|
||||
return;
|
||||
}
|
||||
Debug.Log($"[OfferTreasureAscent] eventId: {manager.EventId}, status: {manager.Status}, roundCount: {manager.RoundCount}");
|
||||
}
|
||||
|
||||
static void OfferTreasureAscentReset()
|
||||
{
|
||||
var manager = GContext.container.Resolve<OfferTreasureAscent.Manager>();
|
||||
if (manager == null)
|
||||
{
|
||||
Debug.Log("OfferTreasureAscent.Manager not found");
|
||||
return;
|
||||
}
|
||||
manager.Reset();
|
||||
Debug.Log($"[OfferTreasureAscent] Reset Done.");
|
||||
}
|
||||
|
||||
static void PinballUncountableBlockKick(int block)
|
||||
{
|
||||
var manager = GContext.container.Resolve<PinballUncountable.Manager>();
|
||||
if (manager == null)
|
||||
{
|
||||
Debug.Log("PinballUncountable.Manager not found");
|
||||
return;
|
||||
}
|
||||
manager.EventAggregator.Publish(new PinballUncountable.EventBlockKick { Block = block != 0 });
|
||||
}
|
||||
}
|
||||
@@ -1,829 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PlayFab;
|
||||
using PlayFab.MultiplayerModels;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TMPro;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class EventFishingDuelPanel : BasePanel
|
||||
{
|
||||
Tables tables;
|
||||
Button btn_close;
|
||||
GameObject btn_close_image;
|
||||
Animation anim;
|
||||
|
||||
GameObject MiddlePanel;
|
||||
TMP_Text text_time;
|
||||
Image icon_rank;
|
||||
TMP_Text text_rank;
|
||||
Button btn_questionmark1;
|
||||
Button btn_questionmark;
|
||||
GameObject bg_bar;
|
||||
Image bar;
|
||||
TMP_Text text_progress;
|
||||
Button btn_reward;
|
||||
List<RewardItemNew> rewardItemNews;
|
||||
TMP_Text win_text_num;
|
||||
TMP_Text loss_text_num;
|
||||
TMP_Text middle_text_time;
|
||||
Button icon_ticket;
|
||||
Button btn_confirm;
|
||||
GameObject loading;
|
||||
GameObject p_text;
|
||||
TMP_Text text_num;
|
||||
|
||||
//GameObject effect_beilv_loop;
|
||||
Button btn_beilv;
|
||||
//Image btn_beilv_image;
|
||||
Button btn_beilv_max;
|
||||
//private Image btn_beilv_max_image;
|
||||
|
||||
TMP_Text text_beilv;
|
||||
TMP_Text text_beilv_max;
|
||||
TMP_Text text_beilv_show;
|
||||
|
||||
|
||||
GameObject matchingPanel;
|
||||
DuelMatchPlayer myself;
|
||||
DuelMatchPlayer enemy;
|
||||
GameObject fx_ui_matchingPanel_match_glow_01;
|
||||
TMP_Text match_text_time;
|
||||
Button but_stop;
|
||||
Coroutine matchIng;
|
||||
FishingDuelManager FDM;
|
||||
Sound audio_matchingloop;
|
||||
AudioClip audio_ui_fishingduel_matchingloop;
|
||||
IUIService uiService;
|
||||
Timer timer;
|
||||
FishingEventData fishingEventData;
|
||||
|
||||
Button btn_advert;
|
||||
|
||||
DuelRandomMapPanel randomMapPanel;
|
||||
|
||||
GameObject fishConfrimPanel;
|
||||
|
||||
TicketTips ticket_tips;
|
||||
|
||||
IDisposable disRequestSvrTime;
|
||||
float MatchPlayerProb;
|
||||
private void Awake()
|
||||
{
|
||||
fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
uiService = GContext.container.Resolve<IUIService>();
|
||||
tables = GContext.container.Resolve<Tables>();
|
||||
btn_close = transform.Find("bottom/btn_close").GetComponent<Button>();
|
||||
btn_close_image = transform.Find("bottom/btn_close/Image").gameObject;
|
||||
GContext.container.Unregister<FishingDuelManager>();
|
||||
FDM = new FishingDuelManager();
|
||||
GContext.container.RegisterInstance(FDM);
|
||||
btn_advert = transform.Find("root/MiddlePanel/bottom/btn_advert").GetComponent<Button>();
|
||||
//====================倍率调整
|
||||
btn_beilv = transform.Find("root/MiddlePanel/bottom/beilv").GetComponent<Button>();
|
||||
btn_beilv_max = transform.Find("root/MiddlePanel/bottom/beilv_max").GetComponent<Button>();
|
||||
|
||||
text_beilv = transform.Find("root/MiddlePanel/bottom/beilv/text_beilv").GetComponent<TMP_Text>();
|
||||
text_beilv_max = transform.Find("root/MiddlePanel/bottom/beilv_max/text_beilv").GetComponent<TMP_Text>();
|
||||
|
||||
|
||||
//====================
|
||||
ticket_tips = transform.Find("root/MiddlePanel/bottom/ticket_tips").GetComponent<TicketTips>();
|
||||
//UI_EventPartnerFishbowlPanel_27
|
||||
anim = transform.Find("root/matchingPanel").GetComponent<Animation>();
|
||||
btn_questionmark1 = transform.Find("root/MiddlePanel/top/title/btn_questionmark").GetComponent<Button>();
|
||||
//===================匹配界面
|
||||
MiddlePanel = transform.Find("root/MiddlePanel").gameObject;
|
||||
text_time = transform.Find("root/MiddlePanel/top/text_time").GetComponent<TMP_Text>();
|
||||
icon_rank = transform.Find("root/MiddlePanel/rank/icon_rank").GetComponent<Image>();
|
||||
text_rank = transform.Find("root/MiddlePanel/rank/text_rank").GetComponent<TMP_Text>();
|
||||
btn_questionmark = transform.Find("root/MiddlePanel/rank/text_rank/btn_questionmark").GetComponent<Button>();
|
||||
bg_bar = transform.Find("root/MiddlePanel/rank/bg_bar").gameObject;
|
||||
bar = transform.Find("root/MiddlePanel/rank/bg_bar/bar").GetComponent<Image>();
|
||||
text_progress = transform.Find("root/MiddlePanel/rank/bg_bar/text_progress").GetComponent<TMP_Text>();
|
||||
btn_reward = transform.Find("root/MiddlePanel/rank/btn_reward").GetComponent<Button>();
|
||||
rewardItemNews = new List<RewardItemNew>();
|
||||
Transform layout_reward = transform.Find("root/MiddlePanel/reward/layout_reward");
|
||||
int layout_reward_count = layout_reward.childCount;
|
||||
for (int i = 0; i < layout_reward_count; i++)
|
||||
{
|
||||
rewardItemNews.Add(layout_reward.GetChild(i).GetComponent<RewardItemNew>());
|
||||
}
|
||||
|
||||
icon_ticket = transform.Find("root/MiddlePanel/bottom/icon_ticket").GetComponent<Button>();
|
||||
|
||||
win_text_num = transform.Find("root/MiddlePanel/reward/info/win/text_num").GetComponent<TMP_Text>();
|
||||
loss_text_num = transform.Find("root/MiddlePanel/reward/info/loss/text_num").GetComponent<TMP_Text>();
|
||||
btn_confirm = transform.Find("root/MiddlePanel/bottom/btn_confirm/btn_green").GetComponent<Button>();
|
||||
loading = transform.Find("root/MiddlePanel/bottom/btn_confirm/btn_green/Ani_Container/loading").gameObject;
|
||||
p_text = transform.Find("root/MiddlePanel/bottom/btn_confirm/btn_green/Ani_Container/p_text").gameObject;
|
||||
middle_text_time = transform.Find("root/MiddlePanel/top/text_time").GetComponent<TMP_Text>();
|
||||
text_num = transform.Find("root/MiddlePanel/bottom/icon_ticket/text_num").GetComponent<TMP_Text>();
|
||||
//================================
|
||||
//=====================匹配中
|
||||
matchingPanel = transform.Find("root/matchingPanel").gameObject;
|
||||
myself = transform.Find("root/matchingPanel/myself").GetComponent<DuelMatchPlayer>();
|
||||
enemy = transform.Find("root/matchingPanel/enermy").GetComponent<DuelMatchPlayer>();
|
||||
match_text_time = transform.Find("root/matchingPanel/text_time").GetComponent<TMP_Text>();
|
||||
but_stop = transform.Find("root/matchingPanel/btn_stop/btn_green").GetComponent<Button>();
|
||||
fx_ui_matchingPanel_match_glow_01 = transform.Find("root/matchingPanel/enermy/fx_ui_matchingPanel_match_glow_01").gameObject;
|
||||
//=================================
|
||||
randomMapPanel = transform.Find("root/matchingPanel/RandomMap").GetComponent<DuelRandomMapPanel>();
|
||||
fishConfrimPanel = transform.Find("root/FishConfirmPanel").gameObject;
|
||||
fishConfrimPanel.SetActive(false);
|
||||
LoadAudio();
|
||||
GContext.container.Resolve<GuideDataCenter>().InspectTriggerGuide("EventFishingDuelPanel");
|
||||
CheckResource();
|
||||
MatchPlayerProb = FDM.eventSoloConfig.MatchPlayerProb;
|
||||
}
|
||||
ILoadResourceService loadResourceService;
|
||||
IDisposable loadResourceIdis;
|
||||
string resourceKey;
|
||||
public async void CheckResource()
|
||||
{
|
||||
PlayerFishData playerFishData = GContext.container.Resolve<PlayerFishData>();
|
||||
List<int> rodIDs = tables.TbRodData.DataList.FindAll(_ => !playerFishData.NotRod(_.ID)).Select(_ => _.ID).ToList();
|
||||
|
||||
List<string> resourceName = new List<string>();
|
||||
RodSkinData rodSkinData;
|
||||
foreach (var rodID in rodIDs)
|
||||
{
|
||||
int skindID = playerFishData.GetRodSkin(rodID);
|
||||
rodSkinData = tables.TbRodSkinData.GetOrDefault(skindID);
|
||||
resourceName.Add(rodSkinData.Fbx);
|
||||
}
|
||||
loadResourceService = GContext.container.Resolve<ILoadResourceService>();
|
||||
resourceKey = "RodList" + rodIDs.Count;
|
||||
SetLoading(true);
|
||||
bool isReady = await loadResourceService.CheckResourceLoadQueue(resourceKey, resourceName);
|
||||
if (!isReady)
|
||||
{
|
||||
loadResourceIdis = GContext.OnEvent<LoadEventResourceEvent>().Where(_ => _.key == resourceKey).Subscribe(LoadEventResourceEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadEventResource(true);
|
||||
}
|
||||
}
|
||||
void LoadEventResourceEvent(LoadEventResourceEvent loadEvent)
|
||||
{
|
||||
Debug.Log($"LoadEventResourceEvent {loadEvent.key} {loadEvent.isSucceeded} ");
|
||||
DisposeEvent();
|
||||
LoadEventResource(loadEvent.isSucceeded);
|
||||
}
|
||||
|
||||
void DisposeEvent()
|
||||
{
|
||||
loadResourceIdis?.Dispose();
|
||||
loadResourceIdis = null;
|
||||
}
|
||||
void LoadEventResource(bool succeeded)
|
||||
{
|
||||
if (this == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (succeeded)
|
||||
{
|
||||
try
|
||||
{
|
||||
SetLoading(false);
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError($"Load Event Resource Exception! == {gameObject.name} \n {e}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Load Event Resource Failed! == {gameObject.name}");
|
||||
}
|
||||
}
|
||||
async void LoadAudio()
|
||||
{
|
||||
audio_ui_fishingduel_matchingloop = await Addressables.LoadAssetAsync<AudioClip>("audio_ui_fishingduel_matchingloop").Task;
|
||||
}
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
GContext.Publish(new HideHomePanelEvent());
|
||||
//disposable = GContext.OnEvent<RewardPanelClose>().Subscribe(OnRewardPanelClose);
|
||||
ShowPanel(false);
|
||||
btn_close.onClick.AddListener(OnClickClose);
|
||||
|
||||
InitMiddlePanel();
|
||||
|
||||
but_stop.onClick.AddListener(OnClickStop);
|
||||
|
||||
IUserService userService = GContext.container.Resolve<IUserService>();
|
||||
var url = userService.AvatarUrl;
|
||||
|
||||
uiService.SetHeadImage(myself.icon_head, url);
|
||||
myself.text_name.text = userService.DisplayName;
|
||||
myself.icon_rank.sprite = icon_rank.sprite;
|
||||
myself.text_score.text = ConvertTools.GetNumberString2(FDM.duelData.progress);
|
||||
|
||||
btn_beilv.onClick.AddListener(OnclickMagnification);
|
||||
btn_beilv_max.onClick.AddListener(OnclickMagnification);
|
||||
|
||||
SetShowMagnification();
|
||||
btn_reward.onClick.AddListener(() => _ = UIManager.Instance.ShowUI(UITypes.EventFishingDuelRewardPopupPanel));
|
||||
btn_advert.onClick.AddListener(() => _ = UIManager.Instance.ShowUI(UITypes.EventFishingduelShopPopupPanel));
|
||||
Item itemdata = tables.TbItem.GetOrDefault(2051);
|
||||
string nameL = LocalizationMgr.GetText(itemdata.Name_l10n_key);
|
||||
ticket_tips.SetText(LocalizationMgr.GetFormatTextValue("UI_EventPartnerFishbowlPanel_27", nameL));
|
||||
}
|
||||
|
||||
void OnclickMagnification()
|
||||
{
|
||||
FDM.SetMagnification();
|
||||
SetShowMagnification();
|
||||
}
|
||||
void SetShowMagnification()
|
||||
{
|
||||
bool IsMax = FDM.IsMaxMagnification();
|
||||
btn_beilv.gameObject.SetActive(!IsMax);
|
||||
btn_beilv_max.gameObject.SetActive(IsMax);
|
||||
text_beilv.text = text_beilv_max.text = $"x{FDM.magnification}";
|
||||
SetReward();
|
||||
}
|
||||
//void OnRewardPanelClose(RewardPanelClose rewardPanelClose)
|
||||
//{
|
||||
// SetEnergy();
|
||||
//}
|
||||
void SetEnergy()
|
||||
{
|
||||
//获取配置表
|
||||
|
||||
int MatchCost = FDM.magnification;
|
||||
int Energy = FDM.duelData.tickets;
|
||||
if (MatchCost > Energy)
|
||||
{
|
||||
//ConvertTools.GetNumberString
|
||||
text_num.text = $"<color=red>{Energy}/{MatchCost}</color>";
|
||||
}
|
||||
else
|
||||
{
|
||||
text_num.text = $"{Energy}/{MatchCost}";
|
||||
}
|
||||
}
|
||||
void SetReward()
|
||||
{
|
||||
var rewardItemData = FDM.GetReward();
|
||||
if (rewardItemData != null)
|
||||
{
|
||||
int dataCount = rewardItemData.Count;
|
||||
int rewardCount = rewardItemNews.Count;
|
||||
for (int i = 0; i < rewardCount; i++)
|
||||
{
|
||||
if (i < dataCount)
|
||||
{
|
||||
rewardItemData[i].count *= FDM.magnification;
|
||||
rewardItemNews[i].SetData(rewardItemData[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
rewardItemNews[i].gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
win_text_num.text = $"+{FDM.eventSolomain.WinTrophy * FDM.magnification}";
|
||||
loss_text_num.text = $"-{FDM.eventSolomain.LoseTrophy * FDM.magnification}";
|
||||
SetEnergy();
|
||||
}
|
||||
void InitMiddlePanel()
|
||||
{
|
||||
DoConsumeBait();
|
||||
icon_ticket.onClick.AddListener(OnClickIcon);
|
||||
btn_confirm.onClick.AddListener(OnClickMaching);
|
||||
btn_questionmark.onClick.AddListener(() => _ = UIManager.Instance.ShowUI(UITypes.EventFishingDuelRankPopupPanel));
|
||||
btn_questionmark1.onClick.AddListener(() => _ = UIManager.Instance.ShowUI(UITypes.EventFishingDuelInfoPopupPanel));
|
||||
uiService.SetImageSprite(icon_rank, FDM.eventSolomain.RankIcon);
|
||||
text_rank.text = LocalizationMgr.GetText(FDM.eventSolomain.RankText_l10n_key);
|
||||
text_progress.text = $"{FDM.duelData.progress}";
|
||||
win_text_num.text = $"+{FDM.eventSolomain.WinTrophy * FDM.magnification}";
|
||||
loss_text_num.text = $"-{FDM.eventSolomain.LoseTrophy * FDM.magnification}";
|
||||
}
|
||||
private void DoConsumeBait()
|
||||
{
|
||||
Debug.Log("NetworkReachability");
|
||||
if (Application.internetReachability == NetworkReachability.NotReachable)
|
||||
{
|
||||
Debug.Log("NetworkReachability.NotReachable");
|
||||
NoInternet();
|
||||
}
|
||||
}
|
||||
|
||||
private async void NoInternet()
|
||||
{
|
||||
GameObject go = await UIManager.Instance.ShowUI(UITypes.NoticeConfirmPopupPanel);
|
||||
var NoticeConfirmPopupPanel = go.GetComponent<NoticeConfirmPopupPanel>();
|
||||
string info = LocalizationMgr.GetText("UI_FishingReconnectPanel_101003");
|
||||
string label = LocalizationMgr.GetText("UI_FishingRewardPanel_101022");
|
||||
string title = LocalizationMgr.GetText("UI_FishingReconnectPanel_101002");
|
||||
NoticeConfirmPopupPanel.Init(1, title, info,
|
||||
onClickLeft: DoConsumeBait,
|
||||
onClose: DoConsumeBait,
|
||||
btn_left_text: label);
|
||||
}
|
||||
void OnClickMaching()
|
||||
{
|
||||
if (FDM.duelData.tickets < FDM.magnification)
|
||||
{
|
||||
ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_86"));
|
||||
//var pos = btn_confirm.transform.position;
|
||||
//pos.y += 50;
|
||||
//ticket_tips.SetPos(pos);
|
||||
//ticket_tips.gameObject.SetActive(!ticket_tips.gameObject.activeSelf);
|
||||
//门票不足
|
||||
FDM.SetMaxMagnification();
|
||||
SetShowMagnification();
|
||||
return;
|
||||
}
|
||||
if (loading.activeSelf)
|
||||
{
|
||||
ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_114"));
|
||||
return;
|
||||
}
|
||||
disRequestSvrTime?.Dispose();
|
||||
disRequestSvrTime = GContext.OnEvent<RequestSvrTimeEvent>().Subscribe(RequestSvrTimeEvent);
|
||||
ShowPanel(true);
|
||||
matchIng = StartCoroutine(StartMaching());
|
||||
}
|
||||
void SetLoading(bool value)
|
||||
{
|
||||
//loading.SetActive(value);
|
||||
loading.SetActive(value);
|
||||
p_text.SetActive(!value);
|
||||
}
|
||||
void RequestSvrTimeEvent(RequestSvrTimeEvent requestSvrTimeEvent)
|
||||
{
|
||||
if (!requestSvrTimeEvent.Status)
|
||||
{
|
||||
OnClickStop();
|
||||
}
|
||||
}
|
||||
void OnClickIcon()
|
||||
{
|
||||
var pos = icon_ticket.transform.position;
|
||||
pos.y += 40;
|
||||
ticket_tips.SetPos(pos);
|
||||
ticket_tips.gameObject.SetActive(!ticket_tips.gameObject.activeSelf);
|
||||
|
||||
}
|
||||
void OnClickStop()
|
||||
{
|
||||
FDM.StopRT();
|
||||
CancelMatchmaking();
|
||||
disRequestSvrTime?.Dispose();
|
||||
disRequestSvrTime = null;
|
||||
ticketId = false;
|
||||
if (matchIng != null)
|
||||
{
|
||||
StopCoroutine(matchIng);
|
||||
matchIng = null;
|
||||
}
|
||||
randomMapPanel.Stop();
|
||||
ShowPanel(false);
|
||||
}
|
||||
void ShowPanel(bool isMatch)
|
||||
{
|
||||
if (isMatch)
|
||||
{
|
||||
StopTimer();
|
||||
}
|
||||
else
|
||||
{
|
||||
SetTimer();
|
||||
}
|
||||
btn_close.enabled = !isMatch;
|
||||
btn_close_image.SetActive(!isMatch);
|
||||
MiddlePanel.SetActive(!isMatch);
|
||||
matchingPanel.SetActive(isMatch);
|
||||
StopLoopAudio();
|
||||
}
|
||||
void PlayLoopAudio()
|
||||
{
|
||||
StopLoopAudio();
|
||||
audio_matchingloop = GContext.container.Resolve<ISoundService>().GetNewUISound(audio_ui_fishingduel_matchingloop);
|
||||
audio_matchingloop.audioSource.Play();
|
||||
audio_matchingloop.audioSource.loop = true;
|
||||
}
|
||||
void StopLoopAudio()
|
||||
{
|
||||
if (audio_matchingloop)
|
||||
{
|
||||
audio_matchingloop.audioSource.Stop();
|
||||
audio_matchingloop.ReturnPool();
|
||||
audio_matchingloop = null;
|
||||
}
|
||||
}
|
||||
IEnumerator StartMaching()
|
||||
{
|
||||
but_stop.gameObject.SetActive(false);
|
||||
//anim.Play("eventfishingduelpanel_maprandom");
|
||||
Dictionary<int, Robot> robots = tables.TbRobot.DataMap;
|
||||
List<int> RobotAccountList = FDM.eventSolomain.RobotAccountList;
|
||||
int robotId = RobotAccountList[UnityEngine.Random.Range(0, RobotAccountList.Count)];
|
||||
EventSoloRobot eventSoloRobot = tables.TbEventSoloRobot.GetOrDefault(robotId);
|
||||
List<int> robotList = eventSoloRobot.RobotList;
|
||||
robotId = robotList[UnityEngine.Random.Range(0, robotList.Count)];
|
||||
Robot robot = robots[robotId];
|
||||
SetRobot(robot, eventSoloRobot.Trophy);
|
||||
//等待匹配
|
||||
randomMapPanel.StartMap();
|
||||
PlayLoopAudio();
|
||||
yield return new WaitForSeconds(1);
|
||||
currentTicketId = null;
|
||||
ticketId = false;
|
||||
yield return FDM.StartMaching().AsIEnumerator();
|
||||
int MaxMatchTime = FDM.eventSoloConfig.MaxMatchTime;
|
||||
float matchPlayerProb = UnityEngine.Random.value;
|
||||
if (matchPlayerProb >= MatchPlayerProb || MaxMatchTime < 2 || FDM.rts == null)
|
||||
{
|
||||
but_stop.gameObject.SetActive(true);
|
||||
ticketId = true;
|
||||
yield return Awaiters.Seconds(UnityEngine.Random.Range(0, MaxMatchTime));
|
||||
if (ticketId)
|
||||
{
|
||||
FDM.MachingEnd(null);
|
||||
}
|
||||
ticketId = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateMatchmakingTicket();
|
||||
}
|
||||
//anim.Play("eventfishingduelpanel_loop");
|
||||
float time = 0.1f;
|
||||
while (FDM.MachingState < 2)
|
||||
{
|
||||
robotId = RobotAccountList[UnityEngine.Random.Range(0, RobotAccountList.Count)];
|
||||
eventSoloRobot = tables.TbEventSoloRobot.GetOrDefault(robotId);
|
||||
|
||||
robotList = eventSoloRobot.RobotList;
|
||||
robotId = robotList[UnityEngine.Random.Range(0, robotList.Count)];
|
||||
|
||||
robot = robots[robotId];
|
||||
SetRobot(robot, eventSoloRobot.Trophy);
|
||||
yield return new WaitForSeconds(time);
|
||||
}
|
||||
|
||||
but_stop.gameObject.SetActive(false);
|
||||
disRequestSvrTime?.Dispose();
|
||||
disRequestSvrTime = null;
|
||||
FDM.OnJoin();
|
||||
var matchDelayTime = FDM.eventSoloConfig.MatchDelayTime;
|
||||
float machTimeM = UnityEngine.Random.Range(matchDelayTime[0], matchDelayTime[1]);
|
||||
//最后两秒等的越来越长
|
||||
while (machTimeM > 0f)
|
||||
{
|
||||
robotId = RobotAccountList[UnityEngine.Random.Range(0, RobotAccountList.Count)];
|
||||
eventSoloRobot = tables.TbEventSoloRobot.GetOrDefault(robotId);
|
||||
|
||||
robotList = eventSoloRobot.RobotList;
|
||||
robotId = robotList[UnityEngine.Random.Range(0, robotList.Count)];
|
||||
|
||||
robot = robots[robotId];
|
||||
SetRobot(robot, eventSoloRobot.Trophy);
|
||||
machTimeM -= time;
|
||||
yield return new WaitForSeconds(time);
|
||||
}
|
||||
randomMapPanel.SetClose();
|
||||
while (!randomMapPanel.IsStop)
|
||||
{
|
||||
robotId = RobotAccountList[UnityEngine.Random.Range(0, RobotAccountList.Count)];
|
||||
eventSoloRobot = tables.TbEventSoloRobot.GetOrDefault(robotId);
|
||||
|
||||
robotList = eventSoloRobot.RobotList;
|
||||
robotId = robotList[UnityEngine.Random.Range(0, robotList.Count)];
|
||||
|
||||
robot = robots[robotId];
|
||||
SetRobot(robot, eventSoloRobot.Trophy);
|
||||
yield return new WaitForSeconds(time);
|
||||
}
|
||||
fx_ui_matchingPanel_match_glow_01.SetActive(true);
|
||||
|
||||
uiService.SetHeadImage(enemy.icon_head, FDM.EnemyModel.PlayerAvatar);
|
||||
enemy.text_name.text = FDM.EnemyModel.PlayerName;
|
||||
var eventSolomain = FDM.GetEventSolomain(FDM.EnemyModel.RankScore);
|
||||
uiService.SetImageSprite(enemy.icon_rank, eventSolomain.RankIcon);
|
||||
enemy.text_score.text = ConvertTools.GetNumberString2(FDM.EnemyModel.RankScore);
|
||||
StopLoopAudio();
|
||||
yield return new WaitForSeconds(2);
|
||||
anim.Play("eventfishingduelpanel_matchingpanel_end");
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
//PlayLoopAudio();
|
||||
//yield return new WaitForSeconds(2);
|
||||
//StopLoopAudio();
|
||||
ShowFishConfrimPanel();
|
||||
}
|
||||
void SetRobot(Robot robot, int trophy)
|
||||
{
|
||||
int robotTier = FDM.eventSolomain.MatchTierList[UnityEngine.Random.Range(0, FDM.eventSolomain.MatchTierList.Count)];
|
||||
var eventSolomain = tables.TbEventSolomain.DataMap[robotTier];
|
||||
uiService.SetHeadImage(enemy.icon_head, robot.Avatar);
|
||||
enemy.text_name.text = LocalizationMgr.GetText(robot.Name_l10n_key);
|
||||
uiService.SetImageSprite(enemy.icon_rank, eventSolomain.RankIcon);
|
||||
enemy.text_score.text = ConvertTools.GetNumberString2(trophy);
|
||||
}
|
||||
public void OnClickClose()
|
||||
{
|
||||
GContext.container.Unregister<FishingDuelManager>();
|
||||
GContext.Publish(new RestartShowHomeUIEvent());
|
||||
UIManager.Instance.DestroyUI(UITypes.EventFishingDuelPanel);
|
||||
disRequestSvrTime?.Dispose();
|
||||
disRequestSvrTime = null;
|
||||
}
|
||||
|
||||
void SetTimer()
|
||||
{
|
||||
StopTimer();
|
||||
DateTime endDateTime = fishingEventData.GetEventEndTime(fishingEventData.duelData.eventID)
|
||||
.AddSeconds(-tables.TbEventSoloConfig.SettlementTime);
|
||||
TimeSpan now = endDateTime - ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
text_time.text = LocalizationMgr.GetFormatTextValue("UI_COMMON_reset",
|
||||
ConvertTools.ConvertTime2(now.Days, now.Hours, now.Minutes, now.Seconds));
|
||||
double seconds = now.TotalSeconds;
|
||||
if (seconds < 1)
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
else
|
||||
{
|
||||
timer = this.AttachTimer((float)seconds, Refresh,
|
||||
(elapsed) =>
|
||||
{
|
||||
now = endDateTime - ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
text_time.text = LocalizationMgr.GetFormatTextValue("UI_COMMON_reset",
|
||||
ConvertTools.ConvertTime2(now.Days, now.Hours, now.Minutes, now.Seconds));
|
||||
}, useRealTime: true);
|
||||
}
|
||||
}
|
||||
void StopTimer()
|
||||
{
|
||||
timer?.Cancel();
|
||||
timer = null;
|
||||
}
|
||||
void Refresh()
|
||||
{
|
||||
timer?.Cancel();
|
||||
timer = null;
|
||||
OnClickClose();
|
||||
UIManager.Instance.DestroyUI(UITypes.EventFishingDuelRankPopupPanel);
|
||||
UIManager.Instance.DestroyUI(UITypes.EventFishingDuelRewardPopupPanel);
|
||||
UIManager.Instance.DestroyUI(UITypes.EventFishingduelShopPopupPanel);
|
||||
UIManager.Instance.DestroyUI(UITypes.EventFishingDuelInfoPopupPanel);
|
||||
}
|
||||
|
||||
public void ShowFishConfrimPanel()
|
||||
{
|
||||
matchingPanel.gameObject.SetActive(false);
|
||||
fishConfrimPanel.SetActive(true);
|
||||
}
|
||||
|
||||
#region Matchmaking
|
||||
public string currentTicketId;
|
||||
public bool ticketId;
|
||||
string GetDetails()
|
||||
{
|
||||
IUserService userService = GContext.container.Resolve<IUserService>();
|
||||
MatchDetails matchDetails = new MatchDetails();
|
||||
matchDetails.ID = userService.UserId;
|
||||
matchDetails.Name = userService.DisplayName;
|
||||
matchDetails.Avatar = userService.AvatarUrl;
|
||||
matchDetails.RandScore = FDM.duelData.progress;
|
||||
matchDetails.Mag = FDM.magnification;
|
||||
return Newtonsoft.Json.JsonConvert.SerializeObject(matchDetails);
|
||||
}
|
||||
void CreateMatchmakingTicket()
|
||||
{
|
||||
if (GContext.container.Resolve<IUserService>() is not PlayFabUserService)
|
||||
{
|
||||
Debug.LogWarning("KV matchmaking service is not connected.");
|
||||
FDM.MachingEnd(null);
|
||||
return;
|
||||
}
|
||||
int MaxMatchTime = FDM.eventSoloConfig.MaxMatchTime;
|
||||
//可匹配段位列表,杯数,选择倍率,地图列表,随机种子seed,鱼卡等级
|
||||
List<int> matchTierList = FDM.eventSolomain.MatchTierList;
|
||||
List<int> mapIDs = FDM.mapIDs;
|
||||
|
||||
List<string> matchTierListStr = new List<string>();
|
||||
for (int i = 0; i < matchTierList.Count; i++)
|
||||
{
|
||||
matchTierListStr.Add(matchTierList[i].ToString());
|
||||
}
|
||||
List<string> mapIDsStr = new List<string>();
|
||||
for (int i = 0; i < mapIDs.Count; i++)
|
||||
{
|
||||
mapIDsStr.Add(mapIDs[i].ToString());
|
||||
}
|
||||
IUserService userService = GContext.container.Resolve<IUserService>();
|
||||
List<string> Group = FDM.GetGroups();
|
||||
string details = GetDetails();
|
||||
var request = new CreateMatchmakingTicketRequest
|
||||
{
|
||||
Creator = new MatchmakingPlayer
|
||||
{
|
||||
Entity = new EntityKey
|
||||
{
|
||||
Id = ((PlayFabUserService)userService).entityKey.Id,
|
||||
Type = ((PlayFabUserService)userService).entityKey.Type
|
||||
},
|
||||
Attributes = new MatchmakingPlayerAttributes
|
||||
{
|
||||
DataObject = new
|
||||
{
|
||||
Tier = matchTierListStr.ToArray(),
|
||||
Map = mapIDsStr.ToArray(),
|
||||
Group = Group.ToArray(),
|
||||
Details = details,
|
||||
}
|
||||
}
|
||||
},
|
||||
GiveUpAfterSeconds = MaxMatchTime,
|
||||
QueueName = FishingDuelManager.QueueName // 需要根据实际的匹配队列名称修改
|
||||
};
|
||||
|
||||
PlayFabMultiplayerAPI.CreateMatchmakingTicket(request,
|
||||
(result) =>
|
||||
{
|
||||
currentTicketId = result.TicketId;
|
||||
Debug.Log("匹配请求已创建,等待匹配...");
|
||||
Debug.Log("匹配Ticket ID: " + result.TicketId);
|
||||
//// 开始轮询匹配状态
|
||||
StartCoroutine(CheckMatchmakingStatus());
|
||||
//显示取消按钮
|
||||
but_stop.gameObject.SetActive(true);
|
||||
},
|
||||
(error) =>
|
||||
{
|
||||
Debug.LogError("创建匹配请求失败: " + error.GenerateErrorReport());
|
||||
FDM.MachingEnd(null);
|
||||
});
|
||||
}
|
||||
|
||||
IEnumerator CheckMatchmakingStatus()
|
||||
{
|
||||
while (!string.IsNullOrEmpty(currentTicketId))
|
||||
{
|
||||
yield return new WaitForSeconds(3f);
|
||||
|
||||
if (string.IsNullOrEmpty(currentTicketId))
|
||||
yield break;
|
||||
|
||||
var request = new GetMatchmakingTicketRequest
|
||||
{
|
||||
TicketId = currentTicketId,
|
||||
QueueName = FishingDuelManager.QueueName, // 需要根据实际的匹配队列名称修改
|
||||
EscapeObject = false
|
||||
};
|
||||
|
||||
PlayFabMultiplayerAPI.GetMatchmakingTicket(request,
|
||||
(result) =>
|
||||
{
|
||||
string statusInfo = "匹配状态: " + result.Status;
|
||||
if (result.MatchId != null)
|
||||
{
|
||||
statusInfo += ", Match ID: " + result.MatchId;
|
||||
}
|
||||
Debug.Log(statusInfo.ToString());
|
||||
|
||||
if (result.Status == "Matched")
|
||||
{
|
||||
but_stop.gameObject.SetActive(false);
|
||||
Debug.Log("匹配成功,获取服务器信息: ");
|
||||
GetMatchDetails(result.MatchId);
|
||||
}
|
||||
else if (result.Status == "Canceled")
|
||||
{
|
||||
Debug.Log("匹配已取消: Canceled " + result.CancellationReasonString);
|
||||
//if (result.CancellationReasonString != "Requested")
|
||||
//{
|
||||
MachingEnd();
|
||||
//}
|
||||
}
|
||||
},
|
||||
(error) =>
|
||||
{
|
||||
Debug.LogError("检查匹配状态失败: " + error.GenerateErrorReport());
|
||||
MachingEnd();
|
||||
});
|
||||
}
|
||||
}
|
||||
void GetMatchDetails(string matchId)
|
||||
{
|
||||
if (GContext.container.Resolve<IUserService>() is not PlayFabUserService)
|
||||
{
|
||||
Debug.LogWarning("KV matchmaking service is not connected.");
|
||||
FDM.MachingEnd(null);
|
||||
return;
|
||||
}
|
||||
var request = new PlayFab.MultiplayerModels.GetMatchRequest
|
||||
{
|
||||
MatchId = matchId,
|
||||
QueueName = FishingDuelManager.QueueName, // 需要根据实际的匹配队列名称修改
|
||||
EscapeObject = false,
|
||||
ReturnMemberAttributes = true
|
||||
};
|
||||
|
||||
PlayFabMultiplayerAPI.GetMatch(request,
|
||||
(result) =>
|
||||
{
|
||||
Debug.Log("匹配成功: " + result.MatchId);
|
||||
if (string.IsNullOrEmpty(currentTicketId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// 添加玩家信息
|
||||
if (result.Members != null && result.Members.Count > 0)
|
||||
{
|
||||
//AddMatchmakingLog("玩家数量: " + result.Members.Count);
|
||||
IUserService userService = GContext.container.Resolve<IUserService>();
|
||||
MatchDetails matchDetails = null;
|
||||
int[] mapArray = null;
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < result.Members.Count; i++)
|
||||
{
|
||||
MatchmakingPlayerWithTeamAssignment member = result.Members[i];
|
||||
if (member.Entity.Id != ((PlayFabUserService)userService).entityKey.Id)
|
||||
{
|
||||
JObject dataObject = JObject.FromObject(result.Members[i].Attributes.DataObject);
|
||||
string[] tierArray = dataObject["Tier"].ToObject<string[]>();
|
||||
mapArray = dataObject["Map"].ToObject<int[]>();
|
||||
string matchDetailsStr = dataObject["Details"].ToString();
|
||||
matchDetails = Newtonsoft.Json.JsonConvert.DeserializeObject<MatchDetails>(matchDetailsStr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
matchDetails = null;
|
||||
mapArray = null;
|
||||
}
|
||||
currentTicketId = null;
|
||||
FDM.MachingEnd(result.MatchId, matchDetails, mapArray);
|
||||
StartCoroutine(CheckReady());
|
||||
}
|
||||
},
|
||||
(error) =>
|
||||
{
|
||||
Debug.LogError("获取匹配详情失败: " + error.GenerateErrorReport());
|
||||
MachingEnd();
|
||||
});
|
||||
}
|
||||
IEnumerator CheckReady()
|
||||
{
|
||||
yield return new WaitForSeconds(5f);
|
||||
if (FDM.MachingState < 2)
|
||||
{
|
||||
Debug.LogError("匹配Ready超时 ");
|
||||
//返回 还是给个机器人? OnClickStop();
|
||||
FDM.MachingState = 0;
|
||||
FDM.MachingEnd(null);
|
||||
}
|
||||
}
|
||||
void MachingEnd()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(currentTicketId))
|
||||
{
|
||||
currentTicketId = null;
|
||||
FDM.MachingEnd(null);
|
||||
}
|
||||
}
|
||||
void CancelMatchmaking()
|
||||
{
|
||||
if (string.IsNullOrEmpty(currentTicketId))
|
||||
return;
|
||||
but_stop.gameObject.SetActive(false);
|
||||
var request = new CancelMatchmakingTicketRequest
|
||||
{
|
||||
TicketId = currentTicketId,
|
||||
QueueName = FishingDuelManager.QueueName // 需要根据实际的匹配队列名称修改
|
||||
};
|
||||
currentTicketId = null;
|
||||
PlayFabMultiplayerAPI.CancelMatchmakingTicket(request,
|
||||
(result) =>
|
||||
{
|
||||
Debug.Log("匹配已取消: ");
|
||||
},
|
||||
(error) =>
|
||||
{
|
||||
Debug.LogError("取消匹配失败: " + error.GenerateErrorReport());
|
||||
});
|
||||
}
|
||||
#endregion Matchmaking
|
||||
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
using System;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public abstract class AEventDataManager<T>:IDisposable where T : class, IEventData, new()
|
||||
{
|
||||
protected AEventDataManager()
|
||||
{
|
||||
Disposables=new CompositeDisposable();
|
||||
GContext.OnEvent<TargetEvent>().Subscribe(OnEvent_TargetEvent).AddTo(Disposables);
|
||||
}
|
||||
private Tables _tables;
|
||||
[Inject] public PlayerDataStorage.IPlayerSaveStore SaveStore { get; set; }
|
||||
bool disposed;
|
||||
|
||||
[Inject]
|
||||
public Tables Tables
|
||||
{
|
||||
get=>_tables??=GContext.container.Resolve<Tables>();
|
||||
set=>_tables=value;
|
||||
}
|
||||
|
||||
protected CompositeDisposable Disposables { get; }
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
disposed = true;
|
||||
Disposables?.Dispose();
|
||||
}
|
||||
|
||||
#region event
|
||||
|
||||
protected abstract int EventType { get; }
|
||||
protected abstract int EventSubType { get; }
|
||||
|
||||
protected virtual void OnEvent_TargetEvent(TargetEvent targetEvent)
|
||||
{
|
||||
if (disposed || SaveStore == null || !SaveStore.IsCurrent) return;
|
||||
if (targetEvent.type == EventType && targetEvent.subType == EventSubType) RefreshData(targetEvent.id);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region data
|
||||
|
||||
protected T _data;
|
||||
|
||||
protected internal T Data => _data ??= new T();
|
||||
|
||||
protected virtual void SaveData()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd {typeof(T).Name} SaveData {JsonConvert.SerializeObject(Data)}");
|
||||
#endif
|
||||
if (disposed) throw new ObjectDisposedException(GetType().Name);
|
||||
SaveStore.Write(typeof(T).Name, JsonConvert.SerializeObject(Data)).RequireAccepted();
|
||||
}
|
||||
|
||||
protected virtual void SaveData(T data)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd {typeof(T).Name} SaveData {JsonConvert.SerializeObject(data)}");
|
||||
#endif
|
||||
if (disposed) throw new ObjectDisposedException(GetType().Name);
|
||||
SaveStore.Write(typeof(T).Name, JsonConvert.SerializeObject(data)).RequireAccepted();
|
||||
}
|
||||
|
||||
protected virtual void RefreshData(int eventId)
|
||||
{
|
||||
//判断周开启时间
|
||||
var fishingEvent = Tables.TbFishingEvent.GetOrDefault(eventId);
|
||||
if (fishingEvent == null) return;
|
||||
var cacheData = GetCacheData();
|
||||
if (cacheData != null && cacheData.EventID == eventId)
|
||||
{
|
||||
Data.SetData(cacheData);
|
||||
return;
|
||||
}
|
||||
|
||||
InitData(eventId);
|
||||
}
|
||||
|
||||
protected T GetCacheData()
|
||||
{
|
||||
if (disposed) throw new ObjectDisposedException(GetType().Name);
|
||||
var read = SaveStore.Read(typeof(T).Name);
|
||||
var dataStr = read.ValueOrThrow();
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd {typeof(T).Name} LoadData sData {dataStr}");
|
||||
#endif
|
||||
if (read.Found == false) return new T();
|
||||
if (string.IsNullOrWhiteSpace(dataStr)) throw new JsonSerializationException("Stored event data cannot be empty.");
|
||||
return JsonConvert.DeserializeObject<T>(dataStr) ?? throw new JsonSerializationException("Stored event data cannot be null.");
|
||||
}
|
||||
|
||||
protected virtual void InitData(int eventId)
|
||||
{
|
||||
_data = new T();
|
||||
Data.EventID = eventId;
|
||||
Data.InflationRate=GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static void Register<M>()where M:AEventDataManager<T>
|
||||
{
|
||||
var instance=GContext.container.Resolve<M>();
|
||||
if (instance != null) return;
|
||||
GContext.container.Register<M>().AsSingleton();
|
||||
GContext.container.Resolve<M>();
|
||||
}
|
||||
#region Token
|
||||
|
||||
public virtual void AddToken(int count)
|
||||
{
|
||||
var currentCount = GetTokenCount();
|
||||
if (Data == null) return;
|
||||
var totalCount = currentCount + count;
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(Data.EventID, totalCount);
|
||||
}
|
||||
|
||||
protected virtual void RemoveToken(int count)
|
||||
{
|
||||
if (Data == null) return;
|
||||
var eventData = GContext.container.Resolve<FishingEventData>();
|
||||
if (eventData == null) return;
|
||||
var current = GetTokenCount();
|
||||
var resCount = Mathf.Max(0, current - count);
|
||||
eventData.SaveTransitionData(Data.EventID, resCount);
|
||||
}
|
||||
|
||||
public virtual int GetTokenCount()
|
||||
{
|
||||
if (Data == null) return 0;
|
||||
var eventData = GContext.container.Resolve<FishingEventData>();
|
||||
return eventData?.GetTransitionDataCount(Data.EventID) ?? 0;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,567 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GameCore;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using asap.core;
|
||||
using game;
|
||||
using cfg;
|
||||
|
||||
public class EventBingoModel
|
||||
{
|
||||
public readonly static int BoardSize = 5;
|
||||
public readonly static int NumberCount = BoardSize * BoardSize;
|
||||
public readonly static string SfxItemIn = "audio_ui_eventbingobaseball_in", SfxItemOut = "audio_ui_eventbingobaseball_out";
|
||||
public const int RecordLength = 4;
|
||||
public const char Splitter = '|', Comma = ',';
|
||||
private int _stepInRound, _ticketCount, _roundCount, _eventId, _chainProgress, _chainTaskTokenProgress;
|
||||
float _inflationRate;
|
||||
private EventBingoRoundData _roundData;
|
||||
private EventBingoTableContext _ctx;
|
||||
private bool _isFirstTime;
|
||||
// This is actually used as a dequeue.
|
||||
public LinkedList<EventBingoNumberRollerItemData> NumberRollerDataList;
|
||||
public int[] BoardNumbers => _roundData.BoardNumbers;
|
||||
public EventBingoStepState StepState => _roundData[_stepInRound];
|
||||
public int BoardState => StepState.BoardState;
|
||||
public int TicketCount => _ticketCount;
|
||||
public int RoundCount => _roundCount;
|
||||
public int EventId => _eventId;
|
||||
public float InflationRate => _inflationRate;
|
||||
public int StepInRound => _stepInRound;
|
||||
public bool IsActivated
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ctx == null)
|
||||
return false;
|
||||
var timeLimit = _ctx.GetTimeLimit();
|
||||
return timeLimit.Item1 <= ZZTimeHelper.UtcNow() && ZZTimeHelper.UtcNow() < timeLimit.Item2;
|
||||
}
|
||||
}
|
||||
public bool IsFirstTime => _isFirstTime;
|
||||
public EventBingoTableContext Ctx => _ctx;
|
||||
public int PackId => _ctx.GetPackId();
|
||||
private const string BingoBattlePassKey = "MiniBPBingo";
|
||||
|
||||
public void Init(FishingEvent e)
|
||||
{
|
||||
_eventId = e.ID;
|
||||
_inflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
RefreshTableContext();
|
||||
_roundCount = 0;
|
||||
var seed = _ctx.PickSeed(_roundCount);
|
||||
_roundData = new EventBingoRoundData(seed);
|
||||
_stepInRound = 0;
|
||||
NumberRollerDataList = new LinkedList<EventBingoNumberRollerItemData>();
|
||||
_ticketCount = 0;
|
||||
AddTicket(_ctx.GetWelcomeGift());
|
||||
SetUiPanel(_ctx.GetUiData());
|
||||
_isFirstTime = true;
|
||||
ToPlayfabData().Save();
|
||||
}
|
||||
|
||||
public void Load(EventBingoPlayfabData pfData, EventBingoPlayerPreferenceData ppData, FishingEvent e)
|
||||
{
|
||||
_eventId = pfData.EventId;
|
||||
_inflationRate = pfData.InflationRate;
|
||||
RefreshTableContext();
|
||||
_ticketCount = pfData.TicketCount;
|
||||
_roundCount = pfData.RoundCount;
|
||||
_stepInRound = pfData.StepInRound;
|
||||
_roundData = new EventBingoRoundData(pfData.Seed);
|
||||
if (!_roundData.ValidateStepIndex(_stepInRound))
|
||||
{
|
||||
Debug.LogWarning($"[EventBingo] Wrong data detected. Step count out of range. Reset round data.");
|
||||
_stepInRound = 0;
|
||||
var seed = _ctx.PickSeed(_roundCount);
|
||||
_roundData = new EventBingoRoundData(seed);
|
||||
}
|
||||
if (ppData == null || ppData.RollerDataList == null)
|
||||
NumberRollerDataList = new LinkedList<EventBingoNumberRollerItemData>();
|
||||
else
|
||||
NumberRollerDataList = new LinkedList<EventBingoNumberRollerItemData>(ppData.RollerDataList);
|
||||
SetUiPanel(_ctx.GetUiData());
|
||||
_isFirstTime = ppData == null ? true : ppData.IsFirstTime;
|
||||
}
|
||||
|
||||
public void InitTest()
|
||||
{
|
||||
Debug.Log("[EventBingo] Abandon.");
|
||||
}
|
||||
|
||||
public void NextStep()
|
||||
{
|
||||
_stepInRound++;
|
||||
ToPlayfabData().Save();
|
||||
// Debug.Log($"[EventBingo] Step{_stepInRound}: {StepState.Number}.");
|
||||
}
|
||||
|
||||
public void NextRound()
|
||||
{
|
||||
_roundCount++;
|
||||
var seed = _ctx.PickSeed(_roundCount);
|
||||
_roundData = new EventBingoRoundData(seed);
|
||||
_stepInRound = 0;
|
||||
NumberRollerDataList.Clear();
|
||||
ToPlayfabData().Save();
|
||||
ToPlayerPreferenceData().Save();
|
||||
}
|
||||
|
||||
public bool CheckIfTaken(int index)
|
||||
{
|
||||
var flag = 1 << index;
|
||||
return (BoardState & flag) != 0;
|
||||
}
|
||||
|
||||
public List<EventBingoBlockViewData> GetBoardInitiationData()
|
||||
{
|
||||
var res = new List<EventBingoBlockViewData>();
|
||||
for (int i = 0; i < NumberCount; i++)
|
||||
{
|
||||
_roundData.MinorRewards.TryGetValue(i, out var minorReward);
|
||||
res.Add(new EventBingoBlockViewData
|
||||
{
|
||||
number = _roundData.BoardNumbers[i],
|
||||
reward = minorReward,
|
||||
haveItem = CheckIfTaken(i)
|
||||
});
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public void AddNumberRecord(int number)
|
||||
{
|
||||
_ctx.GetRandomItemIcon(out int iconIdx, out string iconUrl);
|
||||
NumberRollerDataList.AddLast(new EventBingoNumberRollerItemData
|
||||
{
|
||||
number = number,
|
||||
iconIdx = iconIdx,
|
||||
});
|
||||
while (NumberRollerDataList.Count > RecordLength)
|
||||
NumberRollerDataList.RemoveFirst();
|
||||
ToPlayerPreferenceData().Save();
|
||||
}
|
||||
|
||||
public bool AddTicket(int count)
|
||||
{
|
||||
_ticketCount += count;
|
||||
if (_ticketCount < 0)
|
||||
{
|
||||
_ticketCount -= count;
|
||||
return false;
|
||||
}
|
||||
EventBingoAct.EventAggregator.Publish(new EventBingoTicketUpdateEvent());
|
||||
ToPlayfabData().Save();
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(_eventId, _ticketCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool DeliverMinorReward(int index, out ItemData reward)
|
||||
{
|
||||
bool haveReward = _roundData.MinorRewards.TryGetValue(index, out reward);
|
||||
if (haveReward)
|
||||
{
|
||||
reward.changeSource = new ChangeSource(EventId, "Event_Minigame1", "Event_Minigame1_Bingo", "DirectReward");
|
||||
EventBingoSystem.GrantReward(reward);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Debug.Log($"[EventBingo] No reward for idx {index}");
|
||||
}
|
||||
return haveReward;
|
||||
}
|
||||
|
||||
public void RefreshTableContext()
|
||||
{
|
||||
_ctx = new EventBingoTableContext(_eventId);
|
||||
}
|
||||
|
||||
public EventBingoPlayfabData ToPlayfabData()
|
||||
{
|
||||
return new EventBingoPlayfabData(
|
||||
_eventId, _ticketCount, _roundCount, _stepInRound, _roundData.Seed, _chainProgress,
|
||||
_chainTaskTokenProgress, _inflationRate);
|
||||
}
|
||||
|
||||
public EventBingoEntranceData ToEntranceData()
|
||||
{
|
||||
(var startTime, var expiryTime) = _ctx.GetTimeLimit();
|
||||
var miniBattlePassRedPointStatus = RedPointManager.Instance.GetRedPointState(BingoBattlePassKey);
|
||||
var miniBattlePassRedPointIndex = miniBattlePassRedPointStatus ? RedPointManager.Instance.GetRedPointCount(BingoBattlePassKey) : 0;
|
||||
return new EventBingoEntranceData
|
||||
{
|
||||
NeedRedPoint = _ticketCount > 0 || miniBattlePassRedPointStatus,
|
||||
RedPointIndex = _ticketCount + miniBattlePassRedPointIndex,
|
||||
ExpiryTime = expiryTime,
|
||||
StartTime = startTime,
|
||||
Icon = _ctx.GetEntranceIcon(),
|
||||
};
|
||||
}
|
||||
|
||||
public EventBingoPlayerPreferenceData ToPlayerPreferenceData()
|
||||
{
|
||||
return new EventBingoPlayerPreferenceData
|
||||
{
|
||||
RollerDataList = NumberRollerDataList.ToList(),
|
||||
IsFirstTime = _isFirstTime
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#region ChainPack
|
||||
public int GetChainProgress()
|
||||
{
|
||||
return _chainProgress;
|
||||
}
|
||||
|
||||
public void SetChainProgress(int p)
|
||||
{
|
||||
_chainProgress = p;
|
||||
}
|
||||
|
||||
public int GetTaskProgress()
|
||||
{
|
||||
return _chainTaskTokenProgress;
|
||||
}
|
||||
|
||||
public void SetTaskProgress(int p)
|
||||
{
|
||||
_chainTaskTokenProgress = p;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
ToPlayfabData().Save();
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void SetUiPanel(EventBingoUiData panelData)
|
||||
{
|
||||
UITypes.EventBingoInfoPanel.SetType(panelData.InfoPanel);
|
||||
UITypes.EventBingoNeoNormalPackPanel.SetType(panelData.NeoNormalPackPanel);
|
||||
}
|
||||
|
||||
public bool IsHit(int number)
|
||||
{
|
||||
foreach (var n in BoardNumbers)
|
||||
{
|
||||
if (n == number)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetFirstTimeFlag()
|
||||
{
|
||||
_isFirstTime = false;
|
||||
ToPlayerPreferenceData().Save();
|
||||
}
|
||||
|
||||
public void ActivateInstantBingo()
|
||||
{
|
||||
_stepInRound = _roundData.StepCount - 2;
|
||||
}
|
||||
|
||||
public EventNeoNormalPackData ToNeoNormalPackData()
|
||||
{
|
||||
return new EventNeoNormalPackData
|
||||
{
|
||||
PackId = PackId,
|
||||
EventId = EventId,
|
||||
Panel = _ctx.GetPackPanel()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class EventBingoRoundData
|
||||
{
|
||||
private int _seed;
|
||||
public int[] BoardNumbers;
|
||||
private List<int> _playerNumberSequence;
|
||||
private List<int> _stateSequence;
|
||||
private List<int> _playerIdxSequence;
|
||||
public Dictionary<int, ItemData> MinorRewards;
|
||||
public int Seed => _seed;
|
||||
public int StepCount => _stateSequence.Count;
|
||||
|
||||
public EventBingoStepState this[int idx]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (idx >= _stateSequence.Count || idx >= _playerNumberSequence.Count)
|
||||
{
|
||||
var actualIdx = Mathf.Min(_stateSequence.Count - 2, _playerNumberSequence.Count - 2);
|
||||
return new EventBingoStepState
|
||||
{
|
||||
Number = _playerNumberSequence[actualIdx],
|
||||
BoardState = _stateSequence[actualIdx],
|
||||
Index = _playerIdxSequence[actualIdx],
|
||||
};
|
||||
throw new IndexOutOfRangeException(
|
||||
$"[EventBingo] Index {idx} out of range. Expected range: [0, {Math.Min(_stateSequence.Count, _playerNumberSequence.Count)}]");
|
||||
}
|
||||
return new EventBingoStepState
|
||||
{
|
||||
Number = _playerNumberSequence[idx],
|
||||
BoardState = _stateSequence[idx],
|
||||
Index = _playerIdxSequence[idx]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public EventBingoRoundData(int seed)
|
||||
{
|
||||
_playerNumberSequence = new List<int>();
|
||||
_playerIdxSequence = new List<int>();
|
||||
_stateSequence = new List<int>();
|
||||
UpdateWithSeed(seed);
|
||||
}
|
||||
|
||||
public void UpdateWithSeed(int seed)
|
||||
{
|
||||
var model = GContext.container.Resolve<EventBingoModel>();
|
||||
_seed = seed;
|
||||
var rng = new System.Random(_seed);
|
||||
BoardNumbers = EventBingoSystem.GenerateBoardNumbers(rng).ToArray();
|
||||
_playerNumberSequence.Clear();
|
||||
_playerIdxSequence.Clear();
|
||||
_stateSequence.Clear();
|
||||
_playerNumberSequence.Add(-1);
|
||||
_playerIdxSequence.Add(-1);
|
||||
_stateSequence.Add(0b0);
|
||||
var numberPool = Enumerable.Range(1, 99).ToList();
|
||||
int state = 0b0;
|
||||
var pickedIndexSet = new HashSet<int>();
|
||||
while (numberPool.Count > 0 && EventBingoSystem.CheckBingo(state, out _) <= 0)
|
||||
{
|
||||
var pickedNumber = EventBingoSystem.PickRandomNumberFromList(numberPool, rng);
|
||||
var pickedIdx = Array.IndexOf(BoardNumbers, pickedNumber);
|
||||
if (pickedIdx > -1)
|
||||
{
|
||||
pickedIndexSet.Add(pickedIdx);
|
||||
state |= 1 << pickedIdx;
|
||||
}
|
||||
_playerIdxSequence.Add(pickedIdx);
|
||||
_playerNumberSequence.Add(pickedNumber);
|
||||
_stateSequence.Add(state);
|
||||
}
|
||||
MinorRewards = new Dictionary<int, ItemData>();
|
||||
model.Ctx.GetMinorDropData(rng, out var availableRewards, out var displayRewards);
|
||||
|
||||
var allIndices = Enumerable.Range(0, EventBingoModel.NumberCount).ToList();
|
||||
FtUtils.ShuffleList(allIndices, rng);
|
||||
var pickedIndices = pickedIndexSet.ToList();
|
||||
FtUtils.ShuffleList(pickedIndices, rng);
|
||||
var unpickedIndices = allIndices.Except(pickedIndexSet).ToList();
|
||||
FtUtils.ShuffleList(unpickedIndices, rng);
|
||||
// Assign rewards to picked positions
|
||||
for (int i = 0; i < availableRewards.Count && i < pickedIndices.Count; i++)
|
||||
{
|
||||
MinorRewards[pickedIndices[i]] = availableRewards[i];
|
||||
// Debug.Log($"[EventBingo] Available MiniDrop: item {availableRewards[i].id} @ idx{pickedIndices[i]}");
|
||||
}
|
||||
// Assign display rewards to unpicked positions
|
||||
for (int i = 0; i < displayRewards.Count && i < unpickedIndices.Count; i++)
|
||||
{
|
||||
MinorRewards[unpickedIndices[i]] = displayRewards[i];
|
||||
// Debug.Log($"[EventBingo] Unavailable MiniDrop: item {displayRewards[i].id} @ idx {unpickedIndices[i]}");
|
||||
}
|
||||
// var leftOverIndices = Enumerable.Range(0, EventBingoModel.NumberCount)
|
||||
// .Where(x => !pickedIndexSet.Contains(x))
|
||||
// .ToHashSet()
|
||||
// .ToList()
|
||||
// .OrderBy(x => rng.Next())
|
||||
// .ToList();
|
||||
// var pickedIndexList = pickedIndexSet.ToList().OrderBy(x => rng.Next()).ToList();
|
||||
// for (int i = 0; i < availableRewards.Count && i < pickedIndexList.Count; i++)
|
||||
// {
|
||||
// var idx = pickedIndexList.First();
|
||||
// MinorRewards[idx] = availableRewards[i];
|
||||
// pickedIndexList.Remove(idx);
|
||||
// }
|
||||
// for (int i = 0; i < displayRewards.Count && i < leftOverIndices.Count; i++)
|
||||
// {
|
||||
// var idx = leftOverIndices.First();
|
||||
// MinorRewards[idx] = displayRewards[i];
|
||||
// leftOverIndices.Remove(idx);
|
||||
// }
|
||||
}
|
||||
|
||||
public int[] GetNumberRollerDataList(int step)
|
||||
{
|
||||
var startIdx = Mathf.Max(1, step - EventBingoModel.RecordLength + 1);
|
||||
var count = Mathf.Min(EventBingoModel.RecordLength, step);
|
||||
return _playerNumberSequence.GetRange(startIdx, count).ToArray();
|
||||
}
|
||||
|
||||
public bool ValidateStepIndex(int idx)
|
||||
{
|
||||
return idx < _playerNumberSequence.Count && idx < _playerIdxSequence.Count && idx < _stateSequence.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventBingoStepState
|
||||
{
|
||||
public int BoardState;
|
||||
public int Number;
|
||||
public int Index;
|
||||
}
|
||||
|
||||
public class EventBingoPlayfabData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public int EventId, TicketCount, RoundCount, StepInRound, Seed, ChainProgress, ChainTaskTokenProgress;
|
||||
public float InflationRate;
|
||||
private const int TokenCount = 8;
|
||||
private const string Key = "EventBingo";
|
||||
private string Serialize()
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.Append(EventId).Append(EventBingoModel.Splitter);
|
||||
sb.Append(TicketCount).Append(EventBingoModel.Splitter);
|
||||
sb.Append(RoundCount).Append(EventBingoModel.Splitter);
|
||||
sb.Append(StepInRound).Append(EventBingoModel.Splitter);
|
||||
sb.Append(Seed).Append(EventBingoModel.Splitter);
|
||||
sb.Append(ChainProgress).Append(EventBingoModel.Splitter);
|
||||
sb.Append(ChainTaskTokenProgress).Append(EventBingoModel.Splitter);
|
||||
sb.Append(InflationRate);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static EventBingoPlayfabData Deserialize(string s)
|
||||
{
|
||||
if (s == null || s == "")
|
||||
return null;
|
||||
var tokens = s.Trim(EventBingoModel.Splitter).Split(EventBingoModel.Splitter);
|
||||
if (tokens.Length != TokenCount && tokens.Length != TokenCount - 1)
|
||||
{
|
||||
Debug.LogWarning($"[EventBingo]Wrong amount of parameters. Expect {TokenCount}, but got {tokens.Length} in \"{s}\".");
|
||||
return null;
|
||||
}
|
||||
var eventId = int.Parse(tokens[0]);
|
||||
var ticketCount = int.Parse(tokens[1]);
|
||||
var roundCount = int.Parse(tokens[2]);
|
||||
var stepInRound = int.Parse(tokens[3]);
|
||||
var seed = int.Parse(tokens[4]);
|
||||
var chainProgress = int.Parse(tokens[5]);
|
||||
var chainTaskTokenProgress = int.Parse(tokens[6]);
|
||||
var inflationRate = 0f;
|
||||
if (tokens.Length == TokenCount)
|
||||
{
|
||||
inflationRate = float.Parse(tokens[7]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Old data detected, missing inflation rate. Setting to 0.");
|
||||
}
|
||||
return new EventBingoPlayfabData(eventId, ticketCount, roundCount,
|
||||
stepInRound, seed, chainProgress, chainTaskTokenProgress, inflationRate);
|
||||
}
|
||||
|
||||
public EventBingoPlayfabData(int eventId, int ticketCount, int roundCount, int stepInRound,
|
||||
int seed, int chainProgress, int chainTaskTokenProgress, float inflationRate)
|
||||
{
|
||||
EventId = eventId;
|
||||
TicketCount = ticketCount;
|
||||
RoundCount = roundCount;
|
||||
StepInRound = stepInRound;
|
||||
Seed = seed;
|
||||
ChainProgress = chainProgress;
|
||||
ChainTaskTokenProgress = chainTaskTokenProgress;
|
||||
InflationRate = inflationRate;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
saveStore.Write(Key, Serialize()).RequireAccepted();
|
||||
}
|
||||
|
||||
public static EventBingoPlayfabData Load()
|
||||
{
|
||||
var s = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>().Read(Key).ValueOrThrow();
|
||||
return Deserialize(s);
|
||||
}
|
||||
}
|
||||
|
||||
public class EventBingoEntranceData
|
||||
{
|
||||
public bool NeedRedPoint;
|
||||
public int RedPointIndex;
|
||||
public DateTime ExpiryTime;
|
||||
public DateTime StartTime;
|
||||
public string Icon;
|
||||
}
|
||||
|
||||
public class EventBingoPlayerPreferenceData
|
||||
{
|
||||
public List<EventBingoNumberRollerItemData> RollerDataList;
|
||||
public bool IsFirstTime = true;
|
||||
private readonly static string Key = "EventBingo" + GContext.container.Resolve<IUserService>().UserId;
|
||||
|
||||
private string Serialize()
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.Append(IsFirstTime ? "1" : "0").Append(EventBingoModel.Splitter);
|
||||
foreach (var data in RollerDataList)
|
||||
sb.Append(data.number).Append(EventBingoModel.Comma).Append(data.iconIdx).Append(EventBingoModel.Splitter);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static EventBingoPlayerPreferenceData Deserialize(string s)
|
||||
{
|
||||
var tokens = s.Trim(EventBingoModel.Splitter).Split(EventBingoModel.Splitter);
|
||||
var rollerData = new List<EventBingoNumberRollerItemData>();
|
||||
bool isFirstTime;
|
||||
if (tokens.Length < 1)
|
||||
{
|
||||
throw new Exception("[EventBingo] Invalid serialized data, length < 1");
|
||||
}
|
||||
isFirstTime = tokens[0] == "1";
|
||||
for (int i = 1; i < tokens.Length; i++)
|
||||
{
|
||||
var token = tokens[i];
|
||||
var tokenTokens = token.Split(EventBingoModel.Comma);
|
||||
if (tokenTokens.Length != 2)
|
||||
throw new Exception("[EventBingo] Wrong amount of parameters in " + token);
|
||||
rollerData.Add(new EventBingoNumberRollerItemData
|
||||
{
|
||||
number = int.Parse(tokenTokens[0]),
|
||||
iconIdx = int.Parse(tokenTokens[1])
|
||||
});
|
||||
}
|
||||
return new EventBingoPlayerPreferenceData { RollerDataList = rollerData, IsFirstTime = isFirstTime };
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
PlayerPrefs.SetString(Key, Serialize());
|
||||
}
|
||||
|
||||
public static EventBingoPlayerPreferenceData Load()
|
||||
{
|
||||
var s = PlayerPrefs.GetString(Key);
|
||||
if (s == null || s == "")
|
||||
return null;
|
||||
try
|
||||
{
|
||||
var res = Deserialize(s);
|
||||
return res;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class EventBingoTicketUpdateEvent { }
|
||||
|
||||
public class EventBingoUiData
|
||||
{
|
||||
public string InfoPanel { get; set; }
|
||||
public string NeoNormalPackPanel { get; set; }
|
||||
}
|
||||
@@ -1,406 +0,0 @@
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using SystemRandom = System.Random;
|
||||
using System.Text;
|
||||
using System;
|
||||
using UnityEngine.Assertions;
|
||||
using GameCore;
|
||||
using cfg;
|
||||
using System.Collections.Generic;
|
||||
using asap.core;
|
||||
|
||||
public class DrillBlockData
|
||||
{
|
||||
public ItemData Item { get; set; }
|
||||
public bool IsEmpty => Item == null || Item.id == -1 || (int)Item.count == 0;
|
||||
public static DrillBlockData CreateEmpty()
|
||||
{
|
||||
return new DrillBlockData();
|
||||
}
|
||||
}
|
||||
|
||||
public class DrillJobModel
|
||||
{
|
||||
public int Cost { get; set; }
|
||||
public DrillBlockData[] Blocks { get; set; }
|
||||
}
|
||||
|
||||
public class EventBreakTaskGenerationInfo
|
||||
{
|
||||
public int TableId { get; set; }
|
||||
public int CurrentGemProgress { get; set; }
|
||||
public int TargetItemId { get; set; }
|
||||
}
|
||||
|
||||
public class EventBreakTaskInfo
|
||||
{
|
||||
public int TaskId { get; set; }
|
||||
public int TargetItemId { get; set; }
|
||||
public ItemData Reward { get; set; }
|
||||
public int TotalGemRequired { get; set; }
|
||||
public int CurrentGemProgress { get; set; }
|
||||
}
|
||||
|
||||
public class DrillModel : IHasChainPack
|
||||
{
|
||||
public int EventId { get; set; }
|
||||
public int ChainPackID { get; set; }
|
||||
public float InflationRate { get; set; }
|
||||
|
||||
public int TicketCount { get; private set; }
|
||||
public DrillJobModel[] DrillJobList { get; set; }
|
||||
public EventBreakTaskInfo[] TaskInfoList { get; set; }
|
||||
public int[] DrillJobSeeds { get; set; }
|
||||
public int ChainPackProgress;
|
||||
public void Update(int eventId, EventBreakPfData pfData = null) // new event
|
||||
{
|
||||
if (pfData == null || pfData.EventId != eventId)
|
||||
{
|
||||
GenSeeds();
|
||||
InflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
EventBreakAct.Ctx.GetFreshNewModelData(DrillJobSeeds, out var welcomeGift, out var drillJobs, out var taskInfoList);
|
||||
EventId = eventId;
|
||||
TicketCount = welcomeGift;
|
||||
TaskInfoList = taskInfoList;
|
||||
DrillJobList = drillJobs;
|
||||
ChainPackProgress = 0;
|
||||
ChainPackID = EventBreakAct.Ctx.GetChainPackID();
|
||||
ToPlayfabData().Save();
|
||||
return;
|
||||
}
|
||||
// var playerPreferenceData = EventBreakPlayerPreferenceData.Get(EventBreakAct.Ctx.TunnelCount);
|
||||
EventId = eventId;
|
||||
ChainPackID = pfData.ChainPackID;
|
||||
TicketCount = pfData.TicketCount;
|
||||
InflationRate = pfData.InflationRate;
|
||||
DrillJobSeeds = pfData.DrillJobSeeds;
|
||||
TaskInfoList = EventBreakAct.Ctx.GetTaskInfoArray(pfData.TaskData);
|
||||
DrillJobList = EventBreakAct.Ctx.GetDrillJobModels(pfData.DrillJobSeeds);
|
||||
ChainPackProgress = pfData.ChainPackProgress;
|
||||
return;
|
||||
}
|
||||
|
||||
private void GenSeeds()
|
||||
{
|
||||
DrillJobSeeds ??= new int[EventBreakAct.Ctx.TunnelCount];
|
||||
for (int i = 0; i < DrillJobSeeds.Length; i++)
|
||||
DrillJobSeeds[i] = 0;
|
||||
// ToPlayfabData().Save();
|
||||
}
|
||||
|
||||
public void UpdateSeed(int idx)
|
||||
{
|
||||
// DrillJobSeeds ??= new int[EventBreakAct.Ctx.TunnelCount];
|
||||
if (EventBreakAct.Ctx.DoesSeedNeedInc(idx, DrillJobSeeds[idx]))
|
||||
{
|
||||
DrillJobSeeds[idx]++;
|
||||
ToPlayfabData().Save();
|
||||
return;
|
||||
}
|
||||
Assert.IsTrue(idx >= 0 && idx < DrillJobSeeds.Length,
|
||||
$"[EventBreak] Cannot genenrate seed, idx {idx} is out of range [0, {DrillJobSeeds.Length}]");
|
||||
var seed = DateTime.Now.Millisecond;
|
||||
DrillJobSeeds[idx] = new SystemRandom(seed).Next();
|
||||
ToPlayfabData().Save();
|
||||
}
|
||||
|
||||
public bool AddTicket(int add)
|
||||
{
|
||||
if (TicketCount + add < 0)
|
||||
return false;
|
||||
TicketCount += add;
|
||||
ToPlayfabData().Save();
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(EventId, TicketCount);
|
||||
EventBreakAct.EventAggregator.Publish(new EventTicketUpdate());
|
||||
return true;
|
||||
}
|
||||
|
||||
public EventBreakPfData ToPlayfabData()
|
||||
{
|
||||
return new EventBreakPfData
|
||||
{
|
||||
EventId = EventId,
|
||||
ChainPackID = ChainPackID,
|
||||
InflationRate = InflationRate,
|
||||
TicketCount = TicketCount,
|
||||
TaskData = TaskInfoList.Select(i => (i.TaskId, i.CurrentGemProgress)).ToArray(),
|
||||
ChainPackProgress = ChainPackProgress,
|
||||
DrillJobSeeds = DrillJobSeeds
|
||||
};
|
||||
}
|
||||
|
||||
public void SetChainProgress(int p)
|
||||
{
|
||||
ChainPackProgress = p;
|
||||
}
|
||||
|
||||
public int GetChainProgress()
|
||||
{
|
||||
return ChainPackProgress;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
ToPlayfabData().Save();
|
||||
}
|
||||
|
||||
public GenericChainPackData<DrillModel> ToChainPackData()
|
||||
{
|
||||
return GenericChainPackData<DrillModel>.Create(this, EventBreakAct.Ctx.GetPackData(ChainPackID));
|
||||
}
|
||||
}
|
||||
|
||||
public class EventBreakPfData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public int EventId { get; set; }
|
||||
public int TicketCount { get; set; }
|
||||
public int ChainPackProgress { get; set; }
|
||||
public float InflationRate { get; set; }
|
||||
public int ChainPackID { get; set; }
|
||||
public (int id, int progress)[] TaskData { get; set; }
|
||||
public int[] DrillJobSeeds { get; set; }
|
||||
private const string Splitter = ",", Comma = "|";
|
||||
public const string PfKey = "EventBreak";
|
||||
/// <summary>
|
||||
/// Temporary stash.
|
||||
/// </summary>
|
||||
// public static EventBreakPfData Stash = null;
|
||||
//private const int TokenCount = 13;
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
var s = new StringBuilder();
|
||||
s.Append(EventId + Splitter);
|
||||
s.Append(TicketCount + Splitter);
|
||||
s.Append(ChainPackProgress + Splitter);
|
||||
foreach (var (id, progress) in TaskData)
|
||||
s.Append(id + Splitter + progress + Splitter);
|
||||
foreach (var seed in DrillJobSeeds)
|
||||
s.Append(seed + Splitter);
|
||||
s.Append(InflationRate + Splitter);
|
||||
s.Append(ChainPackID + Splitter);
|
||||
return s.ToString();
|
||||
}
|
||||
|
||||
public static EventBreakPfData Deserialize(string s)
|
||||
{
|
||||
if (s == null)
|
||||
{
|
||||
Debug.Log($"[EventBreak]Null string.");
|
||||
return null;
|
||||
}
|
||||
var res = new EventBreakPfData();
|
||||
var tokens = s.Trim(Splitter[0]).Split(Splitter);
|
||||
if (tokens.Length < 12)
|
||||
{
|
||||
Debug.Log($"[EventBreak]Wrong string format: string \"{s}\" has {tokens.Length} tokens, and the expected number is 14 or 13 or 12");
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
res.EventId = int.Parse(tokens[0]);
|
||||
res.TicketCount = int.Parse(tokens[1]);
|
||||
res.ChainPackProgress = int.Parse(tokens[2]);
|
||||
res.TaskData = new (int id, int progress)[3];
|
||||
int i = 3;
|
||||
while (i < 9)
|
||||
{
|
||||
res.TaskData[i / 2 - 1] = (int.Parse(tokens[i]), int.Parse(tokens[i + 1]));
|
||||
i += 2;
|
||||
}
|
||||
res.DrillJobSeeds = new int[3];
|
||||
while (i < 12)
|
||||
{
|
||||
res.DrillJobSeeds[i - 9] = int.Parse(tokens[i]);
|
||||
i++;
|
||||
}
|
||||
if (tokens.Length >= 13)
|
||||
{
|
||||
res.InflationRate = float.Parse(tokens[12]);
|
||||
if (tokens.Length >= 14)
|
||||
{
|
||||
res.ChainPackID = int.Parse(tokens[13]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Old data detected, missing inflation rate. Setting to 0.");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Log($"[EventBreak]Possible parse error: {e.Message}\n{e.StackTrace}");
|
||||
return null;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var s = Serialize();
|
||||
saveStore.Write(PfKey, s).RequireAccepted();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public class EventBreakPlayerPreferenceDataLegacy
|
||||
{
|
||||
public int[] Seeds { get; set; }
|
||||
private const string Splitter = ",", PpKey = "EventBreakCache";
|
||||
|
||||
public static EventBreakPlayerPreferenceDataLegacy Get(int seedCount = 3)
|
||||
{
|
||||
var s = PlayerPrefs.GetString(PpKey);
|
||||
if (s == "")
|
||||
return GenSeeds(seedCount);
|
||||
var res = Deserialize(s);
|
||||
if (res == null)
|
||||
return GenSeeds(seedCount);
|
||||
return res;
|
||||
}
|
||||
|
||||
private static EventBreakPlayerPreferenceDataLegacy GenSeeds(int count)
|
||||
{
|
||||
var seeds = new int[count];
|
||||
var rng = new SystemRandom(DateTime.Now.Millisecond);
|
||||
for (int i = 0; i < count; i++)
|
||||
seeds[i] = rng.Next();
|
||||
return new EventBreakPlayerPreferenceDataLegacy
|
||||
{
|
||||
Seeds = seeds
|
||||
};
|
||||
}
|
||||
|
||||
private static EventBreakPlayerPreferenceDataLegacy Deserialize(string s)
|
||||
{
|
||||
var tokens = s.Trim(Splitter[0]).Split(Splitter);
|
||||
var seeds = new int[tokens.Length];
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < tokens.Length; i++)
|
||||
seeds[i] = int.Parse(tokens[i]);
|
||||
return new EventBreakPlayerPreferenceDataLegacy
|
||||
{
|
||||
Seeds = seeds
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Log($"[EventBreak]Possible parse error: {e.Message}\n{e.StackTrace}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private string Serialize()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var seed in Seeds)
|
||||
{
|
||||
sb.Append(seed + Splitter);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var s = Serialize();
|
||||
PlayerPrefs.SetString(PpKey, s);
|
||||
}
|
||||
}
|
||||
|
||||
public class EventBreakUIData
|
||||
{
|
||||
public string MainPanelUrl { get; set; }
|
||||
public string InfoPanelUrl { get; set; }
|
||||
public string ChainPackPanelUrl { get; set; }
|
||||
public string NormalPackPanelUrl { get; set; }
|
||||
public string RewardPanelUrl { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class EventBreakChainPackGenerationInfo
|
||||
{
|
||||
public List<int> ChainList { get; set; }
|
||||
public DateTime ExpireTime { get; set; }
|
||||
public Pack[] Packs { get; set; }
|
||||
}
|
||||
|
||||
// public class EventBreakChainPackData : IChainPackData
|
||||
// {
|
||||
// private const int SlotCount = 6;
|
||||
// private List<int> ChainList { get; set; }
|
||||
// private Pack[] Packs { get; set; }
|
||||
// public bool IsEndGame => ChainProgress > ChainList.Count - SlotCount;
|
||||
// public int ChainListCount => ChainList.Count;
|
||||
// public int ChainProgress { get; set; }
|
||||
// public int EventId { get; set; }
|
||||
// private DateTime ExpireTime { get; set; }
|
||||
// public TimeSpan RemainingTime => ExpireTime - ZZTimeHelper.UtcNow();
|
||||
|
||||
// public bool IsChainPackDepleted => ChainProgress >= ChainListCount;
|
||||
|
||||
// public bool DoNeedPackRedPoint
|
||||
// {
|
||||
// get
|
||||
// {
|
||||
// if (!IsChainPackDepleted && ChainProgress < ChainListCount)
|
||||
// return Packs[ChainProgress].IAPID == 0;
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// public string RedPointKey => "eventbreak.pack";
|
||||
|
||||
// public EventBreakChainPackData(EventBreakChainPackGenerationInfo info, int eventId, ref int chainProgress)
|
||||
// {
|
||||
// ChainList = info.ChainList;
|
||||
// ChainProgress = chainProgress;
|
||||
// EventId = eventId;
|
||||
// ExpireTime = info.ExpireTime;
|
||||
// Packs = info.Packs;
|
||||
// }
|
||||
|
||||
// public Pack GetChainPackByChainProgress(int chainProgress)
|
||||
// {
|
||||
// // Debug.Log($"[EventBreak] chainProgress: {chainProgress}");
|
||||
// return Packs[chainProgress];
|
||||
// }
|
||||
|
||||
// public int GetChainProgressBySlotIdx(int slotIdx)
|
||||
// {
|
||||
// int res;
|
||||
// if (ChainProgress > ChainList.Count - SlotCount)
|
||||
// res = ChainList.Count - SlotCount + slotIdx;
|
||||
// else
|
||||
// res = ChainProgress + slotIdx;
|
||||
// Assert.IsTrue(res < ChainList.Count, $"Progress {res} out of range: {ChainListCount}");
|
||||
// // Debug.Log($"[EventBreak] slotIdx: {slotIdx}, chainProgress: {res}");
|
||||
// return res;
|
||||
// }
|
||||
|
||||
// // Is this needed or what?
|
||||
// public List<int> GetTokenProgressRewardAfterAddingToken(int tokenAdded)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
|
||||
// public void UploadData()
|
||||
// {
|
||||
// EventBreakAct.DrillModel.ChainPackProgress = ChainProgress;
|
||||
// EventBreakAct.DrillModel.ToPlayfabData().Save();
|
||||
// }
|
||||
|
||||
// public void OnBuySuccess()
|
||||
// {
|
||||
// ChainProgress++;
|
||||
// UploadData();
|
||||
// }
|
||||
|
||||
// public List<ItemData> GetItemsByPackDropId(int dropId)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
// }
|
||||
@@ -1,564 +0,0 @@
|
||||
using asap.core;
|
||||
using GameCore;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using game;
|
||||
using cfg;
|
||||
using System;
|
||||
|
||||
public class EventCanModel
|
||||
{
|
||||
public int TicketCount, EventId, CycleId, RedirectId, ChainPackProgress, ChainPackID;
|
||||
public float InflationRate;
|
||||
public EventCanCanInfo[] CanList;
|
||||
public Dictionary<EEventCanRewardType, EventCanGemTaskInfo> GemTasks;
|
||||
public EventCanProgressTaskInfo ProgressTaskInfo;
|
||||
public EventCanRewardInfo[] RewardPool;
|
||||
public EventCanWeightModifier WeightModifier;
|
||||
private const int CanColorCount = 4;
|
||||
public EEventCanBlockState BlockState = EEventCanBlockState.Available;
|
||||
|
||||
public static EventCanModel GetModel(int eventId, EventCanPlayfabData pfData = null, EventCanPlayerPreferenceData ppData = null)
|
||||
{
|
||||
var res = new EventCanModel();
|
||||
ppData ??= EventCanPlayerPreferenceData.RandomInit(CanColorCount);
|
||||
if (pfData == null || pfData.EventId != eventId) //New Event
|
||||
{
|
||||
res.EventId = eventId;
|
||||
res.TicketCount = GContext.container.Resolve<EventCanTableContext>().GetWelcomeGift();
|
||||
// res.CycleId = ;
|
||||
// res.RedirectId = ;
|
||||
res.CanList = new EventCanCanInfo[GContext.container.Resolve<EventCanTableContext>().GetMaxCanCount()];
|
||||
for (int i = 0; i < res.CanList.Length; i++)
|
||||
res.CanList[i] = new EventCanCanInfo(true, UnityEngine.Random.Range(0, CanColorCount));
|
||||
res.GemTasks = GContext.container.Resolve<EventCanTableContext>().InitGemTaskInfo();
|
||||
res.ProgressTaskInfo = GContext.container.Resolve<EventCanTableContext>().InitProgressTaskInfo();
|
||||
res.RewardPool = GContext.container.Resolve<EventCanTableContext>().InitRewardPool().ToArray();
|
||||
res.WeightModifier = new EventCanWeightModifier(GContext.container.Resolve<EventCanTableContext>().GetInitialDrawsSinceLastRewards());
|
||||
res.ChainPackProgress = 0;
|
||||
res.ToPlayerPreferenceData().Save();
|
||||
res.ToPlayfabData().Save();
|
||||
}
|
||||
else //read from playfab and playerprefs
|
||||
{
|
||||
res.EventId = eventId;
|
||||
res.TicketCount = pfData.TicketCount;
|
||||
res.CanList = new EventCanCanInfo[GContext.container.Resolve<EventCanTableContext>().GetMaxCanCount()];
|
||||
for (int i = 0; i < res.CanList.Length; i++)
|
||||
{
|
||||
var doesExist = (pfData.CanState & (1 << i)) != 0;
|
||||
res.CanList[i] = new EventCanCanInfo(doesExist, ppData.ColorIndices[i]);
|
||||
}
|
||||
res.GemTasks = pfData.GemTaskInfo
|
||||
.ToDictionary(info => (EEventCanRewardType)(info.Key % 1000 / 100),
|
||||
info => GContext.container.Resolve<EventCanTableContext>().GetGemTaskInfo(info.Key, info.Value));
|
||||
GContext.container.Resolve<EventCanTableContext>().GetProgressInfoByIdx(pfData.ProgressTaskIdx, out var progressRequired, out var reward);
|
||||
res.ProgressTaskInfo = EventCanProgressTaskInfo.Init(pfData.ProgressTaskIdx, pfData.ProgressTaskProgress);
|
||||
res.RewardPool = GContext.container.Resolve<EventCanTableContext>().InitRewardPool().ToArray();
|
||||
res.WeightModifier = new EventCanWeightModifier(pfData.DrawsSinceLastRewards);
|
||||
res.ChainPackProgress = pfData.ChainPackProgress;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public void InitializeModel(int eventId, EventCanPlayfabData pfData = null, EventCanPlayerPreferenceData ppData = null)
|
||||
{
|
||||
ppData ??= EventCanPlayerPreferenceData.RandomInit(CanColorCount);
|
||||
if (pfData == null || pfData.EventId != eventId) //New Event
|
||||
{
|
||||
EventId = eventId;
|
||||
InflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
TicketCount = GContext.container.Resolve<EventCanTableContext>().GetWelcomeGift();
|
||||
// this.CycleId = ;
|
||||
// this.RedirectId = ;
|
||||
CanList = new EventCanCanInfo[GContext.container.Resolve<EventCanTableContext>().GetMaxCanCount()];
|
||||
for (int i = 0; i < CanList.Length; i++)
|
||||
CanList[i] = new EventCanCanInfo(true, UnityEngine.Random.Range(0, CanColorCount));
|
||||
GemTasks = GContext.container.Resolve<EventCanTableContext>().InitGemTaskInfo();
|
||||
ProgressTaskInfo = GContext.container.Resolve<EventCanTableContext>().InitProgressTaskInfo();
|
||||
RewardPool = GContext.container.Resolve<EventCanTableContext>().InitRewardPool().ToArray();
|
||||
WeightModifier = new EventCanWeightModifier(GContext.container.Resolve<EventCanTableContext>().GetInitialDrawsSinceLastRewards());
|
||||
ChainPackProgress = 0;
|
||||
ChainPackID = GContext.container.Resolve<EventCanTableContext>().GetChainPackID();
|
||||
ToPlayerPreferenceData().Save();
|
||||
ToPlayfabData().Save();
|
||||
}
|
||||
else //read from playfab and playerprefs
|
||||
{
|
||||
EventId = eventId;
|
||||
ChainPackID = pfData.ChainPackID;
|
||||
TicketCount = pfData.TicketCount;
|
||||
InflationRate = pfData.InflationRate;
|
||||
CanList = new EventCanCanInfo[GContext.container.Resolve<EventCanTableContext>().GetMaxCanCount()];
|
||||
for (int i = 0; i < CanList.Length; i++)
|
||||
{
|
||||
var doesExist = (pfData.CanState & (1 << i)) != 0;
|
||||
CanList[i] = new EventCanCanInfo(doesExist, ppData.ColorIndices[i]);
|
||||
}
|
||||
GemTasks = pfData.GemTaskInfo
|
||||
.ToDictionary(info => (EEventCanRewardType)(info.Key % 1000 / 100),
|
||||
info => GContext.container.Resolve<EventCanTableContext>().GetGemTaskInfo(info.Key, info.Value));
|
||||
GContext.container.Resolve<EventCanTableContext>().GetProgressInfoByIdx(pfData.ProgressTaskIdx, out var progressRequired, out var reward);
|
||||
ProgressTaskInfo = EventCanProgressTaskInfo.Init(pfData.ProgressTaskIdx, pfData.ProgressTaskProgress);
|
||||
RewardPool = GContext.container.Resolve<EventCanTableContext>().InitRewardPool().ToArray();
|
||||
WeightModifier = new EventCanWeightModifier(pfData.DrawsSinceLastRewards);
|
||||
ChainPackProgress = pfData.ChainPackProgress;
|
||||
}
|
||||
}
|
||||
|
||||
public bool AddTicket(int count)
|
||||
{
|
||||
bool res;
|
||||
TicketCount += count;
|
||||
if (TicketCount < 0)
|
||||
{
|
||||
TicketCount = 0;
|
||||
res = false;
|
||||
}
|
||||
else
|
||||
res = true;
|
||||
ToPlayfabData().Save();
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(EventId, TicketCount);
|
||||
EventCanAct.EventAggregator.Publish(new EventCanTicketUpdate());
|
||||
return res;
|
||||
}
|
||||
|
||||
public void Log()
|
||||
{
|
||||
Debug.Log($"[EventCan] Model: TicketCount {TicketCount}");
|
||||
foreach (var task in GemTasks.Values)
|
||||
{
|
||||
Debug.Log($"[EventCan] GemTask{task.TaskId}: {task.CurrentGemProgress}/{task.TotalGemRequired}");
|
||||
}
|
||||
Debug.Log($"[EventCan] ProgressTask: {ProgressTaskInfo.TaskIdx} {ProgressTaskInfo.CurrentProgress}/{ProgressTaskInfo.TotalProgressRequired}");
|
||||
}
|
||||
|
||||
public EventCanPlayfabData ToPlayfabData()
|
||||
{
|
||||
int cs = 0;
|
||||
for (int i = 0; i < CanList.Length; i++)
|
||||
if (CanList[i].DoesExist)
|
||||
cs += 1 << i;
|
||||
var res = new EventCanPlayfabData
|
||||
{
|
||||
EventId = EventId,
|
||||
ChainPackID = ChainPackID,
|
||||
InflationRate = InflationRate,
|
||||
CycleId = CycleId,
|
||||
RedirectId = RedirectId,
|
||||
TicketCount = TicketCount,
|
||||
CanState = cs,
|
||||
ChainPackProgress = ChainPackProgress,
|
||||
ProgressTaskIdx = ProgressTaskInfo.TaskIdx,
|
||||
ProgressTaskProgress = ProgressTaskInfo.CurrentProgress,
|
||||
GemTaskInfo = GemTasks.ToDictionary(kv => kv.Value.TaskId, kv => kv.Value.CurrentGemProgress),
|
||||
DrawsSinceLastRewards = WeightModifier.DrawsSinceLastRewards
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
public EventCanPlayerPreferenceData ToPlayerPreferenceData()
|
||||
{
|
||||
var res = new EventCanPlayerPreferenceData
|
||||
{
|
||||
ColorIndices = CanList.Select(c => c.ColorIdx).ToArray()
|
||||
};
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventCanWeightModifier
|
||||
{
|
||||
/// <summary>
|
||||
/// RewardId, DrawCount
|
||||
/// </summary>
|
||||
public Dictionary<int, int> DrawsSinceLastRewards;
|
||||
public EventCanWeightModifier(Dictionary<int, int> d) => DrawsSinceLastRewards = d;
|
||||
|
||||
public void UpdateDrawsSinceLastRewards(int rewardId)
|
||||
{
|
||||
foreach (var k in DrawsSinceLastRewards.Keys.ToArray())
|
||||
if (k == rewardId)
|
||||
DrawsSinceLastRewards[k] = 0;
|
||||
else
|
||||
DrawsSinceLastRewards[k] += 1;
|
||||
}
|
||||
|
||||
public void GetWeightDelta(int rewardId, out int weight, out bool mustGet)
|
||||
{
|
||||
if (!GContext.container.Resolve<EventCanTableContext>().GetWeightChangeThreshold(rewardId, out var t))
|
||||
{
|
||||
mustGet = false;
|
||||
weight = 0;
|
||||
return;
|
||||
}
|
||||
if (!DrawsSinceLastRewards.TryGetValue(rewardId, out var drawCount))
|
||||
{
|
||||
// Debug.Log($"[EventCan] No draw count found for reward id {rewardId}, init error.");
|
||||
mustGet = false;
|
||||
weight = 0;
|
||||
return;
|
||||
}
|
||||
// Debug.Log($"[EventCan] Draw count for reward id {rewardId} is {drawCount}, threshold is {t}");
|
||||
if (drawCount + 1 >= t.Item2)
|
||||
{
|
||||
mustGet = true;
|
||||
weight = 0;
|
||||
return;
|
||||
}
|
||||
if (drawCount + 1 >= t.Item1)
|
||||
{
|
||||
mustGet = false;
|
||||
weight = GContext.container.Resolve<EventCanTableContext>().GetRewardWeight(rewardId);
|
||||
return;
|
||||
}
|
||||
mustGet = false;
|
||||
weight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventCanRewardInfo
|
||||
{
|
||||
public int RewardId;
|
||||
/// <summary>
|
||||
/// Used to decide which task to fulfill and whether to calculate dynamic weight
|
||||
/// </summary>
|
||||
public EEventCanRewardType RewardType;
|
||||
public int ItemId;
|
||||
public int ItemCount;
|
||||
/// <summary>
|
||||
/// 0 if this reward has dynamic weight.
|
||||
/// </summary>
|
||||
public int BaseWeight;
|
||||
/// <summary>
|
||||
/// Returns the item data of the reward.
|
||||
/// </summary>
|
||||
/// <returns>Transformed Item Data. Null if the reward is either gem or supply.</returns>
|
||||
public ItemData ToItemData()
|
||||
{
|
||||
if (RewardType == EEventCanRewardType.Supply)
|
||||
{
|
||||
Debug.Log($"[EventCan] RewardType {RewardType} is not Item.");
|
||||
return null;
|
||||
}
|
||||
var res = new ItemData(ItemId, ItemCount);
|
||||
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
playerItemData.ItemTransition(res);
|
||||
if (ItemId == 1001)
|
||||
{
|
||||
playerItemData.ItemInflation(res, GContext.container.Resolve<EventCanModel>().InflationRate);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventCanCanInfo
|
||||
{
|
||||
public bool DoesExist;
|
||||
public int ColorIdx;
|
||||
public EventCanCanInfo(bool doesExist, int colorIdx)
|
||||
{
|
||||
DoesExist = doesExist;
|
||||
ColorIdx = colorIdx;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventCanGemTaskInfo
|
||||
{
|
||||
public int TaskId;
|
||||
public int CurrentGemProgress;
|
||||
public int TotalGemRequired;
|
||||
public ItemData Reward;
|
||||
public int GemIdx => TaskId % 1000 / 100;
|
||||
public EEventCanRewardType GemType => (EEventCanRewardType)(TaskId % 1000 / 100);
|
||||
public bool IsCompleted => CurrentGemProgress >= TotalGemRequired;
|
||||
|
||||
public void UpdateGemTasks(EventCanRewardInfo info, out ItemData reward, out int oldTaskId)
|
||||
{
|
||||
reward = null;
|
||||
oldTaskId = 0;
|
||||
if (info.RewardType != EEventCanRewardType.Green
|
||||
&& info.RewardType != EEventCanRewardType.Blue
|
||||
&& info.RewardType != EEventCanRewardType.Purple
|
||||
&& info.RewardType != EEventCanRewardType.Red)
|
||||
{
|
||||
Debug.Log($"[EventCan] RewardType {info.RewardType} is not gem.");
|
||||
return;
|
||||
}
|
||||
CurrentGemProgress += info.ItemCount;
|
||||
if (!IsCompleted)
|
||||
return;
|
||||
reward = Reward;
|
||||
oldTaskId = TaskId;
|
||||
GContext.container.Resolve<EventCanTableContext>().GetNextGemTaskInfo(this);
|
||||
}
|
||||
|
||||
public EventCanGemTaskInfo DeepCopy()
|
||||
{
|
||||
var info = new EventCanGemTaskInfo();
|
||||
info.TaskId = TaskId;
|
||||
info.CurrentGemProgress = CurrentGemProgress;
|
||||
info.TotalGemRequired = TotalGemRequired;
|
||||
info.Reward = new ItemData(Reward.id, (int)Reward.count);
|
||||
info.Reward.origin = Reward.origin;
|
||||
info.Reward.inflate = Reward.inflate;
|
||||
return info;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class EventCanProgressTaskInfo
|
||||
{
|
||||
public int TaskIdx;
|
||||
public int CurrentProgress;
|
||||
public int TotalProgressRequired;
|
||||
public ItemData Reward;
|
||||
public bool IsCompleted => CurrentProgress >= TotalProgressRequired;
|
||||
public void UpdateProgressTask(out ItemData reward)
|
||||
{
|
||||
reward = null;
|
||||
CurrentProgress++;
|
||||
if (IsCompleted)
|
||||
{
|
||||
reward = Reward;
|
||||
GContext.container.Resolve<EventCanTableContext>().GetNextProgressTaskInfo(this);
|
||||
}
|
||||
}
|
||||
|
||||
public static EventCanProgressTaskInfo Init(int TaskIdx, int CurrentProgress)
|
||||
{
|
||||
EventCanProgressTaskInfo info = new EventCanProgressTaskInfo();
|
||||
info.TaskIdx = TaskIdx;
|
||||
info.CurrentProgress = CurrentProgress;
|
||||
GContext.container.Resolve<EventCanTableContext>().GetProgressInfoByIdx(TaskIdx, out info.TotalProgressRequired, out info.Reward);
|
||||
return info;
|
||||
}
|
||||
|
||||
public EventCanProgressTaskInfo DeepCopy()
|
||||
{
|
||||
return new EventCanProgressTaskInfo()
|
||||
{
|
||||
TaskIdx = TaskIdx,
|
||||
CurrentProgress = CurrentProgress,
|
||||
TotalProgressRequired = TotalProgressRequired,
|
||||
Reward = new ItemData(Reward.id, (int)Reward.count)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class EventCanPlayfabData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public const string PlayFabKey = "EventCan";
|
||||
private const char Splitter = '|', Comma = ',';
|
||||
private const int SerializedTokenCount = 11;
|
||||
public int TicketCount, ProgressTaskIdx, ProgressTaskProgress, EventId, CycleId, RedirectId,
|
||||
CanState, ChainPackProgress, ChainPackID;
|
||||
/// <summary>
|
||||
/// GemTaskId, CurrentGemProgress
|
||||
/// </summary>
|
||||
public Dictionary<int, int> GemTaskInfo;
|
||||
/// <summary>
|
||||
/// RewardId, DrawCount
|
||||
/// </summary>
|
||||
public Dictionary<int, int> DrawsSinceLastRewards;
|
||||
public float InflationRate;
|
||||
public string Serialize()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(TicketCount).Append(Splitter);
|
||||
sb.Append(EventId).Append(Splitter);
|
||||
sb.Append(CycleId).Append(Splitter);
|
||||
sb.Append(RedirectId).Append(Splitter);
|
||||
sb.Append(CanState).Append(Splitter);
|
||||
sb.Append(ChainPackProgress).Append(Splitter);
|
||||
sb.Append(ProgressTaskIdx).Append(Comma)
|
||||
.Append(ProgressTaskProgress).Append(Comma)
|
||||
.Append(InflationRate).Append(Comma)
|
||||
.Append(ChainPackID).Append(Splitter);
|
||||
foreach (var kv in GemTaskInfo)
|
||||
sb.Append(kv.Key).Append(Comma).Append(kv.Value).Append(Splitter);
|
||||
foreach (var kv in DrawsSinceLastRewards)
|
||||
sb.Append(kv.Key).Append(Comma).Append(kv.Value).Append(Splitter);
|
||||
// Debug.Log("[EventCan] Serialize: " + sb.ToString());
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static EventCanPlayfabData Deserialize(string s)
|
||||
{
|
||||
if (s == null || s == "")
|
||||
return null;
|
||||
var res = new EventCanPlayfabData();
|
||||
try
|
||||
{
|
||||
var tokens = s.Trim(Splitter).Split(Splitter);
|
||||
if (tokens.Length < SerializedTokenCount)
|
||||
throw new Exception($"[EventCan]Wrong amount of parameters. Expect at least {SerializedTokenCount}, but got {tokens.Length} in \"{s}\".");
|
||||
res.TicketCount = int.Parse(tokens[0]);
|
||||
res.EventId = int.Parse(tokens[1]);
|
||||
res.CycleId = int.Parse(tokens[2]);
|
||||
res.RedirectId = int.Parse(tokens[3]);
|
||||
res.CanState = int.Parse(tokens[4]);
|
||||
res.ChainPackProgress = int.Parse(tokens[5]);
|
||||
var token = tokens[6].Split(Comma);
|
||||
res.ProgressTaskIdx = int.Parse(token[0]);
|
||||
res.ProgressTaskProgress = int.Parse(token[1]);
|
||||
if (token.Length >= 3)
|
||||
{
|
||||
res.InflationRate = float.Parse(token[2]);
|
||||
if (token.Length >= 4)
|
||||
res.ChainPackID = int.Parse(token[3]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Old data detected, missing inflation rate. Setting to 0.");
|
||||
}
|
||||
res.GemTaskInfo = new Dictionary<int, int>();
|
||||
for (int i = 7; i < 11; i++)
|
||||
res.GemTaskInfo.Add(int.Parse(tokens[i].Split(Comma)[0]), int.Parse(tokens[i].Split(Comma)[1]));
|
||||
res.DrawsSinceLastRewards = new Dictionary<int, int>();
|
||||
for (int i = 11; i < tokens.Length; i++)
|
||||
res.DrawsSinceLastRewards.Add(int.Parse(tokens[i].Split(Comma)[0]), int.Parse(tokens[i].Split(Comma)[1]));
|
||||
Debug.Log("[EventCan] Deserialize: " + res);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventCan] Failed to deserialize event can model: {e.Message}\n{e.StackTrace}");
|
||||
return null;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var s = Serialize();
|
||||
saveStore.Write(PlayFabKey, s).RequireAccepted();
|
||||
}
|
||||
|
||||
public void Log()
|
||||
{
|
||||
Debug.Log($"[EventCan] TicketCount: {TicketCount}\n" +
|
||||
$"EventId: {EventId}\n" +
|
||||
$"CycleId: {CycleId}\n" +
|
||||
$"RedirectId: {RedirectId}\n" +
|
||||
$"CanState: {CanState}\n" +
|
||||
$"ProgressTaskIdx: {ProgressTaskIdx}\n" +
|
||||
$"ProgressTaskProgress: {ProgressTaskProgress}\n"
|
||||
);
|
||||
foreach (var kv in GemTaskInfo)
|
||||
Debug.Log($"GemTaskInfo: {kv.Key}, {kv.Value}");
|
||||
foreach (var kv in DrawsSinceLastRewards)
|
||||
Debug.Log($"DrawsSinceLastRewards: {kv.Key}, {kv.Value}");
|
||||
}
|
||||
}
|
||||
|
||||
public class EventCanPlayerPreferenceData
|
||||
{
|
||||
public static string PlayerPrefsKey => "EventCanPlayerPreferenceData" + GContext.container.Resolve<IUserService>().UserId;
|
||||
public int[] ColorIndices;
|
||||
public string Serialize()
|
||||
{
|
||||
return Newtonsoft.Json.JsonConvert.SerializeObject(ColorIndices);
|
||||
}
|
||||
public static EventCanPlayerPreferenceData Deserialize(string s)
|
||||
{
|
||||
if (s == null || s == "")
|
||||
return null;
|
||||
var data = new EventCanPlayerPreferenceData();
|
||||
data.ColorIndices = Newtonsoft.Json.JsonConvert.DeserializeObject<int[]>(s);
|
||||
return data;
|
||||
}
|
||||
public void Save()
|
||||
{
|
||||
var s = Serialize();
|
||||
PlayerPrefs.SetString(PlayerPrefsKey, s);
|
||||
}
|
||||
|
||||
public static EventCanPlayerPreferenceData RandomInit(int canColorCount)
|
||||
{
|
||||
var res = new EventCanPlayerPreferenceData();
|
||||
res.ColorIndices = new int[GContext.container.Resolve<EventCanTableContext>().GetMaxCanCount()];
|
||||
for (int i = 0; i < res.ColorIndices.Length; i++)
|
||||
{
|
||||
res.ColorIndices[i] = UnityEngine.Random.Range(0, canColorCount);
|
||||
}
|
||||
res.Save();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class EventCanChainPackData : AChainPackData
|
||||
{
|
||||
public EventCanChainPackData() { }
|
||||
public EventCanChainPackData(EventChainPackInfo info, int eventId, ref int chainProgress) : base(eventId, chainProgress)
|
||||
{
|
||||
ChainList = info.ChainList;
|
||||
ExpireTime = info.ExpireTime;
|
||||
Packs = info.Packs;
|
||||
}
|
||||
|
||||
public void Init(EventChainPackInfo info, int eventId, ref int chainProgress)
|
||||
{
|
||||
EventId = eventId;
|
||||
ChainProgress = chainProgress;
|
||||
ChainList = info.ChainList;
|
||||
ExpireTime = info.ExpireTime;
|
||||
Packs = info.Packs;
|
||||
}
|
||||
|
||||
public override void UploadData()
|
||||
{
|
||||
var model = GContext.container.Resolve<EventCanModel>();
|
||||
model.ChainPackProgress = ChainProgress;
|
||||
model.ToPlayfabData().Save();
|
||||
}
|
||||
|
||||
public int RedPointIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
int res = 0;
|
||||
for (int i = ChainProgress; i < ChainListCount; i++)
|
||||
{
|
||||
if (Packs[i].IAPID == 0)
|
||||
res++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class EventCanEntranceBtnData
|
||||
{
|
||||
public string BtnIconUrl;
|
||||
public DateTime ExpiryTime, StartTime;
|
||||
public TimeSpan RemainingTime => ExpiryTime - ZZTimeHelper.UtcNow();
|
||||
public bool IsActive => ZZTimeHelper.UtcNow() >= StartTime && ZZTimeHelper.UtcNow() < ExpiryTime;
|
||||
public int RedPointTicketThreshold;
|
||||
}
|
||||
|
||||
public enum EEventCanRewardType
|
||||
{
|
||||
Green = 1,
|
||||
Blue = 2,
|
||||
Purple = 3,
|
||||
Red = 4,
|
||||
Supply = 5,
|
||||
Item = 6
|
||||
}
|
||||
|
||||
|
||||
[Flags]
|
||||
public enum EEventCanBlockState
|
||||
{
|
||||
Available,
|
||||
Gem,
|
||||
Supply,
|
||||
Blocked = Gem | Supply
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,350 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using GameCore;
|
||||
using asap.core;
|
||||
using System.Linq;
|
||||
|
||||
public class EventGatherCoreData : IHasChainPack
|
||||
{
|
||||
/// <summary>
|
||||
/// How many stages has been played.
|
||||
/// </summary>
|
||||
private int _ticketCount, _stageCount, _eventId, _chainProgress;
|
||||
float _inflationRate;
|
||||
public float InflationRate => _inflationRate;
|
||||
public int StageCount => _stageCount;
|
||||
public int TicketCount => _ticketCount;
|
||||
public int EventId => _eventId;
|
||||
public int ChainPackID { get; set; }
|
||||
public EventGatherCoreStageData StageData;
|
||||
public EventGatherCoreData() { }
|
||||
|
||||
public static EventGatherCoreData CreateNew(cfg.FishingEvent e)
|
||||
{
|
||||
var res = new EventGatherCoreData();
|
||||
res._stageCount = 0;
|
||||
res.StageData = EventGatherCoreStageData.CreateNew();
|
||||
res._eventId = e.ID;
|
||||
return res;
|
||||
}
|
||||
|
||||
public void InitNew(int eventId)
|
||||
{
|
||||
_stageCount = 0;
|
||||
_ticketCount = EventGatherCoreAct.Ctx.GetWelcomeGift();
|
||||
ChainPackID = EventGatherCoreAct.Ctx.GetChainPackID();
|
||||
_eventId = eventId;
|
||||
StageData = EventGatherCoreStageData.CreateNew();
|
||||
StageData.MoveToStage(0);
|
||||
_chainProgress = 0;
|
||||
_inflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
}
|
||||
|
||||
public void LoadData(EventGatherCorePlayfabData data)
|
||||
{
|
||||
_eventId = data.EventId;
|
||||
_ticketCount = data.TicketCount;
|
||||
ChainPackID = data.ChainPackID;
|
||||
_stageCount = data.StageCount;
|
||||
StageData = EventGatherCoreStageData.CreateNew();
|
||||
StageData.Init(data.StageCount, data.RewardClaimStateFlag, data.TriggerItemStateFlag);
|
||||
_chainProgress = data.ChainProgress;
|
||||
_inflationRate = data.InflationRate;
|
||||
}
|
||||
|
||||
public void MoveToNextStage()
|
||||
{
|
||||
_stageCount++;
|
||||
StageData.MoveToStage(StageCount);
|
||||
ToPlayfabData().Save();
|
||||
}
|
||||
|
||||
public void AddTicket(int n)
|
||||
{
|
||||
_ticketCount += n;
|
||||
if (_ticketCount < 0)
|
||||
{
|
||||
_ticketCount -= n;
|
||||
}
|
||||
ToPlayfabData().Save();
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(_eventId, _ticketCount);
|
||||
GContext.Publish(new EventTicketUpdate());
|
||||
}
|
||||
|
||||
public EventGatherCorePlayfabData ToPlayfabData()
|
||||
{
|
||||
var res = new EventGatherCorePlayfabData
|
||||
{
|
||||
EventId = _eventId,
|
||||
TicketCount = _ticketCount,
|
||||
StageCount = _stageCount,
|
||||
ChainPackID = ChainPackID,
|
||||
RewardClaimStateFlag = StageData.RewardClaimStateFlag,
|
||||
TriggerItemStateFlag = StageData.TriggerItemStateFlag,
|
||||
ChainProgress = _chainProgress,
|
||||
InflationRate = _inflationRate
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
public EventBingoEntranceData ToEntranceData()
|
||||
{
|
||||
var res = new EventBingoEntranceData();
|
||||
var ctx = EventGatherCoreAct.Ctx;
|
||||
var t = ctx.GetEventStartTimeAndEndTime();
|
||||
var chainPackData = GenericChainPackData<EventGatherCoreData>.Create(this, ctx.GetChainPackInfo(ChainPackID));
|
||||
res.StartTime = t.Item1;
|
||||
res.ExpiryTime = t.Item2;
|
||||
res.Icon = ctx.GetEntranceIcon();
|
||||
res.NeedRedPoint = GContext.container.Resolve<EventGatherCoreData>().TicketCount >= ctx.GetRedPointThreshold();
|
||||
res.RedPointIndex = _ticketCount + chainPackData.RedPointIndex;
|
||||
return res;
|
||||
}
|
||||
|
||||
#region ChainPack
|
||||
public void SetChainProgress(int p)
|
||||
{
|
||||
_chainProgress = p;
|
||||
}
|
||||
|
||||
public int GetChainProgress()
|
||||
{
|
||||
return _chainProgress;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
ToPlayfabData().Save();
|
||||
}
|
||||
public GenericChainPackData<EventGatherCoreData> ToChainPackData()
|
||||
{
|
||||
return GenericChainPackData<EventGatherCoreData>.Create(this, EventGatherCoreAct.Ctx.GetChainPackInfo(ChainPackID));
|
||||
}
|
||||
#endregion
|
||||
|
||||
public TimerContext ToTimerContext(System.Action onExpire)
|
||||
{
|
||||
var ctx = EventGatherCoreAct.Ctx;
|
||||
var t = ctx.GetEventStartTimeAndEndTime();
|
||||
var res = new TimerContext
|
||||
{
|
||||
ExpiryTime = t.Item2,
|
||||
StartTime = t.Item1,
|
||||
OnExpire = onExpire
|
||||
};
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventGatherCoreStageData
|
||||
{
|
||||
public List<int> RewardIdList = new List<int>();
|
||||
public long RewardClaimStateFlag, TriggerItemStateFlag;
|
||||
public int StageId;
|
||||
// public int CoreItemReceivedCount => RewardIdList.Count((id, idx) => EventGatherCoreTableContext.CoreItemIndicator == id && IsRewardClaimed(idx));
|
||||
public int CoreItemReceivedCount
|
||||
{
|
||||
get
|
||||
{
|
||||
int result = 0;
|
||||
if (RewardIdList == null)
|
||||
return result;
|
||||
result = RewardIdList.Where((id, idx) => EventGatherCoreAct.Ctx.IsCoreItem(id) && IsRewardClaimed(idx)).Count();
|
||||
// Debug.Log($"[EventGatherCore] CoreItemReceivedCount: {result}");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
private EventGatherCoreStageData() { }
|
||||
|
||||
public static EventGatherCoreStageData CreateNew()
|
||||
{
|
||||
var res = new EventGatherCoreStageData();
|
||||
res.MoveToStage(0);
|
||||
return res;
|
||||
}
|
||||
|
||||
public int PlayCount
|
||||
{
|
||||
get
|
||||
{
|
||||
int res = 0;
|
||||
long flag = RewardClaimStateFlag;
|
||||
while (flag > 0)
|
||||
{
|
||||
if ((flag & 1) != 0)
|
||||
{
|
||||
res++;
|
||||
}
|
||||
flag >>= 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
public void Init(int roundCount, long rewardClaimStateFlag = 0L, long triggerItemStateFlag = 0L)
|
||||
{
|
||||
// Debug.Log($"[EventGatherCore]Move to stage {roundCount}");
|
||||
RewardClaimStateFlag = rewardClaimStateFlag;
|
||||
TriggerItemStateFlag = triggerItemStateFlag;
|
||||
StageId = EventGatherCoreAct.Ctx.GetStageId(roundCount);
|
||||
RewardIdList = EventGatherCoreAct.Ctx.GetRewardIdList(StageId);
|
||||
}
|
||||
|
||||
public void MoveToStage(int roundCount)
|
||||
{
|
||||
// Debug.Log($"[EventGatherCore]Move to stage {roundCount}");
|
||||
var ctx = EventGatherCoreAct.Ctx;
|
||||
StageId = ctx.GetStageId(roundCount);
|
||||
RewardIdList = ctx.GetStageInfo(StageId);
|
||||
RewardClaimStateFlag = 0L;
|
||||
TriggerItemStateFlag = 0L;
|
||||
}
|
||||
|
||||
public bool IsRewardClaimed(int rewardIdx)
|
||||
{
|
||||
return (RewardClaimStateFlag & (1L << rewardIdx)) != 0;
|
||||
}
|
||||
|
||||
public bool IsTriggerUsed(int triggerIdx)
|
||||
{
|
||||
return (TriggerItemStateFlag & (1L << triggerIdx)) != 0;
|
||||
}
|
||||
|
||||
public bool IsEarlyTry(EventGatherCoreTableContext ctx)
|
||||
{
|
||||
return PlayCount < ctx.GetStageMinAttemptCount(StageId) - 1;
|
||||
}
|
||||
|
||||
public bool IsCloseToFinish(EventGatherCoreTableContext ctx)
|
||||
{
|
||||
return CoreItemReceivedCount == ctx.GetCoreItemTargetCount(StageId) - 1;
|
||||
}
|
||||
|
||||
public void SetRewardClaimed(int rewardIdx)
|
||||
{
|
||||
RewardClaimStateFlag |= 1L << rewardIdx;
|
||||
}
|
||||
|
||||
public void SetTriggerUsed(int triggerIdx)
|
||||
{
|
||||
TriggerItemStateFlag |= 1L << triggerIdx;
|
||||
}
|
||||
// public void AddCoreItem(int num = 1)
|
||||
// {
|
||||
// CoreItemReceivedCount += num;
|
||||
// }
|
||||
|
||||
public bool IsStageCleared(EventGatherCoreTableContext ctx)
|
||||
{
|
||||
long fullFlag = (1L << ctx.GetStageInfo(StageId).Count) - 1;
|
||||
return CoreItemReceivedCount >= ctx.GetCoreItemTargetCount(StageId) || (RewardClaimStateFlag & fullFlag) == fullFlag;
|
||||
}
|
||||
|
||||
public List<int> GetRemainingRewardIds()
|
||||
{
|
||||
return RewardIdList.Where((id, idx) => ((1 << idx) & RewardClaimStateFlag) == 0).ToList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class EventGatherCoreTriggerTaken
|
||||
{
|
||||
public bool Succeeded;
|
||||
public int RewardIdx;
|
||||
public int RewardId;
|
||||
public Vector2 Position;
|
||||
}
|
||||
|
||||
public class EventGatherCoreRewardInfo
|
||||
{
|
||||
public int Idx;
|
||||
public ItemData ItemData;
|
||||
public bool IsSpecial = false;
|
||||
public bool IsClaimed;
|
||||
}
|
||||
|
||||
public class EventGatherCorePlayfabData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public int EventId { get; set; }
|
||||
public int TicketCount { get; set; }
|
||||
public int ChainPackID { get; set; }
|
||||
public int StageCount { get; set; }
|
||||
public long RewardClaimStateFlag { get; set; }
|
||||
public long TriggerItemStateFlag { get; set; }
|
||||
public int ChainProgress { get; set; }
|
||||
public float InflationRate { get; set; }
|
||||
public static readonly string Key = "EventGatherCoreData";
|
||||
private readonly char Splitter = '|';
|
||||
private readonly int TokenCount = 7;
|
||||
|
||||
private string Serialize()
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.Append(EventId).Append(Splitter);
|
||||
sb.Append(TicketCount).Append(Splitter);
|
||||
sb.Append(StageCount).Append(Splitter);
|
||||
sb.Append(RewardClaimStateFlag).Append(Splitter);
|
||||
sb.Append(TriggerItemStateFlag).Append(Splitter);
|
||||
sb.Append(ChainProgress).Append(Splitter);
|
||||
sb.Append(InflationRate).Append(Splitter);
|
||||
sb.Append(ChainPackID);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private void Deserialize(string data)
|
||||
{
|
||||
var tokens = data.Trim(Splitter).Split(Splitter);
|
||||
if (tokens.Length < 7)
|
||||
throw new System.Exception($"[EventGatherCore]Wrong amount of parameters. Expect {TokenCount}, but got {tokens.Length} in \"{data}\".");
|
||||
EventId = int.Parse(tokens[0]);
|
||||
TicketCount = int.Parse(tokens[1]);
|
||||
StageCount = int.Parse(tokens[2]);
|
||||
RewardClaimStateFlag = long.Parse(tokens[3]);
|
||||
TriggerItemStateFlag = long.Parse(tokens[4]);
|
||||
ChainProgress = int.Parse(tokens[5]);
|
||||
|
||||
InflationRate = float.Parse(tokens[6]);
|
||||
if (tokens.Length > 7)
|
||||
{
|
||||
ChainPackID = int.Parse(tokens[7]);
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
saveStore.Write(Key, Serialize()).RequireAccepted();
|
||||
}
|
||||
|
||||
public static EventGatherCorePlayfabData Load()
|
||||
{
|
||||
var s = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>().Read(Key).ValueOrThrow();
|
||||
var res = new EventGatherCorePlayfabData();
|
||||
try
|
||||
{
|
||||
res.Deserialize(s);
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogWarning($"[EventGatherCore]Error when deserializing EventGatherCoreData: {e}");
|
||||
return null;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class EventGatherCoreUiData
|
||||
{
|
||||
public string MainPanelUrl { get; set; }
|
||||
public string InfoPanelUrl { get; set; }
|
||||
public string ChainPackPanelUrl { get; set; }
|
||||
public string NormalPackPanelUrl { get; set; }
|
||||
public string RewardPopupPanelUrl { get; set; }
|
||||
}
|
||||
|
||||
public class EventGatherCoreRewardPopupPanelClose
|
||||
{
|
||||
public ItemData Reward { get; set; }
|
||||
}
|
||||
@@ -1,329 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
/// <summary>
|
||||
/// 同一系列的活动使用同一个Model
|
||||
/// 礼包、代币、进度奖励等数据结构都相同
|
||||
/// 后续活动只用关系自己的玩法数据即可
|
||||
/// </summary>
|
||||
public class EventLuckyGameModel : ILuckyTaskGetData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public static EventLuckyGameModel Get()
|
||||
{
|
||||
var model = GContext.container.Resolve<EventLuckyGameModel>();
|
||||
if (model == null)
|
||||
{
|
||||
GContext.container.Register<EventLuckyGameModel>().AsSingleton();
|
||||
model = GContext.container.Resolve<EventLuckyGameModel>();
|
||||
}
|
||||
return model;
|
||||
}
|
||||
public const string Key = "EventLuckGameRecord";
|
||||
public const string RedPointKey = "luckgame";
|
||||
EventLuckGameRecord eventLuckGameRecord = new EventLuckGameRecord();
|
||||
FishingEventData fishingEventData;
|
||||
Tables tables;
|
||||
public FishingEvent fishingEvent { get; private set; }
|
||||
public EventLuckGameInfo eventInfo { get; private set; } = new EventLuckGameInfo();
|
||||
public FishingEventCycleItem cycle { get; private set; }
|
||||
public int EventId => eventLuckGameRecord.EventId;
|
||||
public int Progress => eventLuckGameRecord.Progress;
|
||||
public string EventGameData => eventLuckGameRecord.EventGameData;
|
||||
public TimeSpan RemainingTime
|
||||
{
|
||||
get
|
||||
{
|
||||
if (fishingEvent == null || fishingEvent.TimeDefinition is not LimitedTime limitedTime)
|
||||
{
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
return System.DateTime.Parse(limitedTime.EndTime) - ZZTimeHelper.UtcNow();
|
||||
}
|
||||
}
|
||||
|
||||
public EventLuckyGameModel(FishingEventData fishingEventData, Tables tables)
|
||||
{
|
||||
this.fishingEventData = fishingEventData ?? GContext.container.Resolve<FishingEventData>();
|
||||
this.tables = tables ?? GContext.container.Resolve<Tables>();
|
||||
luckyTaskData = new LuckyTaskData(this);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
SetRedPointState();
|
||||
|
||||
string s = Newtonsoft.Json.JsonConvert.SerializeObject(eventLuckGameRecord);
|
||||
//Debug.Log($"[EventLuckGameModel] Save EventLuckGameRecord: {s}");
|
||||
saveStore.Write(Key, s).RequireAccepted();
|
||||
}
|
||||
|
||||
//=========================以下是活动数据=========================
|
||||
public int TicketCount
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsActive)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
//_ticketCount = fishingEventData.GetTransitionDataCount(EventId);
|
||||
return eventLuckGameRecord.TicketCount;
|
||||
}
|
||||
private set
|
||||
{
|
||||
if (!IsActive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
eventLuckGameRecord.TicketCount = value;
|
||||
if (eventLuckGameRecord.TicketCount < 0)
|
||||
{
|
||||
eventLuckGameRecord.TicketCount = 0;
|
||||
}
|
||||
fishingEventData.SaveTransitionData(EventId, eventLuckGameRecord.TicketCount);
|
||||
Save();
|
||||
}
|
||||
}
|
||||
void SetRedPointState()
|
||||
{
|
||||
if (!IsActive)
|
||||
return;
|
||||
if (luckyTaskData == null || eventLuckGameRecord == null)
|
||||
return;
|
||||
int count = 1;
|
||||
if (fishingEvent.SubType == 4)
|
||||
{
|
||||
var luckyCardsData = Newtonsoft.Json.JsonConvert.DeserializeObject<LuckyCardsData>(EventGameData);
|
||||
|
||||
if (luckyCardsData != null && luckyCardsData.pos.Count > 0)
|
||||
{
|
||||
var config = tables.TbEventLuckyCardsConfig.GetOrDefault(luckyCardsData.pos.Count + 1);
|
||||
if (config == null)
|
||||
{
|
||||
config = tables.TbEventLuckyCardsConfig.GetOrDefault(1); // 默认配置
|
||||
}
|
||||
count = config.ItemCost;
|
||||
}
|
||||
}
|
||||
RedPointManager.Instance.SetRedPointState(RedPointKey, eventLuckGameRecord.TicketCount >= count, eventLuckGameRecord.TicketCount + luckyTaskData.RedCount);
|
||||
}
|
||||
public bool IsActive
|
||||
{
|
||||
get
|
||||
{
|
||||
if (fishingEvent == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fishingEvent.TimeDefinition is LimitedTime limitedTime)
|
||||
{
|
||||
return ZZTimeHelper.UtcNow() < System.DateTime.Parse(limitedTime.EndTime) && ZZTimeHelper.UtcNow() > System.DateTime.Parse(limitedTime.StartTime);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void AddTicket(int count)
|
||||
{
|
||||
if (fishingEvent == null || cycle == null || !IsActive)
|
||||
{
|
||||
Debug.LogError("[EventLuckGameModel] _tableEvent is null, cannot add ticket.");
|
||||
return;
|
||||
}
|
||||
TicketCount += count;
|
||||
}
|
||||
|
||||
#region 进度奖励
|
||||
public void AddProgress(int progress)
|
||||
{
|
||||
if (fishingEvent == null)
|
||||
{
|
||||
Debug.LogError("[EventLuckGameModel] _tableEvent is null, cannot add progress.");
|
||||
return;
|
||||
}
|
||||
eventLuckGameRecord.Progress += progress;
|
||||
if (eventLuckGameRecord.Progress < 0)
|
||||
{
|
||||
eventLuckGameRecord.Progress = 0;
|
||||
}
|
||||
Save();
|
||||
}
|
||||
|
||||
#endregion 进度奖励
|
||||
|
||||
public void OnEvent(FishingEvent t)
|
||||
{
|
||||
fishingEvent = t;
|
||||
if (t == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
string s = saveStore.Read(Key).ValueOrThrow();
|
||||
if (s == null)
|
||||
{
|
||||
eventLuckGameRecord = new EventLuckGameRecord();
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
eventLuckGameRecord = Newtonsoft.Json.JsonConvert.DeserializeObject<EventLuckGameRecord>(s) ?? throw new Newtonsoft.Json.JsonSerializationException("Null luck-game save.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventLuckGameModel] Deserialize Error: {e.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
DicTasks = eventLuckGameRecord.DicTasks;
|
||||
cycle = tables.TbFishingEventCycleItem.GetOrDefault(t.RedirectID);
|
||||
if (cycle == null)
|
||||
{
|
||||
Debug.LogError($"[EventLuckGameModel] CycleItem3 not found for RedirectID: {t.RedirectID}");
|
||||
return;
|
||||
}
|
||||
if (eventLuckGameRecord.EventId != t.ID)
|
||||
{
|
||||
eventLuckGameRecord.EventId = t.ID;
|
||||
eventLuckGameRecord.ChainProgress = 0;
|
||||
eventLuckGameRecord.Progress = 0;
|
||||
eventLuckGameRecord.EventGameData = string.Empty;
|
||||
TicketCount = cycle.WelcomeGift;
|
||||
DicTasks.Clear();
|
||||
}
|
||||
SetEventInfo();
|
||||
}
|
||||
|
||||
//=========================以上是活动数据=========================
|
||||
//=========================以下是玩法数据=========================
|
||||
public void SetEventGameData(string data)
|
||||
{
|
||||
if (fishingEvent == null)
|
||||
{
|
||||
Debug.LogError("[EventLuckyGameModel] fishingEvent is null, cannot set event game data.");
|
||||
return;
|
||||
}
|
||||
eventLuckGameRecord.EventGameData = data;
|
||||
Save();
|
||||
}
|
||||
void SetEventInfo()
|
||||
{
|
||||
if (fishingEvent == null)
|
||||
{
|
||||
Debug.LogError("[EventLuckyGameModel] fishingEvent is null, cannot set event info.");
|
||||
return;
|
||||
}
|
||||
eventInfo = GetEventLuckGameInfo();
|
||||
|
||||
if (DicTasks.Count == 0)
|
||||
{
|
||||
luckyTaskData.InitTasks();
|
||||
}
|
||||
else
|
||||
{
|
||||
luckyTaskData.SetRedPoint();
|
||||
}
|
||||
SetRedPointState();
|
||||
}
|
||||
|
||||
EventLuckGameInfo GetEventLuckGameInfo()
|
||||
{
|
||||
var eventInfo = new EventLuckGameInfo();
|
||||
if (fishingEvent.SubType == 4)
|
||||
{
|
||||
EventLuckyCardsMain _tableMain = tables.TbEventLuckyCardsMain.GetOrDefault(cycle.RedirectID);
|
||||
if (_tableMain == null)
|
||||
{
|
||||
Debug.LogError($"[EventLuckyCardsMain] EventLuckyCardsMain is Null RedirectID {cycle.RedirectID}");
|
||||
fishingEvent = null;
|
||||
return null;
|
||||
}
|
||||
eventInfo.IconName = _tableMain.Icon;
|
||||
eventInfo.MainPrefab = _tableMain.Prefab;
|
||||
eventInfo.TaskPanelName = _tableMain.TaskPrefab;
|
||||
eventInfo.ItemID = _tableMain.Item;
|
||||
eventInfo.PackId = _tableMain.PackId;
|
||||
}
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
#region 任务相关数据获取接口
|
||||
//这是一个工具类,只有一个监听任务触发的状态,处理任务数据
|
||||
private LuckyTaskData luckyTaskData;
|
||||
/// 获取任务面板名称,配表
|
||||
public string TaskPanelName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (DicTasks.GetValueOrDefault(-4, 0) == 1)
|
||||
{
|
||||
return "EventLuckyCardsADTaskPopupPanel";
|
||||
}
|
||||
return eventInfo.TaskPanelName;
|
||||
}
|
||||
}
|
||||
public string LuckyTaskRedPointKey => "luckgame.luckytask";
|
||||
//任务数据存储
|
||||
public Dictionary<int, int> DicTasks { get; set; } = new Dictionary<int, int>();
|
||||
|
||||
public void InitDicTasks()
|
||||
{
|
||||
luckyTaskData.RefreshTasks();
|
||||
}
|
||||
|
||||
public void SaveTaskData()
|
||||
{
|
||||
eventLuckGameRecord.DicTasks = DicTasks;
|
||||
Save();
|
||||
}
|
||||
public bool OnGetTaskReward(int id)
|
||||
{
|
||||
return luckyTaskData.OnGetTaskReward(id);
|
||||
}
|
||||
|
||||
public int GetLuckyPackID()
|
||||
{
|
||||
if (fishingEvent == null || cycle == null || eventInfo == null)
|
||||
{
|
||||
Debug.LogError("[EventLuckyGameModel] fishingEvent or cycle is null, cannot get lucky gift info.");
|
||||
return 0;
|
||||
}
|
||||
return eventInfo.PackId;
|
||||
}
|
||||
|
||||
#endregion 任务相关数据获取接口
|
||||
//=========================以上是玩法数据=========================
|
||||
public void Dispose()
|
||||
{
|
||||
luckyTaskData.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public class EventLuckGameRecord
|
||||
{
|
||||
public int EventId { get; set; }
|
||||
public int ChainProgress { get; set; }
|
||||
public int Progress { get; set; }
|
||||
public int TicketCount { get; set; } = 0;
|
||||
public string EventGameData { get; set; } = "";
|
||||
public Dictionary<int, int> DicTasks { get; set; } = new Dictionary<int, int>();
|
||||
|
||||
}
|
||||
|
||||
public class EventLuckGameInfo
|
||||
{
|
||||
public string ActName = "EventLuckyCardsAct";
|
||||
public string IconName;
|
||||
public string MainPrefab;
|
||||
public string TaskPanelName;
|
||||
public int ItemID;
|
||||
public int PackId;
|
||||
}
|
||||
|
||||
@@ -1,354 +0,0 @@
|
||||
using System.Text;
|
||||
using asap.core;
|
||||
using GameCore;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class EventOfferJourneyData : AEventOfferJourneyData
|
||||
{
|
||||
private int _progress, _eventId, _ticketCount, _vipLvlOnTrigger, _roundCount;
|
||||
private float _inflationRate;
|
||||
private EventOfferJourneyTableContext _ctx;
|
||||
public override int Progress => _progress;
|
||||
public override int TicketCount => _ticketCount;
|
||||
public override int EventId => _eventId;
|
||||
public override float InflationRate => _inflationRate;
|
||||
public override ShopBuyType BuyType => ShopBuyType.EventPack;
|
||||
public int VipLvlOnTrigger => _vipLvlOnTrigger;
|
||||
|
||||
public void Init(int eventId, int vipLvl)
|
||||
{
|
||||
_progress = 0;
|
||||
_eventId = eventId;
|
||||
_ticketCount = 0;
|
||||
_vipLvlOnTrigger = vipLvl;
|
||||
_roundCount = 0;
|
||||
_ctx = EventOfferJourneyTableContext.CreateNew(eventId);
|
||||
_inflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
}
|
||||
|
||||
public void InitTest()
|
||||
{
|
||||
_progress = 0;
|
||||
_eventId = 90500001;
|
||||
_ticketCount = 114514;
|
||||
_vipLvlOnTrigger = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
_roundCount = 0;
|
||||
_ctx = EventOfferJourneyTableContext.GetTestSample();
|
||||
}
|
||||
|
||||
private EventOfferJourneyPlayfabData ToPlayfabData()
|
||||
{
|
||||
return new EventOfferJourneyPlayfabData(_eventId, _progress, _ticketCount, _vipLvlOnTrigger, _roundCount, _inflationRate);
|
||||
}
|
||||
|
||||
public override bool AddTicket(int n)
|
||||
{
|
||||
_ticketCount += n;
|
||||
if (_ticketCount < 0)
|
||||
{
|
||||
_ticketCount -= n;
|
||||
return false;
|
||||
}
|
||||
SaveData();
|
||||
// GContext.container.Resolve<FishingEventData>().SaveTransitionData(_eventId, _ticketCount);
|
||||
GContext.Publish(new EventTicketUpdate());
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnBuySuccess()
|
||||
{
|
||||
if (_ctx == null)
|
||||
{
|
||||
Debug.LogError($"[EventOfferJourney]Trying to buy pack but there is no context.");
|
||||
return;
|
||||
}
|
||||
if (_progress >= _ctx.GetRewardList(_vipLvlOnTrigger).Count)
|
||||
{
|
||||
Debug.LogError($"[EventOfferJourney]Trying to buy pack no.{_progress} but there are only {_ctx.GetRewardList(_vipLvlOnTrigger).Count} rewards.");
|
||||
return;
|
||||
}
|
||||
_progress++;
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public override void ProgressUp()
|
||||
{
|
||||
if (_ctx == null)
|
||||
{
|
||||
Debug.LogError($"[EventOfferJourney]Trying to progress pack but there is no context.");
|
||||
return;
|
||||
}
|
||||
_progress++;
|
||||
if (_progress >= _ctx.GetRewardList(_vipLvlOnTrigger).Count)
|
||||
{
|
||||
_progress = 0;
|
||||
_roundCount++;
|
||||
Debug.Log($"[EventOfferJourney] Update vip level from {_vipLvlOnTrigger}");
|
||||
_vipLvlOnTrigger = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
Debug.Log($"[EventOfferJourney] to {_vipLvlOnTrigger}");
|
||||
}
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public override void SaveData()
|
||||
{
|
||||
ToPlayfabData().Save();
|
||||
}
|
||||
|
||||
public override bool LoadDataFromPlayfab()
|
||||
{
|
||||
var pfData = EventOfferJourneyPlayfabData.Load();
|
||||
if (pfData == null)
|
||||
return false;
|
||||
_eventId = pfData.EventId;
|
||||
_inflationRate = pfData.InflationRate;
|
||||
_progress = pfData.Progress;
|
||||
_ticketCount = pfData.TicketCount;
|
||||
_vipLvlOnTrigger = pfData.VipLvlOnTrigger;
|
||||
_roundCount = pfData.RoundCount;
|
||||
_ctx = EventOfferJourneyTableContext.CreateNew(_eventId);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override EventOfferJourneyEntranceData ToEntranceData()
|
||||
{
|
||||
if (_ctx == null)
|
||||
{
|
||||
return new EventOfferJourneyEntranceData(false, TimeSpan.Zero, "", false);
|
||||
}
|
||||
(var startTime, var endTime) = _ctx.GetEventStartTimeAndEndTime();
|
||||
var isActive = ZZTimeHelper.UtcNow() >= startTime && ZZTimeHelper.UtcNow() < endTime && _roundCount < _ctx.MaxRoundCount;
|
||||
var remainingTime = endTime - ZZTimeHelper.UtcNow();
|
||||
var iconUrl = _ctx.EntranceIconUrl;
|
||||
var cost = _ctx.GetTicketCostByProgress(_progress, _vipLvlOnTrigger);
|
||||
var doNeedRedPoint = cost > 0 && cost <= _ticketCount;
|
||||
var res = new EventOfferJourneyEntranceData(isActive, remainingTime, iconUrl, doNeedRedPoint);
|
||||
return res;
|
||||
}
|
||||
|
||||
public override List<EventOfferJourneyBlockData> GetRewardList()
|
||||
{
|
||||
if (_ctx == null)
|
||||
return new List<EventOfferJourneyBlockData>();
|
||||
return _ctx.GetRewardList(_vipLvlOnTrigger);
|
||||
}
|
||||
|
||||
public override ITimerContext ToTimerData(Action OnExpire)
|
||||
{
|
||||
if (_ctx == null)
|
||||
{
|
||||
Debug.LogError($"[EventOfferJourney]Trying to get timer data but there is no context.");
|
||||
return new TimerContext();
|
||||
}
|
||||
var res = new TimerContext
|
||||
{
|
||||
StartTime = _ctx.GetEventStartTimeAndEndTime().Item1,
|
||||
ExpiryTime = _ctx.GetEventStartTimeAndEndTime().Item2,
|
||||
OnExpire = OnExpire
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
public override EventOfferJourneyUiData GetUiData()
|
||||
{
|
||||
return _ctx.GetUiData();
|
||||
}
|
||||
|
||||
public override int[] GetTicketRewardArray()
|
||||
{
|
||||
return _ctx.GetTicketRewardArray();
|
||||
}
|
||||
|
||||
public override string GetTicketName()
|
||||
{
|
||||
return _ctx.GetTicketName();
|
||||
}
|
||||
|
||||
public override void OnRaid(int multiplier = 1)
|
||||
{
|
||||
int rewardCount = _ctx.GetTicketRewardArray()[0] * multiplier;
|
||||
GContext.Publish(new game.TargetAddData(_ctx.GetTicketId(), TicketCount, rewardCount));
|
||||
AddTicket(rewardCount);
|
||||
}
|
||||
|
||||
public override void OnBomb(int multiplier = 1)
|
||||
{
|
||||
int rewardCount = _ctx.GetTicketRewardArray()[1] * multiplier;
|
||||
GContext.Publish(new game.TargetAddData(_ctx.GetTicketId(), TicketCount, rewardCount));
|
||||
AddTicket(rewardCount);
|
||||
}
|
||||
|
||||
public override int GetStartPosParam()
|
||||
{
|
||||
return _ctx.GetStartPosParam();
|
||||
}
|
||||
}
|
||||
public abstract class AEventOfferJourneyData : IEventOfferJourneyData
|
||||
{
|
||||
public abstract int Progress { get; }
|
||||
public abstract int TicketCount { get; }
|
||||
public abstract int EventId { get; }
|
||||
public abstract float InflationRate { get; }
|
||||
public abstract ShopBuyType BuyType { get; }
|
||||
public string RedPointKey => "event_offer_journey.entrance";
|
||||
public abstract bool AddTicket(int n);
|
||||
public abstract List<EventOfferJourneyBlockData> GetRewardList();
|
||||
public abstract int GetStartPosParam();
|
||||
public abstract string GetTicketName();
|
||||
public abstract int[] GetTicketRewardArray();
|
||||
public abstract EventOfferJourneyUiData GetUiData();
|
||||
public abstract bool LoadDataFromPlayfab();
|
||||
public abstract void OnBomb(int multiplier = 1);
|
||||
public abstract void OnBuySuccess();
|
||||
public abstract void OnRaid(int multiplier = 1);
|
||||
public abstract void ProgressUp();
|
||||
public abstract void SaveData();
|
||||
public abstract EventOfferJourneyEntranceData ToEntranceData();
|
||||
public abstract ITimerContext ToTimerData(Action OnExpire);
|
||||
}
|
||||
|
||||
public interface IEventOfferJourneyData
|
||||
{
|
||||
public int Progress { get; }
|
||||
public int TicketCount { get; }
|
||||
public int EventId { get; }
|
||||
public float InflationRate { get; }
|
||||
public ShopBuyType BuyType { get; }
|
||||
public bool AddTicket(int n);
|
||||
public bool LoadDataFromPlayfab();
|
||||
public void SaveData();
|
||||
public void ProgressUp();
|
||||
public void OnBuySuccess();
|
||||
public List<EventOfferJourneyBlockData> GetRewardList();
|
||||
public EventOfferJourneyEntranceData ToEntranceData();
|
||||
public EventOfferJourneyUiData GetUiData();
|
||||
public ITimerContext ToTimerData(Action OnExpire);
|
||||
public int[] GetTicketRewardArray();
|
||||
public string GetTicketName();
|
||||
public void OnRaid(int multiplier = 1);
|
||||
public void OnBomb(int multiplier = 1);
|
||||
public int GetStartPosParam();
|
||||
public string RedPointKey { get; }
|
||||
}
|
||||
|
||||
public class EventOfferJourneyBlockData
|
||||
{
|
||||
public enum EType { IAP, Ticket }
|
||||
public EType Type;
|
||||
public int Price, DropId;
|
||||
private EventOfferJourneyBlockData() { }
|
||||
|
||||
public static EventOfferJourneyBlockData CreateIapData(int iapId, int dropId)
|
||||
{
|
||||
var data = new EventOfferJourneyBlockData();
|
||||
data.Type = EType.IAP;
|
||||
data.Price = iapId;
|
||||
data.DropId = dropId;
|
||||
return data;
|
||||
}
|
||||
|
||||
public static EventOfferJourneyBlockData CreateTicketData(int ticketCost, int dropId)
|
||||
{
|
||||
var data = new EventOfferJourneyBlockData();
|
||||
data.Type = EType.Ticket;
|
||||
data.Price = ticketCost;
|
||||
data.DropId = dropId;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventOfferJourneyPlayfabData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public int EventId, Progress, TicketCount, VipLvlOnTrigger, RoundCount;
|
||||
public float InflationRate;
|
||||
private const string Key = "EventOfferJourneyData";
|
||||
private const int TokenCount = 6;
|
||||
private const char Splitter = '|', Comma = ',';
|
||||
|
||||
public EventOfferJourneyPlayfabData(int eventId, int progress, int ticketCount, int vipLvlOnTrigger, int roundCount, float inflationRate)
|
||||
{
|
||||
EventId = eventId;
|
||||
Progress = progress;
|
||||
TicketCount = ticketCount;
|
||||
VipLvlOnTrigger = vipLvlOnTrigger;
|
||||
RoundCount = roundCount;
|
||||
InflationRate = inflationRate;
|
||||
}
|
||||
|
||||
private string Serialize()
|
||||
{
|
||||
var res = new StringBuilder();
|
||||
res.Append(EventId).Append(Splitter)
|
||||
.Append(Progress).Append(Splitter)
|
||||
.Append(TicketCount).Append(Splitter)
|
||||
.Append(VipLvlOnTrigger).Append(Splitter)
|
||||
.Append(RoundCount).Append(Splitter)
|
||||
.Append(InflationRate);
|
||||
return res.ToString();
|
||||
}
|
||||
|
||||
private static EventOfferJourneyPlayfabData Deserialize(string s)
|
||||
{
|
||||
if (s == null || s == "")
|
||||
return null;
|
||||
var tokens = s.Trim(Splitter).Split(Splitter);
|
||||
if (tokens.Length != TokenCount && tokens.Length != TokenCount - 1)
|
||||
{
|
||||
Debug.LogError($"[EventOfferJourney]Wrong amount of parameters. Expect {TokenCount}, but got {tokens.Length} in \"{s}\".");
|
||||
return null;
|
||||
}
|
||||
|
||||
var eventId = int.Parse(tokens[0]);
|
||||
var progress = int.Parse(tokens[1]);
|
||||
var ticketCount = int.Parse(tokens[2]);
|
||||
var vipLvlOnTrigger = int.Parse(tokens[3]);
|
||||
var roundCount = int.Parse(tokens[4]);
|
||||
float inflationRate = 0;
|
||||
if (tokens.Length == TokenCount)
|
||||
{
|
||||
inflationRate = float.Parse(tokens[5]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Old data detected, missing inflation rate. Setting to 0.");
|
||||
}
|
||||
return new EventOfferJourneyPlayfabData(eventId, progress, ticketCount, vipLvlOnTrigger, roundCount, inflationRate);
|
||||
}
|
||||
|
||||
public static EventOfferJourneyPlayfabData Load()
|
||||
{
|
||||
var s = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>().Read(Key).ValueOrThrow();
|
||||
return Deserialize(s);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var s = Serialize();
|
||||
saveStore.Write(Key, s).RequireAccepted();
|
||||
}
|
||||
}
|
||||
|
||||
public class EventOfferJourneyEntranceData
|
||||
{
|
||||
public bool IsActive { get; }
|
||||
public TimeSpan RemainingTime { get; }
|
||||
public string IconUrl { get; }
|
||||
public bool DoNeedRedPoint { get; }
|
||||
public EventOfferJourneyEntranceData(bool isActive, TimeSpan remainingTime, string iconUrl, bool doNeedRedPoint)
|
||||
{
|
||||
IsActive = isActive;
|
||||
RemainingTime = remainingTime;
|
||||
IconUrl = iconUrl;
|
||||
DoNeedRedPoint = doNeedRedPoint;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventOfferJourneyUiData
|
||||
{
|
||||
public string PanelUrl { get; set; }
|
||||
public string InfoPanelUrl { get; set; }
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using GameCore;
|
||||
using UnityEngine;
|
||||
using asap.core;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class EventOfferJourneyLvlTriggerData : AEventOfferJourneyData
|
||||
{
|
||||
private int _progress, _ticketCount, _roundCount;
|
||||
private EventOfferJourneyLvlTriggerTableContext _ctx = new EventOfferJourneyLvlTriggerTableContext();
|
||||
public override int EventId => _ctx.TriggerPackId;
|
||||
public override int Progress => _progress;
|
||||
public override int TicketCount => _ticketCount;
|
||||
public override float InflationRate => 0;
|
||||
|
||||
public override ShopBuyType BuyType => ShopBuyType.TriggerPack;
|
||||
public bool IsActive => _ctx.IsEventActive && _roundCount < _ctx.MaxRoundCount;
|
||||
// private int VipLvlOnTrigger => _ctx.VipLvlOnTrigger;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
_progress = 0;
|
||||
_ticketCount = 0;
|
||||
_roundCount = 0;
|
||||
}
|
||||
|
||||
public override bool AddTicket(int n)
|
||||
{
|
||||
_ticketCount += n;
|
||||
if (_ticketCount < 0)
|
||||
{
|
||||
_ticketCount -= n;
|
||||
return false;
|
||||
}
|
||||
SaveData();
|
||||
// GContext.container.Resolve<FishingEventData>().SaveTransitionData(_eventId, _ticketCount);
|
||||
GContext.Publish(new EventTicketUpdate());
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnBuySuccess()
|
||||
{
|
||||
if (_progress >= _ctx.GetRewardList().Count)
|
||||
{
|
||||
Debug.LogError($"[EventOfferJourney]Trying to buy pack no.{_progress} but there are only {_ctx.GetRewardList().Count} rewards.");
|
||||
return;
|
||||
}
|
||||
_progress++;
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public override void ProgressUp()
|
||||
{
|
||||
_progress++;
|
||||
if (_progress >= _ctx.GetRewardList().Count)
|
||||
{
|
||||
_progress = 0;
|
||||
_roundCount++;
|
||||
}
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public override void SaveData()
|
||||
{
|
||||
ToPlayfabData().Save();
|
||||
}
|
||||
|
||||
public override bool LoadDataFromPlayfab()
|
||||
{
|
||||
var pfData = EventOfferJourneyLvlTriggerPlayfabData.Load();
|
||||
if (pfData == null)
|
||||
return false;
|
||||
_progress = pfData.Progress;
|
||||
_ticketCount = pfData.TicketCount;
|
||||
_roundCount = pfData.RoundCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
private EventOfferJourneyLvlTriggerPlayfabData ToPlayfabData()
|
||||
{
|
||||
return new EventOfferJourneyLvlTriggerPlayfabData(_progress, _ticketCount, _roundCount);
|
||||
}
|
||||
|
||||
public override EventOfferJourneyEntranceData ToEntranceData()
|
||||
{
|
||||
if (!IsActive)
|
||||
{
|
||||
return new EventOfferJourneyEntranceData(false, TimeSpan.Zero, "", false);
|
||||
}
|
||||
TimeSpan remainingTime = _ctx.GetEventStartTimeAndEndTime().Item2 - ZZTimeHelper.UtcNow();
|
||||
// Debug.Log($"[EventOfferJourney] Remaining time: {remainingTime.TotalSeconds}s.");
|
||||
var isActive = IsActive && remainingTime.TotalSeconds > 0;
|
||||
// Debug.Log($"[EventOfferJourney] First Is active: {IsActive}.");
|
||||
// Debug.Log($"[EventOfferJourney] Total Is active: {isActive}.");
|
||||
var iconUrl = _ctx.EntranceIconUrl;
|
||||
// Debug.Log($"[EventOfferJourney] Icon URL: {iconUrl}.");
|
||||
var cost = _ctx.GetTicketCostByProgress(_progress);
|
||||
// Debug.Log($"[EventOfferJourney] Cost: {cost}.");
|
||||
var doNeedRedPoint = cost > 0 && cost <= _ticketCount;
|
||||
// Debug.Log($"[EventOfferJourney] Do need red point: {doNeedRedPoint}.");
|
||||
var res = new EventOfferJourneyEntranceData(isActive, remainingTime, iconUrl, doNeedRedPoint);
|
||||
return res;
|
||||
}
|
||||
|
||||
public override List<EventOfferJourneyBlockData> GetRewardList()
|
||||
{
|
||||
return _ctx.GetRewardList();
|
||||
}
|
||||
|
||||
public override ITimerContext ToTimerData(Action OnExpire)
|
||||
{
|
||||
var res = new TimerContext
|
||||
{
|
||||
StartTime = _ctx.GetEventStartTimeAndEndTime().Item1,
|
||||
ExpiryTime = _ctx.GetEventStartTimeAndEndTime().Item2,
|
||||
OnExpire = OnExpire
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
public override EventOfferJourneyUiData GetUiData()
|
||||
{
|
||||
return _ctx.GetUiData();
|
||||
}
|
||||
public override int[] GetTicketRewardArray()
|
||||
{
|
||||
return _ctx.GetTicketRewardArray();
|
||||
}
|
||||
|
||||
public override string GetTicketName()
|
||||
{
|
||||
return _ctx.GetTicketName();
|
||||
}
|
||||
|
||||
public override void OnRaid(int multiplier = 1)
|
||||
{
|
||||
int rewardCount = _ctx.GetTicketRewardArray()[0] * multiplier;
|
||||
GContext.Publish(new game.TargetAddData(_ctx.GetTicketId(), TicketCount, rewardCount));
|
||||
AddTicket(rewardCount);
|
||||
}
|
||||
|
||||
public override void OnBomb(int multiplier = 1)
|
||||
{
|
||||
int rewardCount = _ctx.GetTicketRewardArray()[1] * multiplier;
|
||||
GContext.Publish(new game.TargetAddData(_ctx.GetTicketId(), TicketCount, rewardCount));
|
||||
AddTicket(rewardCount);
|
||||
}
|
||||
|
||||
public override int GetStartPosParam()
|
||||
{
|
||||
return _ctx.GetStartPosParam();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class EventOfferJourneyLvlTriggerPlayfabData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public int Progress, TicketCount, RoundCount;
|
||||
private const string Key = "EventOfferJourneyLvlTriggerData";
|
||||
private const int TokenCount = 3;
|
||||
private const char Splitter = '|', Comma = ',';
|
||||
|
||||
public EventOfferJourneyLvlTriggerPlayfabData(int progress, int ticketCount, int roundCount)
|
||||
{
|
||||
Progress = progress;
|
||||
TicketCount = ticketCount;
|
||||
RoundCount = roundCount;
|
||||
}
|
||||
|
||||
private string Serialize()
|
||||
{
|
||||
var res = new StringBuilder();
|
||||
res.Append(Progress).Append(Splitter)
|
||||
.Append(TicketCount).Append(Splitter)
|
||||
.Append(RoundCount).Append(Splitter);
|
||||
return res.ToString();
|
||||
}
|
||||
|
||||
private static EventOfferJourneyLvlTriggerPlayfabData Deserialize(string s)
|
||||
{
|
||||
if (s == null || s == "")
|
||||
return null;
|
||||
var tokens = s.Trim(Splitter).Split(Splitter);
|
||||
if (tokens.Length != TokenCount)
|
||||
{
|
||||
Debug.LogError($"[EventOfferJourney]Wrong amount of parameters. Expect {TokenCount}, but got {tokens.Length} in \"{s}\".");
|
||||
return null;
|
||||
}
|
||||
var progress = int.Parse(tokens[0]);
|
||||
var ticketCount = int.Parse(tokens[1]);
|
||||
var roundCount = int.Parse(tokens[2]);
|
||||
return new EventOfferJourneyLvlTriggerPlayfabData(progress, ticketCount, roundCount);
|
||||
}
|
||||
|
||||
public static EventOfferJourneyLvlTriggerPlayfabData Load()
|
||||
{
|
||||
var s = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>().Read(Key).ValueOrThrow();
|
||||
return Deserialize(s);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var s = Serialize();
|
||||
saveStore.Write(Key, s).RequireAccepted();
|
||||
}
|
||||
}
|
||||
@@ -1,741 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
public class EventLuckMagicModel : IHasChainPack, ILuckyTaskGetData
|
||||
{
|
||||
private int _eventId, _ticketCount, _chainProgress, _roundCount;
|
||||
|
||||
/// <summary>
|
||||
/// Position of selection GameObject, or null if hidden.
|
||||
/// </summary>
|
||||
public Vector2? SelectionState;
|
||||
private readonly List<EventLuckMagicRing> _rings = new List<EventLuckMagicRing>();
|
||||
public EventLuckMagicRing CurrentRing { get; set; }
|
||||
public EventLuckMagicRing OuterRing => _rings[0];
|
||||
public EventLuckMagicRing MiddleRing => _rings[1];
|
||||
public EventLuckMagicRing InnerRing => _rings[2];
|
||||
public EventLuckMagicHigherRingData HigherRingData;
|
||||
public Dictionary<EEventLuckMagicRingType, EventLuckMagicRing> RingMap;
|
||||
public int TicketCount => _ticketCount;
|
||||
public Dictionary<int, int> TaskProgress;
|
||||
public const int TaskProgressCount = 3;
|
||||
public bool IsGameDepleted => _rings.All(x => x.IsDepleted);
|
||||
public bool DoesSkipAnimation = false;
|
||||
private DateTime _expiryTime, _startTime;
|
||||
public TimeSpan RemainingTime => _expiryTime - ZZTimeHelper.UtcNow();
|
||||
public bool IsActive => RemainingTime.TotalSeconds > 0;
|
||||
public int EventId => _eventId;
|
||||
public int RoundCount => _roundCount;
|
||||
public DateTime ExpiryTime => _expiryTime;
|
||||
|
||||
public EventLuckMagicParamsCtrl ParamsCtrl;
|
||||
|
||||
public void InitNew(int eventId)
|
||||
{
|
||||
float inflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
_eventId = eventId;
|
||||
_rings.Clear();
|
||||
EventLuckMagicAct.TableContext.GetRingsInfo(out int[] costs, out int[] roadSignIds);
|
||||
for (int i = 0; i < costs.Length; i++)
|
||||
_rings.Add(EventLuckMagicRing.CreateEmpty(costs[i], roadSignIds[i], (EEventLuckMagicRingType)(i + 1)));
|
||||
EventLuckMagicAct.TableContext.GetBlocksInfo(out var infoList, inflationRate);
|
||||
RingMap = new Dictionary<EEventLuckMagicRingType, EventLuckMagicRing>()
|
||||
{
|
||||
{EEventLuckMagicRingType.OuterRing, OuterRing},
|
||||
{EEventLuckMagicRingType.MiddleRing, MiddleRing},
|
||||
{EEventLuckMagicRingType.InnerRing, InnerRing}
|
||||
};
|
||||
foreach (var info in infoList)
|
||||
{
|
||||
RingMap[info.RingType].AddBlock(new EventLuckMagicBlock(info, RingMap[info.RingType]));
|
||||
}
|
||||
RingMap[EEventLuckMagicRingType.OuterRing].ImportBitWiseBlockState(0);
|
||||
RingMap[EEventLuckMagicRingType.MiddleRing].ImportBitWiseBlockState(0);
|
||||
RingMap[EEventLuckMagicRingType.InnerRing].ImportBitWiseBlockState(0);
|
||||
|
||||
HigherRingData = EventLuckMagicHigherRingData.CreateEmpty();
|
||||
LinkRings();
|
||||
|
||||
_ticketCount = EventLuckMagicAct.TableContext.GetWelcomeGift();
|
||||
_chainProgress = 0;
|
||||
|
||||
TaskProgress = GetTaskProgress(inflationRate);
|
||||
CurrentRing = _rings.FirstOrDefault(r => !r.IsDepleted) ?? _rings.Last();
|
||||
SelectionState = null;
|
||||
DoesSkipAnimation = false;
|
||||
(_startTime, _expiryTime) = EventLuckMagicAct.TableContext.GetEventStartTimeAndEndTime();
|
||||
_roundCount = 0;
|
||||
|
||||
_taskData?.Dispose();
|
||||
_dicTasks = new Dictionary<int, int>();
|
||||
_taskData = new LuckyTaskData(this);
|
||||
_taskData.InitTasks();
|
||||
}
|
||||
|
||||
public void Load(EventLuckMagicPlayfabData pfData, EventLuckMagicPlayerPreferenceData ppData)
|
||||
{
|
||||
_eventId = pfData.EventId;
|
||||
_dicTasks = pfData.DicTasks;
|
||||
_taskData = new LuckyTaskData(this);
|
||||
float inflationRate = _dicTasks.GetValueOrDefault(-3, 0) / 100f;
|
||||
EventLuckMagicAct.TableContext.GetRingsInfo(out int[] costs, out int[] roadSignIds);
|
||||
for (int i = 0; i < costs.Length; i++)
|
||||
_rings.Add(EventLuckMagicRing.CreateEmpty(costs[i], roadSignIds[i], (EEventLuckMagicRingType)(i + 1)));
|
||||
EventLuckMagicAct.TableContext.GetBlocksInfo(out var infoList, inflationRate);
|
||||
RingMap = new Dictionary<EEventLuckMagicRingType, EventLuckMagicRing>()
|
||||
{
|
||||
{EEventLuckMagicRingType.OuterRing, OuterRing},
|
||||
{EEventLuckMagicRingType.MiddleRing, MiddleRing},
|
||||
{EEventLuckMagicRingType.InnerRing, InnerRing}
|
||||
};
|
||||
foreach (var info in infoList)
|
||||
{
|
||||
RingMap[info.RingType].AddBlock(new EventLuckMagicBlock(info, RingMap[info.RingType]));
|
||||
}
|
||||
RingMap[EEventLuckMagicRingType.OuterRing].ImportBitWiseBlockState(pfData.OuterState);
|
||||
RingMap[EEventLuckMagicRingType.MiddleRing].ImportBitWiseBlockState(pfData.MiddleState);
|
||||
RingMap[EEventLuckMagicRingType.InnerRing].ImportBitWiseBlockState(pfData.InnerState);
|
||||
|
||||
HigherRingData = EventLuckMagicHigherRingData.Create(RingMap[pfData.HigherRingType], pfData.HigherRingExpireTime.Value);
|
||||
LinkRings();
|
||||
|
||||
_ticketCount = pfData.TicketCount;
|
||||
_chainProgress = pfData.ChainProgress;
|
||||
_roundCount = pfData.RoundCount;
|
||||
|
||||
TaskProgress = GetTaskProgress(inflationRate);
|
||||
CurrentRing = _rings.FirstOrDefault(r => !r.IsDepleted) ?? _rings.Last();
|
||||
ppData ??= EventLuckMagicPlayerPreferenceData.CreateEmpty();
|
||||
SelectionState = ppData.SelectionPosition;
|
||||
DoesSkipAnimation = ppData.DoesSkipAnimation;
|
||||
(_startTime, _expiryTime) = EventLuckMagicAct.TableContext.GetEventStartTimeAndEndTime();
|
||||
SetRedPoint();
|
||||
}
|
||||
void SetRedPoint()
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState(EventLuckMagicEntranceButton.RedPointKey,
|
||||
TicketCount > 0, TicketCount + _taskData.RedCount);
|
||||
}
|
||||
public void AddTicket(int num)
|
||||
{
|
||||
if (_ticketCount + num < 0)
|
||||
{
|
||||
Debug.Log($"[EventLuckMagic] Try to add {num} ticket(s) failed. Having {_ticketCount} ticket(s) now.");
|
||||
return;
|
||||
}
|
||||
_ticketCount += num;
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(_eventId, _ticketCount);
|
||||
ToPfData().Save();
|
||||
EventLuckMagicAct.EventAggregator.Publish(new EventLuckMagicTicketChange());
|
||||
SetRedPoint();
|
||||
}
|
||||
|
||||
public void LinkRings()
|
||||
{
|
||||
for (int i = 0; i < _rings.Count - 1; i++)
|
||||
_rings[i].HigherRing = _rings[i + 1];
|
||||
CurrentRing = _rings.FirstOrDefault(r => !r.IsDepleted);
|
||||
if (CurrentRing == null)
|
||||
{
|
||||
Debug.Log($"[EventLuckMagic] Cannot find any available ring. Seems that all rings are depleted.");
|
||||
return;
|
||||
}
|
||||
var r = CurrentRing;
|
||||
// The Higher ring of r is the next ring that is not depleted.
|
||||
while (r != null && r.HigherRing != null)
|
||||
{
|
||||
while (r.HigherRing != null && r.HigherRing.IsDepleted)
|
||||
{
|
||||
r.HigherRing = r.HigherRing.HigherRing;
|
||||
}
|
||||
r = r.HigherRing;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateTaskProgress(int targetId)
|
||||
{
|
||||
TaskProgress[targetId]++;
|
||||
// Debug.Log($"[EventLuckMagic] Task progress of {targetId} is {TaskProgress[targetId]}.");
|
||||
ToPfData().Save();
|
||||
}
|
||||
|
||||
public bool IsTaskCompleted(int targetId)
|
||||
{
|
||||
return TaskProgress[targetId] >= TaskProgressCount;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
for (int i = 0; i < _rings.Count; i++)
|
||||
_rings[i].Reset();
|
||||
HigherRingData.SetEmpty();
|
||||
LinkRings();
|
||||
HigherRingData.PointTo(CurrentRing.HigherRing);
|
||||
HigherRingData.ExpireTime = System.DateTime.MinValue;
|
||||
int inflationRate = DicTasks.GetValueOrDefault(-3, 0);
|
||||
float inflate = inflationRate / 100f;
|
||||
|
||||
TaskProgress = EventLuckMagicAct.TableContext.GetTaskInfo(GContext.container.Resolve<EventLuckMagicModel>().RoundCount, inflate)
|
||||
.Keys.ToDictionary(x => x, x => 0);
|
||||
CurrentRing = _rings.FirstOrDefault(r => !r.IsDepleted) ?? _rings.Last();
|
||||
|
||||
_taskData.RefreshTasks();
|
||||
|
||||
ToPfData().Save();
|
||||
}
|
||||
|
||||
public void SetChainProgress(int p)
|
||||
{
|
||||
Debug.Log($"[EventLuckMagic]SetProgress {p}");
|
||||
_chainProgress = p;
|
||||
}
|
||||
|
||||
public int GetChainProgress()
|
||||
{
|
||||
return _chainProgress;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
ToPfData().Save();
|
||||
}
|
||||
|
||||
public EventLuckMagicPlayerPreferenceData ToPpData()
|
||||
{
|
||||
return new EventLuckMagicPlayerPreferenceData
|
||||
{
|
||||
SelectionPosition = SelectionState,
|
||||
DoesSkipAnimation = DoesSkipAnimation
|
||||
};
|
||||
}
|
||||
|
||||
public EventLuckMagicPlayfabData ToPfData()
|
||||
{
|
||||
var expireTime = HigherRingData.DoesExist ? HigherRingData.ExpireTime : DateTime.MinValue;
|
||||
// var expireTime = HigherRingData.DoesExist ? HigherRingData.ExpireTime : ZZTimeHelper.UtcNow().AddSeconds(-1);
|
||||
var ringType = HigherRingData.DoesExist ? HigherRingData.Ring.RingType : EEventLuckMagicRingType.OuterRing;
|
||||
return new EventLuckMagicPlayfabData
|
||||
{
|
||||
EventId = _eventId,
|
||||
TicketCount = TicketCount,
|
||||
ChainProgress = _chainProgress,
|
||||
InnerState = InnerRing.GetBitWiseBlockState(),
|
||||
MiddleState = MiddleRing.GetBitWiseBlockState(),
|
||||
OuterState = OuterRing.GetBitWiseBlockState(),
|
||||
HigherRingExpireTime = expireTime,
|
||||
HigherRingType = ringType,
|
||||
RoundCount = _roundCount,
|
||||
DicTasks = _dicTasks
|
||||
};
|
||||
}
|
||||
|
||||
private Dictionary<int, int> GetTaskProgress(float inflate)
|
||||
{
|
||||
Debug.Log($"[EventLuckMagic] GetTaskProgress with inflation {inflate}");
|
||||
var taskInfo = EventLuckMagicAct.TableContext.GetTaskInfo(RoundCount, inflate);
|
||||
var res = taskInfo.ToDictionary(
|
||||
kv => kv.Key,
|
||||
kv => _rings.Sum(ring => ring.Blocks.Count(x => x.Reward.id == kv.Key && x.IsTaken))
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
public EventLuckMagicEntranceData ToEntranceData()
|
||||
{
|
||||
return new EventLuckMagicEntranceData
|
||||
{
|
||||
TicketCount = _ticketCount,
|
||||
ExpiryTime = _expiryTime,
|
||||
Icon = EventLuckMagicAct.TableContext.GetIcon(),
|
||||
StartTime = _startTime,
|
||||
};
|
||||
}
|
||||
|
||||
public GenericChainPackData<EventLuckMagicModel> ToChainPackData()
|
||||
{
|
||||
return GenericChainPackData<EventLuckMagicModel>.Create(
|
||||
this, EventLuckMagicAct.TableContext.GetChainPackInfo());
|
||||
}
|
||||
|
||||
public void AddRoundCount()
|
||||
{
|
||||
_roundCount++;
|
||||
Save();
|
||||
}
|
||||
|
||||
public void SaveTaskData()
|
||||
{
|
||||
SetRedPoint();
|
||||
ToPfData().Save();
|
||||
}
|
||||
|
||||
|
||||
#region Lucky Task
|
||||
private Dictionary<int, int> _dicTasks;
|
||||
private LuckyTaskData _taskData;
|
||||
public string LuckyTaskRedPointKey => "eventluckmagic.entrance.luckytask";
|
||||
public Dictionary<int, int> DicTasks { get => _dicTasks; set => _dicTasks = value; }
|
||||
public string TaskPanelName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (DicTasks.GetValueOrDefault(-4, 0) == 1)
|
||||
{
|
||||
return "EventLuckyMagicADTaskPopupPanel";
|
||||
}
|
||||
return "EventLuckyMagicTaskPopupPanel";
|
||||
}
|
||||
}
|
||||
public bool OnGetTaskReward(int id)
|
||||
{
|
||||
return _taskData.OnGetTaskReward(id);
|
||||
}
|
||||
|
||||
public int GetLuckyPackID()
|
||||
{
|
||||
return EventLuckMagicAct.TableContext.GetPackId();
|
||||
}
|
||||
|
||||
public void DisposeLuckyTaskSubscription()
|
||||
{
|
||||
_taskData.Dispose();
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class EventLuckMagicRing
|
||||
{
|
||||
public readonly int Cost, RoadSignId;
|
||||
public List<EventLuckMagicBlock> Blocks;
|
||||
public bool IsDepleted => Blocks.All(x => x.IsTaken || EventLuckMagicAct.TableContext.IsRewardRoadSign(x.Reward.id, out _, out _, out _));
|
||||
public readonly EEventLuckMagicRingType RingType;
|
||||
public EventLuckMagicRing HigherRing { get; set; }
|
||||
public int[] CornerIndices = new int[4];
|
||||
|
||||
private int BlockCount => Blocks.Count;
|
||||
|
||||
public int AvailableBlockCount => Blocks.Count(x => !x.IsTaken);
|
||||
|
||||
private EventLuckMagicRing(int cost, int roadSignId, EEventLuckMagicRingType ringType)
|
||||
{
|
||||
Blocks = new List<EventLuckMagicBlock>();
|
||||
Cost = cost;
|
||||
RoadSignId = roadSignId;
|
||||
RingType = ringType;
|
||||
HigherRing = null;
|
||||
}
|
||||
|
||||
public static EventLuckMagicRing CreateEmpty(int cost, int roadSignId, EEventLuckMagicRingType ringType)
|
||||
{
|
||||
return new EventLuckMagicRing(cost, roadSignId, ringType);
|
||||
}
|
||||
|
||||
public void AddBlock(EventLuckMagicBlock block)
|
||||
{
|
||||
Blocks.Add(block);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Blocks.ForEach(x => x.Reset());
|
||||
}
|
||||
|
||||
public void GetLoopAnimationInfo(int targetIdx, int extraLoopCount, out int startIdx, out int totalMovementCount)
|
||||
{
|
||||
int idx = 0, gap = BlockCount / 4;
|
||||
startIdx = 0;
|
||||
while (idx < BlockCount)
|
||||
{
|
||||
idx += gap;
|
||||
if (GetShortestDistance(idx, targetIdx) > GetShortestDistance(startIdx, targetIdx))
|
||||
startIdx = idx;
|
||||
}
|
||||
totalMovementCount = GetClockWiseDistance(startIdx, targetIdx) + extraLoopCount * BlockCount;
|
||||
}
|
||||
|
||||
private int GetClockWiseDistance(int startIdx, int endIdx)
|
||||
{
|
||||
Assert.IsTrue(startIdx >= 0 && startIdx < BlockCount && endIdx >= 0 && endIdx < BlockCount);
|
||||
return (endIdx - startIdx + BlockCount) % BlockCount;
|
||||
}
|
||||
|
||||
private int GetShortestDistance(int startIdx, int endIdx)
|
||||
{
|
||||
int d = Mathf.Abs(startIdx - endIdx);
|
||||
d = d > BlockCount / 2 ? BlockCount - d : d;
|
||||
return d;
|
||||
}
|
||||
|
||||
public int GetBitWiseBlockState()
|
||||
{
|
||||
var res = 0;
|
||||
for (int i = 0; i < BlockCount; i++)
|
||||
{
|
||||
if (Blocks[i].IsTaken)
|
||||
res |= 1 << i;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public void ImportBitWiseBlockState(int state)
|
||||
{
|
||||
for (int i = 0; i < BlockCount; i++)
|
||||
{
|
||||
if ((state & (1 << i)) != 0)
|
||||
Blocks[i].Take();
|
||||
else
|
||||
Blocks[i].Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class EventLuckMagicBlock
|
||||
{
|
||||
public int TableId;
|
||||
public readonly ItemData Reward;
|
||||
private bool _isTaken;
|
||||
public bool IsTaken
|
||||
{
|
||||
get
|
||||
{
|
||||
if (EventLuckMagicAct.TableContext.IsRewardRoadSign(Reward.id, out _, out _, out _))
|
||||
return ParentRing == null || ParentRing.HigherRing == null || ParentRing.HigherRing.IsDepleted;
|
||||
return _isTaken;
|
||||
}
|
||||
}
|
||||
|
||||
private int _weight;
|
||||
public int Weight
|
||||
{
|
||||
get
|
||||
{
|
||||
if (EventLuckMagicAct.TableContext.IsRewardRoadSign(Reward.id, out _, out _, out var weightConfig))
|
||||
{
|
||||
if (weightConfig.Length != 2)
|
||||
return _weight;
|
||||
return ParentRing.AvailableBlockCount <= weightConfig[0] ? weightConfig[1] : _weight;
|
||||
}
|
||||
return _weight;
|
||||
}
|
||||
set
|
||||
{
|
||||
_weight = value;
|
||||
}
|
||||
}
|
||||
public EEventLuckMagicBlockType BlockType;
|
||||
/// <summary>
|
||||
/// The ring this block belongs to.
|
||||
/// </summary>
|
||||
public EventLuckMagicRing ParentRing { get; }
|
||||
|
||||
public EventLuckMagicBlock(EventLuckMagicBlockInfo info, EventLuckMagicRing parentRing)
|
||||
{
|
||||
Reward = info.Reward;
|
||||
Weight = info.Weight;
|
||||
BlockType = GetBlockType(info.RingType, info.Reward.id);
|
||||
ParentRing = parentRing;
|
||||
TableId = info.TableId;
|
||||
// _isTaken = isTaken;
|
||||
}
|
||||
|
||||
public void Take()
|
||||
{
|
||||
_isTaken = true;
|
||||
}
|
||||
|
||||
private static EEventLuckMagicBlockType GetBlockType(EEventLuckMagicRingType ringType, int rewardId)
|
||||
{
|
||||
if (EventLuckMagicAct.TableContext.IsRewardRoadSign(ringType, rewardId))
|
||||
return EEventLuckMagicBlockType.RoadSign;
|
||||
else
|
||||
return (EEventLuckMagicBlockType)(ringType - 1);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_isTaken = false;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventLuckMagicHigherRingData
|
||||
{
|
||||
public EventLuckMagicRing Ring { get; set; }
|
||||
public System.DateTime ExpireTime;
|
||||
public System.TimeSpan RemainingTime => ExpireTime - ZZTimeHelper.UtcNow();
|
||||
public bool DoesExist => Ring != null && !Ring.IsDepleted && RemainingTime.TotalSeconds > 0;
|
||||
|
||||
private EventLuckMagicHigherRingData() { }
|
||||
|
||||
public void PointTo(EventLuckMagicRing ring)
|
||||
{
|
||||
Ring = ring;
|
||||
}
|
||||
|
||||
public void Activate(int expireDuration)
|
||||
{
|
||||
ExpireTime = ZZTimeHelper.UtcNow().AddSeconds(expireDuration);
|
||||
}
|
||||
|
||||
public void SetEmpty()
|
||||
{
|
||||
Ring = null;
|
||||
ExpireTime = ZZTimeHelper.UtcNow().AddSeconds(-5);
|
||||
}
|
||||
|
||||
public static EventLuckMagicHigherRingData CreateEmpty()
|
||||
{
|
||||
var res = new EventLuckMagicHigherRingData();
|
||||
res.SetEmpty();
|
||||
return res;
|
||||
}
|
||||
|
||||
public static EventLuckMagicHigherRingData Create(EventLuckMagicRing ring, System.DateTime? expireTime)
|
||||
{
|
||||
var res = new EventLuckMagicHigherRingData();
|
||||
res.PointTo(ring);
|
||||
if (expireTime != null)
|
||||
res.ExpireTime = expireTime.Value;
|
||||
else
|
||||
res.ExpireTime = ZZTimeHelper.UtcNow().AddSeconds(-4);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventLuckMagicPanelData
|
||||
{
|
||||
public EventLuckMagicTableContext TableContext;
|
||||
public EventLuckMagicModel Model;
|
||||
public EventLuckMagicSystem System;
|
||||
public EventLuckMagicParamsCtrl ParamsCtrl;
|
||||
public IEventAggregator EventAggregator;
|
||||
public Vector2? SelectionPosition;
|
||||
}
|
||||
|
||||
public class EventLuckMagicSpinParams
|
||||
{
|
||||
public float MaxInterval;
|
||||
public float MinInterval;
|
||||
public int ExtraLoopCount;
|
||||
public AnimationCurve IntervalLerpCurve;
|
||||
}
|
||||
|
||||
public class EventLuckMagicSpinCtx
|
||||
{
|
||||
public EventLuckMagicRing Ring;
|
||||
public int Index;
|
||||
public ItemData Reward;
|
||||
public bool doesHideButton;
|
||||
public int RoundCount;
|
||||
}
|
||||
|
||||
public class RewardFlyToStashRequest
|
||||
{
|
||||
public ItemData Reward;
|
||||
public bool DoesPlayOpen;
|
||||
// public Vector2 Position;
|
||||
// public float SourceIconSize;
|
||||
public RectTransform SourceIconRt;
|
||||
public RectTransform SourceTextRt;
|
||||
public RewardFlyToStashRequest(ItemData reward, RectTransform sourceIconRt,
|
||||
bool doesPlayOpen = true, RectTransform sourceTextRt = null)
|
||||
{
|
||||
Reward = reward;
|
||||
DoesPlayOpen = doesPlayOpen;
|
||||
SourceIconRt = sourceIconRt;
|
||||
SourceTextRt = sourceTextRt;
|
||||
}
|
||||
}
|
||||
|
||||
public class RewardItemNewFlyToStashRequest
|
||||
{
|
||||
public RewardItemNew Source;
|
||||
}
|
||||
|
||||
public class EventLuckMagicPlayfabData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public int EventId;
|
||||
public int TicketCount;
|
||||
public int ChainProgress;
|
||||
public int InnerState, MiddleState, OuterState;
|
||||
public int RoundCount;
|
||||
public System.DateTime? HigherRingExpireTime;
|
||||
public EEventLuckMagicRingType HigherRingType;
|
||||
public Dictionary<int, int> DicTasks;
|
||||
|
||||
public const string Key = "EventLuckMagicPlayfabData";
|
||||
private const string Splitter = "|", Comma = ",";
|
||||
private const int Count = 11;
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(EventId).Append(Splitter);
|
||||
sb.Append(TicketCount).Append(Splitter);
|
||||
sb.Append(ChainProgress).Append(Splitter);
|
||||
sb.Append(InnerState).Append(Splitter);
|
||||
sb.Append(MiddleState).Append(Splitter);
|
||||
sb.Append(OuterState).Append(Splitter);
|
||||
sb.Append(HigherRingExpireTime).Append(Splitter);
|
||||
sb.Append((int)HigherRingType).Append(Splitter);
|
||||
sb.Append(RoundCount).Append(Splitter);
|
||||
if (DicTasks != null)
|
||||
{
|
||||
foreach (var kv in DicTasks)
|
||||
sb.Append(kv.Key).Append(Comma);
|
||||
sb.Append(Splitter);
|
||||
foreach (var kv in DicTasks)
|
||||
sb.Append(kv.Value).Append(Comma);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static EventLuckMagicPlayfabData Deserialize(string s)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (s == null || s == "")
|
||||
return null;
|
||||
var tokens = s.Trim(Splitter.ToCharArray()).Split(Splitter);
|
||||
if (tokens.Length != Count)
|
||||
{
|
||||
var e = new Exception($"[EventLuckMagic]Wrong amount of parameters. Expect {Count}, but got {tokens.Length} in \"{s}\".");
|
||||
Debug.LogError(e);
|
||||
return null;
|
||||
}
|
||||
// Debug.Log($"[EventLuckMagic] time string:{tokens[6]}");
|
||||
var keyList = tokens[9].Trim(Comma[0]).Split(Comma).Select(int.Parse).ToList();
|
||||
var valueList = tokens[10].Trim(Comma[0]).Split(Comma).Select(int.Parse).ToList();
|
||||
if (keyList == null || valueList == null || keyList.Count != valueList.Count)
|
||||
{
|
||||
var e = new Exception($"[EventLuckMagic]Dictionary Error.");
|
||||
Debug.LogError(e);
|
||||
return null;
|
||||
}
|
||||
var dicTasks = new Dictionary<int, int>();
|
||||
for (int i = 0; i < keyList.Count; i++)
|
||||
dicTasks.Add(keyList[i], valueList[i]);
|
||||
return new EventLuckMagicPlayfabData
|
||||
{
|
||||
EventId = int.Parse(tokens[0]),
|
||||
TicketCount = int.Parse(tokens[1]),
|
||||
ChainProgress = int.Parse(tokens[2]),
|
||||
InnerState = int.Parse(tokens[3]),
|
||||
MiddleState = int.Parse(tokens[4]),
|
||||
OuterState = int.Parse(tokens[5]),
|
||||
HigherRingExpireTime = ParseTime(tokens[6]),
|
||||
HigherRingType = (EEventLuckMagicRingType)int.Parse(tokens[7]),
|
||||
RoundCount = int.Parse(tokens[8]),
|
||||
DicTasks = dicTasks
|
||||
};
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Log($"[EventLuckMagic] Deserialize error: ");
|
||||
Debug.LogError(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static System.DateTime ParseTime(string timeString)
|
||||
{
|
||||
if (timeString == "")
|
||||
return System.DateTime.MinValue;
|
||||
else
|
||||
try
|
||||
{
|
||||
return System.DateTime.Parse(timeString);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Debug.LogError($"[EventLuckMagic] Failed to parse time string \"{timeString}\"");
|
||||
return System.DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var s = Serialize();
|
||||
// Debug.Log($"[EventLuckMagic] Save playfab data : \n" + s);
|
||||
saveStore.Write(Key, s).RequireAccepted();
|
||||
}
|
||||
}
|
||||
|
||||
public class EventLuckMagicPlayerPreferenceData
|
||||
{
|
||||
public Vector2? SelectionPosition;
|
||||
public bool DoesSkipAnimation;
|
||||
public static string Key => "EventLuckMagic" + GContext.container.Resolve<IUserService>().UserId;
|
||||
private const char Splitter = '|', Comma = ',';
|
||||
|
||||
public static EventLuckMagicPlayerPreferenceData CreateEmpty()
|
||||
{
|
||||
var res = new EventLuckMagicPlayerPreferenceData();
|
||||
res.SelectionPosition = null;
|
||||
res.DoesSkipAnimation = false;
|
||||
return res;
|
||||
}
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(DoesSkipAnimation).Append(Splitter);
|
||||
if (SelectionPosition != null)
|
||||
sb.Append(SelectionPosition.Value.x).Append(Comma).Append(SelectionPosition.Value.y).Append(Splitter);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static EventLuckMagicPlayerPreferenceData Deserialize(string s)
|
||||
{
|
||||
var res = new EventLuckMagicPlayerPreferenceData();
|
||||
if (s == null || s == "")
|
||||
return res;
|
||||
try
|
||||
{
|
||||
var tokens = s.Trim(Splitter).Split(Splitter);
|
||||
res.DoesSkipAnimation = bool.Parse(tokens[0]);
|
||||
if (tokens.Length > 1)
|
||||
{
|
||||
var posTokens = tokens[1].Split(Comma);
|
||||
res.SelectionPosition = new Vector2(float.Parse(posTokens[0]), float.Parse(posTokens[1]));
|
||||
}
|
||||
else
|
||||
res.SelectionPosition = null;
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventLuckMagic] Failed to deserialize:");
|
||||
Debug.LogError(e);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var s = Serialize();
|
||||
PlayerPrefs.SetString(Key, s);
|
||||
}
|
||||
}
|
||||
|
||||
public class EventLuckMagicEntranceData
|
||||
{
|
||||
public int TicketCount;
|
||||
public DateTime ExpiryTime;
|
||||
public DateTime StartTime;
|
||||
public TimeSpan RemainingTime => ExpiryTime - ZZTimeHelper.UtcNow();
|
||||
public bool IsActive => ZZTimeHelper.UtcNow() >= StartTime && ZZTimeHelper.UtcNow() < ExpiryTime;
|
||||
public string Icon;
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
public class WelcomeBackData
|
||||
{
|
||||
public string lastLoginTime;
|
||||
public string openTime;
|
||||
public string rewardTime;
|
||||
public int received;
|
||||
public int eventCount;
|
||||
public int offlineDays;
|
||||
}
|
||||
public class EventWelcomeBackManager
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
string DataKey_WelcomeBack = "WelcomeBack";
|
||||
Tables tables;
|
||||
public TbEventWelcomeBackConfig eventWelcomeBackConfig;
|
||||
public WelcomeBackData welcomeBackData;
|
||||
public EventWelcomeBackManager()
|
||||
{
|
||||
tables = GContext.container.Resolve<Tables>();
|
||||
eventWelcomeBackConfig = tables.TbEventWelcomeBackConfig;
|
||||
}
|
||||
public WelcomeBackData LoadData()
|
||||
{
|
||||
try
|
||||
{
|
||||
string data = saveStore.Read(DataKey_WelcomeBack).ValueOrThrow();
|
||||
if (data != null)
|
||||
{
|
||||
welcomeBackData = Newtonsoft.Json.JsonConvert.DeserializeObject<WelcomeBackData>(data) ?? throw new Newtonsoft.Json.JsonSerializationException("Null welcome-back save.");
|
||||
}
|
||||
else
|
||||
{
|
||||
welcomeBackData = new WelcomeBackData();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"LoadData failed: {e}");
|
||||
throw;
|
||||
}
|
||||
|
||||
return welcomeBackData;
|
||||
}
|
||||
|
||||
public int GetState(int index, EventWelcomeBackTimeReward timeReward)
|
||||
{
|
||||
if (index < welcomeBackData.received)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
// 仅用于本方法内计算,不修改 welcomeBackData,避免 GetState 产生副作用
|
||||
string baseTimeStr = welcomeBackData.received < 7 ? welcomeBackData.openTime : welcomeBackData.rewardTime;
|
||||
if (string.IsNullOrEmpty(baseTimeStr))
|
||||
return 0;
|
||||
System.DateTime curTime = ZZTimeHelper.UtcNow();
|
||||
System.DateTime baseTime = System.DateTime.Parse(baseTimeStr);
|
||||
return baseTime.AddMinutes(timeReward.Time) <= curTime ? 1 : 0;
|
||||
}
|
||||
|
||||
public int CheckRedPoint()
|
||||
{
|
||||
var dataList = tables.TbEventWelcomeBackTimeReward.DataList;
|
||||
int red = 0;
|
||||
int count = dataList.Count;
|
||||
if (welcomeBackData.received < 7)
|
||||
{
|
||||
count = Math.Min(7, dataList.Count);
|
||||
}
|
||||
for (int i = welcomeBackData.received; i < count; i++)
|
||||
{
|
||||
int state = GetState(i, dataList[i]);
|
||||
if (state == 1)
|
||||
{
|
||||
red++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return red;
|
||||
}
|
||||
|
||||
public List<int> ClaimReward()
|
||||
{
|
||||
var dataList = tables.TbEventWelcomeBackTimeReward.DataList;
|
||||
List<int> dropID = new List<int>();
|
||||
int state = 0;
|
||||
int count = dataList.Count;
|
||||
if (welcomeBackData.received < 7)
|
||||
{
|
||||
count = Math.Min(7, dataList.Count);
|
||||
}
|
||||
int curIndex = welcomeBackData.received;
|
||||
for (int i = welcomeBackData.received; i < count; i++)
|
||||
{
|
||||
state = GetState(i, dataList[i]);
|
||||
if (state == 1)
|
||||
{
|
||||
dropID.Add(dataList[i].DropID);
|
||||
welcomeBackData.received = i + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dropID.Count > 0)
|
||||
{
|
||||
if (curIndex < 7 && welcomeBackData.received >= 7)
|
||||
{
|
||||
welcomeBackData.rewardTime = ZZTimeHelper.UtcNow().ToString();
|
||||
}
|
||||
GContext.container.Resolve<PlayerItemData>().AddItemByDropListLureInflation(null, dropID, null, false);
|
||||
SaveData();
|
||||
return dropID;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public System.DateTime GetCurrentRoundEndTime()
|
||||
{
|
||||
System.DateTime baseTime;
|
||||
int windowSeconds;
|
||||
if (welcomeBackData.received < 7)
|
||||
{
|
||||
baseTime = System.DateTime.Parse(welcomeBackData.openTime);
|
||||
windowSeconds = eventWelcomeBackConfig.WelcomeBackTimeRewardWindow[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
baseTime = System.DateTime.Parse(welcomeBackData.rewardTime);
|
||||
windowSeconds = eventWelcomeBackConfig.WelcomeBackTimeRewardWindow[1];
|
||||
}
|
||||
return baseTime.AddSeconds(windowSeconds);
|
||||
}
|
||||
|
||||
public EventWelcomeBackTimeReward GetCurRefreshTime()
|
||||
{
|
||||
if (welcomeBackData.received < tables.TbEventWelcomeBackTimeReward.DataList.Count)
|
||||
{
|
||||
return tables.TbEventWelcomeBackTimeReward.DataList[welcomeBackData.received];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool CheckWelcomeBack()
|
||||
{
|
||||
if (GContext.container.Resolve<PlayerData>().lv < eventWelcomeBackConfig.WelcomeBackConditionLevel)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
LoadData();
|
||||
|
||||
if (string.IsNullOrEmpty(welcomeBackData.lastLoginTime))
|
||||
{
|
||||
SetLastLoginTime();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
System.DateTime lastTime = System.DateTime.Parse(welcomeBackData.lastLoginTime);
|
||||
System.DateTime curTime = ZZTimeHelper.UtcNow();
|
||||
|
||||
if (!string.IsNullOrEmpty(welcomeBackData.openTime))
|
||||
{
|
||||
System.DateTime openTime = System.DateTime.Parse(welcomeBackData.openTime);
|
||||
var offlineTime = curTime - openTime;
|
||||
if (offlineTime.TotalSeconds >= eventWelcomeBackConfig.WelcomeBackCD)
|
||||
{
|
||||
welcomeBackData.openTime = string.Empty;// 超过回归活动CD时间,重置数据
|
||||
SaveData();
|
||||
// 不 return,继续往下判断能否开启新一轮(依据 lastLogin 离线时间)
|
||||
}
|
||||
else if (curTime >= GetCurrentRoundEndTime()
|
||||
|| welcomeBackData.received >= tables.TbEventWelcomeBackTimeReward.DataList.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrEmpty(welcomeBackData.openTime))
|
||||
{
|
||||
var offlineTime = curTime - lastTime;
|
||||
if (offlineTime.TotalSeconds >= eventWelcomeBackConfig.WelcomeBackConditionTime)
|
||||
{
|
||||
welcomeBackData.openTime = curTime.ToString();
|
||||
welcomeBackData.rewardTime = curTime.ToString();
|
||||
welcomeBackData.received = 0;
|
||||
welcomeBackData.eventCount++;
|
||||
welcomeBackData.offlineDays = (int)offlineTime.TotalDays;
|
||||
SetLastLoginTime();
|
||||
GContext.container.Resolve<IFaceUIService>().AddFaceCustomUIData(new UIType("ComebackGiftPopupPanel"));
|
||||
GContext.container.Resolve<IFaceUIService>().AddFaceCustomUIData(new UIType("ComebackChainsPopupPanel"));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
SetLastLoginTime();
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetLastLoginTime()
|
||||
{
|
||||
if (welcomeBackData == null)
|
||||
{
|
||||
welcomeBackData = new WelcomeBackData();
|
||||
}
|
||||
welcomeBackData.lastLoginTime = ZZTimeHelper.UtcNow().ToString();
|
||||
SaveData();
|
||||
}
|
||||
|
||||
void SaveData()
|
||||
{
|
||||
saveStore.Write(DataKey_WelcomeBack, Newtonsoft.Json.JsonConvert.SerializeObject(welcomeBackData)).RequireAccepted();
|
||||
}
|
||||
}
|
||||
public struct ClaimComeBack
|
||||
{
|
||||
public int state;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,88 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class InfiniteBuildingData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public int Lvl, Step, StepLvl = 1, LastHangingBlock = -1;
|
||||
public List<BlockRecord> Record = new List<BlockRecord>();
|
||||
// private readonly TbConstructionUnits BlocksTable = GContext.container.Resolve<Tables>().TbConstructionUnits;
|
||||
public int ConstructPrice => (int)
|
||||
(GContext.container.Resolve<Tables>().TbMapData.DataList[^1].ConstrutionCost
|
||||
* GContext.container.Resolve<Tables>().TbConstructionInfiniteConfig.CashMagList[Step]);
|
||||
public struct BlockRecord
|
||||
{
|
||||
public readonly int ID;
|
||||
public Vector2 Pos;
|
||||
public readonly float Height => GContext.container.Resolve<Tables>().TbConstructionUnits[ID].Height;
|
||||
public readonly string PrefabID => GContext.container.Resolve<Tables>().TbConstructionUnits[ID].Prefab;
|
||||
public BlockRecord(int id = 0, Vector2 pos = default)
|
||||
{
|
||||
this.ID = id;
|
||||
this.Pos = pos;
|
||||
}
|
||||
public BlockRecord(int id = 0, float x = 0, float y = 0)
|
||||
{
|
||||
this.ID = id;
|
||||
Pos = new Vector2(x, y);
|
||||
}
|
||||
public readonly override string ToString() => $"{ID},{Pos.x},{Pos.y}";
|
||||
}
|
||||
private string Serialize()
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.Append($"{Lvl},{Step},{StepLvl},{LastHangingBlock},");
|
||||
foreach (BlockRecord node in Record)
|
||||
sb.Append($"{node.ToString()},");
|
||||
return sb.ToString().Trim(',');
|
||||
}
|
||||
public void Deserialize(string s)
|
||||
{
|
||||
string[] tokens = s.Split(',');
|
||||
if (tokens == null || tokens.Length < 4 || tokens.Length % 3 != 1)
|
||||
{
|
||||
Debug.LogWarning($"string \"{s}\" cannot be deserialized.");
|
||||
return;
|
||||
}
|
||||
Lvl = int.Parse(tokens[0]);
|
||||
Step = int.Parse(tokens[1]);
|
||||
StepLvl = int.Parse(tokens[2]);
|
||||
LastHangingBlock = int.Parse(tokens[3]);
|
||||
Record = new List<BlockRecord>();
|
||||
for (int i = 4; i < tokens.Length; i+=3)
|
||||
{
|
||||
Record.Add(new BlockRecord(int.Parse(tokens[i]),
|
||||
float.Parse(tokens[i + 1]), float.Parse(tokens[i + 2])));
|
||||
//Debug.Log($"{int.Parse(tokens[i])}, {float.Parse(tokens[i + 1])}, {float.Parse(tokens[i + 2])}");
|
||||
}
|
||||
}
|
||||
public void UploadData()
|
||||
{
|
||||
string s = Serialize();
|
||||
//Debug.Log("Upload!!!!!!!!!!!!!!");
|
||||
//Debug.Log(s);
|
||||
GContext.container.Resolve<PlayerData>().InfiniteBuildingData = s;
|
||||
saveStore.Write("InfiniteBuildingData", s).RequireAccepted();
|
||||
}
|
||||
|
||||
public void DownloadData(string s)
|
||||
{
|
||||
Deserialize(s);
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
public void PrintSelf()
|
||||
{
|
||||
Debug.Log($"Lvl: {Lvl}");
|
||||
Debug.Log($"Step: {Step}");
|
||||
Debug.Log($"StepLvl: {StepLvl}");
|
||||
foreach (BlockRecord node in Record)
|
||||
{
|
||||
Debug.Log($"node: {node.PrefabID}, {node.Pos}");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1,344 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
public class LuckyADTaskPopupPanel : LuckyTaskPanelBase
|
||||
{
|
||||
game.PlayerDataStorage.IPlayerSaveStore saveStore;
|
||||
|
||||
List<LuckyTaskItemData> taskList;
|
||||
List<LuckyADTaskItem> taskItems = new List<LuckyADTaskItem>();
|
||||
List<LuckyADTaskItemData> taskADList = new List<LuckyADTaskItemData>();
|
||||
LuckyADTask luckyTaskItem;
|
||||
TMP_Text text_time;
|
||||
Timer timer;
|
||||
System.DateTime refreshTime;
|
||||
protected override void Awake()
|
||||
{
|
||||
saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
base.Awake();
|
||||
luckyTaskItem = transform.Find("root/hook_missions_object").GetComponent<LuckyADTask>();
|
||||
text_time = transform.Find("root/text_time").GetComponent<TMP_Text>();
|
||||
}
|
||||
protected override void SetItem()
|
||||
{
|
||||
taskList = new List<LuckyTaskItemData>();
|
||||
int level = taskData.GetValueOrDefault(-2, 0);
|
||||
float inflationRate = taskData.GetValueOrDefault(-3, 0) / 100f;
|
||||
foreach (var item in taskData)
|
||||
{
|
||||
if (item.Key <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
CommonTask commonTask = tables.TbCommonTask.Get(item.Key);
|
||||
if (commonTask != null)
|
||||
{
|
||||
LuckyTaskItemData luckyTaskData = new LuckyTaskItemData();
|
||||
luckyTaskData.id = item.Key;
|
||||
int dropID = commonTask.TaskReward[level];
|
||||
|
||||
luckyTaskData.reward = dropID;
|
||||
|
||||
luckyTaskData.inflationRate = inflationRate;
|
||||
string desc = LocalizationMgr.GetText(commonTask.TaskDesc_l10n_key);
|
||||
int taskParam = commonTask.TaskParam[level];
|
||||
if (commonTask.TaskType == ConditionType.ConsumeEnergy)
|
||||
{
|
||||
taskParam = GContext.container.Resolve<PlayerItemData>().IntInflation(taskParam, inflationRate);
|
||||
}
|
||||
luckyTaskData.target = taskParam;
|
||||
luckyTaskData.progress = taskParam;
|
||||
if (item.Value == -1)
|
||||
{
|
||||
luckyTaskData.state = -1;
|
||||
}
|
||||
else if (item.Value >= taskParam)
|
||||
{
|
||||
luckyTaskData.state = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
luckyTaskData.progress = item.Value;
|
||||
luckyTaskData.state = 0;
|
||||
}
|
||||
desc = desc.SafeFormat(taskParam);
|
||||
luckyTaskData.desc = desc;
|
||||
taskList.Add(luckyTaskData);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Task ID {item.Key} not found in CommonTask table.");
|
||||
}
|
||||
}
|
||||
taskList.Sort((a, b) => b.state.CompareTo(a.state)); // 按state排序
|
||||
luckyTaskItem.Init(taskList[0], OnClickLuckyTaskItem);
|
||||
SetADItem();
|
||||
}
|
||||
void SetADItem()
|
||||
{
|
||||
taskADList = SetADItemData();
|
||||
for (int i = 0; i < taskADList.Count; i++)
|
||||
{
|
||||
LuckyADTaskItem luckyTaskItem = Instantiate(itemPrefab, content).AddComponent<LuckyADTaskItem>();
|
||||
luckyTaskItem.gameObject.SetActive(true);
|
||||
luckyTaskItem.Init(taskADList[i], OnBuySuccess);
|
||||
taskItems.Add(luckyTaskItem);
|
||||
}
|
||||
//设置倒计时
|
||||
var nowTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
refreshTime = nowTime.Date.AddDays(1);
|
||||
TimeSpan now = refreshTime - ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
text_time.text = LocalizationMgr.GetFormatTextValue("UI_COMMON_refresh",
|
||||
ConvertTools.ConvertTime2(now.Days, now.Hours, now.Minutes, now.Seconds));
|
||||
double seconds = now.TotalSeconds;
|
||||
if (seconds < 1)
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
else
|
||||
{
|
||||
timer = this.AttachTimer((float)seconds, Refresh,
|
||||
(elapsed) =>
|
||||
{
|
||||
now = refreshTime - ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
text_time.text = LocalizationMgr.GetFormatTextValue("UI_COMMON_refresh",
|
||||
ConvertTools.ConvertTime2(now.Days, now.Hours, now.Minutes, now.Seconds));
|
||||
}, useRealTime: true);
|
||||
}
|
||||
}
|
||||
void Refresh()
|
||||
{
|
||||
timer?.Cancel();
|
||||
timer = null;
|
||||
UIManager.Instance.DestroyUI(gameObject.name);
|
||||
}
|
||||
List<LuckyADTaskItemData> SetADItemData()
|
||||
{
|
||||
int round = taskData.GetValueOrDefault(-1, 0);
|
||||
float inflationRate = taskData.GetValueOrDefault(-3, 0) / 100f;
|
||||
int group = taskData.GetValueOrDefault(-4, 0);
|
||||
string adData = saveStore.Read(LuckyTaskData.ADTaskADKey).ValueOrThrow();
|
||||
int index = 0;
|
||||
int curDay = ZZTimeHelper.UtcNow().UtcNowOffset().DayOfYear;
|
||||
if (!string.IsNullOrEmpty(adData))
|
||||
{
|
||||
string[] strings = adData.Split("|");
|
||||
int day = -1;
|
||||
if (strings.Length == 2 && int.TryParse(strings[1], out day) && day == curDay)
|
||||
{
|
||||
int.TryParse(strings[0], out index);
|
||||
}
|
||||
else
|
||||
{
|
||||
adData = $"{index}|{curDay}";
|
||||
saveStore.Write(LuckyTaskData.ADTaskADKey, adData).RequireAccepted();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
adData = $"{index}|{curDay}";
|
||||
saveStore.Write(LuckyTaskData.ADTaskADKey, adData).RequireAccepted();
|
||||
}
|
||||
List<LuckyADTaskItemData> taskADList = new List<LuckyADTaskItemData>();
|
||||
TbEventLuckyTask eventLuckyTask = LuckyTaskData.GetTbEventLuckyTask(luckyModel.EventId, round, group);
|
||||
var adReward = eventLuckyTask.adReward;
|
||||
for (int i = 0; i < adReward.Count; i++)
|
||||
{
|
||||
int dropID = adReward[i];
|
||||
LuckyADTaskItemData luckyTaskData = new LuckyADTaskItemData();
|
||||
luckyTaskData.reward = dropID;
|
||||
luckyTaskData.inflationRate = inflationRate;
|
||||
luckyTaskData.index = i;
|
||||
if (i < index)
|
||||
{
|
||||
luckyTaskData.state = -1;
|
||||
}
|
||||
else if (i == index)
|
||||
{
|
||||
luckyTaskData.state = 1;
|
||||
}
|
||||
taskADList.Add(luckyTaskData);
|
||||
}
|
||||
taskADList.Sort((a, b) => b.state.CompareTo(a.state)); // 按state排序
|
||||
return taskADList;
|
||||
}
|
||||
void OnBuySuccess(LuckyADTaskItem luckyADTaskItem)
|
||||
{
|
||||
canvasGroup.blocksRaycasts = false;
|
||||
//领奖
|
||||
int dropID = luckyADTaskItem.data.reward;
|
||||
|
||||
ChangeSource changeSource = new ChangeSource(EventId, "Event_Minigame3", "Event_Minigame3_LuckMagic", "TaskReward");
|
||||
if (fishingEvent.SubType == 4)
|
||||
{
|
||||
changeSource.Source_Type = "Event_Minigame3_LuckyCards";
|
||||
}
|
||||
List<ItemData> rewardItem = GContext.container.Resolve<PlayerItemData>().AddItemByDropLureInflation(luckyTaskItem.data.inflationRate, dropID, changeSource, false);
|
||||
|
||||
luckyADTaskItem.Play();
|
||||
|
||||
string adData = saveStore.Read(LuckyTaskData.ADTaskADKey).ValueOrThrow();
|
||||
int index = 0;
|
||||
int day = ZZTimeHelper.UtcNow().UtcNowOffset().DayOfYear;
|
||||
if (!string.IsNullOrEmpty(adData))
|
||||
{
|
||||
string[] strings = adData.Split("|");
|
||||
if (strings.Length == 2)
|
||||
{
|
||||
int.TryParse(strings[0], out index);
|
||||
int.TryParse(strings[1], out day);
|
||||
}
|
||||
}
|
||||
//保存进度
|
||||
index++;
|
||||
adData = $"{index}|{day}";
|
||||
saveStore.Write(LuckyTaskData.ADTaskADKey, adData).RequireAccepted();
|
||||
RefreshADItemState(index);
|
||||
canvasGroup.blocksRaycasts = true;
|
||||
GContext.Publish(new ShowData());
|
||||
}
|
||||
void RefreshADItemState(int index)
|
||||
{
|
||||
for (int i = 0; i < taskADList.Count; i++)
|
||||
{
|
||||
taskADList[i].state = 0;
|
||||
if (taskADList[i].index < index)
|
||||
{
|
||||
taskADList[i].state = -1;
|
||||
}
|
||||
else if (taskADList[i].index == index)
|
||||
{
|
||||
taskADList[i].state = 1;
|
||||
}
|
||||
taskItems[i].Refresh();
|
||||
}
|
||||
}
|
||||
void RefreshAdItem()
|
||||
{
|
||||
|
||||
}
|
||||
public void TaskItemPlay(LuckyADTask luckyTaskItem)
|
||||
{
|
||||
canvasGroup.blocksRaycasts = false;
|
||||
try
|
||||
{
|
||||
luckyTaskItem.data.state = -1; // 更新状态为已领取
|
||||
luckyTaskItem.Refresh();
|
||||
luckyTaskItem.Play();
|
||||
taskList.Sort((a, b) => b.state.CompareTo(a.state)); // 按state排序
|
||||
luckyTaskItem.PlayFillAmount(taskList[0]);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError($"Error during TaskItemPlay: {ex.Message}");
|
||||
}
|
||||
canvasGroup.blocksRaycasts = true;
|
||||
GContext.Publish(new ShowData());
|
||||
}
|
||||
|
||||
void OnClickLuckyTaskItem(LuckyADTask luckyTaskItem)
|
||||
{
|
||||
if (IsOpen)
|
||||
{
|
||||
//奖励补领
|
||||
OnClickLuckyTaskItem1(luckyTaskItem);
|
||||
return;
|
||||
}
|
||||
if (luckyTaskItem.data.state == 1)
|
||||
{
|
||||
bool result = luckyModel.OnGetTaskReward(luckyTaskItem.data.id);
|
||||
if (result)
|
||||
{
|
||||
CommonTask commonTask = tables.TbCommonTask.GetOrDefault(luckyTaskItem.data.id);
|
||||
if (commonTask != null)
|
||||
{
|
||||
// 任务奖励
|
||||
int level = taskData.GetValueOrDefault(-2, 0);
|
||||
int dropID = commonTask.TaskReward[level];
|
||||
|
||||
ChangeSource changeSource = new ChangeSource(EventId, "Event_Minigame3", "Event_Minigame3_LuckMagic", "TaskReward");
|
||||
if (fishingEvent.SubType == 4)
|
||||
{
|
||||
changeSource.Source_Type = "Event_Minigame3_LuckyCards";
|
||||
}
|
||||
List<ItemData> rewardItem = GContext.container.Resolve<PlayerItemData>().AddItemByDropLureInflation(luckyTaskItem.data.inflationRate, dropID, changeSource, false);
|
||||
if (rewardItem != null)
|
||||
{
|
||||
TaskItemPlay(luckyTaskItem);
|
||||
}
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("event_lucky_task"))
|
||||
{
|
||||
int round = luckyModel.DicTasks.GetValueOrDefault(-1, 0);
|
||||
e.AddContent("task_id", luckyTaskItem.data.id)
|
||||
.AddContent("round", round)
|
||||
.AddContent("combine_id", round * 100000 + luckyTaskItem.data.id % 10000)
|
||||
.AddContent("task_reward", dropID);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("任务奖励领取失败");
|
||||
}
|
||||
}
|
||||
else if (luckyTaskItem.data.state == 0)
|
||||
{
|
||||
OnClickGo(luckyTaskItem.data.id);
|
||||
Debug.Log("任务未完成,无法领取奖励");
|
||||
}
|
||||
else if (luckyTaskItem.data.state == -1)
|
||||
{
|
||||
Debug.Log("任务已领取,无法重复领取");
|
||||
}
|
||||
}
|
||||
|
||||
void OnClickLuckyTaskItem1(LuckyADTask luckyTaskItem)
|
||||
{
|
||||
if (luckyTaskItem.data.state == 1)
|
||||
{
|
||||
CommonTask commonTask = tables.TbCommonTask.GetOrDefault(luckyTaskItem.data.id);
|
||||
taskData[luckyTaskItem.data.id] = -1; // 标记为已领取
|
||||
if (commonTask != null)
|
||||
{
|
||||
// 任务奖励
|
||||
int level = taskData.GetValueOrDefault(-2, 0);
|
||||
int dropID = commonTask.TaskReward[level];
|
||||
ChangeSource changeSource = new ChangeSource(EventId, "Event_Minigame3", "Event_Minigame3_LuckMagic", "TaskReward");
|
||||
if (fishingEvent.SubType == 4)
|
||||
{
|
||||
changeSource.Source_Type = "Event_Minigame3_LuckyCards";
|
||||
}
|
||||
|
||||
List<ItemData> rewardItem = GContext.container.Resolve<PlayerItemData>().AddItemByDropLureInflation(luckyTaskItem.data.inflationRate, dropID, changeSource, false);
|
||||
if (rewardItem != null)
|
||||
{
|
||||
TaskItemPlay(luckyTaskItem);
|
||||
}
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("event_lucky_task"))
|
||||
{
|
||||
int round = luckyModel.DicTasks.GetValueOrDefault(-1, 0);
|
||||
e.AddContent("task_id", luckyTaskItem.data.id)
|
||||
.AddContent("round", round)
|
||||
.AddContent("combine_id", round * 100000 + luckyTaskItem.data.id % 10000)
|
||||
.AddContent("task_reward", dropID);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
UIManager.Instance.DestroyUI(gameObject.name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class LuckyTaskBtn : MonoBehaviour, IUIRedPoint
|
||||
{
|
||||
game.PlayerDataStorage.IPlayerSaveStore saveStore;
|
||||
|
||||
Button btn;
|
||||
ILuckyTaskGetData model;
|
||||
|
||||
public string key;
|
||||
public GameObject redPoint;
|
||||
public GameObject redpoint_big;
|
||||
public TMP_Text text_redpoint_big;
|
||||
void Awake()
|
||||
{
|
||||
saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
btn = GetComponent<Button>();
|
||||
btn.onClick.AddListener(OnClick);
|
||||
}
|
||||
public void Init(ILuckyTaskGetData model, string key)
|
||||
{
|
||||
this.model = model;
|
||||
this.key = key;
|
||||
SetKey();
|
||||
}
|
||||
async void OnClick()
|
||||
{
|
||||
if (model != null)
|
||||
{
|
||||
GameObject taskPanelName = await UIManager.Instance.ShowUI(new UIType(model.TaskPanelName));
|
||||
if (this == null || !saveStore.IsCurrent) return;
|
||||
if (taskPanelName != null)
|
||||
{
|
||||
var panel = taskPanelName.GetComponent<LuckyTaskPanelBase>();
|
||||
if (panel != null)
|
||||
{
|
||||
panel.Init(model);
|
||||
int count = RedPointManager.Instance.GetRedPointCount(key);
|
||||
RedPointManager.Instance.SetRedPointState(key, count > 0, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(key))
|
||||
{
|
||||
RedPointManager.Instance.RemoveRedPoint(key);
|
||||
key = "";
|
||||
}
|
||||
}
|
||||
public void SetKey()
|
||||
{
|
||||
RedPointManager.Instance.AddRedPoint(key, this);
|
||||
SetRedPointState(RedPointManager.Instance.GetRedPointState(key));
|
||||
}
|
||||
|
||||
public void SetRedPointState(bool state)
|
||||
{
|
||||
int count = RedPointManager.Instance.GetRedPointCount(key);
|
||||
if (!state)
|
||||
{
|
||||
string adData = saveStore.Read(LuckyTaskData.ADTaskADKey).ValueOrThrow();
|
||||
if (!string.IsNullOrEmpty(adData))
|
||||
{
|
||||
string[] strings = adData.Split("|");
|
||||
int day = -1;
|
||||
int curDay = ZZTimeHelper.UtcNow().UtcNowOffset().DayOfYear;
|
||||
if (strings.Length != 2 || !int.TryParse(strings[1], out day) || day != curDay)
|
||||
{
|
||||
state = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state = true;
|
||||
}
|
||||
}
|
||||
if (redpoint_big != null && text_redpoint_big != null)
|
||||
{
|
||||
if (count > 1)
|
||||
{
|
||||
redpoint_big.SetActive(true);
|
||||
if (count > 99)
|
||||
{
|
||||
text_redpoint_big.text = "99+";
|
||||
}
|
||||
else
|
||||
{
|
||||
text_redpoint_big.text = count.ToString();
|
||||
}
|
||||
state = false;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
redpoint_big.SetActive(false);
|
||||
}
|
||||
}
|
||||
if (redPoint != null)
|
||||
{
|
||||
redPoint.SetActive(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,259 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class LuckyTaskPanel : LuckyTaskPanelBase
|
||||
{
|
||||
protected override void Awake()
|
||||
{
|
||||
saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
base.Awake();
|
||||
}
|
||||
|
||||
game.PlayerDataStorage.IPlayerSaveStore saveStore;
|
||||
|
||||
/// <summary>
|
||||
/// luckyModel 也作为是否自动全部领奖的标志
|
||||
/// </summary>
|
||||
List<LuckyTaskItem> taskItems = new List<LuckyTaskItem>();
|
||||
protected override void SetItem()
|
||||
{
|
||||
string adData = saveStore.Read(LuckyTaskData.ADTaskADKey).ValueOrThrow();
|
||||
int curDay = ZZTimeHelper.UtcNow().UtcNowOffset().DayOfYear;
|
||||
if (!string.IsNullOrEmpty(adData))
|
||||
{
|
||||
string[] strings = adData.Split("|");
|
||||
int day = -1;
|
||||
if (strings.Length != 2 || !int.TryParse(strings[1], out day) || day != curDay)
|
||||
{
|
||||
adData = $"{0}|{curDay}";
|
||||
saveStore.Write(LuckyTaskData.ADTaskADKey, adData).RequireAccepted();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
adData = $"{0}|{curDay}";
|
||||
saveStore.Write(LuckyTaskData.ADTaskADKey, adData).RequireAccepted();
|
||||
}
|
||||
List<LuckyTaskItemData> taskList = new List<LuckyTaskItemData>();
|
||||
int level = taskData.GetValueOrDefault(-2, 0);
|
||||
float inflationRate = taskData.GetValueOrDefault(-3, 0) / 100f;
|
||||
foreach (var item in taskData)
|
||||
{
|
||||
if (item.Key <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
CommonTask commonTask = tables.TbCommonTask.Get(item.Key);
|
||||
if (commonTask != null)
|
||||
{
|
||||
LuckyTaskItemData luckyTaskData = new LuckyTaskItemData();
|
||||
luckyTaskData.id = item.Key;
|
||||
int dropID = commonTask.TaskReward[level];
|
||||
|
||||
luckyTaskData.reward = dropID;
|
||||
|
||||
luckyTaskData.inflationRate = inflationRate;
|
||||
string desc = LocalizationMgr.GetText(commonTask.TaskDesc_l10n_key);
|
||||
string progressText;
|
||||
if (commonTask.TaskType == ConditionType.RechargeAmount)
|
||||
{
|
||||
//sku.ProdPrice
|
||||
int iapid = commonTask.TaskParam[level];
|
||||
var iap = tables.TbIAPItemList.GetOrDefault(iapid);
|
||||
SKUDetailDataEvent sKUDetailDataEvent = new SKUDetailDataEvent(iap);
|
||||
GContext.Publish(sKUDetailDataEvent);
|
||||
desc = desc.SafeFormat(sKUDetailDataEvent.price);
|
||||
float TaskParam = iap.VIPPoint;
|
||||
float value = item.Value;
|
||||
float ratio = value / TaskParam;
|
||||
float ProdPrice = sKUDetailDataEvent.prodPrice;
|
||||
float truncatedPrice = (float)Mathf.Ceil(ratio * ProdPrice * 100) / 100;
|
||||
progressText = $"({truncatedPrice:F2}/{ProdPrice})";
|
||||
if (item.Value == -1)
|
||||
{
|
||||
progressText = $"({ProdPrice}/{ProdPrice})";
|
||||
luckyTaskData.state = -1;
|
||||
}
|
||||
else if (value >= TaskParam)
|
||||
{
|
||||
luckyTaskData.state = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
luckyTaskData.state = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int taskParam = commonTask.TaskParam[level];
|
||||
if (commonTask.TaskType == ConditionType.ConsumeEnergy)
|
||||
{
|
||||
taskParam = GContext.container.Resolve<PlayerItemData>().IntInflation(taskParam, inflationRate);
|
||||
}
|
||||
progressText = $"({item.Value}/{taskParam})";
|
||||
if (item.Value == -1)
|
||||
{
|
||||
progressText = $"({taskParam}/{taskParam})";
|
||||
luckyTaskData.state = -1;
|
||||
}
|
||||
else if (item.Value >= taskParam)
|
||||
{
|
||||
luckyTaskData.state = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
luckyTaskData.state = 0;
|
||||
}
|
||||
desc = desc.SafeFormat(taskParam);
|
||||
}
|
||||
luckyTaskData.desc = desc + progressText;
|
||||
taskList.Add(luckyTaskData);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Task ID {item.Key} not found in CommonTask table.");
|
||||
}
|
||||
}
|
||||
taskList.Sort((a, b) => b.state.CompareTo(a.state)); // 按state排序
|
||||
for (int i = 0; i < taskList.Count; i++)
|
||||
{
|
||||
LuckyTaskItem luckyTaskItem = Instantiate(itemPrefab, content).AddComponent<LuckyTaskItem>();
|
||||
luckyTaskItem.gameObject.SetActive(true);
|
||||
luckyTaskItem.Init(taskList[i], OnClickLuckyTaskItem);
|
||||
taskItems.Add(luckyTaskItem);
|
||||
}
|
||||
}
|
||||
|
||||
public void TaskItemPlay(LuckyTaskItem luckyTaskItem)
|
||||
{
|
||||
canvasGroup.blocksRaycasts = false;
|
||||
try
|
||||
{
|
||||
luckyTaskItem.data.state = -1; // 更新状态为已领取
|
||||
luckyTaskItem.Refresh();
|
||||
luckyTaskItem.Play();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError($"Error during TaskItemPlay: {ex.Message}");
|
||||
}
|
||||
canvasGroup.blocksRaycasts = true;
|
||||
GContext.Publish(new ShowData());
|
||||
}
|
||||
|
||||
void OnClickLuckyTaskItem(LuckyTaskItem luckyTaskItem)
|
||||
{
|
||||
if (IsOpen)
|
||||
{
|
||||
//奖励补领
|
||||
OnClickLuckyTaskItem1(luckyTaskItem);
|
||||
return;
|
||||
}
|
||||
if (luckyTaskItem.data.state == 1)
|
||||
{
|
||||
bool result = luckyModel.OnGetTaskReward(luckyTaskItem.data.id);
|
||||
if (result)
|
||||
{
|
||||
CommonTask commonTask = tables.TbCommonTask.GetOrDefault(luckyTaskItem.data.id);
|
||||
if (commonTask != null)
|
||||
{
|
||||
// 任务奖励
|
||||
int level = taskData.GetValueOrDefault(-2, 0);
|
||||
int dropID = commonTask.TaskReward[level];
|
||||
|
||||
ChangeSource changeSource = new ChangeSource(EventId, "Event_Minigame3", "Event_Minigame3_LuckMagic", "TaskReward");
|
||||
if (fishingEvent.SubType == 4)
|
||||
{
|
||||
changeSource.Source_Type = "Event_Minigame3_LuckyCards";
|
||||
}
|
||||
List<ItemData> rewardItem = GContext.container.Resolve<PlayerItemData>().AddItemByDropLureInflation(luckyTaskItem.data.inflationRate, dropID, changeSource, false);
|
||||
if (rewardItem != null)
|
||||
{
|
||||
TaskItemPlay(luckyTaskItem);
|
||||
}
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("event_lucky_task"))
|
||||
{
|
||||
int round = luckyModel.DicTasks.GetValueOrDefault(-1, 0);
|
||||
e.AddContent("task_id", luckyTaskItem.data.id)
|
||||
.AddContent("round", round)
|
||||
.AddContent("combine_id", round * 100000 + luckyTaskItem.data.id % 10000)
|
||||
.AddContent("task_reward", dropID);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("任务奖励领取失败");
|
||||
}
|
||||
}
|
||||
else if (luckyTaskItem.data.state == 0)
|
||||
{
|
||||
OnClickGo(luckyTaskItem.data.id);
|
||||
Debug.Log("任务未完成,无法领取奖励");
|
||||
}
|
||||
else if (luckyTaskItem.data.state == -1)
|
||||
{
|
||||
Debug.Log("任务已领取,无法重复领取");
|
||||
}
|
||||
}
|
||||
|
||||
void OnClickLuckyTaskItem1(LuckyTaskItem luckyTaskItem)
|
||||
{
|
||||
if (luckyTaskItem.data.state == 1)
|
||||
{
|
||||
CommonTask commonTask = tables.TbCommonTask.GetOrDefault(luckyTaskItem.data.id);
|
||||
taskData[luckyTaskItem.data.id] = -1; // 标记为已领取
|
||||
if (commonTask != null)
|
||||
{
|
||||
// 任务奖励
|
||||
int level = taskData.GetValueOrDefault(-2, 0);
|
||||
int dropID = commonTask.TaskReward[level];
|
||||
ChangeSource changeSource = new ChangeSource(EventId, "Event_Minigame3", "Event_Minigame3_LuckMagic", "TaskReward");
|
||||
if (fishingEvent.SubType == 4)
|
||||
{
|
||||
changeSource.Source_Type = "Event_Minigame3_LuckyCards";
|
||||
}
|
||||
|
||||
List<ItemData> rewardItem = GContext.container.Resolve<PlayerItemData>().AddItemByDropLureInflation(luckyTaskItem.data.inflationRate, dropID, changeSource, false);
|
||||
if (rewardItem != null)
|
||||
{
|
||||
TaskItemPlay(luckyTaskItem);
|
||||
}
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("event_lucky_task"))
|
||||
{
|
||||
int round = luckyModel.DicTasks.GetValueOrDefault(-1, 0);
|
||||
e.AddContent("task_id", luckyTaskItem.data.id)
|
||||
.AddContent("round", round)
|
||||
.AddContent("combine_id", round * 100000 + luckyTaskItem.data.id % 10000)
|
||||
.AddContent("task_reward", dropID);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
UIManager.Instance.DestroyUI(gameObject.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
public class LuckyTaskItemData
|
||||
{
|
||||
public int id;
|
||||
public string desc;
|
||||
public int target;
|
||||
public int reward;
|
||||
public int progress;
|
||||
/// <summary>
|
||||
/// // 0:未完成 1:可领取 -1:已领取
|
||||
/// </summary>
|
||||
public int state;
|
||||
public float inflationRate;
|
||||
}
|
||||
@@ -1,499 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using LitJson;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
public class MatchGameData
|
||||
{
|
||||
public int eventID;
|
||||
public int index;
|
||||
public int vip;
|
||||
public float inflation;
|
||||
public int seed;
|
||||
public List<int> isOpenedList = new List<int>();
|
||||
//连续翻开不同的次数
|
||||
public int openCount;
|
||||
//兔子交换记录,-1 表示无交换
|
||||
public int swapFrom = -1;
|
||||
public int swapTo = -1;
|
||||
public int swapFrom2 = -1;
|
||||
public int swapTo2 = -1;
|
||||
public int tokenConsumed;
|
||||
}
|
||||
|
||||
public enum FlipResult
|
||||
{
|
||||
// InsufficientTokens,
|
||||
FirstHatFlipped,
|
||||
NoMatch,
|
||||
Match,
|
||||
}
|
||||
|
||||
|
||||
public class EventMatchGameManager
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public string EventMatchGameDataKey => "EventMatchGameDataKey";
|
||||
public int ItemSubType => 3;
|
||||
public int ItemType => 6;
|
||||
int EventType => 4;
|
||||
int EventSubType => 13;
|
||||
Tables tables;
|
||||
FishingEventData fishingEventData;
|
||||
TbFishingEvent tbFishingEvent;
|
||||
TbFishingEventCycleItem tbCycleItem;
|
||||
TbEventMatchInit tbEventMatchInit;
|
||||
public EventMatchGameManager()
|
||||
{
|
||||
tables = GContext.container.Resolve<Tables>();
|
||||
fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
tbFishingEvent = tables.TbFishingEvent;
|
||||
tbCycleItem = tables.TbFishingEventCycleItem;
|
||||
tbEventMatchInit = tables.TbEventMatchInit;
|
||||
}
|
||||
|
||||
public MatchGameData GetEventData()
|
||||
{
|
||||
var matchGameData = new MatchGameData();
|
||||
|
||||
string data = saveStore.Read(EventMatchGameDataKey).ValueOrThrow();
|
||||
if (data != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
matchGameData = Newtonsoft.Json.JsonConvert.DeserializeObject<MatchGameData>(data) ?? throw new Newtonsoft.Json.JsonSerializationException("Null MatchGameData save.");
|
||||
if (matchGameData.vip > 10)
|
||||
{
|
||||
matchGameData.vip = 10;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return matchGameData;
|
||||
}
|
||||
|
||||
public MatchGameData CheckEventData()
|
||||
{
|
||||
MatchGameData matchGameData = GetEventData();
|
||||
FishingEvent tr = GetEvent();
|
||||
if (tr == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (matchGameData.eventID != tr.ID)
|
||||
{
|
||||
FishingEventCycleItem cycleItem = tbCycleItem.GetOrDefault(tr.RedirectID);
|
||||
matchGameData = new MatchGameData();
|
||||
matchGameData.eventID = tr.ID;
|
||||
matchGameData.inflation = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
matchGameData.seed = new System.Random().Next();
|
||||
matchGameData.index = 0;
|
||||
matchGameData.vip = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
if (matchGameData.vip > 10)
|
||||
{
|
||||
matchGameData.vip = 10;
|
||||
}
|
||||
SetTokens(tr.ID, cycleItem.WelcomeGift);
|
||||
SaveData(matchGameData);
|
||||
}
|
||||
return matchGameData;
|
||||
}
|
||||
|
||||
public bool CheckOpen()
|
||||
{
|
||||
var matchGameData = CheckEventData();
|
||||
if (matchGameData == null || matchGameData.eventID == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FishingEvent fishingEvent = tables.TbFishingEvent.GetOrDefault(matchGameData.eventID);
|
||||
if (fishingEvent == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LimitedTime limitedTime = fishingEvent.TimeDefinition as LimitedTime;
|
||||
var startTime = DateTime.Parse(limitedTime.StartTime);
|
||||
var endTime = DateTime.Parse(limitedTime.EndTime);
|
||||
var curTime = ZZTimeHelper.UtcNow();
|
||||
if (curTime < startTime || curTime > endTime)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
FishingEvent GetEvent()
|
||||
{
|
||||
return fishingEventData.GetEventByTypeAndSubType(EventType, EventSubType);
|
||||
}
|
||||
|
||||
#region Tokens
|
||||
public void AddTokens(int subType, int count)
|
||||
{
|
||||
if (subType == ItemSubType)
|
||||
{
|
||||
FishingEvent tr = GetEvent();
|
||||
if (tr != null)
|
||||
{
|
||||
int tokens = GetTokenCount(tr.ID);
|
||||
tokens += count;
|
||||
SetTokens(tr.ID, tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
void SetTokens(int eventID, int count)
|
||||
{
|
||||
fishingEventData.SaveTransitionData(eventID, count);
|
||||
}
|
||||
|
||||
public void RemoveTokens(int eventID, int count)
|
||||
{
|
||||
int tokens = GetTokenCount(eventID);
|
||||
tokens -= count;
|
||||
SetTokens(eventID, tokens);
|
||||
}
|
||||
|
||||
public int GetTokenCount()
|
||||
{
|
||||
FishingEvent tr = GetEvent();
|
||||
if (tr != null)
|
||||
{
|
||||
return GetTokenCount(tr.ID);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public int GetTokenCount(int eventID)
|
||||
{
|
||||
return fishingEventData.GetTransitionDataCount(eventID);
|
||||
}
|
||||
#endregion Tokens
|
||||
|
||||
public FlipResult AddProgress(int eventID, MatchGameData data, List<int> eventSpecialItemList, int opened, List<int> board, out List<int> openPos)
|
||||
{
|
||||
openPos = new List<int>();
|
||||
FlipResult idOpened = FlipResult.FirstHatFlipped;
|
||||
RemoveTokens(eventID, 1);
|
||||
data.tokenConsumed++;
|
||||
int count = data.isOpenedList.Count;
|
||||
if (count % 2 == 0)
|
||||
{
|
||||
data.isOpenedList.Add(opened);
|
||||
openPos.Add(opened);
|
||||
}
|
||||
else
|
||||
{
|
||||
//做对比
|
||||
int curOpened = data.isOpenedList[count - 1];
|
||||
//获取 curOpened 的数据,对比 opened 的数据
|
||||
int curId = board[curOpened];
|
||||
int nextId = board[opened];
|
||||
openPos.Add(curOpened);
|
||||
openPos.Add(opened);
|
||||
|
||||
#if AGG
|
||||
EventMatchInit matchInit = GetMatchInit(eventID);
|
||||
if (matchInit != null)
|
||||
{
|
||||
EventMatchStage matchStage = GetMatchStage(data, matchInit);
|
||||
int countR1 = matchInit.StageListRound1.Count;
|
||||
int roundId = data.index < countR1 ? 1 : (data.index - countR1) / matchInit.StageListRound2.Count + 2;
|
||||
using (var e = GEvent.GameEvent("event_match"))
|
||||
{
|
||||
e.AddContent("event_id", eventID)
|
||||
.AddContent("round_id", roundId)
|
||||
.AddContent("stage_id", matchStage.ID)
|
||||
.AddContent("item_id1", curId)
|
||||
.AddContent("item_id2", nextId);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (curId == nextId)
|
||||
{
|
||||
//成功
|
||||
data.isOpenedList.Add(opened);
|
||||
data.openCount = 0;
|
||||
idOpened = FlipResult.Match;
|
||||
if (eventSpecialItemList.Contains(curId))
|
||||
{
|
||||
var spOpenPos = GetSpItemOpen(data, board);
|
||||
if (spOpenPos.Count > 0)
|
||||
{
|
||||
data.isOpenedList.AddRange(spOpenPos);
|
||||
openPos.AddRange(spOpenPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//失败
|
||||
data.isOpenedList.RemoveAt(count - 1);
|
||||
data.openCount++;
|
||||
idOpened = FlipResult.NoMatch;
|
||||
if (data.openCount >= 4)
|
||||
{
|
||||
var tipsPos = GetTipsItemOpen(data, board);
|
||||
if (tipsPos.Count > 0)
|
||||
{
|
||||
openPos.AddRange(tipsPos);
|
||||
}
|
||||
data.openCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
GameDebug.Log("[EventMatch] ItemID " + string.Join(',', board) + " " + string.Join(',', data.isOpenedList));
|
||||
SaveData(data);
|
||||
return idOpened;
|
||||
}
|
||||
List<int> GetSpItemOpen(MatchGameData data, List<int> board)
|
||||
{
|
||||
List<int> openPos = new List<int>();
|
||||
Dictionary<int, int> boardDic = new Dictionary<int, int>();
|
||||
|
||||
for (int i = 0; i < board.Count; i++)
|
||||
{
|
||||
if (!data.isOpenedList.Contains(i))
|
||||
{
|
||||
boardDic.Add(i, board[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (boardDic.Count <= 4)
|
||||
{
|
||||
openPos.AddRange(boardDic.Keys);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Random random = new System.Random();
|
||||
// 执行两次随机选择
|
||||
SelectAndRemoveGroup(boardDic, openPos, random);
|
||||
SelectAndRemoveGroup(boardDic, openPos, random);
|
||||
}
|
||||
return openPos;
|
||||
}
|
||||
|
||||
List<int> GetTipsItemOpen(MatchGameData data, List<int> board)
|
||||
{
|
||||
List<int> openPos = new List<int>();
|
||||
Dictionary<int, int> dict = new Dictionary<int, int>();
|
||||
for (int i = 0; i < board.Count; i++)
|
||||
{
|
||||
if (!data.isOpenedList.Contains(i))
|
||||
{
|
||||
dict.Add(i, board[i]);
|
||||
}
|
||||
}
|
||||
System.Random random = new System.Random();
|
||||
var keys = dict.Keys.ToList();
|
||||
int key = keys[random.Next(keys.Count)];
|
||||
int value = dict[key];
|
||||
// 找到所有具有相同值的键
|
||||
return dict.Where(kv => kv.Value == value).Select(kv => kv.Key).ToList();
|
||||
}
|
||||
|
||||
void SelectAndRemoveGroup(Dictionary<int, int> dict, List<int> result, System.Random random)
|
||||
{
|
||||
var keys = dict.Keys.ToList();
|
||||
int key = keys[random.Next(keys.Count)];
|
||||
int value = dict[key];
|
||||
|
||||
// 找到所有具有相同值的键
|
||||
var group = dict.Where(kv => kv.Value == value).Select(kv => kv.Key).ToList();
|
||||
|
||||
// 添加到结果列表
|
||||
result.AddRange(group);
|
||||
|
||||
// 从字典中移除已选中的组
|
||||
foreach (int k in group)
|
||||
{
|
||||
dict.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
public int NextStage()
|
||||
{
|
||||
MatchGameData data = GetEventData();
|
||||
EventMatchInit eventMatchInit = GetMatchInit(data.eventID);
|
||||
|
||||
EventMatchStage stage = GetMatchStage(data, eventMatchInit);
|
||||
if (data.isOpenedList.Count == stage.ItemCount)
|
||||
{
|
||||
#if AGG
|
||||
int countR1 = eventMatchInit.StageListRound1.Count;
|
||||
int roundId = data.index < countR1 ? 1 : (data.index - countR1) / eventMatchInit.StageListRound2.Count + 2;
|
||||
using (var e = GEvent.GameEvent("event_match_complete"))
|
||||
{
|
||||
e.AddContent("event_id", data.eventID)
|
||||
.AddContent("round_id", roundId)
|
||||
.AddContent("stage_id", stage.ID)
|
||||
.AddContent("item_consume", data.tokenConsumed);
|
||||
}
|
||||
#endif
|
||||
//领奖
|
||||
ChangeSource changeSource = new ChangeSource(data.eventID, "Event_Minigame1", "Event_Minigame1_Match", "StageReward");
|
||||
List<ItemData> itemData = GContext.container.Resolve<PlayerItemData>().AddItemByDropLureInflation(data.inflation, stage.DropId, changeSource);
|
||||
|
||||
//下一关
|
||||
data.index++;
|
||||
data.seed = new System.Random().Next();
|
||||
data.isOpenedList.Clear();
|
||||
data.swapFrom = -1;
|
||||
data.swapTo = -1;
|
||||
data.swapFrom2 = -1;
|
||||
data.swapTo2 = -1;
|
||||
data.tokenConsumed = 0;
|
||||
SaveData(data);
|
||||
if (stage.ID == eventMatchInit.StageListRound1[^1] || stage.ID == eventMatchInit.StageListRound2[^1])
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public List<int> GetBoardData(MatchGameData data)
|
||||
{
|
||||
EventMatchInit eventMatchInit = GetMatchInit(data.eventID);
|
||||
|
||||
EventMatchStage stage = GetMatchStage(data, eventMatchInit);
|
||||
int count = stage.ItemCount / 2;
|
||||
|
||||
List<EventMatchItem> eventMatchItems = tables.TbEventMatchItem.DataList;
|
||||
List<int> ids = new List<int>(eventMatchInit.EventItemList);
|
||||
List<int> itemList = new List<int>(stage.ItemList);
|
||||
List<int> itemID = new List<int>();
|
||||
int id;
|
||||
if (itemList.Count > 0)
|
||||
{
|
||||
count -= itemList.Count;
|
||||
for (int i = 0; i < itemList.Count; i++)
|
||||
{
|
||||
id = eventMatchInit.EventItemList[itemList[i] - 1];
|
||||
ids.Remove(id);
|
||||
EventMatchItem eventMatchItem = tables.TbEventMatchItem.GetOrDefault(id);
|
||||
if (eventMatchItem != null)
|
||||
{
|
||||
itemID.Add(id);
|
||||
itemID.Add(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
System.Random random = new System.Random(data.seed);
|
||||
if (count > 0)
|
||||
{
|
||||
int index;
|
||||
double spRoll = random.NextDouble();
|
||||
if (spRoll < stage.SpItemProb)
|
||||
{
|
||||
var spItemIDs = eventMatchInit.EventSpecialItemList;
|
||||
if (spItemIDs.Count > 0)
|
||||
{
|
||||
count--;
|
||||
index = random.Next(spItemIDs.Count);
|
||||
itemID.Add(spItemIDs[index]);
|
||||
itemID.Add(spItemIDs[index]);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
index = random.Next(ids.Count);
|
||||
id = ids[index];
|
||||
ids.Remove(id);
|
||||
|
||||
itemID.Add(id);
|
||||
itemID.Add(id);
|
||||
}
|
||||
}
|
||||
//itemID 打乱顺序
|
||||
if (data.index == 0)
|
||||
{
|
||||
itemID[2] = itemID[0];
|
||||
itemID[1] = itemID[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
itemID = ConvertTools.Shuffle(itemID, random);
|
||||
}
|
||||
Debug.Log("[EventMatch] ItemID " + string.Join(',', itemID));
|
||||
return itemID;
|
||||
}
|
||||
|
||||
public async void ShowFestPack(int eventID)
|
||||
{
|
||||
try
|
||||
{
|
||||
var init = GetMatchInit(eventID);
|
||||
if (init == null)
|
||||
return;
|
||||
var packData = new EventNeoNormalPackData
|
||||
{
|
||||
EventId = eventID,
|
||||
PackId = init.PackId,
|
||||
// Panel = init.PackPanel
|
||||
};
|
||||
await EventNeoNormalPackPanel.TryShowAsync(packData);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
}
|
||||
public EventMatchInit GetMatchInit(int eventID)
|
||||
{
|
||||
try
|
||||
{
|
||||
FishingEvent fishingEvent = tbFishingEvent.GetOrDefault(eventID);
|
||||
FishingEventCycleItem cycleItem = tbCycleItem.GetOrDefault(fishingEvent.RedirectID);
|
||||
return tbEventMatchInit[cycleItem.RedirectID];
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public EventMatchInit GetMatchInitRedirectID(int redirectID)
|
||||
{
|
||||
try
|
||||
{
|
||||
FishingEventCycleItem cycleItem = tbCycleItem.GetOrDefault(redirectID);
|
||||
return tbEventMatchInit[cycleItem.RedirectID];
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public EventMatchStage GetMatchStage(MatchGameData data, EventMatchInit eventMatchInit)
|
||||
{
|
||||
int stageID;
|
||||
int countRound1 = eventMatchInit.StageListRound1.Count;
|
||||
if (data.index < countRound1)
|
||||
{
|
||||
stageID = eventMatchInit.StageListRound1[data.index];
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = data.index - countRound1;
|
||||
index %= eventMatchInit.StageListRound2.Count;
|
||||
stageID = eventMatchInit.StageListRound2[index];
|
||||
}
|
||||
return tables.TbEventMatchStage.GetOrDefault(stageID);
|
||||
}
|
||||
|
||||
public void SaveData(MatchGameData matchGameData)
|
||||
{
|
||||
saveStore.Write(EventMatchGameDataKey, JsonMapper.ToJson(matchGameData)).RequireAccepted();
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,518 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using Script.RuntimeScript;
|
||||
using Script.RuntimeScript.model.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
|
||||
public class DiggingGameModel
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
#region 协议Const区域
|
||||
|
||||
//工具个数
|
||||
private const string DigCountKey = "DigCount";
|
||||
//棋盘管卡数据
|
||||
private const string DigGridDataKey = "DigGridData";
|
||||
|
||||
#endregion
|
||||
|
||||
#region 管卡json中原始数据
|
||||
private DinggingSceneData _dinggingSceneData;
|
||||
#endregion
|
||||
|
||||
#region 服务器数据
|
||||
/// <summary>
|
||||
/// 沙滩寻宝基本数据
|
||||
/// </summary>
|
||||
private SandDigServerData _serverData = null;
|
||||
public int MaxCount = 10;
|
||||
/// <summary>
|
||||
/// 沙滩寻宝棋盘网格数据
|
||||
/// </summary>
|
||||
private List<ServerGridData> _allServerGridData = new List<ServerGridData>();
|
||||
private int _serverDigCount = 0;
|
||||
int redDot = 10;
|
||||
//最新获取 道具个数 *** 已经废弃
|
||||
public int NewAddDigCount = 0;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 本期活动开放的所有管卡
|
||||
/// </summary>
|
||||
private List<int> _allClamps = new List<int>();
|
||||
public List<int> AllClamps => _allClamps;
|
||||
|
||||
private int _curActivityId = 0;
|
||||
public int CurActivityId
|
||||
{
|
||||
get => _curActivityId;
|
||||
}
|
||||
public float InflationRate;
|
||||
public DiggingActivityInit digActivityConfig;
|
||||
public string ResourceTopic => digActivityConfig.ResourceTopic;
|
||||
#region 数据初始化
|
||||
|
||||
/// <summary>
|
||||
/// 在线时候 活动开启
|
||||
/// </summary>
|
||||
public void ActivityOpen(cfg.FishingEvent t)
|
||||
{
|
||||
if (_curActivityId == t.ID)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_curActivityId = t.ID;
|
||||
if (_curActivityId > 0)
|
||||
{
|
||||
FishingEventCycleItem cycle = GContext.container.Resolve<Tables>().TbFishingEventCycleItem.GetOrDefault(t.RedirectID);
|
||||
digActivityConfig = GContext.container.Resolve<Tables>().TbDiggingActivityInit.Get(cycle.RedirectID);
|
||||
MaxCount = digActivityConfig.StageCount;
|
||||
redDot = cycle.RedDot;
|
||||
}
|
||||
DiggingGameManager.LogError("沙滩寻宝开启");
|
||||
string countJson = saveStore.Read(DigCountKey).ValueOrThrow();
|
||||
string digGridDataJson = saveStore.Read(DigGridDataKey).ValueOrThrow();
|
||||
if (string.IsNullOrEmpty(countJson))
|
||||
{
|
||||
SaveNull();
|
||||
}
|
||||
DoServerData(countJson, digGridDataJson);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 进入游戏初始化数据
|
||||
/// </summary>
|
||||
/// <param name="userDatas"></param>
|
||||
public async System.Threading.Tasks.Task InitServerData()
|
||||
{
|
||||
_curActivityId = GContext.container.Resolve<DiggingGameManager>().GetActivityId();
|
||||
DiggingGameManager.LogError("沙滩寻宝数据初始化");
|
||||
if (_curActivityId > 0)
|
||||
{
|
||||
var _tables = GContext.container.Resolve<Tables>();
|
||||
FishingEvent fishingEvent = _tables.TbFishingEvent.GetOrDefault(_curActivityId);
|
||||
FishingEventCycleItem cycle = _tables.TbFishingEventCycleItem.GetOrDefault(fishingEvent.RedirectID);
|
||||
|
||||
digActivityConfig = _tables.TbDiggingActivityInit.Get(cycle.RedirectID);
|
||||
MaxCount = digActivityConfig.StageCount;
|
||||
}
|
||||
|
||||
string countJson = saveStore.Read(DigCountKey).ValueOrThrow();
|
||||
string digGridDataJson = saveStore.Read(DigGridDataKey).ValueOrThrow();
|
||||
DoServerData(countJson, digGridDataJson);
|
||||
await InitClientData(ClientDataHandleEnum.Init);
|
||||
}
|
||||
private void DoServerData(string countJson, string digGridDataJson)
|
||||
{
|
||||
if (_curActivityId <= 0)
|
||||
{
|
||||
SaveNull();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
var tables = GContext.container.Resolve<Tables>();
|
||||
|
||||
FishingEvent acInfo = GContext.container.Resolve<DiggingGameManager>().GetActivityConfig(_curActivityId);
|
||||
FishingEventCycleItem cycle = tables.TbFishingEventCycleItem.GetOrDefault(acInfo.RedirectID);
|
||||
int redirectID = cycle.RedirectID;
|
||||
redDot = cycle.RedDot;
|
||||
|
||||
DiggingActivityInit digActivityConfig = GContext.container.Resolve<Tables>().TbDiggingActivityInit.Get(redirectID);
|
||||
if (digActivityConfig == null)
|
||||
{
|
||||
SaveNull();
|
||||
return;
|
||||
}
|
||||
_allClamps = digActivityConfig.StageList;
|
||||
|
||||
if (!string.IsNullOrEmpty(countJson))
|
||||
{
|
||||
try
|
||||
{
|
||||
SaveDigCountJson data = Newtonsoft.Json.JsonConvert.DeserializeObject<SaveDigCountJson>(countJson);
|
||||
if (data.ActivityId == _curActivityId)
|
||||
{
|
||||
_serverDigCount = data.DigCount;
|
||||
DiggingGameManager.LogError("玩家铲子个数 :::" + _serverDigCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
SaveNull();
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
SaveNull();
|
||||
Debug.LogError(e);
|
||||
}
|
||||
}
|
||||
_serverData = null;
|
||||
_allServerGridData.Clear();
|
||||
if (!string.IsNullOrEmpty(digGridDataJson))
|
||||
{
|
||||
SaveDataJson data = Newtonsoft.Json.JsonConvert.DeserializeObject<SaveDataJson>(digGridDataJson);
|
||||
if (data.ActivityId == _curActivityId)
|
||||
{
|
||||
_serverData = data.GetSerializableData();
|
||||
_allServerGridData = data.GetSerializableGridData().ToList();
|
||||
InflationRate = data.InflationRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
SaveNull();
|
||||
}
|
||||
}
|
||||
if (_serverData == null || (!_allClamps.Contains(_serverData.ClampId) && _serverData.ClampId > 0))
|
||||
{
|
||||
InflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
_serverData = new SandDigServerData();
|
||||
_serverData.Index = 0;
|
||||
_allServerGridData.Clear();
|
||||
}
|
||||
int index = _serverData.Index % MaxCount;
|
||||
_serverData.ClampId = _allClamps[index];
|
||||
SetRedPoint();
|
||||
//#if UNITY_EDITOR
|
||||
// //测试
|
||||
// _allServerGridData = new List<ServerGridData>();
|
||||
// _allServerGridPropsData = new List<ServerGridPropsData>();
|
||||
// _serverData = new SandDigServerData();
|
||||
// _serverData.ClampId = _allClamps[1];
|
||||
//#endif
|
||||
}
|
||||
}
|
||||
public void GMSetClamp(int index)
|
||||
{
|
||||
if (_curActivityId <= 0 || _serverData == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
_serverData.Index = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_serverData.Index = index;
|
||||
}
|
||||
NewData();
|
||||
}
|
||||
public async Task<bool> InitClientData(ClientDataHandleEnum handleType)
|
||||
{
|
||||
if (_serverData == null || _serverData.ClampId <= 0)
|
||||
return false;
|
||||
DiggingGameManager.LogError("初始化客户端管卡数据");
|
||||
DiggingStageReward config = GContext.container.Resolve<Tables>().TbDiggingStageReward.Get(_serverData.ClampId);
|
||||
string JsonPath = config.StageName;// $"Assets/ABPackage/SandDig/ClampJson/Clamp{_serverData.ClampId}";
|
||||
|
||||
GContext.container.Resolve<DiggingGameManager>().GameState = SandDigGameState.LoadJson;
|
||||
AsyncOperationHandle<TextAsset> handle = Addressables.LoadAssetAsync<TextAsset>(JsonPath);
|
||||
TaskCompletionSource<bool> taskCompletion = new TaskCompletionSource<bool>();
|
||||
handle.Completed += (AsyncOperationHandle<TextAsset> obj) =>
|
||||
{
|
||||
if (obj.Status == AsyncOperationStatus.Succeeded)
|
||||
{
|
||||
DiggingGameManager.LogError("新管卡数据加载完毕");
|
||||
_dinggingSceneData = JsonConvert.DeserializeObject<DinggingSceneData>(obj.Result.ToString());
|
||||
_dinggingSceneData.AnalysisData(_allServerGridData);
|
||||
// 释放资源
|
||||
Addressables.Release(handle);
|
||||
if (handleType == ClientDataHandleEnum.PassClamp)
|
||||
{
|
||||
//可以切换了
|
||||
GContext.container.Resolve<DiggingGameManager>().GameState = SandDigGameState.WaitPassLoading;
|
||||
}
|
||||
else if (handleType == ClientDataHandleEnum.EnterGame)
|
||||
{
|
||||
GContext.container.Resolve<DiggingGameManager>().GameState = SandDigGameState.LoadJsonOver;
|
||||
}
|
||||
else if (handleType == ClientDataHandleEnum.Init)
|
||||
{
|
||||
InitCheckClampPass();
|
||||
}
|
||||
taskCompletion.SetResult(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
taskCompletion.SetResult(false);
|
||||
DiggingGameManager.LogError("Failed to load asset: " + JsonPath);
|
||||
}
|
||||
};
|
||||
return await taskCompletion.Task;
|
||||
}
|
||||
public void InitCheckClampPass()
|
||||
{
|
||||
List<int> allHaveProps = new List<int>(GetAllPassIds());
|
||||
PropData[] allData = GetAllPropData();
|
||||
bool allFinded = true;
|
||||
|
||||
for (int i = 0; i < allData.Length; i++)
|
||||
{
|
||||
var propData = allData[i];
|
||||
if (!allHaveProps.Contains(propData.PropId))
|
||||
{
|
||||
allFinded = false;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
allHaveProps.Remove(propData.PropId);
|
||||
}
|
||||
}
|
||||
if (!allFinded)
|
||||
{
|
||||
allFinded = true;
|
||||
GridData[] gridDatas = GetAllGridData();
|
||||
for (int i = 0; i < gridDatas.Length; i++)
|
||||
{
|
||||
GridData gridData = gridDatas[i];
|
||||
if (gridData.IsBorder || gridData.IsOpen)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
allFinded = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allFinded)
|
||||
{
|
||||
//DiggingStageReward config = GContext.container.Resolve<Tables>().TbDiggingStageReward.Get(_serverData.ClampId);
|
||||
_serverData.Index++;
|
||||
NewData();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 通过当前管卡发奖
|
||||
/// </summary>
|
||||
public void PassClamp()
|
||||
{
|
||||
GContext.container.Resolve<DiggingGameManager>().ClampIsPass = true;
|
||||
DiggingStageReward config = GContext.container.Resolve<Tables>().TbDiggingStageReward.Get(_serverData.ClampId);
|
||||
|
||||
var playerData = GContext.container.Resolve<PlayerItemData>();
|
||||
|
||||
DiggingGameManager.LogError("获得的Drop奖励 :::" + config.DropID);
|
||||
ChangeSource changeSource = new ChangeSource(CurActivityId, "Event_Minigame1", "Event_Minigame1_Dig", "StageReward");
|
||||
|
||||
GContext.container.Resolve<DiggingGameManager>().itemDatas = playerData.AddItemByDropLureInflation(InflationRate, config.DropID, changeSource);
|
||||
|
||||
_serverData.Index++;
|
||||
|
||||
//if (_serverData.Index >= MaxCount && _serverData.Index % MaxCount == 0)
|
||||
//{
|
||||
// GContext.container.Resolve<DiggingGameManager>().itemDatas = playerData.AddItemByDropLureInflation(InflationRate, digActivityConfig.Reward);
|
||||
//}
|
||||
NewData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在线进入新关卡,数据初始化
|
||||
/// </summary>
|
||||
public void NewData()
|
||||
{
|
||||
int index = _serverData.Index % MaxCount;
|
||||
_serverData.ClampId = _allClamps[index];
|
||||
_serverData.ClickCount = 0;
|
||||
_serverData.Reset();
|
||||
_allServerGridData.Clear();
|
||||
_dinggingSceneData?.Reset();
|
||||
SaveAll();
|
||||
}
|
||||
public void NewClampInit()
|
||||
{
|
||||
GContext.Publish(new EventSandDigNewClamp(_serverData.ClampId));
|
||||
if (_serverData.ClampId <= 0)
|
||||
{
|
||||
//通关所有关卡
|
||||
DiggingGameManager.LogError("通关所有关卡!!!!!!!");
|
||||
}
|
||||
else
|
||||
{
|
||||
InitClientData(ClientDataHandleEnum.PassClamp);
|
||||
}
|
||||
}
|
||||
|
||||
#region 数据接口
|
||||
|
||||
/// <summary>
|
||||
/// 使用道具
|
||||
/// </summary>
|
||||
public void UseDig()
|
||||
{
|
||||
_serverDigCount--;
|
||||
_serverData.ClickCount++;
|
||||
SaveDigCount();
|
||||
|
||||
GContext.Publish(new EventSandDigPropUpdate());
|
||||
}
|
||||
/// <summary>
|
||||
/// 增加道具
|
||||
/// </summary>
|
||||
/// <param name="count"></param>
|
||||
public void AddDigCount(int count)
|
||||
{
|
||||
_serverDigCount += count;
|
||||
GContext.Publish(new EventSandDigPropUpdate());
|
||||
SaveDigCount();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获得目标道具
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
public void AddProp(int id)
|
||||
{
|
||||
_serverData.PassPropIds.Add(id);
|
||||
}
|
||||
|
||||
public List<int> GetAllPassIds()
|
||||
{
|
||||
return _serverData.PassPropIds;
|
||||
}
|
||||
|
||||
public DinggingSceneData GetBroadData()
|
||||
{
|
||||
return _dinggingSceneData;
|
||||
}
|
||||
|
||||
public BroadData[] GetAllBroadData()
|
||||
{
|
||||
return _dinggingSceneData.GetAllBroadData();
|
||||
}
|
||||
|
||||
public GridData[] GetAllGridData()
|
||||
{
|
||||
return _dinggingSceneData.GetAllGridData();
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取一个格子数据
|
||||
/// </summary>
|
||||
/// <param name="row"></param>
|
||||
/// <param name="col"></param>
|
||||
/// <returns></returns>
|
||||
public GridData GetGridData(int row, int col)
|
||||
{
|
||||
return _dinggingSceneData.GetGridData(row, col);
|
||||
}
|
||||
|
||||
public PropData[] GetAllPropData()
|
||||
{
|
||||
return _dinggingSceneData.GetAllPropData();
|
||||
}
|
||||
|
||||
public List<PropTempData> GetOnePropTempData()
|
||||
{
|
||||
int index = _serverData.Index / MaxCount;
|
||||
return _dinggingSceneData.GetAllPropTempData(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取道具个数
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int GetDigCount()
|
||||
{
|
||||
return _serverDigCount;
|
||||
}
|
||||
|
||||
public int CurIndex()
|
||||
{
|
||||
return _serverData.Index;
|
||||
}
|
||||
public SandDigServerData GetServerData()
|
||||
{
|
||||
return _serverData;
|
||||
}
|
||||
|
||||
public int GetCurClampId()
|
||||
{
|
||||
return _serverData.ClampId;
|
||||
}
|
||||
public int GetClickCount()
|
||||
{
|
||||
return _serverData.ClickCount;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region 数据存储
|
||||
|
||||
public void SaveDigCount()
|
||||
{
|
||||
SetRedPoint();
|
||||
SaveDigCountJson dataJson = new SaveDigCountJson();
|
||||
dataJson.ActivityId = _curActivityId;
|
||||
dataJson.DigCount = _serverDigCount;
|
||||
string json = JsonConvert.SerializeObject(dataJson);
|
||||
saveStore.Write(DigCountKey, json).RequireAccepted();
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(dataJson.ActivityId, dataJson.DigCount);
|
||||
}
|
||||
void SetRedPoint()
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState("Home.Digging", _serverData != null && _serverDigCount > 0, _serverDigCount);
|
||||
}
|
||||
public void SaveData()
|
||||
{
|
||||
//通用数据 + 任务目标数据
|
||||
SaveDataJson dataJson = new SaveDataJson();
|
||||
dataJson.ActivityId = _curActivityId;
|
||||
dataJson.SandDigServerData = _serverData.ToJson();
|
||||
dataJson.InflationRate = InflationRate;
|
||||
//网格数据
|
||||
GridData[] allGridData = _dinggingSceneData?.GetAllGridData() ?? new GridData[0];
|
||||
string allGridStr = allGridData.Length > 0 ? "[" : string.Empty;
|
||||
|
||||
for (int i = 0; i < allGridData.Length; i++)
|
||||
{
|
||||
if (i == allGridData.Length - 1)
|
||||
{
|
||||
allGridStr += allGridData[i].ToJson() + "]";
|
||||
}
|
||||
else
|
||||
{
|
||||
allGridStr += allGridData[i].ToJson() + ",";
|
||||
}
|
||||
}
|
||||
dataJson.AllGridJson = allGridStr;
|
||||
|
||||
string json = JsonConvert.SerializeObject(dataJson);
|
||||
//DiggingGameManager.LogError(json);
|
||||
saveStore.Write(DigGridDataKey, json).RequireAccepted();
|
||||
}
|
||||
|
||||
public void SaveAll()
|
||||
{
|
||||
this.SaveDigCount();
|
||||
this.SaveData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空上一期活动数据
|
||||
/// </summary>
|
||||
private void SaveNull()
|
||||
{
|
||||
_serverDigCount = GContext.container.Resolve<FishingEventData>().GetInitWelcomeGift(_curActivityId);
|
||||
SaveDigCount();
|
||||
saveStore.Write(DigGridDataKey, string.Empty).RequireAccepted();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,706 +0,0 @@
|
||||
using asap.core;
|
||||
using com.fpnn;
|
||||
using com.fpnn.rtm;
|
||||
using GameCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UniRx;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public class ChatQuestProcessor : RTMQuestProcessor
|
||||
{
|
||||
public Func<bool> CanRelogin;
|
||||
public override void SessionClosed(int ClosedByErrorCode)
|
||||
{
|
||||
Debug.Log("SessionClosed errorCode = " + ClosedByErrorCode);
|
||||
}
|
||||
|
||||
public override bool ReloginWillStart(int lastErrorCode, int retriedCount)
|
||||
{
|
||||
Debug.Log("ReloginWillStart lastErrorCode = " + lastErrorCode + ", retriedCount = " + retriedCount);
|
||||
return CanRelogin?.Invoke() == true;
|
||||
}
|
||||
|
||||
public override void Kickout()
|
||||
{
|
||||
Debug.Log("Kickout");
|
||||
}
|
||||
|
||||
public override void KickoutRoom(long roomId)
|
||||
{
|
||||
Debug.Log("KickoutRoom roomId = " + roomId);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ChatMessage : IComparable<ChatMessage>
|
||||
{
|
||||
public ChatMessage(long messageId, long fromUid, string message, long mtime, long cursorId = 0)
|
||||
{
|
||||
this.messageId = messageId;
|
||||
this.fromUid = fromUid;
|
||||
this.message = message;
|
||||
this.mtime = mtime;
|
||||
this.cursorId = cursorId;
|
||||
}
|
||||
public static ChatMessage ConvertoFromRTMMessage(RTMMessage message)
|
||||
{
|
||||
ChatMessage chatDemoMessage = new ChatMessage(message.messageId, message.fromUid, message.stringMessage, message.modifiedTime);
|
||||
return chatDemoMessage;
|
||||
}
|
||||
|
||||
public static ChatMessage ConvertoFromHistoryMessage(HistoryMessage message)
|
||||
{
|
||||
ChatMessage chatDemoMessage = new ChatMessage(message.messageId, message.fromUid, message.stringMessage, message.modifiedTime, message.cursorId);
|
||||
return chatDemoMessage;
|
||||
}
|
||||
|
||||
public int CompareTo(ChatMessage message)
|
||||
{
|
||||
if (mtime > message.mtime)
|
||||
return 1;
|
||||
else if (mtime < message.mtime)
|
||||
return -1;
|
||||
else
|
||||
{
|
||||
if (messageId >= message.messageId)
|
||||
return 1;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
public long messageId;
|
||||
public long fromUid;
|
||||
public string message;
|
||||
public long mtime;
|
||||
public long cursorId;
|
||||
}
|
||||
|
||||
public class ChatMessageCompare : IEqualityComparer<ChatMessage>
|
||||
{
|
||||
public bool Equals(ChatMessage msg1, ChatMessage msg2)
|
||||
{
|
||||
if (msg1.mtime == msg2.mtime && msg1.messageId == msg2.messageId)
|
||||
{
|
||||
if (msg1.cursorId == msg2.cursorId)
|
||||
return true;
|
||||
else if ((msg1.cursorId == 0 && msg2.cursorId != 0) || (msg1.cursorId != 0 && msg2.cursorId == 0))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public int GetHashCode(ChatMessage msg)
|
||||
{
|
||||
if (msg == null)
|
||||
return 0;
|
||||
else
|
||||
return string.Format("{0}_{1}_{2}", msg.mtime, msg.messageId, msg.message).GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ChatConversation
|
||||
{
|
||||
//public Int32 unreadCount;
|
||||
public Int64 lastMessageTime;
|
||||
//public ChatMessage lastMessage;
|
||||
|
||||
//public void AddMessage(ChatMessage message)
|
||||
//{
|
||||
// if (lastMessage == null)
|
||||
// {
|
||||
// lastMessage = message;
|
||||
// return;
|
||||
// }
|
||||
// if (message.mtime > lastMessage.mtime)
|
||||
// {
|
||||
// lastMessage = message;
|
||||
// return;
|
||||
// }
|
||||
// else if (message.mtime == lastMessage.mtime && lastMessage.messageId > message.messageId)
|
||||
// {
|
||||
// lastMessage = message;
|
||||
// return;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
public class ChatChatData
|
||||
{
|
||||
public Int64 lastId;
|
||||
public List<ChatMessage> messageList;
|
||||
}
|
||||
|
||||
public class ChatChangeEvent
|
||||
{
|
||||
public bool nextContent;
|
||||
public bool push;
|
||||
}
|
||||
|
||||
public enum ChatType
|
||||
{
|
||||
Text = 1,//文本消息
|
||||
System = 2,//系统内提示
|
||||
System2 = 3,//解锁地图
|
||||
Help = 4,//请求照片
|
||||
Emoji = 5,//发送表情
|
||||
HelpEnergy = 6//请求体力帮助
|
||||
}
|
||||
|
||||
public class ChatService : IChatService,IDisposable
|
||||
{
|
||||
private IUserService userService { get; set; }
|
||||
private IConfig config { get; set; }
|
||||
private int sendInterval = 2;
|
||||
long groupConversationId = 0;
|
||||
RTMClient client;
|
||||
ChatConversation chatConversation;
|
||||
ChatChatData chat;
|
||||
readonly CancellationToken loginLifetime;
|
||||
readonly CancellationTokenRegistration closeOnLogout;
|
||||
bool disposed;
|
||||
bool loginStarted;
|
||||
bool IsCurrent => !disposed && !loginLifetime.IsCancellationRequested && userService.IsAuthorized;
|
||||
|
||||
public ChatService(IUserService userService, IConfig config, cfg.Tables tables)
|
||||
{
|
||||
this.userService = userService;
|
||||
loginLifetime = userService.LoginLifetime;
|
||||
closeOnLogout = loginLifetime.Register(() => client?.Close());
|
||||
this.config = config;
|
||||
this.sendInterval = tables.TbServerConfig.ClubChatLimitTime;
|
||||
ClientEngine.Init();
|
||||
RTMControlCenter.Init();
|
||||
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
disposed = true;
|
||||
disposable?.Dispose();
|
||||
closeOnLogout.Dispose();
|
||||
client?.Close();
|
||||
client = null;
|
||||
}
|
||||
|
||||
private IDisposable disposable;
|
||||
public void WaitingForLogin()
|
||||
{
|
||||
if (disposed || loginLifetime.IsCancellationRequested || loginStarted || disposable != null) return;
|
||||
disposable = RootCtx.OnEvent<EvtAPISvrLoginSuccess>().Subscribe(OnApiSvrLoginSuccess);
|
||||
if (userService.IsAuthorized) OnApiSvrLoginSuccess(default);
|
||||
}
|
||||
|
||||
private async void OnApiSvrLoginSuccess(EvtAPISvrLoginSuccess evt)
|
||||
{
|
||||
if (!IsCurrent || loginStarted) return;
|
||||
loginStarted = true;
|
||||
disposable?.Dispose();
|
||||
disposable = null;
|
||||
try
|
||||
{
|
||||
int retryCount = 0;
|
||||
while (IsCurrent && !await LoginRTM())
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
retryCount++;
|
||||
await Task.Delay(Math.Min(5000 + retryCount * 1000, 10000), loginLifetime);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) when (loginLifetime.IsCancellationRequested) { }
|
||||
catch (Exception ex) { if (IsCurrent) Debug.LogException(ex); }
|
||||
}
|
||||
|
||||
private async Task<bool> LoginRTM(int timeout = 5)
|
||||
{
|
||||
if (!IsCurrent) return false;
|
||||
var userIdLong = userService.InternalId;
|
||||
var pid = long.Parse(config.Get(GConstant.K_RTMP_ID, "80000560"));
|
||||
var rtmServerEndpoint = config.Get(GConstant.K_RTM_Server_Endpoint, "rtm-nx-front.ilivedata.com:13321");
|
||||
var hmacSecret = config.Get(GConstant.K_RTM_Hmac_Secret, "7a1ba6bb85a4473d96fa4bf45b01f1dd");
|
||||
ChatQuestProcessor processor = new ChatQuestProcessor();
|
||||
InitProcessor(processor);
|
||||
client = RTMClient.getInstance(rtmServerEndpoint, pid, userIdLong, processor);
|
||||
|
||||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||||
using var cancelled = loginLifetime.Register(() => taskCompletionSource.TrySetCanceled());
|
||||
|
||||
string token = GetToken(pid, hmacSecret, userIdLong, out long ts);
|
||||
|
||||
client.Login((long pid, long uid, bool successful, int errorCode) =>
|
||||
{
|
||||
if (!IsCurrent) { taskCompletionSource.TrySetResult(false); return; }
|
||||
if (successful)
|
||||
{
|
||||
taskCompletionSource.TrySetResult(true);
|
||||
//RTMControlCenter.callbackQueue.PostAction(() =>
|
||||
//{
|
||||
// GetConversationList();
|
||||
//});
|
||||
}
|
||||
else
|
||||
{
|
||||
taskCompletionSource.TrySetResult(false);
|
||||
Debug.Log("Login failed, errorCode = " + errorCode);
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("ilivedata"))
|
||||
{
|
||||
e.AddContent("connection_failure", errorCode)
|
||||
.AddContent("pid", pid)
|
||||
.AddContent("uid", uid)
|
||||
.AddContent("userIdLong", userIdLong);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}, token, ts, timeout);
|
||||
|
||||
return await (taskCompletionSource.Task);
|
||||
}
|
||||
|
||||
string GetToken(long pid, string hmacSecret, long uid, out long ts)
|
||||
{
|
||||
ts = ClientEngine.GetCurrentSeconds();
|
||||
string text = pid.ToString() + ":" + uid.ToString() + ":" + ts.ToString();
|
||||
|
||||
using (var hmacsha256 = new HMACSHA256(Convert.FromBase64String(hmacSecret)))
|
||||
{
|
||||
var hash = hmacsha256.ComputeHash(Encoding.UTF8.GetBytes(text));
|
||||
return Convert.ToBase64String(hash);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<HashSet<long>> GetOnlineUsers(List<long> playerIDs)
|
||||
{
|
||||
HashSet<long> longs = new HashSet<long>();
|
||||
|
||||
if (!IsCurrent || client?.Status != RTMClient.ClientStatus.Connected)
|
||||
return longs;
|
||||
|
||||
var taskCompletionSource = new TaskCompletionSource<HashSet<long>>();
|
||||
using var cancelled = loginLifetime.Register(() => taskCompletionSource.TrySetResult(null));
|
||||
longs = new HashSet<long>(playerIDs);
|
||||
client.GetOnlineUsers((HashSet<long> uids, int errorCode) =>
|
||||
{
|
||||
if (IsCurrent && errorCode == 0)
|
||||
{
|
||||
taskCompletionSource.TrySetResult(uids);
|
||||
}
|
||||
else
|
||||
{
|
||||
taskCompletionSource.TrySetResult(null);
|
||||
}
|
||||
}, longs);
|
||||
return await taskCompletionSource.Task;
|
||||
}
|
||||
|
||||
public void GetConversationList(long groupConversationId)
|
||||
{
|
||||
this.groupConversationId = groupConversationId;
|
||||
|
||||
if (!IsCurrent || client?.Status != RTMClient.ClientStatus.Connected)
|
||||
return;
|
||||
|
||||
RTMControlCenter.callbackQueue.PostAction(() =>
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
GetConversationList();
|
||||
});
|
||||
}
|
||||
|
||||
void InitProcessor(ChatQuestProcessor processor)
|
||||
{
|
||||
processor.CanRelogin = () => IsCurrent;
|
||||
processor.PushGroupChatCallback = OnPushGroupChat;
|
||||
processor.PushGroupMessageCallback = OnPushGroupMessage;
|
||||
//processor.PushMessageCallback = OnPushGroupMessage;
|
||||
//processor.PushChatCallback = OnPushGroupChat;
|
||||
processor.ReloginCompletedCallback = OnRelogin;
|
||||
}
|
||||
|
||||
void OnPushGroupChat(RTMMessage message)
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
if (message.toId == groupConversationId)
|
||||
{
|
||||
AddMessage(groupConversationId, true);
|
||||
}
|
||||
//else if (message.toId == userIdLong)
|
||||
//{
|
||||
// //个人聊天列表
|
||||
|
||||
//}
|
||||
}
|
||||
|
||||
void OnPushGroupMessage(RTMMessage message)
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
if (message.toId == groupConversationId)
|
||||
{
|
||||
//主界面推送消息
|
||||
|
||||
}
|
||||
//else if (message.toId == userIdLong)
|
||||
//{
|
||||
// //个人聊天列表
|
||||
|
||||
//}
|
||||
}
|
||||
|
||||
void OnRelogin(bool successful, bool retryAgain, int errorCode, int retriedCount)
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
//Debug.Log("OnRelogin successful = " + successful + " retryAgain = " + retryAgain + ", errorCode = " + errorCode + ", retriedCount = " + retriedCount);
|
||||
if (successful && chatConversation != null)
|
||||
{
|
||||
client?.GetGroupUnreadConversationList((List<Conversation> groupConversationList, int errorCode) =>
|
||||
{
|
||||
if (IsCurrent && errorCode == 0)
|
||||
{
|
||||
InitConversationList(groupConversationList);
|
||||
}
|
||||
}, startTime: chatConversation.lastMessageTime);
|
||||
}
|
||||
}
|
||||
|
||||
void GetConversationList()
|
||||
{
|
||||
if (!IsCurrent || client == null) return;
|
||||
long uid = client.Uid;
|
||||
client?.GetGroupConversationList((List<Conversation> groupConversationList, int errorCode) =>
|
||||
{
|
||||
if (IsCurrent && errorCode == 0)
|
||||
{
|
||||
InitConversationList(groupConversationList);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void InitConversationList(List<Conversation> list)
|
||||
{
|
||||
RTMControlCenter.callbackQueue.PostAction(() =>
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
foreach (Conversation conversation in list)
|
||||
{
|
||||
if (conversation.id == groupConversationId)
|
||||
{
|
||||
if (chatConversation == null)
|
||||
{
|
||||
chatConversation = new ChatConversation();
|
||||
}
|
||||
//chatConversation.unreadCount += conversation.unreadCount;
|
||||
//if (conversation.lastMessage != null && conversation.lastMessage.messageId != 0)
|
||||
// chatConversation.AddMessage(ChatMessage.ConvertoFromHistoryMessage(conversation.lastMessage));
|
||||
UpdateConversationList(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void UpdateConversationList(bool push)
|
||||
{
|
||||
if (chatConversation == null)
|
||||
return;
|
||||
//client?.GetGroupMessage((int count, long lastCursorId, long beginMsec, long endMsec, List<HistoryMessage> messages, int errorCode) =>
|
||||
//{
|
||||
// if (IsCurrent && errorCode == 0)
|
||||
// {
|
||||
// InitChat(lastCursorId, messages);
|
||||
// }
|
||||
//}, groupConversationId, true, 10);
|
||||
//两个接口一样 GetGroupMessage多一个指定类型
|
||||
client?.GetGroupChat((int count, long lastCursorId, long beginMsec, long endMsec, List<HistoryMessage> messages, int errorCode) =>
|
||||
{
|
||||
if (IsCurrent && errorCode == 0)
|
||||
{
|
||||
InitChat(lastCursorId, messages, push, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("GetGroupChat errorCode = " + errorCode);
|
||||
}
|
||||
}, groupConversationId, true, 10);
|
||||
}
|
||||
|
||||
void InitChat(long lastId, List<HistoryMessage> messages, bool push, bool nextContent)
|
||||
{
|
||||
RTMControlCenter.callbackQueue.PostAction(() =>
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
if (chat == null)
|
||||
{
|
||||
chat = new ChatChatData();
|
||||
chat.messageList = new List<ChatMessage>();
|
||||
}
|
||||
if (messages != null)
|
||||
{
|
||||
if ((chat.lastId == 0 || lastId < chat.lastId) && lastId != 0)
|
||||
chat.lastId = lastId;
|
||||
|
||||
foreach (HistoryMessage message in messages)
|
||||
{
|
||||
chat.messageList.Add(ChatMessage.ConvertoFromHistoryMessage(message));
|
||||
}
|
||||
chat.messageList = chat.messageList.Distinct(new ChatMessageCompare()).ToList();
|
||||
chat.messageList.Sort();
|
||||
GContext.Publish(new ChatChangeEvent() { nextContent = nextContent, push = push });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async void AddMessage(long conversationId, bool push)
|
||||
{
|
||||
await Task.Delay(100);//服务器聊天记录存储延时
|
||||
RTMControlCenter.callbackQueue.PostAction(() =>
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
if (conversationId == groupConversationId)
|
||||
{
|
||||
if (chatConversation == null)
|
||||
{
|
||||
chatConversation = new ChatConversation();
|
||||
//chatConversation.unreadCount = 1;
|
||||
//chatConversation.lastMessage = message;
|
||||
}
|
||||
//else
|
||||
//{
|
||||
// chatConversation.AddMessage(message);
|
||||
//}
|
||||
UpdateConversationList(push);
|
||||
//if (chat == null)
|
||||
// return;
|
||||
//chat.messageList.Add(message);
|
||||
//chat.messageList = chat.messageList.Distinct(new ChatMessageCompare()).ToList();
|
||||
//chat.messageList.Sort();
|
||||
//GContext.Publish(new ChatChangeEvent());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async Task<long> CanSend(string message)
|
||||
{
|
||||
if (!IsCurrent || client == null || groupConversationId == 0)
|
||||
return 0;
|
||||
var taskCompletionSource = new TaskCompletionSource<long>();
|
||||
using var cancelled = loginLifetime.Register(() => taskCompletionSource.TrySetResult(0));
|
||||
message = string.Format("{0}#{1}", userService.UserId, message);
|
||||
client?.SendGroupChat((long messageId, long mtime, int errorCode) =>
|
||||
{
|
||||
if (!IsCurrent) { taskCompletionSource.TrySetResult(0); return; }
|
||||
if (errorCode != 0)
|
||||
{
|
||||
taskCompletionSource.TrySetResult(0);
|
||||
Debug.Log("SendGroupChat errorCode = " + errorCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
taskCompletionSource.TrySetResult(messageId);
|
||||
RTMControlCenter.callbackQueue.PostAction(() =>
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
AddMessage(groupConversationId, false);
|
||||
if (chatConversation != null) chatConversation.lastMessageTime = mtime;
|
||||
});
|
||||
}
|
||||
}, groupConversationId, message);
|
||||
long messageId = await taskCompletionSource.Task;
|
||||
return messageId;
|
||||
}
|
||||
|
||||
async Task<long> Send(string message)
|
||||
{
|
||||
if (!IsCurrent || client == null || groupConversationId == 0)
|
||||
return 0;
|
||||
|
||||
if (!CanSend())
|
||||
{
|
||||
ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_42"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
var taskCompletionSource = new TaskCompletionSource<long>();
|
||||
using var cancelled = loginLifetime.Register(() => taskCompletionSource.TrySetResult(0));
|
||||
message = string.Format("{0}#{1}", userService.UserId, message);
|
||||
client?.SendGroupChat((long messageId, long mtime, int errorCode) =>
|
||||
{
|
||||
if (!IsCurrent) { taskCompletionSource.TrySetResult(0); return; }
|
||||
if (errorCode != 0)
|
||||
{
|
||||
taskCompletionSource.TrySetResult(0);
|
||||
Debug.Log("SendGroupChat errorCode = " + errorCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
taskCompletionSource.TrySetResult(messageId);
|
||||
RTMControlCenter.callbackQueue.PostAction(() =>
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
AddMessage(groupConversationId, false);
|
||||
if (chatConversation != null) chatConversation.lastMessageTime = mtime;
|
||||
});
|
||||
}
|
||||
}, groupConversationId, message);
|
||||
long messageId = await taskCompletionSource.Task;
|
||||
return messageId;
|
||||
}
|
||||
|
||||
Task sendTask = null;
|
||||
private bool CanSend()
|
||||
{
|
||||
if (sendTask == null || sendTask.IsCompleted)
|
||||
{
|
||||
sendTask = Task.Delay(TimeSpan.FromSeconds(sendInterval));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void NextPage()
|
||||
{
|
||||
if (!IsCurrent || client == null || groupConversationId == 0)
|
||||
return;
|
||||
if (chat == null)
|
||||
return;
|
||||
if (chat.messageList.Count >= GContext.container.Resolve<cfg.Tables>().TbServerConfig.ClubChatMaxCount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
client?.GetGroupChat((int count, long lastCursorId, long beginMsec, long endMsec, List<HistoryMessage> messages, int errorCode) =>
|
||||
{
|
||||
if (IsCurrent && errorCode == 0)
|
||||
{
|
||||
InitChat(lastCursorId, messages, false, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("GetGroupChat errorCode = " + errorCode);
|
||||
}
|
||||
}, groupConversationId, true, 10, lastId: chat.lastId);
|
||||
}
|
||||
|
||||
public void OnQuit()
|
||||
{
|
||||
chat = null;
|
||||
chatConversation = null;
|
||||
groupConversationId = 0;
|
||||
}
|
||||
|
||||
public void InitChatData()
|
||||
{
|
||||
chat = null;
|
||||
}
|
||||
|
||||
public ChatChatData GetGroupChat()
|
||||
{
|
||||
return chat;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析消息类型
|
||||
/// </summary>
|
||||
/// <param name="chat"></param>
|
||||
/// <returns>1.文本消息 2.系统消息(没有点赞)3.系统消息 4.求助帖 5.图片</returns>
|
||||
public string[] ParseChat(string chat)
|
||||
{
|
||||
return chat.Split('#');
|
||||
}
|
||||
|
||||
public async Task<long> SendHelpEnergyMessage(int itemId)
|
||||
{
|
||||
string chat = $"{(int)ChatType.HelpEnergy}#{itemId}";
|
||||
return await CanSend(chat);
|
||||
}
|
||||
|
||||
public void SendEmoji(string chat)
|
||||
{
|
||||
chat = $"{(int)ChatType.Emoji}#{chat}";
|
||||
_ = Send(chat);
|
||||
}
|
||||
|
||||
public async Task<long> SendGetPhotoMessage(string chat)
|
||||
{
|
||||
chat = $"{(int)ChatType.Help}#{chat}";
|
||||
return await CanSend(chat);
|
||||
}
|
||||
|
||||
//谁干了什么
|
||||
public void SendSystemMessage2(string chat)
|
||||
{
|
||||
chat = $"{(int)ChatType.System2}#{chat}";
|
||||
_ = CanSend(chat);
|
||||
}
|
||||
|
||||
//俱乐部系统提示
|
||||
public void SendSystemMessage(string chat)
|
||||
{
|
||||
chat = $"{(int)ChatType.System}#{chat}";
|
||||
_ = CanSend(chat);
|
||||
}
|
||||
|
||||
public async void SendTxTMessage(string chat)
|
||||
{
|
||||
//审核
|
||||
bool value = await TextAudit(chat);
|
||||
if (!IsCurrent) return;
|
||||
if (value)
|
||||
{
|
||||
string message = $"{(int)ChatType.Text}#{chat}";
|
||||
var messageId = await Send(message);
|
||||
#if AGG
|
||||
if (messageId > 0)
|
||||
{
|
||||
await Awaiters.NextFrame;
|
||||
if (!IsCurrent) return;
|
||||
using (var e = GEvent.GameEvent("chat_msg"))
|
||||
{
|
||||
e.AddContent("content", chat);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_33"));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> TextAudit(string text)
|
||||
{
|
||||
if (!IsCurrent || client == null) return false;
|
||||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||||
using var cancelled = loginLifetime.Register(() => taskCompletionSource.TrySetResult(false));
|
||||
client.TextCheck((TextCheckResult result, int errorCode) =>
|
||||
{
|
||||
if (!IsCurrent) { taskCompletionSource.TrySetResult(false); return; }
|
||||
if (errorCode != com.fpnn.ErrorCode.FPNN_EC_OK)
|
||||
{
|
||||
taskCompletionSource.TrySetResult(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Debug.Log(" -- result " + result.result);
|
||||
|
||||
//if (result.tags != null)
|
||||
// Debug.Log(" -- tags.Count " + result.tags.Count);
|
||||
//if (result.wlist != null)
|
||||
// Debug.Log(" -- wlist.Count " + result.wlist.Count);
|
||||
taskCompletionSource.TrySetResult(result.result == 0);
|
||||
}
|
||||
}, text);
|
||||
return await taskCompletionSource.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,230 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using UniRx;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
public interface IDeferredRewardStashService
|
||||
{
|
||||
public void DisposeSubscriptions();
|
||||
public void Reset();
|
||||
public void Flush(RewardType rewardType = RewardType.FishBoxFly, bool isShowReward = true);
|
||||
public void UploadData();
|
||||
public void LoadData();
|
||||
public IReadOnlyList<ItemData> Items { get; }
|
||||
public bool IsEmpty { get; }
|
||||
}
|
||||
|
||||
public class DeferredRewardStashService : IDeferredRewardStashService
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
private readonly TbItem _itemTable = GContext.container.Resolve<Tables>().TbItem;
|
||||
private readonly PlayerItemData _playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
private CompositeDisposable _disposables;
|
||||
private List<ItemData> _items;
|
||||
public IReadOnlyList<ItemData> Items => _items;
|
||||
public bool IsEmpty => _items is not { Count: > 0 };
|
||||
private const string Key = "DeferredRewardStashService", Comma = ",", Splitter = "|";
|
||||
|
||||
|
||||
public DeferredRewardStashService()
|
||||
{
|
||||
_disposables = new CompositeDisposable();
|
||||
GContext.OnEvent<EventStashDrop>().Subscribe(StashItemsByDrop).AddTo(_disposables);
|
||||
GContext.OnEvent<EventStashItems>().Subscribe(StashItems).AddTo(_disposables);
|
||||
GContext.OnEvent<EventStashDropList>().Subscribe(StashItemsByDropList).AddTo(_disposables);
|
||||
GContext.OnEvent<EventStashItem>().Subscribe(e => StashItem(e.Item)).AddTo(_disposables);
|
||||
}
|
||||
|
||||
public void DisposeSubscriptions()
|
||||
{
|
||||
_disposables.Dispose();
|
||||
_disposables = null;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_items = new List<ItemData>();
|
||||
//_items.Clear();
|
||||
}
|
||||
|
||||
public void Flush(RewardType rewardType = RewardType.FishBoxFly, bool isShowReward = true)
|
||||
{
|
||||
Debug.Log("[DeferredRewardStashService] Flush.");
|
||||
string textValue = LocalizationMgr.GetText("UI_MiniBattlePass_DivePopupPanel_8");
|
||||
if (IsEmpty)
|
||||
{
|
||||
rewardType = RewardType.FishBoxFlyRestore;
|
||||
textValue = LocalizationMgr.GetText("UI_FishingRewardPanel_101042");
|
||||
LoadData();
|
||||
}
|
||||
if (IsEmpty)
|
||||
return;
|
||||
if (isShowReward)
|
||||
{
|
||||
ShowData showData = new ShowData(_items, rewardType);
|
||||
showData.textValue = textValue;
|
||||
//UI_FishingRewardPanel_101041
|
||||
game.PlayerDataStorage.PlayerSaveAction.Present(() => GContext.Publish(showData));
|
||||
}
|
||||
_playerItemData.AddItem(_items, null);
|
||||
Reset();
|
||||
UploadData();
|
||||
}
|
||||
|
||||
private void StashItemsByDropList(EventStashDropList e)
|
||||
{
|
||||
foreach (var dropId in e.DropList)
|
||||
{
|
||||
var items = _playerItemData.GetItemDataByDropIdLureInflation(e.Inflate, dropId);
|
||||
foreach (var item in items)
|
||||
{
|
||||
item.changeSource = e.changeSource;
|
||||
StashItem(item);
|
||||
}
|
||||
}
|
||||
UploadData();
|
||||
}
|
||||
|
||||
private void StashItemsByDrop(EventStashDrop e)
|
||||
{
|
||||
var items = _playerItemData.GetItemDataByDropIdLureInflation(e.Inflate, e.DropId);
|
||||
foreach (var item in items)
|
||||
{
|
||||
item.changeSource = e.changeSource;
|
||||
StashItem(item);
|
||||
}
|
||||
UploadData();
|
||||
}
|
||||
|
||||
private void StashItems(EventStashItems e)
|
||||
{
|
||||
var items = e.Items;
|
||||
foreach (var item in items)
|
||||
{
|
||||
StashItem(item);
|
||||
}
|
||||
UploadData();
|
||||
}
|
||||
|
||||
private void StashItem(ItemData item)
|
||||
{
|
||||
if (!_itemTable.DataMap.ContainsKey(item.id))
|
||||
{
|
||||
//Debug.LogError($"Item id {item.id} not found in item table.");
|
||||
return;
|
||||
}
|
||||
// Debug.Log($"[RewardStash] Item {item.id} * {item.count} Stashed.");
|
||||
var itemDropPackageItems = GetItemDropPackageItemList(item);
|
||||
if (itemDropPackageItems != null && itemDropPackageItems.Count > 0)
|
||||
{
|
||||
GContext.container.Resolve<PlayerItemData>().LureInflation(itemDropPackageItems, item.inflate);
|
||||
foreach (var dropItem in itemDropPackageItems)
|
||||
{
|
||||
dropItem.changeSource = item.changeSource;
|
||||
_items.Add(dropItem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_items.Add(item);
|
||||
}
|
||||
|
||||
UploadData();
|
||||
// _playerItemData.AddItem(item);
|
||||
}
|
||||
List<ItemData> GetItemDropPackageItemList(ItemData itemData)
|
||||
{
|
||||
Item item = _itemTable.GetOrDefault(itemData.id);
|
||||
if (item.Type == 9)
|
||||
{
|
||||
switch (item.SubType)
|
||||
{
|
||||
case 1:
|
||||
case 3:
|
||||
case 4:
|
||||
return _playerItemData.GetItemDropPackageItemList(item.RedirectID, itemData.count);
|
||||
case 7:
|
||||
case 8:
|
||||
return _playerItemData.GetItemDropItemList(item.RedirectID, itemData.count);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private string Serialize()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var item in _items)
|
||||
sb.Append(item.id + Comma + (int)item.count + Splitter);
|
||||
if (sb.Length > 0)
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private List<ItemData> Deserialize(string s)
|
||||
{
|
||||
if (s == null || s == "")
|
||||
return new List<ItemData>();
|
||||
var items = s.Split(Splitter);
|
||||
var res = new List<ItemData>();
|
||||
foreach (var item in items)
|
||||
{
|
||||
var split = item.Split(Comma);
|
||||
if (split.Length != 2)
|
||||
{
|
||||
Debug.LogWarning($"[DeferredRewardStashService]Wrong amount of parameters in \"{item}\".");
|
||||
continue;
|
||||
}
|
||||
var id = int.Parse(split[0]);
|
||||
var count = int.Parse(split[1]);
|
||||
var itemData = new ItemData(id, count);
|
||||
res.Add(itemData);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public void UploadData()
|
||||
{
|
||||
var s = Serialize();
|
||||
saveStore.Write(Key, s).RequireAccepted();
|
||||
}
|
||||
|
||||
public void LoadData()
|
||||
{
|
||||
var s = saveStore.Read(Key).ValueOrThrow();
|
||||
Reset();
|
||||
if (string.IsNullOrEmpty(s))
|
||||
return;
|
||||
_items = Deserialize(s);
|
||||
}
|
||||
|
||||
public class EventStashItems
|
||||
{
|
||||
public List<ItemData> Items;
|
||||
}
|
||||
|
||||
public class EventStashDrop
|
||||
{
|
||||
public int DropId;
|
||||
public float Inflate = 0;
|
||||
public ChangeSource changeSource;
|
||||
}
|
||||
|
||||
public class EventStashDropList
|
||||
{
|
||||
public List<int> DropList;
|
||||
public ChangeSource changeSource;
|
||||
public float Inflate = 0;
|
||||
}
|
||||
|
||||
public class EventStashItem
|
||||
{
|
||||
public ItemData Item;
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
using System;
|
||||
public interface IFootPrintService
|
||||
{
|
||||
public EFootPrint FootPrint { get; }
|
||||
public void UpdateFootPrint(EFootPrint newState);
|
||||
public void UploadData();
|
||||
public void LoadData(string data);
|
||||
public bool HasFootPrint(EFootPrint state);
|
||||
}
|
||||
|
||||
// ReSharper disable once ClassNeverInstantiated.Global
|
||||
public class FootPrintService : IFootPrintService
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore SaveStore { get; set; }
|
||||
|
||||
private EFootPrint _footPrint;
|
||||
private ulong _kvCompleteFootPrint;
|
||||
internal ulong CompleteFootPrintForKVValidation => _kvCompleteFootPrint;
|
||||
internal static game.PlayerDataStorage.PlayerKVFootPrintState DecodeForKVLoad(
|
||||
System.Collections.Generic.IReadOnlyDictionary<string, string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors)
|
||||
=> game.PlayerDataStorage.PlayerKVSaveSchema.DecodeFootPrint(raw, errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
_kvCompleteFootPrint = 0;
|
||||
_footPrint = 0;
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVFootPrintState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
_kvCompleteFootPrint = state.Bits;
|
||||
// This legacy view contains only the low 32 bits. The ulong field retains the complete KV state.
|
||||
_footPrint = unchecked((EFootPrint)(int)(uint)state.Bits);
|
||||
}
|
||||
public EFootPrint FootPrint => _footPrint;
|
||||
public void UpdateFootPrint(EFootPrint newState)
|
||||
{
|
||||
_footPrint |= newState;
|
||||
UploadData();
|
||||
}
|
||||
|
||||
public void UploadData()
|
||||
{
|
||||
_kvCompleteFootPrint |= unchecked((uint)(int)_footPrint);
|
||||
var data = _kvCompleteFootPrint;
|
||||
SaveStore.Write("FootPrint", data.ToString()).RequireAccepted();
|
||||
}
|
||||
|
||||
public void LoadData(string data)
|
||||
{
|
||||
_kvCompleteFootPrint = ulong.Parse(data);
|
||||
_footPrint = unchecked((EFootPrint)(int)(uint)_kvCompleteFootPrint);
|
||||
}
|
||||
|
||||
public bool HasFootPrint(EFootPrint state)
|
||||
{
|
||||
return (_footPrint & state) == state;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Record Status. Max out at 64th record.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum EFootPrint : int
|
||||
{
|
||||
HasCommunityGuidancePanelBeenOpened = 1 << 0,
|
||||
IsFaceBookRewardClaimed = 1 << 1,
|
||||
IsInstagramRewardClaimed = 1 << 2,
|
||||
IsDiscordRewardClaimed = 1 << 3,
|
||||
IsTiktokRewardClaimed = 1 << 4,
|
||||
IsYouTubeRewardClaimed = 1 << 5,
|
||||
IsEventCanGuidePoped = 1 << 6,
|
||||
IsFaceBookRewardLink = 1 << 7,
|
||||
}
|
||||
@@ -1,461 +0,0 @@
|
||||
using game.PlayerDataStorage;
|
||||
using asap.core;
|
||||
using asap.playfab.async;
|
||||
using GameCore;
|
||||
using PlayFab.ClientModels;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public class FriendService
|
||||
{
|
||||
[Inject]
|
||||
public IUserService userService { get; set; }
|
||||
|
||||
[Inject]
|
||||
public ICustomServerMgr customServerMgr { get; set; }
|
||||
|
||||
[Inject]
|
||||
public IRTService rts { get; set; }
|
||||
|
||||
[Inject]
|
||||
public cfg.Tables tables { get; set; }
|
||||
|
||||
[Inject] public IPlayerSaveStore SaveStore { get; set; }
|
||||
public bool IsCurrent => SaveStore.IsCurrent && !userService.LoginLifetime.IsCancellationRequested;
|
||||
bool linkRewardPending;
|
||||
|
||||
public const string InviteLinkRewardCountKey = "InviteLinkRewardCount";
|
||||
IDisposable linkCompletedDisposable;
|
||||
|
||||
public void GetFriendData()
|
||||
{
|
||||
//GetFriendLeaderboard();
|
||||
GetFriendList();
|
||||
//GetInvitedListCount();
|
||||
}
|
||||
|
||||
#region 邀请Link相关
|
||||
public int InviteLinkNumber { private set; get; }
|
||||
int InviteLinkRewardCount = 0;
|
||||
|
||||
|
||||
void SetLinkAwardData()
|
||||
{
|
||||
List<cfg.Invite> invites = tables.TbInvite.DataList;
|
||||
//已经领取奖励的数量
|
||||
var read = SaveStore.Read(InviteLinkRewardCountKey);
|
||||
var value = read.ValueOrThrow();
|
||||
if (read.Found == false) InviteLinkRewardCount = 0;
|
||||
else if (!int.TryParse(value, out InviteLinkRewardCount) || InviteLinkRewardCount < 0 || InviteLinkRewardCount > invites.Count)
|
||||
throw new InvalidOperationException("Invalid invitation reward count.");
|
||||
int canRewardCount = InviteLinkRewardCount;
|
||||
for (int i = InviteLinkRewardCount; i < invites.Count; i++)
|
||||
{
|
||||
//领取奖励
|
||||
if (invites[i].NumInvite <= InviteLinkNumber)
|
||||
{
|
||||
canRewardCount = i + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Invite, canRewardCount > InviteLinkRewardCount);
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("invite_increase"))
|
||||
{
|
||||
e.AddContent("inviteprogress", InviteLinkNumber)
|
||||
.AddContent("rewardprogress", InviteLinkRewardCount);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public async Task<int> OnReceiveLinkAward()
|
||||
{
|
||||
if (!IsCurrent) throw new OperationCanceledException();
|
||||
if (linkRewardPending) throw new InvalidOperationException("Invitation reward is pending or requires recovery.");
|
||||
SetLinkAwardData();
|
||||
int canRewardCount = InviteLinkRewardCount;
|
||||
List<cfg.Invite> invites = tables.TbInvite.DataList;
|
||||
List<int> DropIDs = new List<int>();
|
||||
for (int i = InviteLinkRewardCount; i < invites.Count; i++)
|
||||
{
|
||||
//领取奖励
|
||||
if (invites[i].NumInvite <= InviteLinkNumber)
|
||||
{
|
||||
canRewardCount = i + 1;
|
||||
DropIDs.Add(invites[i].DropID);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (InviteLinkRewardCount != canRewardCount)
|
||||
{
|
||||
linkRewardPending = true;
|
||||
var items = GContext.container.Resolve<PlayerItemData>();
|
||||
var player = GContext.container.Resolve<PlayerData>();
|
||||
await PlayerSaveAction.RunAsync(SaveStore, "invite-reward", () =>
|
||||
{
|
||||
items.AddItemByDropList(DropIDs, null);
|
||||
PlayerSaveAction.Present(() => GContext.Publish(new ShowData()));
|
||||
InviteLinkRewardCount = canRewardCount;
|
||||
SaveStore.Write(InviteLinkRewardCountKey, InviteLinkRewardCount.ToString()).RequireAccepted();
|
||||
}, () => player.SavePlayerData(), userService.LoginLifetime);
|
||||
if (!IsCurrent) throw new OperationCanceledException();
|
||||
linkRewardPending = false;
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Invite, false);
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("invite_reward"))
|
||||
{
|
||||
e.AddContent("inviteprogress", InviteLinkNumber)
|
||||
.AddContent("rewardprogress", InviteLinkRewardCount);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return InviteLinkRewardCount;
|
||||
}
|
||||
#endregion 邀请Link相关
|
||||
|
||||
#region 判断是否好友
|
||||
public bool IsFriend(string playFabID)
|
||||
{
|
||||
if (m_FriendList == null || m_FriendList.Count < 1 || string.IsNullOrEmpty(playFabID)) return false;
|
||||
|
||||
foreach (var friend in m_FriendList)
|
||||
{
|
||||
if (friend.playFabId.Equals(playFabID))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 获取好友列表
|
||||
private List<LeadboardItemData> m_FriendList = new List<LeadboardItemData>();
|
||||
public List<LeadboardItemData> FriendList { get { return m_FriendList; } }
|
||||
public bool IsFriendListRefreshing { get; private set; } = true;
|
||||
public async void GetFriendList()
|
||||
{
|
||||
GetFriendsListRequest getFriendsListRequest = new GetFriendsListRequest
|
||||
{
|
||||
ProfileConstraints = new PlayerProfileViewConstraints()
|
||||
{
|
||||
ShowAvatarUrl = true,
|
||||
ShowDisplayName = true,
|
||||
ShowStatistics = true,
|
||||
ShowLastLogin = true,
|
||||
}
|
||||
};
|
||||
|
||||
var result = await PlayFabClientAsyncAPI.GetFriendsListAsync(getFriendsListRequest);
|
||||
var list = result?.Friends;
|
||||
if (list != null)
|
||||
{
|
||||
string userId = GContext.container.Resolve<IUserService>().UserId;
|
||||
List<LeadboardItemData> leadboardList = new List<LeadboardItemData>();
|
||||
int count = list.Count;
|
||||
foreach (FriendInfo friendInfo in list)
|
||||
{
|
||||
if (friendInfo.Profile == null) continue;
|
||||
var listStatistic = friendInfo.Profile.Statistics;
|
||||
if (listStatistic == null) continue;
|
||||
|
||||
if (userId.Equals(friendInfo.FriendPlayFabId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(friendInfo.Profile.DisplayName))
|
||||
{
|
||||
friendInfo.Profile.DisplayName = userService.GetDefaultName(friendInfo.FriendPlayFabId);
|
||||
}
|
||||
|
||||
int level = 0;
|
||||
foreach (var item in listStatistic)
|
||||
{
|
||||
if (item == null) continue;
|
||||
if (item.Name == LeadboardData.leadboardLevelName)
|
||||
{
|
||||
//level = item.Value / LeadboardData.LV_MODELING;
|
||||
level = item.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
userService.SetPlayInfo(friendInfo.FriendPlayFabId, friendInfo.Profile.DisplayName, friendInfo.Profile.AvatarUrl);
|
||||
|
||||
LeadboardItemData friendData = new LeadboardItemData();
|
||||
friendData.playFabId = friendInfo.FriendPlayFabId;
|
||||
friendData.displayName = friendInfo.Profile.DisplayName;
|
||||
friendData.avatarUrl = friendInfo.Profile.AvatarUrl;
|
||||
friendData.LastLogin = friendInfo.Profile.LastLogin;
|
||||
friendData.value = level;
|
||||
friendData.position = 0;
|
||||
friendData.rank = 0;
|
||||
|
||||
leadboardList.Add(friendData);
|
||||
}
|
||||
|
||||
leadboardList.Sort((item1, item2) =>
|
||||
{
|
||||
return item2.value.CompareTo(item1.value);
|
||||
});
|
||||
|
||||
count = leadboardList.Count;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
LeadboardItemData itemData = leadboardList[i];
|
||||
itemData.position = i;
|
||||
itemData.rank = i + 1;
|
||||
}
|
||||
|
||||
m_FriendList = leadboardList;
|
||||
IsFriendListRefreshing = false;
|
||||
|
||||
GContext.Publish<SocialFriendListChangedData>(new SocialFriendListChangedData());
|
||||
#if AGG
|
||||
GEvent.SetProp("friends_count", count);
|
||||
GEvent.SetUserProp("friends_count", count);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 邀请好友
|
||||
public async Task<string> AddFriend(string playfabID)
|
||||
{
|
||||
int count = m_FriendList.Count;
|
||||
AddFriendReq getFriendListReq = new AddFriendReq()
|
||||
{
|
||||
MySelf = new BFriendInfo
|
||||
{
|
||||
PlayFabId = userService.UserId,
|
||||
AvatarUrl = userService.AvatarUrl,
|
||||
DisplayName = userService.DisplayName,
|
||||
Level = GContext.container.Resolve<PlayerData>().lv
|
||||
},
|
||||
MyFriendCount = count,
|
||||
FriendPlayFabId = playfabID,
|
||||
};
|
||||
|
||||
FriendHelper.FixBFriendInfoData(getFriendListReq.MySelf);
|
||||
|
||||
return await customServerMgr.FriendRequest("AddFriend", getFriendListReq);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 删除好友
|
||||
public async Task<bool> DeleteFriend(string friendPlayfabID)
|
||||
{
|
||||
var req = new FriendDeleteReq()
|
||||
{
|
||||
FriendPlayFabId = friendPlayfabID,
|
||||
};
|
||||
var json = await customServerMgr.FriendRequest("DeleteFriend", req);
|
||||
if (string.IsNullOrEmpty(json)) return false;
|
||||
var resp = Newtonsoft.Json.JsonConvert.DeserializeObject<FriendDeleteResp>(json);
|
||||
if (resp.Code == FriendHelper.Code_OK)
|
||||
{
|
||||
m_FriendList.RemoveAll(f => f.playFabId == friendPlayfabID);
|
||||
GContext.Publish<SocialFriendListChangedData>(new SocialFriendListChangedData());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 搜索好友
|
||||
public async Task<string> SearchFriend(string playfabID)
|
||||
{
|
||||
SearchFriendReq searchFriendReq = new SearchFriendReq()
|
||||
{
|
||||
PlayFabId = playfabID
|
||||
};
|
||||
|
||||
return await customServerMgr.FriendRequest("SearchFriend", searchFriendReq);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 接受好友邀请/忽略好友邀请
|
||||
// status:1:同意邀请;2:忽略, playfabID 接受/忽略 friendPlayFabID 的邀请
|
||||
public async Task<string> AcceptFriend(int status, string playfabID, string friendPlayFabID)
|
||||
{
|
||||
AcceptFriendReq acceptFriendReq = new AcceptFriendReq()
|
||||
{
|
||||
Status = status,
|
||||
PlayFabId = playfabID,
|
||||
FriendPlayFabId = friendPlayFabID
|
||||
};
|
||||
|
||||
return await customServerMgr.FriendRequest("AcceptFriend", acceptFriendReq);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 好友主页
|
||||
public async Task<string> GetHomePage(string playfabID, int pageNo)
|
||||
{
|
||||
GetHomePageReq getHomePageReq = new GetHomePageReq()
|
||||
{
|
||||
PlayFabId = playfabID,
|
||||
PageNo = pageNo
|
||||
};
|
||||
|
||||
return await customServerMgr.FriendRequest("GetHomePage", getHomePageReq);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 好友邀请列表&推荐列表
|
||||
public async Task<string> GetRecommendedFriends(string playfabID, int pageNo)
|
||||
{
|
||||
GetRecommendedFriendsReq acceptFriendReq = new GetRecommendedFriendsReq()
|
||||
{
|
||||
PlayFabId = playfabID,
|
||||
PageNo = pageNo
|
||||
};
|
||||
|
||||
return await customServerMgr.FriendRequest("GetRecommendedFriends", acceptFriendReq);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 获取申请成为我的朋友的列表
|
||||
public async Task<string> GetInvitedList()
|
||||
{
|
||||
return await customServerMgr.FriendRequest("GetInvitedList", "");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 获取邀请自己的列表数量
|
||||
public async void GetInvitedListCount()
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
string json = await customServerMgr.FriendRequest("GetInvitedListCount", "");
|
||||
if (!IsCurrent) return;
|
||||
if (!string.IsNullOrEmpty(json))
|
||||
{
|
||||
GetInvitedListCountResp getRecommendedFriendsResp = Newtonsoft.Json.JsonConvert.DeserializeObject<GetInvitedListCountResp>(json);
|
||||
if (getRecommendedFriendsResp != null)
|
||||
{
|
||||
if (getRecommendedFriendsResp.FriendInvitedCount >= 0)
|
||||
{
|
||||
await Awaiters.NextFrame;
|
||||
if (!IsCurrent) return;
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Friend_Add, getRecommendedFriendsResp.FriendInvitedCount > 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[FriendService::GetInvitedListCount] FriendInvitedCount Failed:");
|
||||
}
|
||||
|
||||
if (getRecommendedFriendsResp.DeepLinkInvitedCount > 0)
|
||||
{
|
||||
InviteLinkNumber = getRecommendedFriendsResp.DeepLinkInvitedCount;
|
||||
SetLinkAwardData();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 邀请码处理
|
||||
const string chars = "0M4KL3FZSTG8XCY5E6VW179A2UBDHQRIJNOP";
|
||||
readonly ulong baseLength = (ulong)chars.Length;
|
||||
public string EncryptUlongToInvitationCode(ulong number)
|
||||
{
|
||||
StringBuilder encoded = new StringBuilder();
|
||||
while (number > 0)
|
||||
{
|
||||
encoded.Insert(0, chars[(int)(number % baseLength)]);
|
||||
number /= baseLength;
|
||||
}
|
||||
return encoded.ToString().PadLeft(14, chars[0]); // Pad with '0' if necessary
|
||||
}
|
||||
public ulong DecryptInvitationCodeToUlong(string invitationCode)
|
||||
{
|
||||
ulong number = 0;
|
||||
foreach (char c in invitationCode)
|
||||
{
|
||||
number *= baseLength;
|
||||
number += (ulong)chars.IndexOf(c);
|
||||
}
|
||||
return number;
|
||||
}
|
||||
public string GetInvitationCodeByPlayFabID(string playFabID)
|
||||
{
|
||||
ulong code = Convert.ToUInt64(playFabID, 16);
|
||||
string invitationCode = EncryptUlongToInvitationCode(code);
|
||||
|
||||
return InsertFormat(invitationCode, 3, "-");
|
||||
}
|
||||
public string InsertFormat(string input, int interval, string value)
|
||||
{
|
||||
for (int i = interval; i < input.Length; i += interval + 1)
|
||||
input = input.Insert(i, value);
|
||||
return input;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 获得X天内登录过的活跃好友数量
|
||||
|
||||
public int GetActiveFriendCount(int xDays)
|
||||
{
|
||||
return FriendList.Count(data =>
|
||||
data.LastLogin is not null &&
|
||||
ZZTimeHelper.UtcNow() - data.LastLogin <= TimeSpan.FromDays(xDays));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 判断当前用户是否处于被邀请状态
|
||||
public async Task<string> IsFriendWaitingForAccept(string playfabID, string friendPlayfabID)
|
||||
{
|
||||
IsFriendWaitingForAcceptReq req = new IsFriendWaitingForAcceptReq()
|
||||
{
|
||||
PlayFabId = playfabID,
|
||||
FriendPlayFabId = friendPlayfabID
|
||||
};
|
||||
|
||||
return await customServerMgr.FriendRequest("IsFriendWaitingForAccept", req);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 后台操作
|
||||
List<FriendAddPanelBackendOperation> m_FriendAddPanelBackendOperationList = new List<FriendAddPanelBackendOperation>();
|
||||
public void StartInvite(string playfabID)
|
||||
{
|
||||
var backendOperation = new FriendAddPanelBackendOperation();
|
||||
m_FriendAddPanelBackendOperationList.Add(backendOperation);
|
||||
backendOperation.StartInvite(playfabID, OnInviteCallback);
|
||||
}
|
||||
public void OnInviteCallback(FriendAddPanelBackendOperation backendOperation, string playfabID, int code)
|
||||
{
|
||||
if (backendOperation != null)
|
||||
{
|
||||
backendOperation.Clear();
|
||||
m_FriendAddPanelBackendOperationList.Remove(backendOperation);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void Release()
|
||||
{
|
||||
linkCompletedDisposable?.Dispose();
|
||||
linkCompletedDisposable = null;
|
||||
|
||||
m_FriendAddPanelBackendOperationList.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,519 +0,0 @@
|
||||
using game.PlayerDataStorage;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using game;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using UniRx;
|
||||
|
||||
public class FetchMailRequest
|
||||
{
|
||||
public DateTime? lastSyncTime;
|
||||
}
|
||||
|
||||
public class FetchMailResp
|
||||
{
|
||||
public bool success;
|
||||
public List<GameMail> mails;
|
||||
public string error;
|
||||
}
|
||||
|
||||
public class MailStateInfo
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public EMailState state { get; set; }
|
||||
|
||||
public MailStateInfo(GameMail mail, EMailState newState)
|
||||
{
|
||||
Id = mail.Id.ToString();
|
||||
state = mail.State | newState;
|
||||
}
|
||||
}
|
||||
|
||||
public class UpdateMailStateRequest
|
||||
{
|
||||
public MailStateInfo[] mailStates;
|
||||
}
|
||||
|
||||
public class UpdateMailStateResp
|
||||
{
|
||||
public bool success;
|
||||
public MailStateInfo[] states;
|
||||
}
|
||||
|
||||
public class FetchMailEvt
|
||||
{
|
||||
DateTime TimeStamp;
|
||||
}
|
||||
|
||||
public class MailInfo
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public EMailType Type { get; set; }
|
||||
|
||||
public int expireDays { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
|
||||
public string From { get; set; }
|
||||
|
||||
public string Content { get; set; }
|
||||
|
||||
public int[] DropIds { get; set; }
|
||||
}
|
||||
|
||||
public class SendMailRequest
|
||||
{
|
||||
public MailInfo[] mailInfos;
|
||||
}
|
||||
|
||||
public class SendMailResp
|
||||
{
|
||||
public bool success;
|
||||
public List<GameMail> mails;
|
||||
public string error;
|
||||
}
|
||||
|
||||
public class AutoMailData
|
||||
{
|
||||
public string Id;
|
||||
public int levelMin;
|
||||
public int levelMax;
|
||||
public DateTime accountCreateTime;
|
||||
public DateTime loginTimeMin;
|
||||
public DateTime loginTimeMax;
|
||||
public int expireDays;
|
||||
public int DropID;
|
||||
|
||||
public AutoMailData(cfg.AutoDeliveryMail mail)
|
||||
{
|
||||
Id = mail.MailID.ToString();
|
||||
levelMin = mail.AccountLevelRange[0];
|
||||
levelMax = mail.AccountLevelRange[1] == -1 ? int.MaxValue : mail.AccountLevelRange[1];
|
||||
accountCreateTime = DateTime.Parse(mail.AccountCreateTimestamp);
|
||||
loginTimeMin = DateTime.Parse(mail.LoginTimestampRange[0]);
|
||||
loginTimeMax = DateTime.Parse(mail.LoginTimestampRange[1]);
|
||||
DropID = mail.DropID;
|
||||
expireDays = mail.ExpireDay;
|
||||
}
|
||||
|
||||
public bool IsSatisfy(DateTime loginTime, DateTime CreateTime, int level)
|
||||
{
|
||||
return loginTime >= loginTimeMin && loginTime <= loginTimeMax
|
||||
&& CreateTime < accountCreateTime
|
||||
&& level >= levelMin && level <= levelMax;
|
||||
}
|
||||
|
||||
public MailInfo ToMailInfo()
|
||||
{
|
||||
return new MailInfo
|
||||
{
|
||||
Id = Id.ToString(),
|
||||
Type = EMailType.AutoDelivery,
|
||||
DropIds = new[] { DropID },
|
||||
expireDays = expireDays
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static class MailUrls
|
||||
{
|
||||
public const string Fetch = "mail/fetch";
|
||||
public const string UpdateState = "mail/updateState";
|
||||
public const string Send = "mail/send";
|
||||
}
|
||||
|
||||
public class InGameMailService : IInGameMailService
|
||||
{
|
||||
private MailCache _mailCache;
|
||||
private BaseMailHandler _handler;
|
||||
readonly IUserService userService;
|
||||
private List<AutoMailData> _autoMailDatas;
|
||||
|
||||
private CompositeDisposable _disposables;
|
||||
|
||||
private bool _forceFetch = false;
|
||||
|
||||
//private const int Fetch_Interval = 3;
|
||||
|
||||
public bool IsUnlock { get; set; }
|
||||
|
||||
|
||||
public InGameMailService(ICustomServerMgr customServerMgr, cfg.Tables tables, IUserService userService, IPlayerSaveStore saveStore)
|
||||
{
|
||||
this._mailCache = new MailCache(tables.TbAutoDeliveryMail);
|
||||
this.userService = userService;
|
||||
this._handler = new BaseMailHandler(_mailCache, customServerMgr, userService, saveStore);
|
||||
_autoMailDatas = tables.TbAutoDeliveryMail.DataList
|
||||
.Select(x => new AutoMailData(x)).ToList();
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
if (!userService.IsAuthorized) throw new InvalidOperationException("Mail cache requires an authenticated user.");
|
||||
_mailCache.Load(userService.UserId);
|
||||
|
||||
_disposables = new CompositeDisposable();
|
||||
|
||||
var minActiveLevel = _autoMailDatas.Min(x => x.levelMin) - 1;
|
||||
|
||||
_ = _handler.FetchMail();
|
||||
|
||||
GContext.OnEvent<OnLvChangeEvent>()
|
||||
.Skip(1)
|
||||
.Where(x => x.lvl >= minActiveLevel)
|
||||
.Subscribe(OnLevelChange)
|
||||
.AddTo(_disposables);
|
||||
|
||||
//GContext.OnEvent<RestartShowHomeUIEvent>()
|
||||
//.Skip(1)
|
||||
//.Subscribe(OnHomeUIShow)
|
||||
//.AddTo(_disposables);
|
||||
|
||||
var rtService = GContext.container.Resolve<IRTService>();
|
||||
|
||||
rtService.OnEvent<FetchMailEvt>(async evt => await _handler.FetchMail())
|
||||
.AddTo(_disposables);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_handler.Close();
|
||||
_mailCache?.Unload();
|
||||
_disposables?.Dispose();
|
||||
_disposables = null;
|
||||
}
|
||||
|
||||
public async Task FetchMail()
|
||||
{
|
||||
await _handler.FetchMail();
|
||||
}
|
||||
|
||||
public IList<GameMail> GetAllMail()
|
||||
{
|
||||
return _mailCache.GetAll();
|
||||
}
|
||||
|
||||
public async Task<bool> MarkRead(GameMail mail)
|
||||
{
|
||||
return await _handler.MarkAsRead(mail);
|
||||
}
|
||||
|
||||
public async Task<bool> MarkRead(GameMail[] mails = null)
|
||||
{
|
||||
return await _handler.MarkAsRead(mails);
|
||||
}
|
||||
|
||||
public async Task<bool> DelMail(GameMail mail)
|
||||
{
|
||||
return await _handler.DeleteMail(mail);
|
||||
}
|
||||
|
||||
public async Task<bool> DelMail(GameMail[] mails = null)
|
||||
{
|
||||
return await _handler.DeleteMail(mails);
|
||||
}
|
||||
|
||||
public async Task<bool> TakeReward(GameMail mail)
|
||||
{
|
||||
return await _handler.TakeReward(mail);
|
||||
}
|
||||
|
||||
public async Task<bool> TakeReward(GameMail[] mails)
|
||||
{
|
||||
return await _handler.TakeReward(mails);
|
||||
}
|
||||
|
||||
private void OnLevelChange(OnLvChangeEvent evt)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
UnityEngine.Debug.Log("[InGameMailService] OnLevelChange");
|
||||
#endif
|
||||
GenAutoDeliveryMail();
|
||||
}
|
||||
|
||||
//private async void OnHomeUIShow(RestartShowHomeUIEvent evt)
|
||||
//{
|
||||
|
||||
//#if UNITY_EDITOR
|
||||
//var deltaTime = _mailCache.LastSyncTime == null ? 0 : (ZZTimeHelper.UtcNow() - _mailCache.LastSyncTime.Value).TotalSeconds;
|
||||
//UnityEngine.Debug.Log($"[InGameMailService] OnHomeUIShow deltaTime {deltaTime}");
|
||||
//#endif
|
||||
//if(_mailCache.LastSyncTime == null
|
||||
//|| _forceFetch
|
||||
//|| ZZTimeHelper.UtcNow() - _mailCache.LastSyncTime.Value > TimeSpan.FromMinutes(Fetch_Interval))
|
||||
//{
|
||||
//if(await _handler.FetchMail())
|
||||
//{
|
||||
//_forceFetch = false;
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
|
||||
private async void GenAutoDeliveryMail()
|
||||
{
|
||||
var playerData = GContext.container.Resolve<PlayerData>();
|
||||
var userData = userService;
|
||||
if (!_handler.IsCurrent) return;
|
||||
|
||||
var mailsToSend = _autoMailDatas.Where(m => !_mailCache.HasMail(m.Id)
|
||||
&& m.IsSatisfy(userData.LoginTime, userData.CreateTime, playerData.lv))
|
||||
.Select(m => m.ToMailInfo())
|
||||
.ToArray();
|
||||
|
||||
if (mailsToSend.Length > 0)
|
||||
{
|
||||
if (await _handler.SendMail(mailsToSend) && _handler.IsCurrent)
|
||||
{
|
||||
_forceFetch = true;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
var mailids = mailsToSend.Select(m => m.Id);
|
||||
var mailidsStr = string.Join(',', mailids);
|
||||
UnityEngine.Debug.Log($"[InGameMailService] GenAutoDeliveryMail {mailidsStr}");
|
||||
#endif
|
||||
}
|
||||
|
||||
public void PrintCacheMail()
|
||||
{
|
||||
UnityEngine.Debug.Log(_mailCache.PrintCacheMails());
|
||||
}
|
||||
}
|
||||
|
||||
public class BaseMailHandler : IMailHandler
|
||||
{
|
||||
protected MailCache _cache;
|
||||
protected ICustomServerMgr _customServerMgr;
|
||||
readonly IUserService userService;
|
||||
readonly IPlayerSaveStore saveStore;
|
||||
bool closed, rewardPending;
|
||||
public bool IsCurrent => !closed && saveStore.IsCurrent && !userService.LoginLifetime.IsCancellationRequested;
|
||||
public void Close() { closed = true; }
|
||||
|
||||
public BaseMailHandler(MailCache cache, ICustomServerMgr customServerMgr, IUserService userService, IPlayerSaveStore saveStore)
|
||||
{
|
||||
this.userService = userService;
|
||||
this.saveStore = saveStore;
|
||||
this._cache = cache;
|
||||
this._customServerMgr = customServerMgr;
|
||||
}
|
||||
|
||||
public virtual async Task<bool> FetchMail()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IsCurrent) return false;
|
||||
await Awaiters.NextFrame;
|
||||
if (!IsCurrent) return false;
|
||||
#if UNITY_EDITOR
|
||||
UnityEngine.Debug.Log($"[InGameMailService] Start Fetch Mails");
|
||||
#endif
|
||||
var req = new FetchMailRequest()
|
||||
{
|
||||
lastSyncTime = _cache.LastSyncTime
|
||||
};
|
||||
|
||||
if (!IsCurrent) return false;
|
||||
var result = await _customServerMgr.CustomServerPost(MailUrls.Fetch, JsonConvert.SerializeObject(req));
|
||||
if (!IsCurrent) return false;
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
var resp = JsonConvert.DeserializeObject<FetchMailResp>(result);
|
||||
if (resp.success)
|
||||
{
|
||||
if (resp.mails != null && resp.mails.Count > 0)
|
||||
{
|
||||
_cache.Cache(resp.mails);
|
||||
_cache.Save();
|
||||
var UnreadMailCount = _cache.UnreadMailCount;
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Unread_Mail, UnreadMailCount > 0, UnreadMailCount);
|
||||
#if UNITY_EDITOR
|
||||
UnityEngine.Debug.Log($"[InGameMailService] FetchMail {UnreadMailCount} LastSyncTime = {_cache.LastSyncTime}");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
_cache.UpdateSyncTime();
|
||||
#if UNITY_EDITOR
|
||||
UnityEngine.Debug.Log($"[InGameMailService] No Mails Fetch Fetched LastSyncTime = {_cache.LastSyncTime}");
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Debug.LogError($"[InGameMailService] FetchMail Failed {resp.error} ");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogException(e);
|
||||
}
|
||||
|
||||
UnityEngine.Debug.LogWarning("[InGameMailService] FetchMail Failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> SendMail(MailInfo[] mailInfos)
|
||||
{
|
||||
try
|
||||
{
|
||||
var req = new SendMailRequest()
|
||||
{
|
||||
mailInfos = mailInfos
|
||||
};
|
||||
|
||||
if (!IsCurrent) return false;
|
||||
var result = await _customServerMgr.CustomServerPost(MailUrls.Send, JsonConvert.SerializeObject(req));
|
||||
if (!IsCurrent) return false;
|
||||
if (result != null)
|
||||
{
|
||||
var resp = JsonConvert.DeserializeObject<SendMailResp>(result);
|
||||
if (!resp.success)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"[InGameMailService] SendMail Failed {resp.error} ");
|
||||
}
|
||||
return resp.success;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogError(e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual async Task<bool> UpdateMailState(GameMail[] mails, EMailState state)
|
||||
{
|
||||
var req = new UpdateMailStateRequest()
|
||||
{
|
||||
mailStates = mails.Select(m => new MailStateInfo(m, state)).ToArray()
|
||||
};
|
||||
|
||||
if (!IsCurrent) return false;
|
||||
var result = await _customServerMgr.CustomServerPost(MailUrls.UpdateState, JsonConvert.SerializeObject(req));
|
||||
if (!IsCurrent) return false;
|
||||
if (result != null)
|
||||
{
|
||||
var resp = JsonConvert.DeserializeObject<UpdateMailStateResp>(result);
|
||||
if (resp.success)
|
||||
{
|
||||
foreach (var s in req.mailStates)
|
||||
{
|
||||
var mail = _cache.GetMail(s.Id);
|
||||
if (mail != null) mail.State = s.state;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task<bool> MarkAsRead(GameMail mail)
|
||||
{
|
||||
if (!IsCurrent || mail.IsRead) return false;
|
||||
|
||||
var result = await UpdateMailState(new[] { mail }, EMailState.HasRead);
|
||||
if (result) _cache.Save();
|
||||
return result;
|
||||
}
|
||||
|
||||
public virtual async Task<bool> MarkAsRead(GameMail[] mails = null)
|
||||
{
|
||||
if (!IsCurrent) return false;
|
||||
if (mails == null)
|
||||
{
|
||||
mails = _cache.GetAll()
|
||||
.Where(m => !m.IsRead)
|
||||
.ToArray();
|
||||
}
|
||||
return await UpdateMailState(mails, EMailState.HasRead);
|
||||
}
|
||||
|
||||
public Task<bool> TakeReward(GameMail mail) => TakeReward(new[] { mail });
|
||||
|
||||
public async Task<bool> TakeReward(GameMail[] mails = null)
|
||||
{
|
||||
if (!IsCurrent || rewardPending) return false;
|
||||
if (saveStore is KVPlayerSaveStore && !saveStore.Read("NewPlayerDataDic").Success) return false;
|
||||
var eligible = (mails ?? _cache.GetAll().ToArray())
|
||||
.Where(m => m != null && m.HasDrops && !m.HasReward)
|
||||
.GroupBy(m => m.Id).Select(g => g.First()).ToArray();
|
||||
if (eligible.Length == 0) return false;
|
||||
// Freeze eligibility and the personal-mail inflation rule before changing server state.
|
||||
var rewards = eligible.Select(m => new { m.Type, Drops = m.DropIds.ToList() }).ToArray();
|
||||
var items = GContext.container.Resolve<PlayerItemData>();
|
||||
var player = GContext.container.Resolve<PlayerData>();
|
||||
rewardPending = true;
|
||||
try
|
||||
{
|
||||
if (!await UpdateMailState(eligible, EMailState.RewardTaken) || !IsCurrent) return false;
|
||||
_cache.Save();
|
||||
await PlayerSaveAction.RunAsync(saveStore, "mail-reward", () =>
|
||||
{
|
||||
foreach (var reward in rewards)
|
||||
{
|
||||
if (reward.Type == EMailType.Personal)
|
||||
items.AddItemByDropListLureInflation(null, reward.Drops, null);
|
||||
else items.AddItemByDropList(reward.Drops, null);
|
||||
}
|
||||
PlayerSaveAction.Present(() => GContext.Publish(new ShowData()));
|
||||
}, () => player.SavePlayerData(), userService.LoginLifetime);
|
||||
if (!IsCurrent) return false;
|
||||
rewardPending = false;
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogException(e);
|
||||
return false;
|
||||
}
|
||||
// On failure keep the claim blocked in this login. Server state and KV saves are not atomic.
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteMail(GameMail mail)
|
||||
{
|
||||
if (!IsCurrent || mail.IsDeleted)
|
||||
return false;
|
||||
|
||||
var result = await UpdateMailState(new[] { mail }, EMailState.HasDeleted);
|
||||
if (result) _cache.Save();
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteMail(GameMail[] mails = null)
|
||||
{
|
||||
if (!IsCurrent) return false;
|
||||
if (mails == null)
|
||||
{
|
||||
mails = _cache.GetAll()
|
||||
.Where(m => m.IsRead && m.HasReward)
|
||||
.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
mails = mails.Where(m => !m.IsDeleted)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
if (mails.Length == 0) return false;
|
||||
|
||||
var result = await UpdateMailState(mails, EMailState.HasDeleted);
|
||||
if (result) _cache.Save();
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,310 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using game.PlayerDataStorage;
|
||||
using GameCore;
|
||||
using tysdk;
|
||||
using UnityEngine;
|
||||
|
||||
namespace game
|
||||
{
|
||||
// One service belongs to one login lifetime. A revoked instance is never rebound.
|
||||
public sealed class KVUserService : IUserService, IKVUserLoadReceiver, IKVUserLifecycle
|
||||
{
|
||||
[Inject] public ICustomServerMgr customServerMgr { get; set; }
|
||||
[Inject] public IPlayerSaveStore SaveStore { get; set; }
|
||||
readonly PlayerKVRuntimeSelection selection;
|
||||
readonly Func<CookieContainer, HttpMessageHandler> handlerFactory;
|
||||
readonly CancellationTokenSource lifetime = new CancellationTokenSource();
|
||||
readonly Dictionary<string, PlayInfo> profiles = new Dictionary<string, PlayInfo>();
|
||||
readonly Dictionary<string, int> levels = new Dictionary<string, int>();
|
||||
PlayerKVGameLoadCoordinator coordinator;
|
||||
PlayerKVGameSessionHandle handle;
|
||||
PlayerKVInitializationIntents intents;
|
||||
bool attempted, loading, closing, published, promptsBound;
|
||||
Task closeTask;
|
||||
long epoch;
|
||||
string playerId;
|
||||
uint internalId;
|
||||
public string CustomId { get; set; }
|
||||
public string UserId => IsAuthorized ? playerId : null;
|
||||
public string ApiPlayerId => UserId;
|
||||
public uint InternalId => IsAuthorized ? internalId : 0;
|
||||
public bool IsLogin => IsAuthorized;
|
||||
public bool IsAuthorized => published && KVHandoffReady;
|
||||
public CancellationToken LoginLifetime => lifetime.Token;
|
||||
public DateTime LastLoginTime { get; private set; }
|
||||
public DateTime LoginTime { get; private set; }
|
||||
public DateTime CreateTime { get; private set; }
|
||||
public string DisplayName { get; private set; }
|
||||
public string AvatarUrl { get; private set; }
|
||||
public string Language { get; private set; }
|
||||
public string ContinentCode { get; private set; }
|
||||
public Dictionary<string, string> EAccoutUrl { get; private set; } = new Dictionary<string, string>();
|
||||
internal int KVTapJoyBalance { get; private set; }
|
||||
internal PlayerKVGameLoadError KVLastError { get; private set; }
|
||||
internal bool KVHandoffReady => !closing && handle != null && coordinator != null &&
|
||||
coordinator.TryGetCurrentGameHandle(epoch, out var current) && ReferenceEquals(current, handle);
|
||||
bool IKVUserLifecycle.KVHandoffReady => KVHandoffReady;
|
||||
PlayerKVGameLoadError IKVUserLifecycle.KVLastError => KVLastError;
|
||||
|
||||
public KVUserService() : this(SubBoostrap.RuntimeSelection, jar => new HttpClientHandler
|
||||
{ CookieContainer = jar, UseCookies = true, AllowAutoRedirect = false }) { }
|
||||
|
||||
internal KVUserService(PlayerKVRuntimeSelection selection, Func<CookieContainer, HttpMessageHandler> handlerFactory)
|
||||
{
|
||||
this.selection = selection;
|
||||
this.handlerFactory = handlerFactory ?? throw new ArgumentNullException(nameof(handlerFactory));
|
||||
Application.quitting += Logout;
|
||||
}
|
||||
|
||||
public async Task<ELoginStep> Login(Action<int> progress, bool tyrLoginByToken = true)
|
||||
{
|
||||
if (attempted || closing || selection == null || !selection.IsKV ||
|
||||
!selection.TryAdmitPlayer(CustomId, out var canonicalId))
|
||||
{ KVLastError = PlayerKVGameLoadError.Input; return ELoginStep.Api; }
|
||||
// Set before the first await: duplicate calls cannot authenticate or subscribe twice.
|
||||
attempted = true;
|
||||
var requestedId = CustomId;
|
||||
try
|
||||
{
|
||||
if (customServerMgr is not CustomServerMgr server ||
|
||||
server.ApiBaseUri.AbsoluteUri != PlayerKVRuntimeSelection.FormalApiUrl)
|
||||
throw new InvalidOperationException("Formal KV transport required.");
|
||||
loading = true;
|
||||
var transaction = PlayerKVGameplayBindings.Bind(this);
|
||||
coordinator = new PlayerKVGameLoadCoordinator(transaction,
|
||||
() => loading && !closing && !published && handle == null,
|
||||
() => { }, () => intents?.Count ?? 0, server.BindKVForGame, server.ClearKVForGame);
|
||||
server.KVUnauthorized += OnUnauthorized;
|
||||
var identity = new PlayerKVIdentitySnapshot(null, false, PlayerKVIdentityChannel.none,
|
||||
Array.Empty<PlayerKVIdentityChannel>());
|
||||
var snapshot = PlayerKVGameplayBindings.Snapshot(DateTime.UtcNow, "formal-v1");
|
||||
var capability = PlayerKVGameLoadCapability.CreateForGame(identity, snapshot, selection.Api,
|
||||
Application.persistentDataPath, handlerFactory, handlerFactory, handlerFactory);
|
||||
var result = await coordinator.LoadForGameAsync(canonicalId, capability, LoginLifetime,
|
||||
() => !closing && CustomId == requestedId);
|
||||
KVLastError = result.Error;
|
||||
if (result.State != PlayerKVGameLoadState.GameLoaded || closing || CustomId != requestedId ||
|
||||
result.Account == null || result.Account.PlayerId != canonicalId || result.Account.Epoch != result.Epoch ||
|
||||
!coordinator.TryGetCurrentGameHandle(result.Epoch, out var original))
|
||||
{ Logout(); return ELoginStep.Api; }
|
||||
epoch = result.Epoch;
|
||||
handle = original;
|
||||
if (SaveStore is not KVPlayerSaveStore store) throw new InvalidOperationException("KV save store required.");
|
||||
store.Bind(original, LoginLifetime);
|
||||
PrepareProfile(result.Account);
|
||||
BindPromptServices(original);
|
||||
// Chat subscribes before the unique success event. No SDK or analytics initialization here.
|
||||
GContext.container.Resolve<IChatService>().WaitingForLogin();
|
||||
if (!KVHandoffReady || CustomId != requestedId) throw new InvalidOperationException("Stale KV login.");
|
||||
server.EnableKVBusiness(epoch);
|
||||
playerId = result.Account.PlayerId;
|
||||
internalId = result.Account.InternalId;
|
||||
published = true;
|
||||
GContext.Publish(new EvtAPISvrLoginSuccess { playerId = playerId, internalId = internalId });
|
||||
if (!IsAuthorized) return ELoginStep.Api;
|
||||
_ = RunSaveScheduleAsync(store);
|
||||
progress?.Invoke(2);
|
||||
return ELoginStep.Done;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
KVLastError = PlayerKVGameLoadError.Apply;
|
||||
Debug.LogError("KV login failed: " + ex.GetType().Name);
|
||||
Logout();
|
||||
return ELoginStep.Api;
|
||||
}
|
||||
finally { loading = false; }
|
||||
}
|
||||
|
||||
void PrepareProfile(PlayerKVAccountDescriptor account)
|
||||
{
|
||||
if (account.InternalId == 0 || account.CreateTimeUtc == DateTime.MinValue || account.ServerTimeUtc == DateTime.MinValue)
|
||||
throw new InvalidOperationException("Authenticated account metadata required.");
|
||||
CreateTime = account.CreateTimeUtc;
|
||||
LoginTime = account.ServerTimeUtc;
|
||||
var previous = Read("LastLoginTimeUtc");
|
||||
LastLoginTime = ParseLastLoginTime(previous.Value, previous.Found.Value);
|
||||
DisplayName = Read("DisplayName").Value;
|
||||
if (string.IsNullOrEmpty(DisplayName)) DisplayName = GetDefaultName(account.PlayerId);
|
||||
AvatarUrl = Read("AvatarUrl").Value ?? string.Empty;
|
||||
Language = Read("Language").Value;
|
||||
if (string.IsNullOrEmpty(Language)) Language = GContext.container.Resolve<ISettingService>().Lang;
|
||||
var continent = Read("ContinentCode").Value;
|
||||
ContinentCode = new[] { "AF", "AN", "AS", "EU", "NA", "OC", "SA" }.Contains(continent) ? continent : null;
|
||||
}
|
||||
|
||||
internal static DateTime ParseLastLoginTime(string value, bool found)
|
||||
{
|
||||
if (!found) return DateTime.MinValue;
|
||||
if (!DateTime.TryParseExact(value, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var time) ||
|
||||
time == DateTime.MinValue || time.Kind == DateTimeKind.Unspecified)
|
||||
throw new FormatException("Invalid LastLoginTimeUtc.");
|
||||
return time.ToUniversalTime();
|
||||
}
|
||||
|
||||
PlayerSaveReadResult Read(string key)
|
||||
{
|
||||
var result = SaveStore.Read(key);
|
||||
if (!result.Success) throw new InvalidOperationException("KV profile read failed: " + result.Error);
|
||||
return result;
|
||||
}
|
||||
|
||||
void BindPromptServices(PlayerKVGameSessionHandle original)
|
||||
{
|
||||
if (promptsBound || !KVHandoffReady) throw new InvalidOperationException("Prompt binding is stale.");
|
||||
var settings = GContext.container.Resolve<ISettingService>() as DefaultSettingService;
|
||||
var notifications = GContext.container.Resolve<ILocalNotificationService>() as LocalNotificationService;
|
||||
var club = GContext.container.Resolve<ClubService>();
|
||||
if (settings == null || notifications == null || club == null ||
|
||||
!ReferenceEquals(settings, GContext.container.Resolve<ISettingService>()) ||
|
||||
!ReferenceEquals(notifications, GContext.container.Resolve<ILocalNotificationService>()) ||
|
||||
!ReferenceEquals(club, GContext.container.Resolve<ClubService>()))
|
||||
throw new InvalidOperationException("Formal prompt services required.");
|
||||
settings.PreflightFormalPromptOwner(original);
|
||||
club.PreflightFormalPromptOwner(original);
|
||||
notifications.PreflightFormalPromptSettings(settings);
|
||||
promptsBound = true;
|
||||
settings.BindFormalPromptOwner(original);
|
||||
club.BindFormalPromptOwner(original);
|
||||
notifications.BindFormalPromptSettings(settings);
|
||||
if (!settings.HasFormalPromptOwner(original) || !club.HasFormalPromptOwner(original) ||
|
||||
!notifications.HasFormalPromptSettings(settings) || !KVHandoffReady)
|
||||
throw new InvalidOperationException("Formal prompt binding changed.");
|
||||
}
|
||||
|
||||
void OnUnauthorized(int version)
|
||||
{
|
||||
if (version == customServerMgr.SessionVersion) Logout();
|
||||
}
|
||||
|
||||
public void Logout() => ObserveClose(CloseKVForValidationAsync());
|
||||
async void ObserveClose(Task task)
|
||||
{
|
||||
try { await task; }
|
||||
catch (Exception ex) { Debug.LogError("KV close failed: " + ex.GetType().Name); }
|
||||
}
|
||||
internal Task CloseKVForValidationAsync()
|
||||
{
|
||||
if (closeTask != null) return closeTask;
|
||||
closing = true;
|
||||
published = false;
|
||||
(SaveStore as KVPlayerSaveStore)?.Dispose();
|
||||
playerId = null; internalId = 0; handle = null; epoch = 0;
|
||||
DisplayName = AvatarUrl = Language = ContinentCode = null;
|
||||
ClearPlayInfos();
|
||||
Application.quitting -= Logout;
|
||||
if (customServerMgr is CustomServerMgr server)
|
||||
{
|
||||
server.KVUnauthorized -= OnUnauthorized;
|
||||
server.DisableKVBusiness();
|
||||
}
|
||||
// Revoke first; a synchronous cancellation callback must already see invalid identity.
|
||||
closeTask = coordinator?.Revoke() ?? Task.CompletedTask;
|
||||
lifetime.Cancel();
|
||||
return closeTask;
|
||||
}
|
||||
Task IKVUserLifecycle.CloseKVForValidationAsync() => CloseKVForValidationAsync();
|
||||
internal Task<PlayerKVShutdownStatus> ObserveKVQuitAsync() =>
|
||||
coordinator?.ObserveQuitAsync() ?? Task.FromResult(PlayerKVShutdownStatus.Ready);
|
||||
Task<PlayerKVShutdownStatus> IKVUserLifecycle.ObserveKVQuitAsync() => ObserveKVQuitAsync();
|
||||
|
||||
void IKVUserLoadReceiver.ResetForKVLoad()
|
||||
{
|
||||
if ((!loading && !closing) || published || handle != null)
|
||||
throw new InvalidOperationException("KV identity must be unpublished during load/reset.");
|
||||
intents?.Discard(); intents = null;
|
||||
EAccoutUrl = new Dictionary<string, string>(); KVTapJoyBalance = 0;
|
||||
}
|
||||
void IKVUserLoadReceiver.ReplaceFromKVDecoded(PlayerKVLoginState state, long loadEpoch, IReadOnlyDictionary<string, string> raw)
|
||||
{
|
||||
if (!loading || closing || published || handle != null || state == null || raw == null)
|
||||
throw new InvalidOperationException("KV load receiver is not current.");
|
||||
intents = new PlayerKVInitializationIntents(loadEpoch, state.Channel);
|
||||
foreach (var pair in state.Normalizations) intents.Add(loadEpoch, pair.Key, pair.Value);
|
||||
EAccoutUrl = state.AccountUrls.ToDictionary(p => p.Key, p => p.Value, StringComparer.Ordinal);
|
||||
KVTapJoyBalance = state.TapJoyBalance;
|
||||
}
|
||||
void IKVUserLoadReceiver.DiscardKVIntents() => intents?.Discard();
|
||||
|
||||
async Task SaveProfile(string key, string value)
|
||||
{
|
||||
var store = SaveStore;
|
||||
if (!IsAuthorized || store == null) throw new InvalidOperationException("KV identity unavailable.");
|
||||
var result = await store.WriteAndConfirmAsync(key, value, LoginLifetime);
|
||||
if (!IsAuthorized || !ReferenceEquals(store, SaveStore) || !store.IsCurrent || !result.Confirmed)
|
||||
throw new InvalidOperationException("KV profile save was not confirmed.");
|
||||
}
|
||||
|
||||
async Task RunSaveScheduleAsync(IPlayerSaveStore store)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (IsAuthorized && store.IsCurrent)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(40), LoginLifetime);
|
||||
if (!IsAuthorized || !store.IsCurrent) return;
|
||||
var result = await store.FlushAsync(LoginLifetime);
|
||||
if (!result.Confirmed && !LoginLifetime.IsCancellationRequested)
|
||||
Debug.LogWarning("KV scheduled save: " + result.Status);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
catch (Exception ex) { Debug.LogError("KV save scheduler failed: " + ex.GetType().Name); }
|
||||
}
|
||||
public async Task<string> ChangeDisplayName(string newName)
|
||||
{
|
||||
await SaveProfile("DisplayName", newName);
|
||||
DisplayName = newName; GContext.Publish(new ChangeAvatarEvent()); UpdateProfile(); return newName;
|
||||
}
|
||||
public async Task<string> UpdateAvatarUrl(string url)
|
||||
{
|
||||
await SaveProfile("AvatarUrl", url);
|
||||
AvatarUrl = url; GContext.Publish(new ChangeAvatarEvent()); UpdateProfile(); return url;
|
||||
}
|
||||
public async Task<string> UpdateLanguage(string code)
|
||||
{ await SaveProfile("Language", code); Language = code; return code; }
|
||||
public double CreateTotalSeconds() => (ZZTimeHelper.UtcNow() - CreateTime).TotalSeconds;
|
||||
// The non-AGG channel has no account-link operation; never manufacture a successful binding.
|
||||
public Task<(int code, int userid)> LinkAccount(EAccoutType type) => Task.FromResult((1, 0));
|
||||
public Task<bool> GetLinkState(EAccoutType type) => Task.FromResult(false);
|
||||
public string GetDefaultName(string id) => "Angler#" + (string.IsNullOrEmpty(id) ? string.Empty : id.Substring(0, Math.Min(5, id.Length)));
|
||||
public void SetPlayerLv(string id, int level) => levels[id] = id == UserId ? GContext.container.Resolve<PlayerData>().lv : level;
|
||||
public int GetPlayerLv(string id) => id == UserId && IsAuthorized ? GContext.container.Resolve<PlayerData>().lv :
|
||||
id != null && levels.TryGetValue(id, out var level) ? level : -1;
|
||||
public Task<(string, int)> GetPlayLv(string id)
|
||||
{
|
||||
var level = GetPlayerLv(id);
|
||||
return level >= 0 ? Task.FromResult((id, level)) :
|
||||
Task.FromException<(string, int)>(new InvalidOperationException("Public player level has not been loaded."));
|
||||
}
|
||||
public PlayInfo GetPlayInfo(string id)
|
||||
{
|
||||
if (id == UserId && IsAuthorized) return new PlayInfo { name = DisplayName, avatarUrl = AvatarUrl };
|
||||
return id != null && profiles.TryGetValue(id, out var profile) ? profile : null;
|
||||
}
|
||||
public void SetPlayInfo(string id, string name, string avatarUrl)
|
||||
{
|
||||
if (string.IsNullOrEmpty(id)) return;
|
||||
profiles[id] = new PlayInfo { name = string.IsNullOrEmpty(name) ? GetDefaultName(id) : name, avatarUrl = avatarUrl };
|
||||
}
|
||||
public void ClearPlayInfos() { profiles.Clear(); levels.Clear(); }
|
||||
public void UpdateProfile()
|
||||
{
|
||||
if (IsAuthorized) customServerMgr.AccountRequest("UpdateProfile", new LoginRequest { playerId = ApiPlayerId,
|
||||
playerProfile = new PlayerProfile { Level = GContext.container.Resolve<PlayerData>().lv, Avatar = AvatarUrl, DisplayName = DisplayName } });
|
||||
}
|
||||
public void UpdateProfileLv()
|
||||
{
|
||||
if (IsAuthorized) customServerMgr.AccountRequest("UpdateProfile", new LoginRequest { playerId = ApiPlayerId,
|
||||
playerProfile = new PlayerProfile { Level = GContext.container.Resolve<PlayerData>().lv } });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace game.PlayerDataStorage
|
||||
{
|
||||
internal interface IKVUserLifecycle
|
||||
{
|
||||
bool KVHandoffReady { get; }
|
||||
PlayerKVGameLoadError KVLastError { get; }
|
||||
Task CloseKVForValidationAsync();
|
||||
Task<PlayerKVShutdownStatus> ObserveKVQuitAsync();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace game.PlayerDataStorage
|
||||
{
|
||||
internal interface IKVUserLoadReceiver
|
||||
{
|
||||
void ResetForKVLoad();
|
||||
void ReplaceFromKVDecoded(PlayerKVLoginState state, long epoch, IReadOnlyDictionary<string, string> raw);
|
||||
void DiscardKVIntents();
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace game.PlayerDataStorage
|
||||
{
|
||||
// A store belongs to one login. Callers retain this instance across awaits.
|
||||
public interface IPlayerSaveStore
|
||||
{
|
||||
bool IsCurrent { get; }
|
||||
PlayerSaveReadResult Read(string key);
|
||||
PlayerSaveWriteResult Write(string key, string value, bool remoteStore = true);
|
||||
Task<PlayerSaveConfirmation> WriteAndConfirmAsync(string key, string value, CancellationToken token = default);
|
||||
Task<PlayerSaveConfirmation> ConfirmAsync(PlayerSaveReceipt receipt, CancellationToken token = default);
|
||||
Task<PlayerSaveConfirmation> FlushAsync(CancellationToken token = default);
|
||||
}
|
||||
|
||||
public sealed class PlayerSaveReadResult
|
||||
{
|
||||
public bool Success => Error == null;
|
||||
public bool? Found { get; }
|
||||
public string Value { get; }
|
||||
public string Error { get; }
|
||||
internal PlayerSaveReadResult(bool found, string value) { Found = found; Value = value; }
|
||||
internal PlayerSaveReadResult(string error) { Error = error ?? "ReadFailed"; }
|
||||
public string ValueOrThrow()
|
||||
{
|
||||
if (!Success) throw new InvalidOperationException("Player save read failed: " + Error);
|
||||
return Value;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PlayerSaveReceipt
|
||||
{
|
||||
internal readonly IPlayerSaveStore Store;
|
||||
internal readonly object Ticket;
|
||||
internal PlayerSaveReceipt(IPlayerSaveStore store, object ticket) { Store = store; Ticket = ticket; }
|
||||
}
|
||||
|
||||
public sealed class PlayerSaveWriteResult
|
||||
{
|
||||
public bool Accepted => Error == null;
|
||||
public string Error { get; }
|
||||
public PlayerSaveReceipt Receipt { get; }
|
||||
internal PlayerSaveWriteResult(PlayerSaveReceipt receipt) { Receipt = receipt; }
|
||||
internal PlayerSaveWriteResult(string error) { Error = error ?? "WriteRejected"; }
|
||||
public void RequireAccepted()
|
||||
{
|
||||
if (!Accepted) throw new InvalidOperationException("Player save write rejected: " + Error);
|
||||
}
|
||||
}
|
||||
|
||||
// Buffered and LocalOnly are deliberately different from cloud confirmation.
|
||||
public enum PlayerSaveStatus { Confirmed, Buffered, LocalOnly, Rejected, Superseded, Unknown, Cancelled }
|
||||
public sealed class PlayerSaveConfirmation
|
||||
{
|
||||
public PlayerSaveStatus Status { get; }
|
||||
public bool Confirmed => Status == PlayerSaveStatus.Confirmed;
|
||||
public string Error { get; }
|
||||
internal PlayerSaveConfirmation(PlayerSaveStatus status, string error = null) { Status = status; Error = error; }
|
||||
}
|
||||
}
|
||||
@@ -1,251 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace game.PlayerDataStorage
|
||||
{
|
||||
internal enum PlayerKVGameLoadError
|
||||
{ None, BackendNotEnabled, BackendUnsupported, Input, Prepare, Schema, Apply, Stale, SideEffect, GateBusy, PreviousClosePending, PreviousCloseFailed, ClosePending, CloseFailed }
|
||||
internal enum PlayerKVGameLoadState { Rejected, LoadedForValidation, GameLoaded }
|
||||
// Preserve stage evidence without retaining a successful Prepare payload on a later load failure.
|
||||
internal sealed class PlayerKVPrepareEvidence
|
||||
{
|
||||
internal PlayerKVLoginPrepareError Error { get; }
|
||||
internal PlayerKVLoginPrepareError OriginalError { get; }
|
||||
internal game.Account.AccountLoginError? AccountError { get; }
|
||||
internal PlayerKVGameOpenError? OpenError { get; }
|
||||
internal PlayerKVSessionOpenError? OwnerError { get; }
|
||||
internal PlayerKVError? KVError { get; }
|
||||
internal PlayerKVSyncStatus? SyncStatus { get; }
|
||||
internal int? HttpStatusCode { get; }
|
||||
internal string ExceptionType { get; }
|
||||
internal string CleanupExceptionType { get; }
|
||||
internal PlayerKVPrepareEvidence(PlayerKVLoginPrepareResult value)
|
||||
{ Error=value.Error; OriginalError=value.OriginalError; AccountError=value.AccountError; OpenError=value.OpenError;
|
||||
OwnerError=value.OwnerError; KVError=value.KVError; SyncStatus=value.SyncStatus; HttpStatusCode=value.HttpStatusCode;
|
||||
ExceptionType=value.ExceptionType; CleanupExceptionType=value.CleanupExceptionType; }
|
||||
}
|
||||
internal sealed class PlayerKVGameLoadResult
|
||||
{
|
||||
internal PlayerKVGameLoadState State { get; }
|
||||
internal PlayerKVGameLoadError Error { get; }
|
||||
internal PlayerKVGameLoadError OriginalError { get; }
|
||||
internal bool CleanupFailed => Error==PlayerKVGameLoadError.CloseFailed;
|
||||
internal PlayerKVSchemaVerdict? SchemaVerdict { get; }
|
||||
internal PlayerKVPrepareEvidence Prepare { get; }
|
||||
internal long Epoch { get; }
|
||||
internal PlayerKVAccountDescriptor Account { get; }
|
||||
internal int ModuleCount { get; }
|
||||
internal int IntentCount { get; }
|
||||
internal PlayerKVGameLoadResult(PlayerKVGameLoadError error,long epoch,PlayerKVGameLoadError? original=null,
|
||||
PlayerKVSchemaVerdict? verdict=null,PlayerKVLoginPrepareResult prepare=null,int modules=0,int intents=0,bool forGame=false,PlayerKVAccountDescriptor account=null)
|
||||
{ Error=error; OriginalError=original ?? error; SchemaVerdict=verdict; Prepare=prepare==null ? null : new PlayerKVPrepareEvidence(prepare); Epoch=epoch; ModuleCount=modules; IntentCount=intents;
|
||||
Account=error==PlayerKVGameLoadError.None && forGame ? account : null;
|
||||
State=error!=PlayerKVGameLoadError.None ? PlayerKVGameLoadState.Rejected : forGame ? PlayerKVGameLoadState.GameLoaded : PlayerKVGameLoadState.LoadedForValidation; }
|
||||
}
|
||||
internal sealed class PlayerKVGameLoadCoordinator
|
||||
{
|
||||
readonly SemaphoreSlim loginGate=new SemaphoreSlim(1,1);
|
||||
readonly object closeGate=new object();
|
||||
readonly PlayerKVGameLoadTransaction transaction;
|
||||
readonly Func<bool> unstarted;
|
||||
readonly Action notify;
|
||||
readonly Func<int> intentCount;
|
||||
readonly Action<PlayerKVAccountLease> bind;
|
||||
readonly Func<Task> unbind;
|
||||
readonly PlayerKVShutdownRegistry registry;
|
||||
readonly int mainThread;
|
||||
readonly SynchronizationContext mainContext;
|
||||
PlayerKVLoginContext context;
|
||||
PlayerKVGameLoadGuard guard;
|
||||
PlayerKVGameLoadCapability capability;
|
||||
Task closing;
|
||||
CancellationTokenRegistration lifetimeRegistration;
|
||||
Action clearAfterApply;
|
||||
long epoch;
|
||||
bool loaded;
|
||||
PlayerKVGameSessionHandle gameHandle;
|
||||
internal bool LoadedForValidation => loaded && context!=null && context.IsCurrent && guard!=null && guard.IsCurrent && guard.Violations==0;
|
||||
internal int QuitObserveMs => capability?.QuitObserveMs ?? 5000;
|
||||
internal PlayerKVGameLoadCoordinator(PlayerKVGameLoadTransaction transaction,Func<bool> unstarted,Action notify,Func<int> intentCount,
|
||||
Action<PlayerKVAccountLease> bind,Func<Task> unbind,PlayerKVShutdownRegistry registry=null)
|
||||
{
|
||||
this.transaction=transaction ?? throw new ArgumentNullException(nameof(transaction));
|
||||
this.unstarted=unstarted ?? throw new ArgumentNullException(nameof(unstarted)); this.notify=notify ?? throw new ArgumentNullException(nameof(notify));
|
||||
this.intentCount=intentCount ?? throw new ArgumentNullException(nameof(intentCount)); this.bind=bind ?? throw new ArgumentNullException(nameof(bind));
|
||||
this.unbind=unbind ?? throw new ArgumentNullException(nameof(unbind)); this.registry=registry ?? PlayerKVShutdownRegistry.Process;
|
||||
mainThread=Thread.CurrentThread.ManagedThreadId; mainContext=SynchronizationContext.Current;
|
||||
}
|
||||
internal Task<PlayerKVGameLoadResult> LoadAsync(string id,PlayerKVGameLoadCapability next,CancellationToken token=default,Func<bool> identityCurrent=null)
|
||||
=> LoadCoreAsync(id,next,token,identityCurrent,false);
|
||||
internal Task<PlayerKVGameLoadResult> LoadForGameAsync(string id,PlayerKVGameLoadCapability next,CancellationToken token=default,Func<bool> identityCurrent=null)
|
||||
=> LoadCoreAsync(id,next,token,identityCurrent,true);
|
||||
internal bool TryGetCurrentGameHandle(long expectedEpoch,out PlayerKVGameSessionHandle handle)
|
||||
{
|
||||
handle=null;
|
||||
PlayerKVLoginContext owner; PlayerKVGameSessionHandle published;
|
||||
lock(closeGate)
|
||||
{
|
||||
if(closing!=null || epoch!=expectedEpoch || gameHandle==null) return false;
|
||||
owner=context; published=gameHandle;
|
||||
}
|
||||
// Never call the external identity predicate while holding a publication monitor.
|
||||
if(!owner.TryAcquireGameHandle(expectedEpoch,out var original) || !ReferenceEquals(original,published)) return false;
|
||||
lock(closeGate)
|
||||
{
|
||||
if(closing!=null || epoch!=expectedEpoch || !ReferenceEquals(context,owner) || !ReferenceEquals(gameHandle,published)) return false;
|
||||
handle=published;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
async Task<PlayerKVGameLoadResult> LoadCoreAsync(string id,PlayerKVGameLoadCapability next,CancellationToken token,Func<bool> identityCurrent,bool forGame)
|
||||
{
|
||||
if(next==null || Thread.CurrentThread.ManagedThreadId!=mainThread || string.IsNullOrEmpty(id))
|
||||
return Result(PlayerKVGameLoadError.Input);
|
||||
if(!await EnterGate(next,token)) return Result(token.IsCancellationRequested ? PlayerKVGameLoadError.Stale : PlayerKVGameLoadError.GateBusy);
|
||||
PlayerKVLoginPrepareResult prepared=null; PlayerKVSchemaVerdict? verdict=null;
|
||||
long attempt=0;
|
||||
try
|
||||
{
|
||||
// An earlier successful validation must also be explicitly revoked before replacing its account.
|
||||
if(context!=null) _=Revoke();
|
||||
var previous=await registry.ObserveAsync(next.LoginObserveMs,token,next.Delay);
|
||||
if(previous!=PlayerKVShutdownStatus.Ready) return Result(previous==PlayerKVShutdownStatus.Failed ? PlayerKVGameLoadError.PreviousCloseFailed :
|
||||
previous==PlayerKVShutdownStatus.Cancelled ? PlayerKVGameLoadError.Stale : PlayerKVGameLoadError.PreviousClosePending);
|
||||
if(token.IsCancellationRequested || !unstarted() || transaction.Poisoned) return Result(PlayerKVGameLoadError.Input);
|
||||
if(!PlayerKVGameLoadGuard.TryEnter(out var enteredGuard)) return Result(PlayerKVGameLoadError.GateBusy);
|
||||
lifetimeRegistration.Dispose();
|
||||
lock(closeGate)
|
||||
{
|
||||
guard=enteredGuard; capability=next; closing=null; loaded=false; gameHandle=null;
|
||||
attempt=Interlocked.Increment(ref epoch);
|
||||
context=next.CreateContext(id,attempt,e=>Interlocked.Read(ref epoch)==e && !token.IsCancellationRequested && (identityCurrent==null || identityCurrent()));
|
||||
}
|
||||
var owner=context;
|
||||
lifetimeRegistration=token.Register(()=> { _=Revoke(attempt); });
|
||||
var work=context.PrepareAsync(token);
|
||||
var cancelled=new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
using(token.Register(()=>cancelled.TrySetResult(true)))
|
||||
{
|
||||
if(await Task.WhenAny(work,cancelled.Task)!=work)
|
||||
return await Reject(PlayerKVGameLoadError.Stale,attempt,next,prepared,verdict);
|
||||
prepared=await work;
|
||||
}
|
||||
if(!prepared.Prepared) return await Reject(prepared.CleanupFailed ? PlayerKVGameLoadError.CloseFailed : PlayerKVGameLoadError.Prepare,attempt,next,prepared,verdict);
|
||||
if(!context.IsCurrent || epoch!=attempt) return await Reject(PlayerKVGameLoadError.Stale,attempt,next,prepared,verdict);
|
||||
if(next.RequireExistingArchive && (context.AccountDescriptor?.NewlyCreated!=false || prepared.Data==null || prepared.Data.Count==0))
|
||||
return await Reject(PlayerKVGameLoadError.Schema,attempt,next,prepared,verdict);
|
||||
var decoded=PlayerKVSaveSchema.Decode(prepared,context.AccountDescriptor,id,attempt,context.IsCurrent,next.Snapshot,next.Snapshot.Version,next.Identity);
|
||||
verdict=decoded.Verdict;
|
||||
if(decoded.Data==null) return await Reject(PlayerKVGameLoadError.Schema,attempt,next,prepared,verdict);
|
||||
if(!context.TryAcquireAccountLease(next.LeaseCapability,out var lease)) return await Reject(PlayerKVGameLoadError.Stale,attempt,next,prepared,verdict);
|
||||
bind(lease);
|
||||
PlayerKVApplyError applied;
|
||||
try { applied=transaction.Apply(decoded.Data,()=>context.IsCurrent && epoch==attempt && unstarted(),()=>guard.IsCurrent && guard.Violations==0,notify); }
|
||||
finally { var pending=clearAfterApply; clearAfterApply=null; pending?.Invoke(); }
|
||||
if(applied!=PlayerKVApplyError.None) return await Reject(applied==PlayerKVApplyError.Stale ? PlayerKVGameLoadError.Stale :
|
||||
applied==PlayerKVApplyError.SideEffect ? PlayerKVGameLoadError.SideEffect : PlayerKVGameLoadError.Apply,attempt,next,prepared,verdict);
|
||||
if(token.IsCancellationRequested || !owner.IsCurrent || enteredGuard.Violations!=0) return await Reject(PlayerKVGameLoadError.Stale,attempt,next,prepared,verdict);
|
||||
var intents=intentCount();
|
||||
PlayerKVGameSessionHandle original=null;
|
||||
if(forGame && !owner.TryAcquireGameHandle(attempt,out original)) return await Reject(PlayerKVGameLoadError.Stale,attempt,next,prepared,verdict);
|
||||
bool published=false;
|
||||
lock(closeGate)
|
||||
{
|
||||
if(closing==null && epoch==attempt && !token.IsCancellationRequested && ReferenceEquals(context,owner) && enteredGuard.IsCurrent && enteredGuard.Violations==0)
|
||||
{
|
||||
if(forGame)
|
||||
{
|
||||
enteredGuard.Dispose();
|
||||
// Dispose serializes with Reject; its final count cannot change afterwards.
|
||||
if(enteredGuard.Violations==0) { gameHandle=original; published=true; }
|
||||
}
|
||||
else { loaded=true; published=true; }
|
||||
}
|
||||
}
|
||||
if(!published) return await Reject(PlayerKVGameLoadError.Stale,attempt,next,prepared,verdict);
|
||||
return new PlayerKVGameLoadResult(PlayerKVGameLoadError.None,attempt,verdict:verdict,modules:31,intents:intents,forGame:forGame,account:context.AccountDescriptor);
|
||||
}
|
||||
catch { return await Reject(PlayerKVGameLoadError.Apply,attempt,next,prepared,verdict); }
|
||||
finally { loginGate.Release(); }
|
||||
}
|
||||
async Task<bool> EnterGate(PlayerKVGameLoadCapability next,CancellationToken token)
|
||||
{
|
||||
using(var cancellation=CancellationTokenSource.CreateLinkedTokenSource(token))
|
||||
{
|
||||
var wait=loginGate.WaitAsync(cancellation.Token); var deadline=next.Delay(next.LoginObserveMs,cancellation.Token);
|
||||
bool acquired=false,accepted=false;
|
||||
try
|
||||
{
|
||||
if(await Task.WhenAny(wait,deadline)==wait) { await wait; acquired=true; accepted=!token.IsCancellationRequested && !deadline.IsCompleted; return accepted; }
|
||||
cancellation.Cancel();
|
||||
try { await wait; acquired=true; } catch(OperationCanceledException) { }
|
||||
return false;
|
||||
}
|
||||
catch(OperationCanceledException) { return false; }
|
||||
finally
|
||||
{
|
||||
cancellation.Cancel();
|
||||
// A lock won at the timeout/cancel boundary belongs to this attempt and must be returned.
|
||||
if(acquired && !accepted) loginGate.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
internal Task Revoke(long? expectedEpoch=null)
|
||||
{
|
||||
lock(closeGate)
|
||||
{
|
||||
if(expectedEpoch.HasValue && expectedEpoch.Value!=Interlocked.Read(ref epoch)) return Task.CompletedTask;
|
||||
if(closing!=null) return closing;
|
||||
loaded=false; gameHandle=null; Interlocked.Increment(ref epoch);
|
||||
if(context==null && guard==null) return Task.CompletedTask;
|
||||
var completion=new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
closing=completion.Task;
|
||||
var owner=context; var leaseGuard=guard; var source=capability;
|
||||
// Context cancellation is synchronous and occurs even if the main-thread reset is queued.
|
||||
var drain=owner?.CloseAsync() ?? Task.CompletedTask;
|
||||
var cleared=new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
void Clear()
|
||||
{
|
||||
try
|
||||
{
|
||||
var detached=unbind();
|
||||
if(!transaction.Clear()) throw new InvalidOperationException("Controlled container is poisoned.");
|
||||
CompleteClear(detached,cleared);
|
||||
}
|
||||
catch(Exception ex) { cleared.TrySetException(ex); }
|
||||
}
|
||||
if(Thread.CurrentThread.ManagedThreadId==mainThread && transaction.IsApplying) clearAfterApply=Clear;
|
||||
else if(Thread.CurrentThread.ManagedThreadId==mainThread) Clear();
|
||||
else if(mainContext!=null) mainContext.Post(_=>Clear(),null);
|
||||
else cleared.TrySetException(new InvalidOperationException("Main-thread cleanup unavailable."));
|
||||
_=FinishClose(drain,cleared.Task,leaseGuard,source,completion);
|
||||
registry.Register(drain,owner);
|
||||
registry.Register(cleared.Task,owner);
|
||||
registry.Register(closing,owner);
|
||||
return closing;
|
||||
}
|
||||
}
|
||||
static async void CompleteClear(Task detached,TaskCompletionSource<bool> cleared)
|
||||
{ try { await detached; cleared.TrySetResult(true); } catch(Exception ex) { cleared.TrySetException(ex); } }
|
||||
static async Task FinishClose(Task drain,Task clear,PlayerKVGameLoadGuard leaseGuard,PlayerKVGameLoadCapability source,TaskCompletionSource<bool> completion)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.WhenAll(drain,clear).ConfigureAwait(false); source?.CloseUnused(); leaseGuard?.Dispose(); completion.TrySetResult(true);
|
||||
}
|
||||
catch(Exception ex) { completion.TrySetException(ex); }
|
||||
}
|
||||
async Task<PlayerKVGameLoadResult> Reject(PlayerKVGameLoadError original,long attempt,PlayerKVGameLoadCapability source,
|
||||
PlayerKVLoginPrepareResult prepare,PlayerKVSchemaVerdict? verdict)
|
||||
{
|
||||
var close=Revoke();
|
||||
var status=await registry.ObserveAsync(source.LoginObserveMs,CancellationToken.None,source.Delay);
|
||||
var error=status==PlayerKVShutdownStatus.Failed || close.IsFaulted ? PlayerKVGameLoadError.CloseFailed :
|
||||
!close.IsCompleted ? PlayerKVGameLoadError.ClosePending : original;
|
||||
return new PlayerKVGameLoadResult(error,attempt,original:prepare?.CleanupFailed==true ? PlayerKVGameLoadError.Prepare : original,verdict:verdict,prepare:prepare);
|
||||
}
|
||||
PlayerKVGameLoadResult Result(PlayerKVGameLoadError error) => new PlayerKVGameLoadResult(error,Interlocked.Read(ref epoch));
|
||||
internal Task<PlayerKVShutdownStatus> ObserveQuitAsync() => registry.ObserveAsync(QuitObserveMs,delay:capability?.Delay);
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
#if !SERVICE_HOST
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using asap.core;
|
||||
|
||||
namespace game.PlayerDataStorage
|
||||
{
|
||||
// Bind the existing game container. Missing registrations fail closed; no parallel fixture objects.
|
||||
internal static class PlayerKVGameplayBindings
|
||||
{
|
||||
static T Require<T>() where T:class => GContext.container.Resolve<T>() ??
|
||||
throw new InvalidOperationException("KV game module is not registered: "+typeof(T).FullName);
|
||||
internal static PlayerKVSchemaSnapshot Snapshot(DateTime nowUtc,string version="kv-loop")
|
||||
{
|
||||
var tables=Require<cfg.Tables>();
|
||||
return new PlayerKVSchemaSnapshot(version,tables.TbRodData.DataMap.Keys,tables.TbMapData.DataMap.Keys,
|
||||
Enum.GetNames(typeof(cfg.ConditionType)).ToDictionary(n=>n,n=>(int)Enum.Parse(typeof(cfg.ConditionType),n)),CultureInfo.InvariantCulture,
|
||||
nowUtc,tables.TbGlobalConfig.InitWheelTicket,
|
||||
tables.TbEventPackManager.DataMap.ToDictionary(p=>p.Key,p=>p.Value.PackType),
|
||||
tables.TbFishingEvent.DataMap.ToDictionary(p=>p.Key,p=>new PlayerKVEventConfig(p.Value.RedirectID,(p.Value.TimeDefinition as cfg.LimitedTime)?.EndTime)),tables.TbGlobalConfig.GlobalTimeOffset,
|
||||
tables.TbFishData.DataMap.ToDictionary(p=>p.Key,p=>new PlayerKVFishConfig(p.Key,p.Value.AreaID,p.Value.Quality,p.Value.FishCardID,p.Value.BaseWeight,p.Value.PointExtraParam,p.Value.WeightRandom)),
|
||||
tables.TbFishCard.DataMap.ToDictionary(p=>p.Key,p=>new PlayerKVFishCardConfig(p.Key,p.Value.MaxLevel,p.Value.CardsRequiredList,p.Value.WeightMagList)));
|
||||
}
|
||||
internal static PlayerKVGameLoadTransaction Bind(IKVUserLoadReceiver user)
|
||||
{
|
||||
var player=Require<GameCore.PlayerData>(); var small=Require<GameCore.SmallGameData>(); var fish=Require<GameCore.PlayerFishData>();
|
||||
var shop=Require<GameCore.PlayerShopData>(); var eventBase=Require<GameCore.FishingEventData>(); var buffs=Require<BuffDataCenter>(); var camp=Require<CampDataMM>();
|
||||
var achievements=Require<AchievementDataManager>(); var box=Require<FishingBoxData>(); var guide=Require<GameCore.GuideDataCenter>(); var battle=Require<BattlePassData>();
|
||||
var sign=Require<GameCore.SigninData>(); var trigger=Require<GameCore.TriggerPackData>(); var club=Require<ClubService>(); var challenge=Require<FishingChallengeManager>();
|
||||
var chains=Require<DataCenter.OfferChainsChestManager>(); var disco=Require<DataCenter.OfferDiscoPartyManager>(); var partner=Require<DataCenter.EventPartnerGatherManager>(); var cube=Require<EventCubeMeltManager>();
|
||||
var eventPack=Require<EventPackData>(); var piggy=Require<PiggyBankPackData>(); var bargain=Require<BargainPackData>(); var foot=Require<IFootPrintService>() as FootPrintService ?? throw new InvalidOperationException("M23 FootPrint binding mismatch."); var shooting=Require<EventShootingRangeData>();
|
||||
var potion=Require<EventPotionDataManager>(); var pinball=Require<EventPinballDataManager>(); var scratch=Require<EventScratchTicketDataManager>();
|
||||
var progress=eventBase.ThanksGivingPackData; var migration=eventBase.ChainPackWithProgressMigrationData;
|
||||
var pinata=Require<EventPinataPackDataManager>(); var digging=Require<Script.RuntimeScript.DiggingGameManager>();
|
||||
var modules=new List<IPlayerKVLoadModule>();
|
||||
void Add<T>(int id,Action reset,Action<T> replace) where T:class => modules.Add(new PlayerKVLoadModule(id,reset,d=>replace(d.Module<T>(id))));
|
||||
Add<PlayerKVPlayerState>(1,player.ResetForKVLoad,player.ReplaceFromKVDecoded); Add<PlayerKVCoreState>(2,small.ResetForKVLoad,small.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVFishState>(3,fish.ResetForKVLoad,fish.ReplaceFromKVDecoded); Add<PlayerKVShopState>(4,shop.ResetForKVLoad,shop.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVEventBaseState>(5,eventBase.ResetForKVLoad,eventBase.ReplaceFromKVDecoded); Add<PlayerKVBuffState>(6,buffs.ResetForKVLoad,buffs.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVCampState>(7,camp.ResetForKVLoad,camp.ReplaceFromKVDecoded); Add<PlayerKVAchievementState>(8,achievements.ResetForKVLoad,achievements.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVIntMapState>(9,box.ResetForKVLoad,box.ReplaceFromKVDecoded); Add<PlayerKVGuideState>(10,guide.ResetForKVLoad,guide.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVBattlePassState>(11,battle.ResetForKVLoad,battle.ReplaceFromKVDecoded); Add<PlayerKVMonthlySignState>(12,sign.ResetForKVLoad,sign.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVTriggerState>(13,trigger.ResetForKVLoad,trigger.ReplaceFromKVDecoded); Add<PlayerKVClubState>(14,club.ResetForKVLoad,club.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVChallengeState>(15,challenge.ResetForKVLoad,challenge.ReplaceFromKVDecoded); Add<PlayerKVOfferState>(16,chains.ResetForKVLoad,chains.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVOfferState>(17,disco.ResetForKVLoad,disco.ReplaceFromKVDecoded); Add<PlayerKVPartnerState>(18,partner.ResetForKVLoad,partner.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVCubeState>(19,cube.ResetForKVLoad,cube.ReplaceFromKVDecoded); Add<PlayerKVEventPackState>(20,eventPack.ResetForKVLoad,eventPack.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVPiggyBankState>(21,piggy.ResetForKVLoad,piggy.ReplaceFromKVDecoded); Add<PlayerKVBargainState>(22,bargain.ResetForKVLoad,bargain.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVFootPrintState>(23,foot.ResetForKVLoad,foot.ReplaceFromKVDecoded); Add<PlayerKVShootingRangeState>(24,shooting.ResetForKVLoad,shooting.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVPotionState>(25,potion.ResetForKVLoad,potion.ReplaceFromKVDecoded); Add<PlayerKVPinballState>(26,pinball.ResetForKVLoad,pinball.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVScratchState>(27,scratch.ResetForKVLoad,scratch.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVProgressPackPair>(28,()=>{progress.ResetForKVLoad();migration.ResetForKVLoad();},p=>{progress.ReplaceFromKVDecoded(p.Current);migration.ReplaceFromKVDecoded(p.Migration);});
|
||||
Add<PlayerKVPinataState>(29,pinata.ResetForKVLoad,pinata.ReplaceFromKVDecoded); Add<PlayerKVEmptyModuleState>(30,digging.ResetForKVLoad,digging.ReplaceFromKVDecoded);
|
||||
modules.Add(new PlayerKVLoadModule(31,user.ResetForKVLoad,d=>user.ReplaceFromKVDecoded(d.Module<PlayerKVLoginState>(31),d.Epoch,d.Raw)));
|
||||
return new PlayerKVGameLoadTransaction(modules,System.Threading.Thread.CurrentThread.ManagedThreadId,user.DiscardKVIntents);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,272 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace game.PlayerDataStorage
|
||||
{
|
||||
// Collection is limited to a synchronous gameplay fragment. It never flows across await.
|
||||
public sealed class PlayerSaveAction
|
||||
{
|
||||
[ThreadStatic] static PlayerSaveAction current;
|
||||
readonly KVPlayerSaveStore store;
|
||||
readonly Dictionary<string, PlayerSaveReceipt> receipts = new Dictionary<string, PlayerSaveReceipt>(StringComparer.Ordinal);
|
||||
readonly List<Action> presentations = new List<Action>();
|
||||
string failure;
|
||||
|
||||
PlayerSaveAction(KVPlayerSaveStore store) { this.store = store; }
|
||||
|
||||
internal static bool Accepts(KVPlayerSaveStore store)
|
||||
{
|
||||
if (current == null || ReferenceEquals(current.store, store)) return true;
|
||||
current.failure = "ForeignStoreInReward";
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static void Record(KVPlayerSaveStore store, string key, PlayerSaveWriteResult result)
|
||||
{
|
||||
if (current == null || !ReferenceEquals(current.store, store)) return;
|
||||
if (!result.Accepted) current.failure = result.Error;
|
||||
else current.receipts[key] = result.Receipt;
|
||||
}
|
||||
|
||||
public static void Present(Action presentation)
|
||||
{
|
||||
if (current == null) presentation();
|
||||
else current.presentations.Add(presentation);
|
||||
}
|
||||
|
||||
public static async Task RunAsync(IPlayerSaveStore store, string actionKey, Action apply,
|
||||
Action savePlayer, CancellationToken token = default)
|
||||
{
|
||||
if (!store.IsCurrent || token.IsCancellationRequested) throw new OperationCanceledException();
|
||||
if (!(store is KVPlayerSaveStore kv)) { apply(); return; }
|
||||
// A live login may still have an UnknownOutcome/Guarded owner. Check readiness before inventory mutation.
|
||||
kv.Read("NewPlayerDataDic").ValueOrThrow();
|
||||
if (current != null) throw new InvalidOperationException("Reward action must be awaited by its caller.");
|
||||
if (!kv.BeginAction(actionKey)) throw new InvalidOperationException("Reward is pending or requires recovery.");
|
||||
var action = new PlayerSaveAction(kv);
|
||||
try
|
||||
{
|
||||
current = action;
|
||||
apply();
|
||||
savePlayer();
|
||||
}
|
||||
finally { current = null; }
|
||||
// Keep the action reserved after any failure: replaying rewards is not recovery.
|
||||
if (action.failure != null) throw new InvalidOperationException("Reward write rejected: " + action.failure);
|
||||
if (action.receipts.Count == 0) throw new InvalidOperationException("Reward produced no save receipts.");
|
||||
foreach (var receipt in action.receipts.Values)
|
||||
{
|
||||
var result = await store.ConfirmAsync(receipt, token);
|
||||
if (!result.Confirmed) throw new InvalidOperationException("Reward save not confirmed: " + result.Status + "/" + result.Error);
|
||||
}
|
||||
if (!store.IsCurrent || token.IsCancellationRequested) throw new OperationCanceledException();
|
||||
// Presentation failure must not cause an already committed reward to replay.
|
||||
foreach (var presentation in action.presentations)
|
||||
{
|
||||
if (!store.IsCurrent || token.IsCancellationRequested) throw new OperationCanceledException();
|
||||
presentation();
|
||||
}
|
||||
kv.CompleteAction(actionKey);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class KVPlayerSaveStore : IPlayerSaveStore, IDisposable
|
||||
{
|
||||
PlayerKVGameSessionHandle handle;
|
||||
CancellationToken lifetime;
|
||||
bool bound, disposed;
|
||||
readonly object receiptsGate = new object();
|
||||
readonly Dictionary<string, PlayerKVSyncTicket> pendingReceipts = new Dictionary<string, PlayerKVSyncTicket>(StringComparer.Ordinal);
|
||||
readonly HashSet<string> actions = new HashSet<string>(StringComparer.Ordinal);
|
||||
internal bool BeginAction(string key) { lock (receiptsGate) return IsCurrent && actions.Add(key); }
|
||||
internal void CompleteAction(string key) { lock (receiptsGate) actions.Remove(key); }
|
||||
public bool IsCurrent => bound && !disposed && !lifetime.IsCancellationRequested && handle.Status != PlayerKVSyncStatus.Closed;
|
||||
|
||||
internal void Bind(PlayerKVGameSessionHandle original, CancellationToken loginLifetime)
|
||||
{
|
||||
if (bound || disposed || original == null || loginLifetime.IsCancellationRequested)
|
||||
throw new InvalidOperationException("Player save store cannot be rebound.");
|
||||
// This also checks the load guard before permitting business use.
|
||||
if (!KVMgr.ReadKV(original, "LastLoginTimeUtc").Success)
|
||||
throw new InvalidOperationException("Player save handle is not ready.");
|
||||
handle = original;
|
||||
lifetime = loginLifetime;
|
||||
bound = true;
|
||||
}
|
||||
|
||||
public PlayerSaveReadResult Read(string key)
|
||||
{
|
||||
if (!IsCurrent) return new PlayerSaveReadResult("StaleOrNotReady");
|
||||
var result = KVMgr.ReadKV(handle, key);
|
||||
return result.Success ? new PlayerSaveReadResult(result.Found.Value, result.Value) :
|
||||
new PlayerSaveReadResult(result.Error.ToString());
|
||||
}
|
||||
|
||||
public PlayerSaveWriteResult Write(string key, string value, bool remoteStore = true)
|
||||
{
|
||||
lock (receiptsGate)
|
||||
{
|
||||
if (!PlayerSaveAction.Accepts(this)) return new PlayerSaveWriteResult("ForeignStoreInReward");
|
||||
if (!IsCurrent)
|
||||
{
|
||||
var rejected = new PlayerSaveWriteResult("StaleOrNotReady");
|
||||
PlayerSaveAction.Record(this, key, rejected);
|
||||
return rejected;
|
||||
}
|
||||
var result = KVMgr.WriteKV(handle, key, value, remoteStore);
|
||||
if (result.Accepted && result.Ticket != null) pendingReceipts[key] = result.Ticket;
|
||||
var write = result.Accepted ? new PlayerSaveWriteResult(new PlayerSaveReceipt(this, result.Ticket)) :
|
||||
new PlayerSaveWriteResult(result.StateResult.Error.ToString());
|
||||
PlayerSaveAction.Record(this, key, write);
|
||||
return write;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<PlayerSaveConfirmation> WriteAndConfirmAsync(string key, string value, CancellationToken token = default)
|
||||
{
|
||||
if (token.IsCancellationRequested) return Cancelled();
|
||||
var write = Write(key, value);
|
||||
if (!write.Accepted) return new PlayerSaveConfirmation(PlayerSaveStatus.Rejected, write.Error);
|
||||
return await ConfirmAsync(write.Receipt, token);
|
||||
}
|
||||
|
||||
public async Task<PlayerSaveConfirmation> ConfirmAsync(PlayerSaveReceipt receipt, CancellationToken token = default)
|
||||
{
|
||||
if (!IsCurrent || token.IsCancellationRequested) return Cancelled();
|
||||
if (receipt == null || !ReferenceEquals(receipt.Store, this))
|
||||
return new PlayerSaveConfirmation(PlayerSaveStatus.Rejected, "ForeignReceipt");
|
||||
if (receipt.Ticket == null) return new PlayerSaveConfirmation(PlayerSaveStatus.LocalOnly);
|
||||
using (var wait = CancellationTokenSource.CreateLinkedTokenSource(lifetime, token))
|
||||
{
|
||||
try
|
||||
{
|
||||
await KVMgr.FlushKVAsync(handle, wait.Token);
|
||||
var ticket = await KVMgr.WaitForKVTicketAsync(handle, (PlayerKVSyncTicket)receipt.Ticket, wait.Token);
|
||||
if (!IsCurrent || wait.IsCancellationRequested) return Cancelled();
|
||||
if (ticket.Status == PlayerKVTicketStatus.Confirmed)
|
||||
{
|
||||
ForgetConfirmed((PlayerKVSyncTicket)receipt.Ticket);
|
||||
return new PlayerSaveConfirmation(PlayerSaveStatus.Confirmed);
|
||||
}
|
||||
return new PlayerSaveConfirmation(ticket.Status == PlayerKVTicketStatus.Unknown ? PlayerSaveStatus.Unknown :
|
||||
ticket.Status == PlayerKVTicketStatus.Superseded ? PlayerSaveStatus.Superseded :
|
||||
ticket.Status == PlayerKVTicketStatus.Cancelled ? PlayerSaveStatus.Cancelled : PlayerSaveStatus.Rejected,
|
||||
ticket.TransportError.ToString());
|
||||
}
|
||||
catch (OperationCanceledException) { return Cancelled(); }
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<PlayerSaveConfirmation> FlushAsync(CancellationToken token = default)
|
||||
{
|
||||
if (!IsCurrent || token.IsCancellationRequested) return Cancelled();
|
||||
PlayerKVSyncTicket[] captured;
|
||||
lock (receiptsGate) captured = pendingReceipts.Values.ToArray();
|
||||
using (var wait = CancellationTokenSource.CreateLinkedTokenSource(lifetime, token))
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await KVMgr.FlushKVAsync(handle, wait.Token);
|
||||
if (!IsCurrent || wait.IsCancellationRequested) return Cancelled();
|
||||
var status = result.Unknown > 0 || result.CacheStatus == PlayerKVSyncStatus.UnknownOutcome ?
|
||||
PlayerSaveStatus.Unknown : result.Blocked > 0 || result.TransportError != PlayerKVError.None ||
|
||||
result.CacheStatus != PlayerKVSyncStatus.Ready ? PlayerSaveStatus.Rejected : PlayerSaveStatus.Confirmed;
|
||||
if (status != PlayerSaveStatus.Confirmed) return new PlayerSaveConfirmation(status, result.TransportError.ToString());
|
||||
// Multiple writes before this flush are covered by the captured final receipt.
|
||||
// A replacement during the await belongs to a later flush and cannot confirm it.
|
||||
foreach (var receipt in captured)
|
||||
{
|
||||
var ticket = await KVMgr.WaitForKVTicketAsync(handle, receipt, wait.Token);
|
||||
if (!IsCurrent || wait.IsCancellationRequested) return Cancelled();
|
||||
if (ticket.Status != PlayerKVTicketStatus.Confirmed)
|
||||
return new PlayerSaveConfirmation(ticket.Status == PlayerKVTicketStatus.Superseded ? PlayerSaveStatus.Superseded :
|
||||
ticket.Status == PlayerKVTicketStatus.Unknown ? PlayerSaveStatus.Unknown :
|
||||
ticket.Status == PlayerKVTicketStatus.Cancelled ? PlayerSaveStatus.Cancelled : PlayerSaveStatus.Rejected,
|
||||
ticket.TransportError.ToString());
|
||||
ForgetConfirmed(receipt);
|
||||
}
|
||||
return new PlayerSaveConfirmation(PlayerSaveStatus.Confirmed);
|
||||
}
|
||||
catch (OperationCanceledException) { return Cancelled(); }
|
||||
}
|
||||
}
|
||||
|
||||
static PlayerSaveConfirmation Cancelled() => new PlayerSaveConfirmation(PlayerSaveStatus.Cancelled);
|
||||
void ForgetConfirmed(PlayerKVSyncTicket receipt)
|
||||
{
|
||||
lock (receiptsGate)
|
||||
if (pendingReceipts.TryGetValue(receipt.Key, out var latest) && ReferenceEquals(latest, receipt))
|
||||
pendingReceipts.Remove(receipt.Key);
|
||||
}
|
||||
public void Dispose() { disposed = true; lock (receiptsGate) { pendingReceipts.Clear(); actions.Clear(); } }
|
||||
}
|
||||
|
||||
public sealed class PlayFabPlayerSaveStore : IPlayerSaveStore
|
||||
{
|
||||
readonly PlayFabMgr manager;
|
||||
readonly CancellationToken lifetime;
|
||||
public PlayFabPlayerSaveStore(IUserService user) : this(PlayFabMgr.Instance, user.LoginLifetime) { }
|
||||
internal PlayFabPlayerSaveStore(PlayFabMgr manager, CancellationToken lifetime)
|
||||
{ this.manager = manager; this.lifetime = lifetime; }
|
||||
public bool IsCurrent => manager != null && !lifetime.IsCancellationRequested;
|
||||
|
||||
public PlayerSaveReadResult Read(string key)
|
||||
{
|
||||
if (!IsCurrent) return new PlayerSaveReadResult("StaleOrNotReady");
|
||||
try
|
||||
{
|
||||
var value = manager.GetLocalData(key);
|
||||
return new PlayerSaveReadResult(value != null, value);
|
||||
}
|
||||
catch (Exception ex) { return new PlayerSaveReadResult(ex.GetType().Name); }
|
||||
}
|
||||
|
||||
public PlayerSaveWriteResult Write(string key, string value, bool remoteStore = true)
|
||||
{
|
||||
if (!IsCurrent) return new PlayerSaveWriteResult("StaleOrNotReady");
|
||||
try
|
||||
{
|
||||
manager.UpdateUserDataValue(key, value, remoteStore);
|
||||
return new PlayerSaveWriteResult(new PlayerSaveReceipt(this, remoteStore ? (object)true : null));
|
||||
}
|
||||
catch (Exception ex) { return new PlayerSaveWriteResult(ex.GetType().Name); }
|
||||
}
|
||||
|
||||
public async Task<PlayerSaveConfirmation> WriteAndConfirmAsync(string key, string value, CancellationToken token = default)
|
||||
{
|
||||
if (!IsCurrent || token.IsCancellationRequested) return new PlayerSaveConfirmation(PlayerSaveStatus.Cancelled);
|
||||
using (var wait = CancellationTokenSource.CreateLinkedTokenSource(lifetime, token))
|
||||
{
|
||||
var cancelled = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
using (wait.Token.Register(() => cancelled.TrySetResult(false)))
|
||||
{
|
||||
try
|
||||
{
|
||||
var save = manager.SyncDataImmidiate(key, value);
|
||||
var finished = await Task.WhenAny(save, cancelled.Task);
|
||||
if (!IsCurrent || wait.IsCancellationRequested) return new PlayerSaveConfirmation(PlayerSaveStatus.Cancelled);
|
||||
return new PlayerSaveConfirmation(await finished ? PlayerSaveStatus.Confirmed : PlayerSaveStatus.Rejected);
|
||||
}
|
||||
catch (Exception ex) { return new PlayerSaveConfirmation(PlayerSaveStatus.Rejected, ex.GetType().Name); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Task<PlayerSaveConfirmation> ConfirmAsync(PlayerSaveReceipt receipt, CancellationToken token = default) =>
|
||||
Task.FromResult(new PlayerSaveConfirmation(!IsCurrent || token.IsCancellationRequested ? PlayerSaveStatus.Cancelled :
|
||||
receipt == null || !ReferenceEquals(receipt.Store, this) ? PlayerSaveStatus.Rejected :
|
||||
receipt.Ticket == null ? PlayerSaveStatus.LocalOnly : PlayerSaveStatus.Buffered));
|
||||
|
||||
public Task<PlayerSaveConfirmation> FlushAsync(CancellationToken token = default)
|
||||
{
|
||||
if (!IsCurrent || token.IsCancellationRequested)
|
||||
return Task.FromResult(new PlayerSaveConfirmation(PlayerSaveStatus.Cancelled));
|
||||
// The original manager owns its timer and callback-based buffered dispatch.
|
||||
manager.FlushBufferedData();
|
||||
return Task.FromResult(new PlayerSaveConfirmation(PlayerSaveStatus.Buffered));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
using game.PlayerDataStorage;
|
||||
using Newtonsoft.Json;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
public class SurveyService
|
||||
{
|
||||
const string SurveyUrl = "https://wesurvey.com/";
|
||||
const string SurveyData = "SurveyData";
|
||||
const string SurveyReadSidData = "SurveyReadSidData";
|
||||
|
||||
readonly IPlayerSaveStore saveStore;
|
||||
readonly HashSet<string> pendingRewards = new HashSet<string>();
|
||||
IUserService userService;
|
||||
cfg.Tables tables;
|
||||
List<string> receivedReward = new List<string>();
|
||||
List<string> readSid = new List<string>();
|
||||
public SurveyData tableData;
|
||||
public SurveyService(IUserService userService, cfg.Tables tables, IPlayerSaveStore saveStore)
|
||||
{
|
||||
this.userService = userService;
|
||||
this.tables = tables;
|
||||
tableData = null;
|
||||
this.saveStore = saveStore;
|
||||
receivedReward = ReadList(SurveyData);
|
||||
readSid = ReadList(SurveyReadSidData);
|
||||
}
|
||||
List<string> ReadList(string key)
|
||||
{
|
||||
var read = saveStore.Read(key);
|
||||
var json = read.ValueOrThrow();
|
||||
if (read.Found == false) return new List<string>();
|
||||
if (string.IsNullOrEmpty(json)) throw new JsonSerializationException("Empty survey save: " + key);
|
||||
return JsonConvert.DeserializeObject<List<string>>(json) ?? throw new JsonSerializationException("Null survey save: " + key);
|
||||
}
|
||||
public bool IsCurrent => saveStore.IsCurrent && !userService.LoginLifetime.IsCancellationRequested;
|
||||
/// <summary>
|
||||
/// 拉取问卷
|
||||
/// </summary>
|
||||
public void GetSurveyMatchlist()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
PlayerData playerData = GContext.container.Resolve<PlayerData>();
|
||||
|
||||
List<SurveyData> surveyDataTable = tables.TbSurveyData.DataList;
|
||||
ulong userId = Convert.ToUInt64(userService.UserId, saveStore is KVPlayerSaveStore ? 10 : 16);
|
||||
tableData = null;
|
||||
if (surveyDataTable.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < surveyDataTable.Count; i++)
|
||||
{
|
||||
if (!receivedReward.Contains(surveyDataTable[i].ID.ToString()))
|
||||
{
|
||||
if (surveyDataTable[i].LevelCondition >= playerData.lv)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int tailNumber = (int)(userId % 10);
|
||||
if (!surveyDataTable[i].TailNumberList.Contains(tailNumber))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
tableData = surveyDataTable[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
////设置红点
|
||||
SetRedPoint();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError("GetSurveyMatchlist:" + e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SetRedPoint()
|
||||
{
|
||||
if (tableData == null)
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState("menu.survey", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState("menu.survey", true);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 请求答题的链接
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string GetSurveyAnswer()
|
||||
{
|
||||
if (!IsCurrent || tableData == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
string url = $"{SurveyUrl}s2/{tableData.ID}/{tableData.Hash}/?&uid={userService.CustomId}";
|
||||
//标记已读
|
||||
var next = new List<string>(readSid);
|
||||
if (!next.Contains(tableData.ID.ToString())) next.Add(tableData.ID.ToString());
|
||||
saveStore.Write(SurveyReadSidData, JsonConvert.SerializeObject(next)).RequireAccepted();
|
||||
readSid = next;
|
||||
GetSurveyMatchlist();
|
||||
return url;
|
||||
}
|
||||
/// <summary>
|
||||
/// 检查是否能领奖
|
||||
/// </summary>
|
||||
public async Task GetSurveyCheckAnswer(SurveyData survey)
|
||||
{
|
||||
if (!IsCurrent) throw new OperationCanceledException();
|
||||
if (survey == null) return;
|
||||
string sid = survey.ID.ToString();
|
||||
if (pendingRewards.Contains(sid)) throw new InvalidOperationException("Survey reward is pending or requires recovery.");
|
||||
if (receivedReward.Contains(sid)) return;
|
||||
pendingRewards.Add(sid);
|
||||
var items = GContext.container.Resolve<PlayerItemData>();
|
||||
var player = GContext.container.Resolve<PlayerData>();
|
||||
await PlayerSaveAction.RunAsync(saveStore, "survey:" + sid, () =>
|
||||
{
|
||||
items.AddItemByDrop(survey.Reward, null);
|
||||
PlayerSaveAction.Present(() => GContext.Publish(new ShowData()));
|
||||
receivedReward.Add(sid);
|
||||
saveStore.Write(SurveyData, JsonConvert.SerializeObject(receivedReward)).RequireAccepted();
|
||||
}, () => player.SavePlayerData(), userService.LoginLifetime);
|
||||
if (!IsCurrent) throw new OperationCanceledException();
|
||||
pendingRewards.Remove(sid);
|
||||
GetSurveyMatchlist();
|
||||
}
|
||||
}
|
||||
@@ -1,557 +0,0 @@
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
namespace game.PlayerDataStorage
|
||||
{
|
||||
internal static partial class PlayerKVGameplayLoop
|
||||
{
|
||||
internal static async Task<string> VerifyFishingRewardForEditorAsync()
|
||||
{
|
||||
if(UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode || GContext.Ready ||
|
||||
Resources.FindObjectsOfTypeAll<GContext>().Length!=0 || PlayFabMgr.HasLiveInstanceForKVLoad || Enabled || handle!=null ||
|
||||
File.Exists(Path.Combine(Root,"permit.json")) || validationCleanupFailure!=null)
|
||||
throw new InvalidOperationException("Idle editor without a real permit required.");
|
||||
if(Interlocked.CompareExchange(ref validationRunning,1,0)!=0) throw new InvalidOperationException("Validation running.");
|
||||
var saved=(Enabled,Ready,Error,Permit,RunPath,handle,onFault,onClose,collecting,mainThread,failed,mainContext,fishingAction,rewardPlan,rewardAction);
|
||||
var oldContainer=GContext.container;var oldEvents=FishingContextAccess.Events;
|
||||
var folder=Path.Combine(Root,"reward-fixed-runs",DateTime.UtcNow.ToString("yyyyMMddTHHmmss")+"_"+Guid.NewGuid().ToString("N"));
|
||||
EquipmentFixture fixture=null;Task pending=null;Container container=null;PlayerData player=null;
|
||||
RewardSubscribers subscribers=null;
|
||||
var oldLanguages=LocalizationMgr.enumLanguage;
|
||||
var textTable=typeof(LocalizationMgr).GetField("table",BindingFlags.Static|BindingFlags.NonPublic);
|
||||
var oldTextTable=textTable.GetValue(null);
|
||||
GameObject pfShell=null;var pfField=typeof(PlayFabMgr).GetField("_instance",BindingFlags.Static|BindingFlags.NonPublic);var oldPF=pfField.GetValue(null);
|
||||
int checks=0,groups=0;var passed=new List<string>();string result=null;
|
||||
var ioDiagnostics=new System.Collections.Concurrent.ConcurrentQueue<string>();
|
||||
EventHandler<System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs> observeIO=(_,e)=>
|
||||
{if(e.Exception is IOException || e.Exception is UnauthorizedAccessException)ioDiagnostics.Enqueue(e.Exception.ToString());};
|
||||
AppDomain.CurrentDomain.FirstChanceException+=observeIO;
|
||||
void Check(bool yes,string reason) {checks++;if(!yes)throw new InvalidOperationException("Reward validation: "+reason);}
|
||||
async Task Start(string name)
|
||||
{
|
||||
subscribers?.Dispose();subscribers=null;
|
||||
player?.StopRecoveryEnergy();player=null;
|
||||
if(fixture!=null)await fixture.Close();if(pending!=null){try{await pending;}catch{}pending=null;}
|
||||
pfField.SetValue(null,oldPF);if(pfShell!=null)UnityEngine.Object.DestroyImmediate(pfShell);pfShell=null;
|
||||
FishingContextAccess.Use(oldContainer);FishingContextAccess.Events=oldEvents;container?.Dispose();container=null;
|
||||
fixture=new EquipmentFixture(Path.Combine(folder,(++groups).ToString()));
|
||||
fixture.Remote["Map_5_102"]="{\"10211\":\"0|10|20|20|False\",\"10241\":\"2|20|30|40|False\"}";
|
||||
var owner=await fixture.Open();
|
||||
Enabled=true;Ready=false;Error=null;failed=0;handle=null;collecting=null;
|
||||
Permit=new PlayerKVLoopPermit{Version=3,Operation="fishing-reward-once",PlayerId="101",
|
||||
AllowedKeys=new[]{"NewPlayerDataDic","Map_5_102","BattlePassRecordTask","late"}};
|
||||
RunPath=folder;mainThread=Thread.CurrentThread.ManagedThreadId;mainContext=SynchronizationContext.Current;
|
||||
Attach(_=>fixture.Faults++,fixture.Close);Publish(owner);
|
||||
fishingAction=new FishingAction(new object(),owner){Consuming=true,Confirmed=true,Continued=true};
|
||||
rewardPlan=new FishingRewardPlan{Fish=10211,Area=102};
|
||||
rewardAction=new FishingRewardAction(fishingAction,new object(),rewardPlan);
|
||||
passed.Add(name);
|
||||
}
|
||||
PlayerKVEditResult Batch(bool localFinal=false)
|
||||
{
|
||||
using(BeginRewardSegment("strike")) Write("BattlePassRecordTask","strike",true);
|
||||
rewardAction.Ending=true;using(BeginRewardSegment("ending")){}
|
||||
rewardAction.Applying=true;
|
||||
PlayerKVEditResult primary;
|
||||
using(BeginRewardSegment("reward"))
|
||||
{
|
||||
Write("BattlePassRecordTask","reward-task",true);
|
||||
Write("Map_5_102","fish",!localFinal);
|
||||
Write("NewPlayerDataDic","intermediate",false);
|
||||
primary=Write("NewPlayerDataDic","reward-main",true);
|
||||
}
|
||||
rewardAction.GuideNotifications=1;
|
||||
return primary;
|
||||
}
|
||||
async Task Reject(Task task,string label)
|
||||
{
|
||||
bool rejected=false;try{await task;}catch(InvalidOperationException){rejected=true;}
|
||||
Check(rejected && !Ready && fixture.Faults==1,label);
|
||||
}
|
||||
void RejectSync(Action body,string label)
|
||||
{bool rejected=false;try{body();}catch(InvalidOperationException){rejected=true;}Check(rejected,label);}
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(folder);
|
||||
// Exercise the real async Init path, using isolated services and the actual loaded guide asset.
|
||||
var potionSource=JObject.Parse(File.ReadAllText(Path.GetFullPath(Path.Combine(Application.dataPath,
|
||||
"../../CodexOutput/upserver/KVGameplayLoopTests/backups/20260928T082052Z/101.json"))))["data"].ToObject<Dictionary<string,string>>();
|
||||
var guideAssetHandle=UnityEngine.AddressableAssets.Addressables.LoadAssetAsync<object>("GuidanceConfig");
|
||||
var guideAsset=(GuidanceDefineSetting)guideAssetHandle.WaitForCompletion();
|
||||
var savedGuideGroups=guideAsset.guidanceGroupDefines;
|
||||
var awaitAssembly=typeof(IEnumeratorAwaitExtensions).Assembly;
|
||||
var syncType=awaitAssembly.GetType("UnityAsyncAwaitUtil.SyncContextUtil",true);
|
||||
var syncField=syncType.GetField("<UnitySynchronizationContext>k__BackingField",BindingFlags.Static|BindingFlags.NonPublic);
|
||||
var threadField=syncType.GetField("<UnityThreadId>k__BackingField",BindingFlags.Static|BindingFlags.NonPublic);
|
||||
var runnerField=awaitAssembly.GetType("UnityAsyncAwaitUtil.AsyncCoroutineRunner",true).GetField("_instance",BindingFlags.Static|BindingFlags.NonPublic);
|
||||
var savedSync=syncField.GetValue(null);var savedThread=threadField.GetValue(null);var savedRunner=runnerField.GetValue(null);
|
||||
syncField.SetValue(null,SynchronizationContext.Current);threadField.SetValue(null,Thread.CurrentThread.ManagedThreadId);
|
||||
try
|
||||
{
|
||||
foreach(var scenario in new[]{"unavailable-real-101","available","query-failure","node-panel-mismatch",
|
||||
"other-group","finished","index-zero","missing-save","out-of-range","preserve-other-pointer"})
|
||||
{
|
||||
await Start("potion-actual-init-"+scenario);
|
||||
rewardPlan=null;rewardAction=null;
|
||||
Permit.AllowedKeys=Permit.AllowedKeys.Concat(new[]{"GuideSaveDataNew"}).ToArray();
|
||||
var tables=new cfg.Tables(n=>SimpleJSON.JSON.Parse(File.ReadAllText(Path.GetFullPath(Path.Combine(Application.dataPath,"../../cfgData",n+".json")))));
|
||||
container=new Container();FishingContextAccess.Use(container);FishingContextAccess.Events=new EventAggregator();
|
||||
player=new PlayerData(tables,null);player.lv=62;
|
||||
container.RegisterInstance(tables);container.RegisterInstance(player);
|
||||
var eventData=new FishingEventData(tables);
|
||||
if(scenario!="query-failure")container.RegisterInstance(eventData);
|
||||
if(scenario=="available")
|
||||
{
|
||||
var ev=tables.TbFishingEvent.DataList.First(e=>e.Type==4 && e.SubType==5);
|
||||
typeof(cfg.FishingEvent).GetProperty("TimeDefinition").SetValue(ev,new cfg.LimitedTime(
|
||||
ZZTimeHelper.UtcNow().AddHours(-1).ToString("yyyy/MM/dd HH:mm:ss",System.Globalization.CultureInfo.InvariantCulture),
|
||||
ZZTimeHelper.UtcNow().AddHours(1).ToString("yyyy/MM/dd HH:mm:ss",System.Globalization.CultureInfo.InvariantCulture)));
|
||||
typeof(cfg.FishingEvent).GetProperty("ConditionList").SetValue(ev,new List<cfg.EventCondition>{
|
||||
new cfg.EventCondition(cfg.ConditionType.AccountLevel,new List<string>{"1"},"")});
|
||||
Check(eventData.GetEvent(4,5)==ev.ID,"isolated actual query sees available potion event");
|
||||
}
|
||||
else if(scenario!="query-failure")Check(eventData.GetEvent(4,5)==-1,"isolated actual query sees no potion event");
|
||||
var guideState=new GuideDataCenter(tables,player);container.RegisterInstance(guideState);
|
||||
var diagnostics=new PlayerKVSchemaDiagnostics();guideState.ReplaceFromKVDecoded(GuideDataCenter.DecodeForKVLoad(potionSource,diagnostics));
|
||||
Check(diagnostics.TotalErrors==0,"real 101 guide source decoded");
|
||||
var potionGroup=JsonUtility.FromJson<GuidanceGroupDefine>(JsonUtility.ToJson(savedGuideGroups.First(g=>g.groupName==GroupName.EventPotion01)));
|
||||
guideAsset.guidanceGroupDefines=savedGuideGroups.Select(g=>g.groupName==GroupName.EventPotion01?potionGroup:g).ToArray();
|
||||
var key=GroupName.EventPotion01.ToString();var entry=guideState.GuideSaveDataDic[key];
|
||||
Check(entry.guideSubIndex==4 && !entry.isFinish,"real historical potion progress is unfinished at four");
|
||||
if(scenario=="node-panel-mismatch")potionGroup.guidanceDefines[entry.guideSubIndex].panelName="DifferentPanel";
|
||||
if(scenario=="other-group")
|
||||
{
|
||||
guideState.GuideSaveDataDic.Remove(key);key=GroupName.EventPotion02.ToString();
|
||||
entry.groupName=key;guideState.GuideSaveDataDic[key]=entry;
|
||||
guideAsset.guidanceGroupDefines=guideAsset.guidanceGroupDefines.Where(g=>g.groupName!=GroupName.EventPotion02).ToArray();
|
||||
potionGroup.groupName=GroupName.EventPotion02;
|
||||
}
|
||||
if(scenario=="finished")entry.isFinish=true;
|
||||
if(scenario=="index-zero")entry.guideSubIndex=0;
|
||||
if(scenario=="missing-save")guideState.GuideSaveDataDic.Remove(key);
|
||||
if(scenario=="out-of-range")entry.guideSubIndex=potionGroup.guidanceDefines.Length;
|
||||
var otherGroup=savedGuideGroups.First(g=>g.groupName==GroupName.Fish_01);
|
||||
if(scenario=="preserve-other-pointer")
|
||||
{
|
||||
guideState.curGroupDefine=otherGroup;guideState.curDefine=otherGroup.guidanceDefines[0];
|
||||
guideState.curSaveData=guideState.GuideSaveDataDic[otherGroup.groupName.ToString()];
|
||||
}
|
||||
var before=JsonConvert.SerializeObject(guideState.GuideSaveDataDic);
|
||||
pfShell=new GameObject("KV inactive guide Init fixture");pfShell.SetActive(false);pfShell.hideFlags=HideFlags.HideAndDontSave;
|
||||
var pf=pfShell.AddComponent<PlayFabMgr>();
|
||||
Check(pf.IsUnstartedForKVLoad,"guide fixture never starts PF lifecycle");pfField.SetValue(null,pf);
|
||||
var initErrors=new List<string>();var initWarnings=new List<string>();int completeEvents=0;
|
||||
Application.LogCallback onLog=(message,stack,type)=>{if(type==LogType.Exception || type==LogType.Error)initErrors.Add(message);if(type==LogType.Warning)initWarnings.Add(message);};
|
||||
Application.logMessageReceived+=onLog;
|
||||
var completion=GContext.OnEvent<ConditionTypeEvent>().Subscribe(e=>{if(e.type==cfg.ConditionType.CompleteGuide)completeEvents++;});
|
||||
bool initStarted=false;
|
||||
try
|
||||
{
|
||||
// Asset is preloaded above: Init has no outstanding asset operation when teardown begins.
|
||||
initStarted=true;guideState.Init();
|
||||
var deadline=System.Diagnostics.Stopwatch.StartNew();
|
||||
while(guideState.guidanceGroupDefines==null && deadline.ElapsedMilliseconds<10000)await Task.Delay(10);
|
||||
await Task.Delay(1);
|
||||
Check(guideState.guidanceGroupDefines!=null && initErrors.Count==0,"actual Init completed without asynchronous errors: "+scenario+"; "+string.Join(" | ",initErrors));
|
||||
if(scenario=="unavailable-real-101")
|
||||
{
|
||||
Check(guideState.curDefine==null && guideState.curGroupDefine==null && guideState.curSaveData==null,
|
||||
"unavailable historical potion guide is not activated by actual Init");
|
||||
Check(before==JsonConvert.SerializeObject(guideState.GuideSaveDataDic),"deferred actual Init preserves every guide entry");
|
||||
guideState.AddIndex(true);GContext.Publish(new GuideNextEvent());
|
||||
Check(before==JsonConvert.SerializeObject(guideState.GuideSaveDataDic) && completeEvents==0,
|
||||
"real AddIndex and GuideNext cannot finish deferred potion progress");
|
||||
Check(!handle.ReadLocalSnapshot().Data.ContainsKey("GuideSaveDataNew") && fixture.Sets==0,
|
||||
"deferral and callbacks introduce no guide write or remote request");
|
||||
}
|
||||
else if(scenario=="preserve-other-pointer")
|
||||
Check(ReferenceEquals(guideState.curGroupDefine,otherGroup) && ReferenceEquals(guideState.curDefine,otherGroup.guidanceDefines[0]),"deferral does not clear another active guide");
|
||||
else if(scenario=="available" || scenario=="query-failure" || scenario=="node-panel-mismatch" || scenario=="other-group")
|
||||
Check(ReferenceEquals(guideState.curGroupDefine,potionGroup) && guideState.curDefine!=null,"non-deferred input retains original recovery: "+scenario);
|
||||
else
|
||||
Check(guideState.curGroupDefine==null && guideState.curDefine==null,"original non-resuming boundary retained: "+scenario);
|
||||
if(scenario!="missing-save")Check(ReferenceEquals(guideState.GuideSaveDataDic[key],entry),"saved progress object retained: "+scenario);
|
||||
if(scenario=="query-failure")Check(initWarnings.Any(w=>w.Contains("Potion guide availability")),"query failure is reported and not treated as unavailable");
|
||||
}
|
||||
finally
|
||||
{
|
||||
completion.Dispose();Application.logMessageReceived-=onLog;guideState.Dispose();
|
||||
guideAsset.guidanceGroupDefines=savedGuideGroups;
|
||||
if(initStarted)UnityEngine.AddressableAssets.Addressables.Release(guideAsset);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
guideAsset.guidanceGroupDefines=savedGuideGroups;
|
||||
UnityEngine.AddressableAssets.Addressables.Release(guideAssetHandle);
|
||||
var runner=runnerField.GetValue(null) as MonoBehaviour;
|
||||
if(runner!=null && !ReferenceEquals(runner,savedRunner))UnityEngine.Object.DestroyImmediate(runner.gameObject);
|
||||
runnerField.SetValue(null,savedRunner);syncField.SetValue(null,savedSync);threadField.SetValue(null,savedThread);
|
||||
}
|
||||
await Start("original-tickets-cross-segment-supersession");var primary=Batch();
|
||||
Check(collecting==null && rewardAction.Segment==null && rewardAction.Records.Count==5,"three scopes disposed and all original writes captured");
|
||||
await ConfirmRewardAsync(rewardAction,primary);int shows=0;PresentReward(rewardAction,()=>shows++);PresentReward(rewardAction,()=>shows++);
|
||||
Check(shows==1 && fixture.Remote["Map_5_102"]=="fish" && fixture.Remote["BattlePassRecordTask"]=="reward-task","final values and one presentation");
|
||||
|
||||
await Start("pending-cannot-present");primary=Batch();fixture.HoldSet=true;
|
||||
pending=ConfirmRewardAsync(rewardAction,primary);await fixture.SetEntered.Task;
|
||||
Check(!rewardAction.Confirmed && !rewardAction.Presented,"pending is not success");
|
||||
fixture.ReleaseSet.TrySetResult(true);await pending;pending=null;Check(rewardAction.Confirmed,"response confirms original action");
|
||||
|
||||
await Start("localOnly-map-final-rejected");await Reject(ConfirmRewardAsync(rewardAction,Batch(true)),"local map not remote confirmed");
|
||||
await Start("outside-segment-before-final-save-rejected");
|
||||
using(BeginRewardSegment("strike"))Write("BattlePassRecordTask","strike",true);
|
||||
RejectSync(()=>Write("NewPlayerDataDic","external",true),"external changed write denied before later reward can hide it");
|
||||
Check(fixture.Sets==0 && ReadRejected(),"stopped facade and no outgoing request");
|
||||
|
||||
await Start("same-value-external-supersession-still-rejected");primary=Batch();
|
||||
Write("NewPlayerDataDic","reward-main",true);
|
||||
await Reject(ConfirmRewardAsync(rewardAction,primary),"same-value external ticket is not the original final ticket");
|
||||
await Start("unexpected-backed-up-key-before-admission");
|
||||
using(BeginRewardSegment("strike"))RejectSync(()=>Write("late","unrelated",true),"backed-up but unrelated reward key rejected");
|
||||
Check(rewardAction.Records.Count==0 && fixture.Sets==0,"no unrelated admission");
|
||||
|
||||
await Start("lost-response-no-reward-replay");primary=Batch();fixture.LoseResponse=true;
|
||||
await Reject(ConfirmRewardAsync(rewardAction,primary),"unknown result stops reward");Check(fixture.Sets==1 && !rewardAction.Presented,"single request only");
|
||||
await Start("unauthorized-no-reauthentication");primary=Batch();fixture.Unauthorized=true;
|
||||
await Reject(ConfirmRewardAsync(rewardAction,primary),"401 stops reward");Check(fixture.Auth==1 && fixture.Sets==1,"no relogin or replay");
|
||||
await Start("partial-commit-is-not-success");primary=Batch();fixture.PartialCommit=true;fixture.LoseResponse=true;
|
||||
await Reject(ConfirmRewardAsync(rewardAction,primary),"partial result remains failure");Check(fixture.Sets==1 && !rewardAction.Confirmed,"no refund or second write");
|
||||
await Start("cancel-after-consume-before-reward-confirmed");primary=Batch();fixture.HoldSet=true;
|
||||
pending=ConfirmRewardAsync(rewardAction,primary);await fixture.SetEntered.Task;
|
||||
CancelFishing(fishingAction,"actual consumed action cancellation");fixture.ReleaseSet.TrySetResult(true);
|
||||
await Reject(pending,"C1 Continued cannot exempt pending reward");pending=null;Check(rewardAction.Cancelled && !rewardAction.Presented,"old callbacks revoked");
|
||||
await Start("confirmed-normal-cancel-no-second-exit");primary=Batch();await ConfirmRewardAsync(rewardAction,primary);
|
||||
CancelReward(rewardAction.Controller,"normal disable");CancelFishing(fishingAction,"normal actor destroy");
|
||||
Check(rewardAction.Cancelled && fishingAction.Cancelled && fixture.Faults==0 && Ready,"confirmed normal disable never faults");
|
||||
await Start("battle-controller-disable-still-cancels");
|
||||
CancelReward(rewardAction.Controller,"controller disabled");
|
||||
Check(rewardAction.Cancelled && !Ready && fixture.Faults==1,"battle controller disable still cancels");
|
||||
await Start("ending-teardown-pre-confirm-not-cancel");primary=Batch();
|
||||
CancelReward(rewardAction.Controller,"controller disabled");CancelReward(rewardAction.Controller,"controller destroyed");
|
||||
Check(!rewardAction.Cancelled && Ready && fixture.Faults==0,"ending teardown window is not a cancellation");
|
||||
await ConfirmRewardAsync(rewardAction,primary);shows=0;PresentReward(rewardAction,()=>shows++);
|
||||
Check(shows==1 && rewardAction.Confirmed && rewardAction.Presented,"reward completes and presents after teardown window");
|
||||
await Start("repeated-segment-and-sealed-action-rejected");
|
||||
using(BeginRewardSegment("strike")){}
|
||||
RejectSync(()=>BeginRewardSegment("strike"),"strike cannot repeat");
|
||||
|
||||
await Start("reward-storage-error-before-send");
|
||||
var file=Directory.GetFiles(Path.Combine(folder,groups.ToString()),"state.json",SearchOption.AllDirectories).Single();
|
||||
using(var locked=new FileStream(file,FileMode.Open,FileAccess.Read,FileShare.None))
|
||||
using(BeginRewardSegment("strike"))RejectSync(()=>Write("NewPlayerDataDic","locked",true),"real state-file storage failure");
|
||||
Check(fixture.Sets==0 && !Ready,"storage fault before network");
|
||||
|
||||
foreach(var scenario in new[]{"non-record","new-record","new-fish"})
|
||||
{
|
||||
await Start("real-fish-final-save-"+scenario);
|
||||
var tables=new cfg.Tables(n=>SimpleJSON.JSON.Parse(File.ReadAllText(Path.GetFullPath(Path.Combine(Application.dataPath,"../../cfgData",n+".json")))));
|
||||
container=new Container();FishingContextAccess.Use(container);FishingContextAccess.Events=new EventAggregator();container.RegisterInstance(tables);
|
||||
player=new PlayerData(tables,null);var fish=new PlayerFishData(tables,null);container.RegisterInstance(player);container.RegisterInstance(fish);
|
||||
var core=new PlayerKVCoreState(new object[]{60,100UL,0,393,5,4,30003,new DateTime(2026,1,1),0,112,7UL,2,102,102,13,102,0,0,0});
|
||||
player.ReplaceFromKVDecoded(new PlayerKVPlayerState(core,new Dictionary<string,string>(),new Dictionary<int,int>{{-1,1}},new List<int>(),0,false,"0"));
|
||||
var data=new Dictionary<int,FishWeightData>{{10241,FishWeightData.Deserialize("2|20|30|40|False")}};
|
||||
if(scenario!="new-fish")data[10211]=FishWeightData.Deserialize("0|10|20|20|False");
|
||||
var fields=BindingFlags.Instance|BindingFlags.NonPublic;
|
||||
typeof(PlayerFishData).GetField("fishData",fields).SetValue(fish,data);
|
||||
typeof(PlayerFishData).GetField("mapFishData",fields).SetValue(fish,new Dictionary<int,Dictionary<int,FishWeightData>>{{102,data}});
|
||||
pfShell=new GameObject("KV reward unstarted PF fixture");pfShell.SetActive(false);pfShell.hideFlags=HideFlags.HideAndDontSave;
|
||||
var pf=pfShell.AddComponent<PlayFabMgr>();Check(pf.IsUnstartedForKVLoad,"PF lifecycle never starts");pfField.SetValue(null,pf);
|
||||
rewardAction.BeforeFishCount=scenario=="new-fish"?0:20;
|
||||
using(BeginRewardSegment("strike")){}rewardAction.Ending=true;using(BeginRewardSegment("ending")){}
|
||||
rewardAction.Applying=true;
|
||||
var events=new List<cfg.ConditionType>();using(GContext.OnEvent<ConditionTypeEvent>().Subscribe(e=>events.Add(e.type)))
|
||||
using(BeginRewardSegment("reward"))
|
||||
{
|
||||
fish.SetGetFishCount(10211);fish.SetWeight(10211,scenario=="new-record"?12:5);
|
||||
fish.SaveCaughtFishForKVLoop(10211);
|
||||
primary=Write("NewPlayerDataDic","fixture-main",true);
|
||||
}
|
||||
rewardAction.GuideNotifications=1;await ConfirmRewardAsync(rewardAction,primary);
|
||||
var savedMap=JsonConvert.DeserializeObject<Dictionary<int,string>>(fixture.Remote["Map_5_102"]);
|
||||
var value=FishWeightData.Deserialize(savedMap[10211]);
|
||||
Check(value.TotalNum==(scenario=="new-fish"?10:30),"original count increment exactly once");
|
||||
Check(value.MaxG==(scenario=="new-record"?12:scenario=="new-fish"?5:10),"original maximum weight rule");
|
||||
Check(savedMap[10241]=="2|20|30|40|False" && value.TotalCard==(scenario=="new-fish"?0:20) && value.Level==0,"other fish and card data unchanged");
|
||||
Check(events.Count==2 && events.Contains(cfg.ConditionType.GetFishWeight) && events.Contains(cfg.ConditionType.GetFishID),"final save never republishes fish events");
|
||||
var fishSchema=new PlayerKVSchemaSnapshot("reward-fish",tables.TbRodData.DataMap.Keys,tables.TbMapData.DataMap.Keys,new Dictionary<string,int>(),System.Globalization.CultureInfo.CurrentCulture,new DateTime(2026,9,24),0,
|
||||
fish:tables.TbFishData.DataMap.ToDictionary(p=>p.Key,p=>new PlayerKVFishConfig(p.Key,p.Value.AreaID,p.Value.Quality,p.Value.FishCardID,p.Value.BaseWeight,p.Value.PointExtraParam,p.Value.WeightRandom)),
|
||||
cards:tables.TbFishCard.DataMap.ToDictionary(p=>p.Key,p=>new PlayerKVFishCardConfig(p.Key,p.Value.MaxLevel,p.Value.CardsRequiredList,p.Value.WeightMagList)));
|
||||
var fishErrors=new PlayerKVSchemaDiagnostics();
|
||||
var decodedFish=PlayerKVSaveSchema.DecodeFish(new Dictionary<string,string>{{"Map_5_102",fixture.Remote["Map_5_102"]}},new Dictionary<string,string>(),fishSchema,fishErrors);
|
||||
Check(decodedFish!=null && fishErrors.TotalErrors==0 && decodedFish.Maps[102][10211].Number==value.TotalNum && decodedFish.RawMaps[102][10211].Weight==value.MaxG,"real Map schema reads the saved catch");
|
||||
}
|
||||
foreach(var cash in new[]{0,122})
|
||||
{
|
||||
await Start("actual-main-reward-and-subscribers-cash-"+cash);
|
||||
var tables=new cfg.Tables(n=>SimpleJSON.JSON.Parse(File.ReadAllText(Path.GetFullPath(Path.Combine(Application.dataPath,"../../cfgData",n+".json")))));
|
||||
container=new Container();FishingContextAccess.Use(container);FishingContextAccess.Events=new EventAggregator();container.RegisterInstance(tables);
|
||||
LocalizationMgr.enumLanguage=tables.TbLanguageConfig.DataList.Select(x=>x.ID).ToList();textTable.SetValue(null,null);
|
||||
player=new PlayerData(tables,null);var fish=new PlayerFishData(tables,null);var small=new SmallGameData(tables,null,null);
|
||||
var achievements=new AchievementDataManager(tables);var box=new FishingBoxData();var events=new FishingEventData(tables);
|
||||
container.RegisterInstance(player);container.RegisterInstance(fish);container.RegisterInstance(small);container.RegisterInstance(achievements);container.RegisterInstance(box);container.RegisterInstance(events);
|
||||
fish.ResetForKVLoad();small.ResetForKVLoad();achievements.ResetForKVLoad();box.ResetForKVLoad();achievements.Init();
|
||||
var core=new PlayerKVCoreState(new object[]{60,100UL,0,393,5,4,30003,new DateTime(2026,1,1),0,112,7UL,2,102,102,13,102,0,0,0});
|
||||
player.ReplaceFromKVDecoded(new PlayerKVPlayerState(core,new Dictionary<string,string>(),new Dictionary<int,int>{{-1,1}},new List<int>(),0,false,"0"));
|
||||
subscribers=new RewardSubscribers();subscribers.Bind(tables,events,container);
|
||||
Permit.AllowedKeys=Permit.AllowedKeys.Concat(rewardTaskKeys).Distinct().ToArray();
|
||||
int countEvents=0;var seen=new List<cfg.ConditionType>();
|
||||
using(GContext.OnEvent<ConditionTypeEvent>().Subscribe(e=>{seen.Add(e.type);if(e.type==cfg.ConditionType.GetFishCount)countEvents++;}))
|
||||
{
|
||||
using(BeginRewardSegment("strike"))GContext.Publish(new ConditionTypeEvent(cfg.ConditionType.PerfectStrikeCount,1));
|
||||
rewardAction.Ending=true;using(BeginRewardSegment("ending")){}
|
||||
rewardAction.Applying=true;
|
||||
using(BeginRewardSegment("reward"))
|
||||
{
|
||||
// Actual unchanged data methods; visual/formula extraction is tested separately.
|
||||
player.SaveFishingFrequency();
|
||||
GContext.Publish(new ConditionTypeEvent(cfg.ConditionType.CashFromFishing,cash));
|
||||
GContext.Publish(new ConditionTypeEvent(cfg.ConditionType.MaxCashFromFishing,cash/10));
|
||||
if(cash>0)player.AddGold((ulong)cash);
|
||||
foreach(var condition in new[]{cfg.ConditionType.GetFishWeight,cfg.ConditionType.GetFishID,cfg.ConditionType.GetBoosFish,cfg.ConditionType.GetBiggerThanHugeFishCount})
|
||||
GContext.Publish(new ConditionTypeEvent(condition,2));
|
||||
Write("Map_5_102",Read("Map_5_102"),true);
|
||||
primary=player.SaveFishingRewardForKVLoop();
|
||||
}
|
||||
}
|
||||
rewardAction.GuideNotifications=1;await ConfirmRewardAsync(rewardAction,primary);
|
||||
var modern=JsonConvert.DeserializeObject<Dictionary<string,string>>(fixture.Remote["NewPlayerDataDic"]);
|
||||
var coreValues=modern["newData"].Split(',');
|
||||
Check(coreValues[1]==(100+cash).ToString() && coreValues[14]=="14" && coreValues[15]=="112","cash and one catch with original x10 total frequency");
|
||||
Check(coreValues[3]=="393" && coreValues[9]=="112" && countEvents==1,"reward never consumes again or repeats count event");
|
||||
Check(modern["ad"]==achievements.AchievementDataToJson() && achievements.GetAchievementDatas(cfg.ConditionType.GetFishCount)==1,"last primary serializes real achievement subscriber");
|
||||
Check(modern.ContainsKey("ld") && modern["ld"].Split('|').Length==3,"original luck serialization preserved");
|
||||
Check(subscribers.Battle.Data.BattleTask.dic_dailyTasks.Any(k=>tables.TbBattlePassTask.GetOrDefault(k.Key).Type==cfg.ConditionType.PerfectStrikeCount && k.Value==1),"S1 real battle task writes");
|
||||
Check(subscribers.Lucky.DicTasks.All(k=>k.Value==0) && subscribers.Magic.DicTasks.All(k=>k.Value==0) && subscribers.Mini.Data.Exp==0,"actual lucky/magic/mini ignore reward conditions absent from their real config");
|
||||
foreach(var condition in rewardConditions.Where(x=>x!=cfg.ConditionType.ConsumeEnergy))
|
||||
Check(seen.Count(x=>x==condition)==1,"reward condition once: "+condition);
|
||||
Check(fixture.Remote.ContainsKey("BattlePassRecordTask") && !fixture.Remote.ContainsKey("MiniBPKeyNew") && !fixture.Remote.ContainsKey("EventLuckGameRecord") && !fixture.Remote.ContainsKey("EventLuckMagicPlayfabData"),"only applicable original task saves confirmed; no invented task updates");
|
||||
var schema=new PlayerKVSchemaSnapshot("reward-main",tables.TbRodData.DataMap.Keys,tables.TbMapData.DataMap.Keys,new Dictionary<string,int>(),System.Globalization.CultureInfo.CurrentCulture,new DateTime(2026,9,24),0);
|
||||
var errors=new PlayerKVSchemaDiagnostics();Check(PlayerKVSaveSchema.DecodeCore(new Dictionary<string,string>(),modern,schema,errors)!=null && errors.TotalErrors==0,"main schema reads final original serializer");
|
||||
}
|
||||
await Start("actual-reentry-and-old-permit-gates");
|
||||
Check(!BeginRewardDrawing(rewardAction.Controller),"duplicate drawing never reaches original battle");
|
||||
rewardAction.Ending=true;Check(!BeginRewardEnding(rewardAction.Controller,true),"duplicate fish end never opens another panel");
|
||||
rewardAction.Applying=true;Check(BindRewardPanel(new object())==null,"second panel never reapplies rewards");
|
||||
Permit.Version=2;Permit.Operation="fishing-consume-once";RejectSync(()=>BeginRewardDrawing(new object()),"v2 cannot draw");
|
||||
await Start("v1-cannot-draw");Permit.Version=1;Permit.Operation=null;RejectSync(()=>BeginRewardDrawing(new object()),"v1 cannot draw");
|
||||
await Start("natural-failure-stops-without-reward");
|
||||
RejectSync(()=>BeginRewardEnding(rewardAction.Controller,false),"natural failure not converted to catch");
|
||||
Check(fixture.Sets==0 && rewardAction.Records.Count==0 && !rewardAction.Applying,"failure has no reward or refund");
|
||||
foreach(bool complete in new[]{false,true})
|
||||
{
|
||||
await Start(complete?"actual-panel-disable-confirmed":"actual-panel-disable-pending");
|
||||
primary=Batch();if(complete)await ConfirmRewardAsync(rewardAction,primary);
|
||||
var shell=new GameObject("KV inactive reward panel lifecycle fixture");shell.SetActive(false);shell.hideFlags=HideFlags.HideAndDontSave;
|
||||
try
|
||||
{
|
||||
var panel=shell.AddComponent<FishingRewardPanel>();rewardAction.Panel=panel;
|
||||
typeof(FishingRewardPanel).GetMethod("OnDisable",BindingFlags.Instance|BindingFlags.NonPublic).Invoke(panel,null);
|
||||
Check(rewardAction.Cancelled && fixture.Faults==(complete?0:1),"actual panel OnDisable revokes pending only");
|
||||
Check(!RewardPanelCurrent(rewardAction,panel),"old panel continuation is revoked");
|
||||
}
|
||||
finally{UnityEngine.Object.DestroyImmediate(shell);}
|
||||
}
|
||||
await Start("real-backup-no-special-fish-guide-preflight");
|
||||
container=new Container();FishingContextAccess.Use(container);FishingContextAccess.Events=new EventAggregator();
|
||||
var guide=new GuideDataCenter(null,null);container.RegisterInstance(guide);
|
||||
var backup=JsonConvert.DeserializeObject<Dictionary<string,object>>(File.ReadAllText(Path.GetFullPath(Path.Combine(Application.dataPath,"../../CodexOutput/upserver/KVGameplayLoopTests/backups/20260924T084836Z/102.json"))));
|
||||
var raw=JsonConvert.DeserializeObject<Dictionary<string,string>>(backup["data"].ToString());
|
||||
var guideErrors=new PlayerKVSchemaDiagnostics();guide.ReplaceFromKVDecoded(GuideDataCenter.DecodeForKVLoad(raw,guideErrors));
|
||||
guide.guidanceGroupDefines=UnityEditor.AssetDatabase.LoadAssetAtPath<GuidanceDefineSetting>("Assets/ABPackage/pkgPrepare/Export/GuidanceConfig.asset").guidanceGroupDefines;
|
||||
Check(guideErrors.TotalErrors==0,"real guide backup decodes");
|
||||
var gateTables=new cfg.Tables(n=>SimpleJSON.JSON.Parse(File.ReadAllText(Path.GetFullPath(Path.Combine(Application.dataPath,"../../cfgData",n+".json")))));
|
||||
container.RegisterInstance(gateTables);
|
||||
rewardPlan.Data=new FishingData{curFishData=gateTables.TbFishData.GetOrDefault(10211)};
|
||||
var guideBefore=JsonConvert.SerializeObject(guide.GuideSaveDataDic);
|
||||
CheckRewardGuide();
|
||||
Check(Ready && fixture.Sets==0 && guideBefore==JsonConvert.SerializeObject(guide.GuideSaveDataDic),"ordinary fish admitted without guide writes or consumption");
|
||||
var originalGroups=guide.guidanceGroupDefines;
|
||||
foreach(var kind in new[]{"ordinary-incomplete","active-node","active-group","last-node","extra-node","missing-node","trigger","parameter","unknown-group","positive-skill","last-skill","escape-skill","active-skill","active-dash","active-dirty"})
|
||||
{
|
||||
// Read-only fault probes share an isolated fixture; no real run is reset.
|
||||
var guideCopy=JsonUtility.FromJson<GuidanceGroupDefine>(JsonUtility.ToJson(originalGroups.First(g=>g.groupName.ToString()=="FishSkill01")));
|
||||
var baseFish=gateTables.TbFishData.GetOrDefault(10211);
|
||||
var fishJson=SimpleJSON.JSON.Parse(baseFish.json.ToString());
|
||||
var testFish=new cfg.FishData(fishJson);
|
||||
var testData=new FishingData{curFishData=testFish};
|
||||
guide.guidanceGroupDefines=originalGroups.Select(g=>g.groupName==guideCopy.groupName?guideCopy:g).ToArray();
|
||||
guide.curDefine=null;guide.curGroupDefine=null;
|
||||
guide.GuideSaveDataDic["Fish_01"].isFinish=true;
|
||||
switch(kind)
|
||||
{
|
||||
case "ordinary-incomplete":guide.GuideSaveDataDic["Fish_01"].isFinish=false;break;
|
||||
case "active-node":guide.curDefine=guideCopy.guidanceDefines[0];break;
|
||||
case "active-group":guide.curGroupDefine=guideCopy;break;
|
||||
case "last-node":guideCopy.guidanceDefines.Last().panelName="FishingRewardPanel";break;
|
||||
case "extra-node":guideCopy.guidanceDefines=guideCopy.guidanceDefines.Concat(new[]{new GuidanceDefine{panelName="FishingPanel_Advanced"}}).ToArray();break;
|
||||
case "missing-node":guideCopy.guidanceDefines=Array.Empty<GuidanceDefine>();break;
|
||||
case "trigger":guideCopy.TriggerConditionsType=GuideTriggerConditions.Next;break;
|
||||
case "parameter":guideCopy.TriggerConditionsParameter="changed";break;
|
||||
case "unknown-group":guideCopy.groupName=(GroupName)999999;break;
|
||||
case "positive-skill":case "last-skill":case "escape-skill":
|
||||
var skillJson=SimpleJSON.JSON.Parse(baseFish.FishEscape.json.ToString());skillJson["SpSkillID"]=123456789;
|
||||
if(kind=="escape-skill")fishJson["FishEscape"]=skillJson;
|
||||
else {var list=new SimpleJSON.JSONArray();if(kind=="last-skill")list.Add(SimpleJSON.JSON.Parse(baseFish.FishEscape.json.ToString()));list.Add(skillJson);fishJson["FishSKillList"]=list;}
|
||||
testData.curFishData=new cfg.FishData(fishJson);break;
|
||||
case "active-skill":testData.fishSpSkillData=new CorruptSpSkillData();break;
|
||||
case "active-dash":testData.superDashSpSkillData=new SuperDashSpSkillData();break;
|
||||
case "active-dirty":testData.dirtyStuckSpSkillData=new DirtyStuckSpSkillData();break;
|
||||
}
|
||||
// Stop() is idempotent; all cases are read-only gate probes, not fresh consumption attempts.
|
||||
RejectSync(()=>CheckRewardGuide(testData),"guide/skill drift denied: "+kind);
|
||||
Check(fixture.Sets==0,"gate drift never writes: "+kind);
|
||||
}
|
||||
guide.guidanceGroupDefines=originalGroups;guide.curDefine=null;guide.curGroupDefine=null;guide.GuideSaveDataDic["Fish_01"].isFinish=true;
|
||||
|
||||
foreach(var scenario in new[]{"ordinary","zero-cruise","cruise","invalid-cruise","free","retry","fixed","duel","special-item","missing-map","wrong-area","event-field","event-count","trigger-pack","activity-drift","guide-before-S1","guide-before-S2","guide-before-S3"})
|
||||
{
|
||||
await Start("actual-full-reward-preflight-"+scenario);
|
||||
var tables=new cfg.Tables(n=>SimpleJSON.JSON.Parse(File.ReadAllText(Path.GetFullPath(Path.Combine(Application.dataPath,"../../cfgData",n+".json")))));
|
||||
container=new Container();FishingContextAccess.Use(container);FishingContextAccess.Events=new EventAggregator();container.RegisterInstance(tables);
|
||||
player=new PlayerData(tables,null);var fish=new PlayerFishData(tables,null);fish.ResetForKVLoad();
|
||||
var events=new FishingEventData(tables);var guideState=new GuideDataCenter(tables,player);
|
||||
var diag=new PlayerKVSchemaDiagnostics();guideState.ReplaceFromKVDecoded(GuideDataCenter.DecodeForKVLoad(raw,diag));guideState.guidanceGroupDefines=originalGroups;
|
||||
guideState.guidanceDefineSetting=UnityEditor.AssetDatabase.LoadAssetAtPath<GuidanceDefineSetting>("Assets/ABPackage/pkgPrepare/Export/GuidanceConfig.asset");
|
||||
var scopes=new Activity.ActivityScopeManager();var manager=new Activity.ActivityManager(tables,scopes);
|
||||
container.RegisterInstance(player);container.RegisterInstance(fish);container.RegisterInstance(events);container.RegisterInstance(guideState);
|
||||
container.RegisterInstance(new CollectingData());container.RegisterInstance(new PiggyBankPackData());container.RegisterInstance(new BargainPackData());container.RegisterInstance(new FishingChallengeManager());
|
||||
container.RegisterInstance(scopes);container.RegisterInstance(manager);
|
||||
player.ReplaceFromKVDecoded(new PlayerKVPlayerState(new PlayerKVCoreState(new object[]{60,100UL,0,393,5,4,30003,new DateTime(2026,1,1),0,112,7UL,2,102,102,13,102,0,0,0}),new Dictionary<string,string>(),new Dictionary<int,int>{{-1,1}},new List<int>(),0,false,"0"));
|
||||
player.IsOpenMagnification=true;fish.IsOpenPiercing=true;
|
||||
var data=new FishingData{curFishData=tables.TbFishData.GetOrDefault(10211),fishItem=tables.TbItem.DataList.First(x=>x.RedirectID==10211 && x.SubType==1),RodId=30003,MapId=102};
|
||||
rewardPlan=null;rewardAction=null;
|
||||
switch(scenario)
|
||||
{
|
||||
case "zero-cruise":case "cruise":case "invalid-cruise":
|
||||
typeof(PlayerFishData).GetField("fishSwimToMap",BindingFlags.Instance|BindingFlags.NonPublic).SetValue(fish,new Dictionary<int,int>{{102,scenario=="zero-cruise"?0:scenario=="cruise"?10211:-2}});break;
|
||||
case "free":data.IsFree=true;break;case "retry":data.IsRetry=true;break;case "duel":data.IsDuel=true;break;
|
||||
case "fixed":data.fixedFishList=tables.TbFixedFishList.DataList.First();break;
|
||||
case "special-item":data.fishItem=tables.TbItem.DataList.First(x=>x.SubType==11);break;
|
||||
case "missing-map":Permit.AllowedKeys=new[]{"NewPlayerDataDic"};break;
|
||||
case "wrong-area":var fj=SimpleJSON.JSON.Parse(data.curFishData.json.ToString());fj["AreaID"]=999999;data.curFishData=new cfg.FishData(fj);break;
|
||||
case "event-field":var ev=tables.TbFishingEvent.DataList.First(x=>x.ID==10100000);typeof(cfg.FishingEvent).GetProperty("PopupPriority").SetValue(ev,ev.PopupPriority+1);break;
|
||||
case "event-count":tables.TbFishingEvent.DataList.Remove(tables.TbFishingEvent.DataList.First(x=>x.ID==10100000));break;
|
||||
case "trigger-pack":typeof(cfg.TriggerPackManager).GetProperty("Condition").SetValue(tables.TbTriggerPackManager.DataList.First(),cfg.ConditionType.GetFishCount);break;
|
||||
}
|
||||
if(scenario=="ordinary" || scenario=="zero-cruise" || scenario=="activity-drift" || scenario.StartsWith("guide-before-"))
|
||||
{
|
||||
PrepareFishingReward(fishingAction,data);
|
||||
Check(Ready && rewardPlan.Data==data && fixture.Sets==0,"actual full preflight admitted before any consumption");
|
||||
if(scenario=="activity-drift")
|
||||
{scopes.CreateActivityScope((1,1));RejectSync(CheckRewardContext,"actual activity scope drift denied");}
|
||||
else if(scenario.StartsWith("guide-before-"))
|
||||
{
|
||||
var actor=new object();
|
||||
if(scenario!="guide-before-S1") {Check(BeginRewardDrawing(actor),"first drawing");using(BeginRewardSegment("strike")){} }
|
||||
if(scenario=="guide-before-S3") {Check(BeginRewardEnding(actor,true),"first ending");using(BeginRewardSegment("ending")){} }
|
||||
guideState.curDefine=originalGroups.First().guidanceDefines.First();
|
||||
if(scenario=="guide-before-S1")RejectSync(()=>BeginRewardDrawing(actor),"active guide before drawing");
|
||||
else if(scenario=="guide-before-S2")RejectSync(()=>BeginRewardEnding(actor,true),"active guide before ending");
|
||||
else RejectSync(()=>BindRewardPanel(new object()),"active guide before reward data");
|
||||
}
|
||||
else
|
||||
{
|
||||
var actor=new object();Check(BeginRewardDrawing(actor),"actual prepare-draw");using(BeginRewardSegment("strike")){}
|
||||
Check(BeginRewardEnding(actor,true),"actual ending");using(BeginRewardSegment("ending")){}
|
||||
Check(BindRewardPanel(new object())!=null,"actual panel binding with before count");
|
||||
using(GContext.OnEvent<OnEventTriggerGuide>().Subscribe(guideState.TriggerGuideNext))
|
||||
using(BeginRewardSegment("reward")){GContext.Publish(new OnEventTriggerGuide("FishingRewardPanel"));RecordRewardGuideNotification();}
|
||||
Check(rewardAction.GuideNotifications==1 && guideState.curDefine==null && guideState.curGroupDefine==null,"one original guide notification and no activated guide");
|
||||
}
|
||||
}
|
||||
else RejectSync(()=>PrepareFishingReward(fishingAction,data),"full gate rejects "+scenario);
|
||||
Check(fixture.Sets==0,"preflight case no network or consumed energy: "+scenario);
|
||||
}
|
||||
await Start("reward-confirmation-timeout-no-replay");primary=Batch();fixture.HoldSet=true;
|
||||
pending=ConfirmRewardAsync(rewardAction,primary);await fixture.SetEntered.Task;
|
||||
await Reject(pending,"actual 30-second timeout");pending=null;fixture.ReleaseSet.TrySetResult(true);
|
||||
Check(fixture.Sets==1 && !rewardAction.Confirmed && !rewardAction.Presented,"timeout never repeats reward");
|
||||
result=JsonConvert.SerializeObject(new{success=true,groups,assertions=checks,passed,formalRequests=0,synthetic=true,folder});
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
subscribers?.Dispose();subscribers=null;
|
||||
LocalizationMgr.enumLanguage=oldLanguages;textTable.SetValue(null,oldTextTable);
|
||||
player?.StopRecoveryEnergy();if(fixture!=null)await fixture.Close();if(pending!=null){try{await pending;}catch{}}
|
||||
pfField.SetValue(null,oldPF);if(pfShell!=null)UnityEngine.Object.DestroyImmediate(pfShell);
|
||||
FishingContextAccess.Use(oldContainer);FishingContextAccess.Events=oldEvents;container?.Dispose();
|
||||
(Enabled,Ready,Error,Permit,RunPath,handle,onFault,onClose,collecting,mainThread,failed,mainContext,fishingAction,rewardPlan,rewardAction)=saved;
|
||||
}
|
||||
catch {validationCleanupFailure=fixture;throw;}
|
||||
finally
|
||||
{
|
||||
AppDomain.CurrentDomain.FirstChanceException-=observeIO;
|
||||
if(Directory.Exists(folder))File.WriteAllText(Path.Combine(folder,"storage-diagnostics.json"),JsonConvert.SerializeObject(ioDiagnostics.ToArray()));
|
||||
Interlocked.Exchange(ref validationRunning,0);
|
||||
}
|
||||
}
|
||||
File.WriteAllText(Path.Combine(folder,"result.json"),result);return result;
|
||||
}
|
||||
sealed class RewardSubscribers:IDisposable
|
||||
{
|
||||
const System.Reflection.BindingFlags Fields=System.Reflection.BindingFlags.Instance|System.Reflection.BindingFlags.NonPublic;
|
||||
static readonly System.Reflection.FieldInfo PF=typeof(PlayFabMgr).GetField("_instance",System.Reflection.BindingFlags.Static|System.Reflection.BindingFlags.NonPublic);
|
||||
static readonly System.Reflection.FieldInfo Red=typeof(RedPointManager).GetField("instance",System.Reflection.BindingFlags.Static|System.Reflection.BindingFlags.NonPublic);
|
||||
readonly object oldRed=Red.GetValue(null),oldPF=PF.GetValue(null);
|
||||
GameObject shell; LuckyTaskData magicTask; MiniBattlePassDataProvider provider;
|
||||
internal BattlePassDataProvider Battle; internal EventLuckyGameModel Lucky;
|
||||
internal EventLuckMagicModel Magic; internal MiniBattlePassDataModel Mini;
|
||||
internal void Bind(cfg.Tables tables,FishingEventData events,Container container)
|
||||
{
|
||||
Red.SetValue(null,new RedPointManager());
|
||||
shell=new GameObject("KV inactive subscriber storage fixture");shell.SetActive(false);shell.hideFlags=HideFlags.HideAndDontSave;
|
||||
var pf=shell.AddComponent<PlayFabMgr>();
|
||||
if(!pf.IsUnstartedForKVLoad)throw new InvalidOperationException("No PF lifecycle may start in this fixture.");
|
||||
PF.SetValue(null,pf);
|
||||
var items=new PlayerItemData(tables);container.RegisterInstance(items);
|
||||
var data=new BattlePassData(tables);
|
||||
data.BattlePassRecord.ID=tables.TbFishingEvent.DataList.First(e=>tables.TbBattlePassMain.GetOrDefault(e.RedirectID)!=null).ID;
|
||||
data.BattlePassRecord.Level=1;
|
||||
foreach(var t in tables.TbBattlePassTask.DataList.Where(t=>rewardConditions.Contains(t.Type)))data.BattleTask.dic_dailyTasks.Add(t.TaskID,0);
|
||||
Battle=new BattlePassDataProvider(tables,data,items);container.RegisterInstance(Battle);
|
||||
var common=tables.TbCommonTask.DataList.Where(t=>rewardConditions.Contains(t.TaskType)).Select(t=>t.Id).ToArray();
|
||||
Lucky=new EventLuckyGameModel(events,tables);foreach(var id in common)Lucky.DicTasks.Add(id,0);
|
||||
Magic=new EventLuckMagicModel{DicTasks=common.ToDictionary(x=>x,x=>0),HigherRingData=EventLuckMagicHigherRingData.CreateEmpty()};
|
||||
var rings=(List<EventLuckMagicRing>)typeof(EventLuckMagicModel).GetField("_rings",Fields).GetValue(Magic);
|
||||
for(int i=1;i<=3;i++)rings.Add(EventLuckMagicRing.CreateEmpty(1,0,(EEventLuckMagicRingType)i));
|
||||
magicTask=new LuckyTaskData(Magic);typeof(EventLuckMagicModel).GetField("_taskData",Fields).SetValue(Magic,magicTask);
|
||||
provider=new MiniBattlePassDataProvider(tables,items);
|
||||
var table=tables.TbMiniBattlePass.DataList.First(t=>t.TaskType==cfg.ConditionType.ConsumeEnergy && t.Param==0);
|
||||
var activity=tables.TbFishingEvent.DataList.First(t=>t.Type==9 && t.RedirectID==table.Id);
|
||||
var type=(MiniBattlePassType)table.Type;
|
||||
Mini=provider.GetModel(type);
|
||||
provider.GetMiniBattlePassRecord("BP_"+type).Id=activity.ID;
|
||||
typeof(MiniBattlePassDataModel).GetField("fishingEvent",Fields).SetValue(Mini,activity);
|
||||
typeof(MiniBattlePassDataModel).GetMethod("InitData",Fields).Invoke(Mini,new object[]{table.Id});
|
||||
// Synthetic fixture has an active window; real-run time is never modified.
|
||||
Mini.startTime=ZZTimeHelper.UtcNow().AddDays(-1);
|
||||
typeof(MiniBattlePassDataModel).GetProperty("endTime").SetValue(Mini,ZZTimeHelper.UtcNow().AddDays(1));
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
Battle?.Dispose();Lucky?.Dispose();magicTask?.Dispose();
|
||||
if(Mini!=null)(typeof(MiniBattlePassDataModel).GetField("disposables",Fields).GetValue(Mini) as IDisposable)?.Dispose();
|
||||
PF.SetValue(null,oldPF);Red.SetValue(null,oldRed);
|
||||
if(shell!=null)UnityEngine.Object.DestroyImmediate(shell);
|
||||
}
|
||||
}
|
||||
static bool ReadRejected() {try{Read("NewPlayerDataDic");return false;}catch(InvalidOperationException){return true;}}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,330 +0,0 @@
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using UniRx;
|
||||
using DG.Tweening;
|
||||
using UnityEngine;
|
||||
|
||||
namespace game.PlayerDataStorage
|
||||
{
|
||||
internal static partial class PlayerKVGameplayLoop
|
||||
{
|
||||
// Only a fixed idle-editor test; the handler inherited from EquipmentFixture never forwards requests.
|
||||
internal static async Task<string> VerifyFishingForEditorAsync()
|
||||
{
|
||||
if(UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode || GContext.Ready ||
|
||||
Resources.FindObjectsOfTypeAll<GContext>().Length!=0 || PlayFabMgr.HasLiveInstanceForKVLoad ||
|
||||
Enabled || handle!=null || File.Exists(Path.Combine(Root,"permit.json")) || validationCleanupFailure!=null)
|
||||
throw new InvalidOperationException("Idle editor without a real permit required.");
|
||||
if(Interlocked.CompareExchange(ref validationRunning,1,0)!=0) throw new InvalidOperationException("Validation running.");
|
||||
var saved=(Enabled,Ready,Error,Permit,RunPath,handle,onFault,onClose,collecting,mainThread,failed,mainContext,fishingAction);
|
||||
var oldContainer=GContext.container; var oldEvents=FishingContextAccess.Events;
|
||||
var folder=Path.Combine(Root,"fishing-fixed-runs",DateTime.UtcNow.ToString("yyyyMMddTHHmmss")+"_"+Guid.NewGuid().ToString("N"));
|
||||
EquipmentFixture fixture=null; Task pending=null; Container container=null; PlayerData player=null; IDisposable subscription=null;
|
||||
var oldLanguages=LocalizationMgr.enumLanguage;
|
||||
var textTable=typeof(LocalizationMgr).GetField("table",System.Reflection.BindingFlags.Static|System.Reflection.BindingFlags.NonPublic);
|
||||
var oldTextTable=textTable.GetValue(null);
|
||||
FishingSubscribers subscribers=null;
|
||||
int checks=0,groups=0; var passed=new List<string>(); string result=null;
|
||||
void Check(bool yes,string reason) { checks++; if(!yes) throw new InvalidOperationException("Fishing validation: "+reason); }
|
||||
async Task Start(string name)
|
||||
{
|
||||
subscribers?.Dispose(); subscribers=null;
|
||||
player?.StopRecoveryEnergy(); player=null; subscription?.Dispose(); subscription=null;
|
||||
if(fixture!=null) await fixture.Close();
|
||||
if(pending!=null) { try { await pending; } catch { } pending=null; }
|
||||
FishingContextAccess.Use(oldContainer); FishingContextAccess.Events=oldEvents; container?.Dispose(); container=null;
|
||||
fixture=new EquipmentFixture(Path.Combine(folder,(++groups).ToString()));
|
||||
var owner=await fixture.Open();
|
||||
Enabled=true; Ready=false; Error=null; failed=0; handle=null; collecting=null; fishingAction=null;
|
||||
Permit=new PlayerKVLoopPermit{Version=2,Operation="fishing-consume-once",PlayerId="101",AllowedKeys=new[]{"k","NewPlayerDataDic","late"}};
|
||||
RunPath=folder; mainThread=Thread.CurrentThread.ManagedThreadId; mainContext=SynchronizationContext.Current;
|
||||
Attach(_=>fixture.Faults++,fixture.Close); Publish(owner);
|
||||
passed.Add(name);
|
||||
}
|
||||
(FishingAction action,WriteBatch batch,PlayerKVEditResult primary) Batch(bool duplicates=false)
|
||||
{
|
||||
var action=BeginFishing(new object()); Check(ClaimFishingConsumption(action),"first consume admitted");
|
||||
var batch=BeginFishingWrites(action);
|
||||
if(duplicates) { Write("NewPlayerDataDic","intermediate-local",false); Write("NewPlayerDataDic","intermediate-remote",true); }
|
||||
Write("k","task",true); var primary=Write("NewPlayerDataDic","final",true); batch.Dispose();
|
||||
return(action,batch,primary);
|
||||
}
|
||||
async Task Reject(Task task,string label)
|
||||
{
|
||||
bool rejected=false; try { await task; } catch(InvalidOperationException) { rejected=true; }
|
||||
Check(rejected && !Ready && fixture.Faults==1,label);
|
||||
}
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(folder);
|
||||
await Start("duplicate-state-writes-and-original-final-tickets");
|
||||
var b=Batch(true); Check(b.batch.Records.Count==4 && !b.batch.Records[0].Remote && b.batch.Records[0].Edit.Ticket==null,"local intermediate captured");
|
||||
await ConfirmFishingAsync(b.action,b.batch,b.primary);
|
||||
Check(fixture.Remote["NewPlayerDataDic"]=="final" && fixture.Remote["k"]=="task","both final values persisted");
|
||||
int continued=0; ContinueFishing(b.action,()=>continued++); ContinueFishing(b.action,()=>continued++);
|
||||
Check(continued==1 && !ClaimFishingConsumption(b.action) && BeginFishing(new object())==null,"continue and consume/cast only once");
|
||||
|
||||
await Start("pending-response-does-not-continue"); b=Batch(); fixture.HoldSet=true;
|
||||
pending=ConfirmFishingAsync(b.action,b.batch,b.primary);
|
||||
Check(ReferenceEquals(await Task.WhenAny(fixture.SetEntered.Task,pending,Task.Delay(5000)),fixture.SetEntered.Task),"Set entered");
|
||||
Check(!b.action.Confirmed && !b.action.Continued,"no continuation during pending response");
|
||||
fixture.ReleaseSet.TrySetResult(true); await pending; pending=null;
|
||||
Check(b.action.Confirmed,"original response confirms");
|
||||
|
||||
await Start("final-localOnly-rejected-before-flush"); b=Batch();
|
||||
// A sealed record ledger from actual writes with a localOnly final key.
|
||||
collecting=b.batch; Write("k","local-tail",false); b.batch.Dispose();
|
||||
await Reject(ConfirmFishingAsync(b.action,b.batch,b.primary),"local final cannot report remote success"); Check(fixture.Sets==0,"no flush of rejected batch");
|
||||
|
||||
await Start("external-final-supersession"); b=Batch(); Write("NewPlayerDataDic","later-action",true);
|
||||
await Reject(ConfirmFishingAsync(b.action,b.batch,b.primary),"never substitute later ticket");
|
||||
|
||||
await Start("lost-response-after-commit"); b=Batch(); fixture.LoseResponse=true;
|
||||
await Reject(ConfirmFishingAsync(b.action,b.batch,b.primary),"Unknown stops run");
|
||||
Check(fixture.Sets==1 && fixture.Remote["NewPlayerDataDic"]=="final","one uncertain commit, no replay");
|
||||
|
||||
await Start("unauthorized-does-not-reauthenticate"); b=Batch(); fixture.Unauthorized=true;
|
||||
await Reject(ConfirmFishingAsync(b.action,b.batch,b.primary),"401 stops run"); Check(fixture.Auth==1 && fixture.Sets==1,"single auth and Set");
|
||||
|
||||
await Start("cancelled-object-does-not-continue-after-commit"); b=Batch(); fixture.HoldSet=true;
|
||||
pending=ConfirmFishingAsync(b.action,b.batch,b.primary); await fixture.SetEntered.Task;
|
||||
CancelFishing(b.action,"synthetic disable"); fixture.ReleaseSet.TrySetResult(true);
|
||||
await Reject(pending,"disabled original action cannot continue"); pending=null;
|
||||
Check(!b.action.Continued && b.action.Cancelled,"no stale continuation");
|
||||
|
||||
await Start("close-before-confirmation"); b=Batch(); await fixture.Close();
|
||||
await Reject(ConfirmFishingAsync(b.action,b.batch,b.primary),"closed owner rejected"); Check(fixture.Sets==0,"close does not invent flush");
|
||||
|
||||
await Start("partial-commit-is-not-atomic-success"); b=Batch(); fixture.PartialCommit=true; fixture.LoseResponse=true;
|
||||
await Reject(ConfirmFishingAsync(b.action,b.batch,b.primary),"partial unknown stops run");
|
||||
Check(fixture.Sets==1 && fixture.Remote["k"]=="task" && fixture.Remote["NewPlayerDataDic"]=="original-save","partial commit retained without compensation");
|
||||
|
||||
await Start("storage-error-before-send");
|
||||
var stateFile=Directory.GetFiles(Path.Combine(folder,groups.ToString()),"state.json",SearchOption.AllDirectories).Single();
|
||||
using(var locked=new FileStream(stateFile,FileMode.Open,FileAccess.Read,FileShare.None))
|
||||
{
|
||||
bool storage=false; try { Write("NewPlayerDataDic","locked",true); } catch(InvalidOperationException e) { storage=e.Message.Contains("StorageError"); }
|
||||
Check(storage && !Ready && fixture.Faults==1 && fixture.Sets==0,"storage failure cannot be accepted or retried");
|
||||
}
|
||||
|
||||
foreach(bool complete in new[]{false,true})
|
||||
{
|
||||
await Start(complete?"actual-tween-autoKill-after-complete":"actual-tween-kill-before-complete");
|
||||
var shell=new GameObject("KV inactive fishing tween fixture"); shell.SetActive(false); shell.hideFlags=HideFlags.HideAndDontSave;
|
||||
Tween tween=null;
|
||||
// DOTween.Init deliberately does nothing in EditMode; Kill otherwise returns
|
||||
// before reaching the real tween engine. Scope this fixed fixture gate to this
|
||||
// synchronous block and restore it even when an assertion fails.
|
||||
var initialized=typeof(DOTween).GetField("initialized",System.Reflection.BindingFlags.Static|System.Reflection.BindingFlags.NonPublic|System.Reflection.BindingFlags.Public);
|
||||
var wasInitialized=initialized.GetValue(null);
|
||||
try
|
||||
{
|
||||
initialized.SetValue(null,true);
|
||||
var behavior=shell.AddComponent<FishingBehaviorB>();
|
||||
var action=BeginFishing(behavior);
|
||||
typeof(FishingBehaviorB).GetField("kvFishingAction",System.Reflection.BindingFlags.Instance|System.Reflection.BindingFlags.NonPublic).SetValue(behavior,action);
|
||||
float value=0; tween=DOTween.To(()=>value,v=>value=v,1,100).SetAutoKill(true).SetUpdate(UpdateType.Manual);
|
||||
typeof(FishingBehaviorB).GetMethod("TrackKVCastTween",System.Reflection.BindingFlags.Instance|System.Reflection.BindingFlags.NonPublic).Invoke(behavior,new object[]{tween});
|
||||
Check(tween.onKill!=null && tween.onComplete!=null,"actual callbacks installed");
|
||||
if(complete) tween.Complete(); else tween.Kill(false);
|
||||
Check(action.Cancelled!=complete && !action.Continued,"actual Tween Kill boundary complete="+complete+" cancelled="+action.Cancelled+" enabled="+Enabled+" active="+tween.IsActive());
|
||||
Check(fixture.Faults==(complete?0:1) && fixture.Sets==0,"autoKill is not a cancellation");
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { tween?.Kill(false); UnityEngine.Object.DestroyImmediate(shell); }
|
||||
finally { initialized.SetValue(null,wasInitialized); }
|
||||
}
|
||||
}
|
||||
|
||||
await Start("completed-action-normal-disable-is-not-failure");
|
||||
var completedShell=new GameObject("KV completed disable fixture");completedShell.SetActive(false);completedShell.hideFlags=HideFlags.HideAndDontSave;
|
||||
try
|
||||
{
|
||||
var behavior=completedShell.AddComponent<FishingBehaviorB>();
|
||||
var action=BeginFishing(behavior);Check(ClaimFishingConsumption(action),"completed action admitted");
|
||||
var batch=BeginFishingWrites(action);var primary=Write("NewPlayerDataDic","completed",true);batch.Dispose();
|
||||
await ConfirmFishingAsync(action,batch,primary);ContinueFishing(action,()=>{});
|
||||
typeof(FishingBehaviorB).GetField("kvFishingAction",System.Reflection.BindingFlags.Instance|System.Reflection.BindingFlags.NonPublic).SetValue(behavior,action);
|
||||
typeof(FishingBehaviorB).GetMethod("OnDisable",System.Reflection.BindingFlags.Instance|System.Reflection.BindingFlags.NonPublic).Invoke(behavior,null);
|
||||
Check(action.Cancelled && action.Continued && Ready && fixture.Faults==0,"normal disable after completed consumption must not Fault");
|
||||
Check(!FishingCurrent(action) && BeginFishing(behavior)==null,"normal disable still revokes callbacks and never restores quota");
|
||||
}
|
||||
finally { UnityEngine.Object.DestroyImmediate(completedShell); }
|
||||
|
||||
await Start("observation-timeout-does-not-replay"); b=Batch(); fixture.HoldSet=true;
|
||||
pending=ConfirmFishingAsync(b.action,b.batch,b.primary);
|
||||
await Reject(pending,"30 second observer deadline stops continuation"); pending=null;
|
||||
Check(fixture.Sets==1 && !b.action.Continued,"timeout leaves one admitted write");
|
||||
fixture.ReleaseSet.TrySetResult(true);
|
||||
|
||||
await Start("equipment-permit-cannot-cast"); Permit.Version=1; Permit.Operation=null;
|
||||
bool denied=false; try { BeginFishing(new object()); } catch(InvalidOperationException) { denied=true; }
|
||||
Check(denied && fixture.Sets==0,"old permit cannot consume");
|
||||
|
||||
foreach(var scenario in new[]{"above-limit","cross-limit","below-limit","recovery-during-pending"})
|
||||
{
|
||||
await Start("actual-PlayerData-"+scenario);
|
||||
int initialEnergy=scenario=="above-limit"?400:scenario=="cross-limit"?300:250;
|
||||
var tables=new cfg.Tables(n=>SimpleJSON.JSON.Parse(File.ReadAllText(Path.GetFullPath(Path.Combine(Application.dataPath,"../../cfgData",n+".json")))));
|
||||
container=new Container(); FishingContextAccess.Use(container); FishingContextAccess.Events=new EventAggregator();
|
||||
container.RegisterInstance(tables);
|
||||
LocalizationMgr.enumLanguage=tables.TbLanguageConfig.DataList.Select(x=>x.ID).ToList();
|
||||
textTable.SetValue(null,null);
|
||||
player=new PlayerData(tables,null); var small=new SmallGameData(tables,null,null); var fish=new PlayerFishData(tables,null);
|
||||
var achievements=new AchievementDataManager(tables); var box=new FishingBoxData(); var events=new FishingEventData(tables);
|
||||
container.RegisterInstance(player); container.RegisterInstance(small); container.RegisterInstance(fish);
|
||||
container.RegisterInstance(achievements); container.RegisterInstance(box); container.RegisterInstance(events);
|
||||
container.RegisterInstance<ILocalNotificationService>(new FishingNotifications());
|
||||
fish.ResetForKVLoad(); small.ResetForKVLoad(); achievements.ResetForKVLoad(); box.ResetForKVLoad();
|
||||
achievements.Init();
|
||||
int rod=tables.TbRodData.DataMap.Keys.First(),map=tables.TbMapData.DataMap.Keys.First();
|
||||
var core=new PlayerKVCoreState(new object[]{1,10UL,0,initialEnergy,5,0,rod,new DateTime(2026,1,1),0,7,10UL,2,map,map,0,0,0,0,0});
|
||||
player.ReplaceFromKVDecoded(new PlayerKVPlayerState(core,new Dictionary<string,string>(),new Dictionary<int,int>{{-1,1}},new List<int>(),0,false,"0"));
|
||||
// Fixed backing property only, no caller-controlled reflection or gameplay registration.
|
||||
typeof(PlayerData).GetProperty("benefitsEnergyLimit").SetValue(player,300);
|
||||
typeof(PlayerData).GetProperty("benefitsEnergyRecover").SetValue(player,1);
|
||||
if(scenario=="above-limit")
|
||||
{
|
||||
Permit.AllowedKeys=Permit.AllowedKeys.Concat(new[]{"BattlePassRecordTask","MiniBPKeyNew","EventLuckGameRecord","EventLuckMagicPlayfabData"}).ToArray();
|
||||
subscribers=new FishingSubscribers();
|
||||
subscribers.Bind(tables,events,container);
|
||||
Check(!tables.TbFishingEvent.DataList.Any(e=>e.ConditionList.Any(c=>c.Condition==cfg.ConditionType.ConsumeEnergy)) &&
|
||||
!tables.TbTriggerPackManager.DataList.Any(t=>t.Condition==cfg.ConditionType.ConsumeEnergy),"real config excludes async activation and trigger-pack writes");
|
||||
}
|
||||
int consumed=0,notified=0;
|
||||
subscription=GContext.OnEvent<ConditionTypeEvent>().Subscribe(e=>{if(e.type==cfg.ConditionType.ConsumeEnergy){consumed+=e.count; Write("k","task-"+e.count,true);}});
|
||||
using(GContext.OnEvent<PlayerData>().Subscribe(_=>notified++))
|
||||
{
|
||||
var action=BeginFishing(new object()); Check(ClaimFishingConsumption(action),"real action claimed");
|
||||
fixture.HoldSet=scenario=="recovery-during-pending";
|
||||
var consumption=player.ConsumeEnergyForKVLoopAsync(3,action); pending=consumption;
|
||||
if(fixture.HoldSet)
|
||||
{
|
||||
await fixture.SetEntered.Task;
|
||||
Check(!action.Confirmed && !action.Continued,"real player pending cannot continue");
|
||||
// Drive the actual due recovery tick while the original Set is still held.
|
||||
// No replacement serialization, no paused production recovery algorithm.
|
||||
player.CoRecoveryEnergy(tables.TbGlobalConfig.InitEnergyRestorationSpeed*60);
|
||||
Check((int)player.Energy==initialEnergy-2 && !action.Confirmed,"real recovery can write during pending");
|
||||
fixture.ReleaseSet.TrySetResult(true);
|
||||
}
|
||||
await consumption; pending=null;
|
||||
Check((int)player.Energy==initialEnergy-(fixture.HoldSet?2:3) && player.GetEnergyRM()==10 && consumed==3,"one real deduction and event");
|
||||
Check(notified==(scenario=="cross-limit"?2:fixture.HoldSet?3:1),"original notification count preserved");
|
||||
var modern=JsonConvert.DeserializeObject<Dictionary<string,string>>(fixture.Remote["NewPlayerDataDic"]);
|
||||
Check(modern["newData"].Split(',')[3]==(initialEnergy-3).ToString() && !modern.ContainsKey("energy"),"one canonical representation");
|
||||
Check(fixture.Remote["k"]=="task-3" && action.Confirmed,"synchronous task write confirmed");
|
||||
Check(achievements.GetAchievementDatas(cfg.ConditionType.ConsumeEnergy)==3 && modern["ad"].Contains(":") && modern["ad"]==achievements.AchievementDataToJson(),"actual achievement subscriber captured after event");
|
||||
var savedTime=DateTime.Parse(modern["newData"].Split(',')[7]);
|
||||
Check(scenario=="cross-limit"?savedTime>new DateTime(2026,1,1):savedTime==new DateTime(2026,1,1),"recovery timestamp crossing behavior preserved");
|
||||
if(fixture.HoldSet)
|
||||
{
|
||||
await KVMgr.FlushKVAsync(action.Owner);
|
||||
var recovered=JsonConvert.DeserializeObject<Dictionary<string,string>>(fixture.Remote["NewPlayerDataDic"]);
|
||||
Check(recovered["newData"].Split(',')[3]==(initialEnergy-2).ToString(),"later recovery has separate flush, not original action confirmation");
|
||||
}
|
||||
if(subscribers!=null)
|
||||
{
|
||||
Check(subscribers.Battle.Data.BattleTask.dic_dailyTasks.Values.Single()==3 && subscribers.Lucky.DicTasks.Values.Single()==3 && subscribers.Magic.DicTasks.Values.Single()==3,"actual task subscriptions each receive one consume event");
|
||||
Check(subscribers.Mini.Data.Exp==3,"actual mini BP subscription receives the event");
|
||||
Check(new[]{"BattlePassRecordTask","MiniBPKeyNew","EventLuckGameRecord","EventLuckMagicPlayfabData"}.All(fixture.Remote.ContainsKey),"all actual subscriber save methods confirmed in original action");
|
||||
}
|
||||
var parsed=new PlayerKVSchemaSnapshot("fixed-fishing",tables.TbRodData.DataMap.Keys,tables.TbMapData.DataMap.Keys,
|
||||
new Dictionary<string,int>(),System.Globalization.CultureInfo.CurrentCulture,new DateTime(2026,9,24,0,0,0,DateTimeKind.Utc),0);
|
||||
var errors=new PlayerKVSchemaDiagnostics();
|
||||
Check(PlayerKVSaveSchema.DecodeCore(new Dictionary<string,string>(),modern,parsed,errors)!=null && errors.TotalErrors==0,"actual serialized core loads without alias conflict");
|
||||
}
|
||||
}
|
||||
result=JsonConvert.SerializeObject(new{success=true,groups,assertions=checks,passed,formalRequests=0,synthetic=true,restored=true,folder});
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
subscribers?.Dispose(); subscribers=null;
|
||||
player?.StopRecoveryEnergy(); subscription?.Dispose();
|
||||
if(fixture!=null) await fixture.Close();
|
||||
if(pending!=null) { try { await pending; } catch(Exception e) { Debug.LogWarning("Observed fishing test failure: "+e.GetType().Name); } }
|
||||
FishingContextAccess.Use(oldContainer); FishingContextAccess.Events=oldEvents; container?.Dispose();
|
||||
LocalizationMgr.enumLanguage=oldLanguages; textTable.SetValue(null,oldTextTable);
|
||||
(Enabled,Ready,Error,Permit,RunPath,handle,onFault,onClose,collecting,mainThread,failed,mainContext,fishingAction)=saved;
|
||||
}
|
||||
catch { validationCleanupFailure=fixture; throw; }
|
||||
finally { Interlocked.Exchange(ref validationRunning,0); }
|
||||
}
|
||||
File.WriteAllText(Path.Combine(folder,"result.json"),result); return result;
|
||||
}
|
||||
// Fixed real subscriber fixture. Reflection targets are compiled constants, never supplied by CLI.
|
||||
sealed class FishingSubscribers:IDisposable
|
||||
{
|
||||
const System.Reflection.BindingFlags Fields=System.Reflection.BindingFlags.Instance|System.Reflection.BindingFlags.NonPublic;
|
||||
static readonly System.Reflection.FieldInfo PF=typeof(PlayFabMgr).GetField("_instance",System.Reflection.BindingFlags.Static|System.Reflection.BindingFlags.NonPublic);
|
||||
static readonly System.Reflection.FieldInfo Red=typeof(RedPointManager).GetField("instance",System.Reflection.BindingFlags.Static|System.Reflection.BindingFlags.NonPublic);
|
||||
readonly object oldRed=Red.GetValue(null),oldPF=PF.GetValue(null);
|
||||
GameObject shell; LuckyTaskData magicTask; MiniBattlePassDataProvider provider;
|
||||
internal BattlePassDataProvider Battle; internal EventLuckyGameModel Lucky;
|
||||
internal EventLuckMagicModel Magic; internal MiniBattlePassDataModel Mini;
|
||||
internal void Bind(cfg.Tables tables,FishingEventData events,Container container)
|
||||
{
|
||||
Red.SetValue(null,new RedPointManager());
|
||||
shell=new GameObject("KV inactive subscriber storage fixture");shell.SetActive(false);shell.hideFlags=HideFlags.HideAndDontSave;
|
||||
var pf=shell.AddComponent<PlayFabMgr>();
|
||||
if(!pf.IsUnstartedForKVLoad)throw new InvalidOperationException("No PF lifecycle may start in this fixture.");
|
||||
PF.SetValue(null,pf);
|
||||
var items=new PlayerItemData(tables);container.RegisterInstance(items);
|
||||
var data=new BattlePassData(tables);
|
||||
data.BattlePassRecord.ID=tables.TbFishingEvent.DataList.First(e=>tables.TbBattlePassMain.GetOrDefault(e.RedirectID)!=null).ID;
|
||||
data.BattlePassRecord.Level=1;
|
||||
data.BattleTask.dic_dailyTasks.Add(tables.TbBattlePassTask.DataList.First(t=>t.Type==cfg.ConditionType.ConsumeEnergy).TaskID,0);
|
||||
Battle=new BattlePassDataProvider(tables,data,items);container.RegisterInstance(Battle);
|
||||
var common=tables.TbCommonTask.DataList.First(t=>t.TaskType==cfg.ConditionType.ConsumeEnergy).Id;
|
||||
Lucky=new EventLuckyGameModel(events,tables);Lucky.DicTasks.Add(common,0);
|
||||
Magic=new EventLuckMagicModel{DicTasks=new Dictionary<int,int>{{common,0}},HigherRingData=EventLuckMagicHigherRingData.CreateEmpty()};
|
||||
var rings=(List<EventLuckMagicRing>)typeof(EventLuckMagicModel).GetField("_rings",Fields).GetValue(Magic);
|
||||
for(int i=1;i<=3;i++)rings.Add(EventLuckMagicRing.CreateEmpty(1,0,(EEventLuckMagicRingType)i));
|
||||
magicTask=new LuckyTaskData(Magic);typeof(EventLuckMagicModel).GetField("_taskData",Fields).SetValue(Magic,magicTask);
|
||||
provider=new MiniBattlePassDataProvider(tables,items);
|
||||
var table=tables.TbMiniBattlePass.DataList.First(t=>t.TaskType==cfg.ConditionType.ConsumeEnergy && t.Param==0);
|
||||
var activity=tables.TbFishingEvent.DataList.First(t=>t.Type==9 && t.RedirectID==table.Id);
|
||||
var type=(MiniBattlePassType)table.Type;
|
||||
Mini=provider.GetModel(type);
|
||||
provider.GetMiniBattlePassRecord("BP_"+type).Id=activity.ID;
|
||||
typeof(MiniBattlePassDataModel).GetField("fishingEvent",Fields).SetValue(Mini,activity);
|
||||
typeof(MiniBattlePassDataModel).GetMethod("InitData",Fields).Invoke(Mini,new object[]{table.Id});
|
||||
// Synthetic fixture has an active window; real-run time is never modified.
|
||||
Mini.startTime=ZZTimeHelper.UtcNow().AddDays(-1);
|
||||
typeof(MiniBattlePassDataModel).GetProperty("endTime").SetValue(Mini,ZZTimeHelper.UtcNow().AddDays(1));
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
Battle?.Dispose();Lucky?.Dispose();magicTask?.Dispose();
|
||||
if(Mini!=null)(typeof(MiniBattlePassDataModel).GetField("disposables",Fields).GetValue(Mini) as IDisposable)?.Dispose();
|
||||
PF.SetValue(null,oldPF);Red.SetValue(null,oldRed);
|
||||
if(shell!=null)UnityEngine.Object.DestroyImmediate(shell);
|
||||
}
|
||||
}
|
||||
sealed class FishingContextAccess:GContext
|
||||
{
|
||||
protected override Task SetupAsync()=>throw new InvalidOperationException("Test-only context.");
|
||||
internal static void Use(Container value) { container=value; }
|
||||
internal static IEventAggregator Events { get=>eventAggregator; set=>eventAggregator=value; }
|
||||
}
|
||||
sealed class FishingNotifications:ILocalNotificationService
|
||||
{
|
||||
public Task Init()=>Task.CompletedTask; public bool IsNoticeOpen=>false;
|
||||
public void OpenSettings(){} public void ScheduleOneTimeNotification(int id,DateTime time,string title,string text){}
|
||||
public void ScheduleDailyNotification(int id,DateTime time,string title,string text){}
|
||||
public void ClearAllNotification(){} public void ClearNotification(int id){} public void ScheduleDefaultNotification(){}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,123 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using game.PlayerDataStorage;
|
||||
using UnityEngine;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public partial class PlayFabUserService
|
||||
{
|
||||
readonly bool kvControlledInstance;
|
||||
PlayerKVGameLoadCoordinator kvCoordinator;
|
||||
Action<IDictionary<string,string>> kvReceiver;
|
||||
int kvReceiverUsers;
|
||||
internal PlayerKVGameLoadError KVLastError { get; private set; }
|
||||
internal string CaptureSaveBackend() => config==null ? "playfab" : config.Get<string>("SaveBackend","playfab");
|
||||
internal bool RejectSaveBackend(string backend)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled && backend=="kv-loop" && !kvControlledInstance)
|
||||
{ KVLastError=PlayerKVGameLoadError.None; return false; }
|
||||
#endif
|
||||
KVLastError=backend=="kv" ? PlayerKVGameLoadError.BackendNotEnabled : backend!="playfab" ? PlayerKVGameLoadError.BackendUnsupported :
|
||||
kvControlledInstance ? PlayerKVGameLoadError.Input : PlayerKVGameLoadError.None;
|
||||
return KVLastError!=PlayerKVGameLoadError.None;
|
||||
}
|
||||
internal void ConfigureKVForValidation(PlayerKVGameLoadTransaction transaction,PlayFabMgr pf,CustomServerMgr server,Func<bool> unstarted)
|
||||
{
|
||||
if(!kvControlledInstance || kvCoordinator!=null || pf==null || !pf.IsUnstartedForKVLoad || PlayFabMgr.HasLiveInstanceForKVLoad ||
|
||||
server==null || !server.IsFreshForKVLoad || unstarted==null || !unstarted()) throw new InvalidOperationException("Fresh controlled services required.");
|
||||
kvCoordinator=new PlayerKVGameLoadCoordinator(transaction,()=>pf.IsUnstartedForKVLoad && InternalId==0 && unstarted(),
|
||||
NotifyKVLoaded,()=>KVIntents?.Count ?? 0,server.BindKVForValidation,server.ClearKVForValidation);
|
||||
server.KVUnauthorized+=version=> { if(version==server.SessionVersion) { InternalId=0; _=kvCoordinator.Revoke(); } };
|
||||
}
|
||||
internal virtual async Task<PlayerKVGameLoadResult> LoadKVForValidationAsync(PlayerKVGameLoadCapability capability)
|
||||
{
|
||||
if(!kvControlledInstance || kvCoordinator==null || capability==null)
|
||||
return new PlayerKVGameLoadResult(PlayerKVGameLoadError.Input,0);
|
||||
try
|
||||
{
|
||||
int loginMs=config==null ? 10000 : PlayerKVSaveSchema.Int32(config.Get<string>("KVLoginCloseObserveMs","10000"));
|
||||
int quitMs=config==null ? 5000 : PlayerKVSaveSchema.Int32(config.Get<string>("KVQuitCloseObserveMs","5000"));
|
||||
if(loginMs!=capability.LoginObserveMs || quitMs!=capability.QuitObserveMs || loginMs<1000 || loginMs>15000 || quitMs<1000 || quitMs>15000)
|
||||
return new PlayerKVGameLoadResult(PlayerKVGameLoadError.Input,0);
|
||||
}
|
||||
catch { return new PlayerKVGameLoadResult(PlayerKVGameLoadError.Input,0); }
|
||||
var requestedId=CustomId; var generation=logoutGeneration;
|
||||
var result=await kvCoordinator.LoadAsync(requestedId,capability,LoginLifetime,()=>requestedId==CustomId && generation==logoutGeneration);
|
||||
if(generation!=logoutGeneration || requestedId!=CustomId)
|
||||
{
|
||||
_=kvCoordinator.Revoke();
|
||||
if(result.State==PlayerKVGameLoadState.LoadedForValidation) result=new PlayerKVGameLoadResult(PlayerKVGameLoadError.Stale,result.Epoch);
|
||||
}
|
||||
KVLastError=result.Error; return result;
|
||||
}
|
||||
internal Task CloseKVForValidationAsync() => kvCoordinator?.Revoke() ?? Task.CompletedTask;
|
||||
internal Task<PlayerKVShutdownStatus> ObserveKVQuitAsync() => kvCoordinator?.ObserveQuitAsync() ?? Task.FromResult(PlayerKVShutdownStatus.Ready);
|
||||
internal bool KVLoadedForValidation => kvCoordinator?.LoadedForValidation ?? false;
|
||||
internal int KVReceiverCountForValidation => kvReceiverUsers;
|
||||
internal void EnterKVReceiver(Action<IDictionary<string,string>> receiver)
|
||||
{
|
||||
if(!kvControlledInstance || receiver==null || (kvReceiver!=null && kvReceiver!=receiver)) throw new InvalidOperationException("Controlled receiver required.");
|
||||
if(kvReceiverUsers++==0) { kvReceiver=receiver; OnInitData+=receiver; }
|
||||
}
|
||||
internal void ExitKVReceiver()
|
||||
{ if(--kvReceiverUsers==0) { OnInitData-=kvReceiver; kvReceiver=null; } }
|
||||
void NotifyKVLoaded()
|
||||
{
|
||||
var listeners=OnInitData?.GetInvocationList();
|
||||
if(listeners!=null && (listeners.Length!=1 || kvReceiver==null || !listeners[0].Equals(kvReceiver)))
|
||||
throw new InvalidOperationException("Unregistered controlled receiver.");
|
||||
OnInitData?.Invoke(KVLoadingCopy.ToDictionary(p=>p.Key,p=>p.Value,StringComparer.Ordinal));
|
||||
}
|
||||
internal int KVTapJoyBalance { get; private set; }
|
||||
internal PlayerKVInitializationIntents KVIntents { get; private set; }
|
||||
internal IReadOnlyDictionary<string,string> KVLoadingCopy { get; private set; }
|
||||
internal int MMTestForKVValidation => MMTestValue;
|
||||
internal bool IsControlledKVInstance => kvControlledInstance;
|
||||
|
||||
internal virtual void ResetForKVLoad()
|
||||
{
|
||||
bool admitted=kvControlledInstance, sessionAllowed=apiSession==null;
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
admitted=admitted || KVLoopLoading;
|
||||
sessionAllowed=sessionAllowed || KVLoopLoading;
|
||||
#endif
|
||||
if(!admitted || InternalId!=0 || !sessionAllowed)
|
||||
throw new InvalidOperationException("Unstarted controlled identity required.");
|
||||
KVIntents?.Discard(); KVIntents=null; KVLoadingCopy=null;
|
||||
MMTestValue=0; EAccoutUrl=new Dictionary<string,string>(StringComparer.Ordinal); KVTapJoyBalance=0;
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(PlayerKVLoginState state,long epoch,IReadOnlyDictionary<string,string> raw)
|
||||
{
|
||||
bool admitted=kvControlledInstance;
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
admitted=admitted || KVLoopLoading;
|
||||
#endif
|
||||
if(!admitted || state==null || raw==null) throw new InvalidOperationException("Controlled login state required.");
|
||||
var copy=raw.ToDictionary(p=>p.Key,p=>p.Value,StringComparer.Ordinal);
|
||||
var intents=new PlayerKVInitializationIntents(epoch,state.Channel);
|
||||
foreach(var pair in state.Normalizations) { copy[pair.Key]=pair.Value; intents.Add(epoch,pair.Key,pair.Value); }
|
||||
KVIntents?.Discard(); KVIntents=intents;
|
||||
KVLoadingCopy=new System.Collections.ObjectModel.ReadOnlyDictionary<string,string>(copy);
|
||||
MMTestValue=state.MMTest; KVTapJoyBalance=state.TapJoyBalance;
|
||||
EAccoutUrl=state.AccountUrls.ToDictionary(p=>p.Key,p=>p.Value,StringComparer.Ordinal);
|
||||
ReplaceKVLinked(state);
|
||||
}
|
||||
void IKVUserLoadReceiver.ResetForKVLoad() => ResetForKVLoad();
|
||||
void IKVUserLoadReceiver.ReplaceFromKVDecoded(PlayerKVLoginState state,long epoch,IReadOnlyDictionary<string,string> raw)
|
||||
=> ReplaceFromKVDecoded(state,epoch,raw);
|
||||
void IKVUserLoadReceiver.DiscardKVIntents() => KVIntents?.Discard();
|
||||
bool IKVUserLifecycle.KVHandoffReady => false;
|
||||
PlayerKVGameLoadError IKVUserLifecycle.KVLastError => KVLastError;
|
||||
Task IKVUserLifecycle.CloseKVForValidationAsync() => CloseKVForValidationAsync();
|
||||
Task<PlayerKVShutdownStatus> IKVUserLifecycle.ObserveKVQuitAsync() => ObserveKVQuitAsync();
|
||||
internal virtual void ReplaceKVLinked(PlayerKVLoginState state) { }
|
||||
}
|
||||
}
|
||||
@@ -1,914 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using game.Account;
|
||||
using asap.core;
|
||||
using PlayFab;
|
||||
using asap.playfab.async;
|
||||
using PlayFab.ClientModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
#if UNITY_ANDROID
|
||||
using TapjoyUnity.Internal;
|
||||
#endif
|
||||
|
||||
using tysdk;
|
||||
using UniRx;
|
||||
using YiDun.Protector;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public enum ELoginStep
|
||||
{
|
||||
PlayFab,
|
||||
Sdk,
|
||||
Api,
|
||||
Done
|
||||
}
|
||||
|
||||
public class LoginRequest
|
||||
{
|
||||
public string playerId { get; set; }
|
||||
public PlayerProfile playerProfile { get; set; }
|
||||
}
|
||||
|
||||
public class PlayerProfile
|
||||
{
|
||||
public int Level { get; set; }
|
||||
public string Avatar { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
}
|
||||
|
||||
public class EvtAPISvrLoginSuccess
|
||||
{
|
||||
public string playerId { get; set; }
|
||||
public uint internalId { get; set; }
|
||||
}
|
||||
|
||||
public class EvtAPISvrUnauthorized { public int SessionVersion { get; set; } }
|
||||
|
||||
public interface IUserService
|
||||
{
|
||||
Task<ELoginStep> Login(Action<int> progress, bool tyrLoginByToken = true);
|
||||
void Logout();
|
||||
System.Threading.CancellationToken LoginLifetime { get; }
|
||||
bool IsLogin { get; }
|
||||
bool IsAuthorized { get; }
|
||||
DateTime LastLoginTime { get; }
|
||||
DateTime LoginTime { get; }
|
||||
DateTime CreateTime { get; }
|
||||
Task<string> ChangeDisplayName(string newName);
|
||||
string DisplayName { get; }
|
||||
string AvatarUrl { get; }
|
||||
Dictionary<string, string> EAccoutUrl { get; }
|
||||
Task<string> UpdateAvatarUrl(string url);
|
||||
string UserId { get; }
|
||||
string ApiPlayerId { get; }
|
||||
string CustomId { get; set; }
|
||||
uint InternalId { get; }
|
||||
string ContinentCode { get; }
|
||||
//string CountryCode { get; }
|
||||
double CreateTotalSeconds();
|
||||
string Language { get; }
|
||||
Task<string> UpdateLanguage(string code);
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Link sns account
|
||||
/// success return ( code 0, current userid)
|
||||
/// exists return ( code -1, exists account user id)
|
||||
/// fail return ( code 1, 0 )
|
||||
/// </summary>
|
||||
/// <param name="accoutType"> sns type [hwGoogle, hwFacebook, hwGuest, Apple]</param>
|
||||
/// <returns>code and userid</returns>
|
||||
Task<(int code, int userid)> LinkAccount(EAccoutType accoutType);
|
||||
Task<bool> GetLinkState(EAccoutType accoutType);
|
||||
void SetPlayerLv(string playFabId, int lv);
|
||||
Task<(string, int)> GetPlayLv(string playFabId);
|
||||
int GetPlayerLv(string playFabId);
|
||||
|
||||
PlayInfo GetPlayInfo(string id);
|
||||
void ClearPlayInfos();
|
||||
void SetPlayInfo(string playFabId, string name, string avatarUrl);
|
||||
string GetDefaultName(string id);
|
||||
void UpdateProfile();
|
||||
void UpdateProfileLv();
|
||||
}
|
||||
|
||||
public partial class PlayFabUserService : IUserService, PlayerDataStorage.IKVUserLoadReceiver, PlayerDataStorage.IKVUserLifecycle
|
||||
{
|
||||
[Inject]
|
||||
public IConfig config { get; set; }
|
||||
|
||||
[Inject]
|
||||
public ICustomServerMgr customServerMgr { get; set; }
|
||||
|
||||
public bool IsLogin => !kvControlledInstance && PlayFabClientAPI.IsClientLoggedIn() && IsAuthorized;
|
||||
|
||||
public bool IsAuthorized => InternalId != 0 && apiSession != null && apiSession.IsAuthenticated;
|
||||
|
||||
// UserId remains the legacy storage identity until the KV migration.
|
||||
public string ApiPlayerId => IsAuthorized ? apiSession.PlayerId : null;
|
||||
|
||||
public DateTime LastLoginTime { get; protected set; }
|
||||
|
||||
public DateTime LoginTime { get; protected set; }
|
||||
|
||||
public DateTime CreateTime { get; protected set; }
|
||||
|
||||
public string DisplayName { get; protected set; }
|
||||
|
||||
public string AvatarUrl { get; protected set; }
|
||||
public Dictionary<string, string> EAccoutUrl { get; protected set; }
|
||||
public string Language { get; protected set; }
|
||||
|
||||
public string UserId { get; protected set; }
|
||||
public EntityKey entityKey { get; protected set; }
|
||||
public string CustomId { get; set; }
|
||||
public uint InternalId { get; protected set; }
|
||||
public string ContinentCode { get; protected set; }
|
||||
public string CountryCode { get; protected set; }
|
||||
protected virtual string NewPlayerName => $"Angler#{CustomId}";
|
||||
|
||||
protected Action<IDictionary<string, string>> OnInitData;
|
||||
|
||||
protected virtual string NewPlayerAvatar => string.Empty;
|
||||
private bool IsApiServerLogin => InternalId != 0;
|
||||
int MMTestValue;//0=>A 原来的 对照组 1=>B 新的实验组
|
||||
const string MMTestKey = "MMTest";
|
||||
const string MMTestSubKey = "MMFailParam";
|
||||
public string GetDefaultName(string id)
|
||||
{
|
||||
return "Angler#" + (string.IsNullOrEmpty(id) ? string.Empty : id.Substring(0, Math.Min(5, id.Length)));
|
||||
}
|
||||
|
||||
public PlayFabUserService() : this(null) { }
|
||||
|
||||
internal PlayFabUserService(PlayerDataStorage.PlayerKVControlledLoadCapability capability)
|
||||
{
|
||||
kvControlledInstance = capability != null;
|
||||
if (kvControlledInstance) return;
|
||||
GContext.OnEvent<EvtAPISvrUnauthorized>().Subscribe(evt =>
|
||||
{
|
||||
if (evt.SessionVersion != customServerMgr.SessionVersion) return;
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) { game.PlayerDataStorage.PlayerKVGameplayLoop.Fault("API unauthorized; restart required."); return; }
|
||||
#endif
|
||||
InternalId = 0;
|
||||
if (PlayFabClientAPI.IsClientLoggedIn()) _ = LoginApiServer();
|
||||
});
|
||||
}
|
||||
|
||||
protected int logoutGeneration;
|
||||
private System.Threading.CancellationTokenSource loginLifetime = new System.Threading.CancellationTokenSource();
|
||||
public System.Threading.CancellationToken LoginLifetime => loginLifetime.Token;
|
||||
private readonly System.Threading.SemaphoreSlim loginGate = new System.Threading.SemaphoreSlim(1, 1);
|
||||
public virtual Task<ELoginStep> Login(Action<int> progress, bool tyrLoginByToken = true) => LoginWithBackend(progress,CaptureSaveBackend());
|
||||
|
||||
protected async Task<ELoginStep> LoginWithBackend(Action<int> progress,string backend)
|
||||
{
|
||||
var generation = logoutGeneration;
|
||||
var requestedId = CustomId;
|
||||
if(RejectSaveBackend(backend)) return ELoginStep.Api;
|
||||
await loginGate.WaitAsync();
|
||||
try
|
||||
{
|
||||
if (generation != logoutGeneration || requestedId != CustomId) return ELoginStep.Api;
|
||||
if (!long.TryParse(requestedId, System.Globalization.NumberStyles.None,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out var numericId) || numericId <= 0)
|
||||
{
|
||||
return ELoginStep.Api;
|
||||
}
|
||||
return await LoginCore(progress, generation);
|
||||
}
|
||||
finally { loginGate.Release(); }
|
||||
}
|
||||
|
||||
private async Task<ELoginStep> LoginCore(Action<int> progress, int generation)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) return await LoginKVGameplayLoopAsync(progress,generation);
|
||||
#endif
|
||||
if (IsLogin)
|
||||
{
|
||||
progress?.Invoke(2);
|
||||
return ELoginStep.Done;
|
||||
}
|
||||
|
||||
if (!PlayFabClientAPI.IsClientLoggedIn())
|
||||
{
|
||||
if (!await LoginPlayFab(generation))
|
||||
return ELoginStep.PlayFab;
|
||||
}
|
||||
|
||||
if (generation != logoutGeneration) return ELoginStep.Api;
|
||||
|
||||
SetMMTestValue();
|
||||
|
||||
GContext.container.Resolve<IChatService>().WaitingForLogin();
|
||||
|
||||
OnYinDunLogin();
|
||||
if (!await LoginApiServer() || generation != logoutGeneration)
|
||||
return ELoginStep.Api;
|
||||
|
||||
InitTapJoy();
|
||||
progress?.Invoke(2);
|
||||
return ELoginStep.Done;
|
||||
}
|
||||
|
||||
private static void InitTapJoy()
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
var lastBalanceStr = PlayFabMgr.Instance.GetLocalData("TapJoy_Diamond") ?? "";
|
||||
Debug.Log($"InitTapJoy -> {lastBalanceStr}");
|
||||
var lastBalance = int.TryParse(lastBalanceStr, out var temp) ? temp : 0;
|
||||
|
||||
var tapjoyObject = GameObject.FindObjectOfType<TapjoyComponent>();
|
||||
if (tapjoyObject && tapjoyObject.gameObject && !tapjoyObject.GetComponent<TapjoyHelper>())
|
||||
{
|
||||
var tapjoyHelper = tapjoyObject.gameObject.AddComponent<TapjoyHelper>();
|
||||
GameObject.DontDestroyOnLoad(tapjoyHelper);
|
||||
tapjoyHelper.Init(lastBalance, OnEarnCurrencyName, OnBalance);
|
||||
}
|
||||
return;
|
||||
void OnEarnCurrencyName(string currencyName, int amount)
|
||||
{
|
||||
Debug.Log($"OnEarnCurrencyName -> {currencyName} {amount}");
|
||||
// var amount = (balance - lastBalance);
|
||||
var diamonds = new ItemData { id = 1005, count = amount };
|
||||
diamonds.curCount = (ulong)GContext.container.Resolve<PlayerItemData>().GetItemCount(diamonds.id);
|
||||
GContext.container.Resolve<PlayerItemData>().AddItem(diamonds, null);
|
||||
GContext.Publish(new ShowData(new List<ItemData> { diamonds }, RewardType.NormalOne));
|
||||
GContext.Publish(new ShowData());
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("tapjoy"))
|
||||
{
|
||||
e.AddContent("item_id", diamonds.id)
|
||||
.AddContent("item_count", diamonds.count);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
void OnBalance(string currencyName, int balance)
|
||||
{
|
||||
Debug.Log($"OnBalance -> {currencyName} {balance}");
|
||||
// const string tapjoyKey = "TapJoy_Diamond";
|
||||
// var lastBalanceStr = PlayFabMgr.Instance.GetLocalData(tapjoyKey) ?? "";
|
||||
// var lastBalance = int.TryParse(lastBalanceStr, out var temp) ? temp : 0;
|
||||
{
|
||||
// PlayFabMgr.Instance.UpdateUserDataValue(tapjoyKey, balance.ToString());
|
||||
// if (balance > lastBalance)
|
||||
// {
|
||||
// var amount = (balance - lastBalance);
|
||||
// var diamonds = new ItemData { id = 1005, count = amount };
|
||||
// diamonds.curCount = (ulong)GContext.container.Resolve<PlayerItemData>().GetItemCount(diamonds.id);
|
||||
// GContext.container.Resolve<PlayerItemData>().AddItem(diamonds);
|
||||
// GContext.Publish(new ShowData(new List<ItemData> { diamonds }, RewardType.NormalOne));
|
||||
// // GContext.Publish(new ShowData(diamonds));
|
||||
// GContext.Publish(new ShowData());
|
||||
// }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
protected async void OnYinDunLogin()
|
||||
{
|
||||
// 开发模式下,先不启用了
|
||||
if (config != null && (config.Get("DEBUG", "0") == "1" || config.Get("GM", "0") == "1"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var userService = GContext.container.Resolve<IUserService>();
|
||||
var yiDunManager = GContext.container.Resolve<YiDunManager>();
|
||||
var roleName = userService.DisplayName;
|
||||
var roleId = userService.UserId;
|
||||
var roleAccount = userService.CustomId;
|
||||
var versionData = new
|
||||
{
|
||||
GameVersion = Application.version,
|
||||
AssetVersion = config.Get(GConstant.K_AssetVersion, string.Empty),
|
||||
#if UNITY_ANDROID
|
||||
Channel = GConstant.Yidun_GooglePlayChannel
|
||||
#elif UNITY_IOS
|
||||
Channel = GConstant.Yidun_IOSChannel
|
||||
#endif
|
||||
};
|
||||
Debug.Log($"JsonUtility.ToJson(versionData) => {JsonConvert.SerializeObject(versionData)}");
|
||||
|
||||
var businessId = GConstant.Yidun_AndroidBusinessId;
|
||||
#if UNITY_IOS
|
||||
businessId = GConstant.Yidun_IOSBusinessId;
|
||||
#endif
|
||||
|
||||
#if (UNITY_ANDROID || UNITY_IOS || UNITY_IPHONE) && !UNITY_EDITOR
|
||||
yiDunManager.SetRoleInfo(businessId, roleId, roleName, roleAccount, null, -1, JsonConvert.SerializeObject(versionData));
|
||||
yiDunManager.PostRiskCheck();
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetMMTestValue()
|
||||
{
|
||||
if (MMTestValue == -1)
|
||||
{
|
||||
//int id = (int)(InternalId / 2) * 2 + 1;
|
||||
//int AB = (int)(id % 8);
|
||||
// convert UserId string to ulong
|
||||
var ulongUserId = ulong.Parse(UserId, System.Globalization.NumberStyles.HexNumber);
|
||||
int AB = (int)(ulongUserId % 16);
|
||||
MMTestValue = AB;
|
||||
PlayFabMgr.Instance.UpdateUserDataValue(MMTestKey, $"{MMTestSubKey}#{MMTestValue}");
|
||||
}
|
||||
string ab = GContext.container.Resolve<MMTService>().Init(MMTestValue);
|
||||
GEvent.SetUserProp("MMTestValue", ab, true);
|
||||
Debug.Log($"UserId:{UserId} MMTestValue :0b{Convert.ToString(MMTestValue, 2)} MMTestValueString:{ab}");
|
||||
}
|
||||
public void GMMMT(int value)
|
||||
{
|
||||
MMTestValue = value;
|
||||
PlayFabMgr.Instance.UpdateUserDataValue(MMTestKey, $"{MMTestSubKey}#{MMTestValue}");
|
||||
string ab = GContext.container.Resolve<MMTService>().Init(MMTestValue);
|
||||
GEvent.SetUserProp("MMTestValue", ab, true);
|
||||
Debug.Log($"UserId:{UserId} MMTestValue :0b{Convert.ToString(MMTestValue, 2)} MMTestValueString:{ab}");
|
||||
}
|
||||
private async Task<bool> LoginPlayFab(int generation)
|
||||
{
|
||||
PlayFabSettings.TitleId = config.Get<string>(GConstant.K_PlayFab_Title, GConstant.V_Debug_PF_Title);
|
||||
PlayFab.Internal.PlayFabWebRequest.SkipCertificateValidation();
|
||||
|
||||
var timeout = config.Get<int>(GConstant.K_PlayFab_TimeOut, 0);
|
||||
if (timeout != 0)
|
||||
{
|
||||
PlayFabSettings.RequestTimeout = timeout;
|
||||
}
|
||||
|
||||
var sw = new System.Diagnostics.Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
var info_request_parameters = new GetPlayerCombinedInfoRequestParams
|
||||
{
|
||||
GetUserAccountInfo = true,
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
GetUserData = !game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled,
|
||||
#else
|
||||
GetUserData = true,
|
||||
#endif
|
||||
GetPlayerProfile = true,
|
||||
ProfileConstraints = new PlayerProfileViewConstraints()
|
||||
{
|
||||
ShowAvatarUrl = true,
|
||||
ShowDisplayName = true,
|
||||
ShowLocations = true,
|
||||
}
|
||||
};
|
||||
|
||||
LoginResult loginResult = null;
|
||||
|
||||
|
||||
if (CustomId != string.Empty)
|
||||
{
|
||||
var request = new LoginWithCustomIDRequest
|
||||
{
|
||||
CustomId = CustomId,
|
||||
CreateAccount = true,
|
||||
InfoRequestParameters = info_request_parameters,
|
||||
};
|
||||
Debug.Log($"[UserService::LoginPlayFab] LoginWithCustomIDAsync customId:{CustomId}");
|
||||
loginResult = await PlayFabClientAsyncAPI.ExecuteWithRetryAsync(
|
||||
() => PlayFabClientAsyncAPI.LoginWithCustomIDAsync(request));
|
||||
Debug.Log($"[UserService::LoginPlayFab] LoginWithCustomIDAsync result:{loginResult != null}");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
#if UNITY_IOS
|
||||
CustomId = UnityEngine.iOS.Device.advertisingIdentifier;
|
||||
var request = new LoginWithIOSDeviceIDRequest
|
||||
{
|
||||
DeviceId = UnityEngine.iOS.Device.advertisingIdentifier,
|
||||
OS = "iOS",
|
||||
DeviceModel = UnityEngine.iOS.Device.generation.ToString(),
|
||||
CreateAccount = true,
|
||||
InfoRequestParameters = info_request_parameters,
|
||||
};
|
||||
|
||||
loginResult = await PlayFabClientAsyncAPI.LoginWithIOSDeviceIDAsync(request);
|
||||
|
||||
#elif UNITY_ANDROID
|
||||
|
||||
CustomId = SystemInfo.deviceUniqueIdentifier;
|
||||
var request = new LoginWithAndroidDeviceIDRequest
|
||||
{
|
||||
AndroidDeviceId = SystemInfo.deviceUniqueIdentifier,
|
||||
AndroidDevice = SystemInfo.deviceModel,
|
||||
CreateAccount = true,
|
||||
InfoRequestParameters = info_request_parameters,
|
||||
};
|
||||
|
||||
loginResult = await PlayFabClientAsyncAPI.LoginWithAndroidDeviceIDAsync(request);
|
||||
|
||||
#else
|
||||
CustomId = Application.identifier;
|
||||
var request = new LoginWithCustomIDRequest
|
||||
{
|
||||
AndroidDeviceId = Application.identifier,
|
||||
CreateAccount = true,
|
||||
InfoRequestParameters = info_request_parameters,
|
||||
};
|
||||
loginResult = await PlayFabClientAsyncAPI.ExecuteWithRetryAsync(
|
||||
() => PlayFabClientAsyncAPI.LoginWithCustomIDAsync(request));
|
||||
#endif
|
||||
}
|
||||
sw.Stop();
|
||||
if (generation != logoutGeneration)
|
||||
{
|
||||
PlayFabClientAPI.ForgetAllCredentials();
|
||||
return false;
|
||||
}
|
||||
if (loginResult == null) return false;
|
||||
|
||||
Debug.Log($"LoginPlayFab elapsed={sw.Elapsed.TotalSeconds}");
|
||||
sw.Reset();
|
||||
|
||||
|
||||
entityKey = loginResult.EntityToken.Entity;
|
||||
if (!loginResult.NewlyCreated)
|
||||
{
|
||||
DisplayName = loginResult.InfoResultPayload.PlayerProfile?.DisplayName ?? string.Empty;
|
||||
AvatarUrl = loginResult.InfoResultPayload.PlayerProfile?.AvatarUrl ?? string.Empty;
|
||||
ContinentCode = loginResult.InfoResultPayload.PlayerProfile?.Locations[0]?.ContinentCode?.ToString() ?? "UNKNOW";
|
||||
CountryCode = loginResult.InfoResultPayload.PlayerProfile?.Locations[0]?.CountryCode?.ToString();
|
||||
|
||||
config.Set("NewlyCreated", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
sw.Start();
|
||||
|
||||
GetPlayerProfileRequest getPlayerProfileRequest = new GetPlayerProfileRequest()
|
||||
{
|
||||
ProfileConstraints = new PlayerProfileViewConstraints()
|
||||
{
|
||||
//ShowAvatarUrl = true,
|
||||
//ShowDisplayName = true,
|
||||
ShowLocations = true,
|
||||
}
|
||||
};
|
||||
var data = await PlayFabClientAsyncAPI.GetPlayerProfileAsync(getPlayerProfileRequest);
|
||||
if (generation != logoutGeneration) return false;
|
||||
//DisplayName = data.PlayerProfile.DisplayName;
|
||||
//AvatarUrl = data.PlayerProfile.AvatarUrl;
|
||||
ContinentCode = data?.PlayerProfile.Locations[0]?.ContinentCode?.ToString() ?? "UNKNOW";
|
||||
CountryCode = data?.PlayerProfile.Locations[0]?.CountryCode?.ToString();
|
||||
sw.Stop();
|
||||
Debug.Log($"LoginPlayFab GetPlayerProfile elapsed={sw.Elapsed.TotalSeconds}");
|
||||
sw.Reset();
|
||||
|
||||
config.Set("NewlyCreated", true);
|
||||
}
|
||||
|
||||
UserId = loginResult.PlayFabId;
|
||||
if (string.IsNullOrEmpty(DisplayName))
|
||||
{
|
||||
sw.Start();
|
||||
var newDisplayName = await ChangeDisplayName(NewPlayerName);
|
||||
if (generation != logoutGeneration) return false;
|
||||
DisplayName = newDisplayName;
|
||||
sw.Stop();
|
||||
Debug.Log($"LoginPlayFab ChangeDisplayName elapsed={sw.Elapsed.TotalSeconds}");
|
||||
sw.Reset();
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(AvatarUrl))
|
||||
{
|
||||
sw.Start();
|
||||
/*AvatarUrl = */
|
||||
await UpdateAvatarUrl(NewPlayerAvatar);
|
||||
if (generation != logoutGeneration) return false;
|
||||
sw.Stop();
|
||||
Debug.Log($"LoginPlayFab update avatar elapsed={sw.Elapsed.TotalSeconds}");
|
||||
sw.Reset();
|
||||
}
|
||||
//BuglyAgent.SetUserId(UserId);
|
||||
var setting = GContext.container.Resolve<ISettingService>();
|
||||
await UpdateLanguage(setting.Lang);
|
||||
if (generation != logoutGeneration) return false;
|
||||
|
||||
var playfabUserData = loginResult.InfoResultPayload?.UserData;
|
||||
|
||||
CreateTime = loginResult.InfoResultPayload.AccountInfo.Created.ToUniversalTime();
|
||||
var tokenExpiration = loginResult.EntityToken.TokenExpiration?.ToUniversalTime();
|
||||
if (tokenExpiration.HasValue)
|
||||
{
|
||||
LoginTime = tokenExpiration.Value.ToUniversalTime() - TimeSpan.FromDays(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LoginTime = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
if (loginResult.LastLoginTime.HasValue)
|
||||
LastLoginTime = loginResult.LastLoginTime.Value.ToUniversalTime();
|
||||
|
||||
ZZTimeHelper.AdjustTime(LoginTime);
|
||||
ZZTimeHelper.WathForcusChange();
|
||||
|
||||
//InitData should invoke after local time adjusted.
|
||||
//save login time to local storage.
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(!game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled)
|
||||
#endif
|
||||
InitData(playfabUserData, UserId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private S101AccountSession apiSession;
|
||||
private Task<bool> apiLoginTask;
|
||||
private int apiLoginGeneration;
|
||||
|
||||
private Task<bool> LoginApiServer()
|
||||
{
|
||||
if (apiLoginTask != null && !apiLoginTask.IsCompleted)
|
||||
return apiLoginTask;
|
||||
apiLoginTask = LoginApiServerCore();
|
||||
return apiLoginTask;
|
||||
}
|
||||
|
||||
private async Task<bool> LoginApiServerCore()
|
||||
{
|
||||
var generation = ++apiLoginGeneration;
|
||||
var requestedId = CustomId;
|
||||
InternalId = 0;
|
||||
apiSession?.Dispose();
|
||||
apiSession = null;
|
||||
try
|
||||
{
|
||||
customServerMgr.ClearAccountSession();
|
||||
var session = new S101AccountSession(customServerMgr.ApiBaseUri);
|
||||
apiSession = session;
|
||||
var result = await session.LoginAsync(requestedId);
|
||||
if (generation != apiLoginGeneration || requestedId != CustomId)
|
||||
return false;
|
||||
if (!result.Success)
|
||||
{
|
||||
Debug.LogWarning($"API login failed: {result.Error}, HTTP {result.StatusCode}");
|
||||
return false;
|
||||
}
|
||||
customServerMgr.UseAccountSession(session);
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) return true; // Identity is published only after all KV modules load.
|
||||
#endif
|
||||
InternalId = session.InternalId;
|
||||
GContext.Publish(new EvtAPISvrLoginSuccess
|
||||
{
|
||||
playerId = session.PlayerId,
|
||||
internalId = InternalId
|
||||
});
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (generation == apiLoginGeneration)
|
||||
{
|
||||
InternalId = 0;
|
||||
apiSession?.Dispose();
|
||||
apiSession = null;
|
||||
customServerMgr.ClearAccountSession();
|
||||
}
|
||||
Debug.LogError($"API login failed: {e.GetType().Name}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async virtual Task<(int code, int userid)> LinkAccount(EAccoutType accoutType)
|
||||
{
|
||||
await Awaiters.NextFrame;
|
||||
//测试
|
||||
//#if UNITY_EDITOR
|
||||
// var _accountInfo = new TYSdkFacade.AccountInfo()
|
||||
// {
|
||||
// channel = accoutType,
|
||||
// avatar = "https://tse2-mm.cn.bing.net/th/id/OIP-C.s4x0YU7qC-oat3mnyubG5gAAAA?rs=1&pid=ImgDetMain",
|
||||
// };
|
||||
// AvatarToPF(_accountInfo);
|
||||
|
||||
// if (_accountInfo.channel == EAccoutType.hwFacebook)
|
||||
// {
|
||||
// _ = UpdateAvatarUrl(_accountInfo.avatar);
|
||||
// }
|
||||
//#endif
|
||||
return (1,0);
|
||||
}
|
||||
|
||||
protected void AvatarToPF(TYSdkFacade.AccountInfo accountInfo)
|
||||
{
|
||||
if (accountInfo != null
|
||||
&& (accountInfo.channel == EAccoutType.hwFacebook
|
||||
|| accountInfo.channel == EAccoutType.hwGoogle)
|
||||
&& !string.IsNullOrEmpty(accountInfo.avatar))
|
||||
{
|
||||
if (EAccoutUrl == null)
|
||||
{
|
||||
EAccoutUrl = new Dictionary<string, string>();
|
||||
}
|
||||
EAccoutUrl[accountInfo.channel.ToString()] = accountInfo.avatar;
|
||||
string EAccoutUrlStr = Newtonsoft.Json.JsonConvert.SerializeObject(EAccoutUrl);
|
||||
PlayFabMgr.Instance.UpdateUserDataValue("EAccoutUrl", EAccoutUrlStr);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitData(Dictionary<string, UserDataRecord> datas, string userId)
|
||||
{
|
||||
var comparedData = PlayFabMgr.Instance.CompareDatas(datas, userId);
|
||||
if (comparedData != null)
|
||||
{
|
||||
MMTestValue = 0;
|
||||
if (!comparedData.ContainsKey(MMTestKey))
|
||||
{
|
||||
if (config.Get<bool>("NewlyCreated", false))
|
||||
{
|
||||
//新用户设置MMTest
|
||||
MMTestValue = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
//老用户设置MMTest
|
||||
PlayFabMgr.Instance.UpdateUserDataValue(MMTestKey, $"{MMTestSubKey}#{MMTestValue}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string mmTestValue = comparedData[MMTestKey];
|
||||
string[] mmTestValueData = mmTestValue.Split('#');
|
||||
//if (mmTestValueData.Length > 1 && mmTestValueData[0] == MMTestSubOldKey)
|
||||
//{
|
||||
// int.TryParse(mmTestValueData[1], out int value);
|
||||
// MMTestValue = value & 0b101;
|
||||
// //升级老用户数据
|
||||
// PlayFabMgr.Instance.UpdateUserDataValue(MMTestKey, $"{MMTestSubKey}#{MMTestValue}");
|
||||
//}
|
||||
//else
|
||||
if (mmTestValueData.Length > 1 && mmTestValueData[0] == MMTestSubKey)
|
||||
{
|
||||
|
||||
int.TryParse(mmTestValueData[1], out int value);
|
||||
MMTestValue = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
MMTestValue = 0;
|
||||
PlayFabMgr.Instance.UpdateUserDataValue(MMTestKey, $"{MMTestSubKey}#{MMTestValue}");
|
||||
}
|
||||
}
|
||||
GContext.container.Resolve<PlayerData>().LoadPlayerData(comparedData);
|
||||
OnInitData?.Invoke(comparedData);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Logout()
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) { _=StopKVGameplayLoopAsync(); return; }
|
||||
#endif
|
||||
if(kvControlledInstance)
|
||||
{
|
||||
logoutGeneration++; InternalId=0; UserId=null; DisplayName=null; AvatarUrl=null;
|
||||
_=kvCoordinator?.Revoke();
|
||||
var previous=loginLifetime; loginLifetime=new System.Threading.CancellationTokenSource();
|
||||
try { previous.Cancel(); } finally { previous.Dispose(); }
|
||||
return;
|
||||
}
|
||||
|
||||
logoutGeneration++;
|
||||
apiLoginGeneration++;
|
||||
apiLoginTask = null;
|
||||
apiSession?.Dispose();
|
||||
apiSession = null;
|
||||
UserId = null;
|
||||
InternalId = 0;
|
||||
DisplayName = null;
|
||||
AvatarUrl = null;
|
||||
PlayFabClientAPI.ForgetAllCredentials();
|
||||
customServerMgr.ClearAccountSession();
|
||||
var previousLifetime = loginLifetime;
|
||||
loginLifetime = new System.Threading.CancellationTokenSource();
|
||||
try { previousLifetime.Cancel(); }
|
||||
finally { previousLifetime.Dispose(); }
|
||||
}
|
||||
|
||||
public virtual async Task<string> ChangeDisplayName(string newName)
|
||||
{
|
||||
var generation = logoutGeneration;
|
||||
var originName = string.IsNullOrEmpty(DisplayName) ? NewPlayerName : DisplayName;
|
||||
var result = await PlayFabClientAsyncAPI.UpdateUserTitleDisplayNameAsync(new UpdateUserTitleDisplayNameRequest
|
||||
{
|
||||
DisplayName = newName
|
||||
});
|
||||
if (generation != logoutGeneration || result == null)
|
||||
return originName;
|
||||
DisplayName = result.DisplayName;
|
||||
GContext.Publish(new ChangeAvatarEvent());
|
||||
UpdateProfileName();
|
||||
return DisplayName;
|
||||
}
|
||||
|
||||
public virtual async Task<string> UpdateAvatarUrl(string url)
|
||||
{
|
||||
var generation = logoutGeneration;
|
||||
var originUrl = AvatarUrl;
|
||||
|
||||
Debug.Log("UpdateAvatarUrlAsync originUrl:" + originUrl + " url " + url);
|
||||
|
||||
if (string.IsNullOrEmpty(url)) return originUrl;
|
||||
if (url == originUrl)
|
||||
{
|
||||
return originUrl;
|
||||
}
|
||||
var result = await PlayFabClientAsyncAPI.UpdateAvatarUrlAsync(new UpdateAvatarUrlRequest
|
||||
{
|
||||
ImageUrl = url
|
||||
});
|
||||
Debug.Log("UpdateAvatarUrlAsync :" + result);
|
||||
if (generation != logoutGeneration || result == null) return originUrl;
|
||||
AvatarUrl = url;
|
||||
GContext.Publish(new ChangeAvatarEvent());
|
||||
UpdateProfileAvatar();
|
||||
return url;
|
||||
}
|
||||
|
||||
public virtual async Task<string> UpdateLanguage(string code)
|
||||
{
|
||||
var generation = logoutGeneration;
|
||||
if (Language == code)
|
||||
{
|
||||
return Language;
|
||||
}
|
||||
PlayFab.ProfilesModels.SetProfileLanguageRequest setProfileLanguageRequest = new PlayFab.ProfilesModels.SetProfileLanguageRequest()
|
||||
{
|
||||
Language = code.Replace('_', '-')
|
||||
};
|
||||
|
||||
var result = await PlayFabProfilesAsyncAPI.SetProfileLanguageAsync(setProfileLanguageRequest);
|
||||
if (generation == logoutGeneration && result != null)
|
||||
Language = code;
|
||||
return Language;
|
||||
}
|
||||
|
||||
public double CreateTotalSeconds()
|
||||
{
|
||||
return (ZZTimeHelper.UtcNow() - CreateTime).TotalSeconds;
|
||||
}
|
||||
|
||||
public virtual async Task<bool> GetLinkState(EAccoutType accoutType)
|
||||
{
|
||||
await Awaiters.NextFrame;
|
||||
return false;
|
||||
}
|
||||
public void UpdateProfile()
|
||||
{
|
||||
if (!IsAuthorized) return;
|
||||
PlayerProfile playerProfile = new PlayerProfile();
|
||||
playerProfile.Avatar = AvatarUrl;
|
||||
playerProfile.DisplayName = DisplayName;
|
||||
playerProfile.Level = GContext.container.Resolve<PlayerData>().lv;
|
||||
LoginRequest loginRequest = new LoginRequest() { playerId = ApiPlayerId, playerProfile = playerProfile };
|
||||
customServerMgr.AccountRequest("UpdateProfile", loginRequest);
|
||||
}
|
||||
public void UpdateProfileLv()
|
||||
{
|
||||
if (!IsAuthorized) return;
|
||||
PlayerProfile playerProfile = new PlayerProfile();
|
||||
playerProfile.Level = GContext.container.Resolve<PlayerData>().lv;
|
||||
LoginRequest loginRequest = new LoginRequest() { playerId = ApiPlayerId, playerProfile = playerProfile };
|
||||
customServerMgr.AccountRequest("UpdateProfile", loginRequest);
|
||||
}
|
||||
void UpdateProfileName()
|
||||
{
|
||||
if (!IsAuthorized) return;
|
||||
PlayerProfile playerProfile = new PlayerProfile();
|
||||
playerProfile.DisplayName = DisplayName;
|
||||
LoginRequest loginRequest = new LoginRequest() { playerId = ApiPlayerId, playerProfile = playerProfile };
|
||||
customServerMgr.AccountRequest("UpdateProfile", loginRequest);
|
||||
}
|
||||
void UpdateProfileAvatar()
|
||||
{
|
||||
if (!IsAuthorized) return;
|
||||
PlayerProfile playerProfile = new PlayerProfile();
|
||||
playerProfile.Avatar = AvatarUrl;
|
||||
LoginRequest loginRequest = new LoginRequest() { playerId = ApiPlayerId, playerProfile = playerProfile };
|
||||
customServerMgr.AccountRequest("UpdateProfile", loginRequest);
|
||||
}
|
||||
#region 其他玩家头像昵称缓存
|
||||
|
||||
Dictionary<string, PlayInfo> PlayInfos = new Dictionary<string, PlayInfo>();
|
||||
Dictionary<string, int> PlayLvs = new Dictionary<string, int>();
|
||||
public async Task<(string, int)> GetPlayLv(string playFabId)
|
||||
{
|
||||
if (!PlayLvs.ContainsKey(playFabId))
|
||||
{
|
||||
var resultUserData = await PlayFabClientAsyncAPI.GetUserDataAsync(new GetUserDataRequest()
|
||||
{
|
||||
PlayFabId = playFabId,
|
||||
Keys = new List<string>() { "Lv" }
|
||||
});
|
||||
if (resultUserData != null)
|
||||
{
|
||||
string level = resultUserData.Data.TryGetValue("Lv", out var value) ? value.Value : "1";
|
||||
PlayLvs[playFabId] = int.Parse(level);
|
||||
}
|
||||
}
|
||||
int lv = PlayLvs[playFabId];
|
||||
|
||||
return (playFabId, lv);
|
||||
}
|
||||
public int GetPlayerLv(string playFabId)
|
||||
{
|
||||
return PlayLvs.ContainsKey(playFabId) ? PlayLvs[playFabId] : 1;
|
||||
}
|
||||
|
||||
public void SetPlayerLv(string playFabId, int lv)
|
||||
{
|
||||
if (playFabId == UserId)
|
||||
{
|
||||
lv = GContext.container.Resolve<PlayerData>().lv;
|
||||
}
|
||||
PlayLvs[playFabId] = lv;
|
||||
}
|
||||
|
||||
public void ClearPlayInfos()
|
||||
{
|
||||
PlayInfos.Clear();
|
||||
PlayLvs.Clear();
|
||||
}
|
||||
|
||||
public PlayInfo GetPlayInfo(string id)
|
||||
{
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!PlayInfos.ContainsKey(id))
|
||||
{
|
||||
PlayInfo playInfo = new PlayInfo();
|
||||
playInfo.name = GetDefaultName(id);
|
||||
PlayInfos.Add(id, playInfo);
|
||||
SetPlayInfo(id, playInfo);
|
||||
}
|
||||
return PlayInfos[id];
|
||||
}
|
||||
|
||||
async void SetPlayInfo(string playFabId, PlayInfo playInfo)
|
||||
{
|
||||
if (playFabId == UserId)
|
||||
{
|
||||
playInfo.name = DisplayName;
|
||||
playInfo.avatarUrl = AvatarUrl;
|
||||
GContext.Publish(new GetClubPlayInfoEvent() { id = playFabId, name = playInfo.name, avatarUrl = playInfo.avatarUrl });
|
||||
return;
|
||||
}
|
||||
var resultUserTitle = await PlayFabClientAsyncAPI.GetPlayerProfileAsync(new GetPlayerProfileRequest()
|
||||
{
|
||||
PlayFabId = playFabId,
|
||||
ProfileConstraints = new PlayerProfileViewConstraints()
|
||||
{
|
||||
ShowDisplayName = true,
|
||||
ShowLocations = true,
|
||||
ShowAvatarUrl = true,
|
||||
}
|
||||
});
|
||||
if (resultUserTitle?.PlayerProfile != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(resultUserTitle?.PlayerProfile.DisplayName))
|
||||
{
|
||||
playInfo.name = resultUserTitle?.PlayerProfile.DisplayName;
|
||||
}
|
||||
playInfo.avatarUrl = resultUserTitle?.PlayerProfile.AvatarUrl;
|
||||
GContext.Publish(new GetClubPlayInfoEvent() { id = playFabId, name = playInfo.name, avatarUrl = playInfo.avatarUrl });
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPlayInfo(string playFabId, string name, string avatarUrl)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
name = GetDefaultName(playFabId);
|
||||
}
|
||||
PlayInfo playInfo = new PlayInfo();
|
||||
playInfo.name = name;
|
||||
playInfo.avatarUrl = avatarUrl;
|
||||
PlayInfos[playFabId] = playInfo;
|
||||
}
|
||||
#endregion 其他玩家头像昵称缓存
|
||||
}
|
||||
|
||||
public class PlayInfo
|
||||
{
|
||||
public string name;
|
||||
public string avatarUrl;
|
||||
}
|
||||
}
|
||||
@@ -1,648 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using DataCenter;
|
||||
using Game;
|
||||
using GameCore;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Activity;
|
||||
using Activity.EventSubscriber;
|
||||
using SideGameFromFishing;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
using Activity.Services;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public struct RewardPanelClose
|
||||
{
|
||||
|
||||
}
|
||||
public struct RewardPanelCloseSkipFace
|
||||
{
|
||||
|
||||
}
|
||||
public struct TargetAddData
|
||||
{
|
||||
public TargetAddData(int id, int curCount, int addCount, bool fly = true)
|
||||
{
|
||||
this.id = id;
|
||||
this.curCount = curCount;
|
||||
this.addCount = addCount;
|
||||
this.fly = fly;
|
||||
}
|
||||
public int id;
|
||||
public int curCount;
|
||||
public int addCount;
|
||||
public bool fly;
|
||||
}
|
||||
public struct ShowData
|
||||
{
|
||||
public ShowData(List<ItemData> itemDatas, RewardType rewardType = RewardType.Normal, int id = 0)
|
||||
{
|
||||
this.itemDatas = itemDatas;
|
||||
this.rewardType = rewardType;
|
||||
this.id = id;
|
||||
textValue = "";
|
||||
}
|
||||
public ShowData(ItemData itemData)
|
||||
{
|
||||
this.itemDatas = new List<ItemData>
|
||||
{
|
||||
itemData
|
||||
};
|
||||
this.rewardType = RewardType.Normal;
|
||||
this.id = 0;
|
||||
textValue = "";
|
||||
}
|
||||
public ShowData(RewardType rewardType = RewardType.Open, int id = 0, string textValue = "")
|
||||
{
|
||||
this.itemDatas = null;
|
||||
this.rewardType = rewardType;
|
||||
//弹窗相应-1数量中断
|
||||
this.id = id;
|
||||
this.textValue = textValue;
|
||||
}
|
||||
public List<ItemData> itemDatas;
|
||||
public RewardType rewardType;
|
||||
public int id;
|
||||
public string textValue;
|
||||
}
|
||||
public class CurRewardQCount
|
||||
{
|
||||
public int count;
|
||||
}
|
||||
public enum RewardType
|
||||
{
|
||||
Open,
|
||||
Normal,
|
||||
CardHolder,
|
||||
SkillUpgrade,
|
||||
CollectionAlbum,
|
||||
FeaturesUnlock,
|
||||
BenefitUpgrade,
|
||||
Destroy,
|
||||
NormalOne,
|
||||
NormalRestore,
|
||||
|
||||
FishBoxOpen,
|
||||
NewRod,
|
||||
AutoOpen,
|
||||
|
||||
FishBoxFly,
|
||||
FishBoxFlyRestore,
|
||||
|
||||
FishCardBoxOpen,
|
||||
}
|
||||
public class UnloadActToNextAct
|
||||
{
|
||||
public UnloadActToNextAct(string actId = "FishingAct", UIType transitionPanel = null, float time = 0.5f)
|
||||
{
|
||||
this.actId = actId;
|
||||
if (transitionPanel == null)
|
||||
{
|
||||
TransitionPanel = UITypes.CloudTransitionPanel;
|
||||
}
|
||||
else
|
||||
{
|
||||
TransitionPanel = transitionPanel;
|
||||
}
|
||||
|
||||
this.time = time;
|
||||
}
|
||||
public string actId;
|
||||
public UIType TransitionPanel = UITypes.CloudTransitionPanel;
|
||||
public float time = 0.5f;
|
||||
}
|
||||
public class FishingStage : AStage
|
||||
{
|
||||
public List<TargetAddData> q_TargetAddData = new List<TargetAddData>();
|
||||
public List<TargetAddData> q_TargetShowData = new List<TargetAddData>();
|
||||
[Inject]
|
||||
public IUserService UserService { get; set; }
|
||||
|
||||
Queue<ShowData> itemDatasQueue = new Queue<ShowData>();
|
||||
ShowData fishBoxOpen = new ShowData();
|
||||
bool fishCardOpen = false;
|
||||
//Queue<ShowData> itemDatasQueueNext = new Queue<ShowData>();
|
||||
|
||||
Dictionary<string, Queue<ShowData>> itemDatasQueueNextDic = new Dictionary<string, Queue<ShowData>>();
|
||||
|
||||
public bool IsNextQueue = false;
|
||||
public string NextQueueKey;
|
||||
CompositeDisposable disposable = new CompositeDisposable();
|
||||
string actName = "";
|
||||
FishingStageData stageData;
|
||||
bool rewardShowing = false;
|
||||
|
||||
private AutoRestart _autoRestart;
|
||||
private bool stopping;
|
||||
private PlayerDataStorage.IPlayerSaveStore sessionSaveStore;
|
||||
|
||||
public override async Task StartAsync()
|
||||
{
|
||||
if (stopping || this == null) return;
|
||||
var user = UserService;
|
||||
var loginLifetime = user.LoginLifetime;
|
||||
sessionSaveStore = GContext.container.Resolve<PlayerDataStorage.IPlayerSaveStore>();
|
||||
var store = sessionSaveStore;
|
||||
var kvUser = user as KVUserService;
|
||||
if (kvUser != null && (!kvUser.IsAuthorized || !kvUser.KVHandoffReady || !store.IsCurrent))
|
||||
throw new System.InvalidOperationException("KV first scene requires the current completed login.");
|
||||
LoadingScreen.SetProgress(0.54f);
|
||||
await Awaiters.NextFrame;
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested) return;
|
||||
|
||||
// wait authorize , 5 sec max
|
||||
var checkAuthorTimes = 0;
|
||||
while (checkAuthorTimes < 10 && !UserService.IsAuthorized)
|
||||
{
|
||||
await Task.Delay(500);
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested) return;
|
||||
checkAuthorTimes++;
|
||||
}
|
||||
|
||||
if (!UserService.IsAuthorized)
|
||||
{
|
||||
Debug.LogWarning("[FishingStage::startAsync] Api Svr not author");
|
||||
}
|
||||
|
||||
if (GContext.container.Resolve<IConfig>().Get("DEBUG", "0") == "1")
|
||||
{
|
||||
var console = await UnityEngine.AddressableAssets.Addressables.InstantiateAsync("IngameDebugConsole").Task;
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested)
|
||||
{
|
||||
if (console != null) UnityEngine.AddressableAssets.Addressables.ReleaseInstance(console);
|
||||
return;
|
||||
}
|
||||
}
|
||||
await GContext.container.Resolve<SoundMgr>().Init((progress) =>
|
||||
{
|
||||
if (!stopping && this != null && !loginLifetime.IsCancellationRequested) LoadingScreen.SetProgress(0.55f + progress * 0.2f);
|
||||
});
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested) return;
|
||||
//GContext.Publish()
|
||||
if (kvUser != null && (!kvUser.IsAuthorized || !kvUser.KVHandoffReady || !store.IsCurrent))
|
||||
throw new System.InvalidOperationException("KV session changed before first scene initialization.");
|
||||
stageData = new FishingStageData();
|
||||
|
||||
LoadingScreen.SetProgress(0.75f);
|
||||
|
||||
// 注册 EventTokenService 为单例,供活动代币相关逻辑统一使用
|
||||
GContext.container.Register<IEventTokenService,EventTokenService>().AsSingleton();
|
||||
GContext.container.Register<IDeferredRewardStashService, DeferredRewardStashService>().AsSingleton();
|
||||
GContext.container.Resolve<IDeferredRewardStashService>().Reset();
|
||||
|
||||
InitData();
|
||||
InitManager();
|
||||
if (kvUser != null)
|
||||
{
|
||||
if (!GContext.container.Resolve<TimeManager.TimeRefreshManager>().InitialRefreshSucceeded)
|
||||
throw new System.InvalidOperationException("KV initial daily refresh failed; previous login must be retained.");
|
||||
// Daily refresh consumes the previous login first. Save its actual player aggregate
|
||||
// and pending module writes before advancing the persisted login timestamp.
|
||||
GContext.container.Resolve<PlayerData>().SavePlayerData();
|
||||
var initialization = await store.FlushAsync(loginLifetime);
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested) return;
|
||||
if (!kvUser.IsAuthorized || !store.IsCurrent || !initialization.Confirmed)
|
||||
throw new System.InvalidOperationException("KV initialization saves were not confirmed.");
|
||||
var loginSaved = await store.WriteAndConfirmAsync("LastLoginTimeUtc",
|
||||
kvUser.LoginTime.ToUniversalTime().ToString("O", System.Globalization.CultureInfo.InvariantCulture), loginLifetime);
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested) return;
|
||||
if (!kvUser.IsAuthorized || !store.IsCurrent || !loginSaved.Confirmed)
|
||||
throw new System.InvalidOperationException("KV login timestamp was not confirmed.");
|
||||
}
|
||||
GContext.container.RegisterInstance<IStageData>(stageData);
|
||||
GContext.container.Register<SurveyService>().AsSingleton();
|
||||
GContext.OnEvent<TargetAddData>().Subscribe(OnAddTarget).AddTo(disposable);
|
||||
GContext.OnEvent<ShowData>().Subscribe(OnShowDataEvent).AddTo(disposable);
|
||||
GContext.OnEvent<ItemClick>().Subscribe(OnItemClickSettlementEvent).AddTo(disposable);
|
||||
GContext.OnEvent<CurRewardQCount>().Subscribe(GetShowDataCount).AddTo(disposable);
|
||||
GContext.OnEvent<UnloadActToNextAct>().Subscribe(ToFishingStageAct).AddTo(disposable);
|
||||
GContext.OnEvent<game.EvtAPISvrUnauthorized>().Subscribe(OnUnauthorized).AddTo(disposable);
|
||||
await GContext.container.Resolve<IUIService>().LoadAtlas();
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested) return;
|
||||
LoadingScreen.SetProgress(0.77f);
|
||||
|
||||
//这里拿补单信息,拿到之后再进入主界面处理
|
||||
await GContext.container.Resolve<PlayerShopData>().GetReplenishmentData();
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested) return;
|
||||
PlayerData playerData = GContext.container.Resolve<PlayerData>();
|
||||
//加载当前场景
|
||||
var mapData = playerData.GetCurrentMapData();
|
||||
ILoadResourceService loadResourceService = GContext.container.Resolve<ILoadResourceService>();
|
||||
List<string> mapNames = new List<string>() { mapData.EnvName };
|
||||
mapNames.AddRange(GContext.container.Resolve<CampDataMM>().AllPrefabs);
|
||||
bool result = await loadResourceService.Loading(mapNames, (progress) =>
|
||||
{
|
||||
if (!stopping && this != null && !loginLifetime.IsCancellationRequested) LoadingScreen.SetProgress(0.77f + progress * 0.13f);
|
||||
});
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested) return;
|
||||
if (!result)
|
||||
{
|
||||
playerData.SetCurrentMapId(GContext.container.Resolve<Tables>().TbMapData.DataList[0].ID);
|
||||
}
|
||||
LoadingScreen.SetLoadingInfo(LocalizationMgr.GetText("msg_loadingfinishing"));
|
||||
|
||||
GContext.container.Resolve<LeadboardData>().SetLvData();
|
||||
try { await base.StartAsync(); }
|
||||
catch (System.Exception ex) when (stopping || this == null || loginLifetime.IsCancellationRequested)
|
||||
{
|
||||
Debug.LogWarning($"[FishingStage] Startup stopped, cancelled or stage destroyed: {ex.GetType().Name}: {ex.Message}");
|
||||
return;
|
||||
}
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested) return;
|
||||
//静默加载后续场景
|
||||
playerData.LoadNewMap();
|
||||
//更新数据
|
||||
//GContext.container.Resolve<LeadboardData>().UpdateMemberRank();
|
||||
LoadingScreen.SetProgress(1);
|
||||
GContext.container.Resolve<SurveyService>().GetSurveyMatchlist();
|
||||
AiHelpManager.Instance.Login();
|
||||
//AntiAddiction
|
||||
// await GContext.container.Resolve<AntiAddictionService>().ChckIfCanPlay();
|
||||
// GContext.container.Register<IDeferredRewardStashService, DeferredRewardStashService>().AsSingleton();
|
||||
//
|
||||
InitEvent();
|
||||
GContext.container.Resolve<AntiCheatManager>().CheckBan();
|
||||
_autoRestart = new AutoRestart();
|
||||
_autoRestart.Init();
|
||||
}
|
||||
|
||||
void InitEvent()
|
||||
{
|
||||
var rtService = GContext.container.Resolve<IRTService>();
|
||||
rtService.OnEvent<NotifyFriendInvitedCount>(notify => GContext.container.Resolve<FriendService>().GetInvitedListCount())
|
||||
.AddTo(disposable);
|
||||
|
||||
GContext.container.Resolve<FriendService>().GetInvitedListCount();
|
||||
|
||||
MainThreadDispatcher.OnApplicationPauseAsObservable()
|
||||
.Subscribe(paused =>
|
||||
{
|
||||
if (paused) _ = FlushPausedSaveAsync();
|
||||
else _ = OnApplicationResumedAsync();
|
||||
})
|
||||
.AddTo(disposable);
|
||||
}
|
||||
|
||||
private async Task FlushPausedSaveAsync()
|
||||
{
|
||||
var user = UserService;
|
||||
var store = sessionSaveStore;
|
||||
if (stopping || user is not KVUserService || !user.IsAuthorized || store == null || !store.IsCurrent) return;
|
||||
try
|
||||
{
|
||||
var result = await store.FlushAsync(user.LoginLifetime);
|
||||
if (!stopping && store.IsCurrent && !result.Confirmed)
|
||||
Debug.LogWarning("KV pause save: " + result.Status);
|
||||
}
|
||||
catch (System.Exception ex) { Debug.LogError("KV pause save failed: " + ex.GetType().Name); }
|
||||
}
|
||||
|
||||
private async Task OnApplicationResumedAsync()
|
||||
{
|
||||
var userService = UserService;
|
||||
if (userService == null || !userService.IsLogin)
|
||||
return;
|
||||
var lifetime = userService.LoginLifetime;
|
||||
var store = sessionSaveStore;
|
||||
if (userService is KVUserService && (!userService.IsAuthorized || store == null || !store.IsCurrent)) return;
|
||||
|
||||
var rtService = GContext.container.Resolve<IRTService>();
|
||||
var mailService = GContext.container.Resolve<IInGameMailService>();
|
||||
if (rtService != null && mailService != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await rtService.Connect();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
if (stopping || lifetime.IsCancellationRequested || !userService.IsLogin ||
|
||||
(userService is KVUserService && !store.IsCurrent)) return;
|
||||
await mailService.FetchMail();
|
||||
}
|
||||
}
|
||||
|
||||
void InitData()
|
||||
{
|
||||
GContext.container.Resolve<CampDataMM>().Init();
|
||||
GContext.container.Resolve<PlayerData>().Init();
|
||||
GContext.container.Resolve<FishingEventData>().Init();
|
||||
GContext.container.Resolve<PlayerFishData>().Init();
|
||||
GContext.container.Resolve<GuideDataCenter>().Init();
|
||||
GContext.container.Resolve<PlayerShopData>().Init();
|
||||
GContext.container.Resolve<TriggerPackData>().Init();
|
||||
//- 不要再这里Init,保持原来的数据
|
||||
// GContext.container.Resolve<OfferChainsChestManager>().Init();
|
||||
// GContext.container.Resolve<OfferDiscoPartyManager>().Init();
|
||||
GContext.container.Resolve<EventPartnerGatherManager>().Init();
|
||||
GContext.container.Resolve<EventCubeMeltManager>().Init();
|
||||
GContext.container.Resolve<FishingChallengeManager>().Init();
|
||||
GContext.container.Resolve<ClubService>().Init();
|
||||
GContext.container.Resolve<AchievementDataManager>().Init();
|
||||
GContext.container.Resolve<SmallGameData>().Init();
|
||||
GContext.Publish(new ConditionTypeEvent { type = cfg.ConditionType.LoginDays, count = 1 });
|
||||
}
|
||||
private void OnAddTarget(TargetAddData data)
|
||||
{
|
||||
if (data.fly)
|
||||
{
|
||||
q_TargetAddData.Add(data);
|
||||
}
|
||||
q_TargetShowData.Add(data);
|
||||
}
|
||||
|
||||
public void ClearTargetAddData()
|
||||
{
|
||||
q_TargetAddData.Clear();
|
||||
q_TargetShowData.Clear();
|
||||
}
|
||||
|
||||
void GetShowDataCount(CurRewardQCount curRewardQCount)
|
||||
{
|
||||
curRewardQCount.count = itemDatasQueue.Count;
|
||||
if (fishCardOpen)
|
||||
{
|
||||
curRewardQCount.count++;
|
||||
}
|
||||
if (fishBoxOpen.id > 0)
|
||||
{
|
||||
curRewardQCount.count++;
|
||||
}
|
||||
}
|
||||
|
||||
void OnShowDataEvent(ShowData data)
|
||||
{
|
||||
if (data.rewardType == RewardType.Open || data.rewardType == RewardType.AutoOpen)
|
||||
{
|
||||
ShowReward(data);
|
||||
}
|
||||
else if (data.rewardType == RewardType.Destroy)
|
||||
{
|
||||
OnDestroyUI(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnAddItem(data);
|
||||
}
|
||||
}
|
||||
|
||||
void OnAddItem(ShowData showData)
|
||||
{
|
||||
if (IsNextQueue)
|
||||
{
|
||||
//if (showData.rewardType == RewardType.FishBoxOpen)
|
||||
//{
|
||||
// if (itemDatasQueueNext.Where((_) => _.rewardType == showData.rewardType).Count() > 0)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//}
|
||||
//itemDatasQueueNext.Enqueue(showData);
|
||||
if (!itemDatasQueueNextDic.TryGetValue(NextQueueKey, out Queue<ShowData> queue))
|
||||
{
|
||||
queue = new Queue<ShowData>();
|
||||
itemDatasQueueNextDic.Add(NextQueueKey, queue);
|
||||
}
|
||||
queue.Enqueue(showData);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (showData.rewardType == RewardType.FishBoxOpen)
|
||||
{
|
||||
if (fishBoxOpen.id > 0)
|
||||
{
|
||||
showData.itemDatas = null;
|
||||
}
|
||||
fishBoxOpen = showData;
|
||||
}
|
||||
else if (showData.rewardType == RewardType.CardHolder)
|
||||
{
|
||||
fishCardOpen = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemDatasQueue.Enqueue(showData);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void NextToQueue(string nextQueueKey)
|
||||
{
|
||||
if (itemDatasQueueNextDic.ContainsKey(nextQueueKey))
|
||||
{
|
||||
var queue = itemDatasQueueNextDic[nextQueueKey];
|
||||
if (queue.Count > 0)
|
||||
{
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
itemDatasQueue.Enqueue(queue.Dequeue());
|
||||
}
|
||||
}
|
||||
itemDatasQueueNextDic.Remove(nextQueueKey);
|
||||
}
|
||||
//if (itemDatasQueueNext.Count > 0)
|
||||
//{
|
||||
// while (itemDatasQueueNext.Count > 0)
|
||||
// {
|
||||
// itemDatasQueue.Enqueue(itemDatasQueueNext.Dequeue());
|
||||
// }
|
||||
//}
|
||||
}
|
||||
ShowData showData;
|
||||
void ShowReward(ShowData showData)
|
||||
{
|
||||
AddBox();
|
||||
if (itemDatasQueue.Count == 0 || rewardShowing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.showData = showData;
|
||||
ShowPeek();
|
||||
}
|
||||
void AddBox()
|
||||
{
|
||||
if (fishCardOpen)
|
||||
{
|
||||
var fishCardShowData = new ShowData(RewardType.CardHolder);
|
||||
itemDatasQueue.Enqueue(fishCardShowData);
|
||||
fishCardOpen = false;
|
||||
}
|
||||
|
||||
if (fishBoxOpen.id > 0)
|
||||
{
|
||||
itemDatasQueue.Enqueue(fishBoxOpen);
|
||||
fishBoxOpen = new ShowData();
|
||||
}
|
||||
}
|
||||
void OnDestroyUI(ShowData showData)
|
||||
{
|
||||
rewardShowing = false;
|
||||
AddBox();
|
||||
GContext.container.Resolve<PlayerData>().ShowNewRod();
|
||||
|
||||
if (itemDatasQueue.Count > 0)
|
||||
{
|
||||
itemDatasQueue.Dequeue();
|
||||
ShowPeek();
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyRewardPanel();
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyRewardPanel()
|
||||
{
|
||||
UIManager.Instance.DestroyUI(UITypes.RewardPopupPanel);
|
||||
GContext.Publish(new RewardPanelClose());
|
||||
if (itemClick != null)
|
||||
{
|
||||
GContext.Publish<ItemClick>(itemClick);
|
||||
}
|
||||
}
|
||||
|
||||
async void ShowPeek()
|
||||
{
|
||||
if (itemDatasQueue.Count == 0 || this.showData.id == 1)
|
||||
{
|
||||
rewardShowing = false;
|
||||
DestroyRewardPanel();
|
||||
}
|
||||
else
|
||||
{
|
||||
rewardShowing = true;
|
||||
this.showData.id--;
|
||||
ShowData curShowData = itemDatasQueue.Peek();
|
||||
RewardPopupPanel rewardPopupPanel = (await UIManager.Instance.ShowUI(UITypes.RewardPopupPanel)).GetComponent<RewardPopupPanel>();
|
||||
rewardPopupPanel.ShowPanel(curShowData, showData.textValue, showData.rewardType);
|
||||
}
|
||||
}
|
||||
private ItemClick itemClick;
|
||||
private async void OnItemClickSettlementEvent(ItemClick data)
|
||||
{
|
||||
if (data == null)
|
||||
return;
|
||||
//缓存数据 等待奖励界面关闭,触发
|
||||
if (itemClick == null)
|
||||
{
|
||||
itemClick = data;
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrEmpty(data.Prefab))
|
||||
{
|
||||
itemClick = null;
|
||||
return;
|
||||
}
|
||||
UITypes.ItemClickSettlementPanel.SetType(data.Prefab);
|
||||
var panel = (await UIManager.Instance.ShowUILoad(UITypes.ItemClickSettlementPanel))
|
||||
?.GetComponent<ItemClickSettlementPanel>();
|
||||
panel?.SetData(data);
|
||||
itemClick = null;
|
||||
}
|
||||
void ToFishingStageAct(UnloadActToNextAct toFishingStageAct)
|
||||
{
|
||||
if (actName == toFishingStageAct.actId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var faceService = GContext.container.Resolve<IFaceUIService>();
|
||||
faceService.StopFaceUI();
|
||||
faceService.StopPostFaceUI();
|
||||
UIManager.Instance.panelStack.Clear();
|
||||
actName = toFishingStageAct.actId;
|
||||
_transitionPanel = toFishingStageAct.TransitionPanel;
|
||||
transitionTime = toFishingStageAct.time;
|
||||
CloudTransitionPanel();
|
||||
}
|
||||
|
||||
async void CloudTransitionPanel()
|
||||
{
|
||||
var transitionPanel = _transitionPanel ?? UITypes.CloudTransitionPanel;
|
||||
await UIManager.Instance.ShowUI(transitionPanel);
|
||||
await Awaiters.Seconds(transitionTime);
|
||||
UIManager.Instance.DestroyUI(UITypes.LackOfResourcePopupPanel);
|
||||
await LoadActAsync(actName);
|
||||
}
|
||||
|
||||
private async void OnUnauthorized(EvtAPISvrUnauthorized evt)
|
||||
{
|
||||
var userService = GContext.container.Resolve<IUserService>();
|
||||
// if (userService.IsAuthorized)
|
||||
// {
|
||||
// GameObject go = await UIManager.Instance.ShowUI(UITypes.NoticeConfirmPopupPanel);
|
||||
// var noticeConfirmPopupPanel = go.GetComponent<NoticeConfirmPopupPanel>();
|
||||
// string title = LocalizationMgr.GetText("UI_NoticePopupPanel_3");
|
||||
// string info = LocalizationMgr.GetText("UI_NoticePopupPanel_4");
|
||||
// string label = LocalizationMgr.GetText("UI_COMMON_confirm");
|
||||
// var stageService = GContext.container.Resolve<IStageService>();
|
||||
// noticeConfirmPopupPanel.Init(1, title, info,
|
||||
// onClickLeft: () => { stageService.SwitchStageAsync("QuitStage"); },
|
||||
// onClose: () => { stageService.SwitchStageAsync("QuitStage"); },
|
||||
// btn_left_text: label);
|
||||
// }
|
||||
}
|
||||
|
||||
public override async Task Stop()
|
||||
{
|
||||
stopping = true;
|
||||
GContext.container.Unregister<IStageData>();
|
||||
GContext.container.Unregister<SurveyService>();
|
||||
GContext.container.Resolve<IDeferredRewardStashService>()?.DisposeSubscriptions();
|
||||
GContext.container.Unregister<IDeferredRewardStashService>();
|
||||
disposable?.Dispose();
|
||||
disposable = null;
|
||||
_autoRestart?.Dispose();
|
||||
_autoRestart = null;
|
||||
await base.Stop();
|
||||
}
|
||||
protected override void OnDestroy() { stopping = true; }
|
||||
|
||||
//protected override async Task OnActUnload(IAct act, bool requestNextAct)
|
||||
//{
|
||||
// if (requestNextAct)
|
||||
// {
|
||||
// var transitionPanel = _transitionPanel ?? UITypes.CloudTransitionPanel;
|
||||
// await UIManager.Instance.ShowUI(transitionPanel);
|
||||
// await Task.Delay(500);
|
||||
// UIManager.Instance.DestroyUI(UITypes.LackOfResourcePopupPanel);
|
||||
// }
|
||||
// await base.OnActUnload(act, requestNextAct);
|
||||
//}
|
||||
|
||||
private UIType _transitionPanel;
|
||||
float transitionTime = 0.5f;
|
||||
protected override async Task OnActLoaded(IAct act)
|
||||
{
|
||||
GContext.Publish(new EndTransition());
|
||||
await base.OnActLoaded(act);
|
||||
await Awaiters.SecondsRealtime(0.5f);
|
||||
LoadingScreen.Hide();
|
||||
}
|
||||
|
||||
protected override async Task RequestNextAct(string actId)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
//await LoadActAsync(actName);
|
||||
}
|
||||
|
||||
//init by type
|
||||
public void InitManager()
|
||||
{
|
||||
foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
|
||||
{
|
||||
OrderManager.OrderManager.Init(type);
|
||||
SideGameFromFishingManager.Init(type);
|
||||
ActivitySentryManager.InitActivitySentryType(type);
|
||||
ActivitySubscriberManager.InitEventSubscriber(type);
|
||||
}
|
||||
GContext.container.Resolve<ActivityManager>().Init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
using asap.core;
|
||||
using DataCenter;
|
||||
using Game;
|
||||
using GameCore;
|
||||
using Script.RuntimeScript;
|
||||
using System.Threading.Tasks;
|
||||
using TimeManager;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public class LoginStage : AStage
|
||||
{
|
||||
protected override void OnDestroy() { }
|
||||
|
||||
|
||||
public override async Task StartAsync()
|
||||
{
|
||||
using (GContext.container.InjectionScope())
|
||||
{
|
||||
#if AGG
|
||||
GContext.container.Register<IIAPService, AggIAPService>().AsSingleton();
|
||||
GContext.container.Register<IUserService, AggUserService>().AsSingleton();
|
||||
GContext.container.Register<PlayerDataStorage.IPlayerSaveStore, PlayerDataStorage.PlayFabPlayerSaveStore>().AsSingleton();
|
||||
#else
|
||||
if (SubBoostrap.RuntimeSelection?.IsKV == true)
|
||||
{
|
||||
if (PlayFabMgr.HasLiveInstanceForKVLoad)
|
||||
throw new System.InvalidOperationException("KV startup found an active legacy save manager.");
|
||||
GContext.container.Register<IUserService, KVUserService>().AsSingleton();
|
||||
GContext.container.Register<PlayerDataStorage.IPlayerSaveStore, PlayerDataStorage.KVPlayerSaveStore>().AsSingleton();
|
||||
}
|
||||
else
|
||||
{
|
||||
GContext.container.Register<IUserService, PlayFabUserService>().AsSingleton();
|
||||
GContext.container.Register<PlayerDataStorage.IPlayerSaveStore, PlayerDataStorage.PlayFabPlayerSaveStore>().AsSingleton();
|
||||
}
|
||||
GContext.container.Register<IIAPService, DevIAPService>().AsSingleton();
|
||||
#endif
|
||||
GContext.container.Register<PlayerFishData>().AsSingleton();
|
||||
GContext.container.Register<PlayerItemData>().AsSingleton();
|
||||
GContext.container.Register<CampDataMM>().AsSingleton();
|
||||
GContext.container.Register<FishingEventData>().AsSingleton();
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerDataStorage.PlayerKVGameplayLoop.Enabled || SubBoostrap.RuntimeSelection?.IsKV==true)
|
||||
GContext.container.Register<EventShootingRangeData>().AsSingleton();
|
||||
#else
|
||||
if(SubBoostrap.RuntimeSelection?.IsKV==true)
|
||||
GContext.container.Register<EventShootingRangeData>().AsSingleton();
|
||||
#endif
|
||||
GContext.container.Register<SigninData>().AsSingleton();
|
||||
//GContext.container.Register<TaskData>().AsSingleton();
|
||||
GContext.container.Register<ClubService>().AsSingleton();
|
||||
GContext.container.Register<LeadboardData>().AsSingleton();
|
||||
GContext.container.Register<PlayerShopData>().AsSingleton();
|
||||
GContext.container.Register<TriggerPackData>().AsSingleton();
|
||||
GContext.container.Register<GuideDataCenter>().AsSingleton();
|
||||
GContext.container.Register<SmallGameData>().AsSingleton();
|
||||
GContext.container.Register<PlayerData>().AsSingleton();
|
||||
GContext.container.Register<MMTService>().AsSingleton();
|
||||
GContext.container.Register<BuffDataCenter>().AsSingleton();
|
||||
GContext.container.Register<FishingBoxData>().AsSingleton();
|
||||
GContext.container.Register<FishingBoxDataProvier>().AsSingleton();
|
||||
// GContext.container.Register<FishingChallengeCenter>().AsSingleton();
|
||||
GContext.container.Register<FishingChallengeManager>().AsSingleton();
|
||||
GContext.container.Register<OfferChainsChestManager>().AsSingleton();
|
||||
GContext.container.Register<OfferDiscoPartyManager>().AsSingleton();
|
||||
GContext.container.Register<BattlePassDataProvider>().AsSingleton();
|
||||
GContext.container.Register<MiniBattlePassDataProvider>().AsSingleton();
|
||||
// GContext.container.Register<OfferRushDataProvider>().AsSingleton();
|
||||
GContext.container.Register<BattlePassData>().AsSingleton();
|
||||
GContext.container.Register<EventPackData>().AsSingleton();
|
||||
GContext.container.Register<AchievementDataManager>().AsSingleton();
|
||||
|
||||
//GContext.container.Register<PokerScoreData>().AsSingleton();
|
||||
GContext.container.Register<PiggyBankPackData>().AsSingleton();
|
||||
|
||||
GContext.container.Register<BargainPackData>().AsSingleton();
|
||||
GContext.container.Register<DiggingGameManager>().AsSingleton();
|
||||
GContext.container.Register<EventPartnerData>().AsSingleton();
|
||||
GContext.container.Register<EventPartnerGatherManager>().AsSingleton();
|
||||
GContext.container.Register<EventCubeMeltManager>().AsSingleton();
|
||||
GContext.container.Register<IFootPrintService, FootPrintService>().AsSingleton();
|
||||
GContext.container.Register<FriendService>().AsSingleton();
|
||||
GContext.container.Register<EventPartnerService>().AsSingleton();
|
||||
GContext.container.Register<EventPotionDataManager>().AsSingleton();
|
||||
GContext.container.Register<EventPinballDataManager>().AsSingleton();
|
||||
GContext.container.Register<EventPinataPackDataManager>().AsSingleton();
|
||||
GContext.container.Register<EventScratchTicketDataManager>().AsSingleton();
|
||||
GContext.container.Register<Assets.Scripts.UI.OfferSweep.OfferSweepManager>().AsSingleton();
|
||||
GContext.container.Register<Assets.Scripts.UI.AdWheel.AdWheelManager>().AsSingleton();
|
||||
GContext.container.Register<Assets.Scripts.UI.JigsawPuzzle.JigsawPuzzleManager>().AsSingleton();
|
||||
|
||||
GContext.container.Register<AntiCheatManager>().AsSingleton();
|
||||
GContext.container.Register<Activity.ActivityManager>().AsSingleton();
|
||||
GContext.container.Register<Activity.ActivityScopeManager>().AsSingleton();
|
||||
GContext.container.Register<TimeRefreshManager>().AsSingleton();
|
||||
GContext.container.Register<ITimeTickService, TimeTickService>().AsSingleton();
|
||||
|
||||
|
||||
UnityEngine.Debug.Log("LoginStage StartAsync step 2");
|
||||
}
|
||||
|
||||
UnityEngine.Debug.Log("LoginStage StartAsync step 3");
|
||||
|
||||
LoadingScreen.SetProgress(0.20f);
|
||||
#if AGG
|
||||
startActId = "AggLoginAct";
|
||||
#else
|
||||
startActId = "PFLoginAct";
|
||||
#endif
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) startActId="PFLoginAct";
|
||||
#endif
|
||||
if (!LoadingScreen.isShowing())
|
||||
{
|
||||
await LoadingScreen.Show();
|
||||
}
|
||||
LoadingScreen.SetLoadingInfo(Boostrapi18n.Instance.GetText("msg_loggingin"));
|
||||
UnityEngine.Debug.Log($"LoginStage StartAsync {startActId}");
|
||||
|
||||
await base.StartAsync();
|
||||
}
|
||||
|
||||
protected override async Task RequestNextAct(string actId)
|
||||
{
|
||||
if(SubBoostrap.RuntimeSelection?.IsKV==true)
|
||||
return;
|
||||
await GContext.container.Resolve<IStageService>().SwitchStageAsync("FishingStage");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,372 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using GameCore;
|
||||
using System.Linq;
|
||||
|
||||
namespace game
|
||||
{
|
||||
|
||||
public class PFLoginAct : AGameAct
|
||||
{
|
||||
[Inject] public IUserService userService { get; set; }
|
||||
[Inject] public IConfig config { get; set; }
|
||||
[Inject] public IInGameMailService MailService { get; set; }
|
||||
[Inject] public IRTService rtService { get; set; }
|
||||
[Inject] public IIAPService iapService { get; set; }
|
||||
|
||||
private PlayerDataStorage.IKVUserLifecycle kvLifecycle => userService as PlayerDataStorage.IKVUserLifecycle;
|
||||
|
||||
[SerializeField]
|
||||
private GameObject customInput;
|
||||
|
||||
[SerializeField]
|
||||
private InputField customIdInput;
|
||||
[SerializeField]
|
||||
private Button inputOkBtn;
|
||||
|
||||
[SerializeField] SimpleDialog dialog;
|
||||
private string[] loadingInfos;
|
||||
private CancellationTokenSource loginLifetime;
|
||||
private CancellationToken loginToken;
|
||||
|
||||
public override async Task<bool> StartAsync()
|
||||
{
|
||||
loginLifetime = CancellationTokenSource.CreateLinkedTokenSource(userService.LoginLifetime);
|
||||
loginToken = loginLifetime.Token;
|
||||
Debug.Log("PFLoginAct StartAsync");
|
||||
|
||||
loadingInfos = new string[] {
|
||||
"UI_FishingLoginPanel_LoginGameStage_1",
|
||||
"UI_FishingLoginPanel_LoginGameStage_2",
|
||||
"UI_FishingLoginPanel_LoginGameStage_3",
|
||||
"UI_FishingLoginPanel_LoginGameStage_4",
|
||||
"UI_FishingLoginPanel_LoginGameStage_5",
|
||||
"UI_FishingLoginPanel_LoginGameStage_6",
|
||||
"UI_FishingLoginPanel_LoginGameStage_7",
|
||||
"UI_FishingLoginPanel_LoginGameStage_8",
|
||||
"UI_FishingLoginPanel_LoginGameStage_9"
|
||||
}.Select(key => LocalizationMgr.GetText(key)).ToArray();
|
||||
|
||||
await base.StartAsync();
|
||||
if (loginToken.IsCancellationRequested) return false;
|
||||
DoLogin();
|
||||
return true;
|
||||
}
|
||||
|
||||
//CancellationTokenSource source;
|
||||
private async void DoLogin()
|
||||
{
|
||||
try { await LoginAsync(); }
|
||||
catch (OperationCanceledException) when (loginToken.IsCancellationRequested) { }
|
||||
catch (Exception ex) { Debug.LogException(ex); }
|
||||
finally { StopLoadingInfo(); }
|
||||
}
|
||||
|
||||
private async Task LoginAsync()
|
||||
{
|
||||
loginToken.ThrowIfCancellationRequested();
|
||||
UpdateLoadingInfo();
|
||||
var configCustomId = config.Get<string>("CustomID", string.Empty);
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) configCustomId=game.PlayerDataStorage.PlayerKVGameplayLoop.Permit.PlayerId;
|
||||
#endif
|
||||
if (configCustomId == @"{{input}}")
|
||||
{
|
||||
LoadingScreen.HideProgressBar();
|
||||
StopLoadingInfo();
|
||||
await InputID();
|
||||
loginToken.ThrowIfCancellationRequested();
|
||||
LoadingScreen.ShowProgressBar();
|
||||
UpdateLoadingInfo();
|
||||
}
|
||||
else
|
||||
{
|
||||
userService.CustomId = configCustomId;
|
||||
}
|
||||
|
||||
//source = new CancellationTokenSource();
|
||||
//LoadingAutoInc(source.Token, 1f);
|
||||
|
||||
var formalKV=SubBoostrap.RuntimeSelection?.IsKV==true;
|
||||
rtService.WaitForAPISvrLogin();
|
||||
var step = await userService.Login(OnLoginStep);
|
||||
loginToken.ThrowIfCancellationRequested();
|
||||
if(formalKV)
|
||||
{
|
||||
StopLoadingInfo();
|
||||
LoadingScreen.HideProgressBar();
|
||||
if(step==ELoginStep.Done && kvLifecycle?.KVHandoffReady==true)
|
||||
LoadingScreen.SetLoadingInfo("KV account loaded. Gameplay is not enabled yet.");
|
||||
else
|
||||
{
|
||||
var error=kvLifecycle?.KVLastError.ToString() ?? "Input";
|
||||
dialog.Config(new SimpleDialog.Context
|
||||
{
|
||||
message="KV login unavailable",
|
||||
message2="Login stopped ("+error+"). Sign out or restart before trying again.",
|
||||
okAction=()=>userService.Logout(), closeAction=()=>userService.Logout()
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (step != ELoginStep.Done)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) throw game.PlayerDataStorage.PlayerKVGameplayLoop.Stop("Login failed; restart the Unity process before another attempt.");
|
||||
#endif
|
||||
//source.Cancel();
|
||||
//source.Dispose();
|
||||
await ReytryDialog(userService.Login, LocalizationMgr.GetText("subtitle_loginfailed")
|
||||
, LocalizationMgr.GetText("msg_loginfailed"));
|
||||
}
|
||||
loginToken.ThrowIfCancellationRequested();
|
||||
|
||||
StopLoadingInfo();
|
||||
//source.Cancel();
|
||||
//source.Dispose();
|
||||
// try
|
||||
// {
|
||||
// await rtService.Connect();
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// Debug.LogException(ex);
|
||||
// }
|
||||
MailService.Init();
|
||||
iapService.Init();
|
||||
|
||||
Unload();
|
||||
}
|
||||
|
||||
private async Task InputID()
|
||||
{
|
||||
customInput.SetActive(true);
|
||||
var tempInput = PlayerPrefs.GetString("CustomID", string.Empty);
|
||||
var inputTask = new TaskCompletionSource<string>();
|
||||
UnityEngine.Events.UnityAction accept = () =>
|
||||
{
|
||||
if (!loginToken.IsCancellationRequested) inputTask.TrySetResult(customIdInput.text);
|
||||
};
|
||||
inputOkBtn.onClick.AddListener(accept);
|
||||
customIdInput.text = tempInput;
|
||||
try
|
||||
{
|
||||
using (loginToken.Register(() => inputTask.TrySetCanceled()))
|
||||
tempInput = await inputTask.Task;
|
||||
}
|
||||
finally { if (inputOkBtn != null) inputOkBtn.onClick.RemoveListener(accept); }
|
||||
loginToken.ThrowIfCancellationRequested();
|
||||
if (!string.IsNullOrEmpty(tempInput))
|
||||
{
|
||||
PlayerPrefs.SetString("CustomID", tempInput);
|
||||
}
|
||||
userService.CustomId = tempInput;
|
||||
|
||||
customInput.SetActive(false);
|
||||
}
|
||||
|
||||
private async Task ReytryDialog(Func<Action<int>, bool, Task<ELoginStep>> func, string title, string message)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
loginToken.ThrowIfCancellationRequested();
|
||||
//source = new CancellationTokenSource();
|
||||
var retry = new TaskCompletionSource<ELoginStep>();
|
||||
bool submitted = false;
|
||||
async void Submit()
|
||||
{
|
||||
if (submitted || loginToken.IsCancellationRequested) return;
|
||||
submitted = true;
|
||||
try
|
||||
{
|
||||
if (config.Get<string>("CustomID", string.Empty) == @"{{input}}")
|
||||
{
|
||||
LoadingScreen.HideProgressBar();
|
||||
StopLoadingInfo();
|
||||
await InputID();
|
||||
loginToken.ThrowIfCancellationRequested();
|
||||
LoadingScreen.ShowProgressBar();
|
||||
UpdateLoadingInfo();
|
||||
}
|
||||
retry.TrySetResult(await func(OnLoginStep, true));
|
||||
}
|
||||
catch (Exception ex) { retry.TrySetException(ex); }
|
||||
}
|
||||
using var cancellation = loginToken.Register(() => retry.TrySetCanceled());
|
||||
//LoadingAutoInc(source.Token, 1f);
|
||||
dialog.Config(new SimpleDialog.Context()
|
||||
{
|
||||
message = title,
|
||||
message2 = message,
|
||||
okAction = Submit,
|
||||
closeAction = Submit
|
||||
});
|
||||
var result = await retry.Task;
|
||||
//LoadingAutoInc(source.Token, 1f);
|
||||
await Task.Delay(1000, loginToken);
|
||||
if (result == ELoginStep.Done)
|
||||
return;
|
||||
//else
|
||||
//{
|
||||
//source.Cancel();
|
||||
//source.Dispose();
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLoginStep(int step)
|
||||
{
|
||||
if (loginToken.IsCancellationRequested) return;
|
||||
LoadingScreen.SetProgress(0.2f + step / 2.0f * 0.3f);
|
||||
}
|
||||
|
||||
float lastChangeTime = 0;
|
||||
float lastWaitTime = 0;
|
||||
int lastChangeIndex = 0;
|
||||
float Text_Change_interval = 1.5f;
|
||||
float Text_Wait_Interval = 0.15f;
|
||||
bool isUpdatingLoadingInfo;
|
||||
|
||||
private async void UpdateLoadingInfo()
|
||||
{
|
||||
if (isUpdatingLoadingInfo) return;
|
||||
isUpdatingLoadingInfo = true;
|
||||
var deltaTime = 0f;
|
||||
var info = string.Empty;
|
||||
while (isUpdatingLoadingInfo && !loginToken.IsCancellationRequested)
|
||||
{
|
||||
info = GetTextByDeltaTime(deltaTime, info);
|
||||
LoadingScreen.SetLoadingInfo(info);
|
||||
await Awaiters.NextFrame;
|
||||
deltaTime += Time.deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetTextByDeltaTime(float deltaTime, string txt)
|
||||
{
|
||||
if (deltaTime == 0)
|
||||
{
|
||||
lastChangeTime = 0;
|
||||
lastChangeIndex = 0;
|
||||
return loadingInfos[0];
|
||||
}
|
||||
|
||||
var interval = deltaTime - lastChangeTime;
|
||||
|
||||
if (interval > Text_Change_interval)
|
||||
{
|
||||
lastWaitTime = deltaTime;
|
||||
lastChangeTime = deltaTime;
|
||||
lastChangeIndex++;
|
||||
return loadingInfos[lastChangeIndex % loadingInfos.Length];
|
||||
}
|
||||
|
||||
return txt;
|
||||
}
|
||||
|
||||
//private string GetTextByDeltaTime(float deltaTime, string txt)
|
||||
//{
|
||||
//if (deltaTime == 0)
|
||||
//{
|
||||
//lastChangeTime = 0;
|
||||
//lastChangeIndex = 0;
|
||||
//return loadingInfos[0];
|
||||
//}
|
||||
|
||||
//var interval = deltaTime - lastChangeTime;
|
||||
|
||||
//if (interval > Text_Change_interval)
|
||||
//{
|
||||
//lastWaitTime = deltaTime;
|
||||
//lastChangeTime = deltaTime;
|
||||
//lastChangeIndex++;
|
||||
//return loadingInfos[lastChangeIndex % loadingInfos.Length];
|
||||
//}
|
||||
|
||||
//interval = deltaTime - lastWaitTime;
|
||||
//if (interval > Text_Wait_Interval)
|
||||
//{
|
||||
//var info = loadingInfos[lastChangeIndex % loadingInfos.Length];
|
||||
//lastWaitTime = deltaTime;
|
||||
//if(txt.Length - info.Length >= 4)
|
||||
//return info;
|
||||
//return txt + ".";
|
||||
//}
|
||||
//return txt;
|
||||
//}
|
||||
|
||||
private void StopLoadingInfo()
|
||||
{
|
||||
isUpdatingLoadingInfo = false;
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
StopLoadingInfo();
|
||||
loginLifetime?.Cancel();
|
||||
loginLifetime?.Dispose();
|
||||
loginLifetime = null;
|
||||
if(SubBoostrap.RuntimeSelection?.IsKV==true) userService.Logout();
|
||||
}
|
||||
|
||||
|
||||
//protected async void LoadingAutoInc(CancellationToken token, float total)
|
||||
//{
|
||||
//float progress = 0.01f;
|
||||
//string msg = LocalizationMgr.GetRandomText();
|
||||
//LoadingScreen.ShowProgressBar();
|
||||
//LoadingScreen.SetProgress(progress);
|
||||
//LoadingScreen.SetLoadingInfo(msg);
|
||||
//string msg_mem = msg;
|
||||
//try
|
||||
//{
|
||||
//float timer=0;
|
||||
//float innertimer=0;
|
||||
//float innerTimer2 = 0;
|
||||
//while(progress < 1)
|
||||
//{
|
||||
//timer+=(Time.deltaTime+0.1f);
|
||||
//innertimer+=(Time.deltaTime+0.1f);
|
||||
//innerTimer2+=(Time.deltaTime+0.1f);
|
||||
//if (token.IsCancellationRequested)
|
||||
//break;
|
||||
////每0.3秒给原信息后加一个.
|
||||
|
||||
|
||||
//if(innertimer>0.3f){
|
||||
//msg = "<space=0.376em>"+msg+".";
|
||||
//LoadingScreen.SetLoadingInfo(msg);
|
||||
//innertimer=0;
|
||||
//}
|
||||
//if(innerTimer2>1.2f){
|
||||
//msg=msg_mem;
|
||||
//LoadingScreen.SetLoadingInfo(msg);
|
||||
//innerTimer2=0;
|
||||
//innertimer=0;
|
||||
//}
|
||||
//if(timer>2.4f){
|
||||
//msg = LocalizationMgr.GetRandomText();
|
||||
//msg_mem = msg;
|
||||
//LoadingScreen.SetLoadingInfo(msg);
|
||||
//timer=0;
|
||||
//innertimer = 0;
|
||||
//innerTimer2 = 0;
|
||||
//}
|
||||
//progress = Mathf.Min(progress + 0.01f, total);
|
||||
//LoadingScreen.SetProgress(progress);
|
||||
//await Task.Delay(100);
|
||||
//}
|
||||
//}
|
||||
//catch (System.Exception)
|
||||
//{
|
||||
//LoadingScreen.SetProgress(0);
|
||||
//LoadingScreen.HideProgressBar();
|
||||
//LoadingScreen.SetLoadingInfo(string.Empty);
|
||||
//}
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using Game;
|
||||
using GameCore;
|
||||
using UnityEngine;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public class QuitAct : AGameAct
|
||||
{
|
||||
[Inject]
|
||||
public IConfig config { get; set; }
|
||||
|
||||
[Inject]
|
||||
public IRTService rtService { get; set; }
|
||||
|
||||
[Inject]
|
||||
public IUserService userService { get; set; }
|
||||
|
||||
[Inject]
|
||||
public SoundMgr soundMgr { get; set; }
|
||||
|
||||
[Inject]
|
||||
public IAdsService adsService { get; set; }
|
||||
|
||||
#if AGG
|
||||
[Inject]
|
||||
public HeartBeat heartBeat {get; set;}
|
||||
#endif
|
||||
|
||||
protected override void OnDestroy() { }
|
||||
|
||||
public string[] DestoryRoot;
|
||||
|
||||
public override async Task<bool> StartAsync()
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) _=game.PlayerDataStorage.PlayerKVGameplayLoop.CloseAsync();
|
||||
#endif
|
||||
if(userService is PlayerDataStorage.IKVUserLifecycle closingKVUser) _=closingKVUser.CloseKVForValidationAsync();
|
||||
// Invalidate identity before any await can allow old login/HTTP callbacks to resume.
|
||||
if (QuitStage.Logout)
|
||||
{
|
||||
QuitStage.Logout = false;
|
||||
try { userService.Logout(); }
|
||||
catch (System.Exception ex) { Debug.LogException(ex); }
|
||||
}
|
||||
LoadingScreen.Hide();
|
||||
GameObject.Destroy(UIManager.Instance.gameObject);
|
||||
|
||||
config.Rest();
|
||||
|
||||
try { await rtService.ReleaseAsync(); }
|
||||
catch (System.Exception ex) { Debug.LogException(ex); }
|
||||
|
||||
// Observation is bounded; the process registry still owns any unfinished close.
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) await PlayerDataStorage.PlayerKVShutdownRegistry.Process.ObserveAsync(5000);
|
||||
else
|
||||
#endif
|
||||
if(userService is PlayerDataStorage.IKVUserLifecycle kvUser) await kvUser.ObserveKVQuitAsync();
|
||||
|
||||
adsService.Release();
|
||||
|
||||
GameEventMgr.Instance.Clear();
|
||||
|
||||
#if AGG
|
||||
heartBeat?.Dispose();
|
||||
#endif
|
||||
GameCore.Timer.CancelAllRegisteredTimers();
|
||||
GameCore.Timer.Destroy();
|
||||
|
||||
soundMgr.Release();
|
||||
RedPointManager.Instance.Init();
|
||||
return await base.StartAsync();
|
||||
}
|
||||
|
||||
protected override void OnStarted()
|
||||
{
|
||||
RootCtx.DestoryRootCtx();
|
||||
for (int i = 0; i < DestoryRoot.Length; i++)
|
||||
{
|
||||
GameObject go = GameObject.Find(DestoryRoot[i]);
|
||||
if (go != null)
|
||||
{
|
||||
GameObject.DestroyImmediate(go);
|
||||
}
|
||||
}
|
||||
base.OnStarted();
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if (game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled)
|
||||
{
|
||||
var status = PlayerDataStorage.PlayerKVShutdownRegistry.Process.Status;
|
||||
Debug.Log("KV loop ended; restart Unity before another run. Shutdown: " + status);
|
||||
if (status == PlayerDataStorage.PlayerKVShutdownStatus.Ready)
|
||||
UnityEditor.EditorApplication.isPlaying = false;
|
||||
return; // Keep the process barrier; never return this run to PreBoot.
|
||||
}
|
||||
#endif
|
||||
Invoke("Unload", 2f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,459 +0,0 @@
|
||||
|
||||
using AppsFlyerSDK;
|
||||
using asap.core;
|
||||
using asap.core.common;
|
||||
using DataCenter;
|
||||
using game;
|
||||
using Game;
|
||||
using GameCore;
|
||||
using MoreMountains.NiceVibrations;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Script.RuntimeScript;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ThinkingData.Analytics;
|
||||
using TimeManager;
|
||||
using tysdk;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.Networking;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
public class SubBoostrap
|
||||
{
|
||||
internal static game.PlayerDataStorage.PlayerKVRuntimeSelection RuntimeSelection { get; private set; }
|
||||
|
||||
public static async System.Threading.Tasks.Task RegisterServices(GameObject rootCtxGo, IConfig config)
|
||||
{
|
||||
if(config==null) throw new ArgumentNullException(nameof(config));
|
||||
#if AGG
|
||||
const bool aggBuild=true;
|
||||
#else
|
||||
const bool aggBuild=false;
|
||||
#endif
|
||||
var selection=game.PlayerDataStorage.PlayerKVRuntimeSelection.Capture(
|
||||
config.Get<string>("SaveBackend","playfab"),
|
||||
config.Get<string>(GConstant.K_Event_API_URL,game.Account.S101AccountSession.DefaultApiUrl),aggBuild);
|
||||
RuntimeSelection=selection;
|
||||
var container = GContext.container;
|
||||
container.RegisterInstance(selection);
|
||||
VibrationController vibrationController = new VibrationController();
|
||||
vibrationController.Init();
|
||||
container.RegisterInstance(vibrationController);
|
||||
container.Register<IObjectPoolService, ObjectPoolService>().AsSingleton();
|
||||
|
||||
|
||||
#if AGG && !UNITY_EDITOR //AIHelp init
|
||||
//string appKey = "TUYOOGAME_app_a6e73dd23da84d5ab72a5f68e4a58271";
|
||||
string domain = "tuyoogame.aihelp.net";
|
||||
#if UNITY_ANDROID
|
||||
string appId = "tuyoogame_platform_7a6a236be774b7a82c23d3e59f7d1476";
|
||||
#elif UNITY_IOS
|
||||
string appId = "tuyoogame_platform_782757bd1f7ba24498cd9df0d0b9f214";
|
||||
#endif
|
||||
|
||||
AIHelp.AIHelpSupport.enableLogging(false);
|
||||
AIHelp.AIHelpSupport.AdditionalSupportFor(AIHelp.PublishCountryOrRegion.IN);
|
||||
|
||||
AIHelp.AIHelpSupport.RegisterAsyncEventListener(AIHelp.EventType.Initialization, (jsonEventData, ack)
|
||||
=>Debug.Log($"[HotUpateBoostrap] AIHelpInitialized {JObject.Parse(jsonEventData).ToString()}"));
|
||||
|
||||
AIHelp.AIHelpSupport.Initialize(domain, appId);
|
||||
|
||||
#endif //AIHelp init end
|
||||
|
||||
#if AGG
|
||||
var heartBeat = new HeartBeat();
|
||||
#endif
|
||||
using (container.InjectionScope())
|
||||
{
|
||||
#if AGG
|
||||
container.RegisterInstance(heartBeat);
|
||||
#endif
|
||||
container.Register<ISoundService, SoundService>().AsSingleton();
|
||||
container.Register<ILoadResourceService, LoadResourceService>().AsSingleton();
|
||||
container.Register<SoundMgr>().AsSingleton();
|
||||
container.Register<TileNewsManager>().AsSingleton();
|
||||
}
|
||||
|
||||
#if AGG
|
||||
heartBeat.Init();
|
||||
#endif
|
||||
|
||||
/*
|
||||
* _____ ___ ____ ___
|
||||
*|_ _/ _ \| _ \ / _ \
|
||||
* | || | | | | | | | | |
|
||||
* | || |_| | |_| | |_| |
|
||||
* |_| \___/|____/ \___/
|
||||
*/
|
||||
// TODO Refactor all managers
|
||||
#if UNITY_EDITOR
|
||||
UnityEngine.Application.runInBackground = true;
|
||||
GameCore.GameEventMgr.Init(true);
|
||||
#else
|
||||
GameCore.GameEventMgr.Init();
|
||||
#endif
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
game.PlayerDataStorage.PlayerKVGameplayLoop.BeforeBootstrap();
|
||||
#endif
|
||||
if(!selection.IsKV) rootCtxGo.AddComponent<PlayFabMgr>();
|
||||
// TODO Replace DataCenter with seprated data class
|
||||
// Create();
|
||||
// -----------------------------------------
|
||||
await Awaiters.NextFrame;
|
||||
GameCore.Timer.Create();
|
||||
//-----------------------TODO--------------------------
|
||||
Debug.Log("[HotUpateBoostrap] RegisterServices Done");
|
||||
await Awaiters.NextFrame;
|
||||
}
|
||||
|
||||
public static async System.Threading.Tasks.Task AfterAssetUpdate(IConfig config, Action<float> progressAction)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
var tables = await cfg.Tables.InitEditor(progress => progressAction(0.16f + progress * 0.02f));
|
||||
#else
|
||||
var tables = await cfg.Tables.Init(progress => progressAction(0.16f + progress * 0.02f));
|
||||
#endif
|
||||
var container = GContext.container;
|
||||
GContext.container.RegisterInstance(tables);
|
||||
GContext.container.Register<IFaceUIService, FaceUIService>().AsSingleton();
|
||||
GContext.container.Register<IUIService, UIService>().AsSingleton();
|
||||
GContext.container.Register<IChatService, ChatService>().AsSingleton();
|
||||
GContext.container.Register<IReportService, ReportService>().AsSingleton();
|
||||
GContext.container.Register<ILocalNotificationService, LocalNotificationService>().AsSingleton();
|
||||
GContext.container.Register<ICustomServerMgr, CustomServerMgr>().AsSingleton();
|
||||
GContext.container.Register<IInGameMailService, InGameMailService>().AsSingleton();
|
||||
GContext.container.Register<IRTService, SignalRService>().AsSingleton();
|
||||
GContext.container.RegisterInstance(new AvatarService());
|
||||
|
||||
var settings = new DefaultSettingService(RuntimeSelection?.IsKV == true);
|
||||
settings.Init(tables);
|
||||
GContext.container.RegisterInstance<ISettingService>(settings);
|
||||
GameCore.LocalizationMgr.Init();
|
||||
QualityManager.AutoSetQualityLevel();
|
||||
RootCtx.showFPS = !string.IsNullOrEmpty(config.Get(GConstant.K_FPS, string.Empty));
|
||||
DebugFunc.Init();
|
||||
|
||||
zzwater.URPHelper.RendererFeatureSetActive(false, "FSBlur");
|
||||
zzwater.URPHelper.RendererFeatureSetActive(false, "FSWave");
|
||||
|
||||
await InitVibration();
|
||||
//#if UNITY_ANDROID
|
||||
//RequestAndriodPermission();
|
||||
//#endif
|
||||
await Awaiters.NextFrame;
|
||||
|
||||
await GContext.container.Resolve<ILocalNotificationService>().Init();
|
||||
|
||||
IAdsService adsService;
|
||||
|
||||
if (Application.isEditor)
|
||||
{
|
||||
adsService = new DevAdsService();
|
||||
}
|
||||
else
|
||||
{
|
||||
adsService = new AggAdsService(settings);
|
||||
}
|
||||
adsService.Init();
|
||||
GContext.container.RegisterInstance<IAdsService>(adsService);
|
||||
|
||||
Debug.Log("[HotUpateBoostrap] AfterAssetUpdate Done");
|
||||
|
||||
}
|
||||
|
||||
public static async System.Threading.Tasks.Task ShowLoadingScreen(IConfig config, GameObject rootCtxGo)
|
||||
{
|
||||
var fxClick = await Addressables.InstantiateAsync("TopUIFX").Task;
|
||||
if (fxClick != null)
|
||||
{
|
||||
fxClick.name = "TopUIFX";
|
||||
GameObject.DontDestroyOnLoad(fxClick);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("TopUIFX is null");
|
||||
}
|
||||
|
||||
// TODO replace with new UI service
|
||||
await GameCore.UIManager.Init(
|
||||
UITypes.UIRoot, GameCore.ScreenType.PORTRAIT,
|
||||
() => MessageDialog.ShowErrorDialog(-20, "Default Waiting Timeout")
|
||||
);
|
||||
|
||||
var uiCam = rootCtxGo.FindChildGameObject("UICamera").GetComponent<Camera>();
|
||||
var ui3DCam = rootCtxGo.FindChildGameObject("UI3DCamera").GetComponent<Camera>();
|
||||
UIManager.SetUICamera(uiCam, ui3DCam);
|
||||
|
||||
//Camera.main.SetCamStack();
|
||||
await LoadingScreen.Show(0.19f);
|
||||
|
||||
|
||||
//LoadingScreen.SetVersionInfo($"Ver {Application.version}");
|
||||
var reVer = VersionTool.FromString(config.Get(GConstant.K_Ver, string.Empty));
|
||||
reVer.Save();
|
||||
LoadingScreen.SetVersionInfo(reVer.ToVerString());
|
||||
|
||||
Debug.Log("[HotUpateBoostrap] ShowLoadingScreen Done");
|
||||
}
|
||||
|
||||
private static async Task InitVibration()
|
||||
{
|
||||
var data = await Addressables.LoadAssetAsync<TextAsset>("vibrationdata").Task;
|
||||
if (null != data)
|
||||
{
|
||||
var jsonData = JObject.Parse(data.text);
|
||||
if (jsonData.ContainsKey("Soft"))
|
||||
{
|
||||
var soft = jsonData["Soft"];
|
||||
MMVibrationManager.SoftDuration = soft["duration"].Value<long>();
|
||||
MMVibrationManager.SoftAmplitude = soft["amplitude"].Value<int>();
|
||||
}
|
||||
if (jsonData.ContainsKey("Rigid"))
|
||||
{
|
||||
var rigid = jsonData["Rigid"];
|
||||
MMVibrationManager.RigidDuration = rigid["duration"].Value<long>();
|
||||
MMVibrationManager.RigidAmplitude = rigid["amplitude"].Value<int>();
|
||||
}
|
||||
if (jsonData.ContainsKey("Light"))
|
||||
{
|
||||
var light = jsonData["Light"];
|
||||
MMVibrationManager.LightDuration = light["duration"].Value<long>();
|
||||
MMVibrationManager.LightAmplitude = light["amplitude"].Value<int>();
|
||||
}
|
||||
if (jsonData.ContainsKey("Heavy"))
|
||||
{
|
||||
var heavy = jsonData["Heavy"];
|
||||
MMVibrationManager.LightDuration = heavy["duration"].Value<long>();
|
||||
MMVibrationManager.LightAmplitude = heavy["amplitude"].Value<int>();
|
||||
}
|
||||
if (jsonData.ContainsKey("Medium"))
|
||||
{
|
||||
var medium = jsonData["Medium"];
|
||||
MMVibrationManager.LightDuration = medium["duration"].Value<long>();
|
||||
MMVibrationManager.LightAmplitude = medium["amplitude"].Value<int>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#if UNITY_ANDROID
|
||||
//private static void RequestAndriodPermission()
|
||||
//{
|
||||
//if(!UnityEngine.Android.Permission.HasUserAuthorizedPermission("android.permission.QUERY_ALL_PACKAGES"))
|
||||
//{
|
||||
//UnityEngine.Android.Permission.RequestUserPermission("android.permission.QUERY_ALL_PACKAGES");
|
||||
//}
|
||||
//}
|
||||
//#endif
|
||||
}
|
||||
|
||||
// public static class CameraExtension
|
||||
// {
|
||||
// public static void SetCamStack(this Camera cam)
|
||||
// {
|
||||
// if (cam != Camera.main) return;
|
||||
// // For URP rendering, add UICamera into main scene camera's stack
|
||||
// //
|
||||
// Camera ui_cam = null;
|
||||
//
|
||||
// if (GameCore.UIManager.Instance != null)
|
||||
// {
|
||||
// ui_cam = GameCore.UIManager.Instance.UICamera;
|
||||
// }
|
||||
//
|
||||
// if (ui_cam != null && cam != ui_cam)
|
||||
// {
|
||||
// var cam_stack = cam.GetUniversalAdditionalCameraData().cameraStack;
|
||||
//
|
||||
// if (!cam_stack.Contains(ui_cam))
|
||||
// {
|
||||
// cam_stack.Add(ui_cam);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public class InternalEditorBoost
|
||||
{
|
||||
public static async Task Boost()
|
||||
{
|
||||
UnityEngine.Debug.Log("<color=orange>Editor Internal Boot</color>");
|
||||
GContext.container.Register<IUIService, UIService>().AsSingleton();
|
||||
|
||||
await GameCore.UIManager.Init(
|
||||
UITypes.UIRoot, GameCore.ScreenType.PORTRAIT,
|
||||
() => MessageDialog.ShowErrorDialog(-20, "Default Waiting Timeout")
|
||||
);
|
||||
var ieb = new InternalEditorBoost();
|
||||
await ieb.LoadConfig();
|
||||
await ieb.Init();
|
||||
}
|
||||
|
||||
private async Task LoadConfig()
|
||||
{
|
||||
var config = GContext.container.Resolve<IConfig>();
|
||||
// Load Debug Config
|
||||
var debugConfigPath = Path.Combine(Application.persistentDataPath, GConstant.V_Debug_Config_Path);
|
||||
string remoteConfigUrl = null;
|
||||
SimpleJSON.JSONNode debugConfig = null;
|
||||
|
||||
if (File.Exists(debugConfigPath))
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"<color=yellow>Debug config [{debugConfigPath}] in use</color>");
|
||||
#endif
|
||||
debugConfig = SimpleJSON.JSON.Parse(File.ReadAllText(debugConfigPath));
|
||||
if (debugConfig.HasKey(GConstant.K_Static_Res_URL))
|
||||
{
|
||||
remoteConfigUrl = debugConfig[GConstant.K_Static_Res_URL];
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"<color=yellow>Debug Static Res URL => {remoteConfigUrl}</color>");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
var staticResURL = GConstant.V_Static_Res_URL;
|
||||
if (ChannelManager.IsRustoreStore)
|
||||
staticResURL = GConstant.V_Static_Res_URL_RU;
|
||||
|
||||
// load remote Config
|
||||
remoteConfigUrl = remoteConfigUrl ?? string.Format(staticResURL, VersionTool.PackVer);
|
||||
|
||||
var request = UnityWebRequest.Get($"{remoteConfigUrl}/{GConstant.V_Remote_Config_Path}");
|
||||
await request.SendWebRequest();
|
||||
if (request.result != UnityWebRequest.Result.Success)
|
||||
throw new Exception($"Load Config Error: {request.error}");
|
||||
|
||||
string remoteConfigStr = request.downloadHandler.text;
|
||||
remoteConfigStr = Decrypt(remoteConfigStr);
|
||||
var remoteConfig = SimpleJSON.JSON.Parse(remoteConfigStr);
|
||||
// Merge Configs
|
||||
foreach (var element in remoteConfig)
|
||||
{
|
||||
config.Set(element.Key, element.Value.Value);
|
||||
}
|
||||
|
||||
if (null != debugConfig)
|
||||
{
|
||||
foreach (var element in debugConfig)
|
||||
{
|
||||
config.Set(element.Key, element.Value.Value);
|
||||
}
|
||||
}
|
||||
|
||||
// Change Addressable remote location, using custom assetbundle server
|
||||
var customAddressableRemoteURL = config.Get(GConstant.K_Custom_Addressable, string.Empty);
|
||||
var originAddressableRemoteURL = config.Get(GConstant.K_Origin_Addressable, string.Empty);
|
||||
if (!string.IsNullOrEmpty(customAddressableRemoteURL)
|
||||
&& !string.IsNullOrEmpty(originAddressableRemoteURL))
|
||||
{
|
||||
var ctx = (RootCtx)typeof(GContext).GetField("root", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).GetValue(null);
|
||||
|
||||
ctx.ChangeAddressableRemoteUrl(customAddressableRemoteURL, originAddressableRemoteURL);
|
||||
}
|
||||
|
||||
}
|
||||
string RTMKey = "tbambooz";
|
||||
public string Decrypt(string text)
|
||||
{
|
||||
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
|
||||
des.Key = Encoding.ASCII.GetBytes(RTMKey);
|
||||
des.IV = Encoding.ASCII.GetBytes(RTMKey);
|
||||
|
||||
|
||||
ICryptoTransform decryptor = des.CreateDecryptor(des.Key, des.IV);
|
||||
byte[] inputBytes = Convert.FromBase64String(text);
|
||||
byte[] outputBytes = decryptor.TransformFinalBlock(inputBytes, 0, inputBytes.Length);
|
||||
|
||||
return Encoding.UTF8.GetString(outputBytes);
|
||||
}
|
||||
private async Task Init()
|
||||
{
|
||||
var selection = GContext.container.Resolve<game.PlayerDataStorage.PlayerKVRuntimeSelection>();
|
||||
if (selection == null)
|
||||
{
|
||||
var startupConfig = GContext.container.Resolve<IConfig>();
|
||||
var backend = startupConfig?.Get<string>("SaveBackend", "playfab");
|
||||
if (!string.Equals(backend, "playfab", StringComparison.Ordinal))
|
||||
throw new InvalidOperationException("Editor direct scene boot only supports playfab; KV requires RegisterServices.");
|
||||
// Editor direct scene boot is PF-only; KV and kv-loop must use RegisterServices.
|
||||
selection = game.PlayerDataStorage.PlayerKVRuntimeSelection.Capture("playfab", null, false);
|
||||
GContext.container.RegisterInstance(selection);
|
||||
if (!ReferenceEquals(GContext.container.Resolve<game.PlayerDataStorage.PlayerKVRuntimeSelection>(), selection))
|
||||
throw new InvalidOperationException("Editor backend selection registration failed.");
|
||||
}
|
||||
if (selection.IsKV)
|
||||
throw new InvalidOperationException("Editor direct scene boot cannot reuse a KV selection.");
|
||||
var tables = await cfg.Tables.Init(x => { Debug.Log(x); });
|
||||
GContext.container.RegisterInstance(tables);
|
||||
GContext.container.Register<IFaceUIService, FaceUIService>().AsSingleton();
|
||||
GContext.container.Register<IUIService, UIService>().AsSingleton();
|
||||
GContext.container.Register<ICustomServerMgr, CustomServerMgr>().AsSingleton();
|
||||
GContext.container.Register<IChatService, ChatService>().AsSingleton();
|
||||
GContext.container.Register<ILocalNotificationService, LocalNotificationService>().AsSingleton();
|
||||
GContext.container.Register<IReportService, ReportService>().AsSingleton();
|
||||
GContext.container.Register<ModuleManager.ModuleManager>().AsSingleton();
|
||||
GContext.container.Register<IInGameMailService, InGameMailService>().AsSingleton();
|
||||
|
||||
using (var scope = GContext.container.InjectionScope())
|
||||
{
|
||||
GContext.container.Register<game.PlayerDataStorage.IPlayerSaveStore, game.PlayerDataStorage.PlayFabPlayerSaveStore>().AsSingleton();
|
||||
// IAdsService 在 AfterAssetUpdate 中手动注册并调用 Init(),此处不再重复注册
|
||||
GContext.container.Register<PlayerFishData>().AsSingleton();
|
||||
GContext.container.Register<PlayerItemData>().AsSingleton();
|
||||
GContext.container.Register<CampDataMM>().AsSingleton();
|
||||
GContext.container.Register<FishingEventData>().AsSingleton();
|
||||
GContext.container.Register<SigninData>().AsSingleton();
|
||||
//GContext.container.Register<TaskData>().AsSingleton();
|
||||
GContext.container.Register<ClubService>().AsSingleton();
|
||||
GContext.container.Register<LeadboardData>().AsSingleton();
|
||||
GContext.container.Register<PlayerShopData>().AsSingleton();
|
||||
GContext.container.Register<TriggerPackData>().AsSingleton();
|
||||
GContext.container.Register<GuideDataCenter>().AsSingleton();
|
||||
GContext.container.Register<SmallGameData>().AsSingleton();
|
||||
GContext.container.Register<PlayerData>().AsSingleton();
|
||||
GContext.container.Register<SoundMgr>().AsSingleton();
|
||||
GContext.container.Register<AvatarService>().AsSingleton();
|
||||
GContext.container.Register<BuffDataCenter>().AsSingleton();
|
||||
GContext.container.Register<FishingBoxData>().AsSingleton();
|
||||
GContext.container.Register<FishingBoxDataProvier>().AsSingleton();
|
||||
// GContext.container.Register<FishingChallengeCenter>().AsSingleton();
|
||||
GContext.container.Register<FishingChallengeManager>().AsSingleton();
|
||||
GContext.container.Register<OfferChainsChestManager>().AsSingleton();
|
||||
GContext.container.Register<OfferDiscoPartyManager>().AsSingleton();
|
||||
GContext.container.Register<BattlePassDataProvider>().AsSingleton();
|
||||
GContext.container.Register<MiniBattlePassDataProvider>().AsSingleton();
|
||||
// GContext.container.Register<OfferRushDataProvider>().AsSingleton();
|
||||
GContext.container.Register<BattlePassData>().AsSingleton();
|
||||
GContext.container.Register<EventPackData>().AsSingleton();
|
||||
GContext.container.Register<AchievementDataManager>().AsSingleton();
|
||||
//GContext.container.Register<PokerScoreData>().AsSingleton();
|
||||
//GContext.container.Register<RedeemCodeService>().AsSingleton();
|
||||
GContext.container.Register<PiggyBankPackData>().AsSingleton();
|
||||
GContext.container.Register<BargainPackData>().AsSingleton();
|
||||
GContext.container.Register<DiggingGameManager>().AsSingleton();
|
||||
GContext.container.Register<EventPartnerData>().AsSingleton();
|
||||
GContext.container.Register<EventPinataPackDataManager>().AsSingleton();
|
||||
GContext.container.Register<Assets.Scripts.UI.OfferSweep.OfferSweepManager>().AsSingleton();
|
||||
GContext.container.Register<Assets.Scripts.UI.AdWheel.AdWheelManager>().AsSingleton();
|
||||
GContext.container.Register<Assets.Scripts.UI.JigsawPuzzle.JigsawPuzzleManager>().AsSingleton();
|
||||
|
||||
GContext.container.Register<AntiCheatManager>().AsSingleton();
|
||||
GContext.container.Register<Activity.ActivityManager>().AsSingleton();
|
||||
GContext.container.Register<Activity.ActivityScopeManager>().AsSingleton();
|
||||
GContext.container.Register<TimeRefreshManager>().AsSingleton();
|
||||
}
|
||||
var settings = new DefaultSettingService(selection.IsKV);
|
||||
settings.Init(tables);
|
||||
GContext.container.RegisterInstance<ISettingService>(settings);
|
||||
GameCore.LocalizationMgr.Init();
|
||||
await GContext.container.Resolve<ILocalNotificationService>().Init();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,164 +0,0 @@
|
||||
using System;
|
||||
using asap.core;
|
||||
using game;
|
||||
using GameCore;
|
||||
using TimeManager.Event;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TimeManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 时间刷新管理器 - 检测跨天、跨小时并触发刷新。须在启动链中显式 <c>Resolve<TimeRefreshManager>()</c>(见 ActivityManager.Init)。
|
||||
/// </summary>
|
||||
public class TimeRefreshManager : IDisposable
|
||||
{
|
||||
public TimeRefreshManager(ITimeTickService tickService, IUserService userService)
|
||||
{
|
||||
_tickService = tickService;
|
||||
_userService = userService;
|
||||
|
||||
_sessionStartTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
_lastCheckedHour = _sessionStartTime.Hour;
|
||||
InitialRefreshSucceeded = CheckLastLogin();
|
||||
_tickService.MinuteTick
|
||||
.Subscribe(_ =>
|
||||
{
|
||||
CheckSessionCrossDay();
|
||||
CheckSessionCrossHour();
|
||||
})
|
||||
.AddTo(_disposables);
|
||||
}
|
||||
|
||||
private readonly ITimeTickService _tickService;
|
||||
private readonly IUserService _userService;
|
||||
public bool InitialRefreshSucceeded { get; }
|
||||
|
||||
private int _lastCheckedDayOfYear = -1;
|
||||
private int _lastCheckedHour = -1;
|
||||
private DateTime _sessionStartTime;
|
||||
private CompositeDisposable _disposables = new();
|
||||
|
||||
#region Check登录时间
|
||||
|
||||
private bool CheckLastLogin()
|
||||
{
|
||||
if (IsLastLoginToday()) return true;
|
||||
Debug.Log("上次登录不是今天");
|
||||
return OnCrossDayRefresh();
|
||||
}
|
||||
private void CheckSessionCrossDay()
|
||||
{
|
||||
if (!IsSessionCrossDay()) return;
|
||||
OnCrossDayRefresh();
|
||||
_sessionStartTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
}
|
||||
|
||||
private void CheckSessionCrossHour()
|
||||
{
|
||||
if (!IsSessionCrossHour()) return;
|
||||
OnCrossHourRefresh();
|
||||
var now = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
_lastCheckedHour = now.Hour;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region 登录时间相关
|
||||
|
||||
/// <summary>
|
||||
/// 判断上次登录是否是今天
|
||||
/// 从 UserService 获取 PlayFab 记录的真实上次登录时间
|
||||
/// </summary>
|
||||
public bool IsLastLoginToday()
|
||||
{
|
||||
var lastLoginTime = GetLastLoginTime();
|
||||
if (!lastLoginTime.HasValue)
|
||||
{
|
||||
return false; // 从未登录过
|
||||
}
|
||||
|
||||
var now = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
return lastLoginTime.Value.DayOfYear == now.DayOfYear &&
|
||||
lastLoginTime.Value.Year == now.Year;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断当前会话是否跨天(从登录到现在的过程中是否跨过了午夜)
|
||||
/// </summary>
|
||||
public bool IsSessionCrossDay()
|
||||
{
|
||||
var now = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
return _sessionStartTime.DayOfYear != now.DayOfYear ||
|
||||
_sessionStartTime.Year != now.Year;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断当前会话是否跨小时(从上次检查到现在是否跨过了整点)
|
||||
/// </summary>
|
||||
public bool IsSessionCrossHour()
|
||||
{
|
||||
var now = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
return now.Hour != _lastCheckedHour;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取上次登录时间(从 UserService 获取)
|
||||
/// </summary>
|
||||
public DateTime? GetLastLoginTime()
|
||||
{
|
||||
var value = _userService?.LastLoginTime;
|
||||
return value == DateTime.MinValue ? null : value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取会话开始时间(本次游戏启动时间)
|
||||
/// </summary>
|
||||
public DateTime GetSessionStartTime()
|
||||
{
|
||||
return _sessionStartTime;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 跨天刷新逻辑
|
||||
/// </summary>
|
||||
private bool OnCrossDayRefresh()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1. 发送活动跨天刷新事件
|
||||
GContext.Publish(new ActivityCrossDayRefreshEvent());
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[TimeRefreshManager] Error during cross day refresh: {e.Message}\n{e.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 跨小时刷新逻辑
|
||||
/// </summary>
|
||||
private void OnCrossHourRefresh()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 发送跨小时刷新事件
|
||||
GContext.Publish(new ActivityCrossHourRefreshEvent());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[TimeRefreshManager] Error during cross hour refresh: {e.Message}\n{e.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_disposables?.Dispose();
|
||||
_disposables = null;
|
||||
Debug.Log("[TimeRefreshManager] Disposed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,300 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using UI.Wheel;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Assets.Scripts.UI.AdWheel
|
||||
{
|
||||
public class AdWheelManager:IDisposable
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
|
||||
public AdWheelManager()
|
||||
{
|
||||
GContext.OnEvent<TargetEvent>().Subscribe(OnEvent_TargetEvent).AddTo(Disposables);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_disposableSimple?.Dispose();
|
||||
Disposables?.Dispose();
|
||||
}
|
||||
|
||||
private CompositeDisposable _disposable;
|
||||
private CompositeDisposable Disposables => _disposable ??= new CompositeDisposable();
|
||||
#region event
|
||||
private void OnEvent_TargetEvent(TargetEvent targetEvent)
|
||||
{
|
||||
if (targetEvent.type == 2 && targetEvent.subType == 4)
|
||||
{
|
||||
//只有一个广告转盘
|
||||
RefreshData(targetEvent.id);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region data
|
||||
[Inject]
|
||||
public Tables Tables { get; set; }
|
||||
private AdWheelData _adWheelData;
|
||||
public AdWheelData Data => _adWheelData ??= new AdWheelData(Tables);
|
||||
private void RefreshData(int eventId)
|
||||
{
|
||||
//判断开启时间
|
||||
var fishingEvent = Tables.TbFishingEvent.GetOrDefault(eventId);
|
||||
if (fishingEvent == null) return;
|
||||
var daily = fishingEvent.TimeDefinition as Daily;
|
||||
var now = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
var refreshTime = new DateTimeOffset(now).ToUnixTimeSeconds() + (daily.EndTime - (long)now.TimeOfDay.TotalSeconds);
|
||||
var cacheData = GetCachedData();
|
||||
if (cacheData != null && cacheData.ActiveEventTime > 0)
|
||||
{
|
||||
this.Data.SetData(cacheData);
|
||||
if (this.Data.EventRefreshTimeSeconds != refreshTime)
|
||||
{
|
||||
this.Data.EventRefreshTimeSeconds = refreshTime;
|
||||
this.Data.ClearProgress();
|
||||
SaveData();
|
||||
}
|
||||
if (this.Data.EventID != eventId)
|
||||
{
|
||||
this.Data.EventID = eventId;
|
||||
SaveData();
|
||||
}
|
||||
SetRedPoint();
|
||||
CheckIsAddGiftFaceUI();
|
||||
return;
|
||||
}
|
||||
|
||||
bool isOpenEvent = IsOpenEvent(eventId);
|
||||
if (!isOpenEvent) return;
|
||||
|
||||
InitData(eventId, now, refreshTime);
|
||||
SaveData();
|
||||
}
|
||||
|
||||
private void InitData(int eventId, DateTime now, long refreshTime)
|
||||
{
|
||||
var nowTimeSeconds = new DateTimeOffset(now).ToUnixTimeSeconds();
|
||||
this.Data.EventID = eventId;
|
||||
this.Data.lastViewAdTime = 0;
|
||||
this.Data.ActiveEventTime = nowTimeSeconds;
|
||||
this.Data.EventRefreshTimeSeconds = refreshTime;
|
||||
this.Data.ClearProgress();
|
||||
CheckIsAddGiftFaceUI();
|
||||
}
|
||||
|
||||
private void CheckIsAddGiftFaceUI()
|
||||
{
|
||||
if(Data.IsActive&&GetRedPointState())
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUI(Data.EventID, UITypes.EventBonusWheelPopupPanel, 0);
|
||||
}
|
||||
|
||||
private AdWheelData GetCachedData()
|
||||
{
|
||||
var dataStr = saveStore.Read(nameof(AdWheelData)).ValueOrThrow();
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd AdWheelData LoadData sData {dataStr}");
|
||||
#endif
|
||||
return dataStr == null ? null : JsonConvert.DeserializeObject<AdWheelData>(dataStr) ?? throw new JsonSerializationException("Null AdWheelData save.");
|
||||
}
|
||||
|
||||
private bool IsOpenEvent(int eventId)
|
||||
{
|
||||
var eventConfig = Tables.TbFishingEvent.GetOrDefault(eventId);
|
||||
if (eventConfig == null) return true;
|
||||
var isOpen = GContext.container.Resolve<FishingEventData>()?.Condition(eventConfig) ?? false;
|
||||
return isOpen;
|
||||
}
|
||||
public void SaveData()
|
||||
{
|
||||
SetRedPoint();
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd AdWheelData SaveData {JsonConvert.SerializeObject(Data)}");
|
||||
#endif
|
||||
saveStore.Write(nameof(AdWheelData), JsonConvert.SerializeObject(Data)).RequireAccepted();
|
||||
}
|
||||
|
||||
private List<IWheelReward> _rewards = new List<IWheelReward>();
|
||||
public List<IWheelReward> GetEventAdWheelRewards()
|
||||
{
|
||||
if (Data == null) return null;
|
||||
|
||||
if (_rewards == null)
|
||||
_rewards = new List<IWheelReward>();
|
||||
else
|
||||
_rewards.Clear();
|
||||
var adWheelMainID = Data.EventConfig.RedirectID;
|
||||
if (adWheelMainID <= 0) return null;
|
||||
|
||||
var adWheelMain = Data.AdWheelMain;
|
||||
if (adWheelMain == null) return null;
|
||||
|
||||
foreach (var id in adWheelMain.WheelRewardList)
|
||||
{
|
||||
var rewardConfig = Tables.TbEventAdWheelReward.Get(id);
|
||||
if (rewardConfig == null
|
||||
|| _rewards.Find(x => x.Index == rewardConfig.Index) != null)
|
||||
continue;
|
||||
_rewards.Add(new EventAdWheelRewardAdapter(rewardConfig,0));
|
||||
|
||||
}
|
||||
CalculateWeights();
|
||||
return _rewards;
|
||||
}
|
||||
|
||||
// 缓存数据
|
||||
private int totalWeight = 0;
|
||||
private List<int> weightRanges = new List<int>();
|
||||
|
||||
private void CalculateWeights()
|
||||
{
|
||||
totalWeight = _rewards.Sum(r => r.Weight);
|
||||
BuildWeightRanges();
|
||||
}
|
||||
private void BuildWeightRanges()
|
||||
{
|
||||
weightRanges.Clear();
|
||||
int currentWeight = 0;
|
||||
|
||||
foreach (var reward in _rewards)
|
||||
{
|
||||
currentWeight += reward.Weight;
|
||||
weightRanges.Add(currentWeight);
|
||||
}
|
||||
}
|
||||
|
||||
// 根据权重随机获取奖励
|
||||
public int GetRandomRewardIndex()
|
||||
{
|
||||
if (totalWeight <= 0)
|
||||
{
|
||||
Debug.LogError("总权重为0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int randomValue = UnityEngine.Random.Range(0, totalWeight);
|
||||
|
||||
for (int i = 0; i < weightRanges.Count; i++)
|
||||
{
|
||||
if (randomValue < weightRanges[i])
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return _rewards.Count - 1;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static List<ItemData> GetProgressRewards(AdWheelData adWheelData)
|
||||
{
|
||||
if (adWheelData == null) return null;
|
||||
|
||||
var adWheelMainID = adWheelData.EventConfig.RedirectID;
|
||||
if (adWheelMainID <= 0) return null;
|
||||
|
||||
var adWheelMain = adWheelData.AdWheelMain;
|
||||
if (adWheelMain == null) return null;
|
||||
|
||||
List<ItemData> rewards = new List<ItemData>();
|
||||
foreach (var dropID in adWheelMain.ProgressRewardDropIDList)
|
||||
{
|
||||
var itemData = GContext.container.Resolve<PlayerItemData>().GetItemDataOne(dropID);
|
||||
if (itemData != null)
|
||||
rewards.Add(itemData);
|
||||
}
|
||||
return rewards;
|
||||
}
|
||||
public static bool GetProgressRewardByProgress(AdWheelData adWheelData, out int dropID)
|
||||
{
|
||||
dropID = -1;
|
||||
if (adWheelData == null) return false;
|
||||
|
||||
var adWheelMainID = adWheelData.EventConfig.RedirectID;
|
||||
if (adWheelMainID <= 0) return false;
|
||||
|
||||
var adWheelMain = adWheelData.AdWheelMain;
|
||||
if (adWheelMain == null) return false;
|
||||
|
||||
if (adWheelMain.ProgressRewardADTimeList == null)
|
||||
{
|
||||
Debug.LogError($"AdWheelManager GetProgressRewardByProgress adWheelMain {adWheelMain.ID} ProgressRewardADTimeList is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
var index = adWheelMain.ProgressRewardADTimeList.IndexOf(adWheelData.Count);
|
||||
if (index == -1) return false;
|
||||
|
||||
if (adWheelMain.ProgressRewardDropIDList == null)
|
||||
{
|
||||
Debug.LogError($"AdWheelManager GetProgressRewardByProgress adWheelMain {adWheelMain.ID} ProgressRewardDropIDList is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index > adWheelMain.ProgressRewardDropIDList.Count - 1)
|
||||
{
|
||||
Debug.LogError($"AdWheelManager GetProgressRewardByProgress adWheelMain {adWheelMain.ID} index error index:{index}");
|
||||
return false;
|
||||
}
|
||||
dropID = adWheelMain.ProgressRewardDropIDList[index];
|
||||
return true;
|
||||
}
|
||||
public bool IsOpen(FishingEventData data, FishingEvent fishingEvent,
|
||||
Func<EventCondition,bool> getEventCondition,
|
||||
Func<int, bool> Condition_NoRechargeSeconds)
|
||||
{
|
||||
//开过一次一直开
|
||||
var cacheData = GetCachedData();
|
||||
if (cacheData is { ActiveEventTime: > 0 })
|
||||
return true;
|
||||
for (int i = 0; i < fishingEvent.ConditionList.Count; i++)
|
||||
{
|
||||
var condition= fishingEvent.ConditionList[i];
|
||||
if (condition.Condition == ConditionType.NoRechargeSeconds)
|
||||
{
|
||||
var mmt = GContext.container.Resolve<MMTService>();
|
||||
int time=int.Parse(condition.Param[0]);
|
||||
if (!Condition_NoRechargeSeconds(time)) return false;
|
||||
}
|
||||
else if(!getEventCondition(condition)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#region Red
|
||||
private IDisposable _disposableSimple;
|
||||
private string GetRedPointKey()
|
||||
{
|
||||
return "eventAdWheel.red";
|
||||
}
|
||||
private bool GetRedPointState()
|
||||
{
|
||||
if (Data?.AdWheelMain == null) return false;
|
||||
|
||||
var nowTime = new DateTimeOffset(ZZTimeHelper.UtcNow().UtcNowOffset()).ToUnixTimeSeconds();
|
||||
var cdTime = nowTime - Data.lastViewAdTime;
|
||||
var remainCd = Data.AdWheelMain.ADInterval - cdTime;
|
||||
var isCd = remainCd > 0;
|
||||
if (!isCd) return Data.Progerss < 1;
|
||||
_disposableSimple?.Dispose();
|
||||
_disposableSimple=Observable.Interval(TimeSpan.FromSeconds(remainCd)).Subscribe(_ =>
|
||||
{
|
||||
SetRedPoint();
|
||||
_disposableSimple?.Dispose();
|
||||
});
|
||||
return false;
|
||||
}
|
||||
private void SetRedPoint()
|
||||
{
|
||||
RedPointManager.Instance?.SetRedPointState(GetRedPointKey(), GetRedPointState());
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
using System;
|
||||
using asap.core;
|
||||
using Assets.Scripts.UI.AdWheel;
|
||||
using cfg;
|
||||
using DG.Tweening;
|
||||
using game;
|
||||
using GameCore;
|
||||
using TMPro;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
public class AdvertData
|
||||
{
|
||||
public int ID;
|
||||
public int index;
|
||||
public int day;
|
||||
public string cdTime;
|
||||
public bool isOpen;
|
||||
}
|
||||
public class AdvertPanel : MonoBehaviour
|
||||
{
|
||||
game.PlayerDataStorage.IPlayerSaveStore saveStore;
|
||||
|
||||
Button btn_close;
|
||||
TMP_Text txt_energy, txt_money, txt_ticket;
|
||||
ulong _curGold;
|
||||
int _curEnergy;
|
||||
int _curTicket;
|
||||
protected CompositeDisposable disposables = new CompositeDisposable();
|
||||
|
||||
|
||||
private Transform panel_gift1;
|
||||
private Transform panel_gift2;
|
||||
private Transform panel_gift3;
|
||||
|
||||
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
txt_ticket = transform.Find("top_area/resource/icon_ticket/text_num").GetComponent<TMP_Text>();
|
||||
txt_energy = transform.Find("top_area/resource/icon_energy/text_num").GetComponent<TMP_Text>();
|
||||
txt_money = transform.Find("top_area/resource/icon_money/text_num").GetComponent<TMP_Text>();
|
||||
btn_close = transform.Find("root/btn_close").GetComponent<Button>();
|
||||
|
||||
//
|
||||
panel_gift1 = transform.Find("root/ScrollView/Viewport/Content/Item/panel_gift1");
|
||||
panel_gift2 = transform.Find("root/ScrollView/Viewport/Content/Item/panel_gift2");
|
||||
panel_gift3 = transform.Find("root/ScrollView/Viewport/Content/Item/panel_gift3");
|
||||
|
||||
}
|
||||
private void Start()
|
||||
{
|
||||
var playerData = GContext.container.Resolve<PlayerData>();
|
||||
_curGold = playerData.gold;
|
||||
_curEnergy = playerData.Energy;
|
||||
_curTicket = GContext.container.Resolve<PlayerShopData>().GetAdticketCount();
|
||||
txt_money.text = ConvertTools.GetNumberString2(_curGold);
|
||||
txt_energy.text = ConvertTools.GetNumberString2(_curEnergy);
|
||||
txt_ticket.text = ConvertTools.GetNumberString2(_curTicket);
|
||||
btn_close.onClick.AddListener(() =>
|
||||
{
|
||||
GContext.Publish(new RestartShowHomeUIEvent());
|
||||
UIManager.Instance.DestroyUI(UITypes.AdvertPanel);
|
||||
});
|
||||
GContext.OnEvent<ResAddEvent>().Where(x => x.id == 1002).Subscribe(x =>
|
||||
{
|
||||
GoldAddAnim();
|
||||
}).AddTo(disposables);
|
||||
GContext.OnEvent<ResAddEvent>().Where(x => x.id == 1001).Subscribe(x =>
|
||||
{
|
||||
EnergyAddAnim();
|
||||
}).AddTo(disposables);
|
||||
GContext.OnEvent<ResAddEvent>().Where(x => x.id == 1006).Subscribe(x =>
|
||||
{
|
||||
TicketAddAnim();
|
||||
}).AddTo(disposables);
|
||||
|
||||
GContext.OnEvent<ResAddEvent>().Where(x => x.id == 1007).Subscribe(x =>
|
||||
{
|
||||
TicketAddAnim();
|
||||
}).AddTo(disposables);
|
||||
|
||||
// panel_gift3.gameObject.SetActive(false);
|
||||
#if UNITY_IOS || UNITY_IPHONE
|
||||
panel_gift2?.gameObject.SetActive(true);
|
||||
panel_gift3?.gameObject.SetActive(false);
|
||||
#else
|
||||
CheckTapJoyOpen();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
// 7天
|
||||
private readonly int _conditionSeconds = 604800; // 7 * 24 * 60 * 60;
|
||||
|
||||
// 判断是否满足{seconds}没有充值了
|
||||
private bool Condition_NoRechargeSeconds(int timeSeconds)
|
||||
{
|
||||
var lastPaymentStr = saveStore.Read("LastPaymentTime").ValueOrThrow();
|
||||
DateTime curTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
DateTime endTime = curTime;
|
||||
if (string.IsNullOrEmpty(lastPaymentStr) || !lastPaymentStr.Contains("__"))
|
||||
endTime = GContext.container.Resolve<IUserService>().CreateTime.UtcNowOffset();
|
||||
|
||||
var curTimeSecond = new DateTimeOffset(curTime).ToUnixTimeSeconds();
|
||||
var endTimeSecond = new DateTimeOffset(endTime).ToUnixTimeSeconds();
|
||||
if (curTimeSecond - endTimeSecond >= timeSeconds)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void CheckTapJoyOpen()
|
||||
{
|
||||
var tables = GContext.container.Resolve<Tables>();
|
||||
var tapjoyWakeParam = tables.TbGlobalConfig.TapjoyWakeParam * 24 * 60 * 60;
|
||||
var config = GContext.container.Resolve<IConfig>();
|
||||
// bool isTapJoyOpen = !(config != null && string.IsNullOrEmpty(config.Get(GConstant.K_TAPJOY, string.Empty)));
|
||||
bool isTapJoyOpen = config == null || string.IsNullOrEmpty(config.Get(GConstant.K_TAPJOY_BAN, string.Empty)) ;
|
||||
|
||||
#if UNITY_ANDROID
|
||||
if (Condition_NoRechargeSeconds(tapjoyWakeParam) && isTapJoyOpen)
|
||||
{
|
||||
panel_gift2?.gameObject.SetActive(false);
|
||||
panel_gift3?.gameObject.SetActive(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
panel_gift2?.gameObject.SetActive(true);
|
||||
panel_gift3?.gameObject.SetActive(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
//private async System.Threading.Tasks.Task WaitForRewardPanel()
|
||||
//{
|
||||
// await new WaitForSeconds(0.5f);
|
||||
// var rewardPanelTask = new RewardPopupData();
|
||||
// GContext.Publish(rewardPanelTask);
|
||||
// if (rewardPanelTask.tcs != null)
|
||||
// {
|
||||
// await rewardPanelTask.tcs.Task;
|
||||
// }
|
||||
|
||||
//}
|
||||
void GoldAddAnim()
|
||||
{
|
||||
//await WaitForRewardPanel();
|
||||
DOTween.Kill("txt_money");
|
||||
txt_money.text = ConvertTools.GetNumberString2(_curGold);
|
||||
var EndDiamond = GContext.container.Resolve<PlayerData>().gold;
|
||||
DOTween.To(() => _curGold, x => _curGold = x, EndDiamond, 1f).OnUpdate(() =>
|
||||
{
|
||||
txt_money.text = ConvertTools.GetNumberString2(_curGold);
|
||||
}).SetId("txt_money");
|
||||
}
|
||||
void EnergyAddAnim()
|
||||
{
|
||||
//await WaitForRewardPanel();
|
||||
DOTween.Kill("advert_text_energy");
|
||||
txt_energy.text = ConvertTools.GetNumberString2(_curEnergy);
|
||||
var EndEnergy = GContext.container.Resolve<PlayerData>().Energy;
|
||||
DOTween.To(() => _curEnergy, x => _curEnergy = x, EndEnergy, 1f).OnUpdate(() =>
|
||||
{
|
||||
txt_energy.text = ConvertTools.GetNumberString2(_curEnergy);
|
||||
}).SetId("advert_text_energy");
|
||||
}
|
||||
void TicketAddAnim()
|
||||
{
|
||||
//await WaitForRewardPanel();
|
||||
DOTween.Kill("text_ticket");
|
||||
txt_ticket.text = ConvertTools.GetNumberString2(_curTicket);
|
||||
var Ticket = GContext.container.Resolve<PlayerShopData>().GetAdticketCount();
|
||||
DOTween.To(() => _curTicket, x => _curTicket = x, Ticket, 1f).OnUpdate(() =>
|
||||
{
|
||||
txt_ticket.text = ConvertTools.GetNumberString2(_curTicket);
|
||||
}).SetId("text_ticket");
|
||||
}
|
||||
private void OnDestroy()
|
||||
{
|
||||
disposables.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,674 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GameCore;
|
||||
using asap.core;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using cfg;
|
||||
using IngameDebugConsole;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using game;
|
||||
using System.Linq;
|
||||
using Activity.Event;
|
||||
|
||||
public class DeBugPanel : MonoBehaviour
|
||||
{
|
||||
game.PlayerDataStorage.IPlayerSaveStore saveStore;
|
||||
|
||||
static public bool isShow = true;
|
||||
private CanvasGroup _canvasGroup;
|
||||
private Button btn_close;
|
||||
private Button root_review_closs;
|
||||
private Button btn_changjing;
|
||||
private Button btn_yugan;
|
||||
//private Button btn_sure;
|
||||
private Button btn_sure1;
|
||||
|
||||
TMP_Text text_cur_time;
|
||||
|
||||
TMP_Text text_cur_viplv;
|
||||
TMP_Text text_cur_pricelv;
|
||||
public TMP_InputField inputField_vip;
|
||||
public Button btn_vip;
|
||||
TMP_InputField inputField_Seconds;
|
||||
Button btn_Day;
|
||||
Button btn_Seconds;
|
||||
TMP_InputField inputField_Digging;
|
||||
Button btn_Digging;
|
||||
|
||||
TMP_InputField inputField_duel;
|
||||
Button btn_duel;
|
||||
private TMP_InputField inputField_yu;
|
||||
//private TMP_InputField inputField_grade;
|
||||
|
||||
TMP_InputField inputField_itemId;
|
||||
TMP_InputField inputField_itemCount;
|
||||
|
||||
Button btn_addItem;
|
||||
|
||||
//账号等级
|
||||
TMP_InputField inputField_level;
|
||||
|
||||
Button btn_level;
|
||||
|
||||
//账号等级
|
||||
TMP_InputField inputField_supplyDrop;
|
||||
|
||||
Button btn_supplyDrop;
|
||||
|
||||
//活动ID
|
||||
TMP_InputField inputField_activityId;
|
||||
Button btn_activityId;
|
||||
|
||||
//鱼竿
|
||||
public
|
||||
TMP_InputField inputField_rod;
|
||||
public
|
||||
Button btn_rodFull;
|
||||
public
|
||||
Button btn_rod_full_star;
|
||||
//鱼熟练度全升级
|
||||
Button btn_fishFull;
|
||||
//鱼熟练度全满
|
||||
Button btn_fishFull1;
|
||||
//鱼熟练度全升级
|
||||
Button btn_fishFullAll;
|
||||
Button btn_fishFullAll1;
|
||||
//图鉴全满
|
||||
Button btn_albumFull;
|
||||
Button btn_show_panel;
|
||||
|
||||
TMP_InputField inputField_guide;
|
||||
Button btn_guide;
|
||||
|
||||
TMP_InputField inputField_build;
|
||||
Button btn_build;
|
||||
|
||||
Button btn_bomb;
|
||||
TMP_InputField inputField_btn_bomb;
|
||||
|
||||
Button btn_aquarium;
|
||||
Button btn_raid;
|
||||
Button btn_bomb_game;
|
||||
|
||||
private GameObject root_review;
|
||||
private GameObject root;
|
||||
|
||||
private TMP_Text text_title;
|
||||
|
||||
private Transform content;
|
||||
|
||||
GameObject item;
|
||||
|
||||
List<DeBugItem> deBugItems = new List<DeBugItem>();
|
||||
|
||||
int _index = 0;
|
||||
Button btn_album_orientation;
|
||||
Button btn_album_orientation1;
|
||||
Button btn_console_toggle;
|
||||
public TMP_InputField inputField_mmt;
|
||||
public Button btn_mmt;
|
||||
float inflate = 0f;
|
||||
// Start is called before the first frame update
|
||||
void Awake()
|
||||
{
|
||||
saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
inputField_itemId = transform.Find("root/InputField_icon").GetComponent<TMP_InputField>();
|
||||
inputField_itemCount = transform.Find("root/InputField_icon_num").GetComponent<TMP_InputField>();
|
||||
btn_addItem = transform.Find("root/btn_sure2/btn_green").GetComponent<Button>();
|
||||
inputField_level = transform.Find("root/InputField_account_grade").GetComponent<TMP_InputField>();
|
||||
btn_level = transform.Find("root/btn_sure3/btn_green").GetComponent<Button>();
|
||||
|
||||
inputField_supplyDrop = transform.Find("root/InputField_supplyDropIndex").GetComponent<TMP_InputField>();
|
||||
btn_supplyDrop = transform.Find("root/btn_suresupplyDrop/btn_green").GetComponent<Button>();
|
||||
|
||||
inputField_activityId = transform.Find("root/InputField_event").GetComponent<TMP_InputField>();
|
||||
btn_activityId = transform.Find("root/btn_sure4/btn_green").GetComponent<Button>();
|
||||
btn_fishFull = transform.Find("root/btn_fish_upgrade1/btn_green").GetComponent<Button>();
|
||||
btn_fishFull1 = transform.Find("root/btn_fish_full1/btn_green").GetComponent<Button>();
|
||||
btn_fishFullAll = transform.Find("root/btn_fish_upgrade2/btn_green").GetComponent<Button>();
|
||||
btn_fishFullAll1 = transform.Find("root/btn_fish_full2/btn_green").GetComponent<Button>();
|
||||
btn_albumFull = transform.Find("root/btn_album_full/btn_green").GetComponent<Button>();
|
||||
btn_show_panel = transform.Find("root/btn_show_panel/btn_green").GetComponent<Button>();
|
||||
inputField_guide = transform.Find("root/InputField_guide").GetComponent<TMP_InputField>();
|
||||
btn_guide = transform.Find("root/btn_guide/btn_green").GetComponent<Button>();
|
||||
inputField_build = transform.Find("root/InputField_build").GetComponent<TMP_InputField>();
|
||||
btn_build = transform.Find("root/btn_build/btn_green").GetComponent<Button>();
|
||||
btn_bomb = transform.Find("root/btn_bomb/btn_green").GetComponent<Button>();
|
||||
inputField_btn_bomb = transform.Find("root/InputField_bomb").GetComponent<TMP_InputField>();
|
||||
|
||||
btn_aquarium = transform.Find("root/btn_aquarium/btn_green").GetComponent<Button>();
|
||||
btn_raid = transform.Find("root/btn_raid/btn_green").GetComponent<Button>();
|
||||
btn_bomb_game = transform.Find("root/btn_bomb_game/btn_green").GetComponent<Button>();
|
||||
_canvasGroup = GetComponent<CanvasGroup>();
|
||||
root = transform.Find("root").gameObject;
|
||||
btn_close = transform.Find("root/btn_close").GetComponent<Button>();
|
||||
root_review_closs = transform.Find("root_review/btn_close").GetComponent<Button>();
|
||||
btn_changjing = transform.Find("root/btn_changjing/btn_green").GetComponent<Button>();
|
||||
btn_yugan = transform.Find("root/btn_yugan/btn_green").GetComponent<Button>();
|
||||
//btn_sure = transform.Find("root/btn_sure/btn_green").GetComponent<Button>();
|
||||
btn_sure1 = transform.Find("root/btn_sure1/btn_green").GetComponent<Button>();
|
||||
inputField_yu = transform.Find("root/InputField_yu").GetComponent<TMP_InputField>();
|
||||
//inputField_grade = transform.Find("root/InputField_grade").GetComponent<TMP_InputField>();
|
||||
root_review = transform.Find("root_review").gameObject;
|
||||
text_title = transform.Find("root_review/text_title").GetComponent<TMP_Text>();
|
||||
content = transform.Find("root_review/Scroll View/Viewport/Content");
|
||||
item = transform.Find("root_review/Scroll View/Viewport/Content/Item").gameObject;
|
||||
btn_album_orientation = transform.Find("root/btn_album_orientation/btn_green").GetComponent<Button>();
|
||||
btn_album_orientation1 = transform.Find("root/btn_album_orientation1/btn_green").GetComponent<Button>();
|
||||
btn_console_toggle = transform.Find("root/Console/btn_green").GetComponent<Button>();
|
||||
SetSecondsTime();
|
||||
SetDigging();
|
||||
SetDuelTier();
|
||||
SetVIP();
|
||||
SetMMT();
|
||||
|
||||
inflate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
}
|
||||
void SetMMT()
|
||||
{
|
||||
inputField_mmt.text = GContext.container.Resolve<MMTService>().MMTStr;
|
||||
btn_mmt.onClick.AddListener(ChangeMMT);
|
||||
}
|
||||
void SetVIP()
|
||||
{
|
||||
text_cur_viplv = transform.Find("root/text_cur_viplv").GetComponent<TMP_Text>();
|
||||
text_cur_pricelv = transform.Find("root/text_cur_pricelv").GetComponent<TMP_Text>();
|
||||
btn_vip.onClick.AddListener(SetPriceLv);
|
||||
PlayerData playerData = GContext.container.Resolve<PlayerData>();
|
||||
var _tables = GContext.container.Resolve<Tables>();
|
||||
var dynamicVipParam = _tables.TbGlobalConfig.DynamicVipParam;
|
||||
int allPackPoints = (int)(playerData.vipAllScore * dynamicVipParam[0] + playerData.vipLastScore * dynamicVipParam[1]);
|
||||
text_cur_viplv.text = $"{playerData.vip}({playerData.vipAllScore})";
|
||||
text_cur_pricelv.text = $"{playerData.PriceLv}({allPackPoints})";
|
||||
}
|
||||
|
||||
void SetPriceLv()
|
||||
{
|
||||
string secStr = inputField_vip.text;
|
||||
if (int.TryParse(secStr, out int sec))
|
||||
{
|
||||
PlayerData playerData = GContext.container.Resolve<PlayerData>();
|
||||
playerData.SetPriceLevel(sec);
|
||||
var _tables = GContext.container.Resolve<Tables>();
|
||||
var dynamicVipParam = _tables.TbGlobalConfig.DynamicVipParam;
|
||||
int allPackPoints = (int)(playerData.vipAllScore * dynamicVipParam[0] + playerData.vipLastScore * dynamicVipParam[1]);
|
||||
text_cur_viplv.text = $"{playerData.vip}({playerData.vipAllScore})";
|
||||
text_cur_pricelv.text = $"{playerData.PriceLv}({allPackPoints})";
|
||||
}
|
||||
}
|
||||
|
||||
void SetSecondsTime()
|
||||
{
|
||||
text_cur_time = transform.Find("root/text_cur_time").GetComponent<TMP_Text>();
|
||||
inputField_Seconds = transform.Find("root/InputField_Seconds").GetComponent<TMP_InputField>();
|
||||
inputField_Seconds.text = ZZTimeHelper.LocalTimeOffsetInSec.ToString();
|
||||
btn_Day = transform.Find("root/btn_Day/btn_green").GetComponent<Button>();
|
||||
btn_Seconds = transform.Find("root/btn_Seconds/btn_green").GetComponent<Button>();
|
||||
|
||||
text_cur_time.text = ZZTimeHelper.UtcNow().ToString("yyyy/MM/dd HH:mm:ss");
|
||||
btn_Seconds.onClick.AddListener(OnClickSetTime);
|
||||
btn_Day.onClick.AddListener(OnClickSetTimeDay);
|
||||
}
|
||||
|
||||
void OnClickSetTime()
|
||||
{
|
||||
string secStr = inputField_Seconds.text;
|
||||
if (!string.IsNullOrEmpty(secStr))
|
||||
{
|
||||
if (int.TryParse(secStr, out int sec))
|
||||
{
|
||||
ZZTimeHelper.LocalTimeOffsetInSec = sec;
|
||||
text_cur_time.text = ZZTimeHelper.UtcNow().ToString("yyyy/MM/dd HH:mm:ss");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("输入的秒数格式不正确");
|
||||
}
|
||||
}
|
||||
}
|
||||
void OnClickSetTimeDay()
|
||||
{
|
||||
string secStr = inputField_Seconds.text;
|
||||
if (!string.IsNullOrEmpty(secStr))
|
||||
{
|
||||
if (int.TryParse(secStr, out int sec))
|
||||
{
|
||||
ZZTimeHelper.LocalTimeOffsetInSec = sec * 86400;
|
||||
text_cur_time.text = ZZTimeHelper.UtcNow().ToString("yyyy/MM/dd HH:mm:ss");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("输入的天数格式不正确");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetDigging()
|
||||
{
|
||||
inputField_Digging = transform.Find("root/InputField_Digging").GetComponent<TMP_InputField>();
|
||||
btn_Digging = transform.Find("root/btn_Digging/btn_green").GetComponent<Button>();
|
||||
btn_Digging.onClick.AddListener(OnClickDigging);
|
||||
}
|
||||
void OnClickDigging()
|
||||
{
|
||||
string indexStr = inputField_Digging.text;
|
||||
if (!string.IsNullOrEmpty(indexStr))
|
||||
{
|
||||
int.TryParse(indexStr, out int index);
|
||||
GContext.container.Resolve<Script.RuntimeScript.DiggingGameManager>().GMSetClamp(index);
|
||||
}
|
||||
}
|
||||
private void Start()
|
||||
{
|
||||
btn_close.onClick.AddListener(OnClosePanel);
|
||||
btn_changjing.onClick.AddListener(OnChangjing);
|
||||
btn_yugan.onClick.AddListener(OnYugan);
|
||||
//btn_sure.onClick.AddListener(OnSure);
|
||||
btn_sure1.onClick.AddListener(OnSure1);
|
||||
btn_addItem.onClick.AddListener(AddItem);
|
||||
btn_level.onClick.AddListener(SetLevel);
|
||||
|
||||
btn_supplyDrop.onClick.AddListener(SetSupplyDrop);
|
||||
|
||||
btn_activityId.onClick.AddListener(SetaCtivityId);
|
||||
btn_rodFull.onClick.AddListener(() =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(inputField_rod.text))
|
||||
{
|
||||
int level = Convert.ToInt32(inputField_rod.text);
|
||||
GContext.container.Resolve<PlayerFishData>().GMSetLvAll(level);
|
||||
}
|
||||
});
|
||||
btn_rod_full_star.onClick.AddListener(() =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(inputField_rod.text))
|
||||
{
|
||||
int level = Convert.ToInt32(inputField_rod.text);
|
||||
GContext.container.Resolve<PlayerFishData>().GMSetStarAll(level);
|
||||
}
|
||||
});
|
||||
btn_fishFull.onClick.AddListener(() =>
|
||||
{
|
||||
GContext.container.Resolve<PlayerFishData>().GMAddAllFishMastery();
|
||||
});
|
||||
btn_fishFull1.onClick.AddListener(() =>
|
||||
{
|
||||
GContext.container.Resolve<PlayerFishData>().GMAddAllFishMastery1();
|
||||
});
|
||||
btn_fishFullAll.onClick.AddListener(() =>
|
||||
{
|
||||
GContext.container.Resolve<PlayerFishData>().GMAddAllFishMastery(true);
|
||||
});
|
||||
btn_fishFullAll1.onClick.AddListener(() =>
|
||||
{
|
||||
GContext.container.Resolve<PlayerFishData>().GMAddAllFishMastery1(true);
|
||||
});
|
||||
btn_albumFull.onClick.AddListener(() =>
|
||||
{
|
||||
//GContext.container.Resolve<AlbumData>().GMAddAll();
|
||||
MMTService mmService = GContext.container.Resolve<MMTService>();
|
||||
mmService.CanShowHand = !mmService.CanShowHand;
|
||||
GContext.Publish(new ShowHandEvent() { isShow = mmService.CanShowHand });
|
||||
});
|
||||
btn_guide.onClick.AddListener(() =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(inputField_guide.text))
|
||||
{
|
||||
GContext.container.Resolve<GuideDataCenter>().ClearGuide(inputField_guide.text);
|
||||
OnClosePanel();
|
||||
}
|
||||
});
|
||||
btn_build.onClick.AddListener(() =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(inputField_build.text))
|
||||
{
|
||||
if (inputField_build.text.Contains("-"))
|
||||
{
|
||||
string[] strings = inputField_build.text.Split('-');
|
||||
GContext.container.Resolve<CampDataMM>().SetCampAreaIndex(strings[0]);
|
||||
GContext.container.Resolve<CampDataMM>().SetCampAreaStep(strings[1]);
|
||||
GContext.container.Resolve<CampDataMM>().UpdateCurrentData();
|
||||
}
|
||||
else
|
||||
{
|
||||
GContext.container.Resolve<CampDataMM>().ChangeCampId(int.Parse(inputField_build.text));
|
||||
}
|
||||
OnClosePanel();
|
||||
}
|
||||
});
|
||||
btn_bomb.onClick.AddListener(() =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(inputField_btn_bomb.text))
|
||||
{
|
||||
GContext.container.Resolve<SmallGameData>().GMSetBomb(inputField_btn_bomb.text);
|
||||
OnClosePanel();
|
||||
}
|
||||
});
|
||||
|
||||
root_review_closs.onClick.AddListener(() => SetRootReview(false));
|
||||
btn_show_panel.onClick.AddListener(ChangeShow);
|
||||
btn_album_orientation.onClick.AddListener(() =>
|
||||
{
|
||||
Screen.orientation = ScreenOrientation.LandscapeLeft;
|
||||
});
|
||||
btn_album_orientation1.onClick.AddListener(() =>
|
||||
{
|
||||
Screen.orientation = ScreenOrientation.Portrait;
|
||||
});
|
||||
|
||||
btn_console_toggle.onClick.AddListener(() =>
|
||||
{
|
||||
if (null == DebugLogManager.Instance)
|
||||
{
|
||||
Addressables.InstantiateAsync("IngameDebugConsole");
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(DebugLogManager.Instance.gameObject);
|
||||
}
|
||||
});
|
||||
btn_aquarium.onClick.AddListener(GMShootFishRefresh);
|
||||
btn_bomb_game.onClick.AddListener(async () =>
|
||||
{
|
||||
var eventGamePlayPanel = await UIManager.Instance.GetUIAsync(UITypes.EventGamePlayPanel);
|
||||
eventGamePlayPanel.GetComponent<EventGamePlayPanel>().StartGame(12);
|
||||
UIManager.Instance.DestroyUI(UITypes.DeBugPanel);
|
||||
});
|
||||
btn_raid.onClick.AddListener(async () =>
|
||||
{
|
||||
var eventGamePlayPanel = await UIManager.Instance.GetUIAsync(UITypes.EventGamePlayPanel);
|
||||
eventGamePlayPanel.GetComponent<EventGamePlayPanel>().StartGame(11);
|
||||
UIManager.Instance.DestroyUI(UITypes.DeBugPanel);
|
||||
});
|
||||
}
|
||||
// Update is called once per frame
|
||||
|
||||
|
||||
// public override void OnPause()
|
||||
// {
|
||||
// //当弹出新的面板的时候,让主菜单面板不再和鼠标交互
|
||||
// _canvasGroup.alpha = 0;
|
||||
// _canvasGroup.blocksRaycasts = false;
|
||||
// }
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
//当弹出新的面板的时候,让主菜单面板不再和鼠标交互
|
||||
_canvasGroup.alpha = 1;
|
||||
_canvasGroup.blocksRaycasts = true;
|
||||
|
||||
//UIManager.Instance.DestroyUI(UITypes.HomePanel);
|
||||
GContext.Publish(new HideHomePanelEvent());
|
||||
}
|
||||
|
||||
public void OnDisable()
|
||||
{
|
||||
// _canvasGroup.alpha = 0;
|
||||
// _canvasGroup.blocksRaycasts = false;
|
||||
|
||||
RestartShowHomeUIEvent restartShowHomeUIEvent = new RestartShowHomeUIEvent();
|
||||
GContext.Publish(restartShowHomeUIEvent);
|
||||
}
|
||||
|
||||
public void OnClosePanel()
|
||||
{
|
||||
UIManager.Instance.DestroyUI(UITypes.DeBugPanel);
|
||||
}
|
||||
|
||||
void SetRootReview(bool isShow, int index = 0)
|
||||
{
|
||||
_index = index;
|
||||
root_review.SetActive(isShow);
|
||||
root.SetActive(!isShow);
|
||||
}
|
||||
|
||||
void OnChangjing()
|
||||
{
|
||||
if (_index == 2)
|
||||
return;
|
||||
var list = GContext.container.Resolve<Tables>().TbMapData.DataList;
|
||||
int count = deBugItems.Count;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
deBugItems[i].gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
if (i >= count)
|
||||
{
|
||||
deBugItems.Add(Instantiate(item, content).AddComponent<DeBugItem>());
|
||||
}
|
||||
|
||||
var i1 = i;
|
||||
var map_name = LocalizationMgr.GetText(list[i].Name_l10n_key);
|
||||
deBugItems[i].deBugItemData = new DeBugItemData($"{map_name}\n{list[i].ID}",
|
||||
async () =>
|
||||
{
|
||||
ChangeMapDataAsyncEvent changeMapDataAsyncEvent = new ChangeMapDataAsyncEvent(list[i1].ID);
|
||||
GContext.Publish(changeMapDataAsyncEvent);
|
||||
await changeMapDataAsyncEvent.source.Task;
|
||||
});
|
||||
deBugItems[i].gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
SetRootReview(true, 2);
|
||||
}
|
||||
|
||||
void OnYugan()
|
||||
{
|
||||
if (_index == 1)
|
||||
return;
|
||||
var list = GContext.container.Resolve<Tables>().TbRodData.DataList;
|
||||
int count = deBugItems.Count;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
deBugItems[i].gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void OnSure1()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(inputField_yu.text))
|
||||
{
|
||||
int yu = Convert.ToInt32(inputField_yu.text);
|
||||
var fishItem = GContext.container.Resolve<Tables>().GetItemData(yu);
|
||||
if (fishItem != null && fishItem.RedirectID > 0 && fishItem.Type == 4)
|
||||
{
|
||||
GContext.container.Resolve<FishingData>().fishItem = fishItem;
|
||||
GContext.Publish(new ChangeFishDataEvent(fishItem.RedirectID));
|
||||
}
|
||||
else
|
||||
{
|
||||
ToastPanel.Show("输入的ID不是鱼");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddItem()
|
||||
{
|
||||
int.TryParse(inputField_itemCount.text, out int count);
|
||||
if (count == 0)
|
||||
{
|
||||
count = 1;
|
||||
}
|
||||
GContext.container.Resolve<PlayerItemData>().AddItemCount(Convert.ToInt32(inputField_itemId.text), count, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SetLevel()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(inputField_level.text))
|
||||
{
|
||||
int level = Convert.ToInt32(inputField_level.text);
|
||||
GContext.container.Resolve<PlayerData>().SetLv(level);
|
||||
GContext.container.Resolve<CampDataMM>().Init();
|
||||
}
|
||||
}
|
||||
|
||||
void SetSupplyDrop()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(inputField_supplyDrop.text))
|
||||
{
|
||||
int level = Convert.ToInt32(inputField_supplyDrop.text);
|
||||
GContext.container.Resolve<PlayerData>().SupplyDropIndex = level;
|
||||
}
|
||||
}
|
||||
|
||||
void SetaCtivityId()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(inputField_activityId.text))
|
||||
{
|
||||
int activityId = Convert.ToInt32(inputField_activityId.text);
|
||||
GContext.container.Resolve<FishingEventData>().GMSetEvent(activityId);
|
||||
var eventConfig=GContext.container.Resolve<Tables>()?.TbFishingEvent.GetOrDefault(activityId);
|
||||
GContext.Publish(new GMActivityOpenedEvent(eventConfig));
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeShow()
|
||||
{
|
||||
isShow = !isShow;
|
||||
CampPanel campPanel = GContext.container.Resolve<CampPanel>();
|
||||
if (campPanel != null)
|
||||
{
|
||||
campPanel.SetAlpha();
|
||||
}
|
||||
}
|
||||
|
||||
void SetDuelTier()
|
||||
{
|
||||
inputField_duel = transform.Find("root/InputField_duel").GetComponent<TMP_InputField>();
|
||||
btn_duel = transform.Find("root/btn_sure_duel/btn_green").GetComponent<Button>();
|
||||
btn_duel.onClick.AddListener(GMDuelSetTier);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// GM命令调段位
|
||||
/// </summary>
|
||||
/// <param name="initTier"></param>
|
||||
public void GMDuelSetTier()
|
||||
{
|
||||
string indexStr = inputField_duel.text;
|
||||
if (!string.IsNullOrEmpty(indexStr))
|
||||
{
|
||||
int.TryParse(indexStr, out int count);
|
||||
GContext.container.Resolve<FishingEventData>().GMAddSoloProgress(count);
|
||||
}
|
||||
}
|
||||
public void GMShootFishRefresh()
|
||||
{
|
||||
var dataSever = AquariumManager.GetAquariumManagerData();
|
||||
if (dataSever != null)
|
||||
{
|
||||
var data = dataSever;
|
||||
//新数据
|
||||
data.startTime = ZZTimeHelper.UtcNow().AddDays(-2);
|
||||
data.refreshCount = 0;
|
||||
data.adCount = 0;
|
||||
data.refreshDay = ZZTimeHelper.UtcNow().UtcNowOffset().Date.DayOfYear;
|
||||
saveStore.Write(AquariumManager.AquariumEventDataNewKey, data.Serialize()).RequireAccepted();
|
||||
}
|
||||
}
|
||||
|
||||
private void ChangeMMT()
|
||||
{
|
||||
var userService = GContext.container.Resolve<game.IUserService>() as game.PlayFabUserService;
|
||||
if (userService == null) return;
|
||||
string mmt = inputField_mmt.text;
|
||||
List<string> mmts = mmt.ToLower().Select(c => c.ToString()).ToList();
|
||||
var mmDatas = GContext.container.Resolve<Tables>().TbMMTInit.DataList;
|
||||
int value = 0;
|
||||
for (int i = 0; i < mmDatas.Count; i++)
|
||||
{
|
||||
if (mmts.Count > i && mmts[i] == "b")
|
||||
{
|
||||
value |= 1 << (mmDatas[i].ID - 1001);
|
||||
}
|
||||
}
|
||||
userService.GMMMT(value);
|
||||
}
|
||||
|
||||
private string _cfg_ver;
|
||||
private string CfgVer => _cfg_ver ??
|
||||
#if UNITY_EDITOR
|
||||
(_cfg_ver = $"Conf Ver [{System.IO.File.ReadAllText("../cfgData/conf_ver.txt").Trim()}]");
|
||||
#else
|
||||
(_cfg_ver = $"Conf Ver [{System.IO.File.ReadAllText(System.IO.Path.Combine(Application.persistentDataPath,"cfgData/conf_ver.txt")).Trim()}]");
|
||||
#endif
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
//draw cfg ver
|
||||
if (!_originFlag)
|
||||
{
|
||||
_originFlag = true;
|
||||
GetOriginalGUIStyle();
|
||||
}
|
||||
GUIStyle style = GUI.skin.label;
|
||||
style.fontSize = 80;
|
||||
style.normal.textColor = Color.magenta;
|
||||
GUI.Label(new Rect(50, 50, 2000, 100), CfgVer, style);
|
||||
GUILayout.Space(200);
|
||||
string inflateStr = $"inflate {inflate}";
|
||||
GUILayout.Label(inflateStr, style, GUILayout.Width(2000), GUILayout.Height(100));
|
||||
SetOriginalGUIStyle();
|
||||
}
|
||||
private GUIStyle _originalGUIStyle;
|
||||
private bool _originFlag = false;
|
||||
private void GetOriginalGUIStyle()
|
||||
{
|
||||
_originalGUIStyle = new GUIStyle(GUI.skin.label);
|
||||
}
|
||||
private void SetOriginalGUIStyle()
|
||||
{
|
||||
if (_originalGUIStyle == null)
|
||||
return;
|
||||
GUI.skin.GetStyle("Label").normal.textColor = _originalGUIStyle.normal.textColor;
|
||||
GUI.skin.GetStyle("Label").fontSize = _originalGUIStyle.fontSize;
|
||||
}
|
||||
}
|
||||
|
||||
class DeBugItem : MonoBehaviour
|
||||
{
|
||||
public TMP_Text text;
|
||||
public Button btn;
|
||||
public DeBugItemData deBugItemData;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
text = transform.Find("text").GetComponent<TMP_Text>();
|
||||
btn = transform.GetComponent<Button>();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
text.text = deBugItemData.str;
|
||||
btn.onClick.AddListener(() => deBugItemData.action());
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
btn.onClick.RemoveAllListeners();
|
||||
}
|
||||
}
|
||||
|
||||
class DeBugItemData
|
||||
{
|
||||
public string str;
|
||||
public Action action;
|
||||
|
||||
public DeBugItemData(string str, Action action)
|
||||
{
|
||||
this.str = str;
|
||||
this.action = action;
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using tysdk;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityNative.Sharing;
|
||||
|
||||
public class EventInviteFriends : MonoBehaviour
|
||||
{
|
||||
private IUnityNativeSharing adapter;
|
||||
|
||||
Button btn_close;
|
||||
Button btn_mask;
|
||||
Button btn_questionmark;
|
||||
Image bar;
|
||||
GameObject scale;
|
||||
TMP_Text text_num;
|
||||
RewardItemNew bigReward;
|
||||
float rewardWidth;
|
||||
RewardItemNew[] smallReward;
|
||||
Button btn_invite_link;
|
||||
Button btn_invite_fb;
|
||||
string inviteLink;
|
||||
List<Invite> invites;
|
||||
int maxNum;
|
||||
int maxIndex;
|
||||
Tables tables;
|
||||
PlayerItemData playerItemData;
|
||||
FriendService friendService;
|
||||
private void Awake()
|
||||
{
|
||||
friendService = GContext.container.Resolve<FriendService>();
|
||||
playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
tables = GContext.container.Resolve<Tables>();
|
||||
invites = tables.TbInvite.DataList;
|
||||
maxIndex = invites.Count - 1;
|
||||
maxNum = invites[maxIndex].NumInvite;
|
||||
btn_close = transform.Find("btn_close").GetComponent<Button>();
|
||||
btn_mask = transform.Find("mask").GetComponent<Button>();
|
||||
btn_questionmark = transform.Find("root/btn_questionmark").GetComponent<Button>();
|
||||
bar = transform.Find("root/bg_bar/bar").GetComponent<Image>();
|
||||
scale = transform.Find("root/bg_bar/bar/scale").gameObject;
|
||||
text_num = transform.Find("root/bg_done/text_num").GetComponent<TMP_Text>();
|
||||
bigReward = transform.Find("root/bigreward/reward").GetComponent<RewardItemNew>();
|
||||
Transform rewardRoot = transform.Find("root/reward");
|
||||
rewardWidth = rewardRoot.GetComponent<RectTransform>().rect.width;
|
||||
smallReward = rewardRoot.GetComponentsInChildren<RewardItemNew>();
|
||||
btn_invite_link = transform.Find("root/btn_invite_link/btn_green").GetComponent<Button>();
|
||||
btn_invite_fb = transform.Find("root/btn_invite_link/btn_green_fb").GetComponent<Button>();
|
||||
}
|
||||
private async void Start()
|
||||
{
|
||||
btn_close.onClick.AddListener(OnClickClose);
|
||||
btn_mask.onClick.AddListener(OnClickClose);
|
||||
btn_questionmark.onClick.AddListener(OnClickQuestionmark);
|
||||
btn_invite_link.onClick.AddListener(OnClickInviteLink);
|
||||
btn_invite_fb.onClick.AddListener(OnClickInviteFB);
|
||||
btn_invite_fb.gameObject.SetActive(!ChannelManager.IsRustoreStore);
|
||||
|
||||
int InviteRewardCount;
|
||||
try { InviteRewardCount = await friendService.OnReceiveLinkAward(); }
|
||||
catch (System.Exception e) { Debug.LogException(e); return; }
|
||||
if (this == null || !friendService.IsCurrent) return;
|
||||
|
||||
for (int i = 1; i < maxNum; i++)
|
||||
{
|
||||
var go = Instantiate(scale, bar.transform);
|
||||
go.transform.localPosition = new Vector3((i / (float)maxNum - 0.5f) * rewardWidth, 0, 0);
|
||||
}
|
||||
for (int i = 0; i < maxIndex; i++)
|
||||
{
|
||||
if (i < smallReward.Length)
|
||||
{
|
||||
if (string.IsNullOrEmpty(invites[i].GiftIcon))
|
||||
{
|
||||
var dataItem = playerItemData.GetItemDataOne(invites[i].DropID);
|
||||
if (dataItem != null)
|
||||
{
|
||||
smallReward[i].SetData(dataItem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
smallReward[i].SetDropIcon(invites[i].GiftIcon, invites[i].DropID);
|
||||
}
|
||||
smallReward[i].SetReceived(i < InviteRewardCount);
|
||||
smallReward[i].transform.localPosition =
|
||||
new Vector3((invites[i].NumInvite / (float)maxNum - 0.5f) * rewardWidth, 0, 0);
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrEmpty(invites[maxIndex].GiftIcon))
|
||||
{
|
||||
var dataItem = playerItemData.GetItemDataOne(invites[maxIndex].DropID);
|
||||
if (dataItem != null)
|
||||
{
|
||||
bigReward.SetData(dataItem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bigReward.SetDropIcon(invites[maxIndex].GiftIcon, invites[maxIndex].DropID);
|
||||
}
|
||||
bigReward.SetReceived(maxIndex < InviteRewardCount);
|
||||
int InviteNumber = friendService.InviteLinkNumber;
|
||||
if (InviteNumber > maxNum)
|
||||
{
|
||||
InviteNumber = maxNum;
|
||||
}
|
||||
bar.fillAmount = InviteNumber / (float)maxNum;
|
||||
text_num.text = InviteNumber + "/" + maxNum;
|
||||
GenInviteLink();
|
||||
}
|
||||
async void GenInviteLink()
|
||||
{
|
||||
inviteLink = "";
|
||||
IUserService userService = GContext.container.Resolve<IUserService>();
|
||||
|
||||
var link = await AFHelper.Instance.GenInviteLink(userService.CustomId, userService.UserId);
|
||||
if (this != null && friendService.IsCurrent) inviteLink = link;
|
||||
}
|
||||
void OnClickClose()
|
||||
{
|
||||
UIManager.Instance.DestroyUI(UITypes.FishingSocialInviteFriendsPopupPanel);
|
||||
}
|
||||
void OnClickQuestionmark()
|
||||
{
|
||||
_ = UIManager.Instance.ShowUI(UITypes.FishingSocialInviteFriendsInfoPopupPanel);
|
||||
}
|
||||
void OnClickInviteLink()
|
||||
{
|
||||
if (string.IsNullOrEmpty(inviteLink))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (adapter == null)
|
||||
{
|
||||
adapter = UnityNativeSharing.Create();
|
||||
}
|
||||
string invite = LocalizationMgr.GetFormatTextValue("Invite_Link", inviteLink);
|
||||
adapter.ShareText(invite);
|
||||
}
|
||||
void OnClickInviteFB()
|
||||
{
|
||||
if (string.IsNullOrEmpty(inviteLink))
|
||||
{
|
||||
return;
|
||||
}
|
||||
string FBInvite_title = LocalizationMgr.GetText("FBInvite_title");
|
||||
string FBInvite_content = LocalizationMgr.GetText("FBInvite_content");
|
||||
UnityBridgeFunc.FBShareLink(FBInvite_title, FBInvite_content, inviteLink);
|
||||
}
|
||||
}
|
||||
@@ -1,317 +0,0 @@
|
||||
using System;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Assets.Scripts.UI.JigsawPuzzle
|
||||
{
|
||||
public class JigsawPuzzleManager:IDisposable
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public const string PriceImgName = "separate_icon_eventjigsawpuzzle_piece_";
|
||||
|
||||
public JigsawPuzzleManager()
|
||||
{
|
||||
GContext.OnEvent<TargetEvent>().Subscribe(OnEvent_TargetEvent).AddTo(Disposables);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Disposables?.Dispose();
|
||||
_rewards?.Clear();
|
||||
}
|
||||
|
||||
private CompositeDisposable _disposable;
|
||||
private CompositeDisposable Disposables => _disposable ??= new CompositeDisposable();
|
||||
|
||||
#region event
|
||||
|
||||
private void OnEvent_TargetEvent(TargetEvent targetEvent)
|
||||
{
|
||||
if (targetEvent.type == 4 && targetEvent.subType == 9)
|
||||
{
|
||||
RefreshData(targetEvent.id);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion event
|
||||
|
||||
#region data
|
||||
|
||||
[Inject]
|
||||
public Tables Tables { get; set; }
|
||||
|
||||
private JigsawPuzzleData _jigsawPuzzleData;
|
||||
public JigsawPuzzleData Data => _jigsawPuzzleData ??= new JigsawPuzzleData(Tables);
|
||||
|
||||
private void RefreshData(int eventId)
|
||||
{
|
||||
//判断周开启时间
|
||||
FishingEvent fishingEvent = Tables.TbFishingEvent.GetOrDefault(eventId);
|
||||
if (fishingEvent == null) return;
|
||||
var CacheData = GetCacheData();
|
||||
if (CacheData != null && CacheData.EventID == eventId)
|
||||
{
|
||||
this.Data.SetData(CacheData);
|
||||
SetRedPoint();
|
||||
return;
|
||||
}
|
||||
InitData(eventId);
|
||||
SaveData();
|
||||
}
|
||||
private void InitData(int eventId)
|
||||
{
|
||||
this.Data.EventID = eventId;
|
||||
Data.CompletedRoundCount = 0;
|
||||
Data.PieceRepeatAmount = 0;
|
||||
this.Data.RefreshCache();
|
||||
Data.FixedInitList?.Clear();
|
||||
this.Data.InflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
var welcomeGift= GContext.container.Resolve<FishingEventData>().GetInitWelcomeGift(eventId);
|
||||
Debug.Log($"jsd InitData {welcomeGift}");
|
||||
AddToken(welcomeGift);
|
||||
}
|
||||
private JigsawPuzzleData GetCacheData()
|
||||
{
|
||||
string dataStr = saveStore.Read(nameof(JigsawPuzzleData)).ValueOrThrow();
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd JigsawPuzzleData LoadData sData {dataStr}");
|
||||
#endif
|
||||
if (dataStr == null)
|
||||
return null;
|
||||
|
||||
return JsonConvert.DeserializeObject<JigsawPuzzleData>(dataStr) ?? throw new JsonSerializationException("Null JigsawPuzzleData save.");
|
||||
}
|
||||
public void SaveData()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd JigsawPuzzleData SaveData {JsonConvert.SerializeObject(Data)}");
|
||||
#endif
|
||||
saveStore.Write(nameof(JigsawPuzzleData), JsonConvert.SerializeObject(Data)).RequireAccepted();
|
||||
}
|
||||
|
||||
private List<EventJigsawPuzzleReward> _rewards = new List<EventJigsawPuzzleReward>();
|
||||
|
||||
public List<EventJigsawPuzzleReward> GetEventRewards()
|
||||
{
|
||||
if (Data == null) return null;
|
||||
|
||||
if (_rewards == null)
|
||||
_rewards = new List<EventJigsawPuzzleReward>();
|
||||
else
|
||||
_rewards.Clear();
|
||||
|
||||
var round = Data.RoundConfig;
|
||||
if (round == null) return null;
|
||||
|
||||
foreach (var id in round.PuzzleRewardList)
|
||||
{
|
||||
var rewardConfig = Tables.TbEventJigsawPuzzleReward.Get(id);
|
||||
if (rewardConfig == null) continue;
|
||||
if (_rewards.Contains(rewardConfig)) continue;
|
||||
_rewards.Add(rewardConfig);
|
||||
}
|
||||
return _rewards;
|
||||
}
|
||||
|
||||
public EventJigsawPuzzleReward GetRandomReward()
|
||||
{
|
||||
if (Data.FixedInitList.Count < Data.EventMain.FixedInitList.Count)
|
||||
{
|
||||
var rewardID = Data.EventMain.FixedInitList[Data.FixedInitList.Count];
|
||||
if (rewardID != 0)
|
||||
{
|
||||
Data.FixedInitList.Add(rewardID);
|
||||
SaveData();
|
||||
var reward = _rewards.Find(x => x.Index == rewardID);
|
||||
if(reward==null)
|
||||
Debug.LogError($"get fixed reward rewardID:{rewardID} reward is null");
|
||||
return reward;
|
||||
}
|
||||
}
|
||||
|
||||
var progressConfig = GetProgressConfig(Data.CompletedPicCount);
|
||||
if (progressConfig == null)
|
||||
{
|
||||
Debug.LogError("progressConfig error");
|
||||
return null;
|
||||
}
|
||||
|
||||
bool isPuzzlePiece = UnityEngine.Random.Range(0, 101) < progressConfig.NotPieceProb * 100;
|
||||
|
||||
if (isPuzzlePiece)
|
||||
return GetRandomItemRewardIndex();
|
||||
|
||||
bool isRepeatPuzzle = UnityEngine.Random.Range(0, 101) < progressConfig.RepeatProb * 100;
|
||||
if (isRepeatPuzzle)
|
||||
return GetRandomRepeatPuzzleRewardIndex();
|
||||
return GetRandomNewPuzzleRewardIndex();
|
||||
}
|
||||
|
||||
private EventJigsawPuzzleExProb GetProgressConfig(int completedCount)
|
||||
{
|
||||
foreach (var probId in Data.RoundConfig.ProbList)
|
||||
{
|
||||
var config = Tables.TbEventJigsawPuzzleExProb.GetOrDefault(probId);
|
||||
if (config == null)
|
||||
{
|
||||
Debug.LogError($"配置错误 probId:{probId} 没有对应的config ");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config.CompletedPicCount == completedCount)
|
||||
return config;
|
||||
}
|
||||
Debug.LogError($"RoundConfig {Data.RoundConfig.ID} 没有完成这个 {completedCount}数量 的奖励 配置错误");
|
||||
return null;
|
||||
}
|
||||
|
||||
private EventJigsawPuzzleReward GetRandomRepeatPuzzleRewardIndex()
|
||||
{
|
||||
var puzzleRewards = _rewards.Where(x => x.ItemDropID == 0).ToList();
|
||||
var ownedPuzzles = puzzleRewards.Where(r => Data.ProgressPairs.ContainsKey(r.PictureID)
|
||||
&& Data.ProgressPairs[r.PictureID].Count == 6)
|
||||
.ToList();
|
||||
|
||||
if (ownedPuzzles.Count == 0)
|
||||
return GetWeightedRandomReward(puzzleRewards);
|
||||
|
||||
return GetWeightedRandomReward(ownedPuzzles);
|
||||
}
|
||||
|
||||
private EventJigsawPuzzleReward GetRandomNewPuzzleRewardIndex()
|
||||
{
|
||||
var puzzleRewards = _rewards.Where(x => x.ItemDropID == 0).ToList();
|
||||
var newPuzzles = puzzleRewards.Where(r => Data.ProgressPairs.ContainsKey(r.PictureID)
|
||||
&& Data.ProgressPairs[r.PictureID].Count < 6).ToList();
|
||||
|
||||
if (newPuzzles.Count == 0)
|
||||
return GetRandomRepeatPuzzleRewardIndex();
|
||||
|
||||
return GetWeightedRandomReward(newPuzzles);
|
||||
}
|
||||
|
||||
private EventJigsawPuzzleReward GetRandomItemRewardIndex()
|
||||
{
|
||||
var itemRewards = _rewards.Where(r => r.ItemDropID > 0).ToList();
|
||||
|
||||
if (itemRewards.Count == 0)
|
||||
return GetWeightedRandomReward(_rewards);
|
||||
|
||||
return GetWeightedRandomReward(itemRewards);
|
||||
}
|
||||
|
||||
private EventJigsawPuzzleReward GetWeightedRandomReward(List<EventJigsawPuzzleReward> availableRewards)
|
||||
{
|
||||
if (availableRewards.Count == 0)
|
||||
{
|
||||
Debug.LogError($"get random reward error {availableRewards.Count} ");
|
||||
return null;
|
||||
}
|
||||
|
||||
int totalWeight = availableRewards.Sum(r => r.Weight);
|
||||
|
||||
if (totalWeight <= 0)
|
||||
{
|
||||
Debug.LogError("总权重为0");
|
||||
return null;
|
||||
}
|
||||
|
||||
int randomValue = UnityEngine.Random.Range(0, totalWeight);
|
||||
int currentWeight = 0;
|
||||
|
||||
for (int i = 0; i < availableRewards.Count; i++)
|
||||
{
|
||||
currentWeight += availableRewards[i].Weight;
|
||||
if (randomValue < currentWeight)
|
||||
{
|
||||
return availableRewards[i];
|
||||
}
|
||||
}
|
||||
|
||||
return availableRewards.LastOrDefault();
|
||||
}
|
||||
|
||||
#endregion data
|
||||
|
||||
#region config
|
||||
|
||||
public int GetRewardDropID(int pictureID)
|
||||
{
|
||||
var index = Data.RoundConfig.PictureList.IndexOf(pictureID);
|
||||
if (index >= 0 && index < Data.RoundConfig.PictureRewardList.Count)
|
||||
return Data.RoundConfig.PictureRewardList[index];
|
||||
Debug.LogError($"{this.GetType().Name} 配置错误 puzzlePicture is null PictureID {pictureID}");
|
||||
return 0;
|
||||
}
|
||||
public int GetPictureIDByQuality(int quality)
|
||||
{
|
||||
var index = quality - 1;
|
||||
if(index >= Data.RoundConfig.PictureList.Count|| index < 0)
|
||||
return -1;
|
||||
return Data.RoundConfig.PictureList[index];
|
||||
}
|
||||
public string GetPictureNameByQuality(int quality)
|
||||
{
|
||||
return Tables.TbEventJigsawPuzzlePicture.GetOrDefault(GetPictureIDByQuality(quality))?.PictureName;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region token
|
||||
|
||||
public void AddToken(int count)
|
||||
{
|
||||
var currentCount = GetTokenCount();
|
||||
if (Data == null) return;
|
||||
var totalCount = currentCount + count;
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(Data.EventID, totalCount);
|
||||
SetRedPoint();
|
||||
}
|
||||
|
||||
public void RemoveToken(int count)
|
||||
{
|
||||
if (Data == null) return;
|
||||
var eventData = GContext.container.Resolve<FishingEventData>();
|
||||
if (eventData == null) return;
|
||||
var current = eventData.GetTransitionDataCount(Data.EventID);
|
||||
var resCount = Mathf.Max(0, current - count);
|
||||
eventData.SaveTransitionData(Data.EventID, resCount);
|
||||
SetRedPoint();
|
||||
}
|
||||
|
||||
public int GetTokenCount()
|
||||
{
|
||||
if (Data == null) return 0;
|
||||
var eventData = GContext.container.Resolve<FishingEventData>();
|
||||
return eventData?.GetTransitionDataCount(Data.EventID) ?? 0;
|
||||
}
|
||||
|
||||
public bool IsCompleteAllPuzzle()
|
||||
{
|
||||
return Data.CompletedPicCount >= Data.RoundConfig.PictureList.Count;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region Red
|
||||
|
||||
private string GetRedPointKey()
|
||||
{
|
||||
return "eventJigsawPuzzle.red";
|
||||
}
|
||||
private bool GetRedPointState()
|
||||
{
|
||||
return GetTokenCount()>0;
|
||||
}
|
||||
private void SetRedPoint()
|
||||
{
|
||||
RedPointManager.Instance?.SetRedPointState(GetRedPointKey(), GetRedPointState(),GetTokenCount());
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,303 +0,0 @@
|
||||
using System.Text;
|
||||
using GameCore;
|
||||
using cfg;
|
||||
using asap.core;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
|
||||
namespace OfferStoryChain
|
||||
{
|
||||
public class Manager
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
#region RedPoint
|
||||
public bool DoNeedRedPoint
|
||||
{
|
||||
get
|
||||
{
|
||||
var packIdList = GetPackIdList();
|
||||
if (_roundCount >= packIdList.Count)
|
||||
return false;
|
||||
var packList = GetPackList(packIdList[_roundCount]);
|
||||
if (_progress < 0 || _progress >= packList.Count)
|
||||
return false;
|
||||
return packList[_progress].IAPID == 0;
|
||||
}
|
||||
}
|
||||
|
||||
public const string RedPointKey = "offerstorychain";
|
||||
#endregion
|
||||
|
||||
|
||||
#region DataStorage
|
||||
private int _eventId, _progress, _vipOnTrigger, _roundCount;
|
||||
public int EventId => _eventId;
|
||||
public int Progress => _progress;
|
||||
public int VipLevel => _vipOnTrigger;
|
||||
public int RoundCount => _roundCount;
|
||||
public bool IsActive
|
||||
{
|
||||
get
|
||||
{
|
||||
(var startTime, var endTime) = FtUtils.GetEventTimeLimit(_eventId);
|
||||
var isWithinTime = ZZTimeHelper.UtcNow() >= startTime && ZZTimeHelper.UtcNow() <= endTime;
|
||||
var isOnGoing = _roundCount < GetMaxRoundCount();
|
||||
return isWithinTime && isOnGoing;
|
||||
}
|
||||
}
|
||||
|
||||
private const string PlayFabKey = "OfferStoryChain";
|
||||
private PlayerItemData _playerItemData => GContext.container.Resolve<PlayerItemData>();
|
||||
|
||||
public static void InitEvent(cfg.FishingEvent e)
|
||||
{
|
||||
var manager = GContext.container.Resolve<Manager>();
|
||||
if (manager == null)
|
||||
{
|
||||
GContext.container.Register<Manager>().AsSingleton();
|
||||
manager = GContext.container.Resolve<Manager>();
|
||||
}
|
||||
var loadRes = manager.Load();
|
||||
if (!loadRes || manager.EventId != e.ID)
|
||||
{
|
||||
manager.Init(e.ID);
|
||||
manager.Save();
|
||||
}
|
||||
UITypes.OfferStoryChainPanel.SetType(manager.GetUiAddressableUrl());
|
||||
}
|
||||
|
||||
public void Init(int eventId)
|
||||
{
|
||||
_eventId = eventId;
|
||||
_progress = 0;
|
||||
_redirectId = _tables.TbFishingEvent[_eventId].RedirectID;
|
||||
_vipOnTrigger = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
_roundCount = 0;
|
||||
}
|
||||
|
||||
public void InitTest()
|
||||
{
|
||||
_eventId = 114514;
|
||||
_progress = 0;
|
||||
_redirectId = 92101001;
|
||||
_vipOnTrigger = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
_roundCount = 0;
|
||||
}
|
||||
|
||||
public bool Load()
|
||||
{
|
||||
var s = saveStore.Read(PlayFabKey).ValueOrThrow();
|
||||
if (string.IsNullOrEmpty(s))
|
||||
{
|
||||
Debug.LogWarning("[OfferStoryChain] No pf data found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var parts = s.Split(',');
|
||||
if (parts.Length != 4)
|
||||
{
|
||||
Debug.LogWarning($"[OfferStoryChain] String format error. Expect 4, but got {parts.Length}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!int.TryParse(parts[0], out int eventId)
|
||||
|| !int.TryParse(parts[1], out int progress)
|
||||
|| !int.TryParse(parts[2], out int vipLevel)
|
||||
|| !int.TryParse(parts[3], out int roundCount))
|
||||
{
|
||||
Debug.LogWarning($"[OfferStoryChain] Parsing error.");
|
||||
return false;
|
||||
}
|
||||
|
||||
_eventId = eventId;
|
||||
_progress = progress;
|
||||
_vipOnTrigger = vipLevel;
|
||||
_redirectId = _tables.TbFishingEvent[_eventId].RedirectID;
|
||||
_roundCount = roundCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat("{0},{1},{2},{3}", _eventId, _progress, _vipOnTrigger, _roundCount);
|
||||
saveStore.Write(PlayFabKey, sb.ToString()).RequireAccepted();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_progress = 0;
|
||||
_roundCount = 0;
|
||||
Save();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Table Content
|
||||
private readonly Tables _tables = GContext.container.Resolve<Tables>();
|
||||
private int _redirectId;
|
||||
private EventOfferStoryChain TableMain => _tables.TbEventOfferStoryChain[_redirectId];
|
||||
private TbEventPackManager TablePacks => _tables.TbEventPackManager;
|
||||
|
||||
private List<int> GetPackIdList()
|
||||
{
|
||||
var vip = Mathf.Clamp(_vipOnTrigger, 0, TableMain.VipOfferList.Count - 1);
|
||||
return TableMain.VipOfferList[vip];
|
||||
}
|
||||
|
||||
private List<Pack> GetPackList(int packId)
|
||||
{
|
||||
return TablePacks[packId].VIPPackList[0].Select(p => _tables.TbPack[p]).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Max progress in one round.
|
||||
/// Progress more than this will either trigger next round or end this game.
|
||||
/// -1 because the last pack is the final reward.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int GetMaxProgressInRound()
|
||||
{
|
||||
return GetPackList(GetPackIdList()[0]).Count - 1;
|
||||
}
|
||||
|
||||
private int GetMaxRoundCount()
|
||||
{
|
||||
var vip = Mathf.Clamp(_vipOnTrigger, 0, TableMain.VipOfferList.Count - 1);
|
||||
return TableMain.VipOfferList[vip].Count;
|
||||
}
|
||||
|
||||
public int GetFinalRewardDropId(int roundCount)
|
||||
{
|
||||
roundCount = Mathf.Clamp(roundCount, 0, GetMaxRoundCount() - 1);
|
||||
var packIdList = GetPackIdList();
|
||||
var packList = GetPackList(packIdList[roundCount]);
|
||||
return packList[^1].DropID;
|
||||
}
|
||||
|
||||
public ScrollerItemViewData[] GetScrollerSlotDataArray(int roundCount)
|
||||
{
|
||||
var packIdList = GetPackIdList();
|
||||
var packList = GetPackList(packIdList[roundCount]);
|
||||
return Enumerable.Range(0, GetMaxProgressInRound())
|
||||
.Select(i => new ScrollerItemViewData
|
||||
{
|
||||
Index = i,
|
||||
EventId = _eventId,
|
||||
DropId = packList[i].DropID,
|
||||
IapId = packList[i].IAPID,
|
||||
State = GetStateFromIndex(i)
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region System
|
||||
|
||||
private OfferStoryChain.ScrollerItemViewState GetStateFromIndex(int idx)
|
||||
{
|
||||
if (idx < _progress)
|
||||
return OfferStoryChain.ScrollerItemViewState.Bought;
|
||||
if (idx == _progress)
|
||||
return OfferStoryChain.ScrollerItemViewState.Available;
|
||||
return OfferStoryChain.ScrollerItemViewState.Locked;
|
||||
|
||||
}
|
||||
public IEventAggregator EventAggregator = new EventAggregator();
|
||||
|
||||
public MainPanelData GetMainPanelData()
|
||||
{
|
||||
var res = new MainPanelData();
|
||||
res.Progress = _progress;
|
||||
res.Reward = _playerItemData.GetItemDataByDropIdLureInflation(null, GetFinalRewardDropId(_roundCount))[0];
|
||||
res.ScrollerItemViewDataList = GetScrollerSlotDataArray(_roundCount);
|
||||
res.SpineBaseName = TableMain.Spine;
|
||||
return res;
|
||||
}
|
||||
|
||||
public List<ItemData> GrantReward(int dropId)
|
||||
{
|
||||
var rewards = _playerItemData.AddItemByDropLureInflation(null, dropId, null);
|
||||
MoveToNextSlotAndTryGrantFinalReward();
|
||||
return rewards;
|
||||
}
|
||||
|
||||
public async System.Threading.Tasks.Task<(bool res, List<ItemData> rewards)> GrantReward(int dropId, IAPItemList iapItemList, int eventId)
|
||||
{
|
||||
var rewards = _playerItemData.GetItemDataByDropIdLureInflation(null, dropId);
|
||||
var shopBuyTypeData = new ShopBuyTypeData { type = ShopBuyType.EventPack, ID = eventId };
|
||||
bool res = await GContext.container.Resolve<PlayerShopData>()
|
||||
.OnBuy(dropId, shopBuyTypeData, iapItemList, rewards);
|
||||
return (res, rewards);
|
||||
}
|
||||
|
||||
public void MoveToNextSlotAndTryGrantFinalReward()
|
||||
{
|
||||
Debug.Log($"[OfferStoryChain] Move to next slot. Current Progress: {_progress}");
|
||||
_progress++;
|
||||
if (_progress >= GetMaxProgressInRound())
|
||||
{
|
||||
var finalRewardDropId = GetFinalRewardDropId(_roundCount);
|
||||
_playerItemData.AddItemByDropLureInflation(null, finalRewardDropId, null, false);
|
||||
_progress = 0;
|
||||
_roundCount++;
|
||||
}
|
||||
Save();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI Data
|
||||
public TimerContext GetTimerData()
|
||||
{
|
||||
var res = new TimerContext();
|
||||
(res.StartTime, res.ExpiryTime) = FtUtils.GetEventTimeLimit(_eventId);
|
||||
return res;
|
||||
}
|
||||
|
||||
public string GetEntrancIconUrl()
|
||||
{
|
||||
return TableMain.Icon;
|
||||
}
|
||||
|
||||
public string GetUiAddressableUrl()
|
||||
{
|
||||
return TableMain.Addressable;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class ScrollerItemViewData
|
||||
{
|
||||
// public ItemData[] ItemDataList { get; set; }
|
||||
// public string TextPrice { get; set; }
|
||||
public int Index { get; set; }
|
||||
public int EventId { get; set; }
|
||||
public int DropId { get; set; }
|
||||
public int IapId { get; set; }
|
||||
// public string TextDiscount { get; set; }
|
||||
public ScrollerItemViewState State { get; set; }
|
||||
}
|
||||
|
||||
public enum ScrollerItemViewState
|
||||
{
|
||||
Bought,
|
||||
Locked,
|
||||
Available
|
||||
}
|
||||
|
||||
public class MainPanelData
|
||||
{
|
||||
public int Progress { get; set; }
|
||||
public ItemData Reward { get; set; }
|
||||
public ScrollerItemViewData[] ScrollerItemViewDataList { get; set; }
|
||||
public string SpineBaseName { get; set; }
|
||||
// public System.DateTime StartTime { get; set; }
|
||||
// public System.DateTime EndTime { get; set; }
|
||||
}
|
||||
|
||||
public class OfferStoryChainCloseEvent { }
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
using System;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using game;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OrderManager;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Assets.Scripts.UI.OfferSweep
|
||||
{
|
||||
public class OfferSweepManager : IDisposable,IOrderReplenishment
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public OfferSweepManager()
|
||||
{
|
||||
GContext.OnEvent<TargetEvent>().Subscribe(OnEvent_TargetEvent).AddTo(Disposables);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Disposables?.Dispose();
|
||||
}
|
||||
|
||||
private CompositeDisposable _disposable;
|
||||
private CompositeDisposable Disposables => _disposable ??= new CompositeDisposable();
|
||||
#region event
|
||||
public void OnEvent_TargetEvent(TargetEvent targetEvent)
|
||||
{
|
||||
if (targetEvent.type == 4)
|
||||
{
|
||||
RefreshData();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region data
|
||||
[Inject]
|
||||
public Tables Tables { get; set; }
|
||||
private OfferSweepPackData _sweepPackData;
|
||||
public OfferSweepPackData SweepPackData => _sweepPackData ??= new OfferSweepPackData();
|
||||
|
||||
private OfferSweepPackData GetCachedData()
|
||||
{
|
||||
var dataStr = saveStore.Read(nameof(OfferSweepPackData)).ValueOrThrow();
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd OfferSweepPackData LoadData sData {dataStr}");
|
||||
#endif
|
||||
return dataStr != null ? (JsonConvert.DeserializeObject<OfferSweepPackData>(dataStr) ?? throw new JsonSerializationException("Null sweep save.")) : new OfferSweepPackData();
|
||||
}
|
||||
|
||||
public void RefreshData()
|
||||
{
|
||||
var isResetData = IsResetData(SweepPackData, out int eventId, out int sweepPackId);
|
||||
if (isResetData)
|
||||
{
|
||||
SweepPackData.FishingEventID = eventId;
|
||||
SweepPackData.SweepPackId = sweepPackId;
|
||||
SweepPackData.StartTimeVipLevel = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
SweepPackData.PurchasePackDir?.Clear();
|
||||
SweepPackData.PurchasedCount = 0;
|
||||
SaveData();
|
||||
}
|
||||
else
|
||||
{
|
||||
var cachedData = GetCachedData();
|
||||
SweepPackData.SetData(cachedData);
|
||||
}
|
||||
|
||||
|
||||
if (!SweepPackData.IsActive) return;
|
||||
var packManager = GetOfferSweepEventPackManager();
|
||||
UITypes.OfferSweepPanel.SetType(packManager.Panel);
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUI(eventId, UITypes.OfferSweepPanel, 0);
|
||||
}
|
||||
private bool IsResetData(OfferSweepPackData offerSweepPackData, out int eventId, out int sweepPackId)
|
||||
{
|
||||
eventId = sweepPackId = 0;
|
||||
|
||||
if (offerSweepPackData == null) return true;
|
||||
|
||||
var @event = GetCurrentLoopEvent();
|
||||
if (@event == null) return true;
|
||||
eventId = @event.ID;
|
||||
|
||||
sweepPackId = GetOfferSweepPackId(@event);
|
||||
if (sweepPackId <= 0) return true;
|
||||
|
||||
var cacheData = GetCachedData();
|
||||
if (cacheData == null) return false;
|
||||
if (cacheData.FishingEventID != @event.ID) return true;
|
||||
if (cacheData.SweepPackId == 0) cacheData.SweepPackId = sweepPackId;
|
||||
|
||||
return false;
|
||||
}
|
||||
public void SaveData()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd OfferSweepPackData SaveData {JsonConvert.SerializeObject(SweepPackData)}");
|
||||
#endif
|
||||
saveStore.Write(nameof(OfferSweepPackData), JsonConvert.SerializeObject(SweepPackData)).RequireAccepted();
|
||||
}
|
||||
|
||||
public FishingEvent GetCurrentLoopEvent()
|
||||
{
|
||||
//4 循环活动
|
||||
var events = Tables.TbFishingEvent.GetAllDataByType(4);
|
||||
var fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
foreach (var e in events)
|
||||
{
|
||||
if (!EventStrategyFactory.IsRegisterEventBySubType(e.SubType))
|
||||
continue;
|
||||
var isOpen = fishingEventData.Condition(e);
|
||||
if (isOpen) return e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public EventPackManager GetOfferSweepEventPackManager(FishingEvent fishingEvent = null)
|
||||
{
|
||||
return Tables.TbEventPackManager.GetOrDefault(SweepPackData.SweepPackId);
|
||||
}
|
||||
public bool GetData(int index, out EventPackManager packManager, out Pack packConfig, out List<ItemData> itemDatas, out IAPItemList iapItem, out int packId)
|
||||
{
|
||||
packManager = null;
|
||||
packConfig = null;
|
||||
itemDatas = null;
|
||||
iapItem = null;
|
||||
packId = 0;
|
||||
|
||||
packManager = GetOfferSweepEventPackManager();
|
||||
if (packManager == null) return false;
|
||||
var VipLevel = this.SweepPackData.StartTimeVipLevel;
|
||||
if (packManager.VIPPackList == null || packManager.VIPPackList[VipLevel].Count != 7)
|
||||
{
|
||||
Debug.LogError($"配置错误 eventPackManager ID {packManager.ID} VIP {VipLevel} list count {packManager.VIPPackList[VipLevel]?.Count}");
|
||||
return false;
|
||||
}
|
||||
|
||||
packId = packManager.VIPPackList?[VipLevel]?[index] ?? 0;
|
||||
|
||||
packConfig = Tables.TbPack?.GetOrDefault(packId);
|
||||
if (packConfig == null)
|
||||
{
|
||||
Debug.LogError($"配置错误 eventPackManager id {packId}");
|
||||
return false;
|
||||
}
|
||||
|
||||
itemDatas = GContext.container.Resolve<PlayerItemData>().GetItemDataByDropIdLureInflation(null, packConfig.DropID);
|
||||
if (itemDatas == null) return false;
|
||||
|
||||
iapItem = Tables.TbIAPItemList?.GetOrDefault(packConfig.IAPID);
|
||||
if (iapItem == null) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<ItemData> GetPurchasedItemDataByIndex(int index)
|
||||
{
|
||||
if (SweepPackData == null) return null;
|
||||
if (index < 0) return null;
|
||||
if (!SweepPackData.PurchasePackDir.ContainsKey(index)) return null;
|
||||
var packId = SweepPackData.PurchasePackDir[index];
|
||||
var packConfig = Tables.TbPack?.GetOrDefault(packId);
|
||||
if (packConfig == null)
|
||||
{
|
||||
Debug.LogError($"配置错误 eventPackManager id {packId}");
|
||||
return null;
|
||||
}
|
||||
return GContext.container.Resolve<PlayerItemData>().GetItemDataByDropId(packConfig.DropID);
|
||||
}
|
||||
#endregion
|
||||
#region static method
|
||||
public static int GetOfferSweepPackId(FishingEvent fishingEvent = null)
|
||||
{
|
||||
if (fishingEvent == null)
|
||||
fishingEvent = GContext.container.Resolve<OfferSweepManager>().GetCurrentLoopEvent();
|
||||
|
||||
if (fishingEvent == null) return 0;
|
||||
|
||||
var strategy = EventStrategyFactory.GetStrategy(fishingEvent.SubType);
|
||||
var tables = GContext.container.Resolve<Tables>();
|
||||
var cycleConfig = tables.TbFishingEventCycleItem.GetOrDefault(fishingEvent.RedirectID);
|
||||
return strategy.GetSweepPackId(cycleConfig.RedirectID);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void OnReplenish(ShopBuyTypeData buyType, IAPItemList iAPItemList, List<ItemData> itemDataGift, RewardType rewardShowType,
|
||||
bool voucher = false)
|
||||
{
|
||||
if(buyType.extraPurcheData==null) return;
|
||||
if (!buyType.extraPurcheData.TryGetValue(nameof(OfferSweepPackData), out var value)) return;
|
||||
if (!buyType.extraPurcheData.TryGetValue(nameof(_sweepPackData.FishingEventID), out var fishingEventIdjToken))
|
||||
{
|
||||
Debug.LogError($"参数错误 eventPackManager id {_sweepPackData.FishingEventID}");
|
||||
return;
|
||||
}
|
||||
var eventID= fishingEventIdjToken.ToObject<int>();
|
||||
if (eventID != _sweepPackData.FishingEventID)
|
||||
{
|
||||
//todo 保底机制
|
||||
return;
|
||||
}
|
||||
|
||||
var packIDs = value.ToObject<Dictionary<int, int>>();
|
||||
if(packIDs==null) return;
|
||||
foreach (var packID in packIDs)
|
||||
{
|
||||
_sweepPackData.PurchasePackDir.TryAdd(packID.Key, packID.Value);
|
||||
}
|
||||
if (_sweepPackData.PurchasePackDir.Count == 6)
|
||||
{
|
||||
_sweepPackData.PurchasedCount++;
|
||||
}
|
||||
SaveData();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,447 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using GameCore;
|
||||
using System.Linq;
|
||||
using asap.core;
|
||||
using UnityEngine;
|
||||
using cfg;
|
||||
using System.Text;
|
||||
using game;
|
||||
|
||||
namespace OfferTreasureAscent
|
||||
{
|
||||
public class Manager
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
private int _status = 0, _eventId, _redirectId;
|
||||
private List<int> _failedPurchaseRewardIndices;
|
||||
private PlayerItemData _playerItemData => GContext.container.Resolve<PlayerItemData>();
|
||||
private cfg.Tables _tables => GContext.container.Resolve<cfg.Tables>();
|
||||
public int EventId => _eventId;
|
||||
public int RoundCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return FtUtils.PopCount(_status);
|
||||
}
|
||||
}
|
||||
|
||||
public int Status => _status;
|
||||
#region RedPoint
|
||||
public bool DoNeedRedPoint()
|
||||
{
|
||||
return GetIapId() <= 0;
|
||||
}
|
||||
|
||||
public readonly string RedPointKey = "offer_treasure_ascent";
|
||||
|
||||
#endregion
|
||||
|
||||
#region DataStorage
|
||||
|
||||
public const string PfKey = "OfferTreasureAscent";
|
||||
public static void InitEvent(FishingEvent e)
|
||||
{
|
||||
var manager = GContext.container.Resolve<Manager>();
|
||||
if (manager == null)
|
||||
{
|
||||
GContext.container.Register<Manager>().AsSingleton();
|
||||
manager = GContext.container.Resolve<Manager>();
|
||||
}
|
||||
var loadRes = manager.Load();
|
||||
if (!loadRes || e.ID != manager.EventId)
|
||||
{
|
||||
manager.Init(e.ID);
|
||||
manager.Save();
|
||||
}
|
||||
UITypes.OfferTreasureAscentMainPanel.SetType(manager.GetUiData()[0]);
|
||||
UITypes.OfferTreasureAscentBoxPanel.SetType(manager.GetUiData()[2]);
|
||||
}
|
||||
|
||||
public void InitTest()
|
||||
{
|
||||
_eventId = 90400139;
|
||||
_redirectId = 1001;
|
||||
_status = 0;
|
||||
_failedPurchaseRewardIndices = new List<int>();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_status = 0;
|
||||
_failedPurchaseRewardIndices = new List<int>();
|
||||
Save();
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat("{0},{1},", _eventId, _status);
|
||||
if (_failedPurchaseRewardIndices != null && _failedPurchaseRewardIndices.Count > 0)
|
||||
foreach (var i in _failedPurchaseRewardIndices)
|
||||
sb.AppendFormat("{0},", i);
|
||||
saveStore.Write(PfKey, sb.ToString()).RequireAccepted();
|
||||
}
|
||||
|
||||
public bool Load()
|
||||
{
|
||||
try
|
||||
{
|
||||
var s = saveStore.Read(PfKey).ValueOrThrow();
|
||||
if (s == null || s == "")
|
||||
return false;
|
||||
var tokens = s.Trim(',').Split(',');
|
||||
if (tokens.Length < 2)
|
||||
return false;
|
||||
_eventId = int.Parse(tokens[0]);
|
||||
_status = int.Parse(tokens[1]);
|
||||
_failedPurchaseRewardIndices = new List<int>();
|
||||
for (int i = 2; i < tokens.Length; i++)
|
||||
_failedPurchaseRewardIndices.Add(int.Parse(tokens[i]));
|
||||
_redirectId = _tables.TbFishingEvent[_eventId].RedirectID;
|
||||
return true;
|
||||
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogWarning(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Init(int eventId)
|
||||
{
|
||||
_eventId = eventId;
|
||||
_redirectId = _tables.TbFishingEvent[_eventId].RedirectID;
|
||||
_status = 0;
|
||||
_failedPurchaseRewardIndices = new List<int>();
|
||||
Save();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TableContext
|
||||
|
||||
private EventOfferTreasureAscent TbMain
|
||||
{
|
||||
get
|
||||
{
|
||||
return _tables.TbEventOfferTreasureAscent[_redirectId];
|
||||
}
|
||||
}
|
||||
|
||||
public List<EventOfferTreasureAscentRewards> GetRandomizedRewardList()
|
||||
{
|
||||
// warp the ordered list to randomize the order, with player playfab id as seed so that player got consistent result.
|
||||
var id = GContext.container.Resolve<IUserService>().UserId;
|
||||
// treat playfab id as an ordinary string instead of a hexadicimal number, and roll it into a hash. Consistant for each player.
|
||||
int seed = id.Aggregate(17, (hash,c)=> hash * 31 + c);
|
||||
var rng = new System.Random(seed);
|
||||
// var rng = new System.Random((int)System.Convert.ToUInt64(id, 16));
|
||||
|
||||
var res = _tables.TbEventOfferTreasureAscentRewards.DataList.ToList();
|
||||
|
||||
FtUtils.ShuffleList(res, rng);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get item data from reward index. Will return null if no table data found. Note that the ID of the item, which is not used here, is not the same as the index.
|
||||
/// </summary>
|
||||
/// <param name="idx">Index of rewards.</param>
|
||||
/// <returns>The ItemData, after transition and stuff. Will return null if not found.</returns>
|
||||
public ItemData GetItemFromIndex(int idx)
|
||||
{
|
||||
// var tbRewards = _tables.TbEventOfferTreasureAscentRewards.DataList;
|
||||
var tbRewards = GetRandomizedRewardList();
|
||||
idx = Mathf.Clamp(idx, 0, tbRewards.Count - 1);
|
||||
var reward = tbRewards[idx];
|
||||
var res = new ItemData(reward.Item, reward.Number);
|
||||
_playerItemData.ItemTransition(res);
|
||||
_playerItemData.LureInflation(res, null);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pick random reward index.
|
||||
/// </summary>
|
||||
/// <param name="currentRound">Current round count that limits the option.</param>
|
||||
/// <returns>Reward idx. -1 if not found.</returns>
|
||||
public int PickRandomAvailableRewardIdx(int currentRound)
|
||||
{
|
||||
// var tbRewards = _tables.TbEventOfferTreasureAscentRewards.DataList;
|
||||
var tbRewards = GetRandomizedRewardList();
|
||||
var availableIdxList = tbRewards
|
||||
.Select((r, i) => (r, i))
|
||||
.Where(ri => ri.r.LimitConfig - 1 <= currentRound && !IsClaimed(ri.i))
|
||||
.Select(ri => ri.i)
|
||||
.ToList();
|
||||
if (availableIdxList.Count <= 0)
|
||||
{
|
||||
Debug.LogError($"[OfferTreasureAscent] No available reward found in round {currentRound}. This is not supposed to happen.");
|
||||
return -1;
|
||||
}
|
||||
var weightList = availableIdxList.Select(i => tbRewards[i].Weight).ToList();
|
||||
return FtUtils.PickRandomItemWithWeight(availableIdxList, i => weightList[i]);
|
||||
}
|
||||
|
||||
public int PickRandomAvailableRewardIdx()
|
||||
{
|
||||
return PickRandomAvailableRewardIdx(RoundCount);
|
||||
}
|
||||
|
||||
public int GetRewardCount()
|
||||
{
|
||||
return _tables.TbEventOfferTreasureAscentRewards.DataList.Count;
|
||||
}
|
||||
|
||||
public async System.Threading.Tasks.Task<(int normalRewardIdx, List<ItemData> finalRewards, int iap_id)> GrantRandomReward()
|
||||
{
|
||||
var iapId = GetIapId();
|
||||
var pickedRewardIdx = PickRandomAvailableRewardIdx();
|
||||
List<ItemData> finalRewards = null;
|
||||
if (iapId > 0)
|
||||
{
|
||||
GContext.container.Resolve<PlayerItemData>().ResolveIapId(iapId, out var iapItemList, out _);
|
||||
var buyTypeData = new ShopBuyTypeData
|
||||
{
|
||||
type = ShopBuyType.EventPack,
|
||||
ID = EventId,
|
||||
NoShow = true
|
||||
};
|
||||
var rewardItem = GetItemFromIndex(pickedRewardIdx);
|
||||
if (pickedRewardIdx < 0 || rewardItem == null)
|
||||
{
|
||||
Debug.LogError("[OfferTreasureAscent] No reward to claim.");
|
||||
return (-1, null, iapId);
|
||||
}
|
||||
// Drop id required here is fill with 0, because it is a legacy feature and is no longer used.
|
||||
var res = await GContext.container.Resolve<PlayerShopData>()
|
||||
.OnBuy(0, buyTypeData, iapItemList, new List<ItemData>() { rewardItem });
|
||||
if (res)
|
||||
{
|
||||
Claim(pickedRewardIdx);
|
||||
// Debug.Log($"[OfferTreasureAscent] Grant paid {pickedRewardIdx}, ID {pickedRewardIdx + 1}");
|
||||
finalRewards = TryGrantFinalReward();
|
||||
}
|
||||
else
|
||||
{
|
||||
RecordFailedRewardIdx(pickedRewardIdx);
|
||||
return (-1, null, iapId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pickedRewardIdx >= 0)
|
||||
{
|
||||
Claim(pickedRewardIdx);
|
||||
TryGrantReward(pickedRewardIdx);
|
||||
finalRewards = TryGrantFinalReward();
|
||||
}
|
||||
}
|
||||
return (pickedRewardIdx, finalRewards, iapId);
|
||||
}
|
||||
|
||||
public List<ItemData> GetRewards()
|
||||
{
|
||||
var res = new List<ItemData>();
|
||||
for (int i = 0; i < GetRewardCount(); i++)
|
||||
res.Add(GetItemFromIndex(i));
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get IAP ID list. Fill in 0s for free chances.
|
||||
/// </summary>
|
||||
/// <returns>IAP ID</returns>
|
||||
public List<int> GetIapIdList()
|
||||
{
|
||||
var res = new List<int>();
|
||||
for (int i = 0; i < TbMain.FreeChance; i++)
|
||||
{
|
||||
res.Add(0);
|
||||
}
|
||||
res.AddRange(TbMain.IapList);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get IAP ID. Get 0 for free chance.
|
||||
/// </summary>
|
||||
/// <param name="currentRound">Round count. Starts with 0.</param>
|
||||
/// <returns>IAP ID, get 0 for free chance.</returns>
|
||||
public int GetIapId(int currentRound)
|
||||
{
|
||||
var iapIdList = GetIapIdList();
|
||||
currentRound = Mathf.Clamp(currentRound, 0, iapIdList.Count - 1);
|
||||
return iapIdList[currentRound];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get IAP ID. Get 0 for free chance. For current round.
|
||||
/// </summary>
|
||||
/// <returns>IAP ID of this round. 0 for free chance.</returns>
|
||||
public int GetIapId()
|
||||
{
|
||||
return GetIapId(RoundCount);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI data
|
||||
/// <summary>
|
||||
/// Get UI Data.
|
||||
/// </summary>
|
||||
/// <returns>First is main panel, second is the grand reward showing animation panel.</returns>
|
||||
public string[] GetUiData()
|
||||
{
|
||||
return new string[] { TbMain.UIPanel, TbMain.Icon, TbMain.BoxOpenPrefab};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Entrance button data.
|
||||
/// </summary>
|
||||
/// <param name="idx"></param>
|
||||
public OfferTreasureAscentEntranceData GetEntranceData()
|
||||
{
|
||||
var res = new OfferTreasureAscentEntranceData();
|
||||
res.IconUrl = TbMain.Icon;
|
||||
(var startTime, var endTime) = FtUtils.GetEventTimeLimit(EventId);
|
||||
res.TimerData = new TimerContext
|
||||
{
|
||||
StartTime = startTime,
|
||||
ExpiryTime = endTime
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
public GeneralProbabilityData[] GetProbabilityData()
|
||||
{
|
||||
var res = new GeneralProbabilityData[GetRewardCount()];
|
||||
var tbRewards = GetRandomizedRewardList();
|
||||
var totalWeight = tbRewards.Where((r, i) => !IsClaimed(i)).Sum(r => r.Weight);
|
||||
for (int i = 0; i < GetRewardCount(); i++)
|
||||
{
|
||||
res[i] = new GeneralProbabilityData
|
||||
{
|
||||
Item = GetItemFromIndex(i),
|
||||
Probability = tbRewards[i].Weight / (float)totalWeight,
|
||||
IsClaimed = IsClaimed(i)
|
||||
};
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region System
|
||||
|
||||
public void Claim(int idx)
|
||||
{
|
||||
_status |= 1 << idx;
|
||||
Save();
|
||||
}
|
||||
|
||||
public bool IsClaimed(int idx)
|
||||
{
|
||||
return (_status & (1 << idx)) != 0;
|
||||
}
|
||||
|
||||
public ItemData GetGrandReward()
|
||||
{
|
||||
return new ItemData(TbMain.ChestReward, 1);
|
||||
}
|
||||
|
||||
public int GetGrandRewardItemId()
|
||||
{
|
||||
return TbMain.ChestReward;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if all normal rewards are claimed
|
||||
/// </summary>
|
||||
/// <returns>True is all is claimed.</returns>
|
||||
public bool IsNormalRewardsDepleted
|
||||
{
|
||||
get
|
||||
{
|
||||
int rewardCount = GetRewardCount();
|
||||
|
||||
if (rewardCount <= 0)
|
||||
return true;
|
||||
|
||||
if (rewardCount > 31)
|
||||
{
|
||||
UnityEngine.Debug.LogError("[OfferTreasureAscent] Reward count is too large for int: " + rewardCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
int expectedMask = (1 << rewardCount) - 1;
|
||||
return (_status & expectedMask) == expectedMask;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsFinalRewardClaimed
|
||||
{
|
||||
get
|
||||
{
|
||||
return IsNormalRewardsDepleted && IsClaimed(GetRewardCount());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Grant reward according to reward index, and try grant final reward if all rewards are claimed.
|
||||
/// </summary>
|
||||
/// <param name="rewardIdx">The index of the target reward.</param>
|
||||
public void TryGrantReward(int rewardIdx)
|
||||
{
|
||||
Debug.Log($"[OfferTreasureAscent] Grant free {rewardIdx}, ID {rewardIdx + 1}");
|
||||
var rewardItem = GetItemFromIndex(rewardIdx);
|
||||
if (rewardItem == null)
|
||||
{
|
||||
Debug.LogError("[OfferTreasureAscent] Reward item is null. This is not supposed to happen.");
|
||||
return;
|
||||
}
|
||||
_playerItemData.AddItem(rewardItem, null);
|
||||
GContext.Publish(new ShowData(rewardItem));
|
||||
}
|
||||
|
||||
public List<ItemData> TryGrantFinalReward()
|
||||
{
|
||||
// Debug.Log("[OfferTreasureAscent]Try Grant Final...");
|
||||
if (!IsNormalRewardsDepleted || IsFinalRewardClaimed)
|
||||
return null;
|
||||
// var grandReward = GetGrandReward();
|
||||
// _playerItemData.AddItem(grandReward, null);
|
||||
var grandRewardId = GetGrandRewardItemId();
|
||||
var items = FtUtils.ResolveChestContent(grandRewardId);
|
||||
if (items.Count <= 0)
|
||||
{
|
||||
Debug.LogError("[OfferTreasureAscent] Grand reward is empty.");
|
||||
return null;
|
||||
}
|
||||
_playerItemData.AddItem(items, null);
|
||||
Claim(GetRewardCount());
|
||||
// Debug.Log("[OfferTreasureAscent] Grant Final.");
|
||||
return items;
|
||||
}
|
||||
|
||||
public void OnBuySuccess()
|
||||
{
|
||||
if (_failedPurchaseRewardIndices is { Count: > 0 })
|
||||
{
|
||||
foreach (var idx in _failedPurchaseRewardIndices)
|
||||
Claim(idx);
|
||||
_failedPurchaseRewardIndices.Clear();
|
||||
Save();
|
||||
}
|
||||
TryGrantFinalReward();
|
||||
}
|
||||
|
||||
public void RecordFailedRewardIdx(int idx)
|
||||
{
|
||||
_failedPurchaseRewardIndices.Add(idx);
|
||||
Save();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,179 +0,0 @@
|
||||
using asap.core;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UniRx;
|
||||
using GameCore;
|
||||
using game;
|
||||
using static EventPartnerData;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class EventPartnerEntryButton : EventButtonResource
|
||||
{
|
||||
game.PlayerDataStorage.IPlayerSaveStore saveStore;
|
||||
|
||||
[SerializeField] private TMP_Text _textTimer;
|
||||
[SerializeField] private Button _btn;
|
||||
[SerializeField] CanvasGroup _homeCanvasGroup;
|
||||
[SerializeField] private Image icon;
|
||||
private readonly IUserService _userService = GContext.container.Resolve<IUserService>();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
// Debug.Log($"<color=#e23f31>[EventPartner] Icon Awake</color>");
|
||||
GContext.OnEvent<EventPartnerDataLoaded>().Subscribe(_ => OnDataLoaded()).AddTo(this);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
var _data = GContext.container.Resolve<EventPartnerData>();
|
||||
// _data.LogActivation();
|
||||
|
||||
//var manager = GContext.container.Resolve(Partainer)?
|
||||
|
||||
if (!_data.IsEventActivated)
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
return;
|
||||
}
|
||||
_textTimer.text = ConvertTools.ConvertTime2(_data.RemainingTime);
|
||||
RedPointManager.Instance.SetRedPointState("Home.Fishbowl", _data.DoNeedRedPoint);
|
||||
Observable.Interval(System.TimeSpan.FromSeconds(1.0f)).Subscribe(_ =>
|
||||
{
|
||||
_textTimer.text = ConvertTools.ConvertTime2(_data.RemainingTime);
|
||||
RedPointManager.Instance.SetRedPointState("Home.Fishbowl", _data.DoNeedRedPoint);
|
||||
if (_data.RemainingTime.TotalSeconds <= 0) gameObject.SetActive(false);
|
||||
}).AddTo(this);
|
||||
// Debug.Log($"<color=#e23f31>icon: {_data.Icon}</color>");
|
||||
CheckResource(new List<string>() { "EventPartnerCommonPrefabs", _data.AddressableLabel, _data.Icon });
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_btn.onClick.AddListener(() => EnterEventPartnerAct());
|
||||
}
|
||||
|
||||
private async void EnterEventPartnerAct()
|
||||
{
|
||||
try
|
||||
{
|
||||
var _data = GContext.container.Resolve<EventPartnerData>();
|
||||
_homeCanvasGroup.blocksRaycasts = false;
|
||||
var isWithinCoolDown = _data.LastDetailRequestFromEntranceButton != null
|
||||
&& ZZTimeHelper.UtcNow() - _data.LastDetailRequestFromEntranceButton <= System.TimeSpan.FromSeconds(15);
|
||||
|
||||
if (!_data.IsWhole || !isWithinCoolDown)
|
||||
{
|
||||
_data.LastDetailRequestFromEntranceButton = ZZTimeHelper.UtcNow();
|
||||
var dataRes = await LoadData();
|
||||
if (!dataRes)
|
||||
{
|
||||
Debug.Log($"<color=red>[EventPartner] LoadData Failed.</color>");
|
||||
ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_107"));
|
||||
_homeCanvasGroup.blocksRaycasts = true;
|
||||
_data.IsLastTimeDetailRequestFromEntranceButtonValid = false;
|
||||
return;
|
||||
}
|
||||
_data.IsLastTimeDetailRequestFromEntranceButtonValid = true;
|
||||
// else open panel.
|
||||
}
|
||||
else if (!_data.IsLastTimeDetailRequestFromEntranceButtonValid)
|
||||
{
|
||||
Debug.Log($"<color=red>[EventPartner] Too frequent.</color>");
|
||||
ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_107"));
|
||||
_homeCanvasGroup.blocksRaycasts = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Load resource and enter event act
|
||||
_data.SubscribeEvent();
|
||||
if (await LoadAssetBundle())
|
||||
GContext.Publish(new UnloadActToNextAct("EventPartnerAct"));
|
||||
else
|
||||
{
|
||||
var panel = await UIManager.Instance.ShowUI(UITypes.CloudTransitionPanel);
|
||||
panel.GetComponent<CloudTransitionPanel>().SetBtn(true, () => GContext.Publish(new UnloadActToNextAct("EventPartnerAct")));
|
||||
}
|
||||
_homeCanvasGroup.blocksRaycasts = true;
|
||||
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.Log($"<color=#22a6f2>[EventPartner] EnterActError: {e.Message}\n{e.StackTrace}</color>");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task<bool> LoadData()
|
||||
{
|
||||
var _data = GContext.container.Resolve<EventPartnerData>();
|
||||
var _playerPreferenceKey = "EventPartnerRecommendCache" + _userService.UserId;
|
||||
//Load Player Preferences
|
||||
var pfString = saveStore.Read(EventPartnerPlayfabData.Key).ValueOrThrow();
|
||||
EventPartnerPlayfabData pfData = EventPartnerPlayfabData.FromString(pfString);
|
||||
Debug.Log($"<color=#2d7cee>[EventPartner] Try get response in load data from entrance:</color>");
|
||||
var detailRequest = new EventBuildDetailRequest() { eventId = _data.EventId };
|
||||
var response = await GContext.container.Resolve<ICustomServerMgr>()
|
||||
.EventPartnerRequest<EventBuildDetailResp>(DetailUrl, detailRequest);
|
||||
if (this == null || !saveStore.IsCurrent) return false;
|
||||
if (response == null)
|
||||
{
|
||||
Debug.Log($"<color=red>[EventPartner] No response. </color>");
|
||||
return false;
|
||||
}
|
||||
Debug.Log($"<color=#2d7cee>[EventPartner] Response: {response}</color>");
|
||||
Debug.Log($"<color=#2d7cee>[EventPartner] ResponseState: {response.State}</color>");
|
||||
Debug.Log($"<color=#2d7cee>[EventPartner] ResponseMessage: {response.Message}</color>");
|
||||
switch (response.State)
|
||||
{
|
||||
case EEventBuildState.Failed:
|
||||
Debug.Log($"<color=red>[EventPartner] Load data error from server: {response.Message} </color>");
|
||||
return false;
|
||||
case EEventBuildState.NoActiveEvent:
|
||||
Debug.Log($"<color=red>[EventPartner] Event partner not active in server: {response.Message}, but this is not supposed to happen any more.</color>");
|
||||
return false;
|
||||
case EEventBuildState.Success:
|
||||
_data.ResolveAllDataButEvent(response, _data.Cache, pfData);
|
||||
return true;
|
||||
default:
|
||||
Debug.Log($"<color=red>[EventPartner] Unexpected status: {response.State}./nMessage: {response.Message} </color>");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task<bool> LoadAssetBundle()
|
||||
{
|
||||
var _data = GContext.container.Resolve<EventPartnerData>();
|
||||
var loadResourceService = GContext.container.Resolve<ILoadResourceService>();
|
||||
var isCommentAssetLoaded = await loadResourceService.Load("EventPartnerCommonPrefabs");
|
||||
var isCurrentAssetLoaded = await loadResourceService.Load(_data.AddressableLabel);
|
||||
return isCommentAssetLoaded && isCurrentAssetLoaded;
|
||||
}
|
||||
|
||||
private async void OnDataLoaded()
|
||||
{
|
||||
Debug.Log($"<color=#2d7cee>[EventPartner]On Loaded.</color>");
|
||||
await Awaiters.NextFrame;
|
||||
//gameObject.SetActive(true);
|
||||
var _data = GContext.container.Resolve<EventPartnerData>();
|
||||
CheckResource(new List<string>() { "EventPartnerCommonPrefabs", _data.AddressableLabel, _data.Icon });
|
||||
}
|
||||
|
||||
protected override void OnLoadEventResource()
|
||||
{
|
||||
var _data = GContext.container.Resolve<EventPartnerData>();
|
||||
if (_data == null)
|
||||
{
|
||||
Debug.LogError("EventPartnerEntryButton EventPartnerData 为空");
|
||||
return;
|
||||
}
|
||||
// _data.LogActivation();
|
||||
if (_data.IsEventActivated)
|
||||
{
|
||||
GContext.container.Resolve<IUIService>().SetImageSprite(icon, _data.Icon);
|
||||
gameObject.SetActive(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
public class EventPartnerDataLoaded { }
|
||||
@@ -1,112 +0,0 @@
|
||||
using asap.core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
public class EventPartnerStatuePlayfabData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public int EventId;
|
||||
public int TicketCount;
|
||||
public int BuffFreeMultiplier;
|
||||
public DateTime BuffPointExpiryTime;
|
||||
// public List<string> PartnerIdList;
|
||||
public Dictionary<string, (int self, int partner)> DisplayScoreMap;
|
||||
public Dictionary<string, (int self, int bot)> RobotScoreMap;
|
||||
public bool IsFinalRewardClaimed;
|
||||
public float LureInflationRate;
|
||||
public bool IsEnableAutoMatch;
|
||||
/// <summary>UTC 时间,开启自动匹配时记录;用于关闭时的 CD 与 Gather 活动一致。</summary>
|
||||
public DateTime LastAutoMatchTimeUtc = DateTime.MinValue;
|
||||
private const char Pipe = '|', Comma = ',';
|
||||
public const string Key = "EventPartnerStatueData";
|
||||
|
||||
public string Serialized()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(EventId).Append(Pipe);
|
||||
sb.Append(TicketCount).Append(Pipe);
|
||||
sb.Append(BuffFreeMultiplier).Append(Pipe);
|
||||
sb.Append(BuffPointExpiryTime.ToString()).Append(Pipe);
|
||||
sb.Append(IsFinalRewardClaimed.ToString()).Append(Pipe);
|
||||
sb.Append(LureInflationRate).Append(Pipe);
|
||||
foreach (var kv in DisplayScoreMap)
|
||||
sb.Append(kv.Key).Append(Comma).Append(kv.Value.self).Append(Comma).Append(kv.Value.partner).Append(Comma);
|
||||
sb.Append(Pipe);
|
||||
foreach (var kv in RobotScoreMap)
|
||||
sb.Append(kv.Key).Append(Comma).Append(kv.Value.self).Append(Comma).Append(kv.Value.bot).Append(Comma);
|
||||
sb.Append(Pipe);
|
||||
sb.Append(IsEnableAutoMatch.ToString()).Append(Pipe);
|
||||
sb.Append(LastAutoMatchTimeUtc.ToString("o"));
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will return null if deserialization failed
|
||||
/// </summary>
|
||||
/// <param name="s">String from playfab</param>
|
||||
/// <returns>Playfab Data. Null if fails.</returns>
|
||||
public static EventPartnerStatuePlayfabData Deserialize(string s)
|
||||
{
|
||||
try
|
||||
{
|
||||
var arr = s.Trim(Pipe).Split(Pipe);
|
||||
var res = new EventPartnerStatuePlayfabData();
|
||||
res.EventId = int.Parse(arr[0]);
|
||||
res.TicketCount = int.Parse(arr[1]);
|
||||
res.BuffFreeMultiplier = int.Parse(arr[2]);
|
||||
res.BuffPointExpiryTime = DateTime.Parse(arr[3]);
|
||||
res.DisplayScoreMap = new Dictionary<string, (int self, int partner)>();
|
||||
res.RobotScoreMap = new Dictionary<string, (int self, int bot)>();
|
||||
res.IsFinalRewardClaimed = bool.Parse(arr[4]);
|
||||
res.LureInflationRate = float.Parse(arr[5]);
|
||||
if (arr.Length <= 6)
|
||||
return res;
|
||||
if (!string.IsNullOrEmpty(arr[6]))
|
||||
{
|
||||
var displayScoreStringList = arr[6].Trim(Comma).Split(Comma);
|
||||
for (int i = 0; i + 2 < displayScoreStringList.Length; i += 3)
|
||||
res.DisplayScoreMap.Add(displayScoreStringList[i], (int.Parse(displayScoreStringList[i + 1]), int.Parse(displayScoreStringList[i + 2])));
|
||||
}
|
||||
|
||||
if (arr.Length <= 7)
|
||||
return res;
|
||||
if (!string.IsNullOrEmpty(arr[7]))
|
||||
{
|
||||
var robotScoreStringList = arr[7].Trim(Comma).Split(Comma);
|
||||
for (int i = 0; i + 2 < robotScoreStringList.Length; i += 3)
|
||||
res.RobotScoreMap.Add(robotScoreStringList[i], (int.Parse(robotScoreStringList[i + 1]), int.Parse(robotScoreStringList[i + 2])));
|
||||
}
|
||||
|
||||
if (arr.Length > 8)
|
||||
res.IsEnableAutoMatch = bool.TryParse(arr[8], out var v) && v;
|
||||
|
||||
if (arr.Length > 9 && !string.IsNullOrEmpty(arr[9]) &&
|
||||
DateTime.TryParse(arr[9], null, System.Globalization.DateTimeStyles.RoundtripKind, out var lastAm))
|
||||
res.LastAutoMatchTimeUtc = lastAm;
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static EventPartnerStatuePlayfabData Load()
|
||||
{
|
||||
var s = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>().Read(Key).ValueOrThrow();
|
||||
return Deserialize(s);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var s = Serialized();
|
||||
saveStore.Write(Key, s).RequireAccepted();
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user