diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index d6f40846c66dc5ef6b9a4a02abab6de42352a213..a3f1d9811da4aaceb00cc331ab0962f2c318d9c2 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -1883,6 +1883,37 @@ static int lib_pDoSpring(lua_State *L)
 	return 1;
 }
 
+static int lib_pTryCameraMove(lua_State *L)
+{
+	camera_t *cam = *((camera_t **)luaL_checkudata(L, 1, META_CAMERA));
+	fixed_t x = luaL_checkfixed(L, 2);
+	fixed_t y = luaL_checkfixed(L, 3);
+
+	if (!cam)
+		return LUA_ErrInvalid(L, "camera_t");
+	lua_pushboolean(L, P_TryCameraMove(x, y, cam));
+	return 1;
+}
+
+static int lib_pTeleportCameraMove(lua_State *L)
+{
+	camera_t *cam = *((camera_t **)luaL_checkudata(L, 1, META_CAMERA));
+	fixed_t x = luaL_checkfixed(L, 2);
+	fixed_t y = luaL_checkfixed(L, 3);
+	fixed_t z = luaL_checkfixed(L, 4);
+
+	if (!cam)
+		return LUA_ErrInvalid(L, "camera_t");
+	cam->x = x;
+	cam->y = y;
+	cam->z = z;
+	P_CheckCameraPosition(x, y, cam);
+	cam->subsector = R_PointInSubsector(x, y);
+	cam->floorz = tmfloorz;
+	cam->ceilingz = tmceilingz;
+	return 0;
+}
+
 // P_INTER
 ////////////
 
@@ -3881,6 +3912,8 @@ static luaL_Reg lib[] = {
 	{"P_FloorzAtPos",lib_pFloorzAtPos},
 	{"P_CeilingzAtPos",lib_pCeilingzAtPos},
 	{"P_DoSpring",lib_pDoSpring},
+	{"P_TryCameraMove", lib_pTryCameraMove},
+	{"P_TeleportCameraMove", lib_pTeleportCameraMove},
 
 	// p_inter
 	{"P_RemoveShield",lib_pRemoveShield},
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 306ffaf9482dd996fb10d637ab83f70998fcb393..0f8ccb089d1e5d88c9a93bc16e834d22f9ec2b73 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -396,9 +396,9 @@ static int camera_set(lua_State *L)
 	case camera_subsector:
 	case camera_floorz:
 	case camera_ceilingz:
-	case camera_height:
-	case camera_radius:
-		return luaL_error(L, LUA_QL("camera_t") " field " LUA_QS " should not be set directly.", camera_opt[field]);
+	case camera_x:
+	case camera_y:
+		return luaL_error(L, LUA_QL("camera_t") " field " LUA_QS " should not be set directly. Use " LUA_QL("P_TryCameraMove") " or " LUA_QL("P_TeleportCameraMove") " instead.", camera_opt[field]);
 	case camera_chase:
 		if (cam == &camera)
 			CV_SetValue(&cv_chasecam, (INT32)luaL_checkboolean(L, 3));
@@ -410,18 +410,31 @@ static int camera_set(lua_State *L)
 	case camera_aiming:
 		cam->aiming = luaL_checkangle(L, 3);
 		break;
-	case camera_x:
-		cam->x = luaL_checkfixed(L, 3);
-		break;
-	case camera_y:
-		cam->y = luaL_checkfixed(L, 3);
-		break;
 	case camera_z:
 		cam->z = luaL_checkfixed(L, 3);
+		P_CheckCameraPosition(cam->x, cam->y, cam);
+		cam->floorz = tmfloorz;
+		cam->ceilingz = tmceilingz;
 		break;
 	case camera_angle:
 		cam->angle = luaL_checkangle(L, 3);
 		break;
+	case camera_radius:
+		cam->radius = luaL_checkfixed(L, 3);
+		if (cam->radius < 0)
+			cam->radius = 0;
+		P_CheckCameraPosition(cam->x, cam->y, cam);
+		cam->floorz = tmfloorz;
+		cam->ceilingz = tmceilingz;
+		break;
+	case camera_height:
+		cam->height = luaL_checkfixed(L, 3);
+		if (cam->height < 0)
+			cam->height = 0;
+		P_CheckCameraPosition(cam->x, cam->y, cam);
+		cam->floorz = tmfloorz;
+		cam->ceilingz = tmceilingz;
+		break;
 	case camera_momx:
 		cam->momx = luaL_checkfixed(L, 3);
 		break;