Compare commits
23 Commits
17bdd3e37f
...
08fc96187b
| Author | SHA1 | Date |
|---|---|---|
|
|
08fc96187b | |
|
|
1613ab575f | |
|
|
e9e8c7e650 | |
|
|
d3111b436c | |
|
|
ddac012fd9 | |
|
|
40b50ac12f | |
|
|
3b3cc2dd0b | |
|
|
441d4a3343 | |
|
|
3ee0a5ece8 | |
|
|
ed573d3338 | |
|
|
9adb0db66b | |
|
|
13cb12717a | |
|
|
6afb3584e5 | |
|
|
a4e7517571 | |
|
|
86eb04d391 | |
|
|
f07c534205 | |
|
|
329886aa5a | |
|
|
f468f6f285 | |
|
|
326d4ce98c | |
|
|
dc766f961f | |
|
|
96589f4b6e | |
|
|
84ea584bb0 | |
|
|
3e47cd2163 |
|
|
@ -15,8 +15,8 @@
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<maven.compiler.source>8</maven.compiler.source>
|
<maven.compiler.source>21</maven.compiler.source>
|
||||||
<maven.compiler.target>8</maven.compiler.target>
|
<maven.compiler.target>21</maven.compiler.target>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,18 @@ import java.util.List;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
|
|
||||||
import de.hs_mannheim.informatik.chess.gui.Gui;
|
|
||||||
import de.hs_mannheim.informatik.chess.model.ChessEngine;
|
import de.hs_mannheim.informatik.chess.model.ChessEngine;
|
||||||
|
import de.hs_mannheim.informatik.chess.model.MoveDTO;
|
||||||
|
import de.hs_mannheim.informatik.chess.model.BoardDTO;
|
||||||
|
import de.hs_mannheim.informatik.chess.view.GameGui;
|
||||||
|
|
||||||
public class Controller {
|
public class Controller {
|
||||||
Gui gui;
|
GameGui gui;
|
||||||
ChessEngine engine;
|
ChessEngine engine;
|
||||||
private int selectedRow = -1, selectedCol = -1;
|
private int selectedRow = -1, selectedCol = -1;
|
||||||
private List<int[]> highlightedFields = new ArrayList<>();
|
private List<int[]> highlightedFields = new ArrayList<>();
|
||||||
|
|
||||||
public Controller(Gui gui, ChessEngine engine) {
|
public Controller(GameGui gui, ChessEngine engine) {
|
||||||
this.gui = gui;
|
this.gui = gui;
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
initListeners();
|
initListeners();
|
||||||
|
|
@ -44,13 +46,14 @@ public class Controller {
|
||||||
selectedCol = col;
|
selectedCol = col;
|
||||||
gui.getField(row, col).setBorder(BorderFactory.createLineBorder(Color.RED, 2));
|
gui.getField(row, col).setBorder(BorderFactory.createLineBorder(Color.RED, 2));
|
||||||
|
|
||||||
//Mögliche Ziele highlighten
|
// Mögliche Ziele highlighten (DTOs!)
|
||||||
String from = coordToChessNotation(row, col);
|
String fromSquare = coordToChessNotation(row, col); // z.B. "E2"
|
||||||
List<String> targets = engine.getLegalDestinations(from);
|
List<MoveDTO> moves = engine.getLegalDestinations(fromSquare);
|
||||||
for (String t : targets) {
|
for (MoveDTO move : moves) {
|
||||||
int[] xy = chessNotationToCoord(t);
|
int toRow = move.getToRow();
|
||||||
gui.getField(xy[0], xy[1]).setBackground(Color.YELLOW);
|
int toCol = move.getToCol();
|
||||||
highlightedFields.add(xy);
|
gui.getField(toRow, toCol).setBackground(Color.YELLOW);
|
||||||
|
highlightedFields.add(new int[]{toRow, toCol});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Zweites Feld: Zug probieren
|
// Zweites Feld: Zug probieren
|
||||||
|
|
@ -61,23 +64,24 @@ public class Controller {
|
||||||
highlightedFields.clear();
|
highlightedFields.clear();
|
||||||
|
|
||||||
gui.getField(selectedRow, selectedCol).setBorder(null);
|
gui.getField(selectedRow, selectedCol).setBorder(null);
|
||||||
handleMove(selectedRow, selectedCol, row, col);
|
|
||||||
|
// **Neues MoveDTO statt String-Kram!**
|
||||||
|
MoveDTO move = new MoveDTO(selectedRow, selectedCol, row, col);
|
||||||
|
handleMove(move);
|
||||||
|
|
||||||
selectedRow = -1;
|
selectedRow = -1;
|
||||||
selectedCol = -1;
|
selectedCol = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleMove(int fromX, int fromY, int toX, int toY) {
|
public void handleMove(MoveDTO move) {
|
||||||
String from = coordToChessNotation(fromX, fromY);
|
if (engine.move(move)) {
|
||||||
String to = coordToChessNotation(toX, toY);
|
|
||||||
if (engine.move(from, to)) {
|
|
||||||
updateGuiBoard();
|
updateGuiBoard();
|
||||||
|
|
||||||
// Jetzt: Spielstatus prüfen!
|
// Jetzt: Spielstatus prüfen!
|
||||||
if (engine.isMated()) {
|
if (engine.isMated()) {
|
||||||
String winner = engine.getCurrentPlayer().equals("WHITE") ? "SCHWARZ" : "WEIß";
|
String winner = engine.getCurrentPlayer().equals("WHITE") ? "SCHWARZ" : "WEIß";
|
||||||
gui.displayMessage(winner + " hat gewonnen (Schachmatt)!");
|
gui.displayMessage(winner + " hat gewonnen (Schachmatt)!");
|
||||||
// Optional: Hier Spiel beenden/disable oder restart anbieten
|
|
||||||
} else if (engine.isStalemate() || engine.isDraw()) {
|
} else if (engine.isStalemate() || engine.isDraw()) {
|
||||||
gui.displayMessage("Remis! (Stalemate oder andere Regel)");
|
gui.displayMessage("Remis! (Stalemate oder andere Regel)");
|
||||||
}
|
}
|
||||||
|
|
@ -87,25 +91,18 @@ public class Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateGuiBoard() {
|
public void updateGuiBoard() {
|
||||||
gui.updateBoard(engine.getBoardUnicode());
|
BoardDTO board = engine.getBoardAsDTO();
|
||||||
|
gui.updateBoard(board); // Passe die GUI an, damit sie ein BoardDTO nimmt!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hilfsmethode, um von Koordinaten (row/col) auf z.B. "E2" zu kommen
|
||||||
private String coordToChessNotation(int x, int y) {
|
private String coordToChessNotation(int x, int y) {
|
||||||
// (0,0) -> "A8", (7,7) -> "H1"
|
char file = (char)('A' + y);
|
||||||
char file = (char)('A' + y); // ACHTUNG! col == y == FILE, row == x == RANK
|
|
||||||
int rank = 8 - x;
|
int rank = 8 - x;
|
||||||
return "" + file + rank;
|
return "" + file + rank;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int[] chessNotationToCoord(String square) {
|
// ... resetFieldBackground wie gehabt ...
|
||||||
// "A8" -> (0,0), "H1" -> (7,7)
|
|
||||||
char file = square.charAt(0);
|
|
||||||
int rank = square.charAt(1) - '0';
|
|
||||||
int x = 8 - rank;
|
|
||||||
int y = file - 'A';
|
|
||||||
return new int[] {x, y};
|
|
||||||
}
|
|
||||||
|
|
||||||
private void resetFieldBackground(int row, int col) {
|
private void resetFieldBackground(int row, int col) {
|
||||||
if ((row + col) % 2 == 0) {
|
if ((row + col) % 2 == 0) {
|
||||||
gui.getField(row, col).setBackground(new Color(0x778da9));
|
gui.getField(row, col).setBackground(new Color(0x778da9));
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
package de.hs_mannheim.informatik.chess.main;
|
package de.hs_mannheim.informatik.chess.main;
|
||||||
import de.hs_mannheim.informatik.chess.gui.Gui;
|
|
||||||
import de.hs_mannheim.informatik.chess.controller.Controller;
|
import de.hs_mannheim.informatik.chess.controller.Controller;
|
||||||
import de.hs_mannheim.informatik.chess.model.ChessEngine;
|
import de.hs_mannheim.informatik.chess.model.ChessEngine;
|
||||||
|
import de.hs_mannheim.informatik.chess.view.GameGui;
|
||||||
|
import de.hs_mannheim.informatik.chess.view.MainGui;
|
||||||
|
|
||||||
|
|
||||||
public class Main{
|
public class Main{
|
||||||
|
|
||||||
public static void main( String[] args ){
|
public static void main( String[] args ){
|
||||||
Gui gui = new Gui();
|
new MainGui(() -> {
|
||||||
|
//Wenn "Normal Modus" gedrückt wird
|
||||||
|
GameGui gui = new GameGui();
|
||||||
ChessEngine engine = new ChessEngine();
|
ChessEngine engine = new ChessEngine();
|
||||||
new Controller(gui, engine);
|
new Controller(gui, engine);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package de.hs_mannheim.informatik.chess.model;
|
||||||
|
|
||||||
|
public class BoardDTO {
|
||||||
|
|
||||||
|
private PieceDTO[][] board; // 8x8 Feld
|
||||||
|
|
||||||
|
public BoardDTO(PieceDTO[][] board) {
|
||||||
|
this.board = board;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PieceDTO[][] getBoard() {
|
||||||
|
return board;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBoard(PieceDTO[][] board) {
|
||||||
|
this.board = board;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
package de.hs_mannheim.informatik.chess.model;
|
package de.hs_mannheim.informatik.chess.model;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
@ -15,42 +14,47 @@ public class ChessEngine {
|
||||||
board = new Board();
|
board = new Board();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean move(String from, String to) {
|
public boolean move(MoveDTO move) {
|
||||||
Move move = new Move(Square.valueOf(from.toUpperCase()), Square.valueOf(to.toUpperCase()));
|
String from = "" + (char)('A' + move.getFromCol()) + (8 - move.getFromRow());
|
||||||
if (board.legalMoves().contains(move)) {
|
String to = "" + (char)('A' + move.getToCol()) + (8 - move.getToRow());
|
||||||
board.doMove(move);
|
Move libMove = new Move(Square.valueOf(from), Square.valueOf(to));
|
||||||
|
if (board.legalMoves().contains(libMove)) {
|
||||||
|
board.doMove(libMove);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getLegalDestinations(String from) {
|
public List<MoveDTO> getLegalDestinations(String from) {
|
||||||
List<String> destinations = new ArrayList<>();
|
List<MoveDTO> destinations = new ArrayList<>();
|
||||||
Square fromSq = Square.valueOf(from.toUpperCase());
|
Square fromSq = Square.valueOf(from.toUpperCase());
|
||||||
for (Move move : board.legalMoves()) {
|
for (Move move : board.legalMoves()) {
|
||||||
if (move.getFrom().equals(fromSq)) {
|
if (move.getFrom().equals(fromSq)) {
|
||||||
destinations.add(move.getTo().toString()); // z.B. "E4"
|
int fromRow = 8 - fromSq.getRank().ordinal() - 1;
|
||||||
|
int fromCol = fromSq.getFile().ordinal();
|
||||||
|
int toRow = 8 - move.getTo().getRank().ordinal() - 1;
|
||||||
|
int toCol = move.getTo().getFile().ordinal();;
|
||||||
|
destinations.add(new MoveDTO(fromRow, fromCol, toRow, toCol));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return destinations;
|
return destinations;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPieceAt(String square) {
|
public PieceDTO getPieceAt(String square) {
|
||||||
Piece piece = board.getPiece(Square.valueOf(square.toUpperCase()));
|
Piece piece = board.getPiece(Square.valueOf(square.toUpperCase()));
|
||||||
return piece.toString(); // z.B. "WHITE_PAWN"
|
return convertPieceToDTO(piece);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String[][] getBoardUnicode() {
|
public BoardDTO getBoardAsDTO() {
|
||||||
String[][] unicodeBoard = new String[8][8];
|
PieceDTO[][] dtoBoard = new PieceDTO[8][8];
|
||||||
for (int rank = 8; rank >= 1; rank--) {
|
for (int rank = 8; rank >= 1; rank--) {
|
||||||
for (int file = 0; file < 8; file++) {
|
for (int file = 0; file < 8; file++) {
|
||||||
Square square = Square.valueOf("" + (char)('A' + file) + rank);
|
Square square = Square.valueOf("" + (char)('A' + file) + rank);
|
||||||
Piece piece = board.getPiece(square);
|
Piece piece = board.getPiece(square);
|
||||||
unicodeBoard[8-rank][file] = pieceToUnicode(piece);
|
dtoBoard[8-rank][file] = convertPieceToDTO(piece);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return unicodeBoard;
|
return new BoardDTO(dtoBoard);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String pieceToUnicode(Piece piece) {
|
private String pieceToUnicode(Piece piece) {
|
||||||
|
|
@ -71,6 +75,14 @@ public class ChessEngine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PieceDTO convertPieceToDTO(Piece piece) {
|
||||||
|
if (piece == null || piece.equals(Piece.NONE)) return null;
|
||||||
|
String color = piece.name().startsWith("WHITE") ? "WHITE" : "BLACK";
|
||||||
|
String type = piece.name().substring(piece.name().indexOf('_') + 1); // "PAWN", "KING"...
|
||||||
|
String symbol = pieceToUnicode(piece);
|
||||||
|
return new PieceDTO(type, color, symbol);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isMated() {
|
public boolean isMated() {
|
||||||
return board.isMated();
|
return board.isMated();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
package de.hs_mannheim.informatik.chess.model;
|
||||||
|
|
||||||
|
public class MoveDTO {
|
||||||
|
private int fromRow, fromCol;
|
||||||
|
private int toRow, toCol;
|
||||||
|
|
||||||
|
public MoveDTO(int fromRow, int fromCol, int toRow, int toCol) {
|
||||||
|
this.fromRow = fromRow;
|
||||||
|
this.fromCol = fromCol;
|
||||||
|
this.toRow = toRow;
|
||||||
|
this.toCol = toCol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFromRow() {
|
||||||
|
return fromRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFromCol() {
|
||||||
|
return fromCol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getToRow() {
|
||||||
|
return toRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getToCol() {
|
||||||
|
return toCol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package de.hs_mannheim.informatik.chess.model;
|
||||||
|
|
||||||
|
public class PieceDTO {
|
||||||
|
private String type; // z.B. "KING", "PAWN"
|
||||||
|
private String color; // "WHITE" oder "BLACK"
|
||||||
|
private String unicodeSymbol;
|
||||||
|
|
||||||
|
public PieceDTO(String type, String color, String unicodeSymbol) {
|
||||||
|
this.type = type;
|
||||||
|
this.color = color;
|
||||||
|
this.unicodeSymbol = unicodeSymbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getColor() {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUnicodeSymbol() {
|
||||||
|
return unicodeSymbol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package de.hs_mannheim.informatik.chess.gui;
|
package de.hs_mannheim.informatik.chess.view;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
|
@ -14,11 +14,14 @@ import javax.swing.JOptionPane;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
|
|
||||||
public class Gui {
|
import de.hs_mannheim.informatik.chess.model.BoardDTO;
|
||||||
|
import de.hs_mannheim.informatik.chess.model.PieceDTO;
|
||||||
|
|
||||||
|
public class GameGui {
|
||||||
|
|
||||||
private JLabel[][] fields = new JLabel[8][8];
|
private JLabel[][] fields = new JLabel[8][8];
|
||||||
|
|
||||||
public Gui(){
|
public GameGui(){
|
||||||
mainFrame();
|
mainFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,13 +114,20 @@ public class Gui {
|
||||||
return fields[row][col];
|
return fields[row][col];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateBoard(String[][] unicodeBoard) {
|
public void updateBoard(BoardDTO boardDTO) {
|
||||||
|
PieceDTO[][] board = boardDTO.getBoard();
|
||||||
for (int row = 0; row < 8; row++) {
|
for (int row = 0; row < 8; row++) {
|
||||||
for (int col = 0; col < 8; col++) {
|
for (int col = 0; col < 8; col++) {
|
||||||
fields[row][col].setText(unicodeBoard[row][col]);
|
PieceDTO piece = board[row][col];
|
||||||
|
if (piece != null) {
|
||||||
|
fields[row][col].setText(piece.getUnicodeSymbol());
|
||||||
|
} else {
|
||||||
|
fields[row][col].setText("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void displayMessage(String msg) {
|
public void displayMessage(String msg) {
|
||||||
JOptionPane.showMessageDialog(null, msg);
|
JOptionPane.showMessageDialog(null, msg);
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
package de.hs_mannheim.informatik.chess.view;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
public class MainGui {
|
||||||
|
|
||||||
|
private JFrame frame;
|
||||||
|
private Runnable onStartGame;
|
||||||
|
|
||||||
|
public MainGui(Runnable onStartGame) {
|
||||||
|
this.onStartGame = onStartGame;
|
||||||
|
|
||||||
|
frame = new JFrame("Chess - Hauptmenü");
|
||||||
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
frame.setSize(1600, 1000);
|
||||||
|
frame.setLocationRelativeTo(null);
|
||||||
|
|
||||||
|
//Haupt-Panel mit GridBagLayout
|
||||||
|
JPanel mainPanel = new JPanel(new GridBagLayout());
|
||||||
|
mainPanel.setBackground(new Color(0xe0e1dd));
|
||||||
|
|
||||||
|
GridBagConstraints gbc = new GridBagConstraints();
|
||||||
|
gbc.gridx = 0;
|
||||||
|
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
gbc.insets = new Insets(15, 0, 15, 0);
|
||||||
|
|
||||||
|
//Title
|
||||||
|
JLabel title = new JLabel("Chess", SwingConstants.CENTER);
|
||||||
|
title.setFont(new Font("Serif", Font.BOLD, 150));
|
||||||
|
title.setForeground(new Color(0x1b263b));
|
||||||
|
gbc.gridy = 0;
|
||||||
|
gbc.ipady = 50;
|
||||||
|
mainPanel.add(title, gbc);
|
||||||
|
|
||||||
|
//Abstand nach unten
|
||||||
|
gbc.gridy = 1;
|
||||||
|
gbc.ipady = 15;
|
||||||
|
mainPanel.add(Box.createRigidArea(new Dimension(0, 20)), gbc);
|
||||||
|
|
||||||
|
//Buttons
|
||||||
|
JButton btnMode1 = new JButton("Normal Mode");
|
||||||
|
JButton btnMode2 = new JButton("Mode 2 (coming soon)");
|
||||||
|
JButton btnMode3 = new JButton("Mode 3 (coming soon)");
|
||||||
|
|
||||||
|
styleButton(btnMode1);
|
||||||
|
styleButton(btnMode2);
|
||||||
|
styleButton(btnMode3);
|
||||||
|
|
||||||
|
gbc.gridy = 2; gbc.ipady = 25;
|
||||||
|
mainPanel.add(btnMode1, gbc);
|
||||||
|
|
||||||
|
gbc.gridy = 3;
|
||||||
|
mainPanel.add(btnMode2, gbc);
|
||||||
|
|
||||||
|
gbc.gridy = 4;
|
||||||
|
mainPanel.add(btnMode3, gbc);
|
||||||
|
|
||||||
|
frame.add(mainPanel);
|
||||||
|
frame.setVisible(true);
|
||||||
|
|
||||||
|
//Button ActionListener für "Normal Modus"
|
||||||
|
btnMode1.addActionListener(e -> {
|
||||||
|
frame.dispose(); // Hauptmenü schließen
|
||||||
|
onStartGame.run(); // **Ruft den Callback auf**
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper für Button Styling
|
||||||
|
private void styleButton(JButton btn) {
|
||||||
|
btn.setFont(new Font("Serif", Font.BOLD, 30));
|
||||||
|
btn.setBackground(new Color(0x778da9));
|
||||||
|
btn.setForeground(Color.WHITE);
|
||||||
|
btn.setFocusPainted(false);
|
||||||
|
btn.setBorder(BorderFactory.createEmptyBorder(10, 40, 10, 40));
|
||||||
|
btn.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue