diff --git a/android/assets/jsons/translations/template.properties b/android/assets/jsons/translations/template.properties index 75a1fd1ba3..17fdf8e153 100644 --- a/android/assets/jsons/translations/template.properties +++ b/android/assets/jsons/translations/template.properties @@ -788,6 +788,7 @@ You need to restart the game for this change to take effect. = # Notifications Research of [technologyName] has completed! = +We gained [amount] Science from Research Agreement = [construction] has become obsolete and was removed from the queue in [cityName]! = [construction] has become obsolete and was removed from the queue in [amount] cities! = [cityName] changed production from [oldUnit] to [newUnit] = diff --git a/core/src/com/unciv/logic/civilization/managers/TechManager.kt b/core/src/com/unciv/logic/civilization/managers/TechManager.kt index 66341c998d..a3b0865be5 100644 --- a/core/src/com/unciv/logic/civilization/managers/TechManager.kt +++ b/core/src/com/unciv/logic/civilization/managers/TechManager.kt @@ -90,6 +90,19 @@ class TechManager : IsPartOfGameInfoSerialization { fun getNumberOfTechsResearched(): Int = techsResearched.size + fun getOverflowScience(techName: String): Int { + return if (overflowScience == 0) 0 + else (getScienceModifier(techName) * overflowScience).toInt() + } + + private fun getScienceModifier(techName: String): Float { // https://forums.civfanatics.com/threads/the-mechanics-of-overflow-inflation.517970/ + val techsResearchedKnownCivs = civInfo.getKnownCivs() + .count { it.isMajorCiv() && it.tech.isResearched(techName) } + val undefeatedCivs = civInfo.gameInfo.civilizations + .count { it.isMajorCiv() && !it.isDefeated() } + return 1 + techsResearchedKnownCivs / undefeatedCivs.toFloat() * 0.3f + } + private fun getRuleset() = civInfo.gameInfo.ruleset fun costOfTech(techName: String): Int { @@ -97,12 +110,7 @@ class TechManager : IsPartOfGameInfoSerialization { if (civInfo.isHuman()) techCost *= civInfo.getDifficulty().researchCostModifier techCost *= civInfo.gameInfo.speed.scienceCostModifier - val techsResearchedKnownCivs = civInfo.getKnownCivs() - .count { it.isMajorCiv() && it.tech.isResearched(techName) } - val undefeatedCivs = civInfo.gameInfo.civilizations - .count { it.isMajorCiv() && !it.isDefeated() } - // https://forums.civfanatics.com/threads/the-mechanics-of-overflow-inflation.517970/ - techCost /= 1 + techsResearchedKnownCivs / undefeatedCivs.toFloat() * 0.3f + techCost /= getScienceModifier(techName) // https://civilization.fandom.com/wiki/Map_(Civ5) val worldSizeModifier = with (civInfo.gameInfo.tileMap.mapParameters.mapSize) { when { @@ -129,11 +137,18 @@ class TechManager : IsPartOfGameInfoSerialization { fun researchOfTech(TechName: String?) = techsInProgress[TechName] ?: 0 // Was once duplicated as fun scienceSpentOnTech(tech: String): Int - fun remainingScienceToTech(techName: String) = costOfTech(techName) - researchOfTech(techName) + fun remainingScienceToTech(techName: String): Int { + val spareScience = if (canBeResearched(techName)) getOverflowScience(techName) else 0 + return costOfTech(techName) - researchOfTech(techName) - spareScience + } - fun turnsToTech(techName: String) = when { - civInfo.stats.statsForNextTurn.science <= 0f -> "∞" - else -> max(1, ceil(remainingScienceToTech(techName).toDouble() / civInfo.stats.statsForNextTurn.science).toInt()).toString() + fun turnsToTech(techName: String): String { + val remainingCost = remainingScienceToTech(techName).toDouble() + return when { + remainingCost <= 0f -> "0" + civInfo.stats.statsForNextTurn.science <= 0f -> "∞" + else -> max(1, ceil(remainingCost / civInfo.stats.statsForNextTurn.science).toInt()).toString() + } } fun isResearched(techName: String): Boolean = techsResearched.contains(techName) @@ -213,15 +228,15 @@ class TechManager : IsPartOfGameInfoSerialization { var finalScienceToAdd = scienceForNewTurn if (scienceFromResearchAgreements != 0) { - finalScienceToAdd += scienceFromResearchAgreements() + val scienceBoost = scienceFromResearchAgreements() + finalScienceToAdd += scienceBoost scienceFromResearchAgreements = 0 + civInfo.addNotification("We gained [$scienceBoost] Science from Research Agreement", + NotificationCategory.General, + NotificationIcon.Science) } - if (overflowScience != 0) { // https://forums.civfanatics.com/threads/the-mechanics-of-overflow-inflation.517970/ - val techsResearchedKnownCivs = civInfo.getKnownCivs() - .count { it.isMajorCiv() && it.tech.isResearched(currentTechnologyName()!!) } - val undefeatedCivs = civInfo.gameInfo.civilizations.count { it.isMajorCiv() && !it.isDefeated() } - val finalScienceFromOverflow = ((1 + techsResearchedKnownCivs / undefeatedCivs.toFloat() * 0.3f) * overflowScience).toInt() - finalScienceToAdd += finalScienceFromOverflow + if (overflowScience != 0) { + finalScienceToAdd += getOverflowScience(currentTechnologyName()!!) overflowScience = 0 } @@ -241,6 +256,20 @@ class TechManager : IsPartOfGameInfoSerialization { addTechnology(currentTechnology) } + /** + * Checks whether the research on the current technology can be completed + * and, if so, completes the research. + */ + fun updateResearchProgress() { + val currentTechnology = currentTechnologyName() ?: return + val realOverflow = getOverflowScience(currentTechnology) + val scienceSpent = researchOfTech(currentTechnology) + realOverflow + if (scienceSpent >= costOfTech(currentTechnology)) { + overflowScience = 0 + addScience(realOverflow) + } + } + fun getFreeTechnology(techName: String) { freeTechs-- addTechnology(techName) @@ -301,6 +330,7 @@ class TechManager : IsPartOfGameInfoSerialization { } moveToNewEra() + updateResearchProgress() } private fun obsoleteOldUnits(techName: String) { diff --git a/core/src/com/unciv/logic/civilization/managers/TurnManager.kt b/core/src/com/unciv/logic/civilization/managers/TurnManager.kt index 08044fd8c0..95c62556e1 100644 --- a/core/src/com/unciv/logic/civilization/managers/TurnManager.kt +++ b/core/src/com/unciv/logic/civilization/managers/TurnManager.kt @@ -30,6 +30,9 @@ class TurnManager(val civInfo: Civilization) { civInfo.statsHistory.recordRankingStats(civInfo) } + if (civInfo.cities.isNotEmpty() && civInfo.gameInfo.ruleset.technologies.isNotEmpty()) + civInfo.tech.updateResearchProgress() + civInfo.civConstructions.startTurn() civInfo.attacksSinceTurnStart.clear() civInfo.updateStatsForNextTurn() // for things that change when turn passes e.g. golden age, city state influence diff --git a/core/src/com/unciv/ui/screens/pickerscreens/TechPickerScreen.kt b/core/src/com/unciv/ui/screens/pickerscreens/TechPickerScreen.kt index f0f608a3ab..98b9539aea 100644 --- a/core/src/com/unciv/ui/screens/pickerscreens/TechPickerScreen.kt +++ b/core/src/com/unciv/ui/screens/pickerscreens/TechPickerScreen.kt @@ -115,6 +115,8 @@ class TechPickerScreen( } else civTech.techsToResearch = tempTechsToResearch + civTech.updateResearchProgress() + game.settings.addCompletedTutorialTask("Pick technology") game.popScreen() @@ -424,7 +426,7 @@ class TechPickerScreen( } private fun getTechProgressLabel(techs: List): String { - val progress = techs.sumOf { tech -> civTech.researchOfTech(tech) } + val progress = techs.sumOf { tech -> civTech.researchOfTech(tech) } + civTech.getOverflowScience(techs.first()) val techCost = techs.sumOf { tech -> civInfo.tech.costOfTech(tech) } return "(${progress}/${techCost})" }