diff --git a/PREVIEW/Cover.png b/PREVIEW/Cover.png new file mode 100644 index 0000000..69d5d3f Binary files /dev/null and b/PREVIEW/Cover.png differ diff --git a/PREVIEW/Preview.png b/PREVIEW/Preview.png new file mode 100644 index 0000000..d7a3553 Binary files /dev/null and b/PREVIEW/Preview.png differ diff --git a/PREVIEW/QR code.png b/PREVIEW/QR code.png new file mode 100644 index 0000000..7e5c566 Binary files /dev/null and b/PREVIEW/QR code.png differ diff --git "a/PROJECT/docs/\345\261\261\347\224\260\345\207\211\344\270\212\345\261\261 \345\277\253\351\200\237\345\205\245\351\227\250.pdf" "b/PROJECT/docs/\345\261\261\347\224\260\345\207\211\344\270\212\345\261\261 \345\277\253\351\200\237\345\205\245\351\227\250.pdf" new file mode 100644 index 0000000..ea34308 Binary files /dev/null and "b/PROJECT/docs/\345\261\261\347\224\260\345\207\211\344\270\212\345\261\261 \345\277\253\351\200\237\345\205\245\351\227\250.pdf" differ diff --git "a/PROJECT/docs/\345\261\261\347\224\260\345\207\211\344\270\212\345\261\261 \350\257\264\346\230\216\346\226\207\346\241\243.pdf" "b/PROJECT/docs/\345\261\261\347\224\260\345\207\211\344\270\212\345\261\261 \350\257\264\346\230\216\346\226\207\346\241\243.pdf" new file mode 100644 index 0000000..5f048a9 Binary files /dev/null and "b/PROJECT/docs/\345\261\261\347\224\260\345\207\211\344\270\212\345\261\261 \350\257\264\346\230\216\346\226\207\346\241\243.pdf" differ diff --git a/PROJECT/icons/favicon.ico b/PROJECT/icons/favicon.ico new file mode 100644 index 0000000..9590cf7 Binary files /dev/null and b/PROJECT/icons/favicon.ico differ diff --git a/PROJECT/images/Background.jpg b/PROJECT/images/Background.jpg new file mode 100644 index 0000000..16a25a1 Binary files /dev/null and b/PROJECT/images/Background.jpg differ diff --git a/PROJECT/images/GotouHitori.png b/PROJECT/images/GotouHitori.png new file mode 100644 index 0000000..d28c01a Binary files /dev/null and b/PROJECT/images/GotouHitori.png differ diff --git a/PROJECT/images/Title.png b/PROJECT/images/Title.png new file mode 100644 index 0000000..de5d7e9 Binary files /dev/null and b/PROJECT/images/Title.png differ diff --git a/PROJECT/images/YamadaRyou.png b/PROJECT/images/YamadaRyou.png new file mode 100644 index 0000000..c1fba92 Binary files /dev/null and b/PROJECT/images/YamadaRyou.png differ diff --git a/PROJECT/index.html b/PROJECT/index.html new file mode 100644 index 0000000..83db301 --- /dev/null +++ b/PROJECT/index.html @@ -0,0 +1,2100 @@ + + + + + + + + + + + + + + + + 山田凉上山 + + + + + + + + + + + + + +
+ +
+ +
+ +
+
+

游戏

+
+
+ +
+
+
+ 玩家图像 +
+
+ 追逐者图像 +
+
+
    +
  • + 追逐者图像 + 间距 +
  • +
  • +
+
+
+
+ +
+
+
+
+
+
+
+
    +
  • + 里程 + 里程 +
  • +
  • + 失误 + 失误 +
  • +
  • + 时长 + 时长 +
  • +
  • + 击键速度 + 击键速度 +
  • +
  • + 准确率 + 准确率 +
  • +
  • + 总分 + 总分 +
  • +
+
+
+
    +
  • +
      +
    • +
      + 500 +
    • +
    • +
      +
    • +
    • +
      + 480 +
    • +
    • +
      +
    • +
    • +
      + 460 +
    • +
    • +
      +
    • +
    • +
      + 440 +
    • +
    • +
      +
    • +
    • +
      + 420 +
    • +
    • +
      +
    • +
    • +
      + 400 +
    • +
    • +
      +
    • +
    • +
      + 380 +
    • +
    • +
      +
    • +
    • +
      + 360 +
    • +
    • +
      +
    • +
    • +
      + 340 +
    • +
    • +
      +
    • +
    • +
      + 320 +
    • +
    • +
      +
    • +
    • +
      + 300 +
    • +
    • +
      +
    • +
    • +
      + 280 +
    • +
    • +
      +
    • +
    • +
      + 260 +
    • +
    • +
      +
    • +
    • +
      + 240 +
    • +
    • +
      +
    • +
    • +
      + 220 +
    • +
    • +
      +
    • +
    • +
      + 200 +
    • +
    • +
      +
    • +
    • +
      + 180 +
    • +
    • +
      +
    • +
    • +
      + 160 +
    • +
    • +
      +
    • +
    • +
      + 140 +
    • +
    • +
      +
    • +
    • +
      + 120 +
    • +
    • +
      +
    • +
    • +
      + 100 +
    • +
    • +
      +
    • +
    • +
      + 80 +
    • +
    • +
      +
    • +
    • +
      + 60 +
    • +
    • +
      +
    • +
    • +
      + 40 +
    • +
    • +
      +
    • +
    • +
      + 20 +
    • +
    • +
      +
    • +
    • +
      + 0 +
    • +
    +
  • +
  • +
      +
    • +
      +
      +
    • +
    +
      +
    • +
    • +
    • +
    +
  • +
  • +
      +
    • +
        +
      • +
        + 54321 +
        +
      • +
      • +
        + 1098765432109 +
        +
      • +
      • +
        + 1098765432109 +
        +
      • +
      +
    • +
    • +
    +
  • +
+
+
+
    +
  • +
      +
    • +
      + 30000 +
    • +
    • +
      +
    • +
    • +
      + 29800 +
    • +
    • +
      +
    • +
    • +
      + 29600 +
    • +
    • +
      +
    • +
    • +
      + 29400 +
    • +
    • +
      +
    • +
    • +
      + 29200 +
    • +
    • +
      +
    • +
    • +
      + 29000 +
    • +
    • +
      +
    • +
    • +
      + 28800 +
    • +
    • +
      +
    • +
    • +
      + 28600 +
    • +
    • +
      +
    • +
    • +
      + 28400 +
    • +
    • +
      +
    • +
    • +
      + 28200 +
    • +
    • +
      +
    • +
    • +
      + 28000 +
    • +
    • +
      +
    • +
    • +
      + 27800 +
    • +
    • +
      +
    • +
    • +
      + 27600 +
    • +
    • +
      +
    • +
    • +
      + 27400 +
    • +
    • +
      +
    • +
    • +
      + 27200 +
    • +
    • +
      +
    • +
    • +
      + 27000 +
    • +
    • +
      +
    • +
    • +
      + 26800 +
    • +
    • +
      +
    • +
    • +
      + 26600 +
    • +
    • +
      +
    • +
    • +
      + 26400 +
    • +
    • +
      +
    • +
    • +
      + 26200 +
    • +
    • +
      +
    • +
    • +
      + 26000 +
    • +
    • +
      +
    • +
    • +
      + 25800 +
    • +
    • +
      +
    • +
    • +
      + 25600 +
    • +
    • +
      +
    • +
    • +
      + 25400 +
    • +
    • +
      +
    • +
    • +
      + 25200 +
    • +
    • +
      +
    • +
    • +
      + 25000 +
    • +
    • +
      +
    • +
    • +
      + 24800 +
    • +
    • +
      +
    • +
    • +
      + 24600 +
    • +
    • +
      +
    • +
    • +
      + 24400 +
    • +
    • +
      +
    • +
    • +
      + 24200 +
    • +
    • +
      +
    • +
    • +
      + 24000 +
    • +
    • +
      +
    • +
    • +
      + 23800 +
    • +
    • +
      +
    • +
    • +
      + 23600 +
    • +
    • +
      +
    • +
    • +
      + 23400 +
    • +
    • +
      +
    • +
    • +
      + 23200 +
    • +
    • +
      +
    • +
    • +
      + 23000 +
    • +
    • +
      +
    • +
    • +
      + 22800 +
    • +
    • +
      +
    • +
    • +
      + 22600 +
    • +
    • +
      +
    • +
    • +
      + 22400 +
    • +
    • +
      +
    • +
    • +
      + 22200 +
    • +
    • +
      +
    • +
    • +
      + 22000 +
    • +
    • +
      +
    • +
    • +
      + 21800 +
    • +
    • +
      +
    • +
    • +
      + 21600 +
    • +
    • +
      +
    • +
    • +
      + 21400 +
    • +
    • +
      +
    • +
    • +
      + 21200 +
    • +
    • +
      +
    • +
    • +
      + 21000 +
    • +
    • +
      +
    • +
    • +
      + 20800 +
    • +
    • +
      +
    • +
    • +
      + 20600 +
    • +
    • +
      +
    • +
    • +
      + 20400 +
    • +
    • +
      +
    • +
    • +
      + 20200 +
    • +
    • +
      +
    • +
    • +
      + 20000 +
    • +
    • +
      +
    • +
    • +
      + 19800 +
    • +
    • +
      +
    • +
    • +
      + 19600 +
    • +
    • +
      +
    • +
    • +
      + 19400 +
    • +
    • +
      +
    • +
    • +
      + 19200 +
    • +
    • +
      +
    • +
    • +
      + 19000 +
    • +
    • +
      +
    • +
    • +
      + 18800 +
    • +
    • +
      +
    • +
    • +
      + 18600 +
    • +
    • +
      +
    • +
    • +
      + 18400 +
    • +
    • +
      +
    • +
    • +
      + 18200 +
    • +
    • +
      +
    • +
    • +
      + 18000 +
    • +
    • +
      +
    • +
    • +
      + 17800 +
    • +
    • +
      +
    • +
    • +
      + 17600 +
    • +
    • +
      +
    • +
    • +
      + 17400 +
    • +
    • +
      +
    • +
    • +
      + 17200 +
    • +
    • +
      +
    • +
    • +
      + 17000 +
    • +
    • +
      +
    • +
    • +
      + 16800 +
    • +
    • +
      +
    • +
    • +
      + 16600 +
    • +
    • +
      +
    • +
    • +
      + 16400 +
    • +
    • +
      +
    • +
    • +
      + 16200 +
    • +
    • +
      +
    • +
    • +
      + 16000 +
    • +
    • +
      +
    • +
    • +
      + 15800 +
    • +
    • +
      +
    • +
    • +
      + 15600 +
    • +
    • +
      +
    • +
    • +
      + 15400 +
    • +
    • +
      +
    • +
    • +
      + 15200 +
    • +
    • +
      +
    • +
    • +
      + 15000 +
    • +
    • +
      +
    • +
    • +
      + 14800 +
    • +
    • +
      +
    • +
    • +
      + 14600 +
    • +
    • +
      +
    • +
    • +
      + 14400 +
    • +
    • +
      +
    • +
    • +
      + 14200 +
    • +
    • +
      +
    • +
    • +
      + 14000 +
    • +
    • +
      +
    • +
    • +
      + 13800 +
    • +
    • +
      +
    • +
    • +
      + 13600 +
    • +
    • +
      +
    • +
    • +
      + 13400 +
    • +
    • +
      +
    • +
    • +
      + 13200 +
    • +
    • +
      +
    • +
    • +
      + 13000 +
    • +
    • +
      +
    • +
    • +
      + 12800 +
    • +
    • +
      +
    • +
    • +
      + 12600 +
    • +
    • +
      +
    • +
    • +
      + 12400 +
    • +
    • +
      +
    • +
    • +
      + 12200 +
    • +
    • +
      +
    • +
    • +
      + 12000 +
    • +
    • +
      +
    • +
    • +
      + 11800 +
    • +
    • +
      +
    • +
    • +
      + 11600 +
    • +
    • +
      +
    • +
    • +
      + 11400 +
    • +
    • +
      +
    • +
    • +
      + 11200 +
    • +
    • +
      +
    • +
    • +
      + 11000 +
    • +
    • +
      +
    • +
    • +
      + 10800 +
    • +
    • +
      +
    • +
    • +
      + 10600 +
    • +
    • +
      +
    • +
    • +
      + 10400 +
    • +
    • +
      +
    • +
    • +
      + 10200 +
    • +
    • +
      +
    • +
    • +
      + 10000 +
    • +
    • +
      +
    • +
    • +
      + 9800 +
    • +
    • +
      +
    • +
    • +
      + 9600 +
    • +
    • +
      +
    • +
    • +
      + 9400 +
    • +
    • +
      +
    • +
    • +
      + 9200 +
    • +
    • +
      +
    • +
    • +
      + 9000 +
    • +
    • +
      +
    • +
    • +
      + 8800 +
    • +
    • +
      +
    • +
    • +
      + 8600 +
    • +
    • +
      +
    • +
    • +
      + 8400 +
    • +
    • +
      +
    • +
    • +
      + 8200 +
    • +
    • +
      +
    • +
    • +
      + 8000 +
    • +
    • +
      +
    • +
    • +
      + 7800 +
    • +
    • +
      +
    • +
    • +
      + 7600 +
    • +
    • +
      +
    • +
    • +
      + 7400 +
    • +
    • +
      +
    • +
    • +
      + 7200 +
    • +
    • +
      +
    • +
    • +
      + 7000 +
    • +
    • +
      +
    • +
    • +
      + 6800 +
    • +
    • +
      +
    • +
    • +
      + 6600 +
    • +
    • +
      +
    • +
    • +
      + 6400 +
    • +
    • +
      +
    • +
    • +
      + 6200 +
    • +
    • +
      +
    • +
    • +
      + 6000 +
    • +
    • +
      +
    • +
    • +
      + 5800 +
    • +
    • +
      +
    • +
    • +
      + 5600 +
    • +
    • +
      +
    • +
    • +
      + 5400 +
    • +
    • +
      +
    • +
    • +
      + 5200 +
    • +
    • +
      +
    • +
    • +
      + 5000 +
    • +
    • +
      +
    • +
    • +
      + 4800 +
    • +
    • +
      +
    • +
    • +
      + 4600 +
    • +
    • +
      +
    • +
    • +
      + 4400 +
    • +
    • +
      +
    • +
    • +
      + 4200 +
    • +
    • +
      +
    • +
    • +
      + 4000 +
    • +
    • +
      +
    • +
    • +
      + 3800 +
    • +
    • +
      +
    • +
    • +
      + 3600 +
    • +
    • +
      +
    • +
    • +
      + 3400 +
    • +
    • +
      +
    • +
    • +
      + 3200 +
    • +
    • +
      +
    • +
    • +
      + 3000 +
    • +
    • +
      +
    • +
    • +
      + 2800 +
    • +
    • +
      +
    • +
    • +
      + 2600 +
    • +
    • +
      +
    • +
    • +
      + 2400 +
    • +
    • +
      +
    • +
    • +
      + 2200 +
    • +
    • +
      +
    • +
    • +
      + 2000 +
    • +
    • +
      +
    • +
    • +
      + 1800 +
    • +
    • +
      +
    • +
    • +
      + 1600 +
    • +
    • +
      +
    • +
    • +
      + 1400 +
    • +
    • +
      +
    • +
    • +
      + 1200 +
    • +
    • +
      +
    • +
    • +
      + 1000 +
    • +
    • +
      +
    • +
    • +
      + 800 +
    • +
    • +
      +
    • +
    • +
      + 600 +
    • +
    • +
      +
    • +
    • +
      + 400 +
    • +
    • +
      +
    • +
    • +
      + 200 +
    • +
    • +
      +
    • +
    • +
      + 0 +
    • +
    • +
      +
    • +
    • +
      + -200 +
    • +
    • +
      +
    • +
    • +
      + -400 +
    • +
    • +
      +
    • +
    • +
      + -600 +
    • +
    • +
      +
    • +
    • +
      + -800 +
    • +
    • +
      +
    • +
    • +
      + -1000 +
    • +
    • +
      +
    • +
    • +
      + -1200 +
    • +
    • +
      +
    • +
    • +
      + -1400 +
    • +
    • +
      +
    • +
    • +
      + -1600 +
    • +
    • +
      +
    • +
    • +
      + -1800 +
    • +
    • +
      +
    • +
    • +
      + -2000 +
    • +
    +
  • +
  • +
      +
    • +
        +
      • +
        + 321 +
        +
      • +
      • +
        + 1098765432109 +
        +
      • +
      • +
        + 1098765432109 +
        +
      • +
      • +
        + 2000806040200080 +
        +
      • +
      +
    • +
    • +
    +
  • +
