alexrsworld 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Apps/30dollarwebsite.html +403 -0
- package/Apps/emulatorjs.html +258 -0
- package/Apps/ruffle.html +93 -0
- package/Apps/soundboard.html +4 -0
- package/Games/WAflash/connect4.html +87 -0
- package/Games/WAflash/dealornodeal.html +89 -0
- package/Games/WAflash/escapingtheprison.html +87 -0
- package/Games/WAflash/fancypants2.html +86 -0
- package/Games/WAflash/freegear.html +87 -0
- package/Games/WAflash/learntoflyidle.html +87 -0
- package/Games/WAflash/papascoop.html +88 -0
- package/Games/WAflash/papashotdog.html +88 -0
- package/Games/WAflash/papaspancake.html +89 -0
- package/Games/WAflash/papaswingeria.html +87 -0
- package/Games/WAflash/pottyracers3.html +86 -0
- package/Games/WAflash/run.html +88 -0
- package/Games/WAflash/run2.html +88 -0
- package/Games/WAflash/shoppingcarthero2.html +88 -0
- package/Games/WAflash/sonicultimate.html +87 -0
- package/Games/WAflash/sugarsugar.html +91 -0
- package/Games/WAflash/vex.html +91 -0
- package/Games/WAflash/worldshardestgame3.html +89 -0
- package/Games/emulated/GBA/emeraldversion.html +30 -0
- package/Games/emulated/GBA/fireredversion.html +30 -0
- package/Games/emulated/GBA/leafgreenversion.html +30 -0
- package/Games/emulated/GBA/rubyversion.html +30 -0
- package/Games/emulated/GBA/sapphireversion.html +30 -0
- package/Games/emulated/N64/mariokart64.html +27 -0
- package/Games/emulated/N64/supermario64.html +30 -0
- package/Games/emulated/SNES/earthbound.html +31 -0
- package/Games/ruffle/MahjongTower.html +40 -0
- package/Games/ruffle/QWOP.html +41 -0
- package/Games/ruffle/achievementunlocked.html +41 -0
- package/Games/ruffle/achievementunlocked2.html +41 -0
- package/Games/ruffle/achievementunlocked3.html +41 -0
- package/Games/ruffle/ageofdefense.html +41 -0
- package/Games/ruffle/ageofwar.html +41 -0
- package/Games/ruffle/ageofwar2.html +41 -0
- package/Games/ruffle/airship.html +41 -0
- package/Games/ruffle/alien.html +41 -0
- package/Games/ruffle/angrybirdshalloween.html +40 -0
- package/Games/ruffle/appleshooter.html +41 -0
- package/Games/ruffle/axisfootballleague.html +40 -0
- package/Games/ruffle/badpiggies.html +41 -0
- package/Games/ruffle/bejeweledtwist.html +41 -0
- package/Games/ruffle/blinding.html +40 -0
- package/Games/ruffle/bloons.html +41 -0
- package/Games/ruffle/bloonsupermonkey.html +40 -0
- package/Games/ruffle/bloxorz.html +40 -0
- package/Games/ruffle/bobtherobber.html +41 -0
- package/Games/ruffle/bowling.html +40 -0
- package/Games/ruffle/breakingthebank.html +40 -0
- package/Games/ruffle/btd.html +40 -0
- package/Games/ruffle/btd2.html +40 -0
- package/Games/ruffle/btd3.html +41 -0
- package/Games/ruffle/btd4.html +41 -0
- package/Games/ruffle/btd4expansion.html +41 -0
- package/Games/ruffle/btd5.html +41 -0
- package/Games/ruffle/bubbleshooter.html +41 -0
- package/Games/ruffle/burgerrun.html +41 -0
- package/Games/ruffle/burritobisonrevenge.html +41 -0
- package/Games/ruffle/crimsonroom.html +41 -0
- package/Games/ruffle/cubefield.html +40 -0
- package/Games/ruffle/curveball.html +41 -0
- package/Games/ruffle/deadzed2.html +40 -0
- package/Games/ruffle/dinerdash.html +41 -0
- package/Games/ruffle/donkeykong.html +40 -0
- package/Games/ruffle/douchebagworkout.html +40 -0
- package/Games/ruffle/douchebagworkout2.html +40 -0
- package/Games/ruffle/ducklife.html +40 -0
- package/Games/ruffle/ducklife2.html +40 -0
- package/Games/ruffle/ducklife3.html +40 -0
- package/Games/ruffle/ducklife4.html +40 -0
- package/Games/ruffle/electricman.html +41 -0
- package/Games/ruffle/electricman2.html +41 -0
- package/Games/ruffle/factoryballs.html +42 -0
- package/Games/ruffle/factoryballs2.html +42 -0
- package/Games/ruffle/factoryballs3.html +41 -0
- package/Games/ruffle/factoryballs4.html +41 -0
- package/Games/ruffle/fancypants.html +41 -0
- package/Games/ruffle/fancypants3.html +41 -0
- package/Games/ruffle/fleeingthecomplex.html +41 -0
- package/Games/ruffle/fltron.html +40 -0
- package/Games/ruffle/foresttemple.html +40 -0
- package/Games/ruffle/frogger.html +40 -0
- package/Games/ruffle/frontlinedefense.html +40 -0
- package/Games/ruffle/galagaflash.html +41 -0
- package/Games/ruffle/gravitee.html +41 -0
- package/Games/ruffle/gravitee2.html +41 -0
- package/Games/ruffle/growcube.html +41 -0
- package/Games/ruffle/growisland.html +41 -0
- package/Games/ruffle/growvalley.html +41 -0
- package/Games/ruffle/impossiblequiz.html +41 -0
- package/Games/ruffle/impossiblequiz2.html +41 -0
- package/Games/ruffle/injustice.html +41 -0
- package/Games/ruffle/isoball.html +41 -0
- package/Games/ruffle/johnnyupgrade.html +41 -0
- package/Games/ruffle/jumpingfinn.html +40 -0
- package/Games/ruffle/kaboomz.html +40 -0
- package/Games/ruffle/leaguebowling.html +41 -0
- package/Games/ruffle/learntofly.html +40 -0
- package/Games/ruffle/learntofly2.html +40 -0
- package/Games/ruffle/learntofly3.html +40 -0
- package/Games/ruffle/lemonade.html +40 -0
- package/Games/ruffle/madness.html +40 -0
- package/Games/ruffle/madnessa.html +40 -0
- package/Games/ruffle/mahjonggardens.html +40 -0
- package/Games/ruffle/mahjongtitans.html +40 -0
- package/Games/ruffle/mariocombat.html +40 -0
- package/Games/ruffle/minecrafttowerdefense.html +41 -0
- package/Games/ruffle/minecrafttowerdefense2.html +41 -0
- package/Games/ruffle/motherload.html +40 -0
- package/Games/ruffle/murder.html +41 -0
- package/Games/ruffle/myfriendpedro.html +40 -0
- package/Games/ruffle/neonrider.html +41 -0
- package/Games/ruffle/nyancatlostinspace.html +40 -0
- package/Games/ruffle/pacman.html +40 -0
- package/Games/ruffle/pacxon.html +41 -0
- package/Games/ruffle/pacxondeluxe.html +41 -0
- package/Games/ruffle/pandemic2.html +41 -0
- package/Games/ruffle/papas sushi.html +40 -0
- package/Games/ruffle/papasbake.html +40 -0
- package/Games/ruffle/papasburgeria.html +41 -0
- package/Games/ruffle/papascheese.html +40 -0
- package/Games/ruffle/papascupcake.html +40 -0
- package/Games/ruffle/papasdonut.html +40 -0
- package/Games/ruffle/papaspasta.html +42 -0
- package/Games/ruffle/papaspizza.html +41 -0
- package/Games/ruffle/papastaco.html +42 -0
- package/Games/ruffle/plantsvszombies.html +41 -0
- package/Games/ruffle/poppit.html +42 -0
- package/Games/ruffle/portaltheflashversion.html +42 -0
- package/Games/ruffle/pottyracers.html +40 -0
- package/Games/ruffle/pottyracers2.html +40 -0
- package/Games/ruffle/raftwars.html +40 -0
- package/Games/ruffle/redball.html +42 -0
- package/Games/ruffle/redball2.html +41 -0
- package/Games/ruffle/redball3.html +41 -0
- package/Games/ruffle/riddleschool.html +41 -0
- package/Games/ruffle/riddleschool2.html +41 -0
- package/Games/ruffle/riddleschool3.html +41 -0
- package/Games/ruffle/riddleschool4.html +41 -0
- package/Games/ruffle/riddleschool5.html +41 -0
- package/Games/ruffle/riddletransfer.html +41 -0
- package/Games/ruffle/riddletransfer2.html +41 -0
- package/Games/ruffle/rollercoaster.html +41 -0
- package/Games/ruffle/shoppingcarthero.html +41 -0
- package/Games/ruffle/spaceiskey.html +41 -0
- package/Games/ruffle/spaceiskey2.html +41 -0
- package/Games/ruffle/sprinter.html +41 -0
- package/Games/ruffle/stealingthediamond.html +41 -0
- package/Games/ruffle/stickwar.html +41 -0
- package/Games/ruffle/strikeforceheros.html +40 -0
- package/Games/ruffle/strikeforceheros2.html +40 -0
- package/Games/ruffle/strikeforcekitty.html +41 -0
- package/Games/ruffle/sugarsugar2.html +41 -0
- package/Games/ruffle/superfighters.html +41 -0
- package/Games/ruffle/supermarioflash.html +41 -0
- package/Games/ruffle/supermarioflash2.html +41 -0
- package/Games/ruffle/supermarioflash3.html +41 -0
- package/Games/ruffle/supersmashflash.html +41 -0
- package/Games/ruffle/tactical.html +41 -0
- package/Games/ruffle/tactical2.html +41 -0
- package/Games/ruffle/tacticaloriginal.html +41 -0
- package/Games/ruffle/territorywar.html +41 -0
- package/Games/ruffle/tetrix2.html +41 -0
- package/Games/ruffle/thefightforglorton.html +41 -0
- package/Games/ruffle/thinice.html +41 -0
- package/Games/ruffle/treasurehunt.html +40 -0
- package/Games/ruffle/unfairmario.html +40 -0
- package/Games/ruffle/vex2.html +41 -0
- package/Games/ruffle/whenpizzaattacks.html +41 -0
- package/Games/ruffle/whg.html +40 -0
- package/Games/ruffle/whg2.html +40 -0
- package/Games/ruffle/whg4.html +40 -0
- package/Games/ruffle/yahootennis.html +40 -0
- package/Games/ruffle/zombocalypse.html +41 -0
- package/Games/singlefile.html +67 -0
- package/Games/sonic-games/sonicthehedgehog.html +29 -0
- package/Games/sonic-games/sonicthehedgehog2.html +31 -0
- package/Games/sonic-games/sonicthehedgehog3.html +23 -0
- package/Games/standalone/10minutestilldawn.html +85 -0
- package/Games/standalone/1v1lol.html +299 -0
- package/Games/standalone/2048.html +129 -0
- package/Games/standalone/2048cupcakes.html +135 -0
- package/Games/standalone/Angry Birds.html +20 -0
- package/Games/standalone/BlockPost.html +36 -0
- package/Games/standalone/BuildNow.gg.html +136 -0
- package/Games/standalone/Flappy Dunk.html +169 -0
- package/Games/standalone/Granny 2.html +202 -0
- package/Games/standalone/Time Shooter 2.html +94 -0
- package/Games/standalone/adventure.html +128 -0
- package/Games/standalone/angrybirdsshowdown.html +24 -0
- package/Games/standalone/badicecream.html +163 -0
- package/Games/standalone/badicecream2.html +165 -0
- package/Games/standalone/badicecream3.html +165 -0
- package/Games/standalone/badtime.html +145 -0
- package/Games/standalone/baldi.html +26 -0
- package/Games/standalone/basketrandom.html +46 -0
- package/Games/standalone/bigtower.html +65 -0
- package/Games/standalone/bigtower2.html +20 -0
- package/Games/standalone/bitlife.html +22 -0
- package/Games/standalone/blockblast.html +94 -0
- package/Games/standalone/blockthepig.html +142 -0
- package/Games/standalone/bowmasters.html +27 -0
- package/Games/standalone/boxingrandom.html +0 -0
- package/Games/standalone/candycrush.html +67 -0
- package/Games/standalone/carssimulator.html +40 -0
- package/Games/standalone/caseclicker.html +310 -0
- package/Games/standalone/cellmachine.html +24 -0
- package/Games/standalone/choppyorc.html +210 -0
- package/Games/standalone/circleo.html +99 -0
- package/Games/standalone/clusterrush.html +47 -0
- package/Games/standalone/colorswitch.html +224 -0
- package/Games/standalone/colortunnel.html +24 -0
- package/Games/standalone/cookieclicker.html +150 -0
- package/Games/standalone/crazycattle3d.html +246 -0
- package/Games/standalone/crossyroad.html +23 -0
- package/Games/standalone/cyberpunkracing.html +37 -0
- package/Games/standalone/dadish.html +46 -0
- package/Games/standalone/dadish2.html +72 -0
- package/Games/standalone/deathrun3d.html +69 -0
- package/Games/standalone/dogeminer.html +996 -0
- package/Games/standalone/dragonvsbricks.html +48 -0
- package/Games/standalone/driftboss.html +146 -0
- package/Games/standalone/drifthunters.html +123 -0
- package/Games/standalone/drivemad.html +41 -0
- package/Games/standalone/ducklifebattle.html +131 -0
- package/Games/standalone/ducklifespace.html +93 -0
- package/Games/standalone/earntodie.html +117 -0
- package/Games/standalone/economical.html +77 -0
- package/Games/standalone/fireandice.html +22 -0
- package/Games/standalone/flappybird.html +137 -0
- package/Games/standalone/fnaf.html +115 -0
- package/Games/standalone/fnaf2.html +34 -0
- package/Games/standalone/fnaf3.html +337 -0
- package/Games/standalone/fnaf4.html +338 -0
- package/Games/standalone/fnaf4halloween.html +34 -0
- package/Games/standalone/fridaynightfunkin.html +76 -0
- package/Games/standalone/fruitninja.html +99 -0
- package/Games/standalone/galaga.html +216 -0
- package/Games/standalone/gdlite.html +255 -0
- package/Games/standalone/gladihoppers.html +36 -0
- package/Games/standalone/googlefeud.html +1163 -0
- package/Games/standalone/granny.html +233 -0
- package/Games/standalone/grindcraft.html +44 -0
- package/Games/standalone/happywheels.html +19 -0
- package/Games/standalone/helixjump.html +30 -0
- package/Games/standalone/holeio.html +53 -0
- package/Games/standalone/idlebreakout.html +95 -0
- package/Games/standalone/jetpack.html +21 -0
- package/Games/standalone/magictiles3.html +111 -0
- package/Games/standalone/monkeymart.html +365 -0
- package/Games/standalone/motox3m.html +1 -0
- package/Games/standalone/motox3mpoolparty.html +18 -0
- package/Games/standalone/motox3mspookyland.html +68 -0
- package/Games/standalone/ngon.html +577 -0
- package/Games/standalone/omnombounce.html +82 -0
- package/Games/standalone/pacmanoriginal.html +30 -0
- package/Games/standalone/papasfreezeria.html +348 -0
- package/Games/standalone/paperio2.html +54 -0
- package/Games/standalone/parkingfury.html +45 -0
- package/Games/standalone/polytrack.html +40 -0
- package/Games/standalone/pou.html +73 -0
- package/Games/standalone/retrobowl.html +119 -0
- package/Games/standalone/retrobowlcollege.html +179 -0
- package/Games/standalone/rocketsoccer.html +39 -0
- package/Games/standalone/run3.html +64 -0
- package/Games/standalone/slender.html +40 -0
- package/Games/standalone/slope.html +59 -0
- package/Games/standalone/snowrider3d.html +27 -0
- package/Games/standalone/soccerrandom.html +0 -0
- package/Games/standalone/solitaire.html +32 -0
- package/Games/standalone/sonicroboblast2.html +2169 -0
- package/Games/standalone/spacewaves.html +239 -0
- package/Games/standalone/stack.html +639 -0
- package/Games/standalone/station141.html +27 -0
- package/Games/standalone/stickmangolf.html +22 -0
- package/Games/standalone/stickmanhook.html +39 -0
- package/Games/standalone/stuntcars3.html +26 -0
- package/Games/standalone/subwaysurfers.html +55 -0
- package/Games/standalone/superhot.html +35 -0
- package/Games/standalone/superliquidsoccer.html +311 -0
- package/Games/standalone/templerun2.html +38 -0
- package/Games/standalone/timeshooter.html +99 -0
- package/Games/standalone/tinyfishing.html +139 -0
- package/Games/standalone/tombofthemask.html +14 -0
- package/Games/standalone/triviacrack.html +24 -0
- package/Games/standalone/tubejumpers.html +20 -0
- package/Games/standalone/ultimatecustomnight.html +34 -0
- package/Games/standalone/undertheredsky.html +46 -0
- package/Games/standalone/vex3.html +42 -0
- package/Games/standalone/vex4.html +38 -0
- package/Games/standalone/vex5.html +43 -0
- package/Games/standalone/vex6.html +56 -0
- package/Games/standalone/vex7.html +53 -0
- package/Games/standalone/vex8.html +46 -0
- package/Games/standalone/wordle.html +27 -0
- package/Games/standalone/worldtour.html +95 -0
- package/backup.html +2016 -0
- package/games.json +2373 -0
- package/index.html +2302 -0
- package/new logo.png +0 -0
- package/package.json +21 -0
- package/port.html +0 -0
- package/readme.md +1 -0
- package/singlefilegames.json +2266 -0
- package/sounds/ambience.mp3 +0 -0
- package/sounds/click.mp3 +0 -0
- package/sounds/close.mp3 +0 -0
- package/sounds/hover.mp3 +0 -0
- package/sounds/select.mp3 +0 -0
package/backup.html
ADDED
|
@@ -0,0 +1,2016 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Alexr Games</title>
|
|
7
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
8
|
+
<style>
|
|
9
|
+
/* --- PS4 SYSTEM FONTS & VARS --- */
|
|
10
|
+
:root {
|
|
11
|
+
--ps-blue: #00439c;
|
|
12
|
+
--ps-light-blue: #00a0e9; /* Default accent color */
|
|
13
|
+
--text-white: #e3f2fd;
|
|
14
|
+
--card-size: 130px;
|
|
15
|
+
--card-selected: 180px;
|
|
16
|
+
--anim-speed: 0.25s;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
* { box-sizing: border-box; user-select: none; }
|
|
20
|
+
|
|
21
|
+
body {
|
|
22
|
+
margin: 0;
|
|
23
|
+
padding: 0;
|
|
24
|
+
background-color: #003791;
|
|
25
|
+
background-image:
|
|
26
|
+
radial-gradient(circle at 50% 0%, rgba(255,255,255,0.1) 0%, transparent 60%),
|
|
27
|
+
linear-gradient(110deg, #003791 0%, #005c9e 50%, #003791 100%);
|
|
28
|
+
background-size: 200% 200%;
|
|
29
|
+
color: white;
|
|
30
|
+
font-family: "SST", "Segoe UI", sans-serif;
|
|
31
|
+
height: 100vh;
|
|
32
|
+
overflow: hidden;
|
|
33
|
+
animation: bgFlow 15s ease infinite;
|
|
34
|
+
background-position: 0% 50%;
|
|
35
|
+
background-repeat: no-repeat;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* Theme variants (backgrounds and accent tweaks) */
|
|
39
|
+
body.theme-default {
|
|
40
|
+
background-color: #003791;
|
|
41
|
+
background-image:
|
|
42
|
+
radial-gradient(circle at 50% 0%, rgba(255,255,255,0.1) 0%, transparent 60%),
|
|
43
|
+
linear-gradient(110deg, #003791 0%, #005c9e 50%, #003791 100%);
|
|
44
|
+
--ps-light-blue: #00a0e9; /* Ensure default is set */
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
body.theme-dark {
|
|
48
|
+
background-color: #050816;
|
|
49
|
+
background-image: radial-gradient(circle at 0 0, #222 0%, transparent 55%),
|
|
50
|
+
radial-gradient(circle at 100% 100%, #111 0%, transparent 55%),
|
|
51
|
+
linear-gradient(135deg,#050816,#000000);
|
|
52
|
+
--ps-light-blue: #999999;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* MODIFIED: Red theme background update */
|
|
56
|
+
body.theme-red {
|
|
57
|
+
background-color: #3a0000 !important;
|
|
58
|
+
background-image:
|
|
59
|
+
radial-gradient(circle at 20% 0, rgba(255,80,80,0.25) 0%, transparent 55%),
|
|
60
|
+
radial-gradient(circle at 80% 100%, rgba(180,0,0,0.35) 0%, transparent 55%),
|
|
61
|
+
linear-gradient(135deg, #3a0000, #7a0000) !important;
|
|
62
|
+
--ps-light-blue: #ff4d4d !important; /* Use red accent color */
|
|
63
|
+
}
|
|
64
|
+
body.theme-red .card {
|
|
65
|
+
background: rgba(60,0,0,0.6) !important;
|
|
66
|
+
}
|
|
67
|
+
body.theme-red .card.active {
|
|
68
|
+
box-shadow: 0 0 0 4px #ff4d4d, 0 10px 20px rgba(0,0,0,0.8) !important;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
body.theme-blue-dark {
|
|
73
|
+
background-color: #020b1a;
|
|
74
|
+
background-image: linear-gradient(135deg,#020b1a,#012b45);
|
|
75
|
+
--ps-light-blue: #44aaff;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* MODIFIED: Define theme accent variable for use in library/slider */
|
|
79
|
+
body.theme-hacker {
|
|
80
|
+
background-color: #000000;
|
|
81
|
+
background-image: radial-gradient(circle at 20% 0, rgba(0,255,0,0.25) 0%, transparent 55%),
|
|
82
|
+
radial-gradient(circle at 80% 100%, rgba(0,255,0,0.25) 0%, transparent 55%);
|
|
83
|
+
color: #00ff88;
|
|
84
|
+
--ps-light-blue: #00ff88; /* ACCENT COLOR FOR CATEGORY BUTTONS/SLIDER */
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* MODIFIED: Define theme accent variable for use in library/slider */
|
|
88
|
+
body.theme-tokyo {
|
|
89
|
+
background-color: #050016;
|
|
90
|
+
background-image: radial-gradient(circle at 10% 0, rgba(255,0,128,0.35) 0%, transparent 55%),
|
|
91
|
+
radial-gradient(circle at 90% 100%, rgba(0,255,255,0.35) 0%, transparent 55%),
|
|
92
|
+
linear-gradient(135deg,#050016,#001133);
|
|
93
|
+
color: #e3f2fd; /* Text color remains light */
|
|
94
|
+
--ps-light-blue: #ff4aff; /* ACCENT COLOR FOR CATEGORY BUTTONS/SLIDER */
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Custom uploaded background (covers themes if present) */
|
|
98
|
+
body.has-custom-bg {
|
|
99
|
+
background-size: cover;
|
|
100
|
+
background-position: center center;
|
|
101
|
+
background-repeat: no-repeat;
|
|
102
|
+
animation: none;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* PS5-style game background */
|
|
106
|
+
.game-bg-overlay {
|
|
107
|
+
position: fixed;
|
|
108
|
+
top: 0;
|
|
109
|
+
left: 0;
|
|
110
|
+
width: 100%;
|
|
111
|
+
height: 100%;
|
|
112
|
+
z-index: -1;
|
|
113
|
+
opacity: 0;
|
|
114
|
+
transition: opacity 0.6s ease;
|
|
115
|
+
pointer-events: none;
|
|
116
|
+
background-size: cover;
|
|
117
|
+
background-position: center;
|
|
118
|
+
background-repeat: no-repeat;
|
|
119
|
+
filter: blur(20px) brightness(0.4);
|
|
120
|
+
transform: scale(1.1);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
body.ps5-bg-enabled .game-bg-overlay.active {
|
|
124
|
+
opacity: 1;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@keyframes bgFlow {
|
|
128
|
+
0% { background-position: 0% 50%; }
|
|
129
|
+
50% { background-position: 100% 50%; }
|
|
130
|
+
100% { background-position: 0% 50%; }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* --- TOP NAVIGATION BAR --- */
|
|
134
|
+
.top-bar {
|
|
135
|
+
display: flex;
|
|
136
|
+
justify-content: space-between;
|
|
137
|
+
align-items: center;
|
|
138
|
+
padding: 30px 60px;
|
|
139
|
+
font-size: 1.5rem;
|
|
140
|
+
opacity: 0.9;
|
|
141
|
+
text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.site-title {
|
|
145
|
+
font-weight: 300;
|
|
146
|
+
font-size: 1.8rem;
|
|
147
|
+
letter-spacing: 1px;
|
|
148
|
+
display: flex;
|
|
149
|
+
align-items: center;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.site-title img {
|
|
153
|
+
height: 30px;
|
|
154
|
+
margin-right: 10px;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.clock-area {
|
|
158
|
+
font-size: 1.2rem;
|
|
159
|
+
font-weight: 300;
|
|
160
|
+
display: flex;
|
|
161
|
+
align-items: center;
|
|
162
|
+
gap: 20px;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* Social Media Icons */
|
|
166
|
+
.social-icons a {
|
|
167
|
+
color: inherit; /* Inherit color from body (e.g., #00ff88 for hacker) */
|
|
168
|
+
transition: color 0.2s;
|
|
169
|
+
}
|
|
170
|
+
.social-icons a:hover {
|
|
171
|
+
color: var(--ps-light-blue); /* Use accent color on hover */
|
|
172
|
+
}
|
|
173
|
+
.social-icons {
|
|
174
|
+
display: flex;
|
|
175
|
+
gap: 15px;
|
|
176
|
+
font-size: 1.4rem;
|
|
177
|
+
color: white; /* Default color */
|
|
178
|
+
}
|
|
179
|
+
body.theme-hacker .social-icons { color: #00ff88; }
|
|
180
|
+
body.theme-tokyo .social-icons { color: #ff4aff; }
|
|
181
|
+
body.theme-red .social-icons { color: var(--ps-light-blue); } /* Apply theme color */
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
/* --- MAIN CAROUSEL AREA --- */
|
|
185
|
+
.main-stage {
|
|
186
|
+
margin-top: 40px;
|
|
187
|
+
width: 100%;
|
|
188
|
+
position: relative;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.carousel-container {
|
|
192
|
+
display: flex;
|
|
193
|
+
align-items: center;
|
|
194
|
+
padding-left: 60px;
|
|
195
|
+
gap: 15px;
|
|
196
|
+
transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
197
|
+
will-change: transform;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.card {
|
|
201
|
+
width: var(--card-size);
|
|
202
|
+
height: var(--card-size);
|
|
203
|
+
background: rgba(0,0,0,0.3);
|
|
204
|
+
flex-shrink: 0;
|
|
205
|
+
position: relative;
|
|
206
|
+
transition: all var(--anim-speed) ease;
|
|
207
|
+
display: flex;
|
|
208
|
+
flex-direction: column;
|
|
209
|
+
justify-content: flex-end;
|
|
210
|
+
overflow: visible;
|
|
211
|
+
cursor: pointer;
|
|
212
|
+
box-shadow: 0 4px 10px rgba(0,0,0,0.3);
|
|
213
|
+
/* Drag-and-drop */
|
|
214
|
+
touch-action: pan-y; /* Allow vertical scroll, but let JS handle horizontal drag */
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* NEW: Dragging state for cards */
|
|
218
|
+
.card:not(.system-card) {
|
|
219
|
+
/* Only game cards are draggable */
|
|
220
|
+
cursor: grab;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.card.dragging {
|
|
224
|
+
opacity: 0.5;
|
|
225
|
+
border: 2px dashed var(--ps-light-blue);
|
|
226
|
+
box-shadow: none;
|
|
227
|
+
transform: translateY(0); /* Remove selection lift */
|
|
228
|
+
cursor: grabbing !important;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
body.theme-hacker .card {
|
|
233
|
+
background: rgba(0,20,0,0.7);
|
|
234
|
+
}
|
|
235
|
+
body.theme-tokyo .card {
|
|
236
|
+
background: rgba(10,0,30,0.7);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.card img {
|
|
240
|
+
width: 100%;
|
|
241
|
+
height: 100%;
|
|
242
|
+
object-fit: cover;
|
|
243
|
+
opacity: 0.8;
|
|
244
|
+
transition: opacity 0.2s;
|
|
245
|
+
pointer-events: none; /* Ignore pointer events on img for better drag/touch */
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/* System Cards (Settings, Favorites, Library) */
|
|
249
|
+
.card.system-card {
|
|
250
|
+
background: linear-gradient(135deg, #0b407a, #001f4d);
|
|
251
|
+
display: flex;
|
|
252
|
+
align-items: center;
|
|
253
|
+
justify-content: center;
|
|
254
|
+
border: 1px solid rgba(255,255,255,0.1);
|
|
255
|
+
cursor: pointer; /* System cards are not draggable */
|
|
256
|
+
}
|
|
257
|
+
body.theme-hacker .card.system-card {
|
|
258
|
+
background: linear-gradient(135deg,#004400,#001400);
|
|
259
|
+
border: 1px solid rgba(0,255,136,0.3);
|
|
260
|
+
}
|
|
261
|
+
body.theme-tokyo .card.system-card {
|
|
262
|
+
background: linear-gradient(135deg,#ff007a, #3b00b3);
|
|
263
|
+
border: 1px solid rgba(255,74,255,0.3);
|
|
264
|
+
}
|
|
265
|
+
body.theme-red .card.system-card {
|
|
266
|
+
background: linear-gradient(135deg,#7a0000, #3a0000);
|
|
267
|
+
border: 1px solid rgba(255,77,77,0.3);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.card.system-card i { font-size: 3rem; color: white; opacity: 0.8; }
|
|
271
|
+
body.theme-hacker .card.system-card i,
|
|
272
|
+
body.theme-tokyo .card.system-card i,
|
|
273
|
+
body.theme-red .card.system-card i {
|
|
274
|
+
color: inherit; /* Use themed color */
|
|
275
|
+
opacity: 1;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
/* --- SELECTED STATE --- */
|
|
280
|
+
.card.active {
|
|
281
|
+
width: var(--card-selected);
|
|
282
|
+
height: var(--card-selected);
|
|
283
|
+
transform: translateY(10px);
|
|
284
|
+
background: rgba(255,255,255,0.1);
|
|
285
|
+
box-shadow: 0 0 0 4px white, 0 10px 20px rgba(0,0,0,0.5);
|
|
286
|
+
z-index: 10;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
body.theme-hacker .card.active {
|
|
290
|
+
box-shadow: 0 0 0 4px #00ff88, 0 10px 20px rgba(0,0,0,0.8);
|
|
291
|
+
}
|
|
292
|
+
body.theme-tokyo .card.active {
|
|
293
|
+
box-shadow: 0 0 0 4px var(--ps-light-blue), 0 10px 20px rgba(0,0,0,0.8);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.card.active img { opacity: 1; }
|
|
297
|
+
|
|
298
|
+
/* --- INFO AREA (Below Carousel) --- */
|
|
299
|
+
.info-area {
|
|
300
|
+
margin-top: 80px;
|
|
301
|
+
padding-left: 60px;
|
|
302
|
+
opacity: 0;
|
|
303
|
+
transform: translateY(20px);
|
|
304
|
+
transition: all 0.3s ease;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.info-area.visible {
|
|
308
|
+
opacity: 1;
|
|
309
|
+
transform: translateY(0);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/* NEW: Hide sub-content when system tile is selected */
|
|
313
|
+
.sub-content.hidden {
|
|
314
|
+
display: none !important;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.game-title {
|
|
318
|
+
font-size: 2.2rem;
|
|
319
|
+
font-weight: 300;
|
|
320
|
+
margin-bottom: 5px;
|
|
321
|
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.6);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.game-meta {
|
|
325
|
+
display: flex;
|
|
326
|
+
gap: 15px;
|
|
327
|
+
font-size: 0.9rem;
|
|
328
|
+
color: rgba(255,255,255,0.8);
|
|
329
|
+
margin-bottom: 25px;
|
|
330
|
+
text-transform: uppercase;
|
|
331
|
+
letter-spacing: 1px;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/* --- SUB CONTENT ROW --- */
|
|
335
|
+
.sub-content {
|
|
336
|
+
display: flex;
|
|
337
|
+
gap: 20px;
|
|
338
|
+
margin-top: 20px;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.sub-tile {
|
|
342
|
+
width: 300px;
|
|
343
|
+
height: 160px;
|
|
344
|
+
background: rgba(0,0,0,0.4);
|
|
345
|
+
display: flex;
|
|
346
|
+
flex-direction: column;
|
|
347
|
+
justify-content: flex-end;
|
|
348
|
+
padding: 15px;
|
|
349
|
+
position: relative;
|
|
350
|
+
overflow: hidden;
|
|
351
|
+
border-radius: 4px;
|
|
352
|
+
}
|
|
353
|
+
.sub-tile img {
|
|
354
|
+
position: absolute; top:0; left:0; width:100%; height:100%; object-fit: cover; opacity: 0.5; z-index: 0;
|
|
355
|
+
}
|
|
356
|
+
.sub-tile div { z-index: 1; position: relative; text-shadow: 1px 1px 2px black; }
|
|
357
|
+
.sub-head { font-weight: bold; font-size: 1.1rem; }
|
|
358
|
+
.sub-desc { font-size: 0.8rem; color: #ccc; }
|
|
359
|
+
|
|
360
|
+
#favoriteTile {
|
|
361
|
+
cursor: pointer;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/* --- LIBRARY & SETTINGS OVERLAY (Grid / Tabs View) --- */
|
|
365
|
+
.library-overlay {
|
|
366
|
+
position: fixed;
|
|
367
|
+
inset: 0;
|
|
368
|
+
background: rgba(11, 23, 48, 0.98);
|
|
369
|
+
z-index: 1000;
|
|
370
|
+
display: none;
|
|
371
|
+
flex-direction: column;
|
|
372
|
+
padding: 40px 80px;
|
|
373
|
+
overflow-y: auto;
|
|
374
|
+
opacity: 0;
|
|
375
|
+
transition: opacity 0.3s ease;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
body.theme-hacker .library-overlay {
|
|
379
|
+
background: rgba(0,0,0,0.96);
|
|
380
|
+
}
|
|
381
|
+
body.theme-tokyo .library-overlay {
|
|
382
|
+
background: rgba(5,0,25,0.96);
|
|
383
|
+
}
|
|
384
|
+
body.theme-red .library-overlay {
|
|
385
|
+
background: rgba(58,0,0,0.96);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.library-overlay.show { display: flex; opacity: 1; }
|
|
389
|
+
|
|
390
|
+
.lib-header {
|
|
391
|
+
display: flex;
|
|
392
|
+
justify-content: space-between;
|
|
393
|
+
align-items: center;
|
|
394
|
+
margin-bottom: 20px;
|
|
395
|
+
border-bottom: 1px solid rgba(255,255,255,0.1);
|
|
396
|
+
padding-bottom: 20px;
|
|
397
|
+
}
|
|
398
|
+
.lib-title { font-size: 2rem; font-weight: 300; }
|
|
399
|
+
.close-btn {
|
|
400
|
+
background: none; border: 1px solid white; color: white;
|
|
401
|
+
padding: 10px 20px; cursor: pointer; border-radius: 4px; font-size: 1rem;
|
|
402
|
+
transition: background 0.2s;
|
|
403
|
+
}
|
|
404
|
+
.close-btn:hover { background: white; color: black; }
|
|
405
|
+
|
|
406
|
+
/* New: Filter area styles */
|
|
407
|
+
.lib-filters {
|
|
408
|
+
margin-bottom: 30px;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.lib-search {
|
|
412
|
+
width: 100%;
|
|
413
|
+
padding: 15px;
|
|
414
|
+
margin-bottom: 15px;
|
|
415
|
+
font-size: 1.2rem;
|
|
416
|
+
border: 2px solid rgba(255,255,255,0.5);
|
|
417
|
+
background: rgba(0,0,0,0.2);
|
|
418
|
+
color: white;
|
|
419
|
+
border-radius: 4px;
|
|
420
|
+
outline: none;
|
|
421
|
+
transition: border-color 0.2s;
|
|
422
|
+
}
|
|
423
|
+
.lib-search:focus {
|
|
424
|
+
border-color: var(--ps-light-blue);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.lib-categories {
|
|
428
|
+
display: flex;
|
|
429
|
+
gap: 10px;
|
|
430
|
+
flex-wrap: wrap;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.category-btn {
|
|
434
|
+
background: rgba(255,255,255,0.1);
|
|
435
|
+
border: 1px solid rgba(255,255,255,0.3);
|
|
436
|
+
color: white;
|
|
437
|
+
padding: 8px 16px;
|
|
438
|
+
cursor: pointer;
|
|
439
|
+
border-radius: 20px;
|
|
440
|
+
font-size: 0.9rem;
|
|
441
|
+
transition: background 0.2s, color 0.2s, border-color 0.2s;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.category-btn.active {
|
|
445
|
+
background: var(--ps-light-blue); /* Uses theme accent color */
|
|
446
|
+
border-color: var(--ps-light-blue); /* Uses theme accent color */
|
|
447
|
+
font-weight: bold;
|
|
448
|
+
}
|
|
449
|
+
.category-btn:hover {
|
|
450
|
+
background: rgba(255,255,255,0.3);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/* End New: Filter area styles */
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
.lib-grid {
|
|
457
|
+
display: grid;
|
|
458
|
+
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
|
459
|
+
gap: 25px;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.lib-card {
|
|
463
|
+
aspect-ratio: 1/1;
|
|
464
|
+
background: #222;
|
|
465
|
+
border-radius: 4px;
|
|
466
|
+
position: relative;
|
|
467
|
+
cursor: pointer;
|
|
468
|
+
transition: transform 0.2s;
|
|
469
|
+
overflow: hidden;
|
|
470
|
+
}
|
|
471
|
+
body.theme-hacker .lib-card { background: #011 !box-shadow: none; }
|
|
472
|
+
body.theme-tokyo .lib-card { background: #102; box-shadow: none; }
|
|
473
|
+
body.theme-red .lib-card { background: #200; box-shadow: none; } /* Red theme adjustment */
|
|
474
|
+
|
|
475
|
+
.lib-card:hover { transform: scale(1.05); box-shadow: 0 5px 15px rgba(0,0,0,0.5); z-index: 2;}
|
|
476
|
+
.lib-card img { width: 100%; height: 100%; object-fit: cover; }
|
|
477
|
+
.lib-card-title {
|
|
478
|
+
position: absolute; bottom: 0; left: 0; right: 0;
|
|
479
|
+
background: rgba(0,0,0,0.8); padding: 8px; font-size: 0.85rem;
|
|
480
|
+
text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/* Fullscreen iframe fix - REPLACED VERSION */
|
|
484
|
+
.game-modal {
|
|
485
|
+
position: fixed !important;
|
|
486
|
+
top: 0 !important;
|
|
487
|
+
left: 0 !important;
|
|
488
|
+
width: 100vw !important;
|
|
489
|
+
height: 100vh !important;
|
|
490
|
+
margin: 0 !important;
|
|
491
|
+
padding: 0 !important;
|
|
492
|
+
background: black !important;
|
|
493
|
+
z-index: 9999 !important;
|
|
494
|
+
display: flex !important;
|
|
495
|
+
flex-direction: column !important;
|
|
496
|
+
border: none !important;
|
|
497
|
+
box-sizing: border-box !important;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.game-modal > div:first-child {
|
|
501
|
+
position: relative !important;
|
|
502
|
+
height: 40px !important;
|
|
503
|
+
flex-shrink: 0 !important;
|
|
504
|
+
z-index: 10000 !important;
|
|
505
|
+
background: #222 !important;
|
|
506
|
+
display: flex !important;
|
|
507
|
+
align-items: center !important;
|
|
508
|
+
padding: 0 20px !important;
|
|
509
|
+
justify-content: space-between !important;
|
|
510
|
+
color: white !important;
|
|
511
|
+
gap: 10px !important;
|
|
512
|
+
}
|
|
513
|
+
/* Ensure the bar background is themed or dark */
|
|
514
|
+
body.theme-hacker .game-modal > div:first-child { background: #000 !important; color: #00ff88 !important; }
|
|
515
|
+
body.theme-tokyo .game-modal > div:first-child { background: #111 !important; color: #ff4aff !important; }
|
|
516
|
+
body.theme-red .game-modal > div:first-child { background: #3a0000 !important; color: var(--ps-light-blue) !important; } /* Red theme fix */
|
|
517
|
+
|
|
518
|
+
.game-modal > div:last-child {
|
|
519
|
+
position: relative !important;
|
|
520
|
+
flex: 1 !important;
|
|
521
|
+
width: 100% !important;
|
|
522
|
+
height: calc(100vh - 40px) !important;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.game-modal iframe {
|
|
526
|
+
position: absolute !important;
|
|
527
|
+
top: 0 !important;
|
|
528
|
+
left: 0 !important;
|
|
529
|
+
width: 100% !important;
|
|
530
|
+
height: 100% !important;
|
|
531
|
+
border: none !important;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/* Updated styles for iframe buttons to inherit site colors/theme better */
|
|
535
|
+
.game-modal button,
|
|
536
|
+
.game-modal .close-btn {
|
|
537
|
+
/* Use current color properties which change with the theme */
|
|
538
|
+
background: rgba(255,255,255,0.2) !important;
|
|
539
|
+
border: 1px solid currentColor !important; /* Uses the current text color */
|
|
540
|
+
color: currentColor !important; /* Uses the current text color */
|
|
541
|
+
padding: 4px 12px !important;
|
|
542
|
+
border-radius: 4px !important;
|
|
543
|
+
cursor: pointer !important;
|
|
544
|
+
font-size: 0.85rem !important;
|
|
545
|
+
opacity: 1 !important;
|
|
546
|
+
visibility: visible !important;
|
|
547
|
+
display: inline-block !important;
|
|
548
|
+
z-index: 10001 !important;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
.game-modal button:hover {
|
|
552
|
+
background: white !important;
|
|
553
|
+
color: black !important;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/* Fix hide bar button - ADD THIS */
|
|
557
|
+
.game-modal .top-bar {
|
|
558
|
+
position: relative !important;
|
|
559
|
+
z-index: 10000 !important;
|
|
560
|
+
transition: opacity 0.2s ease !important;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.game-modal .show-bar-btn {
|
|
564
|
+
position: absolute !important;
|
|
565
|
+
top: 5px !important;
|
|
566
|
+
left: 5px !important;
|
|
567
|
+
z-index: 10001 !important;
|
|
568
|
+
width: 36px !important;
|
|
569
|
+
height: 36px !important;
|
|
570
|
+
padding: 0 !important;
|
|
571
|
+
border: 1px solid #666 !important;
|
|
572
|
+
background: rgba(0,0,0,0.9) !important;
|
|
573
|
+
color: white !important;
|
|
574
|
+
border-radius: 6px !important;
|
|
575
|
+
font-size: 1.2rem !important;
|
|
576
|
+
font-weight: bold !important;
|
|
577
|
+
cursor: pointer !important;
|
|
578
|
+
display: none !important;
|
|
579
|
+
line-height: 1 !important;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/* Targeting the specific element for display: none */
|
|
583
|
+
.game-modal > div:first-child[style*="display: none"] ~ div .show-bar-btn {
|
|
584
|
+
display: block !important;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
/* Other Things tabs buttons row */
|
|
589
|
+
.other-tabs {
|
|
590
|
+
display:flex; gap:10px; margin-bottom:20px;
|
|
591
|
+
flex-wrap: wrap;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
.tab-btn {
|
|
595
|
+
background: none;
|
|
596
|
+
border: 1px solid white;
|
|
597
|
+
color: white;
|
|
598
|
+
padding: 8px 16px;
|
|
599
|
+
cursor: pointer;
|
|
600
|
+
border-radius: 4px;
|
|
601
|
+
font-size: 0.9rem;
|
|
602
|
+
transition: background 0.2s, color 0.2s;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
.tab-btn.active,
|
|
606
|
+
.tab-btn:hover {
|
|
607
|
+
background: white;
|
|
608
|
+
color: black;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/* NEW: Styling for settings input/select */
|
|
612
|
+
.settings-group {
|
|
613
|
+
margin-bottom: 20px;
|
|
614
|
+
padding-bottom: 20px;
|
|
615
|
+
border-bottom: 1px solid rgba(255,255,255,0.1);
|
|
616
|
+
}
|
|
617
|
+
.settings-group:last-child {
|
|
618
|
+
border-bottom: none;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
.settings-group label {
|
|
622
|
+
display: block;
|
|
623
|
+
font-size: 0.9rem;
|
|
624
|
+
color: #ccc;
|
|
625
|
+
margin-bottom: 5px;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
.settings-input,
|
|
629
|
+
.settings-select {
|
|
630
|
+
width: 100%;
|
|
631
|
+
max-width: 400px;
|
|
632
|
+
padding: 10px;
|
|
633
|
+
font-size: 1rem;
|
|
634
|
+
border: 2px solid rgba(255,255,255,0.5);
|
|
635
|
+
background: rgba(0,0,0,0.2);
|
|
636
|
+
color: white;
|
|
637
|
+
border-radius: 4px;
|
|
638
|
+
outline: none;
|
|
639
|
+
transition: border-color 0.2s;
|
|
640
|
+
box-sizing: border-box;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
.settings-input:focus,
|
|
644
|
+
.settings-select:focus {
|
|
645
|
+
border-color: var(--ps-light-blue);
|
|
646
|
+
}
|
|
647
|
+
.settings-select option {
|
|
648
|
+
background: #000;
|
|
649
|
+
color: white;
|
|
650
|
+
}
|
|
651
|
+
/* END NEW: Styling for settings input/select */
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
/* Upload input hidden; use label button */
|
|
655
|
+
#bgUploadInput {
|
|
656
|
+
display:none;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/* NEW CSS for Sound Toggle Switch */
|
|
660
|
+
.sound-control {
|
|
661
|
+
display: flex;
|
|
662
|
+
align-items: center;
|
|
663
|
+
gap: 15px;
|
|
664
|
+
margin-top: 15px;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
.switch {
|
|
668
|
+
position: relative;
|
|
669
|
+
display: inline-block;
|
|
670
|
+
width: 60px;
|
|
671
|
+
height: 34px;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
.switch input {
|
|
675
|
+
opacity: 0;
|
|
676
|
+
width: 0;
|
|
677
|
+
height: 0;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
.slider {
|
|
681
|
+
position: absolute;
|
|
682
|
+
cursor: pointer;
|
|
683
|
+
top: 0;
|
|
684
|
+
left: 0;
|
|
685
|
+
right: 0;
|
|
686
|
+
bottom: 0;
|
|
687
|
+
background-color: #ccc;
|
|
688
|
+
transition: .4s;
|
|
689
|
+
border-radius: 34px;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
.slider:before {
|
|
693
|
+
position: absolute;
|
|
694
|
+
content: "";
|
|
695
|
+
height: 26px;
|
|
696
|
+
width: 26px;
|
|
697
|
+
left: 4px;
|
|
698
|
+
bottom: 4px;
|
|
699
|
+
background-color: white;
|
|
700
|
+
transition: .4s;
|
|
701
|
+
border-radius: 50%;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
input:checked + .slider {
|
|
705
|
+
background-color: var(--ps-light-blue); /* Uses theme accent color */
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
input:focus + .slider {
|
|
709
|
+
box-shadow: 0 0 1px var(--ps-light-blue);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
input:checked + .slider:before {
|
|
713
|
+
transform: translateX(26px);
|
|
714
|
+
}
|
|
715
|
+
</style>
|
|
716
|
+
</head>
|
|
717
|
+
<body>
|
|
718
|
+
<div class="game-bg-overlay" id="gameBgOverlay"></div>
|
|
719
|
+
|
|
720
|
+
<div class="top-bar">
|
|
721
|
+
<div class="site-title"><img src="/new logo.png" alt="Alexr Games Logo"> Alexr's Math World</div>
|
|
722
|
+
<div class="clock-area">
|
|
723
|
+
<div class="social-icons">
|
|
724
|
+
<a href="https://youtube.com/yourchannel" target="_blank" title="YouTube"><i class="fab fa-youtube"></i></a>
|
|
725
|
+
<a href="https://discord.gg/yourserver" target="_blank" title="Discord"><i class="fab fa-discord"></i></a>
|
|
726
|
+
<a href="https://tiktok.com/@youraccount" target="_blank" title="TikTok"><i class="fab fa-tiktok"></i></a>
|
|
727
|
+
<a href="https://instagram.com/youraccount" target="_blank" title="Instagram"><i class="fab fa-instagram"></i></a>
|
|
728
|
+
</div>
|
|
729
|
+
<span id="clock">12:00</span>
|
|
730
|
+
</div>
|
|
731
|
+
</div>
|
|
732
|
+
|
|
733
|
+
<div class="main-stage">
|
|
734
|
+
<div class="carousel-container" id="carousel"></div>
|
|
735
|
+
</div>
|
|
736
|
+
|
|
737
|
+
<div class="info-area visible" id="infoArea">
|
|
738
|
+
<div class="game-title" id="gameTitle">Game Title</div>
|
|
739
|
+
<div class="game-meta">
|
|
740
|
+
<span id="gameCat">Category</span> •
|
|
741
|
+
<span>Unblocked</span>
|
|
742
|
+
</div>
|
|
743
|
+
|
|
744
|
+
<div class="sub-content">
|
|
745
|
+
<div class="sub-tile">
|
|
746
|
+
<img src="" id="bgPreview1" style="background:#222">
|
|
747
|
+
<div class="sub-head">Overview</div>
|
|
748
|
+
<div class="sub-desc" id="overviewDesc">Game Info</div>
|
|
749
|
+
</div>
|
|
750
|
+
<div class="sub-tile" id="versionsTile">
|
|
751
|
+
<img src="" id="bgPreview2" style="background:#333">
|
|
752
|
+
<div class="sub-head" id="versionsHead">Other Versions</div>
|
|
753
|
+
<div class="sub-desc" id="versionsDesc">No other versions</div>
|
|
754
|
+
</div>
|
|
755
|
+
<div class="sub-tile" id="favoriteTile" style="background: linear-gradient(135deg, #00439c, #00285e); align-items: center; justify-content: center;">
|
|
756
|
+
<div id="favoriteLabel" style="font-size: 1.5rem; font-weight: bold;">Favorite this game</div>
|
|
757
|
+
<div class="sub-desc" id="favoriteStatus">Click to add to favorites</div>
|
|
758
|
+
</div>
|
|
759
|
+
</div>
|
|
760
|
+
</div>
|
|
761
|
+
|
|
762
|
+
<div class="library-overlay" id="libraryOverlay">
|
|
763
|
+
<div class="lib-header">
|
|
764
|
+
<div class="lib-title" id="libraryTitle">Your Collection</div>
|
|
765
|
+
<button class="close-btn" onclick="closeOverlay()">Close</button>
|
|
766
|
+
</div>
|
|
767
|
+
|
|
768
|
+
<div class="lib-filters">
|
|
769
|
+
<input type="text" id="libSearchInput" class="lib-search" placeholder="Search games by title..." oninput="renderOverlayGrid()" list="game-suggestions">
|
|
770
|
+
<datalist id="game-suggestions"></datalist>
|
|
771
|
+
<div class="lib-categories" id="libCategories">
|
|
772
|
+
</div>
|
|
773
|
+
</div>
|
|
774
|
+
<div class="lib-grid" id="libraryGrid"></div>
|
|
775
|
+
</div>
|
|
776
|
+
|
|
777
|
+
<div class="library-overlay" id="otherOverlay">
|
|
778
|
+
<div class="lib-header">
|
|
779
|
+
<div class="lib-title">Settings</div>
|
|
780
|
+
<button class="close-btn" onclick="toggleOther(false)">Close</button>
|
|
781
|
+
</div>
|
|
782
|
+
|
|
783
|
+
<div class="other-tabs">
|
|
784
|
+
<button class="tab-btn" id="tab_changelog" onclick="showOtherTab('changelog')">Changelog</button>
|
|
785
|
+
<button class="tab-btn" id="tab_proxy" onclick="showOtherTab('proxy')">Proxy</button>
|
|
786
|
+
<button class="tab-btn" id="tab_themes" onclick="showOtherTab('themes')">Themes</button>
|
|
787
|
+
<button class="tab-btn" id="tab_sounds" onclick="showOtherTab('sounds')">Sounds</button>
|
|
788
|
+
<button class="tab-btn" id="tab_cloaking" onclick="showOtherTab('cloaking')">Cloaking</button>
|
|
789
|
+
</div>
|
|
790
|
+
|
|
791
|
+
<div id="other_changelog">
|
|
792
|
+
<p>v1.0 – Initial release.</p>
|
|
793
|
+
<p>v1.1 – Added favorites, recent games, and themes.</p>
|
|
794
|
+
<p>v1.2 – Added custom backgrounds, more themes, and in-game actions.</p>
|
|
795
|
+
<p>v1.3 – Added PS5-style game background feature and dedicated Settings tile.</p>
|
|
796
|
+
<p>v1.4 – Added search bar and categories to the Library ("All Games") view.</p>
|
|
797
|
+
<p>v1.5 – Added PS4-style sound effects and ambience control.</p>
|
|
798
|
+
<p>v1.6 – Added Suggest/Report system tiles, themed Library, and fixed game bar toggle.</p>
|
|
799
|
+
<p>v1.7 – Removed text labels from system tiles for a cleaner look.</p>
|
|
800
|
+
<p>v1.8 – **NEW:** Added Tab Cloaking and Drag-and-Drop game reordering.</p>
|
|
801
|
+
</div>
|
|
802
|
+
|
|
803
|
+
<div id="other_proxy" style="display:none;">
|
|
804
|
+
<p>Proxy links or information go here.</p>
|
|
805
|
+
</div>
|
|
806
|
+
|
|
807
|
+
<div id="other_themes" style="display:none;">
|
|
808
|
+
<div class="settings-group">
|
|
809
|
+
<p>Select a theme:</p>
|
|
810
|
+
<button class="close-btn" onclick="setTheme('default')">Default</button>
|
|
811
|
+
<button class="close-btn" onclick="setTheme('dark')">Dark</button>
|
|
812
|
+
<button class="close-btn" onclick="setTheme('red')">Red</button>
|
|
813
|
+
<button class="close-btn" onclick="setTheme('blue-dark')">Blue Dark</button>
|
|
814
|
+
<button class="close-btn" onclick="setTheme('hacker')">Hacker</button>
|
|
815
|
+
<button class="close-btn" onclick="setTheme('tokyo')">Tokyo</button>
|
|
816
|
+
</div>
|
|
817
|
+
|
|
818
|
+
<div class="settings-group">
|
|
819
|
+
<p>PS5-Style Game Backgrounds:</p>
|
|
820
|
+
<button class="close-btn" id="ps5BgToggle" onclick="togglePS5Backgrounds()">Enable PS5 Backgrounds</button>
|
|
821
|
+
<p style="font-size:0.85rem; color:#aaa; margin-top:8px;">Show selected game image as blurred background</p>
|
|
822
|
+
</div>
|
|
823
|
+
|
|
824
|
+
<div class="settings-group">
|
|
825
|
+
<p>Custom background image:</p>
|
|
826
|
+
<label class="close-btn" for="bgUploadInput">Upload Background</label>
|
|
827
|
+
<input type="file" id="bgUploadInput" accept="image/*">
|
|
828
|
+
<button class="close-btn" onclick="clearCustomBackground()">Clear Custom Background</button>
|
|
829
|
+
</div>
|
|
830
|
+
</div>
|
|
831
|
+
|
|
832
|
+
<div id="other_sounds" style="display:none;">
|
|
833
|
+
<div class="settings-group">
|
|
834
|
+
<p>Toggle Sound Effects and Ambience:</p>
|
|
835
|
+
<div class="sound-control">
|
|
836
|
+
<label for="soundToggle">Enable Sounds</label>
|
|
837
|
+
<label class="switch">
|
|
838
|
+
<input type="checkbox" id="soundToggle" onchange="toggleSoundEffects(this.checked)">
|
|
839
|
+
<span class="slider"></span>
|
|
840
|
+
</label>
|
|
841
|
+
</div>
|
|
842
|
+
|
|
843
|
+
<p style="font-size:0.85rem; color:#aaa; margin-top:15px;">
|
|
844
|
+
Sound effects include card hovers, selection, and a menu background track.
|
|
845
|
+
</p>
|
|
846
|
+
</div>
|
|
847
|
+
|
|
848
|
+
<div class="settings-group">
|
|
849
|
+
<p>Ambience Volume:</p>
|
|
850
|
+
<input type="range" id="ambienceVolume" min="0" max="1" step="0.1" value="0.5" oninput="setAmbienceVolume(this.value)" style="width: 100%; margin-top: 10px;">
|
|
851
|
+
</div>
|
|
852
|
+
</div>
|
|
853
|
+
|
|
854
|
+
<div id="other_cloaking" style="display:none;">
|
|
855
|
+
<div class="settings-group">
|
|
856
|
+
<p>Select a preset cloak:</p>
|
|
857
|
+
<select id="cloakPresetSelect" class="settings-select" onchange="applyCloakPreset(this.value)">
|
|
858
|
+
<option value="none">None (Default)</option>
|
|
859
|
+
<option value="drive">Google Drive</option>
|
|
860
|
+
<option value="wiki">Wikipedia</option>
|
|
861
|
+
<option value="classlink">ClassLink</option>
|
|
862
|
+
<option value="school">School Website</option>
|
|
863
|
+
</select>
|
|
864
|
+
<button class="close-btn" style="margin-top:10px;" onclick="applyCloakPreset('none')">Clear Cloak</button>
|
|
865
|
+
</div>
|
|
866
|
+
|
|
867
|
+
<div class="settings-group">
|
|
868
|
+
<p>Customize Tab:</p>
|
|
869
|
+
<label for="customTitleInput">Tab Title:</label>
|
|
870
|
+
<input type="text" id="customTitleInput" class="settings-input" placeholder="e.g., My Homework" oninput="setCustomTitle(this.value)">
|
|
871
|
+
</div>
|
|
872
|
+
|
|
873
|
+
<div class="settings-group">
|
|
874
|
+
<label for="customFaviconInput">Favicon URL:</label>
|
|
875
|
+
<input type="text" id="customFaviconInput" class="settings-input" placeholder="e.g., https://example.com/favicon.ico" oninput="setCustomFavicon(this.value)">
|
|
876
|
+
<p style="font-size:0.85rem; color:#aaa; margin-top:8px;">Enter a URL for a favicon image (e.g., a .ico or small .png).</p>
|
|
877
|
+
</div>
|
|
878
|
+
|
|
879
|
+
<button class="close-btn" onclick="saveCustomCloak()">Save Custom Cloak</button>
|
|
880
|
+
</div>
|
|
881
|
+
</div>
|
|
882
|
+
|
|
883
|
+
<script type="application/json" id="games-data">
|
|
884
|
+
[
|
|
885
|
+
{"title": "Subway Surfers", "category": "Runner", "path": "/games/subway_surfers/", "iframe": true, "img": "img/subway.png", "description": "Run, dodge, and collect coins on the train tracks."},
|
|
886
|
+
{"title": "Slope", "category": "Arcade", "path": "/games/slope/", "iframe": true, "img": "img/slope.png", "description": "Roll a ball down a treacherous, neon slope."},
|
|
887
|
+
{"title": "Tunnel Rush", "category": "Arcade", "path": "/games/tunnel_rush/", "iframe": true, "img": "img/tunnel.png", "description": "Navigate a high-speed tunnel of obstacles."},
|
|
888
|
+
{"title": "Retro Bowl", "category": "Sports", "path": "/games/retro_bowl/", "iframe": true, "img": "img/retro.png", "description": "Classic-style American football simulation."},
|
|
889
|
+
{"title": "Minecraft", "category": "Sandbox", "path": "/games/minecraft/", "iframe": true, "img": "img/minecraft.png", "description": "Explore, mine resources, and build anything you can imagine."},
|
|
890
|
+
{"title": "FNAF", "category": "Horror", "path": "/games/fnaf/", "iframe": true, "img": "img/fnaf.png", "description": "Survive the night at Freddy Fazbear's Pizza."},
|
|
891
|
+
{"title": "Drift Hunters", "category": "Racing", "path": "/games/drift_hunters/", "iframe": true, "img": "img/drift.png", "description": "High-quality 3D drifting game."},
|
|
892
|
+
{"title": "Basket Random", "category": "Sports", "path": "/games/basket_random/", "iframe": true, "img": "img/basket.png", "description": "Hilarious physics-based 2v2 basketball."},
|
|
893
|
+
{"title": "2048", "category": "Puzzle", "path": "/games/2048/", "iframe": true, "img": "img/2048.png", "description": "Combine numbered tiles to reach the 2048 tile."},
|
|
894
|
+
{"title": "Vex 6", "category": "Platformer", "path": "/games/vex6/", "iframe": true, "img": "img/vex6.png", "description": "The latest installment in the challenging stickman platformer series."}
|
|
895
|
+
]
|
|
896
|
+
</script>
|
|
897
|
+
|
|
898
|
+
|
|
899
|
+
<script>
|
|
900
|
+
const carousel = document.getElementById('carousel');
|
|
901
|
+
const infoArea = document.getElementById('infoArea');
|
|
902
|
+
const libOverlay = document.getElementById('libraryOverlay');
|
|
903
|
+
const libGrid = document.getElementById('libraryGrid');
|
|
904
|
+
const libraryTitle = document.getElementById('libraryTitle');
|
|
905
|
+
const otherOverlay = document.getElementById('otherOverlay');
|
|
906
|
+
const libSearchInput = document.getElementById('libSearchInput');
|
|
907
|
+
const libCategoriesDiv = document.getElementById('libCategories');
|
|
908
|
+
const gameSuggestionsDatalist = document.getElementById('game-suggestions');
|
|
909
|
+
const subContent = document.querySelector('.sub-content');
|
|
910
|
+
|
|
911
|
+
let allGamesData = [];
|
|
912
|
+
let displayList = [];
|
|
913
|
+
let selectedIndex = 0;
|
|
914
|
+
let isLibraryOpen = false;
|
|
915
|
+
let isOtherOpen = false;
|
|
916
|
+
let currentOverlayMode = 'library';
|
|
917
|
+
let favoritesSet = new Set();
|
|
918
|
+
let currentCategoryFilter = 'All';
|
|
919
|
+
let areSoundsEnabled = false;
|
|
920
|
+
|
|
921
|
+
// Drag-and-Drop state
|
|
922
|
+
let dragSrcEl = null;
|
|
923
|
+
let systemTileStart = 4; // Index of the first game tile
|
|
924
|
+
let systemTileEnd = 1; // Number of non-game tiles at the end (Library)
|
|
925
|
+
|
|
926
|
+
|
|
927
|
+
// System item definitions
|
|
928
|
+
const settingsItem = {
|
|
929
|
+
title: "Settings",
|
|
930
|
+
category: "System",
|
|
931
|
+
description: "Customize themes and manage backgrounds.",
|
|
932
|
+
systemType: "settings",
|
|
933
|
+
icon: "fa-cog"
|
|
934
|
+
};
|
|
935
|
+
|
|
936
|
+
const favoritesItem = {
|
|
937
|
+
title: "Favorites",
|
|
938
|
+
category: "Favorites",
|
|
939
|
+
description: "Your favorite games.",
|
|
940
|
+
systemType: "favorites",
|
|
941
|
+
icon: "fa-heart"
|
|
942
|
+
};
|
|
943
|
+
|
|
944
|
+
// NEW SYSTEM TILES
|
|
945
|
+
const suggestItem = {
|
|
946
|
+
title: "Suggest Game",
|
|
947
|
+
category: "System",
|
|
948
|
+
description: "Suggest a game via Google Form.",
|
|
949
|
+
systemType: "suggest",
|
|
950
|
+
icon: "fa-lightbulb",
|
|
951
|
+
path: "https://docs.google.com/forms/d/e/1FAIpQLScXQf_W-j9Fk1G4_r_oA9-3A0b4vG0eYt-n0O4/viewform?usp=sf_link" // Placeholder URL
|
|
952
|
+
};
|
|
953
|
+
|
|
954
|
+
const reportItem = {
|
|
955
|
+
title: "Report Bug",
|
|
956
|
+
category: "System",
|
|
957
|
+
description: "Report a bug via Google Form.",
|
|
958
|
+
systemType: "report",
|
|
959
|
+
icon: "fa-bug",
|
|
960
|
+
path: "https://docs.google.com/forms/d/e/1FAIpQLScXQf_W-j9Fk1G4_r_oA9-3A0b4vG0eYt-n0O4/viewform?usp=sf_link" // Placeholder URL
|
|
961
|
+
};
|
|
962
|
+
|
|
963
|
+
const libraryItem = {
|
|
964
|
+
title: "Library",
|
|
965
|
+
category: "Collection",
|
|
966
|
+
description: "View all games in your collection.",
|
|
967
|
+
systemType: "library",
|
|
968
|
+
icon: "fa-th"
|
|
969
|
+
};
|
|
970
|
+
|
|
971
|
+
/* --- CLOAKING MANAGER --- */
|
|
972
|
+
|
|
973
|
+
const defaultTitle = "Alexr Games";
|
|
974
|
+
const defaultFavicon = 'new logo.png'; // Assuming this is the default favicon
|
|
975
|
+
|
|
976
|
+
const cloakPresets = {
|
|
977
|
+
none: { title: defaultTitle, favicon: defaultFavicon },
|
|
978
|
+
drive: { title: "My Drive - Google Drive", favicon: "https://ssl.gstatic.com/images/branding/product/2x/drive_2020q4_48dp.png" },
|
|
979
|
+
wiki: { title: "Wikipedia, the free encyclopedia", favicon: "https://en.wikipedia.org/static/favicon/wikipedia.ico" },
|
|
980
|
+
classlink: { title: "ClassLink", favicon: "https://www.classlink.com/favicon.ico" },
|
|
981
|
+
school: { title: "Student Portal | Home", favicon: "https://www.example.edu/favicon.ico" } // Placeholder for a school
|
|
982
|
+
};
|
|
983
|
+
|
|
984
|
+
function setFavicon(url) {
|
|
985
|
+
let link = document.querySelector("link[rel~='icon']");
|
|
986
|
+
if (!link) {
|
|
987
|
+
link = document.createElement('link');
|
|
988
|
+
link.rel = 'icon';
|
|
989
|
+
document.head.appendChild(link);
|
|
990
|
+
}
|
|
991
|
+
// Use local default if 'none' or blank
|
|
992
|
+
if (!url || url === defaultFavicon) {
|
|
993
|
+
link.href = defaultFavicon;
|
|
994
|
+
link.rel = 'icon'; // Keep rel as 'icon'
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
link.href = url;
|
|
999
|
+
link.rel = 'icon'; // Ensure it's correctly set as favicon
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
function setTitle(title) {
|
|
1003
|
+
document.title = title;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
function loadCloak() {
|
|
1007
|
+
const savedTitle = localStorage.getItem('alexrGames_cloakTitle');
|
|
1008
|
+
const savedFavicon = localStorage.getItem('alexrGames_cloakFavicon');
|
|
1009
|
+
|
|
1010
|
+
if (savedTitle && savedFavicon) {
|
|
1011
|
+
setTitle(savedTitle);
|
|
1012
|
+
setFavicon(savedFavicon);
|
|
1013
|
+
} else {
|
|
1014
|
+
setTitle(defaultTitle);
|
|
1015
|
+
setFavicon(defaultFavicon);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
// Update inputs in settings
|
|
1019
|
+
const titleInput = document.getElementById('customTitleInput');
|
|
1020
|
+
const faviconInput = document.getElementById('customFaviconInput');
|
|
1021
|
+
const presetSelect = document.getElementById('cloakPresetSelect');
|
|
1022
|
+
|
|
1023
|
+
if (titleInput) titleInput.value = savedTitle && savedTitle !== defaultTitle ? savedTitle : '';
|
|
1024
|
+
if (faviconInput) faviconInput.value = savedFavicon && savedFavicon !== defaultFavicon ? savedFavicon : '';
|
|
1025
|
+
if (presetSelect) presetSelect.value = 'none'; // Clear preset selection on load
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
function saveCloak(title, favicon) {
|
|
1029
|
+
if (title === defaultTitle && favicon === defaultFavicon) {
|
|
1030
|
+
localStorage.removeItem('alexrGames_cloakTitle');
|
|
1031
|
+
localStorage.removeItem('alexrGames_cloakFavicon');
|
|
1032
|
+
} else {
|
|
1033
|
+
localStorage.setItem('alexrGames_cloakTitle', title);
|
|
1034
|
+
localStorage.setItem('alexrGames_cloakFavicon', favicon);
|
|
1035
|
+
}
|
|
1036
|
+
setTitle(title);
|
|
1037
|
+
setFavicon(favicon);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
function applyCloakPreset(preset) {
|
|
1041
|
+
const cloak = cloakPresets[preset] || cloakPresets['none'];
|
|
1042
|
+
saveCloak(cloak.title, cloak.favicon);
|
|
1043
|
+
|
|
1044
|
+
// Also update custom inputs
|
|
1045
|
+
const titleInput = document.getElementById('customTitleInput');
|
|
1046
|
+
const faviconInput = document.getElementById('customFaviconInput');
|
|
1047
|
+
if (titleInput) titleInput.value = cloak.title !== defaultTitle ? cloak.title : '';
|
|
1048
|
+
if (faviconInput) faviconInput.value = cloak.favicon !== defaultFavicon ? cloak.favicon : '';
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
function setCustomTitle(title) {
|
|
1052
|
+
// Live update the title if it's not empty, but don't save to storage yet
|
|
1053
|
+
if(title) {
|
|
1054
|
+
document.title = title;
|
|
1055
|
+
} else if (!document.getElementById('customFaviconInput').value) {
|
|
1056
|
+
document.title = defaultTitle;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
function setCustomFavicon(url) {
|
|
1061
|
+
// Live update the favicon if it's a valid URL, but don't save to storage yet
|
|
1062
|
+
if(url.startsWith('http')) {
|
|
1063
|
+
setFavicon(url);
|
|
1064
|
+
} else if (!document.getElementById('customTitleInput').value) {
|
|
1065
|
+
setFavicon(defaultFavicon);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
function saveCustomCloak() {
|
|
1070
|
+
const titleInput = document.getElementById('customTitleInput');
|
|
1071
|
+
const faviconInput = document.getElementById('customFaviconInput');
|
|
1072
|
+
const presetSelect = document.getElementById('cloakPresetSelect');
|
|
1073
|
+
|
|
1074
|
+
const title = titleInput.value.trim() || defaultTitle;
|
|
1075
|
+
const favicon = faviconInput.value.trim() || defaultFavicon;
|
|
1076
|
+
|
|
1077
|
+
saveCloak(title, favicon);
|
|
1078
|
+
|
|
1079
|
+
// Reset preset selector
|
|
1080
|
+
if (presetSelect) presetSelect.value = 'none';
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
/* --- END CLOAKING MANAGER --- */
|
|
1084
|
+
|
|
1085
|
+
/* --- SOUND MANAGER --- */
|
|
1086
|
+
|
|
1087
|
+
const ambience = new Audio('sounds/ambience.mp3');
|
|
1088
|
+
ambience.loop = true;
|
|
1089
|
+
ambience.volume = localStorage.getItem('alexrGames_ambience_volume') || 0.5;
|
|
1090
|
+
|
|
1091
|
+
const soundEffects = {
|
|
1092
|
+
click: new Audio('sounds/click.mp3'),
|
|
1093
|
+
hover: new Audio('sounds/hover.mp3'),
|
|
1094
|
+
select: new Audio('sounds/select.mp3'),
|
|
1095
|
+
};
|
|
1096
|
+
|
|
1097
|
+
function loadSoundSettings() {
|
|
1098
|
+
const saved = localStorage.getItem('alexrGames_sounds_enabled') === 'true';
|
|
1099
|
+
areSoundsEnabled = saved;
|
|
1100
|
+
|
|
1101
|
+
const toggle = document.getElementById('soundToggle');
|
|
1102
|
+
if (toggle) toggle.checked = saved;
|
|
1103
|
+
|
|
1104
|
+
const volumeInput = document.getElementById('ambienceVolume');
|
|
1105
|
+
if (volumeInput) volumeInput.value = ambience.volume;
|
|
1106
|
+
|
|
1107
|
+
if (saved && !document.querySelector('.game-modal')) {
|
|
1108
|
+
playAmbience();
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
function toggleSoundEffects(enabled) {
|
|
1113
|
+
areSoundsEnabled = enabled;
|
|
1114
|
+
localStorage.setItem('alexrGames_sounds_enabled', enabled);
|
|
1115
|
+
if (enabled) {
|
|
1116
|
+
playAmbience();
|
|
1117
|
+
} else {
|
|
1118
|
+
pauseAmbience();
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
function setAmbienceVolume(volume) {
|
|
1123
|
+
ambience.volume = volume;
|
|
1124
|
+
localStorage.setItem('alexrGames_ambience_volume', volume);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
function playSound(name) {
|
|
1128
|
+
if (!areSoundsEnabled) return;
|
|
1129
|
+
const audio = soundEffects[name];
|
|
1130
|
+
if (audio) {
|
|
1131
|
+
audio.currentTime = 0;
|
|
1132
|
+
audio.play().catch(e => console.warn("Audio playback failed:", e));
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
function playAmbience() {
|
|
1137
|
+
if (areSoundsEnabled) {
|
|
1138
|
+
// Check if a game is open or an overlay is up before playing
|
|
1139
|
+
if (!document.querySelector('.game-modal') && !isLibraryOpen && !isOtherOpen) {
|
|
1140
|
+
ambience.play().catch(e => console.warn("Ambience playback failed:", e));
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
function pauseAmbience() {
|
|
1146
|
+
ambience.pause();
|
|
1147
|
+
}
|
|
1148
|
+
/* --- END SOUND MANAGER --- */
|
|
1149
|
+
|
|
1150
|
+
/* THEME + CUSTOM BACKGROUND */
|
|
1151
|
+
|
|
1152
|
+
function togglePS5Backgrounds() {
|
|
1153
|
+
const isEnabled = document.body.classList.toggle('ps5-bg-enabled');
|
|
1154
|
+
localStorage.setItem('alexrGames_ps5bg', isEnabled ? 'true' : 'false');
|
|
1155
|
+
const btn = document.getElementById('ps5BgToggle');
|
|
1156
|
+
if (btn) {
|
|
1157
|
+
btn.textContent = isEnabled ? 'Disable PS5 Backgrounds' : 'Enable PS5 Backgrounds';
|
|
1158
|
+
}
|
|
1159
|
+
if (!isEnabled) {
|
|
1160
|
+
const overlay = document.getElementById('gameBgOverlay');
|
|
1161
|
+
if (overlay) overlay.classList.remove('active');
|
|
1162
|
+
} else {
|
|
1163
|
+
updateGameBackground(displayList[selectedIndex]);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
function loadPS5BackgroundSetting() {
|
|
1168
|
+
const saved = localStorage.getItem('alexrGames_ps5bg');
|
|
1169
|
+
const isEnabled = saved === 'true';
|
|
1170
|
+
if (isEnabled) {
|
|
1171
|
+
document.body.classList.add('ps5-bg-enabled');
|
|
1172
|
+
}
|
|
1173
|
+
const btn = document.getElementById('ps5BgToggle');
|
|
1174
|
+
if (btn) {
|
|
1175
|
+
btn.textContent = isEnabled ? 'Disable PS5 Backgrounds' : 'Enable PS5 Backgrounds';
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
function updateGameBackground(game) {
|
|
1180
|
+
const overlay = document.getElementById('gameBgOverlay');
|
|
1181
|
+
if (!overlay) return;
|
|
1182
|
+
|
|
1183
|
+
const isEnabled = document.body.classList.contains('ps5-bg-enabled');
|
|
1184
|
+
|
|
1185
|
+
if (!isEnabled || !game || game.systemType || !game.img) {
|
|
1186
|
+
overlay.classList.remove('active');
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
overlay.style.backgroundImage = `url(${game.img})`;
|
|
1191
|
+
overlay.classList.add('active');
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
function applyTheme(themeName) {
|
|
1195
|
+
document.body.classList.remove('theme-default','theme-dark','theme-red','theme-blue-dark','theme-hacker','theme-tokyo');
|
|
1196
|
+
document.body.classList.add('theme-' + themeName);
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
function setTheme(themeName) {
|
|
1200
|
+
applyTheme(themeName);
|
|
1201
|
+
localStorage.setItem('alexrGames_theme', themeName);
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
function loadTheme() {
|
|
1205
|
+
const savedTheme = localStorage.getItem('alexrGames_theme') || 'default';
|
|
1206
|
+
applyTheme(savedTheme);
|
|
1207
|
+
const bg = localStorage.getItem('alexrGames_bg');
|
|
1208
|
+
if (bg) {
|
|
1209
|
+
document.body.style.backgroundImage = `url(${bg})`;
|
|
1210
|
+
document.body.classList.add('has-custom-bg');
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
function clearCustomBackground() {
|
|
1215
|
+
localStorage.removeItem('alexrGames_bg');
|
|
1216
|
+
document.body.classList.remove('has-custom-bg');
|
|
1217
|
+
document.body.style.backgroundImage = '';
|
|
1218
|
+
const savedTheme = localStorage.getItem('alexrGames_theme') || 'default';
|
|
1219
|
+
applyTheme(savedTheme);
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
const bgUploadInput = document.getElementById('bgUploadInput');
|
|
1223
|
+
if (bgUploadInput) {
|
|
1224
|
+
bgUploadInput.addEventListener('change', function() {
|
|
1225
|
+
const file = this.files[0];
|
|
1226
|
+
if (!file) return;
|
|
1227
|
+
const reader = new FileReader();
|
|
1228
|
+
reader.onload = function(e) {
|
|
1229
|
+
const dataURL = e.target.result;
|
|
1230
|
+
document.body.style.backgroundImage = `url(${dataURL})`;
|
|
1231
|
+
document.body.classList.add('has-custom-bg');
|
|
1232
|
+
localStorage.setItem('alexrGames_bg', dataURL);
|
|
1233
|
+
};
|
|
1234
|
+
reader.readAsDataURL(file);
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
// Load favorites from localStorage
|
|
1239
|
+
function loadFavorites() {
|
|
1240
|
+
const saved = localStorage.getItem('alexrGames_favorites');
|
|
1241
|
+
if (saved) {
|
|
1242
|
+
try {
|
|
1243
|
+
const arr = JSON.parse(saved);
|
|
1244
|
+
favoritesSet = new Set(arr);
|
|
1245
|
+
} catch (e) {
|
|
1246
|
+
favoritesSet = new Set();
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
function saveFavorites() {
|
|
1252
|
+
localStorage.setItem('alexrGames_favorites', JSON.stringify(Array.from(favoritesSet)));
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
// Load game data with localStorage override
|
|
1256
|
+
function loadGames() {
|
|
1257
|
+
loadFavorites();
|
|
1258
|
+
|
|
1259
|
+
const saved = localStorage.getItem('alexrGames_allGamesData');
|
|
1260
|
+
if (saved) {
|
|
1261
|
+
try {
|
|
1262
|
+
allGamesData = JSON.parse(saved);
|
|
1263
|
+
buildLists();
|
|
1264
|
+
return;
|
|
1265
|
+
} catch (e) {
|
|
1266
|
+
console.warn('Bad saved data, falling back to default');
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
// Using the embedded JSON as fallback/default source
|
|
1271
|
+
try {
|
|
1272
|
+
const embedded = JSON.parse(document.getElementById('games-data').textContent);
|
|
1273
|
+
allGamesData = embedded;
|
|
1274
|
+
buildLists();
|
|
1275
|
+
} catch(e) {
|
|
1276
|
+
console.error("Failed to load embedded game data:", e);
|
|
1277
|
+
allGamesData = [];
|
|
1278
|
+
buildLists();
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
function buildLists() {
|
|
1283
|
+
const firstSix = allGamesData.slice(0, 6);
|
|
1284
|
+
|
|
1285
|
+
// MODIFIED: Order system tiles as requested: [Suggest, Report, Favorites, Settings, ...Games, Library]
|
|
1286
|
+
displayList = [suggestItem, reportItem, favoritesItem, settingsItem, ...firstSix, libraryItem];
|
|
1287
|
+
|
|
1288
|
+
// Adjust systemTileStart based on the current system cards (4 system cards before first game)
|
|
1289
|
+
systemTileStart = 4;
|
|
1290
|
+
|
|
1291
|
+
initCarousel();
|
|
1292
|
+
initLibraryGrid();
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
function saveGamesOrder() {
|
|
1296
|
+
// Save only the *game* data, excluding system tiles
|
|
1297
|
+
const gameDataToSave = displayList.slice(systemTileStart, displayList.length - systemTileEnd)
|
|
1298
|
+
.map(item => ({
|
|
1299
|
+
title: item.title,
|
|
1300
|
+
category: item.category,
|
|
1301
|
+
path: item.path,
|
|
1302
|
+
iframe: item.iframe,
|
|
1303
|
+
img: item.img,
|
|
1304
|
+
description: item.description,
|
|
1305
|
+
versions: item.versions // Preserve other game data
|
|
1306
|
+
}));
|
|
1307
|
+
localStorage.setItem('alexrGames_allGamesData', JSON.stringify(gameDataToSave));
|
|
1308
|
+
allGamesData = gameDataToSave; // Update the source of truth
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
function promoteToRecent(game) {
|
|
1312
|
+
// Find the index of the game in the full game list
|
|
1313
|
+
const gameIndex = allGamesData.findIndex(g => g.title === game.title);
|
|
1314
|
+
if (gameIndex > 0) {
|
|
1315
|
+
// Move to the beginning (most recent)
|
|
1316
|
+
const [movedGame] = allGamesData.splice(gameIndex, 1);
|
|
1317
|
+
allGamesData.unshift(movedGame);
|
|
1318
|
+
saveGamesOrder();
|
|
1319
|
+
buildLists();
|
|
1320
|
+
// index 0=Suggest, 1=Report, 2=Favorites, 3=Settings, 4=first game
|
|
1321
|
+
updateSelection(systemTileStart);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
/* --- DRAG-AND-DROP HANDLERS --- */
|
|
1326
|
+
function handleDragStart(e) {
|
|
1327
|
+
if (e.target.classList.contains('system-card')) return; // Ignore system cards
|
|
1328
|
+
|
|
1329
|
+
e.target.classList.add('dragging');
|
|
1330
|
+
dragSrcEl = e.target;
|
|
1331
|
+
e.dataTransfer.effectAllowed = 'move';
|
|
1332
|
+
e.dataTransfer.setData('text/html', e.target.outerHTML);
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
function handleDragOver(e) {
|
|
1336
|
+
e.preventDefault();
|
|
1337
|
+
e.dataTransfer.dropEffect = 'move';
|
|
1338
|
+
|
|
1339
|
+
const target = e.target.closest('.card:not(.system-card)');
|
|
1340
|
+
if (!target || target === dragSrcEl) return;
|
|
1341
|
+
|
|
1342
|
+
// Simple visual feedback: indicate where the card will drop
|
|
1343
|
+
// Find current game card elements for comparison
|
|
1344
|
+
const cards = Array.from(carousel.querySelectorAll('.card:not(.system-card)'));
|
|
1345
|
+
const draggedIndex = cards.findIndex(c => c === dragSrcEl);
|
|
1346
|
+
const targetIndex = cards.findIndex(c => c === target);
|
|
1347
|
+
|
|
1348
|
+
// Determine if dragging left or right over the midpoint of the target
|
|
1349
|
+
const targetRect = target.getBoundingClientRect();
|
|
1350
|
+
const midpoint = targetRect.left + (targetRect.width / 2);
|
|
1351
|
+
|
|
1352
|
+
if (draggedIndex < targetIndex && e.clientX > midpoint) {
|
|
1353
|
+
// Dragging from left to right, drop after target
|
|
1354
|
+
target.style.marginLeft = '15px'; // Push target right
|
|
1355
|
+
target.style.marginRight = '0';
|
|
1356
|
+
} else if (draggedIndex > targetIndex && e.clientX < midpoint) {
|
|
1357
|
+
// Dragging from right to left, drop before target
|
|
1358
|
+
target.style.marginRight = '15px'; // Push target left
|
|
1359
|
+
target.style.marginLeft = '0';
|
|
1360
|
+
} else {
|
|
1361
|
+
// Reset margins if not dropping immediately next to it
|
|
1362
|
+
target.style.marginLeft = '0';
|
|
1363
|
+
target.style.marginRight = '0';
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
function handleDragLeave(e) {
|
|
1368
|
+
// Reset margin when leaving a card
|
|
1369
|
+
const target = e.target.closest('.card:not(.system-card)');
|
|
1370
|
+
if (target) {
|
|
1371
|
+
target.style.marginLeft = '0';
|
|
1372
|
+
target.style.marginRight = '0';
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
function handleDrop(e) {
|
|
1377
|
+
e.stopPropagation();
|
|
1378
|
+
const targetCard = e.target.closest('.card:not(.system-card)');
|
|
1379
|
+
|
|
1380
|
+
if (!targetCard || dragSrcEl === targetCard) {
|
|
1381
|
+
return false;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
// Get the indices of the games in the *displayList*
|
|
1385
|
+
const dragIndex = parseInt(dragSrcEl.dataset.index);
|
|
1386
|
+
const dropIndex = parseInt(targetCard.dataset.index);
|
|
1387
|
+
|
|
1388
|
+
// 1. Update the underlying allGamesData array
|
|
1389
|
+
// Convert to indices relative to allGamesData (which starts after the 4 system tiles)
|
|
1390
|
+
const dragGameIndex = dragIndex - systemTileStart;
|
|
1391
|
+
const dropGameIndex = dropIndex - systemTileStart;
|
|
1392
|
+
|
|
1393
|
+
const [draggedGameData] = allGamesData.splice(dragGameIndex, 1);
|
|
1394
|
+
allGamesData.splice(dropGameIndex, 0, draggedGameData);
|
|
1395
|
+
|
|
1396
|
+
// 2. Persist the new order
|
|
1397
|
+
saveGamesOrder();
|
|
1398
|
+
|
|
1399
|
+
// 3. Re-render the carousel and set selection
|
|
1400
|
+
buildLists();
|
|
1401
|
+
updateSelection(dropIndex); // Select the newly dropped card
|
|
1402
|
+
|
|
1403
|
+
return false;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
function handleDragEnd(e) {
|
|
1407
|
+
e.target.classList.remove('dragging');
|
|
1408
|
+
|
|
1409
|
+
// Reset all card margins
|
|
1410
|
+
document.querySelectorAll('.card:not(.system-card)').forEach(card => {
|
|
1411
|
+
card.style.marginLeft = '0';
|
|
1412
|
+
card.style.marginRight = '0';
|
|
1413
|
+
});
|
|
1414
|
+
|
|
1415
|
+
dragSrcEl = null;
|
|
1416
|
+
}
|
|
1417
|
+
/* --- END DRAG-AND-DROP HANDLERS --- */
|
|
1418
|
+
|
|
1419
|
+
|
|
1420
|
+
function initCarousel() {
|
|
1421
|
+
carousel.innerHTML = '';
|
|
1422
|
+
|
|
1423
|
+
displayList.forEach((game, index) => {
|
|
1424
|
+
const card = document.createElement('div');
|
|
1425
|
+
card.className = `card ${game.systemType ? 'system-card' : ''}`;
|
|
1426
|
+
card.dataset.index = index;
|
|
1427
|
+
|
|
1428
|
+
if (game.systemType) {
|
|
1429
|
+
card.innerHTML = `<i class="fas ${game.icon}"></i>`;
|
|
1430
|
+
card.style.justifyContent = 'center';
|
|
1431
|
+
card.style.alignItems = 'center';
|
|
1432
|
+
// System cards are not draggable
|
|
1433
|
+
} else {
|
|
1434
|
+
const img = document.createElement('img');
|
|
1435
|
+
img.src = game.img || '';
|
|
1436
|
+
img.onerror = function() { this.style.display='none'; card.style.background='#333'; };
|
|
1437
|
+
card.appendChild(img);
|
|
1438
|
+
|
|
1439
|
+
// Game cards are draggable
|
|
1440
|
+
card.setAttribute('draggable', true);
|
|
1441
|
+
card.addEventListener('dragstart', handleDragStart);
|
|
1442
|
+
card.addEventListener('dragover', handleDragOver);
|
|
1443
|
+
card.addEventListener('dragleave', handleDragLeave);
|
|
1444
|
+
card.addEventListener('drop', handleDrop);
|
|
1445
|
+
card.addEventListener('dragend', handleDragEnd);
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
card.addEventListener('click', () => {
|
|
1449
|
+
if(selectedIndex === index) activateItem(index);
|
|
1450
|
+
else updateSelection(index);
|
|
1451
|
+
});
|
|
1452
|
+
|
|
1453
|
+
// Add hover sound listener for non-touch devices
|
|
1454
|
+
card.addEventListener('mouseover', () => {
|
|
1455
|
+
if (!card.classList.contains('active')) {
|
|
1456
|
+
playSound('hover');
|
|
1457
|
+
}
|
|
1458
|
+
});
|
|
1459
|
+
|
|
1460
|
+
carousel.appendChild(card);
|
|
1461
|
+
});
|
|
1462
|
+
|
|
1463
|
+
// Default to first game if exists (index 4 in new layout)
|
|
1464
|
+
updateSelection(displayList.findIndex(g => !g.systemType) || 0);
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
function getUniqueCategories() {
|
|
1468
|
+
const categories = allGamesData.map(game => game.category).filter(Boolean);
|
|
1469
|
+
return ['All', ...new Set(categories)];
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
function renderSearchSuggestions() {
|
|
1473
|
+
gameSuggestionsDatalist.innerHTML = '';
|
|
1474
|
+
allGamesData.forEach(game => {
|
|
1475
|
+
const option = document.createElement('option');
|
|
1476
|
+
option.value = game.title;
|
|
1477
|
+
gameSuggestionsDatalist.appendChild(option);
|
|
1478
|
+
});
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
function renderCategoryButtons() {
|
|
1482
|
+
libCategoriesDiv.innerHTML = '';
|
|
1483
|
+
const categories = getUniqueCategories();
|
|
1484
|
+
|
|
1485
|
+
categories.forEach(category => {
|
|
1486
|
+
const button = document.createElement('button');
|
|
1487
|
+
button.className = 'category-btn';
|
|
1488
|
+
button.textContent = category;
|
|
1489
|
+
button.dataset.category = category;
|
|
1490
|
+
|
|
1491
|
+
if (category === currentCategoryFilter) {
|
|
1492
|
+
button.classList.add('active');
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
button.onclick = () => {
|
|
1496
|
+
currentCategoryFilter = category;
|
|
1497
|
+
renderCategoryButtons();
|
|
1498
|
+
renderOverlayGrid();
|
|
1499
|
+
};
|
|
1500
|
+
|
|
1501
|
+
libCategoriesDiv.appendChild(button);
|
|
1502
|
+
});
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
function initLibraryGrid() {
|
|
1506
|
+
renderSearchSuggestions();
|
|
1507
|
+
renderCategoryButtons();
|
|
1508
|
+
renderOverlayGrid();
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
function renderOverlayGrid() {
|
|
1512
|
+
libGrid.innerHTML = '';
|
|
1513
|
+
let gamesToShow = [];
|
|
1514
|
+
|
|
1515
|
+
if (currentOverlayMode === 'library') {
|
|
1516
|
+
libraryTitle.textContent = 'All Games';
|
|
1517
|
+
gamesToShow = allGamesData;
|
|
1518
|
+
|
|
1519
|
+
// Filtering logic
|
|
1520
|
+
const searchTerm = libSearchInput.value.toLowerCase().trim();
|
|
1521
|
+
|
|
1522
|
+
gamesToShow = gamesToShow.filter(game => {
|
|
1523
|
+
const matchesSearch = !searchTerm || game.title.toLowerCase().includes(searchTerm);
|
|
1524
|
+
const matchesCategory = currentCategoryFilter === 'All' || game.category === currentCategoryFilter;
|
|
1525
|
+
return matchesSearch && matchesCategory;
|
|
1526
|
+
});
|
|
1527
|
+
|
|
1528
|
+
} else {
|
|
1529
|
+
libraryTitle.textContent = 'Your Favorites';
|
|
1530
|
+
gamesToShow = allGamesData.filter(g => favoritesSet.has(g.title));
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
gamesToShow.forEach((game) => {
|
|
1534
|
+
const card = document.createElement('div');
|
|
1535
|
+
card.className = 'lib-card';
|
|
1536
|
+
card.innerHTML = `
|
|
1537
|
+
<img src="${game.img || ''}" onerror="this.style.display='none'">
|
|
1538
|
+
<div class="lib-card-title">${game.title}</div>
|
|
1539
|
+
`;
|
|
1540
|
+
card.onclick = () => {
|
|
1541
|
+
playSound('select');
|
|
1542
|
+
toggleOverlay(false);
|
|
1543
|
+
launchGame(game);
|
|
1544
|
+
};
|
|
1545
|
+
libGrid.appendChild(card);
|
|
1546
|
+
});
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
function updateSelection(index) {
|
|
1550
|
+
if(isLibraryOpen || isOtherOpen) return;
|
|
1551
|
+
if(index < 0) index = 0;
|
|
1552
|
+
if(index >= displayList.length) index = displayList.length - 1;
|
|
1553
|
+
|
|
1554
|
+
selectedIndex = index;
|
|
1555
|
+
|
|
1556
|
+
const cards = document.querySelectorAll('.card');
|
|
1557
|
+
cards.forEach(c => c.classList.remove('active'));
|
|
1558
|
+
if (cards[index]) cards[index].classList.add('active');
|
|
1559
|
+
|
|
1560
|
+
const gap = 15;
|
|
1561
|
+
let offset = 0;
|
|
1562
|
+
|
|
1563
|
+
// Calculate the position of the selected card
|
|
1564
|
+
let activeCardPosition = 0;
|
|
1565
|
+
for(let i = 0; i < index; i++) {
|
|
1566
|
+
const cardElement = cards[i];
|
|
1567
|
+
const cardWidth = cardElement.classList.contains('active') ? 180 : 130;
|
|
1568
|
+
activeCardPosition += cardWidth + gap + (parseFloat(cardElement.style.marginLeft) || 0) + (parseFloat(cardElement.style.marginRight) || 0);
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
// Calculate the required translation to center the selected card
|
|
1572
|
+
// We want to bring the left edge of the selected card to 60px from the left,
|
|
1573
|
+
// minus the drag-and-drop margins that might be temporarily applied.
|
|
1574
|
+
const targetOffset = activeCardPosition - 0;
|
|
1575
|
+
offset = Math.max(0, targetOffset);
|
|
1576
|
+
|
|
1577
|
+
carousel.style.transform = `translateX(-${offset}px)`;
|
|
1578
|
+
|
|
1579
|
+
|
|
1580
|
+
const g = displayList[index];
|
|
1581
|
+
document.getElementById('gameTitle').innerText = g.title || '';
|
|
1582
|
+
document.getElementById('gameCat').innerText = g.category || 'Game';
|
|
1583
|
+
document.getElementById('overviewDesc').innerText = g.description || 'Game Info';
|
|
1584
|
+
|
|
1585
|
+
const bg1 = document.getElementById('bgPreview1');
|
|
1586
|
+
const bg2 = document.getElementById('bgPreview2');
|
|
1587
|
+
|
|
1588
|
+
if(g.img) {
|
|
1589
|
+
bg1.src = g.img; bg2.src = g.img;
|
|
1590
|
+
bg1.style.filter = "grayscale(100%) blur(2px)";
|
|
1591
|
+
bg2.style.filter = "sepia(50%)";
|
|
1592
|
+
} else {
|
|
1593
|
+
bg1.src = ""; bg2.src = "";
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
// NEW FIX: Hide sub-content area for system tiles
|
|
1597
|
+
if (g.systemType) {
|
|
1598
|
+
subContent.classList.add('hidden');
|
|
1599
|
+
document.getElementById('gameTitle').innerText = g.title;
|
|
1600
|
+
document.getElementById('gameCat').innerText = g.category;
|
|
1601
|
+
} else {
|
|
1602
|
+
subContent.classList.remove('hidden');
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
updateFavoriteTile(g);
|
|
1606
|
+
updateVersionsTile(g);
|
|
1607
|
+
updateGameBackground(g);
|
|
1608
|
+
|
|
1609
|
+
infoArea.classList.remove('visible');
|
|
1610
|
+
setTimeout(() => infoArea.classList.add('visible'), 50);
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
function activateItem(index) {
|
|
1614
|
+
playSound('select');
|
|
1615
|
+
const item = displayList[index];
|
|
1616
|
+
|
|
1617
|
+
if (item.systemType === 'library') {
|
|
1618
|
+
currentOverlayMode = 'library';
|
|
1619
|
+
currentCategoryFilter = 'All';
|
|
1620
|
+
libSearchInput.value = '';
|
|
1621
|
+
renderCategoryButtons();
|
|
1622
|
+
renderOverlayGrid();
|
|
1623
|
+
toggleOverlay(true);
|
|
1624
|
+
} else if (item.systemType === 'favorites') {
|
|
1625
|
+
currentOverlayMode = 'favorites';
|
|
1626
|
+
renderOverlayGrid();
|
|
1627
|
+
toggleOverlay(true);
|
|
1628
|
+
} else if (item.systemType === 'settings') {
|
|
1629
|
+
// Default to 'themes' tab when opening settings
|
|
1630
|
+
showOtherTab('themes');
|
|
1631
|
+
toggleOther(true);
|
|
1632
|
+
} else if (item.systemType === 'suggest' || item.systemType === 'report') {
|
|
1633
|
+
window.open(item.path, '_blank');
|
|
1634
|
+
} else {
|
|
1635
|
+
launchGame(item);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
function toggleOverlay(show) {
|
|
1640
|
+
isLibraryOpen = show;
|
|
1641
|
+
if(show) {
|
|
1642
|
+
libOverlay.classList.add('show');
|
|
1643
|
+
libOverlay.style.opacity = '0';
|
|
1644
|
+
setTimeout(()=>libOverlay.style.opacity='1', 10);
|
|
1645
|
+
} else {
|
|
1646
|
+
libOverlay.style.opacity = '0';
|
|
1647
|
+
setTimeout(()=>libOverlay.classList.remove('show'), 300);
|
|
1648
|
+
// Resume ambience if returning to home menu (no other overlay and no game)
|
|
1649
|
+
if (!isOtherOpen && !document.querySelector('.game-modal')) {
|
|
1650
|
+
playAmbience();
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
function closeOverlay() {
|
|
1656
|
+
playSound('click');
|
|
1657
|
+
toggleOverlay(false);
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
function toggleOther(show) {
|
|
1661
|
+
isOtherOpen = show;
|
|
1662
|
+
if (show) {
|
|
1663
|
+
otherOverlay.classList.add('show');
|
|
1664
|
+
otherOverlay.style.opacity = '0';
|
|
1665
|
+
setTimeout(() => otherOverlay.style.opacity = '1', 10);
|
|
1666
|
+
} else {
|
|
1667
|
+
playSound('click');
|
|
1668
|
+
otherOverlay.style.opacity = '0';
|
|
1669
|
+
setTimeout(() => otherOverlay.classList.remove('show'), 300);
|
|
1670
|
+
// Resume ambience if returning to home menu (no other overlay and no game)
|
|
1671
|
+
if (!isLibraryOpen && !document.querySelector('.game-modal')) {
|
|
1672
|
+
playAmbience();
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
function showOtherTab(tab) {
|
|
1678
|
+
const tabs = ['changelog','proxy','themes', 'sounds', 'cloaking'];
|
|
1679
|
+
tabs.forEach(t => {
|
|
1680
|
+
document.getElementById('other_' + t).style.display = (t === tab) ? 'block' : 'none';
|
|
1681
|
+
const btn = document.getElementById('tab_' + t);
|
|
1682
|
+
if (btn) {
|
|
1683
|
+
if (t === tab) btn.classList.add('active');
|
|
1684
|
+
else btn.classList.remove('active');
|
|
1685
|
+
}
|
|
1686
|
+
});
|
|
1687
|
+
|
|
1688
|
+
// Re-load cloaking settings when opening its tab
|
|
1689
|
+
if (tab === 'cloaking') {
|
|
1690
|
+
loadCloak();
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
function launchGame(game) {
|
|
1695
|
+
pauseAmbience();
|
|
1696
|
+
|
|
1697
|
+
const modal = document.createElement('div');
|
|
1698
|
+
modal.className = 'game-modal';
|
|
1699
|
+
|
|
1700
|
+
// --- 1. Top Bar Setup (Common to both) ---
|
|
1701
|
+
const bar = document.createElement('div');
|
|
1702
|
+
Object.assign(bar.style, {
|
|
1703
|
+
height: '40px', background: '#222', display: 'flex',
|
|
1704
|
+
alignItems: 'center', padding: '0 20px',
|
|
1705
|
+
justifyContent: 'space-between', color:'white', gap:'10px'
|
|
1706
|
+
});
|
|
1707
|
+
|
|
1708
|
+
// Apply theme colors to the bar explicitly
|
|
1709
|
+
const currentBodyTheme = document.body.className.match(/theme-(\w+)/)?.[1] || 'default';
|
|
1710
|
+
if (currentBodyTheme === 'hacker') {
|
|
1711
|
+
Object.assign(bar.style, { background: '#000', color: '#00ff88' });
|
|
1712
|
+
} else if (currentBodyTheme === 'tokyo') {
|
|
1713
|
+
Object.assign(bar.style, { background: '#111', color: '#ff4aff' });
|
|
1714
|
+
} else if (currentBodyTheme === 'red') {
|
|
1715
|
+
// Red theme fix using CSS variable
|
|
1716
|
+
const redAccent = getComputedStyle(document.body).getPropertyValue('--ps-light-blue');
|
|
1717
|
+
Object.assign(bar.style, { background: '#3a0000', color: redAccent });
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
const leftSpan = document.createElement('span');
|
|
1721
|
+
leftSpan.textContent = game.title;
|
|
1722
|
+
|
|
1723
|
+
const controls = document.createElement('div');
|
|
1724
|
+
controls.style.display = 'flex';
|
|
1725
|
+
controls.style.gap = '8px';
|
|
1726
|
+
|
|
1727
|
+
// --- 2. Button Handlers (Common to both) ---
|
|
1728
|
+
|
|
1729
|
+
// Reusable logic for opening in about:blank
|
|
1730
|
+
const openAboutBlank = () => {
|
|
1731
|
+
const w = window.open('about:blank', '_blank');
|
|
1732
|
+
if (w && w.document) {
|
|
1733
|
+
const iframe = w.document.createElement('iframe');
|
|
1734
|
+
iframe.style.border = 'none';
|
|
1735
|
+
iframe.style.width = '100%';
|
|
1736
|
+
iframe.style.height = '100%';
|
|
1737
|
+
iframe.src = game.path;
|
|
1738
|
+
w.document.body.style.margin = '0';
|
|
1739
|
+
w.document.body.appendChild(iframe);
|
|
1740
|
+
}
|
|
1741
|
+
};
|
|
1742
|
+
|
|
1743
|
+
const btnAboutBlank = document.createElement('button');
|
|
1744
|
+
btnAboutBlank.textContent = 'Open in about:blank';
|
|
1745
|
+
btnAboutBlank.className = 'close-btn';
|
|
1746
|
+
btnAboutBlank.onclick = openAboutBlank;
|
|
1747
|
+
|
|
1748
|
+
const btnDownload = document.createElement('button');
|
|
1749
|
+
btnDownload.textContent = 'Download';
|
|
1750
|
+
btnDownload.className = 'close-btn';
|
|
1751
|
+
btnDownload.onclick = () => {
|
|
1752
|
+
const a = document.createElement('a');
|
|
1753
|
+
a.href = game.path;
|
|
1754
|
+
a.download = '';
|
|
1755
|
+
document.body.appendChild(a);
|
|
1756
|
+
a.click();
|
|
1757
|
+
document.body.removeChild(a);
|
|
1758
|
+
};
|
|
1759
|
+
|
|
1760
|
+
const btnFav = document.createElement('button');
|
|
1761
|
+
btnFav.textContent = favoritesSet.has(game.title) ? 'Unfavorite' : 'Favorite';
|
|
1762
|
+
btnFav.className = 'close-btn';
|
|
1763
|
+
btnFav.onclick = () => {
|
|
1764
|
+
if (favoritesSet.has(game.title)) {
|
|
1765
|
+
favoritesSet.delete(game.title);
|
|
1766
|
+
btnFav.textContent = 'Favorite';
|
|
1767
|
+
} else {
|
|
1768
|
+
favoritesSet.add(game.title);
|
|
1769
|
+
btnFav.textContent = 'Unfavorite';
|
|
1770
|
+
}
|
|
1771
|
+
saveFavorites();
|
|
1772
|
+
updateFavoriteTile(game);
|
|
1773
|
+
};
|
|
1774
|
+
|
|
1775
|
+
const btnHide = document.createElement('button');
|
|
1776
|
+
btnHide.textContent = 'Hide bar';
|
|
1777
|
+
btnHide.className = 'close-btn';
|
|
1778
|
+
|
|
1779
|
+
const btnClose = document.createElement('button');
|
|
1780
|
+
btnClose.textContent = '✕ Close';
|
|
1781
|
+
btnClose.className = 'close-btn';
|
|
1782
|
+
btnClose.onclick = () => {
|
|
1783
|
+
playSound('click');
|
|
1784
|
+
document.body.removeChild(modal);
|
|
1785
|
+
// Only resume ambience if no system overlay is open
|
|
1786
|
+
if (!isLibraryOpen && !isOtherOpen) {
|
|
1787
|
+
playAmbience();
|
|
1788
|
+
}
|
|
1789
|
+
};
|
|
1790
|
+
|
|
1791
|
+
// Assemble controls
|
|
1792
|
+
controls.appendChild(btnAboutBlank);
|
|
1793
|
+
controls.appendChild(btnDownload);
|
|
1794
|
+
controls.appendChild(btnFav);
|
|
1795
|
+
controls.appendChild(btnHide);
|
|
1796
|
+
controls.appendChild(btnClose);
|
|
1797
|
+
|
|
1798
|
+
bar.appendChild(leftSpan);
|
|
1799
|
+
bar.appendChild(controls);
|
|
1800
|
+
|
|
1801
|
+
// --- 3. Content Area Setup (Custom per iframe status) ---
|
|
1802
|
+
const frameWrapper = document.createElement('div');
|
|
1803
|
+
frameWrapper.style.position = 'relative';
|
|
1804
|
+
frameWrapper.style.flex = '1';
|
|
1805
|
+
|
|
1806
|
+
const showBarBtn = document.createElement('button');
|
|
1807
|
+
showBarBtn.textContent = '≡';
|
|
1808
|
+
showBarBtn.className = 'show-bar-btn';
|
|
1809
|
+
|
|
1810
|
+
btnHide.onclick = () => {
|
|
1811
|
+
bar.style.display = 'none';
|
|
1812
|
+
showBarBtn.style.display = 'block';
|
|
1813
|
+
};
|
|
1814
|
+
showBarBtn.onclick = () => {
|
|
1815
|
+
bar.style.display = 'flex';
|
|
1816
|
+
showBarBtn.style.display = 'none';
|
|
1817
|
+
};
|
|
1818
|
+
|
|
1819
|
+
|
|
1820
|
+
if (game.iframe) {
|
|
1821
|
+
// Content: Game iframe
|
|
1822
|
+
const frame = document.createElement('iframe');
|
|
1823
|
+
frame.src = game.path;
|
|
1824
|
+
frame.style.flex = 1;
|
|
1825
|
+
frame.style.border = 'none';
|
|
1826
|
+
|
|
1827
|
+
frameWrapper.appendChild(frame);
|
|
1828
|
+
frameWrapper.appendChild(showBarBtn);
|
|
1829
|
+
|
|
1830
|
+
} else {
|
|
1831
|
+
// Content: Custom message for unsupported game (User Request)
|
|
1832
|
+
frameWrapper.style.display = 'flex';
|
|
1833
|
+
frameWrapper.style.flexDirection = 'column';
|
|
1834
|
+
frameWrapper.style.justifyContent = 'center';
|
|
1835
|
+
frameWrapper.style.alignItems = 'center';
|
|
1836
|
+
frameWrapper.style.textAlign = 'center';
|
|
1837
|
+
frameWrapper.style.background = '#000';
|
|
1838
|
+
|
|
1839
|
+
const messageDiv = document.createElement('div');
|
|
1840
|
+
messageDiv.style.padding = '50px';
|
|
1841
|
+
messageDiv.style.maxWidth = '80%';
|
|
1842
|
+
|
|
1843
|
+
const messageP = document.createElement('p');
|
|
1844
|
+
messageP.textContent = "Due to issues, this game is not supported here.";
|
|
1845
|
+
messageP.style.fontSize = '1.8rem';
|
|
1846
|
+
messageP.style.color = 'white';
|
|
1847
|
+
messageP.style.marginBottom = '0.5rem';
|
|
1848
|
+
|
|
1849
|
+
const instructionsP = document.createElement('p');
|
|
1850
|
+
instructionsP.textContent = "Click Open in About:blank to proceed.";
|
|
1851
|
+
instructionsP.style.fontSize = '1.2rem';
|
|
1852
|
+
instructionsP.style.color = '#ccc';
|
|
1853
|
+
instructionsP.style.marginTop = '0';
|
|
1854
|
+
|
|
1855
|
+
// Use a styled button for the About:blank link
|
|
1856
|
+
const bigAboutBlankBtn = btnAboutBlank.cloneNode(true);
|
|
1857
|
+
bigAboutBlankBtn.textContent = 'Open in About:blank';
|
|
1858
|
+
bigAboutBlankBtn.style.marginTop = '20px';
|
|
1859
|
+
bigAboutBlankBtn.style.padding = '10px 30px';
|
|
1860
|
+
bigAboutBlankBtn.style.fontSize = '1.1rem';
|
|
1861
|
+
bigAboutBlankBtn.onclick = openAboutBlank; // Re-wire handler
|
|
1862
|
+
|
|
1863
|
+
messageDiv.appendChild(messageP);
|
|
1864
|
+
messageDiv.appendChild(instructionsP);
|
|
1865
|
+
messageDiv.appendChild(bigAboutBlankBtn);
|
|
1866
|
+
|
|
1867
|
+
frameWrapper.appendChild(messageDiv);
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
// --- 4. Final Assembly & Launch (Common to both) ---
|
|
1871
|
+
modal.appendChild(bar);
|
|
1872
|
+
modal.appendChild(frameWrapper);
|
|
1873
|
+
document.body.appendChild(modal);
|
|
1874
|
+
promoteToRecent(game);
|
|
1875
|
+
}
|
|
1876
|
+
function updateFavoriteTile(game) {
|
|
1877
|
+
const tile = document.getElementById('favoriteTile');
|
|
1878
|
+
const label = document.getElementById('favoriteLabel');
|
|
1879
|
+
const status = document.getElementById('favoriteStatus');
|
|
1880
|
+
|
|
1881
|
+
if (!game || game.systemType) {
|
|
1882
|
+
// Should not happen as sub-content is hidden, but for safety:
|
|
1883
|
+
tile.style.background = 'linear-gradient(135deg, #555, #333)';
|
|
1884
|
+
label.textContent = "Favorite this game";
|
|
1885
|
+
status.textContent = "Unavailable for system tiles";
|
|
1886
|
+
tile.onclick = null;
|
|
1887
|
+
return;
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
const isFav = favoritesSet.has(game.title);
|
|
1891
|
+
|
|
1892
|
+
if (isFav) {
|
|
1893
|
+
tile.style.background = 'linear-gradient(135deg, #00c853, #64dd17)';
|
|
1894
|
+
label.textContent = "Favorited";
|
|
1895
|
+
status.textContent = "Click to remove from favorites";
|
|
1896
|
+
} else {
|
|
1897
|
+
tile.style.background = 'linear-gradient(135deg, #00439c, #00285e)';
|
|
1898
|
+
label.textContent = "Favorite this game";
|
|
1899
|
+
status.textContent = "Click to add to favorites";
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
tile.onclick = () => {
|
|
1903
|
+
if (favoritesSet.has(game.title)) {
|
|
1904
|
+
favoritesSet.delete(game.title);
|
|
1905
|
+
} else {
|
|
1906
|
+
favoritesSet.add(game.title);
|
|
1907
|
+
}
|
|
1908
|
+
saveFavorites();
|
|
1909
|
+
updateFavoriteTile(game);
|
|
1910
|
+
};
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
function updateVersionsTile(game) {
|
|
1914
|
+
const head = document.getElementById('versionsHead');
|
|
1915
|
+
const desc = document.getElementById('versionsDesc');
|
|
1916
|
+
const tile = document.getElementById('versionsTile');
|
|
1917
|
+
|
|
1918
|
+
if (!game || game.systemType) {
|
|
1919
|
+
head.textContent = "Other Versions";
|
|
1920
|
+
desc.textContent = "Not available";
|
|
1921
|
+
tile.style.opacity = '0.5';
|
|
1922
|
+
tile.style.cursor = 'default';
|
|
1923
|
+
tile.onclick = null;
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
const versions = game.versions || [];
|
|
1928
|
+
|
|
1929
|
+
if (versions.length > 0) {
|
|
1930
|
+
head.textContent = "Other Versions";
|
|
1931
|
+
desc.textContent = versions.length + " available version(s)";
|
|
1932
|
+
tile.style.opacity = '1';
|
|
1933
|
+
tile.style.cursor = 'pointer';
|
|
1934
|
+
|
|
1935
|
+
tile.onclick = () => {
|
|
1936
|
+
const choice = prompt(
|
|
1937
|
+
"Other versions:\n" + versions.map((v, i) => (i + 1) + ". " + v).join("\n") +
|
|
1938
|
+
"\nEnter number to launch:"
|
|
1939
|
+
);
|
|
1940
|
+
const idx = parseInt(choice, 10) - 1;
|
|
1941
|
+
if (!isNaN(idx) && versions[idx]) {
|
|
1942
|
+
window.open(versions[idx], '_blank');
|
|
1943
|
+
}
|
|
1944
|
+
};
|
|
1945
|
+
} else {
|
|
1946
|
+
head.textContent = "Other Versions";
|
|
1947
|
+
desc.textContent = "No other versions";
|
|
1948
|
+
tile.style.opacity = '0.5';
|
|
1949
|
+
tile.style.cursor = 'default';
|
|
1950
|
+
tile.onclick = null;
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
// Clock
|
|
1955
|
+
setInterval(() => {
|
|
1956
|
+
const now = new Date();
|
|
1957
|
+
const h = String(now.getHours()).padStart(2, '0');
|
|
1958
|
+
const m = String(now.getMinutes()).padStart(2, '0');
|
|
1959
|
+
document.getElementById('clock').innerText = `${h}:${m}`;
|
|
1960
|
+
}, 1000);
|
|
1961
|
+
|
|
1962
|
+
// Inputs
|
|
1963
|
+
window.addEventListener('keydown', (e) => {
|
|
1964
|
+
if (isLibraryOpen || isOtherOpen) {
|
|
1965
|
+
if(e.key === 'Escape') {
|
|
1966
|
+
if(isLibraryOpen) {
|
|
1967
|
+
libSearchInput.value = '';
|
|
1968
|
+
currentCategoryFilter = 'All';
|
|
1969
|
+
renderCategoryButtons();
|
|
1970
|
+
renderOverlayGrid();
|
|
1971
|
+
closeOverlay();
|
|
1972
|
+
} else if (isOtherOpen) {
|
|
1973
|
+
toggleOther(false);
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
return;
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
// Do not allow movement while dragging a card
|
|
1980
|
+
if (dragSrcEl) return;
|
|
1981
|
+
|
|
1982
|
+
const oldIndex = selectedIndex;
|
|
1983
|
+
|
|
1984
|
+
if(e.key === 'ArrowRight') updateSelection(selectedIndex + 1);
|
|
1985
|
+
if(e.key === 'ArrowLeft') updateSelection(selectedIndex - 1);
|
|
1986
|
+
|
|
1987
|
+
if ((e.key === 'ArrowRight' || e.key === 'ArrowLeft') && selectedIndex !== oldIndex) {
|
|
1988
|
+
playSound('hover');
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
if(e.key === 'Enter') activateItem(selectedIndex);
|
|
1992
|
+
});
|
|
1993
|
+
|
|
1994
|
+
// Initialize
|
|
1995
|
+
loadTheme();
|
|
1996
|
+
loadGames();
|
|
1997
|
+
loadPS5BackgroundSetting();
|
|
1998
|
+
loadSoundSettings();
|
|
1999
|
+
loadCloak();
|
|
2000
|
+
</script>
|
|
2001
|
+
|
|
2002
|
+
<style>
|
|
2003
|
+
/* RED THEME OVERRIDES (match other themes) */
|
|
2004
|
+
/* The main theme-red block above handles most of this now, but keeping the background override for safety/clarity */
|
|
2005
|
+
body.theme-red {
|
|
2006
|
+
background-color: #3a0000 !important;
|
|
2007
|
+
background-image:
|
|
2008
|
+
radial-gradient(circle at 20% 0, rgba(255,80,80,0.25) 0%, transparent 55%),
|
|
2009
|
+
radial-gradient(circle at 80% 100%, rgba(180,0,0,0.35) 0%, transparent 55%),
|
|
2010
|
+
linear-gradient(135deg, #3a0000, #7a0000) !important;
|
|
2011
|
+
--ps-light-blue: #ff4d4d !important;
|
|
2012
|
+
}
|
|
2013
|
+
</style>
|
|
2014
|
+
|
|
2015
|
+
</body>
|
|
2016
|
+
</html>
|