Compare commits
	
		
			1 Commits
		
	
	
		
			dev/client
			...
			view_asset
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					def68f30be | 
							
								
								
									
										18
									
								
								Projekte/.run/MdgaApp.run.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,18 @@
 | 
			
		||||
<component name="ProjectRunConfigurationManager">
 | 
			
		||||
  <configuration default="false" name="MdgaApp" type="Application" factoryName="Application" singleton="false"
 | 
			
		||||
                 nameIsGenerated="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"/>
 | 
			
		||||
    <option name="WORKING_DIRECTORY" value="$MODULE_WORKING_DIR$"/>
 | 
			
		||||
    <extension name="coverage">
 | 
			
		||||
      <pattern>
 | 
			
		||||
        <option name="PATTERN" value="pp.mdga.client.*"/>
 | 
			
		||||
        <option name="ENABLED" value="true"/>
 | 
			
		||||
      </pattern>
 | 
			
		||||
    </extension>
 | 
			
		||||
    <method v="2">
 | 
			
		||||
      <option name="Make" enabled="true"/>
 | 
			
		||||
    </method>
 | 
			
		||||
  </configuration>
 | 
			
		||||
</component>
 | 
			
		||||
@@ -5,16 +5,9 @@
 | 
			
		||||
description = 'mdga client'
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    implementation project(":jme-common")
 | 
			
		||||
    implementation project(":mdga:model")
 | 
			
		||||
 | 
			
		||||
    implementation libs.jme3.desktop
 | 
			
		||||
    implementation libs.jme3.core
 | 
			
		||||
    implementation libs.jme3.lwjgl3
 | 
			
		||||
    implementation libs.jme3.lwjgl
 | 
			
		||||
    implementation libs.jme3.desktop
 | 
			
		||||
    implementation libs.jme3.effects
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    runtimeOnly libs.jme3.awt.dialogs
 | 
			
		||||
    runtimeOnly libs.jme3.plugins
 | 
			
		||||
@@ -25,4 +18,4 @@ implementation project(":mdga:model")
 | 
			
		||||
