package misc; import java.util.Map; import java.util.Set; import java.util.HashMap; import java.util.HashSet; import javax.swing.JComponent; import rescuecore2.messages.Command; import rescuecore2.messages.control.KSCommands; import rescuecore2.worldmodel.Entity; import rescuecore2.worldmodel.EntityID; import rescuecore2.worldmodel.EntityListener; import rescuecore2.worldmodel.Property; import rescuecore2.worldmodel.ChangeSet; import rescuecore2.log.Logger; import rescuecore2.standard.entities.Building; import rescuecore2.standard.entities.Refuge; import rescuecore2.standard.entities.Human; import rescuecore2.standard.entities.AmbulanceTeam; import rescuecore2.standard.entities.StandardPropertyURN; import rescuecore2.standard.messages.AKRescue; import rescuecore2.standard.components.StandardSimulator; import rescuecore2.GUIComponent; import java.util.Formatter; /** * Implementation of the legacy misc simulator. * @author Maitreyi Nanjanath * @author Cameron Skinner */ public class MiscSimulator extends StandardSimulator implements GUIComponent { private Map<EntityID, HumanAttributes> humans; private Set<EntityID> newlyBrokenBuildings; private MiscParameters parameters; private MiscSimulatorGUI gui; @Override public JComponent getGUIComponent() { if (gui == null) { gui = new MiscSimulatorGUI(); } return gui; } @Override public String getGUIComponentName() { return "Misc simulator"; } @Override protected void postConnect() { super.postConnect(); parameters = new MiscParameters(config); humans = new HashMap<EntityID, HumanAttributes>(); newlyBrokenBuildings = new HashSet<EntityID>(); Logger.info("MiscSimulator connected. World has " + model.getAllEntities().size() + " entities."); BuildingChangeListener buildingListener = new BuildingChangeListener(); //HumanChangeListener humanListener = new HumanChangeListener(); for (Entity et : model.getAllEntities()) { if (et instanceof Building) { et.addEntityListener(buildingListener); } else if (et instanceof Human) { //et.addEntityListener(humanListener); Human human = (Human)et; HumanAttributes ha = new HumanAttributes(human, config); humans.put(ha.getID(), ha); } } } @Override protected void processCommands(KSCommands c, ChangeSet changes) { long start = System.currentTimeMillis(); int time = c.getTime(); Logger.info("Timestep " + time); for (Command com : c.getCommands()) { if (checkValidity(com)) { if (com instanceof AKRescue) { Human human = (Human)(model.getEntity(((AKRescue)com).getTarget())); handleRescue(human, changes); } } else { Logger.debug("Ignoring " + com); } } processBrokenBuildings(changes); processBurningBuildings(changes); updateDamage(changes); // Clean up newlyBrokenBuildings.clear(); writeDebugOutput(c.getTime()); if (gui != null) { gui.refresh(humans.values()); } long end = System.currentTimeMillis(); Logger.info("Timestep " + time + " took " + (end - start) + " ms"); } private void processBrokenBuildings(ChangeSet changes) { for (HumanAttributes hA : humans.values()) { Human human = hA.getHuman(); EntityID positionID = human.getPosition(); if (!newlyBrokenBuildings.contains(positionID)) { continue; } // Human is in a newly collapsed building // Check for buriedness Logger.trace("Checking if human should be buried in broken building"); Building b = (Building)human.getPosition(model); if (parameters.shouldBuryAgent(b)) { int buriedness = parameters.getBuriedness(b); if (buriedness != 0) { int oldBuriedness = human.isBuriednessDefined() ? human.getBuriedness() : 0; human.setBuriedness(Math.max(oldBuriedness, buriedness)); changes.addChange(human, human.getBuriednessProperty()); // Check for injury from being buried int damage = parameters.getBuryDamage(b, human); if (damage != 0) { hA.addBuriednessDamage(damage); } } } // Now check for injury from the collapse int damage = parameters.getCollapseDamage(b, human); if (damage != 0) { hA.addCollapseDamage(damage); } } } private void processBurningBuildings(ChangeSet changes) { for (HumanAttributes hA : humans.values()) { Human human = hA.getHuman(); EntityID positionID = human.getPosition(); Entity position = human.getPosition(model); if (position instanceof Building && ((Building)position).isOnFire()) { // Human is in a burning building int damage = parameters.getFireDamage((Building)position, human); if (damage != 0) { hA.addFireDamage(damage); } } } } private void writeDebugOutput(int time) { StringBuilder builder = new StringBuilder(); Formatter format = new Formatter(builder); format.format("Agents damaged or buried at timestep %1d%n", time); format.format(" ID | HP | Damage | Bury | Collapse | Fire | Buriedness%n"); for (HumanAttributes ha : humans.values()) { Human h = ha.getHuman(); int hp = h.isHPDefined() ? h.getHP() : 0; int damage = ha.getTotalDamage(); int buriedness = h.isBuriednessDefined() ? h.getBuriedness() : 0; boolean isAlive = hp > 0; boolean hasDamage = damage > 0; boolean isBuried = buriedness > 0; if ((hasDamage || isBuried) && isAlive) { format.format("%1$9d | %2$6d | %3$6d | %4$8.3f | %5$8.3f | %6$8.3f | %7$6d%n", ha.getID().getValue(), hp, damage, ha.getBuriednessDamage(), ha.getCollapseDamage(), ha.getFireDamage(), buriedness); } } Logger.debug(builder.toString()); } private void updateDamage(ChangeSet changes) { for (HumanAttributes ha : humans.values()) { updateDamage(ha); Human h = ha.getHuman(); int hp = h.isHPDefined() ? h.getHP() : 0; int damage = ha.getTotalDamage(); h.setDamage(damage); changes.addChange(ha.getHuman(), ha.getHuman().getDamageProperty()); // Update HP boolean isAlive = hp > 0; boolean hasDamage = damage > 0; if (isAlive && hasDamage) { int newHP = Math.max(0, hp - damage); h.setHP(newHP); changes.addChange(ha.getHuman(), ha.getHuman().getHPProperty()); } // Treat damage if in a refuge if (h.getPosition(model) instanceof Refuge) { ha.clearDamage(); h.setDamage(0); changes.addChange(ha.getHuman(), ha.getHuman().getDamageProperty()); } } } private void updateDamage(HumanAttributes ha) { Human h = ha.getHuman(); if (h.getHP() <= 0) { return; // Agent is already dead. } ha.progressDamage(); } private boolean checkValidity(Command command) { Entity e = model.getEntity(command.getAgentID()); if (e == null) { Logger.warn("Received a " + command.getURN() + " command from an unknown agent: " + command.getAgentID()); return false; } if (command instanceof AKRescue) { return checkRescue((AKRescue)command, e); } return false; } private boolean checkRescue(AKRescue rescue, Entity agent) { EntityID targetID = rescue.getTarget(); Entity target = model.getEntity(targetID); if (!(agent instanceof AmbulanceTeam)) { Logger.warn("Rejecting rescue command from agent " + agent.getID() + " who is of type " + agent.getURN()); return false; } if (target == null) { Logger.warn("Rejecting rescue command from agent " + agent.getID() + " for a non-existant target " + targetID); return false; } if (!(target instanceof Human)) { Logger.warn("Rejecting rescue command from agent " + agent.getID() + " for a non-human target: " + targetID + " is of type " + target.getURN()); return false; } Human h = (Human)target; AmbulanceTeam at = (AmbulanceTeam)agent; if (at.isHPDefined() && at.getHP() <= 0) { Logger.warn("Rejecting rescue command from agent " + agent.getID() + ": agent is dead"); return false; } if (at.isBuriednessDefined() && at.getBuriedness() > 0) { Logger.warn("Rejecting rescue command from agent " + agent.getID() + ": agent is buried"); return false; } if (!h.isBuriednessDefined() || h.getBuriedness() == 0) { Logger.warn("Rejecting rescue command from agent " + agent.getID() + " for a non-buried target " + targetID); return false; } if (!h.isPositionDefined() || !at.isPositionDefined() || !h.getPosition().equals(at.getPosition())) { Logger.warn("Rejecting rescue command from agent " + agent.getID() + " for a non-adjacent target " + targetID); return false; } if (h.getID().equals(at.getID())) { Logger.warn("Rejecting rescue command from agent " + agent.getID() + ": tried to rescue self"); return false; } return true; } private void handleRescue(Human target, ChangeSet changes) { target.setBuriedness(Math.max(0, target.getBuriedness() - 1)); changes.addChange(target, target.getBuriednessProperty()); } private class BuildingChangeListener implements EntityListener { @Override public void propertyChanged(Entity e, Property p, Object oldValue, Object newValue) { if (!(e instanceof Building)) { return; //we want to only look at buildings } if (!p.getURN().equals(StandardPropertyURN.BROKENNESS.toString())) { // Only care about brokenness changes return; } double old = oldValue == null ? 0 : (Integer)oldValue; double next = newValue == null ? 0 : (Integer)newValue; if (next > old) { newlyBrokenBuildings.add(e.getID()); } } } }