Skip to content

Commit

Permalink
Merge pull request #178 from fmasa/remove-npcs
Browse files Browse the repository at this point in the history
Remove non-character NPCs
  • Loading branch information
fmasa authored Sep 6, 2023
2 parents dc841c1 + 60c2ca6 commit 2f0d00a
Show file tree
Hide file tree
Showing 27 changed files with 223 additions and 1,368 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,13 @@ import cz.frantisekmasa.wfrp_master.common.core.firebase.functions.CloudFunction
import cz.frantisekmasa.wfrp_master.common.core.firebase.repositories.FirestoreCharacterItemRepository
import cz.frantisekmasa.wfrp_master.common.core.firebase.repositories.FirestoreCharacterRepository
import cz.frantisekmasa.wfrp_master.common.core.firebase.repositories.FirestoreEncounterRepository
import cz.frantisekmasa.wfrp_master.common.core.firebase.repositories.FirestoreNpcRepository
import cz.frantisekmasa.wfrp_master.common.core.firebase.repositories.FirestorePartyRepository
import cz.frantisekmasa.wfrp_master.common.core.firebase.repositories.FirestoreSkillRepository
import cz.frantisekmasa.wfrp_master.common.core.serialization.UuidSerializer
import cz.frantisekmasa.wfrp_master.common.core.tips.DismissedUserTipsHolder
import cz.frantisekmasa.wfrp_master.common.encounters.EncounterDetailScreenModel
import cz.frantisekmasa.wfrp_master.common.encounters.EncountersScreenModel
import cz.frantisekmasa.wfrp_master.common.encounters.domain.EncounterRepository
import cz.frantisekmasa.wfrp_master.common.encounters.domain.NpcRepository
import cz.frantisekmasa.wfrp_master.common.gameMaster.GameMasterScreenModel
import cz.frantisekmasa.wfrp_master.common.invitation.InvitationScreenModel
import cz.frantisekmasa.wfrp_master.common.invitation.domain.FirestoreInvitationProcessor
Expand Down Expand Up @@ -157,7 +155,6 @@ val appModule = DI.Module("Common") {
}

bindSingleton<EncounterRepository> { FirestoreEncounterRepository(instance(), mapper()) }
bindSingleton<NpcRepository> { FirestoreNpcRepository(instance(), mapper()) }

bindSingleton<CharacterAvatarChanger> { CloudFunctionCharacterAvatarChanger(instance()) }

