Skip to content
This repository has been archived by the owner on Aug 2, 2023. It is now read-only.

Add second airport to the map #46

Merged
merged 5 commits into from
Mar 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/atc/v1/map.proto
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ message Airport {
}

message Map {
Airport airport = 1;
repeated Airport airports = 1;
repeated Node routing_grid = 2;
}

Expand Down
28 changes: 19 additions & 9 deletions game/src/api/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,25 @@ mod tests {
let map = response.into_inner().map.unwrap();

assert_eq!(
Airport {
node: Some(Node {
longitude: 0,
latitude: 0,
restricted: false
}),
tag: Tag::Red.into()
},
map.airport.unwrap()
vec![
Airport {
node: Some(Node {
longitude: -2,
latitude: -2,
restricted: false
}),
tag: Tag::Red.into()
},
Airport {
node: Some(Node {
longitude: 1,
latitude: 4,
restricted: false
}),
tag: Tag::Blue.into()
}
],
map.airports
);
}

Expand Down
2 changes: 2 additions & 0 deletions game/src/components/tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::api::AsApi;

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Component)]
pub enum Tag {
Blue,
Red,
}

Expand All @@ -14,6 +15,7 @@ impl AsApi for Tag {

fn as_api(&self) -> Self::ApiType {
match self {
Tag::Blue => ApiTag::Blue,
Tag::Red => ApiTag::Red,
}
}
Expand Down
46 changes: 29 additions & 17 deletions game/src/map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub const MAP_WIDTH_RANGE: RangeInclusive<i32> = -(MAP_WIDTH as i32 / 2)..=(MAP_

#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct Map {
airport: Airport,
airports: Vec<Airport>,
routing_grid: Vec<Node>,
}

Expand All @@ -42,8 +42,8 @@ impl Map {
Self::default()
}

pub fn airport(&self) -> &Airport {
&self.airport
pub fn airports(&self) -> &[Airport] {
&self.airports
}

pub fn routing_grid(&self) -> &Vec<Node> {
Expand All @@ -53,11 +53,15 @@ impl Map {

impl Default for Map {
fn default() -> Self {
let airport = Airport::new(Node::unrestricted(0, 0), Direction::West, Tag::Red);
let routing_grid = generate_routing_grid(&airport);
let airports = vec![
Airport::new(Node::unrestricted(-2, -2), Direction::West, Tag::Red),
Airport::new(Node::unrestricted(1, 4), Direction::South, Tag::Blue),
];

let routing_grid = generate_routing_grid(&airports);

Self {
airport,
airports,
routing_grid,
}
}
Expand All @@ -68,28 +72,36 @@ impl AsApi for Map {

fn as_api(&self) -> Self::ApiType {
ApiMap {
airport: Some(self.airport.as_api()),
airports: self
.airports
.iter()
.map(|airport| airport.as_api())
.collect(),
routing_grid: self.routing_grid.iter().map(|node| node.as_api()).collect(),
}
}
}

fn generate_routing_grid(airport: &Airport) -> Vec<Node> {
let airport_node = airport.node();
fn generate_routing_grid(airports: &[Airport]) -> Vec<Node> {
let mut nodes = Vec::with_capacity(MAP_WIDTH * MAP_HEIGHT);

for y in MAP_HEIGHT_RANGE {
for x in MAP_WIDTH_RANGE {
let node = Node::unrestricted(x, y);
nodes.push(Node::unrestricted(x, y));
}
}

let direction_to_airport =
Direction::between(&node.as_point(), &airport_node.as_point());
for airport in airports {
let airport_node = airport.node();

let restricted = airport_node != &node
&& airport_node.is_neighbor(&node)
&& direction_to_airport != airport.runway();
for neighbor in airport_node.neighbors() {
let direction_to_airport =
Direction::between(&neighbor.as_point(), &airport_node.as_point());

nodes.push(Node::new(x, y, restricted));
if direction_to_airport != airport.runway() {
*nodes.get_mut(neighbor.as_index()).unwrap() =
Node::restricted(neighbor.longitude(), neighbor.latitude());
}
}
}

Expand All @@ -106,7 +118,7 @@ mod tests {
fn generate_routing_grid_removes_neighbors() {
let map = Map::default();

let airport = map.airport().node();
let airport = map.airports().get(0).unwrap().node();
let neighbors = vec![
Node::restricted(airport.longitude(), airport.latitude() + 1),
Node::restricted(airport.longitude() + 1, airport.latitude() + 1),
Expand Down
34 changes: 23 additions & 11 deletions game/src/map/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use geo::{point, Point};
use atc::v1::Node as ApiNode;

use crate::api::AsApi;
use crate::map::{MAP_HEIGHT_RANGE, MAP_WIDTH_RANGE};
use crate::map::{MAP_HEIGHT, MAP_HEIGHT_RANGE, MAP_WIDTH, MAP_WIDTH_RANGE};
use crate::TILE_SIZE;

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
Expand All @@ -18,15 +18,6 @@ pub struct Node {
}

impl Node {
pub fn new(longitude: i32, latitude: i32, restricted: bool) -> Self {
Self {
longitude,
latitude,
restricted,
}
}

#[cfg(test)]
pub fn restricted(longitude: i32, latitude: i32) -> Self {
Self {
longitude,
Expand Down Expand Up @@ -86,6 +77,15 @@ impl Node {
delta_x.abs() <= 1 && delta_y.abs() <= 1
}

pub fn as_index(&self) -> usize {
let x = self.longitude + MAP_WIDTH as i32 / 2;
let y = self.latitude + MAP_HEIGHT as i32 / 2;

let index = (y * MAP_WIDTH as i32) + x;

index as usize
}

pub fn as_point(&self) -> Point<f32> {
let x = (self.longitude * TILE_SIZE) as f32;
let y = (self.latitude * TILE_SIZE) as f32;
Expand Down Expand Up @@ -141,7 +141,7 @@ impl AsApi for Node {
mod tests {
use geo::point;

use crate::map::{MAP_HEIGHT_RANGE, MAP_WIDTH_RANGE};
use crate::map::{MAP_HEIGHT, MAP_HEIGHT_RANGE, MAP_WIDTH, MAP_WIDTH_RANGE};

use super::{Node, TILE_SIZE};

Expand Down Expand Up @@ -220,6 +220,18 @@ mod tests {
assert!(!neighbor.is_neighbor(&node));
}

#[test]
fn as_index_at_bottom_left() {
let node = Node::unrestricted(*MAP_WIDTH_RANGE.start(), *MAP_HEIGHT_RANGE.start());
assert_eq!(0, node.as_index());
}

#[test]
fn as_index_at_top_right() {
let node = Node::unrestricted(*MAP_WIDTH_RANGE.end(), *MAP_HEIGHT_RANGE.end());
assert_eq!((MAP_WIDTH * MAP_HEIGHT) - 1, node.as_index());
}

#[test]
fn trait_display() {
let node = Node::unrestricted(1, 2);
Expand Down
62 changes: 33 additions & 29 deletions game/src/systems/despawn_airplane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,44 +12,48 @@ pub fn despawn_airplane(
mut query: Query<(Entity, &AirplaneId, &mut FlightPlan, &Tag, &Transform), With<Landing>>,
event_bus: Local<EventBus>,
) {
let airport_location = map.airport().node().as_vec3(2.0);
let airport_tag = map.airport().tag();

for (entity, airplane_id, mut flight_plan, tag, transform) in query.iter_mut() {
let airplane_location = transform.translation;
for airport in map.airports() {
let airport_location = airport.node().as_vec3(2.0);
let airport_tag = airport.tag();

if airplane_location != airport_location {
continue;
}
let airplane_location = transform.translation;

if airplane_location != airport_location {
continue;
}

if tag == &airport_tag {
commands.entity(entity).despawn_recursive();

if tag == &airport_tag {
commands.entity(entity).despawn_recursive();
score.increment();

score.increment();
event_bus
.sender()
.send(Event::AirplaneLanded(airplane_id.clone()))
.expect("failed to send event"); // TODO: Handle error
} else {
let go_around = go_around_procedure(airport);

event_bus
.sender()
.send(Event::AirplaneLanded(airplane_id.clone()))
.expect("failed to send event"); // TODO: Handle error
} else {
let go_around = go_around_procedure(map.airport());
*flight_plan = FlightPlan::new(vec![go_around]);

*flight_plan = FlightPlan::new(vec![go_around]);
commands.entity(entity).remove::<Landing>();

commands.entity(entity).remove::<Landing>();
event_bus
.sender()
.send(Event::LandingAborted(airplane_id.clone()))
.expect("failed to send event"); // TODO: Handle error

event_bus
.sender()
.send(Event::LandingAborted(airplane_id.clone()))
.expect("failed to send event"); // TODO: Handle error
event_bus
.sender()
.send(Event::FlightPlanUpdated(
airplane_id.clone(),
flight_plan.clone(),
))
.expect("failed to send event"); // TODO: Handle error
}

event_bus
.sender()
.send(Event::FlightPlanUpdated(
airplane_id.clone(),
flight_plan.clone(),
))
.expect("failed to send event"); // TODO: Handle error
break;
}
}
}
Expand Down
42 changes: 12 additions & 30 deletions game/src/systems/generate_flight_plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,52 +43,34 @@ pub fn generate_random_plan(travelled_route: &TravelledRoute, map: &Res<Map>) ->
let mut next_node;

for _ in 0..FLIGHT_PLAN_LENGTH {
next_node = pick_next_tile(&current_node, &previous_node, map.routing_grid());
next_node = pick_next_tile(&current_node, &previous_node, map);
flight_plan.push(next_node);

if &next_node == map.airport().node() {
break;
}

previous_node = Some(current_node);
current_node = next_node;
}

FlightPlan::new(flight_plan.iter().rev().cloned().collect())
}

fn pick_next_tile(
current_tile: &Node,
previous_tile: &Option<Node>,
routing_grid: &[Node],
) -> Node {
let mut potential_tiles = current_tile.neighbors();

// Mustn't be the previous tile
if let Some(previous_tile) = previous_tile {
if let Some(index) = potential_tiles
.iter()
.position(|tile| tile == previous_tile)
{
potential_tiles.remove(index);
}
}

// Mustn't be a restricted node
let potential_tiles: Vec<Node> = potential_tiles
fn pick_next_tile(current_tile: &Node, previous_tile: &Option<Node>, map: &Map) -> Node {
let airports: Vec<&Node> = map
.airports()
.iter()
.filter(|tile| routing_grid.contains(*tile))
.cloned()
.map(|airport| airport.node())
.collect();

// Shouldn't be too close to the edge of the map
let potential_tiles: Vec<Node> = potential_tiles
.iter()
let potential_tiles: Vec<Node> = current_tile
.neighbors()
.into_iter()
.filter(|node| &Some(*node) != previous_tile)
.filter(|node| !airports.contains(&node))
.filter(|node| !node.is_restricted())
.filter(|tile| {
// Shouldn't be too close to the edge of the map
tile.longitude().abs() != *MAP_WIDTH_RANGE.end()
&& tile.latitude().abs() != *MAP_HEIGHT_RANGE.end()
})
.cloned()
.collect();

// Pick random neighbor of the current tile
Expand Down
8 changes: 4 additions & 4 deletions game/src/systems/land_airplane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ pub fn land_airplane(
map: Res<Map>,
query: Query<(Entity, &FlightPlan), Without<Landing>>,
) {
let airport = map.airport();

for (entity, flight_plan) in query.iter() {
if flight_plan.get().len() != 1 {
continue;
}

let final_node = flight_plan.get().get(0).unwrap();

if final_node == airport.node() {
commands.entity(entity).insert(Landing);
for airport in map.airports() {
if final_node == airport.node() {
commands.entity(entity).insert(Landing);
}
}
}
}
Loading