From ff15ca7441332f4e19d16b8bc7a7348f2397442a Mon Sep 17 00:00:00 2001 From: Sven Eberhardt Date: Mon, 25 May 2009 05:01:26 -0400 Subject: [PATCH 01/12] Moving all player controls to script --- engine/engine.vc9.vcproj | 18 ++- engine/inc/C4KeyboardInput.h | 2 +- engine/inc/C4PlayerControl.h | 204 +++++++++++++++++++++++++++++++++ engine/src/C4KeyboardInput.cpp | 15 ++- engine/src/C4PlayerControl.cpp | 179 +++++++++++++++++++++++++++++ standard/inc/StdAdaptors.h | 4 +- standard/inc/StdCompiler.h | 1 + 7 files changed, 413 insertions(+), 10 deletions(-) create mode 100644 engine/inc/C4PlayerControl.h create mode 100644 engine/src/C4PlayerControl.cpp diff --git a/engine/engine.vc9.vcproj b/engine/engine.vc9.vcproj index 66367e213..eb9db2aa1 100644 --- a/engine/engine.vc9.vcproj +++ b/engine/engine.vc9.vcproj @@ -83,7 +83,7 @@ LinkIncremental="2" SuppressStartupBanner="true" AdditionalLibraryDirectories="..\deps\lib" - IgnoreDefaultLibraryNames="LIBCMT" + IgnoreDefaultLibraryNames="LIBCMT,LIBCMTD" GenerateDebugInformation="true" ProgramDatabaseFile="..\intermediate\vc9\Engine\Debug\clonk.pdb" SubSystem="2" @@ -183,7 +183,7 @@ LinkIncremental="1" SuppressStartupBanner="true" AdditionalLibraryDirectories="..\deps\lib" - IgnoreDefaultLibraryNames="LIBCMT" + IgnoreDefaultLibraryNames="LIBCMT,LIBCMTD" ProgramDatabaseFile="..\intermediate\vc9\clonk.pdb" SubSystem="2" RandomizedBaseAddress="1" @@ -283,7 +283,7 @@ LinkIncremental="1" SuppressStartupBanner="true" AdditionalLibraryDirectories="..\deps\lib" - IgnoreDefaultLibraryNames="LIBCMT" + IgnoreDefaultLibraryNames="LIBCMT,LIBCMTD" GenerateDebugInformation="true" ProgramDatabaseFile="..\intermediate\vc9\engine\ExtremeDebug\clonk.pdb" SubSystem="2" @@ -383,7 +383,7 @@ LinkIncremental="1" SuppressStartupBanner="true" AdditionalLibraryDirectories="..\deps\lib" - IgnoreDefaultLibraryNames="LIBCMT" + IgnoreDefaultLibraryNames="LIBCMT,LIBCMTD" ProgramDatabaseFile="..\intermediate\vc9\clonk.pdb" SubSystem="1" RandomizedBaseAddress="1" @@ -481,7 +481,7 @@ LinkIncremental="2" SuppressStartupBanner="true" AdditionalLibraryDirectories="..\deps\lib" - IgnoreDefaultLibraryNames="LIBCMT" + IgnoreDefaultLibraryNames="LIBCMT,LIBCMTD" GenerateDebugInformation="true" ProgramDatabaseFile="..\intermediate\vc9\Engine\DebugConsole\clonk.pdb" SubSystem="1" @@ -6884,6 +6884,10 @@ /> + + @@ -10202,6 +10206,10 @@ RelativePath="inc\C4Player.h" > + + diff --git a/engine/inc/C4KeyboardInput.h b/engine/inc/C4KeyboardInput.h index b01f3503a..c08bcaac4 100644 --- a/engine/inc/C4KeyboardInput.h +++ b/engine/inc/C4KeyboardInput.h @@ -162,7 +162,7 @@ struct C4KeyCodeEx return Key == v2.Key && dwShift == v2.dwShift; } - void CompileFunc(StdCompiler *pComp); + void CompileFunc(StdCompiler *pComp, StdStrBuf *pOutBufIfUndefined=NULL); C4KeyCodeEx(C4KeyCode Key = KEY_Default, C4KeyShiftState Shift = KEYS_None, bool fIsRepeated = false) : Key(Key), dwShift(Shift), fRepeated(fIsRepeated) {} diff --git a/engine/inc/C4PlayerControl.h b/engine/inc/C4PlayerControl.h new file mode 100644 index 000000000..83187043a --- /dev/null +++ b/engine/inc/C4PlayerControl.h @@ -0,0 +1,204 @@ +/* + * 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 + +#ifndef INC_C4PlayerControl +#define INC_C4PlayerControl + +#ifndef BIG_C4INCLUDE +#include +#endif + +// one control definition, e.g. "Left", "Throw", etc. +class C4PlayerControlDef + { + private: + 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 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 + bool fDefaultDisabled; // if true, the control is disabled by default and needs to be enabled by script + + public: + C4PlayerControlDef() : fIsHoldKey(false), fDefaultDisabled(false) {} + ~C4PlayerControlDef() {}; + + void CompileFunc(StdCompiler *pComp); + + const char *GetIdentifier() const { return sIdentifier.getData(); } + const char *GetGUIName() const { return sGUIName.getData(); } + const char *GetGUIDesc() const { return sGUIDesc.getData(); } + + //C4PlayerControlDef &operator =(const C4PlayerControlDef &src); + bool operator ==(const C4PlayerControlDef &cmp) const; + }; + +// CON_* constants are indices into the C4PlayerControlDefs list +enum { CON_None = -1 }; // No/undefined control + +// list of all known player control definitions +class C4PlayerControlDefs + { + private: + typedef std::vector DefVecImpl; + DefVecImpl Defs; + + public: + 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 + }; + +// a key/mouse/gamepad assignment to a PlayerControlDef +class C4PlayerControlAssignment + { + private: + // KeyCombo list: + // if size()>1, the control is triggered only if this combo is fulfilled + // used for simultanuous keypresses or sequences + struct KeyComboItem + { + C4KeyCodeEx Key; + StdCopyStrBuf sKeyName; + void CompileFunc(StdCompiler *pComp); + bool operator ==(const KeyComboItem &cmp) const { return sKeyName==cmp.sKeyName; } + }; + typedef std::vector KeyComboVec; + KeyComboVec KeyCombo; + bool fComboIsSequence; // if true, the keys must be pressed in sequence. Otherwise, they must be pressed simultanuously + + // trigger key: key/mouse/gamepad event triggering this assignment. For combinations, the last key of the combo. + C4KeyCodeEx TriggerKey; + + 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 + + // 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; + + public: + C4PlayerControlAssignment() : TriggerKey(), iControl(CON_None), fAlwaysUnhandled(false), eTriggerMode(CTM_Default) {} + ~C4PlayerControlAssignment(); + + void CompileFunc(StdCompiler *pComp); + void ResolveRefs(C4PlayerControlDefs *pControlDefs); // resolve references between assignments + + bool operator ==(const C4PlayerControlAssignment &cmp) const; // doesn't compare resolved TriggerKey/iControl + const char *GetControlName() const { return sControlName.getData(); } + }; + +// a set of key/mouse/gamepad assignments to all controls +class C4PlayerControlAssignmentSet + { + private: + StdCopyStrBuf sName; + typedef std::vector AssignmentsVec; + AssignmentsVec Assignments; + + public: + C4PlayerControlAssignmentSet() {} + ~C4PlayerControlAssignmentSet() {} + + void CompileFunc(StdCompiler *pComp); + void 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; + }; + +// list of C4PlayerControlAssignmentSet +class C4PlayerControlAssignmentSets + { + private: + typedef std::list AssignmentSetList; + + public: + C4PlayerControlAssignmentSets() {} + ~C4PlayerControlAssignmentSets() {} + + void CompileFunc(StdCompiler *pComp); + + void MergeFrom(const C4PlayerControlAssignmentSets &Src, bool fLowPrio); // take over all assignments in known sets and new sets defined in Src + }; + +// contents of one PlayerControls.txt file +class C4PlayerControlFile + { + private: + C4PlayerControlDef ControlDefs; + C4PlayerControlAssignmentSets AssignmentSets; + public: + bool Load(C4Group &hGroup, const char *szFilename); + + const C4PlayerControlDef &GetControlDefs() const { return ControlDefs; } + const C4PlayerControlAssignmentSets &GetAssignmentSets() const { return AssignmentSets; } + }; + +// runtime information about a player's control +class C4PlayerControl + { + private: + struct RecentKey + { + C4KeyCodeEx Key; + int32_t iFrame; + }; + // shortcut + C4PlayerControlDefs &ControlDefs; + + // 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 + + // sync values + struct + { + 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. + + void CompileFunc(StdCompiler *pComp); + } 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); + + public: + C4PlayerControl(); + ~C4PlayerControl() { Clear(); } + void Clear(); + + void RegisterKeyset(C4PlayerControlAssignmentSet *pKeyset); // register all keys into Game.KeyboardInput creating KeyBindings + }; + + +#endif // INC_C4PlayerControl \ No newline at end of file diff --git a/engine/src/C4KeyboardInput.cpp b/engine/src/C4KeyboardInput.cpp index b8c318e75..fa360b177 100644 --- a/engine/src/C4KeyboardInput.cpp +++ b/engine/src/C4KeyboardInput.cpp @@ -441,7 +441,7 @@ StdStrBuf C4KeyCodeEx::ToString(bool fHumanReadable, bool fShort) /* ----------------- C4KeyCodeEx ------------------ */ -void C4KeyCodeEx::CompileFunc(StdCompiler *pComp) +void C4KeyCodeEx::CompileFunc(StdCompiler *pComp, StdStrBuf *pOutBufIfUndefined) { if (pComp->isCompiler()) { @@ -464,7 +464,18 @@ void C4KeyCodeEx::CompileFunc(StdCompiler *pComp) // last section: convert to key code C4KeyCode eCode = String2KeyCode(sCode); if (eCode == KEY_Undefined) - pComp->excCorrupt("undefined key code: %s", sCode.getData()); + { + if (pOutBufIfUndefined) + { + // unknown key, but an output buffer for unknown keys was provided. Use it. + pOutBufIfUndefined->Take(sCode); + eCode = KEY_Default; + } + else + { + pComp->excCorrupt("undefined key code: %s", sCode.getData()); + } + } dwShift = dwSetShift; Key = eCode; } diff --git a/engine/src/C4PlayerControl.cpp b/engine/src/C4PlayerControl.cpp new file mode 100644 index 000000000..cf283c572 --- /dev/null +++ b/engine/src/C4PlayerControl.cpp @@ -0,0 +1,179 @@ +/* + * 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 +#include + + +/* C4PlayerControlDef */ + +void C4PlayerControlDef::CompileFunc(StdCompiler *pComp) + { + if (!pComp->Name("ControlDef")) 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(fDefaultDisabled, "DefaultDisabled", false)); + pComp->NameEnd(); + } + +bool C4PlayerControlDef::operator ==(const C4PlayerControlDef &cmp) const + { + return sIdentifier == cmp.sIdentifier + && sGUIName == cmp.sGUIName + && sGUIDesc == cmp.sGUIDesc + && fIsHoldKey == cmp.fIsHoldKey + && iRepeat == cmp.iRepeat + && fDefaultDisabled == cmp.fDefaultDisabled; + } + + +/* C4PlayerControlDefs */ + +void C4PlayerControlDefs::CompileFunc(StdCompiler *pComp) + { + pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(Defs, StdCompiler::SEP_NONE), "ControlDefs", DefVecImpl())); + } + +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); + } + } + } + +C4PlayerControlDef *C4PlayerControlDefs::GetControlByIndex(int32_t idx) + { + // safe index + if (idx<0 || idx>=Defs.size()) return NULL; + 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; + } + + +/* 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) + { + if (!pComp->Name("ControlAssignment")) pComp->excNotFound("ControlAssignment"); + 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[] = { + { "Default", CTM_Default }, + { "Hold", CTM_Hold }, + { "Release", CTM_Release }, + { NULL, CTM_Default } }; + pComp->Value(mkNamingAdapt(mkEnumAdapt(eTriggerMode, TriggerModeNames), "TriggerMode", CTM_Default)); + pComp->NameEnd(); + } + +bool C4PlayerControlAssignment::operator ==(const C4PlayerControlAssignment &cmp) const + { + // doesn't compare resolved TriggerKey/iControl + return KeyCombo == cmp.KeyCombo + && sControlName == cmp.sControlName + && fAlwaysUnhandled == cmp.fAlwaysUnhandled + && eTriggerMode == cmp.eTriggerMode; + } + + +/* C4PlayerControlAssignmentSet */ + +void C4PlayerControlAssignmentSet::CompileFunc(StdCompiler *pComp) + { + if (!pComp->Name("ControlAssignmentSet")) pComp->excNotFound("ControlAssignmentSet"); + pComp->Value(mkNamingAdapt(mkParAdapt(sName, StdCompiler::RCT_Idtf), "Name", "None")); + pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(Assignments, StdCompiler::SEP_NONE), "Key", AssignmentsVec())); + 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) + { + const C4PlayerControlAssignment &SrcAssignment = *i; + // overwrite if def of same name existed if it's not low priority anyway + C4PlayerControlAssignment *pPrevAssignment = GetAssignmentByControlName(SrcAssignment.GetControlName()); + if (pPrevAssignment) + { + if (!fLowPrio) *pPrevAssignment = SrcAssignment; + } + else + { + // new def: Append a copy + Assignments.push_back(SrcAssignment); + } + } + } + +C4PlayerControlAssignment *C4PlayerControlAssignmentSet::GetAssignmentByControlName(const char *szControlName) const + { + for (AssignmentsVec::const_iterator i = Assignments.begin(); i != Assignments.end(); ++i) + if (SEqual((*i).GetControlName(), szControlName)) + return &*i; + return NULL; + } + + +/* C4PlayerControlAssignmentSets */ + diff --git a/standard/inc/StdAdaptors.h b/standard/inc/StdAdaptors.h index 5641ce311..7b425bfda 100644 --- a/standard/inc/StdAdaptors.h +++ b/standard/inc/StdAdaptors.h @@ -518,7 +518,7 @@ struct StdSTLContainerAdapt // Write all entries for (typename C::const_iterator i = rStruct.begin(); i != rStruct.end(); ++i) { - if (i != rStruct.begin()) pComp->Seperator(eSep); + if (i != rStruct.begin() && eSep) pComp->Seperator(eSep); pComp->Value(const_cast(*i)); } } @@ -549,7 +549,7 @@ struct StdSTLContainerAdapt break; } } - while(pComp->Seperator(eSep)); + while(!eSep || pComp->Seperator(eSep)); } } // Operators for default checking/setting diff --git a/standard/inc/StdCompiler.h b/standard/inc/StdCompiler.h index 4482fcdd6..a862ae92c 100644 --- a/standard/inc/StdCompiler.h +++ b/standard/inc/StdCompiler.h @@ -107,6 +107,7 @@ public: // ending the naming, too. enum Sep { + SEP_NONE=0, // No seperator ("") SEP_SEP, // Array seperation (",") SEP_SEP2, // Array seperation 2 (";") SEP_SET, // Map pair seperation ("=") From ce6d73b92a094089df3e3da59d8ca65f8f22b414 Mon Sep 17 00:00:00 2001 From: Sven Eberhardt Date: Tue, 26 May 2009 02:10:38 -0400 Subject: [PATCH 02/12] 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; From feeb6f8dac73037c714bf9350329286e62b1bcb4 Mon Sep 17 00:00:00 2001 From: Peter Wortmann Date: Wed, 27 May 2009 16:08:46 +0200 Subject: [PATCH 03/12] #! Build fixes for XCode --- engine/inc/C4Control.h | 1 + engine/inc/C4PlayerControl.h | 1 + engine/src/C4PlayerControl.cpp | 18 ++++++++++++++++++ xcode/Clonk.xcodeproj/project.pbxproj | 6 ++++++ 4 files changed, 26 insertions(+) diff --git a/engine/inc/C4Control.h b/engine/inc/C4Control.h index 8dca26d98..275f104d9 100644 --- a/engine/inc/C4Control.h +++ b/engine/inc/C4Control.h @@ -24,6 +24,7 @@ #include "C4PacketBase.h" #include "C4PlayerInfo.h" #include "C4Client.h" +#include "C4KeyboardInput.h" // *** control base classes diff --git a/engine/inc/C4PlayerControl.h b/engine/inc/C4PlayerControl.h index 375965ceb..08cb447e3 100644 --- a/engine/inc/C4PlayerControl.h +++ b/engine/inc/C4PlayerControl.h @@ -21,6 +21,7 @@ #ifndef BIG_C4INCLUDE #include +#include #endif // one control definition, e.g. "Left", "Throw", etc. diff --git a/engine/src/C4PlayerControl.cpp b/engine/src/C4PlayerControl.cpp index 69c421392..eb6ef52b1 100644 --- a/engine/src/C4PlayerControl.cpp +++ b/engine/src/C4PlayerControl.cpp @@ -19,6 +19,13 @@ #include #include +#ifndef BIG_C4INCLUDE +#include +#include +#include +#include +#include +#endif /* C4PlayerControlDef */ @@ -583,3 +590,14 @@ void C4PlayerControl::RegisterKeyset(int32_t iPlr, C4PlayerControlAssignmentSet // register all keys into Game.KeyboardInput creating KeyBindings } +// TODO?! +void C4PlayerControlAssignmentSet::GetAssignmentsByKey(const C4KeyCodeEx &key, bool fHoldKeysOnly, C4PlayerControlAssignmentVec *pOutVec, const C4PlayerControlRecentKeyList &DownKeys, const C4PlayerControlRecentKeyList &RecentKeys) +{ } +void C4PlayerControl::CSync::SetControlDownState(int32_t iControl, const C4KeyEventData &rDownState, int32_t iDownFrame, bool fDownByUser) +{ } +void C4PlayerControl::CSync::SetControlDisabled(int32_t iControl, int32_t iVal) +{ } +int32_t C4PlayerControl::CSync::GetControlDisabled(int32_t) const +{ return 0; } +const C4PlayerControl::CSync::ControlDownState *C4PlayerControl::CSync::GetControlDownState(int32_t iControl) const +{ return NULL; } diff --git a/xcode/Clonk.xcodeproj/project.pbxproj b/xcode/Clonk.xcodeproj/project.pbxproj index 74bd7cdc0..025c46ab4 100644 --- a/xcode/Clonk.xcodeproj/project.pbxproj +++ b/xcode/Clonk.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ AFFAA14E0C2A896200444B95 /* C4ChatDlg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFFAA14B0C2A896200444B95 /* C4ChatDlg.cpp */; }; AFFAA14F0C2A896200444B95 /* C4InteractiveThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFFAA14C0C2A896200444B95 /* C4InteractiveThread.cpp */; }; AFFAA1500C2A896200444B95 /* C4Network2IRC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFFAA14D0C2A896200444B95 /* C4Network2IRC.cpp */; }; + D0AD7B7F0FCD7C1C00EAC39A /* C4PlayerControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0AD7B7E0FCD7C1C00EAC39A /* C4PlayerControl.cpp */; }; D406269A0BB9440C00815E5D /* c4group_ng.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D40626990BB9440C00815E5D /* c4group_ng.cpp */; }; D40626A00BB9477A00815E5D /* C4Config.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D439CADC0B8CB129001D508B /* C4Config.cpp */; }; D40626A10BB9477A00815E5D /* C4Group.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D439CAF80B8CB12A001D508B /* C4Group.cpp */; }; @@ -284,6 +285,8 @@ AFFAA14B0C2A896200444B95 /* C4ChatDlg.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = C4ChatDlg.cpp; sourceTree = ""; }; AFFAA14C0C2A896200444B95 /* C4InteractiveThread.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = C4InteractiveThread.cpp; sourceTree = ""; }; AFFAA14D0C2A896200444B95 /* C4Network2IRC.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = C4Network2IRC.cpp; sourceTree = ""; }; + D0AD7B7D0FCD7C0C00EAC39A /* C4PlayerControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = C4PlayerControl.h; sourceTree = ""; }; + D0AD7B7E0FCD7C1C00EAC39A /* C4PlayerControl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = C4PlayerControl.cpp; sourceTree = ""; }; D40626910BB9418400815E5D /* c4group */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = c4group; sourceTree = BUILT_PRODUCTS_DIR; }; D40626990BB9440C00815E5D /* c4group_ng.cpp */ = {isa = PBXFileReference; fileEncoding = 12; lastKnownFileType = sourcecode.cpp.cpp; name = c4group_ng.cpp; path = ../group/c4group_ng.cpp; sourceTree = SOURCE_ROOT; }; D40626AA0BB947A700815E5D /* C4Update.cpp */ = {isa = PBXFileReference; fileEncoding = 12; lastKnownFileType = sourcecode.cpp.cpp; path = C4Update.cpp; sourceTree = ""; }; @@ -886,6 +889,7 @@ D439CAD10B8CB129001D508B /* src */ = { isa = PBXGroup; children = ( + D0AD7B7E0FCD7C1C00EAC39A /* C4PlayerControl.cpp */, 3562890E0DA62F9000FB1482 /* C4MainMenu.cpp */, 3562890F0DA62F9000FB1482 /* C4ObjectMenu.cpp */, 3588FB750DA0FFD2001544C0 /* C4FileSelDlg.cpp */, @@ -1068,6 +1072,7 @@ D45B48A40D6B333A001A7AE2 /* inc */ = { isa = PBXGroup; children = ( + D0AD7B7D0FCD7C0C00EAC39A /* C4PlayerControl.h */, 356289130DA62FA600FB1482 /* C4MainMenu.h */, 356288E60DA62CAD00FB1482 /* C4ObjectMenu.h */, 3588FB730DA0FFBE001544C0 /* C4FileSelDlg.h */, @@ -1515,6 +1520,7 @@ 356289100DA62F9000FB1482 /* C4MainMenu.cpp in Sources */, 356289110DA62F9000FB1482 /* C4ObjectMenu.cpp in Sources */, 3514CB3A0DBCCE5F00534809 /* OpenURL.cpp in Sources */, + D0AD7B7F0FCD7C1C00EAC39A /* C4PlayerControl.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From 2d5ad9fca1d918f6e1d4c6b22142125c82373499 Mon Sep 17 00:00:00 2001 From: Sven Eberhardt Date: Wed, 27 May 2009 20:14:54 -0400 Subject: [PATCH 04/12] Moving controls to script... --- engine/inc/C4Control.h | 19 +- engine/inc/C4Game.h | 3 - engine/inc/C4Gui.h | 10 +- engine/inc/C4KeyboardInput.h | 21 +- engine/inc/C4MessageInput.h | 8 +- engine/inc/C4Object.h | 4 - engine/inc/C4Player.h | 11 +- engine/inc/C4PlayerControl.h | 67 +++- engine/inc/C4StartupOptionsDlg.h | 2 +- engine/src/C4Control.cpp | 29 +- engine/src/C4Game.cpp | 96 ------ engine/src/C4GraphicsSystem.cpp | 2 +- engine/src/C4GuiDialogs.cpp | 2 +- engine/src/C4GuiEdit.cpp | 2 +- engine/src/C4GuiMenu.cpp | 3 +- engine/src/C4KeyboardInput.cpp | 9 +- engine/src/C4MessageInput.cpp | 8 +- engine/src/C4MouseControl.cpp | 5 +- engine/src/C4Object.cpp | 493 ----------------------------- engine/src/C4ObjectCom.cpp | 14 +- engine/src/C4ObjectMenu.cpp | 12 +- engine/src/C4Packet2.cpp | 2 +- engine/src/C4Player.cpp | 141 +-------- engine/src/C4PlayerControl.cpp | 309 ++++++++++++++++-- engine/src/C4PlayerList.cpp | 6 +- engine/src/C4Script.cpp | 20 -- engine/src/C4StartupOptionsDlg.cpp | 2 +- engine/src/C4Viewport.cpp | 34 +- 28 files changed, 424 insertions(+), 910 deletions(-) diff --git a/engine/inc/C4Control.h b/engine/inc/C4Control.h index 8dca26d98..630beef3e 100644 --- a/engine/inc/C4Control.h +++ b/engine/inc/C4Control.h @@ -163,22 +163,11 @@ public: class C4ControlPlayerControl : public C4ControlPacket // sync { public: - C4ControlPlayerControl() - : iPlr(-1), iCom(-1), iData(-1) { } - C4ControlPlayerControl(int32_t iPlr, int32_t iCom, int32_t iData) - : iPlr(iPlr), iCom(iCom), iData(iData) { } -protected: - int32_t iPlr, iCom, iData; -public: - DECLARE_C4CONTROL_VIRTUALS -}; - -class C4ControlPlayerControl2 : public C4ControlPacket // sync -{ -public: - C4ControlPlayerControl2() : iPlr(-1), fRelease(false) {} - C4ControlPlayerControl2(int32_t iPlr, bool fRelease, const C4KeyEventData &rExtraData) + C4ControlPlayerControl() : iPlr(-1), fRelease(false) {} + C4ControlPlayerControl(int32_t iPlr, bool fRelease, const C4KeyEventData &rExtraData) : iPlr(iPlr), fRelease(fRelease), ExtraData(rExtraData) { } + C4ControlPlayerControl(int32_t iPlr, int32_t iControl, int32_t iExtraData) // old-style menu com emulation + : iPlr(iPlr), fRelease(false), ExtraData(iExtraData,0,0) { AddControl(iControl,0); } struct ControlItem { diff --git a/engine/inc/C4Game.h b/engine/inc/C4Game.h index 90e0e6064..513dbf718 100644 --- a/engine/inc/C4Game.h +++ b/engine/inc/C4Game.h @@ -204,9 +204,6 @@ class C4Game 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); bool Init(); bool PreInit(); diff --git a/engine/inc/C4Gui.h b/engine/inc/C4Gui.h index d92439d4d..338f7bae5 100644 --- a/engine/inc/C4Gui.h +++ b/engine/inc/C4Gui.h @@ -1163,7 +1163,7 @@ namespace C4GUI { private: enum CursorOperation { COP_BACK, COP_DELETE, COP_LEFT, COP_RIGHT, COP_HOME, COP_END, }; - bool KeyCursorOp(C4KeyCodeEx key, CursorOperation op); + bool KeyCursorOp(const C4KeyCodeEx &key, CursorOperation op); bool KeyEnter(); bool KeyCopy() { Copy(); return true; } bool KeyPaste() { Paste(); return true; } @@ -1671,7 +1671,7 @@ namespace C4GUI { bool KeyBack(); bool KeyAbort(); bool KeyConfirm(); - bool KeyHotkey(C4KeyCodeEx key); + bool KeyHotkey(const C4KeyCodeEx &key); private: static int32_t iGlobalMenuIndex; @@ -2005,7 +2005,7 @@ namespace C4GUI { virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // input: mouse. forwards to child controls private: - bool KeyHotkey(C4KeyCodeEx key); + bool KeyHotkey(const C4KeyCodeEx &key); bool KeyFocusDefault(); public: @@ -2329,7 +2329,7 @@ namespace C4GUI { { private: typedef C4KeyCBPassKey Base; - typedef bool(TargetClass::*CallbackFunc)(C4KeyCodeEx key); + typedef bool(TargetClass::*CallbackFunc)(const C4KeyCodeEx &key); public: DlgKeyCBPassKey(TargetClass &rTarget, CallbackFunc pFuncDown, CallbackFunc pFuncUp=NULL, CallbackFunc pFuncPressed=NULL) : Base(rTarget, pFuncDown, pFuncUp, pFuncPressed) {} @@ -2366,7 +2366,7 @@ namespace C4GUI { { private: typedef C4KeyCBExPassKey Base; - typedef bool(TargetClass::*CallbackFunc)(C4KeyCodeEx key, ParameterType par); + typedef bool(TargetClass::*CallbackFunc)(const C4KeyCodeEx &key, ParameterType par); public: ControlKeyCBExPassKey(TargetClass &rTarget, const ParameterType &rPar, CallbackFunc pFuncDown, CallbackFunc pFuncUp=NULL, CallbackFunc pFuncPressed=NULL) : Base(rTarget, rPar, pFuncDown, pFuncUp, pFuncPressed) {} diff --git a/engine/inc/C4KeyboardInput.h b/engine/inc/C4KeyboardInput.h index c8b9776f9..bd1d196e2 100644 --- a/engine/inc/C4KeyboardInput.h +++ b/engine/inc/C4KeyboardInput.h @@ -176,6 +176,7 @@ 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) {} + C4KeyEventData(int32_t iStrength, int32_t x, int32_t y) : iStrength(iStrength), x(x), y(y) {} void CompileFunc(StdCompiler *pComp); bool operator ==(const struct C4KeyEventData &cmp) const; }; @@ -189,7 +190,7 @@ class C4KeyboardCallbackInterface class C4CustomKey *pOriginalKey; public: - virtual bool OnKeyEvent(C4KeyCodeEx key, C4KeyEventType eEv) = 0; // return true if processed + virtual bool OnKeyEvent(const C4KeyCodeEx &key, C4KeyEventType eEv) = 0; // return true if processed friend class C4KeyboardMapping; @@ -214,7 +215,7 @@ template class C4KeyCB : public C4KeyboardCallbackInterface CallbackFunc pFuncDown, pFuncUp, pFuncPressed; protected: - virtual bool OnKeyEvent(C4KeyCodeEx key, C4KeyEventType eEv) + virtual bool OnKeyEvent(const C4KeyCodeEx &key, C4KeyEventType eEv) { if (!CheckCondition()) return false; switch (eEv) @@ -237,14 +238,14 @@ template class C4KeyCB : public C4KeyboardCallbackInterface template class C4KeyCBPassKey : public C4KeyboardCallbackInterface { public: - typedef bool(TargetClass::*CallbackFunc)(C4KeyCodeEx key); + typedef bool(TargetClass::*CallbackFunc)(const C4KeyCodeEx &key); protected: TargetClass &rTarget; CallbackFunc pFuncDown, pFuncUp, pFuncPressed; protected: - virtual bool OnKeyEvent(C4KeyCodeEx key, C4KeyEventType eEv) + virtual bool OnKeyEvent(const C4KeyCodeEx &key, C4KeyEventType eEv) { if (!CheckCondition()) return false; switch (eEv) @@ -275,7 +276,7 @@ template class C4KeyCBEx : public C4Key ParameterType par; protected: - virtual bool OnKeyEvent(C4KeyCodeEx key, C4KeyEventType eEv) + virtual bool OnKeyEvent(const C4KeyCodeEx &key, C4KeyEventType eEv) { if (!CheckCondition()) return false; switch (eEv) @@ -297,7 +298,7 @@ template class C4KeyCBEx : public C4Key template class C4KeyCBExPassKey : public C4KeyboardCallbackInterface { public: - typedef bool(TargetClass::*CallbackFunc)(C4KeyCodeEx key, ParameterType par); + typedef bool(TargetClass::*CallbackFunc)(const C4KeyCodeEx &key, ParameterType par); protected: TargetClass &rTarget; @@ -305,7 +306,7 @@ template class C4KeyCBExPassKey : publi ParameterType par; protected: - virtual bool OnKeyEvent(C4KeyCodeEx key, C4KeyEventType eEv) + virtual bool OnKeyEvent(const C4KeyCodeEx &key, C4KeyEventType eEv) { if (!CheckCondition()) return false; switch (eEv) @@ -356,9 +357,9 @@ class C4CustomKey int iRef; public: - C4CustomKey(C4KeyCodeEx DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority = PRIO_Base); // ctor for default key + C4CustomKey(const C4KeyCodeEx &DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority = PRIO_Base); // ctor for default key C4CustomKey(const CodeList &rDefCodes, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority = PRIO_Base); // ctor for default key with multiple possible keys assigned - C4CustomKey(C4KeyCodeEx Code, const StdStrBuf &rName); // ctor for single custom key override + C4CustomKey(const C4KeyCodeEx &Code, const StdStrBuf &rName); // ctor for single custom key override C4CustomKey(const C4CustomKey &rCpy, bool fCopyCallbacks); virtual ~C4CustomKey(); // dtor @@ -382,7 +383,7 @@ class C4CustomKey class C4KeyBinding : protected C4CustomKey { public: - C4KeyBinding(C4KeyCodeEx DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority = PRIO_Base); // ctor for default key + C4KeyBinding(const C4KeyCodeEx &DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority = PRIO_Base); // ctor for default key C4KeyBinding(const CodeList &rDefCodes, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority = PRIO_Base); // ctor for default key ~C4KeyBinding(); }; diff --git a/engine/inc/C4MessageInput.h b/engine/inc/C4MessageInput.h index 90fdcb5f4..4cd317a3f 100644 --- a/engine/inc/C4MessageInput.h +++ b/engine/inc/C4MessageInput.h @@ -46,10 +46,10 @@ class C4ChatInputDialog : public C4GUI::InputDialog private: bool KeyHistoryUpDown(bool fUp); bool KeyCompleteNick(); // complete nick at cursor pos of edit - bool KeyPlrControl(C4KeyCodeEx key); - bool KeyGamepadControlDown(C4KeyCodeEx key); - bool KeyGamepadControlUp(C4KeyCodeEx key); - bool KeyGamepadControlPressed(C4KeyCodeEx key); + bool KeyPlrControl(const C4KeyCodeEx &key); + bool KeyGamepadControlDown(const C4KeyCodeEx &key); + bool KeyGamepadControlUp(const C4KeyCodeEx &key); + bool KeyGamepadControlPressed(const C4KeyCodeEx &key); bool KeyBackspaceClose(); // close if chat text box is empty (on backspace) protected: diff --git a/engine/inc/C4Object.h b/engine/inc/C4Object.h index 7a0949464..5027f8670 100644 --- a/engine/inc/C4Object.h +++ b/engine/inc/C4Object.h @@ -220,7 +220,6 @@ class C4Object C4Object *ComposeContents(C4ID id); BOOL MenuCommand(const char *szCommand); - BOOL CallControl(C4Player *pPlr, BYTE byCom, C4AulParSet *pPars = 0); C4Value Call(const char *szFunctionCall, C4AulParSet *pPars = 0, bool fPassError = false); @@ -338,9 +337,6 @@ class C4Object const char *GetName(); void SetName (const char *NewName = 0); int32_t GetValue(C4Object *pInBase, int32_t iForPlayer); - void DirectCom(BYTE byCom, int32_t iData); - void AutoStopDirectCom(BYTE byCom, int32_t iData); - void AutoStopUpdateComDir(); BOOL BuyEnergy(); void AutoSellContents(); BOOL SetOwner(int32_t iOwner); diff --git a/engine/inc/C4Player.h b/engine/inc/C4Player.h index 51ed23811..143050990 100644 --- a/engine/inc/C4Player.h +++ b/engine/inc/C4Player.h @@ -24,6 +24,7 @@ #include #include #include +#include const int32_t C4PVM_Cursor = 0, C4PVM_Target = 1, @@ -70,7 +71,7 @@ class C4Player: public C4PlayerInfoCore int32_t Team; // team ID - may be 0 for no teams int32_t Color; // OldGfx color index uint32_t ColorDw; // color as DWord for newgfx - int32_t Control; + int32_t ControlSet; int32_t MouseControl; int32_t Position; int32_t PlrStartIndex; @@ -112,13 +113,10 @@ class C4Player: public C4PlayerInfoCore C4IDList Knowledge; C4IDList Magic; // Control + C4PlayerControl Control; C4Object *Cursor, *ViewCursor; int32_t SelectCount; int32_t SelectFlash,CursorFlash; - int32_t LastCom; - int32_t LastComDelay; - int32_t LastComDownDouble; - int32_t PressedComs; int32_t CursorSelection,CursorToggled; class C4GamePadOpener *pGamepad; // Message @@ -169,9 +167,6 @@ class C4Player: public C4PlayerInfoCore void CursorToggle(); void SelectAllCrew(); void UpdateSelectionToggleStatus(); - void DirectCom(BYTE byCom, int32_t iData); - void InCom(BYTE byCom, int32_t iData); - BOOL ObjectCom(BYTE byCom, int32_t iData); BOOL ObjectCommand(int32_t iCommand, C4Object *pTarget, int32_t iTx, int32_t iTy, C4Object *pTarget2=NULL, int32_t iData=0, int32_t iAddMode=C4P_Command_Set); void ObjectCommand2Obj(C4Object *cObj, int32_t iCommand, C4Object *pTarget, int32_t iX, int32_t iY, C4Object *pTarget2, int32_t iData, int32_t iMode); BOOL DoPoints(int32_t iChange); diff --git a/engine/inc/C4PlayerControl.h b/engine/inc/C4PlayerControl.h index 375965ceb..be7e712f0 100644 --- a/engine/inc/C4PlayerControl.h +++ b/engine/inc/C4PlayerControl.h @@ -60,6 +60,8 @@ class C4PlayerControlDef bool IsHoldKey() const { return fIsHoldKey; } C4ID GetExtraData() const { return idControlExtraData; } bool IsGlobal() const { return fGlobal; } + int32_t GetRepeatDelay() const { return iRepeatDelay; } + int32_t GetInitialRepeatDelay() const { return iInitialRepeatDelay; } //C4PlayerControlDef &operator =(const C4PlayerControlDef &src); bool operator ==(const C4PlayerControlDef &cmp) const; @@ -80,6 +82,15 @@ class C4PlayerControlDefs typedef std::vector DefVecImpl; DefVecImpl Defs; + public: + struct CInternalCons + { + int32_t CON_MenuSelect, CON_MenuEnterAll, CON_MenuEnter, CON_MenuClose; + CInternalCons() : CON_MenuSelect(CON_None), CON_MenuEnterAll(CON_None), CON_MenuEnter(CON_None), CON_MenuClose(CON_None) {} + } InternalCons; + + void UpdateInternalCons(); + public: C4PlayerControlDefs() {} ~C4PlayerControlDefs() {} @@ -88,12 +99,25 @@ class C4PlayerControlDefs 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); + const C4PlayerControlDef *GetControlByIndex(int32_t idx) const; int32_t GetControlIndexByIdentifier(const char *szIdentifier) const; // return CON_None for not found + int32_t GetCount() const { return Defs.size(); } bool operator ==(const C4PlayerControlDefs &cmp) const { return Defs == cmp.Defs; } }; +struct C4PlayerControlRecentKey + { + C4KeyCodeEx Key; + DWORD tTime; + C4PlayerControlRecentKey(const C4KeyCodeEx &Key, DWORD tTime) : Key(Key), tTime(tTime) {} + 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; + +typedef std::vector C4KeyCodeExVec; + // a key/mouse/gamepad assignment to a PlayerControlDef class C4PlayerControlAssignment { @@ -140,6 +164,7 @@ class C4PlayerControlAssignment void CompileFunc(StdCompiler *pComp); bool ResolveRefs(class C4PlayerControlAssignmentSet *pParentSet, C4PlayerControlDefs *pControlDefs); // resolve references between assignments + bool IsComboMatched(const C4PlayerControlRecentKeyList &DownKeys, const C4PlayerControlRecentKeyList &RecentKeys) const; // check if combo is currently fulfilled (assuming TriggerKey is already matched) bool operator ==(const C4PlayerControlAssignment &cmp) const; // doesn't compare resolved TriggerKey/iControl bool operator <(const C4PlayerControlAssignment &cmp) const { return iPriority < cmp.iPriority; } @@ -148,19 +173,13 @@ class C4PlayerControlAssignment bool IsRefsResolved() const { return fRefsResolved; } bool IsAlwaysUnhandled() const { return iTriggerMode & CTM_AlwaysUnhandled; } int32_t GetTriggerMode() const { return iTriggerMode; } + const C4KeyCodeEx &GetTriggerKey() const { return TriggerKey; } + bool HasCombo() const { return KeyCombo.size()>1; } }; typedef std::vector C4PlayerControlAssignmentVec; +typedef std::vector C4PlayerControlAssignmentPVec; -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 @@ -181,7 +200,8 @@ class C4PlayerControlAssignmentSet const char *GetName() const { return sName.getData(); } 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 + void GetAssignmentsByKey(const C4PlayerControlDefs &rDefs, const C4KeyCodeEx &key, bool fHoldKeysOnly, C4PlayerControlAssignmentPVec *pOutVec, const C4PlayerControlRecentKeyList &DownKeys, const C4PlayerControlRecentKeyList &RecentKeys) const; // match only by TriggerKey (last key of Combo) if fHoldKeysOnly + void GetTriggerKeys(const C4PlayerControlDefs &rDefs, C4KeyCodeExVec *pRegularKeys, C4KeyCodeExVec *pHoldKeys) const; // put all trigger keys of keyset into output vectors bool operator ==(const C4PlayerControlAssignmentSet &cmp) const; }; @@ -225,6 +245,9 @@ class C4PlayerControlFile // runtime information about a player's control class C4PlayerControl { + public: + enum { MaxRecentKeyLookback = 3000, MaxSequenceKeyDelay = 800 }; // milliseconds: Time to press key combos + private: C4PlayerControlDefs &ControlDefs; // shortcut @@ -232,8 +255,9 @@ class C4PlayerControl int32_t iPlr; // async values - C4PlayerControlAssignmentSet *pControlSet; // the control set used by this player - std::list KeyBindings; // keys registered into Game.KeyboardInput + C4PlayerControlAssignmentSet *pControlSet; // the control set used by this player - may be NULL if the player cannot be controlled! + typedef std::list KeyBindingList; + KeyBindingList KeyBindings; // keys registered into Game.KeyboardInput C4PlayerControlRecentKeyList RecentKeys; // keys pressed recently; for combinations C4PlayerControlRecentKeyList DownKeys; // keys currently held down @@ -247,8 +271,13 @@ class C4PlayerControl 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) {} + bool IsDown() const { return DownState.iStrength>0; } + + ControlDownState() : DownState(), iDownFrame(0), fDownByUser(false) {} + void CompileFunc(StdCompiler *pComp); + bool operator ==(const ControlDownState &cmp) const; }; - typedef std::vector DownStateVec; + 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. @@ -259,6 +288,7 @@ class C4PlayerControl void SetControlDownState(int32_t iControl, const C4KeyEventData &rDownState, int32_t iDownFrame, bool fDownByUser); void SetControlDisabled(int32_t iControl, int32_t iVal); + void Clear(); void CompileFunc(StdCompiler *pComp); bool operator ==(const CSync &cmp) const; } Sync; @@ -273,8 +303,8 @@ class C4PlayerControl 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(); + // init + void AddKeyBinding(const C4KeyCodeEx &key, bool fHoldKey, int32_t idx); public: C4PlayerControl(); @@ -288,7 +318,10 @@ class C4PlayerControl bool IsGlobal() const { return iPlr==-1; } // callback from control queue - void ExecuteControlPacket(const class C4ControlPlayerControl2 *pCtrl); + void ExecuteControlPacket(const class C4ControlPlayerControl *pCtrl); + + // sync execution: Do keyrepeat, etc. + void Execute(); }; diff --git a/engine/inc/C4StartupOptionsDlg.h b/engine/inc/C4StartupOptionsDlg.h index d6c85101f..a7c6f3707 100644 --- a/engine/inc/C4StartupOptionsDlg.h +++ b/engine/inc/C4StartupOptionsDlg.h @@ -171,7 +171,7 @@ class C4StartupOptionsDlg : public C4StartupDlg int32_t iCtrlSet; protected: - bool KeyDown(C4KeyCodeEx key); + bool KeyDown(const C4KeyCodeEx &key); public: KeySelDialog(int32_t iKeyID, int32_t iCtrlSet, bool fGamepad); virtual ~KeySelDialog(); diff --git a/engine/src/C4Control.cpp b/engine/src/C4Control.cpp index a5d3a64e0..5d809b4ad 100644 --- a/engine/src/C4Control.cpp +++ b/engine/src/C4Control.cpp @@ -355,31 +355,10 @@ void C4ControlPlayerSelect::CompileFunc(StdCompiler *pComp) C4ControlPacket::CompileFunc(pComp); } + // *** C4ControlPlayerControl void C4ControlPlayerControl::Execute() const -{ - C4Player *pPlr=Game.Players.Get(iPlr); - if(pPlr) - { - if (!Inside(iCom, COM_ReleaseFirst, COM_ReleaseLast)) - pPlr->CountControl(C4Player::PCID_DirectCom, iCom*10000+iData); - pPlr->InCom(iCom, iData); - } -} - -void C4ControlPlayerControl::CompileFunc(StdCompiler *pComp) -{ - pComp->Value(mkNamingAdapt(mkIntPackAdapt(iPlr), "Player", -1)); - pComp->Value(mkNamingAdapt(mkIntPackAdapt(iCom), "Com", 0)); - pComp->Value(mkNamingAdapt(mkIntPackAdapt(iData), "Data", 0)); - C4ControlPacket::CompileFunc(pComp); -} - - -// *** C4ControlPlayerControl2 - -void C4ControlPlayerControl2::Execute() const { C4PlayerControl *pTargetCtrl = NULL; if (iPlr == -1) @@ -392,20 +371,20 @@ void C4ControlPlayerControl2::Execute() const C4Player *pPlr=Game.Players.Get(iPlr); if (pPlr) { - //pPlr->CountControl(C4Player::PCID_DirectCom, iCom*10000+iData); + pTargetCtrl = &(pPlr->Control); } } if (pTargetCtrl) pTargetCtrl->ExecuteControlPacket(this); } -void C4ControlPlayerControl2::ControlItem::CompileFunc(StdCompiler *pComp) +void C4ControlPlayerControl::ControlItem::CompileFunc(StdCompiler *pComp) { pComp->Value(iControl); pComp->Seperator(); pComp->Value(iTriggerMode); } -void C4ControlPlayerControl2::CompileFunc(StdCompiler *pComp) +void C4ControlPlayerControl::CompileFunc(StdCompiler *pComp) { pComp->Value(mkNamingAdapt(mkIntPackAdapt(iPlr), "Player", -1)); pComp->Value(mkNamingAdapt(fRelease, "Release", false)); diff --git a/engine/src/C4Game.cpp b/engine/src/C4Game.cpp index 91ab28120..663866b73 100644 --- a/engine/src/C4Game.cpp +++ b/engine/src/C4Game.cpp @@ -3161,37 +3161,6 @@ bool C4Game::InitKeyboard() KeyboardInput.RegisterKey(new C4CustomKey(C4KeyCodeEx(KEY_Default ), "NetAllowJoinToggle", KEYSCOPE_Generic, new C4KeyCB (Network, &C4Network2::ToggleAllowJoin))); KeyboardInput.RegisterKey(new C4CustomKey(C4KeyCodeEx(KEY_Default ), "NetStatsToggle", KEYSCOPE_Generic, new C4KeyCB (GraphicsSystem, &C4GraphicsSystem::ToggleShowNetStatus))); - // Map player keyboard controls - int32_t iKdbSet,iCtrl; - StdStrBuf sPlrCtrlName; - for (iKdbSet=C4P_Control_Keyboard1; iKdbSet<=C4P_Control_Keyboard4; iKdbSet++) - for (iCtrl=0; iCtrl(*this, C4KeySetCtrl(iKdbSet, iCtrl), &C4Game::LocalControlKey, &C4Game::LocalControlKeyUp), - C4CustomKey::PRIO_PlrControl)); - } - - // Map player gamepad controls - int32_t iGamepad; - for (iGamepad=C4P_Control_GamePad1; iGamepad<=C4P_Control_GamePad1+C4ConfigMaxGamepads; iGamepad++) - { - C4ConfigGamepad &cfg = Config.Gamepads[iGamepad-C4P_Control_GamePad1]; - for (iCtrl=0; iCtrl(*this, C4KeySetCtrl(iGamepad, iCtrl), &C4Game::LocalControlKey, &C4Game::LocalControlKeyUp), - C4CustomKey::PRIO_PlrControl)); - } - } - // load any custom keysboard overloads KeyboardInput.LoadCustomConfig(); @@ -3297,71 +3266,6 @@ void C4Game::FixRandom(int32_t iSeed) Randomize3(); } -bool C4Game::LocalControlKey(C4KeyCodeEx key, C4KeySetCtrl Ctrl) - { - // keyboard callback: Perform local player control - C4Player *pPlr; - if (pPlr = Players.GetLocalByKbdSet(Ctrl.iKeySet)) - { - // Swallow a event generated from Keyrepeat for AutoStopControl - if (pPlr->PrefControlStyle) - { - if (key.IsRepeated()) - return true; - } - LocalPlayerControl(pPlr->Number,Control2Com(Ctrl.iCtrl, false)); - return true; - } - // not processed - must return false here, so unused keyboard control sets do not block used ones - return false; - } - -bool C4Game::LocalControlKeyUp(C4KeyCodeEx key, C4KeySetCtrl Ctrl) - { - // Direct callback for released key in AutoStopControl-mode (ignore repeated) - if (key.IsRepeated()) - return true; - C4Player *pPlr; - if ((pPlr = Players.GetLocalByKbdSet(Ctrl.iKeySet)) && pPlr->PrefControlStyle) - { - int iCom = Control2Com(Ctrl.iCtrl, true); - if (iCom != COM_None) LocalPlayerControl(pPlr->Number, iCom); - return true; - } - // not processed - must return false here, so unused keyboard control sets do not block used ones - return false; - } - -void C4Game::LocalPlayerControl(int32_t iPlayer, int32_t iCom) - { - C4Player *pPlr = Players.Get(iPlayer); if (!pPlr) return; - int32_t iData=0; - // Menu button com - if (iCom==COM_PlayerMenu) - { - // Player menu open: close - if (pPlr->Menu.IsActive()) - pPlr->Menu.Close(false); - // Menu closed: open main menu - else - pPlr->ActivateMenuMain(); - return; - } - // Local player menu active: convert menu com and control local - if (pPlr->Menu.ConvertCom(iCom,iData,true)) - { - pPlr->Menu.Control(iCom,iData); - return; - } - // Pre-queue asynchronous menu conversions - if (pPlr->Cursor && pPlr->Cursor->Menu) - pPlr->Cursor->Menu->ConvertCom(iCom,iData,true); - // Not for eliminated (checked again in DirectCom, but make sure no control is generated for eliminated players!) - if (pPlr->Eliminated) return; - // Player control: add to control queue - Input.Add(CID_PlrControl, new C4ControlPlayerControl(iPlayer,iCom,iData)); - } - BOOL C4Game::DefinitionFilenamesFromSaveGame() { const char *pSource; diff --git a/engine/src/C4GraphicsSystem.cpp b/engine/src/C4GraphicsSystem.cpp index 8df85e7a9..1c0260269 100644 --- a/engine/src/C4GraphicsSystem.cpp +++ b/engine/src/C4GraphicsSystem.cpp @@ -515,7 +515,7 @@ void C4GraphicsSystem::SortViewportsByPlayerControl() pPlr1 = Game.Players.Get(pView->Player); pPlr2 = Game.Players.Get(pNext->Player); // Swap order - if (pPlr1 && pPlr2 && ( LayoutOrder(pPlr1->Control) > LayoutOrder(pPlr2->Control) )) + if (pPlr1 && pPlr2 && ( LayoutOrder(pPlr1->ControlSet) > LayoutOrder(pPlr2->ControlSet) )) { if (pPrev) pPrev->Next = pNext; else FirstViewport = pNext; pView->Next = pNext->Next; diff --git a/engine/src/C4GuiDialogs.cpp b/engine/src/C4GuiDialogs.cpp index 44faccc02..ea5761fb8 100644 --- a/engine/src/C4GuiDialogs.cpp +++ b/engine/src/C4GuiDialogs.cpp @@ -593,7 +593,7 @@ BOOL Dialog::CharIn(const char * c) return FALSE; } -bool Dialog::KeyHotkey(C4KeyCodeEx key) +bool Dialog::KeyHotkey(const C4KeyCodeEx &key) { WORD wKey = WORD(key.Key); // do hotkey procs for standard alphanumerics only diff --git a/engine/src/C4GuiEdit.cpp b/engine/src/C4GuiEdit.cpp index c8634dc2a..ce5a406bb 100644 --- a/engine/src/C4GuiEdit.cpp +++ b/engine/src/C4GuiEdit.cpp @@ -396,7 +396,7 @@ bool Edit::KeyEnter() return true; } -bool Edit::KeyCursorOp(C4KeyCodeEx key, CursorOperation op) +bool Edit::KeyCursorOp(const C4KeyCodeEx &key, CursorOperation op) { bool fShift = !!(key.dwShift & KEYS_Shift); bool fCtrl = !!(key.dwShift & KEYS_Control); diff --git a/engine/src/C4GuiMenu.cpp b/engine/src/C4GuiMenu.cpp index 77bdc6ade..371e407fa 100644 --- a/engine/src/C4GuiMenu.cpp +++ b/engine/src/C4GuiMenu.cpp @@ -296,7 +296,8 @@ bool ContextMenu::KeyConfirm() DoOK(); return true; } -bool ContextMenu::KeyHotkey(C4KeyCodeEx key) + +bool ContextMenu::KeyHotkey(const C4KeyCodeEx &key) { // not if focus is in submenu if (pSubmenu) return false; diff --git a/engine/src/C4KeyboardInput.cpp b/engine/src/C4KeyboardInput.cpp index 7fbd4392c..dd6a0fda2 100644 --- a/engine/src/C4KeyboardInput.cpp +++ b/engine/src/C4KeyboardInput.cpp @@ -511,7 +511,7 @@ bool C4KeyEventData::operator ==(const struct C4KeyEventData &cmp) const /* ----------------- C4CustomKey------------------ */ -C4CustomKey::C4CustomKey(C4KeyCodeEx DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority) +C4CustomKey::C4CustomKey(const C4KeyCodeEx &DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority) : Scope(Scope), Name(), uiPriority(uiPriority), iRef(0) { // generate code @@ -539,7 +539,7 @@ C4CustomKey::C4CustomKey(const CodeList &rDefCodes, const char *szName, C4KeySco } } -C4CustomKey::C4CustomKey(C4KeyCodeEx Code, const StdStrBuf &rName) +C4CustomKey::C4CustomKey(const C4KeyCodeEx &Code, const StdStrBuf &rName) : Codes(), DefaultCodes(), Scope(KEYSCOPE_None), Name(), uiPriority(PRIO_None), iRef(0) { // ctor for custom key override @@ -620,7 +620,7 @@ bool C4CustomKey::Execute(C4KeyEventType eEv, C4KeyCodeEx key) /* ----------------- C4KeyBinding ------------------ */ -C4KeyBinding::C4KeyBinding(C4KeyCodeEx DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority) +C4KeyBinding::C4KeyBinding(const C4KeyCodeEx &DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority) : C4CustomKey(DefCode, szName, Scope, pCallback, uiPriority) { // self holds a ref @@ -764,6 +764,9 @@ void C4KeyboardInput::UnregisterKeyBinding(C4CustomKey *pUnregKey) bool C4KeyboardInput::DoInput(const C4KeyCodeEx &InKey, C4KeyEventType InEvent, DWORD InScope) { + // store last-key-info + LastKeyExtraData.iStrength = (InEvent != KEYEV_Up) * 100; + LastKeyExtraData.x = LastKeyExtraData.y = 0; // check all key events generated by this key: First the keycode itself, then any more generic key events like KEY_Any const int32_t iKeyRangeMax = 5; int32_t iKeyRangeCnt=0, j; diff --git a/engine/src/C4MessageInput.cpp b/engine/src/C4MessageInput.cpp index 37857b2db..f2c801c71 100644 --- a/engine/src/C4MessageInput.cpp +++ b/engine/src/C4MessageInput.cpp @@ -177,7 +177,7 @@ bool C4ChatInputDialog::KeyHistoryUpDown(bool fUp) return true; } -bool C4ChatInputDialog::KeyPlrControl(C4KeyCodeEx key) +bool C4ChatInputDialog::KeyPlrControl(const C4KeyCodeEx &key) { // Control pressed while doing this key: Reroute this key as a player-control Game.DoKeyboardInput(WORD(key.Key), KEYEV_Down, !!(key.dwShift & KEYS_Alt), false, !!(key.dwShift & KEYS_Shift), key.IsRepeated(), NULL, true); @@ -185,7 +185,7 @@ bool C4ChatInputDialog::KeyPlrControl(C4KeyCodeEx key) return true; } -bool C4ChatInputDialog::KeyGamepadControlDown(C4KeyCodeEx key) +bool C4ChatInputDialog::KeyGamepadControlDown(const C4KeyCodeEx &key) { // filter gamepad control if (!Key_IsGamepad(key.Key)) return false; @@ -194,7 +194,7 @@ bool C4ChatInputDialog::KeyGamepadControlDown(C4KeyCodeEx key) return true; } -bool C4ChatInputDialog::KeyGamepadControlUp(C4KeyCodeEx key) +bool C4ChatInputDialog::KeyGamepadControlUp(const C4KeyCodeEx &key) { // filter gamepad control if (!Key_IsGamepad(key.Key)) return false; @@ -203,7 +203,7 @@ bool C4ChatInputDialog::KeyGamepadControlUp(C4KeyCodeEx key) return true; } -bool C4ChatInputDialog::KeyGamepadControlPressed(C4KeyCodeEx key) +bool C4ChatInputDialog::KeyGamepadControlPressed(const C4KeyCodeEx &key) { // filter gamepad control if (!Key_IsGamepad(key.Key)) return false; diff --git a/engine/src/C4MouseControl.cpp b/engine/src/C4MouseControl.cpp index 6a68d64dd..6498d1f50 100644 --- a/engine/src/C4MouseControl.cpp +++ b/engine/src/C4MouseControl.cpp @@ -1064,8 +1064,9 @@ void C4MouseControl::Wheel(DWORD dwFlags) // Ctrl + Wheel: pass to player control (might be used for inventory or such) else { - if(iDelta > 0) Game.LocalPlayerControl(Player, COM_WheelUp); - if(iDelta < 0) Game.LocalPlayerControl(Player, COM_WheelDown); + // 2do + //if(iDelta > 0) Game.LocalPlayerControl(Player, COM_WheelUp); + //if(iDelta < 0) Game.LocalPlayerControl(Player, COM_WheelDown); } } diff --git a/engine/src/C4Object.cpp b/engine/src/C4Object.cpp index 5535576ea..c2d1b1aac 100644 --- a/engine/src/C4Object.cpp +++ b/engine/src/C4Object.cpp @@ -3181,499 +3181,6 @@ void C4Object::Clear() while (FirstRef) FirstRef->Set(0); } -BOOL C4Object::ContainedControl(BYTE byCom) - { - // Check - if (!Contained) return FALSE; - // Check if object is about to exit; if so, return - // dunno, maybe I should check all the commands, not just the first one? - if ((byCom == COM_Left || byCom == COM_Right) && Command) - if (Command->Command == C4CMD_Exit) - // hack: in structures only; not in vehicles - // they might have a pending Exit-command due to a down-control - if (Contained->Category & C4D_Structure) - return FALSE; // or TRUE? Currently it doesn't matter. - // get script function if defined - C4AulFunc* sf = Contained->Def->Script.GetSFunc(FormatString(PSF_ContainedControl,ComName(byCom)).getData()); - // in old versions, do hardcoded actions first (until gwe3) - // new objects may overload them - C4Def *pCDef = Contained->Def; - bool fCallSfEarly = CompareVersion(pCDef->rC4XVer[0],pCDef->rC4XVer[1],pCDef->rC4XVer[2],pCDef->rC4XVer[3],4,9,1,3) >= 0; - bool result = false; - C4Player * pPlr = Game.Players.Get(Controller); - if(fCallSfEarly) - { - if (sf && !!sf->Exec(Contained, &C4AulParSet(C4VObj(this)))) result = true; - // AutoStopControl: Also notify container about controlupdate - // Note Contained may be nulled now due to ContainedControl call - if(Contained && !(byCom & (COM_Single | COM_Double)) && pPlr->PrefControlStyle) - { - int32_t PressedComs = pPlr->PressedComs; - C4AulParSet set(C4VObj(this), - C4VInt(Coms2ComDir(PressedComs)), - C4VBool(!!(PressedComs & (1 << COM_Dig))), - C4VBool(!!(PressedComs & (1 << COM_Throw)))); - Contained->Call(PSF_ContainedControlUpdate, &set); - } - } - if(result) return true; - - // hardcoded actions - switch (byCom) - { - case COM_Down: - PlayerObjectCommand(Owner,C4CMD_Exit); - break; - case COM_Throw: - PlayerObjectCommand(Owner,C4CMD_Throw); - break; - case COM_Up: - if (ValidPlr(Contained->Base)) - if (!Hostile(Owner,Contained->Base)) - if (Game.C4S.Game.Realism.BaseFunctionality & BASEFUNC_Buy) - ActivateMenu(C4MN_Buy); - break; - case COM_Dig: - if (ValidPlr(Contained->Base)) - if (!Hostile(Owner,Contained->Base)) - if (Game.C4S.Game.Realism.BaseFunctionality & BASEFUNC_Sell) - ActivateMenu(C4MN_Sell); - break; - } - // Call container script if defined for old versions - if (!fCallSfEarly) - { - if(sf) sf->Exec(Contained, &C4AulParSet(C4VObj(this))); - if(Contained && !(byCom & (COM_Single | COM_Double)) && pPlr->PrefControlStyle) - { - int32_t PressedComs = pPlr->PressedComs; - C4AulParSet set(C4VObj(this), - C4VInt(Coms2ComDir(PressedComs)), - C4VBool(!!(PressedComs & (1 << COM_Dig))), - C4VBool(!!(PressedComs & (1 << COM_Throw)))); - Contained->Call(PSF_ContainedControlUpdate, &set); - } - } - // Take/Take2 - if(!sf || fCallSfEarly) switch (byCom) - { - case COM_Left: - PlayerObjectCommand(Owner,C4CMD_Take); - break; - case COM_Right: - PlayerObjectCommand(Owner,C4CMD_Take2); - break; - } - // Success - return TRUE; - } - -BOOL C4Object::CallControl(C4Player *pPlr, BYTE byCom, C4AulParSet *pPars) - { - assert(pPlr); - - bool result = !!Call(FormatString(PSF_Control,ComName(byCom)).getData(),pPars); - - // Call ControlUpdate when using Jump'n'Run control - if(pPlr->PrefControlStyle) - { - int32_t PressedComs = pPlr->PressedComs; - C4AulParSet set(pPars ? pPars->Par[0] : C4VObj(this), - C4VInt(Coms2ComDir(PressedComs)), - C4VBool(!!(PressedComs & (1 << COM_Dig))), - C4VBool(!!(PressedComs & (1 << COM_Throw))), - C4VBool(!!(PressedComs & (1 << COM_Special))), - C4VBool(!!(PressedComs & (1 << COM_Special2)))); - Call(PSF_ControlUpdate, &set); - } - return result; - } - -void C4Object::DirectCom(BYTE byCom, int32_t iData) // By player ObjectCom - { -#ifdef DEBUGREC_OBJCOM - C4RCObjectCom rc = { byCom, iData, Number }; - AddDbgRec(RCT_ObjCom, &rc, sizeof(C4RCObjectCom)); -#endif - - // COM_Special and COM_Contents specifically bypass the menu and always go to the object - bool fBypassMenu = ((byCom == COM_Special) || (byCom == COM_Contents)); - - // Menu control - if (!fBypassMenu) - if (Menu && Menu->Control(byCom,iData)) return; - - // Ignore any menu com leftover in control queue from closed menu - if (Inside(byCom,COM_MenuNavigation1,COM_MenuNavigation2)) return; - - // Wether this is a KeyRelease-event - bool IsRelease = Inside(byCom, COM_ReleaseFirst, COM_ReleaseLast); - - // Decrease NoCollectDelay - if (!(byCom & COM_Single) && !(byCom & COM_Double) && !IsRelease) - if (NoCollectDelay>0) - NoCollectDelay--; - - // COM_Contents contents shift (data is target number (not ID!)) - // contents shift must always be done to container object, which is not necessarily this - if (byCom==COM_Contents) - { - C4Object *pTarget = Game.Objects.SafeObjectPointer(iData); - if (pTarget && pTarget->Contained) - pTarget->Contained->DirectComContents(pTarget, true); - return; - } - - // Contained control (except specials - hey, doesn't catch singles or doubles) - if (Contained) - if (byCom!=COM_Special && byCom!=COM_Special2 && byCom!=COM_WheelUp && byCom!=COM_WheelDown) - { ContainedControl(byCom); return; } - - // Regular DirectCom clears commands - if (!(byCom & COM_Single) && !(byCom & COM_Double) && !IsRelease) - ClearCommands(); - - // Object script override - C4Player *pController; - if (pController = Game.Players.Get(Controller)) - if (CallControl(pController, byCom)) - return; - - // direct wheel control - if (byCom==COM_WheelUp || byCom==COM_WheelDown) - // scroll contents - { ShiftContents(byCom==COM_WheelUp, TRUE); return; } - - // The Player updates Controller before calling this, so trust Players.Get will return it - if (pController && pController->PrefControlStyle) - { - AutoStopDirectCom(byCom, iData); - return; - } - - // Control by procedure - switch (GetProcedure()) - { - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - case DFA_WALK: - switch (byCom) - { - case COM_Left: ObjectComMovement(this,COMD_Left); break; - case COM_Right: ObjectComMovement(this,COMD_Right); break; - case COM_Down: ObjectComMovement(this,COMD_Stop); break; - case COM_Up: ObjectComUp(this); break; - case COM_Down_D: ObjectComDownDouble(this); break; - case COM_Dig_S: - if (ObjectComDig(this)) - { - Action.ComDir = (Action.Dir==DIR_Right) ? COMD_DownRight : COMD_DownLeft; - } - break; - case COM_Dig_D: ObjectComDigDouble(this); break; - case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Throw); break; - } - break; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - case DFA_FLIGHT: case DFA_KNEEL: case DFA_THROW: - switch (byCom) - { - case COM_Left: ObjectComMovement(this,COMD_Left); break; - case COM_Right: ObjectComMovement(this,COMD_Right); break; - case COM_Down: ObjectComMovement(this,COMD_Stop); break; - case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Throw); break; - } - break; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - case DFA_SCALE: - switch (byCom) - { - case COM_Left: - if (Action.Dir==DIR_Left) ObjectComMovement(this,COMD_Stop); - else { ObjectComMovement(this,COMD_Left); ObjectComLetGo(this,-1); } - break; - case COM_Right: - if (Action.Dir==DIR_Right) ObjectComMovement(this,COMD_Stop); - else { ObjectComMovement(this,COMD_Right); ObjectComLetGo(this,+1); } - break; - case COM_Up: ObjectComMovement(this,COMD_Up); break; - case COM_Down: ObjectComMovement(this,COMD_Down); break; - case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Drop); break; - } - break; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - case DFA_HANGLE: - switch (byCom) - { - case COM_Left: ObjectComMovement(this,COMD_Left); break; - case COM_Right: ObjectComMovement(this,COMD_Right); break; - case COM_Up: ObjectComMovement(this,COMD_Stop); break; - case COM_Down: ObjectComLetGo(this,0); break; - case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Drop); break; - } - break; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - case DFA_DIG: - switch (byCom) - { - case COM_Left: if (Inside(Action.ComDir,COMD_UpRight,COMD_Left)) Action.ComDir++; break; - case COM_Right: if (Inside(Action.ComDir,COMD_Right,COMD_UpLeft)) Action.ComDir--; break; - case COM_Down: ObjectComStop(this); break; - case COM_Dig_D: ObjectComDigDouble(this); break; - case COM_Dig_S: Action.Data = (!Action.Data); break; // Dig mat 2 object request - } - break; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - case DFA_SWIM: - switch (byCom) - { - case COM_Left: ObjectComMovement(this,COMD_Left); break; - case COM_Right: ObjectComMovement(this,COMD_Right); break; - case COM_Up: - ObjectComMovement(this,COMD_Up); - ObjectComUp(this); break; - case COM_Down: ObjectComMovement(this,COMD_Down); break; - case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Drop); break; - case COM_Dig_D: ObjectComDigDouble(this); break; - } - break; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - case DFA_BRIDGE: case DFA_BUILD: case DFA_CHOP: - switch (byCom) - { - case COM_Down: ObjectComStop(this); break; - } - break; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - case DFA_FIGHT: - switch (byCom) - { - case COM_Left: ObjectComMovement(this,COMD_Left); break; - case COM_Right: ObjectComMovement(this,COMD_Right); break; - case COM_Down: ObjectComStop(this); break; - } - break; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - case DFA_PUSH: - { - bool fGrabControlOverload = false; - if(Action.Target) - { - // New grab-control model: objects version 4.95 or higher (CE) - // may overload control of grabbing clonks - C4Def *pTDef = Action.Target->Def; - if(CompareVersion(pTDef->rC4XVer[0],pTDef->rC4XVer[1],pTDef->rC4XVer[2],pTDef->rC4XVer[3],4,9,5,0) >= 0) - fGrabControlOverload = true; - } - // Call object control first in case it overloads - if (fGrabControlOverload) - if (Action.Target) - if (Action.Target->CallControl(pController, byCom, &C4AulParSet(C4VObj(this)))) - return; - // Clonk direct control - switch (byCom) - { - case COM_Left: ObjectComMovement(this,COMD_Left); break; - case COM_Right: ObjectComMovement(this,COMD_Right); break; - case COM_Up: - // Target -> enter - if (ObjectComEnter(Action.Target)) - ObjectComMovement(this,COMD_Stop); - // Else, comdir up for target straightening - else - ObjectComMovement(this,COMD_Up); - break; - case COM_Down: ObjectComMovement(this,COMD_Stop); break; - case COM_Down_D: ObjectComUnGrab(this); break; - case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Throw); break; - } - // Action target call control late for old objects - if (!fGrabControlOverload) - if (Action.Target) - Action.Target->CallControl(pController, byCom, &C4AulParSet(C4VObj(this))); - break; - } - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - } - } - -void C4Object::AutoStopDirectCom(BYTE byCom, int32_t iData) // By DirecCom - { - C4Player * pPlayer = Game.Players.Get(Controller); - // Control by procedure - switch (GetProcedure()) - { - case DFA_WALK: - switch (byCom) - { - case COM_Up: ObjectComUp(this); break; - case COM_Down: - // inhibit controldownsingle on freshly grabbed objects - if (ObjectComDownDouble(this)) - pPlayer->LastCom = COM_None; - break; - case COM_Dig_S: ObjectComDig(this); break; - case COM_Dig_D: ObjectComDigDouble(this); break; - case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Throw); break; - default: AutoStopUpdateComDir(); - } - break; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - case DFA_FLIGHT: - switch (byCom) - { - case COM_Throw: - // Drop when pressing left, right or down - if (pPlayer->PressedComs & ((1<Def; - if(CompareVersion(pTDef->rC4XVer[0],pTDef->rC4XVer[1],pTDef->rC4XVer[2],pTDef->rC4XVer[3],4,9,5,0) >= 0) - fGrabControlOverload = true; - // Call object control first in case it overloads - if (fGrabControlOverload) - { - if (Action.Target->CallControl(pPlayer, byCom, &C4AulParSet(C4VObj(this)))) - { - return; - } - } - } - // Clonk direct control - switch (byCom) - { - case COM_Up: - // Target -> enter - if (ObjectComEnter(Action.Target)) - ObjectComMovement(this,COMD_Stop); - // Else, comdir up for target straightening - else - AutoStopUpdateComDir(); - break; - case COM_Down: - // FIXME: replace constants - // ComOrder(3) is COM_Down, ComOrder(11) is COM_Down_S and ComOrder(19) is COM_Down_D - if(Action.Target - && !DrawCommandQuery(Controller, Action.Target->Def->Script, Action.Target->Def->Script.ControlMethod, 3) - && !DrawCommandQuery(Controller, Action.Target->Def->Script, Action.Target->Def->Script.ControlMethod, 11) - && !DrawCommandQuery(Controller, Action.Target->Def->Script, Action.Target->Def->Script.ControlMethod, 19)) - { - ObjectComUnGrab(this); - } - break; - case COM_Down_D: ObjectComUnGrab(this); break; - case COM_Throw: PlayerObjectCommand(Owner,C4CMD_Drop); break; - default: - AutoStopUpdateComDir(); - } - // Action target call control late for old objects - if (!fGrabControlOverload && Action.Target) - Action.Target->CallControl(pPlayer, byCom, &C4AulParSet(C4VObj(this))); - break; - } - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - } - } - -void C4Object::AutoStopUpdateComDir() - { - C4Player * pPlr = Game.Players.Get(Controller); - if (!pPlr || pPlr->Cursor != this) return; - int32_t NewComDir = Coms2ComDir(pPlr->PressedComs); - if (Action.ComDir == NewComDir) return; - if (NewComDir == COMD_Stop && GetProcedure() == DFA_DIG) - { - ObjectComStop(this); - return; - } - ObjectComMovement(this, NewComDir); - } - BOOL C4Object::MenuCommand(const char *szCommand) { // Native script execution diff --git a/engine/src/C4ObjectCom.cpp b/engine/src/C4ObjectCom.cpp index ff913baa6..6fbbbd448 100644 --- a/engine/src/C4ObjectCom.cpp +++ b/engine/src/C4ObjectCom.cpp @@ -607,10 +607,10 @@ BOOL ObjectComPut(C4Object *cObj, C4Object *pTarget, C4Object *pThing) if (pTarget!=cObj->Contained) if (!(pTarget->Def->GrabPutGet & C4D_Grab_Put)) { - // Was meant to be a drop anyway - if (ValidPlr(cObj->Owner)) - if (Game.Players.Get(cObj->Owner)->LastComDownDouble) - return ObjectComDrop(cObj, pThing); + // Was meant to be a drop anyway - probably obsolete as controls are being revised + //if (ValidPlr(cObj->Owner)) + // if (Game.Players.Get(cObj->Owner)->LastComDownDouble) + // return ObjectComDrop(cObj, pThing); // No grab put: fail return FALSE; } @@ -1009,8 +1009,8 @@ BOOL PlayerObjectCommand(int32_t plr, int32_t cmdf, C4Object *pTarget, int32_t t if (cmdf==C4CMD_Throw) { bool fConvertToDrop = false; - // Drop on down-down-throw (classic) - if (pPlr->LastComDownDouble) + // Drop on down-down-throw (classic) - obsolete? + /*if (pPlr->LastComDownDouble) { fConvertToDrop = true; // Dropping one object automatically reenables LastComDownDouble to @@ -1021,7 +1021,7 @@ BOOL PlayerObjectCommand(int32_t plr, int32_t cmdf, C4Object *pTarget, int32_t t pPlr->LastComDownDouble = C4DoubleClick; } // Jump'n'Run: Drop on combined Down/Left/Right+Throw - if (pPlr->PrefControlStyle && (pPlr->PressedComs & (1 << COM_Down))) fConvertToDrop = true; + if (pPlr->PrefControlStyle && (pPlr->PressedComs & (1 << COM_Down))) fConvertToDrop = true;*/ if (fConvertToDrop) return pPlr->ObjectCommand(C4CMD_Drop,pTarget,tx,ty,NULL,0,iAddMode); } // Route to player diff --git a/engine/src/C4ObjectMenu.cpp b/engine/src/C4ObjectMenu.cpp index 2988bda09..888ef6454 100644 --- a/engine/src/C4ObjectMenu.cpp +++ b/engine/src/C4ObjectMenu.cpp @@ -434,20 +434,20 @@ void C4ObjectMenu::Execute() void C4ObjectMenu::OnUserSelectItem(int32_t Player, int32_t iIndex) { - // queue.... - Game.Input.Add(CID_PlrControl, new C4ControlPlayerControl(Player,COM_MenuSelect,iIndex | C4MN_AdjustPosition)); + // queue.... 2do + Game.Input.Add(CID_PlrControl, new C4ControlPlayerControl(Player,Game.PlayerControlDefs.InternalCons.CON_MenuSelect,iIndex | C4MN_AdjustPosition)); } void C4ObjectMenu::OnUserEnter(int32_t Player, int32_t iIndex, bool fRight) { - // object menu: Through queue - Game.Input.Add(CID_PlrControl, new C4ControlPlayerControl(Player,fRight ? COM_MenuEnterAll : COM_MenuEnter,iIndex)); + // object menu: Through queue 2do + Game.Input.Add(CID_PlrControl, new C4ControlPlayerControl(Player,fRight ? Game.PlayerControlDefs.InternalCons.CON_MenuEnterAll : Game.PlayerControlDefs.InternalCons.CON_MenuEnter,iIndex)); } void C4ObjectMenu::OnUserClose() { - // Queue - Game.Input.Add(CID_PlrControl, new C4ControlPlayerControl(Game.MouseControl.GetPlayer(),COM_MenuClose,0)); + // Queue 2do + Game.Input.Add(CID_PlrControl, new C4ControlPlayerControl(Game.MouseControl.GetPlayer(),Game.PlayerControlDefs.InternalCons.CON_MenuClose,0)); } bool C4ObjectMenu::IsReadOnly() diff --git a/engine/src/C4Packet2.cpp b/engine/src/C4Packet2.cpp index d92bf4863..2570c9299 100644 --- a/engine/src/C4Packet2.cpp +++ b/engine/src/C4Packet2.cpp @@ -113,7 +113,7 @@ const C4PktHandlingData PktHandlingData[] = { CID_JoinPlr, PC_Control, "Join Player", false, true, 0, PKT_UNPACK(C4ControlJoinPlayer) }, { CID_RemovePlr, PC_Control, "Remove Player", false, true, 0, PKT_UNPACK(C4ControlRemovePlr) }, { CID_PlrSelect, PC_Control, "Player Select", false, true, 0, PKT_UNPACK(C4ControlPlayerSelect)}, - { CID_PlrControl, PC_Control, "Player Control", false, true, 0, PKT_UNPACK(C4ControlPlayerControl)}, + { CID_PlrControl, PC_Control, "Player Control", false, true, 0, PKT_UNPACK(C4ControlPlayerControl)}, { CID_PlrCommand, PC_Control, "Player Command", false, true, 0, PKT_UNPACK(C4ControlPlayerCommand)}, { CID_Message, PC_Control, "Message", false, true, 0, PKT_UNPACK(C4ControlMessage) }, { CID_EMMoveObj, PC_Control, "EM Move Obj", false, true, 0, PKT_UNPACK(C4ControlEMMoveObject)}, diff --git a/engine/src/C4Player.cpp b/engine/src/C4Player.cpp index a1894b75c..c2b5555a4 100644 --- a/engine/src/C4Player.cpp +++ b/engine/src/C4Player.cpp @@ -1081,7 +1081,6 @@ void C4Player::Default() ControlCount = ActionCount = 0; LastControlType = PCID_None; LastControlID = 0; - PressedComs = 0; pMsgBoardQuery = NULL; pGamepad = NULL; NoEliminationCheck = false; @@ -1209,26 +1208,7 @@ BOOL C4Player::MakeCrewMember(C4Object *pObj, bool fForceInfo, bool fDoCalls) void C4Player::ExecuteControl() { - - // LastCom - if (LastCom != COM_None) - { - // Advance delay counter - LastComDelay++; - // Check for COM_Single com (after delay) - if (LastComDelay > C4DoubleClick) - { - // Pass additional COM_Single com (unless it already was a single com in the first place) - if (!(LastCom & COM_Single)) - DirectCom(LastCom | COM_Single, 0); // Currently, com data is not stored for single coms... - LastCom = COM_None; - LastComDelay = 0; - } - } - - // LastComDownDouble - if (LastComDownDouble>0) LastComDownDouble--; - + Control.Execute(); } void C4Player::AdjustCursorCommand() @@ -1375,30 +1355,6 @@ void C4Player::UpdateSelectionToggleStatus() CursorToggled=0; } -BOOL C4Player::ObjectCom(BYTE byCom, int32_t iData) // By DirectCom - { - if (Eliminated) return FALSE; -#ifdef DEBUGREC_OBJCOM - C4RCObjectCom rc = { byCom, iData, Number }; - AddDbgRec(RCT_PlrCom, &rc, sizeof(C4RCObjectCom)); -#endif - // Hide startup - ShowStartup=FALSE; - // If regular com, update cursor & selection status - if (!(byCom & COM_Single) && !(byCom & COM_Double) && (byCom < COM_ReleaseFirst || byCom > COM_ReleaseLast)) - UpdateSelectionToggleStatus(); - // Apply direct com to cursor object - if (Cursor) - { - // update controller - Cursor->Controller = Number; - // send com - Cursor->DirectCom(byCom,iData); - } - // Done - return TRUE; - } - BOOL C4Player::ObjectCommand(int32_t iCommand, C4Object *pTarget, int32_t iX, int32_t iY, C4Object *pTarget2, int32_t iData, int32_t iMode) { // Eliminated @@ -1455,71 +1411,6 @@ void C4Player::ObjectCommand2Obj(C4Object *cObj, int32_t iCommand, C4Object *pTa else if (iMode & C4P_Command_Set) cObj->SetCommand(iCommand,pTarget,iX,iY,pTarget2,TRUE,iData); } -void C4Player::DirectCom(BYTE byCom, int32_t iData) // By InCom or ExecuteControl - { - switch (byCom) - { - case COM_CursorLeft: CursorLeft(); break; - case COM_CursorRight: CursorRight(); break; - case COM_CursorToggle: CursorToggle(); break; - case COM_CursorToggle_D: SelectAllCrew(); break; - - default: ObjectCom(byCom,iData); break; - } - } - -void C4Player::InCom(BYTE byCom, int32_t iData) - { -#ifdef DEBUGREC_OBJCOM - C4RCObjectCom rc = { byCom, iData, Number }; - AddDbgRec(RCT_PlrInCom, &rc, sizeof(C4RCObjectCom)); -#endif - // Cursor object menu active: convert regular com to menu com - if (Cursor) if (Cursor->Menu) - { - int32_t iCom = byCom; - Cursor->Menu->ConvertCom(iCom,iData, false); - byCom = iCom; - } - // Menu control: no single/double processing - if (Inside(byCom,COM_MenuFirst,COM_MenuLast)) - { DirectCom(byCom,iData); return; } - // Ignore KeyRelease for Single/Double - if (!Inside(byCom, COM_ReleaseFirst, COM_ReleaseLast)) - { - // Reset view - ResetCursorView(); - // Update state - if (Inside(byCom, COM_ReleaseFirst - 16, COM_ReleaseLast - 16)) - PressedComs |= 1 << byCom; - // Check LastCom buffer for prior COM_Single - if (LastCom!=COM_None) - if (LastCom!=byCom) - { - DirectCom(LastCom | COM_Single,iData); - // AutoStopControl uses a single COM_Down instead of DOM_Down_D for drop - // So a COM_Down_S does what a COM_Down_D normally does, if generated by another key - // instead of a timeout - if (PrefControlStyle && LastCom == COM_Down) LastComDownDouble = C4DoubleClick; - } - // Check LastCom buffer for COM_Double - if (LastCom==byCom) byCom|=COM_Double; - // LastCom/Del process - // this is set before issuing the DirectCom, so DirectCom-scripts may delete it - LastCom=byCom; LastComDelay=0; - } - else - { - // Update state - if (Inside(byCom, COM_ReleaseFirst, COM_ReleaseLast)) - PressedComs &= ~(1 << (byCom - 16)); - } - // Pass regular/COM_Double byCom to player - DirectCom(byCom,iData); - // LastComDownDouble process - if (byCom == COM_Down_D) LastComDownDouble = C4DoubleClick; - } - void C4Player::CompileFunc(StdCompiler *pComp) { assert(ID); @@ -1534,7 +1425,7 @@ void C4Player::CompileFunc(StdCompiler *pComp) pComp->Value(mkNamingAdapt(Evaluated, "Evaluated", false)); pComp->Value(mkNamingAdapt(Color, "Color", -1)); pComp->Value(mkNamingAdapt(ColorDw, "ColorDw", 0u)); - pComp->Value(mkNamingAdapt(Control, "Control", 0)); + pComp->Value(mkNamingAdapt(ControlSet, "Control", 0)); pComp->Value(mkNamingAdapt(MouseControl, "MouseControl", 0)); pComp->Value(mkNamingAdapt(Position, "Position", 0)); pComp->Value(mkNamingAdapt(ViewMode, "ViewMode", C4PVM_Cursor)); @@ -1563,10 +1454,6 @@ void C4Player::CompileFunc(StdCompiler *pComp) pComp->Value(mkNamingAdapt((int32_t&)Cursor, "Cursor", 0)); pComp->Value(mkNamingAdapt((int32_t&)ViewCursor,"ViewCursor", 0)); pComp->Value(mkNamingAdapt((int32_t&)Captain, "Captain", 0)); - pComp->Value(mkNamingAdapt(LastCom, "LastCom", 0)); - pComp->Value(mkNamingAdapt(LastComDelay, "LastComDel", 0)); - pComp->Value(mkNamingAdapt(PressedComs, "PressedComs", 0)); - pComp->Value(mkNamingAdapt(LastComDownDouble, "LastComDownDouble", 0)); pComp->Value(mkNamingAdapt(CursorSelection, "CursorSelection", 0)); pComp->Value(mkNamingAdapt(CursorToggled, "CursorToggled", 0)); pComp->Value(mkNamingAdapt(MessageStatus, "MessageStatus", 0)); @@ -1672,7 +1559,7 @@ void C4Player::DefaultRuntimeData() AtClient=C4ClientIDUnknown; SCopy("Local",AtClientName); Color=-1; - Control=C4P_Control_None; + ControlSet=C4P_Control_None; MouseControl=FALSE; Position=-1; PlrStartIndex=0; @@ -1694,9 +1581,6 @@ void C4Player::DefaultRuntimeData() Cursor=ViewCursor=NULL; SelectCount=0; SelectFlash=CursorFlash=30; - LastCom=0; - LastComDelay=0; - LastComDownDouble=0; CursorSelection=CursorToggled=0; MessageStatus=0; MessageBuf[0]=0; @@ -1836,7 +1720,7 @@ void C4Player::InitControl() if (!GetInfo() || GetInfo()->GetType() == C4PT_User) LocalControl=TRUE; // Set control - Control=C4P_Control_None; + ControlSet=C4P_Control_None; // Preferred control int32_t iControl = PrefControl; // gamepad control safety @@ -1848,32 +1732,35 @@ void C4Player::InitControl() if (Game.Players.ControlTaken(iControl)) { // Preferred control taken, search for available keyboard control - for (iControl=C4P_Control_Keyboard1; iControl<=C4P_Control_Keyboard4; iControl++) + for (iControl=C4P_Control_Keyboard1; iControl<=C4P_Control_Keyboard2; iControl++) if (!Game.Players.ControlTaken(iControl)) // Available control found break; // No available control found - if (iControl>C4P_Control_Keyboard4) + if (iControl>C4P_Control_Keyboard2) iControl=C4P_Control_None; } // Set control - Control=iControl; + ControlSet=iControl; // init gamepad if (pGamepad) { delete pGamepad; pGamepad=NULL; } - if (Inside(Control, C4P_Control_GamePad1, C4P_Control_GamePadMax)) + if (Inside(ControlSet, C4P_Control_GamePad1, C4P_Control_GamePadMax)) { - pGamepad = new C4GamePadOpener(Control - C4P_Control_GamePad1); + pGamepad = new C4GamePadOpener(ControlSet - C4P_Control_GamePad1); } // Mouse if (PrefMouse && !Game.Control.isReplay()) if (!Game.C4S.Head.DisableMouse) - if (Inside(Control, C4P_Control_Keyboard1, C4P_Control_GamePadMax)) + if (Inside(ControlSet, C4P_Control_Keyboard1, C4P_Control_GamePadMax)) if (!Game.Players.MouseControlTaken()) MouseControl=TRUE; // no controls issued yet ControlCount = ActionCount = 0; LastControlType = PCID_None; LastControlID = 0; - PressedComs = 0; + // init control callbacks + StdStrBuf sKeysetName; + sKeysetName.Format("Keyboard%d%s", ControlSet, PrefControlStyle ? "" : "Classic"); + Control.RegisterKeyset(Number, Game.PlayerControlAssignmentSets.GetSetByName(sKeysetName.getData())); } int igOffX, igOffY; diff --git a/engine/src/C4PlayerControl.cpp b/engine/src/C4PlayerControl.cpp index 69c421392..f2eeae6a1 100644 --- a/engine/src/C4PlayerControl.cpp +++ b/engine/src/C4PlayerControl.cpp @@ -66,14 +66,24 @@ bool C4PlayerControlDef::operator ==(const C4PlayerControlDef &cmp) const /* C4PlayerControlDefs */ +void C4PlayerControlDefs::UpdateInternalCons() + { + InternalCons.CON_MenuSelect = GetControlIndexByIdentifier("MenuSelect"); + InternalCons.CON_MenuEnter = GetControlIndexByIdentifier("MenuEnter"); + InternalCons.CON_MenuEnterAll = GetControlIndexByIdentifier("MenuEnterAll"); + InternalCons.CON_MenuClose = GetControlIndexByIdentifier("MenuClose"); + } + void C4PlayerControlDefs::Clear() { Defs.clear(); + UpdateInternalCons(); } void C4PlayerControlDefs::CompileFunc(StdCompiler *pComp) { pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(Defs, StdCompiler::SEP_NONE), "ControlDefs", DefVecImpl())); + if (pComp->isCompiler()) UpdateInternalCons(); } void C4PlayerControlDefs::MergeFrom(const C4PlayerControlDefs &Src) @@ -94,9 +104,10 @@ void C4PlayerControlDefs::MergeFrom(const C4PlayerControlDefs &Src) Defs.push_back(SrcDef); } } + UpdateInternalCons(); } -C4PlayerControlDef *C4PlayerControlDefs::GetControlByIndex(int32_t idx) +const C4PlayerControlDef *C4PlayerControlDefs::GetControlByIndex(int32_t idx) const { // safe index if (idx<0 || idx>=Defs.size()) return NULL; @@ -201,6 +212,49 @@ bool C4PlayerControlAssignment::ResolveRefs(C4PlayerControlAssignmentSet *pParen return true; } +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; + } + bool C4PlayerControlAssignment::operator ==(const C4PlayerControlAssignment &cmp) const { // doesn't compare resolved TriggerKey/iControl @@ -267,6 +321,63 @@ bool C4PlayerControlAssignmentSet::operator ==(const C4PlayerControlAssignmentSe && sName == cmp.sName; } +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); + } + } + } + /* C4PlayerControlAssignmentSets */ @@ -355,6 +466,67 @@ void C4PlayerControlFile::Clear() /* C4PlayerControl */ +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 + if (iControl < 0 || iControl >= ControlDownStates.size()) return NULL; + return &ControlDownStates[iControl]; + } + +int32_t C4PlayerControl::CSync::GetControlDisabled(int32_t iControl) const + { + // safe access + if (iControl < 0 || iControl >= ControlDisableStates.size()) return 0; + return ControlDisableStates[iControl]; + } + +void C4PlayerControl::CSync::SetControlDownState(int32_t iControl, const C4KeyEventData &rDownState, int32_t iDownFrame, bool fDownByUser) + { + // update state + if (iControl < 0) return; + if (iControl >= ControlDownStates.size()) ControlDownStates.resize(iControl+1); + 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; + if (iControl >= ControlDisableStates.size()) ControlDisableStates.resize(iControl+1); + 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(); + } + void C4PlayerControl::CSync::CompileFunc(StdCompiler *pComp) { pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(ControlDownStates), "Down", DownStateVec())); @@ -376,20 +548,22 @@ void C4PlayerControl::CompileFunc(StdCompiler *pComp) bool C4PlayerControl::ProcessKeyEvent(const C4KeyCodeEx &key, bool fUp, const C4KeyEventData &rKeyExtraData) { // collect all matching keys - C4PlayerControlAssignmentVec Matches; - pControlSet->GetAssignmentsByKey(key, fUp, &Matches, DownKeys, RecentKeys); + C4PlayerControlAssignmentPVec Matches; + assert(pControlSet); // shouldn't get this callback for players without control set + pControlSet->GetAssignmentsByKey(ControlDefs, key, fUp, &Matches, DownKeys, RecentKeys); // process async controls - C4ControlPlayerControl2 *pControlPacket = NULL; - for (C4PlayerControlAssignmentVec::const_iterator i = Matches.begin(); i != Matches.end(); ++i) + C4ControlPlayerControl *pControlPacket = NULL; + for (C4PlayerControlAssignmentPVec::const_iterator i = Matches.begin(); i != Matches.end(); ++i) { - const C4PlayerControlAssignment &rAssignment = *i; - int32_t iControlIndex = rAssignment.GetControl(); - C4PlayerControlDef *pControlDef = ControlDefs.GetControlByIndex(iControlIndex); + const C4PlayerControlAssignment *pAssignment = *i; + assert(pAssignment); + int32_t iControlIndex = pAssignment->GetControl(); + const 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())) + if (ExecuteControl(iControlIndex, fUp, rKeyExtraData, pAssignment->GetTriggerMode(), key.IsRepeated())) return true; } else @@ -399,24 +573,31 @@ bool C4PlayerControl::ProcessKeyEvent(const C4KeyCodeEx &key, bool fUp, const C4 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()); + if (!pControlPacket) pControlPacket = new C4ControlPlayerControl(iPlr, fUp, rKeyExtraData); + pControlPacket->AddControl(iControlIndex, pAssignment->GetTriggerMode()); break; } } } // push sync control to input - if (pControlPacket) Game.Input.Add(CID_PlrControl2, pControlPacket); + if (pControlPacket) + { + Game.Input.Add(CID_PlrControl2, pControlPacket); + // assume processed (although we can't really know that yet) + return true; + } + return false; } 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)); + C4PlayerControlRecentKey RKey(key,timeGetTime()); + if (std::find(DownKeys.begin(), DownKeys.end(), C4PlayerControlRecentKey(key,0)) == DownKeys.end()) DownKeys.push_back(RKey); // 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)); + if (!key.IsRepeated()) RecentKeys.push_back(RKey); return fResult; } @@ -429,14 +610,14 @@ bool C4PlayerControl::ProcessKeyUp(const C4KeyCodeEx &key) return ProcessKeyEvent(key, true, Game.KeyboardInput.GetLastKeyExtraData()); } -void C4PlayerControl::ExecuteControlPacket(const class C4ControlPlayerControl2 *pCtrl) +void C4PlayerControl::ExecuteControlPacket(const class C4ControlPlayerControl *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) + for (C4ControlPlayerControl::ControlItemVec::const_iterator i = pCtrl->GetControlItems().begin(); i != pCtrl->GetControlItems().end(); ++i) { - const C4ControlPlayerControl2::ControlItem &rItem = *i; - C4PlayerControlDef *pCtrlDef = ControlDefs.GetControlByIndex(rItem.iControl); + const C4ControlPlayerControl::ControlItem &rItem = *i; + const C4PlayerControlDef *pCtrlDef = ControlDefs.GetControlByIndex(rItem.iControl); if (pCtrlDef) { if (ExecuteControl(rItem.iControl, pCtrl->IsReleaseControl(), pCtrl->GetExtraData(), rItem.iTriggerMode, false)) @@ -449,10 +630,12 @@ void C4PlayerControl::ExecuteControlPacket(const class C4ControlPlayerControl2 * 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); + const C4PlayerControlDef *pControlDef = ControlDefs.GetControlByIndex(iControl); if (!pControlDef || Sync.IsControlDisabled(iControl)) return false; C4PlayerControlDef::Actions eAction = pControlDef->GetAction(); C4KeyEventData KeyExtraData(rKeyExtraData); + const CSync::ControlDownState *pCtrlDownState = Sync.GetControlDownState(iControl); + bool fWasDown = pCtrlDownState ? pCtrlDownState->IsDown() : false; // global controls only in global context if (IsGlobal() != pControlDef->IsGlobal()) return false; // hold-actions only work on script controls with the hold flag @@ -462,9 +645,6 @@ bool C4PlayerControl::ExecuteControl(int32_t iControl, bool fUp, const C4KeyEven 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? @@ -479,6 +659,7 @@ bool C4PlayerControl::ExecuteControl(int32_t iControl, bool fUp, const C4KeyEven else //if (iTriggerMode & C4PlayerControlAssignment::CTM_Hold) - must be true { // control is down but trigger key is pressed again: Refresh down state + // (this will restart the KeyRepeat time) Sync.SetControlDownState(iControl, KeyExtraData, Game.FrameCounter, false); // now process as a regular, repeated "down" event fRepeated = true; @@ -503,10 +684,14 @@ bool C4PlayerControl::ExecuteControl(int32_t iControl, bool fUp, const C4KeyEven 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; + 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; } // perform action for this control bool fHandled = ExecuteControlAction(iControl, eAction, pControlDef->GetExtraData(), fUp, KeyExtraData, fRepeated); @@ -546,6 +731,19 @@ bool C4PlayerControl::ExecuteControlAction(int32_t iControl, C4PlayerControlDef: bool C4PlayerControl::ExecuteControlScript(int32_t iControl, C4ID idControlExtraData, bool fUp, const C4KeyEventData &rKeyExtraData, bool fRepeated) { + C4Player *pPlr = Game.Players.Get(iPlr); + 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; + } if (!fUp) { // control down @@ -567,7 +765,39 @@ bool C4PlayerControl::ExecuteControlScript(int32_t iControl, C4ID idControlExtra void C4PlayerControl::Execute() { - // sync execution: Do keyrepeat, etc. + // sync execution: Do keyrepeat + for (int32_t i=0; iIsDown()) + { + 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); } C4PlayerControl::C4PlayerControl() : ControlDefs(Game.PlayerControlDefs), iPlr(-1), pControlSet(NULL) @@ -576,10 +806,37 @@ C4PlayerControl::C4PlayerControl() : ControlDefs(Game.PlayerControlDefs), iPlr(- void C4PlayerControl::Clear() { + 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(); } void C4PlayerControl::RegisterKeyset(int32_t iPlr, C4PlayerControlAssignmentSet *pKeyset) { + // clear any previous settings + Clear(); + // setup + pControlSet = pKeyset; + this->iPlr = iPlr; // register all keys into Game.KeyboardInput creating KeyBindings + 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++); + } } +void C4PlayerControl::AddKeyBinding(const C4KeyCodeEx &key, bool fHoldKey, int32_t idx) + { + KeyBindings.push_back(new C4CustomKey( + key, FormatString("PlrKey%02d", idx).getData(), KEYSCOPE_Control, + new C4KeyCBPassKey(*this, &C4PlayerControl::ProcessKeyDown, fHoldKey ? &C4PlayerControl::ProcessKeyUp : NULL), + C4CustomKey::PRIO_PlrControl)); + } diff --git a/engine/src/C4PlayerList.cpp b/engine/src/C4PlayerList.cpp index f541a0117..78b9a1010 100644 --- a/engine/src/C4PlayerList.cpp +++ b/engine/src/C4PlayerList.cpp @@ -141,7 +141,7 @@ int C4PlayerList::CheckColorDw(DWORD dwColor, C4Player *pExclude) BOOL C4PlayerList::ControlTaken(int iControl) const { for (C4Player *pPlr=First; pPlr; pPlr=pPlr->Next) - if (pPlr->Control==iControl) + if (pPlr->ControlSet==iControl) if (pPlr->LocalControl) return TRUE; return FALSE; @@ -185,7 +185,7 @@ C4Player* C4PlayerList::GetLocalByKbdSet(int iKbdSet) const { for (C4Player *pPlr=First; pPlr; pPlr=pPlr->Next) if (pPlr->LocalControl) - if (pPlr->Control==iKbdSet) + if (pPlr->ControlSet==iKbdSet) return pPlr; return NULL; } @@ -645,7 +645,7 @@ void C4PlayerList::DenumeratePointers() int C4PlayerList::ControlTakenBy(int iControl) const { for (C4Player *pPlr=First; pPlr; pPlr=pPlr->Next) - if (pPlr->Control==iControl) + if (pPlr->ControlSet==iControl) if (pPlr->LocalControl) return pPlr->Number; return NO_OWNER; diff --git a/engine/src/C4Script.cpp b/engine/src/C4Script.cpp index 961ca091b..f84f2fbaf 100644 --- a/engine/src/C4Script.cpp +++ b/engine/src/C4Script.cpp @@ -2692,24 +2692,6 @@ static bool FnDoHomebaseProduction(C4AulContext *cthr, long iPlr, C4ID id, long return Game.Players.Get(iPlr)->HomeBaseProduction.SetIDCount(id,iLastcount+iChange,TRUE); } -static long FnGetPlrDownDouble(C4AulContext *cthr, long iPlr) - { - if (!ValidPlr(iPlr)) return FALSE; - return Game.Players.Get(iPlr)->LastComDownDouble; - } - -static bool FnClearLastPlrCom(C4AulContext *cthr, long iPlr) - { - // get player - C4Player *pPlr = Game.Players.Get(iPlr); - if (!pPlr) return FALSE; - // reset last coms - pPlr->LastCom = COM_None; - pPlr->LastComDownDouble = 0; - // done, success - return TRUE; - } - static bool FnSetPlrKnowledge(C4AulContext *cthr, long iPlr, C4ID id, bool fRemove) { C4Player *pPlr=Game.Players.Get(iPlr); @@ -6586,8 +6568,6 @@ void InitFunctionMap(C4AulScriptEngine *pEngine) AddFunc(pEngine, "SetPlrView", FnSetPlrView); AddFunc(pEngine, "SetPlrKnowledge", FnSetPlrKnowledge); AddFunc(pEngine, "SetPlrMagic", FnSetPlrMagic); - AddFunc(pEngine, "GetPlrDownDouble", FnGetPlrDownDouble); - AddFunc(pEngine, "ClearLastPlrCom", FnClearLastPlrCom); AddFunc(pEngine, "GetPlrViewMode", FnGetPlrViewMode); AddFunc(pEngine, "GetPlrView", FnGetPlrView); AddFunc(pEngine, "GetWealth", FnGetWealth); diff --git a/engine/src/C4StartupOptionsDlg.cpp b/engine/src/C4StartupOptionsDlg.cpp index da9401bab..ac45d65c2 100644 --- a/engine/src/C4StartupOptionsDlg.cpp +++ b/engine/src/C4StartupOptionsDlg.cpp @@ -170,7 +170,7 @@ C4StartupOptionsDlg::KeySelDialog::~KeySelDialog() delete pKeyListener; } -bool C4StartupOptionsDlg::KeySelDialog::KeyDown(C4KeyCodeEx key) +bool C4StartupOptionsDlg::KeySelDialog::KeyDown(const C4KeyCodeEx &key) { // check if key is valid for this set // do not mix gamepad and keyboard keys diff --git a/engine/src/C4Viewport.cpp b/engine/src/C4Viewport.cpp index 06fc221aa..26b2322de 100644 --- a/engine/src/C4Viewport.cpp +++ b/engine/src/C4Viewport.cpp @@ -1407,10 +1407,10 @@ StdStrBuf PlrControlKeyName(int32_t iPlayer, int32_t iControl, bool fShort) // player control if (pPlr) { - if (Inside(pPlr->Control,C4P_Control_Keyboard1,C4P_Control_Keyboard4)) - return C4KeyCodeEx::KeyCode2String(Config.Controls.Keyboard[pPlr->Control][iControl], true, fShort); - if (Inside(pPlr->Control,C4P_Control_GamePad1,C4P_Control_GamePadMax)) - return C4KeyCodeEx::KeyCode2String(Config.Gamepads[pPlr->Control-C4P_Control_GamePad1].Button[iControl], true, fShort); + if (Inside(pPlr->ControlSet,C4P_Control_Keyboard1,C4P_Control_Keyboard4)) + return C4KeyCodeEx::KeyCode2String(Config.Controls.Keyboard[pPlr->ControlSet][iControl], true, fShort); + if (Inside(pPlr->ControlSet,C4P_Control_GamePad1,C4P_Control_GamePadMax)) + return C4KeyCodeEx::KeyCode2String(Config.Gamepads[pPlr->ControlSet-C4P_Control_GamePad1].Button[iControl], true, fShort); } // global control else @@ -1461,24 +1461,8 @@ void C4Viewport::DrawPlayerControls(C4TargetFacet &cgo) ty = cgo.Y+15; break; } - int32_t iShowCtrl = Game.Players.Get(Player)->ShowControl; - int32_t iLastCtrl = Com2Control(Game.Players.Get(Player)->LastCom); - int32_t scwdt=size/3,schgt=size/4; - BOOL showtext; - const int32_t C4MaxShowControl = 10; - - for (int32_t iCtrl=0; iCtrl18) showtext=FALSE; - C4Facet ccgo; - ccgo.Set(cgo.Surface,tx+scwdt*(iCtrl%3),ty+schgt*(iCtrl/3),scwdt,schgt); - DrawControlKey(ccgo,iCtrl,(iLastCtrl==iCtrl) ? 1 : 0, - showtext ? PlrControlKeyName(Player,iCtrl,true).getData() : NULL); - } + // TODO } extern int32_t DrawMessageOffset; @@ -1496,20 +1480,20 @@ void C4Viewport::DrawPlayerStartup(C4TargetFacet &cgo) cgo.X+(cgo.Wdt-GfxR->fctKeyboard.Wdt)/2+55, cgo.Y+cgo.Hgt * 2/3 - 10 + DrawMessageOffset, 0,0); - if (Inside(pPlr->Control,C4P_Control_Keyboard1,C4P_Control_Keyboard4)) + if (Inside(pPlr->ControlSet,C4P_Control_Keyboard1,C4P_Control_Keyboard4)) { GfxR->fctKeyboard.Draw(cgo.Surface, cgo.X+(cgo.Wdt-GfxR->fctKeyboard.Wdt)/2, cgo.Y+cgo.Hgt * 2/3 + DrawMessageOffset, - pPlr->Control-C4P_Control_Keyboard1,0); + pPlr->ControlSet-C4P_Control_Keyboard1,0); iNameHgtOff=GfxR->fctKeyboard.Hgt; } - else if (Inside(pPlr->Control,C4P_Control_GamePad1,C4P_Control_GamePad4)) + else if (Inside(pPlr->ControlSet,C4P_Control_GamePad1,C4P_Control_GamePad4)) { GfxR->fctGamepad.Draw(cgo.Surface, cgo.X+(cgo.Wdt-GfxR->fctKeyboard.Wdt)/2, cgo.Y+cgo.Hgt * 2/3 + DrawMessageOffset, - pPlr->Control-C4P_Control_GamePad1,0); + pPlr->ControlSet-C4P_Control_GamePad1,0); iNameHgtOff=GfxR->fctGamepad.Hgt; } From 22d689d56005338e9acb7cef60aa1ffaf4ff807b Mon Sep 17 00:00:00 2001 From: Sven Eberhardt Date: Tue, 9 Jun 2009 00:29:55 -0400 Subject: [PATCH 05/12] Moving controls to script: Docs --- docs/sdk/playercontrols.xml | 265 ++++++++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 docs/sdk/playercontrols.xml diff --git a/docs/sdk/playercontrols.xml b/docs/sdk/playercontrols.xml new file mode 100644 index 000000000..6ffdd37f9 --- /dev/null +++ b/docs/sdk/playercontrols.xml @@ -0,0 +1,265 @@ + + + + +Spielersteuerung +Spielersteuerung + +Ab OC erlaubt die Engine, Steuerungskommandos fuer Spieler komplett frei zu definieren. Eigene Tastaturkommandos koennen hinzugefuegt oder veraendert werden. Alle unterstuetzten Eingabegeraete wie Maus, Tastatur und Gamepads koennen frei belegt werden und Kommandos koennen aus beliebigen Tastenkombinationen oder Sequenzen bestehen. +PlayerControls.txt +Alle Steuerungsbefehle, die ein Spieler an das Spiel geben kann, werden in der Datei PlayerControls.txt definiert. Die Standardtasten sowie deren Standardbelegung fuer verschiedene Eingabegeraete befinden sich in der globalen Definitionsdatei in der System.c4g. Objektdefinitionen und Szenarien koennen weitere Tasten in ihrer lokalen System.c4g hinzufuegen oder die Parameter vorhandener Steuerkommandos ueberladen**. +Zusaetzliche Dateien PlayerControls.txt koennen in Sprachpaketen abgelegt werden, um die Standardbelegungen der Tasten bei verschiedenen, geladenen Sprachen an die Tastaturen des jeweiligen Landes anzupassen**. + + +Sektion [ControlDefs] +Definition der moeglichen Spielerkommandos. Nicht gueltig in Sprachpaketen. Dieser Sektion untergeordnet: + + + + Wert + Datentyp + Beschreibung + + + Identifier + Zeichenfolge (Max. 96 Zeichen) + Intern benutzter Name zur Identifikation des Kommandos. Das Kommando wird unter diesem Namen in Standardbelegungen referenziert und im Script als CON_Name-Konstante vordefiniert. Der Name sollte folglich im Script gueltig sein, d.h. nur aus Buchstaben, Zahlen sowie _ bestehen. Insbesondere sollten keine Leerzeichen oder deutsche Umlaute verwendet werden. Zur Vermeidung von Konflikten gelten in szenarienlokalen sowie Objektpaketlokalen Definitionen dieselben Benennungsregeln wie zur Vergabe von Objekt-IDs. + + + GUIName + Zeichenfolge + Name, der dem Spieler im Steuerungskonfigurationsdialog sowie in den Steuerungstooltips angezeigt wird. Lokalisierte Zeichenketten koennen aus dem zugehoerigen StringTable refeenziert werden ($Name$). + + + GUIDesc + Zeichenfolge + Erlaeuternde Beschreibung, die dem Spieler im Steuerungskonfigurationsdialog angezeigt wird. Lokalisierte Zeichenketten koennen aus dem zugehoerigen StringTable refeenziert werden ($Name$). + + + Global + Boolean + Wenn wahr, ist dies eine globale, d.h. keinem Spieler zugeordnete Definition. Siehe Globale Definitionen. + + + Hold + Boolean + Wenn wahr, wird das Kommando als ein gehaltenes Kommando interpretiert. Ein solches Kommando speichert, ob die Steuerungstaste gedrueckt ist und generiert beim Loslassen ein zusaetzliches Scriptereignis. Siehe Gehaltene Tasten. + + + RepeatDelay + Integer + Nur gueltig wenn Hold wahr. Wenn groesser 0, generiert die Taste im gehaltenen Zustand im angegebenen Abstand (in Frames) weitere Scriptereignisse. Siehe Tastenwiederholungen. + + + InitialRepeatDelay + Integer + Wenn angegeben, kann die Wartezeit fuer das erste Tastenwiederholungsereignis geaendert werden. Siehe Tastenwiederholungen. + + + DefaultDisabled + Boolean + Wenn wahr, ist das Kommando im Normalfall deaktiviert und muss erst per Script aktiviert werden. Nuetzlich fuer Kommandos, die nur in sehr speziellen Situationen benoetigt werden. Siehe Deaktivierte Kommandos. + + + ExtraData + C4ID + Optionale ID, die an die Scriptfunktion uebergeben wird. Siehe ExtraData. + + + Action + Zeichenfolge + Auszufuehrende Aktion bei diesem Kommando. Moegliche Werte: +
Beliebig viele Sektionen [ControlDef]
+ + Wert + Beschreibung + + + None + Keine Aktion. + + + Script (Standardwert) + Ausfuehrung des Scriptbefehls PlayerControl bzw. PlayerControlReleased. Siehe Script-Callbacks. + + + Menu + Oeffnen des Spielermenues (asynchrones Kommando). + + + MenuOK + Bestaetigen des ausgewaehlten Elementes im Spielermenue (asynchrones Kommando). + + + MenuCancel + Schliessen des Spielermenues (asynchrones Kommando). + + + MenuLeft / MenuUp / MenuRight / MenuDown + Navigation im Spielermenu (asynchrones Kommando). +
+ +
+ +Sektion [ControlSets] +Definition von Standard-Steuerungsbelegungen. + + + + Wert + Datentyp + Beschreibung + + + Name + Zeichenfolge + Interner Name zur Identifikation gleicher Steuerungsbelegungen. Die Namen der Standardbelegungen sind Keyboard1, Keyboard1Classic, Keyboard2, Keyboard2Classic, Gamepad +
Beliebig viele Sektionen [ControlSet]
+ + + + + Wert + Datentyp + Beschreibung + + + Key + Zeichenfolge + Taste(n) dieser Belegung oder Referenz auf eine andere Belegung. Siehe Tastenbelegungen. + + + Control + Zeichenfolge + Kommando, das mit dieser Belegung verknuepft wird. Der Name sollte dem Identifier eines in einer [ControlDef] definierten Kommandos entsprechen. + + + Priority + Integer + Prioritaet der Belegung. Nutzen mehrere Belegungen die gleichen Tasten, so wird zunaechst die Taste mit der hoeheren Prioritaet ausgefuehrt, bis ein Kommando als behandelt gilt. + + + TriggerMode + Bitmaske + Ausloesmodus dieser Belegung. Bitmaske aus folgenden Werten: +
Beliebig viele Sektionen [Assignment]
+ + Wert + Beschreibung + + + Default (Standardwert) + Keine besondere Aktion. + + + Hold + Die Taste versetzt das verlinkte Kommando in den gedrueckten Zustand, selbst wenn die Taste selbst nur angeschlagen wird. Nur gueltig, wenn das Kommando das Hold-Attribut gesetzt hat. Dieser Zustand bleibt erhalten, bis eine entsprechende Belegung mit Ausloesemodus Release gedrueckt wird. Siehe Gehaltene Tasten. + + + Release + Die Taste entfernt den gedrueckten Zustand. Eine Taste kann auch sowohl Hold als auch Release setzen, um zwischen den Zustaenden hin und her zu schalten. Siehe Gehaltene Tasten. + + + AlwaysUnhandled + Der Tastendruck wird immer an die Belegung mit der naechstniedrigen Prioritaet weitergereicht, unabhaengig davon, ob das vorherige Kommando erfolgreich ausgefuehrt wurde. + + + ToggleUnhandled + Der Tastendruck wird genau dann an die Belegung mit der naechstniedrigen Prioritaet weitergereicht, wenn das vorherige Kommando erfolgreich ausgefuehrt wurde. Hiermit lassen sich Makros definieren. ** +
+ +
+ +
+ +
+Script-Callbacks +Die meisten Kommandos (abgesehen von asyrnchronen Kommandos im Spielermenue), rufen eine globale Scriptfunktion auf: +global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated) +Fuer eine Erlaeuterung der Parameter siehe PlayerControl. Die Funktion erhaelt unter anderem den aufrufenden Spieler in iPlr, sowie das ausgefuehrte Kommando in iControl. +Fuer ein einfaches Beispiel sei in der globalen PlayerControls.txt folgendes Kommando definiert: +[ControlDefs] + [ControlDef] + Identifier=Jump + GUIName=Jump + GUIDesc=Hoppin' around + Repeat=5 + +[ControlSets] + + [ControlSet] + Name=Keyboard1 + + [Assignment] + Key=W + Control=Jump + Priority=50 + +Dies definiert eine Sprungtaste und die zugehoerige Standardbelegung auf der Tastatur fuer den ersten Spieler. Dazu folgendes Script zur Behandlung: +global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated) +{ + // Welches Kommando wurde ausgeloest? + // Die Konstante CON_Jump wurde automatisch durch die Definition in PlayerControls.txt angelegt + if (control == CON_Jump) + { + // Sprungtaste gedrueckt. Der vom Spieler ausgewaehlte Clonk soll springen + var player_clonk = GetCursor(player); + if (player_clonk && player_clonk->Jump()) + { + // Das Kommando wurde erfolgreich abgearbeitet + return true; + } + } + // Unbekanntes Kommando + return false; +} + + +ExtraData +Da nicht jede Objektdefinition die globale PlayerControl-Funktion ueberladen kann, gibt es das ExtraData-Feld zum Verteilen von Kommandos. Zum Beispiel fuer folgende Definition: +[ControlDefs] + [ControlDef] + Identifier=Dig + GUIName=Dig + GUIDesc=Going underground + ExtraData=SHVL +Dabei sei SHVL die ID eines Schaufelobjektes. Im globalen Script kann zum Beispiel folgende, allgemeine Behandlung fuer unbekannte Kommandos stehen: +global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated) +{ + // Behandlung bekannter Befehle + // [...] + // Befehl mit eigener Behandlung + if (control_extra) return control_extra->PlayerControl(player, control, x, y, strength, repeat); + // Unbekanntes Kommando + return false; +} + +Und im Script der Schaufel: +func PlayerControl(int player, int control, int x, int y, int strength, bool repeated) +{ + // Behandlung bekannter Befehle + // Grabkommando direkt an die Schaufel + if (control == CON_Dig) + { + // Nur, wenn ein Clonk ausgewaehlt ist, der graben kann + var player_clonk = GetCursor(player); + if (player_clonk && player_clonk->HasShovel()) + { + return player_clonk->StartDig(); + } + } + // Unbekanntes Kommando + return false; +} + + +Gehaltene Tasten +Globale Definitionen +Tastenwiederholungen +Deaktivierte Kommandos +Tastenbelegungen +Prioritaeten +
+** - noch nicht implementiert +Sven2Juni 2009 +
From f6fadb300db00e4e47ee3c6cc83f664142d78b55 Mon Sep 17 00:00:00 2001 From: Sven Eberhardt Date: Tue, 9 Jun 2009 19:29:16 -0400 Subject: [PATCH 06/12] Moving controls to script... --- docs/sdk/playercontrols.xml | 90 ++++++++++++++++++++++++---- engine/inc/C4PlayerControl.h | 6 +- engine/inc/C4Script.h | 4 +- engine/src/C4Game.cpp | 3 + engine/src/C4PlayerControl.cpp | 21 ++++++- planet/System.c4g/PlayerControl.c | 13 ++++ planet/System.c4g/PlayerControls.txt | 60 +++++++++++++++++++ 7 files changed, 179 insertions(+), 18 deletions(-) create mode 100644 planet/System.c4g/PlayerControl.c create mode 100644 planet/System.c4g/PlayerControls.txt diff --git a/docs/sdk/playercontrols.xml b/docs/sdk/playercontrols.xml index 6ffdd37f9..58b06fa1a 100644 --- a/docs/sdk/playercontrols.xml +++ b/docs/sdk/playercontrols.xml @@ -80,7 +80,7 @@ Script (Standardwert) - Ausfuehrung des Scriptbefehls PlayerControl bzw. PlayerControlReleased. Siehe Script-Callbacks. + Ausfuehrung des Scriptbefehls PlayerControl bzw. PlayerControlRelease. Siehe Script-Callbacks. Menu @@ -113,7 +113,7 @@ Name Zeichenfolge - Interner Name zur Identifikation gleicher Steuerungsbelegungen. Die Namen der Standardbelegungen sind Keyboard1, Keyboard1Classic, Keyboard2, Keyboard2Classic, Gamepad + Interner Name zur Identifikation gleicher Steuerungsbelegungen. Die Namen der Standardbelegungen sind Keyboard1, Keyboard1Classic, Keyboard2, Keyboard2Classic, Gamepad. Ueber Platzhalter (*) koennen Tasten direkt in mehreren Belegungen definiert werden**. @@ -128,6 +128,11 @@ ZeichenfolgeTaste(n) dieser Belegung oder Referenz auf eine andere Belegung. Siehe Tastenbelegungen. + + ComboIsSequence + Boolean + Wenn wahr, werden mehrfache Tasten als Sequenz interpretiert. Das heisst, sie muessen nacheinander statt gleichzeitig gedrueckt werden. Siehe Tastenbelegungen. + ControlZeichenfolge @@ -193,8 +198,7 @@ [Assignment] Key=W Control=Jump - Priority=50 - + Priority=50Dies definiert eine Sprungtaste und die zugehoerige Standardbelegung auf der Tastatur fuer den ersten Spieler. Dazu folgendes Script zur Behandlung:global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated) { @@ -212,8 +216,7 @@ } // Unbekanntes Kommando return false; -} - +}ExtraDataDa nicht jede Objektdefinition die globale PlayerControl-Funktion ueberladen kann, gibt es das ExtraData-Feld zum Verteilen von Kommandos. Zum Beispiel fuer folgende Definition: @@ -232,8 +235,7 @@ if (control_extra) return control_extra->PlayerControl(player, control, x, y, strength, repeat); // Unbekanntes Kommando return false; -} - +}Und im Script der Schaufel:func PlayerControl(int player, int control, int x, int y, int strength, bool repeated) { @@ -250,15 +252,81 @@ } // Unbekanntes Kommando return false; -} - +}Gehaltene Tasten +Wird fuer ein Kommando das Hold-Flag gesetzt, so speichert die Engine den gegenwaertigen Tastenzustand fuer diese Taste. Solche Tasten haben einige Besonderheiten: +
    +
  • Sie generieren beim Loslassen PlayerControlRelease-Aufrufe im Script.
  • +
  • Belegungen koennen mit den Hold/Release-Flags dauerhafte Tastendruecke emulieren.
  • +
  • Tastenwiederholungen werden erzeugt.
  • +
  • Der Haltezustand der Taste kann mit GetPlayerControlState im Script abgefragt werden.
  • +
