openclonk/planet/Objects.ocd/Libraries.ocd/Stackable.ocd/Script.c

201 lines
4.7 KiB
C

/*--
Stackable
Author: Newton
Including this object means, the object is stackable. Other objects of
the same type will be added automatically to the object. This functionality
is similar to the Pack-functionality of the arrows in old clonk titles only
more general.
The count of how many objects are stacked together (into a single one, this
one) is shown in the picture of the object and can be queried and set via
GetStackCount()/SetStackCount().
To take one object of the stack, call TakeObject(). As long
as the object exists, one can always take an object, even if it is the last
one (self). This object is always outside.
On entrance (or to be more precise: on RejectEntrance), it will be checked
if the entering stackable object can be distributed over the other objects
of the same ID. If yes, this object is deleted and the other object(s) will
have a higher stack-count.
Example 1:
'15x Arrow' is about to enter a clonk which has '5x Arrow'. 15 will be added
to the stack-count of the clonks '5x Arrow'-object (making it '20x Arrow'),
the entering object will be deleted.
Example 2:
'17x Arrow' is about to enter a clonk which has '15x Arrow' and a bow with
'10x Arrow' in it's ammunition slot. 10 will be added to the stack-count
of the arrows in the bow, 5 to the stack-count of the arrows in the clonk
(assuming MaxStackCount() is 20) and the original arrows-object will have
2 arrows left. If there is an inventory slot left, the '2x Arrow" object
will enter the clonk.
Most objects which can be stacked might want to set different pictures
and ingame graphics for different counts of objects. This can be done
by overloading UpdatePicture(), but remember to write _inherited() then.
--*/
local count;
public func IsStackable() { return true; }
public func GetStackCount() { return Max(1, count); }
public func MaxStackCount() { return 20; }
protected func Construction()
{
count = MaxStackCount();
}
public func Stack(object obj)
{
if (obj->GetID() != GetID())
return 0;
var howmany = Min(obj->GetStackCount(), MaxStackCount() - GetStackCount());
if (count + howmany > 999)
return 0;
SetStackCount(count + howmany);
return howmany;
}
public func SetStackCount(int amount)
{
count = BoundBy(amount, 0, 999);
Update();
}
public func TakeObject()
{
if (count == 1)
{
Exit();
return this;
}
else if (count > 1)
{
SetStackCount(count - 1);
var take = CreateObject(GetID(), 0, 0, GetOwner());
take->SetStackCount(1);
Update();
return take;
}
}
public func Update()
{
UpdatePicture();
UpdateMass();
UpdateName();
// notify hud
var container = Contained();
if (container)
{
// has an extra slot
if (container->~HasExtraSlot())
container->~NotifyHUD();
// is a clonk with new inventory system
else if (container->~GetSelected())
{
var pos = container->GetItemPos(this);
container->~OnSlotFull(pos);
}
}
}
private func UpdatePicture()
{
var one = GetStackCount() % 10;
var ten = (GetStackCount() / 10) % 10;
var hun = (GetStackCount() / 100) % 10;
var s = 400;
var yoffs = 14000;
var xoffs = 22000;
var spacing = 14000;
if (hun > 0)
{
SetGraphics(Format("%d", hun), Icon_Number, 10, GFXOV_MODE_Picture);
SetObjDrawTransform(s, 0, xoffs - 2 * spacing, 0, s, yoffs, 10);
}
else
SetGraphics(nil, nil, 10);
if (ten > 0 || hun > 0)
{
SetGraphics(Format("%d", ten), Icon_Number, 11, GFXOV_MODE_Picture);
SetObjDrawTransform(s, 0, xoffs - spacing, 0, s, yoffs, 11);
}
else
SetGraphics(nil, nil, 11);
SetGraphics(Format("%d", one), Icon_Number, 12, GFXOV_MODE_Picture);
SetObjDrawTransform(s, 0, xoffs, 0, s, yoffs, 12);
}
private func UpdateName()
{
SetName(Format("%dx %s", GetStackCount(), GetID()->GetName()));
}
private func UpdateMass()
{
SetMass(GetID()->GetMass() * Max(GetStackCount(), 1) / MaxStackCount());
}
protected func RejectEntrance(object into)
{
if (TryPutInto(into))
return true;
return _inherited(into, ...);
}
/* Value */
public func CalcValue(object in_base, int for_plr)
{
return GetID()->GetValue() * Max(GetStackCount(), 1) / MaxStackCount();
}
private func TryPutInto(object into)
{
var contents = FindObjects(Find_Container(into));
// first check if stackable can be put into object with extra slot
for (var content in contents)
{
if (!content)
continue;
if (content->~HasExtraSlot())
if (TryPutInto(content))
return true;
}
// then check this object
for (var content in contents)
{
var howmany = 0;
if (!content)
continue;
if (content->~IsStackable())
if (content->GetID() == GetID())
if (howmany = content->Stack(this))
{
count -= howmany;
if(count <= 0)
{
RemoveObject();
return true;
}
}
}
Update();
return false;
}