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