Commit 85d98d58 authored by Nemo Ma's avatar Nemo Ma

BUGFIX: assorted fixes

parent 8dfad3ce
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. 长时间运行稳定性测试
# 枫火歌者种火数据结构修复
时间: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` 需要正确包含所有依赖的函数文件
<?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);
?>
...@@ -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;
} }
......
This diff is collapsed.
...@@ -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)
......
...@@ -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);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment