new power system (work in progress)

issue1247
Maikel de Vries 2014-12-30 20:24:13 +01:00
parent a237caa467
commit 2d00588dff
20 changed files with 1386 additions and 1041 deletions

View File

@ -28,40 +28,39 @@ func FxScheduleRefreshAllPowerHelpersTimer()
return -1;
}
// Refreshes all power networks (Library_Power objects).
func RefreshAllPowerHelpers()
{
// no power helpers created yet
if(GetType(Library_Power_power_compounds) != C4V_Array)
// Don't do anything if there are no power helpers created yet.
if (GetType(LIB_POWR_Networks) != C4V_Array)
return;
// special handling for neutral
var neutral = nil;
for(var obj in Library_Power_power_compounds)
// Special handling for neutral networks of which there is only at most one.
var neutral_network = nil;
for (var network in LIB_POWR_Networks)
{
if(!obj || !obj.neutral) continue;
neutral = obj;
if (!network || !network.lib_neutral_network) continue;
neutral_network = network;
break;
}
if (neutral_network)
RefreshPowerHelper(neutral_network);
if(neutral)
// Do the same for all other helpers: delete / refresh.
for (var i = GetLength(LIB_POWR_Networks) - 1; i >= 0; i--)
{
RefreshPowerHelper(neutral);
}
// same for all helpers - delete / refresh
for(var i = GetLength(Library_Power_power_compounds); --i >= 0;)
{
var obj = Library_Power_power_compounds[i];
if (!obj) continue;
if(GetLength(obj.power_links) == 0 && GetLength(obj.sleeping_links) == 0)
{
obj->RemoveObject();
Library_Power_power_compounds[i] = Library_Power_power_compounds[GetLength(Library_Power_power_compounds) - 1];
SetLength(Library_Power_power_compounds, GetLength(Library_Power_power_compounds) - 1);
var network = LIB_POWR_Networks[i];
if (!network)
continue;
}
obj->CheckPowerBalance();
/*if (GetLength(network.power_links) == 0 && GetLength(network.sleeping_links) == 0)
{
network->RemoveObject();
LIB_POWR_Networks[i] = LIB_POWR_Networks[GetLength(LIB_POWR_Networks) - 1];
SetLength(LIB_POWR_Networks, GetLength(LIB_POWR_Networks) - 1);
continue;
}*/
network->CheckPowerBalance();
}
}
@ -334,8 +333,8 @@ func RefreshLinkedFlags()
// since we don't know whether flag links have been lost we will create a new power helper and possibly remove old ones
Library_Power->Init(); // make sure the power system is set up
var old = lflag.power_helper;
lflag.power_helper = CreateObjectAbove(Library_Power, 0, 0, NO_OWNER);
Library_Power_power_compounds[GetLength(Library_Power_power_compounds)] = lflag.power_helper;
lflag.power_helper = CreateObject(Library_Power, 0, 0, NO_OWNER);
LIB_POWR_Networks[GetLength(LIB_POWR_Networks)] = lflag.power_helper;
// list of helpers yet to merge
var to_merge = [old];
@ -364,29 +363,29 @@ func RefreshLinkedFlags()
func RefreshPowerHelper(h)
{
// merge both power_links and sleeping_links
for(var o in h.power_links)
/*for(var o in h.power_links)
{
if(o == nil) continue; // possible
var actual = Library_Power->GetPowerHelperForObject(o.obj);
var actual = Library_Power->GetPowerNetwork(o.obj);
if (!actual) continue;
if(actual == h) continue; // right one already
if (actual == h) continue; // right one already
// remove from old and add to new
h->RemovePowerLink(o.obj, true);
//h->RemovePowerLink(o.obj, true);
actual->AddPowerLink(o.obj, o.amount, true);
}
for(var i = GetLength(h.sleeping_links); --i >= 0;)
for (var i = GetLength(h.sleeping_links); --i >= 0;)
{
var o = h.sleeping_links[i];
var actual = Library_Power->GetPowerHelperForObject(o.obj);
var actual = Library_Power->GetPowerNetwork(o.obj);
if(actual == h) continue; // right one already
// remove from old one and add to new
actual.sleeping_links[GetLength(actual.sleeping_links)] = o;
h.sleeping_links[i] = h.sleeping_links[GetLength(h.sleeping_links) - 1];
SetLength(h.sleeping_links, GetLength(h.sleeping_links) - 1);
}
}*/
}
public func CopyLinkedFlags(object from, array flaglist)

View File

@ -1,194 +1,108 @@
/**
Power Consumer
Handles some aspects of power producing structures, this library
should be included by all power producing structures.
Handles some aspects of power consuming structures, this library should be
included by all power consuming structures. Certain functions should be
overloaded and others can be used to implement the consumption of power in
a uniform way consistent with the network, see text below.
Cares about showing the "No Power"-symbol
and provides CurrentlyHasPower()
also handles requesting 0 power and the NoPowerNeed-rule correctly
The interfact is built up such that the consumer can make a request for power
with the network by using the functions:
* RegisterPowerRequest(int amount)
* UnregisterPowerRequest()
The network will then continously search for available power to deliver to
this consumer and will notify it via the callbacks
* OnEnoughPower(int amount)
* OnNotEnoughPower()
The last callback will be called when the consumer had enough power and the
network stopped delivering to this consumer, due to whatever reason except
the consumer itself unregistering the request for power.
The main part of the power system is handled by Library_Power
The consumer structures can overload the function GetConsumerPriority() and
return the priority for power requests. The library will deliver power
preferentially to consumers with higher priority. In this way certain
consumers can be prioritized over others. Typical return values are:
* Pump 25
* Workshop: 50
* Elevator: 100
See also the scripts of these structures for more details on the usage of the
power consumer library.
Usage:
a power consumer should always include this library.
production (or anything else) should /only/ be started or continued in the callback OnEnoughPower.
in the callback OnNotEnoughPower the user should pause the production.
Using the callback GetActualPowerConsumer() the power consumption of an object
can be passed to its main structure.
when everything is ready to produce the user should call MakePowerConsumer(amount) where amount is the amount of power to request (works with 0).
when the production is done or the building wants to cease consuming power, it should call UnmakePowerConsumer() - note that the callback OnNotEnoughPower is not called this way.
other:
the callback GetActualPowerConsumer can return another object that acts as the power consumer when checking for the affiliation to a flag. For example the elevator case could be the power consumer but use the elevator building as the actual power consumer.
CurrentlyHasPower() returns true when the object has requested power and is not in the sleeping queue.
Note that the object including this library should return _inherited(...) in
the Destruction callback if overloaded.
@author Zapper, Maikel
*/
// Local variables to track power requests and amount.
local has_power = false;
local last_request = 0;
local last_amount = 0;
// States for being able to handle 0-power requests.
static const PowerConsumer_LPR_None = 0;
static const PowerConsumer_LPR_Zero = 1;
static const PowerConsumer_LPR_NonZero = 2;
// This object is a power consumer.
public func IsPowerConsumer() { return true; }
/*-- Interface --*/
// Call this function in the power consuming structure to indicate to the network
// a request for power of the specified amount.
private func RegisterPowerRequest(int amount)
{
Library_Power->RegisterPowerConsumer(this, amount);
return;
}
// Call this function in the power consuming structure to indicate to the network
// a the end of a power request.
private func UnregisterPowerRequest()
{
Library_Power->UnregisterPowerConsumer(this);
return;
}
/*-- Callbacks --*/
// Callback by the power network. Overload this function to start the consumers
// functionality, since enough power is available. return inherited(amount, ...)
// to remove the no-power symbol. It is not allowed to (un)register a power request
// in this callback.
public func OnEnoughPower()
{
// Remove the no-power symbol.
RemoveStatusSymbol(Library_PowerConsumer);
return;
}
// Callback by the power network. Overload this function to stop the consumers
// functionality, since not enough power is available. return inherited(amount, ...)
// to add the no-power symbol. It is not allowed to (un)register a power request
// in this callback.
public func OnNotEnoughPower()
{
// Show the no-power symbol.
AddStatusSymbol(Library_PowerConsumer);
return;
}
// Consumer priority: the need of a consumer to have power. This can be used
// by the structures to prioritize certain consumption, for example letting
// an elevator be dominant over a pump.
public func GetConsumerPriority() { return 0; }
public func CurrentlyHasPower()
{
return has_power;
}
/*-- Interface --*/
// Is the object just a part of a building? For example elevator and its case.
// This callback may return an object which acts as the actual power consumer. For example
// the elevator case may return the main elevator object as the main consumer.
public func GetActualPowerConsumer()
{
return nil;
}
// How much would you like to withdraw your power request?
// Normal objects: not so much, battery: very much.
public func QueryWaivePowerRequest()
{
return 0;
}
// The object requested power but there is no power!
// Should possibly not use MakePowerConsumer/Producer in this callback.
public func OnNotEnoughPower()
{
has_power = false;
// Show symbol.
this->AddStatusSymbol(Library_PowerConsumer);
return;
}
/*-- Library Code --*/
// called when the object was deleted from the sleeping queue
// that means, the object had requested power before
public func OnRemovedFromPowerSleepingQueue()
{
// Remove symbol.
this->RemoveStatusSymbol(Library_PowerConsumer);
return;
}
// Called when consumer was sleeping but power is available again.
// Should possibly not use MakePowerConsumer/Producer in this callback.
public func OnEnoughPower()
{
has_power = true;
// Remove symbol.
this->RemoveStatusSymbol(Library_PowerConsumer);
return;
}
// Add/Remove an effect such that this structure does not need power
public func SetNoPowerNeed(bool to_val)
{
if (to_val)
AddEffect("NoPowerNeed", this, 1);
else
RemoveEffect("NoPowerNeed", this);
return true;
}
public func FxNoPowerNeedSaveScen(object obj, proplist fx, proplist props)
{
// This building doesn't need power, save that to scenario.
props->AddCall("NoPowerNeed", obj, "SetNoPowerNeed", true);
return true;
}
// Wrapper for MakePowerConsumer to handle requesting 0 power and the NoPowerNeed rule correctly.
// With an option to just pass on to the global method.
public func MakePowerConsumer(int amount, bool just_pass_to_global)
{
if (just_pass_to_global == true)
return inherited(amount, just_pass_to_global, ...);
var no_power_need = !!ObjectCount(Find_ID(Rule_NoPowerNeed)) || GetEffect("NoPowerNeed", this);
// Don't do anything if the request is the exact same as the previous one (succesive call).
if ((amount > 0) && !no_power_need)
if (last_request == PowerConsumer_LPR_NonZero)
if (last_amount == amount)
return true;
// Special handling for zero amount.
if ((amount == 0) || no_power_need)
{
// Initially requesting 0 power?
if (last_request == PowerConsumer_LPR_None)
{
last_request = PowerConsumer_LPR_Zero;
last_amount = amount;
// Always enable.
this->~OnEnoughPower();
return true;
}
// Requesting 0 power as a second request.
else if (last_request == PowerConsumer_LPR_Zero)
{
last_amount = amount;
// Should still have power at this point.
return true;
}
// Requesting 0 power after having requested nonzero power.
else
{
last_request = PowerConsumer_LPR_Zero;
last_amount = amount;
// Remove as official power consumer.
inherited(0);
// Re-enable power supply.
this->~OnEnoughPower();
return true;
}
}
else
{
// Requesting power != 0.
last_request = PowerConsumer_LPR_NonZero;
last_amount = amount;
}
return inherited(amount, just_pass_to_global, ...);
}
// Turns the object off as a power consumer.
public func UnmakePowerConsumer()
{
// Succesive calls have no effect.
if (last_request == PowerConsumer_LPR_None)
return true;
// We don't have power anymore.
has_power = false;
// We were not officially registered as power consumer anyway.
if (last_request == PowerConsumer_LPR_Zero)
{
last_request = PowerConsumer_LPR_None;
return true;
}
last_request = PowerConsumer_LPR_None;
return MakePowerConsumer(0, true);
}
// Destruction callback: let power network know this object is not a consumer anymore.
// 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.
public func Destruction()
{
UnmakePowerConsumer();
UnregisterPowerRequest();
return _inherited(...);
}

