From 132b3d8a305bdca529f07cd4c4c5b1a01cf17ac2 Mon Sep 17 00:00:00 2001 From: L3P3 Date: Sun, 4 Aug 2024 16:39:05 +0200 Subject: [PATCH] #29 texture selection --- app-dev.html | 3 - app-prod.html | 3 - assets/blocks.webp | Bin 5948 -> 0 bytes build.js | 8 +-- locales/de.csv | 2 +- locales/en.csv | 2 +- locales/ru.csv | 2 +- locales/tr.csv | 2 +- locales/uk.csv | 4 +- package.json | 2 +- src/app.css | 5 +- src/app.js | 4 ++ src/etc/constants.js | 5 +- src/etc/env.js | 2 +- src/etc/helpers.js | 3 + src/etc/locale.js | 2 +- src/etc/state.js | 26 ++++--- src/etc/textures.js | 31 +++++++- src/externs.js | 14 ++-- src/game/c_app.js | 14 +++- src/game/c_bar.js | 7 +- src/game/c_game.js | 5 ++ src/game/c_inventory.js | 13 ++-- src/game/c_menu_start.js | 50 +++++++------ src/game/c_settings.js | 136 ++++++++++++++++++++++++++---------- src/game/c_stack.js | 7 +- src/game/m_game.js | 8 ++- src/game/m_renderer.js | 80 ++++++++++++--------- tools/textures/blocks.json | 32 --------- tools/textures/blocks.png | Bin 9043 -> 0 bytes tools/textures/package.json | 19 ----- tools/textures/packer.js | 97 ------------------------- tools/textures/textures.js | 32 --------- 33 files changed, 296 insertions(+), 324 deletions(-) delete mode 100644 assets/blocks.webp mode change 120000 => 100644 src/etc/textures.js delete mode 100644 tools/textures/blocks.json delete mode 100644 tools/textures/blocks.png delete mode 100644 tools/textures/package.json delete mode 100644 tools/textures/packer.js delete mode 100644 tools/textures/textures.js diff --git a/app-dev.html b/app-dev.html index 529750d..155fc30 100755 --- a/app-dev.html +++ b/app-dev.html @@ -10,8 +10,5 @@ - diff --git a/app-prod.html b/app-prod.html index c46e5cc..5171e85 100755 --- a/app-prod.html +++ b/app-prod.html @@ -10,8 +10,5 @@ - diff --git a/assets/blocks.webp b/assets/blocks.webp deleted file mode 100644 index c5ecda34d0c40836ad033528eda3359c40fa5b84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5948 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 Promise.all([ +const env_set = async (version, debug, lang, api, api_data) => Promise.all([ writeFile( './src/etc/env.js', `export const VERSION = '${version}'; export const DEBUG = ${debug}; export const LANG = '${lang}'; export const API = '${api}'; -export const API_DOWNLOAD = '${api_download}'; +export const API_DATA = '${api_data}'; `, 'utf8' ), @@ -92,7 +92,7 @@ async function build_js(lang) { false, lang, prod ? '/api/minicraft/' : '//l3p3.de/api/minicraft/', - prod ? '/static/minicraft/worlds/' : '//l3p3.de/static/minicraft/worlds/' + prod ? '/static/minicraft/' : '//l3p3.de/static/minicraft/' ); console.log('js pass 1...'); @@ -170,7 +170,7 @@ try { for (const lang of languages) await build_js(lang); } finally { - await env_set('dev', true, 'en', '//l3p3.de/api/minicraft/', '//l3p3.de/static/minicraft/worlds/'); + await env_set('dev', true, 'en', '//l3p3.de/api/minicraft/', '//l3p3.de/static/minicraft/'); } console.log('done.'); diff --git a/locales/de.csv b/locales/de.csv index 9d97735..220182e 100644 --- a/locales/de.csv +++ b/locales/de.csv @@ -1,6 +1,7 @@ amount: Anzahl ask_world_delete_1: Welt " ask_world_delete_2: " wirklich löschen? +back: Zurück back_to_game: Zurück zum Spiel change_world_name: Welt-Namen ändern chunk_regenerated: Chunk neu generiert. @@ -78,7 +79,6 @@ settings: Einstellungen show_world_settings: Aktionen/Einstellungen zur Welt anzeigen spawn_updated: Startpunkt aktualisiert. surfaces_colored: Einfarbig -surfaces_textured: Texturiert surfaces: Oberflächen teleported_to_spawn: Zum Startpunkt teleportiert. teleported_to: Teleportiert zu diff --git a/locales/en.csv b/locales/en.csv index 2458f82..ff6cd18 100644 --- a/locales/en.csv +++ b/locales/en.csv @@ -1,6 +1,7 @@ amount: Amount ask_world_delete_1: Really delete world " ask_world_delete_2: "? +back: Back back_to_game: Back to game change_world_name: Change world name chunk_regenerated: Chunk regenerated. @@ -78,7 +79,6 @@ settings: Settings show_world_settings: Show world settings spawn_updated: Spawn updated. surfaces_colored: Plain -surfaces_textured: Textured surfaces: Surfaces teleported_to_spawn: Teleported to spawn. teleported_to: Teleported to diff --git a/locales/ru.csv b/locales/ru.csv index 3e7e989..b29c711 100644 --- a/locales/ru.csv +++ b/locales/ru.csv @@ -1,6 +1,7 @@ amount: Число ask_world_delete_1: Мир " ask_world_delete_2: " точно удалить? +back: Вернуться back_to_game: Вернуться в игру change_world_name: Изменит имя миру chunk_regenerated: Чанки перезагружается @@ -78,7 +79,6 @@ settings: Настройки show_world_settings: Акции/Показать настройки мира spawn_updated: Началная точка обновлена surfaces_colored: Одноцветный -surfaces_textured: С текстурами surfaces: Поверхность teleported_to_spawn: Переместится к стартовой точки teleported_to: Переместится к diff --git a/locales/tr.csv b/locales/tr.csv index 1f5f6d7..e76193a 100644 --- a/locales/tr.csv +++ b/locales/tr.csv @@ -1,6 +1,7 @@ amount: Miktar ask_world_delete_1: " ask_world_delete_2: " isimli dünya gerçekten silinsin mi? +back: Geri back_to_game: Oyuna geri dön change_world_name: Dünyayı değiştir chunk_regenerated: Parça yenilendi. @@ -78,7 +79,6 @@ settings: Ayarlar show_world_settings: Dünya ayarlarını göster spawn_updated: Spawn güncellendi. surfaces_colored: Sade -surfaces_textured: Dokulu surfaces: Yüzeyler teleported_to_spawn: Spawn\'a ışınlandı. teleported_to: Şuraya ışınlandı: diff --git a/locales/uk.csv b/locales/uk.csv index 0a17e8f..ab9d074 100644 --- a/locales/uk.csv +++ b/locales/uk.csv @@ -1,6 +1,7 @@ amount: Номер ask_world_delete_1: Світ " ask_world_delete_2: " дійсно видалити? +back: Повернуться back_to_game: Повернуться в гру change_world_name: Змінити ім\'я світу chunk_regenerated: Участки відновлені. @@ -77,8 +78,7 @@ selection_secondary: Друга крапка вибору settings: Налаштування show_world_settings: Акції/Показати налаштування світу. spawn_updated: Початкова крапка оновлена. -surfaces_colored: Однотонні -surfaces_textured: Текстурований +surfaces_colored: Однотонні surfaces: Поверхности teleported_to_spawn: Перемістився до початкової. teleported_to: Переміститися до diff --git a/package.json b/package.json index 173150a..6fb9799 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "minicraft", - "version": "0.10.7", + "version": "0.11.0", "description": "voxel-based 3d game, written in javascript", "homepage": "https://l3p3.de/minicraft", "repository": { diff --git a/src/app.css b/src/app.css index 6ea4ed1..4a1ef40 100644 --- a/src/app.css +++ b/src/app.css @@ -82,7 +82,6 @@ button { } .game .bitmap { background-size: 100% auto; - transform: scaleY(-1); } .game > .diagnostics { position: absolute; @@ -266,9 +265,11 @@ button { margin: 1rem; display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); - grid-template-rows: 3rem; gap: 1rem; } +.game .menu > .settings > * { + min-height: 2rem; +} .game .menu.inventory, .game .menu.advanced { display: flex; align-items: center; diff --git a/src/app.js b/src/app.js index 744f1b1..ccd5d36 100755 --- a/src/app.js +++ b/src/app.js @@ -38,6 +38,9 @@ import { game_key, game_save, } from './game/m_game.js'; +import { + tiles_set, +} from './game/m_renderer.js'; import App from './game/c_app.js'; @@ -121,6 +124,7 @@ function Root() { addEventListener_('touchstart', handler_touch, true); } }, [flag_touch]); + hook_effect(tiles_set, [state.config.textures]); hook_dom('', { onkeydown: handler_key, diff --git a/src/etc/constants.js b/src/etc/constants.js index da3c872..1856714 100644 --- a/src/etc/constants.js +++ b/src/etc/constants.js @@ -5,8 +5,9 @@ import { locale_item_labels, } from './locale.js'; -export const APP_VIEW_WORLDS = 0; -export const APP_VIEW_GAME = 1; +export const APP_VIEW_GAME = 0; +export const APP_VIEW_SETTINGS = 1; +export const APP_VIEW_WORLDS = 2; export const CHUNK_WIDTH_L2 = 4; export const CHUNK_WIDTH = 1 << CHUNK_WIDTH_L2; diff --git a/src/etc/env.js b/src/etc/env.js index fa95b70..3b06346 100644 --- a/src/etc/env.js +++ b/src/etc/env.js @@ -2,4 +2,4 @@ export const VERSION = 'dev'; export const DEBUG = true; export const LANG = 'en'; export const API = '//l3p3.de/api/minicraft/'; -export const API_DOWNLOAD = '//l3p3.de/static/minicraft/worlds/'; +export const API_DATA = '//l3p3.de/static/minicraft/'; diff --git a/src/etc/helpers.js b/src/etc/helpers.js index 792f2a4..495e0df 100644 --- a/src/etc/helpers.js +++ b/src/etc/helpers.js @@ -33,8 +33,11 @@ export const localStorage_getItem = key => localStorage_.getItem(key); export const localStorage_setItem = localStorage_.setItem.bind(localStorage_); export const localStorage_removeItem = localStorage_.removeItem.bind(localStorage_); export const indexedDB_ = window_.indexedDB; +export const fetch_ = fetch; +export const Error_ = msg => new Error(msg); export const Uint8Array_ = Uint8Array; export const Uint32Array_ = Uint32Array; +export const Set_ = Set; export const Map_ = Map; export const Number_ = Number; export const Object_ = Object; diff --git a/src/etc/locale.js b/src/etc/locale.js index 9fe84b2..0643189 100644 --- a/src/etc/locale.js +++ b/src/etc/locale.js @@ -1,6 +1,7 @@ export const locale_amount = 'Amount'; export const locale_ask_world_delete_1 = 'Really delete world "'; export const locale_ask_world_delete_2 = '"?'; +export const locale_back = 'Back'; export const locale_back_to_game = 'Back to game'; export const locale_change_world_name = 'Change world name'; export const locale_chunk_regenerated = 'Chunk regenerated.'; @@ -78,7 +79,6 @@ export const locale_settings = 'Settings'; export const locale_show_world_settings = 'Show world settings'; export const locale_spawn_updated = 'Spawn updated.'; export const locale_surfaces_colored = 'Plain'; -export const locale_surfaces_textured = 'Textured'; export const locale_surfaces = 'Surfaces'; export const locale_teleported_to_spawn = 'Teleported to spawn.'; export const locale_teleported_to = 'Teleported to'; diff --git a/src/etc/state.js b/src/etc/state.js index aa617f6..9633079 100644 --- a/src/etc/state.js +++ b/src/etc/state.js @@ -6,6 +6,7 @@ import { JSON_parse, JSON_stringify, Object_keys, + Set_, localStorage_, localStorage_getItem, localStorage_removeItem, @@ -25,7 +26,7 @@ if ( config_loaded['worlds'] && config_loaded['version'].startsWith('0.9.') ) { - const prefixes_keep = new Set( + const prefixes_keep = new Set_( config_loaded['worlds'] .map(world => 'minicraft.world.' + world.id) ); @@ -42,11 +43,11 @@ export const reducers = { init: () => { let needs_save = false; const config = { - flag_textures: true, flag_touch: false, // not saved pixel_grouping: 1, mouse_sensitivity: 3, resolution_scaling: 4, + textures: 1, view_angle: 120, view_distance: 64, world_last: 0, @@ -54,13 +55,8 @@ export const reducers = { worlds: [], }; if (config_loaded) { - let tmp = config_loaded['flag_textures']; + let tmp = config_loaded['pixel_grouping']; if (tmp != null) { - config.flag_textures = tmp; - } - if (( - tmp = config_loaded['pixel_grouping'] - ) != null) { config.pixel_grouping = tmp; } if (( @@ -69,6 +65,18 @@ export const reducers = { config.mouse_sensitivity = tmp; } config.resolution_scaling = config_loaded['resolution_scaling']; + if (( + tmp = config_loaded['textures'] + ) != null) { + config.textures = tmp; + } + else { + config.textures = ( + config_loaded['flag_textures'] + ? 1 + : 0 + ); + } config.view_angle = config_loaded['view_angle']; config.view_distance = config_loaded['view_distance']; if (( @@ -117,10 +125,10 @@ export const reducers = { if (config === state.config_saved) return state; localStorage_setItem('minicraft.config', JSON_stringify({ 'version': VERSION, - 'flag_textures': config.flag_textures, 'pixel_grouping': config.pixel_grouping, 'mouse_sensitivity': config.mouse_sensitivity, 'resolution_scaling': config.resolution_scaling, + 'textures': config.textures, 'view_angle': config.view_angle, 'view_distance': config.view_distance, 'world_last': config.world_last, diff --git a/src/etc/textures.js b/src/etc/textures.js deleted file mode 120000 index 2cecc10..0000000 --- a/src/etc/textures.js +++ /dev/null @@ -1 +0,0 @@ -../../tools/textures/textures.js \ No newline at end of file diff --git a/src/etc/textures.js b/src/etc/textures.js new file mode 100644 index 0000000..2d10f04 --- /dev/null +++ b/src/etc/textures.js @@ -0,0 +1,30 @@ +export const TILES_COUNT = 26; +export const TILES_RESOLUTION_LOG2 = 4; +export const TILES_RESOLUTION = 1 << TILES_RESOLUTION_LOG2; + +export const TILE_STONE = 0; +export const TILE_GRASS_TOP = 1; +export const TILE_DIRT = 2; +export const TILE_COBBLE = 3; +export const TILE_PLANKS = 4; +export const TILE_BEDROCK = 5; +export const TILE_LOG_SIDE = 6; +export const TILE_LEAVES = 7; +export const TILE_BRICKS = 8; +export const TILE_WOOL = 9; +export const TILE_SAND = 10; +export const TILE_GRAVEL = 11; +export const TILE_GLASS = 12; +export const TILE_BOOKSHELF = 13; +export const TILE_OBSIDIAN = 14; +export const TILE_STONE_BRICKS = 15; +export const TILE_SANDSTONE = 16; +export const TILE_LAPIS_BLOCK = 17; +export const TILE_IRON_BLOCK = 18; +export const TILE_GOLD_BLOCK = 19; +export const TILE_DIAMOND_BLOCK = 20; +export const TILE_EMERALD_BLOCK = 21; +export const TILE_REDSTONE_BLOCK = 22; +export const TILE_QUARTZ_BLOCK = 23; +export const TILE_GRASS_SIDE = 24; +export const TILE_LOG_TOP = 25; diff --git a/src/externs.js b/src/externs.js index 58f296e..e583693 100644 --- a/src/externs.js +++ b/src/externs.js @@ -262,11 +262,6 @@ var onmousedown; */ var onmouseup; -/** - @type {string} -*/ -var ASSETS; - /** @type {boolean} */ @@ -327,6 +322,15 @@ var TYPE_WORLD_API; */ var TYPE_CHAT_API; +/** + @typedef {{ + id: number, + label: string, + owner: string, + }} +*/ +var TYPE_TEXTURES_ITEM; + /** @typedef {{ account: { diff --git a/src/game/c_app.js b/src/game/c_app.js index e80126c..3b49caa 100644 --- a/src/game/c_app.js +++ b/src/game/c_app.js @@ -6,11 +6,13 @@ import { import { APP_VIEW_GAME, + APP_VIEW_SETTINGS, APP_VIEW_WORLDS, } from '../etc/constants.js'; import Game from './c_game.js'; import MenuStart from './c_menu_start.js'; +import Settings from './c_settings.js'; export default function App({ account, @@ -23,13 +25,23 @@ export default function App({ const frame = hook_dom('div[className=game]'); return [ - view === APP_VIEW_WORLDS && + ( + view === APP_VIEW_WORLDS || + view === APP_VIEW_SETTINGS + ) && node(MenuStart, { account, actions, config, view_set, }), + view === APP_VIEW_SETTINGS && + node(Settings, { + actions, + config, + game: null, + view_set, + }), view === APP_VIEW_GAME && node(Game, { account, diff --git a/src/game/c_bar.js b/src/game/c_bar.js index 10e9eb5..4931ec1 100644 --- a/src/game/c_bar.js +++ b/src/game/c_bar.js @@ -13,14 +13,11 @@ import { Math_min, } from '../etc/helpers.js'; -import { - tiles_data, -} from './m_renderer.js'; - import Stack from './c_stack.js'; export default function Bar({ player, + textures_id, time_now, }) { hook_dom('div[className=bar]', { @@ -44,7 +41,6 @@ export default function Bar({ const {gamemode} = player; return ( - tiles_data && player.inventory .slice(0, PLAYER_SLOTS) .map(({content}, index) => ( @@ -62,6 +58,7 @@ export default function Bar({ data: content.data, gamemode, id: content.id, + textures_id, }), ]) )) diff --git a/src/game/c_game.js b/src/game/c_game.js index 0c21c6d..0d32011 100644 --- a/src/game/c_game.js +++ b/src/game/c_game.js @@ -187,6 +187,9 @@ export default function Game({ hook_rerender(); + const textures_id_ref = hook_static({val: 1}); + if (config.textures) textures_id_ref.val = config.textures; + return [ node_dom('canvas', { R: hook_static(canvas_element => ( @@ -214,11 +217,13 @@ export default function Game({ model.player.gamemode !== GAMEMODE_SPECTATOR && node(Bar, { player: model.player, + textures_id: textures_id_ref.val, time_now, }), model.menu === MENU_INVENTORY && node(Inventory, { game: model, + textures_id: textures_id_ref.val, time_now, }), model.menu === MENU_SETTINGS && diff --git a/src/game/c_inventory.js b/src/game/c_inventory.js index 47481d4..658f37b 100644 --- a/src/game/c_inventory.js +++ b/src/game/c_inventory.js @@ -22,9 +22,6 @@ import { import { game_menu_close, } from './m_game.js'; -import { - tiles_data, -} from './m_renderer.js'; import { slot_create, slot_transfer, @@ -37,6 +34,7 @@ import Stack from './c_stack.js'; function Palette({ slot_hand, + textures_id, }) { hook_dom('div[className=grid]', { onclick: ({ @@ -69,6 +67,7 @@ function Palette({ data: null, gamemode: GAMEMODE_CREATIVE, id, + textures_id, }), ]) )) @@ -77,10 +76,11 @@ function Palette({ export default function Inventory({ game, + textures_id, }) { const slot_hand = hook_memo(() => slot_create(null)); - const gamemode = game.player.gamemode; + const {gamemode} = game.player; hook_dom('div[className=menu overlay inventory]', hook_memo(() => ({ onclick: ({ @@ -137,12 +137,13 @@ export default function Inventory({ }, }))); - return tiles_data && [ + return [ node_dom('div[className=window]', null, [ node_dom(`h2[innerText=${locale_inventory}]`), gamemode === GAMEMODE_CREATIVE && node(Palette, { slot_hand, + textures_id, }), node_dom('div[className=grid]', null, game.player.inventory.map(({content}, index) => @@ -160,6 +161,7 @@ export default function Inventory({ data: content.data, gamemode, id: content.id, + textures_id, }), ]) ) @@ -176,6 +178,7 @@ export default function Inventory({ data: slot_hand.content.data, gamemode: GAMEMODE_SURVIVAL, id: slot_hand.content.id, + textures_id, }), ]), ]; diff --git a/src/game/c_menu_start.js b/src/game/c_menu_start.js index 524ca6c..0086d24 100644 --- a/src/game/c_menu_start.js +++ b/src/game/c_menu_start.js @@ -13,19 +13,22 @@ import { import { APP_VIEW_GAME, + APP_VIEW_SETTINGS, } from '../etc/constants.js'; import { API, - API_DOWNLOAD, + API_DATA, VERSION, } from '../etc/env.js'; import { Date_now, + Error_, JSON_stringify, Math_max, Math_min, Promise_, datify, + fetch_, } from '../etc/helpers.js'; import { locale_ask_world_delete_1, @@ -67,12 +70,12 @@ import { locale_only_local, locale_open, locale_owner, - locale_project_page, locale_public, locale_publish_world, locale_refresh, locale_reload_list, locale_rename, + locale_settings, locale_show_world_settings, locale_transfer, locale_unpublish_world, @@ -146,6 +149,11 @@ function WorldItem({ ]; } +const headers_json_post = { + method: 'POST', + headers: {'Content-Type': 'application/json'}, +}; + export default function MenuStart({ account, actions, @@ -173,8 +181,8 @@ export default function MenuStart({ async () => { try { const initial = !world_list_remote_ref.value && !refreshes; - const response = await fetch(API + `world?what=${initial ? 'initial' : 'meta_all'}`); - if (!response.ok) throw new Error(locale_error_connection); + const response = await fetch_(`${API}world?what=${initial ? 'initial' : 'meta_all'}`); + if (!response.ok) throw Error_(locale_error_connection); const json = await response.json(); if (!initial) return /** @type {!Array} */ (json); const json_initial = /** @type {TYPE_RESPONSE_INITIAL} */ (json); @@ -307,10 +315,9 @@ export default function MenuStart({ let cancelled = false; const world_busy = world_list.find(world => world.id === world_busy_id); - const prefix = `minicraft.world.${world_busy_id}:`; if (world_busy.local < world_busy.remote) { // download - fetch(API_DOWNLOAD + `${world_busy.hash}.json`) + fetch_(`${API_DATA}worlds/${world_busy.hash}.json`) .then(response => response.json()) .then(json => { if (cancelled) return; @@ -342,21 +349,19 @@ export default function MenuStart({ }); return; } - const prefix_length = prefix.length; let id_new = world_busy_id; ( // register new world? world_busy.remote === 1 - ? fetch(API + 'world', { - method: 'POST', - headers: {'Content-Type': 'application/json'}, + ? fetch_(API + 'world', { + ...headers_json_post, body: JSON_stringify({ what: 'meta', label: world_busy.label, }), }) .then(response => { - if (!response.ok) throw new Error( + if (!response.ok) throw Error_( response.status === 403 ? locale_error_no_permission_logged_in : locale_error_connection @@ -374,9 +379,8 @@ export default function MenuStart({ }) .then(json => { if (cancelled) throw null; - return fetch(API + 'world', { - method: 'POST', - headers: {'Content-Type': 'application/json'}, + return fetch_(API + 'world', { + ...headers_json_post, body: JSON_stringify({ what: 'data', world: id_new, @@ -385,7 +389,7 @@ export default function MenuStart({ }); }) .then(response => { - if (!response.ok) throw new Error( + if (!response.ok) throw Error_( response.status === 403 ? locale_error_no_permission_logged_in : locale_error_connection @@ -519,9 +523,9 @@ export default function MenuStart({ }); } }), - node_dom(`button[innerText=${locale_project_page}]`, { + node_dom(`button[innerText=${locale_settings}]`, { onclick: () => { - open('//github.com/L3P3/minicraft'); + view_set(APP_VIEW_SETTINGS); }, }), ]), @@ -584,7 +588,7 @@ export default function MenuStart({ } if (world_selected.remote) { busy_set(true); - fetch(API + 'world', { + fetch_(API + 'world', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON_stringify({ @@ -594,7 +598,7 @@ export default function MenuStart({ }), }) .then(response => { - if (!response.ok) throw new Error( + if (!response.ok) throw Error_( response.status === 403 ? locale_error_no_permission_logged_in : locale_error_connection @@ -636,7 +640,7 @@ export default function MenuStart({ } else { busy_set(true); - fetch(API + 'world', { + fetch_(API + 'world', { method: 'DELETE', headers: {'Content-Type': 'application/json'}, body: JSON_stringify({ @@ -645,7 +649,7 @@ export default function MenuStart({ }), }) .then(response => { - if (!response.ok) throw new Error( + if (!response.ok) throw Error_( response.status === 403 ? locale_error_no_permission_logged_in : locale_error_connection @@ -686,7 +690,7 @@ export default function MenuStart({ }`, onclick: () => { busy_set(true); - fetch(API + 'world', { + fetch_(API + 'world', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON_stringify({ @@ -696,7 +700,7 @@ export default function MenuStart({ }), }) .then(response => { - if (!response.ok) throw new Error( + if (!response.ok) throw Error_( response.status === 403 ? locale_error_no_permission_logged_in : locale_error_connection diff --git a/src/game/c_settings.js b/src/game/c_settings.js index e1325b7..ed305f8 100644 --- a/src/game/c_settings.js +++ b/src/game/c_settings.js @@ -1,21 +1,31 @@ import { + hook_async, hook_dom, hook_effect, - hook_static, + hook_state, node_dom, + node_map, } from '../etc/lui.js'; import { APP_VIEW_WORLDS, } from '../etc/constants.js'; import { + Promise_, + fetch_, +} from '../etc/helpers.js'; +import { + API_DATA, +} from '../etc/env.js'; +import { + locale_back, locale_back_to_game, locale_mouse_sensitivity, locale_pixel_grouping, + locale_project_page, locale_resolution, locale_settings, locale_surfaces_colored, - locale_surfaces_textured, locale_surfaces, locale_view_angle, locale_view_distance, @@ -27,103 +37,159 @@ import { game_save, } from './m_game.js'; +function TextureItem({ + /** TYPE_TEXTURES_ITEM */ I, + config_set, + current, +}) { + hook_dom('button', { + disabled: I.id === current, + innerText: `${I.label} (${I.owner})`, + onclick: () => { + config_set({ + textures: I.id, + }); + }, + }); + return null; +} + export default function Settings({ actions: { - config_reduce, config_set, }, config, game, view_set, }) { - hook_effect(() => ( + game && hook_effect(() => ( game_save(game) )); + + const [textures_opened, textures_opened_set] = hook_state(false); + const textures = hook_async(() => ( + !textures_opened ? Promise_.resolve(null) : + fetch_(`${API_DATA}textures.json`) + .then(response => ( + response.ok + ? response.json() + : null + )) + .catch(e => null) + ), [textures_opened], null); hook_dom('div[className=menu overlay]'); return [ - node_dom(`h1[innerText=${locale_settings}]`), - hook_static(node_dom('center', null, [ - node_dom(`button[innerText=${locale_back_to_game}]`, { + node_dom('h1', { + innerText: ( + textures_opened + ? locale_surfaces + : locale_settings + ), + }), + node_dom('center', null, [ + node_dom('button', { + innerText: ( + game && !textures_opened + ? locale_back_to_game + : locale_back + ), onclick: () => { - game_menu_close(game); + if (textures_opened) textures_opened_set(false); + else if (game) game_menu_close(game); + else view_set(APP_VIEW_WORLDS); }, }), - ])), + ]), + !textures_opened && node_dom('div[className=settings]', null, [ - node_dom('button', { - innerText: ( - locale_surfaces + ':\n' + - ( - config.flag_textures - ? locale_surfaces_textured - : locale_surfaces_colored - ) - ), - onclick: hook_static(() => ( - config_reduce(config => ({ - flag_textures: !config.flag_textures, - })) - )), + node_dom(`button[innerText=${locale_surfaces}...]`, { + onclick: () => { + textures_opened_set(true); + }, }), node_dom(`label[innerText=${locale_resolution}:]`, null, [ node_dom('input[type=range][min=1][max=100][step=1]', { value: 101 - config.resolution_scaling, - onchange: hook_static(event => ( + onchange: event => ( config_set({ resolution_scaling: 101 - Number(event.target.value), }) - )), + ), }), ]), node_dom(`label[innerText=${locale_view_angle}:]`, null, [ node_dom('input[type=range][min=1][max=180][step=1]', { value: config.view_angle, - onchange: hook_static(event => ( + onchange: event => ( config_set({ view_angle: Number(event.target.value), }) - )), + ), }), ]), node_dom(`label[innerText=${locale_view_distance}:]`, null, [ node_dom('input[type=range][min=1][max=128][step=1]', { value: config.view_distance, - onchange: hook_static(event => ( + onchange: event => ( config_set({ view_distance: Number(event.target.value), }) - )), + ), }), ]), node_dom(`label[innerText=${locale_pixel_grouping}:]`, null, [ node_dom('input[type=range][min=1][max=6][step=1]', { value: config.pixel_grouping, - onchange: hook_static(event => ( + onchange: event => ( config_set({ pixel_grouping: Number(event.target.value), }) - )), + ), }), ]), node_dom(`label[innerText=${locale_mouse_sensitivity}:]`, null, [ node_dom('input[type=range][min=1][max=15][step=1]', { value: config.mouse_sensitivity, - onchange: hook_static(event => ( + onchange: event => ( config_set({ mouse_sensitivity: Number(event.target.value), }) - )), + ), }), ]), + node_dom(`button[innerText=${locale_project_page}]`, { + onclick: () => { + open('//github.com/L3P3/minicraft'); + }, + }), ]), - hook_static(node_dom('center', null, [ + game && + !textures_opened && + node_dom('center', null, [ node_dom(`button[innerText=${locale_world_leave}]`, { onclick: () => { view_set(APP_VIEW_WORLDS); }, }), - ])), + ]), + + textures_opened && + node_dom('div[className=settings]', null, [ + node_dom(`button[innerText=${locale_surfaces_colored}]`, { + disabled: config.textures === 0, + onclick: () => { + config_set({ + textures: 0, + }); + }, + }), + textures && + node_map(TextureItem, textures, { + config_set, + current: config.textures, + }), + ]), ]; } diff --git a/src/game/c_stack.js b/src/game/c_stack.js index 7c425bb..ebc0f90 100644 --- a/src/game/c_stack.js +++ b/src/game/c_stack.js @@ -10,14 +10,17 @@ import { ITEM_LABELS, } from '../etc/constants.js'; import { + API_DATA, LANG, } from '../etc/env.js'; const Bitmap = ({ id, + textures_id, }) => ( - hook_dom('#tile', { + hook_dom('div[className=bitmap]', { S: { + backgroundImage: `url(${API_DATA}textures/${textures_id}.png)`, backgroundPositionY: `-${(id - 1) * 2}rem`, }, }), @@ -29,6 +32,7 @@ export default function Stack({ data, gamemode, id, + textures_id, }) { hook_dom('div[className=stack]', { title: ( @@ -41,6 +45,7 @@ export default function Stack({ return [ node(Bitmap, { id, + textures_id, }), amount !== 1 && node_dom('div[className=amount]', { diff --git a/src/game/m_game.js b/src/game/m_game.js index 1008466..a0ac6ef 100644 --- a/src/game/m_game.js +++ b/src/game/m_game.js @@ -58,6 +58,7 @@ import { clearInterval_, clearTimeout_, Date_now, + fetch_, flag_chromium, JSON_stringify, Math_ceil, @@ -66,6 +67,7 @@ import { Math_max, Math_PI, Number_, + Set_, setInterval_, setTimeout_, } from '../etc/helpers.js'; @@ -147,7 +149,7 @@ export const game_create = (actions, frame_element, config, account) => { flag_hud: true, frame_element, frame_last: 0, - keys_active: new Set, + keys_active: new Set_, keys_active_check: '', menu: MENU_NONE, messages: [], @@ -885,7 +887,7 @@ const game_poll = (model, msg) => ( clearTimeout_(model.poll_timeout), ( msg - ? fetch(API_CHAT, { + ? fetch_(API_CHAT, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -894,7 +896,7 @@ const game_poll = (model, msg) => ( msg, })), }) - : fetch(API_CHAT) + : fetch_(API_CHAT) ) .then(res => { if (!res.ok) return; diff --git a/src/game/m_renderer.js b/src/game/m_renderer.js index 1be6ac1..2070aad 100644 --- a/src/game/m_renderer.js +++ b/src/game/m_renderer.js @@ -1,7 +1,3 @@ -import { - dom_define, -} from '../etc/lui.js'; - import { BLOCK_COLORS, BLOCK_TYPE_AIR, @@ -16,6 +12,7 @@ import { SKY_COLOR, } from '../etc/constants.js'; import { + API_DATA, VERSION, } from '../etc/env.js'; import { @@ -32,6 +29,7 @@ import { number_padStart2, number_square, number_toFixed2, + Set_, setInterval_, Uint32Array_, } from '../etc/helpers.js'; @@ -52,32 +50,49 @@ import { } from '../etc/textures.js'; // parse png -export let tiles_data = null; -let tiles_data_onload = null; -let tiles_image = new Image(); -tiles_image.crossOrigin = 'anonymous'; -tiles_image.onload = () => { - const canvas_temp = document_.createElement('canvas'); - canvas_temp.width = 1 << TILES_RESOLUTION_LOG2; - canvas_temp.height = TILES_COUNT << TILES_RESOLUTION_LOG2; - const context = canvas_temp.getContext('2d'); - context.drawImage(tiles_image, 0, 0); - dom_define('tile', 'div[className=bitmap]', { - S: { - backgroundImage: `url(${canvas_temp.toDataURL()})`, - }, - }); - tiles_data = new Uint32Array_( - context.getImageData( - 0, 0, - 1 << TILES_RESOLUTION_LOG2, - TILES_COUNT << TILES_RESOLUTION_LOG2 - ).data.buffer - ); - tiles_data_onload && tiles_data_onload(); - tiles_image = tiles_data_onload = null; +let tiles_data = null; +let tiles_image_loading = null; +const renderer_instances = new Set_; + +export const tiles_set = id => { + if (!id) { + tiles_data = null; + } + else { + const tiles_image = tiles_image_loading = new Image(); + if (VERSION === 'dev') { + tiles_image.crossOrigin = 'anonymous'; + } + tiles_image.onload = () => { + if (tiles_image_loading !== tiles_image) return; + const canvas_temp = document_.createElement('canvas'); + canvas_temp.width = TILES_RESOLUTION; + canvas_temp.height = TILES_COUNT << TILES_RESOLUTION_LOG2; + const context = canvas_temp.getContext('2d'); + // draw image flipped + context.scale(1, -1); + for (let tile = 0; tile < TILES_COUNT; ++tile) { + context.drawImage( + tiles_image, + 0, tile << TILES_RESOLUTION_LOG2, + TILES_RESOLUTION, TILES_RESOLUTION, + 0, -(tile << TILES_RESOLUTION_LOG2) - TILES_RESOLUTION, + TILES_RESOLUTION, TILES_RESOLUTION + ); + } + tiles_data = new Uint32Array_( + context.getImageData( + 0, 0, + TILES_RESOLUTION, + TILES_COUNT << TILES_RESOLUTION_LOG2 + ).data.buffer + ); + for (const model of renderer_instances) model.flag_dirty = true; + tiles_image_loading = null; + } + tiles_image.src = `${API_DATA}textures/${id}.png`; + } } -tiles_image.src = ASSETS + 'blocks.webp'; export const renderer_create = (game, canvas_element) => { const model = { @@ -98,14 +113,13 @@ export const renderer_create = (game, canvas_element) => { ), 1e3), game, }; - if (!tiles_data) { - tiles_data_onload = () => model.flag_dirty = true; - } + renderer_instances.add(model); renderer_canvas_init(model); return model; } export const renderer_destroy = model => ( + renderer_instances.delete(model), clearInterval_(model.fps_interval) ); @@ -153,7 +167,7 @@ export const renderer_render = (model, now) => { blocks, size_l2, } = world; - const flag_textures = config.flag_textures && tiles_data !== null; + const flag_textures = tiles_data !== null; const resolution_x_1d = 1 / resolution_x; const resolution_y_1d = 1 / resolution_y; const resolution_x_h = resolution_x >> 1; diff --git a/tools/textures/blocks.json b/tools/textures/blocks.json deleted file mode 100644 index 60de2b8..0000000 --- a/tools/textures/blocks.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "resolution": 16, - "tiles": [ - "stone", - "grass_top", - "dirt", - "cobble", - "planks", - "bedrock", - "log_side", - "leaves", - "bricks", - "wool", - "sand", - "gravel", - "glass", - "bookshelf", - "obsidian", - "stone_bricks", - "sandstone", - "lapis_block", - "iron_block", - "gold_block", - "diamond_block", - "emerald_block", - "redstone_block", - "quartz_block", - - "grass_side", - "log_top" - ] -} diff --git a/tools/textures/blocks.png b/tools/textures/blocks.png deleted file mode 100644 index 4e561d18707746d0c326e44e01e657e32b13b7b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9043 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 (https://l3p3.de)", - "license": "Zlib", - "devDependencies": { - "pngjs": "latest" - } -} diff --git a/tools/textures/packer.js b/tools/textures/packer.js deleted file mode 100644 index 9b2a53b..0000000 --- a/tools/textures/packer.js +++ /dev/null @@ -1,97 +0,0 @@ -import {execSync} from 'child_process'; -import { - readFileSync, - unlinkSync, - writeFileSync, -} from 'fs'; -import {PNG} from 'pngjs'; - -const input_meta = JSON.parse(readFileSync('./blocks.json', 'utf8')); -const tiles_length = input_meta.tiles.length; -const {resolution} = input_meta; -console.log('metafile read, tiles:', tiles_length); - -const input_resolution_log2 = Math.log2(resolution); -if (input_resolution_log2 % 1) - throw new Error('resolution must be in 1, 2, 4, 8...'); - -const input_data = readFileSync('blocks.png'); -console.log('input read, size:', input_data.length); - -const png_instance = PNG.sync.read(input_data); -console.log('input parsed, resolution:', png_instance.width, png_instance.height); - -if (png_instance.width !== resolution) - throw new Error('wrong input width'); -if (png_instance.height !== resolution * tiles_length) - throw new Error('wrong input height'); - -console.log('input size matches metafile'); - -const image_buffer = png_instance.data; - -const resolution_half = resolution >>> 1; -for (let texture = 0; texture < tiles_length; ++texture) { - const texture_offset = texture << (input_resolution_log2 << 1); - for (let row = 0; row < resolution_half; ++row) { - const row_offset_a = ( - texture_offset | - row << input_resolution_log2 - ); - const row_offset_b = ( - texture_offset | - (resolution - 1 - row) << input_resolution_log2 - ); - for (let column = 0; column < resolution; ++column) { - let index_a = ( - row_offset_a | - column - ) << 2; - let index_b = ( - row_offset_b | - column - ) << 2; - let tmp = image_buffer[index_a]; - image_buffer[index_a] = image_buffer[index_b]; - image_buffer[index_b] = tmp; - tmp = image_buffer[++index_a]; - image_buffer[index_a] = image_buffer[++index_b]; - image_buffer[index_b] = tmp; - tmp = image_buffer[++index_a]; - image_buffer[index_a] = image_buffer[++index_b]; - image_buffer[index_b] = tmp; - tmp = image_buffer[++index_a]; - image_buffer[index_a] = image_buffer[++index_b]; - image_buffer[index_b] = tmp; - } - } -} - -const transformed_data = PNG.sync.write(png_instance); -console.log('transformed png generated, size:', transformed_data.length); - -writeFileSync('/tmp/blocks.png', transformed_data); - -execSync('cwebp -m 6 -near_lossless 40 -sharp_yuv -segments 1 -q 100 /tmp/blocks.png -o ../../assets/blocks.webp'); -console.log('blocks.webp written'); - -unlinkSync('/tmp/blocks.png'); - -let output_text = `// This file was automatically generated by packer.js - -`; - -let tiles_index = 0; -for (const tile of input_meta.tiles) { - output_text += 'export const TILE_' + tile.toUpperCase() + ' = ' + tiles_index + ';\n'; - ++tiles_index; -} - -output_text += ` -export const TILES_COUNT = ${tiles_length}; -export const TILES_RESOLUTION = ${resolution}; -export const TILES_RESOLUTION_LOG2 = ${input_resolution_log2}; -`; - -writeFileSync('textures.js', output_text, 'utf8'); -console.log('textures.js written'); diff --git a/tools/textures/textures.js b/tools/textures/textures.js deleted file mode 100644 index e10297a..0000000 --- a/tools/textures/textures.js +++ /dev/null @@ -1,32 +0,0 @@ -// This file was automatically generated by packer.js - -export const TILE_STONE = 0; -export const TILE_GRASS_TOP = 1; -export const TILE_DIRT = 2; -export const TILE_COBBLE = 3; -export const TILE_PLANKS = 4; -export const TILE_BEDROCK = 5; -export const TILE_LOG_SIDE = 6; -export const TILE_LEAVES = 7; -export const TILE_BRICKS = 8; -export const TILE_WOOL = 9; -export const TILE_SAND = 10; -export const TILE_GRAVEL = 11; -export const TILE_GLASS = 12; -export const TILE_BOOKSHELF = 13; -export const TILE_OBSIDIAN = 14; -export const TILE_STONE_BRICKS = 15; -export const TILE_SANDSTONE = 16; -export const TILE_LAPIS_BLOCK = 17; -export const TILE_IRON_BLOCK = 18; -export const TILE_GOLD_BLOCK = 19; -export const TILE_DIAMOND_BLOCK = 20; -export const TILE_EMERALD_BLOCK = 21; -export const TILE_REDSTONE_BLOCK = 22; -export const TILE_QUARTZ_BLOCK = 23; -export const TILE_GRASS_SIDE = 24; -export const TILE_LOG_TOP = 25; - -export const TILES_COUNT = 26; -export const TILES_RESOLUTION = 16; -export const TILES_RESOLUTION_LOG2 = 4;