[QUESTION] Add (max) HP on taking damage? / [now: Veterancy Mod]

It’s not the line of code itself that is the problem; it’s something to do with the subsystem…

I changed that one line of code to SobGroup_CreateSubSystem(CustomGroup, "FighterProduction") just as a test, and it worked just fine.

Ha! Apparently if it’s not a type string that the game recognizes (such as "FighterProduction", "Research", etc.), the subsystem name has to be in all caps ("VET_MOD_RANK1").

oO strange, to say the least !

Okay, nope, I lied… It does not have to be in all caps.

But!!! The ship building the subsystem has to have the line addAbility(NewShipType,"CanBuildShips",1,"","");, and the subsystem to be built has to be included in the race’s def_build.lua.

The downside is that now your build list is cluttered…

doesn’t explain why it worked when you changed to SobGroup_CreateSubSystem(CustomGroup, ā€œFighterProductionā€)
The need of the addAbility ā€œCanBuildShipsā€ should exist in the two cases, no ?

[quote=ā€œDwarfinator, post:43, topic:1245263ā€]
The need of the addAbility ā€œCanBuildShipsā€ should exist in the two cases, no ?
[/quote]I had that in there before. It’s when I started removing lines of debug code that I discovered that particular requirement.

ah ok, makes sense

Version 2.1

Rank 1: Gains +15% health
Rank 2: Gains +15% speed
Rank 3: Gains hyperspace capability
Rank 4: Gains +15% damage, +15% accuracy
Rank 5: Gains cloak ability

2 Likes

Holy smokes! NICE. :smiley: I see that a good bit of extra stuff ended up being necessary… well done! This is really impressive, and it works fantastically over here: all upgrades applied and working on schedule. Absolutely beautiful.

So my intent now is to go ahead and add veterancy to all frigates and capital ships.

  • Will my previous tactic of using CFHodEd to quickly add hardpoints be sufficient? I notice that your testfrigate UI drawing looks different, so I’m thinking maybe you did something extra to the model?

  • What code needs to be added to .ship files – same as before? The .ship file for the test frigate has ā€œaddShieldā€ and ā€œaddAbilityā€ lines that are new, and probably others I’m missing (and is arranged differently!), but I’m not sure if they’re part of the testing process or absolutely necessary.

(And, just in case you didn’t see above, do you happen to have a Steam wishlist with anything on it? :slight_smile: )

Thanks!

I didn’t mess with the model you sent at all. Honestly, I’m a little hesitant to use the new CFHodEd, given @BitVenom’s persistent insistence, and I’m still feeling my way around 3DSMax… But, that’s just me. :slight_smile: And no, I didn’t do anything with the UI.

The only necessary lines of code to add are:

addAbility(NewShipType,"CanBuildShips",1,"","");
addCustomCode(NewShipType,"data:scripts/lib/veteran.lua","","Create_VeteranShip","Update_VeteranShip","Destroy_VeteranShip","hgn_torpedofrigate_test",1);
StartShipHardPointConfig(NewShipType,"Veteran1","Hardpoint_Veteran1","System","Innate","Indestructible","","vet_mod_rank1","","","","","","","");
StartShipHardPointConfig(NewShipType,"Veteran2","Hardpoint_Veteran2","System","Innate","Indestructible","","vet_mod_rank2","","","","","","","");
StartShipHardPointConfig(NewShipType,"Veteran3","Hardpoint_Veteran3","System","Innate","Indestructible","","vet_mod_rank3","","","","","","","");
StartShipHardPointConfig(NewShipType,"Veteran4","Hardpoint_Veteran4","System","Innate","Indestructible","","vet_mod_rank4","","","","","","","");
StartShipHardPointConfig(NewShipType,"Veteran5","Hardpoint_Veteran5","System","Innate","Indestructible","","vet_mod_rank5","","","","","","","");

The only other line of code I added to the ship file was addAbility(NewShipType,"CloakAbility",0,2,0,4000,1,1,1,1000); since the final rank granted cloaking. (The addShield line was already present, it just got jumbled around in the midst of my code changes.)

Mind you, you don’t actually have to use the rank bonuses I did; those was just arbitrary choices on my part, just to fill the void.

I did see it, but… http://steamcommunity.com/profiles/76561198030787818/wishlist :wink:
Edit: Well, it was empty when I posted the link…

1 Like

Thanks for the quick help!

I didn’t mess with the model you sent at all. Honestly, I’m a little hesitant to use the new CFHodEd, given @BitVenom’s persistent insistence, and I’m still feeling my way around 3DSMax…

Gotcha! Yeah, I saw him saying that CFHodEd is likely to foul up on you; but it seems to take quite a bit more effort to just add an empty hardpoint with 3DS or Blender, and my hope was that something so small would be okay.

