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: tutorial and natural new player experience (#983)

Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/983 Co-authored-by: Ordis <134585663+OrdisPrime@users.noreply.github.com> Co-committed-by: Ordis <134585663+OrdisPrime@users.noreply.github.com>

bf7fd421
Ordis <134585663+OrdisPrime@users.noreply.github.com>
提交于

代码差异

12 个文件 +257 -144
Modified package-lock.json +3 -3
@@ -4093,9 +4093,9 @@
4093 4093 }
4094 4094 },
4095 4095 "node_modules/warframe-public-export-plus": {
4096 "version": "0.5.30",
4097 "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.30.tgz",
4098 "integrity": "sha512-vzs+naEqp3iFZTbgIky4jiNbjNIovuR4oSimrFiuyIbrnfTlfXFzDfzT0hG2rgS8yEXBAbOcv2Zfm3fmWuZ0Kg=="
4096 "version": "0.5.32",
4097 "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.32.tgz",
4098 "integrity": "sha512-jO9i2Gzz9DWibiHlEO17D975ajs6KrTay8cS5I0GkUUe1XWVU8mML4b+IYCHzM4FWq1t6p2YPCGznQfknqvorg=="
4099 4099 },
4100 4100 "node_modules/warframe-riven-info": {
4101 4101 "version": "0.1.2",
Modified src/controllers/api/getNewRewardSeedController.ts +2 -4
@@ -1,13 +1,11 @@
1 1 import { RequestHandler } from "express";
2 2
3 const getNewRewardSeedController: RequestHandler = (_req, res) => {
3 export const getNewRewardSeedController: RequestHandler = (_req, res) => {
4 4 res.json({ rewardSeed: generateRewardSeed() });
5 5 };
6 6
7 function generateRewardSeed(): number {
7 export function generateRewardSeed(): number {
8 8 const min = -Number.MAX_SAFE_INTEGER;
9 9 const max = Number.MAX_SAFE_INTEGER;
10 10 return Math.floor(Math.random() * (max - min + 1)) + min;
11 11 }
12
13 export { getNewRewardSeedController };
Added src/controllers/api/giveStartingGearController.ts +96 -0
@@ -0,0 +1,96 @@
1 import { getJSONfromString } from "@/src/helpers/stringHelpers";
2 import { InventoryDocumentProps } from "@/src/models/inventoryModels/inventoryModel";
3 import {
4 addEquipment,
5 addItem,
6 combineInventoryChanges,
7 getInventory,
8 updateSlots
9 } from "@/src/services/inventoryService";
10 import { getAccountIdForRequest } from "@/src/services/loginService";
11 import { IInventoryClient, IInventoryDatabase, InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
12 import { IInventoryChanges } from "@/src/types/purchaseTypes";
13 import { RequestHandler } from "express";
14 import { HydratedDocument } from "mongoose";
15
16 type TPartialStartingGear = Pick<IInventoryClient, "LongGuns" | "Suits" | "Pistols" | "Melee">;
17
18 export const giveStartingGearController: RequestHandler = async (req, res) => {
19 const accountId = await getAccountIdForRequest(req);
20 const startingGear = getJSONfromString<TPartialStartingGear>(String(req.body));
21 const inventory = await getInventory(accountId);
22
23 const inventoryChanges = await addStartingGear(inventory, startingGear);
24 await inventory.save();
25
26 res.send(inventoryChanges);
27 };
28
29 //TODO: RawUpgrades might need to return a LastAdded
30 const awakeningRewards = [
31 "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem1",
32 "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem2",
33 "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem3",
34 "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem4",
35 "/Lotus/Types/Restoratives/LisetAutoHack",
36 "/Lotus/Upgrades/Mods/Warframe/AvatarShieldMaxMod"
37 ];
38
39 export const addStartingGear = async (
40 inventory: HydratedDocument<IInventoryDatabase, InventoryDocumentProps>,
41 startingGear: TPartialStartingGear | undefined = undefined
42 ) => {
43 const { LongGuns, Pistols, Suits, Melee } = startingGear || {
44 LongGuns: [{ ItemType: "/Lotus/Weapons/Tenno/Rifle/Rifle" }],
45 Pistols: [{ ItemType: "/Lotus/Weapons/Tenno/Pistol/Pistol" }],
46 Suits: [{ ItemType: "/Lotus/Powersuits/Excalibur/Excalibur" }],
47 Melee: [{ ItemType: "/Lotus/Weapons/Tenno/Melee/LongSword/LongSword" }]
48 };
49
50 //TODO: properly merge weapon bin changes it is currently static here
51 const inventoryChanges: IInventoryChanges = {};
52 addEquipment(inventory, "LongGuns", LongGuns[0].ItemType, undefined, inventoryChanges);
53 addEquipment(inventory, "Pistols", Pistols[0].ItemType, undefined, inventoryChanges);
54 addEquipment(inventory, "Melee", Melee[0].ItemType, undefined, inventoryChanges);
55 addEquipment(inventory, "Suits", Suits[0].ItemType, undefined, inventoryChanges, { Configs: Suits[0].Configs });
56 addEquipment(
57 inventory,
58 "DataKnives",
59 "/Lotus/Weapons/Tenno/HackingDevices/TnHackingDevice/TnHackingDeviceWeapon",
60 undefined,
61 inventoryChanges,
62 { XP: 450_000 }
63 );
64 addEquipment(
65 inventory,
66 "Scoops",
67 "/Lotus/Weapons/Tenno/Speedball/SpeedballWeaponTest",
68 undefined,
69 inventoryChanges
70 );
71
72 updateSlots(inventory, InventorySlot.SUITS, 0, 1);
73 updateSlots(inventory, InventorySlot.WEAPONS, 0, 3);
74 inventoryChanges.SuitBin = { count: 1, platinum: 0, Slots: -1 };
75 inventoryChanges.WeaponBin = { count: 3, platinum: 0, Slots: -3 };
76
77 await addItem(inventory, "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain");
78 inventory.ActiveQuest = "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain";
79
80 inventory.PremiumCredits = 50;
81 inventory.PremiumCreditsFree = 50;
82 inventoryChanges.PremiumCredits = 50;
83 inventoryChanges.PremiumCreditsFree = 50;
84 inventory.RegularCredits = 3000;
85 inventoryChanges.RegularCredits = 3000;
86
87 for (const item of awakeningRewards) {
88 const inventoryDelta = await addItem(inventory, item);
89 combineInventoryChanges(inventoryChanges, inventoryDelta.InventoryChanges);
90 }
91
92 inventory.PlayedParkourTutorial = true;
93 inventory.ReceivedStartingGear = true;
94
95 return inventoryChanges;
96 };
Modified src/controllers/custom/manageQuestsController.ts +0 -3
@@ -63,8 +63,6 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
63 63 inventory.ArchwingEnabled = true;
64 64 }
65 65 }
66
67 inventory.ActiveQuest = "";
68 66 break;
69 67 }
70 68 case "ResetAll": {
@@ -103,7 +101,6 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
103 101 inventory.ArchwingEnabled = true;
104 102 }
105 103 }
106 inventory.ActiveQuest = "";
107 104 break;
108 105 }
109 106 case "giveAll": {
Modified src/models/inventoryModels/inventoryModel.ts +61 -12
@@ -64,7 +64,11 @@ import {
64 64 IKubrowPetEggClient,
65 65 ICustomMarkers,
66 66 IMarkerInfo,
67 IMarker
67 IMarker,
68 ICalendarProgress,
69 IPendingCouponDatabase,
70 IPendingCouponClient,
71 ILibraryAvailableDailyTaskInfo
68 72 } from "../../types/inventoryTypes/inventoryTypes";
69 73 import { IOid } from "../../types/commonTypes";
70 74 import {
@@ -323,7 +327,7 @@ MailboxSchema.set("toJSON", {
323 327 const DuviriInfoSchema = new Schema<IDuviriInfo>(
324 328 {
325 329 Seed: Number,
326 NumCompletions: Number
330 NumCompletions: { type: Number, default: 0 }
327 331 },
328 332 {
329 333 _id: false,
@@ -435,6 +439,7 @@ const seasonChallengeHistorySchema = new Schema<ISeasonChallenge>(
435 439 //TODO: check whether this is complete
436 440 const playerSkillsSchema = new Schema<IPlayerSkills>(
437 441 {
442 LPP_NONE: { type: Number, default: 0 },
438 443 LPP_SPACE: { type: Number, default: 0 },
439 444 LPS_PILOTING: { type: Number, default: 0 },
440 445 LPS_GUNNERY: { type: Number, default: 0 },
@@ -891,19 +896,63 @@ const CustomMarkersSchema = new Schema<ICustomMarkers>(
891 896 { _id: false }
892 897 );
893 898
899 const calenderProgressSchema = new Schema<ICalendarProgress>(
900 {
901 Version: { type: Number, default: 19 },
902 Iteration: { type: Number, default: 2 },
903 YearProgress: {
904 Upgrades: { type: [] }
905 },
906 SeasonProgress: {
907 SeasonType: String,
908 LastCompletedDayIdx: { type: Number, default: -1 },
909 LastCompletedChallengeDayIdx: { type: Number, default: -1 },
910 ActivatedChallenges: []
911 }
912 },
913 { _id: false }
914 );
915
916 const pendingCouponSchema = new Schema<IPendingCouponDatabase>(
917 {
918 Expiry: { type: Date, default: new Date(0) },
919 Discount: { type: Number, default: 0 }
920 },
921 { _id: false }
922 );
923
924 pendingCouponSchema.set("toJSON", {
925 transform(_doc, ret, _options) {
926 (ret as IPendingCouponClient).Expiry = toMongoDate((ret as IPendingCouponDatabase).Expiry);
927 }
928 });
929
930 const libraryAvailableDailyTaskInfoSchema = new Schema<ILibraryAvailableDailyTaskInfo>(
931 {
932 EnemyTypes: [String],
933 EnemyLocTag: String,
934 EnemyIcon: String,
935 ScansRequired: Number,
936 RewardStoreItem: String,
937 RewardQuantity: Number,
938 RewardStanding: Number
939 },
940 { _id: false }
941 );
942
894 943 const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
895 944 {
896 945 accountOwnerId: Schema.Types.ObjectId,
897 SubscribedToEmails: Number,
898 Created: Date,
946 SubscribedToEmails: { type: Number, default: 0 },
947 SubscribedToEmailsPersonalized: { type: Number, default: 0 },
899 948 RewardSeed: Number,
900 949
901 950 //Credit
902 951 RegularCredits: { type: Number, default: 0 },
903 952 //Platinum
904 PremiumCredits: { type: Number, default: 50 },
953 PremiumCredits: { type: Number, default: 0 },
905 954 //Gift Platinum(Non trade)
906 PremiumCreditsFree: { type: Number, default: 50 },
955 PremiumCreditsFree: { type: Number, default: 0 },
907 956 //Endo
908 957 FusionPoints: { type: Number, default: 0 },
909 958 //Regal Aya
@@ -911,7 +960,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
911 960
912 961 //Slots
913 962 SuitBin: { type: slotsBinSchema, default: { Slots: 3 } },
914 WeaponBin: { type: slotsBinSchema, default: { Slots: 10 } },
963 WeaponBin: { type: slotsBinSchema, default: { Slots: 11 } },
915 964 SentinelBin: { type: slotsBinSchema, default: { Slots: 10 } },
916 965 SpaceSuitBin: { type: slotsBinSchema, default: { Slots: 4 } },
917 966 SpaceWeaponBin: { type: slotsBinSchema, default: { Slots: 4 } },
@@ -1022,7 +1071,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
1022 1071 //Complete Mission\Quests
1023 1072 Missions: [missionSchema],
1024 1073 QuestKeys: [questKeysSchema],
1025 ActiveQuest: { type: String, default: "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain" }, //TODO: check after mission starting gear
1074 ActiveQuest: { type: String, default: "" },
1026 1075 //item like DojoKey or Boss missions key
1027 1076 LevelKeys: [Schema.Types.Mixed],
1028 1077 //Active quests
@@ -1137,7 +1186,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
1137 1186 //Cephalon Simaris Entries Example:"TargetType"+"Scans"(1-10)+"Completed": true|false
1138 1187 LibraryPersonalProgress: [Schema.Types.Mixed],
1139 1188 //Cephalon Simaris Daily Task
1140 LibraryAvailableDailyTaskInfo: Schema.Types.Mixed,
1189 LibraryAvailableDailyTaskInfo: libraryAvailableDailyTaskInfoSchema,
1141 1190
1142 1191 //https://warframe.fandom.com/wiki/Invasion
1143 1192 InvasionChainProgress: [Schema.Types.Mixed],
@@ -1184,7 +1233,6 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
1184 1233 HandlerPoints: Number,
1185 1234 ChallengesFixVersion: Number,
1186 1235 PlayedParkourTutorial: Boolean,
1187 SubscribedToEmailsPersonalized: Number,
1188 1236 ActiveLandscapeTraps: [Schema.Types.Mixed],
1189 1237 RepVotes: [Schema.Types.Mixed],
1190 1238 LeagueTickets: [Schema.Types.Mixed],
@@ -1202,7 +1250,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
1202 1250 HasResetAccount: { type: Boolean, default: false },
1203 1251
1204 1252 //Discount Coupon
1205 PendingCoupon: Schema.Types.Mixed,
1253 PendingCoupon: pendingCouponSchema,
1206 1254 //Like BossAladV,BossCaptainVor come for you on missions % chance
1207 1255 DeathMarks: [String],
1208 1256 //Zanuka
@@ -1212,7 +1260,8 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
1212 1260
1213 1261 EndlessXP: { type: [endlessXpProgressSchema], default: undefined },
1214 1262
1215 DialogueHistory: dialogueHistorySchema
1263 DialogueHistory: dialogueHistorySchema,
1264 CalendarProgress: calenderProgressSchema
1216 1265 },
1217 1266 { timestamps: { createdAt: "Created", updatedAt: false } }
1218 1267 );
Modified src/routes/api.ts +2 -0
@@ -87,6 +87,7 @@ import { updateSessionGetController, updateSessionPostController } from "@/src/c
87 87 import { updateThemeController } from "../controllers/api/updateThemeController";
88 88 import { upgradesController } from "@/src/controllers/api/upgradesController";
89 89 import { saveSettingsController } from "../controllers/api/saveSettingsController";
90 import { giveStartingGearController } from "@/src/controllers/api/giveStartingGearController";
90 91
91 92 const apiRouter = express.Router();
92 93
@@ -146,6 +147,7 @@ apiRouter.post("/gildWeapon.php", gildWeaponController);
146 147 apiRouter.post("/giveKeyChainTriggeredItems.php", giveKeyChainTriggeredItemsController);
147 148 apiRouter.post("/giveKeyChainTriggeredMessage.php", giveKeyChainTriggeredMessageController);
148 149 apiRouter.post("/giveQuestKeyReward.php", giveQuestKeyRewardController);
150 apiRouter.post("/giveStartingGear.php", giveStartingGearController);
149 151 apiRouter.post("/guildTech.php", guildTechController);
150 152 apiRouter.post("/hostSession.php", hostSessionController);
151 153 apiRouter.post("/infestedFoundry.php", infestedFoundryController);
Modified src/services/inboxService.ts +2 -2
@@ -2,7 +2,7 @@ import { IMessageDatabase, Inbox } from "@/src/models/inboxModel";
2 2 import { getAccountForRequest } from "@/src/services/loginService";
3 3 import { HydratedDocument } from "mongoose";
4 4 import { Request } from "express";
5 import messages from "@/static/fixed_responses/messages.json";
5 import eventMessages from "@/static/fixed_responses/eventMessages.json";
6 6 import { logger } from "@/src/utils/logger";
7 7
8 8 export const getAllMessagesSorted = async (accountId: string): Promise<HydratedDocument<IMessageDatabase>[]> => {
@@ -32,7 +32,7 @@ export const createNewEventMessages = async (req: Request) => {
32 32 const latestEventMessageDate = account.LatestEventMessageDate;
33 33
34 34 //TODO: is baroo there? create these kind of messages too (periodical messages)
35 const newEventMessages = messages.Messages.filter(m => new Date(m.eventMessageDate) > latestEventMessageDate);
35 const newEventMessages = eventMessages.Messages.filter(m => new Date(m.eventMessageDate) > latestEventMessageDate);
36 36
37 37 if (newEventMessages.length === 0) {
38 38 logger.debug(`No new event messages. Latest event message date: ${latestEventMessageDate.toISOString()}`);
Modified src/services/inventoryService.ts +53 -68
@@ -22,7 +22,9 @@ import {
22 22 IDailyAffiliations,
23 23 IInventoryDatabase,
24 24 IKubrowPetEggDatabase,
25 IKubrowPetEggClient
25 IKubrowPetEggClient,
26 ILibraryAvailableDailyTaskInfo,
27 ICalendarProgress
26 28 } from "@/src/types/inventoryTypes/inventoryTypes";
27 29 import { IGenericUpdate } from "../types/genericUpdate";
28 30 import {
@@ -32,7 +34,7 @@ import {
32 34 } from "../types/requestTypes";
33 35 import { logger } from "@/src/utils/logger";
34 36 import { getExalted, getKeyChainItems } from "@/src/services/itemDataService";
35 import { IEquipmentClient, IItemConfig } from "../types/inventoryTypes/commonInventoryTypes";
37 import { IEquipmentClient, IEquipmentDatabase, IItemConfig } from "../types/inventoryTypes/commonInventoryTypes";
36 38 import {
37 39 ExportArcanes,
38 40 ExportCustoms,
@@ -51,6 +53,9 @@ import { createShip } from "./shipService";
51 53 import { creditBundles, fusionBundles } from "@/src/services/missionInventoryUpdateService";
52 54 import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
53 55 import { toOid } from "../helpers/inventoryHelpers";
56 import { generateRewardSeed } from "@/src/controllers/api/getNewRewardSeedController";
57 import { addStartingGear } from "@/src/controllers/api/giveStartingGearController";
58 import { completeQuest } from "@/src/services/questService";
54 59
55 60 export const createInventory = async (
56 61 accountOwnerId: Types.ObjectId,
@@ -65,65 +70,18 @@ export const createInventory = async (
65 70 ReceivedStartingGear: config.skipTutorial
66 71 });
67 72
68 if (config.skipTutorial) {
69 const defaultEquipment = [
70 // Awakening rewards
71 { ItemCount: 1, ItemType: "/Lotus/Powersuits/Excalibur/Excalibur" },
72 { ItemCount: 1, ItemType: "/Lotus/Weapons/Tenno/Melee/LongSword/LongSword" },
73 { ItemCount: 1, ItemType: "/Lotus/Weapons/Tenno/Pistol/Pistol" },
74 { ItemCount: 1, ItemType: "/Lotus/Weapons/Tenno/Rifle/Rifle" },
75 { ItemCount: 1, ItemType: "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem1" },
76 { ItemCount: 1, ItemType: "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem2" },
77 { ItemCount: 1, ItemType: "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem3" },
78 { ItemCount: 1, ItemType: "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem4" },
79 { ItemCount: 1, ItemType: "/Lotus/Types/Restoratives/LisetAutoHack" }
80 ];
81
82 // const vorsPrizeRewards = [
83 // // Vor's Prize rewards
84 // { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarHealthMaxMod" },
85 // { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarShieldMaxMod" },
86 // { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarAbilityRangeMod" },
87 // { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarAbilityStrengthMod" },
88 // { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarAbilityDurationMod" },
89 // { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarPickupBonusMod" },
90 // { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarPowerMaxMod" },
91 // { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarEnemyRadarMod" },
92 // { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Melee/WeaponFireRateMod" },
93 // { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Melee/WeaponMeleeDamageMod" },
94 // { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Rifle/WeaponFactionDamageCorpus" },
95 // { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Rifle/WeaponFactionDamageGrineer" },
96 // { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Rifle/WeaponDamageAmountMod" },
97 // { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Pistol/WeaponFireDamageMod" },
98 // { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Pistol/WeaponElectricityDamageMod" },
99 // { ItemCount: 1, ItemType: "/Lotus/Upgrades/Mods/Pistol/WeaponDamageAmountMod" },
100 // { ItemCount: 1, ItemType: "/Lotus/Types/Recipes/Weapons/BurstonRifleBlueprint" },
101 // { ItemCount: 1, ItemType: "/Lotus/Types/Items/MiscItems/Morphic" },
102 // { ItemCount: 400, ItemType: "/Lotus/Types/Items/MiscItems/PolymerBundle" },
103 // { ItemCount: 150, ItemType: "/Lotus/Types/Items/MiscItems/AlloyPlate" }
104 // ];
105 for (const equipment of defaultEquipment) {
106 await addItem(inventory, equipment.ItemType, equipment.ItemCount);
107 }
108
109 // Missing in Public Export
110 inventory.Horses.push({
111 ItemType: "/Lotus/Types/NeutralCreatures/ErsatzHorse/ErsatzHorsePowerSuit"
112 });
113 inventory.DataKnives.push({
114 ItemType: "/Lotus/Weapons/Tenno/HackingDevices/TnHackingDevice/TnHackingDeviceWeapon",
115 XP: 450000
116 });
117 inventory.Scoops.push({
118 ItemType: "/Lotus/Weapons/Tenno/Speedball/SpeedballWeaponTest"
119 });
120 inventory.DrifterMelee.push({
121 ItemType: "/Lotus/Types/Friendly/PlayerControllable/Weapons/DuviriDualSwords"
122 });
73 inventory.LibraryAvailableDailyTaskInfo = createLibraryAvailableDailyTaskInfo();
74 inventory.CalendarProgress = createCalendar();
75 inventory.RewardSeed = generateRewardSeed();
76 inventory.DuviriInfo = {
77 Seed: generateRewardSeed(),
78 NumCompletions: 0
79 };
80 await addItem(inventory, "/Lotus/Types/Friendly/PlayerControllable/Weapons/DuviriDualSwords");
123 81
124 inventory.QuestKeys.push({
125 ItemType: "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain"
126 });
82 if (config.skipTutorial) {
83 await addStartingGear(inventory);
84 await completeQuest(inventory, "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain");
127 85
128 86 const completedMissions = ["SolNode27", "SolNode89", "SolNode63", "SolNode85", "SolNode15", "SolNode79"];
129 87
@@ -133,14 +91,11 @@ export const createInventory = async (
133 91 Tag: tag
134 92 }))
135 93 );
136
137 inventory.RegularCredits = 25000;
138 inventory.FusionPoints = 180;
139 94 }
140 95
141 96 await inventory.save();
142 97 } catch (error) {
143 throw new Error(`Error creating inventory: ${error instanceof Error ? error.message : "Unknown error"}`);
98 throw new Error(`Error creating inventory: ${error instanceof Error ? error.message : "Unknown error type"}`);
144 99 }
145 100 };
146 101
@@ -150,6 +105,7 @@ export const createInventory = async (
150 105 * @param InventoryChanges - will hold the combined changes
151 106 * @param delta - inventory changes to be added
152 107 */
108 //TODO: this fails silently when providing an incorrect object to delta
153 109 export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, delta: IInventoryChanges): void => {
154 110 for (const key in delta) {
155 111 if (!(key in InventoryChanges)) {
@@ -779,15 +735,19 @@ export const addEquipment = (
779 735 category: TEquipmentKey,
780 736 type: string,
781 737 modularParts: string[] | undefined = undefined,
782 inventoryChanges: IInventoryChanges = {}
738 inventoryChanges: IInventoryChanges = {},
739 defaultOverwrites: Partial<IEquipmentDatabase> | undefined = undefined
783 740 ): IInventoryChanges => {
784 const index =
785 inventory[category].push({
741 const equipment = Object.assign(
742 {
786 743 ItemType: type,
787 744 Configs: [],
788 745 XP: 0,
789 746 ModularParts: modularParts
790 }) - 1;
747 },
748 defaultOverwrites
749 );
750 const index = inventory[category].push(equipment) - 1;
791 751
792 752 inventoryChanges[category] ??= [];
793 753 inventoryChanges[category].push(inventory[category][index].toJSON<IEquipmentClient>());
@@ -1173,3 +1133,28 @@ export const addKeyChainItems = async (
1173 1133
1174 1134 return inventoryChanges;
1175 1135 };
1136 const createLibraryAvailableDailyTaskInfo = (): ILibraryAvailableDailyTaskInfo => {
1137 return {
1138 EnemyTypes: ["/Lotus/Types/Enemies/Orokin/RifleLancerAvatar"],
1139 EnemyLocTag: "/Lotus/Language/Game/CorruptedLancer",
1140 EnemyIcon: "/Lotus/Interface/Icons/Npcs/OrokinRifleLancerAvatar.png",
1141 ScansRequired: 3,
1142 RewardStoreItem: "/Lotus/StoreItems/Upgrades/Mods/FusionBundles/UncommonFusionBundle",
1143 RewardQuantity: 7,
1144 RewardStanding: 7500
1145 };
1146 };
1147
1148 const createCalendar = (): ICalendarProgress => {
1149 return {
1150 Version: 19,
1151 Iteration: 2,
1152 YearProgress: { Upgrades: [] },
1153 SeasonProgress: {
1154 SeasonType: "CST_SPRING",
1155 LastCompletedDayIdx: -1,
1156 LastCompletedChallengeDayIdx: -1,
1157 ActivatedChallenges: []
1158 }
1159 };
1160 };
Modified src/services/questService.ts +2 -1
@@ -126,7 +126,8 @@ export const completeQuest = async (inventory: TInventoryDatabaseDocument, quest
126 126 });
127 127 }
128 128 }
129 //TODO: handle quest completions
129 inventory.ActiveQuest = "";
130 //TODO: handle quest completion items
130 131 };
131 132
132 133 export const giveKeyChainItem = async (inventory: TInventoryDatabaseDocument, keyChainInfo: IKeyChainRequest) => {
Modified src/types/inventoryTypes/inventoryTypes.ts +24 -2
@@ -38,6 +38,7 @@ export interface IInventoryDatabase
38 38 | "InfestedFoundry"
39 39 | "DialogueHistory"
40 40 | "KubrowPetEggs"
41 | "PendingCoupon"
41 42 | TEquipmentKey
42 43 >,
43 44 InventoryDatabaseEquipment {
@@ -61,6 +62,7 @@ export interface IInventoryDatabase
61 62 InfestedFoundry?: IInfestedFoundryDatabase;
62 63 DialogueHistory?: IDialogueHistoryDatabase;
63 64 KubrowPetEggs?: IKubrowPetEggDatabase[];
65 PendingCoupon: IPendingCouponDatabase;
64 66 }
65 67
66 68 export interface IQuestKeyDatabase {
@@ -318,11 +320,12 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
318 320 CollectibleSeries: ICollectibleSery[];
319 321 LibraryAvailableDailyTaskInfo: ILibraryAvailableDailyTaskInfo;
320 322 HasResetAccount: boolean;
321 PendingCoupon: IPendingCoupon;
323 PendingCoupon: IPendingCouponClient;
322 324 Harvestable: boolean;
323 325 DeathSquadable: boolean;
324 326 EndlessXP?: IEndlessXpProgress[];
325 327 DialogueHistory?: IDialogueHistoryClient;
328 CalendarProgress: ICalendarProgress;
326 329 }
327 330
328 331 export interface IAffiliation {
@@ -759,7 +762,12 @@ export enum Manifest {
759 762 LotusTypesGameNemesisKuvaLichKuvaLichManifestVersionTwo = "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionTwo"
760 763 }
761 764
762 export interface IPendingCoupon {
765 export interface IPendingCouponDatabase {
766 Expiry: Date;
767 Discount: number;
768 }
769
770 export interface IPendingCouponClient {
763 771 Expiry: IMongoDate;
764 772 Discount: number;
765 773 }
@@ -858,6 +866,7 @@ export interface IPersonalTechProject {
858 866 }
859 867
860 868 export interface IPlayerSkills {
869 LPP_NONE: number;
861 870 LPP_SPACE: number;
862 871 LPS_PILOTING: number;
863 872 LPS_GUNNERY: number;
@@ -1042,3 +1051,16 @@ export interface IMarker {
1042 1051 z: number;
1043 1052 showInHud: boolean;
1044 1053 }
1054 export interface ISeasonProgress {
1055 SeasonType: "CST_UNDEFINED" | "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL";
1056 LastCompletedDayIdx: number;
1057 LastCompletedChallengeDayIdx: number;
1058 ActivatedChallenges: unknown[];
1059 }
1060
1061 export interface ICalendarProgress {
1062 Version: number;
1063 Iteration: number;
1064 YearProgress: { Upgrades: unknown[] };
1065 SeasonProgress: ISeasonProgress;
1066 }
Added static/fixed_responses/eventMessages.json +12 -0
@@ -0,0 +1,12 @@
1 {
2 "Messages": [
3 {
4 "sub": "Welcome to Space Ninja Server",
5 "sndr": "/Lotus/Language/Bosses/Ordis",
6 "msg": "Enjoy your Space Ninja Experience",
7 "icon": "/Lotus/Interface/Icons/Npcs/Darvo.png",
8 "eventMessageDate": "2025-01-30T13:00:00.000Z",
9 "r": false
10 }
11 ]
12 }
Deleted static/fixed_responses/messages.json +0 -49
@@ -1,49 +0,0 @@
1 {
2 "Messages": [
3 {
4 "sub": "Welcome to Space Ninja Server",
5 "sndr": "/Lotus/Language/Bosses/Ordis",
6 "msg": "Enjoy your Space Ninja Experience",
7 "icon": "/Lotus/Interface/Icons/Npcs/Darvo.png",
8 "eventMessageDate": "2025-01-30T13:00:00.000Z",
9 "r": false
10 },
11 {
12 "sub": "/Lotus/Language/Inbox/DarvoWeaponCraftingMessageBTitle",
13 "sndr": "/Lotus/Language/Bosses/Darvo",
14 "msg": "/Lotus/Language/Inbox/DarvoWeaponCraftingMessageBDesc",
15 "icon": "/Lotus/Interface/Icons/Npcs/Darvo.png",
16 "countedAtt": [
17 {
18 "ItemCount": 1,
19 "ItemType": "/Lotus/Types/Recipes/Weapons/BurstonRifleBlueprint"
20 },
21 {
22 "ItemCount": 1,
23 "ItemType": "/Lotus/Types/Items/MiscItems/Morphic"
24 },
25 {
26 "ItemCount": 400,
27 "ItemType": "/Lotus/Types/Items/MiscItems/PolymerBundle"
28 },
29 {
30 "ItemCount": 150,
31 "ItemType": "/Lotus/Types/Items/MiscItems/AlloyPlate"
32 }
33 ],
34 "highPriority": true,
35 "eventMessageDate": "2023-10-01T17:00:00.000Z",
36 "r": false
37 },
38 {
39 "sub": "/Lotus/Language/G1Quests/Beginner_Growth_Inbox_Title",
40 "sndr": "/Lotus/Language/Menu/Mailbox_WarframeSender",
41 "msg": "/Lotus/Language/G1Quests/Beginner_Growth_Inbox_Desc",
42 "icon": "/Lotus/Interface/Icons/Npcs/Lotus_d.png",
43 "transmission": "/Lotus/Sounds/Dialog/VorsPrize/DLisetPostAssassinate110Lotus",
44 "highPriority": true,
45 "eventMessageDate": "2023-09-01T17:00:00.000Z",
46 "r": false
47 }
48 ]
49 }