Files
BMagnu fcaa9dd998 Allow trail particles to be parented to their spawner (#7528)
* Adapt particle at runtime

* Refactor EffectHosts

* fix issues

* cleanup

* Remove live transitive particles

* paticle hosts now do local correctly

* Fix effecthostparticle local mode

* again

* Set particle host orientation

* I don't like mixed local and global vectors

* agaiiin

* Fix validity check

* parent foo

* Fix bugs, hopefully finally

* refactor

* Flip new table option for intuitiveness

* Fix support for GCC9 due to issues with LWG3052

* Address feedback
2026-07-15 15:10:15 +09:00

527 lines
17 KiB
C++

#ifndef _FSO_UTILITY_H
#define _FSO_UTILITY_H
#include <vector>
#include "globalincs/globals.h"
#include "globalincs/toolchain.h"
inline static constexpr size_t MAX_ITEMS_SENTINEL = i2sz(-1);
// Goober5000
// A sort for use with small or almost-sorted lists. Iteration time is O(n) for a fully-sorted list.
// This uses a type-safe version of the function prototype for stdlib's qsort, although the size is an int rather than a size_t (for the reasons that j is an int).
// The fncompare function should return <0, 0, or >0 as the left item is less than, equal to, or greater than the right item.
template <typename array_t, typename T>
void insertion_sort(array_t& array_base, int array_size, int (*fncompare)(const T*, const T*))
{
// NOTE: j *must* be a signed type because j reaches -1 and j+1 must be 0.
int i, j;
T *current, *current_buf;
// allocate space for the element being moved
// (Taylor says that for optimization purposes malloc/free should be used rather than vm_malloc/vm_free here)
current_buf = new T();
Assertion(current_buf != nullptr, "Malloc failed!");
if (current_buf == nullptr)
{
return;
}
// loop
for (i = 1; i < array_size; i++)
{
// grab the current element
// this does a lazy move/copy because if the array is mostly sorted,
// there's no sense copying sorted items to their own places
bool lazily_copied = false;
current = &array_base[i];
// bump other elements toward the end of the array
for (j = i - 1; (j >= 0) && (fncompare(&array_base[j], current) > 0); j--)
{
if (!lazily_copied)
{
// this may look strange but it is just copying the data
// into the buffer, then pointing to the buffer
*current_buf = std::move(*current);
current = current_buf;
lazily_copied = true;
}
array_base[j + 1] = std::move(array_base[j]);
}
if (lazily_copied)
{
// insert the current element at the correct place
array_base[j + 1] = std::move(*current);
}
}
// free the allocated space
delete current_buf;
}
//
// See https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C++
//
template<typename T>
typename T::size_type GeneralizedLevenshteinDistance(const T &source,
const T &target,
typename T::size_type insert_cost = 1,
typename T::size_type delete_cost = 1,
typename T::size_type replace_cost = 1) {
if (source.size() > target.size()) {
return GeneralizedLevenshteinDistance(target, source, delete_cost, insert_cost, replace_cost);
}
using TSizeType = typename T::size_type;
const TSizeType min_size = source.size(), max_size = target.size();
std::vector<TSizeType> lev_dist(min_size + 1);
lev_dist[0] = 0;
for (TSizeType i = 1; i <= min_size; ++i) {
lev_dist[i] = lev_dist[i - 1] + delete_cost;
}
for (TSizeType j = 1; j <= max_size; ++j) {
TSizeType previous_diagonal = lev_dist[0], previous_diagonal_save;
lev_dist[0] += insert_cost;
for (TSizeType i = 1; i <= min_size; ++i) {
previous_diagonal_save = lev_dist[i];
if (source[i - 1] == target[j - 1]) {
lev_dist[i] = previous_diagonal;
}
else {
lev_dist[i] = std::min(std::min(lev_dist[i - 1] + delete_cost, lev_dist[i] + insert_cost), previous_diagonal + replace_cost);
}
previous_diagonal = previous_diagonal_save;
}
}
return lev_dist[min_size];
}
template<typename T>
bool stringcost_equal(const T& a, const T& b)
{
return a == b;
}
template<typename charT>
bool stringcost_tolower_equal(const charT& a, const charT& b)
{
return SCP_tolower(a) == SCP_tolower(b);
}
// Lafiel
template<typename T, typename charT = typename T::value_type>
typename T::size_type stringcost(const T& op, const T& input, typename T::size_type max_expected_length = NAME_LENGTH,
bool (*equal_check)(const charT&a, const charT&b) = stringcost_equal<charT>)
{
using TSizeType = typename T::size_type;
if(input.empty())
return T::npos;
struct string_search_it {
TSizeType count;
TSizeType lastpos;
TSizeType cost;
};
std::vector<string_search_it> iterators;
//Go through the input. If we find it split up into parts, prefer things that are least split, and within this prefer things that are closer together
for (TSizeType i = 0; i < op.length(); i++) {
std::vector<string_search_it> insert;
for (auto& it : iterators) {
if (it.count < input.length() && equal_check(op[i], input[it.count])) {
//We found something. There may be a better match for this later, so only make a copy.
insert.emplace_back(string_search_it{it.count + 1, i, i - it.lastpos <= 1 ? it.cost : max_expected_length + i - it.lastpos - 1});
}
}
iterators.insert(iterators.end(), insert.begin(), insert.end());
if (equal_check(op[i], input[0]))
iterators.emplace_back(string_search_it{1, i, i});
}
auto cost = T::npos;
for (const auto& it : iterators) {
//Things that are missing letters are considered worse by default
auto localcost = (input.length() - it.count) * (max_expected_length * max_expected_length) + it.cost;
if (localcost < cost)
cost = localcost;
}
return cost;
}
template <typename VECTOR_T, typename ITEM_T, typename FIELD_T>
size_t count_items_with_string(const VECTOR_T& item_vector, FIELD_T* ITEM_T::* field, const char* str, int start_index = 0, size_t num_items = MAX_ITEMS_SENTINEL)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
int end = (num_items == MAX_ITEMS_SENTINEL) ? sz2i(item_vector.size()) : sz2i(std::min(i2sz(start_index) + num_items, item_vector.size()));
size_t count = 0;
for (int index = start_index; index < end; ++index)
{
const ITEM_T& item = item_vector[index];
if (str == nullptr && item.*field == nullptr)
++count;
else if (str == nullptr || item.*field == nullptr)
;
else if (!stricmp(item.*field, str))
++count;
}
return count;
}
template <typename VECTOR_T, typename ITEM_T, typename FIELD_T>
size_t count_items_with_ptr_string(const VECTOR_T& item_vector, FIELD_T ITEM_T::* field, const char* str, int start_index = 0, size_t num_items = MAX_ITEMS_SENTINEL)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
int end = (num_items == MAX_ITEMS_SENTINEL) ? sz2i(item_vector.size()) : sz2i(std::min(i2sz(start_index) + num_items, item_vector.size()));
size_t count = 0;
for (int index = start_index; index < end; ++index)
{
const ITEM_T& item = item_vector[index];
if (str == nullptr && (item.*field).get() == nullptr)
++count;
else if (str == nullptr || (item.*field).get() == nullptr)
;
else if (!stricmp((item.*field).get(), str))
++count;
}
return count;
}
template <typename VECTOR_T, typename ITEM_T, typename FIELD_T>
size_t count_items_with_string(const VECTOR_T& item_vector, FIELD_T ITEM_T::* field, const SCP_string& str, int start_index = 0, size_t num_items = MAX_ITEMS_SENTINEL)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
int end = (num_items == MAX_ITEMS_SENTINEL) ? sz2i(item_vector.size()) : sz2i(std::min(i2sz(start_index) + num_items, item_vector.size()));
size_t count = 0;
for (int index = start_index; index < end; ++index)
{
if (lcase_equal(item_vector[index].*field, str))
++count;
}
return count;
}
template <typename VECTOR_T, typename ITEM_T>
size_t count_items_with_string(const VECTOR_T& item_vector, SCP_string ITEM_T::* field, const char* str, int start_index = 0, size_t num_items = MAX_ITEMS_SENTINEL)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
if (str == nullptr)
return 0;
return count_items_with_string(item_vector, field, SCP_string(str), start_index, num_items);
}
template <typename VECTOR_T, typename ITEM_T, typename FIELD_T>
size_t count_items_with_field(const VECTOR_T& item_vector, FIELD_T ITEM_T::* field, const FIELD_T& search, int start_index = 0, size_t num_items = MAX_ITEMS_SENTINEL)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
int end = (num_items == MAX_ITEMS_SENTINEL) ? sz2i(item_vector.size()) : sz2i(std::min(i2sz(start_index) + num_items, item_vector.size()));
size_t count = 0;
for (int index = start_index; index < end; ++index)
{
if (item_vector[index].*field == search)
++count;
}
return count;
}
template <typename ITEM_T, typename FIELD_T>
size_t count_items_with_string(const ITEM_T* item_array, size_t num_items, FIELD_T* ITEM_T::* field, const char* str, int start_index = 0)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
if (item_array == nullptr)
return 0;
size_t count = 0;
for (size_t i = i2sz(start_index); i < num_items; ++i)
{
if (str == nullptr && item_array[i].*field == nullptr)
++count;
else if (str == nullptr || item_array[i].*field == nullptr)
;
else if (!stricmp(item_array[i].*field, str))
++count;
}
return count;
}
template <typename ITEM_T, typename FIELD_T>
size_t count_items_with_ptr_string(const ITEM_T* item_array, size_t num_items, FIELD_T ITEM_T::* field, const char* str, int start_index = 0)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
if (item_array == nullptr)
return 0;
size_t count = 0;
for (size_t i = i2sz(start_index); i < num_items; ++i)
{
if (str == nullptr && (item_array[i].*field).get() == nullptr)
++count;
else if (str == nullptr || (item_array[i].*field).get() == nullptr)
;
else if (!stricmp((item_array[i].*field).get(), str))
++count;
}
return count;
}
template <typename ITEM_T, typename FIELD_T>
size_t count_items_with_string(const ITEM_T* item_array, size_t num_items, FIELD_T ITEM_T::* field, const SCP_string& str, int start_index = 0)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
if (item_array == nullptr)
return 0;
size_t count = 0;
for (size_t i = i2sz(start_index); i < num_items; ++i)
if (lcase_equal(item_array[i].*field, str))
++count;
return count;
}
template <typename ITEM_T>
size_t count_items_with_string(const ITEM_T* item_array, size_t num_items, SCP_string ITEM_T::* field, const char* str, int start_index = 0)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
if (item_array == nullptr || str == nullptr)
return 0;
return count_items_with_string(item_array, num_items, field, SCP_string(str), start_index);
}
template <typename ITEM_T, typename FIELD_T>
size_t count_items_with_field(const ITEM_T* item_array, size_t num_items, FIELD_T ITEM_T::* field, const FIELD_T& search, int start_index = 0)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
if (item_array == nullptr)
return 0;
size_t count = 0;
for (size_t i = i2sz(start_index); i < num_items; ++i)
if (item_array[i].*field == search)
++count;
return count;
}
template <typename VECTOR_T>
size_t count_items_with_value(const VECTOR_T& item_vector, int start_index = 0, size_t num_items = MAX_ITEMS_SENTINEL)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
auto begin_it = std::next(item_vector.begin(), start_index);
auto end_it = (num_items == MAX_ITEMS_SENTINEL) ? item_vector.end() : std::next(item_vector.begin(), static_cast<ptrdiff_t>(std::min(i2sz(start_index) + num_items, item_vector.size())));
return std::count_if(begin_it, end_it,
[](const typename VECTOR_T::value_type& element) { return element.has_value(); });
}
template <typename VECTOR_T, typename ITEM_T, typename FIELD_T>
int find_item_with_string(const VECTOR_T& item_vector, FIELD_T* ITEM_T::* field, const char* str, int start_index = 0, size_t num_items = MAX_ITEMS_SENTINEL)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
int end = (num_items == MAX_ITEMS_SENTINEL) ? sz2i(item_vector.size()) : sz2i(std::min(i2sz(start_index) + num_items, item_vector.size()));
for (int index = start_index; index < end; ++index)
{
const ITEM_T& item = item_vector[index];
if (str == nullptr && item.*field == nullptr)
return index;
else if (str == nullptr || item.*field == nullptr)
;
else if (!stricmp(item.*field, str))
return index;
}
return -1;
}
template <typename VECTOR_T, typename ITEM_T, typename FIELD_T>
int find_item_with_ptr_string(const VECTOR_T& item_vector, FIELD_T ITEM_T::* field, const char* str, int start_index = 0, size_t num_items = MAX_ITEMS_SENTINEL)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
int end = (num_items == MAX_ITEMS_SENTINEL) ? sz2i(item_vector.size()) : sz2i(std::min(i2sz(start_index) + num_items, item_vector.size()));
for (int index = start_index; index < end; ++index)
{
const ITEM_T& item = item_vector[index];
if (str == nullptr && (item.*field).get() == nullptr)
return index;
else if (str == nullptr || (item.*field).get() == nullptr)
;
else if (!stricmp((item.*field).get(), str))
return index;
}
return -1;
}
template <typename VECTOR_T, typename ITEM_T, typename FIELD_T>
int find_item_with_string(const VECTOR_T& item_vector, FIELD_T ITEM_T::* field, const SCP_string& str, int start_index = 0, size_t num_items = MAX_ITEMS_SENTINEL)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
int end = (num_items == MAX_ITEMS_SENTINEL) ? sz2i(item_vector.size()) : sz2i(std::min(i2sz(start_index) + num_items, item_vector.size()));
for (int index = start_index; index < end; ++index)
{
if (lcase_equal(item_vector[index].*field, str))
return index;
}
return -1;
}
template <typename VECTOR_T, typename ITEM_T>
int find_item_with_string(const VECTOR_T& item_vector, SCP_string ITEM_T::* field, const char* str, int start_index = 0, size_t num_items = MAX_ITEMS_SENTINEL)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
if (str == nullptr)
return -1;
return find_item_with_string(item_vector, field, SCP_string(str), start_index, num_items);
}
template <typename VECTOR_T, typename ITEM_T, typename FIELD_T>
int find_item_with_field(const VECTOR_T& item_vector, FIELD_T ITEM_T::* field, const FIELD_T& search, int start_index = 0, size_t num_items = MAX_ITEMS_SENTINEL)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
int end = (num_items == MAX_ITEMS_SENTINEL) ? sz2i(item_vector.size()) : sz2i(std::min(i2sz(start_index) + num_items, item_vector.size()));
for (int index = start_index; index < end; ++index)
{
if (item_vector[index].*field == search)
return index;
}
return -1;
}
template <typename ITEM_T, typename FIELD_T>
int find_item_with_string(const ITEM_T* item_array, size_t num_items, FIELD_T* ITEM_T::* field, const char* str, int start_index = 0)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
if (item_array == nullptr)
return -1;
for (size_t i = i2sz(start_index); i < num_items; ++i)
{
if (str == nullptr && item_array[i].*field == nullptr)
return sz2i(i);
else if (str == nullptr || item_array[i].*field == nullptr)
;
else if (!stricmp(item_array[i].*field, str))
return sz2i(i);
}
return -1;
}
template <typename ITEM_T, typename FIELD_T>
int find_item_with_ptr_string(const ITEM_T* item_array, size_t num_items, FIELD_T ITEM_T::* field, const char* str, int start_index = 0)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
if (item_array == nullptr)
return -1;
for (size_t i = i2sz(start_index); i < num_items; ++i)
{
if (str == nullptr && (item_array[i].*field).get() == nullptr)
return sz2i(i);
else if (str == nullptr || (item_array[i].*field).get() == nullptr)
;
else if (!stricmp((item_array[i].*field).get(), str))
return sz2i(i);
}
return -1;
}
template <typename ITEM_T, typename FIELD_T>
int find_item_with_string(const ITEM_T* item_array, size_t num_items, FIELD_T ITEM_T::* field, const SCP_string& str, int start_index = 0)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
if (item_array == nullptr)
return -1;
for (size_t i = i2sz(start_index); i < num_items; ++i)
if (lcase_equal(item_array[i].*field, str))
return sz2i(i);
return -1;
}
template <typename ITEM_T>
int find_item_with_string(const ITEM_T* item_array, size_t num_items, SCP_string ITEM_T::* field, const char* str, int start_index = 0)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
if (item_array == nullptr || str == nullptr)
return -1;
return find_item_with_string(item_array, num_items, field, SCP_string(str), start_index);
}
template <typename ITEM_T, typename FIELD_T>
int find_item_with_field(const ITEM_T* item_array, size_t num_items, FIELD_T ITEM_T::* field, const FIELD_T& search, int start_index = 0)
{
Assertion(start_index >= 0, "start_index cannot be negative!");
if (item_array == nullptr)
return -1;
for (size_t i = i2sz(start_index); i < num_items; ++i)
if (item_array[i].*field == search)
return sz2i(i);
return -1;
}
template <typename T>
const T* coalesce(const T* possibly_null, const T* value_if_null)
{
Assertion(value_if_null != nullptr, "value_if_null can never be null itself!");
return (possibly_null != nullptr) ? possibly_null : value_if_null;
}
template<typename... Ts>
struct overloads : Ts... {
constexpr overloads(Ts... t) : Ts(t)... {}
using Ts::operator()...;
};
#endif