code: async def can_cast(self, unit: Unit, ability_id: AbilityId, target: Optional[Union[Unit, Point2, Point3]]=None, only_check_energy_and_cooldown: bool=False, cached_abilities_of_unit: List[AbilityId]=None) -> bool:
          
          assert isinstance(unit, Unit)
          assert isinstance(ability_id, AbilityId)
          assert isinstance(target, (type(None), Unit, Point2, Point3))
          # check if unit has enough energy to cast or if ability is on cooldown
          if cached_abilities_of_unit:
              abilities = cached_abilities_of_unit
          else:
              abilities = (await self.get_available_abilities([unit]))[0]
  
          if ability_id in abilities:
              if only_check_energy_and_cooldown:
                  return True
              cast_range = self._game_data.abilities[ability_id.value]._proto.cast_range
              ability_target = self._game_data.abilities[ability_id.value]._proto.target
              # Check if target is in range (or is a self cast like stimpack)
              if ability_target == 1 or ability_target == Target.PointOrNone.value and isinstance(target, (Point2, Point3)) and unit.distance_to(target) <= cast_range: # cant replace 1 with "Target.None.value" because ".None" doesnt seem to be a valid enum name
                  return True
              # Check if able to use ability on a unit
              elif ability_target in {Target.Unit.value, Target.PointOrUnit.value} and isinstance(target, Unit) and unit.distance_to(target) <= cast_range:
                  return True
              # Check if able to use ability on a position
              elif ability_target in {Target.Point.value, Target.PointOrUnit.value} and isinstance(target, (Point2, Point3)) and unit.distance_to(target) <= cast_range:
                  return True
          return False
docs: Tests if a unit has an ability available and enough energy to cast it.
          See data_pb2.py (line 161) for the numbers 1-5 to make sense