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

391 lines
10 KiB
C

/*--- The Base ---*/
// Author: Randrian
// Determines it the Building is acutally a base
local fIsBase;
local iEnergy;
// ---------------- Settings for base funcionallity --------------------
// --- these functions can be overloaded for vendors or special bases ---
// Determines if the base can heal allied clonks
public func CanHeal() { return true; }
// The amount of energy the base can store, if none the base can't heal
public func GetHeal() { return 100; }
// The money one filling of the bases energy storage costs
public func GetHealCost() { return 5;}
// Determines if the base can extinguish allied clonks
public func CanExtinguish() { return true; }
// The autosell function
public func ExecAutoSell()
{
// Search all objects for objects that want to be sold automatically
for(pObj in FindObjects(Find_Container(this), Find_Func("AutoSell")))
Sell(pObj->GetOwner(), pObj, this);
}
// Does the base block enemies?
public func CanBlockEnemies() { return true; }
// ---------------------------------------------------------
// ----------------- Settings for the trading of objects ----------------
// --- these functions can be overloaded for vendors or special bases ---
// returns an array with the definition, the amount
func GetBuyObject(int iIndex)
{
var aBuy = [0,0];
var idDef = GetHomebaseMaterial(GetOwner(), nil, iIndex, C4D_All);
aBuy[0] = idDef;
aBuy[1] = GetHomebaseMaterial(GetOwner(), idDef, 0);
if(!idDef) return 0;
// The default implementation returns the Homebasemaerial of the playeer
return aBuy;
}
// returns an array with all buyable objects
func GetBuyObjects()
{
var aBuyObjects = [];
var iIndex, aBuy;
while(aBuy = GetBuyObject(iIndex++))
aBuyObjects[iIndex-1] = aBuy;
return aBuyObjects;
}
// returns the value of the object if sold in this base
func GetSellValue(object pObj)
{
// By default call the engine function
return pObj->GetValue();
}
func GetBuyValue(id idObj)
{
// By default call the engine function
return idObj->GetValue();
}
// change the amount of buyable material
func DoBaseMaterial(id idDef, int iCount)
{
// by default use Homebase engine function
DoHomebaseMaterial(GetOwner(), idDef, iCount);
// this should also call UpdateClonkBuyMenus() if the standart function isn't used
}
public func OnHomebaseMaterialChange()
{
// and update the buy menu
UpdateClonkBuyMenus();
}
// ---------------------------------------------------------
// ------------- Base states -------------------------------
// This Building can be a base
public func IsBaseBuilding() { return true; }
// This Building is a base at the moment
public func IsBase() { return fIsBase; }
// Makes this building a base or removes the base functionallity
public func MakeBase(bool fRemoveBase)
{
if(fRemoveBase)
{
fIsBase = 0;
RemoveEffect("IntBase", this);
}
else
{
fIsBase = 1;
AddEffect("IntBase", this, 1, 10, this);
if(!FindObject(Find_ID(BaseMaterial), Find_Owner(GetOwner())))
CreateObject(BaseMaterial,AbsX(10),AbsY(10),GetOwner());
}
}
// ---------- Healing, Extinguishing and Autosell -----------
func FxIntBaseTimer(pThis, effect, iTime)
{
var pObj;
// Can this base heal? Then look for clonks that need some
if(CanHeal() && GetHeal())
for(pObj in FindObjects(Find_Container(this), Find_OCF(OCF_CrewMember), Find_Allied(GetOwner())))
{
if(pObj->GetEnergy() < pObj->GetMaxEnergy() && !GetEffect("IntBaseHeal", pObj))
AddEffect("IntBaseHeal", pObj, 1, 1, this);
}
// Can this base extinguish? Then look for something on fire
if(CanExtinguish())
for(pObj in FindObjects(Find_Container(this), Find_OCF(OCF_OnFire), Find_Allied(GetOwner())))
pObj->Extinguish();
// Sell objects
ExecAutoSell();
// Update the sell menu of clonks (if someone knows a way to directly get info if the contents of the base change this coult be imporved)
if(aClonkSellList)
{
// Only if there are clonks with menus
var fFound;
for(pClonk in aClonkSellList)
if(pClonk)
{
fFound = 1;
break;
}
if(fFound)
UpdateSellList();
}
}
func FxIntBaseHealTimer(pClonk, effect)
{
// The clonk has left the base? Stop!
if(pClonk->Contained() != this) return -1;
// Full energy? Stop too.
if(pClonk->GetEnergy() >= pClonk->GetMaxEnergy()) return -1;
// No energy left? Buy some
if(!iEnergy)
{
if(GetWealth(GetOwner()) >= GetHealCost())
{
DoWealth(GetOwner(), -GetHealCost());
Sound("UnCash", 0, 100, pClonk->GetOwner()+1); // TODO: get sound
iEnergy = GetHeal()*5;
}
}
// Some energy in the storage? heal clonk
if(iEnergy)
{
pClonk->DoEnergy(200, 1, FX_Call_EngBaseRefresh, GetOwner()+1);
iEnergy--;
}
}
// ------------------------ Buying -------------------------------------
local aClonkBuyList;
func AddClonkBuyList(object pClonk)
{
if(!aClonkBuyList) aClonkBuyList = [];
var iIndex = 0;
// find free slot
while(aClonkBuyList[iIndex] && aClonkBuyList[iIndex] != pClonk) iIndex++;
aClonkBuyList[iIndex] = pClonk;
}
func UpdateClonkBuyMenus()
{
if(!aClonkBuyList) aClonkBuyList = [];
// Reopen the menu for all clonks
var pClonk;
var iIndex;
for(pClonk in aClonkBuyList)
{
iIndex++;
if(!pClonk) continue;
if(pClonk->GetMenu() != Library_Base)
{
aClonkBuyList[iIndex-1] = 0;
continue;
}
OpenBuyMenu(pClonk, nil, pClonk->GetMenuSelection());
}
}
// Buy
func OpenBuyMenu(object pClonk, id idDef, int iSelection)
{
var aBuy = [0,0,0];
var iIndex, iSelection;
AddClonkBuyList(pClonk);
pClonk->CreateMenu (Library_Base, this, C4MN_Extra_Value, "$TxtNothingToBuy$", 0, C4MN_Style_Normal, 0, C4Id("BuyMenu"));
for(aBuy in GetBuyObjects())
{
if(aBuy[0] == idDef) iSelection = iIndex;
pClonk->AddMenuItem("$TxtBuy$", "BuyDummy", aBuy[0], aBuy[1], pClonk, nil, 128, 0, GetBuyValue(aBuy[0]));
iIndex++;
}
if(idDef || iSelection) pClonk->SelectMenuItem(iSelection);
}
func BuyDummy(id idDef, object pClonk, bool bRight, int iValue)
{
var iPlr = pClonk->GetOwner();
DoBuy(idDef, iPlr, GetOwner(), pClonk, bRight, 1);
OpenBuyMenu(pClonk, idDef);
}
func DoBuy(id idDef, int iForPlr, int iPayPlr, object pClonk, bool bRight, bool fShowErrors)
{
if(!GetHomebaseMaterial(iPayPlr, idDef)) return; //TODO
var iValue = GetBuyValue(idDef);
// Has the clonk enought money?
if(iValue > GetWealth(iPayPlr))
{
// TODO: get an errorsound
if(fShowErrors)
{
Sound("Error", 0, 100, iForPlr+1);
PlayerMessage(iForPlr, "$TxtNotEnoughtMoney$");
}
return -1;
}
// Take the cash
DoWealth(iPayPlr, -iValue);
Sound("UnCash", 0, 100, iForPlr+1); // TODO: get sound
// Decrease the Basematerial
DoBaseMaterial(idDef, -1);
// Deliver the object
var pObj = CreateContents(idDef);
pObj->SetOwner(iForPlr);
if(pObj->GetOCF() & OCF_CrewMember) pObj->MakeCrewMember(iForPlr);
if(pObj->GetOCF() & OCF_Collectible) pClonk->Collect(pObj);
// is right clicked? then buy another object
if(bRight)
DoBuy(idDef, iForPlr, iPayPlr, pClonk, bRight, false);
return pObj;
}
// -------------------------- Selling -------------------------------------
func GetSellableContents()
{
return FindObjects(Find_Container(this), Find_Or(Find_Category(C4D_Object), Find_Category(C4D_Vehicle), Find_Category(65536/*C4D_TradeLiving*/)));
}
local aSellList;
local aClonkSellList;
func UpdateSellList()
{
aSellList = [];
var iIndex;
// Create a list of the sellable objects
for(pObj in GetSellableContents())
{
// Are we allowed to sell the object?
if (pObj.NoSell) continue;
// Only check the last item to stack, the engine normally sorts the objects so this should be enought to check
if(iIndex && CanStack(aSellList[iIndex-1][2], pObj))
aSellList[iIndex-1][1]++;
else
aSellList[iIndex++] = [pObj->GetID(), 1, pObj];
}
UpdateClonkSellMenus();
}
func AddClonkSellList(object pClonk)
{
if(!aClonkSellList) aClonkSellList = [];
var iIndex = 0;
// find free slot
while(aClonkSellList[iIndex] && aClonkSellList[iIndex] != pClonk) iIndex++;
aClonkSellList[iIndex] = pClonk;
}
func UpdateClonkSellMenus()
{
if(!aClonkSellList) return;
// Reopen the menu for all clonks
var pClonk;
var iIndex;
for(pClonk in aClonkSellList)
{
iIndex++;
if(!pClonk) continue;
if(pClonk->GetMenu() != BaseMaterial)
{
aClonkSellList[iIndex-1] = 0;
continue;
}
OpenSellMenu(pClonk, pClonk->GetMenuSelection(), 1);
}
}
// Sell Object
func OpenSellMenu(object pClonk, int iSelection, bool fNoListUpdate)
{
// Filled with [idDef, iCount, pObj] arrays
var aArray;
var iIndex;
if(!fNoListUpdate)
UpdateSellList();
AddClonkSellList(pClonk);
pClonk->CreateMenu (BaseMaterial, this, C4MN_Extra_Value, "$TxtNothingToSell$", 0, C4MN_Style_Normal, 1);
var iIndex;
for(aArray in aSellList) // aArray contains [idDef, iCount, pObj]
{
pClonk->AddMenuItem(Format("$TxtSell$", aArray[2]->GetName()), "SellDummy", aArray[0], aArray[1], pClonk, nil, 128+4, aArray[2], GetSellValue(aArray[2]));
iIndex++;
}
if(iSelection == iIndex) iSelection--;
pClonk->SelectMenuItem(iSelection);
}
func CanStack(object pFirst, object pSecond)
{
// Test if these Objects differ from each other
if(!pFirst->CanConcatPictureWith(pSecond)) return false;
if(GetSellValue(pFirst) != GetSellValue(pSecond)) return false;
// ok they can be stacked
return true;
}
func SellDummy(id idDef, object pClonk, bool bRight)
{
var iIndex = pClonk->GetMenuSelection();
var aArray = aSellList[iIndex];
DoSell(aArray[2], GetOwner(), bRight);
OpenSellMenu(pClonk, iIndex);
}
func DoSell(object pObj, int iPlr, bool bRight)
{
// Test the object
if(pObj.NoSell || pObj->GetOCF() & OCF_CrewMember)
{
// Enter base (needed for NoSell objects in other objects which are sold)
if(pObj->Contained() != this)
pObj->Enter(this);
return 0;
}
// Sell contents first
var pContents;
for(pContents in FindObjects(Find_Container(pObj)))
DoSell(pContents, iPlr);
// Give the player the cash
DoWealth(iPlr, GetSellValue(pObj));
Sound("Cash", 0, 100, iPlr+1); // TODO: get sound
if(pObj.Rebuy)
DoBaseMaterial(pObj->GetID(), 1);
// right clicked? then sell other objects too
var pNewObj;
var bFound;
if(bRight)
{
for(var pNewObj in GetSellableContents())
if(CanStack(pObj, pNewObj) && pObj != pNewObj)
{
bFound = 1;
break;
}
}
// And remove the object
pObj->RemoveObject();
if(bFound) return DoSell(pNewObj, iPlr, bRight);
return true;
}
local Name = "$Name$";