aboutsummaryrefslogtreecommitdiff
path: root/games/rstnode/rst-core/src/data.rs
//! Data structures for the game

#![allow(unused)]

use crate::io::Io;
use async_std::sync::Arc;
use rand::{seq::SliceRandom, thread_rng};
use ratman::Router;
use serde::{Deserialize, Serialize};
use std::{
    collections::VecDeque,
    sync::atomic::{AtomicBool, AtomicU16, AtomicU32, Ordering},
};

pub type NodeId = usize;

/// A node is a computer on the network graph
///
/// It's owned by a player, and has some upgrade state, as well as
/// base stats.
#[derive(Serialize, Deserialize)]
pub struct Node {
    /// Each node has a unique ID by which it's addressed
    pub id: NodeId,
    /// The current health
    pub health: AtomicU32,
    /// The max health
    pub max_health: AtomicU32,
    /// The owner of this node
    pub owner: Owner,
    /// Upgrade state
    pub type_: Upgrade,
    /// Number of links on the map
    pub links: u8,
    /// Active link states
    pub link_states: Vec<Arc<Link>>,
    /// Router state
    #[serde(skip)]
    pub router: Option<Router>,
    /// Input buffer
    #[serde(skip)]
    pub buffer: VecDeque<Packet>,
}

pub type LinkId = usize;

/// A one-to-one link between two nodes
#[derive(Serialize, Deserialize)]
pub struct Link {
    /// This link ID
    pub id: LinkId,
    /// Node 1
    pub a: NodeId,
    /// Node 2
    pub b: NodeId,
    /// The step length
    pub length: usize,
    /// Packets present on this link
    #[serde(skip)]
    pub pp: Vec<(Packet, AtomicU32)>,
    /// Actual Rx, Tx pair
    #[serde(skip)]
    pub io: Io,
}

pub type PacketId = usize;

/// A packet going across the network
pub struct Packet {
    /// The packet ID
    id: PacketId,
    /// Declare this packet to be removed
    dead: AtomicBool,
    /// Each packet is owned by a player
    owner: Arc<Player>,
    /// What type of packet this is
    data_: PacketType,
}

pub type PlayerId = usize;

/// A player who's having fun
#[derive(Serialize, Deserialize)]
pub struct Player {
    /// A unique player ID (per match)
    pub id: PlayerId,
    /// The player name
    pub name: String,
    /// Player color
    pub color: Color,
    /// The player's money
    pub money: AtomicU16,
}

// This is required because atomics can't safely be cloned
impl Clone for Player {
    fn clone(&self) -> Self {
        let Self {
            ref id,
            ref name,
            ref color,
            ref money,
        } = self;

        Self {
            id: id.clone(),
            name: name.clone(),
            color: color.clone(),
            money: money.load(Ordering::Relaxed).into(),
        }
    }
}

/// Optionally, players can create teams
#[derive(Clone, Serialize, Deserialize)]
pub struct Team {
    /// Name of the team
    name: String,
    /// Unified color of the team
    color: Color,
    /// All team members by their ID
    roster: Vec<u16>,
}

/// An RGB color without alpha
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Color(pub u8, pub u8, pub u8);

impl Color {
    pub fn black() -> Self {
        Self(50, 50, 50)
    }

    pub fn red() -> Self {
        Self(250, 50, 50)
    }

    pub fn green() -> Self {
        Self(100, 250, 100)
    }

    pub fn blue() -> Self {
        Self(100, 100, 250)
    }

    pub fn teal() -> Self {
        Self(150, 250, 250)
    }

    pub fn purple() -> Self {
        Self(150, 100, 250)
    }

    pub fn orange() -> Self {
        Self(250, 200, 100)
    }

    pub fn yellow() -> Self {
        Self(250, 250, 100)
    }

    pub fn white() -> Self {
        Self(225, 225, 225)
    }
}

