# 对话选择系统实现文档

## 概述
本文档记录了对话选择系统的设计与实现过程。对话选择系统允许玩家在对话中选择不同的选项，从而影响游戏的进程。

## 系统设计

### 数据结构
1. **对话内容**：在`gamedata/cache/dialogue_1.php`中定义，使用`$dialogues`数组存储对话内容。
2. **对话选项**：在`gamedata/cache/dialogue_1.php`中定义，使用`$dialogue_branch`数组存储对话选项。
3. **选择结果**：在`gamedata/cache/dialogue_1.php`中定义，使用`$dialogue_log`数组存储选择结果。
4. **玩家选择记录**：在玩家数据的`$clbpara['dialogue_choice']`中记录玩家的选择。

### 流程设计
1. 玩家触发对话（通常是使用物品或进入特定区域）。
2. 系统显示对话内容，玩家可以翻页查看。
3. 当对话到达最后一页时，系统显示选项。
4. 玩家选择一个选项，系统记录选择并显示相应的结果。
5. 对话结束，系统清除对话状态。

## 实现细节

### 1. 对话数据定义
在`gamedata/cache/dialogue_1.php`中定义对话内容、选项和结果：

```php
// 对话内容
$dialogues = Array(
    'choiceTestingDialog' => Array(
        0 => '这是带选择的测试对话第一页',
        1 => '这是带选择的测试对话第二页',
        2 => '请选择下面的选项：',
    ),
);

// 对话选项
$dialogue_branch = Array(
    'choiceTestingDialog' => Array(
        '选项A','选项B','选项C',
    ),
);

// 选择结果
$dialogue_log = Array(
    'choiceTestingDialog' => Array(
        0 => '你选择了选项A',
        1 => '你选择了选项B',
        2 => '你选择了选项C',
    ),
);
```

### 2. 对话界面实现
在`templates/default/dialogue.htm`中实现对话界面：

```html
<dialog id="dialogue" style="width: 460px; max-width: 90%;max-height: 80%;">
<script src="include/dialogue.js?v=<!--{eval time();}-->"></script>
<p><center>

<!-- 当前阅读页面 -->
<div id="dmarkpage" style="display: none;">0</div>
<!-- 对话框尾页 -->
<!--{eval $endpage = count($dialogues[$dialogue_id])-1;}-->
<!--{eval $maxdkey = $endpage + 1;}-->
<div id="dendpage" style="display: none;">$endpage</div>

<!-- 对白分段显示 -->
<!--{loop $dialogues[$dialogue_id] $dkey $dinfo}-->
<div id="d{$dkey}" <!--{if $dkey == 0}--> class="ach_box" style="overflow-y: auto; border:0; text-align: center; min-height: min-content; width: max-content; max-width: 560px;" <!--{else}-->  style="display: none;"  <!--{/if}-->>
    <table>
        <tr>
        <!-- 带头像对白 -->
        <!--{if isset($dialogue_icon[$dialogue_id][$dkey])}-->
            <td>
                <img style="width:140px;height:80px;" src="$dialogue_icon[$dialogue_id][$dkey]">
            </td>
            <td class="ach-cont" style="border: 1px; width:280px;height:80px;text-align: left; padding: 3% 6%;">
                $dinfo
            </td>
        <!-- 无头像对白 -->
        <!--{else}-->
            <td class="ach-cont" style="width:320px;height:80px;text-align: center; padding: 3% 6%;">
                $dinfo
            </td>
        <!--{/if}-->
        </tr>
    </table>
    <br>
    <!--{if $dkey > 0}-->
        <input type="button" class="cmdbutton" id="i" style="margin-right: 5%;" value="[I]上一页" onclick="changePages('d',-1);">
    <!--{/if}-->
    <!--{if (($dkey <= ($endpage-1)) || (isset($dialogue_branch[$dialogue_id])))}-->
        <input type="button" class="cmdbutton" id="p" value="[P]下一页" onclick="changePages('d',1);">
    <!--{elseif isset($dialogue_end[$dialogue_id])}-->
        $dialogue_end[$dialogue_id]
    <!--{/if}-->
</div>
 <!--{/loop}-->

<!-- 选择肢显示 -->
 <!--{if isset($dialogue_branch[$dialogue_id])}-->
<div id="d{$maxdkey}" style="display: none;" class="ach_box">
    <table border="0" style="text-align: center;"><tr><td style="width:320px;height:80px; text-align: center; padding: 3% 6%;">
    <div style="margin-bottom: 10px;">请选择：</div>
    <!--{loop $dialogue_branch[$dialogue_id] $bkey $binfo}-->
        <input type="button" class="cmdbutton" style="margin: 5px; display: block; width: 200px;" value="$binfo" onclick="handleDialogueChoice('{$dialogue_id}', '{$bkey}'); return false;">
     <!--{/loop}-->
    </td></tr></table>
</div>
<!--{/if}-->

</center></p>
<!--{if (isset($dialogue_branch[$dialogue_id]) || isset($dialogue_end[$dialogue_id]))}-->
    <img class="dialog-background" src="img/profile.gif" onclick="">
<!--{else}-->
    <img class="dialog-background" src="img/profile.gif" onclick="closeDialog($('dialogue'));$('command').value='end_dialogue';postCmd('gamecmd','command.php');this.disabled=true;">
<!--{/if}-->
</dialog>
```

