Rework campaign map storage
authorTheSaminator <thesaminator@users.noreply.github.com>
Thu, 7 Jul 2022 19:03:51 +0000 (15:03 -0400)
committerTheSaminator <thesaminator@users.noreply.github.com>
Thu, 7 Jul 2022 19:03:51 +0000 (15:03 -0400)
src/commonMain/kotlin/net/starshipfights/campaign/admirals.kt
src/commonMain/kotlin/net/starshipfights/campaign/fleet_presence.kt
src/commonMain/kotlin/net/starshipfights/game/client_mode.kt
src/jsMain/kotlin/net/starshipfights/campaign/campaign_main.kt
src/jsMain/kotlin/net/starshipfights/game/game_render.kt
src/jvmMain/kotlin/net/starshipfights/campaign/cluster_fleets.kt
src/jvmMain/kotlin/net/starshipfights/data/admiralty/admirals.kt
src/jvmMain/kotlin/net/starshipfights/data/space/star_clusters.kt

index b59810a57700ac4e1748ae4144f33790e43a4f50..f154ee89d1a5bacbe100664f602b707bffceac3e 100644 (file)
@@ -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"
                }
 }
 
index da47bc7b81962961e953094e0e8a165891366e13..c80d2e4348e568079e21c842581741817f2bf78d 100644 (file)
@@ -72,16 +72,50 @@ val FactionFlavor.shipSource: Faction
 @Serializable
 data class FleetPresence(
        val name: String,
-       val owner: FactionFlavor,
        val ships: Map<Id<Ship>, 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
+       }
 }
index 70c9d677d7801ce128c9a21d042259b53de21c0a..f2fc00ee5ee8fafe978cd633ef48a53dd521a22d 100644 (file)
@@ -9,7 +9,7 @@ import net.starshipfights.data.Id
 sealed class ClientMode {
        @Serializable
        data class CampaignMap(
-               val playingAs: Id<InGameAdmiral>,
+               val playingAs: Id<InGameAdmiral>?,
                val admirals: Map<Id<InGameAdmiral>, CampaignAdmiral>,
                val clusterToken: Id<StarClusterView>,
                val clusterView: StarClusterView
index 85730c0ef1558c8251906322f5310984602fca35..15d8467c6dabd30f54c8d95410c3b447a08ba4c4 100644 (file)
@@ -15,7 +15,7 @@ import net.starshipfights.game.*
 
 var mySide: CampaignAdmiral? = null
 
-suspend fun campaignMain(playingAs: Id<InGameAdmiral>, admirals: Map<Id<InGameAdmiral>, CampaignAdmiral>, clusterToken: Id<StarClusterView>, clusterView: StarClusterView) {
+suspend fun campaignMain(playingAs: Id<InGameAdmiral>?, admirals: Map<Id<InGameAdmiral>, CampaignAdmiral>, clusterToken: Id<StarClusterView>, clusterView: StarClusterView) {
        Popup.LoadingScreen("Loading resources...") {
                CampaignResources.load()
        }.display()
index 306ebab84b82e459b3ec140023850693f4abe731..034c3ba446a3ab8786307cef01d7cc0565249f7b 100644 (file)
@@ -4,7 +4,7 @@ import externals.threejs.*
 import net.starshipfights.data.Id
 
 object GameRender {
-       private val shipMeshCache = mutableMapOf<Id<ShipInstance>, Mesh>()
+       private val shipMeshCache = mutableMapOf<Id<ShipInstance>, 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 })
                        }
                }
        }
index 156341e16d39ce2d672be971d7ccbbbfeb18712e..b9b7c76b8403904e7a94f4f91d4c5e30e6e0c02b 100644 (file)
@@ -44,10 +44,12 @@ fun generateFleetPresences(owner: FactionFlavor, maxFleets: Int, sizeMult: Doubl
        
        Id<FleetPresence>() 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
+               )
        )
 }
