Dev Diary 0: Introducing "C7" Godot Prototype

WildWeazel

Carthago Creanda Est
Joined
Jul 14, 2003
Messages
7,363
Location
/mnt/games/Civ3/Conquests/Scenarios
We've been at this long enough that it's time for a proper status update. Dev Diary is a term for an informal technical report on work in progress. It's a pretty common practice among open source, indie, and even some commercial projects these days. Since we admittedly don't have much to show yet, this one will be pretty brief.

icon.png

a logo makes it official

@Puppeteer, @Quintillus, and I have been putting together this prototype to demonstrate some of the core technical features in Godot. The prototype is a place for us to experiment with the engine before committing to a proper solution. It's called C7, for Civfanatics Creation & Customization Community's Civ3 Conquests Clone. That's the codename for the whole new game, or "Build It" option. We created a (temporarily) private GitHub team and repository in which to collaborate, so I can't directly link to the code here but if you have a GitHub account I'll give you access.

Here's the current state of things. The demo finds Civ3 and loads some PCX files. First the title screen is shown with a button to start a game. Click the button to proceed to the next "scene" which is a random map of correctly placed terrain tiles. There's another button to end your turn. Click this one, and you'll see the console output cycle through some steps. The button is disabled for 2 seconds while the computer "takes a turn" before control is returned to you.

Screenshot from 2021-05-27 22-36-29.png

arguably a "turn based game"

And here's me, after patching together some code samples I found online to make it work:


come at me, Jon Skeet

Now this doesn't look like much, and it's really not, but there's a fair amount going on here beyond printing a few lines:

Instead of a linear "game loop" there's a script tracking the turn state (game start, player begin, player end, computer start, computer end...) and raising events - signals that other code can listen for - whenever the state changes.

The humble turn count is not just a loop index, in fact it doesn't know anything about the main script. It's an example of a component that reacts to an event, in this case this case the start of a turn, by incrementing and printing its counter. In a real game this component would also calculate the new date and pass both back to the UI, while other components would react to the same event by generating an autosave, recalculating scores, and so on.

These components can get access to each other via a component manager to set up these event listeners and pull state data as needed.

That's the gist of the planned architecture: lots of small, independent components publishing and subscribing to each others' events, stored in the manager and orchestrated by the game state cycle.

Screenshot from 2021-05-27 23-38-55.png
I dunno, blockchain or something

So far we've opted to do all of this in code, and that in C#, because it's what we're most familiar with. Godot lets you do quite a bit directly in the editor, but it remains to be seen whether that's useful since this will be a very data-driven and customizable game. It also provides its own scripting language GDScript, similar to Python, with even more engine support but a bit less powerful as a language.

So where do we go from here? It's a bit open ended since we're still exploring the engine, but there are several more features I'd like to try out in this prototype:
  • Add a couple of units for each player to move once per turn
  • Play unit animations with civ colors
  • Integrate at least some of Puppeteer's SimpleGame logic
  • Some kind of game setting loaded from an external file (e.g. map size, unit stats)
  • Figure out how to visually scroll and wrap the map
  • A simple "mod script" loaded during the game
  • Distribute a compiled runnable version
And that's the state of things. Again, if you'd like to see the code or just follow progress, give me your GitHub username. I think our most valuable technical skill for now is Godot itself, so if you have any experience or would like to learn you're more than welcome to jump in.
 
As a rule of thumb, I dislike quoting myself, BUT I did suggest:

OK, my co-conspirators, my thoughts are as follows:

[...]

IF we can make a new map (full component sets; tiles fully compositable with all existing game assets) then we are 90%+ on our way [to "C7"*]

[...]

Lastly, @Quintillus' editor should be the "gold standard" to use for all compatibility tests

* :goodjob: "C7"
 
I'm not sure I ever ran this code. I mean your commits after my last commit. Oh yeah, May got weird and busy for me.

I ran it now. Still works. In Godot 3.3.3.

I saw "PlayerTurn" and "ComputerTurn" as was triggered to think about suggesting treating each turn as equal, but duh this is the UI, so the local player's turn is absolutely handled differently. Especially in its current state.

I was trying to remember why I don't have showing real maps yet as that would increase the wow factor to passersby, but yeah I think I'm remembering now. I was much less clear on how I wanted the legacy data extraction to work, but I think I have that pretty solid in my head (and ReadCivData.QueryCiv3 prototype) now.

Oh, yeah, also, I think I'm *only* handling the grass-coast-plain terrain graphic and not the whole terrain set. So integrating the rest of the terrain will be some extra work.

