fixed no power need rule

issue1247
Maikel de Vries 2015-02-04 12:40:29 +01:00
parent 750945ee1c
commit a5155f6d88
7 changed files with 185 additions and 107 deletions

View File

@ -9,6 +9,7 @@
with the network by using the functions:
* RegisterPowerRequest(int amount)
* UnregisterPowerRequest()
* UpdatePowerRequest() - used internally only.
The network will then continously search for available power to deliver to
this consumer and will notify it via the callbacks
* OnEnoughPower(int amount)
@ -30,9 +31,13 @@
Using the callback GetActualPowerConsumer() the power consumption of an object
can be passed to its main structure.
The consumer does not consume power if the Rule_NoPowerNeed is active, to set
the need for power on a per consumer basis you can use the function
SetNoPowerNeed(bool no_need).
Important notes when including this library:
* The object including this library should return _inherited(...) in the
Destruction callback if overloaded.
Initialize and Destruction callback if overloaded.
@author Zapper, Maikel
*/
@ -60,6 +65,14 @@ private func UnregisterPowerRequest()
return;
}
// Call this function in the power consuming structure to request and update from
// the power network of this consumer.
private func UpdatePowerRequest()
{
Library_Power->UpdateForPowerLink(this);
return;
}
/*-- Callbacks --*/
@ -100,6 +113,23 @@ public func GetActualPowerConsumer()
/*-- Library Code --*/
// All power related local variables are stored in a single proplist.
// This reduces the chances of clashing local variables. See
// Initialize for which variables are being used.
local lib_power;
// Initialize callback by the engine: check whether the no power need rule is active.
protected func Initialize()
{
// Initialize the single proplist for the power consumer library.
if (lib_power == nil)
lib_power = {};
// A single variable to keep track whether power is needed.
// Power is not needed when the no power need rule is active.
lib_power.power_need = ObjectCount(Find_ID(Rule_NoPowerNeed)) == 0;
return _inherited(...);
}
// Destruction callback by the engine: let power network know this object is not
// a consumer anymore, it must always be unregistered from the power network.
protected func Destruction()
@ -107,3 +137,20 @@ protected func Destruction()
UnregisterPowerRequest();
return _inherited(...);
}
// By calling this function you can make this consumer ignore the power need. This is
// used by the power need rule and can be used by scripters to temporarily turn off the
// need for power in a certain consumer.
public func SetNoPowerNeed(bool no_need)
{
lib_power.power_need = !no_need;
// Make sure the power balance of the network is updated.
UpdatePowerRequest();
return;
}
// Returns whether this consumer has a power need or not.
public func HasPowerNeed()
{
return lib_power.power_need;
}

View File

