Excruciating FOCS doubts

Creation, discussion, and balancing of game content such as techs, buildings, ship parts.

Moderators: Oberlus, Committer

Post Reply
Message
Author
User avatar
Oberlus
Cosmic Dragon
Posts: 5714
Joined: Mon Apr 10, 2017 4:25 pm

Excruciating FOCS doubts

#1 Post by Oberlus »

I'm trying to understand FOCS, to generate my own code, rather than being subject to mimic existing pieces of code without knowing why a previous attempt didn't work or how and why this other piece of code does work.
I've searched for a general thread of FOCS doubts and couldn't find one. I've found specific doubts on dedicated (short) threads, but here I'm not trying to solve a specific doubt, I'm just browsing FOCS files at random, watching their code, and trying to understand each pieace of code. So I thought a general FOCS doubts threads makes sense.

So I'll be posting doubts here. However, if you think it would be better to have a thread for each doubt, I'll split the thread as needed.

User avatar
Oberlus
Cosmic Dragon
Posts: 5714
Joined: Mon Apr 10, 2017 4:25 pm

Re: Excruciating FOCS doubts

#2 Post by Oberlus »

SP_KRILL_SPAWNER.focs.txt is a ship part (category General), mountable in internal slots.

I don't understant its first EffectsGroup:

Code: Select all

Part
    name = "SP_KRILL_SPAWNER"
    description = "SP_KRILL_SPAWNER_DESC"
    class = General
    mountableSlotTypes = Internal
    buildcost = 100 * [[FLEET_UPKEEP_MULTIPLICATOR]] * [[SHIP_PART_COST_MULTIPLIER]]
    buildtime = 9
    tags = [ "PEDIA_PC_GENERAL" ]
    location = All
    effectsgroups = [
        EffectsGroup
            scope = And [
                Planet
                Planet type = Asteroids
                InSystem id = Source.SystemID
                Unowned
            ]
            activation = And [
                InSystem
                Not ContainedBy And [
                    Object id = Source.SystemID
                    System
                    Contains And [
                        Ship
                        InSystem id = Source.SystemID
                        Or [
                            DesignHasHull name = "SH_KRILL_1_BODY"
                            DesignHasHull name = "SH_KRILL_2_BODY"
                            DesignHasHull name = "SH_KRILL_3_BODY"
                            DesignHasHull name = "SH_KRILL_4_BODY"
                        ]
                    ]
                ]
            ]
            stackinggroup = "SP_KRILL_SPAWN_STACK"
            effects = CreateShip designname = "SM_KRILL_1"

        EffectsGroup
            scope = Source
            activation = And [
                            Not DesignHasPartClass Low=1 High=999 Class=Stealth
                            Not Armed
                        ]
            effects = SetStealth value = Value + 40
    ]
First of all, Source is the ship part SP_KRILL_SPAWNER itself, right?

Scope:
  • Any planet
  • of type Asteroids
  • that is in the system of the Source
  • and that is unowned
So this applies to asteroid belts in the same system than the ship mounting the SP_KRILL_SPAWNER, right?

