Fractal Softworks Forum

Please login or register.

Login with username, password and session length
Advanced search  

News:

Starsector 0.97a is out! (02/02/24); New blog post: Simulator Enhancements (03/13/24)

Author Topic: [HELP!] Ghost ship mysteries  (Read 2685 times)

Vayra

  • Admiral
  • *****
  • Posts: 627
  • jangala delenda est
    • View Profile
[HELP!] Ghost ship mysteries
« on: September 10, 2019, 08:14:09 PM »

Originally posted in misc. modding questions thread, updated following some adventures in rules.csv:

Spoiler
Is there a way to override the current dialogue and force it into a ship recovery from within that dialog script?

that probably didn't make sense, so here's what I'm trying to do specifically (trimmed slightly for length):

1) Manager EveryFrameScript is added by ModPlugin, and under certain conditions adds Event EveryFrameScript instance and serves it a system (this part works fine, and I'm pretty sure isn't breaking the rest of it)

2) Event EveryFrameScript spawns a derelict, and assigns it some custom special interaction dialogue. That (the spawning and assigning) looks like this:

Code: java
    protected void spawnWreck(StarSystemAPI system) {
       
        WeightedRandomPicker<String> factions = SalvageSpecialAssigner.getNearbyFactions(null, system.getLocation(), 15f, 5f, 5f);
        String faction = factions.pick();
        String variantId = DerelictShipEntityPlugin.pickLargeVariantId(faction, new Random());
        ShipCondition condition = Math.random() > 0.5f ? ShipCondition.PRISTINE : ShipCondition.GOOD;
        DerelictShipEntityPlugin.DerelictShipData params = new DerelictShipEntityPlugin.DerelictShipData(new ShipRecoverySpecial.PerShipData(variantId, condition), false);
       
        params.durationDays = duration;
        entity = (CustomCampaignEntityAPI) BaseThemeGenerator.addSalvageEntity(system, Entities.WRECK, Factions.NEUTRAL, params);
        entity.addTag(Tags.EXPIRES);
       
        DerelictShipEntityPlugin plugin = new DerelictShipEntityPlugin();
        plugin.init(entity, params);
       
        Misc.setSalvageSpecial(entity, (Object) new VayraGhostShipSpecial.VayraGhostShipSpecialData((CustomCampaignEntityAPI) entity));
                   
        entity.setName("Derelict Ship (" + Global.getSettings().getVariant(variantId).getFullDesignationWithHullName() + ")");
    }

3) Here's where it gets complicated. The special interaction dialogue script contains this specialData subclass:

Code
    public static class VayraGhostShipSpecialData implements SalvageSpecialInteraction.SalvageSpecialData {

        public Danger danger;
        public Type type;
        public CustomCampaignEntityAPI entity;
        public ShipVariantAPI variant;

        public VayraGhostShipSpecialData(CustomCampaignEntityAPI entity) {

            this.entity = entity;
            DerelictShipEntityPlugin plugin = (DerelictShipEntityPlugin) entity.getCustomPlugin();
            PerShipData shipData = plugin.getData().ship;
            this.variant = shipData.variant;
            if (this.variant == null) {
                this.variant = Global.getSettings().getVariant(shipData.variantId);
            }
            shipData.variantId = null;
            this.variant = this.variant.clone();

            this.danger = DANGERS.pick();
            log.info("picked danger level " + this.danger);

            if (this.danger.equals(Danger.NONE)) {
                this.type = null;
                log.info("not picking a danger type");
            } else {
                this.type = TYPES.pick();
                log.info("picked danger type " + this.type);
            }
        }

        @Override
        public SalvageSpecialInteraction.SalvageSpecialPlugin createSpecialPlugin() {
            return new VayraGhostShipSpecial();
        }
    }

And does all this:

