505 lines
15 KiB
HTML
505 lines
15 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>凡人修仙传 - 文字版</title>
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css">
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
background: #1a1a2e;
|
|
min-height: 100vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-family: 'Consolas', 'Monaco', monospace;
|
|
}
|
|
|
|
.container {
|
|
width: 100%;
|
|
max-width: 900px;
|
|
padding: 20px;
|
|
}
|
|
|
|
.header {
|
|
text-align: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.header h1 {
|
|
color: #eee;
|
|
font-size: 24px;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.header p {
|
|
color: #888;
|
|
font-size: 14px;
|
|
}
|
|
|
|
/* 登录界面 */
|
|
#auth-panel {
|
|
background: #16213e;
|
|
border-radius: 10px;
|
|
padding: 30px;
|
|
max-width: 400px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
#auth-panel h2 {
|
|
color: #eee;
|
|
text-align: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.form-group label {
|
|
display: block;
|
|
color: #aaa;
|
|
margin-bottom: 5px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.form-group input {
|
|
width: 100%;
|
|
padding: 10px 15px;
|
|
border: 1px solid #0f3460;
|
|
border-radius: 5px;
|
|
background: #1a1a2e;
|
|
color: #eee;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.form-group input:focus {
|
|
outline: none;
|
|
border-color: #e94560;
|
|
}
|
|
|
|
.btn-group {
|
|
display: flex;
|
|
gap: 10px;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.btn {
|
|
flex: 1;
|
|
padding: 12px;
|
|
border: none;
|
|
border-radius: 5px;
|
|
font-size: 16px;
|
|
cursor: pointer;
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.btn-primary {
|
|
background: #e94560;
|
|
color: white;
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
background: #ff6b6b;
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: #0f3460;
|
|
color: white;
|
|
}
|
|
|
|
.btn-secondary:hover {
|
|
background: #16213e;
|
|
}
|
|
|
|
.message {
|
|
padding: 10px;
|
|
border-radius: 5px;
|
|
margin-bottom: 15px;
|
|
text-align: center;
|
|
display: none;
|
|
}
|
|
|
|
.message.error {
|
|
background: rgba(233, 69, 96, 0.2);
|
|
color: #e94560;
|
|
display: block;
|
|
}
|
|
|
|
.message.success {
|
|
background: rgba(46, 213, 115, 0.2);
|
|
color: #2ed573;
|
|
display: block;
|
|
}
|
|
|
|
/* 游戏终端 */
|
|
#game-panel {
|
|
display: none;
|
|
}
|
|
|
|
.terminal-header {
|
|
background: #16213e;
|
|
padding: 10px 15px;
|
|
border-radius: 10px 10px 0 0;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.terminal-header .user-info {
|
|
color: #2ed573;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.terminal-header .logout-btn {
|
|
background: #e94560;
|
|
color: white;
|
|
border: none;
|
|
padding: 5px 15px;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.terminal-header .logout-btn:hover {
|
|
background: #ff6b6b;
|
|
}
|
|
|
|
#terminal {
|
|
background: #0d0d0d;
|
|
border-radius: 0 0 10px 10px;
|
|
padding: 10px;
|
|
}
|
|
|
|
.input-area {
|
|
background: #16213e;
|
|
padding: 10px;
|
|
border-radius: 0 0 10px 10px;
|
|
display: flex;
|
|
gap: 10px;
|
|
}
|
|
|
|
.input-area input {
|
|
flex: 1;
|
|
padding: 10px;
|
|
border: 1px solid #0f3460;
|
|
border-radius: 5px;
|
|
background: #1a1a2e;
|
|
color: #eee;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.input-area input:focus {
|
|
outline: none;
|
|
border-color: #e94560;
|
|
}
|
|
|
|
.input-area button {
|
|
padding: 10px 20px;
|
|
background: #e94560;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.quick-buttons {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 5px;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.quick-btn {
|
|
padding: 8px 15px;
|
|
background: #0f3460;
|
|
color: #eee;
|
|
border: none;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.quick-btn:hover {
|
|
background: #16213e;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<h1>凡人修仙传 - 文字版</h1>
|
|
<p>Web Terminal Edition</p>
|
|
</div>
|
|
|
|
<!-- 登录/注册面板 -->
|
|
<div id="auth-panel">
|
|
<h2>欢迎</h2>
|
|
<div id="auth-message" class="message"></div>
|
|
<div class="form-group">
|
|
<label>用户名</label>
|
|
<input type="text" id="username" placeholder="请输入用户名">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>密码</label>
|
|
<input type="password" id="password" placeholder="请输入密码">
|
|
</div>
|
|
<div class="btn-group">
|
|
<button class="btn btn-primary" onclick="login()">登录</button>
|
|
<button class="btn btn-secondary" onclick="register()">注册</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 游戏面板 -->
|
|
<div id="game-panel">
|
|
<div class="terminal-header">
|
|
<span class="user-info">玩家: <span id="player-name">-</span></span>
|
|
<button class="logout-btn" onclick="logout()">退出登录</button>
|
|
</div>
|
|
<div id="terminal"></div>
|
|
<div class="input-area">
|
|
<input type="text" id="game-input" placeholder="输入命令..." onkeypress="handleKeyPress(event)">
|
|
<button onclick="sendInput()">发送</button>
|
|
</div>
|
|
<div class="quick-buttons">
|
|
<button class="quick-btn" onclick="quickSend('1')">1.战斗</button>
|
|
<button class="quick-btn" onclick="quickSend('2')">2.属性</button>
|
|
<button class="quick-btn" onclick="quickSend('3')">3.背包</button>
|
|
<button class="quick-btn" onclick="quickSend('4')">4.故人</button>
|
|
<button class="quick-btn" onclick="quickSend('5')">5.同伴</button>
|
|
<button class="quick-btn" onclick="quickSend('6')">6.天赋</button>
|
|
<button class="quick-btn" onclick="quickSend('7')">7.地图</button>
|
|
<button class="quick-btn" onclick="quickSend('8')">8.休息</button>
|
|
<button class="quick-btn" onclick="quickSend('0')">0.返回</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.min.js"></script>
|
|
<script>
|
|
let terminal = null;
|
|
let fitAddon = null;
|
|
let isLoggedIn = false;
|
|
|
|
// 初始化终端
|
|
function initTerminal() {
|
|
terminal = new Terminal({
|
|
cursorBlink: true,
|
|
fontSize: 16,
|
|
fontFamily: 'Consolas, Monaco, monospace',
|
|
theme: {
|
|
background: '#0d0d0d',
|
|
foreground: '#eee',
|
|
cursor: '#e94560',
|
|
cursorAccent: '#0d0d0d',
|
|
selection: 'rgba(233, 69, 96, 0.3)',
|
|
black: '#1a1a2e',
|
|
red: '#e94560',
|
|
green: '#2ed573',
|
|
yellow: '#ffa502',
|
|
blue: '#70a1ff',
|
|
magenta: '#ff6b81',
|
|
cyan: '#1e90ff',
|
|
white: '#eee',
|
|
brightBlack: '#666',
|
|
brightRed: '#ff6b6b',
|
|
brightGreen: '#7bed9f',
|
|
brightYellow: '#ffda79',
|
|
brightBlue: '#a4b0be',
|
|
brightMagenta: '#ff7f9f',
|
|
brightCyan: '#34ace0',
|
|
brightWhite: '#fff'
|
|
},
|
|
rows: 24,
|
|
cols: 80
|
|
});
|
|
|
|
fitAddon = new FitAddon.FitAddon();
|
|
terminal.loadAddon(fitAddon);
|
|
terminal.open(document.getElementById('terminal'));
|
|
fitAddon.fit();
|
|
|
|
window.addEventListener('resize', () => {
|
|
fitAddon.fit();
|
|
});
|
|
}
|
|
|
|
// API 请求
|
|
async function api(endpoint, data = {}) {
|
|
try {
|
|
const response = await fetch('/api/' + endpoint, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(data),
|
|
credentials: 'include'
|
|
});
|
|
return await response.json();
|
|
} catch (error) {
|
|
console.error('API Error:', error);
|
|
return { success: false, message: '网络错误' };
|
|
}
|
|
}
|
|
|
|
// 显示消息
|
|
function showMessage(message, type = 'error') {
|
|
const el = document.getElementById('auth-message');
|
|
el.textContent = message;
|
|
el.className = 'message ' + type;
|
|
}
|
|
|
|
// 登录
|
|
async function login() {
|
|
const username = document.getElementById('username').value.trim();
|
|
const password = document.getElementById('password').value;
|
|
|
|
if (!username || !password) {
|
|
showMessage('请输入用户名和密码');
|
|
return;
|
|
}
|
|
|
|
const result = await api('login', { username, password });
|
|
|
|
if (result.success) {
|
|
showMessage('登录成功!', 'success');
|
|
setTimeout(() => {
|
|
enterGame(username);
|
|
}, 500);
|
|
} else {
|
|
showMessage(result.message || '登录失败');
|
|
}
|
|
}
|
|
|
|
// 注册
|
|
async function register() {
|
|
const username = document.getElementById('username').value.trim();
|
|
const password = document.getElementById('password').value;
|
|
|
|
if (!username || !password) {
|
|
showMessage('请输入用户名和密码');
|
|
return;
|
|
}
|
|
|
|
const result = await api('register', { username, password });
|
|
|
|
if (result.success) {
|
|
showMessage('注册成功!自动登录中...', 'success');
|
|
setTimeout(() => {
|
|
enterGame(username);
|
|
}, 500);
|
|
} else {
|
|
showMessage(result.message || '注册失败');
|
|
}
|
|
}
|
|
|
|
// 退出登录
|
|
async function logout() {
|
|
await api('logout');
|
|
isLoggedIn = false;
|
|
document.getElementById('auth-panel').style.display = 'block';
|
|
document.getElementById('game-panel').style.display = 'none';
|
|
document.getElementById('password').value = '';
|
|
showMessage('', '');
|
|
}
|
|
|
|
// 进入游戏
|
|
function enterGame(username) {
|
|
isLoggedIn = true;
|
|
document.getElementById('auth-panel').style.display = 'none';
|
|
document.getElementById('game-panel').style.display = 'block';
|
|
document.getElementById('player-name').textContent = username;
|
|
|
|
if (!terminal) {
|
|
initTerminal();
|
|
}
|
|
|
|
terminal.clear();
|
|
terminal.writeln('\x1b[33m正在加载游戏...\x1b[0m');
|
|
|
|
// 渲染游戏界面
|
|
renderGame();
|
|
|
|
// 聚焦输入框
|
|
document.getElementById('game-input').focus();
|
|
}
|
|
|
|
// 渲染游戏
|
|
async function renderGame() {
|
|
const result = await api('game/render');
|
|
|
|
if (result.success) {
|
|
terminal.clear();
|
|
const lines = result.output.split('\n');
|
|
lines.forEach(line => {
|
|
terminal.writeln(line);
|
|
});
|
|
} else {
|
|
terminal.writeln('\x1b[31m' + (result.message || '加载失败') + '\x1b[0m');
|
|
if (result.message === '请先登录') {
|
|
logout();
|
|
}
|
|
}
|
|
}
|
|
|
|
// 发送输入
|
|
async function sendInput() {
|
|
const inputEl = document.getElementById('game-input');
|
|
const input = inputEl.value.trim();
|
|
inputEl.value = '';
|
|
|
|
if (!input) return;
|
|
|
|
terminal.writeln('\x1b[36m> ' + input + '\x1b[0m');
|
|
terminal.writeln('');
|
|
|
|
const result = await api('game/input', { input });
|
|
|
|
if (result.success) {
|
|
terminal.clear();
|
|
const lines = result.output.split('\n');
|
|
lines.forEach(line => {
|
|
terminal.writeln(line);
|
|
});
|
|
} else {
|
|
terminal.writeln('\x1b[31m' + (result.message || '操作失败') + '\x1b[0m');
|
|
if (result.message === '请先登录') {
|
|
logout();
|
|
}
|
|
}
|
|
|
|
inputEl.focus();
|
|
}
|
|
|
|
// 快捷按钮发送
|
|
function quickSend(value) {
|
|
document.getElementById('game-input').value = value;
|
|
sendInput();
|
|
}
|
|
|
|
// 按键处理
|
|
function handleKeyPress(event) {
|
|
if (event.key === 'Enter') {
|
|
sendInput();
|
|
}
|
|
}
|
|
|
|
// 页面加载时检查登录状态
|
|
window.onload = async function() {
|
|
const result = await api('status');
|
|
if (result.success && result.loggedIn) {
|
|
enterGame(result.username);
|
|
}
|
|
};
|
|
</script>
|
|
</body>
|
|
</html>
|