2009-05-25 09:01:26 +00:00
/*
* OpenClonk , http : //www.openclonk.org
*
2013-12-17 20:01:09 +00:00
* Copyright ( c ) 2005 - 2009 , RedWolf Design GmbH , http : //www.clonk.de/
2016-04-03 18:18:29 +00:00
* Copyright ( c ) 2009 - 2016 , The OpenClonk Team and contributors
2009-05-25 09:01:26 +00:00
*
2013-12-17 20:01:09 +00:00
* Distributed under the terms of the ISC license ; see accompanying file
* " COPYING " for details .
2009-05-25 09:01:26 +00:00
*
2013-12-17 20:01:09 +00:00
* " Clonk " is a registered trademark of Matthes Bender , used with permission .
* See accompanying file " TRADEMARK " for details .
2009-05-25 09:01:26 +00:00
*
2013-12-17 20:01:09 +00:00
* To redistribute this file separately , substitute the full license texts
* for the above references .
2009-05-25 09:01:26 +00:00
*/
// Input to player control mapping
2009-12-17 13:06:24 +00:00
# include "C4Include.h"
2016-10-22 11:19:22 +00:00
# include "C4ForbidLibraryCompilation.h"
2016-04-03 18:07:56 +00:00
# include "control/C4PlayerControl.h"
2009-05-25 09:01:26 +00:00
2016-04-03 18:07:56 +00:00
# include "object/C4DefList.h"
# include "c4group/C4LangStringTable.h"
# include "player/C4Player.h"
# include "player/C4PlayerList.h"
# include "control/C4Control.h"
# include "game/C4Game.h"
# include "platform/C4GamePadCon.h"
# include "lib/C4Log.h"
# include "graphics/C4GraphicsResource.h"
# include "gui/C4MouseControl.h"
# include "game/C4GraphicsSystem.h"
# include "game/C4Viewport.h"
# include "object/C4Object.h"
# include "object/C4ObjectMenu.h"
2016-04-02 17:44:44 +00:00
# include "script/C4Aul.h"
2009-12-17 13:06:24 +00:00
# include <algorithm>
2009-05-25 09:01:26 +00:00
2016-04-03 18:07:56 +00:00
# include "control/C4Record.h"
2011-12-28 23:12:21 +00:00
2009-05-25 09:01:26 +00:00
/* C4PlayerControlDef */
void C4PlayerControlDef : : CompileFunc ( StdCompiler * pComp )
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
if ( ! pComp - > Name ( " ControlDef " ) ) { pComp - > NameEnd ( ) ; pComp - > excNotFound ( " ControlDef " ) ; }
2009-05-25 09:01:26 +00:00
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( sIdentifier , StdCompiler : : RCT_Idtf ) , " Identifier " , " None " ) ) ;
2011-03-31 14:26:59 +00:00
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( sGUIName , StdCompiler : : RCT_All ) , " GUIName " , " " ) ) ;
2009-05-25 09:01:26 +00:00
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( sGUIDesc , StdCompiler : : RCT_All ) , " GUIDesc " , " " ) ) ;
2009-05-26 06:10:38 +00:00
pComp - > Value ( mkNamingAdapt ( fGlobal , " Global " , false ) ) ;
pComp - > Value ( mkNamingAdapt ( fIsHoldKey , " Hold " , false ) ) ;
pComp - > Value ( mkNamingAdapt ( iRepeatDelay , " RepeatDelay " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( iInitialRepeatDelay , " InitialRepeatDelay " , 0 ) ) ;
2009-05-25 09:01:26 +00:00
pComp - > Value ( mkNamingAdapt ( fDefaultDisabled , " DefaultDisabled " , false ) ) ;
2010-01-25 15:57:57 +00:00
pComp - > Value ( mkNamingAdapt ( idControlExtraData , " ExtraData " , C4ID : : None ) ) ;
2011-11-20 20:49:38 +00:00
const StdEnumEntry < CoordinateSpace > CoordSpaceNames [ ] =
{
{ " Game " , COS_Game } ,
{ " Viewport " , COS_Viewport } ,
2016-11-02 23:58:02 +00:00
{ nullptr , COS_Game }
2011-11-20 20:49:38 +00:00
} ;
pComp - > Value ( mkNamingAdapt ( mkEnumAdapt < CoordinateSpace , int32_t > ( eCoordSpace , CoordSpaceNames ) , " CoordinateSpace " , COS_Game ) ) ;
2010-04-10 20:44:00 +00:00
pComp - > Value ( mkNamingAdapt ( fSendCursorPos , " SendCursorPos " , false ) ) ;
2010-03-28 18:58:01 +00:00
const StdEnumEntry < Actions > ActionNames [ ] =
{
2009-05-26 06:10:38 +00:00
{ " None " , CDA_None } ,
{ " Script " , CDA_Script } ,
{ " Menu " , CDA_Menu } ,
{ " MenuOK " , CDA_MenuOK } ,
{ " MenuCancel " , CDA_MenuCancel } ,
{ " MenuLeft " , CDA_MenuLeft } ,
{ " MenuUp " , CDA_MenuUp } ,
{ " MenuRight " , CDA_MenuRight } ,
{ " MenuDown " , CDA_MenuDown } ,
2014-08-10 18:58:26 +00:00
{ " ObjectMenuTextComplete " , CDA_ObjectMenuTextComplete } ,
2010-02-08 20:52:46 +00:00
{ " ObjectMenuOK " , CDA_ObjectMenuOK } ,
{ " ObjectMenuOKAll " , CDA_ObjectMenuOKAll } ,
{ " ObjectMenuSelect " , CDA_ObjectMenuSelect } ,
{ " ObjectMenuCancel " , CDA_ObjectMenuCancel } ,
{ " ObjectMenuLeft " , CDA_ObjectMenuLeft } ,
{ " ObjectMenuUp " , CDA_ObjectMenuUp } ,
{ " ObjectMenuRight " , CDA_ObjectMenuRight } ,
{ " ObjectMenuDown " , CDA_ObjectMenuDown } ,
2009-12-31 17:20:45 +00:00
{ " ZoomIn " , CDA_ZoomIn } ,
{ " ZoomOut " , CDA_ZoomOut } ,
2016-11-02 23:58:02 +00:00
{ nullptr , CDA_None }
2010-03-28 18:58:01 +00:00
} ;
2009-05-26 06:10:38 +00:00
pComp - > Value ( mkNamingAdapt ( mkEnumAdapt < Actions , int32_t > ( eAction , ActionNames ) , " Action " , CDA_Script ) ) ;
2009-05-25 09:01:26 +00:00
pComp - > NameEnd ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-25 09:01:26 +00:00
bool C4PlayerControlDef : : operator = = ( const C4PlayerControlDef & cmp ) const
2010-03-28 18:58:01 +00:00
{
2009-05-25 09:01:26 +00:00
return sIdentifier = = cmp . sIdentifier
2010-03-28 18:58:01 +00:00
& & sGUIName = = cmp . sGUIName
& & sGUIDesc = = cmp . sGUIDesc
& & fGlobal = = cmp . fGlobal
& & fIsHoldKey = = cmp . fIsHoldKey
& & iRepeatDelay = = cmp . iRepeatDelay
& & iInitialRepeatDelay = = cmp . iInitialRepeatDelay
& & fDefaultDisabled = = cmp . fDefaultDisabled
& & idControlExtraData = = cmp . idControlExtraData
2010-04-10 20:44:00 +00:00
& & fSendCursorPos = = cmp . fSendCursorPos
2010-03-28 18:58:01 +00:00
& & eAction = = cmp . eAction ;
}
2009-05-25 09:01:26 +00:00
/* C4PlayerControlDefs */
2009-05-28 00:14:54 +00:00
void C4PlayerControlDefs : : UpdateInternalCons ( )
2010-03-28 18:58:01 +00:00
{
2010-02-08 20:52:46 +00:00
InternalCons . CON_ObjectMenuSelect = GetControlIndexByIdentifier ( " ObjectMenuSelect " ) ;
InternalCons . CON_ObjectMenuOK = GetControlIndexByIdentifier ( " ObjectMenuOK " ) ;
InternalCons . CON_ObjectMenuOKAll = GetControlIndexByIdentifier ( " ObjectMenuOKAll " ) ;
InternalCons . CON_ObjectMenuCancel = GetControlIndexByIdentifier ( " ObjectMenuCancel " ) ;
2010-05-07 13:57:56 +00:00
InternalCons . CON_CursorPos = GetControlIndexByIdentifier ( " CursorPos " ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-28 00:14:54 +00:00
2009-05-26 06:10:38 +00:00
void C4PlayerControlDefs : : Clear ( )
2010-03-28 18:58:01 +00:00
{
2011-07-16 09:58:38 +00:00
clear_previous = false ;
2009-05-26 06:10:38 +00:00
Defs . clear ( ) ;
2009-05-28 00:14:54 +00:00
UpdateInternalCons ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
2009-05-25 09:01:26 +00:00
void C4PlayerControlDefs : : CompileFunc ( StdCompiler * pComp )
2010-03-28 18:58:01 +00:00
{
2011-07-16 09:58:38 +00:00
pComp - > Value ( mkNamingAdapt ( clear_previous , " ClearPrevious " , false ) ) ;
pComp - > Value ( mkSTLContainerAdapt ( Defs , StdCompiler : : SEP_NONE ) ) ;
2017-03-11 14:05:41 +00:00
if ( pComp - > isDeserializer ( ) ) UpdateInternalCons ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-25 09:01:26 +00:00
void C4PlayerControlDefs : : MergeFrom ( const C4PlayerControlDefs & Src )
2010-03-28 18:58:01 +00:00
{
2011-07-16 09:58:38 +00:00
// Clear previous defs if specified in merge set
if ( Src . clear_previous ) Defs . clear ( ) ;
2009-05-25 09:01:26 +00:00
// copy all defs from source file; overwrite defs of same name if found
for ( DefVecImpl : : const_iterator i = Src . Defs . begin ( ) ; i ! = Src . Defs . end ( ) ; + + i )
2010-03-28 18:58:01 +00:00
{
2009-05-25 09:01:26 +00:00
const C4PlayerControlDef & SrcDef = * i ;
// overwrite if def of same name existed
int32_t iPrevIdx = GetControlIndexByIdentifier ( SrcDef . GetIdentifier ( ) ) ;
if ( iPrevIdx ! = CON_None )
2010-03-28 18:58:01 +00:00
{
2009-05-25 09:01:26 +00:00
Defs [ iPrevIdx ] = SrcDef ;
2010-03-28 18:58:01 +00:00
}
2009-05-25 09:01:26 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-25 09:01:26 +00:00
// new def: Append a copy
Defs . push_back ( SrcDef ) ;
}
}
2010-03-28 18:58:01 +00:00
UpdateInternalCons ( ) ;
}
2009-05-25 09:01:26 +00:00
2009-05-28 00:14:54 +00:00
const C4PlayerControlDef * C4PlayerControlDefs : : GetControlByIndex ( int32_t idx ) const
2010-03-28 18:58:01 +00:00
{
2009-05-25 09:01:26 +00:00
// safe index
2016-11-02 23:58:02 +00:00
if ( idx < 0 | | idx > = int32_t ( Defs . size ( ) ) ) return nullptr ;
2009-05-25 09:01:26 +00:00
return & ( Defs [ idx ] ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-25 09:01:26 +00:00
int32_t C4PlayerControlDefs : : GetControlIndexByIdentifier ( const char * szIdentifier ) const
2010-03-28 18:58:01 +00:00
{
2009-05-25 09:01:26 +00:00
for ( DefVecImpl : : const_iterator i = Defs . begin ( ) ; i ! = Defs . end ( ) ; + + i )
if ( SEqual ( ( * i ) . GetIdentifier ( ) , szIdentifier ) )
return i - Defs . begin ( ) ;
return CON_None ;
2010-03-28 18:58:01 +00:00
}
2009-05-25 09:01:26 +00:00
2009-06-09 23:29:16 +00:00
void C4PlayerControlDefs : : FinalInit ( )
2010-03-28 18:58:01 +00:00
{
2009-06-09 23:29:16 +00:00
// Assume all defs have been loaded
// Register scritp constants
for ( DefVecImpl : : const_iterator i = Defs . begin ( ) ; i ! = Defs . end ( ) ; + + i )
2010-03-28 18:58:01 +00:00
{
2009-06-09 23:29:16 +00:00
const char * szIdtf = ( * i ) . GetIdentifier ( ) ;
if ( szIdtf & & * szIdtf & & ! SEqual ( szIdtf , " None " ) )
2010-03-28 18:58:01 +00:00
{
2009-08-10 14:48:25 +00:00
: : ScriptEngine . RegisterGlobalConstant ( FormatString ( " CON_%s " , szIdtf ) . getData ( ) , C4VInt ( i - Defs . begin ( ) ) ) ;
2009-06-09 23:29:16 +00:00
}
}
2010-03-28 18:58:01 +00:00
}
2009-06-09 23:29:16 +00:00
2009-05-25 09:01:26 +00:00
/* C4PlayerControlAssignment */
void C4PlayerControlAssignment : : KeyComboItem : : CompileFunc ( StdCompiler * pComp )
2010-03-27 18:08:01 +00:00
{
2009-05-25 09:01:26 +00:00
// if key is compiled, also store as a string into KeyName for later resolving
2017-03-11 14:05:41 +00:00
if ( pComp - > isDeserializer ( ) )
2010-03-27 18:08:01 +00:00
{
Key . dwShift = 0 ;
2009-05-25 09:01:26 +00:00
sKeyName . Clear ( ) ;
pComp - > Value ( mkParAdapt ( Key , & sKeyName ) ) ;
2010-03-27 18:08:01 +00:00
}
else
{
2011-03-30 20:11:47 +00:00
// decompiler: If there's a stored key name, just write it. Regardless of whether it's a key, undefined or a reference
2012-10-08 22:54:34 +00:00
// If no key name is stored, it was probably assigned at runtime and sKeyName needs to be recreated
2011-03-30 20:11:47 +00:00
if ( ! sKeyName ) UpdateKeyName ( ) ;
2009-05-25 09:01:26 +00:00
pComp - > Value ( mkParAdapt ( sKeyName , StdCompiler : : RCT_Idtf ) ) ;
}
2010-03-27 18:08:01 +00:00
}
2009-05-25 09:01:26 +00:00
2011-03-30 20:11:47 +00:00
void C4PlayerControlAssignment : : KeyComboItem : : UpdateKeyName ( )
{
// update key name from key
sKeyName . Copy ( Key . ToString ( false , false ) ) ;
2012-10-08 22:54:34 +00:00
if ( Key . dwShift )
sKeyName . Take ( FormatString ( " %s+%s " , C4KeyCodeEx : : KeyShift2String ( ( C4KeyShiftState ) Key . dwShift ) . getData ( ) , sKeyName . getData ( ) ) ) ;
2011-03-30 20:11:47 +00:00
}
2009-05-25 09:01:26 +00:00
void C4PlayerControlAssignment : : CompileFunc ( StdCompiler * pComp )
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
if ( ! pComp - > Name ( " Assignment " ) ) { pComp - > NameEnd ( ) ; pComp - > excNotFound ( " Assignment " ) ; }
2009-05-25 09:01:26 +00:00
pComp - > Value ( mkNamingAdapt ( mkSTLContainerAdapt ( KeyCombo ) , " Key " , KeyComboVec ( ) ) ) ;
2009-06-09 23:29:16 +00:00
pComp - > Value ( mkNamingAdapt ( fComboIsSequence , " ComboIsSequence " , false ) ) ;
2009-05-25 09:01:26 +00:00
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( sControlName , StdCompiler : : RCT_Idtf ) , " Control " , " None " ) ) ;
2012-10-08 20:29:15 +00:00
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( sGUIName , StdCompiler : : RCT_All ) , " GUIName " , " " ) ) ;
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( sGUIDesc , StdCompiler : : RCT_All ) , " GUIDesc " , " " ) ) ;
2012-10-09 21:59:32 +00:00
pComp - > Value ( mkNamingAdapt ( iGUIGroup , " GUIGroup " , 0 ) ) ;
2012-10-09 20:24:48 +00:00
pComp - > Value ( mkNamingAdapt ( fGUIDisabled , " GUIDisabled " , false ) ) ;
2009-05-26 06:10:38 +00:00
pComp - > Value ( mkNamingAdapt ( iPriority , " Priority " , 0 ) ) ;
2010-03-28 18:58:01 +00:00
const StdBitfieldEntry < int32_t > TriggerModeNames [ ] =
{
2009-05-25 09:01:26 +00:00
{ " Default " , CTM_Default } ,
{ " Hold " , CTM_Hold } ,
{ " Release " , CTM_Release } ,
2009-05-26 06:10:38 +00:00
{ " AlwaysUnhandled " , CTM_AlwaysUnhandled } ,
2013-09-02 15:27:15 +00:00
{ " ClearRecentKeys " , CTM_ClearRecentKeys } ,
2016-11-02 23:58:02 +00:00
{ nullptr , 0 }
2010-03-28 18:58:01 +00:00
} ;
2009-05-26 06:10:38 +00:00
pComp - > Value ( mkNamingAdapt ( mkBitfieldAdapt < int32_t > ( iTriggerMode , TriggerModeNames ) , " TriggerMode " , CTM_Default ) ) ;
2010-03-27 18:08:01 +00:00
pComp - > Value ( mkNamingAdapt ( fOverrideAssignments , " OverrideAssignments " , false ) ) ;
2009-05-25 09:01:26 +00:00
pComp - > NameEnd ( ) ;
2009-05-26 06:10:38 +00:00
// newly loaded structures are not resolved
2017-03-11 14:05:41 +00:00
if ( pComp - > isDeserializer ( ) ) fRefsResolved = false ;
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
2011-03-30 20:11:47 +00:00
void C4PlayerControlAssignment : : ResetKeyToInherited ( )
{
if ( inherited_assignment ) CopyKeyFrom ( * inherited_assignment ) ;
}
2011-03-31 14:26:59 +00:00
bool C4PlayerControlAssignment : : IsKeyChanged ( ) const
{
// no inherited assignment? Then the key is always custom
if ( ! inherited_assignment ) return true ;
// otherwise, compare
return KeyCombo ! = inherited_assignment - > KeyCombo | | fComboIsSequence ! = inherited_assignment - > fComboIsSequence ;
}
2011-03-30 20:11:47 +00:00
void C4PlayerControlAssignment : : SetKey ( const C4KeyCodeEx & key )
{
// set as one-key-combo
KeyCombo . resize ( 1 ) ;
KeyCombo [ 0 ] . Key = key ;
KeyCombo [ 0 ] . Key . fRepeated = false ;
KeyCombo [ 0 ] . sKeyName . Clear ( ) ;
fComboIsSequence = false ;
TriggerKey = key ;
}
void C4PlayerControlAssignment : : CopyKeyFrom ( const C4PlayerControlAssignment & src_assignment )
{
// just copy key settings; keep control and priorities
KeyCombo = src_assignment . KeyCombo ;
TriggerKey = src_assignment . TriggerKey ;
fComboIsSequence = src_assignment . fComboIsSequence ;
if ( ! src_assignment . fRefsResolved ) fRefsResolved = false ;
}
2009-05-26 06:10:38 +00:00
bool C4PlayerControlAssignment : : ResolveRefs ( C4PlayerControlAssignmentSet * pParentSet , C4PlayerControlDefs * pControlDefs )
2010-03-27 18:08:01 +00:00
{
2009-05-26 06:10:38 +00:00
// avoid circular chains
2012-10-08 18:45:04 +00:00
static int32_t recursion_check = 0 ;
if ( recursion_check > 10 )
2010-03-27 18:08:01 +00:00
{
2012-10-08 18:45:04 +00:00
LogFatal ( FormatString ( " Maximum recursion limit reached while resolving player control assignments of set %s in assignment for key %s. This is probably due to a circular control chain. " , pParentSet - > GetName ( ) , GetControlName ( ) ) . getData ( ) ) ;
2009-05-26 06:10:38 +00:00
return false ;
2010-03-27 18:08:01 +00:00
}
2012-10-08 18:45:04 +00:00
+ + recursion_check ;
2009-05-26 06:10:38 +00:00
// resolve control name
iControl = pControlDefs - > GetControlIndexByIdentifier ( sControlName . getData ( ) ) ;
// resolve keys
KeyComboVec NewCombo ;
for ( KeyComboVec : : iterator i = KeyCombo . begin ( ) ; i ! = KeyCombo . end ( ) ; + + i )
2010-03-27 18:08:01 +00:00
{
2009-05-26 06:10:38 +00:00
KeyComboItem & rKeyComboItem = * i ;
2012-10-08 18:45:04 +00:00
const char * szKeyName = rKeyComboItem . sKeyName . getData ( ) ;
// check if this is a key reference. A key reference must be preceded by CON_
// it may also be preceded by modifiers (Shift+), which are already set in rKeyComboItem.Key.dwShift
bool is_key_reference = false ;
int last_shift_delim_pos ;
if ( szKeyName & & * szKeyName )
2010-03-27 18:08:01 +00:00
{
if ( ( last_shift_delim_pos = SCharLastPos ( ' + ' , szKeyName ) ) > - 1 ) szKeyName + = last_shift_delim_pos + 1 ;
2012-10-08 18:45:04 +00:00
if ( SEqual2 ( szKeyName , " CON_ " ) )
{
is_key_reference = true ;
szKeyName + = 4 ;
}
}
if ( is_key_reference )
{
// this is a key reference
// - find referenced target assignment
2009-06-16 02:48:53 +00:00
C4PlayerControlAssignment * pRefAssignment = pParentSet - > GetAssignmentByControlName ( szKeyName ) ;
2009-05-26 06:10:38 +00:00
if ( pRefAssignment )
2010-03-27 18:08:01 +00:00
{
2009-05-26 06:10:38 +00:00
// resolve itself if necessary
2012-10-08 18:45:04 +00:00
if ( ! pRefAssignment - > IsRefsResolved ( ) ) if ( ! pRefAssignment - > ResolveRefs ( pParentSet , pControlDefs ) ) { - - recursion_check ; return false ; }
2009-05-26 06:10:38 +00:00
// insert all keys of that combo into own combo
2010-03-27 18:08:01 +00:00
// add any extra shift states from reference
DWORD ref_shift = rKeyComboItem . Key . dwShift ;
if ( ref_shift )
{
for ( KeyComboVec : : iterator j = pRefAssignment - > KeyCombo . begin ( ) ; j ! = pRefAssignment - > KeyCombo . end ( ) ; + + j )
{
KeyComboItem assignment_combo_item = * j ;
assignment_combo_item . Key . dwShift | = ref_shift ;
NewCombo . push_back ( assignment_combo_item ) ;
}
2009-05-26 06:10:38 +00:00
}
2010-03-27 18:08:01 +00:00
else
2009-05-26 06:10:38 +00:00
{
2010-03-27 18:08:01 +00:00
NewCombo . insert ( NewCombo . end ( ) , pRefAssignment - > KeyCombo . begin ( ) , pRefAssignment - > KeyCombo . end ( ) ) ;
}
}
else
{
2009-05-26 06:10:38 +00:00
// undefined reference? Not fatal, but inform user
LogF ( " WARNING: Control %s of set %s contains reference to unassigned control %s. " , GetControlName ( ) , pParentSet - > GetName ( ) , rKeyComboItem . sKeyName . getData ( ) ) ;
NewCombo . clear ( ) ;
}
2010-03-27 18:08:01 +00:00
}
2009-05-26 06:10:38 +00:00
else
2010-03-27 18:08:01 +00:00
{
2012-10-08 18:45:04 +00:00
// non-reference: check if the assignment was valid
2012-11-30 21:16:59 +00:00
# ifndef USE_CONSOLE
2012-10-08 18:45:04 +00:00
if ( rKeyComboItem . Key = = KEY_Default )
2012-11-30 21:16:59 +00:00
LogF ( " WARNING: Control %s of set %s contains undefined key \" %s \" . " , GetControlName ( ) , pParentSet - > GetName ( ) , szKeyName ) ;
# endif
2012-10-08 18:45:04 +00:00
// ...and just keep this item.
2009-05-26 06:10:38 +00:00
NewCombo . push_back ( rKeyComboItem ) ;
}
2010-03-27 18:08:01 +00:00
}
2009-05-26 06:10:38 +00:00
KeyCombo = NewCombo ;
2015-09-01 23:10:34 +00:00
// adjust Control and Shift into key states for non-sequence combo keys
// e.g. LeftControl,A should become LeftControl,Ctrl+A.
if ( KeyCombo . size ( ) > 1 & & ! fComboIsSequence )
{
int32_t shift = 0 ;
for ( KeyComboVec : : iterator i = KeyCombo . begin ( ) ; i ! = KeyCombo . end ( ) ; + + i )
{
if ( i - > Key . Key = = K_CONTROL_L | | i - > Key . Key = = K_CONTROL_R ) shift | = KEYS_Control ;
if ( i - > Key . Key = = K_SHIFT_L | | i - > Key . Key = = K_SHIFT_R ) shift | = KEYS_Shift ;
shift | = i - > Key . dwShift ;
}
for ( KeyComboVec : : iterator i = KeyCombo . begin ( ) ; i ! = KeyCombo . end ( ) ; + + i ) i - > Key . dwShift | = shift ;
}
// remove control/shift duplications
for ( KeyComboVec : : iterator i = KeyCombo . begin ( ) ; i ! = KeyCombo . end ( ) ; + + i ) i - > Key . FixShiftKeys ( ) ;
2009-05-26 06:10:38 +00:00
// the trigger key is always last of the chain
if ( KeyCombo . size ( ) ) TriggerKey = KeyCombo . back ( ) . Key ; else TriggerKey = C4KeyCodeEx ( ) ;
// done
fRefsResolved = true ;
2012-10-08 18:45:04 +00:00
- - recursion_check ;
2009-05-26 06:10:38 +00:00
return true ;
2010-03-27 18:08:01 +00:00
}
2009-05-25 09:01:26 +00:00
2009-05-28 00:14:54 +00:00
bool C4PlayerControlAssignment : : IsComboMatched ( const C4PlayerControlRecentKeyList & DownKeys , const C4PlayerControlRecentKeyList & RecentKeys ) const
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
assert ( HasCombo ( ) ) ;
// check if combo is currently fulfilled (assuming TriggerKey is already matched)
if ( fComboIsSequence )
2010-03-28 18:58:01 +00:00
{
2013-12-07 14:27:01 +00:00
C4TimeMilliseconds tKeyLast = C4TimeMilliseconds : : Now ( ) ;
2009-05-28 00:14:54 +00:00
// combo is a sequence: The last keys of RecentKeys must match the sequence
// the last ComboKey is the TriggerKey, which is omitted because it has already been matched and is not to be found in RecentKeys yet
2012-10-28 16:47:02 +00:00
KeyComboVec : : const_reverse_iterator i = KeyCombo . rbegin ( ) + 1 ;
for ( C4PlayerControlRecentKeyList : : const_reverse_iterator ri = RecentKeys . rbegin ( ) ; i ! = KeyCombo . rend ( ) ; + + ri )
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
// no more keys pressed but combo didn't end? -> no combo match
if ( ri = = RecentKeys . rend ( ) ) return false ;
const C4PlayerControlRecentKey & rk = * ri ;
// user waited for too long?
2013-12-04 12:35:07 +00:00
C4TimeMilliseconds tKeyRecent = rk . tTime ;
2009-05-28 00:14:54 +00:00
if ( tKeyLast - tKeyRecent > C4PlayerControl : : MaxSequenceKeyDelay ) return false ;
// key doesn't match?
const KeyComboItem & k = * i ;
2012-10-28 16:47:02 +00:00
if ( ! ( rk . matched_key = = k . Key ) )
{
// mouse movement commands do not break sequences
if ( Key_IsMouse ( rk . matched_key . Key ) & & Key_GetMouseEvent ( rk . matched_key . Key ) = = KEY_MOUSE_Move ) continue ;
return false ;
}
2009-05-28 00:14:54 +00:00
// key OK
2012-10-28 16:47:02 +00:00
+ + i ;
2009-05-28 00:14:54 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-28 00:14:54 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
// combo requires keys to be down simultanuously: check that all keys of the combo are in the down-list
for ( KeyComboVec : : const_iterator i = KeyCombo . begin ( ) ; i ! = KeyCombo . end ( ) ; + + i )
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
const KeyComboItem & k = * i ;
bool fFound = false ;
for ( C4PlayerControlRecentKeyList : : const_iterator di = DownKeys . begin ( ) ; di ! = DownKeys . end ( ) ; + + di )
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
const C4PlayerControlRecentKey & dk = * di ;
2009-10-16 14:23:17 +00:00
if ( dk . matched_key = = k . Key ) { fFound = true ; break ; }
2009-05-28 00:14:54 +00:00
}
2010-03-28 18:58:01 +00:00
if ( ! fFound ) return false ;
2009-05-28 00:14:54 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-28 00:14:54 +00:00
// combo OK!
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-28 00:14:54 +00:00
2009-05-25 09:01:26 +00:00
bool C4PlayerControlAssignment : : operator = = ( const C4PlayerControlAssignment & cmp ) const
2010-03-28 18:58:01 +00:00
{
2009-05-25 09:01:26 +00:00
// doesn't compare resolved TriggerKey/iControl
return KeyCombo = = cmp . KeyCombo
2010-03-28 18:58:01 +00:00
& & sControlName = = cmp . sControlName
2012-10-08 20:29:15 +00:00
& & sGUIName = = cmp . sGUIName
& & sGUIDesc = = cmp . sGUIDesc
2012-10-09 20:24:48 +00:00
& & fGUIDisabled = = cmp . fGUIDisabled
2010-03-28 18:58:01 +00:00
& & iTriggerMode = = cmp . iTriggerMode
& & iPriority = = cmp . iPriority ;
}
2009-05-25 09:01:26 +00:00
2012-04-17 15:06:16 +00:00
StdStrBuf C4PlayerControlAssignment : : GetKeysAsString ( bool human_readable , bool short_name ) const
{
// create a short, human-readable string of the assigned key
// to be displayed e.g. in tutorial messages explaining controls
StdStrBuf result ;
if ( ! KeyCombo . size ( ) ) return result ;
// trigger key
KeyComboVec : : const_iterator i = KeyCombo . begin ( ) ;
result . Take ( i - > Key . ToString ( human_readable , short_name ) ) ;
// extra keys of combo
while ( + + i ! = KeyCombo . end ( ) )
{
result . AppendChar ( fComboIsSequence ? ' , ' : ' + ' ) ;
result . Append ( i - > Key . ToString ( human_readable , short_name ) ) ;
}
return result ;
}
2012-10-08 20:29:15 +00:00
const char * C4PlayerControlAssignment : : GetGUIName ( const C4PlayerControlDefs & defs ) const
{
// local name?
if ( sGUIName . getLength ( ) )
{
// special: None defaults to empty name
if ( sGUIName = = " None " ) return " " ;
return sGUIName . getData ( ) ;
}
// otherwise, fall back to def
const C4PlayerControlDef * def = defs . GetControlByIndex ( GetControl ( ) ) ;
if ( def ) return def - > GetGUIName ( ) ;
// no def and no name...
2016-11-02 23:58:02 +00:00
return nullptr ;
2012-10-08 20:29:15 +00:00
}
const char * C4PlayerControlAssignment : : GetGUIDesc ( const C4PlayerControlDefs & defs ) const
{
// local desc?
if ( sGUIDesc . getLength ( ) ) return sGUIDesc . getData ( ) ;
// otherwise, fall back to def
const C4PlayerControlDef * def = defs . GetControlByIndex ( GetControl ( ) ) ;
if ( def ) return def - > GetGUIDesc ( ) ;
// no def and no desc...
2016-11-02 23:58:02 +00:00
return nullptr ;
2012-10-08 20:29:15 +00:00
}
2012-10-09 20:24:48 +00:00
bool C4PlayerControlAssignment : : IsGUIDisabled ( ) const
{
return fGUIDisabled ;
}
2009-05-25 09:01:26 +00:00
2012-10-09 21:59:32 +00:00
int32_t C4PlayerControlAssignment : : GetGUIGroup ( ) const
{
return iGUIGroup ;
}
2009-05-25 09:01:26 +00:00
/* C4PlayerControlAssignmentSet */
2011-03-30 20:11:47 +00:00
void C4PlayerControlAssignmentSet : : InitEmptyFromTemplate ( const C4PlayerControlAssignmentSet & template_set )
{
// copy all fields except assignments
sName . Copy ( template_set . sName ) ;
sGUIName . Copy ( template_set . sGUIName ) ;
sParentSetName . Copy ( template_set . sParentSetName ) ;
has_keyboard = template_set . has_keyboard ;
has_mouse = template_set . has_mouse ;
has_gamepad = template_set . has_gamepad ;
}
2009-05-25 09:01:26 +00:00
void C4PlayerControlAssignmentSet : : CompileFunc ( StdCompiler * pComp )
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
if ( ! pComp - > Name ( " ControlSet " ) ) { pComp - > NameEnd ( ) ; pComp - > excNotFound ( " ControlSet " ) ; }
2009-06-16 00:38:39 +00:00
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( sName , StdCompiler : : RCT_All ) , " Name " , " None " ) ) ; // can't do RCT_Idtf because of wildcards
2011-03-30 20:11:47 +00:00
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( sGUIName , StdCompiler : : RCT_All ) , " GUIName " , " undefined " ) ) ;
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( sParentSetName , StdCompiler : : RCT_Idtf ) , " Parent " , " " ) ) ;
2009-10-14 21:29:27 +00:00
pComp - > Value ( mkNamingAdapt ( has_keyboard , " Keyboard " , true ) ) ;
pComp - > Value ( mkNamingAdapt ( has_mouse , " Mouse " , true ) ) ;
pComp - > Value ( mkNamingAdapt ( has_gamepad , " Gamepad " , false ) ) ;
2009-05-26 06:10:38 +00:00
pComp - > Value ( mkSTLContainerAdapt ( Assignments , StdCompiler : : SEP_NONE ) ) ;
2009-05-25 09:01:26 +00:00
pComp - > NameEnd ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-25 09:01:26 +00:00
2012-10-09 21:59:32 +00:00
2011-03-30 20:11:47 +00:00
void C4PlayerControlAssignmentSet : : MergeFrom ( const C4PlayerControlAssignmentSet & Src , MergeMode merge_mode )
2010-03-27 18:08:01 +00:00
{
2009-05-25 09:01:26 +00:00
// take over all assignments defined in Src
2009-05-26 06:10:38 +00:00
for ( C4PlayerControlAssignmentVec : : const_iterator i = Src . Assignments . begin ( ) ; i ! = Src . Assignments . end ( ) ; + + i )
2010-03-27 18:08:01 +00:00
{
2009-05-25 09:01:26 +00:00
const C4PlayerControlAssignment & SrcAssignment = * i ;
2010-03-27 18:08:01 +00:00
bool fIsReleaseKey = ! ! ( SrcAssignment . GetTriggerMode ( ) & C4PlayerControlAssignment : : CTM_Release ) ;
// overwrite same def and release key state
2011-03-30 20:11:47 +00:00
if ( merge_mode ! = MM_LowPrio & & SrcAssignment . IsOverrideAssignments ( ) )
2010-03-27 18:08:01 +00:00
{
// high priority override control clears all previous (very inefficient method...might as well recreate the whole list)
bool any_remaining = true ;
while ( any_remaining )
2009-05-25 09:01:26 +00:00
{
2010-03-27 18:08:01 +00:00
any_remaining = false ;
for ( C4PlayerControlAssignmentVec : : iterator j = Assignments . begin ( ) ; j ! = Assignments . end ( ) ; + + j )
if ( SEqual ( ( * j ) . GetControlName ( ) , SrcAssignment . GetControlName ( ) ) )
{
bool fSelfIsReleaseKey = ! ! ( ( * j ) . GetTriggerMode ( ) & C4PlayerControlAssignment : : CTM_Release ) ;
if ( fSelfIsReleaseKey = = fIsReleaseKey )
{
Assignments . erase ( j ) ;
any_remaining = true ;
break ;
}
}
2009-05-25 09:01:26 +00:00
}
}
2011-03-30 20:11:47 +00:00
else if ( merge_mode = = MM_LowPrio | | merge_mode = = MM_ConfigOverload )
2010-03-27 18:08:01 +00:00
{
// if this is low priority, another override control kills this
bool any_override = false ;
for ( C4PlayerControlAssignmentVec : : iterator j = Assignments . begin ( ) ; j ! = Assignments . end ( ) ; + + j )
if ( SEqual ( ( * j ) . GetControlName ( ) , SrcAssignment . GetControlName ( ) ) )
{
bool fSelfIsReleaseKey = ! ! ( ( * j ) . GetTriggerMode ( ) & C4PlayerControlAssignment : : CTM_Release ) ;
if ( fSelfIsReleaseKey = = fIsReleaseKey )
{
any_override = true ;
2011-03-30 20:11:47 +00:00
// config overloads just change the key of the inherited assignment
if ( merge_mode = = MM_ConfigOverload )
{
( * j ) . CopyKeyFrom ( SrcAssignment ) ;
( * j ) . SetInherited ( false ) ;
}
2010-03-27 18:08:01 +00:00
break ;
}
}
if ( any_override ) continue ;
}
// new def: Append a copy
Assignments . push_back ( SrcAssignment ) ;
2011-03-30 20:11:47 +00:00
// inherited marker
if ( merge_mode = = MM_Inherit )
{
Assignments . back ( ) . SetInherited ( true ) ;
Assignments . back ( ) . SetInheritedAssignment ( & SrcAssignment ) ;
}
2009-05-25 09:01:26 +00:00
}
2010-03-27 18:08:01 +00:00
}
2009-05-25 09:01:26 +00:00
2011-03-30 20:11:47 +00:00
C4PlayerControlAssignment * C4PlayerControlAssignmentSet : : CreateAssignmentForControl ( const char * control_name )
{
Assignments . push_back ( C4PlayerControlAssignment ( ) ) ;
Assignments . back ( ) . SetControlName ( control_name ) ;
return & Assignments . back ( ) ;
}
void C4PlayerControlAssignmentSet : : RemoveAssignmentByControlName ( const char * control_name )
{
for ( C4PlayerControlAssignmentVec : : iterator i = Assignments . begin ( ) ; i ! = Assignments . end ( ) ; + + i )
if ( SEqual ( ( * i ) . GetControlName ( ) , control_name ) )
{
Assignments . erase ( i ) ;
return ;
}
}
2009-05-26 06:10:38 +00:00
bool C4PlayerControlAssignmentSet : : ResolveRefs ( C4PlayerControlDefs * pDefs )
2010-03-28 18:58:01 +00:00
{
2012-10-08 18:45:04 +00:00
// reset all resolved flags to allow re-resolve after overloads
for ( C4PlayerControlAssignmentVec : : iterator i = Assignments . begin ( ) ; i ! = Assignments . end ( ) ; + + i )
( * i ) . ResetRefsResolved ( ) ;
2009-05-26 06:10:38 +00:00
// resolve in order; ignore already resolved because they might have been resolved by cross reference
for ( C4PlayerControlAssignmentVec : : iterator i = Assignments . begin ( ) ; i ! = Assignments . end ( ) ; + + i )
if ( ! ( * i ) . IsRefsResolved ( ) )
if ( ! ( * i ) . ResolveRefs ( this , pDefs ) )
return false ;
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
2011-03-31 14:26:59 +00:00
void C4PlayerControlAssignmentSet : : SortAssignments ( )
{
// final init: sort assignments by priority
// note this screws up sorting for config dialog
std : : sort ( Assignments . begin ( ) , Assignments . end ( ) ) ;
}
2011-03-30 20:11:47 +00:00
C4PlayerControlAssignment * C4PlayerControlAssignmentSet : : GetAssignmentByIndex ( int32_t index )
{
2016-11-02 23:58:02 +00:00
if ( index < 0 | | index > = int32_t ( Assignments . size ( ) ) ) return nullptr ;
2011-03-30 20:11:47 +00:00
return & Assignments [ index ] ;
}
2009-05-26 06:10:38 +00:00
C4PlayerControlAssignment * C4PlayerControlAssignmentSet : : GetAssignmentByControlName ( const char * szControlName )
2009-10-14 16:46:22 +00:00
{
2009-05-26 06:10:38 +00:00
for ( C4PlayerControlAssignmentVec : : iterator i = Assignments . begin ( ) ; i ! = Assignments . end ( ) ; + + i )
2009-05-25 09:01:26 +00:00
if ( SEqual ( ( * i ) . GetControlName ( ) , szControlName ) )
2009-06-16 02:48:53 +00:00
// We don't like release keys... (2do)
if ( ! ( ( * i ) . GetTriggerMode ( ) & C4PlayerControlAssignment : : CTM_Release ) )
return & * i ;
2016-11-02 23:58:02 +00:00
return nullptr ;
2009-10-14 16:46:22 +00:00
}
2011-03-30 20:11:47 +00:00
C4PlayerControlAssignment * C4PlayerControlAssignmentSet : : GetAssignmentByControl ( int32_t control )
2009-10-14 16:46:22 +00:00
{
// TODO: Might want to stuff this into a vector indexed by control for faster lookup
for ( C4PlayerControlAssignmentVec : : iterator i = Assignments . begin ( ) ; i ! = Assignments . end ( ) ; + + i )
if ( ( * i ) . GetControl ( ) = = control )
// We don't like release keys... (2do)
if ( ! ( ( * i ) . GetTriggerMode ( ) & C4PlayerControlAssignment : : CTM_Release ) )
return & * i ;
2016-11-02 23:58:02 +00:00
return nullptr ;
2009-10-14 16:46:22 +00:00
}
2009-05-25 09:01:26 +00:00
2009-05-26 06:10:38 +00:00
bool C4PlayerControlAssignmentSet : : operator = = ( const C4PlayerControlAssignmentSet & cmp ) const
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
return Assignments = = cmp . Assignments
2010-03-28 18:58:01 +00:00
& & sName = = cmp . sName ;
}
2009-05-26 06:10:38 +00:00
2009-05-28 00:14:54 +00:00
void C4PlayerControlAssignmentSet : : GetAssignmentsByKey ( const C4PlayerControlDefs & rDefs , const C4KeyCodeEx & key , bool fHoldKeysOnly , C4PlayerControlAssignmentPVec * pOutVec , const C4PlayerControlRecentKeyList & DownKeys , const C4PlayerControlRecentKeyList & RecentKeys ) const
2010-02-18 17:43:38 +00:00
{
2009-05-28 00:14:54 +00:00
assert ( pOutVec ) ;
2010-02-18 17:43:38 +00:00
// primary match by TriggerKey (todo: Might use a hash map here if matching speed becomes an issue due to large control sets)
2009-05-28 00:14:54 +00:00
for ( C4PlayerControlAssignmentVec : : const_iterator i = Assignments . begin ( ) ; i ! = Assignments . end ( ) ; + + i )
2010-02-18 17:43:38 +00:00
{
2009-05-28 00:14:54 +00:00
const C4PlayerControlAssignment & rAssignment = * i ;
2010-02-18 17:43:38 +00:00
const C4KeyCodeEx & rAssignmentTriggerKey = rAssignment . GetTriggerKey ( ) ;
if ( ! ( rAssignmentTriggerKey . Key = = key . Key ) ) continue ;
// special: hold-keys-only ignore shift, because shift state might have been release during hold
if ( ! fHoldKeysOnly ) if ( rAssignmentTriggerKey . dwShift ! = key . dwShift ) continue ;
2009-05-28 00:14:54 +00:00
// check linked control def
const C4PlayerControlDef * pCtrl = rDefs . GetControlByIndex ( rAssignment . GetControl ( ) ) ;
if ( ! pCtrl ) continue ;
// only want hold keys?
if ( fHoldKeysOnly )
2010-02-18 17:43:38 +00:00
{
2009-05-28 00:14:54 +00:00
// a hold/release-trigger key is not a real hold key, even if the underlying control is
if ( ! pCtrl - > IsHoldKey ( ) | | ( rAssignment . GetTriggerMode ( ) & ( C4PlayerControlAssignment : : CTM_Hold | C4PlayerControlAssignment : : CTM_Release ) ) ) continue ;
2010-02-18 17:43:38 +00:00
}
2009-05-28 00:14:54 +00:00
else if ( rAssignment . HasCombo ( ) )
2010-02-18 17:43:38 +00:00
{
2009-05-28 00:14:54 +00:00
// hold-only events match the trigger key only (i.e., Release-events are generated as soon as the trigger key goes up)
// other events must match either the sequence or the down-key-combination
if ( ! rAssignment . IsComboMatched ( DownKeys , RecentKeys ) ) continue ;
2010-02-18 17:43:38 +00:00
}
2009-05-28 00:14:54 +00:00
// we got match! Store it
pOutVec - > push_back ( & rAssignment ) ;
}
2010-02-18 17:43:38 +00:00
}
2009-05-28 00:14:54 +00:00
void C4PlayerControlAssignmentSet : : GetTriggerKeys ( const C4PlayerControlDefs & rDefs , C4KeyCodeExVec * pRegularKeys , C4KeyCodeExVec * pHoldKeys ) const
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
// put all trigger keys of keyset into output vectors
// first all hold keys
for ( C4PlayerControlAssignmentVec : : const_iterator i = Assignments . begin ( ) ; i ! = Assignments . end ( ) ; + + i )
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
const C4PlayerControlAssignment & rAssignment = * i ;
const C4PlayerControlDef * pDef = rDefs . GetControlByIndex ( rAssignment . GetControl ( ) ) ;
if ( pDef & & pDef - > IsHoldKey ( ) )
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
const C4KeyCodeEx & rKey = rAssignment . GetTriggerKey ( ) ;
if ( std : : find ( pHoldKeys - > begin ( ) , pHoldKeys - > end ( ) , rKey ) = = pHoldKeys - > end ( ) ) pHoldKeys - > push_back ( rKey ) ;
}
2010-03-28 18:58:01 +00:00
}
2009-05-28 00:14:54 +00:00
// then all regular keys that aren't in the hold keys list yet
for ( C4PlayerControlAssignmentVec : : const_iterator i = Assignments . begin ( ) ; i ! = Assignments . end ( ) ; + + i )
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
const C4PlayerControlAssignment & rAssignment = * i ;
const C4PlayerControlDef * pDef = rDefs . GetControlByIndex ( rAssignment . GetControl ( ) ) ;
if ( pDef & & ! pDef - > IsHoldKey ( ) )
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
const C4KeyCodeEx & rKey = rAssignment . GetTriggerKey ( ) ;
if ( std : : find ( pHoldKeys - > begin ( ) , pHoldKeys - > end ( ) , rKey ) = = pHoldKeys - > end ( ) )
if ( std : : find ( pRegularKeys - > begin ( ) , pRegularKeys - > end ( ) , rKey ) = = pRegularKeys - > end ( ) )
pRegularKeys - > push_back ( rKey ) ;
}
}
2010-03-28 18:58:01 +00:00
}
2009-05-28 00:14:54 +00:00
2009-10-14 16:46:22 +00:00
C4Facet C4PlayerControlAssignmentSet : : GetPicture ( ) const
{
// get image to be drawn to represent this control set
// picture per set not implemented yet. So just default to out standard images
2016-02-20 17:26:30 +00:00
if ( HasGamepad ( ) ) return : : GraphicsResource . fctGamepad . GetPhase ( 0 ) ;
2012-01-29 02:13:55 +00:00
// if (HasMouse()) return ::GraphicsResource.fctMouse; // might be useful again with changing control sets
2012-01-25 04:53:59 +00:00
if ( HasKeyboard ( ) ) return : : GraphicsResource . fctKeyboard . GetPhase ( Game . PlayerControlUserAssignmentSets . GetSetIndex ( this ) ) ;
2009-10-14 16:46:22 +00:00
return C4Facet ( ) ;
}
2009-10-17 15:53:26 +00:00
bool C4PlayerControlAssignmentSet : : IsMouseControlAssigned ( int32_t mouseevent ) const
{
// TODO
return true ;
}
2009-05-25 09:01:26 +00:00
/* C4PlayerControlAssignmentSets */
2009-05-26 06:10:38 +00:00
void C4PlayerControlAssignmentSets : : Clear ( )
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
Sets . clear ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
void C4PlayerControlAssignmentSets : : CompileFunc ( StdCompiler * pComp )
2010-03-28 18:58:01 +00:00
{
2017-03-11 14:05:41 +00:00
if ( pComp - > isSerializer ( ) & & pComp - > isRegistry ( ) )
2011-03-30 20:11:47 +00:00
{
pComp - > Default ( " ControlSets " ) ; // special registry compiler: Clean out everything before
}
2011-07-16 09:58:38 +00:00
pComp - > Value ( mkNamingAdapt ( clear_previous , " ClearPrevious " , false ) ) ;
pComp - > Value ( mkSTLContainerAdapt ( Sets , StdCompiler : : SEP_NONE ) ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
2011-03-30 20:11:47 +00:00
bool C4PlayerControlAssignmentSets : : operator = = ( const C4PlayerControlAssignmentSets & cmp ) const
{
2011-07-16 09:58:38 +00:00
return Sets = = cmp . Sets & & clear_previous = = cmp . clear_previous ;
2011-03-30 20:11:47 +00:00
}
void C4PlayerControlAssignmentSets : : MergeFrom ( const C4PlayerControlAssignmentSets & Src , C4PlayerControlAssignmentSet : : MergeMode merge_mode )
2010-03-28 18:58:01 +00:00
{
2011-07-16 09:58:38 +00:00
// if source set is flagged to clear previous, do this!
if ( Src . clear_previous ) Sets . clear ( ) ;
2009-05-26 06:10:38 +00:00
// take over all assignments in known sets and new sets defined in Src
for ( AssignmentSetList : : const_iterator i = Src . Sets . begin ( ) ; i ! = Src . Sets . end ( ) ; + + i )
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
const C4PlayerControlAssignmentSet & SrcSet = * i ;
// overwrite if def of same name existed if it's not low priority anyway
2009-06-16 00:38:39 +00:00
bool fIsWildcardSet = SrcSet . IsWildcardName ( ) ;
if ( ! fIsWildcardSet )
2010-03-28 18:58:01 +00:00
{
2009-06-16 00:38:39 +00:00
C4PlayerControlAssignmentSet * pPrevSet = GetSetByName ( SrcSet . GetName ( ) ) ;
2011-03-30 20:11:47 +00:00
if ( ! pPrevSet & & merge_mode = = C4PlayerControlAssignmentSet : : MM_Inherit )
{
// inherited sets must go through merge procedure to set inherited links
pPrevSet = CreateEmptySetByTemplate ( SrcSet ) ;
}
2009-06-16 00:38:39 +00:00
if ( pPrevSet )
2010-03-28 18:58:01 +00:00
{
2011-03-30 20:11:47 +00:00
pPrevSet - > MergeFrom ( SrcSet , merge_mode ) ;
2010-03-28 18:58:01 +00:00
}
2009-06-16 00:38:39 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-06-16 00:38:39 +00:00
// new def: Append a copy
Sets . push_back ( SrcSet ) ;
2009-05-26 06:10:38 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-06-16 00:38:39 +00:00
// source is a wildcard: Merge with all matching sets
for ( AssignmentSetList : : iterator j = Sets . begin ( ) ; j ! = Sets . end ( ) ; + + j )
2010-03-28 18:58:01 +00:00
{
2009-06-16 00:38:39 +00:00
C4PlayerControlAssignmentSet & DstSet = * j ;
if ( WildcardMatch ( SrcSet . GetName ( ) , DstSet . GetName ( ) ) )
2010-03-28 18:58:01 +00:00
{
2011-03-30 20:11:47 +00:00
DstSet . MergeFrom ( SrcSet , merge_mode ) ;
2009-06-16 00:38:39 +00:00
}
2009-05-26 06:10:38 +00:00
}
}
}
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
bool C4PlayerControlAssignmentSets : : ResolveRefs ( C4PlayerControlDefs * pDefs )
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
for ( AssignmentSetList : : iterator i = Sets . begin ( ) ; i ! = Sets . end ( ) ; + + i )
if ( ! ( * i ) . ResolveRefs ( pDefs ) ) return false ;
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
2011-03-31 14:26:59 +00:00
void C4PlayerControlAssignmentSets : : SortAssignments ( )
{
for ( AssignmentSetList : : iterator i = Sets . begin ( ) ; i ! = Sets . end ( ) ; + + i )
( * i ) . SortAssignments ( ) ;
}
2009-05-26 06:10:38 +00:00
C4PlayerControlAssignmentSet * C4PlayerControlAssignmentSets : : GetSetByName ( const char * szName )
2009-10-13 20:02:44 +00:00
{
2009-05-26 06:10:38 +00:00
for ( AssignmentSetList : : iterator i = Sets . begin ( ) ; i ! = Sets . end ( ) ; + + i )
2009-10-13 20:02:44 +00:00
if ( WildcardMatch ( szName , ( * i ) . GetName ( ) ) )
2009-05-26 06:10:38 +00:00
return & * i ;
2016-11-02 23:58:02 +00:00
return nullptr ;
2009-10-13 20:02:44 +00:00
}
2009-05-26 06:10:38 +00:00
2009-10-14 16:46:22 +00:00
C4PlayerControlAssignmentSet * C4PlayerControlAssignmentSets : : GetDefaultSet ( )
{
// default set is first defined control set
2016-11-02 23:58:02 +00:00
if ( Sets . empty ( ) ) return nullptr ; // nothing defined :(
2009-10-14 16:46:22 +00:00
return & Sets . front ( ) ;
}
int32_t C4PlayerControlAssignmentSets : : GetSetIndex ( const C4PlayerControlAssignmentSet * set ) const
{
// find set in list; return index
int32_t index = 0 ;
for ( AssignmentSetList : : const_iterator i = Sets . begin ( ) ; i ! = Sets . end ( ) ; + + i , + + index )
if ( & * i = = set )
return index ;
return - 1 ; // not found
}
C4PlayerControlAssignmentSet * C4PlayerControlAssignmentSets : : GetSetByIndex ( int32_t index )
{
// bounds check
2016-11-02 23:58:02 +00:00
if ( index < 0 | | index > = ( int32_t ) Sets . size ( ) ) return nullptr ;
2009-10-14 16:46:22 +00:00
// return indexed set
AssignmentSetList : : iterator i = Sets . begin ( ) ;
while ( index - - ) + + i ;
return & * i ;
}
2011-03-30 20:11:47 +00:00
C4PlayerControlAssignmentSet * C4PlayerControlAssignmentSets : : CreateEmptySetByTemplate ( const C4PlayerControlAssignmentSet & template_set )
{
Sets . push_back ( C4PlayerControlAssignmentSet ( ) ) ;
Sets . back ( ) . InitEmptyFromTemplate ( template_set ) ;
return & Sets . back ( ) ;
}
void C4PlayerControlAssignmentSets : : RemoveSetByName ( const char * set_name )
{
for ( AssignmentSetList : : iterator i = Sets . begin ( ) ; i ! = Sets . end ( ) ; + + i )
if ( SEqual ( set_name , ( * i ) . GetName ( ) ) )
{
Sets . erase ( i ) ;
return ;
}
}
2009-05-26 06:10:38 +00:00
/* C4PlayerControlFile */
void C4PlayerControlFile : : CompileFunc ( StdCompiler * pComp )
2010-03-28 18:58:01 +00:00
{
2011-07-16 09:58:38 +00:00
pComp - > Value ( mkNamingAdapt ( ControlDefs , " ControlDefs " , C4PlayerControlDefs ( ) ) ) ;
pComp - > Value ( mkNamingAdapt ( AssignmentSets , " ControlSets " , C4PlayerControlAssignmentSets ( ) ) ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
bool C4PlayerControlFile : : Load ( C4Group & hGroup , const char * szFilename , C4LangStringTable * pLang )
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
// clear previous
Clear ( ) ;
// load and prepare file contents
StdStrBuf Buf ;
2011-03-05 01:44:26 +00:00
if ( ! hGroup . LoadEntryString ( szFilename , & Buf ) ) return false ;
2009-05-26 06:10:38 +00:00
if ( pLang ) pLang - > ReplaceStrings ( Buf ) ;
// parse it!
if ( ! CompileFromBuf_LogWarn < StdCompilerINIRead > ( * this , Buf , szFilename ) ) return false ;
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
bool C4PlayerControlFile : : Save ( C4Group & hGroup , const char * szFilename )
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
// decompile to buffer and save buffer to group
StdStrBuf Buf ;
if ( ! DecompileToBuf_Log < StdCompilerINIWrite > ( * this , & Buf , szFilename ) ) return false ;
hGroup . Add ( szFilename , Buf , false , true ) ;
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
void C4PlayerControlFile : : Clear ( )
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
ControlDefs . Clear ( ) ;
AssignmentSets . Clear ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
/* C4PlayerControl */
2009-05-28 00:14:54 +00:00
void C4PlayerControl : : CSync : : ControlDownState : : CompileFunc ( StdCompiler * pComp )
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
pComp - > Value ( DownState ) ;
2010-04-01 21:08:06 +00:00
pComp - > Separator ( ) ;
Make GetPlayerControlState() query the current controller state
To keep compatibility with scripts which expect only binary buttons,
this adds a third parameter to the function which enables the new
functionality.
Bonus /script to test the controller stick x axis:
Schedule(GetCursor(), "Message(\"%d / %d\", GetPlayerControlState(0, CON_Left, true), GetPlayerControlState(0, CON_Right, true))", 10, 100000000)
Note that the values will be inconsistent if multiple analog sticks are
bound to the same control, as values from one stick will overwrite those
from the other one. This can happen even if you move only one stick.
2016-02-15 23:21:19 +00:00
pComp - > Value ( MovedState ) ;
pComp - > Separator ( ) ;
2009-05-28 00:14:54 +00:00
pComp - > Value ( iDownFrame ) ;
2010-04-01 21:08:06 +00:00
pComp - > Separator ( ) ;
Make GetPlayerControlState() query the current controller state
To keep compatibility with scripts which expect only binary buttons,
this adds a third parameter to the function which enables the new
functionality.
Bonus /script to test the controller stick x axis:
Schedule(GetCursor(), "Message(\"%d / %d\", GetPlayerControlState(0, CON_Left, true), GetPlayerControlState(0, CON_Right, true))", 10, 100000000)
Note that the values will be inconsistent if multiple analog sticks are
bound to the same control, as values from one stick will overwrite those
from the other one. This can happen even if you move only one stick.
2016-02-15 23:21:19 +00:00
pComp - > Value ( iMovedFrame ) ;
pComp - > Separator ( ) ;
2009-05-28 00:14:54 +00:00
pComp - > Value ( fDownByUser ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-28 00:14:54 +00:00
bool C4PlayerControl : : CSync : : ControlDownState : : operator = = ( const ControlDownState & cmp ) const
2010-03-28 18:58:01 +00:00
{
Make GetPlayerControlState() query the current controller state
To keep compatibility with scripts which expect only binary buttons,
this adds a third parameter to the function which enables the new
functionality.
Bonus /script to test the controller stick x axis:
Schedule(GetCursor(), "Message(\"%d / %d\", GetPlayerControlState(0, CON_Left, true), GetPlayerControlState(0, CON_Right, true))", 10, 100000000)
Note that the values will be inconsistent if multiple analog sticks are
bound to the same control, as values from one stick will overwrite those
from the other one. This can happen even if you move only one stick.
2016-02-15 23:21:19 +00:00
return DownState = = cmp . DownState & & MovedState = = cmp . MovedState & & iDownFrame = = cmp . iDownFrame & & iMovedFrame = = cmp . iMovedFrame & & fDownByUser = = cmp . fDownByUser ;
2010-03-28 18:58:01 +00:00
}
2009-05-28 00:14:54 +00:00
const C4PlayerControl : : CSync : : ControlDownState * C4PlayerControl : : CSync : : GetControlDownState ( int32_t iControl ) const
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
// safe access
2016-11-02 23:58:02 +00:00
if ( iControl < 0 | | iControl > = int32_t ( ControlDownStates . size ( ) ) ) return nullptr ;
2009-05-28 00:14:54 +00:00
return & ControlDownStates [ iControl ] ;
2010-03-28 18:58:01 +00:00
}
2009-05-28 00:14:54 +00:00
int32_t C4PlayerControl : : CSync : : GetControlDisabled ( int32_t iControl ) const
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
// safe access
2009-08-10 14:48:25 +00:00
if ( iControl < 0 | | iControl > = int32_t ( ControlDisableStates . size ( ) ) ) return 0 ;
2009-05-28 00:14:54 +00:00
return ControlDisableStates [ iControl ] ;
2010-03-28 18:58:01 +00:00
}
2009-05-28 00:14:54 +00:00
void C4PlayerControl : : CSync : : SetControlDownState ( int32_t iControl , const C4KeyEventData & rDownState , int32_t iDownFrame , bool fDownByUser )
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
// update state
if ( iControl < 0 ) return ;
2009-08-10 14:48:25 +00:00
if ( iControl > = int32_t ( ControlDownStates . size ( ) ) ) ControlDownStates . resize ( iControl + 1 ) ;
2009-05-28 00:14:54 +00:00
ControlDownState & rState = ControlDownStates [ iControl ] ;
rState . DownState = rDownState ;
rState . iDownFrame = iDownFrame ;
rState . fDownByUser = fDownByUser ;
2010-03-28 18:58:01 +00:00
}
2009-05-28 00:14:54 +00:00
Make GetPlayerControlState() query the current controller state
To keep compatibility with scripts which expect only binary buttons,
this adds a third parameter to the function which enables the new
functionality.
Bonus /script to test the controller stick x axis:
Schedule(GetCursor(), "Message(\"%d / %d\", GetPlayerControlState(0, CON_Left, true), GetPlayerControlState(0, CON_Right, true))", 10, 100000000)
Note that the values will be inconsistent if multiple analog sticks are
bound to the same control, as values from one stick will overwrite those
from the other one. This can happen even if you move only one stick.
2016-02-15 23:21:19 +00:00
void C4PlayerControl : : CSync : : SetControlMovedState ( int32_t iControl , const C4KeyEventData & rMovedState , int32_t iMovedFrame )
{
// update state
if ( iControl < 0 ) return ;
if ( iControl > = int32_t ( ControlDownStates . size ( ) ) ) ControlDownStates . resize ( iControl + 1 ) ;
ControlDownState & rState = ControlDownStates [ iControl ] ;
rState . MovedState = rMovedState ;
rState . iMovedFrame = iMovedFrame ;
}
2009-12-27 18:11:14 +00:00
bool C4PlayerControl : : CSync : : SetControlDisabled ( int32_t iControl , int32_t iVal )
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
// disable control
2009-12-27 18:11:14 +00:00
if ( iControl < 0 ) return false ;
2009-08-10 14:48:25 +00:00
if ( iControl > = int32_t ( ControlDisableStates . size ( ) ) ) ControlDisableStates . resize ( iControl + 1 ) ;
2009-05-28 00:14:54 +00:00
ControlDisableStates [ iControl ] = iVal ;
// if a control is disabled, its down-state is reset silently
2010-07-30 20:30:54 +00:00
ResetControlDownState ( iControl ) ;
return true ;
}
void C4PlayerControl : : CSync : : ResetControlDownState ( int32_t iControl )
{
// silently reset down state of control
2009-05-28 00:14:54 +00:00
const ControlDownState * pDownState = GetControlDownState ( iControl ) ;
if ( pDownState & & pDownState - > IsDown ( ) )
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
C4KeyEventData KeyDownState = pDownState - > DownState ;
KeyDownState . iStrength = 0 ;
SetControlDownState ( iControl , KeyDownState , 0 , false ) ;
Make GetPlayerControlState() query the current controller state
To keep compatibility with scripts which expect only binary buttons,
this adds a third parameter to the function which enables the new
functionality.
Bonus /script to test the controller stick x axis:
Schedule(GetCursor(), "Message(\"%d / %d\", GetPlayerControlState(0, CON_Left, true), GetPlayerControlState(0, CON_Right, true))", 10, 100000000)
Note that the values will be inconsistent if multiple analog sticks are
bound to the same control, as values from one stick will overwrite those
from the other one. This can happen even if you move only one stick.
2016-02-15 23:21:19 +00:00
SetControlMovedState ( iControl , KeyDownState , 0 ) ;
2009-05-28 00:14:54 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-28 00:14:54 +00:00
2010-03-26 13:48:28 +00:00
void C4PlayerControl : : CSync : : InitDefaults ( const C4PlayerControlDefs & ControlDefs )
{
const C4PlayerControlDef * def ;
int32_t i = 0 ;
2010-03-26 22:51:52 +00:00
while ( ( def = ControlDefs . GetControlByIndex ( i ) ) )
2010-03-26 13:48:28 +00:00
{
if ( def - > IsDefaultDisabled ( ) ) SetControlDisabled ( i , true ) ;
+ + i ;
}
}
2009-05-28 00:14:54 +00:00
void C4PlayerControl : : CSync : : Clear ( )
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
ControlDownStates . clear ( ) ;
ControlDisableStates . clear ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-28 00:14:54 +00:00
2009-05-26 06:10:38 +00:00
void C4PlayerControl : : CSync : : CompileFunc ( StdCompiler * pComp )
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
pComp - > Value ( mkNamingAdapt ( mkSTLContainerAdapt ( ControlDownStates ) , " Down " , DownStateVec ( ) ) ) ;
pComp - > Value ( mkNamingAdapt ( mkSTLContainerAdapt ( ControlDisableStates ) , " Disabled " , DisableStateVec ( ) ) ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
bool C4PlayerControl : : CSync : : operator = = ( const CSync & cmp ) const
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
return ControlDownStates = = cmp . ControlDownStates
2010-03-28 18:58:01 +00:00
& & ControlDisableStates = = cmp . ControlDisableStates ;
}
2009-05-26 06:10:38 +00:00
2010-03-26 13:48:28 +00:00
void C4PlayerControl : : Init ( )
{
// defaultdisabled controls
Sync . InitDefaults ( ControlDefs ) ;
}
2009-05-26 06:10:38 +00:00
void C4PlayerControl : : CompileFunc ( StdCompiler * pComp )
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
// compile sync values only
2010-03-26 13:48:28 +00:00
CSync DefaultSync ;
DefaultSync . InitDefaults ( ControlDefs ) ;
pComp - > Value ( mkNamingAdapt ( Sync , " PlayerControl " , DefaultSync ) ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
2016-02-15 15:20:37 +00:00
bool C4PlayerControl : : ProcessKeyEvent ( const C4KeyCodeEx & pressed_key , const C4KeyCodeEx & matched_key , ControlState state , const C4KeyEventData & rKeyExtraData , bool reset_down_states_only , bool * clear_recent_keys )
2010-03-28 18:58:01 +00:00
{
2016-02-20 21:25:07 +00:00
if ( Key_IsGamepad ( pressed_key . Key ) )
{
// We have to filter gamepad events here.
C4Player * plr = : : Players . Get ( iPlr ) ;
if ( ! plr | | ! plr - > pGamepad | | plr - > pGamepad - > GetID ( ) ! = pressed_key . deviceId )
return false ;
}
2009-05-26 06:10:38 +00:00
// collect all matching keys
2009-05-28 00:14:54 +00:00
C4PlayerControlAssignmentPVec Matches ;
assert ( pControlSet ) ; // shouldn't get this callback for players without control set
2016-02-15 15:20:37 +00:00
pControlSet - > GetAssignmentsByKey ( ControlDefs , matched_key , state ! = CONS_Down , & Matches , DownKeys , RecentKeys ) ;
2009-05-26 06:10:38 +00:00
// process async controls
2016-08-07 14:48:54 +00:00
bool cursor_pos_added = false ;
2016-11-02 23:58:02 +00:00
C4ControlPlayerControl * pControlPacket = nullptr ;
2009-05-28 00:14:54 +00:00
for ( C4PlayerControlAssignmentPVec : : const_iterator i = Matches . begin ( ) ; i ! = Matches . end ( ) ; + + i )
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
const C4PlayerControlAssignment * pAssignment = * i ;
assert ( pAssignment ) ;
int32_t iControlIndex = pAssignment - > GetControl ( ) ;
const C4PlayerControlDef * pControlDef = ControlDefs . GetControlByIndex ( iControlIndex ) ;
2016-02-15 15:20:37 +00:00
if ( pControlDef & & pControlDef - > IsValid ( ) & & ! Sync . IsControlDisabled ( iControlIndex ) & & ( state = = CONS_Down | | pControlDef - > IsHoldKey ( ) ) )
2010-03-28 18:58:01 +00:00
{
2013-09-02 15:27:15 +00:00
// clear RecentKeys if requested by this assignment. Must be done before sync queue, so multiple combos can be issued in a single control frame.
if ( clear_recent_keys & & ( pAssignment - > GetTriggerMode ( ) & C4PlayerControlAssignment : : CTM_ClearRecentKeys ) ) * clear_recent_keys = true ;
2010-04-10 20:44:00 +00:00
// extra data from key or overwrite by current cursor pos if definition requires it
2009-05-26 06:10:38 +00:00
if ( pControlDef - > IsAsync ( ) & & ! pControlPacket )
2010-03-28 18:58:01 +00:00
{
2010-05-07 13:57:56 +00:00
if ( pControlDef - > IsSendCursorPos ( ) ) IsCursorPosRequested = true ; // async cursor pos request - doesn't really make sense to set this flag for async controls
2016-02-15 15:20:37 +00:00
if ( ExecuteControl ( iControlIndex , state , rKeyExtraData , pAssignment - > GetTriggerMode ( ) , pressed_key . IsRepeated ( ) , reset_down_states_only ) )
2009-05-26 06:10:38 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
// sync control
// ignore key repeats, because we do our own key repeat for sync controls
2009-10-16 14:23:17 +00:00
if ( pressed_key . IsRepeated ( ) ) return false ;
2009-05-26 06:10:38 +00:00
// sync control has higher priority - no more async execution then
// build a control packet and add control data instead. even for async controls later in chain, as they may be blocked by a sync handler
2016-02-15 15:20:37 +00:00
if ( ! pControlPacket ) pControlPacket = new C4ControlPlayerControl ( iPlr , state , rKeyExtraData ) ;
2010-07-30 20:30:54 +00:00
int32_t extra_trigger_mode = 0 ;
if ( reset_down_states_only ) extra_trigger_mode | = C4PlayerControlAssignment : : CTM_HandleDownStatesOnly ;
pControlPacket - > AddControl ( iControlIndex , pAssignment - > GetTriggerMode ( ) | extra_trigger_mode ) ;
2010-05-07 13:57:56 +00:00
// sync cursor pos request; pos will be added to control before it is synced/executed
2016-08-07 14:48:54 +00:00
if ( pControlDef - > IsSendCursorPos ( ) & & ! cursor_pos_added )
{
int32_t x , y , game_x , game_y ;
// Add current cursor pos in GUI and game coordinates to input
if ( GetCurrentPlayerCursorPos ( & x , & y , & game_x , & game_y ) )
{
C4KeyEventData cursor_key_data ( rKeyExtraData ) ;
cursor_key_data . vp_x = x ; cursor_key_data . vp_y = y ;
cursor_key_data . game_x = game_x ; cursor_key_data . game_y = game_y ;
pControlPacket - > SetExtraData ( cursor_key_data ) ;
}
// Will also send a CON_CursorPos packet separately
IsCursorPosRequested = true ;
cursor_pos_added = true ;
}
2009-05-26 06:10:38 +00:00
}
}
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
// push sync control to input
2009-05-28 00:14:54 +00:00
if ( pControlPacket )
2010-03-28 18:58:01 +00:00
{
2009-12-30 00:56:52 +00:00
Game . Input . Add ( CID_PlrControl , pControlPacket ) ;
2009-05-28 00:14:54 +00:00
// assume processed (although we can't really know that yet)
return true ;
2009-05-26 06:10:38 +00:00
}
2010-03-28 18:58:01 +00:00
return false ;
}
2009-05-26 06:10:38 +00:00
2009-10-16 14:23:17 +00:00
bool C4PlayerControl : : ProcessKeyDown ( const C4KeyCodeEx & pressed_key , const C4KeyCodeEx & matched_key )
2009-10-17 15:53:26 +00:00
{
2009-05-26 06:10:38 +00:00
// add key to local "down" list if it's not already in there
2009-10-17 15:53:26 +00:00
// except for some mouse events for which a down state does not make sense
2013-12-07 14:27:01 +00:00
C4PlayerControlRecentKey RKey ( pressed_key , matched_key , C4TimeMilliseconds : : Now ( ) ) ;
2011-11-20 20:49:38 +00:00
if ( ! Key_IsMouse ( pressed_key . Key ) | | Inside < uint8_t > ( Key_GetMouseEvent ( pressed_key . Key ) , KEY_MOUSE_Button1 , KEY_MOUSE_ButtonMax ) )
2009-10-17 15:53:26 +00:00
{
if ( std : : find ( DownKeys . begin ( ) , DownKeys . end ( ) , pressed_key ) = = DownKeys . end ( ) ) DownKeys . push_back ( RKey ) ;
}
2009-05-26 06:10:38 +00:00
// process!
2013-09-02 15:27:15 +00:00
bool clear_recent_keys = false ;
2016-02-15 15:20:37 +00:00
bool fResult = ProcessKeyEvent ( pressed_key , matched_key , CONS_Down , Game . KeyboardInput . GetLastKeyExtraData ( ) , false , & clear_recent_keys ) ;
2013-09-02 15:27:15 +00:00
// unless assignment requests a clear, always add keys to recent list even if not handled
if ( clear_recent_keys )
RecentKeys . clear ( ) ;
else if ( ! pressed_key . IsRepeated ( ) ) // events caused by holding down the key are not added to recent list (so you cannot cause "double-Q" just by holding down Q)
RecentKeys . push_back ( RKey ) ;
2009-05-26 06:10:38 +00:00
return fResult ;
2009-10-17 15:53:26 +00:00
}
2009-05-26 06:10:38 +00:00
2011-11-20 20:49:38 +00:00
bool C4PlayerControl : : ProcessKeyUp ( const C4KeyCodeEx & pressed_key , const C4KeyCodeEx & matched_key )
2009-10-17 15:53:26 +00:00
{
2009-05-26 06:10:38 +00:00
// remove key from "down" list
2009-10-17 15:53:26 +00:00
// except for some mouse events for which a down state does not make sense
if ( ! Key_IsMouse ( pressed_key . Key ) | | Inside < uint8_t > ( Key_GetMouseEvent ( pressed_key . Key ) , KEY_MOUSE_Button1 , KEY_MOUSE_ButtonMax ) )
{
C4PlayerControlRecentKeyList : : iterator i = find ( DownKeys . begin ( ) , DownKeys . end ( ) , pressed_key ) ;
if ( i ! = DownKeys . end ( ) ) DownKeys . erase ( i ) ;
}
2009-05-26 06:10:38 +00:00
// process!
2016-02-15 15:20:37 +00:00
return ProcessKeyEvent ( pressed_key , matched_key , CONS_Up , Game . KeyboardInput . GetLastKeyExtraData ( ) ) ;
}
bool C4PlayerControl : : ProcessKeyMoved ( const C4KeyCodeEx & pressed_key , const C4KeyCodeEx & matched_key )
{
// process!
return ProcessKeyEvent ( pressed_key , matched_key , CONS_Moved , Game . KeyboardInput . GetLastKeyExtraData ( ) ) ;
2009-10-17 15:53:26 +00:00
}
2009-05-26 06:10:38 +00:00
2009-05-28 00:14:54 +00:00
void C4PlayerControl : : ExecuteControlPacket ( const class C4ControlPlayerControl * pCtrl )
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
// callback from control queue. Execute controls in packet until one of them gets processed
// assume async packets always as not processed to ensure sync safety (usually, sync commands should better not ovberride async commands anyway)
2010-07-30 20:30:54 +00:00
bool fHandleDownStateOnly = false ;
2009-05-28 00:14:54 +00:00
for ( C4ControlPlayerControl : : ControlItemVec : : const_iterator i = pCtrl - > GetControlItems ( ) . begin ( ) ; i ! = pCtrl - > GetControlItems ( ) . end ( ) ; + + i )
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
const C4ControlPlayerControl : : ControlItem & rItem = * i ;
const C4PlayerControlDef * pCtrlDef = ControlDefs . GetControlByIndex ( rItem . iControl ) ;
2009-05-26 06:10:38 +00:00
if ( pCtrlDef )
2010-03-28 18:58:01 +00:00
{
2013-05-25 20:38:08 +00:00
if ( Config . General . DebugRec )
2011-12-28 23:12:21 +00:00
{
2013-05-25 20:38:08 +00:00
if ( pCtrlDef - > IsSync ( ) )
{
AddDbgRec ( RCT_PlrCom , & rItem . iControl , sizeof ( rItem . iControl ) ) ;
}
2011-12-28 23:12:21 +00:00
}
2016-02-15 15:20:37 +00:00
if ( ExecuteControl ( rItem . iControl , pCtrl - > GetState ( ) , pCtrl - > GetExtraData ( ) , rItem . iTriggerMode , false , fHandleDownStateOnly ) )
2009-05-26 06:10:38 +00:00
if ( pCtrlDef - > IsSync ( ) )
2010-07-30 20:30:54 +00:00
{
2016-02-15 15:20:37 +00:00
if ( pCtrl - > GetState ( ) = = CONS_Up )
2010-07-30 20:30:54 +00:00
{
// control processed. however, for key releases, overriden keys are released silently so following down events aren't handled as key repeats
// note this does not affect CTM_Hold/CTM_Release, because they ignore release controls anyway
fHandleDownStateOnly = true ;
}
else
{
break ;
}
}
2009-05-26 06:10:38 +00:00
}
}
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
2016-02-15 15:20:37 +00:00
bool C4PlayerControl : : ExecuteControl ( int32_t iControl , ControlState state , const C4KeyEventData & rKeyExtraData , int32_t iTriggerMode , bool fRepeated , bool fHandleDownStateOnly )
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
// execute single control. return if handled
2009-05-28 00:14:54 +00:00
const C4PlayerControlDef * pControlDef = ControlDefs . GetControlByIndex ( iControl ) ;
2009-05-26 06:10:38 +00:00
if ( ! pControlDef | | Sync . IsControlDisabled ( iControl ) ) return false ;
C4PlayerControlDef : : Actions eAction = pControlDef - > GetAction ( ) ;
C4KeyEventData KeyExtraData ( rKeyExtraData ) ;
2009-05-28 00:14:54 +00:00
const CSync : : ControlDownState * pCtrlDownState = Sync . GetControlDownState ( iControl ) ;
bool fWasDown = pCtrlDownState ? pCtrlDownState - > IsDown ( ) : false ;
2009-05-26 06:10:38 +00:00
// global controls only in global context
if ( IsGlobal ( ) ! = pControlDef - > IsGlobal ( ) ) return false ;
2010-07-30 20:30:54 +00:00
// down state handling only?
if ( iTriggerMode & C4PlayerControlAssignment : : CTM_HandleDownStatesOnly ) fHandleDownStateOnly = true ;
2009-05-26 06:10:38 +00:00
// hold-actions only work on script controls with the hold flag
if ( iTriggerMode & ( C4PlayerControlAssignment : : CTM_Hold | C4PlayerControlAssignment : : CTM_Release ) )
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
if ( eAction ! = C4PlayerControlDef : : CDA_Script ) return false ;
if ( ! pControlDef - > IsHoldKey ( ) ) return false ;
2016-02-15 15:20:37 +00:00
if ( state = = CONS_Up ) return false ; // hold triggers have no "up"-event
2009-05-26 06:10:38 +00:00
// perform hold/release
if ( fWasDown )
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
// control is currently down: release?
if ( iTriggerMode & C4PlayerControlAssignment : : CTM_Release )
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
KeyExtraData . iStrength = 0 ;
Sync . SetControlDownState ( iControl , KeyExtraData , Game . FrameCounter , false ) ;
// now process as a regular "Up" event
2016-02-15 15:20:37 +00:00
state = CONS_Up ;
2009-05-26 06:10:38 +00:00
fRepeated = false ;
2010-03-28 18:58:01 +00:00
}
2015-03-25 18:04:04 +00:00
else
2010-03-28 18:58:01 +00:00
{
2015-03-25 18:04:04 +00:00
assert ( iTriggerMode & C4PlayerControlAssignment : : CTM_Hold ) ;
2009-05-26 06:10:38 +00:00
// control is down but trigger key is pressed again: Refresh down state
2009-05-28 00:14:54 +00:00
// (this will restart the KeyRepeat time)
2009-05-26 06:10:38 +00:00
Sync . SetControlDownState ( iControl , KeyExtraData , Game . FrameCounter , false ) ;
// now process as a regular, repeated "down" event
fRepeated = true ;
}
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
// control is currently up. Put into hold-down-state if this is a hold key
if ( iTriggerMode & C4PlayerControlAssignment : : CTM_Hold )
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
Sync . SetControlDownState ( iControl , KeyExtraData , Game . FrameCounter , false ) ;
// now process as a regular "down" event
fRepeated = false ;
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
//. Ignore if it's only a release key
return false ;
}
}
2010-03-28 18:58:01 +00:00
}
2016-02-15 15:20:37 +00:00
else if ( state = = CONS_Up )
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
// regular ControlUp: Only valid if that control was down
if ( ! fWasDown ) return false ;
2009-05-28 00:14:54 +00:00
Sync . SetControlDownState ( iControl , KeyExtraData , Game . FrameCounter , true ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-28 00:14:54 +00:00
else if ( pControlDef - > IsHoldKey ( ) )
2010-03-28 18:58:01 +00:00
{
2016-02-15 15:20:37 +00:00
if ( state = = CONS_Moved )
Make GetPlayerControlState() query the current controller state
To keep compatibility with scripts which expect only binary buttons,
this adds a third parameter to the function which enables the new
functionality.
Bonus /script to test the controller stick x axis:
Schedule(GetCursor(), "Message(\"%d / %d\", GetPlayerControlState(0, CON_Left, true), GetPlayerControlState(0, CON_Right, true))", 10, 100000000)
Note that the values will be inconsistent if multiple analog sticks are
bound to the same control, as values from one stick will overwrite those
from the other one. This can happen even if you move only one stick.
2016-02-15 23:21:19 +00:00
{
Sync . SetControlMovedState ( iControl , KeyExtraData , Game . FrameCounter ) ;
2016-02-15 15:20:37 +00:00
fRepeated = true ;
Make GetPlayerControlState() query the current controller state
To keep compatibility with scripts which expect only binary buttons,
this adds a third parameter to the function which enables the new
functionality.
Bonus /script to test the controller stick x axis:
Schedule(GetCursor(), "Message(\"%d / %d\", GetPlayerControlState(0, CON_Left, true), GetPlayerControlState(0, CON_Right, true))", 10, 100000000)
Note that the values will be inconsistent if multiple analog sticks are
bound to the same control, as values from one stick will overwrite those
from the other one. This can happen even if you move only one stick.
2016-02-15 23:21:19 +00:00
}
2016-02-15 15:20:37 +00:00
else
{
// regular ControlDown on Hold Key: Set in down list
Sync . SetControlDownState ( iControl , KeyExtraData , Game . FrameCounter , true ) ;
fRepeated = fWasDown ;
}
2010-03-28 18:58:01 +00:00
}
2010-07-30 20:30:54 +00:00
// down state handling done
if ( fHandleDownStateOnly ) return false ;
2009-05-26 06:10:38 +00:00
// perform action for this control
2016-02-15 15:20:37 +00:00
bool fHandled = ExecuteControlAction ( iControl , eAction , pControlDef - > GetExtraData ( ) , state , KeyExtraData , fRepeated ) ;
2009-10-16 14:23:17 +00:00
// handled controls hide control display
C4Player * pPlr ;
2010-01-25 04:00:59 +00:00
if ( ( pPlr = : : Players . Get ( iPlr ) ) ) if ( pPlr - > ShowStartup ) pPlr - > ShowStartup = false ;
2009-05-26 06:10:38 +00:00
// return if handled, unless control is defined as always unhandled
return fHandled & & ! ( iTriggerMode & C4PlayerControlAssignment : : CTM_AlwaysUnhandled ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
2016-02-15 15:20:37 +00:00
bool C4PlayerControl : : ExecuteControlAction ( int32_t iControl , C4PlayerControlDef : : Actions eAction , C4ID idControlExtraData , ControlState state , const C4KeyEventData & rKeyExtraData , bool fRepeated )
2010-03-28 18:58:01 +00:00
{
2016-02-15 15:20:37 +00:00
// moved events don't make sense for menus and are only handled by script
if ( state = = CONS_Moved & & eAction ! = C4PlayerControlDef : : CDA_Script ) return false ;
2009-05-26 06:10:38 +00:00
// get affected player
2016-11-02 23:58:02 +00:00
C4Player * pPlr = nullptr ;
2009-12-31 17:20:45 +00:00
C4Viewport * pVP ;
2016-11-02 23:58:02 +00:00
C4Object * pCursor = nullptr ;
C4Menu * pCursorMenu = nullptr ;
2009-05-26 06:10:38 +00:00
if ( iPlr > - 1 )
2010-03-28 18:58:01 +00:00
{
2009-08-10 14:48:25 +00:00
pPlr = : : Players . Get ( iPlr ) ;
2009-05-26 06:10:38 +00:00
if ( ! pPlr ) return false ;
2010-02-08 20:52:46 +00:00
pCursor = pPlr - > Cursor ;
if ( pCursor & & pCursor - > Menu & & pCursor - > Menu - > IsActive ( ) ) pCursorMenu = pCursor - > Menu ;
2010-03-28 18:58:01 +00:00
}
2016-02-15 15:20:37 +00:00
bool fUp = state = = CONS_Up ;
2009-05-26 06:10:38 +00:00
// exec action (on player)
switch ( eAction )
2010-03-28 18:58:01 +00:00
{
2009-05-26 06:10:38 +00:00
// scripted player control
2010-03-28 18:58:01 +00:00
case C4PlayerControlDef : : CDA_Script :
2016-02-15 15:20:37 +00:00
return ExecuteControlScript ( iControl , idControlExtraData , state , rKeyExtraData , fRepeated ) ;
2009-05-26 06:10:38 +00:00
// menu controls
2010-03-28 18:58:01 +00:00
case C4PlayerControlDef : : CDA_Menu : if ( ! pPlr | | fUp ) return false ; if ( pPlr - > Menu . IsActive ( ) ) pPlr - > Menu . TryClose ( false , true ) ; else pPlr - > ActivateMenuMain ( ) ; return true ; // toggle
case C4PlayerControlDef : : CDA_MenuOK : if ( ! pPlr | | ! pPlr - > Menu . IsActive ( ) | | fUp ) return false ; pPlr - > Menu . Control ( COM_MenuEnter , 0 ) ; return true ; // ok on item
case C4PlayerControlDef : : CDA_MenuCancel : if ( ! pPlr | | ! pPlr - > Menu . IsActive ( ) | | fUp ) return false ; pPlr - > Menu . Control ( COM_MenuClose , 0 ) ; return true ; // close menu
case C4PlayerControlDef : : CDA_MenuLeft : if ( ! pPlr | | ! pPlr - > Menu . IsActive ( ) | | fUp ) return false ; pPlr - > Menu . Control ( COM_MenuLeft , 0 ) ; return true ; // navigate
case C4PlayerControlDef : : CDA_MenuUp : if ( ! pPlr | | ! pPlr - > Menu . IsActive ( ) | | fUp ) return false ; pPlr - > Menu . Control ( COM_MenuUp , 0 ) ; return true ; // navigate
case C4PlayerControlDef : : CDA_MenuRight : if ( ! pPlr | | ! pPlr - > Menu . IsActive ( ) | | fUp ) return false ; pPlr - > Menu . Control ( COM_MenuRight , 0 ) ; return true ; // navigate
2016-01-27 02:59:42 +00:00
case C4PlayerControlDef : : CDA_MenuDown : if ( ! pPlr | | ! pPlr - > Menu . IsActive ( ) | | fUp ) return false ; pPlr - > Menu . Control ( COM_MenuDown , 0 ) ; return true ; // navigate
2014-08-10 18:58:26 +00:00
case C4PlayerControlDef : : CDA_ObjectMenuTextComplete : if ( ! pCursorMenu | | fUp | | ! pCursorMenu - > IsTextProgressing ( ) ) return false ; pCursorMenu - > Control ( COM_MenuShowText , 0 ) ; return true ; // fast-foward text display
2010-03-28 18:58:01 +00:00
case C4PlayerControlDef : : CDA_ObjectMenuOK : if ( ! pCursorMenu | | fUp ) return false ; pCursorMenu - > Control ( COM_MenuEnter , 0 ) ; return true ; // ok on item
case C4PlayerControlDef : : CDA_ObjectMenuOKAll : if ( ! pCursorMenu | | fUp ) return false ; pCursorMenu - > Control ( COM_MenuEnterAll , 0 ) ; return true ; // alt ok on item
case C4PlayerControlDef : : CDA_ObjectMenuSelect : if ( ! pCursorMenu | | fUp ) return false ; pCursorMenu - > Control ( COM_MenuSelect , rKeyExtraData . iStrength ) ; return true ; // select an item directly
case C4PlayerControlDef : : CDA_ObjectMenuCancel : if ( ! pCursorMenu | | fUp ) return false ; pCursorMenu - > Control ( COM_MenuClose , 0 ) ; return true ; // close menu
case C4PlayerControlDef : : CDA_ObjectMenuLeft : if ( ! pCursorMenu | | fUp ) return false ; pCursorMenu - > Control ( COM_MenuLeft , 0 ) ; return true ; // navigate
case C4PlayerControlDef : : CDA_ObjectMenuUp : if ( ! pCursorMenu | | fUp ) return false ; pCursorMenu - > Control ( COM_MenuUp , 0 ) ; return true ; // navigate
case C4PlayerControlDef : : CDA_ObjectMenuRight : if ( ! pCursorMenu | | fUp ) return false ; pCursorMenu - > Control ( COM_MenuRight , 0 ) ; return true ; // navigate
case C4PlayerControlDef : : CDA_ObjectMenuDown : if ( ! pCursorMenu | | fUp ) return false ; pCursorMenu - > Control ( COM_MenuDown , 0 ) ; return true ; // navigate
2010-12-15 01:16:24 +00:00
case C4PlayerControlDef : : CDA_ZoomIn : if ( ! pPlr | | fUp | | ! ( pVP = : : Viewports . GetViewport ( iPlr ) ) ) return false ; pVP - > ChangeZoom ( C4GFX_ZoomStep ) ; return true ; // viewport zoom
case C4PlayerControlDef : : CDA_ZoomOut : if ( ! pPlr | | fUp | | ! ( pVP = : : Viewports . GetViewport ( iPlr ) ) ) return false ; pVP - > ChangeZoom ( 1.0f / C4GFX_ZoomStep ) ; return true ; // viewport zoom
2009-05-26 06:10:38 +00:00
//unknown action
2010-03-28 18:58:01 +00:00
default : return false ;
2009-05-26 06:10:38 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
2016-02-15 15:20:37 +00:00
bool C4PlayerControl : : ExecuteControlScript ( int32_t iControl , C4ID idControlExtraData , ControlState state , const C4KeyEventData & rKeyExtraData , bool fRepeated )
2010-03-28 18:58:01 +00:00
{
2009-08-10 14:48:25 +00:00
C4Player * pPlr = : : Players . Get ( iPlr ) ;
2009-05-28 00:14:54 +00:00
if ( pPlr )
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
// Not for eliminated (checked again in DirectCom, but make sure no control is generated for eliminated players!)
if ( pPlr - > Eliminated ) return false ;
2016-02-15 15:20:37 +00:00
// control count for statistics (but don't count analog stick wiggles)
if ( state ! = CONS_Moved )
pPlr - > CountControl ( C4Player : : PCID_DirectCom , iControl * 2 + state ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-28 00:14:54 +00:00
else if ( iPlr > - 1 )
2010-03-28 18:58:01 +00:00
{
2009-05-28 00:14:54 +00:00
// player lost?
return false ;
2010-03-28 18:58:01 +00:00
}
2011-11-20 20:49:38 +00:00
// get coordinates
int32_t x , y ;
const C4PlayerControlDef * def = ControlDefs . GetControlByIndex ( iControl ) ;
if ( def & & def - > GetCoordinateSpace ( ) = = C4PlayerControlDef : : COS_Viewport )
{
x = rKeyExtraData . vp_x ; y = rKeyExtraData . vp_y ;
}
else
{
x = rKeyExtraData . game_x ; y = rKeyExtraData . game_y ;
}
2016-01-17 03:06:19 +00:00
C4Value vx = ( x = = C4KeyEventData : : KeyPos_None ) ? C4VNull : C4VInt ( x ) ;
C4Value vy = ( y = = C4KeyEventData : : KeyPos_None ) ? C4VNull : C4VInt ( y ) ;
2011-11-20 20:49:38 +00:00
// exec control function
2016-02-15 15:20:37 +00:00
C4AulParSet Pars ( iPlr , iControl , C4Id2Def ( idControlExtraData ) , vx , vy , rKeyExtraData . iStrength , fRepeated , C4VInt ( state ) ) ;
2011-10-15 20:07:45 +00:00
return : : ScriptEngine . GetPropList ( ) - > Call ( PSF_PlayerControl , & Pars ) . getBool ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-26 06:10:38 +00:00
void C4PlayerControl : : Execute ( )
2009-10-17 15:53:26 +00:00
{
2009-05-28 00:14:54 +00:00
// sync execution: Do keyrepeat
2010-04-21 19:12:49 +00:00
for ( size_t i = 0 ; i < ControlDefs . GetCount ( ) ; + + i )
2009-10-17 15:53:26 +00:00
{
2009-05-28 00:14:54 +00:00
const CSync : : ControlDownState * pControlDownState = Sync . GetControlDownState ( i ) ;
if ( pControlDownState & & pControlDownState - > IsDown ( ) )
2009-10-17 15:53:26 +00:00
{
2009-05-28 00:14:54 +00:00
const C4PlayerControlDef * pCtrlDef = ControlDefs . GetControlByIndex ( i ) ;
assert ( pCtrlDef ) ;
int32_t iCtrlRepeatDelay = pCtrlDef - > GetRepeatDelay ( ) ;
if ( iCtrlRepeatDelay )
2009-10-17 15:53:26 +00:00
{
2009-05-28 00:14:54 +00:00
int32_t iFrameDiff = Game . FrameCounter - pControlDownState - > iDownFrame ;
int32_t iCtrlInitialRepeatDelay = pCtrlDef - > GetInitialRepeatDelay ( ) ;
if ( iFrameDiff & & iFrameDiff > = iCtrlInitialRepeatDelay )
2009-10-17 15:53:26 +00:00
{
2009-05-28 00:14:54 +00:00
if ( ! ( ( iFrameDiff - iCtrlInitialRepeatDelay ) % iCtrlRepeatDelay ) )
2009-10-17 15:53:26 +00:00
{
2009-05-28 00:14:54 +00:00
// it's RepeatTime for this key!
2016-02-15 15:20:37 +00:00
ExecuteControlAction ( i , pCtrlDef - > GetAction ( ) , pCtrlDef - > GetExtraData ( ) , CONS_Down , pControlDownState - > DownState , true ) ;
2009-05-28 00:14:54 +00:00
}
}
}
}
2009-10-17 15:53:26 +00:00
}
2009-05-28 00:14:54 +00:00
// cleanup old recent keys
2013-12-07 14:27:01 +00:00
C4TimeMilliseconds tNow = C4TimeMilliseconds : : Now ( ) ;
2009-05-28 00:14:54 +00:00
C4PlayerControlRecentKeyList : : iterator irk ;
for ( irk = RecentKeys . begin ( ) ; irk ! = RecentKeys . end ( ) ; + + irk )
2009-10-17 15:53:26 +00:00
{
2009-05-28 00:14:54 +00:00
C4PlayerControlRecentKey & rk = * irk ;
if ( rk . tTime + MaxRecentKeyLookback > tNow ) break ;
2009-05-26 06:10:38 +00:00
}
2009-10-17 15:53:26 +00:00
if ( irk ! = RecentKeys . begin ( ) ) RecentKeys . erase ( RecentKeys . begin ( ) , irk ) ;
}
2009-05-26 06:10:38 +00:00
2016-11-02 23:58:02 +00:00
C4PlayerControl : : C4PlayerControl ( ) : ControlDefs ( Game . PlayerControlDefs ) , iPlr ( - 1 ) , pControlSet ( nullptr ) , IsCursorPosRequested ( false )
2009-10-17 15:53:26 +00:00
{
}
2009-05-26 06:10:38 +00:00
void C4PlayerControl : : Clear ( )
2009-10-17 15:53:26 +00:00
{
2009-05-28 00:14:54 +00:00
iPlr = NO_OWNER ;
2016-11-02 23:58:02 +00:00
pControlSet = nullptr ;
2009-05-28 00:14:54 +00:00
for ( KeyBindingList : : iterator i = KeyBindings . begin ( ) ; i ! = KeyBindings . end ( ) ; + + i ) delete * i ;
KeyBindings . clear ( ) ;
RecentKeys . clear ( ) ;
DownKeys . clear ( ) ;
Sync . Clear ( ) ;
2010-05-07 13:57:56 +00:00
IsCursorPosRequested = false ;
2009-10-17 15:53:26 +00:00
}
2009-05-26 06:10:38 +00:00
void C4PlayerControl : : RegisterKeyset ( int32_t iPlr , C4PlayerControlAssignmentSet * pKeyset )
2009-10-17 15:53:26 +00:00
{
2009-05-28 00:14:54 +00:00
// setup
pControlSet = pKeyset ;
this - > iPlr = iPlr ;
2009-05-26 06:10:38 +00:00
// register all keys into Game.KeyboardInput creating KeyBindings
2009-05-28 00:14:54 +00:00
if ( pControlSet )
2009-10-17 15:53:26 +00:00
{
2009-05-28 00:14:54 +00:00
C4KeyCodeExVec RegularKeys , HoldKeys ;
pControlSet - > GetTriggerKeys ( ControlDefs , & RegularKeys , & HoldKeys ) ;
int32_t idx = 0 ;
for ( C4KeyCodeExVec : : const_iterator i = RegularKeys . begin ( ) ; i ! = RegularKeys . end ( ) ; + + i ) AddKeyBinding ( * i , false , idx + + ) ;
for ( C4KeyCodeExVec : : const_iterator i = HoldKeys . begin ( ) ; i ! = HoldKeys . end ( ) ; + + i ) AddKeyBinding ( * i , true , idx + + ) ;
2009-05-26 06:10:38 +00:00
}
2009-10-17 15:53:26 +00:00
}
2009-05-26 06:10:38 +00:00
2009-05-28 00:14:54 +00:00
void C4PlayerControl : : AddKeyBinding ( const C4KeyCodeEx & key , bool fHoldKey , int32_t idx )
2009-10-17 15:53:26 +00:00
{
2009-06-09 23:29:16 +00:00
KeyBindings . push_back ( new C4KeyBinding (
2010-03-28 18:58:01 +00:00
key , FormatString ( " PlrKey%02d " , idx ) . getData ( ) , KEYSCOPE_Control ,
2016-11-02 23:58:02 +00:00
new C4KeyCBExPassKey < C4PlayerControl , C4KeyCodeEx > ( * this , key , & C4PlayerControl : : ProcessKeyDown , fHoldKey ? & C4PlayerControl : : ProcessKeyUp : nullptr , nullptr , fHoldKey ? & C4PlayerControl : : ProcessKeyMoved : nullptr ) ,
2010-03-28 18:58:01 +00:00
C4CustomKey : : PRIO_PlrControl ) ) ;
2009-10-17 15:53:26 +00:00
}
2009-12-31 17:20:45 +00:00
bool C4PlayerControl : : DoMouseInput ( uint8_t mouse_id , int32_t mouseevent , float game_x , float game_y , float gui_x , float gui_y , bool is_ctrl_down , bool is_shift_down , bool is_alt_down , int wheel_dir )
2009-10-17 15:53:26 +00:00
{
// convert moueevent to key code
uint8_t mouseevent_code ;
C4KeyCodeEx mouseevent_keycode ;
2009-10-17 20:20:36 +00:00
bool is_down = true ;
2009-10-17 15:53:26 +00:00
switch ( mouseevent )
{
2010-03-28 18:58:01 +00:00
case C4MC_Button_None : mouseevent_code = KEY_MOUSE_Move ; break ;
case C4MC_Button_LeftUp : is_down = false ; // nobreak
case C4MC_Button_LeftDown : mouseevent_code = KEY_MOUSE_ButtonLeft ; break ;
2016-08-18 20:43:42 +00:00
case C4MC_Button_LeftDouble : mouseevent_code = KEY_MOUSE_ButtonLeftDouble ; break ;
2010-03-28 18:58:01 +00:00
case C4MC_Button_RightUp : is_down = false ; // nobreak
case C4MC_Button_RightDown : mouseevent_code = KEY_MOUSE_ButtonRight ; break ;
case C4MC_Button_RightDouble : mouseevent_code = KEY_MOUSE_ButtonRightDouble ; break ;
case C4MC_Button_MiddleUp : is_down = false ; // nobreak
case C4MC_Button_MiddleDown : mouseevent_code = KEY_MOUSE_ButtonMiddle ; break ;
2016-08-18 20:43:42 +00:00
case C4MC_Button_MiddleDouble : mouseevent_code = KEY_MOUSE_ButtonMiddleDouble ; break ;
case C4MC_Button_X1Up : is_down = false ; // nobreak
case C4MC_Button_X1Down : mouseevent_code = KEY_MOUSE_ButtonX1 ; break ;
case C4MC_Button_X1Double : mouseevent_code = KEY_MOUSE_ButtonX1Double ; break ;
case C4MC_Button_X2Up : is_down = false ; // nobreak
case C4MC_Button_X2Down : mouseevent_code = KEY_MOUSE_ButtonX2 ; break ;
case C4MC_Button_X2Double : mouseevent_code = KEY_MOUSE_ButtonX2Double ; break ;
2010-03-28 18:58:01 +00:00
case C4MC_Button_Wheel :
if ( ! wheel_dir ) return false ;
mouseevent_code = ( wheel_dir > 0 ) ? KEY_MOUSE_Wheel1Up : KEY_MOUSE_Wheel1Down ; break ;
default : assert ( false ) ; return false ;
2009-10-17 15:53:26 +00:00
}
// compose keycode
if ( is_ctrl_down ) mouseevent_keycode . dwShift | = KEYS_Control ;
if ( is_shift_down ) mouseevent_keycode . dwShift | = KEYS_Shift ;
if ( is_alt_down ) mouseevent_keycode . dwShift | = KEYS_Alt ;
2011-11-20 20:49:38 +00:00
mouseevent_keycode . Key = KEY_Mouse ( mouse_id , mouseevent_code ) ;
2009-10-17 15:53:26 +00:00
// first, try processing it as GUI mouse event. if not assigned, process as Game mous event
// TODO: May route this through Game.DoKeyboardInput instead - would allow assignment of mouse events in CustomConfig
// and would get rid of the Game.KeyboardInput.SetLastKeyExtraData-hack
C4KeyEventData mouseevent_data ;
2009-10-17 20:20:36 +00:00
mouseevent_data . iStrength = 100 * is_down ; // TODO: May get pressure from tablet here
2011-11-20 20:49:38 +00:00
mouseevent_data . vp_x = uint32_t ( gui_x ) ;
mouseevent_data . vp_y = uint32_t ( gui_y ) ;
mouseevent_data . game_x = uint32_t ( game_x ) ;
mouseevent_data . game_y = uint32_t ( game_y ) ;
2009-10-17 15:53:26 +00:00
Game . KeyboardInput . SetLastKeyExtraData ( mouseevent_data ) ; // ProcessKeyDown/Up queries it from there...
bool result ;
if ( is_down )
result = ProcessKeyDown ( mouseevent_keycode , mouseevent_keycode ) ;
else
result = ProcessKeyUp ( mouseevent_keycode , mouseevent_keycode ) ;
return result ;
}
2010-04-10 20:44:00 +00:00
2011-11-20 20:49:38 +00:00
bool C4PlayerControl : : GetCurrentPlayerCursorPos ( int32_t * x_out , int32_t * y_out , int32_t * game_x_out , int32_t * game_y_out )
2010-04-10 20:44:00 +00:00
{
// prefer mouse position if this is a mouse control
if ( pControlSet & & pControlSet - > HasMouse ( ) )
{
2016-08-07 15:08:42 +00:00
if ( MouseControl . GetLastCursorPos ( x_out , y_out , game_x_out , game_y_out ) )
{
2010-04-10 20:44:00 +00:00
return true ;
2016-08-07 15:08:42 +00:00
}
2010-04-10 20:44:00 +00:00
// if getting the mouse position failed, better fall back to cursor pos
}
// no mouse position known. Use cursor.
C4Player * plr = Players . Get ( iPlr ) ;
if ( ! plr ) return false ;
C4Object * cursor_obj = plr - > Cursor ;
if ( ! cursor_obj ) return false ;
2010-09-29 01:44:05 +00:00
C4Viewport * vp = : : Viewports . GetViewport ( iPlr ) ;
2010-04-11 11:42:07 +00:00
if ( ! vp ) return false ;
2010-04-10 20:44:00 +00:00
int32_t game_x = cursor_obj - > GetX ( ) , game_y = cursor_obj - > GetY ( ) ;
2011-11-20 20:49:38 +00:00
* game_x_out = game_x ; * game_y_out = game_y ;
2010-04-10 20:44:00 +00:00
// game coordinate to screen coordinates...
2010-04-26 23:32:45 +00:00
float screen_x = ( float ( game_x ) - vp - > last_game_draw_cgo . TargetX - vp - > last_game_draw_cgo . X ) * vp - > GetZoom ( ) ;
float screen_y = ( float ( game_y ) - vp - > last_game_draw_cgo . TargetY - vp - > last_game_draw_cgo . Y ) * vp - > GetZoom ( ) ;
2010-04-10 20:44:00 +00:00
// ...and screen coordinates to GUI coordinates (might push this into a helper function of C4Viewport?)
float gui_x = ( screen_x - vp - > last_game_draw_cgo . X ) / C4GUI : : GetZoom ( ) + vp - > last_game_draw_cgo . X ;
float gui_y = ( screen_y - vp - > last_game_draw_cgo . Y ) / C4GUI : : GetZoom ( ) + vp - > last_game_draw_cgo . Y ;
* x_out = int32_t ( gui_x ) ; * y_out = int32_t ( gui_y ) ;
return true ;
2010-05-07 13:57:56 +00:00
}
void C4PlayerControl : : PrepareInput ( )
{
if ( IsCursorPosRequested )
{
2011-11-20 20:49:38 +00:00
int32_t x , y , game_x , game_y ;
2010-05-07 13:57:56 +00:00
// add current cursor pos in GUI coordinates to input
2011-11-20 20:49:38 +00:00
if ( GetCurrentPlayerCursorPos ( & x , & y , & game_x , & game_y ) )
2010-05-07 13:57:56 +00:00
{
// CON_CursorPos might not have been defined in definition file
if ( ControlDefs . InternalCons . CON_CursorPos ! = CON_None )
{
C4KeyEventData ev ;
ev . iStrength = 0 ;
2011-11-20 20:49:38 +00:00
ev . vp_x = x ; ev . vp_y = y ;
ev . game_x = game_x ; ev . game_y = game_y ;
2016-02-15 15:20:37 +00:00
C4ControlPlayerControl * pControlPacket = new C4ControlPlayerControl ( iPlr , CONS_Down , ev ) ;
2010-05-07 13:57:56 +00:00
pControlPacket - > AddControl ( ControlDefs . InternalCons . CON_CursorPos , C4PlayerControlAssignment : : CTM_Default ) ;
// make sure it's added at head, because controls that have SendCursorPos=1 set will follow, which will rely
// on the cursor pos being known
Game . Input . AddHead ( CID_PlrControl , pControlPacket ) ;
}
}
else
{
// no cursor is known (e.g.: Cursor Clonk dead, etc.). Don't create a control.
// Script will probably fall back to last known cursor pos
}
IsCursorPosRequested = false ;
}
2010-12-23 00:01:24 +00:00
}