SwappableState #42

Open
opened 2023-11-16 17:04:29 +00:00 by thepaperpilot · 3 comments
Collaborator

Basically a more automagic version of what advent did for mastery. It should take a list of objects that will be recursively searched for persistent values from, and will swap it all out. Care will have to be taken that it is given non-proxied objects. (layer objects or objects in player should work)

Basically a more automagic version of what advent did for mastery. It should take a list of objects that will be recursively searched for persistent values from, and will swap it all out. Care will have to be taken that it is given non-proxied objects. (layer objects or objects in player should work)
thepaperpilot added this to the Profectus project 2023-11-16 17:04:29 +00:00
Author
Collaborator

@escapee What did you do for abyss challenges? Could that perhaps be separated out into a reusable feature?

@escapee What did you do for abyss challenges? Could that perhaps be separated out into a reusable feature?
Collaborator

Not fully separable, although chunks of it could be - the way I made that work was to have a swapData object in my abyss layer, with two utility functions to assist with generating and swapping the values. A lot of the time it could just be a simple use of duplicating save data with clonePersistentData() then swapping it whenever with swapPersistentData(), but there wasn't any good way to exclude specific values from that process.

export function clonePersistentData(object: unknown) {
    if (object == undefined) return;
    if (typeof object !== "object") return;
    if (object instanceof Decimal) return;
    if (object instanceof Formula) return;
    if (SkipPersistence in object && object[SkipPersistence] === true) return;

    if (DefaultValue in object) {
        const original = object as NonPersistent;
        return persistent(original[DefaultValue]);
    }

    if (isRef(object)) return;

    const copy = {} as Record<string | number | symbol, unknown>;
    for (const [key, value] of Object.entries(object)) {
        if (['requirements', 'tabs', 'prerequisites'].includes(key)) continue;
        const data = clonePersistentData(value);
        if (data !== undefined) copy[key] = data;
    }
    if (Object.keys(copy).length === 0) return;
    return copy;
}

export function swapPersistentData(object: unknown, clone: unknown) {
    if (object == undefined || clone == undefined) return;
    if (typeof object !== "object" || typeof clone !== "object") return;
    if (object instanceof Decimal || clone instanceof Decimal) return;
    if (object instanceof Formula || clone instanceof Formula) return;
    if (SkipPersistence in object && object[SkipPersistence] === true) return;
    if (SkipPersistence in clone && clone[SkipPersistence] === true) return;
    
    if (DefaultValue in clone) {
        const o = (object as NonPersistent);
        const c = (clone as NonPersistent);
        const swap = o.value;
        o.value = c.value;
        c.value = swap;
        return;
    }

    if (isRef(object)) return;
    if (isRef(clone)) return;

    for (const key in clone) {
        swapPersistentData((object as Record<string, unknown>)[key], (clone as Record<string, unknown>)[key]);
    }
}
const challenge = createChallenge(feature => ({
    requirements: createCostRequirement( ... ),
    completionLimit: 4,
    onEnter() {
        swapPersistentData(layers, swapData);
        ...
    },
    onExit() {
        swapPersistentData(layers, swapData);
        ...
    }
}));