### 3. JavaScript实现
在`include/dialogue.js`中实现对话翻页和选择处理：

```javascript
// 对话系统的JavaScript扩展

// 扩展changePages函数，处理对话选择页面
function dialogueChangePages(mode, cPages) {
    var nowpage = Number(document.getElementById(mode + 'markpage').innerHTML);
    var endpage = Number(document.getElementById(mode + 'endpage').innerHTML);
    var maxdkey = endpage + 1; // 选择页面的ID

    if(nowpage < 0 || nowpage > endpage) {
        nowpage = 0;
    }

    var nextpage = nowpage + cPages;
    document.getElementById(mode + 'markpage').innerHTML = nextpage;

    // 隐藏当前页面
    var currentPage = document.getElementById(mode + nowpage);
    if(currentPage) {
        currentPage.style.display = "none";
    }

    // 如果下一页是选择页面
    if(nextpage > endpage) {
        // 显示选择页面
        var choicePage = document.getElementById(mode + maxdkey);
        if(choicePage) {
            choicePage.style.display = "block";
        }
    } else {
        // 显示普通对话页面
        var dialoguePage = document.getElementById(mode + nextpage);
        if(dialoguePage) {
            dialoguePage.style.display = "block";
        }
    }
}

// 处理对话选择
function handleDialogueChoice(dialogueId, choiceIndex) {
    try {
        // 禁用所有选择按钮，防止重复点击
        var choiceButtons = document.querySelectorAll('#dialogue input.cmdbutton');
        if (choiceButtons && choiceButtons.length > 0) {
            for(var i = 0; i < choiceButtons.length; i++) {
                choiceButtons[i].disabled = true;
            }
        }

        // 设置命令值
        var commandInput = document.getElementById('command');
        if(commandInput) {
            commandInput.value = 'dialogue_choice ' + dialogueId + ' ' + choiceIndex;
        } else {
            return false;
        }

        // 关闭对话框
        var dialogueElement = document.getElementById('dialogue');
        if(dialogueElement) {
            try {
                // 在关闭对话框前添加一个"处理中"的提示
                var processingDiv = document.createElement('div');
                processingDiv.innerHTML = '<span style="color: yellow; font-weight: bold;">正在处理选择...</span>';
                processingDiv.style.textAlign = 'center';
                processingDiv.style.padding = '10px';
                dialogueElement.appendChild(processingDiv);

                // 延迟关闭对话框，给用户一个反馈
                setTimeout(function() {
                    try {
                        dialogueElement.close();
                    } catch (closeError) { }

                    // 在关闭对话框后再提交命令
                    setTimeout(function() {
                        try {
                            postCmd('gamecmd', 'command.php');
                        } catch (postError) {
                            // 如果提交命令出错，尝试直接提交表单
                            try {
                                document.getElementById('gamecmd').submit();
                            } catch (submitError) { }
                        }
                    }, 100);
                }, 500);
            } catch (processError) {
                // 如果处理对话框出错，直接提交命令
                postCmd('gamecmd', 'command.php');
            }
        } else {
            // 如果没有找到对话框，也要提交命令
            setTimeout(function() {
                postCmd('gamecmd', 'command.php');
            }, 100);
        }
    } catch (error) {
        // 如果出现任何错误，尝试直接提交命令
        try {
            var commandInput = document.getElementById('command');
            if(commandInput) {
                commandInput.value = 'dialogue_choice ' + dialogueId + ' ' + choiceIndex;
            }
            postCmd('gamecmd', 'command.php');
        } catch (finalError) { }
    }

    return false;
}
```

