diff --git a/backend/src/main/java/game/SecretHitlerGame.java b/backend/src/main/java/game/SecretHitlerGame.java index 775d6d1..81dcc3c 100644 --- a/backend/src/main/java/game/SecretHitlerGame.java +++ b/backend/src/main/java/game/SecretHitlerGame.java @@ -67,7 +67,7 @@ public class SecretHitlerGame implements Serializable { // The last president and chancellor that were successfully voted into office. private String lastPresident; private String lastChancellor; - private Policy.Type lastEnactedPolicy; + private Policy.Type lastEnactedPolicy = Policy.Type.FASCIST; private String currentPresident; private String currentChancellor; diff --git a/backend/src/main/java/server/SecretHitlerServer.java b/backend/src/main/java/server/SecretHitlerServer.java index 9be2e7d..20fa086 100644 --- a/backend/src/main/java/server/SecretHitlerServer.java +++ b/backend/src/main/java/server/SecretHitlerServer.java @@ -618,7 +618,7 @@ private static void onWebSocketMessage(WsMessageContext ctx) { break; case COMMAND_GET_STATE: // Requests the updated state of the game. - lobby.updateUser(ctx); + lobby.updateUser(ctx, name); break; case COMMAND_NOMINATE_CHANCELLOR: // params: PARAM_TARGET (String) diff --git a/backend/src/main/java/server/util/GameToJSONConverter.java b/backend/src/main/java/server/util/GameToJSONConverter.java index 23dbcb8..b3b1b75 100644 --- a/backend/src/main/java/server/util/GameToJSONConverter.java +++ b/backend/src/main/java/server/util/GameToJSONConverter.java @@ -2,6 +2,7 @@ import game.GameState; import game.SecretHitlerGame; +import game.datastructures.Identity; import game.datastructures.Player; import game.datastructures.Policy; import org.json.JSONObject; @@ -14,34 +15,50 @@ public class GameToJSONConverter { /** * Creates a JSON object from a SecretHitlerGame that represents its state. + * * @param game the SecretHitlerGame to convert. + * @param name the name of the user to create JSON content for. This is used + * to determine what player identity information to send to the + * user. * @throws NullPointerException if {@code game} is null. * @return a JSONObject with the following properties: - * - {@code state}: the state of the game. - * - {@code player-order}: an array of names representing the order of the players in the game. + * - {@code state}: the state of the game. + * - {@code player-order}: an array of names representing the order of + * the players in the game. * - * - {@code players}: a JSONObject map, with keys that are a player's {@code username}. - * Each {@code username} key maps to an object with the properties {@code id} (String), - * {@code alive} (boolean), and {@code investigated} (boolean), to represent the player. - * The identity is either this.HITLER, this.FASCIST, or this.LIBERAL. - * Ex: {"player1":{"alive": true, "investigated": false, "id": "LIBERAL"}}. + * - {@code players}: a JSONObject map, with keys that are a player's + * {@code username}. + * Each {@code username} key maps to an object with the properties + * {@code id} (String), + * {@code alive} (boolean), and {@code investigated} (boolean), to + * represent the player. + * The identity is either this.HITLER, this.FASCIST, or this.LIBERAL. + * Ex: {"player1":{"alive": true, "investigated": false, "id": + * "LIBERAL"}}. * - * - {@code president}: the username of the current president. - * - {@code chancellor}: the username of the current chancellor (can be null). - * - {@code last-president}: The username of the last president that presided over a legislative session. - * - {@code last-chancellor}: The username of the last chancellor that presided over a legislative session. - * - {@code draw-size}: The size of the draw deck. - * - {@code discard-size}: The size of the discard deck. - * - {@code fascist-policies}: The number of passed fascist policies. - * - {@code liberal-policies}: The number of passed liberal policies.: - * - {@code user-votes}: A map from each user to their vote from the last chancellor nomination. - * - {@code president-choices}: The choices for the president during the legislative session (only if in - * game state LEGISLATIVE_PRESIDENT). - * - {@code chancellor-choices}: The choices for the chancellor during the legislative session (only if in - * game state LEGISLATIVE_CHANCELLOR). - * - {@code veto-occurred}: Set to true if a veto has already taken place on this legislative session. + * - {@code president}: the username of the current president. + * - {@code chancellor}: the username of the current chancellor (can be + * null). + * - {@code last-president}: The username of the last president that + * presided over a legislative session. + * - {@code last-chancellor}: The username of the last chancellor that + * presided over a legislative session. + * - {@code draw-size}: The size of the draw deck. + * - {@code discard-size}: The size of the discard deck. + * - {@code fascist-policies}: The number of passed fascist policies. + * - {@code liberal-policies}: The number of passed liberal policies.: + * - {@code user-votes}: A map from each user to their vote from the + * last chancellor nomination. + * - {@code president-choices}: The choices for the president during the + * legislative session (only if in + * game state LEGISLATIVE_PRESIDENT). + * - {@code chancellor-choices}: The choices for the chancellor during + * the legislative session (only if in + * game state LEGISLATIVE_CHANCELLOR). + * - {@code veto-occurred}: Set to true if a veto has already taken + * place on this legislative session. */ - public static JSONObject convert(SecretHitlerGame game) { + public static JSONObject convert(SecretHitlerGame game, String userName) { if (game == null) { throw new NullPointerException(); } @@ -51,14 +68,22 @@ public static JSONObject convert(SecretHitlerGame game) { String[] playerOrder = new String[game.getPlayerList().size()]; List playerList = game.getPlayerList(); + // Players should only be shown all roles under specific circumstances. + Identity role = game.getPlayer(userName).getIdentity(); + boolean showAllRoles = game.hasGameFinished() || role == Identity.FASCIST + || (role == Identity.HITLER && game.getPlayerList().size() <= 6); + for (int i = 0; i < playerList.size(); i++) { JSONObject playerObj = new JSONObject(); Player player = playerList.get(i); playerObj.put("alive", player.isAlive()); - String id = player.getIdentity().toString(); - playerObj.put("id", id); + // Only include player role for self or under specific rules + if (player.getUsername().equals(userName) || showAllRoles) { + String id = player.getIdentity().toString(); + playerObj.put("id", id); + } playerObj.put("investigated", player.hasBeenInvestigated()); playerData.put(player.getUsername(), playerObj); @@ -79,6 +104,8 @@ public static JSONObject convert(SecretHitlerGame game) { out.put("election-tracker", game.getElectionTracker()); out.put("election-tracker-advanced", game.didElectionTrackerAdvance()); + out.put("last-policy", game.getLastEnactedPolicy().toString().toUpperCase()); + out.put("draw-size", game.getDrawSize()); out.put("discard-size", game.getDiscardSize()); out.put("fascist-policies", game.getNumFascistPolicies()); @@ -101,13 +128,15 @@ public static JSONObject convert(SecretHitlerGame game) { /** * Converts a list of policies into a string array. + * * @param list the list of policies. - * @return a string array with the same length as the list, where each index is either "FASCIST" or "LIBERAL" + * @return a string array with the same length as the list, where each index is + * either "FASCIST" or "LIBERAL" * according to the type of the Policy at that index in the list. */ public static String[] convertPolicyListToStringArray(List list) { String[] out = new String[list.size()]; - for(int i = 0; i < list.size(); i++) { + for (int i = 0; i < list.size(); i++) { out[i] = list.get(i).getType().toString(); } return out; diff --git a/backend/src/main/java/server/util/Lobby.java b/backend/src/main/java/server/util/Lobby.java index afeaf26..57c2b43 100644 --- a/backend/src/main/java/server/util/Lobby.java +++ b/backend/src/main/java/server/util/Lobby.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.io.Serializable; import java.util.*; +import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentSkipListSet; @@ -291,8 +292,8 @@ synchronized public int getUserCount() { * updates all connected CpuPlayers after a set amount of time. */ synchronized public void updateAllUsers() { - for (WsContext ws : userToUsername.keySet()) { - updateUser(ws); + for (Entry entry : userToUsername.entrySet()) { + updateUser(entry.getKey(), entry.getValue()); } // Check if the game ended. @@ -365,10 +366,10 @@ public void run() { * SecretHitlerGame is sent * to the specified WsContext. ({@code GameToJSONConverter.convert()}) */ - synchronized public void updateUser(WsContext ctx) { + synchronized public void updateUser(WsContext ctx, String userName) { JSONObject message; if (isInGame()) { - message = GameToJSONConverter.convert(game); // sends the game state + message = GameToJSONConverter.convert(game, userName); // sends the game state message.put(SecretHitlerServer.PARAM_PACKET_TYPE, SecretHitlerServer.PACKET_GAME_STATE); } else { message = new JSONObject(); diff --git a/frontend/src/App.js b/frontend/src/App.js index d4f0a26..4298e86 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -53,6 +53,7 @@ import { PARAM_ELEC_TRACKER_ADVANCED, COMMAND_END_TERM, PARAM_LAST_STATE, + PARAM_LAST_POLICY, STATE_PP_PEEK, PLAYER_IS_ALIVE, PARAM_TARGET, @@ -921,7 +922,7 @@ class App extends Component { this.queueAlert(( + policyType={newState[PARAM_LAST_POLICY]}/> )); } diff --git a/frontend/src/GlobalDefinitions.js b/frontend/src/GlobalDefinitions.js index ed8d5ef..8860720 100644 --- a/frontend/src/GlobalDefinitions.js +++ b/frontend/src/GlobalDefinitions.js @@ -118,5 +118,6 @@ export const PARAM_DISCARD_DECK = "discard-size"; export const PARAM_PRESIDENT_CHOICES = "president-choices"; export const PARAM_CHANCELLOR_CHOICES = "chancellor-choices"; export const PARAM_TARGET = "target-user"; +export const PARAM_LAST_POLICY = "last-policy"; export const PARAM_DID_VETO_OCCUR = "veto-occurred"; // diff --git a/frontend/src/util/PolicyDisplay.js b/frontend/src/util/PolicyDisplay.js index 3b532a3..97c910b 100644 --- a/frontend/src/util/PolicyDisplay.js +++ b/frontend/src/util/PolicyDisplay.js @@ -1,37 +1,48 @@ -import React, {Component} from 'react'; +import React, { Component } from "react"; import PropTypes from "prop-types"; -import './PolicyDisplay.css'; -import {LIBERAL} from "../GlobalDefinitions"; +import "./PolicyDisplay.css"; +import { LIBERAL } from "../GlobalDefinitions"; import LiberalPolicy from "../assets/policy-liberal.png"; import FascistPolicy from "../assets/policy-fascist.png"; class PolicyDisplay extends Component { - render() { - return ( -
- {this.props.policies.map((value, index) => { - let policyName = value === LIBERAL ? "liberal" : "fascist"; - return ( - this.props.onClick(index)} - disabled={!this.props.allowSelection} - src={value === LIBERAL ? LiberalPolicy : FascistPolicy} // Toggles fascist/liberal policy - alt={"A " + policyName + " policy." + (this.props.allowSelection ? " Click to select." : "")} - /> - ); - } )} -
- ); - } + render() { + return ( +
+ {this.props.policies.map((value, index) => { + let policyName = value === LIBERAL ? "liberal" : "fascist"; + return ( + this.props.onClick(index)} + disabled={!this.props.allowSelection} + src={value === LIBERAL ? LiberalPolicy : FascistPolicy} // Toggles fascist/liberal policy + alt={ + "A " + + policyName + + " policy." + + (this.props.allowSelection ? " Click to select." : "") + } + /> + ); + })} +
+ ); + } } PolicyDisplay.propTypes = { - policies: PropTypes.array.isRequired, - onClick: PropTypes.func, // If undefined, the policies cannot be selected. - selection: PropTypes.number, - allowSelection: PropTypes.bool, + policies: PropTypes.array.isRequired, + onClick: PropTypes.func, // If undefined, the policies cannot be selected. + selection: PropTypes.number, + allowSelection: PropTypes.bool, }; -export default PolicyDisplay; \ No newline at end of file +export default PolicyDisplay;