forked from Mirrors/openclonk
391 lines
10 KiB
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$"; |