package misc; import rescuecore2.config.Config; import rescuecore2.messages.control.KSCommands; import rescuecore2.messages.control.KSUpdate; import rescuecore2.messages.Command; import rescuecore2.worldmodel.Entity; import rescuecore2.worldmodel.ChangeSet; import rescuecore2.worldmodel.EntityID; import rescuecore2.worldmodel.Property; import rescuecore2.misc.EntityTools; import rescuecore2.log.Logger; import rescuecore2.standard.components.StandardSimulator; import rescuecore2.standard.entities.StandardEntity; import rescuecore2.standard.entities.StandardEntityURN; import rescuecore2.standard.entities.StandardPropertyURN; import rescuecore2.standard.entities.Building; import rescuecore2.standard.entities.Refuge; import rescuecore2.standard.entities.Road; import rescuecore2.standard.entities.Human; import rescuecore2.standard.entities.Civilian; import rescuecore2.standard.entities.AmbulanceTeam; import rescuecore2.standard.entities.PoliceForce; import rescuecore2.standard.messages.AKClear; import rescuecore2.standard.messages.AKRescue; import rescuecore2.standard.messages.AKLoad; import rescuecore2.standard.messages.AKUnload; import org.uncommons.maths.random.GaussianGenerator; import java.util.Formatter; /** A simple misc simulator. This simulator handles buriedness, health, loading, unloading and road clearing. */ public class MiscSimulator extends StandardSimulator { private static final String[] CODES = {"wood", "steel", "concrete"}; private static final String PREFIX = "misc."; private static final String BURIEDNESS_SUFFIX = ".buriedness"; private static final String SLIGHT_SUFFIX = ".slight"; private static final String MODERATE_SUFFIX = ".moderate"; private static final String SEVERE_SUFFIX = ".severe"; private static final String DESTROYED_SUFFIX = ".destroyed"; private static final String DAMAGE_MEAN_KEY = "misc.damage.mean"; private static final String DAMAGE_SD_KEY = "misc.damage.sd"; private static final String DAMAGE_FIRE_KEY = "misc.damage.fire"; private static final String CLEAR_RATE_KEY = "misc.clear.rate"; private static final int SLIGHT = 25; private static final int MODERATE = 50; private static final int SEVERE = 75; private static final int DESTROYED = 100; private ChangeSet changes; private BuriednessStats[] stats; private int fire; private int clearRate; private GaussianGenerator gaussian; /** Create a MiscSimulator. */ public MiscSimulator() { changes = new ChangeSet(); } @Override public String getName() { return "Basic misc simulator"; } @Override protected void postConnect() { super.postConnect(); stats = new BuriednessStats[CODES.length]; for (int i = 0; i < CODES.length; ++i) { stats[i] = new BuriednessStats(i, config); } fire = config.getIntValue(DAMAGE_FIRE_KEY); clearRate = config.getIntValue(CLEAR_RATE_KEY); double mean = config.getFloatValue(DAMAGE_MEAN_KEY); double sd = config.getFloatValue(DAMAGE_SD_KEY); gaussian = new GaussianGenerator(mean, sd, config.getRandom()); } @Override protected void processCommands(KSCommands c, ChangeSet cs) { int time = c.getTime(); // Handle clear and rescue commands for (Command next : c.getCommands()) { if (next instanceof AKClear) { processClear((AKClear)next); } if (next instanceof AKRescue) { processRescue((AKRescue)next); } if (next instanceof AKLoad) { processLoad((AKLoad)next); } if (next instanceof AKUnload) { processUnload((AKUnload)next); } } updateHealth(); Logger.info("Time: " + time); writeInfo(); cs.merge(changes); } @Override protected void handleUpdate(KSUpdate u) { super.handleUpdate(u); changes = new ChangeSet(); // Update buriedness if buildings have collapsed for (EntityID id : u.getChangeSet().getChangedEntities()) { Entity next = model.getEntity(id); if (next instanceof Building) { Building b = (Building)next; Property brokenness = u.getChangeSet().getChangedProperty(id, StandardPropertyURN.BROKENNESS.toString()); if (brokenness != null) { // Brokenness has changed. Bury any agents inside. Logger.debug(b + " is broken. Updating trapped agents"); for (Entity e : model.getEntitiesOfType(StandardEntityURN.CIVILIAN, StandardEntityURN.FIRE_BRIGADE, StandardEntityURN.POLICE_FORCE, StandardEntityURN.AMBULANCE_TEAM)) { Human h = (Human)e; if (h.isPositionDefined() && h.getPosition().equals(b.getID())) { Logger.debug("Human in building: " + h); int buriedness = h.isBuriednessDefined() ? h.getBuriedness() : 0; int increase = calculateBuriedness(h, b); buriedness += increase; h.setBuriedness(buriedness); changes.addChange(h, h.getBuriednessProperty()); Logger.debug("Changed buriedness: increase by " + increase + " to " + buriedness); } } } } } } private void processClear(AKClear clear) { StandardEntity agent = model.getEntity(clear.getAgentID()); StandardEntity target = model.getEntity(clear.getTarget()); if (agent == null) { Logger.warn("Rejecting clear command " + clear + ": agent does not exist"); return; } if (target == null) { Logger.warn("Rejecting clear command " + clear + ": target does not exist"); return; } if (!(agent instanceof PoliceForce)) { Logger.warn("Rejecting clear command " + clear + ": agent is not a police officer"); return; } if (!(target instanceof Road)) { Logger.warn("Rejecting clear command " + clear + ": target is not a road"); return; } PoliceForce police = (PoliceForce)agent; StandardEntity agentPosition = police.getPosition(model); if (agentPosition == null) { Logger.warn("Rejecting clear command " + clear + ": could not locate agent"); return; } if (!police.isHPDefined() || police.getHP() <= 0) { Logger.warn("Rejecting clear command " + clear + ": agent is dead"); return; } if (police.isBuriednessDefined() && police.getBuriedness() > 0) { Logger.warn("Rejecting clear command " + clear + ": agent is buried"); return; } Road targetRoad = (Road)target; if (!targetRoad.isBlockDefined() || targetRoad.getBlock() <= 0) { Logger.warn("Rejecting clear command " + clear + ": road is not blocked"); return; } EntityID agentPositionID = police.getPosition(); if (agentPositionID == null || !agentPositionID.equals(target.getID()) && !agentPositionID.equals(targetRoad.getHead()) && !agentPositionID.equals(targetRoad.getTail())) { Logger.warn("Rejecting clear command " + clear + ": agent is not adjacent to target road"); return; } // All checks passed int block = targetRoad.getBlock(); targetRoad.setBlock(Math.max(0, block - clearRate)); changes.addChange(targetRoad, targetRoad.getBlockProperty()); Logger.debug("Clear: " + clear); Logger.debug("Reduced road block from " + block + " to: " + targetRoad.getBlock()); } private void processRescue(AKRescue rescue) { StandardEntity agent = model.getEntity(rescue.getAgentID()); StandardEntity target = model.getEntity(rescue.getTarget()); if (agent == null) { Logger.warn("Rejecting rescue command " + rescue + ": agent does not exist"); return; } if (target == null) { Logger.warn("Rejecting rescue command " + rescue + ": target does not exist"); return; } if (!(agent instanceof AmbulanceTeam)) { Logger.warn("Rejecting rescue command " + rescue + ": agent is not an ambulance"); return; } if (!(target instanceof Human)) { Logger.warn("Rejecting rescue command " + rescue + ": target is not a human"); return; } AmbulanceTeam ambulance = (AmbulanceTeam)agent; Human targetHuman = (Human)target; StandardEntity agentPosition = ambulance.getPosition(model); StandardEntity targetPosition = targetHuman.getPosition(model); if (agentPosition == null) { Logger.warn("Rejecting rescue command " + rescue + ": could not locate agent"); return; } if (targetPosition == null) { Logger.warn("Rejecting rescue command " + rescue + ": could not locate target"); return; } if (!(targetPosition instanceof Building)) { Logger.warn("Rejecting rescue command " + rescue + ": target is not in a building"); return; } if (!ambulance.isHPDefined() || ambulance.getHP() <= 0) { Logger.warn("Rejecting rescue command " + rescue + ": agent is dead"); return; } if (ambulance.isBuriednessDefined() && ambulance.getBuriedness() > 0) { Logger.warn("Rejecting rescue command " + rescue + ": agent is buried"); return; } if (!targetHuman.isBuriednessDefined() || targetHuman.getBuriedness() <= 0) { Logger.warn("Rejecting rescue command " + rescue + ": target is not buried"); return; } if (!agentPosition.equals(targetPosition)) { Logger.warn("Rejecting rescue command " + rescue + ": agent is at a different location to the target"); return; } // All checks passed int buriedness = targetHuman.getBuriedness(); targetHuman.setBuriedness(Math.max(0, buriedness - 1)); changes.addChange(targetHuman, targetHuman.getBuriednessProperty()); Logger.debug("Rescue: " + rescue); Logger.debug("Reduced buriedness from " + buriedness + " to: " + targetHuman.getBuriedness()); } private void processLoad(AKLoad load) { StandardEntity agent = model.getEntity(load.getAgentID()); StandardEntity target = model.getEntity(load.getTarget()); if (agent == null) { Logger.warn("Rejecting load command " + load + ": agent does not exist"); return; } if (target == null) { Logger.warn("Rejecting load command " + load + ": target does not exist"); return; } if (!(agent instanceof AmbulanceTeam)) { Logger.warn("Rejecting load command " + load + ": agent is not an ambulance"); return; } if (!(target instanceof Civilian)) { Logger.warn("Rejecting load command " + load + ": target is not a civilian"); return; } AmbulanceTeam ambulance = (AmbulanceTeam)agent; Civilian targetCivilian = (Civilian)target; StandardEntity agentPosition = ambulance.getPosition(model); StandardEntity targetPosition = targetCivilian.getPosition(model); EntityID agentID = agent.getID(); if (agentPosition == null) { Logger.warn("Rejecting load command " + load + ": could not locate agent"); return; } if (targetPosition == null) { Logger.warn("Rejecting load command " + load + ": could not locate target"); return; } if (!ambulance.isHPDefined() || ambulance.getHP() <= 0) { Logger.warn("Rejecting load command " + load + ": agent is dead"); return; } if (ambulance.isBuriednessDefined() && ambulance.getBuriedness() > 0) { Logger.warn("Rejecting load command " + load + ": agent is buried"); return; } if (targetCivilian.isBuriednessDefined() && targetCivilian.getBuriedness() > 0) { Logger.warn("Rejecting load command " + load + ": target is buried"); return; } if (!agentPosition.equals(targetPosition)) { Logger.warn("Rejecting load command " + load + ": agent is at a different location to the target"); return; } // Is there something already loaded? for (Entity e : model.getEntitiesOfType(StandardEntityURN.CIVILIAN)) { Civilian c = (Civilian)e; if (c.isPositionDefined() && agentID.equals(c.getPosition())) { Logger.warn("Rejecting load command " + load + ": agent already has something loaded"); return; } } // All checks passed targetCivilian.setPosition(agentID); changes.addChange(targetCivilian, targetCivilian.getPositionProperty()); Logger.debug("Load: " + load); Logger.debug("Ambulance " + agentID + " loaded civilian " + targetCivilian.getID()); } private void processUnload(AKUnload unload) { StandardEntity agent = model.getEntity(unload.getAgentID()); if (agent == null) { Logger.warn("Rejecting unload command " + unload + ": agent does not exist"); return; } if (!(agent instanceof AmbulanceTeam)) { Logger.warn("Rejecting unload command " + unload + ": agent is not an ambulance"); return; } EntityID agentID = agent.getID(); AmbulanceTeam ambulance = (AmbulanceTeam)agent; StandardEntity agentPosition = ambulance.getPosition(model); if (agentPosition == null) { Logger.warn("Rejecting unload command " + unload + ": could not locate agent"); return; } if (!ambulance.isHPDefined() && ambulance.getHP() <= 0) { Logger.warn("Rejecting unload command " + unload + ": agent is dead"); return; } if (ambulance.isBuriednessDefined() && ambulance.getBuriedness() > 0) { Logger.warn("Rejecting unload command " + unload + ": agent is buried"); return; } // Is there something loaded? Civilian target = null; for (Entity e : model.getEntitiesOfType(StandardEntityURN.CIVILIAN)) { Civilian c = (Civilian)e; if (c.isPositionDefined() && agentID.equals(c.getPosition())) { target = c; break; } } if (target == null) { Logger.warn("Rejecting unload command " + unload + ": agent is not carrying any civilians"); return; } // All checks passed target.setPosition(ambulance.getPosition()); changes.addChange(target, target.getPositionProperty()); Logger.debug("Unload: " + unload); Logger.debug("Ambulance " + agentID + " unloaded " + target.getID() + " at " + ambulance.getPosition()); } private void updateHealth() { for (Entity e : model.getEntitiesOfType(StandardEntityURN.CIVILIAN, StandardEntityURN.FIRE_BRIGADE, StandardEntityURN.POLICE_FORCE, StandardEntityURN.AMBULANCE_TEAM)) { Human h = (Human)e; int buriedness = h.isBuriednessDefined() ? h.getBuriedness() : 0; int damage = h.isDamageDefined() ? h.getDamage() : 0; int hp = h.isHPDefined() ? h.getHP() : 0; StandardEntity position = h.getPosition(model); if (position instanceof Refuge) { if (damage > 0) { h.setDamage(0); changes.addChange(h, h.getDamageProperty()); } continue; } boolean onFire = (position instanceof Building) && ((Building)position).isOnFire(); // Increase damage if the agent is buried if (buriedness > 0) { damage += Math.max(0, (int)(gaussian.nextValue() * buriedness)); } if (onFire) { damage += fire; } // Update HP hp = Math.max(0, hp - damage); // Update entity h.setDamage(damage); h.setHP(hp); changes.addChange(h, h.getDamageProperty()); changes.addChange(h, h.getHPProperty()); } } private int calculateBuriedness(Human h, Building b) { if (!b.isBuildingCodeDefined()) { return 0; } int code = b.getBuildingCode(); return stats[code].computeBuriedness(b); } private void writeInfo() { StringBuilder builder = new StringBuilder(); Formatter format = new Formatter(builder); format.format("| Civ ID | HP | Damage | Buriedness |%n"); format.format("--------------------------------------------%n"); for (Entity e : EntityTools.sortedList(model.getEntitiesOfType(StandardEntityURN.CIVILIAN, StandardEntityURN.FIRE_BRIGADE, StandardEntityURN.POLICE_FORCE, StandardEntityURN.AMBULANCE_TEAM))) { Human h = (Human)e; int hp = h.isHPDefined() ? h.getHP() : 0; int damage = h.isDamageDefined() ? h.getDamage() : 0; int buriedness = h.isBuriednessDefined() ? h.getBuriedness() : 0; if (hp > 0 && (damage > 0 || buriedness > 0)) { format.format("| %1$9d | %2$6d | %3$6d | %4$10d |%n", h.getID().getValue(), hp, damage, buriedness); } } format.format("--------------------------------------------%n"); format.format("| Road ID | Block |%n"); format.format("----------------------%n"); for (Entity e : EntityTools.sortedList(model.getEntitiesOfType(StandardEntityURN.ROAD))) { Road r = (Road)e; int block = r.isBlockDefined() ? r.getBlock() : 0; if (block > 0) { format.format("| %1$9d | %2$6d |%n", r.getID().getValue(), block); } } format.format("---------------------%n"); Logger.info(builder.toString()); } private static class BuriednessStats { private int destroyed; private int severe; private int moderate; private int slight; BuriednessStats(int code, Config config) { destroyed = config.getIntValue(PREFIX + CODES[code] + BURIEDNESS_SUFFIX + DESTROYED_SUFFIX); severe = config.getIntValue(PREFIX + CODES[code] + BURIEDNESS_SUFFIX + SEVERE_SUFFIX); moderate = config.getIntValue(PREFIX + CODES[code] + BURIEDNESS_SUFFIX + MODERATE_SUFFIX); slight = config.getIntValue(PREFIX + CODES[code] + BURIEDNESS_SUFFIX + SLIGHT_SUFFIX); } int computeBuriedness(Building b) { if (!b.isBrokennessDefined()) { return 0; } int damage = b.getBrokenness(); if (damage < SLIGHT) { return 0; } if (damage < MODERATE) { return slight; } if (damage < SEVERE) { return moderate; } if (damage < DESTROYED) { return severe; } return destroyed; } } }