浏览代码

Initial load theme information

Thomas Dy 7 年之前
父节点
当前提交
d983267393
共有 9 个文件被更改,包括 196 次插入72 次删除
  1. 二进制
      dist/decide.wav
  2. 3 1
      dist/index.html
  3. 59 52
      dist/levels.json
  4. 二进制
      dist/select.wav
  5. 31 5
      dist/style.css
  6. 4 1
      src/display.ts
  7. 51 0
      src/game.ts
  8. 3 9
      src/index.ts
  9. 45 4
      src/level.ts

二进制
dist/decide.wav


+ 3 - 1
dist/index.html

@@ -4,7 +4,9 @@
     <link rel="stylesheet" href="style.css" />
   </head>
   <body>
-    <div id="container"></div>
+    <div id="container">
+      <div id="loading">Loading...</div>
+    </div>
     <script type="text/javascript" src="bundle.js"></script>
   </body>
 </html>

+ 59 - 52
dist/levels.json

@@ -1,52 +1,59 @@
-[
-  {
-    "name": "Test",
-    "levels": [
-      {
-        "name": "Jugemu Jugemu",
-        "lines": [
-          {
-            "kanji": "寿限無、寿限無",
-            "kana": "じゅげむじゅげむ"
-          },
-          {
-            "kanji": "五劫の擦り切れ",
-            "kana": "ごこうのすりきれ"
-          },
-          {
-            "kanji": "海砂利水魚の",
-            "kana": "かいじゃりすいぎょうの"
-          },
-          {
-            "kanji": "水行末 雲来末 風来末",
-            "kana": "すいぎょうまつうんらいまつふうらいまつ"
-          },
-          {
-            "kanji": "食う寝る処に住む処",
-            "kana": "くうねるところにすむところ"
-          },
-          {
-            "kanji": "やぶら小路の藪柑子",
-            "kana": "やぶらこうじのぶらこうじ"
-          },
-          {
-            "kanji": "パイポパイポ パイポのシューリンガン",
-            "kana": "ぱいぽぱいぽぱいぽのしゅうりんがん"
-          },
-          {
-            "kanji": "シューリンガンのグーリンダイ",
-            "kana": "しゅうりんがんのぐうりんだい"
-          },
-          {
-            "kanji": "グーリンダイのポンポコピーのポンポコナーの",
-            "kana": "ぐうりんだいのぽんぽこぴいのぽんぽこなあの"
-          },
-          {
-            "kanji": "長久命の長助",
-            "kana": "ちょうきゅうめいのちょうすけ"
-          }
-        ]
-      }
-    ]
-  }
-]
+{
+  "background": "black",
+  "baseColor": "white",
+  "highlightColor": "cyan",
+  "selectSound": "select.wav",
+  "decideSound": "decide.wav",
+  "levelSets": [
+    {
+      "name": "Test",
+      "levels": [
+        {
+          "name": "Jugemu Jugemu",
+          "lines": [
+            {
+              "kanji": "寿限無、寿限無",
+              "kana": "じゅげむじゅげむ"
+            },
+            {
+              "kanji": "五劫の擦り切れ",
+              "kana": "ごこうのすりきれ"
+            },
+            {
+              "kanji": "海砂利水魚の",
+              "kana": "かいじゃりすいぎょうの"
+            },
+            {
+              "kanji": "水行末 雲来末 風来末",
+              "kana": "すいぎょうまつうんらいまつふうらいまつ"
+            },
+            {
+              "kanji": "食う寝る処に住む処",
+              "kana": "くうねるところにすむところ"
+            },
+            {
+              "kanji": "やぶら小路の藪柑子",
+              "kana": "やぶらこうじのぶらこうじ"
+            },
+            {
+              "kanji": "パイポパイポ パイポのシューリンガン",
+              "kana": "ぱいぽぱいぽぱいぽのしゅうりんがん"
+            },
+            {
+              "kanji": "シューリンガンのグーリンダイ",
+              "kana": "しゅうりんがんのぐうりんだい"
+            },
+            {
+              "kanji": "グーリンダイのポンポコピーのポンポコナーの",
+              "kana": "ぐうりんだいのぽんぽこぴいのぽんぽこなあの"
+            },
+            {
+              "kanji": "長久命の長助",
+              "kana": "ちょうきゅうめいのちょうすけ"
+            }
+          ]
+        }
+      ]
+    }
+  ]
+}

二进制
dist/select.wav


+ 31 - 5
dist/style.css

@@ -1,3 +1,22 @@
+#container {
+  --base-color: white;
+  --highlight-color: red;
+
+  position: relative;
+  display: grid;
+  background-color: black;
+  color: var(--base-color);
+  height: 450px;
+  width: 800px;
+  margin: 0 auto;
+}
+
+#container #loading {
+  display: inline-block;
+  align-self: center;
+  justify-self: center;
+}
+
 .kana {
   display: inline-block;
   position: relative;
@@ -10,7 +29,7 @@
   position: absolute;
   left: 0;
   top: 0;
-  color: red;
+  color: var(--highlight-color);
   font-weight: bold;
   overflow: hidden;
   width: 0px;
@@ -50,13 +69,20 @@
 .progress-bar {
   position: relative;
   height: 5px;
-  background-color: lightgrey;
+}
+
+.progress-bar .bg {
+  position: absolute;
+  width: 100%;
+  height: 5px;
+  background-color: var(--base-color);
+  opacity: 0.5;
 }
 
 .progress-bar .shade {
   position: absolute;
   height: 5px;
-  background-color: red;
+  background-color: var(--base-color);
   animation-timing-function: linear;
   animation-play-state: paused;
 }
@@ -70,8 +96,8 @@
   }
 }
 