pub trait ColorPalette {
    /// Create a new color palette
    fn palette() -> Self;
    /// Get a palette without a certain colour
    fn without(&mut self, b: &Color);
    /// Mix a color back into the available palette
    fn remix(&mut self, new: Color);
}

impl ColorPalette for Vec<Color> {
    fn palette() -> Self {
        let mut rng = thread_rng();
        let mut pal = vec![
            Color::black(),
            Color::red(),
            Color::green(),
            Color::blue(),
            Color::teal(),
            Color::purple(),
            Color::orange(),
            Color::yellow(),
            Color::white(),
        ];
        pal.shuffle(&mut rng);
        pal
    }

    /// Drop a colour from the palette
    fn without(&mut self, b: &Color) {
        if let Some(pos) = self.into_iter().rposition(|a| a == b) {
            self.remove(pos);
        }
    }

    fn remix(&mut self, new: Color) {
        let mut rng = thread_rng();
        self.push(new);
        self.shuffle(&mut rng);
    }
}

/// Describes ownership state
#[derive(Serialize, Deserialize)]
pub enum Owner {
    /// Nobody owns this
    Neutral,
    /// A player owns this
    Player(Player),
}

/// Encodes upgrade level without numbers
#[derive(Copy, Clone, Serialize, Deserialize)]
pub enum Level {
    /// 1
    One,
    /// 2
    Two,
    /// 3 (wow)
    Three,
}

/// Describes upgrade state
#[derive(Copy, Clone, Serialize, Deserialize)]
pub enum Upgrade {
    /// A basic node
    Base,
    /// Battle (attack/defence) nodes
    Guard(Level),
    /// These nodes make money
    Compute(Level),
    /// Good at packet switching
    Relay(Level),
}

/// Possible types of packets
pub enum PacketType {
    /// A keepalive packet
    ///
    /// These are sent by all nodes if their neighbours are either
    /// friendly or neutral, and used to keep the network alive.
    /// Sending them costs nothing, and when they are received, they
    /// yield a small amount of funds, and restoring health of a node.
    Ping,
    /// A non exploit capture
    ///
    /// This is a packet that can be sent out by any node (except a
    /// switch) to claim a neutral node on the network.  The path to
    /// the node needs to consist only of friendlies, and if an enemy
    /// node processes this type, nothing happens.
    Capture,
    /// A compute packet
    ///
    /// The first value is the target compute value, which each
    /// compute node adds on to.  The second value is the current.  If
    /// a compute packet passes through a compromised or enemy node,
    /// it might subtract from the second value, before palling it on.
    Compute {
        max: u16,
        curr: AtomicU16,
        step: u16,
    },
    /// A special wrapper packet generated by guards
    ///
    /// Sometimes, when a hostily attack packet encounters a guard, it
    /// manages to capture the attack, and forwards it to a random
    /// compute node.  If the node manages to handle the packet,
    /// without it getting dropped in the meantime), it yields a
    /// specified reward, like a computation would.
    Payload { inner: Box<PacketType>, reward: u16 },
    /// A reset attack packet
    ///
    /// When encountering a hostile node, it will make that node drop
    /// all packets in it's buffers.
    Reset,
    /// Cross-node-scripting attack
    ///
    /// Decreases the strength of a node, also having a small chance
    /// of spawning a new packet into a random output buffer.  When
    /// applied to a neutral node, it makes capturing nodes go faster.
    CNS,
    /// Node-in-the-middle attack
    ///
    /// Infect the routing behaviour of a node to route all traffic to
    /// a specified enemy node instead
    Nitm,
    /// Virus infection attack
    ///
    /// Infects a node to capture it's earnings, both active and
    /// passive, for a short time, without taking on it's costs.
    Virus,
    /// A total control exploit
    ///
    /// This is very hard to do, and a node will basically always
    /// resist it, but if successful, transforms the node into a guard
    /// node and yields control to the attackinng player.
    TakeOver,
}