From 4fa52db71d3033da582188a4738739d8a6e24ff2 Mon Sep 17 00:00:00 2001 From: Isse Date: Mon, 16 Oct 2023 01:51:30 +0200 Subject: [PATCH] more work on tavern --- voxygen/src/scene/terrain/watcher.rs | 4 +- world/src/site/namegen.rs | 159 ++++++++++++ world/src/site2/mod.rs | 2 +- world/src/site2/plot/adlet.rs | 8 +- world/src/site2/plot/bridge.rs | 4 +- world/src/site2/plot/gnarling.rs | 4 +- world/src/site2/plot/tavern.rs | 369 +++++++++++++++++++++++++-- world/src/site2/util/mod.rs | 11 +- 8 files changed, 530 insertions(+), 31 deletions(-) diff --git a/voxygen/src/scene/terrain/watcher.rs b/voxygen/src/scene/terrain/watcher.rs index cfb9a8e338..0109c06715 100644 --- a/voxygen/src/scene/terrain/watcher.rs +++ b/voxygen/src/scene/terrain/watcher.rs @@ -207,7 +207,9 @@ impl BlocksOfInterest { ) .with_z(0.0), )), - Some(SpriteKind::Sign) => interactables.push((pos, Interaction::Read)), + Some(SpriteKind::Sign | SpriteKind::HangingSign) => { + interactables.push((pos, Interaction::Read)) + }, _ if block.is_mountable() => interactables.push((pos, Interaction::Mount)), _ => {}, }, diff --git a/world/src/site/namegen.rs b/world/src/site/namegen.rs index 7ffde99f91..5ea1c7605f 100644 --- a/world/src/site/namegen.rs +++ b/world/src/site/namegen.rs @@ -689,4 +689,163 @@ impl<'a, R: Rng> NameGen<'a, R> { ]; self.generate_theme_from_parts(&start, &middle, &vowel, &end) } + + pub fn generate_tavern(&mut self) -> String { + let adjectives = [ + "Crazy", + "Big", + "Tiny", + "Slimy", + "Warm", + "Rigid", + "Soft", + "Wet", + "Humid", + "Smelly", + "Hidden", + "Smart", + "Fragile", + "Strong", + "Weak", + "Happy", + "Sad", + "Glad", + "Scared", + "Emberrassed", + "Goofy", + "Spicy", + "Salty", + "Peaceful", + "Awful", + "Sweet", + "Colossal", + "Puzzled", + "Cheap", + "Valuable", + "Rich", + "Obnoxious", + "Puzzled", + "Snoring", + "Fast", + "Quick", + "Magical", + "Violet", + "Red", + "Blue", + "Green", + "Yellow", + "Golden", + "Shiny", + "Tired", + "Twin", + "Incompetent", + "Light", + "Dark", + "Glorious", + "Best", + "Free", + "Odd", + "Juicy", + "Shaking", + "Tall", + "Short", + "Precious", + "Regular", + "Slow", + "Anxious", + "Naive", + "Sore", + "Next", + "Silver", + "Secret", + "Honorable", + "Rapid", + "Sleepy", + "Lying", + "Zesty", + "Fancy", + "Stylish", + ]; + let tavern_synonyms = ["Tavern", "Bar", "Pub"]; + let subjectives = [ + "Apple", + "Pumpkin", + "Cucumber", + "Squash", + "Demons", + "Mango", + "Coconut", + "Cats", + "Hill", + "Mountain", + "Squirrel", + "Rabbit", + "Moose", + "Driggle", + "Iron", + "Velorite", + "Plate", + "Eagle", + "Birds", + "Drumstick", + "Dog", + "Tiger", + "Knight", + "Leader", + "Huntress", + "Hunter", + "Dwarf", + "Toad", + "Clams", + "Bell", + "Avocado", + "Egg", + "Spade", + "Stream", + "Cabbage", + "Tomato", + "Rapier", + "Katana", + "Whisper", + "Hammer", + "Axe", + "Sword", + "Saurok", + "Danari", + "Elf", + "Human", + "Draugr", + "Orc", + "Pie", + "Stick", + "Rope", + "Knife", + "Shield", + "Bow", + "Spear", + "Staff", + "Crow", + "Crown", + "Parrot", + "Parrots", + "Pelican", + "Whale", + "Cube", + "Minotaur", + "Oni", + "Monster", + ]; + let kind = self.rng.gen_range(0..10); + let mut choose = |slice: &[&'static str]| *slice.choose(self.rng).unwrap(); + match kind { + 0 => format!("The {} {}", choose(&adjectives), choose(&tavern_synonyms)), + 1..=7 => format!("The {} {}", choose(&adjectives), choose(&subjectives)), + _ => format!( + "The {} {} {}", + choose(&adjectives), + choose(&subjectives), + choose(&tavern_synonyms) + ), + } + } } diff --git a/world/src/site2/mod.rs b/world/src/site2/mod.rs index 4b62ad7a70..9746cce08c 100644 --- a/world/src/site2/mod.rs +++ b/world/src/site2/mod.rs @@ -939,7 +939,7 @@ impl Site { &mut reseed(&mut rng), &site, door_tile, - Dir::from_vector(door_dir), + Dir::from_vec2(door_dir), aabr, ); let tavern_alt = tavern.door_wpos.z; diff --git a/world/src/site2/plot/adlet.rs b/world/src/site2/plot/adlet.rs index c333b6a756..4478520af8 100644 --- a/world/src/site2/plot/adlet.rs +++ b/world/src/site2/plot/adlet.rs @@ -129,7 +129,7 @@ impl AdletStronghold { let mut outer_structures = Vec::<(AdletStructure, Vec2, Dir)>::new(); - let entrance_dir = Dir::from_vector(entrance - cavern_center); + let entrance_dir = Dir::from_vec2(entrance - cavern_center); outer_structures.push((AdletStructure::TunnelEntrance, Vec2::zero(), entrance_dir)); let desired_structures = surface_radius.pow(2) / 100; @@ -176,7 +176,7 @@ impl AdletStronghold { Some((structure_center, structure_kind)) } }) { - let dir_to_wall = Dir::from_vector(rpos); + let dir_to_wall = Dir::from_vec2(rpos); let door_rng: u32 = rng.gen_range(0..9); let door_dir = match door_rng { 0..=3 => dir_to_wall, @@ -352,7 +352,7 @@ impl AdletStronghold { .then_some((structure, rpos)) }) { // Direction facing the central bonfire - let dir = Dir::from_vector(rpos).opposite(); + let dir = Dir::from_vec2(rpos).opposite(); cavern_structures.push((structure, rpos, dir)); } } @@ -493,7 +493,7 @@ impl Structure for AdletStronghold { // Tunnel let dist: f32 = self.cavern_center.as_().distance(self.entrance.as_()); - let dir = Dir::from_vector(self.entrance - self.cavern_center); + let dir = Dir::from_vec2(self.entrance - self.cavern_center); let tunnel_start: Vec3 = match dir { Dir::X => Vec2::new(self.entrance.x + 7, self.entrance.y), Dir::Y => Vec2::new(self.entrance.x, self.entrance.y + 7), diff --git a/world/src/site2/plot/bridge.rs b/world/src/site2/plot/bridge.rs index 64a0a94ae8..c4d0575288 100644 --- a/world/src/site2/plot/bridge.rs +++ b/world/src/site2/plot/bridge.rs @@ -884,7 +884,7 @@ impl Bridge { let min_water_dist = 5; let find_edge = |start: Vec2, end: Vec2| { let mut test_start = start; - let dir = Dir::from_vector(end - start).to_vec2(); + let dir = Dir::from_vec2(end - start).to_vec2(); let mut last_alt = if let Some(col) = land.column_sample(start, index) { col.alt as i32 } else { @@ -938,7 +938,7 @@ impl Bridge { start, end, center, - dir: Dir::from_vector(end.xy() - start.xy()), + dir: Dir::from_vec2(end.xy() - start.xy()), kind: bridge, biome: land .get_chunk_wpos(center.xy()) diff --git a/world/src/site2/plot/gnarling.rs b/world/src/site2/plot/gnarling.rs index ce25e7ad2a..5e4fff446b 100644 --- a/world/src/site2/plot/gnarling.rs +++ b/world/src/site2/plot/gnarling.rs @@ -237,7 +237,7 @@ impl GnarlingFortification { )) } }) { - let dir_to_center = Dir::from_vector(hut_loc.xy()).opposite(); + let dir_to_center = Dir::from_vec2(hut_loc.xy()).opposite(); let door_rng: u32 = rng.gen_range(0..9); let door_dir = match door_rng { 0..=3 => dir_to_center, @@ -262,7 +262,7 @@ impl GnarlingFortification { let chieftain_hut_loc = ((inner_tower_locs[0] + inner_tower_locs[1]) + 2 * outer_wall_corners[chieftain_indices[1]]) / 4; - let chieftain_hut_ori = Dir::from_vector(chieftain_hut_loc).opposite(); + let chieftain_hut_ori = Dir::from_vec2(chieftain_hut_loc).opposite(); structure_locations.push(( GnarlingStructure::ChieftainHut, chieftain_hut_loc.with_z(rpos_height(chieftain_hut_loc)), diff --git a/world/src/site2/plot/tavern.rs b/world/src/site2/plot/tavern.rs index e59f3222c0..bf638b385e 100644 --- a/world/src/site2/plot/tavern.rs +++ b/world/src/site2/plot/tavern.rs @@ -1,9 +1,10 @@ use std::{cmp::Ordering, mem::swap, ops::RangeInclusive}; use common::{ + comp::Content, lottery::Lottery, store::{Id, Store}, - terrain::{Block, BlockKind, SpriteKind}, + terrain::{Block, BlockKind, SpriteCfg, SpriteKind}, }; use enum_map::EnumMap; use hashbrown::HashSet; @@ -12,7 +13,9 @@ use strum::{EnumIter, IntoEnumIterator}; use vek::*; use crate::{ - site2::{Dir, Fill, Site, Structure}, + site::namegen, + site2::{gen::PrimitiveTransform, Dir, Fill, Site, Structure}, + util::RandomField, IndexRef, Land, }; @@ -66,6 +69,7 @@ struct Stairs { } pub struct Tavern { + name: String, rooms: Store, stairs: Store, walls: Store, @@ -204,7 +208,7 @@ impl Tavern { top_alt: entrance_room_aabb.max.z, from: None, to: Some(entrance_id), - to_dir: door_dir, + to_dir: -door_dir, door: Some(door_dir.rotated_cw().select(door_wpos.xy() - start).abs()), }); rooms[entrance_id].walls[door_dir].push(wall_id); @@ -504,7 +508,7 @@ impl Tavern { let p1 = n_room_bounds.projected_point(room_bounds.center()); let p0 = room_bounds.projected_point(p1); - let to_dir = Dir::from_vector(p1 - p0); + let to_dir = Dir::from_vec2(p1 - p0); let intersection = to_dir .extend_aabr(room_bounds, 1) @@ -566,31 +570,46 @@ impl Tavern { // Compute detail areas for room in rooms.values_mut() { - room.detail_areas.push(to_aabr(room.bounds)); + let bounds = to_aabr(room.bounds); + let c = bounds.center(); + let mut b = bounds.split_at_x(c.x); + b[0].max.x -= 1; + room.detail_areas.extend(b.into_iter().flat_map(|b| { + let mut b = b.split_at_y(c.y); + b[0].max.y -= 1; + b + })); for (dir, dir_walls) in room.walls.iter() { for door_pos in dir_walls.iter().filter_map(|wall_id| { let wall = &walls[*wall_id]; wall.door.map(|door| { - let wall_dir = Dir::from_vector(wall.end - wall.start); + let wall_dir = Dir::from_vec2(wall.end - wall.start); wall.start + wall_dir.to_vec2() * door }) }) { let orth = dir.orthogonal(); for i in 0..room.detail_areas.len() { - if let Some([a, b]) = - orth.try_split_aabr(room.detail_areas[i], orth.select(door_pos)) - { - room.detail_areas[i] = a; - room.detail_areas.push(b); + let bc = room.detail_areas[i].center(); + // Check if we are on the doors side of the center of the room. + if dir.select(bc - c) * dir.signum() >= 0 { + if let Some([a, b]) = + orth.try_split_aabr(room.detail_areas[i], orth.select(door_pos)) + { + room.detail_areas[i] = orth.extend_aabr(a, -1); + room.detail_areas.push(orth.opposite().extend_aabr(b, -1)); + } } } + room.detail_areas.retain(|area| area.is_valid()); } } - room.detail_areas.retain(|area| area.size().product() >= 4); } + let name = namegen::NameGen::location(rng).generate_tavern(); + Self { + name, rooms, stairs, walls, @@ -620,7 +639,9 @@ impl Structure for Tavern { let stone = Fill::Brick(BlockKind::Rock, Rgb::new(70, 70, 70), 10); let wood = Fill::Block(Block::new(BlockKind::Wood, Rgb::new(106, 73, 64))); + let dark_wood = Fill::Block(Block::new(BlockKind::Wood, Rgb::new(80, 53, 48))); + let field = RandomField::new(740384); painter .aabb(aabb(Aabb { min: bounds.min.with_z(self.door_wpos.z - 10), @@ -637,20 +658,244 @@ impl Structure for Tavern { })) .fill(wood.clone()); + let room_aabr = Aabr { + min: room.bounds.min.xy(), + max: room.bounds.max.xy(), + }; + let table_set = |pos, bounds: Aabr| -> bool { + if bounds.size().reduce_min() >= 1 { + painter.sprite(pos, SpriteKind::TableDining); + + for dir in Dir::iter() { + let pos = pos + dir.to_vec2(); + if bounds.contains_point(pos.xy()) { + painter.rotated_sprite( + pos, + SpriteKind::ChairSingle, + dir.opposite().sprite_ori(), + ); + } + } + true + } else { + false + } + }; + for (i, aabr) in room.detail_areas.iter().enumerate() { + let color = fxhash::hash32(&i).to_le_bytes(); + + painter + .aabb(aabb(Aabb { + min: aabr.min.with_z(room.bounds.min.z - 1), + max: aabr.max.with_z(room.bounds.min.z - 1), + })) + .fill(Fill::Block(Block::new( + BlockKind::Rock, + Rgb::new(color[0], color[1], color[3]), + ))); + } match room.kind { RoomKind::Garden => { for aabr in room.detail_areas.iter() { + let pos = aabr.center().with_z(room.bounds.min.z); + + if field.chance(pos, 0.6) { + table_set(pos, *aabr); + } + } + + let dir = Dir::from_vec2(room_aabr.size().into()); + + painter + .aabb(aabb(Aabb { + min: dir + .select_aabr_with(room_aabr, room_aabr.min - 2) + .with_z(room.bounds.max.z + 1), + max: dir + .select_aabr_with(room_aabr, room_aabr.max + 2) + .with_z(room.bounds.max.z + 1), + })) + .repeat( + -dir.to_vec3() * 2, + (dir.select(room_aabr.size()) as u32 + 3) / 2, + ) + .fill(dark_wood.clone()) + }, + RoomKind::StageRoom => { + let mut stage = None; + let mut stage_score = 0; + for aabr in room.detail_areas.iter() { + let edges = Dir::iter() + .filter(|dir| dir.select_aabr(*aabr) == dir.select_aabr(room_aabr)) + .count() as i32; + let test_stage_score = edges * aabr.size().product(); + let Some(aabr) = (if stage_score < test_stage_score { + stage_score = test_stage_score; + stage.replace(*aabr) + } else { + Some(*aabr) + }) else { + continue; + }; + + table_set(aabr.center().with_z(room.bounds.min.z), aabr); + for dir in Dir::iter().filter(|dir| { + dir.select_aabr(aabr) == dir.select_aabr(room_aabr) + && dir.rotated_cw().select_aabr(aabr) + == dir.rotated_cw().select_aabr(room_aabr) + }) { + let pos = dir.select_aabr_with( + aabr, + Vec2::broadcast(dir.rotated_cw().select_aabr(aabr)), + ); + painter.sprite(pos.with_z(room.bounds.min.z), SpriteKind::StreetLamp); + } + } + if let Some(aabr) = stage { painter .aabb(aabb(Aabb { min: aabr.min.with_z(room.bounds.min.z), max: aabr.max.with_z(room.bounds.min.z), })) - .fill(Fill::Sprite(SpriteKind::Apple)) + .fill(stone.clone()); + painter + .aabb(aabb(Aabb { + min: (aabr.min + 1).with_z(room.bounds.min.z), + max: (aabr.max - 1).with_z(room.bounds.min.z), + })) + .fill(wood.clone()); + for dir in Dir::iter().filter(|dir| { + dir.select_aabr(aabr) != dir.select_aabr(room_aabr) + && dir.rotated_cw().select_aabr(aabr) + != dir.rotated_cw().select_aabr(room_aabr) + }) { + let pos = dir.select_aabr_with( + aabr, + Vec2::broadcast(dir.rotated_cw().select_aabr(aabr)), + ); + painter + .column(pos, room.bounds.min.z..=room.bounds.max.z) + .fill(dark_wood.clone()); + + for dir in Dir::iter() { + painter.rotated_sprite( + pos.with_z(room.bounds.center().z + 1) + dir.to_vec2(), + SpriteKind::WallSconce, + dir.sprite_ori(), + ); + } + } + } + }, + RoomKind::BarRoom => { + let mut bar = None; + let mut bar_score = 0; + for aabr in room.detail_areas.iter() { + let test_stage_score = Dir::iter() + .any(|dir| dir.select_aabr(*aabr) == dir.select_aabr(room_aabr)) + as i32 + * aabr.size().product(); + let Some(aabr) = (if bar_score < test_stage_score { + bar_score = test_stage_score; + bar.replace(*aabr) + } else { + Some(*aabr) + }) else { + continue; + }; + + for dir in Dir::iter() + .filter(|dir| dir.select_aabr(aabr) == dir.select_aabr(room_aabr)) + { + let pos = dir + .select_aabr_with(aabr, aabr.center()) + .with_z(room.bounds.center().z); + + painter.rotated_sprite( + pos, + SpriteKind::WallLampSmall, + dir.opposite().sprite_ori(), + ); + } + } + if let Some(aabr) = bar { + for dir in Dir::iter() { + let edge = dir.select_aabr(aabr); + let rot_dir = if field.chance(aabr.center().with_z(0), 0.5) { + dir.rotated_cw() + } else { + dir.rotated_ccw() + }; + let rot_edge = rot_dir.select_aabr(aabr); + match ( + edge == dir.select_aabr(room_aabr), + rot_edge == rot_dir.select_aabr(room_aabr), + ) { + (false, _) => { + let (min, max) = ( + dir.select_aabr_with( + aabr, + Vec2::broadcast(rot_dir.select_aabr(aabr)), + ), + dir.select_aabr_with( + aabr, + Vec2::broadcast(rot_dir.opposite().select_aabr(aabr)), + ), + ); + painter + .aabb(aabb(Aabb { + min: (min - rot_dir.to_vec2()) + .with_z(room.bounds.min.z), + max: max.with_z(room.bounds.min.z), + })) + .fill(dark_wood.clone()); + painter + .aabb(aabb(Aabb { + min: min.with_z(room.bounds.min.z + 3), + max: max.with_z(room.bounds.max.z), + })) + .fill(dark_wood.clone()); + }, + (true, true) => { + painter.sprite( + dir.vec2(edge, rot_edge).with_z(room.bounds.min.z), + SpriteKind::CookingPot, + ); + }, + (true, false) => {}, + } + } + } + }, + RoomKind::EntranceRoom => { + for aabr in room.detail_areas.iter() { + let edges = Dir::iter() + .filter(|dir| dir.select_aabr(*aabr) == dir.select_aabr(room_aabr)) + .count(); + let hanger_pos = if edges == 2 { + let pos = aabr.center().with_z(room.bounds.min.z); + painter.sprite(pos, SpriteKind::CoatRack); + Some(pos) + } else { + None + }; + + for dir in Dir::iter() + .filter(|dir| dir.select_aabr(*aabr) == dir.select_aabr(room_aabr)) + { + let pos = dir + .select_aabr_with(*aabr, aabr.center()) + .with_z(room.bounds.center().z + 1); + if hanger_pos.map_or(false, |p| p.xy() != pos.xy()) { + painter.rotated_sprite( + pos, + SpriteKind::WallLampSmall, + dir.opposite().sprite_ori(), + ); + } + } } }, - RoomKind::StageRoom => {}, - RoomKind::BarRoom => {}, - RoomKind::EntranceRoom => {}, } } @@ -660,27 +905,53 @@ impl Structure for Tavern { min: wall.start.with_z(wall.base_alt), max: wall.end.with_z(wall.top_alt), }; + let wall_dir = Dir::from_vec2(wall.end - wall.start); match (wall.from.map(get_kind), wall.to.map(get_kind)) { (Some(RoomKind::Garden), Some(RoomKind::Garden) | None) | (None, Some(RoomKind::Garden)) => { let hgt = wall_aabb.min.z..=wall_aabb.max.z; painter .column(wall_aabb.min.xy(), hgt.clone()) - .fill(wood.clone()); - painter.column(wall_aabb.max.xy(), hgt).fill(wood.clone()); + .fill(dark_wood.clone()); + painter + .column(wall_aabb.max.xy(), hgt) + .fill(dark_wood.clone()); + let z = (wall.base_alt + wall.top_alt) / 2; + + painter.rotated_sprite( + wall_aabb.min.with_z(z) + wall_dir.to_vec2(), + SpriteKind::WallSconce, + wall_dir.sprite_ori(), + ); + painter.rotated_sprite( + wall_aabb.max.with_z(z) - wall_dir.to_vec2(), + SpriteKind::WallSconce, + wall_dir.opposite().sprite_ori(), + ); painter .aabb(aabb(Aabb { min: wall_aabb.min, max: wall_aabb.max.with_z(wall_aabb.min.z), })) - .fill(wood.clone()); + .fill(dark_wood.clone()); + painter + .aabb(aabb(Aabb { + min: wall_aabb.min.with_z(wall_aabb.max.z), + max: wall_aabb.max, + })) + .fill(dark_wood.clone()); }, (None, None) => {}, _ => { painter.aabb(aabb(wall_aabb)).fill(wood.clone()); + painter + .column(wall.start, wall.base_alt..=wall.top_alt) + .fill(dark_wood.clone()); + painter + .column(wall.end, wall.base_alt..=wall.top_alt) + .fill(dark_wood.clone()); }, } - let wall_dir = Dir::from_vector(wall.end - wall.start); if let Some(door) = wall.door { let door_pos = wall.start + wall_dir.to_vec2() * door; let min = match wall.from { @@ -698,6 +969,64 @@ impl Structure for Tavern { })) .clear(); } + if let (Some(room), to @ None) | (None, to @ Some(room)) = + (wall.from.map(get_kind), wall.to.map(get_kind)) + { + let dir = if to.is_none() { + -wall.to_dir + } else { + wall.to_dir + }; + let width = dir.orthogonal().select(wall.end - wall.start).abs(); + let wall_center = (wall.start + wall.end) / 2; + let d = wall_dir.select(wall_center - wall.start) * wall_dir.signum(); + match room { + RoomKind::Garden => { + if wall.door.map_or(true, |door| (door - d).abs() >= 2) { + painter.rotated_sprite( + wall_center.with_z(wall.base_alt + 1), + SpriteKind::Planter, + dir.sprite_ori(), + ); + } + }, + _ => { + if width >= 5 && wall.door.map_or(true, |door| (door - d).abs() > 3) { + painter + .aabb(aabb(Aabb { + min: (wall_center + dir.rotated_ccw().to_vec2()) + .with_z(wall.base_alt + 1), + max: (wall_center + dir.rotated_cw().to_vec2()) + .with_z(wall.base_alt + 2), + })) + .fill(Fill::RotatedSprite(SpriteKind::Window1, dir.sprite_ori())); + } + }, + } + if let Some(door) = wall.door { + let door_pos = wall.start + wall_dir.to_vec2() * door; + let diff = door_pos - wall_aabb.center().xy(); + let orth = if diff == Vec2::zero() { + wall_dir + } else { + Dir::from_vec2(diff) + }; + let pos = door_pos - dir.to_vec2() + orth.to_vec2(); + let (sprite, z) = match room { + RoomKind::Garden => (SpriteKind::Sign, 0), + _ => (SpriteKind::HangingSign, 2), + }; + painter.rotated_sprite_with_cfg( + pos.with_z(wall.base_alt + z), + sprite, + dir.opposite().sprite_ori(), + SpriteCfg { + unlock: None, + content: Some(Content::Plain(self.name.clone())), + }, + ); + } + } } for (_, stairs) in self.stairs.iter() { diff --git a/world/src/site2/util/mod.rs b/world/src/site2/util/mod.rs index 7d0b035072..50e008093c 100644 --- a/world/src/site2/util/mod.rs +++ b/world/src/site2/util/mod.rs @@ -26,7 +26,7 @@ impl Dir { } } - pub fn from_vector(vec: Vec2) -> Dir { + pub fn from_vec2(vec: Vec2) -> Dir { if vec.x.abs() > vec.y.abs() { if vec.x > 0 { Dir::X } else { Dir::NegX } } else if vec.y > 0 { @@ -110,6 +110,15 @@ impl Dir { } } + pub fn vec2(self, x: i32, y: i32) -> Vec2 { + match self { + Dir::X => Vec2::new(x, y), + Dir::NegX => Vec2::new(x, y), + Dir::Y => Vec2::new(y, x), + Dir::NegY => Vec2::new(y, x), + } + } + /// Returns a 3x3 matrix that rotates Vec3(1, 0, 0) to the direction you get /// in to_vec3. Inteded to be used with Primitive::Rotate. ///