use std::cell::RefCell;
use std::rc::Rc;
use hocon_rs::Value;
use logic_constructor::prelude::*;
#[derive(Clone)]
pub struct PlayerEntity {
pub hp: Rc<RefCell<f32>>,
}
#[derive(Clone)]
pub struct EnemyEntity {
pub hp: Rc<RefCell<f32>>,
}
#[derive(Clone)]
pub struct NpcEntity {
pub age: i16,
}
#[derive(Clone)]
pub enum LcGameEntity {
Player(PlayerEntity),
Enemy(EnemyEntity),
Npc(NpcEntity),
}
impl LcEntityType for LcGameEntity {
fn type_id(&self) -> LcEntityTypeId {
match self {
LcGameEntity::Player(_) => 1,
LcGameEntity::Enemy(_) => 2,
LcGameEntity::Npc(_) => 3,
}
}
}
impl From<PlayerEntity> for LcEntity<LcGameEntity> {
fn from(value: PlayerEntity) -> Self {
LcEntity {
game_entity: LcGameEntity::Player(value),
}
}
}
impl From<EnemyEntity> for LcEntity<LcGameEntity> {
fn from(value: EnemyEntity) -> Self {
LcEntity {
game_entity: LcGameEntity::Enemy(value),
}
}
}
impl From<NpcEntity> for LcEntity<LcGameEntity> {
fn from(value: NpcEntity) -> Self {
LcEntity {
game_entity: LcGameEntity::Npc(value),
}
}
}
impl LcGameEntity {
pub fn maybe_health(&self) -> Option<Rc<RefCell<f32>>> {
match self {
LcGameEntity::Player(e) => Some(e.hp.clone()),
LcGameEntity::Enemy(e) => Some(e.hp.clone()),
LcGameEntity::Npc(_) => None,
}
}
}
macro_rules! impl_lc_game_action {
($t:ty) => {
impl LcAction<LcGameEntity> for $t {
fn apply(&self, source: &LcEntity<LcGameEntity>, target: &LcEntity<LcGameEntity>) {
<Self as LcGameAction>::apply(
self,
source.game_entity.clone(),
target.game_entity.clone(),
)
}
fn clone_box(&self) -> Box<dyn LcAction<LcGameEntity>> {
Box::new(self.clone())
}
}
};
}
pub trait LcGameAction: LcAction<LcGameEntity> {
fn apply(&self, source: LcGameEntity, target: LcGameEntity);
}
#[derive(Clone)]
pub struct DealDamage {
pub amount: f32,
}
impl_lc_game_action!(DealDamage);
impl LcGameAction for DealDamage {
fn apply(&self, _source: LcGameEntity, target: LcGameEntity) {
if let Some(health) = target.maybe_health() {
*health.borrow_mut() -= self.amount;
}
}
}
#[derive(Clone)]
pub struct Heal {
pub amount: f32,
}
impl_lc_game_action!(Heal);
impl LcGameAction for Heal {
fn apply(&self, _source: LcGameEntity, target: LcGameEntity) {
if let Some(health) = target.maybe_health() {
*health.borrow_mut() += self.amount;
}
}
}
pub fn parse_game_effect(value: &Value) -> Result<Box<dyn LcAction<LcGameEntity>>, String> {
let obj = value
.as_object()
.ok_or_else(|| format!("expected object, got {:?}", value))?;
if obj.len() != 1 {
return Err(format!("expected single key, got {}", obj.len()));
}
let (name, inner) = obj.iter().next().unwrap();
let amount = match inner {
Value::Number(n) => n
.as_f64()
.ok_or_else(|| format!("{} expects a number", name))? as f32,
_ => return Err(format!("{} expects a number", name)),
};
match name.as_str() {
"DealDamage" => Ok(Box::new(DealDamage { amount })),
"Heal" => Ok(Box::new(Heal { amount })),
other => Err(format!("unknown effect: {}", other)),
}
}