991 lines
26 KiB
C++
991 lines
26 KiB
C++
//
|
|
// system/MappingManager.cpp
|
|
//
|
|
// Copyright (C) 1999-2014 Rockstar Games. All Rights Reserved.
|
|
//
|
|
|
|
#include "MappingManager.h"
|
|
|
|
#if ENABLE_INPUT_COMBOS
|
|
|
|
// Rage Headers.
|
|
#include "math/amath.h"
|
|
#include "parser/macros.h"
|
|
#include "parsercore/settings.h"
|
|
|
|
extern ::rage::parEnumData parser_rage__InputType_Data;
|
|
extern ::rage::parEnumData parser_rage__ControlInput__Trigger_Data;
|
|
|
|
namespace rage
|
|
{
|
|
RAGE_DEFINE_CHANNEL(MappingManager)
|
|
|
|
|
|
void CMappingManager::LoadMappingFile(const char* path)
|
|
{
|
|
RemoveMappingFile(path);
|
|
|
|
// Find the first available index in the mappings!
|
|
ComboGroup* group = NULL;
|
|
for(u32 i = 0; i < m_MappingGroups.size(); ++i)
|
|
{
|
|
if(m_MappingGroups[i].GetId() == INVALID_GROUP_ID)
|
|
{
|
|
group = &m_MappingGroups[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(group == NULL)
|
|
{
|
|
group = &m_MappingGroups.Grow();
|
|
}
|
|
|
|
parSettings settings;
|
|
settings.SetFlag(parSettings::READ_SAFE_BUT_SLOW, true);
|
|
|
|
INIT_PARSER;
|
|
ControlInput::MappingData mappingData;
|
|
if(mapmgrVerifyf(PARSER.LoadObject(path, "", mappingData, &settings), "Failed to mapping file '%s'!", path))
|
|
{
|
|
group->Load(GetPathHash(path), mappingData);
|
|
}
|
|
SHUTDOWN_PARSER;
|
|
|
|
BANK_ONLY(RebuildWidgets());
|
|
}
|
|
|
|
void CMappingManager::RemoveMappingFile(const char* path)
|
|
{
|
|
const atHashWithStringNotFinal fileId = GetPathHash(path);
|
|
for(u32 i = 0; i < m_MappingGroups.size(); ++i)
|
|
{
|
|
if(m_MappingGroups[i].GetId() == fileId)
|
|
{
|
|
m_MappingGroups[i].Unload();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CMappingManager::Update(u32 timeMS)
|
|
{
|
|
++m_UpdateId;
|
|
|
|
for(u32 i = 0; i < MAX_INPUTS; ++i)
|
|
{
|
|
m_Inputs[i].Update(m_UpdateId, timeMS);
|
|
}
|
|
|
|
for(u32 i = 0; i < m_MappingGroups.size(); ++i)
|
|
{
|
|
ComboGroup& group = m_MappingGroups[i];
|
|
if(group.GetId().GetHash() != INVALID_GROUP_ID)
|
|
{
|
|
const u32 winningCombo = group.Update();
|
|
if(winningCombo != ComboGroup::INVALID_COMBO_MAPPING_ID)
|
|
{
|
|
const InputType input = group.GetComboInput(winningCombo);
|
|
if(mapmgrVerifyf(input != UNDEFINED_INPUT, "Unknown input (%d)!", input))
|
|
{
|
|
mapmgrDebugf1( "%s fired in combo group %u from combo %u from '%s'.",
|
|
GetInputName(input),
|
|
i,
|
|
winningCombo,
|
|
group.GetId().TryGetCStr() );
|
|
m_Inputs[input].SetCurrentValue(ioValue::MAX_AXIS, group.GetComboSource(winningCombo));
|
|
}
|
|
group.Restart();
|
|
}
|
|
}
|
|
}
|
|
|
|
BANK_ONLY(UpdateDebugDisplay());
|
|
}
|
|
|
|
|
|
rage::ioSource CMappingManager::ConvertSource(const ControlInput::InputSource& inputSource)
|
|
{
|
|
ioSource source;
|
|
|
|
// TR: TODO: Use correct index!
|
|
source.m_DeviceIndex = 0;
|
|
source.m_Device = inputSource.m_Type;
|
|
|
|
// TR: TODO: Convert parameters.
|
|
source.m_Parameter = ioMapper::ConvertParameterToDeviceValue(inputSource.m_Type, inputSource.m_Parameters[0]);
|
|
|
|
return source;
|
|
}
|
|
|
|
atHashWithStringNotFinal CMappingManager::GetPathHash(const char* path)
|
|
{
|
|
// As we hash the path as the key, we need to ensure our hash matches the parser otherwise we get an assert about a hash collision
|
|
// if are slashes do not match what the parser uses. The parser calls ASSET.AddExtension() that normalizes the path.
|
|
char fullPath[RAGE_MAX_PATH] = {0};
|
|
ASSET.AddExtension(fullPath, RAGE_MAX_PATH, path, "");
|
|
return atHashWithStringNotFinal(fullPath);
|
|
}
|
|
|
|
const char* CMappingManager::GetInputName(InputType input)
|
|
{
|
|
if(mapmgrVerifyf(input >= 0 && input < MAX_INPUTS && input != UNDEFINED_INPUT, "Invalid input!"))
|
|
{
|
|
return parser_rage__InputType_Data.m_Names[input];
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
void CMappingManager::ComboTrack::Update(u32 step)
|
|
{
|
|
if(mapmgrVerifyf(step < m_Steps.size(), "Invalid step index!"))
|
|
{
|
|
switch(m_Steps[step])
|
|
{
|
|
case ControlInput::DOWN:
|
|
if(Abs(ioMapper::GetSourceValue(m_Source, false)) >= ioValue::BUTTON_DOWN_THRESHOLD)
|
|
{
|
|
m_StepState = IN_PROGRESS;
|
|
}
|
|
else
|
|
{
|
|
m_StepState = FAILED;
|
|
}
|
|
break;
|
|
|
|
case ControlInput::UP:
|
|
if(Abs(ioMapper::GetSourceValue(m_Source, false)) < ioValue::BUTTON_DOWN_THRESHOLD)
|
|
{
|
|
m_StepState = IN_PROGRESS;
|
|
}
|
|
else
|
|
{
|
|
m_StepState = FAILED;
|
|
}
|
|
break;
|
|
|
|
case ControlInput::PRESSED:
|
|
if(Abs(ioMapper::GetSourceValue(m_Source, false)) >= ioValue::BUTTON_DOWN_THRESHOLD)
|
|
{
|
|
if(m_StepState != NOT_STARTED)
|
|
{
|
|
m_StepState = PASSED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_StepState = STARTED;
|
|
}
|
|
break;
|
|
|
|
case ControlInput::RELEASED:
|
|
if(Abs(ioMapper::GetSourceValue(m_Source, false)) < ioValue::BUTTON_DOWN_THRESHOLD)
|
|
{
|
|
if(m_StepState != NOT_STARTED)
|
|
{
|
|
m_StepState = PASSED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_StepState = STARTED;
|
|
}
|
|
break;
|
|
|
|
case ControlInput::NONZERO:
|
|
{
|
|
float val = ioMapper::GetSourceValue(m_Source, false);
|
|
if(ioValue::RequiresDeadZone(m_Source))
|
|
{
|
|
val = ioAddDeadZone(val, ioValue::DEFAULT_DEAD_ZONE_VALUE);
|
|
}
|
|
|
|
if(Abs(val) != 0.0f)
|
|
{
|
|
m_StepState = PASSED;
|
|
}
|
|
else
|
|
{
|
|
m_StepState = NOT_STARTED;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ControlInput::ANY_STATE:
|
|
m_StepState = PASSED;
|
|
break;
|
|
|
|
default:
|
|
mapmgrAssertf(false, "Unknown mapping test type!");
|
|
m_StepState = FAILED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CMappingManager::ComboMapping::Update()
|
|
{
|
|
mapmgrDebugf3("Testing step %u.", m_CurrentStep);
|
|
|
|
if(mapmgrVerifyf(m_Tracks.size() > 0, "Mapping has no tracks!") && m_RunningState == RUNNING)
|
|
{
|
|
bool passedAllTracks = true;
|
|
bool trackNotStarted = false;
|
|
bool failedATrack = false;
|
|
|
|
for(u32 trackIndex = 0; trackIndex < m_Tracks.size(); ++trackIndex)
|
|
{
|
|
mapmgrDebugf3("Testing step %u track %u.", m_CurrentStep, trackIndex);
|
|
ComboTrack& track = m_Tracks[trackIndex];
|
|
track.Update(m_CurrentStep);
|
|
|
|
if(track.m_StepState == ComboTrack::FAILED)
|
|
{
|
|
mapmgrDebugf3("Step %u track %u failed.", m_CurrentStep, trackIndex);
|
|
failedATrack = true;
|
|
passedAllTracks = false;
|
|
|
|
// This track has not passed the combo so stop checking others.
|
|
break;
|
|
}
|
|
else if(track.m_StepState == ComboTrack::NOT_STARTED)
|
|
{
|
|
mapmgrDebugf3("Step %u track %u not started.", m_CurrentStep, trackIndex);
|
|
trackNotStarted = true;
|
|
passedAllTracks = false;
|
|
}
|
|
else if(track.m_StepState != ComboTrack::PASSED)
|
|
{
|
|
mapmgrDebugf3("Step %u track %u not finished (state %d).", m_CurrentStep, trackIndex, track.m_StepState);
|
|
passedAllTracks = false;
|
|
}
|
|
else
|
|
{
|
|
mapmgrDebugf3( "Step %u track %u passed.", m_CurrentStep, trackIndex);
|
|
}
|
|
}
|
|
|
|
// The first state can start at any time so if we are waiting restart the timer.
|
|
if(failedATrack)
|
|
{
|
|
mapmgrDebugf3("Failed a track, stopping combo.");
|
|
m_RunningState = FAILED_WAITING;
|
|
}
|
|
else if(m_CurrentStep == 0 && trackNotStarted)
|
|
{
|
|
mapmgrDebugf3("On first step or no start time, restarting timer.");
|
|
m_StepStartTime = fwTimer::GetSystemTimeInMilliseconds();
|
|
}
|
|
else if( passedAllTracks ||
|
|
m_StepStartTime == 0u ||
|
|
(m_StepStartTime + m_TimeDelta) < fwTimer::GetSystemTimeInMilliseconds() )
|
|
{
|
|
mapmgrDebugf3("Step %u has finished or failed, progressing combo step.", m_CurrentStep);
|
|
ProgressStep();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CMappingManager::ComboMapping::Restart()
|
|
{
|
|
m_RunningState = RUNNING;
|
|
m_StepStartTime = fwTimer::GetSystemTimeInMilliseconds();
|
|
m_CurrentStep = 0;
|
|
|
|
for(u32 i = 0; i < m_Tracks.size(); ++i)
|
|
{
|
|
m_Tracks[i].m_StepState = ComboTrack::NOT_STARTED;
|
|
}
|
|
}
|
|
|
|
void CMappingManager::ComboMapping::ProgressStep()
|
|
{
|
|
bool passed = true;
|
|
m_StepStartTime = fwTimer::GetSystemTimeInMilliseconds();
|
|
for(u32 i = 0; i < m_Tracks.size(); ++i)
|
|
{
|
|
ComboTrack& track = m_Tracks[i];
|
|
if(track.m_StepState != ComboTrack::PASSED && track.m_StepState != ComboTrack::IN_PROGRESS)
|
|
{
|
|
mapmgrDebugf3("Step %u track %u not passed and not in progress.", m_CurrentStep, i);
|
|
passed = false;
|
|
}
|
|
|
|
m_Tracks[i].m_StepState = ComboTrack::NOT_STARTED;
|
|
}
|
|
|
|
if(passed)
|
|
{
|
|
mapmgrDebugf2("Combo step passed, progressing from step %u.", m_CurrentStep);
|
|
|
|
if(m_CurrentStep >= (m_Tracks[0].m_Steps.size() -1))
|
|
{
|
|
mapmgrDebugf1("Combo mapped to %s completed successfully.", CMappingManager::GetInputName(m_Input));
|
|
m_RunningState = SUCCESS_WAITING;
|
|
}
|
|
else
|
|
{
|
|
++m_CurrentStep;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mapmgrDebugf3("Resetting combo step to 0.");
|
|
m_CurrentStep = 0;
|
|
m_RunningState = FAILED_WAITING;
|
|
}
|
|
}
|
|
|
|
void CMappingManager::ComboMapping::Load(const ControlInput::ComboMapping &mapping)
|
|
{
|
|
m_Input = mapping.m_Input;
|
|
m_TimeDelta = mapping.m_TimeDelta;
|
|
m_CurrentStep = 0u;
|
|
|
|
// We use Reset() rather than ResetCount() because m_Tracks is an array of an array and we dont want extra
|
|
// unused memory lying around! We will not likely do this often so its best free the memory.
|
|
m_Tracks.Reset();
|
|
|
|
// We need to find out exactly how and which sources are used in the combo.
|
|
for(u32 stepIndex = 0; stepIndex < mapping.m_Steps.size(); ++stepIndex)
|
|
{
|
|
const ControlInput::ComboStep& step = mapping.m_Steps[stepIndex];
|
|
for(u32 stateIndex = 0; stateIndex < step.m_States.size(); ++stateIndex)
|
|
{
|
|
const ControlInput::ComboState& state = step.m_States[stateIndex];
|
|
|
|
const ioSource stateSource = ConvertSource(state.m_Source);
|
|
u32 trackId = INVALID_TRACK_ID;
|
|
|
|
// Find the track that represents this source.
|
|
for(u32 trackIndex = 0; trackIndex < m_Tracks.size(); ++trackIndex)
|
|
{
|
|
const ioSource& trackSource = m_Tracks[trackIndex].m_Source;
|
|
if(trackSource.m_Device == stateSource.m_Device && trackSource.m_Parameter == stateSource.m_Parameter)
|
|
{
|
|
trackId = trackIndex;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(trackId == INVALID_TRACK_ID)
|
|
{
|
|
// This is a new source for the combo so add a new track.
|
|
trackId = m_Tracks.size();
|
|
ComboTrack& track = m_Tracks.Grow();
|
|
|
|
track.m_Source = stateSource;
|
|
track.m_Steps.Reserve(mapping.m_Steps.size());
|
|
|
|
for(u32 paddingIndex = 0; paddingIndex < stepIndex; ++paddingIndex)
|
|
{
|
|
track.m_Steps.Push(ControlInput::ANY_STATE);
|
|
}
|
|
}
|
|
|
|
m_Tracks[trackId].m_Steps.Push(state.m_Trigger);
|
|
}
|
|
|
|
// Now fill in any tracks with out a step as any state.
|
|
for(u32 trackIndex = 0; trackIndex < m_Tracks.size(); ++trackIndex)
|
|
{
|
|
ComboTrack& track = m_Tracks[trackIndex];
|
|
while(track.m_Steps.size() < (stepIndex+1))
|
|
{
|
|
track.m_Steps.Push(ControlInput::ANY_STATE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
u32 CMappingManager::ComboGroup::Update()
|
|
{
|
|
bool hasHigherRunningCombo = false;
|
|
|
|
for (u32 mappingIndex = 0; mappingIndex < m_ComboMappings.size(); ++mappingIndex)
|
|
{
|
|
ComboMapping& mapping = m_ComboMappings[mappingIndex];
|
|
|
|
if(mapping.m_RunningState == ComboMapping::FAILED_WAITING)
|
|
{
|
|
mapmgrDebugf3( "Combo mapping %u mapped to %s in '%s' has stopped, restarting.",
|
|
mappingIndex,
|
|
CMappingManager::GetInputName(mapping.m_Input),
|
|
m_Id.TryGetCStr() );
|
|
|
|
mapping.Restart();
|
|
}
|
|
|
|
if(mapping.m_RunningState == ComboMapping::RUNNING)
|
|
{
|
|
mapmgrDebugf3( "Updating combo mapping %u mapped to %s in '%s'.",
|
|
mappingIndex,
|
|
CMappingManager::GetInputName(mapping.m_Input),
|
|
m_Id.TryGetCStr() );
|
|
|
|
mapping.Update();
|
|
if(mapping.m_RunningState == ComboMapping::RUNNING)
|
|
{
|
|
hasHigherRunningCombo = true;
|
|
}
|
|
}
|
|
|
|
if(mapping.m_RunningState == ComboMapping::SUCCESS_WAITING)
|
|
{
|
|
if(hasHigherRunningCombo == false)
|
|
{
|
|
// We have a winner so stop.
|
|
mapmgrDebugf2( "Combo mapping %u mapped to %s completed in group '%s', finishing.",
|
|
mappingIndex,
|
|
CMappingManager::GetInputName(mapping.m_Input),
|
|
m_Id.TryGetCStr() );
|
|
|
|
return mappingIndex;
|
|
}
|
|
else
|
|
{
|
|
mapmgrDebugf2( "Combo mapping %u mapped to %s completed but a higher priority combo is still running in group '%s'.",
|
|
mappingIndex,
|
|
CMappingManager::GetInputName(mapping.m_Input),
|
|
m_Id.TryGetCStr() );
|
|
|
|
#if RSG_ASSERT
|
|
// This is to detect input deadlocks where a combo has passed but waiting for more important combos to complete.
|
|
// It is possible that the more important combos keep cycling between running and failed so this combo will never
|
|
// fire. We assert if this happens as its a design flaw in the combo sequences.
|
|
if(m_WaitStartTime == 0)
|
|
{
|
|
m_WaitStartTime = fwTimer::GetSystemTimeInMilliseconds();
|
|
}
|
|
else
|
|
{
|
|
mapmgrAssertf( (m_WaitStartTime + m_WaitDuration) > fwTimer::GetSystemTimeInMilliseconds(),
|
|
"A mapping in to %s in '%s' has passed but multiple higher priority combos continuing to run. This is likely a design flaw in the combos!",
|
|
CMappingManager::GetInputName(mapping.m_Input),
|
|
m_Id.TryGetCStr() );
|
|
}
|
|
#endif // RSG_ASSERT
|
|
}
|
|
}
|
|
}
|
|
|
|
return INVALID_COMBO_MAPPING_ID;
|
|
}
|
|
|
|
rage::InputType CMappingManager::ComboGroup::GetComboInput(u32 comboId) const
|
|
{
|
|
if( mapmgrVerifyf(comboId != INVALID_COMBO_MAPPING_ID, "Invalid combo id used to retrieve input!") &&
|
|
mapmgrVerifyf(comboId < m_ComboMappings.size(), "Out of range combo id used to retrieve input!"))
|
|
{
|
|
return m_ComboMappings[comboId].m_Input;
|
|
}
|
|
else
|
|
{
|
|
return UNDEFINED_INPUT;
|
|
}
|
|
}
|
|
|
|
rage::ioSource CMappingManager::ComboGroup::GetComboSource(u32 comboId) const
|
|
{
|
|
if( mapmgrVerifyf(comboId != INVALID_COMBO_MAPPING_ID, "Invalid combo id used to retrieve input!") &&
|
|
mapmgrVerifyf(comboId < m_ComboMappings.size(), "Out of range combo id used to retrieve input!"))
|
|
{
|
|
const ComboMapping& mapping = m_ComboMappings[comboId];
|
|
if(mapping.m_Tracks.size() == 1)
|
|
{
|
|
return mapping.m_Tracks[0].m_Source;
|
|
}
|
|
else if(mapmgrVerifyf(mapping.m_Tracks.size() > 0, "No tracks for mapping!"))
|
|
{
|
|
const s32 stepSize = mapping.m_Tracks[0].m_Steps.size();
|
|
|
|
u32 sourceTrack = ComboMapping::INVALID_TRACK_ID;
|
|
|
|
// We need to search the last steps
|
|
for (u32 trackIndex = 0; trackIndex < mapping.m_Tracks.size(); ++trackIndex)
|
|
{
|
|
const ComboTrack& track = mapping.m_Tracks[trackIndex];
|
|
if(mapmgrVerifyf(track.m_Steps.size() == stepSize, "Wrong number of combo track!"))
|
|
{
|
|
ControlInput::Trigger trigger = track.m_Steps[stepSize-1];
|
|
if(trigger == ControlInput::DOWN || trigger == ControlInput::PRESSED)
|
|
{
|
|
// Save the track to use for the source index.
|
|
if(sourceTrack == ComboMapping::INVALID_TRACK_ID)
|
|
{
|
|
sourceTrack = trackIndex;
|
|
}
|
|
// There are more than one so stop and use none of them.
|
|
else
|
|
{
|
|
sourceTrack = ComboMapping::INVALID_TRACK_ID;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(sourceTrack != ComboMapping::INVALID_TRACK_ID)
|
|
{
|
|
return mapping.m_Tracks[sourceTrack].m_Source;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ioSource::UNKNOWN_SOURCE;
|
|
}
|
|
|
|
void CMappingManager::ComboGroup::Load(atHashWithStringNotFinal id, const ControlInput::MappingData& mappingData)
|
|
{
|
|
Unload();
|
|
|
|
m_Id = id;
|
|
|
|
ASSERT_ONLY(m_WaitDuration = 0u);
|
|
|
|
m_ComboMappings.Reserve(mappingData.m_ComboMappings.size());
|
|
for(u32 mappingIndex = 0; mappingIndex < mappingData.m_ComboMappings.size(); ++mappingIndex)
|
|
{
|
|
const ControlInput::ComboMapping& mapping = mappingData.m_ComboMappings[mappingIndex];
|
|
ComboMapping& newMapping = m_ComboMappings.Append();
|
|
newMapping.Load(mapping);
|
|
|
|
#if RSG_ASSERT
|
|
// Get the full duration of the longest running combo.
|
|
if(newMapping.m_Tracks.size() > 0)
|
|
{
|
|
u32 duration = newMapping.m_Tracks[0].m_Steps.size() * newMapping.m_TimeDelta;
|
|
if(duration > m_WaitDuration)
|
|
{
|
|
m_WaitDuration = duration;
|
|
}
|
|
}
|
|
#endif // RSG_ASSERT
|
|
}
|
|
|
|
#if RSG_ASSERT
|
|
// Verify data structures are valid!
|
|
for (u32 mappingIndex = 0; mappingIndex < m_ComboMappings.size(); ++mappingIndex)
|
|
{
|
|
const ComboMapping& mapping = m_ComboMappings[mappingIndex];
|
|
if(mapmgrVerifyf(mapping.m_Tracks.size() > 0, "A mapping has been loaded with no input mappings!"))
|
|
{
|
|
const s32 size = mapping.m_Tracks[0].m_Steps.size();
|
|
for(u32 trackIndex = 1; trackIndex < mapping.m_Tracks.size(); ++trackIndex)
|
|
{
|
|
const ComboTrack& track = mapping.m_Tracks[trackIndex];
|
|
mapmgrAssertf( size == track.m_Steps.size(),
|
|
"Mapping %u has tracks of different sizes (%u and %u) inside '%s'!",
|
|
mappingIndex,
|
|
size,
|
|
track.m_Steps.size(),
|
|
id.TryGetCStr() );
|
|
|
|
for(u32 stepIndex = 0; stepIndex < track.m_Steps.size(); ++stepIndex)
|
|
{
|
|
mapmgrAssertf( track.m_Steps[stepIndex] != ControlInput::INVALID_TRIGGER,
|
|
"Invalid trigger on mapping %u track %u step %u! This is probably a typo in '%s'!",
|
|
mappingIndex,
|
|
trackIndex,
|
|
stepIndex,
|
|
id.TryGetCStr() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // RSG_ASSERT
|
|
}
|
|
|
|
|
|
void CMappingManager::ComboGroup::Unload()
|
|
{
|
|
m_Id = "";
|
|
|
|
// We use Reset() rather than ResetCount() because m_Tracks is an array of an array and we don't want extra
|
|
// unused memory lying around! We will not likely do this often so it's best to free the memory.
|
|
m_ComboMappings.Reset();
|
|
}
|
|
|
|
void CMappingManager::ComboGroup::Restart()
|
|
{
|
|
ASSERT_ONLY(m_WaitStartTime = 0);
|
|
|
|
for(u32 i = 0; i < m_ComboMappings.size(); ++i)
|
|
{
|
|
m_ComboMappings[i].Restart();
|
|
}
|
|
}
|
|
|
|
#if RSG_BANK
|
|
void CMappingManager::InitWidgets(bkBank& bank)
|
|
{
|
|
// Reset any currently displaying debug data.
|
|
m_DebugGroup = 0;
|
|
m_EnableDebugging = false;
|
|
|
|
// Store the bank to update later.
|
|
m_BankParent = &bank;
|
|
m_BankGroup = bank.PushGroup("Mapping Manager");
|
|
|
|
bank.AddText("Path", m_FilePath, RAGE_MAX_PATH);
|
|
bank.AddButton("Load File", datCallback(MFA(CMappingManager::DebugLoadMappingfile), this));
|
|
|
|
bank.AddToggle("EnableDebugging", &m_EnableDebugging);
|
|
|
|
const char** groupNames = Alloca(const char*, m_MappingGroups.size());
|
|
int groupAmount = 0;
|
|
for(u32 i = 0; i < m_MappingGroups.size(); ++i)
|
|
{
|
|
if(m_MappingGroups[i].GetId().GetHash() != INVALID_GROUP_ID)
|
|
{
|
|
groupNames[i] = m_MappingGroups[i].GetId().TryGetCStr();
|
|
++groupAmount;
|
|
}
|
|
}
|
|
|
|
bank.AddCombo( "Debug Group",
|
|
&m_DebugGroup,
|
|
groupAmount,
|
|
groupNames );
|
|
|
|
for(u32 i = 0; i < m_MappingGroups.size(); ++i)
|
|
{
|
|
if(m_MappingGroups[i].GetId().GetHash() != INVALID_GROUP_ID)
|
|
{
|
|
m_MappingGroups[i].InitWidgets(bank);
|
|
}
|
|
}
|
|
|
|
bank.PopGroup();
|
|
}
|
|
|
|
void CMappingManager::RebuildWidgets()
|
|
{
|
|
if(m_BankGroup && m_BankParent)
|
|
{
|
|
m_BankParent->Remove(*m_BankGroup);
|
|
m_BankGroup = NULL;
|
|
InitWidgets(*m_BankParent);
|
|
}
|
|
}
|
|
|
|
void CMappingManager::UpdateDebugDisplay()
|
|
{
|
|
if( m_EnableDebugging &&
|
|
m_DebugGroup < m_MappingGroups.size() &&
|
|
m_MappingGroups[m_DebugGroup].GetId().GetHash() != INVALID_GROUP_ID )
|
|
{
|
|
m_MappingGroups[m_DebugGroup].UpdateDebugInfo();
|
|
}
|
|
}
|
|
|
|
const char* CMappingManager::GetTriggerName(ControlInput::Trigger trigger)
|
|
{
|
|
if(mapmgrVerifyf(trigger >= ControlInput::INVALID_TRIGGER && trigger <= ControlInput::ANY_STATE, "Invalid trigger!"))
|
|
{
|
|
return parser_rage__ControlInput__Trigger_Data.m_Names[trigger];
|
|
}
|
|
|
|
return "Unknown";
|
|
}
|
|
|
|
void CMappingManager::DebugLoadMappingfile()
|
|
{
|
|
LoadMappingFile(m_FilePath);
|
|
}
|
|
|
|
const char* CMappingManager::ComboTrack::GetTestStateStr(TestState state)
|
|
{
|
|
switch(state)
|
|
{
|
|
case NOT_STARTED:
|
|
return "Not Started";
|
|
|
|
case STARTED:
|
|
return "Started";
|
|
|
|
case IN_PROGRESS:
|
|
return "In Progress";
|
|
|
|
case PASSED:
|
|
return "Passed";
|
|
|
|
case FAILED:
|
|
return "Failed";
|
|
|
|
default:
|
|
mapmgrAssertf(false, "Unknown TestState (%d)!", state);
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
rage::Color32 CMappingManager::ComboTrack::GetTestStateColor(TestState state)
|
|
{
|
|
switch(state)
|
|
{
|
|
case NOT_STARTED:
|
|
return Color_DarkOrange;
|
|
|
|
case STARTED:
|
|
return Color_orange;
|
|
|
|
case IN_PROGRESS:
|
|
return Color_DarkOliveGreen;
|
|
|
|
case PASSED:
|
|
return Color_green;
|
|
|
|
case FAILED:
|
|
return Color_red;
|
|
|
|
default:
|
|
mapmgrAssertf(false, "Unknown TestState (%d)!", state);
|
|
return Color_red;
|
|
}
|
|
}
|
|
|
|
const char* CMappingManager::ComboMapping::GetRunningStateStr(RunningState state)
|
|
{
|
|
switch(state)
|
|
{
|
|
case FAILED_WAITING:
|
|
return "Failed/Stopped";
|
|
|
|
case RUNNING:
|
|
return "Running";
|
|
|
|
case SUCCESS_WAITING:
|
|
return "Success/Waiting";
|
|
|
|
default:
|
|
mapmgrAssertf(false, "Unknown RunningState (%d)!", state);
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
rage::Color32 CMappingManager::ComboMapping::GetRunningStateColor(RunningState state)
|
|
{
|
|
switch(state)
|
|
{
|
|
case FAILED_WAITING:
|
|
return Color_red;
|
|
|
|
case RUNNING:
|
|
return Color_orange;
|
|
|
|
case SUCCESS_WAITING:
|
|
return Color_green;
|
|
|
|
default:
|
|
mapmgrAssertf(false, "Unknown RunningState (%d)!", state);
|
|
return Color_red;
|
|
}
|
|
}
|
|
|
|
void CMappingManager::ComboGroup::InitWidgets(bkBank& bank)
|
|
{
|
|
bank.PushGroup(m_Id.TryGetCStr());
|
|
|
|
// + 1 for the for the whole group.
|
|
const char** mappingNames = Alloca(const char*, m_ComboMappings.size() + 1);
|
|
mappingNames[0] = "All Mappings";
|
|
for(u32 i = 0; i < m_ComboMappings.size(); ++i)
|
|
{
|
|
mappingNames[i + 1] = CMappingManager::GetInputName(m_ComboMappings[i].m_Input);
|
|
}
|
|
|
|
bank.AddCombo( "Debug Group",
|
|
&m_DebugMapping,
|
|
m_ComboMappings.size() + 1,
|
|
mappingNames );
|
|
|
|
bank.PopGroup();
|
|
}
|
|
|
|
void CMappingManager::ComboGroup::UpdateDebugInfo()
|
|
{
|
|
if(m_DebugMapping == 0)
|
|
{
|
|
DebugDrawComboList();
|
|
}
|
|
// + 1 as 0 means all mappings, all mappings are offset by 1.
|
|
else if(mapmgrVerifyf(m_DebugMapping > 0 && m_DebugMapping < (m_ComboMappings.size() + 1), "Invalid mapping index!"))
|
|
{
|
|
DebugDrawCombo(m_DebugMapping - 1);
|
|
}
|
|
}
|
|
|
|
void CMappingManager::ComboGroup::DebugDrawCombo(u32 comboId) const
|
|
{
|
|
if(m_ComboMappings.size() > comboId && m_ComboMappings[comboId].m_Tracks.size() > 0)
|
|
{
|
|
const ComboMapping& mapping = m_ComboMappings[comboId];
|
|
|
|
const float paddingY = 0.2f;
|
|
const float paddingX = 0.15f;
|
|
const float fullLength = 1.0f - (2.0f * paddingX);
|
|
const float dy = Min((1.0f - (2.0f * paddingY)) / mapping.m_Tracks.size(), 0.2f);
|
|
const float height = dy / 2.0f;
|
|
const float stepLength = fullLength / static_cast<float>(mapping.m_Tracks[0].m_Steps.size());
|
|
const Vector2 vDirection(1.0f,0.0f);
|
|
|
|
// Output the input that this combo is mapped to.
|
|
grcDebugDraw::Text(Vector2(0, 0.0f), Color_DarkOrange, GetInputName(mapping.m_Input));
|
|
|
|
// Loop through each track and display its status.
|
|
for(u32 trackIndex = 0u; trackIndex < mapping.m_Tracks.size(); ++trackIndex)
|
|
{
|
|
const ComboTrack& track = mapping.m_Tracks[trackIndex];
|
|
const float trackY = paddingY + (trackIndex * dy);
|
|
|
|
// Output the track button information (currently displayed as TODO until we can get a string representation).
|
|
grcDebugDraw::Text(Vector2(0.0f, trackY - (dy / 2.0f)), Color_blue, "TODO: Button");
|
|
|
|
// Display each step in the track as meter.
|
|
for(u32 stepIndex = 0u; stepIndex < track.m_Steps.size(); ++stepIndex)
|
|
{
|
|
const Vector2 stepPos(paddingX + (stepIndex * stepLength), trackY);
|
|
|
|
Color32 stepColor;
|
|
// If the step has already been passed.
|
|
if(mapping.m_CurrentStep > stepIndex)
|
|
{
|
|
stepColor = Color_green;
|
|
}
|
|
// If this is the step currently in progress.
|
|
else if(mapping.m_CurrentStep == stepIndex)
|
|
{
|
|
stepColor = ComboTrack::GetTestStateColor(track.m_StepState);
|
|
|
|
if(mapping.m_RunningState == ComboMapping::RUNNING)
|
|
{
|
|
|
|
float progress = static_cast<float>(fwTimer::GetSystemPrevElapsedTimeInMilliseconds() - mapping.m_StepStartTime) /
|
|
static_cast<float>(mapping.m_TimeDelta);
|
|
|
|
// Show a value (in this case a time bar) of how far through the step we are.
|
|
grcDebugDraw::MeterValue(stepPos, vDirection, stepLength, progress, height, Color_red, true);
|
|
}
|
|
}
|
|
// The step has not been started yet.
|
|
else
|
|
{
|
|
stepColor = Color_DarkOliveGreen4;
|
|
}
|
|
|
|
grcDebugDraw::Meter( stepPos,
|
|
vDirection,
|
|
stepLength,
|
|
height,
|
|
stepColor,
|
|
CMappingManager::GetTriggerName(track.m_Steps[stepIndex]) );
|
|
}
|
|
|
|
// Add summary information to the end of the track.
|
|
char stateText[60];
|
|
formatf( stateText,
|
|
"%s: %s",
|
|
ComboTrack::GetTestStateStr(track.m_StepState),
|
|
CMappingManager::GetTriggerName(track.m_Steps[mapping.m_CurrentStep]) );
|
|
|
|
grcDebugDraw::Text( Vector2(1.0f - paddingX, trackY - (dy / 2.0f)),
|
|
ComboTrack::GetTestStateColor(track.m_StepState),
|
|
stateText );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void CMappingManager::ComboGroup::DebugDrawComboList() const
|
|
{
|
|
// Output the group id being displayed (the group id is the file path).
|
|
grcDebugDraw::Text(Vector2(0, 0.0f), Color_DarkOrange, m_Id.TryGetCStr());
|
|
|
|
// Although each mapping has a different number of steps, we use the largest to keep all steps the same size making
|
|
// the debug display easier to understand.
|
|
u32 maxSteps = 0u;
|
|
for(u32 mappingIndex = 0; mappingIndex < m_ComboMappings.size(); ++mappingIndex)
|
|
{
|
|
if(m_ComboMappings[mappingIndex].m_Tracks.size() > 0)
|
|
{
|
|
maxSteps = Max(maxSteps, (u32)m_ComboMappings[mappingIndex].m_Tracks[0].m_Steps.size());
|
|
}
|
|
}
|
|
|
|
const float paddingY = 0.2f;
|
|
const float paddingX = 0.15f;
|
|
const float fullLength = 1.0f - (2.0f * paddingX);
|
|
const float dy = Min((1.0f - (2.0f * paddingY)) / m_ComboMappings.size(), 0.2f);
|
|
const float height = dy / 2.0f;
|
|
const float stepLength = fullLength / static_cast<float>(maxSteps);
|
|
const Vector2 vDirection(1.0f,0.0f);
|
|
|
|
// Display all mappings in this group.
|
|
for(u32 mappingIndex = 0; mappingIndex < m_ComboMappings.size(); ++mappingIndex)
|
|
{
|
|
const ComboMapping& mapping = m_ComboMappings[mappingIndex];
|
|
const float comboY = paddingY + (mappingIndex * dy);
|
|
const Color32 mappingColor = ComboMapping::GetRunningStateColor(mapping.m_RunningState);
|
|
|
|
// Display the input name for this mapping next to its track.
|
|
grcDebugDraw::Text(Vector2(0.0f, comboY - (dy / 2.0f)), mappingColor, GetInputName(mapping.m_Input));
|
|
|
|
if(mapping.m_Tracks.size() > 0)
|
|
{
|
|
// Display each step separately for a mapping but without individual track states. This can help track down
|
|
// mapping hierarchy issues when a mapping is waiting on a higher mapping that has a lot of steps.
|
|
for(u32 stepIndex = 0u; stepIndex < mapping.m_Tracks[0].m_Steps.size(); ++stepIndex)
|
|
{
|
|
const Vector2 stepPos(paddingX + (stepIndex * stepLength), comboY);
|
|
|
|
Color32 stepColor;
|
|
const char* stepStr = NULL;
|
|
|
|
// If this step has already been passed.
|
|
if(mapping.m_CurrentStep > stepIndex)
|
|
{
|
|
stepStr = "Complete";
|
|
stepColor = Color_green;
|
|
}
|
|
// If this is the current step.
|
|
else if(mapping.m_CurrentStep == stepIndex)
|
|
{
|
|
stepStr = ComboMapping::GetRunningStateStr(mapping.m_RunningState);
|
|
stepColor = ComboMapping::GetRunningStateColor(mapping.m_RunningState);
|
|
|
|
if(mapping.m_RunningState == ComboMapping::RUNNING)
|
|
{
|
|
float progress = static_cast<float>(fwTimer::GetSystemPrevElapsedTimeInMilliseconds() - mapping.m_StepStartTime) /
|
|
static_cast<float>(mapping.m_TimeDelta);
|
|
|
|
grcDebugDraw::MeterValue(stepPos, vDirection, stepLength, progress, height, Color_red, true);
|
|
}
|
|
}
|
|
// This step has not started yet.
|
|
else
|
|
{
|
|
stepStr = "Waiting";
|
|
stepColor = Color_DarkOliveGreen4;
|
|
}
|
|
|
|
grcDebugDraw::Meter( stepPos,
|
|
vDirection,
|
|
stepLength,
|
|
height,
|
|
stepColor,
|
|
stepStr );
|
|
}
|
|
}
|
|
|
|
grcDebugDraw::Text( Vector2(1.0f - paddingX, comboY - (dy / 2.0f)),
|
|
mappingColor,
|
|
ComboMapping::GetRunningStateStr(mapping.m_RunningState) );
|
|
}
|
|
}
|
|
|
|
#endif // RSG_BANK
|
|
}
|
|
|
|
#endif // ENABLE_INPUT_COMBOS
|