Under the Comanche Moon (dev thread)

Code:
local newWagon = gen.createUnit(unitAliases.WagonTrain, civ.getTribe(6), {145,13,0}, {homeCity = nil, veteran = nil})
The way you've written it, newWagon is a table. newWagon[1] would be the actual wagon. gen.createUnit makes a table of units, not an individual unit.
 
Code:
local newWagon = gen.createUnit(unitAliases.WagonTrain, civ.getTribe(6), {145,13,0}, {homeCity = nil, veteran = nil})
The way you've written it, newWagon is a table. newWagon[1] would be the actual wagon. gen.createUnit makes a table of units, not an individual unit.
Here is my latest attempt:

Code:
    if turn == 7 then
        local independenceKansas = civ.getTile(145,13,0)
        local santaFe = civ.getTile(34,60,0)
        local newWagon = civ.createUnit(unitAliases.WagonTrain,civ.getTribe(6),independenceKansas)
        gen.setToGoingTo(newWagon, santaFe)
    end

For the unit, I tried two things:

1. Made it like a helicopter; air unit with 0 range. It traveled the path, for 2-3 turns, then abruptly doubled back and went through some random hills for a while.
2. Made it like a bomber, air unit with 10 range. It traveled the path for a lot longer -- did pretty well! At one point, though, it doubled back and wound up not moving at all, by a river. Which is sort of how a wagon train might actually behave, so not a terrible outcome. :) I wonder, though, if I should be separating these goTo orders into smaller chunks along the map. Interestingly, in two tests of this, it did the exact same thing each time.
3. On the 3rd attempt, it went westward to the other side of the map!
4. 4th and 5th attempts, it doubled back again -- interestingly, over by Comanche lands.

I wonder if the AI senses danger/multiple units, and turns around, or if this is what you had mentioned about it canceling its goTo when it encounters an enemy unit.
 
For the unit, I tried two things:

1. Made it like a helicopter; air unit with 0 range. It traveled the path, for 2-3 turns, then abruptly doubled back and went through some random hills for a while.
2. Made it like a bomber, air unit with 10 range. It traveled the path for a lot longer -- did pretty well! At one point, though, it doubled back and wound up not moving at all, by a river. Which is sort of how a wagon train might actually behave, so not a terrible outcome. :) I wonder, though, if I should be separating these goTo orders into smaller chunks along the map. Interestingly, in two tests of this, it did the exact same thing each time.
3. On the 3rd attempt, it went westward to the other side of the map!
4. 4th and 5th attempts, it doubled back again -- interestingly, over by Comanche lands.

I wonder if the AI senses danger/multiple units, and turns around, or if this is what you had mentioned about it canceling its goTo when it encounters an enemy unit.
I don't know how the AI decides when to clear goto orders, but you should probably have an onActivateUnit or onTribeTurnBegin event that renews the order.
 
I don't know how the AI decides when to clear goto orders, but you should probably have an onActivateUnit or onTribeTurnBegin event that renews the order.
As you suggested, it seems easiest to either be OK with the randomness of a goTo order -- and perhaps a unit retirement in place -- or do a teleport. I may even implement some version of both.

I feel fairly dense, but I am unable to get this to work. For every "Tipi" unit next to either a sea tile or a river tile (next to, not on), I want 25 gold to generate. What am I doing wrong here?
This is in onTribeTurnBegin.lua.

Spoiler :
Code:
local function riverNearby(tile)
    if tile.river then
        return true
    end
    for _,adjacentTile in pairs(gen.getAdjacentTiles(tile)) do
        if adjacentTile.river then
            return true
        end
    end
    return false
end

local function waterNearby(tile)
    if tile.baseTerrain == 10 then
        return true
    end
    for _,adjacentTile in pairs(gen.getAdjacentTiles(tile)) do
        if adjacentTile.baseTerrain == 10 then
            return true
        end
    end
    return false
end

discreteEvents.onTribeTurnBegin(function(turn, tribe)
if turn > 0 and tribe.isHuman then
    for unit in civ.iterateUnits() do
        if unit.type == unitAliases.Tipi and waterNearby(tile) or riverNearby(tile) then
        tribe.money = math.max(0,tribe.money + 25)
                gen.justOnce("tipisByWater",function()
                        civ.ui.text("Each tipi placed by water of any kind will generate a small number of horses each turn.")
                        end)
        end
    end
end
end)
 
