forked from OpenWF/SpaceNinjaServer
		
	feat: credit boosters (+ daily first win) (#2324)
Daily first win is kinda weird because the client doesn't even seem to acknowledge it. Also fixed missionCompletionCredits being added to inventory inconsistently (sometimes once, sometimes twice). Closes #1086 Closes #2322 Reviewed-on: OpenWF/SpaceNinjaServer#2324 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
							
								
									690b872b5e
								
							
						
					
					
						commit
						4895b4630b
					
				@ -1,16 +1,12 @@
 | 
				
			|||||||
 | 
					import { getAccountForRequest } from "@/src/services/loginService";
 | 
				
			||||||
import { RequestHandler } from "express";
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const checkDailyMissionBonusController: RequestHandler = (_req, res) => {
 | 
					export const checkDailyMissionBonusController: RequestHandler = async (req, res) => {
 | 
				
			||||||
    const data = Buffer.from([
 | 
					    const account = await getAccountForRequest(req);
 | 
				
			||||||
        0x44, 0x61, 0x69, 0x6c, 0x79, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x6f, 0x6e, 0x75, 0x73, 0x3a,
 | 
					    const today = Math.trunc(Date.now() / 86400000) * 86400;
 | 
				
			||||||
        0x31, 0x2d, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x50, 0x56, 0x50, 0x57, 0x69, 0x6e, 0x42, 0x6f, 0x6e, 0x75, 0x73,
 | 
					    if (account.DailyFirstWinDate != today) {
 | 
				
			||||||
        0x3a, 0x31, 0x0a
 | 
					        res.send("DailyMissionBonus:1-DailyPVPWinBonus:1\n");
 | 
				
			||||||
    ]);
 | 
					    } else {
 | 
				
			||||||
    res.writeHead(200, {
 | 
					        res.send("DailyMissionBonus:0-DailyPVPWinBonus:1\n");
 | 
				
			||||||
        "Content-Type": "text/html",
 | 
					    }
 | 
				
			||||||
        "Content-Length": data.length
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    res.end(data);
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
export { checkDailyMissionBonusController };
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -88,7 +88,7 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
 | 
				
			|||||||
        AffiliationMods,
 | 
					        AffiliationMods,
 | 
				
			||||||
        SyndicateXPItemReward,
 | 
					        SyndicateXPItemReward,
 | 
				
			||||||
        ConquestCompletedMissionsCount
 | 
					        ConquestCompletedMissionsCount
 | 
				
			||||||
    } = await addMissionRewards(inventory, missionReport, firstCompletion);
 | 
					    } = await addMissionRewards(account, inventory, missionReport, firstCompletion);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (missionReport.EndOfMatchUpload) {
 | 
					    if (missionReport.EndOfMatchUpload) {
 | 
				
			||||||
        inventory.RewardSeed = generateRewardSeed();
 | 
					        inventory.RewardSeed = generateRewardSeed();
 | 
				
			||||||
 | 
				
			|||||||
@ -26,7 +26,7 @@ export const completeAllMissionsController: RequestHandler = async (req, res) =>
 | 
				
			|||||||
        if (mission.Completes == 0) {
 | 
					        if (mission.Completes == 0) {
 | 
				
			||||||
            mission.Completes++;
 | 
					            mission.Completes++;
 | 
				
			||||||
            if (node.missionReward) {
 | 
					            if (node.missionReward) {
 | 
				
			||||||
                addFixedLevelRewards(node.missionReward, inventory, MissionRewards);
 | 
					                addFixedLevelRewards(node.missionReward, MissionRewards);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        mission.Tier = 1;
 | 
					        mission.Tier = 1;
 | 
				
			||||||
 | 
				
			|||||||
@ -23,9 +23,9 @@ export const setBoosterController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
        res.status(400).send("Invalid ItemType provided.");
 | 
					        res.status(400).send("Invalid ItemType provided.");
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const now = Math.floor(Date.now() / 1000);
 | 
					    const now = Math.trunc(Date.now() / 1000);
 | 
				
			||||||
    for (const { ItemType, ExpiryDate } of requests) {
 | 
					    for (const { ItemType, ExpiryDate } of requests) {
 | 
				
			||||||
        if (ExpiryDate < now) {
 | 
					        if (ExpiryDate <= now) {
 | 
				
			||||||
            // remove expired boosters
 | 
					            // remove expired boosters
 | 
				
			||||||
            const index = boosters.findIndex(item => item.ItemType === ItemType);
 | 
					            const index = boosters.findIndex(item => item.ItemType === ItemType);
 | 
				
			||||||
            if (index !== -1) {
 | 
					            if (index !== -1) {
 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,8 @@ const databaseAccountSchema = new Schema<IDatabaseAccountJson>(
 | 
				
			|||||||
        LastLogin: { type: Date, default: 0 },
 | 
					        LastLogin: { type: Date, default: 0 },
 | 
				
			||||||
        LatestEventMessageDate: { type: Date, default: 0 },
 | 
					        LatestEventMessageDate: { type: Date, default: 0 },
 | 
				
			||||||
        LastLoginRewardDate: { type: Number, default: 0 },
 | 
					        LastLoginRewardDate: { type: Number, default: 0 },
 | 
				
			||||||
        LoginDays: { type: Number, default: 1 }
 | 
					        LoginDays: { type: Number, default: 1 },
 | 
				
			||||||
 | 
					        DailyFirstWinDate: { type: Number, default: 0 }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    opts
 | 
					    opts
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
				
			|||||||
@ -962,6 +962,7 @@ const droptableAliases: Record<string, string> = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
//TODO: return type of partial missioninventoryupdate response
 | 
					//TODO: return type of partial missioninventoryupdate response
 | 
				
			||||||
export const addMissionRewards = async (
 | 
					export const addMissionRewards = async (
 | 
				
			||||||
 | 
					    account: TAccountDocument,
 | 
				
			||||||
    inventory: TInventoryDatabaseDocument,
 | 
					    inventory: TInventoryDatabaseDocument,
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        wagerTier: wagerTier,
 | 
					        wagerTier: wagerTier,
 | 
				
			||||||
@ -1009,13 +1010,17 @@ export const addMissionRewards = async (
 | 
				
			|||||||
        const fixedLevelRewards = getLevelKeyRewards(levelKeyName);
 | 
					        const fixedLevelRewards = getLevelKeyRewards(levelKeyName);
 | 
				
			||||||
        //logger.debug(`fixedLevelRewards ${fixedLevelRewards}`);
 | 
					        //logger.debug(`fixedLevelRewards ${fixedLevelRewards}`);
 | 
				
			||||||
        if (fixedLevelRewards.levelKeyRewards) {
 | 
					        if (fixedLevelRewards.levelKeyRewards) {
 | 
				
			||||||
            addFixedLevelRewards(fixedLevelRewards.levelKeyRewards, inventory, MissionRewards, rewardInfo);
 | 
					            missionCompletionCredits += addFixedLevelRewards(
 | 
				
			||||||
 | 
					                fixedLevelRewards.levelKeyRewards,
 | 
				
			||||||
 | 
					                MissionRewards,
 | 
				
			||||||
 | 
					                rewardInfo
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (fixedLevelRewards.levelKeyRewards2) {
 | 
					        if (fixedLevelRewards.levelKeyRewards2) {
 | 
				
			||||||
            for (const reward of fixedLevelRewards.levelKeyRewards2) {
 | 
					            for (const reward of fixedLevelRewards.levelKeyRewards2) {
 | 
				
			||||||
                //quest stage completion credit rewards
 | 
					                //quest stage completion credit rewards
 | 
				
			||||||
                if (reward.rewardType == "RT_CREDITS") {
 | 
					                if (reward.rewardType == "RT_CREDITS") {
 | 
				
			||||||
                    missionCompletionCredits += reward.amount; // will be added to inventory in addCredits
 | 
					                    missionCompletionCredits += reward.amount;
 | 
				
			||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                MissionRewards.push({
 | 
					                MissionRewards.push({
 | 
				
			||||||
@ -1044,12 +1049,11 @@ export const addMissionRewards = async (
 | 
				
			|||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            const levelCreditReward = getLevelCreditRewards(node);
 | 
					            const levelCreditReward = getLevelCreditRewards(node);
 | 
				
			||||||
            missionCompletionCredits += levelCreditReward;
 | 
					            missionCompletionCredits += levelCreditReward;
 | 
				
			||||||
            inventory.RegularCredits += levelCreditReward;
 | 
					 | 
				
			||||||
            logger.debug(`levelCreditReward ${levelCreditReward}`);
 | 
					            logger.debug(`levelCreditReward ${levelCreditReward}`);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (node.missionReward) {
 | 
					        if (node.missionReward) {
 | 
				
			||||||
            missionCompletionCredits += addFixedLevelRewards(node.missionReward, inventory, MissionRewards, rewardInfo);
 | 
					            missionCompletionCredits += addFixedLevelRewards(node.missionReward, MissionRewards, rewardInfo);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (rewardInfo.sortieTag == "Mission1") {
 | 
					        if (rewardInfo.sortieTag == "Mission1") {
 | 
				
			||||||
@ -1159,7 +1163,9 @@ export const addMissionRewards = async (
 | 
				
			|||||||
        combineInventoryChanges(inventoryChanges, inventoryChange.InventoryChanges);
 | 
					        combineInventoryChanges(inventoryChanges, inventoryChange.InventoryChanges);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const credits = addCredits(inventory, {
 | 
					    inventory.RegularCredits += missionCompletionCredits;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const credits = await addCredits(account, inventory, {
 | 
				
			||||||
        missionCompletionCredits,
 | 
					        missionCompletionCredits,
 | 
				
			||||||
        missionDropCredits: creditDrops ?? 0,
 | 
					        missionDropCredits: creditDrops ?? 0,
 | 
				
			||||||
        rngRewardCredits: inventoryChanges.RegularCredits ?? 0
 | 
					        rngRewardCredits: inventoryChanges.RegularCredits ?? 0
 | 
				
			||||||
@ -1382,48 +1388,61 @@ export const addMissionRewards = async (
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//creditBonus is not entirely accurate.
 | 
					export const addCredits = async (
 | 
				
			||||||
//TODO: consider ActiveBoosters
 | 
					    account: TAccountDocument,
 | 
				
			||||||
export const addCredits = (
 | 
					 | 
				
			||||||
    inventory: TInventoryDatabaseDocument,
 | 
					    inventory: TInventoryDatabaseDocument,
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        missionDropCredits,
 | 
					        missionDropCredits,
 | 
				
			||||||
        missionCompletionCredits,
 | 
					        missionCompletionCredits,
 | 
				
			||||||
        rngRewardCredits
 | 
					        rngRewardCredits
 | 
				
			||||||
    }: { missionDropCredits: number; missionCompletionCredits: number; rngRewardCredits: number }
 | 
					    }: { missionDropCredits: number; missionCompletionCredits: number; rngRewardCredits: number }
 | 
				
			||||||
): IMissionCredits => {
 | 
					): Promise<IMissionCredits> => {
 | 
				
			||||||
    const hasDailyCreditBonus = true;
 | 
					 | 
				
			||||||
    const totalCredits = missionDropCredits + missionCompletionCredits + rngRewardCredits;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const finalCredits: IMissionCredits = {
 | 
					    const finalCredits: IMissionCredits = {
 | 
				
			||||||
        MissionCredits: [missionDropCredits, missionDropCredits],
 | 
					        MissionCredits: [missionDropCredits, missionDropCredits],
 | 
				
			||||||
        CreditBonus: [missionCompletionCredits, missionCompletionCredits],
 | 
					        CreditsBonus: [missionCompletionCredits, missionCompletionCredits],
 | 
				
			||||||
        TotalCredits: [totalCredits, totalCredits]
 | 
					        TotalCredits: [0, 0]
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (hasDailyCreditBonus) {
 | 
					    const today = Math.trunc(Date.now() / 86400000) * 86400;
 | 
				
			||||||
 | 
					    if (account.DailyFirstWinDate != today) {
 | 
				
			||||||
 | 
					        account.DailyFirstWinDate = today;
 | 
				
			||||||
 | 
					        await account.save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        logger.debug(`daily first win, doubling missionCompletionCredits (${missionCompletionCredits})`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        finalCredits.DailyMissionBonus = true;
 | 
				
			||||||
        inventory.RegularCredits += missionCompletionCredits;
 | 
					        inventory.RegularCredits += missionCompletionCredits;
 | 
				
			||||||
        finalCredits.CreditBonus[1] *= 2;
 | 
					        finalCredits.CreditsBonus[1] *= 2;
 | 
				
			||||||
        finalCredits.MissionCredits[1] *= 2;
 | 
					 | 
				
			||||||
        finalCredits.TotalCredits[1] *= 2;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!hasDailyCreditBonus) {
 | 
					    const totalCredits = finalCredits.MissionCredits[1] + finalCredits.CreditsBonus[1] + rngRewardCredits;
 | 
				
			||||||
        return finalCredits;
 | 
					    finalCredits.TotalCredits = [totalCredits, totalCredits];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (config.worldState?.creditBoost) {
 | 
				
			||||||
 | 
					        inventory.RegularCredits += finalCredits.TotalCredits[1];
 | 
				
			||||||
 | 
					        finalCredits.TotalCredits[1] += finalCredits.TotalCredits[1];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return { ...finalCredits, DailyMissionBonus: true };
 | 
					    const now = Math.trunc(Date.now() / 1000); // TOVERIFY: Should we maybe subtract mission time as to apply credit boosters that expired during mission?
 | 
				
			||||||
 | 
					    if ((inventory.Boosters.find(x => x.ItemType == "/Lotus/Types/Boosters/CreditBooster")?.ExpiryDate ?? 0) > now) {
 | 
				
			||||||
 | 
					        inventory.RegularCredits += finalCredits.TotalCredits[1];
 | 
				
			||||||
 | 
					        finalCredits.TotalCredits[1] += finalCredits.TotalCredits[1];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if ((inventory.Boosters.find(x => x.ItemType == "/Lotus/Types/Boosters/CreditBlessing")?.ExpiryDate ?? 0) > now) {
 | 
				
			||||||
 | 
					        inventory.RegularCredits += finalCredits.TotalCredits[1];
 | 
				
			||||||
 | 
					        finalCredits.TotalCredits[1] += finalCredits.TotalCredits[1];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return finalCredits;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const addFixedLevelRewards = (
 | 
					export const addFixedLevelRewards = (
 | 
				
			||||||
    rewards: IMissionRewardExternal,
 | 
					    rewards: IMissionRewardExternal,
 | 
				
			||||||
    inventory: TInventoryDatabaseDocument,
 | 
					 | 
				
			||||||
    MissionRewards: IMissionReward[],
 | 
					    MissionRewards: IMissionReward[],
 | 
				
			||||||
    rewardInfo?: IRewardInfo
 | 
					    rewardInfo?: IRewardInfo
 | 
				
			||||||
): number => {
 | 
					): number => {
 | 
				
			||||||
    let missionBonusCredits = 0;
 | 
					    let missionBonusCredits = 0;
 | 
				
			||||||
    if (rewards.credits) {
 | 
					    if (rewards.credits) {
 | 
				
			||||||
        missionBonusCredits += rewards.credits;
 | 
					        missionBonusCredits += rewards.credits;
 | 
				
			||||||
        inventory.RegularCredits += rewards.credits;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (rewards.items) {
 | 
					    if (rewards.items) {
 | 
				
			||||||
        for (const item of rewards.items) {
 | 
					        for (const item of rewards.items) {
 | 
				
			||||||
 | 
				
			|||||||
@ -331,7 +331,7 @@ export const giveKeyChainMissionReward = async (
 | 
				
			|||||||
            const fixedLevelRewards = getLevelKeyRewards(missionName);
 | 
					            const fixedLevelRewards = getLevelKeyRewards(missionName);
 | 
				
			||||||
            if (fixedLevelRewards.levelKeyRewards) {
 | 
					            if (fixedLevelRewards.levelKeyRewards) {
 | 
				
			||||||
                const missionRewards: { StoreItem: string; ItemCount: number }[] = [];
 | 
					                const missionRewards: { StoreItem: string; ItemCount: number }[] = [];
 | 
				
			||||||
                addFixedLevelRewards(fixedLevelRewards.levelKeyRewards, inventory, missionRewards);
 | 
					                inventory.RegularCredits += addFixedLevelRewards(fixedLevelRewards.levelKeyRewards, missionRewards);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                for (const reward of missionRewards) {
 | 
					                for (const reward of missionRewards) {
 | 
				
			||||||
                    await addItem(inventory, fromStoreItem(reward.StoreItem), reward.ItemCount);
 | 
					                    await addItem(inventory, fromStoreItem(reward.StoreItem), reward.ItemCount);
 | 
				
			||||||
 | 
				
			|||||||
@ -25,6 +25,7 @@ export interface IDatabaseAccount extends IDatabaseAccountRequiredFields {
 | 
				
			|||||||
    LatestEventMessageDate: Date;
 | 
					    LatestEventMessageDate: Date;
 | 
				
			||||||
    LastLoginRewardDate: number;
 | 
					    LastLoginRewardDate: number;
 | 
				
			||||||
    LoginDays: number;
 | 
					    LoginDays: number;
 | 
				
			||||||
 | 
					    DailyFirstWinDate: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Includes virtual ID
 | 
					// Includes virtual ID
 | 
				
			||||||
 | 
				
			|||||||
@ -17,9 +17,9 @@ export interface IMissionReward {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IMissionCredits {
 | 
					export interface IMissionCredits {
 | 
				
			||||||
    MissionCredits: number[];
 | 
					    MissionCredits: [number, number];
 | 
				
			||||||
    CreditBonus: number[];
 | 
					    CreditsBonus: [number, number]; // "Credit Reward"; `CreditsBonus[1]` is `CreditsBonus[0] * 2` if DailyMissionBonus
 | 
				
			||||||
    TotalCredits: number[];
 | 
					    TotalCredits: [number, number];
 | 
				
			||||||
    DailyMissionBonus?: boolean;
 | 
					    DailyMissionBonus?: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2289,14 +2289,13 @@ function doAcquireBoosters() {
 | 
				
			|||||||
    const ExpiryDate = Date.now() / 1000 + 3 * 24 * 60 * 60; // default 3 days
 | 
					    const ExpiryDate = Date.now() / 1000 + 3 * 24 * 60 * 60; // default 3 days
 | 
				
			||||||
    setBooster(uniqueName, ExpiryDate, () => {
 | 
					    setBooster(uniqueName, ExpiryDate, () => {
 | 
				
			||||||
        $("#acquire-type-Boosters").val("");
 | 
					        $("#acquire-type-Boosters").val("");
 | 
				
			||||||
        updateInventory();
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function doChangeBoosterExpiry(ItemType, ExpiryDateInput) {
 | 
					function doChangeBoosterExpiry(ItemType, ExpiryDateInput) {
 | 
				
			||||||
    console.log("Changing booster expiry for", ItemType, "to", ExpiryDateInput.value);
 | 
					    console.log("Changing booster expiry for", ItemType, "to", ExpiryDateInput.value);
 | 
				
			||||||
    // cast local datetime string to unix timestamp
 | 
					    // cast local datetime string to unix timestamp
 | 
				
			||||||
    const ExpiryDate = new Date(ExpiryDateInput.value).getTime() / 1000;
 | 
					    const ExpiryDate = Math.trunc(new Date(ExpiryDateInput.value).getTime() / 1000);
 | 
				
			||||||
    if (isNaN(ExpiryDate)) {
 | 
					    if (isNaN(ExpiryDate)) {
 | 
				
			||||||
        ExpiryDateInput.addClass("is-invalid").focus();
 | 
					        ExpiryDateInput.addClass("is-invalid").focus();
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user