forked from Mirrors/openclonk
Control: Extract library for using menus
External packs use this functionality without using the whole default Clonk control.ipv6
parent
39c54c323c
commit
fa28a7a643
|
@ -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
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
[DefCore]
|
||||
id=Library_ClonkMenuControl
|
||||
Version=8,0
|
||||
Category=C4D_StaticBack
|
||||
HideInCreator=true
|
|
@ -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(...);
|
||||
}
|
||||
|
Loading…
Reference in New Issue