forked from Mirrors/openclonk
317 lines
9.7 KiB
C++
317 lines
9.7 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 1998-2000, Matthes Bender
|
|
* Copyright (c) 2008-2009, RedWolf Design GmbH, http://www.clonk.de/
|
|
* Copyright (c) 2009-2013, The OpenClonk Team and contributors
|
|
*
|
|
* Distributed under the terms of the ISC license; see accompanying file
|
|
* "COPYING" for details.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
|
* See accompanying file "TRADEMARK" for details.
|
|
*
|
|
* To redistribute this file separately, substitute the full license texts
|
|
* for the above references.
|
|
*/
|
|
// Menus attached to objects; script created or internal
|
|
|
|
#include "C4Include.h"
|
|
#include "C4ObjectMenu.h"
|
|
|
|
#include "C4Control.h"
|
|
#include "C4Object.h"
|
|
#include "C4ObjectCom.h"
|
|
#include "C4Player.h"
|
|
#include "C4Viewport.h"
|
|
#include "C4MouseControl.h"
|
|
#include "C4GraphicsResource.h"
|
|
#include "C4Game.h"
|
|
#include "C4PlayerList.h"
|
|
#include "C4GameObjects.h"
|
|
|
|
|
|
// -----------------------------------------------------------
|
|
// C4ObjectMenu
|
|
|
|
C4ObjectMenu::C4ObjectMenu() : C4Menu()
|
|
{
|
|
Default();
|
|
}
|
|
|
|
void C4ObjectMenu::Default()
|
|
{
|
|
C4Menu::Default();
|
|
eCallbackType = CB_None;
|
|
Object = ParentObject = RefillObject = NULL;
|
|
RefillObjectContentsCount=0;
|
|
UserMenu = false;
|
|
CloseQuerying = false;
|
|
}
|
|
|
|
bool C4ObjectMenu::IsCloseDenied()
|
|
{
|
|
// abort if menu is permanented by script; stop endless recursive calls if user opens a new menu by CloseQuerying-flag
|
|
if (UserMenu && !CloseQuerying)
|
|
{
|
|
CloseQuerying = true;
|
|
bool fResult = false;
|
|
C4AulParSet pars(C4VInt(Selection), C4VObj(ParentObject));
|
|
if (eCallbackType == CB_Object)
|
|
{
|
|
if (Object) fResult = !!Object->Call(PSF_MenuQueryCancel, &pars);
|
|
}
|
|
else if (eCallbackType == CB_Scenario)
|
|
fResult = !!::GameScript.Call(PSF_MenuQueryCancel, &pars);
|
|
CloseQuerying = false;
|
|
if (fResult) return true;
|
|
}
|
|
// close OK
|
|
return false;
|
|
}
|
|
|
|
void C4ObjectMenu::LocalInit(C4Object *pObject, bool fUserMenu)
|
|
{
|
|
Object=pObject;
|
|
UserMenu=fUserMenu;
|
|
ParentObject=GetParentObject();
|
|
if (pObject) eCallbackType = CB_Object; else eCallbackType = CB_Scenario;
|
|
}
|
|
|
|
bool C4ObjectMenu::Init(C4FacetSurface &fctSymbol, const char *szEmpty, C4Object *pObject, int32_t iExtra, int32_t iExtraData, int32_t iId, int32_t iStyle, bool fUserMenu)
|
|
{
|
|
if (!DoInit(fctSymbol, szEmpty, iExtra, iExtraData, iId, iStyle)) return false;
|
|
LocalInit(pObject, fUserMenu);
|
|
return true;
|
|
}
|
|
|
|
bool C4ObjectMenu::InitRefSym(const C4TargetFacet &fctSymbol, const char *szEmpty, C4Object *pObject, int32_t iExtra, int32_t iExtraData, int32_t iId, int32_t iStyle, bool fUserMenu)
|
|
{
|
|
if (!DoInitRefSym(fctSymbol, szEmpty, iExtra, iExtraData, iId, iStyle)) return false;
|
|
LocalInit(pObject, fUserMenu);
|
|
return true;
|
|
}
|
|
|
|
void C4ObjectMenu::OnSelectionChanged(int32_t iNewSelection)
|
|
{
|
|
// do selection callback
|
|
if (UserMenu)
|
|
{
|
|
C4AulParSet pars(C4VInt(iNewSelection), C4VObj(ParentObject));
|
|
if (eCallbackType == CB_Object && Object)
|
|
Object->Call(PSF_MenuSelection, &pars);
|
|
else if (eCallbackType == CB_Scenario)
|
|
::GameScript.Call(PSF_MenuSelection, &pars);
|
|
}
|
|
}
|
|
|
|
void C4ObjectMenu::ClearPointers(C4Object *pObj)
|
|
{
|
|
if (Object==pObj) { Object=NULL; }
|
|
if (ParentObject==pObj) ParentObject=NULL; // Reason for menu close anyway.
|
|
if (RefillObject==pObj) RefillObject=NULL;
|
|
C4Menu::ClearPointers(pObj);
|
|
}
|
|
|
|
C4Object* C4ObjectMenu::GetParentObject()
|
|
{
|
|
for (C4Object *cObj : Objects)
|
|
if (cObj->Menu == this)
|
|
return cObj;
|
|
return NULL;
|
|
}
|
|
|
|
void C4ObjectMenu::SetRefillObject(C4Object *pObj)
|
|
{
|
|
RefillObject=pObj;
|
|
NeedRefill=true;
|
|
Refill();
|
|
}
|
|
|
|
bool C4ObjectMenu::DoRefillInternal(bool &rfRefilled)
|
|
{
|
|
// Variables
|
|
C4FacetSurface fctSymbol;
|
|
C4Object *pObj;
|
|
char szCaption[256+1],szCommand[256+1],szCommand2[256+1];
|
|
int32_t iCount;
|
|
C4Def *pDef;
|
|
C4IDList ListItems;
|
|
C4Object *pTarget;
|
|
C4Facet fctTarget;
|
|
|
|
// Refill
|
|
switch (Identification)
|
|
{
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
case C4MN_Activate:
|
|
// Clear items
|
|
ClearItems();
|
|
// Refill target
|
|
if (!(pTarget=RefillObject)) return false;
|
|
{
|
|
// Add target contents items
|
|
C4ObjectListIterator iter(pTarget->Contents);
|
|
while ((pObj = iter.GetNext(&iCount)))
|
|
{
|
|
pDef = pObj->Def;
|
|
if (pDef->NoGet) continue;
|
|
// Prefer fully constructed objects
|
|
if (~pObj->OCF & OCF_FullCon)
|
|
{
|
|
// easy way: only if first concat check matches
|
|
// this doesn't catch all possibilities, but that will rarely matter
|
|
C4Object *pObj2=pTarget->Contents.Find(pDef, ANY_OWNER, OCF_FullCon);
|
|
if (pObj2) if (pObj2->CanConcatPictureWith(pObj)) pObj = pObj2;
|
|
}
|
|
// Caption
|
|
sprintf(szCaption,LoadResStr("IDS_MENU_ACTIVATE"),(const char *) pObj->GetName());
|
|
// Picture
|
|
fctSymbol.Set(fctSymbol.Surface, 0,0,C4SymbolSize,C4SymbolSize);
|
|
pObj->Picture2Facet(fctSymbol);
|
|
// Commands
|
|
sprintf(szCommand,"SetCommand(\"Activate\",Object(%d))&&ExecuteCommand()",pObj->Number);
|
|
sprintf(szCommand2,"SetCommand(\"Activate\",nil,%d,0,Object(%d),%s)&&ExecuteCommand()",pTarget->Contents.ObjectCount(pDef->id),pTarget->Number,pDef->id.ToString());
|
|
// Add menu item
|
|
Add(szCaption,fctSymbol,szCommand,iCount,pObj,"",pDef->id,szCommand2,true,pObj->GetValue(pTarget, NO_OWNER));
|
|
// facet taken over (arrg!)
|
|
fctSymbol.Default();
|
|
}
|
|
}
|
|
break;
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
case C4MN_Get:
|
|
case C4MN_Contents:
|
|
// Clear items
|
|
ClearItems();
|
|
// Refill target
|
|
if (!(pTarget = RefillObject)) return false;
|
|
{
|
|
// Add target contents items
|
|
C4ObjectListIterator iter(pTarget->Contents);
|
|
while ((pObj = iter.GetNext(&iCount)))
|
|
{
|
|
pDef = pObj->Def;
|
|
if (pDef->NoGet) continue;
|
|
// Prefer fully constructed objects
|
|
if (~pObj->OCF & OCF_FullCon)
|
|
{
|
|
// easy way: only if first concat check matches
|
|
// this doesn't catch all possibilities, but that will rarely matter
|
|
C4Object *pObj2 = pTarget->Contents.Find(pDef, ANY_OWNER, OCF_FullCon);
|
|
if (pObj2) if (pObj2->CanConcatPictureWith(pObj)) pObj = pObj2;
|
|
}
|
|
// Determine whether to get or activate
|
|
bool fGet = true;
|
|
if (!(pObj->OCF & OCF_Carryable)) fGet = false; // not a carryable item
|
|
if (Identification == C4MN_Contents)
|
|
{
|
|
if (Object && !!Object->Call(PSF_RejectCollection, &C4AulParSet(C4VPropList(pObj->Def), C4VObj(pObj)))) fGet = false; // collection rejected
|
|
}
|
|
if (!(pTarget->OCF & OCF_Entrance)) fGet = true; // target object has no entrance: cannot activate - force get
|
|
// Caption
|
|
sprintf(szCaption, LoadResStr(fGet ? "IDS_MENU_GET" : "IDS_MENU_ACTIVATE"), (const char *)pObj->GetName());
|
|
// Picture
|
|
fctSymbol.Set(fctSymbol.Surface, 0, 0, C4SymbolSize, C4SymbolSize);
|
|
pObj->Picture2Facet(fctSymbol);
|
|
// Primary command: get/activate single object
|
|
sprintf(szCommand, "SetCommand(\"%s\", Object(%d)) && ExecuteCommand()", fGet ? "Get" : "Activate", pObj->Number);
|
|
// Secondary command: get/activate all objects of the chosen type
|
|
szCommand2[0] = 0; int32_t iAllCount;
|
|
if ((iAllCount = pTarget->Contents.ObjectCount(pDef->id)) > 1)
|
|
sprintf(szCommand2, "SetCommand(\"%s\", nil, %d,0, Object(%d), %s) && ExecuteCommand()", fGet ? "Get" : "Activate", iAllCount, pTarget->Number, pDef->id.ToString());
|
|
// Add menu item (with object)
|
|
Add(szCaption, fctSymbol, szCommand, iCount, pObj, "", pDef->id, szCommand2);
|
|
fctSymbol.Default();
|
|
}
|
|
}
|
|
break;
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
default:
|
|
// Not an internal menu
|
|
return true;
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
}
|
|
|
|
// Successfull internal refill
|
|
rfRefilled = true;
|
|
return true;
|
|
}
|
|
|
|
void C4ObjectMenu::Execute()
|
|
{
|
|
if (!IsActive()) return;
|
|
// Immediate refill check by RefillObject contents count check
|
|
if (RefillObject)
|
|
if (RefillObject->Contents.ObjectCount()!=RefillObjectContentsCount)
|
|
{ NeedRefill=true; RefillObjectContentsCount=RefillObject->Contents.ObjectCount(); }
|
|
// inherited
|
|
C4Menu::Execute();
|
|
}
|
|
|
|
void C4ObjectMenu::OnUserSelectItem(int32_t Player, int32_t iIndex)
|
|
{
|
|
// queue.... 2do
|
|
Game.Input.Add(CID_PlrControl, new C4ControlPlayerControl(Player,Game.PlayerControlDefs.InternalCons.CON_ObjectMenuSelect,iIndex | C4MN_AdjustPosition));
|
|
}
|
|
|
|
void C4ObjectMenu::OnUserEnter(int32_t Player, int32_t iIndex, bool fRight)
|
|
{
|
|
// object menu: Through queue 2do
|
|
Game.Input.Add(CID_PlrControl, new C4ControlPlayerControl(Player,fRight ? Game.PlayerControlDefs.InternalCons.CON_ObjectMenuOKAll : Game.PlayerControlDefs.InternalCons.CON_ObjectMenuOK,iIndex));
|
|
}
|
|
|
|
void C4ObjectMenu::OnUserClose()
|
|
{
|
|
// Queue 2do
|
|
Game.Input.Add(CID_PlrControl, new C4ControlPlayerControl(::MouseControl.GetPlayer(),Game.PlayerControlDefs.InternalCons.CON_ObjectMenuCancel,0));
|
|
}
|
|
|
|
bool C4ObjectMenu::IsReadOnly()
|
|
{
|
|
// get viewport
|
|
C4Viewport *pVP = GetViewport();
|
|
if (!pVP) return false;
|
|
// is it an observer viewport?
|
|
if (pVP->fIsNoOwnerViewport)
|
|
// is this a synced menu?
|
|
if (eCallbackType == CB_Object || eCallbackType == CB_Scenario)
|
|
// then don't control it!
|
|
return true;
|
|
// if the player is eliminated, do not control either!
|
|
if (!pVP->fIsNoOwnerViewport)
|
|
{
|
|
C4Player *pPlr = ::Players.Get(::MouseControl.GetPlayer());
|
|
if (pPlr && pPlr->Eliminated) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int32_t C4ObjectMenu::GetControllingPlayer()
|
|
{
|
|
// menu controlled by object controller
|
|
return Object ? Object->Controller : NO_OWNER;
|
|
}
|
|
|
|
bool C4ObjectMenu::MenuCommand(const char *szCommand, bool fIsCloseCommand)
|
|
{
|
|
switch (eCallbackType)
|
|
{
|
|
case CB_Object:
|
|
// Object menu
|
|
if (Object) Object->MenuCommand(szCommand);
|
|
break;
|
|
|
|
case CB_Scenario:
|
|
// Object menu with scenario script callback
|
|
::GameScript.DirectExec(NULL, szCommand, "MenuCommand");
|
|
break;
|
|
|
|
case CB_None:
|
|
// TODO
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|