XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

SpaceNinjaServer

A simple server for a small space ninja game

公开
关注 0 Fork 1 Star 0
返回提交历史

XFEstudio/SpaceNinjaServer

feat: nemesis credits tax (#3273)

Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/3273 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>

b7eed4a2
Sainan <63328889+Sainan@users.noreply.github.com>
提交于

代码差异

7 个文件 +117 -16
Modified src/controllers/api/missionInventoryUpdateController.ts +6 -2
@@ -97,7 +97,9 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
97 97 credits,
98 98 AffiliationMods,
99 99 SyndicateXPItemReward,
100 ConquestCompletedMissionsCount
100 ConquestCompletedMissionsCount,
101 NemesisTaxInfo,
102 RecoveredItemInfo
101 103 } = await addMissionRewards(account, inventory, missionReport, firstCompletion);
102 104 handleConservation(inventory, missionReport, AffiliationMods); // Conservation reports have GS_SUCCESS
103 105
@@ -115,7 +117,9 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
115 117 //FusionPoints: inventoryChanges?.FusionPoints, // This in combination with InventoryJson or InventoryChanges seems to just double the number of endo shown, so unsure when this is needed.
116 118 SyndicateXPItemReward,
117 119 AffiliationMods,
118 ConquestCompletedMissionsCount
120 ConquestCompletedMissionsCount,
121 NemesisTaxInfo,
122 RecoveredItemInfo
119 123 };
120 124 if (
121 125 missionReport.BMI ||
Modified src/helpers/general.ts +4 -0
@@ -70,3 +70,7 @@ export const isObject = (objectCandidate: unknown): objectCandidate is Record<st
70 70 !Array.isArray(objectCandidate)
71 71 );
72 72 };
73
74 export const lerp = (v0: number, v1: number, t: number): number => {
75 return v0 + t * (v1 - v0);
76 };
Modified src/helpers/nemesisHelpers.ts +19 -1
@@ -1,10 +1,12 @@
1 1 import { ExportRegions, ExportWarframes } from "warframe-public-export-plus";
2 import type { IInfNode, TNemesisFaction } from "../types/inventoryTypes/inventoryTypes.ts";
2 import type { IInfNode, INemesisDatabase, TNemesisFaction } from "../types/inventoryTypes/inventoryTypes.ts";
3 3 import { generateRewardSeed, getRewardAtPercentage, SRng } from "../services/rngService.ts";
4 4 import type { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel.ts";
5 5 import type { IOid } from "../types/commonTypes.ts";
6 6 import { isArchwingMission } from "../services/worldStateService.ts";
7 7 import gameToBuildVersion from "../constants/gameToBuildVersion.ts";
8 import type { INemesisTaxInfo } from "../types/missionTypes.ts";
9 import { lerp } from "./general.ts";
8 10
9 11 type TInnateDamageTag =
10 12 | "InnateElectricityDamage"
@@ -491,3 +493,19 @@ export const getInfestedLichItemRewards = (fp: bigint): string[] => {
491 493 const rotBReward = getRewardAtPercentage(infestedLichRotB, rng.randomFloat())!.type;
492 494 return [rotAReward, rotBReward];
493 495 };
496
497 export const getNemesisTaxInfo = (nemesis: INemesisDatabase): INemesisTaxInfo | undefined => {
498 if (nemesis.Faction == "FC_GRINEER") {
499 return {
500 TaxRate: nemesis.InfNodes.length * 0.007,
501 TaxCreditsOnly: false
502 };
503 }
504 if (nemesis.Faction == "FC_CORPUS") {
505 return {
506 TaxRate: lerp(0.5, 0.95, Math.min(nemesis.InfNodes.length / 10, 1)),
507 TaxCreditsOnly: true
508 };
509 }
510 return undefined;
511 };
Modified src/models/inventoryModels/inventoryModel.ts +2 -0
@@ -1879,6 +1879,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
1879 1879 Nemesis: nemesisSchema,
1880 1880 NemesisHistory: { type: [nemesisSchema], default: undefined },
1881 1881 LastNemesisAllySpawnTime: { type: Date, default: undefined },
1882 NemesisTaxedCredits: Number,
1882 1883
1883 1884 //TradingRulesConfirmed,ShowFriendInvNotifications(Option->Social)
1884 1885 Settings: settingsSchema,
@@ -1990,6 +1991,7 @@ inventorySchema.set("toJSON", {
1990 1991 delete returnedObject.MissionRelicRewards;
1991 1992 delete returnedObject.HarvesterPoints;
1992 1993 delete returnedObject.DeathSquadPoints;
1994 delete returnedObject.NemesisTaxedCredits;
1993 1995
1994 1996 const inventoryDatabase = returnedObject as Partial<IInventoryDatabase>;
1995 1997 const inventoryResponse = returnedObject as IInventoryClient;
Modified src/services/missionInventoryUpdateService.ts +44 -8
@@ -56,7 +56,7 @@ import { fromStoreItem, getLevelKeyRewards, isStoreItem, toStoreItem } from "./i
56 56 import type { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel.ts";
57 57 import { getEntriesUnsafe } from "../utils/ts-utils.ts";
58 58 import { handleStoreItemAcquisition } from "./purchaseService.ts";
59 import type { IMissionCredits, IMissionReward } from "../types/missionTypes.ts";
59 import type { IMissionCredits, IMissionReward, INemesisTaxInfo, IRecoveredItemInfo } from "../types/missionTypes.ts";
60 60 import { crackRelic } from "../helpers/relicHelper.ts";
61 61 import type { IMessageCreationTemplate } from "./inboxService.ts";
62 62 import { createMessage } from "./inboxService.ts";
@@ -69,7 +69,8 @@ import {
69 69 getInfNodes,
70 70 getKillTokenRewardCount,
71 71 getNemesisManifest,
72 getNemesisPasscode
72 getNemesisPasscode,
73 getNemesisTaxInfo
73 74 } from "../helpers/nemesisHelpers.ts";
74 75 import { Loadout } from "../models/inventoryModels/loadoutModel.ts";
75 76 import {
@@ -1026,6 +1027,8 @@ interface AddMissionRewardsReturnType {
1026 1027 AffiliationMods: IAffiliationMods[];
1027 1028 SyndicateXPItemReward?: number;
1028 1029 ConquestCompletedMissionsCount?: number;
1030 NemesisTaxInfo?: INemesisTaxInfo;
1031 RecoveredItemInfo?: IRecoveredItemInfo;
1029 1032 }
1030 1033
1031 1034 interface IConquestReward {
@@ -1189,6 +1192,7 @@ export const addMissionRewards = async (
1189 1192 {
1190 1193 wagerTier: wagerTier,
1191 1194 Nemesis: nemesis,
1195 NemesisKillConvert: NemesisKillConvert,
1192 1196 RewardInfo: rewardInfo,
1193 1197 LevelKeyName: levelKeyName,
1194 1198 Missions: missions,
@@ -1467,12 +1471,11 @@ export const addMissionRewards = async (
1467 1471 }
1468 1472 }
1469 1473
1474 let nodeControlledByNemesis = false;
1470 1475 if (inventory.Nemesis) {
1471 if (
1472 nemesis ||
1473 (inventory.Nemesis.Faction == "FC_INFESTATION" &&
1474 inventory.Nemesis.InfNodes.find(obj => obj.Node == rewardInfo.node))
1475 ) {
1476 nodeControlledByNemesis = inventory.Nemesis.InfNodes.some(obj => obj.Node == rewardInfo.node);
1477
1478 if (nemesis || (inventory.Nemesis.Faction == "FC_INFESTATION" && nodeControlledByNemesis)) {
1476 1479 inventoryChanges.Nemesis ??= {};
1477 1480 const nodeIndex = inventory.Nemesis.InfNodes.findIndex(obj => obj.Node === rewardInfo.node);
1478 1481 if (nodeIndex !== -1) inventory.Nemesis.InfNodes.splice(nodeIndex, 1);
@@ -1624,6 +1627,37 @@ export const addMissionRewards = async (
1624 1627 rngRewardCredits: inventoryChanges.RegularCredits ?? 0
1625 1628 });
1626 1629
1630 const NemesisTaxInfo: INemesisTaxInfo | undefined = nodeControlledByNemesis
1631 ? getNemesisTaxInfo(inventory.Nemesis!)
1632 : undefined;
1633 if (NemesisTaxInfo) {
1634 const taxedCredits = Math.round(credits.TotalCredits[0] * NemesisTaxInfo.TaxRate);
1635 NemesisTaxInfo.TaxedCredits = taxedCredits;
1636 inventory.RegularCredits -= taxedCredits;
1637 inventoryChanges.RegularCredits ??= 0;
1638 inventoryChanges.RegularCredits -= taxedCredits;
1639 credits.TotalCredits[1] -= taxedCredits;
1640 inventory.NemesisTaxedCredits ??= 0;
1641 inventory.NemesisTaxedCredits += taxedCredits;
1642 }
1643
1644 const RecoveredItemInfo: IRecoveredItemInfo | undefined = NemesisKillConvert
1645 ? { RecoveredMiscItems: [] }
1646 : undefined;
1647 if (RecoveredItemInfo) {
1648 if (inventory.NemesisTaxedCredits) {
1649 RecoveredItemInfo.RecoveredCredits = inventory.NemesisTaxedCredits;
1650 inventory.RegularCredits += inventory.NemesisTaxedCredits;
1651 inventoryChanges.RegularCredits ??= 0;
1652 inventoryChanges.RegularCredits += inventory.NemesisTaxedCredits;
1653 credits.TotalCredits[0] += inventory.NemesisTaxedCredits;
1654 credits.TotalCredits[1] += inventory.NemesisTaxedCredits;
1655 credits.CreditsBonus[0] += inventory.NemesisTaxedCredits;
1656 credits.CreditsBonus[1] += inventory.NemesisTaxedCredits;
1657 inventory.NemesisTaxedCredits = undefined;
1658 }
1659 }
1660
1627 1661 if (strippedItems) {
1628 1662 if (endOfMatchUpload) {
1629 1663 for (const si of strippedItems) {
@@ -1725,7 +1759,9 @@ export const addMissionRewards = async (
1725 1759 credits,
1726 1760 AffiliationMods,
1727 1761 SyndicateXPItemReward,
1728 ConquestCompletedMissionsCount
1762 ConquestCompletedMissionsCount,
1763 NemesisTaxInfo,
1764 RecoveredItemInfo
1729 1765 };
1730 1766 };
1731 1767
Modified src/types/inventoryTypes/inventoryTypes.ts +6 -3
@@ -142,7 +142,13 @@ export interface IInventoryDatabase
142 142 >,
143 143 InventoryDatabaseEquipment,
144 144 IAccountCheats {
145 // SNS-specific fields for server-side tracking
145 146 accountOwnerId: Types.ObjectId;
147 MissionRelicRewards?: ITypeCount[];
148 HarvesterPoints?: number;
149 DeathSquadPoints?: number;
150 NemesisTaxedCredits?: number;
151
146 152 Created: Date;
147 153 CurrentLoadOutIds: Types.ObjectId[] | IOid[]; // should be Types.ObjectId[] but might be IOid[] because of old commits
148 154 TrainingDate: Date;
@@ -183,7 +189,6 @@ export interface IInventoryDatabase
183 189 EndlessXP?: IEndlessXpProgressDatabase[];
184 190 DescentRewards?: IDescentCategoryRewardDatabase[];
185 191 PersonalGoalProgress?: IGoalProgressDatabase[];
186 MissionRelicRewards?: ITypeCount[];
187 192 FocusLoadouts?: IFocusLoadoutDatabase[];
188 193 ChallengeInstanceStates?: IChallengeInstanceStateDatabase[];
189 194 }
@@ -463,8 +468,6 @@ export interface IInventoryClient
463 468 PendingCoupon?: IPendingCouponClient;
464 469 Harvestable?: boolean;
465 470 DeathSquadable?: boolean;
466 HarvesterPoints?: number;
467 DeathSquadPoints?: number;
468 471 EndlessXP?: IEndlessXpProgressClient[];
469 472 DescentRewards?: IDescentCategoryRewardClient[];
470 473 DialogueHistory?: IDialogueHistoryClient;
Modified src/types/missionTypes.ts +36 -2
@@ -1,3 +1,5 @@
1 import type { IMongoDate, IOid, ITypeCount } from "./commonTypes.ts";
2 import type { INemesisBaseClient } from "./inventoryTypes/inventoryTypes.ts";
1 3 import type { IAffiliationMods, IInventoryChanges } from "./purchaseTypes.ts";
2 4
3 5 export interface IMissionReward {
@@ -16,17 +18,32 @@ export interface IMissionReward {
16 18 export interface IMissionCredits {
17 19 MissionCredits: [number, number];
18 20 CreditsBonus: [number, number]; // "Credit Reward"; `CreditsBonus[1]` is `CreditsBonus[0] * 2` if DailyMissionBonus
19 TotalCredits: [number, number];
21 TotalCredits: [number, number]; // TotalCredits[0] - TaxedCredits = TotalCredits[1]
20 22 DailyMissionBonus?: boolean;
21 23 }
22 24
23 export interface IMissionInventoryUpdateResponseRailjackInterstitial extends Partial<IMissionCredits> {
25 // These fields will only be respected by the client when completing a showdown mission.
26 export interface INemsisEndBody {
27 WeaponRecipes?: {
28 ItemType: string;
29 CompletionDate: IMongoDate;
30 TargetFingerprint: string;
31 ItemId: IOid;
32 }[]; // TODO: We should probably send this sometimes when defeating a nemesis?
33 RecoveredItemInfo?: IRecoveredItemInfo;
34 EphemeraReward?: string; // TODO: We should probably send this sometimes when defeating a nemesis?
35 BaitReward?: "/Lotus/StoreItems/Types/Restoratives/Consumable/NemesisBait"; // TODO: We should probably send this sometimes when defeating a nemesis?
36 history?: INemesisBaseClient; // TODO: We should probably send this sometimes when defeating a nemesis?
37 }
38
39 export interface IMissionInventoryUpdateResponseRailjackInterstitial extends Partial<IMissionCredits>, INemsisEndBody {
24 40 ConquestCompletedMissionsCount?: number;
25 41 MissionRewards?: IMissionReward[];
26 42 InventoryChanges?: IInventoryChanges;
27 43 FusionPoints?: number;
28 44 SyndicateXPItemReward?: number;
29 45 AffiliationMods?: IAffiliationMods[];
46 NemesisTaxInfo?: INemesisTaxInfo;
30 47 }
31 48
32 49 export interface IMissionInventoryUpdateResponse extends IMissionInventoryUpdateResponseRailjackInterstitial {
@@ -36,3 +53,20 @@ export interface IMissionInventoryUpdateResponse extends IMissionInventoryUpdate
36 53 export interface IMissionInventoryUpdateResponseBackToDryDock {
37 54 InventoryJson: string;
38 55 }
56
57 export interface INemesisTaxInfo {
58 TaxRate: number; // client errors if this field is not given
59 TaxCreditsOnly: boolean; // always given in the response on live, but not used by the client
60 TaxedCredits?: number;
61 TaxedFusionPoints?: number;
62 TaxedCrewShipFusionPoints?: number;
63 TaxedCollectedItems?: IMissionReward[]; // these must also be present in MissionRewards array
64 TaxedMiscItems?: ITypeCount[]; // can also contain upgrade types
65 }
66
67 export interface IRecoveredItemInfo {
68 RecoveredMiscItems: ITypeCount[];
69 RecoveredCredits?: number;
70 RecoveredFusionPoints?: number;
71 RecoveredCollectedItems?: ITypeCount[]; // not added to MissionRewards in this case
72 }