Edit: Huh, I vaguely remember showing a from-SAV map with only the grass-coast-plain graphics showing and the rest blank. I'll have to figure out where I did that. Or if I dreamed it.

Edit 2: Oh, it's in my C# SAV/BIQ/media library thread. The linked post has the partial image, and the 2nd following post has tundra and ocean, too! It looks like maybe I had the base terrain working but not the overlay terrain. And I guess that's in my repo.

Now that the C7 project is public I should quit trying to sync the two projects and make the C7 Prototype the canonical source project.
 
Last edited:
Hmm, we seem go suddenly need some better organization. I want to reply to Flintlock's report of errors under Mono SDK, but the announcement thread doesn't seem to be the right place, but I'm not sure where is the right place.

Maybe we should standardize on at least an SDK. I've been using whatever dotnetcore version installs with chocolatey or homebrew packages because it's what I was messing with before we settled on Godot. I'm now more familiar with the dotnet sdk and how it handles...everything, but it probably would make a lot more sense to use the Mono SDK.

I know it handles several things differently like Nuget packages, build object file locations, and some other stuff, but maybe it won't be too hard to figure out at this point. Probably easier than trying to explain to any new potential devs that we're deploying in Mono but developing in dotnet/dotnetcore.

All that said, I thought I had successfully cold-installed Godot Mono and the repo and had it work when building from Godot.
 
A well-written post that I saw saw late. Looks like I might not be the only one that applies to, though :).

I was sufficiently inspired to dive back in, pull down the latest code, read through it, and even make some updates. A couple notes on what's already there:

- @Puppeteer has some commits that mention zoom, including by mouse wheel, on the map. It doesn't seem to be working, though. I'd planned to try to figure out why but got distracted, and haven't yet.
- There are four files that have tabs expanded to spaces, and the rest don't (namely, LegacyMap.cs, OldLegacyMap.cs, TurnCounter.cs, and ViewPcxFlc.cs). I propose we standardize on tabs since Godot seems to like that, and that way those files won't keep showing up as modified. Or maybe my editors (Godot and VSCode) are not playing nicely. But if we do want tabs, I can commit those.
- I agree that we probably should standardize on an SDK. But it's been long enough since I set things up that I forget which one I'm using. I'm on Windows, for what it's worth, and it's probably worth making sure it always works on Windows.
- We also might want to synchronize on Godot. It's been mentioned that we may want to move to Godot 4, and I see Godot 3.3 is out now, and has a noteworthy new feature in mp3 support. Right now, I'm 98% sure I'm using Godot 3.2, which was current when the project was set up. I'm also aware that my changes tonight may be obsolete when we move to 4.0, but I'm counting on my improved Godot knowledge still having some transferable value, hopefully.

On all those standardizing-on-things questions, I give my approval to what WildWeazel and the others propose, given I am not sure how active I will be.
 
- There are four files that have tabs expanded to spaces, and the rest don't (namely, LegacyMap.cs, OldLegacyMap.cs, TurnCounter.cs, and ViewPcxFlc.cs). I propose we standardize on tabs since Godot seems to like that, and that way those files won't keep showing up as modified. Or maybe my editors (Godot and VSCode) are not playing nicely. But if we do want tabs, I can commit those.

Oh, yeah, code standards. I'm using VSCode which seems to use spaces. I think. I'm probably a mess with standards because I usually code alone and keep switching languages.

I'm not even sure how to change VSCode to use one or the other.
 
Oh, yeah, code standards. I'm using VSCode which seems to use spaces. I think. I'm probably a mess with standards because I usually code alone and keep switching languages.

I'm not even sure how to change VSCode to use one or the other.

Funny story, I am also using VSCode, and also didn't know how to configure that (and a search in the settings didn't make it obvious). So I looked it up. There's a status bar at the bottom that looks like this:

upload_2021-11-3_3-17-26.png


If you click where it says "Tab Size: 4" (or probably Spaces: 4 in your case), it will open a menu at the top of the screen that lets you choose Indent with Spaces or Indent with Tabs, and then how many for each case.

I don't really have strong feelings on which one we use (and don't know which one my personal projects like the editor use), just so long as we settle on one and don't have the Git history showing changes where all that changed was tabs to spaces or spaces to tabs.

(We should probably also have a "Coding Standards" thread if there isn't a good candidate to bump, so we have reference. I don't want to add too many at this early experimental stage, but tabs vs spaces seems like a good early one, and the variable naming convention also is easier to set early)
 
