diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c6a3ce799db5690be895b5f73c71bbed698ae8a6
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,33 @@
+language: c
+sudo: required
+dist: trusty
+
+env:
+- CFLAGS=-Wno-absolute-value -Werror
+
+compiler:
+  - gcc
+  - clang
+
+cache:
+  directories:
+    - $HOME/srb2_cache
+
+addons:
+  apt:
+    packages:
+    - libsdl2-mixer-dev
+    - libpng-dev
+    - libgl1-mesa-dev
+    - libgme-dev
+    - p7zip-full
+
+before_script:
+  - mkdir $HOME/srb2_cache
+  - wget http://rosenthalcastle.org/srb2/SRB2-v2114-Installer.exe -c -O $HOME/srb2_cache/SRB2-v2114-Installer.exe
+  - 7z x $HOME/srb2_cache/SRB2-v2114-Installer.exe -oassets
+  - mkdir build
+  - cd build
+  - cmake ..
+
+script: make
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b8fe0ab5731f219e3661c9f8316c873986ec98d5..738e5332d7f3ac9926b46e397a122e2a66051ede 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -80,9 +80,6 @@ endif()
 
 if(${CMAKE_SYSTEM} MATCHES "Darwin")
 	add_definitions(-DMACOSX)
-	if(${CMAKE_C_COMPILER_ID} MATCHES "Clang")
-		set(CLANG ON)
-	endif()
 endif()
 
 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index bb4f9a4a6f514ed1a098c2e5f38b4f8c08fd6917..54a08ea0bb31b03d23c3758b37874dc99f3fdcd0 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -168,7 +168,7 @@ set(SRB2_CORE_GAME_SOURCES
 	p_tick.h
 )
 
-if(NOT CLANG)
+if(NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
 	set(SRB2_CORE_SOURCES ${SRB2_CORE_SOURCES} string.c)
 endif()
 
@@ -406,10 +406,14 @@ endif()
 
 # Compatibility flag with later versions of GCC
 # We should really fix our code to not need this
-if(NOT CLANG AND NOT MSVC)
+if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
 	set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -mno-ms-bitfields)
 endif()
 
+if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+	set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -Wno-absolute-value)
+endif()
+
 add_definitions(-DCMAKECONFIG)
 
 #add_library(SRB2Core STATIC
@@ -431,4 +435,4 @@ endif()
 
 if(NOT ${SRB2_SDL2_AVAILABLE} AND NOT ${SRB2_WIN32_AVAILABLE})
 	message(FATAL_ERROR "There are no targets available to build an SRB2 executable. :(")
-endif()
\ No newline at end of file
+endif()
diff --git a/src/b_bot.c b/src/b_bot.c
index 5e62e58e6d38230f82b874fddd4cec5a9f8e0aff..3072b1d75785d2b2834760c166e08fb10f28e99f 100644
--- a/src/b_bot.c
+++ b/src/b_bot.c
@@ -49,7 +49,7 @@ static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cm
 		if (sonic->player->pflags & (PF_MACESPIN|PF_ITEMHANG))
 		{
 			cmd->forwardmove = sonic->player->cmd.forwardmove;
-			cmd->angleturn = abs(tails->angle - sonic->angle)>>16;
+			cmd->angleturn = abs((tails->angle - sonic->angle))>>16;
 			if (sonic->angle < tails->angle)
 				cmd->angleturn = -cmd->angleturn;
 		} else if (dist > FixedMul(512*FRACUNIT, tails->scale))
