From ce6d73b92a094089df3e3da59d8ca65f8f22b414 Mon Sep 17 00:00:00 2001 From: Sven Eberhardt Date: Tue, 26 May 2009 02:10:38 -0400 Subject: [PATCH] Moving controls to script... --- engine/inc/C4Components.h | 1 + engine/inc/C4Control.h | 31 ++ engine/inc/C4Game.h | 151 +++++----- engine/inc/C4Include.h | 1 + engine/inc/C4KeyboardInput.h | 14 +- engine/inc/C4PacketBase.h | 1 + engine/inc/C4PlayerControl.h | 157 +++++++--- engine/inc/C4Script.h | 2 + engine/src/C4Control.cpp | 39 +++ engine/src/C4Game.cpp | 28 +- engine/src/C4KeyboardInput.cpp | 16 ++ engine/src/C4PlayerControl.cpp | 440 +++++++++++++++++++++++++++-- engine/src/C4StartupOptionsDlg.cpp | 1 + 13 files changed, 755 insertions(+), 127 deletions(-) diff --git a/engine/inc/C4Components.h b/engine/inc/C4Components.h index 3fbabd0c9..438ae158e 100644 --- a/engine/inc/C4Components.h +++ b/engine/inc/C4Components.h @@ -118,6 +118,7 @@ #define C4CFN_Teams "Teams.txt" #define C4CFN_Parameters "Parameters.txt" #define C4CFN_RoundResults "RoundResults.txt" +#define C4CFN_PlayerControls "PlayerControls.txt" #define C4CFN_MapFolderData "FolderMap.txt" #define C4CFN_MapFolderBG "FolderMap" diff --git a/engine/inc/C4Control.h b/engine/inc/C4Control.h index e72168184..8dca26d98 100644 --- a/engine/inc/C4Control.h +++ b/engine/inc/C4Control.h @@ -173,6 +173,37 @@ public: DECLARE_C4CONTROL_VIRTUALS }; +class C4ControlPlayerControl2 : public C4ControlPacket // sync +{ +public: + C4ControlPlayerControl2() : iPlr(-1), fRelease(false) {} + C4ControlPlayerControl2(int32_t iPlr, bool fRelease, const C4KeyEventData &rExtraData) + : iPlr(iPlr), fRelease(fRelease), ExtraData(rExtraData) { } + + struct ControlItem + { + int32_t iControl; + int32_t iTriggerMode; + ControlItem() : iControl(-1), iTriggerMode(0) {} + ControlItem(int32_t iControl, int32_t iTriggerMode) : iControl(iControl), iTriggerMode(iTriggerMode) {} + void CompileFunc(StdCompiler *pComp); + bool operator ==(const struct ControlItem &cmp) const { return iControl==cmp.iControl && iTriggerMode == cmp.iTriggerMode; } + }; + typedef std::vector ControlItemVec; +protected: + int32_t iPlr; + bool fRelease; + C4KeyEventData ExtraData; + ControlItemVec ControlItems; +public: + DECLARE_C4CONTROL_VIRTUALS + void AddControl(int32_t iControl, int32_t iTriggerMode) + { ControlItems.push_back(ControlItem(iControl, iTriggerMode)); } + const ControlItemVec &GetControlItems() const { return ControlItems; } + bool IsReleaseControl() const { return fRelease; } + const C4KeyEventData &GetExtraData() const { return ExtraData; } +}; + class C4ControlPlayerCommand : public C4ControlPacket // sync { public: diff --git a/engine/inc/C4Game.h b/engine/inc/C4Game.h index 32280dd1e..90e0e6064 100644 --- a/engine/inc/C4Game.h +++ b/engine/inc/C4Game.h @@ -58,9 +58,10 @@ #include #include #include +#include class C4Game - { + { private: // used as StdCompiler-parameter struct CompileSettings @@ -81,13 +82,13 @@ class C4Game C4KeySetCtrl(int32_t iKeySet, int32_t iCtrl) : iKeySet(iKeySet), iCtrl(iCtrl) {} }; - public: - C4Game(); + public: + C4Game(); ~C4Game(); public: - C4DefList Defs; - C4TextureMap TextureMap; - C4RankSystem Rank; + C4DefList Defs; + C4TextureMap TextureMap; + C4RankSystem Rank; C4GraphicsSystem GraphicsSystem; C4MessageInput MessageInput; C4GraphicsResource GraphicsResource; @@ -100,13 +101,13 @@ class C4Game C4RoundResults RoundResults; C4GameMessageList Messages; C4MouseControl MouseControl; - C4Weather Weather; + C4Weather Weather; C4MaterialMap Material; C4GameObjects Objects; C4ObjectList BackObjects; // objects in background (C4D_Background) C4ObjectList ForeObjects; // objects in foreground (C4D_Foreground) - C4Landscape Landscape; - C4Scenario C4S; + C4Landscape Landscape; + C4Scenario C4S; C4ComponentHost Info; C4ComponentHost Title; C4ComponentHost Names; @@ -114,7 +115,7 @@ class C4Game C4AulScriptEngine ScriptEngine; C4GameScriptHost Script; C4LangStringTable MainSysLangStringTable, ScenarioLangStringTable, ScenarioSysLangStringTable; - C4MassMoverSet MassMover; + C4MassMoverSet MassMover; C4PXSSystem PXS; C4ParticleSystem Particles; C4PlayerList Players; @@ -124,13 +125,15 @@ class C4Game C4PathFinder PathFinder; C4TransferZones TransferZones; - C4Group ScenarioFile; + C4Group ScenarioFile; C4GroupSet GroupSet; C4Group *pParentGroup; C4Extra Extra; C4GUIScreen *pGUI; C4ScenarioSection *pScenarioSections, *pCurrentScenarioSection; C4Effect *pGlobalEffects; + C4PlayerControlDefs PlayerControlDefs; + C4PlayerControlAssignmentSets PlayerControlAssignmentSets; #ifndef USE_CONSOLE // We don't need fonts when we don't have graphics C4FontLoader FontLoader; @@ -142,8 +145,8 @@ class C4Game class C4FileMonitor *pFileMonitor; class C4GameSec1Timer *pSec1Timer; - char CurrentScenarioSection[C4MaxName+1]; - char ScenarioFilename[_MAX_PATH+1]; + char CurrentScenarioSection[C4MaxName+1]; + char ScenarioFilename[_MAX_PATH+1]; StdCopyStrBuf ScenarioTitle; char PlayerFilenames[20*_MAX_PATH+1]; char DefinitionFilenames[20*_MAX_PATH+1]; @@ -152,13 +155,13 @@ class C4Game int32_t StartupPlayerCount; int32_t FPS,cFPS; int32_t HaltCount; - bool GameOver; - bool Evaluated; + bool GameOver; + bool Evaluated; bool GameOverDlgShown; bool fScriptCreatedObjects; bool fLobby; int32_t iLobbyTimeout; - bool fObserve; + bool fObserve; bool fReferenceDefinitionOverride; bool NetworkActive; bool Record; @@ -192,27 +195,27 @@ class C4Game // next mission to be played after this one StdCopyStrBuf NextMission, NextMissionText, NextMissionDesc; - public: - // Init and execution + public: + // Init and execution void Default(); void Clear(); void Abort(bool fApproved = false); // hard-quit on Esc+Y (/J/O) - void Evaluate(); + void Evaluate(); void ShowGameOverDlg(); bool DoKeyboardInput(C4KeyCode vk_code, C4KeyEventType eEventType, bool fAlt, bool fCtrl, bool fShift, bool fRepeated, class C4GUI::Dialog *pForDialog=NULL, bool fPlrCtrlOnly=false); void DrawCursors(C4TargetFacet &cgo, int32_t iPlayer); bool LocalControlKey(C4KeyCodeEx key, C4KeySetCtrl Ctrl); bool LocalControlKeyUp(C4KeyCodeEx key, C4KeySetCtrl Ctrl); - void LocalPlayerControl(int32_t iPlayer, int32_t iCom); - void FixRandom(int32_t iSeed); + void LocalPlayerControl(int32_t iPlayer, int32_t iCom); + void FixRandom(int32_t iSeed); bool Init(); bool PreInit(); - void ParseCommandLine(const char *szCmdLine); - BOOL Execute(); - class C4Player *JoinPlayer(const char *szFilename, int32_t iAtClient, const char *szAtClientName, C4PlayerInfo *pInfo); - BOOL DoGameOver(); + void ParseCommandLine(const char *szCmdLine); + BOOL Execute(); + class C4Player *JoinPlayer(const char *szFilename, int32_t iAtClient, const char *szAtClientName, C4PlayerInfo *pInfo); + BOOL DoGameOver(); bool CanQuickSave(); - BOOL QuickSave(const char *strFilename, const char *strTitle, bool fForceSave=false); + BOOL QuickSave(const char *strFilename, const char *strTitle, bool fForceSave=false); void SetInitProgress(float fToProgress); void OnResolutionChanged(unsigned int iXRes, unsigned int iYRes); // update anything that's dependant on screen resolution void InitFullscreenComponents(bool fRunning); @@ -223,58 +226,58 @@ class C4Game bool Unpause(); bool IsPaused(); // Network - void Synchronize(BOOL fSavePlayerFiles); - void SyncClearance(); + void Synchronize(BOOL fSavePlayerFiles); + void SyncClearance(); BOOL ReSync(); void SyncCheckFiles(); // check if files are in sync - // Editing + // Editing BOOL DropFile(const char *szFilename, float iX, float iY); BOOL CreateViewport(int32_t iPlayer, bool fSilent=false); - BOOL DropDef(C4ID id, float iX, float iY); - BOOL LoadDef(const char *szFilename); - BOOL ReloadFile(const char *szPath); - BOOL ReloadDef(C4ID id); + BOOL DropDef(C4ID id, float iX, float iY); + BOOL LoadDef(const char *szFilename); + BOOL ReloadFile(const char *szPath); + BOOL ReloadDef(C4ID id); BOOL ReloadParticle(const char *szName); - // Object functions - void ClearPointers(C4Object *cobj); - C4Object *CreateObject(C4ID type, C4Object *pCreator, int32_t owner=NO_OWNER, - int32_t x=50, int32_t y=50, int32_t r=0, - FIXED xdir=Fix0, FIXED ydir=Fix0, FIXED rdir=Fix0, int32_t iController=NO_OWNER); - C4Object *CreateObjectConstruction(C4ID type, - C4Object *pCreator, - int32_t owner, - int32_t ctx=0, int32_t bty=0, - int32_t con=1, BOOL terrain=FALSE); - C4Object *CreateInfoObject(C4ObjectInfo *cinf, int32_t owner, - int32_t tx=50, int32_t ty=50); - void BlastObjects(int32_t tx, int32_t ty, int32_t level, C4Object *inobj, int32_t iCausedBy, C4Object *pByObj); + // Object functions + void ClearPointers(C4Object *cobj); + C4Object *CreateObject(C4ID type, C4Object *pCreator, int32_t owner=NO_OWNER, + int32_t x=50, int32_t y=50, int32_t r=0, + FIXED xdir=Fix0, FIXED ydir=Fix0, FIXED rdir=Fix0, int32_t iController=NO_OWNER); + C4Object *CreateObjectConstruction(C4ID type, + C4Object *pCreator, + int32_t owner, + int32_t ctx=0, int32_t bty=0, + int32_t con=1, BOOL terrain=FALSE); + C4Object *CreateInfoObject(C4ObjectInfo *cinf, int32_t owner, + int32_t tx=50, int32_t ty=50); + void BlastObjects(int32_t tx, int32_t ty, int32_t level, C4Object *inobj, int32_t iCausedBy, C4Object *pByObj); void ShakeObjects(int32_t tx, int32_t ry, int32_t range); - C4Object *OverlapObject(int32_t tx, int32_t ty, int32_t wdt, int32_t hgt, - int32_t category); - C4Object *FindObject(C4ID id, - int32_t iX=0, int32_t iY=0, int32_t iWdt=0, int32_t iHgt=0, - DWORD ocf=OCF_All, - const char *szAction=NULL, C4Object *pActionTarget=NULL, - C4Object *pExclude=NULL, - C4Object *pContainer=NULL, - int32_t iOwner=ANY_OWNER, - C4Object *pFindNext=NULL); + C4Object *OverlapObject(int32_t tx, int32_t ty, int32_t wdt, int32_t hgt, + int32_t category); + C4Object *FindObject(C4ID id, + int32_t iX=0, int32_t iY=0, int32_t iWdt=0, int32_t iHgt=0, + DWORD ocf=OCF_All, + const char *szAction=NULL, C4Object *pActionTarget=NULL, + C4Object *pExclude=NULL, + C4Object *pContainer=NULL, + int32_t iOwner=ANY_OWNER, + C4Object *pFindNext=NULL); C4Object *FindVisObject( // find object in view at pos, regarding parallaxity and visibility (but not distance) - float tx, float ty, int32_t iPlr, const C4Facet &fctViewport, - float iX=0, float iY=0, float iWdt=0, float iHgt=0, - DWORD ocf=OCF_All, - C4Object *pExclude=NULL, - int32_t iOwner=ANY_OWNER, - C4Object *pFindNext=NULL); - int32_t ObjectCount(C4ID id, - int32_t x=0, int32_t y=0, int32_t wdt=0, int32_t hgt=0, - DWORD ocf=OCF_All, - const char *szAction=NULL, C4Object *pActionTarget=NULL, - C4Object *pExclude=NULL, - C4Object *pContainer=NULL, - int32_t iOwner=ANY_OWNER); - C4Object *FindBase(int32_t iPlayer, int32_t iIndex); - C4Object *FindFriendlyBase(int32_t iPlayer, int32_t iIndex); + float tx, float ty, int32_t iPlr, const C4Facet &fctViewport, + float iX=0, float iY=0, float iWdt=0, float iHgt=0, + DWORD ocf=OCF_All, + C4Object *pExclude=NULL, + int32_t iOwner=ANY_OWNER, + C4Object *pFindNext=NULL); + int32_t ObjectCount(C4ID id, + int32_t x=0, int32_t y=0, int32_t wdt=0, int32_t hgt=0, + DWORD ocf=OCF_All, + const char *szAction=NULL, C4Object *pActionTarget=NULL, + C4Object *pExclude=NULL, + C4Object *pContainer=NULL, + int32_t iOwner=ANY_OWNER); + C4Object *FindBase(int32_t iPlayer, int32_t iIndex); + C4Object *FindFriendlyBase(int32_t iPlayer, int32_t iIndex); C4Object *FindObjectByCommand(int32_t iCommand, C4Object *pTarget=NULL, C4Value iTx=C4VNull, int32_t iTy=0, C4Object *pTarget2=NULL, C4Object *pFindNext=NULL); void CastObjects(C4ID id, C4Object *pCreator, int32_t num, int32_t level, int32_t tx, int32_t ty, int32_t iOwner=NO_OWNER, int32_t iController=NO_OWNER); void BlastCastObjects(C4ID id, C4Object *pCreator, int32_t num, int32_t tx, int32_t ty, int32_t iController=NO_OWNER); @@ -282,14 +285,16 @@ class C4Game C4Object *PlaceAnimal(C4ID idAnimal); BOOL LoadScenarioSection(const char *szSection, DWORD dwFlags); - BOOL SaveDesc(C4Group &hGroup, BOOL fSaveGame=FALSE, BOOL fReference=FALSE, BOOL fLobby=FALSE, BOOL fUnregistered=FALSE, BOOL fRecord=FALSE); + BOOL SaveDesc(C4Group &hGroup, BOOL fSaveGame=FALSE, BOOL fReference=FALSE, BOOL fLobby=FALSE, BOOL fUnregistered=FALSE, BOOL fRecord=FALSE); bool DrawTextSpecImage(C4FacetSurface &fctTarget, const char *szSpec, uint32_t dwClr=0xff); bool SpeedUp(); bool SlowDown(); bool InitKeyboard(); // register main keyboard input functions + void UpdateLanguage(); + bool InitPlayerControlSettings(); - protected: + protected: bool InitSystem(); void InitInEarth(); void InitVegetation(); diff --git a/engine/inc/C4Include.h b/engine/inc/C4Include.h index c3886c88a..3c7f456db 100644 --- a/engine/inc/C4Include.h +++ b/engine/inc/C4Include.h @@ -202,6 +202,7 @@ #include "C4PathFinder.h" #include "C4Physics.h" #include "C4Player.h" +#include "C4PlayerControl.h" #include "C4PlayerInfo.h" #include "C4PlayerInfoListBox.h" #include "C4PlayerList.h" diff --git a/engine/inc/C4KeyboardInput.h b/engine/inc/C4KeyboardInput.h index c08bcaac4..c8b9776f9 100644 --- a/engine/inc/C4KeyboardInput.h +++ b/engine/inc/C4KeyboardInput.h @@ -167,7 +167,17 @@ struct C4KeyCodeEx C4KeyCodeEx(C4KeyCode Key = KEY_Default, C4KeyShiftState Shift = KEYS_None, bool fIsRepeated = false) : Key(Key), dwShift(Shift), fRepeated(fIsRepeated) {} - bool IsRepeated() { return fRepeated; } + bool IsRepeated() const { return fRepeated; } + }; + +// extra data associated with a key event +struct C4KeyEventData + { + int32_t iStrength; // pressure between 0 and 100 (100 for nomal keypress) + int32_t x,y; // position for mouse event + C4KeyEventData() : iStrength(0), x(0), y(0) {} + void CompileFunc(StdCompiler *pComp); + bool operator ==(const struct C4KeyEventData &cmp) const; }; // callback interface @@ -392,6 +402,7 @@ class C4KeyboardInput // mapping of all keys by code and name KeyCodeMap KeysByCode; KeyNameMap KeysByName; + C4KeyEventData LastKeyExtraData; public: static bool IsValid; // global var to fix any deinitialization orders of key map and static keys @@ -417,6 +428,7 @@ class C4KeyboardInput C4CustomKey *GetKeyByName(const char *szKeyName); StdStrBuf GetKeyCodeNameByKeyName(const char *szKeyName, bool fShort = false, int32_t iIndex = 0); + const C4KeyEventData &GetLastKeyExtraData() const { return LastKeyExtraData; } }; // keyboardinput-initializer-helper diff --git a/engine/inc/C4PacketBase.h b/engine/inc/C4PacketBase.h index be413be95..f63129f02 100644 --- a/engine/inc/C4PacketBase.h +++ b/engine/inc/C4PacketBase.h @@ -159,6 +159,7 @@ enum C4PacketType CID_PlrControl = CID_First | 0x21, CID_PlrCommand = CID_First | 0x22, CID_Message = CID_First | 0x23, + CID_PlrControl2 = CID_First | 0x24, CID_EMMoveObj = CID_First | 0x30, CID_EMDrawTool = CID_First | 0x31, diff --git a/engine/inc/C4PlayerControl.h b/engine/inc/C4PlayerControl.h index 83187043a..375965ceb 100644 --- a/engine/inc/C4PlayerControl.h +++ b/engine/inc/C4PlayerControl.h @@ -30,12 +30,25 @@ class C4PlayerControlDef StdCopyStrBuf sIdentifier; // name as seen in script and config StdCopyStrBuf sGUIName; // name as displayed to player StdCopyStrBuf sGUIDesc; // key description displayed to player in config dialog + bool fGlobal; // if true, control can be bound to the global player only bool fIsHoldKey; // if true, the control can be in down and up state - int32_t iRepeat; // if >0, the key will generate successive events when held down + int32_t iRepeatDelay; // if >0, the key will generate successive events when held down + int32_t iInitialRepeatDelay; // delay after which KeyRepeat will be enabled bool fDefaultDisabled; // if true, the control is disabled by default and needs to be enabled by script + C4ID idControlExtraData; // extra data to be passed to script function + public: + enum Actions //action to be performed when control is triggered + { + CDA_None=0, // do nothing + CDA_Script, // default: Script callback + CDA_Menu, // open player menu (async) + CDA_MenuOK, CDA_MenuCancel, CDA_MenuLeft, CDA_MenuUp, CDA_MenuRight, CDA_MenuDown, // player menu controls (async) + }; + private: + Actions eAction; public: - C4PlayerControlDef() : fIsHoldKey(false), fDefaultDisabled(false) {} + C4PlayerControlDef() : fIsHoldKey(false), fDefaultDisabled(false), eAction(CDA_Script), fGlobal(false), idControlExtraData(C4ID_None) {} ~C4PlayerControlDef() {}; void CompileFunc(StdCompiler *pComp); @@ -43,9 +56,18 @@ class C4PlayerControlDef const char *GetIdentifier() const { return sIdentifier.getData(); } const char *GetGUIName() const { return sGUIName.getData(); } const char *GetGUIDesc() const { return sGUIDesc.getData(); } + Actions GetAction() const { return eAction; } + bool IsHoldKey() const { return fIsHoldKey; } + C4ID GetExtraData() const { return idControlExtraData; } + bool IsGlobal() const { return fGlobal; } //C4PlayerControlDef &operator =(const C4PlayerControlDef &src); bool operator ==(const C4PlayerControlDef &cmp) const; + + bool Execute(bool fUp, const C4KeyEventData &rKeyExtraData); // key was triggered - execute and return if handled + bool IsAsync() const { return eAction != CDA_None && eAction != CDA_Script; } // true if to be executed directly when triggered + bool IsSync() const { return eAction == CDA_Script; } // true if to be executed via control queue + bool IsValid() const { return eAction != CDA_None; } }; // CON_* constants are indices into the C4PlayerControlDefs list @@ -59,11 +81,17 @@ class C4PlayerControlDefs DefVecImpl Defs; public: + C4PlayerControlDefs() {} + ~C4PlayerControlDefs() {} + void Clear(); + void CompileFunc(StdCompiler *pComp); void MergeFrom(const C4PlayerControlDefs &Src); // copy all defs from source file; overwrite defs of same name if found C4PlayerControlDef *GetControlByIndex(int32_t idx); int32_t GetControlIndexByIdentifier(const char *szIdentifier) const; // return CON_None for not found + + bool operator ==(const C4PlayerControlDefs &cmp) const { return Defs == cmp.Defs; } }; // a key/mouse/gamepad assignment to a PlayerControlDef @@ -89,47 +117,73 @@ class C4PlayerControlAssignment StdCopyStrBuf sControlName; // name of the control to be executed on this key int32_t iControl; // the control to be executed on this key, i.e. the resolved sControlName - bool fAlwaysUnhandled; // if true, the key will not block handling of other keys even if it got handled + int32_t iPriority; // higher priority assignments get handled first + public: // action to be performed on the control upon this key enum TriggerModes { - CTM_Default=0, // standard behaviour: The control will be triggered - CTM_Hold, // the control will be put into "down"-mode - CTM_Release, // the hold mode of the control will be released - } eTriggerMode; + CTM_Default=0, // standard behaviour: The control will be triggered + CTM_Hold= 1<<0, // the control will be put into "down"-mode + CTM_Release= 1<<1, // the hold mode of the control will be released + CTM_AlwaysUnhandled= 1<<2, // the key will not block handling of other keys even if it got handled + }; + + private: + int32_t iTriggerMode; + + bool fRefsResolved; // set to true after sControlName and sKeyNames have been resolved to runtime values public: - C4PlayerControlAssignment() : TriggerKey(), iControl(CON_None), fAlwaysUnhandled(false), eTriggerMode(CTM_Default) {} - ~C4PlayerControlAssignment(); + C4PlayerControlAssignment() : TriggerKey(), iControl(CON_None), iTriggerMode(CTM_Default), iPriority(0), fRefsResolved(false) {} + ~C4PlayerControlAssignment() {} void CompileFunc(StdCompiler *pComp); - void ResolveRefs(C4PlayerControlDefs *pControlDefs); // resolve references between assignments + bool ResolveRefs(class C4PlayerControlAssignmentSet *pParentSet, C4PlayerControlDefs *pControlDefs); // resolve references between assignments bool operator ==(const C4PlayerControlAssignment &cmp) const; // doesn't compare resolved TriggerKey/iControl + bool operator <(const C4PlayerControlAssignment &cmp) const { return iPriority < cmp.iPriority; } const char *GetControlName() const { return sControlName.getData(); } + int32_t GetControl() const { return iControl; } + bool IsRefsResolved() const { return fRefsResolved; } + bool IsAlwaysUnhandled() const { return iTriggerMode & CTM_AlwaysUnhandled; } + int32_t GetTriggerMode() const { return iTriggerMode; } }; +typedef std::vector C4PlayerControlAssignmentVec; + +struct C4PlayerControlRecentKey + { + C4KeyCodeEx Key; + int32_t iFrame; + C4PlayerControlRecentKey(const C4KeyCodeEx &Key, int32_t iFrame) : Key(Key), iFrame(iFrame) {} + bool operator ==(const C4PlayerControlRecentKey &cmp) { return Key==cmp.Key; } // comparison op for finding items in lists: Search for the key only + }; + +typedef std::list C4PlayerControlRecentKeyList; + // a set of key/mouse/gamepad assignments to all controls class C4PlayerControlAssignmentSet { private: StdCopyStrBuf sName; - typedef std::vector AssignmentsVec; - AssignmentsVec Assignments; + C4PlayerControlAssignmentVec Assignments; public: C4PlayerControlAssignmentSet() {} ~C4PlayerControlAssignmentSet() {} void CompileFunc(StdCompiler *pComp); - void ResolveRefs(C4PlayerControlDefs *pControlDefs); // resolve references between assignments + bool ResolveRefs(C4PlayerControlDefs *pControlDefs); // resolve references between assignments void MergeFrom(const C4PlayerControlAssignmentSet &Src, bool fLowPrio); // take over all assignments defined in Src const char *GetName() const { return sName.getData(); } - C4PlayerControlAssignment *GetAssignmentByControlName(const char *szControlName) const; + C4PlayerControlAssignment *GetAssignmentByControlName(const char *szControlName); + void GetAssignmentsByKey(const C4KeyCodeEx &key, bool fHoldKeysOnly, C4PlayerControlAssignmentVec *pOutVec, const C4PlayerControlRecentKeyList &DownKeys, const C4PlayerControlRecentKeyList &RecentKeys); // match only by TriggerKey (last key of Combo) if fHoldKeysOnly + + bool operator ==(const C4PlayerControlAssignmentSet &cmp) const; }; // list of C4PlayerControlAssignmentSet @@ -137,26 +191,34 @@ class C4PlayerControlAssignmentSets { private: typedef std::list AssignmentSetList; + AssignmentSetList Sets; public: C4PlayerControlAssignmentSets() {} ~C4PlayerControlAssignmentSets() {} + void Clear(); void CompileFunc(StdCompiler *pComp); + bool ResolveRefs(C4PlayerControlDefs *pControlDefs); // resolve references between assignments void MergeFrom(const C4PlayerControlAssignmentSets &Src, bool fLowPrio); // take over all assignments in known sets and new sets defined in Src + + C4PlayerControlAssignmentSet *GetSetByName(const char *szName); }; // contents of one PlayerControls.txt file class C4PlayerControlFile { private: - C4PlayerControlDef ControlDefs; + C4PlayerControlDefs ControlDefs; C4PlayerControlAssignmentSets AssignmentSets; public: - bool Load(C4Group &hGroup, const char *szFilename); + void Clear(); + void CompileFunc(StdCompiler *pComp); + bool Load(C4Group &hGroup, const char *szFilename, C4LangStringTable *pLang); + bool Save(C4Group &hGroup, const char *szFilename); - const C4PlayerControlDef &GetControlDefs() const { return ControlDefs; } + const C4PlayerControlDefs &GetControlDefs() const { return ControlDefs; } const C4PlayerControlAssignmentSets &GetAssignmentSets() const { return AssignmentSets; } }; @@ -164,40 +226,69 @@ class C4PlayerControlFile class C4PlayerControl { private: - struct RecentKey - { - C4KeyCodeEx Key; - int32_t iFrame; - }; - // shortcut - C4PlayerControlDefs &ControlDefs; + C4PlayerControlDefs &ControlDefs; // shortcut + + // owner + int32_t iPlr; // async values C4PlayerControlAssignmentSet *pControlSet; // the control set used by this player std::list KeyBindings; // keys registered into Game.KeyboardInput - std::list RecentKeys; // keys pressed recently; for combinations - std::vector DownKeys; // keys currently held down + C4PlayerControlRecentKeyList RecentKeys; // keys pressed recently; for combinations + C4PlayerControlRecentKeyList DownKeys; // keys currently held down // sync values - struct + struct CSync { - std::vector ControlDownStates; // indexed by C4PlayerControlID: Down-state of a control. 0=up, 100=down; values inbetween e.g. for gamepad sticks - std::vector ControlDisableStates; // indexed by C4PlayerControlID: Disable-states of controls. >0 is disabled. + struct ControlDownState + { + C4KeyEventData DownState; // control is down if DownState.iStrength>0 + int32_t iDownFrame; // frame when control was pressed + bool fDownByUser; // if true, the key is actually pressed. Otherwise, it's triggered as down by another key + ControlDownState(const C4KeyEventData &rDownState, int32_t iDownFrame, bool fDownByUser) + : DownState(rDownState), iDownFrame(iDownFrame), fDownByUser(fDownByUser) {} + }; + typedef std::vector DownStateVec; + DownStateVec ControlDownStates; // indexed by C4PlayerControlID: Down-state of a control. 0=up, 100=down; values inbetween e.g. for gamepad sticks + typedef std::vector DisableStateVec; + DisableStateVec ControlDisableStates; // indexed by C4PlayerControlID: Disable-states of controls. >0 is disabled. + + const ControlDownState *GetControlDownState(int32_t iControl) const; + int32_t GetControlDisabled(int32_t iControl) const; + bool IsControlDisabled(int32_t iControl) const { return GetControlDisabled(iControl)>0; } + void SetControlDownState(int32_t iControl, const C4KeyEventData &rDownState, int32_t iDownFrame, bool fDownByUser); + void SetControlDisabled(int32_t iControl, int32_t iVal); void CompileFunc(StdCompiler *pComp); + bool operator ==(const CSync &cmp) const; } Sync; // callbacks from Game.KeyboardInput - bool ProcessKeyPress(C4KeyCodeEx key, int32_t iKeyIndex); - bool ProcessKeyDown(C4KeyCodeEx key, int32_t iKeyIndex); - bool ProcessKeyUp(C4KeyCodeEx key, int32_t iKeyIndex); + bool ProcessKeyEvent(const C4KeyCodeEx &key, bool fUp, const C4KeyEventData &rKeyExtraData); + bool ProcessKeyDown(const C4KeyCodeEx &key); + bool ProcessKeyUp(const C4KeyCodeEx &key); + + // execute single control. return if handled. + bool ExecuteControl(int32_t iControl, bool fUp, const C4KeyEventData &rKeyExtraData, int32_t iTriggerMode, bool fRepeated); + bool ExecuteControlAction(int32_t iControl, C4PlayerControlDef::Actions eAction, C4ID idControlExtraData, bool fUp, const C4KeyEventData &rKeyExtraData, bool fRepeated); + bool ExecuteControlScript(int32_t iControl, C4ID idControlExtraData, bool fUp, const C4KeyEventData &rKeyExtraData, bool fRepeated); + + // sync execution: Do keyrepeat, etc. + void Execute(); public: C4PlayerControl(); ~C4PlayerControl() { Clear(); } void Clear(); - void RegisterKeyset(C4PlayerControlAssignmentSet *pKeyset); // register all keys into Game.KeyboardInput creating KeyBindings + void CompileFunc(StdCompiler *pComp); + + void RegisterKeyset(int32_t iPlr, C4PlayerControlAssignmentSet *pKeyset); // register all keys into Game.KeyboardInput creating KeyBindings + + bool IsGlobal() const { return iPlr==-1; } + + // callback from control queue + void ExecuteControlPacket(const class C4ControlPlayerControl2 *pCtrl); }; diff --git a/engine/inc/C4Script.h b/engine/inc/C4Script.h index 4cc149a08..74f11ba27 100644 --- a/engine/inc/C4Script.h +++ b/engine/inc/C4Script.h @@ -150,6 +150,8 @@ void InitFunctionMap(C4AulScriptEngine *pEngine); // add functions to engine #define PSF_OnHostilityChange "~OnHostilityChange" // int iPlr1, int iPlr2, bool fNewHostility, bool fOldHostility #define PSF_OnTeamSwitch "~OnTeamSwitch" // int iPlr1, int idNewTeam, int idOldTeam #define PSF_OnOwnerRemoved "~OnOwnerRemoved" +#define PSF_PlayerControl "~PlayerControl" // int iControl, C4ID idControlExtraData, int x, int y, int iStrength, bool fRepeated +#define PSF_PlayerControlRelease "~PlayerControlRelease" // int iControl, C4ID idControlExtraData, int x, int y // Fx%s is automatically prefixed #define PSFS_FxAdd "Add" // C4Object *pTarget, int iEffectNumber, C4String *szNewEffect, int iNewTimer, C4Value vNewEffectVar1, C4Value vNewEffectVar2, C4Value vNewEffectVar3, C4Value vNewEffectVar4 diff --git a/engine/src/C4Control.cpp b/engine/src/C4Control.cpp index a9fc28255..a5d3a64e0 100644 --- a/engine/src/C4Control.cpp +++ b/engine/src/C4Control.cpp @@ -376,6 +376,45 @@ void C4ControlPlayerControl::CompileFunc(StdCompiler *pComp) C4ControlPacket::CompileFunc(pComp); } + +// *** C4ControlPlayerControl2 + +void C4ControlPlayerControl2::Execute() const +{ + C4PlayerControl *pTargetCtrl = NULL; + if (iPlr == -1) + { + // neutral control packet: Execute in global control + } + else + { + // player-based control: Execute on control owned by player + C4Player *pPlr=Game.Players.Get(iPlr); + if (pPlr) + { + //pPlr->CountControl(C4Player::PCID_DirectCom, iCom*10000+iData); + } + } + if (pTargetCtrl) pTargetCtrl->ExecuteControlPacket(this); +} + +void C4ControlPlayerControl2::ControlItem::CompileFunc(StdCompiler *pComp) +{ + pComp->Value(iControl); + pComp->Seperator(); + pComp->Value(iTriggerMode); +} + +void C4ControlPlayerControl2::CompileFunc(StdCompiler *pComp) +{ + pComp->Value(mkNamingAdapt(mkIntPackAdapt(iPlr), "Player", -1)); + pComp->Value(mkNamingAdapt(fRelease, "Release", false)); + pComp->Value(mkNamingAdapt(ExtraData, "ExtraData", C4KeyEventData())); + pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(ControlItems), "Controls", ControlItemVec())); + C4ControlPacket::CompileFunc(pComp); +} + + // *** C4ControlPlayerCommand C4ControlPlayerCommand::C4ControlPlayerCommand(int32_t iPlr, int32_t iCmd, int32_t iX, int32_t iY, diff --git a/engine/src/C4Game.cpp b/engine/src/C4Game.cpp index e3952394c..91ab28120 100644 --- a/engine/src/C4Game.cpp +++ b/engine/src/C4Game.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #endif #include @@ -572,6 +573,8 @@ void C4Game::Clear() KeyboardInput.Clear(); SetMusicLevel(100); PlayList.Clear(); + PlayerControlAssignmentSets.Clear(); + PlayerControlDefs.Clear(); // global fullscreen class is not cleared, because it holds the carrier window // but the menu must be cleared (maybe move Fullscreen.Menu somewhere else?) @@ -2443,9 +2446,6 @@ BOOL C4Game::InitScriptEngine() { LogFatal(LoadResStr("IDS_ERR_INVALIDSYSGRP")); return FALSE; } C4Group &File = Application.SystemGroup; - // Load string table - MainSysLangStringTable.LoadEx("StringTbl", File, C4CFN_ScriptStringTbl, Config.General.LanguageEx); - // get scripts char fn[_MAX_FNAME+1] = { 0 }; File.ResetSearch(); @@ -3238,12 +3238,34 @@ bool C4Game::InitSystem() // init keyboard input (default keys, plus overloads) if (!InitKeyboard()) { LogFatal(LoadResStr("IDS_ERR_NOKEYBOARD")); return false; } + // Load string table + UpdateLanguage(); + // Player keyboard input: Key definitions and default sets + if (!InitPlayerControlSettings()) return false; // Rank system Rank.Init(Config.GetSubkeyPath("ClonkRanks"), LoadResStr("IDS_GAME_DEFRANKS"), 1000); // done, success return true; } +void C4Game::UpdateLanguage() + { + // Reload System.c4g string table + MainSysLangStringTable.LoadEx("StringTbl", Application.SystemGroup, C4CFN_ScriptStringTbl, Config.General.LanguageEx); + } + +bool C4Game::InitPlayerControlSettings() + { + C4PlayerControlFile PlayerControlFile; + if (!PlayerControlFile.Load(Application.SystemGroup, C4CFN_PlayerControls, &MainSysLangStringTable)) { LogFatal("[!]Error loading player controls"); return false; } + PlayerControlDefs = PlayerControlFile.GetControlDefs(); + PlayerControlAssignmentSets = PlayerControlFile.GetAssignmentSets(); + PlayerControlAssignmentSets.ResolveRefs(&PlayerControlDefs); + // And overwrites from config + //PlayerControlAssignmentSets.MergeFrom(Config.Controls.Assignments); + return true; + } + C4Player *C4Game::JoinPlayer(const char *szFilename, int32_t iAtClient, const char *szAtClientName, C4PlayerInfo *pInfo) { assert(pInfo); diff --git a/engine/src/C4KeyboardInput.cpp b/engine/src/C4KeyboardInput.cpp index fa360b177..7fbd4392c 100644 --- a/engine/src/C4KeyboardInput.cpp +++ b/engine/src/C4KeyboardInput.cpp @@ -494,6 +494,21 @@ void C4KeyCodeEx::CompileFunc(StdCompiler *pComp, StdStrBuf *pOutBufIfUndefined) } } +void C4KeyEventData::CompileFunc(StdCompiler *pComp) + { + pComp->Value(iStrength); + pComp->Seperator(); + pComp->Value(x); + pComp->Seperator(); + pComp->Value(y); + } + +bool C4KeyEventData::operator ==(const struct C4KeyEventData &cmp) const + { + return iStrength == cmp.iStrength + && x == cmp.x && y == cmp.y; + } + /* ----------------- C4CustomKey------------------ */ C4CustomKey::C4CustomKey(C4KeyCodeEx DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority) @@ -640,6 +655,7 @@ bool C4KeyboardInput::IsValid = false; void C4KeyboardInput::Clear() { + LastKeyExtraData = C4KeyEventData(); // release all keys - name map is guarantueed to contain them all for (KeyNameMap::const_iterator i = KeysByName.begin(); i != KeysByName.end(); ++i) i->second->Deref(); diff --git a/engine/src/C4PlayerControl.cpp b/engine/src/C4PlayerControl.cpp index cf283c572..69c421392 100644 --- a/engine/src/C4PlayerControl.cpp +++ b/engine/src/C4PlayerControl.cpp @@ -24,13 +24,28 @@ void C4PlayerControlDef::CompileFunc(StdCompiler *pComp) { - if (!pComp->Name("ControlDef")) pComp->excNotFound("ControlDef"); + if (!pComp->Name("ControlDef")) { pComp->NameEnd(); pComp->excNotFound("ControlDef"); } 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", "")); - pComp->Value(mkNamingAdapt(fIsHoldKey, "IsHoldKey", false)); - pComp->Value(mkNamingAdapt(iRepeat, "Repeat", 0)); + pComp->Value(mkNamingAdapt(fGlobal, "Global", false)); + pComp->Value(mkNamingAdapt(fIsHoldKey, "Hold", false)); + pComp->Value(mkNamingAdapt(iRepeatDelay, "RepeatDelay", 0)); + pComp->Value(mkNamingAdapt(iInitialRepeatDelay, "InitialRepeatDelay", 0)); pComp->Value(mkNamingAdapt(fDefaultDisabled, "DefaultDisabled", false)); + pComp->Value(mkNamingAdapt(mkC4IDAdapt(idControlExtraData), "ExtraData", C4ID_None)); + const StdEnumEntry 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(eAction, ActionNames), "Action", CDA_Script)); pComp->NameEnd(); } @@ -39,14 +54,23 @@ bool C4PlayerControlDef::operator ==(const C4PlayerControlDef &cmp) const return sIdentifier == cmp.sIdentifier && sGUIName == cmp.sGUIName && sGUIDesc == cmp.sGUIDesc + && fGlobal == cmp.fGlobal && fIsHoldKey == cmp.fIsHoldKey - && iRepeat == cmp.iRepeat - && fDefaultDisabled == cmp.fDefaultDisabled; + && iRepeatDelay == cmp.iRepeatDelay + && iInitialRepeatDelay == cmp.iInitialRepeatDelay + && fDefaultDisabled == cmp.fDefaultDisabled + && idControlExtraData == cmp.idControlExtraData + && eAction == cmp.eAction; } /* C4PlayerControlDefs */ +void C4PlayerControlDefs::Clear() + { + Defs.clear(); + } + void C4PlayerControlDefs::CompileFunc(StdCompiler *pComp) { pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(Defs, StdCompiler::SEP_NONE), "ControlDefs", DefVecImpl())); @@ -113,17 +137,68 @@ void C4PlayerControlAssignment::KeyComboItem::CompileFunc(StdCompiler *pComp) void C4PlayerControlAssignment::CompileFunc(StdCompiler *pComp) { - if (!pComp->Name("ControlAssignment")) pComp->excNotFound("ControlAssignment"); + if (!pComp->Name("Assignment")) { pComp->NameEnd(); pComp->excNotFound("Assignment"); } pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(KeyCombo), "Key", KeyComboVec())); pComp->Value(mkNamingAdapt(mkParAdapt(sControlName, StdCompiler::RCT_Idtf), "Control", "None")); - pComp->Value(mkNamingAdapt(fAlwaysUnhandled, "AlwaysUnhandled", false)); - const StdEnumEntry TriggerModeNames[] = { + pComp->Value(mkNamingAdapt(iPriority, "Priority", 0)); + const StdBitfieldEntry TriggerModeNames[] = { { "Default", CTM_Default }, { "Hold", CTM_Hold }, { "Release", CTM_Release }, - { NULL, CTM_Default } }; - pComp->Value(mkNamingAdapt(mkEnumAdapt(eTriggerMode, TriggerModeNames), "TriggerMode", CTM_Default)); + { "AlwaysUnhandled", CTM_AlwaysUnhandled }, + { NULL, 0 } }; + pComp->Value(mkNamingAdapt(mkBitfieldAdapt< int32_t>(iTriggerMode, TriggerModeNames), "TriggerMode", CTM_Default)); pComp->NameEnd(); + // 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()) + { + // this is a key reference - find it + C4PlayerControlAssignment *pRefAssignment = pParentSet->GetAssignmentByControlName(rKeyComboItem.sKeyName.getData()); + 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; } bool C4PlayerControlAssignment::operator ==(const C4PlayerControlAssignment &cmp) const @@ -131,8 +206,8 @@ bool C4PlayerControlAssignment::operator ==(const C4PlayerControlAssignment &cmp // doesn't compare resolved TriggerKey/iControl return KeyCombo == cmp.KeyCombo && sControlName == cmp.sControlName - && fAlwaysUnhandled == cmp.fAlwaysUnhandled - && eTriggerMode == cmp.eTriggerMode; + && iTriggerMode == cmp.iTriggerMode + && iPriority == cmp.iPriority; } @@ -140,16 +215,16 @@ bool C4PlayerControlAssignment::operator ==(const C4PlayerControlAssignment &cmp void C4PlayerControlAssignmentSet::CompileFunc(StdCompiler *pComp) { - if (!pComp->Name("ControlAssignmentSet")) pComp->excNotFound("ControlAssignmentSet"); + if (!pComp->Name("ControlSet")) { pComp->NameEnd(); pComp->excNotFound("ControlSet"); } pComp->Value(mkNamingAdapt(mkParAdapt(sName, StdCompiler::RCT_Idtf), "Name", "None")); - pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(Assignments, StdCompiler::SEP_NONE), "Key", AssignmentsVec())); + pComp->Value(mkSTLContainerAdapt(Assignments, StdCompiler::SEP_NONE)); pComp->NameEnd(); } void C4PlayerControlAssignmentSet::MergeFrom(const C4PlayerControlAssignmentSet &Src, bool fLowPrio) { // take over all assignments defined in Src - for (AssignmentsVec::const_iterator i = Src.Assignments.begin(); i != Src.Assignments.end(); ++i) + for (C4PlayerControlAssignmentVec::const_iterator i = Src.Assignments.begin(); i != Src.Assignments.end(); ++i) { const C4PlayerControlAssignment &SrcAssignment = *i; // overwrite if def of same name existed if it's not low priority anyway @@ -166,14 +241,345 @@ void C4PlayerControlAssignmentSet::MergeFrom(const C4PlayerControlAssignmentSet } } -C4PlayerControlAssignment *C4PlayerControlAssignmentSet::GetAssignmentByControlName(const char *szControlName) const +bool C4PlayerControlAssignmentSet::ResolveRefs(C4PlayerControlDefs *pDefs) { - for (AssignmentsVec::const_iterator i = Assignments.begin(); i != Assignments.end(); ++i) + // 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) + { + for (C4PlayerControlAssignmentVec::iterator i = Assignments.begin(); i != Assignments.end(); ++i) if (SEqual((*i).GetControlName(), szControlName)) return &*i; return NULL; } +bool C4PlayerControlAssignmentSet::operator ==(const C4PlayerControlAssignmentSet &cmp) const + { + return Assignments == cmp.Assignments + && sName == cmp.sName; + } + /* C4PlayerControlAssignmentSets */ +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 + C4PlayerControlAssignmentSet *pPrevSet = GetSetByName(SrcSet.GetName()); + if (pPrevSet) + { + pPrevSet->MergeFrom(SrcSet, fLowPrio); + } + else + { + // new def: Append a copy + Sets.push_back(SrcSet); + } + } + } + +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) + { + for (AssignmentSetList::iterator i = Sets.begin(); i != Sets.end(); ++i) + if (SEqual((*i).GetName(), szName)) + return &*i; + return NULL; + } + + +/* 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(*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(*this, &Buf, szFilename)) return false; + hGroup.Add(szFilename, Buf, false, true); + return true; + } + +void C4PlayerControlFile::Clear() + { + ControlDefs.Clear(); + AssignmentSets.Clear(); + } + + +/* C4PlayerControl */ + +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 + C4PlayerControlAssignmentVec Matches; + pControlSet->GetAssignmentsByKey(key, fUp, &Matches, DownKeys, RecentKeys); + // process async controls + C4ControlPlayerControl2 *pControlPacket = NULL; + for (C4PlayerControlAssignmentVec::const_iterator i = Matches.begin(); i != Matches.end(); ++i) + { + const C4PlayerControlAssignment &rAssignment = *i; + int32_t iControlIndex = rAssignment.GetControl(); + C4PlayerControlDef *pControlDef = ControlDefs.GetControlByIndex(iControlIndex); + if (pControlDef && pControlDef->IsValid() && (!fUp || pControlDef->IsHoldKey())) + { + if (pControlDef->IsAsync() && !pControlPacket) + { + if (ExecuteControl(iControlIndex, fUp, rKeyExtraData, rAssignment.GetTriggerMode(), key.IsRepeated())) + 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 + if (!pControlPacket) pControlPacket = new C4ControlPlayerControl2(iPlr, fUp, rKeyExtraData); + pControlPacket->AddControl(iControlIndex, rAssignment.GetTriggerMode()); + break; + } + } + } + // push sync control to input + if (pControlPacket) Game.Input.Add(CID_PlrControl2, pControlPacket); + } + +bool C4PlayerControl::ProcessKeyDown(const C4KeyCodeEx &key) + { + // add key to local "down" list if it's not already in there + if (std::find(DownKeys.begin(), DownKeys.end(), C4PlayerControlRecentKey(key,0)) == DownKeys.end()) DownKeys.push_back(C4PlayerControlRecentKey(key,Game.FrameCounter)); + // process! + bool fResult = ProcessKeyEvent(key, false, Game.KeyboardInput.GetLastKeyExtraData()); + // add to recent list unless repeated + if (!key.IsRepeated()) RecentKeys.push_back(C4PlayerControlRecentKey(key,Game.FrameCounter)); + 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()); + } + +void C4PlayerControl::ExecuteControlPacket(const class C4ControlPlayerControl2 *pCtrl) + { + // 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) + for (C4ControlPlayerControl2::ControlItemVec::const_iterator i = pCtrl->GetControlItems().begin(); i != pCtrl->GetControlItems().end(); ++i) + { + const C4ControlPlayerControl2::ControlItem &rItem = *i; + C4PlayerControlDef *pCtrlDef = ControlDefs.GetControlByIndex(rItem.iControl); + 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 + C4PlayerControlDef *pControlDef = ControlDefs.GetControlByIndex(iControl); + if (!pControlDef || Sync.IsControlDisabled(iControl)) return false; + C4PlayerControlDef::Actions eAction = pControlDef->GetAction(); + C4KeyEventData KeyExtraData(rKeyExtraData); + // 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 + const CSync::ControlDownState *pCtrlDownState = Sync.GetControlDownState(iControl); + if (!pCtrlDownState) return false; + bool fWasDown = (pCtrlDownState->DownState.iStrength > 0); + 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 + 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 + const CSync::ControlDownState *pCtrlDownState = Sync.GetControlDownState(iControl); + if (!pCtrlDownState) return false; + bool fWasDown = (pCtrlDownState->DownState.iStrength > 0); + if (!fWasDown) return false; + } + // 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) + { + pPlr = Game.Players.Get(iPlr); + 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) + { + if (!fUp) + { + // control down + C4AulFunc *pFunc = Game.ScriptEngine.GetFirstFunc(PSF_PlayerControl); + if (!pFunc) return false; + C4AulParSet Pars(C4VInt(iControl), C4VID(idControlExtraData), C4VInt(rKeyExtraData.x), C4VInt(rKeyExtraData.y), C4VInt(rKeyExtraData.iStrength), C4VBool(fRepeated)); + return !!pFunc->Exec(NULL, &Pars); + } + else + { + // control up + C4AulFunc *pFunc = Game.ScriptEngine.GetFirstFunc(PSF_PlayerControlRelease); + if (!pFunc) return false; + C4AulParSet Pars(C4VInt(iControl), C4VID(idControlExtraData), C4VInt(rKeyExtraData.x), C4VInt(rKeyExtraData.y)); + return !!pFunc->Exec(NULL, &Pars); + } + } + + +void C4PlayerControl::Execute() + { + // sync execution: Do keyrepeat, etc. + } + +C4PlayerControl::C4PlayerControl() : ControlDefs(Game.PlayerControlDefs), iPlr(-1), pControlSet(NULL) + { + } + +void C4PlayerControl::Clear() + { + } + +void C4PlayerControl::RegisterKeyset(int32_t iPlr, C4PlayerControlAssignmentSet *pKeyset) + { + // register all keys into Game.KeyboardInput creating KeyBindings + } + diff --git a/engine/src/C4StartupOptionsDlg.cpp b/engine/src/C4StartupOptionsDlg.cpp index cf346a4f8..da9401bab 100644 --- a/engine/src/C4StartupOptionsDlg.cpp +++ b/engine/src/C4StartupOptionsDlg.cpp @@ -1340,6 +1340,7 @@ bool C4StartupOptionsDlg::OnLangComboSelChange(C4GUI::ComboBox *pForCombo, int32 Config.General.Language[2] = '\0'; UpdateLanguage(); Languages.LoadLanguage(Config.General.LanguageEx); + Game.UpdateLanguage(); // recreate everything to reflect language changes RecreateDialog(true); return true;