迁移证esc

This commit is contained in:
wfz
2026-05-02 18:07:11 +08:00
parent 990b31a12a
commit 9fd572c8c4
51 changed files with 3659 additions and 1820 deletions

View File

@@ -54,7 +54,10 @@ export class GameEngine {
[WEAPONS.PISTOL]: Infinity, // 手枪无限弹药
[WEAPONS.MACHINE_GUN]: 100,
[WEAPONS.SHOTGUN]: 20,
[WEAPONS.GRENADE]: 10
[WEAPONS.GRENADE]: 10,
[WEAPONS.MOLOTOV]: 5,
[WEAPONS.NUT_WALL]: 3,
[WEAPONS.AUTO_TURRET]: 2
}
// 手雷蓄力相关
@@ -217,8 +220,8 @@ export class GameEngine {
const localPlayer = this.players.get(this.localPlayerId)
if (localPlayer) {
this.scene.updateCamera(localPlayer.x, localPlayer.y)
// 手雷瞄准指示器
if (this.isChargingGrenade && this.currentWeaponIndex === 3) {
// 投掷武器瞄准指示器(手雷/燃烧瓶)
if (this.isChargingGrenade && (this.currentWeaponIndex === 3 || this.currentWeaponIndex === 4)) {
this.scene.showGrenadeTarget(localPlayer.x, localPlayer.y,
this.input.mouse.groundX || 0, this.input.mouse.groundY || 0,
this.grenadeChargePercent)
@@ -260,11 +263,16 @@ export class GameEngine {
// 应用本地预测(客户端预测)
this._applyLocalPrediction(inputState)
// 添加手雷相关数据到输入状态
// 添加蓄力武器相关数据到输入状态(手雷/燃烧瓶)
inputState.grenadeCharge = this.grenadeChargePercent
inputState.grenadeReleased = this.grenadeReleased
// 手雷释放前不发火
inputState.firing = this.currentWeaponIndex === 3 ? false : inputState.firing
// 蓄力武器释放前不发火
const weaponList7 = [WEAPONS.PISTOL, WEAPONS.MACHINE_GUN, WEAPONS.SHOTGUN, WEAPONS.GRENADE, WEAPONS.MOLOTOV, WEAPONS.NUT_WALL, WEAPONS.AUTO_TURRET]
const currentWeaponId = weaponList7[this.currentWeaponIndex]
const currentConfig = WEAPON_CONFIG[currentWeaponId]
if (currentConfig && currentConfig.chargeable) {
inputState.firing = false
}
// 保存输入以便进行客户端预测校正
this.pendingInputs.push(inputState)
@@ -287,10 +295,11 @@ export class GameEngine {
* 手雷需要长按鼠标蓄力,松开释放
*/
_handleGrenadeCharge(inputState) {
const weaponList = [WEAPONS.PISTOL, WEAPONS.MACHINE_GUN, WEAPONS.SHOTGUN, WEAPONS.GRENADE]
const currentWeapon = weaponList[this.currentWeaponIndex]
const weaponList7 = [WEAPONS.PISTOL, WEAPONS.MACHINE_GUN, WEAPONS.SHOTGUN, WEAPONS.GRENADE, WEAPONS.MOLOTOV, WEAPONS.NUT_WALL, WEAPONS.AUTO_TURRET]
const currentWeapon = weaponList7[this.currentWeaponIndex]
const config = WEAPON_CONFIG[currentWeapon]
if (currentWeapon === WEAPONS.GRENADE && WEAPON_CONFIG[WEAPONS.GRENADE].chargeable) {
if (config && config.chargeable) {
// 开始蓄力
if (inputState.firing && !this.isChargingGrenade) {
this.isChargingGrenade = true
@@ -298,13 +307,13 @@ export class GameEngine {
} else if (inputState.firing && this.isChargingGrenade) {
// 蓄力中,计算蓄力百分比
const elapsed = Date.now() - this.grenadeChargeStart
this.grenadeChargePercent = Math.min(1, elapsed / WEAPON_CONFIG[WEAPONS.GRENADE].maxCharge)
this.grenadeChargePercent = Math.min(1, elapsed / config.maxCharge)
} else if (!inputState.firing && this.isChargingGrenade) {
// 释放手雷
// 释放
this.grenadeReleased = true
this.isChargingGrenade = false
const elapsed = Date.now() - this.grenadeChargeStart
this.grenadeChargePercent = Math.min(1, elapsed / WEAPON_CONFIG[WEAPONS.GRENADE].maxCharge)
this.grenadeChargePercent = Math.min(1, elapsed / config.maxCharge)
}
} else {
this.isChargingGrenade = false
@@ -417,9 +426,9 @@ export class GameEngine {
// 新增子弹
this.bullets.set(bs.id, { ...bs })
this.scene.addBullet(bs)
// 枪口火焰特效(手雷除外)
// 枪口火焰特效(投掷类武器除外)
const player = this.players.get(bs.ownerId)
if (player && bs.weapon !== WEAPONS.GRENADE) {
if (player && bs.weaponIndex !== 3 && bs.weaponIndex !== 4) {
this.scene.addMuzzleFlash(player.x, player.y, bs.angle || 0)
}
} else {

View File

@@ -556,22 +556,30 @@ export class GameScene {
*/
addBullet(bullet) {
const group = new THREE.Group()
const isGrenade = bullet.weapon === WEAPONS.GRENADE
// weaponIndex: 0=pistol, 1=machinegun, 2=shotgun, 3=grenade, 4=molotov
const wIdx = bullet.weaponIndex
const isGrenade = wIdx === 3
const isMolotov = wIdx === 4
const isThrown = isGrenade || isMolotov
const z = bullet.z || 0.5
if (isGrenade) {
// 手雷:球体 + 发光 + 拖尾
if (isThrown) {
// 投掷物:球体 + 发光 + 拖尾
const bodyColor = isMolotov ? 0xff6600 : 0x44ff44
const glowColor = isMolotov ? 0xff4400 : 0x22cc22
const trailColor = isMolotov ? 0xff8844 : 0x88ff88
const geo = new THREE.SphereGeometry(0.12, 8, 8)
const mat = new THREE.MeshBasicMaterial({ color: 0x44ff44 })
const mat = new THREE.MeshBasicMaterial({ color: bodyColor })
const mesh = new THREE.Mesh(geo, mat)
mesh.position.set(bullet.x, z, bullet.y)
group.add(mesh)
const glowGeo = new THREE.SphereGeometry(0.18, 8, 8)
const glowMat = new THREE.MeshBasicMaterial({
color: 0x22cc22,
color: glowColor,
transparent: true,
opacity: 0.4
opacity: 0.5
})
const glow = new THREE.Mesh(glowGeo, glowMat)
glow.position.set(bullet.x, z, bullet.y)
@@ -580,7 +588,7 @@ export class GameScene {
// 拖尾线
const trailGeo = new THREE.BufferGeometry()
const trailMat = new THREE.LineBasicMaterial({
color: 0x88ff88,
color: trailColor,
transparent: true,
opacity: 0.6
})
@@ -598,14 +606,8 @@ export class GameScene {
const angle = bullet.angle || 0
// 根据武器类型调整大小
switch (bullet.weapon) {
case WEAPONS.MACHINE_GUN:
bulletSize = 0.05
break
case WEAPONS.SHOTGUN:
bulletSize = 0.04
break
}
if (wIdx === 1) bulletSize = 0.05 // 机枪
else if (wIdx === 2) bulletSize = 0.04 // 霰弹枪
// 拖尾
const trailLength = 2.5
@@ -651,9 +653,10 @@ export class GameScene {
x: bullet.x,
y: bullet.y,
z: z,
weapon: bullet.weapon,
weaponIndex: wIdx,
angle: bullet.angle || 0,
isGrenade
isGrenade,
isMolotov
})
}
@@ -695,7 +698,7 @@ export class GameScene {
child.position.z = y
}
// 更新拖尾位置
if (child.isLine && !bullet.isGrenade) {
if (child.isLine && !bullet.isGrenade && !bullet.isMolotov) {
const trailLength = 2.5
const positions = child.geometry.attributes.position.array
positions[0] = x - Math.sin(angle) * trailLength
@@ -1083,7 +1086,7 @@ export class GameScene {
// 更新子弹拖尾
for (const bullet of this.bullets) {
if (!bullet.isGrenade && bullet.trail) {
if (!bullet.isGrenade && !bullet.isMolotov && bullet.trail) {
const positions = bullet.trail.geometry.attributes.position.array
positions[0] = bullet.x - Math.sin(bullet.angle) * 1.5
positions[1] = 0.5

View File

@@ -28,7 +28,7 @@ export class HUD {
this.weaponPanel.className = 'hud-weapon-panel'
// 武器列表
const weaponList = [WEAPONS.PISTOL, WEAPONS.MACHINE_GUN, WEAPONS.SHOTGUN, WEAPONS.GRENADE]
const weaponList = [WEAPONS.PISTOL, WEAPONS.MACHINE_GUN, WEAPONS.SHOTGUN, WEAPONS.GRENADE, WEAPONS.MOLOTOV, WEAPONS.NUT_WALL, WEAPONS.AUTO_TURRET]
this.weaponSlots = []
for (let i = 0; i < weaponList.length; i++) {
const slot = document.createElement('div')
@@ -135,7 +135,7 @@ export class HUD {
* @param {Object} ammo 各武器弹药数
*/
updateWeapons(currentIndex, ammo) {
const weaponList = [WEAPONS.PISTOL, WEAPONS.MACHINE_GUN, WEAPONS.SHOTGUN, WEAPONS.GRENADE]
const weaponList = [WEAPONS.PISTOL, WEAPONS.MACHINE_GUN, WEAPONS.SHOTGUN, WEAPONS.GRENADE, WEAPONS.MOLOTOV, WEAPONS.NUT_WALL, WEAPONS.AUTO_TURRET]
for (let i = 0; i < this.weaponSlots.length; i++) {
const slot = this.weaponSlots[i]
// 高亮当前武器

View File

@@ -17,7 +17,10 @@ export class SettingsUI {
weapon1: 'Digit1',
weapon2: 'Digit2',
weapon3: 'Digit3',
weapon4: 'Digit4'
weapon4: 'Digit4',
weapon5: 'Digit5',
weapon6: 'Digit6',
weapon7: 'Digit7'
}
this.bindings = { ...this.defaultBindings }
}
@@ -68,7 +71,10 @@ export class SettingsUI {
weapon1: 'Weapon 1 (Pistol)',
weapon2: 'Weapon 2 (MG)',
weapon3: 'Weapon 3 (Shotgun)',
weapon4: 'Weapon 4 (Grenade)'
weapon4: 'Weapon 4 (Grenade)',
weapon5: 'Weapon 5 (Molotov)',
weapon6: 'Weapon 6 (Wall)',
weapon7: 'Weapon 7 (Turret)'
}
// 生成每种操作的按键配置行

View File

@@ -23,7 +23,10 @@ export const WEAPONS = {
PISTOL: 'pistol', // 手枪
MACHINE_GUN: 'machine_gun', // 机枪
SHOTGUN: 'shotgun', // 霰弹枪
GRENADE: 'grenade' // 手雷
GRENADE: 'grenade', // 手雷
MOLOTOV: 'molotov', // 燃烧瓶
NUT_WALL: 'nut_wall', // 坚果墙体
AUTO_TURRET: 'auto_turret' // 自动机枪塔
}
// ========== 武器配置 ==========
@@ -84,7 +87,51 @@ export const WEAPON_CONFIG = {
auto: false,
chargeable: true, // 可蓄力
maxCharge: 2000, // 最大蓄力时间(毫秒)
explosionRadius: 3 // 爆炸半径
explosionRadius: 3 // 爆炸半径
},
// 燃烧瓶配置
[WEAPONS.MOLOTOV]: {
name: 'Molotov',
damage: 80,
fireRate: 2000,
ammo: 5,
maxAmmo: 5,
speed: 12,
spread: 0,
pellets: 1,
range: 12,
auto: false,
chargeable: true, // 可蓄力
maxCharge: 2000,
explosionRadius: 2
},
// 坚果墙体配置
[WEAPONS.NUT_WALL]: {
name: 'Wall',
damage: 0,
fireRate: 1000,
ammo: 3,
maxAmmo: 3,
speed: 0,
spread: 0,
pellets: 0,
range: 0,
auto: false,
chargeable: false
},
// 自动机枪塔配置
[WEAPONS.AUTO_TURRET]: {
name: 'Turret',
damage: 0,
fireRate: 2000,
ammo: 2,
maxAmmo: 2,
speed: 0,
spread: 0,
pellets: 0,
range: 0,
auto: false,
chargeable: false
}
}

View File

@@ -18,7 +18,10 @@ export class InputManager {
weapon1: 'Digit1',
weapon2: 'Digit2',
weapon3: 'Digit3',
weapon4: 'Digit4'
weapon4: 'Digit4',
weapon5: 'Digit5',
weapon6: 'Digit6',
weapon7: 'Digit7'
}
// 待处理的输入序列(用于客户端预测)
@@ -100,13 +103,16 @@ export class InputManager {
/**
* 获取当前选择的武器
* @returns {number} 武器索引0-3),无选择返回-1
* @returns {number} 武器索引0-6),无选择返回-1
*/
getSelectedWeapon() {
if (this.keys[this.keyBindings.weapon1]) return 0
if (this.keys[this.keyBindings.weapon2]) return 1
if (this.keys[this.keyBindings.weapon3]) return 2
if (this.keys[this.keyBindings.weapon4]) return 3
if (this.keys[this.keyBindings.weapon5]) return 4
if (this.keys[this.keyBindings.weapon6]) return 5
if (this.keys[this.keyBindings.weapon7]) return 6
return -1
}