await states.map(async state => { здесь промис создаётся "в никуда", и его не ждут, и return statesDto остаётся пустым. Так вроде.
И Array.map() не так применяется.
Нужен флаг, отвечающий за запрет параллельного запуска функции.
Нужно делать "хвостовую" рекурсию, чтобы последующий запуск функции не удерживал в памяти предыдущую итерацию. И проще всего сделать if (data) setTimeout( recursionIsCool, 10 );
При "столкновении" ставим об этом флаг для персонажа, простую логическую переменную или свойство в объекте, как угодно. Одновременно с этим запускаем другую самозацикленную процедуру с миганием персонажа и счётчиком для трёх секунд, это может быть функция сравнения текущего времени со стартовым для этой процедуры или даже просто счётчик с фиксированным интервалом отсчёта в setTimeout/setInterval. Как отсчёт трёх секунд завершается, так и зацикленность процедуры прерывается, а вместе с этим опускается флаг "столкновения".
Если такое описание не помогает, то я ничем помочь больше не смогу.