├── .editorconfig
├── .gitattributes
├── .github
└── workflows
│ ├── composite
│ └── buildSetup
│ │ └── action.yml
│ ├── export-c7.yml
│ ├── readme.md
│ └── test-action.yml
├── .gitignore
├── Blast
├── BitStream.cs
├── Blast.cs
├── Blast.csproj
├── BlastException.cs
├── HuffmanTable.cs
├── InputBuffer.cs
├── readme.md
└── reference-code
│ ├── blast.c
│ └── blast.h
├── C7
├── Actions.cs
├── AnimationManager.cs
├── AnimationTracker.cs
├── Art
│ ├── .gdignore
│ ├── Cities
│ │ ├── MIDEASTWALL.png
│ │ └── rMIDEAST.png
│ ├── SmallHeads
│ │ ├── popHeads.pdn
│ │ └── popHeads.png
│ ├── Terrain
│ │ ├── Mountains-snow.png
│ │ ├── Mountains.png
│ │ ├── TerrainBuildings.pdn
│ │ ├── TerrainBuildings.png
│ │ ├── Volcanos forests.png
│ │ ├── Volcanos jungles.png
│ │ ├── Volcanos.png
│ │ ├── grassland forests.png
│ │ ├── hill forests.png
│ │ ├── hill jungle.png
│ │ ├── irrigation DESETT.png
│ │ ├── irrigation PLAINS.png
│ │ ├── irrigation TUNDRA.png
│ │ ├── irrigation.pdn
│ │ ├── irrigation.png
│ │ ├── marsh.png
│ │ ├── mountain forests.png
│ │ ├── mountain jungles.png
│ │ ├── mtnRivers.png
│ │ ├── plains forests.png
│ │ ├── tnt.pdn
│ │ ├── tnt.png
│ │ ├── tundra forests.png
│ │ ├── wCSO.pdn
│ │ ├── wCSO.png
│ │ ├── wOOO.pdn
│ │ ├── wOOO.png
│ │ ├── wSSS.pdn
│ │ ├── wSSS.png
│ │ ├── xdgc.pdn
│ │ ├── xdgc.png
│ │ ├── xdgp.pdn
│ │ ├── xdgp.png
│ │ ├── xdpc.pdn
│ │ ├── xdpc.png
│ │ ├── xggc.pdn
│ │ ├── xggc.png
│ │ ├── xhills.png
│ │ ├── xpgc.pdn
│ │ ├── xpgc.png
│ │ ├── xtgc.pdn
│ │ └── xtgc.png
│ ├── Units
│ │ └── Settler
│ │ │ ├── Build.flc
│ │ │ ├── Captured.flc
│ │ │ ├── Death.flc
│ │ │ ├── Default.flc
│ │ │ ├── Fidget.flc
│ │ │ ├── Pedia
│ │ │ ├── Large.pcx
│ │ │ ├── Small.pcx
│ │ │ ├── Unit32.pcx
│ │ │ └── Unit32_TrueColour.pcx
│ │ │ ├── Run.flc
│ │ │ └── Settler.ini
│ ├── WorldSetup
│ │ ├── CLIMTEMPAGEDepress.png
│ │ ├── CLIMTEMPAGERollovers.png
│ │ ├── age.pdn
│ │ ├── age.png
│ │ ├── background.pdn
│ │ ├── background.png
│ │ ├── climate.pdn
│ │ ├── climate.png
│ │ ├── landmassWaterSMALL.png
│ │ ├── landmassWaterSMALLdepress.png
│ │ ├── landmassWaterSMALLrollovers.png
│ │ ├── landmassWaterlarge.pdn
│ │ ├── landmassWaterlarge.png
│ │ ├── temperature.pdn
│ │ └── temperature.png
│ ├── city screen
│ │ ├── CityIcons.pdn
│ │ ├── CityIcons.png
│ │ ├── ProdButton.png
│ │ ├── background.png
│ │ └── cityMgmtButtons.png
│ └── resources.png
├── C7.csproj
├── C7.sln
├── C7Game.tscn
├── C7Theme.tres
├── Credits.cs
├── Credits.tscn
├── DoubleClickHandler.cs
├── Fonts
│ ├── LICENSE-NotoSans.txt
│ ├── NSansFont12Pt co.tres
│ ├── NSansFont24Pt.tres
│ ├── NotoSans-Bold.ttf
│ ├── NotoSans-Bold.ttf.import
│ ├── NotoSans-BoldItalic.ttf
│ ├── NotoSans-BoldItalic.ttf.import
│ ├── NotoSans-Italic.ttf
│ ├── NotoSans-Italic.ttf.import
│ ├── NotoSans-Regular.ttf
│ └── NotoSans-Regular.ttf.import
├── Game.cs
├── GlobalSingleton.cs
├── GodotSink.cs
├── LogManager.cs
├── Map
│ ├── BorderLayer.cs
│ ├── CityLabelScene.cs
│ ├── CityLayer.cs
│ ├── CityScene.cs
│ ├── FogOfWarLayer.cs
│ ├── GotoLayer.cs
│ ├── ResourceLayer.cs
│ ├── TileAssignmentLayer.cs
│ ├── TileOverlayLayer.cs
│ ├── TntLayer.cs
│ └── UnitLayer.cs
├── MapView.cs
├── PCXToGodot.cs
├── ParameterWrapper.cs
├── Text
│ ├── TextureConfigs
│ │ ├── c7.lua
│ │ ├── civ3.lua
│ │ └── civ3
│ │ │ ├── cities.lua
│ │ │ ├── player_setup.lua
│ │ │ ├── popheads.lua
│ │ │ ├── resources.lua
│ │ │ ├── terrain.lua
│ │ │ └── world_setup.lua
│ ├── c7-static-map-save.json
│ └── credits.txt
├── TextureLoader.cs
├── UIElements
│ ├── Advisors
│ │ ├── Advisors.cs
│ │ ├── DomesticAdvisor.cs
│ │ ├── MilitaryAdvisor.cs
│ │ ├── ScienceAdvisor.cs
│ │ ├── TechBox.cs
│ │ └── domestic_advisor.tscn
│ ├── CityScreen
│ │ ├── CityScreen.cs
│ │ ├── PopHead.cs
│ │ ├── ProductionMenu.cs
│ │ └── city_screen.tscn
│ ├── Civ3FileDialog.cs
│ ├── Civ3TextureButton.cs
│ ├── Civ3TextureRect.cs
│ ├── Diplomacy
│ │ ├── DealScreen.cs
│ │ ├── Diplomacy.cs
│ │ ├── TalkScreen.cs
│ │ ├── TradeOfferUi.cs
│ │ └── TradingTree.cs
│ ├── GameStatus
│ │ ├── ConsoleButton.cs
│ │ ├── LowerRightInfoBox.cs
│ │ └── StatusMenu.cs
│ ├── LabelExtensions.cs
│ ├── MainMenu
│ │ ├── Civ3MenuButton.cs
│ │ ├── MainMenu.cs
│ │ ├── MainMenuMusicPlayer.cs
│ │ ├── MainMenuTheme.tres
│ │ ├── MenuButtonContainer.cs
│ │ └── main_menu.tscn
│ ├── NewGame
│ │ ├── PlayerSetup.cs
│ │ ├── WorldSetup.cs
│ │ ├── player_setup.tscn
│ │ └── world_setup.tscn
│ ├── PalaceScreen
│ │ ├── PalaceScreen.cs
│ │ └── palace_screen.tscn
│ ├── Popups
│ │ ├── AdvisorHead.cs
│ │ ├── BuildCityDialog.cs
│ │ ├── CivilizationDestroyed.cs
│ │ ├── ConfirmationPopup.cs
│ │ ├── DiplomacySelection.cs
│ │ ├── ErrorMessage.cs
│ │ ├── EscapeQuitPopup.cs
│ │ ├── GameMenu.cs
│ │ ├── GovernmentSelection.cs
│ │ ├── InformationalPopup.cs
│ │ ├── Popup.cs
│ │ ├── PopupOverlay.cs
│ │ ├── ScienceSelection.cs
│ │ ├── TemporaryPopup.cs
│ │ ├── TextDialog.cs
│ │ └── WarConfirmation.cs
│ ├── RightClickMenu.cs
│ ├── Theme.cs
│ ├── UnitButtons
│ │ ├── RenameButton.cs
│ │ ├── UnitButtons.cs
│ │ └── UnitControlButton.cs
│ ├── UpperLeftNav
│ │ ├── AdvisorButton.cs
│ │ ├── CivilopediaButton.cs
│ │ └── MenuButton.cs
│ ├── black_sidebars.tscn
│ └── civ3_file_dialog.tscn
├── UnitTint.gdshader
├── Util.cs
├── default_env.tres
├── export_presets.cfg
├── icon.png
├── icon.png.import
├── project.godot
├── readme.md
├── title-screen.png
└── title-screen.png.import
├── C7Engine
├── AI
│ ├── BarbarianAI.cs
│ ├── ChooseProducible.cs
│ ├── CityTileAssignmentAI.cs
│ ├── IAI.cs
│ ├── Pathing
│ │ ├── AStarAlgorithm.cs
│ │ ├── BinaryMinHeap.cs
│ │ ├── Edge.cs
│ │ ├── EdgeWalker.cs
│ │ ├── PathingAlgorithm.cs
│ │ ├── PathingAlgorithmChooser.cs
│ │ ├── PathingNotes.txt
│ │ └── TradeNetwork.cs
│ ├── PlayerAI.cs
│ ├── StrategicAI
│ │ ├── ExpansionPriority.cs
│ │ ├── ExplorationPriority.cs
│ │ ├── PriorityAggregator.cs
│ │ ├── UtilityCalculations.cs
│ │ └── WarPriority.cs
│ ├── StrategicPriorityArbitrator.cs
│ ├── UnitAI
│ │ ├── CombatAI.cs
│ │ ├── DefenderAI.cs
│ │ ├── EscortAI.cs
│ │ ├── ExplorerAI.cs
│ │ ├── SettlerAI.cs
│ │ ├── SettlerLocationAI.cs
│ │ └── WorkerAI.cs
│ └── UnitAiExtension.cs
├── C7Engine.csproj
├── C7GameData
│ ├── AIData
│ │ ├── CombatAIData.cs
│ │ ├── DefenderAIData.cs
│ │ ├── EscortAIData.cs
│ │ ├── ExplorerAIData.cs
│ │ ├── SettlerAIData.cs
│ │ ├── StrategicPriority.cs
│ │ ├── TileKnowledge.cs
│ │ ├── TilePath.cs
│ │ ├── UnitAIData.cs
│ │ └── WorkerAIData.cs
│ ├── AnimationParams.cs
│ ├── BarbarianInfo.cs
│ ├── Building.cs
│ ├── CitizenType.cs
│ ├── City.cs
│ ├── CityResident.cs
│ ├── Civilization.cs
│ ├── CombatResult.cs
│ ├── CombatRole.cs
│ ├── Difficulty.cs
│ ├── ExperienceLevel.cs
│ ├── GameData.cs
│ ├── GameMap.cs
│ ├── Government.cs
│ ├── HeightMap.cs
│ ├── ID.cs
│ ├── IProducible.cs
│ ├── ImportCiv3.cs
│ ├── Json2DArrayConverter.cs
│ ├── MapGenNoise.cs
│ ├── MapUnit.cs
│ ├── MovementPoints.cs
│ ├── PediaIcons.cs
│ ├── Player.cs
│ ├── Resource.cs
│ ├── Rules.cs
│ ├── Save
│ │ ├── SaveBuilding.cs
│ │ ├── SaveCity.cs
│ │ ├── SaveGame.cs
│ │ ├── SaveMap.cs
│ │ ├── SavePlayer.cs
│ │ ├── SaveTech.cs
│ │ ├── SaveTerraform.cs
│ │ ├── SaveTile.cs
│ │ ├── SaveUnit.cs
│ │ ├── SaveUnitPrototype.cs
│ │ └── TileLocation.cs
│ ├── StrengthBonus.cs
│ ├── Tech.cs
│ ├── Terraform.cs
│ ├── TerrainImprovement.cs
│ ├── TerrainType.cs
│ ├── Tile.cs
│ ├── TradeOffer.cs
│ ├── UnitAI.cs
│ ├── UnitPrototype.cs
│ └── readme.md
├── C7Settings.cs
├── EngineStorage.cs
├── EntryPoints
│ ├── CityInteractions.cs
│ ├── CreateGame.cs
│ ├── MessageToEngine.cs
│ ├── MessageToUI.cs
│ ├── TurnHandling.cs
│ └── UnitInteractions.cs
├── MapGenerator.cs
├── SaveManager.cs
├── TerrainTextureFiles.cs
├── Weighting.cs
└── readme.md
├── ConvertCiv3Media
├── Civ3UnitSprite.cs
├── ConvertCiv3Media.csproj
├── ReadFlic.cs
├── ReadPcx.cs
├── readme.md
└── test.ps1
├── EngineTests
├── AI
│ └── Pathing
│ │ ├── BinaryMinHeapTest.cs
│ │ └── EdgeWalkerTest.cs
├── EngineTests.csproj
├── GameData
│ ├── CityTest.cs
│ ├── HeightMapTest.cs
│ ├── IDFactoryTests.cs
│ ├── MapGeneratorTest.cs
│ └── SaveTest.cs
└── data
│ └── output
│ └── info.txt
├── LICENSE
├── QueryCiv3
├── Biq.cs
├── BiqSections
│ ├── Bldg.cs
│ ├── City.cs
│ ├── Clny.cs
│ ├── Cont.cs
│ ├── Ctzn.cs
│ ├── Cult.cs
│ ├── Diff.cs
│ ├── Eras.cs
│ ├── Espn.cs
│ ├── Expr.cs
│ ├── Flav.cs
│ ├── Game.cs
│ ├── Good.cs
│ ├── Govt.cs
│ ├── Lead.cs
│ ├── Prto.cs
│ ├── Race.cs
│ ├── Rule.cs
│ ├── Sloc.cs
│ ├── Tech.cs
│ ├── Terr.cs
│ ├── Tfrm.cs
│ ├── Tile.cs
│ ├── Unit.cs
│ ├── Wchr.cs
│ ├── Wmap.cs
│ └── Wsiz.cs
├── Civ3Location.cs
├── QueryCiv3.cs
├── QueryCiv3.csproj
├── Sav.cs
├── SavSections
│ ├── Aibs.cs
│ ├── Binf.cs
│ ├── Bitm.cs
│ ├── City.cs
│ ├── Clny.cs
│ ├── Cnsl.cs
│ ├── Cont.cs
│ ├── Ctpg.cs
│ ├── Ctzn.cs
│ ├── Date.cs
│ ├── Faxx.cs
│ ├── Game.cs
│ ├── Hist.cs
│ ├── Idls.cs
│ ├── Lead.cs
│ ├── Outp.cs
│ ├── Palv.cs
│ ├── Peer.cs
│ ├── Plgi.cs
│ ├── Popd.cs
│ ├── Radt.cs
│ ├── Rple.cs
│ ├── Rplt.cs
│ ├── Tile.cs
│ ├── Tutr.cs
│ ├── Unit.cs
│ ├── Vloc.cs
│ └── Wrld.cs
├── Util.cs
└── readme.md
├── README.md
├── _Console
└── BuildDevSave
│ ├── BuildDevSave.csproj
│ ├── BuildDevSave.sln
│ ├── Program.cs
│ └── readme.md
└── doc
└── dev_environment.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Identify text-based files and standardize to Unix (lf) line endings
2 |
3 | # C# files
4 | *.cs text eol=lf
5 | *.csproj text eol=lf
6 | *.sln text eol=lf
7 |
8 | # Godot files
9 | *.tscn text eol=lf
10 | *.godot text eol=lf
11 | *.tres text eol=lf
12 | *.import text eol=lf
13 | *.gd text eol=lf
14 | *.shader text eol=lf
15 |
16 | # Other
17 | *.md text eol=lf
18 | *.ps1 text eol=lf
19 |
20 | .gitignore text eol=lf
21 | .gitattributes text eol=lf
22 | LICENSE text eol=lf
23 |
24 | # Super-long text files
25 | C7/Text/c7-static-map-save.json binary
26 | C7/Text/c7-static-map-save.json -diff
--------------------------------------------------------------------------------
/.github/workflows/composite/buildSetup/action.yml:
--------------------------------------------------------------------------------
1 | name: "Cross-Platform Setup"
2 | description: "Performs cross-platform build setup"
3 |
4 | runs:
5 | using: "composite"
6 | steps:
7 | - name: Optionally set BUILD_SUFFIX
8 | shell: bash
9 | run: |
10 | if [[ ${{ github.event.inputs.debug-build || 'true' }} == 'true' ]]; then
11 | echo "BUILD_SUFFIX=-debug" >> $GITHUB_ENV
12 | else
13 | echo "BUILD_SUFFIX=-release" >> $GITHUB_ENV
14 | fi
15 | - name: Optionally set GH_USERNAME
16 | shell: bash
17 | if: github.event.inputs.include-username == 'true'
18 | run: |
19 | echo "GH_USERNAME=-${{ github.actor }}" >> $GITHUB_ENV
20 | - name: Optionally set BUILD_TIME
21 | shell: bash
22 | if: github.event.inputs.include-datetime == 'true'
23 | run: |
24 | echo "BUILD_TIME=-$(date --iso-8601=minutes | sed -e 's/:/-/g' | sed -e 's/\+.*//')" >> $GITHUB_ENV
25 | - name: Set .net8.0 SDK
26 | shell: bash
27 | run: |
28 | wget https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
29 | dpkg -i packages-microsoft-prod.deb
30 | rm packages-microsoft-prod.deb
31 | apt-get update -qq
32 | apt-get install -y apt-transport-https
33 | apt-get update -qq
34 | apt-get install -y dotnet-sdk-8.0
35 | - name: Set FOLDER_NAME
36 | shell: bash
37 | run: |
38 | echo "FOLDER_NAME=${EXPORT_NAME}${{ github.event.inputs.folder-suffix || 'automated_build' }}${GH_USERNAME}${BUILD_TIME}" >> $GITHUB_ENV
39 | - name: Setup
40 | shell: bash
41 | run: |
42 | mkdir -v -p ~/.local/share/godot/export_templates
43 | mv /root/.local/share/godot/export_templates/${GODOT_VERSION}.stable.mono ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable.mono
44 |
45 |
--------------------------------------------------------------------------------
/.github/workflows/test-action.yml:
--------------------------------------------------------------------------------
1 | name: Test Action
2 | on:
3 | workflow_dispatch:
4 | inputs:
5 | drink-order:
6 | type: choice
7 | description: Drink Order
8 | options:
9 | - Pan-Galactic Gargle Blaster
10 | - Tea
11 | must-check:
12 | type: boolean
13 | description: Please check this box
14 |
15 | jobs:
16 | do-a-thing:
17 | runs-on: ubuntu-latest
18 | steps:
19 | - name: Check if box was checked
20 | if: github.event.inputs.must-check == 'false'
21 | run: |
22 | echo "::warning title=Box not checked::User did not click the box!"
23 | - name: Echo some stuff
24 | run: |
25 | echo "I did a thing"
26 | echo "Here is your ${{ github.event.inputs.drink-order }}"
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Godot-specific ignores
2 | .import/
3 | export.cfg
4 | logs/
5 | .godot
6 |
7 | ### This is excluded because Android builds may have secrets in export_presets.cfg ; see https://github.com/godotengine/godot-demo-projects/issues/329
8 | # re-ignoring it so manual exports don't mess with the to-be-finely-honed settings; can manually add if needs updating
9 | export_presets.cfg
10 | mono_crash.*.json
11 | mono_crash.*.blob
12 |
13 | # Exports folder ignores
14 | C7/Exports
15 | Exports/
16 |
17 | # Test runtime files
18 | EngineTests/data/
19 |
20 | # Mono-specific ignores
21 | .mono/
22 | data_*/
23 |
24 | # Below here are ignores imported from Puppeteer's project
25 | sav/
26 | *.pcx
27 | *.flc
28 | *.mp4
29 | *.webp
30 | *.bin
31 |
32 | *.swp
33 | *.*~
34 | project.lock.json
35 | .DS_Store
36 | *.pyc
37 |
38 | # Visual Studio Code
39 | .vscode
40 |
41 | # User-specific files
42 | *.suo
43 | *.user
44 | *.userosscache
45 | *.sln.docstates
46 | C7.ini
47 | log.txt
48 | *.csproj.old
49 |
50 | # The file we use to allow generating new maps
51 | C7/Text/c7-autosave-turn-0.json
52 |
53 | # Build results
54 | [Dd]ebug/
55 | [Dd]ebugPublic/
56 | [Rr]elease/
57 | [Rr]eleases/
58 | x64/
59 | x86/
60 | build/
61 | bld/
62 | [Bb]in/
63 | [Oo]bj/
64 | msbuild.log
65 | msbuild.err
66 | msbuild.wrn
67 | *.exe
68 |
69 | # Visual Studio 2015
70 | .vs/
71 |
--------------------------------------------------------------------------------
/Blast/BitStream.cs:
--------------------------------------------------------------------------------
1 | namespace Blast {
2 | public class BitStream {
3 | private readonly InputBuffer _inputBuffer;
4 |
5 | private int _bitBuffer = 0; // bit buffer
6 | private int _bitBufferCount = 0; // number of bits in bit buffer
7 |
8 | public BitStream(InputBuffer inputBuffer) {
9 | _inputBuffer = inputBuffer;
10 | }
11 |
12 | // FIXME stop allowing internal state to be mutable
13 | public (int buffer, int bufferCount) State {
14 | get {
15 | return (_bitBuffer, _bitBufferCount);
16 | }
17 |
18 | set {
19 | _bitBuffer = value.buffer;
20 | _bitBufferCount = value.bufferCount;
21 | }
22 | }
23 |
24 | public int GetBits(int need) {
25 | int val = _bitBuffer;
26 |
27 | while (_bitBufferCount < need) {
28 | val |= ((int)_inputBuffer.ConsumeByte()) << _bitBufferCount;
29 | _bitBufferCount += 8;
30 | }
31 |
32 | _bitBuffer = val >> need;
33 | _bitBufferCount -= need;
34 |
35 | return val & ((1 << need) - 1);
36 | }
37 |
38 | public void FlushBits() {
39 | _bitBufferCount = 0;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Blast/Blast.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 |
6 | BlastDecompressor
7 | Blast Decompressor
8 | A port of the PkWare Blast compression algorithm based on the C implementation
9 | by Mark Adler.
10 | 0.1-pre
11 | Apache-2.0
12 | false
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Blast/BlastException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Blast {
4 | public class BlastException : Exception {
5 | public const string OutOfInputMessage = "Ran out of input before completing decompression";
6 | public const string OutputMessage = "Output error before completing decompression";
7 | public const string LiteralFlagMessage = "Literal flag not zero or one";
8 | public const string DictionarySizeMessage = "Dictionary size not in 4..6";
9 | public const string DistanceMessage = "Distance is too far back";
10 |
11 | public BlastException() : base() { }
12 | public BlastException(string message) : base(message) { }
13 | public BlastException(string message, Exception inner) : base(message, inner) { }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Blast/InputBuffer.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Blast {
4 | public class InputBuffer {
5 | private readonly Stream _inputStream;
6 | private readonly byte[] _inputBuffer = new byte[16384];
7 |
8 | private int _inputBufferPos = 0;
9 | private int _inputBufferRemaining = 0; // available input in buffer
10 |
11 | public InputBuffer(Stream inputStream) {
12 | _inputStream = inputStream;
13 | }
14 |
15 | public byte ConsumeByte() {
16 | if (_inputBufferRemaining == 0) {
17 | DoReadBuffer();
18 |
19 | if (_inputBufferRemaining == 0) {
20 | throw new BlastException(BlastException.OutOfInputMessage);
21 | }
22 | }
23 |
24 | byte b = _inputBuffer[_inputBufferPos++];
25 | _inputBufferRemaining--;
26 |
27 | return b;
28 | }
29 |
30 | private void DoReadBuffer() {
31 | _inputBufferRemaining = _inputStream.Read(_inputBuffer, 0, _inputBuffer.Length);
32 | _inputBufferPos = 0;
33 | }
34 |
35 | ///
36 | /// Check for presence of more input without consuming it.
37 | /// May refill the input buffer.
38 | ///
39 | ///
40 | public bool IsInputRemaining() {
41 | // is there any input in the buffer?
42 | if (_inputBufferRemaining > 0) {
43 | return true;
44 | }
45 |
46 | // try to fill it if not
47 | DoReadBuffer();
48 |
49 | // true if input now available
50 | return _inputBufferRemaining > 0;
51 | }
52 |
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Blast/readme.md:
--------------------------------------------------------------------------------
1 | # Blast
2 |
3 | Code in this folder is copied from https://github.com/jamestelfer/Blast/tree/3f8c7919c0444c75121f7371c812ec5c2bb9905b/Blast , is copyright James Telfer.
4 |
5 | ## Licensing
6 |
7 | Any part of this implementation that is not covered by the original
8 | notice is Copyright © 2012 James Telfer, and is released under the
9 | [Apache 2.0 license](http://www.apache.org/licenses/LICENSE-2.0.html).
10 |
11 | It should be noted that this algorithm was originally implemented by
12 | PKWare.
13 |
14 | The C implementation (upon which this is based) is located
15 | within the [ZLib source tree](https://github.com/madler/zlib/blob/master/contrib/blast/),
16 | with a copy residing as a reference [within](https://github.com/jamestelfer/Blast/blob/master/Blast/reference-code/) this source tree.
17 |
18 | See the original [blast.h](https://github.com/madler/zlib/blob/master/contrib/blast/blast.h)
19 | for the terms of the license under which the C implementation was released.
--------------------------------------------------------------------------------
/C7/Art/.gdignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/.gdignore
--------------------------------------------------------------------------------
/C7/Art/Cities/MIDEASTWALL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Cities/MIDEASTWALL.png
--------------------------------------------------------------------------------
/C7/Art/Cities/rMIDEAST.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Cities/rMIDEAST.png
--------------------------------------------------------------------------------
/C7/Art/SmallHeads/popHeads.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/SmallHeads/popHeads.pdn
--------------------------------------------------------------------------------
/C7/Art/SmallHeads/popHeads.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/SmallHeads/popHeads.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/Mountains-snow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/Mountains-snow.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/Mountains.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/Mountains.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/TerrainBuildings.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/TerrainBuildings.pdn
--------------------------------------------------------------------------------
/C7/Art/Terrain/TerrainBuildings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/TerrainBuildings.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/Volcanos forests.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/Volcanos forests.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/Volcanos jungles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/Volcanos jungles.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/Volcanos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/Volcanos.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/grassland forests.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/grassland forests.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/hill forests.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/hill forests.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/hill jungle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/hill jungle.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/irrigation DESETT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/irrigation DESETT.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/irrigation PLAINS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/irrigation PLAINS.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/irrigation TUNDRA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/irrigation TUNDRA.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/irrigation.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/irrigation.pdn
--------------------------------------------------------------------------------
/C7/Art/Terrain/irrigation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/irrigation.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/marsh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/marsh.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/mountain forests.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/mountain forests.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/mountain jungles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/mountain jungles.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/mtnRivers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/mtnRivers.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/plains forests.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/plains forests.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/tnt.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/tnt.pdn
--------------------------------------------------------------------------------
/C7/Art/Terrain/tnt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/tnt.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/tundra forests.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/tundra forests.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/wCSO.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/wCSO.pdn
--------------------------------------------------------------------------------
/C7/Art/Terrain/wCSO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/wCSO.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/wOOO.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/wOOO.pdn
--------------------------------------------------------------------------------
/C7/Art/Terrain/wOOO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/wOOO.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/wSSS.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/wSSS.pdn
--------------------------------------------------------------------------------
/C7/Art/Terrain/wSSS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/wSSS.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/xdgc.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/xdgc.pdn
--------------------------------------------------------------------------------
/C7/Art/Terrain/xdgc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/xdgc.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/xdgp.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/xdgp.pdn
--------------------------------------------------------------------------------
/C7/Art/Terrain/xdgp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/xdgp.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/xdpc.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/xdpc.pdn
--------------------------------------------------------------------------------
/C7/Art/Terrain/xdpc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/xdpc.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/xggc.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/xggc.pdn
--------------------------------------------------------------------------------
/C7/Art/Terrain/xggc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/xggc.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/xhills.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/xhills.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/xpgc.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/xpgc.pdn
--------------------------------------------------------------------------------
/C7/Art/Terrain/xpgc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/xpgc.png
--------------------------------------------------------------------------------
/C7/Art/Terrain/xtgc.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/xtgc.pdn
--------------------------------------------------------------------------------
/C7/Art/Terrain/xtgc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Terrain/xtgc.png
--------------------------------------------------------------------------------
/C7/Art/Units/Settler/Build.flc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Units/Settler/Build.flc
--------------------------------------------------------------------------------
/C7/Art/Units/Settler/Captured.flc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Units/Settler/Captured.flc
--------------------------------------------------------------------------------
/C7/Art/Units/Settler/Death.flc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Units/Settler/Death.flc
--------------------------------------------------------------------------------
/C7/Art/Units/Settler/Default.flc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Units/Settler/Default.flc
--------------------------------------------------------------------------------
/C7/Art/Units/Settler/Fidget.flc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Units/Settler/Fidget.flc
--------------------------------------------------------------------------------
/C7/Art/Units/Settler/Pedia/Large.pcx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Units/Settler/Pedia/Large.pcx
--------------------------------------------------------------------------------
/C7/Art/Units/Settler/Pedia/Small.pcx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Units/Settler/Pedia/Small.pcx
--------------------------------------------------------------------------------
/C7/Art/Units/Settler/Pedia/Unit32.pcx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Units/Settler/Pedia/Unit32.pcx
--------------------------------------------------------------------------------
/C7/Art/Units/Settler/Pedia/Unit32_TrueColour.pcx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Units/Settler/Pedia/Unit32_TrueColour.pcx
--------------------------------------------------------------------------------
/C7/Art/Units/Settler/Run.flc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/Units/Settler/Run.flc
--------------------------------------------------------------------------------
/C7/Art/Units/Settler/Settler.ini:
--------------------------------------------------------------------------------
1 | [Speed]
2 | Normal Speed=225
3 | Fast Speed=225
4 |
5 | [Animations]
6 | BLANK=
7 | DEFAULT=Default.flc
8 | WALK=
9 | RUN=Run.flc
10 | ATTACK1=
11 | ATTACK2=
12 | ATTACK3=
13 | DEFEND=
14 | DEATH=Death.flc
15 | DEAD=
16 | FORTIFY=
17 | FORTIFYHOLD=
18 | FIDGET=Fidget.flc
19 | VICTORY=
20 | TURNLEFT=
21 | TURNRIGHT=
22 | BUILD=Build.flc
23 | ROAD=
24 | MINE=
25 | IRRIGATE=
26 | FORTRESS=
27 | CAPTURE=Captured.flc
28 | STOP_AT_LAST_FRAME=
29 | PauseROAD=
30 | PauseMINE=
31 | PauseIRRIGATE=
32 | JUNGLE=
33 | FOREST=
34 | PauseFOREST=
35 | [Timing]
36 | BLANK=0.500000
37 | DEFAULT=0.500000
38 | WALK=0.500000
39 | RUN=0.500000
40 | ATTACK1=0.500000
41 | ATTACK2=0.500000
42 | ATTACK3=0.500000
43 | DEFEND=0.500000
44 | DEATH=0.500000
45 | DEAD=0.500000
46 | FORTIFY=0.500000
47 | FORTIFYHOLD=0.500000
48 | FIDGET=0.500000
49 | VICTORY=0.500000
50 | TURNLEFT=0.500000
51 | TURNRIGHT=0.500000
52 | BUILD=0.500000
53 | ROAD=0.500000
54 | MINE=0.500000
55 | IRRIGATE=0.500000
56 | FORTRESS=0.500000
57 | CAPTURE=0.500000
58 | STOP_AT_LAST_FRAME=0.500000
59 | PauseROAD=0.500000
60 | PauseMINE=0.500000
61 | PauseIRRIGATE=0.500000
62 | JUNGLE=0.500000
63 | FOREST=0.500000
64 | PauseFOREST=0.500000
65 | [Sound Effects]
66 | BLANK=
67 | DEFAULT=
68 | WALK=
69 | RUN=../Settler/SettlerRun.amb
70 | ATTACK1=
71 | ATTACK2=
72 | ATTACK3=
73 | DEFEND=
74 | DEATH=../Worker/WorkerDeath.wav
75 | DEAD=
76 | FORTIFY=
77 | FORTIFYHOLD=
78 | FIDGET=
79 | VICTORY=
80 | TURNLEFT=
81 | TURNRIGHT=
82 | BUILD=../Settler/SettlerBuild.wav
83 | ROAD=
84 | MINE=
85 | IRRIGATE=
86 | FORTRESS=
87 | CAPTURE=
88 | STOP_AT_LAST_FRAME=
89 | PauseROAD=
90 | PauseMINE=
91 | PauseIRRIGATE=
92 | JUNGLE=
93 | FOREST=
94 | PauseFOREST=
95 | [Version]
96 | VERSION=1
97 | [Palette]
98 | PALETTE=
99 |
--------------------------------------------------------------------------------
/C7/Art/WorldSetup/CLIMTEMPAGEDepress.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/WorldSetup/CLIMTEMPAGEDepress.png
--------------------------------------------------------------------------------
/C7/Art/WorldSetup/CLIMTEMPAGERollovers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/WorldSetup/CLIMTEMPAGERollovers.png
--------------------------------------------------------------------------------
/C7/Art/WorldSetup/age.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/WorldSetup/age.pdn
--------------------------------------------------------------------------------
/C7/Art/WorldSetup/age.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/WorldSetup/age.png
--------------------------------------------------------------------------------
/C7/Art/WorldSetup/background.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/WorldSetup/background.pdn
--------------------------------------------------------------------------------
/C7/Art/WorldSetup/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/WorldSetup/background.png
--------------------------------------------------------------------------------
/C7/Art/WorldSetup/climate.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/WorldSetup/climate.pdn
--------------------------------------------------------------------------------
/C7/Art/WorldSetup/climate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/WorldSetup/climate.png
--------------------------------------------------------------------------------
/C7/Art/WorldSetup/landmassWaterSMALL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/WorldSetup/landmassWaterSMALL.png
--------------------------------------------------------------------------------
/C7/Art/WorldSetup/landmassWaterSMALLdepress.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/WorldSetup/landmassWaterSMALLdepress.png
--------------------------------------------------------------------------------
/C7/Art/WorldSetup/landmassWaterSMALLrollovers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/WorldSetup/landmassWaterSMALLrollovers.png
--------------------------------------------------------------------------------
/C7/Art/WorldSetup/landmassWaterlarge.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/WorldSetup/landmassWaterlarge.pdn
--------------------------------------------------------------------------------
/C7/Art/WorldSetup/landmassWaterlarge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/WorldSetup/landmassWaterlarge.png
--------------------------------------------------------------------------------
/C7/Art/WorldSetup/temperature.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/WorldSetup/temperature.pdn
--------------------------------------------------------------------------------
/C7/Art/WorldSetup/temperature.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/WorldSetup/temperature.png
--------------------------------------------------------------------------------
/C7/Art/city screen/CityIcons.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/city screen/CityIcons.pdn
--------------------------------------------------------------------------------
/C7/Art/city screen/CityIcons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/city screen/CityIcons.png
--------------------------------------------------------------------------------
/C7/Art/city screen/ProdButton.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/city screen/ProdButton.png
--------------------------------------------------------------------------------
/C7/Art/city screen/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/city screen/background.png
--------------------------------------------------------------------------------
/C7/Art/city screen/cityMgmtButtons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/city screen/cityMgmtButtons.png
--------------------------------------------------------------------------------
/C7/Art/resources.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Art/resources.png
--------------------------------------------------------------------------------
/C7/C7.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0
4 | true
5 | true
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/C7/C7Theme.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="Theme" load_steps=7 format=3 uid="uid://c1jpmssnhvodi"]
2 |
3 | [ext_resource type="FontFile" uid="uid://c1qd602n7xu3h" path="res://Fonts/NotoSans-Regular.ttf" id="1_6qeon"]
4 |
5 | [sub_resource type="StyleBoxEmpty" id="3"]
6 |
7 | [sub_resource type="StyleBoxEmpty" id="4"]
8 |
9 | [sub_resource type="StyleBoxEmpty" id="6"]
10 |
11 | [sub_resource type="StyleBoxFlat" id="5"]
12 | bg_color = Color(1, 0.968627, 0.870588, 1)
13 | expand_margin_left = 2.0
14 | expand_margin_top = 3.0
15 | expand_margin_right = 2.0
16 | expand_margin_bottom = 3.0
17 |
18 | [sub_resource type="StyleBoxFlat" id="7"]
19 | bg_color = Color(1, 0.968627, 0.870588, 1)
20 | expand_margin_left = 2.0
21 | expand_margin_top = 3.0
22 | expand_margin_right = 2.0
23 | expand_margin_bottom = 3.0
24 |
25 | [resource]
26 | Button/colors/font_color = Color(0, 0, 0, 1)
27 | Button/colors/font_focus_color = Color(0, 0.45098, 0.741176, 1)
28 | Button/colors/font_hover_color = Color(0.709804, 0.352941, 0, 1)
29 | Button/colors/font_pressed_color = Color(0, 0.45098, 0.741176, 1)
30 | Button/font_sizes/font_size = 13
31 | Button/fonts/font = ExtResource("1_6qeon")
32 | Button/styles/focus = SubResource("3")
33 | Button/styles/hover = SubResource("4")
34 | Button/styles/normal = SubResource("6")
35 | Button/styles/pressed = SubResource("5")
36 | Label/colors/font_color = Color(0, 0, 0, 1)
37 | Label/font_sizes/font_size = 14
38 | Label/fonts/font = ExtResource("1_6qeon")
39 | LineEdit/colors/caret_color = Color(0, 0, 0, 1)
40 | LineEdit/colors/cursor_color = Color(0, 0, 0, 1)
41 | LineEdit/colors/font_color = Color(0, 0, 0, 1)
42 | LineEdit/colors/font_selected_color = Color(0, 0, 0, 1)
43 | LineEdit/colors/selection_color = Color(0.709804, 0.807843, 0.709804, 1)
44 | LineEdit/font_sizes/font_size = 14
45 | LineEdit/fonts/font = ExtResource("1_6qeon")
46 | LineEdit/styles/focus = SubResource("7")
47 | LineEdit/styles/normal = SubResource("7")
48 |
--------------------------------------------------------------------------------
/C7/Credits.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=2 format=3 uid="uid://pfefjvwiljdp"]
2 |
3 | [ext_resource type="Script" path="res://Credits.cs" id="1"]
4 |
5 | [node name="Node2D" type="Node2D"]
6 | script = ExtResource("1")
7 |
--------------------------------------------------------------------------------
/C7/DoubleClickHandler.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 |
3 | ///
4 | /// A class for detecting double-clicks.
5 | ///
6 | /// To differentiate between single and double clicks, this class uses
7 | /// a timer-based detection mechanism. When a click occurs, it waits
8 | /// for a short period (DOUBLE_CLICK_DELAY) to check if another click
9 | /// follows. If a second click happens, it emits a double-click
10 | /// signal; otherwise, it emits a single-click signal.
11 | ///
12 | /// Although Godot provides built-in double-click detection, it does
13 | /// not allow handling single clicks separately. For example, with
14 | /// Godot’s built-in detection, clicking on a city that contains a
15 | /// unit would both zoom to the city and select the unit.
16 | ///
17 | [GlobalClass]
18 | public partial class DoubleClickHandler : Node {
19 | int leftMouseButtonClickCount = 0;
20 | InputEventMouseButton lastEventMouseButton;
21 | const double DOUBLE_CLICK_DELAY = 0.2;
22 |
23 | Timer timer = new();
24 |
25 | [Signal] public delegate void SingleClickEventHandler(InputEventMouseButton eventMouseButton);
26 | [Signal] public delegate void DoubleClickEventHandler(InputEventMouseButton eventMouseButton);
27 |
28 | public override void _Ready() {
29 | AddChild(timer);
30 | timer.OneShot = true;
31 | timer.Timeout += OnTimeout;
32 | }
33 |
34 | public void Accept(InputEventMouseButton eventMouseButton) {
35 | lastEventMouseButton = eventMouseButton;
36 | ++leftMouseButtonClickCount;
37 |
38 | timer.Stop();
39 |
40 | if (leftMouseButtonClickCount >= 2) {
41 | EmitSignal(SignalName.DoubleClick, lastEventMouseButton);
42 | leftMouseButtonClickCount = 0;
43 | } else {
44 | timer.Start(DOUBLE_CLICK_DELAY);
45 | }
46 | }
47 |
48 | private void OnTimeout() {
49 | EmitSignal(SignalName.SingleClick, lastEventMouseButton);
50 | leftMouseButtonClickCount = 0;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/C7/Fonts/NSansFont12Pt co.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="FontFile" load_steps=2 format=2]
2 |
3 | [ext_resource path="res://Fonts/NotoSans-Regular.ttf" type="FontFile" id=1]
4 |
5 | [resource]
6 | size = 12
7 | font_data = ExtResource( 1 )
8 |
--------------------------------------------------------------------------------
/C7/Fonts/NSansFont24Pt.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="FontFile" load_steps=2 format=2]
2 |
3 | [ext_resource path="res://Fonts/NotoSans-Regular.ttf" type="FontFile" id=1]
4 |
5 | [resource]
6 | size = 24
7 | font_data = ExtResource( 1 )
8 |
--------------------------------------------------------------------------------
/C7/Fonts/NotoSans-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Fonts/NotoSans-Bold.ttf
--------------------------------------------------------------------------------
/C7/Fonts/NotoSans-Bold.ttf.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="font_data_dynamic"
4 | type="FontFile"
5 | uid="uid://cuvua6uejddii"
6 | path="res://.godot/imported/NotoSans-Bold.ttf-eb740e9cd6689575964198a403490f94.fontdata"
7 |
8 | [deps]
9 |
10 | source_file="res://Fonts/NotoSans-Bold.ttf"
11 | dest_files=["res://.godot/imported/NotoSans-Bold.ttf-eb740e9cd6689575964198a403490f94.fontdata"]
12 |
13 | [params]
14 |
15 | Rendering=null
16 | antialiasing=1
17 | generate_mipmaps=false
18 | multichannel_signed_distance_field=false
19 | msdf_pixel_range=8
20 | msdf_size=48
21 | allow_system_fallback=true
22 | force_autohinter=false
23 | hinting=1
24 | subpixel_positioning=1
25 | oversampling=0.0
26 | Fallbacks=null
27 | fallbacks=[]
28 | Compress=null
29 | compress=true
30 | preload=[]
31 | language_support={}
32 | script_support={}
33 | opentype_features={}
34 |
--------------------------------------------------------------------------------
/C7/Fonts/NotoSans-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Fonts/NotoSans-BoldItalic.ttf
--------------------------------------------------------------------------------
/C7/Fonts/NotoSans-BoldItalic.ttf.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="font_data_dynamic"
4 | type="FontFile"
5 | uid="uid://b1xkj8g82jbx5"
6 | path="res://.godot/imported/NotoSans-BoldItalic.ttf-71eeef40a3adcde05af9e25cfa9ec103.fontdata"
7 |
8 | [deps]
9 |
10 | source_file="res://Fonts/NotoSans-BoldItalic.ttf"
11 | dest_files=["res://.godot/imported/NotoSans-BoldItalic.ttf-71eeef40a3adcde05af9e25cfa9ec103.fontdata"]
12 |
13 | [params]
14 |
15 | Rendering=null
16 | antialiasing=1
17 | generate_mipmaps=false
18 | multichannel_signed_distance_field=false
19 | msdf_pixel_range=8
20 | msdf_size=48
21 | allow_system_fallback=true
22 | force_autohinter=false
23 | hinting=1
24 | subpixel_positioning=1
25 | oversampling=0.0
26 | Fallbacks=null
27 | fallbacks=[]
28 | Compress=null
29 | compress=true
30 | preload=[]
31 | language_support={}
32 | script_support={}
33 | opentype_features={}
34 |
--------------------------------------------------------------------------------
/C7/Fonts/NotoSans-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Fonts/NotoSans-Italic.ttf
--------------------------------------------------------------------------------
/C7/Fonts/NotoSans-Italic.ttf.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="font_data_dynamic"
4 | type="FontFile"
5 | uid="uid://sbyjmuofuuob"
6 | path="res://.godot/imported/NotoSans-Italic.ttf-91b1a16a67414379e5eafe5c3c6604c2.fontdata"
7 |
8 | [deps]
9 |
10 | source_file="res://Fonts/NotoSans-Italic.ttf"
11 | dest_files=["res://.godot/imported/NotoSans-Italic.ttf-91b1a16a67414379e5eafe5c3c6604c2.fontdata"]
12 |
13 | [params]
14 |
15 | Rendering=null
16 | antialiasing=1
17 | generate_mipmaps=false
18 | multichannel_signed_distance_field=false
19 | msdf_pixel_range=8
20 | msdf_size=48
21 | allow_system_fallback=true
22 | force_autohinter=false
23 | hinting=1
24 | subpixel_positioning=1
25 | oversampling=0.0
26 | Fallbacks=null
27 | fallbacks=[]
28 | Compress=null
29 | compress=true
30 | preload=[]
31 | language_support={}
32 | script_support={}
33 | opentype_features={}
34 |
--------------------------------------------------------------------------------
/C7/Fonts/NotoSans-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/Fonts/NotoSans-Regular.ttf
--------------------------------------------------------------------------------
/C7/Fonts/NotoSans-Regular.ttf.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="font_data_dynamic"
4 | type="FontFile"
5 | uid="uid://c1qd602n7xu3h"
6 | path="res://.godot/imported/NotoSans-Regular.ttf-cac627b64fb56e196f72eb70f3c31c08.fontdata"
7 |
8 | [deps]
9 |
10 | source_file="res://Fonts/NotoSans-Regular.ttf"
11 | dest_files=["res://.godot/imported/NotoSans-Regular.ttf-cac627b64fb56e196f72eb70f3c31c08.fontdata"]
12 |
13 | [params]
14 |
15 | Rendering=null
16 | antialiasing=1
17 | generate_mipmaps=false
18 | multichannel_signed_distance_field=false
19 | msdf_pixel_range=8
20 | msdf_size=48
21 | allow_system_fallback=true
22 | force_autohinter=false
23 | hinting=1
24 | subpixel_positioning=1
25 | oversampling=0.0
26 | Fallbacks=null
27 | fallbacks=[]
28 | Compress=null
29 | compress=true
30 | preload=[{
31 | "chars": [],
32 | "glyphs": [],
33 | "name": "New Configuration",
34 | "size": Vector2i(16, 0)
35 | }]
36 | language_support={}
37 | script_support={}
38 | opentype_features={}
39 |
--------------------------------------------------------------------------------
/C7/GlobalSingleton.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using QueryCiv3;
3 | using C7Engine;
4 |
5 | /****
6 | Need to pass values from one scene to another, particularly when loading
7 | a game in main menu. This script is set to auto load in project settings.
8 | See https://docs.godotengine.org/en/stable/getting_started/step_by_step/singletons_autoload.html
9 | ****/
10 | public partial class GlobalSingleton : Node {
11 | // Will have main menu file picker set this and Game.cs pass it to C7Engine.createGame
12 | // which then should blank it again to prevent reloading same if going back to main menu
13 | // and back to game
14 | public string LoadGamePath;
15 | // For now this needs to get passed to QueryCiv3 when importing.
16 | public string DefaultBicPath { get => Util.GetCiv3Path() + @"/Conquests/conquests.biq"; }
17 |
18 | // This is the 'static map' used in lieu of terrain generation
19 | public string DefaultGamePath { get => @"./Text/c7-static-map-save.json"; }
20 |
21 | // The file where a generated map is saved, until we get more advanced ways
22 | // to generate new games.
23 | // TODO: improve this.
24 | public string DefaultGeneratedGamePath { get => @"./Text/c7-autosave-turn-0.json"; }
25 |
26 | public void ResetLoadGamePath() {
27 | LoadGamePath = DefaultGamePath;
28 | }
29 |
30 | // The characteristics of the world to generate. This exists in the singleton
31 | // to allow the world setup screen to pass the information to the player
32 | // setup screen, which is what actually kicks off the world generation.
33 | public WorldCharacteristics WorldCharacteristics;
34 | }
35 |
--------------------------------------------------------------------------------
/C7/GodotSink.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using Serilog;
3 | using Serilog.Core;
4 | using Serilog.Events;
5 | using Serilog.Configuration;
6 | using Serilog.Formatting;
7 | using System.IO;
8 |
9 | public partial class GodotSink : ILogEventSink {
10 | private readonly ITextFormatter _formatter;
11 |
12 | public GodotSink(ITextFormatter formatter) {
13 | _formatter = formatter;
14 | }
15 |
16 | public void Emit(LogEvent logEvent) {
17 | var message = string.Empty;
18 | if (_formatter is null) {
19 | message = logEvent.RenderMessage();
20 | } else {
21 | var writer = new StringWriter();
22 | _formatter.Format(logEvent, writer);
23 | message = writer.ToString();
24 | }
25 | GD.Print(message);
26 | }
27 | }
28 |
29 | public static class GodotSinkExtensions {
30 | public static LoggerConfiguration GodotSink(
31 | this LoggerSinkConfiguration loggerConfiguration,
32 | ITextFormatter formatter = null) {
33 | return loggerConfiguration.Sink(new GodotSink(formatter));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/C7/Map/CityLayer.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using C7GameData;
3 | using Godot;
4 | using Serilog;
5 |
6 | namespace C7.Map {
7 | public class CityLayer : LooseLayer {
8 | private Dictionary citySceneLookup = new();
9 |
10 | public CityLayer() {
11 | }
12 |
13 | public void UpdateAfterCityDestruction(City city) {
14 | citySceneLookup.Remove(city, out CityScene cityScene);
15 | if (cityScene != null) {
16 | cityScene.Hide();
17 | }
18 | }
19 |
20 | public override void drawObject(LooseView looseView, GameData gameData, Tile tile, Vector2 tileCenter) {
21 | if (tile.cityAtTile is null) {
22 | return;
23 | }
24 |
25 | City city = tile.cityAtTile;
26 | if (!citySceneLookup.ContainsKey(city)) {
27 | CityScene cityScene = new CityScene(city, new Vector2I((int)tileCenter.X, (int)tileCenter.Y));
28 | looseView.AddChild(cityScene);
29 | citySceneLookup[city] = cityScene;
30 | } else {
31 | CityScene scene = citySceneLookup[city];
32 | scene._Draw();
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/C7/Map/FogOfWarLayer.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using C7GameData;
4 | using ConvertCiv3Media;
5 | using Godot;
6 |
7 | namespace C7.Map {
8 | public partial class FogOfWarLayer : LooseLayer {
9 |
10 | private readonly ImageTexture fogOfWarTexture;
11 | private readonly Vector2 tileSize;
12 |
13 | public FogOfWarLayer() {
14 | Pcx fogOfWarPcx = new Pcx(Util.Civ3MediaPath("Art/Terrain/FogOfWar.pcx"));
15 | fogOfWarTexture = PCXToGodot.getPureAlphaFromPCX(fogOfWarPcx);
16 | tileSize = fogOfWarTexture.GetSize() / 9;
17 | }
18 |
19 | public override void drawObject(LooseView looseView, GameData gameData, Tile tile, Vector2 tileCenter) {
20 |
21 | // Fog of war is computed from the squares' intersections.
22 | // Thus, for each iteration, we have to consider that we are at the north intersection of the iterated tile
23 |
24 | Tile north = tile.neighbors[TileDirection.NORTH];
25 | Tile south = tile;
26 | Tile east = tile.neighbors[TileDirection.NORTHEAST];
27 | Tile west = tile.neighbors[TileDirection.NORTHWEST];
28 |
29 | TileKnowledge tileKnowledge = gameData.GetFirstHumanPlayer().tileKnowledge;
30 | int column = 0;
31 | int row = 0;
32 |
33 | if (tileKnowledge.isActiveTile(north)) {
34 | column += 2;
35 | } else if (tileKnowledge.isTileKnown(north)) {
36 | column += 1;
37 | }
38 |
39 | if (tileKnowledge.isActiveTile(west)) {
40 | column += 6;
41 | } else if (tileKnowledge.isTileKnown(west)) {
42 | column += 3;
43 | }
44 |
45 | if (tileKnowledge.isActiveTile(east)) {
46 | row += 2;
47 | } else if (tileKnowledge.isTileKnown(east)) {
48 | row += 1;
49 | }
50 |
51 | if (tileKnowledge.isActiveTile(south)) {
52 | row += 6;
53 | } else if (tileKnowledge.isTileKnown(south)) {
54 | row += 3;
55 | }
56 |
57 | var fogOrigin = new Vector2(tileCenter.X - tileSize.X/2, tileCenter.Y - tileSize.Y);
58 | var fogRect = new Rect2(fogOrigin, tileSize);
59 | var spriteRect = new Rect2(column * tileSize.X, row * tileSize.Y, tileSize * 0.999f);
60 | looseView.DrawTextureRectRegion(fogOfWarTexture, fogRect, spriteRect);
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/C7/Map/ResourceLayer.cs:
--------------------------------------------------------------------------------
1 | using C7GameData;
2 | using Godot;
3 | using Resource = C7GameData.Resource;
4 | using Serilog;
5 |
6 | namespace C7.Map {
7 | public partial class ResourceLayer : LooseLayer {
8 | private ILogger log = LogManager.ForContext();
9 |
10 | private static readonly Vector2 resourceSize = new Vector2(50, 50);
11 |
12 | public override void drawObject(LooseView looseView, GameData gameData, Tile tile, Vector2 tileCenter) {
13 | Resource resource = tile.Resource;
14 | if (resource == Resource.NONE) {
15 | return;
16 | }
17 |
18 | if (!ResourceVisible(gameData, tile)) {
19 | return;
20 | }
21 |
22 | var texture = TextureLoader.Load("resources.large", resource, useCache: true);
23 |
24 | looseView.DrawTexture(texture, tileCenter - 0.5f * texture.GetSize());
25 | }
26 |
27 | private bool ResourceVisible(GameData gameData, Tile t) {
28 | if (gameData.observerMode) {
29 | return true;
30 | }
31 | return gameData.GetFirstHumanPlayer().KnowsAboutResource(t.Resource);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/C7/Map/TntLayer.cs:
--------------------------------------------------------------------------------
1 | using C7GameData;
2 | using Godot;
3 | using Resource = C7GameData.Resource;
4 | using Serilog;
5 |
6 | namespace C7.Map {
7 | ///
8 | /// Displays terrain yield overlays (from the tnt.pcx file). These are most well known for letting you know where
9 | /// there are bonus grasslands.
10 | /// Note: I don't know why it's called tnt.
11 | ///
12 | public partial class TntLayer : LooseLayer {
13 | private ILogger log = LogManager.ForContext();
14 |
15 | private static readonly Vector2 tntSize = new Vector2(128, 64);
16 | private ImageTexture tntTexture;
17 |
18 | //Each row corresponds to a terrain. For now we're only adding one, maybe someday we'll add full TNT support
19 | #pragma warning disable CS0414
20 | private readonly int GRASSLAND_ROW = 0;
21 | private readonly int BONUS_GRASSLAND_ROW = 1;
22 | private readonly int PLAINS_ROW = 2;
23 | private readonly int DESERT_ROW = 3;
24 | private readonly int BONUS_GRASSLAND_TNT_OFF_ROW = 3;
25 | private readonly int TUNDRA_ROW = 4;
26 | private readonly int FLOOD_PLAIN_ROW = 5;
27 | #pragma warning restore CS0414
28 |
29 | public TntLayer() {
30 | tntTexture = TextureLoader.Load("terrain.tnt");
31 | }
32 | public override void drawObject(LooseView looseView, GameData gameData, Tile tile, Vector2 tileCenter) {
33 | if (tile.overlayTerrainType.Key == "grassland" && tile.isBonusShield) {
34 | Rect2 tntRectangle = new Rect2(0, BONUS_GRASSLAND_TNT_OFF_ROW * tntSize.Y, tntSize);
35 | Rect2 screenTarget = new Rect2(tileCenter - 0.5f * tntSize, tntSize);
36 | looseView.DrawTextureRectRegion(tntTexture, screenTarget, tntRectangle);
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/C7/ParameterWrapper.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 |
3 | /**
4 | * A work-around to not being able to pass objects in Godot.
5 | * To pass via signals, an object must extend Godot.Object.
6 | * This class allows us to work around that limitation fairly
7 | * easily.
8 | * Taken from https://github.com/godotengine/godot/issues/16706#issuecomment-394605337
9 | * Example sending:
10 | * ParameterWrapper wrappedUnit = new ParameterWrapper(SelectedUnit);
11 | * EmitSignal(nameof(NewAutoselectedUnit), wrappedUnit);
12 | * Example receiving:
13 | * public void OnNewUnitSelected(ParameterWrapper mapUnitThing) {
14 | * MapUnit unwrappedUnit = mapUnitThing.Value;
15 | * // Do whatever you like with unwrappedUnit...
16 | * }
17 | **/
18 |
19 | public partial class ParameterWrapper : RefCounted {
20 | public T Value { get; private set; }
21 |
22 | public ParameterWrapper(T value) {
23 | Value = value;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/C7/Text/TextureConfigs/civ3/cities.lua:
--------------------------------------------------------------------------------
1 | local CITY_WIDTH = 166
2 | local CITY_HEIGHT = 95
3 |
4 | local cities = {
5 | extra_data = {
6 | path = "Art/Cities/rMIDEAST.pcx",
7 | wallsPath = "Art/Cities/MIDEASTWALL.pcx",
8 | },
9 | }
10 |
11 | function cities:map_object_to_sprite(city_graphics_details)
12 | if (city_graphics_details:GetType().Name ~= "CityGraphicsDetails") then
13 | error "Expected a CityGraphicsDetails object"
14 | end
15 |
16 | if city_graphics_details.hasWalls then
17 | return {
18 | path = self.extra_data.wallsPath,
19 | crop_region = { 0, city_graphics_details.eraIndex * CITY_HEIGHT, CITY_WIDTH, CITY_HEIGHT },
20 | shadows = false,
21 | }
22 | end
23 |
24 | return {
25 | path = self.extra_data.path,
26 | crop_region = { city_graphics_details.sizeRank * CITY_WIDTH, city_graphics_details.eraIndex * CITY_HEIGHT, CITY_WIDTH, CITY_HEIGHT },
27 | shadows = false,
28 | }
29 | end
30 |
31 | return cities
--------------------------------------------------------------------------------
/C7/Text/TextureConfigs/civ3/player_setup.lua:
--------------------------------------------------------------------------------
1 | local PLAYER_SETUP = "Art/PlayerSetup/"
2 |
3 | local player_setup = {
4 | background = PLAYER_SETUP .. "playerSetup.pcx",
5 | }
6 |
7 | return player_setup
8 |
--------------------------------------------------------------------------------
/C7/Text/TextureConfigs/civ3/popheads.lua:
--------------------------------------------------------------------------------
1 | local HEAD_SIZE = 48
2 | local HEAD_SIZE_WITH_BORDER = 50
3 | local NUM_ERAS = 4
4 | local MOODS_PER_ERA = 4
5 |
6 | local popheads = {
7 | extra_data = {
8 | path = "Art/SmallHeads/popHeads.pcx",
9 | },
10 | }
11 |
12 | local function validate_input(pair)
13 | local resident = pair.cityResident
14 | local era_num = pair.eraNum
15 |
16 | if resident:GetType().Name ~= "CityResident" then
17 | error "Expected a CityResident object"
18 | end
19 |
20 | if type(era_num) ~= "number" then
21 | error "Expected a number"
22 | end
23 |
24 | return resident, era_num
25 | end
26 |
27 | local function get_laborer(resident, era_num)
28 | local column = nil
29 |
30 | if resident.mood == resident.mood.Content then
31 | column = 0
32 | elseif resident.mood == resident.mood.Happy then
33 | column = 1
34 | elseif resident.mood == resident.mood.Unhappy then
35 | column = 3
36 | end
37 |
38 | if column == nil then
39 | error "Unknown mood type"
40 | end
41 |
42 | local x = 1
43 | local y = MOODS_PER_ERA * HEAD_SIZE_WITH_BORDER * era_num + HEAD_SIZE_WITH_BORDER * column + 1
44 |
45 | return { x, y, HEAD_SIZE, HEAD_SIZE }
46 | end
47 |
48 | local function get_specialist(resident, era_num)
49 | local citizen_type = resident.citizenType
50 | local x = HEAD_SIZE_WITH_BORDER * era_num
51 |
52 | local num_rows_of_laborers = NUM_ERAS * MOODS_PER_ERA
53 | local y = HEAD_SIZE_WITH_BORDER * num_rows_of_laborers + HEAD_SIZE_WITH_BORDER * (citizen_type.SpecialistIndex - 1)
54 |
55 | return { x + 1, y + 1, HEAD_SIZE, HEAD_SIZE }
56 | end
57 |
58 | function popheads:map_object_to_sprite(pair)
59 | local resident, eru_num = validate_input(pair)
60 | local citizen_type = resident.citizenType
61 |
62 | local crop_region
63 | if citizen_type.IsDefaultCitizen then
64 | crop_region = get_laborer(resident, eru_num)
65 | else
66 | crop_region = get_specialist(resident, eru_num)
67 | end
68 |
69 | return { crop_region = crop_region, path = self.extra_data.path }
70 | end
71 |
72 | return popheads
73 |
--------------------------------------------------------------------------------
/C7/Text/TextureConfigs/civ3/resources.lua:
--------------------------------------------------------------------------------
1 | local RESOURCE_SIZE = 50
2 | local LUXURY_SMALL_SIZE = 22
3 |
4 | local resources = {}
5 |
6 | resources.large = {
7 | extra_data = {
8 | path = "Art/resources.pcx",
9 | },
10 | }
11 |
12 | function resources.large:map_object_to_sprite(resource)
13 | if resource:GetType().Name ~= "Resource" then
14 | error "Expected a Resource object"
15 | end
16 |
17 | local icon = resource.Icon
18 | local row = icon // 6
19 | local col = icon % 6
20 |
21 | return {
22 | path = self.extra_data.path,
23 | crop_region = { col * RESOURCE_SIZE, row * RESOURCE_SIZE, RESOURCE_SIZE, RESOURCE_SIZE },
24 | shadows = false,
25 | }
26 | end
27 |
28 | resources.small = {
29 | extra_data = {
30 | path = "Art/city screen/luxuryicons_small.pcx",
31 | },
32 | }
33 |
34 | function resources.small:map_object_to_sprite(resource)
35 | if resource:GetType().Name ~= "Resource" then
36 | error "Expected a Resource object"
37 | end
38 |
39 | local icon = resource.Icon
40 | local col = icon - 8
41 |
42 | if col < 0 then
43 | error "Invalid resource icon index"
44 | end
45 |
46 | return {
47 | path = self.extra_data.path,
48 | crop_region = { col * LUXURY_SMALL_SIZE, 0, LUXURY_SMALL_SIZE, LUXURY_SMALL_SIZE },
49 | shadows = false,
50 | }
51 | end
52 |
53 | return resources
54 |
--------------------------------------------------------------------------------
/C7/UIElements/Advisors/Advisors.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using System;
3 | using Serilog;
4 | using System.Collections.Generic;
5 |
6 | /**
7 | * Handles managing the advisor screens.
8 | * Showing them, hiding them... maybe some other things eventually.
9 | * This is part of the effort to de-centralize from Game.cs and be more event driven.
10 | */
11 | public partial class Advisors : CenterContainer {
12 | private ILogger log = LogManager.ForContext();
13 |
14 | [Export] public DomesticAdvisor domesticAdvisor;
15 | private MilitaryAdvisor militaryAdvisor;
16 | private ScienceAdvisor scienceAdvisor;
17 |
18 | // A list of all the non-null advisors, so we can hide them whenever we
19 | // draw a different advisor.
20 | private List advisors = new();
21 |
22 | // Called when the node enters the scene tree for the first time.
23 | public override void _Ready() {
24 | //Center the advisor container. Following directions at https://docs.godotengine.org/en/stable/tutorials/gui/size_and_anchors.html?highlight=anchor
25 | //Also taking advantage of it being 1024x768, as the directions didn't really work. This is not 100% ideal (would be great for a general-purpose solution to work),
26 | //but does work with the current graphics independent of resolution.
27 | this.Hide();
28 | }
29 |
30 | private void ShowLatestAdvisor() {
31 | log.Debug("Received request to show latest advisor");
32 |
33 | OnShowSpecificAdvisor("F1");
34 | this.Show();
35 | }
36 |
37 | private void OnShowSpecificAdvisor(string advisorType) {
38 | // Hide any existing advisors so we can draw the requested one.
39 | foreach (TextureRect tr in advisors) {
40 | tr.Hide();
41 | }
42 | domesticAdvisor.Hide();
43 |
44 | if (advisorType.Equals("F1")) {
45 | domesticAdvisor.ShowAdvisor();
46 | this.Show();
47 | }
48 | if (advisorType.Equals("F3")) {
49 | if (militaryAdvisor != null) {
50 | RemoveChild(militaryAdvisor);
51 | militaryAdvisor = null;
52 | }
53 |
54 | militaryAdvisor = new MilitaryAdvisor();
55 | advisors.Add(militaryAdvisor);
56 | AddChild(militaryAdvisor);
57 | this.Show();
58 | }
59 | if (advisorType.Equals("F6")) {
60 | // TODO: What's the best way to refresh the tech tree UI without
61 | // adding too many children?
62 | if (scienceAdvisor != null) {
63 | RemoveChild(scienceAdvisor);
64 | scienceAdvisor = null;
65 | }
66 |
67 | scienceAdvisor = new ScienceAdvisor();
68 | advisors.Add(scienceAdvisor);
69 | AddChild(scienceAdvisor);
70 | this.Show();
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/C7/UIElements/Advisors/TechBox.cs:
--------------------------------------------------------------------------------
1 |
2 | using System;
3 | using C7GameData;
4 | using Godot;
5 |
6 | public partial class TechBox : TextureButton {
7 | private Tech tech;
8 | private TechState techState;
9 |
10 | public enum TechState {
11 | // This tech is known to the player.
12 | kKnown,
13 | // The player is actively researching this tech.
14 | kInProgress,
15 | // The player could research this tech.
16 | kPossible,
17 | // The player needs to research the prerequisites before this tech can
18 | // be researched.
19 | kBlocked,
20 | }
21 |
22 | public TechBox(Tech tech, TechState techState) {
23 | this.tech = tech;
24 | this.techState = techState;
25 | }
26 |
27 | public override void _Ready() {
28 | // TODO: Figure out how to pick which of the different sized tech boxes
29 | // we should use for a given tech.
30 | //
31 | // NOTE: this pcx has 16 rows, 4 per era, with different sizes.
32 | //
33 | // NOTE: the X coordinates of each column were found via guess+check.
34 | ImageTexture knownTechBox = TextureLoader.Load("tech_box.known");
35 | ImageTexture inProgressTechBox = TextureLoader.Load("tech_box.in_progress");
36 | ImageTexture possibleTechBox = TextureLoader.Load("tech_box.possible");
37 | ImageTexture blockedTechBox = TextureLoader.Load("tech_box.blocked");
38 |
39 | TextureNormal = techState switch {
40 | TechState.kKnown => knownTechBox,
41 | TechState.kInProgress => inProgressTechBox,
42 | TechState.kPossible => possibleTechBox,
43 | TechState.kBlocked => blockedTechBox,
44 | _ => throw new ArgumentOutOfRangeException("Invalid tech state")
45 | };
46 |
47 | ImageTexture iconTexture = TextureLoader.LoadFromPCX(tech.SmallIconPath);
48 | TextureRect icon = new() {
49 | Texture = iconTexture
50 | };
51 | icon.SetPosition(new Vector2(12, 32));
52 | AddChild(icon);
53 |
54 | // TODO: Have the name trail off if it is too large for the box.
55 | Label techName = new() {
56 | Text = tech.Name,
57 | OffsetLeft = 12,
58 | OffsetTop = 12
59 | };
60 | AddChild(techName);
61 |
62 | if (!tech.RequiredForEraAdvancement) {
63 | TextureRect notRequired = new() {
64 | Texture = TextureLoader.Load("tech_box.non_required"),
65 | };
66 | notRequired.SetPosition(new Vector2(85, 0));
67 | AddChild(notRequired);
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/C7/UIElements/CityScreen/PopHead.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using System;
3 | using C7GameData;
4 |
5 | // A utility class for rendering pop heads.
6 | public class PopHead {
7 | public const int HEAD_SIZE = 48;
8 |
9 | public static ImageTexture GetTexture(CityResident cityResident, int eraNum) {
10 | return TextureLoader.Load("popheads", new { cityResident, eraNum });
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/C7/UIElements/CityScreen/ProductionMenu.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Godot;
3 | using C7GameData;
4 | using C7Engine;
5 | using System;
6 |
7 | [Tool]
8 | [GlobalClass]
9 | public partial class ProductionMenu : Civ3TextureRect {
10 | private Dictionary itemMapping = new();
11 |
12 | Tree tree;
13 | Theme fontTheme = new();
14 |
15 | public ProductionMenu() { }
16 |
17 | public override void _Ready() {
18 | this.Texture = TextureLoader.Load("city_screen.production_queue");
19 |
20 | // Load the font we'll use.
21 | FontFile font = ResourceLoader.Load("res://Fonts/NotoSans-Regular.ttf", null, ResourceLoader.CacheMode.Ignore);
22 | font.FixedSize = 12;
23 | fontTheme.DefaultFont = font;
24 | }
25 |
26 | public void AddItems(GameData gameData, City city, Action chooseProduction) {
27 | if (tree != null) {
28 | itemMapping.Clear();
29 | RemoveChild(tree);
30 | tree = null;
31 | }
32 | tree = new();
33 |
34 | // Set up the tree of items. We use a tree so we get a scroll bar and
35 | // other niceties.
36 | AddChild(tree);
37 | tree.Columns = 2;
38 | tree.Size = new Vector2(203, 360);
39 | TradingTree.ConfigureTreeTheme(tree, fontTheme);
40 |
41 | TreeItem root = TradingTree.CreateTreeRoot(tree);
42 |
43 | foreach (IProducible option in city.ListProductionOptions(gameData)) {
44 | int buildTime = city.TurnsToProduce(option);
45 |
46 | TreeItem child = tree.CreateItem(root);
47 | string text = $"{option.name}";
48 | if (option is UnitPrototype proto) {
49 | text += $" {proto.attack}.{proto.defense}.{proto.movement}";
50 | }
51 | child.SetText(0, text);
52 | child.SetText(1, $"{buildTime} turns");
53 | child.SetIcon(0, RightClickChooseProductionMenu.GetProducibleIcon(option));
54 | itemMapping[child] = option;
55 | }
56 |
57 | // We only want clickable behavior, not selectable behavior.
58 | tree.ItemSelected += () => {
59 | TreeItem ti = tree.GetSelected();
60 | ti.Deselect(0);
61 | chooseProduction(itemMapping[ti]);
62 | };
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/C7/UIElements/Civ3FileDialog.cs:
--------------------------------------------------------------------------------
1 | using C7Engine;
2 | using C7GameData;
3 | using Godot;
4 | using Serilog;
5 |
6 | [GlobalClass]
7 | public partial class Civ3FileDialog : FileDialog {
8 | // An object for passing information (like save file paths) between scenes.
9 | GlobalSingleton Global;
10 | private ILogger log;
11 |
12 | public override void _Ready() {
13 | base._Ready();
14 | log = LogManager.ForContext();
15 |
16 | FileMode = FileDialog.FileModeEnum.OpenFile;
17 | Access = AccessEnum.Filesystem;
18 |
19 | Global = GetNode("/root/GlobalSingleton");
20 | FileSelected += OnFileSelected;
21 | }
22 |
23 | public void SetDirectoryForLoading(string RelPath) {
24 | CurrentDir = Util.Civ3Root + "/" + RelPath;
25 | FileMode = FileDialog.FileModeEnum.OpenFile;
26 | }
27 |
28 | public void SetDirectoryForSaving(string RelPath) {
29 | CurrentDir = Util.Civ3Root + "/" + RelPath;
30 | FileMode = FileDialog.FileModeEnum.SaveFile;
31 | }
32 |
33 | private void OnFileSelected(string path) {
34 | if (FileMode == FileDialog.FileModeEnum.OpenFile) {
35 | log.Information($"loading {path}");
36 | Global.LoadGamePath = path;
37 | GetTree().ChangeSceneToFile("res://C7Game.tscn");
38 | } else {
39 | if (!path.EndsWith(".json")) {
40 | path = path + ".json";
41 | }
42 |
43 | log.Information($"Saving game to {path}");
44 | EngineStorage.ReadGameData((GameData gameData) => {
45 | C7GameData.Save.SaveGame.FromGameData(gameData).Save(path);
46 | });
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/C7/UIElements/Civ3TextureButton.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using System.Collections.Generic;
3 |
4 | [GlobalClass]
5 | [Tool]
6 | public partial class Civ3TextureButton : TextureButton {
7 | public override void _ValidateProperty(Godot.Collections.Dictionary property) {
8 | Util.ApplyNoSaveFlag(property, [PropertyName.TextureNormal, PropertyName.TextureHover, PropertyName.TexturePressed]);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/C7/UIElements/Civ3TextureRect.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using System.Collections.Generic;
3 |
4 | [GlobalClass]
5 | [Tool]
6 | public partial class Civ3TextureRect : TextureRect {
7 | public override void _ValidateProperty(Godot.Collections.Dictionary property) {
8 | Util.ApplyNoSaveFlag(property, [PropertyName.Texture]);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/C7/UIElements/GameStatus/ConsoleButton.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using System;
3 |
4 | ///
5 | /// ConsoleButton is a custom control node that displays a textured button with a
6 | /// label. During gameplay the button is hidden by default.
7 | ///
8 | /// This control is specifically made to be used inside a BoxContainer. Even
9 | /// when the button is hidden, the ConsoleButton node maintains its size to avoid
10 | /// triggering layout recalculations.
11 | ///
12 | /// The button's tooltip and label text are customizable via exports.
13 | ///
14 | [GlobalClass]
15 | [Tool]
16 | public partial class ConsoleButton : Control {
17 | [Export] string text;
18 | [Export] string tooltipText;
19 |
20 | [Signal] public delegate void PressedEventHandler();
21 |
22 | private TextureButton button;
23 |
24 | public override void _Ready() {
25 | button = new() {
26 | TextureNormal = TextureLoader.Load("ui.console.normal"),
27 | TextureHover = TextureLoader.Load("ui.console.hover"),
28 | TexturePressed = TextureLoader.Load("ui.console.pressed"),
29 | TooltipText = tooltipText
30 | };
31 | button.Pressed += () => { EmitSignal(SignalName.Pressed); };
32 |
33 | Label label = new() {
34 | Text = text,
35 | OffsetLeft = 3,
36 | OffsetTop = -3,
37 | MouseFilter = MouseFilterEnum.Ignore
38 | };
39 |
40 | AddChild(button);
41 | button.AddChild(label);
42 |
43 | CustomMinimumSize = button.Size;
44 |
45 | // During the gameplay the button should be hidden by default
46 | if (!Engine.IsEditorHint()) {
47 | button.Hide();
48 | }
49 | }
50 |
51 | public void ShowButton() {
52 | button.Show();
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/C7/UIElements/GameStatus/StatusMenu.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using System;
3 | using C7GameData;
4 | using C7Engine;
5 |
6 | [GlobalClass]
7 | public partial class StatusMenu : Control {
8 | [Export] ConsoleButton openDiplomacy;
9 | [Export] ConsoleButton openPalaceScreen;
10 |
11 | [Export] PopupOverlay popupOverlay;
12 | [Export] PalaceScreen palaceScreen;
13 |
14 | public override void _Ready() {
15 | openDiplomacy.Pressed += OpenDiplomacyPopup;
16 | openPalaceScreen.Pressed += palaceScreen.Show;
17 | }
18 |
19 | public override void _Process(double delta) {
20 | EngineStorage.ReadGameData((GameData gD) => {
21 | if (gD.observerMode) {
22 | return;
23 | }
24 |
25 | Player player = gD.GetFirstHumanPlayer();
26 |
27 | // Only show the diplomacy button if we have civs to talk to.
28 | if (player.playerRelationships.Count > 0) {
29 | openDiplomacy.ShowButton();
30 | }
31 |
32 | // TODO: Don't show the palace button if the player can't start building the palace
33 | openPalaceScreen.ShowButton();
34 | });
35 | }
36 |
37 | private void OpenDiplomacyPopup() {
38 | EngineStorage.ReadGameData((GameData gD) => {
39 | Player player = gD.GetFirstHumanPlayer();
40 |
41 | popupOverlay.ShowPopup(new DiplomacySelection(player, gD.players), PopupOverlay.PopupCategory.Info);
42 | });
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/C7/UIElements/LabelExtensions.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 |
3 | public static class LabelExtensions {
4 | public static void SetTextAndCenterLabel(this Label label, string text) {
5 | //For the centered labels, we anchor them center, with equal weight on each side.
6 | //Then, when they are visible, we add a left margin that's negative and equal to half
7 | //their width.
8 | //Seems like there probably is an easier way, but I haven't found it yet.
9 | label.Text = text;
10 | label.HorizontalAlignment = HorizontalAlignment.Center;
11 | label.AnchorLeft = 0.5f;
12 | label.AnchorRight = 0.5f;
13 | label.OffsetLeft = -1 * (label.Size.X / 2.0f);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/C7/UIElements/MainMenu/MainMenuTheme.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="Theme" load_steps=6 format=3 uid="uid://b0jisy3avwkxf"]
2 |
3 | [sub_resource type="StyleBoxEmpty" id="3"]
4 |
5 | [sub_resource type="StyleBoxEmpty" id="4"]
6 |
7 | [sub_resource type="StyleBoxEmpty" id="2"]
8 |
9 | [sub_resource type="StyleBoxEmpty" id="5"]
10 |
11 | [sub_resource type="StyleBoxFlat" id="1"]
12 | bg_color = Color(1, 0.968627, 0.870588, 1)
13 | expand_margin_left = 2.0
14 | expand_margin_top = 3.0
15 | expand_margin_right = 2.0
16 | expand_margin_bottom = 3.0
17 |
18 | [resource]
19 | Button/colors/font_color = Color(0.1, 0.1, 0.1, 1)
20 | Button/colors/font_color_focus = Color(0, 0.45098, 0.741176, 1)
21 | Button/colors/font_color_hover = Color(0.709804, 0.352941, 0, 1)
22 | Button/colors/font_color_pressed = Color(0, 0.45098, 0.741176, 1)
23 | Button/styles/focus = SubResource("3")
24 | Button/styles/hover = SubResource("4")
25 | Button/styles/normal = SubResource("2")
26 | Button/styles/pressed = SubResource("5")
27 | Label/colors/font_color = Color(0, 0, 0, 1)
28 | LineEdit/colors/cursor_color = Color(0, 0, 0, 1)
29 | LineEdit/colors/font_color = Color(0, 0, 0, 1)
30 | LineEdit/colors/selection_color = Color(0.709804, 0.807843, 0.709804, 1)
31 | LineEdit/styles/focus = SubResource("1")
32 | LineEdit/styles/normal = SubResource("1")
33 |
--------------------------------------------------------------------------------
/C7/UIElements/MainMenu/MenuButtonContainer.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using System;
3 |
4 | [Tool]
5 | public partial class MenuButtonContainer : VBoxContainer {
6 | public Civ3MenuButton NewGame { get; private set; }
7 | public Civ3MenuButton QuickStart { get; private set; }
8 | public Civ3MenuButton Tutorial { get; private set; }
9 | public Civ3MenuButton LoadGame { get; private set; }
10 | public Civ3MenuButton LoadScenario { get; private set; }
11 | public Civ3MenuButton HallOfFame { get; private set; }
12 | public Civ3MenuButton ToggleGraphics { get; private set; }
13 | public Civ3MenuButton Preferences { get; private set; }
14 | public Civ3MenuButton AudioPreferences { get; private set; }
15 | public Civ3MenuButton Credits { get; private set; }
16 | public Civ3MenuButton Exit { get; private set; }
17 |
18 | public override void _Ready() {
19 | CreateButtons();
20 | }
21 |
22 | private void CreateButtons() {
23 | NewGame = new Civ3MenuButton() { Text = "New Game" };
24 | AddChild(NewGame);
25 |
26 | QuickStart = new Civ3MenuButton() { Text = "Quick Start" };
27 | AddChild(QuickStart);
28 |
29 | Tutorial = new Civ3MenuButton() { Text = "Tutorial" };
30 | AddChild(Tutorial);
31 |
32 | LoadGame = new Civ3MenuButton() { Text = "Load Game" };
33 | AddChild(LoadGame);
34 |
35 | LoadScenario = new Civ3MenuButton() { Text = "Load Scenario" };
36 | AddChild(LoadScenario);
37 |
38 | HallOfFame = new Civ3MenuButton() { Text = "Hall of Fame" };
39 | AddChild(HallOfFame);
40 |
41 | ToggleGraphics = new Civ3MenuButton() { Text = "Turn on C7 Graphics" };
42 | AddChild(ToggleGraphics);
43 |
44 | Preferences = new Civ3MenuButton() { Text = "Preferences" };
45 | AddChild(Preferences);
46 |
47 | AudioPreferences = new Civ3MenuButton() { Text = "Audio Preferences" };
48 | AddChild(AudioPreferences);
49 |
50 | Credits = new Civ3MenuButton() { Text = "Credits" };
51 | AddChild(Credits);
52 |
53 | Exit = new Civ3MenuButton() { Text = "Exit" };
54 | AddChild(Exit);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/C7/UIElements/PalaceScreen/PalaceScreen.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 |
3 | [GlobalClass]
4 | [Tool]
5 | public partial class PalaceScreen : Control {
6 | [Export] TextureRect background;
7 | [Export] TextureButton close;
8 |
9 | public override void _Ready() {
10 | background.Texture = TextureLoader.Load("palace.background");
11 |
12 | close.TextureNormal = TextureLoader.Load("city_screen.buttons.close.normal");
13 | close.TextureHover = TextureLoader.Load("city_screen.buttons.close.hover");
14 | close.TexturePressed = TextureLoader.Load("city_screen.buttons.close.pressed");
15 |
16 | close.Pressed += Hide;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/C7/UIElements/PalaceScreen/palace_screen.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=5 format=3 uid="uid://dkcu255o1qrt1"]
2 |
3 | [ext_resource type="PackedScene" uid="uid://bnvwn0hhrsn82" path="res://UIElements/black_sidebars.tscn" id="1_a64pg"]
4 | [ext_resource type="Script" path="res://UIElements/PalaceScreen/PalaceScreen.cs" id="2_er1um"]
5 | [ext_resource type="Script" path="res://UIElements/Civ3TextureRect.cs" id="2_p0gtj"]
6 | [ext_resource type="Script" path="res://UIElements/Civ3TextureButton.cs" id="4_yi45k"]
7 |
8 | [node name="PalaceScreen" node_paths=PackedStringArray("background", "close") instance=ExtResource("1_a64pg")]
9 | script = ExtResource("2_er1um")
10 | background = NodePath("HBoxContainer/Background")
11 | close = NodePath("HBoxContainer/Background/Close")
12 |
13 | [node name="Background" type="TextureRect" parent="HBoxContainer" index="1"]
14 | layout_mode = 2
15 | script = ExtResource("2_p0gtj")
16 |
17 | [node name="Close" type="TextureButton" parent="HBoxContainer/Background" index="0"]
18 | layout_mode = 0
19 | offset_left = 979.0
20 | offset_top = 11.0
21 | offset_right = 1011.0
22 | offset_bottom = 59.0
23 | script = ExtResource("4_yi45k")
24 |
--------------------------------------------------------------------------------
/C7/UIElements/Popups/AdvisorHead.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using System;
3 |
4 | public class AdvisorHead {
5 | public enum Mood {
6 | Happy,
7 | Angry,
8 | Sad,
9 | Surprised
10 | };
11 |
12 | public enum Advisor {
13 | Domestic,
14 | Trade,
15 | Military,
16 | Foreign,
17 | Culture,
18 | Science
19 | };
20 |
21 | public static ImageTexture GetPopupImage(Advisor advisor, Mood mood, int eraIndex) {
22 | return TextureLoader.LoadFromPCX(GetFilename(advisor), new(1 + (int)mood * 150, 150 * (eraIndex + 1) - 110, 149, 110), false);
23 | }
24 |
25 | private static string GetFilename(Advisor advisor) {
26 | switch (advisor) {
27 | case Advisor.Domestic: return "Art/SmallHeads/popupDOMESTIC.pcx";
28 | case Advisor.Trade: return "Art/SmallHeads/popupTRADE.pcx";
29 | case Advisor.Military: return "Art/SmallHeads/popupMILITARY.pcx";
30 | case Advisor.Foreign: return "Art/SmallHeads/popupFOREIGN.pcx";
31 | case Advisor.Culture: return "Art/SmallHeads/popupCULTURE.pcx";
32 | case Advisor.Science: return "Art/SmallHeads/popupSCIENCE.pcx";
33 | }
34 | throw new Exception($"Unknown advisor type: {advisor}");
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/C7/UIElements/Popups/CivilizationDestroyed.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using System;
3 | using System.Diagnostics;
4 | using C7GameData;
5 | using Serilog;
6 |
7 | public partial class CivilizationDestroyed : Popup {
8 | string civNoun = "";
9 |
10 | public CivilizationDestroyed(Civilization civ) {
11 | alignment = BoxContainer.AlignmentMode.End;
12 | margins = new Margins(right: 10);
13 | civNoun = civ.noun;
14 | }
15 |
16 | public override void _Ready() {
17 | base._Ready();
18 |
19 | //Dimensions are 530x260 (roughly).
20 | //The top 110 px are for the advisor.
21 | AddTexture(530, 260);
22 |
23 | TextureRect advisorHead = new() {
24 | Texture = AdvisorHead.GetPopupImage(AdvisorHead.Advisor.Military, AdvisorHead.Mood.Happy, eraIndex: 0),
25 | };
26 | //Appears at 400, 110 in game, but leftmost 25px are transparent with default graphics
27 | advisorHead.SetPosition(new Vector2(375, 0));
28 | AddChild(advisorHead);
29 |
30 | AddBackground(530, 150, 110);
31 | AddHeader("Military Advisor", 120);
32 |
33 | Label message = new() {
34 | Text = "The " + civNoun + " have been destroyed."
35 | };
36 | message.SetPosition(new Vector2(25, 170));
37 | AddChild(message);
38 |
39 | AddButton("Very well.", 215, ContinueAction);
40 | }
41 |
42 | private void ContinueAction() {
43 | GetParent().EmitSignal("HidePopup");
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/C7/UIElements/Popups/DiplomacySelection.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using System;
3 | using System.Diagnostics;
4 | using C7GameData;
5 | using C7GameData.Save;
6 | using System.Collections.Generic;
7 | using Serilog;
8 |
9 | // The popup for selecting which other civilization to contact.
10 | public partial class DiplomacySelection : Popup {
11 | private Player player;
12 | private List allPlayers;
13 |
14 | public DiplomacySelection(Player player, List allPlayers) {
15 | alignment = BoxContainer.AlignmentMode.Center;
16 | margins = new Margins(top: 200);
17 | this.player = player;
18 | this.allPlayers = allPlayers;
19 | }
20 |
21 | public override void _Ready() {
22 | base._Ready();
23 |
24 | int width = 530;
25 | int height = 115 + 25 * player.playerRelationships.Keys.Count;
26 | AddTexture(width, height);
27 | AddBackground(width, height);
28 | AddHeader("Pick the civilization...", 10);
29 |
30 | int vOffset = 65;
31 | foreach (KeyValuePair kvp in player.playerRelationships) {
32 | string status = kvp.Value.atWar ? "War" : "Peace";
33 | AddButton($"{allPlayers.Find(x => x.id == kvp.Key).civilization.noun} (at {status})", vOffset, () => {
34 | Node parent = GetParent();
35 | parent.EmitSignal(PopupOverlay.SignalName.HidePopup);
36 | parent.EmitSignal(PopupOverlay.SignalName.DiplomacySelection, new ParameterWrapper(kvp.Key));
37 | });
38 | vOffset += 25;
39 | }
40 |
41 | // TODO: Do something when the confirm button is pressed.
42 | AddConfirmButton(new Vector2(width - 55, height - 47), () => { });
43 | AddCancelButton(new Vector2(width - 30, height - 47));
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/C7/UIElements/Popups/ErrorMessage.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 |
3 | public partial class ErrorMessage : Popup {
4 | private string message = "";
5 |
6 | public ErrorMessage(string message) {
7 | this.message = message;
8 | alignment = BoxContainer.AlignmentMode.Center;
9 | margins = new Margins(top: 100);
10 | }
11 |
12 | public override void _Ready() {
13 | base._Ready();
14 |
15 | AddTexture(615, 325);
16 | AddBackground(615, 325);
17 |
18 | AddHeader("Load Error", 10);
19 |
20 | Label errorDescription = new Label();
21 | //TODO: General-purpose text breaking up util. Instead of \n
22 | //This appears to be the way to do multi line labels, see: https://godotengine.org/qa/30459/label-or-richtextlabel-auto-width
23 | //Maybe there's an awesomer control we can user instead.
24 | //But it should also be general-purpose, not just coded one-off here.
25 | errorDescription.Text = "Not a valid save file\n" + message;
26 | errorDescription.SetPosition(new Vector2(25, 162));
27 | AddChild(errorDescription);
28 |
29 | AddButton("Return to Menu", 290, quit);
30 | }
31 |
32 | private void quit() {
33 | GetTree().ChangeSceneToFile("res://UIElements/MainMenu/main_menu.tscn");
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/C7/UIElements/Popups/EscapeQuitPopup.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 |
3 | public partial class EscapeQuitPopup : Popup {
4 |
5 | public EscapeQuitPopup() {
6 | alignment = BoxContainer.AlignmentMode.Center;
7 | margins = new Margins(top: 100);
8 | }
9 |
10 | public override void _Ready() {
11 | base._Ready();
12 |
13 | // Dimensions in-game are 270x295, centered at the top
14 | // 100px margin from the top (this is different than the 110px when there's an advisor)
15 |
16 | // Create a transparent texture background of the appropriate size.
17 | // This is super important as if we just add the children, the parent won't be able to figure
18 | // out the size of this TextureRect, and it won't be able to align it properly.
19 | AddTexture(270, 195);
20 | AddBackground(270, 195);
21 |
22 | AddHeader("Oh No!", 10);
23 |
24 | Label warningMessage = new Label();
25 | // TODO: General-purpose text breaking up util. Instead of \n
26 | // This appears to be the way to do multi line labels, see: https://godotengine.org/qa/11126/how-to-break-line-on-the-label-using-gdscript
27 | // Maybe there's an awesomer control we can user instead
28 | warningMessage.Text = "Do you really want to quit?";
29 |
30 | warningMessage.SetPosition(new Vector2(25, 62));
31 | AddChild(warningMessage);
32 |
33 | AddButton("No, not really", 88, cancel);
34 | AddButton("Yes, immediately!", 116, quit);
35 | }
36 |
37 | private void quit() {
38 | GetParent().EmitSignal(PopupOverlay.SignalName.Quit);
39 | }
40 |
41 | private void cancel() {
42 | GetParent().EmitSignal(PopupOverlay.SignalName.HidePopup);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/C7/UIElements/Popups/GameMenu.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Godot;
3 | using Serilog;
4 |
5 | public partial class GameMenu : Popup {
6 | public GameMenu() {
7 | alignment = BoxContainer.AlignmentMode.Center;
8 | margins = new Margins(top: 100);
9 | }
10 |
11 | public override void _Ready() {
12 | base._Ready();
13 |
14 | AddTexture(370, 300);
15 | AddBackground(370, 300);
16 |
17 | AddHeader("Main Menu", 10);
18 |
19 | AddButton("Map", 60, map);
20 | AddButton("Load Game (Ctrl-L)", 85, load);
21 | AddButton("New Game (Ctrl-Shift-Q)", 110, newGame);
22 | AddButton("Preferences (Ctrl-P)", 135, preferences);
23 | AddButton("Retire (Ctrl-Q)", 160, retire);
24 | AddButton("Save Game (Ctrl-S)", 185, save);
25 | AddButton("Quit Game (ESC)", 210, quit);
26 |
27 | }
28 |
29 | private void save() {
30 | var loadDialog = GetNode("../%LoadDialog");
31 | // TODO: this should go to our own saves directory.
32 | loadDialog.SetDirectoryForSaving(@"Conquests/Saves");
33 |
34 | // TODO: The main menu does sound playing but we don't know our path in
35 | // the scene, which makes this hard.
36 | // PlayButtonPressedSound();
37 | GetParent().EmitSignal(PopupOverlay.SignalName.HidePopup);
38 |
39 | loadDialog.Popup();
40 | }
41 |
42 | private void preferences() {
43 | throw new NotImplementedException();
44 | }
45 |
46 | private void newGame() {
47 | throw new NotImplementedException();
48 | }
49 |
50 | private void load() {
51 | var loadDialog = GetNode("../%LoadDialog");
52 | loadDialog.SetDirectoryForLoading(@"Conquests/Saves");
53 |
54 | // TODO: The main menu does sound playing but we don't know our path in
55 | // the scene, which makes this hard.
56 | // PlayButtonPressedSound();
57 | GetParent().EmitSignal(PopupOverlay.SignalName.HidePopup);
58 |
59 | loadDialog.Popup();
60 | }
61 |
62 | private void quit() {
63 | GetParent().EmitSignal(PopupOverlay.SignalName.Quit);
64 | }
65 |
66 | private void retire() {
67 | GetParent().EmitSignal(PopupOverlay.SignalName.Retire);
68 | }
69 |
70 | private void map() {
71 | GetParent().EmitSignal(PopupOverlay.SignalName.HidePopup);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/C7/UIElements/Popups/GovernmentSelection.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using System;
3 | using System.Diagnostics;
4 | using C7GameData;
5 | using C7Engine;
6 | using C7GameData.Save;
7 | using System.Collections.Generic;
8 | using Serilog;
9 |
10 | // The popup for selecting which other civilization to contact.
11 | public partial class GovernmentSelection : Popup {
12 | private Player player;
13 | private List governments;
14 |
15 | public GovernmentSelection(Player player, List governments) {
16 | alignment = BoxContainer.AlignmentMode.Center;
17 | margins = new Margins(top: 200);
18 | this.player = player;
19 | this.governments = governments;
20 | }
21 |
22 | public override void _Ready() {
23 | base._Ready();
24 |
25 | int width = 530;
26 | int height = 115 + 25 * governments.Count;
27 | AddTexture(width, height);
28 | AddBackground(width, height);
29 | AddHeader("Select a new government type", 10);
30 |
31 | int vOffset = 65;
32 | foreach (Government g in governments) {
33 | AddButton($"{g.name}", vOffset, () => {
34 | Node parent = GetParent();
35 | new SelectGovernmentMsg(player, g).send();
36 | parent.EmitSignal(PopupOverlay.SignalName.HidePopup);
37 | });
38 | vOffset += 25;
39 | }
40 | }
41 |
42 | public override void _ExitTree() {
43 | // Restart the turn once a selection has been made.
44 | new MsgStartTurn().send();
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/C7/UIElements/Popups/InformationalPopup.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using System;
3 | using System.Diagnostics;
4 | using C7GameData;
5 | using Serilog;
6 |
7 | // A generic popup for some sort of information.
8 | public partial class InformationalPopup : Popup {
9 | string message;
10 | AdvisorHead.Advisor advisor;
11 | AdvisorHead.Mood mood;
12 |
13 | public InformationalPopup(string message, AdvisorHead.Advisor advisor = AdvisorHead.Advisor.Foreign, AdvisorHead.Mood mood = AdvisorHead.Mood.Happy) {
14 | alignment = BoxContainer.AlignmentMode.End;
15 | margins = new Margins(right: 10);
16 | this.message = message;
17 | this.advisor = advisor;
18 | this.mood = mood;
19 | }
20 |
21 | public override void _Ready() {
22 | base._Ready();
23 |
24 | int width = 430;
25 | int height = 230;
26 |
27 | TextureRect advisorHead = new();
28 | advisorHead.Texture = AdvisorHead.GetPopupImage(advisor, mood, eraIndex: 0);
29 | advisorHead.SetPosition(new Vector2(275, 0));
30 | AddChild(advisorHead);
31 |
32 | AddTexture(width, height);
33 | AddBackground(width, height - 110, 110);
34 | AddHeader("Foreign Advisor", 120);
35 |
36 | Label messageLabel = new();
37 | messageLabel.Text = message;
38 | messageLabel.SetPosition(new Vector2(25, 160));
39 | AddChild(messageLabel);
40 |
41 | AddConfirmButton(new Vector2(width - 40, height - 40), () => {
42 | GetParent().EmitSignal(PopupOverlay.SignalName.HidePopup);
43 | });
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/C7/UIElements/Popups/TemporaryPopup.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using System;
3 | using System.Threading.Tasks;
4 |
5 | public partial class TemporaryPopup : Label {
6 | private int durationInMillis;
7 |
8 | public TemporaryPopup(string text, float durationInSec) {
9 | Text = text;
10 | this.durationInMillis = (int)(durationInSec * 1000);
11 |
12 | StyleBoxFlat styleBox = new();
13 | styleBox.ContentMarginLeft = 5;
14 | styleBox.ContentMarginRight = 5;
15 | styleBox.ContentMarginTop = 2;
16 | styleBox.ContentMarginBottom = 2;
17 | styleBox.BgColor = Color.FromHtml("1C1C1C");
18 | AddThemeStyleboxOverride("normal", styleBox);
19 | AddThemeColorOverride("font_color", Colors.White);
20 | }
21 |
22 | public async void ShowPopup() {
23 | // Wait until we hit our duration then destroy ourself.
24 | await Task.Delay(durationInMillis);
25 |
26 | Visible = false;
27 | QueueFree();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/C7/UIElements/Popups/TextDialog.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using System;
3 |
4 | public partial class TextDialog : Popup {
5 | LineEdit textEditBox = new LineEdit();
6 | private string header;
7 | private string prompt;
8 | private string defaultText;
9 | private BoxContainer.AlignmentMode alignment;
10 | Action handleText;
11 |
12 |
13 | public TextDialog(string header,
14 | string prompt,
15 | string defaultText,
16 | BoxContainer.AlignmentMode alignment,
17 | Action handleText) {
18 | this.defaultText = defaultText;
19 | this.prompt = prompt;
20 | this.header = header;
21 | this.handleText = handleText;
22 | this.alignment = alignment;
23 |
24 | textEditBox.Theme = ThemeFactory.DefaultTheme;
25 | textEditBox.CaretBlink = true;
26 | alignment = BoxContainer.AlignmentMode.End;
27 | margins = new Margins(right: -10); // 10px margin from the right
28 | }
29 |
30 | public override void _Ready() {
31 | base._Ready();
32 |
33 | AddTexture(530, 260);
34 | AddBackground(530, 150, 110);
35 | AddHeader(header, 120);
36 |
37 | HBoxContainer labelAndTextBox = new HBoxContainer();
38 | labelAndTextBox.Alignment = alignment;
39 | labelAndTextBox.SizeFlagsHorizontal = SizeFlags.ExpandFill;
40 | labelAndTextBox.SizeFlagsStretchRatio = 1;
41 | labelAndTextBox.AnchorLeft = 0.0f;
42 | labelAndTextBox.AnchorRight = 0.85f;
43 | labelAndTextBox.SetPosition(new Vector2(30, 170));
44 |
45 | Label promptLabel = new Label();
46 | promptLabel.Text = prompt;
47 | labelAndTextBox.AddChild(promptLabel);
48 |
49 | textEditBox.SizeFlagsHorizontal = SizeFlags.ExpandFill;
50 | textEditBox.SizeFlagsStretchRatio = 1;
51 | textEditBox.Text = defaultText;
52 | labelAndTextBox.AddChild(textEditBox);
53 |
54 | this.AddChild(labelAndTextBox);
55 |
56 | textEditBox.SelectAll();
57 | textEditBox.GrabFocus();
58 |
59 | textEditBox.TextSubmitted += HandleTextInput;
60 |
61 | AddConfirmButton(new Vector2(475, 213), () => { HandleTextInput(textEditBox.Text); });
62 | AddCancelButton(new Vector2(500, 213));
63 | }
64 |
65 | private void HandleTextInput(string text) {
66 | GetViewport().SetInputAsHandled();
67 | GetParent().EmitSignal(PopupOverlay.SignalName.HidePopup);
68 | handleText(text);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/C7/UIElements/Popups/WarConfirmation.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using System;
3 | using System.Diagnostics;
4 | using C7GameData;
5 | using Serilog;
6 |
7 | public partial class WarConfirmation : Popup {
8 | Player opponent;
9 | Action action;
10 |
11 | public WarConfirmation(Player opponent, Action action) {
12 | alignment = BoxContainer.AlignmentMode.End;
13 | margins = new Margins(right: 10);
14 | this.opponent = opponent;
15 | this.action = action;
16 | }
17 |
18 | public override void _Ready() {
19 | base._Ready();
20 |
21 | AddTexture(530, 320);
22 |
23 | TextureRect advisorHead = new();
24 | advisorHead.Texture = AdvisorHead.GetPopupImage(AdvisorHead.Advisor.Foreign, AdvisorHead.Mood.Surprised, eraIndex: 0);
25 | //Appears at 400, 110 in game, but leftmost 25px are transparent with default graphics
26 | advisorHead.SetPosition(new Vector2(375, 0));
27 | AddChild(advisorHead);
28 |
29 | AddBackground(530, 210, 110);
30 | AddHeader("Foreign Advisor", 120);
31 |
32 | Label warningMessage = new Label();
33 | warningMessage.Text = $"This will cause war with the {opponent.civilization.noun}.\nAre you sure?";
34 | warningMessage.SetPosition(new Vector2(25, 170));
35 | AddChild(warningMessage);
36 |
37 | AddButton("I said DO IT!", 215, () => {
38 | action();
39 | GetParent().EmitSignal(PopupOverlay.SignalName.HidePopup);
40 | });
41 | AddButton("No. You're right, perhaps we should reconsider.", 245, cancel);
42 | }
43 |
44 | private void cancel() {
45 | GetParent().EmitSignal(PopupOverlay.SignalName.HidePopup);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/C7/UIElements/Theme.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 |
3 | public static class ThemeFactory {
4 |
5 | static ThemeFactory() {
6 | defaultTheme = new Theme();
7 | defaultTheme.SetColor("caret_color", "LineEdit", Colors.Black);
8 | }
9 |
10 | private static Theme defaultTheme;
11 |
12 | public static Theme DefaultTheme {
13 | get => defaultTheme;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/C7/UIElements/UnitButtons/RenameButton.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using ConvertCiv3Media;
3 |
4 | [Tool]
5 | public partial class RenameButton : Civ3TextureButton {
6 | public override void _Ready() {
7 | ImageTexture menuTexture = TextureLoader.Load("ui.rename");
8 | this.TextureNormal = menuTexture;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/C7/UIElements/UnitButtons/UnitControlButton.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using ConvertCiv3Media;
3 | using System;
4 |
5 | public partial class UnitControlButton : TextureButton {
6 |
7 | public string action; // corresponding Godot action (keybinding)
8 | private int X;
9 | private int Y;
10 | private Action onPressedAction;
11 |
12 | public static int scale = 32; // how many pixels each button is in each direction
13 |
14 | public UnitControlButton(string action, int X, int Y, Action onPressedAction) {
15 | this.action = action;
16 | this.X = X;
17 | this.Y = Y;
18 | this.onPressedAction = onPressedAction;
19 | }
20 |
21 | // Called when the node enters the scene tree for the first time.
22 | public override void _Ready() {
23 | Pcx buttonPcx = new Pcx(Util.Civ3MediaPath("Conquests/Art/interface/NormButtons.PCX"));
24 | Pcx buttonPcxRollover = new Pcx(Util.Civ3MediaPath("Conquests/Art/interface/rolloverbuttons.PCX"));
25 | Pcx buttonPcxPressed = new Pcx(Util.Civ3MediaPath("Conquests/Art/interface/highlightedbuttons.PCX"));
26 | Pcx buttonPcxAlpha = new Pcx(Util.Civ3MediaPath("Conquests/Art/interface/ButtonAlpha.pcx"));
27 | ImageTexture menuTexture = PCXToGodot.getImageFromPCXWithAlphaBlend(buttonPcx, buttonPcxAlpha, new(X * scale, Y * scale, scale, scale));
28 | ImageTexture rolloverTexture = PCXToGodot.getImageFromPCXWithAlphaBlend(buttonPcxRollover, buttonPcxAlpha, new(X * scale, Y * scale, scale, scale));
29 | ImageTexture pressedTexture = PCXToGodot.getImageFromPCXWithAlphaBlend(buttonPcxPressed, buttonPcxAlpha, new(X * scale, Y * scale, scale, scale));
30 | this.TextureNormal = menuTexture;
31 | this.TextureHover = rolloverTexture;
32 | this.TexturePressed = pressedTexture;
33 |
34 | this.Pressed += onButtonPress;
35 | }
36 |
37 | private void onButtonPress() {
38 | onPressedAction(this.action);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/C7/UIElements/UpperLeftNav/AdvisorButton.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using ConvertCiv3Media;
3 |
4 | [Tool]
5 | public partial class AdvisorButton : Civ3TextureButton {
6 | // Called when the node enters the scene tree for the first time.
7 | public override void _Ready() {
8 | TextureLoader.SetButtonTextures(this, "upper_left_navigation.advisor");
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/C7/UIElements/UpperLeftNav/CivilopediaButton.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using ConvertCiv3Media;
3 |
4 | [Tool]
5 | public partial class CivilopediaButton : Civ3TextureButton {
6 |
7 | // Called when the node enters the scene tree for the first time.
8 | public override void _Ready() {
9 | ImageTexture menuTexture = TextureLoader.Load("upper_left_navigation.civilopedia");
10 | this.TextureNormal = menuTexture;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/C7/UIElements/UpperLeftNav/MenuButton.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using ConvertCiv3Media;
3 |
4 | [Tool]
5 | public partial class MenuButton : Civ3TextureButton {
6 |
7 | [Export]
8 | private PopupOverlay popupOverlay;
9 |
10 | public override void _Ready() {
11 | ImageTexture menuTexture = TextureLoader.Load("upper_left_navigation.menu");
12 | this.TextureNormal = menuTexture;
13 | }
14 |
15 | public override void _Pressed() {
16 | popupOverlay.ShowPopup(new GameMenu(), PopupOverlay.PopupCategory.Info);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/C7/UIElements/black_sidebars.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene format=3 uid="uid://bnvwn0hhrsn82"]
2 |
3 | [node name="Screen" type="VBoxContainer"]
4 | anchors_preset = 15
5 | anchor_right = 1.0
6 | anchor_bottom = 1.0
7 | grow_horizontal = 2
8 | grow_vertical = 2
9 | theme_override_constants/separation = 0
10 |
11 | [node name="ColorRect" type="ColorRect" parent="."]
12 | layout_mode = 2
13 | size_flags_vertical = 3
14 | color = Color(0, 0, 0, 1)
15 |
16 | [node name="HBoxContainer" type="HBoxContainer" parent="."]
17 | layout_mode = 2
18 | theme_override_constants/separation = 0
19 |
20 | [node name="ColorRect" type="ColorRect" parent="HBoxContainer"]
21 | layout_mode = 2
22 | size_flags_horizontal = 3
23 | size_flags_vertical = 3
24 | color = Color(0, 0, 0, 1)
25 |
26 | [node name="ColorRect2" type="ColorRect" parent="HBoxContainer"]
27 | layout_mode = 2
28 | size_flags_horizontal = 3
29 | size_flags_vertical = 3
30 | color = Color(0, 0, 0, 1)
31 |
32 | [node name="ColorRect2" type="ColorRect" parent="."]
33 | layout_mode = 2
34 | size_flags_vertical = 3
35 | color = Color(0, 0, 0, 1)
36 |
--------------------------------------------------------------------------------
/C7/UIElements/civ3_file_dialog.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=2 format=3 uid="uid://bvncm1bgcii5e"]
2 |
3 | [ext_resource type="Script" path="res://UIElements/Civ3FileDialog.cs" id="1_mb1lm"]
4 |
5 | [node name="LoadDialog" type="FileDialog"]
6 | initial_position = 1
7 | size = Vector2i(312, 450)
8 | visible = true
9 | script = ExtResource("1_mb1lm")
10 |
--------------------------------------------------------------------------------
/C7/UnitTint.gdshader:
--------------------------------------------------------------------------------
1 | shader_type canvas_item;
2 |
3 | render_mode unshaded;
4 |
5 | uniform vec3 tintColor;
6 |
7 | vec4 applyTint(vec4 color)
8 | {
9 | return vec4(tintColor, 1.0) * color;
10 | }
11 |
12 | void fragment() {
13 | COLOR = applyTint(COLOR);
14 | }
15 |
--------------------------------------------------------------------------------
/C7/default_env.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="Environment" load_steps=2 format=2]
2 |
3 | [sub_resource type="ProceduralSky" id=1]
4 |
5 | [resource]
6 | background_mode = 2
7 | background_sky = SubResource( 1 )
8 |
--------------------------------------------------------------------------------
/C7/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/icon.png
--------------------------------------------------------------------------------
/C7/icon.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://bk71ywmbfvg35"
6 | path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://icon.png"
14 | dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/C7/readme.md:
--------------------------------------------------------------------------------
1 | ## Path to Civlization III Files
2 |
3 | If running on Windows 64-bit, C7 will grab the install folder from the registry. On Mac or Linux, copy over the files from your Windows installation of Civ III and set the environment variable `CIV3_HOME` to the top-level Civ III install folder. e.g. `export CIV3_HOME=/path/to/civ3` and then run Godot Mono from that session.
4 |
5 | - If you have the Steam version of Civ III, the top-level folder is called "Sid Meier's Civilization III Complete"
6 |
7 | ### Setting the environment on MacOS
8 |
9 | If you're having trouble setting the environment variable on Mac to apply to all apps, you can use the following workaround to get Godot to launch with it:
10 |
11 | 1. Create a folder in Applications called "Godot_with_env.app"
12 | 2. In that folder create a script called "Godot_with_env":
13 | ```bash
14 | #!/bin/bash
15 | export CIV3_HOME="/path/to/civ3"
16 | open /Applications/Godot_mono.app
17 | ```
18 | 3. `chmod +x Godot_with_env`
19 | 4. [Copy over the app icon](https://9to5mac.com/2021/11/08/change-mac-icons/) if you want
20 | 5. Launch Godot by using the Godot_with_env app instead of Godot_mono
21 |
22 | ## What is that?
23 |
24 | - Fonts/ - Fonts for Godot text
25 | - main_menu.tscn & MainMenu.cs - The startup scene and main menu
26 | - C7Game.tscn & Game.cs - An early prototype map view accessible from the main menu
27 |
--------------------------------------------------------------------------------
/C7/title-screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/C7-Game/Prototype/733adc306a130f4f4c77cadbc7bdbfd7831529c6/C7/title-screen.png
--------------------------------------------------------------------------------
/C7/title-screen.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://s55xvcaini8h"
6 | path="res://.godot/imported/title-screen.png-6457748713109437a88cea8d9928fe3c.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://title-screen.png"
14 | dest_files=["res://.godot/imported/title-screen.png-6457748713109437a88cea8d9928fe3c.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/C7Engine/AI/IAI.cs:
--------------------------------------------------------------------------------
1 | namespace C7Engine {
2 | using C7GameData;
3 |
4 | interface IAI {
5 | void PlayTurn(Player player, GameData gameData);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/C7Engine/AI/Pathing/BinaryMinHeap.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace C7Engine.Pathing {
5 | /**
6 | * https://en.wikipedia.org/wiki/Binary_heap
7 | */
8 | public class BinaryMinHeap where TValue : IComparable {
9 | private readonly List data = new List();
10 |
11 | public int count { get => data.Count; }
12 |
13 | // average O(1), worst case O(log N)
14 | public void insert(TValue v) {
15 | data.Add(v);
16 | siftUp(data.Count - 1);
17 | }
18 |
19 | // extract the smallest value, O(log N)
20 | public TValue extract() {
21 | TValue result = data[0];
22 | data[0] = data[data.Count - 1];
23 | data.RemoveAt(data.Count - 1);
24 | if (data.Count > 0) {
25 | siftDown(0);
26 | }
27 | return result;
28 | }
29 |
30 | private void siftUp(int childIndex) {
31 | if (childIndex == 0) return;
32 | int parentIndex = getParentIndex(childIndex);
33 | if (!isRightOrder(parentIndex, childIndex)) {
34 | swap(parentIndex, childIndex);
35 | siftUp(parentIndex);
36 | }
37 | }
38 |
39 | private void siftDown(int parentIndex) {
40 | int leftChild = getLeftChild(parentIndex);
41 | int rightChild = leftChild + 1;
42 | if (rightChild < data.Count) {
43 | // two children
44 | bool leftShouldBeHigher = isRightOrder(leftChild, rightChild);
45 | int topChildIndex = leftShouldBeHigher ? leftChild : rightChild;
46 | if (!isRightOrder(parentIndex, topChildIndex)) {
47 | swap(parentIndex, topChildIndex);
48 | siftDown(topChildIndex);
49 | }
50 | return;
51 | }
52 |
53 | if (leftChild >= data.Count) {
54 | // no children
55 | return;
56 | }
57 |
58 | // one children
59 | if (!isRightOrder(parentIndex, leftChild)) {
60 | swap(parentIndex, leftChild);
61 | }
62 | }
63 |
64 | private bool isRightOrder(int parentIndex, int childIndex) {
65 | return data[parentIndex].CompareTo(data[childIndex]) <= 0;
66 | }
67 |
68 | private void swap(int index, int otherIndex) {
69 | (data[index], data[otherIndex]) = (data[otherIndex], data[index]);
70 | }
71 |
72 | // 0 -> 0; 1,2 -> 0; 3,4 -> 1; 5,6 -> 2;
73 | private static int getParentIndex(int index) {
74 | return (index + 1) / 2 - 1;
75 | }
76 |
77 | // 0 -> 1; 1 -> 3; 2 -> 5; 3 -> 7
78 | private static int getLeftChild(int index) {
79 | return (index + 1) * 2 - 1;
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/C7Engine/AI/Pathing/Edge.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace C7Engine.Pathing {
4 | public class Edge : IComparable> {
5 | public readonly TNode prev;
6 | public readonly TNode current;
7 | public float distanceToCurrent { get; private set; }
8 |
9 | public Edge(TNode prev, TNode current, float distanceToCurrent) {
10 | this.prev = prev;
11 | this.current = current;
12 | this.distanceToCurrent = distanceToCurrent;
13 | }
14 |
15 | public int CompareTo(Edge other) {
16 | return distanceToCurrent.CompareTo(other.distanceToCurrent);
17 | }
18 |
19 | internal void addDistance(Edge previous) {
20 | distanceToCurrent += previous.distanceToCurrent;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/C7Engine/AI/Pathing/EdgeWalker.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using C7GameData;
3 |
4 | namespace C7Engine.Pathing {
5 | public abstract class EdgeWalker {
6 | public abstract IEnumerable> getEdges(TNode node);
7 | }
8 |
9 | public class UnitWalker : EdgeWalker {
10 | private MapUnit unit;
11 |
12 | public UnitWalker(MapUnit unit) {
13 | this.unit = unit;
14 | }
15 |
16 | public override IEnumerable> getEdges(Tile node) {
17 | bool landUnit = unit.IsLandUnit();
18 |
19 | List> result = new List>();
20 | foreach (KeyValuePair pair in node.neighbors) {
21 | TileDirection direction = pair.Key;
22 | Tile neighbor = pair.Value;
23 | bool neighborHasCityWithSameOwner = neighbor.cityAtTile != null && neighbor.cityAtTile.owner == unit.owner;
24 |
25 |
26 | // Land units can only go on to land.
27 | if (landUnit && !neighbor.IsLand()) {
28 | continue;
29 | }
30 |
31 | // Water units can only go on water or coastal cities with the
32 | // same owner (to support canals).
33 | if (!landUnit && !(neighbor.IsWater() || neighborHasCityWithSameOwner)) {
34 | continue;
35 | }
36 |
37 | float tileMovementCost = TilePath.getMovementCost(node, direction, neighbor);
38 | float unitMovementPoints = unit.unitType.movement;
39 |
40 | // If this tile would consume all of the movement points of this
41 | // unit, it has a cost of 1 turn. Otherwise we use the fraction
42 | // of a turn it would use as the cost.
43 | //
44 | // Examples:
45 | // - Warrior (1mp) moving onto Grassland (cost 1) => 1 turn
46 | // - Warrior (1mp) moving onto Hills (cost 2) => 1 turn
47 | // - Warrior (1mp) moving onto Jungle (cost 3) => 1 turn
48 | //
49 | // - Cavalry (3mp) moving onto Grassland (cost 1) => 1/3
50 | // - Cavalry (3mp) moving onto Hills (cost 2) => 2/3
51 | // - Cavalry (3mp) moving onto Jungle (cost 3) => 3/3
52 | //
53 | if (tileMovementCost >= unitMovementPoints) {
54 | result.Add(new Edge(node, neighbor, 1));
55 | } else {
56 | result.Add(new Edge(node, neighbor, tileMovementCost / unitMovementPoints));
57 | }
58 | }
59 | return result;
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/C7Engine/AI/Pathing/PathingAlgorithm.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using C7GameData;
4 |
5 | namespace C7Engine.Pathing {
6 | public abstract class PathingAlgorithm {
7 | public abstract TilePath PathFrom(Tile start, Tile destination);
8 |
9 | // Should not be public
10 | public TilePath ConstructPath(Tile destination, Dictionary predecessors) {
11 | List tilesInPath = new List() {destination};
12 | Tile tile = destination;
13 | while (predecessors.ContainsKey(tile)) {
14 | tile = predecessors[tile];
15 | tilesInPath.Add(tile);
16 | }
17 | tilesInPath.Reverse();
18 | Queue path = new Queue();
19 | foreach (Tile t in tilesInPath.Skip(1)) {
20 | path.Enqueue(t);
21 | }
22 | return new TilePath(destination, path);
23 | }
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/C7Engine/AI/Pathing/PathingAlgorithmChooser.cs:
--------------------------------------------------------------------------------
1 | using C7GameData;
2 |
3 | namespace C7Engine.Pathing {
4 | /**
5 | * Returns a pathing algorithm to use.
6 | */
7 | public class PathingAlgorithmChooser {
8 | public static PathingAlgorithm GetAlgorithm(MapUnit unit) {
9 | return new AStarAlgorithm(
10 | new UnitWalker(unit),
11 | (Tile from, Tile to) => {
12 | // HACK: for land-based movement we have to deal with railroads,
13 | // which have zero movement cost. If our heuristic is too strong it
14 | // will result in units taking a direct path between points A and B,
15 | // even if a more indirect path could be taken entirely by railroad.
16 | // To avoid this problem we scale our heuristic function down by a
17 | // constant (arbitraily chosen to work well in practice) so that a
18 | // typical tile movement cost (around 1/3 to 3, depending on roads
19 | // and terrain) dwarfs the heuristic. The heuristic is still enough
20 | // to point the search in the proper direction, and since it is
21 | // still an underestimate in most cases, it works properly.
22 | if (unit.IsLandUnit()) {
23 | return from.distanceTo(to) / 100.0;
24 | }
25 |
26 | return from.distanceTo(to);
27 | },
28 | (Tile neighbor, Tile destination) => {
29 | // Only allow a potentially attacking move for the last step,
30 | // to allow pathing to attack. We don't want to try and path
31 | // through opponents on the way to our destination though,
32 | // as we can get stuck.
33 | bool allowCombat = neighbor == destination;
34 | return unit.CanEnterTile(neighbor, allowCombat);
35 | }
36 | );
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/C7Engine/AI/StrategicAI/ExplorationPriority.cs:
--------------------------------------------------------------------------------
1 | using C7GameData;
2 | using C7GameData.AIData;
3 |
4 | namespace C7Engine.AI.StrategicAI {
5 | public class ExplorationPriority : StrategicPriority {
6 | private readonly int TEMP_GAME_LENGTH = 540;
7 |
8 | public ExplorationPriority() { }
9 |
10 | public override void CalculateWeightAndMetadata(Player player) {
11 | //Eventually this should consider the expected number of unknown tiles and the ability to explore them
12 | //For now this is somewhat placeholder, and one of very few options.
13 | int gameTurn = EngineStorage.gameData.turn;
14 | int percentOfGameFinished = (gameTurn * 100) / TEMP_GAME_LENGTH;
15 |
16 | this.calculatedWeight = 100 - 2 * percentOfGameFinished;
17 | }
18 |
19 | ///
20 | /// This priority prefers fast units.
21 | /// Eventually it will also consider things being inexpensive, and consider land versus sea.
22 | ///
23 | ///
24 | ///
25 | public override float GetProductionItemPreferenceWeight(IProducible producible) {
26 | if (producible is UnitPrototype prototype) {
27 | if (prototype.movement > 1) {
28 | return 1.0f * (prototype.movement - 1);
29 | }
30 | }
31 | return 0.0f;
32 | }
33 |
34 | public override string ToString() {
35 | return "ExplorationPriority";
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/C7Engine/AI/StrategicAI/PriorityAggregator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using C7GameData.AIData;
4 |
5 | namespace C7Engine.AI.StrategicAI {
6 | ///
7 | /// Eventually this will be a sophisticated way to look up all registered strategic priorities, including from DLLs or the equivalent.
8 | /// For now, it's hard-coded, but that's okay for the time being.
9 | ///
10 | public class PriorityAggregator {
11 |
12 | ///
13 | /// Returns a list of all strategic priorities registered in the game.
14 | ///
15 | /// This returns the type, so you can create an instance and then call methods on them.
16 | /// This follows what appears to be the C# equivalent of using Java class objects, useful for things like making annotation processors.
17 | /// In this case, I'm intentionally following that paradigm because by doing so, this method can be enhanced to look for additional
18 | /// types registered by mods, or simply stored in different components (DLLs/JARs/whatever the .NET equivalent is).
19 | ///
20 | ///
21 | public static List GetAllStrategicPriorityTypes() {
22 | List priorities = new List();
23 | priorities.Add(typeof(ExpansionPriority));
24 | priorities.Add(typeof(ExplorationPriority));
25 | priorities.Add(typeof(WarPriority));
26 | return priorities;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/C7Engine/AI/StrategicAI/UtilityCalculations.cs:
--------------------------------------------------------------------------------
1 |
2 | using System.Collections.Generic;
3 | using C7GameData;
4 |
5 | namespace C7Engine.AI.StrategicAI {
6 |
7 | ///
8 | /// For now, this is an area where methods shared between multiple strategic AI classes can live.
9 | /// The structure of this may change over time...
10 | ///
11 | public class UtilityCalculations {
12 |
13 | private static readonly int POSSIBLE_CITY_LOCATION_SCORE = 2; //how much weight to give to each possible city location
14 | private static readonly int TILE_SCORE_DIVIDER = 10; //how much to divide each location's tile score by
15 |
16 | public static int CalculateAvailableLandScore(Player player) {
17 | //Figure out if there's land to settle, and how much
18 | Dictionary possibleLocations = SettlerLocationAI.GetScoredSettlerCandidates(player.cities[0].location, player);
19 | int score = possibleLocations.Count * POSSIBLE_CITY_LOCATION_SCORE;
20 | foreach (int i in possibleLocations.Values) {
21 | score += i / TILE_SCORE_DIVIDER;
22 | }
23 | return score;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/C7Engine/AI/UnitAiExtension.cs:
--------------------------------------------------------------------------------
1 | using C7GameData;
2 | using Serilog;
3 | using C7Engine.Pathing;
4 |
5 | namespace C7Engine {
6 | // This goofy class only exists because of the arbitrary separation between
7 | // C7GameData and C7Engine.
8 | public static class UnitAiExtension {
9 | private static ILogger log = Log.ForContext();
10 |
11 | // Attempts to move the supplied unit along the given path.
12 | //
13 | // `path` is a ref so that the path can be recalculated if necessary.
14 | public static UnitAI.Result TryToMoveAlongPath(this UnitAI unitAi, MapUnit unit, ref TilePath path, bool allowCombat) {
15 | if (!unit.movementPoints.canMove) {
16 | return UnitAI.Result.InProgress;
17 | }
18 | Tile nextTile = path.Next();
19 | if (nextTile == Tile.NONE || !unit.CanEnterTile(nextTile, allowCombat)) {
20 | log.Information($"Attempting to repath {unit} from {unit.location} to {path.destination}");
21 | // Attempt to repath. If we succeed, return inprogress so we get
22 | // called again.
23 | path = PathingAlgorithmChooser.GetAlgorithm(unit).PathFrom(unit.location, path.destination);
24 | if ((path?.PathLength() ?? -1) == -1 || path.PeekNext() == Tile.NONE || !unit.CanEnterTile(path.PeekNext(), allowCombat)) {
25 | return UnitAI.Result.Error;
26 | }
27 |
28 | return UnitAI.Result.InProgress;
29 | }
30 | bool stillAlive = unit.move(unit.location.directionTo(nextTile));
31 | if (!stillAlive) {
32 | return UnitAI.Result.Error;
33 | }
34 | return UnitAI.Result.InProgress;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/C7Engine/C7Engine.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/AIData/CombatAIData.cs:
--------------------------------------------------------------------------------
1 | namespace C7GameData.AIData {
2 | public class CombatAIData : UnitAIData {
3 | public Tile destination;
4 | public TilePath path;
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/AIData/DefenderAIData.cs:
--------------------------------------------------------------------------------
1 | namespace C7GameData.AIData {
2 | public class DefenderAIData : UnitAIData {
3 | //I've been a bit expansive in possible goals here, perhaps some of them should be
4 | //in other types of AIs. But all of them are things I might do with a defensive unit in Civ.
5 | public enum DefenderGoal {
6 | DEFEND_CITY,
7 | DEFEND_RESOURCE,
8 | ESCORT_UNITS, //including settlers and offensive armies
9 | DEFEND_CHOKEPOINT,
10 | DEFEND_BORDER,
11 | DEFEND_TILE, //e.g. mountains so the enemy can't take them
12 | ESTABLISH_BEACHHEAD, //e.g. on a new continent
13 | PILLAGE_ENEMY_LANDS
14 | }
15 |
16 | public DefenderGoal goal;
17 | public Tile destination;
18 | public TilePath pathToDestination;
19 |
20 | public override string ToString() {
21 | string cityName = destination.HasCity ? destination.cityAtTile.name : " at " + destination.ToString();
22 | return goal + " " + cityName;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/AIData/EscortAIData.cs:
--------------------------------------------------------------------------------
1 | namespace C7GameData.AIData {
2 | public class EscortAIData : UnitAIData {
3 | public MapUnit unitToEscort;
4 |
5 | public override string ToString() {
6 | return "escorting " + unitToEscort;
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/AIData/ExplorerAIData.cs:
--------------------------------------------------------------------------------
1 | namespace C7GameData.AIData {
2 | public class ExplorerAIData : UnitAIData {
3 | public Tile destination;
4 | public TilePath pathToDestination;
5 |
6 | public override string ToString() {
7 | return "exploring toward " + destination;
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/AIData/SettlerAIData.cs:
--------------------------------------------------------------------------------
1 | namespace C7GameData.AIData {
2 | /**
3 | * I'm playing around with different possibilities for AI here.
4 | * I realized that I'd like units/players/etc. to be able to have references to their
5 | * AI data. This is because AI may be stateful. A Settler has a destination. A war plan
6 | * has a start date. A war has a goal, e.g. secure Iron. A transport convoy has a destination.
7 | *
8 | * Hopefully, keeping track of this will result in a more purposeful AI. Certain events
9 | * may cause it to re-evaluate, "should this worker still be building irrigation on the border
10 | * now that there's a war there?", but it's hard to tell a Settler "build a city here"
11 | * without a way to store "here".
12 | *
13 | * This will probably have to be broken into sub-classes over time, based on unit abilities.
14 | *
15 | * I'm also unsure of how much the logic of figuring out what to do should be in these
16 | * classes, versus higher-level ones.
17 | */
18 | public class SettlerAIData : UnitAIData {
19 | public enum SettlerGoal {
20 | BUILD_CITY,
21 | JOIN_CITY
22 | }
23 | public SettlerGoal goal;
24 | public Tile destination;
25 | public TilePath pathToDestination;
26 | public MapUnit escort;
27 |
28 | public override string ToString() {
29 | return goal + " at " + destination;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/AIData/UnitAIData.cs:
--------------------------------------------------------------------------------
1 | namespace C7GameData.AIData {
2 | //Base class from which other AIs can inherit.
3 | public interface UnitAIData {
4 |
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/AIData/WorkerAIData.cs:
--------------------------------------------------------------------------------
1 | namespace C7GameData.AIData {
2 | public class WorkerAIData : UnitAIData {
3 | public Terraform workerMove;
4 | public Tile destination;
5 | public TilePath pathToDestination;
6 |
7 | public override string ToString() {
8 | return workerMove + " at " + destination;
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/AnimationParams.cs:
--------------------------------------------------------------------------------
1 | namespace C7GameData {
2 | // My intention is that eventually AnimatedEffect will be a class with some useful stuff in it. Right now it's just an enum that lets the engine
3 | // communicate to the UI which Civ 3 special effect it wants to trigger. The UI must provide the file names for the animations, unfortunately they
4 | // can't be guessed based on the enum names (like MapUnit.AnimatedAction) because the files aren't all stored in predictable folder locations.
5 | // These enum values match the names of animation files in civ3PTW/Art/Animations/Trajectory. The actual mapping from enum value to file name is
6 | // determined by a dictionary in the Civ3Anim class (in Civ3AnimData.cs, part of the UI). TODO: Ultimately we'll want a way for the engine to specify
7 | // effect animations that is extensible for modders and not limited to Civ 3's animations.
8 | public enum AnimatedEffect {
9 | Hit,
10 | Hit2,
11 | Hit3,
12 | Hit5,
13 | Miss,
14 | WaterMiss
15 | }
16 |
17 | // Controls what happens to an animation once its time is up
18 | public enum AnimationEnding {
19 | Stop, // Automatically end animation and return to default pose
20 | Pause, // Hold animation on last frame until it's ended or replaced by another
21 | Repeat // Restart animation from beginning
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/BarbarianInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace C7GameData {
4 | ///
5 | /// Contains info pertaining to barbarian setup.
6 | /// This was in the catch-all RULE in Civ3. I'm giving it its own class in part
7 | /// because we may want to add more customization options.
8 | ///
9 | public class BarbarianInfo {
10 | //Legacy Civ3-compatible config
11 | public int basicBarbarianIndex = 0;
12 | public int advancedBarbarianIndex = 0;
13 | public int barbarianSeaUnitIndex = 0;
14 | [JsonIgnore] public UnitPrototype basicBarbarian;
15 | [JsonIgnore] public UnitPrototype advancedBarbarian;
16 | [JsonIgnore] public UnitPrototype barbarianSeaUnit;
17 |
18 | public int defaultHitpoints = 2;
19 | public int maxHitpoints = 2;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/CitizenType.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace C7GameData {
5 | public class CitizenType {
6 | public ID Id;
7 |
8 | // Should this citizen be the default?
9 | public bool IsDefaultCitizen;
10 |
11 | // If !IsDefaultCitizen, the index of this specialist, for looking up in
12 | // popHeads.pcx. 0 is the first non-laborer row.
13 | public int SpecialistIndex;
14 |
15 | // Like "Laborer" or "Scientist"
16 | public string SingularName;
17 |
18 | public string CivilopediaEntry;
19 |
20 | // Like "Laborers" or "Scientists"
21 | public string PluralName;
22 |
23 | // If non-null, the tech needed to use this citizen type.
24 | public ID PrerequisiteTech;
25 |
26 | // The contribution, in gold per turn, that this citizen makes towards
27 | // luxuries/happiness.
28 | public int Luxuries;
29 |
30 | // The contribution, in beakers, that this citizen makes towards teching
31 | public int Research;
32 |
33 | // The contribution, in gold per turn, that this citizen makes towards
34 | // the treasury.
35 | public int Taxes;
36 |
37 | // TODO: Figure out the details of how corruption is reduced by
38 | // policemen.
39 | public int Corruption;
40 |
41 | // The contribution, in shields per turn, that this citizen makes
42 | // towards production.
43 | public int Construction;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/CityResident.cs:
--------------------------------------------------------------------------------
1 | namespace C7GameData {
2 | public class CityResident {
3 | public CitizenType citizenType;
4 |
5 | // Only relevant if citizenType.IsDefaultCitizen == true
6 | public Tile tileWorked = Tile.NONE;
7 | public Civilization nationality;
8 | public City city;
9 |
10 | // Only relevant if citizenType.IsDefaultCitizen == true
11 | public enum Mood {
12 | Happy,
13 | Content,
14 | Unhappy
15 | };
16 | public Mood mood;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/Civilization.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace C7GameData {
5 | /**
6 | * Represents a civilization, such as the French, which can be
7 | * assigned to a player.
8 | */
9 | public enum Gender {
10 | Male,
11 | Female,
12 | }
13 |
14 | public class Civilization {
15 | public enum Trait {
16 | Militaristic,
17 | Commercial,
18 | Expansionist,
19 | Scientific,
20 | Religious,
21 | Industrious,
22 | Agricultural,
23 | Seafaring,
24 | }
25 |
26 | public Civilization() { }
27 |
28 | public Civilization(string name) {
29 | this.name = name;
30 | }
31 | public string name;
32 |
33 | // `noun` is "Americans" for "America", or "Spanish" for "Spain", etc.
34 | public string noun;
35 | public string leader;
36 | public int colorIndex;
37 | public Gender leaderGender;
38 |
39 | // Like `art\advisors\LZ_all.pcx` for the English.
40 | public string leaderArtFile;
41 |
42 | public List cityNames = new List();
43 |
44 | // The IDs of all the techs that this civ starts with.
45 | public HashSet startingTechs = new();
46 |
47 | // The traits that this civilization has.
48 | public HashSet traits = new();
49 |
50 | [JsonIgnore]
51 | public UnitPrototype uniqueUnit;
52 |
53 | private UnitPrototype GetDirectUpgrade(UnitPrototype unit) {
54 | // Check if a regular upgrade is replaced by a unique unit
55 | if (uniqueUnit != null
56 | && uniqueUnit.unique.replace != null
57 | && uniqueUnit.unique.replace == unit.upgradeTo) {
58 | return uniqueUnit;
59 | }
60 |
61 | return unit.upgradeTo;
62 | }
63 |
64 | public List GetUpgradeChain(UnitPrototype unit) {
65 | List result = [];
66 | var current = unit;
67 |
68 | while (true) {
69 | var upgrade = GetDirectUpgrade(current);
70 | if (upgrade == null) break;
71 |
72 | result.Add(upgrade);
73 | current = upgrade;
74 | }
75 |
76 | return result;
77 | }
78 |
79 | public bool IsUnitAvailable(UnitPrototype unit) {
80 | if (unit.unproducible) {
81 | return false;
82 | }
83 |
84 | // Check if unit is replaced by a unique unit
85 | if (uniqueUnit != null && uniqueUnit.unique.replace == unit) {
86 | return false;
87 | }
88 |
89 | // Check if unit is a unique unit from another civilization
90 | if (unit.unique != null && unit.unique.civilization != this) {
91 | return false;
92 | }
93 |
94 | return true;
95 | }
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/CombatResult.cs:
--------------------------------------------------------------------------------
1 | namespace C7GameData {
2 | public enum CombatResult {
3 | AttackerKilled,
4 | DefenderKilled,
5 | AttackerRetreated,
6 | DefenderRetreated,
7 | Impossible
8 | }
9 |
10 | public static class CombatResultExtensions {
11 | public static bool AttackerWon(this CombatResult cR) {
12 | return (cR == CombatResult.DefenderKilled) || (cR == CombatResult.DefenderRetreated);
13 | }
14 |
15 | public static bool DefenderWon(this CombatResult cR) {
16 | return (cR == CombatResult.AttackerKilled) || (cR == CombatResult.AttackerRetreated);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/CombatRole.cs:
--------------------------------------------------------------------------------
1 | namespace C7GameData {
2 | using System;
3 |
4 | public enum CombatRole {
5 | Attack,
6 | Defense,
7 | Bombard,
8 | BombardDefense,
9 | DefensiveBombard,
10 | DefensiveBombardDefense
11 | }
12 |
13 | public static class CombatRoleExtensions {
14 | public static bool Attacking(this CombatRole cR) {
15 | return cR == CombatRole.Attack || cR == CombatRole.Bombard || cR == CombatRole.DefensiveBombard;
16 | }
17 |
18 | public static bool Defending(this CombatRole cR) {
19 | return !cR.Attacking();
20 | }
21 |
22 | public static bool Bombarding(this CombatRole cR) {
23 | return cR == CombatRole.Bombard || cR == CombatRole.DefensiveBombard;
24 | }
25 |
26 | public static CombatRole Reversed(this CombatRole cR) {
27 | switch (cR) {
28 | case CombatRole.Attack: return CombatRole.Defense;
29 | case CombatRole.Defense: return CombatRole.Attack;
30 | case CombatRole.Bombard: return CombatRole.BombardDefense;
31 | case CombatRole.BombardDefense: return CombatRole.Bombard;
32 | case CombatRole.DefensiveBombard: return CombatRole.DefensiveBombardDefense;
33 | case CombatRole.DefensiveBombardDefense: return CombatRole.DefensiveBombard;
34 | default: throw new ArgumentOutOfRangeException("Invalid CombatRole");
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/Difficulty.cs:
--------------------------------------------------------------------------------
1 | namespace C7GameData {
2 | // https://forums.civfanatics.com/threads/ai-difficulty-level-bonuses.37490/
3 | public class Difficulty {
4 | public ID id;
5 | public string Name;
6 | public int NumberOfCitizensBornContent;
7 |
8 | // Only applies to AI; at higher difficulty levels the AI can change
9 | // governments much faster.
10 | public int MaxAiGovernmentTransitionTime;
11 |
12 | // If non-zero, the AI gets this number of the best offensive/defensive
13 | // units the AI can build at the start of the game.
14 | public int NumberOfAIDefensiveStartingUnits;
15 | public int NumberOfAIOffensiveStartingUnits;
16 |
17 | // Usually the number of extra settlers
18 | public int ExtraStartUnit1;
19 |
20 | // Usually the number of extra workers
21 | public int ExtraStartUnit2;
22 |
23 | // Bonuses for unit support.
24 | public int AdditionalFreeUnitSupport;
25 | public int UnitSupportBonusForEachSettlement;
26 |
27 | public int AttackBonusAgainstBarbarians;
28 |
29 | // The cost factor for techs, growth, and production, 10 is a neutral
30 | // value.
31 | public int AiCostFactor;
32 | public int HumanCostFactor = 10;
33 |
34 | public int PercentageOfOptimalCities;
35 |
36 | public int AIToAITradeRate;
37 | public int CorruptionPercentage;
38 |
39 | // Number of citizens quelled by military.
40 | //
41 | // See https://www.civfanatics.com/civ3/strategy/game-mechanics/the-inner-workings-of-resistance-revealed/
42 | // for details on how this is used - even though it appears to always be
43 | // 1 in the default game and scenarios, it is moddable.
44 | public int MilitaryLaw;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/ExperienceLevel.cs:
--------------------------------------------------------------------------------
1 | namespace C7GameData {
2 | using QueryCiv3.Biq;
3 |
4 | public class ExperienceLevel {
5 | public string key;
6 | public string displayName;
7 | public int baseHitPoints;
8 | public double retreatChance;
9 | public double promotionChance;
10 |
11 | public ExperienceLevel(string key, string displayName, int baseHitPoints, double retreatChance, double promotionChance) {
12 | this.key = key;
13 | this.displayName = displayName;
14 | this.baseHitPoints = baseHitPoints;
15 | this.retreatChance = retreatChance;
16 | this.promotionChance = promotionChance;
17 | }
18 |
19 | public static ExperienceLevel ImportFromCiv3(string key, EXPR expr, int levelIndex) {
20 | var promotionChances = new double[] { 0.5, 0.25, 0.125, 0.0 };
21 | return new ExperienceLevel(key, expr.Name, expr.BaseHitPoints, expr.RetreatBonus / 100.0, promotionChances[levelIndex]);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/Government.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace C7GameData {
5 | public class Government {
6 | public ID id;
7 | public string name;
8 | public string civilopediaEntry;
9 |
10 | // If non-null, the tech required to use this government.
11 | public ID prerequisiteTech;
12 |
13 | // If true, this is the starting government.
14 | public bool defaultType;
15 |
16 | // If true, this is the government used while switching governments.
17 | public bool transitionType;
18 |
19 | // The "despotism penalty" applies for this goverment; reduces all
20 | // commerce, production and food output done by citizen laborers by -1
21 | // when they produce more than 2.
22 | public bool hasTilePenalty {
23 | get => _hasTilePenalty;
24 | set {
25 | _hasTilePenalty = value;
26 | if (value) {
27 | tileModifier += TilePenalty;
28 | }
29 | }
30 | }
31 | private bool _hasTilePenalty;
32 |
33 | // +1 commerce any tile with at least 1 commerce.
34 | public bool hasTradeBonus {
35 | get => _hasTradeBonus;
36 | set {
37 | _hasTradeBonus = value;
38 | if (value) {
39 | tileModifier += TradeBonus;
40 | }
41 | }
42 | }
43 | private bool _hasTradeBonus;
44 |
45 | [JsonIgnore]
46 | public Action tileModifier;
47 |
48 | // See https://codehappy.net/apolyton/threads/46801-1.htm and
49 | // https://forums.civfanatics.com/threads/everything-about-corruption-c3c-edition.76619/.
50 | public enum CorruptionType {
51 | Minimal,
52 | Nuisance,
53 | Problematic,
54 | Rampant,
55 | Catastrophic,
56 | Communal,
57 | Off
58 | };
59 | public CorruptionType corruptionType;
60 |
61 | public enum HurryProductionType {
62 | CannotHurry,
63 | ForcedLabor,
64 | PaidLabor,
65 | };
66 | public HurryProductionType hurryingType;
67 |
68 | public int draftLimit;
69 | public int militaryPoliceLimit;
70 | public int workerRate;
71 |
72 | public bool allUnitsFree;
73 | public int freeUnitsPerTown;
74 | public int freeUnitsPerCity;
75 | public int freeUnitsPerMetropolis;
76 | public int unitCost;
77 |
78 | private static void TradeBonus(Tile.Yield yield) {
79 | if (yield.type == Tile.YieldType.Commerce && yield.baseYield > 0) {
80 | yield.bonus += 1;
81 | }
82 | }
83 |
84 | private static void TilePenalty(Tile.Yield yield) {
85 | yield.penalty += yield.baseYield > 2 ? 1 : 0;
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/IProducible.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace C7GameData {
4 | /**
5 | * Represents something that can be produced by a city.
6 | * Known examples are Buildings and UnitPrototypes.
7 | */
8 | public interface IProducible {
9 | string name { get; set; }
10 | int populationCost { get; set; }
11 | Tech requiredTech { get; set; }
12 | HashSet requiredResources { get; set; }
13 |
14 | int ShieldCost(HashSet civTraits, float costFactor);
15 |
16 | bool CanProduce(City city, HashSet accessibleResources);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/Resource.cs:
--------------------------------------------------------------------------------
1 | namespace C7GameData {
2 | public class Resource {
3 | public string Key { get; set; }
4 | public int Icon { get; set; }
5 | public int FoodBonus { get; set; }
6 | public int ShieldsBonus { get; set; }
7 | public int CommerceBonus { get; set; }
8 | public ResourceCategory Category { get; set; }
9 | public int AppearanceRatio { get; set; }
10 | public int DisappearanceRatio { get; set; }
11 | public string Name { get; set; }
12 | public string CivilopediaEntry { get; set; }
13 | public ID Prerequisite { get; set; }
14 |
15 | public static readonly Resource NONE = new Resource
16 | {
17 | Key = "NONE",
18 | Category = ResourceCategory.NONE
19 | };
20 |
21 | public override string ToString() {
22 | return $"Resource named {Name} of type {Category} with bonuses {FoodBonus}, {ShieldsBonus}, {CommerceBonus}";
23 | }
24 | }
25 |
26 | public enum ResourceCategory {
27 | BONUS,
28 | LUXURY,
29 | STRATEGIC,
30 | NONE
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/Rules.cs:
--------------------------------------------------------------------------------
1 | namespace C7GameData {
2 | public class Rules {
3 | public int MaximumResearchTime;
4 | public int MinimumResearchTime;
5 | public int ShieldValueInGold;
6 | public int ForestValueInShields;
7 | public int CitizenValueInShields;
8 | public int TurnPenaltyForEachHurrySacrifice;
9 | public int MaximumLevel1CitySize;
10 | public int MaximumLevel2CitySize;
11 | public int FoodNeededToGrowForLevel1Cities = 20;
12 | public int FoodNeededToGrowForLevel2Cities = 40;
13 | public int FoodNeededToGrowForLevel3Cities = 60;
14 | public float BuildingDiscountForCivTraits = .5f;
15 | public string StartUnitType1;
16 | public string StartUnitType2;
17 | public string ScoutUnitType;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/Save/SaveBuilding.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace C7GameData.Save {
5 | public class SaveBuilding {
6 | public enum Flag {
7 | IsCenterOfEmpire,
8 | VeteranGroundUnits,
9 | VeteranSeaUnits,
10 | MustBeCoastal,
11 | MustBeNearRiver,
12 | IncreasesLuxuryTrade,
13 | ReducesCorruption,
14 | ForbiddenPalace,
15 | IncreasesShieldsInWater,
16 | IncreasesFoodInWater,
17 | IncreasesTradeInWater,
18 | AllowsCitySize2,
19 | AllowsCitySize3,
20 | DoublesCityGrowthRate,
21 | ProvidesWalls,
22 | CanOnlyBeBuiltInTowns,
23 | }
24 |
25 | public class GreatWonderProperties {
26 | // The name of the building this building gives to every city in the
27 | // empire on on the continent (like the pyramids or the internet).
28 | public string buildingGainedInEveryCity;
29 | public string buildingGainedInEveryCityOnContinent;
30 | }
31 |
32 | public string name;
33 | public int shieldCost;
34 | public int populationCost;
35 | public ID requiredTech;
36 | public string requiredBuilding;
37 | public GreatWonderProperties? greatWonderProperties;
38 | public bool isSmallWonder;
39 | public int culturePerTurn;
40 | public int contentFacesInCity;
41 | public double combatDefenseBonus;
42 | public int maintenanceCost;
43 | public int iconRowIndex;
44 | public ID? renderedObsoleteBy;
45 |
46 | // Assorted boolean flags for the building. They're stored in this set
47 | // rather than as booleans to avoid bloating the json file.
48 | public HashSet flags = new();
49 |
50 | // The set of traits this building has. Civilizations with a matching
51 | // trait get discounted production costs.
52 | public HashSet traits = new();
53 |
54 | public HashSet requiredResources = [];
55 |
56 | public SaveBuilding() { }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/Save/SaveMap.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace C7GameData.Save {
5 |
6 | public class SaveMap {
7 | public int tilesWide, tilesTall;
8 | public bool wrapHorizontally, wrapVertically;
9 | public int techRate;
10 | public int optimalNumberOfCities;
11 | public List tiles = new List();
12 | public List startingLocations = new();
13 | public SaveMap() { }
14 |
15 | public SaveMap(GameMap map) {
16 | tilesWide = map.numTilesWide;
17 | tilesTall = map.numTilesTall;
18 | wrapHorizontally = map.wrapHorizontally;
19 | wrapVertically = map.wrapVertically;
20 | techRate = map.techRate;
21 | optimalNumberOfCities = map.optimalNumberOfCities;
22 | tiles = map.tiles.ConvertAll(tile => new SaveTile(tile));
23 | startingLocations = map.startingLocations.ConvertAll(x => new SaveTile(x));
24 | }
25 | public GameMap ToGameMap(GameData gd) {
26 | GameMap gameMap = new GameMap{
27 | numTilesWide = tilesWide,
28 | numTilesTall = tilesTall,
29 | wrapHorizontally = wrapHorizontally,
30 | wrapVertically = wrapVertically,
31 | techRate = techRate,
32 | optimalNumberOfCities = optimalNumberOfCities,
33 | tiles = tiles.ConvertAll(tile => tile.ToTile(gd.terrainTypes, gd.Resources)),
34 | };
35 | foreach (SaveTile st in startingLocations) {
36 | gameMap.startingLocations.Add(gameMap.tiles.Find(t => t.XCoordinate == st.X && t.YCoordinate == st.Y));
37 | }
38 | gameMap.computeNeighbors();
39 | gameMap.barbarianCamps = gameMap.tiles.Where(tile => tile.hasBarbarianCamp).ToList();
40 | return gameMap;
41 | }
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/Save/SaveTech.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace C7GameData.Save {
4 | // A class representing a single technology that can be researched.
5 | //
6 | // This is intended for use in save games, so it does not have recursive
7 | // references for prereqs.
8 | public class SaveTech {
9 | public enum Flag {
10 | BonusTechToFirstCivThatResearches,
11 | }
12 |
13 | public ID id;
14 | public string Name { get; set; }
15 | public string CivilopediaEntry { get; set; }
16 | public int Cost;
17 | public bool RequiredForEraAdvancement;
18 |
19 | // The civilopedia name of the era this tech is part of
20 | // (like ERA_Ancient_Times). This is what art lookups are based on.
21 | public string EraCivilopediaName { get; set; }
22 |
23 | // The path, like "Art\tech chooser\Icons\39-Mapmaking-small.pcx", of
24 | // the small icon for this tech.
25 | public string SmallIconPath;
26 |
27 | // The position of this tech within the tech advisor UI.
28 | public int X;
29 | public int Y;
30 |
31 | public List Prerequisites = new();
32 |
33 | // Assorted boolean flags for the tech. They're stored in this set
34 | // rather than as booleans to avoid bloating the json file.
35 | public HashSet flags = new();
36 |
37 | public C7GameData.Tech ToTechWithoutPrereqs() {
38 | C7GameData.Tech result = new() {
39 | id = this.id,
40 | Name = this.Name,
41 | CivilopediaEntry = this.CivilopediaEntry,
42 | Cost = this.Cost,
43 | RequiredForEraAdvancement = this.RequiredForEraAdvancement,
44 | BonusTechToFirstCivThatResearches = this.flags.Contains(SaveTech.Flag.BonusTechToFirstCivThatResearches),
45 | EraCivilopediaName = this.EraCivilopediaName,
46 | SmallIconPath = this.SmallIconPath,
47 | X = this.X,
48 | Y = this.Y,
49 | DataSource = this,
50 | };
51 | return result;
52 | }
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/Save/SaveTerraform.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace C7GameData.Save;
4 |
5 | public class SaveTerraform {
6 | public ID Id;
7 | public string Name;
8 | public string CivilopediaEntry;
9 | public int TurnsToComplete;
10 | public ID RequiredTech;
11 | public List RequiredResources = [];
12 | public UnitAction Action;
13 |
14 | public SaveTerraform() { }
15 | }
16 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/Save/SaveUnitPrototype.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace C7GameData.Save {
5 | public class SaveUnitPrototype {
6 | public class Unique {
7 | public string replace;
8 | public string civilization;
9 | };
10 |
11 | public string name { get; set; }
12 | public string artName { get; set; }
13 | public int shieldCost { get; set; }
14 | public int populationCost { get; set; }
15 | public ID requiredTech { get; set; }
16 | public int attack { get; set; }
17 | public int defense { get; set; }
18 | public int bombard { get; set; }
19 | public int movement { get; set; }
20 | public int iconIndex { get; set; }
21 |
22 | public string upgradeTo;
23 | public Unique unique;
24 | public bool unproducible;
25 |
26 | public HashSet categories = new HashSet();
27 |
28 | public HashSet actions = [];
29 |
30 | public HashSet attributes = new HashSet();
31 |
32 | public HashSet requiredResources = [];
33 |
34 | public SaveUnitPrototype() { }
35 |
36 | public SaveUnitPrototype(UnitPrototype proto) {
37 | (name, artName, shieldCost, populationCost, unproducible,
38 | attack, defense, bombard, movement, iconIndex) =
39 | (proto.name, proto.artName, proto.shieldCost, proto.populationCost, proto.unproducible,
40 | proto.attack, proto.defense, proto.bombard, proto.movement, proto.iconIndex);
41 |
42 | if (proto.requiredTech != null)
43 | requiredTech = proto.requiredTech.id;
44 |
45 | if (proto.upgradeTo != null)
46 | upgradeTo = proto.upgradeTo.name;
47 |
48 | if (proto.unique != null) {
49 | unique = new() {
50 | civilization = proto.unique.civilization.name,
51 | replace = proto.unique.replace?.name
52 | };
53 | }
54 |
55 | categories = new HashSet(proto.categories);
56 | actions = proto.actions;
57 | attributes = new HashSet(proto.attributes);
58 |
59 | requiredResources = proto.requiredResources.Select(r => r.Key).ToHashSet();
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/Save/TileLocation.cs:
--------------------------------------------------------------------------------
1 | namespace C7GameData.Save {
2 | public struct TileLocation {
3 | public int X, Y;
4 | public TileLocation(int X, int Y) {
5 | this.X = X;
6 | this.Y = Y;
7 | }
8 | public TileLocation(Tile tile) {
9 | X = tile.XCoordinate;
10 | Y = tile.YCoordinate;
11 | }
12 | public TileLocation() {
13 | X = -1;
14 | Y = -1;
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/StrengthBonus.cs:
--------------------------------------------------------------------------------
1 | namespace C7GameData {
2 | using System.Collections.Generic;
3 |
4 | public struct StrengthBonus {
5 | public string description;
6 | public double amount; // e.g. 0.25 = +25% strength
7 |
8 | public StrengthBonus(string description, double amount) {
9 | this.description = description;
10 | this.amount = amount;
11 | }
12 |
13 | // Converts a list of strength bonuses to a multiplier on strength. For example, a list of two bonuses of +10% and +25% would return a
14 | // multiplier of 1.35. Multipliers cannot be less than zero.
15 | public static double ListToMultiplier(IEnumerable bonuses) {
16 | double m = 1.0;
17 | foreach (StrengthBonus bonus in bonuses)
18 | m += bonus.amount;
19 | return (m >= 0.0) ? m : 0.0;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/Tech.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using C7GameData.Save;
3 |
4 | namespace C7GameData {
5 | // The in-game representation of a tech that can be researched.
6 | public class Tech {
7 | public ID id;
8 | public string Name { get; set; }
9 | public string CivilopediaEntry { get; set; }
10 | public int Cost;
11 | public bool RequiredForEraAdvancement;
12 | public bool BonusTechToFirstCivThatResearches;
13 |
14 | // The civilopedia name of the era this tech is part of
15 | // (like ERA_Ancient_Times). This is what art lookups are based on.
16 | public string EraCivilopediaName { get; set; }
17 |
18 | // The path, like "Art\tech chooser\Icons\39-Mapmaking-small.pcx", of
19 | // the small icon for this tech.
20 | public string SmallIconPath;
21 |
22 | // The position of this tech within the tech advisor UI.
23 | public int X;
24 | public int Y;
25 |
26 | public List Prerequisites = new();
27 |
28 | // The backing save tech, for serialization purposes. This should not be
29 | // made public - add accessors or fields for what you need.
30 | public SaveTech DataSource { private get; set; }
31 |
32 | public C7GameData.Save.SaveTech ToSaveTech() {
33 | return DataSource;
34 | }
35 |
36 | public void FillInPrereqs(List saveTechs, List techs) {
37 | SaveTech st = saveTechs.Find(st => st.id == this.id);
38 |
39 | foreach (ID prereq in st.Prerequisites) {
40 | this.Prerequisites.Add(techs.Find(t => t.id == prereq));
41 | }
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/TerrainImprovement.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace C7GameData {
4 | public class TerrainImprovement {
5 | public enum Layer {
6 | Roads,
7 | ResourceDevelopment, // mine, irrigation
8 | Holdings, // outpost, radar tower, fortress, barricade
9 | }
10 |
11 | private static Dictionary improvementByKey = [];
12 |
13 | public static readonly TerrainImprovement road = new(nameof(road), Layer.Roads, movementCost: 1.0f / 3);
14 | public static readonly TerrainImprovement railroad = new(nameof(railroad), Layer.Roads, road, movementCost: 0);
15 | public static readonly TerrainImprovement mine = new(nameof(mine), Layer.ResourceDevelopment);
16 | public static readonly TerrainImprovement irrigation = new(nameof(irrigation), Layer.ResourceDevelopment);
17 | public static readonly TerrainImprovement fortress = new(nameof(fortress), Layer.Holdings, defenseBonus: new(nameof(fortress), 0.5));
18 | public static readonly TerrainImprovement barricade = new(nameof(barricade), Layer.Holdings, fortress, defenseBonus: new(nameof(barricade), 1));
19 |
20 | public readonly string key;
21 |
22 | public readonly Layer layer;
23 | public readonly StrengthBonus? defenseBonus;
24 |
25 | // A terrain improvement with negative movement cost shouldn't affect the tile movement cost
26 | public readonly float movementCost = -1;
27 |
28 | // In the default ruleset, Road upgrades to Railroad and Fortress upgrades to Barricade.
29 | // The upgrade relationship affects:
30 | // 1) confirmation dialog when replacing improvements (if an improvement is replaced with its upgrade, there is no need for a confirmation)
31 | // 2) availability of an improvement (a player can't build an upgraded improvement without building a base improvement)
32 | // 3) result of pillaging (after pillaging, an upgraded improvement will downgrade)
33 | public readonly TerrainImprovement upgradesFrom;
34 |
35 | public TerrainImprovement(
36 | string key,
37 | Layer layer,
38 | TerrainImprovement upgradesFrom = null,
39 | StrengthBonus? defenseBonus = null,
40 | float movementCost = -1
41 | ) {
42 | this.key = key;
43 | this.layer = layer;
44 | this.defenseBonus = defenseBonus;
45 | this.upgradesFrom = upgradesFrom;
46 | this.movementCost = movementCost;
47 |
48 | improvementByKey[key] = this;
49 | }
50 |
51 | public static TerrainImprovement FromKey(string key) {
52 | return improvementByKey[key];
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/TradeOffer.cs:
--------------------------------------------------------------------------------
1 |
2 | using System.Collections.Generic;
3 |
4 | namespace C7GameData {
5 | // A class representing one side of a diplomatic agreement between two civs.
6 | public class TradeOffer {
7 | // True if two players involved in the agreement were at war prior to
8 | // the agreement.
9 | public bool partOfPeaceTreaty = false;
10 |
11 | public int? gold = null;
12 | public List techs = new();
13 |
14 | // Calculate how much this trade offer is worth for a given player. This
15 | // has to be per-player because tech costs vary based on how many civs
16 | // a player have already researched the tech.
17 | public int GoldEquivalentFor(GameData gameData, Player p) {
18 | int result = 0;
19 | if (gold.HasValue) {
20 | result += gold.Value;
21 | }
22 | foreach (Tech t in techs) {
23 | result += gameData.TechCostFor(t, p);
24 | }
25 |
26 | return result;
27 | }
28 |
29 | public void Clear() {
30 | partOfPeaceTreaty = false;
31 | gold = null;
32 | techs.Clear();
33 | }
34 |
35 | public string ToString() {
36 | List pieces = new();
37 | if (partOfPeaceTreaty) {
38 | pieces.Add("peace treaty");
39 | }
40 | if (gold != null) {
41 | pieces.Add($"{gold.Value} gold");
42 | }
43 | foreach (Tech t in techs) {
44 | pieces.Add(t.Name);
45 | }
46 | return string.Join(",", pieces);
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/UnitAI.cs:
--------------------------------------------------------------------------------
1 | using C7GameData;
2 | using C7GameData.AIData;
3 | using System;
4 |
5 | namespace C7GameData {
6 | //Not-fully-fleshed out player unit AI.
7 | //Right now it's kind of random to just add some appearance
8 | //of stuff being done. I.e. it kinda sucks. But that's okay.
9 | //It has to start somewhere, right?
10 | public interface UnitAI {
11 | enum Result {
12 | Done,
13 | InProgress,
14 | Error,
15 | };
16 |
17 | public Result PlayTurn(Player player, MapUnit unit) {
18 | while (unit.movementPoints.canMove && !unit.isFortified) {
19 | Result result = PlayTurnImpl(player, unit);
20 | if (result == Result.Error || result == Result.Done) {
21 | return result;
22 | }
23 | }
24 | return Result.InProgress;
25 | }
26 |
27 | // To be implemented by each AI subclass.
28 | protected abstract Result PlayTurnImpl(Player player, MapUnit unit);
29 |
30 | // Provide a string representation of the current AI plan.
31 | string SummarizePlan();
32 |
33 | // Do any bookkeeping required when the unit is destroyed. For example,
34 | // if an escort is destroyed, update the unit being escorted to reflect
35 | // that it no longer has an escort.
36 | void UpdateOnDeath();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/C7Engine/C7GameData/readme.md:
--------------------------------------------------------------------------------
1 | # C7 Game Data
2 |
3 | This namespace is intended to store the game data. There will be some top level object, and various sub-objects, e.g. units, maps, cities, etc.
4 |
5 | The current thought is the UI will interact with the C7 Engine, and the C7 Engine will update, or read from, the C7 Game Data as needed. This Game Data would thus be the master copy of the game state.
6 |
7 | This may be tweaked in the future, but this is a starting point that might be sustainable, and as we're increasingly getting to the point where we need some data, why not take a stab at it?
--------------------------------------------------------------------------------
/C7Engine/C7Settings.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using IniParser.Exceptions;
3 |
4 | namespace C7Engine {
5 | using IniParser;
6 | using IniParser.Model;
7 |
8 | public class C7Settings {
9 | private const string SETTINGS_FILE_NAME = "C7.ini";
10 | public static IniData settings;
11 |
12 | public static void LoadSettings() {
13 | try {
14 | settings = new FileIniDataParser().ReadFile(SETTINGS_FILE_NAME);
15 | } catch (ParsingException) {
16 | //First run. The file doesn't exist. That's okay. We'll use sensible defaults.
17 | settings = new IniData();
18 | SaveSettings();
19 | }
20 | }
21 |
22 | public static void SaveSettings() {
23 | new FileIniDataParser().WriteFile(SETTINGS_FILE_NAME, settings);
24 | }
25 |
26 | public static void SetValue(string section, string key, string value) {
27 | if (settings == null) {
28 | LoadSettings();
29 | }
30 | settings[section][key] = value;
31 | }
32 |
33 | public static string GetSettingValue(string section, string key) {
34 | if (settings == null) {
35 | LoadSettings();
36 | }
37 | return settings[section][key];
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/C7Engine/EntryPoints/CreateGame.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using C7GameData;
4 | using C7GameData.Save;
5 |
6 | namespace C7Engine {
7 |
8 | public class CreateGame {
9 | /**
10 | * For now, I'm making the methods that the C7 client can call be static.
11 | * We may want a different solution in the end, but this lets us start prototyping
12 | * quickly. By keeping all the client-callable APIs in the EntryPoints folder,
13 | * hopefully it won't be too much of a goose hunt to refactor it later if we decide to do so.
14 | **/
15 | public static Player createGame(string loadFilePath, string defaultBicPath,
16 | Func getPediaIconsPath) {
17 | EngineStorage.createThread();
18 |
19 | SaveGame save = SaveManager.LoadSave(loadFilePath, defaultBicPath, getPediaIconsPath);
20 | GameData gameData = save.ToGameData();
21 |
22 | EngineStorage.gameData = gameData;
23 |
24 | // TODO: (pcen) initially, in the false branch I assigned gameData.CreateDummyGameData
25 | // to humanPlayer, but this is not correct since there are already players and units in
26 | // the .sav - instead, we should remove CreateDummyGameData and implement simple save
27 | // generation using the new SaveGame class. This would be difficult before due to GameData's
28 | // members containing numerous references to eachother, but with SaveGame, each entity is
29 | // only defined once in the save file, and references to it are stored as IDs making it easy
30 | // to generate and modify valid save files.
31 | Player humanPlayer = gameData.players.Any(p => p.isHuman) switch {
32 | true => gameData.players.Find(p => p.isHuman),
33 | false => throw new Exception($"{loadFilePath} does not contain a human player"),
34 | };
35 |
36 | EngineStorage.uiControllerID = humanPlayer.id;
37 | TurnHandling.OnBeginTurn(); // Call for the first turn
38 | TurnHandling.AdvanceTurn();
39 |
40 | return humanPlayer;
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/C7Engine/SaveManager.cs:
--------------------------------------------------------------------------------
1 | namespace C7Engine {
2 | using System;
3 | using System.IO;
4 | using C7GameData;
5 | using C7GameData.Save;
6 |
7 | enum SaveFileFormat {
8 | Sav,
9 | Biq,
10 | C7,
11 | Invalid,
12 | }
13 |
14 | // The engine performs all save file creating, reading, and updating
15 | // via the SaveManager
16 | public static class SaveManager {
17 | private static SaveFileFormat getFileFormat(string path) {
18 | return Path.GetExtension(path).ToUpper() switch {
19 | ".SAV" => SaveFileFormat.Sav,
20 | ".BIQ" => SaveFileFormat.Biq,
21 | ".JSON" => SaveFileFormat.C7,
22 | ".ZIP" => SaveFileFormat.C7,
23 | _ => SaveFileFormat.Invalid,
24 | };
25 | }
26 |
27 | // Load and initialize a save
28 | public static SaveGame LoadSave(string path, string bicPath, Func getPediaIconsPath) {
29 | SaveGame save = getFileFormat(path) switch {
30 | SaveFileFormat.Sav => ImportCiv3.ImportSav(path, bicPath, getPediaIconsPath),
31 | SaveFileFormat.Biq => ImportCiv3.ImportBiq(path, bicPath, getPediaIconsPath),
32 | SaveFileFormat.C7 => SaveGame.Load(path, getPediaIconsPath),
33 | _ => throw new FileLoadException("invalid save format"),
34 | };
35 | return save;
36 | }
37 |
38 | public static void Save(string path) {
39 | GameData gameData = EngineStorage.gameData;
40 | SaveGame save = SaveGame.FromGameData(gameData);
41 | save.Save(path);
42 | }
43 |
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/C7Engine/Weighting.cs:
--------------------------------------------------------------------------------
1 | namespace C7Engine {
2 | ///
3 | /// For weighted actions, this class is used to help determine how the AI makes its decision.
4 | /// The idea is that for some things, the AI should always choose what it considers the best option. But for other
5 | /// things, it should make a weighted decision so that there's some degree of randomness.
6 | ///
7 | /// This both reflects human decision making, and makes things more interesting than if the AI always did the same
8 | /// thing.
9 | ///
10 | /// In theory, with a good AI, ALWAYS_CHOOSE_HIGHEST_SCORE should give the strongest AI results. Quadratic weighting
11 | /// squares the weights, and linear doesn't. So e.g. if the weights for two options are 5 and 4, with always-highest,
12 | /// it will always choose the 5 option. With linear, it will choose the 5 option 55.5% of the time. With quadratic,
13 | /// it will choose the 5 option 25/41sts of the time, or 61% of the time. Which is less of a boost than I expected,
14 | /// but is still a boost.
15 | ///
16 | /// If the results are less close, the gap grows. If it's 7 vs 3, linear gives 70/30, quadratic gives 49/58, or 84%.
17 | /// At 9 vs 1, it's 90/10 versus 98.8% for quadratic. So it does work as intended - if the AI is pretty sure, it
18 | /// is much more likely to go with what it thinks is best; if it's only sort of sure, it's may second guess itself.
19 | ///
20 | public enum Weighting {
21 | ALWAYS_CHOOSE_HIGHEST_SCORE,
22 | WEIGHTED_QUADRATIC,
23 | WEIGHTED_LINEAR,
24 | }
25 |
26 | public static class WeightAdjuster {
27 | public static double AdjustWeightByFactor(double baseWeight, Weighting weighting) {
28 | if (weighting == Weighting.WEIGHTED_QUADRATIC) {
29 | return baseWeight * baseWeight;
30 | } else {
31 | return baseWeight;
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/C7Engine/readme.md:
--------------------------------------------------------------------------------
1 | # C7 Engine
2 |
3 | The C7 Engine is the core gameplay mechanic part of the code base. The UI (in the C7 folder) will call the engine when the player takes actions, and the engine will update the game state and perhaps send a reply back (e.g. a result of combat). The C7 Engine will interact with the C7 Game Data, which will store the state of the game.
4 |
5 | This should hopefully keep the UI and the engine and the data somewhat decoupled, and facilitate both maintenance and exploring networking options.
6 |
7 | ## Layout
8 |
9 | I've added a folder, EntryPoints, which is intended to be where we put all the methods that can be invoked in the engine by the game. I'm anticipating that the engine might become slightly complex someday, and having all the places you can call the engine be separate from all the helper methods and deep calculations it performs could be useful.
--------------------------------------------------------------------------------
/ConvertCiv3Media/ConvertCiv3Media.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ConvertCiv3Media/test.ps1:
--------------------------------------------------------------------------------
1 | # Testing the ini-parser NuGet package against unit ini files
2 |
3 | $NugetPath = '/Users/jim/.nuget/packages'
4 | $Civ3Root = '/Users/jim/civ3'
5 | $IniPath = $Civ3Root + '/Art/Units/Samurai/Samurai.INI'
6 |
7 | Add-Type -Path ($NugetPath + '/ini-parser/3.4.0/lib/net20/INIFileParser.dll')
8 | <#
9 |
10 | $parser = New-Object IniParser.FileIniDataParser
11 | $data = $parser.ReadFile($IniPath)
12 | $data.Sections["Animations"] | Select-Object KeyName, Value | Format-List
13 | #>
14 |
15 | Add-Type -Path ('bin/Debug/netstandard2.0/ReadCivData.ConvertCiv3Media.dll')
16 |
17 | $foo = New-Object ReadCivData.ConvertCiv3Media.Civ3UnitSprite($IniPath)
--------------------------------------------------------------------------------
/EngineTests/AI/Pathing/BinaryMinHeapTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using C7Engine.Pathing;
4 | using Xunit;
5 |
6 | namespace EngineTests {
7 | public class BinaryMinHeapTest {
8 | private void checkBunchInsert(List list) where T : IComparable {
9 | BinaryMinHeap heap = new BinaryMinHeap();
10 | foreach (T v in list) {
11 | heap.insert(v);
12 | }
13 |
14 | Assert.Equal(list.Count, heap.count);
15 |
16 | List result = new List();
17 | while (heap.count > 0) {
18 | result.Add(heap.extract());
19 | }
20 | Assert.Equal(list.Count, result.Count);
21 |
22 | List sortedCopy = new List(list);
23 | sortedCopy.Sort();
24 | Assert.Equal(result, sortedCopy);
25 | }
26 |
27 | public List generateLargeList() {
28 | List lst = new List();
29 | for (int i = 0; i < 1000; ++i) {
30 | lst.Add(i);
31 | }
32 |
33 | Random random = new Random(0x1337);
34 | for (int i = lst.Count - 1; i > 1; --i) {
35 | var pos = random.Next(i - 1);
36 | (lst[i], lst[pos]) = (lst[pos], lst[i]);
37 | }
38 |
39 | return lst;
40 | }
41 |
42 | [Fact]
43 | public void testBinaryMinHeap() {
44 | List singleValue = new List(){1};
45 | List fewValues = new List() {9, 1, 3, 5, 6};
46 | List fewRepeatingValues = new List() {1, 5, 6, 1, 5, 6};
47 | List floatValues = new List() {float.MinValue, 0.1f, 0, 1, -1, 1000};
48 |
49 | checkBunchInsert(singleValue);
50 | checkBunchInsert(fewValues);
51 | checkBunchInsert(fewRepeatingValues);
52 | checkBunchInsert(floatValues);
53 | checkBunchInsert(generateLargeList());
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/EngineTests/EngineTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | false
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | runtime; build; native; contentfiles; analyzers; buildtransitive
15 | all
16 |
17 |
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 | all
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/EngineTests/GameData/HeightMapTest.cs:
--------------------------------------------------------------------------------
1 | using C7GameData;
2 | using Xunit;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 | using SixLabors.ImageSharp;
8 | using SixLabors.ImageSharp.PixelFormats;
9 | using SixLabors.ImageSharp.Processing;
10 |
11 |
12 | namespace C7GameDataTests;
13 |
14 | public class HeightMapTest {
15 | public static void SaveNoiseMapAsPng(List heightMaps, string filePath) {
16 | using (Image image = new(heightMaps[0].mapWidth, heightMaps[0].mapHeight * heightMaps.Count)) {
17 | for (int i = 0; i < heightMaps.Count; ++i) {
18 | for (int x = 0; x < heightMaps[i].mapWidth; x++) {
19 | for (int y = 0; y < heightMaps[i].mapHeight; y++) {
20 | byte grayScale = (byte)(heightMaps[i].GetHeight(x, y));
21 | image[x, y + i * heightMaps[0].mapHeight] = new Bgra32(grayScale, grayScale, grayScale, (byte)255);
22 | }
23 | }
24 | }
25 |
26 | image.SaveAsPng(filePath);
27 | }
28 |
29 | Console.WriteLine($"Noise map saved to: {filePath}");
30 | }
31 |
32 | [Fact]
33 | public void HeightMapGeneration() {
34 | List hms = new();
35 |
36 | // Roughly archipelago shaped.
37 | for (int i = 0; i < 4; ++i) {
38 | hms.Add(new HeightMap(seed: new Random().Next(int.MaxValue), width: 100, height: 100, scale: 0.2));
39 | }
40 |
41 | // Roughly continents shaped.
42 | for (int i = 0; i < 4; ++i) {
43 | hms.Add(new HeightMap(seed: new Random().Next(int.MaxValue), width: 100, height: 100, scale: 0.05));
44 | }
45 |
46 | // Roughly pangaea shaped.
47 | for (int i = 0; i < 4; ++i) {
48 | hms.Add(new HeightMap(seed: new Random().Next(int.MaxValue), width: 100, height: 100, scale: 0.02));
49 | }
50 |
51 | // Save the noise maps as a png to make it easier to understand how the
52 | // scale parameter (and other height map parameters) change things.
53 | SaveNoiseMapAsPng(hms, "debug_noise_map.png");
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/EngineTests/GameData/IDFactoryTests.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Xunit;
3 | using C7GameData;
4 | using C7GameData.Save;
5 |
6 | public class IDFactoryTests {
7 | [Fact]
8 | public void CreateID_FirstID_ReturnsCorrectID() {
9 | var factory = new ID.Factory();
10 |
11 | var id = factory.CreateID("unit");
12 |
13 | Assert.Equal("unit-1", id.ToString());
14 | }
15 |
16 | [Fact]
17 | public void CreateID_MultipleIDs_IncrementCorrectly() {
18 | var factory = new ID.Factory();
19 |
20 | ID id1 = factory.CreateID("unit");
21 | ID id2 = factory.CreateID("unit");
22 | ID id3 = factory.CreateID("unit");
23 |
24 | Assert.Equal("unit-1", id1.ToString());
25 | Assert.Equal("unit-2", id2.ToString());
26 | Assert.Equal("unit-3", id3.ToString());
27 | }
28 |
29 | [Fact]
30 | public void Factory_InitializesWithSaveGame_CorrectlyCountsExistingIDs() {
31 | var warrior1 = new SaveUnit{id=ID.FromString("warrior-1")};
32 | var warrior2 = new SaveUnit{id=ID.FromString("warrior-3")};
33 | var worker = new SaveUnit{id=ID.FromString("worker-2")};
34 | var city = new SaveCity{id=ID.FromString("city-1")};
35 |
36 | var saveGame = new SaveGame {
37 | Units = new List { warrior1, warrior2, worker },
38 | Cities = new List { city }
39 | };
40 |
41 | var factory = new ID.Factory(saveGame);
42 |
43 | ID newUnitID = factory.CreateID("warrior");
44 | ID newWorkerID = factory.CreateID("worker");
45 | ID newCityID = factory.CreateID("city");
46 |
47 | Assert.Equal("warrior-4", newUnitID.ToString());
48 | Assert.Equal("worker-3", newWorkerID.ToString());
49 | Assert.Equal("city-2", newCityID.ToString());
50 | }
51 |
52 | [Fact]
53 | public void Factory_HandlesEmptySaveGame_CreatesNewIDs() {
54 | var saveGame = new SaveGame();
55 | var factory = new ID.Factory(saveGame);
56 |
57 | ID id = factory.CreateID("unit");
58 |
59 | Assert.Equal("unit-1", id.ToString());
60 | }
61 | }
62 |
63 |
--------------------------------------------------------------------------------
/EngineTests/data/output/info.txt:
--------------------------------------------------------------------------------
1 | Tests may create files in this directory but they are ignored and should not be checked in.
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 C7-Game
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/City.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct CITY {
7 | public int Length;
8 | public byte HasWalls;
9 | public byte HasPalace;
10 |
11 | private fixed byte Text[24];
12 | public string Name { get => Util.GetString(ref this, 6, 30); }
13 |
14 | public int OwnerType; // 0: None, 1: Barb, 2: Civ, 3: Player
15 | public int NumberOfBuildings;
16 |
17 | /*
18 | Dynamic length gap
19 | In BIQ files, for each buildings, 4 bytes of space are used to point to building id
20 | Data is instead stored in 2d array CityBuilding
21 | */
22 |
23 | public int Culture;
24 | public int Owner;
25 | public int Size;
26 | public int X;
27 | public int Y;
28 | public int CityLevel;
29 | public int BorderLevel;
30 | public int UseAutoName;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/Clny.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct CLNY {
7 | public int Length;
8 | public int OwnerType;
9 | public int Owner;
10 | public int X;
11 | public int Y;
12 | public int ImprovementType;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/Cont.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct CONT {
7 | public int Length;
8 | public int Type; // 0: Water, 1: Land
9 | public int NumberOfTiles;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/Ctzn.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct CTZN {
7 | public int Length;
8 | public int DefaultCitizen;
9 |
10 | private fixed byte Text[96];
11 | public string SingularName { get => Util.GetString(ref this, 8, 32); }
12 | public string CivilopediaEntry { get => Util.GetString(ref this, 40, 32); }
13 | public string PluralName { get => Util.GetString(ref this, 72, 32); }
14 |
15 | public int Prerequisite;
16 | public int Luxuries;
17 | public int Research;
18 | public int Taxes;
19 | public int Corruption;
20 | public int Construction;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/Cult.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct CULT {
7 | public int Length;
8 |
9 | private fixed byte Text[64];
10 | public string Name { get => Util.GetString(ref this, 4, 64); }
11 |
12 | public int ChanceOfSuccessfulPropaganda;
13 | public int CultureRatioPercentage;
14 | public int CultureRatioDenominator;
15 | public int CultureRatioNumerator;
16 | public int InitialResistanceChance;
17 | public int ContinuedResistanceChance;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/Diff.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct DIFF {
7 | public int Length;
8 |
9 | private fixed byte Text[64];
10 | public string Name { get => Util.GetString(ref this, 4, 64); }
11 |
12 | public int NumberOfCitizensBornContent;
13 | public int MaxGovernmentTransitionTime;
14 | public int NumberOfAIDefensiveStartingUnits;
15 | public int NumberOfAIOffensiveStartingUnits;
16 | public int ExtraStartUnit1;
17 | public int ExtraStartUnit2;
18 | public int AdditionalFreeSupport;
19 | public int UnitSupportBonusForEachSettlement;
20 | public int AttackBonusAgainstBarbarians;
21 | public int CostFactor;
22 | public int PercentageOfOptimalCities;
23 | public int AIToAITradeRate;
24 | public int CorruptionPercentage;
25 | public int MilitaryLaw;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/Eras.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct ERAS {
7 | public int Length;
8 |
9 | private fixed byte Text[256];
10 | public string Name { get => Util.GetString(ref this, 4, 64); }
11 | public string CivilopediaEntry { get => Util.GetString(ref this, 68, 32); }
12 | public string Researcher1 { get => Util.GetString(ref this, 100, 32); }
13 | public string Researcher2 { get => Util.GetString(ref this, 132, 32); }
14 | public string Researcher3 { get => Util.GetString(ref this, 164, 32); }
15 | public string Researcher4 { get => Util.GetString(ref this, 196, 32); }
16 | public string Researcher5 { get => Util.GetString(ref this, 228, 32); }
17 |
18 | public int NumberOfUsedResearcherNames;
19 | private fixed byte Unknown[4];
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/Espn.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct ESPN {
7 | public int Length;
8 |
9 | private fixed byte Text[224];
10 | public string Description { get => Util.GetString(ref this, 4, 128); }
11 | public string Name { get => Util.GetString(ref this, 132, 64); }
12 | public string CivilopediaEntry { get => Util.GetString(ref this, 196, 32); }
13 |
14 | private fixed byte Flags[4];
15 | public bool Diplomat { get => Util.GetFlag(Flags[0], 0); }
16 | public bool Spy { get => Util.GetFlag(Flags[0], 1); }
17 |
18 | public int BaseCost;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/Expr.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct EXPR {
7 | public int Length;
8 |
9 | private fixed byte Text[32];
10 | public string Name { get => Util.GetString(ref this, 4, 32); }
11 |
12 | public int BaseHitPoints;
13 | public int RetreatBonus;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/Flav.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct FLAVFLAV {
7 | private fixed int Relationships[7];
8 | public int this[int index] { get => Relationships[index]; }
9 | }
10 |
11 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
12 | public unsafe struct FLAV {
13 | private fixed byte UnknownBuffer[4];
14 |
15 | private fixed byte Text[256];
16 | public string Name { get => Util.GetString(ref this, 4, 256); }
17 |
18 | // So FLAV should be considered a dynamic section, because it has an amount of flavor relationship data determined by NumberOfFlavors
19 | // However, NumberOfFlavors seems to always be 7, and it doesn't look like flavors can be added to or removed in the Civ3Editor
20 | // So for now, I'm treating FLAV as static until a counterexample is discovered
21 | public int NumberOfFlavors;
22 | public FLAVFLAV FlavorRelationship;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/Good.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct GOOD {
7 | public int Length;
8 |
9 | private fixed byte Text[56];
10 | public string Name { get => Util.GetString(ref this, 4, 24); }
11 | public string CivilopediaEntry { get => Util.GetString(ref this, 28, 32); }
12 |
13 | public int Type;
14 | public int AppearanceRatio;
15 | public int DisappearanceProbability;
16 | public int Icon;
17 | public int Prerequisite;
18 | public int FoodBonus;
19 | public int ShieldsBonus;
20 | public int CommerceBonus;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/Lead.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct LEAD_Unit {
7 | public int NumberOfStartUnits;
8 | public int UnitType;
9 | }
10 |
11 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
12 | public unsafe struct LEAD {
13 | public int Length;
14 | public int CustomCivData; // 0: don't use, 1: use
15 | public int HumanPlayer; // 0: no, 1: yes
16 |
17 | private fixed byte Text[32];
18 | public string Name { get => Util.GetString(ref this, 12, 32); }
19 |
20 | private fixed byte UnknownBuffer[8]; // Are we sure this buffer isn't just a continuation of the text?
21 | public int NumberOfStartUnitTypes;
22 |
23 | /*
24 | Dynamic length gap
25 | In BIQ files, for each starting unit type, there are 2 ints: one for the amount of that unit and one for its ID
26 | Data is instead stored in 2d array of LEADPRTO
27 | */
28 |
29 | public int GenderOfLeaderName;
30 | public int NumberOfStartingTechnologies;
31 |
32 | /*
33 | Dynamic length gap
34 | In BIQ files, for each starting tech, there is 1 int for that tech's ID
35 | Data is instead stored in 2d array LeadTech
36 | */
37 |
38 | public int Difficulty;
39 | public int InitialEra;
40 | public int StartCash; // $$$$$$$$$$$$$$$$$
41 | public int Government;
42 | public int Civ; // -3: any, -2: random
43 | public int Color;
44 | public int SkipFirstTurn;
45 | private fixed byte UnknownBuffer2[4];
46 | public byte StartEmbassies;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/Sloc.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct SLOC {
7 | public int Length;
8 | public int OwnerType; // 0: None, 1: Barbarian, 2: Civ, 3: Player
9 | public int Owner; // Race ID
10 | public int X;
11 | public int Y;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/Tech.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct TECH {
7 | public int Length;
8 |
9 | private fixed byte Text[64];
10 | public string Name { get => Util.GetString(ref this, 4, 32); }
11 | public string CivilopediaEntry { get => Util.GetString(ref this, 36, 32); }
12 |
13 | public int Cost;
14 | public int Era;
15 | public int AdvanceIcon;
16 | public int X;
17 | public int Y;
18 | public int Prerequisite1;
19 | public int Prerequisite2;
20 | public int Prerequisite3;
21 | public int Prerequisite4;
22 |
23 | private fixed byte Flags[4];
24 | public bool EnablesDiplomats { get => Util.GetFlag(Flags[0], 0); }
25 | public bool EnablesIrrigationEverywhere { get => Util.GetFlag(Flags[0], 1); }
26 | public bool EnablesBridges { get => Util.GetFlag(Flags[0], 2); }
27 | public bool DisablesDiseases { get => Util.GetFlag(Flags[0], 3); }
28 | public bool EnablesConscription { get => Util.GetFlag(Flags[0], 4); }
29 | public bool EnablesMobilization { get => Util.GetFlag(Flags[0], 5); }
30 | public bool EnablesRecycling { get => Util.GetFlag(Flags[0], 6); }
31 | public bool EnablesPrecisionBombing { get => Util.GetFlag(Flags[0], 7); }
32 | public bool EnablesMutualProtection { get => Util.GetFlag(Flags[1], 0); }
33 | public bool EnablesRightOfPassage { get => Util.GetFlag(Flags[1], 1); }
34 | public bool EnablesMilitaryAlliances { get => Util.GetFlag(Flags[1], 2); }
35 | public bool EnablesTradeEmbargoes { get => Util.GetFlag(Flags[1], 3); }
36 | public bool DoublesWealthProduction { get => Util.GetFlag(Flags[1], 4); }
37 | public bool EnablesTradeOverSea { get => Util.GetFlag(Flags[1], 5); }
38 | public bool EnablesTradeOverOcean { get => Util.GetFlag(Flags[1], 6); }
39 | public bool EnablesMapTrading { get => Util.GetFlag(Flags[1], 7); }
40 | public bool EnablesCommunicationTrading { get => Util.GetFlag(Flags[2], 0); }
41 | public bool NotRequiredForEraAdvancement { get => Util.GetFlag(Flags[2], 1); }
42 | public bool DoublesWorkerRate { get => Util.GetFlag(Flags[2], 2); }
43 | public bool CannotBeTraded { get => Util.GetFlag(Flags[2], 3); }
44 | public bool PermitsSacrifice { get => Util.GetFlag(Flags[2], 4); }
45 | public bool BonusTechToFirstCivThatResearches { get => Util.GetFlag(Flags[2], 5); }
46 | public bool RevealsWorldMap { get => Util.GetFlag(Flags[2], 6); }
47 |
48 | public int Flavors;
49 | private fixed byte UnknownBuffer[4];
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/Terr.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct TERR {
7 | public int Length;
8 | public int NumPossibleResources;
9 | /*
10 | In the TERR section of Civ 3 BIQ files, there is a bit array for possible resources
11 | One bit for each resource (as defined in GOOD), round up to the nearest byte for space
12 | Because this is a dynamic-length section, it is stored separately from this struct
13 | */
14 |
15 | private fixed byte Text[64];
16 | public string Name { get => Util.GetString(ref this, 8, 32); }
17 | public string CivilopediaEntry { get => Util.GetString(ref this, 40, 32); }
18 |
19 | public int IrrigationBonus;
20 | public int MiningBonus;
21 | public int RoadBonus;
22 | public int DefenseBonus;
23 | public int MovementCost;
24 | public int Food;
25 | public int Shields;
26 | public int Commerce;
27 | // Which worker job (TFRM) can be performed on this terrain type
28 | private int WorkerJobAllowed;
29 | public readonly bool CanPlantForest { get => WorkerJobAllowed == 5; }
30 | public readonly bool CanChopForest { get => WorkerJobAllowed == 6; }
31 | public readonly bool CanClearWetlands { get => WorkerJobAllowed == 7; }
32 | // Which Terrain this Terrain becomes if affected by pollution. -1 = not affected. 14 = Base Terrain Type (probably 12 in Vanilla/PTW)
33 | public int PollutionEffect;
34 | public byte AllowCities;
35 | public byte AllowColonies;
36 | public byte Impassable;
37 | public byte ImpassableByWheeled;
38 | public byte AllowAirfields;
39 | public byte AllowForts;
40 | public byte AllowOutposts;
41 | public byte AllowRadarTowers;
42 | private int Unknown;
43 | public byte LandmarkEnabled;
44 | public int LandmarkFood;
45 | public int LandmarkShields;
46 | public int LandmarkCommerce;
47 | public int LandmarkIrrigationBonus;
48 | public int LandmarkMiningBonus;
49 | public int LandmarkRoadBonus;
50 | public int LandmarkMovementBonus;
51 | public int LandmarkDefensiveBonus;
52 |
53 | private fixed byte Text2[64];
54 | public string LandmarkName { get => Util.GetString(ref this, 157, 32); }
55 | public string LandmarkCivilopediaEntry { get => Util.GetString(ref this, 189, 32); }
56 |
57 | private int Unknown2;
58 | public int TerrainFlags;
59 | public int DiseaseStrength;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/Tfrm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct TFRM {
7 | public int Length;
8 |
9 | private fixed byte Text[64];
10 | public string Name { get => Util.GetString(ref this, 4, 32); }
11 | public string CivilopediaEntry { get => Util.GetString(ref this, 36, 32); }
12 |
13 | public int TurnsToComplete;
14 | public int Required;
15 | public int RequiredResource1;
16 | public int RequiredResource2;
17 |
18 | private fixed byte Text2[32];
19 | public string Order { get => Util.GetString(ref this, 84, 32); }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/Unit.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct UNIT {
7 | public int Length;
8 |
9 | // There are 2 sections in Unit that have space for names, but for PTW and Conquests files only the second buffer is used
10 | // Because the current BIQ file structure layout targets BIX and BIQ files but not BIC, we can ignore this first buffer for
11 | // now unless we decide later on to support full backwards compatibility
12 | private fixed byte Text[32];
13 | // public string Name { get => Util.GetString(ref this, 4, 32); }
14 |
15 | public int OwnerType; // 0: None, 1: Barbarian, 2: Civ, 3: Player
16 | public int ExperienceLevel;
17 | public int Owner;
18 | public int UnitType;
19 | public int AIStrategy;
20 | public int X;
21 | public int Y;
22 |
23 | private fixed byte Text2[57];
24 | public string Name { get => Util.GetString(ref this, 64, 57); }
25 |
26 | public int UseCivilizationKing;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/Wchr.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct WCHR {
7 | public int Length;
8 | public int SelectedClimate; // 0: Arid, 1: Normal, 2: Wet, 3: Random
9 | public int ActualClimate;
10 | public int SelectedBarbarianActivity; // -1: None, 0: Sedentary, 1: Roaming, 2: Restless, 3: Raging, 4: Random
11 | public int ActualBarbarianActivity;
12 | public int SelectedLandform; // 0: Archipelago, 1: Continents, 2: Pangaea, 3: Random
13 | public int ActualLandform;
14 | public int SelectedOceanCoverage; // 0: 80%, 1: 70%, 2: 60%, 3: Random
15 | public int ActualOceanCoverage;
16 | public int SelectedTemperature; // 0: Cool, 1: Temperate, 2: Warm, 3: Random
17 | public int ActualTemperature;
18 | public int SelectedAge; // 0: 3 billion years, 1: 4'', 2: 5'', 3: Random
19 | public int ActualAge;
20 | public int WorldSize;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/Wmap.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct WMAP {
7 | public int Length;
8 | public int NumberOfResources;
9 |
10 | /*
11 | Dynamic length gap
12 | In BIQ files, for each resource, 4 bytes are used to store that resource's ID
13 | Data is instead stored in 2d array WmapResource;
14 | */
15 |
16 | public int NumberOfContinents;
17 | public int Height;
18 | public int DistanceBetweenCivs;
19 | public int NumberOfCivs;
20 | private fixed byte UnknownBuffer[8];
21 | public int Width;
22 | private fixed byte UnknownBuffer2[128];
23 | public int MapSeed;
24 |
25 | private fixed byte Flags[4];
26 | public bool XWrapping { get => Util.GetFlag(Flags[0], 0); }
27 | public bool YWrapping { get => Util.GetFlag(Flags[0], 1); }
28 | public bool PolarIceCaps { get => Util.GetFlag(Flags[0], 2); }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/QueryCiv3/BiqSections/Wsiz.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Biq {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct WSIZ {
7 | public int Length;
8 | public int OptimalNumberOfCities;
9 | public int TechRate;
10 |
11 | private fixed byte Text[56];
12 | public string Name { get => Util.GetString(ref this, 36, 32); }
13 |
14 | public int Height;
15 | public int DistanceBetweenCivs;
16 | public int NumberOfCivs;
17 | public int Width;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/QueryCiv3/QueryCiv3.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | true
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Aibs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct AIBS {
7 | private fixed byte HeaderText[4];
8 | public int Length;
9 | public int UniqueID;
10 | public int XCoordinate;
11 | public int YCoordinate;
12 | public int PlayerID; // Index into LEAD
13 | public bool UsedThisTurn;
14 | private fixed byte PaddingBuffer[3];
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Binf.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct BINF {
7 | private fixed byte HeaderText[4];
8 | public int Length;
9 | public int BuildingCount;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Bitm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct BITM {
7 | private fixed byte HeaderText[4];
8 | public int Length;
9 | private fixed byte UsableBuildingBits[32];
10 | public int BuildingCount;
11 | public int BuildingBytes;
12 |
13 | public bool IsBuildingUsable(int buildingIndex) {
14 | int byteIndex = buildingIndex / 8;
15 | int bitIndex = buildingIndex % 8;
16 | fixed (byte* ptr = UsableBuildingBits) {
17 | return (ptr[byteIndex] & (1 << bitIndex)) != 0;
18 | }
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Clny.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct CLNY {
7 | private fixed byte HeaderText[4];
8 | public int Length;
9 | public int UniqueID;
10 | public int XCoordinate;
11 | public int YCoordinate;
12 | public int PlayerID; // Index into LEAD
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Cnsl.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct CNSL {
7 | private fixed byte HeaderText[4];
8 | public int Length;
9 | private fixed byte UnknownBuffer[228];
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Cont.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct CONT {
7 | private fixed byte HeaderText[4];
8 | public int Length;
9 | public bool Type; // 0: land, 1: water
10 | private fixed byte Padding[3];
11 | public int TileCount;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Ctpg.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct CTPG {
7 |
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Ctzn.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct CTZN {
7 | private fixed byte HeaderText[4];
8 | public int Length;
9 | private fixed byte UnknownBuffer[1];
10 |
11 | // See ImportCiv3::GetTileFromSpiral for details on this field.
12 | public byte TileWorked;
13 |
14 | private fixed byte Text[262];
15 | public string Name { get => Util.GetString(ref this, 10, 262); }
16 |
17 | public int Type; // 0: Happy, 1: Content, 2: Sad, 3: Resistor, 4: Specialist
18 | public int Sex; // 0: Male, 1: Female
19 | public int BirthDate;
20 | public int CityID;
21 | public int ID;
22 | public int SpecialistType;
23 | public int Nationality;
24 | public int AffectedBy;
25 | public int AffectedSince;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Date.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct DATE {
7 | private fixed byte HeaderText[4];
8 | public int Length;
9 |
10 | private fixed byte Text[64];
11 | public string YearText { get => Util.GetString(ref this, 8, 64); }
12 |
13 | public int BaseUnit;
14 | public int Month;
15 | public int Week;
16 | public int Year;
17 | private fixed byte UnknownBuffer[4];
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Faxx.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct FAXX {
7 | private fixed byte HeaderText[4];
8 | public int Length;
9 | private fixed byte UnknownBuffer[88];
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Hist.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct Turn {
7 | public int TurnNumber;
8 | public int Date;
9 | public int RemainingCivs;
10 | }
11 |
12 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
13 | public unsafe struct HIST {
14 | private fixed byte HeaderText[4];
15 | public int TurnCount;
16 | public IntBitmap Civs;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Idls.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct IDLS {
7 | private fixed byte HeaderText[4];
8 | public int Length;
9 | private fixed byte UnknownBuffer[48];
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Outp.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct OUTP {
7 | private fixed byte HeaderText[4];
8 | public int Length;
9 | public int UniqueID;
10 | public int XCoordinate;
11 | public int YCoordinate;
12 | public int PlayerID; // Index into LEAD
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Palv.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct PALV {
7 | private fixed byte HeaderText[4];
8 | public int Length;
9 | private fixed byte UnknownBuffer[4];
10 | private fixed int SectionCultures[32]; // 0: American, 1: European, 2: Med, 3: Mideast, 4: Asian
11 | private fixed byte UnknownBuffer2[8];
12 | public IntBitmap Sections;
13 |
14 | // if version >= ptw:
15 | public int UnusedUpgrades;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Peer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct PEER {
7 | private fixed byte HeaderText[4];
8 | public int Length;
9 | private fixed byte UnknownBuffer[8];
10 | public int MP1;
11 | public int MP2;
12 | private fixed byte UnknownBuffer2[8];
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Plgi.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct PLGI {
7 | private fixed byte HeaderText[4];
8 | public int Length;
9 | private fixed byte UnknownBuffer[4];
10 | private fixed byte HeaderText2[4];
11 | public int Length2;
12 | private fixed byte UnknownBuffer2[8];
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Popd.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct POPD {
7 | private fixed byte HeaderText[4];
8 | public int Length;
9 | public int SpecialistCount;
10 | public int CitizenCount;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Radt.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct RADT {
7 | // It seems that RADT, VLOC, and OUTP all have completely identical structs
8 | private fixed byte HeaderText[4];
9 | public int Length;
10 | public int UniqueID;
11 | public int XCoordinate;
12 | public int YCoordinate;
13 | public int PlayerID; // Index into LEAD
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Rple.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct RPLE {
7 | private fixed byte HeaderText[4];
8 | public int DataHeader;
9 | public byte EventType;
10 | private fixed byte UnknownBuffer1[1];
11 | public short XLocation;
12 | public short YLocation;
13 | private fixed byte UnknownBuffer2[4];
14 |
15 | // RPLE ends in a null-terminated string, which is variable length, so it can't be included here
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Rplt.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct RPLT {
7 | private fixed byte HeaderText[4];
8 | public int DataHeader;
9 | public int TurnNumber;
10 | public bool ReloadIndicator;
11 | public int EventCount;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Tutr.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct TUTR {
7 | private fixed byte HeaderText[4];
8 | public int Length;
9 | private fixed byte UnknownBuffer[92];
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Unit.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct UNIT {
7 | private fixed byte HeaderText[4];
8 | public int Length;
9 | public int ID;
10 | public int X;
11 | public int Y;
12 | public int PreviousX;
13 | public int PreviousY;
14 | public int OwnerID;
15 | public int Nationality;
16 | public int BarbTribe;
17 | public int UnitType;
18 | public int ExperienceLevel;
19 |
20 | // TODO: Fully populate all flags
21 | private fixed byte Flags[4];
22 | public bool AttackedThisTurn { get => Util.GetFlag(Flags[0], 2); }
23 | public bool GeneratedLeader { get => Util.GetFlag(Flags[0], 5); }
24 |
25 | public int Damage;
26 | public int MovementUsed; // In thirds of a point
27 |
28 | // The amount of progress the worker has made towards its job.
29 | //
30 | // This goes up by 2 each turn for a native worker, and 1 each turn for
31 | // a foreign worker (configurable via PRTO::WorkerStrength).
32 | public int WorkerProgressTowardsJob;
33 | public int WorkerJob; // Index into TFRM, -1 if idle
34 | private int UnknownBuffer2;
35 | public int LoadedOnUnitId;
36 |
37 | private fixed byte Flags2[12];
38 | public bool Fortified { get => Util.GetFlag(Flags2[0], 0); }
39 |
40 | // Appears to be true for multiple types of automation, including
41 | // exploring.
42 | public bool IsAutomated { get => Util.GetFlag(Flags2[0], 4); }
43 |
44 | // A utility for trying to understand the values of the various flags.
45 | public string DumpFlags() {
46 | string result = "";
47 | for (int i = 0; i < 4; ++i) {
48 | result += Flags[i] + " : ";
49 | }
50 | for (int i = 0; i < 12; ++i) {
51 | result += Flags2[i] + " : ";
52 | }
53 | for (int i = 0; i < 4; ++i) {
54 | result += Flags3[i] + " : ";
55 | }
56 | return result;
57 | }
58 |
59 | public int UseName;
60 |
61 | private fixed byte Text[60];
62 | public string Name { get => Util.GetString(ref this, 92, 60); }
63 |
64 | public int GoToX;
65 | public int GoToY;
66 | private fixed byte UnknownBuffer3[265];
67 |
68 | // if c3c:
69 | private fixed byte UnknownBuffer4[44];
70 |
71 | private fixed byte Flags3[4];
72 | public bool HasIDLSSection { get => Util.GetFlag(Flags3[3], 1); }
73 |
74 | private fixed byte UnknownBuffer5[7];
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Vloc.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct VLOC {
7 | private fixed byte HeaderText[4];
8 | public int Length;
9 | public int UniqueID;
10 | public int XCoordinate;
11 | public int YCoordinate;
12 | public int PlayerID; // Index into LEAD
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/QueryCiv3/SavSections/Wrld.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace QueryCiv3.Sav {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public unsafe struct WRLD {
7 | private fixed byte HeaderText1[4];
8 | public int Length1;
9 | public short ContinentCount;
10 | private fixed byte HeaderText2[4];
11 | public int Length2;
12 | public int NumberOfLandContinents;
13 | public int Height;
14 | public int CivDistance;
15 | public int NumberOfCivs;
16 | public int PercentWater; // is this int or float?
17 | private fixed byte UnknownBuffer1[4];
18 | public int Width;
19 | private fixed int PlayerData[32];
20 | public int WorldSeed;
21 |
22 | private fixed byte Flags[4];
23 | public bool XWrapping { get => Util.GetFlag(Flags[0], 0); }
24 | public bool YWrapping { get => Util.GetFlag(Flags[0], 1); }
25 | public bool PolarIceCaps { get => Util.GetFlag(Flags[0], 2); }
26 |
27 | private fixed byte HeaderText3[4];
28 | public int Length3;
29 | public int SelectedClimate;
30 | public int ActualClimate;
31 | public int SelectedBarbarians;
32 | public int ActualBarbarians;
33 | public int SelectedLandform;
34 | public int ActualLandform;
35 | public int SelectedOceanCoverage;
36 | public int ActualOceanCoverage;
37 | public int SelectedTemperature;
38 | public int ActualTemperature;
39 | public int SelectedAge;
40 | public int ActualAge;
41 | public int WsizID;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/QueryCiv3/Util.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using Blast;
4 | using System.Text.RegularExpressions;
5 | using System.Text;
6 |
7 | namespace QueryCiv3 {
8 | public struct ByteBitmap {
9 | private byte Flags;
10 | public bool this[int i] { get => ((Flags >> i) & 1) == 1; }
11 | }
12 |
13 | public struct IntBitmap {
14 | private int Flags;
15 | public bool this[int i] { get => ((Flags >> i) & 1) == 1; }
16 | }
17 |
18 | public class Util {
19 | // Encoding code page ID; 1252 is Civ3 encoding for US language version
20 | public static Encoding Civ3Encoding;
21 |
22 | static Util() {
23 | Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
24 | Civ3Encoding = Encoding.GetEncoding(1252);
25 | }
26 |
27 | public static byte[] ReadFile(string pathName) {
28 | byte[] MyFileData = File.ReadAllBytes(pathName);
29 | if (MyFileData[0] == 0x00 && (MyFileData[1] == 0x04 || MyFileData[1] == 0x05 || MyFileData[1] == 0x06)) {
30 | return Decompress(MyFileData);
31 | }
32 | return MyFileData;
33 | }
34 |
35 | public static byte[] Decompress(byte[] compressedBytes) {
36 | MemoryStream DecompressedStream = new MemoryStream();
37 | BlastDecoder Decompressor = new BlastDecoder(new MemoryStream(compressedBytes, writable: false), DecompressedStream);
38 | Decompressor.Decompress();
39 | return DecompressedStream.ToArray();
40 | }
41 |
42 | public static string GetString(byte[] bytes) {
43 | string Out = Civ3Encoding.GetString(bytes);
44 | Regex TrimAfterNull = new Regex(@"^[^\0]*");
45 | Match NoNullMatch = TrimAfterNull.Match(Out);
46 | return NoNullMatch.Value;
47 | }
48 |
49 | public static unsafe string GetString(ref T structData, int start, int length) where T : unmanaged {
50 | byte[] Arr = new byte[length];
51 | fixed (void* dataPtr = &structData, arrPtr = Arr) {
52 | Buffer.MemoryCopy(((byte*)dataPtr) + start, (byte*)arrPtr, length, length);
53 | }
54 | return GetString(Arr);
55 | }
56 |
57 | public static bool GetFlag(byte flags, int index) {
58 | return ((flags >> index) & 1) == 1;
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/QueryCiv3/readme.md:
--------------------------------------------------------------------------------
1 | # QueryCiv3
2 |
3 | QueryCiv3 is an assembly designed to read a Civilization III SAV or BIQ file, identify offsets of the 4-character section headers, and retrieve data based on offsets in the data stream.
4 |
5 | It is a C# port of my [Go library in c3sat](https://github.com/myjimnelson/c3sat/tree/master/queryciv3).
6 |
7 | **Note:** All file access (e.g. read file from path constructors) has intentionally been removed from SaveData, BicData, and Civ3File as they are intended to be exposed to user-made Lua scripts, and I don't want any potential paths to unintended system access.
8 |
9 | ## Methods
10 |
11 | - `Civ3File.SectionOffset(string name, int nth)` - Returns an int offset of the nth (1, 2, 3, etc.) occurrence of the named section header (GAME, TILE, CITY, etc.).
12 | - `Civ3File.ReadInt32(int offset, bool signed = true)` - Returns an int containing the 32-bit integer at the provided offset.
13 | - `Civ3File.ReadInt16(int offset, bool signed = false)` - Returns an int containing the 16-bit integer at the provided offset.
14 | - `Civ3File.ReadByte(int offset)` - Returns an int containing the 8-bit integer at the provided offset.
15 |
--------------------------------------------------------------------------------
/_Console/BuildDevSave/BuildDevSave.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Exe
10 | net8.0
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/_Console/BuildDevSave/BuildDevSave.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.5.002.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BuildDevSave", "BuildDevSave.csproj", "{B24B599B-C676-4522-9C5D-C60D0276E792}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {B24B599B-C676-4522-9C5D-C60D0276E792}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {B24B599B-C676-4522-9C5D-C60D0276E792}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {B24B599B-C676-4522-9C5D-C60D0276E792}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {B24B599B-C676-4522-9C5D-C60D0276E792}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {EE4C72A2-071F-4D6A-B83A-0BDEF630FE08}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/_Console/BuildDevSave/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using QueryCiv3;
4 | using C7GameData;
5 | using C7GameData.Save;
6 |
7 | namespace BuildDevSave {
8 | class Program {
9 |
10 | static string GetCiv3Path { get => Civ3Location.GetCiv3Path(); }
11 | static string C7DefaultSaveDir { get => @"../../C7/Text"; }
12 |
13 | static void Info(string path, SaveGame save) {
14 | Console.WriteLine($"generated save file from {path}:");
15 | Console.WriteLine($"\tmap dimensions: with = {save.Map.tilesWide}, height = {save.Map.tilesTall}");
16 | Console.WriteLine($"\tfound {save.Civilizations.Count} civilizations");
17 | Console.WriteLine($"\tfound {save.Players.Count} players");
18 | Console.WriteLine($"\tfound {save.Cities.Count} cities");
19 | Console.WriteLine($"\tfound {save.UnitPrototypes.Count} unit prototypes");
20 | Console.WriteLine($"\tfound {save.Units.Count} units");
21 | }
22 |
23 | static void Main(string[] args) {
24 | if (args.Length < 1) {
25 | Console.WriteLine("provide civ3 SAV absolute path as command line argument");
26 | return;
27 | }
28 | DateTime start = DateTime.Now;
29 | string fullSavePath = args[0];
30 | string outputPath = Path.Combine(C7DefaultSaveDir, "c7-static-map-save.json");
31 | SaveGame output = ImportCiv3.ImportSav(fullSavePath, GetCiv3Path + @"/Conquests/conquests.biq", (scenarioSearchPath) => {
32 | return GetCiv3Path + @"/Conquests/Text/PediaIcons.txt";
33 | });
34 | output.Save(outputPath);
35 | DateTime stop = DateTime.Now;
36 | int elapsed = (stop - start).Milliseconds;
37 | Console.WriteLine($"finished generating save in {elapsed} milliseconds");
38 | Info(fullSavePath, output);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/_Console/BuildDevSave/readme.md:
--------------------------------------------------------------------------------
1 | # BuildDevSave
2 | Reads a .SAV file, converts it to C7's save format, then writes it to `C7/Text/c7-static-map-save.json`.
3 |
4 | ## Build
5 | `dotnet build -m`
6 |
7 | ## Usage
8 | `dotnet run `
9 |
--------------------------------------------------------------------------------