[QUOTE="Quintillus, post: 16179013, member: 112675]We should probably also have a "Coding Standards" thread[/QUOTE]

Forgive my lack of knowledge of current coding methodologies -

Is it possible to make each set of functions 100% modular, as in "plug compatible?" - Meaning, any designed/built set of specific functions - any specific/comprehensive set of code relating to overall gameplay (combat; AI build choices) - can simply be swapped out for an upgrade requires nothing more the (try not to gag :)) equivalent of an API?
 
@Quintillus thanks!

@Ozymandias I think so. What I've been saying about "handler pattern" and convenience objects I think is the way to do this. I tried to model it in the simplegame repo where a class is defined as an API which is sent to a handler, and that handler has a default but is also pluggable. And either .NET code or Lua (via MoonSharp) can then use that class/API however it likes.

For simplegame...well this is from my readme:
Spoiler API of simplegame turn object :

  • Turn is an object with methods to expose to the player or AI to read the situation and take their turn. This object is passed to the PlayTurn method (in C#) or play_turn global function (in Lua). You can perform only one action per turn.
    • Getters/Setters
      • IsTurnDone(get) - Returns true if no further action can be done on this turn
      • IsPlayerDead(get) - Returns true if current player has died (should only bee seen if player dies attacking)
      • IsEnemyDead(get) - Returns true if the Enemy is dead
      • Action(get) - Returns a string summarizing the action taken by the player during this turn
      • PlayerNote(get or set) - Player can set this string to annotate their turn
      • IsEnemyInRange(get) - Returns true if the enemy is in attack range
      • EnemyX(get) - Returns map X of enemy
      • EnemyY(get) - Returns map Y of enemy
      • EnemyDefense(get) - Returns enemy tile's defense bonus (but this is not yet implemented for combat so it's meaningless; combat is currently a coin toss)
    • Actions
      • Attack() - Attack an enemy (must be in range)
      • Move(x, y) - Move relative coordinates (e.g. -1, 1). Is coded to limit movement to 1 in each axis. Can move to 0,0 and stay put / pass.


Spoiler C# turn AI for simplegame :

Code:
    class SimpleAI : IAI {
        public void PlayTurn(Turn turn)
        {
            if(turn.IsEnemyInRange)
            {
                bool Result = turn.Attack();
                if(Result)
                {
                    turn.PlayerNote = "Woo! PWNED!";
                }
                else
                {
                    turn.PlayerNote = "Lame!";
                }
            }
            else
            {
                Random Rng = new Random();
                turn.Move(1 - Rng.Next(0,3), 1 - Rng.Next(0,3));
                // turn.PlayerNote = "Where are they?";
            }
        }
    }


Spoiler Lua AI for simplegame :

Code:
        function player_turn(turn)
            if turn.isEnemyInRange == true then
                turn.attack()
            else
                local x = math.random(-1,1)
                local y = math.random(-1,1)
                turn.move(x,y)
            end
        end


I was going to have combat bonuses affect combat and allow the AI to perhaps consider that, and maybe there could be a defensive AI that just waits and runs, or waits on a high defense tile to defend, or pursues, etc., etc.. Note that the same turn object ideally is offered to the human player via a UI, so player and computer are playing from the same available information relative to their positioning.

I can imagine a similar handler pattern for every rule. I mean a building modifier for gold production, for example, shouldn't be able delete cities, but maybe it can boost or limit gold production by a number of available factors (trade, vicinity of building, corruption of each city, etc.).

But at this point it's all rather idealistic and hand-wavey, but I'm using the ideas in simplegame and in using my save file reader. I just redid some code yesterday so reading a C3C save file can go like the following, and the same API is available in Lua now. Having a .NET assembly dll of any CLR language should be able to handle it pluggably, too:

Spoiler Save file reader using a convenience class API :

Code:
MapUI.MapHeight = LegacyMapReader.Wrld.Height;
MapUI.MapWidth = LegacyMapReader.Wrld.Width;

               // more code here removed for brevity

                TempTile ThisTile = new TempTile();
                ThisTile.LegacyBaseTerrainID = mapTiles[i].BaseTerrain;
                ThisTile.LegacyOverlayTerrainID = mapTiles[i].OverlayTerrain;
                ThisTile.DebugByte = LegacyMapReader.Sav.ReadByte(mapTiles[i].Offset+TileOffset);
                ThisTile.LegacyFileID = mapTiles[i].BaseTerrainFileID;
                ThisTile.LegacyImageID = mapTiles[i].BaseTerrainImageID;
 
Top Bottom