-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
247 lines (197 loc) · 37.2 KB
/
Copy pathindex.html
File metadata and controls
247 lines (197 loc) · 37.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RIPTIDE</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/howler/2.2.4/howler.min.js"></script>
<style>
*{margin:0;padding:0;box-sizing:border-box;user-select:none}
body{background:#fdfbf7;overflow:hidden;cursor:crosshair;font-family:'Comic Sans MS','Chalkboard SE','Marker Felt',cursive;transition:filter 0.3s}
canvas{position:fixed;top:0;left:0;z-index:1;display:block}
.paper-lines{position:fixed;inset:0;pointer-events:none;z-index:998;background:repeating-linear-gradient(transparent,transparent 29px,rgba(100,149,237,0.2) 30px);mix-blend-mode:multiply}
.paper-noise{position:fixed;inset:0;pointer-events:none;z-index:997;opacity:0.06;background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")}
.scanlines{position:fixed;inset:0;pointer-events:none;z-index:996;background:repeating-linear-gradient(0deg,rgba(0,0,0,0.04) 0px,rgba(0,0,0,0.04) 1px,transparent 1px,transparent 2px);mix-blend-mode:multiply}
.screen{position:fixed;inset:0;display:none;flex-direction:column;align-items:center;justify-content:center;z-index:10;background:#fdfbf7}
.screen.active{display:flex}
.flash-btn{background:linear-gradient(180deg,#fff 0%,#eee 50%,#ccc 100%);border:3px solid #000;border-radius:2px;padding:10px 30px;font-family:inherit;font-size:18px;font-weight:bold;cursor:pointer;color:#000;text-shadow:1px 1px 0 #fff;box-shadow:3px 3px 0 #000;transition:all 0.1s;text-transform:uppercase;letter-spacing:1px;margin:5px}
.flash-btn:hover{background:linear-gradient(180deg,#ffff00 0%,#ffcc00 50%,#ff9900 100%);color:#000;transform:translate(1px,1px);box-shadow:2px 2px 0 #000}
.flash-btn:active{transform:translate(3px,3px);box-shadow:0 0 0 #000}
.title-text{font-size:clamp(4rem,12vw,10rem);color:#333;text-shadow:4px 4px 0 #fff,-2px -2px 0 #ff0000;letter-spacing:-5px;transform:rotate(-3deg) skewX(-10deg);margin-bottom:10px}
.subtitle-text{font-size:1.2rem;color:#666;margin-bottom:40px;background:#ffff00;padding:2px 8px;transform:rotate(2deg);display:inline-block;border:1px solid #000}
.menu-box{display:flex;flex-direction:column;gap:15px;align-items:center}
.cmd-grid{display:flex;flex-wrap:wrap;gap:20px;justify-content:center;max-width:900px}
.cmd-card{background:#fff;border:3px solid #000;width:200px;padding:15px;cursor:pointer;box-shadow:4px 4px 0 #000;transition:transform 0.2s}
.cmd-card:hover{transform:translate(-2px,-2px);box-shadow:6px 6px 0 #000}
.cmd-card.selected{border-color:#ff0000;background:#ffffcc}
.hud-top{position:fixed;top:10px;left:10px;right:10px;display:flex;justify-content:space-between;align-items:flex-start;z-index:20;pointer-events:none}
.hud-top>*{pointer-events:auto}
.hp-bar{width:300px;height:25px;background:#fff;border:3px solid #000;position:relative}
.hp-fill{height:100%;background:#00ff00;transition:width 0.2s}
.hp-text{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-weight:bold;font-size:14px;color:#000;z-index:1}
.wave-badge{background:#fff;border:3px solid #000;padding:5px 15px;font-weight:bold;font-size:18px;box-shadow:2px 2px 0 #000}
.ability-box{position:fixed;bottom:20px;right:20px;z-index:20;background:#fff;border:3px solid #000;padding:10px;box-shadow:3px 3px 0 #000;cursor:pointer;width:70px;height:70px;display:flex;align-items:center;justify-content:center;font-size:24px;font-weight:bold}
.ability-box.ready{background:#ffff00;animation:abPulse 1s infinite}
@keyframes abPulse{0%,100%{box-shadow:3px 3px 0 #000}50%{box-shadow:3px 3px 0 #000,0 0 15px #ff0000}}
.dialogue-box{position:fixed;bottom:100px;left:50%;transform:translateX(-50%);width:80%;max-width:600px;background:#fff;border:3px solid #000;padding:15px;z-index:30;display:none;box-shadow:5px 5px 0 #000}
.dialogue-box.show{display:block}
.dlg-name{font-weight:bold;color:#ff0000;margin-bottom:5px;font-size:18px}
.dlg-text{font-size:16px;line-height:1.4}
.pause-screen{position:fixed;inset:0;background:rgba(253,251,247,0.9);z-index:50;display:none;flex-direction:column;align-items:center;justify-content:center;gap:20px;border:10px solid #ffff00}
.pause-screen.show{display:flex}
.upgrade-screen{position:fixed;inset:0;background:rgba(253,251,247,0.95);z-index:40;display:none;flex-direction:column;align-items:center;justify-content:center;gap:15px}
.upgrade-screen.show{display:flex}
.upg-card{background:#fff;border:3px solid #000;padding:15px 25px;cursor:pointer;font-size:18px;font-weight:bold;box-shadow:3px 3px 0 #000;width:300px;text-align:center}
.upg-card:hover{background:#ccffcc}
.modal-bg{position:fixed;inset:0;background:rgba(0,0,0,0.6);z-index:60;display:none;align-items:center;justify-content:center}
.modal-bg.show{display:flex}
.modal-box{background:#fff;border:4px solid #000;padding:20px;width:90%;max-width:400px;box-shadow:6px 6px 0 #000}
.modal-box textarea{width:100%;padding:8px;margin:5px 0;font-family:inherit;border:2px solid #000;box-sizing:border-box;height:80px;resize:vertical}
.explosion-text{position:fixed;font-family:impact,sans-serif;pointer-events:none;z-index:25;animation:boomAnim 0.8s forwards;font-size:40px;color:#ff0000;text-shadow:2px 2px 0 #000,-1px -1px 0 #ffff00;transform:rotate(-10deg)}
@keyframes boomAnim{0%{opacity:1;transform:scale(0.5) rotate(-10deg)}50%{transform:scale(1.5) rotate(10deg)}100%{opacity:0;transform:scale(2) rotate(-5deg) translateY(-50px)}}
.dmg-pop{position:fixed;pointer-events:none;z-index:26;font-weight:bold;animation:dmgUp 1s forwards}
@keyframes dmgUp{0%{opacity:1;transform:translateY(0)}100%{opacity:0;transform:translateY(-40px)}}
.minimap-box{position:fixed;bottom:10px;left:10px;background:#fff;border:3px solid #000;box-shadow:2px 2px 0 #000;z-index:20}
body.dark-mode{filter:invert(1) hue-rotate(180deg) contrast(0.9) brightness(0.9)}
body.dark-mode .paper-lines{filter:invert(1) hue-rotate(180deg)}
body.dark-mode .paper-noise{filter:invert(1)}
body.dark-mode .scanlines{filter:invert(1)}
</style>
</head>
<body>
<div class="paper-lines"></div>
<div class="paper-noise"></div>
<div class="scanlines"></div>
<div id="menuScreen" class="screen active">
<div class="title-text">RIPTIDE</div>
<div class="subtitle-text" id="subTitle">Naval Warfare 2007</div>
<div class="menu-box" id="menuBox"></div>
</div>
<div id="cmdScreen" class="screen">
<h2 style="color:#333;margin-bottom:20px;transform:rotate(-2deg);background:#fff;display:inline-block;padding:5px 15px;border:2px solid #000" id="cmdTitle">CHOOSE YOUR COMMANDER</h2>
<div class="cmd-grid" id="cmdGrid"></div>
<div style="margin-top:20px;display:flex;gap:10px">
<button class="flash-btn" onclick="startGame()">BATTLE!</button>
<button class="flash-btn" onclick="goMenu()">BACK</button>
</div>
</div>
<div id="gameScreen" class="screen">
<canvas id="gc"></canvas>
<div class="hud-top">
<div class="hp-bar"><div class="hp-fill" id="hpFill"></div><div class="hp-text" id="hpTxt">1000/1000</div></div>
<div class="wave-badge" id="waveBadge">WAVE 1</div>
</div>
<div class="ability-box" id="abBox" onclick="useAbility()">Q</div>
<div class="minimap-box"><canvas id="mm" width="120" height="90"></canvas></div>
</div>
<div id="dlgBox" class="dialogue-box">
<div class="dlg-name" id="dlgName"></div>
<div class="dlg-text" id="dlgText"></div>
</div>
<div id="pauseScr" class="pause-screen">
<h1>PAUSED</h1>
<button class="flash-btn" onclick="resume()">RESUME</button>
<button class="flash-btn" onclick="saveGame()">SAVE</button>
<button class="flash-btn" onclick="openExp()">EXPORT</button>
<button class="flash-btn" onclick="toggleDark()">DARK MODE</button>
<button class="flash-btn" onclick="goMenu()">QUIT</button>
</div>
<div id="upgScr" class="upgrade-screen">
<h2 style="color:#333;background:#ffff00;padding:5px 15px;border:2px solid #000;display:inline-block" id="upgTitle">WAVE CLEARED!</h2>
<div id="upgOpts" style="display:flex;gap:15px;flex-wrap:wrap;justify-content:center"></div>
</div>
<div id="modBg" class="modal-bg">
<div class="modal-box" id="modBox">
<h3 id="modTitle">LOAD</h3>
<div id="modContent"></div>
<textarea id="modArea" rows="4" style="display:none"></textarea>
<div style="margin-top:10px;display:flex;gap:5px">
<button class="flash-btn" id="modOk" onclick="modOkFn()">OK</button>
<button class="flash-btn" onclick="closeMod()">CLOSE</button>
</div>
</div>
</div>
<script>
var L={en:{start:'START GAME',load:'LOAD GAME',imp:'IMPORT SAVE',dark:'DARK MODE',lang:'PORTUGUÊS',cmd:'CHOOSE YOUR COMMANDER',battle:'BATTLE!',back:'BACK',pause:'PAUSED',resume:'RESUME',save:'SAVE',export:'EXPORT',quit:'QUIT',wave:'WAVE',wclear:'WAVE CLEARED!',loadtxt:'Load from slot:',impTxt:'Paste your save code here:',expTxt:'Copy your save code:',slot1:'Slot 1',nosave:'No save found',cmd1:'xX_ShadowSniper_Xx',cmd2:'EpicFailGuy',cmd3:'NyanAdmiral',cmd4:'ORLY_Owl',boss:'WARNING! BOSS INCOMING!',victory:'MISSION COMPLETE!',defeat:'SHIP SUNK!',repair:'REPAIR PARTY',sakura:'CHERRY BLOSSOM',gaster:'GASTER BLASTER',thunder:'THUNDER STRIKE',upg1:'+100 HP',upg2:'+10% DMG',upg3:'+8% SPD',upg4:'REPAIR 50%',upg5:'+5 TORPEDOS',sub:'Naval Warfare 2007'},pt:{start:'INICIAR JOGO',load:'CARREGAR',imp:'IMPORTAR SAVE',dark:'MODO ESCURO',lang:'ENGLISH',cmd:'ESCOLHA SEU COMANDANTE',battle:'BATALHAR!',back:'VOLTAR',pause:'PAUSADO',resume:'CONTINUAR',save:'SALVAR',export:'EXPORTAR',quit:'SAIR',wave:'ONDA',wclear:'ONDA DERROTADA!',loadtxt:'Carregar do slot:',impTxt:'Cole o codigo do save aqui:',expTxt:'Copie seu codigo:',slot1:'Slot 1',nosave:'Nenhum save',cmd1:'xX_ShadowSniper_Xx',cmd2:'EpicFailGuy',cmd3:'NyanAdmiral',cmd4:'ORLY_Owl',boss:'ALERTA! BOSS A VISTA!',victory:'MISSAO COMPLETA!',defeat:'NAVIO AFUNDOU!',repair:'EQUIPE DE REPARO',sakura:'FLOR DE CEREJEIRA',gaster:'GASTER BLASTER',thunder:'ATAQUE TROVAO',upg1:'+100 HP',upg2:'+10% DANO',upg3:'+8% VEL',upg4:'REPARAR 50%',upg5:'+5 TORPEDOS',sub:'Guerra Naval 2007'}};
var lang='en';
function t(k){return L[lang][k]||k}
function toggleLang(){lang=lang==='en'?'pt':'en';updateAllText()}
function toggleDark(){document.body.classList.toggle('dark-mode')}
function updateAllText(){document.getElementById('subTitle').innerText=t('sub');var mb=document.getElementById('menuBox');mb.innerHTML='';mb.innerHTML='<button class="flash-btn" onclick="showCmdSelect()">'+t('start')+'</button><button class="flash-btn" onclick="openSave()">'+t('load')+'</button><button class="flash-btn" onclick="openImp()">'+t('imp')+'</button><button class="flash-btn" onclick="toggleDark()">'+t('dark')+'</button><button class="flash-btn" onclick="toggleLang()">'+t('lang')+'</button>';document.getElementById('cmdTitle').innerText=t('cmd');buildCmds()}
var G={screen:'menu',cmd:0,wave:1,paused:false,player:null,enemies:[],proj:[],parts:[],picks:[],islands:[],cam:{x:0,y:0,sx:0,sy:0,sd:0},mouse:{x:0,y:0,wx:0,wy:0,down:false},keys:{},ctx:null,cvs:null,W:0,H:0,ww:4000,wh:3000,xp:0,lvl:1,xpn:100,kills:0,abCd:0,abMax:15,gameTime:0,dt:0,lastT:0,bDmg:0,bSpd:0,bHp:0,dialogQ:[],dialogOn:false,selWep:0,stats:{k:0,d:0,t:0},bossDead:false,mmCtx:null};
var CMDS=[{id:0,name:'cmd1',color:'#ff0000',hp:1.2,dmg:1.0,spd:0.9,ab:'repair',abName:'repair'},{id:1,name:'cmd2',color:'#00ff00',hp:0.9,dmg:1.1,spd:1.2,ab:'sakura',abName:'sakura'},{id:2,name:'cmd3',color:'#ff69b4',hp:1.0,dmg:1.3,spd:1.0,ab:'gaster',abName:'gaster'},{id:3,name:'cmd4',color:'#9932cc',hp:0.8,dmg:1.0,spd:1.1,ab:'thunder',abName:'thunder'}];
var WEPS=[{dmg:40,rate:0.8,spd:14,rng:600,spread:0.04,ammo:999,count:2,col:'#000'},{dmg:15,rate:0.3,spd:12,rng:400,spread:0.08,ammo:999,count:4,col:'#333'},{dmg:120,rate:4,spd:5,rng:500,spread:0.02,ammo:10,count:1,col:'#00f'},{dmg:8,rate:0.1,spd:16,rng:300,spread:0.15,ammo:999,count:8,col:'#999'}];
var ETYPES=[{hp:300,spd:2.5,dmg:12,rate:1.5,rng:350,sz:18,col:'#8e44ad',ai:'aggro',xp:30},{hp:600,spd:1.8,dmg:20,rate:2,rng:450,sz:25,col:'#2980b9',ai:'tact',xp:50},{hp:1200,spd:1.0,dmg:35,rate:3,rng:550,sz:35,col:'#c0392b',ai:'def',xp:80},{hp:800,spd:0.7,dmg:15,rate:4,rng:600,sz:40,col:'#16a085',ai:'sup',xp:100},{hp:100,spd:3.5,dmg:8,rate:0.8,rng:250,sz:12,col:'#d35400',ai:'swarm',xp:15}];
var DLG_LINES={firstblood:['FIRST BLOOD!','Pwned!','GG!','Rekt!'],lowhp:['HULL CRITICAL!','Abandon ship?!','leeroy jenkins!','OMFG HELP!'],boss:['WARNING! BOSS INCOMING!','O SHI-','U wot m8?'],victory:['MISSION COMPLETE!','GG no re!','All your base are belong to us!'],revenge:['REVENGE!','That was for my homie!','Taste justice!'],retreat:["THEY'RE RETREATING!",'Cowards!','Cya n00bs!']};
var audioCtx=null;
var musicStep=0;
var musicTimer=null;
var currentTrack='menu';
var tracks={menu:{bpm:300,lead:[440,0,523,0,659,0,523,0],bass:[220,0,0,0,165,0,0,0]},calm:{bpm:250,lead:[523,0,659,0,784,0,659,0],bass:[261,0,0,0,196,0,0,0]},battle:{bpm:150,lead:[440,523,659,523,440,523,587,494],bass:[110,110,0,110,146,146,0,146]},boss:{bpm:100,lead:[659,622,587,554,659,622,587,554],bass:[82,82,82,82,82,82,82,82]}};
function initAudio(){if(!audioCtx){audioCtx=new(window.AudioContext||window.webkitAudioContext)();startMusic()}}
function playNote(freq,dur,type,vol){if(!freq||!audioCtx)return;var o=audioCtx.createOscillator();var g=audioCtx.createGain();o.type=type||'square';o.frequency.value=freq;g.gain.setValueAtTime(vol||0.05,audioCtx.currentTime);g.gain.exponentialRampToValueAtTime(0.001,audioCtx.currentTime+dur);o.connect(g);g.connect(audioCtx.destination);o.start();o.stop(audioCtx.currentTime+dur)}
function musicTick(){var tr=tracks[currentTrack];var lead=tr.lead[musicStep%tr.lead.length];var bass=tr.bass[musicStep%tr.bass.length];if(lead)playNote(lead,tr.bpm/1000,'square',0.03);if(bass)playNote(bass,tr.bpm/500,'triangle',0.05);musicStep++}
function startMusic(){if(!audioCtx)return;if(musicTimer)clearInterval(musicTimer);musicTimer=setInterval(musicTick,tracks[currentTrack].bpm)}
function changeTrack(name){if(currentTrack===name||!audioCtx)return;currentTrack=name;if(musicTimer)clearInterval(musicTimer);musicTimer=setInterval(musicTick,tracks[currentTrack].bpm)}
function sfx(type){if(!audioCtx)return;if(type==='shoot'){var o=audioCtx.createOscillator();var g=audioCtx.createGain();o.type='sawtooth';o.frequency.setValueAtTime(800,audioCtx.currentTime);o.frequency.exponentialRampToValueAtTime(200,audioCtx.currentTime+0.1);g.gain.setValueAtTime(0.05,audioCtx.currentTime);g.gain.exponentialRampToValueAtTime(0.001,audioCtx.currentTime+0.1);o.connect(g);g.connect(audioCtx.destination);o.start();o.stop(audioCtx.currentTime+0.1)}else if(type==='boom'){var o=audioCtx.createOscillator();var g=audioCtx.createGain();o.type='square';o.frequency.setValueAtTime(150,audioCtx.currentTime);o.frequency.exponentialRampToValueAtTime(30,audioCtx.currentTime+0.3);g.gain.setValueAtTime(0.1,audioCtx.currentTime);g.gain.exponentialRampToValueAtTime(0.001,audioCtx.currentTime+0.3);o.connect(g);g.connect(audioCtx.destination);o.start();o.stop(audioCtx.currentTime+0.3)}else if(type==='hit'){var o=audioCtx.createOscillator();var g=audioCtx.createGain();o.type='square';o.frequency.setValueAtTime(400,audioCtx.currentTime);g.gain.setValueAtTime(0.04,audioCtx.currentTime);g.gain.exponentialRampToValueAtTime(0.001,audioCtx.currentTime+0.05);o.connect(g);g.connect(audioCtx.destination);o.start();o.stop(audioCtx.currentTime+0.05)}else if(type==='pickup'){var o=audioCtx.createOscillator();var g=audioCtx.createGain();o.type='sine';o.frequency.setValueAtTime(600,audioCtx.currentTime);o.frequency.exponentialRampToValueAtTime(1200,audioCtx.currentTime+0.1);g.gain.setValueAtTime(0.06,audioCtx.currentTime);g.gain.exponentialRampToValueAtTime(0.001,audioCtx.currentTime+0.15);o.connect(g);g.connect(audioCtx.destination);o.start();o.stop(audioCtx.currentTime+0.15)}else if(type==='ability'){var o=audioCtx.createOscillator();var g=audioCtx.createGain();o.type='sine';o.frequency.setValueAtTime(200,audioCtx.currentTime);o.frequency.linearRampToValueAtTime(800,audioCtx.currentTime+0.2);g.gain.setValueAtTime(0.08,audioCtx.currentTime);g.gain.exponentialRampToValueAtTime(0.001,audioCtx.currentTime+0.3);o.connect(g);g.connect(audioCtx.destination);o.start();o.stop(audioCtx.currentTime+0.3)}}
function showScr(id){document.querySelectorAll('.screen').forEach(s=>s.classList.remove('active'));document.getElementById(id).classList.add('active');G.screen=id}
function goMenu(){showScr('menuScreen');updateAllText();G.paused=false;document.getElementById('pauseScr').classList.remove('show');changeTrack('menu')}
function buildCmds(){var g=document.getElementById('cmdGrid');g.innerHTML='';CMDS.forEach((c,i)=>{var d=document.createElement('div');d.className='cmd-card'+(G.cmd===i?' selected':'');d.innerHTML='<div style="font-size:30px;margin-bottom:10px">'+['💀','😡','🐱','🦉'][i]+'</div><div style="font-weight:bold;color:'+c.color+';margin-bottom:5px">'+t(c.name)+'</div><div style="font-size:12px;color:#666">'+t(c.abName)+'</div>';d.onclick=()=>{G.cmd=i;buildCmds()};g.appendChild(d)})}
function showCmdSelect(){buildCmds();showScr('cmdScreen')}
function startGame(){G.cvs=document.getElementById('gc');G.ctx=G.cvs.getContext('2d');G.mmCtx=document.getElementById('mm').getContext('2d');resize();showScr('gameScreen');initBattle();G.lastT=performance.now();requestAnimationFrame(loop)}
function resize(){G.W=window.innerWidth;G.H=window.innerHeight;G.cvs.width=G.W;G.cvs.height=G.H}
window.addEventListener('resize',resize);
function initBattle(){G.wave=1;G.xp=0;G.lvl=1;G.xpn=100;G.kills=0;G.abCd=0;G.bDmg=0;G.bSpd=0;G.bHp=0;G.gameTime=0;G.stats={k:0,d:0,t:0};G.selWep=0;G.dialogQ=[];G.dialogOn=false;var c=CMDS[G.cmd];var hp=Math.floor(1000*c.hp);G.player={x:G.ww/2,y:G.wh/2,a:0,hp:hp,mhp:hp,spd:3*c.spd,sz:30,ta:0,fcd:[0,0,0,0],torp:10,repT:0,inv:0};G.cam={x:G.ww/2-G.W/2,y:G.wh/2-G.H/2,sx:0,sy:0,sd:0};G.enemies=[];G.proj=[];G.parts=[];G.picks=[];G.islands=[];genIslands();spawnWave()}
function genIslands(){G.islands=[];for(var i=0;i<8;i++){var r=30+Math.random()*70;G.islands.push({x:r+Math.random()*(G.ww-r*2),y:r+Math.random()*(G.wh-r*2),r:r})}}
function spawnWave(){G.enemies=[];var boss=G.wave%5===0;var cnt=Math.min(3+Math.floor(G.wave*1.2),15);if(boss)cnt=cnt-2<2?2:cnt-2;for(var i=0;i<cnt;i++){var tp;if(boss&&i===0)tp=ETYPES[2];else{var rr=Math.random();tp=rr<0.3?ETYPES[0]:rr<0.6?ETYPES[4]:rr<0.8?ETYPES[1]:ETYPES[3]}var hm=1+G.wave*0.1;var ehp=Math.floor(tp.hp*hm*(boss&&i===0?3:1));var ex,ey;var s=Math.floor(Math.random()*4);if(s===0){ex=Math.random()*G.ww;ey=-50}else if(s===1){ex=G.ww+50;ey=Math.random()*G.wh}else if(s===2){ex=Math.random()*G.ww;ey=G.wh+50}else{ex=-50;ey=Math.random()*G.wh}G.enemies.push({x:ex,y:ey,a:0,hp:ehp,mhp:ehp,spd:tp.spd*(0.9+Math.random()*0.2),dmg:Math.floor(tp.dmg*(1+G.wave*0.05)),rate:tp.rate,fcd:Math.random()*tp.rate,rng:tp.rng,sz:tp.sz*(boss&&i===0?1.8:1),col:tp.col,ai:tp.ai,xp:Math.floor(tp.xp*(boss&&i===0?5:1)),ta:0,sd:Math.random()>0.5?1:-1,st:0,isBoss:boss&&i===0})}if(boss)queueDlg('boss');announceWave()}
function announceWave(){var el=document.createElement('div');el.className='explosion-text';el.style.top='40%';el.style.left='50%';el.style.transform='translate(-50%,-50%) scale(1)';el.style.fontSize='60px';el.style.color='#000';el.innerText=t('wave')+' '+G.wave;document.body.appendChild(el);setTimeout(()=>el.remove(),1000)}
function dist(x1,y1,x2,y2){return Math.sqrt((x2-x1)**2+(y2-y1)**2)}
function clamp(v,a,b){return Math.max(a,Math.min(b,v))}
function jit(v){return v+(Math.random()*2-1)}
function queueDlg(key){if(!DLG_LINES[key])return;var lines=DLG_LINES[key];var txt=lines[Math.floor(Math.random()*lines.length)];var names=[t(CMDS[G.cmd].name),'Enemy Commander'];G.dialogQ.push({sp:names[Math.random()>0.3?0:1],txt:txt})}
function advanceDlg(){G.dialogOn=false;document.getElementById('dlgBox').classList.remove('show');if(G.dialogQ.length>0){var d=G.dialogQ.shift();document.getElementById('dlgName').innerText=d.sp;document.getElementById('dlgText').innerText=d.txt;document.getElementById('dlgBox').classList.add('show');G.dialogOn=true}}
function addPart(x,y,vx,vy,life,sz,col,type){G.parts.push({x,y,vx,vy,life,ml:life,sz:Math.max(1,sz),col,type:type||'def',alpha:1})}
function pickFrom(arr){return arr[Math.floor(Math.random()*arr.length)]}
function shake(i){G.cam.sx=(Math.random()-0.5)*i;G.cam.sy=(Math.random()-0.5)*i;G.cam.sd=i}
function addBoom(x,y,pow){pow=pow||1;sfx('boom');for(var i=0;i<15*pow;i++){var a=Math.random()*Math.PI*2;var s=1+Math.random()*4*pow;var c=pickFrom(['#ff0000','#ff9900','#ffff00','#000']);addPart(x,y,Math.cos(a)*s,Math.sin(a)*s,0.5+Math.random()*0.5,3+Math.random()*6,c,'fire')}for(var i=0;i<8*pow;i++){var a2=Math.random()*Math.PI*2;addPart(x,y,Math.cos(a2)*Math.random()*2,Math.sin(a2)*Math.random()*2-1,0.8+Math.random()*0.5,5+Math.random()*8,'#666','smoke')}shake(5*pow);var el=document.createElement('div');el.className='explosion-text';el.style.left=(x-G.cam.x)+'px';el.style.top=(y-G.cam.y)+'px';var txts=['BOOM','POW','BAM','CRASH','PWNED'];el.innerText=txts[Math.floor(Math.random()*txts.length)];document.body.appendChild(el);setTimeout(()=>el.remove(),800)}
function fireWep(wi){var p=G.player;if(!p)return;var w=WEPS[wi];if(p.fcd[wi]>0)return;if(wi===2&&p.torp<=0)return;p.fcd[wi]=w.rate;sfx('shoot');for(var i=0;i<w.count;i++){var sp=(Math.random()-0.5)*w.spread;var a=p.ta+sp;var px=p.x+Math.cos(p.ta)*p.sz;var py=p.y+Math.sin(p.ta)*p.sz;G.proj.push({x:px,y:py,vx:Math.cos(a)*w.spd,vy:Math.sin(a)*w.spd,dmg:Math.floor(w.dmg*(1+G.bDmg)*CMDS[G.cmd].dmg),rng:w.rng,dist:0,own:'p',col:w.col,sz:wi===2?5:2,type:wi===2?'torp':'shell',life:5})}if(wi===2)p.torp--}
function useAbility(){if(G.abCd>0||!G.player)return;G.abCd=G.abMax;sfx('ability');var c=CMDS[G.cmd];switch(c.ab){case'repair':G.player.repT=8;addPart(G.player.x,G.player.y,0,0,2,50,'#00ff00','ab');break;case'sakura':var ex=G.player.x+Math.cos(G.player.ta)*200;var ey=G.player.y+Math.sin(G.player.ta)*200;G.enemies.forEach(e=>{if(dist(e.x,e.y,ex,ey)<200){var d=150*c.dmg;e.hp-=d;addBoom(e.x,e.y,0.5)}});for(var i=0;i<30;i++)addPart(ex+jit(100),ey+jit(100),jit(2),jit(2)-1,1,5,pickFrom(['#ff69b4','#ff1493','#fff']),'sakura');shake(8);break;case'gaster':var ba=G.player.ta;G.enemies.forEach(e=>{var ea=Math.atan2(e.y-G.player.y,e.x-G.player.x);if(Math.abs(ea-ba)<0.4&&dist(e.x,e.y,G.player.x,G.player.y)<800){var d=200*c.dmg;e.hp-=d;addBoom(e.x,e.y,0.7)}});for(var dd=0;dd<800;dd+=15)addPart(G.player.x+Math.cos(ba)*dd,G.player.y+Math.sin(ba)*dd,0,0,0.5,8,'#fff','gaster');shake(12);break;case'thunder':G.enemies.slice(0,4).forEach(e=>{var d=100*c.dmg;e.hp-=d;addBoom(e.x,e.y,0.6);for(var j=0;j<5;j++)addPart(e.x+jit(10),e.y-jit(50),jit(1),1+Math.random()*3,0.3,3,'#ffff00','ltn')});break}}
function updateAI(e,dt){e.st+=dt;var p=G.player;var d=dist(e.x,e.y,p.x,p.y);var atp=Math.atan2(p.y-e.y,p.x-e.x);e.ta=atp;if(e.ai==='aggro'){if(d>e.rng*0.7)e.a=lerp(e.a,atp,2*dt);else e.a=lerp(e.a,atp+1.5*e.sd,dt)}else if(e.ai==='tact'){if(d>e.rng)e.a=lerp(e.a,atp,1.5*dt);else if(d<e.rng*0.4)e.a=lerp(e.a,atp+Math.PI,2*dt);else e.a=lerp(e.a,atp+1.5*e.sd,1.5*dt)}else if(e.ai==='def'){if(d<e.rng*0.5)e.a=lerp(e.a,atp+Math.PI,2*dt);else if(d>e.rng)e.a=lerp(e.a,atp,dt);else e.a=lerp(e.a,atp+1.2*e.sd,dt)}else if(e.ai==='sup'){if(d>e.rng*0.8)e.a=lerp(e.a,atp,dt);else e.a=lerp(e.a,atp+1.5*e.sd,dt)}else{e.a=lerp(e.a,atp,3*dt)}e.x+=Math.cos(e.a)*e.spd*60*dt;e.y+=Math.sin(e.a)*e.spd*60*dt;e.x=clamp(e.x,20,G.ww-20);e.y=clamp(e.y,20,G.wh-20);G.islands.forEach(isl=>{if(dist(e.x,e.y,isl.x,isl.y)<isl.r+e.sz){var pa=Math.atan2(e.y-isl.y,e.x-isl.x);e.x=isl.x+Math.cos(pa)*(isl.r+e.sz+2);e.y=isl.y+Math.sin(pa)*(isl.r+e.sz+2)}});e.fcd-=dt;if(d<e.rng&&e.fcd<=0){e.fcd=e.rate;var a2=atp+(Math.random()-0.5)*0.15;G.proj.push({x:e.x+Math.cos(atp)*e.sz,y:e.y+Math.sin(atp)*e.sz,vx:Math.cos(a2)*8,vy:Math.sin(a2)*8,dmg:e.dmg,rng:e.rng,dist:0,own:'e',col:'#ff0000',sz:2,type:'shell',life:4})}if(e.st>4+Math.random()*3){e.sd*=-1;e.st=0}}
function lerp(a,b,t){return a+(b-a)*t}
function updateProjs(dt){for(var i=G.proj.length-1;i>=0;i--){var p=G.proj[i];p.x+=p.vx*60*dt;p.y+=p.vy*60*dt;p.dist+=Math.sqrt(p.vx*p.vx+p.vy*p.vy)*60*dt;p.life-=dt;if(p.dist>p.rng||p.life<=0){G.proj.splice(i,1);continue}var hit=false;if(p.own==='p'){for(var j=G.enemies.length-1;j>=0;j--){var e=G.enemies[j];if(dist(p.x,p.y,e.x,e.y)<e.sz+p.sz){var dmg=p.dmg;var crit=Math.random()<0.2;if(crit)dmg*=2;e.hp-=dmg;G.stats.d+=dmg;showDmg(e.x,e.y,dmg,crit);addBoom(p.x,p.y,0.2);if(e.hp<=0){G.enemies.splice(j,1);addBoom(e.x,e.y,1.2);G.xp+=e.xp;G.kills++;G.stats.k++;showScore(e.x,e.y,'+'+e.xp+'XP');if(e.isBoss){G.bossDead=true;queueDlg('victory')}if(G.kills===1)queueDlg('firstblood');checkLvl()}hit=true;break}}}else{if(dist(p.x,p.y,G.player.x,G.player.y)<G.player.sz+p.sz){var arm=1-G.bHp*0.03;var dmg=p.dmg*arm;if(G.player.inv>0)dmg=0;G.player.hp-=dmg;G.stats.t+=dmg;addBoom(p.x,p.y,0.2);hit=true;if(G.player.hp<G.player.mhp*0.3&&Math.random()<0.05)queueDlg('lowhp');if(G.player.hp<=0){G.player.hp=0;showDefeat()}}}if(hit){G.proj.splice(i,1)}}}
function showDmg(x,y,amt,crit){var el=document.createElement('div');el.className='dmg-pop';el.style.left=(x-G.cam.x)+'px';el.style.top=(y-G.cam.y)+'px';el.style.color=crit?'#ff0000':'#000';el.style.fontSize=crit?'24px':'16px';el.innerText=(crit?'CRIT! ':'')+Math.round(amt);document.body.appendChild(el);setTimeout(()=>el.remove(),1000)}
function showScore(x,y,txt){var el=document.createElement('div');el.className='dmg-pop';el.style.left=(x-G.cam.x)+'px';el.style.top=(y-G.cam.y-20)+'px';el.style.color='#0000ff';el.innerText=txt;document.body.appendChild(el);setTimeout(()=>el.remove(),1500)}
function checkLvl(){while(G.xp>=G.xpn){G.xp-=G.xpn;G.lvl++;G.xpn=Math.floor(G.xpn*1.5);G.player.mhp+=50;G.player.hp=Math.min(G.player.hp+100,G.player.mhp)}}
function updateParts(dt){for(var i=G.parts.length-1;i>=0;i--){var p=G.parts[i];p.x+=p.vx*60*dt;p.y+=p.vy*60*dt;p.life-=dt;p.alpha=Math.max(0,p.life/p.ml);if(p.type==='smoke'){p.vy-=0.5*dt;p.sz+=3*dt}else if(p.type==='fire'){p.sz*=0.98;p.vy-=1*dt}else if(p.type==='sakura'){p.vy-=0.5*dt;p.vx+=Math.sin(p.life*5)*0.5}else if(p.type==='ltn'){p.vy+=5*dt}if(p.life<=0)G.parts.splice(i,1)}if(G.parts.length>400)G.parts.splice(0,G.parts.length-400)}
function updatePlayer(dt){var p=G.player;if(!p)return;var mx=0,my=0;if(G.keys['w']||G.keys['arrowup'])my=-1;if(G.keys['s']||G.keys['arrowdown'])my=1;if(G.keys['a']||G.keys['arrowleft'])mx=-1;if(G.keys['d']||G.keys['arrowright'])mx=1;if(mx||my){var ma=Math.atan2(my,mx);p.x+=Math.cos(ma)*p.spd*(1+G.bSpd*0.1)*60*dt;p.y+=Math.sin(ma)*p.spd*(1+G.bSpd*0.1)*60*dt;p.a=lerp(p.a,ma,5*dt)}p.x=clamp(p.x,20,G.ww-20);p.y=clamp(p.y,20,G.wh-20);G.islands.forEach(isl=>{if(dist(p.x,p.y,isl.x,isl.y)<isl.r+p.sz){var pa=Math.atan2(p.y-isl.y,p.x-isl.x);p.x=isl.x+Math.cos(pa)*(isl.r+p.sz+2);p.y=isl.y+Math.sin(pa)*(isl.r+p.sz+2)}});p.ta=Math.atan2(G.mouse.wy-p.y,G.mouse.wx-p.x);for(var i=0;i<p.fcd.length;i++){if(p.fcd[i]>0)p.fcd[i]-=dt}if(G.mouse.down&&!G.dialogOn)fireWep(G.selWep);if(G.abCd>0)G.abCd-=dt;if(p.repT>0){p.repT-=dt;p.hp=Math.min(p.mhp,p.hp+p.mhp*0.03*dt)}if(p.inv>0)p.inv-=dt;document.getElementById('abBox').className='ability-box'+(G.abCd<=0?' ready':'');document.getElementById('abBox').innerText=G.abCd>0?G.abCd.toFixed(1):'Q'}
function updateCam(dt){var p=G.player;if(!p)return;G.cam.x=lerp(G.cam.x,p.x-G.W/2,3*dt);G.cam.y=lerp(G.cam.y,p.y-G.H/2,3*dt);if(G.cam.sd>0){G.cam.sx*=0.85;G.cam.sy*=0.85;G.cam.sd*=0.85;if(G.cam.sd<0.1){G.cam.sx=0;G.cam.sy=0;G.cam.sd=0}}}
function checkWave(){if(G.enemies.length===0&&!G.dialogOn&&G.screen==='gameScreen'){G.stats.t=G.gameTime;if(G.wave>=25){showVictory()}else{showUpgrades();G.wave++}}}
function showUpgrades(){G.paused=true;document.getElementById('upgTitle').innerText=t('wclear');var opts=document.getElementById('upgOpts');opts.innerHTML='';var upgs=[{txt:t('upg1'),fn:()=>{G.player.mhp+=100;G.player.hp+=100;G.bHp++}},{txt:t('upg2'),fn:()=>{G.bDmg+=0.1}},{txt:t('upg3'),fn:()=>{G.bSpd+=0.08}},{txt:t('upg4'),fn:()=>{G.player.hp=Math.min(G.player.mhp,G.player.hp+G.player.mhp*0.5)}},{txt:t('upg5'),fn:()=>{G.player.torp+=5}}];var shuffled=upgs.sort(()=>Math.random()-0.5).slice(0,3);shuffled.forEach(u=>{var b=document.createElement('div');b.className='upg-card';b.innerText=u.txt;b.onclick=()=>{u.fn();document.getElementById('upgScr').classList.remove('show');G.paused=false;spawnWave()};opts.appendChild(b)});document.getElementById('upgScr').classList.add('show')}
function showVictory(){var el=document.createElement('div');el.className='explosion-text';el.style.top='50%';el.style.left='50%';el.style.transform='translate(-50%,-50%)';el.style.fontSize='80px';el.style.color='#00ff00';el.innerText=t('victory');document.body.appendChild(el);setTimeout(()=>{el.remove();goMenu()},3000)}
function showDefeat(){var el=document.createElement('div');el.className='explosion-text';el.style.top='50%';el.style.left='50%';el.style.transform='translate(-50%,-50%)';el.style.fontSize='80px';el.style.color='#ff0000';el.innerText=t('defeat');document.body.appendChild(el);setTimeout(()=>{el.remove();goMenu()},3000)}
function render(){var ctx=G.ctx;ctx.clearRect(0,0,G.W,G.H);ctx.fillStyle='#fdfbf7';ctx.fillRect(0,0,G.W,G.H);ctx.strokeStyle='rgba(100,149,237,0.2)';ctx.lineWidth=1;for(var y=0;y<G.H;y+=30){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(G.W,y);ctx.stroke()}G.islands.forEach(isl=>{var sx=isl.x-G.cam.x+G.cam.sx;var sy=isl.y-G.cam.y+G.cam.sy;ctx.fillStyle='#ccffcc';ctx.strokeStyle='#006600';ctx.lineWidth=2;ctx.beginPath();ctx.arc(sx,sy,isl.r,0,Math.PI*2);ctx.fill();ctx.stroke();ctx.font='14px Comic Sans MS';ctx.textAlign='center';ctx.fillText('⛰️',sx,sy+5)});G.picks.forEach(pk=>{var sx=pk.x-G.cam.x+G.cam.sx;var sy=pk.y-G.cam.y+G.cam.sy;ctx.font='20px Comic Sans MS';ctx.textAlign='center';ctx.fillText(pk.type==='hp'?'❤️':pk.type==='torp'?'🐟':'⭐',sx,sy)});G.proj.forEach(p=>{var sx=p.x-G.cam.x+G.cam.sx;var sy=p.y-G.cam.y+G.cam.sy;ctx.fillStyle=p.col;ctx.beginPath();ctx.arc(sx,sy,Math.max(1,p.sz),0,Math.PI*2);ctx.fill();ctx.strokeStyle=p.col;ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(sx,sy);ctx.lineTo(sx-p.vx*0.5,sy-p.vy*0.5);ctx.stroke()});G.enemies.forEach(e=>{drawShip(ctx,e.x,e.y,e.a,e.sz,e.col,false,e.ta,e.isBoss,e.hp,e.mhp)});if(G.player)drawShip(ctx,G.player.x,G.player.y,G.player.a||0,G.player.sz,CMDS[G.cmd].color,true,G.player.ta,false,G.player.hp,G.player.mhp);G.parts.forEach(p=>{var sx=p.x-G.cam.x+G.cam.sx;var sy=p.y-G.cam.y+G.cam.sy;ctx.globalAlpha=p.alpha;ctx.fillStyle=p.col;if(p.type==='fire'||p.type==='def'){ctx.beginPath();ctx.arc(sx,sy,Math.max(0.5,p.sz),0,Math.PI*2);ctx.fill()}else if(p.type==='smoke'){ctx.beginPath();ctx.arc(sx,sy,Math.max(0.5,p.sz),0,Math.PI*2);ctx.fill()}else if(p.type==='sakura'){ctx.fillRect(sx-2,sy-1,4,2)}else if(p.type==='gaster'){ctx.fillRect(sx-4,sy-20,8,40)}else if(p.type==='ltn'){ctx.fillRect(sx-1,sy,2,10)}ctx.globalAlpha=1});var p=G.player;if(p){ctx.strokeStyle='rgba(0,0,0,0.1)';ctx.setLineDash([5,5]);ctx.beginPath();ctx.arc(p.x-G.cam.x+G.cam.sx,p.y-G.cam.y+G.cam.sy,WEPS[G.selWep].rng,0,Math.PI*2);ctx.stroke();ctx.setLineDash([])}drawMinimap()}
function drawShip(ctx,x,y,a,sz,col,isP,ta,isBoss,hp,mhp){var sx=x-G.cam.x+G.cam.sx;var sy=y-G.cam.y+G.cam.sy;if(sx<-100||sx>G.W+100||sy<-100||sy>G.H+100)return;ctx.save();ctx.translate(sx,sy);ctx.rotate(a);ctx.fillStyle=col;ctx.strokeStyle='#000';ctx.lineWidth=2;ctx.lineJoin='round';ctx.lineCap='round';ctx.beginPath();ctx.moveTo(jit(sz),jit(0));ctx.lineTo(jit(-sz*0.7),jit(-sz*0.5));ctx.lineTo(jit(-sz*0.5),jit(0));ctx.lineTo(jit(-sz*0.7),jit(sz*0.5));ctx.closePath();ctx.fill();ctx.stroke();ctx.rotate(-a);ctx.rotate(ta);ctx.fillStyle='#555';ctx.fillRect(-sz*0.3,-sz*0.1,sz*0.9,sz*0.2);ctx.strokeRect(-sz*0.3,-sz*0.1,sz*0.9,sz*0.2);ctx.restore();if(!isP&&isBoss){ctx.strokeStyle='#ff0000';ctx.lineWidth=2;ctx.setLineDash([3,3]);ctx.beginPath();ctx.arc(sx,sy,sz+10,0,Math.PI*2);ctx.stroke();ctx.setLineDash([]);ctx.font='bold 14px Comic Sans MS';ctx.fillStyle='#ff0000';ctx.textAlign='center';ctx.fillText('☠️ BOSS ☠️',sx,sy-sz-25)}var bw=sz*1.8;var bx=sx-bw/2;var by=sy-sz-15;ctx.fillStyle='#fff';ctx.strokeStyle='#000';ctx.lineWidth=1;ctx.fillRect(bx,by,bw,5);ctx.strokeRect(bx,by,bw,5);ctx.fillStyle=hp/mhp>0.5?'#00ff00':hp/mhp>0.25?'#ffff00':'#ff0000';ctx.fillRect(bx,by,bw*(hp/mhp),5)}
function drawMinimap(){var mc=G.mmCtx;var mw=120,mh=90;mc.fillStyle='#fdfbf7';mc.fillRect(0,0,mw,mh);mc.strokeStyle='#000';mc.lineWidth=2;mc.strokeRect(0,0,mw,mh);var sx=mw/G.ww,sy=mh/G.wh;G.islands.forEach(i=>{mc.fillStyle='#ccffcc';mc.beginPath();mc.arc(i.x*sx,i.y*sy,Math.max(2,i.r*sx),0,Math.PI*2);mc.fill()});G.enemies.forEach(e=>{mc.fillStyle='#ff0000';mc.fillRect(e.x*sx-1,e.y*sy-1,3,3)});if(G.player){mc.fillStyle='#0000ff';mc.fillRect(G.player.x*sx-2,G.player.y*sy-2,4,4)}}
function updateHUD(){if(!G.player)return;var p=G.player;var pct=Math.max(0,p.hp/p.mhp*100);document.getElementById('hpFill').style.width=pct+'%';document.getElementById('hpFill').style.background=pct>50?'#00ff00':pct>25?'#ffff00':'#ff0000';document.getElementById('hpTxt').innerText=Math.ceil(p.hp)+'/'+p.mhp;document.getElementById('waveBadge').innerText=t('wave')+' '+G.wave}
var modOkFn=function(){};
function utoa(str){return btoa(unescape(encodeURIComponent(str)))}
function atou(str){return decodeURIComponent(escape(atob(str)))}
function openSave(){document.getElementById('modTitle').innerText=t('load');document.getElementById('modArea').style.display='none';var c=document.getElementById('modContent');c.innerHTML='';var raw=localStorage.getItem('riptide_save');if(raw){var b=document.createElement('button');b.className='flash-btn';b.innerText=t('slot1');b.onclick=()=>{loadGame()};c.appendChild(b)}else{var p=document.createElement('p');p.innerText=t('nosave');c.appendChild(p)}document.getElementById('modBg').classList.add('show');modOkFn=()=>closeMod()}
function openImp(){document.getElementById('modTitle').innerText=t('imp');document.getElementById('modContent').innerHTML='';document.getElementById('modArea').style.display='block';document.getElementById('modArea').value='';document.getElementById('modBg').classList.add('show');modOkFn=()=>{var code=document.getElementById('modArea').value.trim();if(code){try{var raw=atou(code);JSON.parse(raw);localStorage.setItem('riptide_save',raw);loadGame()}catch(e){alert('Invalid code!')}}closeMod()}}
function openExp(){saveGame();document.getElementById('modTitle').innerText=t('export');document.getElementById('modContent').innerHTML='';document.getElementById('modArea').style.display='block';var raw=localStorage.getItem('riptide_save')||'';document.getElementById('modArea').value=utoa(raw);document.getElementById('modBg').classList.add('show');modOkFn=()=>{document.getElementById('modArea').select();document.execCommand('copy');closeMod()}}
function closeMod(){document.getElementById('modBg').classList.remove('show')}
function saveGame(){var data={cmd:G.cmd,wave:G.wave,hp:G.player?G.player.hp:0,mhp:G.player?G.player.mhp:0,torp:G.player?G.player.torp:0,xp:G.xp,lvl:G.lvl,xpn:G.xpn,bDmg:G.bDmg,bSpd:G.bSpd,bHp:G.bHp,kills:G.kills,stats:G.stats,time:G.gameTime,lang:lang};localStorage.setItem('riptide_save',JSON.stringify(data))}
function loadGame(){var raw=localStorage.getItem('riptide_save');if(!raw)return;try{var data=JSON.parse(raw);G.cmd=data.cmd;lang=data.lang||'en';updateAllText();G.cvs=document.getElementById('gc');G.ctx=G.cvs.getContext('2d');G.mmCtx=document.getElementById('mm').getContext('2d');resize();showScr('gameScreen');G.wave=data.wave;G.xp=data.xp;G.lvl=data.lvl;G.xpn=data.xpn;G.bDmg=data.bDmg||0;G.bSpd=data.bSpd||0;G.bHp=data.bHp||0;G.kills=data.kills||0;G.stats=data.stats||{k:0,d:0,t:0};G.gameTime=data.time||0;var c=CMDS[G.cmd];var hp=data.mhp||1000;G.player={x:G.ww/2,y:G.wh/2,a:0,hp:data.hp,mhp:hp,spd:3*c.spd,sz:30,ta:0,fcd:[0,0,0,0],torp:data.torp||10,repT:0,inv:0};G.cam={x:G.ww/2-G.W/2,y:G.wh/2-G.H/2,sx:0,sy:0,sd:0};G.enemies=[];G.proj=[];G.parts=[];G.picks=[];G.islands=[];G.dialogQ=[];G.dialogOn=false;G.paused=false;G.abCd=0;G.selWep=0;genIslands();spawnWave();G.lastT=performance.now();requestAnimationFrame(loop);closeMod()}catch(e){alert('Error loading!')}}
function checkPressure(){if(!G.player||!audioCtx)return;var p=G.player.hp/G.player.mhp;var e=G.enemies.length;var boss=G.enemies.some(en=>en.isBoss);if(boss){changeTrack('boss')}else if(e>5||p<0.4){changeTrack('battle')}else{changeTrack('calm')}}
function loop(time){if(G.screen!=='gameScreen')return;G.dt=Math.min((time-G.lastT)/1000,0.05);G.lastT=time;if(!G.paused&&G.screen==='gameScreen'){G.gameTime+=G.dt;updatePlayer(G.dt);G.enemies.forEach(e=>updateAI(e,G.dt));updateProjs(G.dt);updateParts(G.dt);updateCam(G.dt);checkWave();updateHUD();checkPressure();if(Math.random()<0.002&&!G.dialogOn){var ks=Object.keys(DLG_LINES);queueDlg(ks[Math.floor(Math.random()*ks.length)])}}render();requestAnimationFrame(loop)}
document.addEventListener('keydown',e=>{var k=e.key.toLowerCase();G.keys[k]=true;if(k===' '){e.preventDefault();if(G.dialogOn)advanceDlg();else fireWep(G.selWep)}if(k==='p'&&G.screen==='gameScreen'){G.paused=!G.paused;document.getElementById('pauseScr').classList.toggle('show',G.paused);if(G.paused)changeTrack('menu');else checkPressure()}if(k==='q'&&G.screen==='gameScreen')useAbility();if(k>='1'&&k<='4')G.selWep=parseInt(k)-1});
document.addEventListener('keyup',e=>{G.keys[e.key.toLowerCase()]=false});
document.addEventListener('mousemove',e=>{G.mouse.x=e.clientX;G.mouse.y=e.clientY;G.mouse.wx=e.clientX+G.cam.x;G.mouse.wy=e.clientY+G.cam.y});
document.addEventListener('mousedown',e=>{if(e.button===0)G.mouse.down=true});
document.addEventListener('mouseup',e=>{if(e.button===0)G.mouse.down=false});
document.addEventListener('contextmenu',e=>e.preventDefault());
document.body.addEventListener('click',function(){if(!audioCtx){initAudio()}},{once:true});
updateAllText();
</script>
</body>
</html>