Expand Down Expand Up @@ -196,7 +193,7 @@ val appModule = DI.Module("Common") {
bindFactory { partyId: PartyId -> EncountersScreenModel(partyId, instance()) }
bindFactory { partyId: PartyId -> PartyScreenModel(partyId, instance()) }
bindFactory { encounterId: EncounterId ->
EncounterDetailScreenModel(encounterId, instance(), instance(), instance(), instance())
EncounterDetailScreenModel(encounterId, instance(), instance(), instance())
}
bindFactory { characterId: CharacterId ->
SkillsScreenModel(characterId, instance(), instance(), instance(), instance())
Expand Down Expand Up @@ -316,7 +313,6 @@ val appModule = DI.Module("Common") {
instance(),
instance(),
instance(),
instance(),
)
}
bindFactory { partyId: PartyId -> PartySettingsScreenModel(partyId, instance()) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ import cz.frantisekmasa.wfrp_master.common.core.domain.party.PartyId
import cz.frantisekmasa.wfrp_master.common.core.domain.party.combat.Advantage
import cz.frantisekmasa.wfrp_master.common.core.domain.party.combat.GroupAdvantage
import cz.frantisekmasa.wfrp_master.common.core.domain.party.settings.AdvantageSystem
import cz.frantisekmasa.wfrp_master.common.core.shared.Resources
import cz.frantisekmasa.wfrp_master.common.core.ui.CharacterAvatar
import cz.frantisekmasa.wfrp_master.common.core.ui.StatBlock
import cz.frantisekmasa.wfrp_master.common.core.ui.StatBlockData
Expand All @@ -81,7 +80,6 @@ import cz.frantisekmasa.wfrp_master.common.core.ui.scaffolding.OptionsAction
import cz.frantisekmasa.wfrp_master.common.core.ui.scaffolding.Subtitle
import cz.frantisekmasa.wfrp_master.common.encounters.CombatantItem
import cz.frantisekmasa.wfrp_master.common.encounters.domain.Wounds
import cz.frantisekmasa.wfrp_master.common.npcs.NpcDetailScreen
import dev.icerock.moko.resources.compose.stringResource
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -152,13 +150,10 @@ class ActiveCombatScreen(
isGroupAdvantageSystemEnabled = isGroupAdvantageSystemEnabled,
onDetailOpenRequest = {
navigation.navigate(
when (freshCombatant) {
is CombatantItem.Npc -> NpcDetailScreen(freshCombatant.npcId)
is CombatantItem.Character -> CharacterDetailScreen(
freshCombatant.characterId,
comingFromCombat = true,
)
}
CharacterDetailScreen(
freshCombatant.characterId,
comingFromCombat = true,
)
)
coroutineScope.launch { bottomSheetState.hide() }
},
Expand Down Expand Up @@ -279,7 +274,7 @@ class ActiveCombatScreen(

@Stable
private fun canEditCombatant(userId: UserId, isGameMaster: Boolean, combatant: CombatantItem) =
isGameMaster || (combatant is CombatantItem.Character && combatant.userId == userId)
isGameMaster || combatant.userId == userId

@Composable
private fun GroupAdvantageBar(
Expand Down Expand Up @@ -593,16 +588,7 @@ class ActiveCombatScreen(

Column {
ListItem(
icon = {
when (combatant) {
is CombatantItem.Character -> {
CharacterAvatar(combatant.avatarUrl, ItemIcon.Size.Small)
}
is CombatantItem.Npc -> {
ItemIcon(Resources.Drawable.Npc, ItemIcon.Size.Small)
}
}
},
icon = { CharacterAvatar(combatant.avatarUrl, ItemIcon.Size.Small) },
text = {
Column {
Text(combatant.name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import cz.frantisekmasa.wfrp_master.common.core.domain.character.CharacterType
import cz.frantisekmasa.wfrp_master.common.core.domain.character.CurrentConditions
import cz.frantisekmasa.wfrp_master.common.core.domain.identifiers.CharacterId
import cz.frantisekmasa.wfrp_master.common.core.domain.identifiers.EncounterId
import cz.frantisekmasa.wfrp_master.common.core.domain.identifiers.NpcId
import cz.frantisekmasa.wfrp_master.common.core.domain.party.Party
import cz.frantisekmasa.wfrp_master.common.core.domain.party.PartyId
import cz.frantisekmasa.wfrp_master.common.core.domain.party.PartyRepository
Expand All @@ -33,28 +32,23 @@ import cz.frantisekmasa.wfrp_master.common.core.logging.Reporter
import cz.frantisekmasa.wfrp_master.common.core.ui.StatBlockData
import cz.frantisekmasa.wfrp_master.common.core.utils.right
import cz.frantisekmasa.wfrp_master.common.encounters.CombatantItem
import cz.frantisekmasa.wfrp_master.common.encounters.domain.Npc
import cz.frantisekmasa.wfrp_master.common.encounters.domain.NpcRepository
import cz.frantisekmasa.wfrp_master.common.encounters.domain.Wounds
import io.github.aakira.napier.Napier
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.transform
import kotlin.random.Random

class CombatScreenModel(
private val partyId: PartyId,
private val random: Random,
private val parties: PartyRepository,
private val npcs: NpcRepository,
private val characters: CharacterRepository,
private val skills: SkillRepository,
private val talents: TalentRepository,
Expand All @@ -70,10 +64,6 @@ class CombatScreenModel(
.mapLatest { it.activeCombat }
.filterNotNull()

private val activeEncounterId: Flow<EncounterId> = combatFlow
.mapLatest { EncounterId(partyId, it.encounterId) }
.distinctUntilChanged()

val turn: Flow<Int> = combatFlow
.mapLatest { it.getTurn() }
.distinctUntilChanged()
Expand All @@ -86,28 +76,13 @@ class CombatScreenModel(
.mapLatest { it.groupAdvantage }
.distinctUntilChanged()

suspend fun loadNpcsFromEncounter(encounterId: Uuid): List<Npc> =
npcs.findByEncounter(EncounterId(partyId, encounterId)).first()

suspend fun loadCharacters(): List<Character> =
characters.inParty(partyId, CharacterType.PLAYER_CHARACTER).first()

suspend fun loadNpcs(): List<Character> =
characters.inParty(partyId, CharacterType.NPC).first()

suspend fun getStatBlockData(combatant: CombatantItem): StatBlockData {
if (combatant !is CombatantItem.Character) {
return StatBlockData(
"",
emptyList(),
emptyList(),
emptyList(),
emptyList(),
emptyList(),
emptyList(),
)
}

val characterId = combatant.characterId

return coroutineScope {
Expand All @@ -133,39 +108,31 @@ class CombatScreenModel(
suspend fun startCombat(
encounterId: Uuid,
characters: List<Character>,
npcs: List<Npc>,
npcCharacters: Map<Character, Int>,
) {
val globalEncounterId = EncounterId(partyId, encounterId)
val combatants =
characters.map {
characteristics(it) to Combatant.Character(
characteristics(it) to Combatant(
id = uuid4(),
characterId = it.id,
initiative = 1,
)
} +
npcs.map {
it.stats to Combatant.Npc(
id = uuid4(),
npcId = NpcId(globalEncounterId, it.id),
initiative = 1,
)
} +
npcCharacters.flatMap { (character, count) ->
val characteristics = characteristics(character)

if (count == 1)
listOf(
characteristics to Combatant.Character(
characteristics to Combatant(
id = uuid4(),
name = character.publicName,
characterId = character.id,
initiative = 1,
)
)
else (1..count).map { index ->
characteristics to Combatant.Character(
characteristics to Combatant(
id = uuid4(),
characterId = character.id,
initiative = 1,
Expand Down Expand Up @@ -205,8 +172,6 @@ class CombatScreenModel(
}

fun combatants(): Flow<List<CombatantItem>> {
val npcsFlow = activeEncounterId.transform { emitAll(npcs.findByEncounter(it)) }

val charactersFlow = characters
.inParty(partyId, CharacterType.values().toSet())
.distinctUntilChanged()
Expand All @@ -215,36 +180,18 @@ class CombatScreenModel(
.mapNotNull { it.activeCombat?.getCombatants() }
.distinctUntilChanged()

return combineFlows(
combatantsFlow,
npcsFlow,
charactersFlow
) { combatants, npcs, characters ->
val npcsById = npcs.associateBy { it.id }
return combatantsFlow.combine(charactersFlow) { combatants, characters ->
val charactersById = characters.associateBy { it.id }

combatants
.map { combatant ->
when (combatant) {
is Combatant.Character -> {
val character = charactersById[combatant.characterId] ?: return@map null

CombatantItem.Character(
characterId = CharacterId(partyId, character.id),
character = character,
combatant = combatant,
)
}
is Combatant.Npc -> {
val npc = npcsById[combatant.npcId.npcId] ?: return@map null

CombatantItem.Npc(
npcId = combatant.npcId,
npc = npc,
combatant = combatant,
)
}
}
val character = charactersById[combatant.characterId] ?: return@map null

CombatantItem(
characterId = CharacterId(partyId, character.id),
character = character,
combatant = combatant,
)
}.filterNotNull()
}
}
Expand Down Expand Up @@ -282,43 +229,21 @@ class CombatScreenModel(
else party.updateCombat(updatedCombat)
}

private fun <T1, T2, T3, R> combineFlows(
first: Flow<T1>,
second: Flow<T2>,
third: Flow<T3>,
transform: suspend (T1, T2, T3) -> R,
): Flow<R> =
first.combine(second) { a, b -> Pair(a, b) }
.combine(third) { (a, b), c -> transform(a, b, c) }

suspend fun updateWounds(combatant: CombatantItem, wounds: Wounds) {
if (combatant.combatant.wounds != null) {
// Wounds are combatant specific (there may be multiple combatants of same character)
updateCombat { it.updateCombatant(combatant.combatant.withWounds(wounds)) }
return
}

when (combatant) {
is CombatantItem.Character -> {
val character = characters.get(combatant.characterId)
val points = character.points

if (points.wounds == wounds.current) {
return
}

characters.save(partyId, character.updatePoints(points.copy(wounds = wounds.current)))
}
is CombatantItem.Npc -> {
val npc = npcs.get(combatant.npcId)

if (npc.wounds == wounds) {
return
}
val character = characters.get(combatant.characterId)
val points = character.points

npcs.save(combatant.npcId.encounterId, npc.updateCurrentWounds(wounds.current))
}
if (points.wounds == wounds.current) {
return
}

characters.save(partyId, character.updatePoints(points.copy(wounds = wounds.current)))
}

suspend fun updateConditions(combatant: CombatantItem, conditions: CurrentConditions) {
Expand All @@ -328,20 +253,13 @@ class CombatScreenModel(
return
}

when (combatant) {
is CombatantItem.Character -> {
val character = characters.get(combatant.characterId)

if (character.conditions == conditions) {
return
}
val character = characters.get(combatant.characterId)

characters.save(partyId, character.updateConditions(conditions))
}
is CombatantItem.Npc -> {
// NPC do not have conditions, so this must have been handled as combatant specific
}
if (character.conditions == conditions) {
return
}

characters.save(partyId, character.updateConditions(conditions))
}

suspend fun updateAdvantage(combatant: Combatant, advantage: Advantage) {
Expand Down
Loading

0 comments on commit 2f0d00a

Please sign in to comment.