Boudicca's Rebellion - Creation Thread

Looks like I forgot to link the munitions module to the state table in events.lua. I added a feature that keeps track of what unit generated the munition, which can be accessed with munitions.getShooter(munitionUnit).

EDIT: If this doesn't work, send me your munitionsSettings and keyPress files. I was also expecting these kinds of things to come up. The only way around it would be to have put in a lot of events myself for testing.
 

Attachments

  • boudicca-events.zip
    3.2 KB · Views: 36
It's still throwing an error so here are the files
 

Attachments

  • munitionsSettings.zip
    1.5 KB · Views: 37
That's a generic object file, it doesn't have uRomanArchers, or any other objects specified. I can build you a basic file from the rules if you would like. If you let me know by ~3:30, I can probably have it done around 4 or 4:30, unless something comes up.

When I removed references to the object file in the munitionSettings, stuff worked.

This suggests to me (and I've already thought this) that it might make sense to have a program that parses the rules, builds a sample object file, and provides objects for stuff like terrain and extra unit attributes.
 
It should have the uRomanArchers in it - at least the one I'm staring at does (unitType7) and uArrows (unitType23). Does yours not show that?

I'd love help with a basic file from the rules if it's a quick thing for you.
 
Also - the event works when the archers have "none" as a home city, as I had them at payload to test things out - the payload restriction message pops appropriately. It's when I go to generate the munition with one that has a home that I get the error. Finally - the munition DOES generate - it just also throws this:

Code:
File number 1 event ending at line 12 successfully parsed.  Event number is 1.
D:\Test of Time\Scenario\Boudicca\LuaCore\munitions.lua:74: attempt to index a nil value (field 'munitionIDGeneratorID')
stack traceback:
    D:\Test of Time\Scenario\Boudicca\LuaCore\munitions.lua:74: in upvalue 'linkMunitionAndShooter'
    D:\Test of Time\Scenario\Boudicca\LuaCore\munitions.lua:431: in function 'munitions.doMunition'
    (...tail calls...)
    ...Time\Scenario\Boudicca\LuaRulesEvents\keyPressEvents.lua:58: in field '?'
    ...Time\Scenario\Boudicca\LuaRulesEvents\keyPressEvents.lua:95: in function 'keyPressEvents.doKeyPress'
 
Did you replace events.lua in the main Boudicca folder with the file I provided below, or did you extract that into one of the other folders, such as LuaCore?

Boudicca's Rebellion - Creation Thread

I'll produce an object file shortly for you. I think I may have extracted the file you provided into the wrong place, since when I searched for uRomanArchers, I didn't find anything.
 
I just downloaded it again to confirm that I replaced it in the main Boudicca folder.
 
I've uploaded three Files, munitions.lua goes into LuaCore, events.lua in the main folder, and object.lua into the parameters file.

If this doesn't work, then I'll need your entire scenario folder, since something has gotten lost at some point. (You can cut out the images to reduce file size.)
 

Attachments

  • TheeBoudiccaFiles.zip
    10.6 KB · Views: 35
is there no justOnce function in this or is it renamed as something else that I've missed?
 
is there no justOnce function in this or is it renamed as something else that I've missed?

I put one in the General Library, along with a more generic version. I should have mentioned it.

--#gen.limitedExecutions(key,maxTimes,limitedFunction)--> void
-- gen.justOnce(key,limitedFunction) --> void

Actually, it looks like I forgot to put justOnce in the function list at the top of the General Library.

Works the same way as before, you need a key for a table, and the key should not conflict with other keys (unless you only want one of the events to happen at most once).

gen.imitedExecutions basically lets you choose to have something happen more than once, up to maxTimes. gen.justOnce invokes limitedExecutions with maxTimes equal to 1.
 
In the spirit of mentioning stuff (and something that should have an example and a test), there is a module for delaying function executions. (delayedAction.lua)

-- Functions that can be delayed will be provided to this module in
-- a table indexed by strings
-- These functions will take a table as their argument.
-- The keys to the argument table must be strings or numbers,
-- and the values must be strings, numbers, or tables
-- (and each table must only have strings, numbers, and tables)
--
-- usage:
-- delay = require("delayedAction")
--
-- To enable a function to be delayed
-- delay.makeFunctionDelayable(functionNameString,function)
--
-- To delay a function use one of these two functions
-- delay.doInFuture(functionNameString,argumentTable,turn,tribeID=-1)
-- performs an action specified by the functionNameString
-- and the argumentTable, on the turn specified
-- happens after production of the tribe associated with tribeID,
-- or onTurn, if submitted tribeID is -1
--
-- delay.doNextOpportunity(functionNameString,argumentTable={},tribeOrTribeID=-1)
-- performs a delayed action the next time a tribe is active, or immediately
-- if the tribe is the currently active tribe. If no tribe is specified,
-- or the tribeID specified is -1,
-- the action will be performed with the onTurn actions for the next turn
-- if no argumentTable is specified, an empty table is generated
 
Perhaps we could:

Spoiler :

Use this for when legions vacate certain areas? For example, if reinforcements are summoned from the north, perhaps a delayed response is that the tribes there spawn units that flood to the south?

This one would probably need a probability of occurring on a turn after a certain delay. If we want a simple example without probability, we could have a certain unpopular Roman tax collector flee a turn after Boudicca shows up.


I was able to accomplish quite a bit for Boudicca today. All cities are placed and filled (sparsely) with city improvements, the units that need to start placed are placed (though maybe we should put the Caledonii units on the map and then "freeze" then via onActivate to show off that feature). Also, the munitions are done (though I need to put in a secondary attack on something just to have it).

I was also thinking it might be helpful if I wrote a "designers readme" that has screenshots showing how the files kind of work together, and how someone might approach an object file, or achieve some of the techniques. I figure I'd take a stab at translating this to designer speak. Not coding level or details, just organization/structure/how this all works.

While the modular system seems overwhelming at first glance, it should be soooo much easier to craft large-scale scenarios with. Also, less error prone one would think.
 
You use for the delayed action makes sense. I don't know enough about the history to know if it would be better to have units 'frozen' on the map until needed, or if creating units as needed would make more sense. I'd say that from an example stand point, creating units is more important but there doesn't seem to be a reason we can't have both.

I was also thinking it might be helpful if I wrote a "designers readme" that has screenshots showing how the files kind of work together, and how someone might approach an object file, or achieve some of the techniques. I figure I'd take a stab at translating this to designer speak. Not coding level or details, just organization/structure/how this all works.

While the modular system seems overwhelming at first glance, it should be soooo much easier to craft large-scale scenarios with. Also, less error prone one would think.

I've been procrastinating on starting the documentation.

I think there is a lot of merit to a "Designer's Guide," and you would be in a better position to write that than me.

Then, we also need the 'technical' documentation, that explains the exact details of use. That will fall on my shoulders. Some of it is already done in the actual module files, but will have to be moved to the settings files and separate files.

I've also been thinking of making some videos as an alternative to my existing lua lessons, which makes use of the templates. Instead of starting by teaching programming, the lessons start by filling in data for the existing "rules" modules, and then start introducing programming after that. Perhaps something along the lines of converting and enhancing an existing scenario.

P.S.
If you want treaties to change outside of events, comment out this line
-- uncomment to disallow diplomacy that is
-- not driven by lua events
diplomacy.setEventTreatiesOnly("You may have received a message about a change in diplomacy. This change has been undone.")
 
When I went to create a unit, civlua wasn't found, so I changed the event to civluaModified, which does not throw an error, yet this still does not happen. Any ideas why?

civluaModified.createUnit(object.uEvocatiFresh, object.tRomans, {{loser.location.x,loser.location.y,loser.location.z}}, {randomize=false, veteran=true})
 
Your file must have the line

local civlua = require("civluaModified")

then you can write

civlua.createUnit

If you want to access functionality provided by a different file, your file must always have the line

local moduleRef = require("myModule")

And then you can use the specific function you want

moduleRef.myModuleFunction(argument1,argument2)
 
I have that line in the unitKilled events. I don't get any error but the unit is not created. I've also messed around with not using {loser.location.x,loser.location.y,loser.location.z}} with the actual location. No error is thrown, but no unit is created, which is strange. Note that the text does display.

