forked from OpenWF/SpaceNinjaServer
		
	feat: archon hunt rewards (#1713)
also added a check for first completion to avoid giving another reward for repeating the final mission Closes #1624 Reviewed-on: OpenWF/SpaceNinjaServer#1713 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
							
								
									da6067ec43
								
							
						
					
					
						commit
						cdead6fdf8
					
				@ -53,6 +53,9 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
 | 
			
		||||
    logger.debug("mission report:", missionReport);
 | 
			
		||||
 | 
			
		||||
    const inventory = await getInventory(accountId);
 | 
			
		||||
    const firstCompletion = missionReport.SortieId
 | 
			
		||||
        ? inventory.CompletedSorties.indexOf(missionReport.SortieId) == -1
 | 
			
		||||
        : false;
 | 
			
		||||
    const inventoryUpdates = await addMissionInventoryUpdates(inventory, missionReport);
 | 
			
		||||
 | 
			
		||||
    if (
 | 
			
		||||
@ -69,7 +72,7 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const { MissionRewards, inventoryChanges, credits, AffiliationMods, SyndicateXPItemReward } =
 | 
			
		||||
        await addMissionRewards(inventory, missionReport);
 | 
			
		||||
        await addMissionRewards(inventory, missionReport, firstCompletion);
 | 
			
		||||
 | 
			
		||||
    await inventory.save();
 | 
			
		||||
    const inventoryResponse = await getInventoryResponse(inventory, true);
 | 
			
		||||
 | 
			
		||||
@ -91,7 +91,8 @@ import {
 | 
			
		||||
    ICrewMemberSkill,
 | 
			
		||||
    ICrewMemberSkillEfficiency,
 | 
			
		||||
    ICrewMemberDatabase,
 | 
			
		||||
    ICrewMemberClient
 | 
			
		||||
    ICrewMemberClient,
 | 
			
		||||
    ISortieRewardAttenuation
 | 
			
		||||
} from "../../types/inventoryTypes/inventoryTypes";
 | 
			
		||||
import { IOid } from "../../types/commonTypes";
 | 
			
		||||
import {
 | 
			
		||||
@ -1276,6 +1277,14 @@ lastSortieRewardSchema.set("toJSON", {
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const sortieRewardAttenutationSchema = new Schema<ISortieRewardAttenuation>(
 | 
			
		||||
    {
 | 
			
		||||
        Tag: String,
 | 
			
		||||
        Atten: Number
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const lockedWeaponGroupSchema = new Schema<ILockedWeaponGroupDatabase>(
 | 
			
		||||
    {
 | 
			
		||||
        s: Schema.Types.ObjectId,
 | 
			
		||||
@ -1495,6 +1504,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
			
		||||
        CompletedSorties: [String],
 | 
			
		||||
        LastSortieReward: { type: [lastSortieRewardSchema], default: undefined },
 | 
			
		||||
        LastLiteSortieReward: { type: [lastSortieRewardSchema], default: undefined },
 | 
			
		||||
        SortieRewardAttenuation: { type: [sortieRewardAttenutationSchema], default: undefined },
 | 
			
		||||
 | 
			
		||||
        // Resource Extractor Drones
 | 
			
		||||
        Drones: [droneSchema],
 | 
			
		||||
 | 
			
		||||
@ -412,7 +412,9 @@ export const addMissionInventoryUpdates = async (
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case "SortieId": {
 | 
			
		||||
                inventory.CompletedSorties.push(value);
 | 
			
		||||
                if (inventory.CompletedSorties.indexOf(value) == -1) {
 | 
			
		||||
                    inventory.CompletedSorties.push(value);
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case "SeasonChallengeCompletions": {
 | 
			
		||||
@ -569,7 +571,8 @@ export const addMissionRewards = async (
 | 
			
		||||
        RegularCredits: creditDrops,
 | 
			
		||||
        VoidTearParticipantsCurrWave: voidTearWave,
 | 
			
		||||
        StrippedItems: strippedItems
 | 
			
		||||
    }: IMissionInventoryUpdateRequest
 | 
			
		||||
    }: IMissionInventoryUpdateRequest,
 | 
			
		||||
    firstCompletion: boolean
 | 
			
		||||
): Promise<AddMissionRewardsReturnType> => {
 | 
			
		||||
    if (!rewardInfo) {
 | 
			
		||||
        //TODO: if there is a case where you can have credits collected during a mission but no rewardInfo, add credits needs to be handled earlier
 | 
			
		||||
@ -583,22 +586,12 @@ export const addMissionRewards = async (
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //TODO: check double reward merging
 | 
			
		||||
    const MissionRewards: IMissionReward[] = getRandomMissionDrops(rewardInfo, wagerTier);
 | 
			
		||||
    const MissionRewards: IMissionReward[] = getRandomMissionDrops(inventory, rewardInfo, wagerTier, firstCompletion);
 | 
			
		||||
    logger.debug("random mission drops:", MissionRewards);
 | 
			
		||||
    const inventoryChanges: IInventoryChanges = {};
 | 
			
		||||
    const AffiliationMods: IAffiliationMods[] = [];
 | 
			
		||||
    let SyndicateXPItemReward;
 | 
			
		||||
 | 
			
		||||
    if (rewardInfo.sortieTag == "Final") {
 | 
			
		||||
        inventory.LastSortieReward = [
 | 
			
		||||
            {
 | 
			
		||||
                SortieId: new Types.ObjectId(rewardInfo.sortieId!.split("_")[1]),
 | 
			
		||||
                StoreItem: MissionRewards[0].StoreItem,
 | 
			
		||||
                Manifest: "/Lotus/Types/Game/MissionDecks/SortieRewards"
 | 
			
		||||
            }
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let missionCompletionCredits = 0;
 | 
			
		||||
    //inventory change is what the client has not rewarded itself, also the client needs to know the credit changes for display
 | 
			
		||||
    if (levelKeyName) {
 | 
			
		||||
@ -962,11 +955,78 @@ function getLevelCreditRewards(node: IRegion): number {
 | 
			
		||||
    //TODO: get dark sektor fixed credit rewards and railjack bonus
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getRandomMissionDrops(RewardInfo: IRewardInfo, tierOverride: number | undefined): IMissionReward[] {
 | 
			
		||||
function getRandomMissionDrops(
 | 
			
		||||
    inventory: TInventoryDatabaseDocument,
 | 
			
		||||
    RewardInfo: IRewardInfo,
 | 
			
		||||
    tierOverride: number | undefined,
 | 
			
		||||
    firstCompletion: boolean
 | 
			
		||||
): IMissionReward[] {
 | 
			
		||||
    const drops: IMissionReward[] = [];
 | 
			
		||||
    if (RewardInfo.sortieTag == "Final") {
 | 
			
		||||
        const drop = getRandomRewardByChance(ExportRewards["/Lotus/Types/Game/MissionDecks/SortieRewards"][0])!;
 | 
			
		||||
        drops.push({ StoreItem: drop.type, ItemCount: drop.itemCount });
 | 
			
		||||
    if (RewardInfo.sortieTag == "Final" && firstCompletion) {
 | 
			
		||||
        const arr = RewardInfo.sortieId!.split("_");
 | 
			
		||||
        let sortieId = arr[1];
 | 
			
		||||
        if (sortieId == "Lite") {
 | 
			
		||||
            sortieId = arr[2];
 | 
			
		||||
 | 
			
		||||
            // TODO: Some way to get from sortieId to reward to make this faster + more reliable at week rollover.
 | 
			
		||||
            const boss = getWorldState().LiteSorties[0].Boss as
 | 
			
		||||
                | "SORTIE_BOSS_AMAR"
 | 
			
		||||
                | "SORTIE_BOSS_NIRA"
 | 
			
		||||
                | "SORTIE_BOSS_BOREAL";
 | 
			
		||||
            let crystalType = {
 | 
			
		||||
                SORTIE_BOSS_AMAR: "/Lotus/StoreItems/Types/Gameplay/NarmerSorties/ArchonCrystalAmar",
 | 
			
		||||
                SORTIE_BOSS_NIRA: "/Lotus/StoreItems/Types/Gameplay/NarmerSorties/ArchonCrystalNira",
 | 
			
		||||
                SORTIE_BOSS_BOREAL: "/Lotus/StoreItems/Types/Gameplay/NarmerSorties/ArchonCrystalBoreal"
 | 
			
		||||
            }[boss];
 | 
			
		||||
            const attenTag = {
 | 
			
		||||
                SORTIE_BOSS_AMAR: "NarmerSortieAmarCrystalRewards",
 | 
			
		||||
                SORTIE_BOSS_NIRA: "NarmerSortieNiraCrystalRewards",
 | 
			
		||||
                SORTIE_BOSS_BOREAL: "NarmerSortieBorealCrystalRewards"
 | 
			
		||||
            }[boss];
 | 
			
		||||
            const attenIndex = inventory.SortieRewardAttenuation?.findIndex(x => x.Tag == attenTag) ?? -1;
 | 
			
		||||
            const mythicProbability =
 | 
			
		||||
                0.2 + (inventory.SortieRewardAttenuation?.find(x => x.Tag == attenTag)?.Atten ?? 0);
 | 
			
		||||
            if (Math.random() < mythicProbability) {
 | 
			
		||||
                crystalType += "Mythic";
 | 
			
		||||
                if (attenIndex != -1) {
 | 
			
		||||
                    inventory.SortieRewardAttenuation!.splice(attenIndex, 1);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                if (attenIndex == -1) {
 | 
			
		||||
                    inventory.SortieRewardAttenuation ??= [];
 | 
			
		||||
                    inventory.SortieRewardAttenuation.push({
 | 
			
		||||
                        Tag: attenTag,
 | 
			
		||||
                        Atten: 0.2
 | 
			
		||||
                    });
 | 
			
		||||
                } else {
 | 
			
		||||
                    inventory.SortieRewardAttenuation![attenIndex].Atten += 0.2;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            drops.push({ StoreItem: crystalType, ItemCount: 1 });
 | 
			
		||||
 | 
			
		||||
            const drop = getRandomRewardByChance(
 | 
			
		||||
                ExportRewards["/Lotus/Types/Game/MissionDecks/ArchonSortieRewards"][0]
 | 
			
		||||
            )!;
 | 
			
		||||
            drops.push({ StoreItem: drop.type, ItemCount: drop.itemCount });
 | 
			
		||||
            inventory.LastLiteSortieReward = [
 | 
			
		||||
                {
 | 
			
		||||
                    SortieId: new Types.ObjectId(sortieId),
 | 
			
		||||
                    StoreItem: drop.type,
 | 
			
		||||
                    Manifest: "/Lotus/Types/Game/MissionDecks/ArchonSortieRewards"
 | 
			
		||||
                }
 | 
			
		||||
            ];
 | 
			
		||||
        } else {
 | 
			
		||||
            const drop = getRandomRewardByChance(ExportRewards["/Lotus/Types/Game/MissionDecks/SortieRewards"][0])!;
 | 
			
		||||
            drops.push({ StoreItem: drop.type, ItemCount: drop.itemCount });
 | 
			
		||||
            inventory.LastSortieReward = [
 | 
			
		||||
                {
 | 
			
		||||
                    SortieId: new Types.ObjectId(sortieId),
 | 
			
		||||
                    StoreItem: drop.type,
 | 
			
		||||
                    Manifest: "/Lotus/Types/Game/MissionDecks/SortieRewards"
 | 
			
		||||
                }
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (RewardInfo.periodicMissionTag?.startsWith("HardDaily")) {
 | 
			
		||||
        drops.push({
 | 
			
		||||
 | 
			
		||||
@ -285,6 +285,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
 | 
			
		||||
    CompletedSorties: string[];
 | 
			
		||||
    LastSortieReward?: ILastSortieRewardClient[];
 | 
			
		||||
    LastLiteSortieReward?: ILastSortieRewardClient[];
 | 
			
		||||
    SortieRewardAttenuation?: ISortieRewardAttenuation[];
 | 
			
		||||
    Drones: IDroneClient[];
 | 
			
		||||
    StepSequencers: IStepSequencer[];
 | 
			
		||||
    ActiveAvatarImageType: string;
 | 
			
		||||
@ -746,6 +747,11 @@ export interface ILastSortieRewardDatabase extends Omit<ILastSortieRewardClient,
 | 
			
		||||
    SortieId: Types.ObjectId;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ISortieRewardAttenuation {
 | 
			
		||||
    Tag: string;
 | 
			
		||||
    Atten: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ILibraryDailyTaskInfo {
 | 
			
		||||
    EnemyTypes: string[];
 | 
			
		||||
    EnemyLocTag: string;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user