// Custom function to map damage type numbers to DAMAGE_TYPE_* constants
int GetDamageTypeFromNumber(int nDamageType)
{
    switch (nDamageType)
    {
        case 1: return DAMAGE_TYPE_PIERCING;
        case 2: return DAMAGE_TYPE_BLUDGEONING;
        case 3: return DAMAGE_TYPE_SLASHING;
        case 4: return DAMAGE_TYPE_ACID;
        case 5: return DAMAGE_TYPE_COLD;
        case 6: return DAMAGE_TYPE_DIVINE;
        case 7: return DAMAGE_TYPE_ELECTRICAL;
        case 8: return DAMAGE_TYPE_FIRE;
        case 9: return DAMAGE_TYPE_MAGICAL;
        case 10: return DAMAGE_TYPE_NEGATIVE;
        case 11: return DAMAGE_TYPE_POSITIVE;
        case 12: return DAMAGE_TYPE_SONIC;
    }
    // Default to piercing if unknown
    return DAMAGE_TYPE_PIERCING;
}

// Function to parse XdY format (e.g., "20d10") and return the total damage
int RollCustomDamage(string sDamage)
{
    int nTotalDamage = 0;
    int nRolls = StringToInt(GetStringLeft(sDamage, FindSubString(sDamage, "d"))); // Number of dice (X)
    int nDiceType = StringToInt(GetStringRight(sDamage, GetStringLength(sDamage) - FindSubString(sDamage, "d") - 1)); // Dice type (Y)
    
    int i;
    for (i = 0; i < nRolls; i++)
    {
        switch (nDiceType)
        {
            case 4:  nTotalDamage += d4(); break;
            case 6:  nTotalDamage += d6(); break;
            case 8:  nTotalDamage += d8(); break;
            case 10: nTotalDamage += d10(); break;
            case 12: nTotalDamage += d12(); break;
            case 20: nTotalDamage += d20(); break;
            case 100: nTotalDamage += d100(); break;
            default: nTotalDamage += d10(); break; // Default to d10 if unsupported
        }
    }
    
    return nTotalDamage;
}

// Function to check for creatures within a small radius of the VFX location and apply damage
void CheckAndApplyDamageAtVFX(location lPoint, int nDamage, int nDamageType, int nDC)
{
    // Check for creatures within RADIUS_SIZE_SMALL around the VFX location
    object oCreature = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lPoint);

    while (GetIsObjectValid(oCreature))
    {
        if (GetIsPC(oCreature) || GetIsDM(oCreature))
        {
            int nReflexSave = ReflexSave(oCreature, nDC); // Use passed DC
            int bHasEvasion = GetHasFeat(FEAT_EVASION, oCreature);
            int bHasImprovedEvasion = GetHasFeat(FEAT_IMPROVED_EVASION, oCreature);
            int nDamageApplied = nDamage;

            // Adjust damage based on Reflex save result and feats
            if (nReflexSave)  // Successful save
            {
                if (bHasEvasion || bHasImprovedEvasion)
                {
                    nDamageApplied = 0;  // No damage with successful save if Evasion or Improved Evasion is present
                }
                else
                {
                    nDamageApplied /= 2; // Half damage on a successful save without Evasion
                }
            }
            else  // Failed save
            {
                if (bHasImprovedEvasion)
                {
                    nDamageApplied /= 2; // Half damage if Improved Evasion is present
                }
            }

            // Apply damage to the creature
            if (nDamageApplied > 0)
            {
                ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDamageApplied, nDamageType), oCreature);
            }
        }
        oCreature = GetNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lPoint);
    }
}

void ApplySpikeVFXAcrossTrigger(location lCenter, float fWidth, float fLength, float fSpacing, int nVFX, int nDamageType, string sDamage, int nDC)
{
    vector vCenter = GetPositionFromLocation(lCenter);
    float fStartX = vCenter.x - (fWidth / 2);
    float fStartY = vCenter.y - (fLength / 2);

    float fX = fStartX;
    while (fX <= fStartX + fWidth)
    {
        float fY = fStartY;
        while (fY <= fStartY + fLength)
        {
            location lPoint = Location(GetArea(OBJECT_SELF), Vector(fX, fY, vCenter.z), 0.0);
            ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectVisualEffect(nVFX), lPoint, 1.5f);
            
            // Roll damage and apply to creatures within RADIUS_SIZE_SMALL of this VFX location
            int nDamage = RollCustomDamage(sDamage);
            CheckAndApplyDamageAtVFX(lPoint, nDamage, nDamageType, nDC);

            fY += fSpacing;
        }
        fX += fSpacing;
    }
}

void main()
{
    // Check if the trigger is turned off or not activated
    if (GetLocalInt(OBJECT_SELF, "bIsTurnedOff"))
    {
        return; // Exit the script if bIsTurnedOff is set
    }

    location lCenter = GetLocation(OBJECT_SELF);

    // Retrieve dimensions, spacing, VFX, damage type, and DC from the trigger's local variables
    float fWidth = GetLocalFloat(OBJECT_SELF, "fWidth");
    float fLength = GetLocalFloat(OBJECT_SELF, "fLength");
    float fSpacing = GetLocalFloat(OBJECT_SELF, "fSpacing");
    int nVFX = GetLocalInt(OBJECT_SELF, "nVFX"); // VFX ID
    int nDamageType = GetLocalInt(OBJECT_SELF, "nDamageType"); // Damage type as custom number
    int nDC = GetLocalInt(OBJECT_SELF, "nDC"); // Retrieve DC
    string sDamage = GetLocalString(OBJECT_SELF, "sDamage"); // Damage in XdY format

    // Apply default values if the variables are not set
    if (fWidth <= 0.0) fWidth = 5.0;
    if (fLength <= 0.0) fLength = 5.0;
    if (fSpacing <= 0.0) fSpacing = 1.0;
    if (nVFX <= 0) nVFX = VFX_IMP_SPIKE_TRAP; // Default VFX to spike trap
    if (sDamage == "") sDamage = "20d10"; // Default damage if not set
    if (nDC <= 0) nDC = 45; // Default DC to 15 if not set

    ApplySpikeVFXAcrossTrigger(lCenter, fWidth, fLength, fSpacing, nVFX, nDamageType, sDamage, nDC);
}