Code
    @Override
    public void optionSelected(String optionText, Object optionData) {

        if (CONFIRM.equals(optionData)) {
            (a whole bunch of stuff/other options go here)

        } else if (NEVER_AGAIN.equals(optionData)) {
            setDone(true);
            setShowAgain(false);
            setEndWithContinue(false);
        }

        if (recoverable) {
            SalvageSpecialAssigner.ShipRecoverySpecialCreator creator = new SalvageSpecialAssigner.ShipRecoverySpecialCreator(null, 0, 0, false, null, null);
            ShipRecoverySpecialData specialData = (ShipRecoverySpecialData) creator.createSpecial(entity, null);
            Misc.setSalvageSpecial(entity, specialData);
            ShipRecoverySpecial special = new ShipRecoverySpecial();
            special.init(dialog, specialData);
            setDone(true);
            setShowAgain(false);
        }
        }

Problem is, even if recoverable == true, the ship isn't recoverable - like I can't attach a new salvageSpecial to it from inside another salvageSpecial, or while another one is attached or something. :P

Presumably I'm doing something real dumb here, but I can't quite figure out what the "right" way to do this is. Any help would be much appreciated.
[close]

@Vayra: this probably isn't the best thread for it because it's too involved, so might be better to have a separate thread for any follow-up. That said, the way you're creating the ship recovery special is missing some stuff. Here's an example creating one for a derelict:

ShipRecoverySpecialData data = new ShipRecoverySpecialData(null);
DerelictShipEntityPlugin plugin = (DerelictShipEntityPlugin) entity.getCustomPlugin();
data.addShip(plugin.getData().ship.clone());
Misc.setSalvageSpecial(entity, data);

The part you're missing is calling .addShip() for the ShipRecoverySpecialData.

The other issue is the flow of it - you're already in a "special" interaction, and when it ends, it'll unset the "special" data in the entity memory, since you've called setShowAgain(false). You want to call setShowAgain(true), so that it keeps the special data around (the code is assuming it's the old data, but it'll be the data you switched in using Misc.setSalvageSpecial.

However, even with that, I don't believe it'll chain to showing that special - it'll move on straight to salvage, per the sal_specialFinished rule. What you'll want to do is create another rule for the SalvageSpecialFinished trigger (and set a variable in entity memory from your "if (recoverable) {" block so you can tell when to use this rule), and have that rule "FireBest CheckSalvageSpecial" so that on finishing salvage, it loops back around to check for the special again, this time it being the newly-set ship recovery special.

Hope that makes sense!

As a result of this, I've changed the final code block to this:

Code: java
        if (recoverable) {
            log.info("recovering ghost ship");
            SalvageSpecialAssigner.ShipRecoverySpecialCreator creator = new SalvageSpecialAssigner.ShipRecoverySpecialCreator(null, 0, 0, false, null, null);
            ShipRecoverySpecialData specialData = (ShipRecoverySpecialData) creator.createSpecial(entity, null);
            specialData.addShip(data.shipData);
            Misc.setSalvageSpecial(entity, specialData);
            ShipRecoverySpecial special = new ShipRecoverySpecial();
            special.init(dialog, specialData);
            entity.getMemory().set(CHECK_SPECIAL, true);
            setDone(true);
            setEndWithContinue(true);
            setShowAgain(true);
        } else if (destroy) {
            log.info("destroying ghost ship");
            entity.getMemory().set(DESTROY_SHIP, true);
            setDone(true);
            setEndWithContinue(true);
            setShowAgain(false);           
        }

(CHECK_SPECIAL is $vayra_checkSalvageSpecialAgain)

and added the following rules (was playing around with having or not having the continue option):



Now the demolish rule works perfectly, which is awesome! However, the other one appears to trigger, find the special but not do anything about it, clear the special, trigger again, and since there's no longer a special, proceed directly to salvaging (with or without a 'continue' in between):

Quote from: starsector.log
Rules  - Picked: vayra_ghostShipFinishedNoContinue

Rules  - Looking for best match
Rules  - Memory:
Rules  - Found 2 rules for trigger [CheckSalvageSpecial]
Rules  - Checking rule: sal_checkSpecialFound
Rules  - Conditions: $salvageSpecialData != null
Rules  - All conditions passed, score 1
Rules  - Checking rule: sal_checkSpecialNoneFound
Rules  - Conditions:
Rules  - All conditions passed, score 0
Rules  - Number of matches with same score: 1, picking one randomly.
Rules  - Picked: sal_checkSpecialFound

