/**
   * Defines the characteristics of a <i>TranslateTransition</i>. Each call results in ONE segment
   * of motion. When that segment is finished, it "chains" another call to <i>startMotion()</i>
   * (which is NOT recursion)! The initial call is made by the managing <i>Army</i> object;
   * subsequent calls are made through the "chaining" process described here.
   *
   * @param engageInCombat TODO
   */
  public void startMotion(boolean engageInCombat) {
    Army opposingArmy = armyAllegiance.getOpposingArmy();
    Actor opponent =
        opposingArmy.findNearestOpponent(
            this); // could legitimately return a null: 1) no one is visible 2) no Actors in
                   // opposing army

    Point2D newLocation;
    if (opponent != null) {
      System.out.printf(
          "ToMove:[%.1f:%.1f] Opponent:[%.1f:%.1f]\n",
          getAvatar().getTranslateX(),
          getAvatar().getTranslateY(),
          opponent.getAvatar().getTranslateX(),
          opponent.getAvatar().getTranslateX());
      double DISTANCE_FOR_BATTLE = 50.0;
      if (engageInCombat && distanceTo(opponent) < DISTANCE_FOR_BATTLE) {
        double h1, h2, h3, h4; // debug code
        h1 = this.getHealth();
        h2 = opponent.getHealth();

        combatRound(opponent);
        h3 = this.getHealth();
        h4 = opponent.getHealth();
        h4 = h4;
        if (this.getHealth() <= 0.0) {
          armyAllegiance.removeNowDeadActor(this);
        }
        if (opponent.getHealth() <= 0.0) {
          opponent.armyAllegiance.removeNowDeadActor(opponent);
        }
      } // end if (combat)
      newLocation = findNewLocation(opponent);
    } else // end if (test for null opponent)
    newLocation = meander(); // null opponent means we wander around close to our current location

    if (tt.getStatus()
        != Animation.Status.RUNNING) { // if NOT yet RUNNING, start . . . otherwise, do nothing.
      // tt.setToX(Math.random()*getAvatar().getScene().getWidth());
      // tt.setToY(Math.random()*getAvatar().getScene().getHeight());
      tt.setToX(validateCoordinate(newLocation).getX());
      tt.setToY(validateCoordinate(newLocation).getY());
      tt.setDuration(
          Duration.seconds(MAX_SPEED / (getSpeed() * (armyAllegiance.getSpeedControllerValue()))));
      tt.setOnFinished(event -> startMotion(true)); // NOT RECURSION!!!!
      tt
          .play(); // give assembled object to the render engine (of course, play() is an
                   // object-oriented method which has access to "this" inside, and it can use
                   // "this" to give to the render engine.
    }
  } // end startMotion()
 private double distanceTo(Actor opponent) {
   double actorToMoveX = opponent.getAvatar().getTranslateX();
   double actorToMoveY = opponent.getAvatar().getTranslateY();
   double currentX = getAvatar().getTranslateX();
   double currentY = getAvatar().getTranslateY();
   double deltaX = actorToMoveX - currentX;
   double deltaY = actorToMoveY - currentY;
   double calculatedDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
   return calculatedDistance;
 }
  /**
   * processes a single round of combat between two Actor objects: the <b>attacker</b> is this
   * object; the <b>defender</b> is received as an argument. This method is called by the
   * <b>attacker</b> <i>Actor</i> object. This <b>attacker</b> <i>Actor</i> object chooses another
   * <i>Actor</i> object as the <b>defender</b> by sending a reference-to the second <i>Actor</i>
   * object. When program execution arrives in <i>combatRound</i>, the method will have access to 2
   * sets of <i>Actor</i> attributes (a.k.a. instance fields). In particular, this method will need
   * to use <i>health</i> and <i>strength</i> to process a single round of combat. As an outcome of
   * the single round, both <i>Actor</i> objects: the <b>attacker</b> and the <b>defender</b> are
   * likely to loose some <i>health</i> value, but the <i>Actor</i> object with less <i>strength</i>
   * will likely incur more damage to their <i>health</i>. You access the <b>attacker</b> instance
   * fields (such as <i>health</i> using <i>this.health</i> and the <b>defender</b> instance fields
   * using <i>defender.health</i>. Of course, <i>defender</i> is the name of the stack-oriented
   * reference-to variable that is sent to the method.
   *
   * @param defender a reference to a different <i>Actor</i> object that will engage in combat with
   *     this <i>Actor</i> object.
   * @return <i>health</i> of the <i>Actor</i> following the combat round.
   */
  public double combatRound(Actor defender) {
    final double MAX_COMBAT_HEALTH_REDUCTION_OF_LOOSER =
        10.0; // health ranges 0.0 to 100.0, thus could loose 0.0 to 10.0
    final double MAX_COMBAT_HEALTH_REDUCTION_OF_WINNER = 3.0; // could loose 0.0 to 3.0
    double healthAdjustmentOfLooser =
        -(Math.random() * MAX_COMBAT_HEALTH_REDUCTION_OF_LOOSER)
            - 1.0; // looser looses at least 1.0
    double healthAdjustmentOfWinner =
        -(Math.random() * MAX_COMBAT_HEALTH_REDUCTION_OF_WINNER) + 1.0; // winner gains at least 1.0

    double proportionHitPoints =
        getHitPoints() / (getHitPoints() + defender.getHitPoints()); // between 0.0 and 1.0
    if (Math.random() > proportionHitPoints) {
      adjustHealth(healthAdjustmentOfLooser);
      defender.adjustHealth(healthAdjustmentOfWinner);
    } else {
      defender.adjustHealth(healthAdjustmentOfLooser);
      adjustHealth(healthAdjustmentOfWinner);
    }
    return getHealth();
  } // end combatRound()
  /**
   * createTable is static to allow Army to define a table without having any Actor objects present.
   */
  public static TableView<Actor> createTable() {
    TableView<Actor> table = new TableView<Actor>();
    final double PREF_WIDTH_DOUBLE = 80.0;
    table.setPrefWidth(
        PREF_WIDTH_DOUBLE
            * 7.5); // 7.0 because there are 6 individual columns, but one of those is DOUBLE-WIDTH,
                    // and there is some inter-column spacing
    table.setEditable(true);

    TableColumn<Actor, String> nameCol = new TableColumn<>("Name");
    nameCol.setCellValueFactory(new PropertyValueFactory<Actor, String>("name"));
    nameCol.setPrefWidth(PREF_WIDTH_DOUBLE * 2.0);
    TableColumn<Actor, Number> healthCol = new TableColumn<>("Health");
    healthCol.setCellValueFactory(cell -> cell.getValue().health);
    healthCol.setPrefWidth(PREF_WIDTH_DOUBLE);
    TableColumn<Actor, Number> strengthCol = new TableColumn<>("Strength");
    strengthCol.setCellValueFactory(cell -> cell.getValue().strength);
    strengthCol.setPrefWidth(PREF_WIDTH_DOUBLE);
    TableColumn<Actor, Number> speedCol = new TableColumn<>("Speed");
    speedCol.setCellValueFactory(cell -> cell.getValue().speed);
    speedCol.setPrefWidth(PREF_WIDTH_DOUBLE);
    TableColumn<Actor, Number> locationXCol = new TableColumn<>("X");
    locationXCol.setCellValueFactory(cell -> cell.getValue().getAvatar().translateXProperty());
    locationXCol.setPrefWidth(PREF_WIDTH_DOUBLE);
    TableColumn<Actor, Number> locationYCol = new TableColumn<>("Y");
    locationYCol.setCellValueFactory(cell -> cell.getValue().getAvatar().translateYProperty());
    locationYCol.setPrefWidth(PREF_WIDTH_DOUBLE);
    ObservableList<TableColumn<Actor, ?>> c = table.getColumns();
    c.add(nameCol);
    c.add(healthCol);
    c.add(strengthCol);
    c.add(speedCol);
    c.add(locationXCol);
    c.add(locationYCol);
    // Compare line ABOVE with line BELOW: The BELOW line looks cleaner and does actually work . . .
    // but the compiler spits out a warning. The ABOVE line accomplishes the same thing, less
    // elegantly, but without warnings.
    // table.getColumns().addAll(nameCol, healthCol, strengthCol, speedCol, locationXCol,
    // locationYCol);

    // The following code makes each cell in the selected columns editable (Name, Health, Strength,
    // Speed)
    // We CANNOT implement edit capabilities on the X/Y columns since they are READ-ONLY.
    nameCol.setCellFactory(TextFieldTableCell.<Actor>forTableColumn());
    nameCol.setOnEditCommit(
        event -> {
          Actor a = (event.getTableView().getItems().get(event.getTablePosition().getRow()));
          a.setName(event.getNewValue());
          a.resetAvatarAttributes();
        });

    healthCol.setCellFactory(
        TextFieldTableCell.<Actor, Number>forTableColumn(new NumberStringConverter()));
    healthCol.setOnEditCommit(
        event -> {
          Actor a = (event.getTableView().getItems().get(event.getTablePosition().getRow()));
          a.setHealth((Double) event.getNewValue());
          a.resetAvatarAttributes();
        });

    strengthCol.setCellFactory(
        TextFieldTableCell.<Actor, Number>forTableColumn(new NumberStringConverter()));
    strengthCol.setOnEditCommit(
        event -> {
          Actor a = (event.getTableView().getItems().get(event.getTablePosition().getRow()));
          a.setStrength((Double) event.getNewValue());
          a.resetAvatarAttributes();
        });

    speedCol.setCellFactory(
        TextFieldTableCell.<Actor, Number>forTableColumn(new NumberStringConverter()));
    speedCol.setOnEditCommit(
        event -> {
          Actor a = (event.getTableView().getItems().get(event.getTablePosition().getRow()));
          a.setSpeed((Double) event.getNewValue());
          a.resetAvatarAttributes();
        });

    return table;
  } // end createTable()