[TOTPP] Lua Scenario Template

@Prof. Garfield I had to code this generic function to retrieve a random coordinate (just x,y) inside a bigger rectangle. If you find it useful feel free to add it to the generalLibrary.lua :) It doesn't take into account the Z coordinate and doesn't convert to specific tile objects as this is done by any calling/wrapper function. It's been successfully tested in the Lua online interpreter with a couple of examples - https://www.lua.org/cgi-bin/demo

SQL:
-- getRandomTileInRectangle (cornersTable)
-- cornersTable -> Table of rectangle corners
-- returns -> a random valid coordinate (x,y) inside the rectangle passed by
--[[
EXAMPLE:

cornersTable = {{62,0},{76,0},{76,6},{62,6}}

valid return locations are (62,2),(64,4),(63,3) or (65,5)

In Civ2 tiles X and Y have to be of the same type (both evens or both odds)
--]]
local function getRandomTileInRectangle(cornersTable)
    
    local x,y = 0,0
    
    local xCoords = {}
    local yCoords = {}
    
    -- First, loop through cornersTable to split X and Y from all corners of the rectangle
    for _,tile in pairs(cornersTable) do       
        -- add to x,y tables
        table.insert(xCoords, tile[1])
        table.insert(yCoords, tile[2])           
    end
    -- Sort to get mins and max limits
    table.sort(xCoords)
    table.sort(yCoords)
    -- Define Min and Max values for both X and Y (the limits for the rectangle)
    local minX,maxX,minY,maxY = xCoords[1],xCoords[#xCoords],yCoords[1],yCoords[#yCoords]
    
    -- Generate the random X and Y keeping in mind in Civ2 tiles X and Y have to be of the same type (both evens or both odds)   
    math.randomseed(os.time())
    
    x,y = math.random(minX,maxX),math.random(minY,maxY)

    -- Check that X and Y are both evens or odds
    while x % 2 ~= y % 2 do     
        -- Try again
        x,y = math.random(minX,maxX),math.random(minY,maxY)   
    end
    
    return x,y
end

Regards,

Pablo
 
Dunno where to ask this but can someone create generic rules.txt templates for TOTPP so users don't end up cannibalize existing scenarios to make their own?
 
@JPetroski @Pablostuka @Knighttime @CurtSibling @tootall_2012 @Dadais @civ2units @Patine @techumseh @Civinator

I'm not sure who's using my template at the moment, but there is a bug in combatSettings.lua.

There is a line towards the end of the file:
Code:
               local result = coroutine.yield(false,newAttackerDie,newAttackerFirepower,defenderDie,newDefenderFirepower)

Which should be replaced with

Code:
                local result = coroutine.yield(false,newAttackerDie,newAttackerFirepower,newDefenderDie,newDefenderFirepower)
(defenderDie should be replaced with newDefenderDie)

Without this fix, a defender will defend at the defence value calculated by the game normally, rather than the defence value computed by other means.

Here are a few more lines of code around the issue:
Code:
                --the game runs its default combat algorithm. The designer 
                --can additionally yield modified values for attackerDie, 
                --attackerPower, defenderDie and defenderPower (in this order) 
                --which will be used by the game for that round.

                local newAttackerDie = calculatedAttackerStrength
                local newAttackerFirepower = calculatedAttackerFirepower
                local newDefenderDie = calculatedDefenderStrength
                local newDefenderFirepower = calculatedDefenderFirepower
                local result = coroutine.yield(false,newAttackerDie,newAttackerFirepower,defenderDie,newDefenderFirepower)

                --In this case the coroutine resumes with the result of the round, 
                --a table containing four values:
                    -- winner, this is either attacker or defender.

Let me know if you need help making the fix.

Also, does anyone have a file for @Broken_Erika ?

Dunno where to ask this but can someone create generic rules.txt templates for TOTPP so users don't end up cannibalize existing scenarios to make their own?
 
Found another bug in combatSettings.lua, this one in register.onChooseDefender:
Code:
            defenderValue = (defenderStrength/attackerStrength)*possibleDefender.hitpoints//possibleDefender.type.hitpoints
Should be changed to
Code:
            defenderValue = (defenderStrength/attackerStrength)*possibleDefender.hitpoints/possibleDefender.type.hitpoints
(Integer division '//' should be replaced with regular division '/')

This bug will rate all units with a defence strength less than the attacker strength as having 0 defence, when choosing a defender. So, there is no guarantee that the 'best' unit will defend. I'm not tagging everyone again, just in case I find something else.

The game's apparent 'chooseBestDefender' function used integer division, but it didn't divide by attack strength first (since there were almost no attack bonuses). Hence the bug. I think it would be very rare that this new version of the defender value calculation would give a different answer than the original game, and, if it did, the two units would be equal defenders anyway.

Here's a larger chunk of the code:
Code:
        -- instead of defender strength, however, defenderStrength/attackerStrength is used to account
        -- for attack buffs/debuffs (which are very few in original game)
        local defenderValue = nil
        if attackerStrength == 0 then
            defenderValue = 1e7 -- 10 million
        else
            defenderValue = (defenderStrength/attackerStrength)*possibleDefender.hitpoints//possibleDefender.type.hitpoints
        end
        defenderValue = defenderValue + defenderValueModifier(possibleDefender,tile,attacker)
It is possible your version of combatSettings.lua is a bit different. Let me know if you have any problems making the fix.
 
Also, does anyone have a file for @Broken_Erika ?
I held back on that point, facing few issues :

-first, what about Cosmic2 settings ? I guess we could set them by their default values (except for enhancing ones like unitNumber, techNumber and Terrains per map, at their max value) ?

-Second, I never tested the tech expansion and am afraid of bugging it there without spending quite some time.

-Third, my biggest question, is how about which setting for the layers (for Terrain2-4-6-8) in the rule.txt to choose ? Giving the 16 tiles ones per default to all with the empty purple tileset in terrain2.bmp could do it, but would risk to create issues whenever the user wanna use only the 3 tiles or 1 tile choice.
 
Dunno where to ask this but can someone create generic rules.txt templates for TOTPP so users don't end up cannibalize existing scenarios to make their own?

For those who are interested, I created a generic rules.txt file that includes all 252 science and 189 units slots. I've also created a blank 189 unit.bmp graphic file.

Both can be downloaded from the 'Scenario Creation Excel Sheet' thread located in post #2 here.
 
I've updated the Lua Scenario Template.

The most notable change is the addition of the combatModifiers module. This module provides a relatively simple way to change combat based on a wide variety of circumstances. I will soon leverage this to make a leader bonus module.

I also integrated seasonSettings.lua into the template. At the moment, it is just a place to put your 'season' code, and the template will automatically run it onScenarioLoaded and onTurn. I have started a changeRules module, which will read a rules file and change ephemeral properties in the game, so this may be more useful in the not too distant future.

Some bugs were fixed in radar.lua and combatSettings.lua (the combatSettings.lua bugs are significant, but have one line fixes here and here.).

The function traits.assign now accepts a table of objects.

Text.lua now has text.anglicise(str)->str which converts non-English characters to a close English equivalent. This has been added to makeObject.lua, so special characters in names aren't just deleted. For example, Fallschirmjäger was converted to Fallschirmjger in the old system, now it is Fallschirmjager.

Here are some test examples for the combatModifiers module. More documentation is in registerCombatModifiers.lua
Code:
--  example traits assignment (in setTraits.lua)
traits.allowedTraits("religiousBuilding","mounted","siege engine","science wonder")

traits.assign(gen.original.iCathedral,"religiousBuilding")
traits.assign(gen.original.iTemple,"religiousBuilding")

traits.assign({gen.original.uHorsemen, gen.original.uKnights, gen.original.uCrusaders,
        gen.original.uDragoons, gen.original.uCavalry}, "mounted")

traits.assign({gen.original.uCatapult, gen.original.uCannon, gen.original.uArtillery},"siege engine")

traits.assign({gen.original.wCopernicusObservatory, gen.original.wIsaacNewtonsCollege, gen.original.wSETIProgram},"science wonder")
--]]
--
--
--[[
--
-- air units get an attack bonus against ancient units
combatMod.registerCombatModificationRule({
    attacker = {gen.original.uFighter, gen.original.uStlthBmbr, gen.original.uStlthFtr, gen.original.uBomber, gen.original.uHelicopter},
    defender = {gen.original.uWarriors, gen.original.uPhalanx, gen.original.uArchers, gen.original.uLegion},
    aCustomAdd = 3,
})

-- special bonus for defending on Furs or Game (x4 instead of regular)
combatMod.registerCombatModificationRule({
    -- attacker can be anyone
    -- defender can be anyone
    defenderDetail = {gen.original.tFurs, gen.original.tGame},
    dModifier_tundraSpecial = 4,
})

-- Alpine Troops negate tundra special defense bonus,
combatMod.registerCombatModificationRule({
    attacker = gen.original.uAlpineTroops,
    dOverride_tundraSpecial = 1,
})
-- Alpine troops get a defensive bonus of x6 on tundra specials instead of x4
combatMod.registerCombatModificationRule({
    defender = gen.original.uAlpineTroops,
    dOverride_tundraSpecial = 6,
})
-- Crusaders get +4 defense when defending a city with a "religiousBuilding" (temple/cathedral)
-- (or city has cathedral via mike's chapel)
combatMod.registerCombatModificationRule({
    defender = gen.original.uCrusaders,
    defenderDetail = "religiousBuilding",
    --combatSpec = {dCustomAdd = 4 },
    dCustomAdd = 4,
})


-- units with the mounted trait get defense bonuses on
-- flat terrain, and different bonuses on rough terrain
combatMod.registerCombatModificationRule({
    defender = "mounted",
    [gen.original.bDesert] = 2,
    [gen.original.bPlains] = 2.5,
    [gen.original.bGrassland] = 2.5,
    [gen.original.bHills] = 1.5,
    [gen.original.bMountains] = 0.5,
    [gen.original.bTundra] = 1.5,
    [gen.original.bForest] = 1.5,
    [gen.original.bJungle] = 1,
    [gen.original.bSwamp] = 0.75,
    [gen.original.bGlacier] = 0.5,
})

-- 'siege engines' (catapult, cannon, artillery) reduce
-- the effectiveness of fortifications when attacking
combatMod.registerCombatModificationRule({
    attacker = "siege engine",
    dCityWalls = 2,
    dFortress = 1.5,
    dFortified = 1.25,
})
-- fanatics and siege engines are particularly good at defending fortified positions
combatMod.registerCombatModificationRule({
    defender = {"siege engine", gen.original.uFanatics},
    combatSpec = {
        dCityWalls = 4.5,
        dFortress = 3,
        dFortified = 2,
    },
})
-- A granary increases the defensive value of city walls to 5
combatMod.registerCombatModificationRule({
    defenderDetail = gen.original.iGranary,
    dCityWalls = 5,
})
-- Note, if siege engine attacks fanatics in a city with walls,
-- the granary bonus is largest bonus, so the fanatic bonus is ignored
-- So, granary bonus: +2, siege engine debuff = -1 --> 3+2-1 = 4
-- city walls impart x4 defence.



-- mech inf gives a bonus when either another mech inf
-- or an armor attacks from the same tile
-- a single mech inf doesn't get the bonus
combatMod.registerCombatModificationRule({
    attacker = {gen.original.uArmor, gen.original.uMechInf},
    attackerDetail = gen.original.uMechInf,
    aCustomAdd = 2,
})

-- with computers, battleships don't lose firepower during shore bombardment
combatMod.registerCombatModificationRule({
    attacker = gen.original.uBattleship,
    attackerDetail = gen.original.aComputers,
    aFirepowerShoreBombardmentCheck = false,
})

-- tanks get a bonus when the defender is not veteran
combatMod.registerCombatModificationRule({
    attacker = gen.original.uArmor,
    customCheck = function(attacker,defender)
        return not defender.veteran
    end,
    aCustomMult = 1.25,
})

-- tribes with a 'science wonder' get a 10% boost to combat power
-- (Note: this will apply regardless of whether the wonder is expired,
-- unless the trait is conditional on the wonder not being expired)
combatMod.registerCombatModificationRule({
    attackerDetail = "science wonder",
    aCustomMult = 1.1,
})
combatMod.registerCombatModificationRule({
    defenderDetail = "science wonder",
    dCustomMult = 1.1,
})
-- tribes with an unexpired happiness wonder get 20% combat bonus
-- due to high morale
combatMod.registerCombatModificationRule({
    attackerDetail = {gen.original.wHangingGardens, gen.original.wCureforCancer},
    aCustomMult = 1.2,
})
combatMod.registerCombatModificationRule({
    defenderDetail = {gen.original.wHangingGardens, gen.original.wCureforCancer},
    dCustomMult = 1.2,
})
 
Hi Prof.,

I took the time to update my files with your latest versions. I noticed that in the LuaCore folder that there was a file called 'manager.lua' which no longer appears to exist in the latest incarnation of the template.

Is this correct? Should I simply delete it from my folder?
 
Hi Prof.,

I took the time to update my files with your latest versions. I noticed that in the LuaCore folder that there was a file called 'manager.lua' which no longer appears to exist in the latest incarnation of the template.

Is this correct? Should I simply delete it from my folder?
I don't have a file with that name in my LuaCore, and I don't even remember it. You should be able to delete it. I guess you can rename it first, to see if anything breaks when you load the game. It is possible I started writing some code, then stopped and later deleted the file. You could look at the file contents. If there isn't much in the file, then that is probably what happened.
 
I don't remember when I may have downloaded this file but does it look familiar to you?

In any case, it doesn't seem to have any impact.
 

Attachments

I don't remember when I may have downloaded this file but does it look familiar to you?

In any case, it doesn't seem to have any impact.
This can be deleted, no problem. It looks like an earlier start I had with making a system to ensure that required files are sufficiently recent. I introduced equivalent functionality to the template not to long ago (with the functions in the general library).
 
The template has been updated.

The promotions module was not promoting munition users, so that was fixed. (Fixes extend to munitions.lua and events.lua.)

changeRules.lua

This update introduces changeRules.lua, which provides functions to read a rules.txt style file, and change the ephemeral lua fields to reflect the new rules. You can also choose to read only part of a rules file, so you don't have to have a complete file for every single update. E.g. you can have seasons change terrain, and time change unit stats, without having to have a file for each combination of season and unit progression.

The module also introduces the table changeRules.authoritativeDefaultRules. This table keeps track of what the rules are 'supposed' to be by default, so you can revert back to them (or refer to them at all for some COSMIC2 data). For example, suppose you have a function to give a damaged sea unit extra movement, so it can still go somewhere. You can use the authoritativeDefaultRules as the source of the correct movement allowance when a different unit is activated.

As always, let me know if you have difficulty using a feature, or run into any bugs.
 
@Prof. Garfield I hope you will find this encouraging feedback and sincere gratitude. I have personally observed the immense power of the template before in bringing Hinge of Fate (a considerably complex undertaking) to life much sooner than I could have hoped after plodding through previous titles in a pre-template world.

I now have something that I think is remarkable to share.

In my first Cold War scenario, I had two features that were extremely time-consuming for me to create. Granted, a large part of this was my own lack of knowledge, but it was also because I didn't have a template that made things easy in numerous ways.

One feature was a mechanism where the AI would automatically spawn rebels in different countries throughout the globe (aiSpawn).
The second was a mechanism where the human player could spawn rebels throughout the globe (rebelSpawn)

Both of these features are returning to the new version of this scenario I'm building. Here is the difference.

In the first version, aiSpawn took 1,759 lines of code.
In the new version, it takes 147.

In the first version, rebelSpawn took 13,421 lines of code.
It now takes 785.

The amount of time saved is astronomical. The chance for bugs is exceptionally limited. Further, with the way the template is currently designed, everything I might reasonably wish to change (chance %, number of units that spawn, etc.) is controlled in the parameters file in a handful of lines with values that can be quickly changed.

I know you've put a **tremendous** amount of work into this template and just wanted you to know it really helped me out, so thank you. There have been many other time savings as well but I thought this code reduction was pretty darned impressive. Also, not for nothing, but the old 15,000+ lines of code also required hand-taken coordinates for however many countries I had (at least 60+) whereas the new system I just needed to identify a city location (which I didn't need to look up myself, as the object file comes pre-built with it) and the units would randomly scatter within range of it. That alone solves 95% of potential bugs and saves probably WEEKS of work. So thanks!

I figured I'd just rave for a second in your thread so some who haven't used this can get an idea of what it can do/how helpful it is.
 
I just updated a fix to combatCalculator.lua. I'm pretty sure the bug was introduced recently when I modified the file to facilitate a couple combatModifier features.
 
@Prof. Garfield I had to code this generic function to retrieve a random coordinate (just x,y) inside a bigger rectangle. If you find it useful feel free to add it to the generalLibrary.lua :) It doesn't take into account the Z coordinate and doesn't convert to specific tile objects as this is done by any calling/wrapper function. It's been successfully tested in the Lua online interpreter with a couple of examples - https://www.lua.org/cgi-bin/demo
I wrote gen.getRandomTileInPolygon instead, since it is more general. Thanks for the suggestion.

The template is updated with the above function, and also a small feature expansion to gen.calculateWeight (function derived weights can now return false).
 
There's something I'm a bit fuzzy on, and I think I've heard it is possible from a few sources. If a scenario is made for the original (122, I think?) slot update The Nameless One originally came up with (such as the Korean War scenario I made and McMonkey improved, as a VERY good example of where I'm going here), and you just wanted to add a few more units (like maybe 7 or 8, or a line or two), can you just copy and paste the extra unit slots, stick an empty barbarian leader at the end, and not insert 189-slot-plus-barbarian-leader file and have all those empty, pink unit-slots glaring back at you?
 
