use rand::Rng; use specs::{ ReadStorage, WriteStorage, Write, System, Entities, Join }; use crate::{ components::{ Health, AttackInbox, AttackType, Trigger, TriggerBox, Position, Autofight, Ear, ear::{Notification, HealthNotification::{Attack, Damage, Heal}, say}, Visible }, resources::NewEntities, Template, util }; pub struct Attacking; impl <'a> System<'a> for Attacking { type SystemData = ( Entities<'a>, WriteStorage<'a, AttackInbox>, WriteStorage<'a, Health>, WriteStorage<'a, TriggerBox>, ReadStorage<'a, Position>, Write<'a, NewEntities>, WriteStorage<'a, Autofight>, WriteStorage<'a, Ear>, ReadStorage<'a, Visible> ); fn run(&mut self, (entities, mut attackeds, mut healths, mut triggerboxes, positions, mut new, mut autofighters, mut ears, visibles): Self::SystemData) { for (entity, attacked, autofighter) in (&entities, &attackeds, &mut autofighters).join() { for attack in &attacked.messages { if attack.typ.is_hostile() { if let Some(attacker) = attack.attacker { if healths.contains(attacker) && attacker != entity { autofighter.target = Some(attacker); } } } } } for (target, health, attacked) in (&entities, &mut healths, &mut attackeds).join() { let target_name = visibles.get(target).map(|v| v.name.as_str()).unwrap_or("?").to_string(); let mut wounded = false; let mut attackers = Vec::new(); let mut attacker_names = Vec::new(); for attack in attacked.messages.drain(..) { let actor_name = ( if let Some(Some(n)) = attack.attacker.map(|ae| visibles.get(ae).map(|v| v.name.as_str())) {n} else {"?"} ).to_string(); match attack.typ { AttackType::Attack(strength) => { let damage = rand::thread_rng().gen_range(0, strength+1); health.health -= damage; if damage > 0 { wounded = true; if let Some(actor) = attack.attacker { say(&mut ears, actor, Notification::Health{actor: actor_name.clone(), target: target_name.clone(), amount: damage, typ: Attack}); attackers.push(actor); attacker_names.push(actor_name.clone()); } say(&mut ears, target, Notification::Health{actor: actor_name.clone(), target: target_name.clone(), amount: damage, typ: Damage}); } } AttackType::Heal(healthdiff) => { say(&mut ears, target, Notification::Health{actor: actor_name.clone(), target: target_name.clone(), amount: healthdiff, typ: Heal}); health.health += healthdiff; } } } health.health = util::clamp(health.health, 0, health.maxhealth); if health.health == 0 { TriggerBox::add_message(&mut triggerboxes, target, Trigger::Die); let killers = attacker_names.join(" and "); say(&mut ears, target, Notification::Die{actor: killers.clone(), target: target_name.clone()}); for actor in attackers { say(&mut ears, actor, Notification::Kill{actor: killers.clone(), target: target_name.clone()}); } } if let Some(position) = positions.get(target){ if wounded { new.create(position.pos, &Template::empty("wound")).unwrap(); } } } attackeds.clear(); } }