+Bestes Beispiel hierfuer ist ein Richtungskommando: + [ControlDef] + Identifier=Left + GUIName=Left + GUIDesc=Walk left + Hold=1 +Im Script wird die Richtung dann auf den Clonk uebertragen: +global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated) +{ + if (control == CON_Left) return UpdateControlDir(player); + // ... +} + +global func PlayerControlRelease(int player, int control, C4ID control_extra, int x, int y) +{ + if (control == CON_Left) return UpdateControlDir(player); + // ... +} + +global func UpdateControlDir(int player) +{ + // Clonk ausgewaehlt? + var player_clonk = GetCursor(player); + if (player_clonk) + { + // Clonkrichtung aktualisieren + var new_comdir = COMD_Stop; + if (GetPlayerControlState(player, CON_Left)) new_comdir = COMD_Left; + player_clonk->SetComDir(new_comdir); + // Kommando behandelt + return true; + } + // Kommando behandelt + return false; +} +Um klassisches Steuerungsverhalten zu erreichen, kann eine Tastenbelegung den Hold-Zustand emulieren: + [Assignment] + Key=A + Control=Left + TriggerMode=Hold + + [Assignment] + Key=S + Control=Left + TriggerMode=Release | AlwaysUnhandled + Globale Definitionen +...Tastenwiederholungen -Deaktivierte Kommandos +Hat ein Kommando ein RepeatDelay definiert, so werden wiederholte Kommandos beim Halten der Taste erzeugt. Zum Beispiel fuer ein Wurkommando: + [ControlDef] + Identifier=Throw + GUIName=Throw + GUIDesc=Get rid of your selected inventory + Hold=1 + RepeatDelay=5 + InitialRepeatDelay=35 +Im Beispiel koennte man die Wurftaste nach einmal Druecken auch halten. Das Wurfkommando wuerde dann nach 35 Frames (ca. eine Sekunde) halten alle 5 Frames automatisch wiederholt. +Wiederholungen werden nur erzeugt, wenn das Kommando ebenfalls das Hold-Flag gesetzt hat. +Deaktivierte Kommandos** +...Tastenbelegungen +...Prioritaeten +...** - noch nicht implementiertSven2Juni 2009 diff --git a/engine/inc/C4PlayerControl.h b/engine/inc/C4PlayerControl.h index be7e712f0..7daf70cb0 100644 --- a/engine/inc/C4PlayerControl.h +++ b/engine/inc/C4PlayerControl.h @@ -99,6 +99,8 @@ class C4PlayerControlDefs void CompileFunc(StdCompiler *pComp); void MergeFrom(const C4PlayerControlDefs &Src); // copy all defs from source file; overwrite defs of same name if found + void FinalInit(); // after all defs have been loaded: register script constants + const C4PlayerControlDef *GetControlByIndex(int32_t idx) const; int32_t GetControlIndexByIdentifier(const char *szIdentifier) const; // return CON_None for not found int32_t GetCount() const { return Defs.size(); } @@ -167,7 +169,7 @@ class C4PlayerControlAssignment bool IsComboMatched(const C4PlayerControlRecentKeyList &DownKeys, const C4PlayerControlRecentKeyList &RecentKeys) const; // check if combo is currently fulfilled (assuming TriggerKey is already matched) bool operator ==(const C4PlayerControlAssignment &cmp) const; // doesn't compare resolved TriggerKey/iControl - bool operator <(const C4PlayerControlAssignment &cmp) const { return iPriority < cmp.iPriority; } + bool operator <(const C4PlayerControlAssignment &cmp) const { return iPriority > cmp.iPriority; } // assignments are processed in DESCENDING priority! const char *GetControlName() const { return sControlName.getData(); } int32_t GetControl() const { return iControl; } bool IsRefsResolved() const { return fRefsResolved; } @@ -256,7 +258,7 @@ class C4PlayerControl // async values C4PlayerControlAssignmentSet *pControlSet; // the control set used by this player - may be NULL if the player cannot be controlled! - typedef std::list KeyBindingList; + typedef std::list KeyBindingList; KeyBindingList KeyBindings; // keys registered into Game.KeyboardInput C4PlayerControlRecentKeyList RecentKeys; // keys pressed recently; for combinations C4PlayerControlRecentKeyList DownKeys; // keys currently held down diff --git a/engine/inc/C4Script.h b/engine/inc/C4Script.h index 74f11ba27..17127f7b5 100644 --- a/engine/inc/C4Script.h +++ b/engine/inc/C4Script.h @@ -150,8 +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 +#define PSF_PlayerControl "PlayerControl" // int iPlr, int iControl, C4ID idControlExtraData, int x, int y, int iStrength, bool fRepeated +#define PSF_PlayerControlRelease "PlayerControlRelease" // int iPlr, 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/C4Game.cpp b/engine/src/C4Game.cpp index 663866b73..931097406 100644 --- a/engine/src/C4Game.cpp +++ b/engine/src/C4Game.cpp @@ -2273,6 +2273,9 @@ BOOL C4Game::InitGame(C4Group &hGroup, bool fLoadSection, bool fLoadSky) { LogFatal(LoadResStr("IDS_PRC_FAIL")); return FALSE; } SetInitProgress(56); + // Final init for loaded player commands. Before linking scripts, so CON_* constants are registered + PlayerControlDefs.FinalInit(); + // Link scripts if (!LinkScriptEngine()) return FALSE; SetInitProgress(57); diff --git a/engine/src/C4PlayerControl.cpp b/engine/src/C4PlayerControl.cpp index f2eeae6a1..d6b2aa3ec 100644 --- a/engine/src/C4PlayerControl.cpp +++ b/engine/src/C4PlayerControl.cpp @@ -122,6 +122,20 @@ int32_t C4PlayerControlDefs::GetControlIndexByIdentifier(const char *szIdentifie return CON_None; } +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")) + { + Game.ScriptEngine.RegisterGlobalConstant(FormatString("CON_%s", szIdtf).getData(), C4VInt(i-Defs.begin())); + } + } + } + /* C4PlayerControlAssignment */ @@ -150,6 +164,7 @@ void C4PlayerControlAssignment::CompileFunc(StdCompiler *pComp) { if (!pComp->Name("Assignment")) { pComp->NameEnd(); pComp->excNotFound("Assignment"); } pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(KeyCombo), "Key", KeyComboVec())); + pComp->Value(mkNamingAdapt(fComboIsSequence, "ComboIsSequence", false)); pComp->Value(mkNamingAdapt(mkParAdapt(sControlName, StdCompiler::RCT_Idtf), "Control", "None")); pComp->Value(mkNamingAdapt(iPriority, "Priority", 0)); const StdBitfieldEntry TriggerModeNames[] = { @@ -749,7 +764,7 @@ bool C4PlayerControl::ExecuteControlScript(int32_t iControl, C4ID idControlExtra // 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)); + C4AulParSet Pars(C4VInt(iPlr), C4VInt(iControl), C4VID(idControlExtraData), C4VInt(rKeyExtraData.x), C4VInt(rKeyExtraData.y), C4VInt(rKeyExtraData.iStrength), C4VBool(fRepeated)); return !!pFunc->Exec(NULL, &Pars); } else @@ -757,7 +772,7 @@ bool C4PlayerControl::ExecuteControlScript(int32_t iControl, C4ID idControlExtra // 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)); + C4AulParSet Pars(C4VInt(iPlr), C4VInt(iControl), C4VID(idControlExtraData), C4VInt(rKeyExtraData.x), C4VInt(rKeyExtraData.y)); return !!pFunc->Exec(NULL, &Pars); } } @@ -835,7 +850,7 @@ void C4PlayerControl::RegisterKeyset(int32_t iPlr, C4PlayerControlAssignmentSet void C4PlayerControl::AddKeyBinding(const C4KeyCodeEx &key, bool fHoldKey, int32_t idx) { - KeyBindings.push_back(new C4CustomKey( + KeyBindings.push_back(new C4KeyBinding( key, FormatString("PlrKey%02d", idx).getData(), KEYSCOPE_Control, new C4KeyCBPassKey(*this, &C4PlayerControl::ProcessKeyDown, fHoldKey ? &C4PlayerControl::ProcessKeyUp : NULL), C4CustomKey::PRIO_PlrControl)); diff --git a/planet/System.c4g/PlayerControl.c b/planet/System.c4g/PlayerControl.c new file mode 100644 index 000000000..083124dce --- /dev/null +++ b/planet/System.c4g/PlayerControl.c @@ -0,0 +1,13 @@ +#strict 2 + +global func PlayerControl(int plr, int ctrl, id spec_id, int x, int y, int strength, bool repeat) +{ + Log("%d, %d, %i, %d, %d, %d, %v", plr, ctrl, spec_id, x,y,strength, repeat); + return true; +} + +global func PlayerControlRelease(int plr, int ctrl, id spec_id, int x, int y) +{ + Log("re %d, %d, %i, %d, %d", plr, ctrl, spec_id, x,y); + return true; +} \ No newline at end of file diff --git a/planet/System.c4g/PlayerControls.txt b/planet/System.c4g/PlayerControls.txt new file mode 100644 index 000000000..583b29156 --- /dev/null +++ b/planet/System.c4g/PlayerControls.txt @@ -0,0 +1,60 @@ +[ControlDefs] + + [ControlDef] + Identifier=Left + GUIName=Left + GUIDesc=Walk left + Hold=1 + RepeatDelay=5 + InitialRepeatDelay=35 + + [ControlDef] + Identifier=Throw + GUIName=Throw + GUIDesc=Get rid of your selected inventory + Hold=1 + RepeatDelay=5 + InitialRepeatDelay=35 + + + [ControlDef] + Identifier=Dig + GUIName=Dig + GUIDesc=Delve through the earth + + [ControlDef] + Identifier=Test + GUIName=Test + GUIDesc=Test key + + +[ControlSets] + + [ControlSet] + Name=Keyboard2Classic + + [Assignment] + Key=Q + Control=Throw + Priority=50 + + [Assignment] + Key=E + Control=Dig + Priority=50 + + [Assignment] + Key=A + Control=Left + TriggerMode=Hold + + [Assignment] + Key=S + Control=Left + TriggerMode=Release | AlwaysUnhandled + + [Assignment] + Key=Throw,Throw,Dig + ComboIsSequence=1 + Control=Test + Priority=60 From 943b47ebd59946980c66eddb2397b731be9f928b Mon Sep 17 00:00:00 2001 From: Sven Eberhardt Date: Mon, 15 Jun 2009 20:38:39 -0400 Subject: [PATCH 07/12] Moving controls to script: Clonk movement --- docs/sdk/playercontrols.xml | 24 ++-- engine/inc/C4Player.h | 2 +- engine/inc/C4PlayerControl.h | 9 +- engine/inc/C4Script.h | 3 +- engine/src/C4Game.cpp | 25 ++-- engine/src/C4Player.cpp | 8 +- engine/src/C4PlayerControl.cpp | 50 +++---- engine/src/C4Script.cpp | 29 +++- planet/System.c4g/PlayerControl.c | 197 ++++++++++++++++++++++++++- planet/System.c4g/PlayerControls.txt | 160 ++++++++++++++++++---- standard/inc/StdFile.h | 1 + standard/src/StdFile.cpp | 8 ++ 12 files changed, 432 insertions(+), 84 deletions(-) diff --git a/docs/sdk/playercontrols.xml b/docs/sdk/playercontrols.xml index 58b06fa1a..563311655 100644 --- a/docs/sdk/playercontrols.xml +++ b/docs/sdk/playercontrols.xml @@ -80,7 +80,7 @@ Script (Standardwert) - Ausfuehrung des Scriptbefehls PlayerControl bzw. PlayerControlRelease. Siehe Script-Callbacks. + Ausfuehrung des Scriptbefehls PlayerControl. Siehe Script-Callbacks. Menu @@ -180,7 +180,7 @@ Script-CallbacksDie meisten Kommandos (abgesehen von asyrnchronen Kommandos im Spielermenue), rufen eine globale Scriptfunktion auf: -global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated) +global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, bool release)Fuer eine Erlaeuterung der Parameter siehe PlayerControl. Die Funktion erhaelt unter anderem den aufrufenden Spieler in iPlr, sowie das ausgefuehrte Kommando in iControl.Fuer ein einfaches Beispiel sei in der globalen PlayerControls.txt folgendes Kommando definiert:[ControlDefs] @@ -200,11 +200,11 @@ Control=Jump Priority=50Dies definiert eine Sprungtaste und die zugehoerige Standardbelegung auf der Tastatur fuer den ersten Spieler. Dazu folgendes Script zur Behandlung: -global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated) +global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, bool release) { // Welches Kommando wurde ausgeloest? // Die Konstante CON_Jump wurde automatisch durch die Definition in PlayerControls.txt angelegt - if (control == CON_Jump) + if (control == CON_Jump && !release) { // Sprungtaste gedrueckt. Der vom Spieler ausgewaehlte Clonk soll springen var player_clonk = GetCursor(player); @@ -227,17 +227,17 @@ GUIDesc=Going underground ExtraData=SHVL Dabei sei SHVL die ID eines Schaufelobjektes. Im globalen Script kann zum Beispiel folgende, allgemeine Behandlung fuer unbekannte Kommandos stehen: -global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated) +global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, bool release) { // Behandlung bekannter Befehle // [...] // Befehl mit eigener Behandlung - if (control_extra) return control_extra->PlayerControl(player, control, x, y, strength, repeat); + if (control_extra) return control_extra->PlayerControl(player, control, x, y, strength, repeat, release); // Unbekanntes Kommando return false; } Und im Script der Schaufel: -func PlayerControl(int player, int control, int x, int y, int strength, bool repeated) +func PlayerControl(int player, int control, int x, int y, int strength, bool repeated, bool release) { // Behandlung bekannter Befehle // Grabkommando direkt an die Schaufel @@ -257,7 +257,7 @@ Gehaltene Tasten Wird fuer ein Kommando das Hold-Flag gesetzt, so speichert die Engine den gegenwaertigen Tastenzustand fuer diese Taste. Solche Tasten haben einige Besonderheiten:
    -
  • Sie generieren beim Loslassen PlayerControlRelease-Aufrufe im Script.
  • +
  • Sie generieren auch beim Loslassen PlayerControl-Aufrufe mit gesetztem Release-Flag im Script.
  • Belegungen koennen mit den Hold/Release-Flags dauerhafte Tastendruecke emulieren.
  • Tastenwiederholungen werden erzeugt.
  • Der Haltezustand der Taste kann mit GetPlayerControlState im Script abgefragt werden.
  • @@ -269,13 +269,7 @@ GUIDesc=Walk left Hold=1
    Im Script wird die Richtung dann auf den Clonk uebertragen: -global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated) -{ - if (control == CON_Left) return UpdateControlDir(player); - // ... -} - -global func PlayerControlRelease(int player, int control, C4ID control_extra, int x, int y) +global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, bool release) { if (control == CON_Left) return UpdateControlDir(player); // ... diff --git a/engine/inc/C4Player.h b/engine/inc/C4Player.h index 143050990..fef0a262a 100644 --- a/engine/inc/C4Player.h +++ b/engine/inc/C4Player.h @@ -185,7 +185,7 @@ class C4Player: public C4PlayerInfoCore BOOL Sell2Home(C4Object *tobj); BOOL DoWealth(int32_t change); BOOL SetHostility(int32_t iOpponent, int32_t iHostility, BOOL fSilent=FALSE); - void CompileFunc(StdCompiler *pComp); + void CompileFunc(StdCompiler *pComp, bool fExact); BOOL LoadRuntimeData(C4Group &hGroup); bool ActivateMenuMain(); bool ActivateMenuTeamSelection(bool fFromMain); diff --git a/engine/inc/C4PlayerControl.h b/engine/inc/C4PlayerControl.h index eb625e146..bd3f8390a 100644 --- a/engine/inc/C4PlayerControl.h +++ b/engine/inc/C4PlayerControl.h @@ -201,6 +201,7 @@ class C4PlayerControlAssignmentSet void MergeFrom(const C4PlayerControlAssignmentSet &Src, bool fLowPrio); // take over all assignments defined in Src const char *GetName() const { return sName.getData(); } + bool IsWildcardName() const { return IsWildcardString(sName.getData()); } C4PlayerControlAssignment *GetAssignmentByControlName(const char *szControlName); void GetAssignmentsByKey(const C4PlayerControlDefs &rDefs, const C4KeyCodeEx &key, bool fHoldKeysOnly, C4PlayerControlAssignmentPVec *pOutVec, const C4PlayerControlRecentKeyList &DownKeys, const C4PlayerControlRecentKeyList &RecentKeys) const; // match only by TriggerKey (last key of Combo) if fHoldKeysOnly @@ -264,6 +265,7 @@ class C4PlayerControl C4PlayerControlRecentKeyList RecentKeys; // keys pressed recently; for combinations C4PlayerControlRecentKeyList DownKeys; // keys currently held down + public: // sync values struct CSync { @@ -294,7 +296,10 @@ class C4PlayerControl void Clear(); void CompileFunc(StdCompiler *pComp); bool operator ==(const CSync &cmp) const; - } Sync; + }; + + private: + CSync Sync; // callbacks from Game.KeyboardInput bool ProcessKeyEvent(const C4KeyCodeEx &key, bool fUp, const C4KeyEventData &rKeyExtraData); @@ -319,6 +324,8 @@ class C4PlayerControl void RegisterKeyset(int32_t iPlr, C4PlayerControlAssignmentSet *pKeyset); // register all keys into Game.KeyboardInput creating KeyBindings bool IsGlobal() const { return iPlr==-1; } + const CSync::ControlDownState *GetControlDownState(int32_t iControl) const + { return Sync.GetControlDownState(iControl); } // callback from control queue void ExecuteControlPacket(const class C4ControlPlayerControl *pCtrl); diff --git a/engine/inc/C4Script.h b/engine/inc/C4Script.h index 17127f7b5..7cb551b60 100644 --- a/engine/inc/C4Script.h +++ b/engine/inc/C4Script.h @@ -150,8 +150,7 @@ 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 iPlr, int iControl, C4ID idControlExtraData, int x, int y, int iStrength, bool fRepeated -#define PSF_PlayerControlRelease "PlayerControlRelease" // int iPlr, int iControl, C4ID idControlExtraData, int x, int y +#define PSF_PlayerControl "PlayerControl" // int iPlr, int iControl, C4ID idControlExtraData, int x, int y, int iStrength, bool fRepeated, bool fReleased // 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/C4Game.cpp b/engine/src/C4Game.cpp index 931097406..8d9249de3 100644 --- a/engine/src/C4Game.cpp +++ b/engine/src/C4Game.cpp @@ -1824,7 +1824,7 @@ void C4Game::CompileFunc(StdCompiler *pComp, CompileSettings comp) { if (!comp.fScenarioSection && comp.fExact) { - pComp->Name("Game"); + pComp->Name("Game"); pComp->Value(mkNamingAdapt(Time, "Time", 0)); pComp->Value(mkNamingAdapt(FrameCounter, "Frame", 0)); // pComp->Value(mkNamingAdapt(Control.ControlRate, "ControlRate", 0)); @@ -1856,24 +1856,30 @@ void C4Game::CompileFunc(StdCompiler *pComp, CompileSettings comp) if (comp.fExact) { pComp->Value(mkNamingAdapt(Weather, "Weather")); - pComp->Value(mkNamingAdapt(Landscape, "Landscape")); - pComp->Value(mkNamingAdapt(Landscape.Sky, "Sky")); + pComp->Value(mkNamingAdapt(Landscape, "Landscape")); + pComp->Value(mkNamingAdapt(Landscape.Sky, "Sky")); } pComp->Value(mkNamingAdapt(mkNamingPtrAdapt(pGlobalEffects, "GlobalEffects"), "Effects")); - // scoreboard compiles into main level [Scoreboard] - if (!comp.fScenarioSection && comp.fExact) - pComp->Value(mkNamingAdapt(Scoreboard, "Scoreboard")); + if (!comp.fScenarioSection && comp.fExact) + { + // scoreboard compiles into main level [Scoreboard] + pComp->Value(mkNamingAdapt(Scoreboard, "Scoreboard")); + // Keyboard status of global keys synchronized for exact (runtime join) only; not for savegames, + // as keys might be released between a savegame save and its resume + //pComp->Value(GlobalPlayerControl); + } + if (comp.fPlayers) { - assert(pComp->isDecompiler()); + assert(pComp->isDecompiler()); // player parsing: Parse all players // This doesn't create any players, but just parses existing by their ID // Primary player ininitialization (also setting ID) is done by player info list // Won't work this way for binary mode! for (C4Player *pPlr=Players.First; pPlr; pPlr=pPlr->Next) - pComp->Value(mkNamingAdapt(*pPlr, FormatString("Player%d", pPlr->ID).getData())); + pComp->Value(mkNamingAdapt(mkParAdapt(*pPlr, comp.fExact), FormatString("Player%d", pPlr->ID).getData())); } } @@ -3231,7 +3237,8 @@ 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.Clear(); + PlayerControlAssignmentSets.MergeFrom(PlayerControlFile.GetAssignmentSets(), false); PlayerControlAssignmentSets.ResolveRefs(&PlayerControlDefs); // And overwrites from config //PlayerControlAssignmentSets.MergeFrom(Config.Controls.Assignments); diff --git a/engine/src/C4Player.cpp b/engine/src/C4Player.cpp index c2b5555a4..02adddf44 100644 --- a/engine/src/C4Player.cpp +++ b/engine/src/C4Player.cpp @@ -1411,7 +1411,7 @@ void C4Player::ObjectCommand2Obj(C4Object *cObj, int32_t iCommand, C4Object *pTa else if (iMode & C4P_Command_Set) cObj->SetCommand(iCommand,pTarget,iX,iY,pTarget2,TRUE,iData); } -void C4Player::CompileFunc(StdCompiler *pComp) +void C4Player::CompileFunc(StdCompiler *pComp, bool fExact) { assert(ID); @@ -1465,6 +1465,9 @@ void C4Player::CompileFunc(StdCompiler *pComp) pComp->Value(mkNamingAdapt(Crew, "Crew" )); pComp->Value(mkNamingAdapt(CrewInfoList.iNumCreated, "CrewCreated", 0)); pComp->Value(mkNamingPtrAdapt( pMsgBoardQuery, "MsgBoardQueries" )); + + // Keys held down + pComp->Value(Control); } BOOL C4Player::LoadRuntimeData(C4Group &hGroup) @@ -1475,9 +1478,10 @@ BOOL C4Player::LoadRuntimeData(C4Group &hGroup) // safety: Do nothing if playeer section is not even present (could kill initialized values) if (!SSearch(pSource, FormatString("[Player%i]", ID).getData())) return FALSE; // Compile (Search player section - runtime data is stored by unique player ID) + // Always compile exact. Exact data will not be present for savegame load, so it does not matter assert(ID); if(!CompileFromBuf_LogWarn( - mkNamingAdapt(*this, FormatString("Player%i", ID).getData()), + mkNamingAdapt(mkParAdapt(*this, true), FormatString("Player%i", ID).getData()), StdStrBuf(pSource), Game.GameText.GetFilePath())) return FALSE; diff --git a/engine/src/C4PlayerControl.cpp b/engine/src/C4PlayerControl.cpp index 4d9b65e47..9cc6d93fe 100644 --- a/engine/src/C4PlayerControl.cpp +++ b/engine/src/C4PlayerControl.cpp @@ -292,7 +292,7 @@ bool C4PlayerControlAssignment::operator ==(const C4PlayerControlAssignment &cmp void C4PlayerControlAssignmentSet::CompileFunc(StdCompiler *pComp) { if (!pComp->Name("ControlSet")) { pComp->NameEnd(); pComp->excNotFound("ControlSet"); } - pComp->Value(mkNamingAdapt(mkParAdapt(sName, StdCompiler::RCT_Idtf), "Name", "None")); + pComp->Value(mkNamingAdapt(mkParAdapt(sName, StdCompiler::RCT_All), "Name", "None")); // can't do RCT_Idtf because of wildcards pComp->Value(mkSTLContainerAdapt(Assignments, StdCompiler::SEP_NONE)); pComp->NameEnd(); } @@ -420,15 +420,31 @@ void C4PlayerControlAssignmentSets::MergeFrom(const C4PlayerControlAssignmentSet { 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) + bool fIsWildcardSet = SrcSet.IsWildcardName(); + if (!fIsWildcardSet) { - pPrevSet->MergeFrom(SrcSet, fLowPrio); + C4PlayerControlAssignmentSet *pPrevSet = GetSetByName(SrcSet.GetName()); + if (pPrevSet) + { + pPrevSet->MergeFrom(SrcSet, fLowPrio); + } + else + { + // new def: Append a copy + Sets.push_back(SrcSet); + } } else { - // new def: Append a copy - Sets.push_back(SrcSet); + // 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); + } + } } } } @@ -597,7 +613,6 @@ bool C4PlayerControl::ProcessKeyEvent(const C4KeyCodeEx &key, bool fUp, const C4 // 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 C4ControlPlayerControl(iPlr, fUp, rKeyExtraData); pControlPacket->AddControl(iControlIndex, pAssignment->GetTriggerMode()); - break; } } } @@ -766,22 +781,11 @@ bool C4PlayerControl::ExecuteControlScript(int32_t iControl, C4ID idControlExtra // player lost? return false; } - if (!fUp) - { - // control down - C4AulFunc *pFunc = Game.ScriptEngine.GetFirstFunc(PSF_PlayerControl); - if (!pFunc) return false; - C4AulParSet Pars(C4VInt(iPlr), 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(iPlr), C4VInt(iControl), C4VID(idControlExtraData), C4VInt(rKeyExtraData.x), C4VInt(rKeyExtraData.y)); - return !!pFunc->Exec(NULL, &Pars); - } + // control down + C4AulFunc *pFunc = Game.ScriptEngine.GetFirstFunc(PSF_PlayerControl); + 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); } diff --git a/engine/src/C4Script.cpp b/engine/src/C4Script.cpp index f84f2fbaf..5deb18392 100644 --- a/engine/src/C4Script.cpp +++ b/engine/src/C4Script.cpp @@ -4368,7 +4368,7 @@ static C4Value FnGetPlayerVal(C4AulContext* cthr, C4Value* strEntry_C4V, C4Value C4Player* pPlayer = Game.Players.Get(iPlr); // get value - return GetValByStdCompiler(strEntry, strSection, iEntryNr, mkNamingAdapt(*pPlayer, "Player")); + return GetValByStdCompiler(strEntry, strSection, iEntryNr, mkNamingAdapt(mkParAdapt(*pPlayer, true), "Player")); } static C4Value FnGetPlayerInfoCoreVal(C4AulContext* cthr, C4Value* strEntry_C4V, C4Value* strSection_C4V, C4Value* iPlayer_C4V, C4Value *iEntryNr_C4V) @@ -6307,6 +6307,32 @@ static bool FnSetNextMission(C4AulContext *ctx, C4String *szNextMission, C4Strin return true; } +static long FnGetPlayerControlState(C4AulContext *ctx, long iPlr, long iControl) + { + // get control set to check + C4PlayerControl *pCheckCtrl = NULL; + if (iPlr == NO_OWNER) + { + //pCheckCtrl = Game.GlobalPlayerControls; + } + else + { + C4Player *pPlr = Game.Players.Get(iPlr); + if (pPlr) + { + pCheckCtrl = &(pPlr->Control); + } + } + // invalid player or no controls + if (!pCheckCtrl) return 0; + // query control + const C4PlayerControl::CSync::ControlDownState *pControlState = pCheckCtrl->GetControlDownState(iControl); + // no state means not down + if (!pControlState) return 0; + // otherwise take down-value + return pControlState->DownState.iStrength; + } + //=========================== C4Script Function Map =================================== // defined function class @@ -6794,6 +6820,7 @@ void InitFunctionMap(C4AulScriptEngine *pEngine) AddFunc(pEngine, "LocateFunc", FnLocateFunc); AddFunc(pEngine, "PathFree", FnPathFree); AddFunc(pEngine, "SetNextMission", FnSetNextMission); + AddFunc(pEngine, "GetPlayerControlState", FnGetPlayerControlState); new C4AulDefCastFunc(pEngine, "ScoreboardCol", C4V_C4ID, C4V_Int); new C4AulDefCastFunc(pEngine, "CastInt", C4V_Any, C4V_Int); new C4AulDefCastFunc(pEngine, "CastBool", C4V_Any, C4V_Bool); diff --git a/planet/System.c4g/PlayerControl.c b/planet/System.c4g/PlayerControl.c index 083124dce..1bb541795 100644 --- a/planet/System.c4g/PlayerControl.c +++ b/planet/System.c4g/PlayerControl.c @@ -1,13 +1,200 @@ #strict 2 -global func PlayerControl(int plr, int ctrl, id spec_id, int x, int y, int strength, bool repeat) +// Functions to handle player controls (i.e., input keys) + +// PlayerControlRelease +// Called by engine whenever a control is issued +// Forwards control to special handler or cursor +// Return whether handled +global func PlayerControl(int plr, int ctrl, id spec_id, int x, int y, int strength, bool repeat, bool release) { - Log("%d, %d, %i, %d, %d, %d, %v", plr, ctrl, spec_id, x,y,strength, repeat); + //Log("%d, %s, %i, %d, %d, %d, %v, %v", plr, GetPlayerControlName(ctrl), spec_id, x,y,strength, repeat, release); + // Control handled by definition? Forward + if (spec_id) return spec_id->PlayerControl(plr, ctrl, x, y, strength, repeat, release); + // Forward control to cursor + var cursor = GetCursor(plr); + if (cursor) return cursor->ObjectControl(plr, ctrl, x,y, strength, repeat, release); + // No cursor? Nothing to handle control then + return false; +} + +/* Object functions */ +// To be called in an object context only! + +// ObjectControl +// Called from PlayerControl when a control is issued to the cursor +// Return whether handled +global func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool repeat, bool release) +{ + // Object controlled by plr + SetController(plr); + // Any control resets a previously given command + SetCommand(this, "None"); + // Movement controls + if (ctrl==CON_Left || ctrl==CON_Right || ctrl==CON_Up || ctrl==CON_Down) + { + return ObjectControlMovement(plr, ctrl, strength, release); + } + // Unhandled control + var srelease = ""; + if (release) srelease="Up"; + Log("Unhandled: %d, %s%s, %d, %d, %d, %v, %v", plr, GetPlayerControlName(ctrl), srelease, x,y,strength, repeat); + return false; +} + +// ObjectControlMovement +// Called when CON_Left/Right/Up/Down controls are issued/released +// Return whether handled +global func ObjectControlMovement(int plr, int ctrl, int strength, bool release) +{ + var proc = GetProcedure(); + // Some specific movement controls + if (!release) + { + // Jump control + if (ctrl == CON_Up && proc == "WALK") + { + return PlayerObjectCommand(plr, false, "Jump"); + } + if (proc == "SCALE") // Let go from scaling a wall + { + if (ctrl == CON_Left && GetDir() == DIR_Right) return ObjectComLetGo(-10); + if (ctrl == CON_Right && GetDir() == DIR_Left) return ObjectComLetGo(+10); + } + else if (proc == "HANGLE") // Let go from hangling the ceiling + { + if (ctrl == CON_Down) return ObjectComLetGo(0,0); + } + else if (proc == "FIGHT") // Clonk-to-Clonk-fight. Might want to implement some more sophisticated behaviour here? + { + // stop, but don't abort ComDir processing. May want to do Stop while holding a direction to run away? + if (ctrl == CON_Down) ObjectComStop(); + } + // Make sure other selected Clonks are following + PlayerObjectCommand(plr, true, "Follow", this, GetX(), GetY()); + // Direct turnaround if object is standing still. Valid for any procedure in OC + if (!GetXDir()) + { + if (ctrl == CON_Left) SetDir(DIR_Left); + else if (ctrl == CON_Right) SetDir(DIR_Right); + } + } + return ObjectControlUpdateComdir(plr); +} + +// ObjectControlUpdateComdir +// Updates ComDir of object based on current Con_*-directional controls +// Return whether actual, effective direction of movement changed +global func ObjectControlUpdateComdir(int plr) +{ + // Generic movement: Update ComDir based on current control state + var new_comdir = GetPlayerConDir(plr, CON_Left, CON_Up, CON_Right, CON_Down); + var old_comdir = GetComDir(); + if (new_comdir != old_comdir) + { + // ComDir changed. Update. + SetComDir(new_comdir); + //var s = ""; + //if (GetPlayerControlState(plr, CON_Left)) s = Format("%sL", s); + //if (GetPlayerControlState(plr, CON_Up)) s = Format("%sU", s); + //if (GetPlayerControlState(plr, CON_Right)) s = Format("%sR", s); + //if (GetPlayerControlState(plr, CON_Down)) s = Format("%sD", s); + //s = Format("%s %s", s, ["Stop", "Up", "UpRight", "Right", "DownRight", "Down", "DownLeft", "Left", "UpLeft"][new_comdir]); + //Message("@%s", this, s); + // The control is only handled if it had an actual effect on the current movement direction of the Clonk + var old_cx,old_cy,new_cx,new_cy; + ComDir2XY(old_comdir, old_cx, old_cy); + ComDir2XY(new_comdir, new_cx, new_cy); + var is_handled; + var proc = GetProcedure(); + if (proc == "WALK" || proc == "HANGLE" || proc == "PUSH" || proc == "PULL") + is_handled = (old_cx != new_cx) && !new_cy; // Only horizontal movement changed actual direction. Also, enfore clear Left/Right commands without leftover Up/Down + else if (proc == "SCALE") + is_handled = (old_cy != new_cy) && !new_cx; // Only vertical movement changed actual direction. Also, enfore clear Up/Down to prevent "Zuppel" in corner + else if (proc == "SWIM" || proc == "FLOAT" || proc == "DIG") + is_handled = (old_cx != new_cx || old_cy != new_cy); // Free 360 degree movement + else + is_handled = false; + return is_handled; + } + else + { + // ComDir did not change. -> The control was not handled + //Log("NoChange"); + return false; + } +} + +// GetPlayerControlName +// Helper function to turn CON_*-constants into strings +global func GetPlayerControlName(int ctrl) +{ + if (ctrl == CON_Left ) return "Left"; + if (ctrl == CON_Right ) return "Right"; + if (ctrl == CON_Up ) return "Up"; + if (ctrl == CON_Down ) return "Down"; + if (ctrl == CON_Throw ) return "Throw"; + if (ctrl == CON_Dig ) return "Dig"; + return Format("Unknown(%d)", ctrl); +} + +// GetPlayerConDir +// Return COMD_*-constant corresponding to current state of passed directional controls +global func GetPlayerConDir(int plr, int con_left, int con_up, int con_right, int con_down) +{ + var x,y; + if (GetPlayerControlState(plr, con_left)) --x; + if (GetPlayerControlState(plr, con_up)) --y; + if (GetPlayerControlState(plr, con_right)) ++x; + if (GetPlayerControlState(plr, con_down)) ++y; + // Creating an array here for every keypress/release + // Would be so cool to have this static const. Guenther? + var dir_coms = [COMD_UpLeft, COMD_Up, COMD_UpRight, COMD_Left, COMD_None, COMD_Right, COMD_DownLeft, COMD_Down, COMD_DownRight]; + return dir_coms[y*3+x+4]; +} + +// ComDir2XY +// Returns coordinate directions associated with a COMD_Constant +global func ComDir2XY(int comd, &x, &y) +{ + // Creating an array here for every keypress/release + // Would be so cool to have this static const. Guenther? + x = [0,0,1,1,1,0,-1,-1,-1][comd]; + y = [0,-1,-1,0,1,1,1,0,-1][comd]; return true; } -global func PlayerControlRelease(int plr, int ctrl, id spec_id, int x, int y) +// PlayerObjectCommand +// Give a command to all selected Clonks of a player +global func PlayerObjectCommand(int plr, bool exclude_cursor, string command, object target, int tx, int ty, object target2) { - Log("re %d, %d, %i, %d, %d", plr, ctrl, spec_id, x,y); + for (var i=exclude_cursor; i0) || (SCharCount('*', szString)>0); + } + bool WildcardMatch(const char *szWildcard, const char *szString) { // safety From f34c2d79bca482f1087535d362110871b34cfd70 Mon Sep 17 00:00:00 2001 From: Sven Eberhardt Date: Mon, 15 Jun 2009 22:48:53 -0400 Subject: [PATCH 08/12] Moving controls to script: Dig + Throw --- engine/src/C4PlayerControl.cpp | 15 +- engine/src/C4Script.cpp | 8 + planet/Objects.c4d/Clonk.c4d/Script.c | 471 +++++--------------------- planet/System.c4g/C4.c | 13 +- planet/System.c4g/PlayerControl.c | 44 ++- planet/System.c4g/PlayerControls.txt | 84 ++++- 6 files changed, 210 insertions(+), 425 deletions(-) diff --git a/engine/src/C4PlayerControl.cpp b/engine/src/C4PlayerControl.cpp index 9cc6d93fe..74be2587b 100644 --- a/engine/src/C4PlayerControl.cpp +++ b/engine/src/C4PlayerControl.cpp @@ -204,8 +204,12 @@ bool C4PlayerControlAssignment::ResolveRefs(C4PlayerControlAssignmentSet *pParen 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()); + // 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); if (pRefAssignment) { // resolve itself if necessary @@ -303,7 +307,8 @@ void C4PlayerControlAssignmentSet::MergeFrom(const C4PlayerControlAssignmentSet 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 + // 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... C4PlayerControlAssignment *pPrevAssignment = GetAssignmentByControlName(SrcAssignment.GetControlName()); if (pPrevAssignment) { @@ -333,7 +338,9 @@ C4PlayerControlAssignment *C4PlayerControlAssignmentSet::GetAssignmentByControlN { for (C4PlayerControlAssignmentVec::iterator i = Assignments.begin(); i != Assignments.end(); ++i) if (SEqual((*i).GetControlName(), szControlName)) - return &*i; + // We don't like release keys... (2do) + if (!((*i).GetTriggerMode() & C4PlayerControlAssignment::CTM_Release)) + return &*i; return NULL; } diff --git a/engine/src/C4Script.cpp b/engine/src/C4Script.cpp index 5deb18392..6eb33a521 100644 --- a/engine/src/C4Script.cpp +++ b/engine/src/C4Script.cpp @@ -753,6 +753,13 @@ static bool FnSetActionData(C4AulContext *cthr, long iData, C4Object *pObj) return TRUE; } +static long FnGetActionData(C4AulContext *cthr, C4Object *pObj) + { + if (!pObj) pObj=cthr->Obj; if (!pObj || !pObj->Status) return FALSE; + // get data + return pObj->Action.Data; + } + static bool FnObjectSetAction(C4AulContext *cthr, C4Object *pObj, C4String *szAction, C4Object *pTarget, C4Object *pTarget2, bool fDirect) { @@ -6509,6 +6516,7 @@ void InitFunctionMap(C4AulScriptEngine *pEngine) AddFunc(pEngine, "Bubble", FnBubble); AddFunc(pEngine, "SetAction", FnSetAction); AddFunc(pEngine, "SetActionData", FnSetActionData); + AddFunc(pEngine, "GetActionData", FnGetActionData); AddFunc(pEngine, "SetBridgeActionData", FnSetBridgeActionData); AddFunc(pEngine, "GetAction", FnGetAction); AddFunc(pEngine, "GetActTime", FnGetActTime); diff --git a/planet/Objects.c4d/Clonk.c4d/Script.c b/planet/Objects.c4d/Clonk.c4d/Script.c index c6684b449..cda3ed00c 100644 --- a/planet/Objects.c4d/Clonk.c4d/Script.c +++ b/planet/Objects.c4d/Clonk.c4d/Script.c @@ -2,12 +2,6 @@ #strict 2 -// Zauberei - benötigt, wenn der Clonk Zielzauber z.B. aus dem Zauberturm zaubert -// Auch benötigt für den König -local pAimer; // Aktive Zielsteuerung; wird abgrbrochen, wenn der Zauberer gestört wird (Nur Fantasypack) -local pAimedSpell; // Zauber, der gezielt wird (Nur Fantasypack) -local pAimedSpellOrigin; // Objekt, das einen Zielzauber initiiert hat. An dieses werden SpellFailed/SpellSucceeded-Nachrichten weitergeleitet - /* Initialisierung */ @@ -39,8 +33,6 @@ protected func Swimming2() /* Bei Hinzufügen zu der Crew eines Spielers */ protected func Recruitment(int iPlr) { - // Alchemieregel: Jeder Clonk kriegt einen angelegten Beutel spendiert - if(ObjectCount(ALCO)) CreateObject(ALC_,0,0,-1)->~BelongTo(this); // Broadcast für Crew GameCallEx("OnClonkRecruitment", this, iPlr); } @@ -70,82 +62,78 @@ public func FindTree() return FindObject2(Find_AtPoint(), Find_OCF(OCF_Chop), Find_Layer(GetObjectLayer())); } + /* Steuerung */ -protected func ControlLeft() +public func GetInteractionTarget() { - // Steuerung an Effekt weitergeben - if (Control2Effect("ControlLeft")) return 1; - // Steuerung an Pferd weiterleiten - if (IsRiding()) return GetActionTarget()->~ControlLeft(this); - // Keine überladene Steuerung - return 0; -} - -protected func ControlLeftDouble() + // Contained interaction target + var container = Contained(); + if (container) { - // Steuerung an Effekt weitergeben - if (Control2Effect("ControlLeftDouble")) return true; + if (container->GetCategory() & (C4D_Structure | C4D_Vehicle)) return container; } + // Procedure interaction target + // (Except for FIGHT, of course. You can't control your enemy ;)) + var proc = GetProcedure(); + if (proc == "PUSH" || proc == "PULL" || proc == "BUILD") return GetActionTarget(); + // First contents object interaction target + return Contents(0); +} -protected func ControlRightDouble() +public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool repeat, bool release) +{ + // Generic movement + if (inherited(plr, ctrl, x, y, strength, repeat, release)) return true; + var proc = GetProcedure(); + // Handled by InteractionTarget? + var interaction_target = GetInteractionTarget(); + if (interaction_target) { - // Steuerung an Effekt weitergeben - if (Control2Effect("ControlRightDouble")) return true; + if (interaction_target->ObjectControl(plr, ctrl, x,y, strength, repeat, release)) return true; } - -protected func ControlLeftReleased() -{ - // Steuerung an Pferd weiterleiten - if (IsRiding()) return GetActionTarget()->~ControlLeftReleased(this); - // Keine überladene Steuerung - return 0; -} - -protected func ControlRight() -{ - // Steuerung an Effekt weitergeben - if (Control2Effect("ControlRight")) return 1; - // Steuerung an Pferd weiterleiten - if (IsRiding()) return GetActionTarget()->~ControlRight(this); - // Keine überladene Steuerung - return 0; -} - -protected func ControlRightReleased() -{ - // Steuerung an Pferd weiterleiten - if (IsRiding()) return GetActionTarget()->~ControlRightReleased(this); - // Keine überladene Steuerung - return 0; -} - -protected func ControlUp() -{ - // Steuerung an Effekt weitergeben - if (Control2Effect("ControlUp")) return 1; - // Steuerung an Pferd weiterleiten - if (IsRiding()) return GetActionTarget()->~ControlUp(this); - // Bei JnR Delfinsprung - if(GetPlrCoreJumpAndRunControl(GetController())) - DolphinJump(); - // Keine überladene Steuerung - return 0; -} - -protected func ControlUpReleased() -{ - // Steuerung an Pferd weiterleiten - if (IsRiding()) return GetActionTarget()->~ControlUpReleased(this); - // Keine überladene Steuerung - return 0; -} - -protected func ControlUpDouble() -{ - // Steuerung an Effekt weitergeben - if (Control2Effect("ControlUpDouble")) return 1; - DolphinJump(); + // Dolphin jump + if (ctrl == CON_DolphinJump) return DolphinJump(); + // Context menu + else if (ctrl == CON_Context) + { + // Context menu of interaction target (fallback to this if no interaction target) + if (!interaction_target) interaction_target = this; + SetCommand(this,"Context",0,0,0,interaction_target); + return ExecuteCommand(); + } + // Throw + else if (ctrl == CON_Throw) + { + // During Scale+Hangle, this means "Drop". During dig, this means object dig out request. Otherwise, throw. + if (proc == "DIG") + return SetActionData(!GetActionData()); + else if (proc == "SCALE" || proc == "HANGLE") + return PlayerObjectCommand(plr, false, "Drop"); + else + return PlayerObjectCommand(plr, false, "Throw"); + } + // Dig + else if (ctrl == CON_Dig) + { + if (proc == "DIG") + { + // Currently, another press on dig ends digging. Maybe changed once we have the shovel system? + SetAction("Walk"); + return true; + } + else if (proc == "WALK") + { + if (!GetPhysical("Dig")) return false; + if (!SetAction("Dig")) return false; + SetActionData(0); + return true; + } + // Can't dig now + return false; + } + // Unhandled + return false; } private func DolphinJump() @@ -166,105 +154,6 @@ private func DolphinJump() SetAction("Dive"); } -protected func ControlDown() -{ - // Steuerung an Effekt weitergeben - if (Control2Effect("ControlDown")) return 1; - // Steuerung an Pferd weiterleiten - if (IsRiding()) return GetActionTarget()->~ControlDown(this); - // Keine überladene Steuerung - return 0; -} - -protected func ControlDownReleased() -{ - // Steuerung an Pferd weiterleiten - if (IsRiding()) return GetActionTarget()->~ControlDownReleased(this); - // Keine überladene Steuerung - return 0; -} - -protected func ControlDownSingle() -{ - // Steuerung an Effekt weitergeben - if (Control2Effect("ControlDownSingle")) return 1; - // Steuerung an Pferd weiterleiten - if (IsRiding()) return GetActionTarget()->~ControlDownSingle(this); - // Keine überladene Steuerung - return 0; -} - -protected func ControlDownDouble() -{ - // Steuerung an Effekt weitergeben - if (Control2Effect("ControlDownDouble")) return 1; - // Steuerung an Pferd weiterleiten - if (IsRiding()) return GetActionTarget()->~ControlDownDouble(this); - // Keine überladene Steuerung - return 0; -} - -protected func ControlDig() -{ - // Steuerung an Effekt weitergeben - if (Control2Effect("ControlDig")) return 1; - // Steuerung an Pferd weiterleiten - if (IsRiding()) return GetActionTarget()->~ControlDig(this); - // Keine überladene Steuerung - return 0; -} - -protected func ControlDigReleased() -{ - // Steuerung an Pferd weiterleiten - if (IsRiding()) return GetActionTarget()->~ControlDigReleased(this); - // Keine überladene Steuerung - return 0; -} - -protected func ControlDigSingle() -{ - // Steuerung an Pferd weiterleiten - if (IsRiding()) return GetActionTarget()->~ControlDigSingle(this); - // Keine überladene Steuerung - return 0; -} - -protected func ControlDigDouble() -{ - // Steuerung an Effekt weitergeben - if (Control2Effect("ControlDigDouble")) return 1; - // Steuerung an Pferd weiterleiten - if (IsRiding()) return GetActionTarget()->~ControlDigDouble(this); - // Keine überladene Steuerung - return 0; -} - -protected func ControlThrow() -{ - // Bei vorherigem Doppel-Stop nur Ablegen - if (GetPlrDownDouble(GetOwner())) return 0; - // Steuerung an Effekt weitergeben - if (Control2Effect("ControlThrow")) return 1; - // Reiten und Werfen - if (IsRiding()) - if (Contents(0)) - { - SetAction("RideThrow"); - return 1; - } - // Keine überladene Steuerung - return 0; -} - -protected func ControlUpdate(object self, int comdir, bool dig, bool throw) -{ - // Steuerung an Pferd weiterleiten - if(IsRiding()) return GetActionTarget()->~ControlUpdate(self, comdir, dig, throw); - // Keine überladene Steuerung - return 0; -} - protected func ControlCommand(szCommand, pTarget, iTx, iTy, pTarget2, Data) { // Kommando MoveTo an Pferd weiterleiten @@ -291,6 +180,9 @@ protected func ControlCommand(szCommand, pTarget, iTx, iTy, pTarget2, Data) return 0; } +public func ControlDownDouble() {} // dummy + + /* Verwandlung */ private func RedefinePhysical(szPhys, idTo) @@ -400,6 +292,7 @@ public func Redefine(idTo) return 1; } + /* Essen */ public func Feed(iLevel) @@ -409,6 +302,7 @@ public func Feed(iLevel) return 1; } + /* Aktionen */ private func Riding() @@ -501,6 +395,7 @@ protected func Scaling() return 1; } + /* Ereignisse */ protected func CatchBlow() @@ -542,9 +437,6 @@ protected func Death(int iKilledBy) // Der Broadcast könnte seltsame Dinge gemacht haben: Clonk ist noch tot? if (GetAlive()) return; - // den Beutel fallenlassen - if(GetAlchemBag()) GetAlchemBag()->~Loose(); - Sound("Die"); DeathAnnounce(); // Letztes Mannschaftsmitglied tot: Script benachrichtigen @@ -583,6 +475,7 @@ protected func CheckStuck() SetPosition(GetX(), GetY() + 1); } + /* Status */ public func IsRiding() @@ -593,6 +486,7 @@ public func IsRiding() public func IsClonk() { return 1; } + /* Kontext */ public func ContextRelease(pCaller) @@ -644,6 +538,7 @@ public func ContextHome(pCaller) return 1; } + /* Hilfsfunktion */ public func ContainedCall(string strFunction, object pTarget) @@ -653,34 +548,6 @@ public func ContainedCall(string strFunction, object pTarget) AddCommand(this, "Enter", pTarget); } -/* Steuerung */ - -protected func ControlSpecial2() -{ - [$CtrlMenuDesc$|Image=CXTX] - // In einem Gebäude oder Fahrzeug: das Kontextmenü des Gebäudes öffnen - if (Contained()) - if ((Contained()->GetCategory() & C4D_Structure) || (Contained()->GetCategory() & C4D_Vehicle)) - { - SetCommand(this,"Context",0,0,0,Contained()); - return ExecuteCommand(); - } - // Fasst ein Objekt an: Kontextmenü des angefassten Objekts öffnen - if (GetAction() == "Push") - { - SetCommand(this,"Context",0,0,0,GetActionTarget()); - return ExecuteCommand(); - } - // Trägt ein Objekt: Kontextmenü des ersten getragenen Objekts öffnen - if (Contents(0)) - { - SetCommand(this,"Context",0,0,0,Contents(0)); - return ExecuteCommand(); - } - // Ansonsten das Kontextmenü des Clonks öffnen - SetCommand(this,"Context",0,0,0,this); - return ExecuteCommand(); -} /* Callback beim Auswahl aus dem Construct-Kontextmenu */ @@ -692,6 +559,7 @@ public func ControlCommandConstruction(target, x, y, target2, def) return FinishCommand(this, false, 0) ; } + /* Automatische Produktion */ public func ControlCommandAcquire(target, x, y, target2, def) @@ -751,6 +619,7 @@ public func GetProducerOf(def) return FindObject2(Find_InRect(-500,-250,1000,500), Find_Func("IsProducerOf", this, def), Sort_Distance()); } + /* Trinken */ public func Drink(object pDrink) @@ -762,6 +631,7 @@ public func Drink(object pDrink) // die Potions löschen sich meist selber... } + /* Einsammeln */ public func RejectCollect(id idObject, object pObject) @@ -788,6 +658,7 @@ public func RejectCollect(id idObject, object pObject) return GetNonSpecialCount()>=MaxContentsCount(); } + /* Itemlimit */ public func MaxContentsCount() { return 1; } @@ -884,24 +755,6 @@ public func DescendVehicle() } -/* Effektsteuerung */ - -private func Control2Effect(string szControl) - { - // Von Effektzahl abwärts zählen - var i = GetEffectCount(0, this), iEffect; - var res; - while (i--) - { - // Effekte mit Control im Namen benachrichtigen - iEffect = GetEffect("*Control*", this, i); - // Message("%s", this, GetEffect(0, this, iEffect, 1)); - if ( GetEffect(0, this, iEffect, 1) ) - res += EffectCall(this, iEffect, szControl); - } - return res; - } - /* Pfeile */ @@ -973,170 +826,8 @@ private func GetArrowCount() } -/* Zauberei - benötigt, wenn der Clonk Zielzauber z.B. aus dem Zauberturm zaubert */ +/* Dummies, damit die Engine die Namen kennt... */ -public func SpellFailed(id idSpell, object pByCaller) -{ - // Umleiten an eigentliche Zauberquelle? (Buch, Zauberturm, etc.) - var pSpellOrigin = pAimedSpellOrigin; - pAimedSpellOrigin = 0; - if (pSpellOrigin && pSpellOrigin != this) - // Auch bei nicht erfolgreicher Umleitung abbrechen: Das zaubernde Objekt hat im Normalfall die Zutaten/Zauberenergie für den - // Zauber bereit gestellt, und diese sollten nicht an den Clonk zurück gegeben werden - return (pSpellOrigin->~SpellFailed(idSpell, this)); - // Magieenergie zurückgeben - DoMagicEnergy(Value(idSpell), 0, true); - // Alchemische Zutaten zurückgeben - if(ObjectCount(ALCO)) IncreaseAlchem(idSpell); -} - -public func SpellSucceeded(id idSpell, object pByCaller) -{ - // Umleiten an eigentliche Zauberquelle? (Buch, Zauberturm, etc.) - var pSpellOrigin = pAimedSpellOrigin; - pAimedSpellOrigin = 0; - if (pSpellOrigin && pSpellOrigin != this) - // Auch bei nicht erfolgreicher Umleitung abbrechen: Das zaubernde Objekt hat im Normalfall das Magietraining schon erledigt - return (pSpellOrigin->~SpellSucceeded(idSpell, this)); - // Globaler Aufruf für Zauber - OnClonkSucceededSpell(idSpell); -} - -// Der Clonk kann von sich aus nicht zaubern und hat keine Aktivitäten dafür -private func SetMagicAction(id idForSpell) {} -private func SetCastAction() {} -private func EndMagicAction() {} - - -/* Zielsteuerung - nur aktiv, wenn das Fantasypack die globalen Funktionen CreateAimer und CreateSelector überladen hat */ - -public func DoSpellAim(object pSpell, object pSpellOrigin) - { - pAimedSpell = pSpell; - pAimedSpellOrigin = pSpellOrigin; - pAimer = CreateAimer(this, this, GetDir()*180-90); - - if (!pAimer) return 0; - - // Callback an die Zauberquelle, dass noch gezielt wird - if (pSpellOrigin) pSpellOrigin->~SpellAiming(pSpell, this); - - // Zielvorgang für Zauber - SetComDir(COMD_Stop); - SetCastAction(); - return pAimer; - } - -public func OnAimerEnter(int iAngle) - { - // Zauber weg? - if (!pAimedSpell) return OnAimerAbort(iAngle); - var idSpell = GetID(pAimedSpell); - // Aktivität - SetMagicAction(); - // Zauber benachrichtigen - if (!pAimedSpell->~ActivateAngle(this, iAngle)) - { - SpellFailed(idSpell); - return 0; - } - // OK; Zauber erfolgreich - return SpellSucceeded(idSpell); - } - -public func AimingAngle(int iAngle) - { - // Zauber weg? - if (!pAimedSpell) return OnAimerAbort(iAngle); - // Zielaktion setzen, wenn im Laufen. Das ist etwas ungeschickt, weil damit - // die Magic-Aktion abgebrochen wird, die der Magier bereits bis zur Haelfte - // durchgefuehrt hat. Dummerweise laesst sich frueher nicht feststellen, ob - // der auszufuehrende Zauber einen Aimer brauchen wird... - if(GetActMapVal("Name", "AimMagic") ) - if(GetAction() == "Walk" || GetAction() == "Magic") - SetAction("AimMagic"); - - // Phase anpassen - if(GetAction() == "AimMagic") - { - // Auch richtigen Winkel verwenden wenn nach links gedreht - var iHalfAngle = iAngle; - if(iHalfAngle < 0) iHalfAngle = -iHalfAngle; - SetPhase(BoundBy((iHalfAngle + 9) / 18, 0, 9) ); - } - // Weitergabe an den Zauber - return pAimedSpell->~AimingAngle(this, iAngle); - } - -public func OnAimerAbort(int iAngle) - { - // Aktivität zurücksetzen - EndMagicAction(); - // Benachrichtigung - if (!pAimedSpell) return 1; - var idSpell = GetID(pAimedSpell); - if (!pAimedSpell->~AbortAiming(this)) - // Standardaktion: Zauber löschen - RemoveObject(pAimedSpell); - pAimedSpell = 0; - // OK; Zauber nicht erfolgreich - return SpellFailed(idSpell); - } - -public func DoSpellSelect(object pSpell, int iRadius, object pSpellOrigin) - { - // Zauber sichern - pAimedSpell = pSpell; - pAimedSpellOrigin = pSpellOrigin; - pAimer = CreateSelector(pSpell, this, iRadius); - if (!pAimer) return; - // Callback an die Zauberquelle, dass noch gezielt wird - if (pSpellOrigin) pSpellOrigin->~SpellAiming(pSpell, this); - // Zielvorgang für Zauber - SetComDir(COMD_Stop); - SetCastAction(); - return pAimer; - } - -public func OnSelectorEnter(object pTarget) - { - // Zauber weg? - if (!pAimedSpell) return OnAimerAbort(); - var idSpell = GetID(pAimedSpell); - // Aktivität - SetMagicAction(); - // Zauber benachrichtigen - if (!pAimedSpell->~ActivateTarget(this, pTarget)) - { - SpellFailed(idSpell); - return 0; - } - // OK; Zauber erfolgreich - return SpellSucceeded(idSpell); - } - -public func OnSelectorAbort() - { - // Aktivität zurücksetzen - EndMagicAction(); - // Benachrichtigung - if (!pAimedSpell) return 1; - var idSpell = GetID(pAimedSpell); - if (!pAimedSpell->~AbortSelecting(this)) - // Standardaktion: Zauber löschen - RemoveObject(pAimedSpell); - pAimedSpell = 0; - // OK; Zauber nicht erfolgreich - return SpellFailed(idSpell); - } - -public func SelectorTarget(object pTarget) { if(pAimedSpell) return pAimedSpell->~SelectorTarget(this,pTarget); } - -// Momentanen Zauber abbrechen -protected func AbortCasting() - { - if (pAimer) pAimer->Abort(); - return 1; - } - -public func Abort() {} // dummy call to instantiate function name +func Activate() {} +func HowToProduce() {} +func PackCount() {} diff --git a/planet/System.c4g/C4.c b/planet/System.c4g/C4.c index ee5f0bba2..656355263 100644 --- a/planet/System.c4g/C4.c +++ b/planet/System.c4g/C4.c @@ -2,6 +2,7 @@ #strict 2 +/* proplist changes aren't in this branch? // stuff for the proplist changes static const DFA_NONE =-1; static const DFA_WALK = 0; @@ -28,18 +29,15 @@ global func GetActMapVal(string strEntry, string strAction, id idDef, int iEntry if (!idDef) idDef = GetID(); if (strEntry == "Facet") strEntry = ["X", "Y", "Wdt", "Hgt", "OffX", "OffY"][iEntryNr]; return GetProperty(strEntry, GetProperty(strAction, idDef)); -} +} */ global func CastC4ID(x) { return x; } -// Abgelöst durch SetPosition -global func ForcePosition(object obj, int x, int y) { return SetPosition(x, y, obj); } - -// Abgelöst durch RemoveObject -global func AssignRemoval(object obj) { return RemoveObject(obj); } - // Für Szenarien ohne Objects.c4d... global func EmptyBarrelID() { return BARL; } +global func GetBarrelType() {} +func BarrelDoFill() {} +func BarrelIsFull() {} // Fügt das Material in ein Fass im Objekt ein global func ObjectInsertMaterial(int imat, object pTarget) @@ -58,6 +56,7 @@ global func ObjectInsertMaterial(int imat, object pTarget) // Kein Fass? Dann Objekt überlaufen lassen return InsertMaterial(imat, GetX(pTarget)-GetX(), GetY(pTarget)-GetY()); } + // Auffüllbares Fass im Objekt suchen global func FindFillBarrel(object pInObj, id type) diff --git a/planet/System.c4g/PlayerControl.c b/planet/System.c4g/PlayerControl.c index 1bb541795..c47b1d040 100644 --- a/planet/System.c4g/PlayerControl.c +++ b/planet/System.c4g/PlayerControl.c @@ -13,21 +13,47 @@ global func PlayerControl(int plr, int ctrl, id spec_id, int x, int y, int stren if (spec_id) return spec_id->PlayerControl(plr, ctrl, x, y, strength, repeat, release); // Forward control to cursor var cursor = GetCursor(plr); - if (cursor) return cursor->ObjectControl(plr, ctrl, x,y, strength, repeat, release); + if (cursor) + { + // Object controlled by plr + cursor->SetController(plr); + // Overload by effect? + if (cursor->Control2Effect(plr, ctrl, x, y, strength, repeat, release)) return true; + return cursor->ObjectControl(plr, ctrl, x,y, strength, repeat, release); + } // No cursor? Nothing to handle control then return false; } + + /* Object functions */ // To be called in an object context only! +// Control2Effect +// Call control function in all effects that have "Control" in their name +global func Control2Effect(int ctrl, int x, int y, int strength, bool repeat, bool release) + { + // Count down from EffectCount, in case effects get deleted + var i = GetEffectCount("*Control*", this), iEffect; + var res; + while (i--) + { + iEffect = GetEffect("*Control*", this, i); + if ( GetEffect(0, this, iEffect, 1) ) + if (EffectCall(this, iEffect, "Control", ctrl, x,y,strength, repeat, release)) + return true; + } + // No effect handled the control + return false; + } + // ObjectControl // Called from PlayerControl when a control is issued to the cursor // Return whether handled +// To be overloaded by specific objects to enable additional controls global func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool repeat, bool release) { - // Object controlled by plr - SetController(plr); // Any control resets a previously given command SetCommand(this, "None"); // Movement controls @@ -36,9 +62,6 @@ global func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re return ObjectControlMovement(plr, ctrl, strength, release); } // Unhandled control - var srelease = ""; - if (release) srelease="Up"; - Log("Unhandled: %d, %s%s, %d, %d, %d, %v, %v", plr, GetPlayerControlName(ctrl), srelease, x,y,strength, repeat); return false; } @@ -108,9 +131,14 @@ global func ObjectControlUpdateComdir(int plr) var is_handled; var proc = GetProcedure(); if (proc == "WALK" || proc == "HANGLE" || proc == "PUSH" || proc == "PULL") - is_handled = (old_cx != new_cx) && !new_cy; // Only horizontal movement changed actual direction. Also, enfore clear Left/Right commands without leftover Up/Down + // Only horizontal movement changed actual direction + // Also, enfore clear Left/Right commands without leftover Up/Down + // CON_Down is never handled this way, thus forcing a CON_Stop + is_handled = (old_cx != new_cx) && !new_cy; else if (proc == "SCALE") - is_handled = (old_cy != new_cy) && !new_cx; // Only vertical movement changed actual direction. Also, enfore clear Up/Down to prevent "Zuppel" in corner + // Only vertical movement changed actual direction + // Also, enfore clear Up/Down to prevent "Zuppel" in corner + is_handled = (old_cy != new_cy) && !new_cx; else if (proc == "SWIM" || proc == "FLOAT" || proc == "DIG") is_handled = (old_cx != new_cx || old_cy != new_cy); // Free 360 degree movement else diff --git a/planet/System.c4g/PlayerControls.txt b/planet/System.c4g/PlayerControls.txt index 3bca3dfbf..bd0e15278 100644 --- a/planet/System.c4g/PlayerControls.txt +++ b/planet/System.c4g/PlayerControls.txt @@ -1,5 +1,8 @@ [ControlDefs] + [ControlDef] + Identifier=None + [ControlDef] Identifier=Left GUIName=Left @@ -36,6 +39,16 @@ Identifier=Dig GUIName=Dig GUIDesc=Delve through the earth + + [ControlDef] + Identifier=DolphinJump + GUIName=Dolphin Jump + GUIDesc=Jump out of the water while swimming + + [ControlDef] + Identifier=Context + GUIName=Context menu + GUIDesc=Open context menu of current interaction target @@ -70,19 +83,19 @@ TriggerMode=Hold [Assignment] - Key=A + Key=CON_Left Control=Right TriggerMode=Release | AlwaysUnhandled Priority=5 [Assignment] - Key=A + Key=CON_Left Control=Up TriggerMode=Release | AlwaysUnhandled Priority=-5 [Assignment] - Key=A + Key=CON_Left Control=Down TriggerMode=Release | AlwaysUnhandled Priority=-5 @@ -93,19 +106,19 @@ TriggerMode=Hold [Assignment] - Key=D + Key=CON_Right Control=Left TriggerMode=Release | AlwaysUnhandled Priority=5 [Assignment] - Key=D + Key=CON_Right Control=Up TriggerMode=Release | AlwaysUnhandled Priority=-5 [Assignment] - Key=D + Key=CON_Right Control=Down TriggerMode=Release | AlwaysUnhandled Priority=-5 @@ -116,55 +129,94 @@ TriggerMode=Hold [Assignment] - Key=S + Key=CON_Down Control=Up TriggerMode=Release | AlwaysUnhandled Priority=5 [Assignment] - Key=S + Key=CON_Down Control=Left - TriggerMode=Release | AlwaysUnhandled + TriggerMode=Release Priority=-5 [Assignment] - Key=S + Key=CON_Down Control=Right - TriggerMode=Release | AlwaysUnhandled + TriggerMode=Release Priority=-5 + [Assignment] + Key=CON_Down + Control=Down + TriggerMode=Release + Priority=-10 + [Assignment] Key=W Control=Up TriggerMode=Hold [Assignment] - Key=W + Key=CON_Up Control=Down TriggerMode=Release | AlwaysUnhandled Priority=5 [Assignment] - Key=W + Key=CON_Up Control=Left TriggerMode=Release | AlwaysUnhandled Priority=-5 [Assignment] - Key=W + Key=CON_Up Control=Right TriggerMode=Release | AlwaysUnhandled Priority=-5 + [Assignment] + Key=CON_Dig + Control=Down + TriggerMode=Release | AlwaysUnhandled + Priority=5 + + [Assignment] + Key=CON_Dig + Control=Left + TriggerMode=Release | AlwaysUnhandled + Priority=5 + + [Assignment] + Key=CON_Dig + Control=Right + TriggerMode=Release | AlwaysUnhandled + Priority=5 + + [Assignment] + Key=CON_Dig + Control=Up + TriggerMode=Release | AlwaysUnhandled + Priority=5 + + [ControlSet] Name=Keyboard2* [Assignment] Key=Q Control=Throw - Priority=50 [Assignment] Key=E Control=Dig - Priority=50 + + [Assignment] + Key=Up,Up + ComboIsSequence=1 + Control=DolphinJump + Priority=10 + + [Assignment] + Key=F + Control=Context From 63311823c66a0e490fa6d51b5e9c05b8518ba92a Mon Sep 17 00:00:00 2001 From: Sven Eberhardt Date: Mon, 10 Aug 2009 16:48:25 +0200 Subject: [PATCH 09/12] merge main branch into PlrCtrl branch (currently crashing) --- CMakeLists.txt | 1 + engine/engine.vc9.vcproj | 61 ----------------------------- engine/inc/C4Object.h | 2 +- engine/inc/C4PlayerControl.h | 2 +- engine/src/C4Application.cpp | 4 +- engine/src/C4Control.cpp | 2 +- engine/src/C4GameControlNetwork.cpp | 2 +- engine/src/C4Landscape.cpp | 2 +- engine/src/C4Object.cpp | 12 +++--- engine/src/C4ObjectMenu.cpp | 2 +- engine/src/C4PlayerControl.cpp | 21 +++++----- engine/src/C4Record.cpp | 4 +- engine/src/C4Script.cpp | 2 +- standard/src/StdGL.cpp | 2 +- 14 files changed, 30 insertions(+), 89 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cf5efe6fe..5071a58f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,6 +119,7 @@ add_executable(clonk WIN32 MACOSX_BUNDLE engine/src/C4Particles.cpp engine/src/C4PathFinder.cpp engine/src/C4Player.cpp + engine/src/C4PlayerControl.cpp engine/src/C4PlayerInfo.cpp engine/src/C4PlayerInfoConflicts.cpp engine/src/C4PlayerInfoListBox.cpp diff --git a/engine/engine.vc9.vcproj b/engine/engine.vc9.vcproj index eb9db2aa1..4212298e8 100644 --- a/engine/engine.vc9.vcproj +++ b/engine/engine.vc9.vcproj @@ -9760,67 +9760,6 @@ /> - - - - - - - - - - - - - - - - - 1; } diff --git a/engine/src/C4Application.cpp b/engine/src/C4Application.cpp index ecdd27308..c2c991191 100644 --- a/engine/src/C4Application.cpp +++ b/engine/src/C4Application.cpp @@ -561,7 +561,7 @@ C4ApplicationGameTimer::C4ApplicationGameTimer() void C4ApplicationGameTimer::SetGameTickDelay(uint32_t iDelay) { // Smaller than minimum refresh delay? - if (iDelay < Config.Graphics.MaxRefreshDelay) + if (iDelay < uint32_t(Config.Graphics.MaxRefreshDelay)) { // Set critical timer SetDelay(iDelay); @@ -584,7 +584,7 @@ bool C4ApplicationGameTimer::Execute(int iTimeout, pollfd *) { // Check timer and reset if (!CheckAndReset()) return true; - int Now = timeGetTime(); + unsigned int Now = timeGetTime(); // Execute if(Now >= iLastGameTick + iGameTickDelay || Game.GameGo) { diff --git a/engine/src/C4Control.cpp b/engine/src/C4Control.cpp index ba72f18c3..a5673bcb0 100644 --- a/engine/src/C4Control.cpp +++ b/engine/src/C4Control.cpp @@ -384,7 +384,7 @@ void C4ControlPlayerControl::Execute() const else { // player-based control: Execute on control owned by player - C4Player *pPlr=Game.Players.Get(iPlr); + C4Player *pPlr=::Players.Get(iPlr); if (pPlr) { pTargetCtrl = &(pPlr->Control); diff --git a/engine/src/C4GameControlNetwork.cpp b/engine/src/C4GameControlNetwork.cpp index ae4a96b4d..9649fe968 100644 --- a/engine/src/C4GameControlNetwork.cpp +++ b/engine/src/C4GameControlNetwork.cpp @@ -766,7 +766,7 @@ C4GameControlPacket *C4GameControlNetwork::PackCompleteCtrl(int32_t iTick) { // async mode: wait n extra frames for slow clients const int iMaxWait = (Config.Network.AsyncMaxWait * 1000) / iTargetFPS; - if(eMode != CNM_Async || iWaitStart == -1 || timeGetTime() <= iWaitStart + iMaxWait) + if(eMode != CNM_Async || iWaitStart == -1 || timeGetTime() <= uint32_t(iWaitStart + iMaxWait)) return false; } diff --git a/engine/src/C4Landscape.cpp b/engine/src/C4Landscape.cpp index 09c4aa098..b19c00219 100644 --- a/engine/src/C4Landscape.cpp +++ b/engine/src/C4Landscape.cpp @@ -2772,7 +2772,7 @@ bool C4Landscape::Mat2Pal() for (rgb=0; rgb<3; rgb++) Surface8->pPal->Colors[MatTex2PixCol(tex)*3+rgb] = Surface8->pPal->Colors[(MatTex2PixCol(tex)+IFT)*3+rgb] - = dwPix >> ((2-rgb) * 8); + = uint8_t(dwPix >> ((2-rgb) * 8)); // alpha Surface8->pPal->Alpha[MatTex2PixCol(tex)] = 0; Surface8->pPal->Alpha[MatTex2PixCol(tex)+IFT] = 0; diff --git a/engine/src/C4Object.cpp b/engine/src/C4Object.cpp index cdacffcf1..e7826a7ee 100644 --- a/engine/src/C4Object.cpp +++ b/engine/src/C4Object.cpp @@ -3526,7 +3526,7 @@ void C4Object::Resort() // Must not immediately resort - link change/removal would crash Game::ExecObjects } -BOOL C4Object::SetAction(C4PropList * Act, C4Object *pTarget, C4Object *pTarget2, int32_t iCalls, bool fForce) +bool C4Object::SetAction(C4PropList * Act, C4Object *pTarget, C4Object *pTarget2, int32_t iCalls, bool fForce) { C4PropList * LastAction = Action.pActionDef; int32_t iLastPhase=Action.Phase; @@ -3534,7 +3534,7 @@ BOOL C4Object::SetAction(C4PropList * Act, C4Object *pTarget, C4Object *pTarget2 if (LastAction) if (LastAction->GetPropertyInt(P_NoOtherAction) && !fForce) if (Act != LastAction) - return FALSE; + return false; // Stop previous act sound if (LastAction) if (Act != LastAction) @@ -3581,7 +3581,7 @@ BOOL C4Object::SetAction(C4PropList * Act, C4Object *pTarget, C4Object *pTarget2 C4Def *pOldDef = Def; Call(Action.pActionDef->GetPropertyStr(P_StartCall)->GetCStr()); // abort exeution if def changed - if (Def != pOldDef || !Status) return TRUE; + if (Def != pOldDef || !Status) return true; } } // Execute EndCall @@ -3593,7 +3593,7 @@ BOOL C4Object::SetAction(C4PropList * Act, C4Object *pTarget, C4Object *pTarget2 C4Def *pOldDef = Def; Call(LastAction->GetPropertyStr(P_EndCall)->GetCStr()); // abort exeution if def changed - if (Def != pOldDef || !Status) return TRUE; + if (Def != pOldDef || !Status) return true; } } // Execute AbortCall @@ -3605,10 +3605,10 @@ BOOL C4Object::SetAction(C4PropList * Act, C4Object *pTarget, C4Object *pTarget2 C4Def *pOldDef = Def; Call(LastAction->GetPropertyStr(P_AbortCall)->GetCStr(), &C4AulParSet(C4VInt(iLastPhase))); // abort exeution if def changed - if (Def != pOldDef || !Status) return TRUE; + if (Def != pOldDef || !Status) return true; } } - return TRUE; + return true; } void C4Object::UpdateActionFace() diff --git a/engine/src/C4ObjectMenu.cpp b/engine/src/C4ObjectMenu.cpp index 65dc3c5d5..3e8925c4a 100644 --- a/engine/src/C4ObjectMenu.cpp +++ b/engine/src/C4ObjectMenu.cpp @@ -453,7 +453,7 @@ void C4ObjectMenu::OnUserEnter(int32_t Player, int32_t iIndex, bool fRight) void C4ObjectMenu::OnUserClose() { // Queue 2do - Game.Input.Add(CID_PlrControl, new C4ControlPlayerControl(Game.MouseControl.GetPlayer(),Game.PlayerControlDefs.InternalCons.CON_MenuClose,0)); + Game.Input.Add(CID_PlrControl, new C4ControlPlayerControl(::MouseControl.GetPlayer(),Game.PlayerControlDefs.InternalCons.CON_MenuClose,0)); } bool C4ObjectMenu::IsReadOnly() diff --git a/engine/src/C4PlayerControl.cpp b/engine/src/C4PlayerControl.cpp index 74be2587b..daf7512b0 100644 --- a/engine/src/C4PlayerControl.cpp +++ b/engine/src/C4PlayerControl.cpp @@ -22,9 +22,10 @@ #ifndef BIG_C4INCLUDE #include #include +#include #include #include -#include +#include #endif /* C4PlayerControlDef */ @@ -117,7 +118,7 @@ void C4PlayerControlDefs::MergeFrom(const C4PlayerControlDefs &Src) const C4PlayerControlDef *C4PlayerControlDefs::GetControlByIndex(int32_t idx) const { // safe index - if (idx<0 || idx>=Defs.size()) return NULL; + if (idx<0 || idx>=int32_t(Defs.size())) return NULL; return &(Defs[idx]); } @@ -138,7 +139,7 @@ void C4PlayerControlDefs::FinalInit() const char *szIdtf = (*i).GetIdentifier(); if (szIdtf && *szIdtf && !SEqual(szIdtf, "None")) { - Game.ScriptEngine.RegisterGlobalConstant(FormatString("CON_%s", szIdtf).getData(), C4VInt(i-Defs.begin())); + ::ScriptEngine.RegisterGlobalConstant(FormatString("CON_%s", szIdtf).getData(), C4VInt(i-Defs.begin())); } } } @@ -528,14 +529,14 @@ bool C4PlayerControl::CSync::ControlDownState::operator ==(const ControlDownStat const C4PlayerControl::CSync::ControlDownState *C4PlayerControl::CSync::GetControlDownState(int32_t iControl) const { // safe access - if (iControl < 0 || iControl >= ControlDownStates.size()) return NULL; + if (iControl < 0 || iControl >= int32_t(ControlDownStates.size())) return NULL; return &ControlDownStates[iControl]; } int32_t C4PlayerControl::CSync::GetControlDisabled(int32_t iControl) const { // safe access - if (iControl < 0 || iControl >= ControlDisableStates.size()) return 0; + if (iControl < 0 || iControl >= int32_t(ControlDisableStates.size())) return 0; return ControlDisableStates[iControl]; } @@ -543,7 +544,7 @@ void C4PlayerControl::CSync::SetControlDownState(int32_t iControl, const C4KeyEv { // update state if (iControl < 0) return; - if (iControl >= ControlDownStates.size()) ControlDownStates.resize(iControl+1); + if (iControl >= int32_t(ControlDownStates.size())) ControlDownStates.resize(iControl+1); ControlDownState &rState = ControlDownStates[iControl]; rState.DownState = rDownState; rState.iDownFrame = iDownFrame; @@ -554,7 +555,7 @@ void C4PlayerControl::CSync::SetControlDisabled(int32_t iControl, int32_t iVal) { // disable control if (iControl < 0) return; - if (iControl >= ControlDisableStates.size()) ControlDisableStates.resize(iControl+1); + if (iControl >= int32_t(ControlDisableStates.size())) ControlDisableStates.resize(iControl+1); ControlDisableStates[iControl] = iVal; // if a control is disabled, its down-state is reset silently const ControlDownState *pDownState = GetControlDownState(iControl); @@ -749,7 +750,7 @@ bool C4PlayerControl::ExecuteControlAction(int32_t iControl, C4PlayerControlDef: C4Player *pPlr = NULL; if (iPlr > -1) { - pPlr = Game.Players.Get(iPlr); + pPlr = ::Players.Get(iPlr); if (!pPlr) return false; } // exec action (on player) @@ -775,7 +776,7 @@ bool C4PlayerControl::ExecuteControlAction(int32_t iControl, C4PlayerControlDef: bool C4PlayerControl::ExecuteControlScript(int32_t iControl, C4ID idControlExtraData, bool fUp, const C4KeyEventData &rKeyExtraData, bool fRepeated) { - C4Player *pPlr = Game.Players.Get(iPlr); + C4Player *pPlr = ::Players.Get(iPlr); if (pPlr) { // Not for eliminated (checked again in DirectCom, but make sure no control is generated for eliminated players!) @@ -789,7 +790,7 @@ bool C4PlayerControl::ExecuteControlScript(int32_t iControl, C4ID idControlExtra return false; } // control down - C4AulFunc *pFunc = Game.ScriptEngine.GetFirstFunc(PSF_PlayerControl); + C4AulFunc *pFunc = ::ScriptEngine.GetFirstFunc(PSF_PlayerControl); 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); diff --git a/engine/src/C4Record.cpp b/engine/src/C4Record.cpp index 250f3f82d..52a594152 100644 --- a/engine/src/C4Record.cpp +++ b/engine/src/C4Record.cpp @@ -255,7 +255,7 @@ bool C4Record::Rec(C4PacketType eCtrlType, C4ControlPacket *pCtrl, int iFrame) bool C4Record::Rec(int iFrame, const StdBuf &sBuf, C4RecordChunkType eType) { // filler chunks (this should never be necessary, though) - while(iFrame > iLastFrame + 0xff) + while(iFrame > int(iLastFrame + 0xff)) Rec(iLastFrame + 0xff, StdBuf(), RCT_Frame); // get frame difference uint8_t iFrameDiff = Max(0, iFrame - iLastFrame); @@ -990,7 +990,7 @@ StdStrBuf GetDbgRecPktData(C4RecordChunkType eType, const StdBuf & RawData) case RCT_AulFunc: r.Ref(reinterpret_cast(RawData.getData()), RawData.getSize()-1); break; default: - for (int i=0; iControl); diff --git a/standard/src/StdGL.cpp b/standard/src/StdGL.cpp index 85ce559bd..22c02b481 100644 --- a/standard/src/StdGL.cpp +++ b/standard/src/StdGL.cpp @@ -385,7 +385,7 @@ void CStdGL::BlitLandscape(SURFACE sfcSource, float fx, float fy, glActiveTexture(GL_TEXTURE0); } DWORD dwModMask = 0; - SetupTextureEnv(false, mattextures); + SetupTextureEnv(false, !!mattextures); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glMatrixMode(GL_TEXTURE); From 4b6f1ec264cb6fbee10e6bb90b935137c1a468a6 Mon Sep 17 00:00:00 2001 From: Sven Eberhardt Date: Tue, 11 Aug 2009 23:12:36 +0200 Subject: [PATCH 10/12] crash fix --- engine/inc/C4Sector.h | 2 ++ engine/src/C4AulLink.cpp | 10 +++++++++- engine/src/C4GameObjects.cpp | 1 + engine/src/C4Sector.cpp | 16 ++++++++++++++-- planet/System.c4g/C4.c | 3 +-- 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/engine/inc/C4Sector.h b/engine/inc/C4Sector.h index a648d6d5e..0d04e04f4 100644 --- a/engine/inc/C4Sector.h +++ b/engine/inc/C4Sector.h @@ -51,6 +51,7 @@ class C4LSector C4ObjectList ObjectShapes; // objects with shapes that overlap this sector void CompileFunc(StdCompiler *pComp); + void ClearObjects(); // remove all objects from object lists friend class C4LSectors; }; @@ -73,6 +74,7 @@ class C4LSectors void Add(C4Object *pObj, C4ObjectList *pMainList); void Update(C4Object *pObj, C4ObjectList *pMainList); // does not update object order! void Remove(C4Object *pObj); + void ClearObjects(); // remove all objects from object lists void AssertObjectNotInList(C4Object *pObj); // searches all sector lists for object, and assert if it's inside a list diff --git a/engine/src/C4AulLink.cpp b/engine/src/C4AulLink.cpp index 141dbfcaf..ec3aa1bcd 100644 --- a/engine/src/C4AulLink.cpp +++ b/engine/src/C4AulLink.cpp @@ -275,7 +275,15 @@ void C4AulScriptEngine::Link(C4DefList *rDefs) Action->SetProperty(Strings.P[P_Directions], C4VInt(1)); Action->SetProperty(Strings.P[P_Step], C4VInt(1)); Action->SetProperty(Strings.P[P_Procedure], C4VInt(DFA_NONE)); - GlobalNamed.GetItem("Action")->SetPropList(Action); + C4Value *pActionValDef = GlobalNamed.GetItem("Action"); + if (!pActionValDef) + { + Log("WARNING: static Action not defined. Wrong System.c4g?"); + } + else + { + pActionValDef->SetPropList(Action); + } rDefs->CallEveryDefinition(); // display state diff --git a/engine/src/C4GameObjects.cpp b/engine/src/C4GameObjects.cpp index e703096f3..dbddc09d8 100644 --- a/engine/src/C4GameObjects.cpp +++ b/engine/src/C4GameObjects.cpp @@ -353,6 +353,7 @@ void C4GameObjects::PutSolidMasks() void C4GameObjects::DeleteObjects(bool fDeleteInactive) { + Sectors.ClearObjects(); C4ObjectList::DeleteObjects(); BackObjects.Clear(); ForeObjects.Clear(); diff --git a/engine/src/C4Sector.cpp b/engine/src/C4Sector.cpp index 83f535454..b1b678680 100644 --- a/engine/src/C4Sector.cpp +++ b/engine/src/C4Sector.cpp @@ -43,8 +43,7 @@ void C4LSector::Init(int ix, int iy) void C4LSector::Clear() { // clear objects - Objects.Clear(); - ObjectShapes.Clear(); + ClearObjects(); } void C4LSector::CompileFunc(StdCompiler *pComp) @@ -55,6 +54,13 @@ void C4LSector::CompileFunc(StdCompiler *pComp) pComp->Value(mkNamingAdapt(ObjectShapes, "ObjectShapes")); } +void C4LSector::ClearObjects() + { + // clear objects + Objects.Clear(); + ObjectShapes.Clear(); + } + /* sector map */ void C4LSectors::Init(int iWdt, int iHgt) @@ -226,6 +232,12 @@ bool C4LSectors::CheckSort() return true; } +void C4LSectors::ClearObjects() + { + for (int cnt=0; cnt Date: Wed, 12 Aug 2009 16:45:45 +0200 Subject: [PATCH 11/12] move C4PlayerControl.cpp/.h --- {engine/src => src/control}/C4PlayerControl.cpp | 0 {engine/inc => src/control}/C4PlayerControl.h | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {engine/src => src/control}/C4PlayerControl.cpp (100%) rename {engine/inc => src/control}/C4PlayerControl.h (100%) diff --git a/engine/src/C4PlayerControl.cpp b/src/control/C4PlayerControl.cpp similarity index 100% rename from engine/src/C4PlayerControl.cpp rename to src/control/C4PlayerControl.cpp diff --git a/engine/inc/C4PlayerControl.h b/src/control/C4PlayerControl.h similarity index 100% rename from engine/inc/C4PlayerControl.h rename to src/control/C4PlayerControl.h From 826621b5553d2adacf852f54f262ff4ce9b12823 Mon Sep 17 00:00:00 2001 From: Sven Eberhardt Date: Wed, 26 Aug 2009 14:41:29 +0200 Subject: [PATCH 12/12] minor fixes --- src/game/object/C4Sector.cpp | 5 ++++- src/game/player/C4Player.cpp | 4 ++-- src/gui/C4FileSelDlg.cpp | 2 ++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/game/object/C4Sector.cpp b/src/game/object/C4Sector.cpp index 8128e70d4..c12934477 100644 --- a/src/game/object/C4Sector.cpp +++ b/src/game/object/C4Sector.cpp @@ -234,7 +234,10 @@ bool C4LSectors::CheckSort() void C4LSectors::ClearObjects() { - for (int cnt=0; cntValue(mkNamingAdapt(bForceFogOfWar, "ForceFogOfWar", false)); pComp->Value(mkNamingAdapt(ShowStartup, "ShowStartup", false)); - pComp->Value(mkNamingAdapt(ShowControl, "ShowControl", false)); - pComp->Value(mkNamingAdapt(ShowControlPos, "ShowControlPos", false)); + pComp->Value(mkNamingAdapt(ShowControl, "ShowControl", 0)); + pComp->Value(mkNamingAdapt(ShowControlPos, "ShowControlPos", 0)); pComp->Value(mkNamingAdapt(Wealth, "Wealth", 0)); pComp->Value(mkNamingAdapt(Points, "Points", 0)); pComp->Value(mkNamingAdapt(Value, "Value", 0)); diff --git a/src/gui/C4FileSelDlg.cpp b/src/gui/C4FileSelDlg.cpp index 01e97e13d..1a6c5da18 100644 --- a/src/gui/C4FileSelDlg.cpp +++ b/src/gui/C4FileSelDlg.cpp @@ -28,7 +28,9 @@ #endif #ifdef _WIN32 +#ifndef _WIN32_IE #define _WIN32_IE 0x0400 +#endif #include #ifndef CSIDL_MYPICTURES #define CSIDL_MYPICTURES 0x0027