There's something I'm a bit fuzzy on, and I think I've heard it is possible from a few sources. If a scenario is made for the original (122, I think?) slot update The Nameless One originally came up with (such as the Korean War scenario I made and McMonkey improved, as a VERY good example of where I'm going here), and you just wanted to add a few more units (like maybe 7 or 8, or a line or two), can you just copy and paste the extra unit slots, stick an empty barbarian leader at the end, and not insert 189-slot-plus-barbarian-leader file and have all those empty, pink unit-slots glaring back at you?

I believe you can have any multiple of whatever the row length is, but I don't think anyone has ever tried it. I'd honestly just set it to 189 because then you have samples that work without any messing. You can just leave most of them blank if you don't actually need all the units.

Although I will say, Patine, if you of all people don't use every damn unit slot there is in your scenarios we will all beat you with a fish.

:lol::p
 
I believe you can have any multiple of whatever the row length is, but I don't think anyone has ever tried it. I'd honestly just set it to 189 because then you have samples that work without any messing. You can just leave most of them blank if you don't actually need all the units.

Although I will say, Patine, if you of all people don't use every damn unit slot there is in your scenarios we will all beat you with a fish.

:lol::p
Well, then, I will make several A0/D1/H1/F1 Barbarian Naval fish units to swim pointlessly and esthetically around the Korean Peninsula to fill slots. :P