application {
 | 
			
		||||
    mainClass = 'pp.mdga.client.MdgaApp'
 | 
			
		||||
    applicationName = 'MDGA'
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -8,58 +8,46 @@ public enum Asset {
 | 
			
		||||
    jet,
 | 
			
		||||
    lw,
 | 
			
		||||
    marine,
 | 
			
		||||
    node_home_blue("./node_home/node_home.j3o", "./node_home/node_home_blue_diff.png"),
 | 
			
		||||
    node_wait_blue("./node_home/node_home.j3o", "./node_home/node_home_blue_diff.png"),
 | 
			
		||||
    node_home_black("./node_home/node_home.j3o", "./node_home/node_home_black_diff.png"),
 | 
			
		||||
    node_wait_black("./node_home/node_home.j3o", "./node_home/node_home_black_diff.png"),
 | 
			
		||||
    node_home_green("./node_home/node_home.j3o", "./node_home/node_home_green_diff.png"),
 | 
			
		||||
    node_wait_green("./node_home/node_home.j3o", "./node_home/node_home_green_diff.png"),
 | 
			
		||||
    node_home_yellow("./node_home/node_home.j3o", "./node_home/node_home_orange_diff.png"),
 | 
			
		||||
    node_wait_yellow("./node_home/node_home.j3o", "./node_home/node_home_orange_diff.png"),
 | 
			
		||||
    node_home_blue("./node_home/node_home.j3o", "./node_home/node_home_blue.png"),
 | 
			
		||||
    node_home_black("./node_home/node_home.j3o", "./node_home/node_home_black.png"),
 | 
			
		||||
    node_home_green("./node_home/node_home.j3o", "./node_home/node_home_green.png"),
 | 
			
		||||
    node_home_yellow("./node_home/node_home.j3o", "./node_home/node_home_yellow.png"),
 | 
			
		||||
    node_normal,
 | 
			
		||||
    node_start("./node_normal/node_normal.j3o", "./node_normal/node_start_diff.png"),
 | 
			
		||||
    node_bonus("./node_normal/node_normal.j3o", "./node_normal/node_bonus_diff.png"),
 | 
			
		||||
    node_start("./node_normal/node_normal.j3o", "./node_normal/node_normal_start.png"),
 | 
			
		||||
    node_bonus("./node_normal/node_normal.j3o", "./node_normal/node_normal_bonus.png"),
 | 
			
		||||
    radar,
 | 
			
		||||
    ship(0.8f),
 | 
			
		||||
    shieldCard,
 | 
			
		||||
    ship,
 | 
			
		||||
    smallTent,
 | 
			
		||||
    tank,
 | 
			
		||||
//    world(1.2f),
 | 
			
		||||
    world("./world_new/world_export_new.obj", "./world_new/world_new_diff.png", 1.2f),
 | 
			
		||||
    shield_ring("./shield_ring/shield_ring.obj", null),
 | 
			
		||||
    tree_small("./tree_small/tree_small.obj", "./tree_small/tree_small_diff.png"),
 | 
			
		||||
    tree_big("./tree_big/tree_big.obj", "./tree_big/tree_big_diff.png"),
 | 
			
		||||
    turboCard,
 | 
			
		||||
    swapCard,
 | 
			
		||||
    shieldCard
 | 
			
		||||
    ;
 | 
			
		||||
    tank,
 | 
			
		||||
    turboCard,
 | 
			
		||||
    world(1.1f);
 | 
			
		||||
 | 
			
		||||
    private final String modelPath;
 | 
			
		||||
    private final String diffPath;
 | 
			
		||||
    private final float size;
 | 
			
		||||
 | 
			
		||||
    Asset() {
 | 
			
		||||
    private static final String MODEL_FILETYPE = ".j3o";
 | 
			
		||||
    private static final String DIFFUSE_EXT = "_diff.png";
 | 
			
		||||
 | 
			
		||||
    Asset(){
 | 
			
		||||
        String folderFileName = "./" + name() + "/" + name();
 | 
			
		||||
        this.modelPath = folderFileName + ".j3o";
 | 
			
		||||
        this.diffPath = folderFileName + "_diff.png";
 | 
			
		||||
        this.modelPath = folderFileName + MODEL_FILETYPE;
 | 
			
		||||
        this.diffPath = folderFileName + DIFFUSE_EXT;
 | 
			
		||||
        this.size = 1f;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Asset(String modelPath, String diffPath) {
 | 
			
		||||
    Asset(String modelPath, String diffPath){
 | 
			
		||||
        this.modelPath = modelPath;
 | 
			
		||||
        this.diffPath = diffPath;
 | 
			
		||||
        this.size = 1f;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Asset(float size) {
 | 
			
		||||
    Asset(float size){
 | 
			
		||||
        String folderFileName = "./" + name() + "/" + name();
 | 
			
		||||
        this.modelPath = folderFileName + ".j3o";
 | 
			
		||||
        this.diffPath = folderFileName + "_diff.png";
 | 
			
		||||
        this.size = size;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Asset(String modelPath, String diffPath, float size){
 | 
			
		||||
        this.modelPath = modelPath;
 | 
			
		||||
        this.diffPath = diffPath;
 | 
			
		||||
        this.modelPath = folderFileName + MODEL_FILETYPE;
 | 
			
		||||
        this.diffPath = folderFileName + DIFFUSE_EXT;
 | 
			
		||||
        this.size = size;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -71,7 +59,8 @@ public String getDiffPath() {
 | 
			
		||||
        return diffPath;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public float getSize() {
 | 
			
		||||
    public float getSize(){
 | 
			
		||||
        return size;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,72 +0,0 @@
 | 
			
		||||
package pp.mdga.client;
 | 
			
		||||
 | 
			
		||||
import com.jme3.input.InputManager;
 | 
			
		||||
import com.jme3.input.KeyInput;
 | 
			
		||||
import com.jme3.input.MouseInput;
 | 
			
		||||
import com.jme3.input.controls.*;
 | 
			
		||||
 | 
			
		||||
public class InputSyncronizer {
 | 
			
		||||
 | 
			
		||||
    private MdgaApp app;
 | 
			
		||||
    private InputManager inputManager;
 | 
			
		||||
 | 
			
		||||
    protected boolean rightMousePressed = false;
 | 
			
		||||
    private float rotationAngle = 180f;
 | 
			
		||||
    private int scrollValue = 0;
 | 
			
		||||
 | 
			
		||||
    InputSyncronizer(MdgaApp app) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
 | 
			
		||||
        this.inputManager = app.getInputManager();
 | 
			
		||||
 | 
			
		||||
        setupInput();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setupInput() {
 | 
			
		||||
        inputManager.addMapping("Settings", new KeyTrigger(KeyInput.KEY_ESCAPE));
 | 
			
		||||
 | 
			
		||||
        inputManager.addMapping("RotateRightMouse", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
 | 
			
		||||
        inputManager.addMapping("MouseLeft", new MouseAxisTrigger(MouseInput.AXIS_X, false)); // Left movement
 | 
			
		||||
        inputManager.addMapping("MouseRight", new MouseAxisTrigger(MouseInput.AXIS_X, true)); // Right movement
 | 
			
		||||
        inputManager.addMapping("MouseScrollUp", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false)); // Scroll up
 | 
			
		||||
        inputManager.addMapping("MouseScrollDown", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true)); // Scroll down
 | 
			
		||||
 | 
			
		||||
        inputManager.addListener(actionListener, "Settings", "RotateRightMouse");
 | 
			
		||||
        inputManager.addListener(analogListener, "MouseLeft", "MouseRight", "MouseScrollUp", "MouseScrollDown");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final ActionListener actionListener = new ActionListener() {
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onAction(String name, boolean isPressed, float tpf) {
 | 
			
		||||
            if (name.equals("Settings") && isPressed) {
 | 
			
		||||
                app.getView().pressEscape();
 | 
			
		||||
            }
 | 
			
		||||
            if (name.equals("RotateRightMouse")) {
 | 
			
		||||
                rightMousePressed = isPressed;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private final AnalogListener analogListener = new AnalogListener() {
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onAnalog(String name, float value, float tpf) {
 | 
			
		||||
            if (name.equals("MouseLeft") && rightMousePressed) {
 | 
			
		||||
                rotationAngle -= value * 360f;
 | 
			
		||||
            } else if (name.equals("MouseRight") && rightMousePressed) {
 | 
			
		||||
                rotationAngle += value * 360f;
 | 
			
		||||
            } else if (name.equals("MouseScrollUp")) {
 | 
			
		||||
                scrollValue = Math.max(1, scrollValue - 5);
 | 
			
		||||
            } else if (name.equals("MouseScrollDown")) {
 | 
			
		||||
                scrollValue = Math.min(100, scrollValue + 5);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public float getRotation() {
 | 
			
		||||
        return (rotationAngle / 2) % 360;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getScroll() {
 | 
			
		||||
        return scrollValue;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,38 +1,25 @@
 | 
			
		||||
package pp.mdga.client;
 | 
			
		||||
 | 
			
		||||
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.light.AmbientLight;
 | 
			
		||||
import com.jme3.light.DirectionalLight;
 | 
			
		||||
import com.jme3.material.Material;
 | 
			
		||||
import com.jme3.math.ColorRGBA;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.renderer.RenderManager;
 | 
			
		||||
import com.jme3.renderer.queue.RenderQueue;
 | 
			
		||||
import com.jme3.scene.Spatial;
 | 
			
		||||
import com.jme3.shadow.DirectionalLightShadowRenderer;
 | 
			
		||||
import com.jme3.system.AppSettings;
 | 
			
		||||
import pp.mdga.client.view.*;
 | 
			
		||||
 | 
			
		||||
public class MdgaApp extends SimpleApplication {
 | 
			
		||||
    private AnimationHandler animationHandler;
 | 
			
		||||
    private AcousticHandler acousticHandler;
 | 
			
		||||
    private NotificationSynchronizer notificationSynchronizer;
 | 
			
		||||
    private InputSyncronizer inputSyncronizer;
 | 
			
		||||
    private ModelSyncronizer modelSyncronizer;
 | 
			
		||||
 | 
			
		||||
    MdgaView view = null;
 | 
			
		||||
    private MdgaState state = MdgaState.MAIN;
 | 
			
		||||
 | 
			
		||||
    private static float resolutionFactor = 1.8f;
 | 
			
		||||
 | 
			
		||||
    public static void main(String[] args) {
 | 
			
		||||
        MdgaApp app = new MdgaApp();
 | 
			
		||||
        AppSettings settings = new AppSettings(true);
 | 
			
		||||
        settings.setSamples(128);
 | 
			
		||||
        settings.setCenterWindow(true);
 | 
			
		||||
 | 
			
		||||
        int width = (int)(1280 * resolutionFactor);
 | 
			
		||||
        int height = (int)(720 * resolutionFactor);
 | 
			
		||||
 | 
			
		||||
        settings.setWidth(width);
 | 
			
		||||
        settings.setHeight(height);
 | 
			
		||||
 | 
			
		||||
        settings.setVSync(false);
 | 
			
		||||
 | 
			
		||||
        MdgaApp app = new MdgaApp();
 | 
			
		||||
        settings.setWidth(1300);
 | 
			
		||||
        settings.setHeight(1000);
 | 
			
		||||
        app.setSettings(settings);
 | 
			
		||||
        app.setShowSettings(false);
 | 
			
		||||
        app.start();
 | 
			
		||||
@@ -40,83 +27,64 @@ public static void main(String[] args) {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void simpleInitApp() {
 | 
			
		||||
        animationHandler = new AnimationHandler(this);
 | 
			
		||||
        acousticHandler = new AcousticHandler(this);
 | 
			
		||||
        notificationSynchronizer = new NotificationSynchronizer(this);
 | 
			
		||||
        inputSyncronizer = new InputSyncronizer(this);
 | 
			
		||||
        modelSyncronizer = new ModelSyncronizer(this);
 | 
			
		||||
        flyCam.setEnabled(true);
 | 
			
		||||
        int zoom = 20;
 | 
			
		||||
        cam.setLocation(new Vector3f(zoom,0,zoom));
 | 
			
		||||
        cam.lookAt(new Vector3f(0,0,0), new Vector3f(0,0,1));
 | 
			
		||||
 | 
			
		||||
        inputManager.deleteMapping("SIMPLEAPP_Exit");
 | 
			
		||||
        inputManager.deleteMapping("FLYCAM_ZoomIn");
 | 
			
		||||
        inputManager.deleteMapping("FLYCAM_ZoomOut");
 | 
			
		||||
        inputManager.deleteMapping("FLYCAM_RotateDrag");
 | 
			
		||||
        flyCam.setEnabled(false);
 | 
			
		||||
        GuiGlobals.initialize(this);
 | 
			
		||||
        DirectionalLight sun = new DirectionalLight();
 | 
			
		||||
        sun.setColor(ColorRGBA.White);
 | 
			
		||||
        sun.setDirection(new Vector3f(-1,0,-1));
 | 
			
		||||
        rootNode.addLight(sun);
 | 
			
		||||
        AmbientLight ambient = new AmbientLight();
 | 
			
		||||
        ambient.setColor(new ColorRGBA(0.3f,0.3f,0.3f,1));
 | 
			
		||||
        rootNode.addLight(ambient);
 | 
			
		||||
 | 
			
		||||
        enter(state);
 | 
			
		||||
        final int SHADOWMAP_SIZE=1024*8;
 | 
			
		||||
        DirectionalLightShadowRenderer dlsr = new DirectionalLightShadowRenderer(assetManager, SHADOWMAP_SIZE, 4);
 | 
			
		||||
        dlsr.setLight(sun);
 | 
			
		||||
        viewPort.addProcessor(dlsr);
 | 
			
		||||
 | 
			
		||||
        createModel(Asset.lw).setLocalTranslation(new Vector3f(0,-10,0));
 | 
			
		||||
        createModel(Asset.cir).setLocalTranslation(new Vector3f(0,-8,0));
 | 
			
		||||
        createModel(Asset.marine).setLocalTranslation(new Vector3f(0,-6,0));
 | 
			
		||||
        createModel(Asset.heer).setLocalTranslation(new Vector3f(0,-4,0));
 | 
			
		||||
        createModel(Asset.node_normal).setLocalTranslation(new Vector3f(0,-2.5f,0));
 | 
			
		||||
        createModel(Asset.node_home_blue).setLocalTranslation(new Vector3f(0,-1,0));
 | 
			
		||||
        createModel(Asset.smallTent).setLocalTranslation(new Vector3f(0,1,0));
 | 
			
		||||
        createModel(Asset.tank).setLocalTranslation(new Vector3f(0,5,0));
 | 
			
		||||
        createModel(Asset.jet).setLocalTranslation(new Vector3f(0,12,0));
 | 
			
		||||
        createModel(Asset.ship).setLocalTranslation(new Vector3f(0,17,0));
 | 
			
		||||
        createModel(Asset.radar).setLocalTranslation(new Vector3f(0,20,0));
 | 
			
		||||
 | 
			
		||||
        createModel(Asset.world);
 | 
			
		||||
 | 
			
		||||
        System.out.println(Asset.node_normal.getModelPath());
 | 
			
		||||
        System.out.println(Asset.node_normal.getDiffPath());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private Spatial createModel(Asset asset){
 | 
			
		||||
        String modelName = asset.getModelPath();
 | 
			
		||||
        String texName = asset.getDiffPath();
 | 
			
		||||
        Spatial model = assetManager.loadModel(modelName);
 | 
			
		||||
        model.scale(asset.getSize());
 | 
			
		||||
        model.rotate((float) Math.toRadians(0), 0, (float) Math.toRadians(90));
 | 
			
		||||
        model.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
 | 
			
		||||
        Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
 | 
			
		||||
        mat.setTexture("DiffuseMap", assetManager.loadTexture(texName));
 | 
			
		||||
        model.setMaterial(mat);
 | 
			
		||||
        rootNode.attachChild(model);
 | 
			
		||||
        return model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void simpleUpdate(float tpf) {
 | 
			
		||||
        view.update();
 | 
			
		||||
        acousticHandler.update();
 | 
			
		||||
        notificationSynchronizer.update();
 | 
			
		||||
        //this method will be called every game tick and can be used to make updates
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void enter(MdgaState state) {
 | 
			
		||||
        if(null != view) {
 | 
			
		||||
            view.leave();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.state = state;
 | 
			
		||||
 | 
			
		||||
        switch (state) {
 | 
			
		||||
            case MAIN:
 | 
			
		||||
                view = new MainView(this);
 | 
			
		||||
                break;
 | 
			
		||||
            case LOBBY:
 | 
			
		||||
                view = new LobbyView(this);
 | 
			
		||||
                break;
 | 
			
		||||
            case GAME:
 | 
			
		||||
                view = new GameView(this);
 | 
			
		||||
                break;
 | 
			
		||||
            case CEREMONY:
 | 
			
		||||
                view = new CeremonyView(this);
 | 
			
		||||
                break;
 | 
			
		||||
            case NONE:
 | 
			
		||||
                throw new RuntimeException("cant enter state NONE");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        acousticHandler.playState(state);
 | 
			
		||||
 | 
			
		||||
        view.enter();
 | 
			
		||||
    @Override
 | 
			
		||||
    public void simpleRender(RenderManager rm) {
 | 
			
		||||
        //add render code here (if any)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void afteGameCleanup() {
 | 
			
		||||
        //TODO
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AnimationHandler getAnimationHandler() {
 | 
			
		||||
        return animationHandler;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AcousticHandler getAcousticHandler() {
 | 
			
		||||
        return acousticHandler;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MdgaState getState() {return state; }
 | 
			
		||||
 | 
			
		||||
    public float getResolutionFactor() {
 | 
			
		||||
        return resolutionFactor;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MdgaView getView() {
 | 
			
		||||
        return view;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ModelSyncronizer getModelSyncronizer() {
 | 
			
		||||
        return modelSyncronizer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public InputSyncronizer getInputSyncronizer() { return inputSyncronizer; }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +0,0 @@
 | 
			
		||||
package pp.mdga.client;
 | 
			
		||||
 | 
			
		||||
public enum MdgaState {
 | 
			
		||||
    NONE,
 | 
			
		||||
    MAIN,
 | 
			
		||||
    LOBBY,
 | 
			
		||||
    GAME,
 | 
			
		||||
    CEREMONY;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,72 +0,0 @@
 | 
			
		||||
package pp.mdga.client;
 | 
			
		||||
 | 
			
		||||
import pp.mdga.client.view.LobbyView;
 | 
			
		||||
import pp.mdga.game.Color;
 | 
			
		||||
 | 
			
		||||
public class ModelSyncronizer {
 | 
			
		||||
    private MdgaApp app;
 | 
			
		||||
 | 
			
		||||
    ModelSyncronizer(MdgaApp app) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void selectPiece() {
 | 
			
		||||
        //TODO call from somewhere
 | 
			
		||||
        System.out.println("selectPiece");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void selectCard() {
 | 
			
		||||
        //TODO call from somewhere
 | 
			
		||||
        System.out.println("selectCard");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void selectTsk(Color color) {
 | 
			
		||||
        //TODO call from somewhere
 | 
			
		||||
        System.out.println("selectTsk: " + color);
 | 
			
		||||
        LobbyView view = (LobbyView) app.getView();
 | 
			
		||||
        view.setTaken(color, true, true, "OwnPlayerName");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void unselectTsk() {
 | 
			
		||||
        //TODO call from somewhere
 | 
			
		||||
        System.out.println("unselectTsk");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void rolledDice() {
 | 
			
		||||
        //TODO call from somewhere
 | 
			
		||||
        System.out.println("rolledDice");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setName(String name) {
 | 
			
		||||
        //TODO call from somewhere
 | 
			
		||||
        System.out.println("setName:" + name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setReady() {
 | 
			
		||||
        //TODO call from somewhere
 | 
			
		||||
        System.out.println("setReady");
 | 
			
		||||
        app.enter(MdgaState.GAME);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setHost(int port) {
 | 
			
		||||
        //TODO call from somewhere
 | 
			
		||||
        System.out.println("setHost: " + port);
 | 
			
		||||
        app.enter(MdgaState.LOBBY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setJoin(String ip, int port) {
 | 
			
		||||
        //TODO call from somewhere
 | 
			
		||||
        System.out.println("setJoin");
 | 
			
		||||
        app.enter(MdgaState.LOBBY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void leave() {
 | 
			
		||||
        System.out.println("leave");
 | 
			
		||||
        app.enter(MdgaState.MAIN);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void enter(MdgaState state) {
 | 
			
		||||
        System.out.println("enter:" + state);
 | 
			
		||||
        app.enter(state);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,136 +0,0 @@
 | 
			
		||||
package pp.mdga.client;
 | 
			
		||||
 | 
			
		||||
import pp.mdga.client.view.GameView;
 | 
			
		||||
import pp.mdga.client.view.LobbyView;
 | 
			
		||||
import pp.mdga.notification.*;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
 | 
			
		||||
public class NotificationSynchronizer {
 | 
			
		||||
    private final MdgaApp app;
 | 
			
		||||
 | 
			
		||||
    private ArrayList<Notification> notifications = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
    NotificationSynchronizer(MdgaApp app) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void addTestNotification(Notification n) {
 | 
			
		||||
        notifications.add(n);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void update() {
 | 
			
		||||
        //TODO fetch model notifications
 | 
			
		||||
 | 
			
		||||
        for (Notification n : notifications) {
 | 
			
		||||
            switch (app.getState()) {
 | 
			
		||||
                case MAIN:
 | 
			
		||||
                    handleMain(n);
 | 
			
		||||
                    break;
 | 
			
		||||
                case LOBBY:
 | 
			
		||||
                    handleLobby(n);
 | 
			
		||||
                    break;
 | 
			
		||||
                case GAME:
 | 
			
		||||
                    handleGame(n);
 | 
			
		||||
                    break;
 | 
			
		||||
                case CEREMONY:
 | 
			
		||||
                    handleCeremony(n);
 | 
			
		||||
                    break;
 | 
			
		||||
                case NONE:
 | 
			
		||||
                    throw new RuntimeException("no notification expected: " + n.toString());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void handleMain(Notification notification) {
 | 
			
		||||
        if (notification instanceof LobbyDialogNotification) {
 | 
			
		||||
            app.enter(MdgaState.LOBBY);
 | 
			
		||||
        } else {
 | 
			
		||||
            throw new RuntimeException("notification not expected: " + notification.toString());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void handleLobby(Notification notification) {
 | 
			
		||||
        LobbyView lobbyView = (LobbyView) app.getView();
 | 
			
		||||
 | 
			
		||||
        if (notification instanceof TskSelectNotification) {
 | 
			
		||||
            TskSelectNotification n = (TskSelectNotification)notification;
 | 
			
		||||
            lobbyView.setTaken(n.getColor(), true, n.isSelf(), n.getName());
 | 
			
		||||
        } else if (notification instanceof TskUnselectNotification) {
 | 
			
		||||
            TskUnselectNotification n = (TskUnselectNotification)notification;
 | 
			
		||||
            lobbyView.setTaken(n.getColor(), false, false, null);
 | 
			
		||||
        } else if (notification instanceof GameNotification) {
 | 
			
		||||
            app.enter(MdgaState.GAME);
 | 
			
		||||
        } else {
 | 
			
		||||
            throw new RuntimeException("notification not expected: " + notification.toString());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void handleGame(Notification notification) {
 | 
			
		||||
        GameView gameView = (GameView) app.getView();
 | 
			
		||||
 | 
			
		||||
        if (notification instanceof AcquireCardNotification) {
 | 
			
		||||
            // Handle AcquireCardNotification
 | 
			
		||||
        } else if (notification instanceof ActivePlayerNotification) {
 | 
			
		||||
            // Handle ActivePlayerNotification
 | 
			
		||||
        } else if (notification instanceof CeremonyNotification) {
 | 
			
		||||
            app.enter(MdgaState.CEREMONY);
 | 
			
		||||
        } else if (notification instanceof DiceNowNotification) {
 | 
			
		||||
            // Handle DiceNowNotification
 | 
			
		||||
        } else if (notification instanceof DicingNotification) {
 | 
			
		||||
            // Handle DicingNotification
 | 
			
		||||
        } else if (notification instanceof DrawCardNotification) {
 | 
			
		||||
            // Handle DrawCardNotification
 | 
			
		||||
        } else if (notification instanceof HomeMoveNotification) {
 | 
			
		||||
            HomeMoveNotification n = (HomeMoveNotification)notification;
 | 
			
		||||
            gameView.getBoardHandler().moveHomePiece(n.getPieceId(), n.getHomeIndex());
 | 
			
		||||
        } else if (notification instanceof InterruptNotification) {
 | 
			
		||||
            // Handle InterruptNotification
 | 
			
		||||
        } else if (notification instanceof MovePieceNotification) {
 | 
			
		||||
            MovePieceNotification n = (MovePieceNotification)notification;
 | 
			
		||||
            //gameView.getBoardHandler().movePiece(n.get); //TODO
 | 
			
		||||
        } else if (notification instanceof MoveThrowPieceNotification) {
 | 
			
		||||
            MoveThrowPieceNotification n = (MoveThrowPieceNotification)notification;
 | 
			
		||||
            //gameView.getBoardHandler().throwPiece(n.); //TODO
 | 
			
		||||
        } else if (notification instanceof NoShieldNotification) {
 | 
			
		||||
            NoShieldNotification n = (NoShieldNotification)notification;
 | 
			
		||||
            gameView.getBoardHandler().unshieldPiece(n.getPieceId());
 | 
			
		||||
        } else if (notification instanceof PieceInGameNotification) {
 | 
			
		||||
            // Handle PieceInGameNotification
 | 
			
		||||
        } else if (notification instanceof PlayCardNotification) {
 | 
			
		||||
            // Handle PlayCardNotification
 | 
			
		||||
        } else if (notification instanceof PlayerInGameNotification) {
 | 
			
		||||
            // Handle PlayerInGameNotification
 | 
			
		||||
        } else if (notification instanceof ResumeNotification) {
 | 
			
		||||
            // Handle ResumeNotification
 | 
			
		||||
        } else if (notification instanceof RollDiceNotification) {
 | 
			
		||||
            // Handle RollDiceNotification
 | 
			
		||||
        } else if (notification instanceof SelectableCardsNotification) {
 | 
			
		||||
            // Handle SelectableCardsNotification
 | 
			
		||||
        } else if (notification instanceof SelectablePiecesNotification) {
 | 
			
		||||
            // Handle SelectablePiecesNotification
 | 
			
		||||
        } else if (notification instanceof ShieldActiveNotification) {
 | 
			
		||||
            ShieldActiveNotification n = (ShieldActiveNotification)notification;
 | 
			
		||||
            gameView.getBoardHandler().shieldPiece(n.getPieceId());
 | 
			
		||||
        } else if (notification instanceof ShieldSuppressedNotification) {
 | 
			
		||||
            ShieldSuppressedNotification n = (ShieldSuppressedNotification)notification;
 | 
			
		||||
            gameView.getBoardHandler().suppressShield(n.getPieceId());
 | 
			
		||||
        } else if (notification instanceof StartDialogNotification) {
 | 
			
		||||
            app.enter(MdgaState.MAIN);
 | 
			
		||||
        } else if (notification instanceof SwapPieceNotification) {
 | 
			
		||||
            // Handle SwapPieceNotification
 | 
			
		||||
        } else if (notification instanceof WaitMoveNotification) {
 | 
			
		||||
            // Handle WaitMoveNotification
 | 
			
		||||
        } else {
 | 
			
		||||
            throw new RuntimeException("notification not expected: " + notification.toString());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void handleCeremony(Notification notification) {
 | 
			
		||||
        if (notification instanceof StartDialogNotification) {
 | 
			
		||||
            app.enter(MdgaState.MAIN);
 | 
			
		||||
        } else {
 | 
			
		||||
            throw new RuntimeException("notification not expected: " + notification.toString());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,403 +0,0 @@
 | 
			
		||||
package pp.mdga.client.acoustic;
 | 
			
		||||
 | 
			
		||||
import com.jme3.system.NanoTimer;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
import pp.mdga.client.MdgaState;
 | 
			
		||||
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
public class AcousticHandler {
 | 
			
		||||
    private MdgaApp app;
 | 
			
		||||
 | 
			
		||||
    private MdgaState state = MdgaState.NONE;
 | 
			
		||||
 | 
			
		||||
    private boolean playGame = false;
 | 
			
		||||
    private ArrayList<MusicAsset> gameTracks = new ArrayList<>();
 | 
			
		||||
    private NanoTimer trackTimer = new NanoTimer();
 | 
			
		||||
 | 
			
		||||
    private boolean fading = false; // Indicates if a fade is in progress
 | 
			
		||||
    private NanoTimer fadeTimer = new NanoTimer(); // Timer to track fade progress
 | 
			
		||||
    private static final float FADE_DURATION = 3.0f; // Duration for outfade
 | 
			
		||||
    private static final float CROSSFADE_DURATION = 1.5f; // Duration for infade
 | 
			
		||||
    private GameMusic playing = null; // Currently playing track
 | 
			
		||||
    private GameMusic scheduled = null; // Scheduled track to play next
 | 
			
		||||
    private GameMusic old = null; // Old track being faded out
 | 
			
		||||
 | 
			
		||||
    private float mainVolume = 1.0f;
 | 
			
		||||
    private float musicVolume = 1.0f;
 | 
			
		||||
    private float soundVolume = 1.0f;
 | 
			
		||||
 | 
			
		||||
    private ArrayList<GameSound> sounds = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
    public AcousticHandler(MdgaApp app) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This method updates the acousticHandler and should be called every frame
 | 
			
		||||
     */
 | 
			
		||||
    public void update() {
 | 
			
		||||
        updateVolumeAndTrack();
 | 
			
		||||
 | 
			
		||||
        if (playGame) {
 | 
			
		||||
            updateGameTracks();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Iterator<GameSound> iterator = sounds.iterator();
 | 
			
		||||
        while (iterator.hasNext()) {
 | 
			
		||||
            GameSound s = iterator.next();
 | 
			
		||||
 | 
			
		||||
            s.update(getSoundVolumeTotal());
 | 
			
		||||
 | 
			
		||||
            if (!s.isPlaying()) {
 | 
			
		||||
                iterator.remove();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This method instantly plays a sound
 | 
			
		||||
     *
 | 
			
		||||
     * @param sound the sound to be played
 | 
			
		||||
     */
 | 
			
		||||
    public void playSound(MdgaSound sound) {
 | 
			
		||||
        ArrayList<SoundAssetDelayVolume> assets = new ArrayList<SoundAssetDelayVolume>();
 | 
			
		||||
        switch (sound) {
 | 
			
		||||
            case LOST:
 | 
			
		||||
                assets.add(new SoundAssetDelayVolume(SoundAsset.LOST, 1.0f, 0.0f));
 | 
			
		||||
                break;
 | 
			
		||||
            case VICTORY:
 | 
			
		||||
                assets.add(new SoundAssetDelayVolume(SoundAsset.VICTORY, 1.0f, 2.0f));
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (SoundAssetDelayVolume sawd : assets) {
 | 
			
		||||
            GameSound gameSound = new GameSound(app, sawd.asset(), getSoundVolumeTotal(), sawd.subVolume(), sawd.delay());
 | 
			
		||||
            sounds.add(gameSound);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This method fades the played music to fit the state.
 | 
			
		||||
     *
 | 
			
		||||
     * @param state the state of which the corresponding music should be played to be played
 | 
			
		||||
     */
 | 
			
		||||
    public void playState(MdgaState state) {
 | 
			
		||||
        if (this.state == state) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        MusicAsset asset = null;
 | 
			
		||||
 | 
			
		||||
        switch (state) {
 | 
			
		||||
            case MAIN:
 | 
			
		||||
                playGame = false;
 | 
			
		||||
                asset = MusicAsset.MAIN_MENU;
 | 
			
		||||
                break;
 | 
			
		||||
            case LOBBY:
 | 
			
		||||
                playGame = false;
 | 
			
		||||
                asset = MusicAsset.LOBBY;
 | 
			
		||||
                break;
 | 
			
		||||
            case GAME:
 | 
			
		||||
                addGameTracks();
 | 
			
		||||
                playGame = true;
 | 
			
		||||
                assert (!gameTracks.isEmpty()) : "no more game music available";
 | 
			
		||||
                asset = gameTracks.remove(0);
 | 
			
		||||
                break;
 | 
			
		||||
            case CEREMONY:
 | 
			
		||||
                playGame = false;
 | 
			
		||||
                asset = MusicAsset.CEREMONY;
 | 
			
		||||
                break;
 | 
			
		||||
            case NONE:
 | 
			
		||||
                throw new RuntimeException("no music for state NONE");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        assert (null != asset) : "music sceduling went wrong";
 | 
			
		||||
 | 
			
		||||
        scheduled = new GameMusic(app, asset, getMusicVolumeTotal(), asset.getSubVolume(), asset.getLoop(), 0.0f);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Performs linear interpolation between two float values.
 | 
			
		||||
     *
 | 
			
		||||
     * @param start The starting value.
 | 
			
		||||
     * @param end   The ending value.
 | 
			
		||||
     * @param t     The interpolation factor, typically between 0 and 1.
 | 
			
		||||
     * @return The interpolated value between start and end.
 | 
			
		||||
     */
 | 
			
		||||
    private float lerp(float start, float end, float t) {
 | 
			
		||||
        return start + t * (end - start);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates the state of audio playback, handling track transitions and volume adjustments.
 | 
			
		||||
     *
 | 
			
		||||
     * This method ensures smooth transitions between tracks using fade-in and fade-out effects.
 | 
			
		||||
     * It also handles cases where no track is playing, starting a scheduled track immediately at full volume.
 | 
			
		||||
     * The method prioritizes the latest scheduled track if multiple scheduling occurs quickly.
 | 
			
		||||
     *
 | 
			
		||||
     * Behavior:
 | 
			
		||||
     * 1. If nothing is scheduled and no track is playing, it exits early.
 | 
			
		||||
     * 2. If a scheduled track exists and no track is playing, the scheduled track starts immediately at full volume.
 | 
			
		||||
     * 3. If a scheduled track exists while a track is playing, it initiates a fade-out for the currently playing track
 | 
			
		||||
     *    and prepares for the new track to fade in.
 | 
			
		||||
     * 4. If a track transition is in progress (fading), it processes the fade-out and fade-in states.
 | 
			
		||||
     *    If a new track is scheduled during this process, it interrupts the current transition and prioritizes the new track.
 | 
			
		||||
     * 5. If no fading is needed and a track is playing, it ensures the track's volume is updated.
 | 
			
		||||
     *
 | 
			
		||||
     * Special cases:
 | 
			
		||||
     * - If no track is playing and a new track is scheduled, it starts the track immediately without fading.
 | 
			
		||||
     * - If a new track is scheduled during fading, it resets the transition to prioritize the new track.
 | 
			
		||||
     */
 | 
			
		||||
    private void updateVolumeAndTrack() {
 | 
			
		||||
        if (scheduled == null && !fading && playing == null) {
 | 
			
		||||
            // Nothing to do, early exit
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (scheduled != null && playing == null && !fading) {
 | 
			
		||||
            // No current track, start scheduled track immediately at full volume
 | 
			
		||||
            playing = scheduled;
 | 
			
		||||
            scheduled = null;
 | 
			
		||||
            playing.play();
 | 
			
		||||
            playing.update(getMusicVolumeTotal()); // Set volume to full
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (scheduled != null && !fading) {
 | 
			
		||||
            // Initiate a fade process if a new track is scheduled
 | 
			
		||||
            fading = true;
 | 
			
		||||
            fadeTimer.reset();
 | 
			
		||||
            old = playing; // The currently playing track becomes the old track
 | 
			
		||||
            playing = null; // Clear the playing track during the fade process
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (fading) {
 | 
			
		||||
            handleFadeProcess();
 | 
			
		||||
 | 
			
		||||
            // Handle any interruptions due to newly scheduled tracks
 | 
			
		||||
            if (scheduled != null && playing != null && playing != scheduled) {
 | 
			
		||||
                // Interrupt the current infade and switch to the new scheduled track
 | 
			
		||||
                old = playing; // Treat the currently infading track as the old track
 | 
			
		||||
                playing = null; // Reset playing to allow switching
 | 
			
		||||
                fadeTimer.reset(); // Restart fade timer for the new track
 | 
			
		||||
            }
 | 
			
		||||
        } else if (playing != null) {
 | 
			
		||||
            // Update volume for the currently playing track
 | 
			
		||||
            playing.update(getMusicVolumeTotal());
 | 
			
		||||
        } else if (scheduled != null) {
 | 
			
		||||
            // If no track is playing and one is scheduled, start it immediately at full volume
 | 
			
		||||
            playing = scheduled;
 | 
			
		||||
            scheduled = null;
 | 
			
		||||
            playing.play();
 | 
			
		||||
            playing.update(getMusicVolumeTotal()); // Set volume to full
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Manages the fading process during audio track transitions.
 | 
			
		||||
     *
 | 
			
		||||
     * This method handles the fade-out of the currently playing (old) track, manages any pause between the fade-out
 | 
			
		||||
     * and fade-in, and initiates the fade-in for the new track if applicable. It ensures smooth transitions between
 | 
			
		||||
     * tracks while maintaining the correct volume adjustments.
 | 
			
		||||
     *
 | 
			
		||||
     * Behavior:
 | 
			
		||||
     * 1. **Outfade:** Gradually decreases the volume of the `old` track over the duration of `FADE_DURATION`.
 | 
			
		||||
     *    Once the outfade completes, the `old` track is paused and cleared.
 | 
			
		||||
     * 2. **Pause Handling:** Waits for a defined pause (if applicable) before initiating the infade for the next track.
 | 
			
		||||
     * 3. **Infade:** If a `scheduled` track exists and the outfade and pause are complete, it begins playing
 | 
			
		||||
     *    the new track (`playing`) and initiates the infade process.
 | 
			
		||||
     *
 | 
			
		||||
     * Key Details:
 | 
			
		||||
     * - The outfade volume adjustment is interpolated linearly from full volume to zero using the `lerp` function.
 | 
			
		||||
     * - The pause duration is retrieved from the scheduled track if it is specified.
 | 
			
		||||
     * - If a new track is scheduled during the fade process, it is handled by external logic to prioritize transitions.
 | 
			
		||||
     *
 | 
			
		||||
     * Preconditions:
 | 
			
		||||
     * - `fading` is expected to be `true` when this method is called.
 | 
			
		||||
     * - The method is invoked as part of the `updateVolumeAndTrack` process.
 | 
			
		||||
     */
 | 
			
		||||
    private void handleFadeProcess() {
 | 
			
		||||
        float time = fadeTimer.getTimeInSeconds();
 | 
			
		||||
 | 
			
		||||
        // Handle outfade for the old track
 | 
			
		||||
        if (old != null && time <= FADE_DURATION) {
 | 
			
		||||
            float t = Math.min(time / FADE_DURATION, 1.0f);
 | 
			
		||||
            float oldVolume = lerp(1.0f, 0.0f, t);
 | 
			
		||||
            old.update(getMusicVolumeTotal() * oldVolume);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (old != null && time > FADE_DURATION) {
 | 
			
		||||
            // Complete outfade
 | 
			
		||||
            old.pause();
 | 
			
		||||
            old = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Handle pause duration before infade
 | 
			
		||||
        float pause = (scheduled != null) ? scheduled.getPause() : 0.0f;
 | 
			
		||||
        if (time > FADE_DURATION + pause) {
 | 
			
		||||
            if (playing == null && scheduled != null) {
 | 
			
		||||
                // Begin infade for the new track
 | 
			
		||||
                playing = scheduled;
 | 
			
		||||
                scheduled = null;
 | 
			
		||||
                playing.play(); // Start playing the new track
 | 
			
		||||
            }
 | 
			
		||||
            handleInfade(time - FADE_DURATION - pause);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Manages the fade-in process for the currently playing track.
 | 
			
		||||
     *
 | 
			
		||||
     * This method gradually increases the volume of the `playing` track from zero to full volume
 | 
			
		||||
     * over the duration of `CROSSFADE_DURATION`. It ensures a smooth transition into the new track.
 | 
			
		||||
     *
 | 
			
		||||
     * Behavior:
 | 
			
		||||
     * 1. If no track is set as `playing`, the method exits early, as there is nothing to fade in.
 | 
			
		||||
     * 2. Linearly interpolates the volume of the `playing` track from 0.0 to 1.0 based on the elapsed
 | 
			
		||||
     *    `infadeTime` and the specified `CROSSFADE_DURATION`.
 | 
			
		||||
     * 3. Once the fade-in is complete (when `infadeTime` exceeds `CROSSFADE_DURATION`), the method:
 | 
			
		||||
     *    - Marks the fade process (`fading`) as complete.
 | 
			
		||||
     *    - Ensures the `playing` track is updated to its full volume.
 | 
			
		||||
     *
 | 
			
		||||
     * Key Details:
 | 
			
		||||
     * - Uses the `lerp` function to calculate the volume level for the `playing` track during the fade-in.
 | 
			
		||||
     * - Ensures the volume is always a value between 0.0 and 1.0.
 | 
			
		||||
     * - The `infadeTime` parameter should be relative to the start of the fade-in process.
 | 
			
		||||
     *
 | 
			
		||||
     * Preconditions:
 | 
			
		||||
     * - The `playing` track must be initialized and actively fading in for this method to have an effect.
 | 
			
		||||
     * - The method is invoked as part of the `updateVolumeAndTrack` process.
 | 
			
		||||
     *
 | 
			
		||||
     * @param infadeTime The elapsed time (in seconds) since the fade-in process started.
 | 
			
		||||
     */
 | 
			
		||||
    private void handleInfade(float infadeTime) {
 | 
			
		||||
        if (playing == null) {
 | 
			
		||||
            // Nothing to infade
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Proceed with the infade for the current playing track
 | 
			
		||||
        float t = Math.min(infadeTime / CROSSFADE_DURATION, 1.0f);
 | 
			
		||||
        float newVolume = lerp(0.0f, 1.0f, t);
 | 
			
		||||
        playing.update(getMusicVolumeTotal() * newVolume);
 | 
			
		||||
 | 
			
		||||
        if (infadeTime > CROSSFADE_DURATION) {
 | 
			
		||||
            // Infade is complete, finalize state
 | 
			
		||||
            fading = false;
 | 
			
		||||
            playing.update(getMusicVolumeTotal()); // Ensure full volume
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adds a list of game tracks to the gameTracks collection and shuffles them.
 | 
			
		||||
     * This method adds predefined game tracks to the track list and shuffles the order.
 | 
			
		||||
     */
 | 
			
		||||
    private void addGameTracks() {
 | 
			
		||||
        Random random = new Random();
 | 
			
		||||
 | 
			
		||||
        for (int i = 1; i <= 6; i++) {
 | 
			
		||||
            gameTracks.add(MusicAsset.valueOf("GAME_" + i));
 | 
			
		||||
        }
 | 
			
		||||
        Collections.shuffle(gameTracks, random);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates the current game tracks. If the currently playing track is nearing its end,
 | 
			
		||||
     * a new track will be scheduled to play. If the list of game tracks is empty, it will be refreshed.
 | 
			
		||||
     */
 | 
			
		||||
    private void updateGameTracks() {
 | 
			
		||||
        if(null == playing) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (playing.nearEnd(10)) {
 | 
			
		||||
            if (gameTracks.isEmpty()) {
 | 
			
		||||
                addGameTracks();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (playing != null && playing.nearEnd(3) && trackTimer.getTimeInSeconds() > 20) {
 | 
			
		||||
            trackTimer.reset();
 | 
			
		||||
 | 
			
		||||
            MusicAsset nextTrack = gameTracks.remove(0);
 | 
			
		||||
 | 
			
		||||
            scheduled = new GameMusic(app, nextTrack, getMusicVolumeTotal(), nextTrack.getSubVolume(), nextTrack.getLoop(), 0.0f);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieves the main volume level.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The current main volume level.
 | 
			
		||||
     */
 | 
			
		||||
    public float getMainVolume() {
 | 
			
		||||
        return mainVolume;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieves the music volume level.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The current music volume level.
 | 
			
		||||
     */
 | 
			
		||||
    public float getMusicVolume() {
 | 
			
		||||
        return musicVolume;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieves the sound volume level.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The current sound volume level.
 | 
			
		||||
     */
 | 
			
		||||
    public float getSoundVolume() {
 | 
			
		||||
        return soundVolume;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the main volume level.
 | 
			
		||||
     *
 | 
			
		||||
     * @param mainVolume The desired main volume level.
 | 
			
		||||
     */
 | 
			
		||||
    public void setMainVolume(float mainVolume) {
 | 
			
		||||
        this.mainVolume = mainVolume;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the music volume level.
 | 
			
		||||
     *
 | 
			
		||||
     * @param musicVolume The desired music volume level.
 | 
			
		||||
     */
 | 
			
		||||
    public void setMusicVolume(float musicVolume) {
 | 
			
		||||
        this.musicVolume = musicVolume;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the sound volume level.
 | 
			
		||||
     *
 | 
			
		||||
     * @param soundVolume The desired sound volume level.
 | 
			
		||||
     */
 | 
			
		||||
    public void setSoundVolume(float soundVolume) {
 | 
			
		||||
        this.soundVolume = soundVolume;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculates the total music volume by multiplying the music volume by the main volume.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The total music volume.
 | 
			
		||||
     */
 | 
			
		||||
    float getMusicVolumeTotal() {
 | 
			
		||||
 | 
			
		||||
        return getMusicVolume() * getMainVolume();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculates the total sound volume by multiplying the sound volume by the main volume.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The total sound volume.
 | 
			
		||||
     */
 | 
			
		||||
    float getSoundVolumeTotal() {
 | 
			
		||||
        return getSoundVolume() * getMainVolume();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,116 +0,0 @@
 | 
			
		||||
package pp.mdga.client.acoustic;
 | 
			
		||||
 | 
			
		||||
import com.jme3.audio.AudioData;
 | 
			
		||||
import com.jme3.audio.AudioNode;
 | 
			
		||||
import com.jme3.audio.AudioSource;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Represents a game music track, including its playback controls and volume settings.
 | 
			
		||||
 * This class manages the playback of a music track, allowing for playing, pausing,
 | 
			
		||||
 * volume adjustment, and tracking the current status of the music.
 | 
			
		||||
 */
 | 
			
		||||
class GameMusic {
 | 
			
		||||
    private float volume;
 | 
			
		||||
    private final float subVolume;
 | 
			
		||||
    private final AudioNode music;
 | 
			
		||||
    private float pause;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs a new GameMusic object.
 | 
			
		||||
     *
 | 
			
		||||
     * @param app       The instance of the application, used to access the asset manager.
 | 
			
		||||
     * @param asset     The music asset to be played.
 | 
			
		||||
     * @param volume    The total volume of the music, adjusted by the main volume.
 | 
			
		||||
     * @param subVolume A relative volume that modifies the base music volume, typically a percentage.
 | 
			
		||||
     * @param loop      A flag indicating whether the music should loop once it finishes.
 | 
			
		||||
     */
 | 
			
		||||
    GameMusic(MdgaApp app, MusicAsset asset, float volume, float subVolume, boolean loop, float pause) {
 | 
			
		||||
        this.volume = volume;
 | 
			
		||||
        this.subVolume = subVolume;
 | 
			
		||||
        this.pause = pause;
 | 
			
		||||
 | 
			
		||||
        music = new AudioNode(app.getAssetManager(), asset.getPath(), AudioData.DataType.Stream);
 | 
			
		||||
        music.setPositional(false);
 | 
			
		||||
        music.setDirectional(false);
 | 
			
		||||
        music.setVolume(volume * subVolume);
 | 
			
		||||
 | 
			
		||||
        music.setLooping(loop);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Plays the current music track.
 | 
			
		||||
     * If the music is already initialized, it starts playback.
 | 
			
		||||
     * If the music is not available, no action is performed.
 | 
			
		||||
     */
 | 
			
		||||
    void play() {
 | 
			
		||||
        if (null == music) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        music.play();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Pauses the current music track.
 | 
			
		||||
     * If the music is not available or is not playing, no action is performed.
 | 
			
		||||
     */
 | 
			
		||||
    void pause() {
 | 
			
		||||
        if (null == music) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        music.stop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if the current music track is playing.
 | 
			
		||||
     *
 | 
			
		||||
     * @return true if the music is playing, false otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    boolean isPlaying() {
 | 
			
		||||
 | 
			
		||||
        return music.getStatus() == AudioSource.Status.Playing;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if the current music track is near the end.
 | 
			
		||||
     *
 | 
			
		||||
     * @param thresholdSeconds The threshold in seconds. If the remaining time is less than or equal to this value,
 | 
			
		||||
     *                         the track is considered near the end.
 | 
			
		||||
     * @return true if the track is near its end (within the threshold), false otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    boolean nearEnd(float thresholdSeconds) {
 | 
			
		||||
        if (music == null || !isPlaying()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        float currentTime = music.getPlaybackTime(); // Current playback time in seconds
 | 
			
		||||
        float duration = music.getAudioData().getDuration(); // Total duration in seconds
 | 
			
		||||
 | 
			
		||||
        if (duration <= 0) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        float remainingTime = duration - currentTime;
 | 
			
		||||
 | 
			
		||||
        return remainingTime <= thresholdSeconds;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates the volume of the music.
 | 
			
		||||
     * If the volume has changed, it will adjust the music's volume accordingly.
 | 
			
		||||
     *
 | 
			
		||||
     * @param newVolume The new total volume for the music.
 | 
			
		||||
     */
 | 
			
		||||
    void update(float newVolume) {
 | 
			
		||||
        if (volume != newVolume) {
 | 
			
		||||
            volume = newVolume;
 | 
			
		||||
            music.setVolume(volume * subVolume);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    float getPause() {
 | 
			
		||||
        return pause;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,83 +0,0 @@
 | 
			
		||||
package pp.mdga.client.acoustic;
 | 
			
		||||
 | 
			
		||||
import com.jme3.audio.AudioData;
 | 
			
		||||
import com.jme3.audio.AudioNode;
 | 
			
		||||
import com.jme3.audio.AudioSource;
 | 
			
		||||
import com.jme3.system.NanoTimer;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Represents a game sound effect, with control over playback, volume, and timing.
 | 
			
		||||
 * This class manages the playback of a sound effect, including starting playback after a delay,
 | 
			
		||||
 * adjusting volume, and tracking whether the sound has finished playing.
 | 
			
		||||
 */
 | 
			
		||||
class GameSound {
 | 
			
		||||
    private float volume;
 | 
			
		||||
    final private float subVolume;
 | 
			
		||||
 | 
			
		||||
    private final AudioNode sound;
 | 
			
		||||
 | 
			
		||||
    private boolean playing = false;
 | 
			
		||||
    private boolean finished = false;
 | 
			
		||||
 | 
			
		||||
    private float delay = 0.0f;
 | 
			
		||||
    private NanoTimer timer = null;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs a new GameSound object.
 | 
			
		||||
     *
 | 
			
		||||
     * @param app       The instance of the application, used to access the asset manager.
 | 
			
		||||
     * @param asset     The sound asset to be played.
 | 
			
		||||
     * @param volume    The total volume of the sound, adjusted by the main volume.
 | 
			
		||||
     * @param subVolume A relative volume that modifies the base sound volume, typically a percentage.
 | 
			
		||||
     * @param delay     The delay before the sound starts playing, in seconds.
 | 
			
		||||
     */
 | 
			
		||||
    GameSound(MdgaApp app, SoundAsset asset, float volume, float subVolume, float delay) {
 | 
			
		||||
        this.volume = volume;
 | 
			
		||||
        this.subVolume = subVolume;
 | 
			
		||||
        this.delay = delay;
 | 
			
		||||
 | 
			
		||||
        sound = new AudioNode(app.getAssetManager(), asset.getPath(), AudioData.DataType.Buffer);
 | 
			
		||||
        sound.setPositional(false);
 | 
			
		||||
        sound.setDirectional(false);
 | 
			
		||||
        sound.setLooping(false);
 | 
			
		||||
        sound.setVolume(volume * subVolume);
 | 
			
		||||
 | 
			
		||||
        timer = new NanoTimer();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if the sound is currently playing.
 | 
			
		||||
     *
 | 
			
		||||
     * @return true if the sound is playing, false otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    boolean isPlaying() {
 | 
			
		||||
        return !finished;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates the sound playback, adjusting the volume if necessary, and starting
 | 
			
		||||
     * the sound after the specified delay.
 | 
			
		||||
     *
 | 
			
		||||
     * @param newVolume The new total volume for the sound.
 | 
			
		||||
     */
 | 
			
		||||
    void update(float newVolume) {
 | 
			
		||||
        if (!playing && timer.getTimeInSeconds() > delay) {
 | 
			
		||||
            sound.play();
 | 
			
		||||
            playing = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!playing) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (volume != newVolume) {
 | 
			
		||||
            volume = newVolume;
 | 
			
		||||
            sound.setVolume(volume * subVolume);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (sound != null && sound.getStatus() == AudioSource.Status.Playing) {
 | 
			
		||||
            finished = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,23 +0,0 @@
 | 
			
		||||
package pp.mdga.client.acoustic;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Enum representing the various sound effects used in the game.
 | 
			
		||||
 * Each sound corresponds to an event or action in the game and may consist of one or more
 | 
			
		||||
 * audio files, potentially with time delays between them.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * These sounds are used to play specific audio cues, such as when a dice is rolled,
 | 
			
		||||
 * a turn starts or ends, a piece is moved or lost, and various other events in the game.
 | 
			
		||||
 */
 | 
			
		||||
public enum MdgaSound {
 | 
			
		||||
    DICE_ROLL,
 | 
			
		||||
    TURN_START,
 | 
			
		||||
    TURN_END,
 | 
			
		||||
    PIECE_END,
 | 
			
		||||
    PIECE_MOVE,
 | 
			
		||||
    PIECE_LOST,
 | 
			
		||||
    SELECT,
 | 
			
		||||
    DESELECT,
 | 
			
		||||
    HURRY,
 | 
			
		||||
    VICTORY,
 | 
			
		||||
    LOST
 | 
			
		||||
}
 | 
			
		||||
@@ -1,76 +0,0 @@
 | 
			
		||||
package pp.mdga.client.acoustic;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Enum representing various music assets used in the game.
 | 
			
		||||
 * Each constant corresponds to a specific music track, along with its properties such as file path,
 | 
			
		||||
 * looping behavior, and relative volume (subVolume).
 | 
			
		||||
 * These music assets are used to control the music that plays in different parts of the game, such as menus and in-game music.
 | 
			
		||||
 */
 | 
			
		||||
enum MusicAsset {
 | 
			
		||||
    MAIN_MENU("Spaceship.wav", true, 1.0f),
 | 
			
		||||
    LOBBY("DeadPlanet.wav", true, 1.0f),
 | 
			
		||||
    CEREMONY("80s,Disco,Life.wav", true, 1.0f),
 | 
			
		||||
    GAME_1("NeonRoadTrip.wav", 1.0f),
 | 
			
		||||
    GAME_2("NoPressureTrance.wav", 1.0f),
 | 
			
		||||
    GAME_3("TheSynthRave.wav", 1.0f),
 | 
			
		||||
    GAME_4("LaserParty.wav", 1.0f),
 | 
			
		||||
    GAME_5("RetroNoir.wav", 1.0f),
 | 
			
		||||
    GAME_6("SpaceInvaders.wav", 1.0f);
 | 
			
		||||
 | 
			
		||||
    private final String path;
 | 
			
		||||
    private final boolean loop;
 | 
			
		||||
    private final float subVolume;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs a new MusicAsset object with the specified name and sub-volume.
 | 
			
		||||
     * The track will not loop by default.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name      The name of the music file.
 | 
			
		||||
     * @param subVolume A relative volume that modifies the base volume of the track (typically a percentage).
 | 
			
		||||
     */
 | 
			
		||||
    MusicAsset(String name, float subVolume) {
 | 
			
		||||
        this.path = "music/" + name;
 | 
			
		||||
        this.loop = false;
 | 
			
		||||
        this.subVolume = subVolume;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs a new MusicAsset object with the specified name, loop flag, and sub-volume.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name      The name of the music file.
 | 
			
		||||
     * @param loop      If true, the track will loop; otherwise, it will play once.
 | 
			
		||||
     * @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 = "music/" + name;
 | 
			
		||||
        this.loop = loop;
 | 
			
		||||
        this.subVolume = subVolume;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the file path of the music track.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The path to the music file (relative to the music folder).
 | 
			
		||||
     */
 | 
			
		||||
    public String getPath() {
 | 
			
		||||
        return path;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets whether the music track should loop.
 | 
			
		||||
     *
 | 
			
		||||
     * @return true if the track should loop, false otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    public boolean getLoop() {
 | 
			
		||||
        return loop;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the relative volume (subVolume) for the music track.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The relative volume for the track, typically a value between 0.0 and 1.0.
 | 
			
		||||
     */
 | 
			
		||||
    public float getSubVolume() {
 | 
			
		||||
        return subVolume;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,41 +0,0 @@
 | 
			
		||||
package pp.mdga.client.acoustic;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Enum representing various sound assets used in the game.
 | 
			
		||||
 * Each constant corresponds to a specific sound effect used throughout the game.
 | 
			
		||||
 * These sounds are associated with various events and actions, such as dice rolls,
 | 
			
		||||
 * turn changes, piece movements, and game outcomes.
 | 
			
		||||
 */
 | 
			
		||||
enum SoundAsset {
 | 
			
		||||
    DICE_ROLL(""),
 | 
			
		||||
    TURN_START(""),
 | 
			
		||||
    TURN_END(""),
 | 
			
		||||
    PIECE_END(""),
 | 
			
		||||
    PIECE_MOVE(""),
 | 
			
		||||
    PIECE_LOST(""),
 | 
			
		||||
    SELECT(""),
 | 
			
		||||
    DESELECT(""),
 | 
			
		||||
    HURRY(""),
 | 
			
		||||
    VICTORY("LevelUp2.wav"),
 | 
			
		||||
    LOST("GameOver.wav");
 | 
			
		||||
 | 
			
		||||
    private final String path;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs a new SoundAsset object with the specified name.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name The name of the sound file.
 | 
			
		||||
     */
 | 
			
		||||
    SoundAsset(String name) {
 | 
			
		||||
        this.path = "sound/" + name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the file path of the sound effect.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The path to the sound file (relative to the sound folder).
 | 
			
		||||
     */
 | 
			
		||||
    public String getPath() {
 | 
			
		||||
        return path;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
package pp.mdga.client.acoustic;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A record that encapsulates a sound asset along with its playback settings:
 | 
			
		||||
 * the relative volume (subVolume) and a delay before it starts playing.
 | 
			
		||||
 */
 | 
			
		||||
record SoundAssetDelayVolume(SoundAsset asset, float subVolume, float delay) {}
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
package pp.mdga.client.animation;
 | 
			
		||||
 | 
			
		||||
abstract class Animation {
 | 
			
		||||
 | 
			
		||||
    abstract void play();
 | 
			
		||||
 | 
			
		||||
    abstract void stop();
 | 
			
		||||
 | 
			
		||||
    abstract boolean isOver();
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
package pp.mdga.client.animation;
 | 
			
		||||
 | 
			
		||||
public enum MdgaAnimation {
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
package pp.mdga.client.board;
 | 
			
		||||
 | 
			
		||||
import pp.mdga.client.Asset;
 | 
			
		||||
 | 
			
		||||
record AssetOnMap(Asset asset, int x, int y, float rot) {}
 | 
			
		||||
@@ -1,323 +0,0 @@
 | 
			
		||||
package pp.mdga.client.board;
 | 
			
		||||
 | 
			
		||||
import com.jme3.material.Material;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.post.FilterPostProcessor;
 | 
			
		||||
import com.jme3.renderer.queue.RenderQueue;
 | 
			
		||||
import com.jme3.scene.Node;
 | 
			
		||||
import com.jme3.scene.Spatial;
 | 
			
		||||
import com.jme3.scene.control.AbstractControl;
 | 
			
		||||
import pp.mdga.client.Asset;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
import pp.mdga.game.Color;
 | 
			
		||||
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
public class BoardHandler {
 | 
			
		||||
    private static final float GRID_SIZE = 1.72f;
 | 
			
		||||
    private static final float GRID_ELEVATION = 0.0f;
 | 
			
		||||
    private static final String MAP_NAME = "map.mdga";
 | 
			
		||||
 | 
			
		||||
    private final MdgaApp app;
 | 
			
		||||
 | 
			
		||||
    private PileControl drawPile = null;
 | 
			
		||||
    private PileControl discardPile = null;
 | 
			
		||||
 | 
			
		||||
    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<UUID, Color> pieceColor;
 | 
			
		||||
 | 
			
		||||
    private Node node;
 | 
			
		||||
 | 
			
		||||
    private FilterPostProcessor fpp;
 | 
			
		||||
 | 
			
		||||
    private boolean init;
 | 
			
		||||
 | 
			
		||||
    public BoardHandler(MdgaApp app, FilterPostProcessor fpp) {
 | 
			
		||||
        if(app == null) throw new RuntimeException("app is null");
 | 
			
		||||
 | 
			
		||||
        this.init = false;
 | 
			
		||||
        this.app = app;
 | 
			
		||||
        this.fpp = fpp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void initMap() {
 | 
			
		||||
        if (init) return;
 | 
			
		||||
 | 
			
		||||
        this.init = true;
 | 
			
		||||
        this.node = new Node("Asset Node");
 | 
			
		||||
        app.getRootNode().attachChild(node);
 | 
			
		||||
 | 
			
		||||
        this.pieces = new HashMap<>();
 | 
			
		||||
        this.colorAssetsMap = new HashMap<>();
 | 
			
		||||
        this.infield = new ArrayList<>(40);
 | 
			
		||||
        this.homeNodesMap = new HashMap<>();
 | 
			
		||||
        this.waitingNodesMap = new HashMap<>();
 | 
			
		||||
        this.waitingPiecesMap = new HashMap<>();
 | 
			
		||||
        this.pieceColor = new HashMap<>();
 | 
			
		||||
 | 
			
		||||
        List<AssetOnMap> assetOnMaps = MapLoader.loadMap(MAP_NAME);
 | 
			
		||||
 | 
			
		||||
        for (AssetOnMap assetOnMap : assetOnMaps) {
 | 
			
		||||
            switch (assetOnMap.asset()) {
 | 
			
		||||
                case lw -> addFigureToPlayerMap(assetToColor(Asset.lw), assetOnMap);
 | 
			
		||||
                case heer -> addFigureToPlayerMap(assetToColor(Asset.heer), assetOnMap);
 | 
			
		||||
                case cir -> addFigureToPlayerMap(assetToColor(Asset.cir), assetOnMap);
 | 
			
		||||
                case marine -> addFigureToPlayerMap(assetToColor(Asset.marine), assetOnMap);
 | 
			
		||||
                case node_normal, node_bonus, node_start ->
 | 
			
		||||
                    infield.add(displayAndControl(assetOnMap, new NodeControl()));
 | 
			
		||||
                case node_home_black -> addHomeNode(homeNodesMap, Color.AIRFORCE, assetOnMap);
 | 
			
		||||
                case node_home_blue -> addHomeNode(homeNodesMap, Color.NAVY, assetOnMap);
 | 
			
		||||
                case node_home_green -> addHomeNode(homeNodesMap, Color.ARMY, assetOnMap);
 | 
			
		||||
                case node_home_yellow -> addHomeNode(homeNodesMap, Color.CYBER, assetOnMap);
 | 
			
		||||
                case node_wait_black -> addHomeNode(waitingNodesMap, Color.AIRFORCE, assetOnMap);
 | 
			
		||||
                case node_wait_blue -> addHomeNode(waitingNodesMap, Color.NAVY, assetOnMap);
 | 
			
		||||
                case node_wait_green -> addHomeNode(waitingNodesMap, Color.ARMY, assetOnMap);
 | 
			
		||||
                case node_wait_yellow -> addHomeNode(waitingNodesMap, Color.CYBER, assetOnMap);
 | 
			
		||||
 | 
			
		||||
                default -> displayAsset(assetOnMap);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Color assetToColor(Asset asset) {
 | 
			
		||||
        return switch (asset) {
 | 
			
		||||
            case lw -> Color.AIRFORCE;
 | 
			
		||||
            case heer -> Color.ARMY;
 | 
			
		||||
            case marine -> Color.NAVY;
 | 
			
		||||
            case cir -> Color.CYBER;
 | 
			
		||||
            default -> throw new RuntimeException("invalid asset");
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Spatial createModel(Asset asset, Vector3f pos, float rot) {
 | 
			
		||||
        String modelName = asset.getModelPath();
 | 
			
		||||
        String texName = asset.getDiffPath();
 | 
			
		||||
        Spatial model = app.getAssetManager().loadModel(modelName);
 | 
			
		||||
        model.scale(asset.getSize());
 | 
			
		||||
        model.rotate((float) Math.toRadians(0), 0, (float) Math.toRadians(rot));
 | 
			
		||||
        model.setLocalTranslation(pos);
 | 
			
		||||
        model.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
 | 
			
		||||
        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
 | 
			
		||||
        mat.setTexture("DiffuseMap", app.getAssetManager().loadTexture(texName));
 | 
			
		||||
        model.setMaterial(mat);
 | 
			
		||||
        node.attachChild(model);
 | 
			
		||||
//        app.getRootNode().attachChild(model);
 | 
			
		||||
        return model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static Vector3f gridToWorld(int x, int y) {
 | 
			
		||||
        return new Vector3f(GRID_SIZE * x, GRID_SIZE * y, GRID_ELEVATION);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Spatial displayAsset(AssetOnMap assetOnMap) {
 | 
			
		||||
        int x = assetOnMap.x();
 | 
			
		||||
        int y = assetOnMap.y();
 | 
			
		||||
        return createModel(assetOnMap.asset(), gridToWorld(x, y), assetOnMap.rot());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private <T extends AbstractControl> T displayAndControl(AssetOnMap assetOnMap, T control) {
 | 
			
		||||
        Spatial spatial = displayAsset(assetOnMap);
 | 
			
		||||
        spatial.addControl(control);
 | 
			
		||||
        return control;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void movePieceToNode(PieceControl pieceControl, NodeControl nodeControl){
 | 
			
		||||
        pieceControl.setLocation(nodeControl.getLocation());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void addHomeNode(Map<Color, List<NodeControl>> map, Color color, AssetOnMap assetOnMap){
 | 
			
		||||
        List<NodeControl> homeNodes = addItemToMapList(map, color, displayAndControl(assetOnMap, new NodeControl()));
 | 
			
		||||
        if (homeNodes.size() > 4) throw new RuntimeException("too many homeNodes for " + color);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private float getRotationMove(Vector3f prev, Vector3f next) {
 | 
			
		||||
        Vector3f direction = next.subtract(prev).normalizeLocal();
 | 
			
		||||
        //I had to reverse dir.y, because then it worked.
 | 
			
		||||
        return (float) Math.toDegrees(Math.atan2(direction.x, -direction.y));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void movePiece_rek(UUID uuid, int curIndex, int moveIndex){
 | 
			
		||||
        if (curIndex == moveIndex) return;
 | 
			
		||||
 | 
			
		||||
        curIndex = (curIndex + 1) % 40;
 | 
			
		||||
 | 
			
		||||
        PieceControl pieceControl = pieces.get(uuid);
 | 
			
		||||
        NodeControl nodeControl = infield.get(curIndex);
 | 
			
		||||
 | 
			
		||||
        pieceControl.setRotation(getRotationMove(pieceControl.getLocation(),nodeControl.getLocation()));
 | 
			
		||||
 | 
			
		||||
        movePieceToNode(pieceControl, nodeControl);
 | 
			
		||||
 | 
			
		||||
        movePiece_rek(uuid, curIndex, moveIndex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private <T, E> List<T> addItemToMapList(Map<E,List<T>> map, E key, T item){
 | 
			
		||||
        List<T> list = map.getOrDefault(key, new ArrayList<>());
 | 
			
		||||
        list.add(item);
 | 
			
		||||
        map.put(key, list);
 | 
			
		||||
        return list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private <T, E> List<T> removeItemFromMapList(Map<E,List<T>> map, E key, T item){
 | 
			
		||||
        List<T> list = map.getOrDefault(key, new ArrayList<>());
 | 
			
		||||
        list.remove(item);
 | 
			
		||||
        map.put(key, list);
 | 
			
		||||
        return list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //public methods****************************************************************************************************
 | 
			
		||||
    public void addPlayer(Color color, List<UUID> uuid) {
 | 
			
		||||
        if (!init) 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");
 | 
			
		||||
        if (uuid.size() != playerAssets.size()) throw new RuntimeException("UUID array and playerAssets are not the same size");
 | 
			
		||||
 | 
			
		||||
        List<NodeControl> waitNodes = waitingNodesMap.get(color);
 | 
			
		||||
        if (waitNodes.size() != playerAssets.size()) throw new RuntimeException("waitNodes size does not match playerAssets size");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < playerAssets.size(); i++){
 | 
			
		||||
            AssetOnMap assetOnMap = playerAssets.get(i);
 | 
			
		||||
            PieceControl pieceControl = displayAndControl(assetOnMap, new PieceControl(assetOnMap.rot(), app.getAssetManager(), app, fpp));
 | 
			
		||||
            movePieceToNode(pieceControl, waitNodes.get(i));
 | 
			
		||||
 | 
			
		||||
            pieces.put(uuid.get(i), pieceControl);
 | 
			
		||||
 | 
			
		||||
            pieceColor.put(uuid.get(i), color);
 | 
			
		||||
 | 
			
		||||
            addItemToMapList(waitingPiecesMap, color, pieceControl);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void moveHomePiece(UUID uuid, int index){
 | 
			
		||||
        if (!init) throw new RuntimeException("BoardHandler is not initialized");
 | 
			
		||||
 | 
			
		||||
        Color color = pieceColor.get(uuid);
 | 
			
		||||
        if(color == null) throw new RuntimeException("uuid is not mapped to a color");
 | 
			
		||||
 | 
			
		||||
        List<NodeControl> homeNodes = homeNodesMap.get(color);
 | 
			
		||||
 | 
			
		||||
        if(homeNodesMap.size() != 4) throw new RuntimeException("HomeNodes for" + color + " are not properly defined");
 | 
			
		||||
 | 
			
		||||
        PieceControl pieceControl = pieces.get(uuid);
 | 
			
		||||
        NodeControl nodeControl = homeNodes.get(index);
 | 
			
		||||
        movePieceToNode(pieceControl, nodeControl);
 | 
			
		||||
 | 
			
		||||
        //rotate piece in direction of homeNodes
 | 
			
		||||
        NodeControl firstHomeNode = homeNodes.get(0);
 | 
			
		||||
        NodeControl lastHomeNode = homeNodes.get(homeNodes.size()-1);
 | 
			
		||||
 | 
			
		||||
        pieceControl.setRotation(getRotationMove(firstHomeNode.getLocation(), lastHomeNode.getLocation()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void movePieceStart(UUID uuid, int nodeIndex){
 | 
			
		||||
        if (!init) throw new RuntimeException("BoardHandler is not initialized");
 | 
			
		||||
 | 
			
		||||
        Color color = pieceColor.get(uuid);
 | 
			
		||||
        if(color == null) throw new RuntimeException("uuid is not mapped to a color");
 | 
			
		||||
 | 
			
		||||
        PieceControl pieceControl = pieces.get(uuid);
 | 
			
		||||
        movePieceToNode(pieceControl, infield.get(nodeIndex));
 | 
			
		||||
 | 
			
		||||
        removeItemFromMapList(waitingPiecesMap, color, pieceControl);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void movePiece(UUID uuid, int curIndex, int moveIndex){
 | 
			
		||||
        if (!init) throw new RuntimeException("BoardHandler is not initialized");
 | 
			
		||||
 | 
			
		||||
        movePiece_rek(uuid, curIndex, moveIndex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void throwPiece(UUID uuid){
 | 
			
		||||
        if (!init) throw new RuntimeException("BoardHandler is not initialized");
 | 
			
		||||
 | 
			
		||||
        Color color = pieceColor.get(uuid);
 | 
			
		||||
        if(color == null) throw new RuntimeException("uuid is not mapped to a color");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        PieceControl pieceControl = pieces.get(uuid);
 | 
			
		||||
        List<NodeControl> waitNodes = waitingNodesMap.get(color);
 | 
			
		||||
        List<PieceControl> waitPieces = waitingPiecesMap.get(color);
 | 
			
		||||
 | 
			
		||||
        movePieceToNode(pieceControl, waitNodes.get(waitPieces.size()));
 | 
			
		||||
        pieceControl.rotateInit();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void shieldPiece(UUID uuid){
 | 
			
		||||
        if (!init) throw new RuntimeException("BoardHandler is not initialized");
 | 
			
		||||
 | 
			
		||||
        pieces.get(uuid).activateShield();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void unshieldPiece(UUID uuid){
 | 
			
		||||
        if (!init) throw new RuntimeException("BoardHandler is not initialized");
 | 
			
		||||
 | 
			
		||||
        pieces.get(uuid).deactivateShield();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void suppressShield(UUID uuid){
 | 
			
		||||
        if (!init) throw new RuntimeException("BoardHandler is not initialized");
 | 
			
		||||
 | 
			
		||||
        pieces.get(uuid).suppressShield();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void swapPieces(UUID piece1, UUID piece2){
 | 
			
		||||
        if (!init) throw new RuntimeException("BoardHandler is not initialized");
 | 
			
		||||
 | 
			
		||||
        PieceControl piece1_control = pieces.get(piece1);
 | 
			
		||||
        PieceControl piece2_control = pieces.get(piece2);
 | 
			
		||||
 | 
			
		||||
        if(piece1_control == null) throw new RuntimeException("piece1 UUID is not valid");
 | 
			
		||||
        if(piece2_control == null) throw new RuntimeException("piece2 UUID is not valid");
 | 
			
		||||
 | 
			
		||||
        float rot1 = piece1_control.getRotation();
 | 
			
		||||
        float rot2 = piece2_control.getRotation();
 | 
			
		||||
 | 
			
		||||
        piece1_control.setRotation(rot2);
 | 
			
		||||
        piece2_control.setRotation(rot1);
 | 
			
		||||
 | 
			
		||||
        Vector3f pos1 = piece1_control.getLocation().clone();
 | 
			
		||||
        Vector3f pos2 = piece2_control.getLocation().clone();
 | 
			
		||||
 | 
			
		||||
        piece1_control.setLocation(pos2);
 | 
			
		||||
        piece2_control.setLocation(pos1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void init(){
 | 
			
		||||
        initMap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void shutdown(){
 | 
			
		||||
        if (!init) return;
 | 
			
		||||
 | 
			
		||||
        init = false;
 | 
			
		||||
        app.getRootNode().detachChild(node);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //List<Pieces>
 | 
			
		||||
    //List<NodesIndexe>
 | 
			
		||||
    public void highlight(UUID uuid, boolean bool){
 | 
			
		||||
        if (!init) throw new RuntimeException("BoardHandler is not initialized");
 | 
			
		||||
 | 
			
		||||
        pieces.get(uuid).outline(bool);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void unHighlight(UUID uuid){
 | 
			
		||||
        if (!init) throw new RuntimeException("BoardHandler is not initialized");
 | 
			
		||||
 | 
			
		||||
        pieces.get(uuid).deOutline();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,77 +0,0 @@
 | 
			
		||||
package pp.mdga.client.board;
 | 
			
		||||
 | 
			
		||||
import com.jme3.light.AmbientLight;
 | 
			
		||||
import com.jme3.light.DirectionalLight;
 | 
			
		||||
import com.jme3.math.ColorRGBA;
 | 
			
		||||
import com.jme3.math.FastMath;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.post.FilterPostProcessor;
 | 
			
		||||
import com.jme3.shadow.DirectionalLightShadowFilter;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
 | 
			
		||||
public class CameraHandler {
 | 
			
		||||
    MdgaApp app;
 | 
			
		||||
 | 
			
		||||
    private DirectionalLight sun;
 | 
			
		||||
    private AmbientLight ambient;
 | 
			
		||||
 | 
			
		||||
    private static final int SHADOWMAP_SIZE = 1024 * 8;
 | 
			
		||||
 | 
			
		||||
    public CameraHandler(MdgaApp app, FilterPostProcessor fpp){
 | 
			
		||||
        this.app = app;
 | 
			
		||||
 | 
			
		||||
        sun = new DirectionalLight();
 | 
			
		||||
        sun.setColor(ColorRGBA.White);
 | 
			
		||||
        sun.setDirection(new Vector3f(0.3f, 0, -1));
 | 
			
		||||
 | 
			
		||||
        ambient = new AmbientLight();
 | 
			
		||||
        ambient.setColor(new ColorRGBA(0.3f, 0.3f, 0.3f, 1));
 | 
			
		||||
 | 
			
		||||
        DirectionalLightShadowFilter dlsf = new DirectionalLightShadowFilter(app.getAssetManager(), SHADOWMAP_SIZE, 4);
 | 
			
		||||
        dlsf.setLight(sun);
 | 
			
		||||
        fpp.addFilter(dlsf);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void init() {
 | 
			
		||||
        app.getRootNode().addLight(sun);
 | 
			
		||||
        app.getRootNode().addLight(ambient);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void shutdown() {
 | 
			
		||||
        app.getRootNode().removeLight(sun);
 | 
			
		||||
        app.getRootNode().removeLight(ambient);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void update(float scroll, float rotation) {
 | 
			
		||||
        float scrollValue = Math.max(0, Math.min(scroll, 100));
 | 
			
		||||
 | 
			
		||||
        float rotationValue = rotation % 360;
 | 
			
		||||
        if (rotationValue < 0) {
 | 
			
		||||
            rotationValue += 360;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        float radius;
 | 
			
		||||
 | 
			
		||||
        float verticalAngle;
 | 
			
		||||
        if (scroll < 100f) {
 | 
			
		||||
            verticalAngle = 20f + (scrollValue / 100f) * 45f;
 | 
			
		||||
            radius = 30f;
 | 
			
		||||
        } else {
 | 
			
		||||
            verticalAngle = 90f;
 | 
			
		||||
            rotationValue = 270f;
 | 
			
		||||
            radius = 50f;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        float verticalAngleRadians = FastMath.DEG_TO_RAD * verticalAngle;
 | 
			
		||||
 | 
			
		||||
        float z = radius * FastMath.sin(verticalAngleRadians);
 | 
			
		||||
        float x = radius * FastMath.cos(verticalAngleRadians) * FastMath.sin(FastMath.DEG_TO_RAD * rotationValue);
 | 
			
		||||
        float y = radius * FastMath.cos(verticalAngleRadians) * FastMath.cos(FastMath.DEG_TO_RAD * rotationValue);
 | 
			
		||||
 | 
			
		||||
        Vector3f cameraPosition = new Vector3f(x, y, z);
 | 
			
		||||
        app.getCamera().setLocation(cameraPosition);
 | 
			
		||||
 | 
			
		||||
        app.getCamera().lookAt(Vector3f.ZERO, Vector3f.UNIT_Z);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,92 +0,0 @@
 | 
			
		||||
package pp.mdga.client.board;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import pp.mdga.client.Asset;
 | 
			
		||||
 | 
			
		||||
import java.io.BufferedReader;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.io.InputStreamReader;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
class MapLoader {
 | 
			
		||||
    private MapLoader() {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static List<AssetOnMap> loadMap(String mapName) {
 | 
			
		||||
        List<AssetOnMap> assetsOnMap = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        try (
 | 
			
		||||
            InputStream inputStream = MapLoader.class.getClassLoader().getResourceAsStream(mapName);
 | 
			
		||||
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))
 | 
			
		||||
        ) {
 | 
			
		||||
 | 
			
		||||
            while (true) {
 | 
			
		||||
                String entry = reader.readLine();
 | 
			
		||||
                if (entry == null) break;
 | 
			
		||||
 | 
			
		||||
                entry = entry.trim();
 | 
			
		||||
 | 
			
		||||
                if (entry.isEmpty()) continue;
 | 
			
		||||
                if (entry.charAt(0) == '#') continue;
 | 
			
		||||
 | 
			
		||||
                String[] parts = entry.trim().split(" ");
 | 
			
		||||
 | 
			
		||||
                assert (parts.length == 3) : "MapLoader: line has not 3 parts";
 | 
			
		||||
 | 
			
		||||
                String assetName = parts[0];
 | 
			
		||||
                String[] coordinates = parts[1].split(",");
 | 
			
		||||
 | 
			
		||||
                assert (coordinates.length == 2) : "MapLoade: coordinates are wrong";
 | 
			
		||||
 | 
			
		||||
                int x = Integer.parseInt(coordinates[0]);
 | 
			
		||||
                int y = Integer.parseInt(coordinates[1]);
 | 
			
		||||
 | 
			
		||||
                float rot = Float.parseFloat(parts[2]);
 | 
			
		||||
 | 
			
		||||
                Asset asset = getLoadedAsset(assetName);
 | 
			
		||||
                assetsOnMap.add(new AssetOnMap(asset, x, y, rot));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (IOException e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
        }
 | 
			
		||||
        catch (IllegalArgumentException e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return assetsOnMap;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static Asset getLoadedAsset(String assetName) {
 | 
			
		||||
        return switch (assetName) {
 | 
			
		||||
            case "lw" -> Asset.lw;
 | 
			
		||||
            case "cir" -> Asset.cir;
 | 
			
		||||
            case "marine" -> Asset.marine;
 | 
			
		||||
            case "heer" -> Asset.heer;
 | 
			
		||||
            case "node" -> Asset.node_normal;
 | 
			
		||||
            case "node_start" -> Asset.node_start;
 | 
			
		||||
            case "node_bonus" -> Asset.node_bonus;
 | 
			
		||||
            case "node_home_blue" -> Asset.node_home_blue;
 | 
			
		||||
            case "node_home_yellow" -> Asset.node_home_yellow;
 | 
			
		||||
            case "node_home_black" -> Asset.node_home_black;
 | 
			
		||||
            case "node_home_green" -> Asset.node_home_green;
 | 
			
		||||
            case "node_wait_blue" -> Asset.node_wait_blue;
 | 
			
		||||
            case "node_wait_yellow" -> Asset.node_wait_yellow;
 | 
			
		||||
            case "node_wait_black" -> Asset.node_wait_black;
 | 
			
		||||
            case "node_wait_green" -> Asset.node_wait_green;
 | 
			
		||||
            case "world" -> Asset.world;
 | 
			
		||||
            case "jet" -> Asset.jet;
 | 
			
		||||
            case "big_tent" -> Asset.bigTent;
 | 
			
		||||
            case "small_tent" -> Asset.smallTent;
 | 
			
		||||
            case "radar" -> Asset.radar;
 | 
			
		||||
            case "ship" -> Asset.ship;
 | 
			
		||||
            case "tank" -> Asset.tank;
 | 
			
		||||
            case "tree_small" -> Asset.tree_small;
 | 
			
		||||
            case "tree_big" -> Asset.tree_big;
 | 
			
		||||
            default -> throw new IllegalStateException("Unexpected value: " + assetName);
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,22 +0,0 @@
 | 
			
		||||
package pp.mdga.client.board;
 | 
			
		||||
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.renderer.RenderManager;
 | 
			
		||||
import com.jme3.renderer.ViewPort;
 | 
			
		||||
import com.jme3.scene.control.AbstractControl;
 | 
			
		||||
 | 
			
		||||
public class NodeControl extends AbstractControl {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void controlUpdate(float v) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void controlRender(RenderManager renderManager, ViewPort viewPort) {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Vector3f getLocation(){
 | 
			
		||||
        return this.getSpatial().getLocalTranslation();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,79 +0,0 @@
 | 
			
		||||
package pp.mdga.client.board.Outline;
 | 
			
		||||
 | 
			
		||||
import com.jme3.asset.AssetManager;
 | 
			
		||||
import com.jme3.material.Material;
 | 
			
		||||
import com.jme3.material.MaterialDef;
 | 
			
		||||
import com.jme3.math.ColorRGBA;
 | 
			
		||||
import com.jme3.math.Vector2f;
 | 
			
		||||
import com.jme3.post.Filter;
 | 
			
		||||
import com.jme3.renderer.RenderManager;
 | 
			
		||||
import com.jme3.renderer.ViewPort;
 | 
			
		||||
import com.jme3.texture.FrameBuffer;
 | 
			
		||||
 | 
			
		||||
  
 | 
			
		||||
public class OutlineFilter extends Filter {
 | 
			
		||||
 | 
			
		||||
	private OutlinePreFilter outlinePreFilter;
 | 
			
		||||
	private ColorRGBA outlineColor = new ColorRGBA(0, 1, 0, 1);
 | 
			
		||||
	private float outlineWidth = 1;
 | 
			
		||||
 | 
			
		||||
	public OutlineFilter(OutlinePreFilter outlinePreFilter) {
 | 
			
		||||
		super("OutlineFilter");
 | 
			
		||||
		this.outlinePreFilter = outlinePreFilter;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	protected void initFilter(AssetManager assetManager, RenderManager renderManager, ViewPort vp, int w, int h) {
 | 
			
		||||
		MaterialDef matDef = (MaterialDef) assetManager.loadAsset("MatDefs/SelectObjectOutliner/Outline.j3md");
 | 
			
		||||
		material = new Material(matDef);
 | 
			
		||||
		material.setVector2("Resolution", new Vector2f(w, h));
 | 
			
		||||
		material.setColor("OutlineColor", outlineColor);
 | 
			
		||||
		material.setFloat("OutlineWidth", outlineWidth);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	protected void preFrame(float tpf) {
 | 
			
		||||
		super.preFrame(tpf);
 | 
			
		||||
		material.setTexture("OutlineDepthTexture", outlinePreFilter.getOutlineTexture());
 | 
			
		||||
//		System.out.println("OutlineFilter.preFrame()");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) {
 | 
			
		||||
		super.postFrame(renderManager, viewPort, prevFilterBuffer, sceneBuffer);
 | 
			
		||||
//		material.setTexture("OutlineDepthTexture", outlinePreFilter.getDefaultPassDepthTexture());
 | 
			
		||||
//		System.out.println("OutlineFilter.postFrame()");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	protected Material getMaterial() {
 | 
			
		||||
		return material;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public ColorRGBA getOutlineColor() {
 | 
			
		||||
		return outlineColor;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setOutlineColor(ColorRGBA outlineColor) {
 | 
			
		||||
		this.outlineColor = outlineColor;
 | 
			
		||||
		if (material != null) {
 | 
			
		||||
			material.setColor("OutlineColor", outlineColor);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public float getOutlineWidth() {
 | 
			
		||||
		return outlineWidth;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setOutlineWidth(float outlineWidth) {
 | 
			
		||||
		this.outlineWidth = outlineWidth;
 | 
			
		||||
		if (material != null) {
 | 
			
		||||
			material.setFloat("OutlineWidth", outlineWidth);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public OutlinePreFilter getOutlinePreFilter() {
 | 
			
		||||
		return outlinePreFilter;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,67 +0,0 @@
 | 
			
		||||
package pp.mdga.client.board.Outline;
 | 
			
		||||
 | 
			
		||||
import com.jme3.asset.AssetManager;
 | 
			
		||||
import com.jme3.material.Material;
 | 
			
		||||
import com.jme3.post.Filter;
 | 
			
		||||
import com.jme3.renderer.RenderManager;
 | 
			
		||||
import com.jme3.renderer.Renderer;
 | 
			
		||||
import com.jme3.renderer.ViewPort;
 | 
			
		||||
import com.jme3.renderer.queue.RenderQueue;
 | 
			
		||||
import com.jme3.texture.FrameBuffer;
 | 
			
		||||
import com.jme3.texture.Image.Format;
 | 
			
		||||
import com.jme3.texture.Texture;
 | 
			
		||||
 | 
			
		||||
  
 | 
			
		||||
public class OutlinePreFilter extends Filter {
 | 
			
		||||
 | 
			
		||||
	private Pass normalPass;
 | 
			
		||||
	private RenderManager renderManager;
 | 
			
		||||
 
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates a OutlinePreFilter
 | 
			
		||||
	 */
 | 
			
		||||
	public OutlinePreFilter() {
 | 
			
		||||
		super("OutlinePreFilter");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	protected boolean isRequiresDepthTexture() {
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	protected void postQueue(RenderQueue queue) {
 | 
			
		||||
		Renderer r = renderManager.getRenderer();
 | 
			
		||||
		r.setFrameBuffer(normalPass.getRenderFrameBuffer());
 | 
			
		||||
		renderManager.getRenderer().clearBuffers(true, true, false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) {
 | 
			
		||||
		super.postFrame(renderManager, viewPort, prevFilterBuffer, sceneBuffer);
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	protected Material getMaterial() {
 | 
			
		||||
		return material;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public Texture getOutlineTexture() {
 | 
			
		||||
		return normalPass.getRenderedTexture();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
 | 
			
		||||
		this.renderManager = renderManager;
 | 
			
		||||
		normalPass = new Pass();
 | 
			
		||||
		normalPass.init(renderManager.getRenderer(), w, h, Format.RGBA8, Format.Depth);
 | 
			
		||||
		material = new Material(manager, "MatDefs/SelectObjectOutliner/OutlinePre.j3md");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	protected void cleanUpFilter(Renderer r) {
 | 
			
		||||
		normalPass.cleanup(r);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,71 +0,0 @@
 | 
			
		||||
package pp.mdga.client.board.Outline;
 | 
			
		||||
 | 
			
		||||
import com.jme3.asset.AssetManager;
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
public class SelectObjectOutliner {
 | 
			
		||||
 | 
			
		||||
    private final FilterPostProcessor fpp;
 | 
			
		||||
    private final RenderManager renderManager;
 | 
			
		||||
    private final AssetManager assetManager;
 | 
			
		||||
    private final Camera cam;
 | 
			
		||||
    private final int width;
 | 
			
		||||
    private boolean selected;
 | 
			
		||||
    private ViewPort outlineViewport = null;
 | 
			
		||||
    private OutlineFilter outlineFilter = null;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public SelectObjectOutliner(int width, FilterPostProcessor fpp, RenderManager renderManager, AssetManager assetManager, Camera cam) {
 | 
			
		||||
        this.selected = false;
 | 
			
		||||
        this.fpp = fpp;
 | 
			
		||||
        this.renderManager = renderManager;
 | 
			
		||||
        this.assetManager = assetManager;
 | 
			
		||||
        this.cam = cam;
 | 
			
		||||
        this.width = width;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void deselect(Spatial model) {
 | 
			
		||||
        if(selected){
 | 
			
		||||
            selected = false;
 | 
			
		||||
            hideOutlineFilterEffect(model);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void select(Spatial model, ColorRGBA color) {
 | 
			
		||||
        if(!selected){
 | 
			
		||||
            selected = true;
 | 
			
		||||
            showOutlineFilterEffect(model, width, color);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void hideOutlineFilterEffect(Spatial model) {
 | 
			
		||||
        outlineFilter.setEnabled(false);
 | 
			
		||||
        outlineFilter.getOutlinePreFilter().setEnabled(false);
 | 
			
		||||
        fpp.removeFilter(outlineFilter);
 | 
			
		||||
        outlineViewport.detachScene(model);
 | 
			
		||||
        renderManager.removePreView(outlineViewport);
 | 
			
		||||
        outlineViewport = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void showOutlineFilterEffect(Spatial model, int width, ColorRGBA color) {
 | 
			
		||||
        outlineViewport = renderManager.createPreView("outlineViewport", cam);
 | 
			
		||||
        FilterPostProcessor outlineFpp = new FilterPostProcessor(assetManager);
 | 
			
		||||
 | 
			
		||||
        OutlinePreFilter outlinePreFilter = new OutlinePreFilter();
 | 
			
		||||
        outlineFpp.addFilter(outlinePreFilter);
 | 
			
		||||
 | 
			
		||||
        outlineViewport.attachScene(model);
 | 
			
		||||
        outlineViewport.addProcessor(outlineFpp);
 | 
			
		||||
 | 
			
		||||
        outlineFilter = new OutlineFilter(outlinePreFilter);
 | 
			
		||||
        outlineFilter.setOutlineColor(color);
 | 
			
		||||
        outlineFilter.setOutlineWidth(width);
 | 
			
		||||
 | 
			
		||||
        fpp.addFilter(outlineFilter);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,150 +0,0 @@
 | 
			
		||||
package pp.mdga.client.board;
 | 
			
		||||
 | 
			
		||||
import com.jme3.asset.AssetManager;
 | 
			
		||||
import com.jme3.material.Material;
 | 
			
		||||
import com.jme3.material.RenderState;
 | 
			
		||||
import com.jme3.math.ColorRGBA;
 | 
			
		||||
import com.jme3.math.Quaternion;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.post.FilterPostProcessor;
 | 
			
		||||
import com.jme3.renderer.RenderManager;
 | 
			
		||||
import com.jme3.renderer.ViewPort;
 | 
			
		||||
import com.jme3.renderer.queue.RenderQueue;
 | 
			
		||||
import com.jme3.scene.Geometry;
 | 
			
		||||
import com.jme3.scene.Node;
 | 
			
		||||
import com.jme3.scene.Spatial;
 | 
			
		||||
import com.jme3.scene.control.AbstractControl;
 | 
			
		||||
import pp.mdga.client.Asset;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
import pp.mdga.client.board.Outline.SelectObjectOutliner;
 | 
			
		||||
 | 
			
		||||
public class PieceControl extends AbstractControl {
 | 
			
		||||
    private final float initRotation;
 | 
			
		||||
    private final AssetManager assetManager;
 | 
			
		||||
    private Spatial shieldRing;
 | 
			
		||||
    private final Material shieldMat;
 | 
			
		||||
 | 
			
		||||
    private static final float SHIELD_SPEED = 1f;
 | 
			
		||||
    private static final float SHIELD_TRANSPARENCY = 0.6f;
 | 
			
		||||
    private static final ColorRGBA SHIELD_COLOR = new ColorRGBA(0, 0.9f, 1, SHIELD_TRANSPARENCY);
 | 
			
		||||
    private static final ColorRGBA SHIELD_SUPPRESSED_COLOR = new ColorRGBA(1f, 0.5f, 0, SHIELD_TRANSPARENCY);
 | 
			
		||||
    private static final float SHIELD_Z = 0f;
 | 
			
		||||
 | 
			
		||||
    SelectObjectOutliner outlineOwn;
 | 
			
		||||
 | 
			
		||||
    private static final ColorRGBA OUTLINE_OWN_COLOR = ColorRGBA.White;
 | 
			
		||||
    private static final ColorRGBA OUTLINE_ENEMY_COLOR = ColorRGBA.Red;
 | 
			
		||||
    private static final int OUTLINE_THICKNESS = 4;
 | 
			
		||||
    private final Node parentNode;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public PieceControl(float initRotation, AssetManager assetManager, MdgaApp app, FilterPostProcessor fpp){
 | 
			
		||||
        super();
 | 
			
		||||
        this.parentNode = new Node();
 | 
			
		||||
        this.initRotation = initRotation;
 | 
			
		||||
        this.assetManager = assetManager;
 | 
			
		||||
        this.shieldRing = null;
 | 
			
		||||
        this.shieldMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
 | 
			
		||||
        this.shieldMat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        outlineOwn = new SelectObjectOutliner(OUTLINE_THICKNESS, fpp, app.getRenderManager(), app.getAssetManager(), app.getCamera());
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public float getRotation() {
 | 
			
		||||
        return (float) Math.toDegrees(this.spatial.getLocalRotation().toAngleAxis(new Vector3f(0,0,1)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setRotation(float rot){
 | 
			
		||||
        Quaternion quaternion = new Quaternion();
 | 
			
		||||
        quaternion.fromAngleAxis((float) Math.toRadians(rot), new Vector3f(0,0,1));
 | 
			
		||||
        this.spatial.setLocalRotation(quaternion);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Vector3f getLocation(){
 | 
			
		||||
        return this.getSpatial().getLocalTranslation();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void controlUpdate(float delta) {
 | 
			
		||||
        if(shieldRing != null){
 | 
			
		||||
            shieldRing.rotate(0, 0, delta * SHIELD_SPEED);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void controlRender(RenderManager renderManager, ViewPort viewPort) {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setLocation(Vector3f loc){
 | 
			
		||||
        this.getSpatial().setLocalTranslation(loc);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setSpatial(Spatial spatial){
 | 
			
		||||
        if(this.getSpatial() == null && spatial != null){
 | 
			
		||||
            super.setSpatial(spatial);
 | 
			
		||||
            initSpatial();
 | 
			
		||||
        }
 | 
			
		||||
        else{
 | 
			
		||||
            super.setSpatial(spatial);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void initSpatial(){
 | 
			
		||||
        setRotation(this.initRotation);
 | 
			
		||||
 | 
			
		||||
        Node oldParent = this.spatial.getParent();
 | 
			
		||||
        this.parentNode.setName(this.spatial.getName() + " Parent");
 | 
			
		||||
        oldParent.detachChild(this.getSpatial());
 | 
			
		||||
        this.parentNode.attachChild(this.getSpatial());
 | 
			
		||||
        oldParent.attachChild(this.parentNode);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void rotateInit() {
 | 
			
		||||
//        rotate(rotation - initRotation);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void activateShield(){
 | 
			
		||||
        shieldRing = assetManager.loadModel(Asset.shield_ring.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)));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        shieldRing.setQueueBucket(RenderQueue.Bucket.Transparent); // Render in the transparent bucket
 | 
			
		||||
        shieldMat.setColor("Color", SHIELD_COLOR);
 | 
			
		||||
        shieldRing.setMaterial(shieldMat);
 | 
			
		||||
 | 
			
		||||
        parentNode.attachChild(shieldRing);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void deactivateShield(){
 | 
			
		||||
        parentNode.detachChild(shieldRing);
 | 
			
		||||
        shieldRing = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void suppressShield(){
 | 
			
		||||
        assert(shieldRing != null) : "PieceControl: shieldRing is not set";
 | 
			
		||||
        shieldMat.setColor("Color", SHIELD_SUPPRESSED_COLOR);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setMaterial(Material mat){
 | 
			
		||||
        this.spatial.setMaterial(mat);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Material getMaterial(){
 | 
			
		||||
        return ((Geometry) this.spatial).getMaterial();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void outline(boolean enemy) {
 | 
			
		||||
        ColorRGBA color = enemy ? OUTLINE_ENEMY_COLOR : OUTLINE_OWN_COLOR;
 | 
			
		||||
        outlineOwn.select(this.getSpatial(), color);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void deOutline() {
 | 
			
		||||
        outlineOwn.deselect(this.getSpatial());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
package pp.mdga.client.board;
 | 
			
		||||
 | 
			
		||||
class PileControl {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
package pp.mdga.client.board;
 | 
			
		||||
 | 
			
		||||
public enum Rotation {
 | 
			
		||||
    UP,
 | 
			
		||||
    RIGHT,
 | 
			
		||||
    DOWN,
 | 
			
		||||
    LEFT,
 | 
			
		||||
    UP_LEFT,
 | 
			
		||||
    UP_RIGHT,
 | 
			
		||||
    DOWN_RIGHT,
 | 
			
		||||
    DOWN_LEFT
 | 
			
		||||
}
 | 
			
		||||
@@ -1,82 +0,0 @@
 | 
			
		||||
package pp.mdga.client.dialog;
 | 
			
		||||
 | 
			
		||||
import com.jme3.math.ColorRGBA;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.scene.Node;
 | 
			
		||||
import com.simsilica.lemur.Button;
 | 
			
		||||
import com.simsilica.lemur.Container;
 | 
			
		||||
import com.simsilica.lemur.HAlignment;
 | 
			
		||||
import com.simsilica.lemur.VAlignment;
 | 
			
		||||
import com.simsilica.lemur.component.QuadBackgroundComponent;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
 | 
			
		||||
import static com.jme3.math.FastMath.floor;
 | 
			
		||||
 | 
			
		||||
public abstract class Dialog {
 | 
			
		||||
    protected final ColorRGBA COLOR_DEFAULT = ColorRGBA.Gray;
 | 
			
		||||
    protected final ColorRGBA COLOR_HOVER = ColorRGBA.DarkGray;
 | 
			
		||||
 | 
			
		||||
    protected Container container;
 | 
			
		||||
 | 
			
		||||
    protected final MdgaApp app;
 | 
			
		||||
    private final Node node;
 | 
			
		||||
 | 
			
		||||
    protected final float vertical_step;
 | 
			
		||||
    protected final float horitontal_step;
 | 
			
		||||
 | 
			
		||||
    protected float fontSize = 35;
 | 
			
		||||
 | 
			
		||||
    public Dialog(MdgaApp app, Node node) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
        this.node = node;
 | 
			
		||||
        this.container = new Container();
 | 
			
		||||
 | 
			
		||||
        this.horitontal_step = app.getCamera().getWidth() / 16;
 | 
			
		||||
        this.vertical_step = app.getCamera().getHeight() / 9;
 | 
			
		||||
 | 
			
		||||
        int val = (int) (32 * Math.min(app.getResolutionFactor() * 0.9f, 1));
 | 
			
		||||
        fontSize = val;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void show() {
 | 
			
		||||
        node.attachChild(container);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void hide () {
 | 
			
		||||
        node.detachChild(container);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void createButton(String label, Runnable action, Vector3f size) {
 | 
			
		||||
        Button button = new Button(label);
 | 
			
		||||
        button.addClickCommands(source -> action.run());
 | 
			
		||||
        button.setFontSize(fontSize);
 | 
			
		||||
        button.setHighlightColor(ColorRGBA.White);
 | 
			
		||||
        button.setColor(ColorRGBA.Black);
 | 
			
		||||
        button.setPreferredSize(size);
 | 
			
		||||
        button.setTextHAlignment(HAlignment.Center);
 | 
			
		||||
        button.setTextVAlignment(VAlignment.Center);
 | 
			
		||||
 | 
			
		||||
        QuadBackgroundComponent background = new QuadBackgroundComponent(COLOR_DEFAULT);
 | 
			
		||||
        background.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
 | 
			
		||||
        button.setBackground(background);
 | 
			
		||||
 | 
			
		||||
        button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOn, (source) -> {
 | 
			
		||||
            QuadBackgroundComponent hoverBackground = new QuadBackgroundComponent(COLOR_HOVER);
 | 
			
		||||
            hoverBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
 | 
			
		||||
            source.setBackground(hoverBackground);
 | 
			
		||||
            button.setHighlightColor(ColorRGBA.White);
 | 
			
		||||
            button.setColor(ColorRGBA.Black);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOff, (source) -> {
 | 
			
		||||
            QuadBackgroundComponent normalBackground = new QuadBackgroundComponent(COLOR_DEFAULT);
 | 
			
		||||
            normalBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
 | 
			
		||||
            source.setBackground(normalBackground);
 | 
			
		||||
            button.setHighlightColor(ColorRGBA.White);
 | 
			
		||||
            button.setColor(ColorRGBA.Black);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        container.addChild(button);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
package pp.mdga.client.dialog;
 | 
			
		||||
 | 
			
		||||
import java.util.function.Supplier;
 | 
			
		||||
 | 
			
		||||
public class GetPercentRunnable {
 | 
			
		||||
    private final Supplier<Float> action;
 | 
			
		||||
 | 
			
		||||
    public GetPercentRunnable(Supplier<Float> action) {
 | 
			
		||||
        this.action = action;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public float get() {
 | 
			
		||||
        return action.get();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,143 +0,0 @@
 | 
			
		||||
package pp.mdga.client.dialog;
 | 
			
		||||
 | 
			
		||||
import com.jme3.math.ColorRGBA;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.scene.Node;
 | 
			
		||||
import com.jme3.texture.Texture;
 | 
			
		||||
import com.simsilica.lemur.*;
 | 
			
		||||
import com.simsilica.lemur.component.QuadBackgroundComponent;
 | 
			
		||||
import com.simsilica.lemur.component.SpringGridLayout;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
 | 
			
		||||
public class HostDialog extends Dialog {
 | 
			
		||||
    private TextField portInput;
 | 
			
		||||
 | 
			
		||||
    public HostDialog(MdgaApp app, Node node, Runnable backAction) {
 | 
			
		||||
        super(app, node);
 | 
			
		||||
 | 
			
		||||
        QuadBackgroundComponent quad1 = new QuadBackgroundComponent(ColorRGBA.Gray);
 | 
			
		||||
        quad1.setMargin(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor());
 | 
			
		||||
        container.setBackground(quad1);
 | 
			
		||||
 | 
			
		||||
        Texture texture = app.getAssetManager().loadTexture("mdga_logo.png");
 | 
			
		||||
 | 
			
		||||
        QuadBackgroundComponent b = new QuadBackgroundComponent(texture);
 | 
			
		||||
 | 
			
		||||
        Panel imagePanel = new Panel();
 | 
			
		||||
        imagePanel.setBackground(b);
 | 
			
		||||
 | 
			
		||||
        container.addChild(imagePanel).setPreferredSize(new Vector3f(texture.getImage().getWidth() / 4 * app.getResolutionFactor(), texture.getImage().getHeight() / 4 * app.getResolutionFactor(), 0));
 | 
			
		||||
 | 
			
		||||
        //abstandshalter
 | 
			
		||||
        container.addChild(new Panel(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray));
 | 
			
		||||
 | 
			
		||||
        createTextField();
 | 
			
		||||
 | 
			
		||||
        //abstandshalter
 | 
			
		||||
        container.addChild(new Panel(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray));
 | 
			
		||||
 | 
			
		||||
        Container sub = new Container(new SpringGridLayout(Axis.X, Axis.Y));
 | 
			
		||||
 | 
			
		||||
        createButton(sub, "Abbrechen", backAction, new Vector3f(170 * app.getResolutionFactor(), 60 * app.getResolutionFactor(), 0));
 | 
			
		||||
 | 
			
		||||
        sub.addChild(new Panel(40 * app.getResolutionFactor(), 0 * app.getResolutionFactor(), ColorRGBA.Gray));
 | 
			
		||||
 | 
			
		||||
        createButton(sub, "Starten", () -> tryStart(), new Vector3f(170 * app.getResolutionFactor(), 60 * app.getResolutionFactor(), 0));
 | 
			
		||||
 | 
			
		||||
        container.addChild(sub);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void tryStart() {
 | 
			
		||||
        if (null == portInput.getText()) {
 | 
			
		||||
            portInput.setText("");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int port = 0;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            port = Integer.parseInt(portInput.getText());
 | 
			
		||||
        } catch (NumberFormatException e) {
 | 
			
		||||
            portInput.setText("");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(port >= 1 && port <= 65535) {
 | 
			
		||||
            app.getModelSyncronizer().setHost(port);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        portInput.setText("");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void show() {
 | 
			
		||||
        super.show();
 | 
			
		||||
 | 
			
		||||
        container.setLocalTranslation(
 | 
			
		||||
            app.getCamera().getWidth() / 2 - container.getPreferredSize().x / 2,
 | 
			
		||||
            app.getCamera().getHeight() / 2 + container.getPreferredSize().y / 2,
 | 
			
		||||
            0
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void hide() {
 | 
			
		||||
        super.hide();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void createTextField() {
 | 
			
		||||
        Container subContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
 | 
			
		||||
 | 
			
		||||
        Label nameLabel = new Label("Port:\t");
 | 
			
		||||
        nameLabel.setFontSize(fontSize);
 | 
			
		||||
        nameLabel.setColor(ColorRGBA.Black);
 | 
			
		||||
 | 
			
		||||
        portInput = new TextField("");
 | 
			
		||||
 | 
			
		||||
        portInput.setColor(ColorRGBA.Black);
 | 
			
		||||
        portInput.setTextHAlignment(HAlignment.Left);
 | 
			
		||||
        portInput.setFontSize(fontSize);
 | 
			
		||||
        portInput.setSingleLine(true);
 | 
			
		||||
 | 
			
		||||
        QuadBackgroundComponent grayBackground = new QuadBackgroundComponent(ColorRGBA.DarkGray);
 | 
			
		||||
        portInput.setBackground(grayBackground);
 | 
			
		||||
 | 
			
		||||
        subContainer.addChild(nameLabel);
 | 
			
		||||
        subContainer.addChild(portInput);
 | 
			
		||||
 | 
			
		||||
        container.addChild(subContainer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void createButton(Container c, String label, Runnable action, Vector3f size) {
 | 
			
		||||
        Button button = new Button(label);
 | 
			
		||||
        button.addClickCommands(source -> action.run());
 | 
			
		||||
        button.setFontSize(fontSize);
 | 
			
		||||
        button.setHighlightColor(ColorRGBA.White);
 | 
			
		||||
        button.setColor(ColorRGBA.Black);
 | 
			
		||||
        button.setPreferredSize(size);
 | 
			
		||||
        button.setTextHAlignment(HAlignment.Center);
 | 
			
		||||
        button.setTextVAlignment(VAlignment.Center);
 | 
			
		||||
 | 
			
		||||
        QuadBackgroundComponent background = new QuadBackgroundComponent(COLOR_DEFAULT);
 | 
			
		||||
        background.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
 | 
			
		||||
        button.setBackground(background);
 | 
			
		||||
 | 
			
		||||
        button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOn, (source) -> {
 | 
			
		||||
            QuadBackgroundComponent hoverBackground = new QuadBackgroundComponent(COLOR_HOVER);
 | 
			
		||||
            hoverBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
 | 
			
		||||
            source.setBackground(hoverBackground);
 | 
			
		||||
            button.setHighlightColor(ColorRGBA.White);
 | 
			
		||||
            button.setColor(ColorRGBA.Black);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOff, (source) -> {
 | 
			
		||||
            QuadBackgroundComponent normalBackground = new QuadBackgroundComponent(COLOR_DEFAULT);
 | 
			
		||||
            normalBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
 | 
			
		||||
            source.setBackground(normalBackground);
 | 
			
		||||
            button.setHighlightColor(ColorRGBA.White);
 | 
			
		||||
            button.setColor(ColorRGBA.Black);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        c.addChild(button);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,192 +0,0 @@
 | 
			
		||||
package pp.mdga.client.dialog;
 | 
			
		||||
 | 
			
		||||
import com.jme3.math.ColorRGBA;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.scene.Node;
 | 
			
		||||
import com.jme3.texture.Texture;
 | 
			
		||||
import com.simsilica.lemur.*;
 | 
			
		||||
import com.simsilica.lemur.component.QuadBackgroundComponent;
 | 
			
		||||
import com.simsilica.lemur.component.SpringGridLayout;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
 | 
			
		||||
public class JoinDialog extends Dialog {
 | 
			
		||||
    private TextField portInput;
 | 
			
		||||
    private TextField ipInput;
 | 
			
		||||
 | 
			
		||||
    public JoinDialog(MdgaApp app, Node node, Runnable backAction) {
 | 
			
		||||
        super(app, node);
 | 
			
		||||
 | 
			
		||||
        QuadBackgroundComponent quad1 = new QuadBackgroundComponent(ColorRGBA.Gray);
 | 
			
		||||
        quad1.setMargin(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor());
 | 
			
		||||
        container.setBackground(quad1);
 | 
			
		||||
 | 
			
		||||
        Texture texture = app.getAssetManager().loadTexture("mdga_logo.png");
 | 
			
		||||
 | 
			
		||||
        QuadBackgroundComponent b = new QuadBackgroundComponent(texture);
 | 
			
		||||
 | 
			
		||||
        Panel imagePanel = new Panel();
 | 
			
		||||
        imagePanel.setBackground(b);
 | 
			
		||||
 | 
			
		||||
        container.addChild(imagePanel).setPreferredSize(new Vector3f(texture.getImage().getWidth() / 4 * app.getResolutionFactor(), texture.getImage().getHeight() / 4 * app.getResolutionFactor(), 0));
 | 
			
		||||
 | 
			
		||||
        //abstandshalter
 | 
			
		||||
        container.addChild(new Panel(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray));
 | 
			
		||||
 | 
			
		||||
        createIpField();
 | 
			
		||||
 | 
			
		||||
        //abstandshalter
 | 
			
		||||
        container.addChild(new Panel(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray));
 | 
			
		||||
 | 
			
		||||
        createPortField();
 | 
			
		||||
 | 
			
		||||
        //abstandshalter
 | 
			
		||||
        container.addChild(new Panel(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray));
 | 
			
		||||
 | 
			
		||||
        Container sub = new Container(new SpringGridLayout(Axis.X, Axis.Y));
 | 
			
		||||
 | 
			
		||||
        createButton(sub, "Abbrechen", backAction, new Vector3f(170 * app.getResolutionFactor(), 60 * app.getResolutionFactor(), 0));
 | 
			
		||||
 | 
			
		||||
        sub.addChild(new Panel(40 * app.getResolutionFactor(), 0 * app.getResolutionFactor(), ColorRGBA.Gray));
 | 
			
		||||
 | 
			
		||||
        createButton(sub, "Beitreten", () -> tryJoin(), new Vector3f(170 * app.getResolutionFactor(), 60 * app.getResolutionFactor(), 0));
 | 
			
		||||
 | 
			
		||||
        container.addChild(sub);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void tryJoin() {
 | 
			
		||||
        if (null == portInput.getText()) {
 | 
			
		||||
            portInput.setText("");
 | 
			
		||||
            ipInput.setText("");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int port = 0;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            port = Integer.parseInt(portInput.getText());
 | 
			
		||||
        } catch (NumberFormatException e) {
 | 
			
		||||
            portInput.setText("");
 | 
			
		||||
            ipInput.setText("");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(!(port >= 1 && port <= 65535)) {
 | 
			
		||||
            portInput.setText("");
 | 
			
		||||
            ipInput.setText("");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (null == ipInput.getText()) {
 | 
			
		||||
            portInput.setText("");
 | 
			
		||||
            ipInput.setText("");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        String ipv4Pattern = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
 | 
			
		||||
 | 
			
		||||
        if(!Pattern.matches(ipv4Pattern, ipInput.getText())) {
 | 
			
		||||
            portInput.setText("");
 | 
			
		||||
            ipInput.setText("");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        app.getModelSyncronizer().setJoin(ipInput.getText(), port);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void show() {
 | 
			
		||||
        super.show();
 | 
			
		||||
 | 
			
		||||
        container.setLocalTranslation(
 | 
			
		||||
            app.getCamera().getWidth() / 2 - container.getPreferredSize().x / 2,
 | 
			
		||||
            app.getCamera().getHeight() / 2 + container.getPreferredSize().y / 2,
 | 
			
		||||
            0
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void hide() {
 | 
			
		||||
        super.hide();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void createPortField() {
 | 
			
		||||
        Container subContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
 | 
			
		||||
 | 
			
		||||
        Label nameLabel = new Label("Port:\t");
 | 
			
		||||
        nameLabel.setFontSize(fontSize);
 | 
			
		||||
        nameLabel.setColor(ColorRGBA.Black);
 | 
			
		||||
 | 
			
		||||
        portInput = new TextField("");
 | 
			
		||||
 | 
			
		||||
        portInput.setColor(ColorRGBA.Black);
 | 
			
		||||
        portInput.setTextHAlignment(HAlignment.Left);
 | 
			
		||||
        portInput.setFontSize(fontSize);
 | 
			
		||||
        portInput.setSingleLine(true);
 | 
			
		||||
 | 
			
		||||
        QuadBackgroundComponent grayBackground = new QuadBackgroundComponent(ColorRGBA.DarkGray);
 | 
			
		||||
        portInput.setBackground(grayBackground);
 | 
			
		||||
 | 
			
		||||
        subContainer.addChild(nameLabel);
 | 
			
		||||
        subContainer.addChild(portInput);
 | 
			
		||||
 | 
			
		||||
        container.addChild(subContainer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void createIpField() {
 | 
			
		||||
        Container subContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
 | 
			
		||||
 | 
			
		||||
        Label nameLabel = new Label("Ip:\t");
 | 
			
		||||
        nameLabel.setFontSize(fontSize);
 | 
			
		||||
        nameLabel.setColor(ColorRGBA.Black);
 | 
			
		||||
 | 
			
		||||
        ipInput = new TextField("");
 | 
			
		||||
 | 
			
		||||
        ipInput.setColor(ColorRGBA.Black);
 | 
			
		||||
        ipInput.setTextHAlignment(HAlignment.Left);
 | 
			
		||||
        ipInput.setFontSize(fontSize);
 | 
			
		||||
        ipInput.setSingleLine(true);
 | 
			
		||||
 | 
			
		||||
        QuadBackgroundComponent grayBackground = new QuadBackgroundComponent(ColorRGBA.DarkGray);
 | 
			
		||||
        ipInput.setBackground(grayBackground);
 | 
			
		||||
 | 
			
		||||
        subContainer.addChild(nameLabel);
 | 
			
		||||
        subContainer.addChild(ipInput);
 | 
			
		||||
 | 
			
		||||
        container.addChild(subContainer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void createButton(Container c, String label, Runnable action, Vector3f size) {
 | 
			
		||||
        Button button = new Button(label);
 | 
			
		||||
        button.addClickCommands(source -> action.run());
 | 
			
		||||
        button.setFontSize(fontSize);
 | 
			
		||||
        button.setHighlightColor(ColorRGBA.White);
 | 
			
		||||
        button.setColor(ColorRGBA.Black);
 | 
			
		||||
        button.setPreferredSize(size);
 | 
			
		||||
        button.setTextHAlignment(HAlignment.Center);
 | 
			
		||||
        button.setTextVAlignment(VAlignment.Center);
 | 
			
		||||
 | 
			
		||||
        QuadBackgroundComponent background = new QuadBackgroundComponent(COLOR_DEFAULT);
 | 
			
		||||
        background.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
 | 
			
		||||
        button.setBackground(background);
 | 
			
		||||
 | 
			
		||||
        button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOn, (source) -> {
 | 
			
		||||
            QuadBackgroundComponent hoverBackground = new QuadBackgroundComponent(COLOR_HOVER);
 | 
			
		||||
            hoverBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
 | 
			
		||||
            source.setBackground(hoverBackground);
 | 
			
		||||
            button.setHighlightColor(ColorRGBA.White);
 | 
			
		||||
            button.setColor(ColorRGBA.Black);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOff, (source) -> {
 | 
			
		||||
            QuadBackgroundComponent normalBackground = new QuadBackgroundComponent(COLOR_DEFAULT);
 | 
			
		||||
            normalBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
 | 
			
		||||
            source.setBackground(normalBackground);
 | 
			
		||||
            button.setHighlightColor(ColorRGBA.White);
 | 
			
		||||
            button.setColor(ColorRGBA.Black);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        c.addChild(button);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,165 +0,0 @@
 | 
			
		||||
package pp.mdga.client.dialog;
 | 
			
		||||
 | 
			
		||||
import com.jme3.math.ColorRGBA;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.scene.Node;
 | 
			
		||||
import com.simsilica.lemur.Button;
 | 
			
		||||
import com.simsilica.lemur.Command;
 | 
			
		||||
import com.simsilica.lemur.HAlignment;
 | 
			
		||||
import com.simsilica.lemur.VAlignment;
 | 
			
		||||
import com.simsilica.lemur.component.QuadBackgroundComponent;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
import pp.mdga.game.Color;
 | 
			
		||||
 | 
			
		||||
public class LobbyButtonDialog extends Dialog {
 | 
			
		||||
    private final int pos;
 | 
			
		||||
 | 
			
		||||
    private Button button;
 | 
			
		||||
 | 
			
		||||
    private Command<Button> clickCommand;
 | 
			
		||||
 | 
			
		||||
    private boolean taken = false;
 | 
			
		||||
 | 
			
		||||
    private final String label;
 | 
			
		||||
 | 
			
		||||
    private int val;
 | 
			
		||||
 | 
			
		||||
    public LobbyButtonDialog(MdgaApp app, Node node, String label, int pos) {
 | 
			
		||||
        super(app, node);
 | 
			
		||||
 | 
			
		||||
        clickCommand = (Button source) -> {
 | 
			
		||||
            toggleButton();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        this.pos = pos;
 | 
			
		||||
 | 
			
		||||
        this.label = label;
 | 
			
		||||
        this.button = new Button(label);
 | 
			
		||||
        this.button.setFontSize(fontSize);
 | 
			
		||||
 | 
			
		||||
        val = pos;
 | 
			
		||||
 | 
			
		||||
        createNotTakenButton(new Vector3f(170 * app.getResolutionFactor(), 250 * app.getResolutionFactor(), 0));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void show() {
 | 
			
		||||
        super.show();
 | 
			
		||||
 | 
			
		||||
        float x = ((2 + (4 * pos)) * horitontal_step) - ((0.33333333f * pos) * container.getPreferredSize().x);
 | 
			
		||||
        float y = 6 * vertical_step;
 | 
			
		||||
 | 
			
		||||
        container.setLocalTranslation(x, y, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void hide() {
 | 
			
		||||
        super.hide();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setTaken(boolean isTaken, boolean self, String name) {
 | 
			
		||||
        taken = isTaken;
 | 
			
		||||
 | 
			
		||||
        if(isTaken) {
 | 
			
		||||
            createTakenButton(new Vector3f(170 * app.getResolutionFactor(), 250 * app.getResolutionFactor(), 0), self);
 | 
			
		||||
 | 
			
		||||
            button.setText(label + "\n\n" + name);
 | 
			
		||||
        } else {
 | 
			
		||||
            createNotTakenButton(new Vector3f(170 * app.getResolutionFactor(), 250 * app.getResolutionFactor(), 0));
 | 
			
		||||
 | 
			
		||||
            button.setText(label);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void createNotTakenButton(Vector3f size) {
 | 
			
		||||
        button.setHighlightColor(ColorRGBA.White);
 | 
			
		||||
        button.setColor(ColorRGBA.Black);
 | 
			
		||||
        button.setPreferredSize(size);
 | 
			
		||||
        button.setTextHAlignment(HAlignment.Center);
 | 
			
		||||
        button.setTextVAlignment(VAlignment.Center);
 | 
			
		||||
 | 
			
		||||
        if(button.getClickCommands() != null) {
 | 
			
		||||
            button.removeClickCommands(clickCommand);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        button.addClickCommands(clickCommand);
 | 
			
		||||
 | 
			
		||||
        QuadBackgroundComponent background = new QuadBackgroundComponent(COLOR_DEFAULT);
 | 
			
		||||
        background.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
 | 
			
		||||
        button.setBackground(background);
 | 
			
		||||
 | 
			
		||||
        button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOn, (source) -> {
 | 
			
		||||
            QuadBackgroundComponent hoverBackground = new QuadBackgroundComponent(COLOR_HOVER);
 | 
			
		||||
            hoverBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
 | 
			
		||||
            source.setBackground(hoverBackground);
 | 
			
		||||
            button.setHighlightColor(ColorRGBA.White);
 | 
			
		||||
            button.setColor(ColorRGBA.Black);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOff, (source) -> {
 | 
			
		||||
            QuadBackgroundComponent normalBackground = new QuadBackgroundComponent(COLOR_DEFAULT);
 | 
			
		||||
            normalBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
 | 
			
		||||
            source.setBackground(normalBackground);
 | 
			
		||||
            button.setHighlightColor(ColorRGBA.White);
 | 
			
		||||
            button.setColor(ColorRGBA.Black);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        container.addChild(button);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void toggleButton() {
 | 
			
		||||
        if(taken) {
 | 
			
		||||
            app.getModelSyncronizer().unselectTsk();
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            app.getModelSyncronizer().selectTsk(Color.values()[val]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void createTakenButton(Vector3f size, boolean self) {
 | 
			
		||||
        ColorRGBA color_a;
 | 
			
		||||
        ColorRGBA color_b;
 | 
			
		||||
 | 
			
		||||
        if(button.getClickCommands() != null) {
 | 
			
		||||
            button.removeClickCommands(clickCommand);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(!self) {
 | 
			
		||||
            color_a = ColorRGBA.Red;
 | 
			
		||||
            color_b = ColorRGBA.Red;
 | 
			
		||||
        } else {
 | 
			
		||||
            color_a = ColorRGBA.Green;
 | 
			
		||||
            color_b = ColorRGBA.Yellow;
 | 
			
		||||
 | 
			
		||||
            button.addClickCommands(clickCommand);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        button.setHighlightColor(ColorRGBA.White);
 | 
			
		||||
        button.setColor(ColorRGBA.Black);
 | 
			
		||||
        button.setPreferredSize(size);
 | 
			
		||||
        button.setTextHAlignment(HAlignment.Center);
 | 
			
		||||
        button.setTextVAlignment(VAlignment.Center);
 | 
			
		||||
 | 
			
		||||
        QuadBackgroundComponent background = new QuadBackgroundComponent(color_a);
 | 
			
		||||
        background.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
 | 
			
		||||
        button.setBackground(background);
 | 
			
		||||
 | 
			
		||||
        button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOn, (source) -> {
 | 
			
		||||
            QuadBackgroundComponent hoverBackground = new QuadBackgroundComponent(color_b);
 | 
			
		||||
            hoverBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
 | 
			
		||||
            source.setBackground(hoverBackground);
 | 
			
		||||
            button.setHighlightColor(ColorRGBA.White);
 | 
			
		||||
            button.setColor(ColorRGBA.Black);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOff, (source) -> {
 | 
			
		||||
            QuadBackgroundComponent normalBackground = new QuadBackgroundComponent(color_a);
 | 
			
		||||
            normalBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
 | 
			
		||||
            source.setBackground(normalBackground);
 | 
			
		||||
            button.setHighlightColor(ColorRGBA.White);
 | 
			
		||||
            button.setColor(ColorRGBA.Black);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        container.addChild(button);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
package pp.mdga.client.dialog;
 | 
			
		||||
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
 | 
			
		||||
public class PercentRunnable {
 | 
			
		||||
    private final Consumer<Float> action;
 | 
			
		||||
 | 
			
		||||
    public PercentRunnable(Consumer<Float> action) {
 | 
			
		||||
        this.action = action;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void run(float percent) {
 | 
			
		||||
        action.accept(percent);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,78 +0,0 @@
 | 
			
		||||
package pp.mdga.client.dialog;
 | 
			
		||||
 | 
			
		||||
import com.jme3.math.ColorRGBA;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.scene.Node;
 | 
			
		||||
import com.jme3.texture.Texture;
 | 
			
		||||
import com.simsilica.lemur.Button;
 | 
			
		||||
import com.simsilica.lemur.HAlignment;
 | 
			
		||||
import com.simsilica.lemur.Panel;
 | 
			
		||||
import com.simsilica.lemur.VAlignment;
 | 
			
		||||
import com.simsilica.lemur.component.IconComponent;
 | 
			
		||||
import com.simsilica.lemur.component.QuadBackgroundComponent;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
 | 
			
		||||
public class SettingsButtonDialog extends Dialog {
 | 
			
		||||
    private IconComponent icon;
 | 
			
		||||
 | 
			
		||||
    public SettingsButtonDialog(MdgaApp app, Node node, String label, Runnable action) {
 | 
			
		||||
        super(app, node);
 | 
			
		||||
 | 
			
		||||
        icon = new IconComponent("zahnrad.png");
 | 
			
		||||
        icon.setIconScale(0.1f * app.getResolutionFactor());
 | 
			
		||||
 | 
			
		||||
        createButton(label, action, new Vector3f(60 * app.getResolutionFactor(), 60 * app.getResolutionFactor(), 0));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void show() {
 | 
			
		||||
        super.show();
 | 
			
		||||
 | 
			
		||||
        float x = (15.5f * horitontal_step) - container.getPreferredSize().x;
 | 
			
		||||
        float y = 8.5f * vertical_step;
 | 
			
		||||
 | 
			
		||||
        container.setLocalTranslation(x, y, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void hide() {
 | 
			
		||||
        super.hide();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void createButton(String label, Runnable action, Vector3f size) {
 | 
			
		||||
        Button button = new Button(label);
 | 
			
		||||
        button.addClickCommands(source -> action.run());
 | 
			
		||||
        button.setFontSize(fontSize);
 | 
			
		||||
        button.setHighlightColor(ColorRGBA.White);
 | 
			
		||||
        button.setColor(ColorRGBA.Black);
 | 
			
		||||
        button.setPreferredSize(size);
 | 
			
		||||
        button.setTextHAlignment(HAlignment.Center);
 | 
			
		||||
        button.setTextVAlignment(VAlignment.Center);
 | 
			
		||||
 | 
			
		||||
        QuadBackgroundComponent background = new QuadBackgroundComponent(COLOR_DEFAULT);
 | 
			
		||||
        background.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
 | 
			
		||||
        button.setBackground(background);
 | 
			
		||||
        button.setIcon(icon);
 | 
			
		||||
 | 
			
		||||
        button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOn, (source) -> {
 | 
			
		||||
            QuadBackgroundComponent hoverBackground = new QuadBackgroundComponent(COLOR_HOVER);
 | 
			
		||||
            hoverBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
 | 
			
		||||
            source.setBackground(hoverBackground);
 | 
			
		||||
            button.setHighlightColor(ColorRGBA.White);
 | 
			
		||||
            button.setColor(ColorRGBA.Black);
 | 
			
		||||
            button.setIcon(icon);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOff, (source) -> {
 | 
			
		||||
            QuadBackgroundComponent normalBackground = new QuadBackgroundComponent(COLOR_DEFAULT);
 | 
			
		||||
            normalBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
 | 
			
		||||
            source.setBackground(normalBackground);
 | 
			
		||||
            button.setHighlightColor(ColorRGBA.White);
 | 
			
		||||
            button.setColor(ColorRGBA.Black);
 | 
			
		||||
            button.setIcon(icon);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        container.addChild(button);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,112 +0,0 @@
 | 
			
		||||
package pp.mdga.client.dialog;
 | 
			
		||||
 | 
			
		||||
import com.jme3.math.ColorRGBA;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.scene.Node;
 | 
			
		||||
import com.jme3.scene.Spatial;
 | 
			
		||||
import com.jme3.texture.Texture;
 | 
			
		||||
import com.simsilica.lemur.*;
 | 
			
		||||
import com.simsilica.lemur.component.QuadBackgroundComponent;
 | 
			
		||||
import com.simsilica.lemur.component.SpringGridLayout;
 | 
			
		||||
import com.simsilica.lemur.event.CursorListener;
 | 
			
		||||
import com.simsilica.lemur.event.CursorMotionEvent;
 | 
			
		||||
import com.simsilica.lemur.event.MouseEventControl;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
import com.simsilica.lemur.*;
 | 
			
		||||
import com.simsilica.lemur.style.*;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
 | 
			
		||||
public class SettingsDialog extends Dialog {
 | 
			
		||||
    private TextField nameInput;
 | 
			
		||||
 | 
			
		||||
    private HashMap<Slider, PercentRunnable> map = new HashMap<Slider, PercentRunnable>();
 | 
			
		||||
    private HashMap<Slider, GetPercentRunnable> map2 = new HashMap<Slider, GetPercentRunnable>();
 | 
			
		||||
 | 
			
		||||
    public SettingsDialog(MdgaApp app, Node node, String path) {
 | 
			
		||||
        super(app, node);
 | 
			
		||||
 | 
			
		||||
        QuadBackgroundComponent quad1 = new QuadBackgroundComponent(ColorRGBA.Gray);
 | 
			
		||||
        quad1.setMargin(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor());
 | 
			
		||||
        container.setBackground(quad1);
 | 
			
		||||
 | 
			
		||||
        Texture texture = app.getAssetManager().loadTexture(path);
 | 
			
		||||
 | 
			
		||||
        QuadBackgroundComponent b = new QuadBackgroundComponent(texture);
 | 
			
		||||
 | 
			
		||||
        Panel imagePanel = new Panel();
 | 
			
		||||
        imagePanel.setBackground(b);
 | 
			
		||||
 | 
			
		||||
        container.addChild(imagePanel).setPreferredSize(new Vector3f((texture.getImage().getWidth() / 2) * app.getResolutionFactor(), (texture.getImage().getHeight() / 2) * app.getResolutionFactor(), 0));
 | 
			
		||||
 | 
			
		||||
        //abstandshalter
 | 
			
		||||
        container.addChild(new Panel(80 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void show() {
 | 
			
		||||
        super.show();
 | 
			
		||||
 | 
			
		||||
        container.setLocalTranslation(
 | 
			
		||||
            app.getCamera().getWidth() / 2 - container.getPreferredSize().x / 2,
 | 
			
		||||
            app.getCamera().getHeight() / 2 + container.getPreferredSize().y / 2,
 | 
			
		||||
            0
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void hide() {
 | 
			
		||||
        super.hide();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void addButton(String label, Runnable action, Vector3f size) {
 | 
			
		||||
        createButton(label, action, size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void addSlider(String label, PercentRunnable action, GetPercentRunnable action2, Vector3f size, int start) {
 | 
			
		||||
        Container subContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
 | 
			
		||||
 | 
			
		||||
        Slider slider = new Slider("slider");
 | 
			
		||||
 | 
			
		||||
        QuadBackgroundComponent background = new QuadBackgroundComponent(ColorRGBA.DarkGray);
 | 
			
		||||
        slider.setBackground(background);
 | 
			
		||||
 | 
			
		||||
        slider.setPreferredSize(size);
 | 
			
		||||
        slider.setModel(new DefaultRangedValueModel(0, 100, start));
 | 
			
		||||
        slider.setPreferredSize(new Vector3f(150 * app.getResolutionFactor(), 30 * app.getResolutionFactor(), 0));
 | 
			
		||||
        slider.getDecrementButton().setText(" - ");
 | 
			
		||||
        slider.getIncrementButton().setText(" + ");
 | 
			
		||||
        slider.getDecrementButton().setFontSize(25 * app.getResolutionFactor());
 | 
			
		||||
        slider.getIncrementButton().setFontSize(25 * app.getResolutionFactor());
 | 
			
		||||
 | 
			
		||||
        Label nameLabel = new Label(label);
 | 
			
		||||
        nameLabel.setFontSize(fontSize);
 | 
			
		||||
        nameLabel.setColor(ColorRGBA.Black);
 | 
			
		||||
        nameLabel.setPreferredSize(new Vector3f(150 * app.getResolutionFactor(), 10 * app.getResolutionFactor(), 0));
 | 
			
		||||
 | 
			
		||||
        subContainer.addChild(nameLabel);
 | 
			
		||||
        subContainer.addChild(slider);
 | 
			
		||||
 | 
			
		||||
        container.addChild(subContainer);
 | 
			
		||||
 | 
			
		||||
        map.put(slider, action);
 | 
			
		||||
        map2.put(slider, action2);
 | 
			
		||||
 | 
			
		||||
        //abstandshalter
 | 
			
		||||
        container.addChild(new Panel(20 * app.getResolutionFactor(), 10 * app.getResolutionFactor(), ColorRGBA.Gray));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void initVolume() {
 | 
			
		||||
        map2.forEach((slider, runnable) -> {
 | 
			
		||||
            double val = (double) runnable.get();
 | 
			
		||||
            slider.getModel().setPercent(val);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void update() {
 | 
			
		||||
        map.forEach((slider, runnable) -> {
 | 
			
		||||
            float val = (float) slider.getModel().getPercent();
 | 
			
		||||
            runnable.run(val);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,28 +0,0 @@
 | 
			
		||||
package pp.mdga.client.dialog;
 | 
			
		||||
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.scene.Node;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
 | 
			
		||||
public class SingleButtonLeftDialog extends Dialog {
 | 
			
		||||
    public SingleButtonLeftDialog(MdgaApp app, Node node, String label, Runnable action) {
 | 
			
		||||
        super(app, node);
 | 
			
		||||
 | 
			
		||||
        createButton(label, action, new Vector3f(170 * app.getResolutionFactor(), 60 * app.getResolutionFactor(), 0));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void show() {
 | 
			
		||||
        super.show();
 | 
			
		||||
 | 
			
		||||
        float x = 2 * horitontal_step;
 | 
			
		||||
        float y = 1.8f * vertical_step;
 | 
			
		||||
 | 
			
		||||
        container.setLocalTranslation(x, y, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void hide() {
 | 
			
		||||
        super.hide();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,28 +0,0 @@
 | 
			
		||||
package pp.mdga.client.dialog;
 | 
			
		||||
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.scene.Node;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
 | 
			
		||||
public class SingleButtonRightDialog extends Dialog {
 | 
			
		||||
    public SingleButtonRightDialog(MdgaApp app, Node node, String label, Runnable action) {
 | 
			
		||||
        super(app, node);
 | 
			
		||||
 | 
			
		||||
        createButton(label, action, new Vector3f(170 * app.getResolutionFactor(), 60 * app.getResolutionFactor(), 0));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void show() {
 | 
			
		||||
        super.show();
 | 
			
		||||
 | 
			
		||||
        float x = (14 * horitontal_step) - container.getPreferredSize().x;
 | 
			
		||||
        float y = 1.8f * vertical_step;
 | 
			
		||||
 | 
			
		||||
        container.setLocalTranslation(x, y, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void hide() {
 | 
			
		||||
        super.hide();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,86 +0,0 @@
 | 
			
		||||
package pp.mdga.client.dialog;
 | 
			
		||||
 | 
			
		||||
import com.jme3.math.ColorRGBA;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.scene.Node;
 | 
			
		||||
import com.jme3.texture.Texture;
 | 
			
		||||
import com.simsilica.lemur.*;
 | 
			
		||||
import com.simsilica.lemur.component.QuadBackgroundComponent;
 | 
			
		||||
import com.simsilica.lemur.component.SpringGridLayout;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
 | 
			
		||||
public class StartDialog extends Dialog {
 | 
			
		||||
    private TextField nameInput;
 | 
			
		||||
 | 
			
		||||
    public StartDialog(MdgaApp app, Node node) {
 | 
			
		||||
        super(app, node);
 | 
			
		||||
 | 
			
		||||
        QuadBackgroundComponent quad1 = new QuadBackgroundComponent(ColorRGBA.Gray);
 | 
			
		||||
        quad1.setMargin(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor());
 | 
			
		||||
        container.setBackground(quad1);
 | 
			
		||||
 | 
			
		||||
        Texture texture = app.getAssetManager().loadTexture("mdga_logo.png");
 | 
			
		||||
 | 
			
		||||
        QuadBackgroundComponent b = new QuadBackgroundComponent(texture);
 | 
			
		||||
 | 
			
		||||
        Panel imagePanel = new Panel();
 | 
			
		||||
        imagePanel.setBackground(b);
 | 
			
		||||
 | 
			
		||||
        container.addChild(imagePanel).setPreferredSize(new Vector3f(texture.getImage().getWidth() / 4, texture.getImage().getHeight() / 4, 0));
 | 
			
		||||
 | 
			
		||||
        //abstandshalter
 | 
			
		||||
        container.addChild(new Panel(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray));
 | 
			
		||||
 | 
			
		||||
        createTextField();
 | 
			
		||||
 | 
			
		||||
        //abstandshalter
 | 
			
		||||
        container.addChild(new Panel(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void show() {
 | 
			
		||||
        super.show();
 | 
			
		||||
 | 
			
		||||
        container.setLocalTranslation(
 | 
			
		||||
            app.getCamera().getWidth() / 2 - container.getPreferredSize().x / 2,
 | 
			
		||||
            app.getCamera().getHeight() / 2 + container.getPreferredSize().y / 2,
 | 
			
		||||
            0
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void hide() {
 | 
			
		||||
        super.hide();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void addButton(String label, Runnable action, Vector3f size) {
 | 
			
		||||
        createButton(label, action, size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void createTextField() {
 | 
			
		||||
        Container subContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
 | 
			
		||||
 | 
			
		||||
        Label nameLabel = new Label("Name:\t");
 | 
			
		||||
        nameLabel.setFontSize(fontSize);
 | 
			
		||||
        nameLabel.setColor(ColorRGBA.Black);
 | 
			
		||||
 | 
			
		||||
        nameInput = new TextField("");
 | 
			
		||||
 | 
			
		||||
        nameInput.setColor(ColorRGBA.Black);
 | 
			
		||||
        nameInput.setTextHAlignment(HAlignment.Left);
 | 
			
		||||
        nameInput.setFontSize(fontSize);
 | 
			
		||||
        nameInput.setSingleLine(true);
 | 
			
		||||
 | 
			
		||||
        QuadBackgroundComponent grayBackground = new QuadBackgroundComponent(ColorRGBA.DarkGray);
 | 
			
		||||
        nameInput.setBackground(grayBackground);
 | 
			
		||||
 | 
			
		||||
        subContainer.addChild(nameLabel);
 | 
			
		||||
        subContainer.addChild(nameInput);
 | 
			
		||||
 | 
			
		||||
        container.addChild(subContainer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getName() {
 | 
			
		||||
        return nameInput.getText();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,23 +0,0 @@
 | 
			
		||||
package pp.mdga.client.gui;
 | 
			
		||||
 | 
			
		||||
import com.jme3.renderer.RenderManager;
 | 
			
		||||
import com.jme3.renderer.ViewPort;
 | 
			
		||||
import com.jme3.scene.control.AbstractControl;
 | 
			
		||||
 | 
			
		||||
public class CardControl extends AbstractControl {
 | 
			
		||||
 | 
			
		||||
    public CardControl(){
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void controlUpdate(float tpf) {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void controlRender(RenderManager rm, ViewPort vp) {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,77 +0,0 @@
 | 
			
		||||
package pp.mdga.client.gui;
 | 
			
		||||
 | 
			
		||||
import com.jme3.app.Application;
 | 
			
		||||
import com.jme3.app.state.AbstractAppState;
 | 
			
		||||
import com.jme3.app.state.AppStateManager;
 | 
			
		||||
import com.jme3.material.Material;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.renderer.Camera;
 | 
			
		||||
import com.jme3.renderer.RenderManager;
 | 
			
		||||
import com.jme3.renderer.ViewPort;
 | 
			
		||||
import com.jme3.scene.Node;
 | 
			
		||||
import com.jme3.scene.Spatial;
 | 
			
		||||
import pp.mdga.client.Asset;
 | 
			
		||||
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
public class CardLayer extends AbstractAppState {
 | 
			
		||||
 | 
			
		||||
    private Node root;
 | 
			
		||||
    private Application app;
 | 
			
		||||
    private boolean init;
 | 
			
		||||
 | 
			
		||||
    private List<Spatial> cardBuffer;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public CardLayer() {
 | 
			
		||||
        this.cardBuffer = new ArrayList<>();
 | 
			
		||||
        init = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void initialize(AppStateManager stateManager, Application app ) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
        root = new Node("Under gui viewport Root");
 | 
			
		||||
 | 
			
		||||
        Camera originalCam = app.getCamera();
 | 
			
		||||
 | 
			
		||||
        Camera cam = new Camera(originalCam.getWidth(), originalCam.getHeight());
 | 
			
		||||
        cam.setParallelProjection(false);
 | 
			
		||||
        cam.setFrustum(originalCam.getFrustumNear(), originalCam.getFrustumFar(), originalCam.getFrustumLeft(), originalCam.getFrustumRight(),originalCam.getFrustumTop(), originalCam.getFrustumBottom());
 | 
			
		||||
        cam.setFov(originalCam.getFov());
 | 
			
		||||
        cam.setLocation(new Vector3f(0, 0, 10));
 | 
			
		||||
        cam.lookAt(new Vector3f(0,0,0), Vector3f.UNIT_Y);
 | 
			
		||||
 | 
			
		||||
        ViewPort view = app.getRenderManager().createMainView("Under gui ViewPort", cam);
 | 
			
		||||
        view.setEnabled(true);
 | 
			
		||||
        view.setClearFlags(false, true, false);
 | 
			
		||||
        view.attachScene(root);
 | 
			
		||||
 | 
			
		||||
        if(!init) init = true;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void render(RenderManager rm) {
 | 
			
		||||
        root.updateGeometricState();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void update( float tpf ) {
 | 
			
		||||
        root.updateLogicalState(tpf);
 | 
			
		||||
 | 
			
		||||
        if (init && !cardBuffer.isEmpty()) {
 | 
			
		||||
            for(Spatial spatial : cardBuffer){
 | 
			
		||||
                root.attachChild(spatial);
 | 
			
		||||
            }
 | 
			
		||||
            cardBuffer.clear();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void addCard(Spatial card){
 | 
			
		||||
        cardBuffer.add(card);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,70 +0,0 @@
 | 
			
		||||
package pp.mdga.client.gui;
 | 
			
		||||
 | 
			
		||||
import com.jme3.material.Material;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.scene.Spatial;
 | 
			
		||||
import pp.mdga.client.Asset;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
import pp.mdga.game.BonusCard;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
public class GuiHandler {
 | 
			
		||||
 | 
			
		||||
    private MdgaApp app;
 | 
			
		||||
    private CardLayer cardLayer;
 | 
			
		||||
    private Map<UUID, CardControl> ownCardsMap;
 | 
			
		||||
 | 
			
		||||
    private static final Vector3f START = new Vector3f(-3,-3,0);
 | 
			
		||||
    private static final Vector3f MARGIN = new Vector3f(2.5f,0,0);
 | 
			
		||||
 | 
			
		||||
    public GuiHandler(MdgaApp app) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void init(){
 | 
			
		||||
        cardLayer = new CardLayer();
 | 
			
		||||
        app.getStateManager().attach(cardLayer);
 | 
			
		||||
        ownCardsMap = new HashMap<>();
 | 
			
		||||
 | 
			
		||||
        addCard(BonusCard.SHIELD, UUID.randomUUID());
 | 
			
		||||
        addCard(BonusCard.TURBO, UUID.randomUUID());
 | 
			
		||||
        addCard(BonusCard.SWAP, UUID.randomUUID());
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Asset bonusToAsset(BonusCard card){
 | 
			
		||||
        return switch (card){
 | 
			
		||||
            case TURBO -> Asset.turboCard;
 | 
			
		||||
            case SHIELD -> Asset.shieldCard;
 | 
			
		||||
            case SWAP -> Asset.swapCard;
 | 
			
		||||
            case HIDDEN -> throw new RuntimeException("HIDDEN is not allowed in GUI");
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void addCard(BonusCard card, UUID uuid) {
 | 
			
		||||
        CardControl control = createCard(bonusToAsset(card), nextPos());
 | 
			
		||||
        ownCardsMap.put(uuid, control);
 | 
			
		||||
        cardLayer.addCard(control.getSpatial());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Vector3f nextPos() {
 | 
			
		||||
        return START.add(MARGIN.mult(ownCardsMap.size()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private CardControl createCard(Asset card, Vector3f pos){
 | 
			
		||||
        Spatial spatial = app.getAssetManager().loadModel(card.getModelPath());
 | 
			
		||||
        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
 | 
			
		||||
        mat.setTexture("ColorMap", app.getAssetManager().loadTexture(card.getDiffPath()));
 | 
			
		||||
        spatial.setMaterial(mat);
 | 
			
		||||
        spatial.setLocalScale(1f);
 | 
			
		||||
        spatial.setLocalTranslation(pos);
 | 
			
		||||
        spatial.rotate((float)Math.toRadians(90), (float)Math.toRadians(180), (float)Math.toRadians(180));
 | 
			
		||||
        CardControl control = new CardControl();
 | 
			
		||||
        spatial.addControl(control);
 | 
			
		||||
        return control;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,235 +0,0 @@
 | 
			
		||||
package pp.mdga.client.server;
 | 
			
		||||
 | 
			
		||||
import com.jme3.network.*;
 | 
			
		||||
import com.jme3.network.serializing.Serializer;
 | 
			
		||||
import pp.mdga.game.Game;
 | 
			
		||||
import pp.mdga.game.Player;
 | 
			
		||||
import pp.mdga.message.client.*;
 | 
			
		||||
import pp.mdga.message.server.*;
 | 
			
		||||
import pp.mdga.server.ServerGameLogic;
 | 
			
		||||
import pp.mdga.server.ServerSender;
 | 
			
		||||
 | 
			
		||||
import java.io.FileInputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.lang.System.Logger;
 | 
			
		||||
import java.lang.System.Logger.Level;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.concurrent.BlockingQueue;
 | 
			
		||||
import java.util.concurrent.LinkedBlockingQueue;
 | 
			
		||||
import java.util.logging.LogManager;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Server implementing the visitor pattern as MessageReceiver for ClientMessages
 | 
			
		||||
 */
 | 
			
		||||
public class MdgaServer implements MessageListener<HostedConnection>, ConnectionListener, ServerSender {
 | 
			
		||||
    private static final Logger LOGGER = System.getLogger(MdgaServer.class.getName());
 | 
			
		||||
 | 
			
		||||
    private Server myServer;
 | 
			
		||||
    private final ServerGameLogic logic;
 | 
			
		||||
    private final BlockingQueue<ReceivedMessage> pendingMessages = new LinkedBlockingQueue<>();
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        // Configure logging
 | 
			
		||||
        LogManager manager = LogManager.getLogManager();
 | 
			
		||||
        try {
 | 
			
		||||
            manager.readConfiguration(new FileInputStream("logging.properties"));
 | 
			
		||||
            LOGGER.log(Level.INFO, "Successfully read logging properties"); //NON-NLS
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            LOGGER.log(Level.INFO, e.getMessage());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Starts the Battleships server.
 | 
			
		||||
     */
 | 
			
		||||
    public static void main(String[] args) {
 | 
			
		||||
        new MdgaServer().run();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new MdgaServer.
 | 
			
		||||
     */
 | 
			
		||||
    public MdgaServer() {
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
            initializeSerializables();
 | 
			
		||||
            myServer.start();
 | 
			
		||||
            registerListeners();
 | 
			
		||||
            LOGGER.log(Level.INFO, "Server started: {0}", myServer.isRunning()); //NON-NLS
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            LOGGER.log(Level.ERROR, "Couldn't start server: {0}", e.getMessage()); //NON-NLS
 | 
			
		||||
            exit(1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void processNextMessage() {
 | 
			
		||||
        try {
 | 
			
		||||
            pendingMessages.take().process(logic);
 | 
			
		||||
        } catch (InterruptedException ex) {
 | 
			
		||||
            LOGGER.log(Level.INFO, "Interrupted while waiting for messages"); //NON-NLS
 | 
			
		||||
            Thread.currentThread().interrupt();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void initializeSerializables() {
 | 
			
		||||
        Serializer.registerClass(AnimationEnd.class);
 | 
			
		||||
        Serializer.registerClass(ClientStartGame.class);
 | 
			
		||||
        Serializer.registerClass(DeselectTSK.class);
 | 
			
		||||
        Serializer.registerClass(ForceContinueGame.class);
 | 
			
		||||
        Serializer.registerClass(StartGame.class);
 | 
			
		||||
        Serializer.registerClass(JoinServer.class);
 | 
			
		||||
        Serializer.registerClass(LeaveGame.class);
 | 
			
		||||
        Serializer.registerClass(LobbyNotReady.class);
 | 
			
		||||
        Serializer.registerClass(LobbyReady.class);
 | 
			
		||||
        Serializer.registerClass(NoPowerCard.class);
 | 
			
		||||
        Serializer.registerClass(RequestBriefing.class);
 | 
			
		||||
        Serializer.registerClass(RequestDie.class);
 | 
			
		||||
        Serializer.registerClass(RequestMove.class);
 | 
			
		||||
        Serializer.registerClass(RequestPlayCard.class);
 | 
			
		||||
        Serializer.registerClass(SelectCard.class);
 | 
			
		||||
        Serializer.registerClass(SelectedPieces.class);
 | 
			
		||||
        Serializer.registerClass(SelectTSK.class);
 | 
			
		||||
 | 
			
		||||
        Serializer.registerClass(ActivePlayer.class);
 | 
			
		||||
        Serializer.registerClass(AnyPiece.class);
 | 
			
		||||
        Serializer.registerClass(Briefing.class);
 | 
			
		||||
        Serializer.registerClass(CeremonyMessage.class);
 | 
			
		||||
        Serializer.registerClass(Die.class);
 | 
			
		||||
        Serializer.registerClass(DiceAgain.class);
 | 
			
		||||
        Serializer.registerClass(DiceNow.class);
 | 
			
		||||
        Serializer.registerClass(EndOfTurn.class);
 | 
			
		||||
        Serializer.registerClass(LobbyAccept.class);
 | 
			
		||||
        Serializer.registerClass(LobbyDeny.class);
 | 
			
		||||
        Serializer.registerClass(LobbyPlayerJoin.class);
 | 
			
		||||
        Serializer.registerClass(LobbyPlayerLeave.class);
 | 
			
		||||
        Serializer.registerClass(MoveMessage.class);
 | 
			
		||||
        Serializer.registerClass(NoTurn.class);
 | 
			
		||||
        Serializer.registerClass(PauseGame.class);
 | 
			
		||||
        Serializer.registerClass(PlayCard.class);
 | 
			
		||||
        Serializer.registerClass(PossibleCard.class);
 | 
			
		||||
        Serializer.registerClass(PossiblePiece.class);
 | 
			
		||||
        Serializer.registerClass(RankingResponse.class);
 | 
			
		||||
        Serializer.registerClass(RankingRollAgain.class);
 | 
			
		||||
        Serializer.registerClass(ReconnectBriefing.class);
 | 
			
		||||
        Serializer.registerClass(ResumeGame.class);
 | 
			
		||||
        Serializer.registerClass(ServerStartGame.class);
 | 
			
		||||
        Serializer.registerClass(StartPiece.class);
 | 
			
		||||
        Serializer.registerClass(UpdateReady.class);
 | 
			
		||||
        Serializer.registerClass(UpdateTSK.class);
 | 
			
		||||
        Serializer.registerClass(WaitPiece.class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void registerListeners() {
 | 
			
		||||
        myServer.addMessageListener(this, AnimationEnd.class);
 | 
			
		||||
        myServer.addMessageListener(this, ClientStartGame.class);
 | 
			
		||||
        myServer.addMessageListener(this, DeselectTSK.class);
 | 
			
		||||
        myServer.addMessageListener(this, ForceContinueGame.class);
 | 
			
		||||
        myServer.addMessageListener(this, StartGame.class);
 | 
			
		||||
        myServer.addMessageListener(this, JoinServer.class);
 | 
			
		||||
        myServer.addMessageListener(this, LeaveGame.class);
 | 
			
		||||
        myServer.addMessageListener(this, LobbyNotReady.class);
 | 
			
		||||
        myServer.addMessageListener(this, LobbyReady.class);
 | 
			
		||||
        myServer.addMessageListener(this, NoPowerCard.class);
 | 
			
		||||
        myServer.addMessageListener(this, RequestBriefing.class);
 | 
			
		||||
        myServer.addMessageListener(this, RequestDie.class);
 | 
			
		||||
        myServer.addMessageListener(this, RequestMove.class);
 | 
			
		||||
        myServer.addMessageListener(this, RequestPlayCard.class);
 | 
			
		||||
        myServer.addMessageListener(this, SelectCard.class);
 | 
			
		||||
        myServer.addMessageListener(this, SelectedPieces.class);
 | 
			
		||||
        myServer.addMessageListener(this, SelectTSK.class);
 | 
			
		||||
        myServer.addConnectionListener(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public 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());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void connectionRemoved(Server server, HostedConnection hostedConnection) {
 | 
			
		||||
        LOGGER.log(Level.INFO, "connection closed: {0}", hostedConnection); //NON-NLS
 | 
			
		||||
        final Player player = logic.getGame().getPlayerById(hostedConnection.getId());
 | 
			
		||||
        if (player == null)
 | 
			
		||||
            LOGGER.log(Level.INFO, "closed connection does not belong to an active player"); //NON-NLS
 | 
			
		||||
        else { //NON-NLS
 | 
			
		||||
            LOGGER.log(Level.INFO, "closed connection belongs to {0}", player); //NON-NLS
 | 
			
		||||
            // exit(0);
 | 
			
		||||
            this.handleDisconnect(hostedConnection.getId());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This method will be used to handle unintentional disconnections from players.
 | 
			
		||||
     *
 | 
			
		||||
     * @param id as the id of the disconnected player.
 | 
			
		||||
     */
 | 
			
		||||
    public void handleDisconnect(int id) {
 | 
			
		||||
        this.logic.received(new Disconnected(), id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void exit(int exitValue) { //NON-NLS
 | 
			
		||||
        LOGGER.log(Level.INFO, "close request"); //NON-NLS
 | 
			
		||||
        if (myServer != null)
 | 
			
		||||
            for (HostedConnection client : myServer.getConnections()) //NON-NLS
 | 
			
		||||
                if (client != null) client.close("Game over"); //NON-NLS
 | 
			
		||||
        System.exit(exitValue);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Send the specified message to the specified connection.
 | 
			
		||||
     *
 | 
			
		||||
     * @param id      the connection id
 | 
			
		||||
     * @param message the message
 | 
			
		||||
     */
 | 
			
		||||
    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
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        final HostedConnection connection = myServer.getConnection(id);
 | 
			
		||||
        if (connection != null)
 | 
			
		||||
            connection.send(message);
 | 
			
		||||
        else
 | 
			
		||||
            LOGGER.log(Level.ERROR, "there is no connection with id={0}", id); //NON-NLS
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This method will be used to send the given message parameter to all connected players which are saved inside the
 | 
			
		||||
     * players attribute of Game class.
 | 
			
		||||
     *
 | 
			
		||||
     * @param message as the message which will be sent to all players as a ServerMessage.
 | 
			
		||||
     */
 | 
			
		||||
    public void broadcast(ServerMessage message) {
 | 
			
		||||
        for (Map.Entry<Integer, Player> entry: this.logic.getGame().getPlayers().entrySet()) {
 | 
			
		||||
            this.send(entry.getKey(), message);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //TODO:
 | 
			
		||||
    @Override
 | 
			
		||||
    public void messageReceived(HostedConnection source, Message m) {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
package pp.mdga.client.server;
 | 
			
		||||
 | 
			
		||||
import pp.mdga.message.client.ClientInterpreter;
 | 
			
		||||
import pp.mdga.message.client.ClientMessage;
 | 
			
		||||
 | 
			
		||||
public record ReceivedMessage(ClientMessage msg, int from) {
 | 
			
		||||
    void process(ClientInterpreter interpreter) {
 | 
			
		||||
        msg.accept(interpreter, from);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,98 +0,0 @@
 | 
			
		||||
package pp.mdga.client.view;
 | 
			
		||||
 | 
			
		||||
import com.jme3.scene.Geometry;
 | 
			
		||||
import pp.mdga.client.dialog.SingleButtonLeftDialog;
 | 
			
		||||
import pp.mdga.client.dialog.SingleButtonRightDialog;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
import pp.mdga.client.MdgaState;
 | 
			
		||||
 | 
			
		||||
public class CeremonyView extends MdgaView {
 | 
			
		||||
 | 
			
		||||
    private enum SubState {
 | 
			
		||||
        AWARD_CEREMONY,
 | 
			
		||||
        STATISTICS,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private SubState state;
 | 
			
		||||
 | 
			
		||||
    private SingleButtonRightDialog continueButton;
 | 
			
		||||
    private SingleButtonLeftDialog backButton;
 | 
			
		||||
 | 
			
		||||
    private Geometry background;
 | 
			
		||||
 | 
			
		||||
    public CeremonyView(MdgaApp app) {
 | 
			
		||||
        super(app);
 | 
			
		||||
 | 
			
		||||
        continueButton = new SingleButtonRightDialog(app, node, "Weiter", () -> forward());
 | 
			
		||||
        backButton = new SingleButtonLeftDialog(app, node, "Zurück", () -> back());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onEnter() {
 | 
			
		||||
        enterSub(SubState.AWARD_CEREMONY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onLeave() {
 | 
			
		||||
        continueButton.hide();
 | 
			
		||||
        backButton.hide();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void awardCeremony() {
 | 
			
		||||
        background = createBackground("b1.png");
 | 
			
		||||
        node.attachChild(background);
 | 
			
		||||
 | 
			
		||||
        continueButton.show();
 | 
			
		||||
        backButton.hide();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void statistics() {
 | 
			
		||||
        background = createBackground("b2.png");
 | 
			
		||||
        node.attachChild(background);
 | 
			
		||||
 | 
			
		||||
        continueButton.show();
 | 
			
		||||
        backButton.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void enterSub(SubState state) {
 | 
			
		||||
        this.state = state;
 | 
			
		||||
 | 
			
		||||
        if(null != background) {
 | 
			
		||||
            node.detachChild(background);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        backButton.hide();
 | 
			
		||||
        continueButton.hide();
 | 
			
		||||
 | 
			
		||||
        switch (state) {
 | 
			
		||||
            case AWARD_CEREMONY:
 | 
			
		||||
                awardCeremony();
 | 
			
		||||
                break;
 | 
			
		||||
            case STATISTICS:
 | 
			
		||||
                statistics();
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void forward() {
 | 
			
		||||
        switch (state) {
 | 
			
		||||
            case AWARD_CEREMONY:
 | 
			
		||||
                enterSub(SubState.STATISTICS);
 | 
			
		||||
                break;
 | 
			
		||||
            case STATISTICS:
 | 
			
		||||
                app.getModelSyncronizer().enter(MdgaState.MAIN);
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void back() {
 | 
			
		||||
        switch (state) {
 | 
			
		||||
            case AWARD_CEREMONY:
 | 
			
		||||
                //nothing
 | 
			
		||||
                break;
 | 
			
		||||
            case STATISTICS:
 | 
			
		||||
                enterSub(SubState.AWARD_CEREMONY);
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,79 +0,0 @@
 | 
			
		||||
package pp.mdga.client.view;
 | 
			
		||||
 | 
			
		||||
import com.jme3.post.FilterPostProcessor;
 | 
			
		||||
import pp.mdga.client.board.BoardHandler;
 | 
			
		||||
import pp.mdga.client.board.CameraHandler;
 | 
			
		||||
import pp.mdga.client.dialog.SingleButtonLeftDialog;
 | 
			
		||||
import pp.mdga.client.dialog.SingleButtonRightDialog;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
import pp.mdga.client.MdgaState;
 | 
			
		||||
import pp.mdga.client.gui.GuiHandler;
 | 
			
		||||
 | 
			
		||||
public class GameView extends MdgaView {
 | 
			
		||||
    private BoardHandler boardHandler;
 | 
			
		||||
    private CameraHandler camera;
 | 
			
		||||
    private GuiHandler guiHandler;
 | 
			
		||||
 | 
			
		||||
    private SingleButtonLeftDialog leaveButton;
 | 
			
		||||
    private SingleButtonRightDialog continueButton;
 | 
			
		||||
 | 
			
		||||
    public GameView(MdgaApp app) {
 | 
			
		||||
        super(app);
 | 
			
		||||
 | 
			
		||||
        //Filter für Outline: Reihenfolge CameraHandler(dlsf) -> BoardHandler -> viewPort.addProcessor einhalten!
 | 
			
		||||
        FilterPostProcessor fpp = new FilterPostProcessor(app.getAssetManager());
 | 
			
		||||
        this.camera = new CameraHandler(app, fpp);
 | 
			
		||||
        this.boardHandler = new BoardHandler(app, fpp);
 | 
			
		||||
        app.getViewPort().addProcessor(fpp);
 | 
			
		||||
 | 
			
		||||
        this.guiHandler = new GuiHandler(app);
 | 
			
		||||
 | 
			
		||||
        leaveButton = new SingleButtonLeftDialog(app, settingsNode, "Verlassen", () -> leaveGame());
 | 
			
		||||
 | 
			
		||||
        continueButton = new SingleButtonRightDialog(app, node, "Weiter", () -> app.getModelSyncronizer().enter(MdgaState.CEREMONY));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onEnter() {
 | 
			
		||||
        camera.init();
 | 
			
		||||
        boardHandler.init();
 | 
			
		||||
        guiHandler.init();
 | 
			
		||||
        continueButton.show();
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onLeave() {
 | 
			
		||||
        continueButton.hide();
 | 
			
		||||
 | 
			
		||||
        camera.shutdown();
 | 
			
		||||
        boardHandler.shutdown();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onUpdate() {
 | 
			
		||||
        camera.update(app.getInputSyncronizer().getScroll(), app.getInputSyncronizer().getRotation());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void enterExtendedSettings() {
 | 
			
		||||
        leaveButton.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void leaveExtendedSettings() {
 | 
			
		||||
        leaveButton.hide();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void leaveGame() {
 | 
			
		||||
        leaveSettings(false);
 | 
			
		||||
 | 
			
		||||
        app.getModelSyncronizer().leave();
 | 
			
		||||
 | 
			
		||||
        app.afteGameCleanup();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public BoardHandler getBoardHandler() {
 | 
			
		||||
        return  boardHandler;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,58 +0,0 @@
 | 
			
		||||
package pp.mdga.client.view;
 | 
			
		||||
 | 
			
		||||
import com.jme3.scene.Geometry;
 | 
			
		||||
import pp.mdga.client.dialog.LobbyButtonDialog;
 | 
			
		||||
import pp.mdga.client.dialog.SingleButtonLeftDialog;
 | 
			
		||||
import pp.mdga.client.dialog.SingleButtonRightDialog;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
import pp.mdga.game.Color;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
 | 
			
		||||
public class LobbyView extends MdgaView {
 | 
			
		||||
    private Geometry background;
 | 
			
		||||
 | 
			
		||||
    private SingleButtonRightDialog readyButton;
 | 
			
		||||
    private SingleButtonLeftDialog leaveButton;
 | 
			
		||||
 | 
			
		||||
    private ArrayList<LobbyButtonDialog> lobbyButtons = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
    public LobbyView(MdgaApp app) {
 | 
			
		||||
        super(app);
 | 
			
		||||
 | 
			
		||||
        background = createBackground("lobby.png");
 | 
			
		||||
        node.attachChild(background);
 | 
			
		||||
 | 
			
		||||
        readyButton = new SingleButtonRightDialog(app, node, "Fertig", () -> app.getModelSyncronizer().setReady());
 | 
			
		||||
        leaveButton = new SingleButtonLeftDialog(app, node, "Verlassen", () -> app.getModelSyncronizer().leave());
 | 
			
		||||
 | 
			
		||||
        lobbyButtons.add(new LobbyButtonDialog(app, node, "HEER", 0));
 | 
			
		||||
        lobbyButtons.add(new LobbyButtonDialog(app, node, "MARINE", 1));
 | 
			
		||||
        lobbyButtons.add(new LobbyButtonDialog(app, node, "CIR", 2));
 | 
			
		||||
        lobbyButtons.add(new LobbyButtonDialog(app, node, "LUFTWAFFE", 3));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onEnter() {
 | 
			
		||||
        readyButton.show();
 | 
			
		||||
        leaveButton.show();
 | 
			
		||||
 | 
			
		||||
        for (LobbyButtonDialog b : lobbyButtons) {
 | 
			
		||||
            b.show();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onLeave() {
 | 
			
		||||
        for (LobbyButtonDialog b : lobbyButtons) {
 | 
			
		||||
            b.hide();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        readyButton.hide();
 | 
			
		||||
        leaveButton.hide();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setTaken(Color color, boolean isTaken, boolean isSelf, String name) {
 | 
			
		||||
        lobbyButtons.get(color.ordinal()).setTaken(isTaken, isSelf, name);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,79 +0,0 @@
 | 
			
		||||
package pp.mdga.client.view;
 | 
			
		||||
 | 
			
		||||
import com.jme3.scene.Geometry;
 | 
			
		||||
import pp.mdga.client.dialog.Dialog;
 | 
			
		||||
import pp.mdga.client.dialog.HostDialog;
 | 
			
		||||
import pp.mdga.client.dialog.JoinDialog;
 | 
			
		||||
import pp.mdga.client.dialog.StartDialog;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
 | 
			
		||||
public class MainView extends MdgaView {
 | 
			
		||||
    private Geometry background;
 | 
			
		||||
 | 
			
		||||
    private StartDialog dialog;
 | 
			
		||||
    private Dialog subDialog;
 | 
			
		||||
 | 
			
		||||
    public MainView(MdgaApp app) {
 | 
			
		||||
        super(app);
 | 
			
		||||
 | 
			
		||||
        background = createBackground("powercards.png");
 | 
			
		||||
        node.attachChild(background);
 | 
			
		||||
 | 
			
		||||
        Vector3f size = new Vector3f(280, 60, 0);
 | 
			
		||||
        dialog = new StartDialog(app, node);
 | 
			
		||||
        dialog.addButton("Spiel beitreten", () -> enterJoin(), size);
 | 
			
		||||
        dialog.addButton("Spiel hosten", () -> enterHost(), size);
 | 
			
		||||
        dialog.addButton("Einstellungen", () -> enterSettings(false), size);
 | 
			
		||||
        dialog.addButton("Spiel beenden", () -> app.stop(), size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onEnter() {
 | 
			
		||||
        dialog.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onLeave() {
 | 
			
		||||
        dialog.hide();
 | 
			
		||||
 | 
			
		||||
        if(subDialog != null) {
 | 
			
		||||
            subDialog.hide();
 | 
			
		||||
            subDialog = null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void enterJoin() {
 | 
			
		||||
        app.getModelSyncronizer().setName(dialog.getName());
 | 
			
		||||
 | 
			
		||||
        subDialog = new JoinDialog(app, node, () -> leaveJoin());
 | 
			
		||||
 | 
			
		||||
        dialog.hide();
 | 
			
		||||
        subDialog.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void leaveJoin() {
 | 
			
		||||
        subDialog.hide();
 | 
			
		||||
        dialog.show();
 | 
			
		||||
 | 
			
		||||
        subDialog = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void enterHost() {
 | 
			
		||||
        app.getModelSyncronizer().setName(dialog.getName());
 | 
			
		||||
 | 
			
		||||
        subDialog = new HostDialog(app, node, () -> leaveHost());
 | 
			
		||||
 | 
			
		||||
        dialog.hide();
 | 
			
		||||
        subDialog.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void leaveHost() {
 | 
			
		||||
        subDialog.hide();
 | 
			
		||||
        dialog.show();
 | 
			
		||||
 | 
			
		||||
        subDialog = null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,206 +0,0 @@
 | 
			
		||||
package pp.mdga.client.view;
 | 
			
		||||
 | 
			
		||||
import com.jme3.asset.TextureKey;
 | 
			
		||||
import com.jme3.material.Material;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.scene.Geometry;
 | 
			
		||||
import com.jme3.scene.Node;
 | 
			
		||||
import com.jme3.scene.shape.Quad;
 | 
			
		||||
import com.jme3.texture.Texture;
 | 
			
		||||
import pp.mdga.client.dialog.GetPercentRunnable;
 | 
			
		||||
import pp.mdga.client.dialog.PercentRunnable;
 | 
			
		||||
import pp.mdga.client.dialog.SettingsButtonDialog;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
import pp.mdga.client.dialog.SettingsDialog;
 | 
			
		||||
 | 
			
		||||
public abstract class MdgaView {
 | 
			
		||||
    protected MdgaApp app;
 | 
			
		||||
    protected Node node;
 | 
			
		||||
 | 
			
		||||
    protected int depth = 0;
 | 
			
		||||
    private boolean isVideo = false;
 | 
			
		||||
    private boolean isAudio = false;
 | 
			
		||||
 | 
			
		||||
    protected Node settingsNode;
 | 
			
		||||
    protected Node audioSettingsNode;
 | 
			
		||||
    protected Node videoSettingsNode;
 | 
			
		||||
 | 
			
		||||
    private SettingsButtonDialog settingsButton;
 | 
			
		||||
 | 
			
		||||
    private Geometry settingsBackground;
 | 
			
		||||
    private Geometry audioBackground;
 | 
			
		||||
    private Geometry videoBackground;
 | 
			
		||||
 | 
			
		||||
    private SettingsDialog settings;
 | 
			
		||||
    private SettingsDialog audio;
 | 
			
		||||
    private SettingsDialog video;
 | 
			
		||||
 | 
			
		||||
    public MdgaView(MdgaApp app) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
        this.node = new Node();
 | 
			
		||||
 | 
			
		||||
        this.settingsNode = new Node();
 | 
			
		||||
        this.audioSettingsNode = new Node();
 | 
			
		||||
        this.videoSettingsNode = new Node();
 | 
			
		||||
 | 
			
		||||
        this.settingsButton = new SettingsButtonDialog(app, node, "", () -> enterSettings(false));
 | 
			
		||||
 | 
			
		||||
        this.settingsBackground = createBackground("background/zahnräder.png");
 | 
			
		||||
        settingsNode.attachChild(settingsBackground);
 | 
			
		||||
 | 
			
		||||
        this.audioBackground = createBackground("background/lautsprecher.png");
 | 
			
		||||
        audioSettingsNode.attachChild(audioBackground);
 | 
			
		||||
 | 
			
		||||
        this.videoBackground = createBackground("background/monitors.png");
 | 
			
		||||
        videoSettingsNode.attachChild(videoBackground);
 | 
			
		||||
 | 
			
		||||
        Vector3f size = new Vector3f(280, 60, 0);
 | 
			
		||||
 | 
			
		||||
        this.settings = new SettingsDialog(app, settingsNode, "zahnrad.png");
 | 
			
		||||
        this.settings.addButton("Video", () -> enterVideo(), size);
 | 
			
		||||
        this.settings.addButton("Audio", () -> enterAudio(), size);
 | 
			
		||||
        this.settings.addButton("Zurück", () -> leaveSettings(false), size);
 | 
			
		||||
 | 
			
		||||
        this.audio = new SettingsDialog(app, audioSettingsNode, "audio_icon.png");
 | 
			
		||||
        this.audio.addSlider("Lautstärke", new PercentRunnable(app.getAcousticHandler()::setMainVolume), new GetPercentRunnable(app.getAcousticHandler()::getMainVolume), size, (int) app.getAcousticHandler().getMainVolume() * 100);
 | 
			
		||||
        this.audio.addSlider("Musik", new PercentRunnable(app.getAcousticHandler()::setMusicVolume), new GetPercentRunnable(app.getAcousticHandler()::getMusicVolume), size, (int) app.getAcousticHandler().getMusicVolume() * 100);
 | 
			
		||||
        this.audio.addSlider("Sound", new PercentRunnable(app.getAcousticHandler()::setSoundVolume), new GetPercentRunnable(app.getAcousticHandler()::getSoundVolume), size, (int) app.getAcousticHandler().getSoundVolume() * 100);
 | 
			
		||||
        this.audio.addButton("Zurück", () -> leaveAudio(), size);
 | 
			
		||||
 | 
			
		||||
        this.video = new SettingsDialog(app, videoSettingsNode, "monitor.png");
 | 
			
		||||
        this.video.addButton("A", () -> System.out.println("A"), size);
 | 
			
		||||
        this.video.addButton("B", () -> System.out.println("B"), size);
 | 
			
		||||
        this.video.addButton("Zurück", () -> leaveVideo(), size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void enter() {
 | 
			
		||||
        app.getGuiNode().attachChild(node);
 | 
			
		||||
 | 
			
		||||
        audio.initVolume();
 | 
			
		||||
 | 
			
		||||
        settingsButton.show();
 | 
			
		||||
 | 
			
		||||
        onEnter();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void leave() {
 | 
			
		||||
        onLeave();
 | 
			
		||||
 | 
			
		||||
        settingsButton.hide();
 | 
			
		||||
 | 
			
		||||
        app.getGuiNode().detachChild(node);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void update() {
 | 
			
		||||
        audio.update();
 | 
			
		||||
        onUpdate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected abstract void onEnter();
 | 
			
		||||
    protected abstract void onLeave();
 | 
			
		||||
    protected void onUpdate() {}
 | 
			
		||||
 | 
			
		||||
    protected Geometry createBackground(String texturePath) {
 | 
			
		||||
        TextureKey key = new TextureKey(texturePath, true);
 | 
			
		||||
        Texture backgroundTexture = app.getAssetManager().loadTexture(key);
 | 
			
		||||
        Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight());
 | 
			
		||||
        Geometry background = new Geometry("Background", quad);
 | 
			
		||||
        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
 | 
			
		||||
        mat.setTexture("ColorMap", backgroundTexture);
 | 
			
		||||
        background.setMaterial(mat);
 | 
			
		||||
        background.setLocalTranslation(0, 0, -1);
 | 
			
		||||
        return background;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void enterExtendedSettings() {}
 | 
			
		||||
    protected void leaveExtendedSettings() {}
 | 
			
		||||
 | 
			
		||||
    protected void enterSettings(boolean soft) {
 | 
			
		||||
        leave();
 | 
			
		||||
 | 
			
		||||
        app.getGuiNode().attachChild(settingsNode);
 | 
			
		||||
 | 
			
		||||
        if(!soft) {
 | 
			
		||||
            depth++;
 | 
			
		||||
 | 
			
		||||
            enterExtendedSettings();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        settings.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void leaveSettings(boolean soft) {
 | 
			
		||||
        settings.hide();
 | 
			
		||||
 | 
			
		||||
        app.getGuiNode().detachChild(settingsNode);
 | 
			
		||||
 | 
			
		||||
        if(!soft) {
 | 
			
		||||
            leaveExtendedSettings();
 | 
			
		||||
            enter();
 | 
			
		||||
            depth--;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void enterAudio() {
 | 
			
		||||
        leaveSettings(true);
 | 
			
		||||
 | 
			
		||||
        depth++;
 | 
			
		||||
        isAudio = true;
 | 
			
		||||
 | 
			
		||||
        app.getGuiNode().attachChild(audioSettingsNode);
 | 
			
		||||
 | 
			
		||||
        audio.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void leaveAudio() {
 | 
			
		||||
        audio.hide();
 | 
			
		||||
 | 
			
		||||
        app.getGuiNode().detachChild(audioSettingsNode);
 | 
			
		||||
 | 
			
		||||
        isAudio = false;
 | 
			
		||||
        depth--;
 | 
			
		||||
 | 
			
		||||
        enterSettings(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void enterVideo() {
 | 
			
		||||
        leaveSettings(true);
 | 
			
		||||
 | 
			
		||||
        app.getGuiNode().attachChild(videoSettingsNode);
 | 
			
		||||
 | 
			
		||||
        depth++;
 | 
			
		||||
 | 
			
		||||
        isVideo = true;
 | 
			
		||||
 | 
			
		||||
        video.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void leaveVideo() {
 | 
			
		||||
        video.hide();
 | 
			
		||||
 | 
			
		||||
        app.getGuiNode().detachChild(videoSettingsNode);
 | 
			
		||||
 | 
			
		||||
        depth--;
 | 
			
		||||
        isVideo = false;
 | 
			
		||||
 | 
			
		||||
        enterSettings(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void pressEscape() {
 | 
			
		||||
        if(depth == 0) {
 | 
			
		||||
            enterSettings(false);
 | 
			
		||||
        } else if(depth == 1) {
 | 
			
		||||
            leaveSettings(false);
 | 
			
		||||
        }
 | 
			
		||||
        else if (depth == 2){
 | 
			
		||||
            if(isVideo) {
 | 
			
		||||
                leaveVideo();
 | 
			
		||||
            }
 | 
			
		||||
            if(isAudio) {
 | 
			
		||||
                leaveAudio();
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            throw new RuntimeException();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,29 +0,0 @@
 | 
			
		||||
uniform sampler2D m_Texture;
 | 
			
		||||
uniform sampler2D m_OutlineDepthTexture;
 | 
			
		||||
uniform sampler2D m_DepthTexture;
 | 
			
		||||
varying vec2 texCoord;
 | 
			
		||||
 | 
			
		||||
uniform vec2 m_Resolution;
 | 
			
		||||
uniform vec4 m_OutlineColor;
 | 
			
		||||
uniform float m_OutlineWidth;
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
	vec4 depth = texture2D(m_OutlineDepthTexture, texCoord);
 | 
			
		||||
	vec4 depth1 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(m_OutlineWidth,m_OutlineWidth))/m_Resolution);
 | 
			
		||||
	vec4 depth2 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(m_OutlineWidth,-m_OutlineWidth))/m_Resolution);
 | 
			
		||||
	vec4 depth3 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-m_OutlineWidth,m_OutlineWidth))/m_Resolution);
 | 
			
		||||
	vec4 depth4 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-m_OutlineWidth,-m_OutlineWidth))/m_Resolution);
 | 
			
		||||
	vec4 depth5 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(0.,m_OutlineWidth))/m_Resolution);
 | 
			
		||||
	vec4 depth6 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(0.,-m_OutlineWidth))/m_Resolution);
 | 
			
		||||
	vec4 depth7 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(m_OutlineWidth,0.))/m_Resolution);
 | 
			
		||||
	vec4 depth8 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-m_OutlineWidth,0.))/m_Resolution);
 | 
			
		||||
	vec4 color = texture2D(m_Texture, texCoord);
 | 
			
		||||
	//如果是背景
 | 
			
		||||
	if(depth==vec4(0.) && (depth1 != depth || depth2 != depth || depth3 != depth || depth4 != depth||depth5 != depth || depth6 != depth || depth7 != depth || depth8 != depth)){
 | 
			
		||||
		gl_FragColor = m_OutlineColor;
 | 
			
		||||
	}else{
 | 
			
		||||
		gl_FragColor = color;
 | 
			
		||||
	}
 | 
			
		||||
	//debug
 | 
			
		||||
	//gl_FragColor = vec4(0.,(1.-ratio),0.,1.);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,21 +0,0 @@
 | 
			
		||||
MaterialDef Cartoon Edge {
 | 
			
		||||
 | 
			
		||||
    MaterialParameters {
 | 
			
		||||
	    Int NumSamples
 | 
			
		||||
        Int NumSamplesDepth
 | 
			
		||||
        Texture2D Texture
 | 
			
		||||
        Texture2D OutlineDepthTexture
 | 
			
		||||
        Texture2D DepthTexture
 | 
			
		||||
    	Vector2 Resolution
 | 
			
		||||
    	Color OutlineColor
 | 
			
		||||
    	Float OutlineWidth
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Technique {
 | 
			
		||||
        VertexShader GLSL150:   MatDefs/SelectObjectOutliner/Post15.vert
 | 
			
		||||
        FragmentShader GLSL150: MatDefs/SelectObjectOutliner/Outline.frag
 | 
			
		||||
 | 
			
		||||
        WorldParameters {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
varying vec2 texCoord;
 | 
			
		||||
 | 
			
		||||
uniform sampler2D m_Texture;
 | 
			
		||||
uniform sampler2D m_NormalsTexture;
 | 
			
		||||
uniform sampler2D m_DepthTexture;
 | 
			
		||||
 | 
			
		||||
void main(){
 | 
			
		||||
    vec4 color = texture2D(m_Texture, texCoord);
 | 
			
		||||
	gl_FragColor=color;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,19 +0,0 @@
 | 
			
		||||
MaterialDef Cartoon Edge {
 | 
			
		||||
 | 
			
		||||
    MaterialParameters {
 | 
			
		||||
        Int NumSamples
 | 
			
		||||
        Int NumSamplesDepth
 | 
			
		||||
        Texture2D Texture
 | 
			
		||||
        Texture2D NormalsTexture
 | 
			
		||||
        Texture2D DepthTexture
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
     Technique {
 | 
			
		||||
        VertexShader GLSL150:   MatDefs/SelectObjectOutliner/Post15.vert
 | 
			
		||||
        FragmentShader GLSL150: MatDefs/SelectObjectOutliner/OutlinePre.frag
 | 
			
		||||
 | 
			
		||||
        WorldParameters {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
in vec4 inPosition;
 | 
			
		||||
in vec2 inTexCoord;
 | 
			
		||||
 | 
			
		||||
out vec2 texCoord;
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
    vec2 pos = inPosition.xy * 2.0 - 1.0;
 | 
			
		||||
    gl_Position = vec4(pos, 0.0, 1.0);
 | 
			
		||||
    texCoord = inTexCoord;
 | 
			
		||||
}
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 44 KiB  | 
| 
		 Before Width: | Height: | Size: 21 KiB  | 
| 
		 Before Width: | Height: | Size: 216 KiB  | 
| 
		 Before Width: | Height: | Size: 274 KiB  | 
| 
		 Before Width: | Height: | Size: 3.9 MiB  | 
| 
		 Before Width: | Height: | Size: 3.3 MiB  | 
| 
		 Before Width: | Height: | Size: 3.3 MiB  | 
@@ -1,66 +0,0 @@
 | 
			
		||||
world 0,0
 | 
			
		||||
 | 
			
		||||
# jet 0,0
 | 
			
		||||
 | 
			
		||||
#Nodes für Map
 | 
			
		||||
node_start 8,0
 | 
			
		||||
node 8,1
 | 
			
		||||
node 8,2
 | 
			
		||||
node 7,3
 | 
			
		||||
node_bonus 6,4
 | 
			
		||||
node 5,5
 | 
			
		||||
node 4,6
 | 
			
		||||
node 3,7
 | 
			
		||||
node 2,8
 | 
			
		||||
node 1,8
 | 
			
		||||
node_start 0,8
 | 
			
		||||
node -1,8
 | 
			
		||||
node -2,8
 | 
			
		||||
node -3,7
 | 
			
		||||
node_bonus -4,6
 | 
			
		||||
node -5,5
 | 
			
		||||
node -6,4
 | 
			
		||||
node -7,3
 | 
			
		||||
node -8,2
 | 
			
		||||
node -8,1
 | 
			
		||||
node_start -8,0
 | 
			
		||||
node -8,-1
 | 
			
		||||
node -8,-2
 | 
			
		||||
node -7,-3
 | 
			
		||||
node_bonus -6,-4
 | 
			
		||||
node -5,-5
 | 
			
		||||
node -4,-6
 | 
			
		||||
node -3,-7
 | 
			
		||||
node -2,-8
 | 
			
		||||
node -1,-8
 | 
			
		||||
node_start 0,-8
 | 
			
		||||
node 1,-8
 | 
			
		||||
node 2,-8
 | 
			
		||||
node 3,-7
 | 
			
		||||
node_bonus 4,-6
 | 
			
		||||
node 5,-5
 | 
			
		||||
node 6,-4
 | 
			
		||||
node 7,-3
 | 
			
		||||
node 8,-2
 | 
			
		||||
node 8,-1
 | 
			
		||||
 | 
			
		||||
#Node Home
 | 
			
		||||
node_home_blue 7,0
 | 
			
		||||
node_home_blue 6,0
 | 
			
		||||
node_home_blue 5,0
 | 
			
		||||
node_home_blue 4,0
 | 
			
		||||
 | 
			
		||||
node_home_black 0,4
 | 
			
		||||
node_home_black 0,5
 | 
			
		||||
node_home_black 0,6
 | 
			
		||||
node_home_black 0,7
 | 
			
		||||
 | 
			
		||||
node_home_yellow 0,-4
 | 
			
		||||
node_home_yellow 0,-5
 | 
			
		||||
node_home_yellow 0,-6
 | 
			
		||||
node_home_yellow 0,-7
 | 
			
		||||
 | 
			
		||||
node_home_green -4,0
 | 
			
		||||
node_home_green -5,0
 | 
			
		||||
node_home_green -6,0
 | 
			
		||||
node_home_green -7,0
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 150 KiB  | 
@@ -1,129 +0,0 @@
 | 
			
		||||
world 0,0 90
 | 
			
		||||
 | 
			
		||||
#tree_small 1,1 0
 | 
			
		||||
#tree_big 0,0 0
 | 
			
		||||
 | 
			
		||||
#Marine Pos
 | 
			
		||||
marine 4,-5 -90
 | 
			
		||||
marine 4,-4 -90
 | 
			
		||||
marine 5,-4 -90
 | 
			
		||||
marine 5,-5 -90
 | 
			
		||||
 | 
			
		||||
#Blue (Marine) wait Node
 | 
			
		||||
node_wait_blue 4,-5 0
 | 
			
		||||
node_wait_blue 4,-4 0
 | 
			
		||||
node_wait_blue 5,-4 0
 | 
			
		||||
node_wait_blue 5,-5 0
 | 
			
		||||
 | 
			
		||||
#Lw Pos
 | 
			
		||||
lw -5,4 90
 | 
			
		||||
lw -4,4 90
 | 
			
		||||
lw -4,5 90
 | 
			
		||||
lw -5,5 90
 | 
			
		||||
 | 
			
		||||
#Black (Lw) wait Node
 | 
			
		||||
node_wait_black -5,4 0
 | 
			
		||||
node_wait_black -4,4 0
 | 
			
		||||
node_wait_black -4,5 0
 | 
			
		||||
node_wait_black -5,5 0
 | 
			
		||||
 | 
			
		||||
#Heer Pos
 | 
			
		||||
heer -4,-5 180
 | 
			
		||||
heer -4,-4 180
 | 
			
		||||
heer -5,-4 180
 | 
			
		||||
heer -5,-5 180
 | 
			
		||||
 | 
			
		||||
#Green (Heer) Wait Node
 | 
			
		||||
node_wait_green -4,-5 0
 | 
			
		||||
node_wait_green -4,-4 0
 | 
			
		||||
node_wait_green -5,-4 0
 | 
			
		||||
node_wait_green -5,-5 0
 | 
			
		||||
 | 
			
		||||
#CIR Pos
 | 
			
		||||
cir 4,5 0
 | 
			
		||||
cir 4,4 0
 | 
			
		||||
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
 | 
			
		||||
small_tent -9,7 45
 | 
			
		||||
small_tent -10,5 60
 | 
			
		||||
radar 0,10 -20
 | 
			
		||||
small_tent 6,8 190
 | 
			
		||||
small_tent 8,7 160
 | 
			
		||||
tank -1,-10 135
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#Yellow (CIR) wait Node
 | 
			
		||||
node_wait_yellow 4,5 0
 | 
			
		||||
node_wait_yellow 4,4 0
 | 
			
		||||
node_wait_yellow 5,4 0
 | 
			
		||||
node_wait_yellow 5,5 0
 | 
			
		||||
 | 
			
		||||
#Nodes für Map
 | 
			
		||||
node_start -5,1 0
 | 
			
		||||
node -4,1 0
 | 
			
		||||
node -3,1 0
 | 
			
		||||
node -2,1 0
 | 
			
		||||
node_bonus -1,1 0
 | 
			
		||||
node -1,2 0
 | 
			
		||||
node -1,3 0
 | 
			
		||||
node -1,4 0
 | 
			
		||||
node -1,5 0
 | 
			
		||||
node 0,5 0
 | 
			
		||||
node_start 1,5 0
 | 
			
		||||
node 1,4 0
 | 
			
		||||
node 1,3 0
 | 
			
		||||
node 1,2 0
 | 
			
		||||
node_bonus 1,1 0
 | 
			
		||||
node 2,1 0
 | 
			
		||||
node 3,1 0
 | 
			
		||||
node 4,1 0
 | 
			
		||||
node 5,1 0
 | 
			
		||||
node 5,0 0
 | 
			
		||||
node_start 5,-1 0
 | 
			
		||||
node 4,-1 0
 | 
			
		||||
node 3,-1 0
 | 
			
		||||
node 2,-1 0
 | 
			
		||||
node_bonus 1,-1 0
 | 
			
		||||
node 1,-2 0
 | 
			
		||||
node 1,-3 0
 | 
			
		||||
node 1,-4 0
 | 
			
		||||
node 1,-5 0
 | 
			
		||||
node 0,-5 0
 | 
			
		||||
node_start -1,-5 0
 | 
			
		||||
node -1,-4 0
 | 
			
		||||
node -1,-3 0
 | 
			
		||||
node -1,-2 0
 | 
			
		||||
node_bonus -1,-1 0
 | 
			
		||||
node -2,-1 0
 | 
			
		||||
node -3,-1 0
 | 
			
		||||
node -4,-1 0
 | 
			
		||||
node -5,-1 0
 | 
			
		||||
node -5,0 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#Node Home
 | 
			
		||||
node_home_black -4,0 0
 | 
			
		||||
node_home_black -3,0 0
 | 
			
		||||
node_home_black -2,0 0
 | 
			
		||||
node_home_black -1,0 0
 | 
			
		||||
 | 
			
		||||
node_home_green 0,-4 0
 | 
			
		||||
node_home_green 0,-3 0
 | 
			
		||||
node_home_green 0,-2 0
 | 
			
		||||
node_home_green 0,-1 0
 | 
			
		||||
 | 
			
		||||
node_home_yellow 0,4 0
 | 
			
		||||
node_home_yellow 0,3 0
 | 
			
		||||
node_home_yellow 0,2 0
 | 
			
		||||
node_home_yellow 0,1 0
 | 
			
		||||
 | 
			
		||||
node_home_blue 4,0 0
 | 
			
		||||
node_home_blue 3,0 0
 | 
			
		||||
node_home_blue 2,0 0
 | 
			
		||||
node_home_blue 1,0 0
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 24 KiB  | 
| 
		 Before Width: | Height: | Size: 11 KiB  | 
| 
		 Before Width: | Height: | Size: 12 KiB  | 
@@ -1,22 +0,0 @@
 | 
			
		||||
# Credit
 | 
			
		||||
 | 
			
		||||
## Free SynthWave Music Pack
 | 
			
		||||
 | 
			
		||||
See "Free SynthWave Music Pack License.pdf" for LICENCE information.
 | 
			
		||||
 | 
			
		||||
 - 80s,Dico,Live.wav
 | 
			
		||||
 - LaserParty.wav
 | 
			
		||||
 - NeonRoadTrip.wav
 | 
			
		||||
 - NoPressureTrance.wav
 | 
			
		||||
 - RetroNoir.wav
 | 
			
		||||
 - SpaceInvaders.wav
 | 
			
		||||
 - TheSynthRave.SynthWave
 | 
			
		||||
 | 
			
		||||
## Short Loopable Background Music
 | 
			
		||||
 | 
			
		||||
 https://joshuuu.itch.io/short-loopable-background-music
 | 
			
		||||
 | 
			
		||||
 Free for personal or commercial use as long as you don't redistribute as your own. Donations also welcome! Please add jhaeka to your credits. :)
 | 
			
		||||
 | 
			
		||||
 - DeadPlanet.wav
 | 
			
		||||
 - Spaceship.wav
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 454 KiB  | 
| 
		 Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB  | 
| 
		 Before Width: | Height: | Size: 13 KiB  | 
| 
		 Before Width: | Height: | Size: 1.4 MiB  | 
@@ -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
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 1.2 MiB  | 
| 
		 Before Width: | Height: | Size: 93 KiB  | 
| 
		 Before Width: | Height: | Size: 59 KiB  | 
| 
		 Before Width: | Height: | Size: 10 MiB  | 
| 
		 Before Width: | Height: | Size: 48 KiB  | 
| 
		 Before Width: | Height: | Size: 50 KiB  | 
@@ -0,0 +1,4 @@
 | 
			
		||||
package pp.mdga.client;
 | 
			
		||||
 | 
			
		||||
public class AudioSettings extends Settings {
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,4 @@
 | 
			
		||||
package pp.mdga.client;
 | 
			
		||||
 | 
			
		||||
public class Ceremony extends ClientState {
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,4 @@
 | 
			
		||||
package pp.mdga.client;
 | 
			
		||||
 | 
			
		||||
public class ChoosePiece extends Turn {
 | 
			
		||||
}
 | 
			
		||||