merge the new developmentbranch into the test branch #39

Closed
j23f0712 wants to merge 431 commits from development2 into dev/test
21 changed files with 73 additions and 149 deletions
Showing only changes of commit 82234a7ff9 - Show all commits

View File

@@ -1,12 +1,14 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="MdgaApp" type="Application" factoryName="Application" singleton="false" nameIsGenerated="true">
<option name="ALTERNATIVE_JRE_PATH" value="temurin-20" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
<option name="MAIN_CLASS_NAME" value="pp.mdga.client.MdgaApp" />
<module name="Projekte.mdga.client.main" />
<option name="VM_PARAMETERS" value="-Djava.util.logging.config.file=logging.properties -ea" />
<option name="WORKING_DIRECTORY" value="$MODULE_WORKING_DIR$" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="pp.mdga.client.board.Outline.*" />
<option name="PATTERN" value="pp.mdga.client.board.outline.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>

File diff suppressed because one or more lines are too long

View File

@@ -8,11 +8,11 @@
public enum Asset {
bigTent,
cardStack,
cir("Models/cir/cir_newOrigin.obj"),
heer("Models/heer/heer_newOrigin.obj"),
cir,
heer,
jet,
lw("Models/lw/lw_newOrigin.obj"),
marine("Models/marine/marine_newOrigin.obj"),
lw,
marine,
node_home_blue("Models/node_home/node_home.j3o", "Models/node_home/node_home_blue_diff.png"),
node_wait_blue("Models/node_home/node_home.j3o", "Models/node_home/node_home_blue_diff.png"),
node_home_black("Models/node_home/node_home.j3o", "Models/node_home/node_home_black_diff.png"),
@@ -28,29 +28,29 @@ public enum Asset {
ship(0.8f),
smallTent,
tank,
world("Models/world_new/world_export_new.obj", "Models/world_new/world_new_diff.png", 1.2f),
shield_ring("Models/shield_ring/shield_ring.obj", null),
tree_small("Models/tree_small/tree_small.obj", "Models/tree_small/tree_small_diff.png"),
tree_big("Models/tree_big/tree_big.obj", "Models/tree_big/tree_big_diff.png"),
turboCard("Models/turboCard/turboCard.obj", "Models/turboCard/turboCard_diff.png"),
turboSymbol("Models/turboCard/turboSymbol.obj", "Models/turboCard/turboCard_diff.png"),
swapCard("Models/swapCard/swapCard.obj", "Models/swapCard/swapCard_diff.png"),
swapSymbol("Models/swapCard/swapSymbol.obj", "Models/swapCard/swapCard_diff.png"),
shieldCard("Models/shieldCard/shieldCard.obj", "Models/shieldCard/shieldCard_diff.png"),
shieldSymbol("Models/shieldCard/shieldSymbol.obj", "Models/shieldCard/shieldCard_diff.png"),
dice("Models/dice/dice.obj", "Models/dice/dice_diff.jpeg")
world(1.2f),
shieldRing("Models/shieldRing/shieldRing.j3o", null),
treeSmall,
treeBig,
turboCard,
turboSymbol("Models/turboCard/turboSymbol.j3o", "Models/turboCard/turboCard_diff.j3o"),
swapCard,
swapSymbol("Models/swapCard/swapSymbol.j3o", "Models/swapCard/swapCard_diff.j3o"),
shieldCard,
shieldSymbol("Models/shieldCard/shieldSymbol.j3o", "Models/shieldCard/shieldCard_diff.j3o"),
dice
;
private final String modelPath;
private final String diffPath;
private final float size;
private static final String root = "Models/";
private static final String ROOT = "Models/";
/**
* Default constructor. Initializes modelPath and diffPath based on the enum name and sets default size to 1.0.
*/
Asset() {
String folderFileName = "./" + root + name() + "/" + name();
String folderFileName = "./" + ROOT + name() + "/" + name();
this.modelPath = folderFileName + ".j3o";
this.diffPath = folderFileName + "_diff.png";
this.size = 1f;
@@ -74,7 +74,7 @@ public enum Asset {
* @param modelPath Path to the 3D model file.
*/
Asset(String modelPath) {
String folderFileName = "./" + root + name() + "/" + name();
String folderFileName = "./" + ROOT + name() + "/" + name();
this.modelPath = modelPath;
this.diffPath = folderFileName + "_diff.png";;
this.size = 1f;
@@ -86,7 +86,7 @@ public enum Asset {
* @param size Scaling factor for the asset.
*/
Asset(float size) {
String folderFileName = "./" + root + name() + "/" + name();
String folderFileName = "./" + ROOT + name() + "/" + name();
this.modelPath = folderFileName + ".j3o";
this.diffPath = folderFileName + "_diff.png";
this.size = size;

View File

@@ -0,0 +1,48 @@
package pp.mdga.client;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
/**
* An abstract control class that serves as a base for initializing spatial objects
* in jMonkeyEngine. This class overrides the controlUpdate and controlRender methods
* from the AbstractControl class, providing default empty implementations,
* and adds the ability to initialize spatial objects when they are set.
*/
public abstract class InitControl extends AbstractControl {
@Override
protected void controlUpdate(float tpf) {
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
}
/**
* Sets the spatial object to be controlled. This method also initializes the spatial
* if it is being set for the first time.
*
* @param spatial The spatial object to control.
*/
@Override
public void setSpatial(Spatial spatial) {
if (this.spatial == null && spatial != null) {
super.setSpatial(spatial);
initSpatial();
}
}
/**
* Initializes the spatial object. This method can be overridden by subclasses
* to define custom initialization logic for the spatial.
* This method is called automatically when the spatial is set for the first time.
*/
protected void initSpatial() {
// Default empty implementation. Override to add initialization logic.
}
}

View File

@@ -18,8 +18,14 @@
import pp.mdga.client.board.PieceControl;
import pp.mdga.client.gui.CardControl;
import pp.mdga.client.view.GameView;
import pp.mdga.game.BonusCard;
import pp.mdga.game.Color;
import pp.mdga.game.Piece;
import pp.mdga.notification.FinishNotification;
import pp.mdga.notification.MovePieceNotification;
import pp.mdga.notification.SelectableCardsNotification;
import java.util.List;
public class InputSynchronizer {
@@ -112,6 +118,8 @@ else if(boardSelect != null) {
}
if(name.equals("Test") &&isPressed){
if(app.getView() instanceof GameView gameView){
// app.getNotificationSynchronizer().addTestNotification(new FinishNotification(Color.NAVY));
// app.getNotificationSynchronizer().addTestNotification(new MovePieceNotification());
}
}
}
@@ -146,6 +154,7 @@ else if (name.equals("MouseLeft") || name.equals("MouseRight") || name.equals("M
* Detects the hovered piece and updates its hover state.
*/
private <T extends AbstractControl> T checkHover(Camera cam, Node root, Class<T> controlType) {
if(cam == null || root == null || controlType == null) return null;
CollisionResults results = new CollisionResults();
Ray ray = new Ray(cam.getLocation(), getMousePos(cam).subtract(cam.getLocation()).normalize());
root.collideWith(ray, results);
@@ -159,6 +168,7 @@ private <T extends AbstractControl> T checkHover(Camera cam, Node root, Class<T>
* Detects the hovered card and updates its hover state.
*/
private <T extends AbstractControl> T checkHoverOrtho(Camera cam, Node root, Class<T> controlType) {
if(cam == null || root == null || controlType == null) return null;
CollisionResults results = new CollisionResults();
Vector3f mousePos = getMousePos(cam);
mousePos.setZ(cam.getLocation().getZ());
@@ -272,6 +282,10 @@ public float getRotation() {
return (rotationAngle / 2) % 360;
}
public void setRotation(float rotationAngle){
this.rotationAngle = rotationAngle;
}
/**
* Gets the current scroll value.
*

View File

@@ -3,18 +3,22 @@
import com.jme3.app.SimpleApplication;
import com.simsilica.lemur.GuiGlobals;
import pp.mdga.client.acoustic.AcousticHandler;
import pp.mdga.client.animation.AnimationHandler;
import com.jme3.system.AppSettings;
import pp.mdga.client.dialog.JoinDialog;
import pp.mdga.client.view.*;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.prefs.Preferences;
/**
* Main application class for the MdgaApp game.
* This class extends {@link SimpleApplication} and manages the game's lifecycle, states, and main components.
*/
public class MdgaApp extends SimpleApplication {
/** Handles animations in the application. */
private AnimationHandler animationHandler;
private static Preferences prefs = Preferences.userNodeForPackage(JoinDialog.class);
/** Handles acoustic effects and state-based sounds. */
private AcousticHandler acousticHandler;
@@ -35,7 +39,7 @@ public class MdgaApp extends SimpleApplication {
private MdgaState state = null;
/** Scale for rendering images. */
private static final float IMAGE_SCALE = 1.5f;
private float imageScale = prefs.getInt("scale", 1);
/** The main menu view. */
private MdgaView mainView;
@@ -49,6 +53,18 @@ public class MdgaApp extends SimpleApplication {
/** The ceremony view. */
private MdgaView ceremonyView;
/** The client game logic. */
private final ClientGameLogic clientGameLogic;
private ExecutorService executor;
private ServerConnection networkConnection;
private MdgaApp() {
networkConnection = new NetworkSupport(this);
this.clientGameLogic = new ClientGameLogic(networkConnection);
}
/**
* Main entry point for the application.
* Configures settings and starts the application.
@@ -58,9 +74,9 @@ public class MdgaApp extends SimpleApplication {
public static void main(String[] args) {
AppSettings settings = new AppSettings(true);
settings.setSamples(128);
settings.setWidth(prefs.getInt("width", 1280));
settings.setHeight(prefs.getInt("height", 720));
settings.setCenterWindow(true);
settings.setWidth(1920);
settings.setHeight(1080);
settings.setVSync(false);
MdgaApp app = new MdgaApp();
@@ -80,7 +96,6 @@ public void simpleInitApp() {
flyCam.setEnabled(false);
animationHandler = new AnimationHandler(this);
acousticHandler = new AcousticHandler(this);
notificationSynchronizer = new NotificationSynchronizer(this);
inputSynchronizer = new InputSynchronizer(this);
@@ -141,14 +156,6 @@ public void enter(MdgaState state) {
view.enter();
}
/**
* Gets the animation handler.
*
* @return the {@link AnimationHandler} instance
*/
public AnimationHandler getAnimationHandler() {
return animationHandler;
}
/**
* Gets the acoustic handler.
@@ -174,7 +181,7 @@ public MdgaState getState() {
* @return the image scale as a float
*/
public float getImageScale() {
return IMAGE_SCALE;
return imageScale;
}
/**
@@ -219,5 +226,48 @@ public NotificationSynchronizer getNotificationSynchronizer() {
public void setup() {
}
public ClientGameLogic getGameLogic() {
return clientGameLogic;
}
public ExecutorService getExecutor() {
if (this.executor == null) {
this.executor = Executors.newCachedThreadPool();
}
return this.executor;
}
public ServerConnection getNetworkSupport(){
return networkConnection;
}
public void updateResolution(int width, int height, float imageFactor) {
prefs.putInt("width", width);
prefs.putInt("height", height);
prefs.putFloat("scale", imageFactor);
try {
restartApp();
} catch (Exception e) {
//nothing
}
}
public static void restartApp() throws IOException {
String javaBin = System.getProperty("java.home") + "/bin/java";
String classPath = System.getProperty("java.class.path");
String className = System.getProperty("sun.java.command");
ProcessBuilder builder = new ProcessBuilder(
javaBin, "-cp", classPath, className
);
builder.start();
System.exit(0);
}
}

View File

@@ -1,6 +1,7 @@
package pp.mdga.client;
import pp.mdga.client.acoustic.MdgaSound;
import pp.mdga.client.server.MdgaServer;
import pp.mdga.client.view.CeremonyView;
import pp.mdga.client.view.GameView;
import pp.mdga.client.view.LobbyView;
@@ -12,40 +13,34 @@
import pp.mdga.notification.TskSelectNotification;
import java.util.UUID;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ModelSynchronizer {
private static final Logger LOGGER = Logger.getLogger(ModelSynchronizer.class.getName());
ConsoleHandler handler = new ConsoleHandler();
private MdgaApp app;
private UUID a;
private UUID b;
private BonusCard card;
private boolean swap;
ModelSynchronizer(MdgaApp app) {
this.app = app;
LOGGER.setLevel(Level.INFO);
handler.setLevel(Level.ALL);
LOGGER.addHandler(handler);
LOGGER.setUseParentHandlers(false);
swap = false;
}
private Color testColor;
private int test = 0;
public void animationEnd() {
app.getGameLogic().selectAnimationEnd();
}
public void select(UUID a, UUID b){
if(swap) selectSwap(a,b);
else selectPiece(a);
}
public void selectSwap(UUID a, UUID b) {
// TODO call from somewhere
LOGGER.log(Level.INFO, "selectPiece");
this.a = a;
this.b = b;
@@ -59,6 +54,7 @@ public void selectSwap(UUID a, UUID b) {
}
public void selectPiece(UUID piece) {
// TODO call from somewhere
LOGGER.log(Level.INFO, "selectPiece");
this.a = piece;
@@ -72,6 +68,7 @@ public void selectPiece(UUID piece) {
}
public void selectCard(BonusCard card) {
// TODO call from somewhere
LOGGER.log(Level.INFO, "selectCard");
this.card = card;
@@ -90,13 +87,14 @@ public void confirm() {
GameView gameView = (GameView) app.getView();
if(a != null && b != null) {
//swap
selectPiece(a);
selectPiece(b);
gameView.getBoardHandler().clearSelectable();
} else if (a != null) {
//piece
selectPiece(a);
gameView.getBoardHandler().clearSelectable();
} else if (card != null){
//card
selectCard(card);
gameView.getGuiHandler().clearSelectableCards();
} else {
throw new RuntimeException("nothing to confirm");
@@ -106,43 +104,45 @@ public void confirm() {
}
public void selectTsk(Color color) {
LOGGER.log(Level.INFO, "selectTsk: " + color);
app.getGameLogic().selectTsk(color);
}
public void unselectTsk() {
LOGGER.log(Level.INFO, "unselectTsk");
public void unselectTsk(Color color) {
app.getGameLogic().deselectTSK(color);
}
public void rolledDice() {
LOGGER.log(Level.INFO, "rolledDice");
app.getGameLogic().selectDice();
}
public void setName(String name) {
LOGGER.log(Level.INFO, "setName: " + name);
// TODO call from somewhere
LOGGER.log(Level.INFO, "setName: {0}", name);
app.getGameLogic().selectName(name);
}
public void setReady(boolean ready) {
LOGGER.log(Level.INFO, "setReady");
app.getGameLogic().selectReady(ready);
}
public void setHost(int port) {
LOGGER.log(Level.INFO, "setHost: " + port);
app.getGameLogic().selectJoin("");
}
public void setJoin(String ip, int port) {
LOGGER.log(Level.INFO, "setJoin with IP: " + ip + "\nPort: " + port);
app.getGameLogic().selectJoin(ip);
}
public void leave() {
LOGGER.log(Level.INFO, "leave");
app.getGameLogic().selectLeave();
}
public void enter(MdgaState state) {
LOGGER.log(Level.INFO, "enter: " + state);
LOGGER.log(Level.INFO, "enter: {0}", state);
//app.enter(state);
}
public void setSwap(boolean swap){
this.swap = swap;
}
}

View File

@@ -0,0 +1,91 @@
package pp.mdga.client;
import com.jme3.network.*;
import pp.mdga.message.client.ClientMessage;
import pp.mdga.message.server.ServerMessage;
import java.io.IOException;
public class NetworkSupport implements MessageListener<Client>, ClientStateListener, ServerConnection {
private static final System.Logger LOGGER = System.getLogger(NetworkSupport.class.getName());
private final MdgaApp app;
private Client client;
public NetworkSupport(MdgaApp app) {
this.app = app;
}
public MdgaApp getApp() {
return this.app;
}
public boolean isConnected() {
return this.client != null && this.client.isConnected();
}
public void connect() {
if (this.client != null) {
throw new IllegalStateException("trying to join a game again");
} else {
try {
this.initNetwork("localhost", 2345);
} catch (IOException e) {
LOGGER.log(System.Logger.Level.ERROR, "could not connect to server", e);
}
}
}
public void disconnect() {
if (this.client != null) {
this.client.close();
this.client = null;
LOGGER.log(System.Logger.Level.INFO, "client closed");
}
}
public void initNetwork(String host, int port) throws IOException {
if (this.client != null) {
throw new IllegalStateException("trying to join a game again");
} else {
this.client = Network.connectToServer(host, port);
this.client.start();
this.client.addMessageListener(this);
this.client.addClientStateListener(this);
}
}
public void messageReceived(Client client, Message message) {
LOGGER.log(System.Logger.Level.INFO, "message received from server: {0}", new Object[]{message});
if (message instanceof ServerMessage serverMessage) {
this.app.enqueue(() -> serverMessage.accept(this.app.getGameLogic()));
}
}
public void clientConnected(Client client) {
LOGGER.log(System.Logger.Level.INFO, "Client connected: {0}", new Object[]{client});
}
public void clientDisconnected(Client client, ClientStateListener.DisconnectInfo disconnectInfo) {
LOGGER.log(System.Logger.Level.INFO, "Client {0} disconnected: {1}", new Object[]{client, disconnectInfo});
if (this.client != client) {
throw new IllegalArgumentException("parameter value must be client");
} else {
LOGGER.log(System.Logger.Level.INFO, "client still connected: {0}", new Object[]{client.isConnected()});
this.client = null;
this.disconnect();
}
}
@Override
public void send(ClientMessage message) {
LOGGER.log(System.Logger.Level.INFO, "sending {0}", new Object[]{message});
if (this.client == null) {
LOGGER.log(System.Logger.Level.WARNING, "client not connected");
} else {
this.client.send(message);
}
}
}

View File

@@ -21,11 +21,13 @@ public class NotificationSynchronizer {
public void addTestNotification(Notification n) {
notifications.add(n);
handleGame(n);
}
public void update() {
//TODO fetch model notifications
for (Notification n : notifications) {
Notification n = app.getGameLogic().getNotification();
if(n != null) {
switch (app.getState()) {
case MAIN:
handleMain(n);
@@ -43,14 +45,13 @@ public void update() {
throw new RuntimeException("no notification expected: " + n.toString());
}
}
notifications.clear();
}
private void handleMain(Notification notification) {
if (notification instanceof LobbyDialogNotification) {
app.enter(MdgaState.LOBBY);
} else {
throw new RuntimeException("notification not expected: " + notification.toString());
throw new RuntimeException("notification not expected: ");
}
}
@@ -59,11 +60,12 @@ private void handleLobby(Notification notification) {
if (notification instanceof TskSelectNotification n) {
lobbyView.setTaken(n.getColor(), true, n.isSelf(), n.getName());
lobbyView.setTaken(n.getColor(), true, false, n.getName());
} else if (notification instanceof StartDialogNotification) {
app.enter(MdgaState.MAIN);
} else if (notification instanceof TskUnselectNotification n) {
lobbyView.setTaken(n.getColor(), false, false, null);
//} else if(notification instanceof LobbyReadyNotification lobbyReadyNotification) {
//lobbyView.setReady(lobbyReadyNotification.getColor(), lobbyReadyNotification.isReady()):
} else if(notification instanceof LobbyReadyNotification lobbyReadyNotification) {
lobbyView.setReady(lobbyReadyNotification.getColor(), lobbyReadyNotification.isReady());
} else if (notification instanceof GameNotification n) {
app.enter(MdgaState.GAME);
((GameView) app.getView()).setOwnColor(n.getOwnColor());
@@ -76,11 +78,13 @@ private void handleGame(Notification notification) {
GameView gameView = (GameView) app.getView();
GuiHandler guiHandler = gameView.getGuiHandler();
BoardHandler boardHandler = gameView.getBoardHandler();
ModelSynchronizer modelSynchronizer = app.getModelSynchronize();
if (notification instanceof AcquireCardNotification) {
// Handle AcquireCardNotification
if (notification instanceof AcquireCardNotification n) {
guiHandler.addCard(n.getBonusCard());
} else if (notification instanceof ActivePlayerNotification n) {
gameView.getGuiHandler().setActivePlayer(n.getColor());
boardHandler.showDice(n.getColor());
} else if (notification instanceof CeremonyNotification ceremonyNotification) {
app.enter(MdgaState.CEREMONY);
CeremonyView ceremonyView = (CeremonyView) app.getView();
@@ -110,26 +114,25 @@ private void handleGame(Notification notification) {
}
} else if (notification instanceof DiceNowNotification) {
guiHandler.showDice();
} else if (notification instanceof DicingNotification n) {
//TODO ???
} else if (notification instanceof DrawCardNotification n) {
guiHandler.drawCard(n.getColor());
} else if (notification instanceof HomeMoveNotification home) {
boardHandler.moveHomePiece(home.getPieceId(), home.getHomeIndex());
boardHandler.movePieceHomeAnim(home.getPieceId(), home.getHomeIndex());
guiHandler.hideText();
} else if (notification instanceof InterruptNotification) {
app.enter(MdgaState.LOBBY);
} else if (notification instanceof MovePieceNotification n) {
if(n.isMoveStart()) {
//StartMove
boardHandler.movePieceStart(n.getPiece(), n.getMoveIndex());
boardHandler.movePieceStartAnim(n.getPiece(), n.getMoveIndex());
}
else {
//InfieldMove
boardHandler.movePiece(n.getPiece(), n.getStartIndex(), n.getMoveIndex());
boardHandler.movePieceAnim(n.getPiece(), n.getStartIndex(), n.getMoveIndex());
}
guiHandler.hideText();
} else if (notification instanceof ThrowPieceNotification n) {
boardHandler.throwPiece(n.getPieceId());
boardHandler.throwPieceAnim(n.getPieceId());
} else if (notification instanceof NoShieldNotification n) {
boardHandler.unshieldPiece(n.getPieceId());
} else if (notification instanceof PlayCardNotification n) {
@@ -146,11 +149,12 @@ private void handleGame(Notification notification) {
//ignore
} else if (notification instanceof RollDiceNotification n) {
if(n.getColor() == gameView.getOwnColor()){
//guiHandler.rollDice(n.getEyes(), n.isTurbo() ? n.getMultiplier() : -1);
guiHandler.rollDice(n.getEyes(), n.isTurbo() ? n.getMultiplier() : -1);
}
else {
//if (n.isTurbo()) guiHandler.showRolledDiceMult(n.getEyes(), n.getMultiplier(), n.getColor());
//else guiHandler.showRolledDice(n.getEyes(), n.getColor());
boardHandler.hideDice();
if (n.isTurbo()) guiHandler.showRolledDiceMult(n.getEyes(), n.getMultiplier(), n.getColor());
else guiHandler.showRolledDice(n.getEyes(), n.getColor());
}
} else if (notification instanceof SelectableCardsNotification n) {
guiHandler.setSelectableCards(n.getCards());
@@ -161,18 +165,23 @@ private void handleGame(Notification notification) {
} else if (notification instanceof StartDialogNotification) {
app.enter(MdgaState.MAIN);
} else if (notification instanceof SwapPieceNotification n) {
boardHandler.swapPieces(n.getFirstPiece(), n.getSecondPiece());
// boardHandler.swapPieces(n.getFirstPiece(), n.getSecondPiece());
guiHandler.swap();
} else if (notification instanceof WaitMoveNotification) {
//TODO ???
} else if (notification instanceof SelectableMoveNotification n) {
boardHandler.outlineMove(n.getPieces(), n.getMoveIndexe(), n.getHomeMoves());
boardHandler.outlineMove(n.getPieces(), n.getMoveIndices(), n.getHomeMoves());
modelSynchronizer.setSwap(false);
} else if (notification instanceof SelectableSwapNotification n) {
boardHandler.outlineSwap(n.getOwnPieces(), n.getEnemyPieces());
// } //else if (notification instanceof SelectableShieldNotification n) {
// boardHandler.outlineShield(n.getOwnPieces());
//} else if (notification instanceof TurboActiveNotification){
// guiHandler.turbo();
modelSynchronizer.setSwap(true);
} else if (notification instanceof SelectableShieldNotification n) {
boardHandler.outlineShield(n.getPieces());
modelSynchronizer.setSwap(false);
} else if (notification instanceof TurboActiveNotification){
guiHandler.turbo();
} else if (notification instanceof FinishNotification n){
guiHandler.finish(n.getColorFinished());
} else {
throw new RuntimeException("notification not expected: " + notification.toString());
}

View File

@@ -20,7 +20,7 @@ enum MusicAsset {
private final String path;
private final boolean loop;
private final float subVolume;
private static final String root = "Music/";
private static final String ROOT = "Music/";
/**
* Constructs a new MusicAsset object with the specified name and sub-volume.
@@ -30,7 +30,7 @@ enum MusicAsset {
* @param subVolume A relative volume that modifies the base volume of the track (typically a percentage).
*/
MusicAsset(String name, float subVolume) {
this.path = root + name;
this.path = ROOT + name;
this.loop = false;
this.subVolume = subVolume;
}
@@ -43,7 +43,7 @@ enum MusicAsset {
* @param subVolume A relative volume that modifies the base volume of the track (typically a percentage).
*/
MusicAsset(String name, boolean loop, float subVolume) {
this.path = root + name;
this.path = ROOT + name;
this.loop = loop;
this.subVolume = subVolume;
}

View File

@@ -1,10 +0,0 @@
package pp.mdga.client.animation;
abstract class Animation {
abstract void play();
abstract void stop();
abstract boolean isOver();
}

View File

@@ -1,29 +0,0 @@
package pp.mdga.client.animation;
import pp.mdga.client.MdgaApp;
public class AnimationHandler {
private MdgaApp app;
private Animation animation = null;
public AnimationHandler(MdgaApp app) {
this.app = app;
}
public void playAnimation(MdgaAnimation type) {
}
public void update() {
if (null == animation) {
return;
}
if (animation.isOver()) {
animation = null;
//trigger next state in model
}
}
}

View File

@@ -1,18 +0,0 @@
package pp.mdga.client.animation;
class EmptyAnimation extends Animation {
@Override
void play() {
//nothing
}
@Override
void stop() {
//nothing
}
@Override
boolean isOver() {
return true;
}
}

View File

@@ -1,4 +0,0 @@
package pp.mdga.client.animation;
public enum MdgaAnimation {
}

View File

@@ -0,0 +1,109 @@
package pp.mdga.client.animation;
import com.jme3.math.Vector3f;
import pp.mdga.client.InitControl;
/**
* A control that smoothly moves a spatial from an initial position to an end position
* using a quadratic interpolation, with the option to perform an action after the movement is complete.
* The movement path includes an intermediate "middle" position at a specified height.
*
* <p>Movement speed can be adjusted by modifying the MOVE_SPEED constant. The movement easing follows
* an ease-in-out curve to create a smooth start and stop effect.
* </p>
*/
public class MoveControl extends InitControl {
private boolean moving;
private final Vector3f initPos;
private final Vector3f endPos;
private final Vector3f middlePos;
private final static float HEIGHT = 2;
private final static float MOVE_SPEED = 1f;
private float progress = 0;
private final Runnable actionAfter;
/**
* Creates a new MoveControl with specified initial and end positions, and an action to run after the movement.
* The movement follows a path with a midpoint at a fixed height.
*
* @param initPos The starting position of the spatial.
* @param endPos The target position of the spatial.
* @param actionAfter A Runnable that will be executed after the movement finishes.
*/
public MoveControl(Vector3f initPos, Vector3f endPos, Runnable actionAfter){
moving = false;
this.initPos = initPos;
this.endPos = endPos;
middlePos = new Vector3f(
(initPos.x + endPos.x) / 2,
(initPos.y + endPos.y) / 2,
HEIGHT
);
this.actionAfter = actionAfter;
}
/**
* Initializes the movement by resetting the progress and setting the moving flag to true.
* This is called automatically when the spatial is set.
*/
@Override
protected void initSpatial() {
moving = true;
progress = 0;
}
/**
* Updates the movement of the spatial by interpolating its position along the defined path.
* The movement is smoothed using an easing function.
* Once the movement reaches the target, the {@link #end()} method is called to finish the movement.
*
* @param tpf Time per frame, the time elapsed since the last frame.
*/
@Override
protected void controlUpdate(float tpf) {
if(!moving) return;
progress += tpf * MOVE_SPEED;
if(progress > 1) progress = 1;
spatial.setLocalTranslation(quadInt(initPos,middlePos,endPos, easeInOut(progress)));
if(progress == 1) end();
}
/**
* Ends the movement by stopping the interpolation, running the action after the movement,
* and removing this control from the spatial.
*/
private void end(){
moving = false;
actionAfter.run();
spatial.removeControl(this);
}
/**
* Performs quadratic interpolation between three points.
*
* @param p1 The initial point.
* @param p2 The middle point.
* @param p3 The final point.
* @param t The interpolation parameter (0 <= t <= 1).
* @return The interpolated point.
*/
private Vector3f quadInt(Vector3f p1, Vector3f p2, Vector3f p3, float t) {
// Quadratic interpolation: (1-t)^2 * p1 + 2 * (1-t) * t * p2 + t^2 * p3
float oneMinusT = 1 - t;
return p1.mult(oneMinusT * oneMinusT)
.add(p2.mult(2 * oneMinusT * t))
.add(p3.mult(t * t));
}
/**
* A smooth ease-in-out function for interpolation.
* It accelerates and decelerates the interpolation for a smoother effect.
*
* @param x The interpolation parameter (0 <= x <= 1).
* @return The adjusted interpolation value.
*/
private float easeInOut(float x){
return x < 0.5 ? 4 * x * x * x : (float) (1 - Math.pow(-2 * x + 2, 3) / 2);
}
}

View File

@@ -0,0 +1,213 @@
package pp.mdga.client.animation;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import pp.mdga.client.InitControl;
import pp.mdga.game.BonusCard;
/**
* A control that manages the animation of symbols representing different bonus card states.
* The symbol can animate with zoom, rotation, and translation effects based on the state of the bonus card.
*
* <p>The control supports three main states: SHIELD, SWAP, and TURBO. Each state has its own specific animation logic:
* <ul>
* <li>SHIELD: Zooms in and out, with a scaling effect.</li>
* <li>SWAP: Rotates the symbol 360 degrees.</li>
* <li>TURBO: Moves the symbol along the Y-axis with a zoom effect.</li>
* </ul>
* </p>
*/
public class SymbolControl extends InitControl {
private boolean zoomingIn = false;
private boolean zoomingOut = false;
private float zoomSpeed = 1f;
private float zoomFactor = 3f;
private float progress = 0;
private BonusCard state;
private float rotationSpeed = 0.8f;
private Quaternion initialRotation = null;
private float y = 5;
/**
* Updates the symbol animation based on the current bonus card state.
* The method calls the corresponding update method for each state (SHIELD, SWAP, TURBO).
*
* @param tpf Time per frame, the time elapsed since the last frame.
*/
@Override
protected void controlUpdate(float tpf) {
if (state == null) return;
switch (state) {
case SHIELD -> shieldUpdate(tpf);
case SWAP -> swapUpdate(tpf);
case TURBO -> turboUpdate(tpf);
case HIDDEN -> throw new RuntimeException("forbidden state");
}
}
/**
* Updates the symbol when the state is SHIELD. The symbol zooms in and then zooms out.
* When the zooming out finishes, the symbol is removed from the parent spatial.
*
* @param tpf Time per frame, the time elapsed since the last frame.
*/
private void shieldUpdate(float tpf) {
if (zoomingIn) {
progress += tpf * zoomSpeed;
if (progress > 1) progress = 1;
spatial.setLocalScale(lerp(0, zoomFactor, easeOut(progress)));
if (progress >= 1) {
zoomingIn = false;
zoomingOut = true;
progress = 0;
}
} else if (zoomingOut) {
progress += tpf * zoomSpeed;
spatial.setLocalScale(lerp(zoomFactor, 0, easeIn(progress)));
if (progress > 1) {
zoomingIn = false;
spatial.removeFromParent();
state = null;
progress = 0;
}
}
}
/**
* Updates the symbol when the state is SWAP. The symbol rotates 360 degrees.
* After the rotation finishes, the symbol is removed from the parent spatial.
*
* @param tpf Time per frame, the time elapsed since the last frame.
*/
private void swapUpdate(float tpf) {
if (initialRotation == null) {
initialRotation = spatial.getLocalRotation().clone();
}
progress += tpf * rotationSpeed;
if (progress < 0) return;
float angle = lerp(0, 360, easeInOut(progress));
Quaternion newRotation = new Quaternion();
newRotation.fromAngleAxis((float) Math.toRadians(angle), new Vector3f(0, 1, 0));
spatial.setLocalRotation(initialRotation.mult(newRotation));
if (progress >= 1.2f) {
state = null;
initialRotation = null;
progress = 0;
spatial.removeFromParent();
}
}
/**
* Updates the symbol when the state is TURBO. The symbol moves along the Y-axis with a zoom effect.
* After the movement finishes, the symbol is removed from the parent spatial.
*
* @param tpf Time per frame, the time elapsed since the last frame.
*/
private void turboUpdate(float tpf) {
if (zoomingIn) {
progress += tpf * zoomSpeed;
if (progress > 1) progress = 1;
float y = lerp(-this.y, 0, easeOut(progress));
spatial.setLocalTranslation(0, y, 0);
if (progress >= 1) {
zoomingIn = false;
zoomingOut = true;
progress = 0;
}
} else if (zoomingOut) {
progress += tpf * zoomSpeed;
float y = lerp(0, this.y, easeIn(progress));
spatial.setLocalTranslation(0, y, 0);
if (progress > 1) {
zoomingIn = false;
spatial.removeFromParent();
state = null;
}
}
}
/**
* Starts the SHIELD animation by zooming the symbol in and out.
* The symbol will first zoom in and then zoom out, and will be removed from the parent spatial once done.
*/
public void shield() {
if (state != null) throw new RuntimeException("another state is avtive");
state = BonusCard.SHIELD;
zoomingIn = true;
zoomingOut = false;
progress = 0;
spatial.setLocalScale(1f);
}
/**
* Starts the SWAP animation by rotating the symbol 360 degrees.
* The symbol will rotate once and then be removed from the parent spatial.
*/
public void swap() {
if (state != null) throw new RuntimeException("another state is avtive");
spatial.setLocalScale(3);
state = BonusCard.SWAP;
progress = -0.2f;
}
/**
* Starts the TURBO animation by moving the symbol along the Y-axis.
* The symbol will move upwards and then return to its initial position.
*/
public void turbo() {
if (state != null) throw new RuntimeException("another state is avtive");
spatial.setLocalScale(2);
state = BonusCard.TURBO;
zoomingIn = true;
zoomingOut = false;
progress = 0;
}
/**
* Performs linear interpolation between two values.
*
* @param start The starting value.
* @param end The target value.
* @param t The interpolation parameter (0 <= t <= 1).
* @return The interpolated value.
*/
private static float lerp(float start, float end, float t) {
return (1 - t) * start + t * end;
}
/**
* Ease-out function for smoothing the interpolation.
*
* @param t The interpolation parameter (0 <= t <= 1).
* @return The eased value.
*/
private static float easeOut(float t) {
return (float) Math.sqrt(1 - Math.pow(t - 1, 2));
}
/**
* Ease-in-out function for smoothing the interpolation.
*
* @param t The interpolation parameter (0 <= t <= 1).
* @return The eased value.
*/
private float easeInOut(float t) {
if (t > 1) t = 1;
return (float) -(Math.cos(Math.PI * t) - 1) / 2;
}
/**
* Ease-in function for smoothing the interpolation.
*
* @param t The interpolation parameter (0 <= t <= 1).
* @return The eased value.
*/
private float easeIn(float t) {
return t * t * t * t;
}
}

View File

@@ -0,0 +1,110 @@
package pp.mdga.client.animation;
import pp.mdga.client.InitControl;
/**
* A control that applies a zoom effect to a spatial, smoothly scaling it in and out.
* The zoom effect can be customized with speed and scaling factor.
*
* <p>The control supports zooming in and out with ease-in and ease-out transitions.
* It starts by zooming in, and once complete, it zooms out, eventually removing the spatial from its parent when the animation ends.</p>
*/
public class ZoomControl extends InitControl {
private boolean zoomingIn = false;
private boolean zoomingOut = false;
private float progress = 0;
private float zoomSpeed = 1f;
private float zoomFactor = 1f;
/**
* Constructs a new ZoomControl with the default zoom speed.
*/
public ZoomControl() {
}
/**
* Constructs a new ZoomControl with a specified zoom speed.
*
* @param speed The speed at which the zoom effect occurs.
*/
public ZoomControl(float speed) {
zoomSpeed = speed;
}
/**
* Initializes the spatial for the zoom effect. This method is called when the control is added to the spatial.
* It sets the zooming state to zooming in.
*/
@Override
protected void initSpatial() {
zoomingIn = true;
}
/**
* Updates the zoom effect over time, either zooming in or zooming out.
*
* @param tpf Time per frame, the time elapsed since the last frame.
*/
@Override
protected void controlUpdate(float tpf) {
if (zoomingIn) {
progress += tpf * zoomSpeed;
if (progress > 1) progress = 1;
spatial.setLocalScale(lerp(0, zoomFactor, easeOut(progress)));
if (progress >= 1) {
zoomingIn = false;
zoomingOut = true;
progress = 0;
}
} else if (zoomingOut) {
progress += tpf * zoomSpeed;
spatial.setLocalScale(lerp(zoomFactor, 0, easeIn(progress)));
if (progress > 1) {
zoomingOut = false;
end();
}
}
}
/**
* Ends the zoom animation by removing the spatial from its parent and the control from the spatial.
*/
private void end() {
spatial.removeFromParent();
spatial.removeControl(this);
}
/**
* Performs linear interpolation between two values.
*
* @param start The starting value.
* @param end The target value.
* @param t The interpolation parameter (0 <= t <= 1).
* @return The interpolated value.
*/
private static float lerp(float start, float end, float t) {
return (1 - t) * start + t * end;
}
/**
* Ease-out function for smoothing the zoom-in transition.
*
* @param x The interpolation parameter (0 <= x <= 1).
* @return The eased value.
*/
private float easeOut(float x) {
return x == 1 ? 1 : (float) (1 - Math.pow(2, -10 * x));
}
/**
* Ease-in function for smoothing the zoom-out transition.
*
* @param x The interpolation parameter (0 <= x <= 1).
* @return The eased value.
*/
private float easeIn(float x) {
return x == 0 ? 0 : (float) Math.pow(2, 10 * x - 10);
}
}

View File

@@ -2,4 +2,7 @@
import pp.mdga.client.Asset;
/**
* Record for holding Asset information
*/
record AssetOnMap(Asset asset, int x, int y, float rot) {}

View File

@@ -9,34 +9,45 @@
import com.jme3.scene.control.AbstractControl;
import pp.mdga.client.Asset;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.animation.MoveControl;
import pp.mdga.client.gui.DiceControl;
import pp.mdga.game.Color;
import pp.mdga.game.Piece;
import java.util.*;
/**
* BoardHandler is responsible for managing the game board in the MDGA client, including handling
* the initialization, movement, and management of game pieces and assets.
* It works closely with the MdgaApp to create and manipulate 3D models of assets, track player pieces,
* and manage movement across the game board.
*/
public class BoardHandler {
// Constants defining the grid size and elevation of the board
private static final float GRID_SIZE = 1.72f;
private static final float GRID_ELEVATION = 0.0f;
private static final String MAP_NAME = "Maps/map.mdga";
// The main application instance for accessing game assets and logic
private final MdgaApp app;
// Collection of in-game assets and board elements
private ArrayList<NodeControl> infield;
private Map<UUID, PieceControl> pieces;
private Map<Color, List<AssetOnMap>> colorAssetsMap;
private Map<Color, List<NodeControl>> homeNodesMap;
private Map<Color, List<NodeControl>> waitingNodesMap;
private Map<Color, List<PieceControl>> waitingPiecesMap;
private Map<Color, Map<UUID, NodeControl>> waitingNodes;
private Map<UUID, Color> pieceColor;
private Node rootNodeBoard;
private final Node rootNodeBoard;
private final Node rootNode;
private final FilterPostProcessor fpp;
private boolean isInitialised;
// Flags and lists for handling piece selection and movement
private List<PieceControl> selectableOwnPieces;
private List<PieceControl> selectableEnemyPieces;
private List<NodeControl> outlineNodes;
@@ -44,6 +55,14 @@ public class BoardHandler {
private PieceControl selectedEnemyPiece;
private DiceControl diceControl;
/**
* Creates a new BoardHandler.
*
* @param app The main application instance
* @param rootNode The root node where the board will be attached
* @param fpp The post-processor for effects like shadows or filters
* @throws RuntimeException if the app is null
*/
public BoardHandler(MdgaApp app, Node rootNode, FilterPostProcessor fpp) {
if(app == null) throw new RuntimeException("app is null");
@@ -54,6 +73,9 @@ public BoardHandler(MdgaApp app, Node rootNode, FilterPostProcessor fpp) {
isInitialised = false;
}
/**
* Initializes the game board by setting up the pieces and nodes.
*/
public void init() {
isInitialised = true;
selectableOwnPieces = new ArrayList<>();
@@ -65,18 +87,30 @@ public void init() {
rootNode.attachChild(rootNodeBoard);
}
/**
* Shuts down the board handler by detaching all board-related nodes and clearing selected pieces.
*/
public void shutdown(){
clearSelectable();
isInitialised = false;
initMap();
rootNode.detachChild(rootNodeBoard);
}
/**
* Adds an asset to the map of player assets, ensuring that the player does not have too many assets.
*
* @param col The color of the player
* @param assetOnMap The asset to be added
* @throws RuntimeException if there are too many assets for the player
*/
private void addFigureToPlayerMap(Color col, AssetOnMap assetOnMap) {
List<AssetOnMap> inMap = addItemToMapList(colorAssetsMap, col, assetOnMap);
if (inMap.size() > 4) throw new RuntimeException("to many assets for " + col);
}
/**
* Initializes the map with the assets loaded from the map file and corresponding nodes.
*/
private void initMap() {
pieces = new HashMap<>();
colorAssetsMap = new HashMap<>();
@@ -87,6 +121,12 @@ private void initMap() {
pieceColor = new HashMap<>();
diceControl = new DiceControl(app.getAssetManager());
diceControl.create(new Vector3f(0,0,0), 0.7f, true);
waitingNodes = new HashMap<>();
waitingNodes.put(Color.AIRFORCE, new HashMap<>());
waitingNodes.put(Color.ARMY, new HashMap<>());
waitingNodes.put(Color.NAVY, new HashMap<>());
waitingNodes.put(Color.CYBER, new HashMap<>());
List<AssetOnMap> assetOnMaps = MapLoader.loadMap(MAP_NAME);
@@ -112,6 +152,13 @@ private void initMap() {
}
}
/**
* Converts an asset to its corresponding color.
*
* @param asset The asset to be converted
* @return The color associated with the asset
* @throws RuntimeException if the asset is invalid
*/
private Color assetToColor(Asset asset) {
return switch (asset) {
case lw -> Color.AIRFORCE;
@@ -122,6 +169,14 @@ private Color assetToColor(Asset asset) {
};
}
/**
* Creates a 3D model of an asset and adds it to the board.
*
* @param asset The asset to be displayed
* @param pos The position of the asset on the board
* @param rot The rotation of the asset
* @return The Spatial representation of the asset
*/
private Spatial createModel(Asset asset, Vector3f pos, float rot) {
String modelName = asset.getModelPath();
String texName = asset.getDiffPath();
@@ -138,10 +193,23 @@ private Spatial createModel(Asset asset, Vector3f pos, float rot) {
return model;
}
/**
* Converts grid coordinates to world space.
*
* @param x The x-coordinate on the grid
* @param y The y-coordinate on the grid
* @return The corresponding world position
*/
private static Vector3f gridToWorld(int x, int y) {
return new Vector3f(GRID_SIZE * x, GRID_SIZE * y, GRID_ELEVATION);
}
/**
* Displays an asset on the map at the given position with the specified rotation.
*
* @param assetOnMap The asset to be displayed.
* @return A spatial representation of the asset at the specified location and rotation.
*/
private Spatial displayAsset(AssetOnMap assetOnMap) {
int x = assetOnMap.x();
int y = assetOnMap.y();
@@ -163,6 +231,13 @@ private void addHomeNode(Map<Color, List<NodeControl>> map, Color color, AssetOn
if (homeNodes.size() > 4) throw new RuntimeException("too many homeNodes for " + color);
}
/**
* Calculates the rotation angle required to move a piece from one position to another.
*
* @param prev The previous position.
* @param next The target position.
* @return The rotation angle in degrees.
*/
private float getRotationMove(Vector3f prev, Vector3f next) {
Vector3f direction = next.subtract(prev).normalizeLocal();
//I had to reverse dir.y, because then it worked.
@@ -171,6 +246,14 @@ private float getRotationMove(Vector3f prev, Vector3f next) {
return newRot;
}
/**
* Recursively moves a piece from its current index to the destination index,
* to keep track of the piece rotation.
*
* @param uuid The UUID of the piece to move.
* @param curIndex The current index of the piece.
* @param moveIndex The target index to move the piece to.
*/
private void movePieceRek(UUID uuid, int curIndex, int moveIndex){
if (curIndex == moveIndex) return;
@@ -203,6 +286,12 @@ private Vector3f getWaitingPos(Color color){
return getMeanPosition(waitingNodesMap.get(color).stream().map(NodeControl::getLocation).toList());
}
/**
* Gets the mean position of a list of vectors.
*
* @param vectors The list of vectors.
* @return The mean position as a Vector3f.
*/
public static Vector3f getMeanPosition(List<Vector3f> vectors) {
if (vectors.isEmpty()) return new Vector3f(0, 0, 0);
@@ -213,9 +302,14 @@ public static Vector3f getMeanPosition(List<Vector3f> vectors) {
return sum.divide(vectors.size());
}
//public methods****************************************************************************************************
/**
* Adds a player to the game by associating a color and a list of UUIDs to corresponding assets and waiting nodes.
*
* @param color the color of the player
* @param uuid the list of UUIDs representing the player's assets
* @throws RuntimeException if the number of assets or waiting nodes does not match the provided UUIDs
*/
public void addPlayer(Color color, List<UUID> uuid) {
if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
List<AssetOnMap> playerAssets = colorAssetsMap.get(color);
if (playerAssets == null) throw new RuntimeException("Assets for Player color are not defined");
@@ -227,20 +321,34 @@ public void addPlayer(Color color, List<UUID> uuid) {
for (int i = 0; i < playerAssets.size(); i++){
AssetOnMap assetOnMap = playerAssets.get(i);
UUID pieceUuid = uuid.get(i);
// Initialize PieceControl
PieceControl pieceControl = displayAndControl(assetOnMap, new PieceControl(assetOnMap.rot(), app.getAssetManager(), app, fpp));
pieceControl.setRotation(assetOnMap.rot());
movePieceToNode(pieceControl, waitNodes.get(i));
pieces.put(uuid.get(i), pieceControl);
// Assign piece to waiting node
NodeControl waitNode = getNextWaitingNode(color);
waitingNodes.get(color).put(pieceUuid, waitNode);
pieceColor.put(uuid.get(i), color);
// Move piece to node
movePieceToNode(pieceControl, waitNode);
// Update mappings
pieces.put(pieceUuid, pieceControl);
pieceColor.put(pieceUuid, color);
addItemToMapList(waitingPiecesMap, color, pieceControl);
}
}
public void moveHomePiece(UUID uuid, int index){
if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
/**
* Moves a piece to its corresponding home node based on the given index.
*
* @param uuid the UUID of the piece to move
* @param index the index of the home node to move the piece to
* @throws RuntimeException if the UUID is not mapped to a color or if the home nodes are not properly defined
*/
private void moveHomePiece(UUID uuid, int index){
Color color = pieceColor.get(uuid);
if(color == null) throw new RuntimeException("uuid is not mapped to a color");
@@ -258,90 +366,145 @@ public void moveHomePiece(UUID uuid, int index){
NodeControl lastHomeNode = homeNodes.get(homeNodes.size()-1);
pieceControl.setRotation(getRotationMove(firstHomeNode.getLocation(), lastHomeNode.getLocation()));
app.getModelSynchronize().animationEnd();
}
public void movePieceStart(UUID uuid, int nodeIndex){
if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
/**
* Starts the movement of a piece to a target node based on the given index.
*
* @param uuid the UUID of the piece to move
* @param nodeIndex the index of the target node to move the piece to
* @throws RuntimeException if the UUID is not mapped to a color or the piece control is not found
* @throws IllegalArgumentException if the node index is invalid
*/
private void movePieceStart(UUID uuid, int nodeIndex){
// Farbe des Pieces abrufen
Color color = pieceColor.get(uuid);
if(color == null) throw new RuntimeException("uuid is not mapped to a color");
if (color == null) throw new RuntimeException("UUID is not mapped to a color");
// PieceControl abrufen
PieceControl pieceControl = pieces.get(uuid);
movePieceToNode(pieceControl, infield.get(nodeIndex));
if (pieceControl == null) throw new RuntimeException("PieceControl not found for UUID: " + uuid);
// Zielknoten abrufen und prüfen
if (nodeIndex < 0 || nodeIndex >= infield.size()) {
throw new IllegalArgumentException("Invalid nodeIndex: " + nodeIndex);
}
NodeControl targetNode = infield.get(nodeIndex);
movePieceToNode(pieceControl, targetNode);
removeItemFromMapList(waitingPiecesMap, color, pieceControl);
waitingNodes.get(color).remove(uuid);
app.getModelSynchronize().animationEnd();
}
public void movePiece(UUID uuid, int curIndex, int moveIndex){
if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
/**
* Moves a piece from its current position to the target position based on the given indexes.
*
* @param uuid the UUID of the piece to move
* @param curIndex the current index of the piece
* @param moveIndex the target index of the move
*/
private void movePiece(UUID uuid, int curIndex, int moveIndex){
movePieceRek(uuid, curIndex, moveIndex);
app.getModelSynchronize().animationEnd();
}
public void throwPiece(UUID uuid){
if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
/**
* Throws a piece to the next available waiting node and updates the waiting node mapping.
*
* @param uuid the UUID of the piece to throw
* @throws RuntimeException if the UUID is not mapped to a color or if no available waiting nodes are found
*/
private void throwPiece(UUID uuid){
// Farbe des Pieces abrufen
Color color = pieceColor.get(uuid);
if(color == null) throw new RuntimeException("uuid is not mapped to a color");
if (color == null) throw new RuntimeException("UUID is not mapped to a color");
// PieceControl abrufen
PieceControl pieceControl = pieces.get(uuid);
List<NodeControl> waitNodes = waitingNodesMap.get(color);
List<PieceControl> waitPieces = waitingPiecesMap.get(color);
if (pieceControl == null) throw new RuntimeException("PieceControl not found for UUID: " + uuid);
movePieceToNode(pieceControl, waitNodes.get(waitPieces.size()));
pieceControl.rotateInit();
// Nächste freie Waiting Node abrufen
NodeControl nextWaitNode = getNextWaitingNode(color);
if (nextWaitNode == null) {
throw new IllegalStateException("No available waiting nodes for color: " + color);
}
// Bewegung durchführen
movePieceToNode(pieceControl, nextWaitNode);
// Waiting Nodes aktualisieren
waitingNodes.get(color).put(uuid, nextWaitNode);
// Synchronisation oder Animation
pieceControl.rotateInit();
app.getModelSynchronize().animationEnd();
}
/**
* Activates the shield for the specified piece.
*
* @param uuid the UUID of the piece to shield
*/
public void shieldPiece(UUID uuid){
if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
pieces.get(uuid).activateShield();
}
/**
* Deactivates the shield for the specified piece.
*
* @param uuid the UUID of the piece to unshield
*/
public void unshieldPiece(UUID uuid){
if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
pieces.get(uuid).deactivateShield();
}
/**
* Suppresses the shield for the specified piece.
*
* @param uuid the UUID of the piece to suppress the shield
*/
public void suppressShield(UUID uuid){
if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
pieces.get(uuid).suppressShield();
}
public void swapPieces(UUID piece1, UUID piece2){
if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
/**
* Swaps the positions and rotations of two pieces.
*
* @param p1 the first piece to swap
* @param p2 the second piece to swap
* @param loc1 the original location of the first piece
* @param rot1 the original rotation of the first piece
* @param loc2 the original location of the second piece
* @param rot2 the original rotation of the second piece
*/
private void swapPieces(PieceControl p1, PieceControl p2, Vector3f loc1, float rot1, Vector3f loc2, float rot2){
p1.setLocation(loc2);
p2.setLocation(loc1);
PieceControl piece1Control = pieces.get(piece1);
PieceControl piece2Control = pieces.get(piece2);
p1.setRotation(rot2);
p2.setRotation(rot1);
if(piece1Control == null) throw new RuntimeException("piece1 UUID is not valid");
if(piece2Control == null) throw new RuntimeException("piece2 UUID is not valid");
float rot1 = piece1Control.getRotation();
float rot2 = piece2Control.getRotation();
piece1Control.setRotation(rot2);
piece2Control.setRotation(rot1);
Vector3f pos1 = piece1Control.getLocation().clone();
Vector3f pos2 = piece2Control.getLocation().clone();
piece1Control.setLocation(pos2);
piece2Control.setLocation(pos1);
app.getModelSynchronize().animationEnd();
}
public void highlight(UUID uuid, boolean bool){
if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
pieces.get(uuid).highlight(bool);
pieces.get(uuid).setSelectable(bool);
}
//called when (dice) moveNum is received from server to display the movable pieces and corresponding moveNodes
/**
* Outlines the possible move nodes for a list of pieces based on the move indices and whether it's a home move.
*
* @param pieces the list of UUIDs representing the pieces to outline
* @param moveIndexe the list of indices for the target move nodes
* @param homeMoves the list indicating whether the move is a home move
* @throws RuntimeException if the sizes of the input lists do not match
*/
public void outlineMove(List<UUID> pieces, List<Integer> moveIndexe, List<Boolean> homeMoves) {
if(pieces.size() != moveIndexe.size() || pieces.size() != homeMoves.size()) throw new RuntimeException("arrays are not the same size");
@@ -371,7 +534,12 @@ public void outlineMove(List<UUID> pieces, List<Integer> moveIndexe, List<Boolea
}
}
//called when swap notification is received to highlight and select own/enemy pieces
/**
* Outlines the pieces that can be swapped based on the provided own and enemy pieces.
*
* @param ownPieces the list of UUIDs representing the player's pieces
* @param enemyPieces the list of UUIDs representing the enemy's pieces
*/
public void outlineSwap(List<UUID> ownPieces, List<UUID> enemyPieces){
selectableEnemyPieces.clear();
@@ -395,6 +563,11 @@ public void outlineSwap(List<UUID> ownPieces, List<UUID> enemyPieces){
}
}
/**
* Outlines the pieces that can be shielded based on the provided list of pieces.
*
* @param pieces the list of UUIDs representing the pieces to be shielded
*/
public void outlineShield(List<UUID> pieces){
selectableOwnPieces.clear();
selectableEnemyPieces.clear();
@@ -410,7 +583,11 @@ public void outlineShield(List<UUID> pieces){
}
}
//called from inputSynchronizer when a piece is selectable
/**
* Selects a piece from either the own or enemy pieces based on the input and deselects others if needed.
*
* @param pieceSelected the PieceControl instance representing the piece selected by the user
*/
public void pieceSelect(PieceControl pieceSelected) {
boolean isSelected = pieceSelected.isSelected();
if(selectableOwnPieces.contains(pieceSelected)){
@@ -440,9 +617,13 @@ else if(selectableEnemyPieces.contains(pieceSelected)) {
}
}
else throw new RuntimeException("pieceSelected is not in own/enemySelectablePieces");
app.getModelSynchronize().select(getKeyByValue(pieces, selectedOwnPiece), getKeyByValue(pieces, selectedEnemyPiece));
}
//called when view is no longer needed to select pieces
/**
* Clears all highlighted, selectable, and selected pieces and nodes.
*/
public void clearSelectable(){
for(PieceControl p : selectableEnemyPieces) {
p.unSelect();
@@ -464,19 +645,134 @@ public void clearSelectable(){
selectedOwnPiece = null;
}
public void enableHover(UUID uuid){
pieces.get(uuid).setHoverable(true);
}
/**
* Displays the dice for the specified color at the appropriate position.
*
* @param color the color of the player whose dice should be displayed
*/
public void showDice(Color color){
rootNodeBoard.attachChild(diceControl.getSpatial());
diceControl.setPos(getWaitingPos(color).add(new Vector3f(0,0,4)));
diceControl.spin();
}
/**
* Hides the dice from the view.
*/
public void hideDice(){
diceControl.hide();
}
private <K, V> K getKeyByValue(Map<K, V> map, V value) {
for (Map.Entry<K, V> entry : map.entrySet()) {
if (entry.getValue().equals(value)) {
return entry.getKey();
}
}
return null;
}
/**
* Animates the movement of a piece from its current index to a target index.
*
* @param uuid the UUID of the piece to animate
* @param curIndex the current index of the piece
* @param moveIndex the target index to animate the piece to
*/
public void movePieceAnim(UUID uuid, int curIndex, int moveIndex){
pieces.get(uuid).getSpatial().addControl(new MoveControl(
infield.get(curIndex).getLocation(),
infield.get(moveIndex).getLocation(),
()->movePiece(uuid,curIndex,moveIndex)));
}
/**
* Animates the movement of a piece to its home position based on the given home index.
*
* @param uuid the UUID of the piece to animate
* @param homeIndex the index of the home node to move the piece to
*/
public void movePieceHomeAnim(UUID uuid, int homeIndex){
pieces.get(uuid).getSpatial().addControl(new MoveControl(
pieces.get(uuid).getLocation(),
homeNodesMap.get(pieceColor.get(uuid)).get(homeIndex).getLocation(),
()->moveHomePiece(uuid,homeIndex)));
}
/**
* Animates the start of the movement of a piece to a target index.
*
* @param uuid the UUID of the piece to animate
* @param moveIndex the target index to animate the piece to
*/
public void movePieceStartAnim(UUID uuid, int moveIndex){
pieces.get(uuid).getSpatial().addControl(new MoveControl(
pieces.get(uuid).getLocation(),
infield.get(moveIndex).getLocation(),
()->movePieceStart(uuid, moveIndex)
));
}
/**
* Animates the throwing of a piece to the next available waiting node.
*
* @param uuid the UUID of the piece to animate
*/
public void throwPieceAnim(UUID uuid){
pieces.get(uuid).getSpatial().addControl(new MoveControl(
pieces.get(uuid).getLocation(),
getNextWaitingNode(pieceColor.get(uuid)).getLocation(),
()->throwPiece(uuid)
));
}
/**
* Animates the swapping of two pieces by swapping their positions and rotations.
*
* @param piece1 the UUID of the first piece
* @param piece2 the UUID of the second piece
*/
public void swapPieceAnim(UUID piece1, UUID piece2){
PieceControl piece1Control = pieces.get(piece1);
PieceControl piece2Control = pieces.get(piece2);
Vector3f loc1 = piece1Control.getLocation().clone();
Vector3f loc2 = piece2Control.getLocation().clone();
float rot1 = piece1Control.getRotation();
float rot2 = piece2Control.getRotation();
piece1Control.getSpatial().addControl(new MoveControl(
piece1Control.getLocation().clone(),
piece2Control.getLocation().clone(),
()->{}
));
piece2Control.getSpatial().addControl(new MoveControl(
piece2Control.getLocation().clone(),
piece1Control.getLocation().clone(),
()->swapPieces(piece1Control,piece2Control,loc1,rot1,loc2,rot2)
));
}
/**
* Retrieves the next available waiting node for the specified color.
*
* @param color the color of the player to get the next waiting node for
* @return the next available NodeControl for the specified color
* @throws IllegalStateException if no available waiting nodes are found for the color
*/
private NodeControl getNextWaitingNode(Color color) {
List<NodeControl> nodes = waitingNodesMap.get(color);
if (nodes == null || nodes.isEmpty()) {
throw new IllegalStateException("Keine verfügbaren Warteschleifen-Knoten für die Farbe " + color);
}
for (NodeControl node : nodes) {
if (!waitingNodes.getOrDefault(color, new HashMap<>()).containsValue(node)) {
return node;
}
}
throw new IllegalStateException("Keine freien Nodes im Wartebereich für die Farbe " + color);
}
}

View File

@@ -13,7 +13,12 @@
import com.jme3.util.SkyFactory;
import com.jme3.util.SkyFactory.EnvMapType;
import pp.mdga.client.MdgaApp;
import pp.mdga.game.Color;
/**
* Handles the camera position, rotation, and lighting effects for the game.
* Provides methods for camera initialization, updates based on user input, and shutdown operations.
*/
public class CameraHandler {
MdgaApp app;
@@ -29,11 +34,21 @@ public class CameraHandler {
DirectionalLightShadowFilter dlsf;
Spatial sky;
private Color ownColor;
private boolean init;
private boolean initRot;
/**
* Constructor for the CameraHandler. Initializes the camera settings and lighting.
*
* @param app The main application instance that provides the camera and root node.
* @param fpp The FilterPostProcessor used for post-processing effects.
*/
public CameraHandler(MdgaApp app, FilterPostProcessor fpp) {
init = false;
initRot = false;
this.app = app;
this.fpp = fpp;
// Save the default camera state
this.defaultCameraPosition = app.getCamera().getLocation().clone();
this.defaultCameraRotation = app.getCamera().getRotation().clone();
@@ -56,13 +71,27 @@ public CameraHandler(MdgaApp app, FilterPostProcessor fpp) {
}
public void init() {
/**
* Initializes the camera with a specific color orientation.
* Adds lights, sky, and shadow filters to the scene.
*
* @param ownColor The color that defines the initial camera view angle.
*/
public void init(Color ownColor) {
app.getRootNode().addLight(sun);
app.getRootNode().addLight(ambient);
app.getRootNode().attachChild(sky);
fpp.addFilter(dlsf);
init = true;
initRot = true;
this.ownColor = ownColor;
app.getInputSynchronize().setRotation(getInitAngleByColor(ownColor)*2);
}
/**
* Shuts down the camera handler by removing all lights, sky, and filters,
* and resets the camera position and rotation to its default state.
*/
public void shutdown() {
app.getRootNode().removeLight(sun);
app.getRootNode().removeLight(ambient);
@@ -75,7 +104,15 @@ public void shutdown() {
fpp.removeFilter(dlsf);
}
/**
* Updates the camera position and rotation based on user input (scroll and rotation).
* Adjusts the vertical angle and radius based on zoom and rotation values.
*
* @param scroll The scroll input, determining zoom level.
* @param rotation The rotation input, determining camera orientation.
*/
public void update(float scroll, float rotation) {
if(!init) return;
float scrollValue = Math.max(0, Math.min(scroll, 100));
float rotationValue = rotation % 360;
@@ -83,6 +120,7 @@ public void update(float scroll, float rotation) {
rotationValue += 360;
}
float radius;
float verticalAngle;
@@ -91,10 +129,9 @@ public void update(float scroll, float rotation) {
radius = 30f;
} else {
verticalAngle = 90f;
rotationValue = 270f;
rotationValue = getAngleByColor(ownColor);
radius = 50f;
}
float verticalAngleRadians = FastMath.DEG_TO_RAD * verticalAngle;
float z = radius * FastMath.sin(verticalAngleRadians);
@@ -107,4 +144,30 @@ public void update(float scroll, float rotation) {
app.getCamera().lookAt(Vector3f.ZERO, Vector3f.UNIT_Z);
}
/**
* Returns the camera angle based on the specified color.
*
* @param color The color used to determine the camera angle.
* @return The camera angle in degrees.
*/
private float getAngleByColor(Color color){
return switch (color){
case ARMY -> 0;
case AIRFORCE -> 90;
case NAVY -> 270;
case CYBER -> 180;
default -> throw new RuntimeException("None is not allowed");
};
}
/**
* Returns the initial camera angle based on the specified color.
*
* @param color The color used to determine the camera angle.
* @return The initial camera angle in degrees.
*/
private float getInitAngleByColor(Color color){
return (getAngleByColor(color) + 180) % 360;
}
}

View File

@@ -10,11 +10,27 @@
import java.util.ArrayList;
import java.util.List;
/**
* A utility class for loading and parsing map data from a file.
* The map contains asset names and coordinates for objects placed on the map.
*/
class MapLoader {
/**
* Private constructor to prevent instantiation.
*/
private MapLoader() {
}
/**
* Loads a map file and parses its contents into a list of assets and their positions.
* Each line in the map file defines an asset, its coordinates, and its rotation.
*
* @param mapName The name of the map file to load. The file is expected to be located in the resources directory.
* @return A list of {@link AssetOnMap} objects representing the assets placed on the map.
* @throws IOException If an error occurs while reading the map file.
* @throws IllegalArgumentException If the map file contains invalid data.
*/
public static List<AssetOnMap> loadMap(String mapName) {
List<AssetOnMap> assetsOnMap = new ArrayList<>();
@@ -60,6 +76,13 @@ public static List<AssetOnMap> loadMap(String mapName) {
return assetsOnMap;
}
/**
* Returns the corresponding {@link Asset} for a given asset name.
*
* @param assetName The name of the asset to load.
* @return The {@link Asset} associated with the given name.
* @throws IllegalStateException If the asset name is unrecognized.
*/
private static Asset getLoadedAsset(String assetName) {
return switch (assetName) {
case "lw" -> Asset.lw;
@@ -84,8 +107,8 @@ private static Asset getLoadedAsset(String assetName) {
case "radar" -> Asset.radar;
case "ship" -> Asset.ship;
case "tank" -> Asset.tank;
case "tree_small" -> Asset.tree_small;
case "tree_big" -> Asset.tree_big;
case "tree_small" -> Asset.treeSmall;
case "tree_big" -> Asset.treeBig;
default -> throw new IllegalStateException("Unexpected value: " + assetName);
};
}

View File

@@ -8,19 +8,40 @@
import com.jme3.scene.control.AbstractControl;
import pp.mdga.client.MdgaApp;
/**
* A control that adds highlighting functionality to a node in the game.
* This class extends {@link OutlineControl} to add an outline effect when the node is highlighted.
*/
public class NodeControl extends OutlineControl {
private static final ColorRGBA OUTLINE_HIGHLIGHT_COLOR = ColorRGBA.White;
private static final int OUTLINE_HIGHLIGHT_WIDTH = 6;
/**
* Constructs a {@link NodeControl} with the specified application and post processor.
* This constructor sets up the necessary elements for highlighting functionality.
*
* @param app The {@link MdgaApp} instance to use for the application context.
* @param fpp The {@link FilterPostProcessor} to apply post-processing effects.
*/
public NodeControl(MdgaApp app, FilterPostProcessor fpp) {
super(app, fpp);
}
/**
* Returns the location of the node in 3D space.
* This is the node's local translation in the scene.
*
* @return The {@link Vector3f} representing the node's location.
*/
public Vector3f getLocation(){
return this.getSpatial().getLocalTranslation();
}
/**
* Highlights the node by applying an outline effect.
* The outline color and width are predefined as white and 6, respectively.
*/
public void highlight() {
super.outline(OUTLINE_HIGHLIGHT_COLOR, OUTLINE_HIGHLIGHT_WIDTH);
}

View File

@@ -3,72 +3,64 @@
import com.jme3.math.ColorRGBA;
import com.jme3.post.FilterPostProcessor;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.board.Outline.SelectObjectOutliner;
import pp.mdga.client.InitControl;
import pp.mdga.client.outline.SelectObjectOutliner;
public class OutlineControl extends AbstractControl {
private static final int THICKNESS_DEFAULT = 6;
/**
* A control that provides outline functionality to a spatial object.
* This class is responsible for adding an outline effect to a spatial
* object, allowing it to be highlighted or deselected.
*/
public class OutlineControl extends InitControl {
/** The {@link SelectObjectOutliner} responsible for managing the outline effect. */
private final SelectObjectOutliner outlineOwn;
private static final int THICKNESS_DEFAULT = 6;
private MdgaApp app;
public OutlineControl(MdgaApp app, FilterPostProcessor fpp){
this.app = app;
outlineOwn = new SelectObjectOutliner(THICKNESS_DEFAULT, fpp, app.getRenderManager(), app.getAssetManager(), app.getCamera());
outlineOwn = new SelectObjectOutliner(THICKNESS_DEFAULT, fpp, app.getRenderManager(), app.getAssetManager(), app.getCamera(), app);
}
public OutlineControl(MdgaApp app, FilterPostProcessor fpp, Camera cam){
this.app = app;
outlineOwn = new SelectObjectOutliner(THICKNESS_DEFAULT, fpp, app.getRenderManager(), app.getAssetManager(), cam);
outlineOwn = new SelectObjectOutliner(THICKNESS_DEFAULT, fpp, app.getRenderManager(), app.getAssetManager(), cam, app);
}
public OutlineControl(MdgaApp app, FilterPostProcessor fpp, Camera cam, int thickness){
this.app = app;
outlineOwn = new SelectObjectOutliner(thickness, fpp, app.getRenderManager(), app.getAssetManager(), cam);
outlineOwn = new SelectObjectOutliner(thickness, fpp, app.getRenderManager(), app.getAssetManager(), cam, app);
}
/**
* Applies an outline to the spatial object with the given color.
*
* @param color The {@link ColorRGBA} representing the color of the outline.
*/
public void outline(ColorRGBA color){
outlineOwn.select(spatial, color);
}
/**
* Applies an outline to the spatial object with the given color and width.
*
* @param color The {@link ColorRGBA} representing the color of the outline.
* @param width The width of the outline.
*/
public void outline(ColorRGBA color, int width){
deOutline();
outlineOwn.select(spatial, color, width);
}
/**
* Removes the outline effect from the spatial object.
*/
public void deOutline(){
outlineOwn.deselect(spatial);
}
@Override
protected void controlUpdate(float tpf) {
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
}
public void initSpatial(){
}
@Override
public void setSpatial(Spatial spatial){
if(this.spatial == null && spatial != null){
super.setSpatial(spatial);
initSpatial();
}
else{
super.setSpatial(spatial);
}
}
public MdgaApp getApp() {
return app;
}

View File

@@ -14,6 +14,12 @@
import pp.mdga.client.Asset;
import pp.mdga.client.MdgaApp;
/**
* A control that manages the behavior and properties of a game piece, such as its rotation,
* position, shield activation, and highlighting. This class extends {@link OutlineControl}
* to provide outline functionality and includes additional features like shield effects,
* hover states, and selection states.
*/
public class PieceControl extends OutlineControl {
private final float initRotation;
private final AssetManager assetManager;
@@ -43,7 +49,15 @@ public class PieceControl extends OutlineControl {
private boolean selectable;
private boolean select;
/**
* Constructs a {@link PieceControl} with the specified initial rotation, asset manager,
* application, and post-processor.
*
* @param initRotation The initial rotation of the piece in degrees.
* @param assetManager The {@link AssetManager} used for loading models and materials.
* @param app The {@link MdgaApp} instance to use for the application context.
* @param fpp The {@link FilterPostProcessor} to apply post-processing effects.
*/
public PieceControl(float initRotation, AssetManager assetManager, MdgaApp app, FilterPostProcessor fpp){
super(app, fpp);
this.parentNode = new Node();
@@ -59,10 +73,20 @@ public PieceControl(float initRotation, AssetManager assetManager, MdgaApp app,
select = false;
}
/**
* Gets the current rotation of the piece in degrees.
*
* @return The rotation of the piece in degrees.
*/
public float getRotation() {
return (float) Math.toDegrees(spatial.getLocalRotation().toAngleAxis(new Vector3f(0,0,1)));
}
/**
* Sets the rotation of the piece to the specified value in degrees.
*
* @param rot The rotation in degrees to set.
*/
public void setRotation(float rot){
if(rot < 0) rot =- 360;
@@ -71,10 +95,20 @@ public void setRotation(float rot){
spatial.setLocalRotation(quaternion);
}
/**
* Gets the current location (position) of the piece.
*
* @return The location of the piece as a {@link Vector3f}.
*/
public Vector3f getLocation(){
return spatial.getLocalTranslation();
}
/**
* Updates the piece control every frame. If the shield is active, it will rotate.
*
* @param delta The time difference between frames (time per frame).
*/
@Override
protected void controlUpdate(float delta) {
if(shieldRing != null){
@@ -82,10 +116,19 @@ protected void controlUpdate(float delta) {
}
}
/**
* Sets the location (position) of the piece.
*
* @param loc The location to set as a {@link Vector3f}.
*/
public void setLocation(Vector3f loc){
this.spatial.setLocalTranslation(loc);
}
/**
* Initializes the spatial object and sets its rotation.
* This also moves the spatial to a new parent node for organizational purposes.
*/
@Override
public void initSpatial(){
setRotation(this.initRotation);
@@ -101,8 +144,12 @@ public void rotateInit() {
// rotate(rotation - initRotation);
}
/**
* Activates the shield around the piece.
* This adds a visual shield effect in the form of a rotating ring.
*/
public void activateShield(){
shieldRing = assetManager.loadModel(Asset.shield_ring.getModelPath());
shieldRing = assetManager.loadModel(Asset.shieldRing.getModelPath());
shieldRing.scale(1f);
shieldRing.rotate((float) Math.toRadians(0), 0, (float) Math.toRadians(0));
shieldRing.setLocalTranslation(spatial.getLocalTranslation().add(new Vector3f(0,0,SHIELD_Z)));
@@ -115,11 +162,18 @@ public void activateShield(){
parentNode.attachChild(shieldRing);
}
/**
* Deactivates the shield by removing the shield ring from the scene.
*/
public void deactivateShield(){
parentNode.detachChild(shieldRing);
shieldRing = null;
}
/**
* Suppresses the shield, changing its color to a suppressed state.
*/
public void suppressShield(){
assert(shieldRing != null) : "PieceControl: shieldRing is not set";
shieldMat.setColor("Color", SHIELD_SUPPRESSED_COLOR);
@@ -133,22 +187,36 @@ public Material getMaterial(){
return ((Geometry) getSpatial()).getMaterial();
}
/**
* Highlights the piece with the appropriate outline color based on whether it is an enemy or not.
*
* @param enemy True if the piece is an enemy, false if it is owned by the player.
*/
public void highlight(boolean enemy) {
this.enemy = enemy;
highlight = true;
super.outline(enemy ? OUTLINE_ENEMY_COLOR : OUTLINE_OWN_COLOR, OUTLINE_HIGHLIGHT_WIDTH);
}
/**
* Removes the highlight effect from the piece.
*/
public void unHighlight(){
highlight = false;
deOutline();
}
/**
* Applies a hover effect on the piece if it is hoverable.
*/
public void hover(){
if(!hoverable) return;
super.outline(enemy ? OUTLINE_ENEMY_HOVER_COLOR : OUTLINE_OWN_HOVER_COLOR, OUTLINE_HOVER_WIDTH);
}
/**
* Removes the hover effect from the piece.
*/
public void hoverOff(){
if(!hoverable) return;
@@ -157,28 +225,56 @@ public void hoverOff(){
else deOutline();
}
/**
* Deselects the piece and removes the selection outline. If the piece was highlighted,
* it will be re-highlighted. Otherwise, the outline is removed.
*/
public void unSelect(){
select = false;
if(highlight) highlight(enemy);
else deOutline();
}
/**
* Selects the piece and applies the selection outline. If the piece is an enemy, it will
* be outlined with the enemy selection color; otherwise, the own selection color will be used.
*/
public void select(){
if(!selectable) return;
select = true;
super.outline(enemy ? OUTLINE_ENEMY_SELECT_COLOR : OUTLINE_OWN_SELECT_COLOR, OUTLINE_SELECT_WIDTH);
}
/**
* Sets whether the piece is selectable.
*
* @param selectable True if the piece can be selected, false otherwise.
*/
public void setSelectable(boolean selectable){
this.selectable = selectable;
}
/**
* Checks if the piece is selected.
*
* @return True if the piece is selected, false otherwise.
*/
public boolean isSelected() { return select; }
/**
* Checks if the piece is selectable.
*
* @return True if the piece is selectable, false otherwise.
*/
public boolean isSelectable() {
return selectable;
}
/**
* Sets whether the piece is hoverable.
*
* @param hoverable True if the piece can be hovered over, false otherwise.
*/
public void setHoverable(boolean hoverable) {
this.hoverable = hoverable;
}

View File

@@ -1,5 +0,0 @@
package pp.mdga.client.board;
class PileControl {
}

View File

@@ -1,12 +0,0 @@
package pp.mdga.client.board;
public enum Rotation {
UP,
RIGHT,
DOWN,
LEFT,
UP_LEFT,
UP_RIGHT,
DOWN_RIGHT,
DOWN_LEFT
}

View File

@@ -158,4 +158,8 @@ protected void calculateRelative() {
heightStep = verticalStep / 2;
widthStep = horizontalStep / 2;
}
public Vector2f getPos() {
return pos;
}
}

View File

@@ -197,14 +197,14 @@ protected void setImageRelative(Picture picture) {
return;
}
final float LARGER = 10;
final float larger = 10;
picture.setWidth(instance.getPreferredSize().x + LARGER);
picture.setHeight(instance.getPreferredSize().y + LARGER);
picture.setWidth(instance.getPreferredSize().x + larger);
picture.setHeight(instance.getPreferredSize().y + larger);
picture.setLocalTranslation(
instance.getLocalTranslation().x - LARGER / 2,
(instance.getLocalTranslation().y - picture.getHeight()) + LARGER / 2,
instance.getLocalTranslation().x - larger / 2,
(instance.getLocalTranslation().y - picture.getHeight()) + larger / 2,
instance.getLocalTranslation().z + 0.01f
);
}

View File

@@ -317,7 +317,6 @@ public void setTaken(Taken taken, String name) {
} else {
label.setText(name);
}
onUnHover();
}

View File

@@ -30,20 +30,9 @@ public SettingsButton(MdgaApp app, Node node, Runnable action) {
// Enable adjustment for positioning
adjust = true;
}
/**
* Displays the settings button by attaching it to the scene graph.
*/
@Override
public void show() {
release();
calculateRelative();
setRelative();
setImageRelative(pictureNormal);
node.attachChild(instance);
pictureNormal.setImage(app.getAssetManager(), "Images/Settings_Button_normal.png", true);
pictureHover.setImage(app.getAssetManager(), "Images/Settings_Button_hover.png", true);
}
/**
@@ -51,12 +40,6 @@ public void show() {
*/
@Override
public void onHover() {
icon = new IconComponent("Images/Settings_Button_hover.png");
icon.setIconScale(0.02f * app.getImageScale());
icon.setHAlignment(HAlignment.Center);
icon.setVAlignment(VAlignment.Center);
instance.setIcon(icon);
}
/**
@@ -64,12 +47,6 @@ public void onHover() {
*/
@Override
public void onUnHover() {
icon = new IconComponent("Images/Settings_Button_normal.png");
icon.setIconScale(0.02f * app.getImageScale());
icon.setHAlignment(HAlignment.Center);
icon.setVAlignment(VAlignment.Center);
instance.setIcon(icon);
}
}

View File

@@ -28,5 +28,6 @@ public void hide() {
}
protected abstract void onShow();
protected abstract void onHide();
}

View File

@@ -3,6 +3,7 @@
import com.jme3.math.Vector2f;
import com.jme3.scene.Node;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.NetworkSupport;
import pp.mdga.client.button.ButtonLeft;
import pp.mdga.client.button.ButtonRight;
import pp.mdga.client.button.InputButton;
@@ -11,7 +12,7 @@
import java.util.prefs.Preferences;
public class HostDialog extends Dialog {
public class HostDialog extends NetworkDialog {
private InputButton portInput;
private ButtonRight hostButton;
@@ -22,7 +23,7 @@ public class HostDialog extends Dialog {
private Preferences prefs = Preferences.userNodeForPackage(JoinDialog.class);
public HostDialog(MdgaApp app, Node node, MainView view) {
super(app, node);
super(app, node, (NetworkSupport) app.getNetworkSupport());
this.view = view;
@@ -58,6 +59,7 @@ public void update() {
public String getPort() {
prefs.put("hostPort", portInput.getString());
setPortNumber(Integer.parseInt(portInput.getString()));
return portInput.getString();
}
@@ -65,4 +67,13 @@ public void resetPort() {
portInput.reset();
prefs.put("hostPort", "11111");
}
public void hostServer() {
startServer();
}
public void connectServerAsClient() {
connectServer();
}
}

View File

@@ -3,6 +3,7 @@
import com.jme3.math.Vector2f;
import com.jme3.scene.Node;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.NetworkSupport;
import pp.mdga.client.acoustic.AcousticHandler;
import pp.mdga.client.button.ButtonLeft;
import pp.mdga.client.button.ButtonRight;
@@ -12,7 +13,7 @@
import java.util.prefs.Preferences;
public class JoinDialog extends Dialog {
public class JoinDialog extends NetworkDialog {
private InputButton ipInput;
private InputButton portInput;
@@ -24,7 +25,7 @@ public class JoinDialog extends Dialog {
private Preferences prefs = Preferences.userNodeForPackage(JoinDialog.class);
public JoinDialog(MdgaApp app, Node node, MainView view) {
super(app, node);
super(app, node, (NetworkSupport) app.getNetworkSupport());
this.view = view;
@@ -68,6 +69,7 @@ public void update() {
public String getIpt() {
prefs.put("joinIp", ipInput.getString());
setHostname(ipInput.getString());
return ipInput.getString();
}
@@ -78,6 +80,7 @@ public void resetIp() {
public String getPort() {
prefs.put("joinPort", portInput.getString());
setPortNumber(Integer.parseInt(portInput.getString()));
return portInput.getString();
}
@@ -85,4 +88,8 @@ public void resetPort() {
portInput.reset();
prefs.put("joinPort", "11111");
}
public void connectToServer() {
connectServer();
}
}

View File

@@ -0,0 +1,78 @@
package pp.mdga.client.dialog;
import com.jme3.scene.Node;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.NetworkSupport;
import pp.mdga.client.server.MdgaServer;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
public abstract class NetworkDialog extends Dialog {
private NetworkSupport network;
private String hostname;
private int portNumber;
private Future<Object> connectionFuture;
public NetworkDialog(MdgaApp app, Node node, NetworkSupport network) {
super(app, node);
this.network = network;
}
public void setHostname(String hostname) {
this.hostname = hostname;
}
public void setPortNumber(int portNumber) {
this.portNumber = portNumber;
}
protected Object initNetwork() {
try {
this.network.initNetwork(this.hostname, this.portNumber);
return null;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void connectServer() {
try {
connectionFuture = this.network.getApp().getExecutor().submit(this::initNetwork);
} catch (NumberFormatException var2) {
throw new NumberFormatException("Port must be a number");
}
}
protected void startServer() {
(new Thread(() -> {
try {
MdgaServer mdgaServer = new MdgaServer(portNumber);
mdgaServer.run();
} catch (Exception e) {
throw new RuntimeException(e);
}
})).start();
}
public void update(float delta) {
if (this.connectionFuture != null && this.connectionFuture.isDone()) {
try {
this.connectionFuture.get();
} catch (ExecutionException ignored) {
// todo: implement
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public NetworkSupport getNetwork(){
return network;
}
}

View File

@@ -3,12 +3,21 @@
import com.jme3.math.Vector2f;
import com.jme3.scene.Node;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.button.ButtonLeft;
import pp.mdga.client.button.ButtonRight;
import pp.mdga.client.button.MenuButton;
import pp.mdga.client.view.MdgaView;
public class VideoSettingsDialog extends Dialog {
private MenuButton backButton;
private ButtonLeft hdButton9;
private ButtonLeft fullHdButton9;
private ButtonLeft wqhdButton9;
private ButtonRight hdButton10;
private ButtonRight fullHdButton10;
private ButtonRight wqhdButton10;
private final MdgaView view;
private boolean active = false;
@@ -20,8 +29,30 @@ public VideoSettingsDialog(MdgaApp app, Node node, MdgaView view) {
backButton = new MenuButton(app, node, view::leaveVideoSettings, "Zurück");
// MenuButton für verschiedene Auflösungen erstellen
hdButton9 = new ButtonLeft(app, node, () -> app.updateResolution(1280, 720, 1.0f), "hd 16:9", 10);
fullHdButton9 = new ButtonLeft(app, node, () -> app.updateResolution(1920, 1080, 2.25f), "full hd 16:9", 10);
wqhdButton9 = new ButtonLeft(app, node, () -> app.updateResolution(2560, 1440, 4.0f), "wqhd 16:9", 10);
hdButton10 = new ButtonRight(app, node, () -> app.updateResolution(1280, 800, 1.0f), "hd 16:10", 10);
fullHdButton10 = new ButtonRight(app, node, () -> app.updateResolution(1920, 1200, 2.25f), "full hd 16:10", 10);
wqhdButton10 = new ButtonRight(app, node, () -> app.updateResolution(2560, 1600, 4.0f), "wqhd 16:10", 10);
float offset = 2.8f;
hdButton9.setPos(new Vector2f(hdButton9.getPos().x, MenuButton.VERTICAL - offset));
hdButton10.setPos(new Vector2f(hdButton10.getPos().x, MenuButton.VERTICAL - offset));
offset += 1.5f;
fullHdButton9.setPos(new Vector2f(fullHdButton9.getPos().x, MenuButton.VERTICAL - offset));
fullHdButton10.setPos(new Vector2f(fullHdButton10.getPos().x, MenuButton.VERTICAL - offset));
offset += 1.5f;
wqhdButton9.setPos(new Vector2f(wqhdButton9.getPos().x, MenuButton.VERTICAL - offset));
wqhdButton10.setPos(new Vector2f(wqhdButton10.getPos().x, MenuButton.VERTICAL - offset));
offset += 1.5f;
backButton.setPos(new Vector2f(0, 1.8f));
}
@@ -29,6 +60,14 @@ public VideoSettingsDialog(MdgaApp app, Node node, MdgaView view) {
protected void onShow() {
active = true;
hdButton9.show();
fullHdButton9.show();
wqhdButton9.show();
hdButton10.show();
fullHdButton10.show();
wqhdButton10.show();
backButton.show();
}
@@ -36,6 +75,14 @@ protected void onShow() {
protected void onHide() {
active = false;
hdButton9.hide();
fullHdButton9.hide();
wqhdButton9.hide();
hdButton10.hide();
fullHdButton10.hide();
wqhdButton10.hide();
backButton.hide();
}

View File

@@ -7,7 +7,7 @@
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.system.AppSettings;
import pp.mdga.client.Asset;
import pp.mdga.client.animation.ZoomControl;
import pp.mdga.game.Color;
public class ActionTextHandler {
@@ -111,6 +111,16 @@ public void drawCardOwn(Color color){
createTopText(new String[]{"Du"," erhälst eine Bonuskarte"}, 5,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
}
public void finishText(String name, Color color){
createTopText(new String[]{name," ist fertig!"}, 7,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
}
public void finishTextOwn(Color color){
createTopText(new String[]{"Du", " bist fertig!"}, 7,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
}
private ColorRGBA playerColorToColorRGBA(Color color){
return switch (color){
case ARMY -> ColorRGBA.Green;

View File

@@ -84,9 +84,6 @@ public Node getRoot() {
public void initSpatial(){
}
public void outline(){
super.outline(OUTLINE_COLOR);
}

View File

@@ -4,7 +4,6 @@
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
@@ -15,16 +14,16 @@
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.shadow.DirectionalLightShadowFilter;
import com.jme3.shadow.DirectionalLightShadowRenderer;
import com.jme3.shadow.EdgeFilteringMode;
import com.jme3.texture.Image;
import com.jme3.texture.Texture2D;
import pp.mdga.client.Asset;
import java.util.*;
public class CardLayer extends AbstractAppState {
public static final int SHADOWMAP_SIZE = 1024 * 8;
private Node root;
private Application app;
private boolean init;
@@ -60,8 +59,6 @@ public void initialize(AppStateManager stateManager, Application app ) {
sun.setDirection(new Vector3f(.5f, -.5f, -1));
root.addLight(sun);
final int SHADOWMAP_SIZE=1024*8;
DirectionalLightShadowFilter dlsf = new DirectionalLightShadowFilter(app.getAssetManager(), SHADOWMAP_SIZE, 3);
dlsf.setLight(sun);
dlsf.setEnabled(true);
@@ -80,7 +77,6 @@ public void shutdown(){
}
@Override
public void render(RenderManager rm) {
root.updateGeometricState();
@@ -88,8 +84,6 @@ public void render(RenderManager rm) {
@Override
public void update(float tpf) {
if (init && !cardBuffer.isEmpty()) {
for (Spatial spatial : cardBuffer) {
root.attachChild(spatial);

View File

@@ -10,6 +10,7 @@
import com.jme3.texture.Texture2D;
import pp.mdga.client.Asset;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.animation.SymbolControl;
import pp.mdga.game.BonusCard;
import java.util.*;
@@ -43,6 +44,7 @@ public void init() {
cardLayerCamera = createOverlayCam();
cardLayer = new CardLayer(fpp, cardLayerCamera, backTexture);
app.getStateManager().attach(cardLayer);
diceControl = new DiceControl(app.getAssetManager());
diceControl.create(new Vector3f(0, 0, 0), 1f, false);
}
@@ -50,6 +52,7 @@ public void init() {
public void shutdown() {
if (cardLayer != null) {
cardLayer.shutdown();
clearSelectableCards();
}
cardLayer = null;
}

View File

@@ -7,6 +7,7 @@
import com.jme3.texture.Texture2D;
import pp.mdga.client.MdgaApp;
import pp.mdga.game.Color;
import pp.mdga.game.BonusCard;
import java.util.List;
@@ -15,11 +16,11 @@ public class GuiHandler {
private final CardLayerHandler cardLayerHandler;
private final PlayerNameHandler playerNameHandler;
private final ActionTextHandler actionTextHandler;
private final Color ownColor;
private Color ownColor;
private FrameBuffer backFrameBuffer;
public GuiHandler(MdgaApp app, Node guiNode, Color ownColor) {
public GuiHandler(MdgaApp app, Node guiNode) {
this.app = app;
this.ownColor = ownColor;
@@ -33,8 +34,10 @@ public GuiHandler(MdgaApp app, Node guiNode, Color ownColor) {
actionTextHandler = new ActionTextHandler(guiNode, app.getAssetManager(), app.getContext().getSettings());
}
public void init() {
public void init(Color ownColor) {
cardLayerHandler.init();
playerNameHandler.show();
this.ownColor = ownColor;
app.getViewPort().setOutputFrameBuffer(backFrameBuffer);
}
@@ -48,7 +51,7 @@ public void rollDice(int rollNum, int mult) {
if(mult == -1) actionTextHandler.ownDice(rollNum);
else actionTextHandler.ownDiceMult(rollNum, mult);
hideDice();
//TODO send Model finished
app.getModelSynchronize().animationEnd();
});
}
@@ -70,7 +73,7 @@ public void hideDice() {
cardLayerHandler.hideDice();
}
public void addCard(pp.mdga.game.BonusCard card) {
public void addCard(BonusCard card) {
cardLayerHandler.addCard(card);
}
@@ -78,7 +81,7 @@ public void clearSelectableCards() {
cardLayerHandler.clearSelectableCards();
}
public void setSelectableCards(List<pp.mdga.game.BonusCard> select) {
public void setSelectableCards(List<BonusCard> select) {
cardLayerHandler.setSelectableCards(select);
}
@@ -126,5 +129,10 @@ public void drawCard(Color color) {
else actionTextHandler.drawCard(playerNameHandler.getName(color), color);
}
public void finish(Color color){
if(ownColor == color) actionTextHandler.finishTextOwn(color);
else actionTextHandler.finishText(playerNameHandler.getName(color), color);
}
}

View File

@@ -1,147 +0,0 @@
package pp.mdga.client.gui;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.control.AbstractControl;
import pp.mdga.game.BonusCard;
public class SymbolControl extends AbstractControl {
private boolean zoomingIn = false;
private boolean zoomingOut = false;
private float zoomSpeed = 1f;
private float zoomFactor = 3f;
private float progress = 0;
private BonusCard state;
private float rotationSpeed = 0.8f;
private Quaternion initialRotation = null;
private float Y = 5;
@Override
protected void controlUpdate(float tpf) {
if(state == null) return;
switch (state){
case SHIELD -> shieldUpdate(tpf);
case SWAP -> swapUpdate(tpf);
case TURBO -> turboUpdate(tpf);
case HIDDEN -> throw new RuntimeException("forbidden state");
}
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
}
private void shieldUpdate(float tpf){
if (zoomingIn) {
progress += tpf * zoomSpeed;
if (progress > 1) progress = 1;
spatial.setLocalScale(lerp(0, zoomFactor, easeOut(progress)));
if (progress >= 1) {
zoomingIn = false;
zoomingOut = true;
progress = 0;
}
} else if (zoomingOut) {
progress += tpf * zoomSpeed;
spatial.setLocalScale(lerp(zoomFactor, 0, easeIn(progress)));
if (progress > 1) {
zoomingIn = false;
spatial.removeFromParent();
state = null;
progress = 0;
}
}
}
private void swapUpdate(float tpf){
if (initialRotation == null) {
initialRotation = spatial.getLocalRotation().clone();
}
progress += tpf*rotationSpeed;
if(progress < 0) return;
float angle = lerp(0, 360, easeInOut(progress));
Quaternion newRotation = new Quaternion();
newRotation.fromAngleAxis((float) Math.toRadians(angle), new Vector3f(0, 1, 0));
spatial.setLocalRotation(initialRotation.mult(newRotation));
if (progress >= 1.2f) {
state = null;
initialRotation = null;
progress = 0;
spatial.removeFromParent();
}
}
private void turboUpdate(float tpf){
if (zoomingIn) {
progress += tpf * zoomSpeed;
if (progress > 1) progress = 1;
float y = lerp(-Y,0, easeOut(progress));
spatial.setLocalTranslation(0,y,0);
if (progress >= 1) {
zoomingIn = false;
zoomingOut = true;
progress = 0;
}
} else if (zoomingOut) {
progress += tpf * zoomSpeed;
float y = lerp(0,Y, easeIn(progress));
spatial.setLocalTranslation(0,y,0);
if (progress > 1) {
zoomingIn = false;
spatial.removeFromParent();
state = null;
}
}
}
public void shield(){
if(state != null) throw new RuntimeException("another state is avtive");
state = BonusCard.SHIELD;
zoomingIn = true;
zoomingOut = false;
progress = 0;
spatial.setLocalScale(1f);
}
public void swap(){
if(state != null) throw new RuntimeException("another state is avtive");
spatial.setLocalScale(3);
state = BonusCard.SWAP;
progress = -0.2f;
}
public void turbo(){
if(state != null) throw new RuntimeException("another state is avtive");
spatial.setLocalScale(2);
state = BonusCard.TURBO;
zoomingIn = true;
zoomingOut = false;
progress = 0;
}
private static float lerp(float start, float end, float t) {
return (1 - t) * start + t * end;
}
private static float easeOut(float t) {
return (float) Math.sqrt(1 - Math.pow(t - 1, 2));
}
private float easeInOut(float t) {
if(t>1) t=1;
return (float) -(Math.cos(Math.PI * t) - 1) / 2;
}
private float easeIn(float t) {
return t * t * t * t;
}
}

View File

@@ -1,82 +0,0 @@
package pp.mdga.client.gui;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
public class ZoomControl extends AbstractControl {
private boolean zoomingIn = false;
private boolean zoomingOut = false;
private float progress = 0;
private float zoomSpeed = 1f;
private float zoomFactor = 1f;
public ZoomControl(){}
public ZoomControl(float speed) {
zoomSpeed = speed;
}
@Override
public void setSpatial(Spatial spatial){
if(this.spatial == null && spatial != null){
super.setSpatial(spatial);
initSpatial();
}
}
private void initSpatial() {
zoomingIn = true;
}
@Override
protected void controlUpdate(float tpf) {
if (zoomingIn) {
progress += tpf * zoomSpeed;
if (progress > 1) progress = 1;
spatial.setLocalScale(lerp(0, zoomFactor, easeOut(progress)));
if (progress >= 1) {
zoomingIn = false;
zoomingOut = true;
progress = 0;
}
} else if (zoomingOut) {
progress += tpf * zoomSpeed;
spatial.setLocalScale(lerp(zoomFactor, 0, easeIn(progress)));
if (progress > 1) {
zoomingOut = false;
end();
}
}
}
private void end(){
spatial.removeFromParent();
spatial.removeControl(this);
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
}
private static float lerp(float start, float end, float t) {
return (1 - t) * start + t * end;
}
// private static float easeOut(float t) {
// return (float) Math.sqrt(1 - Math.pow(t - 1, 2));
// }
private float easeOut(float x) {
return x == 1 ? 1 : (float) (1 - Math.pow(2, -10 * x));
}
// private float easeIn(float t) {
// return t * t * t * t;
// }
private float easeIn(float x) {
return x == 0 ? 0 : (float) Math.pow(2, 10 * x - 10);
}
}

View File

@@ -1,4 +1,4 @@
package pp.mdga.client.board.Outline;
package pp.mdga.client.outline;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;

View File

@@ -1,4 +1,4 @@
package pp.mdga.client.board.Outline;
package pp.mdga.client.outline;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;

View File

@@ -1,4 +1,4 @@
package pp.mdga.client.board.Outline;
package pp.mdga.client.outline;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;

View File

@@ -1,4 +1,4 @@
package pp.mdga.client.board.Outline;
package pp.mdga.client.outline;
import com.jme3.asset.AssetManager;
import com.jme3.math.ColorRGBA;
@@ -7,6 +7,7 @@
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial;
import pp.mdga.client.MdgaApp;
public class SelectObjectOutliner {
@@ -19,14 +20,16 @@ public class SelectObjectOutliner {
private ViewPort outlineViewport = null;
// private OutlineFilter outlineFilter = null;
private OutlineProFilter outlineFilter = null;
private final MdgaApp app;
public SelectObjectOutliner(int width, FilterPostProcessor fpp, RenderManager renderManager, AssetManager assetManager, Camera cam) {
public SelectObjectOutliner(int width, FilterPostProcessor fpp, RenderManager renderManager, AssetManager assetManager, Camera cam, MdgaApp app) {
this.selected = false;
this.fpp = fpp;
this.renderManager = renderManager;
this.assetManager = assetManager;
this.cam = cam;
this.width = width;
this.app = app;
}
public void deselect(Spatial model) {
@@ -51,6 +54,7 @@ public void select(Spatial model, ColorRGBA color, int width) {
}
private void hideOutlineFilterEffect(Spatial model) {
// app.enqueue(() -> {
outlineFilter.setEnabled(false);
outlineFilter.getOutlinePreFilter().setEnabled(false);
fpp.removeFilter(outlineFilter);
@@ -58,9 +62,12 @@ private void hideOutlineFilterEffect(Spatial model) {
outlineViewport.clearProcessors();
renderManager.removePreView(outlineViewport);
outlineViewport = null;
// return null;
// });
}
private void showOutlineFilterEffect(Spatial model, int width, ColorRGBA color) {
// app.enqueue(() -> {
outlineViewport = renderManager.createPreView("outlineViewport", cam);
FilterPostProcessor outlineFpp = new FilterPostProcessor(assetManager);
@@ -70,11 +77,12 @@ private void showOutlineFilterEffect(Spatial model, int width, ColorRGBA color)
outlineViewport.attachScene(model);
outlineViewport.addProcessor(outlineFpp);
// outlineFilter = new OutlineFilter(outlinePreFilter);
outlineFilter = new OutlineProFilter(outlinePreFilter);
outlineFilter.setOutlineColor(color);
outlineFilter.setOutlineWidth(width);
fpp.addFilter(outlineFilter);
// return null;
// });
}
}

View File

@@ -2,8 +2,8 @@
import com.jme3.network.*;
import com.jme3.network.serializing.Serializer;
import pp.mdga.game.Game;
import pp.mdga.game.Player;
import com.jme3.network.serializing.serializers.EnumSerializer;
import pp.mdga.game.*;
import pp.mdga.message.client.*;
import pp.mdga.message.server.*;
import pp.mdga.server.ServerGameLogic;
@@ -14,6 +14,7 @@
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.LogManager;
@@ -25,6 +26,7 @@ public class MdgaServer implements MessageListener<HostedConnection>, Connection
private static final Logger LOGGER = System.getLogger(MdgaServer.class.getName());
private Server myServer;
private static int port;
private final ServerGameLogic logic;
private final BlockingQueue<ReceivedMessage> pendingMessages = new LinkedBlockingQueue<>();
@@ -40,32 +42,33 @@ public class MdgaServer implements MessageListener<HostedConnection>, Connection
}
/**
* Starts the Battleships server.
* Constructor.
*
* @param port as the port for this server
*/
public static void main(String[] args) {
new MdgaServer().run();
}
/**
* Creates a new MdgaServer.
*/
public MdgaServer() {
public MdgaServer(int port) {
MdgaServer.port = port;
LOGGER.log(Level.INFO, "Creating MdgaServer"); //NON-NLS
logic = new ServerGameLogic(this, new Game());
}
/**
*
*/
public void run() {
startServer();
this.connectionAdded(myServer, myServer.getConnection(0));
while (true)
processNextMessage();
}
/**
*
*/
private void startServer() {
try {
LOGGER.log(Level.INFO, "Starting server..."); //NON-NLS
myServer = Network.createServer(1234);
myServer = Network.createServer(port);
initializeSerializables();
myServer.start();
registerListeners();
@@ -86,6 +89,7 @@ private void processNextMessage() {
}
private void initializeSerializables() {
Serializer.registerClass(UUID.class, new UUIDSerializer());
Serializer.registerClass(AnimationEndMessage.class);
Serializer.registerClass(ClientStartGameMessage.class);
Serializer.registerClass(DeselectTSKMessage.class);
@@ -103,7 +107,6 @@ private void initializeSerializables() {
Serializer.registerClass(SelectCardMessage.class);
Serializer.registerClass(SelectedPiecesMessage.class);
Serializer.registerClass(SelectTSKMessage.class);
Serializer.registerClass(ActivePlayerMessage.class);
Serializer.registerClass(AnyPieceMessage.class);
Serializer.registerClass(BriefingMessage.class);
@@ -127,16 +130,29 @@ private void initializeSerializables() {
Serializer.registerClass(ReconnectBriefingMessage.class);
Serializer.registerClass(ResumeGameMessage.class);
Serializer.registerClass(ServerStartGameMessage.class);
Serializer.registerClass(ShutdownMessage.class);
Serializer.registerClass(StartPieceMessage.class);
Serializer.registerClass(UpdateReadyMessage.class);
Serializer.registerClass(UpdateTSKMessage.class);
Serializer.registerClass(WaitPieceMessage.class);
Serializer.registerClass(Player.class);
Serializer.registerClass(Statistic.class);
Serializer.registerClass(Board.class);
Serializer.registerClass(Node.class);
Serializer.registerClass(Piece.class);
Serializer.registerClass(BonusNode.class);
Serializer.registerClass(StartNode.class);
Serializer.registerClass(PlayerData.class);
Serializer.registerClass(HomeNode.class);
Serializer.registerClass(PlayerDataMessage.class);
Serializer.registerClass(StartBriefingMessage.class);
}
private void registerListeners() {
myServer.addMessageListener(this, AnimationEndMessage.class);
myServer.addMessageListener(this, ClientStartGameMessage.class);
myServer.addMessageListener(this, DeselectTSKMessage.class);
myServer.addMessageListener(this, DisconnectedMessage.class);
myServer.addMessageListener(this, ForceContinueGameMessage.class);
myServer.addMessageListener(this, StartGameMessage.class);
myServer.addMessageListener(this, JoinedLobbyMessage.class);
@@ -154,17 +170,41 @@ private void registerListeners() {
myServer.addConnectionListener(this);
}
/**
* This method will be used to receive network messages from the given source parameter.
* It will check if the given message parameter is a ClientMessage object. If yes it will call the messageReceived
* method with the casted ClientMessage object.
*
* @param source as the connection which sends the message as a HostedConnection object.
* @param message as the received message as a Message object.
*/
@Override
public void messageReceived(HostedConnection source, Message message) {
if (message instanceof ClientMessage) {
this.messageReceived(source, (ClientMessage) message);
}
}
public void messageReceived(HostedConnection source, ClientMessage message) {
/**
* This method will be used to received network messages from the given source parameter.
* It will add the given message parameter to the pendingMessage attribute of MdgaServer after creating
* a ReceivedMessage object with it and its id.
*
* @param source as the connection which sends the message as a HostedConnection object.
* @param message as the received message as a Message object.
*/
private void messageReceived(HostedConnection source, ClientMessage message) {
LOGGER.log(Level.INFO, "message received from {0}: {1}", source.getId(), message); //NON-NLS
pendingMessages.add(new ReceivedMessage(message, source.getId()));
}
@Override
public void connectionAdded(Server server, HostedConnection hostedConnection) {
LOGGER.log(Level.INFO, "new connection {0}", hostedConnection); //NON-NLS
// ToDo: Synchronize data between server and client.
logic.getGame().addPlayer(hostedConnection.getId(), new Player());
System.out.println("new connection " + hostedConnection); //NON-NLS
LOGGER.log(Level.DEBUG, "new connection {0}", hostedConnection); //NON-NLS
if (this.myServer.getConnections().size() == 1) {
this.logic.getGame().setHost(hostedConnection.getId());
}
}
@Override
@@ -203,6 +243,7 @@ public void exit(int exitValue) { //NON-NLS
* @param id the connection id
* @param message the message
*/
@Override
public void send(int id, ServerMessage message) {
if (myServer == null || !myServer.isRunning()) {
LOGGER.log(Level.ERROR, "no server running when trying to send {0}", message); //NON-NLS
@@ -221,15 +262,20 @@ public void send(int id, ServerMessage message) {
*
* @param message as the message which will be sent to all players as a ServerMessage.
*/
@Override
public void broadcast(ServerMessage message) {
for (Map.Entry<Integer, Player> entry : this.logic.getGame().getPlayers().entrySet()) {
this.send(entry.getKey(), message);
}
}
//TODO:
/**
* This method will be used to diconenect the client depending on the given id parameter.
*
* @param id as the connection id of the client as an Integer.
*/
@Override
public void messageReceived(HostedConnection source, Message m) {
public void disconnectClient(int id) {
this.myServer.getConnection(id).close("");
}
}

View File

@@ -0,0 +1,26 @@
package pp.mdga.client.server;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.UUID;
import com.jme3.network.serializing.Serializer;
public class UUIDSerializer extends Serializer
{
@Override
public <T> T readObject(ByteBuffer data, Class<T> c) throws IOException
{
byte[] uuid = new byte[36];
data.get(uuid);
return (T) UUID.fromString(new String(uuid));
}
@Override
public void writeObject(ByteBuffer buffer, Object object) throws IOException
{
UUID uuid = (UUID) object;
buffer.put(uuid.toString().getBytes());
}
}

View File

@@ -9,9 +9,19 @@
import pp.mdga.client.button.ButtonLeft;
import pp.mdga.client.button.ButtonRight;
import pp.mdga.client.gui.GuiHandler;
import pp.mdga.game.BonusCard;
import pp.mdga.game.Color;
import pp.mdga.notification.AcquireCardNotification;
import pp.mdga.notification.ActivePlayerNotification;
import pp.mdga.notification.DiceNowNotification;
import pp.mdga.notification.GameNotification;
import pp.mdga.notification.MovePieceNotification;
import pp.mdga.notification.PlayerInGameNotification;
import pp.mdga.notification.RollDiceNotification;
import pp.mdga.notification.SelectableCardsNotification;
import pp.mdga.notification.SelectableMoveNotification;
import pp.mdga.notification.SelectableSwapNotification;
import pp.mdga.notification.ShieldActiveNotification;
import java.util.ArrayList;
import java.util.List;
@@ -22,8 +32,6 @@ public class GameView extends MdgaView {
private CameraHandler camera;
private GuiHandler guiHandler;
private ButtonRight cheatButton; //TODO
private ButtonLeft leaveButton;
private ButtonRight confirmButton;
@@ -34,7 +42,6 @@ public class GameView extends MdgaView {
public GameView(MdgaApp app) {
super(app);
cheatButton = new ButtonRight(app, overlayNode, () -> app.getModelSynchronize().enter(MdgaState.CEREMONY), "CHEAT", 1);
leaveButton = new ButtonLeft(app, overlayNode, () -> app.getModelSynchronize().leave(), "Spiel verlassen", 1);
confirmButton = new ButtonRight(app, guiNode, () -> app.getModelSynchronize().confirm(), "Bestätigen", 1);
@@ -43,14 +50,16 @@ public GameView(MdgaApp app) {
this.camera = new CameraHandler(app, fpp);
this.boardHandler = new BoardHandler(app, rootNode, fpp);
guiHandler = new GuiHandler(app, guiNode, ownColor);
guiHandler = new GuiHandler(app, guiNode);
}
@Override
public void onEnter() {
camera.init();
setOwnColor(Color.AIRFORCE);
camera.init(ownColor);
boardHandler.init();
guiHandler.init();
guiHandler.init(ownColor);
app.getViewPort().addProcessor(fpp);
@@ -60,9 +69,10 @@ public void onEnter() {
@Override
public void onLeave() {
camera.shutdown();
boardHandler.shutdown();
guiHandler.shutdown();
camera.shutdown();
confirmButton.hide();
@@ -78,7 +88,6 @@ public void onUpdate(float tpf) {
protected void onEnterOverlay(Overlay overlay) {
if(overlay == Overlay.SETTINGS) {
leaveButton.show();
cheatButton.show();
}
}
@@ -86,7 +95,6 @@ protected void onEnterOverlay(Overlay overlay) {
protected void onLeaveOverlay(Overlay overlay) {
if(overlay == Overlay.SETTINGS) {
leaveButton.hide();
cheatButton.hide();
}
}

View File

@@ -228,7 +228,7 @@ private void toggleTsk(Color color) {
app.getModelSynchronize().selectTsk(color);
break;
case SELF:
app.getModelSynchronize().unselectTsk();
app.getModelSynchronize().unselectTsk(color);
break;
case OTHER:
//nothing

View File

@@ -7,6 +7,9 @@
import pp.mdga.client.dialog.JoinDialog;
import pp.mdga.client.dialog.StartDialog;
import java.net.Inet4Address;
import java.net.UnknownHostException;
public class MainView extends MdgaView {
private enum SubState {
HOST,
@@ -99,12 +102,21 @@ private void tryHost() {
if(port >= 1 && port <= 65535) {
app.getModelSynchronize().setName(startDialog.getName());
hostDialog.hostServer();
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
hostDialog.connectServerAsClient();
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
app.getModelSynchronize().setHost(port);
//app.getAcousticHandler().playSound(MdgaSound.WRONG_INPUT);
return;
}
} catch (NumberFormatException e) {
//nothing
} catch (NumberFormatException ignored) {
}
hostDialog.resetPort();
@@ -114,7 +126,7 @@ private void tryHost() {
private void tryJoin() {
int port = 0;
String ip = joinDialog.getIpt();
String portText = hostDialog.getPort();
String portText = joinDialog.getPort();
try {
// Validate the port
@@ -122,10 +134,16 @@ private void tryJoin() {
if (port < 1 || port > 65535) {
throw new IllegalArgumentException("Invalid port");
}
joinDialog.setPortNumber(port);
// Validate the IP address
if (isValidIpAddress(ip)) {
app.getModelSynchronize().setName(startDialog.getName());
joinDialog.setHostname(ip);
joinDialog.connectToServer();
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
app.getModelSynchronize().setJoin(ip, port);
return;
}
@@ -133,7 +151,7 @@ private void tryJoin() {
// Invalid input, fall through to reset
}
hostDialog.resetPort();
joinDialog.resetPort();
joinDialog.resetIp();
app.getAcousticHandler().playSound(MdgaSound.WRONG_INPUT);
}

View File

@@ -51,8 +51,8 @@ public void enter() {
}
public void leave() {
app.getRootNode().detachChild(rootNode);
app.getGuiNode().detachChild(guiNode);
onLeave();
settingsButton.hide();
@@ -60,7 +60,8 @@ public void leave() {
pressEscape();
}
onLeave();
app.getRootNode().detachChild(rootNode);
app.getGuiNode().detachChild(guiNode);
}
public void enterOverlay(Overlay overlay) {
@@ -83,10 +84,14 @@ public void update(float tpf) {
}
protected abstract void onEnter();
protected abstract void onLeave();
protected void onUpdate(float tpf) {}
protected void onUpdate(float tpf) {
}
protected abstract void onEnterOverlay(Overlay overlay);
protected abstract void onLeaveOverlay(Overlay overlay);
protected Geometry createBackground(String texturePath) {

View File

@@ -46,17 +46,21 @@ cir 5,4 0
cir 5,5 0
#Assets
jet -10,-1 45
ship 11,0 169
big_tent -9,-7 130
big_tent 7,-10 225
jet -12,0 45
jet -17,-2 55
small_tent -9,7 45
small_tent -10,5 60
small_tent -7,8 30
ship 11,0 169
small_tent 6,8 340
small_tent 8,7 320
big_tent -10,-9 130
big_tent 9,-10 225
radar 0,10 -20
small_tent 6,8 190
small_tent 8,7 160
tank -1,-10 135
tank 0,-18 180
tank 3,-18 180
tank -3,-18 180
#Yellow (CIR) wait Node
node_wait_yellow 4,5 0

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 MiB

After

Width:  |  Height:  |  Size: 6.8 MiB

View File

@@ -1,582 +0,0 @@
# Blender 3.6.5
# www.blender.org
o Shield.001
v 0.331177 0.029684 0.498915
v 0.331177 0.029684 -0.019826
v 0.324814 0.029684 -0.113625
v 0.305971 0.029684 -0.203821
v 0.275372 0.029684 -0.286945
v 0.234192 0.029684 -0.359804
v 0.184014 0.029684 -0.419598
v 0.126767 0.029684 -0.464029
v 0.064650 0.029684 -0.491389
v 0.000000 0.029684 -0.500628
v 0.000000 0.029684 0.498915
v 0.318062 -0.020828 0.485801
v 0.318062 -0.020828 -0.019381
v 0.311791 -0.020828 -0.111832
v 0.293331 -0.020828 -0.200195
v 0.263432 -0.020828 -0.281417
v 0.223370 -0.020828 -0.352299
v 0.174868 -0.020828 -0.410095
v 0.120002 -0.020828 -0.452678
v 0.061012 -0.020828 -0.478662
v 0.000000 -0.020828 0.498915
v 0.331177 -0.020828 0.498915
v 0.000000 -0.020828 -0.500628
v 0.331177 -0.020828 -0.019826
v 0.324814 -0.020828 -0.113625
v 0.305971 -0.020828 -0.203821
v 0.275372 -0.020828 -0.286945
v 0.234192 -0.020828 -0.359804
v 0.184014 -0.020828 -0.419598
v 0.126767 -0.020828 -0.464029
v 0.064650 -0.020828 -0.491389
v 0.000000 -0.020828 -0.485507
v 0.000000 -0.020828 0.485800
v 0.000000 -0.020828 0.000147
v 0.318062 -0.030185 0.485801
v 0.318062 -0.030185 -0.019381
v 0.311791 -0.030185 -0.111832
v 0.293331 -0.030185 -0.200195
v 0.263432 -0.030185 -0.281417
v 0.223370 -0.030185 -0.352299
v 0.174868 -0.030185 -0.410095
v 0.120002 -0.030185 -0.452678
v 0.061012 -0.030185 -0.478662
v 0.000000 -0.030185 0.498915
v 0.331177 -0.030185 0.498915
v 0.000000 -0.030185 -0.500628
v 0.331177 -0.030185 -0.019826
v 0.324814 -0.030185 -0.113625
v 0.305971 -0.030185 -0.203821
v 0.275372 -0.030185 -0.286945
v 0.234192 -0.030185 -0.359804
v 0.184014 -0.030185 -0.419598
v 0.126767 -0.030185 -0.464029
v 0.064650 -0.030185 -0.491389
v 0.000000 -0.030185 -0.485507
v 0.000000 -0.030185 0.485800
v -0.331177 0.029684 0.498915
v -0.331177 0.029684 -0.019826
v -0.324814 0.029684 -0.113625
v -0.305971 0.029684 -0.203821
v -0.275372 0.029684 -0.286945
v -0.234192 0.029684 -0.359804
v -0.184014 0.029684 -0.419598
v -0.126767 0.029684 -0.464029
v -0.064650 0.029684 -0.491389
v -0.318062 -0.020828 0.485801
v -0.318062 -0.020828 -0.019381
v -0.311791 -0.020828 -0.111832
v -0.293331 -0.020828 -0.200195
v -0.263432 -0.020828 -0.281417
v -0.223370 -0.020828 -0.352299
v -0.174868 -0.020828 -0.410095
v -0.120002 -0.020828 -0.452678
v -0.061012 -0.020828 -0.478662
v -0.331177 -0.020828 0.498915
v -0.331177 -0.020828 -0.019826
v -0.324814 -0.020828 -0.113625
v -0.305971 -0.020828 -0.203821
v -0.275372 -0.020828 -0.286945
v -0.234192 -0.020828 -0.359804
v -0.184014 -0.020828 -0.419598
v -0.126767 -0.020828 -0.464029
v -0.064650 -0.020828 -0.491389
v -0.318062 -0.030185 0.485801
v -0.318062 -0.030185 -0.019381
v -0.311791 -0.030185 -0.111832
v -0.293331 -0.030185 -0.200195
v -0.263432 -0.030185 -0.281417
v -0.223370 -0.030185 -0.352299
v -0.174868 -0.030185 -0.410095
v -0.120002 -0.030185 -0.452678
v -0.061012 -0.030185 -0.478662
v -0.331177 -0.030185 0.498915
v -0.331177 -0.030185 -0.019826
v -0.324814 -0.030185 -0.113625
v -0.305971 -0.030185 -0.203821
v -0.275372 -0.030185 -0.286945
v -0.234192 -0.030185 -0.359804
v -0.184014 -0.030185 -0.419598
v -0.126767 -0.030185 -0.464029
v -0.064650 -0.030185 -0.491389
vn 0.6574 0.6894 -0.3043
vn 0.8218 -0.0000 -0.5698
vn 0.5971 0.6870 -0.4141
vn -0.5120 -0.0000 0.8590
vn -0.6935 -0.0000 0.7205
vn 0.7153 0.6919 -0.0985
vn 0.9994 -0.0000 -0.0339
vn 0.9907 -0.0000 -0.1364
vn 1.0000 -0.0000 -0.0000
vn 0.2021 0.6777 -0.7070
vn 0.1415 -0.0000 -0.9899
vn 0.1046 0.6729 -0.7323
vn 0.6935 -0.0000 -0.7205
vn 0.5059 0.6840 -0.5256
vn 0.9612 -0.0000 -0.2757
vn 0.6949 0.6910 -0.1993
vn 0.0437 0.7064 0.7064
vn 0.7142 0.6996 -0.0242
vn -0.0000 0.7071 0.7071
vn -0.0000 -0.0000 1.0000
vn 0.5120 -0.0000 -0.8590
vn 0.3751 0.6806 -0.6294
vn 0.9075 -0.0000 -0.4201
vn 0.2748 -0.0000 -0.9615
vn -0.0000 -1.0000 -0.0000
vn -0.8218 -0.0000 0.5698
vn -0.9075 -0.0000 0.4201
vn -0.1115 -0.0000 0.9938
vn -0.2603 -0.0000 0.9655
vn -0.9612 -0.0000 0.2757
vn -0.9994 -0.0000 0.0339
vn -1.0000 -0.0000 -0.0000
vn -0.9907 -0.0000 0.1364
vn -0.0000 -0.0000 -1.0000
vn -0.8218 -0.0000 -0.5698
vn -0.9075 -0.0000 -0.4201
vn 0.6935 -0.0000 0.7205
vn 0.5120 -0.0000 0.8590
vn -0.9907 -0.0000 -0.1364
vn -0.9994 -0.0000 -0.0339
vn -0.1415 -0.0000 -0.9899
vn -0.2748 -0.0000 -0.9615
vn -0.6935 -0.0000 -0.7205
vn -0.9612 -0.0000 -0.2757
vn -0.5120 -0.0000 -0.8590
vn 0.8218 -0.0000 0.5698
vn 0.9075 -0.0000 0.4201
vn 0.2603 -0.0000 0.9655
vn 0.1115 -0.0000 0.9938
vn 0.9612 -0.0000 0.2757
vn 0.9994 -0.0000 0.0339
vn 0.9907 -0.0000 0.1364
vn -0.0000 1.0000 -0.0000
vt 0.852452 0.926767
vt 0.863932 0.943326
vt 0.852452 0.943326
vt 0.416217 0.784524
vt 0.414091 0.772054
vt 0.416217 0.772054
vt 0.852452 0.887374
vt 0.863932 0.866055
vt 0.863932 0.887374
vt 0.550685 0.744478
vt 0.539205 0.630661
vt 0.539205 0.741041
vt 0.348119 0.828570
vt 0.336639 0.813877
vt 0.348119 0.813877
vt 0.863932 0.956916
vt 0.852452 0.956916
vt 0.863932 0.907874
vt 0.852452 0.907874
vt 0.852452 0.748155
vt 0.852452 0.866055
vt 0.643871 0.823767
vt 0.632391 0.748497
vt 0.643871 0.748497
vt 0.348119 0.855700
vt 0.336639 0.842689
vt 0.348119 0.842689
vt 0.863932 0.926767
vt 0.336639 0.828570
vt 0.980591 0.564665
vt 0.984787 0.584748
vt 0.913922 0.610199
vt 0.866059 0.956916
vt 0.537078 0.741041
vt 0.537078 0.744478
vt 0.630264 0.517300
vt 0.632391 0.530436
vt 0.630264 0.530436
vt 0.866059 0.943326
vt 0.632391 0.546546
vt 0.630264 0.546546
vt 0.416217 0.811798
vt 0.414091 0.797931
vt 0.416217 0.797931
vt 0.866059 0.926767
vt 0.632391 0.565007
vt 0.630264 0.565007
vt 0.334512 0.813877
vt 0.434232 0.744478
vt 0.506522 0.741497
vt 0.509502 0.744478
vt 0.505096 0.605666
vt 0.509502 0.626577
vt 0.506522 0.626678
vt 0.500901 0.585583
vt 0.508056 0.605259
vt 0.494105 0.567123
vt 0.503773 0.584759
vt 0.485000 0.551012
vt 0.496819 0.565866
vt 0.473976 0.537876
vt 0.487459 0.549307
vt 0.461506 0.528198
vt 0.476055 0.535717
vt 0.448099 0.522293
vt 0.463044 0.525618
vt 0.434232 0.517300
vt 0.434232 0.520737
vt 0.336639 0.855700
vt 0.334512 0.842689
vt 0.632391 0.823767
vt 0.630264 0.748497
vt 0.414091 0.784524
vt 0.334512 0.828570
vt 0.539205 0.520281
vt 0.539205 0.517300
vt 0.537078 0.517300
vt 0.630264 0.606102
vt 0.632391 0.720921
vt 0.630264 0.720921
vt 0.863932 0.748155
vt 0.866059 0.866055
vt 0.630264 0.585090
vt 0.632391 0.606102
vt 0.866059 0.887374
vt 0.182971 0.916633
vt 0.180845 0.844343
vt 0.182971 0.844343
vt 0.632391 0.585090
vt 0.866059 0.907874
vt 0.600561 0.530890
vt 0.589081 0.547450
vt 0.589081 0.530890
vt 0.414091 0.851542
vt 0.416217 0.839072
vt 0.416217 0.851542
vt 0.600561 0.586842
vt 0.589081 0.608161
vt 0.589081 0.586842
vt 0.348119 0.799183
vt 0.600561 0.517300
vt 0.589081 0.517300
vt 0.600561 0.566342
vt 0.589081 0.566342
vt 0.600561 0.608161
vt 0.589081 0.726061
vt 0.643871 0.899038
vt 0.336639 0.785065
vt 0.348119 0.772054
vt 0.348119 0.785065
vt 0.600561 0.547450
vt 0.336639 0.799183
vt 0.913922 0.720579
vt 0.841633 0.720579
vt 0.602688 0.517300
vt 0.893635 0.938640
vt 0.895762 0.951776
vt 0.893635 0.951776
vt 0.602688 0.530890
vt 0.895762 0.922530
vt 0.893635 0.922530
vt 0.414091 0.825665
vt 0.416217 0.825665
vt 0.602688 0.547450
vt 0.895762 0.904069
vt 0.893635 0.904069
vt 0.361942 0.741497
vt 0.434232 0.741497
vt 0.358961 0.626577
vt 0.363367 0.605666
vt 0.361942 0.626678
vt 0.360408 0.605259
vt 0.367563 0.585583
vt 0.364690 0.584759
vt 0.374359 0.567123
vt 0.371645 0.565866
vt 0.383464 0.551012
vt 0.381004 0.549307
vt 0.394488 0.537876
vt 0.392409 0.535717
vt 0.406958 0.528198
vt 0.405420 0.525618
vt 0.420365 0.522293
vt 0.419538 0.519400
vt 0.334512 0.785065
vt 0.336639 0.772054
vt 0.630264 0.899038
vt 0.632391 0.899038
vt 0.334512 0.799183
vt 0.895762 0.748155
vt 0.893635 0.862974
vt 0.893635 0.748155
vt 0.602688 0.608161
vt 0.600561 0.726061
vt 0.895762 0.862974
vt 0.893635 0.883986
vt 0.602688 0.586842
vt 0.182971 0.772054
vt 0.602688 0.566342
vt 0.550685 0.517300
vt 0.539205 0.744478
vt 0.986212 0.605761
vt 0.986212 0.720579
vt 0.913922 0.499819
vt 0.941197 0.507280
vt 0.927789 0.501375
vt 0.953667 0.516959
vt 0.964690 0.530095
vt 0.973795 0.546205
vt 0.632391 0.517300
vt 0.414091 0.811798
vt 0.448925 0.519400
vt 0.334512 0.855700
vt 0.630264 0.823767
vt 0.537078 0.520281
vt 0.866059 0.748155
vt 0.180845 0.916633
vt 0.414091 0.839072
vt 0.841633 0.605761
vt 0.843058 0.584748
vt 0.847254 0.564665
vt 0.854049 0.546205
vt 0.863155 0.530095
vt 0.874178 0.516959
vt 0.886648 0.507280
vt 0.900055 0.501375
vt 0.895762 0.938640
vt 0.358961 0.744478
vt 0.334512 0.772054
vt 0.602688 0.726061
vt 0.895762 0.883986
vt 0.180845 0.772054
vt 0.591727 0.581771
vt 0.592669 0.565234
vt 0.601155 0.522724
vt 0.866647 0.820446
vt 0.866647 0.893741
vt 0.855496 0.893741
vt 0.593938 0.550739
vt 0.595485 0.538844
vt 0.597249 0.530005
vt 0.599163 0.524562
vt 0.601155 0.721574
vt 0.590950 0.618375
vt 0.591146 0.599714
vt 0.590950 0.721574
vt 0.855496 0.752511
vt 0.857673 0.753816
vt 0.859764 0.757682
vt 0.864768 0.782703
vt 0.863381 0.772408
vt 0.861692 0.763960
vt 0.865798 0.794448
vt 0.866433 0.807192
s 1
f 5/1/1 28/2/2 6/3/3
f 19/4/4 41/5/5 18/6/5
f 3/7/6 24/8/7 25/9/8
f 10/10/9 34/11/9 32/12/9
f 9/13/10 23/14/11 10/15/12
f 6/3/3 29/16/13 7/17/14
f 3/7/6 26/18/15 4/19/16
f 1/20/17 24/8/7 2/21/18
f 11/22/19 22/23/20 1/24/17
f 7/25/14 30/26/21 8/27/22
f 4/19/16 27/28/23 5/1/1
f 8/27/22 31/29/24 9/13/10
f 15/30/25 14/31/25 34/32/25
f 28/2/2 52/33/13 29/16/13
f 32/12/9 55/34/9 46/35/9
f 18/36/5 40/37/26 17/38/26
f 27/28/23 51/39/2 28/2/2
f 17/38/26 39/40/27 16/41/27
f 32/42/28 43/43/29 20/44/29
f 26/18/15 50/45/23 27/28/23
f 16/41/27 38/46/30 15/47/30
f 31/29/24 46/48/11 23/14/11
f 44/49/25 35/50/25 45/51/25
f 37/52/25 47/53/25 36/54/25
f 38/55/25 48/56/25 37/52/25
f 39/57/25 49/58/25 38/55/25
f 40/59/25 50/60/25 39/57/25
f 41/61/25 51/62/25 40/59/25
f 42/63/25 52/64/25 41/61/25
f 43/65/25 53/66/25 42/63/25
f 35/50/25 47/53/25 45/51/25
f 46/67/25 43/65/25 55/68/25
f 29/69/13 53/70/21 30/26/21
f 21/71/20 45/72/20 22/23/20
f 19/4/4 43/43/29 42/73/4
f 30/26/21 54/74/24 31/29/24
f 33/75/9 21/76/9 44/77/9
f 13/78/31 35/79/32 12/80/32
f 22/81/9 47/82/7 24/8/7
f 14/83/33 36/84/31 13/78/31
f 24/8/7 48/85/8 25/9/8
f 12/86/34 56/87/34 33/88/34
f 15/47/30 37/89/33 14/83/33
f 25/9/8 49/90/15 26/18/15
f 80/91/35 61/92/36 62/93/35
f 90/94/37 73/95/38 72/96/37
f 77/97/39 58/98/40 59/99/39
f 23/14/41 65/100/42 10/15/41
f 81/101/43 62/93/35 63/102/43
f 78/103/44 59/99/39 60/104/44
f 76/105/40 57/106/32 58/98/40
f 57/107/20 21/71/20 11/22/20
f 82/108/45 63/109/43 64/110/45
f 79/111/36 60/104/44 61/92/36
f 83/112/42 64/110/45 65/100/42
f 34/32/25 33/113/25 66/114/25
f 99/115/43 80/91/35 81/101/43
f 71/116/46 90/117/37 72/118/37
f 98/119/35 79/111/36 80/91/35
f 88/120/47 71/116/46 70/121/47
f 92/122/48 32/42/49 74/123/48
f 97/124/36 78/103/44 79/111/36
f 87/125/50 70/121/47 69/126/50
f 46/48/41 83/112/42 23/14/41
f 44/49/25 84/127/25 56/128/25
f 94/129/25 86/130/25 85/131/25
f 95/132/25 87/133/25 86/130/25
f 96/134/25 88/135/25 87/133/25
f 97/136/25 89/137/25 88/135/25
f 98/138/25 90/139/25 89/137/25
f 99/140/25 91/141/25 90/139/25
f 100/142/25 92/143/25 91/141/25
f 84/127/25 94/129/25 85/131/25
f 46/67/25 92/143/25 101/144/25
f 100/145/45 81/146/43 82/108/45
f 93/147/20 21/71/20 75/148/20
f 73/95/38 92/122/48 74/123/48
f 101/149/42 82/108/45 83/112/42
f 84/150/9 67/151/51 66/152/9
f 94/153/40 75/154/32 76/105/40
f 85/155/51 68/156/52 67/151/51
f 95/157/39 76/105/40 77/97/39
f 56/87/34 66/158/34 33/88/34
f 68/156/52 87/125/50 69/126/50
f 96/159/44 77/97/39 78/103/44
f 5/1/1 27/28/23 28/2/2
f 19/4/4 42/73/4 41/5/5
f 3/7/6 2/21/18 24/8/7
f 21/76/9 33/75/9 11/160/9
f 11/160/9 34/11/9 10/10/9
f 10/10/9 32/12/9 23/161/9
f 34/11/9 11/160/9 33/75/9
f 9/13/10 31/29/24 23/14/11
f 6/3/3 28/2/2 29/16/13
f 3/7/6 25/9/8 26/18/15
f 1/20/9 22/81/9 24/8/7
f 11/22/19 21/71/20 22/23/20
f 7/25/14 29/69/13 30/26/21
f 4/19/16 26/18/15 27/28/23
f 8/27/22 30/26/21 31/29/24
f 14/31/25 13/162/25 34/32/25
f 13/162/25 12/163/25 34/32/25
f 12/163/25 33/113/25 34/32/25
f 34/32/25 32/164/25 19/165/25
f 32/164/25 20/166/25 19/165/25
f 34/32/25 19/165/25 18/167/25
f 18/167/25 17/168/25 34/32/25
f 17/168/25 16/169/25 34/32/25
f 16/169/25 15/30/25 34/32/25
f 28/2/2 51/39/2 52/33/13
f 32/12/9 46/35/9 23/161/9
f 18/36/5 41/170/5 40/37/26
f 27/28/23 50/45/23 51/39/2
f 17/38/26 40/37/26 39/40/27
f 32/42/28 55/171/28 43/43/29
f 26/18/15 49/90/15 50/45/23
f 16/41/27 39/40/27 38/46/30
f 31/29/24 54/74/24 46/48/11
f 44/49/25 56/128/25 35/50/25
f 37/52/25 48/56/25 47/53/25
f 38/55/25 49/58/25 48/56/25
f 39/57/25 50/60/25 49/58/25
f 40/59/25 51/62/25 50/60/25
f 41/61/25 52/64/25 51/62/25
f 42/63/25 53/66/25 52/64/25
f 43/65/25 54/172/25 53/66/25
f 35/50/25 36/54/25 47/53/25
f 46/67/25 54/172/25 43/65/25
f 29/69/13 52/173/13 53/70/21
f 21/71/20 44/174/20 45/72/20
f 19/4/4 20/44/29 43/43/29
f 30/26/21 53/70/21 54/74/24
f 33/75/9 44/77/9 56/175/9
f 13/78/31 36/84/31 35/79/32
f 22/81/9 45/176/9 47/82/7
f 14/83/33 37/89/33 36/84/31
f 24/8/7 47/82/7 48/85/8
f 12/86/34 35/177/34 56/87/34
f 15/47/30 38/46/30 37/89/33
f 25/9/8 48/85/8 49/90/15
f 80/91/35 79/111/36 61/92/36
f 90/94/37 91/178/38 73/95/38
f 77/97/39 76/105/40 58/98/40
f 23/14/41 83/112/42 65/100/42
f 81/101/43 80/91/35 62/93/35
f 78/103/44 77/97/39 59/99/39
f 76/105/40 75/154/32 57/106/32
f 57/107/20 75/148/20 21/71/20
f 82/108/45 81/146/43 63/109/43
f 79/111/36 78/103/44 60/104/44
f 83/112/42 82/108/45 64/110/45
f 66/114/25 67/179/25 34/32/25
f 67/179/25 68/180/25 34/32/25
f 68/180/25 69/181/25 34/32/25
f 69/181/25 70/182/25 34/32/25
f 70/182/25 71/183/25 34/32/25
f 71/183/25 72/184/25 34/32/25
f 72/184/25 73/185/25 34/32/25
f 73/185/25 74/186/25 32/164/25
f 32/164/25 34/32/25 73/185/25
f 99/115/43 98/119/35 80/91/35
f 71/116/46 89/187/46 90/117/37
f 98/119/35 97/124/36 79/111/36
f 88/120/47 89/187/46 71/116/46
f 92/122/48 55/171/49 32/42/49
f 97/124/36 96/159/44 78/103/44
f 87/125/50 88/120/47 70/121/47
f 46/48/41 101/149/42 83/112/42
f 44/49/25 93/188/25 84/127/25
f 94/129/25 95/132/25 86/130/25
f 95/132/25 96/134/25 87/133/25
f 96/134/25 97/136/25 88/135/25
f 97/136/25 98/138/25 89/137/25
f 98/138/25 99/140/25 90/139/25
f 99/140/25 100/142/25 91/141/25
f 100/142/25 101/144/25 92/143/25
f 84/127/25 93/188/25 94/129/25
f 46/67/25 55/68/25 92/143/25
f 100/145/45 99/189/43 81/146/43
f 93/147/20 44/174/20 21/71/20
f 73/95/38 91/178/38 92/122/48
f 101/149/42 100/145/45 82/108/45
f 84/150/9 85/155/51 67/151/51
f 94/153/40 93/190/32 75/154/32
f 85/155/51 86/191/52 68/156/52
f 95/157/39 94/153/40 76/105/40
f 56/87/34 84/192/34 66/158/34
f 68/156/52 86/191/52 87/125/50
f 96/159/44 95/157/39 77/97/39
f 4/193/16 5/194/1 10/195/12
s 0
f 58/196/53 57/197/53 11/198/53
s 1
f 5/194/1 6/199/3 10/195/12
f 6/199/3 7/200/14 10/195/12
f 7/200/14 8/201/22 10/195/12
f 8/201/22 9/202/10 10/195/12
f 10/195/12 11/203/19 2/204/18
f 4/193/16 10/195/12 3/205/6
f 11/203/19 1/206/17 2/204/18
f 2/204/18 3/205/6 10/195/12
s 0
f 11/198/53 10/207/53 58/196/53
f 10/207/53 65/208/53 64/209/53
f 61/210/53 10/207/53 62/211/53
f 64/209/53 63/212/53 10/207/53
f 63/212/53 62/211/53 10/207/53
f 61/210/53 60/213/53 10/207/53
f 60/213/53 59/214/53 10/207/53
f 59/214/53 58/196/53 10/207/53

View File

@@ -1,616 +0,0 @@
# Blender 3.6.5
# www.blender.org
o shield
v 0.329363 -0.615745 1.151866
v -0.000000 -0.615745 0.415793
v -0.000000 -0.615745 1.151866
v 0.329363 -0.615745 1.092511
v -0.000000 -0.615745 0.415793
v 0.326010 -0.615745 0.996204
v 0.316022 -0.615745 0.901858
v 0.299600 -0.615745 0.811392
v 0.277080 -0.615745 0.726650
v 0.248920 -0.615745 0.649355
v 0.215692 -0.615745 0.581082
v 0.178074 -0.615745 0.523220
v 0.136832 -0.615745 0.476947
v 0.092804 -0.615745 0.443205
v 0.046887 -0.615745 0.422681
v -0.329363 -0.615745 1.151866
v -0.329363 -0.615745 1.092511
v -0.326010 -0.615745 0.996204
v -0.316022 -0.615745 0.901858
v -0.299600 -0.615745 0.811392
v -0.277080 -0.615745 0.726650
v -0.248920 -0.615745 0.649355
v -0.215692 -0.615745 0.581082
v -0.178075 -0.615745 0.523220
v -0.136832 -0.615745 0.476947
v -0.092804 -0.615745 0.443205
v -0.046887 -0.615745 0.422681
v 0.329363 -0.590929 1.151866
v -0.000000 -0.590929 1.151866
v -0.000000 -0.590929 0.415793
v -0.000000 -0.590929 0.415793
v 0.046887 -0.590929 0.422681
v 0.092804 -0.590929 0.443205
v 0.136832 -0.590929 0.476947
v 0.178074 -0.590929 0.523220
v 0.215692 -0.590929 0.581082
v 0.248920 -0.590929 0.649355
v 0.277080 -0.590929 0.726650
v 0.299600 -0.590929 0.811392
v 0.316022 -0.590929 0.901858
v 0.326010 -0.590929 0.996204
v 0.329363 -0.590929 1.092511
v -0.329363 -0.590929 1.151866
v -0.046887 -0.590929 0.422681
v -0.092804 -0.590929 0.443205
v -0.136832 -0.590929 0.476947
v -0.178075 -0.590929 0.523220
v -0.215692 -0.590929 0.581082
v -0.248920 -0.590929 0.649355
v -0.277080 -0.590929 0.726650
v -0.299600 -0.590929 0.811392
v -0.316022 -0.590929 0.901858
v -0.326010 -0.590929 0.996204
v -0.329363 -0.590929 1.092511
v -0.000000 0.740870 0.678108
v -0.000000 0.740870 1.005550
v 0.144537 0.726635 0.678108
v 0.144537 0.726635 1.005550
v 0.283519 0.684475 0.678108
v 0.283519 0.684475 1.005550
v 0.411605 0.616011 0.678108
v 0.411605 0.616011 1.005550
v 0.523874 0.523875 0.678108
v 0.523874 0.523874 1.005550
v 0.616011 0.411606 0.678108
v 0.616011 0.411606 1.005550
v 0.684475 0.283519 0.678108
v 0.684475 0.283519 1.005550
v 0.726635 0.144537 0.678108
v 0.726635 0.144537 1.005550
v 0.740870 0.000000 0.678108
v 0.740870 0.000000 1.005550
v 0.726635 -0.144537 0.678108
v 0.726635 -0.144537 1.005550
v 0.684475 -0.283519 0.678108
v 0.684475 -0.283519 1.005550
v 0.616011 -0.411605 0.678108
v 0.616011 -0.411605 1.005550
v 0.523874 -0.523874 0.678108
v 0.523874 -0.523874 1.005550
v 0.411605 -0.616011 0.678108
v 0.411605 -0.616011 1.005550
v -0.411606 -0.616011 0.678108
v -0.411606 -0.616011 1.005550
v -0.523875 -0.523874 0.678108
v -0.523875 -0.523875 1.005550
v -0.616011 -0.411606 0.678108
v -0.616011 -0.411606 1.005550
v -0.684475 -0.283519 0.678108
v -0.684475 -0.283519 1.005550
v -0.726635 -0.144537 0.678108
v -0.726635 -0.144537 1.005550
v -0.740870 -0.000000 0.678108
v -0.740870 -0.000000 1.005550
v -0.726635 0.144537 0.678108
v -0.726635 0.144537 1.005550
v -0.684475 0.283519 0.678108
v -0.684475 0.283519 1.005550
v -0.616011 0.411606 0.678108
v -0.616011 0.411606 1.005550
v -0.523875 0.523874 0.678108
v -0.523875 0.523874 1.005550
v -0.411606 0.616011 0.678108
v -0.411606 0.616011 1.005550
v -0.283519 0.684475 0.678108
v -0.283519 0.684475 1.005550
v -0.144537 0.726635 0.678108
v -0.144537 0.726635 1.005550
v -0.142275 0.717330 1.005550
v -0.142275 0.717330 0.678108
v -0.279082 0.675829 1.005550
v -0.279082 0.675829 0.678108
v -0.405165 0.608437 1.005550
v -0.405165 0.608437 0.678108
v -0.515677 0.517742 1.005550
v -0.515677 0.517742 0.678108
v -0.606372 0.407230 1.005550
v -0.606372 0.407230 0.678108
v -0.673764 0.281147 1.005550
v -0.673764 0.281147 0.678108
v -0.715265 0.144340 1.005550
v -0.715265 0.144340 0.678108
v -0.729277 0.002065 1.005550
v -0.729277 0.002065 0.678108
v -0.715265 -0.140210 1.005550
v -0.715265 -0.140210 0.678108
v -0.673764 -0.277017 1.005550
v -0.673764 -0.277017 0.678108
v -0.606372 -0.403100 1.005550
v -0.606372 -0.403100 0.678108
v -0.515677 -0.513612 1.005550
v -0.515677 -0.513612 0.678108
v -0.405165 -0.604307 0.678108
v -0.405165 -0.604307 1.005550
v 0.405165 -0.604307 1.005550
v 0.405165 -0.604307 0.678108
v 0.515677 -0.513612 1.005550
v 0.515677 -0.513612 0.678108
v 0.606372 -0.403100 1.005550
v 0.606372 -0.403100 0.678108
v 0.673764 -0.277017 1.005550
v 0.673764 -0.277017 0.678108
v 0.715264 -0.140210 1.005550
v 0.715264 -0.140210 0.678108
v 0.729277 0.002065 1.005550
v 0.729277 0.002065 0.678108
v 0.715264 0.144340 1.005550
v 0.715264 0.144340 0.678108
v 0.673764 0.281147 1.005550
v 0.673764 0.281147 0.678108
v 0.606372 0.407230 1.005550
v 0.606372 0.407230 0.678108
v 0.515677 0.517742 1.005550
v 0.515677 0.517742 0.678108
v 0.405165 0.608437 1.005550
v 0.405165 0.608437 0.678108
v 0.279082 0.675829 1.005550
v 0.279082 0.675829 0.678108
v -0.000000 0.731342 1.005550
v 0.142275 0.717330 1.005550
v 0.142275 0.717330 0.678108
v -0.000000 0.731342 0.678108
vn -0.0000 -0.0000 1.0000
vn -0.9839 -0.0000 0.1786
vn 0.9994 -0.0000 0.0348
vn -0.4081 -0.0000 0.9130
vn 0.7465 -0.0000 0.6654
vn -0.9944 -0.0000 0.1053
vn 1.0000 -0.0000 -0.0000
vn -0.6083 -0.0000 0.7937
vn 0.8384 -0.0000 0.5451
vn -0.9994 -0.0000 0.0348
vn -0.7465 -0.0000 0.6654
vn 0.8992 -0.0000 0.4376
vn -1.0000 -0.0000 -0.0000
vn -0.8384 -0.0000 0.5451
vn 0.9396 -0.0000 0.3423
vn -0.0000 -0.0000 -1.0000
vn -0.8992 -0.0000 0.4376
vn 0.9665 -0.0000 0.2568
vn 0.1453 -0.0000 0.9894
vn -0.9396 -0.0000 0.3423
vn 0.9839 -0.0000 0.1786
vn 0.4081 -0.0000 0.9130
vn -0.9665 -0.0000 0.2568
vn 0.9944 -0.0000 0.1053
vn -0.1453 -0.0000 0.9894
vn 0.6083 -0.0000 0.7937
vn 0.0980 0.9952 -0.0000
vn 0.2903 0.9569 -0.0000
vn 0.4714 0.8819 -0.0000
vn 0.6344 0.7730 -0.0000
vn 0.7730 0.6344 -0.0000
vn 0.8819 0.4714 -0.0000
vn 0.9569 0.2903 -0.0000
vn 0.9952 0.0980 -0.0000
vn 0.9952 -0.0980 -0.0000
vn 0.9569 -0.2903 -0.0000
vn 0.8819 -0.4714 -0.0000
vn 0.7730 -0.6344 -0.0000
vn 0.6344 -0.7730 -0.0000
vn -0.6344 -0.7730 -0.0000
vn -0.7730 -0.6344 -0.0000
vn -0.8819 -0.4714 -0.0000
vn -0.9569 -0.2903 -0.0000
vn -0.9952 -0.0980 -0.0000
vn -0.9952 0.0980 -0.0000
vn -0.9569 0.2903 -0.0000
vn -0.8819 0.4714 -0.0000
vn -0.7730 0.6344 -0.0000
vn -0.6344 0.7730 -0.0000
vn -0.4714 0.8819 -0.0000
vn -0.2903 0.9569 -0.0000
vn -0.0980 0.9952 -0.0000
vn -0.8761 -0.4821 -0.0000
vn 0.4714 -0.8819 -0.0000
vn -0.4714 -0.8819 -0.0000
vn 0.8761 -0.4821 -0.0000
vn -0.2903 -0.9569 -0.0000
vn -0.0980 -0.9952 -0.0000
vn 0.0980 -0.9952 -0.0000
vn 0.2903 -0.9569 -0.0000
vn -0.0000 1.0000 -0.0000
vn -0.0000 -1.0000 -0.0000
vn -0.4081 -0.0000 0.9129
vn 0.4081 -0.0000 0.9129
vt 0.500025 0.000000
vt 0.500000 0.000000
vt 0.500000 1.000000
vt 0.571179 0.009358
vt 0.979748 0.660349
vt 0.954818 0.537446
vt 0.994911 0.788524
vt 1.000000 0.919363
vt 0.640884 0.037241
vt 0.707722 0.083081
vt 0.770332 0.145946
vt 1.000000 1.000000
vt 0.827439 0.224555
vt 0.877881 0.317308
vt 0.920631 0.422318
vt 0.968750 0.500000
vt 1.000000 0.500000
vt 0.968750 1.000000
vt 0.937500 0.500000
vt 0.937500 1.000000
vt 0.906250 0.500000
vt 0.906250 1.000000
vt 0.875000 0.500000
vt 0.875000 1.000000
vt 0.843750 0.500000
vt 0.843750 1.000000
vt 0.812500 0.500000
vt 0.781250 1.000000
vt 0.781250 0.500000
vt 0.750000 1.000000
vt 0.750000 0.500000
vt 0.718750 0.500000
vt 0.687500 1.000000
vt 0.687500 0.500000
vt 0.656250 0.500000
vt 0.656250 1.000000
vt 0.625000 0.500000
vt 0.625000 1.000000
vt 0.593750 0.500000
vt 0.406250 0.500000
vt 0.375000 1.000000
vt 0.375000 0.500000
vt 0.343750 1.000000
vt 0.343750 0.500000
vt 0.312500 0.500000
vt 0.312500 1.000000
vt 0.281250 0.500000
vt 0.250000 1.000000
vt 0.250000 0.500000
vt 0.218750 0.500000
vt 0.218750 1.000000
vt 0.187500 0.500000
vt 0.187500 1.000000
vt 0.156250 0.500000
vt 0.125000 1.000000
vt 0.125000 0.500000
vt 0.093750 0.500000
vt 0.093750 1.000000
vt 0.062500 0.500000
vt 0.000000 1.000000
vt 0.031250 1.000000
vt 0.062500 1.000000
vt 0.031250 0.500000
vt 0.000000 0.500000
vt 0.281250 1.000000
vt 0.593750 1.000000
vt 0.156250 1.000000
vt 0.718750 1.000000
vt 0.812500 1.000000
vt 0.406250 1.000000
s 0
f 5/1/1 2/2/1 3/3/1
f 2/2/1 5/1/1 27/4/1
f 30/2/1 31/1/1 32/4/1
f 31/1/1 30/2/1 29/3/1
f 7/5/2 39/6/2 8/6/2
f 18/7/3 54/8/3 17/8/3
f 15/4/4 33/9/4 32/4/4
f 25/10/5 47/11/5 24/11/5
f 6/7/6 40/5/6 7/5/6
f 17/8/7 43/12/7 16/12/7
f 14/9/8 34/10/8 33/9/8
f 24/11/9 48/13/9 23/13/9
f 4/8/10 41/7/10 6/7/10
f 12/11/11 34/10/11 13/10/11
f 23/13/12 49/14/12 22/14/12
f 1/12/13 42/8/13 4/8/13
f 11/13/14 35/11/14 12/11/14
f 22/14/15 50/15/15 21/15/15
f 3/3/16 28/12/16 1/12/16
f 16/12/16 29/3/16 3/3/16
f 10/14/17 36/13/17 11/13/17
f 21/15/18 51/6/18 20/6/18
f 5/1/19 44/4/19 27/4/19
f 9/15/20 37/14/20 10/14/20
f 20/6/21 52/5/21 19/5/21
f 26/9/22 44/4/22 45/9/22
f 8/6/23 38/15/23 9/15/23
f 19/5/24 53/7/24 18/7/24
f 15/4/25 31/1/25 5/1/25
f 25/10/26 45/9/26 46/10/26
f 56/12/27 57/16/27 55/17/27
f 58/18/28 59/19/28 57/16/28
f 60/20/29 61/21/29 59/19/29
f 62/22/30 63/23/30 61/21/30
f 64/24/31 65/25/31 63/23/31
f 66/26/32 67/27/32 65/25/32
f 67/27/33 70/28/33 69/29/33
f 69/29/34 72/30/34 71/31/34
f 72/30/35 73/32/35 71/31/35
f 73/32/36 76/33/36 75/34/36
f 76/33/37 77/35/37 75/34/37
f 78/36/38 79/37/38 77/35/38
f 80/38/39 81/39/39 79/37/39
f 83/40/40 86/41/40 85/42/40
f 85/42/41 88/43/41 87/44/41
f 88/43/42 89/45/42 87/44/42
f 90/46/43 91/47/43 89/45/43
f 91/47/44 94/48/44 93/49/44
f 94/48/45 95/50/45 93/49/45
f 96/51/46 97/52/46 95/50/46
f 98/53/47 99/54/47 97/52/47
f 99/54/48 102/55/48 101/56/48
f 102/55/49 103/57/49 101/56/49
f 104/58/50 105/59/50 103/57/50
f 56/60/1 109/61/1 159/60/1
f 106/62/51 107/63/51 105/59/51
f 108/61/52 55/64/52 107/63/52
f 94/48/1 125/65/1 123/48/1
f 64/24/1 151/26/1 66/26/1
f 82/66/53 136/39/53 81/39/53
f 80/38/1 135/66/1 82/66/1
f 79/37/16 136/39/16 138/37/16
f 88/43/1 131/41/1 129/43/1
f 87/44/16 132/42/16 85/42/16
f 104/58/1 115/55/1 113/58/1
f 103/57/16 116/56/16 101/56/16
f 58/18/1 157/20/1 60/20/1
f 102/55/1 117/67/1 115/55/1
f 74/68/1 141/33/1 76/33/1
f 95/50/16 124/49/16 93/49/16
f 68/69/1 147/28/1 70/28/1
f 56/12/1 160/18/1 58/18/1
f 72/30/1 143/68/1 74/68/1
f 97/52/16 122/50/16 95/50/16
f 96/51/1 123/48/1 121/51/1
f 112/59/54 113/58/54 114/57/54
f 118/54/37 119/53/37 120/52/37
f 126/47/34 123/48/34 125/65/34
f 130/44/31 131/41/31 132/42/31
f 138/37/48 139/36/48 140/35/48
f 144/32/45 145/30/45 146/31/45
f 150/27/42 151/26/42 152/25/42
f 156/21/55 157/20/55 158/19/55
f 93/49/16 126/47/16 91/47/16
f 60/20/1 155/22/1 62/22/1
f 77/35/16 138/37/16 140/35/16
f 126/47/33 127/46/33 128/45/33
f 128/45/32 129/43/32 130/44/32
f 59/19/16 156/21/16 158/19/16
f 86/41/1 134/70/1 131/41/1
f 67/27/16 148/29/16 150/27/16
f 132/42/30 134/70/30 133/40/30
f 92/65/1 127/46/1 125/65/1
f 136/39/49 137/38/49 138/37/49
f 76/33/1 139/36/1 78/36/1
f 85/42/16 133/40/16 83/40/16
f 140/35/47 141/33/47 142/34/47
f 65/25/16 150/27/16 152/25/16
f 144/32/46 141/33/46 143/68/46
f 75/34/16 140/35/16 142/34/16
f 55/64/16 110/63/16 107/63/16
f 99/54/16 120/52/16 97/52/16
f 83/40/56 134/70/56 84/70/56
f 146/31/44 147/28/44 148/29/44
f 55/17/16 161/16/16 162/17/16
f 108/61/1 111/62/1 109/61/1
f 148/29/43 149/69/43 150/27/43
f 57/16/16 158/19/16 161/16/16
f 63/23/16 152/25/16 154/23/16
f 71/31/16 144/32/16 146/31/16
f 101/56/16 118/54/16 99/54/16
f 152/25/41 153/24/41 154/23/41
f 98/53/1 121/51/1 119/53/1
f 90/46/1 129/43/1 127/46/1
f 154/23/40 155/22/40 156/21/40
f 73/32/16 142/34/16 144/32/16
f 107/63/16 112/59/16 105/59/16
f 70/28/1 145/30/1 72/30/1
f 100/67/1 119/53/1 117/67/1
f 158/19/57 160/18/57 161/16/57
f 69/29/16 146/31/16 148/29/16
f 89/45/16 130/44/16 87/44/16
f 161/16/58 159/12/58 162/17/58
f 62/22/1 153/24/1 64/24/1
f 162/64/59 109/61/59 110/63/59
f 66/26/1 149/69/1 68/69/1
f 110/63/60 111/62/60 112/59/60
f 106/62/1 113/58/1 111/62/1
f 91/47/16 128/45/16 89/45/16
f 61/21/16 154/23/16 156/21/16
f 114/57/39 115/55/39 116/56/39
f 116/56/38 117/67/38 118/54/38
f 105/59/16 114/57/16 103/57/16
f 78/36/1 137/38/1 80/38/1
f 120/52/36 121/51/36 122/50/36
f 122/50/35 123/48/35 124/49/35
f 3/3/61 1/12/61 4/8/61
f 4/8/61 6/7/61 3/3/61
f 6/7/61 7/5/61 3/3/61
f 7/5/61 8/6/61 3/3/61
f 8/6/61 9/15/61 3/3/61
f 9/15/61 10/14/61 3/3/61
f 10/14/61 11/13/61 5/1/61
f 3/3/61 10/14/61 5/1/61
f 11/13/61 12/11/61 5/1/61
f 12/11/61 13/10/61 5/1/61
f 13/10/61 14/9/61 5/1/61
f 14/9/61 15/4/61 5/1/61
f 27/4/61 26/9/61 2/2/61
f 26/9/61 25/10/61 2/2/61
f 25/10/61 24/11/61 2/2/61
f 24/11/61 23/13/61 2/2/61
f 23/13/61 22/14/61 2/2/61
f 22/14/61 21/15/61 3/3/61
f 21/15/61 20/6/61 3/3/61
f 20/6/61 19/5/61 3/3/61
f 19/5/61 18/7/61 3/3/61
f 18/7/61 17/8/61 3/3/61
f 17/8/61 16/12/61 3/3/61
f 22/14/61 3/3/61 2/2/61
f 32/4/62 33/9/62 30/2/62
f 33/9/62 34/10/62 30/2/62
f 34/10/62 35/11/62 30/2/62
f 35/11/62 36/13/62 30/2/62
f 36/13/62 37/14/62 30/2/62
f 37/14/62 38/15/62 29/3/62
f 38/15/62 39/6/62 29/3/62
f 39/6/62 40/5/62 29/3/62
f 40/5/62 41/7/62 29/3/62
f 41/7/62 42/8/62 29/3/62
f 42/8/62 28/12/62 29/3/62
f 37/14/62 29/3/62 30/2/62
f 29/3/62 43/12/62 54/8/62
f 54/8/62 53/7/62 29/3/62
f 53/7/62 52/5/62 29/3/62
f 52/5/62 51/6/62 29/3/62
f 51/6/62 50/15/62 29/3/62
f 50/15/62 49/14/62 29/3/62
f 49/14/62 48/13/62 31/1/62
f 29/3/62 49/14/62 31/1/62
f 48/13/62 47/11/62 31/1/62
f 47/11/62 46/10/62 31/1/62
f 46/10/62 45/9/62 31/1/62
f 45/9/62 44/4/62 31/1/62
f 7/5/2 40/5/2 39/6/2
f 18/7/3 53/7/3 54/8/3
f 15/4/63 14/9/63 33/9/63
f 25/10/5 46/10/5 47/11/5
f 6/7/6 41/7/6 40/5/6
f 17/8/7 54/8/7 43/12/7
f 14/9/8 13/10/8 34/10/8
f 24/11/9 47/11/9 48/13/9
f 4/8/10 42/8/10 41/7/10
f 12/11/11 35/11/11 34/10/11
f 23/13/12 48/13/12 49/14/12
f 1/12/13 28/12/13 42/8/13
f 11/13/14 36/13/14 35/11/14
f 22/14/15 49/14/15 50/15/15
f 3/3/16 29/3/16 28/12/16
f 16/12/16 43/12/16 29/3/16
f 10/14/17 37/14/17 36/13/17
f 21/15/18 50/15/18 51/6/18
f 5/1/19 31/1/19 44/4/19
f 9/15/20 38/15/20 37/14/20
f 20/6/21 51/6/21 52/5/21
f 26/9/64 27/4/64 44/4/64
f 8/6/23 39/6/23 38/15/23
f 19/5/24 52/5/24 53/7/24
f 15/4/25 32/4/25 31/1/25
f 25/10/26 26/9/26 45/9/26
f 56/12/27 58/18/27 57/16/27
f 58/18/28 60/20/28 59/19/28
f 60/20/29 62/22/29 61/21/29
f 62/22/30 64/24/30 63/23/30
f 64/24/31 66/26/31 65/25/31
f 66/26/32 68/69/32 67/27/32
f 67/27/33 68/69/33 70/28/33
f 69/29/34 70/28/34 72/30/34
f 72/30/35 74/68/35 73/32/35
f 73/32/36 74/68/36 76/33/36
f 76/33/37 78/36/37 77/35/37
f 78/36/38 80/38/38 79/37/38
f 80/38/39 82/66/39 81/39/39
f 83/40/40 84/70/40 86/41/40
f 85/42/41 86/41/41 88/43/41
f 88/43/42 90/46/42 89/45/42
f 90/46/43 92/65/43 91/47/43
f 91/47/44 92/65/44 94/48/44
f 94/48/45 96/51/45 95/50/45
f 96/51/46 98/53/46 97/52/46
f 98/53/47 100/67/47 99/54/47
f 99/54/48 100/67/48 102/55/48
f 102/55/49 104/58/49 103/57/49
f 104/58/50 106/62/50 105/59/50
f 56/60/1 108/61/1 109/61/1
f 106/62/51 108/61/51 107/63/51
f 108/61/52 56/60/52 55/64/52
f 94/48/1 92/65/1 125/65/1
f 64/24/1 153/24/1 151/26/1
f 82/66/53 135/66/53 136/39/53
f 80/38/1 137/38/1 135/66/1
f 79/37/16 81/39/16 136/39/16
f 88/43/1 86/41/1 131/41/1
f 87/44/16 130/44/16 132/42/16
f 104/58/1 102/55/1 115/55/1
f 103/57/16 114/57/16 116/56/16
f 58/18/1 160/18/1 157/20/1
f 102/55/1 100/67/1 117/67/1
f 74/68/1 143/68/1 141/33/1
f 95/50/16 122/50/16 124/49/16
f 68/69/1 149/69/1 147/28/1
f 56/12/1 159/12/1 160/18/1
f 72/30/1 145/30/1 143/68/1
f 97/52/16 120/52/16 122/50/16
f 96/51/1 94/48/1 123/48/1
f 112/59/54 111/62/54 113/58/54
f 118/54/37 117/67/37 119/53/37
f 126/47/34 124/49/34 123/48/34
f 130/44/31 129/43/31 131/41/31
f 138/37/48 137/38/48 139/36/48
f 144/32/45 143/68/45 145/30/45
f 150/27/42 149/69/42 151/26/42
f 156/21/55 155/22/55 157/20/55
f 93/49/16 124/49/16 126/47/16
f 60/20/1 157/20/1 155/22/1
f 77/35/16 79/37/16 138/37/16
f 126/47/33 125/65/33 127/46/33
f 128/45/32 127/46/32 129/43/32
f 59/19/16 61/21/16 156/21/16
f 86/41/1 84/70/1 134/70/1
f 67/27/16 69/29/16 148/29/16
f 132/42/30 131/41/30 134/70/30
f 92/65/1 90/46/1 127/46/1
f 136/39/49 135/66/49 137/38/49
f 76/33/1 141/33/1 139/36/1
f 85/42/16 132/42/16 133/40/16
f 140/35/47 139/36/47 141/33/47
f 65/25/16 67/27/16 150/27/16
f 144/32/46 142/34/46 141/33/46
f 75/34/16 77/35/16 140/35/16
f 55/64/16 162/64/16 110/63/16
f 99/54/16 118/54/16 120/52/16
f 83/40/56 133/40/56 134/70/56
f 146/31/44 145/30/44 147/28/44
f 55/17/16 57/16/16 161/16/16
f 108/61/1 106/62/1 111/62/1
f 148/29/43 147/28/43 149/69/43
f 57/16/16 59/19/16 158/19/16
f 63/23/16 65/25/16 152/25/16
f 71/31/16 73/32/16 144/32/16
f 101/56/16 116/56/16 118/54/16
f 152/25/41 151/26/41 153/24/41
f 98/53/1 96/51/1 121/51/1
f 90/46/1 88/43/1 129/43/1
f 154/23/40 153/24/40 155/22/40
f 73/32/16 75/34/16 142/34/16
f 107/63/16 110/63/16 112/59/16
f 70/28/1 147/28/1 145/30/1
f 100/67/1 98/53/1 119/53/1
f 158/19/57 157/20/57 160/18/57
f 69/29/16 71/31/16 146/31/16
f 89/45/16 128/45/16 130/44/16
f 161/16/58 160/18/58 159/12/58
f 62/22/1 155/22/1 153/24/1
f 162/64/59 159/60/59 109/61/59
f 66/26/1 151/26/1 149/69/1
f 110/63/60 109/61/60 111/62/60
f 106/62/1 104/58/1 113/58/1
f 91/47/16 126/47/16 128/45/16
f 61/21/16 63/23/16 154/23/16
f 114/57/39 113/58/39 115/55/39
f 116/56/38 115/55/38 117/67/38
f 105/59/16 112/59/16 114/57/16
f 78/36/1 139/36/1 137/38/1
f 120/52/36 119/53/36 121/51/36
f 122/50/35 121/51/35 123/48/35

View File

@@ -1,275 +0,0 @@
# Blender 3.6.5
# www.blender.org
o turbo.001
v 0.479911 0.031444 0.058178
v 0.008582 0.031444 0.530813
v 0.469532 0.031444 0.039992
v 0.309421 0.031444 0.039992
v 0.001804 0.031444 0.348364
v -0.304681 0.031444 0.041125
v -0.474757 0.031444 0.041125
v -0.474757 0.031444 0.059620
v -0.006999 0.031444 0.530813
v 0.479911 0.031444 -0.248942
v 0.008582 0.031444 0.223694
v 0.469532 0.031444 -0.267127
v 0.309421 0.031444 -0.267127
v 0.001804 0.031444 0.041245
v -0.304681 0.031444 -0.265994
v -0.474757 0.031444 -0.265994
v -0.474757 0.031444 -0.247499
v -0.006999 0.031444 0.223694
v 0.479911 0.031444 -0.561796
v 0.008582 0.031444 -0.092817
v 0.469532 0.031444 -0.579981
v 0.309421 0.031444 -0.579981
v 0.001804 0.031444 -0.275266
v -0.304681 0.031444 -0.578848
v -0.474757 0.031444 -0.578848
v -0.474757 0.031444 -0.560354
v -0.006999 0.031444 -0.092817
v 0.479911 -0.029054 0.058178
v 0.008582 -0.029055 0.530813
v 0.469532 -0.029054 0.039992
v 0.309421 -0.029054 0.039992
v 0.001804 -0.029055 0.348364
v -0.304681 -0.029054 0.041125
v -0.474757 -0.029055 0.041125
v -0.474757 -0.029055 0.059620
v -0.006999 -0.029055 0.530813
v 0.479911 -0.029054 -0.248942
v 0.008582 -0.029055 0.223694
v 0.469532 -0.029054 -0.267127
v 0.309421 -0.029054 -0.267127
v 0.001804 -0.029055 0.041245
v -0.304681 -0.029054 -0.265994
v -0.474757 -0.029055 -0.265994
v -0.474757 -0.029055 -0.247499
v -0.006999 -0.029055 0.223694
v 0.479911 -0.029054 -0.561796
v 0.008582 -0.029055 -0.092817
v 0.469532 -0.029054 -0.579981
v 0.309421 -0.029054 -0.579981
v 0.001804 -0.029055 -0.275266
v -0.304681 -0.029054 -0.578848
v -0.474757 -0.029055 -0.578848
v -0.474757 -0.029055 -0.560354
v -0.006999 -0.029055 -0.092817
vn 0.8685 -0.0000 -0.4957
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -0.0000 -0.0000 -1.0000
vn -0.7080 -0.0000 -0.7062
vn -0.7097 -0.0000 0.7045
vn 0.7081 -0.0000 0.7061
vn -0.7069 -0.0000 0.7073
vn 0.7080 -0.0000 -0.7062
vn -0.7037 -0.0000 -0.7104
vn 0.7053 -0.0000 0.7089
vn 0.7037 -0.0000 -0.7105
vn -0.0000 -1.0000 -0.0000
vt 0.588715 0.839843
vt 0.583870 0.825758
vt 0.588715 0.825758
vt 0.452602 0.883467
vt 0.448296 0.897552
vt 0.448296 0.883467
vt 0.203212 0.786027
vt 0.189127 0.782563
vt 0.203212 0.782563
vt 0.077824 0.851102
vt 0.063739 0.811505
vt 0.077824 0.811505
vt 0.419186 0.782563
vt 0.405101 0.854358
vt 0.405101 0.782563
vt 0.246407 0.922613
vt 0.232322 0.786027
vt 0.246407 0.786027
vt 0.588715 0.883038
vt 0.583869 0.868953
vt 0.588715 0.868953
vt 0.409407 0.883467
vt 0.405101 0.897552
vt 0.405101 0.883467
vt 0.619270 0.531191
vt 0.633355 0.686593
vt 0.619270 0.686593
vt 0.232322 0.782563
vt 0.246407 0.782563
vt 0.462381 0.782563
vt 0.448296 0.854357
vt 0.448296 0.782563
vt 0.881817 0.837845
vt 0.867732 0.701259
vt 0.881817 0.701259
vt 0.881817 0.866955
vt 0.867732 0.904232
vt 0.867732 0.866955
vt 0.588715 0.796648
vt 0.583869 0.782563
vt 0.588715 0.782563
vt 0.619270 0.715702
vt 0.633355 0.871104
vt 0.619270 0.871104
vt 0.867732 0.697796
vt 0.881817 0.697796
vt 0.318711 0.782563
vt 0.332797 0.876581
vt 0.318711 0.876581
vt 0.505575 0.782563
vt 0.491490 0.854357
vt 0.491490 0.782563
vt 0.548770 0.876182
vt 0.534685 0.913458
vt 0.534685 0.876182
vt 0.121019 0.851102
vt 0.106934 0.811505
vt 0.121019 0.811505
vt 0.867732 0.513285
vt 0.881817 0.668686
vt 0.867732 0.668686
vt 0.361906 0.782563
vt 0.375991 0.876581
vt 0.361906 0.876581
vt 0.106934 0.880211
vt 0.111240 0.894296
vt 0.106934 0.894296
vt 0.077824 0.917488
vt 0.063739 0.880211
vt 0.077824 0.880211
vt 0.028640 0.858942
vt 0.014555 0.819345
vt 0.028640 0.819345
vt 0.203212 0.922613
vt 0.189127 0.786027
vt 0.275517 0.782563
vt 0.289602 0.876581
vt 0.275517 0.876581
vt 0.231604 0.642143
vt 0.303398 0.713761
vt 0.299165 0.753454
vt 0.374985 0.642143
vt 0.446779 0.713761
vt 0.442546 0.753454
vt 0.518366 0.642143
vt 0.590160 0.713761
vt 0.585927 0.753454
vt 0.583870 0.839843
vt 0.452602 0.897552
vt 0.063739 0.851102
vt 0.419186 0.854358
vt 0.232322 0.922613
vt 0.583869 0.883038
vt 0.409407 0.897552
vt 0.633355 0.531191
vt 0.462381 0.854357
vt 0.867732 0.837845
vt 0.881817 0.904232
vt 0.583869 0.796649
vt 0.633355 0.715702
vt 0.332797 0.782563
vt 0.505575 0.854357
vt 0.548770 0.913458
vt 0.106934 0.851102
vt 0.881817 0.513285
vt 0.375991 0.782563
vt 0.111240 0.880211
vt 0.063739 0.917488
vt 0.014555 0.858942
vt 0.189127 0.922613
vt 0.289602 0.782563
vt 0.303399 0.751038
vt 0.189127 0.643721
vt 0.189127 0.640093
vt 0.298829 0.531191
vt 0.303135 0.531191
vt 0.303135 0.570788
vt 0.446779 0.751038
vt 0.332508 0.643721
vt 0.332508 0.640093
vt 0.442210 0.531191
vt 0.446516 0.531191
vt 0.446516 0.570788
vt 0.590160 0.751038
vt 0.475889 0.643721
vt 0.475889 0.640093
vt 0.585591 0.531191
vt 0.589897 0.531191
vt 0.589897 0.570788
s 0
f 3/1/1 28/2/1 30/3/1
f 16/4/2 44/5/2 17/6/2
f 9/7/3 29/8/3 2/9/3
f 24/10/4 52/11/4 25/12/4
f 5/13/5 31/14/5 32/15/5
f 17/16/6 45/17/6 18/18/6
f 12/19/1 37/20/1 39/21/1
f 25/22/2 53/23/2 26/24/2
f 2/25/7 28/26/7 1/27/7
f 18/18/3 38/28/3 11/29/3
f 14/30/5 40/31/5 41/32/5
f 26/33/8 54/34/8 27/35/8
f 4/36/4 30/37/4 31/38/4
f 21/39/1 46/40/1 48/41/1
f 11/42/7 37/43/7 10/44/7
f 27/35/3 47/45/3 20/46/3
f 5/47/9 33/48/9 6/49/9
f 23/50/10 49/51/10 50/52/10
f 13/53/4 39/54/4 40/55/4
f 6/56/4 34/57/4 7/58/4
f 20/59/11 46/60/11 19/61/11
f 14/62/9 42/63/9 15/64/9
f 8/65/2 34/66/2 35/67/2
f 21/68/4 49/69/4 22/70/4
f 15/71/4 43/72/4 16/73/4
f 8/74/6 36/75/6 9/7/6
f 23/76/12 51/77/12 24/78/12
f 32/79/13 31/80/13 28/81/13
f 41/82/13 40/83/13 37/84/13
f 50/85/13 49/86/13 46/87/13
f 3/1/1 1/88/1 28/2/1
f 16/4/2 43/89/2 44/5/2
f 9/7/3 36/75/3 29/8/3
f 24/10/4 51/90/4 52/11/4
f 5/13/5 4/91/5 31/14/5
f 17/16/6 44/92/6 45/17/6
f 12/19/1 10/93/1 37/20/1
f 25/22/2 52/94/2 53/23/2
f 2/25/7 29/95/7 28/26/7
f 18/18/3 45/17/3 38/28/3
f 14/30/5 13/96/5 40/31/5
f 26/33/8 53/97/8 54/34/8
f 4/36/4 3/98/4 30/37/4
f 21/39/1 19/99/1 46/40/1
f 11/42/7 38/100/7 37/43/7
f 27/35/3 54/34/3 47/45/3
f 5/47/9 32/101/9 33/48/9
f 23/50/10 22/102/10 49/51/10
f 13/53/4 12/103/4 39/54/4
f 6/56/4 33/104/4 34/57/4
f 20/59/11 47/105/11 46/60/11
f 14/62/9 41/106/9 42/63/9
f 8/65/2 7/107/2 34/66/2
f 21/68/4 48/108/4 49/69/4
f 15/71/4 42/109/4 43/72/4
f 8/74/6 35/110/6 36/75/6
f 23/76/12 50/111/12 51/77/12
f 31/80/13 30/112/13 28/81/13
f 28/81/13 29/113/13 32/79/13
f 29/113/13 36/114/13 32/79/13
f 36/114/13 35/115/13 32/79/13
f 35/115/13 34/116/13 33/117/13
f 32/79/13 35/115/13 33/117/13
f 40/83/13 39/118/13 37/84/13
f 37/84/13 38/119/13 41/82/13
f 38/119/13 45/120/13 41/82/13
f 45/120/13 44/121/13 41/82/13
f 44/121/13 43/122/13 42/123/13
f 41/82/13 44/121/13 42/123/13
f 49/86/13 48/124/13 46/87/13
f 46/87/13 47/125/13 50/85/13
f 47/125/13 54/126/13 50/85/13
f 54/126/13 53/127/13 50/85/13
f 53/127/13 52/128/13 51/129/13
f 50/85/13 53/127/13 51/129/13

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 MiB

After

Width:  |  Height:  |  Size: 10 MiB

View File

@@ -28,9 +28,10 @@ public class ClientGameLogic implements ServerInterpreter {
private final InterruptState interruptState = new InterruptState(null, this);
private final SettingsState settingsState = new SettingsState(null, this);
public ClientGameLogic(Game game, ClientSender clientSender) {
this.game = game;
public ClientGameLogic(ClientSender clientSender) {
this.game = new Game();
this.clientSender = clientSender;
dialogsState.enter();
state = dialogsState;
}
@@ -189,6 +190,19 @@ public void received(ServerStartGameMessage msg) {
state.received(msg);
}
@Override
public void received(ShutdownMessage msg) {state.received(msg);}
@Override
public void received(StartBriefingMessage msg) {
state.received(msg);
}
@Override
public void received(PlayerDataMessage msg) {
state.received(msg);
}
@Override
public void received(StartPieceMessage msg) {
state.received(msg);
@@ -235,6 +249,10 @@ public void selectTsk(Color color){
state.selectTSK(color);
}
public void deselectTSK(Color color){
state.deselectTSK(color);
}
public void selectDice(){
state.selectDice();
}
@@ -244,7 +262,11 @@ public void selectName(String name){
}
public void selectReady(boolean ready){
state.selectReady(ready);
if(ready){
state.selectReady();
} else {
state.selectUnready();
}
}
public void selectHost(String name){
@@ -304,7 +326,11 @@ public SettingsState getSettings(){
}
public Notification getNotification(){
if(!notifications.isEmpty()){
return notifications.remove(0);
} else {
return null;
}
}
public void addNotification(Notification notification){

View File

@@ -150,6 +150,9 @@ public void received(ServerStartGameMessage msg) {
LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg);
}
@Override
public void received(ShutdownMessage msg) {LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg);}
@Override
public void received(StartPieceMessage msg) {
LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg);
@@ -180,6 +183,15 @@ public void received(WaitPieceMessage msg) {
LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg);
}
@Override
public void received(StartBriefingMessage msg) {
LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg);
}
public void received(PlayerDataMessage msg) {
LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg);
}
public void selectPiece(Piece piece) {
LOGGER.log(Level.DEBUG, "Selecting piece not allowed.");
}
@@ -200,7 +212,7 @@ public void setName(String name) {
LOGGER.log(Level.DEBUG, "Setting name not allowed.");
}
public void selectReady(boolean ready) {
public void selectReady() {
LOGGER.log(Level.DEBUG, "Selecting ready not allowed.");
}

View File

@@ -37,7 +37,7 @@ public void enter(){
public void setState(DialogStates newState){
currentState.exit();
currentState.enter();
newState.enter();
currentState = newState;
}
@@ -53,6 +53,10 @@ public void setOwnPlayerName(String ownPlayerName) {
this.ownPlayerName = ownPlayerName;
}
public void setOwnPlayerId(int ownPlayerId) {
this.ownPlayerID = ownPlayerId;
}
public LobbyState getLobby() {
return lobbyState;
}
@@ -75,6 +79,11 @@ public void selectLeave(){
currentState.selectLeave();
}
@Override
public void setName(String name){
currentState.setName(name);
}
@Override
public void selectTSK(Color color){
currentState.selectTSK(color);
@@ -86,8 +95,8 @@ public void deselectTSK(Color color){
}
@Override
public void selectReady(boolean ready){
currentState.selectReady(ready);
public void selectReady(){
currentState.selectReady();
}
@Override
@@ -135,6 +144,16 @@ public void received(ServerStartGameMessage msg){
currentState.received(msg);
}
@Override
public void received(PlayerDataMessage msg){
currentState.received(msg);
}
@Override
public void received(StartBriefingMessage msg){
currentState.received(msg);
}
public DialogStates getState() {
return currentState;
}

View File

@@ -8,6 +8,9 @@
public class GameState extends ClientState {
/**
* the current substate
*/
private GameStates state;
private final AnimationState animationState = new AnimationState(this, logic);
@@ -16,21 +19,38 @@ public class GameState extends ClientState {
private final TurnState turnState = new TurnState(this, logic);
private final WaitingState waitingState = new WaitingState(this, logic);
/**
* Constructor for GameState
*
* @param parent the parent of this state
* @param logic the ClientGameLogic
*/
public GameState(ClientState parent, ClientGameLogic logic) {
super(parent, logic);
state = determineStartPlayerState;
}
/**
* The method to enter the state
*/
@Override
public void enter() {
}
/**
* the method to exit this state
*/
@Override
public void exit() {
state.exit();
}
/**
* This method is used to set a new SubState
*
* @param newState the state to be set
*/
public void setState(GameStates newState){
state.exit();
state.enter();

View File

@@ -8,28 +8,53 @@ public class InterruptState extends ClientState {
private ClientState previousState;
/**
* Creates a new InterruptState
*
* @param parent the parent state
* @param logic the game logic
*/
public InterruptState(ClientState parent, ClientGameLogic logic) {
super(parent, logic);
}
/**
* Enters the new state machine
*/
@Override
public void enter() {
previousState = null;
}
/**
* exits this state
*/
@Override
public void exit() {
previousState = null;
}
/**
* This method sets the stores the gamestate as previous state
*
* @param previousState
*/
public void setPreviousState(ClientState previousState) {
this.previousState = previousState;
}
/**
* returns teh previous gamestate
*
* @return the previous gamestate
*/
public ClientState getPreviousState() {
return previousState;
}
/**
* The host resumes the game
*/
@Override
public void selectResume(){
if(logic.isHost()){
@@ -37,6 +62,11 @@ public void selectResume(){
}
}
/**
* The server resumes the game
*
* @param msg the ResumeGame message received
*/
public void received(ResumeGameMessage msg) {
//TODO: logic.addNotification(new ResumeNotification());
logic.setState(previousState);

View File

@@ -0,0 +1,9 @@
package pp.mdga.client;
public interface ServerConnection extends ClientSender {
boolean isConnected();
void connect();
void disconnect();
}

View File

@@ -7,26 +7,44 @@
public class SettingsState extends ClientState {
/**
* The current state of the settings
*/
private SettingStates currentState;
private final MainSettingsState mainSettingsState = new MainSettingsState(this, logic);
private final AudioSettingsState audioSettingsState = new AudioSettingsState(this, logic);
private final VideoSettingsState videoSettingsState = new VideoSettingsState(this, logic);
/**
* Creates a new SettingsState
*
* @param parent the parent state
* @param logic the game logic
*/
public SettingsState(ClientState parent, ClientGameLogic logic) {
super(parent, logic);
}
/**
* Enters the current state
*/
@Override
public void enter() {
currentState = mainSettingsState;
}
/**
* Exits the current state
*/
@Override
public void exit() {
currentState.exit();
}
/**
* Changes the current state
*/
public SettingStates getState(){
return currentState;
}

View File

@@ -4,15 +4,20 @@
import pp.mdga.client.ClientState;
import pp.mdga.client.DialogsState;
import pp.mdga.game.Color;
import pp.mdga.game.Piece;
import pp.mdga.game.Player;
import pp.mdga.game.PlayerData;
import pp.mdga.message.client.*;
import pp.mdga.message.server.LobbyPlayerJoinedMessage;
import pp.mdga.message.server.LobbyPlayerLeaveMessage;
import pp.mdga.message.server.PlayerDataMessage;
import pp.mdga.message.server.ServerStartGameMessage;
import pp.mdga.message.server.StartBriefingMessage;
import pp.mdga.message.server.UpdateReadyMessage;
import pp.mdga.message.server.UpdateTSKMessage;
import pp.mdga.notification.TskSelectNotification;
import pp.mdga.notification.TskUnselectNotification;
import pp.mdga.notification.*;
import java.util.*;
public class LobbyState extends DialogStates {
@@ -35,6 +40,8 @@ public void exit() {
@Override
public void selectLeave() {
parent.setState(parent.getStartDialog());
logic.addNotification(new StartDialogNotification());
logic.send(new LeaveGameMessage());
}
@Override
@@ -48,7 +55,7 @@ public void deselectTSK(Color color) {
}
@Override
public void selectReady(boolean ready) {
public void selectReady() {
logic.send(new LobbyReadyMessage());
}
@@ -68,21 +75,51 @@ public void selectStart(){
}
}
@Override
public void received(StartBriefingMessage msg){
logic.getGame().setBoard(msg.getBoard());
}
public void received(PlayerDataMessage msg){
logic.getGame().getBoard().addPlayerData(msg.getColor(), msg.getPlayerData());
}
@Override
public void received(ServerStartGameMessage msg){
logic.addNotification(new GameNotification(logic.getGame().getPlayerById(parent.getOwnPlayerId()).getColor()));
for (Map.Entry<Color, PlayerData> entry : logic.getGame().getBoard().getPlayerData().entrySet()) {
List<UUID> pieceList = new ArrayList<>();
for(Piece piece : entry.getValue().getPieces()){
System.out.println(piece.getUuid());
pieceList.add(piece.getUuid());
}
logic.addNotification(new PlayerInGameNotification(entry.getKey(), pieceList , logic.getGame().getPlayerByColor(entry.getKey()).getName()));
}
parent.startGame();
}
@Override
public void received(LobbyPlayerJoinedMessage msg){
if(msg.getPlayer().getName().equals(parent.getOwnPlayerName())){
parent.setOwnPlayerId(msg.getId());
}
if (msg.isHost() && msg.getId() == parent.getOwnPlayerId()){
logic.setHost(true);
}
logic.addNotification(new TskSelectNotification(msg.getPlayer().getColor(), msg.getPlayer().getName(), msg.getPlayer().getName().equals(parent.getOwnPlayerName())));
logic.getGame().getPlayers().put(msg.getId(), msg.getPlayer());
}
@Override
public void received(UpdateTSKMessage msg){
logic.addNotification(new TskUnselectNotification(logic.getGame().getPlayers().get(msg.getId()).getColor()));
logic.getGame().getPlayers().get(msg.getId()).setColor(msg.getColor());
if(msg.isTaken()) {
logic.addNotification(new TskSelectNotification(msg.getColor(), logic.getGame().getPlayers().get(msg.getId()).getName(), parent.getOwnPlayerId()== msg.getId()));
} else {
logic.addNotification(new TskUnselectNotification(logic.getGame().getPlayers().get(msg.getId()).getColor()));
}
logic.getGame().getPlayers().get(msg.getId()).setColor(msg.getColor());
}
@Override
@@ -93,6 +130,8 @@ public void received(LobbyPlayerLeaveMessage msg){
@Override
public void received(UpdateReadyMessage msg){
//TODO server sendet kein update on UNready
logic.addNotification(new LobbyReadyNotification(logic.getGame().getPlayers().get(msg.getPlayerId()).getColor(), msg.isReady()));
logic.getGame().getPlayers().get(msg.getPlayerId()).setReady(msg.isReady());
}
}

Some files were not shown because too many files have changed in this diff Show More