Diablo2D — Shadows of Tristram: complete browser ARPG

- Isometric canvas renderer (depth-sorted, FOV/fog, additive lighting)
- 3 classes x 20 skills, 4 acts x 4 floors + boss lairs, torment I-X
- Diablo-style loot: rarities, affix tiers, 14 legendaries, vendor, stash
- Rogue camp with 6 NPCs: Charsi/Akara/Kashya/Cain/Gheed/storage
- NPC quest chain (accept -> hunt -> turn in) with rewards & gating
- Procedural WebAudio SFX + generative music, EN/VI localization
- Saves, settings, waypoints, hardcore mode, PWA manifest
- 93-assertion headless suite + browser E2E via CDP
This commit is contained in:
2026-08-23 06:59:36 +00:00
commit fc1fa2d51e
42 changed files with 11784 additions and 0 deletions
+128
View File
@@ -0,0 +1,128 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Diablo2D — Shadows of Tristram</title>
<meta name="description" content="A dark isometric action-RPG. Descend procedurally generated dungeons, slay elites and bosses, collect legendary loot. Playable fully offline in your browser.">
<meta property="og:title" content="Diablo2D — Shadows of Tristram">
<meta property="og:description" content="Dark isometric action-RPG in your browser. Procedural dungeons, elite affixes, legendary loot, 3 classes.">
<meta name="theme-color" content="#0b0908">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='12' fill='%230b0908'/%3E%3Cpath d='M32 10 L52 26 L46 54 L18 54 L12 26 Z' fill='%23a32c22'/%3E%3Cpath d='M32 20 L42 28 L38 46 L26 46 L22 28 Z' fill='%23e9c87b'/%3E%3C/svg%3E">
<link rel="manifest" href="manifest.webmanifest">
<link rel="apple-touch-icon" href="icons/icon-180.png">
<meta name="mobile-web-app-capable" content="yes">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div id="app">
<canvas id="game-canvas"></canvas>
<!-- ======== IN-GAME HUD (DOM over canvas) ======== -->
<div id="hud" class="hidden">
<div id="quest-tracker" class="hud-corner"></div>
<div id="minimap-box" class="hud-panel">
<canvas id="minimap" width="216" height="150"></canvas>
<div id="minimap-label"></div>
</div>
<div id="buff-bar"></div>
<div id="bottom-hud">
<div id="orb-life" class="orb" title="">
<div class="orb-fill life" id="life-fill"></div>
<div class="orb-gloss"></div>
<div class="orb-text" id="life-text">0/0</div>
</div>
<div id="center-hud">
<div id="xp-bar-wrap"><div id="xp-bar"></div><div id="xp-text"></div></div>
<div id="hotbar">
<div class="hotbar-group" id="hb-skills"></div>
<div class="hotbar-sep"></div>
<div class="hotbar-group" id="hb-potions"></div>
<div class="hotbar-sep"></div>
<div class="hotbar-group small" id="hb-menus"></div>
</div>
<div id="level-pill"><span id="lvl-num">1</span></div>
</div>
<div id="orb-mana" class="orb">
<div class="orb-fill mana" id="mana-fill"></div>
<div class="orb-gloss"></div>
<div class="orb-text" id="mana-text">0/0</div>
</div>
</div>
<div id="toasts"></div>
<div id="boss-bar-wrap" class="hidden">
<div id="boss-name"></div>
<div id="boss-bar"><div id="boss-fill"></div></div>
</div>
</div>
<!-- ======== FLOATING PANELS ======== -->
<div id="panels"></div>
<div id="tooltip" class="hidden"></div>
<div id="drag-ghost" class="hidden"></div>
<!-- ======== FULL SCREENS ======== -->
<div id="screens">
<div id="screen-loading" class="screen">
<div class="loading-inner">
<h1 class="game-logo">DIABLO<span>2D</span></h1>
<div class="loading-sub">Shadows of Tristram</div>
<div class="loading-bar"><div id="loading-progress"></div></div>
<div id="loading-tip"></div>
</div>
</div>
<div id="screen-title" class="screen hidden"></div>
<div id="screen-classselect" class="screen hidden"></div>
<div id="screen-settings" class="screen hidden"></div>
<div id="screen-help" class="screen hidden"></div>
<div id="screen-death" class="screen hidden"></div>
<div id="screen-victory" class="screen hidden"></div>
</div>
<!-- ======== PAUSE OVERLAY ======== -->
<div id="pause-overlay" class="hidden">
<div class="pause-menu panel-gothic">
<h2 data-i18n="menu.paused">Paused</h2>
<button class="btn btn-primary" id="btn-resume" data-i18n="menu.resume">Resume</button>
<button class="btn" id="btn-pause-settings" data-i18n="menu.settings">Settings</button>
<button class="btn" id="btn-pause-help" data-i18n="menu.help">Help</button>
<button class="btn btn-danger" id="btn-save-exit" data-i18n="menu.saveexit">Save &amp; Exit to Title</button>
</div>
</div>
</div>
<!-- Core -->
<script src="js/core/util.js"></script>
<script src="js/core/i18n.js"></script>
<script src="js/core/save.js"></script>
<script src="js/core/input.js"></script>
<script src="js/core/audio.js"></script>
<!-- Data -->
<script src="js/data/balance.js"></script>
<script src="js/data/items.js"></script>
<script src="js/data/monsters.js"></script>
<script src="js/data/skills.js"></script>
<!-- Simulation -->
<script src="js/game/path.js"></script>
<script src="js/game/fov.js"></script>
<script src="js/game/world.js"></script>
<script src="js/game/entities.js"></script>
<script src="js/game/ai.js"></script>
<script src="js/game/combat.js"></script>
<script src="js/game/loot.js"></script>
<script src="js/game/player.js"></script>
<!-- Presentation -->
<script src="js/render/sprites.js"></script>
<script src="js/render/render.js"></script>
<script src="js/ui/hud.js"></script>
<script src="js/ui/panels.js"></script>
<script src="js/ui/screens.js"></script>
<!-- State machine & bootstrap -->
<script src="js/game/game.js"></script>
<script src="js/main.js"></script>
</body>
</html>