Code:
local object = require("object")
local func = require("functions")
local civlua = require("civluaModified")
local unitKilledEvents = {}
-- This will only run when a unit is killed in combat (i.e. not when an event
-- 'kills' a unit)
-- note that if the aggressor loses, aggressor.location will not work
--
function unitKilledEvents.unitKilledInCombat(loser,winner,aggressor,victim,loserLocation,winnerVetStatus,loserVetSatus)
end
-- This will run any time a unit is killed, either in combat or by events
--
function unitKilledEvents.unitDefeated(loser,winner,aggressor,victim,loserLocation,winnerVetStatus,loserVetStatus)
    civ.ui.text("Unit Killed context 2 test")
  
    --Here we see an example of a unitKilled event.  Because I'm using "loser.owner" rather than "loser.type," it basically means "any unit" from the Druids civ.
    --Note that xFuriesText is found in the object file.
    --The next example will show a specific unit type that is killed.
    if loser.owner == object.tDruids and winner.owner == object.tRomans then
    civ.ui.text(func.splitlines(object.xFuriesText))
    end
  
    --Here is how you have a specific unit killed trigger an event.  However, I have also added an "and" because I want to ensure that the event only triggers on a specific tile.
    --Notice that object.lXXvictrixEvocati is a tile (hence the 'l' prefix) found in the object file (line 110).
    if loser.type == object.uLegionStandard and loser.location == object.lXXvictrixEvocati and loser.owner == object.tTheGods and winner.owner == object.tRomans then
    civ.ui.text("Recent retirees of the XX Valeria Victrix answer the call and rally behind their Evocati standard! They are a bit rusty, but battle will cure that!") -- Note you can have text this way as well if you don't want to store it in the object file
    civlua.createUnit(object.uEvocatiFresh, object.tRomans, {{loser.location.x,loser.location.y,loser.location.z}}, {randomize=false, veteran=true})
    civlua.createUnit(object.uEvocatiFresh, object.tRomans, {{loser.location.x,loser.location.y,loser.location.z}}, {randomize=false, veteran=true})   
    -- I create unit - but note that by using loser.location.x and so on, it will create exactly where the legion standard was killed.  This doesn't really matter for this unit (as the legion standard is immobile) but if you wanted to use this to
    -- capture "slaves" or something and have the unit show up in the same place as the defeated unit, this is how you achieve it.
    end
  
    --Here we switch things up a bit.  Here, there is a small chance that a good event will happen, but a greater chance that an unfortunate event will happen.
    if loser.type == object.uLegionStandard and loser.location == object.lLegioIIAugustaCallToArms and loser.owner == object.tTheGods and winner.owner == object.tRomans then
        local probabilityRoll = math.random()
        if probabilityRoll >= .9 then -- 10% chance of good event
                civ.ui.text(func.splitlines("Roman general [insert text] has answered the call to arms!"))
            elseif probabilityRoll < .9 then -- 90% chance of the historic bad event
                civ.ui.text(func.splitlines("Roman general [insert text] has refused the call to arms!"))
            end
  
    end
  