View File

@ -1,7 +1,4 @@
[DefCore]
id=Library_Power
Version=6,0
Category=C4D_StaticBack
Width=1
Height=1
Offset=-1,-1
Category=C4D_StaticBack

View File

@ -1,29 +1,100 @@
/**
Power Producer
Handles some aspects of power producing structures, this library should be
included by all power producing structures. These structures can overload
the function GetProducerPriority() and return the priority for delivering
power to the network. The library will preferentially drain power from the
producers with highest priority, so that on-demand producers are not drained
before power storages or steady producers. Typical return values are:
included by all power producing structures. Certain functions should be
overloaded and others can be used to implement the production of power
in a uniform way consistent with the network, see text below.
The interface is built up such that the producer can let the network know it
is ready to deliver power to the network by the functions
* RegisterPowerProduction(int amount)
* UnregisterPowerProduction()
Then the network will address the power producer via the callbacks
* OnPowerProductionStart(int amount)
* OnPowerProductionStop()
to start and stop the power production. The producer should overload these
functions and return true if the starting or stopping has succeeded. For
steady producers these calls are not made and a steady production of power
is assumed. A steady producer should have return value true for the function
* IsSteadyPowerProducer()
These structures can overload the function GetProducerPriority() and return
the priority for delivering power to the network. The library will
preferentially drain power from the producers with highest priority, so that
on-demand producers are not drained before power storages or steady producers.
Typical return values are:
* Wind generator: 100
* Compensator: 50
* Steam engine: 0
* Steam engine: 0
See also the scripts of these structures for more details on the usage of the
power producer library.
Note that the object including this library should return _inherited(...) in
the Destruction callback if overloaded.
@author Zapper, Maikel
*/
// This object is a power producer.
public func IsPowerProducer() { return true; }
/*-- Interface -- */
// Call this function in the power producing structure to indicate to the network
// that this structure is available and able to produce the specified amount of power.
private func RegisterPowerProduction(int amount)
{
Library_Power->RegisterPowerProducer(this, amount);
return;
}
// Call this function in the power producing structure to indicate to the network
// that this structure is not able to produce any power any more.
private func UnregisterPowerProduction()
{
Library_Power->UnregisterPowerProducer(this);
return;
}
/*-- Callbacks --*/
// Callback by the power network. Overload this function and start the production
// of power in this structure for the requested amount if possible.
public func OnPowerProductionStart(int amount)
{
// A return value of false indicates to the network that power production is not possible.
return false;
}
// Callback by the power network. Overload this function and stop the production
// of power in this structure if possible.
public func OnPowerProductionStop()
{
// A return value of false indicates to the network that stopping the current production
// was not possible, this should in principle never happen. However, if so the network
// must assume this producer is still actively delivering power.
return true;
}
// Whether this object is a steady power producer which continuously delivers
// power to the network it is in, a producer is not steady by default.
public func IsSteadyPowerProducer() { return false; }
// Producer priority: the willingsness of a producer to deliver energy.
// This is high for steady producers like the wind generator and low
// for producers like the steam engine.
public func GetProducerPriority() { return 0; }
// Destruction callback: let power network know this object is not a producer anymore.
/*-- Library Code --*/
// Destruction callback by the engine: let power network know this object is not a producer
// anymore. A power producer must always be unregistered from the network.
public func Destruction()
{
MakePowerProducer(0);
UnregisterPowerProduction();
return _inherited(...);
}

View File

