feat: credit boosters (+ daily first win) #2324
@ -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
|
||||||
);
|
);
|
||||||
|
@ -968,6 +968,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,
|
||||||
@ -1015,13 +1016,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({
|
||||||
@ -1050,12 +1055,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") {
|
||||||
@ -1165,7 +1169,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
|
||||||
@ -1388,48 +1394,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2275,14 +2275,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