//----------------------------------------------------------------------------- // Copyright (c) 2012 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- #ifndef _ACTIONMAP_H_ #define _ACTIONMAP_H_ #ifndef _PLATFORM_H_ #include "platform/platform.h" #endif #ifndef _TVECTOR_H_ #include "core/util/tVector.h" #endif #ifndef _SIMBASE_H_ #include "console/simBase.h" #endif #ifndef _ITICKABLE_H_ #include "core/iTickable.h" #endif class ContextAction; struct InputEventInfo; struct EventDescriptor { U8 flags; ///< Combination of any modifier flags. U8 eventType; ///< SI_KEY, etc. U16 eventCode; ///< From event.h }; /// Map raw inputs to a variety of actions. This is used for all keymapping /// in the engine. /// @see ActionMap::Node class ActionMap : public SimObject { typedef SimObject Parent; friend class ContextAction; protected: bool onAdd(); struct Node { U32 modifiers; U32 action; enum Flags { Ranged = BIT(0), ///< Ranged input. HasScale = BIT(1), ///< Scaled input. HasDeadZone = BIT(2), ///< Dead zone is present. Inverted = BIT(3), ///< Input is inverted. NonLinear = BIT(4), ///< Input should be re-fit to a non-linear scale BindCmd = BIT(5), ///< Bind a console command to this. Held = BIT(6), DoubleTap = BIT(7) }; U32 flags; ///< @see Node::Flags F32 deadZoneBegin; F32 deadZoneEnd; F32 scaleFactor; SimObject* object; ///< Object to call consoleFunction on. StringTableEntry consoleFunction; ///< Console function to call with new values. char *makeConsoleCommand; ///< Console command to execute when we make this command. char *breakConsoleCommand; ///< Console command to execute when we break this command. ContextAction* contextEvent; ///< Event that kicks off via context-keybind actions such as holding or double-tapping }; /// Used to represent a devices. struct DeviceMap { U32 deviceType; U32 deviceInst; Vector nodeMap; DeviceMap():deviceType(0), deviceInst(0){ VECTOR_SET_ASSOCIATION(nodeMap); } ~DeviceMap(); }; struct BreakEntry { U32 deviceType; U32 deviceInst; U32 objInst; SimObject* object; StringTableEntry consoleFunction; char *breakConsoleCommand; // It's possible that the node could be deleted (unlikely, but possible, // so we replicate the node flags here... // U32 flags; F32 deadZoneBegin; F32 deadZoneEnd; F32 scaleFactor; }; Vector mDeviceMaps; static Vector smBreakTable; // Find: return NULL if not found in current map, Get: create if not // found. const Node* findNode(const U32 inDeviceType, const U32 inDeviceInst, const U32 inModifiers, const U32 inAction); bool findBoundNode( const char* function, U32 &devMapIndex, U32 &nodeIndex ); bool nextBoundNode( const char* function, U32 &devMapIndex, U32 &nodeIndex ); Node* getNode(const U32 inDeviceType, const U32 inDeviceInst, const U32 inModifiers, const U32 inAction, SimObject* object = NULL); void removeNode(const U32 inDeviceType, const U32 inDeviceInst, const U32 inModifiers, const U32 inAction, SimObject* object = NULL); void enterBreakEvent(const InputEventInfo* pEvent, const Node* pNode); static const char* getModifierString(const U32 modifiers); /// Pass index to a break entry, and this function will fire it off. static void fireBreakEvent(U32 idx, F32 value = 0.f); public: ActionMap(); ~ActionMap(); void dumpActionMap(const char* fileName, const bool append) const; static bool createEventDescriptor(const char* pEventString, EventDescriptor* pDescriptor); bool processBind(const U32 argc, const char** argv, SimObject* object = NULL); bool processBindCmd(const char *device, const char *action, const char *makeCmd, const char *breakCmd); bool processUnbind(const char *device, const char *action, SimObject* object = NULL); bool processHoldBind(const char *device, const char *action, const char *holdFunc, const char *tapFunc, const U32 holdTime, const bool holdOnly, const bool returnHoldTime = false); /// @name Console Interface Functions /// @{ const char* getBinding ( const char* command ); ///< Find what the given command is bound to. const char* getCommand ( const char* device, const char* action ); ///< Find what command is bound to the given event descriptor . bool isInverted ( const char* device, const char* action ); F32 getScale ( const char* device, const char* action ); const char* getDeadZone( const char* device, const char* action ); /// @} static bool getKeyString(const U32 action, char* buffer); static bool getDeviceName(const U32 deviceType, const U32 deviceInstance, char* buffer); static const char* buildActionString( const InputEventInfo* event ); bool processAction(const InputEventInfo*); /// Return true if the given event triggers is bound to an action in this map. bool isAction( U32 deviceType, U32 deviceInst, U32 modifiers, U32 action ); /// Returns the global ActionMap. static ActionMap* getGlobalMap(); static bool checkBreakTable(const InputEventInfo*); static bool handleEvent(const InputEventInfo*); static bool handleEventGlobal(const InputEventInfo*); /// Called when we lose focus, to make sure we have no dangling inputs. /// /// This fires a break event for every currently pending item in the break /// table. static void clearAllBreaks(); /// Returns true if the specified key + modifiers are bound to something /// on the global action map. static bool checkAsciiGlobal(U16 key, U32 modifiers); static bool getDeviceTypeAndInstance(const char *device, U32 &deviceType, U32 &deviceInstance); DECLARE_CONOBJECT(ActionMap); }; class ContextAction : public ITickable { ActionMap::Node* mButton; ///< our button we're holding F32 mMinHoldTime; ///< minimum time to qualify as 'held'. If we hold less than this, ///< it's a 'press', otherwise it's a 'held' public: F32 mStartTime; ///< Our timestamp when we first pressed. F32 mEventValue; ///< Event value from our key event. StringTableEntry mConsoleFunctionHeld; ///< Console function to call with new values if we held over ///< a certain time. bool mHoldOnly; ///< does this only care if we're holding? ///< true means that it only fires a function while holding ///< false time-contexts it bool mBreakEvent; ///< Button is no longer being pressed! bool mDidHold; ///< did we, at some point in the process, hold the button? bool mActive; ///< do we be tickin? bool mReturnHoldTime; ///< Do we return back our time held? ContextAction(StringTableEntry func, F32 minHoldTime, ActionMap::Node* button, bool holdOnly); virtual void processTick(); virtual void interpolateTick(F32 delta) {} virtual void advanceTime(F32 timeDelta) {} }; #endif // _ACTIONMAP_H_