Aller au contenu

Photo

Improving AA abilities


  • Veuillez vous connecter pour répondre
27 réponses à ce sujet

#1
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
This has been much requested on our server. Imbue and death arrow have been much improved, and even though hail and seeker have been modified these latter two are still seriously lacking.
 
The problem from my view point for hail and seeker is that it doesn’t actually uses the equipped arrows, nor does it apply our mod’s post 40 mele dmg (for the most part anyways, it does apply one component of it however not the majority which is applied as  a supernatural effect to the toon itself), and because of this it is seriously underpowered at higher levels.
 
Here are our Hail and Seeker scripts, and the include associated to them. I would be very interested in any ideas to get them to actually use the equipped arrows (and subsequently our standard post 40 mele dmg hopefully), and not just do low lvl dmg based off the dmg types of the equipped arrows as a spell effect as it appears to be now.
 
Any ideas would be welcomed.
 
The Include:

#include "x0_i0_spells"
#include "core_inc_post40"
//This include file contains funstions for the improved hail of arrows and seeking arrow scripts
// Written by Twainer for the Aventia persistant world 20/10/2005
 
// This Function Retrieves the values stored in StoreArrowDamage and returns
// an effect with all the calculated damage in it.
// If nCrit is 1 the damage is tripled
effect GetArcaneArrowDamage(object oUser, object oTarget, int nCrit = 0);
 
// This examines the arrows in oUsers arrow slot and determines and damge modifiers
// or damage vs alignment group modifers that those arrows has.
// Values are then stored on the arrows for later use.
void StoreArrowDamage(object oUser);
 
 
 
 
effect GetArcaneArrowDamage(object oUser, object oTarget, int nCrit = 0)
{
    object oArrows = GetItemInSlot(INVENTORY_SLOT_ARROWS, oUser);
    object oBow = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oUser);
    int nBonus = ArcaneArcherCalculateBonus();
    int dieSize, dieCount, damtype, nDamage, i,j, nPierce, align;
    effect eTemp, eTotal;
    string sDam;
 
    if (GetIsObjectValid(oBow) == TRUE){
        if (GetBaseItemType(oBow) == BASE_ITEM_LONGBOW ){
            nPierce = d8();
            if (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_LONGBOW, oUser))
                nPierce += 2;
            if (GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_LONGBOW, oUser))
                nPierce += 4;
        }else if (GetBaseItemType(oBow) == BASE_ITEM_SHORTBOW){
            nPierce = d6();
            if (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SHORTBOW, oUser))
                nPierce += 2;
            if (GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_SHORTBOW, oUser))
                nPierce += 4;
        }
        else
            return eTemp;
    }
    else
        return eTemp;
 
    // add strength bonus if bow has mighty (im lazy so i wont cap it properly)
    if (GetItemHasItemProperty(oBow, ITEM_PROPERTY_MIGHTY))
        nPierce += GetAbilityModifier(ABILITY_STRENGTH,oUser);
    //add AA damage bonus
    nPierce += nBonus;
 
    //add post 40 melee damage
    int nXP = GetXP( oUser );
    int nLevel = pf_GetLevelFromXP( nXP );
    int nP40Bonus;
    if ( nLevel > 39 )
    {
      nP40Bonus = ( nLevel - 40 ) / 2;
      float fMeleeFactor = calcMeleeFactor( oUser );
 
      if ( GetIsObjectValid( GetItemPossessedBy( oUser, "GrtrMeleeRunV2" )))
      {
        fMeleeFactor *= 1.5;
      }
      else if ( GetIsObjectValid( GetItemPossessedBy( oUser, "LesrMeleeRunV2" )))
      {
        fMeleeFactor *= 1.25;
      }
      nP40Bonus = FloatToInt( IntToFloat( nP40Bonus ) * fMeleeFactor );
    }
    nPierce += nP40Bonus;
 
    // Check Arrows Now
    int pcount = GetLocalInt(oArrows, "scy_AA_prop_count");
    if (pcount == 255)
        pcount = 0;
 
    for (j = 1; j <= pcount; j++){
        damtype = GetLocalInt(oArrows, "scy_AA_prop" + IntToString(j) + "_damtype");
        dieSize = GetLocalInt(oArrows, "scy_AA_prop" + IntToString(j) + "_diesize");
        dieCount = GetLocalInt(oArrows, "scy_AA_prop" + IntToString(j) + "_diecount");
        align = GetLocalInt(oArrows, "scy_AA_prop" + IntToString(j) + "_align");
        if (align == 0 || align == GetAlignmentGoodEvil(oTarget) || align == GetAlignmentLawChaos(oTarget)){
            // non-random case
            if(dieCount == 0)
                nDamage = dieSize;
            else{
                nDamage = 0;
                for(i = 1; i <= dieCount; i++)
                    nDamage += 1+ Random(dieSize);
            }
            //SendMessageToPC(oUser, IntToString(idamage) + " " + sDam + " " + Get2DAString("iprp_damagetype", "Label", damtype) + " damage");
            // If its piercing, add it to the bow damage
 
            //Happy Edit - also add the slash / bludge
            if(damtype == DAMAGE_TYPE_PIERCING || damtype == DAMAGE_TYPE_BLUDGEONING || damtype == DAMAGE_TYPE_SLASHING)
                nPierce += nDamage;
            else{
                eTemp = EffectDamage(nDamage*(nCrit*2 + 1), damtype, IPGetDamagePowerConstantFromNumber(nBonus));
                eTotal = EffectLinkEffects(eTotal, eTemp);
            }
        }
    }
    // now chuck all that pierce on as well
    eTemp = EffectDamage(nPierce*(nCrit*2 + 1), DAMAGE_TYPE_PIERCING, IPGetDamagePowerConstantFromNumber(nBonus));
    eTotal = EffectLinkEffects(eTotal, eTemp);
    return eTotal;
}
 
void StoreArrowDamage(object oUser)
{
    object oArrows = GetItemInSlot(INVENTORY_SLOT_ARROWS, oUser);
    if (!GetIsObjectValid(oArrows))
        return;
    if(GetLocalInt(oArrows, "scy_AA_prop_count") == 0){
        itemproperty prop = GetFirstItemProperty(oArrows);
        int add, dloc, damtype, dam, dieS, dieC, subtype, pcount, type, i;
        string sDam;
        while(GetIsItemPropertyValid(prop)){
            add = 0;
            type = GetItemPropertyType(prop);
            // Only deal with damage and damage vs alignment for the moment
            if(type == ITEM_PROPERTY_DAMAGE_BONUS){
                add = 1;
                damtype = GetItemPropertySubType(prop);
                dam = GetItemPropertyCostTableValue(prop);
                subtype = 0;
            }else if(type == ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP){
                subtype = GetItemPropertySubType(prop);
                add = 1;
                damtype = GetItemPropertyParam1Value(prop);
                dam = GetItemPropertyCostTableValue(prop);
            }
            if(add == 1){
                pcount ++;
                /* This code works and is elegant (well i think so) but it uses a 2da fetch so i changed it
                // Get the damage amount from the label in the 2da
                sDam = Get2DAString("iprp_damagecost", "Label", dam);
                // This section extracts the die size and count from the string
                // First find the "d" position
                dloc = 0;
                for(i = 0;i < GetStringLength(sDam); i++){
                    if(GetSubString(sDam, i, 1) == "d"){
                        dloc = i;
                        i = 100;
                    }
                }
                // if there is no "d" the damage is not random
                if(dloc == 0){
                    dieS = StringToInt(sDam);
                    dieC = 0;
                }else{
                    dieS = StringToInt(GetStringRight(sDam, GetStringLength(sDam) - (dloc + 1)));
                    dieC = StringToInt(GetStringLeft(sDam, dloc));
                } */
 
                // Instead just use a really big switch statement
                // See iprp_damagecost.2da
                switch (dam){
                    case 1:
                    case 2:
                    case 3:
                    case 4:
                    case 5://+1-5
                        dieS = dam; dieC = 0; break;
                    case 6:
                    case 7:
                    case 8:
                    case 9://+1d4 1d6 1d8 1d10
                        dieS = 2*(dam - 4); dieC = 1; break;
                    case 10://+2d6
                        dieS = 6; dieC = 2; break;
                    case 11://+2d8
                        dieS = 8; dieC = 2; break;
                    case 12://+2d4
                        dieS = 4; dieC = 2; break;
                    case 13://+2d10
                        dieS = 10; dieC = 2; break;
                    case 14://+1d12
                        dieS = 12; dieC = 1; break;
                    case 15://+2d12
                        dieS = 12; dieC = 2; break;
                    case 16:
                    case 17:
                    case 18:
                    case 19:
                    case 20:
                    case 21:
                    case 22:
                    case 23:
                    case 24:
                    case 25:
                    case 26:
                    case 27:
                    case 28:
                    case 29:
                    case 30://+6-20
                        dieS = dam - 10; dieC = 0; break;
                }
                SendMessageToPC(oUser, IntToString(dieC)+ "d" + IntToString(dieS) + " " + Get2DAString("iprp_damagetype", "Label", damtype) + " damage");
                // iprp_damagetypes has two unused damage lines, need to "delete" them
                if(damtype > 2)
                    damtype -= 2;
                // the bitshift is because the damage types are 1 2 4 8 etc in nwscript
                damtype = 1 << damtype;
                //Store the values on the arrows themselves
                SetLocalInt(oArrows, "scy_AA_prop" + IntToString(pcount) + "_damtype", damtype);
                SetLocalInt(oArrows, "scy_AA_prop" + IntToString(pcount) + "_diesize", dieS);
                SetLocalInt(oArrows, "scy_AA_prop" + IntToString(pcount) + "_diecount", dieC);
                SetLocalInt(oArrows, "scy_AA_prop" + IntToString(pcount) + "_align", subtype);
            }
            prop = GetNextItemProperty(oArrows);
        }
        // If we have no props, make sure it doesnt check them again
        if (pcount == 0)
            SetLocalInt(oArrows, "scy_AA_prop_count", 255);
        else
            SetLocalInt(oArrows, "scy_AA_prop_count", pcount);
    }
}
 
 
/*void main()
{
effect en = GetArcaneArrowDamage(GetPCSpeaker(), GetPCSpeaker());
ApplyEffectToObject(DURATION_TYPE_INSTANT, en, GetPCSpeaker());
} */
 

Hail:


//::///////////////////////////////////////////////
//:: x1_s2_hailarrow
//:: Copyright © 2001 Bioware Corp.
//:://////////////////////////////////////////////
/*
    One arrow per arcane archer level at all targets
 
    GZ SEPTEMBER 2003
        Added damage penetration
    Twainer 20/10/2005
        Updated to use my arrow damage functions
        Also uses up arrows now
 
*/
//:://////////////////////////////////////////////
//:: Created By:
//:: Created On:
//:://////////////////////////////////////////////
#include "x0_i0_spells"
#include "scy_aa_inc"
// GZ: 2003-07-23 fixed criticals not being honored
void DoAttack(object oTarget)
{
    //
    int nTouch = TouchAttackRanged(oTarget);
    if (nTouch > 0)
    {
        effect eDamage = GetArcaneArrowDamage(OBJECT_SELF, oTarget, nTouch == 2);
        ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget);
    }
}
 
void main()
{
 
    object oTarget;
 
    int nLevel = GetLevelByclass(class_TYPE_ARCANE_ARCHER, OBJECT_SELF);
    int i = 0;
    int ntargets = 0;
    float fDist = 0.0;
    float fDelay = 0.0;
 
 
 
    object oArrows = GetItemInSlot(INVENTORY_SLOT_ARROWS);
    if (GetLocalInt(oArrows, "scy_AA_prop_count") == 0)
    {
      StoreArrowDamage(OBJECT_SELF);
    }
    for (i = 1; i <= nLevel; i++)
    {
        oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i);
        if (GetIsObjectValid(oTarget) == TRUE)
        {
            fDist = GetDistanceBetween(OBJECT_SELF, oTarget);
            fDelay = fDist/(3.0 * log(fDist) + 2.0);
 
            //Fire cast spell at event for the specified target
            SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, 603));
            effect eArrow = EffectVisualEffect(357);
            ApplyEffectToObject(DURATION_TYPE_INSTANT, eArrow, oTarget);
            DelayCommand(fDelay, DoAttack(oTarget));
            ntargets++;
        }
    }
    //SendMessageToPC(OBJECT_SELF, IntToString(ntargets) + " Targets " + IntToString(GetItemStackSize(oArrows)));
    SetItemStackSize(oArrows, GetItemStackSize(oArrows) - ntargets);
    // If they need more arrows than they had (or just enough) destroy the stack
    if (GetItemStackSize(oArrows) - ntargets <= 0)
        DestroyObject(oArrows, fDelay + 0.5);
 
    //recharge the feat
    DelayCommand(10.0f, IncrementRemainingFeatUses(OBJECT_SELF, FEAT_PRESTIGE_HAIL_OF_ARROWS));
}

 
Seeker:

 
//::///////////////////////////////////////////////
//:: x1_s2_seeker
//:: Copyright © 2001 Bioware Corp.
//:://////////////////////////////////////////////
/*
    Seeker Arrow
     - creates an arrow that automatically hits target.
     - normal arrow damage, based on base item type
 
     - Must have shortbow or longbow in hand.
 
 
     APRIL 2003
     - gave it double damage to balance for the fact
       that since its a spell you are losing
       all your other attack actions
 
     SEPTEMBER 2003 (GZ)
        Added damage penetration
        Added correct enchantment bonus
     Twainer 20/10/2005
        Updated to use my arrow damage funtions
        Does x3 damage (bow crit) instead of x2
        Uses an arrow
*/
//:://////////////////////////////////////////////
//:: Created By:
//:: Created On:
//:://////////////////////////////////////////////
#include "x0_i0_spells"
#include "scy_aa_inc"
void main()
{
    object oTarget = GetSpellTargetObject();
    object oArrows = GetItemInSlot(INVENTORY_SLOT_ARROWS);
    if (GetLocalInt(oArrows, "scy_AA_prop_count") == 0)
    {
      StoreArrowDamage(OBJECT_SELF);
    }
    if (GetIsObjectValid(oTarget) == TRUE){
        effect eDamage = GetArcaneArrowDamage(OBJECT_SELF, oTarget, 1);
        //Fire cast spell at event for the specified target
        SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, 601));
        ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget);
    }
    if (GetItemStackSize(oArrows) == 1)
        DestroyObject(oArrows);
    else
        SetItemStackSize(oArrows, GetItemStackSize(oArrows) -1);
}

