File size: 29,543 Bytes
b6a38d7 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 |
local remove_entry = table.remove_entry
local max_grid_updates = 10
local MulDivRound = MulDivRound
local Min, Max, Clamp = Min, Max, Clamp
local HighestConsumePriority = config.FluidGridHighestConsumePriority or 1
local LowestConsumePriority = config.FluidGridLowestConsumePriority or 1
local BeyondHighestConsumePriority = HighestConsumePriority - 1
----- FluidGrid
DefineClass.FluidGrid = {
__parents = { "InitDone" },
grid_resource = "electricity",
player = false,
-- arrays with the various elements for faster access
elements = false, -- all elements
producers = false,
consumers = false,
storages = false,
switches = false,
-- smart connections
smart_connections = 0,
-- aggregated for the entire grid
total_production = 0,
total_throttled_production = 0,
total_consumption = 0,
total_variable_consumption = 0,
total_charge = 0,
total_discharge = 0,
total_storage_capacity = 0,
-- current
current_storage_delta = 0,
current_production = 0,
current_production_delta = 0,
current_throttled_production = 0,
current_consumption = 0,
current_variable_consumption = 0,
current_storage = 0,
-- visuals
visual_mesh = false,
visuals_thread = false,
needs_visual_update = false,
-- update
update_thread = false,
needs_update = false,
update_consumers = false,
consumers_supplied = false, -- can be false or a priority index - consumers with higher or same priority are supplied
-- produce tick
production_thread = false,
production_interval = config.FluidGridProductionInterval or 10000, -- how often runs the production logic
restart_supply_delay = config.FluidGridRestartDelay or 10000, -- after turning off, the grid will wait this amount of time to start in order to avoid cyclical grid restarts
restart_supply_time = 0,
LogChangedElements = empty_func,
}
function FluidGrid:Init()
self.elements = {}
self.producers = {}
self.consumers = {}
assert(HighestConsumePriority <= LowestConsumePriority)
for priority = HighestConsumePriority, LowestConsumePriority do
self.consumers[priority] = { consumption = 0, variable_consumption = 0 }
end
self.storages = {}
self.switches = {}
self:RestartThreads()
end
function FluidGrid:RestartThreads()
DeleteThread(self.update_thread)
self.update_thread = CreateGameTimeThread(function(self)
local updates, last_element_count, last_update = 0, #self.elements
while true do
local now = GameTime()
local elem_count = #self.elements
if last_update ~= now or elem_count ~= last_element_count then
last_update = now
last_element_count = elem_count
updates = 0
end
while self.needs_update do
if updates == max_grid_updates then
self:LogChangedElements()
assert(false, "Infinite grid update recursion!")
break
end
self.needs_update = false
procall(self.UpdateGrid, self)
updates = updates + 1
end
WaitWakeup()
end
end, self)
DeleteThread(self.production_thread)
self.production_thread = CreateGameTimeThread(function(self, production_interval)
while true do
Sleep(production_interval)
procall(self.Production, self, production_interval)
end
end, self, self.production_interval)
DeleteThread(self.visuals_thread)
self.visuals_thread = CreateGameTimeThread(function()
while true do
while self.needs_visual_update do
self.needs_visual_update = false
procall(self.UpdateVisuals, self)
end
WaitWakeup()
end
end)
end
function FluidGrid:Done()
local grid_resource = self.grid_resource
for priority = HighestConsumePriority, LowestConsumePriority do
for _, consumer in ipairs(self.consumers[priority]) do
if consumer.current_consumption > 0 and consumer.grid == self then
local old = consumer.current_consumption
consumer.current_consumption = 0
consumer.owner:SetConsumption(grid_resource, old, 0)
end
end
end
for _, element in ipairs(self.elements) do
if element.grid == self then
element.grid = false
end
end
DeleteThread(self.update_thread)
self.update_thread = false
DeleteThread(self.production_thread)
self.production_thread = false
DeleteThread(self.visuals_thread)
self.visuals_thread = false
DoneObject(self.visual_mesh)
self.visual_mesh = false
Msg("FluidGridDestroyed", self)
end
function FluidGrid:AddElement(element, skip_update)
element.grid = self
self.elements[#self.elements + 1] = element
if element.production then
self.total_production = self.total_production + element.production
self.total_throttled_production = self.total_throttled_production + element.throttled_production
self.producers[#self.producers + 1] = element
end
local consumption = element.consumption
if consumption then
local consumer_list = self.consumers[element.consume_priority]
if element.variable_consumption then
self.total_variable_consumption = self.total_variable_consumption + consumption
consumer_list.variable_consumption = consumer_list.variable_consumption + consumption
else
self.total_consumption = self.total_consumption + consumption
consumer_list.consumption = consumer_list.consumption + consumption
end
consumer_list[#consumer_list + 1] = element
end
if element.charge then
self.total_charge = self.total_charge + element.charge
self.total_discharge = self.total_discharge + element.discharge
if element.discharge > 0 then
self.current_storage = self.current_storage + element.current_storage
end
self.total_storage_capacity = self.total_storage_capacity + element.storage_capacity
self.storages[#self.storages + 1] = element
end
if element.is_switch then
self.switches[#self.switches + 1] = element
self:UpdateSmartConnections()
end
if not skip_update then
self:DelayedUpdateGrid(consumption)
self:DelayedUpdateVisuals()
end
Msg("FluidGridAddElement", self, element)
end
function FluidGrid:RemoveElement(element, skip_update)
if element.grid ~= self then return end
if element.current_consumption > 0 then
local old = element.current_consumption
element.current_consumption = 0
element.owner:SetConsumption(self.grid_resource, old, 0)
end
element.grid = false
remove_entry(self.elements, element)
if element.production then
self.total_production = self.total_production - element.production
self.total_throttled_production = self.total_throttled_production - element.throttled_production
remove_entry(self.producers, element)
end
local consumption = element.consumption
if consumption then
local consumer_list = self.consumers[element.consume_priority]
if element.variable_consumption then
self.total_variable_consumption = self.total_variable_consumption - consumption
consumer_list.variable_consumption = consumer_list.variable_consumption - consumption
else
self.total_consumption = self.total_consumption - consumption
consumer_list.consumption = consumer_list.consumption - consumption
end
remove_entry(consumer_list, element)
end
if element.current_storage then
self.total_charge = self.total_charge - element.charge
self.total_discharge = self.total_discharge - element.discharge
if element.discharge > 0 then
self.current_storage = self.current_storage - element.current_storage
end
self.total_storage_capacity = self.total_storage_capacity - element.storage_capacity
remove_entry(self.storages, element)
end
if element.is_switch then
remove_entry(self.switches, element)
self:UpdateSmartConnections()
end
if #(self.elements or "") == 0 then
self:delete()
return
end
if not skip_update then
self:DelayedUpdateGrid()
self:DelayedUpdateVisuals()
end
Msg("FluidGridRemoveElement", self, element)
end
function FluidGrid:CountConsumers(func, ...)
local count = 0
func = func or return_true
for priority = HighestConsumePriority, LowestConsumePriority do
for _, consumer in ipairs(self.consumers[priority]) do
if func(consumer, ...) then
count = count + 1
end
end
end
return count
end
function FluidGrid:DelayedUpdateGrid(update_consumers)
self.update_consumers = self.update_consumers or update_consumers
self.needs_update = true
Wakeup(self.update_thread)
end
function FluidGrid:UpdateGrid(update_consumers)
update_consumers = self.update_consumers or update_consumers
self.update_consumers = false
local total_production = self.total_production
local total_discharge = self.total_discharge
local current_consumption = 0
local current_variable_consumption = 0
local consumers_supplied = false
-- limit restarting supply to lower priority consumers for some time
local ConsumePriorityLimit = (GameTime() - self.restart_supply_time < 0) and (self.consumers_supplied or BeyondHighestConsumePriority) or LowestConsumePriority
-- find out which priority consumers can consume
local total_supply = total_production + total_discharge
for priority = HighestConsumePriority, ConsumePriorityLimit do
local consumer_list = self.consumers[priority]
-- consumers of certain priority will consume only if all consumption and variable consumtion of higher priorities can be supplied
assert(consumer_list)
if #(consumer_list or "") > 0 and current_consumption + current_variable_consumption + consumer_list.consumption <= total_supply then
consumers_supplied = priority
current_consumption = current_consumption + current_variable_consumption + consumer_list.consumption
current_variable_consumption = consumer_list.variable_consumption
if current_consumption + current_variable_consumption >= total_supply then -- all supply is used
current_variable_consumption = Min(current_variable_consumption, total_supply - current_consumption)
break
end
end
end
assert(current_consumption <= self.total_consumption)
current_consumption = current_consumption + current_variable_consumption
assert(current_consumption <= total_supply)
local storage_delta = Clamp(total_production - current_consumption, - total_discharge, self.total_charge)
self.current_throttled_production = Min(total_production - current_consumption - storage_delta, self.total_throttled_production)
self.current_storage_delta = storage_delta
self.current_consumption = current_consumption
self.current_production = total_production - self.current_throttled_production
self.current_production_delta = self.current_production - current_consumption
local old_consumers_supplied = self.consumers_supplied
local old_current_variable_consumption = self.current_variable_consumption
self.consumers_supplied = consumers_supplied
self.current_variable_consumption = current_variable_consumption
if consumers_supplied ~= old_consumers_supplied then
update_consumers = true
if (consumers_supplied or BeyondHighestConsumePriority) < (old_consumers_supplied or BeyondHighestConsumePriority) then -- degradation of supply
self.restart_supply_time = GameTime() + self.restart_supply_delay
end
Msg("FluidGridConsumersSupplied", self, old_consumers_supplied, consumers_supplied)
end
if old_current_variable_consumption ~= current_variable_consumption then
update_consumers = true
Msg("FluidGridVariableConsumption", self, old_consumers_supplied, consumers_supplied)
end
if update_consumers then
local grid_resource = self.grid_resource
local consumers_variable_consumption = consumers_supplied and self.consumers[consumers_supplied].variable_consumption
consumers_supplied = consumers_supplied or BeyondHighestConsumePriority
for priority = HighestConsumePriority, LowestConsumePriority do
for _, consumer in ipairs(self.consumers[priority]) do
local consumption = priority > consumers_supplied and 0 -- lower priority than supplied
or priority < consumers_supplied and consumer.consumption -- full supply to higher priority than supplied
or consumer.variable_consumption and MulDivRound(consumer.consumption, current_variable_consumption, consumers_variable_consumption) or consumer.consumption
local old_consumption = consumer.current_consumption
if old_consumption ~= consumption then
consumer.current_consumption = consumption
consumer.owner:SetConsumption(grid_resource, old_consumption, consumption)
end
end
end
end
ObjModifiedDelayed(self)
end
function FluidGrid:DelayedUpdateVisuals()
self.needs_visual_update = true
Wakeup(self.visuals_thread)
end
function FluidGrid:UpdateVisuals()
local active = self.consumers_supplied
local color = active and const.PowerGridActiveColor or const.PowerGridInactiveColor
local joint_color = active and const.PowerGridActiveJointColor or const.PowerGridInactiveJointColor
local mesh_pstr = pstr("")
local pos
for i, element in ipairs(self.elements) do
local owner = element.owner
assert(IsValid(owner))
if IsValid(owner) then
pos = pos or owner:GetPos()
owner:AddFluidGridVisuals(self.grid_resource, pos, color, joint_color, mesh_pstr)
end
end
local mesh
if #mesh_pstr > 0 then
mesh = self.visual_mesh
mesh = IsValid(mesh) and mesh or PlaceObject("Mesh")
mesh:SetDepthTest(true)
mesh:SetMesh(mesh_pstr)
mesh:SetPos(pos)
end
if self.visual_mesh ~= mesh then
DoneObject(self.visual_mesh)
self.visual_mesh = mesh
end
end
function FluidGrid:UpdateSmartConnections()
local smart_connections = 0
for _, switch in ipairs(self.switches) do
smart_connections = smart_connections | switch.switch_mask
end
if self.smart_connections == smart_connections then return end
local changed_connections = self.smart_connections ~ smart_connections
self.smart_connections = smart_connections
local grid_resource = self.grid_resource
for _, producer in ipairs(self.producers) do
if (producer.smart_connection or 0) & changed_connections ~= 0 then
producer.owner:SmartConnectionChange(grid_resource)
end
end
for priority = HighestConsumePriority, LowestConsumePriority do
for _, consumer in ipairs(self.consumers[priority]) do
if (consumer.smart_connection or 0) & changed_connections ~= 0 then
consumer.owner:SmartConnectionChange(grid_resource)
end
end
end
end
function FluidGrid:IsSmartConnectionOn(smart_connection)
if not smart_connection then return true end -- no smart_connection set, so it is on
return (self.smart_connections & smart_connection) ~= 0
end
function FluidGrid:Production(production_interval)
local grid_resource = self.grid_resource
-- producers
local current_throttled_production = self.current_throttled_production
local total_throttled_production = self.total_throttled_production
for _, producer in ipairs(self.producers) do
producer.current_throttled_production = total_throttled_production > 0
and MulDivRound(producer.throttled_production, current_throttled_production, total_throttled_production) or 0
local production = producer.production - producer.current_throttled_production
producer.owner:OnProduce(grid_resource, production, production_interval)
end
-- consumers
for priority = HighestConsumePriority, LowestConsumePriority do
for _, consumer in ipairs(self.consumers[priority]) do
consumer.owner:OnConsume(grid_resource, consumer.current_consumption, production_interval)
end
end
-- storages
local total_charge = self.total_charge
local total_discharge = self.total_discharge
local storage_delta = self.current_storage_delta
if storage_delta > 0 and total_charge > 0 then
for _, storage in ipairs(self.storages) do
storage:AddStoredCharge(MulDivRound(storage_delta, storage.charge_efficiency * storage.charge, 100 * total_charge), self)
end
elseif storage_delta < 0 and total_discharge > 0 then
for _, storage in ipairs(self.storages) do
storage:AddStoredCharge(MulDivRound(storage_delta, storage.discharge, total_discharge), self)
end
end
if Platform.developer then
-- aggregate storage values and verify they match
local current_storage, total_charge, total_discharge = 0, 0, 0
for _, storage in ipairs(self.storages) do
if storage.discharge > 0 then
current_storage = current_storage + storage.current_storage
end
total_charge = total_charge + storage.charge
total_discharge = total_discharge + storage.discharge
end
assert(self.current_storage == current_storage)
assert(self.total_charge == total_charge)
assert(self.total_discharge == total_discharge)
self.current_storage = current_storage
self.total_charge = total_charge
self.total_discharge = total_discharge
end
self:DelayedUpdateGrid()
end
function MergeGrids(new_grid, grid) -- merges grid into new_grid
if grid == new_grid then return end
for i, element in ipairs(grid.elements) do
new_grid:AddElement(element)
end
grid:delete()
end
----- FluidGridElementOwner
DefineClass.FluidGridElementOwner = {
__parents = { "InitDone" },
}
-- callback when a resource consumption from the grid is modified
AutoResolveMethods.SetConsumption = true
function FluidGridElementOwner:SetConsumption(resource, old_amount, new_amount)
end
-- callback when storage state changes - "empty", "full", "charging", "discharging"
function FluidGridElementOwner:SetStorageState(resource, state)
end
-- callback called each production_interval with the actual amount produced for the grid
function FluidGridElementOwner:OnProduce(resource, amount, production_interval)
end
-- callback called each production_interval with the actual amount consumed from the grid
function FluidGridElementOwner:OnConsume(resource, amount, production_interval)
end
-- callback called when the stored amount of a storage has changed
AutoResolveMethods.ChangeStoredAmount = true
function FluidGridElementOwner:ChangeStoredAmount(resource, storage, old_storage)
end
-- callback called when a smart connection relevant to the grid element has changed
function FluidGridElementOwner:SmartConnectionChange(resource)
end
-- used to construct the visual mesh for the grid
function FluidGridElementOwner:AddFluidGridVisuals(grid_resource, origin, color, joint_color, mesh_pstr)
end
----- FluidGridElement
DefineClass.FluidGridElement = {
__parents = { "InitDone" },
grid = false, -- the grid this element belongs to
owner = false, -- inherits FluidGridElementOwner
smart_connection = false, -- used by switch, consumer and producer
smart_connection2 = false,
-- producer
production = false,
throttled_production = 0, -- how much of the production can be throttled(not produced) when there is no demand for it
current_throttled_production = 0, -- how much of the production is currently being throttled
-- consumer
consumption = false,
variable_consumption = false, -- whether or not the consumer can work with any amount between 0 and consumption
current_consumption = 0,
consume_priority = config.FluidGridDefaultConsumePriority or HighestConsumePriority,
-- storage
storage_active = false,
charge = false,
discharge = false,
storage_capacity = false,
current_storage = false,
max_charge = false,
max_discharge = false,
charge_efficiency = 100,
storage_state = "", -- "empty", "charging", "discharging", "full"
min_discharge_amount = 0, -- minumum stored amount before discharging
-- is_connector
is_connector = false,
-- is_switch
is_switch = false,
switch_state = 0, -- a bitfield indicating which of our 2 smart connections are on (all combinations are valid)
switch_mask = 0, -- a bitfield indicating which grid smart connections are on
RegisterConsumptionChange = empty_func,
}
function NewFluidConnector(owner)
assert(IsValid(owner))
return FluidGridElement:new{
owner = owner,
is_connector = true,
}
end
function NewFluidSwitch(owner, consumption, variable_consumption)
assert(IsValid(owner))
return FluidGridElement:new{
owner = owner,
is_switch = true,
smart_connection = 1,
switch_mask = 1,
consumption = consumption or false,
variable_consumption = variable_consumption,
}
end
function NewFluidProducer(owner, production, throttled_production)
assert(IsValid(owner))
return FluidGridElement:new{
owner = owner,
production = production or 0,
throttled_production = throttled_production or 0,
}
end
function NewFluidConsumer(owner, consumption, variable_consumption)
assert(IsValid(owner))
return FluidGridElement:new{
owner = owner,
consumption = consumption or 0,
variable_consumption = variable_consumption,
}
end
function NewFluidStorage(owner, storage_capacity, current_storage, max_charge, max_discharge, charge_efficiency, min_discharge_amount)
assert(IsValid(owner))
return FluidGridElement:new{
owner = owner,
charge = max_charge,
discharge = 0,
current_storage = current_storage,
storage_capacity = storage_capacity,
max_charge = max_charge,
max_discharge = max_discharge,
charge_efficiency = charge_efficiency,
storage_state = "empty",
min_discharge_amount = min_discharge_amount,
}
end
function FluidGridElement:Done()
if self.grid then
self.grid:RemoveElement(self)
self.grid = nil
end
end
function FluidGridElement:SetProduction(new_production, new_throttled_production, skip_update)
assert(self.production) -- the element should already be a producer
new_production = Max(new_production, 0)
new_throttled_production = Max(new_throttled_production, 0)
if self.production == new_production and self.throttled_production == new_throttled_production then return end
local grid = self.grid
if grid then
grid.total_production = grid.total_production + new_production - self.production
grid.total_throttled_production = grid.total_throttled_production - self.throttled_production + new_throttled_production
end
self.production = new_production
self.throttled_production = new_throttled_production
if grid and not skip_update then
grid:DelayedUpdateGrid()
end
return true
end
function FluidGridElement:SetConsumption(new_consumption, skip_update)
assert(self.consumption) -- the element should already be a consumer
new_consumption = Max(new_consumption, 0)
if self.consumption == new_consumption then return end
self:RegisterConsumptionChange()
local grid = self.grid
if grid then
local delta = new_consumption - self.consumption
local consumer_list = grid.consumers[self.consume_priority]
if self.variable_consumption then
grid.total_variable_consumption = grid.total_variable_consumption + delta
consumer_list.variable_consumption = consumer_list.variable_consumption + delta
else
grid.total_consumption = grid.total_consumption + delta
consumer_list.consumption = consumer_list.consumption + delta
end
end
self.consumption = new_consumption
if grid and not skip_update then
grid:DelayedUpdateGrid(true)
end
return true
end
function FluidGridElement:SetConsumePriority(new_priority, skip_update)
assert(self.consumption) -- the element should already be a consumer
new_priority = Clamp(new_priority, HighestConsumePriority, LowestConsumePriority)
if self.consume_priority == new_priority then return end
self:RegisterConsumptionChange()
local grid = self.grid
if grid then
local old_consumer_list = grid.consumers[self.consume_priority]
local consumer_list = grid.consumers[new_priority]
if self.variable_consumption then
old_consumer_list.variable_consumption = old_consumer_list.variable_consumption - self.consumption
consumer_list.variable_consumption = consumer_list.variable_consumption + self.consumption
else
old_consumer_list.consumption = old_consumer_list.consumption - self.consumption
consumer_list.consumption = consumer_list.consumption + self.consumption
end
remove_entry(old_consumer_list, self)
consumer_list[#consumer_list + 1] = self
end
self.consume_priority = new_priority
if grid and not skip_update then
grid:DelayedUpdateGrid(true)
end
return true
end
function FluidGridElement:SetStorageCapacity(new_storage_capacity)
if self.storage_capacity == new_storage_capacity then return end
local grid = self.grid
if grid then
grid.total_storage_capacity = grid.total_storage_capacity - self.storage_capacity + Max(new_storage_capacity, 0)
end
self.storage_capacity = new_storage_capacity
if grid then
grid:DelayedUpdateGrid()
end
end
function FluidGridElement:SetStorage(max_charge, max_discharge)
if self.max_charge == max_charge and self.max_discharge == max_discharge then return end
self.max_charge = max_charge
self.max_discharge = max_discharge
local grid = self.grid
self:UpdateStorageChargeDischarge(grid)
if grid then
grid:DelayedUpdateGrid()
end
end
function FluidGridElement:UpdateStorageChargeDischarge(grid)
local current_storage = self.current_storage
local new_charge = Min(self.storage_capacity - current_storage, self.max_charge)
local new_discharge = self.storage_state == "charging" and current_storage < self.min_discharge_amount and 0
or Min(current_storage, self.max_discharge)
local old_charge, old_discharge = self.charge, self.discharge
if new_charge == old_charge and new_discharge == old_discharge then return end
self.charge = new_charge
self.discharge = new_discharge
if grid then
grid.total_charge = grid.total_charge - old_charge + new_charge
grid.total_discharge = grid.total_discharge - old_discharge + new_discharge
if new_discharge ~= old_discharge then
if new_discharge == 0 then
-- remove current storage if max discharge has become 0
grid.current_storage = grid.current_storage - current_storage
elseif old_discharge == 0 then
-- add current storage if max discharge has become > 0
grid.current_storage = grid.current_storage + current_storage
end
end
end
end
function FluidGridElement:AddStoredCharge(delta, grid)
assert(self.current_storage)
local storage_capacity = self.storage_capacity
local old_storage = self.current_storage
local current_storage = Clamp(old_storage + delta, 0, storage_capacity)
if current_storage == old_storage then return end
self.current_storage = current_storage
if self.discharge > 0 then
grid.current_storage = grid.current_storage + current_storage - old_storage
end
self:UpdateStorageChargeDischarge(grid)
local state
if current_storage >= storage_capacity then
state = "full"
elseif current_storage <= 0 then
state = "empty"
elseif current_storage < old_storage then
state = "discharging"
else
state = "charging"
end
if self.storage_state ~= state then
self.storage_state = state
self.owner:SetStorageState(grid.grid_resource, state)
end
self.owner:ChangeStoredAmount(grid.grid_resource, current_storage, old_storage)
end
function FluidGridElement:SetStoredAmount(amount)
return self:AddStoredCharge(amount - self.current_storage, self.grid)
end
function FluidGridElement:SetSmartConnection(smart_connection_index)
local smart_connection = smart_connection_index and (1 << (smart_connection_index - 1)) or false
local old_value = self.smart_connection
if old_value == smart_connection then return end
self.smart_connection = smart_connection
self:SetSwitchState(self.switch_state)
end
function FluidGridElement:GetSmartConnection()
local smart_connection = self.smart_connection
local result = smart_connection and LastSetBit(smart_connection)
return result and result + 1
end
function FluidGridElement:SetSmartConnection2(smart_connection_index)
local smart_connection2 = smart_connection_index and (1 << (smart_connection_index - 1)) or 0
local old_value = self.smart_connection2
if old_value == smart_connection2 then return end
self.smart_connection2 = smart_connection2
self:SetSwitchState(self.switch_state)
end
function FluidGridElement:GetSmartConnection2()
local smart_connection2 = self.smart_connection2
local result = smart_connection2 and LastSetBit(smart_connection2)
return result and result + 1
end
function FluidGridElement:GetSwitchState()
return self.switch_state
end
function FluidGridElement:SetSwitchState(state)
self.switch_state = state
local mask = ((state & 1 == 1) and self.smart_connection or 0)
| ((state & 2 == 2) and self.smart_connection2 or 0)
if self.switch_mask == mask then return end
self.switch_mask = mask
if self.grid then
self.grid:UpdateSmartConnections()
end
return true
end
function FluidGridElementOwner:AsyncCheatShowGrid()
DbgToggleFluidGrid(self:GetPowerGrid())
end
if Platform.developer then
FluidGridElement.grid_changed = false
FluidGridElement.grid_changes = 0
function FluidGridElement:RegisterConsumptionChange()
local now = GameTime()
if self.grid_changed == now then
self.grid_changes = self.grid_changes + 1
else
self.grid_changed = now
self.grid_changes = nil
end
end
function FluidGrid:LogChangedElements()
local changed = {}
for _, element in ipairs(self.elements) do
if element.grid_changes > 0 then
changed[#changed + 1] = element
end
end
table.sortby_field_descending(changed, "grid_changes")
print("Most changed elements in the last", max_grid_updates, "grid updates:")
for i=1,Min(#changed, 10) do
local element = changed[i]
local owner = element.owner
print(owner and owner.class or "<no owner>", element.grid_changes)
end
end
end -- Platform.developer
--[[ Test
function FluidTest()
local grid = FluidGrid:new()
local owner = FluidGridElementOwner:new()
local tc = NewFluidConsumer(owner, 1000)
local tp = NewFluidProducer(owner, 2000)
local ts = NewFluidStorage(owner, 100000, 0, 5000, 5000)
owner.SetStorageState = function (self, res, state) print("Storage", tostring(self), res, state, ts.current_storage) end
owner.SetConsumption = function (self, res, amount) print("Consumer", tostring(self), res, amount == 0 and "off" or amount) end
grid:AddElement(tc)
grid:AddElement(tp)
grid:AddElement(ts)
rawset(_G, "tg", grid)
rawset(_G, "tc", tc)
rawset(_G, "tp", tp)
rawset(_G, "ts", ts)
return grid
end
--]] |