Code:
if unit.type == unitAliases.Tipi and waterNearby(tile) or riverNearby(tile) then
and has a higher precedence than or, so I don't think this line does what you want it to. Always use () to group when using and/or in the same line.

Code:
if unit.type == unitAliases.Tipi and (waterNearby(tile) or riverNearby(tile)) then

You're also not checking if the unit is owned by the tribe,

Code:
if unit.type == unitAliases.Tipi and unit.owner == tribe and (waterNearby(tile) or riverNearby(tile)) then

(This may not matter if only the human player can have tipi units.)

Code:
waterNearby(tile)
riverNearby(tile)
should be
Code:
waterNearby(unit.location)
riverNearby(unit.location)
Since tile doesn't seem to be defined anywhere, I would have expected this to generate an error.
Code:
local function riverNearby(tile)
    if tile.river then
        return true
    end
    for _,adjacentTile in pairs(gen.getAdjacentTiles(tile)) do
        if adjacentTile.river then
            return true
        end
    end
    return false
end
since you don't want to generate the gold on the tile, remove
Code:
    if tile.river then
        return true
    end
(I presume its ok to be on a river and generate the gold if an adjacent square is also river. Otherwise, you should return false if the tile is a river.)
 
Thank you, Prof.! This works well. The only issue is that it generates 25 gold flat, even if there are 10 tipis by water. Any ideas why this might be happening?
Just noticed:
Code:
tile.baseTerrain == 10
adjacentTile.baseTerrain == 10
should be
Code:
tile.baseTerrain.type == 10
adjacentTile.baseTerrain.type == 10
That might be it. If you're still not getting enough money, add a line printing information about the current unit to the console, to see if you can figure out what units are missing and why.
 
Just noticed:
Code:
tile.baseTerrain == 10
adjacentTile.baseTerrain == 10
should be
Code:
tile.baseTerrain.type == 10
adjacentTile.baseTerrain.type == 10
That might be it. If you're still not getting enough money, add a line printing information about the current unit to the console, to see if you can figure out what units are missing and why.
That did the trick! Thank you! Works like a charm.

Does this mean that the code was reading the same, single tile of water once, as opposed to all tiles of water? I did baseTerrain for this, but have also used tile.terrainType. I wonder if that would have the same results.
 
Does this mean that the code was reading the same, single tile of water once, as opposed to all tiles of water? I did baseTerrain for this, but have also used tile.terrainType. I wonder if that would have the same results.
One of the Tipis probably had a river tile adjacent.

You can't use tile.terrainType == 10, because tile.terrainType also has 4 flags (river, 2x is the resource being animated, I think suppress resource if far away from land). You have to use tile.terrainType % 16 == 10. One of the CTRL+SHIFT+F4 scripts builds an object.lua file, and that file will have a "name" for the ocean terrain, based on what you named it. It is more readable to use tile.baseTerrain == object.bOcean to check for the ocean tile.
 
Never making things easy for myself here... I'd like to somehow have an option in a dialog that, if selected, immediately "investigates a city" for the player -- e.g., shows that city's window as if you have spied on it! Is this possible? I have an empty slot in the dialog already wanting to be filled. Would I use, "gen.setCityInvestigated," or something along those lines? Is this even doable?
 
Never making things easy for myself here... I'd like to somehow have an option in a dialog that, if selected, immediately "investigates a city" for the player -- e.g., shows that city's window as if you have spied on it! Is this possible? I have an empty slot in the dialog already wanting to be filled. Would I use, "gen.setCityInvestigated," or something along those lines? Is this even doable?
gen.setCityInvestigated will let you click on a foreign city and take a look inside, just as if you had sent a diplomat/spy to look inside the city earlier. I don't know of a way to automatically open a city window.
 
Yes, that works well. In addition to giving a text message to the player that it has gone through, I need to chart the city for the player so they can see it. I tried this, but it does not chart. Any ideas? (Note: All other aspects of the code below are working great!)

Spoiler :
Code:
elseif choice == 3 then
                    local choice = nil
                    local dialog = civ.ui.createDialog()
                    dialog.title = "Anglos Headed to Santa Fe"
                    dialog.width = 500
                    local multiLineText = "What kind of information do you want?"
                    text.addMultiLineTextToDialog(multiLineText,dialog)
                        if tribe.money >= 500 then
                        dialog:addOption("How many US troops are there in total? (500 horses)", 1)
                        end
                        if tribe.money >= 300 then
                        dialog:addOption("Tell us about a US settlement (300 horses)", 2)
                        end
                        dialog:addOption("Nevermind",3)
                    choice = dialog:show()
                        if choice == 1 then
                            local dialog = civ.ui.createDialog()
                            dialog.title = "US Military: Total Numbers"
                            dialog.width = 500
                            local multiLineText = "All right. For your healthy horses, here is what we know:\n^\n^# of US Militia: " ..tostring(countUnits(unitAliases.USMilitia,civ.getTribe(6))) .. "\n^# of US Infantry: " ..tostring(countUnits(unitAliases.USSoldiers,civ.getTribe(6))) .. "\n^# of US Cavalry: " ..tostring(countUnits(unitAliases.USLgtCav,civ.getTribe(6))) .. "\n^# of US Cannon: " ..tostring(countUnits(unitAliases.USCannon,civ.getTribe(6))) .. "\n^# of US Officers: " ..tostring(countUnits(unitAliases.USCommander,civ.getTribe(6))) .. "\n^# of US Homesteaders: " ..tostring(countUnits(unitAliases.Homesteaders,civ.getTribe(6)))
                            text.addMultiLineTextToDialog(multiLineText,dialog)
                            dialog:show()
                            tribe.money = math.max(tribe.money -500)
                        elseif choice == 2 then
                            local menuTable = {}
                            local offset = 3
                                for city in civ.iterateCities() do
                                    if city.owner == civ.getTribe(6) then
                                        menuTable[offset+city.id] = city.name
                                    end
                                end
                                local informationChoice = text.menu(menuTable,"Select a settlement to ask about:","Information for Horses")
                                if informationChoice >= offset then
                                    local chosenCity = civ.getCity(informationChoice-offset)
                                    if civ.isCity(chosenCity) then
                                        gen.chartCity(chosenCity,civ.getTribe(1),1)
                                        gen.setCityInvestigated(chosenCity)
                                        tribe.money = math.max(tribe.money -300)
                                        civ.ui.text("Note: You may now take a look at the city window for "..chosenCity.name.." until the end of this turn.")
                                    end
                                end
                        elseif choice == 3 then
                            civ.ui.text("Take care, then.")
                        end
 
Yes, that works well. In addition to giving a text message to the player that it has gone through, I need to chart the city for the player so they can see it. I tried this, but it does not chart. Any ideas? (Note: All other aspects of the code below are working great!)
You also need to use gen.revealTile to show the tile. chartCity just makes the city visible on the tile, if it is already revealed.
 
That's pretty nice to see such a creative concept !
Thank you, Dadais! Slowly plugging away.

@Prof. Garfield That worked great! I wasn't able to reveal a radius around the tile, just the tile itself. That's fine, though. In fact, in some ways, better.

I've reviewed your lesson on counting and have employed it in this lua to good effect. One area I am uncertain on, though, is how I might count the # of a certain unit type killed. For example, throughout the game, I want to keep track of the # of times the Comanche player raids a wagon train. How might I go about that?
 
One area I am uncertain on, though, is how I might count the # of a certain unit type killed. For example, throughout the game, I want to keep track of the # of times the Comanche player raids a wagon train. How might I go about that?
Use the data module.

You'll need to define the counter and then you can change or increment it. When you need the value, you can get it.

I'm pretty sure you used flags and/or counters in your last project. The data module works in a very similar way, it just combines the flags and counters into one module, and has the same commands as the supplemental data modules (unitData, cityData, etc).
 
Use the data module.

You'll need to define the counter and then you can change or increment it. When you need the value, you can get it.

I'm pretty sure you used flags and/or counters in your last project. The data module works in a very similar way, it just combines the flags and counters into one module, and has the same commands as the supplemental data modules (unitData, cityData, etc).