### 4. 命令处理实现
在`command.php`中实现对话选择命令处理：

```php
// 处理对话选择
if(strpos($command, 'dialogue_choice') === 0) {
    $parts = explode(' ', $command);
    if(count($parts) >= 3) {
        $dialogue_id = $parts[1];
        $choice_index = $parts[2];
        
        // 检查对话ID和选择索引是否有效
        if(isset($dialogue_branch[$dialogue_id]) && isset($dialogue_branch[$dialogue_id][$choice_index])) {
            // 记录玩家的选择
            if(!isset($clbpara['dialogue_choice'])) {
                $clbpara['dialogue_choice'] = array();
            }
            $clbpara['dialogue_choice'][$dialogue_id] = $choice_index;
            
            // 显示选择结果
            if(isset($dialogue_log[$dialogue_id][$choice_index])) {
                $log .= "<span class=\"yellow\">{$dialogue_log[$dialogue_id][$choice_index]}</span><br>";
            } else {
                $log .= "<span class=\"yellow\">你选择了：{$dialogue_branch[$dialogue_id][$choice_index]}</span><br>";
            }
            
            // 清除对话状态
            unset($clbpara['dialogue']);
            unset($clbpara['noskip_dialogue']);
            
            // 设置命令模式为命令模式，确保页面能够正确显示选择结果
            $mode = 'command';
        } else {
            $log .= "<span class=\"red\">无效的对话选择！</span><br>";
        }
    } else {
        $log .= "<span class=\"red\">对话选择格式错误！</span><br>";
    }
}
```

### 5. 对话触发实现
在`include/game/item.test.php`中实现对话触发：

```php
} elseif ($itm == '对话选择测试器') {
    // 带选择的对话测试
    $clbpara['dialogue'] = 'choiceTestingDialog';
    $clbpara['noskip_dialogue'] = 1; // 设置为不可跳过的对话
}
```

### 6. 对话框显示修复
在`game.php`中修复对话框显示：

```php
// 检查是否有对话需要显示，但如果刚刚处理了对话选择，则不显示
// 通过检查 $_POST['command'] 是否包含 'dialogue_choice' 来判断
$just_made_choice = isset($_POST['command']) && strpos($_POST['command'], 'dialogue_choice') === 0;

if(!$just_made_choice && (!empty($clbpara['dialogue']) || !empty($clbpara['noskip_dialogue'])))
{
    $opendialog = $clbpara['noskip_dialogue'] ? 'dialogue' : 'dialogue';
    if(!empty($clbpara['dialogue'])) $dialogue_id = $clbpara['dialogue'];
}
if(isset($opendialog))
{
    $log.="<script>
    var dialogElement = document.getElementById('{$opendialog}');
    if(dialogElement && dialogElement.showModal) {
        dialogElement.showModal();
    }
    </script>";
}
```

