Compare commits

...

4 Commits

Author SHA1 Message Date
Peter Galloway dd43097677 the boat can now turn after snapping to starboard vmg #story[1273] 2017-09-26 17:07:55 +13:00
Haoming Yin 1bd4db73cd Issue 66: client side error pop ups use default javaFx style
- created JFeonix style pop up to replace the default one

tags: #story[1273]
2017-09-26 14:44:37 +13:00
Calum 7027de80c4 Fixed issues with correct protocol implementation.
#issue[64] #fix #testmanual
2017-09-25 22:09:26 +13:00
William Muir 5c50e77efa Merge branch 'Story1278_keybindings' into 'develop'
Story1278 keybindings

# Changes

- Added customised action key binding for all boat actions
- Users can select the turning mode (continuously turning mode, and the default turning mode)
- Refined UI design for key bindings
- Added animation which mouse enters/exits the setting button, and also prompt text to guide users to press key
- Popup window to inform users if their key assignment is successful or not

# Testing

## Manual testing log as below (all passed according to the assigner)
- if a player toggles the turning mode toggle button, the labels for upwind and downwind will be changed correspondingly
- if a player clicks the "reset" button, all the key binding settings will be reset to default
- if a player assigns a key which is already is in use, a popup box will inform the assignment is failed, and action will be changed back
- if a player assigns a key successfully, a popup will inform the success, and the new key binding will appear on the button
- A player can change their key bindings before the race starts and during the race


