2009-05-25 09:01:26 +00:00
/*
* OpenClonk , http : //www.openclonk.org
*
* Copyright ( c ) 2005 - 2009 , RedWolf Design GmbH , http : //www.clonk.de
*
* Portions might be copyrighted by other authors who have contributed
* to OpenClonk .
*
* Permission to use , copy , modify , and / or distribute this software for any
* purpose with or without fee is hereby granted , provided that the above
* copyright notice and this permission notice appear in all copies .
* See isc_license . txt for full license and disclaimer .
*
* " Clonk " is a registered trademark of Matthes Bender .
* See clonk_trademark_license . txt for full license .
*/
// Input to player control mapping
# include <C4Include.h>
# include <C4PlayerControl.h>
2009-05-27 14:08:46 +00:00
# ifndef BIG_C4INCLUDE
# include <C4LangStringTable.h>
# include <C4Player.h>
2009-08-10 14:48:25 +00:00
# include <C4PlayerList.h>
2009-05-27 14:08:46 +00:00
# include <C4Control.h>
# include <C4Game.h>
2009-08-10 14:48:25 +00:00
# include <C4Log.h>
2009-05-27 14:08:46 +00:00
# endif
2009-05-25 09:01:26 +00:00
/* C4PlayerControlDef */
void C4PlayerControlDef : : CompileFunc ( StdCompiler * pComp )
{
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 " ) ) ;
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( sGUIName , StdCompiler : : RCT_All ) , " GUIName " , " undefined " ) ) ;
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 ) ) ;
2009-05-26 06:10:38 +00:00
pComp - > Value ( mkNamingAdapt ( mkC4IDAdapt ( idControlExtraData ) , " ExtraData " , C4ID_None ) ) ;
const StdEnumEntry < Actions > ActionNames [ ] = {
{ " 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 } ,
{ NULL , CDA_None } } ;
pComp - > Value ( mkNamingAdapt ( mkEnumAdapt < Actions , int32_t > ( eAction , ActionNames ) , " Action " , CDA_Script ) ) ;
2009-05-25 09:01:26 +00:00
pComp - > NameEnd ( ) ;
}
bool C4PlayerControlDef : : operator = = ( const C4PlayerControlDef & cmp ) const
{
return sIdentifier = = cmp . sIdentifier
& & sGUIName = = cmp . sGUIName
& & sGUIDesc = = cmp . sGUIDesc
2009-05-26 06:10:38 +00:00
& & fGlobal = = cmp . fGlobal
2009-05-25 09:01:26 +00:00
& & fIsHoldKey = = cmp . fIsHoldKey
2009-05-26 06:10:38 +00:00
& & iRepeatDelay = = cmp . iRepeatDelay
& & iInitialRepeatDelay = = cmp . iInitialRepeatDelay
& & fDefaultDisabled = = cmp . fDefaultDisabled
& & idControlExtraData = = cmp . idControlExtraData
& & eAction = = cmp . eAction ;
2009-05-25 09:01:26 +00:00
}
/* C4PlayerControlDefs */
2009-05-28 00:14:54 +00:00
void C4PlayerControlDefs : : UpdateInternalCons ( )
{
InternalCons . CON_MenuSelect = GetControlIndexByIdentifier ( " MenuSelect " ) ;
InternalCons . CON_MenuEnter = GetControlIndexByIdentifier ( " MenuEnter " ) ;
InternalCons . CON_MenuEnterAll = GetControlIndexByIdentifier ( " MenuEnterAll " ) ;
InternalCons . CON_MenuClose = GetControlIndexByIdentifier ( " MenuClose " ) ;
}
2009-05-26 06:10:38 +00:00
void C4PlayerControlDefs : : Clear ( )
{
Defs . clear ( ) ;
2009-05-28 00:14:54 +00:00
UpdateInternalCons ( ) ;
2009-05-26 06:10:38 +00:00
}
2009-05-25 09:01:26 +00:00
void C4PlayerControlDefs : : CompileFunc ( StdCompiler * pComp )
{
pComp - > Value ( mkNamingAdapt ( mkSTLContainerAdapt ( Defs , StdCompiler : : SEP_NONE ) , " ControlDefs " , DefVecImpl ( ) ) ) ;
2009-05-28 00:14:54 +00:00
if ( pComp - > isCompiler ( ) ) UpdateInternalCons ( ) ;
2009-05-25 09:01:26 +00:00
}
void C4PlayerControlDefs : : MergeFrom ( const C4PlayerControlDefs & Src )
{
// 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 )
{
const C4PlayerControlDef & SrcDef = * i ;
// overwrite if def of same name existed
int32_t iPrevIdx = GetControlIndexByIdentifier ( SrcDef . GetIdentifier ( ) ) ;
if ( iPrevIdx ! = CON_None )
{
Defs [ iPrevIdx ] = SrcDef ;
}
else
{
// new def: Append a copy
Defs . push_back ( SrcDef ) ;
}
}
2009-05-28 00:14:54 +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
2009-05-25 09:01:26 +00:00
{
// safe index
2009-08-10 14:48:25 +00:00
if ( idx < 0 | | idx > = int32_t ( Defs . size ( ) ) ) return NULL ;
2009-05-25 09:01:26 +00:00
return & ( Defs [ idx ] ) ;
}
int32_t C4PlayerControlDefs : : GetControlIndexByIdentifier ( const char * szIdentifier ) const
{
for ( DefVecImpl : : const_iterator i = Defs . begin ( ) ; i ! = Defs . end ( ) ; + + i )
if ( SEqual ( ( * i ) . GetIdentifier ( ) , szIdentifier ) )
return i - Defs . begin ( ) ;
return CON_None ;
}
2009-06-09 23:29:16 +00:00
void C4PlayerControlDefs : : FinalInit ( )
{
// Assume all defs have been loaded
// Register scritp constants
for ( DefVecImpl : : const_iterator i = Defs . begin ( ) ; i ! = Defs . end ( ) ; + + i )
{
const char * szIdtf = ( * i ) . GetIdentifier ( ) ;
if ( szIdtf & & * szIdtf & & ! SEqual ( szIdtf , " None " ) )
{
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
}
}
}
2009-05-25 09:01:26 +00:00
/* C4PlayerControlAssignment */
void C4PlayerControlAssignment : : KeyComboItem : : CompileFunc ( StdCompiler * pComp )
{
// if key is compiled, also store as a string into KeyName for later resolving
if ( pComp - > isCompiler ( ) )
{
sKeyName . Clear ( ) ;
pComp - > Value ( mkParAdapt ( Key , & sKeyName ) ) ;
if ( ! sKeyName )
{
// key was not assigned during compilation - this means it's a regular key (or undefined)
// store this as the name
sKeyName . Copy ( Key . ToString ( false , false ) ) ;
}
}
else
{
// decompiler: Just write the stored key name; regardless of whether it's a key, undefined or a reference
pComp - > Value ( mkParAdapt ( sKeyName , StdCompiler : : RCT_Idtf ) ) ;
}
}
void C4PlayerControlAssignment : : CompileFunc ( StdCompiler * pComp )
{
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 " ) ) ;
2009-05-26 06:10:38 +00:00
pComp - > Value ( mkNamingAdapt ( iPriority , " Priority " , 0 ) ) ;
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 } ,
{ NULL , 0 } } ;
pComp - > Value ( mkNamingAdapt ( mkBitfieldAdapt < int32_t > ( iTriggerMode , TriggerModeNames ) , " TriggerMode " , CTM_Default ) ) ;
2009-05-25 09:01:26 +00:00
pComp - > NameEnd ( ) ;
2009-05-26 06:10:38 +00:00
// newly loaded structures are not resolved
if ( pComp - > isCompiler ( ) ) fRefsResolved = false ;
}
bool C4PlayerControlAssignment : : ResolveRefs ( C4PlayerControlAssignmentSet * pParentSet , C4PlayerControlDefs * pControlDefs )
{
// avoid circular chains
static C4PlayerControlAssignment * pCircularDetect = NULL ;
if ( ! pCircularDetect ) pCircularDetect = this ; else if ( pCircularDetect = = this )
{
LogFatal ( FormatString ( " Circular reference chain detected in player control assignments of set %s in assignment for key %s! " , pParentSet - > GetName ( ) , GetControlName ( ) ) . getData ( ) ) ;
return false ;
}
// resolve control name
iControl = pControlDefs - > GetControlIndexByIdentifier ( sControlName . getData ( ) ) ;
// resolve keys
KeyComboVec NewCombo ;
for ( KeyComboVec : : iterator i = KeyCombo . begin ( ) ; i ! = KeyCombo . end ( ) ; + + i )
{
KeyComboItem & rKeyComboItem = * i ;
if ( rKeyComboItem . Key = = KEY_Default & & rKeyComboItem . sKeyName . getLength ( ) )
{
2009-06-16 02:48:53 +00:00
// this is a key reference
// it may be preceded by CON_ to avoid ambigous keus
const char * szKeyName = rKeyComboItem . sKeyName . getData ( ) ;
if ( SEqual2 ( szKeyName , " CON_ " ) ) szKeyName + = 4 ;
// - find it
C4PlayerControlAssignment * pRefAssignment = pParentSet - > GetAssignmentByControlName ( szKeyName ) ;
2009-05-26 06:10:38 +00:00
if ( pRefAssignment )
{
// resolve itself if necessary
if ( ! pRefAssignment - > IsRefsResolved ( ) ) if ( ! pRefAssignment - > ResolveRefs ( pParentSet , pControlDefs ) ) return false ;
// insert all keys of that combo into own combo
NewCombo . insert ( NewCombo . end ( ) , pRefAssignment - > KeyCombo . begin ( ) , pRefAssignment - > KeyCombo . end ( ) ) ;
}
else
{
// 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 ( ) ;
}
}
else
{
NewCombo . push_back ( rKeyComboItem ) ;
}
}
KeyCombo = NewCombo ;
// the trigger key is always last of the chain
if ( KeyCombo . size ( ) ) TriggerKey = KeyCombo . back ( ) . Key ; else TriggerKey = C4KeyCodeEx ( ) ;
// done
fRefsResolved = true ;
if ( pCircularDetect = = this ) pCircularDetect = NULL ;
return true ;
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
{
assert ( HasCombo ( ) ) ;
// check if combo is currently fulfilled (assuming TriggerKey is already matched)
if ( fComboIsSequence )
{
DWORD tKeyLast = timeGetTime ( ) ;
// 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
C4PlayerControlRecentKeyList : : const_reverse_iterator ri = RecentKeys . rbegin ( ) ;
for ( KeyComboVec : : const_reverse_iterator i = KeyCombo . rbegin ( ) + 1 ; i ! = KeyCombo . rend ( ) ; + + i , + + ri )
{
// 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?
DWORD tKeyRecent = rk . tTime ;
if ( tKeyLast - tKeyRecent > C4PlayerControl : : MaxSequenceKeyDelay ) return false ;
// key doesn't match?
const KeyComboItem & k = * i ;
if ( ! ( rk . Key = = k . Key ) ) return false ;
// key OK
}
}
else
{
// 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 )
{
const KeyComboItem & k = * i ;
bool fFound = false ;
for ( C4PlayerControlRecentKeyList : : const_iterator di = DownKeys . begin ( ) ; di ! = DownKeys . end ( ) ; + + di )
{
const C4PlayerControlRecentKey & dk = * di ;
if ( dk . Key = = k . Key ) { fFound = true ; break ; }
}
if ( ! fFound ) return false ;
}
}
// combo OK!
return true ;
}
2009-05-25 09:01:26 +00:00
bool C4PlayerControlAssignment : : operator = = ( const C4PlayerControlAssignment & cmp ) const
{
// doesn't compare resolved TriggerKey/iControl
return KeyCombo = = cmp . KeyCombo
& & sControlName = = cmp . sControlName
2009-05-26 06:10:38 +00:00
& & iTriggerMode = = cmp . iTriggerMode
& & iPriority = = cmp . iPriority ;
2009-05-25 09:01:26 +00:00
}
/* C4PlayerControlAssignmentSet */
void C4PlayerControlAssignmentSet : : CompileFunc ( StdCompiler * pComp )
{
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
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 ( ) ;
}
void C4PlayerControlAssignmentSet : : MergeFrom ( const C4PlayerControlAssignmentSet & Src , bool fLowPrio )
{
// 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 )
2009-05-25 09:01:26 +00:00
{
const C4PlayerControlAssignment & SrcAssignment = * i ;
2009-06-16 02:48:53 +00:00
// overwrite if def of same name existed if it's not low priority anyway?
// not so easy. Keys may be assigned to multiple controls and we may need to overwrite one or more of them...
2009-05-25 09:01:26 +00:00
C4PlayerControlAssignment * pPrevAssignment = GetAssignmentByControlName ( SrcAssignment . GetControlName ( ) ) ;
if ( pPrevAssignment )
{
if ( ! fLowPrio ) * pPrevAssignment = SrcAssignment ;
}
else
{
// new def: Append a copy
Assignments . push_back ( SrcAssignment ) ;
}
}
}
2009-05-26 06:10:38 +00:00
bool C4PlayerControlAssignmentSet : : ResolveRefs ( C4PlayerControlDefs * pDefs )
{
// 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 ;
// now sort assignments by priority
std : : sort ( Assignments . begin ( ) , Assignments . end ( ) ) ;
return true ;
}
C4PlayerControlAssignment * C4PlayerControlAssignmentSet : : GetAssignmentByControlName ( const char * szControlName )
2009-05-25 09:01:26 +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 ;
2009-05-25 09:01:26 +00:00
return NULL ;
}
2009-05-26 06:10:38 +00:00
bool C4PlayerControlAssignmentSet : : operator = = ( const C4PlayerControlAssignmentSet & cmp ) const
{
return Assignments = = cmp . Assignments
& & sName = = cmp . sName ;
}
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
{
assert ( pOutVec ) ;
// primary match by TriggerKey
for ( C4PlayerControlAssignmentVec : : const_iterator i = Assignments . begin ( ) ; i ! = Assignments . end ( ) ; + + i )
{
const C4PlayerControlAssignment & rAssignment = * i ;
if ( ! ( rAssignment . GetTriggerKey ( ) = = key ) ) continue ;
// check linked control def
const C4PlayerControlDef * pCtrl = rDefs . GetControlByIndex ( rAssignment . GetControl ( ) ) ;
if ( ! pCtrl ) continue ;
// only want hold keys?
if ( fHoldKeysOnly )
{
// 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 ;
}
else if ( rAssignment . HasCombo ( ) )
{
// 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 ;
}
// we got match! Store it
pOutVec - > push_back ( & rAssignment ) ;
}
}
void C4PlayerControlAssignmentSet : : GetTriggerKeys ( const C4PlayerControlDefs & rDefs , C4KeyCodeExVec * pRegularKeys , C4KeyCodeExVec * pHoldKeys ) const
{
// put all trigger keys of keyset into output vectors
// first all hold keys
for ( C4PlayerControlAssignmentVec : : const_iterator i = Assignments . begin ( ) ; i ! = Assignments . end ( ) ; + + i )
{
const C4PlayerControlAssignment & rAssignment = * i ;
const C4PlayerControlDef * pDef = rDefs . GetControlByIndex ( rAssignment . GetControl ( ) ) ;
if ( pDef & & pDef - > IsHoldKey ( ) )
{
const C4KeyCodeEx & rKey = rAssignment . GetTriggerKey ( ) ;
if ( std : : find ( pHoldKeys - > begin ( ) , pHoldKeys - > end ( ) , rKey ) = = pHoldKeys - > end ( ) ) pHoldKeys - > push_back ( rKey ) ;
}
}
// then all regular keys that aren't in the hold keys list yet
for ( C4PlayerControlAssignmentVec : : const_iterator i = Assignments . begin ( ) ; i ! = Assignments . end ( ) ; + + i )
{
const C4PlayerControlAssignment & rAssignment = * i ;
const C4PlayerControlDef * pDef = rDefs . GetControlByIndex ( rAssignment . GetControl ( ) ) ;
if ( pDef & & ! pDef - > IsHoldKey ( ) )
{
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 ) ;
}
}
}
2009-05-25 09:01:26 +00:00
/* C4PlayerControlAssignmentSets */
2009-05-26 06:10:38 +00:00
void C4PlayerControlAssignmentSets : : Clear ( )
{
Sets . clear ( ) ;
}
void C4PlayerControlAssignmentSets : : CompileFunc ( StdCompiler * pComp )
{
pComp - > Value ( mkNamingAdapt ( mkSTLContainerAdapt ( Sets , StdCompiler : : SEP_NONE ) , " ControlSets " , AssignmentSetList ( ) ) ) ;
}
void C4PlayerControlAssignmentSets : : MergeFrom ( const C4PlayerControlAssignmentSets & Src , bool fLowPrio )
{
// 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 )
{
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 )
2009-05-26 06:10:38 +00:00
{
2009-06-16 00:38:39 +00:00
C4PlayerControlAssignmentSet * pPrevSet = GetSetByName ( SrcSet . GetName ( ) ) ;
if ( pPrevSet )
{
pPrevSet - > MergeFrom ( SrcSet , fLowPrio ) ;
}
else
{
// new def: Append a copy
Sets . push_back ( SrcSet ) ;
}
2009-05-26 06:10:38 +00:00
}
else
{
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 )
{
C4PlayerControlAssignmentSet & DstSet = * j ;
if ( WildcardMatch ( SrcSet . GetName ( ) , DstSet . GetName ( ) ) )
{
DstSet . MergeFrom ( SrcSet , fLowPrio ) ;
}
}
2009-05-26 06:10:38 +00:00
}
}
}
bool C4PlayerControlAssignmentSets : : ResolveRefs ( C4PlayerControlDefs * pDefs )
{
for ( AssignmentSetList : : iterator i = Sets . begin ( ) ; i ! = Sets . end ( ) ; + + i )
if ( ! ( * i ) . ResolveRefs ( pDefs ) ) return false ;
return true ;
}
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 ;
return NULL ;
2009-10-13 20:02:44 +00:00
}
2009-05-26 06:10:38 +00:00
/* C4PlayerControlFile */
void C4PlayerControlFile : : CompileFunc ( StdCompiler * pComp )
{
pComp - > Value ( ControlDefs ) ;
pComp - > Value ( AssignmentSets ) ;
}
bool C4PlayerControlFile : : Load ( C4Group & hGroup , const char * szFilename , C4LangStringTable * pLang )
{
// clear previous
Clear ( ) ;
// load and prepare file contents
StdStrBuf Buf ;
if ( ! hGroup . LoadEntryString ( szFilename , Buf ) ) return false ;
if ( pLang ) pLang - > ReplaceStrings ( Buf ) ;
// parse it!
if ( ! CompileFromBuf_LogWarn < StdCompilerINIRead > ( * this , Buf , szFilename ) ) return false ;
return true ;
}
bool C4PlayerControlFile : : Save ( C4Group & hGroup , const char * szFilename )
{
// 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 ;
}
void C4PlayerControlFile : : Clear ( )
{
ControlDefs . Clear ( ) ;
AssignmentSets . Clear ( ) ;
}
/* C4PlayerControl */
2009-05-28 00:14:54 +00:00
void C4PlayerControl : : CSync : : ControlDownState : : CompileFunc ( StdCompiler * pComp )
{
pComp - > Value ( DownState ) ;
pComp - > Seperator ( ) ;
pComp - > Value ( iDownFrame ) ;
pComp - > Seperator ( ) ;
pComp - > Value ( fDownByUser ) ;
}
bool C4PlayerControl : : CSync : : ControlDownState : : operator = = ( const ControlDownState & cmp ) const
{
return DownState = = cmp . DownState & & iDownFrame = = cmp . iDownFrame & & fDownByUser = = cmp . fDownByUser ;
}
const C4PlayerControl : : CSync : : ControlDownState * C4PlayerControl : : CSync : : GetControlDownState ( int32_t iControl ) const
{
// safe access
2009-08-10 14:48:25 +00:00
if ( iControl < 0 | | iControl > = int32_t ( ControlDownStates . size ( ) ) ) return NULL ;
2009-05-28 00:14:54 +00:00
return & ControlDownStates [ iControl ] ;
}
int32_t C4PlayerControl : : CSync : : GetControlDisabled ( int32_t iControl ) const
{
// 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 ] ;
}
void C4PlayerControl : : CSync : : SetControlDownState ( int32_t iControl , const C4KeyEventData & rDownState , int32_t iDownFrame , bool fDownByUser )
{
// 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 ;
}
void C4PlayerControl : : CSync : : SetControlDisabled ( int32_t iControl , int32_t iVal )
{
// disable control
if ( iControl < 0 ) return ;
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
const ControlDownState * pDownState = GetControlDownState ( iControl ) ;
if ( pDownState & & pDownState - > IsDown ( ) )
{
C4KeyEventData KeyDownState = pDownState - > DownState ;
KeyDownState . iStrength = 0 ;
SetControlDownState ( iControl , KeyDownState , 0 , false ) ;
}
}
void C4PlayerControl : : CSync : : Clear ( )
{
ControlDownStates . clear ( ) ;
ControlDisableStates . clear ( ) ;
}
2009-05-26 06:10:38 +00:00
void C4PlayerControl : : CSync : : CompileFunc ( StdCompiler * pComp )
{
pComp - > Value ( mkNamingAdapt ( mkSTLContainerAdapt ( ControlDownStates ) , " Down " , DownStateVec ( ) ) ) ;
pComp - > Value ( mkNamingAdapt ( mkSTLContainerAdapt ( ControlDisableStates ) , " Disabled " , DisableStateVec ( ) ) ) ;
}
bool C4PlayerControl : : CSync : : operator = = ( const CSync & cmp ) const
{
return ControlDownStates = = cmp . ControlDownStates
& & ControlDisableStates = = cmp . ControlDisableStates ;
}
void C4PlayerControl : : CompileFunc ( StdCompiler * pComp )
{
// compile sync values only
pComp - > Value ( mkNamingAdapt ( Sync , " PlayerControl " , CSync ( ) ) ) ;
}
bool C4PlayerControl : : ProcessKeyEvent ( const C4KeyCodeEx & key , bool fUp , const C4KeyEventData & rKeyExtraData )
{
// 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
pControlSet - > GetAssignmentsByKey ( ControlDefs , key , fUp , & Matches , DownKeys , RecentKeys ) ;
2009-05-26 06:10:38 +00:00
// process async controls
2009-05-28 00:14:54 +00:00
C4ControlPlayerControl * pControlPacket = NULL ;
for ( C4PlayerControlAssignmentPVec : : const_iterator i = Matches . begin ( ) ; i ! = Matches . end ( ) ; + + i )
2009-05-26 06:10:38 +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 ) ;
2009-05-26 06:10:38 +00:00
if ( pControlDef & & pControlDef - > IsValid ( ) & & ( ! fUp | | pControlDef - > IsHoldKey ( ) ) )
{
if ( pControlDef - > IsAsync ( ) & & ! pControlPacket )
{
2009-05-28 00:14:54 +00:00
if ( ExecuteControl ( iControlIndex , fUp , rKeyExtraData , pAssignment - > GetTriggerMode ( ) , key . IsRepeated ( ) ) )
2009-05-26 06:10:38 +00:00
return true ;
}
else
{
// sync control
// ignore key repeats, because we do our own key repeat for sync controls
if ( key . IsRepeated ( ) ) return false ;
// 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
2009-05-28 00:14:54 +00:00
if ( ! pControlPacket ) pControlPacket = new C4ControlPlayerControl ( iPlr , fUp , rKeyExtraData ) ;
pControlPacket - > AddControl ( iControlIndex , pAssignment - > GetTriggerMode ( ) ) ;
2009-05-26 06:10:38 +00:00
}
}
}
// push sync control to input
2009-05-28 00:14:54 +00:00
if ( pControlPacket )
{
Game . Input . Add ( CID_PlrControl2 , pControlPacket ) ;
// assume processed (although we can't really know that yet)
return true ;
}
return false ;
2009-05-26 06:10:38 +00:00
}
bool C4PlayerControl : : ProcessKeyDown ( const C4KeyCodeEx & key )
{
// add key to local "down" list if it's not already in there
2009-05-28 00:14:54 +00:00
C4PlayerControlRecentKey RKey ( key , timeGetTime ( ) ) ;
if ( std : : find ( DownKeys . begin ( ) , DownKeys . end ( ) , C4PlayerControlRecentKey ( key , 0 ) ) = = DownKeys . end ( ) ) DownKeys . push_back ( RKey ) ;
2009-05-26 06:10:38 +00:00
// process!
bool fResult = ProcessKeyEvent ( key , false , Game . KeyboardInput . GetLastKeyExtraData ( ) ) ;
// add to recent list unless repeated
2009-05-28 00:14:54 +00:00
if ( ! key . IsRepeated ( ) ) RecentKeys . push_back ( RKey ) ;
2009-05-26 06:10:38 +00:00
return fResult ;
}
bool C4PlayerControl : : ProcessKeyUp ( const C4KeyCodeEx & key )
{
// remove key from "down" list
C4PlayerControlRecentKeyList : : iterator i = find ( DownKeys . begin ( ) , DownKeys . end ( ) , C4PlayerControlRecentKey ( key , 0 ) ) ;
if ( i ! = DownKeys . end ( ) ) DownKeys . erase ( i ) ;
// process!
return ProcessKeyEvent ( key , true , Game . KeyboardInput . GetLastKeyExtraData ( ) ) ;
}
2009-05-28 00:14:54 +00:00
void C4PlayerControl : : ExecuteControlPacket ( const class C4ControlPlayerControl * pCtrl )
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)
2009-05-28 00:14:54 +00:00
for ( C4ControlPlayerControl : : ControlItemVec : : const_iterator i = pCtrl - > GetControlItems ( ) . begin ( ) ; i ! = pCtrl - > GetControlItems ( ) . end ( ) ; + + i )
2009-05-26 06:10:38 +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 )
{
if ( ExecuteControl ( rItem . iControl , pCtrl - > IsReleaseControl ( ) , pCtrl - > GetExtraData ( ) , rItem . iTriggerMode , false ) )
if ( pCtrlDef - > IsSync ( ) )
break ;
}
}
}
bool C4PlayerControl : : ExecuteControl ( int32_t iControl , bool fUp , const C4KeyEventData & rKeyExtraData , int32_t iTriggerMode , bool fRepeated )
{
// 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 ;
// hold-actions only work on script controls with the hold flag
if ( iTriggerMode & ( C4PlayerControlAssignment : : CTM_Hold | C4PlayerControlAssignment : : CTM_Release ) )
{
if ( eAction ! = C4PlayerControlDef : : CDA_Script ) return false ;
if ( ! pControlDef - > IsHoldKey ( ) ) return false ;
if ( fUp ) return false ; // hold triggers have no "up"-event
// perform hold/release
if ( fWasDown )
{
// control is currently down: release?
if ( iTriggerMode & C4PlayerControlAssignment : : CTM_Release )
{
KeyExtraData . iStrength = 0 ;
Sync . SetControlDownState ( iControl , KeyExtraData , Game . FrameCounter , false ) ;
// now process as a regular "Up" event
fUp = true ;
fRepeated = false ;
}
else //if (iTriggerMode & C4PlayerControlAssignment::CTM_Hold) - must be true
{
// 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 ;
}
}
else
{
// control is currently up. Put into hold-down-state if this is a hold key
if ( iTriggerMode & C4PlayerControlAssignment : : CTM_Hold )
{
Sync . SetControlDownState ( iControl , KeyExtraData , Game . FrameCounter , false ) ;
// now process as a regular "down" event
fRepeated = false ;
}
else
{
//. Ignore if it's only a release key
return false ;
}
}
}
else if ( fUp )
{
// 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 ) ;
}
else if ( pControlDef - > IsHoldKey ( ) )
{
// regular ControlDown on Hold Key: Set in down list
Sync . SetControlDownState ( iControl , KeyExtraData , Game . FrameCounter , true ) ;
fRepeated = fWasDown ;
2009-05-26 06:10:38 +00:00
}
// perform action for this control
bool fHandled = ExecuteControlAction ( iControl , eAction , pControlDef - > GetExtraData ( ) , fUp , KeyExtraData , fRepeated ) ;
// return if handled, unless control is defined as always unhandled
return fHandled & & ! ( iTriggerMode & C4PlayerControlAssignment : : CTM_AlwaysUnhandled ) ;
}
bool C4PlayerControl : : ExecuteControlAction ( int32_t iControl , C4PlayerControlDef : : Actions eAction , C4ID idControlExtraData , bool fUp , const C4KeyEventData & rKeyExtraData , bool fRepeated )
{
// get affected player
C4Player * pPlr = NULL ;
if ( iPlr > - 1 )
{
2009-08-10 14:48:25 +00:00
pPlr = : : Players . Get ( iPlr ) ;
2009-05-26 06:10:38 +00:00
if ( ! pPlr ) return false ;
}
// exec action (on player)
switch ( eAction )
{
// scripted player control
case C4PlayerControlDef : : CDA_Script :
return ExecuteControlScript ( iControl , idControlExtraData , fUp , rKeyExtraData , fRepeated ) ;
// menu controls
case C4PlayerControlDef : : CDA_Menu : if ( ! pPlr | | fUp ) return false ; if ( pPlr - > Menu . IsActive ( ) ) pPlr - > Menu . Close ( false ) ; 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
case C4PlayerControlDef : : CDA_MenuDown : if ( ! pPlr | | ! pPlr - > Menu . IsActive ( ) | | fUp ) return false ; pPlr - > Menu . Control ( COM_MenuDown , 0 ) ; return true ; // navigate
//unknown action
default : return false ;
}
}
bool C4PlayerControl : : ExecuteControlScript ( int32_t iControl , C4ID idControlExtraData , bool fUp , const C4KeyEventData & rKeyExtraData , bool fRepeated )
{
2009-08-10 14:48:25 +00:00
C4Player * pPlr = : : Players . Get ( iPlr ) ;
2009-05-28 00:14:54 +00:00
if ( pPlr )
{
// Not for eliminated (checked again in DirectCom, but make sure no control is generated for eliminated players!)
if ( pPlr - > Eliminated ) return false ;
// control count for statistics
pPlr - > CountControl ( C4Player : : PCID_DirectCom , iControl * 2 + fUp ) ;
}
else if ( iPlr > - 1 )
{
// player lost?
return false ;
}
2009-06-16 00:38:39 +00:00
// control down
2009-08-10 14:48:25 +00:00
C4AulFunc * pFunc = : : ScriptEngine . GetFirstFunc ( PSF_PlayerControl ) ;
2009-06-16 00:38:39 +00:00
if ( ! pFunc ) return false ;
C4AulParSet Pars ( C4VInt ( iPlr ) , C4VInt ( iControl ) , C4VID ( idControlExtraData ) , C4VInt ( rKeyExtraData . x ) , C4VInt ( rKeyExtraData . y ) , C4VInt ( rKeyExtraData . iStrength ) , C4VBool ( fRepeated ) , C4VBool ( fUp ) ) ;
return ! ! pFunc - > Exec ( NULL , & Pars ) ;
2009-05-26 06:10:38 +00:00
}
void C4PlayerControl : : Execute ( )
{
2009-05-28 00:14:54 +00:00
// sync execution: Do keyrepeat
for ( int32_t i = 0 ; i < ControlDefs . GetCount ( ) ; + + i )
{
const CSync : : ControlDownState * pControlDownState = Sync . GetControlDownState ( i ) ;
if ( pControlDownState & & pControlDownState - > IsDown ( ) )
{
const C4PlayerControlDef * pCtrlDef = ControlDefs . GetControlByIndex ( i ) ;
assert ( pCtrlDef ) ;
int32_t iCtrlRepeatDelay = pCtrlDef - > GetRepeatDelay ( ) ;
if ( iCtrlRepeatDelay )
{
int32_t iFrameDiff = Game . FrameCounter - pControlDownState - > iDownFrame ;
int32_t iCtrlInitialRepeatDelay = pCtrlDef - > GetInitialRepeatDelay ( ) ;
if ( iFrameDiff & & iFrameDiff > = iCtrlInitialRepeatDelay )
{
if ( ! ( ( iFrameDiff - iCtrlInitialRepeatDelay ) % iCtrlRepeatDelay ) )
{
// it's RepeatTime for this key!
ExecuteControlAction ( i , pCtrlDef - > GetAction ( ) , pCtrlDef - > GetExtraData ( ) , false , pControlDownState - > DownState , true ) ;
}
}
}
}
}
// cleanup old recent keys
C4PlayerControlRecentKeyList : : iterator irk ;
DWORD tNow = timeGetTime ( ) ;
for ( irk = RecentKeys . begin ( ) ; irk ! = RecentKeys . end ( ) ; + + irk )
{
C4PlayerControlRecentKey & rk = * irk ;
if ( rk . tTime + MaxRecentKeyLookback > tNow ) break ;
}
if ( irk ! = RecentKeys . begin ( ) ) RecentKeys . erase ( RecentKeys . begin ( ) , irk ) ;
2009-05-26 06:10:38 +00:00
}
C4PlayerControl : : C4PlayerControl ( ) : ControlDefs ( Game . PlayerControlDefs ) , iPlr ( - 1 ) , pControlSet ( NULL )
{
}
void C4PlayerControl : : Clear ( )
{
2009-05-28 00:14:54 +00:00
iPlr = NO_OWNER ;
pControlSet = NULL ;
for ( KeyBindingList : : iterator i = KeyBindings . begin ( ) ; i ! = KeyBindings . end ( ) ; + + i ) delete * i ;
KeyBindings . clear ( ) ;
RecentKeys . clear ( ) ;
DownKeys . clear ( ) ;
Sync . Clear ( ) ;
2009-05-26 06:10:38 +00:00
}
void C4PlayerControl : : RegisterKeyset ( int32_t iPlr , C4PlayerControlAssignmentSet * pKeyset )
{
2009-05-28 00:14:54 +00:00
// clear any previous settings
Clear ( ) ;
// 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 )
{
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-05-28 00:14:54 +00:00
void C4PlayerControl : : AddKeyBinding ( const C4KeyCodeEx & key , bool fHoldKey , int32_t idx )
{
2009-06-09 23:29:16 +00:00
KeyBindings . push_back ( new C4KeyBinding (
2009-05-28 00:14:54 +00:00
key , FormatString ( " PlrKey%02d " , idx ) . getData ( ) , KEYSCOPE_Control ,
new C4KeyCBPassKey < C4PlayerControl > ( * this , & C4PlayerControl : : ProcessKeyDown , fHoldKey ? & C4PlayerControl : : ProcessKeyUp : NULL ) ,
C4CustomKey : : PRIO_PlrControl ) ) ;
}