How to Create Custom Victory Conditions in Unciv: A Complete Modding Guide

Custom victory conditions in Unciv are defined entirely through JSON configuration files, requiring no changes to the Kotlin source code.

Unciv is an open-source Android/Desktop reimplementation of Civilization V that supports extensive modding through data-driven configuration. Creating custom victory conditions in Unciv allows modders to design unique win states beyond the standard Domination or Scientific victories. This guide explains how to leverage the victory system architecture to add new win conditions using only JSON definitions.

Understanding the Victory System Architecture

The victory evaluation logic resides in VictoryManager.kt at core/src/com/unciv/logic/civilization/managers/VictoryManager.kt. This manager reads the active ruleset's VictoryTypes definitions and evaluates each milestone listed for every victory type. When getVictoryTypeAchieved() determines that the final milestone of a victory type is satisfied, it returns the victory name and triggers the game end.

The system checks GameParameters.victoryTypes on every evaluation cycle (lines 18-21 in VictoryManager.kt) to determine which victory types are active for the current session.

Creating Victory Types with JSON

To add a custom victory condition, create or edit a VictoryTypes.json file in your mod folder at MyMod/Assets/jsons/VictoryTypes.json. This file must conform to the schema defined in docs/Modders/schemas/VictoryTypes.schema.json.

Refer to the vanilla implementation at android/assets/jsons/Civ V - Vanilla/VictoryTypes.json for a working template of built-in victory structures.

Defining Victory Structure

Each entry in VictoryTypes.json requires a name, a list of milestones, and optional parameters such as requiredSpaceshipParts (for space race victories) or hiddenInVictoryScreen. The engine processes milestones sequentially using Milestone.hasBeenCompletedBy(civInfo), checking each in order until it finds one that is incomplete.

Writing Milestone Conditions

Milestones use Unciv's "unique" syntax to specify countable objectives, buildings, technologies, units, or policies. The syntax mirrors standard uniques with bracketed parameters.

[
  {
    "name": "Eco Victory",
    "milestones": [
      "Have [Forest] tile count >= 30",
      "Have [Environmental] policy tree completed",
      "Own the [National Park] improvement in every city"
    ],
    "requiredSpaceshipParts": [],
    "hiddenInVictoryScreen": false
  }
]

Implementing Instant Victory Triggers

For victories tied to specific units or buildings, add the "Triggers Victory" unique to your Units.json or Buildings.json. The VictoryManager checks civInfo.hasUnique(UniqueType.TriggersVictory) as a fallback mechanism (lines 24-26 in VictoryManager.kt), immediately ending the game when the condition is met.

[
  {
    "name": "Eco Dome",
    "requiredTech": "Advanced Ecology",
    "cost": 320,
    "uniques": ["Triggers Victory"]
  }
]

Enabling Custom Victories in Game Parameters

A victory type must be explicitly enabled in the game session through GameParameters.victoryTypes. When starting a custom game or loading a modded map, include your victory's key in this list—either via the Advanced Setup screen or programmatically in MapParameters.json.

{
  "victoryTypes": ["Domination", "Scientific", "Eco Victory"]
}

Without this configuration, the VictoryManager will not evaluate your custom milestones during gameplay.

Adding Victory Screen Illustrations

To display custom artwork on the Victory screen, create a folder structure at VictoryIllustrations/<VictoryName>/ within your mod assets. Include images named Background.png, Won.png, Lost.png, and milestone-specific illustrations. The UI automatically loads these when the victory type is active, as documented in the modding guides.

Summary

  • Victory logic is centralized in VictoryManager.kt and evaluates JSON-defined milestones via getVictoryTypeAchieved()
  • Create VictoryTypes.json in MyMod/Assets/jsons/ to define new win conditions following the schema in VictoryTypes.schema.json
  • Milestones use unique syntax and are checked sequentially via Milestone.hasBeenCompletedBy()
  • Use the "Triggers Victory" unique for instant win conditions on units or buildings
  • Enable victories in GameParameters.victoryTypes for them to appear in-game
  • Add visual assets in VictoryIllustrations/<name>/ for complete UI integration

Frequently Asked Questions

Do I need to modify the Kotlin source code to create custom victories?

No. Unciv's victory system is fully data-driven. You only need to create a VictoryTypes.json file in your mod folder following the schema documented in VictoryTypes.schema.json. The VictoryManager automatically loads and evaluates these definitions at runtime without requiring changes to VictoryManager.kt.

What file path should I use for custom victory definitions?

Place your VictoryTypes.json file at MyMod/Assets/jsons/VictoryTypes.json where MyMod is your mod's root directory. This follows the standard Unciv mod file structure and allows the ruleset loader to discover your victory types automatically alongside other JSON configurations.

How does the game check if a civilization has achieved a custom victory?

The VictoryManager.getVictoryTypeAchieved() function iterates through each enabled victory type in GameParameters.victoryTypes and calls Milestone.hasBeenCompletedBy(civInfo) for every milestone listed. If all milestones return true, or if the civilization has the TriggersVictory unique, the victory is awarded immediately and the game ends.

Can I create a victory condition that requires constructing a specific building?

Yes. Add the "Triggers Victory" unique to any building definition in your Buildings.json. When a civilization completes that building, civInfo.hasUnique(UniqueType.TriggersVictory) returns true in VictoryManager.kt (lines 24-26), triggering an immediate victory regardless of other milestone conditions defined in VictoryTypes.json.

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 →