Lastly the whole switch statement in the include. This is another problem with AAs as I understand it. The default enhanced arrows only apply for arrows with out properties. This switch statement I believe was meant to override that, however a) it seems a bit off to me. although I am not a scripter, and B) I believe it only applies to hail and seeker (not AA arrows in general), so I would also be interested in any ideas for modifying enhanced arrows (both for seeker and hail, as well as just generally) so that the enhancement applies to arrows with properites, or provides some other sort of bonus.

Cheers all!
Laz

Modifié par Lazarus Magni, 04 décembre 2011 - 06:55 .


#2
WhiZard

WhiZard
  • Members
  • 1 204 messages
EDIT: If the post 40 damage is the issue then the problem could be in the include.

Modifié par WhiZard, 04 décembre 2011 - 01:59 .


#3
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
Well since I am not really a scripter, perhaps you all can help me understand these a bit more.

The default versions of the scripts don’t use the AA’s equipped arrows either correct? It’s more like a spell (or touch attack) that just does piercing damage isn’t it?

Perhaps it’s just not possible to code this so that it actually uses the AA’s equipped arrows, in which case I guess I should look for some other way to make these useful, or just chalk these abilities up to being worthless in Av3. I am betting however there is a way to improve these.

Am I also correct in my assumption that AA enhanced arrows don’t apply if the arrow has any properties on it? And as far as these scripts relate to these 2 abilities that whole switch statement… am I correct in my interpretation that it is related to the enhancement of the hailed and seeker “arrows”?

#4
WhiZard

WhiZard
  • Members
  • 1 204 messages

Lazarus Magni wrote...

Well since I am not really a scripter, perhaps you all can help me understand these a bit more.

The default versions of the scripts don’t use the AA’s equipped arrows either correct? It’s more like a spell (or touch attack) that just does piercing damage isn’t it?

Perhaps it’s just not possible to code this so that it actually uses the AA’s equipped arrows, in which case I guess I should look for some other way to make these useful, or just chalk these abilities up to being worthless in Av3. I am betting however there is a way to improve these.

Am I also correct in my assumption that AA enhanced arrows don’t apply if the arrow has any properties on it? And as far as these scripts relate to these 2 abilities that whole switch statement… am I correct in my interpretation that it is related to the enhancement of the hailed and seeker “arrows”?


This code does use equipped arrows and as far as the include shows it seems to work beautifully at getting the arrow properties and applying damage.  You do need a longbow or shortbow equipped for the system to work, but short of that it works fine.


EDIT: For Hail of arrows I noticed the following bug: destroys the arrows if there are not a sufficient number after decrementing the number (thus if you had 11 and used 6 then the number would reduce to 5 and since 5 is not sufficent the rest of the arrows would be destroyed).

If possible the post 40 include may indicate if anything is awry in applying that damage, but outside of that you probably didn't equip a bow when testing the script.

Modifié par WhiZard, 04 décembre 2011 - 07:18 .


#5
WhiZard

WhiZard
  • Members
  • 1 204 messages
I have one other possibility why this might not be working for you. Do you get any feedback of a script error from any of the involved scripts when using this ability? If the post 40 include is faulty (like performing a loop more than 125 times) the engine can terminate the script prematurely.

#6
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
I did test with a long bow equiped. It appears that it doesn't actually use the equiped arrows, but rather checks the equiped arrows properties, and than does the same damage types.

The destroy bug, doesn't seem to be a big issue since stack sizes on Av3 are set to 999. A player might lose a few arrows here and there, but I don't think it's a big deal.

The problem I believe, is due to the fact that the core post 40 bonus mele dmg is applied to the toon, and not to the weapon (or in this case the amo), just like it is for divine might. I think what we need is a way to retrieve the damage on the toon, and pass that on to the damage done by these "arrows". This still would not fix it not actually using the real arrows, but would compensate for this.

#7
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
We do not get any script errors when using this ability.

#8
WhiZard

WhiZard
  • Members
  • 1 204 messages

Lazarus Magni wrote...

I did test with a long bow equiped. It appears that it doesn't actually use the equiped arrows, but rather checks the equiped arrows properties, and than does the same damage types.

It does decrement arrows (unless you are using unlimited ammo).  However, this is not the equivalent to normal firing of arrows as DR penetration is impossible by script (the command EffectDamage() does not work with the penetration argument) and various on-hits will not apply.
The only default attack rolls via script is the touch attack (ranged or melee).  Ranged touch attack respects the dex modifier, the AB bonus on the bow, and the BAB.  You could try to see if your scripters could implement a different attack roll system to suit any custom needs you might have.
On-hit replication would require a lot of script work which may feed into other scripts.

The problem I believe, is due to the fact that the core post 40 bonus mele dmg is applied to the toon, and not to the weapon (or in this case the amo), just like it is for divine might. I think what we need is a way to retrieve the damage on the toon, and pass that on to the damage done by these "arrows". This still would not fix it not actually using the real arrows, but would compensate for this.


When applying damage you could set whatever desired portion of it (and item's tag or resref that is causing it) as a local variable on the target, and then when accessing the target's OnDamaged script you can then check the pertaining local variables.

Modifié par WhiZard, 04 décembre 2011 - 11:48 .


#9
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
Question.

has your iprp_damagecost.2da been modified? If it has none of the content that has been added to it will be taken into account with the switch statment that was added to advoid the 2da read.

If you do change the script to go back to the 2da read. Modify it so it reads the correct lines from the 2da instead of breaking up the lable to get them. The 2da reads will be a lot faster then the string functions breaking up the lable.

#10
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
Well we use CEP 2.3, so yes it has been modified, and this was written in 2005 so it's possible that was pre CEP 2.3? iprp_damagecost.2da has not been modified further, but perhaps you're on to something here Light if this was written for CEP 2.2 or eariler?

Modifié par Lazarus Magni, 05 décembre 2011 - 09:27 .


#11
WhiZard

WhiZard
  • Members
  • 1 204 messages

Lazarus Magni wrote...

Well we use CEP 2.3, so yes it has been modified, and this was written in 2005 so it's possible that was pre CEP 2.3? iprp_damagecost.2da has not been modified further, but perhaps you're on to something here Light if this was written for CEP 2.2 or eariler?


It is written for the vanilla NWN iprp_damagecost.2da.  If the CEP modified this then use the commented out 2da fetch instead of the switch.

#12
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
Thanks Whizard, I will give that a try. So just un-comment the 2da fetch, and comment out the switch I take it?

#13
WhiZard

WhiZard
  • Members
  • 1 204 messages
Correct, though Lightfoot8 has the excellent idea of replacing the commented out section with

dieC = StringToInt(Get2DAString("iprp_damagecost", "NumDice", dam));
dieS = StringToInt(Get2DAString("iprp_damagecost", "Die", dam));

and then commenting out the switch

#14
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
This does indeed work, but it seems only to achieve the same effect. The dmg is still woefully low for high level AAs. I am convinced that getting it to retrieve (and deal) the dmg bonus applied to the PC, in addition to what it does now would solve this. Is there no way to retrieve this information and apply it to the effect?

#15
WhiZard

WhiZard
  • Members
  • 1 204 messages

Lazarus Magni wrote...

This does indeed work, but it seems only to achieve the same effect. The dmg is still woefully low for high level AAs. I am convinced that getting it to retrieve (and deal) the dmg bonus applied to the PC, in addition to what it does now would solve this. Is there no way to retrieve this information and apply it to the effect?


Effect disecting cannot be done.  The components of effects can be stored as local variables by each and every script that produces these effects, however, EffectDamageIncrease() by itself cannot be reduced to its components by script.

#16
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
Hmm that's a shame, as this has been requested by multiple players on our server. I guess I will have to look into some other way to boost these abilites. We have this same issue with smite, and Ki critical.

#17
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
BTW in case it is helpfull this is the core_inc_post40 script:


#include "x2_inc_itemprop"
 
const int MAX_AB_BONUS = 20;               // maximum ab bonus to give out to keep values from overflowing
 
int pf_GetLevelFromXP( int nXP )
{
  int nLevel = FloatToInt(( sqrt(( IntToFloat( nXP ) * 20 ) + 2500 ) / 100 ) + 0.5 );
  return nLevel;
}
 
float calcMeleeFactor( object oPC )
{
  int nLevels = 160;
  nLevels -= ( GetLevelByclass( class_TYPE_WIZARD, oPC ) * 4 );
  nLevels -= ( GetLevelByclass( class_TYPE_SORCERER, oPC ) * 4 );
  nLevels -= ( GetLevelByclass( class_TYPE_PALEMASTER, oPC ) * 3 );
  nLevels -= ( GetLevelByclass( class_TYPE_DRAGON_DISCIPLE, oPC ) * 2 );
  nLevels -= ( GetLevelByclass( class_TYPE_CLERIC, oPC ) * 2 );
  nLevels -= ( GetLevelByclass( class_TYPE_DRUID, oPC ) * 2 );
  nLevels -= ( GetLevelByclass( class_TYPE_SHIFTER, oPC ) * 2 );
  nLevels -= ( GetLevelByclass( class_TYPE_BARD, oPC ) * 2 );
  nLevels -= ( GetLevelByclass( class_TYPE_ROGUE, oPC ) );
  nLevels -= ( GetLevelByclass( class_TYPE_MONK, oPC ) );
  nLevels -= ( GetLevelByclass( class_TYPE_ASSASSIN, oPC ) );
  nLevels -= ( GetLevelByclass( class_TYPE_SHADOWDANCER, oPC ) );
  nLevels -= ( GetLevelByclass( class_TYPE_HARPER, oPC ) );
 
 
  float fFactor = IntToFloat( nLevels ) / 160.0;
  return fFactor;
}
 
void ApplyDmgBonus( object oPC, int nDamageType, int nBonus )
{
  // max damage bonus per type is 20
  effect eDamageBonus;
  switch( nBonus )
  {
    case 0:  break;
    case 1:  nBonus = DAMAGE_BONUS_1;  break;
    case 2:  nBonus = DAMAGE_BONUS_2;  break;
    case 3:  nBonus = DAMAGE_BONUS_3;  break;
    case 4:  nBonus = DAMAGE_BONUS_4;  break;
    case 5:  nBonus = DAMAGE_BONUS_5;  break;
    case 6:  nBonus = DAMAGE_BONUS_6;  break;
    case 7:  nBonus = DAMAGE_BONUS_7;  break;
    case 8:  nBonus = DAMAGE_BONUS_8;  break;
    case 9:  nBonus = DAMAGE_BONUS_9;  break;
    case 10: nBonus = DAMAGE_BONUS_10; break;
    case 11: nBonus = DAMAGE_BONUS_11; break;
    case 12: nBonus = DAMAGE_BONUS_12; break;
    case 13: nBonus = DAMAGE_BONUS_13; break;
    case 14: nBonus = DAMAGE_BONUS_14; break;
    case 15: nBonus = DAMAGE_BONUS_15; break;
    case 16: nBonus = DAMAGE_BONUS_16; break;
    case 17: nBonus = DAMAGE_BONUS_17; break;
    case 18: nBonus = DAMAGE_BONUS_18; break;
    case 19: nBonus = DAMAGE_BONUS_19; break;
    default: nBonus = DAMAGE_BONUS_20; break;
  }
  if ( nBonus != 0 )
  {
    eDamageBonus = SupernaturalEffect( EffectDamageIncrease( nBonus, nDamageType ));
    ApplyEffectToObject( DURATION_TYPE_PERMANENT, eDamageBonus, oPC );
  }
}
 
void ApplyPost40MeleeBonus( object oPC, int nAltTxt = 0 )
{
  // Modifier introduced for barbarian rage
  float fBonusMod = GetLocalFloat(oPC, "core_post_modifier");
  int nXP = GetXP( oPC );
  int nLevel = pf_GetLevelFromXP( nXP );
  if ( nLevel > 39 )
  {
    int nStrMod = GetAbilityModifier( ABILITY_STRENGTH, oPC );
    if ( nStrMod > 11 )
    {
      itemproperty ip = ItemPropertyOnHitCastSpell( 126, 1 ); // planar rift effect
      object oWeapon = GetItemInSlot( INVENTORY_SLOT_RIGHTHAND, oPC );
      if ( IPGetIsMeleeWeapon( oWeapon ))
      {
        IPSafeAddItemProperty( oWeapon, ip, HoursToSeconds( 24 ));
        SendMessageToPC( oPC, "Adding on-hit strength damage to primary weapon" );
      }
    }
 
    string sRune = "You have no melee rune";
    int nPost = ( nLevel - 40 );
    int nPureBonus = 0;
    int nRequiredXP = ((( nLevel * ( nLevel + 1 )) / 2 ) * 1000 );
    float fMeleeFactor = calcMeleeFactor( oPC );
 
    if ( fMeleeFactor == 1.0 )
    {
      if(!nAltTxt)
        SendMessageToPC( oPC, "Additional bonus melee ab/damage for 40 levels of pure melee classes" );
      nPureBonus = nPost/8;
    }
    fMeleeFactor += fBonusMod;
 
    int nAB = FloatToInt( IntToFloat( nPost )/ 2.0 * fMeleeFactor );
 
    if ( GetIsObjectValid( GetItemPossessedBy( oPC, "GrtrMeleeRunV2" )))
    {
      fMeleeFactor *= 1.0 + (0.5);
      sRune = "You have a greater melee rune";
    }
    else if ( GetIsObjectValid( GetItemPossessedBy( oPC, "LesrMeleeRunV2" )))
    {
      fMeleeFactor *= 1.0 + (0.25);
      sRune = "You have a lesser melee rune";
    }
 
    int nBonus = FloatToInt( IntToFloat( nPost ) / 2.0 * fMeleeFactor ) + nPureBonus;
 
    nAB = ( nAB > MAX_AB_BONUS ? MAX_AB_BONUS : nAB );
 
    ApplyEffectToObject( DURATION_TYPE_PERMANENT, SupernaturalEffect( EffectAttackIncrease( nAB, ATTACK_BONUS_MISC )), oPC );
 
    if(!nAltTxt)
        SendMessageToPC( oPC, sRune + " and enough experience for level " +
                              IntToString( nLevel ) + " which gives you +" + IntToString( nAB ) +
                              " bonus AB and " + IntToString( nBonus ) + " bonus damage.  You will reach level " +
                              IntToString( nLevel + 1 ) + " at " + IntToString( nRequiredXP ) + " experience." );
    else
        SendMessageToPC(oPC, "Your Barbarian Rage Has increased your bonus damage to " + IntToString( nBonus ));
 
    ApplyDmgBonus( oPC, DAMAGE_TYPE_SLASHING, nBonus / 6 );
    ApplyDmgBonus( oPC, DAMAGE_TYPE_PIERCING, nBonus / 6 );
    ApplyDmgBonus( oPC, DAMAGE_TYPE_BLUDGEONING, nBonus / 6 + nBonus % 6 );
    ApplyDmgBonus( oPC, DAMAGE_TYPE_FIRE, nBonus / 6 );
    ApplyDmgBonus( oPC, DAMAGE_TYPE_COLD, nBonus / 6 );
    ApplyDmgBonus( oPC, DAMAGE_TYPE_ELECTRICAL, nBonus / 6 );
 
    nBonus -= 120;
    if ( nBonus > 0 )
    {
      ApplyDmgBonus( oPC, DAMAGE_TYPE_ACID, nBonus / 3 );
      ApplyDmgBonus( oPC, DAMAGE_TYPE_SONIC, nBonus / 3 );
      ApplyDmgBonus( oPC, DAMAGE_TYPE_NEGATIVE, nBonus / 3 + nBonus % 3 );
    }
    nBonus -= 60;
    if ( nBonus > 0 )
    {
      ApplyDmgBonus( oPC, DAMAGE_TYPE_DIVINE, nBonus / 3 );
      ApplyDmgBonus( oPC, DAMAGE_TYPE_POSITIVE, nBonus / 3 );
      ApplyDmgBonus( oPC, DAMAGE_TYPE_MAGICAL, nBonus / 3 + nBonus % 3 );
    }
  }
 
  //blinding speed feat changes
  if( GetHasFeat( FEAT_EPIC_BLINDING_SPEED )){
    if( GetLevelByclass( class_TYPE_PALEMASTER, oPC ) < 1 )
    {
        //get the dex mod of oPC
        int iDexMod = GetAbilityModifier( ABILITY_DEXTERITY, oPC );
        //apply this as a deflection armour bonus
        int iDexACBonus = (iDexMod * 7) / 10;
        if ( iDexACBonus > 0 )
        {
        effect eDexMod = EffectACIncrease( iDexACBonus, AC_DEFLECTION_BONUS );
        ApplyEffectToObject( DURATION_TYPE_PERMANENT, SupernaturalEffect( eDexMod ), oPC );
        SendMessageToPC( oPC, "Giving +" + IntToString( iDexACBonus ) + " AC for blinding speed" );
        }
    }
    else
        SendMessageToPC( oPC, "Palemasters do not get a bonus from blinding speed" );
  }
}
 
void ClearPost40Bonus( object oPC )
{
  // iterate through the current effects on the pc and
  // remove all attack and damage effects before adding
  // new ones
  effect eBuff = GetFirstEffect( oPC );
  while( GetIsEffectValid( eBuff ))
  {
    if (( GetEffectDurationType( eBuff ) == DURATION_TYPE_PERMANENT ) &&
        ( GetEffectSubType( eBuff ) == SUBTYPE_SUPERNATURAL ))
    {
      // only checks for permanent, supernatural buffs
      int nType = GetEffectType( eBuff );
      // remove our subtypes
      switch( nType )
      {
        case EFFECT_TYPE_ATTACK_INCREASE:
        case EFFECT_TYPE_DAMAGE_INCREASE:
        case EFFECT_TYPE_IMMUNITY:
        case EFFECT_TYPE_AC_INCREASE:
        case EFFECT_TYPE_AC_DECREASE:
        case EFFECT_TYPE_SKILL_DECREASE:
          RemoveEffect( oPC, eBuff );
          break;
      }
    }
    eBuff = GetNextEffect( oPC );
  }
}

Modifié par Lazarus Magni, 06 décembre 2011 - 02:34 .


#18
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
And the include:

Modifié par Lazarus Magni, 06 décembre 2011 - 02:40 .


#19
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
#include "core_debug"
 
//::///////////////////////////////////////////////
//:: Item Property Functions
//:: x2_inc_itemprop
//:: Copyright © 2003 Bioware Corp.
//:://////////////////////////////////////////////
/*
 
    Holds item property and item modification
    specific code.
 
    If you look for anything specific to item
    properties, your chances are good to find it
    in here.
 
*/
//:://////////////////////////////////////////////
//:: Created By: Georg Zoeller
//:: Created On: 2003-06-05
//:: Last Update: 2003-10-07
//:://////////////////////////////////////////////
 
// *  The tag of the ip work container, a placeable which has to be set into each
// *  module that is using any of the crafting functions.
const string  X2_IP_WORK_CONTAINER_TAG = "x2_plc_ipbox";
// *  2da for the AddProperty ItemProperty
const string X2_IP_ADDRPOP_2DA = "des_crft_props" ;
// *  2da for the Poison Weapon Itemproperty
const string X2_IP_POISONWEAPON_2DA = "des_crft_poison" ;
// *  2da for armor appearance
const string X2_IP_ARMORPARTS_2DA = "des_crft_aparts" ;
// *  2da for armor appearance
const string X2_IP_ARMORAPPEARANCE_2DA = "des_crft_appear" ;
 
// * Base custom token for item modification conversations (do not change unless you want to change the conversation too)
const int    XP_IP_ITEMMODCONVERSATION_CTOKENBASE = 12220;
const int    X2_IP_ITEMMODCONVERSATION_MODE_TAILOR = 0;
const int    X2_IP_ITEMMODCONVERSATION_MODE_CRAFT = 1;
 
// * Number of maximum item properties allowed on most items
const int    X2_IP_MAX_ITEM_PROPERTIES = 8;
 
// *  Constants used with the armor modification system
const int    X2_IP_ARMORTYPE_NEXT = 0;
const int    X2_IP_ARMORTYPE_PREV = 1;
const int    X2_IP_ARMORTYPE_RANDOM = 2;
const int    X2_IP_WEAPONTYPE_NEXT = 0;
const int    X2_IP_WEAPONTYPE_PREV = 1;
const int    X2_IP_WEAPONTYPE_RANDOM = 2;
 
// *  Policy constants for IPSafeAddItemProperty()
const int    X2_IP_ADDPROP_POLICY_REPLACE_EXISTING = 0;
const int    X2_IP_ADDPROP_POLICY_KEEP_EXISTING = 1;
const int    X2_IP_ADDPROP_POLICY_IGNORE_EXISTING =2;
 
 
// *  removes all itemproperties with matching nItemPropertyType and nItemPropertyDuration
void  IPRemoveMatchingItemProperties( object oItem, int nItemPropertyType, int nItemPropertyDuration = DURATION_TYPE_TEMPORARY, int nItemPropertySubType = -1 );
 
// *  Removes ALL item properties from oItem matching nItemPropertyDuration
void  IPRemoveAllItemProperties( object oItem, int nItemPropertyDuration = DURATION_TYPE_TEMPORARY );
 
// *  returns TRUE if item can be equipped.
// *  Uses Get2DAString, so do not use in a loop!
int   IPGetIsItemEquipable( object oItem );
 
// *  Changes the color of an item armor
// *  oItem        - The armor
// *  nColorType   - ITEM_APPR_ARMOR_COLOR_* constant
// *  nColor       - color from 0 to 63
// *  Since oItem is destroyed in the process, the function returns
// *  the item created with the color changed
object IPDyeArmor( object oItem, int nColorType, int nColor );
 
// *  Returns the container used for item property and appearance modifications in the
// *  module. If it does not exist, create it.
object IPGetIPWorkContainer( object oCaller = OBJECT_SELF );
 
// *  This function needs to be rather extensive and needs to be updated if there are new
// *  ip types we want to use, but it goes into the item property include anyway
itemproperty IPGetItemPropertyByID( int nPropID, int nParam1 = 0, int nParam2 = 0, int nParam3 = 0, int nParam4 = 0 );
 
// *  returns TRUE if oItem is a ranged weapon
int   IPGetIsRangedWeapon( object oItem );
 
// *  return TRUE if oItem is a melee weapon
int   IPGetIsMeleeWeapon( object oItem );
 
// *  return TRUE if oItem is a projectile (bolt, arrow, etc)
int   IPGetIsProjectile( object oItem );
 
// *  returns true if weapon is blugeoning (used for poison)
// *  This uses Get2DAstring, so it is slow. Avoid using in loops!
int   IPGetIsBludgeoningWeapon( object oItem );
 
// *  Return the IP_CONST_CASTSPELL_* ID matching to the SPELL_* constant given in nSPELL_ID
// *  This uses Get2DAstring, so it is slow. Avoid using in loops!
// *  returns -1 if there is no matching property for a spell
int   IPGetIPConstCastSpellFromSpellID( int nSpellID );
 
// *  Returns TRUE if an item has ITEM_PROPERTY_ON_HIT and the specified nSubType
// *  possible values for nSubType can be taken from IPRP_ONHIT.2da
// *  popular ones:
// *  5 - Daze   19 - ItemPoison   24 - Vorpal
int   IPGetItemHasItemOnHitPropertySubType( object oTarget, int nSubType );
 
// *  Returns the number of possible armor part variations for the specified part
// *  nPart - ITEM_APPR_ARMOR_MODEL_* constant
// *  Uses Get2DAstring, so do not use in loops
int   IPGetNumberOfAppearances( int nPart );
 
 
// *  Returns the next valid appearance type for oArmor
// *  nPart - ITEM_APPR_ARMOR_MODEL_* constant
// *  Uses Get2DAstring, so do not use in loops
int   IPGetNextArmorAppearanceType(object oArmor, int nPart);
 
// *  Returns the previous valid appearance type for oArmor
// *  nPart - ITEM_APPR_ARMOR_MODEL_* constant
// *  Uses Get2DAstring, so do not use in loops
int   IPGetPrevArmorAppearanceType(object oArmor, int nPart);
 
// *  Returns a random valid appearance type for oArmor
// *  nPart - ITEM_APPR_ARMOR_MODEL_* constant
// *  Uses Get2DAstring, so do not use in loops
int   IPGetRandomArmorAppearanceType(object oArmor, int nPart);
 
// * Returns a new armor based of oArmor with nPartModified
// * nPart - ITEM_APPR_ARMOR_MODEL_* constant of the part to be changed
// * nMode -
// *        X2_IP_ARMORTYPE_NEXT    - next valid appearance
// *        X2_IP_ARMORTYPE_PREV    - previous valid apperance;
// *        X2_IP_ARMORTYPE_RANDOM  - random valid appearance;
// *
// * bDestroyOldOnSuccess - Destroy oArmor in process?
// * Uses Get2DAstring, so do not use in loops
object IPGetModifiedArmor(object oArmor, int nPart, int nMode, int bDestroyOldOnSuccess);
 
// *  Add an item property in a safe fashion, preventing unwanted stacking
// *  Parameters:
// *   oItem     - the item to add the property to
// *   ip        - the itemproperty to add
// *   fDuration - set 0.0f to add the property permanent, anything else is temporary
// *   nAddItemPropertyPolicy - How to handle existing properties. Valid values are:
// *      X2_IP_ADDPROP_POLICY_REPLACE_EXISTING - remove any property of the same type, subtype, durationtype before adding;
// *      X2_IP_ADDPROP_POLICY_KEEP_EXISTING - do not add if any property with same type, subtype and durationtype already exists;
// *      X2_IP_ADDPROP_POLICY_IGNORE_EXISTING - add itemproperty in any case - Do not Use with OnHit or OnHitSpellCast props!
// *
// *  bIgnoreDurationType - If set to TRUE, an item property will be considered identical even if the DurationType is different. Be careful when using this
// *                        with X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, as this could lead to a temporary item property removing a permanent one
// *  bIgnoreSubType      - If set to TRUE an item property will be considered identical even if the SubType is different.
void  IPSafeAddItemProperty(object oItem, itemproperty ip, float fDuration =0.0f, int nAddItemPropertyPolicy = X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, int bIgnoreDurationType = FALSE, int bIgnoreSubType = FALSE);
 
// *  Wrapper for GetItemHasItemProperty that returns true if
// *  oItem has an itemproperty that matches ipCompareTo by Type AND DurationType AND SubType
// *  nDurationType = Valid DURATION_TYPE_* or -1 to ignore
// *  bIgnoreSubType - If set to TRUE an item property will be considered identical even if the SubType is different.
int   IPGetItemHasProperty(object oItem, itemproperty ipCompareTo, int nDurationType, int bIgnoreSubType = FALSE);
 
// *  returns FALSE it the item has no sequencer property
// *  returns number of spells that can be stored in any other case
int   IPGetItemSequencerProperty(object oItem);
 
// *  returns TRUE if the item has the OnHit:IntelligentWeapon property.
int   IPGetIsIntelligentWeapon(object oItem);
 
// *  Mapping between numbers and power constants for ITEM_PROPERTY_DAMAGE_BONUS
// *  returns the appropriate ITEM_PROPERTY_DAMAGE_POWER_* constant for nNumber
int   IPGetDamagePowerConstantFromNumber(int nNumber);
 
// *  returns the appropriate ITEM_PROPERTY_DAMAGE_BONUS_= constant for nNumber
// *  Do not pass in any number <1 ! Will return -1 on error
int   IPGetDamageBonusConstantFromNumber(int nNumber);
 
// *  Special Version of Copy Item Properties for use with greater wild shape
// *  oOld - Item equipped before polymorphing (source for item props)
// *  oNew - Item equipped after polymorphing  (target for item props)
// *  bWeapon - Must be set TRUE when oOld is a weapon.
void  IPWildShapeCopyItemProperties(object oOld, object oNew, int bWeapon = FALSE);
 
// *  Returns the current enhancement bonus of a weapon (+1 to +20), 0 if there is
// *  no enhancement bonus. You can test for a specific type of enhancement bonus
// *  by passing the appropritate ITEM_PROPERTY_ENHANCEMENT_BONUS* constant into
// *  nEnhancementBonusType
int   IPGetWeaponEnhancementBonus(object oWeapon, int nEnhancementBonusType = ITEM_PROPERTY_ENHANCEMENT_BONUS);
 
// *  Shortcut function to set the enhancement bonus of a weapon to a certain bonus
// *  Specifying bOnlyIfHigher as TRUE will prevent a bonus lower than the requested
// *  bonus from being applied. Valid values for nBonus are 1 to 20.
void  IPSetWeaponEnhancementBonus(object oWeapon, int nBonus, int bOnlyIfHigher = TRUE);
 
// *  Shortcut function to upgrade the enhancement bonus of a weapon by the
// *  number specified in nUpgradeBy. If the resulting new enhancement bonus
// *  would be out of bounds (>+20), it will be set to +20
void  IPUpgradeWeaponEnhancementBonus(object oWeapon, int nUpgradeBy);
 
// *  Returns TRUE if a character has any item equipped that has the itemproperty
// *  defined in nItemPropertyConst in it (ITEM_PROPERTY_* constant)
int   IPGetHasItemPropertyOnCharacter(object oPC, int nItemPropertyConst);
 
// *  Returns an integer with the number of properties present oItem
int   IPGetNumberOfItemProperties(object oItem);
 
 
 
//------------------------------------------------------------------------------
//                         I M P L E M E N T A T I O N
//------------------------------------------------------------------------------
 
// ----------------------------------------------------------------------------
// Removes all itemproperties with matching nItemPropertyType and
// nItemPropertyDuration (a DURATION_TYPE_* constant)
// ----------------------------------------------------------------------------
void IPRemoveMatchingItemProperties(object oItem, int nItemPropertyType, int nItemPropertyDuration = DURATION_TYPE_TEMPORARY, int nItemPropertySubType = -1)
{
    itemproperty ip = GetFirstItemProperty(oItem);
 
    // valid ip?
    while (GetIsItemPropertyValid(ip))
    {
        // same property type?
        if ((GetItemPropertyType(ip) == nItemPropertyType))
        {
            // same duration or duration ignored?
            if (GetItemPropertyDurationType(ip) == nItemPropertyDuration || nItemPropertyDuration == -1)
            {
                 // same subtype or subtype ignored
                 if  (GetItemPropertySubType(ip) == nItemPropertySubType || nItemPropertySubType == -1)
                 {
                      // Put a warning into the logfile if someone tries to remove a permanent ip with a temporary one!
                      /*if (nItemPropertyDuration == DURATION_TYPE_TEMPORARY &&  GetItemPropertyDurationType(ip) == DURATION_TYPE_PERMANENT)
                      {
                         WriteTimestampedLogEntry("x2_inc_itemprop:: IPRemoveMatchingItemProperties() - WARNING: Permanent item property removed by temporary on "+GetTag(oItem));
                      }
                      */
                      RemoveItemProperty(oItem, ip);
                 }
            }
        }
        ip = GetNextItemProperty(oItem);
    }
}
 
// ----------------------------------------------------------------------------
// Removes ALL item properties from oItem matching nItemPropertyDuration
// ----------------------------------------------------------------------------
void IPRemoveAllItemProperties(object oItem, int nItemPropertyDuration = DURATION_TYPE_TEMPORARY)
{
    itemproperty ip = GetFirstItemProperty(oItem);
    while (GetIsItemPropertyValid(ip))
    {
        if (GetItemPropertyDurationType(ip) == nItemPropertyDuration)
        {
            RemoveItemProperty(oItem, ip);
        }
        ip = GetNextItemProperty(oItem);
    }
}
 
// ----------------------------------------------------------------------------
// returns TRUE if item can be equipped. Uses Get2DAString, so do not use in a loop!
// ----------------------------------------------------------------------------
int IPGetIsItemEquipable(object oItem)
{
    int nBaseType =GetBaseItemType(oItem);
    string sResult = Get2DAString("baseitems","EquipableSlots",nBaseType);
    return  (sResult != "0x00000");
}
 
// ----------------------------------------------------------------------------
// Changes the color of an item armor
// oItem        - The armor
// nColorType   - ITEM_APPR_ARMOR_COLOR_* constant
// nColor       - color from 0 to 63
// Since oItem is destroyed in the process, the function returns
// the item created with the color changed
// ----------------------------------------------------------------------------
object IPDyeArmor(object oItem, int nColorType, int nColor)
{
        object oRet = CopyItemAndModify(oItem,ITEM_APPR_TYPE_ARMOR_COLOR,nColorType,nColor,TRUE);
        DestroyObject(oItem); // remove old item
        return oRet; //return new item
}
 
// ----------------------------------------------------------------------------
// Returns the container used for item property and appearance modifications in the
// module. If it does not exist, it is created
// ----------------------------------------------------------------------------
object IPGetIPWorkContainer(object oCaller = OBJECT_SELF)
{
    PrintString( "IPGetIPWorkContainer" );
    object oRet = DbgGetObjectByTag(X2_IP_WORK_CONTAINER_TAG);
    if (oRet == OBJECT_INVALID)
    {
        PrintString( "creating IPGetIPWorkContainer" );
        oRet = DbgCreateObject(OBJECT_TYPE_PLACEABLE,X2_IP_WORK_CONTAINER_TAG,GetLocation(oCaller));
        effect eInvis =  EffectVisualEffect( VFX_DUR_CUTSCENE_INVISIBILITY);
        eInvis = ExtraordinaryEffect(eInvis);
        ApplyEffectToObject(DURATION_TYPE_PERMANENT,eInvis,oRet);
        if (oRet == OBJECT_INVALID)
        {
            WriteTimestampedLogEntry("x2_inc_itemprop - critical: Missing container with tag " +X2_IP_WORK_CONTAINER_TAG + "!!");
        }
    }
 
 
    return oRet;
}
 
// ----------------------------------------------------------------------------
// This function needs to be rather extensive and needs to be updated if there are new
// ip types we want to use, but it goes into the item property include anyway
// ----------------------------------------------------------------------------
itemproperty IPGetItemPropertyByID(int nPropID, int nParam1 = 0, int nParam2 = 0, int nParam3 = 0, int nParam4 = 0)
{
   itemproperty ipRet;
 
   if (nPropID == ITEM_PROPERTY_ABILITY_BONUS)
   {
        ipRet = ItemPropertyAbilityBonus(nParam1, nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_AC_BONUS)
   {
        ipRet = ItemPropertyACBonus(nParam1);
   }
   else if (nPropID == ITEM_PROPERTY_AC_BONUS_VS_ALIGNMENT_GROUP)
   {
        ipRet = ItemPropertyACBonusVsAlign(nParam1, nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_AC_BONUS_VS_DAMAGE_TYPE)
   {
        ipRet = ItemPropertyACBonusVsDmgType(nParam1, nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_AC_BONUS_VS_RACIAL_GROUP)
   {
        ipRet = ItemPropertyACBonusVsRace(nParam1, nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_AC_BONUS_VS_SPECIFIC_ALIGNMENT)
   {
        ipRet = ItemPropertyACBonusVsSAlign(nParam1, nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_ATTACK_BONUS)
   {
        ipRet = ItemPropertyAttackBonus(nParam1);
   }
   else if (nPropID == ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP)
   {
        ipRet = ItemPropertyAttackBonusVsAlign(nParam1, nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_ATTACK_BONUS_VS_RACIAL_GROUP)
   {
        ipRet = ItemPropertyAttackBonusVsRace(nParam1, nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNMENT)
   {
        ipRet = ItemPropertyAttackBonusVsSAlign(nParam1, nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_BASE_ITEM_WEIGHT_REDUCTION)
   {
        ipRet = ItemPropertyWeightReduction(nParam1);
   }
   else if (nPropID == ITEM_PROPERTY_BONUS_FEAT)
   {
        ipRet = ItemPropertyBonusFeat(nParam1);
   }
   else if (nPropID == ITEM_PROPERTY_BONUS_SPELL_SLOT_OF_LEVEL_N)
   {
        ipRet = ItemPropertyBonusLevelSpell(nParam1, nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_CAST_SPELL)
   {
        ipRet = ItemPropertyCastSpell(nParam1, nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_DAMAGE_BONUS)
   {
        ipRet = ItemPropertyDamageBonus(nParam1, nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP)
   {
        ipRet = ItemPropertyDamageBonusVsAlign(nParam1, nParam2, nParam3);
   }
   else if (nPropID == ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP)
   {
        ipRet = ItemPropertyDamageBonusVsRace(nParam1, nParam2, nParam3);
   }
   else if (nPropID == ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNMENT)
   {
        ipRet = ItemPropertyDamageBonusVsSAlign(nParam1, nParam2, nParam3);
   }
   else if (nPropID == ITEM_PROPERTY_DAMAGE_REDUCTION)
   {
        ipRet = ItemPropertyDamageReduction(nParam1, nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_DAMAGE_RESISTANCE)
   {
        ipRet = ItemPropertyDamageResistance(nParam1, nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_DAMAGE_VULNERABILITY)
   {
        ipRet = ItemPropertyDamageResistance(nParam1, nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_DARKVISION)
   {
        ipRet = ItemPropertyDarkvision();
   }
   else if (nPropID == ITEM_PROPERTY_DECREASED_ABILITY_SCORE)
   {
        ipRet = ItemPropertyDecreaseAbility(nParam1,nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_DECREASED_AC)
   {
        ipRet = ItemPropertyDecreaseAC(nParam1,nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER)
   {
        ipRet = ItemPropertyAttackPenalty(nParam1);
   }
   else if (nPropID == ITEM_PROPERTY_DECREASED_ENHANCEMENT_MODIFIER)
   {
        ipRet = ItemPropertyEnhancementPenalty(nParam1);
   }
   else if (nPropID == ITEM_PROPERTY_DECREASED_SAVING_THROWS)
   {
        ipRet = ItemPropertyReducedSavingThrow(nParam1, nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_DECREASED_SAVING_THROWS_SPECIFIC)
   {
        ipRet = ItemPropertyBonusSavingThrowVsX(nParam1, nParam2);
   }
    else if (nPropID == ITEM_PROPERTY_DECREASED_SKILL_MODIFIER)
   {
        ipRet = ItemPropertyDecreaseSkill(nParam1, nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_ENHANCED_CONTAINER_REDUCED_WEIGHT)
   {
        ipRet = ItemPropertyContainerReducedWeight(nParam1);
   }
   else if (nPropID == ITEM_PROPERTY_ENHANCEMENT_BONUS)
   {
        ipRet = ItemPropertyEnhancementBonus(nParam1);
   }
   else if (nPropID == ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_ALIGNMENT_GROUP)
   {
        ipRet = ItemPropertyEnhancementBonusVsAlign(nParam1,nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_SPECIFIC_ALIGNEMENT)
   {
        ipRet = ItemPropertyEnhancementBonusVsSAlign(nParam1,nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_RACIAL_GROUP)
   {
        ipRet = ItemPropertyEnhancementBonusVsRace(nParam1,nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_EXTRA_MELEE_DAMAGE_TYPE)
   {
        ipRet = ItemPropertyExtraMeleeDamageType(nParam1);
   }
   else if (nPropID == ITEM_PROPERTY_EXTRA_RANGED_DAMAGE_TYPE)
   {
        ipRet = ItemPropertyExtraRangeDamageType(nParam1);
   }
   else if (nPropID == ITEM_PROPERTY_HASTE)
   {
        ipRet = ItemPropertyHaste();
   }
   else if (nPropID == ITEM_PROPERTY_KEEN)
   {
        ipRet = ItemPropertyKeen();
   }
   else if (nPropID == ITEM_PROPERTY_LIGHT)
   {
        ipRet = ItemPropertyLight(nParam1,nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_MASSIVE_CRITICALS)
   {
        ipRet = ItemPropertyMassiveCritical(nParam1);
   }
   else if (nPropID == ITEM_PROPERTY_NO_DAMAGE)
   {
        ipRet = ItemPropertyNoDamage();
   }
   else if (nPropID == ITEM_PROPERTY_ON_HIT_PROPERTIES)
   {
        ipRet = ItemPropertyOnHitProps(nParam1, nParam2, nParam3);
   }
   else if (nPropID == ITEM_PROPERTY_TRAP)
   {
        ipRet = ItemPropertyTrap(nParam1, nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_TRUE_SEEING)
   {
        ipRet = ItemPropertyTrueSeeing();
   }
   else if (nPropID == ITEM_PROPERTY_UNLIMITED_AMMUNITION)
   {
        ipRet = ItemPropertyUnlimitedAmmo(nParam1);
   }
   else if (nPropID == ITEM_PROPERTY_ONHITCASTSPELL)
   {
        ipRet = ItemPropertyOnHitCastSpell(nParam1, nParam2);
   }
   else if (nPropID == ITEM_PROPERTY_ARCANE_SPELL_FAILURE)
   {
        ipRet = ItemPropertyArcaneSpellFailure(nParam1);
   }
 
 
   return ipRet;
}
 
// ----------------------------------------------------------------------------
// Returns TRUE if oItem is a projectile
// ----------------------------------------------------------------------------
int IPGetIsProjectile(object oItem)
{
  int nBT = GetBaseItemType(oItem);
  return (nBT == BASE_ITEM_ARROW || nBT == BASE_ITEM_BOLT || nBT == BASE_ITEM_BULLET);
}
 
// ----------------------------------------------------------------------------
// Returns TRUE if oItem is a ranged weapon
// ----------------------------------------------------------------------------
int IPGetIsRangedWeapon(object oItem)
{
    return GetWeaponRanged(oItem); // doh !
}
 
// ----------------------------------------------------------------------------
// Returns TRUE if oItem is a melee weapon
// ----------------------------------------------------------------------------
int IPGetIsMeleeWeapon(object oItem)
{
    //Declare major variables
    int nItem = GetBaseItemType(oItem);
 
    if((nItem == BASE_ITEM_BASTARDSWORD) ||
      (nItem == BASE_ITEM_BATTLEAXE) ||
      (nItem == BASE_ITEM_DOUBLEAXE) ||
      (nItem == BASE_ITEM_GREATAXE) ||
      (nItem == BASE_ITEM_GREATSWORD) ||
      (nItem == BASE_ITEM_HALBERD) ||
      (nItem == BASE_ITEM_HANDAXE) ||
      (nItem == BASE_ITEM_KAMA) ||
      (nItem == BASE_ITEM_KATANA) ||
      (nItem == BASE_ITEM_KUKRI) ||
      (nItem == BASE_ITEM_LONGSWORD) ||
      (nItem == BASE_ITEM_SCIMITAR) ||
      (nItem == BASE_ITEM_SCYTHE) ||
      (nItem == BASE_ITEM_SICKLE) ||
      (nItem == BASE_ITEM_TWOBLADEDSWORD) ||
      (nItem == BASE_ITEM_CLUB) ||
      (nItem == BASE_ITEM_DAGGER) ||
      (nItem == BASE_ITEM_DIREMACE) ||
      (nItem == BASE_ITEM_HEAVYFLAIL) ||
      (nItem == BASE_ITEM_LIGHTFLAIL) ||
      (nItem == BASE_ITEM_LIGHTHAMMER) ||
      (nItem == BASE_ITEM_LIGHTMACE) ||
      (nItem == BASE_ITEM_MORNINGSTAR) ||
      (nItem == BASE_ITEM_QUARTERSTAFF) ||
      (nItem == BASE_ITEM_MAGICSTAFF) ||
      (nItem == BASE_ITEM_RAPIER) ||
      (nItem == BASE_ITEM_WHIP) ||
      (nItem == BASE_ITEM_SHORTSPEAR) ||
      (nItem == BASE_ITEM_SHORTSWORD) ||
      (nItem == BASE_ITEM_WARHAMMER)  ||
      (nItem == BASE_ITEM_DWARVENWARAXE))
   {
        return TRUE;
   }
   return FALSE;
}
 
// ----------------------------------------------------------------------------
// Returns TRUE if weapon is a blugeoning weapon
// Uses Get2DAString!
// ----------------------------------------------------------------------------
int IPGetIsBludgeoningWeapon(object oItem)
{
  int nBT = GetBaseItemType(oItem);
  int nWeapon =  ( StringToInt(Get2DAString("baseitems","WeaponType",nBT)));
  // 2 = bludgeoning
  return (nWeapon == 2);
}
 
// ----------------------------------------------------------------------------
// Return the IP_CONST_CASTSPELL_* ID matching to the SPELL_* constant given
// in nSPELL_ID.
// This uses Get2DAstring, so it is slow. Avoid using in loops!
// returns -1 if there is no matching property for a spell
// ----------------------------------------------------------------------------
int IPGetIPConstCastSpellFromSpellID(int nSpellID)
{
    // look up Spell Property Index
    string sTemp = Get2DAString("des_crft_spells","IPRP_SpellIndex",nSpellID);
    /*
    if (sTemp == "") // invalid nSpellID
    {
        PrintString("x2_inc_craft.nss::GetIPConstCastSpellFromSpellID called with invalid nSpellID" + IntToString(nSpellID));
        return -1;
    }
    */
    int nSpellPrpIdx = StringToInt(sTemp);
    return nSpellPrpIdx;
}
// ----------------------------------------------------------------------------
// Returns TRUE if an item has ITEM_PROPERTY_ON_HIT and the specified nSubType
// possible values for nSubType can be taken from IPRP_ONHIT.2da
// popular ones:
//       5 - Daze
//      19 - ItemPoison
//      24 - Vorpal
// ----------------------------------------------------------------------------
int IPGetItemHasItemOnHitPropertySubType(object oTarget, int nSubType)
{
    if (GetItemHasItemProperty(oTarget,ITEM_PROPERTY_ON_HIT_PROPERTIES))
    {
        itemproperty ipTest = GetFirstItemProperty(oTarget);
 
        // loop over item properties to see if there is already a poison effect
        while (GetIsItemPropertyValid(ipTest))
        {
 
            if (GetItemPropertySubType(ipTest) == nSubType)   //19 == onhit poison
            {
                return TRUE;
            }
 
          ipTest = GetNextItemProperty(oTarget);
 
         }
    }
    return FALSE;
}
 
// ----------------------------------------------------------------------------
// Returns the number of possible armor part variations for the specified part
// nPart - ITEM_APPR_ARMOR_MODEL_* constant
// Uses Get2DAstring, so do not use in loops
// ----------------------------------------------------------------------------
int IPGetNumberOfArmorAppearances(int nPart)
{
    int nRet;
    //SpeakString(Get2DAString(X2_IP_ARMORPARTS_2DA ,"NumParts",nPart));
    nRet = StringToInt(Get2DAString(X2_IP_ARMORPARTS_2DA ,"NumParts",nPart));
    return nRet;
}
 
// ----------------------------------------------------------------------------
// (private)
// Returns the previous or next armor appearance type, depending on the specified
// mode (X2_IP_ARMORTYPE_NEXT || X2_IP_ARMORTYPE_PREV)
// ----------------------------------------------------------------------------
int IPGetArmorAppearanceType(object oArmor, int nPart, int nMode)
{
    string sMode;
 
    switch (nMode)
    {
        case        X2_IP_ARMORTYPE_NEXT : sMode ="Next";
                    break;
        case        X2_IP_ARMORTYPE_PREV : sMode ="Prev";
                    break;
    }
 
    int nCurrApp  = GetItemAppearance(oArmor,ITEM_APPR_TYPE_ARMOR_MODEL,nPart);
    int nRet;
 
    if (nPart ==ITEM_APPR_ARMOR_MODEL_TORSO)
    {
        nRet = StringToInt(Get2DAString(X2_IP_ARMORAPPEARANCE_2DA ,sMode,nCurrApp));
        return nRet;
    }
    else
    {
        int nMax =  IPGetNumberOfArmorAppearances(nPart)-1; // index from 0 .. numparts -1
        int nMin =  1; // this prevents part 0 from being chosen (naked)
 
        // return a random valid armor tpze
        if (nMode == X2_IP_ARMORTYPE_RANDOM)
        {
            return Random(nMax)+nMin;
        }
 
        else
        {
            if (nMode == X2_IP_ARMORTYPE_NEXT)
            {
                // current appearance is max, return min
                if (nCurrApp == nMax)
                {
                    return nMin;
                }
                // current appearance is min, return max  -1
                else if (nCurrApp == nMin)
                {
                    nRet = nMin+1;
                    return nRet;
                }
 
                //SpeakString("next");
                // next
                nRet = nCurrApp +1;
                return nRet;
            }
            else                // previous
            {
                // current appearance is max, return nMax-1
                if (nCurrApp == nMax)
                {
                    nRet = nMax--;
                    return nRet;
                }
                // current appearance is min, return max
                else if (nCurrApp == nMin)
                {
                    return nMax;
                }
 
                //SpeakString("prev");
 
                nRet = nCurrApp -1;
                return nRet;
            }
        }
 
     }
 
}
 
// ----------------------------------------------------------------------------
// Returns the next valid appearance type for oArmor
// Uses Get2DAstring, so do not use in loops
// ----------------------------------------------------------------------------
int IPGetNextArmorAppearanceType(object oArmor, int nPart)
{
    return IPGetArmorAppearanceType(oArmor, nPart,  X2_IP_ARMORTYPE_NEXT );
 
}
 
// ----------------------------------------------------------------------------
// Returns the next valid appearance type for oArmor
// Uses Get2DAstring, so do not use in loops
// ----------------------------------------------------------------------------
int IPGetPrevArmorAppearanceType(object oArmor, int nPart)
{
    return IPGetArmorAppearanceType(oArmor, nPart,  X2_IP_ARMORTYPE_PREV );
}
 
// ----------------------------------------------------------------------------
// Returns the next valid appearance type for oArmor
// Uses Get2DAstring, so do not use in loops
// ----------------------------------------------------------------------------
int IPGetRandomArmorAppearanceType(object oArmor, int nPart)
{
    return  IPGetArmorAppearanceType(oArmor, nPart,  X2_IP_ARMORTYPE_RANDOM );
}
 
// ----------------------------------------------------------------------------
// Returns a new armor based of oArmor with nPartModified
// nPart - ITEM_APPR_ARMOR_MODEL_* constant of the part to be changed
// nMode -
//          X2_IP_ARMORTYPE_NEXT    - next valid appearance
//          X2_IP_ARMORTYPE_PREV    - previous valid apperance;
//          X2_IP_ARMORTYPE_RANDOM  - random valid appearance (torso is never changed);
// bDestroyOldOnSuccess - Destroy oArmor in process?
// Uses Get2DAstring, so do not use in loops
// ----------------------------------------------------------------------------
object IPGetModifiedArmor(object oArmor, int nPart, int nMode, int bDestroyOldOnSuccess)
{
    int nNewApp = IPGetArmorAppearanceType(oArmor, nPart,  nMode );
    //SpeakString("old: " + IntToString(GetItemAppearance(oArmor,ITEM_APPR_TYPE_ARMOR_MODEL,nPart)));
    //SpeakString("new: " + IntToString(nNewApp));
 
    object oNew = CopyItemAndModify(oArmor,ITEM_APPR_TYPE_ARMOR_MODEL, nPart, nNewApp,TRUE);
 
    if (oNew != OBJECT_INVALID)
    {
        if( bDestroyOldOnSuccess )
        {
            DestroyObject(oArmor);
        }
        return oNew;
    }
    // Safety fallback, return old armor on failures
       return oArmor;
}
 
// ----------------------------------------------------------------------------
// Creates a special ring on oCreature that gives
// all weapon and armor proficiencies to the wearer
// Item is set non dropable
// ----------------------------------------------------------------------------
object IPCreateProficiencyFeatItemOnCreature(object oCreature)
{
    // create a simple golden ring
    object  oRing = DbgCreateItemOnObject("nw_it_mring023",oCreature);
 
    // just in case
    SetDroppableFlag(oRing, FALSE);
 
    itemproperty ip = ItemPropertyBonusFeat(IP_CONST_FEAT_ARMOR_PROF_HEAVY);
    AddItemProperty(DURATION_TYPE_PERMANENT,ip,oRing);
    ip = ItemPropertyBonusFeat(IP_CONST_FEAT_ARMOR_PROF_MEDIUM);
    AddItemProperty(DURATION_TYPE_PERMANENT,ip,oRing);
    ip = ItemPropertyBonusFeat(IP_CONST_FEAT_ARMOR_PROF_LIGHT);
    AddItemProperty(DURATION_TYPE_PERMANENT,ip,oRing);
    ip = ItemPropertyBonusFeat(IP_CONST_FEAT_WEAPON_PROF_EXOTIC);
    AddItemProperty(DURATION_TYPE_PERMANENT,ip,oRing);
    ip = ItemPropertyBonusFeat(IP_CONST_FEAT_WEAPON_PROF_MARTIAL);
    AddItemProperty(DURATION_TYPE_PERMANENT,ip,oRing);
    ip = ItemPropertyBonusFeat(IP_CONST_FEAT_WEAPON_PROF_SIMPLE);
    AddItemProperty(DURATION_TYPE_PERMANENT,ip,oRing);
 
    return oRing;
}
 
// ----------------------------------------------------------------------------
// Add an item property in a safe fashion, preventing unwanted stacking
// Parameters:
//   oItem     - the item to add the property to
//   ip        - the itemproperty to add
//   fDuration - set 0.0f to add the property permanent, anything else is temporary
//   nAddItemPropertyPolicy - How to handle existing properties. Valid values are:
//     X2_IP_ADDPROP_POLICY_REPLACE_EXISTING - remove any property of the same type, subtype, durationtype before adding;
//     X2_IP_ADDPROP_POLICY_KEEP_EXISTING - do not add if any property with same type, subtype and durationtype already exists;
//     X2_IP_ADDPROP_POLICY_IGNORE_EXISTING - add itemproperty in any case - Do not Use with OnHit or OnHitSpellCast props!
//   bIgnoreDurationType  - If set to TRUE, an item property will be considered identical even if the DurationType is different. Be careful when using this
//                          with X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, as this could lead to a temporary item property removing a permanent one
//   bIgnoreSubType       - If set to TRUE an item property will be considered identical even if the SubType is different.
//
// * WARNING: This function is used all over the game. Touch it and break it and the wrath
//            of the gods will come down on you faster than you can saz "I didn't do it"
// ----------------------------------------------------------------------------
void IPSafeAddItemProperty(object oItem, itemproperty ip, float fDuration =0.0f, int nAddItemPropertyPolicy = X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, int bIgnoreDurationType = FALSE, int bIgnoreSubType = FALSE)
{
    int nType = GetItemPropertyType(ip);
    int nSubType = GetItemPropertySubType(ip);
    int nDuration;
    // if duration is 0.0f, make the item property permanent
    if (fDuration == 0.0f)
    {
 
        nDuration = DURATION_TYPE_PERMANENT;
    } else
    {
 
        nDuration = DURATION_TYPE_TEMPORARY;
    }
 
    int nDurationCompare = nDuration;
    if (bIgnoreDurationType)
    {
        nDurationCompare = -1;
    }
 
    if (nAddItemPropertyPolicy == X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)
    {
 
        // remove any matching properties
        if (bIgnoreSubType)
        {
            nSubType = -1;
        }
        IPRemoveMatchingItemProperties(oItem, nType, nDurationCompare, nSubType );
    }
    else if (nAddItemPropertyPolicy == X2_IP_ADDPROP_POLICY_KEEP_EXISTING )
    {
         // do not replace existing properties
        if(IPGetItemHasProperty(oItem, ip, nDurationCompare, bIgnoreSubType))
        {
          return; // item already has property, return
        }
    }
    else //X2_IP_ADDPROP_POLICY_IGNORE_EXISTING
    {
 
    }
 
    if (nDuration == DURATION_TYPE_PERMANENT)
    {
        AddItemProperty(nDuration,ip, oItem);
    }
    else
    {
        AddItemProperty(nDuration,ip, oItem,fDuration);
    }
}
 
// ----------------------------------------------------------------------------
 
// ----------------------------------------------------------------------------
int IPGetItemHasProperty(object oItem, itemproperty ipCompareTo, int nDurationCompare, int bIgnoreSubType = FALSE)
{
    itemproperty ip = GetFirstItemProperty(oItem);
 
    //PrintString ("Filter - T:" + IntToString(GetItemPropertyType(ipCompareTo))+ " S: " + IntToString(GetItemPropertySubType(ipCompareTo)) + " (Ignore: " + IntToString (bIgnoreSubType) + ") D:" + IntToString(nDurationCompare));
    while (GetIsItemPropertyValid(ip))
    {
        // PrintString ("Testing - T: " + IntToString(GetItemPropertyType(ip)));
        if ((GetItemPropertyType(ip) == GetItemPropertyType(ipCompareTo)))
        {
           &nb

Modifié par Lazarus Magni, 06 décembre 2011 - 02:43 .


#20
WhiZard

WhiZard
  • Members
  • 1 204 messages
Here is a basic start to getting damage increase effects if the calculation behind them is already known. (e.g. if someone has divine might, then the bonus from divine might is known to be the charisma modifier). Sometimes I am giving benefit of doubt (like higher cleric/paladin level for getting divine favor bonus).


struct damage_struct
{
int nMagical, nDivine, nPiercing, nSlashing, nBludgeoning, nNegative, nPositive;
int nFire, nCold, nAcid, nElectrical, nSonic;
};


//Gets various values for damage increase effects
struct damage_struct BonusDamage(object oAttacker = OBJECT_SELF);
//Gets the larger of two numbers
int GetHigher(int n1, int n2);

int GetHigher(int n1, int n2)
{
if(n1 > n2)
return n1;
return n2;
}

struct damage_struct BonusDamage(object oAttacker = OBJECT_SELF)
{
struct damage_struct stBonus;
//For divine might use current charisma modifier.
if(GetHasFeatEffect(FEAT_DIVINE_MIGHT))
stBonus.nDivine += GetAbilityModifier(ABILITY_CHARISMA, oAttacker);
//Divine Favor can only be cast on self, thus we can check, paladin/cleric class and give benefit of doubt, UMDers are stuck with default scroll caster level
if(GetHasSpellEffect(SPELL_DIVINE_FAVOR))
stBonus.nMagical += GetHigher(GetHigher(GetLevelByclass(class_TYPE_CLERIC, oAttacker), GetLevelByclass(class_TYPE_PALADIN, oAttacker)), 5)/3;
/*Here insert more spells/feats*/
return stBonus;
}

//This shows how to access struct values
void main()
{
struct damage_struct stDamage = BonusDamage();
int nMagicalDamage = stDamage.nMagical;
int nDivineDamage = stDamage.nDivine;
int nPiercingDamage = stDamage.nPiercing;
}


EDIT:Note that the above will note compile because this social forum always lowercases the word class.

Modifié par WhiZard, 06 décembre 2011 - 07:25 .


#21
Shadooow

Shadooow
  • Members
  • 4 470 messages
BTW physical damage doesn't have to be categorized since the game engine changes its type to the weapon damage type right?

Also there is warcry, prayer, bard song, pdk feats and war domain... practically impossible to get this done correctly without NWNX. With NWNX it should be easy as NWNX has GetEffectInteger function.

But anyway, had the divine favor and might applied for all arrows with also all arrow property seems very inbalanced to me. And keep in mind that I am admin of epic loot/action module. On my server, imbue arrow is still very powerfull, death arrow is useable even with DC 20, hail can be used to exploit spawn systems, only seeker is somewhat useless.

#22
WhiZard

WhiZard
  • Members
  • 1 204 messages

ShaDoOoW wrote...

BTW physical damage doesn't have to be categorized since the game engine changes its type to the weapon damage type right?

It does in this case because the damage is not being applied through a weapon, but rather by EffectDamage().  However for DR bypassing, it is a good idea to clump them all together (under one physical type, though I haven't tested combo types like bludgeoning/piercing).  Still, if I am defining a damage struct to potentially be used with other struct functions, I might as well declare all the basics.

Also there is warcry, prayer, bard song, pdk feats and war domain... practically impossible to get this done correctly without NWNX. With NWNX it should be easy as NWNX has GetEffectInteger function.

You can account for the vast majority of the above accurately, if you are looking specifically for them, as I performed above.  Re-calculating the amount may cause assumptions to be made (as with bard song and war domain).

But anyway, had the divine favor and might applied for all arrows with also all arrow property seems very inbalanced to me. And keep in mind that I am admin of epic loot/action module. On my server, imbue arrow is still very powerfull, death arrow is useable even with DC 20, hail can be used to exploit spawn systems, only seeker is somewhat useless.


Probably imbalanced, but at least the scripted calculation doesn't bypass immunities and damage resistance like the normal arrows would with damage increase functions.

#23
Shadooow

Shadooow
  • Members
  • 4 470 messages
Lazarus: you aksed for ideas, well my idea is to abadon this solution and rather choose a balance change that would work for you, its easier to implement and also easier to balance. For example on Antiworld, physical damage is added to magical which works for them very well and makes from AAs mage killers.

BTW I tried to implement something similar in past for fake attacks with any weapon, but I never implemented it and because of new NWNX features in past year I also abadoned this solution.

However maybe my old code could be some of use to you, so there is it:

struct offensive_info
{
int Bypass;
int Base, Bludgeoning, Divine, Magical, Positive, Negative;
int Piercing, Slashing, Acid, Cold, Fire, Sonic, Electrical;
int AB;
int CriticalThreat;
int CriticalMultiplier;
int Overwhelming;
int Massive;
int Sneak;
};

//inicialize damage, ab etc. for
//nHand - 1 onhand, 2 offhand
//oWeapon - if invalid, considering as unarmed - not work yet
struct offensive_info InitializeCombatInfo(object oAttacker, object oWeapon, int nHand, object oVersus);

struct offensive_info InitializeCombatInfo(object oAttacker, object oWeapon, int nHand, object oVersus)
{
int nBaseItemType = GetBaseItemType(oWeapon);
 if(oWeapon == OBJECT_INVALID)
 {
 oWeapon = GetItemInSlot(INVENTORY_SLOT_ARMS,oAttacker);
 nBaseItemType = BASE_ITEM_GLOVES;
 }
//sanity check?
struct offensive_info c;
c.Sneak = GetLocalInt(oAttacker,"sneak");
int nBase = StringToInt(Get2DAString("baseitems","NumDice",nBaseItemType));
int nBaseDice = StringToInt(Get2DAString("baseitems","DieToRoll",nBaseItemType));
DebugString("BaseDamage",IntToString(nBase)+"d"+IntToString(nBaseDice));
object oLeftHand = GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oAttacker);
int nEnhancement,nAttack,nMighty,bKeen;
int nFirst,nSecond;
int nEnchantArrow = (GetLevelByclass(class_TYPE_ARCANE_ARCHER,oAttacker)+1)/2;
 if(nBaseItemType != BASE_ITEM_LONGBOW && nBaseItemType != BASE_ITEM_SHORTBOW)
 {
 nEnchantArrow = 0;
 }
itemproperty ip = GetFirstItemProperty(oWeapon);
 while(GetIsItemPropertyValid(ip))
 {
  switch(GetItemPropertyType(ip))
  {
  case ITEM_PROPERTY_KEEN:
  bKeen = TRUE;
  break;
  case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_ALIGNMENT_GROUP:
   if(GetAlignmentGoodEvil(oVersus) == GetItemPropertySubType(ip) || GetAlignmentLawChaos(oVersus) == GetItemPropertySubType(ip))
   {
   nEnhancement = GetItemPropertyCostTableValue(ip) > nEnhancement ? GetItemPropertyCostTableValue(ip) : nEnhancement;
   }
  break;
  case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_SPECIFIC_ALIGNEMENT:
  nSecond = 3;
   switch(GetItemPropertySubType(ip))
   {
   case IP_CONST_ALIGNMENT_NE:
   nSecond--;
   case IP_CONST_ALIGNMENT_LE:
   nSecond--;
   case IP_CONST_ALIGNMENT_CE:
   nFirst = 5;
   break;
   case IP_CONST_ALIGNMENT_NG:
   nSecond--;
   case IP_CONST_ALIGNMENT_LG:
   nSecond--;
   case IP_CONST_ALIGNMENT_CG:
   nFirst = 4;
   break;
   case IP_CONST_ALIGNMENT_TN:
   nSecond--;
   case IP_CONST_ALIGNMENT_LN:
   nSecond--;
   case IP_CONST_ALIGNMENT_CN:
   nFirst = nSecond;
   break;
   }
   if(GetAlignmentGoodEvil(oVersus) == nFirst && GetAlignmentLawChaos(oVersus) == nSecond)
   {
   nEnhancement = GetItemPropertyCostTableValue(ip) > nEnhancement ? GetItemPropertyCostTableValue(ip) : nEnhancement;
   }
  break;
  case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_RACIAL_GROUP:
   if(GetRacialType(oVersus) == GetItemPropertySubType(ip))
   {
   nEnhancement = GetItemPropertyCostTableValue(ip) > nEnhancement ? GetItemPropertyCostTableValue(ip) : nEnhancement;
   }
  break;
  case ITEM_PROPERTY_ENHANCEMENT_BONUS:
  nEnhancement = GetItemPropertyCostTableValue(ip) > nEnhancement ? GetItemPropertyCostTableValue(ip) : nEnhancement;
  break;
  case ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP:
   if(GetAlignmentGoodEvil(oVersus) == GetItemPropertySubType(ip) || GetAlignmentLawChaos(oVersus) == GetItemPropertySubType(ip))
   {
   nAttack = GetItemPropertyCostTableValue(ip) > nAttack ? GetItemPropertyCostTableValue(ip) : nAttack;
   }
  break;
  case ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNMENT:
  nSecond = 3;
   switch(GetItemPropertySubType(ip))
   {
   case IP_CONST_ALIGNMENT_NE:
   nSecond--;
   case IP_CONST_ALIGNMENT_LE:
   nSecond--;
   case IP_CONST_ALIGNMENT_CE:
   nFirst = 5;
   break;
   case IP_CONST_ALIGNMENT_NG:
   nSecond--;
   case IP_CONST_ALIGNMENT_LG:
   nSecond--;
   case IP_CONST_ALIGNMENT_CG:
   nFirst = 4;
   break;
   case IP_CONST_ALIGNMENT_TN:
   nSecond--;
   case IP_CONST_ALIGNMENT_LN:
   nSecond--;
   case IP_CONST_ALIGNMENT_CN:
   nFirst = nSecond;
   break;
   }
   if(GetAlignmentGoodEvil(oVersus) == nFirst && GetAlignmentLawChaos(oVersus) == nSecond)
   {
   nAttack = GetItemPropertyCostTableValue(ip) > nAttack ? GetItemPropertyCostTableValue(ip) : nAttack;
   }
  break;
  case ITEM_PROPERTY_ATTACK_BONUS_VS_RACIAL_GROUP:
   if(GetRacialType(oVersus) == GetItemPropertySubType(ip))
   {
   nAttack = GetItemPropertyCostTableValue(ip) > nAttack ? GetItemPropertyCostTableValue(ip) : nAttack;
   }
  break;
  case ITEM_PROPERTY_ATTACK_BONUS:
  nAttack = GetItemPropertyCostTableValue(ip) > nAttack ? GetItemPropertyCostTableValue(ip) : nAttack;
  break;
  case ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP:
   if(GetAlignmentGoodEvil(oVersus) == GetItemPropertySubType(ip) || GetAlignmentLawChaos(oVersus) == GetItemPropertySubType(ip))
   {
   nFirst = GetItemPropertyCostTableValue(ip);
    switch(GetItemPropertyParam1Value(ip))
    {
    case IP_CONST_DAMAGETYPE_BLUDGEONING:
    c.Bludgeoning = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Bludgeoning)) ? nFirst : c.Bludgeoning;
    break;
    case IP_CONST_DAMAGETYPE_PIERCING:
    c.Piercing = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Piercing)) ? nFirst : c.Piercing;
    break;
    case IP_CONST_DAMAGETYPE_SLASHING:
    c.Slashing = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Slashing)) ? nFirst : c.Slashing;
    break;
    case IP_CONST_DAMAGETYPE_MAGICAL:
    c.Magical = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Magical)) ? nFirst : c.Magical;
    break;
    case IP_CONST_DAMAGETYPE_ACID:
    c.Acid = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Acid)) ? nFirst : c.Acid;
    break;
    case IP_CONST_DAMAGETYPE_COLD:
    c.Cold = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Cold)) ? nFirst : c.Cold;
    break;
    case IP_CONST_DAMAGETYPE_DIVINE:
    c.Divine = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Divine)) ? nFirst : c.Divine;
    break;
    case IP_CONST_DAMAGETYPE_ELECTRICAL:
    c.Electrical = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Electrical)) ? nFirst : c.Electrical;
    break;
    case IP_CONST_DAMAGETYPE_FIRE:
    c.Fire = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Fire)) ? nFirst : c.Fire;
    break;
    case IP_CONST_DAMAGETYPE_NEGATIVE:
    c.Negative = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Negative)) ? nFirst : c.Negative;
    break;
    case IP_CONST_DAMAGETYPE_POSITIVE:
    c.Positive = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Positive)) ? nFirst : c.Positive;
    break;
    case IP_CONST_DAMAGETYPE_SONIC:
    c.Sonic = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Sonic)) ? nFirst : c.Sonic;
    break;
    }
   }
  break;
  case ITEM_PROPERTY_DAMAGE_BONUS_VS_SPECIFIC_ALIGNMENT:
  nSecond = 3;
   switch(GetItemPropertySubType(ip))
   {
   case IP_CONST_ALIGNMENT_NE:
   nSecond--;
   case IP_CONST_ALIGNMENT_LE:
   nSecond--;
   case IP_CONST_ALIGNMENT_CE:
   nFirst = 5;
   break;
   case IP_CONST_ALIGNMENT_NG:
   nSecond--;
   case IP_CONST_ALIGNMENT_LG:
   nSecond--;
   case IP_CONST_ALIGNMENT_CG:
   nFirst = 4;
   break;
   case IP_CONST_ALIGNMENT_TN:
   nSecond--;
   case IP_CONST_ALIGNMENT_LN:
   nSecond--;
   case IP_CONST_ALIGNMENT_CN:
   nFirst = nSecond;
   break;
   }
   if(GetAlignmentGoodEvil(oVersus) == nFirst && GetAlignmentLawChaos(oVersus) == nSecond)
   {
   nFirst = GetItemPropertyCostTableValue(ip);
    switch(GetItemPropertyParam1Value(ip))
    {
    case IP_CONST_DAMAGETYPE_BLUDGEONING:
    c.Bludgeoning = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Bludgeoning)) ? nFirst : c.Bludgeoning;
    break;
    case IP_CONST_DAMAGETYPE_PIERCING:
    c.Piercing = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Piercing)) ? nFirst : c.Piercing;
    break;
    case IP_CONST_DAMAGETYPE_SLASHING:
    c.Slashing = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Slashing)) ? nFirst : c.Slashing;
    break;
    case IP_CONST_DAMAGETYPE_MAGICAL:
    c.Magical = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Magical)) ? nFirst : c.Magical;
    break;
    case IP_CONST_DAMAGETYPE_ACID:
    c.Acid = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Acid)) ? nFirst : c.Acid;
    break;
    case IP_CONST_DAMAGETYPE_COLD:
    c.Cold = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Cold)) ? nFirst : c.Cold;
    break;
    case IP_CONST_DAMAGETYPE_DIVINE:
    c.Divine = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Divine)) ? nFirst : c.Divine;
    break;
    case IP_CONST_DAMAGETYPE_ELECTRICAL:
    c.Electrical = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Electrical)) ? nFirst : c.Electrical;
    break;
    case IP_CONST_DAMAGETYPE_FIRE:
    c.Fire = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Fire)) ? nFirst : c.Fire;
    break;
    case IP_CONST_DAMAGETYPE_NEGATIVE:
    c.Negative = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Negative)) ? nFirst : c.Negative;
    break;
    case IP_CONST_DAMAGETYPE_POSITIVE:
    c.Positive = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Positive)) ? nFirst : c.Positive;
    break;
    case IP_CONST_DAMAGETYPE_SONIC:
    c.Sonic = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Sonic)) ? nFirst : c.Sonic;
    break;
    }
   }
  break;
  case ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP:
   if(GetRacialType(oVersus) == GetItemPropertySubType(ip))
   {
   nFirst = GetItemPropertyCostTableValue(ip);
    switch(GetItemPropertyParam1Value(ip))
    {
    case IP_CONST_DAMAGETYPE_BLUDGEONING:
    c.Bludgeoning = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Bludgeoning)) ? nFirst : c.Bludgeoning;
    break;
    case IP_CONST_DAMAGETYPE_PIERCING:
    c.Piercing = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Piercing)) ? nFirst : c.Piercing;
    break;
    case IP_CONST_DAMAGETYPE_SLASHING:
    c.Slashing = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Slashing)) ? nFirst : c.Slashing;
    break;
    case IP_CONST_DAMAGETYPE_MAGICAL:
    c.Magical = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Magical)) ? nFirst : c.Magical;
    break;
    case IP_CONST_DAMAGETYPE_ACID:
    c.Acid = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Acid)) ? nFirst : c.Acid;
    break;
    case IP_CONST_DAMAGETYPE_COLD:
    c.Cold = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Cold)) ? nFirst : c.Cold;
    break;
    case IP_CONST_DAMAGETYPE_DIVINE:
    c.Divine = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Divine)) ? nFirst : c.Divine;
    break;
    case IP_CONST_DAMAGETYPE_ELECTRICAL:
    c.Electrical = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Electrical)) ? nFirst : c.Electrical;
    break;
    case IP_CONST_DAMAGETYPE_FIRE:
    c.Fire = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Fire)) ? nFirst : c.Fire;
    break;
    case IP_CONST_DAMAGETYPE_NEGATIVE:
    c.Negative = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Negative)) ? nFirst : c.Negative;
    break;
    case IP_CONST_DAMAGETYPE_POSITIVE:
    c.Positive = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Positive)) ? nFirst : c.Positive;
    break;
    case IP_CONST_DAMAGETYPE_SONIC:
    c.Sonic = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Sonic)) ? nFirst : c.Sonic;
    break;
    }
   }
  break;
  case ITEM_PROPERTY_DAMAGE_BONUS:
  nFirst = GetItemPropertyCostTableValue(ip);
   switch(GetItemPropertySubType(ip))
   {
   case IP_CONST_DAMAGETYPE_BLUDGEONING:
   c.Bludgeoning = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Bludgeoning)) ? nFirst : c.Bludgeoning;
   break;
   case IP_CONST_DAMAGETYPE_PIERCING:
   c.Piercing = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Piercing)) ? nFirst : c.Piercing;
   break;
   case IP_CONST_DAMAGETYPE_SLASHING:
   c.Slashing = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Slashing)) ? nFirst : c.Slashing;
   break;
   case IP_CONST_DAMAGETYPE_MAGICAL:
   c.Magical = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Magical)) ? nFirst : c.Magical;
   break;
   case IP_CONST_DAMAGETYPE_ACID:
   c.Acid = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Acid)) ? nFirst : c.Acid;
   break;
   case IP_CONST_DAMAGETYPE_COLD:
   c.Cold = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Cold)) ? nFirst : c.Cold;
   break;
   case IP_CONST_DAMAGETYPE_DIVINE:
   c.Divine = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Divine)) ? nFirst : c.Divine;
   break;
   case IP_CONST_DAMAGETYPE_ELECTRICAL:
   c.Electrical = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Electrical)) ? nFirst : c.Electrical;
   break;
   case IP_CONST_DAMAGETYPE_FIRE:
   c.Fire = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Fire)) ? nFirst : c.Fire;
   break;
   case IP_CONST_DAMAGETYPE_NEGATIVE:
   c.Negative = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Negative)) ? nFirst : c.Negative;
   break;
   case IP_CONST_DAMAGETYPE_POSITIVE:
   c.Positive = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Positive)) ? nFirst : c.Positive;
   break;
   case IP_CONST_DAMAGETYPE_SONIC:
   c.Sonic = StringToInt(Get2DAString("iprp_damagecost","Rank",nFirst)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Sonic)) ? nFirst : c.Sonic;
   break;
   }
  break;
  case ITEM_PROPERTY_MASSIVE_CRITICALS://not stack with itself
  c.Massive = GetItemPropertyCostTableValue(ip) > c.Massive ? GetItemPropertyCostTableValue(ip) : c.Massive;
  break;
  case ITEM_PROPERTY_MIGHTY:
  nMighty = GetItemPropertyCostTableValue(ip) > nMighty ? GetItemPropertyCostTableValue(ip) : nMighty;
  break;
  //TODO  case ITEM_PROPERTY_NO_DAMAGE: -> overwhelming not count
  //TODO  case ITEM_PROPERTY_EXTRA_MELEE_DAMAGE_TYPE:
  //TODO  case ITEM_PROPERTY_EXTRA_RANGED_DAMAGE_TYPE:
  }
 ip = GetNextItemProperty(oWeapon);
 }
