CS2D
用deepseek写了一个CS2D游戏
已经可以简单游玩了
下载的是完整版,下面给的是压行版
下面的代码注意使用UTF-8编码
压行版
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, initial-scale=1.0"><title>CS风格5v5战争迷雾射击游戏-优化完整版</title><style>*{margin:0;padding:0;box-sizing:border-box}body{font-family:'Segoe UI',Tahoma,Geneva,Verdana,sans-serif;background:linear-gradient(135deg,#0f0c29 0%,#302b63 50%,#24243e 100%);color:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;padding:20px;overflow:hidden}.header{text-align:center;margin-bottom:15px;width:100%;max-width:1200px}h1{font-size:2.5rem;margin-bottom:8px;background:linear-gradient(to right,#4A90E2,#2A6EBB);-webkit-background-clip:text;-webkit-text-fill-color:transparent;text-shadow:0 2px 4px rgba(0,0,0,0.3)}.subtitle{color:#a0a0c0;font-size:1rem;margin-bottom:15px}.game-container{position:relative;width:100%;max-width:1200px;margin-bottom:15px}#gameCanvas{background-color:#0d1117;border-radius:10px;box-shadow:0 10px 30px rgba(0,0,0,0.5);display:block;margin:0 auto}.scoreboard{display:flex;justify-content:space-between;background-color:rgba(0,0,0,0.7);padding:12px 20px;border-radius:10px;margin-top:10px;width:100%;max-width:1200px}.team-score{display:flex;align-items:center;gap:15px}.team-ct{color:#4A90E2;font-weight:bold;font-size:1.2rem}.team-t{color:#FF6B6B;font-weight:bold;font-size:1.2rem}.score{font-size:2rem;font-weight:bold;min-width:80px;text-align:center}.score-divider{font-size:1.5rem;color:#a0a0c0}.round-info{display:flex;flex-direction:column;align-items:center;justify-content:center}.round-text{font-size:0.9rem;color:#a0a0c0;margin-bottom:5px}.round-number{font-size:1.4rem;font-weight:bold}.game-phase{font-size:1.1rem;font-weight:bold;margin-top:5px;padding:5px 15px;border-radius:5px;background-color:rgba(255,193,7,0.2);color:#FFC107}.stats-bar{display:flex;justify-content:space-between;background-color:rgba(0,0,0,0.7);padding:10px 15px;border-radius:10px;margin-top:10px;width:100%;max-width:1200px;flex-wrap:wrap}.player-stats{display:flex;gap:20px}.stat-item{display:flex;flex-direction:column;align-items:center;min-width:70px}.stat-label{font-size:0.8rem;color:#a0a0c0;margin-bottom:3px}.stat-value{font-size:1.2rem;font-weight:bold;color:#fff}.money-display{font-size:1.4rem;font-weight:bold;color:#FFD700;display:flex;align-items:center;gap:5px}.game-timer{display:flex;flex-direction:column;align-items:center;justify-content:center;min-width:100px}.timer-label{font-size:0.8rem;color:#a0a0c0;margin-bottom:3px}.timer-value{font-size:1.8rem;font-weight:bold;color:#fff}.controls-container{display:flex;flex-wrap:wrap;justify-content:space-between;width:100%;max-width:1200px;margin-top:15px;gap:15px}.weapon-selector{display:flex;background-color:rgba(0,0,0,0.6);padding:15px;border-radius:10px;flex:2;min-width:400px;gap:10px}.weapon{display:flex;flex-direction:column;align-items:center;padding:8px;border-radius:8px;cursor:pointer;transition:all 0.3s;flex:1;min-width:0}.weapon.active{background-color:rgba(74,144,226,0.2);border:2px solid#4A90E2}.weapon.unavailable{opacity:0.5;cursor:not-allowed}.weapon-icon{font-size:1.5rem;margin-bottom:3px}.weapon-name{font-size:0.8rem;font-weight:bold;margin-bottom:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:100%;text-align:center}.weapon-info{font-size:0.7rem;color:#a0a0c0;text-align:center}.game-controls{background-color:rgba(0,0,0,0.6);padding:15px;border-radius:10px;flex:1;min-width:250px}.game-controls h3{color:#4A90E2;margin-bottom:10px;font-size:1.1rem}.game-controls ul{list-style-type:none;padding-left:5px}.game-controls li{margin-bottom:6px;font-size:0.85rem;color:#ccc}.game-controls kbd{background-color:#333;padding:2px 6px;border-radius:4px;font-family:monospace;font-size:0.8rem}.shop-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:rgba(0,0,0,0.9);display:none;flex-direction:column;justify-content:center;align-items:center;z-index:100;border-radius:10px}.shop-container{background-color:rgba(30,30,46,0.95);padding:25px;border-radius:10px;width:90%;max-width:900px;max-height:80vh;overflow-y:auto}.shop-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px}.shop-title{font-size:1.8rem;color:#FFD700}.shop-money{font-size:1.5rem;font-weight:bold;color:#FFD700}.shop-categories{display:flex;gap:10px;margin-bottom:20px;flex-wrap:wrap}.shop-category{padding:10px 15px;background-color:rgba(0,0,0,0.5);border-radius:5px;cursor:pointer;transition:all 0.3s}.shop-category.active{background-color:#4A90E2;color:white}.shop-items{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:15px}.shop-item{background-color:rgba(0,0,0,0.5);padding:15px;border-radius:8px;cursor:pointer;transition:all 0.3s;display:flex;flex-direction:column;align-items:center}.shop-item:hover{background-color:rgba(74,144,226,0.2);transform:translateY(-5px)}.shop-item.unavailable{opacity:0.5;cursor:not-allowed}.shop-item.unavailable:hover{background-color:rgba(0,0,0,0.5);transform:none}.item-icon{font-size:2rem;margin-bottom:10px}.item-name{font-size:1rem;font-weight:bold;margin-bottom:5px;text-align:center}.item-price{font-size:1.1rem;color:#FFD700;margin-bottom:10px}.item-desc{font-size:0.8rem;color:#a0a0c0;text-align:center;margin-bottom:10px}.close-shop{margin-top:20px;padding:10px 20px;background-color:#4A90E2;border:none;border-radius:5px;color:white;font-weight:bold;cursor:pointer;transition:all 0.3s}.close-shop:hover{background-color:#2A6EBB}.bomb-indicator{position:absolute;top:10px;left:10px;background-color:rgba(0,0,0,0.7);padding:8px 12px;border-radius:5px;font-size:0.9rem;display:none}.game-message{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:rgba(0,0,0,0.8);padding:20px 30px;border-radius:10px;font-size:1.5rem;font-weight:bold;text-align:center;z-index:200;display:none}.reload-indicator{position:absolute;bottom:80px;left:50%;transform:translateX(-50%);background-color:rgba(0,0,0,0.7);padding:8px 15px;border-radius:5px;font-size:0.9rem;display:none}.player-list{position:absolute;top:50px;right:10px;background-color:rgba(0,0,0,0.7);padding:10px;border-radius:5px;font-size:0.9rem;width:200px}.player-list-title{font-weight:bold;margin-bottom:5px;color:#4A90E2}.player-item{display:flex;justify-content:space-between;margin-bottom:3px;padding:3px 5px;border-radius:3px}.player-item.ct{background-color:rgba(74,144,226,0.2)}.player-item.t{background-color:rgba(255,107,107,0.2)}.player-item.dead{opacity:0.5;text-decoration:line-through}.player-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.player-health{font-weight:bold}.player-health.full{color:#4CAF50}.player-health.low{color:#FFC107}.player-health.critical{color:#F44336}.footer{margin-top:15px;text-align:center;color:#a0a0c0;font-size:0.8rem}@media(max-width:700px){.controls-container{flex-direction:column}.weapon-selector,.game-controls{min-width:100%}h1{font-size:2rem}.stats-bar{flex-direction:column;gap:10px}.player-stats,.game-timer{justify-content:center}.player-list{display:none}}.swap-notification{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:rgba(0,0,0,0.9);padding:30px 40px;border-radius:15px;font-size:1.8rem;font-weight:bold;text-align:center;z-index:300;display:none;border:3px solid#FFD700}.team-select-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:linear-gradient(135deg,#0f0c29 0%,#302b63 50%,#24243e 100%);display:flex;flex-direction:column;justify-content:center;align-items:center;z-index:1000;padding:20px}.team-select-container{background-color:rgba(0,0,0,0.9);padding:40px;border-radius:20px;max-width:900px;width:100%;box-shadow:0 20px 50px rgba(0,0,0,0.8);border:2px solid#4A90E2}.team-select-container h2{font-size:2.8rem;text-align:center;margin-bottom:10px;background:linear-gradient(to right,#4A90E2,#FF6B6B);-webkit-background-clip:text;-webkit-text-fill-color:transparent;text-shadow:0 2px 10px rgba(0,0,0,0.5)}.team-description{text-align:center;margin-bottom:30px;font-size:1.2rem;color:#a0a0c0}.team-options{display:flex;gap:30px;justify-content:center;margin-bottom:30px;flex-wrap:wrap}.team-option{background-color:rgba(30,30,46,0.9);padding:25px;border-radius:15px;cursor:pointer;transition:all 0.3s ease;flex:1;min-width:300px;max-width:400px;border:3px solid transparent;display:flex;flex-direction:column;align-items:center}.team-option:hover{transform:translateY(-10px);box-shadow:0 15px 30px rgba(0,0,0,0.5)}.team-option.ct-option:hover{border-color:#4A90E2;background-color:rgba(74,144,226,0.1)}.team-option.t-option:hover{border-color:#FF6B6B;background-color:rgba(255,107,107,0.1)}.team-icon{font-size:4rem;margin-bottom:15px}.team-name{font-size:1.8rem;font-weight:bold;margin-bottom:10px}.team-option.ct-option.team-name{color:#4A90E2}.team-option.t-option.team-name{color:#FF6B6B}.team-desc{font-size:1.1rem;color:#ffffff;margin-bottom:20px;text-align:center;font-weight:bold}.team-info{margin-top:15px;width:100%}.team-info ul{list-style-type:none;padding-left:0}.team-info li{padding:8px 0;font-size:0.9rem;color:#a0a0c0;border-bottom:1px solid rgba(255,255,255,0.1)}.team-info li:last-child{border-bottom:none}.team-select-hint{text-align:center;margin-top:20px;padding-top:20px;border-top:1px solid rgba(255,255,255,0.2);color:#a0a0c0;font-size:0.9rem}.team-select-hint kbd{background-color:#333;padding:2px 8px;border-radius:4px;font-family:monospace;font-size:0.9rem}@media(max-width:768px){.team-options{flex-direction:column;align-items:center}.team-option{max-width:100%}.team-select-container{padding:20px}.team-select-container h2{font-size:2.2rem}}.hit-indicator{position:absolute;width:0;height:0;pointer-events:none;z-index:999;opacity:0;transition:opacity 0.3s}.hit-indicator.visible{opacity:1}.hit-indicator-arrow{width:0;height:0;border-left:20px solid transparent;border-right:20px solid transparent;border-top:30px solid rgba(255,50,50,0.8);filter:drop-shadow(0 0 5px rgba(255,0,0,0.5))}.hit-indicator-text{position:absolute;top:5px;left:50%;transform:translateX(-50%);color:#FF3232;font-weight:bold;font-size:14px;text-shadow:0 0 3px rgba(0,0,0,0.8);white-space:nowrap}.edge-hit-indicator{position:absolute;pointer-events:none;z-index:999;opacity:0;transition:opacity 0.2s}.edge-hit-indicator.visible{opacity:1}.edge-arrow{width:0;height:0;border-style:solid;filter:drop-shadow(0 0 3px rgba(255,0,0,0.7))}.screen-flash{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:998;opacity:0;transition:opacity 0.1s}.screen-flash.red{background-color:rgba(255,0,0,0.3)}.screen-flash.white{background-color:rgba(255,255,255,0.4)}</style></head><body><body><!--添加选边界面--><div class="team-select-overlay"id="teamSelectOverlay"><div class="team-select-container"><h2>选择阵营</h2><div class="team-description"><p>选择您想要加入的阵营开始游戏</p></div><div class="team-options"><div class="team-option ct-option"data-team="ct"><div class="team-icon">🛡️</div><div class="team-name">反恐精英(CT)</div><div class="team-desc">防守炸弹点,拆除炸弹</div><div class="team-info"><ul><li>起始武器:USP-S</li><li>初始金钱:$800</li><li>胜利条件:消灭T或拆除炸弹</li></ul></div></div><div class="team-option t-option"data-team="t"><div class="team-icon">💣</div><div class="team-name">恐怖分子(T)</div><div class="team-desc">安装炸弹,保护炸弹</div><div class="team-info"><ul><li>起始武器:Glock-18</li><li>初始金钱:$800</li><li>胜利条件:消灭CT或引爆炸弹</li></ul></div></div></div><div class="team-select-hint"><p>选择后将直接开始游戏,按<kbd>TAB</kbd>切换观战目标</p></div></div></div><!--添加受击指示器元素--><div class="hit-indicator"id="hitIndicator"><div class="hit-indicator-arrow"></div><div class="hit-indicator-text"id="hitDamageText">0</div></div><!--屏幕边缘受击指示器容器--><div id="edgeHitIndicators"></div><!--屏幕闪烁效果--><div class="screen-flash red"id="screenFlashRed"></div><div class="screen-flash white"id="screenFlashWhite"></div><div class="header"><!--保持原有的头部代码--><div class="header"><h1>CS风格5v5战争迷雾射击游戏-优化完整版</h1><p class="subtitle">5v5对战|12回合换边|13分制|30秒购买时间|1分30秒回合|改进版炙热沙城II地图</p></div><div class="game-container"><canvas id="gameCanvas"width="1200"height="700"></canvas><div class="bomb-indicator"id="bombIndicator"></div><div class="reload-indicator"id="reloadIndicator">换弹中...</div><div class="game-message"id="gameMessage"></div><div class="swap-notification"id="swapNotification">交换阵营!</div><div class="player-list"id="playerList"><div class="player-list-title">玩家列表</div><div id="ctPlayers"><!--CT玩家列表--></div><div style="margin: 10px 0; border-top: 1px solid #444;"></div><div id="tPlayers"><!--T玩家列表--></div></div><div class="shop-overlay"id="shopOverlay"><div class="shop-container"><div class="shop-header"><div class="shop-title">武器商店(剩余时间:<span id="buyTime">30</span>秒)</div><div class="shop-money">金钱:$<span id="shopMoney">0</span></div></div><div class="shop-categories"><div class="shop-category active"data-category="pistols">手枪</div><div class="shop-category"data-category="rifles">步枪</div><div class="shop-category"data-category="smgs">冲锋枪</div><div class="shop-category"data-category="heavy">重武器</div><div class="shop-category"data-category="gear">装备</div></div><div class="shop-items"id="shopItems"><!--商品将通过JavaScript动态生成--></div><button class="close-shop"id="closeShop">完成购买</button></div></div></div><div class="scoreboard"><div class="team-score"><div class="team-ct">反恐精英(CT)</div><div class="score"id="ctScore">0</div></div><div class="round-info"><div class="round-text">回合</div><div class="round-number"id="roundNumber">1</div><div class="game-phase"id="gamePhase">购买阶段</div></div><div class="team-score"><div class="score"id="tScore">0</div><div class="team-t">恐怖分子(T)</div></div></div><div class="stats-bar"><div class="player-stats"><div class="stat-item"><div class="stat-label">生命值</div><div class="stat-value"id="health">100</div></div><div class="stat-item"><div class="stat-label">护甲</div><div class="stat-value"id="armor">0</div></div><div class="stat-item"><div class="stat-label">弹药</div><div class="stat-value"id="ammo">12/24</div></div><div class="stat-item"><div class="stat-label">金钱</div><div class="money-display">$<span id="money">800</span></div></div><div class="stat-item"><div class="stat-label">杀敌/死亡</div><div class="stat-value"id="kda">0/0</div></div></div><div class="game-timer"><div class="timer-label">回合时间</div><div class="timer-value"id="timer">1:30</div></div></div><div class="controls-container"><div class="weapon-selector"id="weaponSelector"><!--武器将通过JavaScript动态生成--></div><div class="game-controls"><h3>游戏控制</h3><ul><li><kbd>W</kbd><kbd>A</kbd><kbd>S</kbd><kbd>D</kbd>-移动</li><li>鼠标-瞄准和射击</li><li><kbd>B</kbd>-打开商店(仅在购买阶段)</li><li><kbd>R</kbd>-换弹</li><li><kbd>E</kbd>-互动(安装/拆除炸弹)</li><li><kbd>1-3</kbd>-切换武器(1-主武器,2-手枪,3-匕首)</li><li><kbd>F</kbd>-开关手电筒</li><li><kbd>ESC</kbd>-关闭商店</li><li><kbd>Q</kbd>-快速切换武器</li><li><kbd>TAB</kbd>-观战模式(死亡时)</li></ul></div></div><div class="footer"><p>CS风格5v5俯视角战争迷雾射击游戏|12回合换边|13分制|团队对战|战术竞技|改进版炙热沙城II地图</p></div><script>const canvas=document.getElementById('gameCanvas');const ctx=canvas.getContext('2d');const bombIndicator=document.getElementById('bombIndicator');const reloadIndicator=document.getElementById('reloadIndicator');const gameMessage=document.getElementById('gameMessage');const swapNotification=document.getElementById('swapNotification');const shopOverlay=document.getElementById('shopOverlay');const shopMoney=document.getElementById('shopMoney');const shopItems=document.getElementById('shopItems');const buyTimeDisplay=document.getElementById('buyTime');const closeShopBtn=document.getElementById('closeShop');const playerList=document.getElementById('playerList');const ctPlayersList=document.getElementById('ctPlayers');const tPlayersList=document.getElementById('tPlayers');const teamSelectOverlay=document.getElementById('teamSelectOverlay');let gameStarted=false;const hitIndicator=document.getElementById('hitIndicator');const hitDamageText=document.getElementById('hitDamageText');const edgeHitIndicators=document.getElementById('edgeHitIndicators');const screenFlashRed=document.getElementById('screenFlashRed');const screenFlashWhite=document.getElementById('screenFlashWhite');const gameState={phase:'buy',ctScore:0,tScore:0,round:1,targetScore:13,buyTime:15,buyTimeLeft:15,roundTime:90,roundTimeLeft:90,bombTimer:45,bombTimeLeft:45,bombPlantTime:4000,bombDefuseTime:10000,bombDefuseTimeWithKit:5000,playerAlive:true,playerHealth:100,playerArmor:0,playerMoney:800,playerKills:0,playerDeaths:0,playerHasBomb:false,playerHasDefuseKit:false,playerFlashlight:true,playerReloading:false,playerReloadProgress:0,playerMoving:false,playerMovingTimer:0,playerKnifing:false,playerKnifeProgress:0,bombPlanted:false,bombSite:null,roundWinner:null,gameOver:false,shopOpen:false,currentWeaponType:'secondary',currentWeapon:'usp',playerWasAlive:true,playerPreviousPrimary:null,playerPreviousSecondary:'usp',playerPreviousArmor:0,roundsSinceLastSwap:0,needToSwapTeams:false,isSwapping:false,spectatorMode:false,spectatorIndex:0,spectatorTarget:null,lastSpectatorCheck:0};const weapons={knife:{name:'匕首',damage:75,attackRate:500,range:25,price:0,type:'melee',color:'#999',icon:'🔪',team:'both',isMelee:true},usp:{name:'USP-S',damage:35,fireRate:200,range:500,bulletSpeed:25,spread:0.02,moveSpreadMultiplier:25.0,bulletsPerShot:1,magSize:12,ammo:100,ammoInMag:12,reloadTime:2000,price:200,type:'pistol',color:'#ffffff',icon:'🔫',team:'ct'},glock:{name:'Glock-18',damage:28,fireRate:150,range:450,bulletSpeed:22,spread:0.03,moveSpreadMultiplier:22.0,bulletsPerShot:1,magSize:20,ammo:120,ammoInMag:20,reloadTime:1800,price:200,type:'pistol',color:'#FF6B6B',icon:'🔫',team:'t'},p250:{name:'P250',damage:38,fireRate:400,range:500,bulletSpeed:26,spread:0.025,moveSpreadMultiplier:24.0,bulletsPerShot:1,magSize:13,ammo:104,ammoInMag:13,reloadTime:1900,price:300,type:'pistol',color:'#ffffff',icon:'🔫',team:'both'},deagle:{name:'Desert Eagle',damage:86,fireRate:500,range:600,bulletSpeed:30,spread:0.015,moveSpreadMultiplier:28.0,bulletsPerShot:1,magSize:7,ammo:35,ammoInMag:7,reloadTime:2200,price:700,type:'pistol',color:'#FFD700',icon:'🔫',team:'both'},dualies:{name:'Dual Berettas',damage:30,fireRate:150,range:450,bulletSpeed:22,spread:0.04,moveSpreadMultiplier:20.0,bulletsPerShot:1,magSize:30,ammo:240,ammoInMag:30,reloadTime:2200,price:400,type:'pistol',color:'#ffffff',icon:'🔫🔫',team:'both',isDual:true,currentBarrel:0,leftMag:30,rightMag:30},ak47:{name:'AK-47',damage:36,fireRate:100,range:1200,bulletSpeed:45,spread:0.05,moveSpreadMultiplier:12.0,bulletsPerShot:1,magSize:30,ammo:180,ammoInMag:30,reloadTime:2500,price:2700,type:'rifle',color:'#FF4500',icon:'🏹',team:'t'},m4a4:{name:'M4A4',damage:33,fireRate:90,range:1200,bulletSpeed:45,spread:0.04,moveSpreadMultiplier:10.0,bulletsPerShot:1,magSize:30,ammo:180,ammoInMag:30,reloadTime:2400,price:3100,type:'rifle',color:'#4A90E2',icon:'🏹',team:'ct'},awp:{name:'AWP',damage:115,fireRate:1500,range:2000,bulletSpeed:60,spread:0.005,moveSpreadMultiplier:30.0,bulletsPerShot:1,magSize:10,ammo:40,ammoInMag:10,reloadTime:3500,price:4750,type:'rifle',color:'#ffffff',icon:'🎯',team:'both'},mp9:{name:'MP9',damage:26,fireRate:40,range:600,bulletSpeed:35,spread:0.06,moveSpreadMultiplier:5.0,bulletsPerShot:1,magSize:30,ammo:240,ammoInMag:30,reloadTime:1800,price:1250,type:'smg',color:'#4A90E2',icon:'🔫',team:'ct'},mac10:{name:'MAC-10',damage:25,fireRate:45,range:600,bulletSpeed:35,spread:0.065,moveSpreadMultiplier:5.0,bulletsPerShot:1,magSize:30,ammo:200,ammoInMag:30,reloadTime:1850,price:1050,type:'smg',color:'#FF6B6B',icon:'🔫',team:'t'},nova:{name:'Nova',damage:88,fireRate:1000,range:300,bulletSpeed:25,spread:0.12,moveSpreadMultiplier:8.0,bulletsPerShot:8,magSize:8,ammo:64,ammoInMag:8,reloadTime:3000,price:1200,type:'heavy',color:'#FFD700',icon:'🔪',team:'both'}};const gearItems={armor_light:{name:'轻型护甲',type:'armor',armorValue:50,price:650,icon:'🛡️',description:'提供50点护甲保护',team:'both'},armor_heavy:{name:'重型护甲',type:'armor',armorValue:100,price:1000,icon:'🛡️',description:'提供100点护甲保护',team:'both'},defuse_kit:{name:'拆弹器',type:'defuse',price:400,icon:'🔧',description:'加快拆弹速度',team:'ct'},bomb:{name:'C4炸弹',type:'bomb',price:0,icon:'💣',description:'安装到炸弹点',team:'t'}};const player={id:'player',name:'玩家',team:'ct',x:0,y:0,radius:12,speed:5,color:'#4A90E2',lastShot:0,direction:0,hasBomb:false,hasDefuseKit:false,primaryWeapon:null,secondaryWeapon:'usp',knife:'knife',currentWeaponType:'secondary',alive:true,health:100,armor:0,money:800,kills:0,deaths:0,isMoving:false,lastMoveTime:0,lastWeaponSwitch:0,weaponSwitchCooldown:200,bombPlanting:false,bombPlantProgress:0,bombPlantingSite:null,bombDefusing:false,bombDefuseProgress:0};const hitIndicatorState={visible:false,showUntil:0,damage:0,sourceX:0,sourceY:0,lastHitTime:0,hitCooldown:500,edgeIndicators:[],edgeIndicatorLifetime:1500};class AStarPathfinder{constructor(walls,worldWidth,worldHeight,gridSize=40){this.walls=walls;this.worldWidth=worldWidth;this.worldHeight=worldHeight;this.gridSize=gridSize;this.gridCols=Math.ceil(worldWidth/gridSize);this.gridRows=Math.ceil(worldHeight/gridSize);this.grid=this.createGrid()}createGrid(){const grid=[];for(let y=0;y<this.gridRows;y++){grid[y]=[];for(let x=0;x<this.gridCols;x++){const worldX=x*this.gridSize+this.gridSize/2;const worldY=y*this.gridSize+this.gridSize/2;grid[y][x]={x:worldX,y:worldY,walkable:this.isWalkable(worldX,worldY),f:0,g:0,h:0,parent:null,closed:false,visited:false}}}return grid}isWalkable(x,y){if(x<0||x>this.worldWidth||y<0||y>this.worldHeight)return false;for(const wall of this.walls){if(x>=wall.x&&x<=wall.x+wall.width&&y>=wall.y&&y<=wall.y+wall.height){return false}}return true}getNode(x,y){const gridX=Math.floor(x/this.gridSize);const gridY=Math.floor(y/this.gridSize);if(gridX<0||gridX>=this.gridCols||gridY<0||gridY>=this.gridRows){return null}return this.grid[gridY][gridX]}getNeighbors(node){const neighbors=[];const x=Math.floor(node.x/this.gridSize);const y=Math.floor(node.y/this.gridSize);const directions=[{x:0,y:-1},{x:1,y:0},{x:0,y:1},{x:-1,y:0},{x:1,y:-1},{x:1,y:1},{x:-1,y:1},{x:-1,y:-1}];for(const dir of directions){const newX=x+dir.x;const newY=y+dir.y;if(newX>=0&&newX<this.gridCols&&newY>=0&&newY<this.gridRows){const neighbor=this.grid[newY][newX];if(neighbor.walkable){if(Math.abs(dir.x)===1&&Math.abs(dir.y)===1){const neighbor1=this.grid[y][newX];const neighbor2=this.grid[newY][x];if(neighbor1.walkable&&neighbor2.walkable){neighbors.push(neighbor)}}else{neighbors.push(neighbor)}}}}return neighbors}heuristic(nodeA,nodeB){return Math.abs(nodeA.x-nodeB.x)+Math.abs(nodeA.y-nodeB.y)}findPath(startX,startY,endX,endY){const startNode=this.getNode(startX,startY);const endNode=this.getNode(endX,endY);if(!startNode||!endNode||!startNode.walkable||!endNode.walkable){return null}this.resetGrid();const openList=[];let currentNode=startNode;openList.push(startNode);while(openList.length>0){let lowestIndex=0;for(let i=0;i<openList.length;i++){if(openList[i].f<openList[lowestIndex].f){lowestIndex=i}}currentNode=openList[lowestIndex];if(currentNode===endNode){const path=[];let temp=currentNode;while(temp.parent){path.push({x:temp.x,y:temp.y});temp=temp.parent}path.reverse();return path}openList.splice(lowestIndex,1);currentNode.closed=true;const neighbors=this.getNeighbors(currentNode);for(const neighbor of neighbors){if(neighbor.closed)continue;const gScore=currentNode.g+this.heuristic(currentNode,neighbor);if(!neighbor.visited){neighbor.visited=true;neighbor.parent=currentNode;neighbor.g=gScore;neighbor.h=this.heuristic(neighbor,endNode);neighbor.f=neighbor.g+neighbor.h;openList.push(neighbor)}else if(gScore<neighbor.g){neighbor.parent=currentNode;neighbor.g=gScore;neighbor.f=neighbor.g+neighbor.h}}}return null}resetGrid(){for(let y=0;y<this.gridRows;y++){for(let x=0;x<this.gridCols;x++){const node=this.grid[y][x];node.f=node.g=node.h=0;node.parent=null;node.closed=node.visited=false}}}simplifyPath(path){if(path.length<=2)return path;const simplified=[path[0]];let lastDirection=null;for(let i=1;i<path.length-1;i++){const prev=path[i-1];const curr=path[i];const next=path[i+1];const dir1={x:Math.sign(curr.x-prev.x),y:Math.sign(curr.y-prev.y)};const dir2={x:Math.sign(next.x-curr.x),y:Math.sign(next.y-curr.y)};if(dir1.x!==dir2.x||dir1.y!==dir2.y){simplified.push(curr)}}simplified.push(path[path.length-1]);return simplified}}const world={width:2500,height:2500,offsetX:0,offsetY:0};let pathfinder=null;const bombSites=[{x:1800,y:1000,radius:60,name:'A点'},{x:600,y:1600,radius:60,name:'B点'}];const spawnPoints={ct:[{x:400,y:400},{x:300,y:500},{x:500,y:300},{x:450,y:450},{x:350,y:350}],t:[{x:2000,y:2000},{x:1900,y:1900},{x:2100,y:2100},{x:1950,y:1950},{x:2050,y:2050}]};let walls=[];let bullets=[];let hitEffects=[];let explosionEffects=[];let weaponDrops=[];let bulletTrails=[];let teammates=[];let enemies=[];const aiEconomy={ct:Array(5).fill(800),t:Array(5).fill(800)};const aimLine={visible:false,startX:0,startY:0,endX:0,endY:0};const keys={};const mouse={x:canvas.width/2,y:canvas.height/2,down:false};function initGame(){showTeamSelect()}function startGame(){gameStarted=true;initGameWorld();initPathfinding();initWeaponSelector();initShop();updateStats();requestAnimationFrame(gameLoop);setInterval(updateGameTimer,1000/60)}function showTeamSelect(){teamSelectOverlay.style.display='flex';gameStarted=false;const teamOptions=document.querySelectorAll('.team-option');teamOptions.forEach(option=>{option.addEventListener('click',()=>{const selectedTeam=option.dataset.team;selectTeam(selectedTeam)})})}function selectTeam(team){console.log(`玩家选择了${team}阵营`);teamSelectOverlay.style.display='none';if(team==='ct'){player.team='ct';player.color='#4A90E2';player.secondaryWeapon='usp';gameState.currentWeapon='usp';weapons.usp.ammoInMag=weapons.usp.magSize;weapons.usp.ammo=weapons.usp.magSize*2}else{player.team='t';player.color='#FF6B6B';player.secondaryWeapon='glock';gameState.currentWeapon='glock';weapons.glock.ammoInMag=weapons.glock.magSize;weapons.glock.ammo=weapons.glock.magSize*2;performInitialTeamSwap()}showMessage(`已加入${team==='ct'?'反恐精英':'恐怖分子'}阵营`,team==='ct'?'#4A90E2':'#FF6B6B');startGame()}function performInitialTeamSwap(){console.log("执行初始队伍交换");const temp=teammates;teammates=enemies;enemies=temp;const tempEconomy=aiEconomy.ct;aiEconomy.ct=aiEconomy.t;aiEconomy.t=tempEconomy;teammates.forEach(ai=>{ai.team='ct';ai.color='#4A90E2';ai.secondaryWeapon='usp';ai.currentWeapon='usp';weapons.usp.ammoInMag=weapons.usp.magSize;weapons.usp.ammo=weapons.usp.magSize*2});enemies.forEach(ai=>{ai.team='t';ai.color='#FF6B6B';ai.secondaryWeapon='glock';ai.currentWeapon='glock';weapons.glock.ammoInMag=weapons.glock.magSize;weapons.glock.ammo=weapons.glock.magSize*2;ai.hasBomb=false});player.hasBomb=true;updateStats()}function initGameWorld(){const spawns=player.team==='ct'?spawnPoints.ct:spawnPoints.t;const spawnIndex=Math.floor(Math.random()*spawns.length);player.x=spawns[spawnIndex].x;player.y=spawns[spawnIndex].y;console.log(`玩家重生在${player.team}阵营位置(${player.x},${player.y})`);player.alive=true;player.health=100;player.hasBomb=false;player.hasDefuseKit=false;world.offsetX=canvas.width/2-player.x;world.offsetY=canvas.height/2-player.y;walls=[];walls.push({x:0,y:0,width:world.width,height:50,color:'#5D4037'});walls.push({x:0,y:world.height-50,width:world.width,height:50,color:'#5D4037'});walls.push({x:0,y:0,width:50,height:world.height,color:'#5D4037'});walls.push({x:world.width-50,y:0,width:50,height:world.height,color:'#5D4037'});walls.push({x:1800,y:600,width:100,height:400,color:'#8B4513'});walls.push({x:1650,y:800,width:300,height:50,color:'#8B4513'});walls.push({x:1500,y:900,width:50,height:200,color:'#8B4513'});walls.push({x:1700,y:750,width:50,height:50,color:'#8B4513'});walls.push({x:400,y:1500,width:150,height:100,color:'#8B4513'});walls.push({x:600,y:1400,width:200,height:50,color:'#8B4513'});walls.push({x:500,y:1300,width:50,height:150,color:'#8B4513'});walls.push({x:450,y:1450,width:100,height:50,color:'#8B4513'});walls.push({x:1150,y:500,width:50,height:300,color:'#8B4513'});walls.push({x:1200,y:500,width:50,height:300,color:'#8B4037'});walls.push({x:1000,y:900,width:200,height:50,color:'#8B4513'});walls.push({x:1300,y:1100,width:100,height:50,color:'#8B4513'});for(let i=0;i<10;i++){let validPosition=false;let attempts=0;let x,y,width,height;while(!validPosition&&attempts<50){x=Math.random()*(world.width-200)+100;y=Math.random()*(world.height-200)+100;width=Math.random()*60+40;height=Math.random()*60+40;let tooClose=false;for(const spawn of[...spawnPoints.ct,...spawnPoints.t]){if(Math.hypot(x-spawn.x,y-spawn.y)<250){tooClose=true;break}}for(const site of bombSites){if(Math.hypot(x-site.x,y-site.y)<200){tooClose=true;break}}for(const wall of walls){if(Math.hypot(x-wall.x,y-wall.y)<150){tooClose=true;break}}if(!tooClose)validPosition=true;attempts++}if(validPosition){walls.push({x,y,width,height,color:'#8B4513'})}}weaponDrops=[];let bombCarrierIndex;if(player.team==='t'){bombCarrierIndex=Math.floor(Math.random()*4)}else{bombCarrierIndex=Math.floor(Math.random()*5)}initTeammates();initEnemies(bombCarrierIndex);if(player.team==='t'&&bombCarrierIndex===0){player.hasBomb=true;console.log("玩家是炸弹携带者")}gameState.bombPlanted=false;gameState.bombSite=null;gameState.bombTimeLeft=gameState.bombTimer;bombIndicator.style.display='none';gameState.playerAlive=true;gameState.playerHealth=100;gameState.playerArmor=player.armor;gameState.playerHasBomb=false;gameState.playerHasDefuseKit=player.hasDefuseKit;gameState.playerKnifing=false;gameState.playerKnifeProgress=0;bullets=[];hitEffects=[];explosionEffects=[];bulletTrails=[];console.log("游戏世界初始化完成")}function initPathfinding(){pathfinder=new AStarPathfinder(walls,world.width,world.height,40)}function initTeammates(){teammates=[];const ctNames=['Alpha','Bravo','Charlie','Delta','Eanna'];let aiCount=player.team==='ct'?4:5;for(let i=0;i<aiCount;i++){const spawnIndex=i<spawnPoints.ct.length-1?i:i%(spawnPoints.ct.length-1);teammates.push({id:`ct_${i}`,name:ctNames[i],team:'ct',x:spawnPoints.ct[spawnIndex].x+(Math.random()*50-25),y:spawnPoints.ct[spawnIndex].y+(Math.random()*50-25),radius:12,speed:3.5,color:'#4A90E2',direction:Math.random()*Math.PI*2,health:100,armor:0,alive:true,wasAlive:true,previousPrimary:null,previousSecondary:'usp',previousArmor:0,currentPath:null,pathIndex:0,lastPathCalc:0,shouldApproachForMelee:false,lastPathUpdate:0,avoidanceDirection:null,avoidanceTimer:0,lastDirectionChange:0,lastBypassAttempt:0,lastDistanceToTarget:undefined,stuckTimer:0,lastMoveCheck:0,lastMoveX:0,lastMoveY:0,lastMoveTime:0,moveThreshold:3,primaryWeapon:null,secondaryWeapon:'usp',knife:'knife',currentWeaponType:'secondary',currentWeapon:'usp',lastShot:0,lastMoveChange:0,moveDirectionChangeInterval:1500+Math.random()*2000,lastTargetUpdate:0,targetUpdateInterval:1000,currentTarget:null,hasDefuseKit:i===0,behavior:'patrol',patrolPoint:null,lastBehaviorChange:0,behaviorChangeInterval:5000+Math.random()*10000,lastHitBy:null,lastHitTime:0,lastWeaponSwitch:0,lastMoveTime:Date.now(),isMoving:false,lastKnifeAttack:0,knifeAttackCooldown:500,bombDefusing:false,bombDefuseProgress:0})}}function initEnemies(bombCarrierIndex){enemies=[];const tNames=['Tango','Victor','Whiskey','Xray','Yankee'];let aiCount=player.team==='t'?4:5;for(let i=0;i<aiCount;i++){const spawnIndex=i<spawnPoints.t.length?i:i%spawnPoints.t.length;enemies.push({id:`t_${i}`,name:tNames[i],team:'t',x:spawnPoints.t[spawnIndex].x+(Math.random()*50-25),y:spawnPoints.t[spawnIndex].y+(Math.random()*50-25),radius:12,speed:3.5,color:'#FF6B6B',direction:Math.random()*Math.PI*2,health:100,armor:0,alive:true,wasAlive:true,previousPrimary:null,previousSecondary:'glock',previousArmor:0,currentPath:null,shouldApproachForMelee:false,pathIndex:0,lastPathCalc:0,lastPathUpdate:0,avoidanceDirection:null,avoidanceTimer:0,lastDirectionChange:0,lastBypassAttempt:0,lastDistanceToTarget:undefined,stuckTimer:0,lastMoveCheck:0,lastMoveX:0,lastMoveY:0,lastMoveTime:0,moveThreshold:3,bombPlanting:false,bombPlantProgress:0,bombPlantSite:null,reachedPlantSite:false,behavior:'patrol',currentTarget:null,patrolPoint:null,primaryWeapon:null,secondaryWeapon:'glock',knife:'knife',currentWeaponType:'secondary',currentWeapon:'glock',lastShot:0,lastMoveChange:0,moveDirectionChangeInterval:1500+Math.random()*2000,lastTargetUpdate:0,targetUpdateInterval:1000,currentTarget:null,hasBomb:i===bombCarrierIndex,behavior:'patrol',patrolPoint:null,lastBehaviorChange:0,behaviorChangeInterval:5000+Math.random()*10000,bombPlanting:false,bombPlantProgress:0,lastHitBy:null,lastHitTime:0,lastWeaponSwitch:0,isMoving:false,lastKnifeAttack:0,knifeAttackCooldown:500})}}function initWeaponSelector(){const weaponSelector=document.getElementById('weaponSelector');weaponSelector.innerHTML='';if(player.primaryWeapon){const weapon=weapons[player.primaryWeapon];const weaponElement=document.createElement('div');weaponElement.className=`weapon ${gameState.currentWeaponType==='primary'?'active':''}`;weaponElement.dataset.weapon=player.primaryWeapon;weaponElement.dataset.type='primary';weaponElement.innerHTML=`<div class="weapon-icon">${weapon.icon}</div><div class="weapon-name">${weapon.name}</div><div class="weapon-info">${weapon.ammoInMag}/${weapon.ammo}</div>`;weaponElement.addEventListener('click',()=>{if(gameState.playerReloading||gameState.phase!=='play'||!player.primaryWeapon)return;gameState.currentWeaponType='primary';gameState.currentWeapon=player.primaryWeapon;updateWeaponUI();updateStats()});weaponSelector.appendChild(weaponElement)}if(player.secondaryWeapon){const weapon=weapons[player.secondaryWeapon];const weaponElement=document.createElement('div');weaponElement.className=`weapon ${gameState.currentWeaponType==='secondary'?'active':''}`;weaponElement.dataset.weapon=player.secondaryWeapon;weaponElement.dataset.type='secondary';weaponElement.innerHTML=`<div class="weapon-icon">${weapon.icon}</div><div class="weapon-name">${weapon.name}</div><div class="weapon-info">${weapon.ammoInMag}/${weapon.ammo}</div>`;weaponElement.addEventListener('click',()=>{if(gameState.playerReloading||gameState.phase!=='play')return;gameState.currentWeaponType='secondary';gameState.currentWeapon=player.secondaryWeapon;updateWeaponUI();updateStats()});weaponSelector.appendChild(weaponElement)}const knife=weapons.knife;const knifeElement=document.createElement('div');knifeElement.className=`weapon ${gameState.currentWeaponType==='melee'?'active':''}`;knifeElement.dataset.weapon='knife';knifeElement.dataset.type='melee';knifeElement.innerHTML=`<div class="weapon-icon">${knife.icon}</div><div class="weapon-name">${knife.name}</div><div class="weapon-info">近战</div>`;knifeElement.addEventListener('click',()=>{if(gameState.playerReloading||gameState.phase!=='play')return;gameState.currentWeaponType='melee';gameState.currentWeapon='knife';updateWeaponUI();updateStats()});weaponSelector.appendChild(knifeElement)}function updateWeaponUI(){const weaponElements=document.querySelectorAll('.weapon');weaponElements.forEach(el=>el.classList.remove('active'));const activeWeapon=document.querySelector(`.weapon[data-weapon="${gameState.currentWeapon}"]`);if(activeWeapon)activeWeapon.classList.add('active')}function initShop(){const categories=document.querySelectorAll('.shop-category');categories.forEach(category=>{category.addEventListener('click',()=>{categories.forEach(c=>c.classList.remove('active'));category.classList.add('active');displayShopItems(category.dataset.category)})});displayShopItems('pistols');closeShopBtn.addEventListener('click',closeShop)}function updateStats(){document.getElementById('ctScore').textContent=gameState.ctScore;document.getElementById('tScore').textContent=gameState.tScore;document.getElementById('roundNumber').textContent=gameState.round;let phaseText='',phaseColor='';switch(gameState.phase){case'buy':phaseText='购买阶段';phaseColor='#FFC107';break;case'play':phaseText='战斗阶段';phaseColor='#4CAF50';break;case'over':phaseText='回合结束';phaseColor='#F44336';break}const gamePhaseEl=document.getElementById('gamePhase');gamePhaseEl.textContent=phaseText;gamePhaseEl.style.backgroundColor=`rgba(${phaseColor==='#FFC107'?'255,193,7':phaseColor==='#4CAF50'?'76,175,80':'244,67,54'},0.2)`;gamePhaseEl.style.color=phaseColor;document.getElementById('health').textContent=gameState.playerHealth;document.getElementById('armor').textContent=gameState.playerArmor;document.getElementById('money').textContent=gameState.playerMoney;document.getElementById('kda').textContent=`${gameState.playerKills}/${gameState.playerDeaths}`;const weapon=weapons[gameState.currentWeapon];if(weapon){if(weapon.type==='melee'){document.getElementById('ammo').textContent='--/--'}else{document.getElementById('ammo').textContent=`${weapon.ammoInMag}/${weapon.ammo}`}}updateTimerDisplay();shopMoney.textContent=gameState.playerMoney;updatePlayerList()}function updateTimerDisplay(){let timeLeft=0;if(gameState.phase==='buy'){timeLeft=gameState.buyTimeLeft}else if(gameState.phase==='play'){timeLeft=gameState.roundTimeLeft}const minutes=Math.floor(timeLeft/60);const seconds=Math.floor(timeLeft%60);document.getElementById('timer').textContent=`${minutes}:${seconds.toString().padStart(2,'0')}`;if(gameState.phase==='buy'){buyTimeDisplay.textContent=Math.ceil(gameState.buyTimeLeft)}}function updatePlayerList(){ctPlayersList.innerHTML='';tPlayersList.innerHTML='';const playerItem=document.createElement('div');playerItem.className=`player-item ${player.team}${!player.alive?'dead':''}`;const playerHealthClass=player.alive?(player.health>50?'full':player.health>25?'low':'critical'):'dead';let specialIcon='';if(player.hasBomb)specialIcon=' 💣';if(player.hasDefuseKit)specialIcon=' 🔧';playerItem.innerHTML=`<div class="player-name">${player.name}$${player.money}${specialIcon}</div><div class="player-health ${playerHealthClass}">${player.alive?player.health+' HP':'死亡'}</div>`;if(player.team==='ct'){ctPlayersList.appendChild(playerItem)}else{tPlayersList.appendChild(playerItem)}teammates.forEach((teammate,index)=>{const teammateItem=document.createElement('div');teammateItem.className=`player-item ct ${!teammate.alive?'dead':''}`;const teammateHealthClass=teammate.alive?(teammate.health>50?'full':teammate.health>25?'low':'critical'):'dead';const money=aiEconomy.ct&&index<aiEconomy.ct.length?aiEconomy.ct[index]:0;let aiSpecialIcon=teammate.hasDefuseKit?' 🔧':'';teammateItem.innerHTML=`<div class="player-name">${teammate.name}$${money}${aiSpecialIcon}</div><div class="player-health ${teammateHealthClass}">${teammate.alive?teammate.health+' HP':'死亡'}</div>`;ctPlayersList.appendChild(teammateItem)});enemies.forEach((enemy,index)=>{const enemyItem=document.createElement('div');enemyItem.className=`player-item t ${!enemy.alive?'dead':''}`;const enemyHealthClass=enemy.alive?(enemy.health>50?'full':enemy.health>25?'low':'critical'):'dead';const money=aiEconomy.t&&index<aiEconomy.t.length?aiEconomy.t[index]:0;let aiSpecialIcon=enemy.hasBomb?' 💣':'';enemyItem.innerHTML=`<div class="player-name">${enemy.name}$${money}${aiSpecialIcon}</div><div class="player-health ${enemyHealthClass}">${enemy.alive?enemy.health+' HP':'死亡'}</div>`;tPlayersList.appendChild(enemyItem)});const ctCount=(player.team==='ct'?1:0)+teammates.length;const tCount=(player.team==='t'?1:0)+enemies.length;console.log(`CT:${ctCount}人,T:${tCount}人`)}function displayShopItems(category){shopItems.innerHTML='';let items=[];switch(category){case'pistols':items=Object.entries(weapons).filter(([id,weapon])=>weapon.type==='pistol');break;case'rifles':items=Object.entries(weapons).filter(([id,weapon])=>weapon.type==='rifle');break;case'smgs':items=Object.entries(weapons).filter(([id,weapon])=>weapon.type==='smg');break;case'heavy':items=Object.entries(weapons).filter(([id,weapon])=>weapon.type==='heavy');break;case'gear':items=Object.entries(gearItems);break}items=items.filter(([id,item])=>{if(item.team==='both'||item.team===player.team)return true;return false});items.forEach(([id,item])=>{const itemElement=document.createElement('div');let alreadyOwned=false;if(item.type==='pistol'){alreadyOwned=player.secondaryWeapon===id}else if(item.type==='rifle'||item.type==='smg'||item.type==='heavy'){alreadyOwned=player.primaryWeapon===id}else if(item.type==='armor'){alreadyOwned=gameState.playerArmor>=item.armorValue}else if(item.type==='defuse'){alreadyOwned=player.hasDefuseKit}itemElement.className=`shop-item ${gameState.playerMoney<item.price||alreadyOwned?'unavailable':''}`;itemElement.innerHTML=`<div class="item-icon">${item.icon}</div><div class="item-name">${item.name}</div><div class="item-price">$${item.price}</div><div class="item-desc">${item.description||`伤害:${item.damage}|射程:${item.range}`}</div>`;if(alreadyOwned){itemElement.innerHTML+=`<div style="color:#ff7e5f; font-size:0.8rem; margin-top:5px;">已拥有</div>`}if(!itemElement.classList.contains('unavailable')){itemElement.addEventListener('click',()=>purchaseItem(id,item))}shopItems.appendChild(itemElement)})}function purchaseItem(id,item){if(gameState.playerMoney<item.price)return;gameState.playerMoney-=item.price;player.money=gameState.playerMoney;if(item.type==='armor'){gameState.playerArmor=Math.max(gameState.playerArmor,item.armorValue);player.armor=gameState.playerArmor;showMessage(`购买了${item.name}`,'#4CAF50')}else if(item.type==='defuse'){gameState.playerHasDefuseKit=true;player.hasDefuseKit=true;showMessage(`购买了${item.name}`,'#4CAF50')}else if(item.type==='pistol'){player.secondaryWeapon=id;weapons[id].ammoInMag=weapons[id].magSize;weapons[id].ammo=weapons[id].magSize*4;if(gameState.currentWeaponType==='secondary'){gameState.currentWeapon=id}initWeaponSelector();showMessage(`购买了${item.name}`,'#4CAF50')}else if(item.type==='rifle'||item.type==='smg'||item.type==='heavy'){player.primaryWeapon=id;weapons[id].ammoInMag=weapons[id].magSize;weapons[id].ammo=weapons[id].magSize*4;if(!gameState.currentWeaponType==='primary'){gameState.currentWeaponType='primary';gameState.currentWeapon=id}initWeaponSelector();showMessage(`购买了${item.name}`,'#4CAF50')}updateStats();displayShopItems(document.querySelector('.shop-category.active').dataset.category)}function showMessage(text,color='#ffffff'){gameMessage.textContent=text;gameMessage.style.color=color;gameMessage.style.display='block';setTimeout(()=>{gameMessage.style.display='none'},2000)}function openShop(){if(gameState.phase!=='buy')return;gameState.shopOpen=true;shopOverlay.style.display='flex';shopMoney.textContent=gameState.playerMoney;displayShopItems(document.querySelector('.shop-category.active').dataset.category)}function closeShop(){gameState.shopOpen=false;shopOverlay.style.display='none'}document.addEventListener('keydown',(e)=>{const key=e.key.toLowerCase();keys[key]=true;if(key==='b'&&gameState.phase==='buy'){openShop()}if(e.key==='Escape'){closeShop()}if(e.key==='1'&&gameState.phase==='play'&&player.alive&&player.primaryWeapon){gameState.currentWeaponType='primary';gameState.currentWeapon=player.primaryWeapon;updateWeaponUI();updateStats()}if(e.key==='2'&&gameState.phase==='play'&&player.alive){gameState.currentWeaponType='secondary';gameState.currentWeapon=player.secondaryWeapon;updateWeaponUI();updateStats()}if(e.key==='3'&&gameState.phase==='play'&&player.alive){gameState.currentWeaponType='melee';gameState.currentWeapon='knife';updateWeaponUI();updateStats()}if(key==='q'&&gameState.phase==='play'&&player.alive){const currentTime=Date.now();if(currentTime-player.lastWeaponSwitch<player.weaponSwitchCooldown)return;if(gameState.currentWeaponType==='primary'&&player.secondaryWeapon){gameState.currentWeaponType='secondary';gameState.currentWeapon=player.secondaryWeapon}else if(gameState.currentWeaponType==='secondary'&&player.primaryWeapon){gameState.currentWeaponType='primary';gameState.currentWeapon=player.primaryWeapon}else if(gameState.currentWeaponType==='melee'&&player.primaryWeapon){gameState.currentWeaponType='primary';gameState.currentWeapon=player.primaryWeapon}else{gameState.currentWeaponType='melee';gameState.currentWeapon='knife'}player.lastWeaponSwitch=currentTime;updateWeaponUI();updateStats()}if(key==='r'&&!gameState.playerReloading&&gameState.phase==='play'&&player.alive){const weapon=weapons[gameState.currentWeapon];if(weapon&&weapon.type!=='melee'&&weapon.ammoInMag<weapon.magSize&&weapon.ammo>0){startReload()}}if(key==='f'&&gameState.phase==='play'){gameState.playerFlashlight=!gameState.playerFlashlight}if(key==='e'&&gameState.phase==='play'&&player.alive){interact()}if(e.key==='Tab'){e.preventDefault();if(!player.alive&&!gameState.spectatorMode){initSpectatorMode()}else if(gameState.spectatorMode){switchSpectatorTarget()}}if(key==='r'&&gameState.spectatorMode&&player.alive){gameState.spectatorMode=false;gameState.spectatorTarget=null;if(typeof gameState.playerFlashlightBeforeSpectate!=='undefined'){gameState.playerFlashlight=gameState.playerFlashlightBeforeSpectate}world.offsetX=canvas.width/2-player.x;world.offsetY=canvas.height/2-player.y;showMessage("已退出观战模式","#4CAF50")}});document.addEventListener('keyup',(e)=>{keys[e.key.toLowerCase()]=false});canvas.addEventListener('mousemove',(e)=>{const rect=canvas.getBoundingClientRect();mouse.x=e.clientX-rect.left;mouse.y=e.clientY-rect.top;const screenCenterX=canvas.width/2;const screenCenterY=canvas.height/2;player.direction=Math.atan2(mouse.y-screenCenterY,mouse.x-screenCenterX);updateAimLine()});canvas.addEventListener('mousedown',()=>{mouse.down=true});canvas.addEventListener('mouseup',()=>{mouse.down=false});function updateGameTimer(){if(!gameStarted||gameState.gameOver||gameState.shopOpen)return;if(gameState.phase==='buy'){if(gameState.buyTimeLeft>0){gameState.buyTimeLeft-=1/60;if(gameState.buyTimeLeft<=0){gameState.buyTimeLeft=0;closeShop();gameState.phase='play';gameState.roundTimeLeft=gameState.roundTime;console.log("购买阶段结束,战斗开始")}}}else if(gameState.phase==='play'){if(gameState.roundTimeLeft>0){gameState.roundTimeLeft-=1/60;if(gameState.roundTimeLeft<=0){gameState.roundTimeLeft=0;if(gameState.bombPlanted){endRound('t','炸弹爆炸')}else{endRound('ct','时间结束')}}}if(gameState.bombPlanted&&gameState.bombTimeLeft>0){gameState.bombTimeLeft-=1/60;bombIndicator.textContent=`炸弹-${gameState.bombSite.name}:${Math.ceil(gameState.bombTimeLeft)}s`;if(gameState.bombTimeLeft<=0){gameState.bombTimeLeft=0;endRound('t','炸弹爆炸')}}}updateTimerDisplay()}function updatePlayer(){if(gameState.phase!=='play'||!player.alive||gameState.shopOpen)return;let dx=0,dy=0;let moving=false;if(keys['w']){dy-=player.speed;moving=true}if(keys['s']){dy+=player.speed;moving=true}if(keys['a']){dx-=player.speed;moving=true}if(keys['d']){dx+=player.speed;moving=true}player.isMoving=moving;if(moving){player.lastMoveTime=Date.now();gameState.playerMoving=true;gameState.playerMovingTimer=300;if(player.bombPlanting||player.bombDefusing){player.bombPlanting=false;player.bombPlantProgress=0;player.bombDefusing=false;player.bombDefuseProgress=0;showMessage("行动已取消","#FF6B6B")}}else if(Date.now()-player.lastMoveTime>300){gameState.playerMoving=false}if(dx!==0&&dy!==0){dx*=0.7071;dy*=0.7071}const newX=player.x+dx;const newY=player.y+dy;let canMove=true;for(const wall of walls){const closestX=Math.max(wall.x,Math.min(newX,wall.x+wall.width));const closestY=Math.max(wall.y,Math.min(newY,wall.y+wall.height));const distance=Math.hypot(newX-closestX,newY-closestY);if(distance<player.radius){canMove=false;break}}if(canMove){player.x=newX;player.y=newY;world.offsetX=canvas.width/2-player.x;world.offsetY=canvas.height/2-player.y}if(mouse.down&&!gameState.shopOpen&&player.alive){shoot()}if(gameState.playerReloading){const weapon=weapons[gameState.currentWeapon];if(!weapon||weapon.type==='melee'){gameState.playerReloading=false;reloadIndicator.style.display='none'}else{gameState.playerReloadProgress+=16;if(gameState.playerReloadProgress>=weapon.reloadTime){finishReload()}else{const percent=Math.floor((gameState.playerReloadProgress/weapon.reloadTime)*100);reloadIndicator.textContent=`换弹中...${percent}%`}}}if(gameState.playerKnifing){gameState.playerKnifeProgress+=16;const weapon=weapons.knife;if(gameState.playerKnifeProgress>=weapon.attackRate){gameState.playerKnifing=false;gameState.playerKnifeProgress=0}}updateAimLine();updatePlayerBombActions()}function updatePlayerBombActions(){if(player.bombPlanting){player.bombPlantProgress+=16;if(player.bombPlantProgress>=gameState.bombPlantTime){gameState.bombPlanted=true;gameState.bombSite=player.bombPlantingSite;gameState.bombTimeLeft=gameState.bombTimer;player.hasBomb=false;player.bombPlanting=false;bombIndicator.style.display='block';bombIndicator.textContent=`炸弹已安装-${player.bombPlantingSite.name}`;bombIndicator.style.backgroundColor='rgba(255, 0, 0, 0.7)';showMessage(`炸弹已安放在${player.bombPlantingSite.name}`,'#FF4500');console.log("玩家完成安放炸弹");notifyBombPlanted()}}if(player.bombDefusing){player.bombDefuseProgress+=16;const defuseTime=player.hasDefuseKit?gameState.bombDefuseTimeWithKit:gameState.bombDefuseTime;if(player.bombDefuseProgress>=defuseTime){gameState.bombPlanted=false;gameState.bombSite=null;gameState.bombTimeLeft=gameState.bombTimer;player.bombDefusing=false;bombIndicator.style.display='none';endRound('ct','拆除了炸弹');console.log("玩家完成拆弹")}}}function interact(){if(!player.alive||gameState.phase!=='play')return;for(const site of bombSites){const distance=Math.hypot(player.x-site.x,player.y-site.y);if(distance<site.radius+player.radius){if(player.team==='t'&&player.hasBomb&&!gameState.bombPlanted){startPlayerBombPlant(site);return}if(player.team==='ct'&&gameState.bombPlanted&&gameState.bombSite===site){startPlayerBombDefuse(site);return}}}const worldX=mouse.x-world.offsetX;const worldY=mouse.y-world.offsetY;for(let i=weaponDrops.length-1;i>=0;i--){const drop=weaponDrops[i];const distance=Math.hypot(worldX-drop.x,worldY-drop.y);if(distance<drop.radius+player.radius){const weapon=weapons[drop.weaponId];if(weapon){if(weapon.type==='pistol'){if(player.secondaryWeapon){createWeaponDrop(player.x,player.y,player.secondaryWeapon)}player.secondaryWeapon=drop.weaponId;weapons[drop.weaponId].ammoInMag=weapons[drop.weaponId].magSize;weapons[drop.weaponId].ammo=weapons[drop.weaponId].magSize*2}else{if(player.primaryWeapon){createWeaponDrop(player.x,player.y,player.primaryWeapon)}player.primaryWeapon=drop.weaponId;weapons[drop.weaponId].ammoInMag=weapons[drop.weaponId].magSize;weapons[drop.weaponId].ammo=weapons[drop.weaponId].magSize*2}weaponDrops.splice(i,1);initWeaponSelector();updateStats();showMessage(`拾取了${weapon.name}`,'#4CAF50')}break}}}function startPlayerBombPlant(site){if(player.bombPlanting)return;player.bombPlanting=true;player.bombPlantProgress=0;player.bombPlantingSite=site;player.isMoving=false;showMessage("正在安放炸弹... (按移动键取消)","#FF4500");console.log("玩家开始安放炸弹 (需要4秒)")}function startPlayerBombDefuse(site){if(player.bombDefusing)return;player.bombDefusing=true;player.bombDefuseProgress=0;player.isMoving=false;const defuseTime=player.hasDefuseKit?gameState.bombDefuseTimeWithKit:gameState.bombDefuseTime;showMessage(`正在拆除炸弹...(${player.hasDefuseKit?'有拆弹器,5秒':'无拆弹器,10秒'},按移动键取消)`,"#4CAF50");console.log(`玩家开始拆弹(${player.hasDefuseKit?'有拆弹器,需要5秒':'无拆弹器,需要10秒'})`)}function shoot(){if(gameState.playerReloading||!player.alive)return;const currentTime=Date.now();const weapon=weapons[gameState.currentWeapon];if(!weapon)return;if(weapon.type==='melee'){knifeAttack();return}if(currentTime-player.lastShot<weapon.fireRate)return;if(weapon.ammoInMag<=0){startReload();return}weapon.ammoInMag--;player.lastShot=currentTime;const angle=player.direction;if(weapon.isDual){weapon.currentBarrel=weapon.currentBarrel===0?1:0;const barrelOffset=weapon.currentBarrel===0?-6:6;const offsetX=player.x+Math.cos(angle+Math.PI/2)*barrelOffset;const offsetY=player.y+Math.sin(angle+Math.PI/2)*barrelOffset;const spread=calculateSpread(weapon,gameState.playerMoving);const bulletAngle=angle+spread;createBullet(offsetX,offsetY,bulletAngle,weapon);createBulletTrail(offsetX,offsetY,offsetX+Math.cos(bulletAngle)*100,offsetY+Math.sin(bulletAngle)*100,weapon.color)}else{const spread=calculateSpread(weapon,gameState.playerMoving);const bulletAngle=angle+spread;createBullet(player.x,player.y,bulletAngle,weapon);createBulletTrail(player.x,player.y,player.x+Math.cos(bulletAngle)*100,player.y+Math.sin(bulletAngle)*100,weapon.color)}updateStats();updateWeaponUI()}function calculateSpread(weapon,isMoving){let totalSpread=weapon.spread;if(isMoving){totalSpread*=weapon.moveSpreadMultiplier;const moveRandomness=Math.random()*0.15;totalSpread*=(1+moveRandomness)}const coneSpread=(Math.random()-0.5)*totalSpread*2;const randomOffset=(Math.random()-0.5)*totalSpread*0.3;return coneSpread+randomOffset}function createBullet(x,y,angle,weapon){bullets.push({x:x,y:y,radius:4,speed:weapon.bulletSpeed,angle:angle,damage:weapon.damage,range:weapon.range,distance:0,color:weapon.color,team:player.team,shooter:player})}function createBulletTrail(startX,startY,endX,endY,color){for(let i=0;i<5;i++){const progress=Math.random();const trailX=startX+(endX-startX)*progress;const trailY=startY+(endY-startY)*progress;bulletTrails.push({x:trailX,y:trailY,radius:1+Math.random()*2,alpha:0.6+Math.random()*0.3,color:color,life:8+Math.random()*4})}}function updateBullets(){for(let i=bullets.length-1;i>=0;i--){const bullet=bullets[i];const prevX=bullet.x-Math.cos(bullet.angle)*bullet.speed;const prevY=bullet.y-Math.sin(bullet.angle)*bullet.speed;bullet.x+=Math.cos(bullet.angle)*bullet.speed;bullet.y+=Math.sin(bullet.angle)*bullet.speed;bullet.distance+=bullet.speed;if(bullet.distance>bullet.range*1.5){bullets.splice(i,1);continue}let hitWall=false;let hitPos={x:bullet.x,y:bullet.y};for(const wall of walls){if(lineRectIntersection(prevX,prevY,bullet.x,bullet.y,wall.x,wall.y,wall.width,wall.height)){hitWall=true;const intersect=getLineRectIntersection(prevX,prevY,bullet.x,bullet.y,wall.x,wall.y,wall.width,wall.height);if(intersect)hitPos=intersect;break}}if(hitWall){createHitEffect(hitPos.x,hitPos.y,bullet.color);bullets.splice(i,1);continue}let hitTarget=false;if(player.alive&&bullet.team!==player.team){if(lineCircleIntersection(prevX,prevY,bullet.x,bullet.y,player.x,player.y,player.radius)){handleBulletHit(bullet,player,'player');bullets.splice(i,1);hitTarget=true}}if(hitTarget)continue;for(let j=teammates.length-1;j>=0;j--){const teammate=teammates[j];if(!teammate.alive||bullet.team===teammate.team)continue;if(lineCircleIntersection(prevX,prevY,bullet.x,bullet.y,teammate.x,teammate.y,teammate.radius)){handleBulletHit(bullet,teammate,'ct');bullets.splice(i,1);hitTarget=true;break}}if(hitTarget)continue;for(let j=enemies.length-1;j>=0;j--){const enemy=enemies[j];if(!enemy.alive||bullet.team===enemy.team)continue;if(lineCircleIntersection(prevX,prevY,bullet.x,bullet.y,enemy.x,enemy.y,enemy.radius)){handleBulletHit(bullet,enemy,'t');bullets.splice(i,1);break}}}}function lineCircleIntersection(x1,y1,x2,y2,cx,cy,radius){const dx=x2-x1;const dy=y2-y1;const fx=cx-x1;const fy=cy-y1;const d2=dx*dx+dy*dy;const dot=fx*dx+fy*dy;let t=dot/d2;t=Math.max(0,Math.min(1,t));const closestX=x1+t*dx;const closestY=y1+t*dy;const distance=Math.hypot(closestX-cx,closestY-cy);return distance<=radius}function handleBulletHit(bullet,target,targetType){let damage=bullet.damage;const headDistance=Math.hypot(bullet.x-target.x,bullet.y-(target.y-target.radius*0.5));if(headDistance<target.radius*0.6){damage*=2}if(target.armor>0){damage*=0.5;target.armor-=damage*0.5;if(target.armor<0)target.armor=0}target.health-=damage;createHitEffect(bullet.x,bullet.y,bullet.color);if(targetType==='player'){showHitIndicator(Math.round(damage),bullet.x,bullet.y);updateStats()}if(targetType!=='player'){target.lastHitBy=bullet.shooter;target.lastHitTime=Date.now();target.behavior='attack';target.currentTarget=bullet.shooter;target.lastBehaviorChange=Date.now()}if(target.health<=0){target.health=0;target.alive=false;createExplosionEffect(target.x,target.y);if(target.primaryWeapon){createWeaponDrop(target.x,target.y,target.primaryWeapon)}if(target===player){gameState.playerDeaths++;player.deaths=gameState.playerDeaths;console.log(`玩家死亡,死亡数:${gameState.playerDeaths}`)}if(bullet.shooter===player){gameState.playerKills++;player.kills=gameState.playerKills;gameState.playerMoney+=300;player.money=gameState.playerMoney;showMessage(`击杀${target.name}`,'#4CAF50')}if(bullet.shooter&&bullet.shooter.id){const team=bullet.shooter.id.startsWith('ct_')?'ct':'t';const index=parseInt(bullet.shooter.id.split('_')[1]);aiGetMoney(team,index,300)}if(targetType==='player'){resetHitIndicator()}updateStats();checkRoundEnd()}}function checkRoundEnd(){const ctAliveCount=teammates.filter(t=>t.alive).length+(player.alive&&player.team==='ct'?1:0);const tAliveCount=enemies.filter(e=>e.alive).length+(player.alive&&player.team==='t'?1:0);console.log(`CT存活:${ctAliveCount},T存活:${tAliveCount},炸弹安放:${gameState.bombPlanted}`);if(player.team==='ct'){if(tAliveCount===0){if(gameState.bombPlanted){console.log("T全部死亡但炸弹已安放,CT需要拆包");teammates.forEach(teammate=>{if(teammate.alive){teammate.behavior='defuse';teammate.currentTarget=null;teammate.lastBehaviorChange=Date.now();console.log(`${teammate.name}自动前往拆包`)}});if(player.alive){showMessage("恐怖分子已全灭,请立即拆除炸弹!","#FFA500")}return}else{endRound('ct','消灭所有敌人')}}if(ctAliveCount===0){endRound('t','消灭所有反恐精英')}}else{if(ctAliveCount===0){if(gameState.bombPlanted){endRound('t','消灭所有CT,炸弹已安放')}else{endRound('t','消灭所有反恐精英')}}if(tAliveCount===0){if(gameState.bombPlanted){console.log("T全部死亡但炸弹已安放,CT需要拆包");teammates.forEach(teammate=>{if(teammate.alive){teammate.behavior='defuse';teammate.currentTarget=null;teammate.lastBehaviorChange=Date.now();console.log(`${teammate.name}自动前往拆包`)}});return}else{endRound('ct','消灭所有恐怖分子')}}}}function knifeAttack(){if(gameState.playerKnifing||!player.alive)return;gameState.playerKnifing=true;gameState.playerKnifeProgress=0;const weapon=weapons.knife;const currentTime=Date.now();const attackX=player.x+Math.cos(player.direction)*weapon.range;const attackY=player.y+Math.sin(player.direction)*weapon.range;if(player.team==='ct'){enemies.forEach(enemy=>{if(!enemy.alive)return;const distance=Math.hypot(attackX-enemy.x,attackY-enemy.y);if(distance<enemy.radius+15){handleKnifeHit(weapon,enemy)}})}else{teammates.forEach(teammate=>{if(!teammate.alive)return;const distance=Math.hypot(attackX-teammate.x,attackY-teammate.y);if(distance<teammate.radius+15){handleKnifeHit(weapon,teammate)}})}player.lastShot=currentTime}function handleKnifeHit(weapon,target){let damage=weapon.damage;const headChance=0.3;if(Math.random()<headChance){damage*=1.5}if(target.armor>0){damage*=0.75;target.armor-=damage*0.25;if(target.armor<0)target.armor=0}target.health-=damage;createHitEffect(target.x,target.y,weapon.color);if(target===player){let sourceX,sourceY;sourceX=player.x+Math.cos(player.direction)*50;sourceY=player.y+Math.sin(player.direction)*50;showHitIndicator(Math.round(damage),sourceX,sourceY);updateStats()}if(target.health<=0){target.alive=false;createExplosionEffect(target.x,target.y);if(target===player){gameState.playerDeaths++;player.deaths=gameState.playerDeaths;console.log(`玩家被匕首击杀,死亡数:${gameState.playerDeaths}`)}if(target.primaryWeapon){createWeaponDrop(target.x,target.y,target.primaryWeapon)}gameState.playerKills++;player.kills=gameState.playerKills;gameState.playerMoney+=1500;player.money=gameState.playerMoney;showMessage(`匕首击杀${target.name}`,'#FF4500');if(target===player){resetHitIndicator()}checkRoundEnd()}}function startReload(){const weapon=weapons[gameState.currentWeapon];if(!weapon||weapon.type==='melee'||weapon.ammoInMag===weapon.magSize||weapon.ammo===0){return}gameState.playerReloading=true;gameState.playerReloadProgress=0;reloadIndicator.style.display='block';reloadIndicator.textContent='换弹中...'}function finishReload(){const weapon=weapons[gameState.currentWeapon];if(!weapon||weapon.type==='melee'){gameState.playerReloading=false;reloadIndicator.style.display='none';return}if(typeof weapon.ammoInMag==='undefined')weapon.ammoInMag=weapon.magSize;if(typeof weapon.ammo==='undefined')weapon.ammo=weapon.magSize*2;if(weapon.ammoInMag<weapon.magSize&&weapon.ammo>0){const needed=weapon.magSize-weapon.ammoInMag;const available=Math.min(needed,weapon.ammo);weapon.ammoInMag+=available;weapon.ammo-=available}gameState.playerReloading=false;reloadIndicator.style.display='none';updateStats();updateWeaponUI()}function updateAI(){const currentTime=Date.now();if(gameState.phase!=='play'){return}teammates.forEach((teammate,index)=>{if(!teammate.alive){teammate.isMoving=false;return}updateAIMovementState(teammate);aiManageWeapons(teammate);const visibleEnemy=getVisibleEnemy(teammate,800);if(visibleEnemy){if(teammate.behavior!=='attack'||teammate.currentTarget!==visibleEnemy){console.log(`${teammate.name}发现可见敌人${visibleEnemy.name},立即攻击`);teammate.behavior='attack';teammate.currentTarget=visibleEnemy;teammate.lastBehaviorChange=Date.now();teammate.bombDefusing=false;teammate.bombDefuseProgress=0}}switch(teammate.behavior){case'attack':handleAIAttack(teammate,index,'ct');break;case'defuse':if(!visibleEnemy){handleAIDefuse(teammate,index)}else{teammate.behavior='attack';teammate.currentTarget=visibleEnemy;teammate.lastBehaviorChange=Date.now()}break;case'patrol':default:if(!visibleEnemy){handleAIPatrol(teammate)}break}if(Math.random()<0.01){aiAutoReload(teammate)}});enemies.forEach((enemy,index)=>{if(!enemy.alive){enemy.isMoving=false;return}updateAIMovementState(enemy);aiManageWeapons(enemy);const visibleEnemy=getVisibleEnemy(enemy,800);if(visibleEnemy){if(enemy.behavior!=='attack'||enemy.currentTarget!==visibleEnemy){console.log(`${enemy.name}发现可见敌人${visibleEnemy.name},立即攻击`);enemy.behavior='attack';enemy.currentTarget=visibleEnemy;enemy.lastBehaviorChange=Date.now();enemy.bombPlanting=false;enemy.bombPlantProgress=0}}if(enemy.lastHitBy&¤tTime-enemy.lastHitTime<1000){if(enemy.behavior!=='plant'){enemy.behavior='attack';enemy.currentTarget=enemy.lastHitBy;enemy.lastBehaviorChange=Date.now();enemy.bombPlanting=false;enemy.bombPlantProgress=0}}if(enemy.hasBomb&&!gameState.bombPlanted&&!enemy.bombPlanting){const nearbyEnemy=hasEnemyNearby(enemy,300);if(nearbyEnemy){enemy.behavior='attack';enemy.currentTarget=nearbyEnemy;enemy.bombPlanting=false;enemy.bombPlantProgress=0}else if(!visibleEnemy){enemy.behavior='plant';enemy.currentTarget=null;enemy.lastBehaviorChange=Date.now()}}if(enemy.behavior!=='plant'&&enemy.behavior!=='attack'&¤tTime-enemy.lastTargetUpdate>enemy.targetUpdateInterval){updateAITarget(enemy)}switch(enemy.behavior){case'attack':handleAIAttack(enemy,index,'t');break;case'plant':if(!visibleEnemy){handleAIPlant(enemy,index)}else{enemy.behavior='attack';enemy.currentTarget=visibleEnemy;enemy.lastBehaviorChange=Date.now()}break;case'patrol':default:if(!visibleEnemy){handleAIPatrol(enemy)}break}if(Math.random()<0.008){aiAutoReload(enemy)}})}function getVisibleEnemy(ai,maxDistance=800){if(!ai.alive)return null;let closestVisibleEnemy=null;let closestDistance=maxDistance;if(ai.team==='ct'){if(player.alive&&player.team==='t'){const distance=Math.hypot(ai.x-player.x,ai.y-player.y);if(distance<closestDistance&&hasLineOfSight(ai.x,ai.y,player.x,player.y)){closestDistance=distance;closestVisibleEnemy=player}}for(const enemy of enemies){if(!enemy.alive)continue;const distance=Math.hypot(ai.x-enemy.x,ai.y-enemy.y);if(distance<closestDistance&&hasLineOfSight(ai.x,ai.y,enemy.x,enemy.y)){closestDistance=distance;closestVisibleEnemy=enemy}}}else{if(player.alive&&player.team==='ct'){const distance=Math.hypot(ai.x-player.x,ai.y-player.y);if(distance<closestDistance&&hasLineOfSight(ai.x,ai.y,player.x,player.y)){closestDistance=distance;closestVisibleEnemy=player}}for(const teammate of teammates){if(!teammate.alive)continue;const distance=Math.hypot(ai.x-teammate.x,ai.y-teammate.y);if(distance<closestDistance&&hasLineOfSight(ai.x,ai.y,teammate.x,teammate.y)){closestDistance=distance;closestVisibleEnemy=teammate}}}return closestVisibleEnemy}function updateAITarget(ai){let closestTarget=null;let closestDistance=Infinity;if(ai.team==='ct'){enemies.forEach(enemy=>{if(!enemy.alive)return;const distance=Math.hypot(ai.x-enemy.x,ai.y-enemy.y);if(distance<800&&hasLineOfSight(ai.x,ai.y,enemy.x,enemy.y)){if(distance<closestDistance){closestDistance=distance;closestTarget=enemy}}})}else{if(player.alive&&player.team==='ct'){const distance=Math.hypot(ai.x-player.x,ai.y-player.y);if(distance<800&&hasLineOfSight(ai.x,ai.y,player.x,player.y)){closestDistance=distance;closestTarget=player}}teammates.forEach(teammate=>{if(!teammate.alive)return;const distance=Math.hypot(ai.x-teammate.x,ai.y-teammate.y);if(distance<800&&hasLineOfSight(ai.x,ai.y,teammate.x,teammate.y)){if(distance<closestDistance){closestDistance=distance;closestTarget=teammate}}})}if(closestTarget){ai.behavior='attack';ai.currentTarget=closestTarget;ai.lastBehaviorChange=Date.now();ai.lastTargetUpdate=Date.now();aiSwitchWeapon(ai,closestDistance);if(ai.currentWeaponType==='melee'&&closestDistance>40){ai.shouldApproachForMelee=true}}else if(ai.behavior==='attack'){ai.behavior='patrol';ai.currentTarget=null;ai.lastBehaviorChange=Date.now()}}function handleAIAttack(ai,index,team){if(!ai.currentTarget||!ai.currentTarget.alive){ai.behavior='patrol';ai.shouldApproachForMelee=false;return}const target=ai.currentTarget;const dx=target.x-ai.x;const dy=target.y-ai.y;const distance=Math.hypot(dx,dy);ai.direction=Math.atan2(dy,dx);const weapon=weapons[ai.currentWeapon];if(weapon&&weapon.type==='melee'){const knifeRange=weapon.range+target.radius+10;if(distance>knifeRange){ai.isMoving=true;ai.shouldApproachForMelee=true;const moveX=Math.cos(ai.direction)*ai.speed;const moveY=Math.sin(ai.direction)*ai.speed;const newX=ai.x+moveX;const newY=ai.y+moveY;if(isPositionWalkable(newX,newY,ai.radius)){ai.x=newX;ai.y=newY}else{const sideAngle=ai.direction+Math.PI/2;const sideX=ai.x+Math.cos(sideAngle)*ai.speed*0.7;const sideY=ai.y+Math.sin(sideAngle)*ai.speed*0.7;if(isPositionWalkable(sideX,sideY,ai.radius)){ai.x=sideX;ai.y=sideY}}}else{ai.isMoving=false;const currentTime=Date.now();if(currentTime-ai.lastShot>=weapon.attackRate){if(aiKnifeAttack(ai,target)){aiGetMoney(team,index,1500);checkRoundEnd()}ai.lastShot=currentTime}}return}if(distance>400){ai.isMoving=true;moveAITowardsTarget(ai,target.x,target.y)}else if(distance>150){ai.isMoving=Math.random()<0.3;if(ai.isMoving){const sideAngles=[ai.direction+Math.PI/2,ai.direction-Math.PI/2,ai.direction+Math.PI/4,ai.direction-Math.PI/4];let moved=false;for(const angle of sideAngles){const testX=ai.x+Math.cos(angle)*ai.speed*0.7;const testY=ai.y+Math.sin(angle)*ai.speed*0.7;if(isPositionWalkable(testX,testY,ai.radius)){ai.x=testX;ai.y=testY;ai.direction=angle;moved=true;break}}if(!moved){const backAngle=ai.direction+Math.PI;const backX=ai.x+Math.cos(backAngle)*ai.speed*0.5;const backY=ai.y+Math.sin(backAngle)*ai.speed*0.5;if(isPositionWalkable(backX,backY,ai.radius)){ai.x=backX;ai.y=backY}}}}else{ai.isMoving=false;if(distance<40&&ai.currentWeaponType!=='melee'){aiSwitchWeapon(ai,distance)}}if(distance<600&&hasLineOfSight(ai.x,ai.y,target.x,target.y)){aiShoot(ai,target)}}function moveAITowardsTarget(ai,targetX,targetY){const moveDirection=getSafeMoveDirection(ai,targetX,targetY);const moveX=Math.cos(moveDirection)*ai.speed;const moveY=Math.sin(moveDirection)*ai.speed;const newX=ai.x+moveX;const newY=ai.y+moveY;if(isPositionWalkable(newX,newY,ai.radius)){ai.x=newX;ai.y=newY}else{const sideAngle=moveDirection+Math.PI/2;const sideX=ai.x+Math.cos(sideAngle)*ai.speed*0.7;const sideY=ai.y+Math.sin(sideAngle)*ai.speed*0.7;if(isPositionWalkable(sideX,sideY,ai.radius)){ai.x=sideX;ai.y=sideY}}}function getSafeMoveDirection(ai,targetX,targetY){const currentTime=Date.now();if(!ai.lastPathCalc||currentTime-ai.lastPathCalc>500){ai.lastPathCalc=currentTime;if(pathfinder){const path=pathfinder.findPath(ai.x,ai.y,targetX,targetY);if(path&&path.length>0){ai.currentPath=pathfinder.simplifyPath(path);ai.pathIndex=0;ai.lastPathUpdate=currentTime}else{ai.currentPath=null;ai.pathIndex=0}}}if(ai.currentPath&&ai.currentPath.length>0&&ai.pathIndex<ai.currentPath.length){const nextNode=ai.currentPath[ai.pathIndex];const dx=nextNode.x-ai.x;const dy=nextNode.y-ai.y;const distance=Math.hypot(dx,dy);if(distance<30){ai.pathIndex++;if(ai.pathIndex>=ai.currentPath.length){ai.currentPath=null;ai.pathIndex=0;return Math.atan2(targetY-ai.y,targetX-ai.x)}const nextNextNode=ai.currentPath[ai.pathIndex];return Math.atan2(nextNextNode.y-ai.y,nextNextNode.x-ai.x)}return Math.atan2(dy,dx)}return getSimpleMoveDirection(ai,targetX,targetY)}function getSimpleMoveDirection(ai,targetX,targetY){const dx=targetX-ai.x;const dy=targetY-ai.y;const baseDirection=Math.atan2(dy,dx);const distance=Math.hypot(dx,dy);if(distance>0&&hasLineOfSight(ai.x,ai.y,targetX,targetY)){return baseDirection}const currentTime=Date.now();if(!ai.lastDirectionChange||currentTime-ai.lastDirectionChange>200){ai.lastDirectionChange=currentTime;const directions=[];for(let i=0;i<8;i++){const angle=baseDirection+(i*Math.PI/4);directions.push(angle)}let bestDirection=baseDirection;let bestDistance=0;for(const direction of directions){const testDistance=100;const testX=ai.x+Math.cos(direction)*testDistance;const testY=ai.y+Math.sin(direction)*testDistance;if(isPositionWalkable(testX,testY,ai.radius)){const toTargetX=targetX-testX;const toTargetY=targetY-testY;const toTargetDist=Math.hypot(toTargetX,toTargetY);if(toTargetDist>bestDistance){bestDistance=toTargetDist;bestDirection=direction}}}ai.avoidanceDirection=bestDirection;ai.avoidanceTimer=currentTime+1000}if(ai.avoidanceDirection&&ai.avoidanceTimer>currentTime){return ai.avoidanceDirection}return baseDirection}function aiShoot(ai,target){if(!ai.alive||!target||!target.alive)return false;const currentTime=Date.now();let weapon=weapons[ai.currentWeapon];if(!weapon)return false;if(weapon.type==='melee'){if(currentTime-ai.lastShot<weapon.attackRate)return false;const distance=Math.hypot(ai.x-target.x,ai.y-target.y);if(distance<weapon.range+target.radius){aiKnifeAttack(ai,target);ai.lastShot=currentTime;return true}return false}if(weapon.ammoInMag<=0){if(weapon.ammo>0){const needed=weapon.magSize;const available=Math.min(needed,weapon.ammo);weapon.ammoInMag=available;weapon.ammo-=available;console.log(`${ai.name}自动换弹:${weapon.ammoInMag}/${weapon.ammo}`)}else{if(ai.secondaryWeapon&&weapons[ai.secondaryWeapon].ammoInMag>0){ai.currentWeaponType='secondary';ai.currentWeapon=ai.secondaryWeapon;weapon=weapons[ai.currentWeapon];console.log(`${ai.name}切换到手枪:${weapon.name}`)}else if(ai.currentWeaponType!=='melee'){ai.currentWeaponType='melee';ai.currentWeapon='knife';return false}}}weapon=weapons[ai.currentWeapon];if(!weapon||weapon.ammoInMag<=0)return false;if(currentTime-ai.lastShot<weapon.fireRate)return false;const dx=target.x-ai.x;const dy=target.y-ai.y;const distance=Math.hypot(dx,dy);if(distance>weapon.range)return false;if(!hasLineOfSight(ai.x,ai.y,target.x,target.y))return false;weapon.ammoInMag--;ai.lastShot=currentTime;const baseAngle=Math.atan2(dy,dx);let spread=0;if(weapon.type!=='melee'){const baseSpread=weapon.spread*0.5;if(ai.isMoving){spread=baseSpread*(weapon.moveSpreadMultiplier*0.3)}else{spread=baseSpread*0.2}if(distance>500)spread*=1.2;else if(distance>300)spread*=1.1}const randomSpread=(Math.random()-0.5)*spread*2;const finalAngle=baseAngle+randomSpread;bullets.push({x:ai.x,y:ai.y,radius:3,speed:weapon.bulletSpeed,angle:finalAngle,damage:weapon.damage,range:weapon.range,distance:0,color:ai.color,team:ai.team,shooter:ai});createBulletTrail(ai.x,ai.y,ai.x+Math.cos(finalAngle)*50,ai.y+Math.sin(finalAngle)*50,weapon.color);if(weapon.ammoInMag<weapon.magSize/4&&weapon.ammo>0){if(Math.random()<0.1){const canReload=true;if(canReload){const needed=weapon.magSize-weapon.ammoInMag;const available=Math.min(needed,weapon.ammo);weapon.ammoInMag+=available;weapon.ammo-=available;console.log(`${ai.name}低弹药换弹:${weapon.ammoInMag}/${weapon.ammo}`)}}}return true}function aiManageWeapons(ai){const currentWeapon=weapons[ai.currentWeapon];if(!currentWeapon)return;if(currentWeapon.type!=='melee'&¤tWeapon.ammoInMag<=0){if(currentWeapon.ammo>0){const needed=currentWeapon.magSize;const available=Math.min(needed,currentWeapon.ammo);currentWeapon.ammoInMag=available;currentWeapon.ammo-=available}else{if(ai.secondaryWeapon&&weapons[ai.secondaryWeapon].ammoInMag>0){ai.currentWeaponType='secondary';ai.currentWeapon=ai.secondaryWeapon}else if(ai.currentWeaponType!=='melee'){ai.currentWeaponType='melee';ai.currentWeapon='knife'}}}}function aiSwitchWeapon(ai,targetDistance){if(!ai.alive)return;if(Date.now()-ai.lastWeaponSwitch<300)return;const currentWeapon=weapons[ai.currentWeapon];if(!currentWeapon)return;if(currentWeapon.type!=='melee'&¤tWeapon.ammoInMag<=0){if(ai.secondaryWeapon&&weapons[ai.secondaryWeapon].ammoInMag>0){ai.currentWeaponType='secondary';ai.currentWeapon=ai.secondaryWeapon;ai.lastWeaponSwitch=Date.now();return}else if(ai.currentWeaponType!=='melee'){ai.currentWeaponType='melee';ai.currentWeapon='knife';ai.lastWeaponSwitch=Date.now();return}}let shouldSwitch=false;let newWeapon=ai.currentWeapon;let newWeaponType=ai.currentWeaponType;if(targetDistance<25){if(ai.currentWeaponType!=='melee'){newWeapon='knife';newWeaponType='melee';shouldSwitch=true}}else if(targetDistance<120){if(ai.primaryWeapon&&weapons[ai.primaryWeapon].type==='smg'&&weapons[ai.primaryWeapon].ammoInMag>0&&ai.currentWeaponType!=='primary'){newWeapon=ai.primaryWeapon;newWeaponType='primary';shouldSwitch=true}else if(ai.secondaryWeapon&&weapons[ai.secondaryWeapon].ammoInMag>0&&ai.currentWeaponType!=='secondary'){newWeapon=ai.secondaryWeapon;newWeaponType='secondary';shouldSwitch=true}}else if(targetDistance<350){if(ai.primaryWeapon&&weapons[ai.primaryWeapon].type==='rifle'&&weapons[ai.primaryWeapon].ammoInMag>0&&ai.currentWeaponType!=='primary'){newWeapon=ai.primaryWeapon;newWeaponType='primary';shouldSwitch=true}}else{if(ai.primaryWeapon==='awp'&&weapons.awp.ammoInMag>0&&ai.currentWeapon!=='awp'){newWeapon='awp';newWeaponType='primary';shouldSwitch=true}}if(shouldSwitch){const newWeaponData=weapons[newWeapon];if(newWeaponData&&newWeaponData.type!=='melee'&&newWeaponData.ammoInMag<=0){return}ai.currentWeapon=newWeapon;ai.currentWeaponType=newWeaponType;ai.lastWeaponSwitch=Date.now()}}function aiKnifeAttack(ai,target){const currentTime=Date.now();if(currentTime-ai.lastKnifeAttack<ai.knifeAttackCooldown)return false;const distance=Math.hypot(ai.x-target.x,ai.y-target.y);if(distance>30)return false;const weapon=weapons.knife;let damage=weapon.damage;const headChance=0.3;if(Math.random()<headChance){damage*=1.5}if(target.armor>0){damage*=0.75;target.armor-=damage*0.25;if(target.armor<0)target.armor=0}target.health-=damage;createHitEffect(target.x,target.y,weapon.color);ai.lastKnifeAttack=currentTime;if(target.health<=0){target.health=0;target.alive=false;createExplosionEffect(target.x,target.y);if(target.primaryWeapon){createWeaponDrop(target.x,target.y,target.primaryWeapon)}return true}return false}function aiAutoReload(ai){const weapon=weapons[ai.currentWeapon];if(!weapon||weapon.type==='melee')return false;if(weapon.ammoInMag<weapon.magSize/2&&weapon.ammo>0){let isSafe=true;if(ai.team==='ct'){for(const enemy of enemies){if(enemy.alive){const dist=Math.hypot(ai.x-enemy.x,ai.y-enemy.y);if(dist<300){isSafe=false;break}}}}else{for(const teammate of teammates){if(teammate.alive){const dist=Math.hypot(ai.x-teammate.x,ai.y-teammate.y);if(dist<300){isSafe=false;break}}}}if(isSafe&&Math.random()<0.5){weapon.ammoInMag=Math.min(weapon.magSize,weapon.ammo);weapon.ammo-=weapon.ammoInMag;return true}}return false}function handleAIDefuse(teammate,index){if(!gameState.bombPlanted||!gameState.bombSite){teammate.bombDefusing=false;teammate.bombDefuseProgress=0;teammate.behavior='patrol';return}const visibleEnemy=getVisibleEnemy(teammate,600);if(visibleEnemy){teammate.bombDefusing=false;teammate.bombDefuseProgress=0;teammate.behavior='attack';teammate.currentTarget=visibleEnemy;teammate.lastBehaviorChange=Date.now();console.log(`${teammate.name}发现可见敌人,停止拆弹转为攻击`);return}const targetX=gameState.bombSite.x;const targetY=gameState.bombSite.y;const distanceToBomb=Math.hypot(teammate.x-targetX,teammate.y-targetY);if(distanceToBomb>1000&&Math.random()<0.8){teammate.behavior='patrol';teammate.currentTarget=null;teammate.lastBehaviorChange=Date.now();return}let someoneDefusing=false;teammates.forEach(otherTeammate=>{if(otherTeammate.id!==teammate.id&&otherTeammate.alive&&otherTeammate.bombDefusing){someoneDefusing=true}});if(someoneDefusing&&Math.random()<0.9){teammate.behavior='patrol';teammate.currentTarget=null;teammate.lastBehaviorChange=Date.now();return}const nearbyEnemy=hasEnemyNearby(teammate,300);if(nearbyEnemy){if(Math.random()<0.95){teammate.bombDefusing=false;teammate.bombDefuseProgress=0;teammate.behavior='attack';teammate.currentTarget=nearbyEnemy;teammate.lastBehaviorChange=Date.now();return}}handleAIMoveToTarget(teammate,targetX,targetY,()=>{const enemyAfterMove=getVisibleEnemy(teammate,400);if(enemyAfterMove){teammate.isMoving=false;teammate.bombDefusing=false;teammate.bombDefuseProgress=0;teammate.behavior='attack';teammate.currentTarget=enemyAfterMove;teammate.lastBehaviorChange=Date.now();return}teammate.isMoving=false;if(!teammate.bombDefusing){teammate.bombDefusing=true;teammate.bombDefuseProgress=0;console.log(`${teammate.name}开始拆包`)}else{teammate.bombDefuseProgress+=16;if(Date.now()-teammate.lastPathCalc>200){const enemyDuringDefuse=getVisibleEnemy(teammate,400);if(enemyDuringDefuse){teammate.bombDefusing=false;teammate.bombDefuseProgress=0;teammate.behavior='attack';teammate.currentTarget=enemyDuringDefuse;teammate.lastBehaviorChange=Date.now();console.log(`${teammate.name}拆弹过程中发现敌人,立即停止拆弹`);return}teammate.lastPathCalc=Date.now()}const defuseTime=teammate.hasDefuseKit?gameState.bombDefuseTimeWithKit:gameState.bombDefuseTime;if(teammate.bombDefuseProgress>=defuseTime){gameState.bombPlanted=false;gameState.bombSite=null;gameState.bombTimeLeft=gameState.bombTimer;teammate.bombDefusing=false;teammate.bombDefuseProgress=0;bombIndicator.style.display='none';endRound('ct','拆除了炸弹')}if(teammate.lastHitBy&&Date.now()-teammate.lastHitTime<1000){teammate.bombDefusing=false;teammate.bombDefuseProgress=0;teammate.behavior='attack';teammate.currentTarget=teammate.lastHitBy;teammate.lastBehaviorChange=Date.now()}}})}function handleAIPlant(enemy,index){if(!enemy.hasBomb||gameState.bombPlanted){enemy.behavior='patrol';enemy.currentTarget=null;enemy.bombPlanting=false;enemy.bombPlantProgress=0;return}const visibleEnemy=getVisibleEnemy(enemy,500);if(visibleEnemy){enemy.bombPlanting=false;enemy.bombPlantProgress=0;enemy.behavior='attack';enemy.currentTarget=visibleEnemy;enemy.lastBehaviorChange=Date.now();console.log(`${enemy.name}发现可见敌人,停止下包转为攻击`);return}let targetSite=null;let minDistance=Infinity;for(const site of bombSites){const dist=Math.hypot(enemy.x-site.x,enemy.y-site.y);if(dist<minDistance){minDistance=dist;targetSite=site}}if(!targetSite){enemy.behavior='patrol';return}enemy.bombPlantSite=targetSite;const targetX=targetSite.x;const targetY=targetSite.y;const dx=targetX-enemy.x;const dy=targetY-enemy.y;const distance=Math.hypot(dx,dy);enemy.direction=Math.atan2(dy,dx);if(distance<=60){enemy.isMoving=false;const enemyNearby=getVisibleEnemy(enemy,400);if(enemyNearby){enemy.bombPlanting=false;enemy.bombPlantProgress=0;enemy.behavior='attack';enemy.currentTarget=enemyNearby;enemy.lastBehaviorChange=Date.now();return}if(!enemy.bombPlanting){enemy.bombPlanting=true;enemy.bombPlantProgress=0}}else{enemy.isMoving=true;const enemyDuringMove=getVisibleEnemy(enemy,500);if(enemyDuringMove){enemy.bombPlanting=false;enemy.bombPlantProgress=0;enemy.behavior='attack';enemy.currentTarget=enemyDuringMove;enemy.lastBehaviorChange=Date.now();return}moveAITowardsTarget(enemy,targetX,targetY)}if(enemy.bombPlanting){const enemyNearby=getVisibleEnemy(enemy,400);if(enemyNearby){enemy.bombPlanting=false;enemy.bombPlantProgress=0;enemy.behavior='attack';enemy.currentTarget=enemyNearby;enemy.lastBehaviorChange=Date.now();return}enemy.bombPlantProgress+=16;if(enemy.bombPlantProgress>=gameState.bombPlantTime){gameState.bombPlanted=true;gameState.bombSite=enemy.bombPlantSite;gameState.bombTimeLeft=gameState.bombTimer;enemy.hasBomb=false;enemy.bombPlanting=false;enemy.bombPlantProgress=0;setTimeout(()=>{enemy.behavior='attack';enemy.currentTarget=null},100);bombIndicator.style.display='block';bombIndicator.textContent=`炸弹已安装-${enemy.bombPlantSite.name}`;bombIndicator.style.backgroundColor='rgba(255, 0, 0, 0.7)';showMessage(`恐怖分子在${enemy.bombPlantSite.name}安装了炸弹`,'#FF4500');notifyBombPlanted()}if(enemy.lastHitBy&&Date.now()-enemy.lastHitTime<500){enemy.bombPlanting=false;enemy.bombPlantProgress=0;enemy.behavior='attack';enemy.currentTarget=enemy.lastHitBy;enemy.lastBehaviorChange=Date.now()}}}function handleAIMoveToTarget(ai,targetX,targetY,onArrival){const dx=targetX-ai.x;const dy=targetY-ai.y;const distance=Math.hypot(dx,dy);ai.direction=Math.atan2(dy,dx);if(distance<=ai.radius+20){ai.isMoving=false;if(onArrival)onArrival();return}ai.isMoving=true;if(pathfinder){const currentTime=Date.now();if(!ai.lastPathCalc||currentTime-ai.lastPathCalc>500){ai.lastPathCalc=currentTime;const path=pathfinder.findPath(ai.x,ai.y,targetX,targetY);if(path&&path.length>0){ai.currentPath=pathfinder.simplifyPath(path);ai.pathIndex=0;ai.lastPathUpdate=currentTime}else{ai.currentPath=null;ai.pathIndex=0}}if(ai.currentPath&&ai.currentPath.length>0&&ai.pathIndex<ai.currentPath.length){const nextNode=ai.currentPath[ai.pathIndex];const dxToNode=nextNode.x-ai.x;const dyToNode=nextNode.y-ai.y;const distanceToNode=Math.hypot(dxToNode,dyToNode);if(distanceToNode<30){ai.pathIndex++;if(ai.pathIndex>=ai.currentPath.length){ai.currentPath=null;ai.pathIndex=0;const moveX=Math.cos(ai.direction)*ai.speed;const moveY=Math.sin(ai.direction)*ai.speed;if(isPositionWalkable(ai.x+moveX,ai.y+moveY,ai.radius)){ai.x+=moveX;ai.y+=moveY}return}const nextNextNode=ai.currentPath[ai.pathIndex];ai.direction=Math.atan2(nextNextNode.y-ai.y,nextNextNode.x-ai.x)}else{ai.direction=Math.atan2(dyToNode,dxToNode)}}}const moveX=Math.cos(ai.direction)*ai.speed;const moveY=Math.sin(ai.direction)*ai.speed;const newX=ai.x+moveX;const newY=ai.y+moveY;if(isPositionWalkable(newX,newY,ai.radius)){ai.x=newX;ai.y=newY}else{const currentTime=Date.now();if(!ai.lastBypassAttempt||currentTime-ai.lastBypassAttempt>500){ai.lastBypassAttempt=currentTime;const bypassAngles=[];for(let i=0;i<8;i++){bypassAngles.push(ai.direction+(i*Math.PI/4))}for(const angle of bypassAngles){const bypassX=ai.x+Math.cos(angle)*ai.speed;const bypassY=ai.y+Math.sin(angle)*ai.speed;if(isPositionWalkable(bypassX,bypassY,ai.radius)){ai.x=bypassX;ai.y=bypassY;ai.direction=angle;break}}}}if(ai.lastDistanceToTarget===undefined){ai.lastDistanceToTarget=distance;ai.stuckTimer=Date.now()}else if(Math.abs(distance-ai.lastDistanceToTarget)<10){if(Date.now()-ai.stuckTimer>3000){ai.currentPath=null;ai.pathIndex=0;ai.lastPathCalc=0;ai.stuckTimer=Date.now();if(isPositionWalkable(targetX,targetY,ai.radius)){ai.x=targetX;ai.y=targetY}}}else{ai.lastDistanceToTarget=distance;ai.stuckTimer=Date.now()}}function handleAIPatrol(ai){const currentTime=Date.now();if(!ai.patrolPoint||currentTime-ai.lastMoveChange>ai.moveDirectionChangeInterval||(ai.patrolPoint&&Math.hypot(ai.x-ai.patrolPoint.x,ai.y-ai.patrolPoint.y)<50)){let attempts=0;let validPoint=false;while(!validPoint&&attempts<20){ai.patrolPoint={x:Math.random()*(world.width-100)+50,y:Math.random()*(world.height-100)+50};if(isPositionWalkable(ai.patrolPoint.x,ai.patrolPoint.y,ai.radius)){if(hasLineOfSight(ai.x,ai.y,ai.patrolPoint.x,ai.patrolPoint.y)||(pathfinder&&pathfinder.findPath(ai.x,ai.y,ai.patrolPoint.x,ai.patrolPoint.y))){validPoint=true}}attempts++}ai.lastMoveChange=currentTime;ai.currentPath=null;ai.pathIndex=0}if(ai.patrolPoint){const dx=ai.patrolPoint.x-ai.x;const dy=ai.patrolPoint.y-ai.y;const distance=Math.hypot(dx,dy);ai.isMoving=distance>30;if(distance>30){const moveDirection=getSafeMoveDirection(ai,ai.patrolPoint.x,ai.patrolPoint.y);ai.direction=moveDirection;const moveX=Math.cos(moveDirection)*ai.speed*0.5;const moveY=Math.sin(moveDirection)*ai.speed*0.5;const newX=ai.x+moveX;const newY=ai.y+moveY;if(isPositionWalkable(newX,newY,ai.radius)){ai.x=newX;ai.y=newY}else{ai.currentPath=null;ai.pathIndex=0;ai.lastMoveChange=0}}}}function updateAIMovementState(ai){const currentTime=Date.now();if(currentTime-ai.lastMoveCheck<100)return;ai.lastMoveCheck=currentTime;const currentX=ai.x;const currentY=ai.y;if(!ai.hasOwnProperty('lastMoveX')){ai.lastMoveX=currentX;ai.lastMoveY=currentY;ai.lastMoveTime=currentTime;ai.moveThreshold=5;return}const dx=currentX-ai.lastMoveX;const dy=currentY-ai.lastMoveY;const distanceMoved=Math.hypot(dx,dy);if(distanceMoved>ai.moveThreshold){ai.isMoving=true;ai.lastMoveTime=currentTime;ai.lastMoveX=currentX;ai.lastMoveY=currentY}else{if(currentTime-ai.lastMoveTime>500){ai.isMoving=false}}}function aiGetMoney(team,index,amount){if(team==='ct'){aiEconomy.ct[index]+=amount}else{aiEconomy.t[index]+=amount}}function aiPurchaseWeapon(ai,team){const index=parseInt(ai.id.split('_')[1]);let money=team==='ct'?aiEconomy.ct[index]:aiEconomy.t[index];if(ai.primaryWeapon){weapons[ai.primaryWeapon].ammoInMag=weapons[ai.primaryWeapon].magSize;weapons[ai.primaryWeapon].ammo=weapons[ai.primaryWeapon].magSize*4}if(ai.secondaryWeapon){weapons[ai.secondaryWeapon].ammoInMag=weapons[ai.secondaryWeapon].magSize;weapons[ai.secondaryWeapon].ammo=weapons[ai.secondaryWeapon].magSize*4}if(!ai.wasAlive||gameState.round===1){ai.secondaryWeapon=team==='ct'?'usp':'glock';if(money>=4750){ai.primaryWeapon='awp';money-=weapons.awp.price;ai.armor=100;money-=gearItems.armor_heavy.price}else if(money>=3700){ai.primaryWeapon=team==='ct'?'m4a4':'ak47';money-=weapons[ai.primaryWeapon].price;ai.armor=100;money-=gearItems.armor_heavy.price}else if(money>=2250+650){ai.primaryWeapon=team==='ct'?'famas':'galil';money-=weapons[ai.primaryWeapon].price;ai.armor=50;money-=gearItems.armor_light.price}else if(money>=1250+650){ai.primaryWeapon=team==='ct'?'mp9':'mac10';money-=weapons[ai.primaryWeapon].price;ai.armor=50;money-=gearItems.armor_light.price}else if(money>=1000){ai.primaryWeapon=null;ai.armor=100;money-=gearItems.armor_heavy.price}else if(money>=650){ai.primaryWeapon=null;ai.armor=50;money-=gearItems.armor_light.price}else{ai.primaryWeapon=null;ai.armor=0}}else{if(!ai.primaryWeapon&&money>=1000){if(money>=3700){ai.primaryWeapon=team==='ct'?'m4a4':'ak47';money-=weapons[ai.primaryWeapon].price}else if(money>=2250+650){ai.primaryWeapon=team==='ct'?'famas':'galil';money-=weapons[ai.primaryWeapon].price}}if(money>=1000&&ai.armor<100){ai.armor=100;money-=gearItems.armor_heavy.price}else if(money>=650&&ai.armor<50){ai.armor=50;money-=gearItems.armor_light.price}}if(!ai.secondaryWeapon){ai.secondaryWeapon=team==='ct'?'usp':'glock'}ai.currentWeaponType=ai.primaryWeapon?'primary':'secondary';ai.currentWeapon=ai.currentWeaponType==='primary'?ai.primaryWeapon:ai.secondaryWeapon;ai.wasAlive=true;ai.previousPrimary=ai.primaryWeapon;ai.previousSecondary=ai.secondaryWeapon;ai.previousArmor=ai.armor;if(team==='ct'){aiEconomy.ct[index]=Math.max(0,money)}else{aiEconomy.t[index]=Math.max(0,money)}if(ai.primaryWeapon){weapons[ai.primaryWeapon].ammoInMag=weapons[ai.primaryWeapon].magSize;weapons[ai.primaryWeapon].ammo=weapons[ai.primaryWeapon].magSize*2}if(ai.secondaryWeapon){weapons[ai.secondaryWeapon].ammoInMag=weapons[ai.secondaryWeapon].magSize;weapons[ai.secondaryWeapon].ammo=weapons[ai.secondaryWeapon].magSize*2}return true}function forceCTToDefuse(){console.log("强制部分CT去拆包");if(!gameState.bombPlanted||!gameState.bombSite){return}const aliveCTCount=teammates.filter(t=>t.alive).length;const defendersNeeded=Math.max(1,Math.floor(aliveCTCount/2));let defendersAssigned=0;teammates.forEach(teammate=>{if(teammate.alive){if(defendersAssigned<defendersNeeded){teammate.behavior='attack';teammate.currentTarget=null;teammate.lastBehaviorChange=Date.now();teammate.bombDefusing=false;teammate.bombDefuseProgress=0;console.log(`${teammate.name}被分配守卫任务`);defendersAssigned++}else{teammate.behavior='defuse';teammate.currentTarget=null;teammate.lastBehaviorChange=Date.now();teammate.bombDefusing=false;teammate.bombDefuseProgress=0;console.log(`${teammate.name}被强制前往拆包`)}}});if(player.alive&&player.team==='ct'){showMessage("恐怖分子已全灭,需要拆除炸弹!","#FFA500");console.log("玩家被提示去拆包")}}function hasEnemyNearby(ai,checkDistance=200){if(!ai.alive)return false;const visibleEnemy=getVisibleEnemy(ai,checkDistance);if(visibleEnemy){return visibleEnemy}if(ai.team==='ct'){for(const enemy of enemies){if(enemy.alive){const distance=Math.hypot(ai.x-enemy.x,ai.y-enemy.y);if(distance<checkDistance*0.7){return enemy}}}}else{if(player.alive&&player.team==='ct'){const distance=Math.hypot(ai.x-player.x,ai.y-player.y);if(distance<checkDistance*0.7){return player}}for(const teammate of teammates){if(teammate.alive){const distance=Math.hypot(ai.x-teammate.x,ai.y-teammate.y);if(distance<checkDistance*0.7){return teammate}}}}return null}function endRound(winner,reason){gameState.phase='over';gameState.roundWinner=winner;console.log(`回合结束:${winner}胜利-${reason}`);gameState.playerWasAlive=player.alive;gameState.playerPreviousPrimary=player.primaryWeapon;gameState.playerPreviousSecondary=player.secondaryWeapon;gameState.playerPreviousArmor=player.armor;if(winner==='ct'){gameState.ctScore++;showMessage(`CT赢得回合-${reason}`,'#4A90E2');if(player.team==='ct'){const baseReward=player.alive?3500:3250;gameState.playerMoney+=baseReward;player.money=gameState.playerMoney}else{const baseReward=player.alive?1900:1400;gameState.playerMoney+=baseReward;player.money=gameState.playerMoney}}else{gameState.tScore++;showMessage(`T赢得回合-${reason}`,'#FF6B6B');if(player.team==='t'){const baseReward=player.alive?3500:3250;gameState.playerMoney+=baseReward;player.money=gameState.playerMoney}else{const baseReward=player.alive?1900:1400;gameState.playerMoney+=baseReward;player.money=gameState.playerMoney}}gameState.roundsSinceLastSwap++;if(gameState.roundsSinceLastSwap>=12){gameState.needToSwapTeams=true;gameState.roundsSinceLastSwap=0;console.log(`已达到12回合,下一回合将交换阵营`)}updateStats();if(gameState.ctScore>=gameState.targetScore||gameState.tScore>=gameState.targetScore){endGame(winner);return}console.log("3秒后开始下一回合...");setTimeout(()=>{startNextRound()},3000)}function startNextRound(){console.log(`===开始第${gameState.round+1}回合===`);console.log(`当前比分:CT ${gameState.ctScore}-${gameState.tScore}T`);if(gameState.needToSwapTeams&&!gameState.isSwapping){swapTeams();return}gameState.round++;gameState.phase='buy';gameState.buyTimeLeft=gameState.buyTime;gameState.roundTimeLeft=gameState.roundTime;gameState.bombPlanted=false;gameState.bombSite=null;player.alive=true;player.health=100;gameState.playerAlive=true;gameState.playerHealth=100;gameState.playerReloading=false;gameState.playerReloadProgress=0;gameState.playerKnifing=false;gameState.playerKnifeProgress=0;teammates.forEach(teammate=>{teammate.alive=true;teammate.health=100;teammate.bombPlanting=false;teammate.bombPlantProgress=0;teammate.bombDefusing=false;teammate.bombDefuseProgress=0});enemies.forEach(enemy=>{enemy.alive=true;enemy.health=100;enemy.bombPlanting=false;enemy.bombPlantProgress=0});if(gameState.playerWasAlive){player.primaryWeapon=gameState.playerPreviousPrimary;player.secondaryWeapon=gameState.playerPreviousSecondary;player.armor=gameState.playerPreviousArmor;gameState.playerArmor=player.armor;if(player.primaryWeapon){const weapon=weapons[player.primaryWeapon];if(weapon){weapon.ammoInMag=weapon.magSize;weapon.ammo=Math.max(weapon.ammo,weapon.magSize*4)}}if(player.secondaryWeapon){const weapon=weapons[player.secondaryWeapon];if(weapon){weapon.ammoInMag=weapon.magSize;weapon.ammo=Math.max(weapon.ammo,weapon.magSize*4)}}}else{player.primaryWeapon=null;player.secondaryWeapon=player.team==='ct'?'usp':'glock';player.armor=0;gameState.playerArmor=0;gameState.currentWeaponType='secondary';gameState.currentWeapon=player.secondaryWeapon;const initialWeapon=weapons[player.secondaryWeapon];if(initialWeapon){initialWeapon.ammoInMag=initialWeapon.magSize;initialWeapon.ammo=initialWeapon.magSize*4}}player.knife='knife';gameState.playerWasAlive=true;gameState.playerPreviousPrimary=player.primaryWeapon;gameState.playerPreviousSecondary=player.secondaryWeapon;gameState.playerPreviousArmor=player.armor;resetGameWorld();teammates.forEach((teammate,index)=>{if(index<aiEconomy.ct.length){teammate.money=aiEconomy.ct[index]}aiPurchaseWeapon(teammate,'ct')});enemies.forEach((enemy,index)=>{if(index<aiEconomy.t.length){enemy.money=aiEconomy.t[index]}aiPurchaseWeapon(enemy,'t')});updateStats();initWeaponSelector();reloadIndicator.style.display='none';bombIndicator.style.display='none';closeShop();resetHitIndicator();console.log(`玩家当前金钱:$${gameState.playerMoney}`);console.log("=== 新回合准备就绪 ===")}function swapTeams(){gameState.isSwapping=true;swapNotification.style.display='block';swapNotification.textContent='交换阵营!';console.log("=== 交换阵营 ===");console.log(`交换前:CT ${gameState.ctScore}-${gameState.tScore}T`);console.log(`玩家当前阵营:${player.team}`);setTimeout(()=>{swapNotification.style.display='none';if(player.team==='ct'){player.team='t';player.color='#FF6B6B';player.secondaryWeapon='glock';gameState.currentWeapon='glock';gameState.playerMoney=800;player.money=800;player.hasBomb=true;console.log("玩家从CT换到T,现在携带炸弹")}else{player.team='ct';player.color='#4A90E2';player.secondaryWeapon='usp';gameState.currentWeapon='usp';gameState.playerMoney=800;player.money=800;player.hasDefuseKit=true;gameState.playerHasDefuseKit=true;console.log("玩家从T换到CT,现在有拆弹器")}const tempScore=gameState.ctScore;gameState.ctScore=gameState.tScore;gameState.tScore=tempScore;console.log(`交换后:CT ${gameState.ctScore}-${gameState.tScore}T`);if(player.team==='ct'){console.log("玩家现在是CT,重新设置AI...");const oldTEconomy=aiEconomy.t.slice(0,enemies.length);const oldCtEconomy=aiEconomy.ct.slice(0,teammates.length);teammates=[];enemies=[];const ctNames=['Alpha','Bravo','Charlie','Delta'];for(let i=0;i<4;i++){teammates.push(createAICharacter('ct',i,ctNames[i]))}const tNames=['Tango','Victor','Whiskey','Xray','Yankee'];for(let i=0;i<5;i++){enemies.push(createAICharacter('t',i,tNames[i]))}aiEconomy.ct=Array(4).fill(800);aiEconomy.t=Array(5).fill(800)}else{console.log("玩家现在是T,重新设置AI...");const oldCtEconomy=aiEconomy.ct.slice(0,teammates.length);const oldTEconomy=aiEconomy.t.slice(0,enemies.length);teammates=[];enemies=[];const tNames=['Tango','Victor','Whiskey','Xray'];for(let i=0;i<4;i++){teammates.push(createAICharacter('t',i,tNames[i]))}const ctNames=['Alpha','Bravo','Charlie','Delta','Echo'];for(let i=0;i<5;i++){enemies.push(createAICharacter('ct',i,ctNames[i]))}aiEconomy.t=Array(4).fill(800);aiEconomy.ct=Array(5).fill(800)}let bombCarrierFound=false;if(player.team==='t'){if(player.hasBomb){bombCarrierFound=true;console.log("玩家携带炸弹")}else{for(let i=0;i<enemies.length;i++){if(enemies[i].team==='t'){enemies[i].hasBomb=true;bombCarrierFound=true;console.log(`${enemies[i].name}(T AI)携带炸弹`);break}}}}else{for(let i=0;i<enemies.length;i++){if(enemies[i].team==='t'){enemies[i].hasBomb=true;bombCarrierFound=true;console.log(`${enemies[i].name}(T AI)携带炸弹`);break}}}if(!bombCarrierFound&&enemies.length>0){const bombCarrierIndex=Math.floor(Math.random()*enemies.length);enemies[bombCarrierIndex].hasBomb=true;console.log(`${enemies[bombCarrierIndex].name}被随机分配携带炸弹`)}let defuseKitFound=false;if(player.team==='ct'){if(player.hasDefuseKit){defuseKitFound=true;console.log("玩家有拆弹器")}}if(!defuseKitFound){for(let i=0;i<teammates.length;i++){if(teammates[i].team==='ct'){teammates[i].hasDefuseKit=true;defuseKitFound=true;console.log(`${teammates[i].name}(CT AI)有拆弹器`);break}}}gameState.needToSwapTeams=false;gameState.isSwapping=false;gameState.phase='buy';gameState.buyTimeLeft=gameState.buyTime;gameState.roundTimeLeft=gameState.roundTime;gameState.bombPlanted=false;gameState.bombSite=null;player.alive=true;player.health=100;player.bombPlanting=false;player.bombPlantProgress=0;player.bombDefusing=false;player.bombDefuseProgress=0;gameState.playerAlive=true;gameState.playerHealth=100;gameState.playerReloading=false;gameState.playerReloadProgress=0;teammates.forEach(teammate=>{teammate.alive=true;teammate.health=100;teammate.bombPlanting=false;teammate.bombPlantProgress=0;teammate.bombDefusing=false;teammate.bombDefuseProgress=0});enemies.forEach(enemy=>{enemy.alive=true;enemy.health=100;enemy.bombPlanting=false;enemy.bombPlantProgress=0});resetGameWorld();teammates.forEach((teammate,index)=>{const team=teammate.team;const economyArray=team==='ct'?aiEconomy.ct:aiEconomy.t;teammate.money=economyArray[index]||800;aiPurchaseWeapon(teammate,team)});enemies.forEach((enemy,index)=>{const team=enemy.team;const economyArray=team==='ct'?aiEconomy.ct:aiEconomy.t;enemy.money=economyArray[index]||800;aiPurchaseWeapon(enemy,team)});updateStats();initWeaponSelector();reloadIndicator.style.display='none';bombIndicator.style.display='none';closeShop();resetHitIndicator();console.log("阵营交换完成");console.log(`玩家现在是${player.team},金钱:$${gameState.playerMoney}`);console.log(`当前比分:CT ${gameState.ctScore}-${gameState.tScore}T`);console.log("=== 新回合准备就绪 ===")},3000)}function createAICharacter(team,index,name){const spawns=team==='ct'?spawnPoints.ct:spawnPoints.t;const spawnIndex=index<spawns.length?index:index%spawns.length;const baseAI={id:`${team}_${index}`,name:name,team:team,x:spawns[spawnIndex].x+(Math.random()*50-25),y:spawns[spawnIndex].y+(Math.random()*50-25),radius:12,speed:3.5,color:team==='ct'?'#4A90E2':'#FF6B6B',direction:Math.random()*Math.PI*2,health:100,armor:0,alive:true,wasAlive:true,previousPrimary:null,previousSecondary:team==='ct'?'usp':'glock',previousArmor:0,currentPath:null,pathIndex:0,lastPathCalc:0,shouldApproachForMelee:false,lastPathUpdate:0,avoidanceDirection:null,avoidanceTimer:0,lastDirectionChange:0,lastBypassAttempt:0,lastDistanceToTarget:undefined,stuckTimer:0,lastMoveCheck:0,lastMoveX:0,lastMoveY:0,lastMoveTime:0,moveThreshold:3,primaryWeapon:null,secondaryWeapon:team==='ct'?'usp':'glock',knife:'knife',currentWeaponType:'secondary',currentWeapon:team==='ct'?'usp':'glock',lastShot:0,lastMoveChange:0,moveDirectionChangeInterval:1500+Math.random()*2000,lastTargetUpdate:0,targetUpdateInterval:1000,currentTarget:null,behavior:'patrol',patrolPoint:null,lastBehaviorChange:0,behaviorChangeInterval:5000+Math.random()*10000,lastHitBy:null,lastHitTime:0,lastWeaponSwitch:0,isMoving:false,lastKnifeAttack:0,knifeAttackCooldown:500};if(team==='ct'){baseAI.hasDefuseKit=index===0;baseAI.hasBomb=false;baseAI.bombDefusing=false;baseAI.bombDefuseProgress=0}else{baseAI.hasBomb=false;baseAI.hasDefuseKit=false;baseAI.bombPlanting=false;baseAI.bombPlantProgress=0;baseAI.bombPlantSite=null;baseAI.reachedPlantSite=false}return baseAI}function resetGameWorld(){const spawns=player.team==='ct'?spawnPoints.ct:spawnPoints.t;const spawnIndex=Math.floor(Math.random()*spawns.length);player.x=spawns[spawnIndex].x;player.y=spawns[spawnIndex].y;world.offsetX=canvas.width/2-player.x;world.offsetY=canvas.height/2-player.y;gameState.bombPlanted=false;gameState.bombSite=null;gameState.bombTimeLeft=gameState.bombTimer;bombIndicator.style.display='none';teammates.forEach((teammate,index)=>{const spawns=teammate.team==='ct'?spawnPoints.ct:spawnPoints.t;const spawnIndex=index<spawns.length?index:index%spawns.length;teammate.x=spawns[spawnIndex].x+(Math.random()*50-25);teammate.y=spawns[spawnIndex].y+(Math.random()*50-25);teammate.direction=Math.random()*Math.PI*2;teammate.behavior='patrol';teammate.currentTarget=null;teammate.lastBehaviorChange=0;teammate.patrolPoint=null;teammate.lastHitBy=null;teammate.lastHitTime=0;teammate.isMoving=false;teammate.shouldApproachForMelee=false});enemies.forEach((enemy,index)=>{const spawns=enemy.team==='ct'?spawnPoints.ct:spawnPoints.t;const spawnIndex=index<spawns.length?index:index%spawns.length;enemy.x=spawns[spawnIndex].x+(Math.random()*50-25);enemy.y=spawns[spawnIndex].y+(Math.random()*50-25);enemy.direction=Math.random()*Math.PI*2;enemy.behavior='patrol';enemy.currentTarget=null;enemy.lastBehaviorChange=0;enemy.patrolPoint=null;enemy.bombPlanting=false;enemy.bombPlantProgress=0;enemy.lastHitBy=null;enemy.lastHitTime=0;enemy.isMoving=false;enemy.shouldApproachForMelee=false;enemy.hasBomb=false});if(enemies.length>0&&player.team=='ct'){const bombCarrierIndex=Math.floor(Math.random()*enemies.length);enemies[bombCarrierIndex].hasBomb=true;console.log(`${enemies[bombCarrierIndex].name}携带炸弹`)}if(player.team=='t')player.hasBomb=true;bullets=[];hitEffects=[];explosionEffects=[];bulletTrails=[];weaponDrops=weaponDrops.filter(drop=>drop.life>0)}function endGame(winner){gameState.gameOver=true;if(winner==='ct'){showMessage('游戏结束! CT获胜!','#4A90E2')}else{showMessage('游戏结束! T获胜!','#FF6B6B')}setTimeout(()=>{restartGame()},5000)}function restartGame(){gameState.ctScore=0;gameState.tScore=0;gameState.round=1;gameState.roundsSinceLastSwap=0;gameState.needToSwapTeams=false;gameState.isSwapping=false;gameState.gameOver=false;gameState.phase='buy';gameState.buyTimeLeft=gameState.buyTime;player.team='ct';player.color='#4A90E2';player.money=800;player.kills=0;player.deaths=0;player.primaryWeapon=null;player.secondaryWeapon='usp';player.armor=0;player.hasBomb=false;player.hasDefuseKit=false;gameState.playerMoney=800;gameState.playerKills=0;gameState.playerDeaths=0;gameState.playerArmor=0;gameState.playerHasBomb=false;gameState.playerHasDefuseKit=false;gameState.currentWeaponType='secondary';gameState.currentWeapon='usp';if(player.team==='ct'){aiEconomy.ct=Array(4).fill(800);aiEconomy.t=Array(5).fill(800)}else{aiEconomy.ct=Array(5).fill(800);aiEconomy.t=Array(4).fill(800)}initGameWorld();updateStats();initWeaponSelector();resetHitIndicator();showMessage("新游戏开始!","#4CAF50");console.log("游戏已重置,新的比赛开始")}function isPositionWalkable(x,y,radius){if(x<radius||x>world.width-radius||y<radius||y>world.height-radius){return false}for(const wall of walls){const closestX=Math.max(wall.x,Math.min(x,wall.x+wall.width));const closestY=Math.max(wall.y,Math.min(y,wall.y+wall.height));const distance=Math.hypot(x-closestX,y-closestY);if(distance<radius){return false}}return true}function hasLineOfSight(startX,startY,endX,endY,ignoreWalls=[]){const steps=40;const dx=(endX-startX)/steps;const dy=(endY-startY)/steps;for(let i=1;i<=steps;i++){const checkX=startX+dx*i;const checkY=startY+dy*i;for(const wall of walls){if(ignoreWalls.includes(wall))continue;if(checkX>=wall.x&&checkX<=wall.x+wall.width&&checkY>=wall.y&&checkY<=wall.y+wall.height){return false}}}return true}function rayCast(startX,startY,angle,maxDistance=2000){let x=startX;let y=startY;const step=1;const dx=Math.cos(angle)*step;const dy=Math.sin(angle)*step;let distance=0;while(distance<maxDistance){x+=dx;y+=dy;distance+=step;for(const wall of walls){if(x>=wall.x&&x<=wall.x+wall.width&&y>=wall.y&&y<=wall.y+wall.height){return{x:x-dx,y:y-dy,distance:distance,hitWall:true}}}if(x<0||x>world.width||y<0||y>world.height){return{x:x,y:y,distance:distance,hitWall:true}}}return{x:x,y:y,distance:distance,hitWall:false}}function updateAimLine(){if(!player.alive||gameState.phase!=='play'||gameState.shopOpen){aimLine.visible=false;return}const screenCenterX=canvas.width/2;const screenCenterY=canvas.height/2;const worldStartX=player.x;const worldStartY=player.y;const result=rayCast(worldStartX,worldStartY,player.direction,1500);aimLine.startX=screenCenterX;aimLine.startY=screenCenterY;aimLine.endX=result.x+world.offsetX;aimLine.endY=result.y+world.offsetY;aimLine.visible=isPointInFOV(result.x,result.y)}function isPointInFOV(x,y){let targetX,targetY,targetDirection;let hasFlashlight=gameState.playerFlashlight;let isAlive=true;if(gameState.spectatorMode&&gameState.spectatorTarget){targetX=gameState.spectatorTarget.x;targetY=gameState.spectatorTarget.y;targetDirection=gameState.spectatorTarget.direction;isAlive=gameState.spectatorTarget.alive;hasFlashlight=true}else{targetX=player.x;targetY=player.y;targetDirection=player.direction;isAlive=player.alive;hasFlashlight=gameState.playerFlashlight}if(!hasFlashlight||!isAlive)return false;const centerX=canvas.width/2;const centerY=canvas.height/2;const screenX=x+world.offsetX;const screenY=y+world.offsetY;const dx=screenX-centerX;const dy=screenY-centerY;const distance=Math.hypot(dx,dy);const angleToPoint=Math.atan2(dy,dx);let angleDiff=angleToPoint-targetDirection;angleDiff=(angleDiff+Math.PI)%(2*Math.PI)-Math.PI;const fovAngle=90*Math.PI/180;if(Math.abs(angleDiff)>fovAngle/2){return false}return hasLineOfSight(targetX,targetY,x,y)}function lineRectIntersection(x1,y1,x2,y2,rx,ry,rw,rh){const left=lineLineIntersection(x1,y1,x2,y2,rx,ry,rx,ry+rh);const right=lineLineIntersection(x1,y1,x2,y2,rx+rw,ry,rx+rw,ry+rh);const top=lineLineIntersection(x1,y1,x2,y2,rx,ry,rx+rw,ry);const bottom=lineLineIntersection(x1,y1,x2,y2,rx,ry+rh,rx+rw,ry+rh);return left||right||top||bottom}function lineLineIntersection(x1,y1,x2,y2,x3,y3,x4,y4){const denom=(y4-y3)*(x2-x1)-(x4-x3)*(y2-y1);if(denom===0)return false;const ua=((x4-x3)*(y1-y3)-(y4-y3)*(x1-x3))/denom;const ub=((x2-x1)*(y1-y3)-(y2-y1)*(x1-x3))/denom;return ua>=0&&ua<=1&&ub>=0&&ub<=1}function getLineRectIntersection(x1,y1,x2,y2,rx,ry,rw,rh){let closestIntersection=null;let closestDistance=Infinity;const edges=[{x1:rx,y1:ry,x2:rx+rw,y2:ry},{x1:rx+rw,y1:ry,x2:rx+rw,y2:ry+rh},{x1:rx,y1:ry+rh,x2:rx+rw,y2:ry+rh},{x1:rx,y1:ry,x2:rx,y2:ry+rh}];for(const edge of edges){const intersect=getLineIntersection(x1,y1,x2,y2,edge.x1,edge.y1,edge.x2,edge.y2);if(intersect){const dist=Math.hypot(intersect.x-x1,intersect.y-y1);if(dist<closestDistance){closestDistance=dist;closestIntersection=intersect}}}return closestIntersection}function getLineIntersection(x1,y1,x2,y2,x3,y3,x4,y4){const denom=(y4-y3)*(x2-x1)-(x4-x3)*(y2-y1);if(denom===0)return null;const ua=((x4-x3)*(y1-y3)-(y4-y3)*(x1-x3))/denom;const ub=((x2-x1)*(y1-y3)-(y2-y1)*(x1-x3))/denom;if(ua>=0&&ua<=1&&ub>=0&&ub<=1){return{x:x1+ua*(x2-x1),y:y1+ua*(y2-y1)}}return null}function createHitEffect(x,y,color){hitEffects.push({x:x,y:y,radius:6,alpha:1,color:color})}function updateHitEffects(){for(let i=hitEffects.length-1;i>=0;i--){const effect=hitEffects[i];effect.radius+=1;effect.alpha-=0.05;if(effect.alpha<=0){hitEffects.splice(i,1)}}}function createExplosionEffect(x,y){for(let i=0;i<12;i++){explosionEffects.push({x:x,y:y,radius:Math.random()*3+1,speedX:(Math.random()-0.5)*3,speedY:(Math.random()-0.5)*3,alpha:1,color:'#FF6B6B'})}}function updateExplosionEffects(){for(let i=explosionEffects.length-1;i>=0;i--){const effect=explosionEffects[i];effect.x+=effect.speedX;effect.y+=effect.speedY;effect.alpha-=0.03;if(effect.alpha<=0){explosionEffects.splice(i,1)}}}function createWeaponDrop(x,y,weaponId){if(!weaponId)return;weaponDrops.push({x:x,y:y,weaponId:weaponId,radius:15,color:weapons[weaponId]?.color||'#FFFFFF',life:300})}function updateWeaponDrops(){for(let i=weaponDrops.length-1;i>=0;i--){const drop=weaponDrops[i];drop.life--;if(drop.life<=0){weaponDrops.splice(i,1)}}}function updateBulletTrails(){for(let i=bulletTrails.length-1;i>=0;i--){const trail=bulletTrails[i];trail.life--;trail.alpha-=0.05;if(trail.life<=0||trail.alpha<=0){bulletTrails.splice(i,1)}}}function notifyBombPlanted(){teammates.forEach(teammate=>{if(teammate.alive){if(Math.random()<0.5){teammate.behavior='defuse';teammate.currentTarget=null;teammate.lastBehaviorChange=Date.now();console.log(`${teammate.name}听到炸弹安放,前往拆包`)}else{if(teammate.behavior==='defuse'){teammate.behavior='patrol';teammate.currentTarget=null;teammate.lastBehaviorChange=Date.now()}}}})}function manageCTDefuseStrategy(){if(!gameState.bombPlanted)return;const bombTimeLeft=gameState.bombTimeLeft;const aliveCTCount=teammates.filter(t=>t.alive).length;if(bombTimeLeft<10){teammates.forEach(teammate=>{if(teammate.alive&&teammate.behavior!=='defuse'){teammate.behavior='defuse';teammate.currentTarget=null;teammate.lastBehaviorChange=Date.now();console.log(`${teammate.name}炸弹即将爆炸,冒险拆弹`)}})}else if(bombTimeLeft<30){teammates.forEach(teammate=>{if(teammate.alive&&Math.random()<0.7&&teammate.behavior!=='defuse'){teammate.behavior='defuse';teammate.currentTarget=null;teammate.lastBehaviorChange=Date.now()}})}let defusingCount=0;teammates.forEach(teammate=>{if(teammate.alive&&teammate.bombDefusing){defusingCount++}});if(defusingCount>2){let excessCount=defusingCount-2;teammates.forEach(teammate=>{if(teammate.alive&&teammate.bombDefusing&&excessCount>0){teammate.bombDefusing=false;teammate.bombDefuseProgress=0;teammate.behavior='attack';teammate.currentTarget=null;teammate.lastBehaviorChange=Date.now();excessCount--;console.log(`${teammate.name}拆弹CT过多,转为守卫`)}})}}function showHitIndicator(damage,sourceX,sourceY){if(!player.alive)return;const currentTime=Date.now();if(currentTime-hitIndicatorState.lastHitTime<hitIndicatorState.hitCooldown){return}hitIndicatorState.visible=true;hitIndicatorState.showUntil=currentTime+1000;hitIndicatorState.damage=damage;hitIndicatorState.sourceX=sourceX;hitIndicatorState.sourceY=sourceY;hitIndicatorState.lastHitTime=currentTime;hitDamageText.textContent=`-${damage}`;const dx=sourceX-player.x;const dy=sourceY-player.y;const distance=Math.hypot(dx,dy);if(isSourceOnScreen(sourceX,sourceY)){showOnScreenHitIndicator(dx,dy,distance)}else{showEdgeHitIndicator(dx,dy)}flashScreen(damage);console.log(`玩家受到${damage}点伤害,来源:(${sourceX},${sourceY})`)}function isSourceOnScreen(sourceX,sourceY){const screenX=sourceX+world.offsetX;const screenY=sourceY+world.offsetY;return screenX>=0&&screenX<=canvas.width&&screenY>=0&&screenY<=canvas.height}function showOnScreenHitIndicator(dx,dy,distance){const angle=Math.atan2(dy,dx);const indicatorDistance=Math.min(150,distance*0.3);const indicatorX=canvas.width/2+Math.cos(angle)*indicatorDistance;const indicatorY=canvas.height/2+Math.sin(angle)*indicatorDistance;hitIndicator.style.left=`${indicatorX}px`;hitIndicator.style.top=`${indicatorY}px`;hitIndicator.style.transform=`translate(-50%,-50%)rotate(${angle+Math.PI/2}rad)`;hitIndicator.classList.add('visible')}function showEdgeHitIndicator(dx,dy){const angle=Math.atan2(dy,dx);const centerX=canvas.width/2;const centerY=canvas.height/2;const intersection=getScreenEdgeIntersection(centerX,centerY,angle);if(intersection){const indicatorId=`edge-indicator-${Date.now()}`;const indicator=document.createElement('div');indicator.id=indicatorId;indicator.className='edge-hit-indicator visible';const arrowSize=20;indicator.style.left=`${intersection.x}px`;indicator.style.top=`${intersection.y}px`;indicator.style.transform=`translate(-50%,-50%)rotate(${angle+Math.PI/2}rad)`;const arrow=document.createElement('div');arrow.className='edge-arrow';let borderWidth=`${arrowSize}px`;let borderColor='rgba(255, 50, 50, 0.9)';arrow.style.borderWidth=`0 ${arrowSize/2}px ${arrowSize}px ${arrowSize/2}px`;arrow.style.borderColor=`transparent transparent ${borderColor}transparent`;indicator.appendChild(arrow);edgeHitIndicators.appendChild(indicator);hitIndicatorState.edgeIndicators.push({id:indicatorId,element:indicator,created:Date.now()});cleanupOldEdgeIndicators()}}function getScreenEdgeIntersection(centerX,centerY,angle){const left=0;const right=canvas.width;const top=0;const bottom=canvas.height;const dirX=Math.cos(angle);const dirY=Math.sin(angle);let intersections=[];if(dirX!==0){const tLeft=(left-centerX)/dirX;if(tLeft>=0){const y=centerY+dirY*tLeft;if(y>=top&&y<=bottom){intersections.push({x:left,y:y,distance:tLeft})}}}if(dirX!==0){const tRight=(right-centerX)/dirX;if(tRight>=0){const y=centerY+dirY*tRight;if(y>=top&&y<=bottom){intersections.push({x:right,y:y,distance:tRight})}}}if(dirY!==0){const tTop=(top-centerY)/dirY;if(tTop>=0){const x=centerX+dirX*tTop;if(x>=left&&x<=right){intersections.push({x:x,y:top,distance:tTop})}}}if(dirY!==0){const tBottom=(bottom-centerY)/dirY;if(tBottom>=0){const x=centerX+dirX*tBottom;if(x>=left&&x<=right){intersections.push({x:x,y:bottom,distance:tBottom})}}}if(intersections.length>0){intersections.sort((a,b)=>a.distance-b.distance);return intersections[0]}return null}function cleanupOldEdgeIndicators(){const currentTime=Date.now();const maxAge=hitIndicatorState.edgeIndicatorLifetime;for(let i=hitIndicatorState.edgeIndicators.length-1;i>=0;i--){const indicator=hitIndicatorState.edgeIndicators[i];const age=currentTime-indicator.created;if(age>maxAge){if(indicator.element&&indicator.element.parentNode){indicator.element.classList.remove('visible');setTimeout(()=>{if(indicator.element.parentNode){indicator.element.parentNode.removeChild(indicator.element)}},200)}hitIndicatorState.edgeIndicators.splice(i,1)}}}function flashScreen(damage){const currentTime=Date.now();let opacity=0;if(damage>=50){opacity=0.5}else if(damage>=25){opacity=0.3}else{opacity=0.2}screenFlashRed.style.opacity=opacity;screenFlashRed.classList.add('visible');screenFlashWhite.style.opacity=0.4;screenFlashWhite.classList.add('visible');setTimeout(()=>{screenFlashRed.classList.remove('visible');screenFlashWhite.classList.remove('visible');setTimeout(()=>{screenFlashRed.style.opacity='0';screenFlashWhite.style.opacity='0'},100)},100)}function updateHitIndicator(){const currentTime=Date.now();if(hitIndicatorState.visible){if(currentTime>=hitIndicatorState.showUntil){hitIndicatorState.visible=false;hitIndicator.classList.remove('visible');setTimeout(()=>{hitIndicator.style.opacity='0'},300)}else{const remaining=hitIndicatorState.showUntil-currentTime;const fadeStart=800;const fadeDuration=200;if(remaining<fadeStart){const fadeProgress=1-((fadeStart-remaining)/fadeDuration);hitIndicator.style.opacity=Math.max(0,fadeProgress).toString()}}}cleanupOldEdgeIndicators()}function resetHitIndicator(){hitIndicatorState.visible=false;hitIndicator.classList.remove('visible');hitIndicator.style.opacity='0';hitIndicatorState.edgeIndicators.forEach(indicator=>{if(indicator.element&&indicator.element.parentNode){indicator.element.parentNode.removeChild(indicator.element)}});hitIndicatorState.edgeIndicators=[];screenFlashRed.classList.remove('visible');screenFlashWhite.classList.remove('visible');screenFlashRed.style.opacity='0';screenFlashWhite.style.opacity='0'}function initSpectatorMode(){if(gameState.spectatorMode)return;gameState.spectatorMode=true;gameState.spectatorIndex=0;gameState.spectatorTarget=null;gameState.playerFlashlightBeforeSpectate=gameState.playerFlashlight;gameState.playerFlashlight=true;let foundTarget=false;for(const teammate of teammates){if(teammate.alive){gameState.spectatorTarget=teammate;foundTarget=true;break}}if(!foundTarget){for(const enemy of enemies){if(enemy.alive){gameState.spectatorTarget=enemy;foundTarget=true;break}}}if(gameState.spectatorTarget){showMessage(`正在观战:${gameState.spectatorTarget.name}`,"#4CAF50");console.log(`进入观战模式,目标:${gameState.spectatorTarget.name}`)}else{gameState.spectatorMode=false;showMessage("没有可观战的目标","#FF6B6B")}}function switchSpectatorTarget(){if(!gameState.spectatorMode)return;const allPlayers=[...teammates,...enemies].filter(p=>p.alive);if(allPlayers.length===0){showMessage("没有可观战的目标","#FF6B6B");return}gameState.spectatorIndex=(gameState.spectatorIndex+1)%allPlayers.length;gameState.spectatorTarget=allPlayers[gameState.spectatorIndex];showMessage(`正在观战:${gameState.spectatorTarget.name}`,"#4CAF50");console.log(`切换观战目标:${gameState.spectatorTarget.name}`)}function updateSpectator(){if(!gameState.spectatorMode||!gameState.spectatorTarget)return;world.offsetX=canvas.width/2-gameState.spectatorTarget.x;world.offsetY=canvas.height/2-gameState.spectatorTarget.y;const currentTime=Date.now();if(currentTime-gameState.lastSpectatorCheck>1000){gameState.lastSpectatorCheck=currentTime;if(!gameState.spectatorTarget.alive){switchSpectatorTarget();if(!gameState.spectatorTarget){gameState.spectatorMode=false;showMessage("观战目标已死亡,退出观战模式","#FF6B6B")}}}}function drawGrid(){ctx.strokeStyle='rgba(255, 255, 255, 0.05)';ctx.lineWidth=1;const gridSize=100;const startX=Math.floor((-world.offsetX)/gridSize)*gridSize;const startY=Math.floor((-world.offsetY)/gridSize)*gridSize;for(let x=startX;x<canvas.width;x+=gridSize){ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,canvas.height);ctx.stroke()}for(let y=startY;y<canvas.height;y+=gridSize){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(canvas.width,y);ctx.stroke()}}function drawTopDownCircle(x,y,radius,direction,color,isPlayer=false){let screenX,screenY;if(isPlayer&&!gameState.spectatorMode){screenX=canvas.width/2;screenY=canvas.height/2}else{screenX=x+world.offsetX;screenY=y+world.offsetY}if(screenX<-radius||screenX>canvas.width+radius||screenY<-radius||screenY>canvas.height+radius){return}ctx.fillStyle=color;ctx.beginPath();ctx.arc(screenX,screenY,radius,0,Math.PI*2);ctx.fill();ctx.fillStyle=isPlayer?'#FFFFFF':(color==='#4A90E2'?'#FFFFFF':'#FF0000');ctx.beginPath();ctx.moveTo(screenX+Math.cos(direction)*radius*1.3,screenY+Math.sin(direction)*radius*1.3);ctx.lineTo(screenX+Math.cos(direction+Math.PI*0.8)*radius*0.8,screenY+Math.sin(direction+Math.PI*0.8)*radius*0.8);ctx.lineTo(screenX+Math.cos(direction-Math.PI*0.8)*radius*0.8,screenY+Math.sin(direction-Math.PI*0.8)*radius*0.8);ctx.closePath();ctx.fill();if(isPlayer&&gameState.playerFlashlight&&!gameState.spectatorMode){ctx.fillStyle='#FFFF00';ctx.beginPath();ctx.arc(screenX+Math.cos(direction)*radius*0.8,screenY+Math.sin(direction)*radius*0.8,radius*0.4,0,Math.PI*2);ctx.fill()}if(gameState.spectatorTarget&&x===gameState.spectatorTarget.x&&y===gameState.spectatorTarget.y){ctx.strokeStyle='#FFFF00';ctx.lineWidth=3;ctx.beginPath();ctx.arc(screenX,screenY,radius+8,0,Math.PI*2);ctx.stroke();ctx.fillStyle='#FFFFFF';ctx.font='bold 14px Arial';ctx.textAlign='center';ctx.fillText(`${gameState.spectatorTarget.name}(${gameState.spectatorTarget.health}HP)`,screenX,screenY-radius-15)}}function drawFOV(){if(!gameState.playerFlashlight)return;let targetX,targetY,targetDirection;let isAlive=true;if(gameState.spectatorMode&&gameState.spectatorTarget){targetX=gameState.spectatorTarget.x;targetY=gameState.spectatorTarget.y;targetDirection=gameState.spectatorTarget.direction;isAlive=gameState.spectatorTarget.alive}else{targetX=player.x;targetY=player.y;targetDirection=player.direction;isAlive=player.alive}if(!isAlive)return;ctx.save();const screenCenterX=canvas.width/2;const screenCenterY=canvas.height/2;const fovAngle=90*Math.PI/180;const viewDistance=1000;ctx.beginPath();ctx.moveTo(screenCenterX,screenCenterY);const startAngle=targetDirection-fovAngle/2;const endAngle=targetDirection+fovAngle/2;const rayCount=120;for(let i=0;i<=rayCount;i++){const angle=startAngle+(endAngle-startAngle)*(i/rayCount);const result=rayCast(targetX,targetY,angle,viewDistance);const screenX=result.x+world.offsetX;const screenY=result.y+world.offsetY;if(i===0){ctx.lineTo(screenX,screenY)}else{ctx.lineTo(screenX,screenY)}}ctx.closePath();const gradient=ctx.createRadialGradient(screenCenterX,screenCenterY,0,screenCenterX,screenCenterY,viewDistance);gradient.addColorStop(0,'rgba(255, 255, 200, 0.15)');gradient.addColorStop(0.7,'rgba(255, 255, 200, 0.05)');gradient.addColorStop(1,'rgba(255, 255, 200, 0)');ctx.fillStyle=gradient;ctx.fill();ctx.restore()}function drawFog(){const fogCanvas=document.createElement('canvas');fogCanvas.width=canvas.width;fogCanvas.height=canvas.height;const fogCtx=fogCanvas.getContext('2d');fogCtx.fillStyle='rgba(0, 0, 0, 0.85)';fogCtx.fillRect(0,0,fogCanvas.width,fogCanvas.height);let targetX,targetY,targetDirection;let hasFlashlight=gameState.playerFlashlight;let isAlive=true;if(gameState.spectatorMode&&gameState.spectatorTarget){targetX=gameState.spectatorTarget.x;targetY=gameState.spectatorTarget.y;targetDirection=gameState.spectatorTarget.direction;isAlive=gameState.spectatorTarget.alive;hasFlashlight=true}else{targetX=player.x;targetY=player.y;targetDirection=player.direction;isAlive=player.alive;hasFlashlight=gameState.playerFlashlight}if(!hasFlashlight||!isAlive){ctx.drawImage(fogCanvas,0,0);return}fogCtx.globalCompositeOperation='destination-out';const centerX=canvas.width/2;const centerY=canvas.height/2;const fovAngle=90*Math.PI/180;const viewDistance=1000;fogCtx.beginPath();fogCtx.moveTo(centerX,centerY);const startAngle=targetDirection-fovAngle/2;const endAngle=targetDirection+fovAngle/2;const rayCount=120;for(let i=0;i<=rayCount;i++){const angle=startAngle+(endAngle-startAngle)*(i/rayCount);const result=rayCast(targetX,targetY,angle,viewDistance);const screenX=result.x+world.offsetX;const screenY=result.y+world.offsetY;if(i===0){fogCtx.lineTo(screenX,screenY)}else{fogCtx.lineTo(screenX,screenY)}}fogCtx.closePath();const gradient=fogCtx.createRadialGradient(centerX,centerY,0,centerX,centerY,viewDistance);gradient.addColorStop(0,'rgba(0, 0, 0, 0.85)');gradient.addColorStop(0.5,'rgba(0, 0, 0, 0.6)');gradient.addColorStop(0.8,'rgba(0, 0, 0, 0.3)');gradient.addColorStop(1,'rgba(0, 0, 0, 0)');fogCtx.fillStyle=gradient;fogCtx.fill();ctx.drawImage(fogCanvas,0,0)}function drawAimLine(){if(!aimLine.visible||!player.alive||gameState.shopOpen||gameState.spectatorMode)return;ctx.strokeStyle='rgba(255, 255, 255, 0.7)';ctx.lineWidth=1;ctx.setLineDash([5,3]);ctx.beginPath();ctx.moveTo(aimLine.startX,aimLine.startY);ctx.lineTo(aimLine.endX,aimLine.endY);ctx.stroke();ctx.setLineDash([]);ctx.fillStyle='rgba(255, 0, 0, 0.5)';ctx.beginPath();ctx.arc(aimLine.endX,aimLine.endY,3,0,Math.PI*2);ctx.fill()}function drawWalls(){for(const wall of walls){const screenX=wall.x+world.offsetX;const screenY=wall.y+world.offsetY;if(screenX<-wall.width||screenX>canvas.width+wall.width||screenY<-wall.height||screenY>canvas.height+wall.height){continue}ctx.fillStyle=wall.color;ctx.fillRect(screenX,screenY,wall.width,wall.height);ctx.strokeStyle='#654321';ctx.lineWidth=2;ctx.strokeRect(screenX,screenY,wall.width,wall.height)}}function drawBombSites(){for(const site of bombSites){const screenX=site.x+world.offsetX;const screenY=site.y+world.offsetY;if(screenX<-site.radius||screenX>canvas.width+site.radius||screenY<-site.radius||screenY>canvas.height+site.radius){continue}ctx.strokeStyle=gameState.bombPlanted&&gameState.bombSite===site?'#FF0000':'#00FF00';ctx.lineWidth=3;ctx.beginPath();ctx.arc(screenX,screenY,site.radius,0,Math.PI*2);ctx.stroke();ctx.fillStyle=gameState.bombPlanted&&gameState.bombSite===site?'#FF0000':'#00FF00';ctx.font='bold 16px Arial';ctx.textAlign='center';ctx.fillText(site.name,screenX,screenY-site.radius-10)}}function drawBullets(){for(const bullet of bullets){const screenX=bullet.x+world.offsetX;const screenY=bullet.y+world.offsetY;if(screenX<-bullet.radius||screenX>canvas.width+bullet.radius||screenY<-bullet.radius||screenY>canvas.height+bullet.radius){continue}ctx.fillStyle=bullet.color;ctx.beginPath();ctx.arc(screenX,screenY,bullet.radius,0,Math.PI*2);ctx.fill()}}function drawBulletTrails(){for(const trail of bulletTrails){const screenX=trail.x+world.offsetX;const screenY=trail.y+world.offsetY;if(screenX<-10||screenX>canvas.width+10||screenY<-10||screenY>canvas.height+10){continue}ctx.fillStyle=`rgba(${parseInt(trail.color.slice(1,3),16)},${parseInt(trail.color.slice(3,5),16)},${parseInt(trail.color.slice(5,7),16)},${trail.alpha})`;ctx.beginPath();ctx.arc(screenX,screenY,trail.radius,0,Math.PI*2);ctx.fill()}}function drawHitEffects(){for(const effect of hitEffects){const screenX=effect.x+world.offsetX;const screenY=effect.y+world.offsetY;ctx.fillStyle=`rgba(${parseInt(effect.color.slice(1,3),16)},${parseInt(effect.color.slice(3,5),16)},${parseInt(effect.color.slice(5,7),16)},${effect.alpha})`;ctx.beginPath();ctx.arc(screenX,screenY,effect.radius,0,Math.PI*2);ctx.fill()}}function drawExplosionEffects(){for(const effect of explosionEffects){const screenX=effect.x+world.offsetX;const screenY=effect.y+world.offsetY;ctx.fillStyle=`rgba(${parseInt(effect.color.slice(1,3),16)},${parseInt(effect.color.slice(3,5),16)},${parseInt(effect.color.slice(5,7),16)},${effect.alpha})`;ctx.beginPath();ctx.arc(screenX,screenY,effect.radius,0,Math.PI*2);ctx.fill()}}function drawWeaponDrops(){for(const drop of weaponDrops){const screenX=drop.x+world.offsetX;const screenY=drop.y+world.offsetY;if(screenX<-drop.radius||screenX>canvas.width+drop.radius||screenY<-drop.radius||screenY>canvas.height+drop.radius){continue}ctx.fillStyle=drop.color;ctx.beginPath();ctx.arc(screenX,screenY,drop.radius,0,Math.PI*2);ctx.fill();ctx.fillStyle='#FFFFFF';ctx.font='20px Arial';ctx.textAlign='center';ctx.textBaseline='middle';ctx.fillText('⬇',screenX,screenY)}}function drawPlayers(){teammates.forEach(teammate=>{if(!teammate.alive)return;const shouldDraw=gameState.spectatorMode||isPointInFOV(teammate.x,teammate.y);if(shouldDraw){drawTopDownCircle(teammate.x,teammate.y,teammate.radius,teammate.direction,teammate.color,false);if(teammate.bombDefusing){const screenX=teammate.x+world.offsetX;const screenY=teammate.y+world.offsetY;const defuseTime=teammate.hasDefuseKit?gameState.bombDefuseTimeWithKit:gameState.bombDefuseTime;const progress=Math.min(1,teammate.bombDefuseProgress/defuseTime);const barWidth=80;const barHeight=8;const barX=screenX-barWidth/2;const barY=screenY-teammate.radius-25;ctx.fillStyle='rgba(0, 0, 0, 0.7)';ctx.fillRect(barX,barY,barWidth,barHeight);ctx.fillStyle=teammate.hasDefuseKit?'#4CAF50':'#00FF00';ctx.fillRect(barX,barY,barWidth*progress,barHeight);ctx.strokeStyle='#FFFFFF';ctx.lineWidth=1;ctx.strokeRect(barX,barY,barWidth,barHeight);ctx.fillStyle='#FFFFFF';ctx.font='bold 12px Arial';ctx.textAlign='center';ctx.textBaseline='middle';const percent=Math.floor(progress*100);ctx.fillText(`${percent}%`,screenX,barY+barHeight/2);if(teammate.hasDefuseKit){ctx.fillStyle='#4CAF50';ctx.font='16px Arial';ctx.fillText('🔧',screenX,screenY-teammate.radius-45)}}}});enemies.forEach(enemy=>{if(!enemy.alive)return;const shouldDraw=gameState.spectatorMode||isPointInFOV(enemy.x,enemy.y);if(shouldDraw){drawTopDownCircle(enemy.x,enemy.y,enemy.radius,enemy.direction,enemy.color,false);if(enemy.bombPlanting){const screenX=enemy.x+world.offsetX;const screenY=enemy.y+world.offsetY;const progress=Math.min(1,enemy.bombPlantProgress/gameState.bombPlantTime);const barWidth=80;const barHeight=8;const barX=screenX-barWidth/2;const barY=screenY-enemy.radius-25;ctx.fillStyle='rgba(0, 0, 0, 0.7)';ctx.fillRect(barX,barY,barWidth,barHeight);ctx.fillStyle='#FF4500';ctx.fillRect(barX,barY,barWidth*progress,barHeight);ctx.strokeStyle='#FFFFFF';ctx.lineWidth=1;ctx.strokeRect(barX,barY,barWidth,barHeight);ctx.fillStyle='#FFFFFF';ctx.font='bold 12px Arial';ctx.textAlign='center';ctx.textBaseline='middle';const percent=Math.floor(progress*100);ctx.fillText(`${percent}%`,screenX,barY+barHeight/2);if(enemy.hasBomb){ctx.fillStyle='#FF4500';ctx.font='16px Arial';ctx.fillText('💣',screenX,screenY-enemy.radius-45)}}}});if(!gameState.spectatorMode&&player.alive){drawTopDownCircle(player.x,player.y,player.radius,player.direction,player.color,true)}}function gameLoop(){if(!gameStarted){requestAnimationFrame(gameLoop);return}ctx.clearRect(0,0,canvas.width,canvas.height);drawGrid();drawWalls();drawBombSites();if(!gameState.spectatorMode&&player.alive){drawFOV()}drawBulletTrails();drawBullets();drawHitEffects();drawExplosionEffects();drawWeaponDrops();drawPlayers();drawFog();drawAimLine();updateStats();if(!gameState.gameOver&&!gameState.isSwapping){updatePlayer();updateAI();if(gameState.bombPlanted){manageCTDefuseStrategy()}updateBullets();updateBulletTrails();updateHitEffects();updateExplosionEffects();updateWeaponDrops();updateSpectator();updateHitIndicator()}requestAnimationFrame(gameLoop)}initGame();</script></body></html>

浙公网安备 33010602011771号