Commit Description:
Spawn trees differently in preserves.
Commit Description:
Spawn trees differently in preserves.
Show/Diff file:
Action:
isometric-park-fna/Engines/Spawners/ContractSpawner.cs
234 lines | 10.3 KiB | text/x-csharp | CSharpLexer
using System;
using System.Diagnostics;
using System.Linq;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Encompass;
using JM.LinqFaster;
using TraceryNet;
using isometricparkfna.Messages;
using isometricparkfna.Components;
using isometricparkfna.Utils;
namespace isometricparkfna.Spawners {
[Receives(typeof(SpawnContractMessage))]
[Reads(typeof(AreaComponent), typeof(ContractStatusComponent),
typeof(OffersContractsComponent), typeof(OrganizationTypeComponent))]
class ContractSpawner : Spawner<SpawnContractMessage>
{
private Random random_generator;
public const int DEFAULT_MIN_SQUARES = 75;
public const int DEFAULT_SQUARES = 100;
public const int CONTRACT_MINIMUM = 50;
public const int CONTRACT_MAXIMUM = 400;
private int MapWidth;
private int MapHeight;
private Simulation simulation;
private Grammar grammar;
public ContractSpawner(int mapWidth, int mapHeight, Simulation simulation, Grammar grammar)
{
this.random_generator = new Random();
this.MapWidth = mapWidth;
this.MapHeight = mapHeight;
this.simulation = simulation;
this.grammar = grammar;
}
private Vector2 FindStart(HashSet<Vector2> occupied_squares)
{
Vector2 new_square;
for (int i = 0; i < 5; i++)
{
new_square = new Vector2(random_generator.Next(0, this.MapHeight), random_generator.Next(0, this.MapWidth));
if (!occupied_squares.Contains(new_square))
{
return new_square;
}
}
return Vector2.Zero;
}
private Vector2[] CreateArea(int start_x, int start_y, int max_size, HashSet<Vector2> occupied_squares, float odds = 0.5f)
{
var squares = new List<Vector2>(new[] { new Vector2(start_x, start_y) });
var squares_to_add = new HashSet<Vector2>();
var attempts = 0;
var maxAttempts = max_size * this.simulation.map.LinearMultiplier * 10;
while (squares.Count < max_size)
{
foreach (var square in squares)
{
foreach (var new_square in new[] {new Vector2(square.X + 1, square.Y),
new Vector2(square.X, square.Y + 1),
new Vector2(square.X - 1, square.Y),
new Vector2(square.X, square.Y - 1)
})
{
if (random_generator.NextDouble() < odds
&& !squares.Contains(new_square)
&& !occupied_squares.Contains(new_square)
&& MathUtils.Between(new_square.X, 0, this.MapWidth)
&& MathUtils.Between(new_square.Y, 0, this.MapHeight)
)
{
squares_to_add.Add(new_square);
}
attempts += 1;
}
}
var remaining = max_size - squares.Count;
squares.AddRange(squares_to_add.Take(remaining));
squares_to_add.Clear();
if (attempts >= maxAttempts && squares.Count < max_size)
{
Logging.Info(string.Format("Attempt to generate enough squares failed. {0} were requested, only {1} were found ({2} attempts)", max_size, squares.Count, attempts));
break;
}
}
if (squares.Count >= max_size)
{
Logging.Debug(string.Format("Successfully generated enough squares. {0} squares were requested, {1} were found ({2} attempts).", max_size, squares.Count, attempts));
}
return squares.ToArray();
}
protected override void Spawn(in SpawnContractMessage message)
{
//for now:
var occupied = new List<Vector2>();
var contractOrganizations = ReadEntities<OffersContractsComponent>().ToArray();
var contractCount = contractOrganizations.Count();
OrganizationType organization_type = OrganizationType.Unspecified;
var organization_index = random_generator.Next(0, contractCount);
if (contractCount > 0)
{
organization_type = GetComponent<OrganizationTypeComponent>(contractOrganizations[organization_index]).type;
}
else {
Logging.Debug(String.Format("Creating contract {0} without organizations.", message.name ));
}
foreach (var (entity, status) in ReadEntities<AreaComponent>()
.WhereF((e) => HasComponent<ContractStatusComponent>(e))
.SelectWhereF((e) => (e, GetComponent<ContractStatusComponent>(e)), (e) => ((e.Item2.status != ContractStatus.Broken) && (e.Item2.status != ContractStatus.Rejected) && (e.Item2.status != ContractStatus.Expired)))) {
var entitySquares = GetComponent<AreaComponent>(entity).squares;
occupied.AddRange(entitySquares);
}
var start = this.FindStart(new HashSet<Vector2>(occupied));
var start_x = (int)start.X;
var start_y = (int)start.Y;
var image_index = random_generator.Next(1, 7);
int max_squares = (organization_type, message.min_squares) switch {
(OrganizationType.Family, null) => random_generator.Next(50, 100 * this.simulation.map.LinearMultiplier),
(OrganizationType.LargeCorporation, null) => random_generator.Next(90, 250 * this.simulation.map.LinearMultiplier),
(OrganizationType.Cooperative, null) => random_generator.Next(50, 75 * this.simulation.map.LinearMultiplier),
(_, null) => random_generator.Next(DEFAULT_MIN_SQUARES, DEFAULT_SQUARES * this.simulation.map.LinearMultiplier),
_ => (message.max_squares == 0) ? DEFAULT_SQUARES : message.max_squares
};
int min_squares = (message.min_squares == null) ? (int)(max_squares*0.75f) : (int)message.min_squares;
var odds_to_try = new[] {0.5f, 0.75f, 1.0f, 0.5f, 0.75f, 1.0f};
var retry_location = new[] {false, false, false, true, false, false};
Debug.Assert(odds_to_try.Count() == retry_location.Count());
for(int i = 0; i < odds_to_try.Count(); i++)
{
Vector2[] squares = (message.squares == null) ?
CreateArea(start_x, start_y, max_squares, new HashSet<Vector2>(occupied)) : message.squares;
if(i > 0)
{
Logging.Info(String.Format("Creating area using a {0} chance didn't work; using {1}.", odds_to_try[i-1], odds_to_try[i]));
}
if (retry_location[i])
{
var old_start_x = start_x;
var old_start_y = start_y;
start = this.FindStart(new HashSet<Vector2>(occupied));
start_x = (int)start.X;
start_y = (int)start.Y;
Logging.Info(String.Format("Creating area using original location ({0}, {1}) didn't work; trying a new one ({2} {3}).",
old_start_x, old_start_y, start_x, start_y));
}
if (squares.Length > min_squares)
{
var contract = CreateEntity();
if (contractCount > 0)
{
AddComponent(contract, new RelatedOrganizationComponent { Entity = contractOrganizations[organization_index] });
}
var deltaTrees = organization_type switch {
OrganizationType.Family => random_generator.Next(-10, 0),
OrganizationType.LargeCorporation => random_generator.Next(-20, 0),
OrganizationType.Cooperative => random_generator.Next(-1, 3),
_ => random_generator.Next(-20, 3)
};
var contract_amount = organization_type switch {
OrganizationType.Family => random_generator.Next(CONTRACT_MINIMUM, (int)(CONTRACT_MAXIMUM/2)),
OrganizationType.LargeCorporation => random_generator.Next(CONTRACT_MINIMUM*3, (int)(CONTRACT_MAXIMUM*1.5)),
OrganizationType.Cooperative => random_generator.Next(CONTRACT_MINIMUM, (int)(CONTRACT_MAXIMUM/2.5)),
_ => random_generator.Next(CONTRACT_MINIMUM, CONTRACT_MAXIMUM)
};
AddComponent(contract, new AreaComponent { squares = squares });
var nameAndDescription = new NameAndDescriptionComponent { DisplayName = this.grammar.Flatten(message.name) };
AddComponent(contract, nameAndDescription);
AddComponent(contract, new SelectedComponent { selected = false, Type = SelectionType.Window });
AddComponent(contract, new ContractStatusComponent { status = ContractStatus.Proposed, date = this.simulation.DateTime });
AddComponent(contract, new TreeDeltaComponent { deltaTrees = new Fact<int>(deltaTrees) });
AddComponent(contract, new BudgetLineComponent
{
category = "Contracts",
//Round to the nearest $5
amount = (decimal)((contract_amount / 5) * 5)
});
AddComponent(contract, new ImageComponent {ImageIndex = image_index});
AddComponent(contract, new WindowTypeComponent { type = Window.Contract});
AddComponent(contract, new VisibilityComponent { visible = false});
Logging.Info(String.Format("Generated contract {0}: ", nameAndDescription));
return;
}
else {
}
}
Logging.Warning(string.Format("Never generated enough squares for area (requested {0}).", max_squares));
}
}
}