## 修复记录

### 1. 模板语法错误修复
在`templates/default/dialogue.htm`文件中，第2行使用了原始的PHP代码`<?php echo time(); ?>`，而不是模板系统的语法`{eval time()}`。这导致模板编译后的文件中出现了语法错误。

修复方法：将原始的PHP代码替换为模板系统的语法。

```html
<script src="include/dialogue.js?v=<!--{eval time();}-->"></script>
```

### 2. 选择页面显示修复
在`templates/default/dialogue.htm`中，添加了`$maxdkey`变量的定义，用于确定选择页面的ID。

```html
<!--{eval $endpage = count($dialogues[$dialogue_id])-1;}-->
<!--{eval $maxdkey = $endpage + 1;}-->
```

### 3. 对话框关闭后重新打开问题修复
在`game.php`中，添加了检查，确保在处理完对话选择后不会重新打开对话框。

```php
$just_made_choice = isset($_POST['command']) && strpos($_POST['command'], 'dialogue_choice') === 0;

if(!$just_made_choice && (!empty($clbpara['dialogue']) || !empty($clbpara['noskip_dialogue'])))
{
    // ...
}
```

### 4. 选择按钮样式改进
在`templates/default/dialogue.htm`中，改进了选择按钮的样式，使其更加明显。

```html
<input type="button" class="cmdbutton" style="margin: 5px; display: block; width: 200px;" value="$binfo" onclick="handleDialogueChoice('{$dialogue_id}', '{$bkey}'); return false;">
```

## 使用方法

### 1. 定义对话内容
在`gamedata/cache/dialogue_1.php`中定义对话内容、选项和结果：

```php
// 对话内容
$dialogues = Array(
    'myDialogue' => Array(
        0 => '这是第一页对话',
        1 => '这是第二页对话',
        2 => '请选择下面的选项：',
    ),
);

// 对话选项
$dialogue_branch = Array(
    'myDialogue' => Array(
        '选项A','选项B','选项C',
    ),
);

// 选择结果
$dialogue_log = Array(
    'myDialogue' => Array(
        0 => '你选择了选项A',
        1 => '你选择了选项B',
        2 => '你选择了选项C',
    ),
);
```

### 2. 触发对话
在游戏代码中，使用以下代码触发对话：

```php
$clbpara['dialogue'] = 'myDialogue';
$clbpara['noskip_dialogue'] = 1; // 设置为不可跳过的对话
```

### 3. 处理选择结果
在游戏代码中，使用以下代码处理选择结果：

```php
if(isset($clbpara['dialogue_choice']['myDialogue'])) {
    $choice = $clbpara['dialogue_choice']['myDialogue'];
    if($choice == 0) {
        // 处理选项A的结果
    } elseif($choice == 1) {
        // 处理选项B的结果
    } elseif($choice == 2) {
        // 处理选项C的结果
    }
}
```

## 注意事项

1. 对话ID必须唯一，且在`$dialogues`、`$dialogue_branch`和`$dialogue_log`中保持一致。
2. 选择索引必须从0开始，且在`$dialogue_branch`和`$dialogue_log`中保持一致。
3. 如果不需要显示选择结果，可以不定义`$dialogue_log`。
4. 如果需要对话框不可跳过，必须设置`$clbpara['noskip_dialogue'] = 1`。
5. 在处理选择结果时，必须检查`$clbpara['dialogue_choice']`是否存在，以避免错误。

## 未来改进

1. 添加对话分支功能，根据玩家的选择显示不同的对话内容。
2. 添加对话条件功能，根据玩家的状态显示不同的选项。
3. 添加对话图标功能，为对话添加头像或图标。
4. 添加对话音效功能，为对话添加音效。
5. 添加对话动画功能，为对话添加动画效果。
