Initial commit
This commit is contained in:
479
mod/PLibBuildings/PBuilding.cs
Normal file
479
mod/PLibBuildings/PBuilding.cs
Normal file
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* Copyright 2022 Peter Han
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using PeterHan.PLib.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace PeterHan.PLib.Buildings {
|
||||
/// <summary>
|
||||
/// A class used for creating new buildings. Abstracts many of the details to allow them
|
||||
/// to be used across different game versions.
|
||||
/// </summary>
|
||||
public sealed partial class PBuilding {
|
||||
/// <summary>
|
||||
/// The building ID which should precede this building ID in the plan menu.
|
||||
/// </summary>
|
||||
public string AddAfter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the building is always operational.
|
||||
/// </summary>
|
||||
public bool AlwaysOperational { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The building's animation.
|
||||
/// </summary>
|
||||
public string Animation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The audio sounds used when placing/completing the building.
|
||||
/// </summary>
|
||||
public string AudioCategory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The audio volume used when placing/completing the building.
|
||||
/// </summary>
|
||||
public string AudioSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this building can break down.
|
||||
/// </summary>
|
||||
public bool Breaks { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The build menu category.
|
||||
/// </summary>
|
||||
public HashedString Category { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The construction time in seconds on x1 speed.
|
||||
/// </summary>
|
||||
public float ConstructionTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The decor of this building.
|
||||
/// </summary>
|
||||
public EffectorValues Decor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The building description.
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Text describing the building's effect.
|
||||
/// </summary>
|
||||
public string EffectText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this building can entomb.
|
||||
/// </summary>
|
||||
public bool Entombs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The heat generation from the exhaust in kDTU/s.
|
||||
/// </summary>
|
||||
public float ExhaustHeatGeneration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this building can flood.
|
||||
/// </summary>
|
||||
public bool Floods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The default priority of this building, with null to not add a priority.
|
||||
/// </summary>
|
||||
public int? DefaultPriority { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The self-heating when active in kDTU/s.
|
||||
/// </summary>
|
||||
public float HeatGeneration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The building height.
|
||||
/// </summary>
|
||||
public int Height { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The building HP until it breaks down.
|
||||
/// </summary>
|
||||
public int HP { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ingredients required for construction.
|
||||
/// </summary>
|
||||
public IList<BuildIngredient> Ingredients { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The building ID.
|
||||
/// </summary>
|
||||
public string ID { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this building is an industrial machine.
|
||||
/// </summary>
|
||||
public bool IndustrialMachine { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The input conduits.
|
||||
/// </summary>
|
||||
public IList<ConduitConnection> InputConduits { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this building is (or can be) a solid tile.
|
||||
/// </summary>
|
||||
public bool IsSolidTile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The logic ports.
|
||||
/// </summary>
|
||||
public IList<LogicPorts.Port> LogicIO { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The building name.
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The noise of this building (not used by Klei).
|
||||
/// </summary>
|
||||
public EffectorValues Noise { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The layer for this building.
|
||||
/// </summary>
|
||||
public ObjectLayer ObjectLayer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The output conduits.
|
||||
/// </summary>
|
||||
public IList<ConduitConnection> OutputConduits { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If null, the building does not overheat; otherwise, it overheats at this
|
||||
/// temperature in K.
|
||||
/// </summary>
|
||||
public float? OverheatTemperature { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The location where this building may be built.
|
||||
/// </summary>
|
||||
public BuildLocationRule Placement { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If null, the building has no power input; otherwise, it uses this much power.
|
||||
/// </summary>
|
||||
public PowerRequirement PowerInput { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If null, the building has no power output; otherwise, it provides this much power.
|
||||
/// </summary>
|
||||
public PowerRequirement PowerOutput { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The directions this building can face.
|
||||
/// </summary>
|
||||
public PermittedRotations RotateMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The scene layer for this building.
|
||||
/// </summary>
|
||||
public Grid.SceneLayer SceneLayer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The subcategory for this building.
|
||||
///
|
||||
/// The base game currently defines the following:
|
||||
/// Base:
|
||||
/// ladders, tiles, printing pods, doors, storage, tubes, default
|
||||
/// Oxygen:
|
||||
/// producers, scrubbers
|
||||
/// Power:
|
||||
/// generators, wires, batteries, transformers, switches
|
||||
/// Food:
|
||||
/// cooking, farming, ranching
|
||||
/// Plumbing:
|
||||
/// bathroom, pipes, pumps, valves, sensors
|
||||
/// HVAC:
|
||||
/// pipes, pumps, valves, sensors
|
||||
/// Refining:
|
||||
/// materials, oil, advanced
|
||||
/// Medical:
|
||||
/// cleaning, hospital, wellness
|
||||
/// Furniture:
|
||||
/// bed, lights, dining, recreation, pots, sculpture, electronic decor, moulding,
|
||||
/// canvas, dispaly, monument, signs
|
||||
/// Equipment:
|
||||
/// research, exploration, work stations, suits general, oxygen masks, atmo suits,
|
||||
/// jet suits, lead suits
|
||||
/// Utilities:
|
||||
/// temperature, other utilities, special
|
||||
/// Automation:
|
||||
/// wires, sensors, logic gates, utilities
|
||||
/// Solid Transport:
|
||||
/// conduit, valves, utilities
|
||||
/// Rocketry:
|
||||
/// telescopes, launch pad, railguns, engines, fuel and oxidizer, cargo, utility,
|
||||
/// command, fittings
|
||||
/// Radiation:
|
||||
/// HEP, uranium, radiation
|
||||
/// </summary>
|
||||
public string SubCategory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The technology name required to unlock the building.
|
||||
/// </summary>
|
||||
public string Tech { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The view mode used when placing this building.
|
||||
/// </summary>
|
||||
public HashedString ViewMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The building width.
|
||||
/// </summary>
|
||||
public int Width { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the building was added to the plan menu.
|
||||
/// </summary>
|
||||
private bool addedPlan;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the strings were added.
|
||||
/// </summary>
|
||||
private bool addedStrings;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the technology wes added.
|
||||
/// </summary>
|
||||
private bool addedTech;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new building. All buildings thus created must be registered using
|
||||
/// PBuilding.Register and have an appropriate IBuildingConfig class.
|
||||
///
|
||||
/// Building should be created in OnLoad or a post-load patch (not in static
|
||||
/// initializers) to give the localization framework time to patch the LocString
|
||||
/// containing the building name and description.
|
||||
/// </summary>
|
||||
/// <param name="id">The building ID.</param>
|
||||
/// <param name="name">The building name.</param>
|
||||
public PBuilding(string id, string name) {
|
||||
if (string.IsNullOrEmpty(id))
|
||||
throw new ArgumentNullException(nameof(id));
|
||||
if (string.IsNullOrEmpty(name))
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
AddAfter = null;
|
||||
AlwaysOperational = false;
|
||||
Animation = "";
|
||||
AudioCategory = "Metal";
|
||||
AudioSize = "medium";
|
||||
Breaks = true;
|
||||
Category = DEFAULT_CATEGORY;
|
||||
ConstructionTime = 10.0f;
|
||||
Decor = TUNING.BUILDINGS.DECOR.NONE;
|
||||
DefaultPriority = null;
|
||||
Description = "Default Building Description";
|
||||
EffectText = "Default Building Effect";
|
||||
Entombs = true;
|
||||
ExhaustHeatGeneration = 0.0f;
|
||||
Floods = true;
|
||||
HeatGeneration = 0.0f;
|
||||
Height = 1;
|
||||
Ingredients = new List<BuildIngredient>(4);
|
||||
IndustrialMachine = false;
|
||||
InputConduits = new List<ConduitConnection>(4);
|
||||
HP = 100;
|
||||
ID = id;
|
||||
LogicIO = new List<LogicPorts.Port>(4);
|
||||
Name = name;
|
||||
Noise = TUNING.NOISE_POLLUTION.NONE;
|
||||
ObjectLayer = PGameUtils.GetObjectLayer(nameof(ObjectLayer.Building), ObjectLayer.
|
||||
Building);
|
||||
OutputConduits = new List<ConduitConnection>(4);
|
||||
OverheatTemperature = null;
|
||||
Placement = BuildLocationRule.OnFloor;
|
||||
PowerInput = null;
|
||||
PowerOutput = null;
|
||||
RotateMode = PermittedRotations.Unrotatable;
|
||||
SceneLayer = Grid.SceneLayer.Building;
|
||||
// Hard coded strings in base game, no const to reference
|
||||
SubCategory = "default";
|
||||
Tech = null;
|
||||
ViewMode = OverlayModes.None.ID;
|
||||
Width = 1;
|
||||
|
||||
addedPlan = false;
|
||||
addedStrings = false;
|
||||
addedTech = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the building def from this class.
|
||||
/// </summary>
|
||||
/// <returns>The Klei building def.</returns>
|
||||
public BuildingDef CreateDef() {
|
||||
// The number of fields in BuildingDef makes it somewhat impractical to detour
|
||||
if (Width < 1)
|
||||
throw new InvalidOperationException("Building width: " + Width);
|
||||
if (Height < 1)
|
||||
throw new InvalidOperationException("Building height: " + Height);
|
||||
if (HP < 1)
|
||||
throw new InvalidOperationException("Building HP: " + HP);
|
||||
if (ConstructionTime.IsNaNOrInfinity())
|
||||
throw new InvalidOperationException("Construction time: " + ConstructionTime);
|
||||
// Build an ingredients list
|
||||
int n = Ingredients.Count;
|
||||
if (n < 1)
|
||||
throw new InvalidOperationException("No ingredients for build");
|
||||
float[] quantity = new float[n];
|
||||
string[] tag = new string[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
var ingredient = Ingredients[i];
|
||||
if (ingredient == null)
|
||||
throw new ArgumentNullException(nameof(ingredient));
|
||||
quantity[i] = ingredient.Quantity;
|
||||
tag[i] = ingredient.Material;
|
||||
}
|
||||
// Melting point is not currently used
|
||||
var def = BuildingTemplates.CreateBuildingDef(ID, Width, Height, Animation, HP,
|
||||
Math.Max(0.1f, ConstructionTime), quantity, tag, 2400.0f, Placement, Decor,
|
||||
Noise);
|
||||
// Solid tile?
|
||||
if (IsSolidTile) {
|
||||
//def.isSolidTile = true;
|
||||
def.BaseTimeUntilRepair = -1.0f;
|
||||
def.UseStructureTemperature = false;
|
||||
BuildingTemplates.CreateFoundationTileDef(def);
|
||||
}
|
||||
def.AudioCategory = AudioCategory;
|
||||
def.AudioSize = AudioSize;
|
||||
if (OverheatTemperature != null) {
|
||||
def.Overheatable = true;
|
||||
def.OverheatTemperature = OverheatTemperature ?? 348.15f;
|
||||
} else
|
||||
def.Overheatable = false;
|
||||
// Plug in
|
||||
if (PowerInput != null) {
|
||||
def.RequiresPowerInput = true;
|
||||
def.EnergyConsumptionWhenActive = PowerInput.MaxWattage;
|
||||
def.PowerInputOffset = PowerInput.PlugLocation;
|
||||
}
|
||||
// Plug out
|
||||
if (PowerOutput != null) {
|
||||
def.RequiresPowerOutput = true;
|
||||
def.GeneratorWattageRating = PowerOutput.MaxWattage;
|
||||
def.PowerOutputOffset = PowerOutput.PlugLocation;
|
||||
}
|
||||
def.Breakable = Breaks;
|
||||
def.PermittedRotations = RotateMode;
|
||||
def.ExhaustKilowattsWhenActive = ExhaustHeatGeneration;
|
||||
def.SelfHeatKilowattsWhenActive = HeatGeneration;
|
||||
def.Floodable = Floods;
|
||||
def.Entombable = Entombs;
|
||||
def.ObjectLayer = ObjectLayer;
|
||||
def.SceneLayer = SceneLayer;
|
||||
def.ViewMode = ViewMode;
|
||||
// Conduits (multiple per building are hard but will be added someday...)
|
||||
if (InputConduits.Count > 1)
|
||||
throw new InvalidOperationException("Only supports one input conduit");
|
||||
foreach (var conduit in InputConduits) {
|
||||
def.UtilityInputOffset = conduit.Location;
|
||||
def.InputConduitType = conduit.Type;
|
||||
}
|
||||
if (OutputConduits.Count > 1)
|
||||
throw new InvalidOperationException("Only supports one output conduit");
|
||||
foreach (var conduit in OutputConduits) {
|
||||
def.UtilityOutputOffset = conduit.Location;
|
||||
def.OutputConduitType = conduit.Type;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the building template of this building. Should be called in
|
||||
/// ConfigureBuildingTemplate.
|
||||
/// </summary>
|
||||
/// <param name="go">The game object to configure.</param>
|
||||
public void ConfigureBuildingTemplate(GameObject go) {
|
||||
if (AlwaysOperational)
|
||||
ApplyAlwaysOperational(go);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates the logic ports of this building. Must be used <b>after</b> the
|
||||
/// PBuilding.DoPostConfigureComplete method if logic ports are required.
|
||||
///
|
||||
/// Should be called in DoPostConfigureComplete, DoPostConfigurePreview, and
|
||||
/// DoPostConfigureUnderConstruction.
|
||||
/// </summary>
|
||||
/// <param name="go">The game object to configure.</param>
|
||||
public void CreateLogicPorts(GameObject go) {
|
||||
SplitLogicPorts(go);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the post-configure complete steps that this building object can do.
|
||||
/// Not exhaustive! Other components must likely be added.
|
||||
///
|
||||
/// This method does NOT add the logic ports. Use CreateLogicPorts to do so,
|
||||
/// <b>after</b> this method has been invoked.
|
||||
/// </summary>
|
||||
/// <param name="go">The game object to configure.</param>
|
||||
public void DoPostConfigureComplete(GameObject go) {
|
||||
if (InputConduits.Count == 1) {
|
||||
var conduitConsumer = go.AddOrGet<ConduitConsumer>();
|
||||
foreach (var conduit in InputConduits) {
|
||||
conduitConsumer.alwaysConsume = true;
|
||||
conduitConsumer.conduitType = conduit.Type;
|
||||
conduitConsumer.wrongElementResult = ConduitConsumer.WrongElementResult.
|
||||
Store;
|
||||
}
|
||||
}
|
||||
if (OutputConduits.Count == 1) {
|
||||
var conduitDispenser = go.AddOrGet<ConduitDispenser>();
|
||||
foreach (var conduit in OutputConduits) {
|
||||
conduitDispenser.alwaysDispense = true;
|
||||
conduitDispenser.conduitType = conduit.Type;
|
||||
conduitDispenser.elementFilter = null;
|
||||
}
|
||||
}
|
||||
if (IndustrialMachine && go.TryGetComponent(out KPrefabID id))
|
||||
id.AddTag(RoomConstraints.ConstraintTags.IndustrialMachinery, false);
|
||||
if (PowerInput != null)
|
||||
go.AddOrGet<EnergyConsumer>();
|
||||
if (PowerOutput != null)
|
||||
go.AddOrGet<EnergyGenerator>();
|
||||
// Set a default priority
|
||||
if (DefaultPriority != null && go.TryGetComponent(out Prioritizable pr)) {
|
||||
Prioritizable.AddRef(go);
|
||||
pr.SetMasterPriority(new PrioritySetting(PriorityScreen.PriorityClass.basic,
|
||||
DefaultPriority ?? 5));
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return "PBuilding[ID={0}]".F(ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user