@ -50,7 +50,8 @@ local lib_power;
protected func Initialize()
{
// Initialize the single proplist for the power library.
lib_power = {};
if (lib_power == nil)
lib_power = {};
// Initialize producer and consumer lists.
// Lists to keep track of the producers and consumers.
lib_power.idle_producers = [];
@ -118,6 +119,18 @@ public func UnregisterPowerConsumer(object consumer)
return;
}
// Definition call: updates the network for this power link.
public func UpdateForPowerLink(object link)
{
// Definition call safety checks.
if (this != Library_Power || !link)
return FatalError("UpdateForPowerLink() either not called from definition context or no link specified.");
// Find the network for this link and update it.
var network = GetPowerNetwork(link);
network->CheckPowerBalance();
return;
}
// Definition call: gives the power helper object.
// TODO: Clean up this code!
public func GetPowerNetwork(object for_obj)
@ -126,17 +139,22 @@ public func GetPowerNetwork(object for_obj)
if (this != Library_Power || !for_obj)
return FatalError("GetPowerNetwork() either not called from definition context or no object specified.");
var w;
while (w = for_obj->~GetActualPowerConsumer())
// Get the actual power consumer for this object. This can for example be the elevator for the case.
var actual;
while (actual = for_obj->~GetActualPowerConsumer())
{
if (w == for_obj)
break; // nope
for_obj = w;
// Stop a possible infinite loop.
if (actual == for_obj)
break;
for_obj = actual;
}
// Get the flag corresponding to the object.
var flag = GetFlagpoleForPosition(for_obj->GetX() - GetX(), for_obj->GetY() - GetY());
// Find the network helper object for this flag.
var helper = nil;
if (!flag) // neutral - needs neutral helper
// If no flag was available the object is neutral and needs a neutral helper.
if (!flag)
{
for (var network in LIB_POWR_Networks)
{
@ -145,18 +163,19 @@ public func GetPowerNetwork(object for_obj)
helper = network;
break;
}
if (helper == nil) // not yet created?
// Create the helper if it does not exist yet.
if (helper == nil)
{
helper = CreateObject(Library_Power, 0, 0, NO_OWNER);
helper.lib_power.neutral_network = true;
LIB_POWR_Networks[GetLength(LIB_POWR_Networks)] = helper;
}
}
}
// Otherwise just get the helper from the flag.
else
{
helper=flag->GetPowerHelper();
// Create the helper if it does not exist yet.
if (helper == nil)
{
helper = CreateObject(Library_Power, 0, 0, NO_OWNER);
@ -165,9 +184,9 @@ public func GetPowerNetwork(object for_obj)
flag->SetPowerHelper(helper);
for (var f in flag->GetLinkedFlags())
{
// Assert different power helpers for same compound.
// Assert different power helpers for the same network.
if (f->GetPowerHelper() != nil)
FatalError("Flags in compound have different power helper!");
FatalError("Flags in the same network have different power helpers.");
f->SetPowerHelper(helper);
}
}
@ -305,7 +324,9 @@ public func AddPowerConsumer(object consumer, int amount, int prio)
if (link.cons_amount != amount || link.priority != prio)
{
lib_power.active_consumers[index] = {obj = consumer, cons_amount = amount, priority = prio};
VisualizePowerChange(link.obj, link.cons_amount, amount, false);
// Only visualize the power change if the consumer had a real power need.
if (link.obj->HasPowerNeed())
VisualizePowerChange(link.obj, link.cons_amount, amount, false);
// Check the power balance of this network, since a change has been made.
CheckPowerBalance();
}
@ -345,7 +366,9 @@ public func RemovePowerConsumer(object consumer)
RemoveArrayIndex(lib_power.active_consumers, index);
// Check the power balance of this network, since a change has been made.
CheckPowerBalance();
VisualizePowerChange(link.obj, link.cons_amount, 0, true);
// Only visualize the power change if the consumer had a real power need.
if (link.obj->HasPowerNeed())
VisualizePowerChange(link.obj, link.cons_amount, 0, true);
return;
}
// If found in neither lists just return without doing anything.
@ -355,7 +378,7 @@ public func RemovePowerConsumer(object consumer)
// Checks the power balance after a change to this network: i.e. removal or addition
// of a consumer or producer. The producers and consumers will be refreshed such that
// the ones with highest priority will be active.
private func CheckPowerBalance()
public func CheckPowerBalance()
{
// First determine whether the storage links in this network need to be producers
// or may be consumers. Get the power needed by all non-storage consumers and the
@ -437,9 +460,13 @@ private func RefreshConsumers(int power_available)
var link = all_consumers[index];
if (!link)
continue;
// Determine the consumption of this consumer, taking into account the power need.
var consumption = link.cons_amount;
if (!link.obj->HasPowerNeed())
consumption = 0;
// Too much power has been used, check if this link was active, if so remove from active.
// Or if the links is a power storage and there is other storage actively producing remove as well.
if (power_used + link.cons_amount > power_available || (link.obj->~IsPowerStorage() && HasProducingStorage()))
if (power_used + consumption > power_available || (link.obj->~IsPowerStorage() && HasProducingStorage()))
{
var idx = GetIndexOf(lib_power.active_consumers, link);
if (idx != -1)
@ -447,22 +474,22 @@ private func RefreshConsumers(int power_available)
PushBack(lib_power.waiting_consumers, link);
RemoveArrayIndex(lib_power.active_consumers, idx);
// On not enough power callback to the deactivated consumer.
link.obj->OnNotEnoughPower(link.cons_amount);
VisualizePowerChange(link.obj, link.cons_amount, 0, true);
link.obj->OnNotEnoughPower(consumption);
VisualizePowerChange(link.obj, consumption, 0, true);
}
}
// In the other case see if consumer is not yet active, if so activate.
else
{
power_used += link.cons_amount;
power_used += consumption;
var idx = GetIndexOf(lib_power.waiting_consumers, link);
if (idx != -1)
{
PushBack(lib_power.active_consumers, link);
RemoveArrayIndex(lib_power.waiting_consumers, idx);
// On enough power callback to the activated consumer.
link.obj->OnEnoughPower(link.cons_amount);
VisualizePowerChange(link.obj, 0, link.cons_amount, false);
link.obj->OnEnoughPower(consumption);
VisualizePowerChange(link.obj, 0, consumption, false);
}
}
}
@ -476,7 +503,8 @@ private func GetPowerConsumption()
for (var index = GetLength(lib_power.active_consumers) - 1; index >= 0; index--)
{
var link = lib_power.active_consumers[index];
if (!link)
// If the link does not exist or has no power need, just continue.
if (!link || !link.obj->HasPowerNeed())
continue;
total += link.cons_amount;
}
@ -491,7 +519,8 @@ private func GetPowerConsumptionNeed()
for (var index = GetLength(all_consumers) - 1; index >= 0; index--)
{
var link = all_consumers[index];
if (!link || link.obj->~IsPowerStorage())
// If the link does not exist, is a power storage or has no power need, just continue.
if (!link || link.obj->~IsPowerStorage() || !link.obj->HasPowerNeed())
continue;
total += link.cons_amount;
}

