Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
phpdts
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Nemo Ma
phpdts
Commits
85d98d58
Commit
85d98d58
authored
Jun 14, 2025
by
Nemo Ma
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
BUGFIX: assorted fixes
parent
8dfad3ce
Changes
7
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
973 additions
and
110 deletions
+973
-110
doc/etc/20250614_bgm_playback_fix.txt
doc/etc/20250614_bgm_playback_fix.txt
+120
-0
doc/etc/20250614_fireseed_data_structure_fix.txt
doc/etc/20250614_fireseed_data_structure_fix.txt
+185
-0
fireseed_data.php
fireseed_data.php
+102
-0
include/game.func.php
include/game.func.php
+65
-5
include/game/club22.func.php
include/game/club22.func.php
+197
-62
include/game20130526.js
include/game20130526.js
+184
-22
templates/default/slidingpanel.htm
templates/default/slidingpanel.htm
+120
-21
No files found.
doc/etc/20250614_bgm_playback_fix.txt
0 → 100644
View file @
85d98d58
BGM播放卡顿问题修复记录
时间:2025-06-14
问题:播放新歌单时出现AbortError错误
== 问题分析 ==
1. Console错误信息:
- "The play() request was interrupted by a new load request"
- "The play() request was interrupted by a call to pause()"
- 错误发生在game20130526.js:583和game.php:1
2. 根本原因:
- changeBGM()函数中,load()和play()方法调用过于紧密
- 没有处理异步播放的Promise返回值
- 缺少对播放中断的错误处理
- 自动播放可能与用户交互冲突
== 解决方案 ==
1. 修改changeBGM()函数 (include/game20130526.js:566-618):
- 在加载新音频前先暂停当前播放
- 使用Promise处理异步播放请求
- 添加错误捕获和重试机制
- 添加控制台日志用于调试
2. 修改BGM初始化 (include/game.func.php:211-246):
- 移除autoplay属性,改为手动控制
- 添加DOMContentLoaded事件处理自动播放
- 添加beforeunload事件防止页面卸载时的播放错误
- 使用Promise处理自动播放的错误
3. 改进音量控制 (include/game20130526.js:553-576):
- 添加音频元素存在性检查
- 使用try-catch包装音量设置
- 同步更新nowbgmvolume元素
== 技术细节 ==
- 使用audioElement.pause()和currentTime=0确保完全停止
- Promise.then()和.catch()处理异步播放状态
- 100ms延迟重试机制避免快速连续操作冲突
- 控制台日志帮助调试播放状态
== 预期效果 ==
- 消除AbortError错误信息
- 改善歌单切换的流畅性
- 提供更好的错误恢复机制
- 保持音频播放的稳定性
== 第二次修复 (问题仍存在) ==
发现问题根源更深层:
1. 多个地方同时触发音频播放请求
2. DOMContentLoaded自动播放与changeBGM冲突
3. 浏览器的自动播放策略限制
4. 并发的load()和play()调用
== 彻底解决方案 ==
1. 实现全局BGM状态管理 (bgmPlayState):
- 防止并发changeBGM调用
- 跟踪当前播放Promise
- 处理待处理的切换请求
2. 移除自动播放,改为用户交互触发:
- 移除DOMContentLoaded中的自动播放
- 添加首次点击事件监听器
- 使用preload="metadata"预加载
3. 改进changeBGM函数:
- 添加并发保护机制
- 使用canplaythrough事件确保加载完成
- 实现请求队列处理
- 添加适当的延迟避免竞争条件
4. 优化音频元素初始化:
- 使用IIFE避免全局变量污染
- 添加初始化状态检查
- 改进事件监听器管理
== 技术改进 ==
- 状态机模式管理播放状态
- Promise链式处理避免中断
- 事件驱动的播放控制
- 用户交互检测机制
== 第三次修复 (全面重构) ==
实现了完整的BGM管理系统:
1. 创建全局BGM管理器 (window.BGMManager):
- 集中管理所有BGM播放状态
- 防止重复初始化和并发调用
- 实现请求队列和状态跟踪
- 提供统一的API接口
2. 重构changeBGM函数:
- 原函数改为调用BGMManager.changeBGM()
- 保持向后兼容性
- 简化调用逻辑
3. 改进BGM初始化:
- 添加唯一时间戳防止重复初始化
- 清理旧的事件监听器
- 优先使用BGMManager处理事件
4. 增强播放控制:
- 使用readyState检查音频加载状态
- 实现智能重试机制
- 添加详细的控制台日志
== 核心改进 ==
- 单例模式的BGM管理器
- 状态机驱动的播放控制
- 防重复初始化机制
- 智能的音频加载检测
- 完善的错误处理和恢复
== 测试建议 ==
1. 快速连续切换BGM测试
2. 页面刷新时的音频状态测试
3. 音量调节时的播放稳定性测试
4. 不同浏览器的兼容性测试
5. 用户首次交互后的自动播放测试
6. 并发播放请求处理测试
7. 长时间运行稳定性测试
doc/etc/20250614_fireseed_data_structure_fix.txt
0 → 100644
View file @
85d98d58
# 枫火歌者种火数据结构修复
时间:2025-06-14
## 问题描述
根据测试玩家反馈和个人推定,枫火歌者的设计与实现存在以下逻辑问题:
1. **数据冗余问题**:被收纳的种火的icon、hp、mhp、sp、msp、att、def、pls以及身上的装备等本已包含在players表其自己的数据目前存在了clbpara中,这导致侧边栏显示的对应数据和实际这些NPC的数据有时会不同步,clbpara中的信息应是对应这些NPC数据的指针,从而减少clbpara本身的长度,并保证玩家看见的数据一直和players表中的实际数据一致。
2. **死亡状态检查缺失**:玩家可以对被杀死(hp = 0)或者被尸体销毁(hp = 0且pls=254)的种火进行操作,且它们会正常运用跟随和索敌逻辑,这是不符合逻辑的——玩家不能对死亡或者被尸体销毁的种火进行操作,它们也不应该参与跟随,索敌以及提升玩家数值的逻辑。
## 修复方案
### 1. 修改种火数据存储结构
**修改文件**: `include/game/club22.func.php`
- 在 `FireseedRecruit` 函数中,将种火数据存储从完整复制改为指针式存储
- 只在 `clbpara['fireseed']` 中保留管理信息:
- `level`: 强化等级
- `mode`: 部署模式
- `horizon`: 视界状态
- `items`: 探物收集的物品
- `recruited_time`: 收纳时间
**修改前**:
```php
$clbpara['fireseed'][$fireseed_id] = array(
'name' => $npc['name'],
'icon' => $npc['icon'],
'level' => 1,
'mode' => 0,
'pls' => $pls,
'horizon' => 0,
'hp' => $npc['mhp'],
'mhp' => $npc['mhp'],
// ... 更多冗余数据
);
```
**修改后**:
```php
$clbpara['fireseed'][$fireseed_id] = array(
'level' => 1,
'mode' => 0,
'horizon' => 0,
'items' => array(),
'recruited_time' => $now
);
```
### 2. 添加辅助函数
新增两个辅助函数来处理种火数据:
```php
function getFireseedRealTimeData($fireseed_id) {
// 从players表获取种火实时数据
// 检查种火是否死亡或被销毁
// 返回完整的种火数据或null
}
function isFireseedAlive($fireseed_id) {
// 检查种火是否存活且可用
// 返回布尔值
}
```
### 3. 修改种火行为函数
在所有种火行为函数中添加死亡状态检查:
- `FireseedSearch`: 探物逻辑
- `FireseedDrainNPC`: 索敌逻辑
- `FireseedFollow`: 跟随逻辑
- `FireseedBuffBonus`: 属性加成逻辑
- `FireseedDeploy`: 部署逻辑
- `FireseedEnhance`: 强化逻辑
每个函数在处理种火前都会调用 `isFireseedAlive()` 检查种火状态。
### 4. 创建实时数据API
**新增文件**: `fireseed_data.php`
提供种火实时数据的API接口,返回JSON格式的数据:
- 合并管理数据(来自clbpara)和实时数据(来自players表)
- 标记死亡种火的状态
- 供前端JavaScript调用
### 5. 修改前端显示逻辑
**修改文件**: `templates/default/slidingpanel.htm`
- 修改 `updateFireseedStatusTable()` 函数
- 使用新的API获取实时数据
- 添加 `fetchFireseedRealTimeData()` 函数
- 添加备用显示逻辑 `updateFireseedStatusTableFallback()`
## 修复效果
1. **数据一致性**: 侧边栏显示的种火数据现在直接来自players表,确保与实际状态一致
2. **死亡状态处理**: 死亡或被销毁的种火不再参与任何游戏逻辑
3. **性能优化**: 减少了clbpara中的数据冗余,降低了存储开销
4. **代码维护性**: 统一了数据获取方式,便于后续维护
## 测试建议
1. 测试种火收纳功能
2. 测试种火部署到不同模式
3. 测试种火死亡后的状态显示
4. 测试种火强化功能
5. 测试侧边栏数据更新
6. 测试种火跟随、探物、索敌逻辑
## 问题修复记录
### 问题:HTTP 500错误 - fetch_playerdata_by_name函数未定义
**错误信息**:
```
PHP Fatal error: Uncaught Error: Call to undefined function fetch_playerdata_by_name() in fireseed_data.php:17
```
**原因分析**:
`fireseed_data.php` 文件中调用了 `fetch_playerdata_by_name()` 函数,但没有包含定义该函数的文件 `include/game.func.php`。
**修复方案**:
在 `fireseed_data.php` 中添加必要的包含文件:
```php
include_once './include/common.inc.php';
include_once GAME_ROOT.'./include/game.func.php'; // 新增
include_once GAME_ROOT.'./include/game/club22.func.php';
```
**修复时间**:2025-06-14
### 问题:种火重复显示
**问题描述**:
侧边栏中每个种火被显示了两次,相同ID和内容的种火出现重复行。
**原因分析**:
1. `updateFireseedStatusTable()` 函数中表格清空逻辑不完善
2. 备用函数 `updateFireseedStatusTableFallback()` 中存在语法错误
3. 前端验证逻辑仍在检查已不存在的 `name` 字段
**修复方案**:
1. 在获取实时数据后再次确保表格清空
2. 修复备用函数中缺少 `insertRow()` 的错误
3. 更新前端验证逻辑,移除对 `name` 字段的检查
**修复时间**:2025-06-14
### 问题:种火强化HTTP 500错误和探物功能失效
**问题描述**:
1. 种火强化时返回HTTP 500错误:`Call to a member function query() on null`
2. 种火探物功能失去原有功能
**原因分析**:
1. **数据库错误**:`FireseedEnhance` 函数中缺少 `$db` 和 `$tablepre` 全局变量声明
2. **探物逻辑问题**:种火行为检查逻辑不够健壮,没有正确处理新旧数据格式的兼容性
**修复方案**:
1. **修复数据库错误**:
```php
function FireseedEnhance($fireseed_id, $item_index) {
global $log, $fireseed_enhance_multipliers, $db, $tablepre; // 添加缺失的全局变量
```
2. **改进种火行为检查逻辑**:
- 优先检查 `pose` 字段(新数据格式)
- 如果没有 `pose` 字段,则使用 `mode` 字段(旧数据兼容)
- 统一所有种火行为函数的检查逻辑
**修复时间**:2025-06-14
## 注意事项
1. 现有的种火数据需要进行迁移,旧的冗余数据会被忽略
2. 前端需要处理API调用失败的情况
3. 死亡种火的物品仍然保留在clbpara中,可以被获取
4. `fireseed_data.php` 需要正确包含所有依赖的函数文件
fireseed_data.php
0 → 100644
View file @
85d98d58
<?php
if
(
!
defined
(
'IN_GAME'
))
{
define
(
'IN_GAME'
,
true
);
}
include_once
'./include/common.inc.php'
;
include_once
GAME_ROOT
.
'./include/game.func.php'
;
include_once
GAME_ROOT
.
'./include/game/club22.func.php'
;
// 检查用户是否登录
if
(
empty
(
$cuser
))
{
echo
json_encode
(
array
(
'error'
=>
'Not logged in'
));
exit
;
}
// 获取玩家数据
$pdata
=
fetch_playerdata_by_name
(
$cuser
);
if
(
!
$pdata
)
{
echo
json_encode
(
array
(
'error'
=>
'Player data not found'
));
exit
;
}
// clbpara 已经在 fetch_playerdata_by_name 中通过 check_player_misc_states 处理过了
// 检查是否为枫火歌者
if
(
$pdata
[
'club'
]
!=
22
)
{
echo
json_encode
(
array
(
'error'
=>
'Not a Fireseed Singer'
));
exit
;
}
// 获取种火实时数据
$fireseed_realtime_data
=
array
();
if
(
!
empty
(
$pdata
[
'clbpara'
][
'fireseed'
]))
{
foreach
(
$pdata
[
'clbpara'
][
'fireseed'
]
as
$fs_id
=>
$fs_data
)
{
// 获取种火实时数据
$realtime_data
=
getFireseedRealTimeData
(
$fs_id
);
if
(
$realtime_data
)
{
// 合并管理数据和实时数据
$fireseed_realtime_data
[
$fs_id
]
=
array
(
// 管理数据(来自 clbpara)
'level'
=>
$fs_data
[
'level'
],
'mode'
=>
$fs_data
[
'mode'
],
'horizon'
=>
isset
(
$fs_data
[
'horizon'
])
?
$fs_data
[
'horizon'
]
:
0
,
'items'
=>
isset
(
$fs_data
[
'items'
])
?
$fs_data
[
'items'
]
:
array
(),
'recruited_time'
=>
isset
(
$fs_data
[
'recruited_time'
])
?
$fs_data
[
'recruited_time'
]
:
0
,
'pose'
=>
isset
(
$fs_data
[
'pose'
])
?
$fs_data
[
'pose'
]
:
null
,
// 实时数据(来自 players 表)
'name'
=>
$realtime_data
[
'name'
],
'icon'
=>
$realtime_data
[
'icon'
],
'hp'
=>
$realtime_data
[
'hp'
],
'mhp'
=>
$realtime_data
[
'mhp'
],
'sp'
=>
$realtime_data
[
'sp'
],
'msp'
=>
$realtime_data
[
'msp'
],
'att'
=>
$realtime_data
[
'att'
],
'def'
=>
$realtime_data
[
'def'
],
'pls'
=>
$realtime_data
[
'pls'
],
'wep'
=>
$realtime_data
[
'wep'
],
'wepk'
=>
$realtime_data
[
'wepk'
],
'wepe'
=>
$realtime_data
[
'wepe'
],
'weps'
=>
$realtime_data
[
'weps'
],
'wepsk'
=>
$realtime_data
[
'wepsk'
],
'arb'
=>
$realtime_data
[
'arb'
],
'arbk'
=>
$realtime_data
[
'arbk'
],
'arbe'
=>
$realtime_data
[
'arbe'
],
'arbs'
=>
$realtime_data
[
'arbs'
],
'arbsk'
=>
$realtime_data
[
'arbsk'
],
'skills'
=>
isset
(
$realtime_data
[
'clbpara'
][
'skill'
])
&&
is_array
(
$realtime_data
[
'clbpara'
][
'skill'
])
?
$realtime_data
[
'clbpara'
][
'skill'
]
:
array
(),
'alive'
=>
true
);
}
else
{
// 种火已死亡或被销毁,但保留基本信息用于显示
$fireseed_realtime_data
[
$fs_id
]
=
array
(
'level'
=>
$fs_data
[
'level'
],
'mode'
=>
$fs_data
[
'mode'
],
'horizon'
=>
isset
(
$fs_data
[
'horizon'
])
?
$fs_data
[
'horizon'
]
:
0
,
'items'
=>
isset
(
$fs_data
[
'items'
])
?
$fs_data
[
'items'
]
:
array
(),
'recruited_time'
=>
isset
(
$fs_data
[
'recruited_time'
])
?
$fs_data
[
'recruited_time'
]
:
0
,
'pose'
=>
isset
(
$fs_data
[
'pose'
])
?
$fs_data
[
'pose'
]
:
null
,
'name'
=>
'已死亡的种火'
,
'icon'
=>
''
,
'hp'
=>
0
,
'mhp'
=>
0
,
'sp'
=>
0
,
'msp'
=>
0
,
'att'
=>
0
,
'def'
=>
0
,
'pls'
=>
254
,
'alive'
=>
false
);
}
}
}
// 返回 JSON 数据
header
(
'Content-Type: application/json; charset=utf-8'
);
echo
json_encode
(
$fireseed_realtime_data
,
JSON_UNESCAPED_UNICODE
);
?>
include/game.func.php
View file @
85d98d58
...
@@ -175,9 +175,17 @@ function init_bgm($force_update=0)
...
@@ -175,9 +175,17 @@ function init_bgm($force_update=0)
}
}
}
}
# 防止重复初始化 - 只在真正需要时重载播放器
static
$bgm_initialized
=
false
;
if
(
$bgm_initialized
&&
!
$force_update
&&
$command
!=
'enter'
)
{
return
''
;
}
# 刷新页面或输入强制重载参数时,重载播放器
# 刷新页面或输入强制重载参数时,重载播放器
if
(
$command
==
'enter'
||
$force_update
)
if
(
$command
==
'enter'
||
$force_update
||
!
$bgm_initialized
)
{
{
$bgm_initialized
=
true
;
$booklist
=
$bgmarr
=
Array
();
$booklist
=
$bgmarr
=
Array
();
# 为播放列表中的曲集关联对应BGM名、链接与种类
# 为播放列表中的曲集关联对应BGM名、链接与种类
foreach
(
$clbpara
[
'bgmbook'
]
as
$book
)
foreach
(
$clbpara
[
'bgmbook'
]
as
$book
)
...
@@ -208,17 +216,69 @@ function init_bgm($force_update=0)
...
@@ -208,17 +216,69 @@ function init_bgm($force_update=0)
# 生成播放器与播放队列 太野蛮了……嘻嘻……
# 生成播放器与播放队列 太野蛮了……嘻嘻……
if
(
!
empty
(
$bgmlink
)
&&
!
empty
(
$bgmtype
))
if
(
!
empty
(
$bgmlink
)
&&
!
empty
(
$bgmtype
))
{
{
$bgm_timestamp
=
time
()
.
rand
(
1000
,
9999
);
// 唯一标识符
$bgmplayer
=
<<<EOT
$bgmplayer
=
<<<EOT
<audio id="gamebgm"
autoplay controls=1" onplay="$('gamebgm').volume=$('nowbgmvolume').innerHTML;
">
<audio id="gamebgm"
controls="1" preload="metadata" data-bgm-init="$bgm_timestamp
">
<source id="gbgm" src="$bgmlink" type="$bgmtype">
<source id="gbgm" src="$bgmlink" type="$bgmtype">
</audio>
</audio>
<div id="bgmlist">$json_bgmarr</div>
<div id="bgmlist">$json_bgmarr</div>
<div id="nowbgm">0</div>
<div id="nowbgm">0</div>
<div id="nowbgmvolume">$volume_r</div>
<div id="nowbgmvolume">$volume_r</div>
<script>
<script>
gamebgm.addEventListener('ended', function () {
// 防止重复初始化BGM播放器
changeBGM();
(function() {
}, false);
var currentTimestamp = '$bgm_timestamp';
// 检查是否已经初始化过相同的实例
if (window.bgmInitTimestamp === currentTimestamp) {
console.log('BGM播放器已存在,跳过重复初始化');
return;
}
window.bgmInitTimestamp = currentTimestamp;
var audioElement = document.getElementById('gamebgm');
if (!audioElement) {
console.log('BGM音频元素不存在');
return;
}
// 清理之前的事件监听器
if (window.bgmEndedHandler) {
audioElement.removeEventListener('ended', window.bgmEndedHandler);
}
function initBGM() {
audioElement.volume = $volume_r;
// 创建新的事件处理器
window.bgmEndedHandler = function() {
if (window.BGMManager && typeof BGMManager.changeBGM === 'function') {
BGMManager.changeBGM();
} else if (typeof changeBGM === 'function') {
changeBGM();
}
};
// 监听音频结束事件
audioElement.addEventListener('ended', window.bgmEndedHandler, false);
console.log('BGM播放器已初始化 (ID: ' + currentTimestamp + ')');
}
// 页面加载完成后初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initBGM);
} else {
initBGM();
}
// 防止页面卸载时的播放中断错误
window.addEventListener('beforeunload', function() {
if (audioElement) {
audioElement.pause();
}
});
})();
</script>
</script>
EOT;
EOT;
}
}
...
...
include/game/club22.func.php
View file @
85d98d58
This diff is collapsed.
Click to expand it.
include/game20130526.js
View file @
85d98d58
...
@@ -551,7 +551,10 @@ function AddMixElements(emix_arr) {
...
@@ -551,7 +551,10 @@ function AddMixElements(emix_arr) {
}
}
function
changeVolume
(
cv
){
function
changeVolume
(
cv
){
var
v
=
$
(
'
gamebgm
'
).
volume
;
var
audioElement
=
$
(
'
gamebgm
'
);
if
(
!
audioElement
)
return
;
var
v
=
audioElement
.
volume
;
v
=
v
+
cv
;
v
=
v
+
cv
;
v
=
Math
.
min
(
1
,
v
);
v
=
Math
.
max
(
0
,
v
);
v
=
v
.
toFixed
(
2
);
v
=
Math
.
min
(
1
,
v
);
v
=
Math
.
max
(
0
,
v
);
v
=
v
.
toFixed
(
2
);
Cookie
.
setCookie
(
"
volume
"
,
v
,
{
Cookie
.
setCookie
(
"
volume
"
,
v
,
{
...
@@ -559,30 +562,189 @@ function changeVolume(cv){
...
@@ -559,30 +562,189 @@ function changeVolume(cv){
path
:
"
/
"
,
path
:
"
/
"
,
});
});
s
=
Math
.
round
(
v
*
100
);
s
=
Math
.
round
(
v
*
100
);
$
(
'
gamebgm
'
).
volume
=
v
;
$
(
'
volume_num
'
).
innerHTML
=
s
+
'
%
'
;
// 安全地设置音量,避免在播放状态变化时出错
try
{
audioElement
.
volume
=
v
;
$
(
'
nowbgmvolume
'
).
innerHTML
=
v
;
if
(
$
(
'
volume_num
'
))
{
$
(
'
volume_num
'
).
innerHTML
=
s
+
'
%
'
;
}
}
catch
(
error
)
{
console
.
log
(
'
音量设置失败:
'
+
error
.
message
);
}
}
}
function
changeBGM
(
mode
=
1
){
// 全局BGM管理器
var
bgmlist
=
JSON
.
parse
(
$
(
'
bgmlist
'
).
innerHTML
);
window
.
BGMManager
=
{
var
nowbgm
=
Math
.
round
(
$
(
'
nowbgm
'
).
innerHTML
);
isChanging
:
false
,
nowbgm
=
nowbgm
+
mode
;
currentPromise
:
null
,
if
(
nowbgm
<
0
){
pendingChange
:
null
,
nowbgm
=
bgmlist
.
length
-
1
;
isPlaying
:
false
,
}
else
{
userInteracted
:
false
,
nowbgm
=
nowbgm
%
bgmlist
.
length
;
initialized
:
false
,
// 初始化BGM管理器
init
:
function
()
{
if
(
this
.
initialized
)
return
;
this
.
initialized
=
true
;
// 在任何用户交互时尝试启动BGM
var
self
=
this
;
document
.
addEventListener
(
'
click
'
,
function
()
{
if
(
!
self
.
userInteracted
)
{
self
.
startBGM
();
}
},
{
once
:
true
});
console
.
log
(
'
BGM管理器已初始化
'
);
},
// 启动BGM播放(需要用户交互)
startBGM
:
function
()
{
if
(
this
.
userInteracted
)
return
;
this
.
userInteracted
=
true
;
var
audioElement
=
$
(
'
gamebgm
'
);
if
(
audioElement
&&
audioElement
.
paused
)
{
var
self
=
this
;
var
playPromise
=
audioElement
.
play
();
if
(
playPromise
!==
undefined
)
{
playPromise
.
then
(
function
()
{
self
.
isPlaying
=
true
;
console
.
log
(
'
BGM开始播放
'
);
}).
catch
(
function
(
error
)
{
console
.
log
(
'
BGM启动失败:
'
+
error
.
message
);
});
}
}
},
// 安全地切换BGM
changeBGM
:
function
(
mode
)
{
mode
=
mode
||
1
;
// 防止并发调用
if
(
this
.
isChanging
)
{
this
.
pendingChange
=
mode
;
console
.
log
(
'
BGM正在切换中,将在完成后执行新的切换请求
'
);
return
;
}
this
.
isChanging
=
true
;
var
self
=
this
;
try
{
var
bgmlist
=
JSON
.
parse
(
$
(
'
bgmlist
'
).
innerHTML
);
var
nowbgm
=
Math
.
round
(
$
(
'
nowbgm
'
).
innerHTML
);
nowbgm
=
nowbgm
+
mode
;
if
(
nowbgm
<
0
){
nowbgm
=
bgmlist
.
length
-
1
;
}
else
{
nowbgm
=
nowbgm
%
bgmlist
.
length
;
}
var
audioElement
=
$
(
'
gamebgm
'
);
var
sourceElement
=
$
(
'
gbgm
'
);
if
(
!
audioElement
||
!
sourceElement
)
{
this
.
isChanging
=
false
;
return
;
}
// 取消当前的播放Promise
if
(
this
.
currentPromise
)
{
audioElement
.
pause
();
this
.
currentPromise
=
null
;
}
// 暂停当前播放并重置
audioElement
.
pause
();
audioElement
.
currentTime
=
0
;
// 更新音频源
sourceElement
.
src
=
bgmlist
[
nowbgm
].
url
;
sourceElement
.
type
=
bgmlist
[
nowbgm
].
type
;
$
(
'
nowbgm
'
).
innerHTML
=
nowbgm
;
Cookie
.
setCookie
(
"
nowbgmid
"
,
bgmlist
[
nowbgm
].
id
,
{
path
:
"
/
"
,
});
var
v
=
audioElement
.
volume
;
if
(
$
(
'
bgmname
'
))
{
$
(
'
bgmname
'
).
innerHTML
=
bgmlist
[
nowbgm
].
name
;
}
// 等待一小段时间确保pause()完成
setTimeout
(
function
()
{
// 加载新音频
audioElement
.
load
();
audioElement
.
volume
=
v
;
// 等待load完成后再播放
function
tryPlay
()
{
if
(
audioElement
.
readyState
>=
3
)
{
// HAVE_FUTURE_DATA
// 使用Promise处理异步播放
self
.
currentPromise
=
audioElement
.
play
();
if
(
self
.
currentPromise
!==
undefined
)
{
self
.
currentPromise
.
then
(
function
()
{
console
.
log
(
'
BGM播放成功:
'
+
bgmlist
[
nowbgm
].
name
);
self
.
currentPromise
=
null
;
self
.
isChanging
=
false
;
self
.
isPlaying
=
true
;
// 处理待处理的切换请求
if
(
self
.
pendingChange
!==
null
)
{
var
pendingMode
=
self
.
pendingChange
;
self
.
pendingChange
=
null
;
setTimeout
(
function
()
{
self
.
changeBGM
(
pendingMode
);
},
100
);
}
}).
catch
(
function
(
error
)
{
console
.
log
(
'
BGM播放失败:
'
+
error
.
message
);
self
.
currentPromise
=
null
;
self
.
isChanging
=
false
;
// 处理待处理的切换请求
if
(
self
.
pendingChange
!==
null
)
{
var
pendingMode
=
self
.
pendingChange
;
self
.
pendingChange
=
null
;
setTimeout
(
function
()
{
self
.
changeBGM
(
pendingMode
);
},
100
);
}
});
}
else
{
self
.
isChanging
=
false
;
}
}
else
{
// 等待音频加载完成
setTimeout
(
tryPlay
,
50
);
}
}
tryPlay
();
},
100
);
}
catch
(
error
)
{
console
.
log
(
'
changeBGM错误:
'
+
error
.
message
);
this
.
isChanging
=
false
;
this
.
currentPromise
=
null
;
}
}
};
// 初始化BGM管理器
BGMManager
.
init
();
// 兼容性函数 - 调用新的BGM管理器
function
changeBGM
(
mode
)
{
if
(
window
.
BGMManager
)
{
BGMManager
.
changeBGM
(
mode
);
}
else
{
console
.
log
(
'
BGM管理器未初始化
'
);
}
}
$
(
'
gbgm
'
).
src
=
bgmlist
[
nowbgm
].
url
;
$
(
'
gbgm
'
).
type
=
bgmlist
[
nowbgm
].
type
;
$
(
'
nowbgm
'
).
innerHTML
=
nowbgm
;
Cookie
.
setCookie
(
"
nowbgmid
"
,
bgmlist
[
nowbgm
].
id
,
{
path
:
"
/
"
,
});
var
v
=
$
(
'
gamebgm
'
).
volume
;
$
(
'
bgmname
'
).
innerHTML
=
bgmlist
[
nowbgm
].
name
;
$
(
'
gamebgm
'
).
load
();
$
(
'
gamebgm
'
).
volume
=
v
;
$
(
'
gamebgm
'
).
play
();
}
}
function
changePages
(
mode
,
cPages
)
function
changePages
(
mode
,
cPages
)
...
...
templates/default/slidingpanel.htm
View file @
85d98d58
...
@@ -1507,23 +1507,128 @@
...
@@ -1507,23 +1507,128 @@
return
;
return
;
}
}
// 获取种火实时数据
fetchFireseedRealTimeData
().
then
(
function
(
realtimeData
)
{
// 再次清除现有行,确保表格是空的
while
(
statusTable
.
rows
.
length
>
1
)
{
statusTable
.
deleteRow
(
1
);
}
// 添加种火数据行
for
(
const
fsId
in
clbpara
.
fireseed
)
{
// 跳过非对象类型的条目或无效数据
if
(
!
clbpara
.
fireseed
[
fsId
]
||
typeof
clbpara
.
fireseed
[
fsId
]
!==
'
object
'
||
!
clbpara
.
fireseed
[
fsId
].
level
)
{
console
.
log
(
'
跳过无效种火数据:
'
,
fsId
,
clbpara
.
fireseed
[
fsId
]);
continue
;
}
const
fsData
=
realtimeData
[
fsId
]
||
clbpara
.
fireseed
[
fsId
];
const
row
=
statusTable
.
insertRow
();
// 名称
const
nameCell
=
row
.
insertCell
();
const
nameSpan
=
document
.
createElement
(
'
span
'
);
nameSpan
.
className
=
fsData
.
alive
===
false
?
'
red
'
:
'
yellow
'
;
nameSpan
.
textContent
=
fsData
.
name
||
'
未知种火
'
;
nameCell
.
appendChild
(
nameSpan
);
// 等级
const
levelCell
=
row
.
insertCell
();
levelCell
.
textContent
=
fsData
.
level
;
// 状态
const
modeCell
=
row
.
insertCell
();
const
modes
=
[
'
跟随
'
,
'
探物
'
,
'
索敌
'
,
'
隐藏
'
];
modeCell
.
textContent
=
modes
[
fsData
.
mode
]
||
'
未知
'
;
// 位置
const
plsCell
=
row
.
insertCell
();
const
plsName
=
getPlsName
(
fsData
.
pls
);
plsCell
.
textContent
=
plsName
;
// 生命
const
hpCell
=
row
.
insertCell
();
hpCell
.
textContent
=
fsData
.
hp
+
'
/
'
+
fsData
.
mhp
;
// 体力
const
spCell
=
row
.
insertCell
();
spCell
.
textContent
=
fsData
.
sp
+
'
/
'
+
fsData
.
msp
;
// 攻击
const
attCell
=
row
.
insertCell
();
attCell
.
textContent
=
fsData
.
att
;
// 防御
const
defCell
=
row
.
insertCell
();
defCell
.
textContent
=
fsData
.
def
;
// ID
const
idCell
=
row
.
insertCell
();
idCell
.
textContent
=
fsId
;
}
}).
catch
(
function
(
error
)
{
console
.
error
(
'
获取种火实时数据失败:
'
,
error
);
// 如果获取实时数据失败,使用原有逻辑
updateFireseedStatusTableFallback
();
});
}
// 获取种火实时数据
function
fetchFireseedRealTimeData
()
{
return
new
Promise
(
function
(
resolve
,
reject
)
{
const
xhr
=
new
XMLHttpRequest
();
xhr
.
open
(
'
GET
'
,
'
fireseed_data.php
'
,
true
);
xhr
.
onload
=
function
()
{
if
(
xhr
.
status
===
200
)
{
try
{
const
data
=
JSON
.
parse
(
xhr
.
responseText
);
if
(
data
.
error
)
{
reject
(
new
Error
(
data
.
error
));
}
else
{
resolve
(
data
);
}
}
catch
(
e
)
{
reject
(
new
Error
(
'
解析响应数据失败
'
));
}
}
else
{
reject
(
new
Error
(
'
请求失败:
'
+
xhr
.
statusText
));
}
};
xhr
.
onerror
=
function
()
{
reject
(
new
Error
(
'
网络错误
'
));
};
xhr
.
send
();
});
}
// 备用的种火状态表格更新函数(使用原有逻辑)
function
updateFireseedStatusTableFallback
()
{
const
statusTable
=
document
.
querySelector
(
'
#fireseed-tab .fireseed-table
'
);
if
(
!
statusTable
)
return
;
// 确保表格是空的
while
(
statusTable
.
rows
.
length
>
1
)
{
statusTable
.
deleteRow
(
1
);
}
// 添加种火数据行
// 添加种火数据行
for
(
const
fsId
in
clbpara
.
fireseed
)
{
for
(
const
fsId
in
clbpara
.
fireseed
)
{
// 跳过非对象类型的条目或无效数据
// 跳过非对象类型的条目或无效数据
if
(
!
clbpara
.
fireseed
[
fsId
]
||
typeof
clbpara
.
fireseed
[
fsId
]
!==
'
object
'
||
if
(
!
clbpara
.
fireseed
[
fsId
]
||
typeof
clbpara
.
fireseed
[
fsId
]
!==
'
object
'
||
!
clbpara
.
fireseed
[
fsId
].
name
||
!
clbpara
.
fireseed
[
fsId
].
level
)
{
!
clbpara
.
fireseed
[
fsId
].
level
)
{
console
.
log
(
'
跳过无效种火数据:
'
,
fsId
,
clbpara
.
fireseed
[
fsId
]);
console
.
log
(
'
跳过无效种火数据:
'
,
fsId
,
clbpara
.
fireseed
[
fsId
]);
continue
;
continue
;
}
}
const
fsData
=
clbpara
.
fireseed
[
fsId
];
const
fsData
=
clbpara
.
fireseed
[
fsId
];
const
row
=
statusTable
.
insertRow
();
const
row
=
statusTable
.
insertRow
();
// 修复:先创建行
// 名称
// 名称
const
nameCell
=
row
.
insertCell
();
const
nameCell
=
row
.
insertCell
();
const
nameSpan
=
document
.
createElement
(
'
span
'
);
const
nameSpan
=
document
.
createElement
(
'
span
'
);
nameSpan
.
className
=
'
yellow
'
;
nameSpan
.
className
=
'
yellow
'
;
nameSpan
.
textContent
=
fsData
.
name
;
nameSpan
.
textContent
=
fsData
.
name
||
'
未知种火
'
;
nameCell
.
appendChild
(
nameSpan
);
nameCell
.
appendChild
(
nameSpan
);
// 等级
// 等级
...
@@ -1537,25 +1642,24 @@
...
@@ -1537,25 +1642,24 @@
// 位置
// 位置
const
plsCell
=
row
.
insertCell
();
const
plsCell
=
row
.
insertCell
();
// 获取位置名称
const
plsName
=
getPlsName
(
fsData
.
pls
);
const
plsName
=
getPlsName
(
fsData
.
pls
);
plsCell
.
textContent
=
plsName
;
plsCell
.
textContent
=
plsName
;
// 生命
// 生命
const
hpCell
=
row
.
insertCell
();
const
hpCell
=
row
.
insertCell
();
hpCell
.
textContent
=
fsData
.
hp
+
'
/
'
+
fsData
.
mhp
;
hpCell
.
textContent
=
(
fsData
.
hp
||
0
)
+
'
/
'
+
(
fsData
.
mhp
||
0
)
;
// 体力
// 体力
const
spCell
=
row
.
insertCell
();
const
spCell
=
row
.
insertCell
();
spCell
.
textContent
=
fsData
.
sp
+
'
/
'
+
fsData
.
msp
;
spCell
.
textContent
=
(
fsData
.
sp
||
0
)
+
'
/
'
+
(
fsData
.
msp
||
0
)
;
// 攻击
// 攻击
const
attCell
=
row
.
insertCell
();
const
attCell
=
row
.
insertCell
();
attCell
.
textContent
=
fsData
.
att
;
attCell
.
textContent
=
fsData
.
att
||
0
;
// 防御
// 防御
const
defCell
=
row
.
insertCell
();
const
defCell
=
row
.
insertCell
();
defCell
.
textContent
=
fsData
.
def
;
defCell
.
textContent
=
fsData
.
def
||
0
;
// ID
// ID
const
idCell
=
row
.
insertCell
();
const
idCell
=
row
.
insertCell
();
...
@@ -1619,22 +1723,17 @@
...
@@ -1619,22 +1723,17 @@
continue
;
continue
;
}
}
// 检查必要字段
,但更宽松一些
// 检查必要字段
- 现在只检查level,因为name不再存储在clbpara中
if
(
!
fsData
.
name
&&
!
fsData
.
level
)
{
if
(
!
fsData
.
level
)
{
console
.
log
(
'
跳过无效种火数据(缺少
name和
level):
'
,
fsId
,
fsData
);
console
.
log
(
'
跳过无效种火数据(缺少level):
'
,
fsId
,
fsData
);
continue
;
continue
;
}
}
const
option
=
document
.
createElement
(
'
option
'
);
const
option
=
document
.
createElement
(
'
option
'
);
option
.
value
=
fsId
;
option
.
value
=
fsId
;
// 构建显示文本,处理可能缺失的字段
// 构建显示文本 - 使用种火ID作为标识
let
displayText
=
''
;
let
displayText
=
'
种火
'
+
fsId
;
if
(
fsData
.
name
)
{
displayText
+=
fsData
.
name
;
}
else
{
displayText
+=
'
种火
'
+
fsId
;
}
if
(
fsData
.
level
)
{
if
(
fsData
.
level
)
{
displayText
+=
'
(等级:
'
+
fsData
.
level
;
displayText
+=
'
(等级:
'
+
fsData
.
level
;
...
@@ -1666,9 +1765,9 @@
...
@@ -1666,9 +1765,9 @@
// 为每个种火创建物品列表
// 为每个种火创建物品列表
for
(
const
fsId
in
clbpara
.
fireseed
)
{
for
(
const
fsId
in
clbpara
.
fireseed
)
{
// 跳过非对象类型的条目或无效数据
// 跳过非对象类型的条目或无效数据
- 现在只检查level
if
(
!
clbpara
.
fireseed
[
fsId
]
||
typeof
clbpara
.
fireseed
[
fsId
]
!==
'
object
'
||
if
(
!
clbpara
.
fireseed
[
fsId
]
||
typeof
clbpara
.
fireseed
[
fsId
]
!==
'
object
'
||
!
clbpara
.
fireseed
[
fsId
].
name
||
!
clbpara
.
fireseed
[
fsId
].
level
)
{
!
clbpara
.
fireseed
[
fsId
].
level
)
{
console
.
log
(
'
跳过无效种火数据(物品列表):
'
,
fsId
,
clbpara
.
fireseed
[
fsId
]);
console
.
log
(
'
跳过无效种火数据(物品列表):
'
,
fsId
,
clbpara
.
fireseed
[
fsId
]);
continue
;
continue
;
}
}
...
@@ -1779,7 +1878,7 @@
...
@@ -1779,7 +1878,7 @@
let
validFireseeds
=
[];
let
validFireseeds
=
[];
for
(
const
fsId
in
clbpara
.
fireseed
)
{
for
(
const
fsId
in
clbpara
.
fireseed
)
{
if
(
clbpara
.
fireseed
[
fsId
]
&&
typeof
clbpara
.
fireseed
[
fsId
]
===
'
object
'
&&
if
(
clbpara
.
fireseed
[
fsId
]
&&
typeof
clbpara
.
fireseed
[
fsId
]
===
'
object
'
&&
clbpara
.
fireseed
[
fsId
].
name
&&
clbpara
.
fireseed
[
fsId
].
level
)
{
clbpara
.
fireseed
[
fsId
].
level
)
{
validFireseeds
.
push
(
fsId
);
validFireseeds
.
push
(
fsId
);
}
}
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment