diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b0a593bb16d760950c71623f49e7349abd1df8ad..7f18407ac7d3eeaedae0541ceeab2c261010278a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -164,6 +164,7 @@ set(SRB2_CORE_GAME_SOURCES
 	p_telept.c
 	p_tick.c
 	p_user.c
+	taglist.c
 
 	p_local.h
 	p_maputl.h
@@ -175,6 +176,7 @@ set(SRB2_CORE_GAME_SOURCES
 	p_slopes.h
 	p_spec.h
 	p_tick.h
+	taglist.h
 )
 
 if(NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
diff --git a/src/Makefile b/src/Makefile
index fdf9c78b774b513ee5650654c32913e468d77d83..d27883563d444383ab13d41f15be267b157584dd 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -474,6 +474,7 @@ OBJS:=$(i_main_o) \
 		$(OBJDIR)/r_patch.o \
 		$(OBJDIR)/r_portal.o \
 		$(OBJDIR)/screen.o   \
+		$(OBJDIR)/taglist.o  \
 		$(OBJDIR)/v_video.o  \
 		$(OBJDIR)/s_sound.o  \
 		$(OBJDIR)/sounds.o   \
diff --git a/src/doomdata.h b/src/doomdata.h
index d9bfb43b14fd78a26b48fda27dea6fdb58d98f40..884c043a4f9d36f3df8d0f746f8ff6abe4b769e9 100644
--- a/src/doomdata.h
+++ b/src/doomdata.h
@@ -23,6 +23,8 @@
 // Some global defines, that configure the game.
 #include "doomdef.h"
 
+#include "taglist.h"
+
 //
 // Map level types.
 // The following data structures define the persistent format
@@ -204,6 +206,7 @@ typedef struct
 	INT16 z;
 	UINT8 extrainfo;
 	INT16 tag;
+	taglist_t tags;
 	struct mobj_s *mobj;
 } mapthing_t;
 
diff --git a/src/p_setup.c b/src/p_setup.c
index 871fb14fe3473b98bae10e1e778f5a93515edb02..e4658d7423ead0602071f9d1b6861601b69f2a83 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -81,6 +81,8 @@
 
 #include "fastcmp.h" // textmap parsing
 
+#include "taglist.h"
+
 //
 // Map MD5, calculated on level load.
 // Sent to clients in PT_SERVERINFO.
@@ -935,6 +937,8 @@ static void P_LoadSectors(UINT8 *data)
 		ss->lightlevel = SHORT(ms->lightlevel);
 		ss->special = SHORT(ms->special);
 		ss->tag = SHORT(ms->tag);
+		if (ss->tag)
+			Tag_Add(&ss->tags, ss->tag);
 
 		ss->floor_xoffs = ss->floor_yoffs = 0;
 		ss->ceiling_xoffs = ss->ceiling_yoffs = 0;
@@ -1049,6 +1053,8 @@ static void P_LoadLinedefs(UINT8 *data)
 		ld->flags = SHORT(mld->flags);
 		ld->special = SHORT(mld->special);
 		ld->tag = SHORT(mld->tag);
+		if (ld->tag)
+			Tag_Add(&ld->tags, ld->tag);
 		memset(ld->args, 0, NUMLINEARGS*sizeof(*ld->args));
 		memset(ld->stringargs, 0x00, NUMLINESTRINGARGS*sizeof(*ld->stringargs));
 		ld->alpha = FRACUNIT;
@@ -1398,7 +1404,21 @@ static void ParseTextmapSectorParameter(UINT32 i, char *param, char *val)
 	else if (fastcmp(param, "special"))
 		sectors[i].special = atol(val);
 	else if (fastcmp(param, "id"))
+	{
 		sectors[i].tag = atol(val);
+		if (sectors[i].tag)
+			Tag_Add(&sectors[i].tags, sectors[i].tag);
+	}
+	else if (fastcmp(param, "moreids"))
+	{
+		char* id = val;
+		while (id)
+		{
+			Tag_Add(&sectors[i].tags, atol(id));
+			if ((id = strchr(id, ' ')))
+				id++;
+		}
+	}
 	else if (fastcmp(param, "xpanningfloor"))
 		sectors[i].floor_xoffs = FLOAT_TO_FIXED(atof(val));
 	else if (fastcmp(param, "ypanningfloor"))
@@ -1434,7 +1454,21 @@ static void ParseTextmapSidedefParameter(UINT32 i, char *param, char *val)
 static void ParseTextmapLinedefParameter(UINT32 i, char *param, char *val)
 {
 	if (fastcmp(param, "id"))
+	{
 		lines[i].tag = atol(val);
+		if (lines[i].tag)
+			Tag_Add(&lines[i].tags, lines[i].tag);
+	}
+	else if (fastcmp(param, "moreids"))
+	{
+		char* id = val;
+		while (id)
+		{
+			Tag_Add(&lines[i].tags, atol(id));
+			if ((id = strchr(id, ' ')))
+				id++;
+		}
+	}
 	else if (fastcmp(param, "special"))
 		lines[i].special = atol(val);
 	else if (fastcmp(param, "v1"))
@@ -1501,8 +1535,22 @@ static void ParseTextmapLinedefParameter(UINT32 i, char *param, char *val)
 static void ParseTextmapThingParameter(UINT32 i, char *param, char *val)
 {
 	if (fastcmp(param, "id"))
+	{
 		mapthings[i].tag = atol(val);
-	if (fastcmp(param, "x"))
+		if (mapthings[i].tag)
+			Tag_Add(&mapthings[i].tags, mapthings[i].tag);
+	}
+	else if (fastcmp(param, "moreids"))
+	{
+		char* id = val;
+		while (id)
+		{
+			Tag_Add(&mapthings[i].tags, atol(id));
+			if ((id = strchr(id, ' ')))
+				id++;
+		}
+	}
+	else if (fastcmp(param, "x"))
 		mapthings[i].x = atol(val);
 	else if (fastcmp(param, "y"))
 		mapthings[i].y = atol(val);
diff --git a/src/r_defs.h b/src/r_defs.h
index 943f54761c3df84c1a0f65209eae85b2ab496629..ab35b205503d611cb1194ce65b7f1c7958065dca 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -30,6 +30,8 @@
 
 #define POLYOBJECTS
 
+#include "taglist.h"
+
 //
 // ClipWallSegment
 // Clips the given range of columns
@@ -290,6 +292,7 @@ typedef struct sector_s
 	INT16 lightlevel;
 	INT16 special;
 	UINT16 tag;
+	taglist_t tags;
 	INT32 nexttag, firsttag; // for fast tag searches
 
 	// origin for any sounds played by the sector
@@ -413,6 +416,7 @@ typedef struct line_s
 	INT16 flags;
 	INT16 special;
 	INT16 tag;
+	taglist_t tags;
 	INT32 args[NUMLINEARGS];
 	char *stringargs[NUMLINESTRINGARGS];
 
diff --git a/src/taglist.c b/src/taglist.c
new file mode 100644
index 0000000000000000000000000000000000000000..9090d4e0b6d079626ef34cb7b0807990c92a6739
--- /dev/null
+++ b/src/taglist.c
@@ -0,0 +1,8 @@
+#include "taglist.h"
+#include "z_zone.h"
+
+void Tag_Add (taglist_t* list, const UINT16 tag)
+{
+	list->tags = Z_Realloc(list->tags, (list->count + 1) * sizeof(list->tags), PU_LEVEL, NULL);
+	list->tags[list->count++] = tag;
+}
diff --git a/src/taglist.h b/src/taglist.h
new file mode 100644
index 0000000000000000000000000000000000000000..744918d7c88a526bc8115a454dbf3e9bb4ab6336
--- /dev/null
+++ b/src/taglist.h
@@ -0,0 +1,14 @@
+#include "doomtype.h"
+
+#ifndef __R_TAGLIST__
+#define __R_TAGLIST__
+
+/// Multitag list.
+typedef struct
+{
+	UINT16* tags;
+	UINT16 count;
+} taglist_t;
+
+void Tag_Add (taglist_t* list, const UINT16 tag);
+#endif //__R_TAGLIST__