See merge request !73
2017-09-25 17:11:12 +13:00
11 changed files with 213 additions and 35 deletions
@@ -5,19 +5,19 @@ package seng302.gameServer.messages;
*/
public class BoatActionMessage extends Message{
private final MessageType MESSAGE_TYPE = MessageType.BOAT_ACTION;
private final int MESSAGE_SIZE = 1;
private final int MESSAGE_SIZE = 5;
private BoatAction actionType;
public BoatActionMessage(BoatAction actionType) {
public BoatActionMessage(BoatAction actionType, int sourceId) {
this.actionType = actionType;
setHeader(new Header(MessageType.BOAT_ACTION, 0, (short) 1)); // the second variable is the source id
setHeader(new Header(MessageType.BOAT_ACTION, sourceId, (short) MESSAGE_SIZE)); // the second variable is the source id
allocateBuffer();
writeHeaderToBuffer();
// Write message fields
putInt(actionType.getValue(), 1);
putInt(sourceId, 4);
writeCRC();
rewind();
}
@Override
@@ -4,8 +4,8 @@ package seng302.gameServer.messages;
public class RegistrationRequestMessage extends Message {
private static int MESSAGE_LENGTH = 2;
public RegistrationRequestMessage(ClientType type){
setHeader(new Header(MessageType.REGISTRATION_REQUEST, 1, (short) getSize()));
public RegistrationRequestMessage(ClientType type, int clientID){
setHeader(new Header(MessageType.REGISTRATION_REQUEST, clientID, (short) getSize()));
allocateBuffer();
writeHeaderToBuffer();
+6 -5
View File
@@ -156,11 +156,11 @@ public class ServerYacht {
/**
* Enables the boats auto pilot feature, which will move the boat towards a given heading.
*
* @param thisHeading The heading to move the boat towards.
* @param newHeading The heading to move the boat towards.
*/
private void setAutoPilot(Double thisHeading) {
private void setAutoPilot(Double newHeading) {
isAuto = true;
autoHeading = thisHeading;
autoHeading = newHeading;
}
/**
@@ -178,8 +178,9 @@ public class ServerYacht {
if (isAuto) {
turnTowardsHeading(autoHeading);
if (Math.abs(heading - autoHeading)
<= turnStep) { //Cancel when within 1 turn step of target.
<= turnStep*1.5) {
isAuto = false;
setHeading(autoHeading);
}
}
}
@@ -265,7 +266,7 @@ public class ServerYacht {
// Take optimal heading and turn into a boat heading rather than a wind heading.
optimalHeading =
optimalHeading + GameState.getWindDirection();
(optimalHeading + GameState.getWindDirection()) % 360;
setAutoPilot(optimalHeading);
}
+28 -11
View File
@@ -4,12 +4,14 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import javafx.scene.paint.Color;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import seng302.model.ClientYacht;
import seng302.model.Colors;
import seng302.model.Limit;
import seng302.model.mark.CompoundMark;
import seng302.model.mark.Corner;
@@ -139,14 +141,26 @@ public class XMLParser {
Node currentBoat = boatsList.item(i);
if (currentBoat.getNodeName().equals("Boat")) {
// Boat boat = new Boat(currentBoat);
BoatMeshType boatMeshType;
try {
boatMeshType = BoatMeshType.valueOf(XMLParser.getNodeAttributeString(currentBoat, "Type"));
} catch (IllegalArgumentException e){
boatMeshType = BoatMeshType.DINGHY;
}
Color color;
try {
color = Color.web(getNodeAttributeString(currentBoat, "Color"));
} catch (NullPointerException npe) {
color = Colors.getColor(new Random().nextInt(8));
}
ClientYacht yacht = new ClientYacht(
BoatMeshType.valueOf(XMLParser.getNodeAttributeString(currentBoat, "Type")),
boatMeshType,
XMLParser.getNodeAttributeInt(currentBoat, "SourceID"),
XMLParser.getNodeAttributeString(currentBoat, "HullNum"),
XMLParser.getNodeAttributeString(currentBoat, "ShortName"),
XMLParser.getNodeAttributeString(currentBoat, "BoatName"),
XMLParser.getNodeAttributeString(currentBoat, "Country"));
yacht.setColour(Color.web(getNodeAttributeString(currentBoat, "Color")));
yacht.setColour(color);
competingBoats.put(yacht.getSourceId(), yacht);
}
}
@@ -195,17 +209,20 @@ public class XMLParser {
*/
private static List<Token> extractTokens(Element docEle) {
List<Token> tokens = new ArrayList<>();
NodeList tokenList = docEle.getElementsByTagName("Tokens").item(0).getChildNodes();
for (int i = 0; i < tokenList.getLength(); i++) {
Node tokenNode = tokenList.item(i);
if (tokenNode.getNodeName().equals("Token")) {
String tokenType = getNodeAttributeString(tokenNode, "TokenType");
Double lat = getNodeAttributeDouble(tokenNode, "TargetLat");
Double lng = getNodeAttributeDouble(tokenNode, "TargetLng");
tokens.add(new Token(TokenType.valueOf(tokenType), lat, lng));
try {
NodeList tokenList = docEle.getElementsByTagName("Tokens").item(0).getChildNodes();
for (int i = 0; i < tokenList.getLength(); i++) {
Node tokenNode = tokenList.item(i);
if (tokenNode.getNodeName().equals("Token")) {
String tokenType = getNodeAttributeString(tokenNode, "TokenType");
Double lat = getNodeAttributeDouble(tokenNode, "TargetLat");
Double lng = getNodeAttributeDouble(tokenNode, "TargetLng");
tokens.add(new Token(TokenType.valueOf(tokenType), lat, lng));
}
}
} catch (NullPointerException npe) {
return new ArrayList<>();
}
return tokens;
}
@@ -175,7 +175,7 @@ public class ClientToServerThread implements Runnable {
* Sends a request to the server asking for a source ID
*/
private void sendRegistrationRequest() {
RegistrationRequestMessage requestMessage = new RegistrationRequestMessage(ClientType.PLAYER);
RegistrationRequestMessage requestMessage = new RegistrationRequestMessage(ClientType.PLAYER, clientId);
try {
os.write(requestMessage.getBuffer());
@@ -193,7 +193,6 @@ public class ClientToServerThread implements Runnable {
private void processRegistrationResponse(StreamPacket packet){
int sourceId = (int) Message.bytesToLong(Arrays.copyOfRange(packet.getPayload(), 0, 3));
int statusCode = (int) Message.bytesToLong(Arrays.copyOfRange(packet.getPayload(), 4,5));
RegistrationResponseStatus status = RegistrationResponseStatus.getResponseStatus(statusCode);
if (status.equals(RegistrationResponseStatus.SUCCESS_PLAYING)){
@@ -243,7 +242,7 @@ public class ClientToServerThread implements Runnable {
new TimerTask() {
@Override
public void run() {
sendBoatActionMessage(new BoatActionMessage(BoatAction.DOWNWIND));
sendBoatActionMessage(new BoatActionMessage(BoatAction.DOWNWIND, clientId));
}
}, 0, PACKET_SENDING_INTERVAL_MS
);
@@ -256,14 +255,14 @@ public class ClientToServerThread implements Runnable {
new TimerTask() {
@Override
public void run() {
sendBoatActionMessage(new BoatActionMessage(BoatAction.UPWIND));
sendBoatActionMessage(new BoatActionMessage(BoatAction.UPWIND, clientId));
}
}, 0, PACKET_SENDING_INTERVAL_MS
);
}
break;
default:
sendBoatActionMessage(new BoatActionMessage(actionType));
sendBoatActionMessage(new BoatActionMessage(actionType, clientId));
break;
}
}
@@ -14,8 +14,6 @@ import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
@@ -46,6 +44,7 @@ import seng302.utilities.XMLParser;
import seng302.visualiser.controllers.LobbyController;
import seng302.visualiser.controllers.RaceViewController;
import seng302.visualiser.controllers.ViewManager;
import seng302.visualiser.controllers.dialogs.PopupDialogController;
/**
* This class is a client side instance of a yacht racing game in JavaFX. The game is instantiated
@@ -169,10 +168,12 @@ public class GameClient {
private void showConnectionError (String message) {
Platform.runLater(() -> {
Alert alert = new Alert(AlertType.ERROR);
alert.setHeaderText("Connection Error");
alert.setContentText(message);
alert.showAndWait();
PopupDialogController controller = ViewManager.getInstance().showPopupDialog();
controller.setHeader("Oops");
controller.setContent(message);
controller.setOptionButtonText("GO HOME");
controller
.setOptionButtonEventHandler(event -> ViewManager.getInstance().goToStartView());
});
}
@@ -398,7 +399,11 @@ public class GameClient {
}
if (gameKeyBind.getKeyCode(KeyAction.SAILS_STATE) == e.getCode()) { // sails in/sails out
socketThread.sendBoatAction(BoatAction.SAILS_IN);
if (allBoatsMap.get(socketThread.getClientId()).getSailIn()) {
socketThread.sendBoatAction(BoatAction.SAILS_OUT);
} else {
socketThread.sendBoatAction(BoatAction.SAILS_IN);
}
allBoatsMap.get(socketThread.getClientId()).toggleSail();
} else if (gameKeyBind.getKeyCode(KeyAction.UPWIND) == e.getCode()
|| gameKeyBind.getKeyCode(KeyAction.DOWNWIND) == e.getCode()) {
@@ -88,7 +88,6 @@ public class FinishScreenViewController implements Initializable {
public void switchToStartScreenView() {
Sounds.playButtonClick();
//TODO merge fix
setContentPane("/views/StartScreenView.fxml");
}
@@ -28,6 +28,7 @@ import seng302.utilities.BonjourInstallChecker;
import seng302.utilities.Sounds;
import seng302.visualiser.GameClient;
import seng302.visualiser.controllers.dialogs.KeyBindingDialogController;
import seng302.visualiser.controllers.dialogs.PopupDialogController;
public class ViewManager {
@@ -229,6 +230,26 @@ public class ViewManager {
keyBindingDialog.close();
}
public PopupDialogController showPopupDialog() {
FXMLLoader dialogContent = new FXMLLoader(
getClass().getResource("/views/dialogs/PopupDialog.fxml"));
for (Node node : decorator.getChildren()) {
if (node instanceof StackPane) {
try {
JFXDialog dialog = new JFXDialog((StackPane) node, dialogContent.load(),
DialogTransition.CENTER);
PopupDialogController popupDialogController = dialogContent.getController();
popupDialogController.setPopupDialog(dialog);
dialog.show();
return popupDialogController;
} catch (IOException e) {
logger.error("Cannot load Popup dialog");
}
}
}
return null;
}
/**
* Show a snackbar at the bottom of the app for 1 second.
*
@@ -0,0 +1,56 @@
package seng302.visualiser.controllers.dialogs;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXDialog;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
public class PopupDialogController implements Initializable {
@FXML
private Label headerLabel;
@FXML
private Label contentLabel;
@FXML
private Label closeLabel;
@FXML
private JFXButton optionButton;
@FXML
private JFXDialog popupDialog;
@Override
public void initialize(URL location, ResourceBundle resources) {
}
public void setContent(String content) {
this.contentLabel.setText(content);
}
public void setHeader(String header) {
this.headerLabel.setText(header);
}
public void setOptionButton(JFXButton jfxButton) {
this.optionButton = jfxButton;
}
public void setOptionButtonText(String text) {
this.optionButton.setText(text);
}
public void setOptionButtonEventHandler(EventHandler<? super MouseEvent> eventHandler) {
this.optionButton.setOnMouseClicked(eventHandler);
}
public void setPopupDialog(JFXDialog popupDialog) {
this.popupDialog = popupDialog;
this.closeLabel.setOnMouseClicked(event -> this.popupDialog.close());
}
}
+33
View File
@@ -0,0 +1,33 @@
#headerLabel {
-fx-font-size: 20px;
-fx-text-fill: -fx-pp-dark-text-color;
}
#closeLabel {
-fx-font-size: 22px;
-fx-text-fill: -fx-pp-dark-text-color;
}
#closeLabel:hover {
-fx-font-size: 24px;
-fx-text-fill: red;
}
#contentLabel {
-fx-font-size: 22px;
-fx-text-fill: -fx-pp-dark-text-color;
}
#optionButton {
-fx-background-color: -fx-pp-theme-color;
-fx-text-fill: -fx-pp-light-text-color;
-fx-font-size: 18px;
-fx-effect: -fx-pp-dropshadow-light;
-fx-max-height: 55;
-fx-focus-traversable: false;
}
#optionButton:hover {
-fx-font-size: 20px !important;
-fx-background-color: -fx-pp-light-theme-color;
}
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.JFXButton?>
<?import com.jfoenix.controls.JFXDialogLayout?>
<?import java.net.URL?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<JFXDialogLayout maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity"
minWidth="-Infinity" prefWidth="550.0" xmlns="http://javafx.com/javafx/8"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="seng302.visualiser.controllers.dialogs.PopupDialogController">
<children>
<GridPane>
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="1.7976931348623157E308"/>
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="-Infinity" minHeight="30.0" prefHeight="30.0"
vgrow="SOMETIMES"/>
<RowConstraints maxHeight="1.7976931348623157E308" minHeight="80.0"
prefHeight="80.0" vgrow="SOMETIMES"/>
<RowConstraints minHeight="50.0" prefHeight="50.0" vgrow="SOMETIMES"/>
</rowConstraints>
<children>
<JFXButton fx:id="optionButton" buttonType="RAISED" prefHeight="55.0"
prefWidth="150.0" GridPane.halignment="RIGHT" GridPane.rowIndex="2"
GridPane.valignment="CENTER">
<GridPane.margin>
<Insets/>
</GridPane.margin>
</JFXButton>
<Label fx:id="contentLabel" text="Popup content goes here ..."
GridPane.rowIndex="1"/>
<Label fx:id="headerLabel" text="Popup header"/>
<Label fx:id="closeLabel" text="✖" translateY="-10.0" GridPane.halignment="RIGHT"/>
</children>
</GridPane>
</children>
<stylesheets>
<URL value="@../../css/dialogs/Popup.css"/>
<URL value="@../../css/Master.css"/>
</stylesheets>
</JFXDialogLayout>