Rules  - Looking for best match
Rules  - Memory:
Rules  - Found 3 rules for trigger [SalvageSpecialFinishedNoContinue]
Rules  - Checking rule: sal_specialFinishedNoContinue
Rules  - Conditions:
Rules  - All conditions passed, score 0
Rules  - Checking rule: vayra_ghostShipFinishedNoContinue
Rules  - Conditions: $vayra_checkSalvageSpecialAgain score:1000
Rules  - All conditions passed, score 1000
Rules  - Checking rule: vayra_ghostShipFinishedNoContinueDestroyed
Rules  - Conditions: $vayra_destroyGhostShip score:1000
Rules  - Failed condition: $vayra_destroyGhostShip score:1000
Rules  - Number of matches with same score: 1, picking one randomly.
Rules  - Picked: vayra_ghostShipFinishedNoContinue

Rules  - Looking for best match
Rules  - Memory:
Rules  - Found 2 rules for trigger [CheckSalvageSpecial]
Rules  - Checking rule: sal_checkSpecialFound
Rules  - Conditions: $salvageSpecialData != null
Rules  - Failed condition: $salvageSpecialData != null
Rules  - Checking rule: sal_checkSpecialNoneFound
Rules  - Conditions:
Rules  - All conditions passed, score 0
Rules  - Number of matches with same score: 1, picking one randomly.
Rules  - Picked: sal_checkSpecialNoneFound

Rules  - Looking for best match
Rules  - Memory:
Rules  - Found 4 rules for trigger [BeginSalvage]
Rules  - Checking rule: sal_showRatingAndCost
Rules  - Conditions:
Rules  - All conditions passed, score 0
Rules  - Checking rule: saic_openDialog
Rules  - Conditions: $saic_eventRef != null score:1000
Rules  - Failed condition: $saic_eventRef != null score:1000
Rules  - Checking rule: cryo_infoText
Rules  - Conditions: $customType == derelict_cryosleeper score:1000
Rules  - Failed condition: $customType == derelict_cryosleeper score:1000
Rules  - Checking rule: psi_beatDefenders
Rules  - Conditions: $psi_planet score:1000
Rules  - Failed condition: $psi_planet score:1000
Rules  - Number of matches with same score: 1, picking one randomly.
Rules  - Picked: sal_showRatingAndCost

