feat: gardening #1849
							
								
								
									
										84
									
								
								src/controllers/api/gardeningController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/controllers/api/gardeningController.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,84 @@
 | 
			
		||||
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
 | 
			
		||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
			
		||||
import { addMiscItem, getInventory } from "@/src/services/inventoryService";
 | 
			
		||||
import { toStoreItem } from "@/src/services/itemDataService";
 | 
			
		||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
			
		||||
import { createGarden, getPersonalRooms } from "@/src/services/personalRoomsService";
 | 
			
		||||
import { IMongoDate } from "@/src/types/commonTypes";
 | 
			
		||||
import { IMissionReward } from "@/src/types/missionTypes";
 | 
			
		||||
import { IPersonalRoomsClient } from "@/src/types/personalRoomsTypes";
 | 
			
		||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
 | 
			
		||||
import { IGardeningClient } from "@/src/types/shipTypes";
 | 
			
		||||
import { RequestHandler } from "express";
 | 
			
		||||
import { dict_en, ExportResources } from "warframe-public-export-plus";
 | 
			
		||||
 | 
			
		||||
export const gardeningController: RequestHandler = async (req, res) => {
 | 
			
		||||
    const data = getJSONfromString<IGardeningRequest>(String(req.body));
 | 
			
		||||
    if (data.Mode != "HarvestAll") {
 | 
			
		||||
        throw new Error(`unexpected gardening mode: ${data.Mode}`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const accountId = await getAccountIdForRequest(req);
 | 
			
		||||
    const [inventory, personalRooms] = await Promise.all([
 | 
			
		||||
        getInventory(accountId, "MiscItems"),
 | 
			
		||||
        getPersonalRooms(accountId, "Apartment")
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    // Harvest plants
 | 
			
		||||
    const inventoryChanges: IInventoryChanges = {};
 | 
			
		||||
    const rewards: Record<string, IMissionReward[][]> = {};
 | 
			
		||||
    for (const planter of personalRooms.Apartment.Gardening.Planters) {
 | 
			
		||||
        rewards[planter.Name] = [];
 | 
			
		||||
        for (const plant of planter.Plants) {
 | 
			
		||||
            const itemType =
 | 
			
		||||
                "/Lotus/Types/Gameplay/Duviri/Resource/DuviriPlantItem" +
 | 
			
		||||
                plant.PlantType.substring(plant.PlantType.length - 1);
 | 
			
		||||
            const itemCount = Math.random() < 0.775 ? 2 : 4;
 | 
			
		||||
 | 
			
		||||
            addMiscItem(inventory, itemType, itemCount, inventoryChanges);
 | 
			
		||||
 | 
			
		||||
            rewards[planter.Name].push([
 | 
			
		||||
                {
 | 
			
		||||
                    StoreItem: toStoreItem(itemType),
 | 
			
		||||
                    TypeName: itemType,
 | 
			
		||||
                    ItemCount: itemCount,
 | 
			
		||||
                    DailyCooldown: false,
 | 
			
		||||
                    Rarity: itemCount == 2 ? 0.7743589743589744 : 0.22564102564102564,
 | 
			
		||||
                    TweetText: `${itemCount}x ${dict_en[ExportResources[itemType].name]} (Resource)`,
 | 
			
		||||
                    ProductCategory: "MiscItems"
 | 
			
		||||
                }
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Refresh garden
 | 
			
		||||
    personalRooms.Apartment.Gardening = createGarden();
 | 
			
		||||
 | 
			
		||||
    await Promise.all([inventory.save(), personalRooms.save()]);
 | 
			
		||||
 | 
			
		||||
    const planter = personalRooms.Apartment.Gardening.Planters[personalRooms.Apartment.Gardening.Planters.length - 1];
 | 
			
		||||
    const plant = planter.Plants[planter.Plants.length - 1];
 | 
			
		||||
    res.json({
 | 
			
		||||
        GardenTagName: planter.Name,
 | 
			
		||||
        PlantType: plant.PlantType,
 | 
			
		||||
        PlotIndex: plant.PlotIndex,
 | 
			
		||||
        EndTime: toMongoDate(plant.EndTime),
 | 
			
		||||
        InventoryChanges: inventoryChanges,
 | 
			
		||||
        Gardening: personalRooms.toJSON<IPersonalRoomsClient>().Apartment.Gardening,
 | 
			
		||||
        Rewards: rewards
 | 
			
		||||
    } satisfies IGardeningResponse);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
interface IGardeningRequest {
 | 
			
		||||
    Mode: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IGardeningResponse {
 | 
			
		||||
    GardenTagName: string;
 | 
			
		||||
    PlantType: string;
 | 
			
		||||
    PlotIndex: number;
 | 
			
		||||
    EndTime: IMongoDate;
 | 
			
		||||
    InventoryChanges: IInventoryChanges;
 | 
			
		||||
    Gardening: IGardeningClient;
 | 
			
		||||
    Rewards: Record<string, IMissionReward[][]>;
 | 
			
		||||
}
 | 
			
		||||
@ -48,6 +48,7 @@ import { findSessionsController } from "@/src/controllers/api/findSessionsContro
 | 
			
		||||
import { fishmongerController } from "@/src/controllers/api/fishmongerController";
 | 
			
		||||
import { focusController } from "@/src/controllers/api/focusController";
 | 
			
		||||
import { fusionTreasuresController } from "@/src/controllers/api/fusionTreasuresController";
 | 
			
		||||
import { gardeningController } from "@/src/controllers/api/gardeningController";
 | 
			
		||||
import { genericUpdateController } from "@/src/controllers/api/genericUpdateController";
 | 
			
		||||
import { getAllianceController } from "@/src/controllers/api/getAllianceController";
 | 
			
		||||
import { getDailyDealStockLevelsController } from "@/src/controllers/api/getDailyDealStockLevelsController";
 | 
			
		||||
@ -240,6 +241,7 @@ apiRouter.post("/findSessions.php", findSessionsController);
 | 
			
		||||
apiRouter.post("/fishmonger.php", fishmongerController);
 | 
			
		||||
apiRouter.post("/focus.php", focusController);
 | 
			
		||||
apiRouter.post("/fusionTreasures.php", fusionTreasuresController);
 | 
			
		||||
apiRouter.post("/gardening.php", gardeningController);
 | 
			
		||||
apiRouter.post("/genericUpdate.php", genericUpdateController);
 | 
			
		||||
apiRouter.post("/getAlliance.php", getAllianceController);
 | 
			
		||||
apiRouter.post("/getFriends.php", getFriendsController);
 | 
			
		||||
 | 
			
		||||
@ -213,6 +213,15 @@ export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, del
 | 
			
		||||
    for (const key in delta) {
 | 
			
		||||
        if (!(key in InventoryChanges)) {
 | 
			
		||||
            InventoryChanges[key] = delta[key];
 | 
			
		||||
        } else if (key == "MiscItems") {
 | 
			
		||||
            for (const deltaItem of delta[key]!) {
 | 
			
		||||
                const existing = InventoryChanges[key]!.find(x => x.ItemType == deltaItem.ItemType);
 | 
			
		||||
                if (existing) {
 | 
			
		||||
                    existing.ItemCount += deltaItem.ItemCount;
 | 
			
		||||
                } else {
 | 
			
		||||
                    InventoryChanges[key]!.push(deltaItem);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else if (Array.isArray(delta[key])) {
 | 
			
		||||
            const left = InventoryChanges[key] as object[];
 | 
			
		||||
            const right: object[] = delta[key];
 | 
			
		||||
@ -1468,6 +1477,22 @@ export const addGearExpByCategory = (
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const addMiscItem = (
 | 
			
		||||
    inventory: TInventoryDatabaseDocument,
 | 
			
		||||
    type: string,
 | 
			
		||||
    count: number,
 | 
			
		||||
    inventoryChanges: IInventoryChanges
 | 
			
		||||
): void => {
 | 
			
		||||
    const miscItemChanges: IMiscItem[] = [
 | 
			
		||||
        {
 | 
			
		||||
            ItemType: type,
 | 
			
		||||
            ItemCount: count
 | 
			
		||||
        }
 | 
			
		||||
    ];
 | 
			
		||||
    addMiscItems(inventory, miscItemChanges);
 | 
			
		||||
    combineInventoryChanges(inventoryChanges, { MiscItems: miscItemChanges });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const addMiscItems = (inventory: TInventoryDatabaseDocument, itemsArray: IMiscItem[]): void => {
 | 
			
		||||
    const { MiscItems } = inventory;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,8 +4,11 @@ import { TPersonalRoomsDatabaseDocument } from "../types/personalRoomsTypes";
 | 
			
		||||
import { IGardeningDatabase } from "../types/shipTypes";
 | 
			
		||||
import { getRandomElement } from "./rngService";
 | 
			
		||||
 | 
			
		||||
export const getPersonalRooms = async (accountId: string): Promise<TPersonalRoomsDatabaseDocument> => {
 | 
			
		||||
    const personalRooms = await PersonalRooms.findOne({ personalRoomsOwnerId: accountId });
 | 
			
		||||
export const getPersonalRooms = async (
 | 
			
		||||
    accountId: string,
 | 
			
		||||
    projection?: string
 | 
			
		||||
): Promise<TPersonalRoomsDatabaseDocument> => {
 | 
			
		||||
    const personalRooms = await PersonalRooms.findOne({ personalRoomsOwnerId: accountId }, projection);
 | 
			
		||||
 | 
			
		||||
    if (!personalRooms) {
 | 
			
		||||
        throw new Error(`personal rooms not found for account ${accountId}`);
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,8 @@ export interface IMissionReward {
 | 
			
		||||
    TypeName?: string;
 | 
			
		||||
    UpgradeLevel?: number;
 | 
			
		||||
    ItemCount: number;
 | 
			
		||||
    DailyCooldown?: boolean;
 | 
			
		||||
    Rarity?: number;
 | 
			
		||||
    TweetText?: string;
 | 
			
		||||
    ProductCategory?: string;
 | 
			
		||||
    FromEnemyCache?: boolean;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user