feat: classic lich ephemera reward (#2067)
Reviewed-on: OpenWF/SpaceNinjaServer#2067 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									2ce5cc4562
								
							
						
					
					
						commit
						8fb676c906
					
				@ -47,6 +47,42 @@ export const showdownNodes: Record<TNemesisFaction, string> = {
 | 
			
		||||
    FC_INFESTATION: "CrewBattleNode559"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ephemeraProbabilities: Record<TNemesisFaction, number> = {
 | 
			
		||||
    FC_GRINEER: 0.05,
 | 
			
		||||
    FC_CORPUS: 0.2,
 | 
			
		||||
    FC_INFESTATION: 0
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type TInnateDamageTag =
 | 
			
		||||
    | "InnateElectricityDamage"
 | 
			
		||||
    | "InnateHeatDamage"
 | 
			
		||||
    | "InnateFreezeDamage"
 | 
			
		||||
    | "InnateToxinDamage"
 | 
			
		||||
    | "InnateMagDamage"
 | 
			
		||||
    | "InnateRadDamage"
 | 
			
		||||
    | "InnateImpactDamage";
 | 
			
		||||
 | 
			
		||||
const ephmeraTypes: Record<"FC_GRINEER" | "FC_CORPUS", Record<TInnateDamageTag, string>> = {
 | 
			
		||||
    FC_GRINEER: {
 | 
			
		||||
        InnateElectricityDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaLightningEphemera",
 | 
			
		||||
        InnateHeatDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaFireEphemera",
 | 
			
		||||
        InnateFreezeDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaIceEphemera",
 | 
			
		||||
        InnateToxinDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaToxinEphemera",
 | 
			
		||||
        InnateMagDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaMagneticEphemera",
 | 
			
		||||
        InnateRadDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaTricksterEphemera",
 | 
			
		||||
        InnateImpactDamage: "/Lotus/Upgrades/Skins/Effects/Kuva/KuvaImpactEphemera"
 | 
			
		||||
    },
 | 
			
		||||
    FC_CORPUS: {
 | 
			
		||||
        InnateElectricityDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraA",
 | 
			
		||||
        InnateHeatDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraB",
 | 
			
		||||
        InnateFreezeDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraC",
 | 
			
		||||
        InnateToxinDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraD",
 | 
			
		||||
        InnateMagDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraE",
 | 
			
		||||
        InnateRadDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraF",
 | 
			
		||||
        InnateImpactDamage: "/Lotus/Upgrades/Skins/Effects/CorpusLichEphemeraG"
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Get a parazon 'passcode' based on the nemesis fingerprint so it's always the same for the same nemesis.
 | 
			
		||||
export const getNemesisPasscode = (nemesis: { fp: bigint; Faction: TNemesisFaction }): number[] => {
 | 
			
		||||
    const rng = new SRng(nemesis.fp);
 | 
			
		||||
@ -271,21 +307,25 @@ export const isNemesisCompatibleWithVersion = (
 | 
			
		||||
    return true;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getInnateDamageTag = (
 | 
			
		||||
    KillingSuit: string
 | 
			
		||||
):
 | 
			
		||||
    | "InnateElectricityDamage"
 | 
			
		||||
    | "InnateFreezeDamage"
 | 
			
		||||
    | "InnateHeatDamage"
 | 
			
		||||
    | "InnateImpactDamage"
 | 
			
		||||
    | "InnateMagDamage"
 | 
			
		||||
    | "InnateRadDamage"
 | 
			
		||||
    | "InnateToxinDamage" => {
 | 
			
		||||
export const getInnateDamageTag = (KillingSuit: string): TInnateDamageTag => {
 | 
			
		||||
    return ExportWarframes[KillingSuit].nemesisUpgradeTag!;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// TODO: For -1399275245665749231n, the value should be 75306944, but we're off by 59 with 75307003.
 | 
			
		||||
export const getInnateDamageValue = (fp: bigint): number => {
 | 
			
		||||
export interface INemesisProfile {
 | 
			
		||||
    innateDamageTag: TInnateDamageTag;
 | 
			
		||||
    innateDamageValue: number;
 | 
			
		||||
    ephemera?: string;
 | 
			
		||||
    petHead?: string;
 | 
			
		||||
    petBody?: string;
 | 
			
		||||
    petLegs?: string;
 | 
			
		||||
    petTail?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const generateNemesisProfile = (
 | 
			
		||||
    fp: bigint = generateRewardSeed(),
 | 
			
		||||
    Faction: TNemesisFaction = "FC_CORPUS",
 | 
			
		||||
    killingSuit: string = "/Lotus/Powersuits/Ember/Ember"
 | 
			
		||||
): INemesisProfile => {
 | 
			
		||||
    const rng = new SRng(fp);
 | 
			
		||||
    rng.randomFloat(); // used for the weapon index
 | 
			
		||||
    const WeaponUpgradeValueAttenuationExponent = 2.25;
 | 
			
		||||
@ -293,7 +333,37 @@ export const getInnateDamageValue = (fp: bigint): number => {
 | 
			
		||||
    if (value >= 0.941428) {
 | 
			
		||||
        value = 1;
 | 
			
		||||
    }
 | 
			
		||||
    return Math.trunc(value * 0x40000000);
 | 
			
		||||
    const profile: INemesisProfile = {
 | 
			
		||||
        innateDamageTag: getInnateDamageTag(killingSuit),
 | 
			
		||||
        innateDamageValue: Math.trunc(value * 0x40000000) // TODO: For -1399275245665749231n, the value should be 75306944, but we're off by 59 with 75307003.
 | 
			
		||||
    };
 | 
			
		||||
    if (rng.randomFloat() <= ephemeraProbabilities[Faction] && Faction != "FC_INFESTATION") {
 | 
			
		||||
        profile.ephemera = ephmeraTypes[Faction][profile.innateDamageTag];
 | 
			
		||||
    }
 | 
			
		||||
    rng.randomFloat(); // something related to sentinel agent maybe
 | 
			
		||||
    if (Faction == "FC_CORPUS") {
 | 
			
		||||
        profile.petHead = rng.randomElement([
 | 
			
		||||
            "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA",
 | 
			
		||||
            "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB",
 | 
			
		||||
            "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC"
 | 
			
		||||
        ])!;
 | 
			
		||||
        profile.petBody = rng.randomElement([
 | 
			
		||||
            "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyA",
 | 
			
		||||
            "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyB",
 | 
			
		||||
            "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyC"
 | 
			
		||||
        ])!;
 | 
			
		||||
        profile.petLegs = rng.randomElement([
 | 
			
		||||
            "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsA",
 | 
			
		||||
            "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsB",
 | 
			
		||||
            "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsC"
 | 
			
		||||
        ])!;
 | 
			
		||||
        profile.petTail = rng.randomElement([
 | 
			
		||||
            "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailA",
 | 
			
		||||
            "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailB",
 | 
			
		||||
            "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailC"
 | 
			
		||||
        ])!;
 | 
			
		||||
    }
 | 
			
		||||
    return profile;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getKillTokenRewardCount = (fp: bigint): number => {
 | 
			
		||||
 | 
			
		||||
@ -84,7 +84,7 @@ import { getRandomElement, getRandomInt, getRandomWeightedReward, SRng } from ".
 | 
			
		||||
import { createMessage } from "./inboxService";
 | 
			
		||||
import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper";
 | 
			
		||||
import { getWorldState } from "./worldStateService";
 | 
			
		||||
import { getInnateDamageTag, getInnateDamageValue } from "../helpers/nemesisHelpers";
 | 
			
		||||
import { generateNemesisProfile, INemesisProfile } from "../helpers/nemesisHelpers";
 | 
			
		||||
 | 
			
		||||
export const createInventory = async (
 | 
			
		||||
    accountOwnerId: Types.ObjectId,
 | 
			
		||||
@ -1969,8 +1969,7 @@ export const giveNemesisWeaponRecipe = (
 | 
			
		||||
    weaponType: string,
 | 
			
		||||
    nemesisName: string = "AGOR ROK",
 | 
			
		||||
    weaponLoc?: string,
 | 
			
		||||
    KillingSuit: string = "/Lotus/Powersuits/Ember/Ember",
 | 
			
		||||
    fp: bigint = generateRewardSeed()
 | 
			
		||||
    profile: INemesisProfile = generateNemesisProfile()
 | 
			
		||||
): void => {
 | 
			
		||||
    if (!weaponLoc) {
 | 
			
		||||
        weaponLoc = ExportWeapons[weaponType].name;
 | 
			
		||||
@ -1991,8 +1990,8 @@ export const giveNemesisWeaponRecipe = (
 | 
			
		||||
                compat: weaponType,
 | 
			
		||||
                buffs: [
 | 
			
		||||
                    {
 | 
			
		||||
                        Tag: getInnateDamageTag(KillingSuit),
 | 
			
		||||
                        Value: getInnateDamageValue(fp)
 | 
			
		||||
                        Tag: profile.innateDamageTag,
 | 
			
		||||
                        Value: profile.innateDamageValue
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
@ -2001,27 +2000,15 @@ export const giveNemesisWeaponRecipe = (
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const giveNemesisPetRecipe = (inventory: TInventoryDatabaseDocument, nemesisName: string = "AGOR ROK"): void => {
 | 
			
		||||
    const head = getRandomElement([
 | 
			
		||||
        "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA",
 | 
			
		||||
        "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB",
 | 
			
		||||
        "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC"
 | 
			
		||||
    ])!;
 | 
			
		||||
    const body = getRandomElement([
 | 
			
		||||
        "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyA",
 | 
			
		||||
        "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyB",
 | 
			
		||||
        "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyC"
 | 
			
		||||
    ])!;
 | 
			
		||||
    const legs = getRandomElement([
 | 
			
		||||
        "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsA",
 | 
			
		||||
        "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsB",
 | 
			
		||||
        "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsC"
 | 
			
		||||
    ])!;
 | 
			
		||||
    const tail = getRandomElement([
 | 
			
		||||
        "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailA",
 | 
			
		||||
        "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailB",
 | 
			
		||||
        "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailC"
 | 
			
		||||
    ])!;
 | 
			
		||||
export const giveNemesisPetRecipe = (
 | 
			
		||||
    inventory: TInventoryDatabaseDocument,
 | 
			
		||||
    nemesisName: string = "AGOR ROK",
 | 
			
		||||
    profile: INemesisProfile = generateNemesisProfile()
 | 
			
		||||
): void => {
 | 
			
		||||
    const head = profile.petHead!;
 | 
			
		||||
    const body = profile.petBody!;
 | 
			
		||||
    const legs = profile.petLegs!;
 | 
			
		||||
    const tail = profile.petTail!;
 | 
			
		||||
    const recipeType = Object.entries(ExportRecipes).find(arr => arr[1].resultType == head)![0];
 | 
			
		||||
    addRecipes(inventory, [
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
@ -57,6 +57,7 @@ import kuriaMessage75 from "@/static/fixed_responses/kuriaMessages/seventyFivePe
 | 
			
		||||
import kuriaMessage100 from "@/static/fixed_responses/kuriaMessages/oneHundredPercent.json";
 | 
			
		||||
import conservationAnimals from "@/static/fixed_responses/conservationAnimals.json";
 | 
			
		||||
import {
 | 
			
		||||
    generateNemesisProfile,
 | 
			
		||||
    getInfNodes,
 | 
			
		||||
    getNemesisPasscode,
 | 
			
		||||
    getWeaponsForManifest,
 | 
			
		||||
@ -659,6 +660,12 @@ export const addMissionInventoryUpdates = async (
 | 
			
		||||
                        k: value.killed
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    const profile = generateNemesisProfile(
 | 
			
		||||
                        inventory.Nemesis.fp,
 | 
			
		||||
                        inventory.Nemesis.Faction,
 | 
			
		||||
                        inventory.Nemesis.KillingSuit
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    if (value.killed) {
 | 
			
		||||
                        if (
 | 
			
		||||
                            value.weaponLoc &&
 | 
			
		||||
@ -667,20 +674,18 @@ export const addMissionInventoryUpdates = async (
 | 
			
		||||
                            const weaponType = getWeaponsForManifest(inventory.Nemesis.manifest)[
 | 
			
		||||
                                inventory.Nemesis.WeaponIdx
 | 
			
		||||
                            ];
 | 
			
		||||
                            giveNemesisWeaponRecipe(
 | 
			
		||||
                                inventory,
 | 
			
		||||
                                weaponType,
 | 
			
		||||
                                value.nemesisName,
 | 
			
		||||
                                value.weaponLoc,
 | 
			
		||||
                                inventory.Nemesis.KillingSuit,
 | 
			
		||||
                                inventory.Nemesis.fp
 | 
			
		||||
                            );
 | 
			
		||||
                            giveNemesisWeaponRecipe(inventory, weaponType, value.nemesisName, value.weaponLoc, profile);
 | 
			
		||||
                        }
 | 
			
		||||
                        if (value.petLoc) {
 | 
			
		||||
                            giveNemesisPetRecipe(inventory);
 | 
			
		||||
                            giveNemesisPetRecipe(inventory, value.nemesisName, profile);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // "Players will receive a Lich's Ephemera regardless of whether they Vanquish or Convert them."
 | 
			
		||||
                    if (profile.ephemera) {
 | 
			
		||||
                        addSkin(inventory, profile.ephemera);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // TOVERIFY: Is the inbox message also sent when converting a lich? If not, how are the rewards given?
 | 
			
		||||
                    if (inventory.Nemesis.Faction == "FC_INFESTATION") {
 | 
			
		||||
                        await sendCodaFinishedMessage(inventory, inventory.Nemesis.fp, value.nemesisName, value.killed);
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user