mlcg 0.0.1

Mindustry Logic Code Generator
Documentation
use super::building::{Building, Buildings};
use super::number::Number;
use super::Type;
use crate::abilities::*;
use crate::command::ucontrol;
use crate::env::*;
use crate::eval::Eval;
use crate::processor::Processor;
use crate::r#ref::Ref;
use crate::String;
use std::marker::PhantomData;

#[derive(Eval, Debug, Clone)]
pub struct Unit<U: Units = Binding> {
    pub(crate) name: String,
    pub(crate) _unit: PhantomData<U>,
}

impl<U: Units> Unit<U> {
    pub fn class_name(&self) -> &'static str {
        U::class_name()
    }
}

impl<U: Units> Type for Unit<U> {
    fn from_name(name: crate::String) -> Self {
        Self {
            name,
            _unit: PhantomData,
        }
    }
}

impl<U: Units> Eval<String> for Unit<U> {
    fn eval(self) -> String {
        self.name
    }
}

impl<U: Units> Target for Unit<U> {}

pub trait Units {
    fn class_name() -> &'static str;
}

impl Processor {
    pub fn unit_bind<U: Units>(&self) -> Ref<'_, Unit> {
        self.borrow_mut()
            .push_command(crate::command::ubind::Ubind {
                ty: U::class_name().eval(),
            });
        self.unit()
    }

    pub fn bind<U: Units>(&self, unit: Ref<'_, Unit<U>>) -> Ref<'_, Unit> {
        self.unit().set_to(unit.cast::<Unit>())
    }
}

impl<'a, U: Units> Ref<'a, Unit<U>> {
    pub fn bind(&self) -> Ref<'a, Unit> {
        self.core.bind(*self)
    }

    pub fn cast_unit<U2: Units>(&self, unit: U2) -> Ref<'a, Unit<U2>> {
        _ = unit;
        self.cast()
    }
}

impl<'a> Ref<'a, Unit> {
    fn ucontrol(&self, command: impl Into<ucontrol::Ucontrol>) {
        self.core.borrow_mut().push_command(command.into());
    }

    pub fn idle(&self) {
        self.ucontrol(ucontrol::Idle {});
    }

