diff --git a/sources/Data/Collections/ArrayList.uc b/sources/Data/Collections/ArrayList.uc
new file mode 100644
index 0000000..bed0e34
--- /dev/null
+++ b/sources/Data/Collections/ArrayList.uc
@@ -0,0 +1,870 @@
+/**
+ * Dynamic array object for storing arbitrary types of data. Generic
+ * storage is achieved by using `AcediaObject` as the stored type. Native
+ * variable types such as `int`, `bool`, etc. can be stored by boxing them into
+ * `AcediaObject`s.
+ * Appropriate classes and APIs for their construction are provided for
+ * main primitive types and can be extended to any custom `struct`.
+ * Copyright 2022 Anton Tarasenko
+ *------------------------------------------------------------------------------
+ * This file is part of Acedia.
+ *
+ * Acedia 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Acedia 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 Acedia. If not, see .
+ */
+class ArrayList extends Collection;
+
+// Actual storage of all our data.
+var private array storedObjects;
+// Recorded `lifeVersions` of all stored objects.
+// Invariant `lifeVersions.length == storedObjects.length` should be
+// enforced by all methods.
+var private array lifeVersions;
+
+// Free array data
+protected function Finalizer()
+{
+ Empty();
+}
+
+// Method, used to compare array values at different indices.
+// Does not check boundary conditions, so make sure passed indices are valid.
+private function bool AreEqual(AcediaObject object1, AcediaObject object2)
+{
+ if (object1 == none && object2 == none) return true;
+ if (object1 == none || object2 == none) return false;
+
+ return object1.IsEqual(object2);
+}
+
+/**
+ * Returns current length of dynamic `ArrayList`.
+ *
+ * @return Returns length of the caller `ArrayList`.
+ * Guaranteed to be non-negative.
+ */
+public final function int GetLength()
+{
+ return storedObjects.length;
+}
+
+/**
+ * Changes length of the caller `ArrayList`.
+ * If `ArrayList` size is increased as a result - added items will be
+ * filled with `none`s.
+ *
+ * @param newLength New length of an `ArrayList`.
+ * If negative value is passes - method will do nothing.
+ * @return Reference to the caller `ArrayList` to allow for method chaining.
+ */
+public final function ArrayList SetLength(int newLength)
+{
+ local int i;
+
+ if (newLength < 0) {
+ return self;
+ }
+ for (i = newLength; i < storedObjects.length; i += 1) {
+ FreeItem(i);
+ }
+ storedObjects.length = newLength;
+ lifeVersions.length = newLength;
+ return self;
+}
+
+/**
+ * Deallocates an item at a given index `index`.
+ * Does not check `ArrayList` bounds for `index`, so you must ensure that
+ * `index` is valid.
+ *
+ * @param index Index of the item to deallocate.
+ * @return Reference to the caller `ArrayList` to allow for method chaining.
+ */
+protected final function ArrayList FreeItem(int index)
+{
+ if (storedObjects[index] == none) {
+ return self;
+ }
+ storedObjects[index].FreeSelf(lifeVersions[index]);
+ storedObjects[index] = none;
+ return self;
+}
+
+public function Empty(optional bool deprecated)
+{
+ SetLength(0);
+}
+
+/**
+ * Adds `amountOfNewItems` empty (`none`) items at the end of
+ * the `ArrayList`.
+ * To insert items at an arbitrary array index, use `Insert()`.
+ *
+ * @param amountOfNewItems Amount of items to add at the end.
+ * If non-positive value is passed, - method does nothing.
+ * @return Reference to the caller `ArrayList` to allow for method chaining.
+ */
+public final function ArrayList Add(int amountOfNewItems)
+{
+ if (amountOfNewItems > 0) {
+ SetLength(storedObjects.length + amountOfNewItems);
+ }
+ return self;
+}
+
+/**
+ * Inserts `count` empty (`none`) items into the `ArrayList`
+ * at specified position.
+ * The indices of the following items are increased by `count` in order
+ * to make room for the new items.
+ *
+ * To add items at the end of an `ArrayList`, consider using `Add()`,
+ * which is equivalent to `array.Insert(array.GetLength(), ...)`.
+ *
+ * @param index Index, where first inserted item will be located.
+ * Must belong to `[0; self.GetLength()]` inclusive interval,
+ * otherwise method does nothing.
+ * @param count Amount of new items to insert.
+ * Must be positive, otherwise method does nothing.
+ * @return Reference to the caller `ArrayList` to allow for method chaining.
+ */
+public final function ArrayList Insert(int index, int count)
+{
+ local int i;
+ local int swapIndex;
+ local int amountToShift;
+
+ if (count <= 0) return self;
+ if (index < 0 || index > storedObjects.length) return self;
+
+ // Native `Insert()` for an array is bugged and cannot be trusted
+ amountToShift = storedObjects.length - index;
+ Add(count);
+ if (amountToShift == 0) {
+ return self;
+ }
+ for (i = 0; i < amountToShift; i += 1)
+ {
+ swapIndex = storedObjects.length - i - 1;
+ Swap(swapIndex, swapIndex - count);
+ }
+ return self;
+}
+
+/**
+ * Swaps two `ArrayList` items.
+ *
+ * @param index1 Index of item to swap.
+ * @param index2 Index of item to swap.
+ */
+protected final function Swap(int index1, int index2)
+{
+ local AcediaObject temporaryItem;
+ local int temporaryNumber;
+
+ // Swap object
+ temporaryItem = storedObjects[index1];
+ storedObjects[index1] = storedObjects[index2];
+ storedObjects[index2] = temporaryItem;
+ // Swap life versions
+ temporaryNumber = lifeVersions[index1];
+ lifeVersions[index1] = lifeVersions[index2];
+ lifeVersions[index2] = temporaryNumber;
+}
+
+/**
+ * Removes number items from the `ArrayList`, starting at `index`.
+ * All items before position and from `index + count` on are not changed,
+ * but the item indices change, - they shift to close the gap,
+ * created by removed items.
+ *
+ * @param index Remove items starting from this index.
+ * Must belong to `[0; self.GetLength() - 1]` inclusive interval,
+ * otherwise method does nothing.
+ * @param count Removes at most this much items.
+ * Must be positive, otherwise method does nothing.
+ * Specifying more items than can be removed simply removes
+ * all items, starting from `index`.
+ * @return Reference to the caller `ArrayList` to allow for method chaining.
+ */
+public final function ArrayList Remove(int index, int count)
+{
+ local int i;
+
+ if (count <= 0) return self;
+ if (index < 0 || index > storedObjects.length) return self;
+
+ count = Min(count, storedObjects.length - index);
+ for (i = 0; i < count; i += 1) {
+ FreeItem(index + i);
+ }
+ storedObjects.Remove(index, count);
+ lifeVersions.Remove(index, count);
+ return self;
+}
+
+/**
+ * Removes item at a given index, shifting all the items that come after
+ * one place backwards.
+ *
+ * @param index Remove items starting from this index.
+ * Must belong to `[0; self.GetLength() - 1]` inclusive interval,
+ * otherwise method does nothing.
+ * @return Reference to the caller `ArrayList` to allow for method chaining.
+ */
+public final function ArrayList RemoveIndex(int index)
+{
+ Remove(index, 1);
+ return self;
+}
+
+/**
+ * Validates item at `index`: in case it was erroneously deallocated while
+ * being stored in caller `ArrayList` - forgets stored `AcediaObject`
+ * reference.
+ *
+ * @param index Index of an item to validate/
+ * @return `true` if `index` is valid for `storedObjects` / `lifeVersions`
+ * and there is no need to check it.
+ */
+private final function bool ValidateIndex(int index)
+{
+ local AcediaObject item;
+
+ if (index < 0) return false;
+ if (index >= storedObjects.length) return false;
+ item = storedObjects[index];
+ if (item == none) return true;
+
+ if (item.GetLifeVersion() != lifeVersions[index])
+ {
+ storedObjects[index] = none;
+ lifeVersions[index] = 0;
+ }
+ return true;
+}
+
+/**
+ * Returns item at `index` and replaces it with `none` inside `ArrayList`.
+ *
+ * @param index Index of an item that `ArrayList` has to return.
+ * @return Either value at `index` in the caller `ArrayList` or `none` if
+ * passed `index` is invalid.
+ */
+public final function AcediaObject TakeItem(int index)
+{
+ local AcediaObject result;
+
+ if (ValidateIndex(index))
+ {
+ result = storedObjects[index];
+ storedObjects[index] = none;
+ lifeVersions[index] = 0;
+ }
+ return result;
+}
+
+/**
+ * Returns borrowed item at `index`. If index is invalid, returns `none`.
+ *
+ * @param index Index of an item that `ArrayList` has to return.
+ * @return Either value at `index` in the caller `ArrayList` or `none` if
+ * passed `index` is invalid.
+ */
+private final function AcediaObject BorrowItem(int index)
+{
+ if (ValidateIndex(index)) {
+ return storedObjects[index];
+ }
+}
+
+/**
+ * Returns item at `index`. If index is invalid, returns `none`.
+ *
+ * @param index Index of an item that `ArrayList` has to return.
+ * @return Either value at `index` in the caller `ArrayList` or `none` if
+ * passed `index` is invalid.
+ */
+public final function AcediaObject GetItem(int index)
+{
+ local AcediaObject result;
+
+ if (ValidateIndex(index))
+ {
+ result = storedObjects[index];
+ storedObjects[index] = none;
+ lifeVersions[index] = 0;
+ }
+ if (result != none) {
+ result.NewRef();
+ }
+ return result;
+}
+
+/**
+ * Changes `ArrayList`'s value at `index` to `item`.
+ *
+ * @param index Index, at which to change the value. If `ArrayList` is
+ * not long enough to hold it, it will be automatically expanded.
+ * If passed index is negative - method will do nothing.
+ * @param item Value to be set at a given index.
+ * @return Reference to the caller `ArrayList` to allow for method chaining.
+ */
+public final function ArrayList SetItem(int index, AcediaObject item)
+{
+ if (index < 0) {
+ return self;
+ }
+ if (index >= storedObjects.length) {
+ SetLength(index + 1);
+ }
+ else if (item != storedObjects[index]) {
+ FreeItem(index);
+ }
+ if (item != none && item.IsAllocated())
+ {
+ item.NewRef();
+ storedObjects[index] = item;
+ lifeVersions[index] = item.GetLifeVersion();
+ }
+ return self;
+}
+
+/**
+ * Creates a new instance of class `valueClass` and records it's value at index
+ * `index` in the caller `ArrayList`.
+ *
+ * @param index Index, at which to change the value. If `ArrayList`
+ * is not long enough to hold it, it will be automatically expanded.
+ * If passed index is negative - method will do nothing.
+ * @param valueClass Class of object to create. Will only be created if
+ * passed `index` is valid.
+ * @return Caller `ArrayList` to allow for method chaining.
+ */
+public final function ArrayList CreateItem(
+ int index,
+ class valueClass)
+{
+ local AcediaObject newObject;
+
+ if (index < 0) return self;
+ if (valueClass == none) return self;
+
+ newObject = AcediaObject(_.memory.Allocate(valueClass));
+ SetItem(index, newObject);
+ _.memory.Free(newObject);
+ return self;
+}
+
+/**
+ * Adds given `item` at the end of the `ArrayList`, expanding it by
+ * one item.
+ *
+ * @param item Item to be added at the end of the `ArrayList`.
+ * @return Reference to the caller `ArrayList` to allow for method chaining.
+ */
+public final function ArrayList AddItem(AcediaObject item)
+{
+ return SetItem(storedObjects.length, item);
+}
+
+/**
+ * Inserts given `item` at index `index` of the `ArrayList`,
+ * shifting all the items starting from `index` one position to the right.
+ *
+ * @param index Index at which to insert new item. Must belong to
+ * inclusive range `[0; self.GetLength()]`, otherwise method does nothing.
+ * @param item Item to insert.
+ * @return Reference to the caller `ArrayList` to allow for method chaining.
+ */
+public final function ArrayList InsertItem(int index, AcediaObject item)
+{
+ if (index < 0) return self;
+ if (index > storedObjects.length) return self;
+
+ Insert(index, 1);
+ SetItem(index, item);
+ return self;
+}
+
+/**
+ * Returns all occurrences of `item` in the caller `ArrayList`
+ * (optionally only first one).
+ *
+ * @param item Item that needs to be removed from a `ArrayList`.
+ * @param onlyFirstItem Set to `true` to only remove first occurrence.
+ * By default `false`, which means all occurrences will be removed.
+ * @return Reference to the caller `ArrayList` to allow for method chaining.
+ */
+public final function ArrayList RemoveItem(
+ AcediaObject item,
+ optional bool onlyFirstItem)
+{
+ local int i;
+ while (i < storedObjects.length)
+ {
+ if (AreEqual(storedObjects[i], item))
+ {
+ Remove(i, 1);
+ if (onlyFirstItem) {
+ return self;
+ }
+ }
+ else {
+ i += 1;
+ }
+ }
+ return self;
+}
+
+/**
+ * Finds first occurrence of `item` in caller `ArrayList` and returns
+ * it's index.
+ *
+ * @param item Item to find in `ArrayList`.
+ * @return Index of first occurrence of `item` in caller `ArrayList`.
+ * `-1` if `item` is not found.
+ */
+public final function int Find(AcediaObject item)
+{
+ local int i;
+ if (item != none && !item.IsAllocated()) {
+ item = none;
+ }
+ for (i = 0; i < storedObjects.length; i += 1)
+ {
+ if (AreEqual(storedObjects[i], item)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+protected function AcediaObject GetByText(BaseText key)
+{
+ local int index, consumed;
+ local Parser parser;
+ parser = _.text.Parse(key);
+ parser.MUnsignedInteger(index,,, consumed);
+ if (!parser.Ok())
+ {
+ parser.FreeSelf();
+ return none;
+ }
+ parser.FreeSelf();
+ return GetItem(index);
+}
+
+/**
+ * Checks if value recorded at a given `index` is `none`.
+ *
+ * @param index Index to check the value at.
+ * @return `true` if `none` value is recorded at `index` and
+ * `false` otherwise.
+ * In case `index` is out-of-bound, nothing is recorded there,
+ * not even `none`, so method will return `false`.
+ */
+public final function bool IsNone(int index)
+{
+ if (ValidateIndex(index)) {
+ return (storedObjects[index] == none);
+ }
+ return false;
+}
+
+/**
+ * Returns `bool` item at `index`. If index is invalid or
+ * stores a non-`bool` value, returns `defaultValue`.
+ *
+ * Referred value must be stored as `BoolBox` or `BoolRef`
+ * (or one of their sub-classes) for this method to work.
+ *
+ * @param index Index of a `bool` item that `ArrayList`
+ * has to return.
+ * @param defaultValue Value to return if there is either no item recorded
+ * at `index` or it has a wrong type.
+ * @return `bool` value at `index` in the caller `ArrayList`.
+ * `defaultValue` if passed `index` is invalid or non-`bool` value
+ * is stored there.
+ */
+public final function bool GetBool(int index, optional bool defaultValue)
+{
+ local AcediaObject result;
+ local BoolBox asBox;
+ local BoolRef asRef;
+ result = BorrowItem(index);
+ if (result == none) {
+ return defaultValue;
+ }
+ asBox = BoolBox(result);
+ if (asBox != none) {
+ return asBox.Get();
+ }
+ asRef = BoolRef(result);
+ if (asRef != none) {
+ return asRef.Get();
+ }
+ return defaultValue;
+}
+
+/**
+ * Changes `ArrayList`'s value at `index` to `value` that will be recorded
+ * as either `BoolBox` or `BoolRef`, depending of `asRef` optional parameter.
+ *
+ * @param index Index, at which to change the value. If `ArrayList` is
+ * not long enough to hold it, it will be automatically expanded.
+ * If passed index is negative - method will do nothing.
+ * @param value Value to be set at a given index.
+ * @param asRef Given `bool` value will be recorded as immutable `BoolBox`
+ * by default (`asRef == false`). Setting this parameter to `true` will
+ * make this method record it as a mutable `BoolRef`.
+ * @return Reference to the caller `ArrayList` to allow for method chaining.
+ */
+public final function ArrayList SetBool(
+ int index,
+ bool value,
+ optional bool asRef)
+{
+ if (asRef) {
+ SetItem(index, _.ref.bool(value));
+ }
+ else {
+ SetItem(index, _.box.bool(value));
+ }
+ return self;
+}
+
+/**
+ * Returns `byte` item at `index`. If index is invalid or
+ * stores a non-`byte` value, returns `defaultValue`.
+ *
+ * Referred value must be stored as `ByteBox` or `ByteRef`
+ * (or one of their sub-classes) for this method to work.
+ *
+ * @param index Index of a `byte` item that `ArrayList`
+ * has to return.
+ * @param defaultValue Value to return if there is either no item recorded
+ * at `index` or it has a wrong type.
+ * @return `byte` value at `index` in the caller `ArrayList`.
+ * `defaultValue` if passed `index` is invalid or non-`byte` value
+ * is stored there.
+ */
+public final function byte GetByte(int index, optional byte defaultValue)
+{
+ local AcediaObject result;
+ local ByteBox asBox;
+ local ByteRef asRef;
+ result = BorrowItem(index);
+ if (result == none) {
+ return defaultValue;
+ }
+ asBox = ByteBox(result);
+ if (asBox != none) {
+ return asBox.Get();
+ }
+ asRef = ByteRef(result);
+ if (asRef != none) {
+ return asRef.Get();
+ }
+ return defaultValue;
+}
+
+/**
+ * Changes `ArrayList`'s value at `index` to `value` that will be recorded
+ * as either `ByteBox` or `ByteRef`, depending of `asRef` optional parameter.
+ *
+ * @param index Index, at which to change the value. If `ArrayList` is
+ * not long enough to hold it, it will be automatically expanded.
+ * If passed index is negative - method will do nothing.
+ * @param value Value to be set at a given index.
+ * @param asRef Given `byte` value will be recorded as immutable `ByteBox`
+ * by default (`asRef == false`). Setting this parameter to `true` will
+ * make this method record it as a mutable `ByteRef`.
+ * @return Reference to the caller `ArrayList` to allow for method chaining.
+ */
+public final function ArrayList SetByte(
+ int index,
+ byte value,
+ optional bool asRef)
+{
+ if (asRef) {
+ SetItem(index, _.ref.byte(value));
+ }
+ else {
+ SetItem(index, _.box.byte(value));
+ }
+ return self;
+}
+
+/**
+ * Returns `int` item at `index`. If index is invalid or
+ * stores a non-`int` value, returns `defaultValue`.
+ *
+ * Referred value must be stored as `IntBox` or `IntRef`
+ * (or one of their sub-classes) for this method to work.
+ *
+ * @param index Index of a `int` item that `ArrayList`
+ * has to return.
+ * @param defaultValue Value to return if there is either no item recorded
+ * at `index` or it has a wrong type.
+ * @return `int` value at `index` in the caller `ArrayList`.
+ * `defaultValue` if passed `index` is invalid or non-`int` value
+ * is stored there.
+ */
+public final function int GetInt(int index, optional int defaultValue)
+{
+ local AcediaObject result;
+ local IntBox asBox;
+ local IntRef asRef;
+ result = BorrowItem(index);
+ if (result == none) {
+ return defaultValue;
+ }
+ asBox = IntBox(result);
+ if (asBox != none) {
+ return asBox.Get();
+ }
+ asRef = IntRef(result);
+ if (asRef != none) {
+ return asRef.Get();
+ }
+ return defaultValue;
+}
+
+/**
+ * Changes `ArrayList`'s value at `index` to `value` that will be recorded
+ * as either `IntBox` or `IntRef`, depending of `asRef` optional parameter.
+ *
+ * @param index Index, at which to change the value. If `ArrayList` is
+ * not long enough to hold it, it will be automatically expanded.
+ * If passed index is negative - method will do nothing.
+ * @param value Value to be set at a given index.
+ * @param asRef Given `int` value will be recorded as immutable `IntBox`
+ * by default (`asRef == false`). Setting this parameter to `true` will
+ * make this method record it as a mutable `IntRef`.
+ * @return Reference to the caller `ArrayList` to allow for method chaining.
+ */
+public final function ArrayList SetInt(
+ int index,
+ int value,
+ optional bool asRef)
+{
+ if (asRef) {
+ SetItem(index, _.ref.int(value));
+ }
+ else {
+ SetItem(index, _.box.int(value));
+ }
+ return self;
+}
+
+/**
+ * Returns `float` item at `index`. If index is invalid or
+ * stores a non-`int` value, returns `defaultValue`.
+ *
+ * Referred value must be stored as `FloatBox` or `FloatRef`
+ * (or one of their sub-classes) for this method to work.
+ *
+ * @param index Index of a `float` item that `ArrayList`
+ * has to return.
+ * @param defaultValue Value to return if there is either no item recorded
+ * at `index` or it has a wrong type.
+ * @return `float` value at `index` in the caller `ArrayList`.
+ * `defaultValue` if passed `index` is invalid or non-`float` value
+ * is stored there.
+ */
+public final function float GetFloat(int index, optional float defaultValue)
+{
+ local AcediaObject result;
+ local FloatBox asBox;
+ local FloatRef asRef;
+ result = BorrowItem(index);
+ if (result == none) {
+ return defaultValue;
+ }
+ asBox = FloatBox(result);
+ if (asBox != none) {
+ return asBox.Get();
+ }
+ asRef = FloatRef(result);
+ if (asRef != none) {
+ return asRef.Get();
+ }
+ return defaultValue;
+}
+
+/**
+ * Changes `ArrayList`'s value at `index` to `value` that will be recorded
+ * as either `FloatBox` or `FloatRef`, depending of `asRef` optional parameter.
+ *
+ * @param index Index, at which to change the value. If `ArrayList` is
+ * not long enough to hold it, it will be automatically expanded.
+ * If passed index is negative - method will do nothing.
+ * @param value Value to be set at a given index.
+ * @param asRef Given `float` value will be recorded as immutable `FloatBox`
+ * by default (`asRef == false`). Setting this parameter to `true` will
+ * make this method record it as a mutable `FloatRef`.
+ * @return Reference to the caller `ArrayList` to allow for method chaining.
+ */
+public final function ArrayList SetFloat(
+ int index,
+ float value,
+ optional bool asRef)
+{
+ if (asRef) {
+ SetItem(index, _.ref.float(value));
+ }
+ else {
+ SetItem(index, _.box.float(value));
+ }
+ return self;
+}
+
+/**
+ * Returns `BaseText` item at `index`. If index is invalid or
+ * stores a non-`BaseText` value, returns `none`.
+ *
+ * Referred value must be stored as `BaseText` (or one of it's sub-classes,
+ * such as `Text` or `MutableText`) for this method to work.
+ *
+ * @param index Index of a `BaseText` item that `ArrayList` has to return.
+ * @return `BaseText` value at `index` in the caller `ArrayList`.
+ * `none` if passed `index` is invalid or non-`BaseText` value
+ * is stored there.
+ */
+public final function BaseText GetBaseText(int index)
+{
+ local BaseText result;
+ result = BaseText(BorrowItem(index));
+ if (result != none) {
+ result.NewRef();
+ }
+ return result;
+}
+
+/**
+ * Returns `Text` item at `index`. If index is invalid or
+ * stores a non-`Text` value, returns `none`.
+ *
+ * Referred value must be stored as `Text` (or one of it's sub-classes,
+ * such as `MutableText`) for this method to work.
+ *
+ * @param index Index of a `Text` item that `ArrayList` has to return.
+ * @return `Text` value at `index` in the caller `ArrayList`.
+ * `none` if passed `index` is invalid or non-`Text` value
+ * is stored there.
+ */
+public final function Text GetText(int index)
+{
+ local Text result;
+ result = Text(BorrowItem(index));
+ if (result != none) {
+ result.NewRef();
+ }
+ return result;
+}
+
+/**
+ * Returns `MutableText` item at `index`. If index is invalid or
+ * stores a non-`Text` value, returns `none`.
+ *
+ * Referred value must be stored as `MutableText` for this method to work.
+ *
+ * @param index Index of a `MutableText` item that `ArrayList` will return.
+ * @return `MutableText` value at `index` in the caller `ArrayList`.
+ * `none` if passed `index` is invalid or non-`MutableText` value
+ * is stored there.
+ */
+public final function MutableText GetMutableText(int index)
+{
+ local MutableText result;
+ result = MutableText(BorrowItem(index));
+ if (result != none) {
+ result.NewRef();
+ }
+ return result;
+}
+
+/**
+ * Returns `AssociativeArray` item at `index`. If index is invalid or
+ * stores a non-`AssociativeArray` value, returns `none`.
+ *
+ * Referred value must be stored as `AssociativeArray`
+ * (or one of it's sub-classes) for this method to work.
+ *
+ * @param index Index of an `AssociativeArray` item that `ArrayList`
+ * has to return.
+ * @return `AssociativeArray` value at `index` in the caller `ArrayList`.
+ * `none` if passed `index` is invalid or non-`AssociativeArray` value
+ * is stored there.
+ */
+public final function AssociativeArray GetAssociativeArray(int index)
+{
+ local AssociativeArray result;
+ result = AssociativeArray(BorrowItem(index));
+ if (result != none) {
+ result.NewRef();
+ }
+ return result;
+}
+
+/**
+ * Returns `DynamicArray` item at `index`. If index is invalid or
+ * stores a non-`DynamicArray` value, returns `none`.
+ *
+ * Referred value must be stored as `Text` (or one of it's sub-classes,
+ * such as `MutableText`) for this method to work.
+ *
+ * @param index Index of a `DynamicArray` item that caller `ArrayList`
+ * has to return.
+ * @return `DynamicArray` value at `index` in the caller `ArrayList`.
+ * `none` if passed `index` is invalid or non-`ArrayList` value
+ * is stored there.
+ */
+public final function DynamicArray GetDynamicArray(int index)
+{
+ local DynamicArray result;
+ result = DynamicArray(BorrowItem(index));
+ if (result != none) {
+ result.NewRef();
+ }
+ return result;
+}
+
+/**
+ * Returns `ArrayList` item at `index`. If index is invalid or
+ * stores a non-`ArrayList` value, returns `none`.
+ *
+ * Referred value must be stored as `Text` (or one of it's sub-classes,
+ * such as `MutableText`) for this method to work.
+ *
+ * @param index Index of a `ArrayList` item that caller `ArrayList`
+ * has to return.
+ * @return `ArrayList` value at `index` in the caller `ArrayList`.
+ * `none` if passed `index` is invalid or non-`ArrayList` value
+ * is stored there.
+ */
+public final function ArrayList GetArrayList(int index)
+{
+ local ArrayList result;
+ result = ArrayList(BorrowItem(index));
+ if (result != none) {
+ result.NewRef();
+ }
+ return result;
+}
+
+defaultproperties
+{
+ iteratorClass = class'ArrayListIterator'
+}
\ No newline at end of file
diff --git a/sources/Data/Collections/ArrayListIterator.uc b/sources/Data/Collections/ArrayListIterator.uc
new file mode 100644
index 0000000..ee8fed2
--- /dev/null
+++ b/sources/Data/Collections/ArrayListIterator.uc
@@ -0,0 +1,76 @@
+/**
+ * Iterator for iterating over `ArrayList`'s items.
+ * Copyright 2022 Anton Tarasenko
+ *------------------------------------------------------------------------------
+ * This file is part of Acedia.
+ *
+ * Acedia 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Acedia 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 Acedia. If not, see .
+ */
+class ArrayListIterator extends Iter;
+
+var private ArrayList relevantCollection;
+var private int currentIndex;
+
+protected function Finalizer()
+{
+ relevantCollection = none;
+}
+
+public function bool Initialize(Collection relevantArray)
+{
+ currentIndex = 0;
+ relevantCollection = ArrayList(relevantArray);
+ if (relevantCollection == none) {
+ return false;
+ }
+ return true;
+}
+
+public function Iter Next(optional bool skipNone)
+{
+ local int collectionLength;
+ if (!skipNone)
+ {
+ currentIndex += 1;
+ return self;
+ }
+ collectionLength = relevantCollection.GetLength();
+ while (currentIndex < collectionLength)
+ {
+ currentIndex += 1;
+ if (!relevantCollection.IsNone(currentIndex)) {
+ return self;
+ }
+ }
+ return self;
+}
+
+public function AcediaObject Get()
+{
+ return relevantCollection.GetItem(currentIndex);
+}
+
+public function AcediaObject GetKey()
+{
+ return _.box.int(currentIndex);
+}
+
+public function bool HasFinished()
+{
+ return currentIndex >= relevantCollection.GetLength();
+}
+
+defaultproperties
+{
+}
\ No newline at end of file
diff --git a/sources/Data/Collections/CollectionsAPI.uc b/sources/Data/Collections/CollectionsAPI.uc
index 623a899..070426d 100644
--- a/sources/Data/Collections/CollectionsAPI.uc
+++ b/sources/Data/Collections/CollectionsAPI.uc
@@ -19,6 +19,41 @@
*/
class CollectionsAPI extends AcediaObject;
+/**
+ * Creates a new `ArrayList`, optionally filling it with objects from
+ * a given native array.
+ *
+ * @param objectArray Objects to place inside created `ArrayList`;
+ * if empty (by default) - new, empty `ArrayList` will be returned.
+ * Objects will be added in the same order as in `objectArray`.
+ * @param managed Flag that indicates whether objects from
+ * `objectArray` argument should be added as managed.
+ * By default `false` - they would not be managed.
+ * @return New `ArrayList`, optionally filled with contents of
+ * `objectArray`. Guaranteed to be not `none` and to not contain any items
+ * outside of `objectArray`.
+ */
+public final function ArrayList NewArrayList(array objectArray)
+{
+ local int i;
+ local ArrayList result;
+ result = ArrayList(_.memory.Allocate(class'ArrayList'));
+ for (i = 0; i < objectArray.length; i += 1) {
+ result.AddItem(objectArray[i]);
+ }
+ return result;
+}
+
+/**
+ * Creates a new empty `ArrayList`.
+ *
+ * @return New empty instance of `ArrayList`.
+ */
+public final function ArrayList EmptyArrayList()
+{
+ return ArrayList(_.memory.Allocate(class'ArrayList'));
+}
+
/**
* Creates a new `DynamicArray`, optionally filling it with objects from
* a given native array.