const swapData: Record<string, unknown> = (() => {
    const swapData = {
        skyrmion: {
            conversion: { amount: persistent<DecimalSource>(1) },
            pion: clonePersistentData(skyrmion.pion),
            spinor: clonePersistentData(skyrmion.spinor)
        },
        fome: {
            protoversal: clonePersistentData(fome.protoversal),
            infinitesimal: clonePersistentData(fome.infinitesimal),
            subspatial: clonePersistentData(fome.subspatial),
            subplanck: clonePersistentData(fome.subplanck),
            quantum: clonePersistentData(fome.quantum)
        },
        acceleron: {
            accelerons: clonePersistentData(acceleron.accelerons),
            bestAccelerons: clonePersistentData(acceleron.bestAccelerons),
            totalAccelerons: clonePersistentData(acceleron.totalAccelerons),
            entropy: clonePersistentData(acceleron.entropy),
            loops: clonePersistentData(acceleron.loops),
            upgrades: clonePersistentData(acceleron.upgrades)
        },
        timecube: clonePersistentData(timecube),
        inflaton: clonePersistentData(inflaton)
    };

    return swapData;
})();
Not fully separable, although chunks of it could be - the way I made that work was to have a `swapData` object in my abyss layer, with two utility functions to assist with generating and swapping the values. A lot of the time it could just be a simple use of duplicating save data with `clonePersistentData()` then swapping it whenever with `swapPersistentData()`, but there wasn't any good way to exclude specific values from that process. ```ts export function clonePersistentData(object: unknown) { if (object == undefined) return; if (typeof object !== "object") return; if (object instanceof Decimal) return; if (object instanceof Formula) return; if (SkipPersistence in object && object[SkipPersistence] === true) return; if (DefaultValue in object) { const original = object as NonPersistent; return persistent(original[DefaultValue]); } if (isRef(object)) return; const copy = {} as Record<string | number | symbol, unknown>; for (const [key, value] of Object.entries(object)) { if (['requirements', 'tabs', 'prerequisites'].includes(key)) continue; const data = clonePersistentData(value); if (data !== undefined) copy[key] = data; } if (Object.keys(copy).length === 0) return; return copy; } export function swapPersistentData(object: unknown, clone: unknown) { if (object == undefined || clone == undefined) return; if (typeof object !== "object" || typeof clone !== "object") return; if (object instanceof Decimal || clone instanceof Decimal) return; if (object instanceof Formula || clone instanceof Formula) return; if (SkipPersistence in object && object[SkipPersistence] === true) return; if (SkipPersistence in clone && clone[SkipPersistence] === true) return; if (DefaultValue in clone) { const o = (object as NonPersistent); const c = (clone as NonPersistent); const swap = o.value; o.value = c.value; c.value = swap; return; } if (isRef(object)) return; if (isRef(clone)) return; for (const key in clone) { swapPersistentData((object as Record<string, unknown>)[key], (clone as Record<string, unknown>)[key]); } } ``` ```ts const challenge = createChallenge(feature => ({ requirements: createCostRequirement( ... ), completionLimit: 4, onEnter() { swapPersistentData(layers, swapData); ... }, onExit() { swapPersistentData(layers, swapData); ... } })); const swapData: Record<string, unknown> = (() => { const swapData = { skyrmion: { conversion: { amount: persistent<DecimalSource>(1) }, pion: clonePersistentData(skyrmion.pion), spinor: clonePersistentData(skyrmion.spinor) }, fome: { protoversal: clonePersistentData(fome.protoversal), infinitesimal: clonePersistentData(fome.infinitesimal), subspatial: clonePersistentData(fome.subspatial), subplanck: clonePersistentData(fome.subplanck), quantum: clonePersistentData(fome.quantum) }, acceleron: { accelerons: clonePersistentData(acceleron.accelerons), bestAccelerons: clonePersistentData(acceleron.bestAccelerons), totalAccelerons: clonePersistentData(acceleron.totalAccelerons), entropy: clonePersistentData(acceleron.entropy), loops: clonePersistentData(acceleron.loops), upgrades: clonePersistentData(acceleron.upgrades) }, timecube: clonePersistentData(timecube), inflaton: clonePersistentData(inflaton) }; return swapData; })(); ```
Collaborator

A good bit of manual work to create and use the data copies, no way I can tell to make it as simple as any other feature, but including those as utility functions for anyone who wants to keep their challenges from resetting the standard route's data would be fine by me

A good bit of manual work to create and use the data copies, no way I can tell to make it as simple as any other feature, but including those as utility functions for anyone who wants to keep their challenges from resetting the standard route's data would be fine by me
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: profectus/Profectus#42
No description provided.