c.Bypass = nAttack > nEnhancement ? nAttack : nEnhancement;
 if(c.Bypass < nEnchantArrow)
 {
 c.Bypass = nEnchantArrow;
 }
struct damage_type d = WeaponDamageType(oWeapon);
 if(nEnhancement > 0)
 {
 nEnhancement = DMGBonusToIP(nEnhancement);
  if(d.type2 < 0)
  {
   switch(d.type1)
   {
   case IP_CONST_DAMAGETYPE_BLUDGEONING:
   c.Bludgeoning = StringToInt(Get2DAString("iprp_damagecost","Rank",nEnhancement)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Bludgeoning)) ? nEnhancement : c.Bludgeoning;
   break;
   case IP_CONST_DAMAGETYPE_PIERCING:
   c.Piercing = StringToInt(Get2DAString("iprp_damagecost","Rank",nEnhancement)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Piercing)) ? nEnhancement : c.Piercing;
   break;
   case IP_CONST_DAMAGETYPE_SLASHING:
   c.Slashing = StringToInt(Get2DAString("iprp_damagecost","Rank",nEnhancement)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Slashing)) ? nEnhancement : c.Slashing;
   break;
   }
  }
  else
  {//if weapon has two damage types second one is used for enhancement purposes
   switch(d.type2)
   {
   case IP_CONST_DAMAGETYPE_BLUDGEONING:
   c.Bludgeoning = StringToInt(Get2DAString("iprp_damagecost","Rank",nEnhancement)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Bludgeoning)) ? nEnhancement : c.Bludgeoning;
   break;
   case IP_CONST_DAMAGETYPE_PIERCING:
   c.Piercing = StringToInt(Get2DAString("iprp_damagecost","Rank",nEnhancement)) > StringToInt(Get2DAString("iprp_damagecost","Rank",c.Piercing)) ? nEnhancement : c.Piercing;
   break;
   }
  }
 }
