import { CorpMaterialName } from "@nsdefs"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver"; import { materialNames } from "./data/Constants"; import { Export } from "./Export"; import { MaterialInfo } from "./MaterialInfo"; interface IConstructorParams { name: CorpMaterialName; } export class Material { // Name of material name: CorpMaterialName; // Amount of material owned stored = 0; // Material's "quality". Unbounded quality = 1; // How much demand the Material has in the market, and the range of possible // values for this "demand" demand = 0; demandRange: number[] = [0, 0]; // How much competition there is for this Material in the market, and the range // of possible values for this "competition" competition = 0; competitionRange: number[] = [0, 0]; // Maximum volatility of this Materials stats maxVolatility = 0; // Markup. Determines how high of a price you can charge on the material // compared to the market price without suffering loss in # of sales // Quality is divided by this to determine markup limits // e,g, If mku is 10 and quality is 100 then you can markup prices by 100/10 = 10 markup = 0; // How much of this material is being bought, sold, imported and produced every second buyAmount = 0; actualSellAmount = 0; productionAmount = 0; importAmount = 0; // Exports of this material to another warehouse/industry exports: Export[] = []; // Total amount of this material exported in the last cycle exportedLastCycle = 0; // Cost / sec to buy this material. AKA Market Price marketPrice = 0; // Average price paid for the material (accounted as marketPrice for produced/imported materials) averagePrice = 0; /** null if there is no limit set on production. 0 actually limits production to 0. */ productionLimit: number | null = null; // Player inputs for sell price and amount. desiredSellAmount: string | number = 0; desiredSellPrice: string | number = ""; // Flags that signal whether automatic sale pricing through Market TA is enabled marketTa1 = false; marketTa2 = false; uiMarketPrice = 0; // Determines the maximum amount of this material that can be sold in one market cycle maxSellPerCycle = 0; constructor(params?: IConstructorParams) { this.name = params?.name ?? materialNames[0]; this.demand = MaterialInfo[this.name].demandBase; this.demandRange = MaterialInfo[this.name].demandRange; this.competition = MaterialInfo[this.name].competitionBase; this.competitionRange = MaterialInfo[this.name].competitionRange; this.marketPrice = MaterialInfo[this.name].baseCost; this.averagePrice = this.marketPrice; this.maxVolatility = MaterialInfo[this.name].maxVolatility; this.markup = MaterialInfo[this.name].baseMarkup; } getMarkupLimit(): number { return this.quality / this.markup; } // Process change in demand, competition, and buy cost of this material processMarket(): void { // The price will change in accordance with demand and competition. // e.g. If demand goes up, then so does price. If competition goes up, price goes down const priceVolatility: number = (Math.random() * this.maxVolatility) / 300; const priceChange: number = 1 + priceVolatility; //This 1st random check determines whether competition increases or decreases const compVolatility: number = (Math.random() * this.maxVolatility) / 100; const compChange: number = 1 + compVolatility; if (Math.random() < 0.5) { this.competition *= compChange; if (this.competition > this.competitionRange[1]) { this.competition = this.competitionRange[1]; } this.marketPrice *= 1 / priceChange; // Competition increases, so price goes down } else { this.competition *= 1 / compChange; if (this.competition < this.competitionRange[0]) { this.competition = this.competitionRange[0]; } this.marketPrice *= priceChange; // Competition decreases, so price goes up } // This 2nd random check determines whether demand increases or decreases const dmdVolatility: number = (Math.random() * this.maxVolatility) / 100; const dmdChange: number = 1 + dmdVolatility; if (Math.random() < 0.5) { this.demand *= dmdChange; if (this.demand > this.demandRange[1]) { this.demand = this.demandRange[1]; } this.marketPrice *= priceChange; // Demand increases, so price goes up } else { this.demand *= 1 / dmdChange; if (this.demand < this.demandRange[0]) { this.demand = this.demandRange[0]; } this.marketPrice *= 1 / priceChange; } } // Serialize the current object to a JSON save state. toJSON(): IReviverValue { return Generic_toJSON("Material", this); } // Initializes a Material object from a JSON save state. static fromJSON(value: IReviverValue): Material { const material = Generic_fromJSON(Material, value.data); if (isNaN(material.quality)) { material.quality = 1; } /** * averagePrice has not been calculated properly, so if it is an invalid value (Number.isFinite returns false) or 0 * (wrong initial value), we set it to marketPrice. */ if (!Number.isFinite(material.averagePrice) || material.averagePrice === 0) { material.averagePrice = material.marketPrice; } return material; } } constructorsForReviver.Material = Material;