I'm stumped!  :'(
« Last Edit: September 10, 2019, 08:42:56 PM by Vayra »
Logged
Kadur Remnant: http://fractalsoftworks.com/forum/index.php?topic=6649
Vayra's Sector: http://fractalsoftworks.com/forum/index.php?topic=16058
Vayra's Ship Pack: http://fractalsoftworks.com/forum/index.php?topic=16059

im gonna push jangala into the sun i swear to god im gonna do it

Alex

  • Administrator
  • Admiral
  • *****
  • Posts: 23986
    • View Profile
Re: [HELP!] Ghost ship mysteries
« Reply #1 on: September 10, 2019, 09:07:40 PM »

If you're calling createSpecial() instead of using the ShipRecoverySpecialData constructor directly (per my example) you probably don't need to call .addShip() since the createSpecial() method does do that.

Quote
However, the other one appears to trigger, find the special but not do anything about it, clear the special, trigger again, and since there's no longer a special, proceed directly to salvaging (with or without a 'continue' in between):

See: ShipRecoverySpecial.java

That probably means it tried to init but failed to find a ship defined in the special data and skipped the special. Why that might happen, I can't quite say - most likely either no ship data specified, or an invalid variant ID, or some such. Looking at the ShipRecoverySpecial code may prove informative and is it's probably worth putting a breakpoint in.


Side note, btw:
Try not to use getMemory(), use getMemoryWithoutUpdate() instead. You don't want to call getMemory() if all you're doing is setting a value. getMemory() triggers an extensive recalculation of a bunch of temporary memory values that get used by rules. Generally, it's a bad idea to call it unless you're doing it from a UI interaction - which this case is, so it's not too bad - but there's also no reason for doing the extra calculations, and using getMemoryWithoutUpdate() for write-only operations is a good habit to get into. And for operations where you don't care about the various temp-computed values such as... well, see CoreCampaignPluginImpl, the various updateXXXX methods - those get called every time getMemory() is called.
Logged

Vayra

  • Admiral
  • *****
  • Posts: 627
  • jangala delenda est
    • View Profile
Re: [HELP!] Ghost ship mysteries
« Reply #2 on: September 10, 2019, 09:20:09 PM »

Ah, okay. Swapped all my writes to getMemoryWithoutUpdate, and my block for your example block plus some logging - we'll see what it's actually adding with addShip...

EDIT: Yep, was passing a PerShipData with a null variant. I'll get that fixed. Thanks!
« Last Edit: September 10, 2019, 09:22:45 PM by Vayra »
Logged
Kadur Remnant: http://fractalsoftworks.com/forum/index.php?topic=6649
Vayra's Sector: http://fractalsoftworks.com/forum/index.php?topic=16058
Vayra's Ship Pack: http://fractalsoftworks.com/forum/index.php?topic=16059

im gonna push jangala into the sun i swear to god im gonna do it

Vayra

  • Admiral
  • *****
  • Posts: 627
  • jangala delenda est
    • View Profile
Re: [HELP!] Ghost ship mysteries
« Reply #3 on: September 10, 2019, 09:59:47 PM »

IIIIIIIT'S WOOOOOOOOOOOORKIIIIIIIIIIIIIIING <3
Logged
Kadur Remnant: http://fractalsoftworks.com/forum/index.php?topic=6649
Vayra's Sector: http://fractalsoftworks.com/forum/index.php?topic=16058
Vayra's Ship Pack: http://fractalsoftworks.com/forum/index.php?topic=16059

im gonna push jangala into the sun i swear to god im gonna do it

Ed

  • Captain
  • ****
  • Posts: 442
    • View Profile
Re: [HELP!] Ghost ship mysteries
« Reply #4 on: September 11, 2019, 01:05:03 PM »

Are you adding it to your large mod or making a new one? Sounds very interesting.
Logged
Check out my ships

Vayra

  • Admiral
  • *****
  • Posts: 627
  • jangala delenda est
    • View Profile
Re: [HELP!] Ghost ship mysteries
« Reply #5 on: September 12, 2019, 12:10:02 AM »

Are you adding it to your large mod or making a new one? Sounds very interesting.

New Vayra's Sector feature, hopefully releasing sometime next week along with some bugfixes.
Logged
Kadur Remnant: http://fractalsoftworks.com/forum/index.php?topic=6649
Vayra's Sector: http://fractalsoftworks.com/forum/index.php?topic=16058
Vayra's Ship Pack: http://fractalsoftworks.com/forum/index.php?topic=16059

im gonna push jangala into the sun i swear to god im gonna do it

Apogee_Freak

  • Lieutenant
  • **
  • Posts: 51
  • Are you hiding the blueprints under your farms?
    • View Profile
Re: [HELP!] Ghost ship mysteries
« Reply #6 on: September 12, 2019, 04:31:45 PM »

What does this do necessarily? Does it create some fleet that disappears? Is it a mission?
Logged

Vayra

  • Admiral
  • *****
  • Posts: 627
  • jangala delenda est
    • View Profile
Re: [HELP!] Ghost ship mysteries
« Reply #7 on: September 13, 2019, 11:24:44 AM »

What does this do necessarily? Does it create some fleet that disappears? Is it a mission?

admiral: hey you guys are back early
salvage team: ship's haunted
admiral: what
salvage team: *loading gun* ship's haunted
Logged
Kadur Remnant: http://fractalsoftworks.com/forum/index.php?topic=6649
Vayra's Sector: http://fractalsoftworks.com/forum/index.php?topic=16058
Vayra's Ship Pack: http://fractalsoftworks.com/forum/index.php?topic=16059

im gonna push jangala into the sun i swear to god im gonna do it

Apogee_Freak

  • Lieutenant
  • **
  • Posts: 51
  • Are you hiding the blueprints under your farms?
    • View Profile
Re: [HELP!] Ghost ship mysteries
« Reply #8 on: September 13, 2019, 11:33:10 PM »

salvage team: *loading gun* ship's haunted

That's hella spooky, looking forward to it.
Logged