Activation:
  • InSystem? That is not travelling a starlane? But isn't now the Target an unowned asteroid belt that should not be travelling? Or maybe this is asking for the Target to be in the same system than Source? Or just this is asking for a System, in general?
  • That is NOT contained by
    • an object that is the System in which the SP_KRILL_SPAWNER was?
    • which is itself a system (shouldn't this be implicit from previous line?)
    • and that contains
      • a ship
      • that is in the system of the SP_KRILL_SPAWNER?
      • and that has a krill body hull
So it creates a monster SM_KRILL_1 in... any place that is not the system of the SP_KRILL_SPAWNER as long as it does not have a SM_KRILL_X in there? Obviously that's not true, so I certainly can't figure out what the heck means all those lines in the conditions of the scope and activation.

Please, a charitable bright mind that helps me out here?

"InSystem" is a keyword not referenced in https://www.freeorion.org/index.php/FOC ... ng_Details nor in https://www.freeorion.org/index.php/FOC ... g_Tutorial
One of the problems with FOCS is that you can't relly on the documentation. You certainly can't go to StackOverflow to ask about it or browse the latest version of the specification.

User avatar
alleryn
Space Dragon
Posts: 259
Joined: Sun Nov 19, 2017 6:32 pm

Re: Excruciating FOCS doubts

#3 Post by alleryn »

First, I'll try to help with the general lexical process i use when i try to decipher FOCS. I'll use as an example InSystem:
  1. Go to the FreeOrion repository. https://github.com/freeorion/freeorion/tree/master
  2. In the search box at the upper right look for the term you are trying to decipher: InSystem
  3. Look through the search result for a file name with "parser" in the title. In this case, on page 4 there is a result in ConditionParser2.cpp
  4. Open up the link to that file, and do a web page search for InSystem. Here you can locate the code snippet:

    Code: Select all

            in_system
                =   (   omit_[tok.InSystem_]
                        >  -(label(tok.ID_)  > int_rules.expr)
                    ) [ _val = construct_movable_(new_<Condition::InSystem>(deconstruct_movable_(_1, _pass))) ]
                ;
    This shows that InSystem (after being tokenized into tok.InSystem_ -- if you want to check this part, it takes a bit more digging, but this tok.<focs>_ construction is common) gets converted into Condition::InSystem.

    Now we can go to Condition.cpp and look for Condition::InSystem to find:

    Code: Select all

    ///////////////////////////////////////////////////////////
    // InSystem                                              //
    ///////////////////////////////////////////////////////////
    InSystem::InSystem(std::unique_ptr<ValueRef::ValueRefBase<int>>&& system_id) :
        ConditionBase(),
        m_system_id(std::move(system_id))
    {}
    
    bool InSystem::operator==(const ConditionBase& rhs) const {
        if (this == &rhs)
            return true;
        if (typeid(*this) != typeid(rhs))
            return false;
    
        const InSystem& rhs_ = static_cast<const InSystem&>(rhs);
    
        CHECK_COND_VREF_MEMBER(m_system_id)
    
        return true;
    }
    
    namespace {
        struct InSystemSimpleMatch {
            InSystemSimpleMatch(int system_id) :
                m_system_id(system_id)
            {}
    
            bool operator()(std::shared_ptr<const UniverseObject> candidate) const {
                if (!candidate)
                    return false;
                if (m_system_id == INVALID_OBJECT_ID)
                    return candidate->SystemID() != INVALID_OBJECT_ID;  // match objects in any system
                else
                    return candidate->SystemID() == m_system_id;        // match objects in specified system
            }
    
            int m_system_id;
        };
    }
    
    void InSystem::Eval(const ScriptingContext& parent_context,
                        ObjectSet& matches, ObjectSet& non_matches,
                        SearchDomain search_domain/* = NON_MATCHES*/) const
    {
        bool simple_eval_safe = !m_system_id || m_system_id->ConstantExpr() ||
                                (m_system_id->LocalCandidateInvariant() &&
                                (parent_context.condition_root_candidate || RootCandidateInvariant()));
        if (simple_eval_safe) {
            // evaluate empire id once, and use to check all candidate objects
            std::shared_ptr<const UniverseObject> no_object;
            int system_id = (m_system_id ? m_system_id->Eval(ScriptingContext(parent_context, no_object)) : INVALID_OBJECT_ID);
            EvalImpl(matches, non_matches, search_domain, InSystemSimpleMatch(system_id));
        } else {
            // re-evaluate empire id for each candidate object
            ConditionBase::Eval(parent_context, matches, non_matches, search_domain);
        }
    }
    
    bool InSystem::RootCandidateInvariant() const
    { return !m_system_id || m_system_id->RootCandidateInvariant(); }
    
    bool InSystem::TargetInvariant() const
    { return !m_system_id || m_system_id->TargetInvariant(); }
    
    bool InSystem::SourceInvariant() const
    { return !m_system_id || m_system_id->SourceInvariant(); }
    
    std::string InSystem::Description(bool negated/* = false*/) const {
        std::string system_str;
        int system_id = INVALID_OBJECT_ID;
        if (m_system_id && m_system_id->ConstantExpr())
            system_id = m_system_id->Eval();
        if (auto system = GetSystem(system_id))
            system_str = system->Name();
        else if (m_system_id)
            system_str = m_system_id->Description();
    
        std::string description_str;
        if (!system_str.empty())
            description_str = (!negated)
                ? UserString("DESC_IN_SYSTEM")
                : UserString("DESC_IN_SYSTEM_NOT");
        else
            description_str = (!negated)
                ? UserString("DESC_IN_SYSTEM_SIMPLE")
                : UserString("DESC_IN_SYSTEM_SIMPLE_NOT");
    
        return str(FlexibleFormat(description_str) % system_str);
    }
    
    std::string InSystem::Dump(unsigned short ntabs) const {
        std::string retval = DumpIndent(ntabs) + "InSystem";
        if (m_system_id)
            retval += " id = " + m_system_id->Dump(ntabs);
        retval += "\n";
        return retval;
    }
    
    void InSystem::GetDefaultInitialCandidateObjects(const ScriptingContext& parent_context,
                                                     ObjectSet& condition_non_targets) const
    {
        if (!m_system_id) {
            // can match objects in any system, or any system
            AddAllObjectsSet(condition_non_targets);
            return;
        }
    
        bool simple_eval_safe = m_system_id->ConstantExpr() ||
                                (m_system_id->LocalCandidateInvariant() &&
                                (parent_context.condition_root_candidate || RootCandidateInvariant()));
    
        if (!simple_eval_safe) {
            // almost anything can be in a system, and can also match the system itself
            AddAllObjectsSet(condition_non_targets);
            return;
        }
    
        // simple case of a single specified system id; can add just objects in that system
        int system_id = m_system_id->Eval(parent_context);
        auto system = GetSystem(system_id);
        if (!system)
            return;
    
        const ObjectMap& obj_map = Objects();
        const std::set<int>& system_object_ids = system->ObjectIDs();
        auto sys_objs = obj_map.FindObjects(system_object_ids);
    
        // insert all objects that have the specified system id
        condition_non_targets.reserve(sys_objs.size() + 1);
        std::copy(sys_objs.begin(), sys_objs.end(), std::back_inserter(condition_non_targets));
        // also insert system itself
        condition_non_targets.push_back(system);
    }
    
    bool InSystem::Match(const ScriptingContext& local_context) const {
        auto candidate = local_context.condition_local_candidate;
        if (!candidate) {
            ErrorLogger() << "InSystem::Match passed no candidate object";
            return false;
        }
        int system_id = (m_system_id ? m_system_id->Eval(local_context) : INVALID_OBJECT_ID);
        return InSystemSimpleMatch(system_id)(candidate);
    }
    
    void InSystem::SetTopLevelContent(const std::string& content_name) {
        if (m_system_id)
            m_system_id->SetTopLevelContent(content_name);
    }
    
    unsigned int InSystem::GetCheckSum() const {
        unsigned int retval{0};
    
        CheckSums::CheckSumCombine(retval, "Condition::InSystem");
        CheckSums::CheckSumCombine(retval, m_system_id);
    
        TraceLogger() << "GetCheckSum(InSystem): retval: " << retval;
        return retval;
    }
    
    Now we've located the actual C++ code that the FOCS "InSystem" is translated into, so we can start trying to figure out what's "really" going on. Generally this is the point at which i start to get really confused, but this is more or less the process that i use, and sometimes it's helpful.

    In this case, IF i'm reading it correctly (big if), InSystem returns a boolean equal to whether the object has an m_system_id (i.e. whether the object is located in a system), and InSystem id returns the m_system_id of the system the object is in.
Now i'll try to answer your questions (to the best of my ability):
Oberlus wrote: Thu Nov 21, 2019 11:21 am First of all, Source is the ship part SP_KRILL_SPAWNER itself, right?
Yes.
Oberlus wrote: Thu Nov 21, 2019 11:21 am So this applies to asteroid belts in the same system than the ship mounting the SP_KRILL_SPAWNER, right?
Yes, to unowned asteroid belts in the same system as the ship with the krill spawner.
Oberlus wrote: Thu Nov 21, 2019 11:21 am InSystem? That is not travelling a starlane? But isn't now the Target an unowned asteroid belt that should not be travelling? Or maybe this is asking for the Target to be in the same system than Source? Or just this is asking for a System, in general?
InSystem should just be checking whether the object is in a system. It seems redundant here, as scope has already limited the options to planets in the same system as the krill spawner part, so maybe i'm missing something, or maybe this is just a redundant check to avoid potential errors.
Oberlus wrote: Thu Nov 21, 2019 11:21 am an object that is the System in which the SP_KRILL_SPAWNER was?
Oberlus wrote: Thu Nov 21, 2019 11:21 am which is itself a system (shouldn't this be implicit from previous line?)
Oberlus wrote: Thu Nov 21, 2019 11:21 am that is in the system of the SP_KRILL_SPAWNER?
As far as the 'shouldn't this be implicit' question, in my limited experience, the FOCS code is kludged together by a variety of contributors, so there isn't really much of a coding standard. It's mostly "if it works, it's fine", so there is a fair bit of redundancy. I would tend to agree here, that if we've already checked that the Object''s ID is the same as the Source's SystemID, it shouldn't be necessary to then check whether the Object is a System. Only Systems should have ID's that are SystemID's.

In terms of understanding this section of FOCS code, let's address the next question:
Oberlus wrote: Thu Nov 21, 2019 11:21 am So it creates a monster SM_KRILL_1 in... any place that is not the system of the SP_KRILL_SPAWNER as long as it does not have a SM_KRILL_X in there?
What you need to realize is that the set of possible objects where activation is to be considered, has already been limited by the Scope above. We are only checking unowned asteroid belts in the same system as the krill spawner to see if they satisfy the activation condition. Once you've gotten that then you are on the right track, i think.

It creates a monster SM_KRIL_1 in any unowned asteroid belt in the same system as the krill spawner which also satisfies the activation condition. Let me try to break down the activation conditon:

I'll skip the InSystem part, as that seems redundant. That leaves the NotContainedBy And. The And is an intersection, so if we aren't contained by the intersection, that's the same as being not contained by at least one of. In other words NOT (A AND B) = (NOT A) OR (NOT B). If this part is confusing, a Venn diagram can help. Ultimately, if i'm correct, the first two items in this AND (the outer AND) are equivalent in this context.

The point of the statement is to check that the system containing the asteroid belt does not already have a krill ship. If it does then we don't want to generate a new ship. I think it would be fine if the code read:

Code: Select all

activation = Not ContainedBy And [
                    Object id = Source.SystemID //alternatively this could just say System
                    Contains And [
                        Ship
                        //I think we can also eliminate the check to see if the ship is in the system.  Since we are already looking at something contained by the system...
                        Or [
                            DesignHasHull name = "SH_KRILL_1_BODY"
                            DesignHasHull name = "SH_KRILL_2_BODY"
                            DesignHasHull name = "SH_KRILL_3_BODY"
                            DesignHasHull name = "SH_KRILL_4_BODY"
                        ]
                    ]
It would activate on any unowned asteroid belt in the system of the krill spawner part as long as that asteroid belt wasn't contained by a system (i.e. the system that the asteroid belts are in) which contains a ship with a krill body.
________________________________
Well, hopefully i didn't make things worse. I don't really understand this stuff very well, but i tried. Good luck!

User avatar
Oberlus
Cosmic Dragon
Posts: 5714
Joined: Mon Apr 10, 2017 4:25 pm

Re: Excruciating FOCS doubts

#4 Post by Oberlus »

Thank you very much, alleryn. I'm copying your system from now on.
I think I've diggested properly all this information. I think now I there are way less chances for me to post another doubt.

User avatar
Geoff the Medio
Programming, Design, Admin
Posts: 13587
Joined: Wed Oct 08, 2003 1:33 am
Location: Munich

Re: Excruciating FOCS doubts

#5 Post by Geoff the Medio »

alleryn wrote: Thu Nov 21, 2019 4:10 pm
Oberlus wrote: Thu Nov 21, 2019 11:21 am First of all, Source is the ship part SP_KRILL_SPAWNER itself, right?
Yes.
Source will be the ship, not the ship part.
Oberlus wrote: Thu Nov 21, 2019 11:21 am InSystem? That is not travelling a starlane? But isn't now the Target an unowned asteroid belt that should not be travelling? Or maybe this is asking for the Target to be in the same system than Source? Or just this is asking for a System, in general?
InSystem should just be checking whether the object is in a system. It seems redundant here, as scope has already limited the options to planets in the same system as the krill spawner part, so maybe i'm missing something, or maybe this is just a redundant check to avoid potential errors.
It is scripted like:

Code: Select all

InSystem id = Source.SystemID
The InSystem has a parameter here, id, which specifies which system the object should be in. So this condition will only match objects in the same system as the Source object's system.
What you need to realize is that the set of possible objects where activation is to be considered, has already been limited by the Scope above.
Activation conditions are evaluated on only the source object, and this happens before the scope condition is evaluated.

Activation conditions test the source object (only) to decide whether an effectsgroup should execute. Scope conditions determine what objects to execute on. There may be no overlap between the objects matched by these conditions and having duplicate conditions inside them is not redundant.

I don't know if there is an old general scripting help thread, but the Scripting and Balancing forum would be a good place to post questions: viewforum.php?f=15

User avatar
Oberlus
Cosmic Dragon
Posts: 5714
Joined: Mon Apr 10, 2017 4:25 pm

Re: Excruciating FOCS doubts

#6 Post by Oberlus »

All playable hulls (all those not in the monster folder) except base hull call the macro "SCAVANGE_FUEL_UNOWNED" (ahem, Scavenge).

Code: Select all

SCAVANGE_FUEL_UNOWNED
'''EffectsGroup
            scope = Source
            activation = And [
                Stationary
                Unowned
                Random probability = 0.6
            ]
            effects = SetFuel value = Value + 1
'''
From the parser I assume Unowned means not owned by any empire (AI or human):

Code: Select all

        owned_by_3
            =   tok.Unowned_
            [ _val = construct_movable_(new_<Condition::EmpireAffiliation>( AFFIL_NONE )) ]
            ;
So that SCAVANGE_FUEL_UNOWNED macro refills fuel of unowned non-monster hulls, right?
I've never found an unowned non-monster hull, except when I had bugs or problems with the save files.
So... What's the point of that macro?

Edit: Maybe, is it necessary for empires to concede?
Edit2: But what would be the point if empires that concede no longer move their ships?

User avatar
Oberlus
Cosmic Dragon
Posts: 5714
Joined: Mon Apr 10, 2017 4:25 pm

Re: Excruciating FOCS doubts

#7 Post by Oberlus »

Another doubt:

Some organic hulls have this effect (either from its own hull definition or calling to LIVING_HULL_EFFECTS_GROUPS):

Code: Select all

    scope = Source
    activation = Source
    effects = [
        SetStructure value = Value + 2
        SetDetection value = Value + 50
    ]
That "SetStructure value = Value + 2" is the living hull regeneration effect, right?

The Bioadaptive hull has instead the following:

Code: Select all

            scope = Source
            activation = Turn low = Source.CreationTurn + 1
            effects = [
                SetStructure value =  Value + Value
            ]
Is it necessary the "activation = Turn" in this case? Why? Why the other hulls doesn't specify the turn condition?

User avatar
Geoff the Medio
Programming, Design, Admin
Posts: 13587
Joined: Wed Oct 08, 2003 1:33 am
Location: Munich

Re: Excruciating FOCS doubts

#8 Post by Geoff the Medio »

Oberlus wrote: Sun Mar 29, 2020 6:49 pmSo that SCAVANGE_FUEL_UNOWNED macro refills fuel of unowned non-monster hulls, right?
Looks like it.
I've never found an unowned non-monster hull, except when I had bugs or problems with the save files.
So... What's the point of that macro?
Probably no point at present, but if any content removed ownership from some ships, it would start refueling them. Could be useful for making some pirates or other non-monster non-player ships do stuff.
Oberlus wrote: Sun Mar 29, 2020 8:19 pmIs it necessary the "activation = Turn" in this case?
Not sure, but it might avoid creating sitreps on the turn a ship is created. There are other similar activation conditions on effects of content like BLD_GATEWAY_VOID for its stealth effect. For the nanorobotic hull, the current version of the effect doubles the structure each turn, but the original version was dependent on the ship's max structure, which might have resulted in the effect setting the current structure to zero if applied on the first turn before the max structure effects had fired. Having the minimum turn when the effect fires be one after the creation turn would have assured that wouldn't happen. If that was it, then the condition probably isn't necessary after switching to doubling the structure and relying on clamping afterwards (outside the effect system) to limit structure by max structure.

Ophiuchus
Programmer
Posts: 3433
Joined: Tue Sep 30, 2014 10:01 am
Location: Wall IV

Re: Excruciating FOCS doubts

#9 Post by Ophiuchus »

Geoff the Medio wrote: Sun Mar 29, 2020 8:49 pm
Oberlus wrote: Sun Mar 29, 2020 6:49 pmI've never found an unowned non-monster hull, except when I had bugs or problems with the save files.
So... What's the point of that macro?
Probably no point at present, but if any content removed ownership from some ships, it would start refueling them. Could be useful for making some pirates or other non-monster non-player ships do stuff.
Doesnt the psionic snowflake steal ships?
Any code or patches in anything posted here is released under the CC and GPL licences in use for the FO project.

Look, ma... four combat bouts!

User avatar
Oberlus
Cosmic Dragon
Posts: 5714
Joined: Mon Apr 10, 2017 4:25 pm

Re: Excruciating FOCS doubts

#10 Post by Oberlus »

Ophiuchus wrote: Mon Mar 30, 2020 10:43 am
Geoff the Medio wrote: Sun Mar 29, 2020 8:49 pm Probably no point at present, but if any content removed ownership from some ships, it would start refueling them. Could be useful for making some pirates or other non-monster non-player ships do stuff.
Doesnt the psionic snowflake steal ships?
Yes!
The hull of the Psionic Snowflake has a 0.2 chance of changing ownership of organic ships within 1 jump distance when there are 4+ organic ships, and 0.8 chance when there are 12+ organic ships if those doesn't have telephatic tag.

User avatar
LienRag
Cosmic Dragon
Posts: 2146
Joined: Fri May 17, 2019 5:03 pm

Re: Excruciating FOCS doubts

#11 Post by LienRag »

I'm trying to experiment with understand FOCS¹ and I've come with some code for an internal part that I think should work¹ for an internal part that provide stackable stealth in systems with Gas Giants (fluff : Variable Pressure Coating allows ship to enter the cloudy atmosphere of gas giants) as I firmly believe that situational stealth is much more strategic and fun than general stealth.

Code: Select all

Part
    name = "ST_COATING_GG"
    description = "ST_COATING_GG_DESC"
    exclusions = [[ST_COATING_GG]]
    mountableSlotTypes = Internal
    buildcost = 2 * [[FLEET_UPKEEP_MULTIPLICATOR]] * [[SHIP_PART_COST_MULTIPLIER]]
    buildtime = 5
    tags = [ "PEDIA_PC_STEALTH" ]
    location = OwnedBy empire = Source.Owner
    effectsgroups = [
        EffectsGroup
            scope = Source
            activation = ContainedBy And [
                System 
                Contains Planet type = Gas_Giant
            ]
            accountinglabel = "GAS_GIANT_FIELD_STEALTH"
            effects = SetStealth value = Value + 20
            stackinggroup = "STEALTH_GG_STACK"

    ]
    icon = "icons/ship_parts/cloak-4.png"

#include "stealth.macros"

#include "/scripting/common/upkeep.macros"
I created the corresponding entries in .../stringtables/en.txt

Code: Select all

ST_COATING_GG
Variable Pressure NanoCoating

ST_COATING_GG_DESC
Allows ships to hide in gas giants
but I don't understand if I'm supposed to declare GAS_GIANT_FIELD_STEALTH and STEALTH_GG_STACK somewhere or if by writing them in this ST_COATING_GG.focs.txt I already declared them.

I did add

Code: Select all

Item type = ShipPart name = "ST_COATING_GG"
to the Robotic Military Control tech FOCS file² and if I understand correctly that's all I needed to do ?³


¹ I was unable to test anything since the FOCS files are in read-only on installation via Snap

² The unlocking tech may change, I just want it to be an early one. Also, I didn't want to add a specific tech at this stage. And since Organic Hulls are the ones with many internal slots that will make the most of this Gas Giant-proof coating (on a Robotic ship with only one internal slot, it doesn't make sense to use a stackable but GG-only stealth part instead of the generic stealth part), I didn't want to use Organic Hull as the unlocking tech.

³ Yes I know that I should make a custom icon for it, I just didn't bother since I'm quite bad at graphic design

Ophiuchus
Programmer
Posts: 3433
Joined: Tue Sep 30, 2014 10:01 am
Location: Wall IV

Re: Excruciating FOCS doubts

#12 Post by Ophiuchus »

LienRag wrote: Thu Jun 11, 2020 10:49 pm but I don't understand if I'm supposed to declare GAS_GIANT_FIELD_STEALTH and STEALTH_GG_STACK somewhere or if by writing them in this ST_COATING_GG.focs.txt I already declared them.
I think the part definition and the item unlock you describe should work.

The stacking group is there to prevent multiple effect application e.g. in this case it prevents the ship from getting the stealth bonus if there are multiple gas giants in the system. Using a unique identifier like you did suffices.

You use GAS_GIANT_FIELD_STEALTH as accounting label key. In the mouseover which describes the effects leading to the meter, this gets used. But you need to add a corresponding stringtable entry so this gets shown as legible text and not as identifier (necessary key maybe ?? GAS_GIANT_FIELD_STEALTH_DESC ??).
Any code or patches in anything posted here is released under the CC and GPL licences in use for the FO project.

Look, ma... four combat bouts!

User avatar
LienRag
Cosmic Dragon
Posts: 2146
Joined: Fri May 17, 2019 5:03 pm

Re: Excruciating FOCS doubts

#13 Post by LienRag »

Well, it doesn't work.
At least the part doesn't appear in the Design panel...
What could I have missed ?
(and yes, I checked that by modifying an icon in the same directory, the modified icon was shown in the design panel; so the file is in the right folder. I made a similar check with the en.txt stringtable file.)

Ophiuchus
Programmer
Posts: 3433
Joined: Tue Sep 30, 2014 10:01 am
Location: Wall IV

Re: Excruciating FOCS doubts

#14 Post by Ophiuchus »

LienRag wrote: Tue Jun 30, 2020 12:56 am Well, it doesn't work.
At least the part doesn't appear in the Design panel...
What could I have missed ?
(and yes, I checked that by modifying an icon in the same directory, the modified icon was shown in the design panel; so the file is in the right folder. I made a similar check with the en.txt stringtable file.)
If you turn on unavailable parts in the design view - does it appear? If it does and it should be unlocked, your unlock effect is wrong. Any errors in the logfiles?

Else you need to post the code.
Any code or patches in anything posted here is released under the CC and GPL licences in use for the FO project.

Look, ma... four combat bouts!

User avatar
LienRag
Cosmic Dragon
Posts: 2146
Joined: Fri May 17, 2019 5:03 pm

Re: Excruciating FOCS doubts

#15 Post by LienRag »

Ophiuchus wrote: Tue Jun 30, 2020 5:44 am
If you turn on unavailable parts in the design view - does it appear? If it does and it should be unlocked, your unlock effect is wrong. Any errors in the logfiles?

Else you need to post the code.
No, it doesn't appear.

It's the exact code I posted above in this thread, I didn't change anything except the icon (and I tried the icon on an existing ship part, it didn't prevent it from appearing in the design panel, so there's no reason it could be the problem now).

I created the file using gedit, could that be the problem ? I think notepad++ isn't available on Linux.

I tried the BLD_TUTORIAL_ONE from the FOCS tutorial and this one works.

Post Reply