From b04b1bb795aad3ca1376130b9a4882c795dcd699 Mon Sep 17 00:00:00 2001 From: DanielsWrath Date: Sun, 28 Apr 2024 15:44:37 +0200 Subject: [PATCH 01/18] added code for capacitorjs --- css/book.css | 91 ++++++- css/buttons.css | 40 +++- css/card.css | 36 ++- css/code.css | 7 + css/drop.css | 2 +- css/loader.css | 225 +++++++++++++++++- css/menu.css | 14 +- css/overlay.css | 17 +- css/style.css | 15 ++ index.html | 20 +- js/koi/code/codeViewer.js | 11 +- js/koi/drop.js | 62 ++++- js/koi/fish/pattern/layer/layerRidge.js | 22 +- js/koi/fish/pattern/layer/layerShapeBody.js | 18 +- js/koi/fish/pattern/layer/layerShapeFin.js | 30 +-- js/koi/fish/pattern/layer/layerSpots.js | 19 +- js/koi/fish/pattern/layer/layerStripes.js | 28 +-- js/koi/fish/pattern/layer/layerWeb.js | 14 +- js/koi/gui/cards/buttons/cardLoadButton.js | 42 ++++ js/koi/gui/cards/buttons/cardMenuButton.js | 42 ++++ js/koi/gui/cards/card.js | 7 +- js/koi/gui/cards/cardBook.js | 64 +++-- js/koi/gui/cards/cardPage.js | 14 +- js/koi/gui/cards/cards.js | 23 +- js/koi/gui/loader/loader.js | 35 ++- js/koi/gui/loader/loaderAndroid.js | 65 +++++ js/koi/gui/loader/loaderApple.js | 66 +++++ js/koi/gui/loader/loaderDiscord.js | 4 +- js/koi/gui/loader/loaderLinks.js | 70 ++++++ js/koi/gui/loader/loaderLoadInfo.js | 63 +++++ js/koi/gui/loader/loaderWebsite.js | 62 +++++ js/koi/gui/menu/menu.js | 1 + js/koi/gui/overlay/overlay.js | 93 +++++++- js/koi/koi.js | 1 + js/koi/tutorial/tutorialBreeding.js | 16 +- js/koi/tutorial/tutorialCards.js | 32 ++- js/main.js | 182 ++++++++++++-- js/render/blit.js | 2 +- js/render/blur.js | 4 +- js/render/bodies.js | 19 +- js/render/commonShaders.js | 18 +- js/render/distanceField.js | 8 +- js/render/drops.js | 2 +- js/render/fishBackground.js | 6 +- js/render/ponds.js | 2 +- js/render/preview.js | 14 +- js/render/sand.js | 4 +- js/render/shadows.js | 12 +- js/render/still.js | 11 + js/render/waves.js | 22 +- js/render/wind.js | 16 +- js/storage/storageCapacitor.js | 103 ++++++++ js/storage/storageFileCapacitor.js | 205 ++++++++++++++++ js/storage/storageLocal.js | 36 ++- js/storage/storagePreferencesCapacitor.js | 103 ++++++++ ...e_App_Store_Badge_US-UK_RGB_blk_092917.svg | 46 ++++ svg/Google_Play_Store_badge_EN_WHITE.svg | 13 + svg/add.svg | 1 + svg/butterfly.svg | 5 + svg/discord-mark-white.svg | 1 + svg/settings.svg | 1 + svg/website.svg | 1 + 62 files changed, 1947 insertions(+), 261 deletions(-) create mode 100644 js/koi/gui/cards/buttons/cardLoadButton.js create mode 100644 js/koi/gui/cards/buttons/cardMenuButton.js create mode 100644 js/koi/gui/loader/loaderAndroid.js create mode 100644 js/koi/gui/loader/loaderApple.js create mode 100644 js/koi/gui/loader/loaderLinks.js create mode 100644 js/koi/gui/loader/loaderLoadInfo.js create mode 100644 js/koi/gui/loader/loaderWebsite.js create mode 100644 js/storage/storageCapacitor.js create mode 100644 js/storage/storageFileCapacitor.js create mode 100644 js/storage/storagePreferencesCapacitor.js create mode 100644 svg/Download_on_the_App_Store_Badge_US-UK_RGB_blk_092917.svg create mode 100644 svg/Google_Play_Store_badge_EN_WHITE.svg create mode 100644 svg/add.svg create mode 100644 svg/butterfly.svg create mode 100644 svg/discord-mark-white.svg create mode 100644 svg/settings.svg create mode 100644 svg/website.svg diff --git a/css/book.css b/css/book.css index 39c21aa3..55ed128c 100644 --- a/css/book.css +++ b/css/book.css @@ -29,9 +29,9 @@ background-color: var(--card-color-background); display: flex; align-items: center; - transition: var(--book-hide-time) ease; - transition-property: top; - box-shadow: 0 0 var(--book-shadow-radius) var(--book-shadow-color); + /*transition: var(--book-hide-time) ease;*/ + /*transition-property: top;*/ + /*box-shadow: 0 0 var(--book-shadow-radius) var(--book-shadow-color);*/ user-select: none; border-radius: var(--card-border-radius); } @@ -68,6 +68,11 @@ opacity: 0; } +#book #spine .page { + transform: none !important; + /*opacity: 0 !important;*/ +} + #book #spine .page .overlay.left { background-color: var(--color-white); } @@ -105,7 +110,7 @@ #book #spine .page .slot { position: relative; background-color: var(--book-page-slot-color); - border-radius: var(--card-border-radius); + border-radius: calc(var(--card-border-radius) * var(--card-scale)); overflow: hidden; } @@ -118,7 +123,24 @@ #book #spine .page .slot .card { margin: 0; transform-origin: left top; - box-shadow: none; + /*box-shadow: none;*/ +} + +#cards button:active svg { + filter: drop-shadow(0 0 var(--page-button-shadow-radius) var(--book-shadow-color)); + +} + +#button-load-card { + position: absolute; + right: var(--book-button-padding); + top: calc(var(--button-height) * 2); +} + +#button-home { + position: absolute; + right: var(--book-button-padding); + top: var(--button-height); } #button-book { @@ -128,7 +150,7 @@ } #button-book svg { - filter: drop-shadow(0 0 var(--page-button-shadow-radius) var(--book-shadow-color)); + /*filter: drop-shadow(0 0 var(--page-button-shadow-radius) var(--book-shadow-color));*/ } #button-book:active path.page { @@ -159,7 +181,7 @@ } .button-page svg { - filter: drop-shadow(0 0 var(--page-button-shadow-radius) var(--book-shadow-color)); + /*filter: drop-shadow(0 0 var(--page-button-shadow-radius) var(--book-shadow-color));*/ } .button-page path { @@ -187,3 +209,58 @@ .button-page.right { right: calc(var(--button-width) * var(--page-button-page-shift) * -1); } + +@media only screen and (orientation: portrait) { + #button-load-card { + position: absolute; + right: calc(var(--button-width) * 2 + var(--book-button-padding)); + top: var(--book-button-padding); + } + + #button-home { + position: absolute; + right: calc(var(--button-width) + var(--book-button-padding)); + top: var(--book-button-padding); + } +} + +@media only screen and (orientation: portrait) and (max-width: 600px), (orientation: portrait) and (max-height: 600px) { + #button-load-card { + position: absolute; + right: calc(var(--button-width-small) * 2 + var(--book-button-padding)); + top: var(--book-button-padding); + } + + #button-home { + position: absolute; + right: calc(var(--button-width-small) + var(--book-button-padding)); + top: var(--book-button-padding); + } +} + +@media only screen and (orientation: landscape) and (max-width: 600px), (orientation: landscape) and (max-height: 600px) { + #button-load-card { + position: absolute; + right: var(--book-button-padding); + top: calc(var(--button-height-small) * 2); + } + + #button-home { + position: absolute; + right: var(--book-button-padding); + top: var(--button-height-small); + } + +} + +/*@media screen and (max-width: 600px), (max-height: 600px) {*/ +/* #button-load-card {*/ +/* right: calc(var(--button-width-small) * 2 + var(--book-button-padding));*/ +/* top: var(--book-button-padding);*/ +/* }*/ + +/* #button-home {*/ +/* right: calc(var(--button-width-small) + var(--book-button-padding));*/ +/* top: var(--book-button-padding);*/ +/* }*/ +/*}*/ \ No newline at end of file diff --git a/css/buttons.css b/css/buttons.css index 176af954..5c207bec 100644 --- a/css/buttons.css +++ b/css/buttons.css @@ -1,12 +1,18 @@ :root { - --button-width: 80px; - --button-height: 80px; + --button-width: 5rem; + --button-height: 5rem; + + --button-height-small: 3.5rem; + --button-width-small: 3.5rem; + --load-card-button-padding: 10px; } button { + /*display: flex;*/ width: var(--button-width); height: var(--button-height); + /*flex: 1;*/ border: none; box-sizing: border-box; cursor: pointer; @@ -18,17 +24,29 @@ button { font-size: 18pt; font-family: inherit; user-select: none; + -webkit-tap-highlight-color: transparent; } -.load-card-button { +.home-button { position: absolute; - left: 0; + right: 0; top: 0; - width: calc(var(--button-width) * 2); - background-color: var(--code-button-color); - box-shadow: 0 0 var(--code-shadow-radius) var(--code-shadow-color); - padding: 0 var(--load-card-button-padding); - margin-top: var(--code-button-margin); - margin-left: var(--code-button-margin); - border-radius: var(--code-button-radius); + +} + +@media (max-width: 600px) or (max-height: 600px){ + button { + font-size: 14pt; + width: var(--button-width-small); + height: var(--button-height-small); + } + + .button-page.left { + left: calc(var(--button-width-small) * var(--page-button-page-shift) * -1); } + + .button-page.right { + right: calc(var(--button-width-small) * var(--page-button-page-shift) * -1); + } + +} \ No newline at end of file diff --git a/css/card.css b/css/card.css index 4a3e5233..e6795c1a 100644 --- a/css/card.css +++ b/css/card.css @@ -4,15 +4,22 @@ --card-color-background-graphics: #7e605e; --card-color-drop-target: #ffffff55; + --card-scale: 1; + + --card-width-default: 250px; + --card-height-default: 350px; + --card-preview-width-default: 200px; + --card-preview-height-default: 120px; + /* Dimensions */ - --card-width: 250px; - --card-height: 350px; - --card-preview-width: 200px; - --card-preview-height: 120px; + --card-width: calc(var(--card-width-default) * var(--card-scale)); + --card-height: calc(var(--card-height-default) * var(--card-scale)); + --card-preview-width: calc(var(--card-preview-width-default) * var(--card-scale)); + --card-preview-height: calc(var(--card-preview-height-default) * var(--card-scale)); --card-preview-margin: calc((var(--card-width) - var(--card-preview-width) - 2 * var(--card-preview-border)) * 0.5); --card-preview-columns: 6; --card-preview-rows: 10; - --card-preview-fps: 60; + --card-preview-fps: 30; --card-preview-border-radius: 6px; --card-preview-border: 2px; --card-border-radius: 12px; @@ -21,15 +28,17 @@ --card-colors-raise: 0.5; --card-colors-overlap: 0.25; --card-colors-gradient: 280%; - --card-info-spacing: 12px; + --card-info-spacing: 10px; --card-info-color: #d7d4c8; --card-info-transparant-color: rgba(215, 212, 200, 0); --card-info-slant: -18deg; - --card-info-padding-right: 6px; + --card-info-padding-right: 2px; --card-info-text-color: #0c0c0c; - --card-code-icon-radius: 32px; - --card-code-icon-margin: 8px; + --card-code-icon-radius: 14px; + --card-code-icon-margin: 2px; --card-code-opacity: 0.5; + + --font-size: .7rem; } .card-shape { @@ -46,9 +55,10 @@ background-color: var(--card-color-background); position: absolute; user-select: none; - box-shadow: 0 0 var(--book-shadow-radius) var(--book-shadow-color); + /*box-shadow: 0 0 var(--book-shadow-radius) var(--book-shadow-color);*/ overflow: hidden; -webkit-tap-highlight-color: transparent; + /*scale: 0.7;*/ } .card svg { @@ -170,7 +180,7 @@ .card .info .property { margin-top: calc(var(--card-info-spacing) / 4); margin-bottom: calc(var(--card-info-spacing) / 4); - font-size: 20px; + font-size: var(--font-size); position: relative; } @@ -261,8 +271,8 @@ left: 50%; bottom: calc(var(--card-drop-target-raise) * var(--card-height)); background-color: var(--card-color-drop-target); - transition: 0.1s; - transition-property: bottom; + /*transition: 0.1s;*/ + /*transition-property: bottom;*/ } #drop-target.hidden { diff --git a/css/code.css b/css/code.css index e7df75e9..52099a56 100644 --- a/css/code.css +++ b/css/code.css @@ -52,6 +52,13 @@ pointer-events: auto; } +@media (max-height: 600px) { + #code .view canvas { + width: 50%; + height: 50%; + } +} + #code .view button { width: 50%; background-color: var(--code-button-color); diff --git a/css/drop.css b/css/drop.css index 2ec5ef75..c8bfcdec 100644 --- a/css/drop.css +++ b/css/drop.css @@ -9,5 +9,5 @@ } #drop.possible { - background-color: #f6f6f655; + background-color: rgba(246, 246, 246, 0); } \ No newline at end of file diff --git a/css/loader.css b/css/loader.css index f510147b..3796e0b6 100644 --- a/css/loader.css +++ b/css/loader.css @@ -15,7 +15,6 @@ --loader-fullscreen-border: 8px; --loader-fullscreen-corner-size: 30%; } - #loader { position: absolute; left: 0; @@ -27,7 +26,7 @@ align-items: center; justify-content: center; flex-direction: column; - transition: var(--loader-fade-out); + transition: var(--loader-fade-out) opacity; } #loader canvas { @@ -39,29 +38,68 @@ pointer-events: none; } -#loader #loader-discord { +#loader-links { position: absolute; right: 0; bottom: 0; - width: 400px; - height: 136px; - transition: var(--loader-fade-in); + display: flex; + flex-direction: row; + align-items: flex-end; + justify-content: flex-end; + width: 100%; + height: 4rem; + max-height: 100px; + padding: 16px; + +} + +#loader-links .loader-icon { + width: 4rem; + margin: auto 0; + transition: var(--loader-fade-in) opacity; transition-timing-function: ease-in; } -#loader #loader-discord path { +#loader-links .loader-icon svg { + width: 100%; + height: 100%; +} + +#loader-links:hover .loader-bar { + opacity: 100%; +} + +#loader #loader-discord path, +#loader #loader-website path { fill: #FEFFFF; } -#loader #loader-discord.invisible { +/*#loader #loader-discord svg{*/ +/* height: 100%;*/ +/* width: auto;*/ +/*}*/ + +/*#loader #loader-website svg {*/ +/* !*fill: #FEFFFF;*!*/ +/* height: 85%;*/ +/* width: auto;*/ +/*}*/ + +#loader .loader-icon.invisible, +#loader .loader-bar.invisible { opacity: 0; } -#loader #loader-discord:hover { +#loader #loader-discord:hover, +#loader #loader-website:hover { cursor: pointer; } -#loader #loader-discord:hover path { +#loader #loader-website:hover path { + fill: #fca938; +} + +#loader #loader-discord:hover path{ fill: #778BD8; } @@ -130,8 +168,10 @@ #loader-graphics { display: flex; justify-content: center; + align-items: center; width: 100%; margin-bottom: var(--loader-logo-margin); + flex-direction: column; } #loader-slots { @@ -139,6 +179,8 @@ justify-content: center; width: 100%; height: 20%; + align-items: center; + align-content: center; } #loader-slots .loader-slot { @@ -174,9 +216,35 @@ margin-bottom: var(--loader-button-margin); } +.loader-bar { + /*width: 100%;*/ + height: var(--loader-logo-width); + /*background-color: var(--color-white);*/ + border-radius: 5px; + /*margin-top: 10px;*/ + /*width: 500px;*/ + margin: auto 0; + align-self: flex-start; + justify-self: flex-start; + transition: var(--loader-fade-in) opacity; + transition-timing-function: ease-in; + cursor: pointer; + opacity: 0; +} + +.loader-bar svg { + width: 100%; + height: 100%; +} + +.loader-bar:hover { + filter: invert(100%); + /*box-shadow: 0 0 10px 0 var(--color-white);*/ +} + #loader-icon { width: var(--loader-logo-width); - transition: var(--loader-fade-in); + transition: var(--loader-fade-in) opacity; transition-timing-function: ease-in; } @@ -205,7 +273,7 @@ justify-content: center; margin-bottom: var(--loader-button-margin); opacity: 0; - transition: var(--loader-button-fade-in); + transition: var(--loader-button-fade-in) opacity; transition-timing-function: ease-in; } @@ -215,7 +283,7 @@ .loader-button button { width: 100%; - transition: 0.5s; + transition: 0.5s opacity; background-color: var(--color-white); color: var(--loader-button-text-color); font-weight: bold; @@ -238,4 +306,135 @@ background-color: var(--color-white); color: var(--loader-button-text-color); transition: 0s; +} + +#loader-loading { + display: flex; + justify-content: center; + align-items: center; + flex-direction: row; + transition: 0.5s opacity; + position: absolute; + top: 20px; +} + +#loader-loading.invisible { + opacity: 0; + animation: none; +} + +#loader-loading-text { + color: var(--color-white); + font-size: 1.5em; + margin-left: 5px; +} + +#loader-loading-icon{ + width: 20px; + animation: flutter 1s ease-in-out infinite; + transform-origin: top left; +} + +@media only screen and (orientation: portrait) { + #loader-slots { + flex-direction: column; + height: unset; + + } + + #loader-slots .loader-slot { + flex-direction: row; + width: 60%; + } + + #loader-slots .loader-slot h1 { + color: var(--color-white); + position: absolute; + left: -60px; + bottom: 16px; + /* top: -60px; */ + top: unset; + width: 60px; + height: 60px; + user-select: none; + background-color: var(--loader-button-text-color); + border-top-left-radius: 60px; + border-bottom-left-radius: 60px; + /*border-bottom-left-radius: 60px;*/ + display: flex; + justify-content: flex-end; + flex-direction: column; + margin: 0; + text-align: center; + line-height: 60px; + padding: 0; + } + + #loader-slots .loader-slot button:nth-child(2) { + border-bottom-left-radius: 0; + } + + #loader-slots .loader-slot button:not(:last-child) { + margin-right: 10px; + } + + .loader-button { + width: 60%; + } +} + +@media only screen and (orientation: landscape) { + +} + +/* !* For Mobile Portrait View *!*/ +/*@media screen*/ +/* and (max-device-width: 480px)*/ +/* and (orientation: portrait){*/ +/* html{*/ +/* background-color: red;*/ +/* width: 80%;*/ +/* }*/ +/*}*/ + +/*!* For Mobile Landscape View *!*/ +/*@media screen*/ +/* and (max-device-width: 640px)*/ +/* and (orientation: landscape){*/ +/* html{*/ +/* background-color: red;*/ + +/* }*/ +/*}*/ + +/*!* For Mobile Phones Portrait or Landscape View *!*/ +/*@media screen*/ +/* and (max-device-width: 640px){*/ +/* html{*/ +/* background-color: red;*/ + +/* }*/ +/*}*/ + +@keyframes flutter { + 0% { + transform: skew(0, 0) scale(1); + } + + 20% { + transform: translateY(100%) skew(-10deg, -10deg) scale(1) translateY(-100%); + } + + 40% { + transform: translateY(100%) skew(10deg, 10deg) scale(1.1) translateY(-100%); + } + + 60% { + transform: translateY(100%) skew(-5deg, -5deg) scale(.9) translateY(-100%); + } + + 80% { + transform: translateY(100%) skew(0deg, 0deg) scale(1) translateY(-100%); + } + } \ No newline at end of file diff --git a/css/menu.css b/css/menu.css index 9706e88c..d29c18a2 100644 --- a/css/menu.css +++ b/css/menu.css @@ -4,6 +4,7 @@ --menu-box-spacing: 16px; --menu-button-color: var(--color-water-deep); --menu-button-border-radius: var(--loader-button-border-radius); + --menu-font-size: 1rem; } #menu { @@ -41,6 +42,7 @@ #menu-box h1 { color: var(--color-white); + margin: unset; } #menu-box button { @@ -53,6 +55,7 @@ padding-left: calc(2 * var(--menu-box-spacing)); padding-right: calc(2 * var(--menu-box-spacing)); height: 54px; + max-height: 5rem; } #menu-box button:active { @@ -62,7 +65,7 @@ #menu-box label { color: var(--color-white); - font-size: 18px; + font-size: var(--menu-font-size); margin-right: var(--menu-box-spacing); } @@ -72,12 +75,21 @@ #menu-box input[type=checkbox] { float: left; + min-width: 1rem; + min-height: 1rem; } #menu-box select { width: 100%; + min-height: 1.5rem; } #menu-box table { text-align: right; +} + +@media screen and (max-width: 600px) or (max-height: 600px){ + #menu-box button { + max-height: var(--button-height-small); + } } \ No newline at end of file diff --git a/css/overlay.css b/css/overlay.css index 954de70f..c4e9c5d2 100644 --- a/css/overlay.css +++ b/css/overlay.css @@ -58,7 +58,7 @@ text-align: center; background-color: #325c73cc; color: var(--color-white); - font-size: 32px; + font-size: 1.5rem; border-radius: 16px; border: 2px solid var(--color-white); } @@ -100,4 +100,19 @@ animation: arrow-bounce-up var(--overlay-arrow-animation-time) infinite; animation-timing-function: ease-in-out; transform: scale(var(--overlay-arrow-scale), 1) rotate(45deg); +} + +.skip-button { + /*margin-top: var(--overlay-text-top);*/ + pointer-events: auto; + /*touch-action: all;*/ + user-select: auto; + position: absolute; + left: 10px; + bottom: var(--overlay-text-top); + background: #325c73cc; + color: var(--color-white); + font-size: 1.5rem; + border-radius: 16px; + border: 2px solid var(--color-white); } \ No newline at end of file diff --git a/css/style.css b/css/style.css index ddfd0c75..cbdcaf23 100644 --- a/css/style.css +++ b/css/style.css @@ -1,6 +1,11 @@ +:root { + --padding-top-notch: env(safe-area-inset-top); +} + body { margin: 0; font-family: Grandstander; + background: var(--color-shrubbery-leaf); } #wrapper { @@ -10,6 +15,7 @@ body { height: 100%; } + #gui { pointer-events: none; position: absolute; @@ -19,6 +25,7 @@ body { height: 100%; } + #gui #cards { position: absolute; left: 0; @@ -34,4 +41,12 @@ body { top: 0; width: 100%; height: 100%; +} + +/* Adjust padding for devices with a notch */ +@media only screen and (orientation: portrait) { + .notch-padded { + padding-top: max(env(safe-area-inset-top), 30px); + padding-bottom: env(safe-area-inset-bottom); /* iOS 11.2+ */ + } } \ No newline at end of file diff --git a/index.html b/index.html index c2fc78fb..d0c90132 100644 --- a/index.html +++ b/index.html @@ -7,6 +7,10 @@ font-family: "Grandstander"; src: url("font/Grandstander.ttf"); } + + body { + background: #4b812e; + } @@ -22,12 +26,13 @@ - + +
- +
@@ -42,17 +47,26 @@ + + + + + + + + + @@ -290,6 +304,8 @@ + + diff --git a/js/koi/code/codeViewer.js b/js/koi/code/codeViewer.js index 40fc7d37..66993a18 100644 --- a/js/koi/code/codeViewer.js +++ b/js/koi/code/codeViewer.js @@ -42,6 +42,15 @@ CodeViewer.prototype.createButtonCopy = function(image, audio) { return button; }; +/** + * Create a file name + * @returns {String} The file name. + */ +CodeViewer.prototype.createFileName = function() { + const datetimeString = new Date().toISOString().replace(/:/g, "-"); + return `koi-${datetimeString}.png`; +} + /** * Create the download button * @param {HTMLCanvasElement} image The fish code image @@ -64,7 +73,7 @@ CodeViewer.prototype.createButtonDownload = function(image, audio) { window.webkit.messageHandlers.saveImage.postMessage({blob: base64data}); } } else { - this.storage.imageToFile(blob, this.DEFAULT_NAME) + this.storage.imageToFile(blob, this.createFileName()) } }); diff --git a/js/koi/drop.js b/js/koi/drop.js index 97aa1585..8ecd991a 100644 --- a/js/koi/drop.js +++ b/js/koi/drop.js @@ -62,7 +62,7 @@ Drop.prototype.drop = function(event) { if (this.gui.cards.hand.isFull()) break; - this.dropFile(file, new Vector2(event.clientX, event.clientY)); + this.dropFile(file, new Vector2(event.clientX || window.screen.width / 2, event.clientY || window.screen.height / 2)); } } }; @@ -73,12 +73,22 @@ Drop.prototype.drop = function(event) { * @param {Vector2} target The position the file was dropped at */ Drop.prototype.dropFile = function(file, target) { - if (!this.IMAGE_TYPES.includes(file.type)) + if (file.type && !this.IMAGE_TYPES.includes(file.type) || file.mimeType && !this.IMAGE_TYPES.includes(file.mimeType)) { + if (Capacitor && Capacitor.Plugins && Capacitor.isPluginAvailable('Toast')) + Capacitor.Plugins.Toast.show({text: "Could not load koi"}); + return; + } - const reader = new FileReader(); + if (file.data && file.data.length > 0) { + let base64Data; + + if (file.data.startsWith('data:image')) { + base64Data = file.data; + } else { + base64Data = `data:${file.mimeType};base64,` + file.data; + } - reader.onload = event => { const image = new Image(); image.onload = () => { @@ -101,11 +111,49 @@ Drop.prototype.dropFile = function(file, target) { this.gui.cards.add(card); } + else { + if (Capacitor && Capacitor.Plugins && Capacitor.isPluginAvailable('Toast')) + Capacitor.Plugins.Toast.show({text: "Could not load koi"}); + } + } else { + if (Capacitor && Capacitor.Plugins && Capacitor.isPluginAvailable('Toast')) + Capacitor.Plugins.Toast.show({text: "Hand is full"}); } }; - image.src = event.target.result; - }; + image.src = base64Data; + } else { + const reader = new FileReader(); + + reader.onload = event => { + const image = new Image(); + + image.onload = () => { + if (!this.gui.cards.hand.isFull()) { + const body = new CodeReader(image).read(); + + if (body) { + const buffer = new BinBuffer(); - reader.readAsDataURL(file); + body.pattern.serialize(buffer); + body.initializeSpine(new Vector2(), new Vector2(1, 0)); + + const card = new Card(body, target, 0); + + card.initialize( + this.systems.preview, + this.systems.atlas, + this.systems.bodies, + this.systems.randomSource); + + this.gui.cards.add(card); + } + } + }; + + image.src = event.target.result; + }; + + reader.readAsDataURL(file); + } }; \ No newline at end of file diff --git a/js/koi/fish/pattern/layer/layerRidge.js b/js/koi/fish/pattern/layer/layerRidge.js index b6065ae0..9a433be8 100644 --- a/js/koi/fish/pattern/layer/layerRidge.js +++ b/js/koi/fish/pattern/layer/layerRidge.js @@ -41,7 +41,7 @@ LayerRidge.prototype.SHADER_VERTEX = `#version 100 attribute vec2 position; attribute vec2 uv; -varying mediump vec2 iUv; +varying highp vec2 iUv; void main() { iUv = uv; @@ -53,25 +53,25 @@ void main() { LayerRidge.prototype.SHADER_FRAGMENT = `#version 100 ` + CommonShaders.cubicNoise3 + ` uniform lowp vec3 color; -uniform mediump float scale; -uniform mediump float power; -uniform mediump float threshold; -uniform mediump float focus; -uniform mediump float focusPower; -uniform mediump vec2 size; +uniform highp float scale; +uniform highp float power; +uniform highp float threshold; +uniform highp float focus; +uniform highp float focusPower; +uniform highp vec2 size; uniform highp vec3 origin; uniform highp mat3 rotate; -varying mediump vec2 iUv; +varying highp vec2 iUv; #define RIDGE_ATTENUATION 1.4 #define ATTENUATION 2.0 void main() { - mediump float phaseThreshold = pow(1.0 - RIDGE_ATTENUATION * abs(iUv.y - 0.5), power); + highp float phaseThreshold = pow(1.0 - RIDGE_ATTENUATION * abs(iUv.y - 0.5), power); highp vec2 at = (iUv - vec2(0.5)) * size * scale; - mediump float noise = cubicNoise(origin + vec3(at, 0.0) * rotate); - mediump float strength = pow(max(0.0, 1.0 - ATTENUATION * abs(iUv.x - focus)), focusPower); + highp float noise = cubicNoise(origin + vec3(at, 0.0) * rotate); + highp float strength = pow(max(0.0, 1.0 - ATTENUATION * abs(iUv.x - focus)), focusPower); if (noise > phaseThreshold * strength) discard; diff --git a/js/koi/fish/pattern/layer/layerShapeBody.js b/js/koi/fish/pattern/layer/layerShapeBody.js index 4354a341..69694a76 100644 --- a/js/koi/fish/pattern/layer/layerShapeBody.js +++ b/js/koi/fish/pattern/layer/layerShapeBody.js @@ -37,21 +37,21 @@ void main() { `; LayerShapeBody.prototype.SHADER_FRAGMENT = `#version 100 -uniform mediump float centerPower; -uniform mediump float radiusPower; -uniform mediump float eyePosition; -uniform mediump float shadePower; -uniform mediump float lightPower; -uniform mediump float ambient; -uniform mediump vec2 size; +uniform highp float centerPower; +uniform highp float radiusPower; +uniform highp float eyePosition; +uniform highp float shadePower; +uniform highp float lightPower; +uniform highp float ambient; +uniform highp vec2 size; uniform lowp vec3 shadeColor; -varying mediump vec2 iUv; +varying highp vec2 iUv; #define EYE_SHADE_PUPIL 0.2 #define EYE_RADIUS_PUPIL 0.1 -mediump float getRadius(mediump float x) { +highp float getRadius(highp float x) { return pow(cos(3.141592 * (pow(x, centerPower) - 0.5)), radiusPower); } diff --git a/js/koi/fish/pattern/layer/layerShapeFin.js b/js/koi/fish/pattern/layer/layerShapeFin.js index 54b1b24d..c89e91e9 100644 --- a/js/koi/fish/pattern/layer/layerShapeFin.js +++ b/js/koi/fish/pattern/layer/layerShapeFin.js @@ -24,11 +24,12 @@ LayerShapeFin.prototype.SAMPLER_DIPS = new SamplerPower(.25, 3, 1.5); LayerShapeFin.prototype.SAMPLER_DIP_POWER = new SamplerPower(.5, 2, .7); LayerShapeFin.prototype.SAMPLER_ROUNDNESS = new SamplerPower(.05, .25, .6); +// language=glsl LayerShapeFin.prototype.SHADER_VERTEX = `#version 100 attribute vec2 position; attribute vec2 uv; -uniform mediump float angle; +uniform highp float angle; varying vec2 iUv; varying float iBeta; @@ -46,25 +47,26 @@ void main() { } `; +// language=glsl LayerShapeFin.prototype.SHADER_FRAGMENT = `#version 100 -uniform mediump float angle; -uniform mediump float inset; -uniform mediump float dips; -uniform mediump float dipPower; -uniform mediump float roundness; +uniform highp float angle; +uniform highp float inset; +uniform highp float dips; +uniform highp float dipPower; +uniform highp float roundness; -varying mediump vec2 iUv; -varying mediump float iBeta; -varying mediump float iCutaway; -varying mediump float iFinRadius; +varying highp vec2 iUv; +varying highp float iBeta; +varying highp float iCutaway; +varying highp float iFinRadius; #define ALPHA 0.8 void main() { - mediump float radiusProgress = clamp((atan(iUv.y, iUv.x) - iBeta) / angle, 0.0, 1.0); - mediump float radiusMultiplier = 1.0 - inset + inset * pow(cos(radiusProgress * 6.283185 * dips) * 0.5 + 0.5, dipPower); - mediump float radius = length(iUv); - mediump float roundnessMultiplier = pow(sin(radiusProgress * 3.141593), roundness); + highp float radiusProgress = clamp((atan(iUv.y, iUv.x) - iBeta) / angle, 0.0, 1.0); + highp float radiusMultiplier = 1.0 - inset + inset * pow(cos(radiusProgress * 6.283185 * dips) * 0.5 + 0.5, dipPower); + highp float radius = length(iUv); + highp float roundnessMultiplier = pow(sin(radiusProgress * 3.141593), roundness); if (radius > iFinRadius * radiusMultiplier * roundnessMultiplier || iUv.x < sqrt(iUv.y) * iCutaway || diff --git a/js/koi/fish/pattern/layer/layerSpots.js b/js/koi/fish/pattern/layer/layerSpots.js index afadcf44..c1e9cca7 100644 --- a/js/koi/fish/pattern/layer/layerSpots.js +++ b/js/koi/fish/pattern/layer/layerSpots.js @@ -53,26 +53,27 @@ void main() { } `; +// language=glsl LayerSpots.prototype.SHADER_FRAGMENT = `#version 100 ` + CommonShaders.cubicNoise3 + ` uniform lowp vec3 color; -uniform mediump float scale; -uniform mediump float stretch; -uniform mediump float threshold; -uniform mediump vec2 focus; -uniform mediump float power; -uniform mediump vec2 size; +uniform highp float scale; +uniform highp float stretch; +uniform highp float threshold; +uniform highp vec2 focus; +uniform highp float power; +uniform highp vec2 size; uniform highp vec3 anchor; uniform highp mat3 rotate; -varying mediump vec2 iUv; +varying highp vec2 iUv; #define ATTENUATION 1.5 void main() { highp vec2 at = vec2(iUv.x * stretch - 0.5, iUv.y - 0.5) * size * scale; - mediump float noise = cubicNoise(anchor + vec3(at, 0.0) * rotate); - mediump float strength = pow(max(0.0, 1.0 - ATTENUATION * length(iUv - focus)), power); + highp float noise = cubicNoise(anchor + vec3(at, 0.0) * rotate); + highp float strength = pow(max(0.0, 1.0 - ATTENUATION * length(iUv - focus)), power); if (noise > threshold * strength) discard; diff --git a/js/koi/fish/pattern/layer/layerStripes.js b/js/koi/fish/pattern/layer/layerStripes.js index 9513527c..c8ff3251 100644 --- a/js/koi/fish/pattern/layer/layerStripes.js +++ b/js/koi/fish/pattern/layer/layerStripes.js @@ -63,28 +63,28 @@ void main() { LayerStripes.prototype.SHADER_FRAGMENT = `#version 100 ` + CommonShaders.cubicNoise3 + ` uniform lowp vec3 color; -uniform mediump float scale; -uniform mediump float distortion; -uniform mediump float roughness; -uniform mediump float threshold; -uniform mediump float slant; -uniform mediump float suppression; -uniform mediump float focus; -uniform mediump float power; -uniform mediump vec2 size; +uniform highp float scale; +uniform highp float distortion; +uniform highp float roughness; +uniform highp float threshold; +uniform highp float slant; +uniform highp float suppression; +uniform highp float focus; +uniform highp float power; +uniform highp vec2 size; uniform highp vec3 anchor; uniform highp mat3 rotate; -varying mediump vec2 iUv; +varying highp vec2 iUv; #define ATTENUATION 2.0 void main() { highp vec2 at = (iUv - 0.5) * size * roughness; - mediump float dx = cubicNoise(anchor + vec3(at, 0.0) * rotate); - mediump float dy = 2.0 * abs(iUv.y - 0.5); - mediump float x = 2.0 * scale * iUv.x + dx * distortion / scale - dy * dy * slant; - mediump float strength = pow(max(0.0, 1.0 - ATTENUATION * abs(iUv.x - focus)), power); + highp float dx = cubicNoise(anchor + vec3(at, 0.0) * rotate); + highp float dy = 2.0 * abs(iUv.y - 0.5); + highp float x = 2.0 * scale * iUv.x + dx * distortion / scale - dy * dy * slant; + highp float strength = pow(max(0.0, 1.0 - ATTENUATION * abs(iUv.x - focus)), power); if (min(mod(x, 2.0), 2.0 - mod(x, 2.0)) + dy * dy * suppression > threshold * strength) discard; diff --git a/js/koi/fish/pattern/layer/layerWeb.js b/js/koi/fish/pattern/layer/layerWeb.js index 61a83d5c..a208669e 100644 --- a/js/koi/fish/pattern/layer/layerWeb.js +++ b/js/koi/fish/pattern/layer/layerWeb.js @@ -27,6 +27,7 @@ LayerWeb.prototype.SAMPLER_SCALE = new SamplerPlateau(1.5, 3, 6.5, .7); LayerWeb.prototype.SAMPLER_THICKNESS = new SamplerPlateau(.1, .15, .3, .7); LayerWeb.prototype.SAMPLER_THRESHOLD = new SamplerPlateau(.3, .5, .7, .6); +// language=glsl LayerWeb.prototype.SHADER_VERTEX = `#version 100 attribute vec2 position; attribute vec2 uv; @@ -40,21 +41,22 @@ void main() { } `; +// language=glsl LayerWeb.prototype.SHADER_FRAGMENT = `#version 100 ` + CommonShaders.cubicNoise3 + ` uniform lowp vec3 color; -uniform mediump float scale; -uniform mediump float thickness; -uniform mediump float threshold; -uniform mediump vec2 size; +uniform highp float scale; +uniform highp float thickness; +uniform highp float threshold; +uniform highp vec2 size; uniform highp vec3 anchor; uniform highp mat3 rotate; -varying mediump vec2 iUv; +varying highp vec2 iUv; void main() { highp vec2 at = (iUv - 0.5) * size * scale; - mediump float noise = cubicNoise(anchor + vec3(at, 0.0) * rotate); + highp float noise = cubicNoise(anchor + vec3(at, 0.0) * rotate); if (noise < threshold - thickness * 0.5 || noise > threshold + thickness * 0.5) discard; diff --git a/js/koi/gui/cards/buttons/cardLoadButton.js b/js/koi/gui/cards/buttons/cardLoadButton.js new file mode 100644 index 00000000..54f1c618 --- /dev/null +++ b/js/koi/gui/cards/buttons/cardLoadButton.js @@ -0,0 +1,42 @@ +/** + * A card load button + * @param {Function} onClick The function to execute on click + * @constructor + */ +const CardLoadButton = function(onClick) { + this.element = this.makeElement(onClick); +}; + +CardLoadButton.prototype.ID = "button-load-card"; +CardLoadButton.prototype.FILE = "svg/add.svg"; +CardLoadButton.prototype.WIDTH = 10; +CardLoadButton.prototype.HEIGHT = 10; + + +CardLoadButton.prototype.loadSVG = function() { + const request = new XMLHttpRequest(); + + request.onload = () => { + this.element.innerHTML = request.responseText; + }; + + request.open("GET", this.FILE, true); + request.send(); +} + + +/** + * Make the button element + * @param {Function} onClick The function to execute on click + * @returns {HTMLButtonElement} The button element + */ +CardLoadButton.prototype.makeElement = function(onClick) { + const element = document.createElement("button"); + + element.onclick = onClick; + element.id = this.ID; + + this.loadSVG(); + + return element; +}; \ No newline at end of file diff --git a/js/koi/gui/cards/buttons/cardMenuButton.js b/js/koi/gui/cards/buttons/cardMenuButton.js new file mode 100644 index 00000000..a0aac4e0 --- /dev/null +++ b/js/koi/gui/cards/buttons/cardMenuButton.js @@ -0,0 +1,42 @@ +/** + * A card menu button + * @param {Function} onClick The function to execute on click + * @constructor + */ +const CardMenuButton = function(onClick) { + this.element = this.makeElement(onClick); +}; + +CardMenuButton.prototype.ID = "button-home"; +CardMenuButton.prototype.FILE = "svg/settings.svg"; +CardMenuButton.prototype.WIDTH = 10; +CardMenuButton.prototype.HEIGHT = 10; + + +CardMenuButton.prototype.loadSVG = function() { + const request = new XMLHttpRequest(); + + request.onload = () => { + this.element.innerHTML = request.responseText; + }; + + request.open("GET", this.FILE, true); + request.send(); +} + + +/** + * Make the button element + * @param {Function} onClick The function to execute on click + * @returns {HTMLButtonElement} The button element + */ +CardMenuButton.prototype.makeElement = function(onClick) { + const element = document.createElement("button"); + + element.onclick = onClick; + element.id = this.ID; + + this.loadSVG(); + + return element; +}; \ No newline at end of file diff --git a/js/koi/gui/cards/card.js b/js/koi/gui/cards/card.js index 9446f2a6..5ffa3c71 100644 --- a/js/koi/gui/cards/card.js +++ b/js/koi/gui/cards/card.js @@ -36,8 +36,11 @@ Card.prototype.CLASS_INFO_BACKGROUND = "background"; Card.prototype.CLASS_INFO_LABEL = "label"; Card.prototype.CLASS_INFO_VALUE = "value"; Card.prototype.CLASS_JAPANESE = "japanese"; -Card.prototype.WIDTH = StyleUtils.getInt("--card-width"); -Card.prototype.HEIGHT = StyleUtils.getInt("--card-height"); +Card.prototype.SCALE = StyleUtils.getFloat("--card-scale"); +Card.prototype.WIDTH_DEFAULT = StyleUtils.getInt("--card-width-default"); +Card.prototype.HEIGHT_DEFAULT = StyleUtils.getInt("--card-height-default"); +Card.prototype.WIDTH = Card.prototype.WIDTH_DEFAULT * Card.prototype.SCALE; +Card.prototype.HEIGHT = Card.prototype.HEIGHT_DEFAULT * Card.prototype.SCALE; Card.prototype.RATIO = Card.prototype.WIDTH / Card.prototype.HEIGHT; Card.prototype.LANG_WEIGHT = "INFO_WEIGHT"; Card.prototype.LANG_LENGTH = "INFO_LENGTH"; diff --git a/js/koi/gui/cards/cardBook.js b/js/koi/gui/cards/cardBook.js index 1656b635..5fcd6696 100644 --- a/js/koi/gui/cards/cardBook.js +++ b/js/koi/gui/cards/cardBook.js @@ -7,7 +7,7 @@ * @param {Function} onUnlock A function to call when a new page is unlocked * @constructor */ -const CardBook = function(width, height, cards, audio, onUnlock) { +const CardBook = function(width, height, cards, audio, onUnlock, animate=true) { this.spine = this.createSpine(); this.element = this.createElement(this.spine); this.width = width; @@ -23,6 +23,7 @@ const CardBook = function(width, height, cards, audio, onUnlock) { this.onUnlock = onUnlock; this.unlocked = 0; this.invisibleTimeout = null; + this.animate = animate; this.populateSpine(); @@ -38,6 +39,7 @@ CardBook.prototype.CLASS_HIDDEN = "hidden"; CardBook.prototype.CLASS_INVISIBLE = "invisible"; CardBook.prototype.HIDE_TIME = StyleUtils.getFloat("--book-hide-time"); CardBook.prototype.PADDING_TOP = .05; +CardBook.prototype.PADDING_TOP_PORTRAIT = .2; CardBook.prototype.PADDING_PAGE = .02; CardBook.prototype.PADDING_CARD = .035; CardBook.prototype.HEIGHT = .65; @@ -61,8 +63,8 @@ CardBook.Flip.prototype.SPEED = .3; CardBook.Flip.prototype.update = function() { this.flipPrevious = this.flip; - if ((this.flip -= this.SPEED) < -1) - this.flip = -1; + // if ((this.flip -= this.SPEED) < -1) + this.flip = -1; return this.flip === this.flipPrevious; }; @@ -282,23 +284,23 @@ CardBook.prototype.update = function() { * @param {Number} time The amount of time since the last update */ CardBook.prototype.renderFlips = function(time) { - let index = this.flipDirection === -1 ? this.page + 1 : this.page; - - for (const flip of this.flips) { - const flipAmount = flip.flipPrevious + (flip.flip - flip.flipPrevious) * time; - - if (!flip.halfway && flipAmount < 0) { - this.pages[index].hide(); - this.pages[index - this.flipDirection].show(this.cards); - - flip.halfway = true; - } - - this.pages[index].setFlip(flipAmount); - this.pages[index - this.flipDirection].setFlip(-flipAmount); - - index -= 2 * this.flipDirection; - } + // let index = this.flipDirection === -1 ? this.page + 1 : this.page; + // + // for (const flip of this.flips) { + // const flipAmount = flip.flipPrevious + (flip.flip - flip.flipPrevious) * time; + // + // if (!flip.halfway && flipAmount < 0) { + // this.pages[index].hide(); + // this.pages[index - this.flipDirection].show(this.cards); + // + // flip.halfway = true; + // } + // + // this.pages[index].setFlip(flipAmount); + // this.pages[index - this.flipDirection].setFlip(-flipAmount); + // + // index -= 2 * this.flipDirection; + // } }; /** @@ -412,6 +414,8 @@ CardBook.prototype.removeFromBook = function(card) { * Fit the book and its contents to the view size */ CardBook.prototype.fit = function() { + let scale = 1; + const pageHeight = Math.round(this.height * this.HEIGHT * (1 - 2 * this.PADDING_PAGE)); const cardPadding = Math.round(pageHeight * this.PADDING_CARD); const cardHeight = Math.round((pageHeight - 3 * cardPadding) * .5); @@ -419,14 +423,22 @@ CardBook.prototype.fit = function() { const pageWidth = cardWidth * 2 + cardPadding * 3; const bookWidth = pageWidth * 2 + Math.round(this.height * this.HEIGHT * this.PADDING_PAGE * 2); - this.element.style.width = bookWidth + "px"; - this.element.style.height = this.height * this.HEIGHT + "px"; - this.element.style.left = (this.width - bookWidth) * .5 + "px"; - this.element.style.top = this.height * this.PADDING_TOP + "px"; - this.spine.style.height = pageHeight + "px"; + if (this.width < this.height) { + scale = this.width / bookWidth * .7; + + this.element.style.top = this.height * this.PADDING_TOP_PORTRAIT + "px"; + } else { + this.element.style.top = this.height * this.PADDING_TOP + "px"; + } + + this.element.style.width = bookWidth * scale + "px"; + this.element.style.height = this.height * this.HEIGHT * scale + "px"; + this.element.style.left = (this.width - bookWidth * scale) * .5 + "px"; + // this.element.style.top = this.height * this.PADDING_TOP + "px"; + this.spine.style.height = pageHeight * scale + "px"; for (const page of this.pages) - page.fit(cardWidth, cardHeight, cardPadding); + page.fit(cardWidth * scale, cardHeight * scale, cardPadding * scale); }; /** diff --git a/js/koi/gui/cards/cardPage.js b/js/koi/gui/cards/cardPage.js index 284b5034..f53240a2 100644 --- a/js/koi/gui/cards/cardPage.js +++ b/js/koi/gui/cards/cardPage.js @@ -9,7 +9,7 @@ const CardPage = function(direction, requirements) { this.cards = new Array(4).fill(null); this.element = this.createElement(direction); this.slots = this.createSlots(this.element); - this.overlay = this.createOverlay(this.element, direction); + // this.overlay = this.createOverlay(this.element, direction); this.rect = null; this.targets = null; this.direction = direction; @@ -101,19 +101,19 @@ CardPage.prototype.hide = function() { * @param {Number} flip The flip amount in the range [-1, 1] */ CardPage.prototype.setFlip = function(flip) { - const scale = Math.sin(flip * Math.PI * .5); - const skew = this.direction * this.PAGE_SKEW * (1 - Math.abs(flip)); + // const scale = Math.sin(flip * Math.PI * .5); + // const skew = this.direction * this.PAGE_SKEW * (1 - Math.abs(flip)); - this.element.style.transform = "scaleX(" + scale.toString() + ") skewY(" + skew.toString() + "deg)"; - this.overlay.style.opacity = ((1 - Math.abs(scale)) * this.SHADE_OPACITY).toString(); + // this.element.style.transform = "scaleX(" + scale.toString() + ") skewY(" + skew.toString() + "deg)"; + // this.overlay.style.opacity = ((1 - Math.abs(scale)) * this.SHADE_OPACITY).toString(); }; /** * Remove all flip effects */ CardPage.prototype.setNoFlip = function() { - this.element.style.removeProperty("transform"); - this.overlay.style.removeProperty("opacity"); + // this.element.style.removeProperty("transform"); + // this.overlay.style.removeProperty("opacity"); }; /** diff --git a/js/koi/gui/cards/cards.js b/js/koi/gui/cards/cards.js index 3378d78d..a67d4882 100644 --- a/js/koi/gui/cards/cards.js +++ b/js/koi/gui/cards/cards.js @@ -11,7 +11,14 @@ const Cards = function(element, codeViewer, audio) { this.audio = audio; this.dropTarget = this.createDropTarget(); this.buttonBook = new CardBookButton(this.toggleBook.bind(this)); + this.buttonLoadCard = new CardLoadButton(() => { + storage.loadImage(); + }); + this.buttonMenu = new CardMenuButton(() => { + menu.toggle(); + }); this.bookEnabled = false; + this.buttonsShown = false; this.book = new CardBook(element.clientWidth, element.clientHeight, this, audio, () => { if (this.koi) this.koi.onUnlock(); @@ -89,6 +96,19 @@ Cards.prototype.toggleBook = function() { } }; +Cards.prototype.hideButtons = function() { + this.element.removeChild(this.buttonMenu.element); + this.element.removeChild(this.buttonLoadCard.element); + this.buttonsShown = false; +} + +Cards.prototype.showButtons = function() { + this.element.appendChild(this.buttonMenu.element); + this.element.appendChild(this.buttonLoadCard.element); + this.buttonsShown = true; + +} + /** * Enable the book button */ @@ -418,6 +438,7 @@ Cards.prototype.remove = function(card, noChild = false) { Cards.prototype.hide = function() { if (this.bookVisible) { this.book.hide(); + this.hideButtons(); this.audio.effectBookInteract.play(); this.hideTimer = this.HIDE_TIME; @@ -431,7 +452,7 @@ Cards.prototype.hide = function() { Cards.prototype.show = function() { if (!this.bookVisible) { this.book.show(); - + this.showButtons(); this.bookVisible = true; this.hidden = false; } diff --git a/js/koi/gui/loader/loader.js b/js/koi/gui/loader/loader.js index 6e4178d4..20ae2216 100644 --- a/js/koi/gui/loader/loader.js +++ b/js/koi/gui/loader/loader.js @@ -19,9 +19,10 @@ const Loader = function( loadFullscreen) { this.element = element; this.icon = new LoaderIcon(); + this.loadInfo = new LoaderLoadInfo(); this.elementSlots = elementSlots; this.elementButtonSettings = elementButtonSettings; - this.elementDiscord = new LoaderDiscord(); + this.elementLinks = new LoaderLinks(); this.resumables = null; this.outstanding = 0; this.finished = 0; @@ -37,13 +38,16 @@ const Loader = function( this.slots = null; - element.appendChild(this.elementDiscord.element); + // element.appendChild(this.elementWebsite.element); + element.appendChild(this.elementLinks.element); if (loadFullscreen) element.appendChild(this.fullscreen.element); elementGraphics.appendChild(this.icon.element); + elementGraphics.appendChild(this.loadInfo.element); + const loop = () => { this.overlayCanvas.getContext("2d").clearRect( 0, @@ -144,16 +148,19 @@ Loader.prototype.complete = function() { if (this.loadFullscreen) this.fullscreen.setLoaded(); - const onNewGame = index => { - this.onNewGame(index); - this.onFinish(); - this.hide(); + const onNewGame = (index) => { + this.onNewGame(index, () => { + this.onFinish(); + this.hide(); + }); }; - const onContinue = index => { - this.onContinue(index); - this.onFinish(); - this.hide(); + const onContinue = (index) => { + this.onContinue(index, () => { + this.onFinish(); + this.hide(); + + }); }; this.slots = [ @@ -162,6 +169,9 @@ Loader.prototype.complete = function() { new LoaderSlot(2, "3", onNewGame, this.resumables[2] ? onContinue : null) ]; + // disable loading icon + this.loadInfo.hide(); + for (const slot of this.slots) this.elementSlots.appendChild(slot.element); @@ -188,6 +198,11 @@ Loader.prototype.complete = function() { } }; +Loader.prototype.finish = function() { + this.onFinish(); + this.hide(); +}; + /** * Check whether the loader has finished loading * @returns {Boolean} True if the loader has already finished diff --git a/js/koi/gui/loader/loaderAndroid.js b/js/koi/gui/loader/loaderAndroid.js new file mode 100644 index 00000000..fd191eda --- /dev/null +++ b/js/koi/gui/loader/loaderAndroid.js @@ -0,0 +1,65 @@ +/** + * A link to the website server + * @constructor + */ +const LoaderAndroid = function () { + this.element = this.createElement(); + + this.loadSVG(); +}; + +LoaderAndroid.prototype.ID = "loader-android"; +LoaderAndroid.prototype.CLASS = "loader-bar"; +LoaderAndroid.prototype.CLASS_INVISIBLE = "invisible"; +LoaderAndroid.prototype.FILE = "svg/Google_Play_Store_badge_EN_WHITE.svg"; +LoaderAndroid.prototype.FADE_IN_DELAY = 2.5; +LoaderAndroid.prototype.URL = "https://play.google.com/store/apps/details?id=com.koifarmgame&utm_source=KoiGame&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1"; +/** + * Load the SVG image + */ +LoaderAndroid.prototype.loadSVG = function () { + const request = new XMLHttpRequest(); + + request.onload = () => { + this.element.innerHTML = request.responseText; + + // setTimeout(() => { + // this.element.classList.remove(this.CLASS_INVISIBLE); + // }, 1000 * this.FADE_IN_DELAY); + + this.element.onclick = () => { + if (window["require"]) { + window["require"]("electron")["shell"]["openExternal"](this.URL); + } else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.openWebsiteHandler) { + window.webkit.messageHandlers.openWebsiteHandler.postMessage({discordURL: this.URL}); + } else { + window.open(this.URL, "_blank"); + } + }; + }; + + request.open("GET", this.FILE, true); + request.send(); +} + +LoaderAndroid.prototype.setInvisible = function () { + this.element.classList.add(this.CLASS_INVISIBLE); +} + +LoaderAndroid.prototype.setVisible = function () { + this.element.classList.remove(this.CLASS_INVISIBLE); +} + +/** + * Create the element + * @returns {HTMLDivElement} The element + */ +LoaderAndroid.prototype.createElement = function () { + const element = document.createElement("div"); + + element.id = this.ID; + // element.className = this.CLASS_INVISIBLE; + element.classList.add(this.CLASS); + + return element; +}; diff --git a/js/koi/gui/loader/loaderApple.js b/js/koi/gui/loader/loaderApple.js new file mode 100644 index 00000000..25117ce3 --- /dev/null +++ b/js/koi/gui/loader/loaderApple.js @@ -0,0 +1,66 @@ +/** + * A link to the website server + * @constructor + */ +const LoaderApple = function() { + this.element = this.createElement(); + + this.loadSVG(); +}; + +LoaderApple.prototype.ID = "loader-apple"; +LoaderApple.prototype.CLASS = "loader-bar"; +LoaderApple.prototype.CLASS_INVISIBLE = "invisible"; +LoaderApple.prototype.FILE = "svg/Download_on_the_App_Store_Badge_US-UK_RGB_blk_092917.svg"; +LoaderApple.prototype.FADE_IN_DELAY = 2.5; +LoaderApple.prototype.URL = "https://apps.apple.com/app/koi-farm/id1607489625"; + +/** + * Load the SVG image + */ +LoaderApple.prototype.loadSVG = function() { + const request = new XMLHttpRequest(); + + request.onload = () => { + this.element.innerHTML = request.responseText; + + // setTimeout(() => { + // this.element.classList.remove(this.CLASS_INVISIBLE); + // }, 1000 * this.FADE_IN_DELAY); + + this.element.onclick = () => { + if (window["require"]) { + window["require"]("electron")["shell"]["openExternal"](this.URL); + } else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.openWebsiteHandler) { + window.webkit.messageHandlers.openWebsiteHandler.postMessage({discordURL: this.URL}); + } else { + window.open(this.URL, "_blank"); + } + }; + }; + + request.open("GET", this.FILE, true); + request.send(); +} + +// LoaderApple.prototype.setInvisible = function () { +// this.element.classList.add(this.CLASS_INVISIBLE); +// } +// +// LoaderApple.prototype.setVisible = function () { +// this.element.classList.remove(this.CLASS_INVISIBLE); +// } + +/** + * Create the element + * @returns {HTMLDivElement} The element + */ +LoaderApple.prototype.createElement = function() { + const element = document.createElement("div"); + + element.id = this.ID; + // element.className = this.CLASS_INVISIBLE; + element.classList.add(this.CLASS); + + return element; +}; diff --git a/js/koi/gui/loader/loaderDiscord.js b/js/koi/gui/loader/loaderDiscord.js index b5e7864f..4b159e0d 100644 --- a/js/koi/gui/loader/loaderDiscord.js +++ b/js/koi/gui/loader/loaderDiscord.js @@ -9,8 +9,9 @@ const LoaderDiscord = function() { }; LoaderDiscord.prototype.ID = "loader-discord"; +LoaderDiscord.prototype.CLASS = "loader-icon"; LoaderDiscord.prototype.CLASS_INVISIBLE = "invisible"; -LoaderDiscord.prototype.FILE = "svg/discord.svg"; +LoaderDiscord.prototype.FILE = "svg/discord-mark-white.svg"; LoaderDiscord.prototype.FADE_IN_DELAY = 2.5; LoaderDiscord.prototype.URL = "https://discord.com/invite/bw3ZFe63Qg"; @@ -51,6 +52,7 @@ LoaderDiscord.prototype.createElement = function() { element.id = this.ID; element.className = this.CLASS_INVISIBLE; + element.classList.add(this.CLASS); return element; }; diff --git a/js/koi/gui/loader/loaderLinks.js b/js/koi/gui/loader/loaderLinks.js new file mode 100644 index 00000000..d0e446e5 --- /dev/null +++ b/js/koi/gui/loader/loaderLinks.js @@ -0,0 +1,70 @@ +/** + * A link to the website server + * @constructor + */ +const LoaderLinks = function() { + this.element = this.createElement(); + + this.elementApple = new LoaderApple(); + this.elementAndroid = new LoaderAndroid(); + + this.elementDiscord = new LoaderDiscord(); + this.elementWebsite = new LoaderWebsite(); + + if (PLATFORM_NAME !== "android" && PLATFORM_NAME !== "ios") { + this.element.appendChild(this.elementApple.element); + this.element.appendChild(this.elementAndroid.element); + } + + this.element.appendChild(this.elementWebsite.element); + this.element.appendChild(this.elementDiscord.element); + + // this.loadSVG(); +}; + +LoaderLinks.prototype.ID = "loader-links"; +LoaderLinks.prototype.CLASS_INVISIBLE = "invisible"; +// LoaderLinks.prototype.FILE = "svg/website.svg"; +LoaderLinks.prototype.FADE_IN_DELAY = 2.5; +// LoaderLinks.prototype.URL = "https://koifarmgame.com"; + +/** + * Load the SVG image + */ +LoaderLinks.prototype.loadSVG = function() { + const request = new XMLHttpRequest(); + + request.onload = () => { + this.element.innerHTML = request.responseText; + + setTimeout(() => { + this.element.classList.remove(this.CLASS_INVISIBLE); + }, 1000 * this.FADE_IN_DELAY); + + this.element.onclick = () => { + if (window["require"]) { + window["require"]("electron")["shell"]["openExternal"](this.URL); + } else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.openLinksHandler) { + window.webkit.messageHandlers.openLinksHandler.postMessage({discordURL: this.URL}); + } else { + window.open(this.URL, "_blank"); + } + }; + }; + + request.open("GET", this.FILE, true); + request.send(); +} + +/** + * Create the element + * @returns {HTMLDivElement} The element + */ +LoaderLinks.prototype.createElement = function() { + const element = document.createElement("div"); + + element.id = this.ID; + // element.className = this.CLASS_INVISIBLE; + + return element; +}; diff --git a/js/koi/gui/loader/loaderLoadInfo.js b/js/koi/gui/loader/loaderLoadInfo.js new file mode 100644 index 00000000..8bdb281e --- /dev/null +++ b/js/koi/gui/loader/loaderLoadInfo.js @@ -0,0 +1,63 @@ +/** + * The loader load info + * @constructor + */ +const LoaderLoadInfo = function() { + this.iconElement = document.createElement("div"); + this.textElement = document.createElement("div"); + this.element = this.createElement(); + + + this.loadSVG(); +}; + +LoaderLoadInfo.prototype.ID = "loader-loading"; +LoaderLoadInfo.prototype.TEXT_ID = "loader-loading-text"; +LoaderLoadInfo.prototype.ICON_ID = "loader-loading-icon"; +LoaderLoadInfo.prototype.TEXT = "Loading..."; +LoaderLoadInfo.prototype.CLASS_INVISIBLE = "invisible"; +LoaderLoadInfo.prototype.FILE = "svg/butterfly.svg"; +LoaderLoadInfo.prototype.FADE_IN_DELAY = .32; + + +/** + * Load the SVG image + */ +LoaderLoadInfo.prototype.loadSVG = function() { + const request = new XMLHttpRequest(); + + request.onload = () => { + this.iconElement.innerHTML = request.responseText; + + // setTimeout(() => { + // this.element.classList.remove(this.CLASS_INVISIBLE); + // }, 1000 * this.FADE_IN_DELAY); + }; + + request.open("GET", LoaderLoadInfo.prototype.FILE, true); + request.send(); +}; + +/** + * Create the element + * @returns {HTMLDivElement} The element + */ +LoaderLoadInfo.prototype.createElement = function() { + const element = document.createElement("div"); + + element.id = this.ID; + + this.iconElement.id = this.ICON_ID; + this.textElement.id = this.TEXT_ID; + + this.textElement.innerText = this.TEXT; + + element.appendChild(this.iconElement); + element.appendChild(this.textElement); + + return element; +}; + +LoaderLoadInfo.prototype.hide = function() { + this.element.classList.add(LoaderLoadInfo.prototype.CLASS_INVISIBLE); +} \ No newline at end of file diff --git a/js/koi/gui/loader/loaderWebsite.js b/js/koi/gui/loader/loaderWebsite.js new file mode 100644 index 00000000..cf1c5c69 --- /dev/null +++ b/js/koi/gui/loader/loaderWebsite.js @@ -0,0 +1,62 @@ +/** + * A link to the website server + * @constructor + */ +const LoaderWebsite = function() { + this.element = this.createElement(); + try { + LoaderWebsite.prototype.URL += "&referrer=" + encodeURIComponent(PLATFORM_NAME); + } catch (e) { + } + + this.loadSVG(); +}; + +LoaderWebsite.prototype.ID = "loader-website"; +LoaderWebsite.prototype.CLASS = "loader-icon"; +LoaderWebsite.prototype.CLASS_INVISIBLE = "invisible"; +LoaderWebsite.prototype.FILE = "svg/website.svg"; +LoaderWebsite.prototype.FADE_IN_DELAY = 2.5; +LoaderWebsite.prototype.URL = "https://koifarmgame.com?source=KoiGame"; + +/** + * Load the SVG image + */ +LoaderWebsite.prototype.loadSVG = function() { + const request = new XMLHttpRequest(); + + request.onload = () => { + this.element.innerHTML = request.responseText; + + setTimeout(() => { + this.element.classList.remove(this.CLASS_INVISIBLE); + }, 1000 * this.FADE_IN_DELAY); + + this.element.onclick = () => { + if (window["require"]) { + window["require"]("electron")["shell"]["openExternal"](this.URL); + } else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.openWebsiteHandler) { + window.webkit.messageHandlers.openWebsiteHandler.postMessage({discordURL: this.URL}); + } else { + window.open(this.URL, "_blank"); + } + }; + }; + + request.open("GET", this.FILE, true); + request.send(); +} + +/** + * Create the element + * @returns {HTMLDivElement} The element + */ +LoaderWebsite.prototype.createElement = function() { + const element = document.createElement("div"); + + element.id = this.ID; + element.className = this.CLASS_INVISIBLE; + element.classList.add(this.CLASS); + + return element; +}; diff --git a/js/koi/gui/menu/menu.js b/js/koi/gui/menu/menu.js index e5c5769f..dd77e2c6 100644 --- a/js/koi/gui/menu/menu.js +++ b/js/koi/gui/menu/menu.js @@ -260,6 +260,7 @@ Menu.prototype.createMSAAToggle = function() { element.type = "checkbox"; element.checked = true; + // TODO: do not use local storage if (window["localStorage"].getItem(this.KEY_MSAA)) element.checked = window["localStorage"].getItem(this.KEY_MSAA) === "true"; else diff --git a/js/koi/gui/overlay/overlay.js b/js/koi/gui/overlay/overlay.js index 33308ec3..21cdbd5e 100644 --- a/js/koi/gui/overlay/overlay.js +++ b/js/koi/gui/overlay/overlay.js @@ -10,12 +10,15 @@ const Overlay = function(element) { this.arrowElement = null; this.arrowParent = null; this.textElement = null; + this.skipElement = null; }; +Overlay.prototype.CLASS_HOME = "home-button"; Overlay.prototype.CLASS_POINTER = "pointer"; Overlay.prototype.CLASS_HIGHLIGHT = "overlay-highlight"; Overlay.prototype.CLASS_ARROW = "overlay-arrow"; Overlay.prototype.CLASS_TEXT = "text"; +Overlay.prototype.CLASS_SKIP = "skip-button"; Overlay.prototype.POINTER_RADIUS = StyleUtils.getInt("--overlay-pointer-radius") + StyleUtils.getInt("--overlay-pointer-border"); @@ -30,6 +33,14 @@ Overlay.prototype.render = function() { } }; +Overlay.prototype.createHomeElement = function() { + const element = document.createElement("div"); + + element.className = this.CLASS_HOME; + + return element; +} + /** * Create a pointer element to indicate where the player should do something * @returns {HTMLDivElement} The element @@ -82,9 +93,58 @@ Overlay.prototype.createHighlightElement = function() { element.className = this.CLASS_HIGHLIGHT; + const pathElement = document.createElement("svg"); + pathElement.setAttribute("viewBox", "0 0 24 24"); + pathElement.setAttribute("width", "24"); + pathElement.setAttribute("height", "24"); + // pathElement.setAttribute("fill", "none"); + pathElement.setAttribute("stroke", "currentColor"); + + const path = document.createElement("path"); + // path.setAttribute("stroke-linecap", "round"); + // path.setAttribute("stroke-linejoin", "round"); + // path.setAttribute("stroke-width", "2"); + path.setAttribute("d", "M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm-4.5 " + + "14c-.828 0-1.5-.672-1.5-1.5s.672-1.5 1.5-1.5 1.5.672 1.5 1.5-.672 1.5-1.5 1.5zm4.5 0c-.828 0-1.5-.672-1.5-1.5s.672-1.5 " + + "1.5-1.5 1.5.672 1.5 1.5-.672 1.5-1.5 1.5zm4.5 0c-.828 0-1.5-.672-1.5-1.5s.672-1.5 1.5-1.5 1.5.672 1.5 1.5-.672 1.5-1.5 1.5z"); + + pathElement.appendChild(path); + element.appendChild(pathElement); + + element.addEventListener("click", function() { + menu.toggle(); + }); + + element.addEventListener("touchstart", function() { + menu.toggle(); + }); + return element; }; +Overlay.prototype.createSkipElement = function() { + const element = document.createElement("button"); + + element.className = this.CLASS_SKIP; + + element.appendChild(document.createTextNode("Skip")); + + return element; +} + +Overlay.prototype.createHome = function() { + this.homeElement = this.createHomeElement(); + this.element.appendChild(this.homeElement); +} + +Overlay.prototype.deleteHome = function() { + if (this.homeElement) { + this.element.removeChild(this.homeElement); + + this.homeElement = null; + } +} + /** * Create a pointer * @returns {Vector2} The pointer position which can be changed @@ -174,4 +234,35 @@ Overlay.prototype.removeText = function() { this.element.removeChild(this.textElement); this.textElement = null; } -}; \ No newline at end of file +}; + +Overlay.prototype.createSkip = function(callback) { + this.deleteSkip(); + + this.skipElement = this.createSkipElement(); + + this.skipElement.onclick = () => { + callback(); + } + + // this.skipElement.addEventListener("click", () => callback); + // this.skipElement.addEventListener("touchstart", () => callback); + + this.element.appendChild(this.skipElement); +} + +Overlay.prototype.deleteSkip = function() { + if (this.skipElement) { + this.element.removeChild(this.skipElement); + + this.skipElement = null; + } +} + +Overlay.prototype.clear = function() { + // this.deleteHome(); + this.deletePointer(); + this.deleteArrow(); + this.removeText(); + this.deleteSkip(); +} \ No newline at end of file diff --git a/js/koi/koi.js b/js/koi/koi.js index 98243ad6..2cf34d7e 100644 --- a/js/koi/koi.js +++ b/js/koi/koi.js @@ -439,6 +439,7 @@ Koi.prototype.update = function() { if (this.tutorial && this.tutorial.update(this)) { if (this.tutorial instanceof TutorialBreeding) { this.spawner.setBehavior(new SpawnerBehaviorDefault()); + // TODO: do something with this storage this.tutorial = new TutorialCards(this.storage, this.gui.overlay); } else diff --git a/js/koi/tutorial/tutorialBreeding.js b/js/koi/tutorial/tutorialBreeding.js index e7de458b..c178a640 100644 --- a/js/koi/tutorial/tutorialBreeding.js +++ b/js/koi/tutorial/tutorialBreeding.js @@ -11,8 +11,12 @@ const TutorialBreeding = function(storage, overlay) { this.targetedFish = null; this.bred = false; this.mutated = false; + this.skip = false; overlay.setText(language.get(this.LANG_MOVE_FISH)); + overlay.createSkip(() => { + this.skip = true; + }); }; TutorialBreeding.prototype = Object.create(Tutorial.prototype); @@ -110,6 +114,12 @@ TutorialBreeding.prototype.pointToSmallPond = function(koi) { * @returns {Boolean} True if the tutorial has finished */ TutorialBreeding.prototype.update = function(koi) { + if (this.skip) { + this.overlay.clear(); + + return true; + } + switch (this.phase) { case this.PHASE_MOVE_FISH: if (this.targetedFish === null) { @@ -117,7 +127,7 @@ TutorialBreeding.prototype.update = function(koi) { this.pointer = this.overlay.createPointer(); } else { - if (koi.mover.move) { + if (koi.mover.move ) { this.overlay.deletePointer(); this.pointer = null; @@ -143,7 +153,7 @@ TutorialBreeding.prototype.update = function(koi) { break; case this.PHASE_DROP_FISH: if (!koi.mover.move) { - if (koi.constellation.small.fish.length === 0) { + if (koi.constellation.small.fish.length === 0 ) { this.overlay.setText(language.get(this.LANG_TO_POND_1)); this.pointToSmallPond(koi); @@ -157,7 +167,7 @@ TutorialBreeding.prototype.update = function(koi) { break; case this.PHASE_TO_POND_1: - if (koi.constellation.small.fish.length === 1) { + if (koi.constellation.small.fish.length === 1 ) { this.overlay.setText(language.get(this.LANG_TO_POND_2)); this.overlay.deleteArrow(); diff --git a/js/koi/tutorial/tutorialCards.js b/js/koi/tutorial/tutorialCards.js index 9dbb3756..410baa66 100644 --- a/js/koi/tutorial/tutorialCards.js +++ b/js/koi/tutorial/tutorialCards.js @@ -5,6 +5,7 @@ * @constructor */ const TutorialCards = function(storage, overlay) { + // TODO: do somthing with this storage Tutorial.call(this, storage, overlay); this.mutations = 0; @@ -13,6 +14,11 @@ const TutorialCards = function(storage, overlay) { this.unlocked = false; this.stored = false; this.highlight1 = this.highlight2 = this.highlight3 = this.highlight4 = null; + this.skip = false; + + overlay.createSkip(() => { + this.skip = true; + }); }; TutorialCards.prototype = Object.create(Tutorial.prototype); @@ -37,6 +43,7 @@ TutorialCards.prototype.start = function() { this.phase = this.PHASE_CREATE_CARD; this.forceMutation = false; this.handEnabled = true; + }; /** @@ -119,17 +126,24 @@ TutorialCards.prototype.markFinished = function(koi) { * @returns {Boolean} True if the tutorial has finished */ TutorialCards.prototype.update = function(koi) { + if (this.skip) { + this.overlay.clear(); + + koi.gui.cards.enableBookButton(koi.audio); + + return true; + } switch (this.phase) { case this.PHASE_START: - if (this.mutations < this.MUTATIONS_REQUIRED) - this.phase = this.PHASE_WAITING; - else if (this.mutations === this.MUTATIONS_REQUIRED) + // if (this.mutations < this.MUTATIONS_REQUIRED) + // this.phase = this.PHASE_WAITING; + // else if (this.mutations === this.MUTATIONS_REQUIRED) this.start(); - else { - koi.gui.cards.enableBookButton(koi.audio); - - return true; - } + // else { + // koi.gui.cards.enableBookButton(koi.audio); + // + // return true; + // } break; case this.PHASE_CREATE_CARD: @@ -206,7 +220,7 @@ TutorialCards.prototype.update = function(koi) { if (!koi.gui.cards.bookVisible || this.unlocked) { this.markFinished(koi); - this.overlay.removeText(); + this.overlay.clear(); return true; } diff --git a/js/main.js b/js/main.js index 5a4de0a3..f913629f 100644 --- a/js/main.js +++ b/js/main.js @@ -1,4 +1,7 @@ const searchParams = new URLSearchParams(window.location.search); +// const preferences = new StoragePreferencesCapacitor(); + +// TODO: do not use localstorage const glParameters = { alpha: false, antialias: window["localStorage"].getItem(Menu.prototype.KEY_MSAA) === null || window["localStorage"].getItem(Menu.prototype.KEY_MSAA) === "true", @@ -16,7 +19,16 @@ let chosenSlot = -1; * Check if game is running within WKWebView on iOS. */ -const RUNNING_ON_WEBVIEW_IOS = (window.webkit && window.webkit.messageHandlers) ? true : false; +const RUNNING_ON_WEBVIEW_IOS = navigator.standalone || window["webkit"] && window["webkit"].messageHandlers; + +const RUNNING_CAPACITOR = typeof Capacitor !== "undefined" && Capacitor.platform !== "web"; + +const PLATFORM_NAME = RUNNING_CAPACITOR ? Capacitor.getPlatform() : "web"; + +const RUNNING_MOBILE = RUNNING_CAPACITOR && (Capacitor.isNative || Capacitor.platform === "web"); + +const preferences = RUNNING_CAPACITOR ? new StoragePreferencesCapacitor() : new StorageLocal(); + /** * Reload the game into the menu @@ -126,6 +138,93 @@ const makeLanguage = locale => { } }; +function removeStatusBar() { + if (!RUNNING_CAPACITOR) + return; + + if (Capacitor.isPluginAvailable('StatusBar')) { + Capacitor.Plugins.StatusBar.hide(); + } else { + console.error("StatusBar plugin not available"); + } +} + +function setFullScreen() { + if (!RUNNING_CAPACITOR) + return; + + + if (PLATFORM_NAME === "android") { + try { + AndroidFullScreen.immersiveMode(() => { + console.log("System UI visibility set"); + }, () => { + console.error("Failed to set system UI visibility"); + }, + AndroidFullScreen.CUTOUT_MODE_NEVER); + } catch { + console.warn("AndroidFullScreen plugin not available"); + + removeStatusBar(); + } + + } +} + +const keepAwake = async () => { + if (!RUNNING_CAPACITOR || !Capacitor.isPluginAvailable('KeepAwake')) + return; + + await Capacitor.Plugins.KeepAwake.keepAwake(); +}; + +function setupPlatform (menu, save) { + if (!RUNNING_CAPACITOR) + return; + + if (PLATFORM_NAME === "android") { + if (Capacitor.isPluginAvailable('App')) { + Capacitor.Plugins.App.addListener('appStateChange', (state) => { + // Check isActive for app state + }); + + Capacitor.Plugins.App.addListener('appUrlOpen', (data) => { + + }); + + Capacitor.Plugins.App.addListener("backButton", () => { + if (chosenSlot !== -1) { + save(); + menu.toggle(); + } + }); + } else { + console.error("Capacitor 'App' plugin not available"); + } + + keepAwake().then( + () => { + console.log("Keep awake enabled"); + } + ); + } +}; + +function padNotch() { + if (!RUNNING_MOBILE) + return; + + const elements = [ + document.getElementById("menu"), + document.getElementById("loader"), + document.getElementById("gui"), + // document.getElementById("drop"), + ] +} + +setFullScreen(); + +// TODO: do not use localstorage const paramLang = window["localStorage"].getItem(Menu.prototype.KEY_LANGUAGE) || searchParams.get("lang"); const language = paramLang ? makeLanguage(paramLang) : makeLanguage(navigator.language.substring(0, 2)); const loader = new Loader( @@ -134,13 +233,17 @@ const loader = new Loader( document.getElementById("loader-slots"), document.getElementById("loader-button-settings"), document.getElementById("wrapper"), - !RUNNING_ON_WEBVIEW_IOS, - !RUNNING_ON_WEBVIEW_IOS); + !RUNNING_MOBILE, + !RUNNING_MOBILE); let imperial = false; +let menu = null; +let storage = null; + if (gl && gl.getExtension("OES_element_index_uint") && (gl.vao = gl.getExtension("OES_vertex_array_object"))) { + const audioEngine = new AudioEngine(new Random()); const audio = new AudioBank(audioEngine); @@ -154,14 +257,14 @@ if (gl && let session = new Session(); let slot = null; const slotNames = ["session", "session2", "session3"]; - const storage = window["require"] ? new StorageFile() : new StorageLocal(); + storage = RUNNING_CAPACITOR ? new StorageFileCapacitor() : new StorageLocal(); const wrapper = document.getElementById("wrapper"); const gui = new GUI( document.getElementById("gui"), new CodeViewer(document.getElementById("code"), storage), audio); const systems = new Systems(gl, new Random(2893), wrapper.clientWidth, wrapper.clientHeight); - const menu = new Menu( + menu = new Menu( document.getElementById("menu"), loader.fullscreen, chosenLocale, @@ -176,6 +279,18 @@ if (gl && let control = false; let shift = false; + + // testFileIO(storage); + + console.log("Koi Farm"); + console.log( + ` + Environment = ${PLATFORM_NAME} + Storage = ${window["require"] ? "file" : "local"} + Language = ${chosenLocale} + ` + ); + new Drop(gui, systems, document.getElementById("drop"), canvas); loader.setMenu(menu); @@ -201,14 +316,22 @@ if (gl && * Save the game state to local storage */ const save = () => { - storage.setBuffer(slot, session.serialize(koi, gui)); + const promise = storage.setBuffer(slot, session.serialize(koi, gui)); + + promise.then(() => { + console.log("Game saved"); + }).catch(() => { + console.error("Failed to save game"); + }); }; + setupPlatform(menu,() => save()); + /** * A function that creates a new game session * @param {number} index Create a new game at a given slot index */ - const newSession = index => { + const newSession = (index, onFinish) => { chosenSlot = index; slot = slotNames[index]; session = new Session(); @@ -219,34 +342,61 @@ if (gl && koi.free(); koi = session.makeKoi(storage, systems, audio, gui, save, new TutorialBreeding(storage, gui.overlay)); + + onFinish(); + // loader.finish(); + }; /** * Continue an existing game * @param {number} index Create a new game at a given slot index */ - const continueGame = index => { + const continueGame = (index, onFinish) => { chosenSlot = index; slot = slotNames[index]; gui.cards.enableBookButton(audio); try { - session.deserialize(storage.getBuffer(slot)); + const p = storage.getBuffer(slot); + + p.then(buffer => { + session.deserialize(buffer); + + koi = session.makeKoi(storage, systems, audio, gui, save); - koi = session.makeKoi(storage, systems, audio, gui, save); + onFinish(); + }).catch(() => { + newSession(index, onFinish); + }); + + // session.deserialize(); + // + // koi = session.makeKoi(storage, systems, audio, gui, save); } catch (error) { - newSession(index); + newSession(index, onFinish); console.warn(error); } }; - loader.setResumables([ - storage.getBuffer(slotNames[0]) !== null, - storage.getBuffer(slotNames[1]) !== null, - storage.getBuffer(slotNames[2]) !== null - ]); + const resumablePromisses = [ + storage.getBuffer(slotNames[0]), + storage.getBuffer(slotNames[1]), + storage.getBuffer(slotNames[2]) + ]; + + Promise.all(resumablePromisses).then((values) => { + loader.setResumables([ + values[0] !== null, + values[1] !== null, + values[2] !== null]); + }).catch( + (error) => { + console.error(error); + } + ); // Trigger the animation frame loop lastTime = performance.now(); diff --git a/js/render/blit.js b/js/render/blit.js index f8fc364d..61a10d9a 100644 --- a/js/render/blit.js +++ b/js/render/blit.js @@ -34,7 +34,7 @@ void main() { Blit.prototype.SHADER_FRAGMENT = `#version 100 uniform sampler2D source; -varying mediump vec2 iUv; +varying highp vec2 iUv; void main() { gl_FragColor = texture2D(source, iUv); diff --git a/js/render/blur.js b/js/render/blur.js index 45e3a8da..5782c4e4 100644 --- a/js/render/blur.js +++ b/js/render/blur.js @@ -58,8 +58,8 @@ void main() { Blur.prototype.SHADER_FRAGMENT = `#version 100 uniform sampler2D source; -uniform mediump vec2 targetSize; -uniform mediump vec2 direction; +uniform highp vec2 targetSize; +uniform highp vec2 direction; lowp vec4 get(int delta) { return texture2D(source, (gl_FragCoord.xy + direction * float(delta)) / targetSize); diff --git a/js/render/bodies.js b/js/render/bodies.js index da78d51a..db419efa 100644 --- a/js/render/bodies.js +++ b/js/render/bodies.js @@ -57,9 +57,9 @@ void main() { Bodies.prototype.SHADER_FRAGMENT = `#version 100 uniform sampler2D atlas; -uniform mediump vec2 shadow; +uniform highp vec2 shadow; -varying mediump vec2 iUv; +varying highp vec2 iUv; void main() { gl_FragColor = texture2D(atlas, iUv); @@ -72,7 +72,7 @@ uniform vec2 scale; attribute vec2 position; attribute vec2 uv; -varying mediump vec2 iUv; +varying highp vec2 iUv; void main() { iUv = uv; @@ -84,7 +84,7 @@ void main() { Bodies.prototype.SHADER_SHADOWS_FRAGMENT = `#version 100 uniform sampler2D atlas; -varying mediump vec2 iUv; +varying highp vec2 iUv; #define TRANSPARENCY 0.6 @@ -128,11 +128,12 @@ Bodies.prototype.render = function( this.buffer.render(); } else { - this.gl.blendFuncSeparate( - this.gl.SRC_ALPHA, - this.gl.ONE_MINUS_SRC_ALPHA, - this.gl.ONE_MINUS_DST_ALPHA, - this.gl.ONE); + // Removing this seems to fix flickering of shadows on older android phones + // this.gl.blendFuncSeparate( + // this.gl.SRC_ALPHA, + // this.gl.ONE_MINUS_SRC_ALPHA, + // this.gl.ONE_MINUS_DST_ALPHA, + // this.gl.ONE); this.program.use(); diff --git a/js/render/commonShaders.js b/js/render/commonShaders.js index 0dddc483..ddfa9ab0 100644 --- a/js/render/commonShaders.js +++ b/js/render/commonShaders.js @@ -7,19 +7,19 @@ const CommonShaders = {}; CommonShaders.random = ` uniform sampler2D noise; -mediump float random2(mediump vec2 x) { +highp float random2(highp vec2 x) { return texture2D(noise, x * 0.011).r; } -mediump float random3(mediump vec3 x) { +highp float random3(highp vec3 x) { return texture2D(noise, x.xy * 0.011 - x.z * 0.017).r; } `; // Cubic interpolation CommonShaders.cubicInterpolation = ` -mediump float interpolate(mediump float a, mediump float b, mediump float c, mediump float d, mediump float x) { - mediump float p = (d - c) - (a - b); +highp float interpolate(highp float a, highp float b, highp float c, highp float d, highp float x) { + highp float p = (d - c) - (a - b); return x * (x * (x * p + ((a - b) - p)) + (c - a)) + b; } @@ -27,7 +27,7 @@ mediump float interpolate(mediump float a, mediump float b, mediump float c, med // 2D cubic noise CommonShaders.cubicNoise2 = CommonShaders.random + CommonShaders.cubicInterpolation + ` -mediump float sampleX(highp vec2 at) { +highp float sampleX(highp vec2 at) { highp float floored = floor(at.x); return interpolate( @@ -38,7 +38,7 @@ mediump float sampleX(highp vec2 at) { at.x - floored) * 0.5 + 0.25; } -mediump float cubicNoise(highp vec2 at) { +highp float cubicNoise(highp vec2 at) { highp float floored = floor(at.y); return interpolate( @@ -52,7 +52,7 @@ mediump float cubicNoise(highp vec2 at) { // 3D cubic noise CommonShaders.cubicNoise3 = CommonShaders.random + CommonShaders.cubicInterpolation + ` -mediump float sampleX(highp vec3 at) { +highp float sampleX(highp vec3 at) { highp float floored = floor(at.x); return interpolate( @@ -63,7 +63,7 @@ mediump float sampleX(highp vec3 at) { at.x - floored) * 0.5 + 0.25; } -mediump float sampleY(highp vec3 at) { +highp float sampleY(highp vec3 at) { highp float floored = floor(at.y); return interpolate( @@ -74,7 +74,7 @@ mediump float sampleY(highp vec3 at) { at.y - floored); } -mediump float cubicNoise(highp vec3 at) { +highp float cubicNoise(highp vec3 at) { highp float floored = floor(at.z); return interpolate( diff --git a/js/render/distanceField.js b/js/render/distanceField.js index 81148c40..ab7234f1 100644 --- a/js/render/distanceField.js +++ b/js/render/distanceField.js @@ -31,15 +31,15 @@ void main() { DistanceField.prototype.SHADER_FRAGMENT = `#version 100 uniform sampler2D source; -uniform mediump vec2 size; -uniform mediump float range; +uniform highp vec2 size; +uniform highp float range; -mediump float get(int dx, int dy) { +highp float get(int dx, int dy) { return texture2D(source, (gl_FragCoord.xy + vec2(float(dx), float(dy))) / size).a; } void main() { - mediump float nearest = min( + highp float nearest = min( min( min( get(-1, -1), diff --git a/js/render/drops.js b/js/render/drops.js index d674a6e2..d05b544c 100644 --- a/js/render/drops.js +++ b/js/render/drops.js @@ -30,7 +30,7 @@ attribute float threshold; varying float iAlpha; void main() { - mediump float age = mod(window - threshold, 1.0) / windowWidth; + highp float age = mod(window - threshold, 1.0) / windowWidth; iAlpha = transparency * alpha * age; diff --git a/js/render/fishBackground.js b/js/render/fishBackground.js index 055bc787..66e0301f 100644 --- a/js/render/fishBackground.js +++ b/js/render/fishBackground.js @@ -49,12 +49,12 @@ void main() { FishBackground.prototype.SHADER_FRAGMENT = `#version 100 uniform lowp vec3 colorInner; uniform lowp vec3 colorOuter; -uniform mediump float gradientPower; +uniform highp float gradientPower; -varying mediump vec2 iPosition; +varying highp vec2 iPosition; void main() { - mediump float dist = pow(min(1.0, length(iPosition) / 0.707106), gradientPower); + highp float dist = pow(min(1.0, length(iPosition) / 0.707106), gradientPower); gl_FragColor = vec4(mix(colorInner, colorOuter, dist), 1.0); } diff --git a/js/render/ponds.js b/js/render/ponds.js index b2e35e2c..443b3c2b 100644 --- a/js/render/ponds.js +++ b/js/render/ponds.js @@ -105,7 +105,7 @@ varying lowp vec2 iUv; #define WAVE_BASE 0.3 #define WATER_HEIGHT 3.7 -lowp float get(mediump vec2 delta) { +lowp float get(highp vec2 delta) { lowp vec2 uv = iUv + delta / waterSize; lowp vec2 sample = texture2D(water, uv).gr; diff --git a/js/render/preview.js b/js/render/preview.js index 22815dbe..487fb568 100644 --- a/js/render/preview.js +++ b/js/render/preview.js @@ -11,8 +11,9 @@ const Preview = function(gl, fishBackground) { }; Preview.prototype = Object.create(ImageMaker.prototype); -Preview.prototype.PREVIEW_WIDTH = StyleUtils.getInt("--card-preview-width"); -Preview.prototype.PREVIEW_HEIGHT = StyleUtils.getInt("--card-preview-height"); +Preview.prototype.PREVIEW_SCALE = StyleUtils.getFloat("--card-scale"); +Preview.prototype.PREVIEW_WIDTH = StyleUtils.getInt("--card-preview-width-default") * Preview.prototype.PREVIEW_SCALE; +Preview.prototype.PREVIEW_HEIGHT = StyleUtils.getInt("--card-preview-height-default") * Preview.prototype.PREVIEW_SCALE; Preview.prototype.PREVIEW_COLUMNS = StyleUtils.getInt("--card-preview-columns"); Preview.prototype.PREVIEW_ROWS = StyleUtils.getInt("--card-preview-rows"); Preview.prototype.SCALE = 150; @@ -33,6 +34,12 @@ Preview.prototype.render = function(body, atlas, bodies) { this.target.target(); this.gl.enable(this.gl.SCISSOR_TEST); + this.gl.blendFuncSeparate( + this.gl.SRC_ALPHA, + this.gl.ONE_MINUS_SRC_ALPHA, + this.gl.ONE_MINUS_DST_ALPHA, + this.gl.ONE); + for (let row = 0; row < this.PREVIEW_ROWS; ++row) for (let column = 0; column < this.PREVIEW_COLUMNS; ++column) { const left = column * this.PREVIEW_WIDTH; const top = (this.PREVIEW_ROWS - row) * this.PREVIEW_HEIGHT; @@ -60,6 +67,9 @@ Preview.prototype.render = function(body, atlas, bodies) { bodies.render(atlas, widthMeters, -heightMeters, false); } + this.gl.blendFunc( + this.gl.SRC_ALPHA, + this.gl.ONE_MINUS_SRC_ALPHA); this.gl.disable(this.gl.SCISSOR_TEST); return this.toCanvas(); diff --git a/js/render/sand.js b/js/render/sand.js index 08a2ea73..e82f74dd 100644 --- a/js/render/sand.js +++ b/js/render/sand.js @@ -51,11 +51,11 @@ void main() { Sand.prototype.SHADER_FRAGMENT = `#version 100 ` + CommonShaders.cubicNoise2 + ` -uniform mediump float scale; +uniform highp float scale; uniform lowp vec3 colorDeep; uniform lowp vec3 colorShallow; -varying mediump vec2 iDepth; +varying highp vec2 iDepth; void main() { lowp float noise = pow(random2(gl_FragCoord.xy), 9.0); diff --git a/js/render/shadows.js b/js/render/shadows.js index 077a28a2..14a7c6b7 100644 --- a/js/render/shadows.js +++ b/js/render/shadows.js @@ -49,15 +49,15 @@ void main() { Shadows.prototype.FRAGMENT_SHADER = `#version 100 uniform sampler2D source; -uniform mediump float meter; -uniform mediump float shadowDepth; +uniform highp float meter; +uniform highp float shadowDepth; -varying mediump vec2 iDepth; -varying mediump vec2 iUv; +varying highp vec2 iDepth; +varying highp vec2 iUv; void main() { - mediump float depthFactor = iDepth.y * (0.5 - 0.5 * cos(3.141592 * sqrt(iDepth.x))); - mediump float depth = depthFactor * meter * shadowDepth; + highp float depthFactor = iDepth.y * (0.5 - 0.5 * cos(3.141592 * sqrt(iDepth.x))); + highp float depth = depthFactor * meter * shadowDepth; gl_FragColor = texture2D(source, iUv + vec2(depth * 0.5, depth)); } diff --git a/js/render/still.js b/js/render/still.js index 96884715..4cb13262 100644 --- a/js/render/still.js +++ b/js/render/still.js @@ -31,6 +31,17 @@ Still.prototype.render = function(body, atlas, bodies) { this.fishBackground.render(widthMeters, heightMeters); + // this.gl.blendFunc( + // this.gl.SRC_ALPHA, + // this.gl.ONE_MINUS_SRC_ALPHA); + + this.gl.blendFuncSeparate( + this.gl.SRC_ALPHA, + this.gl.ONE_MINUS_SRC_ALPHA, + this.gl.ONE_MINUS_DST_ALPHA, + this.gl.ONE); + + body.renderLoop( (this.RADIUS + Math.cos(this.ANGLE) * this.RADIUS) * this.UPSCALE / this.SCALE, (this.RADIUS + Math.sin(this.ANGLE) * this.RADIUS) * this.UPSCALE / this.SCALE, diff --git a/js/render/waves.js b/js/render/waves.js index c5c7a469..70a55621 100644 --- a/js/render/waves.js +++ b/js/render/waves.js @@ -44,20 +44,20 @@ void main() { Waves.prototype.SHADER_FRAGMENT = `#version 100 uniform sampler2D source; -uniform mediump vec2 size; -uniform mediump float damping; +uniform highp vec2 size; +uniform highp float damping; -varying mediump vec2 iUv; +varying highp vec2 iUv; void main() { - mediump vec2 step = 1.0 / size; - mediump vec3 state = texture2D(source, iUv).rgb; - mediump float hLeft = texture2D(source, vec2(iUv.x - step.x, iUv.y)).r; - mediump float hRight = texture2D(source, vec2(iUv.x + step.x, iUv.y)).r; - mediump float hUp = texture2D(source, vec2(iUv.x, iUv.y - step.y)).r; - mediump float hDown = texture2D(source, vec2(iUv.x, iUv.y + step.y)).r; - mediump float momentum = (state.g + state.b) * 2.0 - 1.0; - mediump float newHeight = (hLeft + hUp + hRight + hDown) - 2.0; + highp vec2 step = 1.0 / size; + highp vec3 state = texture2D(source, iUv).rgb; + highp float hLeft = texture2D(source, vec2(iUv.x - step.x, iUv.y)).r; + highp float hRight = texture2D(source, vec2(iUv.x + step.x, iUv.y)).r; + highp float hUp = texture2D(source, vec2(iUv.x, iUv.y - step.y)).r; + highp float hDown = texture2D(source, vec2(iUv.x, iUv.y + step.y)).r; + highp float momentum = (state.g + state.b) * 2.0 - 1.0; + highp float newHeight = (hLeft + hUp + hRight + hDown) - 2.0; gl_FragColor = vec4( ((newHeight - momentum) * damping) * 0.5 + 0.5, diff --git a/js/render/wind.js b/js/render/wind.js index 64c03faf..f5522c93 100644 --- a/js/render/wind.js +++ b/js/render/wind.js @@ -31,7 +31,7 @@ Wind.prototype.DAMPING = .98; Wind.prototype.SHADER_VERTEX = `#version 100 attribute vec2 position; -varying mediump vec2 iUv; +varying highp vec2 iUv; void main() { iUv = position * 0.5 + 0.5; @@ -43,17 +43,17 @@ void main() { Wind.prototype.SHADER_FRAGMENT = `#version 100 uniform sampler2D source; uniform sampler2D springs; -uniform mediump float damping; +uniform highp float damping; -varying mediump vec2 iUv; +varying highp vec2 iUv; void main() { lowp vec3 previous = texture2D(source, iUv).rgb; - mediump float previousState = previous.r * 2.0 - 1.0; - mediump float previousLeft = previous.g; - mediump float previousRight = previous.b; - mediump float motion = previousRight - previousLeft; - mediump float state = previousState + motion * 0.4; + highp float previousState = previous.r * 2.0 - 1.0; + highp float previousLeft = previous.g; + highp float previousRight = previous.b; + highp float motion = previousRight - previousLeft; + highp float state = previousState + motion * 0.4; motion = (motion - state * texture2D(springs, iUv).r) * damping; diff --git a/js/storage/storageCapacitor.js b/js/storage/storageCapacitor.js new file mode 100644 index 00000000..49016735 --- /dev/null +++ b/js/storage/storageCapacitor.js @@ -0,0 +1,103 @@ +/** + * A storage system using files + * @constructor + */ +const StorageCapacitor = function() { + StorageSystem.call(this); +}; + +StorageCapacitor.prototype = Object.create(StorageSystem.prototype); +StorageCapacitor.prototype.EXTENSION = ".sav"; +StorageCapacitor.prototype.DIRECTORY = "save/"; + +if (window["require"]) { + const electron = window["require"]("electron"); + const url = window["require"]("url"); + const fs = window["require"]("fs"); + const os = window["require"]("os"); + let directory = StorageCapacitor.prototype.DIRECTORY; + + if (os["platform"]() === "darwin") + directory = os["homedir"]() + "/Library/Application Support/koifarm/" + directory; + + const makeDirectory = () => { + if (!fs["existsSync"](directory)) + fs["mkdirSync"](directory); + }; + + const fileExists = name => { + return fs["existsSync"](name); + }; + + const pngFilter = { + "name": "PNG Image", + "extensions": ["png"] + }; + + StorageCapacitor.prototype.set = function (key, value) { + makeDirectory(); + + fs["writeFileSync"](url["pathToFileURL"](directory + key + this.EXTENSION), value); + }; + + StorageCapacitor.prototype.setBuffer = function(key, value) { + makeDirectory(); + + fs["writeFileSync"](url["pathToFileURL"](directory + key + this.EXTENSION), value.toByteArray()); + }; + + StorageCapacitor.prototype.get = function(key) { + const file = directory + key + this.EXTENSION; + let contents = null; + + try { + if (fileExists(file)) + contents = fs["readFileSync"](file, "utf8"); + } + catch (error) { + + } + + return contents; + }; + + StorageCapacitor.prototype.getBuffer = function(key) { + const file = directory + key + this.EXTENSION; + let contents = null; + + try { + if (fileExists(file)) + contents = fs["readFileSync"](file); + } + catch (error) { + + } + + return contents ? new BinBuffer(contents) : null; + }; + + StorageCapacitor.prototype.remove = function(key) { + const file = directory + key + this.EXTENSION; + + if (fileExists(file)) + fs["unlinkSync"](url["pathToFileURL"](file)); + }; + + StorageCapacitor.prototype.imageToFile = function(blob, name) { + electron["remote"]["dialog"]["showSaveDialog"]( + null, + { + filters: [pngFilter] + }).then(result => { + if (!result["canceled"]) { + const reader = new FileReader(); + + reader.addEventListener("loadend", () => { + fs["writeFileSync"](url["pathToFileURL"](result["filePath"]), new Uint8Array(reader.result)); + }); + + reader.readAsArrayBuffer(blob); + } + }); + }; +} \ No newline at end of file diff --git a/js/storage/storageFileCapacitor.js b/js/storage/storageFileCapacitor.js new file mode 100644 index 00000000..fb63c47d --- /dev/null +++ b/js/storage/storageFileCapacitor.js @@ -0,0 +1,205 @@ +/** + * A storage system using files + * @constructor + */ +const StorageFileCapacitor = function() { + StorageSystem.call(this); + + this.hasClipboard = false; + + if (!Capacitor.isPluginAvailable('Filesystem')) { + throw new Error('Capacitor Preferences API is not available'); + } + +}; + +StorageFileCapacitor.prototype = Object.create(StorageSystem.prototype); +StorageFileCapacitor.prototype.EXTENSION = ".sav"; +StorageFileCapacitor.prototype.DIRECTORY_SAVE = "save/"; +StorageFileCapacitor.prototype.DIRECTORY_IMAGE = "koi/"; + +const directoryLibrary = "LIBRARY"; +const directoryData = "DOCUMENTS"; +const encodingText = "utf8"; + +const writeFileCap = async (filename, content, dir, encoding=null) => { + await Capacitor.Plugins.Filesystem.writeFile({ + path: filename, + data: content, + directory: dir, + encoding: encoding, + }); +}; + +const readFileCap = async (filename, dir, encoding=null) => { + const contents = await Capacitor.Plugins.Filesystem.readFile({ + path: filename, + directory: dir, + encoding: encoding, + }); + + return contents.data; + + // console.log('secrets:', contents); +}; + +const deleteFileCap = async (filename, dir = directoryLibrary) => { + await Capacitor.Plugins.Filesystem.deleteFile({ + path: filename, + directory: dir, + }); +}; + +const fileExistsCap = async (filename, dir = directoryLibrary) => { + try { + await Capacitor.Plugins.Filesystem.stat( + { + path: filename, + directory: dir, + } + ); + return true; + } catch (checkDirException) { + if (checkDirException.message === 'File does not exist') { + return false; + } else { + return false; + } + } +}; + +const makeDirectory = async (path, dir = directory) => { + if (await fileExistsCap(path, dir)) + return; + + try { + await Capacitor.Plugins.Filesystem.mkdir({ + path: path, + directory: dir, + recursive: true + }); + } catch (error) { + if (error.message.toLowerCase() !== "directory exists") + throw error; + } + +}; + +const fileExists = name => { + return fileExistsCap(name); +}; + +const pngFilter = { + "name": "PNG Image", + "extensions": ["png"] +}; + +StorageFileCapacitor.prototype.set = async function (key, value) { + return writeFileCap(key + this.EXTENSION, value, directoryLibrary, encodingText); +}; + +StorageFileCapacitor.prototype.setBuffer = async function(key, value) { + // makeDirectory(); + + return writeFileCap(key + this.EXTENSION, value.toString(), directoryLibrary, encodingText); +}; + +StorageFileCapacitor.prototype.get = async function(key) { + const file = key + this.EXTENSION; + let contents = null; + + try { + if (await fileExists(file)) + contents = readFileCap(file, directoryLibrary, encodingText); + } + catch (error) { + + } + + return contents; +}; + +StorageFileCapacitor.prototype.getBuffer = async function(key) { + const file = key + this.EXTENSION; + let contents = null; + + try { + if (await fileExists(file)) + contents = await readFileCap(file, directoryLibrary, encodingText); + } + catch (error) { + + } + + return contents ? new BinBuffer(contents) : null; +}; + +StorageFileCapacitor.prototype.remove = async function(key) { + const file = key + this.EXTENSION; + + if (await fileExists(file)) + await deleteFileCap(file); +}; + +const pickFile = async () => { + const result = await Capacitor.Plugins.FilePicker.pickImages({ + "limit": 0, + "readData": true + }); + return result.files; +}; + +const pickFiles = async () => { + const result = await Capacitor.Plugins.FilePicker.pickFiles({ + "limit": 0, + "extensions": ["png", "jpg", "jpeg"], + "readData": true + }); + return result.files; +} + +StorageFileCapacitor.prototype.loadImage = async function(name) { + const files = await pickFile(); + + const e = new CustomEvent('loadImage'); + // + // const blobFiles = []; + // for (const f of files) { + // blobFiles.push(new File([f.data], {type: f.mimeType})); + // } + + e.dataTransfer = { + files: files + } + + console.log('loadImage', files); + + window.dispatchEvent(e); +} + +StorageFileCapacitor.prototype.imageToFile = async function(blob, name) { + + makeDirectory(this.DIRECTORY_IMAGE, directoryData).then( () => { + const file = this.DIRECTORY_IMAGE + name; + + const reader = new FileReader(); + + reader.onloadend = function() { + const base64Data = reader.result; + + writeFileCap(file, base64Data, directoryData).then( () => { + if (Capacitor.isPluginAvailable('Toast')) + Capacitor.Plugins.Toast.show({ + text: 'File written', + duration: 'long' + }); + }); + } + + reader.readAsDataURL(blob); + + } + ); + + +}; diff --git a/js/storage/storageLocal.js b/js/storage/storageLocal.js index d7676e79..02cafe3f 100644 --- a/js/storage/storageLocal.js +++ b/js/storage/storageLocal.js @@ -8,13 +8,36 @@ const StorageLocal = function() { StorageLocal.prototype = Object.create(StorageSystem.prototype); +StorageLocal.prototype.setItem = async function(key, value) { + return new Promise((resolve, reject) => { + resolve(window.localStorage.setItem(key, value)); + } + ); +}; + +StorageLocal.prototype.getItem = async function(key) { + return new Promise((resolve, reject) => { + if (window.localStorage.getItem(key) === null) + resolve(null); + else + resolve(window.localStorage.getItem(key)); + }); +}; + +StorageLocal.prototype.removeItem = async function(key){ + return new Promise((resolve, reject) => { + resolve(window.localStorage.removeItem(key)); + } + ); +}; + /** * Set the value of an item * @param {String} key The key of the item * @param {String} value The value of the item */ StorageLocal.prototype.set = function(key, value) { - window["localStorage"].setItem(key, value); + return this.setItem(key, value); }; /** @@ -23,7 +46,7 @@ StorageLocal.prototype.set = function(key, value) { * @param {BinBuffer} value The buffer of the item */ StorageLocal.prototype.setBuffer = function(key, value) { - this.set(key, value.toString()); + return this.set(key, value.toString()); }; /** @@ -32,7 +55,8 @@ StorageLocal.prototype.setBuffer = function(key, value) { * @returns {String|null} The value of the item, or null if it does not exist */ StorageLocal.prototype.get = function(key) { - return window["localStorage"].getItem(key); + const item = this.getItem(key); + return item; }; /** @@ -40,8 +64,8 @@ StorageLocal.prototype.get = function(key) { * @param {String} key The key of the buffer * @returns {BinBuffer|null} The buffer, or null if it does not exist */ -StorageLocal.prototype.getBuffer = function(key) { - const string = this.get(key); +StorageLocal.prototype.getBuffer = async function(key) { + const string = await this.get(key); if (string) return new BinBuffer(string); @@ -54,7 +78,7 @@ StorageLocal.prototype.getBuffer = function(key) { * @param {String} key The key of the item */ StorageLocal.prototype.remove = function(key) { - window["localStorage"].removeItem(key); + this.removeItem(key); }; /** diff --git a/js/storage/storagePreferencesCapacitor.js b/js/storage/storagePreferencesCapacitor.js new file mode 100644 index 00000000..9168287d --- /dev/null +++ b/js/storage/storagePreferencesCapacitor.js @@ -0,0 +1,103 @@ +/** + * A storage system using the browsers local storage + * @constructor + */ +const StoragePreferencesCapacitor = function() { + StorageSystem.call(this); + + if (!Capacitor.isPluginAvailable('Preferences')) { + throw new Error('Capacitor Preferences API is not available'); + } +}; + +StoragePreferencesCapacitor.prototype = Object.create(StorageSystem.prototype); + +StoragePreferencesCapacitor.prototype.setItem = async function(key, value) { + return await Capacitor.Plugins.Preferences.set({ + key: key, + value: JSON.stringify(value), + }); +}; + +StoragePreferencesCapacitor.prototype.getItem = async function(key) { + const item = await Capacitor.Plugins.Preferences.get({ key: key }); + return JSON.parse(item.value); +}; + +StoragePreferencesCapacitor.prototype.removeItem = async function(key){ + return await Capacitor.Plugins.Preferences.remove({ + key: key, + }); +}; + +/** + * Set the value of an item + * @param {String} key The key of the item + * @param {String} value The value of the item + */ +StoragePreferencesCapacitor.prototype.set = function(key, value) { + return this.setItem(key, value); +}; + +/** + * Set the buffer of an item + * @param {String} key The key of the item + * @param {BinBuffer} value The buffer of the item + */ +StoragePreferencesCapacitor.prototype.setBuffer = function(key, value) { + return this.set(key, value.toString()); +}; + +/** + * Get an item + * @param {String} key The key of the item + * @returns {String|null} The value of the item, or null if it does not exist + */ +StoragePreferencesCapacitor.prototype.get = function(key) { + const item = this.getItem(key); + return item; +}; + +/** + * Get a buffer + * @param {String} key The key of the buffer + * @returns {BinBuffer|null} The buffer, or null if it does not exist + */ +StoragePreferencesCapacitor.prototype.getBuffer = async function(key) { + const string = await this.get(key); + + if (string) + return new BinBuffer(string); + + return null; +}; + +/** + * Remove an item + * @param {String} key The key of the item + */ +StoragePreferencesCapacitor.prototype.remove = function(key) { + this.removeItem(key); +}; + +/** + * Save an image + * @param {Blob} blob The image blob data + * @param {String} name The file name + */ +StoragePreferencesCapacitor.prototype.imageToFile = function(blob, name) { + const a = document.createElement("a"); + const url = URL.createObjectURL(blob); + + a.href = url; + a.download = name; + + document.body.appendChild(a); + + a.click(); + + setTimeout(() => { + document.body.removeChild(a); + URL.revokeObjectURL(url); + }, 0); +}; \ No newline at end of file diff --git a/svg/Download_on_the_App_Store_Badge_US-UK_RGB_blk_092917.svg b/svg/Download_on_the_App_Store_Badge_US-UK_RGB_blk_092917.svg new file mode 100644 index 00000000..072b425a --- /dev/null +++ b/svg/Download_on_the_App_Store_Badge_US-UK_RGB_blk_092917.svg @@ -0,0 +1,46 @@ + + Download_on_the_App_Store_Badge_US-UK_RGB_blk_4SVG_092917 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/Google_Play_Store_badge_EN_WHITE.svg b/svg/Google_Play_Store_badge_EN_WHITE.svg new file mode 100644 index 00000000..cafadec8 --- /dev/null +++ b/svg/Google_Play_Store_badge_EN_WHITE.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/svg/add.svg b/svg/add.svg new file mode 100644 index 00000000..65ea2875 --- /dev/null +++ b/svg/add.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/svg/butterfly.svg b/svg/butterfly.svg new file mode 100644 index 00000000..e73575b2 --- /dev/null +++ b/svg/butterfly.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/svg/discord-mark-white.svg b/svg/discord-mark-white.svg new file mode 100644 index 00000000..7f9a31f0 --- /dev/null +++ b/svg/discord-mark-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/svg/settings.svg b/svg/settings.svg new file mode 100644 index 00000000..f4866a11 --- /dev/null +++ b/svg/settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/svg/website.svg b/svg/website.svg new file mode 100644 index 00000000..8b96a41d --- /dev/null +++ b/svg/website.svg @@ -0,0 +1 @@ + \ No newline at end of file From 9a1c208c2efd870d75dc50f2571d1583d9903cde Mon Sep 17 00:00:00 2001 From: DanielsWrath Date: Sun, 28 Apr 2024 16:00:45 +0200 Subject: [PATCH 02/18] added code for electron --- index.html | 1 - js/koi/gui/cards/cards.js | 10 ++++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index d0c90132..81545898 100644 --- a/index.html +++ b/index.html @@ -47,7 +47,6 @@ - diff --git a/js/koi/gui/cards/cards.js b/js/koi/gui/cards/cards.js index a67d4882..3359bf7e 100644 --- a/js/koi/gui/cards/cards.js +++ b/js/koi/gui/cards/cards.js @@ -98,13 +98,19 @@ Cards.prototype.toggleBook = function() { Cards.prototype.hideButtons = function() { this.element.removeChild(this.buttonMenu.element); - this.element.removeChild(this.buttonLoadCard.element); + + if (RUNNING_CAPACITOR) + this.element.removeChild(this.buttonLoadCard.element); + this.buttonsShown = false; } Cards.prototype.showButtons = function() { this.element.appendChild(this.buttonMenu.element); - this.element.appendChild(this.buttonLoadCard.element); + + if (RUNNING_CAPACITOR) + this.element.appendChild(this.buttonLoadCard.element); + this.buttonsShown = true; } From b21a5b62eb48896b61fe7e540135f1ff39d806db Mon Sep 17 00:00:00 2001 From: DanielsWrath Date: Sun, 28 Apr 2024 16:06:28 +0200 Subject: [PATCH 03/18] removed comments --- js/main.js | 24 ++++-------------------- js/storage/storageFileCapacitor.js | 4 ---- 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/js/main.js b/js/main.js index f913629f..1f0310a5 100644 --- a/js/main.js +++ b/js/main.js @@ -1,7 +1,4 @@ const searchParams = new URLSearchParams(window.location.search); -// const preferences = new StoragePreferencesCapacitor(); - -// TODO: do not use localstorage const glParameters = { alpha: false, antialias: window["localStorage"].getItem(Menu.prototype.KEY_MSAA) === null || window["localStorage"].getItem(Menu.prototype.KEY_MSAA) === "true", @@ -19,7 +16,7 @@ let chosenSlot = -1; * Check if game is running within WKWebView on iOS. */ -const RUNNING_ON_WEBVIEW_IOS = navigator.standalone || window["webkit"] && window["webkit"].messageHandlers; +const RUNNING_ON_WEBVIEW_IOS = (window.webkit && window.webkit.messageHandlers) ? true : false; const RUNNING_CAPACITOR = typeof Capacitor !== "undefined" && Capacitor.platform !== "web"; @@ -224,7 +221,6 @@ function padNotch() { setFullScreen(); -// TODO: do not use localstorage const paramLang = window["localStorage"].getItem(Menu.prototype.KEY_LANGUAGE) || searchParams.get("lang"); const language = paramLang ? makeLanguage(paramLang) : makeLanguage(navigator.language.substring(0, 2)); const loader = new Loader( @@ -233,8 +229,8 @@ const loader = new Loader( document.getElementById("loader-slots"), document.getElementById("loader-button-settings"), document.getElementById("wrapper"), - !RUNNING_MOBILE, - !RUNNING_MOBILE); + !RUNNING_MOBILE && !RUNNING_ON_WEBVIEW_IOS, + !RUNNING_MOBILE && !RUNNING_ON_WEBVIEW_IOS); let imperial = false; let menu = null; let storage = null; @@ -257,7 +253,7 @@ if (gl && let session = new Session(); let slot = null; const slotNames = ["session", "session2", "session3"]; - storage = RUNNING_CAPACITOR ? new StorageFileCapacitor() : new StorageLocal(); + const storage = window["require"] ? new StorageFile() : new StorageLocal(); const wrapper = document.getElementById("wrapper"); const gui = new GUI( document.getElementById("gui"), @@ -279,18 +275,6 @@ if (gl && let control = false; let shift = false; - - // testFileIO(storage); - - console.log("Koi Farm"); - console.log( - ` - Environment = ${PLATFORM_NAME} - Storage = ${window["require"] ? "file" : "local"} - Language = ${chosenLocale} - ` - ); - new Drop(gui, systems, document.getElementById("drop"), canvas); loader.setMenu(menu); diff --git a/js/storage/storageFileCapacitor.js b/js/storage/storageFileCapacitor.js index fb63c47d..574ca6f9 100644 --- a/js/storage/storageFileCapacitor.js +++ b/js/storage/storageFileCapacitor.js @@ -39,8 +39,6 @@ const readFileCap = async (filename, dir, encoding=null) => { }); return contents.data; - - // console.log('secrets:', contents); }; const deleteFileCap = async (filename, dir = directoryLibrary) => { @@ -172,8 +170,6 @@ StorageFileCapacitor.prototype.loadImage = async function(name) { files: files } - console.log('loadImage', files); - window.dispatchEvent(e); } From 4c875154f32f4b26dc1ab70585227222d49dd3a1 Mon Sep 17 00:00:00 2001 From: DanielsWrath Date: Sun, 28 Apr 2024 16:10:45 +0200 Subject: [PATCH 04/18] removed add button in menu on pc --- js/koi/gui/menu/menu.js | 1 - js/koi/koi.js | 1 - 2 files changed, 2 deletions(-) diff --git a/js/koi/gui/menu/menu.js b/js/koi/gui/menu/menu.js index dd77e2c6..e5c5769f 100644 --- a/js/koi/gui/menu/menu.js +++ b/js/koi/gui/menu/menu.js @@ -260,7 +260,6 @@ Menu.prototype.createMSAAToggle = function() { element.type = "checkbox"; element.checked = true; - // TODO: do not use local storage if (window["localStorage"].getItem(this.KEY_MSAA)) element.checked = window["localStorage"].getItem(this.KEY_MSAA) === "true"; else diff --git a/js/koi/koi.js b/js/koi/koi.js index 2cf34d7e..98243ad6 100644 --- a/js/koi/koi.js +++ b/js/koi/koi.js @@ -439,7 +439,6 @@ Koi.prototype.update = function() { if (this.tutorial && this.tutorial.update(this)) { if (this.tutorial instanceof TutorialBreeding) { this.spawner.setBehavior(new SpawnerBehaviorDefault()); - // TODO: do something with this storage this.tutorial = new TutorialCards(this.storage, this.gui.overlay); } else From 730e77d507eb87e5d0aef069aaefea1900ec0196 Mon Sep 17 00:00:00 2001 From: DanielsWrath Date: Sun, 28 Apr 2024 16:41:07 +0200 Subject: [PATCH 05/18] add loading text after loading of the language --- js/koi/gui/loader/loader.js | 7 +++++++ js/koi/gui/loader/loaderLoadInfo.js | 11 +++++++++-- js/main.js | 1 + 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/js/koi/gui/loader/loader.js b/js/koi/gui/loader/loader.js index 20ae2216..8c48e432 100644 --- a/js/koi/gui/loader/loader.js +++ b/js/koi/gui/loader/loader.js @@ -89,6 +89,13 @@ Loader.prototype.ID_DISCORD = "loader-discord"; Loader.prototype.BUTTON_DELAY = .37; Loader.prototype.TRANSITION = StyleUtils.getFloat("--loader-fade-out"); +/** + * Set the loading text. + */ +Loader.prototype.setLoadingText = function() { + this.loadInfo.setText(); +} + /** * Set the menu * @param {Menu} menu The menu diff --git a/js/koi/gui/loader/loaderLoadInfo.js b/js/koi/gui/loader/loaderLoadInfo.js index 8bdb281e..bb03ff4a 100644 --- a/js/koi/gui/loader/loaderLoadInfo.js +++ b/js/koi/gui/loader/loaderLoadInfo.js @@ -14,7 +14,7 @@ const LoaderLoadInfo = function() { LoaderLoadInfo.prototype.ID = "loader-loading"; LoaderLoadInfo.prototype.TEXT_ID = "loader-loading-text"; LoaderLoadInfo.prototype.ICON_ID = "loader-loading-icon"; -LoaderLoadInfo.prototype.TEXT = "Loading..."; +LoaderLoadInfo.prototype.TEXT = "LOADING"; LoaderLoadInfo.prototype.CLASS_INVISIBLE = "invisible"; LoaderLoadInfo.prototype.FILE = "svg/butterfly.svg"; LoaderLoadInfo.prototype.FADE_IN_DELAY = .32; @@ -38,6 +38,13 @@ LoaderLoadInfo.prototype.loadSVG = function() { request.send(); }; +/** + * Set the text + */ +LoaderLoadInfo.prototype.setText = function() { + this.textElement.innerText = language.get("LOADING"); +} + /** * Create the element * @returns {HTMLDivElement} The element @@ -50,7 +57,7 @@ LoaderLoadInfo.prototype.createElement = function() { this.iconElement.id = this.ICON_ID; this.textElement.id = this.TEXT_ID; - this.textElement.innerText = this.TEXT; + // this.textElement.innerText = ; element.appendChild(this.iconElement); element.appendChild(this.textElement); diff --git a/js/main.js b/js/main.js index 1f0310a5..93a84d67 100644 --- a/js/main.js +++ b/js/main.js @@ -244,6 +244,7 @@ if (gl && const audio = new AudioBank(audioEngine); language.load(() => { + loader.setLoadingText(); imperial = language.get("UNIT_LENGTH") === "ft"; const settings = { From 39ca6111c6441d7e2bafa65aad0aab359f5c4d0b Mon Sep 17 00:00:00 2001 From: DanielsWrath Date: Sun, 28 Apr 2024 16:49:14 +0200 Subject: [PATCH 06/18] fixed skip button size --- css/overlay.css | 3 +++ js/koi/gui/overlay/overlay.js | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/css/overlay.css b/css/overlay.css index c4e9c5d2..2871e8e1 100644 --- a/css/overlay.css +++ b/css/overlay.css @@ -106,6 +106,9 @@ /*margin-top: var(--overlay-text-top);*/ pointer-events: auto; /*touch-action: all;*/ + width: auto; + min-width: var(--button-width); + user-select: auto; position: absolute; left: 10px; diff --git a/js/koi/gui/overlay/overlay.js b/js/koi/gui/overlay/overlay.js index 21cdbd5e..5dd44449 100644 --- a/js/koi/gui/overlay/overlay.js +++ b/js/koi/gui/overlay/overlay.js @@ -19,6 +19,7 @@ Overlay.prototype.CLASS_HIGHLIGHT = "overlay-highlight"; Overlay.prototype.CLASS_ARROW = "overlay-arrow"; Overlay.prototype.CLASS_TEXT = "text"; Overlay.prototype.CLASS_SKIP = "skip-button"; +Overlay.prototype.KEY_SKIP = "TUTORIAL_SKIP"; Overlay.prototype.POINTER_RADIUS = StyleUtils.getInt("--overlay-pointer-radius") + StyleUtils.getInt("--overlay-pointer-border"); @@ -127,7 +128,7 @@ Overlay.prototype.createSkipElement = function() { element.className = this.CLASS_SKIP; - element.appendChild(document.createTextNode("Skip")); + element.appendChild(document.createTextNode(language.get(Overlay.prototype.KEY_SKIP))); return element; } From d2b8c8f6b07ac86aa2dfb1ffde52fc699c03746d Mon Sep 17 00:00:00 2001 From: DanielsWrath Date: Sun, 28 Apr 2024 17:34:09 +0200 Subject: [PATCH 07/18] changed koiTranslations checkout --- KoiTranslations | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KoiTranslations b/KoiTranslations index 4a996f37..70994e10 160000 --- a/KoiTranslations +++ b/KoiTranslations @@ -1 +1 @@ -Subproject commit 4a996f3747635b9749223c1c893ee73303cc154d +Subproject commit 70994e105bb43a460af7f89ec268c68a2aa4b6a7 From fab7e7589ce1cb696367161b0a67c39c9b53c0d4 Mon Sep 17 00:00:00 2001 From: DanielsWrath Date: Sun, 28 Apr 2024 17:54:12 +0200 Subject: [PATCH 08/18] fixed size of play store icon --- svg/Google_Play_Store_badge_EN_WHITE.svg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/svg/Google_Play_Store_badge_EN_WHITE.svg b/svg/Google_Play_Store_badge_EN_WHITE.svg index cafadec8..cd64c0ea 100644 --- a/svg/Google_Play_Store_badge_EN_WHITE.svg +++ b/svg/Google_Play_Store_badge_EN_WHITE.svg @@ -5,8 +5,8 @@ xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:krita="http://krita.org/namespaces/svg/krita" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - width="25.2pt" - height="7.13pt" + width="119.66407" + height="40" viewBox="0 0 25.92 7.675"> From 6a5cafa506da92b813d3816d95faaf49c792b5e6 Mon Sep 17 00:00:00 2001 From: DanielsWrath Date: Sun, 28 Apr 2024 18:20:54 +0200 Subject: [PATCH 09/18] made storageFile async --- js/storage/storageFile.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/js/storage/storageFile.js b/js/storage/storageFile.js index 860a09dc..03e3a432 100644 --- a/js/storage/storageFile.js +++ b/js/storage/storageFile.js @@ -34,19 +34,19 @@ if (window["require"]) { "extensions": ["png"] }; - StorageFile.prototype.set = function (key, value) { + StorageFile.prototype.set = async function (key, value) { makeDirectory(); fs["writeFileSync"](url["pathToFileURL"](directory + key + this.EXTENSION), value); }; - StorageFile.prototype.setBuffer = function(key, value) { + StorageFile.prototype.setBuffer = async function(key, value) { makeDirectory(); fs["writeFileSync"](url["pathToFileURL"](directory + key + this.EXTENSION), value.toByteArray()); }; - StorageFile.prototype.get = function(key) { + StorageFile.prototype.get = async function(key) { const file = directory + key + this.EXTENSION; let contents = null; @@ -61,7 +61,7 @@ if (window["require"]) { return contents; }; - StorageFile.prototype.getBuffer = function(key) { + StorageFile.prototype.getBuffer = async function(key) { const file = directory + key + this.EXTENSION; let contents = null; @@ -76,7 +76,7 @@ if (window["require"]) { return contents ? new BinBuffer(contents) : null; }; - StorageFile.prototype.remove = function(key) { + StorageFile.prototype.remove = async function(key) { const file = directory + key + this.EXTENSION; if (fileExists(file)) From 710d4f6b68c90c890475a24a865cc68c303e6fd5 Mon Sep 17 00:00:00 2001 From: DanielsWrath Date: Wed, 1 May 2024 10:49:16 +0200 Subject: [PATCH 10/18] fixed issues with pc version --- css/book.css | 20 ++++++++------------ css/buttons.css | 10 ++-------- css/card.css | 21 ++++++++++----------- css/drop.css | 2 +- css/loader.css | 1 + css/menu.css | 3 +-- css/overlay.css | 2 +- css/style.css | 2 -- 8 files changed, 24 insertions(+), 37 deletions(-) diff --git a/css/book.css b/css/book.css index 55ed128c..a32cbf2e 100644 --- a/css/book.css +++ b/css/book.css @@ -29,9 +29,9 @@ background-color: var(--card-color-background); display: flex; align-items: center; - /*transition: var(--book-hide-time) ease;*/ - /*transition-property: top;*/ - /*box-shadow: 0 0 var(--book-shadow-radius) var(--book-shadow-color);*/ + transition: var(--book-hide-time) ease; + transition-property: top; + box-shadow: 0 0 var(--book-shadow-radius) var(--book-shadow-color); user-select: none; border-radius: var(--card-border-radius); } @@ -68,11 +68,6 @@ opacity: 0; } -#book #spine .page { - transform: none !important; - /*opacity: 0 !important;*/ -} - #book #spine .page .overlay.left { background-color: var(--color-white); } @@ -123,7 +118,7 @@ #book #spine .page .slot .card { margin: 0; transform-origin: left top; - /*box-shadow: none;*/ + box-shadow: none; } #cards button:active svg { @@ -150,7 +145,7 @@ } #button-book svg { - /*filter: drop-shadow(0 0 var(--page-button-shadow-radius) var(--book-shadow-color));*/ + filter: drop-shadow(0 0 var(--page-button-shadow-radius) var(--book-shadow-color)); } #button-book:active path.page { @@ -181,7 +176,7 @@ } .button-page svg { - /*filter: drop-shadow(0 0 var(--page-button-shadow-radius) var(--book-shadow-color));*/ + filter: drop-shadow(0 0 var(--page-button-shadow-radius) var(--book-shadow-color)); } .button-page path { @@ -238,7 +233,8 @@ } } -@media only screen and (orientation: landscape) and (max-width: 600px), (orientation: landscape) and (max-height: 600px) { +@media only screen and (orientation: landscape) and (max-width: 600px), +(orientation: landscape) and (max-height: 600px) { #button-load-card { position: absolute; right: var(--book-button-padding); diff --git a/css/buttons.css b/css/buttons.css index 5c207bec..97744768 100644 --- a/css/buttons.css +++ b/css/buttons.css @@ -1,18 +1,12 @@ :root { - --button-width: 5rem; - --button-height: 5rem; - - --button-height-small: 3.5rem; - --button-width-small: 3.5rem; - + --button-width: 80px; + --button-height: 80px; --load-card-button-padding: 10px; } button { - /*display: flex;*/ width: var(--button-width); height: var(--button-height); - /*flex: 1;*/ border: none; box-sizing: border-box; cursor: pointer; diff --git a/css/card.css b/css/card.css index e6795c1a..cc649210 100644 --- a/css/card.css +++ b/css/card.css @@ -19,7 +19,7 @@ --card-preview-margin: calc((var(--card-width) - var(--card-preview-width) - 2 * var(--card-preview-border)) * 0.5); --card-preview-columns: 6; --card-preview-rows: 10; - --card-preview-fps: 30; + --card-preview-fps: 60; --card-preview-border-radius: 6px; --card-preview-border: 2px; --card-border-radius: 12px; @@ -28,17 +28,17 @@ --card-colors-raise: 0.5; --card-colors-overlap: 0.25; --card-colors-gradient: 280%; - --card-info-spacing: 10px; + --card-info-spacing: 12px; --card-info-color: #d7d4c8; --card-info-transparant-color: rgba(215, 212, 200, 0); --card-info-slant: -18deg; - --card-info-padding-right: 2px; + --card-info-padding-right: 6px; --card-info-text-color: #0c0c0c; - --card-code-icon-radius: 14px; - --card-code-icon-margin: 2px; + --card-code-icon-radius: 32px; + --card-code-icon-margin: 8px; --card-code-opacity: 0.5; - --font-size: .7rem; + --font-size: 20px; } .card-shape { @@ -55,10 +55,9 @@ background-color: var(--card-color-background); position: absolute; user-select: none; - /*box-shadow: 0 0 var(--book-shadow-radius) var(--book-shadow-color);*/ + box-shadow: 0 0 var(--book-shadow-radius) var(--book-shadow-color); overflow: hidden; -webkit-tap-highlight-color: transparent; - /*scale: 0.7;*/ } .card svg { @@ -180,7 +179,7 @@ .card .info .property { margin-top: calc(var(--card-info-spacing) / 4); margin-bottom: calc(var(--card-info-spacing) / 4); - font-size: var(--font-size); + font-size: 20px; position: relative; } @@ -271,8 +270,8 @@ left: 50%; bottom: calc(var(--card-drop-target-raise) * var(--card-height)); background-color: var(--card-color-drop-target); - /*transition: 0.1s;*/ - /*transition-property: bottom;*/ + transition: 0.1s; + transition-property: bottom; } #drop-target.hidden { diff --git a/css/drop.css b/css/drop.css index c8bfcdec..2ec5ef75 100644 --- a/css/drop.css +++ b/css/drop.css @@ -9,5 +9,5 @@ } #drop.possible { - background-color: rgba(246, 246, 246, 0); + background-color: #f6f6f655; } \ No newline at end of file diff --git a/css/loader.css b/css/loader.css index 3796e0b6..4cb16b59 100644 --- a/css/loader.css +++ b/css/loader.css @@ -15,6 +15,7 @@ --loader-fullscreen-border: 8px; --loader-fullscreen-corner-size: 30%; } + #loader { position: absolute; left: 0; diff --git a/css/menu.css b/css/menu.css index d29c18a2..a4b7c941 100644 --- a/css/menu.css +++ b/css/menu.css @@ -55,7 +55,6 @@ padding-left: calc(2 * var(--menu-box-spacing)); padding-right: calc(2 * var(--menu-box-spacing)); height: 54px; - max-height: 5rem; } #menu-box button:active { @@ -65,7 +64,7 @@ #menu-box label { color: var(--color-white); - font-size: var(--menu-font-size); + font-size: 18px; margin-right: var(--menu-box-spacing); } diff --git a/css/overlay.css b/css/overlay.css index 2871e8e1..8294edca 100644 --- a/css/overlay.css +++ b/css/overlay.css @@ -58,7 +58,7 @@ text-align: center; background-color: #325c73cc; color: var(--color-white); - font-size: 1.5rem; + font-size: 32px; border-radius: 16px; border: 2px solid var(--color-white); } diff --git a/css/style.css b/css/style.css index cbdcaf23..cf8473aa 100644 --- a/css/style.css +++ b/css/style.css @@ -15,7 +15,6 @@ body { height: 100%; } - #gui { pointer-events: none; position: absolute; @@ -25,7 +24,6 @@ body { height: 100%; } - #gui #cards { position: absolute; left: 0; From d38db646d4164d791e8ebdb5056c30dc08e8a56d Mon Sep 17 00:00:00 2001 From: DanielsWrath Date: Wed, 1 May 2024 11:20:10 +0200 Subject: [PATCH 11/18] added cached data for loading text --- js/koi/gui/loader/loader.js | 5 +++-- js/koi/gui/loader/loaderLoadInfo.js | 9 +++++++-- js/main.js | 7 +++++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/js/koi/gui/loader/loader.js b/js/koi/gui/loader/loader.js index 8c48e432..84d91303 100644 --- a/js/koi/gui/loader/loader.js +++ b/js/koi/gui/loader/loader.js @@ -91,9 +91,10 @@ Loader.prototype.TRANSITION = StyleUtils.getFloat("--loader-fade-out"); /** * Set the loading text. + * @param {String} text The text to set, or null to use the default text */ -Loader.prototype.setLoadingText = function() { - this.loadInfo.setText(); +Loader.prototype.setLoadingText = function(text = null) { + this.loadInfo.setText(text); } /** diff --git a/js/koi/gui/loader/loaderLoadInfo.js b/js/koi/gui/loader/loaderLoadInfo.js index bb03ff4a..c959ce73 100644 --- a/js/koi/gui/loader/loaderLoadInfo.js +++ b/js/koi/gui/loader/loaderLoadInfo.js @@ -18,6 +18,7 @@ LoaderLoadInfo.prototype.TEXT = "LOADING"; LoaderLoadInfo.prototype.CLASS_INVISIBLE = "invisible"; LoaderLoadInfo.prototype.FILE = "svg/butterfly.svg"; LoaderLoadInfo.prototype.FADE_IN_DELAY = .32; +LoaderLoadInfo.prototype.LOADING_TEXT = "LOADING"; /** @@ -40,9 +41,13 @@ LoaderLoadInfo.prototype.loadSVG = function() { /** * Set the text + * @param {String} text The text, or null to use the default text */ -LoaderLoadInfo.prototype.setText = function() { - this.textElement.innerText = language.get("LOADING"); +LoaderLoadInfo.prototype.setText = function(text = null) { + if (text) + this.textElement.innerText = text; + else + this.textElement.innerText = language.get(LoaderLoadInfo.prototype.LOADING_TEXT); } /** diff --git a/js/main.js b/js/main.js index 93a84d67..774e39ee 100644 --- a/js/main.js +++ b/js/main.js @@ -235,6 +235,10 @@ let imperial = false; let menu = null; let storage = null; +// Set the loading text to a cached value +preferences.get(LoaderLoadInfo.prototype.LOADING_TEXT).then((value) => { + loader.setLoadingText(value ? value : "Loading"); +}); if (gl && gl.getExtension("OES_element_index_uint") && @@ -244,7 +248,10 @@ if (gl && const audio = new AudioBank(audioEngine); language.load(() => { + // Cache the loading text and set it + preferences.set(LoaderLoadInfo.prototype.LOADING_TEXT, language.get(LoaderLoadInfo.prototype.LOADING_TEXT)); loader.setLoadingText(); + imperial = language.get("UNIT_LENGTH") === "ft"; const settings = { From bb8aff037e9b4b0633aa40e00bb9ef80389387c0 Mon Sep 17 00:00:00 2001 From: DanielsWrath Date: Wed, 1 May 2024 11:23:41 +0200 Subject: [PATCH 12/18] added missing csS --- css/buttons.css | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/css/buttons.css b/css/buttons.css index 97744768..4eeae1ad 100644 --- a/css/buttons.css +++ b/css/buttons.css @@ -1,7 +1,11 @@ :root { --button-width: 80px; --button-height: 80px; + --load-card-button-padding: 10px; + + --button-height-small: 3.5rem; + --button-width-small: 3.5rem; } button { diff --git a/package.json b/package.json index c6c6f178..74a5408d 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "google-closure-compiler": "^20210106.0.0" }, "scripts": { - "compress": "python3 squish.py/squish.py index.html release.html", + "compress": "python3.12 squish.py/squish.py index.html release.html", "build-win-32": "npm run compress && electron-packager . KoiFarm --platform=win32 --arch=ia32 --icon=./favicon.ico --appCopyright=\"Copyright Job Talle 2021\" --ignore=\"^/js|^/css|^/squish\\.py|^/node_modules|^/\\.idea|index\\.html|README\\.md|favicon\\.icns|LICENSE|package-lock\\.json\"", "build-win-64": "npm run compress && electron-packager . KoiFarm --platform=win32 --arch=x64 --icon=./favicon.ico --appCopyright=\"Copyright Job Talle 2021\" --ignore=\"^/js|^/css|^/squish\\.py|^/node_modules|^/\\.idea|index\\.html|README\\.md|favicon\\.icns|LICENSE|package-lock\\.json\"", "build-linux-64": "npm run compress && electron-packager . KoiFarm --platform=linux --arch=x64 --icon=./favicon.ico --appCopyright=\"Copyright Job Talle 2021\" --ignore=\"^/js|^/css|^/squish\\.py|^/node_modules|^/\\.idea|index\\.html|README\\.md|favicon\\.icns|LICENSE|package-lock\\.json\"", From 4150adcd6a5982d75f3a6a908842aa5434be7a73 Mon Sep 17 00:00:00 2001 From: Job Talle Date: Wed, 1 May 2024 17:45:22 +0200 Subject: [PATCH 13/18] Reversed links --- css/loader.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/css/loader.css b/css/loader.css index 4cb16b59..6ada0b16 100644 --- a/css/loader.css +++ b/css/loader.css @@ -41,10 +41,10 @@ #loader-links { position: absolute; - right: 0; + left: 0; bottom: 0; display: flex; - flex-direction: row; + flex-direction: row-reverse; align-items: flex-end; justify-content: flex-end; width: 100%; From 49297febd0967132f66a283647033943158ac097 Mon Sep 17 00:00:00 2001 From: DanielsWrath Date: Mon, 6 May 2024 14:09:54 +0200 Subject: [PATCH 14/18] reversed order of icons in loader --- css/loader.css | 9 ++------- package.json | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/css/loader.css b/css/loader.css index 4cb16b59..ca02449f 100644 --- a/css/loader.css +++ b/css/loader.css @@ -41,17 +41,16 @@ #loader-links { position: absolute; - right: 0; + left: 0; bottom: 0; display: flex; - flex-direction: row; + flex-direction: row-reverse; align-items: flex-end; justify-content: flex-end; width: 100%; height: 4rem; max-height: 100px; padding: 16px; - } #loader-links .loader-icon { @@ -218,12 +217,8 @@ } .loader-bar { - /*width: 100%;*/ height: var(--loader-logo-width); - /*background-color: var(--color-white);*/ border-radius: 5px; - /*margin-top: 10px;*/ - /*width: 500px;*/ margin: auto 0; align-self: flex-start; justify-self: flex-start; diff --git a/package.json b/package.json index 74a5408d..c6c6f178 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "google-closure-compiler": "^20210106.0.0" }, "scripts": { - "compress": "python3.12 squish.py/squish.py index.html release.html", + "compress": "python3 squish.py/squish.py index.html release.html", "build-win-32": "npm run compress && electron-packager . KoiFarm --platform=win32 --arch=ia32 --icon=./favicon.ico --appCopyright=\"Copyright Job Talle 2021\" --ignore=\"^/js|^/css|^/squish\\.py|^/node_modules|^/\\.idea|index\\.html|README\\.md|favicon\\.icns|LICENSE|package-lock\\.json\"", "build-win-64": "npm run compress && electron-packager . KoiFarm --platform=win32 --arch=x64 --icon=./favicon.ico --appCopyright=\"Copyright Job Talle 2021\" --ignore=\"^/js|^/css|^/squish\\.py|^/node_modules|^/\\.idea|index\\.html|README\\.md|favicon\\.icns|LICENSE|package-lock\\.json\"", "build-linux-64": "npm run compress && electron-packager . KoiFarm --platform=linux --arch=x64 --icon=./favicon.ico --appCopyright=\"Copyright Job Talle 2021\" --ignore=\"^/js|^/css|^/squish\\.py|^/node_modules|^/\\.idea|index\\.html|README\\.md|favicon\\.icns|LICENSE|package-lock\\.json\"", From 46c5a8d3f02f6727ad19084fb41e09ea90ea88c6 Mon Sep 17 00:00:00 2001 From: DanielsWrath Date: Mon, 6 May 2024 14:49:55 +0200 Subject: [PATCH 15/18] cleaned code --- css/book.css | 14 +-------- css/loader.css | 47 ----------------------------- css/overlay.css | 3 -- js/koi/gui/cards/cardBook.js | 40 ++++++++++++------------ js/koi/gui/cards/cardPage.js | 14 ++++----- js/koi/gui/loader/loaderAndroid.js | 5 --- js/koi/gui/loader/loaderApple.js | 13 -------- js/koi/gui/loader/loaderLinks.js | 5 --- js/koi/gui/loader/loaderLoadInfo.js | 4 --- js/koi/tutorial/tutorialCards.js | 10 +----- js/main.js | 19 ++---------- js/storage/storageFileCapacitor.js | 5 --- 12 files changed, 31 insertions(+), 148 deletions(-) diff --git a/css/book.css b/css/book.css index a32cbf2e..1061c2e2 100644 --- a/css/book.css +++ b/css/book.css @@ -247,16 +247,4 @@ top: var(--button-height-small); } -} - -/*@media screen and (max-width: 600px), (max-height: 600px) {*/ -/* #button-load-card {*/ -/* right: calc(var(--button-width-small) * 2 + var(--book-button-padding));*/ -/* top: var(--book-button-padding);*/ -/* }*/ - -/* #button-home {*/ -/* right: calc(var(--button-width-small) + var(--book-button-padding));*/ -/* top: var(--book-button-padding);*/ -/* }*/ -/*}*/ \ No newline at end of file +} \ No newline at end of file diff --git a/css/loader.css b/css/loader.css index ca02449f..da2b911a 100644 --- a/css/loader.css +++ b/css/loader.css @@ -74,17 +74,6 @@ fill: #FEFFFF; } -/*#loader #loader-discord svg{*/ -/* height: 100%;*/ -/* width: auto;*/ -/*}*/ - -/*#loader #loader-website svg {*/ -/* !*fill: #FEFFFF;*!*/ -/* height: 85%;*/ -/* width: auto;*/ -/*}*/ - #loader .loader-icon.invisible, #loader .loader-bar.invisible { opacity: 0; @@ -235,7 +224,6 @@ .loader-bar:hover { filter: invert(100%); - /*box-shadow: 0 0 10px 0 var(--color-white);*/ } #loader-icon { @@ -348,7 +336,6 @@ position: absolute; left: -60px; bottom: 16px; - /* top: -60px; */ top: unset; width: 60px; height: 60px; @@ -356,7 +343,6 @@ background-color: var(--loader-button-text-color); border-top-left-radius: 60px; border-bottom-left-radius: 60px; - /*border-bottom-left-radius: 60px;*/ display: flex; justify-content: flex-end; flex-direction: column; @@ -379,39 +365,6 @@ } } -@media only screen and (orientation: landscape) { - -} - -/* !* For Mobile Portrait View *!*/ -/*@media screen*/ -/* and (max-device-width: 480px)*/ -/* and (orientation: portrait){*/ -/* html{*/ -/* background-color: red;*/ -/* width: 80%;*/ -/* }*/ -/*}*/ - -/*!* For Mobile Landscape View *!*/ -/*@media screen*/ -/* and (max-device-width: 640px)*/ -/* and (orientation: landscape){*/ -/* html{*/ -/* background-color: red;*/ - -/* }*/ -/*}*/ - -/*!* For Mobile Phones Portrait or Landscape View *!*/ -/*@media screen*/ -/* and (max-device-width: 640px){*/ -/* html{*/ -/* background-color: red;*/ - -/* }*/ -/*}*/ - @keyframes flutter { 0% { transform: skew(0, 0) scale(1); diff --git a/css/overlay.css b/css/overlay.css index 8294edca..485bcf3f 100644 --- a/css/overlay.css +++ b/css/overlay.css @@ -103,12 +103,9 @@ } .skip-button { - /*margin-top: var(--overlay-text-top);*/ pointer-events: auto; - /*touch-action: all;*/ width: auto; min-width: var(--button-width); - user-select: auto; position: absolute; left: 10px; diff --git a/js/koi/gui/cards/cardBook.js b/js/koi/gui/cards/cardBook.js index 5fcd6696..84d966e6 100644 --- a/js/koi/gui/cards/cardBook.js +++ b/js/koi/gui/cards/cardBook.js @@ -63,8 +63,8 @@ CardBook.Flip.prototype.SPEED = .3; CardBook.Flip.prototype.update = function() { this.flipPrevious = this.flip; - // if ((this.flip -= this.SPEED) < -1) - this.flip = -1; + if ((this.flip -= this.SPEED) < -1) + this.flip = -1; return this.flip === this.flipPrevious; }; @@ -284,23 +284,23 @@ CardBook.prototype.update = function() { * @param {Number} time The amount of time since the last update */ CardBook.prototype.renderFlips = function(time) { - // let index = this.flipDirection === -1 ? this.page + 1 : this.page; - // - // for (const flip of this.flips) { - // const flipAmount = flip.flipPrevious + (flip.flip - flip.flipPrevious) * time; - // - // if (!flip.halfway && flipAmount < 0) { - // this.pages[index].hide(); - // this.pages[index - this.flipDirection].show(this.cards); - // - // flip.halfway = true; - // } - // - // this.pages[index].setFlip(flipAmount); - // this.pages[index - this.flipDirection].setFlip(-flipAmount); - // - // index -= 2 * this.flipDirection; - // } + let index = this.flipDirection === -1 ? this.page + 1 : this.page; + + for (const flip of this.flips) { + const flipAmount = flip.flipPrevious + (flip.flip - flip.flipPrevious) * time; + + if (!flip.halfway && flipAmount < 0) { + this.pages[index].hide(); + this.pages[index - this.flipDirection].show(this.cards); + + flip.halfway = true; + } + + this.pages[index].setFlip(flipAmount); + this.pages[index - this.flipDirection].setFlip(-flipAmount); + + index -= 2 * this.flipDirection; + } }; /** @@ -434,7 +434,7 @@ CardBook.prototype.fit = function() { this.element.style.width = bookWidth * scale + "px"; this.element.style.height = this.height * this.HEIGHT * scale + "px"; this.element.style.left = (this.width - bookWidth * scale) * .5 + "px"; - // this.element.style.top = this.height * this.PADDING_TOP + "px"; + this.element.style.top = this.height * this.PADDING_TOP + "px"; this.spine.style.height = pageHeight * scale + "px"; for (const page of this.pages) diff --git a/js/koi/gui/cards/cardPage.js b/js/koi/gui/cards/cardPage.js index f53240a2..284b5034 100644 --- a/js/koi/gui/cards/cardPage.js +++ b/js/koi/gui/cards/cardPage.js @@ -9,7 +9,7 @@ const CardPage = function(direction, requirements) { this.cards = new Array(4).fill(null); this.element = this.createElement(direction); this.slots = this.createSlots(this.element); - // this.overlay = this.createOverlay(this.element, direction); + this.overlay = this.createOverlay(this.element, direction); this.rect = null; this.targets = null; this.direction = direction; @@ -101,19 +101,19 @@ CardPage.prototype.hide = function() { * @param {Number} flip The flip amount in the range [-1, 1] */ CardPage.prototype.setFlip = function(flip) { - // const scale = Math.sin(flip * Math.PI * .5); - // const skew = this.direction * this.PAGE_SKEW * (1 - Math.abs(flip)); + const scale = Math.sin(flip * Math.PI * .5); + const skew = this.direction * this.PAGE_SKEW * (1 - Math.abs(flip)); - // this.element.style.transform = "scaleX(" + scale.toString() + ") skewY(" + skew.toString() + "deg)"; - // this.overlay.style.opacity = ((1 - Math.abs(scale)) * this.SHADE_OPACITY).toString(); + this.element.style.transform = "scaleX(" + scale.toString() + ") skewY(" + skew.toString() + "deg)"; + this.overlay.style.opacity = ((1 - Math.abs(scale)) * this.SHADE_OPACITY).toString(); }; /** * Remove all flip effects */ CardPage.prototype.setNoFlip = function() { - // this.element.style.removeProperty("transform"); - // this.overlay.style.removeProperty("opacity"); + this.element.style.removeProperty("transform"); + this.overlay.style.removeProperty("opacity"); }; /** diff --git a/js/koi/gui/loader/loaderAndroid.js b/js/koi/gui/loader/loaderAndroid.js index fd191eda..6934ffd1 100644 --- a/js/koi/gui/loader/loaderAndroid.js +++ b/js/koi/gui/loader/loaderAndroid.js @@ -23,10 +23,6 @@ LoaderAndroid.prototype.loadSVG = function () { request.onload = () => { this.element.innerHTML = request.responseText; - // setTimeout(() => { - // this.element.classList.remove(this.CLASS_INVISIBLE); - // }, 1000 * this.FADE_IN_DELAY); - this.element.onclick = () => { if (window["require"]) { window["require"]("electron")["shell"]["openExternal"](this.URL); @@ -58,7 +54,6 @@ LoaderAndroid.prototype.createElement = function () { const element = document.createElement("div"); element.id = this.ID; - // element.className = this.CLASS_INVISIBLE; element.classList.add(this.CLASS); return element; diff --git a/js/koi/gui/loader/loaderApple.js b/js/koi/gui/loader/loaderApple.js index 25117ce3..d78c4e68 100644 --- a/js/koi/gui/loader/loaderApple.js +++ b/js/koi/gui/loader/loaderApple.js @@ -24,10 +24,6 @@ LoaderApple.prototype.loadSVG = function() { request.onload = () => { this.element.innerHTML = request.responseText; - // setTimeout(() => { - // this.element.classList.remove(this.CLASS_INVISIBLE); - // }, 1000 * this.FADE_IN_DELAY); - this.element.onclick = () => { if (window["require"]) { window["require"]("electron")["shell"]["openExternal"](this.URL); @@ -43,14 +39,6 @@ LoaderApple.prototype.loadSVG = function() { request.send(); } -// LoaderApple.prototype.setInvisible = function () { -// this.element.classList.add(this.CLASS_INVISIBLE); -// } -// -// LoaderApple.prototype.setVisible = function () { -// this.element.classList.remove(this.CLASS_INVISIBLE); -// } - /** * Create the element * @returns {HTMLDivElement} The element @@ -59,7 +47,6 @@ LoaderApple.prototype.createElement = function() { const element = document.createElement("div"); element.id = this.ID; - // element.className = this.CLASS_INVISIBLE; element.classList.add(this.CLASS); return element; diff --git a/js/koi/gui/loader/loaderLinks.js b/js/koi/gui/loader/loaderLinks.js index d0e446e5..f98982f5 100644 --- a/js/koi/gui/loader/loaderLinks.js +++ b/js/koi/gui/loader/loaderLinks.js @@ -18,15 +18,11 @@ const LoaderLinks = function() { this.element.appendChild(this.elementWebsite.element); this.element.appendChild(this.elementDiscord.element); - - // this.loadSVG(); }; LoaderLinks.prototype.ID = "loader-links"; LoaderLinks.prototype.CLASS_INVISIBLE = "invisible"; -// LoaderLinks.prototype.FILE = "svg/website.svg"; LoaderLinks.prototype.FADE_IN_DELAY = 2.5; -// LoaderLinks.prototype.URL = "https://koifarmgame.com"; /** * Load the SVG image @@ -64,7 +60,6 @@ LoaderLinks.prototype.createElement = function() { const element = document.createElement("div"); element.id = this.ID; - // element.className = this.CLASS_INVISIBLE; return element; }; diff --git a/js/koi/gui/loader/loaderLoadInfo.js b/js/koi/gui/loader/loaderLoadInfo.js index c959ce73..b5c65f2e 100644 --- a/js/koi/gui/loader/loaderLoadInfo.js +++ b/js/koi/gui/loader/loaderLoadInfo.js @@ -29,10 +29,6 @@ LoaderLoadInfo.prototype.loadSVG = function() { request.onload = () => { this.iconElement.innerHTML = request.responseText; - - // setTimeout(() => { - // this.element.classList.remove(this.CLASS_INVISIBLE); - // }, 1000 * this.FADE_IN_DELAY); }; request.open("GET", LoaderLoadInfo.prototype.FILE, true); diff --git a/js/koi/tutorial/tutorialCards.js b/js/koi/tutorial/tutorialCards.js index 410baa66..5d94a57c 100644 --- a/js/koi/tutorial/tutorialCards.js +++ b/js/koi/tutorial/tutorialCards.js @@ -135,15 +135,7 @@ TutorialCards.prototype.update = function(koi) { } switch (this.phase) { case this.PHASE_START: - // if (this.mutations < this.MUTATIONS_REQUIRED) - // this.phase = this.PHASE_WAITING; - // else if (this.mutations === this.MUTATIONS_REQUIRED) - this.start(); - // else { - // koi.gui.cards.enableBookButton(koi.audio); - // - // return true; - // } + this.start(); break; case this.PHASE_CREATE_CARD: diff --git a/js/main.js b/js/main.js index 774e39ee..3efa0d53 100644 --- a/js/main.js +++ b/js/main.js @@ -205,18 +205,6 @@ function setupPlatform (menu, save) { } ); } -}; - -function padNotch() { - if (!RUNNING_MOBILE) - return; - - const elements = [ - document.getElementById("menu"), - document.getElementById("loader"), - document.getElementById("gui"), - // document.getElementById("drop"), - ] } setFullScreen(); @@ -322,6 +310,7 @@ if (gl && /** * A function that creates a new game session * @param {number} index Create a new game at a given slot index + * @param {function} onFinish A function to call when the game has been loaded */ const newSession = (index, onFinish) => { chosenSlot = index; @@ -336,13 +325,13 @@ if (gl && koi = session.makeKoi(storage, systems, audio, gui, save, new TutorialBreeding(storage, gui.overlay)); onFinish(); - // loader.finish(); }; /** * Continue an existing game * @param {number} index Create a new game at a given slot index + * @param {function} onFinish A function to call when the game has been loaded */ const continueGame = (index, onFinish) => { chosenSlot = index; @@ -362,10 +351,6 @@ if (gl && }).catch(() => { newSession(index, onFinish); }); - - // session.deserialize(); - // - // koi = session.makeKoi(storage, systems, audio, gui, save); } catch (error) { newSession(index, onFinish); diff --git a/js/storage/storageFileCapacitor.js b/js/storage/storageFileCapacitor.js index 574ca6f9..1a0732cb 100644 --- a/js/storage/storageFileCapacitor.js +++ b/js/storage/storageFileCapacitor.js @@ -160,11 +160,6 @@ StorageFileCapacitor.prototype.loadImage = async function(name) { const files = await pickFile(); const e = new CustomEvent('loadImage'); - // - // const blobFiles = []; - // for (const f of files) { - // blobFiles.push(new File([f.data], {type: f.mimeType})); - // } e.dataTransfer = { files: files From 4ce785c7d6d3959129c31c19e22d761292e61a24 Mon Sep 17 00:00:00 2001 From: DanielsWrath Date: Mon, 6 May 2024 15:15:59 +0200 Subject: [PATCH 16/18] cleaned code --- js/koi/gui/loader/loader.js | 1 - js/koi/gui/loader/loaderLoadInfo.js | 2 -- js/koi/gui/overlay/overlay.js | 30 ----------------------------- js/koi/tutorial/tutorialCards.js | 1 - 4 files changed, 34 deletions(-) diff --git a/js/koi/gui/loader/loader.js b/js/koi/gui/loader/loader.js index 84d91303..894c2efd 100644 --- a/js/koi/gui/loader/loader.js +++ b/js/koi/gui/loader/loader.js @@ -38,7 +38,6 @@ const Loader = function( this.slots = null; - // element.appendChild(this.elementWebsite.element); element.appendChild(this.elementLinks.element); if (loadFullscreen) diff --git a/js/koi/gui/loader/loaderLoadInfo.js b/js/koi/gui/loader/loaderLoadInfo.js index b5c65f2e..149aaafe 100644 --- a/js/koi/gui/loader/loaderLoadInfo.js +++ b/js/koi/gui/loader/loaderLoadInfo.js @@ -58,8 +58,6 @@ LoaderLoadInfo.prototype.createElement = function() { this.iconElement.id = this.ICON_ID; this.textElement.id = this.TEXT_ID; - // this.textElement.innerText = ; - element.appendChild(this.iconElement); element.appendChild(this.textElement); diff --git a/js/koi/gui/overlay/overlay.js b/js/koi/gui/overlay/overlay.js index 5dd44449..a494d27f 100644 --- a/js/koi/gui/overlay/overlay.js +++ b/js/koi/gui/overlay/overlay.js @@ -94,32 +94,6 @@ Overlay.prototype.createHighlightElement = function() { element.className = this.CLASS_HIGHLIGHT; - const pathElement = document.createElement("svg"); - pathElement.setAttribute("viewBox", "0 0 24 24"); - pathElement.setAttribute("width", "24"); - pathElement.setAttribute("height", "24"); - // pathElement.setAttribute("fill", "none"); - pathElement.setAttribute("stroke", "currentColor"); - - const path = document.createElement("path"); - // path.setAttribute("stroke-linecap", "round"); - // path.setAttribute("stroke-linejoin", "round"); - // path.setAttribute("stroke-width", "2"); - path.setAttribute("d", "M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm-4.5 " + - "14c-.828 0-1.5-.672-1.5-1.5s.672-1.5 1.5-1.5 1.5.672 1.5 1.5-.672 1.5-1.5 1.5zm4.5 0c-.828 0-1.5-.672-1.5-1.5s.672-1.5 " + - "1.5-1.5 1.5.672 1.5 1.5-.672 1.5-1.5 1.5zm4.5 0c-.828 0-1.5-.672-1.5-1.5s.672-1.5 1.5-1.5 1.5.672 1.5 1.5-.672 1.5-1.5 1.5z"); - - pathElement.appendChild(path); - element.appendChild(pathElement); - - element.addEventListener("click", function() { - menu.toggle(); - }); - - element.addEventListener("touchstart", function() { - menu.toggle(); - }); - return element; }; @@ -246,9 +220,6 @@ Overlay.prototype.createSkip = function(callback) { callback(); } - // this.skipElement.addEventListener("click", () => callback); - // this.skipElement.addEventListener("touchstart", () => callback); - this.element.appendChild(this.skipElement); } @@ -261,7 +232,6 @@ Overlay.prototype.deleteSkip = function() { } Overlay.prototype.clear = function() { - // this.deleteHome(); this.deletePointer(); this.deleteArrow(); this.removeText(); diff --git a/js/koi/tutorial/tutorialCards.js b/js/koi/tutorial/tutorialCards.js index 5d94a57c..9470c3be 100644 --- a/js/koi/tutorial/tutorialCards.js +++ b/js/koi/tutorial/tutorialCards.js @@ -5,7 +5,6 @@ * @constructor */ const TutorialCards = function(storage, overlay) { - // TODO: do somthing with this storage Tutorial.call(this, storage, overlay); this.mutations = 0; From 0a66c957b077b94d42bac8f588f85c044e0cf93d Mon Sep 17 00:00:00 2001 From: DanielsWrath Date: Mon, 6 May 2024 15:18:24 +0200 Subject: [PATCH 17/18] deleted unused class --- js/storage/storageCapacitor.js | 103 --------------------------------- 1 file changed, 103 deletions(-) delete mode 100644 js/storage/storageCapacitor.js diff --git a/js/storage/storageCapacitor.js b/js/storage/storageCapacitor.js deleted file mode 100644 index 49016735..00000000 --- a/js/storage/storageCapacitor.js +++ /dev/null @@ -1,103 +0,0 @@ -/** - * A storage system using files - * @constructor - */ -const StorageCapacitor = function() { - StorageSystem.call(this); -}; - -StorageCapacitor.prototype = Object.create(StorageSystem.prototype); -StorageCapacitor.prototype.EXTENSION = ".sav"; -StorageCapacitor.prototype.DIRECTORY = "save/"; - -if (window["require"]) { - const electron = window["require"]("electron"); - const url = window["require"]("url"); - const fs = window["require"]("fs"); - const os = window["require"]("os"); - let directory = StorageCapacitor.prototype.DIRECTORY; - - if (os["platform"]() === "darwin") - directory = os["homedir"]() + "/Library/Application Support/koifarm/" + directory; - - const makeDirectory = () => { - if (!fs["existsSync"](directory)) - fs["mkdirSync"](directory); - }; - - const fileExists = name => { - return fs["existsSync"](name); - }; - - const pngFilter = { - "name": "PNG Image", - "extensions": ["png"] - }; - - StorageCapacitor.prototype.set = function (key, value) { - makeDirectory(); - - fs["writeFileSync"](url["pathToFileURL"](directory + key + this.EXTENSION), value); - }; - - StorageCapacitor.prototype.setBuffer = function(key, value) { - makeDirectory(); - - fs["writeFileSync"](url["pathToFileURL"](directory + key + this.EXTENSION), value.toByteArray()); - }; - - StorageCapacitor.prototype.get = function(key) { - const file = directory + key + this.EXTENSION; - let contents = null; - - try { - if (fileExists(file)) - contents = fs["readFileSync"](file, "utf8"); - } - catch (error) { - - } - - return contents; - }; - - StorageCapacitor.prototype.getBuffer = function(key) { - const file = directory + key + this.EXTENSION; - let contents = null; - - try { - if (fileExists(file)) - contents = fs["readFileSync"](file); - } - catch (error) { - - } - - return contents ? new BinBuffer(contents) : null; - }; - - StorageCapacitor.prototype.remove = function(key) { - const file = directory + key + this.EXTENSION; - - if (fileExists(file)) - fs["unlinkSync"](url["pathToFileURL"](file)); - }; - - StorageCapacitor.prototype.imageToFile = function(blob, name) { - electron["remote"]["dialog"]["showSaveDialog"]( - null, - { - filters: [pngFilter] - }).then(result => { - if (!result["canceled"]) { - const reader = new FileReader(); - - reader.addEventListener("loadend", () => { - fs["writeFileSync"](url["pathToFileURL"](result["filePath"]), new Uint8Array(reader.result)); - }); - - reader.readAsArrayBuffer(blob); - } - }); - }; -} \ No newline at end of file From 92037dbc83fd6ce65f3438257f1c36b15a31bc57 Mon Sep 17 00:00:00 2001 From: Job Talle Date: Mon, 2 Feb 2026 20:39:02 +0100 Subject: [PATCH 18/18] Updated translations --- KoiTranslations | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KoiTranslations b/KoiTranslations index 70994e10..0004e2dc 160000 --- a/KoiTranslations +++ b/KoiTranslations @@ -1 +1 @@ -Subproject commit 70994e105bb43a460af7f89ec268c68a2aa4b6a7 +Subproject commit 0004e2dc1afffcfcd332b11c9fa3ba96df6e5adf