diff --git a/.backstage/build-tile-data.js b/.backstage/build-tile-data.js index 8b1527b0..9b5b4fba 100755 --- a/.backstage/build-tile-data.js +++ b/.backstage/build-tile-data.js @@ -2,6 +2,37 @@ const fs = require("fs"); const path = require("path"); +const { Jimp } = require("jimp"); + +async function processImage() { + const IMAGE_INPUT_PATH = path.join( + __dirname, + "../truncate_client/img/truncate_packed_pre.png", + ); + const IMAGE_OUTPUT_PATH = path.join( + __dirname, + "../truncate_client/img/truncate_packed.png", + ); + + const image = await Jimp.read(IMAGE_INPUT_PATH); + + image.scan((x, y, idx) => { + const red = image.bitmap.data[idx + 0]; + const green = image.bitmap.data[idx + 1]; + const blue = image.bitmap.data[idx + 2]; + + // Find pure magenta and make it transparent + if (red === 255 && green === 0 && blue === 255) { + image.bitmap.data[idx + 3] = 0; + } + }); + + await image.write(IMAGE_OUTPUT_PATH); +} + +processImage().catch((err) => { + console.error("Error processing image:", err); +}); const INPUT_FILE = path.join(__dirname, "../truncate_client/img/tile_order"); const OUTPUT_RUST = path.join( diff --git a/.backstage/package.json b/.backstage/package.json new file mode 100644 index 00000000..79daef6a --- /dev/null +++ b/.backstage/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "jimp": "^1.6.0" + } +} diff --git a/.gitignore b/.gitignore index 5a42db81..70ff851b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ kaikki* node_modules word_definitions/package-lock.json truncate_client/img/package-lock.json +truncate_client/img/truncate_packed_pre.png _site # ungzipped word definition db defs.db @@ -18,3 +19,4 @@ dict_builder/support_data/generated_scowl_wordlists dict_builder/support_data/en_word_freqs.txt dict_builder/support_data/objectionable.json dict_builder/support_data/wordnik_wordlist.txt +.backstage/package-lock.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c97b44ae..18a07803 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -91,11 +91,11 @@ cd truncate_dueller && cargo run --release - **Trim Cels**: unchecked - **Extrude**: CHECKED! - **Output**: - - **Output File**: `truncate_packed.png` + - **Output File**: `truncate_packed_pre.png` - **JSON Data** unchecked -If you modified the tile order at all: +Then: - Edit `truncate_client/img/tile_order` to match the new ordering of tiles - If you added something 32x32, and thus taking up four tiles, suffix their tile names with `_NW`, `_NE`, `_SW`, and `_SE` - From the root of the repo, run `.backstage/build-tile-data.js` to rerun the codegen for using tiles - +- This also generates the final truncate_packed.png that slightly modified the export (e.g. removes magenta mask) diff --git a/truncate_client/img/tile_order b/truncate_client/img/tile_order index 2a381061..1e90fc42 100644 --- a/truncate_client/img/tile_order +++ b/truncate_client/img/tile_order @@ -194,3 +194,211 @@ TERRAIN_BUTTON_NW TERRAIN_BUTTON_NE TERRAIN_BUTTON_SW TERRAIN_BUTTON_SE +LETTER_SOUTH_A_NW +LETTER_SOUTH_A_NE +LETTER_SOUTH_A_SW +LETTER_SOUTH_A_SE +LETTER_SOUTH_B_NW +LETTER_SOUTH_B_NE +LETTER_SOUTH_B_SW +LETTER_SOUTH_B_SE +LETTER_SOUTH_C_NW +LETTER_SOUTH_C_NE +LETTER_SOUTH_C_SW +LETTER_SOUTH_C_SE +LETTER_SOUTH_D_NW +LETTER_SOUTH_D_NE +LETTER_SOUTH_D_SW +LETTER_SOUTH_D_SE +LETTER_SOUTH_E_NW +LETTER_SOUTH_E_NE +LETTER_SOUTH_E_SW +LETTER_SOUTH_E_SE +LETTER_SOUTH_F_NW +LETTER_SOUTH_F_NE +LETTER_SOUTH_F_SW +LETTER_SOUTH_F_SE +LETTER_SOUTH_G_NW +LETTER_SOUTH_G_NE +LETTER_SOUTH_G_SW +LETTER_SOUTH_G_SE +LETTER_SOUTH_H_NW +LETTER_SOUTH_H_NE +LETTER_SOUTH_H_SW +LETTER_SOUTH_H_SE +LETTER_SOUTH_I_NW +LETTER_SOUTH_I_NE +LETTER_SOUTH_I_SW +LETTER_SOUTH_I_SE +LETTER_SOUTH_J_NW +LETTER_SOUTH_J_NE +LETTER_SOUTH_J_SW +LETTER_SOUTH_J_SE +LETTER_SOUTH_K_NW +LETTER_SOUTH_K_NE +LETTER_SOUTH_K_SW +LETTER_SOUTH_K_SE +LETTER_SOUTH_L_NW +LETTER_SOUTH_L_NE +LETTER_SOUTH_L_SW +LETTER_SOUTH_L_SE +LETTER_SOUTH_M_NW +LETTER_SOUTH_M_NE +LETTER_SOUTH_M_SW +LETTER_SOUTH_M_SE +LETTER_SOUTH_N_NW +LETTER_SOUTH_N_NE +LETTER_SOUTH_N_SW +LETTER_SOUTH_N_SE +LETTER_SOUTH_O_NW +LETTER_SOUTH_O_NE +LETTER_SOUTH_O_SW +LETTER_SOUTH_O_SE +LETTER_SOUTH_P_NW +LETTER_SOUTH_P_NE +LETTER_SOUTH_P_SW +LETTER_SOUTH_P_SE +LETTER_SOUTH_Q_NW +LETTER_SOUTH_Q_NE +LETTER_SOUTH_Q_SW +LETTER_SOUTH_Q_SE +LETTER_SOUTH_R_NW +LETTER_SOUTH_R_NE +LETTER_SOUTH_R_SW +LETTER_SOUTH_R_SE +LETTER_SOUTH_S_NW +LETTER_SOUTH_S_NE +LETTER_SOUTH_S_SW +LETTER_SOUTH_S_SE +LETTER_SOUTH_T_NW +LETTER_SOUTH_T_NE +LETTER_SOUTH_T_SW +LETTER_SOUTH_T_SE +LETTER_SOUTH_U_NW +LETTER_SOUTH_U_NE +LETTER_SOUTH_U_SW +LETTER_SOUTH_U_SE +LETTER_SOUTH_V_NW +LETTER_SOUTH_V_NE +LETTER_SOUTH_V_SW +LETTER_SOUTH_V_SE +LETTER_SOUTH_W_NW +LETTER_SOUTH_W_NE +LETTER_SOUTH_W_SW +LETTER_SOUTH_W_SE +LETTER_SOUTH_X_NW +LETTER_SOUTH_X_NE +LETTER_SOUTH_X_SW +LETTER_SOUTH_X_SE +LETTER_SOUTH_Y_NW +LETTER_SOUTH_Y_NE +LETTER_SOUTH_Y_SW +LETTER_SOUTH_Y_SE +LETTER_SOUTH_Z_NW +LETTER_SOUTH_Z_NE +LETTER_SOUTH_Z_SW +LETTER_SOUTH_Z_SE +LETTER_NORTH_A_NW +LETTER_NORTH_A_NE +LETTER_NORTH_A_SW +LETTER_NORTH_A_SE +LETTER_NORTH_B_NW +LETTER_NORTH_B_NE +LETTER_NORTH_B_SW +LETTER_NORTH_B_SE +LETTER_NORTH_C_NW +LETTER_NORTH_C_NE +LETTER_NORTH_C_SW +LETTER_NORTH_C_SE +LETTER_NORTH_D_NW +LETTER_NORTH_D_NE +LETTER_NORTH_D_SW +LETTER_NORTH_D_SE +LETTER_NORTH_E_NW +LETTER_NORTH_E_NE +LETTER_NORTH_E_SW +LETTER_NORTH_E_SE +LETTER_NORTH_F_NW +LETTER_NORTH_F_NE +LETTER_NORTH_F_SW +LETTER_NORTH_F_SE +LETTER_NORTH_G_NW +LETTER_NORTH_G_NE +LETTER_NORTH_G_SW +LETTER_NORTH_G_SE +LETTER_NORTH_H_NW +LETTER_NORTH_H_NE +LETTER_NORTH_H_SW +LETTER_NORTH_H_SE +LETTER_NORTH_I_NW +LETTER_NORTH_I_NE +LETTER_NORTH_I_SW +LETTER_NORTH_I_SE +LETTER_NORTH_J_NW +LETTER_NORTH_J_NE +LETTER_NORTH_J_SW +LETTER_NORTH_J_SE +LETTER_NORTH_K_NW +LETTER_NORTH_K_NE +LETTER_NORTH_K_SW +LETTER_NORTH_K_SE +LETTER_NORTH_L_NW +LETTER_NORTH_L_NE +LETTER_NORTH_L_SW +LETTER_NORTH_L_SE +LETTER_NORTH_M_NW +LETTER_NORTH_M_NE +LETTER_NORTH_M_SW +LETTER_NORTH_M_SE +LETTER_NORTH_N_NW +LETTER_NORTH_N_NE +LETTER_NORTH_N_SW +LETTER_NORTH_N_SE +LETTER_NORTH_O_NW +LETTER_NORTH_O_NE +LETTER_NORTH_O_SW +LETTER_NORTH_O_SE +LETTER_NORTH_P_NW +LETTER_NORTH_P_NE +LETTER_NORTH_P_SW +LETTER_NORTH_P_SE +LETTER_NORTH_Q_NW +LETTER_NORTH_Q_NE +LETTER_NORTH_Q_SW +LETTER_NORTH_Q_SE +LETTER_NORTH_R_NW +LETTER_NORTH_R_NE +LETTER_NORTH_R_SW +LETTER_NORTH_R_SE +LETTER_NORTH_S_NW +LETTER_NORTH_S_NE +LETTER_NORTH_S_SW +LETTER_NORTH_S_SE +LETTER_NORTH_T_NW +LETTER_NORTH_T_NE +LETTER_NORTH_T_SW +LETTER_NORTH_T_SE +LETTER_NORTH_U_NW +LETTER_NORTH_U_NE +LETTER_NORTH_U_SW +LETTER_NORTH_U_SE +LETTER_NORTH_V_NW +LETTER_NORTH_V_NE +LETTER_NORTH_V_SW +LETTER_NORTH_V_SE +LETTER_NORTH_W_NW +LETTER_NORTH_W_NE +LETTER_NORTH_W_SW +LETTER_NORTH_W_SE +LETTER_NORTH_X_NW +LETTER_NORTH_X_NE +LETTER_NORTH_X_SW +LETTER_NORTH_X_SE +LETTER_NORTH_Y_NW +LETTER_NORTH_Y_NE +LETTER_NORTH_Y_SW +LETTER_NORTH_Y_SE +LETTER_NORTH_Z_NW +LETTER_NORTH_Z_NE +LETTER_NORTH_Z_SW +LETTER_NORTH_Z_SE diff --git a/truncate_client/img/truncate.aseprite b/truncate_client/img/truncate.aseprite index 1799998a..1e57facf 100644 Binary files a/truncate_client/img/truncate.aseprite and b/truncate_client/img/truncate.aseprite differ diff --git a/truncate_client/img/truncate_packed.png b/truncate_client/img/truncate_packed.png index 95608baa..5abb3912 100644 Binary files a/truncate_client/img/truncate_packed.png and b/truncate_client/img/truncate_packed.png differ diff --git a/truncate_client/src/app_inner.rs b/truncate_client/src/app_inner.rs index 00e9e3a7..7f35665d 100644 --- a/truncate_client/src/app_inner.rs +++ b/truncate_client/src/app_inner.rs @@ -17,6 +17,7 @@ use crate::{ lobby::Lobby, native_menu::render_native_menu_if_required, replayer::ReplayerState, + rules::{RuleCardAction, RulesState}, single_player::SinglePlayerState, tutorial::TutorialState, }, @@ -33,6 +34,7 @@ pub enum GameStatus { None(RoomCode, Option), Generator(GeneratorState), Tutorial(TutorialState), + Rules(RulesState), PendingSinglePlayer(Lobby), SinglePlayer(SinglePlayerState), PendingDaily, @@ -409,6 +411,15 @@ pub fn render(outer: &mut OuterApplication, ui: &mut egui::Ui, current_time: Dur GameStatus::Replay(replay) => { replay.render(ui, &outer.theme, current_time, &outer.backchannel); } + GameStatus::Rules(rules) => match rules.render(ui, &outer.theme, current_time) { + Some(RuleCardAction::DailyPuzzle) => { + outer.launched_code = Some("DAILY_PUZZLE".to_string()) + } + Some(RuleCardAction::Tutorial) => { + outer.launched_code = Some("TUTORIAL_RULES".to_string()) + } + None => {} + }, GameStatus::HardError(msg) => { let splash = SplashUI::new(msg.clone()).with_button( "reload", diff --git a/truncate_client/src/handle_launch_code.rs b/truncate_client/src/handle_launch_code.rs index 3975489c..341bcbaf 100644 --- a/truncate_client/src/handle_launch_code.rs +++ b/truncate_client/src/handle_launch_code.rs @@ -11,10 +11,10 @@ use truncate_core::{ use crate::{ app_inner::GameStatus, regions::{ - active_game::HeaderType, lobby::Lobby, single_player::SinglePlayerState, + active_game::HeaderType, lobby::Lobby, rules::RulesState, single_player::SinglePlayerState, tutorial::TutorialState, }, - utils::{self, daily::get_puzzle_day, macros::current_time}, + utils::{self, daily::get_puzzle_day, includes::rule_card, macros::current_time}, }; use super::OuterApplication; @@ -40,13 +40,22 @@ pub fn handle_launch_code( "TUTORIAL_RULES" => { return Some(GameStatus::Tutorial(TutorialState::new( "rules".to_string(), - utils::includes::rules(outer.launched_at_day), + utils::includes::tutorial(outer.launched_at_day), ui.ctx(), outer.map_texture.clone(), &outer.theme, outer.event_dispatcher.clone(), ))); } + "RULE_CARD" => { + return Some(GameStatus::Rules(RulesState::new( + ui.ctx(), + outer.map_texture.clone(), + outer.theme.clone(), + rule_card(), + outer.event_dispatcher.clone(), + ))); + } "SINGLE_PLAYER" => { outer.event_dispatcher.event("single_player_lobby"); let mut board = Board::new(9, 9); diff --git a/truncate_client/src/lil_bits/board.rs b/truncate_client/src/lil_bits/board.rs index 89247f1b..6036c571 100644 --- a/truncate_client/src/lil_bits/board.rs +++ b/truncate_client/src/lil_bits/board.rs @@ -312,9 +312,9 @@ impl<'a> BoardUI<'a> { orientation: if *square_player == gameplay.player_number as usize { - Direction::North - } else { Direction::South + } else { + Direction::North }, }], aesthetics, diff --git a/truncate_client/src/lil_bits/changes_splash.rs b/truncate_client/src/lil_bits/changes_splash.rs index 6b90b94b..b035baae 100644 --- a/truncate_client/src/lil_bits/changes_splash.rs +++ b/truncate_client/src/lil_bits/changes_splash.rs @@ -99,7 +99,7 @@ impl ChangelogSplashUI { return; } let block_time = (current_time - self.animate_from).as_secs_f32(); - let animated_text = block.get_partial_slice(block_time, ui); + let animated_text = block.get_partial_word_slice(block_time, ui); match animated_text { Some(animated_block) => { diff --git a/truncate_client/src/lil_bits/hand.rs b/truncate_client/src/lil_bits/hand.rs index aca0587a..9b2af055 100644 --- a/truncate_client/src/lil_bits/hand.rs +++ b/truncate_client/src/lil_bits/hand.rs @@ -85,7 +85,7 @@ impl<'a> HandUI<'a> { } else { None }, - orientation: truncate_core::board::Direction::North, + orientation: truncate_core::board::Direction::South, } }) .collect(), diff --git a/truncate_client/src/regions/mod.rs b/truncate_client/src/regions/mod.rs index ec59dce6..01918852 100644 --- a/truncate_client/src/regions/mod.rs +++ b/truncate_client/src/regions/mod.rs @@ -3,5 +3,6 @@ pub mod generator; pub mod lobby; pub mod native_menu; pub mod replayer; +pub mod rules; pub mod single_player; pub mod tutorial; diff --git a/truncate_client/src/regions/native_menu.rs b/truncate_client/src/regions/native_menu.rs index 60f07ab4..f4939c7e 100644 --- a/truncate_client/src/regions/native_menu.rs +++ b/truncate_client/src/regions/native_menu.rs @@ -38,7 +38,7 @@ pub fn render_native_menu_if_required( if ui.button("Tutorial: Rules").clicked() { return Some(GameStatus::Tutorial(TutorialState::new( "rules".to_string(), - utils::includes::rules(outer.launched_at_day), + utils::includes::tutorial(outer.launched_at_day), ui.ctx(), outer.map_texture.clone(), &outer.theme, diff --git a/truncate_client/src/regions/rules.rs b/truncate_client/src/regions/rules.rs new file mode 100644 index 00000000..a3176c0b --- /dev/null +++ b/truncate_client/src/regions/rules.rs @@ -0,0 +1,578 @@ +use std::{fmt::Alignment, ops::Sub}; + +use eframe::egui::{self, Align2, Layout, Sense}; +use epaint::{hex_color, vec2, Color32, Pos2, Rect, TextureHandle, Vec2}; +use instant::Duration; +use truncate_core::{ + board::Direction, + game::{Game, GAME_COLORS}, + moves::Move, + reporting::{BoardChange, BoardChangeAction, BoardChangeDetail, Change}, +}; + +use crate::{ + app_outer::{Backchannel, EventDispatcher, GLYPHER}, + utils::{ + depot::{AestheticDepot, GameplayDepot, TimingDepot}, + game_evals::get_main_dict, + includes::RuleCard, + mapper::{ImageMusher, MappedBoard, MappedTileVariant}, + tex::{ + self, render_texs_clockwise, BGTexType, PieceLayer, Tex, TexLayers, TexQuad, + TileDecoration, Tint, + }, + text::TextHelper, + timing::get_qs_tick, + urls::back_to_menu, + Diaphanize, Lighten, Theme, + }, +}; + +pub const RULE_PLAYER_COLORS: [Color32; 2] = [ + Color32::from_rgb(GAME_COLORS[1].0, GAME_COLORS[1].1, GAME_COLORS[1].2), + Color32::from_rgb(GAME_COLORS[0].0, GAME_COLORS[0].1, GAME_COLORS[0].2), +]; + +pub enum RuleCardAction { + DailyPuzzle, + Tutorial, +} + +#[derive(Debug, Clone)] +pub struct ParsedRuleCard { + pub sections: Vec, +} + +#[derive(Debug, Clone)] +pub struct ParsedRuleCardSection { + title: String, + examples: Vec, + started_animation_at: Option, + ended_animation_at: Option, + seen: bool, + description: String, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ParsedRuleCardExample { + textures: Vec>, +} + +fn parse_rule_card(rules: RuleCard, theme: &Theme) -> ParsedRuleCard { + ParsedRuleCard { + sections: rules + .sections + .into_iter() + .map(|section| ParsedRuleCardSection { + title: section.title, + examples: section + .examples + .into_iter() + .map(|r| parse_rule_example(r, theme)) + .collect(), + started_animation_at: None, + ended_animation_at: None, + seen: false, + description: section.description, + }) + .collect(), + } +} + +#[derive(Clone)] +pub struct RulesState { + map_texture: TextureHandle, + aesthetics: AestheticDepot, + rules: ParsedRuleCard, + active_rule: usize, + finished: bool, + started_at: Option, + event_dispatcher: EventDispatcher, +} + +impl RulesState { + pub fn new( + ctx: &egui::Context, + map_texture: TextureHandle, + theme: Theme, + rules: RuleCard, + mut event_dispatcher: EventDispatcher, + ) -> Self { + let aesthetics = AestheticDepot { + theme, + qs_tick: 0, + map_texture: map_texture.clone(), + player_colors: vec![Color32::from_rgb(255, 0, 0), Color32::from_rgb(0, 255, 0)], + destruction_tick: 0.05, + destruction_duration: 0.6, + }; + + event_dispatcher.event(format!("tutorial_core_rules")); + + let parsed_rules = parse_rule_card(rules, &aesthetics.theme); + + Self { + map_texture, + aesthetics, + rules: parsed_rules, + active_rule: 0, + finished: false, + started_at: None, + event_dispatcher, + } + } + + #[must_use] + pub fn render( + &mut self, + ui: &mut egui::Ui, + theme: &Theme, + current_time: Duration, + ) -> Option { + let mut action = None; + let cur_animation_tick = get_qs_tick(current_time) as usize; + let page_started_at = self.started_at.get_or_insert(current_time).to_owned(); + let mut time_on_page = (current_time - page_started_at) + .as_secs_f32() + .sub(0.2) + .max(0.0); + + let bg = ui.available_rect_before_wrap(); + ui.painter().rect_filled(bg, 0.0, hex_color!("#c8ecff")); + + let heading_size = if bg.width() > 700.0 { 24.0 } else { 18.0 }; + let text_size = if bg.width() > 700.0 { 32.0 } else { 24.0 }; + let sprite_size = if bg.width() > 700.0 { 48.0 } else { 32.0 }; + + ui.spacing_mut().item_spacing.x = 0.0; + let screen_height = ui.available_height(); + let screen_width = ui.available_width(); + let rulebox = (screen_height / 2.0).max(310.0); + let text_padding = 20.0; + + egui::ScrollArea::new([false, true]).show(ui, |ui| { + ui.expand_to_include_x(ui.available_rect_before_wrap().right()); + + let scroll_pos = ui.next_widget_position().y * -1.0; + let ideal_active_rule = ((scroll_pos + (rulebox / 2.0)) / rulebox).max(0.0) as usize; + + if scroll_pos != 0.0 { + time_on_page = f32::MAX; + } + + if self.active_rule != ideal_active_rule { + let backlash = (scroll_pos + (rulebox / 2.0)) % rulebox; + + if backlash > 50.0 && backlash < (rulebox - 50.0) { + self.active_rule = ideal_active_rule; + } + } + + let (top_rect, _) = + ui.allocate_exact_size(vec2(ui.available_width(), rulebox / 2.0), Sense::hover()); + + let main_heading = TextHelper::heavy_centered( + "Truncate\nQuick Start", + heading_size * 1.25, + Some((ui.available_width() - 16.0).max(0.0)), + ui, + ); + let animated_main_heading = main_heading.get_partial_char_slice(time_on_page, ui); + let animating_heading = animated_main_heading.is_some(); + if animating_heading { + ui.ctx().request_repaint(); + } + let final_size = main_heading.mesh_size(); + let main_heading_pos = top_rect.center() - vec2(0.0, final_size.y / 2.0); + animated_main_heading.unwrap_or(main_heading).paint_at( + main_heading_pos, + theme.text, + ui, + ); + + for (rulenum, rule) in self.rules.sections.iter_mut().enumerate() { + let is_active = self.active_rule == rulenum; + if is_active { + if !rule.seen { + self.event_dispatcher + .event(format!("tutorial_core_rules_seen_{}", rulenum)); + rule.seen = true; + } + } + let texture_gamma = if animating_heading { + 0.0 + } else if is_active { + 1.0 + } else { + 0.2 + }; + let animated_gamma = + ui.ctx() + .animate_value_with_time(ui.id().with(rulenum), texture_gamma, 0.3); + if animated_gamma != texture_gamma { + ui.ctx().request_repaint_after(Duration::from_millis(16)); + } + + let text_color = theme.text.gamma_multiply(animated_gamma); + + let (rule_rect, _) = + ui.allocate_exact_size(vec2(ui.available_width(), rulebox), Sense::hover()); + + let started_at = rule.started_animation_at.unwrap_or(cur_animation_tick); + if is_active && rule.started_animation_at.is_none() && !animating_heading { + rule.started_animation_at = Some(started_at); + } + + let final_stage = rule.examples.len() - 1; + let current_stage = + (cur_animation_tick.saturating_sub(started_at)).min(final_stage); + + if current_stage == final_stage && rule.ended_animation_at.is_none() { + rule.ended_animation_at = Some(cur_animation_tick); + } + + let ended_ago = rule + .ended_animation_at + .map(|e| cur_animation_tick.saturating_sub(e)) + .unwrap_or_default(); + + let target_retrigger_gamma = if ended_ago > 6 { 0.0 } else { 1.0 }; + let retrigger_gamma = ui.ctx().animate_value_with_time( + ui.id().with(rulenum).with("retrigger"), + target_retrigger_gamma, + 0.3, + ); + if retrigger_gamma != target_retrigger_gamma { + ui.ctx().request_repaint_after(Duration::from_millis(16)); + } + let blended_gamma = animated_gamma * retrigger_gamma; + + if ended_ago > 9 { + rule.started_animation_at = if is_active { + Some(cur_animation_tick + 2) + } else { + None + }; + rule.ended_animation_at = None; + } + + let cur_example = &rule.examples[current_stage]; + let example_height = (cur_example.textures.len() as f32 * sprite_size); + let mut example_corner = rule_rect.left_center(); + example_corner.y -= example_height / 2.0; + + for (rownum, row) in cur_example.textures.iter().enumerate() { + let row_width = row.len() as f32 * sprite_size; + let left_buffer = (ui.available_width() - row_width) / 2.0; + let top_buffer = rownum as f32 * sprite_size; + + for (colnum, slot) in row.iter().enumerate() { + let rect = Rect::from_min_size( + Pos2::new( + example_corner.x + left_buffer + colnum as f32 * sprite_size, + example_corner.y + top_buffer, + ), + Vec2::splat(sprite_size), + ); + + if let Some(structures) = slot.structures { + let structures = structures + .iter() + .map(|s| { + s.tint( + s.current_tint() + .unwrap_or(Color32::WHITE) + .gamma_multiply(blended_gamma), + ) + }) + .collect(); + render_texs_clockwise(structures, rect, &self.map_texture, ui); + } + for piece in &slot.pieces { + match piece { + PieceLayer::Texture(texs, _) => { + let texs = texs + .iter() + .map(|t| { + t.tint( + t.current_tint() + .unwrap_or(Color32::WHITE) + .gamma_multiply(blended_gamma), + ) + }) + .collect(); + + render_texs_clockwise(texs, rect, &self.map_texture, ui); + } + PieceLayer::Character(char, color, is_flipped, y_offset) => { + assert!(false); + } + } + } + } + } + + let t = TextHelper::heavy_centered( + &rule.title, + heading_size, + Some((ui.available_width() - 16.0).max(0.0)), + ui, + ); + // Adding an extra smidge of height for visual centering + let heading_height = t.mesh_size().y + 5.0; + let heading_rect = Rect::from_min_size( + Pos2::new( + example_corner.x, + example_corner.y - text_padding - heading_height, + ), + vec2(screen_width, heading_height), + ); + + t.paint_within(heading_rect, Align2::CENTER_TOP, text_color, ui); + + let description = TextHelper::light_centered( + &rule.description, + text_size, + Some((ui.available_width() - 16.0).max(0.0)), + ui, + ); + let text_height = description.mesh_size().y; + let text_area = Rect::from_min_size( + Pos2::new( + example_corner.x, + example_corner.y + example_height + text_padding, + ), + vec2(screen_width, text_height), + ); + + description.paint_within(text_area, Align2::CENTER_TOP, text_color, ui); + } + ui.add_space(rulebox * 0.5); + + let is_post_rules = self.active_rule >= self.rules.sections.len(); + + if is_post_rules && !self.finished { + self.event_dispatcher + .event(format!("tutorial_core_rules_finished")); + self.finished = true; + } + + let end_gamma = if is_post_rules { 1.0 } else { 0.2 }; + let animated_gamma = + ui.ctx() + .animate_value_with_time(ui.id().with("post_rules"), end_gamma, 0.3); + if animated_gamma != end_gamma { + ui.ctx().request_repaint_after(Duration::from_millis(16)); + } + let text_color = theme.text.gamma_multiply(animated_gamma); + + let t = TextHelper::heavy_centered( + "Next steps", + heading_size, + Some((ui.available_width() - 16.0).max(0.0)), + ui, + ); + let (heading_rect, _) = + ui.allocate_at_least(vec2(ui.available_width(), t.mesh_size().y), Sense::hover()); + t.paint_within(heading_rect, Align2::CENTER_CENTER, text_color, ui); + ui.add_space(text_padding); + + let desc = [ + "Play the daily puzzle and learn as you go,", + "or view the interactive tutorial for a full walkthrough.", + ] + .join("\n"); + let d = TextHelper::light_centered( + &desc, + text_size, + Some((ui.available_width() - 16.0).max(0.0)), + ui, + ); + let (desc_rect, _) = + ui.allocate_at_least(vec2(ui.available_width(), d.mesh_size().y), Sense::hover()); + d.paint_within(desc_rect, Align2::CENTER_CENTER, text_color, ui); + ui.add_space(text_padding * 2.0); + + let back_text = TextHelper::heavy("PLAY TODAY'S PUZZLE", 14.0, None, ui); + if back_text + .centered_button(theme.button_emphasis, theme.text, &self.map_texture, ui) + .clicked() + { + self.event_dispatcher + .event(format!("tutorial_core_rules_go_puzzle")); + action = Some(RuleCardAction::DailyPuzzle); + } + ui.add_space(text_padding * 2.0); + + let tut_text = TextHelper::heavy("EXPLORE TUTORIAL", 14.0, None, ui); + if tut_text + .centered_button(theme.water.lighten(), theme.text, &self.map_texture, ui) + .clicked() + { + self.event_dispatcher + .event(format!("tutorial_core_rules_go_tutorial")); + action = Some(RuleCardAction::Tutorial); + } + + let back_text = TextHelper::heavy("RETURN TO MENU", 14.0, None, ui); + if back_text + .centered_button(theme.water.lighten(), theme.text, &self.map_texture, ui) + .clicked() + { + self.event_dispatcher + .event(format!("tutorial_core_rules_go_menu")); + back_to_menu(); + } + ui.add_space(text_padding); + + ui.add_space(rulebox * 0.5); + }); + + action + } +} + +fn parse_rule_example(example: String, theme: &Theme) -> ParsedRuleCardExample { + let textures = example + .split('\n') + .map(|row| { + row.split_whitespace() + .enumerate() + .map(|(i, square)| match square { + "~" => TexLayers::default(), + "$0" | "$1" => { + let color = if square == "$0" { + RULE_PLAYER_COLORS[0] + } else { + RULE_PLAYER_COLORS[1] + }; + + Tex::artifact(color, vec![BGTexType::Land; 4], 0) + } + "+0" | "+1" => { + let color = if square == "+0" { + RULE_PLAYER_COLORS[0] + } else { + RULE_PLAYER_COLORS[1] + }; + + Tex::town(color, i, 0, 0) + } + c => { + let mut chars = c.chars(); + let tile = chars.next().unwrap(); + let modifier = chars.next(); + let player = if tile.is_uppercase() { 1 } else { 0 }; + let mut color = RULE_PLAYER_COLORS[player]; + let mut text_color = None; + let orientation = if player == 0 { + Direction::North + } else { + Direction::South + }; + let mut variant = MappedTileVariant::Healthy; + let mut highlight = None; + + match modifier { + Some('*') => { + variant = MappedTileVariant::Dead; + color = Color32::GRAY; + } + Some('-') => { + variant = MappedTileVariant::Healthy; + color = Color32::GRAY.gamma_multiply(0.3); + text_color = Some(Color32::GRAY.gamma_multiply(0.6)); + } + Some('^') => highlight = Some(theme.ring_added), + Some('<') => highlight = Some(theme.ring_modified), + Some(c) => panic!("Unknown modifier {c}"), + None => {} + } + + Tex::board_game_tile( + variant, + tile.to_ascii_uppercase(), + orientation, + Some(color.lighten()), + text_color, + highlight, + TileDecoration::None, + i, + ) + } + }) + .collect() + }) + .collect(); + + ParsedRuleCardExample { textures } +} + +#[cfg(test)] +pub mod tests { + use super::*; + + #[test] + fn parse() { + let theme = Theme::day(); + let example = + "~ w ~ ~ ~ ~ ~ ~ ~ ~ ~\nW O* R^ $0 $1 ~ +0 +1 r^ o* w".to_string(); + let parsed = parse_rule_example(example, &theme); + + assert!(matches!( + parsed.textures[0][1].pieces[1], + PieceLayer::Texture(_, _) + )); + assert!(matches!( + parsed.textures[1][0].pieces[1], + PieceLayer::Texture(_, _) + )); + assert!(matches!( + parsed.textures[1][1].pieces[1], + PieceLayer::Texture(_, _) + )); + assert!(matches!( + parsed.textures[1][2].pieces[1], + PieceLayer::Texture(_, _) + )); + assert!(matches!( + parsed.textures[1][3].structures, + Some(tex::tiles::quad::ARTIFACT), + )); + assert!(matches!( + parsed.textures[1][4].structures, + Some(tex::tiles::quad::ARTIFACT), + )); + assert!(matches!(parsed.textures[1][5].terrain, None,)); + assert!(parsed.textures[1][6] + .structures + .is_some_and(|texs| texs.iter().any(|t| matches!( + t, + &tex::tiles::HOUSE_0 + | &tex::tiles::HOUSE_1 + | &tex::tiles::HOUSE_2 + | &tex::tiles::HOUSE_3 + )))); + assert!(parsed.textures[1][7] + .structures + .is_some_and(|texs| texs.iter().any(|t| matches!( + t, + &tex::tiles::HOUSE_0 + | &tex::tiles::HOUSE_1 + | &tex::tiles::HOUSE_2 + | &tex::tiles::HOUSE_3 + )))); + assert!(matches!( + parsed.textures[1][8].pieces[1], + PieceLayer::Texture(_, _) + )); + assert!(matches!( + parsed.textures[1][9].pieces[1], + PieceLayer::Texture(_, _) + )); + assert!(matches!( + parsed.textures[1][10].pieces[1], + PieceLayer::Texture(_, _) + )); + } +} diff --git a/truncate_client/src/regions/tutorial.rs b/truncate_client/src/regions/tutorial.rs index e0fec979..4e668a3e 100644 --- a/truncate_client/src/regions/tutorial.rs +++ b/truncate_client/src/regions/tutorial.rs @@ -611,7 +611,7 @@ impl TutorialState { ); let animated_text = - dialog_text.get_partial_slice(time_in_stage, ui); + dialog_text.get_partial_word_slice(time_in_stage, ui); if animated_text.is_some() { ui.ctx().request_repaint(); } @@ -639,7 +639,7 @@ impl TutorialState { ); let animated_text = - dialog_text.get_partial_slice(time_in_stage, ui); + dialog_text.get_partial_word_slice(time_in_stage, ui); let has_animation = animated_text.is_some(); if has_animation { ui.ctx().request_repaint(); @@ -691,7 +691,7 @@ impl TutorialState { ); let animated_text = - dialog_text.get_partial_slice(time_in_stage, ui); + dialog_text.get_partial_word_slice(time_in_stage, ui); let has_animation = animated_text.is_some(); if has_animation { ui.ctx().request_repaint(); @@ -746,7 +746,7 @@ impl TutorialState { ); let animated_text = - dialog_text.get_partial_slice(time_in_stage, ui); + dialog_text.get_partial_word_slice(time_in_stage, ui); let has_animation = animated_text.is_some(); if has_animation { ui.ctx().request_repaint(); diff --git a/truncate_client/src/utils/includes.rs b/truncate_client/src/utils/includes.rs index 387d0de7..3b20f086 100644 --- a/truncate_client/src/utils/includes.rs +++ b/truncate_client/src/utils/includes.rs @@ -52,7 +52,7 @@ pub enum ChangePriority { Low, } -pub fn rules(for_day: u32) -> Tutorial { +pub fn tutorial(for_day: u32) -> Tutorial { [ serde_yaml::from_slice::(include_bytes!("../../tutorials/rules_2.yml")) .expect("Tutorial should match Tutorial format"), @@ -80,3 +80,20 @@ pub fn changelogs() -> HashMap<&'static str, Tutorial> { ), ]) } + +#[derive(Deserialize, Debug, Clone)] +pub struct RuleCard { + pub sections: Vec, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct RuleCardSection { + pub title: String, + pub examples: Vec, + pub description: String, +} + +pub fn rule_card() -> RuleCard { + serde_yaml::from_slice::(include_bytes!("../../tutorials/rule_card.yml")) + .expect("Rules should match RuleCard format") +} diff --git a/truncate_client/src/utils/mapper/mod.rs b/truncate_client/src/utils/mapper/mod.rs index c4e61e8b..27440a2e 100644 --- a/truncate_client/src/utils/mapper/mod.rs +++ b/truncate_client/src/utils/mapper/mod.rs @@ -338,9 +338,9 @@ impl MappedBoard { let orient = |player: usize| { if player == self.for_player { - Direction::North - } else { Direction::South + } else { + Direction::North } }; @@ -436,12 +436,18 @@ impl MappedBoard { None }; let (variant, color) = animated_variant(player); + let text_color = if matches!(variant, MappedTileVariant::Gone) { + Some(hex_color!("#a6caa0")) + } else { + None + }; let tile_layers = Tex::board_game_tile( variant, tile, orient(player), validity_color.or(color), + text_color, None, TileDecoration::Grass, seed_at_coord, @@ -460,12 +466,18 @@ impl MappedBoard { } = change.detail.square { let (variant, color) = animated_variant(player); + let text_color = if matches!(variant, MappedTileVariant::Gone) { + Some(hex_color!("#a6caa0")) + } else { + None + }; let tile_layers = Tex::board_game_tile( variant, tile, orient(player), color, + text_color, None, TileDecoration::Grass, seed_at_coord, @@ -484,12 +496,18 @@ impl MappedBoard { } = change.detail.square { let (variant, color) = animated_variant(player); + let text_color = if matches!(variant, MappedTileVariant::Gone) { + Some(hex_color!("#a6caa0")) + } else { + None + }; let tile_layers = Tex::board_game_tile( variant, tile, orient(player), color, + text_color, None, TileDecoration::Grass, seed_at_coord, @@ -627,6 +645,7 @@ impl MappedBoard { render_as_swap.unwrap_or(*tile), orient(*player), color, + None, highlight, TileDecoration::Grass, seed_at_coord, @@ -652,6 +671,7 @@ impl MappedBoard { orient(*player), Some(validity_color), None, + None, TileDecoration::None, seed_at_coord, ) @@ -679,9 +699,10 @@ impl MappedBoard { let tile_layers = Tex::board_game_tile( MappedTileVariant::Healthy, tile_char, - Direction::North, + Direction::South, Some(self_color.lighten()), None, + None, TileDecoration::None, seed_at_coord, ); @@ -716,6 +737,7 @@ impl MappedBoard { Direction::North, Some(aesthetics.theme.ring_selected_hovered), None, + None, TileDecoration::Grass, seed_at_coord, ); @@ -1184,6 +1206,7 @@ impl MappedTiles { slot.character, slot.orientation, color, + None, slot.highlight, TileDecoration::None, 0, diff --git a/truncate_client/src/utils/tex/letters.rs b/truncate_client/src/utils/tex/letters.rs new file mode 100644 index 00000000..9f72d2be --- /dev/null +++ b/truncate_client/src/utils/tex/letters.rs @@ -0,0 +1,69 @@ +use truncate_core::board::Direction; + +use super::{tiles, TexQuad}; + +pub fn get_letter_quad(letter: char, orientation: Direction) -> TexQuad { + match orientation { + Direction::North => match letter.to_ascii_uppercase() { + 'A' => tiles::quad::LETTER_NORTH_A, + 'B' => tiles::quad::LETTER_NORTH_B, + 'C' => tiles::quad::LETTER_NORTH_C, + 'D' => tiles::quad::LETTER_NORTH_D, + 'E' => tiles::quad::LETTER_NORTH_E, + 'F' => tiles::quad::LETTER_NORTH_F, + 'G' => tiles::quad::LETTER_NORTH_G, + 'H' => tiles::quad::LETTER_NORTH_H, + 'I' => tiles::quad::LETTER_NORTH_I, + 'J' => tiles::quad::LETTER_NORTH_J, + 'K' => tiles::quad::LETTER_NORTH_K, + 'L' => tiles::quad::LETTER_NORTH_L, + 'M' => tiles::quad::LETTER_NORTH_M, + 'N' => tiles::quad::LETTER_NORTH_N, + 'O' => tiles::quad::LETTER_NORTH_O, + 'P' => tiles::quad::LETTER_NORTH_P, + 'Q' => tiles::quad::LETTER_NORTH_Q, + 'R' => tiles::quad::LETTER_NORTH_R, + 'S' => tiles::quad::LETTER_NORTH_S, + 'T' => tiles::quad::LETTER_NORTH_T, + 'U' => tiles::quad::LETTER_NORTH_U, + 'V' => tiles::quad::LETTER_NORTH_V, + 'W' => tiles::quad::LETTER_NORTH_W, + 'X' => tiles::quad::LETTER_NORTH_X, + 'Y' => tiles::quad::LETTER_NORTH_Y, + 'Z' => tiles::quad::LETTER_NORTH_Z, + ' ' => [tiles::NONE; 4], + _ => unimplemented!("No texture for character {}", letter), + }, + Direction::South => match letter.to_ascii_uppercase() { + 'A' => tiles::quad::LETTER_SOUTH_A, + 'B' => tiles::quad::LETTER_SOUTH_B, + 'C' => tiles::quad::LETTER_SOUTH_C, + 'D' => tiles::quad::LETTER_SOUTH_D, + 'E' => tiles::quad::LETTER_SOUTH_E, + 'F' => tiles::quad::LETTER_SOUTH_F, + 'G' => tiles::quad::LETTER_SOUTH_G, + 'H' => tiles::quad::LETTER_SOUTH_H, + 'I' => tiles::quad::LETTER_SOUTH_I, + 'J' => tiles::quad::LETTER_SOUTH_J, + 'K' => tiles::quad::LETTER_SOUTH_K, + 'L' => tiles::quad::LETTER_SOUTH_L, + 'M' => tiles::quad::LETTER_SOUTH_M, + 'N' => tiles::quad::LETTER_SOUTH_N, + 'O' => tiles::quad::LETTER_SOUTH_O, + 'P' => tiles::quad::LETTER_SOUTH_P, + 'Q' => tiles::quad::LETTER_SOUTH_Q, + 'R' => tiles::quad::LETTER_SOUTH_R, + 'S' => tiles::quad::LETTER_SOUTH_S, + 'T' => tiles::quad::LETTER_SOUTH_T, + 'U' => tiles::quad::LETTER_SOUTH_U, + 'V' => tiles::quad::LETTER_SOUTH_V, + 'W' => tiles::quad::LETTER_SOUTH_W, + 'X' => tiles::quad::LETTER_SOUTH_X, + 'Y' => tiles::quad::LETTER_SOUTH_Y, + 'Z' => tiles::quad::LETTER_SOUTH_Z, + ' ' => [tiles::NONE; 4], + _ => unimplemented!("No texture for character {}", letter), + }, + _ => unimplemented!("Need letter textures for direction {orientation:?}"), + } +} diff --git a/truncate_client/src/utils/tex/mod.rs b/truncate_client/src/utils/tex/mod.rs index dc4b8c25..20cb7a55 100644 --- a/truncate_client/src/utils/tex/mod.rs +++ b/truncate_client/src/utils/tex/mod.rs @@ -3,12 +3,14 @@ use epaint::{ hex_color, pos2, vec2, Color32, ColorImage, Mesh, Pos2, Rect, Shape, TextureHandle, TextureId, Vec2, }; +use letters::get_letter_quad; use truncate_core::board::{BoardDistances, Coordinate, Direction, SignedCoordinate, Square}; use crate::{app_outer::TEXTURE_MEASUREMENT, regions::lobby::BoardEditingMode}; use super::mapper::{quickrand, MappedTileVariant}; +pub mod letters; pub mod tiles; #[derive(Debug, Copy, Clone, PartialEq)] @@ -17,6 +19,12 @@ pub struct Tex { tint: Option, } +impl Tex { + pub fn current_tint(&self) -> Option { + self.tint + } +} + pub type TexQuad = [Tex; 4]; pub type IsFlipped = bool; pub type YOffset = isize; @@ -67,12 +75,14 @@ impl TexLayers { fn with_piece_character( mut self, char: char, - color: Color32, - flipped: IsFlipped, - y_offset: YOffset, + color: Option, + orientation: Direction, ) -> Self { - self.pieces - .push(PieceLayer::Character(char, color, flipped, y_offset)); + // TODO: Pull this from the theme + let text_color = color.unwrap_or(hex_color!("#444444")); + let quad = get_letter_quad(char, orientation); + let qq = quad.map(|q| q.tint(text_color)); + self.pieces.push(PieceLayer::Texture(qq, Some(text_color))); self } @@ -213,6 +223,7 @@ impl Tex { character: char, orientation: Direction, color: Option, + text_color: Option, highlight: Option, ) -> TexLayers { let mut layers = TexLayers::default() @@ -220,12 +231,7 @@ impl Tex { tiles::quad::GAME_PIECE.tint(color.unwrap_or(Color32::WHITE)), color, ) - .with_piece_character( - character, - hex_color!("#333333"), - orientation != Direction::North, - -1, - ); + .with_piece_character(character, text_color, orientation); if let Some(highlight) = highlight { layers = @@ -240,11 +246,12 @@ impl Tex { character: char, orientation: Direction, color: Option, + text_color: Option, highlight: Option, decoration: TileDecoration, seed: usize, ) -> TexLayers { - let mut layers = Tex::game_tile(character, orientation, color, highlight); + let mut layers = Tex::game_tile(character, orientation, color, text_color, highlight); if matches!(decoration, TileDecoration::Grass) { layers = layers.with_piece_texture( [ @@ -334,12 +341,7 @@ impl Tex { ], color, ) - .with_piece_character( - character, - hex_color!("#888888"), - orientation != Direction::North, - 0, - ); + .with_piece_character(character, text_color, orientation); } } layers @@ -428,10 +430,13 @@ impl Tex { .concat() } - fn artifact(color: Color32, neighbors: Vec, wind_at_coord: u8) -> TexLayers { + pub fn artifact(color: Color32, neighbors: Vec, wind_at_coord: u8) -> TexLayers { // TODO: Restore directional artifact textures as below: - let (artifact, glyph) = (tiles::quad::ARTIFACT, [tiles::quad::ARTIFACT_GLYPH]); + let (artifact, glyph) = ( + tiles::quad::ARTIFACT, + [tiles::quad::ARTIFACT_GLYPH.tint(color)], + ); // let (artifact, sails) = if matches!(neighbors[1], BGTexType::Land) { // ( @@ -497,7 +502,7 @@ impl Tex { .with_piece_texture(glyph[0], Some(color)) } - fn town(color: Color32, seed: usize, tick: u64, wind_at_coord: u8) -> TexLayers { + pub fn town(color: Color32, seed: usize, tick: u64, wind_at_coord: u8) -> TexLayers { let _anim_index = (quickrand(seed + 3) + tick as usize) % 30; let rand_house = |n: usize| match quickrand(n) { 0..=25 => ( @@ -607,7 +612,7 @@ impl Tex { .with_piece_texture(tinted, Some(color)) } - fn water( + pub fn water( seed: usize, source_coord: SignedCoordinate, board_size: (usize, usize), diff --git a/truncate_client/src/utils/tex/tiles.rs b/truncate_client/src/utils/tex/tiles.rs index d6817b0a..d58a3ca1 100644 --- a/truncate_client/src/utils/tex/tiles.rs +++ b/truncate_client/src/utils/tex/tiles.rs @@ -8,7 +8,7 @@ const fn t(tile: usize) -> Tex { Tex { tile, tint: None } } -pub const MAX_TILE: usize = 196; +pub const MAX_TILE: usize = 404; pub const NONE: Tex = t(0); pub const DEBUG: Tex = t(1); pub const BASE_GRASS: Tex = t(2); @@ -205,6 +205,214 @@ pub const TERRAIN_BUTTON_NW: Tex = t(192); pub const TERRAIN_BUTTON_NE: Tex = t(193); pub const TERRAIN_BUTTON_SW: Tex = t(194); pub const TERRAIN_BUTTON_SE: Tex = t(195); +pub const LETTER_SOUTH_A_NW: Tex = t(196); +pub const LETTER_SOUTH_A_NE: Tex = t(197); +pub const LETTER_SOUTH_A_SW: Tex = t(198); +pub const LETTER_SOUTH_A_SE: Tex = t(199); +pub const LETTER_SOUTH_B_NW: Tex = t(200); +pub const LETTER_SOUTH_B_NE: Tex = t(201); +pub const LETTER_SOUTH_B_SW: Tex = t(202); +pub const LETTER_SOUTH_B_SE: Tex = t(203); +pub const LETTER_SOUTH_C_NW: Tex = t(204); +pub const LETTER_SOUTH_C_NE: Tex = t(205); +pub const LETTER_SOUTH_C_SW: Tex = t(206); +pub const LETTER_SOUTH_C_SE: Tex = t(207); +pub const LETTER_SOUTH_D_NW: Tex = t(208); +pub const LETTER_SOUTH_D_NE: Tex = t(209); +pub const LETTER_SOUTH_D_SW: Tex = t(210); +pub const LETTER_SOUTH_D_SE: Tex = t(211); +pub const LETTER_SOUTH_E_NW: Tex = t(212); +pub const LETTER_SOUTH_E_NE: Tex = t(213); +pub const LETTER_SOUTH_E_SW: Tex = t(214); +pub const LETTER_SOUTH_E_SE: Tex = t(215); +pub const LETTER_SOUTH_F_NW: Tex = t(216); +pub const LETTER_SOUTH_F_NE: Tex = t(217); +pub const LETTER_SOUTH_F_SW: Tex = t(218); +pub const LETTER_SOUTH_F_SE: Tex = t(219); +pub const LETTER_SOUTH_G_NW: Tex = t(220); +pub const LETTER_SOUTH_G_NE: Tex = t(221); +pub const LETTER_SOUTH_G_SW: Tex = t(222); +pub const LETTER_SOUTH_G_SE: Tex = t(223); +pub const LETTER_SOUTH_H_NW: Tex = t(224); +pub const LETTER_SOUTH_H_NE: Tex = t(225); +pub const LETTER_SOUTH_H_SW: Tex = t(226); +pub const LETTER_SOUTH_H_SE: Tex = t(227); +pub const LETTER_SOUTH_I_NW: Tex = t(228); +pub const LETTER_SOUTH_I_NE: Tex = t(229); +pub const LETTER_SOUTH_I_SW: Tex = t(230); +pub const LETTER_SOUTH_I_SE: Tex = t(231); +pub const LETTER_SOUTH_J_NW: Tex = t(232); +pub const LETTER_SOUTH_J_NE: Tex = t(233); +pub const LETTER_SOUTH_J_SW: Tex = t(234); +pub const LETTER_SOUTH_J_SE: Tex = t(235); +pub const LETTER_SOUTH_K_NW: Tex = t(236); +pub const LETTER_SOUTH_K_NE: Tex = t(237); +pub const LETTER_SOUTH_K_SW: Tex = t(238); +pub const LETTER_SOUTH_K_SE: Tex = t(239); +pub const LETTER_SOUTH_L_NW: Tex = t(240); +pub const LETTER_SOUTH_L_NE: Tex = t(241); +pub const LETTER_SOUTH_L_SW: Tex = t(242); +pub const LETTER_SOUTH_L_SE: Tex = t(243); +pub const LETTER_SOUTH_M_NW: Tex = t(244); +pub const LETTER_SOUTH_M_NE: Tex = t(245); +pub const LETTER_SOUTH_M_SW: Tex = t(246); +pub const LETTER_SOUTH_M_SE: Tex = t(247); +pub const LETTER_SOUTH_N_NW: Tex = t(248); +pub const LETTER_SOUTH_N_NE: Tex = t(249); +pub const LETTER_SOUTH_N_SW: Tex = t(250); +pub const LETTER_SOUTH_N_SE: Tex = t(251); +pub const LETTER_SOUTH_O_NW: Tex = t(252); +pub const LETTER_SOUTH_O_NE: Tex = t(253); +pub const LETTER_SOUTH_O_SW: Tex = t(254); +pub const LETTER_SOUTH_O_SE: Tex = t(255); +pub const LETTER_SOUTH_P_NW: Tex = t(256); +pub const LETTER_SOUTH_P_NE: Tex = t(257); +pub const LETTER_SOUTH_P_SW: Tex = t(258); +pub const LETTER_SOUTH_P_SE: Tex = t(259); +pub const LETTER_SOUTH_Q_NW: Tex = t(260); +pub const LETTER_SOUTH_Q_NE: Tex = t(261); +pub const LETTER_SOUTH_Q_SW: Tex = t(262); +pub const LETTER_SOUTH_Q_SE: Tex = t(263); +pub const LETTER_SOUTH_R_NW: Tex = t(264); +pub const LETTER_SOUTH_R_NE: Tex = t(265); +pub const LETTER_SOUTH_R_SW: Tex = t(266); +pub const LETTER_SOUTH_R_SE: Tex = t(267); +pub const LETTER_SOUTH_S_NW: Tex = t(268); +pub const LETTER_SOUTH_S_NE: Tex = t(269); +pub const LETTER_SOUTH_S_SW: Tex = t(270); +pub const LETTER_SOUTH_S_SE: Tex = t(271); +pub const LETTER_SOUTH_T_NW: Tex = t(272); +pub const LETTER_SOUTH_T_NE: Tex = t(273); +pub const LETTER_SOUTH_T_SW: Tex = t(274); +pub const LETTER_SOUTH_T_SE: Tex = t(275); +pub const LETTER_SOUTH_U_NW: Tex = t(276); +pub const LETTER_SOUTH_U_NE: Tex = t(277); +pub const LETTER_SOUTH_U_SW: Tex = t(278); +pub const LETTER_SOUTH_U_SE: Tex = t(279); +pub const LETTER_SOUTH_V_NW: Tex = t(280); +pub const LETTER_SOUTH_V_NE: Tex = t(281); +pub const LETTER_SOUTH_V_SW: Tex = t(282); +pub const LETTER_SOUTH_V_SE: Tex = t(283); +pub const LETTER_SOUTH_W_NW: Tex = t(284); +pub const LETTER_SOUTH_W_NE: Tex = t(285); +pub const LETTER_SOUTH_W_SW: Tex = t(286); +pub const LETTER_SOUTH_W_SE: Tex = t(287); +pub const LETTER_SOUTH_X_NW: Tex = t(288); +pub const LETTER_SOUTH_X_NE: Tex = t(289); +pub const LETTER_SOUTH_X_SW: Tex = t(290); +pub const LETTER_SOUTH_X_SE: Tex = t(291); +pub const LETTER_SOUTH_Y_NW: Tex = t(292); +pub const LETTER_SOUTH_Y_NE: Tex = t(293); +pub const LETTER_SOUTH_Y_SW: Tex = t(294); +pub const LETTER_SOUTH_Y_SE: Tex = t(295); +pub const LETTER_SOUTH_Z_NW: Tex = t(296); +pub const LETTER_SOUTH_Z_NE: Tex = t(297); +pub const LETTER_SOUTH_Z_SW: Tex = t(298); +pub const LETTER_SOUTH_Z_SE: Tex = t(299); +pub const LETTER_NORTH_A_NW: Tex = t(300); +pub const LETTER_NORTH_A_NE: Tex = t(301); +pub const LETTER_NORTH_A_SW: Tex = t(302); +pub const LETTER_NORTH_A_SE: Tex = t(303); +pub const LETTER_NORTH_B_NW: Tex = t(304); +pub const LETTER_NORTH_B_NE: Tex = t(305); +pub const LETTER_NORTH_B_SW: Tex = t(306); +pub const LETTER_NORTH_B_SE: Tex = t(307); +pub const LETTER_NORTH_C_NW: Tex = t(308); +pub const LETTER_NORTH_C_NE: Tex = t(309); +pub const LETTER_NORTH_C_SW: Tex = t(310); +pub const LETTER_NORTH_C_SE: Tex = t(311); +pub const LETTER_NORTH_D_NW: Tex = t(312); +pub const LETTER_NORTH_D_NE: Tex = t(313); +pub const LETTER_NORTH_D_SW: Tex = t(314); +pub const LETTER_NORTH_D_SE: Tex = t(315); +pub const LETTER_NORTH_E_NW: Tex = t(316); +pub const LETTER_NORTH_E_NE: Tex = t(317); +pub const LETTER_NORTH_E_SW: Tex = t(318); +pub const LETTER_NORTH_E_SE: Tex = t(319); +pub const LETTER_NORTH_F_NW: Tex = t(320); +pub const LETTER_NORTH_F_NE: Tex = t(321); +pub const LETTER_NORTH_F_SW: Tex = t(322); +pub const LETTER_NORTH_F_SE: Tex = t(323); +pub const LETTER_NORTH_G_NW: Tex = t(324); +pub const LETTER_NORTH_G_NE: Tex = t(325); +pub const LETTER_NORTH_G_SW: Tex = t(326); +pub const LETTER_NORTH_G_SE: Tex = t(327); +pub const LETTER_NORTH_H_NW: Tex = t(328); +pub const LETTER_NORTH_H_NE: Tex = t(329); +pub const LETTER_NORTH_H_SW: Tex = t(330); +pub const LETTER_NORTH_H_SE: Tex = t(331); +pub const LETTER_NORTH_I_NW: Tex = t(332); +pub const LETTER_NORTH_I_NE: Tex = t(333); +pub const LETTER_NORTH_I_SW: Tex = t(334); +pub const LETTER_NORTH_I_SE: Tex = t(335); +pub const LETTER_NORTH_J_NW: Tex = t(336); +pub const LETTER_NORTH_J_NE: Tex = t(337); +pub const LETTER_NORTH_J_SW: Tex = t(338); +pub const LETTER_NORTH_J_SE: Tex = t(339); +pub const LETTER_NORTH_K_NW: Tex = t(340); +pub const LETTER_NORTH_K_NE: Tex = t(341); +pub const LETTER_NORTH_K_SW: Tex = t(342); +pub const LETTER_NORTH_K_SE: Tex = t(343); +pub const LETTER_NORTH_L_NW: Tex = t(344); +pub const LETTER_NORTH_L_NE: Tex = t(345); +pub const LETTER_NORTH_L_SW: Tex = t(346); +pub const LETTER_NORTH_L_SE: Tex = t(347); +pub const LETTER_NORTH_M_NW: Tex = t(348); +pub const LETTER_NORTH_M_NE: Tex = t(349); +pub const LETTER_NORTH_M_SW: Tex = t(350); +pub const LETTER_NORTH_M_SE: Tex = t(351); +pub const LETTER_NORTH_N_NW: Tex = t(352); +pub const LETTER_NORTH_N_NE: Tex = t(353); +pub const LETTER_NORTH_N_SW: Tex = t(354); +pub const LETTER_NORTH_N_SE: Tex = t(355); +pub const LETTER_NORTH_O_NW: Tex = t(356); +pub const LETTER_NORTH_O_NE: Tex = t(357); +pub const LETTER_NORTH_O_SW: Tex = t(358); +pub const LETTER_NORTH_O_SE: Tex = t(359); +pub const LETTER_NORTH_P_NW: Tex = t(360); +pub const LETTER_NORTH_P_NE: Tex = t(361); +pub const LETTER_NORTH_P_SW: Tex = t(362); +pub const LETTER_NORTH_P_SE: Tex = t(363); +pub const LETTER_NORTH_Q_NW: Tex = t(364); +pub const LETTER_NORTH_Q_NE: Tex = t(365); +pub const LETTER_NORTH_Q_SW: Tex = t(366); +pub const LETTER_NORTH_Q_SE: Tex = t(367); +pub const LETTER_NORTH_R_NW: Tex = t(368); +pub const LETTER_NORTH_R_NE: Tex = t(369); +pub const LETTER_NORTH_R_SW: Tex = t(370); +pub const LETTER_NORTH_R_SE: Tex = t(371); +pub const LETTER_NORTH_S_NW: Tex = t(372); +pub const LETTER_NORTH_S_NE: Tex = t(373); +pub const LETTER_NORTH_S_SW: Tex = t(374); +pub const LETTER_NORTH_S_SE: Tex = t(375); +pub const LETTER_NORTH_T_NW: Tex = t(376); +pub const LETTER_NORTH_T_NE: Tex = t(377); +pub const LETTER_NORTH_T_SW: Tex = t(378); +pub const LETTER_NORTH_T_SE: Tex = t(379); +pub const LETTER_NORTH_U_NW: Tex = t(380); +pub const LETTER_NORTH_U_NE: Tex = t(381); +pub const LETTER_NORTH_U_SW: Tex = t(382); +pub const LETTER_NORTH_U_SE: Tex = t(383); +pub const LETTER_NORTH_V_NW: Tex = t(384); +pub const LETTER_NORTH_V_NE: Tex = t(385); +pub const LETTER_NORTH_V_SW: Tex = t(386); +pub const LETTER_NORTH_V_SE: Tex = t(387); +pub const LETTER_NORTH_W_NW: Tex = t(388); +pub const LETTER_NORTH_W_NE: Tex = t(389); +pub const LETTER_NORTH_W_SW: Tex = t(390); +pub const LETTER_NORTH_W_SE: Tex = t(391); +pub const LETTER_NORTH_X_NW: Tex = t(392); +pub const LETTER_NORTH_X_NE: Tex = t(393); +pub const LETTER_NORTH_X_SW: Tex = t(394); +pub const LETTER_NORTH_X_SE: Tex = t(395); +pub const LETTER_NORTH_Y_NW: Tex = t(396); +pub const LETTER_NORTH_Y_NE: Tex = t(397); +pub const LETTER_NORTH_Y_SW: Tex = t(398); +pub const LETTER_NORTH_Y_SE: Tex = t(399); +pub const LETTER_NORTH_Z_NW: Tex = t(400); +pub const LETTER_NORTH_Z_NE: Tex = t(401); +pub const LETTER_NORTH_Z_SW: Tex = t(402); +pub const LETTER_NORTH_Z_SE: Tex = t(403); pub mod quad { use super::*; @@ -251,4 +459,56 @@ pub mod quad { pub const ARTIFACT_BUTTON: TexQuad = [ARTIFACT_BUTTON_NW, ARTIFACT_BUTTON_NE, ARTIFACT_BUTTON_SE, ARTIFACT_BUTTON_SW]; pub const ARTIFACT_BUTTON_GLYPH: TexQuad = [ARTIFACT_BUTTON_GLYPH_NW, ARTIFACT_BUTTON_GLYPH_NE, ARTIFACT_BUTTON_GLYPH_SE, ARTIFACT_BUTTON_GLYPH_SW]; pub const TERRAIN_BUTTON: TexQuad = [TERRAIN_BUTTON_NW, TERRAIN_BUTTON_NE, TERRAIN_BUTTON_SE, TERRAIN_BUTTON_SW]; + pub const LETTER_SOUTH_A: TexQuad = [LETTER_SOUTH_A_NW, LETTER_SOUTH_A_NE, LETTER_SOUTH_A_SE, LETTER_SOUTH_A_SW]; + pub const LETTER_SOUTH_B: TexQuad = [LETTER_SOUTH_B_NW, LETTER_SOUTH_B_NE, LETTER_SOUTH_B_SE, LETTER_SOUTH_B_SW]; + pub const LETTER_SOUTH_C: TexQuad = [LETTER_SOUTH_C_NW, LETTER_SOUTH_C_NE, LETTER_SOUTH_C_SE, LETTER_SOUTH_C_SW]; + pub const LETTER_SOUTH_D: TexQuad = [LETTER_SOUTH_D_NW, LETTER_SOUTH_D_NE, LETTER_SOUTH_D_SE, LETTER_SOUTH_D_SW]; + pub const LETTER_SOUTH_E: TexQuad = [LETTER_SOUTH_E_NW, LETTER_SOUTH_E_NE, LETTER_SOUTH_E_SE, LETTER_SOUTH_E_SW]; + pub const LETTER_SOUTH_F: TexQuad = [LETTER_SOUTH_F_NW, LETTER_SOUTH_F_NE, LETTER_SOUTH_F_SE, LETTER_SOUTH_F_SW]; + pub const LETTER_SOUTH_G: TexQuad = [LETTER_SOUTH_G_NW, LETTER_SOUTH_G_NE, LETTER_SOUTH_G_SE, LETTER_SOUTH_G_SW]; + pub const LETTER_SOUTH_H: TexQuad = [LETTER_SOUTH_H_NW, LETTER_SOUTH_H_NE, LETTER_SOUTH_H_SE, LETTER_SOUTH_H_SW]; + pub const LETTER_SOUTH_I: TexQuad = [LETTER_SOUTH_I_NW, LETTER_SOUTH_I_NE, LETTER_SOUTH_I_SE, LETTER_SOUTH_I_SW]; + pub const LETTER_SOUTH_J: TexQuad = [LETTER_SOUTH_J_NW, LETTER_SOUTH_J_NE, LETTER_SOUTH_J_SE, LETTER_SOUTH_J_SW]; + pub const LETTER_SOUTH_K: TexQuad = [LETTER_SOUTH_K_NW, LETTER_SOUTH_K_NE, LETTER_SOUTH_K_SE, LETTER_SOUTH_K_SW]; + pub const LETTER_SOUTH_L: TexQuad = [LETTER_SOUTH_L_NW, LETTER_SOUTH_L_NE, LETTER_SOUTH_L_SE, LETTER_SOUTH_L_SW]; + pub const LETTER_SOUTH_M: TexQuad = [LETTER_SOUTH_M_NW, LETTER_SOUTH_M_NE, LETTER_SOUTH_M_SE, LETTER_SOUTH_M_SW]; + pub const LETTER_SOUTH_N: TexQuad = [LETTER_SOUTH_N_NW, LETTER_SOUTH_N_NE, LETTER_SOUTH_N_SE, LETTER_SOUTH_N_SW]; + pub const LETTER_SOUTH_O: TexQuad = [LETTER_SOUTH_O_NW, LETTER_SOUTH_O_NE, LETTER_SOUTH_O_SE, LETTER_SOUTH_O_SW]; + pub const LETTER_SOUTH_P: TexQuad = [LETTER_SOUTH_P_NW, LETTER_SOUTH_P_NE, LETTER_SOUTH_P_SE, LETTER_SOUTH_P_SW]; + pub const LETTER_SOUTH_Q: TexQuad = [LETTER_SOUTH_Q_NW, LETTER_SOUTH_Q_NE, LETTER_SOUTH_Q_SE, LETTER_SOUTH_Q_SW]; + pub const LETTER_SOUTH_R: TexQuad = [LETTER_SOUTH_R_NW, LETTER_SOUTH_R_NE, LETTER_SOUTH_R_SE, LETTER_SOUTH_R_SW]; + pub const LETTER_SOUTH_S: TexQuad = [LETTER_SOUTH_S_NW, LETTER_SOUTH_S_NE, LETTER_SOUTH_S_SE, LETTER_SOUTH_S_SW]; + pub const LETTER_SOUTH_T: TexQuad = [LETTER_SOUTH_T_NW, LETTER_SOUTH_T_NE, LETTER_SOUTH_T_SE, LETTER_SOUTH_T_SW]; + pub const LETTER_SOUTH_U: TexQuad = [LETTER_SOUTH_U_NW, LETTER_SOUTH_U_NE, LETTER_SOUTH_U_SE, LETTER_SOUTH_U_SW]; + pub const LETTER_SOUTH_V: TexQuad = [LETTER_SOUTH_V_NW, LETTER_SOUTH_V_NE, LETTER_SOUTH_V_SE, LETTER_SOUTH_V_SW]; + pub const LETTER_SOUTH_W: TexQuad = [LETTER_SOUTH_W_NW, LETTER_SOUTH_W_NE, LETTER_SOUTH_W_SE, LETTER_SOUTH_W_SW]; + pub const LETTER_SOUTH_X: TexQuad = [LETTER_SOUTH_X_NW, LETTER_SOUTH_X_NE, LETTER_SOUTH_X_SE, LETTER_SOUTH_X_SW]; + pub const LETTER_SOUTH_Y: TexQuad = [LETTER_SOUTH_Y_NW, LETTER_SOUTH_Y_NE, LETTER_SOUTH_Y_SE, LETTER_SOUTH_Y_SW]; + pub const LETTER_SOUTH_Z: TexQuad = [LETTER_SOUTH_Z_NW, LETTER_SOUTH_Z_NE, LETTER_SOUTH_Z_SE, LETTER_SOUTH_Z_SW]; + pub const LETTER_NORTH_A: TexQuad = [LETTER_NORTH_A_NW, LETTER_NORTH_A_NE, LETTER_NORTH_A_SE, LETTER_NORTH_A_SW]; + pub const LETTER_NORTH_B: TexQuad = [LETTER_NORTH_B_NW, LETTER_NORTH_B_NE, LETTER_NORTH_B_SE, LETTER_NORTH_B_SW]; + pub const LETTER_NORTH_C: TexQuad = [LETTER_NORTH_C_NW, LETTER_NORTH_C_NE, LETTER_NORTH_C_SE, LETTER_NORTH_C_SW]; + pub const LETTER_NORTH_D: TexQuad = [LETTER_NORTH_D_NW, LETTER_NORTH_D_NE, LETTER_NORTH_D_SE, LETTER_NORTH_D_SW]; + pub const LETTER_NORTH_E: TexQuad = [LETTER_NORTH_E_NW, LETTER_NORTH_E_NE, LETTER_NORTH_E_SE, LETTER_NORTH_E_SW]; + pub const LETTER_NORTH_F: TexQuad = [LETTER_NORTH_F_NW, LETTER_NORTH_F_NE, LETTER_NORTH_F_SE, LETTER_NORTH_F_SW]; + pub const LETTER_NORTH_G: TexQuad = [LETTER_NORTH_G_NW, LETTER_NORTH_G_NE, LETTER_NORTH_G_SE, LETTER_NORTH_G_SW]; + pub const LETTER_NORTH_H: TexQuad = [LETTER_NORTH_H_NW, LETTER_NORTH_H_NE, LETTER_NORTH_H_SE, LETTER_NORTH_H_SW]; + pub const LETTER_NORTH_I: TexQuad = [LETTER_NORTH_I_NW, LETTER_NORTH_I_NE, LETTER_NORTH_I_SE, LETTER_NORTH_I_SW]; + pub const LETTER_NORTH_J: TexQuad = [LETTER_NORTH_J_NW, LETTER_NORTH_J_NE, LETTER_NORTH_J_SE, LETTER_NORTH_J_SW]; + pub const LETTER_NORTH_K: TexQuad = [LETTER_NORTH_K_NW, LETTER_NORTH_K_NE, LETTER_NORTH_K_SE, LETTER_NORTH_K_SW]; + pub const LETTER_NORTH_L: TexQuad = [LETTER_NORTH_L_NW, LETTER_NORTH_L_NE, LETTER_NORTH_L_SE, LETTER_NORTH_L_SW]; + pub const LETTER_NORTH_M: TexQuad = [LETTER_NORTH_M_NW, LETTER_NORTH_M_NE, LETTER_NORTH_M_SE, LETTER_NORTH_M_SW]; + pub const LETTER_NORTH_N: TexQuad = [LETTER_NORTH_N_NW, LETTER_NORTH_N_NE, LETTER_NORTH_N_SE, LETTER_NORTH_N_SW]; + pub const LETTER_NORTH_O: TexQuad = [LETTER_NORTH_O_NW, LETTER_NORTH_O_NE, LETTER_NORTH_O_SE, LETTER_NORTH_O_SW]; + pub const LETTER_NORTH_P: TexQuad = [LETTER_NORTH_P_NW, LETTER_NORTH_P_NE, LETTER_NORTH_P_SE, LETTER_NORTH_P_SW]; + pub const LETTER_NORTH_Q: TexQuad = [LETTER_NORTH_Q_NW, LETTER_NORTH_Q_NE, LETTER_NORTH_Q_SE, LETTER_NORTH_Q_SW]; + pub const LETTER_NORTH_R: TexQuad = [LETTER_NORTH_R_NW, LETTER_NORTH_R_NE, LETTER_NORTH_R_SE, LETTER_NORTH_R_SW]; + pub const LETTER_NORTH_S: TexQuad = [LETTER_NORTH_S_NW, LETTER_NORTH_S_NE, LETTER_NORTH_S_SE, LETTER_NORTH_S_SW]; + pub const LETTER_NORTH_T: TexQuad = [LETTER_NORTH_T_NW, LETTER_NORTH_T_NE, LETTER_NORTH_T_SE, LETTER_NORTH_T_SW]; + pub const LETTER_NORTH_U: TexQuad = [LETTER_NORTH_U_NW, LETTER_NORTH_U_NE, LETTER_NORTH_U_SE, LETTER_NORTH_U_SW]; + pub const LETTER_NORTH_V: TexQuad = [LETTER_NORTH_V_NW, LETTER_NORTH_V_NE, LETTER_NORTH_V_SE, LETTER_NORTH_V_SW]; + pub const LETTER_NORTH_W: TexQuad = [LETTER_NORTH_W_NW, LETTER_NORTH_W_NE, LETTER_NORTH_W_SE, LETTER_NORTH_W_SW]; + pub const LETTER_NORTH_X: TexQuad = [LETTER_NORTH_X_NW, LETTER_NORTH_X_NE, LETTER_NORTH_X_SE, LETTER_NORTH_X_SW]; + pub const LETTER_NORTH_Y: TexQuad = [LETTER_NORTH_Y_NW, LETTER_NORTH_Y_NE, LETTER_NORTH_Y_SE, LETTER_NORTH_Y_SW]; + pub const LETTER_NORTH_Z: TexQuad = [LETTER_NORTH_Z_NW, LETTER_NORTH_Z_NE, LETTER_NORTH_Z_SE, LETTER_NORTH_Z_SW]; } \ No newline at end of file diff --git a/truncate_client/src/utils/text.rs b/truncate_client/src/utils/text.rs index 031474e1..ad8a213a 100644 --- a/truncate_client/src/utils/text.rs +++ b/truncate_client/src/utils/text.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use eframe::egui::{self, Sense}; +use eframe::egui::{self, Align, Sense}; use epaint::{emath::Align2, pos2, vec2, Color32, Galley, Pos2, Rect, TextureHandle, Vec2}; use super::tex::{paint_dialog_background, render_texs_clockwise, Tex, Tint}; @@ -14,6 +14,7 @@ pub enum TextStyle { pub struct TextHelper<'a> { original_text: &'a str, + centered: bool, size: f32, max_width: Option, galley: Arc, @@ -26,17 +27,44 @@ impl<'a> TextHelper<'a> { size: f32, max_width: Option, ui: &mut egui::Ui, + ) -> TextHelper<'a> { + TextHelper::heavy_inner(text, size, max_width, false, ui) + } + pub fn heavy_centered( + text: &'a str, + size: f32, + max_width: Option, + ui: &mut egui::Ui, + ) -> TextHelper<'a> { + TextHelper::heavy_inner(text, size, max_width, true, ui) + } + + fn heavy_inner( + text: &'a str, + size: f32, + max_width: Option, + centered: bool, + ui: &mut egui::Ui, ) -> TextHelper<'a> { let font = egui::FontSelection::FontId(egui::FontId { - size: size, + size, family: egui::FontFamily::Name("Truncate-Heavy".into()), }); - let galley = egui::WidgetText::RichText( + + let mut lj = egui::WidgetText::RichText( egui::RichText::new(text).line_height(Some((size * 1.34).round())), ) - .into_galley(ui, None, max_width.unwrap_or(10000.0), font); + .into_layout_job(&egui::Style::default(), font, Align::Center); + if centered { + lj.halign = Align::Center; + } + lj.wrap.max_width = max_width.unwrap_or(10000.0); + + let galley = ui.ctx().fonts(|f| f.layout_job(lj)); + Self { original_text: text, + centered, size, max_width, galley, @@ -49,19 +77,44 @@ impl<'a> TextHelper<'a> { size: f32, max_width: Option, ui: &mut egui::Ui, + ) -> TextHelper<'a> { + TextHelper::light_inner(text, size, max_width, false, ui) + } + pub fn light_centered( + text: &'a str, + size: f32, + max_width: Option, + ui: &mut egui::Ui, + ) -> TextHelper<'a> { + TextHelper::light_inner(text, size, max_width, true, ui) + } + + fn light_inner( + text: &'a str, + size: f32, + max_width: Option, + centered: bool, + ui: &mut egui::Ui, ) -> TextHelper<'a> { let font = egui::FontSelection::FontId(egui::FontId { - size: size, + size, family: egui::FontFamily::Proportional, }); - let galley = egui::WidgetText::RichText(egui::RichText::new(text)).into_galley( - ui, - None, - max_width.unwrap_or(10000.0), + let mut lj = egui::WidgetText::RichText(egui::RichText::new(text)).into_layout_job( + &egui::Style::default(), font, + Align::Center, ); + if centered { + lj.halign = Align::Center; + } + lj.wrap.max_width = max_width.unwrap_or(10000.0); + + let galley = ui.ctx().fonts(|f| f.layout_job(lj)); + Self { original_text: text, + centered, size, max_width, galley, @@ -77,32 +130,49 @@ impl<'a> TextHelper<'a> { self.galley.mesh_bounds.size() } - pub fn get_partial_slice(&self, time_passed: f32, ui: &mut egui::Ui) -> Option { + pub fn get_partial_char_slice(&self, time_passed: f32, ui: &mut egui::Ui) -> Option { + let animation_duration = self.original_text.len() as f32 * DIALOG_TIME_PER_CHAR; + if time_passed > animation_duration { + return None; + } + + let char_point = + (self.original_text.len() as f32 * (time_passed / animation_duration)) as usize; + self.inner_partial_slice(char_point, ui) + } + + pub fn get_partial_word_slice(&self, time_passed: f32, ui: &mut egui::Ui) -> Option { let mut breaks = self .original_text .char_indices() .filter_map(|(i, c)| if c == ' ' { Some(i) } else { None }) .collect::>(); - breaks.push(self.original_text.len() - 1); + breaks.push(self.original_text.len()); let animation_duration = breaks.len() as f32 * DIALOG_TIME_PER_CHAR; if time_passed > animation_duration { return None; } let word_count = (breaks.len() as f32 * (time_passed / animation_duration)) as usize; - let shortened_text = &self.original_text[0..=breaks[word_count.saturating_sub(1)]]; + self.inner_partial_slice(breaks[word_count.saturating_sub(1)], ui) + } + + fn inner_partial_slice(&self, at: usize, ui: &mut egui::Ui) -> Option { + let shortened_text = &self.original_text[0..at]; match self.text_style { - TextStyle::Light => Some(TextHelper::light( + TextStyle::Light => Some(TextHelper::light_inner( &shortened_text, self.size, self.max_width, + self.centered, ui, )), - TextStyle::Heavy => Some(TextHelper::heavy( + TextStyle::Heavy => Some(TextHelper::heavy_inner( &shortened_text, self.size, self.max_width, + self.centered, ui, )), } @@ -116,10 +186,14 @@ impl<'a> TextHelper<'a> { pub fn paint_within(self, bounds: Rect, alignment: Align2, color: Color32, ui: &mut egui::Ui) { let dims = self.mesh_size(); let Align2([ha, va]) = alignment; - let x_pos = match ha { - egui::Align::Min => bounds.left(), - egui::Align::Center => bounds.left() + (bounds.width() - dims.x) / 2.0, - egui::Align::Max => bounds.left() + (bounds.width() - dims.x), + let x_pos = match self.galley.job.halign { + Align::Min => match ha { + egui::Align::Min => bounds.left(), + egui::Align::Center => bounds.left() + (bounds.width() - dims.x) / 2.0, + egui::Align::Max => bounds.left() + (bounds.width() - dims.x), + }, + Align::Center => bounds.center().x, + Align::Max => unimplemented!("No right aligned text."), }; let y_pos = match va { egui::Align::Min => bounds.top(), diff --git a/truncate_client/src/utils/theming.rs b/truncate_client/src/utils/theming.rs index b067a9dd..86c5d889 100644 --- a/truncate_client/src/utils/theming.rs +++ b/truncate_client/src/utils/theming.rs @@ -12,6 +12,7 @@ pub struct Theme { pub text: Color32, pub faded: Color32, pub button_primary: Color32, + pub button_emphasis: Color32, pub button_secondary: Color32, pub button_scary: Color32, pub ring_selected: Color32, @@ -40,6 +41,7 @@ impl Theme { text: hex_color!("#333333"), faded: hex_color!("#777777"), button_primary: hex_color!("#FFDE85"), + button_emphasis: hex_color!("#EDBBFF"), button_secondary: hex_color!("#B1DAF9"), button_scary: hex_color!("#FF8E8E"), ring_selected: hex_color!("#FFBE0B"), @@ -68,6 +70,7 @@ impl Theme { text: hex_color!("#333333"), faded: hex_color!("#777777"), button_primary: hex_color!("#FFDE85"), + button_emphasis: hex_color!("#EDBBFF"), button_secondary: hex_color!("#B1DAF9"), button_scary: hex_color!("#FF8E8E"), ring_selected: hex_color!("#FFBE0B"), @@ -96,6 +99,7 @@ impl Theme { text: hex_color!("#333333"), faded: hex_color!("#777777"), button_primary: hex_color!("#FFDE85"), + button_emphasis: hex_color!("#EDBBFF"), button_secondary: hex_color!("#B1DAF9"), button_scary: hex_color!("#FF8E8E"), ring_selected: hex_color!("#FFBE0B"), @@ -124,6 +128,7 @@ impl Theme { text: hex_color!("#FFFFFF"), faded: hex_color!("#CCCCCC"), button_primary: hex_color!("#8F6900"), + button_emphasis: hex_color!("#EDBBFF"), button_secondary: hex_color!("#094472"), button_scary: hex_color!("#FF8E8E"), ring_selected: hex_color!("#FFBE0B"), diff --git a/truncate_client/tutorials/rule_card.yml b/truncate_client/tutorials/rule_card.yml new file mode 100644 index 00000000..a25c4b0e --- /dev/null +++ b/truncate_client/tutorials/rule_card.yml @@ -0,0 +1,269 @@ +sections: + - title: Orientation + examples: + - ~ ~ ~ ~ ~ ~ ~ ~ ~ + - W^ ~ ~ ~ ~ ~ ~ ~ ~ + - W^ ~ ~ ~ ~ ~ ~ ~ ~ + - W ~ ~ ~ ~ ~ ~ ~ w^ + - W ~ ~ ~ ~ ~ ~ ~ w^ + - W O^ ~ ~ ~ ~ ~ ~ w + - W O^ ~ ~ ~ ~ ~ ~ w + - W O ~ ~ ~ ~ ~ o^ w + - W O ~ ~ ~ ~ ~ o^ w + - W O R^ ~ ~ ~ ~ o w + - W O R^ ~ ~ ~ ~ o w + - W O R ~ ~ ~ r^ o w + - W O R ~ ~ ~ r^ o w + - W O R D^ ~ ~ r o w + - W O R D^ ~ ~ r o w + - W O R D ~ d^ r o w + - W O R D ~ d^ r o w + - W O R D ~ d r o w + description: |- + Each turn you play one tile. + Your words read top down and left to right, + your opponent's are the opposite. + - title: Long words beat short words + examples: + - W O R D ~ d r o w + - W O R D ~ d r o w + - W O R D ~ d r o w + - W O R D Y^ d r o w + - W O R D Y d* r o w + - W O R D Y d* r* o w + - W O R D Y d* r* o* w + - W O R D Y d* r* o* w* + - W O R D Y d- r- o- w- + - W O R D Y ~ ~ ~ ~ + description: |- + Words battle when they touch, and long words beat short words. + "WORDY" is longer than "WORD", so "WORD" is removed from the board. + - title: Valid words beat invalid words + examples: + - B E ~ x e d r o w + - B E ~ x e d r o w + - B E ~ x e d r o w + - B E T^ x e d r o w + - B E T x* e d r o w + - B E T x* e* d r o w + - B E T x* e* d* r o w + - B E T x* e* d* r* o w + - B E T x* e* d* r* o* w + - B E T x* e* d* r* o* w* + - B E T x- e- d- r- o- w- + - B E T ~ ~ ~ ~ ~ ~ + description: |- + Valid words beat invalid words, regardless of length. + - title: Artifacts + examples: + - $1 W^ ~ ~ ~ ~ ~ ~ $0 + - $1 W^ ~ ~ ~ ~ ~ ~ $0 + - $1 W I^ ~ ~ ~ ~ ~ $0 + - $1 W I^ ~ ~ ~ ~ ~ $0 + - $1 W I N^ ~ ~ ~ ~ $0 + - $1 W I N^ ~ ~ ~ ~ $0 + - $1 W I N N^ ~ ~ ~ $0 + - $1 W I N N^ ~ ~ ~ $0 + - $1 W I N N I^ ~ ~ $0 + - $1 W I N N I^ ~ ~ $0 + - $1 W I N N I N^ ~ $0 + - $1 W I N N I N^ ~ $0 + - $1 W I N N I N G^ $0 + - $1 W I N N I N G^ $0 + - $1 W I N N I N G $0 + description: |- + You start the game at your artifact, + and can win by touching your opponent's artifact with a valid word. + - title: Towns + examples: + - |- + ~ ~ ~ ~ ~ ~ +0 ~ ~ + $1 W I N ~ ~ ~ ~ $0 + - |- + ~ ~ ~ ~ ~ ~ +0 ~ ~ + $1 W I N ~ ~ ~ ~ $0 + - |- + ~ ~ ~ ~ ~ ~ +0 ~ ~ + $1 W I N ~ ~ ~ ~ $0 + - |- + ~ ~ ~ ~ ~ ~ +0 ~ ~ + $1 W I N N^ ~ ~ ~ $0 + - |- + ~ ~ ~ ~ ~ ~ +0 ~ ~ + $1 W I N N^ ~ ~ ~ $0 + - |- + ~ ~ ~ ~ ~ ~ +0 ~ ~ + $1 W I N N E^ ~ ~ $0 + - |- + ~ ~ ~ ~ ~ ~ +0 ~ ~ + $1 W I N N E^ ~ ~ $0 + - |- + ~ ~ ~ ~ ~ ~ +0 ~ ~ + $1 W I N N E R^ ~ $0 + - |- + ~ ~ ~ ~ ~ ~ +0 ~ ~ + $1 W I N N E R^ ~ $0 + - |- + ~ ~ ~ ~ ~ ~ +0 ~ ~ + $1 W I N N E R ~ $0 + description: |- + You can also win by touching one of your + opponent's towns with a valid word. + - title: Swapping + examples: + - G^ ~ ~ ~ ~ ~ + - G^ ~ ~ ~ ~ ~ + - G^ ~ ~ ~ ~ ~ + - G R^ ~ ~ ~ ~ + - G R^ ~ ~ ~ ~ + - G R I^ ~ ~ ~ + - G R I^ ~ ~ ~ + - G R I N^ ~ ~ + - G R I N^ ~ ~ + - G R I N B^ ~ + - G R I N B^ ~ + - G R I N B^ ~ + - G R I N B^ ~ + - B< R I N G< ~ + - B< R I N G< ~ + - B< R I N G< ~ + - B< R I N G< ~ + - B R I N G S^ + - B R I N G S^ + - B R I N G S + description: |- + Instead of playing a tile, + you can swap two tiles on the board. + - title: Truncation + examples: + - |- + W^ ~ ~ ~ ~ d ~ ~ ~ + $1 ~ ~ ~ ~ a r e $0 + ~ ~ ~ ~ ~ r ~ ~ ~ + - |- + W^ ~ ~ ~ ~ d ~ ~ ~ + $1 ~ ~ ~ ~ a r e $0 + ~ ~ ~ ~ ~ r ~ ~ ~ + - |- + W ~ ~ ~ ~ d ~ ~ ~ + $1 ~ ~ ~ ~ a r e $0 + ~ ~ ~ ~ a^ r ~ ~ ~ + - |- + W ~ ~ ~ ~ d ~ ~ ~ + $1 ~ ~ ~ ~ a r e $0 + ~ ~ ~ ~ a^ r ~ ~ ~ + - |- + W O^ ~ ~ ~ d ~ ~ ~ + $1 ~ ~ ~ ~ a r e $0 + ~ ~ ~ ~ a r ~ ~ ~ + - |- + W O^ ~ ~ ~ d ~ ~ ~ + $1 ~ ~ ~ ~ a r e $0 + ~ ~ ~ ~ a r ~ ~ ~ + - |- + W O ~ ~ ~ d ~ ~ ~ + $1 ~ ~ ~ ~ a r e $0 + ~ ~ ~ n^ a r ~ ~ ~ + - |- + W O ~ ~ ~ d ~ ~ ~ + $1 ~ ~ ~ ~ a r e $0 + ~ ~ ~ n^ a r ~ ~ ~ + - |- + W O R^ ~ ~ d ~ ~ ~ + $1 ~ ~ ~ ~ a r e $0 + ~ ~ ~ n a r ~ ~ ~ + - |- + W O R^ ~ ~ d ~ ~ ~ + $1 ~ ~ ~ ~ a r e $0 + ~ ~ ~ n a r ~ ~ ~ + - |- + W O R ~ ~ d ~ ~ ~ + $1 ~ ~ ~ ~ a r e $0 + ~ ~ g^ n a r ~ ~ ~ + - |- + W O R ~ ~ d ~ ~ ~ + $1 ~ ~ ~ ~ a r e $0 + ~ ~ g^ n a r ~ ~ ~ + - |- + W O R D^ ~ d ~ ~ ~ + $1 ~ ~ ~ ~ a r e $0 + ~ ~ g n a r ~ ~ ~ + - |- + W O R D^ ~ d ~ ~ ~ + $1 ~ ~ ~ ~ a r e $0 + ~ ~ g n a r ~ ~ ~ + - |- + W O R D ~ d ~ ~ ~ + $1 ~ ~ ~ ~ a r e $0 + ~ e^ g n a r ~ ~ ~ + - |- + W O R D ~ d ~ ~ ~ + $1 ~ ~ ~ ~ a r e $0 + ~ e^ g n a r ~ ~ ~ + - |- + W O R D Y^ d ~ ~ ~ + $1 ~ ~ ~ ~ a r e $0 + ~ e g n a r ~ ~ ~ + - |- + W O R D Y d* ~ ~ ~ + $1 ~ ~ ~ ~ a r e $0 + ~ e g n a r ~ ~ ~ + - |- + W O R D Y d* ~ ~ ~ + $1 ~ ~ ~ ~ a* r e $0 + ~ e g n a r ~ ~ ~ + - |- + W O R D Y d* ~ ~ ~ + $1 ~ ~ ~ ~ a* r e $0 + ~ e g n a r* ~ ~ ~ + - |- + W O R D Y d* ~ ~ ~ + $1 ~ ~ ~ ~ a* r e $0 + ~ e g n a r* ~ ~ ~ + - |- + W O R D Y d- ~ ~ ~ + $1 ~ ~ ~ ~ a- r e $0 + ~ e g n a r- ~ ~ ~ + - |- + W O R D Y d- ~ ~ ~ + $1 ~ ~ ~ ~ a- r e $0 + ~ e g n a r- ~ ~ ~ + - |- + W O R D Y d- ~ ~ ~ + $1 ~ ~ ~ ~ a- r e $0 + ~ e g n a r- ~ ~ ~ + - |- + W O R D Y d- ~ ~ ~ + $1 ~ ~ ~ ~ a- r e $0 + ~ e g n a r- ~ ~ ~ + - |- + W O R D Y d- ~ ~ ~ + $1 ~ ~ ~ ~ a- r e $0 + ~ e g n a* r- ~ ~ ~ + - |- + W O R D Y d- ~ ~ ~ + $1 ~ ~ ~ ~ a- r e $0 + ~ e g n* a* r- ~ ~ ~ + - |- + W O R D Y d- ~ ~ ~ + $1 ~ ~ ~ ~ a- r e $0 + ~ e g* n* a* r- ~ ~ ~ + - |- + W O R D Y d- ~ ~ ~ + $1 ~ ~ ~ ~ a- r e $0 + ~ e* g* n* a* r- ~ ~ ~ + - |- + W O R D Y d- ~ ~ ~ + $1 ~ ~ ~ ~ a- r e $0 + ~ e* g* n* a* r- ~ ~ ~ + - |- + W O R D Y d- ~ ~ ~ + $1 ~ ~ ~ ~ a- r e $0 + ~ e- g- n- a- r- ~ ~ ~ + - |- + W O R D Y ~ ~ ~ ~ + $1 ~ ~ ~ ~ ~ r e $0 + ~ ~ ~ ~ ~ ~ ~ ~ ~ + description: |- + When your letters become disconnected from your Artifact, + they are Truncated and removed from the board. diff --git a/web_client/src/_data/tiles.js b/web_client/src/_data/tiles.js index 3d9a6b0c..caa6c191 100644 --- a/web_client/src/_data/tiles.js +++ b/web_client/src/_data/tiles.js @@ -195,6 +195,214 @@ const tiles = { TERRAIN_BUTTON_NE: 193, TERRAIN_BUTTON_SW: 194, TERRAIN_BUTTON_SE: 195, + LETTER_SOUTH_A_NW: 196, + LETTER_SOUTH_A_NE: 197, + LETTER_SOUTH_A_SW: 198, + LETTER_SOUTH_A_SE: 199, + LETTER_SOUTH_B_NW: 200, + LETTER_SOUTH_B_NE: 201, + LETTER_SOUTH_B_SW: 202, + LETTER_SOUTH_B_SE: 203, + LETTER_SOUTH_C_NW: 204, + LETTER_SOUTH_C_NE: 205, + LETTER_SOUTH_C_SW: 206, + LETTER_SOUTH_C_SE: 207, + LETTER_SOUTH_D_NW: 208, + LETTER_SOUTH_D_NE: 209, + LETTER_SOUTH_D_SW: 210, + LETTER_SOUTH_D_SE: 211, + LETTER_SOUTH_E_NW: 212, + LETTER_SOUTH_E_NE: 213, + LETTER_SOUTH_E_SW: 214, + LETTER_SOUTH_E_SE: 215, + LETTER_SOUTH_F_NW: 216, + LETTER_SOUTH_F_NE: 217, + LETTER_SOUTH_F_SW: 218, + LETTER_SOUTH_F_SE: 219, + LETTER_SOUTH_G_NW: 220, + LETTER_SOUTH_G_NE: 221, + LETTER_SOUTH_G_SW: 222, + LETTER_SOUTH_G_SE: 223, + LETTER_SOUTH_H_NW: 224, + LETTER_SOUTH_H_NE: 225, + LETTER_SOUTH_H_SW: 226, + LETTER_SOUTH_H_SE: 227, + LETTER_SOUTH_I_NW: 228, + LETTER_SOUTH_I_NE: 229, + LETTER_SOUTH_I_SW: 230, + LETTER_SOUTH_I_SE: 231, + LETTER_SOUTH_J_NW: 232, + LETTER_SOUTH_J_NE: 233, + LETTER_SOUTH_J_SW: 234, + LETTER_SOUTH_J_SE: 235, + LETTER_SOUTH_K_NW: 236, + LETTER_SOUTH_K_NE: 237, + LETTER_SOUTH_K_SW: 238, + LETTER_SOUTH_K_SE: 239, + LETTER_SOUTH_L_NW: 240, + LETTER_SOUTH_L_NE: 241, + LETTER_SOUTH_L_SW: 242, + LETTER_SOUTH_L_SE: 243, + LETTER_SOUTH_M_NW: 244, + LETTER_SOUTH_M_NE: 245, + LETTER_SOUTH_M_SW: 246, + LETTER_SOUTH_M_SE: 247, + LETTER_SOUTH_N_NW: 248, + LETTER_SOUTH_N_NE: 249, + LETTER_SOUTH_N_SW: 250, + LETTER_SOUTH_N_SE: 251, + LETTER_SOUTH_O_NW: 252, + LETTER_SOUTH_O_NE: 253, + LETTER_SOUTH_O_SW: 254, + LETTER_SOUTH_O_SE: 255, + LETTER_SOUTH_P_NW: 256, + LETTER_SOUTH_P_NE: 257, + LETTER_SOUTH_P_SW: 258, + LETTER_SOUTH_P_SE: 259, + LETTER_SOUTH_Q_NW: 260, + LETTER_SOUTH_Q_NE: 261, + LETTER_SOUTH_Q_SW: 262, + LETTER_SOUTH_Q_SE: 263, + LETTER_SOUTH_R_NW: 264, + LETTER_SOUTH_R_NE: 265, + LETTER_SOUTH_R_SW: 266, + LETTER_SOUTH_R_SE: 267, + LETTER_SOUTH_S_NW: 268, + LETTER_SOUTH_S_NE: 269, + LETTER_SOUTH_S_SW: 270, + LETTER_SOUTH_S_SE: 271, + LETTER_SOUTH_T_NW: 272, + LETTER_SOUTH_T_NE: 273, + LETTER_SOUTH_T_SW: 274, + LETTER_SOUTH_T_SE: 275, + LETTER_SOUTH_U_NW: 276, + LETTER_SOUTH_U_NE: 277, + LETTER_SOUTH_U_SW: 278, + LETTER_SOUTH_U_SE: 279, + LETTER_SOUTH_V_NW: 280, + LETTER_SOUTH_V_NE: 281, + LETTER_SOUTH_V_SW: 282, + LETTER_SOUTH_V_SE: 283, + LETTER_SOUTH_W_NW: 284, + LETTER_SOUTH_W_NE: 285, + LETTER_SOUTH_W_SW: 286, + LETTER_SOUTH_W_SE: 287, + LETTER_SOUTH_X_NW: 288, + LETTER_SOUTH_X_NE: 289, + LETTER_SOUTH_X_SW: 290, + LETTER_SOUTH_X_SE: 291, + LETTER_SOUTH_Y_NW: 292, + LETTER_SOUTH_Y_NE: 293, + LETTER_SOUTH_Y_SW: 294, + LETTER_SOUTH_Y_SE: 295, + LETTER_SOUTH_Z_NW: 296, + LETTER_SOUTH_Z_NE: 297, + LETTER_SOUTH_Z_SW: 298, + LETTER_SOUTH_Z_SE: 299, + LETTER_NORTH_A_NW: 300, + LETTER_NORTH_A_NE: 301, + LETTER_NORTH_A_SW: 302, + LETTER_NORTH_A_SE: 303, + LETTER_NORTH_B_NW: 304, + LETTER_NORTH_B_NE: 305, + LETTER_NORTH_B_SW: 306, + LETTER_NORTH_B_SE: 307, + LETTER_NORTH_C_NW: 308, + LETTER_NORTH_C_NE: 309, + LETTER_NORTH_C_SW: 310, + LETTER_NORTH_C_SE: 311, + LETTER_NORTH_D_NW: 312, + LETTER_NORTH_D_NE: 313, + LETTER_NORTH_D_SW: 314, + LETTER_NORTH_D_SE: 315, + LETTER_NORTH_E_NW: 316, + LETTER_NORTH_E_NE: 317, + LETTER_NORTH_E_SW: 318, + LETTER_NORTH_E_SE: 319, + LETTER_NORTH_F_NW: 320, + LETTER_NORTH_F_NE: 321, + LETTER_NORTH_F_SW: 322, + LETTER_NORTH_F_SE: 323, + LETTER_NORTH_G_NW: 324, + LETTER_NORTH_G_NE: 325, + LETTER_NORTH_G_SW: 326, + LETTER_NORTH_G_SE: 327, + LETTER_NORTH_H_NW: 328, + LETTER_NORTH_H_NE: 329, + LETTER_NORTH_H_SW: 330, + LETTER_NORTH_H_SE: 331, + LETTER_NORTH_I_NW: 332, + LETTER_NORTH_I_NE: 333, + LETTER_NORTH_I_SW: 334, + LETTER_NORTH_I_SE: 335, + LETTER_NORTH_J_NW: 336, + LETTER_NORTH_J_NE: 337, + LETTER_NORTH_J_SW: 338, + LETTER_NORTH_J_SE: 339, + LETTER_NORTH_K_NW: 340, + LETTER_NORTH_K_NE: 341, + LETTER_NORTH_K_SW: 342, + LETTER_NORTH_K_SE: 343, + LETTER_NORTH_L_NW: 344, + LETTER_NORTH_L_NE: 345, + LETTER_NORTH_L_SW: 346, + LETTER_NORTH_L_SE: 347, + LETTER_NORTH_M_NW: 348, + LETTER_NORTH_M_NE: 349, + LETTER_NORTH_M_SW: 350, + LETTER_NORTH_M_SE: 351, + LETTER_NORTH_N_NW: 352, + LETTER_NORTH_N_NE: 353, + LETTER_NORTH_N_SW: 354, + LETTER_NORTH_N_SE: 355, + LETTER_NORTH_O_NW: 356, + LETTER_NORTH_O_NE: 357, + LETTER_NORTH_O_SW: 358, + LETTER_NORTH_O_SE: 359, + LETTER_NORTH_P_NW: 360, + LETTER_NORTH_P_NE: 361, + LETTER_NORTH_P_SW: 362, + LETTER_NORTH_P_SE: 363, + LETTER_NORTH_Q_NW: 364, + LETTER_NORTH_Q_NE: 365, + LETTER_NORTH_Q_SW: 366, + LETTER_NORTH_Q_SE: 367, + LETTER_NORTH_R_NW: 368, + LETTER_NORTH_R_NE: 369, + LETTER_NORTH_R_SW: 370, + LETTER_NORTH_R_SE: 371, + LETTER_NORTH_S_NW: 372, + LETTER_NORTH_S_NE: 373, + LETTER_NORTH_S_SW: 374, + LETTER_NORTH_S_SE: 375, + LETTER_NORTH_T_NW: 376, + LETTER_NORTH_T_NE: 377, + LETTER_NORTH_T_SW: 378, + LETTER_NORTH_T_SE: 379, + LETTER_NORTH_U_NW: 380, + LETTER_NORTH_U_NE: 381, + LETTER_NORTH_U_SW: 382, + LETTER_NORTH_U_SE: 383, + LETTER_NORTH_V_NW: 384, + LETTER_NORTH_V_NE: 385, + LETTER_NORTH_V_SW: 386, + LETTER_NORTH_V_SE: 387, + LETTER_NORTH_W_NW: 388, + LETTER_NORTH_W_NE: 389, + LETTER_NORTH_W_SW: 390, + LETTER_NORTH_W_SE: 391, + LETTER_NORTH_X_NW: 392, + LETTER_NORTH_X_NE: 393, + LETTER_NORTH_X_SW: 394, + LETTER_NORTH_X_SE: 395, + LETTER_NORTH_Y_NW: 396, + LETTER_NORTH_Y_NE: 397, + LETTER_NORTH_Y_SW: 398, + LETTER_NORTH_Y_SE: 399, + LETTER_NORTH_Z_NW: 400, + LETTER_NORTH_Z_NE: 401, + LETTER_NORTH_Z_SW: 402, + LETTER_NORTH_Z_SE: 403, }; const quads = { @@ -240,6 +448,58 @@ const quads = { ARTIFACT_BUTTON: [[tiles.ARTIFACT_BUTTON_NW, tiles.ARTIFACT_BUTTON_NE],[tiles.ARTIFACT_BUTTON_SW, tiles.ARTIFACT_BUTTON_SE]], ARTIFACT_BUTTON_GLYPH: [[tiles.ARTIFACT_BUTTON_GLYPH_NW, tiles.ARTIFACT_BUTTON_GLYPH_NE],[tiles.ARTIFACT_BUTTON_GLYPH_SW, tiles.ARTIFACT_BUTTON_GLYPH_SE]], TERRAIN_BUTTON: [[tiles.TERRAIN_BUTTON_NW, tiles.TERRAIN_BUTTON_NE],[tiles.TERRAIN_BUTTON_SW, tiles.TERRAIN_BUTTON_SE]], + LETTER_SOUTH_A: [[tiles.LETTER_SOUTH_A_NW, tiles.LETTER_SOUTH_A_NE],[tiles.LETTER_SOUTH_A_SW, tiles.LETTER_SOUTH_A_SE]], + LETTER_SOUTH_B: [[tiles.LETTER_SOUTH_B_NW, tiles.LETTER_SOUTH_B_NE],[tiles.LETTER_SOUTH_B_SW, tiles.LETTER_SOUTH_B_SE]], + LETTER_SOUTH_C: [[tiles.LETTER_SOUTH_C_NW, tiles.LETTER_SOUTH_C_NE],[tiles.LETTER_SOUTH_C_SW, tiles.LETTER_SOUTH_C_SE]], + LETTER_SOUTH_D: [[tiles.LETTER_SOUTH_D_NW, tiles.LETTER_SOUTH_D_NE],[tiles.LETTER_SOUTH_D_SW, tiles.LETTER_SOUTH_D_SE]], + LETTER_SOUTH_E: [[tiles.LETTER_SOUTH_E_NW, tiles.LETTER_SOUTH_E_NE],[tiles.LETTER_SOUTH_E_SW, tiles.LETTER_SOUTH_E_SE]], + LETTER_SOUTH_F: [[tiles.LETTER_SOUTH_F_NW, tiles.LETTER_SOUTH_F_NE],[tiles.LETTER_SOUTH_F_SW, tiles.LETTER_SOUTH_F_SE]], + LETTER_SOUTH_G: [[tiles.LETTER_SOUTH_G_NW, tiles.LETTER_SOUTH_G_NE],[tiles.LETTER_SOUTH_G_SW, tiles.LETTER_SOUTH_G_SE]], + LETTER_SOUTH_H: [[tiles.LETTER_SOUTH_H_NW, tiles.LETTER_SOUTH_H_NE],[tiles.LETTER_SOUTH_H_SW, tiles.LETTER_SOUTH_H_SE]], + LETTER_SOUTH_I: [[tiles.LETTER_SOUTH_I_NW, tiles.LETTER_SOUTH_I_NE],[tiles.LETTER_SOUTH_I_SW, tiles.LETTER_SOUTH_I_SE]], + LETTER_SOUTH_J: [[tiles.LETTER_SOUTH_J_NW, tiles.LETTER_SOUTH_J_NE],[tiles.LETTER_SOUTH_J_SW, tiles.LETTER_SOUTH_J_SE]], + LETTER_SOUTH_K: [[tiles.LETTER_SOUTH_K_NW, tiles.LETTER_SOUTH_K_NE],[tiles.LETTER_SOUTH_K_SW, tiles.LETTER_SOUTH_K_SE]], + LETTER_SOUTH_L: [[tiles.LETTER_SOUTH_L_NW, tiles.LETTER_SOUTH_L_NE],[tiles.LETTER_SOUTH_L_SW, tiles.LETTER_SOUTH_L_SE]], + LETTER_SOUTH_M: [[tiles.LETTER_SOUTH_M_NW, tiles.LETTER_SOUTH_M_NE],[tiles.LETTER_SOUTH_M_SW, tiles.LETTER_SOUTH_M_SE]], + LETTER_SOUTH_N: [[tiles.LETTER_SOUTH_N_NW, tiles.LETTER_SOUTH_N_NE],[tiles.LETTER_SOUTH_N_SW, tiles.LETTER_SOUTH_N_SE]], + LETTER_SOUTH_O: [[tiles.LETTER_SOUTH_O_NW, tiles.LETTER_SOUTH_O_NE],[tiles.LETTER_SOUTH_O_SW, tiles.LETTER_SOUTH_O_SE]], + LETTER_SOUTH_P: [[tiles.LETTER_SOUTH_P_NW, tiles.LETTER_SOUTH_P_NE],[tiles.LETTER_SOUTH_P_SW, tiles.LETTER_SOUTH_P_SE]], + LETTER_SOUTH_Q: [[tiles.LETTER_SOUTH_Q_NW, tiles.LETTER_SOUTH_Q_NE],[tiles.LETTER_SOUTH_Q_SW, tiles.LETTER_SOUTH_Q_SE]], + LETTER_SOUTH_R: [[tiles.LETTER_SOUTH_R_NW, tiles.LETTER_SOUTH_R_NE],[tiles.LETTER_SOUTH_R_SW, tiles.LETTER_SOUTH_R_SE]], + LETTER_SOUTH_S: [[tiles.LETTER_SOUTH_S_NW, tiles.LETTER_SOUTH_S_NE],[tiles.LETTER_SOUTH_S_SW, tiles.LETTER_SOUTH_S_SE]], + LETTER_SOUTH_T: [[tiles.LETTER_SOUTH_T_NW, tiles.LETTER_SOUTH_T_NE],[tiles.LETTER_SOUTH_T_SW, tiles.LETTER_SOUTH_T_SE]], + LETTER_SOUTH_U: [[tiles.LETTER_SOUTH_U_NW, tiles.LETTER_SOUTH_U_NE],[tiles.LETTER_SOUTH_U_SW, tiles.LETTER_SOUTH_U_SE]], + LETTER_SOUTH_V: [[tiles.LETTER_SOUTH_V_NW, tiles.LETTER_SOUTH_V_NE],[tiles.LETTER_SOUTH_V_SW, tiles.LETTER_SOUTH_V_SE]], + LETTER_SOUTH_W: [[tiles.LETTER_SOUTH_W_NW, tiles.LETTER_SOUTH_W_NE],[tiles.LETTER_SOUTH_W_SW, tiles.LETTER_SOUTH_W_SE]], + LETTER_SOUTH_X: [[tiles.LETTER_SOUTH_X_NW, tiles.LETTER_SOUTH_X_NE],[tiles.LETTER_SOUTH_X_SW, tiles.LETTER_SOUTH_X_SE]], + LETTER_SOUTH_Y: [[tiles.LETTER_SOUTH_Y_NW, tiles.LETTER_SOUTH_Y_NE],[tiles.LETTER_SOUTH_Y_SW, tiles.LETTER_SOUTH_Y_SE]], + LETTER_SOUTH_Z: [[tiles.LETTER_SOUTH_Z_NW, tiles.LETTER_SOUTH_Z_NE],[tiles.LETTER_SOUTH_Z_SW, tiles.LETTER_SOUTH_Z_SE]], + LETTER_NORTH_A: [[tiles.LETTER_NORTH_A_NW, tiles.LETTER_NORTH_A_NE],[tiles.LETTER_NORTH_A_SW, tiles.LETTER_NORTH_A_SE]], + LETTER_NORTH_B: [[tiles.LETTER_NORTH_B_NW, tiles.LETTER_NORTH_B_NE],[tiles.LETTER_NORTH_B_SW, tiles.LETTER_NORTH_B_SE]], + LETTER_NORTH_C: [[tiles.LETTER_NORTH_C_NW, tiles.LETTER_NORTH_C_NE],[tiles.LETTER_NORTH_C_SW, tiles.LETTER_NORTH_C_SE]], + LETTER_NORTH_D: [[tiles.LETTER_NORTH_D_NW, tiles.LETTER_NORTH_D_NE],[tiles.LETTER_NORTH_D_SW, tiles.LETTER_NORTH_D_SE]], + LETTER_NORTH_E: [[tiles.LETTER_NORTH_E_NW, tiles.LETTER_NORTH_E_NE],[tiles.LETTER_NORTH_E_SW, tiles.LETTER_NORTH_E_SE]], + LETTER_NORTH_F: [[tiles.LETTER_NORTH_F_NW, tiles.LETTER_NORTH_F_NE],[tiles.LETTER_NORTH_F_SW, tiles.LETTER_NORTH_F_SE]], + LETTER_NORTH_G: [[tiles.LETTER_NORTH_G_NW, tiles.LETTER_NORTH_G_NE],[tiles.LETTER_NORTH_G_SW, tiles.LETTER_NORTH_G_SE]], + LETTER_NORTH_H: [[tiles.LETTER_NORTH_H_NW, tiles.LETTER_NORTH_H_NE],[tiles.LETTER_NORTH_H_SW, tiles.LETTER_NORTH_H_SE]], + LETTER_NORTH_I: [[tiles.LETTER_NORTH_I_NW, tiles.LETTER_NORTH_I_NE],[tiles.LETTER_NORTH_I_SW, tiles.LETTER_NORTH_I_SE]], + LETTER_NORTH_J: [[tiles.LETTER_NORTH_J_NW, tiles.LETTER_NORTH_J_NE],[tiles.LETTER_NORTH_J_SW, tiles.LETTER_NORTH_J_SE]], + LETTER_NORTH_K: [[tiles.LETTER_NORTH_K_NW, tiles.LETTER_NORTH_K_NE],[tiles.LETTER_NORTH_K_SW, tiles.LETTER_NORTH_K_SE]], + LETTER_NORTH_L: [[tiles.LETTER_NORTH_L_NW, tiles.LETTER_NORTH_L_NE],[tiles.LETTER_NORTH_L_SW, tiles.LETTER_NORTH_L_SE]], + LETTER_NORTH_M: [[tiles.LETTER_NORTH_M_NW, tiles.LETTER_NORTH_M_NE],[tiles.LETTER_NORTH_M_SW, tiles.LETTER_NORTH_M_SE]], + LETTER_NORTH_N: [[tiles.LETTER_NORTH_N_NW, tiles.LETTER_NORTH_N_NE],[tiles.LETTER_NORTH_N_SW, tiles.LETTER_NORTH_N_SE]], + LETTER_NORTH_O: [[tiles.LETTER_NORTH_O_NW, tiles.LETTER_NORTH_O_NE],[tiles.LETTER_NORTH_O_SW, tiles.LETTER_NORTH_O_SE]], + LETTER_NORTH_P: [[tiles.LETTER_NORTH_P_NW, tiles.LETTER_NORTH_P_NE],[tiles.LETTER_NORTH_P_SW, tiles.LETTER_NORTH_P_SE]], + LETTER_NORTH_Q: [[tiles.LETTER_NORTH_Q_NW, tiles.LETTER_NORTH_Q_NE],[tiles.LETTER_NORTH_Q_SW, tiles.LETTER_NORTH_Q_SE]], + LETTER_NORTH_R: [[tiles.LETTER_NORTH_R_NW, tiles.LETTER_NORTH_R_NE],[tiles.LETTER_NORTH_R_SW, tiles.LETTER_NORTH_R_SE]], + LETTER_NORTH_S: [[tiles.LETTER_NORTH_S_NW, tiles.LETTER_NORTH_S_NE],[tiles.LETTER_NORTH_S_SW, tiles.LETTER_NORTH_S_SE]], + LETTER_NORTH_T: [[tiles.LETTER_NORTH_T_NW, tiles.LETTER_NORTH_T_NE],[tiles.LETTER_NORTH_T_SW, tiles.LETTER_NORTH_T_SE]], + LETTER_NORTH_U: [[tiles.LETTER_NORTH_U_NW, tiles.LETTER_NORTH_U_NE],[tiles.LETTER_NORTH_U_SW, tiles.LETTER_NORTH_U_SE]], + LETTER_NORTH_V: [[tiles.LETTER_NORTH_V_NW, tiles.LETTER_NORTH_V_NE],[tiles.LETTER_NORTH_V_SW, tiles.LETTER_NORTH_V_SE]], + LETTER_NORTH_W: [[tiles.LETTER_NORTH_W_NW, tiles.LETTER_NORTH_W_NE],[tiles.LETTER_NORTH_W_SW, tiles.LETTER_NORTH_W_SE]], + LETTER_NORTH_X: [[tiles.LETTER_NORTH_X_NW, tiles.LETTER_NORTH_X_NE],[tiles.LETTER_NORTH_X_SW, tiles.LETTER_NORTH_X_SE]], + LETTER_NORTH_Y: [[tiles.LETTER_NORTH_Y_NW, tiles.LETTER_NORTH_Y_NE],[tiles.LETTER_NORTH_Y_SW, tiles.LETTER_NORTH_Y_SE]], + LETTER_NORTH_Z: [[tiles.LETTER_NORTH_Z_NW, tiles.LETTER_NORTH_Z_NE],[tiles.LETTER_NORTH_Z_SW, tiles.LETTER_NORTH_Z_SE]], }; module.exports = async function () { diff --git a/web_client/src/_includes/page.html b/web_client/src/_includes/page.html index c0d186ea..f146cc33 100644 --- a/web_client/src/_includes/page.html +++ b/web_client/src/_includes/page.html @@ -542,7 +542,7 @@

Many thanks to the word data sources:

if (!has_played_tut) { this.button("Learn To Play", () => { localStorage.setItem("tutorial_played", "true"); - truncate_runner.join_game('TUTORIAL_RULES'); + truncate_runner.join_game('RULE_CARD'); }, "special"); } @@ -560,27 +560,23 @@

Many thanks to the word data sources:

}); if (has_played_tut) { - this.button("Learn To Play", () => { - localStorage.setItem("tutorial_played", "true"); - truncate_runner.join_game('TUTORIAL_RULES'); + this.button("Tutorials", () => { + this.learnStage(); }); - // Uncomment if adding back a second tutorial - // this.button("Tutorials", () => { - // this.learnStage(); - // }); } } learnStage() { this.reset(); - this.button("Learn To Play", () => { + this.button("Quick Start", () => { localStorage.setItem("tutorial_played", "true"); - truncate_runner.join_game('TUTORIAL_RULES'); + truncate_runner.join_game('RULE_CARD'); }); - this.button("Example Game", () => { - truncate_runner.join_game('TUTORIAL_EXAMPLE'); + this.button("Interactive Tutorial", () => { + localStorage.setItem("tutorial_played", "true"); + truncate_runner.join_game('TUTORIAL_RULES'); }); this.backButton();