+
+
+
    +
  • + 请点击画面 +
  • + +
+
+
+
    +
  • + + Del +
  • +
+
+
+
+ +
+

高分榜

+ +
+ +
+

文库

+ +
+ +
+

设定

+ +
+ +
+

帮助

+ +
+
+ + + +
+ 正在初始化... +
+ +
+ + + diff --git a/PROJECT/scripts/script.js b/PROJECT/scripts/script.js new file mode 100644 index 0000000..ee32762 --- /dev/null +++ b/PROJECT/scripts/script.js @@ -0,0 +1,1613 @@ +// For SamToki.github.io/Yamanobo-Ryou +// Released under GNU GPL v3 open source license. +// (C) 2024 SAM TOKI STUDIO + +// Initialization + // Declare variables + "use strict"; + // Unsaved + const CurrentVersion = 0.23; + var Game0 = { + Terrain: { + WalkedWidth: 0, + Generation: { + Ahead: 1, Behind: 0 + } + }, + Stats: { + PreviousClockTime: 0, PreviousElapsedTime: 0, + KeystrokeSpeedDisplay: 0, + ScoreDisplay: 0, + SpeedTapeDisplay: 0, PreviousSpeedTapeDisplay: 0, SpeedBalloonDisplay: [0, 0, 0, 0], + SpeedTrendDisplay: 0, AvgSpeedDisplay: 0, ChaserSpeedDisplay: 0, DangerousSpeedDisplay: 0, + AltitudeTapeDisplay: 0, AltitudeBalloonDisplay: [0, 0, 0, 0, 0] + } + }; + Interaction.Deletion = 0; + Automation.ClockGame = null; + + // Saved + var Subsystem = { + Display: { + GameFont: "Monospace" + } + }, + Game = { + Progressing: { + Progressing: "Duration", Duration: 3, TravelDistance: 500, Altitude: 1600 + }, + Difficulty: { + ChaserSpeed: { + Initial: 180, Final: 240 + }, + MaxSeparation: 50 + }, + CustomCharacters: { + PlayerImage: "", ChaserImage: "", + BgImage: "" + }, + Status: { + IsRunning: false, IsPaused: false + }, + Terrain: { + Text: "", + Data: [ + [0, "", 0] + ], + Gradient: 3 // Range: -3 to 6. + }, + PreviousTextboxContent: "", + Stats: { + Progress: 0, + Odometer: 0, ChaserOdometer: 0, TypoCount: 0, + StartTime: 0, ElapsedTime: 0, + KeystrokeCount: 0, KeystrokeSpeed: 0, AvgKeystrokeSpeed: 0, + Accuracy: 0, + Score: 0, + Speed: 0, SpeedTrend: 0, AvgSpeed: 0, ChaserSpeed: 0, DangerousSpeed: 0, + Altitude: 0, + KeystrokeTimestamp: [0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], + TypeTimestamp: [0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] + } + }, + Highscore = [ + 0, + [0, "#1", "", "", "", "", ""], + [0, "#2", "", "", "", "", ""], + [0, "#3", "", "", "", "", ""], + [0, "#4", "", "", "", "", ""], + [0, "#5", "", "", "", "", ""], + [0, "#6", "", "", "", "", ""] + ], + Library = { + Selection: 1, + Text: [ + 0, + { + Name: "[英文] Yamada Ryou", + Content: "Yamada Ryou is one of the main characters in the manga and anime series, Bocchi the Rock!. She is in her second year at Shimokitazawa High School and is the bassist of the band, Kessoku Band. She works a part-time job at the live house STARRY with Ijichi Nijika. She has an androgynous appearance and a mysterious atmosphere, such as silence and expressionlessness. She often fascinates women, including Kita Ikuyo, who falls in love at first sight. She lives with a wealthy family but never has any money because she spends it all on instruments. She occasionally eats weeds to get rid of her hunger. Ryou has short blue hair with two black square hair clips on the right side of her bangs and yellow eyes, with a mole on her left cheek. Ryou's uniform consists of a collared white shirt worn with a black ribbon, a dark blue pleated skirt, black leggings, maroon or brown loafers, and a matching long sleeved sweater. This outfit is similar to most Japanese high school uniforms (seifuku). ", + Language: "en-US", + Source: "Bocchi the Rock Wiki https://bocchi-the-rock.fandom.com/wiki/Ryo_Yamada (CC BY-SA 3.0)" + }, + { + Name: "[英文] Computer keyboard", + Content: "A computer keyboard is a peripheral input device modeled after the typewriter keyboard which uses an arrangement of buttons or keys to act as mechanical levers or electronic switches. Replacing early punched cards and paper tape technology, interaction via teleprinter-style keyboards have been the main input method for computers since the 1970s, supplemented by the computer mouse since the 1980s. Keyboard keys (buttons) typically have a set of characters engraved or printed on them, and each press of a key typically corresponds to a single written symbol. However, producing some symbols may require pressing and holding several keys simultaneously or in sequence. While most keys produce characters (letters, numbers or symbols), other keys (such as the escape key) can prompt the computer to execute system commands. In a modern computer, the interpretation of key presses is generally left to the software: the information sent to the computer, the scan code, tells it only which physical key (or keys) was pressed or released. ", + Language: "en-US", + Source: "Wikipedia https://en.wikipedia.org/wiki/Computer_keyboard (CC BY-SA 4.0)" + }, + { + Name: "[简体中文] 贝斯", + Content: "贝斯,又称低音吉他,弹拨乐器。通常有四根弦,但也有五弦、六弦甚至更多弦的。贝斯的定弦方式来源于低音提琴,原理结构来源于吉他。当然,无品贝斯则在一定程度上又回归了同样无品的低音提琴。一般地,我们口头上的“狭义”的贝斯指的是电贝斯,通常与电吉他很相似,但琴颈比吉他要长得多,重量则略重一些,四弦和五弦贝斯的琴头比电吉他窄。贝斯的音阶处于低音位置。琴弦相当粗,弦张力相当大,拨弦的力量要比吉他大得多。广义上的贝斯则还包括了木贝斯,即原音贝斯,但很少见。现实世界中被用来充当木贝斯的一般是拨弦低音提琴,俗称“大贝斯”。不过低音提琴与其他提琴一样没有品格,并且拨奏时声音亦非常短促,并不能完全取代有品格的,形似木吉他的原声贝斯。且电贝斯相较于原声贝斯,需要通过拾音器来向音箱传输声音。电贝斯的拾音器又能分为使用P型拾音器的P贝斯,使用J型拾音器的J贝斯,同时使用P型和J型拾音器的PJ贝斯,还有双线圈拾音器贝斯等等诸多种类。贝斯在乐队中主要担任低音声部,是一个非常重要的角色,常被称为是乐队的灵魂。贝斯的奇妙之处在于控制整个乐队的律动。", + Language: "zh-CN", + Source: "萌娘百科 https://zh.moegirl.org.cn/%E8%B4%9D%E6%96%AF (CC BY-NC-SA 3.0)" + }, + { + Name: "[简体中文] 涡轮风扇发动机", + Content: "涡轮风扇发动机是一种燃气涡轮式航空发动机,主要特点是其首级扇叶的面积较涡轮喷气发动机大上许多。经过涡轮喷气发动机的空气通道称为内涵道,空气在喷气发动机燃烧后获得机械能。外侧的空气通道称为外涵道,由内含的涡轮驱动首级增压扇叶推动空气,增压扇叶同时具有螺旋桨和压缩空气的作用,能将部分吸入的空气通过喷气发动机的外围提供直接推力,推力即由内外涵道共同产生。可同时具有涡轮螺旋桨与涡轮喷气发动机的推力供给。涡轮风扇发动机外观上似乎是更粗的发动机,实际上只有中心是喷气发动机,其他全是扇风推进。因为并非只依靠涡轮喷气直接燃油燃烧出高压空气提供推力,所以单位推力小时耗油率比纯涡轮喷气佳,但螺旋扇叶速度越快的推力重量比耗损功率较大,比较纯涡轮喷气,超音速飞行时其受到附面层影响使其低气压高空与超音速下推力重量不佳。于是乎,涡扇发动机最适合飞行速度为每小时400至2000公里时使用,故此现在多数的喷气机发动机都是采用涡扇发动机作为动力来源。", + Language: "zh-CN", + Source: "Wikipedia https://zh.wikipedia.org/zh-cn/%E6%B8%A6%E8%BC%AA%E6%89%87%E7%99%BC%E5%8B%95%E6%A9%9F (CC BY-SA 4.0)" + }, + { + Name: "[日文] 電車", + Content: "電車は、鉄道車両のうち、電気を動力として自走する事が可能な客車や貨車の総称である。すなわち、客車や貨車そのものに動力が備わっており、機関車なしで自走可能な「電動客車」および「電動貨車」を指す。「電気列車」または「電動列車」とも呼ばれる。電車のうち、動力を持つ車両は電動車、動力を持たず電動車と編成を組む車両は付随車。また、運転席のある付随車は制御車、電動車に運転席のある物は制御電動車と呼称する。電動機を駆動する電力は、集電装置により外部から取り込む場合と、車載の蓄電池から供給する場合の2通りがある。車上の内燃機関で発電機を稼動させ、得られた電力で電動機を駆動する車両は電気式気動車と呼ばれ「電車」には含まれない。また、電気を動力にする鉄道車両としては電気機関車もあるが、これも電車には含まれない。また、電気機関車に牽引される客車や貨車も電車には含まれない。もともと「電車」は、自走式の電動機付き客車「電動客車」、および事業用車を含む電動機付き貨車「電動貨車など」の略称だったが、現在では一般名詞となり、各省庁をはじめ、運輸事業者や車両製造会社でも正式に用いられている。", + Language: "ja-JP", + Source: "Wikipedia https://ja.wikipedia.org/wiki/%E9%9B%BB%E8%BB%8A (CC BY-SA 4.0)" + }, + { + Name: "[测试用] osu!", + Content: "zxzxzxzxzx", + Language: "en-US", + Source: "" + } + ] + }; + + // Load user data + window.onload = Load(); + function Load() { + if(localStorage.System != undefined) { + System = JSON.parse(localStorage.getItem("System")); + } else { + System.I18n.Language = "zh-CN"; + } + switch(System.I18n.Language) { + case "en-US": + ShowDialog("System_LanguageUnsupported", + "Error", + "Sorry, this page currently does not support English (US).", + "", "", "", "OK"); + break; + case "ja-JP": + ShowDialog("System_LanguageUnsupported", + "Error", + "すみません。このページは日本語にまだサポートしていません。", + "", "", "", "OK"); + break; + case "zh-CN": + /* ChangeCursorOverall("wait"); + window.location.replace("index.html"); */ + break; + case "zh-TW": + ShowDialog("System_LanguageUnsupported", + "Error", + "抱歉,本頁面暫不支援繁體中文。", + "", "", "", "確定"); + break; + default: + AlertSystemError("The value of System.I18n.Language \"" + System.I18n.Language + "\" in function Load is invalid."); + break; + } + if(System.Version.YamanoboRyou != undefined) { + if(Math.floor(CurrentVersion) - Math.floor(System.Version.YamanoboRyou) >= 1) { + ShowDialog("System_MajorUpdateDetected", + "Info", + "检测到大版本更新。若您继续使用旧版本的用户数据,则有可能发生兼容性问题。敬请留意。", + "", "", "", "确定"); + System.Version.YamanoboRyou = CurrentVersion; + } + } else { + System.Version.YamanoboRyou = CurrentVersion; + } + RefreshSystem(); + if(localStorage.YamanoboRyou_Subsystem != undefined) { + Subsystem = JSON.parse(localStorage.getItem("YamanoboRyou_Subsystem")); + } + RefreshSubsystem(); + if(localStorage.YamanoboRyou_Game != undefined) { + Game = JSON.parse(localStorage.getItem("YamanoboRyou_Game")); + Game0.Terrain.Generation = { + Ahead: Game.Stats.Odometer + 1, Behind: Game.Stats.Odometer + }; + } + // RefreshGame(); // There are "Library" usages in function RefreshGame. + if(localStorage.YamanoboRyou_Highscore != undefined) { + Highscore = JSON.parse(localStorage.getItem("YamanoboRyou_Highscore")); + } + RefreshHighscore(); + if(localStorage.YamanoboRyou_Library != undefined) { + Library = JSON.parse(localStorage.getItem("YamanoboRyou_Library")); + } + ChangeValue("Textbox_LibraryFilter", ""); + ChangeValue("Textbox_LibraryImport", ""); + ChangeText("Ctnr_GameTerrain", ""); + RefreshGame(); + if(Game.Status.IsRunning == true) { // "RefreshGame" does not refresh the library when the game is running. + RefreshLibrary(); + } + Focus("Textbox_Game"); + setTimeout(HideToast, 0); + } + +// Refresh + // System + function RefreshSystem() { + // Settings + // Display + if(window.matchMedia("(prefers-contrast: more)").matches == false) { + ChangeDisabled("Combobox_SettingsTheme", false); + } else { + System.Display.Theme = "HighContrast"; + ChangeDisabled("Combobox_SettingsTheme", true); + } + ChangeValue("Combobox_SettingsTheme", System.Display.Theme); + switch(System.Display.Theme) { + case "Auto": + ChangeLink("ThemeVariant_Common", "../styles/common_Dark.css"); + ChangeMediaCondition("ThemeVariant_Common", "(prefers-color-scheme: dark)"); + ChangeLink("ThemeVariant_Style", "styles/style_Dark.css"); + ChangeMediaCondition("ThemeVariant_Style", "(prefers-color-scheme: dark)"); + break; + case "Light": + ChangeLink("ThemeVariant_Common", ""); + ChangeMediaCondition("ThemeVariant_Common", ""); + ChangeLink("ThemeVariant_Style", ""); + ChangeMediaCondition("ThemeVariant_Style", ""); + break; + case "Dark": + ChangeLink("ThemeVariant_Common", "../styles/common_Dark.css"); + ChangeMediaCondition("ThemeVariant_Common", ""); + ChangeLink("ThemeVariant_Style", "styles/style_Dark.css"); + ChangeMediaCondition("ThemeVariant_Style", ""); + break; + case "Genshin": + ChangeLink("ThemeVariant_Common", "../styles/common_Genshin.css"); + ChangeMediaCondition("ThemeVariant_Common", ""); + ChangeLink("ThemeVariant_Style", "styles/style_Genshin.css"); + ChangeMediaCondition("ThemeVariant_Style", ""); + break; + case "HighContrast": + ChangeLink("ThemeVariant_Common", "../styles/common_HighContrast.css"); + ChangeMediaCondition("ThemeVariant_Common", ""); + ChangeLink("ThemeVariant_Style", "styles/style_HighContrast.css"); + ChangeMediaCondition("ThemeVariant_Style", ""); + break; + default: + AlertSystemError("The value of System.Display.Theme \"" + System.Display.Theme + "\" in function RefreshSystem is invalid."); + break; + } + ChangeValue("Combobox_SettingsCursor", System.Display.Cursor); + switch(System.Display.Cursor) { + case "Default": + ChangeCursorOverall(""); + break; + case "BTRAhoge": + case "Genshin": + case "GenshinFurina": + case "GenshinNahida": + ChangeCursorOverall("url(../cursors/" + System.Display.Cursor + ".cur), auto"); + break; + default: + AlertSystemError("The value of System.Display.Cursor \"" + System.Display.Cursor + "\" in function RefreshSystem is invalid."); + break; + } + ChangeChecked("Checkbox_SettingsBlurBgImage", System.Display.BlurBgImage); + if(System.Display.BlurBgImage == true) { + ChangeFilter("Ctnr_BgImage", "blur(20px)"); + } else { + ChangeFilter("Ctnr_BgImage", ""); + } + ChangeChecked("Checkbox_SettingsShowTopbar", System.Display.ShowTopbar); + if(System.Display.ShowTopbar == true && IsFullscreen() == false) { + Show("Topbar"); + } else { + Hide("Topbar"); + } + ChangeValue("Combobox_SettingsHotkeyIndicators", System.Display.HotkeyIndicators); + switch(System.Display.HotkeyIndicators) { + case "Disabled": + FadeHotkeyIndicators(); + break; + case "ShowOnWrongKeyPress": + case "ShowOnAnyKeyPress": + break; + case "AlwaysShow": + ShowHotkeyIndicators(); + break; + default: + AlertSystemError("The value of System.Display.HotkeyIndicators \"" + System.Display.HotkeyIndicators + "\" in function RefreshSystem is invalid."); + break; + } + if(window.matchMedia("(prefers-reduced-motion: reduce)").matches == false) { + ChangeDisabled("Combobox_SettingsAnim", false); + } else { + System.Display.Anim = 0; + ChangeDisabled("Combobox_SettingsAnim", true); + } + ChangeValue("Combobox_SettingsAnim", System.Display.Anim); + ChangeAnimOverall(System.Display.Anim); + + // Dev + ChangeChecked("Checkbox_SettingsTryToOptimizePerformance", System.Dev.TryToOptimizePerformance); + if(System.Dev.TryToOptimizePerformance == true) { + AddClass("Html", "PerformanceOptimization"); + } else { + RemoveClass("Html", "PerformanceOptimization"); + } + ChangeChecked("Checkbox_SettingsShowDebugOutlines", System.Dev.ShowDebugOutlines); + ChangeShowDebugOutlines(System.Dev.ShowDebugOutlines); + ChangeChecked("Checkbox_SettingsUseOldTypeface", System.Dev.UseOldTypeface); + if(System.Dev.UseOldTypeface == true) { + ChangeLanguage("Html", "ja-JP"); + } else { + ChangeLanguage("Html", "zh-CN"); + } + ChangeValue("Textbox_SettingsFont", System.Dev.Font); + ChangeFont("Html", System.Dev.Font); + + // User data + ChangeValue("Textbox_SettingsUserDataImport", ""); + + // Save user data + localStorage.setItem("System", JSON.stringify(System)); + } + function RefreshSubsystem() { + // Settings + // Display + ChangeValue("Combobox_SettingsGameFont", Subsystem.Display.GameFont); + switch(Subsystem.Display.GameFont) { + case "Default": + ChangeFont("Ctnr_GameTerrain", ""); + ChangeFont("Textbox_Game", ""); + break; + case "Sans": + case "Serif": + case "Monospace": + ChangeFont("Ctnr_GameTerrain", Subsystem.Display.GameFont.toLowerCase()); + ChangeFont("Textbox_Game", Subsystem.Display.GameFont.toLowerCase()); + break; + default: + AlertSystemError("The value of Subsystem.Display.GameFont \"" + Subsystem.Display.GameFont + "\" in function RefreshSubsystem is invalid."); + break; + } + + // Save user data + localStorage.setItem("YamanoboRyou_Subsystem", JSON.stringify(Subsystem)); + } + + // Game + function ClockGame() { + // Update times (High priority) + if(Game.Status.IsRunning == true) { + if(Game.Status.IsPaused == false) { + Game.Stats.ElapsedTime = Date.now() - Game.Stats.StartTime; + } else { + Game.Stats.StartTime = Date.now() - Game.Stats.ElapsedTime; + } + } + + // Movement + // Player + RemoveClass("Textbox_Game", "HasTypo"); + if(ReadValue("Textbox_Game") != "") { + // Start or continue game + if(Game.Status.IsRunning == false) { + Game.Status.IsRunning = true; + Game.Stats.StartTime = Date.now(); + setTimeout(RefreshGame, 0); + } else { + if(Game.Status.IsPaused == true) { + Game.Status.IsPaused = false; + Game.Stats.StartTime = Date.now() - Game.Stats.ElapsedTime; + setTimeout(RefreshGame, 0); + } + } + + // Check typed text + if(Interaction.IsInIMEComposition == false) { + if(ReadValue("Textbox_Game").charAt(0) == Game.Terrain.Text.charAt(Game.Stats.Odometer)) { + Game.Stats.Odometer++; + if(IsElementExisting("Terrain_Game" + Game.Stats.Odometer) == true) { + Game0.Terrain.WalkedWidth += ReadWidth("Terrain_Game" + Game.Stats.Odometer); + } + Game.Stats.TypeTimestamp.splice(1, 1); + Game.Stats.TypeTimestamp[21] = Game.Stats.ElapsedTime; + Game.Stats.Score += Math.floor(Math.pow(2, (Game.Stats.TypeTimestamp[20] - Game.Stats.TypeTimestamp[21]) / 100) * 100); + if(Game.Stats.Score > 99999999) { + Game.Stats.Score = 99999999; + } + if(IsElementExisting("Terrain_Game" + Game.Stats.Odometer) == true) { + RemoveClass("Terrain_Game" + Game.Stats.Odometer, "Ahead"); + } + ChangeValue("Textbox_Game", ReadValue("Textbox_Game").substring(1)); + } else { + if(Game.PreviousTextboxContent.startsWith(ReadValue("Textbox_Game")) == false) { + Game.Stats.TypoCount++; + } + AddClass("Textbox_Game", "HasTypo"); + } + } + Game.PreviousTextboxContent = ReadValue("Textbox_Game"); + } + + // Chaser + if(Game.Stats.Odometer > 20) { + Game.Stats.ChaserSpeed = Game.Difficulty.ChaserSpeed.Initial + (Game.Difficulty.ChaserSpeed.Final - Game.Difficulty.ChaserSpeed.Initial) * (Game.Stats.Progress / 100); + } else { + Game.Stats.ChaserSpeed = 0; + } + if(Game.Status.IsRunning == true && Game.Status.IsPaused == false) { + Game.Stats.ChaserOdometer += Game.Stats.ChaserSpeed / 60000 * (Game.Stats.ElapsedTime - Game0.Stats.PreviousElapsedTime); + if(Game.Stats.Odometer - Game.Stats.ChaserOdometer > Game.Difficulty.MaxSeparation) { + Game.Stats.ChaserOdometer = Game.Stats.Odometer - Game.Difficulty.MaxSeparation; + } + } + + // Terrain + // Generate text + if(Library.Text[Library.Selection].Content != "") { + while(Game.Terrain.Text.length - Game.Stats.Odometer < 100) { + Game.Terrain.Text += Library.Text[Library.Selection].Content; + } + } else { + AlertSystemError("The selected text in the library is empty."); + } + + // Generate terrain data + while((Game.Terrain.Data.length - 1) < Game.Terrain.Text.length) { + Game.Terrain.Data[Game.Terrain.Data.length] = [ + 0, + Game.Terrain.Text.charAt(Game.Terrain.Data.length - 1), + Game.Terrain.Data[Game.Terrain.Data.length - 1][2] + Game.Terrain.Gradient + ]; + if(Game.Terrain.Data[Game.Terrain.Data.length - 1][2] < 0) { + Game.Terrain.Data[Game.Terrain.Data.length - 1][2] = 0; + } + if(Game.Terrain.Data[Game.Terrain.Data.length - 1][2] > 29000) { + Game.Terrain.Data[Game.Terrain.Data.length - 1][2] = 29000; + } + Game.Terrain.Gradient += Randomize(-36 - Math.floor((Game.Terrain.Gradient - 3) * 6), 36 - Math.floor((Game.Terrain.Gradient - 3) * 6)) / 24; + // This algorithm makes the gradient variation balanced. + // For example, if gradient is -3, the variation is randomized between 0 and +3. So the descent cannot be steeper any more. + } + + // Generate and remove terrain elements + // Ahead + if(Game.Terrain.Data[Game0.Terrain.Generation.Ahead][1] == "") { + AlertSystemError("Terrain #" + Game0.Terrain.Generation.Ahead + " has empty text."); + } + if(IsElementExisting("Terrain_Game" + Game0.Terrain.Generation.Ahead) == false) { + AddText("Ctnr_GameTerrain", "
" + Game.Terrain.Data[Game0.Terrain.Generation.Ahead][1] + "
"); + let CumulativeWidth = 0; + for(let Looper = Game.Stats.Odometer + 1; Looper < Game0.Terrain.Generation.Ahead; Looper++) { + CumulativeWidth += ReadWidth("Terrain_Game" + Looper); + } + ChangeLeft("Terrain_Game" + Game0.Terrain.Generation.Ahead, Game0.Terrain.WalkedWidth + CumulativeWidth + "px"); + ChangeHeight("Terrain_Game" + Game0.Terrain.Generation.Ahead, 2000 + Game.Terrain.Data[Game0.Terrain.Generation.Ahead][2] + "px"); + } + if(Game0.Terrain.Generation.Ahead - Game.Stats.Odometer < 100) { + Game0.Terrain.Generation.Ahead++; + } else { + Game0.Terrain.Generation.Ahead = Game.Stats.Odometer + 100; + } + + // Behind + if(Game.Terrain.Data[Game0.Terrain.Generation.Behind][1] == "" && Game0.Terrain.Generation.Behind > 0) { + AlertSystemError("Terrain #" + Game0.Terrain.Generation.Behind + " has empty text."); + } + if(IsElementExisting("Terrain_Game" + Game0.Terrain.Generation.Behind) == false) { + AddText("Ctnr_GameTerrain", "
" + Game.Terrain.Data[Game0.Terrain.Generation.Behind][1] + "
"); + let CumulativeWidth = 0; + for(let Looper = Game.Stats.Odometer; Looper >= Game0.Terrain.Generation.Behind; Looper--) { + CumulativeWidth += ReadWidth("Terrain_Game" + Looper); + } + ChangeLeft("Terrain_Game" + Game0.Terrain.Generation.Behind, Game0.Terrain.WalkedWidth - CumulativeWidth + "px"); + ChangeHeight("Terrain_Game" + Game0.Terrain.Generation.Behind, 2000 + Game.Terrain.Data[Game0.Terrain.Generation.Behind][2] + "px"); + } + if(Game.Stats.Odometer - Game0.Terrain.Generation.Behind < 100) { + Game0.Terrain.Generation.Behind--; + } else { + Game0.Terrain.Generation.Behind = Game.Stats.Odometer - 100; + } + if(Game0.Terrain.Generation.Behind < 0) { + Game0.Terrain.Generation.Behind = 0; + } + + // Removal + for(let Looper = 0; Looper <= Game0.Terrain.Generation.Behind - 1; Looper++) { + if(IsElementExisting("Terrain_Game" + Looper) == true) { + RemoveElement("Terrain_Game" + Looper); + } + } + + // Align terrain to player + if(IsMobileLayout() == false) { + ChangeBottom("Ctnr_GameTerrain", "calc(50% - 1980px - " + Game.Terrain.Data[Game.Stats.Odometer + 1][2] + "px)"); + } else { + ChangeBottom("Ctnr_GameTerrain", "calc(85px + (100% - 260px) / 2 - 1980px - " + Game.Terrain.Data[Game.Stats.Odometer + 1][2] + "px)"); + } + ChangeRight("Ctnr_GameTerrain", "calc(50% + " + Game0.Terrain.WalkedWidth + "px)"); + + // Characters + // Player + if(IsMobileLayout() == false) { + ChangeBottom("Character_GamePlayer", "calc(50% + 20px - " + (Game.Terrain.Data[Game.Stats.Odometer + 1][2] - Game.Terrain.Data[Game.Stats.Odometer][2]) + "px)"); + } else { + ChangeBottom("Character_GamePlayer", "calc(85px + (100% - 260px) / 2 + 20px - " + (Game.Terrain.Data[Game.Stats.Odometer + 1][2] - Game.Terrain.Data[Game.Stats.Odometer][2]) + "px)"); + } + + // Chaser + // Vertical separation + if(IsMobileLayout() == false) { + ChangeBottom("Character_GameChaser", "calc(50% + 20px - " + (Game.Terrain.Data[Game.Stats.Odometer + 1][2] - Game.Terrain.Data[Math.floor(Game.Stats.ChaserOdometer)][2]) + "px)"); + } else { + ChangeBottom("Character_GameChaser", "calc(85px + (100% - 260px) / 2 + 20px - " + (Game.Terrain.Data[Game.Stats.Odometer + 1][2] - Game.Terrain.Data[Math.floor(Game.Stats.ChaserOdometer)][2]) + "px)"); + } + + // Horizontal separation + let HorizontalSeparation = 0; + for(let Looper = Math.floor(Game.Stats.ChaserOdometer) + 1; Looper <= Game.Stats.Odometer; Looper++) { + if(IsElementExisting("Terrain_Game" + Looper) == true) { + HorizontalSeparation += ReadWidth("Terrain_Game" + Looper); + } + } + if(Game.Stats.Odometer < 20) { + HorizontalSeparation += (20 - Game.Stats.Odometer) * 10; + } + ChangeRight("Character_GameChaser", "calc(50% + " + HorizontalSeparation + "px)"); + + // Chaser balloon + if((IsMobileLayout() == false && ReadLeft("Character_GameChaser") < -130) || + (IsMobileLayout() == true && ReadLeft("Character_GameChaser") < -65)) { + Show("GameChaserBalloon"); + } else { + Fade("GameChaserBalloon"); + } + ChangeText("Label_GameDistance", Game.Stats.Odometer - Math.floor(Game.Stats.ChaserOdometer)); + + // Progbar + switch(Game.Progressing.Progressing) { + case "Duration": + Game.Stats.Progress = Game.Stats.ElapsedTime / (Game.Progressing.Duration * 60000) * 100; + break; + case "TravelDistance": + Game.Stats.Progress = Game.Stats.Odometer / Game.Progressing.TravelDistance * 100; + break; + case "Altitude": + Game.Stats.Progress = Game.Stats.Altitude / Game.Progressing.Altitude * 100; + break; + default: + AlertSystemError("The value of Game.Progressing.Progressing \"" + Game.Progressing.Progressing + "\" in function ClockGame is invalid."); + break; + } + ChangeProgbar("ProgbarFg_Game", "Horizontal", Game.Stats.Progress); + + // Stats + ChangeText("Label_GameOdometerValue", Game.Stats.Odometer); + ChangeText("Label_GameTypoCountValue", Game.Stats.TypoCount); + ChangeText("Label_GameElapsedTimeValue", Math.floor(Game.Stats.ElapsedTime / 60000) + ":" + Math.floor(Game.Stats.ElapsedTime % 60000 / 1000).toString().padStart(2, "0")); + if(Game.Stats.KeystrokeCount > 20) { + if(Game.Stats.KeystrokeTimestamp[1] >= 0) { + Game.Stats.KeystrokeSpeed = 60000 / ((Game.Stats.ElapsedTime - Game.Stats.KeystrokeTimestamp[1]) / 20); + Game.Stats.AvgKeystrokeSpeed = Game.Stats.KeystrokeCount / (Game.Stats.ElapsedTime / 60000); + } else { + AlertSystemError("The array Game.Stats.KeystrokeTimestamp is not filled when Game.Stats.KeystrokeCount got greater than 20."); + } + RemoveClass("Label_GameKeystrokeSpeedValue", "Transparent"); + } else { + Game.Stats.KeystrokeSpeed = 0; + Game.Stats.AvgKeystrokeSpeed = 0; + if(Game.Stats.KeystrokeCount > 0) { + AddClass("Label_GameKeystrokeSpeedValue", "Transparent"); + } else { + RemoveClass("Label_GameKeystrokeSpeedValue", "Transparent"); + } + } + if(System.Display.Anim > 0) { + Game0.Stats.KeystrokeSpeedDisplay += (Game.Stats.KeystrokeSpeed - Game0.Stats.KeystrokeSpeedDisplay) / 200; + } else { + Game0.Stats.KeystrokeSpeedDisplay = Game.Stats.KeystrokeSpeed; + } + ChangeText("Label_GameKeystrokeSpeedValue", Game0.Stats.KeystrokeSpeedDisplay.toFixed(0) + "kpm"); + if(Game.Stats.Odometer > 0) { + Game.Stats.Accuracy = Game.Stats.Odometer / (Game.Stats.Odometer + Game.Stats.TypoCount) * 100; + } else { + Game.Stats.Accuracy = 0; + } + ChangeText("Label_GameAccuracyValue", Game.Stats.Accuracy.toFixed(2) + "%"); + if(System.Display.Anim > 0) { + Game0.Stats.ScoreDisplay += (Game.Stats.Score - Game0.Stats.ScoreDisplay) / 5; + } else { + Game0.Stats.ScoreDisplay = Game.Stats.Score; + } + ChangeText("Label_GameScoreValue", Game0.Stats.ScoreDisplay.toFixed(0).toString().padStart(8, "0")); + + // Speed + if(Game.Stats.Odometer > 20) { // The speed is not calculated when travel distance is less than 20. + if(Game.Stats.TypeTimestamp[1] >= 0) { + Game.Stats.Speed = 60000 / ((Game.Stats.ElapsedTime - Game.Stats.TypeTimestamp[1]) / 20); + Game.Stats.AvgSpeed = Game.Stats.Odometer / (Game.Stats.ElapsedTime / 60000); + } else { + AlertSystemError("The array Game.Stats.TypeTimestamp is not filled when Game.Stats.Odometer got greater than 20."); + } + RemoveClass("CtrlGroup_GameSpeedBalloon", "Transparent"); + } else { + Game.Stats.Speed = 0; + Game.Stats.AvgSpeed = 0; + if(Game.Stats.Odometer > 0) { + AddClass("CtrlGroup_GameSpeedBalloon", "Transparent"); + } else { + RemoveClass("CtrlGroup_GameSpeedBalloon", "Transparent"); + } + } + if(Game.Status.IsRunning == true && Game.Status.IsPaused == false && System.Display.Anim > 0) { + ChangeAnim("CtrlGroup_GameSpeed", "100ms"); + ChangeAnim("CtrlGroup_GameAltitude", "100ms"); + } else { + ChangeAnim("CtrlGroup_GameSpeed", ""); + ChangeAnim("CtrlGroup_GameAltitude", ""); + } + // Tape + Game0.Stats.SpeedTapeDisplay += (Game.Stats.Speed - Game0.Stats.SpeedTapeDisplay) / 200; + if(Game0.Stats.SpeedTapeDisplay > 500) { + Game0.Stats.SpeedTapeDisplay = 500; + } + ChangeTop("CtrlGroup_GameSpeedTape", "calc(50% - 2500px + " + Game0.Stats.SpeedTapeDisplay * 5 + "px)"); + + // Additional indicators + // Speed trend + Game.Stats.SpeedTrend = (Game0.Stats.SpeedTapeDisplay - Game0.Stats.PreviousSpeedTapeDisplay) * (5000 / (Date.now() - Game0.Stats.PreviousClockTime)); + Game0.Stats.SpeedTrendDisplay += (Game.Stats.SpeedTrend - Game0.Stats.SpeedTrendDisplay) / 5; + if(Game0.Stats.SpeedTrendDisplay >= 0) { + RemoveClass("Ctrl_GameSpeedTrend", "Decreasing"); + AddClass("Ctrl_GameSpeedTrend", "Increasing"); + ChangeBottom("Ctrl_GameSpeedTrend", "50%"); + ChangeHeight("Ctrl_GameSpeedTrend", Game0.Stats.SpeedTrendDisplay * 5 + "px"); + } else { + RemoveClass("Ctrl_GameSpeedTrend", "Increasing"); + AddClass("Ctrl_GameSpeedTrend", "Decreasing"); + ChangeBottom("Ctrl_GameSpeedTrend", "calc(50% - " + (-Game0.Stats.SpeedTrendDisplay * 5) + "px)"); + ChangeHeight("Ctrl_GameSpeedTrend", -Game0.Stats.SpeedTrendDisplay * 5 + "px"); + } + if(Math.abs(Game0.Stats.SpeedTrendDisplay) >= 5) { + Show("Ctrl_GameSpeedTrend"); + } else { + Fade("Ctrl_GameSpeedTrend"); + } + + // Other speeds + ChangeTop("CtrlGroup_GameOtherSpeeds", "calc(50% - 2500px + " + Game0.Stats.SpeedTapeDisplay * 5 + "px)"); + // Chaser speed + Game0.Stats.ChaserSpeedDisplay += (Game.Stats.ChaserSpeed - Game0.Stats.ChaserSpeedDisplay) / 5; + ChangeHeight("Ctrl_GameChaserSpeed", Game0.Stats.ChaserSpeedDisplay * 5 + "px"); + + // Dangerous speed + if(Game.Stats.Odometer > 20) { + Game.Stats.DangerousSpeed = Game.Stats.ChaserSpeed - (Game.Stats.Odometer - Game.Stats.ChaserOdometer) * 6; + } else { + Game.Stats.DangerousSpeed = 0; + } + Game0.Stats.DangerousSpeedDisplay += (Game.Stats.DangerousSpeed - Game0.Stats.DangerousSpeedDisplay) / 5; + ChangeHeight("Ctrl_GameDangerousSpeed", Game0.Stats.DangerousSpeedDisplay * 5 + "px"); + + // Avg speed + Game0.Stats.AvgSpeedDisplay += (Game.Stats.AvgSpeed - Game0.Stats.AvgSpeedDisplay) / 5; + if(Game0.Stats.AvgSpeedDisplay > 500) { + Game0.Stats.AvgSpeedDisplay = 500; + } + ChangeBottom("Ctrl_GameAvgSpeed", Game0.Stats.AvgSpeedDisplay * 5 - 35 + "px"); + + // Balloon + Game0.Stats.SpeedBalloonDisplay[1] = Math.floor(Game0.Stats.SpeedTapeDisplay / 100); + Game0.Stats.SpeedBalloonDisplay[2] = Math.floor(Game0.Stats.SpeedTapeDisplay % 100 / 10); + Game0.Stats.SpeedBalloonDisplay[3] = Game0.Stats.SpeedTapeDisplay % 10; + if(System.Display.Anim > 0) { + if(Game0.Stats.SpeedBalloonDisplay[3] > 9) {Game0.Stats.SpeedBalloonDisplay[2] += (Game0.Stats.SpeedBalloonDisplay[3] - 9);} // Imitating the cockpit PFD number scrolling effect. + if(Game0.Stats.SpeedBalloonDisplay[2] > 9) {Game0.Stats.SpeedBalloonDisplay[1] += (Game0.Stats.SpeedBalloonDisplay[2] - 9);} + } else { + Game0.Stats.SpeedBalloonDisplay[3] = Math.floor(Game0.Stats.SpeedBalloonDisplay[3]); + } + if(IsMobileLayout() == false) { + ChangeTop("ScrollingNumber_GameSpeed1", -45 * (5 - Game0.Stats.SpeedBalloonDisplay[1]) + "px"); + ChangeTop("ScrollingNumber_GameSpeed2", -45 * (11 - Game0.Stats.SpeedBalloonDisplay[2]) + "px"); + ChangeTop("ScrollingNumber_GameSpeed3", 15 - 30 * (11 - Game0.Stats.SpeedBalloonDisplay[3]) + "px"); + } else { + ChangeTop("ScrollingNumber_GameSpeed1", -30 * (5 - Game0.Stats.SpeedBalloonDisplay[1]) + "px"); + ChangeTop("ScrollingNumber_GameSpeed2", -30 * (11 - Game0.Stats.SpeedBalloonDisplay[2]) + "px"); + ChangeTop("ScrollingNumber_GameSpeed3", 10 - 20 * (11 - Game0.Stats.SpeedBalloonDisplay[3]) + "px"); + } + if(Game0.Stats.SpeedTapeDisplay < Game0.Stats.DangerousSpeedDisplay) { + AddClass("CtrlGroup_GameSpeedBalloon", "EmphasizedText"); + } else { + RemoveClass("CtrlGroup_GameSpeedBalloon", "EmphasizedText"); + } + + // Altitude + Game.Stats.Altitude = Game.Terrain.Data[Game.Stats.Odometer][2]; + // Tape + Game0.Stats.AltitudeTapeDisplay += (Game.Stats.Altitude - Game0.Stats.AltitudeTapeDisplay) / 5; + ChangeTop("CtrlGroup_GameAltitudeTape", "calc(50% - 30000px + " + Game0.Stats.AltitudeTapeDisplay + "px)"); + + // Balloon + Game0.Stats.AltitudeBalloonDisplay[1] = Math.floor(Game0.Stats.AltitudeTapeDisplay / 10000); + Game0.Stats.AltitudeBalloonDisplay[2] = Math.floor(Game0.Stats.AltitudeTapeDisplay % 10000 / 1000); + Game0.Stats.AltitudeBalloonDisplay[3] = Math.floor(Game0.Stats.AltitudeTapeDisplay % 1000 / 100); + Game0.Stats.AltitudeBalloonDisplay[4] = Game0.Stats.AltitudeTapeDisplay % 100; + if(System.Display.Anim > 0) { + if(Game0.Stats.AltitudeBalloonDisplay[4] > 80) {Game0.Stats.AltitudeBalloonDisplay[3] += ((Game0.Stats.AltitudeBalloonDisplay[4] - 80) / 20);} + if(Game0.Stats.AltitudeBalloonDisplay[3] > 9) {Game0.Stats.AltitudeBalloonDisplay[2] += (Game0.Stats.AltitudeBalloonDisplay[3] - 9);} + if(Game0.Stats.AltitudeBalloonDisplay[2] > 9) {Game0.Stats.AltitudeBalloonDisplay[1] += (Game0.Stats.AltitudeBalloonDisplay[2] - 9);} + } else { + Game0.Stats.AltitudeBalloonDisplay[4] = Math.floor(Game0.Stats.AltitudeBalloonDisplay[4] / 20) * 20; + } + if(IsMobileLayout() == false) { + ChangeTop("ScrollingNumber_GameAltitude1", -45 * (3 - Game0.Stats.AltitudeBalloonDisplay[1]) + "px"); + ChangeTop("ScrollingNumber_GameAltitude2", -45 * (11 - Game0.Stats.AltitudeBalloonDisplay[2]) + "px"); + ChangeTop("ScrollingNumber_GameAltitude3", -45 * (11 - Game0.Stats.AltitudeBalloonDisplay[3]) + "px"); + ChangeTop("ScrollingNumber_GameAltitude4", 17.5 - 25 * (6 - Game0.Stats.AltitudeBalloonDisplay[4] / 20) + "px"); + } else { + ChangeTop("ScrollingNumber_GameAltitude1", -30 * (3 - Game0.Stats.AltitudeBalloonDisplay[1]) + "px"); + ChangeTop("ScrollingNumber_GameAltitude2", -30 * (11 - Game0.Stats.AltitudeBalloonDisplay[2]) + "px"); + ChangeTop("ScrollingNumber_GameAltitude3", -30 * (11 - Game0.Stats.AltitudeBalloonDisplay[3]) + "px"); + ChangeTop("ScrollingNumber_GameAltitude4", 12 - 16 * (6 - Game0.Stats.AltitudeBalloonDisplay[4] / 20) + "px"); + } + + // Prompts + if(Game.Status.IsRunning == false) { + Show("Ctrl_GameStartPrompt"); + if(document.activeElement.id == "Textbox_Game") { + ChangeText("Label_GameStartPrompt", "开始打字即可开始游戏"); + } else { + ChangeText("Label_GameStartPrompt", "请点击画面"); + } + } else { + if(Game.Status.IsPaused == false || Game.Stats.Progress >= 100) { + Hide("Ctrl_GameStartPrompt"); + } else { + Show("Ctrl_GameStartPrompt"); + if(document.activeElement.id == "Textbox_Game") { + ChangeText("Label_GameStartPrompt", "开始打字即可继续游戏"); + } else { + ChangeText("Label_GameStartPrompt", "请点击画面"); + } + } + } + if(Game.Status.IsRunning == true && Game.Status.IsPaused == false && Game.Stats.Odometer <= 20) { + Show("Ctrl_GameChasePrompt"); + } else { + Hide("Ctrl_GameChasePrompt"); + } + + // Victory + if(Game.Status.IsRunning == true && Game.Stats.Progress >= 100) { + Game.Stats.Progress = 100; + ChangeDisabled("Textbox_Game", true); + ChangeDisabled("Cmdbtn_GamePauseOrReset", true); + if(Game.Status.IsPaused == false) { + Game.Status.IsPaused = true; + ChangeValue("Textbox_Game", ""); + ShowToast("胜利!"); + Highscore[6][1] = "最新"; + Highscore[6][2] = new Date(Date.now()).toLocaleDateString(System.I18n.Language); + Highscore[6][3] = Game.Stats.Score.toString().padStart(8, "0"); + Highscore[6][4] = Game.Stats.AvgSpeed.toFixed(0) + "cpm"; + Highscore[6][5] = Game.Stats.AvgKeystrokeSpeed.toFixed(0) + "kpm"; + Highscore[6][6] = Game.Stats.Accuracy.toFixed(2) + "%"; + RefreshHighscore(); + setTimeout(function() { + ResetGame(); + ScrollIntoView("Highscore"); + }, System.Display.Anim * 2 + 1000); + } + } + + // Game over + if(Game.Status.IsRunning == true && Game.Stats.Odometer > 20 && Game.Stats.ChaserOdometer >= Game.Stats.Odometer) { + Game.Stats.ChaserOdometer = Game.Stats.Odometer; + ChangeDisabled("Textbox_Game", true); + ChangeDisabled("Cmdbtn_GamePauseOrReset", true); + if(Game.Status.IsPaused == false) { + Game.Status.IsPaused = true; + ChangeValue("Textbox_Game", ""); + ShowToast("游戏结束"); + setTimeout(ResetGame, System.Display.Anim * 2 + 1000); + } + } + + // Update previous variants + Game0.Stats.PreviousClockTime = Date.now(); + Game0.Stats.PreviousElapsedTime = Game.Stats.ElapsedTime; + Game0.Stats.PreviousSpeedTapeDisplay = Game0.Stats.SpeedTapeDisplay; + } + function RefreshGame() { + // Update the text when the game is not running + if(Game.Status.IsRunning == false) { + RefreshLibrary(); // Section "Game" relies on section "Library". + } + + // Call + ClockGame(); + + // Terrain + ChangeLanguage("Ctnr_GameTerrain", Library.Text[Library.Selection].Language); + + // Textbox + ChangeLanguage("Textbox_Game", Library.Text[Library.Selection].Language); + ChangeDisabled("Textbox_Game", false); + + // Ctrls + if(Game.Status.IsRunning == false) { + ChangeDisabled("Cmdbtn_GamePauseOrReset", true); + ChangeText("Cmdbtn_GamePauseOrReset", "暂停"); + ChangeDisabled("Fieldset_Library", false); + ChangeDisabled("Fieldset_LibraryTextProperties", false); + ChangeDisabled("Fieldset_LibraryManagement", false); + ChangeDisabled("Fieldset_SettingsProgressing", false); + ChangeDisabled("Fieldset_SettingsDifficulty", false); + ChangeDisabled("Combobox_SettingsGameFont", false); + } else { + ChangeDisabled("Cmdbtn_GamePauseOrReset", false); + if(Game.Status.IsPaused == false) { + ChangeText("Cmdbtn_GamePauseOrReset", "暂停"); + } else { + ChangeText("Cmdbtn_GamePauseOrReset", "重置"); + } + ChangeDisabled("Fieldset_Library", true); + ChangeDisabled("Fieldset_LibraryTextProperties", true); + ChangeDisabled("Fieldset_LibraryManagement", true); + ChangeDisabled("Fieldset_SettingsProgressing", true); + ChangeDisabled("Fieldset_SettingsDifficulty", true); + ChangeDisabled("Combobox_SettingsGameFont", true); + } + + // Settings + // Progressing + switch(Game.Progressing.Progressing) { + case "Duration": + ChangeChecked("Radiobtn_SettingsDuration", true); + ChangeChecked("Radiobtn_SettingsTravelDistance", false); + ChangeChecked("Radiobtn_SettingsAltitude", false); + break; + case "TravelDistance": + ChangeChecked("Radiobtn_SettingsDuration", false); + ChangeChecked("Radiobtn_SettingsTravelDistance", true); + ChangeChecked("Radiobtn_SettingsAltitude", false); + break; + case "Altitude": + ChangeChecked("Radiobtn_SettingsDuration", false); + ChangeChecked("Radiobtn_SettingsTravelDistance", false); + ChangeChecked("Radiobtn_SettingsAltitude", true); + break; + default: + AlertSystemError("The value of Game.Progressing.Progressing \"" + Game.Progressing.Progressing + "\" in function RefreshGame is invalid."); + break; + } + ChangeValue("Textbox_SettingsDuration", Game.Progressing.Duration); + ChangeValue("Textbox_SettingsTravelDistance", Game.Progressing.TravelDistance); + ChangeValue("Textbox_SettingsAltitude", Game.Progressing.Altitude); + + // Difficulty + ChangeValue("Textbox_SettingsChaserSpeedInitial", Game.Difficulty.ChaserSpeed.Initial); + ChangeValue("Textbox_SettingsChaserSpeedFinal", Game.Difficulty.ChaserSpeed.Final); + RemoveClass("Cmdbtn_SettingsChaserSpeedWestern", "Active"); + RemoveClass("Cmdbtn_SettingsChaserSpeedCJK", "Active"); + RemoveClass("Cmdbtn_SettingsChaserSpeedZenMode", "Active"); + if(Game.Difficulty.ChaserSpeed.Initial == 180 && Game.Difficulty.ChaserSpeed.Final == 240) { + AddClass("Cmdbtn_SettingsChaserSpeedWestern", "Active"); + } + if(Game.Difficulty.ChaserSpeed.Initial == 40 && Game.Difficulty.ChaserSpeed.Final == 60) { + AddClass("Cmdbtn_SettingsChaserSpeedCJK", "Active"); + } + if(Game.Difficulty.ChaserSpeed.Initial == 10 && Game.Difficulty.ChaserSpeed.Final == 10) { + AddClass("Cmdbtn_SettingsChaserSpeedZenMode", "Active"); + } + ChangeValue("Textbox_SettingsMaxSeparation", Game.Difficulty.MaxSeparation); + + // Custom characters + ChangeValue("Textbox_SettingsPlayerImage", Game.CustomCharacters.PlayerImage); + if(Game.CustomCharacters.PlayerImage != "") { + ChangeImage("Image_GamePlayer", Game.CustomCharacters.PlayerImage); + } else { + ChangeImage("Image_GamePlayer", "images/YamadaRyou.png"); + } + ChangeValue("Textbox_SettingsChaserImage", Game.CustomCharacters.ChaserImage); + if(Game.CustomCharacters.ChaserImage != "") { + ChangeImage("Image_GameChaser", Game.CustomCharacters.ChaserImage); + ChangeImage("Image_GameChaserBalloon", Game.CustomCharacters.ChaserImage); + } else { + ChangeImage("Image_GameChaser", "images/GotouHitori.png"); + ChangeImage("Image_GameChaserBalloon", "images/GotouHitori.png"); + } + ChangeValue("Textbox_SettingsBgImage", Game.CustomCharacters.BgImage); + ChangeBgImage(Game.CustomCharacters.BgImage); + + // Save user data (Only when the game is not running or when the game is paused) + if(Game.Status.IsRunning == false || Game.Status.IsPaused == true) { + localStorage.setItem("YamanoboRyou_Game", JSON.stringify(Game)); + } + } + + // Highscore + function RefreshHighscore() { + // Remove "Latest" from original highscore table + for(let Looper = 1; Looper <= 5; Looper++) { + Highscore[Looper][1] = "名次"; + } + + // Sort (bubble sort) + for(let Looper = 1; Looper <= 5; Looper++) { + for(let Looper2 = 5; Looper2 >= 1; Looper2--) { + if(parseInt(Number(Highscore[Looper2 + 1][3])) > parseInt(Number(Highscore[Looper2][3]))) { + Highscore[0] = Highscore[Looper2]; + Highscore[Looper2] = Highscore[Looper2 + 1]; + Highscore[Looper2 + 1] = Highscore[0]; + } + } + } + Highscore[0] = 0; + + // Refresh + for(let Looper = 1; Looper <= 6; Looper++) { + RemoveClass("Item_HighscoreRow" + Looper, "Box"); + if(Highscore[Looper][1] == "最新") { + AddClass("Item_HighscoreRow" + Looper, "Box"); + } else { + Highscore[Looper][1] = "#" + Looper; + } + ChangeText("Label_HighscoreRow" + Looper + "Sequence", Highscore[Looper][1]); + ChangeText("Label_HighscoreRow" + Looper + "Date", Highscore[Looper][2]); + ChangeText("Label_HighscoreRow" + Looper + "Score", Highscore[Looper][3]); + ChangeText("Label_HighscoreRow" + Looper + "AvgSpeed", Highscore[Looper][4]); + ChangeText("Label_HighscoreRow" + Looper + "AvgKeystrokeSpeed", Highscore[Looper][5]); + ChangeText("Label_HighscoreRow" + Looper + "Accuracy", Highscore[Looper][6]); + } + + // Save user data + localStorage.setItem("YamanoboRyou_Highscore", JSON.stringify(Highscore)); + } + + // Library + function RefreshLibrary() { + // Generate list + ChangeText("CtrlGroup_LibraryList", ""); + for(let Looper = 1; Looper < Library.Text.length; Looper++) { + AddText("CtrlGroup_LibraryList", + "
  • " + + " " + + " " + + " " + + " " + + "
  • "); + } + if(Library.Text.length <= 1) { + AlertSystemError("The library is empty."); + } + if(Library.Text.length == 2) { + ChangeDisabled("Cmdbtn_LibraryText1Delete", true); + } + + // Selection + for(let Looper = 1; Looper < Library.Text.length; Looper++) { + if(Library.Selection == Looper) { + ChangeChecked("Radiobtn_LibraryText" + Looper, true); + AddClass("Label_LibraryText" + Looper, "Active"); + } else { + ChangeChecked("Radiobtn_LibraryText" + Looper, false); + RemoveClass("Label_LibraryText" + Looper, "Active"); + } + } + + // Filter + FilterLibrary(); + + // Text properties + ChangeValue("Textbox_LibraryName", Library.Text[Library.Selection].Name); + ChangeValue("Textbox_LibraryContent", Library.Text[Library.Selection].Content); + ChangeLanguage("Textbox_LibraryContent", Library.Text[Library.Selection].Language); + ChangeValue("Textbox_LibraryLanguage", Library.Text[Library.Selection].Language); + ChangeValue("Textbox_LibrarySource", Library.Text[Library.Selection].Source); + + // Save user data + localStorage.setItem("YamanoboRyou_Library", JSON.stringify(Library)); + } + +// Cmds + // Game + // Textbox + function Keypress() { + if(Game.Status.IsRunning == true && Game.Status.IsPaused == false) { + Game.Stats.KeystrokeCount++; + Game.Stats.KeystrokeTimestamp.splice(1, 1); + Game.Stats.KeystrokeTimestamp[21] = Game.Stats.ElapsedTime; + } + } + function WarnAboutPasting() { + if(Game.Status.IsRunning == true && Game.Status.IsPaused == false) { // Make sure the game is paused before showing the dialog. + Game.Status.IsPaused = true; + ChangeValue("Textbox_Game", ""); + RefreshGame(); + } + ShowDialog("Game_DoNotPaste", + "Caution", + "本游戏是打字游戏。请勿在游戏主界面进行粘贴操作。", + "", "", "", "确定"); + } + + // Ctrl + function PauseOrResetGame() { + if(Game.Status.IsRunning == true) { + if(Game.Status.IsPaused == false) { + Game.Status.IsPaused = true; + ChangeValue("Textbox_Game", ""); + ShowToast("游戏暂停"); + RefreshGame(); + } else { + ResetGame(); + } + } + } + function ResetGame() { + Game.Status = { + IsRunning: false, IsPaused: false + }; + Game.Terrain = { + Text: "", + Data: [ + [0, "", 0] + ], + Gradient: 5 + }; + Game.PreviousTextboxContent = ""; + Game.Stats = { + Progress: 0, + Odometer: 0, ChaserOdometer: 0, TypoCount: 0, + StartTime: 0, ElapsedTime: 0, + KeystrokeCount: 0, KeystrokeSpeed: 0, AvgKeystrokeSpeed: 0, + Accuracy: 0, + Score: 0, + Speed: 0, SpeedTrend: 0, AvgSpeed: 0, ChaserSpeed: 0, DangerousSpeed: 0, + Altitude: 0, + KeystrokeTimestamp: [0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], + TypeTimestamp: [0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] + }; + Game0 = { + Terrain: { + WalkedWidth: 0, + Generation: { + Ahead: 1, Behind: 0 + } + }, + Stats: { + PreviousClockTime: 0, PreviousElapsedTime: 0, + KeystrokeSpeedDisplay: 0, + ScoreDisplay: 0, + SpeedTapeDisplay: 0, PreviousSpeedTapeDisplay: 0, SpeedBalloonDisplay: [0, 0, 0, 0], + SpeedTrendDisplay: 0, AvgSpeedDisplay: 0, ChaserSpeedDisplay: 0, DangerousSpeedDisplay: 0, + AltitudeTapeDisplay: 0, AltitudeBalloonDisplay: [0, 0, 0, 0, 0] + } + }; + ChangeText("Ctnr_GameTerrain", ""); + RefreshGame(); + } + + // Library + // Library + function FilterLibrary() { + let Counter = 0, Counter2 = 0; + for(let Looper = 1; Looper < Library.Text.length; Looper++) { + if(Library.Text[Looper].Name.toLowerCase().includes(ReadValue("Textbox_LibraryFilter").toLowerCase()) == true || + Library.Text[Looper].Content.toLowerCase().includes(ReadValue("Textbox_LibraryFilter").toLowerCase()) == true || + Library.Text[Looper].Language.toLowerCase().includes(ReadValue("Textbox_LibraryFilter").toLowerCase()) == true || + Library.Text[Looper].Source.toLowerCase().includes(ReadValue("Textbox_LibraryFilter").toLowerCase()) == true) { + Show("Ctrl_LibraryText" + Looper); + Counter++; + } else { + Hide("Ctrl_LibraryText" + Looper); + } + Counter2++; + } + ChangeText("Label_LibraryItemCount", "显示 " + Counter + "/" + Counter2); + } + function SetText(Number) { + Library.Selection = Number; + ResetGame(); + } + function DuplicateText(Number) { + Library.Text.splice(Number + 1, 0, structuredClone(Library.Text[Number])); + if(Library.Selection > Number) { + Library.Selection++; + } + RefreshGame(); + } + function ExportText(Number) { + navigator.clipboard.writeText(JSON.stringify(Library.Text[Number])); + if(System.DontShowAgain.includes("YamanoboRyou_Library_TextExported") == false) { + ShowDialog("Library_TextExported", + "Info", + "已导出文本「" + Library.Text[Number].Name + "」至剪贴板。", + "不再弹窗提示", "", "", "确定"); + } else { + ShowToast("已导出文本"); + } + } + function ConfirmDeleteText(Number) { + Interaction.Deletion = Number; + ShowDialog("Library_ConfirmDeleteText", + "Caution", + "您确认要删除文本「" + ConvertEmptyName(Library.Text[Number].Name) + "」?", + "", "", "删除", "取消"); + } + function NewText() { + Library.Text[Library.Text.length] = { + Name: "", + Content: "在此粘贴文本内容", + Language: "", + Source: "" + }; + Library.Selection = Library.Text.length - 1; + ResetGame(); + } + function SortByName() { + for(let Looper = 1; Looper < Library.Text.length - 1; Looper++) { + for(let Looper2 = 1; Looper2 < Library.Text.length - 1; Looper2++) { + if(Library.Text[Looper2].Name > Library.Text[Looper2 + 1].Name) { + Library.Text[0] = structuredClone(Library.Text[Looper2]); + Library.Text[Looper2] = structuredClone(Library.Text[Looper2 + 1]); + Library.Text[Looper2 + 1] = structuredClone(Library.Text[0]); + if(Library.Selection == Looper2) { + Library.Selection++; + } + } + } + } + Library.Text[0] = 0; + ResetGame(); + } + + // Text properties + function SetTextName() { + Library.Text[Library.Selection].Name = ReadValue("Textbox_LibraryName"); + RefreshLibrary(); + } + function SetTextContent() { + if(ReadValue("Textbox_LibraryContent") != "") { + Library.Text[Library.Selection].Content = ReadValue("Textbox_LibraryContent"); + } else { + ShowDialog("Library_ContentCannotBeEmpty", + "Error", + "文本内容不能为空。", + "", "", "", "确定"); + } + ResetGame(); + } + function SetTextLanguage() { + Library.Text[Library.Selection].Language = ReadValue("Textbox_LibraryLanguage"); + ResetGame(); + } + function SetTextSource() { + Library.Text[Library.Selection].Source = ReadValue("Textbox_LibrarySource"); + RefreshLibrary(); + } + + // Management + function ImportLibraryObjects() { + let Objects = ReadValue("Textbox_LibraryImport").split("\n"), Counter = 0, Counter2 = 0; + for(let Looper = 0; Looper < Objects.length; Looper++) { + switch(true) { + // Text + case Objects[Looper].startsWith("{\"Name\":") == true: + Library.Text[Library.Text.length] = JSON.parse(Objects[Looper]); + Counter++; + break; + + // Whole library + case Objects[Looper].startsWith("{\"Selection\":") == true: + Library = JSON.parse(Objects[Looper]); + Counter++; + break; + + // Empty line + case Objects[Looper] == "": + break; + + // Failed to import + default: + Counter2++; + break; + } + } + if(Counter > 0) { + if(Counter2 <= 0) { + ShowDialog("Library_ObjectsImported", + "Info", + "成功导入" + Counter + "个对象。", + "", "", "", "确定"); + } else { + ShowDialog("Library_ObjectsImported", + "Info", + "成功导入" + Counter + "个对象。" + Counter2 + "个对象的 JSON 字符串不合法,无法导入。", + "", "", "", "确定"); + } + ResetGame(); + } else { + if(ReadValue("Textbox_LibraryImport") != "") { + ShowDialog("Library_ImportFailed", + "Error", + "您键入的 JSON 字符串不合法。", + "", "", "", "确定"); + } else { + ShowDialog("Library_ImportFailed", + "Error", + "文本框为空。请先在文本框键入要导入的对象,然后再点击「导入」。", + "", "", "", "确定"); + } + } + ChangeValue("Textbox_LibraryImport", ""); + } + function ExportLibrary() { + navigator.clipboard.writeText(JSON.stringify(Library)); + ShowDialog("Library_LibraryExported", + "Info", + "已导出文库 (" + (Library.Text.length - 1) + "条文本) 至剪贴板。", + "", "", "", "确定"); + } + function ConfirmResetLibrary() { + ShowDialog("Library_ConfirmResetLibrary", + "Caution", + "您确认要重置文库?", + "", "", "重置", "取消"); + } + + // Settings + // Progressing + function SetProgressing(Value) { + switch(Value) { + case "Duration": + case "TravelDistance": + case "Altitude": + Game.Progressing.Progressing = Value; + RefreshGame(); + break; + default: + AlertSystemError("The value of Value \"" + Value + "\" in function SetProgressing is invalid."); + break; + } + } + function SetDuration() { + Game.Progressing.Duration = parseInt(Number(ReadValue("Textbox_SettingsDuration"))); // Use parseInt(Number()) to force convert value to integer. + if(Game.Progressing.Duration < 1) { + Game.Progressing.Duration = 1; + } + if(Game.Progressing.Duration > 60) { + Game.Progressing.Duration = 60; + } + RefreshGame(); + } + function SetTravelDistance() { + Game.Progressing.TravelDistance = parseInt(Number(ReadValue("Textbox_SettingsTravelDistance"))); + if(Game.Progressing.TravelDistance < 50) { + Game.Progressing.TravelDistance = 50; + } + if(Game.Progressing.TravelDistance > 9999) { + Game.Progressing.TravelDistance = 9999; + } + RefreshGame(); + } + function SetAltitude() { + Game.Progressing.Altitude = parseInt(Number(ReadValue("Textbox_SettingsAltitude"))); + if(Game.Progressing.Altitude < 200) { + Game.Progressing.Altitude = 200; + } + if(Game.Progressing.Altitude > 29000) { + Game.Progressing.Altitude = 29000; + } + RefreshGame(); + } + + // Difficulty + function SetChaserSpeedInitial() { + Game.Difficulty.ChaserSpeed.Initial = parseInt(Number(ReadValue("Textbox_SettingsChaserSpeedInitial"))); + if(Game.Difficulty.ChaserSpeed.Initial < 10) { + Game.Difficulty.ChaserSpeed.Initial = 10; + } + if(Game.Difficulty.ChaserSpeed.Initial > 500) { + Game.Difficulty.ChaserSpeed.Initial = 500; + } + if(Game.Difficulty.ChaserSpeed.Initial > Game.Difficulty.ChaserSpeed.Final) { + Game.Difficulty.ChaserSpeed.Final = Game.Difficulty.ChaserSpeed.Initial; + } + if(Game.Difficulty.ChaserSpeed.Initial < Game.Difficulty.ChaserSpeed.Final - 100) { + Game.Difficulty.ChaserSpeed.Final = Game.Difficulty.ChaserSpeed.Initial + 100; + } + RefreshGame(); + } + function SetChaserSpeedFinal() { + Game.Difficulty.ChaserSpeed.Final = parseInt(Number(ReadValue("Textbox_SettingsChaserSpeedFinal"))); + if(Game.Difficulty.ChaserSpeed.Final < 10) { + Game.Difficulty.ChaserSpeed.Final = 10; + } + if(Game.Difficulty.ChaserSpeed.Final > 500) { + Game.Difficulty.ChaserSpeed.Final = 500; + } + if(Game.Difficulty.ChaserSpeed.Final < Game.Difficulty.ChaserSpeed.Initial) { + Game.Difficulty.ChaserSpeed.Initial = Game.Difficulty.ChaserSpeed.Final; + } + if(Game.Difficulty.ChaserSpeed.Final > Game.Difficulty.ChaserSpeed.Initial + 100) { + Game.Difficulty.ChaserSpeed.Initial = Game.Difficulty.ChaserSpeed.Final - 100; + } + RefreshGame(); + } + function SetChaserSpeedWestern() { + Game.Difficulty.ChaserSpeed.Initial = 180; + Game.Difficulty.ChaserSpeed.Final = 240; + RefreshGame(); + } + function SetChaserSpeedCJK() { + Game.Difficulty.ChaserSpeed.Initial = 40; + Game.Difficulty.ChaserSpeed.Final = 60; + RefreshGame(); + } + function SetChaserSpeedZenMode() { + Game.Difficulty.ChaserSpeed.Initial = 10; + Game.Difficulty.ChaserSpeed.Final = 10; + RefreshGame(); + } + function SetMaxSeparation() { + Game.Difficulty.MaxSeparation = parseInt(Number(ReadValue("Textbox_SettingsMaxSeparation"))); + if(Game.Difficulty.MaxSeparation < 10) { + Game.Difficulty.MaxSeparation = 10; + } + if(Game.Difficulty.MaxSeparation > 100) { + Game.Difficulty.MaxSeparation = 100; + } + RefreshGame(); + } + + // Custom characters + function SetPlayerImage() { + Game.CustomCharacters.PlayerImage = ReadValue("Textbox_SettingsPlayerImage"); + RefreshGame(); + } + function SetChaserImage() { + Game.CustomCharacters.ChaserImage = ReadValue("Textbox_SettingsChaserImage"); + RefreshGame(); + } + function SetBgImage() { + Game.CustomCharacters.BgImage = ReadValue("Textbox_SettingsBgImage"); + RefreshGame(); + } + function SwapCharacters() { + let Swapper = Game.CustomCharacters.PlayerImage; + Game.CustomCharacters.PlayerImage = Game.CustomCharacters.ChaserImage; + Game.CustomCharacters.ChaserImage = Swapper; + RefreshGame(); + } + function ResetCustomCharacters() { + Game.CustomCharacters = { + PlayerImage: "", ChaserImage: "", + BgImage: "" + }; + RefreshGame(); + } + + // Display + function SetGameFont() { + Subsystem.Display.GameFont = ReadValue("Combobox_SettingsGameFont"); + RefreshSubsystem(); + ResetGame(); + } + + // User data + function ImportUserData() { + if(ReadValue("Textbox_SettingsUserDataImport") != "") { + if(ReadValue("Textbox_SettingsUserDataImport").startsWith("{\"System\":{\"Display\":{\"Theme\":") == true) { + let Objects = JSON.parse(ReadValue("Textbox_SettingsUserDataImport")); + Object.keys(Objects).forEach(function(ObjectName) { + localStorage.setItem(ObjectName, JSON.stringify(Objects[ObjectName])); + }); + ChangeCursorOverall("wait"); + window.location.reload(); + } else { + ShowDialog("System_JSONStringInvalid", + "Error", + "您键入的 JSON 字符串不合法。", + "", "", "", "确定"); + RefreshSystem(); + } + } + } + function ExportUserData() { + navigator.clipboard.writeText("{" + + "\"System\":" + JSON.stringify(System) + "," + + "\"YamanoboRyou_Subsystem\":" + JSON.stringify(Subsystem) + "," + + "\"YamanoboRyou_Game\":" + JSON.stringify(Game) + "," + + "\"YamanoboRyou_Highscore\":" + JSON.stringify(Highscore) + "," + + "\"YamanoboRyou_Library\":" + JSON.stringify(Library) + + "}"); + ShowDialog("System_UserDataExported", + "Info", + "已导出用户数据至剪贴板。", + "", "", "", "确定"); + } + function ConfirmClearUserData() { + ShowDialog("System_ConfirmClearUserData", + "Caution", + "您确认要清空用户数据?", + "", "", "清空", "取消"); + } + + // Dialog + function AnswerDialog(Selector) { + switch(Interaction.DialogEvent) { + case "System_LanguageUnsupported": + case "System_MajorUpdateDetected": + case "System_JSONStringInvalid": + case "System_UserDataExported": + case "Game_DoNotPaste": + case "Library_ContentCannotBeEmpty": + case "Library_ObjectsImported": + case "Library_ImportFailed": + case "Library_LibraryExported": + switch(Selector) { + case 3: + break; + default: + setTimeout(function() { + AlertSystemError("The value of Selector \"" + Selector + "\" in function AnswerDialog is invalid."); + }, 0); + break; + } + break; + case "System_ConfirmClearUserData": + switch(Selector) { + case 2: + localStorage.clear(); + ChangeCursorOverall("wait"); + window.location.reload(); + break; + case 3: + break; + default: + setTimeout(function() { + AlertSystemError("The value of Selector \"" + Selector + "\" in function AnswerDialog is invalid."); + }, 0); + break; + } + break; + case "System_Error": + switch(Selector) { + case 2: + ScrollIntoView("Item_SettingsUserData"); + ShowIAmHere("Item_SettingsUserData"); + break; + case 3: + break; + default: + setTimeout(function() { + AlertSystemError("The value of Selector \"" + Selector + "\" in function AnswerDialog is invalid."); + }, 0); + break; + } + break; + case "Subsystem_ConfirmGoToTutorial": + switch(Selector) { + case 2: + ScrollIntoView("Item_HelpTutorial"); + ShowIAmHere("Item_HelpTutorial"); + break; + case 3: + break; + default: + setTimeout(function() { + AlertSystemError("The value of Selector \"" + Selector + "\" in function AnswerDialog is invalid."); + }, 0); + break; + } + break; + case "Library_TextExported": + switch(Selector) { + case 3: + if(IsChecked("Checkbox_DialogCheckboxOption") == true) { + System.DontShowAgain[System.DontShowAgain.length] = "YamanoboRyou_Library_TextExported"; + setTimeout(RefreshSystem, 0); + } + break; + default: + setTimeout(function() { + AlertSystemError("The value of Selector \"" + Selector + "\" in function AnswerDialog is invalid."); + }, 0); + break; + } + break; + case "Library_ConfirmDeleteText": + switch(Selector) { + case 2: + if(Library.Selection >= Interaction.Deletion) { + Library.Selection--; + } + Library.Text.splice(Interaction.Deletion, 1); + Interaction.Deletion = 0; + setTimeout(ResetGame, 0); + break; + case 3: + break; + default: + setTimeout(function() { + AlertSystemError("The value of Selector \"" + Selector + "\" in function AnswerDialog is invalid."); + }, 0); + break; + } + break; + case "Library_ConfirmResetLibrary": + switch(Selector) { + case 2: + localStorage.removeItem("YamanoboRyou_Library"); + ChangeCursorOverall("wait"); + window.location.reload(); + break; + case 3: + break; + default: + setTimeout(function() { + AlertSystemError("The value of Selector \"" + Selector + "\" in function AnswerDialog is invalid."); + }, 0); + break; + } + break; + default: + setTimeout(function() { + AlertSystemError("The value of Interaction.DialogEvent \"" + Interaction.DialogEvent + "\" in function AnswerDialog is invalid."); + }, 0); + return; + } + HideDialog(); + } + +// Listeners + // On keyboard + document.addEventListener("keydown", function(Hotkey) { + if(Hotkey.key == "F1") { + if(Game.Status.IsRunning == true && Game.Status.IsPaused == false) { // Make sure the game is paused before showing the dialog. + Game.Status.IsPaused = true; + ChangeValue("Textbox_Game", ""); + RefreshGame(); + } + ShowDialog("Subsystem_ConfirmGoToTutorial", + "Question", + "您按下了 F1 键。是否前往教程?", + "", "", "前往", "取消"); + if(System.Display.HotkeyIndicators == "ShowOnAnyKeyPress" || System.Display.HotkeyIndicators == "AlwaysShow") { + ShowHotkeyIndicators(); + } + } + if((document.activeElement.tagName.toLowerCase() != "input" && document.activeElement.tagName.toLowerCase() != "textarea") || // Prevent hotkey activation when inputing text etc. + document.activeElement.id == "Textbox_Game") { // Except when typing in game. + switch(Hotkey.key.toUpperCase()) { + case "DELETE": + Click("Cmdbtn_GamePauseOrReset"); + if(System.Display.HotkeyIndicators == "ShowOnAnyKeyPress" || System.Display.HotkeyIndicators == "AlwaysShow") { + ShowHotkeyIndicators(); + } + break; + default: + if((System.Display.HotkeyIndicators == "ShowOnWrongKeyPress" && IsWrongKeyNegligible(Hotkey.key) == false && Hotkey.key != "F1") || + System.Display.HotkeyIndicators == "ShowOnAnyKeyPress" || System.Display.HotkeyIndicators == "AlwaysShow") { + ShowHotkeyIndicators(); + } + break; + } + } + }); + + // On resizing window + window.addEventListener("resize", ClockGame); + +// Automations +Automation.ClockGame = setInterval(ClockGame, 20); + +// Features + // Converters + function ConvertEmptyName(Value) { + if(Value != "") { + return Value; + } else { + return "(未命名)"; + } + } + +// Error handling +function AlertSystemError(Message) { + console.error("● 系统错误\n" + + Message); + ShowDialog("System_Error", + "Error", + "抱歉,发生了系统错误。这可能是损坏了的用户数据导致的。是否前往用户数据?
    " + + "
    " + + "错误信息:" + Message, + "", "", "前往", "取消"); +} diff --git a/PROJECT/styles/style.css b/PROJECT/styles/style.css new file mode 100644 index 0000000..35248da --- /dev/null +++ b/PROJECT/styles/style.css @@ -0,0 +1,617 @@ +/* For SamToki.github.io/Yamanobo-Ryou */ +/* Released under GNU GPL v3 open source license. */ +/* (C) 2024 SAM TOKI STUDIO */ + +/* General */ + /* Basics */ + /* Overall */ + #Ctnr_BgImage { + background-image: url(../images/Background.jpg); + } + +/* Area specific */ + /* Header */ + #Ctrl_Title { + width: 172px; + } + #Ctrl_Nav { + width: calc(100% - 182px); + } + + /* Main */ + /* Game */ + /* Terrain */ + #Ctnr_GameTerrain { + position: absolute; bottom: calc(50% - 1980px); right: 50%; + } + .Terrain { + position: absolute; bottom: 0; left: 500000px; + min-width: 10px; height: 0; + background-color: #FFFFC0C0; + font-size: 2.00em; text-align: center; + } + .Terrain.Ahead { + color: #00000060; + } + #Terrain_Game0 { + width: 4000px; + } + + /* Characters */ + .Character { + position: absolute; bottom: calc(50% + 20px); left: unset; + width: 100px; + display: flex; + } + #Character_GamePlayer, #Character_GameChaser { + right: 50%; + } + #Ctnr_GameCharacters img { + width: 100%; border: none; + } + #GameChaserBalloon { + position: absolute; bottom: 20%; left: 175px; + width: 160px; height: 60%; transform-origin: bottom left; + filter: drop-shadow(0 0 1px #000000); + overflow: hidden; + } + #GameChaserBalloon.Faded { + transform: scale(0.5); + } + #GameChaserBalloon .CtrlGroup { + width: 100%; height: 100%; + } + #GameChaserBalloon .Ctrl { + position: absolute; top: unset; left: unset; + height: unset; + background-color: #E0E0E0; + } + #GameChaserBalloon .Balloon { + bottom: 50px; left: 20px; + width: calc(100% - 40px); border-radius: 5px; padding: 10px; + display: flex; flex-direction: column; justify-content: center; align-items: center; overflow: hidden; + } + #Label_GameDistance { + margin: 10px 0 0 0; + font-size: 1.50em; + } + #GameChaserBalloon .BalloonTail { + bottom: 0; left: 0; + width: 100%; height: 50px; clip-path: polygon(20% 0, 40% 0, 0 100%); + } + + /* Textbox */ + #Ctnr_GameTextbox { + position: absolute; top: calc(50% - 20px); left: 50%; + width: calc(50% - 175px); + } + #Textbox_Game { + border: none; border-radius: 0; padding: 0; + background-color: transparent; box-shadow: none !important; + color: #000000; font-size: 2.00em; + } + #Textbox_Game.HasTypo { + background-color: #FFA0A0C0; + } + + /* Progbar */ + #Ctnr_GameProgbar { + position: absolute; top: 0; left: 0; + width: 100%; height: 10px; + } + + /* Stats */ + #Ctnr_GameStats { + position: absolute; top: 25px; left: 175px; + width: calc(100% - 350px); + } + #Ctnr_GameStats .CtrlGroup { + width: 100%; + justify-content: center; + } + #Ctnr_GameStats .Ctrl { + width: 160px; height: 70px; padding: 5px; + text-align: center; + display: flex; flex-direction: column; justify-content: center; align-items: center; + } + .GameStatsValue { + font-size: 1.50em; + } + + /* Speed */ + #Ctnr_GameSpeed { + position: absolute; top: 25px; left: 0; + width: 145px; height: calc(100% - 50px); + } + #CtrlGroup_GameSpeed { + width: 100%; height: 100%; + } + /* Tape */ + .Tape { + position: absolute; top: 0; left: 0; + width: 100%; height: 100%; border-radius: 5px; + background-color: #FFFFFF80; backdrop-filter: blur(5px); + overflow: hidden; + } + .Tape .CtrlGroup { + position: absolute; top: unset; left: 0; + width: 100%; + flex-direction: column; + } + #CtrlGroup_GameSpeedTape { + top: calc(50% - 2500px); + height: 2500px; + } + .Tape .Ctrl { + width: 100%; height: 0; + display: flex; align-items: center; + } + #CtrlGroup_GameSpeedTape .Ctrl { + flex-direction: row-reverse; + } + .TapeScale { + width: 25px; height: 2px; border-radius: 1px; + background-color: #00000080; + } + .TapeNumber { + color: #00000080; font-size: 1.50em; + } + #CtrlGroup_GameSpeedTape .TapeNumber { + margin: 0 20px 0 0; + } + + /* Additional indicators */ + #Ctrl_GameSpeedAdditionalIndicators { + position: absolute; top: 0; left: 0; + width: calc(100% + 20px); height: 100%; + overflow: hidden; + pointer-events: none; + } + /* Speed trend */ + #CtrlGroup_GameSpeedTrend { + position: absolute; top: 0; left: 0; + width: 100%; height: 100%; + } + #Ctrl_GameSpeedTrend { + position: absolute; bottom: 50%; right: 35px; + width: 15px; height: 0; + display: flex; align-items: center; + } + #Ctrl_GameSpeedTrend.Increasing { + flex-direction: column-reverse; + } + #Ctrl_GameSpeedTrend.Decreasing { + flex-direction: column; + } + #GameSpeedTrendLine { + width: 2px; height: calc(100% - 10px); border-radius: 1px; + background-color: #000000; + } + #GameSpeedTrendArrow { + width: 15px; height: 15px; margin: -5px 0; + background-color: #000000; + } + #Ctrl_GameSpeedTrend.Increasing #GameSpeedTrendArrow { + clip-path: polygon(50% 0, 100% 100%, 0 100%); + } + #Ctrl_GameSpeedTrend.Decreasing #GameSpeedTrendArrow { + clip-path: polygon(0 0, 100% 0, 50% 100%); + } + + /* Other speeds */ + #CtrlGroup_GameOtherSpeeds { + position: absolute; top: calc(50% - 2500px); left: 0; + width: 100%; height: 2500px; + } + #CtrlGroup_GameOtherSpeeds .Ctrl { + position: absolute; top: unset; left: unset; + } + #CtrlGroup_GameOtherSpeeds #Ctrl_GameChaserSpeed { + bottom: 0; right: 10px; + width: 10px; height: 0; + background-color: #FFC000; + } + #CtrlGroup_GameOtherSpeeds #Ctrl_GameDangerousSpeed { + bottom: 0; right: 10px; + width: 10px; height: 0; + background-color: #FF0000; + } + #CtrlGroup_GameOtherSpeeds #Ctrl_GameAvgSpeed { + bottom: -35px; right: 0; + width: 20px; height: 70px; clip-path: polygon(100% 30%, 100% 70%, 0 50%); + background-color: #0090D0C0; + } + + /* Balloon */ + #Ctrl_GameSpeedBalloon { + position: absolute; top: calc(50% - 35px); left: 0; + width: 125px; height: 70px; + filter: drop-shadow(0 0 1px #000000); + } + #Ctrl_GameSpeedBalloon > .CtrlGroup { + width: 100%; height: 100%; + } + #Ctrl_GameSpeedBalloon > .CtrlGroup > .Ctrl { + position: absolute; top: 0; left: unset; + height: 100%; + background-color: #FFFFFF; + } + #Ctrl_GameSpeedBalloon .CtrlGroup .Balloon { + left: 0; + width: 105px; border-radius: 5px; + } + #Ctrl_GameSpeedBalloon .CtrlGroup .BalloonTail { + right: 0; + width: 20px; clip-path: polygon(100% 50%, 0 70%, 0 30%); + } + #Ctrl_GameSpeedBalloon .CtrlGroup .Balloon .CtrlGroup { + width: 100%; height: 100%; padding: 5px; + justify-content: end; + } + #Ctrl_GameSpeedBalloon .CtrlGroup .Balloon .Ctrl { + width: 20px; height: 100%; + } + #ScrollingNumber_GameSpeed1 { + top: calc(-45px * 5); + } + #ScrollingNumber_GameSpeed2 { + top: calc(-45px * 11); + } + #ScrollingNumber_GameSpeed3 { + top: calc(15px - 30px * 11); + } + .ScrollingNumber span { + height: 30px; margin: 15px 0; + font-size: 2.00em; + } + #ScrollingNumber_GameSpeed3 span { + margin: 0; + } + + /* Altitude */ + #Ctnr_GameAltitude { + position: absolute; top: 25px; right: 0; + width: 145px; height: calc(100% - 50px); + } + #CtrlGroup_GameAltitude { + width: 100%; height: 100%; + } + /* Tape */ + #CtrlGroup_GameAltitudeTape { + top: calc(50% - 30000px); + height: 32000px; + } + #CtrlGroup_GameAltitudeTape .Ctrl { + flex-direction: row; + } + #CtrlGroup_GameAltitudeTape .TapeNumber { + margin: 0 0 0 20px; + } + + /* Balloon */ + #Ctrl_GameAltitudeBalloon { + position: absolute; top: calc(50% - 35px); right: 0; + width: 125px; height: 70px; + filter: drop-shadow(0 0 1px #000000); + } + #Ctrl_GameAltitudeBalloon > .CtrlGroup { + width: 100%; height: 100%; + } + #Ctrl_GameAltitudeBalloon > .CtrlGroup > .Ctrl { + position: absolute; top: 0; left: unset; + height: 100%; + background-color: #FFFFFF; + } + #Ctrl_GameAltitudeBalloon .CtrlGroup .Balloon { + right: 0; + width: 105px; border-radius: 5px; + } + #Ctrl_GameAltitudeBalloon .CtrlGroup .BalloonTail { + left: 0; + width: 20px; clip-path: polygon(100% 30%, 100% 70%, 0 50%); + } + #Ctrl_GameAltitudeBalloon .CtrlGroup .Balloon .CtrlGroup { + width: 100%; height: 100%; padding: 5px; + justify-content: start; + } + #Ctrl_GameAltitudeBalloon .CtrlGroup .Balloon .Ctrl { + width: 20px; height: 100%; + } + #Ctrl_GameAltitudeBalloon .CtrlGroup .Balloon #Ctrl_GameAltitude4 { + width: 35px; + } + #ScrollingNumber_GameAltitude1 { + top: calc(-45px * 3); + } + #ScrollingNumber_GameAltitude2 { + top: calc(-45px * 11); + } + #ScrollingNumber_GameAltitude3 { + top: calc(-45px * 11); + } + #ScrollingNumber_GameAltitude4 { + top: calc(17.5px - 25px * 6); + } + #ScrollingNumber_GameAltitude4 span { + height: 25px; margin: 0; + font-size: 1.60em; + } + + /* Prompts */ + #Ctnr_GamePrompts { + position: absolute; bottom: 55px; left: 175px; + width: calc(100% - 350px); + } + #Ctnr_GamePrompts .CtrlGroup { + width: 100%; + flex-direction: column; align-items: center; + } + #Ctnr_GamePrompts .Ctrl { + height: unset; padding: 5px; margin: 10px 0 0 0; + } + + /* Game ctrl */ + #Ctnr_GameCtrl { + position: absolute; bottom: 0; left: calc(50% - 60px); + width: 120px; height: 40px; + } + #Ctnr_GameCtrl .CtrlGroup { + width: 100%; height: 100%; + } + #Ctnr_GameCtrl .Ctrl { + width: 100%; + } + #Ctnr_GameCtrl .HotkeyIndicator { + left: calc(50% - 20px); + width: 40px; + } + + /* Highscore */ + #ItemGroup_Highscore { + margin: 0; + flex-direction: column; align-items: center; + } + #ItemGroup_Highscore .Item { + width: unset; height: 50px; margin: 0; + display: flex; justify-content: space-between; + } + #ItemGroup_Highscore .Item span { + text-align: center; + display: flex; justify-content: center; align-items: center; + } + .HighscoreSequence { + width: 65px; + } + .HighscoreDate { + width: 125px; + } + .HighscoreScore { + width: 115px; + } + .HighscoreAvgSpeed { + width: 95px; + } + .HighscoreAvgKeystrokeSpeed { + width: 125px; + } + .HighscoreAccuracy { + width: 105px; + } + + /* Library */ + #Label_LibraryItemCount { + position: absolute; top: unset; right: 5px; + } + .ListItemLabel { + width: calc(100% - 82px); + } + .ListItemName { + max-width: 233px; + overflow: hidden; + } + #Library .Cmdbtn.ShownAsLabel { + width: 24px; height: 24px; margin: 0; + justify-content: center; + } + .ListItemDuplicate { + position: absolute; top: calc(50% - 12px); right: 53px; + } + .ListItemExport { + position: absolute; top: calc(50% - 12px); right: 29px; + } + .ListItemDelete { + position: absolute; top: calc(50% - 12px); right: 5px; + } + #Ctrl_LibraryImport { + width: calc(100% - 90px); height: 90px; + } + #Textbox_LibraryImport { + width: 100%; + white-space: pre; + } + #Ctrl_LibraryImport2 { + width: 80px; height: 90px; + } + +/* Responsive web design */ +@media (max-width: 1070px) { + /* Area specific */ + /* Header */ + #Ctrl_Title { + width: 40px; + } + #Ctrl_Nav { + width: calc(100% - 50px); + } +} +@media (max-width: 830px) { + /* Area specific */ + /* Header */ + #Ctrl_Nav .Nav { + width: calc(100% / 6); + } + + /* Main */ + /* Game */ + /* Terrain */ + #Ctnr_GameTerrain { + bottom: calc(85px + (100% - 260px) / 2 - 1980px); right: 50%; + } + + /* Characters */ + .Character { + bottom: calc(85px + (100% - 260px) / 2 + 20px); + width: 50px; + } + #GameChaserBalloon { + left: 0; + width: 70px; + } + #GameChaserBalloon .Balloon { + bottom: 40px; left: 0; + width: 100%; + } + #Label_GameDistance { + font-size: 1.00em; + } + #GameChaserBalloon .BalloonTail { + width: 100%; height: 40px; + } + + /* Textbox */ + #Ctnr_GameTextbox { + top: calc(175px + (100% - 260px) / 2 - 20px); + width: 50%; + } + + /* Stats */ + #Ctnr_GameStats { + left: 120px; + width: calc(100% - 240px); height: 150px; + } + #Ctnr_GameStats .CtrlGroup { + height: 100%; + } + #Ctnr_GameStats .Ctrl { + width: 50%; height: calc(100% / 3); + } + .GameStatsTitle { + font-size: 0.75em; + } + .GameStatsValue { + font-size: 1.00em; + } + + /* Speed */ + #Ctnr_GameSpeed { + width: 100px; height: 150px; + } + /* Tape */ + .TapeScale { + width: 15px; + } + .TapeNumber { + font-size: 1.00em; + } + + /* Additional indicators */ + #Ctrl_GameSpeedAdditionalIndicators { + width: calc(100% + 15px); + } + /* Speed trend */ + #Ctrl_GameSpeedTrend { + right: 25px; + width: 10px; + } + #GameSpeedTrendArrow { + width: 10px; height: 10px; + } + + /* Other speeds */ + #CtrlGroup_GameOtherSpeeds #Ctrl_GameChaserSpeed, #CtrlGroup_GameOtherSpeeds #Ctrl_GameDangerousSpeed { + width: 5px; + } + #CtrlGroup_GameOtherSpeeds #Ctrl_GameAvgSpeed { + width: 15px; clip-path: polygon(100% 36%, 100% 64%, 0 50%); + } + + /* Balloon */ + #Ctrl_GameSpeedBalloon { + top: calc(50% - 25px); + width: 85px; height: 50px; + } + #Ctrl_GameSpeedBalloon .CtrlGroup .Balloon { + width: 70px; + } + #Ctrl_GameSpeedBalloon .CtrlGroup .BalloonTail { + width: 15px; + } + #Ctrl_GameSpeedBalloon .CtrlGroup .Balloon .Ctrl { + width: 12.5px; + } + #ScrollingNumber_GameSpeed1 { + top: calc(-30px * 5); + } + #ScrollingNumber_GameSpeed2 { + top: calc(-30px * 11); + } + #ScrollingNumber_GameSpeed3 { + top: calc(10px - 20px * 11); + } + .ScrollingNumber span { + height: 20px; margin: 10px 0; + font-size: 1.25em; + } + + /* Altitude */ + #Ctnr_GameAltitude { + width: 100px; height: 150px; + } + /* Balloon */ + #Ctrl_GameAltitudeBalloon { + top: calc(50% - 25px); + width: 85px; height: 50px; + } + #Ctrl_GameAltitudeBalloon .CtrlGroup .Balloon { + width: 70px; + } + #Ctrl_GameAltitudeBalloon .CtrlGroup .BalloonTail { + width: 15px; + } + #Ctrl_GameAltitudeBalloon .CtrlGroup .Balloon .Ctrl { + width: 12.5px; + } + #Ctrl_GameAltitudeBalloon .CtrlGroup .Balloon #Ctrl_GameAltitude4 { + width: 22.5px; + } + #ScrollingNumber_GameAltitude1 { + top: calc(-30px * 3); + } + #ScrollingNumber_GameAltitude2 { + top: calc(-30px * 11); + } + #ScrollingNumber_GameAltitude3 { + top: calc(-30px * 11); + } + #ScrollingNumber_GameAltitude4 { + top: calc(12px - 16px * 6); + } + #ScrollingNumber_GameAltitude4 span { + height: 16px; + font-size: 1.00em; + } + + /* Prompts */ + #Ctnr_GamePrompts { + left: 0; + width: 100%; + } + + /* Highscore */ + .HighscoreAvgSpeed, .HighscoreAvgKeystrokeSpeed, .HighscoreAccuracy { /* .HiddenHorizontally */ + width: 0 !important; border: none !important; padding: 0 !important; margin: 0 !important; + opacity: 0 !important; + overflow: hidden !important; + pointer-events: none !important; + } +} diff --git a/PROJECT/styles/style_Dark.css b/PROJECT/styles/style_Dark.css new file mode 100644 index 0000000..dbd6dfc --- /dev/null +++ b/PROJECT/styles/style_Dark.css @@ -0,0 +1,81 @@ +/* For SamToki.github.io/Yamanobo-Ryou */ +/* Released under GNU GPL v3 open source license. */ +/* (C) 2024 SAM TOKI STUDIO */ + +/* This is a theme variant, so only colors along with few things are configured. */ + +/* Area specific */ + /* Main */ + /* Game */ + /* Terrain */ + .Terrain { + background-color: #404000C0; + } + .Terrain.Ahead { + color: #FFFFFF60; + } + + /* Characters */ + #GameChaserBalloon { + filter: drop-shadow(0 0 1px #FFFFFF); + } + #GameChaserBalloon .Ctrl { + background-color: #202020; + } + + /* Textbox */ + #Textbox_Game { + color: #FFFFFF; + } + #Textbox_Game.HasTypo { + background-color: #800000C0; + } + + /* Speed */ + /* Tape */ + .Tape { + background-color: #00000080; + } + .TapeScale { + background-color: #FFFFFF80; + } + .TapeNumber { + color: #FFFFFF80; + } + + /* Additional indicators */ + /* Speed trend */ + #GameSpeedTrendLine { + background-color: #FFFFFF; + } + #GameSpeedTrendArrow { + background-color: #FFFFFF; + } + + /* Other speeds */ + #CtrlGroup_GameOtherSpeeds #Ctrl_GameChaserSpeed { + background-color: #FFC000; + } + #CtrlGroup_GameOtherSpeeds #Ctrl_GameDangerousSpeed { + background-color: #FF4040; + } + #CtrlGroup_GameOtherSpeeds #Ctrl_GameAvgSpeed { + background-color: #00A0E0C0; + } + + /* Balloon */ + #Ctrl_GameSpeedBalloon { + filter: drop-shadow(0 0 1px #FFFFFF); + } + #Ctrl_GameSpeedBalloon > .CtrlGroup > .Ctrl { + background-color: #000000; + } + + /* Altitude */ + /* Balloon */ + #Ctrl_GameAltitudeBalloon { + filter: drop-shadow(0 0 1px #FFFFFF); + } + #Ctrl_GameAltitudeBalloon > .CtrlGroup > .Ctrl { + background-color: #000000; + } diff --git a/PROJECT/styles/style_Genshin.css b/PROJECT/styles/style_Genshin.css new file mode 100644 index 0000000..51a2f1b --- /dev/null +++ b/PROJECT/styles/style_Genshin.css @@ -0,0 +1,81 @@ +/* For SamToki.github.io/Yamanobo-Ryou */ +/* Released under GNU GPL v3 open source license. */ +/* (C) 2024 SAM TOKI STUDIO */ + +/* This is a theme variant, so only colors along with few things are configured. */ + +/* Area specific */ + /* Main */ + /* Game */ + /* Terrain */ + .Terrain { + background-color: #606000C0; + } + .Terrain.Ahead { + color: #EEE4D980; + } + + /* Characters */ + #GameChaserBalloon { + filter: drop-shadow(0 0 1px #EEE4D9); + } + #GameChaserBalloon .Ctrl { + background-color: #485267; + } + + /* Textbox */ + #Textbox_Game { + color: #EEE4D9; + } + #Textbox_Game.HasTypo { + background-color: #A00000C0; + } + + /* Speed */ + /* Tape */ + .Tape { + background-color: #282D3880; + } + .TapeScale { + background-color: #EEE4D980; + } + .TapeNumber { + color: #EEE4D980; + } + + /* Additional indicators */ + /* Speed trend */ + #GameSpeedTrendLine { + background-color: #EEE4D9; + } + #GameSpeedTrendArrow { + background-color: #EEE4D9; + } + + /* Other speeds */ + #CtrlGroup_GameOtherSpeeds #Ctrl_GameChaserSpeed { + background-color: #FFC000; + } + #CtrlGroup_GameOtherSpeeds #Ctrl_GameDangerousSpeed { + background-color: #FF4040; + } + #CtrlGroup_GameOtherSpeeds #Ctrl_GameAvgSpeed { + background-color: #00A0E0C0; + } + + /* Balloon */ + #Ctrl_GameSpeedBalloon { + filter: drop-shadow(0 0 1px #EEE4D9); + } + #Ctrl_GameSpeedBalloon > .CtrlGroup > .Ctrl { + background-color: #485267; + } + + /* Altitude */ + /* Balloon */ + #Ctrl_GameAltitudeBalloon { + filter: drop-shadow(0 0 1px #EEE4D9); + } + #Ctrl_GameAltitudeBalloon > .CtrlGroup > .Ctrl { + background-color: #485267; + } diff --git a/PROJECT/styles/style_HighContrast.css b/PROJECT/styles/style_HighContrast.css new file mode 100644 index 0000000..508dbbc --- /dev/null +++ b/PROJECT/styles/style_HighContrast.css @@ -0,0 +1,90 @@ +/* For SamToki.github.io/Yamanobo-Ryou */ +/* Released under GNU GPL v3 open source license. */ +/* (C) 2024 SAM TOKI STUDIO */ + +/* This is a theme variant, so only colors along with few things are configured. */ + +/* Area specific */ + /* Main */ + /* Game */ + #Game { + background-color: #0060A0; + } + /* Terrain */ + .Terrain { + background-color: #604000; + } + .Terrain.Ahead { + color: #FF80FF; + } + + /* Characters */ + #GameChaserBalloon { + filter: drop-shadow(0 0 1px #FFFFFF); + } + #GameChaserBalloon .Ctrl { + background-color: #402050; + } + + /* Textbox */ + #Textbox_Game { + color: #FFFFFF; + } + #Textbox_Game.HasTypo { + background-color: #FF0000; + } + + /* Stats */ + #Ctnr_GameStats .CtrlGroup { + border-radius: 5px; + background-color: #404040; + } + + /* Speed */ + /* Tape */ + .Tape { + background-color: #404040; + } + .TapeScale { + background-color: #FFFFFF; + } + .TapeNumber { + color: #FFFFFF; + } + + /* Additional indicators */ + /* Speed trend */ + #GameSpeedTrendLine { + background-color: #00FF00; + } + #GameSpeedTrendArrow { + background-color: #00FF00; + } + + /* Other speeds */ + #CtrlGroup_GameOtherSpeeds #Ctrl_GameChaserSpeed { + background-color: #FFC000; + } + #CtrlGroup_GameOtherSpeeds #Ctrl_GameDangerousSpeed { + background-color: #FF4040; + } + #CtrlGroup_GameOtherSpeeds #Ctrl_GameAvgSpeed { + background-color: #FF80FF; + } + + /* Balloon */ + #Ctrl_GameSpeedBalloon { + filter: drop-shadow(0 0 1px #FFFFFF); + } + #Ctrl_GameSpeedBalloon > .CtrlGroup > .Ctrl { + background-color: #000000; + } + + /* Altitude */ + /* Balloon */ + #Ctrl_GameAltitudeBalloon { + filter: drop-shadow(0 0 1px #FFFFFF); + } + #Ctrl_GameAltitudeBalloon > .CtrlGroup > .Ctrl { + background-color: #000000; + } diff --git a/README.md b/README.md new file mode 100644 index 0000000..19d03b2 --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# 简介 Description + +山田凉上山,「孤独摇滚!」同人打字训练游戏。屑凉被讨债的波奇酱追上山,开始无尽的逃亡。
    +Yamanobo-Ryou (lit. Yamada Ryou climbing mountain) is a *Bocchi the Rock!* Doujin typing game. Ryou, being run after by Bocchi-chan for her debt, starts an endless escape towards a mountain. + +![封面 Cover](/PREVIEW/Cover.png) + +本游戏相比其他打字游戏具备以下特色:
    +This game has the following features when compared to other typing games: + +- 结合了「登山」与「横板卷轴游戏」的元素。文字显示在斜坡上,且坡度会变化。这降低了可读性,从而增强了挑战性。
    Combined the elements of mountain climbing and side-scrolling games. Texts are displayed on a slope, and the gradient of the slope changes dynamically. This decreases readability and makes the game challenging. +- 打错字时不允许忽略,必须更正。这进一步增强了挑战性。
    Typos must be corrected, thus making the game more challenging. +- 可调节您身后的追逐者的速度,与其展开紧张刺激的竞速。
    You can adjust the speed of the chaser, and go on an exciting race against her. +- 可自由更换打字内容与角色图像。
    You can freely change the text you type, and the character images you see. +- 实时、直观地显示打字速度。
    Shows your real-time typing speed straightforwardly. + +# 预览 Preview + +![预览 Preview](/PREVIEW/Preview.png) +(Illustration credits: Pixiv ID [112301934](https://pixiv.net/en/artworks/112301934), [113047912](https://pixiv.net/en/artworks/113047912) by [@Grace C](https://pixiv.net/en/users/33777297)) + +# 立即游玩 Play Now + +https://SamToki.github.io/Yamanobo-Ryou + +可扫描二维码:
    +Scan the QR code to play on mobile devices: + +![二维码 QR code](/PREVIEW/QR%20code.png) + +# 教程 Tutorial + +[快速入门](/PROJECT/docs/山田凉上山%20快速入门.pdf)
    +[完整说明文档](/PROJECT/docs/山田凉上山%20说明文档.pdf) + +# 注意 Caution + +此网页应用(web app)是本人的个人网站的一部分,依赖其上层目录的组件。因此这个 repo 无法独立运行,请勿尝试下载运行源代码。请访问上文的链接,或下载 [Releases](https://github.com/SamToki/Yamanobo-Ryou/releases/latest) 中的文件包来使用。
    +This web app is a part of my personal website. It relies on components at its parent directory. Therefore this repository cannot run independently. Don't try to download and run the source code. Please open the link above, or download the zip file in the [releases](https://github.com/SamToki/Yamanobo-Ryou/releases/latest) to use the web app. + +# 版权说明 Copyright Info + +本站的源代码遵循 [GNU GPL v3 软件开源协议](https://www.gnu.org/licenses/gpl-3.0.en.html)。本站的部分内容为他人作品,版权为原作者所有,本站遵循 [fair use](https://zh.wikipedia.org/wiki/fair_use) 原则使用并于 Credits 标注来源。除上述内容之外,本站所有内容以及排版、美术设计均保留版权。
    +The source codes of this website are released under [GNU GPL v3 open source license](https://www.gnu.org/licenses/gpl-3.0.en.html). Some contents in the website are works by other people, and their copyrights belong to the corresponding authors. They are used under the ["fair use"](https://en.wikipedia.org/wiki/fair_use) principle and are credited. Other contents such as the page design are copyrighted. + +「孤独摇滚!」,原名「ぼっち・ざ・ろっく!」,作品版权为 © はまじあき/芳文社・アニプレックス。
    +The copyright of *Bocchi the Rock!* is © Aki Hamazi/Houbunsha, Aniplex. + +© 2024 SAM TOKI STUDIO