Are there pitfalls to implementing a hybrid site with both responsive layouts and dynamic serving layouts?

I have a 20 year old site that I want to upgrade to a responsive layout. Originally it was a desktop-only layout but when a significant portion of my user base went mobile I implemented dynamic serving layouts where I sniff the user agent and serve different HTML based on whether the user agent is for a mobile device or not.

I started to implement the responsive layouts, but I found that about 5% of my user base is on older browsers that don’t have all the CSS and JavaScript I would like to use. For example, only 95.42% of users fully support CSS grid layouts: https://caniuse.com/css-grid While I wouldn’t want to take the time to develop a site just 5% of my users, I already have a site that works for those users and I don’t want to lose that much of my traffic when I move to responsive.

My current plan is to still do server side tests based on the user agent like this pseudo code:

use responsive-layout if (bot)  use desktop-layout if (msie or firefox < 54 or chrome < 58  or edge < 16) use mobile-layout if (opera-mini or android-browser or samsung-internet < 5 or safari-mobile < 10.3) use responsive-layout otherwise 

Most of my users and search engine crawlers would get the responsive layout. Only specific non-capable and older browsers would get my existing static layouts.

I know that Google supports either responsive layouts or dynamic serving layouts but I haven’t been able to find any information about a hybrid approach like this. Are there any pitfalls (especially with regards to SEO) of mostly using responsive but falling back to dynamic serving for some browsers?

How can I make both hands fingers change by adding another enum mode?

A bit long but this scripts are connected to each other. I will explain the exact things I’m trying to add/change.

This is the controller script :