end
-- this happens whenever a unit 'dies', regardless of combat, as long as it is not replaced
function unitKilledEvents.unitDeath(dyingUnit)
end
-- this happens if a unit is deleted (either through combat death, or by some other event,
-- but not if the unit is disbanded)
-- If the unit isn't being replaced, replacingUnit is nil
function unitKilledEvents.unitDeleted(deletedUnit,replacingUnit)
end
return unitKilledEvents
 
Just as an aside - as I write the events I'm going to add hints that hopefully are clear to designers. I would appreciate feedback from folks on if this would be helpful. My goal will be to have a .pdf that has screenshots of this to illustrate different techniques in the designer guide.

Code:
    --Here there is a situation where I want to have specific text depending on what type of unit is killed, but I don't want to have to do 4x events.  In fact, the only "specific" portion of the text that needs to change is the unit name.
    --You do this by using a string.  This is an extremely useful trick that can help minimize the number of events you need.  Another way you can use this is if you use the parameters file to define certain parameters (such as how many
    --gold pieces are lost every time a unit dies).  By using the parameters file, you can change all events that reference it in one place, and by using tostring, you don't have to remember to change each corresponding text file).
    --Here, param.FirstCohortKilledCost is defined in the parameters.lua file within the Boudicca\LuaParameterFiles folder.
    if (loser.type == object.u1stCohortIIAugusta or loser.type == object.u1stCohortIXHispana or loser.type == object.u1stCohortXIVGMV or loser.type == object.u1stCohortXXVV) then
        civ.ui.text("The " ..tostring(loser.type.name).. " has been defeated in battle! This is a tremendous blow to the legion's morale as their sacred eagle has been captured by the enemy! The Romans lose " ..tostring(param.FirstCohortKilledCost).. " gold pieces as a result.")
        object.tRomans.money = object.tRomans.money - param.FirstCohortKilledCost
    end
 
Top Bottom