feat(web): 适配行动点系统
- 居民卡片显示行动点(AP 圆点指示器) - 添加行动反馈 Toast 提示(成功/失败) - 3 秒后自动消失的反馈动画 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -86,6 +86,20 @@
|
||||
.emotion-calm { background: #1e3a5f; color: #93c5fd; }
|
||||
.agent-info { font-size: 13px; color: #a1a1aa; }
|
||||
|
||||
/* 行动点 */
|
||||
.agent-ap { display: flex; align-items: center; gap: 4px; margin-top: 6px; }
|
||||
.ap-dots { display: flex; gap: 3px; }
|
||||
.ap-dot { width: 8px; height: 8px; border-radius: 50%; background: #3f3f46; }
|
||||
.ap-dot.filled { background: #60a5fa; }
|
||||
.ap-label { font-size: 11px; color: #71717a; }
|
||||
|
||||
/* 行动反馈提示 */
|
||||
#action-feedback { position: fixed; bottom: 20px; right: 20px; z-index: 100; }
|
||||
.feedback-toast { padding: 10px 16px; border-radius: 8px; margin-top: 8px; font-size: 13px; animation: fadeIn 0.3s ease; }
|
||||
.feedback-toast.success { background: #166534; color: #86efac; border: 1px solid #22c55e; }
|
||||
.feedback-toast.fail { background: #7f1d1d; color: #fca5a5; border: 1px solid #ef4444; }
|
||||
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
|
||||
|
||||
/* 行动日志 */
|
||||
.actions-list { max-height: 300px; overflow-y: auto; }
|
||||
.action-item { padding: 8px; background: #27272a; border-radius: 6px; margin-bottom: 6px; font-size: 13px; }
|
||||
@@ -192,6 +206,9 @@
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<div class="main-grid">
|
||||
|
||||
<!-- 行动反馈提示 -->
|
||||
<div id="action-feedback"></div>
|
||||
<div>
|
||||
<div class="card" style="margin-bottom: 16px;">
|
||||
<div class="card-title">世界状态</div>
|
||||
@@ -270,6 +287,21 @@
|
||||
const weatherMap = { sunny: '☀️', rainy: '🌧️' };
|
||||
const actionHistory = [];
|
||||
|
||||
// 显示行动反馈提示
|
||||
function showActionFeedbacks(feedbacks) {
|
||||
const container = document.getElementById('action-feedback');
|
||||
feedbacks.forEach(fb => {
|
||||
if (!fb.user) return;
|
||||
const cls = fb.success ? 'success' : 'fail';
|
||||
const icon = fb.success ? '✓' : '✗';
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `feedback-toast ${cls}`;
|
||||
toast.textContent = `${icon} ${fb.user}: ${fb.reason} (AP: ${fb.remaining_ap})`;
|
||||
container.appendChild(toast);
|
||||
setTimeout(() => toast.remove(), 3000);
|
||||
});
|
||||
}
|
||||
|
||||
// 渲染技能列表
|
||||
function renderSkills(skills, names) {
|
||||
return Object.entries(skills).map(([id, s]) => {
|
||||
@@ -284,13 +316,18 @@
|
||||
}
|
||||
|
||||
function updateUI(data) {
|
||||
const { world_state, actions, global_event, triggered_faction_event, story_event } = data;
|
||||
const { world_state, actions, global_event, triggered_faction_event, story_event, action_feedbacks } = data;
|
||||
|
||||
// 世界状态
|
||||
document.getElementById('tick').textContent = world_state.tick;
|
||||
document.getElementById('weather').textContent = weatherMap[world_state.weather] || world_state.weather;
|
||||
document.getElementById('mood').textContent = world_state.town_mood;
|
||||
|
||||
// 行动反馈提示
|
||||
if (action_feedbacks && action_feedbacks.length > 0) {
|
||||
showActionFeedbacks(action_feedbacks);
|
||||
}
|
||||
|
||||
// ① 能量条
|
||||
if (world_state.global_meter) {
|
||||
const meter = world_state.global_meter;
|
||||
@@ -412,12 +449,23 @@
|
||||
const agentsEl = document.getElementById('agents');
|
||||
agentsEl.innerHTML = '';
|
||||
for (const [id, agent] of Object.entries(world_state.agents)) {
|
||||
const ap = agent.action_points || 0;
|
||||
const maxAp = agent.max_action_points || 3;
|
||||
const apDots = Array(maxAp).fill(0).map((_, i) =>
|
||||
`<span class="ap-dot ${i < ap ? 'filled' : ''}"></span>`
|
||||
).join('');
|
||||
|
||||
agentsEl.innerHTML += `
|
||||
<div class="agent">
|
||||
<div class="agent-header">
|
||||
<span class="agent-name">${id}</span>
|
||||
<span class="agent-emotion emotion-${agent.emotion}">${agent.emotion}</span>
|
||||
</div>
|
||||
<div class="agent-ap">
|
||||
<span class="ap-label">AP:</span>
|
||||
<div class="ap-dots">${apDots}</div>
|
||||
<span class="ap-label">${ap}/${maxAp}</span>
|
||||
</div>
|
||||
<div class="agent-info">记忆: ${agent.memory.slice(-2).join(' | ') || '-'}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user