Spaces:
Running on Zero
Running on Zero
Upload 10 files
Browse files- .gitattributes +8 -0
- MovingDotSpaceExport/MovingDotSpaceStandalone.html +1014 -0
- MovingDotSpaceExport/backgrounds/1stGameLoc123.png +3 -0
- MovingDotSpaceExport/backgrounds/2ndGameLoc123.png +3 -0
- MovingDotSpaceExport/backgrounds/ConstructionGameLoc.png +3 -0
- MovingDotSpaceExport/backgrounds/CyberpunkGameLoc.png +3 -0
- MovingDotSpaceExport/backgrounds/MedievalFantasyGameLoc.png +3 -0
- MovingDotSpaceExport/backgrounds/SpaceOdysseyGameLoc.png +3 -0
- MovingDotSpaceExport/backgrounds/SuperTeacherGameLoc.png +3 -0
- MovingDotSpaceExport/backgrounds/eldoria_background.png +3 -0
- MovingDotSpaceExport/gameConfig.json +436 -0
.gitattributes
CHANGED
|
@@ -35,3 +35,11 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
testmedia/Stable[[:space:]]Audio[[:space:]]-[[:space:]]Raindrops,[[:space:]]output.wav filter=lfs diff=lfs merge=lfs -text
|
| 37 |
testmedia/SVD[[:space:]]-[[:space:]]random[[:space:]]squiggles[[:space:]]as[[:space:]]roads[[:space:]]video[[:space:]]004484.mp4 filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
testmedia/Stable[[:space:]]Audio[[:space:]]-[[:space:]]Raindrops,[[:space:]]output.wav filter=lfs diff=lfs merge=lfs -text
|
| 37 |
testmedia/SVD[[:space:]]-[[:space:]]random[[:space:]]squiggles[[:space:]]as[[:space:]]roads[[:space:]]video[[:space:]]004484.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
MovingDotSpaceExport/backgrounds/1stGameLoc123.png filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
MovingDotSpaceExport/backgrounds/2ndGameLoc123.png filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
MovingDotSpaceExport/backgrounds/ConstructionGameLoc.png filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
MovingDotSpaceExport/backgrounds/CyberpunkGameLoc.png filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
MovingDotSpaceExport/backgrounds/eldoria_background.png filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
MovingDotSpaceExport/backgrounds/MedievalFantasyGameLoc.png filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
MovingDotSpaceExport/backgrounds/SpaceOdysseyGameLoc.png filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
MovingDotSpaceExport/backgrounds/SuperTeacherGameLoc.png filter=lfs diff=lfs merge=lfs -text
|
MovingDotSpaceExport/MovingDotSpaceStandalone.html
ADDED
|
@@ -0,0 +1,1014 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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>Moving Dot Space Game</title>
|
| 7 |
+
<style>
|
| 8 |
+
* {
|
| 9 |
+
box-sizing: border-box;
|
| 10 |
+
margin: 0;
|
| 11 |
+
padding: 0;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
body {
|
| 15 |
+
font-family: Arial, sans-serif;
|
| 16 |
+
background: #f0f0f0;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
#game-root {
|
| 20 |
+
max-width: 100%;
|
| 21 |
+
overflow-x: hidden;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
/* Theme Selector */
|
| 25 |
+
#theme-selector {
|
| 26 |
+
padding: 10px;
|
| 27 |
+
background: #fff;
|
| 28 |
+
border-bottom: 1px solid #ccc;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
#theme-selector select {
|
| 32 |
+
padding: 5px 10px;
|
| 33 |
+
font-size: 14px;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
#theme-selector button {
|
| 37 |
+
margin-left: 10px;
|
| 38 |
+
padding: 5px 10px;
|
| 39 |
+
cursor: pointer;
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
/* Game Container */
|
| 43 |
+
#game-container {
|
| 44 |
+
position: relative;
|
| 45 |
+
width: 100%;
|
| 46 |
+
height: 100vh;
|
| 47 |
+
border: 1px solid black;
|
| 48 |
+
overflow: hidden;
|
| 49 |
+
background-size: cover;
|
| 50 |
+
background-position: center;
|
| 51 |
+
background-color: #333;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
/* Moving Dot (Player) */
|
| 55 |
+
#moving-dot {
|
| 56 |
+
position: absolute;
|
| 57 |
+
width: 20px;
|
| 58 |
+
height: 20px;
|
| 59 |
+
background-color: lightgreen;
|
| 60 |
+
border: 2px solid black;
|
| 61 |
+
cursor: pointer;
|
| 62 |
+
z-index: 10;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
#moving-dot:focus {
|
| 66 |
+
outline: 3px solid yellow;
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
/* Targets */
|
| 70 |
+
.target {
|
| 71 |
+
position: absolute;
|
| 72 |
+
width: 20px;
|
| 73 |
+
height: 20px;
|
| 74 |
+
background-color: red;
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
.target-label {
|
| 78 |
+
position: absolute;
|
| 79 |
+
color: white;
|
| 80 |
+
font-size: 12px;
|
| 81 |
+
text-shadow: 1px 1px 2px black;
|
| 82 |
+
pointer-events: none;
|
| 83 |
+
white-space: nowrap;
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
/* Overlay Text (HUD) */
|
| 87 |
+
#overlay-text {
|
| 88 |
+
position: absolute;
|
| 89 |
+
top: 3%;
|
| 90 |
+
left: 3%;
|
| 91 |
+
color: black;
|
| 92 |
+
background-color: white;
|
| 93 |
+
font-weight: bold;
|
| 94 |
+
font-size: 15px;
|
| 95 |
+
border: 2px solid black;
|
| 96 |
+
padding: 5px 10px;
|
| 97 |
+
max-width: 90%;
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
/* Modal Overlay (shared) */
|
| 101 |
+
.modal-overlay {
|
| 102 |
+
position: fixed;
|
| 103 |
+
top: 0;
|
| 104 |
+
left: 0;
|
| 105 |
+
width: 100%;
|
| 106 |
+
height: 100%;
|
| 107 |
+
background-color: rgba(0, 0, 0, 0.5);
|
| 108 |
+
display: none;
|
| 109 |
+
justify-content: center;
|
| 110 |
+
align-items: center;
|
| 111 |
+
z-index: 1000;
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
.modal-overlay.open {
|
| 115 |
+
display: flex;
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
/* Modal Box */
|
| 119 |
+
.modal {
|
| 120 |
+
background-color: white;
|
| 121 |
+
border-radius: 4px;
|
| 122 |
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
| 123 |
+
max-width: 80%;
|
| 124 |
+
max-height: 80%;
|
| 125 |
+
overflow: auto;
|
| 126 |
+
min-width: 300px;
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
.modal-header {
|
| 130 |
+
display: flex;
|
| 131 |
+
justify-content: space-between;
|
| 132 |
+
align-items: center;
|
| 133 |
+
padding: 16px;
|
| 134 |
+
border-bottom: 1px solid #ddd;
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
.modal-header h2 {
|
| 138 |
+
margin: 0;
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
.modal-header button {
|
| 142 |
+
background: none;
|
| 143 |
+
border: none;
|
| 144 |
+
font-size: 24px;
|
| 145 |
+
cursor: pointer;
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
.modal-content {
|
| 149 |
+
padding: 16px;
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
.modal-content p {
|
| 153 |
+
margin-bottom: 15px;
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
.modal-actions {
|
| 157 |
+
display: flex;
|
| 158 |
+
flex-direction: column;
|
| 159 |
+
gap: 8px;
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
.modal-actions button {
|
| 163 |
+
padding: 10px 15px;
|
| 164 |
+
cursor: pointer;
|
| 165 |
+
background: #4a90d9;
|
| 166 |
+
color: white;
|
| 167 |
+
border: none;
|
| 168 |
+
border-radius: 4px;
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
.modal-actions button:hover {
|
| 172 |
+
background: #357abd;
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
.modal-image {
|
| 176 |
+
max-width: 100%;
|
| 177 |
+
margin-top: 10px;
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
/* Stats Panel */
|
| 181 |
+
#stats-panel {
|
| 182 |
+
border: 2px solid black;
|
| 183 |
+
display: grid;
|
| 184 |
+
grid-template-columns: repeat(4, 1fr);
|
| 185 |
+
gap: 10px;
|
| 186 |
+
padding: 10px;
|
| 187 |
+
background: #fff;
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
.stats-item {
|
| 191 |
+
border: 1px solid #ccc;
|
| 192 |
+
padding: 10px;
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
.stats-item h2, .stats-item h1 {
|
| 196 |
+
font-size: 16px;
|
| 197 |
+
margin-bottom: 10px;
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
.stats-item h6 {
|
| 201 |
+
font-size: 11px;
|
| 202 |
+
color: #666;
|
| 203 |
+
margin-bottom: 8px;
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
.stats-item ul {
|
| 207 |
+
list-style: none;
|
| 208 |
+
padding: 0;
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
.stats-item li {
|
| 212 |
+
margin: 5px 0;
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
.stats-item button {
|
| 216 |
+
margin: 3px;
|
| 217 |
+
padding: 5px 10px;
|
| 218 |
+
cursor: pointer;
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
/* Objectives styling */
|
| 222 |
+
#objectives-list .completed {
|
| 223 |
+
text-decoration: line-through;
|
| 224 |
+
color: green;
|
| 225 |
+
}
|
| 226 |
+
|
| 227 |
+
/* Color coding */
|
| 228 |
+
#stats-section { color: blue; }
|
| 229 |
+
#inventory-section { color: red; }
|
| 230 |
+
#skills-section { color: green; }
|
| 231 |
+
|
| 232 |
+
/* Responsive */
|
| 233 |
+
@media (max-width: 800px) {
|
| 234 |
+
#stats-panel {
|
| 235 |
+
grid-template-columns: repeat(2, 1fr);
|
| 236 |
+
}
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
@media (max-width: 500px) {
|
| 240 |
+
#stats-panel {
|
| 241 |
+
grid-template-columns: 1fr;
|
| 242 |
+
}
|
| 243 |
+
}
|
| 244 |
+
</style>
|
| 245 |
+
</head>
|
| 246 |
+
<body>
|
| 247 |
+
<div id="game-root">
|
| 248 |
+
<!-- Theme Selector -->
|
| 249 |
+
<div id="theme-selector">
|
| 250 |
+
Game Selector:
|
| 251 |
+
<select id="theme-dropdown"></select>
|
| 252 |
+
<button id="load-json-btn">Load gameConfig.json</button>
|
| 253 |
+
<button id="load-external-btn">Paste JSON Config</button>
|
| 254 |
+
<label style="margin-left:10px;">
|
| 255 |
+
Background: <input type="file" id="bg-file-input" accept="image/*" style="width:150px;">
|
| 256 |
+
</label>
|
| 257 |
+
</div>
|
| 258 |
+
|
| 259 |
+
<!-- Game Container -->
|
| 260 |
+
<div id="game-container">
|
| 261 |
+
<div id="overlay-text">Click game area and use arrow keys to move</div>
|
| 262 |
+
<div id="moving-dot" tabindex="0"></div>
|
| 263 |
+
<div id="targets-container"></div>
|
| 264 |
+
</div>
|
| 265 |
+
|
| 266 |
+
<!-- Simple Modal -->
|
| 267 |
+
<div id="simple-modal" class="modal-overlay">
|
| 268 |
+
<div class="modal">
|
| 269 |
+
<div class="modal-header">
|
| 270 |
+
<h2 id="simple-modal-title"></h2>
|
| 271 |
+
<button id="simple-modal-close">×</button>
|
| 272 |
+
</div>
|
| 273 |
+
<div class="modal-content">
|
| 274 |
+
<p id="simple-modal-content"></p>
|
| 275 |
+
<div id="simple-modal-actions" class="modal-actions"></div>
|
| 276 |
+
</div>
|
| 277 |
+
</div>
|
| 278 |
+
</div>
|
| 279 |
+
|
| 280 |
+
<!-- State Machine Modal -->
|
| 281 |
+
<div id="sm-modal" class="modal-overlay">
|
| 282 |
+
<div class="modal">
|
| 283 |
+
<div class="modal-header">
|
| 284 |
+
<h2 id="sm-modal-title"></h2>
|
| 285 |
+
<button id="sm-modal-close">×</button>
|
| 286 |
+
</div>
|
| 287 |
+
<div class="modal-content">
|
| 288 |
+
<p id="sm-modal-content"></p>
|
| 289 |
+
<div id="sm-modal-actions" class="modal-actions"></div>
|
| 290 |
+
<img id="sm-modal-image" class="modal-image" style="display:none;" alt="">
|
| 291 |
+
</div>
|
| 292 |
+
</div>
|
| 293 |
+
</div>
|
| 294 |
+
|
| 295 |
+
<!-- Stats Panel -->
|
| 296 |
+
<div id="stats-panel">
|
| 297 |
+
<div class="stats-item">
|
| 298 |
+
<h1>Objectives</h1>
|
| 299 |
+
<ul id="objectives-list"></ul>
|
| 300 |
+
</div>
|
| 301 |
+
<div class="stats-item" id="inventory-section">
|
| 302 |
+
<h2>Inventory</h2>
|
| 303 |
+
<h6>Affects movement and modal action options</h6>
|
| 304 |
+
<div id="inventory-list"></div>
|
| 305 |
+
</div>
|
| 306 |
+
<div class="stats-item" id="stats-section">
|
| 307 |
+
<h1>Player Stats</h1>
|
| 308 |
+
<h6>Affects Modal Interactions</h6>
|
| 309 |
+
<div id="player-stats"></div>
|
| 310 |
+
</div>
|
| 311 |
+
<div class="stats-item" id="skills-section">
|
| 312 |
+
<h2>Skill Tree / Abilities</h2>
|
| 313 |
+
<h6>Affects effectiveness of inventory</h6>
|
| 314 |
+
<ul id="skills-list"></ul>
|
| 315 |
+
</div>
|
| 316 |
+
</div>
|
| 317 |
+
</div>
|
| 318 |
+
|
| 319 |
+
<script>
|
| 320 |
+
// ===== GAME STATE =====
|
| 321 |
+
const gameState = {
|
| 322 |
+
dotPosition: { x: 900, y: 450 },
|
| 323 |
+
boundaries: { maxX: 1835, maxY: 890, minX: 0, minY: 0 },
|
| 324 |
+
step: 10,
|
| 325 |
+
currentTheme: 'Default',
|
| 326 |
+
currentThemeStage: 0,
|
| 327 |
+
inventory: [],
|
| 328 |
+
skills: [],
|
| 329 |
+
objectives: [],
|
| 330 |
+
targets: [],
|
| 331 |
+
persistentTargets: [],
|
| 332 |
+
playerStats: {
|
| 333 |
+
health: 100,
|
| 334 |
+
mana: 50,
|
| 335 |
+
strength: 10,
|
| 336 |
+
agility: 10,
|
| 337 |
+
intelligence: 10,
|
| 338 |
+
charisma: 10,
|
| 339 |
+
luck: 10,
|
| 340 |
+
money: 1000
|
| 341 |
+
},
|
| 342 |
+
// Modal state
|
| 343 |
+
isSimpleModalOpen: false,
|
| 344 |
+
isSMModalOpen: false,
|
| 345 |
+
currentSMModalStates: null,
|
| 346 |
+
currentSMModalState: 'default',
|
| 347 |
+
// Theme config
|
| 348 |
+
themes: {},
|
| 349 |
+
themeActions: {}
|
| 350 |
+
};
|
| 351 |
+
|
| 352 |
+
// ===== DOM REFERENCES =====
|
| 353 |
+
const elements = {
|
| 354 |
+
gameContainer: document.getElementById('game-container'),
|
| 355 |
+
movingDot: document.getElementById('moving-dot'),
|
| 356 |
+
targetsContainer: document.getElementById('targets-container'),
|
| 357 |
+
overlayText: document.getElementById('overlay-text'),
|
| 358 |
+
themeDropdown: document.getElementById('theme-dropdown'),
|
| 359 |
+
loadExternalBtn: document.getElementById('load-external-btn'),
|
| 360 |
+
// Simple Modal
|
| 361 |
+
simpleModal: document.getElementById('simple-modal'),
|
| 362 |
+
simpleModalTitle: document.getElementById('simple-modal-title'),
|
| 363 |
+
simpleModalContent: document.getElementById('simple-modal-content'),
|
| 364 |
+
simpleModalActions: document.getElementById('simple-modal-actions'),
|
| 365 |
+
simpleModalClose: document.getElementById('simple-modal-close'),
|
| 366 |
+
// State Machine Modal
|
| 367 |
+
smModal: document.getElementById('sm-modal'),
|
| 368 |
+
smModalTitle: document.getElementById('sm-modal-title'),
|
| 369 |
+
smModalContent: document.getElementById('sm-modal-content'),
|
| 370 |
+
smModalActions: document.getElementById('sm-modal-actions'),
|
| 371 |
+
smModalImage: document.getElementById('sm-modal-image'),
|
| 372 |
+
smModalClose: document.getElementById('sm-modal-close'),
|
| 373 |
+
// Stats
|
| 374 |
+
objectivesList: document.getElementById('objectives-list'),
|
| 375 |
+
inventoryList: document.getElementById('inventory-list'),
|
| 376 |
+
playerStats: document.getElementById('player-stats'),
|
| 377 |
+
skillsList: document.getElementById('skills-list')
|
| 378 |
+
};
|
| 379 |
+
|
| 380 |
+
// ===== MOVEMENT =====
|
| 381 |
+
function moveDot(newX, newY) {
|
| 382 |
+
const boundedX = Math.max(gameState.boundaries.minX, Math.min(newX, gameState.boundaries.maxX));
|
| 383 |
+
const boundedY = Math.max(gameState.boundaries.minY, Math.min(newY, gameState.boundaries.maxY));
|
| 384 |
+
|
| 385 |
+
gameState.dotPosition.x = boundedX;
|
| 386 |
+
gameState.dotPosition.y = boundedY;
|
| 387 |
+
|
| 388 |
+
renderDot();
|
| 389 |
+
checkCollisions();
|
| 390 |
+
updateOverlayText();
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
+
function handleKeyPress(e) {
|
| 394 |
+
if (gameState.isSimpleModalOpen || gameState.isSMModalOpen) return;
|
| 395 |
+
|
| 396 |
+
let newX = gameState.dotPosition.x;
|
| 397 |
+
let newY = gameState.dotPosition.y;
|
| 398 |
+
|
| 399 |
+
switch (e.key) {
|
| 400 |
+
case 'ArrowLeft': newX -= gameState.step; e.preventDefault(); break;
|
| 401 |
+
case 'ArrowRight': newX += gameState.step; e.preventDefault(); break;
|
| 402 |
+
case 'ArrowUp': newY -= gameState.step; e.preventDefault(); break;
|
| 403 |
+
case 'ArrowDown': newY += gameState.step; e.preventDefault(); break;
|
| 404 |
+
default: return;
|
| 405 |
+
}
|
| 406 |
+
|
| 407 |
+
moveDot(newX, newY);
|
| 408 |
+
}
|
| 409 |
+
|
| 410 |
+
// ===== COLLISION DETECTION =====
|
| 411 |
+
function checkCollisions() {
|
| 412 |
+
const dotPos = gameState.dotPosition;
|
| 413 |
+
|
| 414 |
+
gameState.targets.forEach(target => {
|
| 415 |
+
if (
|
| 416 |
+
dotPos.x < target.x + 10 &&
|
| 417 |
+
dotPos.x + 10 > target.x &&
|
| 418 |
+
dotPos.y < target.y + 10 &&
|
| 419 |
+
dotPos.y + 10 > target.y
|
| 420 |
+
) {
|
| 421 |
+
handleCollision(target);
|
| 422 |
+
}
|
| 423 |
+
});
|
| 424 |
+
}
|
| 425 |
+
|
| 426 |
+
function handleCollision(target) {
|
| 427 |
+
// Handle subtargets notification
|
| 428 |
+
if (target.subtargets) {
|
| 429 |
+
alert("Subtargets found. Can be used as discovered locations.");
|
| 430 |
+
console.log(target.subtargets);
|
| 431 |
+
}
|
| 432 |
+
|
| 433 |
+
switch (target.collisionType) {
|
| 434 |
+
case "":
|
| 435 |
+
console.log("Nothing happened");
|
| 436 |
+
break;
|
| 437 |
+
|
| 438 |
+
case "alert":
|
| 439 |
+
alert(target.collisiontext);
|
| 440 |
+
break;
|
| 441 |
+
|
| 442 |
+
case "decision":
|
| 443 |
+
if (confirm(target.collisiontext)) {
|
| 444 |
+
alert("You said yes");
|
| 445 |
+
} else {
|
| 446 |
+
alert("You said no");
|
| 447 |
+
}
|
| 448 |
+
break;
|
| 449 |
+
|
| 450 |
+
case "modal":
|
| 451 |
+
openSimpleModal(target.modalConfig);
|
| 452 |
+
break;
|
| 453 |
+
|
| 454 |
+
case "smmodal":
|
| 455 |
+
openSMModal(target.modalStates);
|
| 456 |
+
break;
|
| 457 |
+
|
| 458 |
+
case "storypartchange":
|
| 459 |
+
changeStoryPart(target.transitionToPart);
|
| 460 |
+
break;
|
| 461 |
+
|
| 462 |
+
case "requirementsgated":
|
| 463 |
+
const currentStory = gameState.themes[gameState.currentTheme]?.story?.[gameState.currentThemeStage];
|
| 464 |
+
const objectives = currentStory?.objectives || [];
|
| 465 |
+
const allComplete = objectives.every(obj => obj.complete);
|
| 466 |
+
|
| 467 |
+
if (allComplete && (target.requirements || 0) > 5) {
|
| 468 |
+
alert(target.collisiontext);
|
| 469 |
+
} else {
|
| 470 |
+
alert("You have not journeyed enough to explore this area yet.");
|
| 471 |
+
}
|
| 472 |
+
break;
|
| 473 |
+
|
| 474 |
+
default:
|
| 475 |
+
console.log("Unknown collision type:", target.collisionType);
|
| 476 |
+
}
|
| 477 |
+
}
|
| 478 |
+
|
| 479 |
+
// ===== SIMPLE MODAL =====
|
| 480 |
+
function openSimpleModal(config) {
|
| 481 |
+
gameState.isSimpleModalOpen = true;
|
| 482 |
+
|
| 483 |
+
elements.simpleModalTitle.textContent = config.title || '';
|
| 484 |
+
elements.simpleModalContent.textContent = config.content || '';
|
| 485 |
+
|
| 486 |
+
// Render actions
|
| 487 |
+
elements.simpleModalActions.innerHTML = '';
|
| 488 |
+
(config.actions || []).forEach(action => {
|
| 489 |
+
const btn = document.createElement('button');
|
| 490 |
+
btn.textContent = action.label;
|
| 491 |
+
btn.addEventListener('click', () => {
|
| 492 |
+
closeSimpleModal();
|
| 493 |
+
executeAction(action.action);
|
| 494 |
+
});
|
| 495 |
+
elements.simpleModalActions.appendChild(btn);
|
| 496 |
+
});
|
| 497 |
+
|
| 498 |
+
elements.simpleModal.classList.add('open');
|
| 499 |
+
}
|
| 500 |
+
|
| 501 |
+
function closeSimpleModal() {
|
| 502 |
+
gameState.isSimpleModalOpen = false;
|
| 503 |
+
elements.simpleModal.classList.remove('open');
|
| 504 |
+
elements.movingDot.focus();
|
| 505 |
+
}
|
| 506 |
+
|
| 507 |
+
// ===== STATE MACHINE MODAL =====
|
| 508 |
+
function openSMModal(modalStates) {
|
| 509 |
+
gameState.isSMModalOpen = true;
|
| 510 |
+
gameState.currentSMModalStates = modalStates;
|
| 511 |
+
|
| 512 |
+
// Get first state key
|
| 513 |
+
const firstStateKey = Object.keys(modalStates)[0] || 'default';
|
| 514 |
+
gameState.currentSMModalState = firstStateKey;
|
| 515 |
+
|
| 516 |
+
renderSMModalState();
|
| 517 |
+
elements.smModal.classList.add('open');
|
| 518 |
+
}
|
| 519 |
+
|
| 520 |
+
function renderSMModalState() {
|
| 521 |
+
const states = gameState.currentSMModalStates;
|
| 522 |
+
const currentState = states[gameState.currentSMModalState];
|
| 523 |
+
|
| 524 |
+
if (!currentState) {
|
| 525 |
+
console.error("State not found:", gameState.currentSMModalState);
|
| 526 |
+
return;
|
| 527 |
+
}
|
| 528 |
+
|
| 529 |
+
elements.smModalTitle.textContent = currentState.title || '';
|
| 530 |
+
elements.smModalContent.textContent = currentState.content || '';
|
| 531 |
+
|
| 532 |
+
// Render actions
|
| 533 |
+
elements.smModalActions.innerHTML = '';
|
| 534 |
+
(currentState.items || []).forEach(item => {
|
| 535 |
+
const btn = document.createElement('button');
|
| 536 |
+
btn.textContent = item.label;
|
| 537 |
+
btn.addEventListener('click', () => handleSMModalAction(item));
|
| 538 |
+
elements.smModalActions.appendChild(btn);
|
| 539 |
+
});
|
| 540 |
+
|
| 541 |
+
// Handle image
|
| 542 |
+
if (currentState.modalimg) {
|
| 543 |
+
elements.smModalImage.src = currentState.modalimg;
|
| 544 |
+
elements.smModalImage.style.display = 'block';
|
| 545 |
+
} else {
|
| 546 |
+
elements.smModalImage.style.display = 'none';
|
| 547 |
+
}
|
| 548 |
+
|
| 549 |
+
// Execute consequences
|
| 550 |
+
(currentState.consequences || []).forEach(consequence => {
|
| 551 |
+
executeAction(consequence);
|
| 552 |
+
});
|
| 553 |
+
}
|
| 554 |
+
|
| 555 |
+
function handleSMModalAction(item) {
|
| 556 |
+
const action = item.action;
|
| 557 |
+
|
| 558 |
+
// Check if action is a state transition
|
| 559 |
+
if (gameState.currentSMModalStates[action]) {
|
| 560 |
+
gameState.currentSMModalState = action;
|
| 561 |
+
renderSMModalState();
|
| 562 |
+
} else {
|
| 563 |
+
// Execute as action and close
|
| 564 |
+
executeAction(action);
|
| 565 |
+
closeSMModal();
|
| 566 |
+
}
|
| 567 |
+
}
|
| 568 |
+
|
| 569 |
+
function closeSMModal() {
|
| 570 |
+
gameState.isSMModalOpen = false;
|
| 571 |
+
gameState.currentSMModalStates = null;
|
| 572 |
+
elements.smModal.classList.remove('open');
|
| 573 |
+
elements.movingDot.focus();
|
| 574 |
+
}
|
| 575 |
+
|
| 576 |
+
// ===== ACTIONS =====
|
| 577 |
+
function executeAction(actionName) {
|
| 578 |
+
if (gameState.themeActions && gameState.themeActions[actionName]) {
|
| 579 |
+
gameState.themeActions[actionName]();
|
| 580 |
+
} else {
|
| 581 |
+
console.log("Action executed:", actionName);
|
| 582 |
+
}
|
| 583 |
+
}
|
| 584 |
+
|
| 585 |
+
// ===== STORY PARTS =====
|
| 586 |
+
function changeStoryPart(partNumber) {
|
| 587 |
+
const theme = gameState.themes[gameState.currentTheme];
|
| 588 |
+
if (!theme) return;
|
| 589 |
+
|
| 590 |
+
gameState.currentThemeStage = partNumber;
|
| 591 |
+
const newStoryPart = theme.story?.find(part => part.part === partNumber);
|
| 592 |
+
|
| 593 |
+
if (newStoryPart) {
|
| 594 |
+
gameState.objectives = newStoryPart.objectives || [];
|
| 595 |
+
gameState.targets = [
|
| 596 |
+
...(theme.persistentTargets || []),
|
| 597 |
+
...(newStoryPart.targets || [])
|
| 598 |
+
];
|
| 599 |
+
renderTargets();
|
| 600 |
+
renderStats();
|
| 601 |
+
}
|
| 602 |
+
}
|
| 603 |
+
|
| 604 |
+
// ===== THEME LOADING =====
|
| 605 |
+
function loadTheme(themeName) {
|
| 606 |
+
const theme = gameState.themes[themeName];
|
| 607 |
+
if (!theme) {
|
| 608 |
+
console.error("Theme not found:", themeName);
|
| 609 |
+
return;
|
| 610 |
+
}
|
| 611 |
+
|
| 612 |
+
gameState.currentTheme = themeName;
|
| 613 |
+
gameState.currentThemeStage = 0;
|
| 614 |
+
|
| 615 |
+
// Set background - supports: URLs, base64 data URIs, or solid colors (#hex/named)
|
| 616 |
+
if (theme.background) {
|
| 617 |
+
const bg = theme.background;
|
| 618 |
+
if (bg.startsWith('#') || bg.startsWith('rgb') || /^[a-z]+$/i.test(bg)) {
|
| 619 |
+
// Solid color
|
| 620 |
+
elements.gameContainer.style.backgroundImage = 'none';
|
| 621 |
+
elements.gameContainer.style.backgroundColor = bg;
|
| 622 |
+
} else {
|
| 623 |
+
// URL or base64
|
| 624 |
+
elements.gameContainer.style.backgroundImage = `url('${bg}')`;
|
| 625 |
+
elements.gameContainer.style.backgroundColor = theme.backgroundColor || '#333';
|
| 626 |
+
}
|
| 627 |
+
}
|
| 628 |
+
|
| 629 |
+
// Load inventory and skills
|
| 630 |
+
gameState.inventory = theme.inventory || [];
|
| 631 |
+
gameState.skills = theme.skills || [];
|
| 632 |
+
gameState.persistentTargets = theme.persistentTargets || [];
|
| 633 |
+
|
| 634 |
+
// Load actions
|
| 635 |
+
gameState.themeActions = theme.actions || {};
|
| 636 |
+
|
| 637 |
+
// Load initial story part
|
| 638 |
+
const initialStory = theme.story?.find(part => part.part === 0);
|
| 639 |
+
if (initialStory) {
|
| 640 |
+
gameState.objectives = initialStory.objectives || [];
|
| 641 |
+
|
| 642 |
+
// Combine persistent and story targets
|
| 643 |
+
const uniqueTargets = [
|
| 644 |
+
...gameState.persistentTargets,
|
| 645 |
+
...(initialStory.targets || []).filter(
|
| 646 |
+
target => !gameState.persistentTargets.some(pt => pt.name === target.name)
|
| 647 |
+
)
|
| 648 |
+
];
|
| 649 |
+
gameState.targets = uniqueTargets;
|
| 650 |
+
} else {
|
| 651 |
+
gameState.objectives = [];
|
| 652 |
+
gameState.targets = [...gameState.persistentTargets];
|
| 653 |
+
}
|
| 654 |
+
|
| 655 |
+
renderAll();
|
| 656 |
+
}
|
| 657 |
+
|
| 658 |
+
// ===== RENDERING =====
|
| 659 |
+
function renderDot() {
|
| 660 |
+
elements.movingDot.style.left = gameState.dotPosition.x + 'px';
|
| 661 |
+
elements.movingDot.style.top = gameState.dotPosition.y + 'px';
|
| 662 |
+
}
|
| 663 |
+
|
| 664 |
+
function renderTargets() {
|
| 665 |
+
elements.targetsContainer.innerHTML = '';
|
| 666 |
+
|
| 667 |
+
gameState.targets.forEach(target => {
|
| 668 |
+
// Target box
|
| 669 |
+
const targetEl = document.createElement('div');
|
| 670 |
+
targetEl.className = 'target';
|
| 671 |
+
targetEl.style.left = target.x + 'px';
|
| 672 |
+
targetEl.style.top = target.y + 'px';
|
| 673 |
+
elements.targetsContainer.appendChild(targetEl);
|
| 674 |
+
|
| 675 |
+
// Label
|
| 676 |
+
const label = document.createElement('span');
|
| 677 |
+
label.className = 'target-label';
|
| 678 |
+
label.style.left = target.x + 'px';
|
| 679 |
+
label.style.top = (target.y - 15) + 'px';
|
| 680 |
+
label.textContent = target.name;
|
| 681 |
+
elements.targetsContainer.appendChild(label);
|
| 682 |
+
});
|
| 683 |
+
}
|
| 684 |
+
|
| 685 |
+
function renderStats() {
|
| 686 |
+
// Objectives
|
| 687 |
+
elements.objectivesList.innerHTML = '';
|
| 688 |
+
gameState.objectives.forEach(obj => {
|
| 689 |
+
const li = document.createElement('li');
|
| 690 |
+
li.className = obj.complete ? 'completed' : '';
|
| 691 |
+
li.textContent = `${obj.id}: ${obj.name}${obj.complete ? ' (Completed)' : ''}`;
|
| 692 |
+
elements.objectivesList.appendChild(li);
|
| 693 |
+
});
|
| 694 |
+
|
| 695 |
+
// Inventory
|
| 696 |
+
elements.inventoryList.innerHTML = '';
|
| 697 |
+
gameState.inventory.forEach(item => {
|
| 698 |
+
const btn = document.createElement('button');
|
| 699 |
+
btn.textContent = item.name;
|
| 700 |
+
btn.addEventListener('click', () => {
|
| 701 |
+
alert(`${item.name}\n${item.description}\nType: ${item.type}`);
|
| 702 |
+
});
|
| 703 |
+
elements.inventoryList.appendChild(btn);
|
| 704 |
+
});
|
| 705 |
+
|
| 706 |
+
// Player Stats
|
| 707 |
+
const stats = gameState.playerStats;
|
| 708 |
+
elements.playerStats.innerHTML = `
|
| 709 |
+
<div>Health: ${stats.health} | Mana: ${stats.mana} | Strength: ${stats.strength}</div>
|
| 710 |
+
<div>Agility: ${stats.agility} | Intelligence: ${stats.intelligence} | Charisma: ${stats.charisma}</div>
|
| 711 |
+
<div>Luck: ${stats.luck} | Money: ${stats.money}</div>
|
| 712 |
+
`;
|
| 713 |
+
|
| 714 |
+
// Skills
|
| 715 |
+
elements.skillsList.innerHTML = '';
|
| 716 |
+
gameState.skills.forEach(skill => {
|
| 717 |
+
const li = document.createElement('li');
|
| 718 |
+
li.textContent = `${skill.name}: ${skill.branch} - Learned: ${skill.learned ? 'Yes' : 'No'}`;
|
| 719 |
+
elements.skillsList.appendChild(li);
|
| 720 |
+
});
|
| 721 |
+
}
|
| 722 |
+
|
| 723 |
+
function updateOverlayText() {
|
| 724 |
+
elements.overlayText.textContent = `Position: ${gameState.dotPosition.x}, ${gameState.dotPosition.y} | Theme: ${gameState.currentTheme} | Stage: ${gameState.currentThemeStage}`;
|
| 725 |
+
}
|
| 726 |
+
|
| 727 |
+
function renderThemeDropdown() {
|
| 728 |
+
elements.themeDropdown.innerHTML = '';
|
| 729 |
+
Object.keys(gameState.themes).forEach(themeName => {
|
| 730 |
+
const option = document.createElement('option');
|
| 731 |
+
option.value = themeName;
|
| 732 |
+
option.textContent = themeName;
|
| 733 |
+
if (themeName === gameState.currentTheme) {
|
| 734 |
+
option.selected = true;
|
| 735 |
+
}
|
| 736 |
+
elements.themeDropdown.appendChild(option);
|
| 737 |
+
});
|
| 738 |
+
}
|
| 739 |
+
|
| 740 |
+
function renderAll() {
|
| 741 |
+
renderDot();
|
| 742 |
+
renderTargets();
|
| 743 |
+
renderStats();
|
| 744 |
+
renderThemeDropdown();
|
| 745 |
+
updateOverlayText();
|
| 746 |
+
}
|
| 747 |
+
|
| 748 |
+
// ===== INVENTORY HELPERS =====
|
| 749 |
+
function addInventoryItem(item) {
|
| 750 |
+
gameState.inventory.push(item);
|
| 751 |
+
renderStats();
|
| 752 |
+
}
|
| 753 |
+
|
| 754 |
+
function removeInventoryItem(itemName) {
|
| 755 |
+
gameState.inventory = gameState.inventory.filter(item => item.name !== itemName);
|
| 756 |
+
renderStats();
|
| 757 |
+
}
|
| 758 |
+
|
| 759 |
+
// ===== EVENT LISTENERS =====
|
| 760 |
+
function setupEventListeners() {
|
| 761 |
+
// Keyboard movement
|
| 762 |
+
elements.movingDot.addEventListener('keydown', handleKeyPress);
|
| 763 |
+
|
| 764 |
+
// Click to focus
|
| 765 |
+
elements.gameContainer.addEventListener('click', () => {
|
| 766 |
+
elements.movingDot.focus();
|
| 767 |
+
});
|
| 768 |
+
|
| 769 |
+
// Theme dropdown
|
| 770 |
+
elements.themeDropdown.addEventListener('change', (e) => {
|
| 771 |
+
loadTheme(e.target.value);
|
| 772 |
+
});
|
| 773 |
+
|
| 774 |
+
// Modal close buttons
|
| 775 |
+
elements.simpleModalClose.addEventListener('click', closeSimpleModal);
|
| 776 |
+
elements.smModalClose.addEventListener('click', closeSMModal);
|
| 777 |
+
|
| 778 |
+
// Click outside modal to close
|
| 779 |
+
elements.simpleModal.addEventListener('click', (e) => {
|
| 780 |
+
if (e.target === elements.simpleModal) closeSimpleModal();
|
| 781 |
+
});
|
| 782 |
+
elements.smModal.addEventListener('click', (e) => {
|
| 783 |
+
if (e.target === elements.smModal) closeSMModal();
|
| 784 |
+
});
|
| 785 |
+
|
| 786 |
+
// Load from gameConfig.json button
|
| 787 |
+
document.getElementById('load-json-btn').addEventListener('click', async () => {
|
| 788 |
+
try {
|
| 789 |
+
const response = await fetch('gameConfig.json');
|
| 790 |
+
if (!response.ok) throw new Error('gameConfig.json not found');
|
| 791 |
+
const config = await response.json();
|
| 792 |
+
initMovingDotSpace(config);
|
| 793 |
+
alert('Loaded ' + Object.keys(config.themes).length + ' themes from gameConfig.json');
|
| 794 |
+
} catch (e) {
|
| 795 |
+
alert("Error loading gameConfig.json: " + e.message);
|
| 796 |
+
}
|
| 797 |
+
});
|
| 798 |
+
|
| 799 |
+
// Paste external config button
|
| 800 |
+
elements.loadExternalBtn.addEventListener('click', () => {
|
| 801 |
+
const configStr = prompt("Paste JSON config:");
|
| 802 |
+
if (configStr) {
|
| 803 |
+
try {
|
| 804 |
+
const config = JSON.parse(configStr);
|
| 805 |
+
initMovingDotSpace(config);
|
| 806 |
+
} catch (e) {
|
| 807 |
+
alert("Invalid JSON: " + e.message);
|
| 808 |
+
}
|
| 809 |
+
}
|
| 810 |
+
});
|
| 811 |
+
|
| 812 |
+
// Background file input
|
| 813 |
+
const bgFileInput = document.getElementById('bg-file-input');
|
| 814 |
+
bgFileInput.addEventListener('change', async (e) => {
|
| 815 |
+
const file = e.target.files[0];
|
| 816 |
+
if (file) {
|
| 817 |
+
await setBackgroundFromFile(file);
|
| 818 |
+
}
|
| 819 |
+
});
|
| 820 |
+
}
|
| 821 |
+
|
| 822 |
+
// ===== GRADIO INTEGRATION =====
|
| 823 |
+
// This function accepts config from Gradio or external sources
|
| 824 |
+
function initMovingDotSpace(config) {
|
| 825 |
+
// Handle single theme or multi-theme config
|
| 826 |
+
if (config.themes) {
|
| 827 |
+
// Multi-theme config
|
| 828 |
+
gameState.themes = config.themes;
|
| 829 |
+
const firstTheme = Object.keys(config.themes)[0];
|
| 830 |
+
if (firstTheme) {
|
| 831 |
+
loadTheme(firstTheme);
|
| 832 |
+
}
|
| 833 |
+
} else if (config.background || config.story) {
|
| 834 |
+
// Single theme config - wrap it
|
| 835 |
+
gameState.themes = { 'Custom': config };
|
| 836 |
+
loadTheme('Custom');
|
| 837 |
+
}
|
| 838 |
+
|
| 839 |
+
renderThemeDropdown();
|
| 840 |
+
}
|
| 841 |
+
|
| 842 |
+
// ===== DEFAULT CONFIG (for standalone testing) =====
|
| 843 |
+
// Background can be: URL path, solid color (#hex or color name), or base64 data URI
|
| 844 |
+
// To use local images, place them in backgrounds/ folder and reference as "backgrounds/filename.png"
|
| 845 |
+
const defaultConfig = {
|
| 846 |
+
themes: {
|
| 847 |
+
'Default': {
|
| 848 |
+
// Use local backgrounds folder - images should be in backgrounds/ subfolder
|
| 849 |
+
background: 'backgrounds/1stGameLoc123.png',
|
| 850 |
+
backgroundColor: '#2d5a27', // Fallback color if image doesn't load
|
| 851 |
+
inventory: [
|
| 852 |
+
{ type: "book", name: "My Memories", description: "Currently I dont remember when I start this journey...." },
|
| 853 |
+
{ type: "weapon", name: "Sword", description: "A sharp blade." },
|
| 854 |
+
{ type: "armor", name: "Shield", description: "Protects against attacks." },
|
| 855 |
+
{ type: "consumable", name: "Health Potion", description: "Restores health." }
|
| 856 |
+
],
|
| 857 |
+
skills: [
|
| 858 |
+
{ branch: "Combat", name: "Basic Attack", learned: false },
|
| 859 |
+
{ branch: "Magic", name: "Fireball", learned: false },
|
| 860 |
+
{ branch: "Stealth", name: "Sneak", learned: false }
|
| 861 |
+
],
|
| 862 |
+
persistentTargets: [],
|
| 863 |
+
story: [
|
| 864 |
+
{
|
| 865 |
+
part: 0,
|
| 866 |
+
objectives: [
|
| 867 |
+
{ id: "Main", name: "Explore the area", complete: false }
|
| 868 |
+
],
|
| 869 |
+
targets: [
|
| 870 |
+
{ name: "Target 1", x: 50, y: 50, collisionType: "alert", collisiontext: "You found Target 1!" },
|
| 871 |
+
{ name: "Target 2", x: 100, y: 100, collisionType: "", collisiontext: "" },
|
| 872 |
+
{ name: "State Machine Demo", x: 600, y: 600, collisionType: "smmodal", modalStates: {
|
| 873 |
+
default: {
|
| 874 |
+
title: "Entrance",
|
| 875 |
+
content: "You've reached the Entrance. What's your next step?",
|
| 876 |
+
items: [
|
| 877 |
+
{ label: "Ask for guidance", action: "guidance" },
|
| 878 |
+
{ label: "Buy an Axe", action: "buyAxe" }
|
| 879 |
+
],
|
| 880 |
+
consequences: []
|
| 881 |
+
},
|
| 882 |
+
guidance: {
|
| 883 |
+
title: "Entrance - Guidance",
|
| 884 |
+
content: "The path to the north leads to the forest.",
|
| 885 |
+
items: [
|
| 886 |
+
{ label: "Head North", action: "forest" },
|
| 887 |
+
{ label: "Return", action: "default" }
|
| 888 |
+
],
|
| 889 |
+
consequences: []
|
| 890 |
+
},
|
| 891 |
+
forest: {
|
| 892 |
+
title: "Forest",
|
| 893 |
+
content: "You went to the forest. Leaves blow everywhere.",
|
| 894 |
+
items: [],
|
| 895 |
+
consequences: ['gainleaf']
|
| 896 |
+
}
|
| 897 |
+
}},
|
| 898 |
+
{ name: "Modal Demo", x: 300, y: 300, collisionType: "modal", modalConfig: {
|
| 899 |
+
title: "Simple Modal",
|
| 900 |
+
content: "This is a simple modal with action buttons.",
|
| 901 |
+
actions: [
|
| 902 |
+
{ label: "Action 1", action: "action1" },
|
| 903 |
+
{ label: "Action 2", action: "action2" }
|
| 904 |
+
]
|
| 905 |
+
}},
|
| 906 |
+
{ name: "Switch to Part 1", x: 700, y: 700, collisionType: "storypartchange", transitionToPart: 1 }
|
| 907 |
+
]
|
| 908 |
+
},
|
| 909 |
+
{
|
| 910 |
+
part: 1,
|
| 911 |
+
objectives: [
|
| 912 |
+
{ id: "Part2", name: "You made it to Part 1!", complete: false }
|
| 913 |
+
],
|
| 914 |
+
targets: [
|
| 915 |
+
{ name: "Part 1 Target", x: 50, y: 50, collisionType: "alert", collisiontext: "Welcome to Part 1!" },
|
| 916 |
+
{ name: "Switch to Part 0", x: 600, y: 400, collisionType: "storypartchange", transitionToPart: 0 }
|
| 917 |
+
]
|
| 918 |
+
}
|
| 919 |
+
],
|
| 920 |
+
actions: {
|
| 921 |
+
action1: function() { alert("Action 1 executed!"); },
|
| 922 |
+
action2: function() { alert("Action 2 executed!"); },
|
| 923 |
+
buyAxe: function() {
|
| 924 |
+
alert("Bought an Axe!");
|
| 925 |
+
addInventoryItem({ type: "weapon", name: "Axe", description: "A sturdy axe." });
|
| 926 |
+
},
|
| 927 |
+
gainleaf: function() {
|
| 928 |
+
addInventoryItem({ type: "misc", name: "Leaf", description: "A random leaf blown by the wind." });
|
| 929 |
+
}
|
| 930 |
+
}
|
| 931 |
+
},
|
| 932 |
+
'Space Odyssey': {
|
| 933 |
+
background: 'backgrounds/SpaceOdysseyGameLoc.png',
|
| 934 |
+
backgroundColor: '#0a1628', // Fallback
|
| 935 |
+
inventory: [
|
| 936 |
+
{ type: "weapon", name: "Laser Gun", description: "A powerful laser weapon." },
|
| 937 |
+
{ type: "tool", name: "Repair Kit", description: "A kit for repairs." }
|
| 938 |
+
],
|
| 939 |
+
skills: [
|
| 940 |
+
{ branch: "Piloting", name: "Astro Navigation", learned: false },
|
| 941 |
+
{ branch: "Engineering", name: "Repair Mastery", learned: false }
|
| 942 |
+
],
|
| 943 |
+
persistentTargets: [
|
| 944 |
+
{ name: "Space Station", x: 200, y: 300, collisionType: "alert", collisiontext: "Welcome to the Space Station!" }
|
| 945 |
+
],
|
| 946 |
+
story: [
|
| 947 |
+
{
|
| 948 |
+
part: 0,
|
| 949 |
+
objectives: [
|
| 950 |
+
{ id: "Space", name: "Explore the cosmos", complete: false }
|
| 951 |
+
],
|
| 952 |
+
targets: [
|
| 953 |
+
{ name: "Alien Planet", x: 500, y: 400, collisionType: "alert", collisiontext: "An alien planet! The inhabitants seem cautious." },
|
| 954 |
+
{ name: "Asteroid Field", x: 800, y: 500, collisionType: "decision", collisiontext: "Navigate through the asteroid field?" }
|
| 955 |
+
]
|
| 956 |
+
}
|
| 957 |
+
],
|
| 958 |
+
actions: {}
|
| 959 |
+
}
|
| 960 |
+
}
|
| 961 |
+
};
|
| 962 |
+
|
| 963 |
+
// ===== HELPER: Convert image file to base64 =====
|
| 964 |
+
// Use this in browser console to convert local images:
|
| 965 |
+
// loadImageAsBase64(file) - pass a File object from input
|
| 966 |
+
function loadImageAsBase64(file) {
|
| 967 |
+
return new Promise((resolve, reject) => {
|
| 968 |
+
const reader = new FileReader();
|
| 969 |
+
reader.onload = () => resolve(reader.result);
|
| 970 |
+
reader.onerror = reject;
|
| 971 |
+
reader.readAsDataURL(file);
|
| 972 |
+
});
|
| 973 |
+
}
|
| 974 |
+
|
| 975 |
+
// Helper to load image from file input and update current theme background
|
| 976 |
+
async function setBackgroundFromFile(file) {
|
| 977 |
+
const base64 = await loadImageAsBase64(file);
|
| 978 |
+
gameState.themes[gameState.currentTheme].background = base64;
|
| 979 |
+
elements.gameContainer.style.backgroundImage = `url('${base64}')`;
|
| 980 |
+
console.log("Background updated! Base64 length:", base64.length);
|
| 981 |
+
}
|
| 982 |
+
|
| 983 |
+
// Expose helpers
|
| 984 |
+
window.loadImageAsBase64 = loadImageAsBase64;
|
| 985 |
+
window.setBackgroundFromFile = setBackgroundFromFile;
|
| 986 |
+
|
| 987 |
+
// ===== INITIALIZATION =====
|
| 988 |
+
function init() {
|
| 989 |
+
setupEventListeners();
|
| 990 |
+
|
| 991 |
+
// Load default config
|
| 992 |
+
initMovingDotSpace(defaultConfig);
|
| 993 |
+
|
| 994 |
+
// Focus the dot
|
| 995 |
+
setTimeout(() => elements.movingDot.focus(), 100);
|
| 996 |
+
}
|
| 997 |
+
|
| 998 |
+
// Start on DOM ready
|
| 999 |
+
if (document.readyState === 'loading') {
|
| 1000 |
+
document.addEventListener('DOMContentLoaded', init);
|
| 1001 |
+
} else {
|
| 1002 |
+
init();
|
| 1003 |
+
}
|
| 1004 |
+
|
| 1005 |
+
// Expose for Gradio / external use
|
| 1006 |
+
window.initMovingDotSpace = initMovingDotSpace;
|
| 1007 |
+
window.gameState = gameState;
|
| 1008 |
+
window.addInventoryItem = addInventoryItem;
|
| 1009 |
+
window.removeInventoryItem = removeInventoryItem;
|
| 1010 |
+
|
| 1011 |
+
/* CONFIG_PLACEHOLDER */
|
| 1012 |
+
</script>
|
| 1013 |
+
</body>
|
| 1014 |
+
</html>
|
MovingDotSpaceExport/backgrounds/1stGameLoc123.png
ADDED
|
Git LFS Details
|
MovingDotSpaceExport/backgrounds/2ndGameLoc123.png
ADDED
|
Git LFS Details
|
MovingDotSpaceExport/backgrounds/ConstructionGameLoc.png
ADDED
|
Git LFS Details
|
MovingDotSpaceExport/backgrounds/CyberpunkGameLoc.png
ADDED
|
Git LFS Details
|
MovingDotSpaceExport/backgrounds/MedievalFantasyGameLoc.png
ADDED
|
Git LFS Details
|
MovingDotSpaceExport/backgrounds/SpaceOdysseyGameLoc.png
ADDED
|
Git LFS Details
|
MovingDotSpaceExport/backgrounds/SuperTeacherGameLoc.png
ADDED
|
Git LFS Details
|
MovingDotSpaceExport/backgrounds/eldoria_background.png
ADDED
|
Git LFS Details
|
MovingDotSpaceExport/gameConfig.json
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"themes": {
|
| 3 |
+
"User Custom - Template": {
|
| 4 |
+
"background": "backgrounds/theme_background.png",
|
| 5 |
+
"inventory": [
|
| 6 |
+
{ "type": "weapon", "name": "Random weapon", "description": "A powerful weapon." }
|
| 7 |
+
],
|
| 8 |
+
"skills": [
|
| 9 |
+
{ "branch": "Skill Group 1", "name": "Skill One", "learned": false }
|
| 10 |
+
],
|
| 11 |
+
"persistentTargets": [
|
| 12 |
+
{ "name": "Background Target 1", "x": 500, "y": 500, "collisionType": "alert", "collisiontext": "First Test" }
|
| 13 |
+
],
|
| 14 |
+
"story": [
|
| 15 |
+
{
|
| 16 |
+
"part": 0,
|
| 17 |
+
"objectives": [
|
| 18 |
+
{ "id": 1, "name": "Mission Details", "complete": false, "progress": 0, "inventoryRequired": [] }
|
| 19 |
+
],
|
| 20 |
+
"targets": [
|
| 21 |
+
{ "name": "Target 1", "x": 150, "y": 150, "collisionType": "alert", "collisiontext": "First Test" }
|
| 22 |
+
]
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"part": 1,
|
| 26 |
+
"objectives": [
|
| 27 |
+
{ "id": 2, "name": "Mission Details", "complete": false, "progress": 0, "inventoryRequired": [] }
|
| 28 |
+
],
|
| 29 |
+
"targets": [
|
| 30 |
+
{ "name": "Target 2", "x": 250, "y": 250, "collisionType": "alert", "collisiontext": "First Test" }
|
| 31 |
+
]
|
| 32 |
+
}
|
| 33 |
+
],
|
| 34 |
+
"actions": {}
|
| 35 |
+
},
|
| 36 |
+
"Default": {
|
| 37 |
+
"background": "backgrounds/1stGameLoc123.png",
|
| 38 |
+
"inventory": [
|
| 39 |
+
{ "type": "book", "name": "My Memories", "description": "Currently I dont remember when I start this journey...." },
|
| 40 |
+
{ "type": "weapon", "name": "Sword", "description": "A sharp blade." },
|
| 41 |
+
{ "type": "armor", "name": "Shield", "description": "Protects against attacks." },
|
| 42 |
+
{ "type": "consumable", "name": "Health Potion", "description": "Restores health." }
|
| 43 |
+
],
|
| 44 |
+
"skills": [
|
| 45 |
+
{ "branch": "Combat", "name": "Basic Attack", "learned": false },
|
| 46 |
+
{ "branch": "Magic", "name": "Fireball", "learned": false },
|
| 47 |
+
{ "branch": "Stealth", "name": "Sneak", "learned": false }
|
| 48 |
+
],
|
| 49 |
+
"persistentTargets": [],
|
| 50 |
+
"story": [
|
| 51 |
+
{
|
| 52 |
+
"part": 0,
|
| 53 |
+
"objectives": [],
|
| 54 |
+
"targets": [
|
| 55 |
+
{ "name": "Target 1", "x": 50, "y": 50, "collisionType": "alert", "collisiontext": "First Test",
|
| 56 |
+
"subtargets": [
|
| 57 |
+
{ "name": "Target 1 sub 1", "x": 100, "y": 75, "collisionType": "alert", "collisiontext": "First Test in Sub of First Test" }
|
| 58 |
+
]
|
| 59 |
+
},
|
| 60 |
+
{ "name": "Target 2", "x": 100, "y": 100, "collisionType": "", "collisiontext": "" },
|
| 61 |
+
{ "name": "Target 2 - SM Demo", "x": 600, "y": 600, "collisionType": "smmodal", "modalStates": {
|
| 62 |
+
"default": {
|
| 63 |
+
"title": "Entrance",
|
| 64 |
+
"content": "You've reached the Entrance. What's your next step?",
|
| 65 |
+
"items": [
|
| 66 |
+
{ "label": "Ask for guidance on next move", "action": "guidance" },
|
| 67 |
+
{ "label": "Buy an Axe", "action": "buyAxe" }
|
| 68 |
+
],
|
| 69 |
+
"consequences": [],
|
| 70 |
+
"modalimg": "backgrounds/1stGameLoc123.png"
|
| 71 |
+
},
|
| 72 |
+
"guidance": {
|
| 73 |
+
"title": "Entrance - Guidance",
|
| 74 |
+
"content": "The path to the north leads to the forest, and the path to the south leads to the village.",
|
| 75 |
+
"items": [
|
| 76 |
+
{ "label": "Head North", "action": "forest" },
|
| 77 |
+
{ "label": "Return", "action": "default" }
|
| 78 |
+
],
|
| 79 |
+
"consequences": []
|
| 80 |
+
},
|
| 81 |
+
"forest": {
|
| 82 |
+
"title": "Forest",
|
| 83 |
+
"content": "Went to the forest",
|
| 84 |
+
"items": [],
|
| 85 |
+
"consequences": ["gainleaf"]
|
| 86 |
+
}
|
| 87 |
+
}},
|
| 88 |
+
{ "name": "Entrance", "x": 995, "y": 660, "collisionType": "modal", "modalConfig": {
|
| 89 |
+
"title": "Entrance",
|
| 90 |
+
"content": "You've reached the Entrance. What's your next step?",
|
| 91 |
+
"actions": [
|
| 92 |
+
{ "label": "Ask for guidance on next move", "action": "askforDirections" },
|
| 93 |
+
{ "label": "Buy an Axe", "action": "buyAxeAlert" }
|
| 94 |
+
]
|
| 95 |
+
}},
|
| 96 |
+
{ "name": "Market Stall", "x": 200, "y": 300, "collisionType": "", "collisiontext": "" },
|
| 97 |
+
{ "name": "Inn Entrance", "x": 400, "y": 450, "collisionType": "", "collisiontext": "" },
|
| 98 |
+
{ "name": "Town Hall", "x": 600, "y": 350, "collisionType": "", "collisiontext": "" },
|
| 99 |
+
{ "name": "Fountain", "x": 500, "y": 500, "collisionType": "", "collisiontext": "" },
|
| 100 |
+
{ "name": "Bridge", "x": 1100, "y": 700, "collisionType": "", "collisiontext": "" },
|
| 101 |
+
{ "name": "Waterfall", "x": 1300, "y": 800, "collisionType": "", "collisiontext": "" },
|
| 102 |
+
{ "name": "Mountain Peak", "x": 1500, "y": 100, "collisionType": "", "collisiontext": "" },
|
| 103 |
+
{ "name": "Switch Test 1", "x": 700, "y": 700, "collisionType": "storypartchange", "collisiontext": "First Test", "transitionToPart": 1 }
|
| 104 |
+
]
|
| 105 |
+
},
|
| 106 |
+
{
|
| 107 |
+
"part": 1,
|
| 108 |
+
"objectives": [],
|
| 109 |
+
"targets": [
|
| 110 |
+
{ "name": "Target 1", "x": 50, "y": 50, "collisionType": "alert", "collisiontext": "First Test" },
|
| 111 |
+
{ "name": "Switch Back Test 1", "x": 600, "y": 400, "collisionType": "storypartchange", "collisiontext": "First Test", "transitionToPart": 0 }
|
| 112 |
+
]
|
| 113 |
+
}
|
| 114 |
+
],
|
| 115 |
+
"actions": {}
|
| 116 |
+
},
|
| 117 |
+
"Space Odyssey": {
|
| 118 |
+
"background": "backgrounds/SpaceOdysseyGameLoc.png",
|
| 119 |
+
"inventory": [
|
| 120 |
+
{ "type": "weapon", "name": "Laser Gun", "description": "A powerful laser weapon." },
|
| 121 |
+
{ "type": "tool", "name": "Repair Kit", "description": "A kit for repairing various equipment." },
|
| 122 |
+
{ "type": "tool", "name": "Mining Laser", "description": "A tool for extracting minerals from asteroids." }
|
| 123 |
+
],
|
| 124 |
+
"skills": [
|
| 125 |
+
{ "branch": "Piloting", "name": "Astro Navigation", "learned": false },
|
| 126 |
+
{ "branch": "Engineering", "name": "Repair Mastery", "learned": false }
|
| 127 |
+
],
|
| 128 |
+
"persistentTargets": [
|
| 129 |
+
{ "name": "Space Station", "x": 200, "y": 300, "collisionType": "", "collisiontext": "" },
|
| 130 |
+
{ "name": "Alien Planet", "x": 940, "y": 460, "collisionType": "alert", "collisiontext": "You have arrived at an alien planet. The inhabitants seem cautious but willing to negotiate." },
|
| 131 |
+
{ "name": "Abandoned Spaceship", "x": 460, "y": 570, "collisionType": "alert", "collisiontext": "You have discovered an abandoned spaceship of ancient origin." },
|
| 132 |
+
{ "name": "Colony", "x": 640, "y": 500, "collisionType": "modal", "modalConfig": {
|
| 133 |
+
"title": "Human Colony",
|
| 134 |
+
"content": "You have reached the human colony. Rest and prepare for your upcoming missions.",
|
| 135 |
+
"actions": [
|
| 136 |
+
{ "label": "Request Mission Briefing", "action": "requestBriefing" },
|
| 137 |
+
{ "label": "Purchase Upgrades", "action": "purchaseUpgrades" }
|
| 138 |
+
]
|
| 139 |
+
}},
|
| 140 |
+
{ "name": "Meteor Field", "x": 1100, "y": 470, "collisionType": "modal", "modalConfig": {
|
| 141 |
+
"title": "Dangerous Meteor Field",
|
| 142 |
+
"content": "You have entered a dense meteor field. Navigate carefully.",
|
| 143 |
+
"actions": [
|
| 144 |
+
{ "label": "Activate Shields", "action": "activateShields" },
|
| 145 |
+
{ "label": "Scan for Rare Minerals", "action": "scanMinerals" }
|
| 146 |
+
]
|
| 147 |
+
}}
|
| 148 |
+
],
|
| 149 |
+
"story": [
|
| 150 |
+
{
|
| 151 |
+
"part": 0,
|
| 152 |
+
"objectives": [
|
| 153 |
+
{ "id": 1, "name": "Dock at the Space Station", "complete": false, "progress": 0, "inventoryRequired": [] },
|
| 154 |
+
{ "id": 2, "name": "Repair the Communication Array", "complete": false, "progress": 0, "inventoryRequired": ["Repair Kit"] }
|
| 155 |
+
],
|
| 156 |
+
"targets": [
|
| 157 |
+
{ "name": "Space Station Docking Bay", "x": 500, "y": 400, "collisionType": "alert", "collisiontext": "Welcome to the space station." },
|
| 158 |
+
{ "name": "Communication Room", "x": 800, "y": 200, "collisionType": "alert", "collisiontext": "The communication array needs repair." },
|
| 159 |
+
{ "name": "To Asteroid Field", "x": 900, "y": 100, "collisionType": "storypartchange", "transitionToPart": 1, "collisiontext": "Proceed to investigate the asteroid field." }
|
| 160 |
+
]
|
| 161 |
+
},
|
| 162 |
+
{
|
| 163 |
+
"part": 1,
|
| 164 |
+
"objectives": [
|
| 165 |
+
{ "id": 3, "name": "Collect Rare Asteroid Minerals", "complete": false, "progress": 0, "inventoryRequired": ["Mining Laser"] }
|
| 166 |
+
],
|
| 167 |
+
"targets": [
|
| 168 |
+
{ "name": "Asteroid Field", "x": 1200, "y": 300, "collisionType": "alert", "collisiontext": "Scan asteroids for rare minerals." },
|
| 169 |
+
{ "name": "Alien Encounter", "x": 1250, "y": 350, "collisionType": "alert", "collisiontext": "An alien ship approaches." },
|
| 170 |
+
{ "name": "To Alien Homeworld", "x": 1100, "y": 200, "collisionType": "storypartchange", "transitionToPart": 2, "collisiontext": "Travel to the alien homeworld." }
|
| 171 |
+
]
|
| 172 |
+
},
|
| 173 |
+
{
|
| 174 |
+
"part": 2,
|
| 175 |
+
"objectives": [
|
| 176 |
+
{ "id": 4, "name": "Negotiate Peace with the Aliens", "complete": false, "progress": 0, "inventoryRequired": [] },
|
| 177 |
+
{ "id": 5, "name": "Gather Alien Artifacts", "complete": false, "progress": 0, "inventoryRequired": ["Artifact Scanner"] }
|
| 178 |
+
],
|
| 179 |
+
"targets": [
|
| 180 |
+
{ "name": "Alien Council Chamber", "x": 600, "y": 250, "collisionType": "modal", "modalConfig": {
|
| 181 |
+
"title": "Alien Council Chamber",
|
| 182 |
+
"content": "Present your case for peace.",
|
| 183 |
+
"actions": [
|
| 184 |
+
{ "label": "Present Peace Offer", "action": "presentPeaceOffer" },
|
| 185 |
+
{ "label": "Listen to Alien Concerns", "action": "listenConcerns" }
|
| 186 |
+
]
|
| 187 |
+
}},
|
| 188 |
+
{ "name": "Alien Ruins", "x": 750, "y": 400, "collisionType": "alert", "collisiontext": "Ancient alien ruins discovered." },
|
| 189 |
+
{ "name": "Return to Human Colony", "x": 800, "y": 150, "collisionType": "storypartchange", "transitionToPart": 3, "collisiontext": "Return to share findings." }
|
| 190 |
+
]
|
| 191 |
+
}
|
| 192 |
+
],
|
| 193 |
+
"actions": {}
|
| 194 |
+
},
|
| 195 |
+
"Medieval Fantasy": {
|
| 196 |
+
"background": "backgrounds/MedievalFantasyGameLoc.png",
|
| 197 |
+
"inventory": [
|
| 198 |
+
{ "type": "weapon", "name": "Longsword", "description": "A sturdy steel blade." }
|
| 199 |
+
],
|
| 200 |
+
"skills": [
|
| 201 |
+
{ "branch": "Piloting", "name": "Astro Navigation", "learned": false }
|
| 202 |
+
],
|
| 203 |
+
"persistentTargets": [
|
| 204 |
+
{ "name": "Market Stall", "x": 200, "y": 300, "collisionType": "", "collisiontext": "" },
|
| 205 |
+
{ "name": "Inn Entrance", "x": 400, "y": 450, "collisionType": "", "collisiontext": "" },
|
| 206 |
+
{ "name": "Castle", "x": 940, "y": 460, "collisionType": "alert", "collisiontext": "The King summons you to find and release his champion." },
|
| 207 |
+
{ "name": "Hidden Space", "x": 460, "y": 570, "collisionType": "alert", "collisiontext": "A hidden space. Nobody is here." },
|
| 208 |
+
{ "name": "Home", "x": 640, "y": 500, "collisionType": "modal", "modalConfig": {
|
| 209 |
+
"title": "Your Space",
|
| 210 |
+
"content": "Rest and prepare for your tasks.",
|
| 211 |
+
"actions": [
|
| 212 |
+
{ "label": "Ask for guidance", "action": "askforDirections" },
|
| 213 |
+
{ "label": "Buy an Axe", "action": "buyAxeAlert" }
|
| 214 |
+
]
|
| 215 |
+
}},
|
| 216 |
+
{ "name": "Marketplace", "x": 1100, "y": 470, "collisionType": "modal", "modalConfig": {
|
| 217 |
+
"title": "Marketplace",
|
| 218 |
+
"content": "A bustling marketplace.",
|
| 219 |
+
"actions": [
|
| 220 |
+
{ "label": "Ask for directions", "action": "askforDirections" },
|
| 221 |
+
{ "label": "Buy an Axe", "action": "buyAxeAlert" },
|
| 222 |
+
{ "label": "Ask about the knight", "action": "knightalert" }
|
| 223 |
+
]
|
| 224 |
+
}},
|
| 225 |
+
{ "name": "Mini Waterfall", "x": 500, "y": 640, "collisionType": "alert", "collisiontext": "A calming waterfall. There's a trail of water leading nearby." }
|
| 226 |
+
],
|
| 227 |
+
"story": [
|
| 228 |
+
{
|
| 229 |
+
"part": 0,
|
| 230 |
+
"objectives": [
|
| 231 |
+
{ "id": 1, "name": "Investigate the Disappearances", "complete": false, "progress": 0, "inventoryRequired": [] },
|
| 232 |
+
{ "id": 6, "name": "Rescue the Captured Knight", "complete": false, "progress": 0, "inventoryRequired": ["Longsword"] }
|
| 233 |
+
],
|
| 234 |
+
"targets": [
|
| 235 |
+
{ "name": "Village Elder's House", "x": 500, "y": 400, "collisionType": "alert", "collisiontext": "The village needs your help investigating disappearances." },
|
| 236 |
+
{ "name": "Mysterious Forest", "x": 800, "y": 200, "collisionType": "alert", "collisiontext": "An eerie silence envelops the forest." },
|
| 237 |
+
{ "name": "To Goblin Bandits", "x": 900, "y": 100, "collisionType": "storypartchange", "transitionToPart": 1, "collisiontext": "Follow the tracks deeper into the forest." }
|
| 238 |
+
]
|
| 239 |
+
},
|
| 240 |
+
{
|
| 241 |
+
"part": 1,
|
| 242 |
+
"objectives": [
|
| 243 |
+
{ "id": 2, "name": "Defeat the Goblin Bandits", "complete": false, "progress": 0, "inventoryRequired": ["Longsword"] }
|
| 244 |
+
],
|
| 245 |
+
"targets": [
|
| 246 |
+
{ "name": "Bandit Camp", "x": 1200, "y": 300, "collisionType": "alert", "collisiontext": "Goblins attack! Prepare for battle!" },
|
| 247 |
+
{ "name": "Prisoner Cages", "x": 1250, "y": 350, "collisionType": "alert", "collisiontext": "You free the villagers. They mention a sorcerer." },
|
| 248 |
+
{ "name": "To Gather Information", "x": 1100, "y": 200, "collisionType": "storypartchange", "transitionToPart": 2, "collisiontext": "Gather information about the sorcerer." }
|
| 249 |
+
]
|
| 250 |
+
}
|
| 251 |
+
],
|
| 252 |
+
"actions": {}
|
| 253 |
+
},
|
| 254 |
+
"Cyberpunk": {
|
| 255 |
+
"background": "backgrounds/CyberpunkGameLoc.png",
|
| 256 |
+
"inventory": [
|
| 257 |
+
{ "type": "weapon", "name": "Plasma Rifle", "description": "A high-tech firearm with plasma rounds." },
|
| 258 |
+
{ "type": "armor", "name": "NanoSuit", "description": "Protects with reactive nano technology." },
|
| 259 |
+
{ "type": "consumable", "name": "Stim Pack", "description": "Enhances reflexes temporarily." }
|
| 260 |
+
],
|
| 261 |
+
"skills": [
|
| 262 |
+
{ "branch": "Hacking", "name": "Cyber Intrusion", "learned": false },
|
| 263 |
+
{ "branch": "Combat", "name": "Gun Kata", "learned": false },
|
| 264 |
+
{ "branch": "Stealth", "name": "Cloaking", "learned": false }
|
| 265 |
+
],
|
| 266 |
+
"persistentTargets": [],
|
| 267 |
+
"story": [
|
| 268 |
+
{
|
| 269 |
+
"part": 0,
|
| 270 |
+
"objectives": [],
|
| 271 |
+
"targets": [
|
| 272 |
+
{ "name": "Target 1", "x": 50, "y": 50, "collisionType": "alert", "collisiontext": "First Test" },
|
| 273 |
+
{ "name": "MegaCorp Server", "x": 200, "y": 50, "collisionType": "alert", "collisiontext": "Corporate server detected" },
|
| 274 |
+
{ "name": "City Police", "x": 50, "y": 250, "collisionType": "alert", "collisiontext": "Law enforcement nearby" },
|
| 275 |
+
{ "name": "Rival Gang", "x": 550, "y": 550, "collisionType": "alert", "collisiontext": "Gang territory" }
|
| 276 |
+
]
|
| 277 |
+
}
|
| 278 |
+
],
|
| 279 |
+
"actions": {}
|
| 280 |
+
},
|
| 281 |
+
"Super Teacher": {
|
| 282 |
+
"background": "backgrounds/SuperTeacherGameLoc.png",
|
| 283 |
+
"inventory": [
|
| 284 |
+
{ "type": "book", "name": "Math Advice", "description": "Useful topical knowledge." },
|
| 285 |
+
{ "type": "book", "name": "Science Advice", "description": "Useful topical knowledge." },
|
| 286 |
+
{ "type": "book", "name": "English Advice", "description": "Useful topical knowledge." },
|
| 287 |
+
{ "type": "book", "name": "Economics Advice", "description": "Useful topical knowledge." }
|
| 288 |
+
],
|
| 289 |
+
"skills": [
|
| 290 |
+
{ "branch": "Multitask", "name": "Movement Speed", "learned": false },
|
| 291 |
+
{ "branch": "Multitask", "name": "Stop Window Interference for 1 min", "learned": false }
|
| 292 |
+
],
|
| 293 |
+
"persistentTargets": [
|
| 294 |
+
{ "name": "Super Teacher Toolkit", "x": 1000, "y": 330, "collisionType": "alert", "collisiontext": "Teaching tools available" }
|
| 295 |
+
],
|
| 296 |
+
"story": [
|
| 297 |
+
{
|
| 298 |
+
"part": 0,
|
| 299 |
+
"objectives": [],
|
| 300 |
+
"targets": [
|
| 301 |
+
{ "name": "Random Noise Interference", "x": 250, "y": 110, "collisionType": "alert", "collisiontext": "Distraction detected" },
|
| 302 |
+
{ "name": "Student 1", "x": 310, "y": 620, "collisionType": "alert", "collisiontext": "Needs help with English" },
|
| 303 |
+
{ "name": "Student 2", "x": 660, "y": 610, "collisionType": "alert", "collisiontext": "Doesn't speak English" },
|
| 304 |
+
{ "name": "Student 3", "x": 1010, "y": 620, "collisionType": "alert", "collisiontext": "Needs help with Math" }
|
| 305 |
+
]
|
| 306 |
+
}
|
| 307 |
+
],
|
| 308 |
+
"actions": {}
|
| 309 |
+
},
|
| 310 |
+
"Fantasy Adventure": {
|
| 311 |
+
"background": "backgrounds/eldoria_background.png",
|
| 312 |
+
"inventory": [
|
| 313 |
+
{ "type": "book", "name": "Book of Eldrak", "description": "An ancient tome with half a map." },
|
| 314 |
+
{ "type": "amulet", "name": "Peculiar Amulet", "description": "Reveals the unseen." },
|
| 315 |
+
{ "type": "map", "name": "Tattered Map", "description": "Hints at Whispering Woods locations." },
|
| 316 |
+
{ "type": "artifact", "name": "Magical Acorn", "description": "Gift from the forest spirit." },
|
| 317 |
+
{ "type": "relic", "name": "Shrine Relic", "description": "Enhances magical abilities." },
|
| 318 |
+
{ "type": "record", "name": "Echoing Cave Echoes", "description": "Reveals secrets and hidden paths." }
|
| 319 |
+
],
|
| 320 |
+
"skills": [
|
| 321 |
+
{ "branch": "Combat", "name": "Sword Mastery", "learned": false },
|
| 322 |
+
{ "branch": "Magic", "name": "Elemental Control", "learned": false },
|
| 323 |
+
{ "branch": "Lore", "name": "Ancient Lore", "learned": true },
|
| 324 |
+
{ "branch": "Stealth", "name": "Eavesdropping", "learned": true },
|
| 325 |
+
{ "branch": "Stealth", "name": "Stealth Movement", "learned": true },
|
| 326 |
+
{ "branch": "Diplomacy", "name": "Negotiation", "learned": true },
|
| 327 |
+
{ "branch": "Magic", "name": "Artifact Mastery", "learned": true }
|
| 328 |
+
],
|
| 329 |
+
"persistentTargets": [
|
| 330 |
+
{ "name": "Lila's Home", "x": 460, "y": 600, "collisionType": "alert", "collisiontext": "A cozy cottage where the quest begins." },
|
| 331 |
+
{ "name": "Eldoria Library", "x": 360, "y": 620, "collisionType": "alert", "collisiontext": "A treasure trove of books and maps." }
|
| 332 |
+
],
|
| 333 |
+
"story": [
|
| 334 |
+
{
|
| 335 |
+
"part": 0,
|
| 336 |
+
"objectives": [],
|
| 337 |
+
"targets": [
|
| 338 |
+
{ "name": "Eldoria Main Square", "x": 410, "y": 590, "collisionType": "requirementsgated", "collisiontext": "The heart of Eldoria.", "requirements": 8 },
|
| 339 |
+
{ "name": "Whispering Forest Edge", "x": 830, "y": 700, "collisionType": "alert", "collisiontext": "The mysterious forest borders Eldoria." },
|
| 340 |
+
{ "name": "Serene Hills", "x": 520, "y": 490, "collisionType": "alert", "collisiontext": "Gentle hills promise adventure." },
|
| 341 |
+
{ "name": "Marketplace", "x": 360, "y": 560, "collisionType": "alert", "collisiontext": "Rumors of ancient artifacts." },
|
| 342 |
+
{ "name": "Eldoria", "x": 490, "y": 420, "collisionType": "storypartchange", "collisiontext": "Your journey begins.", "transitionToPart": 1 },
|
| 343 |
+
{ "name": "Old Sage's Hut", "x": 600, "y": 480, "collisionType": "alert", "collisiontext": "Seek the sage's wisdom." }
|
| 344 |
+
]
|
| 345 |
+
},
|
| 346 |
+
{
|
| 347 |
+
"part": 1,
|
| 348 |
+
"objectives": [],
|
| 349 |
+
"targets": [
|
| 350 |
+
{ "name": "Library of Eldrak", "x": 620, "y": 600, "collisionType": "alert", "collisiontext": "You discover the ancient Book of Eldrak." },
|
| 351 |
+
{ "name": "Meridia Market", "x": 750, "y": 590, "collisionType": "alert", "collisiontext": "A peculiar amulet catches your eye." },
|
| 352 |
+
{ "name": "Suspicious Place", "x": 810, "y": 530, "collisionType": "decision", "collisiontext": "Do you know why you came here?" },
|
| 353 |
+
{ "name": "The Ancient Oak", "x": 300, "y": 130, "collisionType": "modal", "modalConfig": {
|
| 354 |
+
"title": "The Ancient Oak Area",
|
| 355 |
+
"content": "The Ancient Oak stands before you.",
|
| 356 |
+
"actions": [
|
| 357 |
+
{ "label": "Talk to the Ancient Oak", "action": "approachAncientOak" }
|
| 358 |
+
]
|
| 359 |
+
}},
|
| 360 |
+
{ "name": "Magical Acorn", "x": 400, "y": 140, "collisionType": "modal", "modalConfig": {
|
| 361 |
+
"title": "Magical Acorn Area",
|
| 362 |
+
"content": "A spirit appears as you approach the acorn.",
|
| 363 |
+
"actions": [
|
| 364 |
+
{ "label": "Talk to the spirit", "action": "acquireMagicalAcorn" }
|
| 365 |
+
]
|
| 366 |
+
}}
|
| 367 |
+
]
|
| 368 |
+
}
|
| 369 |
+
],
|
| 370 |
+
"actions": {}
|
| 371 |
+
},
|
| 372 |
+
"Construction": {
|
| 373 |
+
"background": "backgrounds/ConstructionGameLoc.png",
|
| 374 |
+
"inventory": [
|
| 375 |
+
{ "type": "tool", "name": "Hammer", "description": "A sturdy hammer for construction." },
|
| 376 |
+
{ "type": "tool", "name": "Screwdriver", "description": "For fastening screws." },
|
| 377 |
+
{ "type": "equipment", "name": "Safety Helmet", "description": "Protects from falling objects." },
|
| 378 |
+
{ "type": "equipment", "name": "Safety Vest", "description": "High-visibility vest." },
|
| 379 |
+
{ "type": "consumable", "name": "Energy Bar", "description": "Provides energy." }
|
| 380 |
+
],
|
| 381 |
+
"skills": [
|
| 382 |
+
{ "branch": "Construction", "name": "Electrical Wiring", "learned": true },
|
| 383 |
+
{ "branch": "Construction", "name": "Concrete Pouring", "learned": true },
|
| 384 |
+
{ "branch": "Safety", "name": "Fall Protection", "learned": false },
|
| 385 |
+
{ "branch": "Safety", "name": "Hazard Recognition", "learned": true },
|
| 386 |
+
{ "branch": "Equipment Operation", "name": "Forklift Certification", "learned": false }
|
| 387 |
+
],
|
| 388 |
+
"persistentTargets": [
|
| 389 |
+
{ "name": "Blueprints", "x": 300, "y": 670, "collisionType": "alert", "collisiontext": "Consult the blueprints." },
|
| 390 |
+
{ "name": "Water Cooler", "x": 490, "y": 650, "collisionType": "alert", "collisiontext": "Take a break and hydrate." }
|
| 391 |
+
],
|
| 392 |
+
"story": [
|
| 393 |
+
{
|
| 394 |
+
"part": 0,
|
| 395 |
+
"objectives": [
|
| 396 |
+
{ "id": "Objective1_1", "name": "Attend Morning Briefing", "complete": false },
|
| 397 |
+
{ "id": "Objective1_2", "name": "Gather Required Tools", "complete": false }
|
| 398 |
+
],
|
| 399 |
+
"targets": [
|
| 400 |
+
{ "name": "Construction Site", "x": 470, "y": 310, "collisionType": "alert", "collisiontext": "You arrive at the construction site." },
|
| 401 |
+
{ "name": "Foreman's Office", "x": 1270, "y": 690, "collisionType": "alert", "collisiontext": "Morning briefing with the foreman." },
|
| 402 |
+
{ "name": "Tool Shed", "x": 430, "y": 540, "collisionType": "alert", "collisiontext": "Gather your tools." },
|
| 403 |
+
{ "name": "Continue Construction", "x": 1100, "y": 520, "collisionType": "storypartchange", "transitionToPart": 1, "collisiontext": "Continue to tasks." }
|
| 404 |
+
]
|
| 405 |
+
},
|
| 406 |
+
{
|
| 407 |
+
"part": 1,
|
| 408 |
+
"objectives": [
|
| 409 |
+
{ "id": "Objective2_1", "name": "Install Electrical Wiring", "complete": false },
|
| 410 |
+
{ "id": "Objective2_2", "name": "Assist with Concrete Pouring", "complete": false },
|
| 411 |
+
{ "id": "Objective2_3", "name": "Attend Safety Training", "complete": false }
|
| 412 |
+
],
|
| 413 |
+
"targets": [
|
| 414 |
+
{ "name": "Electrical Room", "x": 37, "y": 13, "collisionType": "alert", "collisiontext": "Install electrical wiring." },
|
| 415 |
+
{ "name": "Foundation Area", "x": 370, "y": 50, "collisionType": "alert", "collisiontext": "Pour concrete for the foundation." },
|
| 416 |
+
{ "name": "Safety Meeting Point", "x": 530, "y": 160, "collisionType": "alert", "collisiontext": "Safety training session." },
|
| 417 |
+
{ "name": "Lunch Break Area", "x": 820, "y": 540, "collisionType": "alert", "collisiontext": "Lunch break time." }
|
| 418 |
+
]
|
| 419 |
+
},
|
| 420 |
+
{
|
| 421 |
+
"part": 2,
|
| 422 |
+
"objectives": [
|
| 423 |
+
{ "id": "Objective3_1", "name": "Inspect Completed Work", "complete": false },
|
| 424 |
+
{ "id": "Objective3_2", "name": "Clean Up and Secure Site", "complete": false }
|
| 425 |
+
],
|
| 426 |
+
"targets": [
|
| 427 |
+
{ "name": "Inspection Point", "x": 90, "y": 600, "collisionType": "alert", "collisiontext": "Inspect your work." },
|
| 428 |
+
{ "name": "Equipment Storage", "x": 220, "y": 800, "collisionType": "alert", "collisiontext": "Store your equipment." },
|
| 429 |
+
{ "name": "Site Exit", "x": 900, "y": 900, "collisionType": "alert", "collisiontext": "End of day. Good work!" }
|
| 430 |
+
]
|
| 431 |
+
}
|
| 432 |
+
],
|
| 433 |
+
"actions": {}
|
| 434 |
+
}
|
| 435 |
+
}
|
| 436 |
+
}
|