change
This commit is contained in:
695
demo2.html
Normal file
695
demo2.html
Normal file
@@ -0,0 +1,695 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>天选打工人 - 系统对战Demo</title>
|
||||
<style>
|
||||
:root {
|
||||
--primary: #00ff9d; /* 赛博绿 */
|
||||
--danger: #ff0055; /* 故障红 */
|
||||
--bg: #0a0a0a;
|
||||
--card-bg: #1a1a1a;
|
||||
--text: #e0e0e0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Consolas", "Courier New", monospace;
|
||||
background-color: var(--bg);
|
||||
color: var(--text);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* --- 顶部:敌人区域 --- */
|
||||
#enemy-area {
|
||||
flex: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-bottom: 1px solid #333;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: #333;
|
||||
border: 2px solid var(--danger);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 40px;
|
||||
margin-bottom: 10px;
|
||||
transition: transform 0.1s;
|
||||
}
|
||||
|
||||
.hp-bar-container {
|
||||
width: 200px;
|
||||
height: 20px;
|
||||
background: #333;
|
||||
border: 1px solid #555;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.hp-bar-fill {
|
||||
height: 100%;
|
||||
background: var(--danger);
|
||||
width: 100%;
|
||||
transition: width 0.3s ease-out;
|
||||
}
|
||||
|
||||
.intent-bubble {
|
||||
margin-top: 10px;
|
||||
background: #222;
|
||||
padding: 5px 10px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--danger);
|
||||
color: var(--danger);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* --- 中间:战斗日志 (LLM文字流) --- */
|
||||
#battle-log {
|
||||
flex: 3;
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
background: rgba(0, 20, 10, 0.5);
|
||||
border-bottom: 1px solid #333;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.log-entry {
|
||||
margin-bottom: 8px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.log-system {
|
||||
color: var(--primary);
|
||||
font-weight: bold;
|
||||
}
|
||||
.log-player {
|
||||
color: #fff;
|
||||
}
|
||||
.log-enemy {
|
||||
color: var(--danger);
|
||||
}
|
||||
.log-desc {
|
||||
color: #888;
|
||||
font-style: italic;
|
||||
font-size: 0.9em;
|
||||
margin-left: 10px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* --- 底部:玩家区域 --- */
|
||||
#player-area {
|
||||
flex: 3;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #111;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#stats-bar {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding: 10px;
|
||||
background: #000;
|
||||
border: 1px solid #333;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
.icon {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
/* --- 卡牌区域 --- */
|
||||
#hand-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
flex-grow: 1;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.card {
|
||||
width: 120px;
|
||||
height: 160px;
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--primary);
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-20px) scale(1.05);
|
||||
box-shadow: 0 0 15px rgba(0, 255, 157, 0.3);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.card-cost {
|
||||
background: var(--primary);
|
||||
color: #000;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
font-weight: bold;
|
||||
color: var(--primary);
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
font-size: 11px;
|
||||
color: #aaa;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.card-type-attack {
|
||||
border-color: #ff4444;
|
||||
}
|
||||
.card-type-skill {
|
||||
border-color: #4488ff;
|
||||
}
|
||||
.card-type-money {
|
||||
border-color: #ffd700;
|
||||
box-shadow: 0 0 5px rgba(255, 215, 0, 0.2);
|
||||
}
|
||||
|
||||
/* 按钮 */
|
||||
#end-turn-btn {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
bottom: 100px;
|
||||
padding: 15px 30px;
|
||||
background: #333;
|
||||
color: white;
|
||||
border: 1px solid #666;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
font-weight: bold;
|
||||
}
|
||||
#end-turn-btn:hover {
|
||||
background: #444;
|
||||
}
|
||||
|
||||
/* 动画特效 */
|
||||
@keyframes shake {
|
||||
0% {
|
||||
transform: translate(1px, 1px) rotate(0deg);
|
||||
}
|
||||
10% {
|
||||
transform: translate(-1px, -2px) rotate(-1deg);
|
||||
}
|
||||
20% {
|
||||
transform: translate(-3px, 0px) rotate(1deg);
|
||||
}
|
||||
30% {
|
||||
transform: translate(3px, 2px) rotate(0deg);
|
||||
}
|
||||
40% {
|
||||
transform: translate(1px, -1px) rotate(1deg);
|
||||
}
|
||||
50% {
|
||||
transform: translate(-1px, 2px) rotate(-1deg);
|
||||
}
|
||||
60% {
|
||||
transform: translate(-3px, 1px) rotate(0deg);
|
||||
}
|
||||
70% {
|
||||
transform: translate(3px, 1px) rotate(-1deg);
|
||||
}
|
||||
80% {
|
||||
transform: translate(-1px, -1px) rotate(1deg);
|
||||
}
|
||||
90% {
|
||||
transform: translate(1px, 2px) rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: translate(1px, -2px) rotate(-1deg);
|
||||
}
|
||||
}
|
||||
|
||||
.shake-anim {
|
||||
animation: shake 0.5s;
|
||||
}
|
||||
|
||||
.damage-text {
|
||||
position: absolute;
|
||||
color: red;
|
||||
font-size: 30px;
|
||||
font-weight: bold;
|
||||
animation: floatUp 1s forwards;
|
||||
}
|
||||
|
||||
@keyframes floatUp {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(-50px);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 状态提示 */
|
||||
#game-over {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 100;
|
||||
}
|
||||
#game-over h1 {
|
||||
color: var(--primary);
|
||||
font-size: 40px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="enemy-area">
|
||||
<div class="avatar" id="enemy-avatar">🤡</div>
|
||||
<h3 id="enemy-name">势利眼班长</h3>
|
||||
<div class="hp-bar-container">
|
||||
<div class="hp-bar-fill" id="enemy-hp-bar"></div>
|
||||
</div>
|
||||
<div style="margin-top: 5px; font-size: 12px">
|
||||
面子 (HP): <span id="enemy-hp-text">80</span>/80
|
||||
</div>
|
||||
|
||||
<div class="intent-bubble" id="enemy-intent">
|
||||
💬 正在酝酿一句很难听的话 (10点伤害)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="battle-log">
|
||||
<div class="log-entry log-system">> 系统初始化完成...</div>
|
||||
<div class="log-entry log-system">> 目标锁定:[势利眼班长]</div>
|
||||
<div class="log-entry log-system">> 任务:让他当众出丑。</div>
|
||||
</div>
|
||||
|
||||
<div id="player-area">
|
||||
<div id="stats-bar">
|
||||
<div class="stat-item" style="color: #ff4444">
|
||||
<span class="icon">❤️</span> 面子:
|
||||
<span id="player-hp">100</span>
|
||||
</div>
|
||||
<div class="stat-item" style="color: #4488ff">
|
||||
<span class="icon">🛡️</span> 厚脸皮:
|
||||
<span id="player-block">0</span>
|
||||
</div>
|
||||
<div class="stat-item" style="color: #00ff9d">
|
||||
<span class="icon">⚡</span> 精力:
|
||||
<span id="player-energy">3</span>/3
|
||||
</div>
|
||||
<div class="stat-item" style="color: #ffd700">
|
||||
<span class="icon">💰</span> 余额: ¥<span id="player-money"
|
||||
>5000</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="hand-container"></div>
|
||||
</div>
|
||||
|
||||
<button id="end-turn-btn" onclick="endTurn()">结束回合</button>
|
||||
|
||||
<div id="game-over">
|
||||
<h1 id="game-result">任务完成</h1>
|
||||
<button
|
||||
onclick="location.reload()"
|
||||
style="padding: 10px 20px; cursor: pointer"
|
||||
>
|
||||
重新开始
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* --- 1. 游戏数据设定 (配置表) --- */
|
||||
|
||||
const GAME_STATE = {
|
||||
player: {
|
||||
hp: 100,
|
||||
maxHp: 100,
|
||||
block: 0,
|
||||
energy: 3,
|
||||
maxEnergy: 3,
|
||||
money: 5000,
|
||||
},
|
||||
enemy: {
|
||||
name: "势利眼班长",
|
||||
hp: 80,
|
||||
maxHp: 80,
|
||||
nextDmg: 10,
|
||||
avatar: "🤡",
|
||||
},
|
||||
hand: [],
|
||||
deck: [],
|
||||
turn: 1,
|
||||
isGameOver: false,
|
||||
};
|
||||
|
||||
// 模拟 LLM 的文案库 (实际开发中这里接 API)
|
||||
const LLM_TEXTS = {
|
||||
start: [
|
||||
"班长看了看你的衣服,露出了鄙夷的眼神。",
|
||||
"系统检测到装逼场力波动,请宿主做好准备。",
|
||||
],
|
||||
atk_normal: [
|
||||
"你冷冷一笑:'这就是你的实力?'",
|
||||
"你指出了他话里的逻辑漏洞,全场鸦雀无声。",
|
||||
"你说:'去年你借我的五百块还没还呢。'",
|
||||
],
|
||||
atk_money: [
|
||||
"你打开手机银行,语音播报:'到账一百万元'。",
|
||||
"你随手掏出一把车钥匙扔在桌上:'挪下车。'",
|
||||
"你用钱扇了扇风:'这屋里穷酸气太重。'",
|
||||
],
|
||||
defend: [
|
||||
"你假装在回消息,完全无视了他的废话。",
|
||||
"你使用了【左耳进右耳出】,他的嘲讽无效。",
|
||||
"你微笑着看着他,就像看着一只猴子。",
|
||||
],
|
||||
enemy_atk: [
|
||||
"班长嘲笑道:'听说你还在租房住?'",
|
||||
"班长向大家展示他的劳力士绿水鬼。",
|
||||
"班长说:'服务员,给这位加把椅子,别让他站着。'",
|
||||
],
|
||||
};
|
||||
|
||||
// 卡牌数据库 (原型)
|
||||
const CARD_DATABASE = [
|
||||
{
|
||||
id: "atk_1",
|
||||
name: "阴阳怪气",
|
||||
type: "attack",
|
||||
cost: 1,
|
||||
val: 8,
|
||||
desc: "造成 8 点面子伤害",
|
||||
flavorType: "atk_normal",
|
||||
},
|
||||
{
|
||||
id: "atk_2",
|
||||
name: "揭露黑历史",
|
||||
type: "attack",
|
||||
cost: 2,
|
||||
val: 18,
|
||||
desc: "造成 18 点面子伤害",
|
||||
flavorType: "atk_normal",
|
||||
},
|
||||
{
|
||||
id: "def_1",
|
||||
name: "装聋作哑",
|
||||
type: "skill",
|
||||
cost: 1,
|
||||
val: 8,
|
||||
desc: "获得 8 点厚脸皮(护盾)",
|
||||
flavorType: "defend",
|
||||
},
|
||||
{
|
||||
id: "money_1",
|
||||
name: "钞能力打击",
|
||||
type: "money",
|
||||
cost: 1,
|
||||
val: 25,
|
||||
moneyCost: 500,
|
||||
desc: "消耗¥500,造成 25 点伤害",
|
||||
flavorType: "atk_money",
|
||||
},
|
||||
{
|
||||
id: "def_2",
|
||||
name: "战术喝水",
|
||||
type: "skill",
|
||||
cost: 0,
|
||||
val: 4,
|
||||
desc: "获得 4 点厚脸皮(0费)",
|
||||
flavorType: "defend",
|
||||
},
|
||||
];
|
||||
|
||||
/* --- 2. 核心逻辑函数 --- */
|
||||
|
||||
function initGame() {
|
||||
updateUI();
|
||||
startPlayerTurn();
|
||||
log(getRandomText(LLM_TEXTS.start), "system");
|
||||
}
|
||||
|
||||
function startPlayerTurn() {
|
||||
GAME_STATE.player.energy = GAME_STATE.player.maxEnergy;
|
||||
GAME_STATE.player.block = 0; // 回合开始护盾清零(简化规则)
|
||||
|
||||
// 简单的抽牌逻辑:随机抽 4 张
|
||||
GAME_STATE.hand = [];
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const randomCard =
|
||||
CARD_DATABASE[
|
||||
Math.floor(Math.random() * CARD_DATABASE.length)
|
||||
];
|
||||
// 深拷贝以防止修改原数据
|
||||
GAME_STATE.hand.push({
|
||||
...randomCard,
|
||||
uid: Date.now() + i,
|
||||
});
|
||||
}
|
||||
|
||||
updateUI();
|
||||
log(`=== 第 ${GAME_STATE.turn} 回合 ===`, "system");
|
||||
}
|
||||
|
||||
function playCard(index) {
|
||||
if (GAME_STATE.isGameOver) return;
|
||||
|
||||
const card = GAME_STATE.hand[index];
|
||||
const player = GAME_STATE.player;
|
||||
|
||||
// 1. 检查资源
|
||||
if (player.energy < card.cost) {
|
||||
log("精力不足!", "system");
|
||||
shakeScreen("stats-bar");
|
||||
return;
|
||||
}
|
||||
if (card.type === "money" && player.money < card.moneyCost) {
|
||||
log("余额不足!无法使用钞能力!", "system");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 扣除资源
|
||||
player.energy -= card.cost;
|
||||
if (card.type === "money") player.money -= card.moneyCost;
|
||||
|
||||
// 3. 执行效果
|
||||
let logMsg = "";
|
||||
if (card.type === "attack" || card.type === "money") {
|
||||
dealDamage(card.val);
|
||||
logMsg = getRandomText(LLM_TEXTS[card.flavorType]);
|
||||
} else if (card.type === "skill") {
|
||||
player.block += card.val;
|
||||
logMsg = getRandomText(LLM_TEXTS[card.flavorType]);
|
||||
}
|
||||
|
||||
// 4. 移除手牌 & 更新UI
|
||||
GAME_STATE.hand.splice(index, 1);
|
||||
log(`你使用了【${card.name}】`, "player");
|
||||
log(logMsg, "desc");
|
||||
|
||||
updateUI();
|
||||
checkWin();
|
||||
}
|
||||
|
||||
function dealDamage(amount) {
|
||||
GAME_STATE.enemy.hp -= amount;
|
||||
if (GAME_STATE.enemy.hp < 0) GAME_STATE.enemy.hp = 0;
|
||||
|
||||
// 视觉特效
|
||||
const enemyEl = document.getElementById("enemy-avatar");
|
||||
enemyEl.classList.add("shake-anim");
|
||||
setTimeout(() => enemyEl.classList.remove("shake-anim"), 500);
|
||||
|
||||
// 飘字
|
||||
showFloatingText(`-${amount}`);
|
||||
}
|
||||
|
||||
function endTurn() {
|
||||
if (GAME_STATE.isGameOver) return;
|
||||
|
||||
// 敌人行动
|
||||
setTimeout(() => {
|
||||
enemyAction();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function enemyAction() {
|
||||
const dmg = GAME_STATE.enemy.nextDmg;
|
||||
const player = GAME_STATE.player;
|
||||
|
||||
// 扣除护盾逻辑
|
||||
let actualDmg = dmg;
|
||||
if (player.block >= actualDmg) {
|
||||
player.block -= actualDmg;
|
||||
actualDmg = 0;
|
||||
} else {
|
||||
actualDmg -= player.block;
|
||||
player.block = 0;
|
||||
}
|
||||
|
||||
player.hp -= actualDmg;
|
||||
|
||||
log(getRandomText(LLM_TEXTS.enemy_atk), "enemy");
|
||||
if (actualDmg > 0) {
|
||||
log(`> 受到 ${actualDmg} 点精神伤害!`, "enemy");
|
||||
document.body.classList.add("shake-anim"); // 全屏震动
|
||||
setTimeout(
|
||||
() => document.body.classList.remove("shake-anim"),
|
||||
500
|
||||
);
|
||||
} else {
|
||||
log(`> 你的厚脸皮完全抵挡了伤害!`, "system");
|
||||
}
|
||||
|
||||
// 随机生成下回合意图
|
||||
GAME_STATE.enemy.nextDmg = Math.floor(Math.random() * 10) + 5;
|
||||
|
||||
GAME_STATE.turn++;
|
||||
updateUI();
|
||||
checkLoss();
|
||||
|
||||
if (!GAME_STATE.isGameOver) {
|
||||
setTimeout(startPlayerTurn, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- 3. 辅助函数 --- */
|
||||
|
||||
function checkWin() {
|
||||
if (GAME_STATE.enemy.hp <= 0) {
|
||||
GAME_STATE.isGameOver = true;
|
||||
document.getElementById("game-result").innerText =
|
||||
"打脸成功!奖励 ¥10000";
|
||||
document.getElementById("game-result").style.color =
|
||||
"#00ff9d";
|
||||
document.getElementById("game-over").style.display = "flex";
|
||||
}
|
||||
}
|
||||
|
||||
function checkLoss() {
|
||||
if (GAME_STATE.player.hp <= 0) {
|
||||
GAME_STATE.isGameOver = true;
|
||||
document.getElementById("game-result").innerText =
|
||||
"任务失败:你社死了";
|
||||
document.getElementById("game-result").style.color =
|
||||
"#ff0055";
|
||||
document.getElementById("game-over").style.display = "flex";
|
||||
}
|
||||
}
|
||||
|
||||
function updateUI() {
|
||||
// 敌人 UI
|
||||
const hpPercent =
|
||||
(GAME_STATE.enemy.hp / GAME_STATE.enemy.maxHp) * 100;
|
||||
document.getElementById(
|
||||
"enemy-hp-bar"
|
||||
).style.width = `${hpPercent}%`;
|
||||
document.getElementById("enemy-hp-text").innerText =
|
||||
GAME_STATE.enemy.hp;
|
||||
document.getElementById(
|
||||
"enemy-intent"
|
||||
).innerText = `💬 准备造成 ${GAME_STATE.enemy.nextDmg} 点伤害`;
|
||||
|
||||
// 玩家 UI
|
||||
document.getElementById("player-hp").innerText =
|
||||
GAME_STATE.player.hp;
|
||||
document.getElementById("player-block").innerText =
|
||||
GAME_STATE.player.block;
|
||||
document.getElementById("player-energy").innerText =
|
||||
GAME_STATE.player.energy;
|
||||
document.getElementById("player-money").innerText =
|
||||
GAME_STATE.player.money;
|
||||
|
||||
// 渲染手牌
|
||||
const handContainer = document.getElementById("hand-container");
|
||||
handContainer.innerHTML = "";
|
||||
GAME_STATE.hand.forEach((card, index) => {
|
||||
const cardEl = document.createElement("div");
|
||||
cardEl.className = `card card-type-${card.type}`;
|
||||
cardEl.onclick = () => playCard(index);
|
||||
|
||||
cardEl.innerHTML = `
|
||||
<div class="card-cost">${card.cost}</div>
|
||||
<div class="card-name">${card.name}</div>
|
||||
<div class="card-desc">${card.desc}</div>
|
||||
`;
|
||||
handContainer.appendChild(cardEl);
|
||||
});
|
||||
}
|
||||
|
||||
function log(text, type) {
|
||||
const logArea = document.getElementById("battle-log");
|
||||
const entry = document.createElement("div");
|
||||
entry.className = `log-entry log-${type}`;
|
||||
entry.innerText = text;
|
||||
logArea.appendChild(entry);
|
||||
logArea.scrollTop = logArea.scrollHeight;
|
||||
}
|
||||
|
||||
function getRandomText(array) {
|
||||
return array[Math.floor(Math.random() * array.length)];
|
||||
}
|
||||
|
||||
function showFloatingText(text) {
|
||||
const enemyArea = document.getElementById("enemy-area");
|
||||
const el = document.createElement("div");
|
||||
el.className = "damage-text";
|
||||
el.innerText = text;
|
||||
el.style.left = "50%";
|
||||
el.style.top = "50%";
|
||||
enemyArea.appendChild(el);
|
||||
setTimeout(() => el.remove(), 1000);
|
||||
}
|
||||
|
||||
function shakeScreen(elementId) {
|
||||
const el = document.getElementById(elementId);
|
||||
el.classList.add("shake-anim");
|
||||
setTimeout(() => el.classList.remove("shake-anim"), 500);
|
||||
}
|
||||
|
||||
// 启动游戏
|
||||
initGame();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user