openclonk/planet/System.ocg/Components.c

131 lines
4.2 KiB
C

/**
Components.c
Handles the components of an object, which are a property of the object / definition.
Components are a property of the form
local Components = {Def1 = cnt1, Def2 = cnt2, ...};
which can be modified directly or via the script functions in this file:
SetComponent2(id component, int count)
GetComponent(id component, int index)
Split2Components()
@author Maikel
*/
// Sets the component of an object or definition.
// documented in /docs/sdk/script/fn
global func SetComponent(id component, int count)
{
// Safety: can only be called from object.
if (!this || GetType(this) != C4V_C4Object)
return FatalError(Format("SetComponent must be called from object context and not from %v", this));
// Safety: component must be specified.
if (!component || GetType(component) != C4V_Def)
return FatalError(Format("First parameter (id component) of SetComponent must be a definition but was %v.", component));
// Ensure count is non-negative.
count = Max(count, 0);
// Initialize Components if it does not exist yet.
if (!this.Components || GetType(this.Components) != C4V_PropList)
this.Components = {};
// Ensure object property is different from definition property.
if (this.Components == GetID().Components)
MakePropertyWritable("Components");
// Write the new count to the components properties.
this.Components[Format("%i", component)] = count;
return;
}
// Returns the amount if the component parameter is specified. If the component parameter is nil
// it returns the definition of the component for the given index.
// documented in /docs/sdk/script/fn
global func GetComponent(id component, int index)
{
// Safety: can only be called from object or definition context.
if (!this || (GetType(this) != C4V_C4Object && GetType(this) != C4V_Def))
return FatalError(Format("GetComponent must be called from object or definition context and not from %v", this));
// Safety: return nil if Components could not be found or are not a proplist.
if (!this.Components || GetType(this.Components) != C4V_PropList)
return;
// If component is specified return the count for that definition.
if (GetType(component) == C4V_Def)
return this.Components[Format("%i", component)];
// Ensure the index is valid.
index = Max(index, 0);
// If component is not specified return the definition of the component at the index.
var cnt = 0;
for (var entry in GetProperties(this.Components))
{
// Check if the entry is an actual valid definition.
var entry_def = GetDefinition(entry);
if (!entry_def)
continue;
if (index == cnt)
return entry_def;
cnt++;
}
return;
}
// Callback by the engine when the completion of an object changes.
global func OnCompletionChange(int old_con, int new_con)
{
if (!this || GetType(this) != C4V_C4Object)
return _inherited(old_con, new_con, ...);
// Determine whether the object allows Oversize.
var oversize = GetDefCoreVal("Oversize", "DefCore");
// Loop over all components and set their new count.
var index = 0, comp;
while (comp = GetID()->GetComponent(nil, index))
{
var def_cnt = GetID()->GetComponent(comp);
var new_cnt = Max(def_cnt * new_con / 100000, 0);
// If the object has no Oversize then constrain the maximum number of components.
if (!oversize)
new_cnt = Min(new_cnt, def_cnt);
SetComponent(comp, new_cnt);
index++;
}
return _inherited(old_con, new_con, ...);
}
// Splits the calling object into its components.
// documented in /docs/sdk/script/fn
global func Split2Components()
{
// Safety: can only be called from object context.
if (!this || GetType(this) != C4V_C4Object)
return FatalError(Format("Split2Components must be called from object context and not from %v", this));
// Transfer all contents to container.
var ctr = Contained();
while (Contents())
if (!ctr || !Contents()->Enter(ctr))
Contents()->Exit();
// Split components.
for (var i = 0, compid; compid = GetComponent(nil, i); ++i)
for (var j = 0; j < GetComponent(compid); ++j)
{
var comp = CreateObjectAbove(compid, nil, nil, GetOwner());
if (OnFire()) comp->Incinerate();
if (!ctr || !comp->Enter(ctr))
{
comp->SetR(Random(360));
comp->SetXDir(Random(3) - 1);
comp->SetYDir(Random(3) - 1);
comp->SetRDir(Random(3) - 1);
}
}
return RemoveObject();
}