Files
@ r28520:f9aebe299cae
Branch filter:
Location: cpp/openttd-patchpack/source/src/script/api/script_list.hpp
r28520:f9aebe299cae
11.9 KiB
text/x-c++hdr
Codechange: MacOS already has MIN/MAX macros defined
This is caused because we use PreCompile Headers, and one of them
includes a system headers which defines MIN/MAX.
This is caused because we use PreCompile Headers, and one of them
includes a system headers which defines MIN/MAX.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 | /*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_list.hpp A list which can keep item/value pairs, which you can walk. */
/** @defgroup ScriptList Classes that create a list of items. */
#ifndef SCRIPT_LIST_HPP
#define SCRIPT_LIST_HPP
#include "script_object.hpp"
/** Maximum number of operations allowed for valuating a list. */
static const int MAX_VALUATE_OPS = 500000;
class ScriptListSorter;
/**
* Class that creates a list which can keep item/value pairs, which you can walk.
* @api ai game
*/
class ScriptList : public ScriptObject {
public:
/** Type of sorter */
enum SorterType {
SORT_BY_VALUE, ///< Sort the list based on the value of the item.
SORT_BY_ITEM, ///< Sort the list based on the item itself.
};
/** Sort ascending */
static const bool SORT_ASCENDING = true;
/** Sort descending */
static const bool SORT_DESCENDING = false;
private:
ScriptListSorter *sorter; ///< Sorting algorithm
SorterType sorter_type; ///< Sorting type
bool sort_ascending; ///< Whether to sort ascending or descending
bool initialized; ///< Whether an iteration has been started
int modifications; ///< Number of modification that has been done. To prevent changing data while valuating.
protected:
template<typename T, class ItemValid, class ItemFilter>
static void FillList(ScriptList *list, ItemValid item_valid, ItemFilter item_filter)
{
for (const T *item : T::Iterate()) {
if (!item_valid(item)) continue;
if (!item_filter(item)) continue;
list->AddItem(item->index);
}
}
template<typename T, class ItemValid>
static void FillList(ScriptList *list, ItemValid item_valid)
{
ScriptList::FillList<T>(list, item_valid, [](const T *) { return true; });
}
template<typename T>
static void FillList(ScriptList *list)
{
ScriptList::FillList<T>(list, [](const T *) { return true; });
}
template<typename T, class ItemValid>
static void FillList(HSQUIRRELVM vm, ScriptList *list, ItemValid item_valid)
{
int nparam = sq_gettop(vm) - 1;
if (nparam >= 1) {
/* Make sure the filter function is really a function, and not any
* other type. It's parameter 2 for us, but for the user it's the
* first parameter they give. */
SQObjectType valuator_type = sq_gettype(vm, 2);
if (valuator_type != OT_CLOSURE && valuator_type != OT_NATIVECLOSURE) {
throw sq_throwerror(vm, "parameter 1 has an invalid type (expected function)");
}
/* Push the function to call */
sq_push(vm, 2);
}
/* Don't allow docommand from a Valuator, as we can't resume in
* mid C++-code. */
bool backup_allow = ScriptObject::GetAllowDoCommand();
ScriptObject::SetAllowDoCommand(false);
if (nparam < 1) {
ScriptList::FillList<T>(list, item_valid);
} else {
/* Limit the total number of ops that can be consumed by a filter operation, if a filter function is present */
SQOpsLimiter limiter(vm, MAX_VALUATE_OPS, "list filter function");
ScriptList::FillList<T>(list, item_valid,
[vm, nparam, backup_allow](const T *item) {
/* Push the root table as instance object, this is what squirrel does for meta-functions. */
sq_pushroottable(vm);
/* Push all arguments for the valuator function. */
sq_pushinteger(vm, item->index);
for (int i = 0; i < nparam - 1; i++) {
sq_push(vm, i + 3);
}
/* Call the function. Squirrel pops all parameters and pushes the return value. */
if (SQ_FAILED(sq_call(vm, nparam + 1, SQTrue, SQTrue))) {
ScriptObject::SetAllowDoCommand(backup_allow);
throw sq_throwerror(vm, "failed to run filter");
}
SQBool add = SQFalse;
/* Retrieve the return value */
switch (sq_gettype(vm, -1)) {
case OT_BOOL:
sq_getbool(vm, -1, &add);
break;
default:
ScriptObject::SetAllowDoCommand(backup_allow);
throw sq_throwerror(vm, "return value of filter is not valid (not bool)");
}
/* Pop the return value. */
sq_poptop(vm);
return add;
}
);
/* Pop the filter function */
sq_poptop(vm);
}
ScriptObject::SetAllowDoCommand(backup_allow);
}
template<typename T>
static void FillList(HSQUIRRELVM vm, ScriptList *list)
{
ScriptList::FillList<T>(vm, list, [](const T *) { return true; });
}
public:
typedef std::set<SQInteger> ScriptItemList; ///< The list of items inside the bucket
typedef std::map<SQInteger, ScriptItemList> ScriptListBucket; ///< The bucket list per value
typedef std::map<SQInteger, SQInteger> ScriptListMap; ///< List per item
ScriptListMap items; ///< The items in the list
ScriptListBucket buckets; ///< The items in the list, sorted by value
ScriptList();
~ScriptList();
#ifdef DOXYGEN_API
/**
* Add a single item to the list.
* @param item the item to add. Should be unique, otherwise it is ignored.
* @param value the value to assign.
*/
void AddItem(SQInteger item, SQInteger value);
#else
void AddItem(SQInteger item, SQInteger value = 0);
#endif /* DOXYGEN_API */
/**
* Remove a single item from the list.
* @param item the item to remove. If not existing, it is ignored.
*/
void RemoveItem(SQInteger item);
/**
* Clear the list, making Count() returning 0 and IsEmpty() returning true.
*/
void Clear();
/**
* Check if an item is in the list.
* @param item the item to check for.
* @return true if the item is in the list.
*/
bool HasItem(SQInteger item);
/**
* Go to the beginning of the list and return the item. To get the value use list.GetValue(list.Begin()).
* @return the first item.
* @note returns 0 if beyond end-of-list. Use IsEnd() to check for end-of-list.
*/
SQInteger Begin();
/**
* Go to the next item in the list and return the item. To get the value use list.GetValue(list.Next()).
* @return the next item.
* @note returns 0 if beyond end-of-list. Use IsEnd() to check for end-of-list.
*/
SQInteger Next();
/**
* Check if a list is empty.
* @return true if the list is empty.
*/
bool IsEmpty();
/**
* Check if there is a element left. In other words, if this is false,
* the last call to Begin() or Next() returned a valid item.
* @return true if the current item is beyond end-of-list.
*/
bool IsEnd();
/**
* Returns the amount of items in the list.
* @return amount of items in the list.
*/
SQInteger Count();
/**
* Get the value that belongs to this item.
* @param item the item to get the value from
* @return the value that belongs to this item.
*/
SQInteger GetValue(SQInteger item);
/**
* Set a value of an item directly.
* @param item the item to set the value for.
* @param value the value to give to the item
* @return true if we could set the item to value, false otherwise.
* @note Changing values of items while looping through a list might cause
* entries to be skipped. Be very careful with such operations.
*/
bool SetValue(SQInteger item, SQInteger value);
/**
* Sort this list by the given sorter and direction.
* @param sorter the type of sorter to use
* @param ascending if true, lowest value is on top, else at bottom.
* @note the current item stays at the same place.
* @see SORT_ASCENDING SORT_DESCENDING
*/
void Sort(SorterType sorter, bool ascending);
/**
* Add one list to another one.
* @param list The list that will be added to the caller.
* @post The list to be added ('list') stays unmodified.
* @note All added items keep their value as it was in 'list'.
* @note If the item already exists inside the caller, the value of the
* list that is added is set on the item.
*/
void AddList(ScriptList *list);
/**
* Swap the contents of two lists.
* @param list The list that will be swapped with.
*/
void SwapList(ScriptList *list);
/**
* Removes all items with a higher value than 'value'.
* @param value the value above which all items are removed.
*/
void RemoveAboveValue(SQInteger value);
/**
* Removes all items with a lower value than 'value'.
* @param value the value below which all items are removed.
*/
void RemoveBelowValue(SQInteger value);
/**
* Removes all items with a value above start and below end.
* @param start the lower bound of the to be removed values (exclusive).
* @param end the upper bound of the to be removed values (exclusive).
*/
void RemoveBetweenValue(SQInteger start, SQInteger end);
/**
* Remove all items with this value.
* @param value the value to remove.
*/
void RemoveValue(SQInteger value);
/**
* Remove the first count items.
* @param count the amount of items to remove.
*/
void RemoveTop(SQInteger count);
/**
* Remove the last count items.
* @param count the amount of items to remove.
*/
void RemoveBottom(SQInteger count);
/**
* Remove everything that is in the given list from this list (same item index that is).
* @param list the list of items to remove.
* @pre list != null
*/
void RemoveList(ScriptList *list);
/**
* Keep all items with a higher value than 'value'.
* @param value the value above which all items are kept.
*/
void KeepAboveValue(SQInteger value);
/**
* Keep all items with a lower value than 'value'.
* @param value the value below which all items are kept.
*/
void KeepBelowValue(SQInteger value);
/**
* Keep all items with a value above start and below end.
* @param start the lower bound of the to be kept values (exclusive).
* @param end the upper bound of the to be kept values (exclusive).
*/
void KeepBetweenValue(SQInteger start, SQInteger end);
/**
* Keep all items with this value.
* @param value the value to keep.
*/
void KeepValue(SQInteger value);
/**
* Keep the first count items, i.e. remove everything except the first count items.
* @param count the amount of items to keep.
*/
void KeepTop(SQInteger count);
/**
* Keep the last count items, i.e. remove everything except the last count items.
* @param count the amount of items to keep.
*/
void KeepBottom(SQInteger count);
/**
* Keeps everything that is in the given list from this list (same item index that is).
* @param list the list of items to keep.
* @pre list != null
*/
void KeepList(ScriptList *list);
#ifndef DOXYGEN_API
/**
* Used for 'foreach()' and [] get from Squirrel.
*/
SQInteger _get(HSQUIRRELVM vm);
/**
* Used for [] set from Squirrel.
*/
SQInteger _set(HSQUIRRELVM vm);
/**
* Used for 'foreach()' from Squirrel.
*/
SQInteger _nexti(HSQUIRRELVM vm);
/**
* The Valuate() wrapper from Squirrel.
*/
SQInteger Valuate(HSQUIRRELVM vm);
#else
/**
* Give all items a value defined by the valuator you give.
* @param valuator_function The function which will be doing the valuation.
* @param params The params to give to the valuators (minus the first param,
* which is always the index-value we are valuating).
* @note You may not add, remove or change (setting the value of) items while
* valuating. You may also not (re)sort while valuating.
* @note You can write your own valuators and use them. Just remember that
* the first parameter should be the index-value, and it should return
* an integer.
* @note Example:
* list.Valuate(ScriptBridge.GetPrice, 5);
* list.Valuate(ScriptBridge.GetMaxLength);
* function MyVal(bridge_id, myparam)
* {
* return myparam * bridge_id; // This is silly
* }
* list.Valuate(MyVal, 12);
*/
void Valuate(void *valuator_function, int params, ...);
#endif /* DOXYGEN_API */
};
#endif /* SCRIPT_LIST_HPP */
|