index 19dfc90f6b30136dec0db65ce92a54360ef5ddc0..07963a520b37e41d4cf4ec950b1241aec66d8111 100644 (file)
@@ -32,6 +32,8 @@ data class Admiral(
        val money: Int,
        
        val inCluster: Id<StarCluster>? = null,
+       val invitedToClusters: Set<Id<StarCluster>> = emptySet(),
+       val requestedClusters: Set<Id<StarCluster>> = emptySet(),
 ) : DataDocument<Admiral> {
        val rank: AdmiralRank
                get() = AdmiralRank.fromAcumen(acumen)
@@ -42,6 +44,8 @@ data class Admiral(
        companion object Table : DocumentTable<Admiral> 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<InGameAdmiral>) = 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<Admiral>): Map<Id<Ship>, Ship> {
index f9bd2906be7027a728a451bf3eacf6e50f98df12..25976aa1a88441bef87637c88182dca574dbd685 100644 (file)
@@ -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<StarCluster>,
+       val host: Id<Admiral>,
        
        val background: StarClusterBackground,
        val lanes: Set<WarpLane>
@@ -60,13 +65,80 @@ data class ClusterFleetPresence(
        override val id: Id<ClusterFleetPresence>,
        val starSystemId: Id<ClusterStarSystem>,
        
-       val fleetPresence: FleetPresence
+       val fleetPresence: FleetPresenceData
 ) : DataDocument<ClusterFleetPresence> {
        companion object Table : DocumentTable<ClusterFleetPresence> by DocumentTable.create({
                index(ClusterFleetPresence::starSystemId)
        })
 }
 
+@Serializable
+sealed class FleetPresenceData {
+       @Serializable
+       data class NPC(
+               val name: String,
+               val ships: Map<Id<Ship>, Ship>,
+               val admiral: FleetPresenceAdmiral.NPC
+       ) : FleetPresenceData()
+       
+       @Serializable
+       data class Player(
+               val admiralId: Id<Admiral>
+       ) : FleetPresenceData()
+}
+
+suspend fun getCampaignStatus(admiral: Admiral, clusterId: Id<StarCluster>): 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<StarCluster>): 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<Ship>() to inDrydock.shipData
+                               },
+                               admiral = FleetPresenceAdmiral.Player(
+                                       CampaignAdmiral(inGameAdmiral, campaignStatus)
+                               )
+                       )
+               }
+       }
+}
+
 suspend fun deleteCluster(clusterId: Id<StarCluster>) {
        coroutineScope {
                launch { StarCluster.del(clusterId) }
@@ -86,9 +158,10 @@ suspend fun deleteCluster(clusterId: Id<StarCluster>) {
        }
 }
 
-suspend fun createCluster(clusterView: StarClusterView): Id<StarCluster> {
+suspend fun createCluster(clusterView: StarClusterView, forAdmiral: Id<Admiral>): Id<StarCluster> {
        val cluster = StarCluster(
                id = Id(),
+               host = forAdmiral,
                background = clusterView.background,
                lanes = clusterView.lanes
        )
@@ -124,7 +197,16 @@ suspend fun createCluster(clusterView: StarClusterView): Id<StarCluster> {
                                                        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<StarCluster>): StarClusterView? {
                                                }
                                        }
                                        val fleetsAsync = async {
-                                               val fleets = ClusterFleetPresence.filter(ClusterFleetPresence::starSystemId eq cSystem.id).toList()
-                                               
-                                               fleets.associate { cFleet ->
-                                                       cFleet.id.reinterpret<FleetPresence>() to cFleet.fleetPresence
-                                               }
+                                               ClusterFleetPresence.filter(ClusterFleetPresence::starSystemId eq cSystem.id).map { fleet ->
+                                                       async {
+                                                               fleet.fleetPresence.resolve(clusterId)?.let { fleet.id.reinterpret<FleetPresence>() to it }
+                                                       }
+                                               }.mapNotNull { it.await() }.toList().toMap()
                                        }
                                        
                                        cSystem.id.reinterpret<StarSystem>() to StarSystem(
@@ -166,7 +248,7 @@ suspend fun viewCluster(clusterId: Id<StarCluster>): 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<StarCluster>): StarClusterView? {
                )
        }
 }
-
-suspend fun deployableFleet(systemId: Id<ClusterStarSystem>, admiral: Admiral): Map<Id<Ship>, 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()
-}