From de4153ceafaab72d547826ed98877b6b820fdb13 Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@users.noreply.github.com>
Date: Wed, 11 Jan 2017 00:57:31 +0100
Subject: [PATCH] some groundwork for the implementation of dynamic arrays

- created script exports for all relevant functions with all integral types.
- created script side definitions for the underlying data types.
- added a void pointer type so that the prototype for the pointer array can use a generic type every pointer can be assigned to.
---
 src/CMakeLists.txt                         |   1 +
 src/dobjtype.cpp                           |   2 +
 src/dobjtype.h                             |   1 +
 src/g_inventory/a_artifacts.cpp            |   2 +-
 src/namedef.h                              |   1 +
 src/scripting/codegeneration/codegen.cpp   |   7 +-
 src/scripting/codegeneration/dynarrays.cpp | 784 +++++++++++++++++++++
 src/scripting/thingdef_data.cpp            |   3 -
 src/scripting/zscript/zcc_compile.cpp      |   4 +
 src/tarray.h                               |   2 +-
 wadsrc/static/zscript.txt                  |   1 +
 wadsrc/static/zscript/actor.txt            |   4 +-
 wadsrc/static/zscript/dynarrays.txt        | 135 ++++
 13 files changed, 938 insertions(+), 9 deletions(-)
 create mode 100644 src/scripting/codegeneration/dynarrays.cpp
 create mode 100644 wadsrc/static/zscript/dynarrays.txt

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f06687988..633868049 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1226,6 +1226,7 @@ set (PCH_SOURCES
 	scripting/thingdef_data.cpp
 	scripting/thingdef_properties.cpp
 	scripting/codegeneration/codegen.cpp
+	scripting/codegeneration/dynarrays.cpp
 	scripting/decorate/olddecorations.cpp
 	scripting/decorate/thingdef_exp.cpp
 	scripting/decorate/thingdef_parse.cpp
diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp
index 9a1c9f83a..3abdd5800 100644
--- a/src/dobjtype.cpp
+++ b/src/dobjtype.cpp
@@ -94,6 +94,7 @@ PStruct *TypeVector3;
 PStruct *TypeColorStruct;
 PStruct *TypeStringStruct;
 PPointer *TypeNullPtr;
+PPointer *TypeVoidPtr;
 
 // PRIVATE DATA DEFINITIONS ------------------------------------------------
 
@@ -593,6 +594,7 @@ void PType::StaticInit()
 	TypeTable.AddType(TypeSpriteID = new PSpriteID);
 	TypeTable.AddType(TypeTextureID = new PTextureID);
 
+	TypeVoidPtr = NewPointer(TypeVoid, false);
 	TypeColorStruct = NewStruct("@ColorStruct", nullptr);	//This name is intentionally obfuscated so that it cannot be used explicitly. The point of this type is to gain access to the single channels of a color value.
 	TypeStringStruct = NewNativeStruct(NAME_String, nullptr);
 #ifdef __BIG_ENDIAN__
diff --git a/src/dobjtype.h b/src/dobjtype.h
index a6dd69a6e..d31dac3ce 100644
--- a/src/dobjtype.h
+++ b/src/dobjtype.h
@@ -965,6 +965,7 @@ extern PStruct *TypeStringStruct;
 extern PStatePointer *TypeState;
 extern PStateLabel *TypeStateLabel;
 extern PPointer *TypeNullPtr;
+extern PPointer *TypeVoidPtr;
 
 // A constant value ---------------------------------------------------------
 
diff --git a/src/g_inventory/a_artifacts.cpp b/src/g_inventory/a_artifacts.cpp
index 2f2cc3c0e..e67e1247f 100644
--- a/src/g_inventory/a_artifacts.cpp
+++ b/src/g_inventory/a_artifacts.cpp
@@ -204,7 +204,7 @@ void APowerup::CallInitEffect()
 
 //===========================================================================
 //
-// APowerup :: isBlinking
+// APowerup :: isBlinking (todo: make this virtual so that child classes can configure their blinking)
 //
 //===========================================================================
 
diff --git a/src/namedef.h b/src/namedef.h
index 778d1c327..13ebf78ca 100644
--- a/src/namedef.h
+++ b/src/namedef.h
@@ -757,6 +757,7 @@ xx(DamageFunction)
 xx(Length)
 xx(Unit)
 xx(Size)
+xx(Voidptr)
 xx(StateLabel)
 xx(SpriteID)
 xx(TextureID)
diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp
index d5dbbdbfa..d8c1aa3dd 100644
--- a/src/scripting/codegeneration/codegen.cpp
+++ b/src/scripting/codegeneration/codegen.cpp
@@ -262,12 +262,15 @@ static bool AreCompatiblePointerTypes(PType *dest, PType *source, bool forcompar
 {
 	if (dest->IsKindOf(RUNTIME_CLASS(PPointer)) && source->IsKindOf(RUNTIME_CLASS(PPointer)))
 	{
-		// Pointers to different types are only compatible if both point to an object and the source type is a child of the destination type.
 		auto fromtype = static_cast<PPointer *>(source);
 		auto totype = static_cast<PPointer *>(dest);
-		if (fromtype == nullptr) return true;
+		// null pointers can be assigned to everything, everything can be assigned to void pointers.
+		if (fromtype == nullptr || totype == TypeVoidPtr) return true;
+		// when comparing const-ness does not matter.
 		if (!forcompare && totype->IsConst != fromtype->IsConst) return false;
+		// A type is always compatible to itself.
 		if (fromtype == totype) return true;
+		// Pointers to different types are only compatible if both point to an object and the source type is a child of the destination type.
 		if (fromtype->PointedType->IsKindOf(RUNTIME_CLASS(PClass)) && totype->PointedType->IsKindOf(RUNTIME_CLASS(PClass)))
 		{
 			auto fromcls = static_cast<PClass *>(fromtype->PointedType);
diff --git a/src/scripting/codegeneration/dynarrays.cpp b/src/scripting/codegeneration/dynarrays.cpp
new file mode 100644
index 000000000..4489bbcb2
--- /dev/null
+++ b/src/scripting/codegeneration/dynarrays.cpp
@@ -0,0 +1,784 @@
+/*
+** dynarray.cpp
+**
+** Compiler backend / code generation for dynamic arrays
+**
+**---------------------------------------------------------------------------
+** Copyright 2016 Christoph Oelckers
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+**
+** 1. Redistributions of source code must retain the above copyright
+**    notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+**    notice, this list of conditions and the following disclaimer in the
+**    documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+**    derived from this software without specific prior written permission.
+** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
+**    covered by the terms of the GNU General Public License as published by
+**    the Free Software Foundation; either version 2 of the License, or (at
+**    your option) any later version.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**---------------------------------------------------------------------------
+**
+*/
+
+#define private public	// this file needs public access to TArray's private 'Count' field so just hack the visibility qualifiers here
+#include "tarray.h"
+#undef private
+#include "dobject.h"
+#include "thingdef.h"
+// We need one specific type for each of the 7 integral VM types and instantiate the needed functions for each of them.
+// Dynamic arrays cannot hold structs because for every type there'd need to be an internal implementation which is impossible.
+
+typedef TArray<uint8_t> FDynArray_I8;
+typedef TArray<uint16_t> FDynArray_I16;
+typedef TArray<uint32_t> FDynArray_I32;
+typedef TArray<float> FDynArray_F32;
+typedef TArray<double> FDynArray_F64;
+typedef TArray<void*> FDynArray_Ptr;
+typedef TArray<FString> FDynArray_String;
+
+//-----------------------------------------------------
+//
+// Int8 array
+//
+//-----------------------------------------------------
+
+DEFINE_ACTION_FUNCTION(FDynArray_I8, Copy)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
+	PARAM_POINTER(other, FDynArray_I8);
+	*self = *other;
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I8, Move)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
+	PARAM_POINTER(other, FDynArray_I8);
+	*self = std::move(*other);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I8, Find)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
+	PARAM_INT(val);
+	ACTION_RETURN_INT(self->Find(val));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I8, Push)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
+	PARAM_INT(val);
+	ACTION_RETURN_INT(self->Push(val));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I8, Pop)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
+	ACTION_RETURN_BOOL(self->Pop());
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I8, Delete)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
+	PARAM_INT(index);
+	PARAM_INT(count);
+	self->Delete(index, count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I8, Insert)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
+	PARAM_INT(index);
+	PARAM_INT(val);
+	self->Insert(index, val);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I8, ShrinkToFit)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
+	self->ShrinkToFit();
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I8, Grow)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
+	PARAM_INT(count);
+	self->Grow(count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I8, Resize)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
+	PARAM_INT(count);
+	self->Resize(count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I8, Reserve)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
+	PARAM_INT(count);
+	ACTION_RETURN_INT(self->Reserve(count));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I8, Max)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
+	ACTION_RETURN_INT(self->Max());
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I8, Clear)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
+	self->Clear();
+	return 0;
+}
+
+//-----------------------------------------------------
+//
+// Int16 array
+//
+//-----------------------------------------------------
+
+DEFINE_ACTION_FUNCTION(FDynArray_I16, Copy)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
+	PARAM_POINTER(other, FDynArray_I16);
+	*self = *other;
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I16, Move)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
+	PARAM_POINTER(other, FDynArray_I16);
+	*self = std::move(*other);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I16, Find)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
+	PARAM_INT(val);
+	ACTION_RETURN_INT(self->Find(val));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I16, Push)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
+	PARAM_INT(val);
+	ACTION_RETURN_INT(self->Push(val));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I16, Pop)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
+	ACTION_RETURN_BOOL(self->Pop());
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I16, Delete)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
+	PARAM_INT(index);
+	PARAM_INT(count);
+	self->Delete(index, count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I16, Insert)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
+	PARAM_INT(index);
+	PARAM_INT(val);
+	self->Insert(index, val);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I16, ShrinkToFit)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
+	self->ShrinkToFit();
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I16, Grow)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
+	PARAM_INT(count);
+	self->Grow(count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I16, Resize)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
+	PARAM_INT(count);
+	self->Resize(count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I16, Reserve)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
+	PARAM_INT(count);
+	ACTION_RETURN_INT(self->Reserve(count));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I16, Max)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
+	ACTION_RETURN_INT(self->Max());
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I16, Clear)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
+	self->Clear();
+	return 0;
+}
+
+//-----------------------------------------------------
+//
+// Int32 array
+//
+//-----------------------------------------------------
+
+DEFINE_ACTION_FUNCTION(FDynArray_I32, Copy)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
+	PARAM_POINTER(other, FDynArray_I32);
+	*self = *other;
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I32, Move)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
+	PARAM_POINTER(other, FDynArray_I32);
+	*self = std::move(*other);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I32, Find)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
+	PARAM_INT(val);
+	ACTION_RETURN_INT(self->Find(val));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I32, Push)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
+	PARAM_INT(val);
+	ACTION_RETURN_INT(self->Push(val));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I32, Pop)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
+	ACTION_RETURN_BOOL(self->Pop());
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I32, Delete)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
+	PARAM_INT(index);
+	PARAM_INT(count);
+	self->Delete(index, count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I32, Insert)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
+	PARAM_INT(index);
+	PARAM_INT(val);
+	self->Insert(index, val);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I32, ShrinkToFit)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
+	self->ShrinkToFit();
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I32, Grow)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
+	PARAM_INT(count);
+	self->Grow(count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I32, Resize)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
+	PARAM_INT(count);
+	self->Resize(count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I32, Reserve)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
+	PARAM_INT(count);
+	ACTION_RETURN_INT(self->Reserve(count));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I32, Max)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
+	ACTION_RETURN_INT(self->Max());
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_I32, Clear)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
+	self->Clear();
+	return 0;
+}
+
+//-----------------------------------------------------
+//
+// Float32 array
+//
+//-----------------------------------------------------
+
+DEFINE_ACTION_FUNCTION(FDynArray_F32, Copy)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
+	PARAM_POINTER(other, FDynArray_F32);
+	*self = *other;
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F32, Move)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
+	PARAM_POINTER(other, FDynArray_F32);
+	*self = std::move(*other);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F32, Find)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
+	PARAM_FLOAT(val);
+	ACTION_RETURN_INT(self->Find((float)val));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F32, Push)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
+	PARAM_FLOAT(val);
+	ACTION_RETURN_INT(self->Push((float)val));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F32, Pop)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
+	ACTION_RETURN_BOOL(self->Pop());
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F32, Delete)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
+	PARAM_INT(index);
+	PARAM_INT(count);
+	self->Delete(index, count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F32, Insert)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
+	PARAM_INT(index);
+	PARAM_FLOAT(val);
+	self->Insert(index, (float)val);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F32, ShrinkToFit)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
+	self->ShrinkToFit();
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F32, Grow)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
+	PARAM_INT(count);
+	self->Grow(count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F32, Resize)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
+	PARAM_INT(count);
+	self->Resize(count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F32, Reserve)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
+	PARAM_INT(count);
+	ACTION_RETURN_INT(self->Reserve(count));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F32, Max)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
+	ACTION_RETURN_INT(self->Max());
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F32, Clear)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
+	self->Clear();
+	return 0;
+}
+
+//-----------------------------------------------------
+//
+// Float64 array
+//
+//-----------------------------------------------------
+
+DEFINE_ACTION_FUNCTION(FDynArray_F64, Copy)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
+	PARAM_POINTER(other, FDynArray_F64);
+	*self = *other;
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F64, Move)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
+	PARAM_POINTER(other, FDynArray_F64);
+	*self = std::move(*other);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F64, Find)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
+	PARAM_FLOAT(val);
+	ACTION_RETURN_INT(self->Find(val));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F64, Push)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
+	PARAM_FLOAT(val);
+	ACTION_RETURN_INT(self->Push(val));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F64, Pop)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
+	ACTION_RETURN_BOOL(self->Pop());
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F64, Delete)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
+	PARAM_INT(index);
+	PARAM_INT(count);
+	self->Delete(index, count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F64, Insert)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
+	PARAM_INT(index);
+	PARAM_FLOAT(val);
+	self->Insert(index, val);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F64, ShrinkToFit)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
+	self->ShrinkToFit();
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F64, Grow)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
+	PARAM_INT(count);
+	self->Grow(count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F64, Resize)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
+	PARAM_INT(count);
+	self->Resize(count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F64, Reserve)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
+	PARAM_INT(count);
+	ACTION_RETURN_INT(self->Reserve(count));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F64, Max)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
+	ACTION_RETURN_INT(self->Max());
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_F64, Clear)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
+	self->Clear();
+	return 0;
+}
+
+//-----------------------------------------------------
+//
+// Pointer array
+//
+//-----------------------------------------------------
+
+DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Copy)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
+	PARAM_POINTER(other, FDynArray_Ptr);
+	*self = *other;
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Move)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
+	PARAM_POINTER(other, FDynArray_Ptr);
+	*self = std::move(*other);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Find)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
+	PARAM_POINTER(val, void);
+	ACTION_RETURN_INT(self->Find(val));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Push)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
+	PARAM_POINTER(val, void);
+	ACTION_RETURN_INT(self->Push(val));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Pop)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
+	ACTION_RETURN_BOOL(self->Pop());
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Delete)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
+	PARAM_INT(index);
+	PARAM_INT(count);
+	self->Delete(index, count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Insert)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
+	PARAM_INT(index);
+	PARAM_POINTER(val, void);
+	self->Insert(index, val);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_Ptr, ShrinkToFit)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
+	self->ShrinkToFit();
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Grow)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
+	PARAM_INT(count);
+	self->Grow(count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Resize)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
+	PARAM_INT(count);
+	self->Resize(count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Reserve)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
+	PARAM_INT(count);
+	ACTION_RETURN_INT(self->Reserve(count));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Max)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
+	ACTION_RETURN_INT(self->Max());
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Clear)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
+	self->Clear();
+	return 0;
+}
+
+
+//-----------------------------------------------------
+//
+// String array
+//
+//-----------------------------------------------------
+
+DEFINE_ACTION_FUNCTION(FDynArray_String, Copy)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
+	PARAM_POINTER(other, FDynArray_String);
+	*self = *other;
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_String, Move)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
+	PARAM_POINTER(other, FDynArray_String);
+	*self = std::move(*other);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_String, Find)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
+	PARAM_STRING(val);
+	ACTION_RETURN_INT(self->Find(val));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_String, Push)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
+	PARAM_STRING(val);
+	ACTION_RETURN_INT(self->Push(val));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_String, Pop)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
+	ACTION_RETURN_BOOL(self->Pop());
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_String, Delete)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
+	PARAM_INT(index);
+	PARAM_INT(count);
+	self->Delete(index, count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_String, Insert)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
+	PARAM_INT(index);
+	PARAM_STRING(val);
+	self->Insert(index, val);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_String, ShrinkToFit)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
+	self->ShrinkToFit();
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_String, Grow)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
+	PARAM_INT(count);
+	self->Grow(count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_String, Resize)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
+	PARAM_INT(count);
+	self->Resize(count);
+	return 0;
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_String, Reserve)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
+	PARAM_INT(count);
+	ACTION_RETURN_INT(self->Reserve(count));
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_String, Max)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
+	ACTION_RETURN_INT(self->Max());
+}
+
+DEFINE_ACTION_FUNCTION(FDynArray_String, Clear)
+{
+	PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
+	self->Clear();
+	return 0;
+}
+
+DEFINE_FIELD_NAMED(FDynArray_I8, Count, Size)		// clashes with type 'Inventory'.
+DEFINE_FIELD_NAMED(FDynArray_I16, Count, Size)		// clashes with type 'Inventory'.
+DEFINE_FIELD_NAMED(FDynArray_I32, Count, Size)		// clashes with type 'Inventory'.
+DEFINE_FIELD_NAMED(FDynArray_F32, Count, Size)		// clashes with type 'Inventory'.
+DEFINE_FIELD_NAMED(FDynArray_F64, Count, Size)		// clashes with type 'Inventory'.
+DEFINE_FIELD_NAMED(FDynArray_Ptr, Count, Size)		// clashes with type 'Inventory'.
+DEFINE_FIELD_NAMED(FDynArray_String, Count, Size)		// clashes with type 'Inventory'.
diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp
index a1f2786da..f57509d81 100644
--- a/src/scripting/thingdef_data.cpp
+++ b/src/scripting/thingdef_data.cpp
@@ -796,9 +796,6 @@ void InitThingdef()
 	playerf = new PField("WP_NOCHANGE", NewPointer(RUNTIME_CLASS(AWeapon), false), VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&wpnochg);
 	GlobalSymbols.AddSymbol(playerf);
 