diff --git a/src/f_finale.c b/src/f_finale.c
index a85fd11cb0438fb3950401339007f55cb8f71858..507616f3ca37c841802d6ec455dcebc9f7adf539 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -603,7 +603,7 @@ static void F_IntroDrawScene(void)
 
 				if (finalecount-84 < 58) { // Pure Fat is driving up!
 					int ftime = (finalecount-84);
-					x = (-189<<FRACBITS) + (FixedMul((6<<FRACBITS)+FRACUNIT/3, ftime<<FRACBITS) - FixedMul((6<<FRACBITS)+FRACUNIT/3, FixedDiv(FixedMul(ftime<<FRACBITS, ftime<<FRACBITS), 120<<FRACBITS)));
+					x = (-189*FRACUNIT) + (FixedMul((6<<FRACBITS)+FRACUNIT/3, ftime<<FRACBITS) - FixedMul((6<<FRACBITS)+FRACUNIT/3, FixedDiv(FixedMul(ftime<<FRACBITS, ftime<<FRACBITS), 120<<FRACBITS)));
 					y = (BASEVIDHEIGHT<<FRACBITS) - FixedMul(417<<FRACBITS, aspect);
 					// Draw the body
 					V_DrawSciencePatch(x, y, V_SNAPTOLEFT|V_SNAPTOBOTTOM, (patch = W_CachePatchName("PUREFAT1", PU_CACHE)), aspect);
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 6a315d88171b61b50371c89731a6d3a2d6b2879e..b71cfb66194010cd2405ed04f43f2810d855d8df 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1346,6 +1346,248 @@ static void HWR_GetBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, con
 	Z_ChangeTag(newmip->grInfo.data, PU_HWRCACHE_UNLOCKED);
 }
 
