2023-03-03 16:23:46 +01:00
import { inject , injectable } from "tsyringe" ;
2023-10-19 19:21:17 +02:00
import { ScavCaseRewardGenerator } from "@spt-aki/generators/ScavCaseRewardGenerator" ;
import { HideoutHelper } from "@spt-aki/helpers/HideoutHelper" ;
import { InventoryHelper } from "@spt-aki/helpers/InventoryHelper" ;
import { PaymentHelper } from "@spt-aki/helpers/PaymentHelper" ;
import { PresetHelper } from "@spt-aki/helpers/PresetHelper" ;
import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper" ;
import { IPmcData } from "@spt-aki/models/eft/common/IPmcData" ;
import { HideoutArea , Product , Production , ScavCase } from "@spt-aki/models/eft/common/tables/IBotBase" ;
import { Upd } from "@spt-aki/models/eft/common/tables/IItem" ;
import { HideoutUpgradeCompleteRequestData } from "@spt-aki/models/eft/hideout/HideoutUpgradeCompleteRequestData" ;
import { IHandleQTEEventRequestData } from "@spt-aki/models/eft/hideout/IHandleQTEEventRequestData" ;
import { IHideoutArea , Stage } from "@spt-aki/models/eft/hideout/IHideoutArea" ;
2023-10-28 16:53:13 +02:00
import { IHideoutCancelProductionRequestData } from "@spt-aki/models/eft/hideout/IHideoutCancelProductionRequestData" ;
2023-10-19 19:21:17 +02:00
import { IHideoutContinuousProductionStartRequestData } from "@spt-aki/models/eft/hideout/IHideoutContinuousProductionStartRequestData" ;
import { IHideoutImproveAreaRequestData } from "@spt-aki/models/eft/hideout/IHideoutImproveAreaRequestData" ;
import { IHideoutProduction } from "@spt-aki/models/eft/hideout/IHideoutProduction" ;
import { IHideoutPutItemInRequestData } from "@spt-aki/models/eft/hideout/IHideoutPutItemInRequestData" ;
import { IHideoutScavCaseStartRequestData } from "@spt-aki/models/eft/hideout/IHideoutScavCaseStartRequestData" ;
import { IHideoutSingleProductionStartRequestData } from "@spt-aki/models/eft/hideout/IHideoutSingleProductionStartRequestData" ;
import { IHideoutTakeItemOutRequestData } from "@spt-aki/models/eft/hideout/IHideoutTakeItemOutRequestData" ;
import { IHideoutTakeProductionRequestData } from "@spt-aki/models/eft/hideout/IHideoutTakeProductionRequestData" ;
import { IHideoutToggleAreaRequestData } from "@spt-aki/models/eft/hideout/IHideoutToggleAreaRequestData" ;
import { IHideoutUpgradeRequestData } from "@spt-aki/models/eft/hideout/IHideoutUpgradeRequestData" ;
import { IQteData } from "@spt-aki/models/eft/hideout/IQteData" ;
import { IRecordShootingRangePoints } from "@spt-aki/models/eft/hideout/IRecordShootingRangePoints" ;
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse" ;
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes" ;
import { HideoutAreas } from "@spt-aki/models/enums/HideoutAreas" ;
import { SkillTypes } from "@spt-aki/models/enums/SkillTypes" ;
import { IHideoutConfig } from "@spt-aki/models/spt/config/IHideoutConfig" ;
import { ILogger } from "@spt-aki/models/spt/utils/ILogger" ;
import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder" ;
import { ConfigServer } from "@spt-aki/servers/ConfigServer" ;
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer" ;
import { SaveServer } from "@spt-aki/servers/SaveServer" ;
import { FenceService } from "@spt-aki/services/FenceService" ;
import { LocalisationService } from "@spt-aki/services/LocalisationService" ;
import { PlayerService } from "@spt-aki/services/PlayerService" ;
import { HashUtil } from "@spt-aki/utils/HashUtil" ;
import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil" ;
import { JsonUtil } from "@spt-aki/utils/JsonUtil" ;
import { RandomUtil } from "@spt-aki/utils/RandomUtil" ;
import { TimeUtil } from "@spt-aki/utils/TimeUtil" ;
2023-03-03 16:23:46 +01:00
@injectable ( )
export class HideoutController
{
2023-12-30 22:30:54 +01:00
protected static nameTaskConditionCountersCrafting = "CounterHoursCrafting" ;
2023-03-03 16:23:46 +01:00
protected hideoutConfig : IHideoutConfig ;
constructor (
@inject ( "WinstonLogger" ) protected logger : ILogger ,
@inject ( "HashUtil" ) protected hashUtil : HashUtil ,
@inject ( "TimeUtil" ) protected timeUtil : TimeUtil ,
@inject ( "DatabaseServer" ) protected databaseServer : DatabaseServer ,
@inject ( "RandomUtil" ) protected randomUtil : RandomUtil ,
@inject ( "InventoryHelper" ) protected inventoryHelper : InventoryHelper ,
@inject ( "SaveServer" ) protected saveServer : SaveServer ,
@inject ( "PlayerService" ) protected playerService : PlayerService ,
@inject ( "PresetHelper" ) protected presetHelper : PresetHelper ,
@inject ( "PaymentHelper" ) protected paymentHelper : PaymentHelper ,
@inject ( "EventOutputHolder" ) protected eventOutputHolder : EventOutputHolder ,
@inject ( "HttpResponseUtil" ) protected httpResponse : HttpResponseUtil ,
@inject ( "ProfileHelper" ) protected profileHelper : ProfileHelper ,
@inject ( "HideoutHelper" ) protected hideoutHelper : HideoutHelper ,
@inject ( "ScavCaseRewardGenerator" ) protected scavCaseRewardGenerator : ScavCaseRewardGenerator ,
@inject ( "LocalisationService" ) protected localisationService : LocalisationService ,
@inject ( "ConfigServer" ) protected configServer : ConfigServer ,
@inject ( "JsonUtil" ) protected jsonUtil : JsonUtil ,
2023-11-16 22:42:06 +01:00
@inject ( "FenceService" ) protected fenceService : FenceService ,
2023-03-03 16:23:46 +01:00
)
{
this . hideoutConfig = this . configServer . getConfig ( ConfigTypes . HIDEOUT ) ;
}
/ * *
2023-07-15 15:49:25 +02:00
* Handle HideoutUpgrade event
2023-03-03 16:23:46 +01:00
* Start a hideout area upgrade
* @param pmcData Player profile
* @param request upgrade start request
* @param sessionID Session id
* @returns IItemEventRouterResponse
* /
2023-11-16 22:42:06 +01:00
public startUpgrade (
pmcData : IPmcData ,
request : IHideoutUpgradeRequestData ,
sessionID : string ,
) : IItemEventRouterResponse
2023-03-03 16:23:46 +01:00
{
const output = this . eventOutputHolder . getOutput ( sessionID ) ;
2023-11-16 22:42:06 +01:00
const items = request . items . map ( ( reqItem ) = >
2023-03-03 16:23:46 +01:00
{
2023-11-16 22:42:06 +01:00
const item = pmcData . Inventory . items . find ( ( invItem ) = > invItem . _id === reqItem . id ) ;
return { inventoryItem : item , requestedItem : reqItem } ;
2023-03-03 16:23:46 +01:00
} ) ;
// If it's not money, its construction / barter items
for ( const item of items )
{
if ( ! item . inventoryItem )
{
2023-11-16 22:42:06 +01:00
this . logger . error (
this . localisationService . getText ( "hideout-unable_to_find_item_in_inventory" , item . requestedItem . id ) ,
) ;
2023-03-03 16:23:46 +01:00
return this . httpResponse . appendErrorToOutput ( output ) ;
}
2023-11-16 22:42:06 +01:00
if (
this . paymentHelper . isMoneyTpl ( item . inventoryItem . _tpl )
2023-03-03 16:23:46 +01:00
&& item . inventoryItem . upd
&& item . inventoryItem . upd . StackObjectsCount
2023-11-16 22:42:06 +01:00
&& item . inventoryItem . upd . StackObjectsCount > item . requestedItem . count
)
2023-03-03 16:23:46 +01:00
{
item . inventoryItem . upd . StackObjectsCount -= item . requestedItem . count ;
}
else
{
this . inventoryHelper . removeItem ( pmcData , item . inventoryItem . _id , sessionID , output ) ;
}
}
// Construction time management
2023-11-16 22:42:06 +01:00
const hideoutArea = pmcData . Hideout . Areas . find ( ( area ) = > area . type === request . areaType ) ;
2023-03-03 16:23:46 +01:00
if ( ! hideoutArea )
{
this . logger . error ( this . localisationService . getText ( "hideout-unable_to_find_area" , request . areaType ) ) ;
return this . httpResponse . appendErrorToOutput ( output ) ;
}
2023-11-16 22:42:06 +01:00
const hideoutData = this . databaseServer . getTables ( ) . hideout . areas . find ( ( area ) = >
area . type === request . areaType
) ;
2023-03-03 16:23:46 +01:00
if ( ! hideoutData )
{
2023-11-16 22:42:06 +01:00
this . logger . error (
this . localisationService . getText ( "hideout-unable_to_find_area_in_database" , request . areaType ) ,
) ;
2023-03-03 16:23:46 +01:00
return this . httpResponse . appendErrorToOutput ( output ) ;
}
const ctime = hideoutData . stages [ hideoutArea . level + 1 ] . constructionTime ;
if ( ctime > 0 )
{
const timestamp = this . timeUtil . getTimestamp ( ) ;
2024-01-10 15:10:03 +01:00
hideoutArea . completeTime = Math . round ( timestamp + ctime ) ;
2023-03-03 16:23:46 +01:00
hideoutArea . constructing = true ;
}
return output ;
}
/ * *
2023-07-15 15:49:25 +02:00
* Handle HideoutUpgradeComplete event
2023-03-03 16:23:46 +01:00
* Complete a hideout area upgrade
* @param pmcData Player profile
* @param request Completed upgrade request
* @param sessionID Session id
* @returns IItemEventRouterResponse
* /
2023-11-16 22:42:06 +01:00
public upgradeComplete (
pmcData : IPmcData ,
request : HideoutUpgradeCompleteRequestData ,
sessionID : string ,
) : IItemEventRouterResponse
2023-03-03 16:23:46 +01:00
{
const output = this . eventOutputHolder . getOutput ( sessionID ) ;
2023-10-10 13:03:20 +02:00
const db = this . databaseServer . getTables ( ) ;
2023-03-03 16:23:46 +01:00
2023-11-16 22:42:06 +01:00
const profileHideoutArea = pmcData . Hideout . Areas . find ( ( area ) = > area . type === request . areaType ) ;
2023-10-10 13:03:20 +02:00
if ( ! profileHideoutArea )
2023-03-03 16:23:46 +01:00
{
this . logger . error ( this . localisationService . getText ( "hideout-unable_to_find_area" , request . areaType ) ) ;
return this . httpResponse . appendErrorToOutput ( output ) ;
}
2023-10-10 13:03:20 +02:00
// Upgrade profile values
profileHideoutArea . level ++ ;
profileHideoutArea . completeTime = 0 ;
profileHideoutArea . constructing = false ;
2023-03-03 16:23:46 +01:00
2023-11-16 22:42:06 +01:00
const hideoutData = db . hideout . areas . find ( ( area ) = > area . type === profileHideoutArea . type ) ;
2023-03-03 16:23:46 +01:00
if ( ! hideoutData )
{
2023-11-16 22:42:06 +01:00
this . logger . error (
this . localisationService . getText ( "hideout-unable_to_find_area_in_database" , request . areaType ) ,
) ;
2023-03-03 16:23:46 +01:00
return this . httpResponse . appendErrorToOutput ( output ) ;
}
// Apply bonuses
2023-10-10 13:03:20 +02:00
const hideoutStage = hideoutData . stages [ profileHideoutArea . level ] ;
const bonuses = hideoutStage . bonuses ;
2023-03-25 17:35:38 +01:00
if ( bonuses ? . length > 0 )
2023-03-03 16:23:46 +01:00
{
for ( const bonus of bonuses )
{
this . hideoutHelper . applyPlayerUpgradesBonuses ( pmcData , bonus ) ;
}
}
2023-10-10 13:03:20 +02:00
// Upgrade includes a container improvement/addition
if ( hideoutStage ? . container )
{
2023-11-16 22:42:06 +01:00
this . addContainerImprovementToProfile (
output ,
sessionID ,
pmcData ,
profileHideoutArea ,
hideoutData ,
hideoutStage ,
) ;
2023-10-10 13:03:20 +02:00
}
// Upgrading water collector / med station
2023-11-16 22:42:06 +01:00
if (
profileHideoutArea . type === HideoutAreas . WATER_COLLECTOR
|| profileHideoutArea . type === HideoutAreas . MEDSTATION
)
2023-10-10 13:03:20 +02:00
{
this . checkAndUpgradeWall ( pmcData ) ;
}
2023-03-03 16:23:46 +01:00
2023-10-10 13:03:20 +02:00
// Add Skill Points Per Area Upgrade
2023-11-16 22:42:06 +01:00
this . profileHelper . addSkillPointsToPlayer (
pmcData ,
SkillTypes . HIDEOUT_MANAGEMENT ,
db . globals . config . SkillsSettings . HideoutManagement . SkillPointsPerAreaUpgrade ,
) ;
2023-03-03 16:23:46 +01:00
return output ;
}
2023-10-10 13:03:20 +02:00
/ * *
* Upgrade wall status to visible in profile if medstation / water collector are both level 1
* @param pmcData Player profile
* /
protected checkAndUpgradeWall ( pmcData : IPmcData ) : void
{
2023-11-16 22:42:06 +01:00
const medStation = pmcData . Hideout . Areas . find ( ( area ) = > area . type === HideoutAreas . MEDSTATION ) ;
const waterCollector = pmcData . Hideout . Areas . find ( ( area ) = > area . type === HideoutAreas . WATER_COLLECTOR ) ;
2023-10-10 13:03:20 +02:00
if ( medStation ? . level >= 1 && waterCollector ? . level >= 1 )
{
2023-11-16 22:42:06 +01:00
const wall = pmcData . Hideout . Areas . find ( ( area ) = > area . type === HideoutAreas . EMERGENCY_WALL ) ;
2023-10-10 13:03:20 +02:00
if ( wall ? . level === 0 )
{
wall . level = 3 ;
}
}
}
/ * *
* @param pmcData Profile to edit
* @param output Object to send back to client
* @param sessionID Session / player id
* @param profileParentHideoutArea Current hideout area for profile
* @param dbHideoutArea Hideout area being upgraded
* @param hideoutStage Stage hideout area is being upgraded to
* /
2023-11-16 22:42:06 +01:00
protected addContainerImprovementToProfile (
output : IItemEventRouterResponse ,
sessionID : string ,
pmcData : IPmcData ,
profileParentHideoutArea : HideoutArea ,
dbHideoutArea : IHideoutArea ,
hideoutStage : Stage ,
) : void
2023-10-10 13:03:20 +02:00
{
// Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id
if ( ! pmcData . Inventory . hideoutAreaStashes [ dbHideoutArea . type ] )
{
pmcData . Inventory . hideoutAreaStashes [ dbHideoutArea . type ] = dbHideoutArea . _id ;
}
// Add/upgrade stash item in player inventory
this . addUpdateInventoryItemToProfile ( pmcData , dbHideoutArea , hideoutStage ) ;
// Inform client of changes
this . addContainerUpgradeToClientOutput ( output , sessionID , dbHideoutArea . type , dbHideoutArea , hideoutStage ) ;
// Some areas like gun stand have a child area linked to it, it needs to do the same as above
2023-11-16 22:42:06 +01:00
const childDbArea = this . databaseServer . getTables ( ) . hideout . areas . find ( ( x ) = >
x . parentArea === dbHideoutArea . _id
) ;
2023-10-10 13:03:20 +02:00
if ( childDbArea )
{
// Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id
if ( ! pmcData . Inventory . hideoutAreaStashes [ childDbArea . type ] )
{
pmcData . Inventory . hideoutAreaStashes [ childDbArea . type ] = childDbArea . _id ;
}
// Set child area level to same as parent area
2023-11-16 22:42:06 +01:00
pmcData . Hideout . Areas . find ( ( x ) = > x . type === childDbArea . type ) . level = pmcData . Hideout . Areas . find ( ( x ) = >
x . type === profileParentHideoutArea . type
) . level ;
2023-10-10 13:03:20 +02:00
// Add/upgrade stash item in player inventory
const childDbAreaStage = childDbArea . stages [ profileParentHideoutArea . level ] ;
this . addUpdateInventoryItemToProfile ( pmcData , childDbArea , childDbAreaStage ) ;
// Inform client of the changes
this . addContainerUpgradeToClientOutput ( output , sessionID , childDbArea . type , childDbArea , childDbAreaStage ) ;
}
}
/ * *
* Add an inventory item to profile from a hideout area stage data
* @param pmcData Profile to update
* @param dbHideoutData Hideout area from db being upgraded
* @param hideoutStage Stage area upgraded to
* /
protected addUpdateInventoryItemToProfile ( pmcData : IPmcData , dbHideoutData : IHideoutArea , hideoutStage : Stage ) : void
{
2023-11-16 22:42:06 +01:00
const existingInventoryItem = pmcData . Inventory . items . find ( ( x ) = > x . _id === dbHideoutData . _id ) ;
if ( existingInventoryItem )
2023-10-10 13:03:20 +02:00
{
// Update existing items container tpl to point to new id (tpl)
existingInventoryItem . _tpl = hideoutStage . container ;
return ;
}
// Add new item as none exists
pmcData . Inventory . items . push ( { _id : dbHideoutData._id , _tpl : hideoutStage.container } ) ;
}
/ * *
2023-11-30 11:02:00 +01:00
* @param output Object to send to client
2023-10-10 13:03:20 +02:00
* @param sessionID Session / player id
* @param areaType Hideout area that had stash added
* @param hideoutDbData Hideout area that caused addition of stash
* @param hideoutStage Hideout area upgraded to this
* /
2023-11-16 22:42:06 +01:00
protected addContainerUpgradeToClientOutput (
output : IItemEventRouterResponse ,
sessionID : string ,
areaType : HideoutAreas ,
hideoutDbData : IHideoutArea ,
hideoutStage : Stage ,
) : void
2023-10-10 13:03:20 +02:00
{
if ( ! output . profileChanges [ sessionID ] . changedHideoutStashes )
{
output . profileChanges [ sessionID ] . changedHideoutStashes = { } ;
}
2023-11-16 22:42:06 +01:00
output . profileChanges [ sessionID ] . changedHideoutStashes [ areaType ] = {
2023-10-10 13:03:20 +02:00
Id : hideoutDbData._id ,
2023-11-16 22:42:06 +01:00
Tpl : hideoutStage.container ,
2023-10-10 13:03:20 +02:00
} ;
}
2023-03-03 16:23:46 +01:00
/ * *
* Handle HideoutPutItemsInAreaSlots
* Create item in hideout slot item array , remove item from player inventory
* @param pmcData Profile data
* @param addItemToHideoutRequest reqeust from client to place item in area slot
* @param sessionID Session id
* @returns IItemEventRouterResponse object
* /
2023-11-16 22:42:06 +01:00
public putItemsInAreaSlots (
pmcData : IPmcData ,
addItemToHideoutRequest : IHideoutPutItemInRequestData ,
sessionID : string ,
) : IItemEventRouterResponse
2023-03-03 16:23:46 +01:00
{
2024-01-16 13:21:42 +01:00
const output = this . eventOutputHolder . getOutput ( sessionID ) ;
2023-03-03 16:23:46 +01:00
2023-11-16 22:42:06 +01:00
const itemsToAdd = Object . entries ( addItemToHideoutRequest . items ) . map ( ( kvp ) = >
2023-03-03 16:23:46 +01:00
{
2023-11-16 22:42:06 +01:00
const item = pmcData . Inventory . items . find ( ( invItem ) = > invItem . _id === kvp [ 1 ] . id ) ;
return { inventoryItem : item , requestedItem : kvp [ 1 ] , slot : kvp [ 0 ] } ;
2023-03-03 16:23:46 +01:00
} ) ;
2023-11-16 22:42:06 +01:00
const hideoutArea = pmcData . Hideout . Areas . find ( ( area ) = > area . type === addItemToHideoutRequest . areaType ) ;
2023-03-03 16:23:46 +01:00
if ( ! hideoutArea )
{
2023-11-16 22:42:06 +01:00
this . logger . error (
this . localisationService . getText (
"hideout-unable_to_find_area_in_database" ,
addItemToHideoutRequest . areaType ,
) ,
) ;
2023-03-03 16:23:46 +01:00
return this . httpResponse . appendErrorToOutput ( output ) ;
}
for ( const item of itemsToAdd )
{
if ( ! item . inventoryItem )
{
2023-11-16 22:42:06 +01:00
this . logger . error (
this . localisationService . getText ( "hideout-unable_to_find_item_in_inventory" , {
itemId : item.requestedItem.id ,
area : hideoutArea.type ,
} ) ,
) ;
2023-03-03 16:23:46 +01:00
return this . httpResponse . appendErrorToOutput ( output ) ;
}
// Add item to area.slots
const destinationLocationIndex = Number ( item . slot ) ;
2023-11-16 22:42:06 +01:00
const hideoutSlotIndex = hideoutArea . slots . findIndex ( ( x ) = > x . locationIndex === destinationLocationIndex ) ;
2023-03-03 16:23:46 +01:00
hideoutArea . slots [ hideoutSlotIndex ] . item = [ {
_id : item.inventoryItem._id ,
_tpl : item.inventoryItem._tpl ,
2023-11-16 22:42:06 +01:00
upd : item.inventoryItem.upd ,
2023-03-03 16:23:46 +01:00
} ] ;
2024-01-16 13:21:42 +01:00
this . inventoryHelper . removeItem ( pmcData , item . inventoryItem . _id , sessionID , output ) ;
2023-11-16 22:42:06 +01:00
}
2023-03-03 16:23:46 +01:00
// Trigger a forced update
this . hideoutHelper . updatePlayerHideout ( sessionID ) ;
return output ;
}
/ * *
2023-07-15 15:49:25 +02:00
* Handle HideoutTakeItemsFromAreaSlots event
2023-03-03 16:23:46 +01:00
* Remove item from hideout area and place into player inventory
* @param pmcData Player profile
* @param request Take item out of area request
* @param sessionID Session id
* @returns IItemEventRouterResponse
* /
2023-11-16 22:42:06 +01:00
public takeItemsFromAreaSlots (
pmcData : IPmcData ,
request : IHideoutTakeItemOutRequestData ,
sessionID : string ,
) : IItemEventRouterResponse
2023-03-03 16:23:46 +01:00
{
const output = this . eventOutputHolder . getOutput ( sessionID ) ;
2023-11-16 22:42:06 +01:00
const hideoutArea = pmcData . Hideout . Areas . find ( ( area ) = > area . type === request . areaType ) ;
2023-03-03 16:23:46 +01:00
if ( ! hideoutArea )
{
this . logger . error ( this . localisationService . getText ( "hideout-unable_to_find_area" , request . areaType ) ) ;
return this . httpResponse . appendErrorToOutput ( output ) ;
}
if ( ! hideoutArea . slots || hideoutArea . slots . length === 0 )
{
2023-11-16 22:42:06 +01:00
this . logger . error (
this . localisationService . getText ( "hideout-unable_to_find_item_to_remove_from_area" , hideoutArea . type ) ,
) ;
2023-03-03 16:23:46 +01:00
return this . httpResponse . appendErrorToOutput ( output ) ;
}
// Handle areas that have resources that can be placed in/taken out of slots from the area
2023-11-16 22:42:06 +01:00
if (
[
HideoutAreas . AIR_FILTERING ,
HideoutAreas . WATER_COLLECTOR ,
HideoutAreas . GENERATOR ,
HideoutAreas . BITCOIN_FARM ,
] . includes ( hideoutArea . type )
)
2023-03-03 16:23:46 +01:00
{
const response = this . removeResourceFromArea ( sessionID , pmcData , request , output , hideoutArea ) ;
this . update ( ) ;
return response ;
}
2023-11-16 22:42:06 +01:00
throw new Error (
this . localisationService . getText ( "hideout-unhandled_remove_item_from_area_request" , hideoutArea . type ) ,
) ;
2023-03-03 16:23:46 +01:00
}
/ * *
* Find resource item in hideout area , add copy to player inventory , remove Item from hideout slot
* @param sessionID Session id
* @param pmcData Profile to update
* @param removeResourceRequest client request
* @param output response to send to client
* @param hideoutArea Area fuel is being removed from
* @returns IItemEventRouterResponse response
* /
2023-11-16 22:42:06 +01:00
protected removeResourceFromArea (
sessionID : string ,
pmcData : IPmcData ,
removeResourceRequest : IHideoutTakeItemOutRequestData ,
output : IItemEventRouterResponse ,
hideoutArea : HideoutArea ,
) : IItemEventRouterResponse
2023-03-03 16:23:46 +01:00
{
const slotIndexToRemove = removeResourceRequest . slots [ 0 ] ;
2023-11-16 22:42:06 +01:00
const itemToReturn = hideoutArea . slots . find ( ( x ) = > x . locationIndex === slotIndexToRemove ) . item [ 0 ] ;
2023-03-03 16:23:46 +01:00
const newReq = {
items : [ {
// eslint-disable-next-line @typescript-eslint/naming-convention
item_id : itemToReturn._tpl ,
2023-11-16 22:42:06 +01:00
count : 1 ,
2023-03-03 16:23:46 +01:00
} ] ,
2023-11-16 22:42:06 +01:00
tid : "ragfair" ,
2023-03-03 16:23:46 +01:00
} ;
2023-11-16 22:42:06 +01:00
output = this . inventoryHelper . addItem (
pmcData ,
newReq ,
output ,
sessionID ,
null ,
! ! itemToReturn . upd . SpawnedInSession ,
itemToReturn . upd ,
) ;
2023-03-03 16:23:46 +01:00
// If addItem returned with errors, drop out
if ( output . warnings && output . warnings . length > 0 )
{
return output ;
}
// Remove items from slot, locationIndex remains
2023-11-16 22:42:06 +01:00
const hideoutSlotIndex = hideoutArea . slots . findIndex ( ( x ) = > x . locationIndex === slotIndexToRemove ) ;
2023-03-03 16:23:46 +01:00
hideoutArea . slots [ hideoutSlotIndex ] . item = undefined ;
return output ;
}
/ * *
2023-07-15 15:49:25 +02:00
* Handle HideoutToggleArea event
2023-03-03 16:23:46 +01:00
* Toggle area on / off
* @param pmcData Player profile
* @param request Toggle area request
* @param sessionID Session id
* @returns IItemEventRouterResponse
* /
2023-11-16 22:42:06 +01:00
public toggleArea (
pmcData : IPmcData ,
request : IHideoutToggleAreaRequestData ,
sessionID : string ,
) : IItemEventRouterResponse
2023-03-03 16:23:46 +01:00
{
const output = this . eventOutputHolder . getOutput ( sessionID ) ;
2023-10-10 13:03:20 +02:00
// Force a production update (occur before area is toggled as it could be generator and doing it after generator enabled would cause incorrect calculaton of production progress)
this . hideoutHelper . updatePlayerHideout ( sessionID ) ;
2023-11-16 22:42:06 +01:00
const hideoutArea = pmcData . Hideout . Areas . find ( ( area ) = > area . type === request . areaType ) ;
2023-03-03 16:23:46 +01:00
if ( ! hideoutArea )
{
this . logger . error ( this . localisationService . getText ( "hideout-unable_to_find_area" , request . areaType ) ) ;
return this . httpResponse . appendErrorToOutput ( output ) ;
}
hideoutArea . active = request . enabled ;
return output ;
}
/ * *
2023-07-15 15:49:25 +02:00
* Handle HideoutSingleProductionStart event
2023-03-03 16:23:46 +01:00
* Start production for an item from hideout area
* @param pmcData Player profile
* @param body Start prodution of single item request
* @param sessionID Session id
* @returns IItemEventRouterResponse
* /
2023-11-16 22:42:06 +01:00
public singleProductionStart (
pmcData : IPmcData ,
body : IHideoutSingleProductionStartRequestData ,
sessionID : string ,
) : IItemEventRouterResponse
2023-03-03 16:23:46 +01:00
{
// Start production
this . registerProduction ( pmcData , body , sessionID ) ;
// Find the recipe of the production
2023-11-16 22:42:06 +01:00
const recipe = this . databaseServer . getTables ( ) . hideout . production . find ( ( p ) = > p . _id === body . recipeId ) ;
2023-06-01 11:46:16 +02:00
2023-03-03 16:23:46 +01:00
// Find the actual amount of items we need to remove because body can send weird data
2023-11-16 22:42:06 +01:00
const requirements = this . jsonUtil . clone ( recipe . requirements . filter ( ( i ) = > i . type === "Item" ) ) ;
2023-03-03 16:23:46 +01:00
const output = this . eventOutputHolder . getOutput ( sessionID ) ;
for ( const itemToDelete of body . items )
{
2023-11-16 22:42:06 +01:00
const itemToCheck = pmcData . Inventory . items . find ( ( i ) = > i . _id === itemToDelete . id ) ;
const requirement = requirements . find ( ( requirement ) = > requirement . templateId === itemToCheck . _tpl ) ;
2023-03-03 16:23:46 +01:00
if ( requirement . count <= 0 )
2023-06-01 11:46:16 +02:00
{
2023-03-03 16:23:46 +01:00
continue ;
2023-06-01 11:46:16 +02:00
}
2023-03-03 16:23:46 +01:00
this . inventoryHelper . removeItemByCount ( pmcData , itemToDelete . id , requirement . count , sessionID , output ) ;
requirement . count -= itemToDelete . count ;
}
return output ;
}
/ * *
2023-07-15 15:49:25 +02:00
* Handle HideoutScavCaseProductionStart event
2023-03-03 16:23:46 +01:00
* Handles event after clicking 'start' on the scav case hideout page
* @param pmcData player profile
* @param body client request object
* @param sessionID session id
* @returns item event router response
* /
2023-11-16 22:42:06 +01:00
public scavCaseProductionStart (
pmcData : IPmcData ,
body : IHideoutScavCaseStartRequestData ,
sessionID : string ,
) : IItemEventRouterResponse
2023-03-03 16:23:46 +01:00
{
2024-01-16 13:21:42 +01:00
const output = this . eventOutputHolder . getOutput ( sessionID ) ;
2023-03-03 16:23:46 +01:00
for ( const requestedItem of body . items )
{
2023-11-16 22:42:06 +01:00
const inventoryItem = pmcData . Inventory . items . find ( ( item ) = > item . _id === requestedItem . id ) ;
2023-03-03 16:23:46 +01:00
if ( ! inventoryItem )
{
2023-11-16 22:42:06 +01:00
this . logger . error (
this . localisationService . getText (
"hideout-unable_to_find_scavcase_requested_item_in_profile_inventory" ,
requestedItem . id ,
) ,
) ;
2023-03-03 16:23:46 +01:00
return this . httpResponse . appendErrorToOutput ( output ) ;
}
2023-11-16 22:42:06 +01:00
if ( inventoryItem . upd ? . StackObjectsCount && inventoryItem . upd . StackObjectsCount > requestedItem . count )
2023-03-03 16:23:46 +01:00
{
inventoryItem . upd . StackObjectsCount -= requestedItem . count ;
}
else
{
2024-01-16 13:21:42 +01:00
this . inventoryHelper . removeItem ( pmcData , requestedItem . id , sessionID , output ) ;
2023-03-03 16:23:46 +01:00
}
}
2023-11-16 22:42:06 +01:00
const recipe = this . databaseServer . getTables ( ) . hideout . scavcase . find ( ( r ) = > r . _id === body . recipeId ) ;
2023-03-03 16:23:46 +01:00
if ( ! recipe )
{
2023-11-16 22:42:06 +01:00
this . logger . error (
this . localisationService . getText ( "hideout-unable_to_find_scav_case_recipie_in_database" , body . recipeId ) ,
) ;
2023-03-03 16:23:46 +01:00
return this . httpResponse . appendErrorToOutput ( output ) ;
}
2023-11-16 22:42:06 +01:00
2023-03-03 16:23:46 +01:00
// @Important: Here we need to be very exact:
// - normal recipe: Production time value is stored in attribute "productionType" with small "p"
// - scav case recipe: Production time value is stored in attribute "ProductionType" with capital "P"
const modifiedScavCaseTime = this . getScavCaseTime ( pmcData , recipe . ProductionTime ) ;
2023-11-16 22:42:06 +01:00
pmcData . Hideout . Production [ body . recipeId ] = this . hideoutHelper . initProduction (
body . recipeId ,
modifiedScavCaseTime ,
false ,
) ;
2023-03-03 16:23:46 +01:00
pmcData . Hideout . Production [ body . recipeId ] . sptIsScavCase = true ;
return output ;
}
/ * *
* Adjust scav case time based on fence standing
2023-11-16 22:42:06 +01:00
*
2023-03-03 16:23:46 +01:00
* @param pmcData Player profile
* @param productionTime Time to complete scav case in seconds
* @returns Adjusted scav case time in seconds
* /
protected getScavCaseTime ( pmcData : IPmcData , productionTime : number ) : number
{
const fenceLevel = this . fenceService . getFenceInfo ( pmcData ) ;
if ( ! fenceLevel )
{
return productionTime ;
}
2023-11-16 22:42:06 +01:00
2023-03-03 16:23:46 +01:00
return productionTime * fenceLevel . ScavCaseTimeModifier ;
}
/ * *
* Add generated scav case rewards to player profile
* @param pmcData player profile to add rewards to
* @param rewards reward items to add to profile
2023-04-23 14:44:15 +02:00
* @param recipeId recipe id to save into Production dict
2023-03-03 16:23:46 +01:00
* /
2023-04-23 14:44:15 +02:00
protected addScavCaseRewardsToProfile ( pmcData : IPmcData , rewards : Product [ ] , recipeId : string ) : void
2023-03-03 16:23:46 +01:00
{
2023-11-16 22:42:06 +01:00
pmcData . Hideout . Production [ ` ScavCase ${ recipeId } ` ] = { Products : rewards } ;
2023-03-03 16:23:46 +01:00
}
/ * *
* Start production of continuously created item
* @param pmcData Player profile
* @param request Continious production request
* @param sessionID Session id
* @returns IItemEventRouterResponse
* /
2023-11-16 22:42:06 +01:00
public continuousProductionStart (
pmcData : IPmcData ,
request : IHideoutContinuousProductionStartRequestData ,
sessionID : string ,
) : IItemEventRouterResponse
2023-03-03 16:23:46 +01:00
{
this . registerProduction ( pmcData , request , sessionID ) ;
return this . eventOutputHolder . getOutput ( sessionID ) ;
}
/ * *
2023-07-15 15:49:25 +02:00
* Handle HideoutTakeProduction event
2023-03-03 16:23:46 +01:00
* Take completed item out of hideout area and place into player inventory
* @param pmcData Player profile
* @param request Remove production from area request
* @param sessionID Session id
* @returns IItemEventRouterResponse
* /
2023-11-16 22:42:06 +01:00
public takeProduction (
pmcData : IPmcData ,
request : IHideoutTakeProductionRequestData ,
sessionID : string ,
) : IItemEventRouterResponse
2023-03-03 16:23:46 +01:00
{
const output = this . eventOutputHolder . getOutput ( sessionID ) ;
2023-11-20 11:14:21 +01:00
const hideoutDb = this . databaseServer . getTables ( ) . hideout ;
2023-03-03 16:23:46 +01:00
if ( request . recipeId === HideoutHelper . bitcoinFarm )
{
return this . hideoutHelper . getBTC ( pmcData , request , sessionID ) ;
}
2023-11-20 11:14:21 +01:00
const recipe = hideoutDb . production . find ( ( r ) = > r . _id === request . recipeId ) ;
2023-03-03 16:23:46 +01:00
if ( recipe )
{
2023-04-23 14:44:15 +02:00
return this . handleRecipe ( sessionID , recipe , pmcData , request , output ) ;
2023-03-03 16:23:46 +01:00
}
2023-11-20 11:14:21 +01:00
const scavCase = hideoutDb . scavcase . find ( ( r ) = > r . _id === request . recipeId ) ;
2023-03-03 16:23:46 +01:00
if ( scavCase )
{
return this . handleScavCase ( sessionID , pmcData , request , output ) ;
}
2023-11-16 22:42:06 +01:00
this . logger . error (
this . localisationService . getText (
"hideout-unable_to_find_production_in_profile_by_recipie_id" ,
request . recipeId ,
) ,
) ;
2023-03-03 16:23:46 +01:00
return this . httpResponse . appendErrorToOutput ( output ) ;
}
/ * *
2023-04-23 14:44:15 +02:00
* Take recipe - type production out of hideout area and place into player inventory
2023-03-03 16:23:46 +01:00
* @param sessionID Session id
2023-04-23 14:44:15 +02:00
* @param recipe Completed recipe of item
2023-03-03 16:23:46 +01:00
* @param pmcData Player profile
* @param request Remove production from area request
* @param output Output object to update
* @returns IItemEventRouterResponse
* /
2023-11-16 22:42:06 +01:00
protected handleRecipe (
sessionID : string ,
recipe : IHideoutProduction ,
pmcData : IPmcData ,
request : IHideoutTakeProductionRequestData ,
output : IItemEventRouterResponse ,
) : IItemEventRouterResponse
2023-03-03 16:23:46 +01:00
{
// Variables for managemnet of skill
let craftingExpAmount = 0 ;
2023-12-30 22:30:54 +01:00
// ? move the logic of TaskConditionCounters in new method?
let counterHoursCrafting = pmcData . TaskConditionCounters [ HideoutController . nameTaskConditionCountersCrafting ] ;
2023-03-03 16:23:46 +01:00
if ( ! counterHoursCrafting )
{
2023-12-30 22:30:54 +01:00
pmcData . TaskConditionCounters [ HideoutController . nameTaskConditionCountersCrafting ] = {
id : recipe._id ,
type : HideoutController . nameTaskConditionCountersCrafting ,
sourceId : "CounterCrafting" ,
2023-11-16 22:42:06 +01:00
value : 0 ,
} ;
2023-12-30 22:30:54 +01:00
counterHoursCrafting = pmcData . TaskConditionCounters [ HideoutController . nameTaskConditionCountersCrafting ] ;
2023-03-03 16:23:46 +01:00
}
let hoursCrafting = counterHoursCrafting . value ;
// create item and throw it into profile
let id = recipe . endProduct ;
// replace the base item with its main preset
if ( this . presetHelper . hasPreset ( id ) )
{
id = this . presetHelper . getDefaultPreset ( id ) . _id ;
}
const newReq = {
items : [ {
// eslint-disable-next-line @typescript-eslint/naming-convention
item_id : id ,
2023-11-16 22:42:06 +01:00
count : recipe.count ,
2023-03-03 16:23:46 +01:00
} ] ,
2023-11-16 22:42:06 +01:00
tid : "ragfair" ,
2023-03-03 16:23:46 +01:00
} ;
const entries = Object . entries ( pmcData . Hideout . Production ) ;
let prodId : string ;
for ( const x of entries )
{
2023-11-13 13:01:48 +01:00
// Skip null production objects
if ( ! x [ 1 ] )
{
continue ;
}
2023-11-16 22:42:06 +01:00
if ( this . hideoutHelper . isProductionType ( x [ 1 ] ) )
{ // Production or ScavCase
2023-03-03 16:23:46 +01:00
if ( ( x [ 1 ] as Production ) . RecipeId === request . recipeId )
{
prodId = x [ 0 ] ; // set to objects key
break ;
}
}
}
if ( prodId === undefined )
{
2023-11-16 22:42:06 +01:00
this . logger . error (
this . localisationService . getText (
"hideout-unable_to_find_production_in_profile_by_recipie_id" ,
request . recipeId ,
) ,
) ;
2023-03-03 16:23:46 +01:00
return this . httpResponse . appendErrorToOutput ( output ) ;
}
2023-11-20 11:14:21 +01:00
// Check if the recipe is the same as the last one
2023-11-16 22:42:06 +01:00
const area = pmcData . Hideout . Areas . find ( ( x ) = > x . type === recipe . areaType ) ;
2023-03-03 16:23:46 +01:00
if ( area && request . recipeId !== area . lastRecipe )
{
// 1 point per craft upon the end of production for alternating between 2 different crafting recipes in the same module
2023-07-12 21:12:16 +02:00
craftingExpAmount += this . hideoutConfig . expCraftAmount ; // Default is 10
2023-03-03 16:23:46 +01:00
}
// 1 point per 8 hours of crafting
hoursCrafting += recipe . productionTime ;
if ( ( hoursCrafting / this . hideoutConfig . hoursForSkillCrafting ) >= 1 )
{
2023-11-16 22:42:06 +01:00
const multiplierCrafting = Math . floor ( hoursCrafting / this . hideoutConfig . hoursForSkillCrafting ) ;
craftingExpAmount += 1 * multiplierCrafting ;
hoursCrafting -= this . hideoutConfig . hoursForSkillCrafting * multiplierCrafting ;
2023-03-03 16:23:46 +01:00
}
2023-11-20 11:14:21 +01:00
// Increment
2023-03-03 16:23:46 +01:00
// if addItem passes validation:
// - increment skill point for crafting
// - delete the production in profile Hideout.Production
const callback = ( ) = >
{
2023-11-20 11:14:21 +01:00
// Manager Hideout skill
2023-03-03 16:23:46 +01:00
// ? use a configuration variable for the value?
2023-07-13 11:26:47 +02:00
const globals = this . databaseServer . getTables ( ) . globals ;
2023-11-16 22:42:06 +01:00
this . profileHelper . addSkillPointsToPlayer (
pmcData ,
SkillTypes . HIDEOUT_MANAGEMENT ,
globals . config . SkillsSettings . HideoutManagement . SkillPointsPerCraft ,
true ,
) ;
2023-12-11 12:45:23 +01:00
// Manager Crafting skill
2023-03-03 16:23:46 +01:00
if ( craftingExpAmount > 0 )
{
2023-11-07 12:20:25 +01:00
this . profileHelper . addSkillPointsToPlayer ( pmcData , SkillTypes . CRAFTING , craftingExpAmount ) ;
2023-12-11 12:45:23 +01:00
const intellectAmountToGive = 0.5 * ( Math . round ( craftingExpAmount / 15 ) ) ;
if ( intellectAmountToGive > 0 )
{
this . profileHelper . addSkillPointsToPlayer (
pmcData ,
SkillTypes . INTELLECT ,
intellectAmountToGive ,
) ;
}
2023-03-03 16:23:46 +01:00
}
area . lastRecipe = request . recipeId ;
counterHoursCrafting . value = hoursCrafting ;
2023-11-20 11:14:21 +01:00
// Continuous crafts have special handling in EventOutputHolder.updateOutputProperties()
2023-11-16 13:55:57 +01:00
pmcData . Hideout . Production [ prodId ] . sptIsComplete = true ;
2023-11-20 11:14:21 +01:00
pmcData . Hideout . Production [ prodId ] . sptIsContinuous = recipe . continuous ;
2023-03-03 16:23:46 +01:00
2023-11-20 11:14:21 +01:00
// Flag normal crafts as complete
if ( ! recipe . continuous )
{
pmcData . Hideout . Production [ prodId ] . inProgress = false ;
}
} ;
2023-03-03 16:23:46 +01:00
2023-04-23 14:44:15 +02:00
// Handle the isEncoded flag from recipe
2023-03-03 16:23:46 +01:00
if ( recipe . isEncoded )
{
2023-11-16 22:42:06 +01:00
const upd : Upd = { RecodableComponent : { IsEncoded : true } } ;
2023-03-03 16:23:46 +01:00
return this . inventoryHelper . addItem ( pmcData , newReq , output , sessionID , callback , true , upd ) ;
}
return this . inventoryHelper . addItem ( pmcData , newReq , output , sessionID , callback , true ) ;
}
/ * *
2023-10-10 13:03:20 +02:00
* Handles generating case rewards and sending to player inventory
2023-03-03 16:23:46 +01:00
* @param sessionID Session id
* @param pmcData Player profile
* @param request Get rewards from scavcase craft request
* @param output Output object to update
* @returns IItemEventRouterResponse
* /
2023-11-16 22:42:06 +01:00
protected handleScavCase (
sessionID : string ,
pmcData : IPmcData ,
request : IHideoutTakeProductionRequestData ,
output : IItemEventRouterResponse ,
) : IItemEventRouterResponse
2023-03-03 16:23:46 +01:00
{
const ongoingProductions = Object . entries ( pmcData . Hideout . Production ) ;
let prodId : string ;
2023-10-10 13:03:20 +02:00
for ( const production of ongoingProductions )
2023-03-03 16:23:46 +01:00
{
2023-11-16 22:42:06 +01:00
if ( this . hideoutHelper . isProductionType ( production [ 1 ] ) )
{ // Production or ScavCase
2023-10-10 13:03:20 +02:00
if ( ( production [ 1 ] as ScavCase ) . RecipeId === request . recipeId )
2023-03-03 16:23:46 +01:00
{
2023-10-10 13:03:20 +02:00
prodId = production [ 0 ] ; // Set to objects key
2023-03-03 16:23:46 +01:00
break ;
}
}
}
if ( prodId === undefined )
{
2023-11-16 22:42:06 +01:00
this . logger . error (
this . localisationService . getText (
"hideout-unable_to_find_production_in_profile_by_recipie_id" ,
request . recipeId ,
) ,
) ;
2023-03-03 16:23:46 +01:00
return this . httpResponse . appendErrorToOutput ( output ) ;
}
// Create rewards for scav case
const scavCaseRewards = this . scavCaseRewardGenerator . generate ( request . recipeId ) ;
2023-11-30 14:44:43 +01:00
// Add scav case rewards to player profile
2023-03-03 16:23:46 +01:00
pmcData . Hideout . Production [ prodId ] . Products = scavCaseRewards ;
// Remove the old production from output object before its sent to client
delete output . profileChanges [ sessionID ] . production [ request . recipeId ] ;
2023-11-30 14:44:43 +01:00
// Get array of item created + count of them after completing hideout craft
2023-11-16 22:42:06 +01:00
const itemsToAdd = pmcData . Hideout . Production [ prodId ] . Products . map (
( x : { _tpl : string ; upd ? : { StackObjectsCount? : number ; } ; } ) = >
2023-03-03 16:23:46 +01:00
{
2023-11-30 14:44:43 +01:00
const itemTpl = this . presetHelper . hasPreset ( x . _tpl )
? this . presetHelper . getDefaultPreset ( x . _tpl ) . _id
: x . _tpl ;
// Count of items crafted
2023-11-16 22:42:06 +01:00
const numOfItems = ! x . upd ? . StackObjectsCount ? 1 : x.upd.StackObjectsCount ;
// eslint-disable-next-line @typescript-eslint/naming-convention
2023-11-30 14:44:43 +01:00
return { item_id : itemTpl , count : numOfItems } ;
2023-11-16 22:42:06 +01:00
} ,
) ;
2023-03-03 16:23:46 +01:00
2023-11-16 22:42:06 +01:00
const newReq = { items : itemsToAdd , tid : "ragfair" } ;
2023-03-03 16:23:46 +01:00
const callback = ( ) = >
{
2023-11-30 14:44:43 +01:00
// Flag as complete - will be cleaned up later by hideoutController.update()
2023-11-16 13:55:57 +01:00
pmcData . Hideout . Production [ prodId ] . sptIsComplete = true ;
2023-11-20 11:22:03 +01:00
2023-11-30 14:44:43 +01:00
// Crafting complete, flag as such
2023-11-20 11:22:03 +01:00
pmcData . Hideout . Production [ prodId ] . inProgress = false ;
2023-03-03 16:23:46 +01:00
} ;
2023-11-30 14:44:43 +01:00
// Add crafted item to player inventory
2023-03-03 16:23:46 +01:00
return this . inventoryHelper . addItem ( pmcData , newReq , output , sessionID , callback , true ) ;
}
/ * *
2023-06-01 11:46:16 +02:00
* Start area production for item by adding production to profiles ' Hideout . Production array
2023-03-03 16:23:46 +01:00
* @param pmcData Player profile
* @param request Start production request
* @param sessionID Session id
* @returns IItemEventRouterResponse
* /
2023-11-16 22:42:06 +01:00
public registerProduction (
pmcData : IPmcData ,
request : IHideoutSingleProductionStartRequestData | IHideoutContinuousProductionStartRequestData ,
sessionID : string ,
) : IItemEventRouterResponse
2023-03-03 16:23:46 +01:00
{
return this . hideoutHelper . registerProduction ( pmcData , request , sessionID ) ;
}
/ * *
* Get quick time event list for hideout
* // TODO - implement this
* @param sessionId Session id
* @returns IQteData array
2023-11-16 22:42:06 +01:00
* /
2023-03-03 16:23:46 +01:00
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getQteList ( sessionId : string ) : IQteData [ ]
{
return this . databaseServer . getTables ( ) . hideout . qte ;
}
/ * *
* Handle HideoutQuickTimeEvent on client / game / profile / items / moving
* Called after completing workout at gym
* @param sessionId Session id
* @param pmcData Profile to adjust
* @param request QTE result object
* /
// eslint-disable-next-line @typescript-eslint/no-unused-vars
2023-11-16 22:42:06 +01:00
public handleQTEEventOutcome (
sessionId : string ,
pmcData : IPmcData ,
request : IHandleQTEEventRequestData ,
) : IItemEventRouterResponse
2023-03-03 16:23:46 +01:00
{
// {
// "Action": "HideoutQuickTimeEvent",
// "results": [true, false, true, true, true, true, true, true, true, false, false, false, false, false, false],
// "id": "63b16feb5d012c402c01f6ef",
// "timestamp": 1672585349
// }
2023-11-16 22:42:06 +01:00
// Skill changes are done in
2023-03-03 16:23:46 +01:00
// /client/hideout/workout (applyWorkoutChanges).
pmcData . Health . Energy . Current -= 50 ;
if ( pmcData . Health . Energy . Current < 1 )
{
pmcData . Health . Energy . Current = 1 ;
}
pmcData . Health . Hydration . Current -= 50 ;
if ( pmcData . Health . Hydration . Current < 1 )
{
pmcData . Health . Hydration . Current = 1 ;
}
return this . eventOutputHolder . getOutput ( sessionId ) ;
}
/ * *
* Record a high score from the shooting range into a player profiles overallcounters
* @param sessionId Session id
* @param pmcData Profile to update
* @param request shooting range score request
* @returns IItemEventRouterResponse
* /
2023-11-16 22:42:06 +01:00
public recordShootingRangePoints (
sessionId : string ,
pmcData : IPmcData ,
request : IRecordShootingRangePoints ,
) : IItemEventRouterResponse
2023-03-03 16:23:46 +01:00
{
// Check if counter exists, add placeholder if it doesnt
2023-11-16 22:42:06 +01:00
if ( ! pmcData . Stats . Eft . OverallCounters . Items . find ( ( x ) = > x . Key . includes ( "ShootingRangePoints" ) ) )
2023-03-03 16:23:46 +01:00
{
2023-11-16 22:42:06 +01:00
pmcData . Stats . Eft . OverallCounters . Items . push ( { Key : [ "ShootingRangePoints" ] , Value : 0 } ) ;
2023-03-03 16:23:46 +01:00
}
// Find counter by key and update value
2023-11-16 22:42:06 +01:00
const shootingRangeHighScore = pmcData . Stats . Eft . OverallCounters . Items . find ( ( x ) = >
x . Key . includes ( "ShootingRangePoints" )
) ;
2023-03-03 16:23:46 +01:00
shootingRangeHighScore . Value = request . points ;
// Check against live, maybe a response isnt necessary
return this . eventOutputHolder . getOutput ( sessionId ) ;
}
/ * *
* Handle client / game / profile / items / moving - HideoutImproveArea
* @param sessionId Session id
2023-10-28 16:53:13 +02:00
* @param pmcData Profile to improve area in
* @param request Improve area request data
2023-03-03 16:23:46 +01:00
* /
2023-11-16 22:42:06 +01:00
public improveArea (
sessionId : string ,
pmcData : IPmcData ,
request : IHideoutImproveAreaRequestData ,
) : IItemEventRouterResponse
2023-03-03 16:23:46 +01:00
{
const output = this . eventOutputHolder . getOutput ( sessionId ) ;
// Create mapping of required item with corrisponding item from player inventory
2023-11-16 22:42:06 +01:00
const items = request . items . map ( ( reqItem ) = >
2023-03-03 16:23:46 +01:00
{
2023-11-16 22:42:06 +01:00
const item = pmcData . Inventory . items . find ( ( invItem ) = > invItem . _id === reqItem . id ) ;
return { inventoryItem : item , requestedItem : reqItem } ;
2023-03-03 16:23:46 +01:00
} ) ;
// If it's not money, its construction / barter items
for ( const item of items )
{
if ( ! item . inventoryItem )
{
2023-11-16 22:42:06 +01:00
this . logger . error (
this . localisationService . getText ( "hideout-unable_to_find_item_in_inventory" , item . requestedItem . id ) ,
) ;
2023-03-03 16:23:46 +01:00
return this . httpResponse . appendErrorToOutput ( output ) ;
}
2023-11-16 22:42:06 +01:00
if (
this . paymentHelper . isMoneyTpl ( item . inventoryItem . _tpl )
2023-03-03 16:23:46 +01:00
&& item . inventoryItem . upd
&& item . inventoryItem . upd . StackObjectsCount
2023-11-16 22:42:06 +01:00
&& item . inventoryItem . upd . StackObjectsCount > item . requestedItem . count
)
2023-03-03 16:23:46 +01:00
{
item . inventoryItem . upd . StackObjectsCount -= item . requestedItem . count ;
}
else
{
this . inventoryHelper . removeItem ( pmcData , item . inventoryItem . _id , sessionId , output ) ;
}
}
2023-11-16 22:42:06 +01:00
const profileHideoutArea = pmcData . Hideout . Areas . find ( ( x ) = > x . type === request . areaType ) ;
2023-03-03 16:23:46 +01:00
if ( ! profileHideoutArea )
{
this . logger . error ( this . localisationService . getText ( "hideout-unable_to_find_area" , request . areaType ) ) ;
return this . httpResponse . appendErrorToOutput ( output ) ;
}
2023-11-16 22:42:06 +01:00
const hideoutDbData = this . databaseServer . getTables ( ) . hideout . areas . find ( ( x ) = > x . type === request . areaType ) ;
2023-03-03 16:23:46 +01:00
if ( ! hideoutDbData )
{
2023-11-16 22:42:06 +01:00
this . logger . error (
this . localisationService . getText ( "hideout-unable_to_find_area_in_database" , request . areaType ) ,
) ;
2023-03-03 16:23:46 +01:00
return this . httpResponse . appendErrorToOutput ( output ) ;
}
// Add all improvemets to output object
const improvements = hideoutDbData . stages [ profileHideoutArea . level ] . improvements ;
const timestamp = this . timeUtil . getTimestamp ( ) ;
2023-11-30 11:02:00 +01:00
if ( ! output . profileChanges [ sessionId ] . improvements )
2023-03-03 16:23:46 +01:00
{
2023-11-30 11:02:00 +01:00
output . profileChanges [ sessionId ] . improvements = { } ;
}
2023-03-03 16:23:46 +01:00
2023-11-30 11:02:00 +01:00
for ( const improvement of improvements )
{
2023-11-16 22:42:06 +01:00
const improvementDetails = {
completed : false ,
improveCompleteTimestamp : timestamp + improvement . improvementTime ,
} ;
2023-03-03 16:23:46 +01:00
output . profileChanges [ sessionId ] . improvements [ improvement . id ] = improvementDetails ;
2023-10-10 13:03:20 +02:00
pmcData . Hideout . Improvement [ improvement . id ] = improvementDetails ;
2023-11-16 22:42:06 +01:00
}
2023-03-03 16:23:46 +01:00
return output ;
}
2023-10-28 16:53:13 +02:00
/ * *
* Handle client / game / profile / items / moving HideoutCancelProductionCommand
* @param sessionId Session id
* @param pmcData Profile with craft to cancel
* @param request Cancel production request data
2023-10-28 18:48:37 +02:00
* @returns IItemEventRouterResponse
2023-10-28 16:53:13 +02:00
* /
2023-11-16 22:42:06 +01:00
public cancelProduction (
sessionId : string ,
pmcData : IPmcData ,
request : IHideoutCancelProductionRequestData ,
) : IItemEventRouterResponse
2023-10-28 16:53:13 +02:00
{
const output = this . eventOutputHolder . getOutput ( sessionId ) ;
const craftToCancel = pmcData . Hideout . Production [ request . recipeId ] ;
if ( ! craftToCancel )
{
const errorMessage = ` Unable to find craft ${ request . recipeId } to cancel ` ;
this . logger . error ( errorMessage ) ;
return this . httpResponse . appendErrorToOutput ( output , errorMessage ) ;
}
2023-10-28 18:48:37 +02:00
// Null out production data so client gets informed when response send back
pmcData . Hideout . Production [ request . recipeId ] = null ;
2023-10-28 16:53:13 +02:00
// TODO - handle timestamp somehow?
return output ;
}
2023-03-03 16:23:46 +01:00
/ * *
* Function called every x seconds as part of onUpdate event
* /
public update ( ) : void
{
for ( const sessionID in this . saveServer . getProfiles ( ) )
{
if ( "Hideout" in this . saveServer . getProfile ( sessionID ) . characters . pmc )
{
this . hideoutHelper . updatePlayerHideout ( sessionID ) ;
}
}
}
}