aboutsummaryrefslogtreecommitdiff
path: root/src/ch/epfl/xblast/client/XBlastComponent.java
blob: f90d6eee3b9805df83b1e005b21a1da8a875b773 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
package ch.epfl.xblast.client;

import ch.epfl.xblast.Cell;
import ch.epfl.xblast.Lists;
import ch.epfl.xblast.PlayerID;

import javax.swing.*;
import java.awt.*;
import java.awt.image.ImageObserver;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * The game graphical component.
 *
 * @author Pacien TRAN-GIRARD (261948)
 * @author Timothée FLOURE (257420)
 */
public final class XBlastComponent extends JComponent {

    /**
     * Representation of the client's window as a grid.
     */
    private static class Grid {

        /**
         * Parameters of a grid.
         */
        private static final Dimension CELL_DIMENSION = new Dimension(64, 48);
        private static final Dimension SCORE_IMG_DIMENSION = new Dimension(48, 48);
        private static final Dimension TIME_LED_DIMENSION = new Dimension(16, 16);
        private static final Dimension FRAME_DIMENSION = totalDimension();

        /**
         * Positions of the elements on the grid.
         */
        private static final List<Point> CELL_POSITIONS = buildCellGrid(CELL_DIMENSION);
        private static final List<Point> SCORE_BG_POSITIONS = buildScoreGrid();
        private static final List<Point> TIME_LINE_POSITIONS = buildTimeLineGrid();
        private static final Map<PlayerID, Point> SCORE_TXT_POSITIONS = buildScoreMap(659);

        /**
         * Returns the dimension of the grid.
         *
         * @return the dimension of the grid
         */
        private static Dimension totalDimension() {
            return new Dimension(
                    CELL_DIMENSION.width * Cell.COLUMNS,
                    CELL_DIMENSION.height * Cell.ROWS + SCORE_IMG_DIMENSION.height + TIME_LED_DIMENSION.height);
        }

        /**
         * Returns a list of positions of every cell on the grid.
         *
         * @param elementDim dimension of a cell
         * @return a list of positions of every cell on the grid
         */
        private static List<Point> buildCellGrid(Dimension elementDim) {
            return Collections.unmodifiableList(Cell.ROW_MAJOR_ORDER.stream()
                    .map(c -> new Point(c.x() * elementDim.width, c.y() * elementDim.height))
                    .collect(Collectors.toList()));
        }

        /**
         * Builds a list containing the positions of the elements of a line.
         *
         * @param elementDim dimension of the every element of the line
         * @param len length of the line
         * @param shift shift
         * @return list containing the positions of the elements of a line.
         */
        private static List<Point> buildLine(Dimension elementDim, int len, Point shift) {
            return Collections.unmodifiableList(IntStream.range(0, len)
                    .mapToObj(x -> new Point(x * elementDim.width + shift.x, shift.y))
                    .collect(Collectors.toList()));
        }

        /**
         * Builds the list of the positions of the scores' line elements.
         *
         * @return the list of the positions of the scores' line elements.
         */
        private static List<Point> buildScoreGrid() {
            return buildLine(
                    SCORE_IMG_DIMENSION,
                    FRAME_DIMENSION.width / SCORE_IMG_DIMENSION.width,
                    new Point(0, CELL_DIMENSION.height * Cell.ROWS));
        }

        /**
         * Builds the list of the positions of the time "line" elements.
         *
         * @return the list of the positions of the time "line" elements.
         */
        private static List<Point> buildTimeLineGrid() {
            return buildLine(
                    TIME_LED_DIMENSION,
                    FRAME_DIMENSION.width / TIME_LED_DIMENSION.width,
                    new Point(0, CELL_DIMENSION.height * Cell.ROWS + SCORE_IMG_DIMENSION.height));
        }

        /**
         * Builds the map linking the players' ID to the location of their scores on the grid.
         *
         * @param vShift vertical position of the scores on the grid
         * @return map linking the players' ID to the location of their scores on the grid
         */
        private static Map<PlayerID, Point> buildScoreMap(int vShift) {
            Map<PlayerID, Point> m = new EnumMap<>(PlayerID.class);
            m.put(PlayerID.PLAYER_1, new Point(96, vShift));
            m.put(PlayerID.PLAYER_2, new Point(240, vShift));
            m.put(PlayerID.PLAYER_3, new Point(768, vShift));
            m.put(PlayerID.PLAYER_4, new Point(912, vShift));
            return m;
        }

