/* * 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; }