feat: opening relics in endless missions (#713)
This commit is contained in:
		
							parent
							
								
									8d7f69ce80
								
							
						
					
					
						commit
						3903b973e2
					
				
							
								
								
									
										99
									
								
								src/controllers/api/getVoidProjectionRewardsController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/controllers/api/getVoidProjectionRewardsController.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,99 @@
 | 
			
		||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
			
		||||
import { addMiscItems, getInventory } from "@/src/services/inventoryService";
 | 
			
		||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
			
		||||
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
 | 
			
		||||
import { getRandomWeightedReward2 } from "@/src/services/rngService";
 | 
			
		||||
import { ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
			
		||||
import { logger } from "@/src/utils/logger";
 | 
			
		||||
import { RequestHandler } from "express";
 | 
			
		||||
import { ExportRelics, ExportRewards, TRarity } from "warframe-public-export-plus";
 | 
			
		||||
 | 
			
		||||
export const getVoidProjectionRewardsController: RequestHandler = async (req, res) => {
 | 
			
		||||
    const accountId = await getAccountIdForRequest(req);
 | 
			
		||||
    const data = getJSONfromString(String(req.body)) as IVoidProjectionRewardRequest;
 | 
			
		||||
    const response: IVoidProjectionRewardResponse = {
 | 
			
		||||
        CurrentWave: data.CurrentWave,
 | 
			
		||||
        ParticipantInfo: data.ParticipantInfo,
 | 
			
		||||
        DifficultyTier: data.DifficultyTier
 | 
			
		||||
    };
 | 
			
		||||
    if (data.ParticipantInfo.QualifiesForReward) {
 | 
			
		||||
        const relic = ExportRelics[data.ParticipantInfo.VoidProjection];
 | 
			
		||||
        const weights = refinementToWeights[relic.quality];
 | 
			
		||||
        logger.debug(`opening a relic of quality ${relic.quality}; rarity weights are`, weights);
 | 
			
		||||
        const reward = getRandomWeightedReward2(
 | 
			
		||||
            ExportRewards[relic.rewardManifest][0] as { type: string; itemCount: number; rarity: TRarity }[], // rarity is nullable in PE+ typings, but always present for relics
 | 
			
		||||
            weights
 | 
			
		||||
        )!;
 | 
			
		||||
        logger.debug(`relic rolled`, reward);
 | 
			
		||||
        response.ParticipantInfo.Reward = reward.type;
 | 
			
		||||
 | 
			
		||||
        // Remove relic
 | 
			
		||||
        const inventory = await getInventory(accountId);
 | 
			
		||||
        addMiscItems(inventory, [
 | 
			
		||||
            {
 | 
			
		||||
                ItemType: data.ParticipantInfo.VoidProjection,
 | 
			
		||||
                ItemCount: -1
 | 
			
		||||
            }
 | 
			
		||||
        ]);
 | 
			
		||||
        await inventory.save();
 | 
			
		||||
 | 
			
		||||
        // Give reward
 | 
			
		||||
        await handleStoreItemAcquisition(reward.type, accountId, reward.itemCount);
 | 
			
		||||
    }
 | 
			
		||||
    res.json(response);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const refinementToWeights = {
 | 
			
		||||
    VPQ_BRONZE: {
 | 
			
		||||
        COMMON: 0.76,
 | 
			
		||||
        UNCOMMON: 0.22,
 | 
			
		||||
        RARE: 0.02,
 | 
			
		||||
        LEGENDARY: 0
 | 
			
		||||
    },
 | 
			
		||||
    VPQ_SILVER: {
 | 
			
		||||
        COMMON: 0.7,
 | 
			
		||||
        UNCOMMON: 0.26,
 | 
			
		||||
        RARE: 0.04,
 | 
			
		||||
        LEGENDARY: 0
 | 
			
		||||
    },
 | 
			
		||||
    VPQ_GOLD: {
 | 
			
		||||
        COMMON: 0.6,
 | 
			
		||||
        UNCOMMON: 0.34,
 | 
			
		||||
        RARE: 0.06,
 | 
			
		||||
        LEGENDARY: 0
 | 
			
		||||
    },
 | 
			
		||||
    VPQ_PLATINUM: {
 | 
			
		||||
        COMMON: 0.5,
 | 
			
		||||
        UNCOMMON: 0.4,
 | 
			
		||||
        RARE: 0.1,
 | 
			
		||||
        LEGENDARY: 0
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
interface IVoidProjectionRewardRequest {
 | 
			
		||||
    CurrentWave: number;
 | 
			
		||||
    ParticipantInfo: IParticipantInfo;
 | 
			
		||||
    VoidTier: string;
 | 
			
		||||
    DifficultyTier: number;
 | 
			
		||||
    VoidProjectionRemovalHash: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IVoidProjectionRewardResponse {
 | 
			
		||||
    CurrentWave: number;
 | 
			
		||||
    ParticipantInfo: IParticipantInfo;
 | 
			
		||||
    DifficultyTier: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IParticipantInfo {
 | 
			
		||||
    AccountId: string;
 | 
			
		||||
    Name: string;
 | 
			
		||||
    ChosenRewardOwner: string;
 | 
			
		||||
    MissionHash: string;
 | 
			
		||||
    VoidProjection: string;
 | 
			
		||||
    Reward: string;
 | 
			
		||||
    QualifiesForReward: boolean;
 | 
			
		||||
    HaveRewardResponse: boolean;
 | 
			
		||||
    RewardsMultiplier: number;
 | 
			
		||||
    RewardProjection: string;
 | 
			
		||||
    HardModeReward: ITypeCount;
 | 
			
		||||
}
 | 
			
		||||
@ -26,6 +26,7 @@ import { getIgnoredUsersController } from "@/src/controllers/api/getIgnoredUsers
 | 
			
		||||
import { getNewRewardSeedController } from "@/src/controllers/api/getNewRewardSeedController";
 | 
			
		||||
import { getShipController } from "@/src/controllers/api/getShipController";
 | 
			
		||||
import { getVendorInfoController } from "@/src/controllers/api/getVendorInfoController";
 | 
			
		||||
import { getVoidProjectionRewardsController } from "@/src/controllers/api/getVoidProjectionRewardsController";
 | 
			
		||||
import { gildWeaponController } from "@/src/controllers/api/gildWeaponController";
 | 
			
		||||
import { guildTechController } from "../controllers/api/guildTechController";
 | 
			
		||||
import { hostSessionController } from "@/src/controllers/api/hostSessionController";
 | 
			
		||||
@ -120,6 +121,7 @@ apiRouter.post("/focus.php", focusController);
 | 
			
		||||
apiRouter.post("/fusionTreasures.php", fusionTreasuresController);
 | 
			
		||||
apiRouter.post("/genericUpdate.php", genericUpdateController);
 | 
			
		||||
apiRouter.post("/getAlliance.php", getAllianceController);
 | 
			
		||||
apiRouter.post("/getVoidProjectionRewards.php", getVoidProjectionRewardsController);
 | 
			
		||||
apiRouter.post("/gildWeapon.php", gildWeaponController);
 | 
			
		||||
apiRouter.post("/guildTech.php", guildTechController);
 | 
			
		||||
apiRouter.post("/hostSession.php", hostSessionController);
 | 
			
		||||
 | 
			
		||||
@ -40,3 +40,22 @@ export const getRandomWeightedReward = (
 | 
			
		||||
    }
 | 
			
		||||
    return getRandomReward(resultPool);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getRandomWeightedReward2 = (
 | 
			
		||||
    pool: { type: string; itemCount: number; rarity: TRarity }[],
 | 
			
		||||
    weights: Record<TRarity, number>
 | 
			
		||||
): IRngResult | undefined => {
 | 
			
		||||
    const resultPool: IRngResult[] = [];
 | 
			
		||||
    const rarityCounts: Record<TRarity, number> = { COMMON: 0, UNCOMMON: 0, RARE: 0, LEGENDARY: 0 };
 | 
			
		||||
    for (const entry of pool) {
 | 
			
		||||
        ++rarityCounts[entry.rarity];
 | 
			
		||||
    }
 | 
			
		||||
    for (const entry of pool) {
 | 
			
		||||
        resultPool.push({
 | 
			
		||||
            type: entry.type,
 | 
			
		||||
            itemCount: entry.itemCount,
 | 
			
		||||
            probability: weights[entry.rarity] / rarityCounts[entry.rarity]
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    return getRandomReward(resultPool);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user