int bWeaponChoice = GetHasFeat(StringToInt(Get2DAString("weaponfeats","WC",nBaseItemType)),oAttacker);
int nStrMod = GetAbilityModifier(ABILITY_STRENGTH,oAttacker);
int nWisMod = GetAbilityModifier(ABILITY_WISDOM,oAttacker);
int nDexMod = GetAbilityModifier(ABILITY_DEXTERITY,oAttacker);
int bRanged = GetWeaponRanged(oWeapon);
float fDistance;
c.AB = GetBaseAttackBonus(oAttacker)+nEnchantArrow;
 if(GetHasFeat(FEAT_WEAPON_FINESSE,oAttacker) && GetIsFinessableWeapon(nBaseItemType))
 {
 c.AB+= nDexMod > nStrMod ? nDexMod : nStrMod;
 }
 else if(bRanged)
 {
  switch(GetCurrentAction(oVersus))
  {//-2 ab in ranged if target is moving
  case ACTION_MOVETOPOINT:
  case ACTION_FOLLOW:
  case ACTION_RANDOMWALK:
  c.AB-= 2;
  break;
  }
  if(GetHasFeat(FEAT_POINT_BLANK_SHOT,oAttacker))
  {//Point Blank Shot -> +1ab if target is in 15 feet distance
  fDistance = GetDistanceBetween(oAttacker,oVersus);
   if(fDistance <= FeetToMeters(15.0) && fDistance > 0.0)
   {
   c.AB++;
   }
  }
  else
  {
  object oEnemy = GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,oAttacker);
   if(GetIsObjectValid(oEnemy) && GetDistanceBetween(oAttacker,oVersus) <= 3.5)
   {//shooting in meelee -> -4 ab penalty
   c.AB-= 4;
   }
  }
  if(GetHasFeat(FEAT_ZEN_ARCHERY,oAttacker))
  {
  c.AB+= nWisMod > nDexMod ? nWisMod : nDexMod;
  }
  else
  {
  c.AB+= nDexMod;
  }
 }
 else
 {
 c.AB+= nStrMod;
 }
