How TechManager Handles Technology Research in Unciv

The TechManager class in Unciv centralizes all civilization research logic, tracking completed technologies in a HashSet, managing the research queue, calculating science costs with multiple modifiers, and processing turn-based advancement including overflow handling and era transitions.

The TechManager is the core research engine for every civilization in yairm210/Unciv, an open-source Android/Desktop reimplementation of Civilization V. Located at core/src/com/unciv/logic/civilization/managers/TechManager.kt, this class encapsulates how civilizations discover technologies, manage science overflow, and progress through historical eras. All other systems query this manager to check prerequisites, validate unit availability, or determine era-specific bonuses.

Core Research State Management

Tracking Completed Technologies

Inside TechManager.kt, the manager maintains two critical data structures for completed research. The persistent techsResearched is a HashSet<String> storing the names of all discovered technologies, while the transient researchedTechnologies list holds the actual Technology objects for fast runtime access. When checking if a civilization knows a specific technology, the isResearched(techName: String) method queries this set in constant time.

Queueing Future Research

The techsToResearch mutable list maintains the civilization's research priority queue. The first element represents the current active research target, accessible via currentTechnologyName(). When players select a new technology in the UI, the system simply adds the tech name to this queue. The canResearchTech(techName: String) helper validates prerequisites before allowing queue insertion, ensuring the civilization meets all requirements.

Turn-Based Research Progression

Processing Science Each Turn

At the end of every turn, GameInfo.kt invokes TechManager.endTurn(scienceForNewTurn: Int) to advance research. This method aggregates multiple science sources:

  • Base science generation from cities
  • Research agreement bonuses
  • Accumulated overflowScience from previous discoveries

The combined total feeds into addScience(science: Int), which updates the running total against the current technology's cost.

Completing Technologies and Triggering Uniques

When accumulated science meets or exceeds the target cost, addScience automatically triggers addTechnology(techName: String). This method performs several critical operations:

  1. Adds the technology to techsResearched and updates researchedTechnologies
  2. Removes the completed tech from techsToResearch (unless it supports continuous research)
  3. Invokes updateTransientBooleans() to refresh flags like unitsCanEmbark, movementSpeedOnRoads, and allTechsAreResearched
  4. Fires all technology-specific uniques and reveals strategic resources
  5. Calculates overflowScience (capped by limitOverflowScience) for the next research target
  6. Calls moveToNewEra() if the technology advances the civilization's historical period

Overflow Science and Era Mechanics

Handling Excess Research

The overflowScience field stores surplus research points that exceed a technology's cost. To prevent runaway accumulation, the limitOverflowScience property caps overflow based on the next technology's cost. During endTurn, uncapped overflow automatically applies to the new research target, ensuring no science is wasted when completing expensive technologies in a single turn.

Advancing Eras

The moveToNewEra() method updates the civilization's era when research crosses chronological thresholds. This triggers era-specific uniques and posts notifications to the player. Additionally, updateEra() recalculates the current era based on the highest era among all researched technologies, ensuring the civilization's historical period always reflects its most advanced discovery.

UI Integration and Automation Helpers

The TechPickerScreen.kt interface interacts directly with the manager to display available technologies, costs, and remaining turns. When players click a tech button, TechButton.kt adds the selection to techsToResearch. For AI and modding support, helper methods like getRequiredTechsToDestination(targetTech: Technology) calculate prerequisite chains, while turnsToTech(techName: String) estimates completion time based on current science output.

Code Examples

// Queue a new technology from UI interaction or modding
civInfo.tech.techsToResearch.add("Pottery")
// Process end-of-turn science (called by GameInfo.kt)
val scienceGenerated = civInfo.stats.statsForNextTurn.science.toInt()
civInfo.tech.endTurn(scienceGenerated)
// Manually inject science for cheats or events
civInfo.tech.addScience(50)
// Query current research progress
val currentTech = civInfo.tech.currentTechnology()
val turnsRemaining = civInfo.tech.turnsToTech(currentTech?.name ?: "")
// Validate technology ownership for AI decisions
if (civInfo.tech.isResearched("Astronomy")) {
    // Enable astronomy-dependent units or buildings
}
// Calculate prerequisite path for long-term planning
val path = civInfo.tech.getRequiredTechsToDestination(
    civInfo.gameInfo.ruleset.technologies["Rocketry"]!!
)

Summary

  • TechManager.kt serves as the single source of truth for all civilization research state, located in the com.unciv.logic.civilization.managers package.
  • State tracking uses techsResearched (HashSet) for persistence and researchedTechnologies (transient list) for runtime performance.
  • Research queue management happens through the techsToResearch mutable list, with currentTechnologyName() identifying the active target.
  • Cost calculation in costOfTech() applies difficulty, game speed, map size, city count, and unique-based modifiers.
  • Turn processing occurs via endTurn(), which handles science aggregation, research agreements, and overflow application.
  • Completion logic in addTechnology() updates flags, fires uniques, reveals resources, and triggers era transitions through moveToNewEra().
  • Overflow protection prevents exploit via limitOverflowScience, while updateTransientBooleans() keeps movement and embarkation flags current.
  • Automation support includes isResearched(), canResearchTech(), and getRequiredTechsToDestination() for AI pathfinding.

Frequently Asked Questions

How does TechManager calculate the science cost for a technology?

The costOfTech(techName: String) method applies a multiplicative modifier chain to the base technology cost defined in the ruleset. It factors in game difficulty, game speed percentage, map size multiplier, the civilization's city count, and any unique-based modifiers from buildings or policies. The final integer represents the total science required before addTechnology() triggers.

What happens to excess science when a technology completes?

Excess science beyond the technology's cost becomes overflowScience, which is capped by limitOverflowScience to prevent stockpiling massive amounts for future expensive technologies. During the next call to endTurn(), this overflow automatically applies to the new research target, ensuring efficient science usage across turn boundaries.

How does the UI communicate with TechManager to change research?

TechPickerScreen.kt displays the technology tree by reading civInfo.tech state. When a player selects a technology, TechButton.kt adds the tech name string to the techsToResearch list. The manager immediately updates cost calculations and turn estimates, reflecting the new queue in the UI without requiring a full game state refresh.

Can mods or scripts directly manipulate research progress?

Yes, the manager exposes several public methods for automation. addScience(Int) allows direct injection of science points for custom events or cheats, while addTechnology(String) immediately grants technologies bypassing normal cost requirements. Mods can also query getRequiredTechsToDestination() to calculate prerequisite chains for AI behavior or victory condition checking.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →