Fallout Tactics utility FTSE - Fallout Tactics Scripting Engine (0.55a)

Discussion in 'Fallout Tactics Modding' started by Melindil, Jul 3, 2018.

  1. nadeauhugo

    nadeauhugo Author of FOT mod THE SUM Modder

    635
    Mar 9, 2014
    It also allows the manipulation of the text log at the bottom left corner, and the addition of any overhead texts. You can also detect any entity by its name or tagname, and modify most of these entities attributes, excluding hitpoints, poison points and rad points. You can force an item to stay equiped, or unequip any item. You can change the time and date, but I think this only applies at game launch. I hightly doupt you can modify or call the time variables or functions other than at the game launch. If you find a way while exploring FTSE, please let us know, it could be useful to trigger a rest, for example, or to add a delay after completing an action, like for example the NPC in Fallout 1 who prepare poison antidote, triggering some passing of time.
     
  2. nadeauhugo

    nadeauhugo Author of FOT mod THE SUM Modder

    635
    Mar 9, 2014
    While coding with FTSE, I realized that GetEffectMaximum() for a consumable item is not working at all. It results in the code not being compiled, so the following code is uselss. GetEffectMinimum() does work, however.
     
  3. Reinar

    Reinar First time out of the vault

    88
    Oct 23, 2011
    Good news! With some story...
    A few days back, I returned to my idea of making Firefly: Tactics. I once again started trying new, unorthodox stuff. Yesterday I was trying to simulate environment without oxygen, which would need you to wear a proper environmental armour (space suit). That would open missions in space - salvage operations on destroyed ships, secret base on asteroid and the like. At first, I used radiation. It was looking really promising, but then I realized that radiation in FT can't kill you, even in insane amounts. It just lowers stats and that's it. So I moved on and fiddled more with FTSE. After a while, I decided to look at the source code of FTSE, thinking maybe I could take the flag after melindil. Realizing it's in C++ made me hesitate, so I don't promise anything. But I looked at the code and found out that there are two undocumented functions on the Actor class: GetField(name) and SetField(name, value). They can be used to read/modify these properties:

    { "hp", {0x281, Actor::FieldType::INTEGER}},
    { "bandaged", {0x285, Actor::FieldType::INTEGER}},
    { "ap", { 0x289, Actor::FieldType::FLOAT}},
    { "radpoints", { 0x28d, Actor::FieldType::INTEGER}},
    { "radlevel", { 0x0291, Actor::FieldType::INTEGER}},
    { "poisonpoints", { 0x295, Actor::FieldType::INTEGER}},
    { "poisonlevel", {0x299, Actor::FieldType::INTEGER}},
    { "overdosepoints", {0x29d, Actor::FieldType::INTEGER}},
    { "goneuplevel", {0x02a1, Actor::FieldType::BOOLEAN}},
    { "stance", {0x1fe, Actor::FieldType::INTEGER}},
    { "posture", {0xf8a, Actor::FieldType::WCHAR_STRING}},
    { "injured", {0xfb2, Actor::FieldType::BOOLEAN}},
    { "injuredtorso",{ 0xfb3, Actor::FieldType::BOOLEAN } },
    { "injuredhead",{ 0xfb4, Actor::FieldType::BOOLEAN } },
    { "injuredeyes",{ 0xfb5, Actor::FieldType::BOOLEAN } },
    { "injuredrightarm",{ 0xfb6, Actor::FieldType::BOOLEAN } },
    { "injuredleftarm",{ 0xfb7, Actor::FieldType::BOOLEAN } },
    { "injuredgroin",{ 0xfb8, Actor::FieldType::BOOLEAN } },
    { "injuredleftleg",{ 0xfb9, Actor::FieldType::BOOLEAN } },
    { "injuredrightleg",{ 0xfba, Actor::FieldType::BOOLEAN } },
    { "unconscious",{ 0xfbb, Actor::FieldType::BOOLEAN } },
    { "unconscioustime",{ 0xfbc, Actor::FieldType::INTEGER } },
    { "reputation",{ 0xfed, Actor::FieldType::INTEGER } },
    { "isgeneral", {0x1053, Actor::FieldType::BOOLEAN} },
    { "isrecruitmaster",{ 0x1054, Actor::FieldType::BOOLEAN } },
    { "isquartermaster",{ 0x1055, Actor::FieldType::BOOLEAN } }
    { "playerindex", {0x163, Actor::FieldType::INTEGER}}

    I successfully tested changing current HP and AP. It's possible to heal/harm actors through code now. Setting HP to 0 will not play death animation though, so the actors remain in their previous idle animation. But you can loot them. Also no XP for you, obviously.
    Thanks to this, I was able to finish another two really cool perks. I wish something playable will ever come from this :D
     
  4. nadeauhugo

    nadeauhugo Author of FOT mod THE SUM Modder

    635
    Mar 9, 2014
    OMFG! You are my hero! Fantastic work here! :D This will be of great use to me too, highly appreciate that. This will also really come in handy to program some form of Doctor character in the future. You set a variable to true in Tactics scripts and then bang, your chars are healed, thanks to your new findings :D Thanks again...

    Hugo
     
  5. nadeauhugo

    nadeauhugo Author of FOT mod THE SUM Modder

    635
    Mar 9, 2014
    I noticed when having a character in let's say the prone position and adding it to the team, the position is permanent, meaning that each time you enter a new map, the said character will always revert to that preset position. This means that maybe using LUA code to set a new default position, like "Stand" will permit the use of prone or crouch characters to make them join the team.

    As for now I did not test this list yet, but I intend on using those fields to program a medic for a future version of my mod. I will post the code here. It will make use of the overhead texts, and probably will cure all your squad when a variable is set to true, so right after a conversation with the said medic.
     
  6. weirwood

    weirwood First time out of the vault

    28
    Mar 29, 2008
    This is a rather basic question, but are there any other bugfix mods that should be installed in addition FTSE if all you want is a smooth, (mostly) bug free vanilla game? FT Improver, maybe?
     
  7. Melindil

    Melindil It Wandered In From the Wastes

    105
    Apr 15, 2018
    So, after way too long of a hiatus, FTSE 0.55a is now available. There's a fix for the FTSE issue with the GetEffectMaximum function in Consumable not working, plus a couple of new game engine bug fixes (notably, radiation and poison resistance now work like they should). There are also a couple of new functions to change entity colors, and allow arbitrary time passage to be triggered from scripts.

    The biggest new feature is a new capability to call, and add hooks for, the Entity class virtual table (vtable) functions. The game has 525 (!!!) functions available in the vtable, and most of them can at least be called and/or hooked by the Lua scripts. This is very much still a work in progress, as only a few of the functions have been examined and fully documented. (I could always use help here for anyone who wants to experiment with them, though! Just be sure to use a backup save file or mod directory, as I can't guarantee stability here yet.)

    Documentation has been updated to include the new capabilities, plus a new example showing off what can be done with the new vtable hooks (specifically, an item that can disable nearby traps when a character with high enough Science skill uses the skill on that item). Over time, the documentation will be extended, and highly useful vtable functions may be converted to regular functions (this was already done for SpriteRefresh, in order to allow color change to work properly).
     
    • [Rad] [Rad] x 1
  8. nadeauhugo

    nadeauhugo Author of FOT mod THE SUM Modder

    635
    Mar 9, 2014
    Oh my, where to start. I am beyond grateful for this existing :D You make my day, especially sinced I didn't expect this to appear. And the timing is impeccable, I will be launching the bêta in some weeks, and my birthday's tomorrow even :P. Thanks a bunch, as always, I'll dive into this with a renewed pleasure.

    I'm taking the opportunity to tell you that a strange crash bug happened to me couple months ago. I was trying to narrow down the cause of it when I realised FTSE might be the culprit, as I think I pinned it on the use of GetPlayer() inside one of the equip hooks: OnEquip(), OnUnequip(), OnCheckEquip(), OnCheckUnequip(), something like that. I don't think any other hook is affected by this. There might be a reason why GetPlayer() can't be used there, but it is curious that it made my game crash.

    Thanks again!
     
  9. Melindil

    Melindil It Wandered In From the Wastes

    105
    Apr 15, 2018
    That's a strange one, and definitely shouldn't happen. Would you happen to have the Lua code from one of the functions that seemed to cause the crash? If not, I'll try to put together a similar case and see if I get the crash.
     
  10. nadeauhugo

    nadeauhugo Author of FOT mod THE SUM Modder

    635
    Mar 9, 2014
    Unfortunately, all I have now is a comment note to tell myself to not add the .GetPlayer function. But it looked like this at the very beginning of my hooks :

    Code:
    local p = world:GetPlayer()
     
  11. nadeauhugo

    nadeauhugo Author of FOT mod THE SUM Modder

    635
    Mar 9, 2014
    I am now attempting to change the color of the player entity, but strangely, all I get is the color being reset to default (white) when I remove or add the character's armor. Do you have an example of SetColor that I could base my code on? I don't know yet what the 2 last numbers do exactly. I presume this is color saturation and transparency? I tried this :
    Code:
    local col = {0.6, 0.0, 0.8, 1.0, 0.8}
    e:DisplayMessage("<Cy>Armor changed<C->")
    e:SetColor(ENTITY_COLOR_TEAM, col)
    e:RefreshSprite()
     
  12. Melindil

    Melindil It Wandered In From the Wastes

    105
    Apr 15, 2018
    It looks like the color table is missing the field labels. Try:

    Code:
    local col = {r=0.6, g=0.0, b=0.8, s=1.0, a=0.8}
    I should probably add a check for colors, and also Vector3 for positions, to accept an array-style table, since it's a bit clumsy requiring the labels every time.
     
  13. nadeauhugo

    nadeauhugo Author of FOT mod THE SUM Modder

    635
    Mar 9, 2014
    Oh yess! Worked thanks! :D I personally don't mind adding the labels everytime, it reminds me of what they do. But I guess some heavy coder would want maybe to use variables with additions or other stuff to assign them and it would be easier for them with just the numbers?
     
  14. Melindil

    Melindil It Wandered In From the Wastes

    105
    Apr 15, 2018
    Re: the crash with OnEquip / OnCheckEquip and GetPlayer - do you remember if the crash occurs right at the start of a new campaign? My test case ran into one there - turns out that at the point the new character is created and equipped (which does trigger the hooks), the list of player/controller objects in the global World class isn't set up. So when we try to get the player's squad (which should always exist), we get an empty list, and when we try to get the first entity in the squad (which should always be the player), we crash.

    I've added a fix, to check if the player list is improperly set up, prior to allowing the equip hooks to fire. This seems to correct the crash.

    If the crash occurred later - I also found a couple of cases where the Lua stack may have had a stack leak, which could also cause a crash over a long enough period of time. I've fixed this as well.
     
  15. nadeauhugo

    nadeauhugo Author of FOT mod THE SUM Modder

    635
    Mar 9, 2014
    Oh that's promising that you were able to find those. My memory is unclear if the game was crashing from start or just when equiping the items I needed for my tests, but as I was assigning a variable from the very start of the hook, it would make sense that the crash had occured to me right at the start of the game, after character selection. I'll keep in mind to update again if I find some use for the getPlayer in those hooks later on.

    On another note, I thinkered with the character sprite's colors today and I sucessfully implemented a color change for all my armors. Pretty fun I must say to see that unfold :D

     
    Last edited: Jun 28, 2024
  16. Lars1205

    Lars1205 First time out of the vault

    3
    Jun 28, 2024
    Hi, thank you very much for your awesome work Melindil.
    I'm not a modder and I thank you guys for your hard work!
    While I was trying to find a fix for the NightPerson trait I saw a post of nadeauhugo in another thread about a script that was working for him. Too bad for me the game was still adding the doNightPerson flag every now and then upping the stats with +1/+1 during day and +3/+3 during the night.
    I tried to see if I could solve my problem and I found that by just adding

    e:SetAttribute("doNightPerson", ACTOR_TABLE_PERM, 0)

    to nadeauhugo script I was able to solve it for me:

    Code:
    function OnLongTick(e)
      if e:GetAttribute("nightPerson", ACTOR_TABLE_CURRENT) == 1 then
        -- use perk entry in temporary table to save info about
        curr = e:GetAttribute("fortuneFinder", ACTOR_TABLE_TEMPORARY)
        stdNP = e:GetAttribute("doNightPerson", ACTOR_TABLE_CURRENT)
        e:SetAttribute("doNightPerson", ACTOR_TABLE_PERM, 0)
        now = world:GetTime()
        if now["hour"] < 7 or now["hour"] > 18 then
          -- night time
          if curr == 0 then
            bonus = { perception=2, intelligence=2 }
            e:ApplyBonus(bonus,false)
            e:SetAttribute("fortuneFinder", ACTOR_TABLE_TEMPORARY, 1)
          end
        else
          -- day time
          if curr == 1 then
            bonus = { perception=2, intelligence=2 }
            e:RemoveBonus(bonus,false)
            e:SetAttribute("fortuneFinder", ACTOR_TABLE_TEMPORARY, 0)
          end
        end
      end
    end
    
    I don't know if it will really solve the problem or if there are some better ways to solve it, but for me it's working for now. I tried with ACTOR_TABLE_CURRENT and ACTOR_TABLE_TEMPORARY, but for some reason it worked only with ACTOR_TABLE_PERM.

    I don't know if it will fix the problem for everyone, but for me it did and I hope it will help in solving the problem.
     
  17. Melindil

    Melindil It Wandered In From the Wastes

    105
    Apr 15, 2018
    Thank you for reminding me of this! During the vtable work, I found the code that recalculates a character's stats, including these flags. I just went back to read through this code in detail, and I think I finally understand why this is broken (as well as the perks that use similar "do*" tags; e.g. Die Hard and Adrenaline Rush). This is a fun one.

    Tech details below, but first: the workaround you posted solves about 99% of the problem. There's still a small gap where the doNightPerson flag can be turned on by the existing code, stats updated, then the flag turned back off in OnLongTick - the bonus from doNightPerson will stay in effect until stats are next recalculated. Anything that might change a stat, from damage to healing to changing equipment, should trigger the recalculation, at which point the stat will be back where it should be.

    ---

    For Night Person, Die Hard, and Adrenaline Rush (all three behave similarly), there are three sets of flags/parameters that are tracked:

    1) The base value of the trait/perk. Set to 1 if you have the trait/perk, 0 otherwise.

    2) The "do" flags - doNightPerson, doDieHard, doAdrenalineRush. These are set when the corresponding bonus is turned on, and (supposed to be) cleared when the bonus is to be turned off.

    3) A set of "last checked" flags for each of these, holding the values they had last time they were checked.

    So, for Night Person, whenever stat recalculation takes place, the state of both the NightPerson trait (#1 above) and the doNightPerson flag (#2 above) are checked. If #1 is not set, then no change is applied. If #1 is set but #2 is clear, then PE and IN are decremented by 1 from their base value. If both #1 and #2 are set, then PE and IN are incremented by 1 from their base value. So this part works as expected. Die Hard + Adrenaline Rush are similar, but there is no "subtract" in those cases - essentially, if the doDieHard or doAdrenalineRush flags are set, the corresponding bonus is added.

    Now to #3 - the "last checked" flags. There's a function that checks whether the do* flags should be set, and it is called in 3 cases - when a character takes damage, is healed, or on passage of time (looks like 3 game-time hours for this last one to trigger). In this function, the "proper" value of the flags is determined - Die Hard and Adrenaline Rush check percentage of HP, and Night Person checks the current game time. Once the proper flag settings are determined, the game will check the result against the "last checked" value, and if they differ, then the flags are updated and stats recalculated, and the new value of the flags is saved in "last checked".

    Ideally, this should all be fine. But, there's a problem: while the trait/perk base value and the "do" flags are persisted in the save file, the "last checked" flags are not. So, on a save/load cycle, these flags revert to 0, or "not set". This normally wouldn't be a huge problem, as the next time one of the three above triggers fires after loading the game, then the flags should be set back to where they belong. The last factor, and the one that really breaks things, is that leaving a mission map and entering another counts as a save/load cycle.

    To see why this is a problem, consider Night Person first. Say the player starts a mission at night. When the mission level is loaded, the first thing that happens is "time passes"; that is, any time spent on the world map is added into the game time, and characters heal as expected. That process is one of the three triggers, and so the game will check the time - it is night, so it sets doNightPerson flags for anyone with the trait. Stats are updated, and the characters get the proper +1/+1. The last checked flag is also set indicating this. Now, assume the mission is completed while it is still night, and the player leaves the map. The game state is saved prior to entering the world map, and reloaded when they reach their new destination (e.g. the bunker), so the last checked flags are cleared. Assume at arrival to the bunker, the game time is now during the day. When the level loads, and "time passes", the game checks the proper state of the flag (false) to the saved state (also false because of the save/load cycle). Since they are the same, nothing is updated, and the doNightPerson flag is left on when it shouldn't be. Once this happens, the condition will persist until the following sequence occurs:

    1) Have one of the three trigger conditions occur at night, so that the last checked flag is set to true.

    2) During the mission (not at the world map, and not with any save/load in between), allow time to pass from night to day.

    3) Once the time has moved to daytime, have one of the three trigger conditions occur again. This will result in the proper flag state and last checked flag state to be different, triggering the doNightPerson flag to finally be turned off, and stats recalculated to reflect it.

    Die Hard and Adrenaline Rush are similar, but there's an additional catch. From the trigger events, it should be the case that healing via stimpack/first aid causes the flags to be checked again, and the "do" flags being cleared if the healing takes the character above the perk threshold. The problem is that in these cases the HP ratio is checked before the heal occurs. So if the character is below 10%/20%, and then healed above that amount in one go, the "do" flag will still be set. It's only if there's an additional triggering event after the character is back above 10%/20% that the flag will be cleared. And as before, if there's an intervening save/load cycle, then the last checked flags will be cleared, and the "do" flags will be left on. (Healing over time suffers the same problem as Night Person - the typical intervening save/load from the world map will clear the last checked flag anyway, which also results in the do flags being left on).

    The fix for all three of these will be to find a way to persist the last checked flags into the save file. I'm currently researching this area in order to support storage of custom parameters in entities (so we don't have to keep using the temporary perk area as that storage). Once I get that figured out, I can maybe come up with a hex patch that will ensure these flags get saved. At that point, all three perks should mostly work - I'd still need to do something about Die Hard / Adrenaline Rush checking pre-heal HP, so maybe I'd also need to re-call the check function when the HP is finally updated.
     
  18. Lars1205

    Lars1205 First time out of the vault

    3
    Jun 28, 2024
    That was very informative, no wonder I wasn’t able to find a fix without using the temporary perk.
    If I didn’t find nadeauhugo code, I would stopped trying since I wasn’t able to reset the doNightPerson in any other way.
    That “last checked” sure is creating some big problems to solve this issue.
    Thank you very much again for all the work you’re doing!
     
    • [Rad] [Rad] x 1
  19. nadeauhugo

    nadeauhugo Author of FOT mod THE SUM Modder

    635
    Mar 9, 2014
    Another example of a pretty thing that can be accomplished using FTSE :
     
    • [Rad] [Rad] x 1
  20. Lars1205

    Lars1205 First time out of the vault

    3
    Jun 28, 2024
    This game had some good ideas, but it was not well implemented. This scripting engine helps a lot in resolving the game issues.
    Thank you very much for all the effort you put in this Melindil!
     
    • [Rad] [Rad] x 1