diff --git a/backend/src/main/kotlin/xyz/poeschl/roborush/controller/ConfigRestController.kt b/backend/src/main/kotlin/xyz/poeschl/roborush/controller/ConfigRestController.kt index 20292dc9..7203c8c4 100644 --- a/backend/src/main/kotlin/xyz/poeschl/roborush/controller/ConfigRestController.kt +++ b/backend/src/main/kotlin/xyz/poeschl/roborush/controller/ConfigRestController.kt @@ -7,10 +7,7 @@ import org.springframework.http.MediaType import org.springframework.security.access.prepost.PreAuthorize import org.springframework.web.bind.annotation.* import org.springframework.web.multipart.MultipartFile -import xyz.poeschl.roborush.controller.restmodels.MapActiveDto -import xyz.poeschl.roborush.controller.restmodels.MapAttributeSaveDto -import xyz.poeschl.roborush.controller.restmodels.MapGenerationResult -import xyz.poeschl.roborush.controller.restmodels.TextConfigDto +import xyz.poeschl.roborush.controller.restmodels.* import xyz.poeschl.roborush.exceptions.InvalidConfigKeyException import xyz.poeschl.roborush.exceptions.InvalidHeightMapException import xyz.poeschl.roborush.exceptions.MapNotFound @@ -18,7 +15,6 @@ import xyz.poeschl.roborush.models.settings.ClientSettings import xyz.poeschl.roborush.models.settings.SaveSettingDto import xyz.poeschl.roborush.models.settings.Setting import xyz.poeschl.roborush.models.settings.SettingKey -import xyz.poeschl.roborush.repositories.Map import xyz.poeschl.roborush.security.repository.User import xyz.poeschl.roborush.service.ConfigService import xyz.poeschl.roborush.service.MapService @@ -71,18 +67,18 @@ class ConfigRestController(private val configService: ConfigService, private val @SecurityRequirement(name = "Bearer Authentication") @PreAuthorize("hasRole('${User.ROLE_ADMIN}')") @GetMapping("/map", produces = [MediaType.APPLICATION_JSON_VALUE]) - fun getMaps(): List { - return mapService.getAllMaps() + fun getMaps(): List { + return mapService.getAllMaps().map { PlaygroundMap(it) } } @SecurityRequirement(name = "Bearer Authentication") @PreAuthorize("hasRole('${User.ROLE_ADMIN}')") @PostMapping("/map/{id}/active", consumes = [MediaType.APPLICATION_JSON_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE]) - fun setMapActive(@PathVariable id: Long, @RequestBody activeDto: MapActiveDto): Map { + fun setMapActive(@PathVariable id: Long, @RequestBody activeDto: MapActiveDto): PlaygroundMap { val map = mapService.getMap(id) if (map != null) { - return mapService.setMapActive(map, activeDto.active) + return PlaygroundMap(mapService.setMapActive(map, activeDto.active)) } else { throw MapNotFound("No matching map found for setting active") } @@ -91,11 +87,11 @@ class ConfigRestController(private val configService: ConfigService, private val @SecurityRequirement(name = "Bearer Authentication") @PreAuthorize("hasRole('${User.ROLE_ADMIN}')") @PostMapping("/map/{id}", consumes = [MediaType.APPLICATION_JSON_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE]) - fun setMapAttributes(@PathVariable id: Long, @RequestBody attributes: MapAttributeSaveDto): Map { + fun setMapAttributes(@PathVariable id: Long, @RequestBody attributes: MapAttributeSaveDto): PlaygroundMap { val map = mapService.getMap(id) if (map != null) { - return mapService.setMapAttributes(map, attributes) + return PlaygroundMap(mapService.setMapAttributes(map, attributes)) } else { throw MapNotFound("No matching map found for given id.") } diff --git a/backend/src/main/kotlin/xyz/poeschl/roborush/controller/GameRestController.kt b/backend/src/main/kotlin/xyz/poeschl/roborush/controller/GameRestController.kt index eb1b0846..eca063bb 100644 --- a/backend/src/main/kotlin/xyz/poeschl/roborush/controller/GameRestController.kt +++ b/backend/src/main/kotlin/xyz/poeschl/roborush/controller/GameRestController.kt @@ -18,7 +18,7 @@ class GameRestController(private val gameHandler: GameHandler) { @GetMapping("/map", produces = [MediaType.APPLICATION_JSON_VALUE]) fun getMap(): PlaygroundMap { - return gameHandler.getCurrentPlaygroundMap() + return PlaygroundMap(gameHandler.getCurrentMap()) } @Operation( diff --git a/backend/src/main/kotlin/xyz/poeschl/roborush/controller/restmodels/MapDtos.kt b/backend/src/main/kotlin/xyz/poeschl/roborush/controller/restmodels/MapDtos.kt index ef742a9c..0c6c2300 100644 --- a/backend/src/main/kotlin/xyz/poeschl/roborush/controller/restmodels/MapDtos.kt +++ b/backend/src/main/kotlin/xyz/poeschl/roborush/controller/restmodels/MapDtos.kt @@ -25,7 +25,7 @@ data class PlaygroundMap( val maxHeight: Int ) { - constructor(map: Map, minHeight: Int, maxHeight: Int) : this( + constructor(map: Map) : this( map.id!!, map.mapName, map.size, @@ -35,7 +35,7 @@ data class PlaygroundMap( map.solarChargeRate, map.active, map.mapData, - minHeight, - maxHeight + minHeight = map.mapData.minOf { it.height }, + maxHeight = map.mapData.maxOf { it.height } ) } diff --git a/backend/src/main/kotlin/xyz/poeschl/roborush/gamelogic/GameHandler.kt b/backend/src/main/kotlin/xyz/poeschl/roborush/gamelogic/GameHandler.kt index 48bd0fce..84a61e7b 100644 --- a/backend/src/main/kotlin/xyz/poeschl/roborush/gamelogic/GameHandler.kt +++ b/backend/src/main/kotlin/xyz/poeschl/roborush/gamelogic/GameHandler.kt @@ -5,7 +5,6 @@ import org.springframework.cache.annotation.CacheEvict import org.springframework.cache.annotation.Cacheable import xyz.poeschl.roborush.configuration.GameLogic import xyz.poeschl.roborush.controller.WebsocketController -import xyz.poeschl.roborush.controller.restmodels.PlaygroundMap import xyz.poeschl.roborush.exceptions.PositionNotAllowedException import xyz.poeschl.roborush.exceptions.PositionOutOfMapException import xyz.poeschl.roborush.gamelogic.actions.RobotAction @@ -46,16 +45,6 @@ class GameHandler( return mapHandler.getMapWithPositions(getGlobalKnownPositions()) } - @Cacheable("playgroundMap") - fun getCurrentPlaygroundMap(): PlaygroundMap { - val fullMap = mapHandler.getCurrentFullMap() - - val minHeight = fullMap.mapData.minOf { it.height } - val maxHeight = fullMap.mapData.maxOf { it.height } - - return PlaygroundMap(getCurrentMap(), minHeight, maxHeight) - } - fun getTileAtPosition(position: Position): Tile { return mapHandler.getTileAtPosition(position) } @@ -129,7 +118,7 @@ class GameHandler( } } - @CacheEvict(cacheNames = ["knownMap", "playgroundMap"], allEntries = true) + @CacheEvict(cacheNames = ["knownMap"], allEntries = true) fun executeAllRobotActions() { robotHandler.executeRobotActions(this) setGameTurn(currentTurn + 1) diff --git a/backend/src/test/kotlin/xyz/poeschl/roborush/gamelogic/GameHandlerTest.kt b/backend/src/test/kotlin/xyz/poeschl/roborush/gamelogic/GameHandlerTest.kt index ed465439..352384af 100644 --- a/backend/src/test/kotlin/xyz/poeschl/roborush/gamelogic/GameHandlerTest.kt +++ b/backend/src/test/kotlin/xyz/poeschl/roborush/gamelogic/GameHandlerTest.kt @@ -68,37 +68,6 @@ class GameHandlerTest { assertThat(result.mapData).containsAll(tiles) } - @Test - fun getCurrentPlaygroundMap() { - // WHEN - val tiles = listOf(a(`$Tile`().withHeight(10)), a(`$Tile`().withHeight(50)), a(`$Tile`().withHeight(20))) - val positions = tiles.map { it.position }.toSet() - val map = a(`$Map`().withId(2)) - tiles.forEach { map.addTile(it) } - val activeRobot1 = a(`$ActiveRobot`().withKnownPositions(positions)) - val activeRobot2 = a(`$ActiveRobot`().withKnownPositions(positions)) - - every { robotHandler.getAllActiveRobots() } returns setOf(activeRobot1, activeRobot2) - every { mapHandler.getCurrentFullMap() } returns map - every { mapHandler.getMapWithPositions(positions) } returns map - every { configService.getBooleanSetting(SettingKey.TARGET_POSITION_IN_GAMEINFO) } returns a(`$BooleanSetting`().withValue(false)) - - // THEN - val result = gameHandler.getCurrentPlaygroundMap() - - // VERIFY - assertThat(result.id).isEqualTo(map.id) - assertThat(result.mapName).isEqualTo(map.mapName) - assertThat(result.possibleStartPositions).isEqualTo(map.possibleStartPositions) - assertThat(result.targetPosition).isEqualTo(map.targetPosition) - assertThat(result.active).isEqualTo(map.active) - assertThat(result.maxRobotFuel).isEqualTo(map.maxRobotFuel) - assertThat(result.solarChargeRate).isEqualTo(map.solarChargeRate) - assertThat(result.mapData).containsAll(tiles) - assertThat(result.minHeight).isEqualTo(10) - assertThat(result.maxHeight).isEqualTo(50) - } - @Test fun isPositionValidForMove() { // WHEN diff --git a/frontend/src/assets/main.scss b/frontend/src/assets/main.scss index df68e277..b934cb06 100644 --- a/frontend/src/assets/main.scss +++ b/frontend/src/assets/main.scss @@ -41,6 +41,7 @@ @forward "bulma/sass/components/card"; @forward "bulma/sass/components/modal"; @forward "bulma/sass/components/navbar"; +@forward "bulma/sass/components/tabs"; @forward "bulma/sass/elements/button"; @forward "bulma/sass/elements/content"; @forward "bulma/sass/elements/delete"; diff --git a/frontend/src/components/MapCanvasComponent.vue b/frontend/src/components/MapCanvasComponent.vue index 1c3e11b8..c28863e2 100644 --- a/frontend/src/components/MapCanvasComponent.vue +++ b/frontend/src/components/MapCanvasComponent.vue @@ -24,6 +24,7 @@ const cellSize = 16; const cellBorder = 1; const specialTileBorderWidth = 4; const fullTileSize = cellSize + 2 * cellBorder; +const smallCanvasThresholdInPixel = 160; const mapBorderColor = new Color(0, 0, 0); const mapColor = new Color(10, 60, 1); @@ -74,6 +75,7 @@ const heightMap = computed(() => { const currentPath = ref({ points: [] }); const mapTileMinHeight = ref(0); const mapTileMaxHeight = ref(255); +const smallCanvas = ref(false); const emits = defineEmits<{ (e: "pathUpdate", path: Path): void; @@ -90,11 +92,9 @@ onMounted(() => { watch( () => props.drawablePath, (newValue) => { - if (newValue) { - currentPath.value.points = []; - emits("pathUpdate", currentPath.value); - redraw(); - } + currentPath.value.points = []; + emits("pathUpdate", currentPath.value); + redraw(); }, ); watch(() => props.map, redraw); @@ -176,6 +176,10 @@ const updateCanvasData = () => { mapTileMinHeight.value = props.map.minHeight; mapTileMaxHeight.value = props.map.maxHeight; + + if (container.value && container.value.offsetWidth < smallCanvasThresholdInPixel) { + smallCanvas.value = true; + } } }; @@ -235,21 +239,31 @@ const drawRobots = () => { }; const drawTile = (drawContext: CanvasRenderingContext2D, color: Color) => { - drawContext.fillStyle = mapBorderColor.toHex(); - drawContext.fillRect(0, 0, fullTileSize, fullTileSize); - drawContext.fillStyle = color.toHex(); - drawContext.fillRect(cellBorder, cellBorder, cellSize, cellSize); + if (smallCanvas.value) { + drawContext.fillStyle = color.toHex(); + drawContext.fillRect(0, 0, fullTileSize, fullTileSize); + } else { + drawContext.fillStyle = mapBorderColor.toHex(); + drawContext.fillRect(0, 0, fullTileSize, fullTileSize); + drawContext.fillStyle = color.toHex(); + drawContext.fillRect(cellBorder, cellBorder, cellSize, cellSize); + } }; const drawTileBorder = (drawContext: CanvasRenderingContext2D, color: Color) => { - drawContext.lineWidth = specialTileBorderWidth; - drawContext.strokeStyle = color.toHex(); - drawContext.strokeRect( - cellBorder + specialTileBorderWidth / 2, - cellBorder + specialTileBorderWidth / 2, - cellSize - specialTileBorderWidth, - cellSize - specialTileBorderWidth, - ); + if (smallCanvas.value) { + drawContext.fillStyle = color.toHex(); + drawContext.fillRect(0, 0, fullTileSize, fullTileSize); + } else { + drawContext.lineWidth = specialTileBorderWidth; + drawContext.strokeStyle = color.toHex(); + drawContext.strokeRect( + cellBorder + specialTileBorderWidth / 2, + cellBorder + specialTileBorderWidth / 2, + cellSize - specialTileBorderWidth, + cellSize - specialTileBorderWidth, + ); + } }; const drawRobot = (drawContext: CanvasRenderingContext2D, color: Color) => { @@ -366,7 +380,6 @@ const pixelOriginOfPosition = (position: Position): PixelPosition => { .map-container { position: relative; - min-width: 200px; width: auto; height: auto; diff --git a/frontend/src/components/MapEditModal.vue b/frontend/src/components/MapEditModal.vue deleted file mode 100644 index d18fabc7..00000000 --- a/frontend/src/components/MapEditModal.vue +++ /dev/null @@ -1,87 +0,0 @@ - - - - - diff --git a/frontend/src/components/MapEditorAttributesBox.vue b/frontend/src/components/MapEditorAttributesBox.vue new file mode 100644 index 00000000..712016bf --- /dev/null +++ b/frontend/src/components/MapEditorAttributesBox.vue @@ -0,0 +1,122 @@ + + + + + diff --git a/frontend/src/components/MapEditorMeasureBox.vue b/frontend/src/components/MapEditorMeasureBox.vue new file mode 100644 index 00000000..ff804add --- /dev/null +++ b/frontend/src/components/MapEditorMeasureBox.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/frontend/src/components/MapListEntry.vue b/frontend/src/components/MapListEntry.vue index 997e5ed0..6050a27c 100644 --- a/frontend/src/components/MapListEntry.vue +++ b/frontend/src/components/MapListEntry.vue @@ -2,7 +2,7 @@
- +
Name
@@ -41,11 +37,11 @@
- +
- - diff --git a/frontend/src/components/MapPreviewModal.vue b/frontend/src/components/MapPreviewModal.vue deleted file mode 100644 index 5ef3e263..00000000 --- a/frontend/src/components/MapPreviewModal.vue +++ /dev/null @@ -1,172 +0,0 @@ - - - - - diff --git a/frontend/src/components/NavBar.vue b/frontend/src/components/NavBar.vue index 973d8c20..86238af1 100644 --- a/frontend/src/components/NavBar.vue +++ b/frontend/src/components/NavBar.vue @@ -40,11 +40,17 @@ API docs + +
+ +
+ Maps +
- Game Configuration + Administration
-
-
Map Editor
-
- During the preparing phase of every game one of the active maps is chosen randomly and the fuel value becomes the robots max fuel -
-
-
- -
-
-
- -
-
-
-
+ + diff --git a/frontend/src/views/MapEditorView.vue b/frontend/src/views/MapEditorView.vue new file mode 100644 index 00000000..b2293feb --- /dev/null +++ b/frontend/src/views/MapEditorView.vue @@ -0,0 +1,142 @@ + + + + +