Modular Data/Code Design - Introducing doscanpath

Many of you are likely aware of what ‘dofile’ in a LUA script does…

But while working on some internal stuff, we decided that having to hard-code every file path and fall prey to hard-coding large systems… we’d instead allow LUA programmers to work smart.

Let me give you an example of something we did just an hour or so ago:

So there’s this file for each Race, called ‘def_research.lua’ - it’s the standard massive, hard-coded list of research items you are all probably sick of seeing (I know I am!). And it sucks. With the new ‘props’ system, you can define a new research file for each GR you have - so that instead of having a huge list of research that you manually have to restrict for each mode (what we initially did for SP/MP this patch) - you can have a single file for each GR, with only the research you need in that situation.

Still, this results in 2 large research lists that are nearly identical. Why wants to keep those up to date?!

So, this afternoon, I (along with Mr Cole) attempted a different solution using one of the new tools we made: doscanpath.

Let me show you some script:

Hiigaran - def_research.lua:

Icon_Speed = "data:ui/newui/research/icons/speed.mres"
Icon_Health = "data:ui/newui/research/icons/health.mres"
Icon_Tech = "data:ui/newui/research/icons/tech.mres"
Icon_Ability = "data:ui/newui/research/icons/ability.mres"
Icon_Build = "data:ui/newui/research/icons/build.mres"
Modifier = 0
Ability = 1
AllShips = 0
Family = 1
Ship = 2

research = {}
res_index = 1

doscanpath("data:Scripts/Races/Hiigaran/Scripts/Research", "*.lua")

The start of that file should be very familiar to you - it hasn’t changed. What has changed, however, is that now this file doesn’t contain any research. It sets up the global and enums required, but then says ‘load every lua you find in Research’.

Here’s a small example of one of those files:

base_research = {
{ 
    Name =            "InstaAdvancedFrigateTech",
    RequiredResearch =    "",                                               -- <list of prerequisite research items>
    RequiredSubSystems =    "AdvancedResearch",                              -- <list of prerequisite sub systems>
    Cost =             0,
    Time =             0,
    DisplayedName =        "",
    DisplayPriority =    1,                                                    -- <display priority (used to sort on screen)>,
    Description =        "Instant Tech",            -- To Localize?
    TargetName =         "Hgn_Mothership",
},
{
    Name =                    "GraviticAttractionMines",
    RequiredResearch =        "",
    RequiredSubSystems =    "AdvancedResearch & CorvetteProduction",
    Cost =                     750,
    Time =                     40,
    DisplayedName =            "$7500",
    DisplayPriority =        2,
    Description =            "$7501",
    TargetName =         "Hgn_MinelayerCorvette",
    Icon = Icon_Tech,
    ShortDisplayedName = "$7200",
},
{
    Name =                    "PlatformIonWeapons",
    Required =        "",
    RequiredSubSystems =    "Research | AdvancedResearch & PlatformProduction",
    Cost =                     600,
    Time =                     100,
    DisplayedName =            "$7502",
    DisplayPriority =        24,
    Description =            "$7503",
    TargetName =         "Hgn_IonTurret",
    Icon = Icon_Tech,
    ShortDisplayedName = "$7201",
}, }

-- Add these items to the research tree!
for i,e in base_research do
    research[res_index] = e
    res_index = res_index+1
end

So, now, when the def_research file is loaded, the actual list of research is pulled from any files present. We can create sub-folders for different needs/GR, and execute doscanpath twice, one for the base, and again for whatever variants we want, in a smaller variant of def_research for our specific GR.

Much less effort and upkeep, easier to understand, easier to mod and mutate, etc. All good stuff.

For those of you proficient in LUA, you can probably imagine other ways to use this - AI ‘sub logic’ factories, instance-able class libraries, etc. I can’t go into details, but we have some INSANE stuff here that leverages this. The sorts of programming you almost never get to do in LUA.

Also, if you notice that little snippet of code at the end of the research example - that adds the research in one file to the global research[] array - that is the worst way to add two tables, but our ancient version of LUA doesn’t support any of the new good stuff. You could also, instead of allowing that code to run, wrap it in code to think about running - useful for maybe gathering a bunch of data but filtering it right at the very end (using a series of global switch, etc). The sky is the limit…

This exact same pattern can be used to rebuild the build lists and other related data, FYI.

If you have any questions, let me know!

3 Likes

only one in fact : where will you stop ? You are crazy guys, seriously… and I won’t complain ! :grinning:

All this in Lua4 and we’re the crazy ones? :stuck_out_tongue:

Is there anyone organizing a wiki for all these threads?

That. Is. Brilliant! I can’t overstate how amazed I am at what you guys are doing.

1 Like

Now that you suggest it @Chimas, sure. :smiley: http://hwmod.wikia.com/wiki/HWR_Tutorials

@Bitvenom - to clarify, is the doscanpath functionality in regards to research (and build?) already implemented or is this a preview of an upcoming functionality?

The function is new in this patch - and not visibly being used. We just noticed a new way to use it with ‘private’ stuff yesterday and thought it would be a good way to introduce it :wink:

1 Like