View File

@ -92,12 +92,17 @@ public func GetStoredPower()
protected func Initialize()
{
// Initialize the single proplist for the power storage library.
lib_power = {};
if (lib_power == nil)
lib_power = {};
// A single variable to keep track of the stored power in the storage.
lib_power.stored_power = 0;
// Register as a power consumer since it has zero stored energy.
RegisterPowerRequest(GetStoragePower());
return _inherited(...);
// Now perform the consumer library's initialization.
_inherited(...);
// Then set the power need for power storages always to true.
lib_power.power_need = true;
return;
}
// Destruction callback by the engine: let power network know this object is not
@ -108,6 +113,18 @@ protected func Destruction()
return _inherited(...);
}
// Overload the consumer library's no power need function to do nothing.
public func SetNoPowerNeed(bool no_need)
{
return;
}
// Returns that a storage always has a power need.
public func HasPowerNeed()
{
return true;
}
// Cooldown effect to prevent continuous switching between consumption and production.
protected func FxCoolDownStart(object target, proplist effect, int temp)
{

View File

@ -1,7 +1,9 @@
/**
No Energy Rule
If this rule is activated, power consumers do not need energy supply
anymore to operate.
anymore to operate. This rule informs existing consumers about its
presence and removal. Consumers created while this rule is active
will themselves set the no power need.
@author Zapper, Maikel
*/
@ -12,89 +14,22 @@ protected func Initialize()
// Don't do anything if this is not the first rule of this type.
if (ObjectCount(Find_ID(Rule_NoPowerNeed)) > 1)
return;
// If there are no power consumers yet, there is nothing to be done.
if (LIB_POWR_Networks == nil)
return;
// Update all consumers forcefully.
var remember = [];
// Get all power compounds (bases).
for (var base in LIB_POWR_Networks)
{
// Iterate through all registered consumers & producers, these are not the sleeping power links.
for (var i = GetLength(base.power_links) - 1; i >= 0; --i)
{
var obj_data = base.power_links[i];
if (obj_data == nil)
continue;
// If it is a power producer, continue to next link.
if (!obj_data.obj->~CurrentlyHasPower() || obj_data.amount > 0)
continue;
// Temporarily stop consuming power to make sure the callback order is correct.
obj_data.obj->~OnNotEnoughPower();
// Power consumer behavior.
obj_data.obj.last_request = 0;
var lpr_a = obj_data.obj.last_amount;
obj_data.obj.last_amount = 0;
// Remove from list.
RemoveArrayIndexUnstable(base.power_links, i);
// Make new power consumer later.
PushBack(remember, {obj = obj_data.obj, amount = lpr_a});
}
// Now awake the sleeping links, after the consumers.
for (var i = GetLength(base.sleeping_links) - 1; i >= 0; --i)
{
var obj_data = base.sleeping_links[i];
PushBack(remember, {obj = obj_data.obj, amount = obj_data.amount});
}
// Reset the sleeping links to an empty list.
base.sleeping_links = [];
}
// Let all remembered consumers consume again!
for (var obj_data in remember)
{
if (obj_data == nil)
continue;
obj_data.obj->MakePowerConsumer(Abs(obj_data.amount));
}
// Find all consumers and tell them to have no power need.
for (var consumer in FindObjects(Find_Func("IsPowerConsumer")))
consumer->SetNoPowerNeed(true);
return;
}
protected func Destruction()
{
// If this is not the last copy of this rule do nothing.
if (ObjectCount(Find_ID(Rule_NoPowerNeed)) >= 1)
if (ObjectCount(Find_ID(Rule_NoPowerNeed)) > 1)
return;
// Schedule the a destruction definition call so that the removal is complete.
Schedule(nil, "Rule_NoPowerNeed->DestructionEx()", 1);
return;
}
public func DestructionEx()
{
// Only perform resetting the links if there are no more rule objects active.
if (ObjectCount(Find_ID(Rule_NoPowerNeed)) >= 1)
return;
// Update all consumers again by going through all existing consumers.
for (var obj in FindObjects(Find_Func("IsPowerConsumer")))
{
var request = obj.last_request;
var amount = obj.last_amount;
// Temporarily stop consuming power to make sure the callback order is correct.
obj->OnNotEnoughPower();
obj.last_amount = 0;
obj->MakePowerConsumer(amount);
}
// Find all consumers and tell them to have power need again.
for (var consumer in FindObjects(Find_Func("IsPowerConsumer")))
consumer->SetNoPowerNeed(false);
return;
}

