@Override
 public String getShortDescription() {
   String hf = World.getHistoricalFigure(getHfId()).getLink();
   String loc = location.getLink("in");
   if (oldJob.equals("standard")) return hf + " became a " + newJob + loc;
   else if (newJob.equals("standard")) return hf + " stopped being a " + oldJob + loc;
   else return hf + " gave up being a " + oldJob + " to become a " + newJob + loc;
 }
 @Override
 public String getLink() {
   String site = World.getSite(siteId).getLink();
   return "the <a href=\""
       + getUrl()
       + "\" class=\"collection purge\">"
       + getOrdinalString()
       + adjective
       + " purge</a> in "
       + site;
 }
  @RequestMapping("/hf/{id}")
  public Template hf(VelocityContext context, int id) {
    HistoricalFigure hf = World.getHistoricalFigure(id);
    HistoricalFigure.setContext(hf);

    context.put("hf", hf);
    context.put("family", new Family(hf, false));

    if (hf.isWerebeast()) {
      context.put("curse", new Family(hf, true));
    }

    context.put(
        "events",
        World.getHistoricalEvents()
            .stream()
            .filter(e -> EventHelper.related(hf, e))
            .collect(Collectors.toList()));

    return Templates.get("hf.vm");
  }
    private void getCurseRoot() {

      HistoricalFigure parent;
      do {
        parent =
            World.getHistoricalEvents()
                .stream()
                .collect(
                    Filters.filterEvent(
                        HfDoesInteractionEvent.class,
                        e -> e.getTargetHfId() == getRoot().hf.getId()))
                .map(HfDoesInteractionEvent::getDoerHfId)
                .map(World::getHistoricalFigure)
                .findFirst()
                .orElseGet(() -> null);
        if (parent != null) {
          root = new FamilyMember(parent, 0, 0);
        }
      } while (parent != null);
    }
    private void analyzeBites(FamilyMember m) {
      if (members.contains(m)) return;
      members.add(m);

      World.getHistoricalEvents()
          .stream()
          .collect(
              Filters.filterEvent(
                  HfDoesInteractionEvent.class,
                  e ->
                      e.getDoerHfId() == m.hf.getId()
                          && e.getInteraction().startsWith(interaction)))
          .map(HfDoesInteractionEvent::getTargetHfId)
          .map(World::getHistoricalFigure)
          .forEach(
              hf -> {
                FamilyMember m2 = new FamilyMember(hf, m.getGeneration() + 1, m.getDistance() + 1);
                m2.father = m;
                m.children.add(m2);
                links.add(new FamilyLink("child", m, m2));
                analyzeBites(m2);
              });
    }
 public String getName() {
   String site = World.getSite(siteId).getLink();
   return "The " + getOrdinalString() + adjective + " Purge in " + site;
 }
 @Override
 public String getShortDescription() {
   String site = World.getSite(siteId).getLink();
   return "the " + getOrdinalString() + adjective + " Purge in " + site + " occurred";
 }
  @RequestMapping("/hfs")
  public Template hfs(VelocityContext context) {
    boolean leader = context.containsKey("leader");
    boolean deity = context.containsKey("deity");
    boolean force = context.containsKey("force");
    boolean vampire = context.containsKey("vampire");
    boolean werebeast = context.containsKey("werebeast");
    boolean necromancer = context.containsKey("necromancer");
    boolean alive = context.containsKey("alive");
    boolean ghost = context.containsKey("ghost");
    boolean adventurer = context.containsKey("adventurer");

    Collection<HistoricalFigure> historicalFigures = World.getHistoricalFigures();
    context.put(
        "races",
        new TreeSet<String>(
            historicalFigures
                .stream()
                .map(hf -> hf.getRace() != null ? hf.getRace() : "UNKNOWN")
                .collect(Collectors.toList())));

    String race = (String) context.get("race");

    if (leader
        || deity
        || force
        || vampire
        || werebeast
        || necromancer
        || alive
        || ghost
        || adventurer
        || (race != null && !race.equals(""))) {
      context.put(
          "elements",
          historicalFigures
              .stream()
              .filter(
                  hf -> {
                    if (leader && !hf.isLeader()) return false;
                    if (deity && !hf.isDeity()) return false;
                    if (force && !hf.isForce()) return false;
                    if (vampire && !hf.isVampire()) return false;
                    if (werebeast && !hf.isWerebeast()) return false;
                    if (necromancer && !hf.isNecromancer()) return false;
                    if (alive && hf.getDeathYear() != -1) return false;
                    if (ghost && !hf.isGhost()) return false;
                    if (adventurer && !hf.isAdventurer()) return false;

                    if (race != null && !race.equals("")) {
                      if (hf.getRace() == null) {
                        if (!race.equals("UNKNOWN")) {
                          return false;
                        }
                      } else if (!hf.getRace().equals(race)) {
                        return false;
                      }
                    }
                    return true;
                  })
              .collect(Collectors.toList()));
    } else {
      context.put("elements", historicalFigures);
    }

    return Templates.get("hfs.vm");
  }
  @Override
  public String getShortDescription() {
    String hf = World.getHistoricalFigure(hfId).getLink();
    String slayer = "";
    if (slayerHfId != -1) slayer = " by " + World.getHistoricalFigure(slayerHfId).getLink();
    else if (slayerRace != null && !slayerRace.equals("-1")) slayer = " by a " + slayerRace;
    String loc = location.getLink("in");

    if (artifactId != -1) {
      slayer += " with " + World.getArtifact(artifactId).getLink();
    } else {
      slayer += item.getText("with a");
      slayer += shooterItem.getText("from a");
    }
    switch (cause) {
      case "old age":
      case "old_age":
        return hf + " died of old age" + loc;
      case "melt":
        return hf + " melted" + loc;
      case "drown":
        return hf + " drowned" + loc;
      case "freezing water":
      case "encase_ice":
        return hf + " was encased in ice" + loc;
      case "struck":
      case "struck_down":
        return hf + " was struck down" + slayer + loc;
      case "murder":
      case "murdered":
        return hf + " was murdered" + slayer + loc;
      case "shot":
        return hf + " was shot and killed" + slayer + loc;
      case "behead":
      case "exec beheaded":
        return hf + " was beheaded" + slayer + loc;
      case "drown_alt":
      case "exec drowned":
        return hf + " was drowned" + slayer + loc;
      case "hack_to_pieces":
      case "exec hacked to pieces":
        return hf + " was hacked to pieces" + slayer + loc;
      case "bury_alive":
      case "exec buried alive":
        return hf + " was buried alive" + slayer + loc;
      case "burn_alive":
      case "exec burned alive":
        return hf + " was burned alive" + slayer + loc;
      case "feed_to_beasts":
      case "exec fed to beasts":
        return hf + " was fed to beasts" + slayer + loc;
      case "crucify":
      case "exec crucified":
        return hf + " was crucified" + slayer + loc;
      case "air":
      case "suffocate":
        return hf + " suffocated, slain by " + slayer + loc;
      case "blood":
      case "bleed":
        return hf + " bled to death, slain by " + slayer + loc;
      case "obstacle":
      case "collision":
        return hf + " died after colliding with an obstacle, slain by " + slayer + loc;
      case "drawbridge":
      case "crushed bridge":
        return hf + " was crushed by a drawbridge" + loc;
      case "drain_blood":
      case "blood drained":
        return hf + " was drained of blood by " + slayer + loc;
      case "slaughter":
      case "slaughtered":
        return hf + " was slaughtered by " + slayer + loc;
      case "thirst":
        return hf + " died of thirst" + loc;
      case "scuttle":
      case "scuttled":
        return hf + " was scuttled" + loc;
      case "memorialize":
      case "put to rest":
        return hf + " was put to rest" + loc;
      default:
        return hf + " died: " + cause + slayer + loc;
    }
  }