-	// this needs to be done manually until it can be given a proper type.
-	RUNTIME_CLASS(AActor)->AddNativeField("DecalGenerator", NewPointer(TypeVoid), myoffsetof(AActor, DecalGenerator));
-
 	// synthesize a symbol for each flag from the flag name tables to avoid redundant declaration of them.
 	for (auto &fl : FlagLists)
 	{
diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp
index 52cbc1d65..13a21eb88 100644
--- a/src/scripting/zscript/zcc_compile.cpp
+++ b/src/scripting/zscript/zcc_compile.cpp
@@ -1457,6 +1457,10 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
 			// statelabel et.al. are not tokens - there really is no need to, it works just as well as an identifier. Maybe the same should be done for some other types, too?
 			switch (btype->UserType->Id)
 			{
+			case NAME_Voidptr:
+				retval = TypeVoidPtr;
+				break;
+
 			case NAME_StateLabel:
 				retval = TypeStateLabel;
 				break;
diff --git a/src/tarray.h b/src/tarray.h
index 3ee1c5cb9..fc9a62587 100644
--- a/src/tarray.h
+++ b/src/tarray.h
@@ -398,8 +398,8 @@ public:
 	}
 private:
 	T *Array;
-	unsigned int Most;
 	unsigned int Count;
+	unsigned int Most;
 
 	void DoCopy (const TArray<T> &other)
 	{
diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt
index d9e60f248..39a26a2f6 100644
--- a/wadsrc/static/zscript.txt
+++ b/wadsrc/static/zscript.txt
@@ -1,4 +1,5 @@
 #include "zscript/base.txt"
+#include "zscript/dynarrays.txt"
 #include "zscript/constants.txt"
 #include "zscript/actor.txt"
 #include "zscript/actor_checks.txt"
diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt
index 8370f9637..b71b8126a 100644
--- a/wadsrc/static/zscript/actor.txt
+++ b/wadsrc/static/zscript/actor.txt
@@ -45,7 +45,7 @@ class Actor : Thinker native
 	native TextureID ceilingpic;
 	native double Height;
 	native readonly double Radius;
-        native readonly double RenderRadius;
+    native readonly double RenderRadius;
 	native double projectilepassheight;
 	native int tics;
 	native readonly State CurState;
@@ -149,6 +149,7 @@ class Actor : Thinker native
 	native readonly State SeeState;
 	native State MeleeState;
 	native State MissileState;
+	native voidptr /*DecalBase*/ DecalGenerator;
 	
 	native meta String Obituary;		// Player was killed by this actor
 	native meta String HitObituary;		// Player was killed by this actor in melee
@@ -177,7 +178,6 @@ class Actor : Thinker native
 	//FRenderStyle RenderStyle;
 	//line_t *BlockingLine; // Line that blocked the last move
 	//int ConversationRoot; // THe root of the current dialogue
-	//DecalBase DecalGenerator;
 
 	// deprecated things.
 	native readonly deprecated double X;
diff --git a/wadsrc/static/zscript/dynarrays.txt b/wadsrc/static/zscript/dynarrays.txt
new file mode 100644
index 000000000..f1ff85afb
--- /dev/null
+++ b/wadsrc/static/zscript/dynarrays.txt
@@ -0,0 +1,135 @@
+// The VM uses 7 integral data types, so for dynamic array support we need one specific set of functions for each of these types.
+// Do not use these structs directly, they are incomplete and only needed to create prototypes for the needed functions.
+
+struct DynArray_I8 native
+{
+	native readonly uint Size;
+	
+	native void Copy(DynArray_I8 other);
+	native void Move(DynArray_I8 other);
+    native uint Find(int item);
+	native uint Push (int item);
+	native bool Pop ();
+	native void Delete (uint index, int deletecount = 1);
+	native void Insert (uint index, int item);
+	native void ShrinkToFit ();
+	native void Grow (uint amount);
+	native void Resize (uint amount);
+	native uint Reserve (uint amount);
+	native uint Max ();
+	native void Clear ();
+}
+
+struct DynArray_I16 native
+{
+	native readonly uint Size;
+
+	native void Copy(DynArray_I16 other);
+	native void Move(DynArray_I16 other);
+    native uint Find(int item);
+	native uint Push (int item);
+	native bool Pop ();
+	native void Delete (uint index, int deletecount = 1);
+	native void Insert (uint index, int item);
+	native void ShrinkToFit ();
+	native void Grow (uint amount);
+	native void Resize (uint amount);
+	native uint Reserve (uint amount);
+	native uint Max ();
+	native void Clear ();
+}
+
+struct DynArray_I32 native
+{
+	native readonly uint Size;
+
+	native void Copy(DynArray_I32 other);
+	native void Move(DynArray_I32 other);
+    native uint Find(int item);
+	native uint Push (int item);
+	native bool Pop ();
+	native void Delete (uint index, int deletecount = 1);
+	native void Insert (uint index, int item);
+	native void ShrinkToFit ();
+	native void Grow (uint amount);
+	native void Resize (uint amount);
+	native uint Reserve (uint amount);
+	native uint Max ();
+	native void Clear ();
+}
+
+struct DynArray_F32 native
+{
+	native readonly uint Size;
+	
+	native void Copy(DynArray_F32 other);
+	native void Move(DynArray_F32 other);
+    native uint Find(double item);
+	native uint Push (double item);
+	native bool Pop ();
+	native void Delete (uint index, int deletecount = 1);
+	native void Insert (uint index, double item);
+	native void ShrinkToFit ();
+	native void Grow (uint amount);
+	native void Resize (uint amount);
+	native uint Reserve (uint amount);
+	native uint Max ();
+	native void Clear ();
+}
+
+struct DynArray_F64 native
+{
+	native readonly uint Size;
+	
+	native void Copy(DynArray_F64 other);
+	native void Move(DynArray_F64 other);
+    native uint Find(double item);
+	native uint Push (double item);
+	native bool Pop ();
+	native void Delete (uint index, int deletecount = 1);
+	native void Insert (uint index, double item);
+	native void ShrinkToFit ();
+	native void Grow (uint amount);
+	native void Resize (uint amount);
+	native uint Reserve (uint amount);
+	native uint Max ();
+	native void Clear ();
+}
+
+struct DynArray_Ptr native
+{
+	native readonly uint Size;
+	
+	native void Copy(DynArray_Ptr other);
+	native void Move(DynArray_Ptr other);
+    native uint Find(voidptr item);
+	native uint Push (voidptr item);
+	native bool Pop ();
+	native void Delete (uint index, int deletecount = 1);
+	native void Insert (uint index, voidptr item);
+	native void ShrinkToFit ();
+	native void Grow (uint amount);
+	native void Resize (uint amount);
+	native uint Reserve (uint amount);
+	native uint Max ();
+	native void Clear ();
+}
+
+struct DynArray_String native
+{
+	native readonly uint Size;
+
+	native void Copy(DynArray_String other);
+	native void Move(DynArray_String other);
+    native uint Find(String item);
+	native uint Push (String item);
+	native bool Pop ();
+	native void Delete (uint index, int deletecount = 1);
+	native void Insert (uint index, String item);
+	native void ShrinkToFit ();
+	native void Grow (uint amount);
+	native void Resize (uint amount);
+	native uint Reserve (uint amount);
+	native uint Max ();
+	native void Clear ();
+}