From e171a56ca94394bce41c9f0beb4d24ad190c6d06 Mon Sep 17 00:00:00 2001 From: TheSaminator Date: Thu, 7 Jul 2022 15:03:51 -0400 Subject: [PATCH] Rework campaign map storage --- .../net/starshipfights/campaign/admirals.kt | 4 +- .../starshipfights/campaign/fleet_presence.kt | 44 ++++++- .../net/starshipfights/game/client_mode.kt | 2 +- .../starshipfights/campaign/campaign_main.kt | 2 +- .../net/starshipfights/game/game_render.kt | 4 +- .../starshipfights/campaign/cluster_fleets.kt | 10 +- .../starshipfights/data/admiralty/admirals.kt | 26 ++-- .../data/space/star_clusters.kt | 112 ++++++++++++++---- 8 files changed, 157 insertions(+), 47 deletions(-) diff --git a/src/commonMain/kotlin/net/starshipfights/campaign/admirals.kt b/src/commonMain/kotlin/net/starshipfights/campaign/admirals.kt index b59810a..f154ee8 100644 --- a/src/commonMain/kotlin/net/starshipfights/campaign/admirals.kt +++ b/src/commonMain/kotlin/net/starshipfights/campaign/admirals.kt @@ -6,15 +6,13 @@ import net.starshipfights.game.InGameAdmiral enum class CampaignAdmiralStatus { HOST, MEMBER, - INVITED, - REQUESTED; + INVITED; val displayName: String get() = when (this) { HOST -> "Campaign Host" MEMBER -> "Campaign Player" INVITED -> "Invited to Campaign" - REQUESTED -> "Requesting to Join" } } diff --git a/src/commonMain/kotlin/net/starshipfights/campaign/fleet_presence.kt b/src/commonMain/kotlin/net/starshipfights/campaign/fleet_presence.kt index da47bc7..c80d2e4 100644 --- a/src/commonMain/kotlin/net/starshipfights/campaign/fleet_presence.kt +++ b/src/commonMain/kotlin/net/starshipfights/campaign/fleet_presence.kt @@ -72,16 +72,50 @@ val FactionFlavor.shipSource: Faction @Serializable data class FleetPresence( val name: String, - val owner: FactionFlavor, val ships: Map, Ship>, - val admiralName: String, - val admiralIsFemale: Boolean, - val admiralRank: AdmiralRank, + val admiral: FleetPresenceAdmiral ) { + val owner: FactionFlavor + get() = admiral.faction + val pointValue: Int get() = ships.values.sumOf { it.pointCost } val admiralFullName: String - get() = "${admiralRank.getDisplayName(owner)} $admiralName" + get() = admiral.fullName +} + +@Serializable +sealed class FleetPresenceAdmiral { + abstract val name: String + abstract val isFemale: Boolean + abstract val faction: FactionFlavor + abstract val rank: AdmiralRank + + val fullName: String + get() = "${rank.getDisplayName(faction)} $name" + + @Serializable + data class NPC( + override val name: String, + override val isFemale: Boolean, + override val faction: FactionFlavor, + override val rank: AdmiralRank, + ) : FleetPresenceAdmiral() + + @Serializable + data class Player(val admiral: CampaignAdmiral) : FleetPresenceAdmiral() { + override val name: String + get() = admiral.admiral.name + + override val isFemale: Boolean + get() = admiral.admiral.isFemale + + override val faction: FactionFlavor + get() = FactionFlavor.defaultForFaction(admiral.admiral.faction) + + override val rank: AdmiralRank + get() = admiral.admiral.rank + } } diff --git a/src/commonMain/kotlin/net/starshipfights/game/client_mode.kt b/src/commonMain/kotlin/net/starshipfights/game/client_mode.kt index 70c9d67..f2fc00e 100644 --- a/src/commonMain/kotlin/net/starshipfights/game/client_mode.kt +++ b/src/commonMain/kotlin/net/starshipfights/game/client_mode.kt @@ -9,7 +9,7 @@ import net.starshipfights.data.Id sealed class ClientMode { @Serializable data class CampaignMap( - val playingAs: Id, + val playingAs: Id?, val admirals: Map, CampaignAdmiral>, val clusterToken: Id, val clusterView: StarClusterView diff --git a/src/jsMain/kotlin/net/starshipfights/campaign/campaign_main.kt b/src/jsMain/kotlin/net/starshipfights/campaign/campaign_main.kt index 85730c0..15d8467 100644 --- a/src/jsMain/kotlin/net/starshipfights/campaign/campaign_main.kt +++ b/src/jsMain/kotlin/net/starshipfights/campaign/campaign_main.kt @@ -15,7 +15,7 @@ import net.starshipfights.game.* var mySide: CampaignAdmiral? = null -suspend fun campaignMain(playingAs: Id, admirals: Map, CampaignAdmiral>, clusterToken: Id, clusterView: StarClusterView) { +suspend fun campaignMain(playingAs: Id?, admirals: Map, CampaignAdmiral>, clusterToken: Id, clusterView: StarClusterView) { Popup.LoadingScreen("Loading resources...") { CampaignResources.load() }.display() diff --git a/src/jsMain/kotlin/net/starshipfights/game/game_render.kt b/src/jsMain/kotlin/net/starshipfights/game/game_render.kt index 306ebab..034c3ba 100644 --- a/src/jsMain/kotlin/net/starshipfights/game/game_render.kt +++ b/src/jsMain/kotlin/net/starshipfights/game/game_render.kt @@ -4,7 +4,7 @@ import externals.threejs.* import net.starshipfights.data.Id object GameRender { - private val shipMeshCache = mutableMapOf, Mesh>() + private val shipMeshCache = mutableMapOf, Object3D>() fun renderGameState(scene: Scene, state: GameState) { scene.background = RenderResources.spaceboxes.getValue(state.battleInfo.bg) @@ -22,7 +22,7 @@ object GameRender { ShipRenderMode.FULL -> shipGroup.add(shipMeshCache[ship.id]?.also { render -> RenderScaling.toWorldRotation(ship.position.facing, render) render.position.copy(RenderScaling.toWorldPosition(ship.position.location)) - } ?: RenderResources.shipMesh.generate(ship)) + } ?: RenderResources.shipMesh.generate(ship).also { shipMeshCache[ship.id] = it }) } } } diff --git a/src/jvmMain/kotlin/net/starshipfights/campaign/cluster_fleets.kt b/src/jvmMain/kotlin/net/starshipfights/campaign/cluster_fleets.kt index 156341e..b9b7c76 100644 --- a/src/jvmMain/kotlin/net/starshipfights/campaign/cluster_fleets.kt +++ b/src/jvmMain/kotlin/net/starshipfights/campaign/cluster_fleets.kt @@ -44,10 +44,12 @@ fun generateFleetPresences(owner: FactionFlavor, maxFleets: Int, sizeMult: Doubl Id() to FleetPresence( name = owner.generateFleetName(), - owner = owner, ships = admiralFleet, - admiralName = AdmiralNames.randomName(AdmiralNameFlavor.forFactionFlavor(owner).random(), admiralIsFemale), - admiralIsFemale = admiralIsFemale, - admiralRank = admiralRank + admiral = FleetPresenceAdmiral.NPC( + name = AdmiralNames.randomName(AdmiralNameFlavor.forFactionFlavor(owner).random(), admiralIsFemale), + isFemale = admiralIsFemale, + faction = owner, + rank = admiralRank + ) ) } diff --git a/src/jvmMain/kotlin/net/starshipfights/data/admiralty/admirals.kt b/src/jvmMain/kotlin/net/starshipfights/data/admiralty/admirals.kt index 19dfc90..07963a5 100644 --- a/src/jvmMain/kotlin/net/starshipfights/data/admiralty/admirals.kt +++ b/src/jvmMain/kotlin/net/starshipfights/data/admiralty/admirals.kt @@ -32,6 +32,8 @@ data class Admiral( val money: Int, val inCluster: Id? = null, + val invitedToClusters: Set> = emptySet(), + val requestedClusters: Set> = emptySet(), ) : DataDocument { val rank: AdmiralRank get() = AdmiralRank.fromAcumen(acumen) @@ -42,6 +44,8 @@ data class Admiral( companion object Table : DocumentTable by DocumentTable.create({ index(Admiral::owningUser) index(Admiral::inCluster) + index(Admiral::invitedToClusters) + index(Admiral::requestedClusters) }) } @@ -122,17 +126,19 @@ suspend fun getAllInGameAdmirals(user: User) = Admiral.filter(Admiral::owningUse ) }.toList() +suspend fun getInGameAdmiral(admiral: Admiral) = User.get(admiral.owningUser)?.let { user -> + InGameAdmiral( + admiral.id.reinterpret(), + InGameUser(user.id.reinterpret(), user.profileName), + admiral.name, + admiral.isFemale, + admiral.faction, + admiral.rank + ) +} + suspend fun getInGameAdmiral(admiralId: Id) = Admiral.get(admiralId.reinterpret())?.let { admiral -> - User.get(admiral.owningUser)?.let { user -> - InGameAdmiral( - admiralId, - InGameUser(user.id.reinterpret(), user.profileName), - admiral.name, - admiral.isFemale, - admiral.faction, - admiral.rank - ) - } + getInGameAdmiral(admiral) } suspend fun getAdmiralsShips(admiralId: Id): Map, Ship> { diff --git a/src/jvmMain/kotlin/net/starshipfights/data/space/star_clusters.kt b/src/jvmMain/kotlin/net/starshipfights/data/space/star_clusters.kt index f9bd290..25976aa 100644 --- a/src/jvmMain/kotlin/net/starshipfights/data/space/star_clusters.kt +++ b/src/jvmMain/kotlin/net/starshipfights/data/space/star_clusters.kt @@ -3,6 +3,7 @@ package net.starshipfights.data.space import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.toList import kotlinx.coroutines.launch import kotlinx.serialization.Serializable @@ -11,15 +12,19 @@ import net.starshipfights.data.DataDocument import net.starshipfights.data.DocumentTable import net.starshipfights.data.Id import net.starshipfights.data.admiralty.Admiral +import net.starshipfights.data.admiralty.ShipInDrydock +import net.starshipfights.data.admiralty.getInGameAdmiral import net.starshipfights.data.invoke import net.starshipfights.game.FactionFlavor import net.starshipfights.game.Position import net.starshipfights.game.Ship import org.litote.kmongo.eq +import org.litote.kmongo.setValue @Serializable data class StarCluster( override val id: Id, + val host: Id, val background: StarClusterBackground, val lanes: Set @@ -60,13 +65,80 @@ data class ClusterFleetPresence( override val id: Id, val starSystemId: Id, - val fleetPresence: FleetPresence + val fleetPresence: FleetPresenceData ) : DataDocument { companion object Table : DocumentTable by DocumentTable.create({ index(ClusterFleetPresence::starSystemId) }) } +@Serializable +sealed class FleetPresenceData { + @Serializable + data class NPC( + val name: String, + val ships: Map, Ship>, + val admiral: FleetPresenceAdmiral.NPC + ) : FleetPresenceData() + + @Serializable + data class Player( + val admiralId: Id + ) : FleetPresenceData() +} + +suspend fun getCampaignStatus(admiral: Admiral, clusterId: Id): CampaignAdmiralStatus? { + val cluster = StarCluster.get(clusterId) ?: return null + + admiral.inCluster?.let { inCluster -> + if (inCluster == clusterId) { + return if (cluster.host == admiral.id) + CampaignAdmiralStatus.HOST + else + CampaignAdmiralStatus.MEMBER + } else if (cluster.host == admiral.id) { + Admiral.set(admiral.id, setValue(Admiral::inCluster, clusterId)) + return CampaignAdmiralStatus.HOST + } + } + + return if (clusterId in admiral.invitedToClusters) + CampaignAdmiralStatus.INVITED + else null +} + +suspend fun FleetPresenceData.resolve(inCluster: Id): FleetPresence? { + return when (this) { + is FleetPresenceData.NPC -> FleetPresence( + name = name, + ships = ships, + admiral = admiral + ) + is FleetPresenceData.Player -> { + val (admiral, ships) = coroutineScope { + val admiralAsync = async { Admiral.get(admiralId) } + val shipsAsync = async { ShipInDrydock.filter(ShipInDrydock::owningAdmiral eq admiralId).toList() } + + admiralAsync.await() to shipsAsync.await() + } + + admiral ?: return null + val inGameAdmiral = getInGameAdmiral(admiral) ?: return null + val campaignStatus = getCampaignStatus(admiral, inCluster) ?: return null + + FleetPresence( + name = "Fleet of ${admiral.fullName}", + ships = ships.associate { inDrydock -> + inDrydock.id.reinterpret() to inDrydock.shipData + }, + admiral = FleetPresenceAdmiral.Player( + CampaignAdmiral(inGameAdmiral, campaignStatus) + ) + ) + } + } +} + suspend fun deleteCluster(clusterId: Id) { coroutineScope { launch { StarCluster.del(clusterId) } @@ -86,9 +158,10 @@ suspend fun deleteCluster(clusterId: Id) { } } -suspend fun createCluster(clusterView: StarClusterView): Id { +suspend fun createCluster(clusterView: StarClusterView, forAdmiral: Id): Id { val cluster = StarCluster( id = Id(), + host = forAdmiral, background = clusterView.background, lanes = clusterView.lanes ) @@ -124,7 +197,16 @@ suspend fun createCluster(clusterView: StarClusterView): Id { ClusterFleetPresence( id = fleetId.reinterpret(), starSystemId = clusterSystem.id, - fleetPresence = fleet + fleetPresence = when (val admiral = fleet.admiral) { + is FleetPresenceAdmiral.NPC -> FleetPresenceData.NPC( + name = fleet.name, + ships = fleet.ships, + admiral = admiral + ) + is FleetPresenceAdmiral.Player -> FleetPresenceData.Player( + admiral.admiral.admiral.id.reinterpret() + ) + } ) } ) @@ -150,11 +232,11 @@ suspend fun viewCluster(clusterId: Id): StarClusterView? { } } val fleetsAsync = async { - val fleets = ClusterFleetPresence.filter(ClusterFleetPresence::starSystemId eq cSystem.id).toList() - - fleets.associate { cFleet -> - cFleet.id.reinterpret() to cFleet.fleetPresence - } + ClusterFleetPresence.filter(ClusterFleetPresence::starSystemId eq cSystem.id).map { fleet -> + async { + fleet.fleetPresence.resolve(clusterId)?.let { fleet.id.reinterpret() to it } + } + }.mapNotNull { it.await() }.toList().toMap() } cSystem.id.reinterpret() to StarSystem( @@ -166,7 +248,7 @@ suspend fun viewCluster(clusterId: Id): StarClusterView? { bodiesAsync.await() ) } - }.toList().associate { it.await() } + }.map { it.await() }.toList().toMap() } val cluster = clusterAsync.await() ?: return@coroutineScope null @@ -179,15 +261,3 @@ suspend fun viewCluster(clusterId: Id): StarClusterView? { ) } } - -suspend fun deployableFleet(systemId: Id, admiral: Admiral): Map, Ship> { - return ClusterFleetPresence.filter(ClusterFleetPresence::starSystemId eq systemId) - .toList() - .filter { admiral.faction in it.fleetPresence.owner.loyalties } - .flatMap { - it.fleetPresence.ships.toList().filter { (_, ship) -> - ship.shipType.weightClass.tier <= admiral.rank.maxShipTier - } - } - .toMap() -} -- 2.25.1