    pub fn r#move(&self, x: impl Eval<Number>, y: impl Eval<Number>) {
        assert_same_core!(self, x, y);
        let command = ucontrol::Move {
            x: x.eval().eval(),
            y: y.eval().eval(),
        };
        self.ucontrol(command);
    }

    pub fn approach(&self, x: impl Eval<Number>, y: impl Eval<Number>, radius: impl Eval<Number>) {
        assert_same_core!(self, x, y, radius);
        let command = ucontrol::Approach {
            x: x.eval().eval(),
            y: y.eval().eval(),
            radius: radius.eval().eval(),
        };
        self.ucontrol(command);
    }

    pub fn path_find(&self, x: impl Eval<Number>, y: impl Eval<Number>) {
        assert_same_core!(self, x, y);
        let command = ucontrol::PathFind {
            x: x.eval().eval(),
            y: y.eval().eval(),
        };
        self.ucontrol(command);
    }

    pub fn auto_path_find(&self) {
        self.ucontrol(ucontrol::AutoPathFind {});
    }

    pub fn boost(&self, enable: impl Eval<Number>) {
        assert_same_core!(self, enable);
        let command = ucontrol::Boost {
            enable: enable.eval().eval(),
        };
        self.ucontrol(command);
    }

    #[doc(alias = "shoot")]
    pub fn target(&self, x: impl Eval<Number>, y: impl Eval<Number>, shoot: impl Eval<Number>) {
        assert_same_core!(self, x, y, shoot);
        let command = ucontrol::Target {
            x: x.eval().eval(),
            y: y.eval().eval(),
            shoot: shoot.eval().eval(),
        };
        self.ucontrol(command);
    }

    #[doc(alias = "shootp")]
    pub fn targetp<At: Target>(&self, at: impl Eval<At>, shoot: impl Eval<Number>) {
        assert_same_core!(self, at, shoot);
        let command = ucontrol::Targetp {
            unit: at.eval().eval(),
            shoot: shoot.eval().eval(),
        };
        self.ucontrol(command);
    }

    pub fn item_drop<B: Buildings>(&self, to: impl Eval<Building<B>>, amount: impl Eval<Number>) {
        assert_same_core!(self, to, amount);
        let command = ucontrol::ItemDrop {
            to: to.eval().eval(),
            amount: amount.eval().eval(),
        };
        self.ucontrol(command);
    }

    pub fn item_take<B: Buildings>(
        &self,
        from: impl Eval<Building<B>>,
        item: impl Eval<String>,
        amount: impl Eval<Number>,
    ) {
        assert_same_core!(self, from, item, amount);
        let command = ucontrol::ItemTake {
            from: from.eval().eval(),
            item: item.eval(),
            amount: amount.eval().eval(),
        };
        self.ucontrol(command);
    }

    pub fn pay_drop(&self) {
        let command = ucontrol::PayDrop {};
        self.ucontrol(command);
    }

    pub fn pay_take(&self, take_units: impl Eval<Number>) {
        assert_same_core!(self, take_units);
        let command = ucontrol::PayTake {
            take_units: take_units.eval().eval(),
        };
        self.ucontrol(command);
    }

    pub fn pay_enter(&self) {
        let command = ucontrol::PayEnter {};
        self.ucontrol(command);
    }

    pub fn mine(&self, x: impl Eval<Number>, y: impl Eval<Number>) {
        assert_same_core!(self, x, y);
        let command = ucontrol::Mine {
            x: x.eval().eval(),
            y: y.eval().eval(),
        };
        self.ucontrol(command);
    }

    pub fn flag(&self, value: impl Eval<Number>) {
        assert_same_core!(self, value);
        let command = ucontrol::Flag {
            value: value.eval().eval(),
        };
        self.ucontrol(command);
    }

    // build

    // getblock

    pub fn within(
        &self,
        x: impl Eval<Number>,
        y: impl Eval<Number>,
        radius: impl Eval<Number>,
    ) -> Ref<'_, Number> {
        assert_same_core!(self, x, y, radius);
        let result = self.core.new_unnamed();
        let command = ucontrol::Within {
            x: x.eval().eval(),
            y: y.eval().eval(),
            radius: radius.eval().eval(),
            result: result.eval(),
        };
        self.ucontrol(command);
        result
    }

    pub fn unbind(&self) {
        let command = ucontrol::Unbind {};
        self.ucontrol(command);
    }
}

pub trait Land: Units + Sepro {}
pub trait Air: Units + Sepro {}
pub trait Naval: Units + Sepro {}

pub trait Attack: Units + Sepro {}

pub trait Support: Units + Sepro {}

pub trait Legs: Units + Sepro + Land {}

pub trait Tank: Units + Erekir {}

pub trait Mech: Units + Erekir {}

pub trait Flying: Units + Erekir {}

pub trait Neoplasm: Units + Erekir {}

pub trait Core: Units {}

pub trait Internal: Units {}

macro_rules! units {
    ($(
        $unit:ident => $class:literal : $( $trait:ident ),+ && $( $ability:ident ),* ;
    )*) => {$(
        pub struct $unit;

        impl Units for $unit {
            fn class_name() -> &'static str {
                concat!('@', $class)
            }
        }

        $(
        impl $trait for $unit {}
        )*

        $(
        impl $ability for $unit {}
        impl $ability for Unit<$unit> {}
        )*

    )*}
}

pub type AnyUnit = Binding;

units! {
    Binding  => "unit"     : Sepro, Erekir, Land, Air, Naval, Support, Legs, Tank, Flying, Neoplasm, Core, Internal && Shoot, Boost;

    Dagger   => "dagger"   : Sepro, Land, Attack && Shoot;
    Mace     => "mace"     : Sepro, Land, Attack && Shoot;
    Fortress => "fortress" : Sepro, Land, Attack && Shoot;
    Scepter  => "scepter"  : Sepro, Land, Attack && Shoot;
    Reign    => "reign"    : Sepro, Land, Attack && Shoot;

    Nova     => "nova"     : Sepro, Land, Support && Shoot, Boost;
    Pulsar   => "pulsar"   : Sepro, Land, Support && Shoot, Boost;
    Quasar   => "quasar"   : Sepro, Land, Support && Shoot, Boost;
    Vela     => "vela"     : Sepro, Land, Support && Shoot, Boost;
    Corvus   => "corvus"   : Sepro, Land, Support && Shoot, Boost;
}