🤯
This changes a lot. Initial tests are working spectacularly. Thank you!

Quick question for you, Prof. In my code, I had defined, to negotiate with a wagon train, a function "isNearWagonTrain (unit)"

Code:
local function isNearWagonTrain(unit)
   for nearbyUnit in gen.nearbyUnits(unit.location,1,unit.location.z) do
          if nearbyUnit.type == unitAliases.WagonTrain then
              return true
          end
   end
   return false
end

This is great for units approaching the wagon in a later discreteEvent. However, how do I reference the actual wagon train in that code? "isNearWagonTrain.nearbyUnit" ? Or do I need to define a whole new function for that?
 
This is great for units approaching the wagon in a later discreteEvent. However, how do I reference the actual wagon train in that code? "isNearWagonTrain.nearbyUnit" ? Or do I need to define a whole new function for that?
Instead of returning true, return the wagonTrain unit. A unitObject is truthy, (will function like true in if statements), so you can use that function the same way if you need to. Instead of returning false, return nil if there is no wagon train.

Code:
local function getNearbyWagonTrain(unit)
   for nearbyUnit in gen.nearbyUnits(unit.location,1,unit.location.z) do
          if nearbyUnit.type == unitAliases.WagonTrain then
              return nearbyUnit
          end
   end
   return nil
end
local wagon = getNearbyWagonTrain(unit)
if wagon then
   -- do something with the found wagon train, since it exists
end
 
UPDATE
On trade, reputation, and the Comanche


I've successfully begun to implement counters through lua, which will impact the Comanche player throughout this scenario.

One of these is the concept of trade reputation. While known for their brutality and warlike nature on the plains, the Comanche were also adept traders, and even skilled diplomats. I wanted to feature this into the scenario somehow. This is rudimentary at this point, but I have a working counter. When a "Chief" is in proximity to a trading house, with a keypress, they can trade with that structure.

trade1.png

A trading screen at the beginning, with the trade reputation counter set at 0.
(I should probably be more clear with what is being traded for what... "Trade 1 hide for 100 horses," for example.)

trade2.png

A completed trade.

For each successful trade, the Comanche receive a more positive trade reputation. Let's say this counter goes up by quite a bit. Here is the trading screen for that:

trade3.png

After reaching higher trade reputation, you can now get more Horses for Hides, you can get 2 crates of Guns for 1 Hide, you can pay fewer horses for guns, etc.
But note that the deals get even better the higher reputation you achieve! It does have an end, though, of course.

So then, you might be wondering: "How does that counter drop?" Raids, of course! Raids on trading houses are immediate. They get you a lot of resources in one go. But they are costly to your trade reputation, and in some cases, could restart the counter from 0. You have to weigh the short-term against the long-term. Later in the game, there are some pretty fantastic trade deals that come up with higher trade rep. On the other hand, raids are extremely lucrative.

OTHER UPDATES
* Music implemented (it just helps me focus... :))
* Art continues to change slightly, even though I thought I was done. (Does anyone have a good frontier-looking building that's on fire?)
* Negotiating with trade wagons on the Santa Fe Trail code has been successfully implemented, may be slightly tweaked.
* Getting wagons to show up on the Santa Fe Trail works in various ways... it seems to be OK.
* Placed initial US, Texan, and Mexican militias, presidios, forts, and other bells and whistles like timber mills, sheds, farms, etc.

Plugging away! Tying up some loose ends in my working code, too. My main concern when it comes time to playtest will be answering this question: Is harvesting and transporting resources fun? I've no doubt that the presence of multiple resource types will be fun in its own way, but how they get from point A to point B, I wonder about. It has been fun in my initial testing. And also, there are some exceptions. Most notably, you only need to transport goods when you are going to use them, but not when you are trading them. So for example, with the above dialogues, you can trade Hides for Guns without carrying hides; the lua code finds the nearest Hides unit you own and eliminates it. The option doesn't even show up if you don't have Hides at all. However, to make a new unit for the Comanche, you need to carry hides or guns or whatever back to tipis to use them. I think this presents an interesting and fun challenge on carrying goods to expand. We'll see how it holds up in playtesting.
 
Top Bottom