c.AB += GetHasFeat(FEAT_EPIC_PROWESS,oAttacker);
c.AB += GetHasFeat(StringToInt(Get2DAString("weaponfeats","WF",nBaseItemType)),oAttacker);
c.AB += GetHasFeat(StringToInt(Get2DAString("weaponfeats","EWF",nBaseItemType)),oAttacker) ? 2 : 0;
 if(bWeaponChoice)
 {
 int nWM = GetLevelByclass(class_TYPE_WEAPON_MASTER,oAttacker);
  if(nWM > 4) //WM na lvl 5 +1ab
  {
  c.AB++;
  nWM = (nWM-10)/3;
   if(nWM > 0)//kazde 3 lvl nad 10 +1ab
   {
   c.AB+= nWM;
   }
  }
 }
int bDouble = GetIsDoubleWeapon(oWeapon); //IPGetIsMeleeWeapon(oLeftHand)
int bLight = GetIsLightWeapon(oWeapon) || bDouble;
 if(GetIsObjectValid(oLeftHand) && (oLeftHand == oWeapon || bDouble || Get2DAString("weaponfeats","EWF",GetBaseItemType(oLeftHand)) != "-"))
 {//dual wield
 c.AB-= 10;
  if(GetHasFeat(374,oAttacker) && GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_CHEST,oAttacker)) ? NWNXFuncs_GetBaseAC(GetItemInSlot(INVENTORY_SLOT_CHEST,oAttacker)) : 0 < 4)
  {//ranger dual wield feat
  c.AB+= 6;
  }
  else
  {
   if(GetHasFeat(FEAT_AMBIDEXTERITY,oAttacker))
   {
   c.AB+= 4;
   }
   if(GetHasFeat(FEAT_TWO_WEAPON_FIGHTING,oAttacker))
   {
   c.AB+= 2;
   }
  }
  if(bLight)
  {//its light - TODO support for oversized two weapon fighting
  c.AB+= 2;
  }
 }
 if(GetActionMode(oAttacker,ACTION_MODE_FLURRY_OF_BLOWS) || GetActionMode(oAttacker,ACTION_MODE_RAPID_SHOT))
 {
 c.AB-= 2;
 }
 else if(GetActionMode(oAttacker,ACTION_MODE_EXPERTISE))
 {
 c.AB-= 5;
 }
 else if(GetActionMode(oAttacker,ACTION_MODE_IMPROVED_EXPERTISE))
 {
 c.AB-= 10;
 }
