Compare commits
	
		
			23 Commits
		
	
	
		
			3070b9864c
			...
			gamelib
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| fed9f8d077 | |||
| cf035dedac | |||
| a28741ac3d | |||
| 12f4d7592b | |||
| d2942341e2 | |||
| 673ba9f89d | |||
| a9acbda343 | |||
| 6e249280c1 | |||
| ebd7d2a12b | |||
| 9f5303ba74 | |||
| 48409b1e4a | |||
| fbe38c978a | |||
| 6ab818b155 | |||
| 77be059d45 | |||
| ddd0f2f953 | |||
| dd8e9813fa | |||
| e68298781b | |||
| d69376c110 | |||
| 315c7d2891 | |||
| 57d384e98c | |||
| df8acbeefa | |||
| adb0a49711 | |||
| cff18b088e | 
							
								
								
									
										79
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						
									
										79
									
								
								build.gradle
									
									
									
									
									
								
							@ -2,6 +2,7 @@ plugins {
 | 
				
			|||||||
    id 'java'
 | 
					    id 'java'
 | 
				
			||||||
    id 'idea'
 | 
					    id 'idea'
 | 
				
			||||||
    id "org.sonarqube" version "3.2.0"
 | 
					    id "org.sonarqube" version "3.2.0"
 | 
				
			||||||
 | 
					    id 'jacoco'
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
repositories {
 | 
					repositories {
 | 
				
			||||||
@ -17,10 +18,76 @@ test {
 | 
				
			|||||||
    maxParallelForks = 1
 | 
					    maxParallelForks = 1
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Fancy live test output (from https://stackoverflow.com/questions/3963708/gradle-how-to-display-test-results-in-the-console-in-real-time)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.gradle.api.tasks.testing.logging.TestExceptionFormat
 | 
				
			||||||
 | 
					import org.gradle.api.tasks.testing.logging.TestLogEvent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tasks.withType(Test) {
 | 
				
			||||||
 | 
					    testLogging {
 | 
				
			||||||
 | 
					        // set options for log level LIFECYCLE
 | 
				
			||||||
 | 
					        events TestLogEvent.FAILED,
 | 
				
			||||||
 | 
					               TestLogEvent.PASSED,
 | 
				
			||||||
 | 
					               TestLogEvent.SKIPPED
 | 
				
			||||||
 | 
					               // TestLogEvent.STANDARD_OUT
 | 
				
			||||||
 | 
					        exceptionFormat TestExceptionFormat.FULL
 | 
				
			||||||
 | 
					        showExceptions true
 | 
				
			||||||
 | 
					        showCauses true
 | 
				
			||||||
 | 
					        showStackTraces true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // set options for log level DEBUG and INFO
 | 
				
			||||||
 | 
					        debug {
 | 
				
			||||||
 | 
					            events TestLogEvent.STARTED,
 | 
				
			||||||
 | 
					                   TestLogEvent.FAILED,
 | 
				
			||||||
 | 
					                   TestLogEvent.PASSED,
 | 
				
			||||||
 | 
					                   TestLogEvent.SKIPPED,
 | 
				
			||||||
 | 
					                   TestLogEvent.STANDARD_ERROR
 | 
				
			||||||
 | 
					                   TestLogEvent.STANDARD_OUT
 | 
				
			||||||
 | 
					            exceptionFormat TestExceptionFormat.FULL
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // info.events = debug.events
 | 
				
			||||||
 | 
					        // info.exceptionFormat = debug.exceptionFormat
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        afterSuite { desc, result ->
 | 
				
			||||||
 | 
					            if (!desc.parent) { // will match the outermost suite
 | 
				
			||||||
 | 
					                def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)"
 | 
				
			||||||
 | 
					                def startItem = '|  ', endItem = '  |'
 | 
				
			||||||
 | 
					                def repeatLength = startItem.length() + output.length() + endItem.length()
 | 
				
			||||||
 | 
					                println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jacocoTestReport {
 | 
				
			||||||
 | 
					    reports {
 | 
				
			||||||
 | 
					        html.enabled true
 | 
				
			||||||
 | 
					        xml.enabled true
 | 
				
			||||||
 | 
					        xml.destination file("${buildDir}/reports/jacoco.xml")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					plugins.withType(JacocoPlugin) {
 | 
				
			||||||
 | 
					    tasks["test"].finalizedBy 'jacocoTestReport'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sonarqube {
 | 
				
			||||||
 | 
					    properties {
 | 
				
			||||||
 | 
					        property "sonar.java.coveragePlugin", "jacoco"
 | 
				
			||||||
 | 
					        property "sonar.host.url", "https://sonarqube.yandrik.dev"
 | 
				
			||||||
 | 
					        property "sonar.coverage.jacoco.xmlReportPath", "${buildDir}/reports/jacoco.xml"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dependencies {
 | 
					dependencies {
 | 
				
			||||||
    implementation "com.fasterxml.jackson.core:jackson-core:2.12.3"
 | 
					    implementation 'com.fasterxml.jackson.core:jackson-core:2.12.4'
 | 
				
			||||||
    implementation "com.fasterxml.jackson.core:jackson-annotations:2.12.3"
 | 
					    implementation 'com.fasterxml.jackson.core:jackson-annotations:2.12.4'
 | 
				
			||||||
    implementation "com.fasterxml.jackson.core:jackson-databind:2.12.3"
 | 
					    implementation 'com.fasterxml.jackson.core:jackson-databind:2.12.4'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    implementation "org.hibernate.validator:hibernate-validator:7.0.1.Final"
 | 
					    implementation "org.hibernate.validator:hibernate-validator:7.0.1.Final"
 | 
				
			||||||
    implementation "org.glassfish:jakarta.el:4.0.1"
 | 
					    implementation "org.glassfish:jakarta.el:4.0.1"
 | 
				
			||||||
@ -28,10 +95,10 @@ dependencies {
 | 
				
			|||||||
    implementation "org.tinylog:tinylog-api:2.4.0-M1"
 | 
					    implementation "org.tinylog:tinylog-api:2.4.0-M1"
 | 
				
			||||||
    implementation "org.tinylog:tinylog-impl:2.4.0-M1"
 | 
					    implementation "org.tinylog:tinylog-impl:2.4.0-M1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    testImplementation "net.jqwik:jqwik:1.5.1"
 | 
					    testImplementation 'net.jqwik:jqwik:1.5.3'
 | 
				
			||||||
    testImplementation "org.junit.jupiter:junit-jupiter-api:5.8.0-M1"
 | 
					    testImplementation "org.junit.jupiter:junit-jupiter-api:5.8.0-M1"
 | 
				
			||||||
    testImplementation "org.mockito:mockito-core:3.+"
 | 
					    testImplementation 'org.mockito:mockito-core:3.11.2'
 | 
				
			||||||
    testImplementation "org.assertj:assertj-core:3.19.0"
 | 
					    testImplementation 'org.assertj:assertj-core:3.20.2'
 | 
				
			||||||
    testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.8.0-M1"
 | 
					    testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.8.0-M1"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -110,6 +110,7 @@ public class IntVector2 implements Serializable {
 | 
				
			|||||||
        return dot(v.x, v.y);
 | 
					        return dot(v.x, v.y);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TODO: This returns a new Vector for now, but should scale the actually modified vector
 | 
				
			||||||
    public IntVector2 scale(float x, float y) {
 | 
					    public IntVector2 scale(float x, float y) {
 | 
				
			||||||
        return new IntVector2(Math.round(this.x * x), Math.round(this.y * y));
 | 
					        return new IntVector2(Math.round(this.x * x), Math.round(this.y * y));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -58,6 +58,37 @@ public class Character extends Entity {
 | 
				
			|||||||
        this.meleeDamage = meleeDamage;
 | 
					        this.meleeDamage = meleeDamage;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new {@link Character} with an empty inventory.
 | 
				
			||||||
 | 
					     * @param id The {@link EntityID} of the character
 | 
				
			||||||
 | 
					     * @param position The position of the character
 | 
				
			||||||
 | 
					     * @param name The name of the character
 | 
				
			||||||
 | 
					     * @param maxHp The maximum hp of the character
 | 
				
			||||||
 | 
					     * @param maxMp The maximum mp of the character
 | 
				
			||||||
 | 
					     * @param maxAp The maximum ap of the character
 | 
				
			||||||
 | 
					     * @param hp The current hp of the character
 | 
				
			||||||
 | 
					     * @param mp The current mp of the character
 | 
				
			||||||
 | 
					     * @param ap The current ap of the character
 | 
				
			||||||
 | 
					     * @param attackRange The ranged attack range of the character
 | 
				
			||||||
 | 
					     * @param rangedDamage The ranged damage of the character
 | 
				
			||||||
 | 
					     * @param meleeDamage The melee damage of the character
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public Character(EntityID id, IntVector2 position, String name, int maxHp, int maxMp, int maxAp, int hp, int mp, int ap, int attackRange, int rangedDamage, int meleeDamage) {
 | 
				
			||||||
 | 
					        super(id, position);
 | 
				
			||||||
 | 
					        solid = false;
 | 
				
			||||||
 | 
					        opaque = true;
 | 
				
			||||||
 | 
					        if(id.type == EntityType.NPC && id.id == NPCType.Thanos.getID()) {
 | 
				
			||||||
 | 
					            solid = true; //characters cannot walk into thanos
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.name = name;
 | 
				
			||||||
 | 
					        this.hp = new Stat(StatType.HP, hp, maxHp);
 | 
				
			||||||
 | 
					        this.mp = new Stat(StatType.MP, mp, maxMp);
 | 
				
			||||||
 | 
					        this.ap = new Stat(StatType.AP, ap, maxAp);
 | 
				
			||||||
 | 
					        this.attackRange = attackRange;
 | 
				
			||||||
 | 
					        this.rangedDamage = rangedDamage;
 | 
				
			||||||
 | 
					        this.meleeDamage = meleeDamage;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Checks if the character is still alive.
 | 
					     * Checks if the character is still alive.
 | 
				
			||||||
     * @return Whether or not the characters hp is greater than 0
 | 
					     * @return Whether or not the characters hp is greater than 0
 | 
				
			||||||
 | 
				
			|||||||
@ -32,8 +32,17 @@ public class Stat {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public Stat(StatType type, int value, int max) {
 | 
					    public Stat(StatType type, int value, int max) {
 | 
				
			||||||
        this.type = type;
 | 
					        this.type = type;
 | 
				
			||||||
        this.max = max;
 | 
					 | 
				
			||||||
        this.value = value;
 | 
					        this.value = value;
 | 
				
			||||||
 | 
					        this.max = max;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new {@link Stat} with the same values as the
 | 
				
			||||||
 | 
					     * given {@link Stat}
 | 
				
			||||||
 | 
					     * @param toCopy is the {@link Stat} to copy
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public Stat(Stat toCopy) {
 | 
				
			||||||
 | 
					        this(toCopy.type, toCopy.getValue(), toCopy.getMax());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public int getValue() {
 | 
					    public int getValue() {
 | 
				
			||||||
 | 
				
			|||||||
@ -119,6 +119,7 @@ public class GameInstance {
 | 
				
			|||||||
        manager.applyEvent(gameStateEvent);
 | 
					        manager.applyEvent(gameStateEvent);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Deprecated
 | 
				
			||||||
    public GameState getGameStateUnsafe() {
 | 
					    public GameState getGameStateUnsafe() {
 | 
				
			||||||
        return _state;
 | 
					        return _state;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -27,7 +27,7 @@ public class GameLogic {
 | 
				
			|||||||
     * Checks a {@link Request} for validity for a {@link GameState}.
 | 
					     * Checks a {@link Request} for validity for a {@link GameState}.
 | 
				
			||||||
     * @param state The game state to check on
 | 
					     * @param state The game state to check on
 | 
				
			||||||
     * @param request The request to validate
 | 
					     * @param request The request to validate
 | 
				
			||||||
     * @return Whether or not the request is valid
 | 
					     * @return Whether the request is valid
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected static boolean checkRequest(GameState state, Request request) {
 | 
					    protected static boolean checkRequest(GameState state, Request request) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
@ -36,15 +36,20 @@ public class GameLogic {
 | 
				
			|||||||
                    CharacterRequest data = (CharacterRequest)request;
 | 
					                    CharacterRequest data = (CharacterRequest)request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Character origin = getCharacter(state, data.originField, data.originEntity);
 | 
					                    Character origin = getCharacter(state, data.originField, data.originEntity);
 | 
				
			||||||
                    Character target = getCharacter(state, data.targetField, data.targetEntity);
 | 
					                    Entity target = getAttackable(state, data.targetField, data.targetEntity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    requireTurn(state, origin);
 | 
					                    requireTurn(state, origin);
 | 
				
			||||||
                    requireOppositeTeam(origin, target);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    requireAlive(origin);
 | 
					                    requireAlive(origin);
 | 
				
			||||||
                    requireAlive(target);
 | 
					 | 
				
			||||||
                    requireAP(origin, 1);
 | 
					                    requireAP(origin, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if(target instanceof Character) {
 | 
				
			||||||
 | 
					                        Character targetCharacter = (Character)target;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        requireOppositeTeam(origin, targetCharacter);
 | 
				
			||||||
 | 
					                        requireAlive(targetCharacter);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if(request.type == RequestType.MeleeAttackRequest) {
 | 
					                    if(request.type == RequestType.MeleeAttackRequest) {
 | 
				
			||||||
                        if(origin.meleeDamage != data.value) {
 | 
					                        if(origin.meleeDamage != data.value) {
 | 
				
			||||||
                            throw new InvalidRequestException("Invalid melee damage");
 | 
					                            throw new InvalidRequestException("Invalid melee damage");
 | 
				
			||||||
@ -126,32 +131,57 @@ public class GameLogic {
 | 
				
			|||||||
                        case SpaceStone -> {
 | 
					                        case SpaceStone -> {
 | 
				
			||||||
                            verifyCoordinates(state, data.targetField);
 | 
					                            verifyCoordinates(state, data.targetField);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            if(state.entities.blocksMovement(data.targetField)) {
 | 
					                            if(state.entities.findByPosition(data.targetField).size() != 0) {
 | 
				
			||||||
                                throw new InvalidRequestException("Using space stone onto blocked field");
 | 
					                                throw new InvalidRequestException("Using space stone onto non-free field");
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        case MindStone -> {
 | 
					                        case MindStone -> {
 | 
				
			||||||
                            if(data.originField == data.targetField) {
 | 
					                            verifyCoordinates(state, data.targetField);
 | 
				
			||||||
                                throw new InvalidRequestException("Invalid mind stone target field");
 | 
					
 | 
				
			||||||
                            }
 | 
					                            requireLineOfSight(state, data.originField, data.targetField);
 | 
				
			||||||
                            if(data.value != state.partyConfig.mindStoneDMG) {
 | 
					
 | 
				
			||||||
                                throw new InvalidRequestException("Invalid mind stone damage");
 | 
					                            getAttackableWithoutID(state, data.targetField);
 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        case RealityStone -> {
 | 
					                        case RealityStone -> {
 | 
				
			||||||
                            // no check done
 | 
					                            verifyCoordinates(state, data.targetField);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            boolean rock = false;
 | 
				
			||||||
 | 
					                            boolean empty = true;
 | 
				
			||||||
 | 
					                            for(Entity entity: state.entities.findByPosition(data.targetField)) {
 | 
				
			||||||
 | 
					                                if(entity.id.type == EntityType.Rocks) {
 | 
				
			||||||
 | 
					                                    rock = true;
 | 
				
			||||||
 | 
					                                    break;
 | 
				
			||||||
 | 
					                                }else {
 | 
				
			||||||
 | 
					                                    empty = false;
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            if(!empty && !rock) {
 | 
				
			||||||
 | 
					                                throw new InvalidRequestException("Using reality stone on non-free field without a rock");
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        case PowerStone -> {
 | 
					                        case PowerStone -> {
 | 
				
			||||||
                            Character target = getCharacter(state, data.targetField, data.targetEntity);
 | 
					                            verifyCoordinates(state, data.targetField);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            requireAlive(target);
 | 
					                            Entity target = getAttackableWithoutID(state, data.targetField);
 | 
				
			||||||
                            requireOppositeTeam(origin, target);
 | 
					
 | 
				
			||||||
 | 
					                            if(target instanceof Character) {
 | 
				
			||||||
 | 
					                                Character targetCharacter = (Character)target;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                requireOppositeTeam(origin, targetCharacter);
 | 
				
			||||||
 | 
					                                requireAlive(targetCharacter);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            if(data.originField.distanceChebyshev(data.targetField) != 1) {
 | 
				
			||||||
 | 
					                                throw new InvalidRequestException("Invalid melee target distance");
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        case TimeStone -> {
 | 
					                        case TimeStone -> {
 | 
				
			||||||
                            // "👍  i approve" - the server
 | 
					                            // "👍  i approve" - the server
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        case SoulStone -> {
 | 
					                        case SoulStone -> {
 | 
				
			||||||
                            Character target = getCharacter(state, data.targetField, data.targetEntity);
 | 
					                            verifyCoordinates(state, data.targetField);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            Character target = getCharacterWithoutID(state, data.targetField);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            if(data.originEntity.equals(data.targetEntity)) {
 | 
					                            if(data.originEntity.equals(data.targetEntity)) {
 | 
				
			||||||
                                throw new InvalidRequestException("Invalid soul stone target (same as origin)");
 | 
					                                throw new InvalidRequestException("Invalid soul stone target (same as origin)");
 | 
				
			||||||
@ -183,6 +213,37 @@ public class GameLogic {
 | 
				
			|||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Retrieves an attack-able Entity ({@link Character} or {@link Rock}) for a {@link Request}.
 | 
				
			||||||
 | 
					     * @param state The game state to use
 | 
				
			||||||
 | 
					     * @param position The requested position
 | 
				
			||||||
 | 
					     * @param entityID The requested {@link EntityID}
 | 
				
			||||||
 | 
					     * @return The found entity
 | 
				
			||||||
 | 
					     * @throws InvalidRequestException if the entity is invalid or not found
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static Entity getAttackable(GameState state, IntVector2 position, EntityID entityID) throws InvalidRequestException {
 | 
				
			||||||
 | 
					        Entity entity = state.entities.findEntity(entityID);
 | 
				
			||||||
 | 
					        if(entity == null || !entity.getPosition().equals(position) || (!(entity instanceof Character) && !(entity instanceof Rock)) || entity.id.type == EntityType.NPC) {
 | 
				
			||||||
 | 
					            throw new InvalidRequestException("Invalid target character or rock");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return entity;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Retrieves an attack-able Entity ({@link Character} or {@link Rock}) for a {@link Request}.
 | 
				
			||||||
 | 
					     * @param state The game state to use
 | 
				
			||||||
 | 
					     * @param position The requested position
 | 
				
			||||||
 | 
					     * @return The found entity
 | 
				
			||||||
 | 
					     * @throws InvalidRequestException if the entity is invalid or not found
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static Entity getAttackableWithoutID(GameState state, IntVector2 position) throws InvalidRequestException {
 | 
				
			||||||
 | 
					        ArrayList<Entity> entities = state.entities.findByPosition(position);
 | 
				
			||||||
 | 
					        if(entities.isEmpty() || entities.get(0).id.type == EntityType.NPC) {
 | 
				
			||||||
 | 
					            throw new InvalidRequestException("Invalid target character or rock");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return entities.get(0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Retrieves a {@link Character} for a {@link Request}.
 | 
					     * Retrieves a {@link Character} for a {@link Request}.
 | 
				
			||||||
     * @param state The game state to use
 | 
					     * @param state The game state to use
 | 
				
			||||||
@ -203,6 +264,25 @@ public class GameLogic {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Retrieves a {@link Character} for a {@link Request}.
 | 
				
			||||||
 | 
					     * @param state The game state to use
 | 
				
			||||||
 | 
					     * @param position The requested position
 | 
				
			||||||
 | 
					     * @return The found character
 | 
				
			||||||
 | 
					     * @throws InvalidRequestException if the character is invalid or not found
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static Character getCharacterWithoutID(GameState state, IntVector2 position) throws InvalidRequestException {
 | 
				
			||||||
 | 
					        ArrayList<Entity> entities = state.entities.findByPosition(position);
 | 
				
			||||||
 | 
					        if(entities.isEmpty() || !(entities.get(0) instanceof Character) || entities.get(0).id.type == EntityType.NPC) {
 | 
				
			||||||
 | 
					            throw new InvalidRequestException("Invalid origin or target character");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            return (Character)entities.get(0);
 | 
				
			||||||
 | 
					        }catch(Exception ignored) {
 | 
				
			||||||
 | 
					            throw new InvalidRequestException("Invalid origin or target character (cast failed)");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Verifies that a {@link Character} has a turn.
 | 
					     * Verifies that a {@link Character} has a turn.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@ -315,26 +395,8 @@ public class GameLogic {
 | 
				
			|||||||
                        .withAmount(data.value)
 | 
					                        .withAmount(data.value)
 | 
				
			||||||
                        .buildEntityEvent());
 | 
					                        .buildEntityEvent());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Character target = (Character)state.entities.findEntity(data.targetEntity);
 | 
					                Entity targetEntity = state.entities.findEntity(data.targetEntity);
 | 
				
			||||||
                if(target.hp.getValue() <= data.value) {
 | 
					                result.addAll(checkDeath(state, targetEntity, data.value));
 | 
				
			||||||
 | 
					 | 
				
			||||||
                    List<StoneType> stones = Arrays.asList(target.inventory.getStonesAsArray());
 | 
					 | 
				
			||||||
                    Collections.shuffle(stones); // required by documents
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    ArrayList<IntVector2> used = new ArrayList<>();
 | 
					 | 
				
			||||||
                    for(StoneType stone: stones) {
 | 
					 | 
				
			||||||
                        ArrayList<IntVector2> options = getFreeNeighbour(state, target.getPosition(), used);
 | 
					 | 
				
			||||||
                        IntVector2 picked = options.get(rand.nextInt(options.size()));
 | 
					 | 
				
			||||||
                        used.add(picked);
 | 
					 | 
				
			||||||
                        result.add(new EventBuilder(EventType.SpawnEntityEvent)
 | 
					 | 
				
			||||||
                                .withEntity(new InfinityStone(
 | 
					 | 
				
			||||||
                                        new EntityID(EntityType.InfinityStones, stone.getID()),
 | 
					 | 
				
			||||||
                                        picked,
 | 
					 | 
				
			||||||
                                        stone
 | 
					 | 
				
			||||||
                                ))
 | 
					 | 
				
			||||||
                                .buildEntityEvent());
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            case MoveRequest -> {
 | 
					            case MoveRequest -> {
 | 
				
			||||||
                CharacterRequest data = (CharacterRequest)request;
 | 
					                CharacterRequest data = (CharacterRequest)request;
 | 
				
			||||||
@ -422,18 +484,16 @@ public class GameLogic {
 | 
				
			|||||||
                                    .buildCharacterEvent());
 | 
					                                    .buildCharacterEvent());
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        case MindStone -> {
 | 
					                        case MindStone -> {
 | 
				
			||||||
                            EntityType target = data.originEntity.type == EntityType.P1 ? EntityType.P2 : EntityType.P1;
 | 
					                            ArrayList<Entity> found = state.entities.findByPosition(data.targetField);
 | 
				
			||||||
                            for(IntVector2 pos: rasterizeInfinity(data.originField, data.targetField, state.mapSize, false)) {
 | 
					                            Entity targetEntity = found.get(0);
 | 
				
			||||||
                                for(Entity entity: state.entities.findByPosition(pos)) {
 | 
					
 | 
				
			||||||
                                    if(entity.id.isSameType(target)) {
 | 
					 | 
				
			||||||
                            result.add(new EventBuilder(EventType.TakenDamageEvent)
 | 
					                            result.add(new EventBuilder(EventType.TakenDamageEvent)
 | 
				
			||||||
                                                .withTargetEntity(entity.id)
 | 
					                                    .withTargetEntity(targetEntity.id)
 | 
				
			||||||
                                                .withTargetField(pos)
 | 
					                                    .withTargetField(data.targetField)
 | 
				
			||||||
                                                .withAmount(data.value)
 | 
					                                    .withAmount(state.partyConfig.mindStoneDMG)
 | 
				
			||||||
                                    .buildEntityEvent());
 | 
					                                    .buildEntityEvent());
 | 
				
			||||||
                                    }
 | 
					
 | 
				
			||||||
                                }
 | 
					                            result.addAll(checkDeath(state, targetEntity, state.partyConfig.mindStoneDMG));
 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        case RealityStone -> {
 | 
					                        case RealityStone -> {
 | 
				
			||||||
                            EntityID target = null;
 | 
					                            EntityID target = null;
 | 
				
			||||||
@ -456,20 +516,29 @@ public class GameLogic {
 | 
				
			|||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        case PowerStone -> {
 | 
					                        case PowerStone -> {
 | 
				
			||||||
 | 
					                            ArrayList<Entity> found = state.entities.findByPosition(data.targetField);
 | 
				
			||||||
 | 
					                            Entity targetEntity = found.get(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            Character origin = (Character)state.entities.findEntity(data.originEntity);
 | 
					                            Character origin = (Character)state.entities.findEntity(data.originEntity);
 | 
				
			||||||
                            int dmg = (int)Math.round(origin.hp.getMax() * 0.1);
 | 
					                            int dmg = (int)Math.round(origin.hp.getMax() * 0.1);
 | 
				
			||||||
                            if(origin.hp.getValue() != 1 && dmg > 0) {
 | 
					                            //this is ugly ... but also easy to understand
 | 
				
			||||||
 | 
					                            int hp1 = origin.hp.getValue();
 | 
				
			||||||
 | 
					                            int hp2 = Math.max(1, origin.hp.getValue() - dmg);
 | 
				
			||||||
 | 
					                            int actualDmg = hp1 - hp2;
 | 
				
			||||||
 | 
					                            if(actualDmg > 0) {
 | 
				
			||||||
                                result.add(new EventBuilder(EventType.TakenDamageEvent)
 | 
					                                result.add(new EventBuilder(EventType.TakenDamageEvent)
 | 
				
			||||||
                                        .withTargetEntity(data.originEntity)
 | 
					                                        .withTargetEntity(data.originEntity)
 | 
				
			||||||
                                        .withTargetField(data.originField)
 | 
					                                        .withTargetField(data.originField)
 | 
				
			||||||
                                        .withAmount(dmg)
 | 
					                                        .withAmount(actualDmg)
 | 
				
			||||||
                                        .buildEntityEvent());
 | 
					                                        .buildEntityEvent());
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            result.add(new EventBuilder(EventType.TakenDamageEvent)
 | 
					                            result.add(new EventBuilder(EventType.TakenDamageEvent)
 | 
				
			||||||
                                    .withTargetEntity(data.targetEntity)
 | 
					                                    .withTargetEntity(targetEntity.id)
 | 
				
			||||||
                                    .withTargetField(data.targetField)
 | 
					                                    .withTargetField(data.targetField)
 | 
				
			||||||
                                    .withAmount(origin.meleeDamage * 2)
 | 
					                                    .withAmount(origin.meleeDamage * 2)
 | 
				
			||||||
                                    .buildEntityEvent());
 | 
					                                    .buildEntityEvent());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            result.addAll(checkDeath(state, targetEntity, origin.meleeDamage * 2));
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        case TimeStone -> {
 | 
					                        case TimeStone -> {
 | 
				
			||||||
                            Character origin = (Character)state.entities.findEntity(data.originEntity);
 | 
					                            Character origin = (Character)state.entities.findEntity(data.originEntity);
 | 
				
			||||||
@ -491,9 +560,11 @@ public class GameLogic {
 | 
				
			|||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        case SoulStone -> {
 | 
					                        case SoulStone -> {
 | 
				
			||||||
                            Character target = (Character)state.entities.findEntity(data.targetEntity);
 | 
					                            ArrayList<Entity> found = state.entities.findByPosition(data.targetField);
 | 
				
			||||||
 | 
					                            Character target = (Character)found.get(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            result.add(new EventBuilder(EventType.HealedEvent)
 | 
					                            result.add(new EventBuilder(EventType.HealedEvent)
 | 
				
			||||||
                                    .withTargetEntity(data.targetEntity)
 | 
					                                    .withTargetEntity(target.id)
 | 
				
			||||||
                                    .withTargetField(data.targetField)
 | 
					                                    .withTargetField(data.targetField)
 | 
				
			||||||
                                    .withAmount(target.hp.getMax())
 | 
					                                    .withAmount(target.hp.getMax())
 | 
				
			||||||
                                    .buildEntityEvent());
 | 
					                                    .buildEntityEvent());
 | 
				
			||||||
@ -512,6 +583,49 @@ public class GameLogic {
 | 
				
			|||||||
        return result;
 | 
					        return result;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Checks for death of a Character or Rock and returns the resulting {@link Event Events}.
 | 
				
			||||||
 | 
					     * @param state The game state to apply to
 | 
				
			||||||
 | 
					     * @param targetEntity The entity to check
 | 
				
			||||||
 | 
					     * @param damage The damage taken
 | 
				
			||||||
 | 
					     * @return The resulting events
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static ArrayList<Event> checkDeath(GameState state, Entity targetEntity, int damage) {
 | 
				
			||||||
 | 
					        ArrayList<Event> result = new ArrayList<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(targetEntity instanceof Character) {
 | 
				
			||||||
 | 
					            Character target = (Character)targetEntity;
 | 
				
			||||||
 | 
					            if(target.hp.getValue() <= damage) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                List<StoneType> stones = Arrays.asList(target.inventory.getStonesAsArray());
 | 
				
			||||||
 | 
					                Collections.shuffle(stones); // required by documents
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                ArrayList<IntVector2> used = new ArrayList<>();
 | 
				
			||||||
 | 
					                for(StoneType stone: stones) {
 | 
				
			||||||
 | 
					                    ArrayList<IntVector2> options = getFreeNeighbour(state, target.getPosition(), used);
 | 
				
			||||||
 | 
					                    IntVector2 picked = options.get(rand.nextInt(options.size()));
 | 
				
			||||||
 | 
					                    used.add(picked);
 | 
				
			||||||
 | 
					                    result.add(new EventBuilder(EventType.SpawnEntityEvent)
 | 
				
			||||||
 | 
					                            .withEntity(new InfinityStone(
 | 
				
			||||||
 | 
					                                    new EntityID(EntityType.InfinityStones, stone.getID()),
 | 
				
			||||||
 | 
					                                    picked,
 | 
				
			||||||
 | 
					                                    stone
 | 
				
			||||||
 | 
					                            ))
 | 
				
			||||||
 | 
					                            .buildEntityEvent());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }else if(targetEntity instanceof Rock) {
 | 
				
			||||||
 | 
					            Rock target = (Rock)targetEntity;
 | 
				
			||||||
 | 
					            if(target.getHp() <= damage) {
 | 
				
			||||||
 | 
					                result.add(new EventBuilder(EventType.DestroyedEntityEvent)
 | 
				
			||||||
 | 
					                        .withTargetField(target.getPosition())
 | 
				
			||||||
 | 
					                        .withTargetEntity(target.id)
 | 
				
			||||||
 | 
					                        .buildEntityEvent());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Applies an {@link Event} to a {@link GameState}.
 | 
					     * Applies an {@link Event} to a {@link GameState}.
 | 
				
			||||||
@ -526,7 +640,10 @@ public class GameLogic {
 | 
				
			|||||||
            case TakenDamageEvent -> {
 | 
					            case TakenDamageEvent -> {
 | 
				
			||||||
                EntityEvent data = (EntityEvent)event;
 | 
					                EntityEvent data = (EntityEvent)event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Character target = (Character)state.entities.findEntity(data.targetEntity);
 | 
					                Entity targetEntity = state.entities.findEntity(data.targetEntity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if(targetEntity instanceof Character) {
 | 
				
			||||||
 | 
					                    Character target = (Character)targetEntity;
 | 
				
			||||||
                    target.hp.decreaseValue(data.amount);
 | 
					                    target.hp.decreaseValue(data.amount);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    EntityType opposing = target.id.type == EntityType.P1 ? EntityType.P2 : EntityType.P1;
 | 
					                    EntityType opposing = target.id.type == EntityType.P1 ? EntityType.P2 : EntityType.P1;
 | 
				
			||||||
@ -540,6 +657,10 @@ public class GameLogic {
 | 
				
			|||||||
                        NPC thanos = (NPC)state.entities.findEntity(state.activeCharacter);
 | 
					                        NPC thanos = (NPC)state.entities.findEntity(state.activeCharacter);
 | 
				
			||||||
                        target.inventory.transfer(thanos.inventory);
 | 
					                        target.inventory.transfer(thanos.inventory);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					                }else if(targetEntity instanceof Rock) {
 | 
				
			||||||
 | 
					                    Rock target = (Rock)targetEntity;
 | 
				
			||||||
 | 
					                    target.decreaseHp(data.amount);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            case ConsumedAPEvent -> {
 | 
					            case ConsumedAPEvent -> {
 | 
				
			||||||
                EntityEvent data = (EntityEvent)event;
 | 
					                EntityEvent data = (EntityEvent)event;
 | 
				
			||||||
 | 
				
			|||||||
@ -43,6 +43,9 @@ public class EntityDeserializer extends JsonDeserializer<Entity> {
 | 
				
			|||||||
                        id,
 | 
					                        id,
 | 
				
			||||||
                        codec.treeToValue(node.get("position"), IntVector2.class),
 | 
					                        codec.treeToValue(node.get("position"), IntVector2.class),
 | 
				
			||||||
                        characterName,
 | 
					                        characterName,
 | 
				
			||||||
 | 
					                        properties.HP,
 | 
				
			||||||
 | 
					                        properties.MP,
 | 
				
			||||||
 | 
					                        properties.AP,
 | 
				
			||||||
                        node.get("HP").asInt(),
 | 
					                        node.get("HP").asInt(),
 | 
				
			||||||
                        node.get("MP").asInt(),
 | 
					                        node.get("MP").asInt(),
 | 
				
			||||||
                        node.get("AP").asInt(),
 | 
					                        node.get("AP").asInt(),
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,67 @@
 | 
				
			|||||||
 | 
					package uulm.teamname.marvelous.gamelibrary;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import net.jqwik.api.*;
 | 
				
			||||||
 | 
					import net.jqwik.api.Tuple;
 | 
				
			||||||
 | 
					import net.jqwik.api.lifecycle.BeforeProperty;
 | 
				
			||||||
 | 
					import org.junit.jupiter.api.BeforeEach;
 | 
				
			||||||
 | 
					import org.junit.jupiter.api.Disabled;
 | 
				
			||||||
 | 
					import org.junit.jupiter.api.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static org.mockito.Mockito.*;
 | 
				
			||||||
 | 
					import static org.assertj.core.api.Assertions.*;
 | 
				
			||||||
 | 
					import static org.junit.jupiter.api.Assertions.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class IntVector2Test {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @BeforeEach
 | 
				
			||||||
 | 
					    @BeforeProperty
 | 
				
			||||||
 | 
					    void beforeAll() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Property
 | 
				
			||||||
 | 
					    @Disabled("This test fails! Therefore,a bug exists here")
 | 
				
			||||||
 | 
					    void settingLengthToOneResultsInCardinalDirections(
 | 
				
			||||||
 | 
					            @ForAll int x,
 | 
				
			||||||
 | 
					            @ForAll int y
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        var vec = new IntVector2(x, y).setLength(1);
 | 
				
			||||||
 | 
					        // System.out.printf("Input was (%d, %d). Output is %s\n", x, y, vec);
 | 
				
			||||||
 | 
					        if (!vec.equals(new IntVector2(0, 0))) {
 | 
				
			||||||
 | 
					            assertThat(IntVector2.CardinalDirections)
 | 
				
			||||||
 | 
					                    .contains(vec);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Property
 | 
				
			||||||
 | 
					    void settingSmallVectorLengthResultsInCardinalDirections(
 | 
				
			||||||
 | 
					            @ForAll("SmallIntegers") Integer x,
 | 
				
			||||||
 | 
					            @ForAll("SmallIntegers") Integer y
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        var vec = new IntVector2(x, y).setLength(1);
 | 
				
			||||||
 | 
					//        System.out.printf("Input was (%d, %d). Output is %s\n", x, y, vec);
 | 
				
			||||||
 | 
					        if (!vec.equals(new IntVector2(0, 0))) {
 | 
				
			||||||
 | 
					            assertThat(IntVector2.CardinalDirections)
 | 
				
			||||||
 | 
					                    .contains(vec);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    void setLengthCardinalTest() {
 | 
				
			||||||
 | 
					        assertThat(new IntVector2(1, 1).setLength(1))
 | 
				
			||||||
 | 
					                .isEqualTo(new IntVector2(1, 1));
 | 
				
			||||||
 | 
					        assertThat(new IntVector2(0, 1).setLength(1))
 | 
				
			||||||
 | 
					                .isEqualTo(new IntVector2(0, 1));
 | 
				
			||||||
 | 
					        assertThat(new IntVector2(1, -1).setLength(1))
 | 
				
			||||||
 | 
					                .isEqualTo(new IntVector2(1, -1));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Provide("SmallIntegers")
 | 
				
			||||||
 | 
					    Arbitrary<Integer> smallIntegers() {
 | 
				
			||||||
 | 
					        return Arbitraries.integers()
 | 
				
			||||||
 | 
					                .between(-1000, 1000);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -120,15 +120,27 @@ class MessageValidationUtilityTest {
 | 
				
			|||||||
        var characterConfig = new CharacterConfig();
 | 
					        var characterConfig = new CharacterConfig();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assertThat(ValidationUtility.validate(characterConfig).get())
 | 
					        assertThat(ValidationUtility.validate(characterConfig).get())
 | 
				
			||||||
                .isEqualTo("characters must not be empty");
 | 
					                .isIn("characters must not be empty", "characters darf nicht leer sein");
 | 
				
			||||||
 | 
					               // .isEqualTo("characters must not be empty");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        characterConfig.characters = new CharacterProperties[] {racoon, quicksilver, hulk, loki, silversurfer};
 | 
					        characterConfig.characters = new CharacterProperties[] {racoon, quicksilver, hulk, loki, silversurfer};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assertThat(ValidationUtility.validate(characterConfig).get())
 | 
					        var results = ValidationUtility.validate(characterConfig).get();
 | 
				
			||||||
                .contains("characters has less than 24 characters",
 | 
					        assertThat(results.contains("characters has less than 24 characters") ||
 | 
				
			||||||
                        "characters[4].rangedDamage must be greater than 0",
 | 
					                results.contains("characters hat weniger als 24 characters"))
 | 
				
			||||||
                        "characters[4].name must not be empty",
 | 
					                .isTrue();
 | 
				
			||||||
                        "characters[4].meleeDamage must be greater than 0");
 | 
					
 | 
				
			||||||
 | 
					        assertThat(results.contains("characters[4].rangedDamage must be greater than 0") ||
 | 
				
			||||||
 | 
					                results.contains("characters[4].rangedDamage muss größer als 0 sein"))
 | 
				
			||||||
 | 
					                .isTrue();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertThat(results.contains("characters[4].name must not be empty") ||
 | 
				
			||||||
 | 
					                results.contains("characters[4].name darf nicht leer sein"))
 | 
				
			||||||
 | 
					                .isTrue();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertThat(results.contains("characters[4].meleeDamage must be greater than 0") ||
 | 
				
			||||||
 | 
					                results.contains("characters[4].meleeDamage muss größer als 0 sein"))
 | 
				
			||||||
 | 
					                .isTrue();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user