View File

@ -36,6 +36,7 @@ protected func Initialize()
front->SetAction("Attach", this);
back->SetAction("Attach", this);
return _inherited(...);
}
// Called by the elevator

View File

@ -42,6 +42,7 @@ func Initialize()
var end = GetAnimationLength("pump");
animation = PlayAnimation("pump", 5, Anim_Linear(GetAnimationPosition(animation), start, end, 35, ANIM_Loop), Anim_Const(1000));
SetState("Wait");
return _inherited(...);
}

View File

@ -20,7 +20,7 @@ protected func Initialize()
{
// Create a script player for some tests.
script_plr = nil;
CreateScriptPlayer("Buddy", RGB(0, 0, 255), nil, CSPF_NoEliminationCheck);
CreateScriptPlayer("PowerBuddy", RGB(0, 0, 255), nil, CSPF_NoEliminationCheck);
return;
}
@ -643,7 +643,7 @@ global func Test11_OnStart(int plr)
ScheduleCall(nil, "EliminatePlayer", 6 * 36, 0, script_plr);
// Rejoin the script player for other tests.
ScheduleCall(nil, "CreateScriptPlayer", 9 * 36, 0, "Buddy", RGB(0, 0, 255), nil, CSPF_NoEliminationCheck);
ScheduleCall(nil, "CreateScriptPlayer", 9 * 36, 0, "PowerBuddy", RGB(0, 0, 255), nil, CSPF_NoEliminationCheck);
// Log what the test is about.
Log("Test connecting two networks by different allied players and then elimination of one player.");
@ -664,8 +664,56 @@ global func Test11_OnFinished()
return;
}
// Test for the supported infinite pump loop, with two pumps pumping in opposite directions.
// Test for the no power need rule.
global func Test12_OnStart(int plr)
{
// Power source: one steam engine.
var steam_engine = CreateObjectAbove(SteamEngine, 40, 160, plr);
steam_engine->CreateContents(Coal, 4);
// Power consumers: one workshop, one inventor's lab.
var workshop = CreateObjectAbove(ToolsWorkshop, 110, 160, plr);
workshop->CreateContents(Wood, 4);
workshop->CreateContents(Metal, 4);
workshop->AddToQueue(Shovel, 4);
var lab = CreateObjectAbove(InventorsLab, 450, 248, plr);
lab->CreateContents(Metal, 8);
lab->AddToQueue(TeleGlove, 4);
lab->SetNoPowerNeed(true);
// Power connection: flagpole.
CreateObjectAbove(Flagpole, 304, 140, plr);
// Create the no power need rule.
ScheduleCall(nil, "CreateObject", 3 * 36, 0, Rule_NoPowerNeed);
// Let the lab consumer power again.
ScheduleCall(lab, "SetNoPowerNeed", 6 * 36, 0, false);
// Remove the no power need rule.
Schedule(nil, "RemoveAll(Find_ID(Rule_NoPowerNeed))", 9 * 36, 0);
// Log what the test is about.
Log("Test for the no power need rule.");
return true;
}
global func Test12_Completed()
{
if (ObjectCount(Find_ID(Shovel)) >= 4 && ObjectCount(Find_ID(TeleGlove)) >= 4)
return true;
return false;
}
global func Test12_OnFinished()
{
// Remove wind generator, steam engine, flagpole, shipyard, airship.
RemoveAll(Find_Or(Find_ID(SteamEngine), Find_ID(ToolsWorkshop), Find_ID(InventorsLab)));
return;
}
// Test for the supported infinite pump loop, with two pumps pumping in opposite directions.
global func Test13_OnStart(int plr)
{
// Power source: wind generator producing the power difference between the two pumps.
SetWindFixed(10);
@ -694,14 +742,14 @@ global func Test12_OnStart(int plr)
return true;
}
global func Test12_Completed()
global func Test13_Completed()
{
if (GetMaterial(248, 48) == Material("Water"))
return true;
return false;
}
global func Test12_OnFinished()
global func Test13_OnFinished()
{
// Restore water levels.
DrawMaterialQuad("Water", 144, 168, 208 + 1, 168, 208 + 1, 304, 144, 304, true);