(The thing I meant is the wireframe-ish drawing when selecting a unit – looks different than default torpedo frigate, but maybe it’s actually from when I edited the hardpoints – no problem, I’d go without a UI at all if it got in veteran units!)

Mind you, you don’t actually have to use the rank bonuses I did; those was just arbitrary choices on my part, just to fill the void.

I like 'em! I actually wrote up a little explanation for each of them, either just for fun or possibly to be included in the ā€œreadmeā€, if you’re looking in that direction :wink: : ā€œRank 2: The crew learns to squeeze a little extra juice out of the engines. Rank 4: The gunners hone their craft and begin to target weak points. Rank 5: HQ presents your ship with a medal of honor… and a cloak generator.ā€ Etc!

I did see it, but… http://steamcommunity.com/profiles/76561198030787818/wishlist :wink:
Edit: Well, it was empty when I posted the link…

Ah, wishlist goblins – big problem over here too. But maybe another item can be scratched off soon… :smiley:

Hmmm, I don’t see that at all…

Haha! :smiley:

You might want to dig in the Complex mod’s files to figure out they did ship experience without using ā€œCanBuildShipsā€

@Hell_Diguner: That’s interesting; I didn’t know anyone else had ever managed it – I asked around a few places and I was told it was impossible to add veterancy (but I thought I’d try it anyway, and then radar turned his genius to bear on it :slight_smile: ). Thanks for the tip!

I don’t want to mess with trying a new system yet, since I’m really liking how this is so tunable and customizable, and I’m thinking that another way probably won’t be as flexible (but maybe!), but it’s definitely worth looking into sometime.


Right now, VetMod v. ā€œ2.2ā€ is working excellently – I’ve added veterancy to all frigate and capital Hiigaran ships (excepting shipyard and mothership) and no problems have appeared, knock on wood. The noble testfrigate has been removed (but never forgotten), and the ranks and exp levels tweaked a bit.

I felt the current exp progression is actually pretty close to perfect, so I just made it take a little longer to get to 1 and 2, and a little sooner to get to 3, 4, and 5. This is just based on how many ships make it to each level in the couple of skirmish games I played, and the ā€œfeelā€ of when they start reaching them. (You could tell that the biggest gap was between 2 and 3, for example.)

However, we’ll see if anyone makes it to 5 (or, conversely, if too many do) in future, more serious games. The modifiers given at each rank have also been Jormungandrized – mainly just by switching the ordering around a bit:

Rank 1: Shipshape: The crew learns to hustle when it’s time to do drills and maintenance. HP Regen +15%
Rank 2: Full Steam Ahead: Engineering gets to know their ship, and with a bit of tuning learns to squeeze a little extra juice out of the powerplant. Speed +10%
Rank 3: Benefit of Rank: HQ doesn’t want to lose veteran any servicemembers. The latest in reactive armor technology is installed. HP +15%
Rank 4: Just Don’t Lead 'Em As Much: The gunners know and love their weapons… and enemy weak points. Weapon Accuracy & Damage +15%
Rank 5: Form 126C.13-8a: For valor in combat, length of service, and being particularly hard to kill, HQ presents your ship with the last word in cloaking technology. Cloaking ability gained

I’m thinking of going to 20% for Rank 1 (regen), since 10% was basically imperceptible, but maybe that’s too high. Also not sure if Rank 5 should be cloaking or hyperspace, or even defense field. Thoughts welcome.


I’m about to go test the campaign with this mod; what I expect is that if a ship reaches rank 3, it will start the next mission with rank 3… but with 0 exp again. My hope and belief is that when that ship gets enough exp for a rank in the new mission, the game won’t have any trouble when it goes to add that rank’s subsystem and finds it already there.

The other thing is that someone might think ā€œhey, my ship got to rank 3, it should start with that much exp!ā€ So I’m looking to see if it’s possible to, e.g., check if a ship has rankN subsystem (and exp < exp-for-rankN), and then automatically set its exp to exp-for-rankN…

…but I’m personally not too bothered if it’s not possible. I mean, if your ships get into heavy combat, they’ll still get ranks, and they’ll still be ā€œindividualizedā€ with their own histories. That’s the main thing, to my mind… and this way, the ranks are balanced for both the campaign and skirmish maps (whereas if the campaign could be made cumulative, the exp for each rank would have to be increased lot, I think).


Anyway, this has already transformed my Homeworld experience – I don’t know about y’all, but the spaceships are the main attraction for me, above and beyond the storyline, tactics, competition, and building… and I’m a naval history buff, so when I think ā€œwarshipā€ I think giant, magnificent, almost-living machines that have names, histories, and accomplishments.

A fleet of interchangeable, assembly-line ships that could have been in a hundred battles or none, that show no sign of their noble and valorous past… well, it’s just not the same. I better wrap it up before I get too poetic ;). so let me just say: three cheers for @radar3301, and a big thank-you to @Dom2, @pascal76680, and everyone else who has contributed!

1 Like

That’s easily fixable.

Maybe I should finish reading your post first, because that’s pretty much what I going to do.

Or maybe more ranks?


The nice thing about this system, is that it can be completely customized. I envisioned the above as just a template to start from. The possibility exists to customize each (type of) ship to have it’s own exp curve, it’s own bonuses, etc. I could even re-write the code to say that for this ship, you get this bonus, and for that ship, you get that bonus. In fact, I just might go do that…

Update to version 3.5.

Version 3.5 contains the following changes:

  • The rank of veteran ships now persists through singleplayer missions.
  • Shorter variable names
-- ####################################
-- # data:scripts/lib/veteran.lua     #
-- # by radar3301                     #
-- #                                  #
-- # Version: 3.5 - 20160307.0443 UTC #
-- # ##################################

-- ##############################
-- #  BEGIN  TUNABLES  SECTION  #
-- # THIS SECTION CAN BE EDITED #
-- #   except as noted below    #
-- ##############################

function Veteran_Init() -- DO NOT EDIT THIS LINE
    AddVeteranRank("default", 70, "vet_healthregen")
    AddVeteranRank("default", 160, "vet_speed")
    AddVeteranRank("default", 280, "vet_health")
    AddVeteranRank("default", 430, "vet_weaponaccdmg")
    AddVeteranRank("default", 590, "vet_hyperspace")
    
    -- NOTE: iRankNum is 1-based
    -- ModifyVeteranRank(<sShipName>, <iRankNum>, <iExpNeeded>, <sBonus>)
    -- ModifyVeteranExp(<sShipName>, <iRankNum>, <iExpNeeded>)
    -- ModifyVeteranBonus(<sShipName>, <iRankNum>, <sBonus>)
    ModifyVeteranExp("hgn_torpedofrigate_test", 3, 300)
    ModifyVeteranRank("hgn_torpedofrigate_test", 4, 400, "vet_weaponacc")
    ModifyVeteranBonus("hgn_torpedofrigate_test", 5, "vet_weapondmg")
    -- AddVeteranRank("hgn_torpedofrigate_test", 750, "vet_hyperspace") -- needs a 6th hardpoint!
    
end -- DO NOT EDIT THIS LINE

-- How often the script updates
-- This number should be the same as the last argument
-- in the addCustomCode line of the ship file
UPDATE_FREQUENCY = 1

-- ##########################
-- # END  TUNABLES  SECTION #
-- ##########################


-- ############################
-- #   BEGIN  CODE  SECTION   #
-- # DO NOT EDIT THIS SECTION #
-- #   unless you know what   #
-- #      you are doing       #
-- ############################

dofilepath("data:scripts/lib/util.lua")

-- globals
vet_statics = {
    default = { nRanks = 0 }
}
vet_ships = {}

-- helpers
function AddVeteranRank(_shipname, _exp, _bonus)
    assertstring(_shipname, 1)
    assertnumber(_exp, 2)
    assertstring(_bonus, 3)
    if (vet_statics[_shipname] == nil) then
        vet_statics[_shipname] = { nRanks = 0 }
    end
    local static = vet_statics[_shipname]
    local n = static.nRanks + 1
    static["rank"..n] = { e = _exp, b = _bonus }
    static.nRanks = n
end

function ModifyVeteranRank(_shipname, _rank, _exp, _bonus)
    assertstring(_shipname, 1)
    assertnumber(_rank, 2)
    assertnumber(_exp, 3)
    assertstring(_bonus, 4)
    __ModifyVeteranRank(_shipname, _rank, _exp, _bonus)
end

function ModifyVeteranExp(_shipname, _rank, _exp)
    assertstring(_shipname, 1)
    assertnumber(_rank, 2)
    assertnumber(_exp, 3)
    __ModifyVeteranRank(_shipname, _rank, _exp, nil)
end

function ModifyVeteranBonus(_shipname, _rank, _bonus)
    assertstring(_shipname, 1)
    assertnumber(_rank, 2)
    assertstring(_bonus, 3)
    __ModifyVeteranRank(_shipname, _rank, nil, _bonus)
end

function __ModifyVeteranRank(_shipname, _rank, _exp, _bonus)
    if (vet_statics[_shipname] == nil) then
        vet_statics[_shipname] = deepcopy(vet_statics["default"])
    end
    local static = vet_statics[_shipname]
    local rank = static["rank".._rank]
    assertnotnil(rank, "rank".._rank.." does not exist for ".._shipname)
    if (_exp   ~= nil) then rank.e = _exp   end
    if (_bonus ~= nil) then rank.b = _bonus end
end

-- addCustomCode FUNCTIONS
function Create_VeteranShip(CustomGroup, playerIndex, shipID)
    if (vet_ships[shipID] ~= nil) then
        print("ERROR: shipID "..shipID.." already exists!")
    end
    
    vet_ships[shipID] = {
        id = shipID,
        name = "ship"..shipID,
        timeCreated = Universe_GameTime(),
        xp = 0,
        rank = 0,
        checked = 0
    }
end

function Update_VeteranShip(CustomGroup, playerIndex, shipID)
    local ship = vet_ships[shipID]
    
    if (ship.checked == 0) then
        local health1 = SobGroup_GetHardPointHealth(CustomGroup, "Veteran1")
        local health2 = SobGroup_GetHardPointHealth(CustomGroup, "Veteran2")
        local health3 = SobGroup_GetHardPointHealth(CustomGroup, "Veteran3")
        local health4 = SobGroup_GetHardPointHealth(CustomGroup, "Veteran4")
        local health5 = SobGroup_GetHardPointHealth(CustomGroup, "Veteran5")
        ship.checked = 1
        
        if (health5 > 1) then
            ship.rank = 5
        elseif (health4 > 1) then
            ship.rank = 4
        elseif (health3 > 1) then
            ship.rank = 3
        elseif (health2 > 1) then
            ship.rank = 2
        elseif (health1 > 1) then
            ship.rank = 1
        end
        if (ship.rank > 0) then
            ship.xp = vet_statics[CustomGroup]["rank"..ship.rank].e
        end
    end
    
    if (SobGroup_UnderAttack(CustomGroup) == 1) then
        ship.xp = ship.xp + UPDATE_FREQUENCY -- gain xp based on how long in combat
    end
    ship.xp = ship.xp + UPDATE_FREQUENCY * 0.1 / (ship.rank + 1) -- gain xp based on how long alive, less experience gained at higher ranks for being alive
    
    local static = vet_statics[CustomGroup]
    if (static == nil) then
        static = vet_statics["default"]
        if (static == nil) then
            error("unable to find the default veteran static")
        end
    end
    
    if (ship.rank < static.nRanks) then
        local rank_stat = static["rank"..(ship.rank+1)];
        if (ship.xp >= rank_stat.e) then
            ship.rank = ship.rank + 1
            SobGroup_CreateSubSystem(CustomGroup, rank_stat.b)
        end
    end
end

function Destroy_VeteranShip(CustomGroup, playerIndex, shipID)
    vet_ships[shipID] = nil
end

-- INITIALIZATION
Veteran_Init()

By the way, I’m also planning for version 4. The major update for v4 will be moving non-standard veterancy award modifiers out of the core veteran file into a ship specific file. Also, I may introduce an experience curve function.

3 Likes

Fantastic stuff. :smiley: Seriously amazing. I can’t wait to replay the campaign with this! (I think the shorter variable names are what all of us have been most looking forward to, though…)

As I mentioned on Steam, I’ve reduced the exp numbers. I’m trying to fit them to my ā€œrank theoryā€ (rank 3 --> elite, rank 4 --> special forces, and rank 5 only seen on 3-4 legendary ships in a long struggle). I just remembered that I was going to see if increasing CPU difficulty meant my games got long enough to make the new numbers too low, though.

I can upload my ā€œplaytesting versionā€, if you’d like to experiment yourself – it’s nothing you couldn’t do, but at least you won’t have to go through and edit all the ships, heh.

Edit: What is this util.lua reference I see? :open_mouth:

util.lua:

(updated)

-- deprecated
-- function checktype(var, _type)
    -- if (type(var) ~= _type) then
        -- error(_type.." expected, got "..type(var))
    -- end
    -- return 1
-- end

function asserttype(var, _type, argn)
    if (type(var) ~= _type) then
        if (argn == nil) then
            error(_type.." expected", 2)
        else
            error("arg #"..argn.." expects a ".._type, 2)
        end
    end
end

function assertstring(var, argn)
    asserttype(var, "string", argn)
end

function assertnumber(var, argn)
    asserttype(var, "number", argn)
end

function asserttable(var, argn)
    asserttype(var, "table", argn)
end

function assertnotnil(var, message)
    if (var == nil) then
        error(message)
    end
    return 1
end

function deepcopy(orig)
    local orig_type = type(orig)
    local copy
    if orig_type == 'table' then
        copy = {}
        for orig_key, orig_value in orig do
            copy[deepcopy(orig_key)] = deepcopy(orig_value)
        end
    else -- number, string, boolean, etc
        copy = orig
    end
    return copy
end

This may or may not make it into the next version…

Check the updated definition for ā€œutil.luaā€

I’m not yet sure what effect the 2.0 patch had on this mod, but if there is still some interest in this, I can look into reviving/working on it more.