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

XFESpaceNinjaServer

A simple server for a small space ninja game

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

XFEstudio/XFESpaceNinjaServer

feat: conquest progression & rewards (#1791)

Closes #1570 Co-authored-by: Jānis <janisslsm@noreply.localhost> Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1791 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>

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

代码差异

7 个文件 +243 -19
Modified package-lock.json +4 -4
@@ -18,7 +18,7 @@
18 18 "morgan": "^1.10.0",
19 19 "ncp": "^2.0.0",
20 20 "typescript": "^5.5",
21 "warframe-public-export-plus": "^0.5.56",
21 "warframe-public-export-plus": "^0.5.57",
22 22 "warframe-riven-info": "^0.1.2",
23 23 "winston": "^3.17.0",
24 24 "winston-daily-rotate-file": "^5.0.0"
@@ -3789,9 +3789,9 @@
3789 3789 }
3790 3790 },
3791 3791 "node_modules/warframe-public-export-plus": {
3792 "version": "0.5.56",
3793 "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.56.tgz",
3794 "integrity": "sha512-px+J7tUm6fkSzwKkvL73ySQReDq9oM1UrHSLM3vbYGBvELM892iBgPYG45okIhScCSdwmmXTiWZTf4x/I4qiNQ=="
3792 "version": "0.5.57",
3793 "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.57.tgz",
3794 "integrity": "sha512-CKbg7/2hSDH7I7yYSWwkrP4N2rEAEK1vNEuehj+RD9vMvl1c4u6klHLMwdh+ULxXiW4djWIlNIhs5bi/fm58Mg=="
3795 3795 },
3796 3796 "node_modules/warframe-riven-info": {
3797 3797 "version": "0.1.2",
Modified package.json +1 -1
@@ -25,7 +25,7 @@
25 25 "morgan": "^1.10.0",
26 26 "ncp": "^2.0.0",
27 27 "typescript": "^5.5",
28 "warframe-public-export-plus": "^0.5.56",
28 "warframe-public-export-plus": "^0.5.57",
29 29 "warframe-riven-info": "^0.1.2",
30 30 "winston": "^3.17.0",
31 31 "winston-daily-rotate-file": "^5.0.0"
Modified src/controllers/api/entratiLabConquestModeController.ts +2 -0
@@ -21,10 +21,12 @@ export const entratiLabConquestModeController: RequestHandler = async (req, res)
21 21 inventory.EntratiVaultCountResetDate = new Date(weekEnd);
22 22 if (inventory.EntratiLabConquestUnlocked) {
23 23 inventory.EntratiLabConquestUnlocked = 0;
24 inventory.EntratiLabConquestCacheScoreMission = 0;
24 25 inventory.EntratiLabConquestActiveFrameVariants = [];
25 26 }
26 27 if (inventory.EchoesHexConquestUnlocked) {
27 28 inventory.EchoesHexConquestUnlocked = 0;
29 inventory.EchoesHexConquestCacheScoreMission = 0;
28 30 inventory.EchoesHexConquestActiveFrameVariants = [];
29 31 inventory.EchoesHexConquestActiveStickers = [];
30 32 }
Modified src/controllers/api/missionInventoryUpdateController.ts +12 -4
@@ -6,6 +6,7 @@ import { addMissionInventoryUpdates, addMissionRewards } from "@/src/services/mi
6 6 import { getInventory } from "@/src/services/inventoryService";
7 7 import { getInventoryResponse } from "./inventoryController";
8 8 import { logger } from "@/src/utils/logger";
9 import { IMissionInventoryUpdateResponse } from "@/src/types/missionTypes";
9 10
10 11 /*
11 12 **** INPUT ****
@@ -71,8 +72,14 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
71 72 return;
72 73 }
73 74
74 const { MissionRewards, inventoryChanges, credits, AffiliationMods, SyndicateXPItemReward } =
75 await addMissionRewards(inventory, missionReport, firstCompletion);
75 const {
76 MissionRewards,
77 inventoryChanges,
78 credits,
79 AffiliationMods,
80 SyndicateXPItemReward,
81 ConquestCompletedMissionsCount
82 } = await addMissionRewards(inventory, missionReport, firstCompletion);
76 83
77 84 await inventory.save();
78 85 const inventoryResponse = await getInventoryResponse(inventory, true);
@@ -86,8 +93,9 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
86 93 ...inventoryUpdates,
87 94 //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.
88 95 SyndicateXPItemReward,
89 AffiliationMods
90 });
96 AffiliationMods,
97 ConquestCompletedMissionsCount
98 } satisfies IMissionInventoryUpdateResponse);
91 99 };
92 100
93 101 /*
Modified src/services/missionInventoryUpdateService.ts +198 -9
@@ -43,7 +43,7 @@ import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/invento
43 43 import { getEntriesUnsafe } from "@/src/utils/ts-utils";
44 44 import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
45 45 import { handleStoreItemAcquisition } from "./purchaseService";
46 import { IMissionReward } from "../types/missionTypes";
46 import { IMissionCredits, IMissionReward } from "../types/missionTypes";
47 47 import { crackRelic } from "@/src/helpers/relicHelper";
48 48 import { createMessage } from "./inboxService";
49 49 import kuriaMessage50 from "@/static/fixed_responses/kuriaMessages/fiftyPercent.json";
@@ -586,8 +586,140 @@ interface AddMissionRewardsReturnType {
586 586 credits?: IMissionCredits;
587 587 AffiliationMods?: IAffiliationMods[];
588 588 SyndicateXPItemReward?: number;
589 ConquestCompletedMissionsCount?: number;
589 590 }
590 591
592 interface IConquestReward {
593 at: number;
594 pool: IRngResult[];
595 }
596
597 const labConquestRewards: IConquestReward[] = [
598 {
599 at: 5,
600 pool: ExportRewards[
601 "/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestSilverRewards"
602 ][0] as IRngResult[]
603 },
604 {
605 at: 10,
606 pool: ExportRewards[
607 "/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestSilverRewards"
608 ][0] as IRngResult[]
609 },
610 {
611 at: 15,
612 pool: [
613 {
614 type: "/Lotus/StoreItems/Types/Gameplay/EntratiLab/Resources/EntratiLanthornBundle",
615 itemCount: 3,
616 probability: 1
617 }
618 ]
619 },
620 {
621 at: 20,
622 pool: ExportRewards[
623 "/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestGoldRewards"
624 ][0] as IRngResult[]
625 },
626 {
627 at: 28,
628 pool: [
629 {
630 type: "/Lotus/StoreItems/Types/Items/MiscItems/DistillPoints",
631 itemCount: 20,
632 probability: 1
633 }
634 ]
635 },
636 {
637 at: 31,
638 pool: ExportRewards[
639 "/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestGoldRewards"
640 ][0] as IRngResult[]
641 },
642 {
643 at: 34,
644 pool: ExportRewards[
645 "/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestArcaneRewards"
646 ][0] as IRngResult[]
647 },
648 {
649 at: 37,
650 pool: [
651 {
652 type: "/Lotus/StoreItems/Types/Items/MiscItems/DistillPoints",
653 itemCount: 50,
654 probability: 1
655 }
656 ]
657 }
658 ];
659
660 const hexConquestRewards: IConquestReward[] = [
661 {
662 at: 5,
663 pool: ExportRewards[
664 "/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestSilverRewards"
665 ][0] as IRngResult[]
666 },
667 {
668 at: 10,
669 pool: ExportRewards[
670 "/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestSilverRewards"
671 ][0] as IRngResult[]
672 },
673 {
674 at: 15,
675 pool: [
676 {
677 type: "/Lotus/StoreItems/Types/BoosterPacks/1999StickersPackEchoesArchimedea",
678 itemCount: 1,
679 probability: 1
680 }
681 ]
682 },
683 {
684 at: 20,
685 pool: ExportRewards[
686 "/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestGoldRewards"
687 ][0] as IRngResult[]
688 },
689 {
690 at: 28,
691 pool: [
692 {
693 type: "/Lotus/StoreItems/Types/Items/MiscItems/1999ConquestBucks",
694 itemCount: 6,
695 probability: 1
696 }
697 ]
698 },
699 {
700 at: 31,
701 pool: ExportRewards[
702 "/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestGoldRewards"
703 ][0] as IRngResult[]
704 },
705 {
706 at: 34,
707 pool: ExportRewards[
708 "/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestArcaneRewards"
709 ][0] as IRngResult[]
710 },
711 {
712 at: 37,
713 pool: [
714 {
715 type: "/Lotus/StoreItems/Types/Items/MiscItems/1999ConquestBucks",
716 itemCount: 9,
717 probability: 1
718 }
719 ]
720 }
721 ];
722
591 723 //TODO: return type of partial missioninventoryupdate response
592 724 export const addMissionRewards = async (
593 725 inventory: TInventoryDatabaseDocument,
@@ -620,6 +752,7 @@ export const addMissionRewards = async (
620 752 const inventoryChanges: IInventoryChanges = {};
621 753 const AffiliationMods: IAffiliationMods[] = [];
622 754 let SyndicateXPItemReward;
755 let ConquestCompletedMissionsCount;
623 756
624 757 let missionCompletionCredits = 0;
625 758 //inventory change is what the client has not rewarded itself, also the client needs to know the credit changes for display
@@ -691,6 +824,62 @@ export const addMissionRewards = async (
691 824 });
692 825 }
693 826
827 if (rewardInfo.ConquestCompleted !== undefined) {
828 let score = 1;
829 if (rewardInfo.ConquestHardModeActive === 1) score += 3;
830
831 if (rewardInfo.ConquestPersonalModifiersActive !== undefined)
832 score += rewardInfo.ConquestPersonalModifiersActive;
833 if (rewardInfo.ConquestEquipmentSuggestionsFulfilled !== undefined)
834 score += rewardInfo.ConquestEquipmentSuggestionsFulfilled;
835
836 score *= rewardInfo.ConquestCompleted + 1;
837
838 if (rewardInfo.ConquestCompleted == 2 && rewardInfo.ConquestHardModeActive === 1) score += 1;
839
840 logger.debug(`completed conquest mission ${rewardInfo.ConquestCompleted + 1} for a score of ${score}`);
841
842 const conquestType = rewardInfo.ConquestType;
843 const conquestNode =
844 conquestType == "HexConquest" ? "EchoesHexConquestHardModeUnlocked" : "EntratiLabConquestHardModeUnlocked";
845 if (score >= 25 && inventory.NodeIntrosCompleted.indexOf(conquestNode) == -1)
846 inventory.NodeIntrosCompleted.push(conquestNode);
847
848 if (conquestType == "HexConquest") {
849 inventory.EchoesHexConquestCacheScoreMission ??= 0;
850 if (score > inventory.EchoesHexConquestCacheScoreMission) {
851 for (const reward of hexConquestRewards) {
852 if (score >= reward.at && inventory.EchoesHexConquestCacheScoreMission < reward.at) {
853 const rolled = getRandomReward(reward.pool)!;
854 logger.debug(`rolled hex conquest reward for reaching ${reward.at} points`, rolled);
855 MissionRewards.push({
856 StoreItem: rolled.type,
857 ItemCount: rolled.itemCount
858 });
859 }
860 }
861 inventory.EchoesHexConquestCacheScoreMission = score;
862 }
863 } else {
864 inventory.EntratiLabConquestCacheScoreMission ??= 0;
865 if (score > inventory.EntratiLabConquestCacheScoreMission) {
866 for (const reward of labConquestRewards) {
867 if (score >= reward.at && inventory.EntratiLabConquestCacheScoreMission < reward.at) {
868 const rolled = getRandomReward(reward.pool)!;
869 logger.debug(`rolled lab conquest reward for reaching ${reward.at} points`, rolled);
870 MissionRewards.push({
871 StoreItem: rolled.type,
872 ItemCount: rolled.itemCount
873 });
874 }
875 }
876 inventory.EntratiLabConquestCacheScoreMission = score;
877 }
878 }
879
880 ConquestCompletedMissionsCount = rewardInfo.ConquestCompleted == 2 ? 0 : rewardInfo.ConquestCompleted + 1;
881 }
882
694 883 for (const reward of MissionRewards) {
695 884 const inventoryChange = await handleStoreItemAcquisition(
696 885 reward.StoreItem,
@@ -882,16 +1071,16 @@ export const addMissionRewards = async (
882 1071 }
883 1072 }
884 1073
885 return { inventoryChanges, MissionRewards, credits, AffiliationMods, SyndicateXPItemReward };
1074 return {
1075 inventoryChanges,
1076 MissionRewards,
1077 credits,
1078 AffiliationMods,
1079 SyndicateXPItemReward,
1080 ConquestCompletedMissionsCount
1081 };
886 1082 };
887 1083
888 interface IMissionCredits {
889 MissionCredits: number[];
890 CreditBonus: number[];
891 TotalCredits: number[];
892 DailyMissionBonus?: boolean;
893 }
894
895 1084 //creditBonus is not entirely accurate.
896 1085 //TODO: consider ActiveBoosters
897 1086 export const addCredits = (
Modified src/types/missionTypes.ts +19 -0
@@ -1,3 +1,5 @@
1 import { IAffiliationMods, IInventoryChanges } from "./purchaseTypes";
2
1 3 export const inventoryFields = ["RawUpgrades", "MiscItems", "Consumables", "Recipes"] as const;
2 4 export type IInventoryFieldType = (typeof inventoryFields)[number];
3 5
@@ -11,3 +13,20 @@ export interface IMissionReward {
11 13 FromEnemyCache?: boolean;
12 14 IsStrippedItem?: boolean;
13 15 }
16
17 export interface IMissionCredits {
18 MissionCredits: number[];
19 CreditBonus: number[];
20 TotalCredits: number[];
21 DailyMissionBonus?: boolean;
22 }
23
24 export interface IMissionInventoryUpdateResponse extends Partial<IMissionCredits> {
25 ConquestCompletedMissionsCount?: number;
26 InventoryJson?: string;
27 MissionRewards?: IMissionReward[];
28 InventoryChanges?: IInventoryChanges;
29 FusionPoints?: number;
30 SyndicateXPItemReward?: number;
31 AffiliationMods?: IAffiliationMods[];
32 }
Modified src/types/requestTypes.ts +7 -1
@@ -125,6 +125,7 @@ export type IMissionInventoryUpdateRequest = {
125 125 wagerTier?: number; // the index
126 126 creditsFee?: number; // the index
127 127 InvasionProgress?: IInvasionProgressClient[];
128 ConquestMissionsCompleted?: number;
128 129 } & {
129 130 [K in TEquipmentKey]?: IEquipmentClient[];
130 131 };
@@ -150,7 +151,12 @@ export interface IRewardInfo {
150 151 PurgatoryRewardQualifications?: string;
151 152 rewardSeed?: number | bigint;
152 153 periodicMissionTag?: string;
153
154 ConquestType?: string;
155 ConquestCompleted?: number;
156 ConquestEquipmentSuggestionsFulfilled?: number;
157 ConquestPersonalModifiersActive?: number;
158 ConquestStickersActive?: number;
159 ConquestHardModeActive?: number;
154 160 // for bounties, only EOM_AFK and node are given from above, plus:
155 161 JobTier?: number;
156 162 jobId?: string;