+static void HWR_CreateBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, GLMipmap_t *grmip, skincolors_t color)
+{
+	UINT16 w = gpatch->width, h = gpatch->height;
+	UINT32 size = w*h;
+	RGBA_t *image, *blendimage, *cur, blendcolor;
+
+	if (grmip->width == 0)
+	{
+
+		grmip->width = gpatch->width;
+		grmip->height = gpatch->height;
+
+		// no wrap around, no chroma key
+		grmip->flags = 0;
+		// setup the texture info
+		grmip->grInfo.format = GR_RGBA;
+	}
+
+	Z_Free(grmip->grInfo.data);
+	grmip->grInfo.data = NULL;
+
+	cur = Z_Malloc(size*4, PU_HWRCACHE, &grmip->grInfo.data);
+	memset(cur, 0x00, size*4);
+
+	image = gpatch->mipmap.grInfo.data;
+	blendimage = blendgpatch->mipmap.grInfo.data;
+
+	switch (color)
+	{
+		case SKINCOLOR_WHITE:
+			blendcolor = V_GetColor(3);
+			break;
+		case SKINCOLOR_SILVER:
+			blendcolor = V_GetColor(10);
+			break;
+		case SKINCOLOR_GREY:
+			blendcolor = V_GetColor(15);
+			break;
+		case SKINCOLOR_BLACK:
+			blendcolor = V_GetColor(27);
+			break;
+		case SKINCOLOR_CYAN:
+			blendcolor = V_GetColor(215);
+			break;
+		case SKINCOLOR_TEAL:
+			blendcolor = V_GetColor(221);
+			break;
+		case SKINCOLOR_STEELBLUE:
+			blendcolor = V_GetColor(203);
+			break;
+		case SKINCOLOR_BLUE:
+			blendcolor = V_GetColor(232);
+			break;
+		case SKINCOLOR_PEACH:
+			blendcolor = V_GetColor(71);
+			break;
+		case SKINCOLOR_TAN:
+			blendcolor = V_GetColor(79);
+			break;
+		case SKINCOLOR_PINK:
+			blendcolor = V_GetColor(147);
+			break;
+		case SKINCOLOR_LAVENDER:
+			blendcolor = V_GetColor(251);
+			break;
+		case SKINCOLOR_PURPLE:
+			blendcolor = V_GetColor(195);
+			break;
+		case SKINCOLOR_ORANGE:
+			blendcolor = V_GetColor(87);
+			break;
+		case SKINCOLOR_ROSEWOOD:
+			blendcolor = V_GetColor(94);
+			break;
+		case SKINCOLOR_BEIGE:
+			blendcolor = V_GetColor(40);
+			break;
+		case SKINCOLOR_BROWN:
+			blendcolor = V_GetColor(57);
+			break;
+		case SKINCOLOR_RED:
+			blendcolor = V_GetColor(130);
+			break;
+		case SKINCOLOR_DARKRED:
+			blendcolor = V_GetColor(139);
+			break;
+		case SKINCOLOR_NEONGREEN:
+			blendcolor = V_GetColor(184);
+			break;
+		case SKINCOLOR_GREEN:
+			blendcolor = V_GetColor(166);
+			break;
+		case SKINCOLOR_ZIM:
+			blendcolor = V_GetColor(180);
+			break;
+		case SKINCOLOR_OLIVE:
+			blendcolor = V_GetColor(108);
+			break;
+		case SKINCOLOR_YELLOW:
+			blendcolor = V_GetColor(104);
+			break;
+		case SKINCOLOR_GOLD:
+			blendcolor = V_GetColor(115);
+			break;
+
+		case SKINCOLOR_SUPER1:
+			blendcolor = V_GetColor(97);
+			break;
+		case SKINCOLOR_SUPER2:
+			blendcolor = V_GetColor(100);
+			break;
+		case SKINCOLOR_SUPER3:
+			blendcolor = V_GetColor(103);
+			break;
+		case SKINCOLOR_SUPER4:
+			blendcolor = V_GetColor(113);
+			break;
+		case SKINCOLOR_SUPER5:
+			blendcolor = V_GetColor(116);
+			break;
+
+		case SKINCOLOR_TSUPER1:
+			blendcolor = V_GetColor(81);
+			break;
+		case SKINCOLOR_TSUPER2:
+			blendcolor = V_GetColor(82);
+			break;
+		case SKINCOLOR_TSUPER3:
+			blendcolor = V_GetColor(84);
+			break;
+		case SKINCOLOR_TSUPER4:
+			blendcolor = V_GetColor(85);
+			break;
+		case SKINCOLOR_TSUPER5:
+			blendcolor = V_GetColor(87);
+			break;
+
+		case SKINCOLOR_KSUPER1:
+			blendcolor = V_GetColor(122);
+			break;
+		case SKINCOLOR_KSUPER2:
+			blendcolor = V_GetColor(123);
+			break;
+		case SKINCOLOR_KSUPER3:
+			blendcolor = V_GetColor(124);
+			break;
+		case SKINCOLOR_KSUPER4:
+			blendcolor = V_GetColor(125);
+			break;
+		case SKINCOLOR_KSUPER5:
+			blendcolor = V_GetColor(126);
+			break;
+		default:
+			blendcolor = V_GetColor(247);
+			break;
+	}
+
+	while (size--)
+	{
+		if (blendimage->s.alpha == 0)
+		{
+			// Don't bother with blending the pixel if the alpha of the blend pixel is 0
+			cur->rgba = image->rgba;
+		}
+		else
+		{
+			INT32 tempcolor;
+			INT16 tempmult, tempalpha;
+			tempalpha = -(abs(blendimage->s.red-127)-127)*2;
+			if (tempalpha > 255)
+				tempalpha = 255;
+			else if (tempalpha < 0)
+				tempalpha = 0;
+
+			tempmult = (blendimage->s.red-127)*2;
+			if (tempmult > 255)
+				tempmult = 255;
+			else if (tempmult < 0)
+				tempmult = 0;
+
+			tempcolor = (image->s.red*(255-blendimage->s.alpha))/255 + ((tempmult + ((tempalpha*blendcolor.s.red)/255)) * blendimage->s.alpha)/255;
+			cur->s.red = (UINT8)tempcolor;
+			tempcolor = (image->s.green*(255-blendimage->s.alpha))/255 + ((tempmult + ((tempalpha*blendcolor.s.green)/255)) * blendimage->s.alpha)/255;
+			cur->s.green = (UINT8)tempcolor;
+			tempcolor = (image->s.blue*(255-blendimage->s.alpha))/255 + ((tempmult + ((tempalpha*blendcolor.s.blue)/255)) * blendimage->s.alpha)/255;
+			cur->s.blue = (UINT8)tempcolor;
+			cur->s.alpha = image->s.alpha;
+		}
+
+		cur++; image++; blendimage++;
+	}
+
+	return;
+}
+
+static void HWR_GetBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, const UINT8 *colormap, skincolors_t color)
+{
+	// mostly copied from HWR_GetMappedPatch, hence the similarities and comment
+	GLMipmap_t *grmip, *newmip;
+
+	if (colormap == colormaps || colormap == NULL)
+	{
+		// Don't do any blending
+		HWD.pfnSetTexture(&gpatch->mipmap);
+		return;
+	}
+
+	// search for the mimmap
+	// skip the first (no colormap translated)
+	for (grmip = &gpatch->mipmap; grmip->nextcolormap; )
+	{
+		grmip = grmip->nextcolormap;
+		if (grmip->colormap == colormap)
+		{
+			if (grmip->downloaded && grmip->grInfo.data)
+			{
+				HWD.pfnSetTexture(grmip); // found the colormap, set it to the correct texture
+				Z_ChangeTag(grmip->grInfo.data, PU_HWRCACHE_UNLOCKED);
+				return;
+			}
+		}
+	}
+
+	// If here, the blended texture has not been created
+	// So we create it
+
+	//BP: WARNING: don't free it manually without clearing the cache of harware renderer
+	//              (it have a liste of mipmap)
+	//    this malloc is cleared in HWR_FreeTextureCache
+	//    (...) unfortunately z_malloc fragment alot the memory :(so malloc is better
+	newmip = calloc(1, sizeof (*newmip));
+	if (newmip == NULL)
+		I_Error("%s: Out of memory", "HWR_GetMappedPatch");
+	grmip->nextcolormap = newmip;
+	newmip->colormap = colormap;
+
+	HWR_CreateBlendedTexture(gpatch, blendgpatch, newmip, color);
+
+	HWD.pfnSetTexture(newmip);
+	Z_ChangeTag(newmip->grInfo.data, PU_HWRCACHE_UNLOCKED);
+}
+
 
 // -----------------+
 // HWR_DrawMD2      : Draw MD2