using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Serialization;      /*      * Asset stores key control config.      */     [ExecuteInEditMode]     [RequireComponent(typeof(Animator))]     public class HandController : MonoBehaviour     {         [SerializeField] private HandPosePresetsAsset m_preset;         [SerializeField] private HandType m_handType = HandType.LeftHand;         [SerializeField] private HandPoseData m_handPoseData;          private HandRuntimeControl m_runtimeControl;         private Avatar m_avatar;          private HandPoseData m_basePose;          public HandType Hand         {             get { return m_handType; }             set             {                 m_handType = value;                 InitializeRuntimeControl();             }         }          public HandPosePresetsAsset Preset         {             get => m_preset;             set => m_preset = value;         }          private void Awake()         {             InitializeRuntimeControl();             #if UNITY_EDITOR                          #endif         }          /// <summary>         /// Set current hand pose as base pose for transformation.          /// </summary>         public void SetCurrentAsBasePose()         {             m_basePose = m_handPoseData;         }                          /// <summary>         /// Set base hand pose from currently assigned preset          /// </summary>         public void SetBasePoseFromCurrentPreset(string poseName)         {             m_basePose = m_preset[poseName];         }                  /// <summary>         /// Set base hand pose from currently assigned preset          /// </summary>         public void SetBasePoseFromCurrentPreset(int index)         {             m_basePose = m_preset[index];         }                          /// <summary>         /// Set base hand pose.         /// </summary>         public void SetBasePose(ref HandPoseData pose)         {             m_basePose = pose;         }                          /// <summary>         /// Set hand pose.           /// </summary>         /// <param name="poseName">name of pose in RuntimeHandPosePresetAsset.</param>         /// <param name="t">lerp value of hand pose. 0.0f=base pose, 1.0f=poseName pose</param>         public void SetHandPose(string poseName, float t = 1.0f)         {             var pose = m_preset[poseName];             SetHandPose(ref pose, t);         }          /// <summary>         /// Set hand pose.           /// </summary>         /// <param name="poseIndex">index of pose in RuntimeHandPosePresetAsset.</param>         /// <param name="t">lerp value of hand pose. 0.0f=base pose, 1.0f=poseName pose</param>         public void SetHandPose(int poseIndex, float t = 1.0f)         {             var pose = m_preset[poseIndex];             SetHandPose(ref pose, t);         }                  /// <summary>         /// Set hand pose.           /// </summary>         /// <param name="data">hand pose</param>         /// <param name="t">lerp value of hand pose. 0.0f=base pose, 1.0f=data</param>         public void SetHandPose(ref HandPoseData data, float t = 1.0f)         {             if (m_runtimeControl == null)             {                 InitializeRuntimeControl();             }             LerpHandPose(ref m_handPoseData, ref m_basePose, ref data, t);         }          private static void LerpHandPose(ref HandPoseData data, ref HandPoseData src, ref HandPoseData dst, float t)         {             switch (t)             {                 case 0f:                     data = src;                     break;                 case 1.0f:                     data = dst;                     break;                 default:                 {                     for (var i = 0; i < HandPoseData.HumanFingerCount; ++i)                     {                         data[i] = new FingerPoseData                         {                             muscle1 = Mathf.Lerp(src[i].muscle1, dst[i].muscle1, t),                             muscle2 = Mathf.Lerp(src[i].muscle2, dst[i].muscle2, t),                             muscle3 = Mathf.Lerp(src[i].muscle3, dst[i].muscle3, t),                             spread = Mathf.Lerp(src[i].spread, dst[i].spread, t)                         };                     }                     break;                 }             }         }          public void InitializeRuntimeControl()         {             var animator = GetComponent<Animator>();             m_avatar = animator.avatar;              if (m_avatar == null)             {                 Debug.LogError("HandController:Animator component doesn't have a valid avatar configured.");             }                          m_runtimeControl = new HandRuntimeControl(gameObject, m_avatar, m_handType);         }          private void LateUpdate()         {             m_runtimeControl?.UpdateHandPose(ref m_handPoseData);         }     } 

The next script is the HandType

In the enum I added the BothHands

using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using UnityEngine;      public enum HandType     {         LeftHand,         RightHand,         BothHands     }      [Serializable]     public struct FingerPoseData     {         [Range(-1f, 1f)]         public float spread;         [Range(-1f, 1f)]         public float muscle1;         [Range(-1f, 1f)]         public float muscle2;         [Range(-1f, 1f)]         public float muscle3;                  public void WeightedAddPose(float weight, ref FingerPoseData data)         {             spread += weight * data.spread;             muscle1 += weight * data.muscle1;             muscle2 += weight * data.muscle2;             muscle3 += weight * data.muscle3;         }     }      [Serializable]     public struct HandPoseData     {         public FingerPoseData thumb;         public FingerPoseData index;         public FingerPoseData middle;         public FingerPoseData ring;         public FingerPoseData little;          public const int HumanFingerCount = 5;                  public FingerPoseData this[int idx]         {             get             {                 if (idx < 0 || idx >= HumanFingerCount)                 {                     throw new IndexOutOfRangeException();                 }                  switch (idx)                 {                     case 0:                         return thumb;                     case 1:                         return index;                     case 2:                         return middle;                     case 3:                         return ring;                     default:                         return little;                 }             }              set             {                 if (idx < 0 || idx >= HumanFingerCount)                 {                     throw new IndexOutOfRangeException();                 }                 switch (idx)                 {                     case 0:                         thumb = value;                         break;                     case 1:                         index = value;                         break;                     case 2:                         middle = value;                         break;                     case 3:                         ring = value;                         break;                     default:                         little = value;                         break;                 }             }         }          public void WeightedAddPose(float weight, ref HandPoseData data)         {             thumb.WeightedAddPose(weight, ref data.thumb);             index.WeightedAddPose(weight, ref data.index);             middle.WeightedAddPose(weight, ref data.middle);             ring.WeightedAddPose(weight, ref data.ring);             little.WeightedAddPose(weight, ref data.little);         }     }      public class HandRuntimeControl     {         private readonly int[] m_handBoneIndexMap;         private readonly HumanPoseHandler m_poseHandler;         private readonly GameObject m_rootObject;         private HumanPose m_humanPose;          public HandRuntimeControl(GameObject rootObject, Avatar avatar, HandType handType)         {             m_rootObject = rootObject;             m_poseHandler = new HumanPoseHandler(avatar, rootObject.transform);             m_handBoneIndexMap = new int[20];              var indexMuscleFingerBegin = handType == HandType.LeftHand ? 55 : 75;                          m_poseHandler.GetHumanPose(ref m_humanPose);              for (var i = 0; i < m_handBoneIndexMap.Length; ++i)             {                 m_handBoneIndexMap[i] = indexMuscleFingerBegin + i;             }         }          public void UpdateHandPose(ref HandPoseData handPose)         {             m_poseHandler.GetHumanPose(ref m_humanPose);                          m_humanPose.bodyPosition = Vector3.zero;             m_humanPose.bodyRotation = Quaternion.identity;                          var i = 0;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.thumb.muscle1;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.thumb.spread;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.thumb.muscle2;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.thumb.muscle3;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.index.muscle1;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.index.spread;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.index.muscle2;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.index.muscle3;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.middle.muscle1;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.middle.spread;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.middle.muscle2;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.middle.muscle3;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.ring.muscle1;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.ring.spread;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.ring.muscle2;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.ring.muscle3;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.little.muscle1;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.little.spread;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.little.muscle2;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.little.muscle3;                          m_poseHandler.SetHumanPose(ref m_humanPose);         }     } 

The last script :

using System; using System.Collections; using System.Collections.Generic; using UnityEngine;  [RequireComponent(typeof(HandController))] public class InteractiveHandControllerAdapter : MonoBehaviour {     [Serializable]     public struct InputConfig     {         public HandType hand;         public string inputName;         public int presetIndex;     }      [SerializeField] private InputConfig[] m_inputs;      private HandController m_leftHand;     private HandController m_rightHand;          // Start is called before the first frame update     void Start()     {         foreach (var c in GetComponents<HandController>())         {             if (c.Hand == HandType.LeftHand)             {                 m_leftHand = c;             }             else             {                 m_rightHand = c;             }         }     }      // Update is called once per frame     void Update()     {         if (m_inputs != null)         {             foreach (var input in m_inputs)             {                 if(input.hand == HandType.LeftHand)                      m_leftHand?.SetHandPose(input.presetIndex, Input.GetAxis(input.inputName));                 else                      m_rightHand?.SetHandPose(input.presetIndex, Input.GetAxis(input.inputName));             }         }     } } 

Its working fine when I change the enum in the editor or runtime for LeftHand or RightHand :

In the Inspector in this screenshot I selected the Left Hand I can change it to the Right Hand and it will affect the hands find but when changing to Both Hands it does nothing or keep changing only the left hand. I want it to change both hands when it’s on the both hands enum mode.

Hands controller script settings in inspector

How can I make both hands fingers change by adding another enum mode?

A bit long but this scripts are connected to each other. I will explain the exact things I’m trying to add/change.

This is the controller script :

using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Serialization;      /*      * Asset stores key control config.      */     [ExecuteInEditMode]     [RequireComponent(typeof(Animator))]     public class HandController : MonoBehaviour     {         [SerializeField] private HandPosePresetsAsset m_preset;         [SerializeField] private HandType m_handType = HandType.LeftHand;         [SerializeField] private HandPoseData m_handPoseData;          private HandRuntimeControl m_runtimeControl;         private Avatar m_avatar;          private HandPoseData m_basePose;          public HandType Hand         {             get { return m_handType; }             set             {                 m_handType = value;                 InitializeRuntimeControl();             }         }          public HandPosePresetsAsset Preset         {             get => m_preset;             set => m_preset = value;         }          private void Awake()         {             InitializeRuntimeControl();             #if UNITY_EDITOR                          #endif         }          /// <summary>         /// Set current hand pose as base pose for transformation.          /// </summary>         public void SetCurrentAsBasePose()         {             m_basePose = m_handPoseData;         }                          /// <summary>         /// Set base hand pose from currently assigned preset          /// </summary>         public void SetBasePoseFromCurrentPreset(string poseName)         {             m_basePose = m_preset[poseName];         }                  /// <summary>         /// Set base hand pose from currently assigned preset          /// </summary>         public void SetBasePoseFromCurrentPreset(int index)         {             m_basePose = m_preset[index];         }                          /// <summary>         /// Set base hand pose.         /// </summary>         public void SetBasePose(ref HandPoseData pose)         {             m_basePose = pose;         }                          /// <summary>         /// Set hand pose.           /// </summary>         /// <param name="poseName">name of pose in RuntimeHandPosePresetAsset.</param>         /// <param name="t">lerp value of hand pose. 0.0f=base pose, 1.0f=poseName pose</param>         public void SetHandPose(string poseName, float t = 1.0f)         {             var pose = m_preset[poseName];             SetHandPose(ref pose, t);         }          /// <summary>         /// Set hand pose.           /// </summary>         /// <param name="poseIndex">index of pose in RuntimeHandPosePresetAsset.</param>         /// <param name="t">lerp value of hand pose. 0.0f=base pose, 1.0f=poseName pose</param>         public void SetHandPose(int poseIndex, float t = 1.0f)         {             var pose = m_preset[poseIndex];             SetHandPose(ref pose, t);         }                  /// <summary>         /// Set hand pose.           /// </summary>         /// <param name="data">hand pose</param>         /// <param name="t">lerp value of hand pose. 0.0f=base pose, 1.0f=data</param>         public void SetHandPose(ref HandPoseData data, float t = 1.0f)         {             if (m_runtimeControl == null)             {                 InitializeRuntimeControl();             }             LerpHandPose(ref m_handPoseData, ref m_basePose, ref data, t);         }          private static void LerpHandPose(ref HandPoseData data, ref HandPoseData src, ref HandPoseData dst, float t)         {             switch (t)             {                 case 0f:                     data = src;                     break;                 case 1.0f:                     data = dst;                     break;                 default:                 {                     for (var i = 0; i < HandPoseData.HumanFingerCount; ++i)                     {                         data[i] = new FingerPoseData                         {                             muscle1 = Mathf.Lerp(src[i].muscle1, dst[i].muscle1, t),                             muscle2 = Mathf.Lerp(src[i].muscle2, dst[i].muscle2, t),                             muscle3 = Mathf.Lerp(src[i].muscle3, dst[i].muscle3, t),                             spread = Mathf.Lerp(src[i].spread, dst[i].spread, t)                         };                     }                     break;                 }             }         }          public void InitializeRuntimeControl()         {             var animator = GetComponent<Animator>();             m_avatar = animator.avatar;              if (m_avatar == null)             {                 Debug.LogError("HandController:Animator component doesn't have a valid avatar configured.");             }                          m_runtimeControl = new HandRuntimeControl(gameObject, m_avatar, m_handType);         }          private void LateUpdate()         {             m_runtimeControl?.UpdateHandPose(ref m_handPoseData);         }     } 

The next script is the HandType

In the enum I added the BothHands

using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using UnityEngine;      public enum HandType     {         LeftHand,         RightHand,         BothHands     }      [Serializable]     public struct FingerPoseData     {         [Range(-1f, 1f)]         public float spread;         [Range(-1f, 1f)]         public float muscle1;         [Range(-1f, 1f)]         public float muscle2;         [Range(-1f, 1f)]         public float muscle3;                  public void WeightedAddPose(float weight, ref FingerPoseData data)         {             spread += weight * data.spread;             muscle1 += weight * data.muscle1;             muscle2 += weight * data.muscle2;             muscle3 += weight * data.muscle3;         }     }      [Serializable]     public struct HandPoseData     {         public FingerPoseData thumb;         public FingerPoseData index;         public FingerPoseData middle;         public FingerPoseData ring;         public FingerPoseData little;          public const int HumanFingerCount = 5;                  public FingerPoseData this[int idx]         {             get             {                 if (idx < 0 || idx >= HumanFingerCount)                 {                     throw new IndexOutOfRangeException();                 }                  switch (idx)                 {                     case 0:                         return thumb;                     case 1:                         return index;                     case 2:                         return middle;                     case 3:                         return ring;                     default:                         return little;                 }             }              set             {                 if (idx < 0 || idx >= HumanFingerCount)                 {                     throw new IndexOutOfRangeException();                 }                 switch (idx)                 {                     case 0:                         thumb = value;                         break;                     case 1:                         index = value;                         break;                     case 2:                         middle = value;                         break;                     case 3:                         ring = value;                         break;                     default:                         little = value;                         break;                 }             }         }          public void WeightedAddPose(float weight, ref HandPoseData data)         {             thumb.WeightedAddPose(weight, ref data.thumb);             index.WeightedAddPose(weight, ref data.index);             middle.WeightedAddPose(weight, ref data.middle);             ring.WeightedAddPose(weight, ref data.ring);             little.WeightedAddPose(weight, ref data.little);         }     }      public class HandRuntimeControl     {         private readonly int[] m_handBoneIndexMap;         private readonly HumanPoseHandler m_poseHandler;         private readonly GameObject m_rootObject;         private HumanPose m_humanPose;          public HandRuntimeControl(GameObject rootObject, Avatar avatar, HandType handType)         {             m_rootObject = rootObject;             m_poseHandler = new HumanPoseHandler(avatar, rootObject.transform);             m_handBoneIndexMap = new int[20];              var indexMuscleFingerBegin = handType == HandType.LeftHand ? 55 : 75;                          m_poseHandler.GetHumanPose(ref m_humanPose);              for (var i = 0; i < m_handBoneIndexMap.Length; ++i)             {                 m_handBoneIndexMap[i] = indexMuscleFingerBegin + i;             }         }          public void UpdateHandPose(ref HandPoseData handPose)         {             m_poseHandler.GetHumanPose(ref m_humanPose);                          m_humanPose.bodyPosition = Vector3.zero;             m_humanPose.bodyRotation = Quaternion.identity;                          var i = 0;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.thumb.muscle1;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.thumb.spread;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.thumb.muscle2;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.thumb.muscle3;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.index.muscle1;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.index.spread;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.index.muscle2;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.index.muscle3;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.middle.muscle1;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.middle.spread;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.middle.muscle2;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.middle.muscle3;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.ring.muscle1;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.ring.spread;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.ring.muscle2;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.ring.muscle3;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.little.muscle1;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.little.spread;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.little.muscle2;             m_humanPose.muscles[m_handBoneIndexMap[i++]] = handPose.little.muscle3;                          m_poseHandler.SetHumanPose(ref m_humanPose);         }     } 

The last script :

using System; using System.Collections; using System.Collections.Generic; using UnityEngine;  [RequireComponent(typeof(HandController))] public class InteractiveHandControllerAdapter : MonoBehaviour {     [Serializable]     public struct InputConfig     {         public HandType hand;         public string inputName;         public int presetIndex;     }      [SerializeField] private InputConfig[] m_inputs;      private HandController m_leftHand;     private HandController m_rightHand;          // Start is called before the first frame update     void Start()     {         foreach (var c in GetComponents<HandController>())         {             if (c.Hand == HandType.LeftHand)             {                 m_leftHand = c;             }             else             {                 m_rightHand = c;             }         }     }      // Update is called once per frame     void Update()     {         if (m_inputs != null)         {             foreach (var input in m_inputs)             {                 if(input.hand == HandType.LeftHand)                      m_leftHand?.SetHandPose(input.presetIndex, Input.GetAxis(input.inputName));                 else                      m_rightHand?.SetHandPose(input.presetIndex, Input.GetAxis(input.inputName));             }         }     } } 

Its working fine when I change the enum in the editor or runtime for LeftHand or RightHand :

In the Inspector in this screenshot I selected the Left Hand I can change it to the Right Hand and it will affect the hands find but when changing to Both Hands it does nothing or keep changing only the left hand. I want it to change both hands when it’s on the both hands enum mode.

Hands controller script settings in inspector

301 or noindex or both

I have lots of categories but I use different pages as landing page for each categories and in pages I just load the contents of each category that I need (I cant delete my categories) what is best solution for categories ?

  • no index
  • 301 redirect
  • or both ?!

Certainly when google see a 301 redirect cant check HTML codes, is true? then it is not important that page had an Noindex robots tag) thanks

Can Lay on Hands be used to both heal hit points and remove diseases/poisons with the same action?

The Paladin’s Lay on Hands feature says:

As an action, you can touch a creature and […] restore a number of hit points to that creature.

It also goes on to say:

Alternatively, you can expend 5 hit points from your pool of healing to cure the target of one disease or neutralize one poison affecting it. You can cure multiple diseases and neutralize multiple poisons with a single use of Lay on Hands […]

Could I opt to do both at the same time? Perhaps an ally was struck by a crossbow bolt with drow poison, and I needed to both heal them as well as remove the poison in one go; is that permissible?

As written, it seems vague. The first paragraph specifies the use of an action to heal hit points, but the second simply indicates an alternative way for the hit point pool to be expended, but doesn’t seem to indicate that the use is exclusive from the first.

Does Furious Finish work with both hits of Cleaving Smash?

Cleaving Smash (from Weapon Trick – Two-Handed Weapon):

When you use Cleave, you can add the additional damage from Vital Strike to both your initial and your secondary attacks. If you also have the Greater Vital Strike feat, you can instead add the damage from Improved Vital Strike to both your initial and your secondary attacks.

Furious Finish:

While raging, when you use the Vital Strike feat, you can choose not to roll your damage dice and instead deal damage equal to the maximum roll possible on those damage dice. If you do, your rage immediately ends, and you are fatigued (even if you would not normally be).

Does Furious Finish apply to both hits of Cleaving Smash, or does rage suddenly end mid-swing?

Or are they not compatible at all since Cleaving Smash doesn’t directly state that you’re actually using the Vital Strike "feat", only that you’re getting all the benefits of it?

Why are zephyr hawks and river drakes both level 3?

Both zephyr hawks and river drakes are level 3 creatures. This seems insane.

Zephyr hawks are worse than river drakes in just about every way. Their AC being 2 points higher than that of a river drake is a small consolation. Zephyr hawks deal one damage die. Their only ability gives 2 attacks without a penalty increase and saves them an action if they also move.

River drakes:

  • Have better HP, perception, skills, ability scores, and attack rolls
  • Deal 2 damage dice
  • Have a 3-attacks-for-2-actions ability, unlimited use
  • Have a 2-moves-for-1-action ability, three times a day
  • Have a 10-foot burst AoE that deals significant acid damage both initially and persistently, along with a speed debuff, once every 1d6 rounds
  • Have a reaction that gives them a free strike and applies a penalty to the target’s roll if the strike hits

I don’t see how anyone could imagine zephyr hawks to be on the same level or be worth the same XP as river drakes.

Last night at the table, with a bit of skewed luck, a party that had just wiped the floor with two zephyr hawks lost three characters to two river drakes. They had full spells, abilities, and HP for the river drake fight, in contrast to having few remaining resources to fight the hawks the day before. Absolutely not surprising when you look at the blatant power difference between these two creatures.

What’s up with them both being level 3?

what happens too both ends of the spell fabricate?

if you cast fabricate and target more raw materials than needed for what you create, what happens to the excess material(casting it on a 120ft cube of forest and turning it into a single chair)? Furthermore, the spell doesn’t specify where the created item would be or any restrictions on it, would the created item have to be within the same 120ft cube or something else? (eg 100 miles in the air, behind a corner, or inside of another creature)

Choose raw materials that you can see within range. You can fabricate a Large or smaller object (contained within a 10-foot cube, or eight connected 5-foot cubes), given a sufficient quantity of raw material.