/** * 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()