@ -1,312 +1,148 @@
/**
Power Library
Cares about power management of a base.
Handles the aspects for a single power networks, for each network in a round
a copy of this library object is created. For each network it is being kept
track of which are the idle/active producers and idle/active consumers and
power is requested from producers and distributed for consumers according
to priority.
callbacks:
QueryWaivePowerRequest()
OnNotEnoughPower()
OnEnoughPower()
OnRemovedFromPowerSleepingQueue(): called when the object was removed from the sleeping queue
globals:
MakePowerConsumer(int amount)
Note: power consumers include the library Library_PowerConsumer and should use UnmakePowerConsumer to turn off as power consumers
MakePowerProducer(int amount)
IsPowerAvailable(int amount)
Callbacks to the power producers (see producer library for details):
* OnPowerProductionStart(int amount)
* OnPowerProductionStop()
* GetProducerPriority()
* IsSteadyPowerProducer()
Callbacks to the power consumers (see consumer library for details):
* OnEnoughPower(int amount)
* OnNotEnoughPower()
* GetConsumerPriority()
* GetActualPowerConsumer()
The network object keeps track of the producers and consumers in lists.
* lib_idle_producers (currently not producing power)
* lib_active_producers (currently producing power)
* lib_waiting_consumers (waiting for power)
* lib_active_consumers (supplied consumers)
Producers are stored according to {obj, prod_amount, priority} and consumers
according to {obj, cons_amount, priority}.
OPEN TODOS:
* Figure out where the nil's and the errors come from.
* Implement correct treatment for steady producers.
* Check the flag library for network merging.
* Check the power balance, also with merging.
@author Zapper, Maikel
*/
static Library_Power_power_compounds;
// for the helper definitions
local power_links; // producers and consumers
local sleeping_links;
local power_balance; // performance
local neutral; // is "the" neutral helper?
// A static variable is used to store the different power networks.
// This variable is also accessed by the flag library.
static LIB_POWR_Networks;
// Lists to keep track of the producers and consumers.
local lib_idle_producers;
local lib_active_producers;
local lib_waiting_consumers;
local lib_active_consumers;
// Main variable which keeps track of the power balance in the network.
local lib_power_balance;
// Whether the network is neutral.
local lib_neutral_network;
// Initialize the local variables needed to keep track of each power network.
protected func Initialize()
{
power_links = [];
sleeping_links = [];
power_balance = 0;
neutral = false;
// Initialize producer and consumer lists.
lib_idle_producers = [];
lib_active_producers = [];
lib_waiting_consumers = [];
lib_active_consumers = [];
// Set power balance to zero.
lib_power_balance = 0;
// Network is not neutral by default.
lib_neutral_network = false;
return;
}
// Adds a power consumer to the network which produces an amount of power.
public func AddPowerProducer(object producer, int amount)
/*-- Definition Calls --*/
// Definition call: registers a power producer with specified amount.
public func RegisterPowerProducer(object producer, int amount)
{
return AddPowerLink(producer, amount);
// Definition call safety checks.
if (this != Library_Power || !producer || !producer->~IsPowerProducer())
return FatalError("RegisterPowerProducer() either not called from definition context or no producer specified.");
Library_Power->Init();
// Find the network for this producer and add it.
var network = GetPowerNetwork(producer);
network->AddPowerProducer(producer, amount, producer->GetProducerPriority());
return;
}
// Adds a power consumer to the network which consumes an amount of power.
public func AddPowerConsumer(object consumer, int amount)
// Definition call: unregisters a power producer.
public func UnregisterPowerProducer(object producer)
{
// Check whether this consumer is among the sleeping links of this network.
for (var i = GetLength(sleeping_links) - 1; i >= 0; i--)
{
var link = sleeping_links[i];
if (link.obj != consumer)
continue;
// Did not affect power balance, we can just remove/change the link.
// If amount equals zero, we can remove the consumer from the network.
if (amount == 0)
{
sleeping_links[i] = sleeping_links[GetLength(sleeping_links) - 1];
SetLength(sleeping_links, GetLength(sleeping_links) - 1);
// Visualize the consumption change and notify the link object.
VisualizePowerChange(link.obj, 0, link.amount, false);
link.obj->~OnRemovedFromPowerSleepingQueue();
return true;
}
// Otherwise, set the new power consumption of this link.
sleeping_links[i].amount = -amount;
return true;
}
// Consumer is not a sleeping link, therefore add it to the network.
return AddPowerLink(consumer, -amount);
// Definition call safety checks.
if (this != Library_Power || !producer || !producer->~IsPowerProducer())
return FatalError("UnregisterPowerProducer() either not called from definition context or no producer specified.");
Library_Power->Init();
// Find the network for this producer and remove it.
var network = GetPowerNetwork(producer);
network->RemovePowerProducer(producer);
return;
}
// Removes a power link from its network.
public func RemovePowerLink(object power_link)
// Definition call: registers a power consumer with specified amount.
public func RegisterPowerConsumer(object consumer, int amount)
{
return AddPowerLink(power_link, 0);
// Definition call safety checks.
if (this != Library_Power || !consumer || !consumer->~IsPowerConsumer())
return FatalError("RegisterPowerConsumer() either not called from definition context or no consumer specified.");
Library_Power->Init();
// Find the network for this consumer and add it.
var network = GetPowerNetwork(consumer);
network->AddPowerConsumer(consumer, amount, consumer->GetConsumerPriority());
return;
}
// Adds a power link to the network.
public func AddPowerLink(object power_link, int power_amount, bool surpress_balance_check)
// Definition call: unregisters a power consumer.
public func UnregisterPowerConsumer(object consumer)
{
var new_link = {obj = power_link, amount = power_amount};
var before = 0;
var found = false;
var first_empty = -1;
var diff = 0;
for (var i = GetLength(power_links) - 1; i >= 0; i--)
{
var link = power_links[i];
// Possible
if (link == nil)
{
first_empty = i;
continue;
}
// Removed from outside.
if (link.obj == nil)
{
power_links[i] = nil;
continue;
}
if (link.obj != power_link)
continue;
found = true;
diff = power_amount - link.amount;
power_balance += diff;
before = power_links[i].amount;
if (power_amount == 0)
power_links[i] = nil;
else
power_links[i] = new_link;
break;
}
if (!found)
{
// place to insert?
if (power_amount != 0)
{
if (first_empty != -1)
power_links[first_empty] = new_link;
else
power_links[GetLength(power_links)] = new_link;
}
diff = new_link.amount;
power_balance += diff;
}
if ((new_link.amount > 0) || ((new_link.amount == 0) && (before > 0)))
VisualizePowerChange(new_link.obj, new_link.amount, before, false);
else if ((new_link.amount < 0) || ((new_link.amount == 0) && (before < 0)))
VisualizePowerChange(new_link.obj, new_link.amount, before, false);
if (new_link.amount < 0)
new_link.obj->~OnEnoughPower(); // might be reverted soon, though
if (!surpress_balance_check)
CheckPowerBalance();
return true;
}
// Main function of the power library which checks the power balance in a closed network.
// This function will deactivate consumers and activate producers whenever necessary.
public func CheckPowerBalance()
{
// Special handling for ownerless links: always sleep them.
if (neutral)
{
for (var i = GetLength(power_links) - 1; i >= 0; i--)
{
var link = power_links[i];
if (link == nil)
continue;
// Power producers don't need te be put down.
if (link.amount > 0)
continue;
SleepLink(i);
}
return false;
}
// Network is not neutral so we can revive sleeping links if the balance is positive.
if (power_balance >= 0)
{
// Loop over all sleeping links and find the best one to revive.
// This is the link with highest priority and which fits the power surplus.
var highest_priority = -0xffffff;
var best_link = nil;
for (var i = GetLength(sleeping_links) - 1; i >= 0; i--)
{
var link = sleeping_links[i];
if (link.obj == nil)
{
sleeping_links[i] = sleeping_links[GetLength(sleeping_links) - 1];
SetLength(sleeping_links, GetLength(sleeping_links) - 1);
continue;
}
// Check if there is enough power to revive this link.
if (power_balance + link.amount < 0)
continue;
// Store the link with current highest priority.
var priority = link.obj->~GetConsumerPriority();
if (priority > highest_priority)
{
highest_priority = priority;
best_link = i;
}
}
// Revive the best link if found and don't execute the rest of this function.
if (best_link != nil)
UnsleepLink(best_link);
return true;
}
// Network has not enough available power: we need to deactivate some consumers.
// Look for the best link to sleep.
var best_amount = -0xffffff;
var least_priority = 0xffffff;
var best_link = nil;
for (var i = GetLength(power_links) - 1; i >= 0; i--)
{
var link = power_links[i];
// Do not shut down producers or invalid links.
if (link == nil || link.amount > 0)
continue;
// New best link if priority is less or equal and amount is larger.
var amount = -link.amount;
var priority = link.obj->~GetConsumerPriority();
if (priority < least_priority || (priority == least_priority && amount > best_amount))
{
best_amount = amount;
least_priority = priority;
best_link = i;
}
}
// Total blackout? No consumer active anymore?
if (best_link == nil)
return false;
// There is a link which we can sleep.
SleepLink(best_link);
// Recurse, because we might need to sleep another link or might activate a consumer with less demands.
CheckPowerBalance();
return false;
}
// Removes a link with given index from the power links and adds it to the sleeping links.
public func SleepLink(int index)
{
// Safety: index must be valid.
if (index < 0 || index >= GetLength(power_links))
return FatalError("SleepLink() called without or invalid index!");
// Only sleep consumers, it does not make sense to sleep producers.
var link = power_links[index];
if (link.amount > 0)
return FatalError("SleepLink() trying to sleep a producer!");
// Delete link from power links and add to sleeping links.
power_links[index] = nil;
power_balance -= link.amount;
sleeping_links[GetLength(sleeping_links)] = link;
// Notify link that it has not enough power and visualize the change.
link.obj->~OnNotEnoughPower();
VisualizePowerChange(link.obj, 0, link.amount, true);
return true;
}
// Removes a link with given index from the sleeping links and adds it to the power links.
public func UnsleepLink(int index)
{
// Safety: index must be valid.
if (index < 0 || index >= GetLength(sleeping_links))
return FatalError("UnsleepLink() called without or invalid index!");
// Get the sleeping link.
var link = sleeping_links[index];
// Delete this link from the list of sleeping links.
sleeping_links[index] = sleeping_links[GetLength(sleeping_links) - 1];
SetLength(sleeping_links, GetLength(sleeping_links) - 1);
// Revive the link by adding it to the power links.
return AddPowerLink(link.obj, link.amount);
}
// get requested power of nodes that are currently sleeping
public func GetPendingPowerAmount()
{
var sum = 0;
for (var i = GetLength(sleeping_links) - 1; i >= 0; i--)
sum += -sleeping_links[i].amount;
return sum;
}
// should always be above zero - otherwise an object would have been deactivated
public func GetPowerBalance()
{
return power_balance;
}
public func IsPowerAvailable(object obj, int amount)
{
// Ignore object for now.
return power_balance >= amount;
}
public func Init()
{
if (GetType(Library_Power_power_compounds) != C4V_Array)
Library_Power_power_compounds = [];
// Definition call safety checks.
if (this != Library_Power || !consumer || !consumer->~IsPowerConsumer())
return FatalError("UnregisterPowerConsumer() either not called from definition context or no consumer specified.");
Library_Power->Init();
// Find the network for this consumer and remove it.
var network = GetPowerNetwork(consumer);
network->RemovePowerConsumer(consumer);
return;
}
// Definition call: gives the power helper object.
public func GetPowerHelperForObject(object who)
// TODO: Clean up this code!
public func GetPowerNetwork(object for_obj)
{
// Definition call safety checks.
if (this != Library_Power || !for_obj)
return FatalError("GetPowerNetwork() either not called from definition context or no object specified.");
var w;
while(w = who->~GetActualPowerConsumer())
while (w = for_obj->~GetActualPowerConsumer())
{
if (w == who)
if (w == for_obj)
break; // nope
who = w;
for_obj = w;
}
var flag = GetFlagpoleForPosition(who->GetX() - GetX(), who->GetY() - GetY());
var flag = GetFlagpoleForPosition(for_obj->GetX() - GetX(), for_obj->GetY() - GetY());
var helper = nil;
if (!flag) // neutral - needs neutral helper
{
for (var obj in Library_Power_power_compounds)
for (var obj in LIB_POWR_Networks)
{
if (!obj || !obj.neutral)
continue;
@ -318,7 +154,7 @@ public func GetPowerHelperForObject(object who)
{
helper = CreateObjectAbove(Library_Power, 0, 0, NO_OWNER);
helper.neutral = true;
Library_Power_power_compounds[GetLength(Library_Power_power_compounds)] = helper;
LIB_POWR_Networks[GetLength(LIB_POWR_Networks)] = helper;
}
}
else
@ -327,8 +163,8 @@ public func GetPowerHelperForObject(object who)
if (helper == nil)
{
helper = CreateObjectAbove(Library_Power, 0, 0, NO_OWNER);
Library_Power_power_compounds[GetLength(Library_Power_power_compounds)] = helper;
helper = CreateObject(Library_Power, 0, 0, NO_OWNER);
LIB_POWR_Networks[GetLength(LIB_POWR_Networks)] = helper;
// Add to all linked flags.
flag->SetPowerHelper(helper);
@ -344,71 +180,354 @@ public func GetPowerHelperForObject(object who)
return helper;
}
/*-- Global Interface --*/
// Returns the amount of unavailable power that is currently being requested.
global func GetPendingPowerAmount()
// Definition call: Initializes tracking of the power networks.
public func Init()
{
if (!this)
return 0;
Library_Power->Init();
return (Library_Power->GetPowerHelperForObject(this))->GetPendingPowerAmount();
}
// Returns the current power balance of the area an object is in.
// This is roughly equivalent to produced power - consumed power.
global func GetCurrentPowerBalance()
{
if (!this)
return 0;
Library_Power->Init();
return (Library_Power->GetPowerHelperForObject(this))->GetPowerBalance();
}
// Turns the object into a power producer that produces amount of power until this function is called again with amount = 0.
global func MakePowerProducer(int amount)
{
if (!this)
return false;
Library_Power->Init();
var power_helper = Library_Power->GetPowerHelperForObject(this);
if (!power_helper)
return false;
return power_helper->AddPowerProducer(this, amount);
}
// Turns the power producer into an object that does not produce power.
global func UnmakePowerProducer()
{
MakePowerProducer(0);
// Definition call safety checks.
if (this != Library_Power)
return;
// Initialize the list of networks if not done already.
if (GetType(LIB_POWR_Networks) != C4V_Array)
LIB_POWR_Networks = [];
return;
}
// Returns true if the current power balance is bigger or equal to the requested amount.
global func IsPowerAvailable(int amount)
/*-- Library Code --*/
public func AddPowerProducer(object producer, int amount, int prio)
{
if (!this)
return false;
Library_Power->Init();
return (Library_Power->GetPowerHelperForObject(this))->IsPowerAvailable(this, amount);
// Debugging logs.
Log("POWR - AddPowerProducer(): network = %v, frame = %d, producer = %v, amount = %d, priority = %d", this, FrameCounter(), producer, amount, prio);
// Check if it is not already in the list of idle producers.
for (var index = GetLength(lib_idle_producers) - 1; index >= 0; index--)
{
var link = lib_idle_producers[index];
if (!link || link.obj != producer)
continue;
// If it is in the list update the values if they have changed.
if (link.prod_amount != amount || link.priority != prio)
{
lib_idle_producers[index] = {obj = producer, prod_amount = amount, priority = prio};
// Check the power balance of this network, since a change has been made.
CheckPowerBalance();
}
return;
}
// Check if it is not already in the list of active producers.
for (var index = GetLength(lib_active_producers) - 1; index >= 0; index--)
{
var link = lib_active_producers[index];
if (!link || link.obj != producer)
continue;
// If it is in the list update the values if they have changed.
if (link.prod_amount != amount || link.priority != prio)
{
lib_active_producers[index] = {obj = producer, prod_amount = amount, priority = prio};
// Check the power balance of this network, since a change has been made.
CheckPowerBalance();
}
return;
}
// Producer was in neither list, so add it to the list of idle producers.
PushBack(lib_idle_producers, {obj = producer, prod_amount = amount, priority = prio});
// Check the power balance of this network, since a change has been made.
CheckPowerBalance();
return;
}
// Turns the object into a power consumer, consumption is turned of with amoun = 0.
global func MakePowerConsumer(int amount)
public func RemovePowerProducer(object producer)
{
if (!this)
return false;
Library_Power->Init();
return (Library_Power->GetPowerHelperForObject(this))->AddPowerConsumer(this, amount);
// Debugging logs.
Log("POWR - RemovePowerProducer(): network = %v, frame = %d, producer = %v", this, FrameCounter(), producer);
// Remove producer from the list of idle producers if it is in there.
for (var index = GetLength(lib_idle_producers) - 1; index >= 0; index--)
{
var link = lib_idle_producers[index];
if (!link || link.obj != producer)
continue;
// Remove the producer from the list.
RemoveArrayIndex(lib_idle_producers, index);
return;
}
// Otherwise, remove producer from the list of active producers if it is in there.
for (var index = GetLength(lib_active_producers) - 1; index >= 0; index--)
{
var link = lib_active_producers[index];
if (!link || link.obj != producer)
continue;
// Reduce the power balance and remove the producer from the list.
lib_power_balance -= link.prod_amount;
RemoveArrayIndex(lib_active_producers, index);
// Notify the active power producer that it should stop producing power.
producer->OnPowerProductionStop();
// Check the power balance of this network, since a change has been made.
CheckPowerBalance();
VisualizePowerChange(link.obj, link.prod_amount, 0, false);
return;
}
// If found in neither lists just return without doing anything.
return;
}
public func AddPowerConsumer(object consumer, int amount, int prio)
{
// Debugging logs.
Log("POWR - AddPowerConsumer(): network = %v, frame = %d, consumer = %v, amount = %d, priority = %d", this, FrameCounter(), consumer, amount, prio);
// Check if it is not already in the list of waiting consumers.
for (var index = GetLength(lib_waiting_consumers) - 1; index >= 0; index--)
{
var link = lib_waiting_consumers[index];
if (!link || link.obj != consumer)
continue;
// If it is in the list update the values if they have changed.
if (link.cons_amount != amount || link.priority != prio)
{
lib_waiting_consumers[index] = {obj = consumer, cons_amount = amount, priority = prio};
// Check the power balance of this network, since a change has been made.
CheckPowerBalance();
}
return;
}
// Check if it is not already in the list of active consumers.
for (var index = GetLength(lib_active_consumers) - 1; index >= 0; index--)
{
var link = lib_active_consumers[index];
if (!link || link.obj != consumer)
continue;
// If it is in the list update the values if they have changed.
if (link.cons_amount != amount || link.priority != prio)
{
lib_active_consumers[index] = {obj = consumer, cons_amount = amount, priority = prio};
// Check the power balance of this network, since a change has been made.
CheckPowerBalance();
}
return;
}
// Consumer was in neither list, so add it to the list of waiting consumers.
PushBack(lib_waiting_consumers, {obj = consumer, cons_amount = amount, priority = prio});
// Check the power balance of this network, since a change has been made.
CheckPowerBalance();
return;
}
public func RemovePowerConsumer(object consumer)
{
// Debugging logs.
Log("POWR - RemovePowerConsumer(): network = %v, frame = %d, consumer = %v", this, FrameCounter(), consumer);
// Remove consumer from the list of waiting consumers if it is in there.
for (var index = GetLength(lib_waiting_consumers) - 1; index >= 0; index--)
{
var link = lib_waiting_consumers[index];
if (!link || link.obj != consumer)
continue;
// Remove the consumer from the list.
RemoveArrayIndex(lib_waiting_consumers, index);
return;
}
// Otherwise, remove consumer from the list of active consumers if it is in there.
for (var index = GetLength(lib_active_consumers) - 1; index >= 0; index--)
{
var link = lib_active_consumers[index];
if (!link || link.obj != consumer)
continue;
// Increase the power balance and remove the consumer from the list.
lib_power_balance += link.cons_amount;
RemoveArrayIndex(lib_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);
return;
}
// If found in neither lists just return without doing anything.
return;
}
private func CheckPowerBalance()
{
// First get the possible power surplus in the network from idle producers.
var surplus = GetPowerSurplus();
// Debugging logs.
LogState("balance start");
// If the surplus is non-positive it implies that first all possible idle producers
// need to be activated and then some consumers need to be deactivated.
if (surplus <= 0 && lib_power_balance < 0)
{
// Activate all idle producers.
ActivateProducers(surplus - lib_power_balance);
// The power balance may have changed but can't be positive, so disable sufficient consumers.
DeactivateConsumers(-lib_power_balance);
// All producers are running and the maximum amount of consumers as well.
LogState("balance_end s < 0");
return;
}
// Otherwise if the surplus is positive one or some idle consumers may be activated.
else if (surplus > 0)
{
// Activate waiting consumers according to the surplus and their priorities.
ActivateConsumers(surplus);
// These newly activated consumers and the already maybe negative power balance must then
// finally be counteracted by activating idle producers according to the new balance.
// It is also possible that some producers may be deactivated.
if (lib_power_balance > 0)
DeactivateProducers(lib_power_balance);
else if (lib_power_balance < 0)
ActivateProducers(-lib_power_balance);
LogState("balance_end s > 0");
return;
}
// TODO: what about the third case? Should we really do nothing there?
// TODO: what about swapping consumers with different priority?
LogState("balance_end s == 0");
return;
}
// Returns the possible power production from idle producers combined with the current power balance.
private func GetPowerSurplus()
{
// This is just the sum of the balance and possible power from all idle producers.
var surplus = lib_power_balance;
for (var index = GetLength(lib_idle_producers) - 1; index >= 0; index--)
{
var link = lib_idle_producers[index];
if (!link)
continue;
surplus += link.prod_amount;
}
return surplus;
}
// Activates idle producers according to priority until power need is satisfied.
private func ActivateProducers(int power_need)
{
// Debugging logs.
Log("POWR - ActivateProducers(): network = %v, frame = %d, power_need = %d", this, FrameCounter(), power_need);
var power_found = 0;
// Sort the idle producers according to priority and then activate producers until balance is restored.
if (GetLength(lib_idle_producers) > 1) SortArrayByProperty(lib_idle_producers, "priority");
for (var index = GetLength(lib_idle_producers) - 1; index >= 0; index--)
{
var link = lib_idle_producers[index];
if (!link)
continue;
power_found += link.prod_amount;
PushBack(lib_active_producers, link);
link.obj->OnPowerProductionStart(link.prod_amount);
lib_power_balance += link.prod_amount;
SetLength(lib_idle_producers, index);
VisualizePowerChange(link.obj, 0, link.prod_amount, false);
// Stop activatng producers if power need is satisfied.
if (power_found >= power_need)
return true;
}
return false;
}
// Deactivates idle producers according to priority until power surplus is gone.
private func DeactivateProducers(int power_surplus)
{
// Debugging logs.
Log("POWR - DeactivateProducers(): network = %v, frame = %d, power_surplus = %d", this, FrameCounter(), power_surplus);
var power_killed = 0;
// Sort the active producers according to priority and deactivate them until balance is restored.
if (GetLength(lib_active_producers) > 1) SortArrayByProperty(lib_active_producers, "priority", true);
for (var index = GetLength(lib_active_producers) - 1; index >= 0; index--)
{
var link = lib_active_producers[index];
power_killed += link.prod_amount;
// Stop deactivating producers if power surplus has been reached.
if (power_killed > power_surplus)
break;
// Move active producer to idle producers.
PushBack(lib_idle_producers, link);
link.obj->OnPowerProductionStop();
lib_power_balance -= link.prod_amount;
SetLength(lib_active_producers, index);
VisualizePowerChange(link.obj, link.prod_amount, 0, false);
}
return;
}
// Activates waiting consumers according to priotrity until available power is used.
private func ActivateConsumers(int power_available)
{
// Debugging logs.
Log("POWR - ActivateConsumers(): network = %v, frame = %d, power_available = %d", this, FrameCounter(), power_available);
var power_used = 0;
// Sort the waiting consumers according to priority and then activate consumers until available power is used.
if (GetLength(lib_waiting_consumers) > 1) SortArrayByProperty(lib_waiting_consumers, "priority");
for (var index = GetLength(lib_waiting_consumers) - 1; index >= 0; index--)
{
var link = lib_waiting_consumers[index];
// If this consumer requires to much power try one of lower priority.
if (power_used + link.cons_amount > power_available)
continue;
power_used += link.cons_amount;
PushBack(lib_active_consumers, link);
link.obj->OnEnoughPower(link.cons_amount);
lib_power_balance -= link.cons_amount;
RemoveArrayIndex(lib_waiting_consumers, index);
VisualizePowerChange(link.obj, 0, link.cons_amount, false);
}
return;
}
// Deactivates active consumers according to priority until power need is satisfied.
private func DeactivateConsumers(int power_need)
{
// Debugging logs.
Log("POWR - DeactivateConsumers(): network = %v, frame = %d, power_need = %d", this, FrameCounter(), power_need);
var power_restored = 0;
// Sort the active consumers according to priority and then deactivate consumers until balance is restored.
if (GetLength(lib_active_consumers) > 1) SortArrayByProperty(lib_active_consumers, "priority", true);
for (var index = GetLength(lib_active_consumers) - 1; index >= 0; index--)
{
var link = lib_active_consumers[index];
power_restored += link.cons_amount;
PushBack(lib_waiting_consumers, link);
link.obj->OnNotEnoughPower(link.cons_amount);
lib_power_balance += link.cons_amount;
SetLength(lib_active_consumers, index);
VisualizePowerChange(link.obj, link.cons_amount, 0, true);
// Stop deactivating consumers if enough power has been freed.
if (power_restored >= power_need)
return true;
}
// This should not ever happen, so put a log here.
Log("Not enough power consumers to deactivate for restoring the power balance. How could this happen?");
return false;
}
/*-- Logging --*/
private func LogState(string tag)
{
Log("==========================================================================");
Log("POWR - State for network %v in frame %d with tag %s", this, FrameCounter(), tag);
Log("==========================================================================");
Log("POWR - lib_neutral_network: %v", lib_neutral_network);
Log("POWR - lib_idle_producers: %v", lib_idle_producers);
Log("POWR - lib_active_producers: %v", lib_active_producers);
Log("POWR - lib_waiting_consumers: %v", lib_waiting_consumers);
Log("POWR - lib_active_consumers: %v", lib_active_consumers);
Log("POWR - lib_power_balance = %d", lib_power_balance);
Log("POWR - surplus: %d", GetPowerSurplus());
Log("==========================================================================");
return;
}
/*-- Power Visualization --*/
// Visualizes the power change on an object.
private func VisualizePowerChange(object obj, int to, int before, bool loss)
// Visualizes the power change on an object from before to to.
private func VisualizePowerChange(object obj, int old_val, int new_val, bool loss)
{
// Safety: object must exist.
if (!obj)
return FatalError("VisualizePowerChange() is called for a non-existing object.");
var before_current = nil;
var effect = GetEffect("VisualPowerChange", obj);
if (!effect)
@ -416,25 +535,25 @@ private func VisualizePowerChange(object obj, int to, int before, bool loss)
else
before_current = effect.current;
var to_abs = Abs(to);
var before_abs = Abs(before);
var old_abs = Abs(old_val);
var new_abs = Abs(new_val);
effect.max = Max(to_abs, before_abs);
effect.current = before_current ?? before_abs;
effect.to = to_abs;
effect.max = Max(old_abs, new_abs);
effect.current = before_current ?? old_abs;
effect.to = new_abs;
if (loss)
effect.back_graphics_name = "Red";
else
effect.back_graphics_name = nil;
if (to < 0)
if (new_val < 0)
effect.graphics_name = "Yellow";
else if (to > 0)
else if (new_val > 0)
effect.graphics_name = "Green";
else // off now
{
if (before < 0)
if (old_val < 0)
effect.graphics_name = "Yellow";
else
effect.graphics_name = "Green";
@ -449,8 +568,10 @@ protected func FxVisualPowerChangeRefresh(object target, proplist effect)
effect.bar->Close();
var vis = VIS_Allies | VIS_Owner;
var controller = target->GetController();
if (controller == NO_OWNER)
vis = VIS_All;
var off_x = -(target->GetDefCoreVal("Width", "DefCore") * 3) / 8;
var off_y = target->GetDefCoreVal("Height", "DefCore") / 2 - 10;

View File

@ -531,9 +531,14 @@ protected func FxProcessProductionStart(object target, proplist effect, int temp
// Callback to the producer.
this->~OnProductionStart(effect.Product);
// consume power
if(PowerNeed() > 0)
MakePowerConsumer(PowerNeed());
// Consume power by registering as a consumer for the needed amount.
// But first hold the production until the power system gives it ok.
if (PowerNeed() > 0)
{
this->~OnProductionHold(effect.Product, effect.Duration);
effect.Active = false;
RegisterPowerRequest(PowerNeed());
}
return 1;
}
@ -541,7 +546,7 @@ protected func FxProcessProductionStart(object target, proplist effect, int temp
public func OnNotEnoughPower()
{
var effect = GetEffect("ProcessProduction", this);
if(effect)
if (effect)
{
effect.Active = false;
this->~OnProductionHold(effect.Product, effect.Duration);
@ -554,7 +559,7 @@ public func OnNotEnoughPower()
public func OnEnoughPower()
{
var effect = GetEffect("ProcessProduction", this);
if(effect)
if (effect)
{
effect.Active = true;
this->~OnProductionContinued(effect.Product, effect.Duration);
@ -586,7 +591,7 @@ protected func FxProcessProductionStop(object target, proplist effect, int reaso
if(temp) return;
// no need to consume power anymore
UnmakePowerConsumer();
UnregisterPowerRequest();
if (reason != 0)
return 1;

View File

@ -14,14 +14,14 @@ protected func Initialize()
return;
// If there are no power consumers yet, there is nothing to be done.
if (Library_Power_power_compounds == nil)
if (LIB_POWR_Networks == nil)
return;
// Update all consumers forcefully.
var remember = [];
// Get all power compounds (bases).
for (var base in Library_Power_power_compounds)
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)
@ -90,12 +90,8 @@ public func DestructionEx()
{
var request = obj.last_request;
var amount = obj.last_amount;
// Did not requested power aka "is not running".
if (request == PowerConsumer_LPR_None)
continue;
// Temporarily stop consuming power to make sure the callback order is correct.
obj->OnNotEnoughPower();
obj.last_request = PowerConsumer_LPR_None;
obj.last_amount = 0;
obj->MakePowerConsumer(amount);
}

View File

@ -21,7 +21,7 @@ public func IsProduct(id product_id)
}
private func ProductionTime(id toProduce) { return 100; }
private func PowerNeed() { return 100; }
private func PowerNeed() { return 150; }
public func OnProductionStart(id product)
{

View File

@ -1,11 +1,10 @@
/**
BurningBattery
Burning Battery
A part of a compensator!
@author Zapper
*/
local Name = "$Name$";
local Description = "$Description$";
local Collectible = 0;
local time;
func Initialize()
@ -23,4 +22,11 @@ func DoSmoke()
{
++time;
Smoke(RandomX(-5, 5), RandomX(-5, 5), 2);
}
}
/*-- Properties --*/
local Name = "$Name$";
local Description = "$Description$";
local Collectible = 0;

View File

@ -1,27 +1,20 @@
/**
ChargeShower
Shows stuff.
Shows charge stuff on the compensator.
*/
local Name = "$Name$";
local Description = "$Description$";
local current, to;
local ActMap = {
Turn = {
Prototype = Action,
Name = "Turn",
Procedure = DFA_ATTACH,
Length=6,
Delay=4,
X = 0,
Y = 0,
Wdt = 11,
Hgt = 19,
NextAction = "Turn"
}
};
public func Initialize()
{
to = 0;
current = 0;
Set(to);
return;
}
func AttachTargetLost()
{
@ -31,16 +24,16 @@ func AttachTargetLost()
func To(int i)
{
to = i;
if(!GetEffect("Adjust", this))
if (!GetEffect("Adjust", this))
AddEffect("Adjust", this, 1, 2, this);
}
func FxAdjustTimer()
{
if(current == to)
if (current == to)
return -1;
if(current < to)
if (current < to)
++current;
else --current;
@ -51,7 +44,7 @@ func FxAdjustTimer()
func Set(int i)
{
SetObjDrawTransform((800 * i)/100, 0, -500, 0, 900, -150);
SetObjDrawTransform((800 * i) / 100, 0, -500, 0, 900, -150);
}
func Init(to)
@ -64,12 +57,26 @@ func Init(to)
this.Plane = to.Plane + 1;
}
public func Initialize()
{
to=0;
current=0;
Set(to);
return true;
}
public func SaveScenarioObject() { return false; }
/*-- Properties --*/
local ActMap = {
Turn = {
Prototype = Action,
Name = "Turn",
Procedure = DFA_ATTACH,
Length=6,
Delay=4,
X = 0,
Y = 0,
Wdt = 11,
Hgt = 19,
NextAction = "Turn"
}
};
local Name = "$Name$";
local Description = "$Description$";

View File

@ -1,4 +1,9 @@
/*-- compensator --*/
/**
Compensator
A small structure which stores surplus energy available in a network.
@author Zapper, Maikel
*/
#include Library_Structure
#include Library_Ownable
@ -8,189 +13,253 @@
local DefaultFlagRadius = 90;
static const Compensator_max_seconds = 15;
static const Compensator_power_usage = 50;
// Power storage variables.
local stored_power;
static const POWR_COMP_PowerUsage = 50;
static const POWR_COMP_MaxStorage = 27000; // 15 * 36 * 50
local power_seconds;
// Variables for displaying the charge.
local leftcharge, rightcharge, anim;
local Name = "$Name$";
local Description = "$Description$";
local leftcharge, rightcharge, lastcharge;
local anim;
func Construction(object creator)
protected func Construction(object creator)
{
power_seconds = 0;
lastcharge = 0;
stored_power = 0;
anim = PlayAnimation("Charge", 1, Anim_Const(GetAnimationLength("Charge")), Anim_Const(1000));
SetAction("Default");
return _inherited(creator, ...);
}
func Initialize()
protected func Initialize()
{
leftcharge = CreateObjectAbove(Compensator_ChargeShower, 7 * GetCalcDir(), 10, NO_OWNER);
leftcharge->Init(this);
rightcharge = CreateObjectAbove(Compensator_ChargeShower, -6 * GetCalcDir(), 10, NO_OWNER);
rightcharge->Init(this);
AddTimer("EnergyCheck", 100);
AddTimer("PowerCheck", 10);
return _inherited(...);
}
public func GetProducerPriority() { return 50 * (power_seconds - Compensator_max_seconds) / Compensator_max_seconds; }
protected func Incineration()
{
if (stored_power == 0)
return Extinguish();
for (var i = 0; i < 2; ++i)
{
var x = -7 + 14 * i;
var b = CreateObject(Compensator_BurningBattery, x, 6, NO_OWNER);
// Set controller for correct kill tracing.
b->SetController(GetController());
b->SetSpeed(-30 + 60 * i + RandomX(-10, 10), RandomX(-50, -30));
}
return Explode(30);
}
/*-- Power --*/
public func SetCharge(int to)
{
stored_power = BoundBy(to, 0, POWR_COMP_MaxStorage);
RefreshAnimationPosition();
return;
}
private func PowerCheck()
{
// Not fully constructed or neutral compensators don't do anything.
if (GetCon() < 100 || GetOwner() == NO_OWNER)
return;
// Register as producer while still consuming if power level is above 2/3.
if (GetEffect("ConsumePower", this))
{
if (stored_power >= 2 * POWR_COMP_MaxStorage / 3)
RegisterPowerProduction(POWR_COMP_PowerUsage);
return;
}
// Register as consumer while still producing if power level is below 1/3.
if (GetEffect("ProducePower", this))
{
if (stored_power <= 1 * POWR_COMP_MaxStorage / 3)
RegisterPowerRequest(POWR_COMP_PowerUsage);
return;
}
// Register as consumer if stored power is zero.
if (stored_power == 0)
{
RegisterPowerRequest(POWR_COMP_PowerUsage);
return;
}
// Register as producer if storage is full.
if (stored_power >= POWR_COMP_MaxStorage)
{
RegisterPowerProduction(POWR_COMP_PowerUsage);
return;
}
return;
}
/*-- Power Production --*/
// Produces power on demand, so not steady.
public func IsSteadyPowerProducer() { return false; }
// Producer priority depends on the amount of power that is stored.
public func GetProducerPriority() { return 50 * (2 * stored_power - POWR_COMP_MaxStorage) / POWR_COMP_MaxStorage; }
// Callback from the power library for production of power request.
public func OnPowerProductionStart(int amount)
{
// Start the production of power.
if (!GetEffect("ProducePower", this))
AddEffect("ProducePower", this, 1, 2, this);
// Stop the consumption of power.
if (GetEffect("ConsumePower", this))
{
RemoveEffect("ConsumePower", this);
// Notify the power network.
UnregisterPowerRequest();
}
return true;
}
// Callback from the power library requesting to stop power production.
public func OnPowerProductionStop()
{
// Stop the production of power.
if (GetEffect("ProducePower", this))
RemoveEffect("ProducePower", this);
return true;
}
protected func FxProducePowerStart(object target, proplist effect, int temp)
{
if (temp)
return 1;
// Set Interval to 2.
effect.Interval = 2;
// Sparkle effect when releasing power.
if (!GetEffect("Sparkle", this))
AddEffect("Sparkle", this, 1, 1, this);
return 1;
}
protected func FxProducePowerTimer(object target, proplist effect, time)
{
// Increase the stored power.
stored_power -= effect.Interval * POWR_COMP_PowerUsage;
// Refresh the animation.
RefreshAnimationPosition();
// If stored power is zero then stop producing power.
if (stored_power <= 0)
{
// Notify the power network.
UnregisterPowerProduction();
return -1;
}
return 1;
}
protected func FxProducePowerStop(object target, proplist effect, int reason, bool temp)
{
if (temp)
return 1;
if (GetEffect("Sparkle", this))
RemoveEffect("Sparkle", this);
return 1;
}
/*-- Power Consumption --*/
// It has a low consumer priority so that all other consumers are supplied first.
public func GetConsumerPriority() { return 0; }
func OnNotEnoughPower()
// Callback from the power library saying there is enough power.
public func OnEnoughPower()
{
// not enough power to sustain a battery - turn off
if(GetEffect("ConsumePower", this))
// Start the consumption of power.
if (!GetEffect("ConsumePower", this))
AddEffect("ConsumePower", this, 1, 2, this);
// Stop the production of power.
if (GetEffect("ProducePower", this))
{
RemoveEffect("ProducePower", this);
// Notify the power network.
UnregisterPowerProduction();
}
return _inherited(...);
}
// Callback from the power library saying there is not enough power.
public func OnNotEnoughPower()
{
if (GetEffect("ConsumePower", this))
RemoveEffect("ConsumePower", this);
ScheduleCall(this, "UnmakePowerConsumer", 1, 0);
return _inherited(...);
}
// devour energy
func OnEnoughPower()
protected func FxConsumePowerStart(object target, proplist effect, int temp)
{
if(!GetEffect("ConsumePower", this))
AddEffect("ConsumePower", this, 1, 36, this);
return _inherited(...);
if (temp)
return 1;
// Set Interval to 2.
effect.Interval = 2;
return 1;
}
func SetCharge(int to)
protected func FxConsumePowerTimer(object target, proplist effect, int time)
{
power_seconds = BoundBy(to, 0, Compensator_max_seconds);
// Increase the stored power.
stored_power += effect.Interval * POWR_COMP_PowerUsage;
// Refresh the animation.
RefreshAnimationPosition();
// If fully charged remove this effect.
if (stored_power >= POWR_COMP_MaxStorage)
{
// Notify the power network.
UnregisterPowerRequest();
return -1;
}
return 1;
}
func RefreshAnimationPosition()
protected func FxConsumePowerStop(object target, proplist effect, int reason, bool temp)
{
var charge = (power_seconds * 100) / Compensator_max_seconds;
if (temp)
return 1;
return 1;
}
/*-- Animations & Effects --*/
private func RefreshAnimationPosition()
{
var charge = (stored_power * 100) / POWR_COMP_MaxStorage;
/*var current = GetAnimationPosition(anim);
var len = GetAnimationLength("Charge");
SetAnimationPosition(anim, Anim_Linear(current, current, len - (charge * len) / 100, 35, ANIM_Hold));*/
leftcharge->To(Min(charge, 50)*2);
rightcharge->To(Max(0, charge-50)*2);
}
func FxConsumePowerTimer(target, effect, time)
{
++power_seconds;
RefreshAnimationPosition();
// fully charged?
if(power_seconds >= Compensator_max_seconds)
{
UnmakePowerConsumer();
return -1;
}
return 1;
}
func EnergyCheck()
{
if(GetCon() < 100) return;
// consuming - everything is alright
if(GetEffect("ConsumePower", this))
return true;
// producing - nothing to change either
if(GetEffect("ProducePower", this))
return true;
// neutral compensators don't do anything
if(GetOwner() == NO_OWNER) return false;
// are we needed?
if(power_seconds > 0)
{
var s = GetPendingPowerAmount();
if(s > 0)
{
// turn on, start the machines!
AddEffect("ProducePower", this, 1, 36, this);
return true;
}
}
// fully charged
if(power_seconds >= Compensator_max_seconds)
return false;
// can we leech power?
var p = GetCurrentPowerBalance();
// we have some play here?
if(p >= Compensator_power_usage)
{
MakePowerConsumer(Compensator_power_usage);
return true;
}
return false;
}
func FxProducePowerStart(target, effect, temp)
{
if(temp) return;
MakePowerProducer(Compensator_power_usage);
// todo: effects
AddEffect("Sparkle", this, 1, 1, this);
}
func FxSparkleTimer(target, effect, time)
protected func FxSparkleTimer(object target, proplist effect, int time)
{
effect.Interval *= 2;
if(effect.Interval > 35*3) return -1;
if (effect.Interval > 35 * 3)
return -1;
CreateParticle("StarSpark", PV_Random(-3, 3), PV_Random(-14, -10), PV_Random(-5, 5), PV_Random(-8, 0), 10, Particles_Magic(), 4);
}
func FxProducePowerTimer(target, effect, time)
{
--power_seconds;
RefreshAnimationPosition();
if(power_seconds <= 0)
{
return -1;
}
// stop when not needed
if((GetCurrentPowerBalance() >= Compensator_power_usage) && GetPendingPowerAmount() == 0)
return -1;
return 1;
}
func FxProducePowerStop(target, effect, reason, temp)
{
if(temp) return;
MakePowerProducer(0);
if(GetEffect("Sparkle", this))
RemoveEffect("Sparkle", this);
}
func Incineration()
{
if(power_seconds == 0)
return Extinguish();
for(var i = 0; i < 2; ++i)
{
var x = -7 + 14 * i;
var b = CreateObjectAbove(Compensator_BurningBattery, x, 6, NO_OWNER);
b->SetController(GetController()); // killtracing
b->SetSpeed(-30 + 60 * i + RandomX(-10, 10), RandomX(-50, -30));
}
Explode(30);
}
/*-- Properties --*/
local ActMap = {
Default = {
@ -205,6 +274,9 @@ local ActMap = {
NextAction = "Default",
},
};
local Name = "$Name$";
local Description = "$Description$";
local BlastIncinerate = 1;
local HitPoints = 25;
local ContactIncinerate = 1;

View File

@ -70,6 +70,7 @@ func Destruction()
wood->SetXDir(RandomX(-10, 10));
wood->SetYDir(RandomX(-2, 0));
}
return _inherited(...);
}
func LostElevator()
@ -126,8 +127,8 @@ func ExecuteSync()
SetPartnerVertices(partner->GetX() - GetX(), partner->GetY() - GetY());
// reset power usage
UnmakePowerConsumer();
partner->UnmakePowerConsumer();
//UnmakePowerConsumer();
//partner->UnmakePowerConsumer();
// can now attach partner on one of the new vertices
partner->SetAction("Attach", this);
@ -292,45 +293,38 @@ func FxFetchVehiclesTimer(target, effect, time)
return 1;
}
/* Energy */
/*-- Power Consumption --*/
func GetNeededPower()
private func GetNeededPower()
{
var p = Elevator_needed_power;
if(partner_was_synced) p = 2 * p;
return p;
if (partner_was_synced)
return 2 * Elevator_needed_power;
return Elevator_needed_power;
}
// for the position
func GetActualPowerConsumer()
// The elevator is the actual power consumer.
public func GetActualPowerConsumer()
{
return elevator;
}
// the lift may not need power when not used
func QueryWaivePowerRequest()
{
// no clonk on elevator? must be automatic
if(CheckIdle()) return 20;
return 0;
}
// Elevator has a high priority.
public func GetConsumerPriority() { return 100; }
func OnNotEnoughPower()
public func OnNotEnoughPower()
{
_inherited(...); // on purpose before the rest
if(GetYDir())
if (GetYDir())
StoreMovementData();
else; // already has data stored
if(GetAction() != "DriveIdle")
if (GetAction() != "DriveIdle")
Halt(false, true);
}
func OnEnoughPower()
public func OnEnoughPower()
{
_inherited(...); // on purpose before the rest
RestoreMovementData();
@ -393,7 +387,7 @@ func SetMoveDirection(int dir, bool user_requested, bool drill)
speed = GetDrillSpeed();
}
if(CurrentlyHasPower())
//if(CurrentlyHasPower())
{
SetYDir(dir * speed);
SetAction(action);
@ -402,10 +396,10 @@ func SetMoveDirection(int dir, bool user_requested, bool drill)
elevator->StartEngine();
}
else
//else
{
StoreMovementData(dir * speed, action);
MakePowerConsumer(GetNeededPower());
// MakePowerConsumer(GetNeededPower());
}
}
@ -428,7 +422,7 @@ func Halt(bool user_requested, bool power_out)
if(user_requested)
{
UnmakePowerConsumer();
//UnmakePowerConsumer();
}
else
{
@ -440,7 +434,7 @@ func Halt(bool user_requested, bool power_out)
func FxStopPowerConsumptionTimer(object target, effect, int time)
{
UnmakePowerConsumer();
//UnmakePowerConsumer();
return -1;
}

View File

@ -1,66 +1,18 @@
/*--
/**
Pump
Author: Maikel, ST-DDT, Sven2, Newton
Pumps liquids using drain and source pipes. Features include:
+ switch on and off
+ consume/produce a variable amount of power depending on the height of
source and drain
--*/
@author Maikel, ST-DDT, Sven2, Newton
*/
#include Library_Structure
#include Library_Ownable
#include Library_PowerConsumer
#include Library_PowerProducer
local Name = "$Name$";
local Description = "$Description$";
local BlastIncinerate = 50;
local HitPoints = 70;
/*
States
"Wait": turned off or source pipe not connected
"WaitForPower": turned on but no power (does consume power)
"WaitForLiquid": turned on but no liquid (does not consume power)
"Pump": currently working and consuming/producing power
*/
local ActMap = {
Pump = {
Prototype = Action,
Name = "Pump",
Length = 30,
Delay = 3,
Sound = "Pumpjack",
NextAction = "Pump",
StartCall = "CheckState",
PhaseCall = "Pumping"
},
Wait = {
Prototype = Action,
Name = "Wait",
Delay = 90,
NextAction = "Wait",
EndCall = "CheckState"
},
WaitForPower = {
Prototype = Action,
Name = "WaitForPower",
Delay = 30,
NextAction = "WaitForPower",
EndCall = "CheckState"
},
WaitForLiquid = {
Prototype = Action,
Name = "WaitForLiquid",
Delay = 30,
NextAction = "WaitForLiquid",
EndCall = "CheckState"
}
};
local animation; // animation handle
local switched_on; // controlled by Interaction. Indicates whether the user wants to pump or not
@ -77,18 +29,10 @@ local drain_pipe;
/** This object is a liquid pump, thus pipes can be connected. */
public func IsLiquidPump() { return true; }
func Definition(def)
{
// for title image
SetProperty("PictureTransformation",Trans_Rotate(50,0,1,0),def);
// for building preview
SetProperty("MeshTransformation",Trans_Rotate(50,0,1,0),def);
}
func Construction()
{
// Rotate at a 45 degree angle towards viewer and add a litte bit of Random
this.MeshTransformation = Trans_Rotate(50 + RandomX(-10,10),0,1,0);
this.MeshTransformation = Trans_Rotate(50 + RandomX(-10, 10), 0, 1, 0);
}
func Initialize()
@ -164,24 +108,20 @@ public func SetSource(object pipe)
/*-- Power stuff --*/
func QueryWaivePowerRequest()
{
// has less priority than other objects, but not too low
return 10;
}
public func GetConsumerPriority() { return 25; }
public func GetProducerPriority() { return 100; }
func OnNotEnoughPower()
public func IsSteadyPowerProducer() { return true; }
public func OnNotEnoughPower()
{
_inherited(...);
powered = false;
CheckState();
}
func OnEnoughPower()
public func OnEnoughPower()
{
_inherited(...);
powered = true;
@ -224,13 +164,13 @@ protected func Pumping()
{
// get new materials
var aMat = GetSourceObject()->ExtractLiquidAmount(0,0, GetPumpSpeed()/10);
var mat = GetSourceObject()->ExtractLiquidAmount(0, 0, GetPumpSpeed()/10);
// no material to pump?
if (aMat)
if (mat)
{
stored_material_index = aMat[0];
stored_material_amount = aMat[1];
stored_material_index = mat[0];
stored_material_amount = mat[1];
}
else
{
@ -259,10 +199,9 @@ protected func Pumping()
stored_material_index = nil;
}
if(!pump_ok)
{
if (!pump_ok)
SetState("WaitForLiquid");
}
return;
}
/** Re check state and change the state if needed */
@ -326,7 +265,7 @@ private func GetPumpHeight()
private func UpdatePowerUsage()
{
var new_power;
if(IsUsingPower())
if (IsUsingPower())
new_power = PumpHeight2Power(GetPumpHeight());
else
new_power = 0;
@ -341,22 +280,23 @@ private func UpdatePowerUsage()
if (power_used < 0)
{
powered = false; // needed since the flag was set manually
UnmakePowerProducer();
UnregisterPowerProduction();
}
MakePowerConsumer(new_power);
RegisterPowerRequest(new_power);
}
else if (new_power < 0)
{
if (power_used > 0)
UnmakePowerConsumer();
MakePowerProducer(-new_power);
UnregisterPowerRequest();
RegisterPowerProduction(-new_power);
powered = true; // when producing, we always have power
}
else // new_power == 0
{
if (power_used < 0) UnmakePowerProducer();
else if (power_used > 0) UnmakePowerConsumer();
if (power_used < 0)
UnregisterPowerProduction();
else if (power_used > 0)
UnregisterPowerRequest();
powered = true;
}
@ -377,7 +317,7 @@ private func PumpHeight2Power(int pump_height)
// max power consumed/produced
var max_power = 150;
return BoundBy((pump_height + power_offset)/15*5, -max_power,max_power+power_offset);
return BoundBy((pump_height + power_offset) / 15 * 5, -max_power, max_power + power_offset);
}
/** Returns whether there is liquid at the source pipe to pump */
@ -421,11 +361,73 @@ func SetState(string act)
// deactivate power usage when not pumping
if (powered && (act == "Wait" || act == "WaitForLiquid"))
{
if (power_used < 0) UnmakePowerProducer();
else if(power_used > 0) UnmakePowerConsumer();
if (power_used < 0)
UnregisterPowerProduction();
else if (power_used > 0)
UnregisterPowerRequest();
power_used = 0;
powered = false;
}
// finally, set the action
SetAction(act);
}
return;
}
/*-- Properties --*/
protected func Definition(def)
{
// for title image
SetProperty("PictureTransformation", Trans_Rotate(50, 0, 1, 0), def);
// for building preview
SetProperty("MeshTransformation", Trans_Rotate(50, 0, 1, 0), def);
}
/*
States
"Wait": turned off or source pipe not connected
"WaitForPower": turned on but no power (does consume power)
"WaitForLiquid": turned on but no liquid (does not consume power)
"Pump": currently working and consuming/producing power
*/
local ActMap = {
Pump = {
Prototype = Action,
Name = "Pump",
Length = 30,
Delay = 3,
Sound = "Pumpjack",
NextAction = "Pump",
StartCall = "CheckState",
PhaseCall = "Pumping"
},
Wait = {
Prototype = Action,
Name = "Wait",
Delay = 90,
NextAction = "Wait",
EndCall = "CheckState"
},
WaitForPower = {
Prototype = Action,
Name = "WaitForPower",
Delay = 30,
NextAction = "WaitForPower",
EndCall = "CheckState"
},
WaitForLiquid = {
Prototype = Action,
Name = "WaitForLiquid",
Delay = 30,
NextAction = "WaitForLiquid",
EndCall = "CheckState"
}
};
local Name = "$Name$";
local Description = "$Description$";
local BlastIncinerate = 50;
local HitPoints = 70;

View File

@ -1,4 +1,11 @@
/*-- Steam engine --*/
/**
Steam Engine
Burns fuels like coal, wood and oil to produce power. The steam engine
produces 300 units of power independent of the fuel. However, the fuel
determines the amount of fuel and thereby the burn time.
@author Maikel
*/
#include Library_Structure
#include Library_Ownable
@ -9,134 +16,158 @@ local DefaultFlagRadius = 200;
static const SteamEngine_produced_power = 300;
local iFuelAmount;
local power_seconds;
// Variable to store the fuel amount currently held in the engine.
local fuel_amount;
func Construction(object creator)
protected func Construction()
{
iFuelAmount = 0;
power_seconds = 0;
fuel_amount = 0;
return _inherited(...);
}
SetAction("Default");
protected func Initialize()
{
SetAction("Idle");
AddTimer("ContentsCheck", 30);
return _inherited(creator, ...);
return _inherited(...);
}
public func IsContainer() { return true; }
public func GetProducerPriority() { return 0; }
func RejectCollect(id item, object obj)
protected func RejectCollect(id item, object obj)
{
if (obj->~IsFuel())
return false;
return true;
}
func Collection(object obj, bool put)
protected func Collection(object obj, bool put)
{
Sound("Clonk");
}
func ContentsCheck()
public func ContentsCheck()
{
//Ejects non fuel items immediately
// Ejects non fuel items immediately
var fuel;
if(fuel = FindObject(Find_Container(this), Find_Not(Find_Func("IsFuel"))))
{
fuel->Exit(-53,21, -20, -1, -1, -30);
Sound("Chuff"); //I think a 'chuff' or 'metal clonk' sound could be good here -Ringwaul
fuel->Exit(-53, 21, -20, -1, -1, -30);
Sound("Chuff");
}
// Still active?
if(GetAction() == "Work") return true;
// or still warm water in the tank?!
if(GetEffect("CreatesPower", this))
return true;
// not needed?
if(GetPendingPowerAmount() == 0)
return false;
// Still has some fuel?
if(iFuelAmount) return SetAction("Work");
// Search for new fuel
if(fuel = FindObject(Find_Container(this), Find_Func("IsFuel")))
// If active don't do anything.
if (GetAction() == "Work")
return;
// If there is fuel available let the network know.
if (fuel_amount > 0 || FindObject(Find_Container(this), Find_Func("IsFuel")))
RegisterPowerProduction(SteamEngine_produced_power);
return;
}
/*-- Power Production --*/
// Produces power on demand, so not steady.
public func IsSteadyPowerProducer() { return false; }
// Low priority so that other sources of power are drained before burning fuel.
public func GetProducerPriority() { return 0; }
// Callback from the power library for production of power request.
public func OnPowerProductionStart(int amount)
{
// Check if there is fuel.
if (fuel_amount <= 0)
{
iFuelAmount += fuel->~GetFuelAmount() / 2;
// Search for new fuel among the contents.
var fuel = FindObject(Find_Container(this), Find_Func("IsFuel"));
if (!fuel)
return false;
// Extract the fuel amount from the new piece of fuel.
fuel_amount += fuel->~GetFuelAmount() * 18;
fuel->RemoveObject();
}
// There is enough fuel so start producing power and notify network of this.
if (GetAction() == "Idle")
SetAction("Work");
return true;
}
return false;
return true;
}
func ConsumeFuel()
{
if(iFuelAmount > 0)
{
// every fuel unit gives power for one second
--iFuelAmount;
power_seconds += 1;
if(!GetEffect("CreatesPower", this))
AddEffect("CreatesPower", this, 1, 36, this);
}
// All used up?
if(!iFuelAmount || ((GetPendingPowerAmount() == 0) && (GetCurrentPowerBalance() >= SteamEngine_produced_power)))
{
SetAction("Default");
ContentsCheck();
}
}
func FxCreatesPowerStart(target, effect, temp)
// Callback from the power library requesting to stop power production.
public func OnPowerProductionStop()
{
// Set action to idle when it was working.
if (GetAction() == "Work")
SetAction("Idle");
return true;
}
// Start call from working action.
protected func WorkStart()
{
if(temp) return;
// fixed amount
MakePowerProducer(SteamEngine_produced_power);
AddEffect("Smoking", this, 1, 5, this);
Sound("SteamEngine", false, nil, nil, 1);
return;
}
func FxCreatesPowerTimer(target, effect)
// Phase call from working action, every two frames.
protected func Working()
{
if(power_seconds == 0) return -1;
--power_seconds;
// Reduce the fuel amount by 1 per frame.
fuel_amount -= 2;
// Check if there is still enough fuel available.
if (fuel_amount <= 0)
{
// Search for new fuel among the contents.
var fuel = FindObject(Find_Container(this), Find_Func("IsFuel"));
if (!fuel)
{
// Set action to idle and unregister this producer as available from the network.
SetAction("Idle");
UnregisterPowerProduction();
return;
}
// Extract the fuel amount from the new piece of fuel.
fuel_amount += fuel->~GetFuelAmount() * 18;
fuel->RemoveObject();
}
// Smoke from the exhaust shaft.
Smoke(-20 * GetCalcDir() + RandomX(-2, 2), -26, 10);
Smoke(-20 * GetCalcDir() + RandomX(-2, 2), -24, 8);
Smoke(-20 * GetCalcDir() + RandomX(-2, 2), -24, 10);
return;
}
func FxCreatesPowerStop(target, effect, reason, temp)
// Stop call from working action.
protected func WorkStop()
{
if(temp) return;
// disable producer
MakePowerProducer(0);
if(GetEffect("Smoking", this))
RemoveEffect("Smoking", this);
// Don't kill the sound in this call, since that would interupt the sound effect.
return;
}
// Abort call from working action.
protected func WorkAbort()
{
// Sound can be safely stopped here since this action will always end with an abort call.
Sound("SteamEngine", false, nil, nil, -1);
return;
}
func FxSmokingTimer()
{
Smoke(-20 * GetCalcDir(), -26, 10);
Smoke(-20 * GetCalcDir(), -24, 8);
return 1;
}
/*-- Properties --*/
local ActMap = {
Default = {
Idle = {
Prototype = Action,
Name = "Default",
Name = "Idle",
Procedure = DFA_NONE,
Directions = 2,
FlipDir = 1,
Length = 1,
Delay = 0,
FacetBase=1,
NextAction = "Default",
FacetBase = 1,
NextAction = "Idle",
},
Work = {
Prototype = Action,
@ -149,13 +180,19 @@ local ActMap = {
FacetBase = 1,
NextAction = "Work",
Animation = "Work",
EndCall = "ConsumeFuel",
PhaseCall = "Working",
StartCall = "WorkStart",
EndCall = "WorkStop",
AbortCall = "WorkAbort",
},
};
func Definition(def) {
SetProperty("MeshTransformation", Trans_Mul(Trans_Rotate(25,0,1,0), Trans_Scale(625)), def);
protected func Definition(def)
{
SetProperty("MeshTransformation", Trans_Mul(Trans_Rotate(25, 0, 1, 0), Trans_Scale(625)), def);
SetProperty("PictureTransformation", Trans_Mul(Trans_Translate(-4000, -18000, 60000), Trans_Rotate(25, 0, 1, 0), Trans_Scale(625)), def);
}
local ContainBlast = true;
local BlastIncinerate = 130;
local HitPoints = 100;

View File

@ -1,4 +1,9 @@
/*-- Wind generator --*/
/**
Wind Generator
Converts wind into a steady power supply.
@author Maikel
*/
#include Library_Structure
#include Library_Ownable
@ -7,14 +12,14 @@
local DefaultFlagRadius = 90;
/* Initialisierung */
/*-- Initialization --*/
local wind_anim;
local last_wind;
local last_power;
local wheel;
func TurnAnimation(){return "Turn";}
func MinRevolutionTime(){return 4500;} // in frames
public func GetProducerPriority() { return 100; }
func TurnAnimation() { return "Turn"; }
func MinRevolutionTime() { return 4500; } // in frames
protected func Construction()
{
@ -25,70 +30,98 @@ protected func Construction()
protected func Initialize()
{
// create wheel
(this.wheel = CreateObjectAbove(WindGenerator_Wheel, 0, 0, NO_OWNER))->Set(this);
wheel = CreateObject(WindGenerator_Wheel, 0, 0, NO_OWNER);
wheel->SetParent(this);
// Start animation
wind_anim = PlayAnimation(TurnAnimation(), 5, this.wheel->Anim_R(0, GetAnimationLength(TurnAnimation())), Anim_Const(1000));
wind_anim = PlayAnimation(TurnAnimation(), 5, wheel->Anim_R(0, GetAnimationLength(TurnAnimation())), Anim_Const(1000));
// Set initial position
AddTimer("Wind2Turn");
AddTimer("Wind2Turn", 4);
Wind2Turn();
return _inherited(...);
}
// used by the windmill too
// returns the wind weighted over several points - not the absolute value!
func GetWeightedWind()
/*-- Power Production --*/
// Always produces power, irrespective of the demand.
public func IsSteadyPowerProducer() { return true; }
// High priority so that this drained first.
public func GetProducerPriority() { return 100; }
// Callback from the power library for production of power request.
public func OnPowerProductionStart(int amount)
{
// This is a steady producer, so it is already running.
return true;
}
// Callback from the power library requesting to stop power production.
public func OnPowerProductionStop()
{
// This is a steady producer, so don't stop anything.
return true;
}
// Returns the wind weighted over several points.
private func GetWeightedWind()
{
// hardcoded for performance reasons
return (
(10 * (GetWind(-150, -30))) +
(25 * (GetWind(-75, -30))) +
(30 * (GetWind(0, -30))) +
(25 * (GetWind(+75, -30))) +
(10 * (GetWind(+150, -30)))
(10 * GetWind(-150, -30)) +
(25 * GetWind( -75, -30)) +
(30 * GetWind( 0, -30)) +
(25 * GetWind( +75, -30)) +
(10 * GetWind(+150, -30))
) / 100;
}
// used by the windmill too
func Wind2Turn()
// Turns wind into power and adjusts the power production accordingly.
public func Wind2Turn()
{
if(GetCon() < 100) return;
// Only produce power if fully constructed.
if (GetCon() < 100)
return;
var current_wind = this->GetWeightedWind();
var current_wind = GetWeightedWind();
var power = 0;
if(this.wheel->Stuck() || this.wheel->HasStopped())
if (wheel->Stuck() || wheel->HasStopped())
{
power = 0;
}
else
{
power = Abs(this.wheel->GetRDir(this->MinRevolutionTime()/90));
if(power < 5) power = 0;
else power = Max(((power + 5) / 25), 1) * 50;
power = Abs(wheel->GetRDir(this->MinRevolutionTime() / 90));
if (power < 5)
power = 0;
else
power = Max((power + 5) / 25, 1) * 50;
}
if(last_wind != power)
// Register the new power production if it changed.
if (last_power != power)
{
last_wind = power;
MakePowerProducer(last_wind);
last_power = power;
RegisterPowerProduction(last_power);
}
// adjust wheel speed
this.wheel->SetRDir(current_wind*90, this->MinRevolutionTime());
// make sounds
// Adjust the wheel speed.
wheel->SetRDir(current_wind * 90, this->MinRevolutionTime());
// Make some sounds.
if (Abs(current_wind) >= 10 && Random(15 - Abs(current_wind / 10)) < 5)
{
if (!Random(2))
Sound("WoodCreak?",false,nil,nil,nil, 75);
else
Sound("HingeCreak?",false,nil,nil,nil, 75);
}
Sound(["WoodCreak?","HingeCreak?"][Random(2)], false, nil, nil, nil, 75);
return;
}
func Definition(def) {
SetProperty("PictureTransformation", Trans_Mul(Trans_Translate(2000,0,7000),Trans_Rotate(-20,1,0,0),Trans_Rotate(30,0,1,0)), def);
/*-- Properties --*/
protected func Definition(def)
{
SetProperty("PictureTransformation", Trans_Mul(Trans_Translate(2000, 0, 7000), Trans_Rotate(-20, 1, 0, 0), Trans_Rotate(30, 0, 1, 0)), def);
}
local Name = "$Name$";
local Description = "$Description$";
local BlastIncinerate = 60;

View File

@ -1,9 +1,39 @@
/**
Wheel
Invisible helper object. Takes care of collisions.
*/
protected func Initialize()
{
return;
}
public func AttachTargetLost()
{
// Remove this helper object when the generator is lost.
return RemoveObject();
}
public func HasStopped()
{
return !GetRDir(1000);
}
public func SetParent(object parent, int con)
{
con = con ?? 100;
SetCon(con);
SetAction("Turn", parent);
return;
}
// Don't duplicate on scenario load.
public func SaveScenarioObject() { return false; }
/*-- Proplist --*/
local ActMap = {
Turn = {
Prototype = Action,
@ -14,29 +44,4 @@ local ActMap = {
FacetBase=1,
NextAction = "Hold",
}
};
func Initialize()
{
}
func AttachTargetLost()
{
return RemoveObject();
}
func HasStopped()
{
return !GetRDir(1000);
}
func Set(to, con)
{
con = con ?? 100;
SetCon(con);
SetAction("Turn", to);
}
// Don't duplicate on scenario load
func SaveScenarioObject() { return false; }
};

View File

@ -37,7 +37,7 @@ protected func Construction(object creator)
protected func Initialize()
{
// create wheel
(this.wheel = CreateObjectAbove(WindGenerator_Wheel, 0, 0, NO_OWNER))->Set(this, 150);
(this.wheel = CreateObject(WindGenerator_Wheel, 0, 0, NO_OWNER))->SetParent(this, 150);
// Set initial position
wind_anim = PlayAnimation(TurnAnimation(), 5, this.wheel->Anim_R(GetAnimationLength(TurnAnimation()), 0), Anim_Const(1000));

View File

@ -0,0 +1,9 @@
func Initialize()
{
var a = [3];
RemoveArrayIndex(a, 0);
SortArrayByProperty(a, "priority");
Log("%v-%d", a, GetLength(a));
for (var i = -1; i >= 0; i--)
Log("%d", i);
}

View File

@ -2,4 +2,7 @@
Title=Power System
[Landscape]
NoScan=1
NoScan=1
[Weather]
Wind=0,0,-100,100

View File

@ -6,6 +6,10 @@
true. Then Test*_OnFinished() is called, to be able to reset
the scenario for the next test.
With LaunchTest(int nr) a specific test can be launched when
called during runtime. A test can be skipped by calling the
function SkipTest().
@author Maikel
*/
@ -18,8 +22,10 @@ protected func Initialize()
protected func InitializePlayer(int plr)
{
// Set zoom and move player to the middle of the scenario.
// Set zoom to full map size.
SetPlayerZoomByViewRange(plr, LandscapeWidth(), nil, PLRZOOM_Direct);
// Move player to the start of the scenario.
GetCrew(plr)->SetPosition(120, 150);
// Add test control effect.
@ -27,7 +33,7 @@ protected func InitializePlayer(int plr)
effect.testnr = 1;
effect.launched = false;
effect.plr = plr;
return true;
return;
}
@ -121,47 +127,74 @@ global func Test1_OnStart(int plr)
{
// Power source: one wind generator.
SetWindFixed(50);
CreateObjectAbove(WindGenerator, 88, 160, plr);
CreateObjectAbove(WindGenerator, 72, 160, plr);
// Power consumer: one pump.
var pump = CreateObjectAbove(Pump, 124, 160, plr);
var source = CreateObjectAbove(Pipe, 176, 292, plr);
var source_pipe = CreateObjectAbove(PipeLine, 144, 160, plr);
source_pipe->SetActionTargets(source, pump);
pump->SetSource(source_pipe);
var drain = CreateObjectAbove(Pipe, 248, 100, plr);
var drain_pipe = CreateObjectAbove(PipeLine, 224, 48, plr);
drain_pipe->AddVertex(208, 48);
drain_pipe->SetActionTargets(drain, pump);
pump->SetDrain(drain_pipe);
// Power consumer: one workshop.
var workshop = CreateObjectAbove(ToolsWorkshop, 110, 160, plr);
workshop->CreateContents(Wood, 2);
workshop->CreateContents(Metal, 2);
workshop->AddToQueue(Shovel, 2);
// Log what the test is about.
Log("A steady power source (wind generator) supplying a steady power consumer (pump).");
Log("A steady power source (wind generator) supplying a steady power consumer (workshop).");
return true;
}
global func Test1_Completed()
{
if (GetMaterial(248, 84) == Material("Water"))
if (ObjectCount(Find_ID(Shovel)) >= 2)
return true;
return false;
}
global func Test1_OnFinished()
{
// Restore water levels.
DrawMaterialQuad("Water", 144, 168, 208 + 1, 168, 208 + 1, 304, 144, 304, true);
for (var x = 216; x <= 280; x++)
for (var y = 24; y <= 120; y++)
if (GetMaterial(x, y) == Material("Water"))
ClearFreeRect(x, y, 1, 1);
// Remove wind generator, pump and the pipes.
RemoveAll(Find_Or(Find_ID(WindGenerator), Find_ID(Pump), Find_ID(Pipe)));
// Remove wind generator, compensator and workshop.
RemoveAll(Find_Or(Find_ID(WindGenerator), Find_ID(Compensator), Find_ID(ToolsWorkshop)));
return;
}
// Test for one on-demand source and a few consumers.
global func Test2_OnStart(int plr)
{
// Power source: one steam engine.
var engine = CreateObjectAbove(SteamEngine, 100, 160, plr);
var coal = engine->CreateContents(Coal, 1);
coal->SetCon(10);
// Power consumer: sawmill.
CreateObject(Sawmill, 40, 160, plr);
for (var i = 0; i < 1; i++)
CreateObject(Tree_Coconut, 40, 160)->ChopDown();
// Power consumer: armory.
var armory = CreateObject(Armory, 280, 160, plr);
armory->CreateContents(Firestone, 8);
armory->CreateContents(Metal, 8);
armory->AddToQueue(IronBomb, 8);
// Log what the test is about.
Log("An on-demand power source (steam engine) supplying a few on-demand power consumers (sawmill, armory).");
return true;
}
global func Test2_Completed()
{
// One wood is being burned as fuel by the steam engine: 3 * 4 - 1 = 11.
if (ObjectCount(Find_ID(Wood)) >= 3 && ObjectCount(Find_ID(IronBomb)) >= 8)
return true;
return false;
}
global func Test2_OnFinished()
{
// Remove steam engine, sawmill, armory.
RemoveAll(Find_Or(Find_ID(SteamEngine), Find_ID(Sawmill), Find_ID(Armory)));
return;
}
// Test for one on-demand source and one steady consumer.
global func Test2_OnStart(int plr)
global func Test3_OnStart(int plr)
{
// Power source: one steam engine.
var engine = CreateObjectAbove(SteamEngine, 40, 160, plr);
@ -187,14 +220,14 @@ global func Test2_OnStart(int plr)
return true;
}
global func Test2_Completed()
global func Test3_Completed()
{
if (GetMaterial(248, 48) == Material("Water"))
return true;
return false;
}
global func Test2_OnFinished()
global func Test3_OnFinished()
{
// Restore water levels.
DrawMaterialQuad("Water", 144, 168, 208 + 1, 168, 208 + 1, 304, 144, 304, true);
@ -208,7 +241,7 @@ global func Test2_OnFinished()
}
// Test one steady source with one steady consumer and a prioritized on-demand consumer.
global func Test3_OnStart(int plr)
global func Test4_OnStart(int plr)
{
// Power source: one wind generator.
SetWindFixed(25);
@ -238,14 +271,14 @@ global func Test3_OnStart(int plr)
return true;
}
global func Test3_Completed()
global func Test4_Completed()
{
if (FindObject(Find_ID(ElevatorCase), Find_InRect(372, 230, 40, 40)))
return true;
return false;
}
global func Test3_OnFinished()
global func Test4_OnFinished()
{
// Restore water levels.
DrawMaterialQuad("Water", 144, 168, 208 + 1, 168, 208 + 1, 304, 144, 304, true);
@ -261,7 +294,7 @@ global func Test3_OnFinished()
}
// Basic test for power storage: one steady supplier, one storage and one consumer which needs energy from both sources.
global func Test4_OnStart(int plr)
global func Test5_OnStart(int plr)
{
// Power source: one wind generator.
SetWindFixed(25);
@ -281,14 +314,14 @@ global func Test4_OnStart(int plr)
return true;
}
global func Test4_Completed()
global func Test5_Completed()
{
if (ObjectCount(Find_ID(Shovel)) >= 2)
return true;
return false;
}
global func Test4_OnFinished()
global func Test5_OnFinished()
{
// Remove wind generator, compensator and workshop.
RemoveAll(Find_Or(Find_ID(WindGenerator), Find_ID(Compensator), Find_ID(ToolsWorkshop)));
@ -296,7 +329,7 @@ global func Test4_OnFinished()
}
// Test one overproducing on-demand producer with power storage and one steady consumer.
global func Test5_OnStart(int plr)
global func Test6_OnStart(int plr)
{
// Power source: one steam engine.
var engine = CreateObjectAbove(SteamEngine, 40, 160, plr);
@ -320,38 +353,79 @@ global func Test5_OnStart(int plr)
return true;
}
global func Test5_Completed()
global func Test6_Completed()
{
if (ObjectCount(Find_ID(Shovel)) >= 16)
return true;
return false;
}
global func Test5_OnFinished()
global func Test6_OnFinished()
{
// Remove steam engine, compensators and workshop.
RemoveAll(Find_Or(Find_ID(SteamEngine), Find_ID(Compensator), Find_ID(ToolsWorkshop)));
return;
}
/*-- Helper Functions --*/
global func RemoveTest()
// Test an on-demand producer as back-up for a steady producer with for steady consumers.
global func Test7_OnStart(int plr)
{
// Remove all objects besides the clonk.
RemoveAll(Find_Not(Find_OCF(OCF_CrewMember)));
// Power source: one steam engine.
var engine = CreateObject(SteamEngine, 40, 160, plr);
engine->CreateContents(Coal, 1);
// Remove all effects besides the test control effect.
/*var effect;
var index = 0;
while (effect = GetEffect("*", nil, index))
// Power source: wind generators which are turned on and off again due to wind.
CreateObject(WindGenerator, 356, 104, plr);
SetWindFixed(80);
Schedule(nil, "SetWindFixed(0)", 5 * 36);
Schedule(nil, "SetWindFixed(80)", 8 * 36);
// Power connection: flagpole.
CreateObject(Flagpole, 328, 132, plr);
// Power consumer: two pumps.
for (var i = 0; i < 2; i++)
{
Log("%v", effect);
index++;
}*/
var pump = CreateObject(Pump, 92 + i * 20, 160, plr);
var source = CreateObject(Pipe, 176, 292, plr);
var source_pipe = CreateObject(PipeLine, 144, 160, plr);
source_pipe->SetActionTargets(source, pump);
pump->SetSource(source_pipe);
var drain = CreateObject(Pipe, 248, 100, plr);
var drain_pipe = CreateObject(PipeLine, 224, 48, plr);
drain_pipe->AddVertex(208, 48);
drain_pipe->SetActionTargets(drain, pump);
pump->SetDrain(drain_pipe);
}
// Log what the test is about.
Log("An on-demand producer (steam engine) as back-up for a steady producer (wind generator) with for steady consumers (pumps).");
return true;
}
global func Test7_Completed()
{
if (GetMaterial(248, 48) == Material("Water"))
return true;
return false;
}
global func Test7_OnFinished()
{
// Restore water levels.
DrawMaterialQuad("Water", 144, 168, 208 + 1, 168, 208 + 1, 304, 144, 304, true);
for (var x = 216; x <= 280; x++)
for (var y = 24; y <= 120; y++)
if (GetMaterial(x, y) == Material("Water"))
ClearFreeRect(x, y, 1, 1);
// Remove steam engine, wind generator, flagpole, pump and the pipes.
RemoveAll(Find_Or(Find_ID(SteamEngine), Find_ID(WindGenerator), Find_ID(Flagpole), Find_ID(Pump), Find_ID(Pipe)));
return;
}
/*-- Helper Functions --*/
global func SetWindFixed(int strength)
{
strength = BoundBy(strength, -100, 100);