        /**
         * Computes the position of a player on the grid.
         *
         * @param p given player
         * @return the player's position on the grid
         */
        private static Point positionForPlayer(GameState.Player p) {
            return new Point(
                    4 * p.position().x() - 24,
                    3 * p.position().y() - 52);
        }

    }

    /**
     * Client's painter.
     */
    private static class Painter {

        /**
         * Painter's parameters.
         */
        private static final ImageObserver IMG_OBSERVER = null;
        private static final Font TXT_FONT = new Font("Arial", Font.BOLD, 25);
        private static final Color TXT_COLOR = Color.WHITE;

        /**
         * Draws a string on the graphic context at the given point.
         *
         * @param g the graphic context
         * @param pos position on the graphic context
         * @param str string to be draw
         */
        private static void drawString(Graphics2D g, Point pos, String str) {
            g.setColor(TXT_COLOR);
            g.setFont(TXT_FONT);
            g.drawString(str, pos.x, pos.y);
        }

        /**
         * Draws an image on the graphic at the given point.
         *
         * @param g the graphic context
         * @param pos position on the graphic context.
         * @param img image to be draw
         */
        private static void drawImage(Graphics2D g, Point pos, Image img) {
            g.drawImage(img, pos.x, pos.y, IMG_OBSERVER);
        }

        /**
         * Draws images given their location on the graphic context.
         *
         * @param g the graphic context
         * @param m map linking images to points on the graphic context
         */
        private static void drawImageMap(Graphics2D g, Map<Point, Image> m) {
            for (Map.Entry<Point, Image> e : m.entrySet())
                drawImage(g, e.getKey(), e.getValue());
        }

        /**
         * Draws strings given their location on the graphic context.
         *
         * @param g the graphic context
         * @param m map linking strings to their locations
         */
        private static <T> void drawStringMap(Graphics2D g, Map<Point, T> m) {
            for (Map.Entry<Point, T> e : m.entrySet())
                drawString(g, e.getKey(), e.getValue().toString());
        }

        /**
         * Draws the players on the graphic context.
         *
         * @param g       the graphic context
         * @param players the list of players to be displayed
         */
        private static void drawPlayers(Graphics2D g, List<GameState.Player> players) {
            for (GameState.Player p : players)
                drawImage(g, Grid.positionForPlayer(p), p.image());
        }

    }

    /**
     * Sorts the players in order to have the current player at first.
     *
     * @param players list of the players
     * @param currentPlayerID player ID of the current player (the player playing on this client)
     * @return the ordered list of players
     */
    private static List<GameState.Player> sortPlayers(List<GameState.Player> players, PlayerID currentPlayerID) {
        if (Objects.isNull(currentPlayerID))
            return players;

        return Lists.sorted(
                players,
                GameState.Player
                        .POSITION_COMPARATOR
                        .thenComparing(GameState.Player.idPushingComparator(currentPlayerID)));
    }

    /**
     * GameState to be displayed.
     */
    private GameState gameState;

    /**
     * ID of the player related to this client.
     */
    private PlayerID playerID;

    /**
     * Displays the given GameState from the point of view of the given playerID.
     *
     * @param gs  GameState to be displayed
     * @param pid playerID related to the view
     */
    public void setGameState(GameState gs, PlayerID pid) {
        this.gameState = gs;
        this.playerID = pid;
        this.repaint();
    }

    /**
     * Returns the value of the preferredSize property.
     *
     * @return the value of the preferredSize property
     */
    @Override
    public Dimension getPreferredSize() {
        return Grid.FRAME_DIMENSION;
    }

    /**
     * Draws the component.
     *
     * @param g0 the Graphics context
     */
    @Override
    protected void paintComponent(Graphics g0) {
        Graphics2D g = (Graphics2D) g0;

        if (Objects.isNull(this.gameState))
            return;

        Painter.drawImageMap(g, Lists.linearMap(Grid.CELL_POSITIONS, this.gameState.board()));
        Painter.drawImageMap(g, Lists.linearMap(Grid.CELL_POSITIONS, this.gameState.explosions()));
        Painter.drawImageMap(g, Lists.linearMap(Grid.SCORE_BG_POSITIONS, this.gameState.scores()));
        Painter.drawImageMap(g, Lists.linearMap(Grid.TIME_LINE_POSITIONS, this.gameState.ticks()));
        Painter.drawStringMap(g, Lists.traverseMaps(Lists.invertMap(Grid.SCORE_TXT_POSITIONS), this.gameState.playerScores()));
        Painter.drawPlayers(g, sortPlayers(this.gameState.players(), this.playerID));
    }

}