Control: Extract library for using menus

External packs use this functionality without using the whole default Clonk control.
ipv6
Mark 2017-01-03 17:23:30 +01:00
parent 39c54c323c
commit fa28a7a643
3 changed files with 226 additions and 182 deletions

View File

@ -34,6 +34,7 @@
#include Library_Inventory
#include Library_ClonkInventoryControl
#include Library_ClonkInteractionControl
#include Library_ClonkMenuControl
#include Library_ClonkUseControl
#include Library_ClonkGamepadControl
@ -59,7 +60,6 @@ static const DEFAULT_THROWING_ANGLE = 500;
this.control.alt: alternate usage by right mouse button
this.control.mlastx: last x position of the cursor
this.control.mlasty: last y position of the cursor
this.control.menu: the menu that is currently assigned to the Clonk. Use the methods SetMenu/GetMenu/etc to access it.
*/
@ -78,33 +78,6 @@ protected func Construction()
this.control.hotkeypressed = false;
this.control.alt = false;
this.control.menu = nil;
return _inherited(...);
}
// ...aaand the same for when the clonk is deselected
protected func CrewSelection(bool unselect)
{
if (unselect)
{
// if there is still a menu, cancel it too...
CancelMenu();
}
return _inherited(unselect,...);
}
protected func Destruction()
{
// close open menus, ...
CancelMenu();
return _inherited(...);
}
protected func Death()
{
// close open menus, ...
CancelMenu();
return _inherited(...);
}
@ -173,7 +146,7 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
{
var is_content = GetMenu()->~IsContentMenu();
// unclosable menu? bad luck
if (!TryCancelMenu()) return true;
if (!this->~TryCancelMenu()) return true;
// If contents menu, don't open new one and return.
if (is_content)
return true;
@ -559,159 +532,6 @@ public func CanEnter()
public func IsMounted() { return GetProcedure() == "ATTACH"; }
/* +++++++++++++++++++++++ Menu control +++++++++++++++++++++++ */
func HasMenuControl()
{
return true;
}
// helper function that can be attached to a proplist to set callbacks on-the-fly
func GetTrue() { return true; }
/*
Sets the menu this Clonk currently has focus of. Old menus that have been opened via SetMenu will be closed, making sure that only one menu is open at a time.
Additionally, the Clonk's control is disabled while a menu is open.
The menu parameter can either be an object that closes its menu via a Close() callback or it can be a menu ID as returned by GuiOpen. When /menu/ is such an ID,
the menu will be closed via GuiClose when a new menu is opened. If you need to do cleaning up, you will have to use the OnClose callback of the menu.
When you call SetMenu with a menu ID, you should also call clonk->MenuClosed(), once your menu is closed.
*/
func SetMenu(new_menu, bool unclosable)
{
unclosable = unclosable ?? false;
var current_menu = this.control.menu;
// no news?
if (new_menu) // if new_menu==nil, it is important that we still do the cleaning-up below even if we didn't have a menu before (see MenuClosed())
if (current_menu == new_menu) return;
// close old one!
if (current_menu != nil)
{
if (GetType(current_menu) == C4V_C4Object)
current_menu->Close();
else if (GetType(current_menu) == C4V_PropList)
GuiClose(current_menu.ID);
else
FatalError("Library_ClonkControl::SetMenu() was called with invalid parameter.");
}
else
{
// we have a new menu but didn't have another one before? Enable menu controls!
if (new_menu)
{
CancelUse();
// stop clonk
SetComDir(COMD_Stop);
if (PlayerHasVirtualCursor(GetOwner()))
VirtualCursor()->StartAim(this, 0, new_menu);
else
{
if (GetType(new_menu) == C4V_C4Object && new_menu->~CursorUpdatesEnabled())
SetPlayerControlEnabled(GetOwner(), CON_GUICursor, true);
SetPlayerControlEnabled(GetOwner(), CON_GUIClick1, true);
SetPlayerControlEnabled(GetOwner(), CON_GUIClick2, true);
}
}
}
if (new_menu)
{
if (GetType(new_menu) == C4V_C4Object)
{
this.control.menu = new_menu;
}
else if (GetType(new_menu) == C4V_Int)
{
// add a proplist, so that it is always safe to call functions on clonk->GetMenu()
this.control.menu =
{
ID = new_menu
};
}
else
FatalError("Library_ClonkControl::SetMenu called with invalid parameter!");
// make sure the menu is unclosable even if it is just a GUI ID
if (unclosable)
{
this.control.menu.Unclosable = Library_ClonkControl.GetTrue;
}
}
else
{
// always disable cursors, even if no old menu existed, because it can happen that a menu removes itself and thus the Clonk never knows whether the cursors are active or not
RemoveVirtualCursor(); // for gamepads
SetPlayerControlEnabled(GetOwner(), CON_GUICursor, false);
SetPlayerControlEnabled(GetOwner(), CON_GUIClick1, false);
SetPlayerControlEnabled(GetOwner(), CON_GUIClick2, false);
this.control.menu = nil;
}
return this.control.menu;
}
func MenuClosed()
{
// make sure not to clean up the menu again
this.control.menu = nil;
// and remove cursors etc.
SetMenu(nil);
}
/*
Returns the current menu or nil. If a menu is returned, it is always a proplist (but not necessarily an object).
Stuff like if (clonk->GetMenu()) clonk->GetMenu()->~IsClosable(); is always safe.
If you want to remove the menu, the suggested method is clonk->TryCancelMenu() to handle unclosable menus correctly.
*/
func GetMenu()
{
// No new-style menu set? Return the classic menu ID. This is deprecated and should be removed in some future.
// This function must return a proplist, but clashes with the engine-defined "GetMenu".
// This workaround here at least allows developers to reach the Clonk's menu ID.
if (this.control.menu == nil)
{
var menu_id = inherited(...);
if (menu_id) return {ID = menu_id};
}
return this.control.menu;
}
// Returns true when an existing menu was closed
func CancelMenu()
{
if (this.control.menu)
{
SetMenu(nil);
return true;
}
return false;
}
// Tries to cancel a non-unclosable menu. Returns true when there is no menu left after this call (even if there never was one).
func TryCancelMenu()
{
if (!this.control.menu) return true;
if (this.control.menu->~Unclosable()) return false;
CancelMenu();
return true;
}
public func RejectShiftCursor()
{
if (this.control.menu && this.control.menu->~Unclosable()) return true;
return _inherited(...);
}
public func OnShiftCursor()
{
TryCancelMenu();
return _inherited(...);
}
/*-- Throwing --*/
// Throwing

View File

@ -0,0 +1,5 @@
[DefCore]
id=Library_ClonkMenuControl
Version=8,0
Category=C4D_StaticBack
HideInCreator=true

View File

@ -0,0 +1,219 @@
/**
Clonk menu controls
Author: Newton
This object provides handling of the clonk controls including item
management, backpack controls and standard throwing behaviour. It
should be included into any clonk/crew definition.
The controls in System.ocg/PlayerControl.c only provide basic movement
handling, namely the movement left, right, up and down. The rest is
handled here:
(object) menu control and it's callbacks and
forwards to script.
Objects that inherit this object need to return _inherited(...) in the
following callbacks (if defined):
Construction, Collection2, Ejection, RejectCollect, Departure,
Entrance, AttachTargetLost, CrewSelection, Death,
Destruction, OnActionChanged
Used properties
this.control.menu: the menu that is currently assigned to the Clonk. Use the methods SetMenu/GetMenu/etc to access it.
*/
/* ++++++++++++++++++++++++ Callbacks ++++++++++++++++++++++++ */
protected func Construction()
{
if(this.control == nil)
this.control = {};
this.control.menu = nil;
return _inherited(...);
}
// ...aaand the same for when the clonk is deselected
protected func CrewSelection(bool unselect)
{
if (unselect)
{
// if there is still a menu, cancel it too...
CancelMenu();
}
return _inherited(unselect,...);
}
protected func Destruction()
{
// close open menus, ...
CancelMenu();
return _inherited(...);
}
protected func Death()
{
// close open menus, ...
CancelMenu();
return _inherited(...);
}
/* +++++++++++++++++++++++ Menu control +++++++++++++++++++++++ */
func HasMenuControl()
{
return true;
}
// helper function that can be attached to a proplist to set callbacks on-the-fly
func GetTrue() { return true; }
/*
Sets the menu this Clonk currently has focus of. Old menus that have been opened via SetMenu will be closed, making sure that only one menu is open at a time.
Additionally, the Clonk's control is disabled while a menu is open.
The menu parameter can either be an object that closes its menu via a Close() callback or it can be a menu ID as returned by GuiOpen. When /menu/ is such an ID,
the menu will be closed via GuiClose when a new menu is opened. If you need to do cleaning up, you will have to use the OnClose callback of the menu.
When you call SetMenu with a menu ID, you should also call clonk->MenuClosed(), once your menu is closed.
*/
func SetMenu(new_menu, bool unclosable)
{
unclosable = unclosable ?? false;
var current_menu = this.control.menu;
// no news?
if (new_menu) // if new_menu==nil, it is important that we still do the cleaning-up below even if we didn't have a menu before (see MenuClosed())
if (current_menu == new_menu) return;
// close old one!
if (current_menu != nil)
{
if (GetType(current_menu) == C4V_C4Object)
current_menu->Close();
else if (GetType(current_menu) == C4V_PropList)
GuiClose(current_menu.ID);
else
FatalError("Library_ClonkControl::SetMenu() was called with invalid parameter.");
}
else
{
// we have a new menu but didn't have another one before? Enable menu controls!
if (new_menu)
{
this->~CancelUse();
// stop clonk
SetComDir(COMD_Stop);
if (this->~HasVirtualCursor())
{
this->~VirtualCursor()->StartAim(this, 0, new_menu);
}
else
{
if (GetType(new_menu) == C4V_C4Object && new_menu->~CursorUpdatesEnabled())
SetPlayerControlEnabled(GetOwner(), CON_GUICursor, true);
SetPlayerControlEnabled(GetOwner(), CON_GUIClick1, true);
SetPlayerControlEnabled(GetOwner(), CON_GUIClick2, true);
}
}
}
if (new_menu)
{
if (GetType(new_menu) == C4V_C4Object)
{
this.control.menu = new_menu;
}
else if (GetType(new_menu) == C4V_Int)
{
// add a proplist, so that it is always safe to call functions on clonk->GetMenu()
this.control.menu =
{
ID = new_menu
};
}
else
FatalError("Library_ClonkControl::SetMenu called with invalid parameter!");
// make sure the menu is unclosable even if it is just a GUI ID
if (unclosable)
{
this.control.menu.Unclosable = Library_ClonkControl.GetTrue;
}
}
else
{
// always disable cursors, even if no old menu existed, because it can happen that a menu removes itself and thus the Clonk never knows whether the cursors are active or not
if (this->~HasVirtualCursor())
{
this->~RemoveVirtualCursor(); // for gamepads
}
SetPlayerControlEnabled(GetOwner(), CON_GUICursor, false);
SetPlayerControlEnabled(GetOwner(), CON_GUIClick1, false);
SetPlayerControlEnabled(GetOwner(), CON_GUIClick2, false);
this.control.menu = nil;
}
return this.control.menu;
}
func MenuClosed()
{
// make sure not to clean up the menu again
this.control.menu = nil;
// and remove cursors etc.
SetMenu(nil);
}
/*
Returns the current menu or nil. If a menu is returned, it is always a proplist (but not necessarily an object).
Stuff like if (clonk->GetMenu()) clonk->GetMenu()->~IsClosable(); is always safe.
If you want to remove the menu, the suggested method is clonk->TryCancelMenu() to handle unclosable menus correctly.
*/
func GetMenu()
{
// No new-style menu set? Return the classic menu ID. This is deprecated and should be removed in some future.
// This function must return a proplist, but clashes with the engine-defined "GetMenu".
// This workaround here at least allows developers to reach the Clonk's menu ID.
if (this.control.menu == nil)
{
var menu_id = inherited(...);
if (menu_id) return {ID = menu_id};
}
return this.control.menu;
}
// Returns true when an existing menu was closed
func CancelMenu()
{
if (this.control.menu)
{
SetMenu(nil);
return true;
}
return false;
}
// Tries to cancel a non-unclosable menu. Returns true when there is no menu left after this call (even if there never was one).
func TryCancelMenu()
{
if (!this.control.menu) return true;
if (this.control.menu->~Unclosable()) return false;
CancelMenu();
return true;
}
public func RejectShiftCursor()
{
if (this.control.menu && this.control.menu->~Unclosable()) return true;
return _inherited(...);
}
public func OnShiftCursor()
{
TryCancelMenu();
return _inherited(...);
}