////////////////////////////////////////////////////////////////////////////////
 if(oLeftHand != oWeapon)//special bonuses from my old-new prestige classes
 {                       //dispater, hextor, power attack/shot
 c.AB+= GetLocalInt(oAttacker,"rAB");
 }
 else
 {
 c.AB+= GetLocalInt(oAttacker,"lAB");
 }
////////////////////////////////////////////////////////////////////////////////
c.CriticalMultiplier = StringToInt(Get2DAString("baseitems","CritHitMult",nBaseItemType));
 if(bWeaponChoice && GetHasFeat(FEAT_INCREASE_MULTIPLIER,oAttacker))
 {
 c.CriticalMultiplier++;
 }
int nCritThreat = StringToInt(Get2DAString("baseitems","CritThreat",nBaseItemType));
nCritThreat*= 1+bKeen+GetHasFeat(StringToInt(Get2DAString("weaponfeats","IC",nBaseItemType)),oAttacker);
 if(bWeaponChoice && GetHasFeat(FEAT_KI_CRITICAL,oAttacker))
 {
 nCritThreat+= 2;
 }
c.CriticalThreat = 21-nCritThreat;
 if(GetHasFeat(StringToInt(Get2DAString("weaponfeats","OC",nBaseItemType)),oAttacker))
 {//overwhelming critical feat handling
 c.Overwhelming = c.CriticalMultiplier;//ingame description wrong its 1d6more
 }