diff --git a/src/info.c b/src/info.c
index 9e04b4e342f4027b0a319f6bcfaaaf35a8ea035b..cc07ae3f29f8dbf3baf67c8108fae29e84207d83 100644
--- a/src/info.c
+++ b/src/info.c
@@ -4223,7 +4223,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		MT_GOOP,           // painchance
 		sfx_dmpain,        // painsound
 		S_EGGMOBILE2_PAIN2, // meleestate
-		MT_EGGMOBILE2_POGO, // missilestate
+		(statenum_t)MT_EGGMOBILE2_POGO, // missilestate
 		S_EGGMOBILE2_DIE1, // deathstate
 		S_EGGMOBILE2_FLEE1,// xdeathstate
 		sfx_cybdth,        // deathsound
diff --git a/src/m_menu.c b/src/m_menu.c
index 65ea1cfe7066b53f72f6594297ae31cac4de0966..780de7ad5f5984e3a74f5a3e7a062938cc02de56 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -6074,7 +6074,7 @@ static void M_RoomMenu(INT32 choice)
 
 	for (i = 0; room_list[i].header.buffer[0]; i++)
 	{
-		if(room_list[i].name != '\0')
+		if(*room_list[i].name != '\0')
 		{
 			MP_RoomMenu[i+1].text = room_list[i].name;
 			roomIds[i] = room_list[i].id;
diff --git a/src/m_misc.c b/src/m_misc.c
index eaafc06967c24e6eb8c16cf8101a08bbf6d1131d..22effdddfef3c65a5d4edb056e19b59d0434fa72 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -677,7 +677,7 @@ static void M_PNGText(png_structp png_ptr, png_infop png_info_ptr, PNG_CONST png
 	else
 		snprintf(maptext, 8, "Unknown");
 
-	if (gamestate == GS_LEVEL && mapheaderinfo[gamemap-1]->lvlttl)
+	if (gamestate == GS_LEVEL && mapheaderinfo[gamemap-1]->lvlttl[0] != '\0')
 		snprintf(lvlttltext, 48, "%s%s%s",
 			mapheaderinfo[gamemap-1]->lvlttl,
 			(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " ZONE",
diff --git a/src/p_enemy.c b/src/p_enemy.c
index df52972714d3e4de177e3dabcac8d81ed7955f1e..bc6f604810448cbf65d917a731c2d91f5a42407e 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -6362,7 +6362,7 @@ void A_Boss2PogoTarget(mobj_t *actor)
 
 	if (actor->info->missilestate) // spawn the pogo stick collision box
 	{
-		mobj_t *pogo = P_SpawnMobj(actor->x, actor->y, actor->z - mobjinfo[actor->info->missilestate].height, actor->info->missilestate);
+		mobj_t *pogo = P_SpawnMobj(actor->x, actor->y, actor->z - mobjinfo[actor->info->missilestate].height, (mobjtype_t)actor->info->missilestate);
 		pogo->target = actor;
 	}
 
diff --git a/src/p_map.c b/src/p_map.c
index 224ed31bfbc87989ab5d9221fcf8973c0d64eab2..7ff913a8632431df5b37aaf9ef981ee20261625d 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -2636,8 +2636,8 @@ isblocking:
 
 			climbangle += (ANGLE_90 * (whichside ? -1 : 1));
 
-			if (((!slidemo->player->climbing && abs(slidemo->angle - ANGLE_90 - climbline) < ANGLE_45)
-			|| (slidemo->player->climbing == 1 && abs(slidemo->angle - climbline) < ANGLE_135))
+			if (((!slidemo->player->climbing && abs((slidemo->angle - ANGLE_90 - climbline)) < ANGLE_45)
+			|| (slidemo->player->climbing == 1 && abs((slidemo->angle - climbline)) < ANGLE_135))
 			&& P_IsClimbingValid(slidemo->player, climbangle))
 			{
 				slidemo->angle = climbangle;
diff --git a/src/p_user.c b/src/p_user.c
index f015c17f4b4cbce60df2c5feed7f4f42d56b4bbc..96841d7e070727fe0fedc833fd55550d2f9e2391 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -7966,9 +7966,9 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 		if (player == &players[consoleplayer])
 		{
 			if (focusangle >= localangle)
-				localangle += abs(focusangle - localangle)>>5;
+				localangle += abs((focusangle - localangle))>>5;
 			else
-				localangle -= abs(focusangle - localangle)>>5;
+				localangle -= abs((focusangle - localangle))>>5;
 		}
 	}
 	else if (P_AnalogMove(player)) // Analog
diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt
index b3fa5390c50a748d60e87920073f46f5c28ff117..eb832797e482c0450dcea44c1929fa0bb763a90d 100644
--- a/src/sdl/CMakeLists.txt
+++ b/src/sdl/CMakeLists.txt
@@ -117,7 +117,7 @@ if(${SDL2_FOUND})
 	add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 ${SRB2_SDL2_TOTAL_SOURCES})
 	set_target_properties(SRB2SDL2 PROPERTIES OUTPUT_NAME ${SRB2_SDL2_EXE_NAME})
 
-	if(CLANG)
+	if((CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang"))
 		add_framework(CoreFoundation SRB2SDL2)
 		add_framework(SDL2 SRB2SDL2)
 		add_framework(SDL2_mixer SRB2SDL2)
@@ -224,7 +224,7 @@ if(${SDL2_FOUND})
 	endif()
 
 	#### Installation ####
-	if (CLANG)
+	if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
 		install(TARGETS SRB2SDL2
 			BUNDLE DESTINATION .
 		)
@@ -265,7 +265,7 @@ if(${SDL2_FOUND})
 
 
 	# Mac bundle fixup
-	if(CLANG)
+	if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
 		install(CODE "
 			include(BundleUtilities)
 			fixup_bundle(\"${CMAKE_INSTALL_PREFIX}/Sonic Robo Blast 2.app\"
@@ -279,4 +279,4 @@ if(${SDL2_FOUND})
 else()
 	message(WARNING "SDL2 was not found, so the SDL2 target will not be available.")
 	set(SRB2_SDL2_AVAILABLE NO PARENT_SCOPE)
-endif()
\ No newline at end of file
+endif()