|
|
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.");
|
|
|
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|