Seriously, though, thank-you for the prompt response.
 
There's something I'm a bit fuzzy on, and I think I've heard it is possible from a few sources. If a scenario is made for the original (122, I think?) slot update The Nameless One originally came up with (such as the Korean War scenario I made and McMonkey improved, as a VERY good example of where I'm going here), and you just wanted to add a few more units (like maybe 7 or 8, or a line or two), can you just copy and paste the extra unit slots, stick an empty barbarian leader at the end, and not insert 189-slot-plus-barbarian-leader file and have all those empty, pink unit-slots glaring back at you?
Yes. The unit.bmp file just need slots equal to your number of units + the barbarian one.

You can thus adapt it to your whish.

You may also use the sample 189unitslot bmp file but just use the number of slots you need considering your ruleset (+ put the barbarian leader at the end of them instead of the last slot of the bmp file)
 
I've implemented a "leaderBonus" module.

Leader bonuses work as follows:

In the leaderBonusSettings.lua file (or, somewhere else, if you want to), you define "leaderClasses." In the leaderClass, you can define the "rank" (name of the class), the "seniority" (which leaderClass outranks another leaderClasses), the kinds of units the leaderClass can lead, and initialise units that are either leaders at the start of the scenario, or unitTypes that are always that leaderClass. You can also define how far away leaders can influence units, and register a combat bonus for that leader.

Whenever a unit activates (or engages in combat), it checks nearby tiles for eligible leaders, and chooses the highest ranking leader that can influence the unit. A unit will have only one leader at a time, even if a lower ranking leader would give it a better bonus in a given situation. If you want to give some kind of non-combat bonus (or not use the combatModifier system), you can check a unit's leader's rank with leaderBonus.getCommanderRank(unit).

The onGetFormattedDate.lua file now also has (commented out) code to show how to replace the date in the status window with the unit's rank (or its leader's rank) when a unit is active.

If anyone has any questions, needs help integrating this functionality into an existing scenario, or runs into problems or errors, please let me know.
 
Back
Top Bottom