-.level-control.waiting {
-  color: grey;
+.level-control.waiting span {
+  opacity: 0.5;
 }
 
 .level-control.waiting .romaji {

+ 4 - 1
src/display.ts

@@ -160,7 +160,7 @@ namespace display {
       this.element = document.createElement('div');
       this.kanaController = new KanaDisplayController();
       this.romajiController = new RomajiDisplayController();
-      this.kanjiHTMLElement = document.createElement('p');
+      this.kanjiHTMLElement = document.createElement('span');
 
       this.element.appendChild(this.kanaController.element);
       this.element.appendChild(this.kanjiHTMLElement);
@@ -339,6 +339,9 @@ namespace display {
       this.element.className = 'progress-bar';
       this.barElement = document.createElement('div');
       this.barElement.className = 'shade';
+      let bgElement = document.createElement('div');
+      bgElement.className = 'bg';
+      this.element.appendChild(bgElement);
       this.element.appendChild(this.barElement);
     }
 

+ 51 - 0
src/game.ts

@@ -0,0 +1,51 @@
+/// <reference path="level.ts" />
+/// <reference path="audio.ts" />
+/// <reference path="display.ts" />
+
+namespace game {
+  enum GameState {
+    LOADING,
+    SELECT,
+    PLAYING
+  }
+
+  export class GameController {
+    container: HTMLElement;
+    configUrl: string;
+    config: level.Config | null;
+    audioManager: audio.AudioManager;
+
+    constructor(container: HTMLElement, configUrl: string) {
+      this.container = container;
+      this.configUrl = configUrl;
+      this.audioManager = new audio.AudioManager();
+    }
+
+    stateLoading(): void {
+      let configPromise;
+      if (this.configUrl.endsWith('.json')) {
+        configPromise = level.loadFromJson(this.configUrl);
+      } else {
+        configPromise = level.loadFromTM(this.configUrl);
+      }
+      configPromise.then(config => {
+        this.onConfigLoad(config);
+      })
+    }
+
+    onConfigLoad(config: level.Config): void {
+      this.config = config;
+      let background = config.background;
+      if (background.indexOf('.') >= 0) {
+        background = `url(${background}), black`;
+      }
+      this.container.style.background = background;
+      this.container.style.setProperty('--base-color', config.baseColor);
+      this.container.style.setProperty('--highlight-color', config.highlightColor);
+      this.container.querySelector('#loading').style.opacity = 0;
+
+      let controller = new display.LevelController(this.audioManager, this.config.levelSets[0].levels[0]);
+      container.appendChild(controller.element);
+    }
+  }
+}

+ 3 - 9
src/index.ts

@@ -1,11 +1,5 @@
-/// <reference path="display.ts" />
-/// <reference path="level.ts" />
-/// <reference path="audio.ts" />
+/// <reference path="game.ts" />
 
-let audioManager = new audio.AudioManager();
 let container = document.querySelector('#container');
-
-level.loadFromJson('levels.json').then(levelsets => {
-  let controller = new display.LevelController(audioManager, levelsets[0].levels[0]);
-  container.appendChild(controller.element);
-});
+let controller = new game.GameController(container, 'levels.json');
+controller.stateLoading();

+ 45 - 4
src/level.ts

@@ -25,7 +25,17 @@ namespace level {
     levels: Level[]
   }
 
-  export function loadFromJson(url: string): Promise<LevelSet[]> {
+  export interface Config {
+    background: string,
+    selectMusic?: string,
+    selectSound: string,
+    decideSound: string,
+    baseColor: string,
+    highlightColor: string,
+    levelSets: LevelSet[]
+  }
+
+  export function loadFromJson(url: string): Promise<Config> {
     return window.fetch(url)
       .then(response => response.json())
   }
@@ -39,10 +49,41 @@ namespace level {
     });
   }
 
-  export function loadFromTM(base: string): Promise<LevelSet[]> {
-    return window.fetch(base+'/folderlist.xml')
+  export function loadFromTM(base: string): Promise<Config> {
+    let settingsXML = window.fetch(base+'/settings.xml').then(parseXML);
+    let levelSets = window.fetch(base+'/folderlist.xml')
       .then(parseXML)
-      .then(dom => parseTMFolderList(base, dom))
+      .then(dom => parseTMFolderList(base, dom));
+
+    return Promise.all([settingsXML, levelSets]).then(pair => {
+      return parseTMSettings(base, pair[1], pair[0]);
+    })
+  }
+
+  function parseTMSettings(base: string, levelSets: LevelSet[], dom: Document): Config {
+    function getData(tag: string): string | null {
+      let elem = dom.querySelector(tag);
+      if (elem === null) {
+        return null;
+      } else {
+        return base+'/'+elem.getAttribute('src');
+      }
+    }
+
+    let background = getData('background');
+    let selectMusic = getData('selectmusic');
+    let selectSound = getData('selectsound');
+    let decideSound = getData('decidesound');
+
+    return {
+      background,
+      baseColor: 'white',
+      highlightColor: 'blue',
+      selectMusic,
+      selectSound,
+      decideSound,
+      levelSets
+    }
   }
 
   function parseTMFolderList(base: string, dom: Document): Promise<LevelSet[]> {