Reaperxxxx commited on
Commit
087ba8a
Β·
verified Β·
1 Parent(s): 8287b73

Update public/index.html

Browse files
Files changed (1) hide show
  1. public/index.html +62 -94
public/index.html CHANGED
@@ -290,8 +290,9 @@
290
  let characters = [];
291
  let activeIndex = -1;
292
  let sessionToken = null;
293
- let originalRaw = '';
294
 
 
 
295
  const STAT_MAP = {
296
  civPower: 9, civDef: 10, civSpd: 11,
297
  supPower: 13, supDef: 14, supSpd: 15,
@@ -328,21 +329,17 @@ fetch('/download-link').then(r => r.json()).then(data => {
328
  }).catch(() => {});
329
 
330
  // ── AUTO-INIT ──────────────────────────────────────────────────────
331
- const LS_TOKEN_KEY = 'supercity_session_token';
332
- const LS_RAW_KEY = 'supercity_raw';
333
- const LS_EDITED_KEY = 'supercity_edited';
334
 
335
  (function initFromServer() {
336
  const wrap = document.getElementById('progressWrap');
337
  const bar = document.getElementById('progressBar');
338
  const label = document.getElementById('progressLabel');
339
 
340
- // Try to restore a previous session from localStorage
341
  const savedToken = localStorage.getItem(LS_TOKEN_KEY);
342
- const savedRaw = localStorage.getItem(LS_RAW_KEY);
343
 
344
- if (savedToken && savedRaw) {
345
- // Verify the session is still alive on the server
346
  label.textContent = 'Resuming session…';
347
  wrap.style.display = 'block';
348
  bar.style.width = '50%';
@@ -351,32 +348,22 @@ const LS_EDITED_KEY = 'supercity_edited';
351
  .then(r => r.json())
352
  .then(data => {
353
  if (data.alive) {
354
- bar.style.width = '100%';
355
  sessionToken = savedToken;
356
- originalRaw = savedRaw;
357
- parseCharacters(savedRaw);
358
-
359
- // Restore edits if any were saved
360
- const savedEdited = localStorage.getItem(LS_EDITED_KEY);
361
- if (savedEdited) {
362
- const editedFields = savedEdited.split('|');
363
- characters.forEach(ch => {
364
- ch.fields = editedFields.slice(ch.startIdx, ch.startIdx + ch.fields.length);
365
- });
366
- }
367
-
368
- wrap.style.display = 'none';
369
- document.getElementById('fileInfoBar').style.display = 'flex';
370
- document.getElementById('fileInfoBar').innerHTML = `
371
- <div class="file-info">β™» <span class="file-info-name">Session restored</span>
372
- &nbsp;Β·&nbsp; ${characters.length} characters loaded${savedEdited ? ' &nbsp;Β·&nbsp; <b style="color:var(--accent2)">edits recovered</b>' : ''}</div>`;
373
- document.getElementById('editorLayout').style.display = 'grid';
374
- setStep(savedEdited ? 3 : 2);
375
- showToast(savedEdited ? 'βœ” Session + edits restored β€” ready to export' : 'βœ” Session restored');
376
  } else {
377
  localStorage.removeItem(LS_TOKEN_KEY);
378
- localStorage.removeItem(LS_RAW_KEY);
379
- loadFresh(wrap, bar, label);
380
  }
381
  })
382
  .catch(() => loadFresh(wrap, bar, label));
@@ -392,12 +379,13 @@ const LS_EDITED_KEY = 'supercity_edited';
392
  fetch('/init')
393
  .then(r => { if (!r.ok) return r.json().then(e => { throw new Error(e.error); }); return r.json(); })
394
  .then(data => {
395
- bar.style.width = '100%';
396
  sessionToken = data.token;
397
- originalRaw = data.characters;
398
  localStorage.setItem(LS_TOKEN_KEY, data.token);
399
- localStorage.setItem(LS_RAW_KEY, data.characters);
400
- parseCharacters(data.characters);
 
 
 
401
  wrap.style.display = 'none';
402
  document.getElementById('fileInfoBar').style.display = 'flex';
403
  document.getElementById('fileInfoBar').innerHTML = `
@@ -414,41 +402,15 @@ const LS_EDITED_KEY = 'supercity_edited';
414
  }
415
  })();
416
 
417
- // ── PARSE ──────────────────────────────────────────────────────────
418
- function parseCharacters(raw) {
419
- characters = [];
420
- const allFields = raw.split('|');
421
-
422
- function isNameToken(s) {
423
- s = s.trim();
424
- return s.length > 1 && /[A-Za-z]/.test(s) && !/^-?[\d.]+$/.test(s);
425
- }
426
- function isNumeric(s) { return /^-?[\d.]+$/.test(s.trim()); }
427
-
428
- const boundaries = [];
429
- for (let i = 1; i < allFields.length - 1; i++) {
430
- if (isNameToken(allFields[i]) && isNumeric(allFields[i + 1]) && isNameToken(allFields[i - 1])) {
431
- boundaries.push(i);
432
- }
433
- }
434
-
435
- for (let b = 0; b < boundaries.length; b++) {
436
- const start = boundaries[b];
437
- const end = b + 1 < boundaries.length ? boundaries[b + 1] - 1 : allFields.length;
438
- const fields = allFields.slice(start, end);
439
- characters.push({
440
- index: b,
441
- name: allFields[start - 1].trim(),
442
- alias: fields[0].trim(),
443
- fields: fields,
444
- original: [...fields],
445
- startIdx: start,
446
- endIdx: end,
447
  });
448
- }
449
-
450
- document.getElementById('charCount').textContent = characters.length;
451
- renderList(characters);
452
  }
453
 
454
  // ── LIST ───────────────────────────────────────────────────────────
@@ -524,45 +486,51 @@ function saveCharacter() {
524
  if (activeIndex < 0) return;
525
  const ch = characters[activeIndex];
526
  let valid = true;
 
 
527
  Object.keys(STAT_MAP).forEach(key => {
528
  const v = parseInt(document.getElementById(key).value, 10);
529
  if (isNaN(v) || v < 0 || v > 99999) { valid = false; document.getElementById(key).classList.add('invalid'); return; }
 
 
530
  ch.fields[STAT_MAP[key]] = String(v);
531
  updateBar(key, v);
532
  });
 
533
  if (!valid) { showToast('Fix invalid values first', true); return; }
534
- document.getElementById('unsavedBadge').style.display = 'none';
535
- showToast(`βœ” ${ch.name} saved`);
536
- setStep(3);
537
- persistEdits();
538
- }
539
 
540
- function persistEdits() {
541
- // Rebuild current edited raw and save to localStorage
542
- const allFields = originalRaw.split('|');
543
- characters.forEach(ch => {
544
- ch.fields.forEach((val, j) => { allFields[ch.startIdx + j] = val; });
545
- });
546
- localStorage.setItem(LS_EDITED_KEY, allFields.join('|'));
 
 
 
 
 
 
547
  }
548
 
549
  function resetCharacter() {
550
  if (activeIndex < 0) return;
551
- characters[activeIndex].fields = [...characters[activeIndex].original];
552
- selectChar(activeIndex);
553
- showToast('β†Ί Reset to original');
 
 
 
 
 
 
 
554
  }
555
 
556
  // ── EXPORT ─────────────────────────────────────────────────────────
557
  function exportAPK() {
558
- if (!sessionToken) { showToast('No APK session β€” re-upload', true); return; }
559
-
560
- // Rebuild raw text from all character fields
561
- const allFields = originalRaw.split('|');
562
- characters.forEach(ch => {
563
- ch.fields.forEach((val, j) => { allFields[ch.startIdx + j] = val; });
564
- });
565
- const rebuiltChars = allFields.join('|');
566
 
567
  const btn = document.getElementById('exportBtn');
568
  btn.disabled = true;
@@ -572,7 +540,7 @@ function exportAPK() {
572
  fetch('/patch', {
573
  method: 'POST',
574
  headers: { 'Content-Type': 'application/json' },
575
- body: JSON.stringify({ token: sessionToken, characters: rebuiltChars }),
576
  })
577
  .then(res => {
578
  if (!res.ok) return res.json().then(e => { throw new Error(e.error); });
@@ -583,7 +551,7 @@ function exportAPK() {
583
  btn.textContent = '⬇ Export Patched APK';
584
  showToast('βœ” Patched β€” click the download link');
585
  setStep(3);
586
- // Show native browser download prompt
587
  const a = document.createElement('a');
588
  a.href = data.url;
589
  a.download = 'supercity_modded.apk';
 
290
  let characters = [];
291
  let activeIndex = -1;
292
  let sessionToken = null;
 
293
 
294
+ // Field indices within each character's fields[] array (server-side record)
295
+ // fields[0]=name, fields[1]=alias, numeric stats follow
296
  const STAT_MAP = {
297
  civPower: 9, civDef: 10, civSpd: 11,
298
  supPower: 13, supDef: 14, supSpd: 15,
 
329
  }).catch(() => {});
330
 
331
  // ── AUTO-INIT ──────────────────────────────────────────────────────
332
+ const LS_TOKEN_KEY = 'supercity_session_token';
 
 
333
 
334
  (function initFromServer() {
335
  const wrap = document.getElementById('progressWrap');
336
  const bar = document.getElementById('progressBar');
337
  const label = document.getElementById('progressLabel');
338
 
339
+ // Try to restore a previous session from the server
340
  const savedToken = localStorage.getItem(LS_TOKEN_KEY);
 
341
 
342
+ if (savedToken) {
 
343
  label.textContent = 'Resuming session…';
344
  wrap.style.display = 'block';
345
  bar.style.width = '50%';
 
348
  .then(r => r.json())
349
  .then(data => {
350
  if (data.alive) {
 
351
  sessionToken = savedToken;
352
+ bar.style.width = '80%';
353
+ return loadCharacters(savedToken).then(() => {
354
+ bar.style.width = '100%';
355
+ wrap.style.display = 'none';
356
+ document.getElementById('fileInfoBar').style.display = 'flex';
357
+ document.getElementById('fileInfoBar').innerHTML = `
358
+ <div class="file-info">β™» <span class="file-info-name">Session restored</span>
359
+ &nbsp;Β·&nbsp; ${characters.length} characters loaded</div>`;
360
+ document.getElementById('editorLayout').style.display = 'grid';
361
+ setStep(2);
362
+ showToast('βœ” Session restored');
363
+ });
 
 
 
 
 
 
 
 
364
  } else {
365
  localStorage.removeItem(LS_TOKEN_KEY);
366
+ return loadFresh(wrap, bar, label);
 
367
  }
368
  })
369
  .catch(() => loadFresh(wrap, bar, label));
 
379
  fetch('/init')
380
  .then(r => { if (!r.ok) return r.json().then(e => { throw new Error(e.error); }); return r.json(); })
381
  .then(data => {
 
382
  sessionToken = data.token;
 
383
  localStorage.setItem(LS_TOKEN_KEY, data.token);
384
+ bar.style.width = '70%';
385
+ return loadCharacters(data.token);
386
+ })
387
+ .then(() => {
388
+ bar.style.width = '100%';
389
  wrap.style.display = 'none';
390
  document.getElementById('fileInfoBar').style.display = 'flex';
391
  document.getElementById('fileInfoBar').innerHTML = `
 
402
  }
403
  })();
404
 
405
+ // ── LOAD CHARACTERS FROM SERVER ────────────────────────────────────
406
+ function loadCharacters(token) {
407
+ return fetch('/characters/' + token)
408
+ .then(r => { if (!r.ok) return r.json().then(e => { throw new Error(e.error); }); return r.json(); })
409
+ .then(data => {
410
+ characters = data.characters;
411
+ document.getElementById('charCount').textContent = characters.length;
412
+ renderList(characters);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
413
  });
 
 
 
 
414
  }
415
 
416
  // ── LIST ───────────────────────────────────────────────────────────
 
486
  if (activeIndex < 0) return;
487
  const ch = characters[activeIndex];
488
  let valid = true;
489
+ const edits = [];
490
+
491
  Object.keys(STAT_MAP).forEach(key => {
492
  const v = parseInt(document.getElementById(key).value, 10);
493
  if (isNaN(v) || v < 0 || v > 99999) { valid = false; document.getElementById(key).classList.add('invalid'); return; }
494
+ edits.push({ characterIndex: ch.index, fieldIndex: STAT_MAP[key], value: String(v) });
495
+ // Update local cache too
496
  ch.fields[STAT_MAP[key]] = String(v);
497
  updateBar(key, v);
498
  });
499
+
500
  if (!valid) { showToast('Fix invalid values first', true); return; }
 
 
 
 
 
501
 
502
+ // Persist edits server-side via /edit-bulk
503
+ fetch('/edit-bulk', {
504
+ method: 'POST',
505
+ headers: { 'Content-Type': 'application/json' },
506
+ body: JSON.stringify({ token: sessionToken, edits }),
507
+ })
508
+ .then(r => { if (!r.ok) return r.json().then(e => { throw new Error(e.error); }); return r.json(); })
509
+ .then(() => {
510
+ document.getElementById('unsavedBadge').style.display = 'none';
511
+ showToast(`βœ” ${ch.name} saved`);
512
+ setStep(3);
513
+ })
514
+ .catch(err => showToast('Save failed: ' + err.message, true));
515
  }
516
 
517
  function resetCharacter() {
518
  if (activeIndex < 0) return;
519
+ // Ask server to reset characters.txt to original APK state
520
+ fetch('/reset/' + sessionToken, { method: 'POST' })
521
+ .then(r => { if (!r.ok) return r.json().then(e => { throw new Error(e.error); }); return r.json(); })
522
+ .then(() => loadCharacters(sessionToken))
523
+ .then(() => {
524
+ // Re-select current character to refresh UI
525
+ if (activeIndex < characters.length) selectChar(activeIndex);
526
+ showToast('β†Ί Reset all to original');
527
+ })
528
+ .catch(err => showToast('Reset failed: ' + err.message, true));
529
  }
530
 
531
  // ── EXPORT ─────────────────────────────────────────────────────────
532
  function exportAPK() {
533
+ if (!sessionToken) { showToast('No APK session β€” reload page', true); return; }
 
 
 
 
 
 
 
534
 
535
  const btn = document.getElementById('exportBtn');
536
  btn.disabled = true;
 
540
  fetch('/patch', {
541
  method: 'POST',
542
  headers: { 'Content-Type': 'application/json' },
543
+ body: JSON.stringify({ token: sessionToken }),
544
  })
545
  .then(res => {
546
  if (!res.ok) return res.json().then(e => { throw new Error(e.error); });
 
551
  btn.textContent = '⬇ Export Patched APK';
552
  showToast('βœ” Patched β€” click the download link');
553
  setStep(3);
554
+ // Trigger browser download
555
  const a = document.createElement('a');
556
  a.href = data.url;
557
  a.download = 'supercity_modded.apk';