铁轮工会 · 第八代 · 营地原型
by EpicCoder88338 lines15.4 KB
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>铁轮工会 · 第八代 · 营地原型</title>
<style>
* { box-sizing: border-box; user-select: none; -webkit-user-select: none; }
body { margin: 0; overflow: hidden; background-color: #1a252c; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; touch-action: none; }
canvas { display: block; }
#ui {
position: absolute; top: 10px; left: 10px; right: 10px; bottom: 10px;
pointer-events: none; display: flex; flex-direction: column; justify-content: space-between;
}
.top-panel {
display: flex; justify-content: space-between; gap: 8px;
}
.card {
background: rgba(20, 30, 40, 0.85); color: #ecf0f1; padding: 8px 12px; border-radius: 8px;
font-size: 13px; border: 1px solid #34495e; backdrop-filter: blur(4px); flex: 1;
}
.card b { color: #f1c40f; }
.log-box {
background: rgba(20, 30, 40, 0.9); color: #bdc3c7; padding: 10px; border-radius: 8px;
font-size: 12px; line-height: 1.6; border: 1px solid #34495e; height: 130px; overflow-y: auto;
display: flex; flex-direction: column; justify-content: flex-end;
}
.log-box p { margin: 0; padding: 2px 0; }
.log-box .sys { color: #3498db; }
.log-box .warn { color: #e74c3c; font-weight: bold; }
.log-box .good { color: #2ecc71; }
#hint {
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: rgba(0,0,0,0.6); color: #fff; padding: 10px 20px; border-radius: 20px;
font-size: 14px; pointer-events: none; opacity: 0; transition: opacity 0.5s;
}
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<div id="hint"></div>
<div id="ui">
<div class="top-panel">
<div class="card">
身份: <b id="role">守法老公民</b><br>
状态: <b id="status">和平</b>
</div>
<div class="card" style="text-align: right;">
行动力: <b id="energy">100</b> / 100<br>
位置: <b id="location">中央营地</b>
</div>
</div>
<div class="log-box" id="logBox">
<p class="sys">[系统] 欢迎来到铁轮工会中央营地。</p>
<p class="sys">[系统] 点击屏幕移动。去捕猎,或去会议台看看。</p>
</div>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
let cw, ch;
function resize() {
cw = window.innerWidth;
ch = window.innerHeight;
canvas.width = cw;
canvas.height = ch;
initMap(); // 重新计算地图坐标
}
// ================== 游戏核心数据 ==================
let state = {
role: "守法老公民",
status: "和平",
energy: 100,
location: "中央营地",
logs: []
};
let player = { x: cw/2, y: ch/2, radius: 12, speed: 4, targetX: cw/2, targetY: ch/2 };
// 越轨者、防卫队、猎物等实体
let entities = [];
// 地图区域(根据你的手绘图设定)
let zones = [];
function initMap() {
// 重置地图数据(防止屏幕旋转后错位)
entities = [
{ type: 'prey', x: cw * 0.15, y: ch * 0.2, size: 12, color: '#8B4513', label: '猎物' },
{ type: 'prey', x: cw * 0.85, y: ch * 0.3, size: 12, color: '#8B4513', label: '猎物' },
{ type: 'prey', x: cw * 0.7, y: ch * 0.8, size: 12, color: '#8B4513', label: '猎物' },
{ type: 'guard', x: cw * 0.8, y: ch * 0.7, size: 15, color: '#e74c3c', label: '防卫队' },
{ type: 'guard', x: cw * 0.2, y: ch * 0.8, size: 15, color: '#e74c3c', label: '防卫队' },
{ type: 'poacher', x: cw * 0.2, y: ch * 0.5, size: 16, color: '#9b59b6', label: '越轨者' }
];
zones = [
// 顶部:外客接待区
{ name: '外客接待区', x: cw/2, y: ch * 0.12, w: 100, h: 40, color: '#95a5a6' },
// 中部左侧:学徒巢穴、工友巢穴
{ name: '学徒巢穴', x: cw * 0.2, y: ch * 0.35, w: 80, h: 50, color: '#e67e22' },
{ name: '工友巢穴', x: cw * 0.2, y: ch * 0.5, w: 80, h: 50, color: '#d35400' },
// 底部左侧:育婴室
{ name: '育婴室', x: cw * 0.25, y: ch * 0.75, w: 80, h: 60, color: '#f39c12' },
// 中部右侧:治疗区、长老巢穴
{ name: '治疗区', x: cw * 0.8, y: ch * 0.35, w: 80, h: 50, color: '#1abc9c' },
{ name: '长老巢穴', x: cw * 0.8, y: ch * 0.5, w: 80, h: 50, color: '#16a085' },
// 中心:会议台
{ name: '会议台', x: cw * 0.5, y: ch * 0.55, w: 80, h: 60, color: '#8e44ad' },
// 底部:训练场
{ name: '训练场', x: cw * 0.7, y: ch * 0.75, w: 100, h: 60, color: '#2980b9' },
// 营地入口(标识)
{ name: '营地入口2', x: cw * 0.08, y: ch * 0.4, w: 40, h: 30, color: '#c0392b' },
{ name: '营地入口1', x: cw * 0.92, y: ch * 0.5, w: 40, h: 30, color: '#c0392b' },
{ name: '秘密通道', x: cw * 0.5, y: ch * 0.95, w: 50, h: 30, color: '#2c3e50' }
];
}
// ================== 日志系统 ==================
function addLog(msg, type = 'sys') {
state.logs.push({ text: msg, type: type });
if (state.logs.length > 8) state.logs.shift();
const box = document.getElementById('logBox');
box.innerHTML = state.logs.map(log => `<p class="${log.type}">${log.text}</p>`).join('');
box.scrollTop = box.scrollHeight;
}
function showHint(text) {
const hint = document.getElementById('hint');
hint.textContent = text;
hint.style.opacity = 1;
setTimeout(() => { hint.style.opacity = 0; }, 1500);
}
// ================== 触摸控制 ==================
canvas.addEventListener('touchstart', (e) => {
e.preventDefault();
const rect = canvas.getBoundingClientRect();
const touch = e.touches[0];
if (touch) {
player.targetX = touch.clientX - rect.left;
player.targetY = touch.clientY - rect.top;
}
}, { passive: false });
canvas.addEventListener('mousedown', (e) => {
player.targetX = e.clientX;
player.targetY = e.clientY;
});
// ================== 碰撞与交互逻辑 ==================
function checkCollisions() {
// 1. 与动态实体的交互
entities.forEach(ent => {
const dist = Math.hypot(player.x - ent.x, player.y - ent.y);
if (dist < player.radius + ent.size) {
if (ent.type === 'prey') {
state.energy = Math.min(100, state.energy + 15);
addLog("你捕获了猎物。行动力 +15", "good");
ent.x = Math.random() * (cw - 100) + 50;
ent.y = Math.random() * (ch - 200) + 100;
} else if (ent.type === 'guard') {
if (state.role === '守法老公民') {
addLog("防卫队: 朋友,领地安全。", "sys");
} else {
addLog("防卫队: 警告!你已被通缉!准备战斗!", "warn");
state.status = "被追捕";
state.energy -= 10; // 被攻击
// 弹开
const angle = Math.atan2(player.y - ent.y, player.x - ent.x);
player.targetX = player.x + Math.cos(angle) * 80;
player.targetY = player.y + Math.sin(angle) * 80;
}
} else if (ent.type === 'poacher') {
if (state.role === '守法老公民') {
addLog("你遇到了越轨者!你决定拔出爪子。【超雄模式】激活!", "warn");
state.role = "超雄玩家 (大镖客)";
state.status = "战斗";
showHint("你已切换为超雄玩家!");
} else {
state.energy -= 5;
addLog("你与越轨者发生了冲突。", "warn");
}
ent.x = Math.random() * (cw - 100) + 50;
ent.y = Math.random() * (ch - 200) + 100;
}
}
});
// 2. 与地图区域的交互
let inZone = false;
zones.forEach(zone => {
const dx = Math.abs(player.x - zone.x);
const dy = Math.abs(player.y - zone.y);
if (dx < zone.w/2 && dy < zone.h/2) {
inZone = true;
if (state.location !== zone.name) {
state.location = zone.name;
addLog(`你进入了【${zone.name}】`);
// 特定区域效果
if (zone.name === '会议台') {
addLog("会议台:玳羽会长曾在此发表演讲。公会投票在此进行。", "sys");
if (state.role === '超雄玩家 (大镖客)') {
addLog("由于你处于通缉状态,你不敢久留。", "warn");
}
} else if (zone.name === '育婴室') {
addLog("育婴室:幼崽的庇护所。任何伤害幼崽的行为都是重罪。", "good");
if (state.role === '超雄玩家 (大镖客)') {
addLog("防卫队: 离育婴室远点!", "warn");
state.energy -= 10;
}
} else if (zone.name === '训练场') {
addLog("训练场:你可以在此提升战斗技巧。", "sys");
} else if (zone.name === '秘密通道') {
addLog("秘密通道:这是避开防卫队的绝佳路线。", "sys");
} else if (zone.name === '治疗区') {
addLog("治疗区:治愈伤口,学习医学知识。", "good");
}
}
}
});
if (!inZone) state.location = "中央营地外围";
// 边界限制
player.x = Math.max(player.radius, Math.min(cw - player.radius, player.x));
player.y = Math.max(player.radius, Math.min(ch - player.radius, player.y));
}
// ================== 主循环 ==================
function update() {
const dx = player.targetX - player.x;
const dy = player.targetY - player.y;
const distance = Math.hypot(dx, dy);
// 移动逻辑
if (distance > 5) {
player.x += (dx / distance) * player.speed;
player.y += (dy / distance) * player.speed;
// 消耗行动力(跑动越久越累)
if (state.energy > 0) {
state.energy -= 0.08;
} else {
addLog("你太累了,跑不动了。去找点吃的吧。", "warn");
player.targetX = player.x;
player.targetY = player.y;
}
}
// 碰撞检测
checkCollisions();
// 实时更新UI
document.getElementById('role').textContent = state.role;
document.getElementById('status').textContent = state.status;
document.getElementById('energy').textContent = Math.floor(state.energy);
document.getElementById('location').textContent = state.location;
// ================== 渲染画面 ==================
// 1. 绘制草地(暗绿色,符合野外营地风格)
ctx.fillStyle = '#2c3e50';
ctx.fillRect(0, 0, cw, ch);
ctx.fillStyle = '#273746';
ctx.fillRect(20, 20, cw - 40, ch - 40); // 内部场地
// 2. 绘制地图区域(带标签的矩形)
zones.forEach(zone => {
ctx.fillStyle = zone.color + '88'; // 半透明
ctx.fillRect(zone.x - zone.w/2, zone.y - zone.h/2, zone.w, zone.h);
ctx.strokeStyle = zone.color;
ctx.lineWidth = 2;
ctx.strokeRect(zone.x - zone.w/2, zone.y - zone.h/2, zone.w, zone.h);
// 区域标签
ctx.fillStyle = '#ecf0f1';
ctx.font = 'bold 12px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(zone.name, zone.x, zone.y + 5);
});
// 3. 绘制实体(猎物、防卫队、越轨者)
entities.forEach(ent => {
ctx.beginPath();
if (ent.type === 'prey') {
ctx.rect(ent.x - ent.size/2, ent.y - ent.size/2, ent.size, ent.size);
ctx.fillStyle = ent.color;
} else if (ent.type === 'guard') {
ctx.moveTo(ent.x, ent.y - ent.size);
ctx.lineTo(ent.x + ent.size, ent.y + ent.size);
ctx.lineTo(ent.x - ent.size, ent.y + ent.size);
ctx.fillStyle = ent.color;
} else {
ctx.arc(ent.x, ent.y, ent.size, 0, Math.PI * 2);
ctx.fillStyle = ent.color;
}
ctx.fill();
// 实体标签
ctx.fillStyle = '#bdc3c7';
ctx.font = '10px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(ent.label, ent.x, ent.y - ent.size - 5);
});
// 4. 绘制玩家(蓝圈,带眼睛的猫脸感)
ctx.beginPath();
ctx.arc(player.x, player.y, player.radius, 0, Math.PI * 2);
ctx.fillStyle = '#3498db';
ctx.fill();
ctx.strokeStyle = '#ecf0f1';
ctx.lineWidth = 2;
ctx.stroke();
// 画两只小耳朵
ctx.beginPath();
ctx.moveTo(player.x - 6, player.y - 10);
ctx.lineTo(player.x - 2, player.y - 18);
ctx.lineTo(player.x + 2, player.y - 10);
ctx.fillStyle = '#ecf0f1';
ctx.fill();
ctx.beginPath();
ctx.moveTo(player.x + 2, player.y - 10);
ctx.lineTo(player.x + 6, player.y - 18);
ctx.lineTo(player.x + 10, player.y - 10);
ctx.fill();
requestAnimationFrame(update);
}
// 初始化
resize();
update();
</script>
</body>
</html>Game Source: 铁轮工会 · 第八代 · 营地原型
Creator: EpicCoder88
Libraries: none
Complexity: complex (338 lines, 15.4 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-epiccoder88-muqnrujx" to link back to the original. Then publish at arcadelab.ai/publish.