diff --git a/src/common/engine/m_haptics.cpp b/src/common/engine/m_haptics.cpp index 52b65e3c5..e078bdca1 100644 --- a/src/common/engine/m_haptics.cpp +++ b/src/common/engine/m_haptics.cpp @@ -55,6 +55,17 @@ // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- +inline void _Rumble(const int); +inline void _RumbleDirect(int, int, double, double, double, double); + +const FName * Joy_GetMapping(const FName); +const FName * Joy_GuessMapping(const FName); +const struct Haptics * Joy_GetRumble(FName); +void Joy_Rumble(const FName, const struct Haptics, double); + +void RumbleDump(); +void RumblePrint(const FName, const FName *, const struct Haptics *, double); + // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern bool AppActive; @@ -88,7 +99,8 @@ const FName HapticIntense = "INTENSE", HapticMedium = "MEDIUM", HapticLight = "LIGHT", HapticSubtle = "SUBTLE", - HapticNone = "NONE"; + HapticNone = "NONE", + HapticDirect = "DIRECT"; // except for this one // PUBLIC DATA DEFINITIONS ------------------------------------------------- @@ -206,7 +218,8 @@ const FName * Joy_GetMapping(const FName identifier) auto id = soundEngine->FindSoundTentative(identifier.GetChars()); // loop a couple layers deep, trying to find a candidate - for (auto i = 0; i < MAX_TRY_DEPTH; i++) { + for (auto i = 0; i < MAX_TRY_DEPTH; i++) + { if (!id.isvalid()) break; actual = soundEngine->GetSoundName(id); @@ -243,7 +256,8 @@ const FName * Joy_GetMapping(const FName identifier) bool replaced = actual != identifier && actual.IsValidName(); - auto warn = [&identifier]() { + auto warn = [&identifier]() + { if (RumbleMissed.Contains(identifier)) return; RumbleMissed.Push(identifier); Printf(DMSG_WARNING|PRINT_NONOTIFY, "Unknown rumble mapping '%s'\n", identifier.GetChars()); @@ -293,7 +307,8 @@ const struct Haptics * Joy_GetRumble(FName identifier) { struct Haptics * rumble = RumbleDefinition.CheckKey(identifier); - if (!rumble && !RumbleMissed.Contains(identifier)) { + if (!rumble && !RumbleMissed.Contains(identifier)) + { RumbleMissed.Push(identifier); Printf(DMSG_ERROR, TEXTCOLOR_RED "Rumble mapping not found! '%s'\n", identifier.GetChars()); return nullptr; @@ -312,6 +327,11 @@ const struct Haptics * Joy_GetRumble(FName identifier) void Joy_AddRumbleType(const FName identifier, const struct Haptics data) { + if (haptics_debug) + { + if (data.ticks == 0) Printf("rumble disabled %s\n", identifier.GetChars()); + else Printf("rumble add %s T%d H%.1g L%.1g\n", identifier.GetChars(), data.ticks, data.high_frequency, data.low_frequency); + } RumbleDefinition.Insert(identifier, data); } @@ -326,6 +346,7 @@ void Joy_AddRumbleType(const FName identifier, const struct Haptics data) void Joy_AddRumbleAlias(const FName alias, const FName actual) { + if (haptics_debug) Printf("rumble alias %s -> %s\n", alias.GetChars(), actual.GetChars()); RumbleAlias.Insert(alias, actual); } @@ -339,6 +360,7 @@ void Joy_AddRumbleAlias(const FName alias, const FName actual) void Joy_MapRumbleType(const FName sound, const FName identifier) { + if (haptics_debug) Printf("rumble map %s -> %s\n", sound.GetChars(), identifier.GetChars()); RumbleMapping.Insert(sound, identifier); } @@ -352,6 +374,7 @@ void Joy_MapRumbleType(const FName sound, const FName identifier) void Joy_ResetRumbleMapping() { + if (haptics_debug) Printf("rumble reset\n"); RumbleMapping.Clear(); RumbleAlias.Clear(); RumbleMissed.Clear(); @@ -373,6 +396,7 @@ void Joy_ResetRumbleMapping() void Joy_ReadyRumbleMapping() { + if (haptics_debug) Printf("rumble ready\n"); TArray found; TMapIterator it(RumbleAlias); TMap::Pair* pair; @@ -405,11 +429,13 @@ void Joy_ReadyRumbleMapping() break; } - while (found.size() > 0) { + while (found.size() > 0) + { RumbleAlias.Remove(found.Last()); found.Pop(); } } + if (haptics_debug) RumbleDump(); } //========================================================================== @@ -420,7 +446,8 @@ void Joy_ReadyRumbleMapping() // //========================================================================== -void Joy_RumbleTick() { +void Joy_RumbleTick() +{ if (!Haptics.enabled) return; // pause detection @@ -507,14 +534,6 @@ void Joy_RumbleTick() { Haptics.dirty = false; } -//========================================================================== -// -// Joy_Rumble -// -// Requests haptic feedback. Attenuation of >=1 results in no-op. -// -//========================================================================== - void Joy_Rumble(const FName source, const struct Haptics data, double attenuation) { if (!Haptics.enabled) return; @@ -523,9 +542,6 @@ void Joy_Rumble(const FName source, const struct Haptics data, double attenuatio float strength = 1 - (attenuation < 0? 0: attenuation); - if (haptics_debug) - Printf("r %s * %g\n", source.GetChars(), strength); - // this will overwrite stuff from same source mapping (weapons/pistol not W_BULLET) Haptics.channels.Insert(source, { Haptics.tic + data.ticks + 1, @@ -538,6 +554,31 @@ void Joy_Rumble(const FName source, const struct Haptics data, double attenuatio Haptics.dirty = true; } +//========================================================================== +// +// Joy_Rumble +// +// Requests haptic feedback based on provided data. Attenuation of >=1 results in no-op. +// +//========================================================================== + +void Joy_Rumble( + const FName source, + int tic_count, + double high_frequency, + double low_frequency, + double left_trigger, + double right_trigger, + double attenuation +) +{ + const struct Haptics rumble {tic_count, high_frequency, low_frequency, left_trigger, right_trigger}; + + if (haptics_debug) RumblePrint(source, &HapticDirect, &rumble, attenuation); + + Joy_Rumble(source, rumble, attenuation); +} + //========================================================================== // // Joy_Rumble @@ -548,15 +589,16 @@ void Joy_Rumble(const FName source, const struct Haptics data, double attenuatio void Joy_Rumble(const FName identifier, double attenuation) { - const FName * mapping = Joy_GetMapping(identifier); + const FName * mapping; + const struct Haptics * rumble; - if (mapping == nullptr) return; + rumble = (nullptr != (mapping = Joy_GetMapping(identifier))) + ? Joy_GetRumble(*mapping) + : nullptr; - const struct Haptics * rumble = Joy_GetRumble(*mapping); + if (haptics_debug) RumblePrint(identifier, mapping, rumble, attenuation); - if (rumble == nullptr) return; - - Joy_Rumble(identifier, * rumble, attenuation); + if (rumble) Joy_Rumble(identifier, * rumble, attenuation); } //========================================================================== @@ -573,69 +615,12 @@ CCMD (rumble) int ticks; double high_freq, low_freq, left_trig, right_trig; - switch (count) { + switch (count) + { case 0: { - TArray unused, used; - - { - TMapIterator it(RumbleDefinition); - TMap::Pair* pair; - while (it.NextPair(pair)) unused.AddUnique(pair->Key.GetChars()); - } - { - TMapIterator it(RumbleAlias); - TMap::Pair* pair; - while (it.NextPair(pair)) unused.AddUnique(pair->Key.GetChars()); - } - { - TMapIterator it(RumbleAlias); - TMap::Pair* pair; - while (it.NextPair(pair)) - { - if (unused.Contains(pair->Value.GetChars())) - unused.Delete(unused.Find(pair->Value.GetChars())); - } - } - { - TMapIterator it(RumbleMapping); - TMap::Pair* pair; - Printf("Mappings:\n"); - while (it.NextPair(pair)) { - used.AddUnique(pair->Value.GetChars()); - if (unused.Contains(pair->Value.GetChars())) - unused.Delete(unused.Find(pair->Value.GetChars())); - auto mapping = Joy_GetRumble(pair->Value); - FString key = pair->Key.GetChars(); - FString val = pair->Value.GetChars(); - key.ToLower(); - val.ToUpper(); - FString a = FStringf("'%s'\t->\t'%s'", key.GetChars(), val.GetChars()); - FString b = mapping - ? FStringf( - "{ %d %g %g %g %g }", - mapping->ticks, - mapping->high_frequency, - mapping->low_frequency, - mapping->left_trigger, - mapping->right_trigger - ) : "[undefined]"; - Printf("\t%s\t->\t%s\n", a.GetChars(), b.GetChars()); - } - } - - if (unused.Size() > 0) - { - Printf("Unused:\n"); - for (auto i:unused) - { - FString s = i.GetChars(); - s.ToUpper(); - Printf("\t'%s'\n", s.GetChars()); - } - } - + RumbleDump(); Printf("Testing rumble for 5s\n"); - Joy_Rumble("", {5 * TICRATE, 1.0, 1.0, 1.0, 1.0}); + Joy_Rumble("", 5 * TICRATE, 1.0, 1.0, 1.0, 1.0); } break; case 1: @@ -643,18 +628,20 @@ CCMD (rumble) Joy_Rumble(argv[1]); break; case 5: - try { + try + { ticks = std::stoi(argv[1], nullptr, 10); high_freq = static_cast (std::stof(argv[2], nullptr)); low_freq = static_cast (std::stof(argv[3], nullptr)); left_trig = static_cast (std::stof(argv[4], nullptr)); right_trig = static_cast (std::stof(argv[5], nullptr)); - } catch (...) { + } catch (...) + { Printf("Failed to parse args\n"); return; } Printf("testing rumble with params (%d, %f, %f, %f, %f)\n", ticks, high_freq, low_freq, left_trig, right_trig); - Joy_Rumble("", {ticks, high_freq, low_freq, left_trig, right_trig}); + Joy_Rumble("", ticks, high_freq, low_freq, left_trig, right_trig); break; default: Printf( @@ -675,7 +662,8 @@ CCMD (rumble) // //========================================================================== -void _Rumble(const int identifier) { +inline void _Rumble(const int identifier) +{ Joy_Rumble(ENamedName(identifier)); } @@ -703,8 +691,23 @@ DEFINE_ACTION_FUNCTION_NATIVE(DHaptics, Rumble, _Rumble) // //========================================================================== -void _RumbleDirect(int source, int tic_count, double high_frequency, double low_frequency, double left_trigger, double right_trigger) { - Joy_Rumble(ENamedName(source), {tic_count, high_frequency, low_frequency, left_trigger, right_trigger}); +inline void _RumbleDirect( + int source, + int tic_count, + double high_frequency, + double low_frequency, + double left_trigger, + double right_trigger +) +{ + Joy_Rumble( + ENamedName(source), + tic_count, + high_frequency, + low_frequency, + left_trigger, + right_trigger + ); } //========================================================================== @@ -727,3 +730,103 @@ DEFINE_ACTION_FUNCTION_NATIVE(DHaptics, RumbleDirect, _RumbleDirect) _RumbleDirect(source, tic_count, high_frequency, low_frequency, left_trigger, right_trigger); return 0; } + + +//========================================================================== +// +// RumblePrint +// +// Debug printout +// +//========================================================================== + +void RumblePrint(const FName identifier, const FName * mapping, const struct Haptics * rumble, double attenuation) +{ + const char * color; + FString text = FStringf("r '%s'", identifier.GetChars()); + if (!mapping) + { color = TEXTCOLOR_ORANGE; text.AppendFormat(" -"); } + else + { + text.AppendFormat(" %s", mapping->GetChars()); + if (!rumble) + { color = TEXTCOLOR_RED; text.AppendFormat(" -"); } + else if (rumble->ticks == 0) + { color = TEXTCOLOR_BLACK; text.AppendFormat(" 0"); } + else + { color = TEXTCOLOR_CYAN; text.AppendFormat(" T%d H%.1g L%.1g A%.1g", + rumble->ticks, rumble->high_frequency, rumble->low_frequency, attenuation); } + } + Printf("%s%s\n", color, text.GetChars()); +} + +//========================================================================== +// +// RumbleDump +// +// Debug printout +// +//========================================================================== + +void RumbleDump() +{ + TArray unused, used; + + { + TMapIterator it(RumbleDefinition); + TMap::Pair* pair; + while (it.NextPair(pair)) unused.AddUnique(pair->Key.GetChars()); + } + { + TMapIterator it(RumbleAlias); + TMap::Pair* pair; + while (it.NextPair(pair)) unused.AddUnique(pair->Key.GetChars()); + } + { + TMapIterator it(RumbleAlias); + TMap::Pair* pair; + while (it.NextPair(pair)) + { + if (unused.Contains(pair->Value.GetChars())) + unused.Delete(unused.Find(pair->Value.GetChars())); + } + } + { + TMapIterator it(RumbleMapping); + TMap::Pair* pair; + Printf("Mappings:\n"); + while (it.NextPair(pair)) + { + used.AddUnique(pair->Value.GetChars()); + if (unused.Contains(pair->Value.GetChars())) + unused.Delete(unused.Find(pair->Value.GetChars())); + auto mapping = Joy_GetRumble(pair->Value); + FString key = pair->Key.GetChars(); + FString val = pair->Value.GetChars(); + key.ToLower(); + val.ToUpper(); + FString a = FStringf("'%s'\t->\t'%s'", key.GetChars(), val.GetChars()); + FString b = mapping + ? FStringf( + "{ %d %g %g %g %g }", + mapping->ticks, + mapping->high_frequency, + mapping->low_frequency, + mapping->left_trigger, + mapping->right_trigger + ) : "[undefined]"; + Printf("\t%s\t->\t%s\n", a.GetChars(), b.GetChars()); + } + } + + if (unused.Size() > 0) + { + Printf("Unused:\n"); + for (auto i:unused) + { + FString s = i.GetChars(); + s.ToUpper(); + Printf("\t'%s'\n", s.GetChars()); + } + } +} diff --git a/src/common/engine/m_haptics.h b/src/common/engine/m_haptics.h index cb1e6294c..6a27dfb33 100644 --- a/src/common/engine/m_haptics.h +++ b/src/common/engine/m_haptics.h @@ -28,7 +28,12 @@ void Joy_MapRumbleType(const FName sound, const FName idenifier); void Joy_ResetRumbleMapping(); void Joy_ReadyRumbleMapping(); void Joy_RumbleTick(); -void Joy_Rumble(const FName source, const struct Haptics data, double attenuation = 0); +void Joy_Rumble( + const FName source, int tic_count, + double high_frequency, double low_frequency, + double left_trigger = 0, double right_trigger = 0, + double attenuation = 0 +); void Joy_Rumble(const FName identifier, double attenuation = 0); #endif