Commit Description:
Add area size to Contract Window.
Commit Description:
Add area size to Contract Window.
File last commit:
Show/Diff file:
Action:
isometric-park-fna/Engines/Spawners/ContractSpawner.cs
221 lines | 7.0 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;
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 = 25;
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, 50), random_generator.Next(0, 50));
if (!occupied_squares.Contains(new_square))
{
return new_square;
}
}
// return null;
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 * 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.Warning(string.Format("Failed to generate enough squares. {0} were requested, only {1} were found ({2} attempts)", max_size, squares.Count, attempts));
break;
}
}
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;
}
foreach (var (entity, status) in ReadEntities<AreaComponent>().SelectWhereF((e) => (e, GetComponent<ContractStatusComponent>(e)),
(e) => (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 = (message.max_squares == 0) ? DEFAULT_SQUARES : message.max_squares;
int min_squares = (message.min_squares == null) ? DEFAULT_MIN_SQUARES : (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());
// foreach( double odds in odds_to_try )
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, 3),
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, CONTRACT_MAXIMUM/2),
OrganizationType.LargeCorporation => random_generator.Next(CONTRACT_MINIMUM*2, CONTRACT_MAXIMUM),
OrganizationType.Cooperative => random_generator.Next(CONTRACT_MINIMUM, CONTRACT_MAXIMUM/2),
_ => random_generator.Next(CONTRACT_MINIMUM, CONTRACT_MAXIMUM)
};
// AddComponent
AddComponent(contract, new AreaComponent { squares = squares });
AddComponent(contract, new NameAndDescriptionComponent { DisplayName = this.grammar.Flatten(message.name) });
AddComponent(contract, new SelectedComponent { selected = false });
AddComponent(contract, new ContractStatusComponent { status = ContractStatus.Proposed, date = this.simulation.DateTime });
AddComponent(contract, new TreeDeltaComponent { deltaTrees = 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});
return;
}
else {
}
}
Logging.Warning("Never generated enough squares for area.");
}
}
}