From 38eed3e48309e59b5819242d6659f2b9aed0e3f6 Mon Sep 17 00:00:00 2001 From: Llywelwyn Date: Sun, 27 Aug 2023 00:06:29 +0100 Subject: [PATCH] refactor, and overmap (heavy wip) --- src/data/visuals.rs | 24 +++-- src/main.rs | 2 +- src/map/mod.rs | 27 +++--- src/map/themes.rs | 89 +++++++++++-------- src/map_builders/mod.rs | 1 - .../prefab_builder/prefab_levels.rs | 2 +- 6 files changed, 89 insertions(+), 56 deletions(-) diff --git a/src/data/visuals.rs b/src/data/visuals.rs index f9402be..efaee3a 100644 --- a/src/data/visuals.rs +++ b/src/data/visuals.rs @@ -1,9 +1,9 @@ // POST-PROCESSING -pub const WITH_SCANLINES: bool = false; // Adds scanlines to the screen. -pub const WITH_SCREEN_BURN: bool = false; // Requires WITH_SCANLINES. pub const WITH_DARKEN_BY_DISTANCE: bool = true; // If further away tiles should get darkened, instead of a harsh transition to non-visible. -pub const MAX_COLOUR_OFFSET: i32 = 30; +pub const BRIGHTEN_FG_COLOUR_BY: i32 = 16; +pub const GLOBAL_OFFSET_MIN_CLAMP: f32 = -0.5; +pub const GLOBAL_OFFSET_MAX_CLAMP: f32 = 1.0; pub const WITH_SCANLINES_BRIGHTEN_AMOUNT: f32 = 0.1; // 0.0 = no brightening, 1.0 = full brightening. pub const NON_VISIBLE_MULTIPLIER: f32 = 0.3; // 0.0 = black, 1.0 = full colour. pub const NON_VISIBLE_MULTIPLIER_IF_SCANLINES: f32 = 0.8; // as above, but when using scanlines. should be higher. @@ -20,23 +20,37 @@ pub const BLOODSTAIN_COLOUR: (u8, u8, u8) = (153, 0, 0); // DEFAULT THEME pub const DEFAULT_BG_COLOUR: (u8, u8, u8) = (29, 50, 50); pub const WALL_COLOUR: (u8, u8, u8) = (229, 191, 94); +pub const WALL_OFFSETS: (i32, i32, i32) = (48, 48, 48); pub const FLOOR_COLOUR: (u8, u8, u8) = (25, 204, 122); +pub const FLOOR_OFFSETS: (i32, i32, i32) = (0, 0, 0); pub const STAIR_COLOUR: (u8, u8, u8) = (200, 200, 0); +pub const STAIR_OFFSETS: (i32, i32, i32) = (0, 0, 0); pub const WOOD_FLOOR_COLOUR: (u8, u8, u8) = (41, 30, 20); +pub const WOOD_FLOOR_OFFSETS: (i32, i32, i32) = (0, 0, 0); pub const FENCE_FG_COLOUR: (u8, u8, u8) = (110, 24, 0); pub const FENCE_COLOUR: (u8, u8, u8) = (45, 30, 10); +pub const FENCE_OFFSETS: (i32, i32, i32) = (0, 0, 0); pub const BRIDGE_COLOUR: (u8, u8, u8) = (42, 48, 37); +pub const BRIDGE_OFFSETS: (i32, i32, i32) = (0, 0, 0); pub const GRAVEL_COLOUR: (u8, u8, u8) = (26, 26, 53); +pub const GRAVEL_OFFSETS: (i32, i32, i32) = (0, 0, 0); pub const ROAD_COLOUR: (u8, u8, u8) = (8, 38, 40); +pub const ROAD_OFFSETS: (i32, i32, i32) = (0, 0, 0); pub const GRASS_COLOUR: (u8, u8, u8) = (9, 65, 6); +pub const GRASS_OFFSETS: (i32, i32, i32) = (3, 40, 3); pub const FOLIAGE_COLOUR: (u8, u8, u8) = (5, 60, 5); +pub const FOLIAGE_OFFSETS: (i32, i32, i32) = (0, 0, 0); pub const HEAVY_FOLIAGE_COLOUR: (u8, u8, u8) = (5, 60, 5); +pub const HEAVY_FOLIAGE_OFFSETS: (i32, i32, i32) = (0, 0, 0); pub const SAND_COLOUR: (u8, u8, u8) = (70, 70, 21); +pub const SAND_OFFSETS: (i32, i32, i32) = (0, 0, 0); pub const SHALLOW_WATER_COLOUR: (u8, u8, u8) = (24, 47, 99); +pub const SHALLOW_WATER_OFFSETS: (i32, i32, i32) = (3, 10, 45); pub const DEEP_WATER_COLOUR: (u8, u8, u8) = (18, 33, 63); +pub const DEEP_WATER_OFFSETS: (i32, i32, i32) = (5, 10, 32); pub const BARS_COLOUR: (u8, u8, u8) = (100, 100, 100); -pub const IMPASSABLE_MOUNTAIN_COLOUR: (u8, u8, u8) = (35, 38, 36); -pub const IMPASSABLE_MOUNTAIN_FG_COLOUR: (u8, u8, u8) = (0, 0, 0); +pub const IMPASSABLE_MOUNTAIN_COLOUR: (u8, u8, u8) = (20, 23, 20); +pub const IMPASSABLE_MOUNTAIN_OFFSETS: (i32, i32, i32) = (4, 4, 4); // FOREST THEME pub const FOREST_WALL_COLOUR: (u8, u8, u8) = (0, 153, 0); diff --git a/src/main.rs b/src/main.rs index da5e63d..e1762f4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -628,7 +628,7 @@ fn main() -> rltk::BError { .with_simple_console(DISPLAYWIDTH, DISPLAYHEIGHT, "curses14x16.png") .build()?; if config::CONFIG.visuals.with_scanlines { - context.with_post_scanlines(data::visuals::WITH_SCREEN_BURN); + context.with_post_scanlines(config::CONFIG.visuals.with_screen_burn); } let mut gs = State { diff --git a/src/map/mod.rs b/src/map/mod.rs index f236122..57d5453 100644 --- a/src/map/mod.rs +++ b/src/map/mod.rs @@ -8,7 +8,7 @@ pub use interval_spawning_system::try_spawn_interval; pub mod dungeon; pub use dungeon::{ level_transition, MasterDungeonMap }; pub mod themes; -use crate::data::visuals::MAX_COLOUR_OFFSET; +use crate::data::visuals::{ BRIGHTEN_FG_COLOUR_BY, GLOBAL_OFFSET_MIN_CLAMP, GLOBAL_OFFSET_MAX_CLAMP }; // FIXME: If the map size gets too small, entities stop being rendered starting from the right. // i.e. on a map size of 40*40, only entities to the left of the player are rendered. @@ -24,7 +24,7 @@ pub struct Map { pub visible_tiles: Vec, pub lit_tiles: Vec, pub telepath_tiles: Vec, - pub colour_offset: Vec<(i32, i32, i32)>, + pub colour_offset: Vec<((f32, f32, f32), (f32, f32, f32))>, pub additional_fg_offset: rltk::RGB, pub id: i32, pub name: String, @@ -50,11 +50,11 @@ impl Map { visible_tiles: vec![false; map_tile_count], lit_tiles: vec![true; map_tile_count], // NYI: Light sources. Once those exist, we can set this to false. telepath_tiles: vec![false; map_tile_count], - colour_offset: vec![(0, 0, 0); map_tile_count], + colour_offset: vec![((0.0, 0.0, 0.0), (0.0, 0.0, 0.0)); map_tile_count], additional_fg_offset: rltk::RGB::from_u8( - MAX_COLOUR_OFFSET as u8, - MAX_COLOUR_OFFSET as u8, - MAX_COLOUR_OFFSET as u8 + BRIGHTEN_FG_COLOUR_BY as u8, + BRIGHTEN_FG_COLOUR_BY as u8, + BRIGHTEN_FG_COLOUR_BY as u8 ), id: new_id, name: name.to_string(), @@ -63,14 +63,19 @@ impl Map { view_blocked: HashSet::new(), }; - const TWICE_OFFSET: i32 = MAX_COLOUR_OFFSET * 2; let mut rng = rltk::RandomNumberGenerator::new(); for idx in 0..map.colour_offset.len() { - let red_roll: i32 = rng.roll_dice(1, TWICE_OFFSET) - MAX_COLOUR_OFFSET; - let blue_roll: i32 = rng.roll_dice(1, TWICE_OFFSET) - MAX_COLOUR_OFFSET; - let green_roll: i32 = rng.roll_dice(1, TWICE_OFFSET) - MAX_COLOUR_OFFSET; - map.colour_offset[idx] = (red_roll, green_roll, blue_roll); + map.colour_offset[idx].0 = ( + rng.range(GLOBAL_OFFSET_MIN_CLAMP, GLOBAL_OFFSET_MAX_CLAMP), + rng.range(GLOBAL_OFFSET_MIN_CLAMP, GLOBAL_OFFSET_MAX_CLAMP), + rng.range(GLOBAL_OFFSET_MIN_CLAMP, GLOBAL_OFFSET_MAX_CLAMP), + ); + map.colour_offset[idx].1 = ( + rng.range(GLOBAL_OFFSET_MIN_CLAMP, GLOBAL_OFFSET_MAX_CLAMP), + rng.range(GLOBAL_OFFSET_MIN_CLAMP, GLOBAL_OFFSET_MAX_CLAMP), + rng.range(GLOBAL_OFFSET_MIN_CLAMP, GLOBAL_OFFSET_MAX_CLAMP), + ); } return map; diff --git a/src/map/themes.rs b/src/map/themes.rs index 0f872f3..98d3fea 100644 --- a/src/map/themes.rs +++ b/src/map/themes.rs @@ -11,7 +11,7 @@ pub fn get_tile_renderables_for_id( other_pos: Option, debug: Option ) -> (rltk::FontCharType, RGB, RGB) { - let (glyph, mut fg, mut bg, offset_mod) = match map.id { + let (glyph, mut fg, mut bg, offsets, bg_main_col) = match map.id { 3 => get_forest_theme_renderables(idx, map, debug), _ => get_default_theme_renderables(idx, map, debug), }; @@ -24,7 +24,7 @@ pub fn get_tile_renderables_for_id( } fg = fg.add(map.additional_fg_offset); - (fg, bg) = apply_colour_offset(fg, bg, map, idx, offset_mod); + (fg, bg) = apply_colour_offset(fg, bg, map, idx, offsets, bg_main_col); if CONFIG.visuals.with_scanlines && WITH_SCANLINES_BRIGHTEN_AMOUNT > 0.0 { (fg, bg) = brighten_by(fg, bg, WITH_SCANLINES_BRIGHTEN_AMOUNT); } @@ -60,53 +60,55 @@ pub fn get_tile_renderables_for_id( } #[rustfmt::skip] -pub fn get_default_theme_renderables(idx: usize, map: &Map, debug: Option) -> (rltk::FontCharType, RGB, RGB, (f32, f32, f32)) { +pub fn get_default_theme_renderables(idx: usize, map: &Map, debug: Option) -> (rltk::FontCharType, RGB, RGB, (i32, i32, i32), bool) { let glyph: rltk::FontCharType; #[allow(unused_assignments)] let mut fg: RGB = RGB::new(); #[allow(unused_assignments)] let mut bg: RGB = RGB::new(); - let mut offset_mod: (f32, f32, f32) = (0.5, 0.2, 0.0); + let mut offsets: (i32, i32, i32) = (0, 0, 0); + let mut bg_main_col = true; match map.tiles[idx] { - TileType::Floor => { glyph = rltk::to_cp437(FLOOR_GLYPH); fg = RGB::named(FLOOR_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); } - TileType::WoodFloor => { glyph = rltk::to_cp437(WOOD_FLOOR_GLYPH); bg = RGB::named(WOOD_FLOOR_COLOUR); } - TileType::Fence => { glyph = rltk::to_cp437(FENCE_GLYPH); fg = RGB::named(FENCE_FG_COLOUR); bg = RGB::named(FENCE_COLOUR); } - TileType::Wall => { let x = idx as i32 % map.width; let y = idx as i32 / map.width; glyph = wall_glyph(&*map, x, y, debug); fg = RGB::named(WALL_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); } - TileType::DownStair => { glyph = rltk::to_cp437(DOWN_STAIR_GLYPH); fg = RGB::named(STAIR_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); } - TileType::UpStair => { glyph = rltk::to_cp437(UP_STAIR_GLYPH); fg = RGB::named(STAIR_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); } - TileType::Bridge => { glyph = rltk::to_cp437(BRIDGE_GLYPH); bg = RGB::named(BRIDGE_COLOUR); } - TileType::Gravel => { glyph = rltk::to_cp437(GRAVEL_GLYPH); bg = RGB::named(GRAVEL_COLOUR); } - TileType::Road => { glyph = rltk::to_cp437(ROAD_GLYPH); bg = RGB::named(ROAD_COLOUR); } - TileType::Grass => { glyph = rltk::to_cp437(GRASS_GLYPH); bg = RGB::named(GRASS_COLOUR); } - TileType::Foliage => { glyph = rltk::to_cp437(FOLIAGE_GLYPH); bg = RGB::named(FOLIAGE_COLOUR); } - TileType::HeavyFoliage => { glyph = rltk::to_cp437(HEAVY_FOLIAGE_GLYPH); bg = RGB::named(HEAVY_FOLIAGE_COLOUR); } - TileType::Sand => { glyph = rltk::to_cp437(SAND_GLYPH); bg = RGB::named(SAND_COLOUR); } - TileType::ShallowWater => { glyph = rltk::to_cp437(SHALLOW_WATER_GLYPH); bg = RGB::named(SHALLOW_WATER_COLOUR); } - TileType::DeepWater => { glyph = rltk::to_cp437(DEEP_WATER_GLYPH); bg = RGB::named(DEEP_WATER_COLOUR); } + TileType::Floor => { glyph = rltk::to_cp437(FLOOR_GLYPH); fg = RGB::named(FLOOR_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); offsets = FLOOR_OFFSETS; } + TileType::WoodFloor => { glyph = rltk::to_cp437(WOOD_FLOOR_GLYPH); bg = RGB::named(WOOD_FLOOR_COLOUR); offsets = WOOD_FLOOR_OFFSETS; } + TileType::Fence => { glyph = rltk::to_cp437(FENCE_GLYPH); fg = RGB::named(FENCE_FG_COLOUR); bg = RGB::named(FENCE_COLOUR); offsets = FENCE_OFFSETS; } + TileType::Wall => { let x = idx as i32 % map.width; let y = idx as i32 / map.width; glyph = wall_glyph(&*map, x, y, debug); fg = RGB::named(WALL_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); offsets = WALL_OFFSETS; bg_main_col = false; } + TileType::DownStair => { glyph = rltk::to_cp437(DOWN_STAIR_GLYPH); fg = RGB::named(STAIR_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); offsets = STAIR_OFFSETS;} + TileType::UpStair => { glyph = rltk::to_cp437(UP_STAIR_GLYPH); fg = RGB::named(STAIR_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); offsets = STAIR_OFFSETS; } + TileType::Bridge => { glyph = rltk::to_cp437(BRIDGE_GLYPH); bg = RGB::named(BRIDGE_COLOUR); offsets = BRIDGE_OFFSETS; } + TileType::Gravel => { glyph = rltk::to_cp437(GRAVEL_GLYPH); bg = RGB::named(GRAVEL_COLOUR); offsets = GRAVEL_OFFSETS;} + TileType::Road => { glyph = rltk::to_cp437(ROAD_GLYPH); bg = RGB::named(ROAD_COLOUR); offsets = ROAD_OFFSETS;} + TileType::Grass => { glyph = rltk::to_cp437(GRASS_GLYPH); bg = RGB::named(GRASS_COLOUR); offsets = GRASS_OFFSETS; } + TileType::Foliage => { glyph = rltk::to_cp437(FOLIAGE_GLYPH); bg = RGB::named(FOLIAGE_COLOUR); offsets = FOLIAGE_OFFSETS; } + TileType::HeavyFoliage => { glyph = rltk::to_cp437(HEAVY_FOLIAGE_GLYPH); bg = RGB::named(HEAVY_FOLIAGE_COLOUR); offsets = HEAVY_FOLIAGE_OFFSETS; } + TileType::Sand => { glyph = rltk::to_cp437(SAND_GLYPH); bg = RGB::named(SAND_COLOUR); offsets = SAND_OFFSETS; } + TileType::ShallowWater => { glyph = rltk::to_cp437(SHALLOW_WATER_GLYPH); bg = RGB::named(SHALLOW_WATER_COLOUR); offsets = SHALLOW_WATER_OFFSETS; } + TileType::DeepWater => { glyph = rltk::to_cp437(DEEP_WATER_GLYPH); bg = RGB::named(DEEP_WATER_COLOUR); offsets = DEEP_WATER_OFFSETS; } TileType::Bars => { glyph = rltk::to_cp437(BARS_GLYPH); fg = RGB::named(BARS_COLOUR); bg = RGB::named(FLOOR_COLOUR); } - TileType::ImpassableMountain => { glyph = rltk::to_cp437(IMPASSABLE_MOUNTAIN_GLYPH); bg = RGB::named(IMPASSABLE_MOUNTAIN_COLOUR); fg = RGB::named((20, 20, 20)) } + TileType::ImpassableMountain => { glyph = rltk::to_cp437(IMPASSABLE_MOUNTAIN_GLYPH); bg = RGB::named(IMPASSABLE_MOUNTAIN_COLOUR); offsets = IMPASSABLE_MOUNTAIN_OFFSETS } } - return (glyph, fg, bg, offset_mod); + return (glyph, fg, bg, offsets, bg_main_col); } #[rustfmt::skip] -fn get_forest_theme_renderables(idx:usize, map: &Map, debug: Option) -> (rltk::FontCharType, RGB, RGB, (f32, f32, f32)) { +fn get_forest_theme_renderables(idx:usize, map: &Map, debug: Option) -> (rltk::FontCharType, RGB, RGB, (i32, i32, i32), bool) { let glyph; #[allow(unused_assignments)] let mut fg = RGB::new(); #[allow(unused_assignments)] let mut bg = RGB::new(); - let mut offset_mod: (f32, f32, f32) = (1.0, 1.0, 1.0); + let mut offsets: (i32, i32, i32) = (0, 0, 0); + let mut bg_main_col = true; match map.tiles[idx] { TileType::Wall => { glyph = rltk::to_cp437(FOREST_WALL_GLYPH); fg = RGB::named(FOREST_WALL_COLOUR); bg = RGB::named(GRASS_COLOUR) } TileType::Road => { glyph = rltk::to_cp437(ROAD_GLYPH); bg = RGB::named(ROAD_COLOUR); } TileType::ShallowWater => { glyph = rltk::to_cp437(SHALLOW_WATER_GLYPH); bg = RGB::named(SHALLOW_WATER_COLOUR); } - _ => { (glyph, fg, _, offset_mod) = get_default_theme_renderables(idx, map, debug); bg = RGB::named(GRASS_COLOUR) } + _ => { (glyph, fg, _, offsets, bg_main_col) = get_default_theme_renderables(idx, map, debug); bg = RGB::named(GRASS_COLOUR) } } - (glyph, fg, bg, offset_mod) + (glyph, fg, bg, offsets, bg_main_col) } fn is_revealed_and_wall(map: &Map, x: i32, y: i32, debug: Option) -> bool { @@ -251,17 +253,30 @@ fn wall_glyph(map: &Map, x: i32, y: i32, debug: Option) -> rltk::FontCharT } } -fn apply_colour_offset(mut fg: RGB, mut bg: RGB, map: &Map, idx: usize, offset_mod: (f32, f32, f32)) -> (RGB, RGB) { - let mut offsets = map.colour_offset[idx]; - let mut additional_fg_offset = map.additional_fg_offset; - offsets.0 = ((offsets.0 as f32) * offset_mod.0) as i32; - offsets.1 = ((offsets.1 as f32) * offset_mod.1) as i32; - offsets.2 = ((offsets.2 as f32) * offset_mod.2) as i32; - additional_fg_offset.r *= offset_mod.0; - additional_fg_offset.g *= offset_mod.1; - additional_fg_offset.b *= offset_mod.2; - fg = add_i32_offsets(fg.add(additional_fg_offset), offsets); - bg = add_i32_offsets(bg, offsets); +fn apply_colour_offset( + mut fg: RGB, + mut bg: RGB, + map: &Map, + idx: usize, + offset: (i32, i32, i32), + bg_main_col: bool +) -> (RGB, RGB) { + let offset_mod = map.colour_offset[idx]; + let fg_offset = ( + (offset.0 as f32) * offset_mod.0.0, + (offset.1 as f32) * offset_mod.0.1, + (offset.2 as f32) * offset_mod.0.2, + ); + fg = add_i32_offsets(fg, fg_offset); + if bg_main_col { + let bg_offset = ( + (offset.0 as f32) * offset_mod.1.0, + (offset.1 as f32) * offset_mod.1.1, + (offset.2 as f32) * offset_mod.1.2, + ); + bg = add_i32_offsets(bg, bg_offset); + } + return (fg, bg); } @@ -272,7 +287,7 @@ fn apply_bloodstain_if_necessary(mut bg: RGB, map: &Map, idx: usize) -> RGB { return bg; } -pub fn add_i32_offsets(rgb: RGB, offsets: (i32, i32, i32)) -> RGB { +pub fn add_i32_offsets(rgb: RGB, offsets: (f32, f32, f32)) -> RGB { let r = rgb.r + (offsets.0 as f32) / 255.0; let g = rgb.g + (offsets.1 as f32) / 255.0; let b = rgb.b + (offsets.2 as f32) / 255.0; diff --git a/src/map_builders/mod.rs b/src/map_builders/mod.rs index e2e06d4..20e814d 100644 --- a/src/map_builders/mod.rs +++ b/src/map_builders/mod.rs @@ -320,7 +320,6 @@ fn random_shape_builder(rng: &mut rltk::RandomNumberGenerator, builder: &mut Bui fn overmap_builder() -> BuilderChain { let mut builder = BuilderChain::new(true, 1, 69, 41, 0, "the world", 1); builder.start_with(PrefabBuilder::overmap()); - builder.with(AreaStartingPosition::new(XStart::CENTRE, YStart::CENTRE)); return builder; } diff --git a/src/map_builders/prefab_builder/prefab_levels.rs b/src/map_builders/prefab_builder/prefab_levels.rs index 0c04cbb..e0e2139 100644 --- a/src/map_builders/prefab_builder/prefab_levels.rs +++ b/src/map_builders/prefab_builder/prefab_levels.rs @@ -93,7 +93,7 @@ const OVERMAP_TEMPLATE: &str = ^^^^^^^........................≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈.....≈ ^^^^^^^^........@..............≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈....≈ ^^^^^^^^..................≈...≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈...≈≈≈...≈≈≈≈≈≈≈≈.≈≈ -^^^^^^^^^.................≈≈....≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈....≈≈......≈≈≈≈≈≈≈≈≈ +^^^^^^^^^.............>...≈≈....≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈....≈≈......≈≈≈≈≈≈≈≈≈ ^^^^^^^^^........≈≈≈≈...≈≈≈≈....≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈..≈≈≈......≈≈≈≈≈≈≈≈≈ ^^^^^^^^^^......≈≈≈≈≈≈≈≈≈≈≈≈≈..≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈........≈≈≈≈≈≈≈≈ ^^^^^^^^^^.....≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈........≈≈≈≈≈≈≈≈≈