nSecond = StringToInt(Get2DAString("baseitems","DieToRoll",nBaseItemType));
 for(nFirst = StringToInt(Get2DAString("baseitems","NumDice",nBaseItemType));nFirst > 0;nFirst--)
 {//base weapon damage
 c.Base+= Random(nSecond)+1;
 }
DebugString("BaseDamage",IntToString(c.Base));
c.Base+= nEnchantArrow;
 if(!bRanged || nBaseItemType == BASE_ITEM_THROWINGAXE || nBaseItemType == BASE_ITEM_DART)
 {//light weapon gets only 50% damage from str, two handed weapons get 1.5x damage from str
 nFirst = (nStrMod/2)*2 != nStrMod;//this makes rounding upward
 c.Base = bLight && nHand == 2 ? nStrMod/2+nFirst : GetIsTwoHandedWeapon(oWeapon) ? (nStrMod*15)/10 : nStrMod;
 }
 else
 {
  switch(nBaseItemType)
  {
  case BASE_ITEM_HEAVYCROSSBOW:
  case BASE_ITEM_LIGHTCROSSBOW:
  case BASE_ITEM_SHURIKEN:
   if(!nMighty) //these three weapons do not have penalty for negative strenght
   {
   nMighty = -1;
   }
  }
  if(nMighty > -1)
  {
  c.Base+= (nStrMod > nMighty ? nMighty : nStrMod);
  }
 }
//specialization handling
c.Base+= GetHasFeat(StringToInt(Get2DAString("weaponfeats","WS",nBaseItemType)),oAttacker) ? 2 : 0;
c.Base+= GetHasFeat(StringToInt(Get2DAString("weaponfeats","EWS",nBaseItemType)),oAttacker) ? 4 : 0;

 for(nFirst=261;nFirst < 282;nFirst++)
 {//ranger bonuses handling
  if(nFirst-261 == GetRacialType(oVersus))
  {
   if(GetHasFeat(nFirst,oAttacker))
   {
   c.Base+= 1+GetLevelByclass(class_TYPE_RANGER,oAttacker)/5;
   }
   if(GetHasFeat(FEAT_EPIC_BANE_OF_ENEMIES,oAttacker))
   {
   c.Base+= d6(2);
   c.AB+= 2;
   }
  break;
  }
 }
DebugInt("Bypass",c.Bypass);
DebugString("Magical",Get2DAString("iprp_damagecost","Label",c.Magical));
DebugString("Fire",Get2DAString("iprp_damagecost","Label",c.Fire));
DebugString("Acid",Get2DAString("iprp_damagecost","Label",c.Acid));
DebugString("Cold",Get2DAString("iprp_damagecost","Label",c.Cold));
DebugString("Sonic",Get2DAString("iprp_damagecost","Label",c.Sonic));
DebugString("Electrical",Get2DAString("iprp_damagecost","Label",c.Electrical));
DebugString("Slashing",Get2DAString("iprp_damagecost","Label",c.Slashing));
DebugString("Bludgeoning",Get2DAString("iprp_damagecost","Label",c.Bludgeoning));
DebugString("Piercing",Get2DAString("iprp_damagecost","Label",c.Piercing));
DebugInt("AB (only base)",c.AB);
DebugString("Massive",Get2DAString("iprp_damagecost","Label",c.Massive));
DebugString("Overwhelming",IntToString(c.Overwhelming)+"d6");
DebugInt("CriticalThreat",c.CriticalThreat);
DebugInt("CriticalMultiplier",c.CriticalMultiplier);
DebugString("Sneak d6",IntToString(c.Sneak)+"d6");
c.Piercing = IPToDMGBonus(c.Piercing);
c.Slashing = IPToDMGBonus(c.Slashing);
c.Bludgeoning = IPToDMGBonus(c.Bludgeoning);
c.Magical = IPToDMGBonus(c.Magical);
c.Acid = IPToDMGBonus(c.Acid);
c.Cold = IPToDMGBonus(c.Cold);
c.Electrical = IPToDMGBonus(c.Electrical);
c.Sonic = IPToDMGBonus(c.Sonic);
c.Divine = IPToDMGBonus(c.Divine);
c.Positive = IPToDMGBonus(c.Positive);
c.Negative = IPToDMGBonus(c.Negative);
c.Massive = IPToDMGBonus(c.Massive);
 if(c.Overwhelming)
 {
 c.Overwhelming = d6(c.Overwhelming);
 }
DebugInt("Bypass",c.Bypass);
DebugString("BaseDamage",IntToString(c.Base));
DebugString("Magical",Get2DAString("iprp_damagecost","Label",c.Magical));
DebugString("Fire",Get2DAString("iprp_damagecost","Label",c.Fire));
DebugString("Acid",Get2DAString("iprp_damagecost","Label",c.Acid));
DebugString("Cold",Get2DAString("iprp_damagecost","Label",c.Cold));
DebugString("Sonic",Get2DAString("iprp_damagecost","Label",c.Sonic));
DebugString("Electrical",Get2DAString("iprp_damagecost","Label",c.Electrical));
DebugString("Slashing",Get2DAString("iprp_damagecost","Label",c.Slashing));
DebugString("Bludgeoning",Get2DAString("iprp_damagecost","Label",c.Bludgeoning));
DebugString("Piercing",Get2DAString("iprp_damagecost","Label",c.Piercing));
DebugInt("AB (only base)",c.AB);
DebugString("Massive",Get2DAString("iprp_damagecost","Label",c.Massive));
DebugString("Overwhelming",IntToString(c.Overwhelming)+"d6");
DebugInt("CriticalThreat",c.CriticalThreat);
DebugInt("CriticalMultiplier",c.CriticalMultiplier);
DebugString("Sneak d6",IntToString(c.Sneak)+"d6");

int nABCap = c.Bypass;
effect e = GetFirstEffect(oAttacker);
object oCreator;
//TODO
int favor, prayer, bardsong, aid, cursesong, bless, might, torm;
 while(GetIsEffectValid(e))
 {
 oCreator = GetEffectCreator(e);
  switch(GetEffectSpellId(e))
  {
  case SPELL_DIVINE_FAVOR://fortunately this effect can be present only once
  nFirst = GetCasterLevel(oCreator);
  nSecond = nFirst > 14 ? 5 : nFirst/3;
   if(favor < nSecond)
   {
   favor = nSecond;
   }
  break;
  case SPELL_PRAYER:
  prayer = 1;
  break;
  case SPELL_AID:
  aid = 1;
  break;
  case SPELL_BLESS:
  bless = 1;
  break;
  case SPELL_DIVINE_MIGHT:
  might = GetAbilityModifier(ABILITY_CHARISMA,oAttacker);
  break;
  case FEAT_DIVINE_WRATH:
  //torm = ?;TODO
  break;
  }
 e = GetNextEffect(oAttacker);
 }
/*struct damage_type d = WeaponDamageType(oWeapon);
  if(d.type2 > -1)
  {
  int bOdd = (nHextorDMG/2)*2 != nHextorDMG;
  nHextorDMG = nHextorDMG/2;
  bOdd = bOdd ? nHextorDMG+1 : nHextorDMG;
   switch(d.type2)
   {
   case 0:
   nBludgeoning+= bOdd;
   break;
   case 1:
   nPiercing+= bOdd;
   break;
   case 2:
   nSlashing+= bOdd;
   break;
   }
  }
  switch(d.type1)
  {
  case 0:
  nBludgeoning+= nHextorDMG;
  break;
  case 1:
  nPiercing+= nHextorDMG;
  break;
  case 2:
  nSlashing+= nHextorDMG;
  break;
  }   */
return c;
}



#24
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages
Thanks for the continued input you all.

ShaDoOoW wrote...

BTW physical damage doesn't have to be categorized since the game engine changes its type to the weapon damage type right?

Also there is warcry, prayer, bard song, pdk feats and war domain... practically impossible to get this done correctly without NWNX. With NWNX it should be easy as NWNX has GetEffectInteger function.

But anyway, had the divine favor and might applied for all arrows with also all arrow property seems very inbalanced to me. And keep in mind that I am admin of epic loot/action module. On my server, imbue arrow is still very powerfull, death arrow is useable even with DC 20, hail can be used to exploit spawn systems, only seeker is somewhat useless.


Just to give you a frame of referance Shadooow, a lvl 200 FTR/WM with one of the top end great axes, does about 4-6k dmg on a crit. By comparison a similarially leveled and equiped AA does decent dmg on regular/critical hits, Imbue is really nice, and death arrow is also quite nice, however with hail and seeker they are doing about 150 and 250 dmg respectively for these abilities. The abilities also sacrifice all other actions for that round I believe. So as you can see they are pretty useless by comparison.

You do make a good point though about hail. There are 20-30 maps in the mod (of over 1000+) that use sparky spawns, and in these areas hail is a bit exploitable (also since it recharges, and isn't just a flat 5x/rest.) It doesn't do much dmg, but when hitting 100 targets at once, obviously that's a different story. Fortunately these areas are few compared to all the areas in the mod, but they are quite popolar ones. But for this reason it might be good to only marginally increase hail (If at all), but substantially increase seeker.

Edit: P.S. we do use nwnx.

Modifié par Lazarus Magni, 06 décembre 2011 - 10:05 .


#25
Lazarus Magni

Lazarus Magni
  • Members
  • 1 134 messages

WhiZard wrote...

Here is a basic start to getting damage increase effects if the calculation behind them is already known. (e.g. if someone has divine might, then the bonus from divine might is known to be the charisma modifier). Sometimes I am giving benefit of doubt (like higher cleric/paladin level for getting divine favor bonus).


struct damage_struct
{
int nMagical, nDivine, nPiercing, nSlashing, nBludgeoning, nNegative, nPositive;
int nFire, nCold, nAcid, nElectrical, nSonic;
};


//Gets various values for damage increase effects
struct damage_struct BonusDamage(object oAttacker = OBJECT_SELF);
//Gets the larger of two numbers
int GetHigher(int n1, int n2);

int GetHigher(int n1, int n2)
{
if(n1 > n2)
return n1;
return n2;
}

struct damage_struct BonusDamage(object oAttacker = OBJECT_SELF)
{
struct damage_struct stBonus;
//For divine might use current charisma modifier.
if(GetHasFeatEffect(FEAT_DIVINE_MIGHT))
stBonus.nDivine += GetAbilityModifier(ABILITY_CHARISMA, oAttacker);
//Divine Favor can only be cast on self, thus we can check, paladin/cleric class and give benefit of doubt, UMDers are stuck with default scroll caster level
if(GetHasSpellEffect(SPELL_DIVINE_FAVOR))
stBonus.nMagical += GetHigher(GetHigher(GetLevelByclass(class_TYPE_CLERIC, oAttacker), GetLevelByclass(class_TYPE_PALADIN, oAttacker)), 5)/3;
/*Here insert more spells/feats*/
return stBonus;
}

//This shows how to access struct values
void main()
{
struct damage_struct stDamage = BonusDamage();
int nMagicalDamage = stDamage.nMagical;
int nDivineDamage = stDamage.nDivine;
int nPiercingDamage = stDamage.nPiercing;
}


EDIT:Note that the above will note compile because this social forum always lowercases the word class.


This looks promising Whizard, but I am not exactly sure how to intergrate this into say the seeker script (or the include) to take into account the post 40 dmg?