/// <summary> /// Checks whetever actions and observations in this query are consistent with those /// </summary> /// <param name="state"></param> /// <param name="worldAction"></param> /// <param name="time"></param> /// <returns></returns> public override QueryResult CheckCondition(World.State state, World.WorldAction worldAction, int time) { _logger.Info("Checking if scenario: " + this._scenario + " with parameters:\nstate: " + state + "\naction: " + worldAction); var result = QueryResult.Undefined; if (this._scenario.observations.Any(sor => sor.Time.Equals(time) && !sor.Expr.Evaluate(state))) { result = QueryResult.False; } if (result == QueryResult.True) { if (this._scenario.actions.Any(sar => sar.Time.Equals(time) && sar.CheckIfActiveAt(time) && !sar.WorldAction.Equals(worldAction))) { result = QueryResult.False; } } string logResult = "Executable: " + result; if (QueryResult.Undefined == result) { _logger.Warn(logResult); } else { _logger.Info(logResult); } return(result); }
public void Update(World world, World.State state) { int index = world.GetIndex(Position.x, Position.y); AirTemperature[CurSampleIndex] = state.LowerAirTemperature[index]; Pressure[CurSampleIndex] = state.LowerAirPressure[index]; Humidity[CurSampleIndex] = state.Humidity[index]; CloudCover[CurSampleIndex] = state.CloudMass[index]; Rainfall[CurSampleIndex] = state.Rainfall[index]; GroundWater[CurSampleIndex] = state.GroundWater[index]; Canopy[CurSampleIndex] = state.Canopy[index]; CurSampleIndex = (CurSampleIndex + 1) % SampleCount; TotalSamples = Math.Min(SampleCount, TotalSamples + 1); }
public override QueryResult CheckCondition(World.State state, World.WorldAction worldAction, int time) { _logger.Info("Checking Action: " + this._worldAction + "at time: " + this._time + "\nwith parameters:\nstate: " + state + "\naction: " + worldAction); var result = QueryResult.Undefined; if (time == _time || _time == -1) { if (this._worldAction == null) { if (worldAction == null) { result = QueryResult.True; } else { result = QueryResult.False; } } else if (this._worldAction.Equals(worldAction)) { result = QueryResult.True; } else { result = QueryResult.False; } } else if (_time < time) { result = QueryResult.False; } else if (_time > time) { result = QueryResult.Undefined; } string logResult = "Method result: " + result; if (QueryResult.Undefined == result) { _logger.Warn(logResult); } else { _logger.Info(logResult); } return(result); }
static public void MoveTile(World world, World.State state, World.State nextState, int index, int newIndex) { nextState.Plate[newIndex] = state.Plate[index]; nextState.Elevation[newIndex] = state.Elevation[index]; nextState.Canopy[newIndex] = state.Canopy[index]; nextState.WaterTableDepth[newIndex] = state.WaterTableDepth[index]; nextState.GroundWater[newIndex] = state.GroundWater[index]; nextState.ShallowSaltMass[newIndex] = state.ShallowSaltMass[index]; nextState.DeepSaltMass[newIndex] = state.DeepSaltMass[index]; nextState.ShallowWaterTemperature[newIndex] = state.ShallowWaterTemperature[index]; nextState.ShallowWaterEnergy[newIndex] = state.ShallowWaterEnergy[index]; nextState.DeepWaterEnergy[newIndex] = state.DeepWaterEnergy[index]; nextState.IceMass[newIndex] = state.IceMass[index]; nextState.SoilFertility[newIndex] = state.SoilFertility[index]; }
/// <summary> /// Checks given parameters with conditionAtTimeQuery and ExecutableScenarioQuery /// </summary> /// <param name="state"></param> /// <param name="worldAction"></param> /// <param name="time"></param> /// <returns></returns> public override QueryResult CheckCondition(World.State state, World.WorldAction worldAction, int time) { _logger.Info("Checking condition: " + this._condition + "\n accesible with parameters:\nstate: " + state + "\naction: " + worldAction); QueryResult condAtTimeResult = CondAtTimeQuery.CheckCondition(state, worldAction, time); QueryResult execResult = ExecQuery.CheckCondition(state, worldAction, time); QueryResult result; if (condAtTimeResult == QueryResult.Error || condAtTimeResult == QueryResult.Error) { result = QueryResult.Error; } else { if (condAtTimeResult == QueryResult.False || condAtTimeResult == QueryResult.False) { result = QueryResult.False; } else { if (condAtTimeResult == QueryResult.Undefined || condAtTimeResult == QueryResult.Undefined) { result = QueryResult.Undefined; } else { result = QueryResult.True; } } } string logResult = "Accesible: " + result; if (QueryResult.Undefined == result) { _logger.Warn(logResult); } else { _logger.Info(logResult); } return(result); }
static public void GetNormalAndFlow(World world, World.State state, int x, int y, int index, float elevation, float soilFertility, out Vector2 groundWaterFlowDirection, out Vector4 shallowWaterFlow, out Vector3 normal) { int indexW = world.GetIndex(world.WrapX(x - 1), y); int indexE = world.GetIndex(world.WrapX(x + 1), y); int indexN = world.GetIndex(x, world.WrapY(y + 1)); int indexS = world.GetIndex(x, world.WrapY(y - 1)); float e = state.Elevation[index]; float west = e - state.Elevation[indexW]; float east = e - state.Elevation[indexE]; float north = e - state.Elevation[indexN]; float south = e - state.Elevation[indexS]; var g = new Vector2(east > west ? Mathf.Max(0, east) : -Mathf.Max(0, west), north > south ? Mathf.Max(0, north) : -Mathf.Max(0, south)); groundWaterFlowDirection = g * world.Data.InverseMetersPerTile * world.Data.GroundWaterFlowSpeed * soilFertility * world.Data.GravitationalAcceleration; shallowWaterFlow = Vector4.zero; float depth = state.WaterAndIceDepth[index]; if (depth > 0) { west += (depth - state.WaterAndIceDepth[indexW]); east += (depth - state.WaterAndIceDepth[indexE]); north += (depth - state.WaterAndIceDepth[indexN]); south += (depth - state.WaterAndIceDepth[indexS]); shallowWaterFlow.x = Mathf.Max(0, west / 2) * world.Data.InverseMetersPerTile * world.Data.GravitationalAcceleration * world.Data.FlowSpeed * world.Data.SecondsPerTick; shallowWaterFlow.y = Mathf.Max(0, east / 2) * world.Data.InverseMetersPerTile * world.Data.GravitationalAcceleration * world.Data.FlowSpeed * world.Data.SecondsPerTick; shallowWaterFlow.z = Mathf.Max(0, north / 2) * world.Data.InverseMetersPerTile * world.Data.GravitationalAcceleration * world.Data.FlowSpeed * world.Data.SecondsPerTick; shallowWaterFlow.w = Mathf.Max(0, south / 2) * world.Data.InverseMetersPerTile * world.Data.GravitationalAcceleration * world.Data.FlowSpeed * world.Data.SecondsPerTick; float totalFlow = shallowWaterFlow.x + shallowWaterFlow.y + shallowWaterFlow.z + shallowWaterFlow.w; if (totalFlow > depth) { shallowWaterFlow /= totalFlow; } else if (depth > 0) { shallowWaterFlow /= depth; } shallowWaterFlow *= world.Data.FlowMax; } normal = Vector3.Normalize(new Vector3((west - east) / 2, (south - north) / 2, world.Data.MetersPerTile)); }
static private Vector2 GetPressureGradient(World world, World.State state, int x, int y, float[] neighborPressure, float[] neighborTemperature, bool isUpper, float pressureAtWindElevation, float windElevation, float molarMass) { Vector2 pressureDifferential = Vector2.zero; Vector2 nWind = Vector2.zero; for (int i = 0; i < 4; i++) { var nIndex = world.GetNeighborIndex(x, y, i); // see bottom of: https://en.wikipedia.org/wiki/Vertical_pressure_variation float neighborElevation = state.Elevation[nIndex] + state.WaterAndIceDepth[nIndex]; float neighborTemperatureElevation; if (isUpper) { neighborTemperatureElevation = neighborElevation + world.Data.BoundaryZoneElevation; } else { neighborTemperatureElevation = neighborElevation; } float neighborTemperatureAtSeaLevel = neighborTemperature[nIndex] - neighborTemperatureElevation * world.Data.TemperatureLapseRate; float neighborElevationAtPressure = neighborTemperatureAtSeaLevel / world.Data.TemperatureLapseRate * (Mathf.Pow(pressureAtWindElevation / neighborPressure[nIndex], -1.0f / (world.Data.PressureExponent * molarMass)) - 1); switch (i) { case 0: pressureDifferential.x += neighborElevationAtPressure - windElevation; break; case 1: pressureDifferential.x -= neighborElevationAtPressure - windElevation; break; case 2: pressureDifferential.y -= neighborElevationAtPressure - windElevation; break; case 3: pressureDifferential.y += neighborElevationAtPressure - windElevation; break; } } return(pressureDifferential); }
static public void Tick(World world, World.State state, World.State nextState) { _ProfileEarthTick.Begin(); for (int y = 0; y < world.Size; y++) { for (int x = 0; x < world.Size; x++) { int index = world.GetIndex(x, y); float elevation = state.Elevation[index]; Vector4 newShallowWaterFlow; Vector2 newFlowDirectionGroundWater; Vector3 newNormal; GetNormalAndFlow(world, state, x, y, index, elevation, state.SoilFertility[index], out newFlowDirectionGroundWater, out newShallowWaterFlow, out newNormal); nextState.FlowDirectionGroundWater[index] = newFlowDirectionGroundWater; nextState.ShallowWaterFlow[index] = newShallowWaterFlow; nextState.Normal[index] = newNormal; } } _ProfileEarthTick.End(); }
static public void Tick(World world, World.State state, World.State nextState) { _ProfileEarthTick.Begin(); float inverseFullIceCoverage = 1.0f / (world.Data.MassIce * world.Data.FullIceCoverage); for (int y = 0; y < world.Size; y++) { for (int x = 0; x < world.Size; x++) { int index = world.GetIndex(x, y); // Foliage //float freshWaterAvailability = 0; float canopy = state.Canopy[index]; float airHeat = state.LowerAirTemperature[index]; float newCanopy = canopy; // if (canopy > 0) { float waterCoverage = Mathf.Clamp01(state.WaterDepth[index] / world.Data.FullWaterCoverage); float t = state.LowerAirTemperature[index]; float sf = state.SoilFertility[index]; //float groundWaterSaturation = GetGroundWaterSaturation(state.GroundWater[index], state.WaterTableDepth[index], sf * world.Data.MaxSoilPorousness); //float surfaceWater = state.SurfaceWater[index]; //freshWaterAvailability = GetFreshWaterAvailability(surfaceWater, groundWaterSaturation); float iceCoverage = Mathf.Clamp01(state.IceMass[index] * inverseFullIceCoverage); float desiredCanopy = sf * (state.Rainfall[index] + state.GroundWater[index]) * (1.0f - waterCoverage) * (1.0f - iceCoverage) * Mathf.Clamp01((t - world.Data.MinTemperatureCanopy) / (world.Data.MaxTemperatureCanopy - world.Data.MinTemperatureCanopy)); float canopyGrowth = (desiredCanopy - canopy) * world.Data.canopyGrowthRate; newCanopy += canopyGrowth; //float expansion = canopy * canopyGrowth * 0.25f; //for (int i = 0; i < 4; i++) //{ // var n = GetNeighbor(x, y, i); // int neighborIndex = GetIndex(n.x, n.y); // if (world.IsOcean(state.Elevation[neighborIndex], state.SeaLevel)) // { // nextState.Canopy[neighborIndex] = Math.Min(1.0f, nextState.Canopy[neighborIndex] + expansion); // } //} nextState.Canopy[index] = Math.Max(0, newCanopy); } } } for (int i = 0; i < world.MaxHerds; i++) { var species = state.Species[state.Herds[i].SpeciesIndex]; // Migrate tiles nextState.Herds[i].ActiveTileCount = state.Herds[i].DesiredTileCount; for (int j = 0; j < Herd.MaxActiveTiles; j++) { if (j < state.Herds[i].DesiredTileCount) { nextState.Herds[i].ActiveTiles[j] = state.Herds[i].DesiredTiles[j]; } } // Generate tile-based shared herd resources float water = 0; float comfort = 0; float food = 0; float social = 0; float populationDensity = 0; int herdPopulation = state.Herds[i].Population; float radiation = 0; if (state.Herds[i].ActiveTileCount > 0) { for (int j = 0; j < state.Herds[i].ActiveTileCount; j++) { var tile = state.Herds[i].ActiveTiles[j]; if (tile.x < 0 || tile.y < 0 || tile.x >= world.Size || tile.y >= world.Size) { // TODO: the active tiles should prob be a list, not a sparse array continue; } int tileIndex = world.GetIndex(tile.x, tile.y); // water += state.SurfaceWater[tileIndex]; comfort += Mathf.Clamp01((species.RestingTemperature + species.TemperatureRange - state.LowerAirTemperature[tileIndex]) / species.TemperatureRange); food += state.Canopy[tileIndex]; radiation += state.Radiation[tileIndex]; } } populationDensity = (float)state.Herds[i].Population / state.Herds[i].ActiveTileCount; social += populationDensity; int population = nextState.Herds[i].Population; // Consume water float waterConsumptionRate = 0.1f; float waterConsumed = Mathf.Clamp(population * waterConsumptionRate, water, GetMaxWaterHeld() - state.Herds[i].Water); nextState.Herds[i].Water += waterConsumed; water -= waterConsumed; // Consume food float foodConsumptionRate = 0.1f; float foodConsumed = Mathf.Clamp(population * foodConsumptionRate, food, GetMaxFoodHeld() - state.Herds[i].Food); nextState.Herds[i].Food += foodConsumed; food -= foodConsumed; nextState.Herds[i].Social = social; // Update unit resources (disease) //float immuneSystem = 1; // Immune system strength is based on water/food/comfort consumed // if (state.Herds[i].Units[j].Maturity) //nextState.Herds[i].Units[j].Disease += immuneSystem * populationDensity; // Death float lifeExpectancy = 10 * world.Data.TicksPerYear; float deathRate = 1.0f / lifeExpectancy; float starvationRate = 1.0f * world.Data.TicksPerYear; float birthRate = 10.0f * world.Data.TicksPerYear; if (nextState.Herds[i].Water <= 0) { deathRate += starvationRate; } if (nextState.Herds[i].Food <= 0) { deathRate += starvationRate; } herdPopulation = (int)Mathf.Max(0, herdPopulation * (1.0f - deathRate)); // Birth if (nextState.Herds[i].Social > 0 && nextState.Herds[i].Water > 0 && nextState.Herds[i].Food > 0) { int births = 0; nextState.Herds[i].Birth += social; if (nextState.Herds[i].Birth > birthRate) { births = (int)(nextState.Herds[i].Birth / birthRate); nextState.Herds[i].Birth -= birthRate * births; herdPopulation += births; } // Mutation and evolution float mutationSpeed = radiation * births; nextState.Herds[i].EvolutionProgress += mutationSpeed; float mutationHealthDelta = state.Herds[i].DesiredMutationHealth - state.Herds[i].MutationHealth; nextState.Herds[i].MutationHealth += Math.Sign(mutationHealthDelta) * Math.Min(mutationSpeed, Math.Abs(mutationHealthDelta)); float mutationReproductionDelta = state.Herds[i].DesiredMutationReproduction - state.Herds[i].MutationReproduction; nextState.Herds[i].MutationReproduction += Math.Sign(mutationReproductionDelta) * Math.Min(mutationSpeed, Math.Abs(mutationReproductionDelta)); float mutationSizeDelta = state.Herds[i].DesiredMutationSize - state.Herds[i].MutationSize; nextState.Herds[i].MutationSize += Math.Sign(mutationSizeDelta) * Math.Min(mutationSpeed, Math.Abs(mutationSizeDelta)); } nextState.Herds[i].Population = herdPopulation; // Collapse units if they can be combined // Split units if they hit population cap } //for (int i = 0;i<MaxHerds;i++) //{ // float population = state.Herds[i].Population; // if (population > 0) // { // int tileIndex = GetIndex((int)state.Herds[i].Position.x, (int)state.Herds[i].Position.y); // float newPopulation = population; // if (world.IsOcean(state.Elevation[tileIndex], state.SeaLevel)) // { // newPopulation = 0; // } // else // { // var species = state.Species[state.Herds[i].Species]; // float populationDensity = population / species.speciesMaxPopulation; // float babiesBorn = population * species.speciesGrowthRate; // newPopulation += babiesBorn; // float diedOfOldAge = Math.Max(0, population * 1.0f / (species.Lifespan * Data.TicksPerYear)); // newPopulation -= diedOfOldAge; // float diedFromTemperature = population * Math.Abs((state.Temperature[tileIndex] - species.RestingTemperature) / species.TemperatureRange); // newPopulation -= diedFromTemperature; // float freshWaterAvailability = GetFreshWaterAvailability(state.SurfaceWater[tileIndex], GetGroundWaterSaturation(state.GroundWater[tileIndex], state.WaterTableDepth[tileIndex], state.SoilFertility[tileIndex] * Data.MaxSoilPorousness)); // float diedOfDehydration = Math.Max(0, population * (populationDensity - freshWaterAvailability / Data.freshWaterMaxAvailability) * species.dehydrationSpeed); // newPopulation -= diedOfDehydration; // if (species.Food == SpeciesType.FoodType.Herbivore) // { // float canopy = nextState.Canopy[tileIndex]; // float diedOfStarvation = Math.Max(0, population * (populationDensity - canopy) * species.starvationSpeed); // newPopulation -= diedOfStarvation; // canopy -= population * species.speciesEatRate; // nextState.Canopy[tileIndex] = canopy; // } // else // { // float availableMeat = 0; // for (int m = 0; m < MaxGroupsPerTile; m++) // { // int meatGroupIndex = state.AnimalsPerTile[tileIndex * MaxGroupsPerTile + m]; // if (meatGroupIndex >= 0 && state.Species[state.Herds[meatGroupIndex].Species].Food == SpeciesType.FoodType.Herbivore) // { // availableMeat += state.Herds[meatGroupIndex].Population; // } // } // float diedOfStarvation = Math.Max(0, population * (population - availableMeat) * species.starvationSpeed); // newPopulation -= diedOfStarvation; // float meatEaten = Math.Min(availableMeat, population * species.speciesEatRate); // for (int m = 0; m < MaxGroupsPerTile; m++) // { // int meatGroupIndex = state.AnimalsPerTile[tileIndex * MaxGroupsPerTile + m]; // if (meatGroupIndex >= 0 && state.Species[state.Herds[meatGroupIndex].Species].Food == SpeciesType.FoodType.Herbivore) // { // float meatPop = nextState.Herds[meatGroupIndex].Population; // nextState.Herds[meatGroupIndex].Population = Math.Max(0, meatPop - meatEaten * meatPop / availableMeat); // } // } // } // if (state.Herds[i].Destination != state.Herds[i].Position) // { // Vector2 diff = state.Herds[i].Destination - state.Herds[i].Position; // float dist = diff.magnitude; // var dir = diff.normalized; // Vector2 move = dist <= species.MovementSpeed ? diff : (species.MovementSpeed * dir); // Vector2 newPos = state.Herds[i].Position + move; // Vector2Int newTile = new Vector2Int((int)newPos.x, (int)newPos.y); // int newTileIndex = GetIndex(newTile.x, newTile.y); // // Don't walk into water // Vector2 nextPos = state.Herds[i].Position + dir; // if (state.Elevation[GetIndex((int)nextPos.x, (int)nextPos.y)] <= state.SeaLevel) // { // nextState.Herds[i].Destination = new Vector2((int)state.Herds[i].Position.x + 0.5f, (int)state.Herds[i].Position.y + 0.5f); // } // // move tiles // if ((int)state.Herds[i].Position.x != newTile.x || (int)state.Herds[i].Position.y != newTile.y) // { // for (int j = 0; j < MaxGroupsPerTile; j++) // { // int newGroupIndex = newTileIndex * MaxGroupsPerTile + j; // if (nextState.AnimalsPerTile[newGroupIndex] == -1) // { // nextState.AnimalsPerTile[newGroupIndex] = i; // nextState.Herds[i].Position = newPos; // for (int k = 0; k < MaxGroupsPerTile; k++) // { // int groupIndex = tileIndex * MaxGroupsPerTile + k; // if (nextState.AnimalsPerTile[groupIndex] == i) // { // nextState.AnimalsPerTile[groupIndex] = -1; // break; // } // } // break; // } // } // } // else // { // nextState.Herds[i].Position = newPos; // } // } // } // nextState.Herds[i].Population = Math.Max(0, newPopulation); // if (newPopulation <= 0) // { // for (int j = 0; j < MaxGroupsPerTile; j++) // { // int groupIndex = tileIndex * MaxGroupsPerTile + j; // if (nextState.AnimalsPerTile[groupIndex] == i) // { // nextState.AnimalsPerTile[groupIndex] = -1; // } // } // } // } //} _ProfileEarthTick.End(); }
static public void Tick(World world, World.State state, World.State nextState) { _ProfileWindTick.Begin(); float inverseFullIceCoverage = 1.0f / world.Data.FullIceCoverage; for (int y = 0; y < world.Size; y++) { float latitude = world.Data.windInfo[y].latitude; var windInfo = world.Data.windInfo[y]; for (int x = 0; x < world.Size; x++) { int index = world.GetIndex(x, y); float upperPressure = state.UpperAirPressure[index]; float lowerPressure = state.LowerAirPressure[index]; float upperTemperature = state.UpperAirTemperature[index]; float lowerTemperature = state.LowerAirTemperature[index]; float elevation = state.Elevation[index]; float waterDepth = state.WaterDepth[index]; float waterAndIceDepth = state.WaterAndIceDepth[index]; float elevationOrSeaLevel = elevation + waterAndIceDepth; var normal = state.Normal[index]; float iceCoverage = state.IceMass[index] * inverseFullIceCoverage; float friction; if (waterDepth > 0) { friction = world.Data.WindOceanFriction; } else { friction = world.Data.WindLandFriction; } friction = Mathf.Clamp01(Mathf.Lerp(friction, world.Data.WindIceFriction, iceCoverage)); float lowerTemperatureAtSeaLevel = lowerTemperature - world.Data.TemperatureLapseRate * elevationOrSeaLevel; float molarMassLowerAir = Atmosphere.GetMolarMassAir(world, state.LowerAirMass[index], state.Humidity[index]); float molarMassUpperAir = Atmosphere.GetMolarMassAir(world, state.UpperAirMass[index] + state.StratosphereMass, state.CloudMass[index]); float surfaceAirDensity = Atmosphere.GetAirDensity(world, lowerPressure, lowerTemperatureAtSeaLevel, molarMassLowerAir); float boundaryElevation = elevationOrSeaLevel + world.Data.BoundaryZoneElevation; float upperTemperatureAtSeaLevel = upperTemperature - world.Data.TemperatureLapseRate * boundaryElevation; float tropopausePressure = upperPressure * Mathf.Pow(1 + world.Data.TemperatureLapseRate / upperTemperatureAtSeaLevel * world.Data.TropopauseElevation, -world.Data.PressureExponent * molarMassUpperAir); float tropopauseDensity = Atmosphere.GetAirDensity(world, tropopausePressure, upperTemperature + world.Data.TemperatureLapseRate * (world.Data.TropopauseElevation - boundaryElevation), molarMassUpperAir); var upperWindH = GetHorizontalWind(world, state, x, y, state.UpperWind[index], latitude, state.PlanetRotationSpeed, windInfo.coriolisParam, windInfo.inverseCoriolisParam, world.Data.GlobalCoriolisInfluenceWindUpper, state.UpperAirPressure, state.UpperAirTemperature, true, tropopausePressure, elevationOrSeaLevel, world.Data.TropopauseElevation, 0, tropopauseDensity, molarMassUpperAir); var lowerWindH = GetHorizontalWind(world, state, x, y, state.LowerWind[index], latitude, state.PlanetRotationSpeed, windInfo.coriolisParam, windInfo.inverseCoriolisParam, world.Data.GlobalCoriolisInfluenceWindLower, state.LowerAirPressure, state.LowerAirTemperature, false, lowerPressure, elevationOrSeaLevel, elevationOrSeaLevel, friction, surfaceAirDensity, molarMassLowerAir); // within 1 km of the ground, frictional forces slow wind down float neighborPressureDifferential = 0; float neighborElevationDifferential = 0; if (lowerWindH.x < 0) { int neighborIndex = world.GetNeighborIndex(x, y, 0); neighborPressureDifferential += -lowerWindH.x * (lowerPressure - state.LowerAirPressure[neighborIndex]); neighborElevationDifferential += -lowerWindH.x * (state.Elevation[neighborIndex] + state.WaterAndIceDepth[neighborIndex] - elevationOrSeaLevel); } else { var neighborIndex = world.GetNeighborIndex(x, y, 1); neighborPressureDifferential += lowerWindH.x * (lowerPressure - state.LowerAirPressure[neighborIndex]); neighborElevationDifferential += lowerWindH.x * (state.Elevation[neighborIndex] + state.WaterAndIceDepth[neighborIndex] - elevationOrSeaLevel); } if (lowerWindH.y < 0) { var neighborIndex = world.GetNeighborIndex(x, y, 3); neighborPressureDifferential += -lowerWindH.y * (lowerPressure - state.LowerAirPressure[neighborIndex]); neighborElevationDifferential += -lowerWindH.y * (state.Elevation[neighborIndex] + state.WaterAndIceDepth[neighborIndex] - elevationOrSeaLevel); } else { var neighborIndex = world.GetNeighborIndex(x, y, 2); neighborPressureDifferential += lowerWindH.y * (lowerPressure - state.LowerAirPressure[neighborIndex]); neighborElevationDifferential += lowerWindH.y * (state.Elevation[neighborIndex] + state.WaterAndIceDepth[neighborIndex] - elevationOrSeaLevel); } var verticalTemperatureDifferential = lowerTemperatureAtSeaLevel - upperTemperatureAtSeaLevel; float lowerWindSpeedH = lowerWindH.magnitude; float lowerWindV = neighborElevationDifferential * world.Data.MountainUpdraftWindSpeed; lowerWindV += neighborPressureDifferential * world.Data.DestinationPressureDifferentialToVerticalWindSpeed; // thermal lowerWindV += (lowerPressure - upperPressure) * world.Data.PressureToVerticalWindSpeed; // thermal nextState.LowerWind[index] = new Vector3(lowerWindH.x, lowerWindH.y, lowerWindV); nextState.UpperWind[index] = new Vector3(upperWindH.x, upperWindH.y, 0); if (float.IsNaN(nextState.UpperWind[index].x) || float.IsNaN(nextState.LowerWind[index].x) || float.IsNaN(nextState.LowerWind[index].z)) { Debug.DebugBreak(); } if (world.IsOcean(state.WaterDepth[index])) { Vector2 densityDifferential = Vector2.zero; Vector2 shallowCurrentH; float shallowCurrentV = 0; if (iceCoverage < 1) { shallowCurrentH = GetCurrentHorizontal(world, x, y, latitude, state.PlanetRotationSpeed, windInfo.coriolisParam, windInfo.inverseCoriolisParam, lowerWindH); if (iceCoverage > 0) { shallowCurrentH *= 1.0f - iceCoverage; } } else { shallowCurrentH = Vector2.zero; } if (state.DeepWaterMass[index] > 0) { float density = state.DeepWaterDensity[index]; for (int i = 0; i < 4; i++) { var neighbor = world.GetNeighbor(x, y, i); int nIndex = world.GetIndex(neighbor.x, neighbor.y); if (state.DeepWaterMass[nIndex] > 0) { //var neighborWind = state.Wind[nIndex]; //nWind += neighborWind; switch (i) { case 0: densityDifferential.x += state.DeepWaterDensity[nIndex] - density; break; case 1: densityDifferential.x -= state.DeepWaterDensity[nIndex] - density; break; case 2: densityDifferential.y -= state.DeepWaterDensity[nIndex] - density; break; case 3: densityDifferential.y += state.DeepWaterDensity[nIndex] - density; break; } } else { //switch (i) //{ // case 0: // shallowCurrentV += shallowCurrentH.x; // if (shallowCurrentH.x < 0) // { // shallowCurrentH.x = 0; // } // break; // case 1: // shallowCurrentV -= shallowCurrentH.x; // if (shallowCurrentH.x > 0) // { // shallowCurrentH.x = 0; // } // break; // case 2: // shallowCurrentV -= shallowCurrentH.y; // if (shallowCurrentH.y > 0) // { // shallowCurrentH.y = 0; // } // break; // case 3: // shallowCurrentV += shallowCurrentH.y; // if (shallowCurrentH.y < 0) // { // shallowCurrentH.y = 0; // } // break; //} } } } densityDifferential *= world.Data.OceanDensityCurrentSpeed; nextState.DeepWaterCurrent[index] = new Vector3(densityDifferential.x, densityDifferential.y, 0); nextState.ShallowWaterCurrent[index] = new Vector3(shallowCurrentH.x, shallowCurrentH.y, shallowCurrentV); } else { nextState.DeepWaterCurrent[index] = Vector3.zero; nextState.ShallowWaterCurrent[index] = Vector3.zero; } } } _ProfileWindTick.End(); }
static public Vector2 GetHorizontalWind(World world, World.State state, int x, int y, Vector3 curWind, float latitude, float planetRotationSpeed, float coriolisParam, float inverseCoriolisParam, float coriolisInfluenceAtElevation, float[] worldPressure, float[] worldTemperature, bool isUpper, float thisPressure, float landElevation, float windElevation, float friction, float density, float molarMass) { float inverseDensity = 1.0f / density; float altitude = Mathf.Max(0, windElevation - landElevation); float complementFrictionAtElevation = 1.0f - friction * Mathf.Max(0, (world.Data.BoundaryZoneElevation - altitude) / world.Data.BoundaryZoneElevation); var pressureGradientForce = GetPressureGradient(world, state, x, y, worldPressure, worldTemperature, isUpper, thisPressure, windElevation, molarMass); pressureGradientForce.x *= world.Data.GravitationalAcceleration * world.Data.InverseMetersPerTile; pressureGradientForce.y *= world.Data.GravitationalAcceleration * world.Data.InverseMetersPerTile; Vector2 wind = Vector2.zero; //for (int i = 0; i < 4; i++) //{ // var nIndex = world.GetNeighborIndex(x, y, i); // var neighborWind = state.UpperWind[nIndex]; // float nWindSpeed = Mathf.Sqrt(neighborWind.x * neighborWind.x + neighborWind.y * neighborWind.y); // switch (i) // { // case 0: // if (neighborWind.x > 0) // { // wind += neighborWind.x / nWindSpeed * new Vector3(neighborWind.x, neighborWind.y, 0); // } // break; // case 1: // if (neighborWind.x < 0) // { // wind += -neighborWind.x / nWindSpeed * new Vector3(neighborWind.x, neighborWind.y, 0); // } // break; // case 2: // if (neighborWind.y < 0) // { // wind += -neighborWind.y / nWindSpeed * new Vector3(neighborWind.x, neighborWind.y, 0); // } // break; // case 3: // if (neighborWind.y > 0) // { // wind += neighborWind.y / nWindSpeed * new Vector3(neighborWind.x, neighborWind.y, 0); // } // break; // } //} var pressureWind = pressureGradientForce * world.Data.PressureGradientWindMultiplier; if (coriolisParam != 0) { float geostrophicInfluence = Mathf.Clamp01(Mathf.Pow(Mathf.Abs(coriolisParam) * 2, 2)) * complementFrictionAtElevation * coriolisInfluenceAtElevation; var geostrophicWind = new Vector2(-pressureGradientForce.y, pressureGradientForce.x) * inverseCoriolisParam / planetRotationSpeed; wind = (geostrophicWind * geostrophicInfluence + pressureWind * (1.0f - geostrophicInfluence)); } else { wind = pressureWind; } wind *= complementFrictionAtElevation * inverseDensity; //wind += inertialWind * world.Data.windInertia; return(wind); }
static public void MovePlate(World world, World.State state, World.State nextState, int plateIndex, Vector2Int direction) { // TODO: enforce conservation of mass for (int y = 0; y < world.Size; y++) { for (int x = 0; x < world.Size; x++) { int index = world.GetIndex(x, y); if (state.Plate[index] == plateIndex) { Vector2Int newPoint = new Vector2Int(world.WrapX(x + direction.x), world.WrapY(y + direction.y)); int newIndex = world.GetIndex(newPoint.x, newPoint.y); if (state.Plate[newIndex] == plateIndex) { MoveTile(world, state, nextState, index, newIndex); Vector2Int divergentPoint = new Vector2Int(world.WrapX(x - direction.x), world.WrapY(y - direction.y)); int divergentIndex = world.GetIndex(divergentPoint.x, divergentPoint.y); if (state.Plate[divergentIndex] != plateIndex) { // divergent zone // if (state.Elevation[index] > state.Elevation[divergentIndex]) { nextState.Plate[index] = state.Plate[divergentIndex]; } nextState.Elevation[index] = (state.Elevation[index] + state.Elevation[divergentIndex]) / 2 - 100; nextState.WaterTableDepth[index] = (state.WaterTableDepth[index] + state.WaterTableDepth[divergentIndex]) / 2; nextState.SoilFertility[index] = (state.SoilFertility[index] + state.SoilFertility[divergentIndex]) / 2; } } else { float startElevation = state.Elevation[index]; float endElevation = state.Elevation[newIndex]; if (!world.IsOcean(state.WaterDepth[index]) && !world.IsOcean(state.WaterDepth[index])) // TODO: this is broken now that sealevel isnt a constant { // continental collision nextState.Elevation[newIndex] += 50; nextState.Elevation[index] += 50; nextState.Plate[newIndex] = plateIndex; } else { // subduction if (!world.IsOcean(state.WaterDepth[index])) { // We are moving OVER the adjacent tile MoveTile(world, state, nextState, index, newIndex); Vector2Int subductionPoint = new Vector2Int(world.WrapX(newPoint.x + direction.x), world.WrapY(newPoint.y + direction.y)); int subductionIndex = world.GetIndex(subductionPoint.x, subductionPoint.y); nextState.Elevation[newIndex] = (state.Elevation[newIndex] + state.Elevation[subductionIndex]) / 2; nextState.Elevation[subductionIndex] -= 100; } else { // we are moving UNDER the adjacent tile nextState.Elevation[newIndex] = (state.Elevation[newIndex] + state.Elevation[index]) / 2; nextState.Elevation[index] -= 100; } } } } } } for (int y = 0; y < world.Size; y++) { for (int x = 0; x < world.Size; x++) { int index = world.GetIndex(x, y); float elevation = state.Elevation[index]; Vector2 newFlowDirectionGroundWater; Vector4 newShallowWaterFlow; Vector3 newNormal; GetNormalAndFlow(world, nextState, x, y, index, elevation, state.SoilFertility[index], out newFlowDirectionGroundWater, out newShallowWaterFlow, out newNormal); nextState.FlowDirectionGroundWater[index] = newFlowDirectionGroundWater; nextState.ShallowWaterFlow[index] = newShallowWaterFlow; nextState.Normal[index] = newNormal; } } }