Skip to content

AI scripting examples

Would anyone who has done AI scripting successfully care to post some examples/explanations for us to learn from? I've not seen specific AI scripting examples discussed in much detail here and think there's quite a bit of interest in making interesting monster behavior.

I've tried a lot of stuff with inconsistent success at best. (at least I have fun trying). I've looked at the x2_ai_... options but clearly don't appreciate everything that is being done in those and why.

Comments

  • TerrorbleTerrorble Member Posts: 169
    Alright, so I guess what I'm interested in is two things:

    1. This is my basic AI script for my Darkener NPC to ensure that it casts darkness when I want (or try to dispel UV/true seeing if they have that up). I've put some comments about parts I don't understand/appreciate what they do. Comments/explanations are welcome.
    2. Share a script to show off what you did. (Maybe AI scripts are inherently too long and complicated for this format, I dunno.)
    void main()
    {
        object oIntruder = GetCreatureOverrideAIScriptTarget();
        ClearCreatureOverrideAIScriptTarget();
    
        //If we're already doing something, don't try something else.
        if(__InCombatRound())
            return;
    
        object oSelf = OBJECT_SELF;
        string sTag = GetTag(oSelf);
    
        object oEnemy = bkAcquireTarget();
    
            //In case our target isn't valid, there should at minimum be a nearby PC... assuming we can see them.
            if( !GetIsObjectValid(oEnemy) )
            {
                oEnemy = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_IS_PC,OBJECT_SELF,1,CREATURE_TYPE_IS_ALIVE,TRUE,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
                //if( GetIsObjectValid(oEnemy) )  AssignCommand(GetObjectByTag("SCREAMER"),ActionSpeakString("oEnemy wasn't valid, got a new one.",TALKVOLUME_SHOUT));//DEBUG
                //else AssignCommand(GetObjectByTag("SCREAMER"),ActionSpeakString("Tried to get a new oEnemy but failed.",TALKVOLUME_SHOUT));//DEBUG
            }
    
    
        if (GetIsObjectValid(oEnemy))
        {
            //OK, we are officially doing something so let's say so to avoid it being interrupted by other AI calls.
                __TurnCombatRoundOn(TRUE);
    
    
            //Only clear actions if we have a valid enemy and therefore are going to decide what to do.
            ClearAllActions();
    
            //I want all NPCs to do this stuff
    		//As far as I can tell, the only things these next few lines make this NPC do are throw up its elemental protection.
    		//Even though the NPC has a number of assigned abilities in the toolset, it doesn't use any of them...
    		//This makes sense because this NPC has no healing capability.
    
                    if(TalentPersistentAbilities()) // * Will put up things like Auras quickly
                    {
                        __TurnCombatRoundOn(FALSE);
                        return;
                    }
                    else if(TalentHealingSelf())
                    {
                        __TurnCombatRoundOn(FALSE);
                        return;
                    }
                    else if(TalentHeal())
                    {
                        __TurnCombatRoundOn(FALSE);
                        return;
                    }
    
    
    ////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////
    ///////////////START NPC SPECIFIC AI ACTIONS////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////
            if( sTag == "darkman" )
            {
                if( !GetHasSpellEffect(SPELL_DARKVISION,oEnemy) && !GetHasSpellEffect(SPELL_TRUE_SEEING,oEnemy) && !GetHasSpellEffect(SPELL_DARKNESS,oEnemy) )
                {
                    if( GetHasSpell(SPELL_DARKNESS,oSelf) )
                    {
                        ActionCastSpellAtLocation(SPELL_DARKNESS,GetLocation(oEnemy));
                    }
                    else
                    {
                        //They don't have any effects but we don't have any Darkness left anyway, so let's quit this special AI altogether.
    		    //Since I achieved what I wanted I can get the normal AI to completely take over by just deleting to custom AIScript var.
    		    
    		    //If I want custom AI actions to resume, I set the AIScript var again in x2_def_userdef when the right conditions are met.
    		    //It's the only way I know right now to get specific behavior when I want, and normal AI the rest of the time.
                        ActionAttack(oEnemy);
                        DeleteLocalString(oSelf,"AIScript");
                        return;
                    }
                }
                else if( GetHasSpell(SPELL_LESSER_DISPEL,oSelf) && GetHasSpellEffect(SPELL_DARKVISION,oEnemy) || GetHasSpellEffect(SPELL_TRUE_SEEING,oEnemy) )
                {
                    ActionCastSpellAtLocation(SPELL_LESSER_DISPEL,GetLocation(oEnemy));
                }
                else if( GetDistanceBetween(oSelf, oEnemy) > 5.0 )
                {
                    bkEquipRanged(oEnemy,FALSE,TRUE);
                    ActionAttack(oEnemy);
                }
                else
                {
                    bkEquipMelee();
    
                    //Don't quit the specific AI completely yet if we still can cast darkness.
                    if( GetHasSpell(SPELL_DARKNESS,oSelf) )
                    {
                        ActionAttack(oEnemy);
                    }
                    else
                    {
                        ActionAttack(oEnemy);
                        DeleteLocalString(oSelf,"AIScript");
                        return;
                    }
                }
            }
        //We're no longer doing specific stuff so allow new actions to be determined.
        __TurnCombatRoundOn(FALSE);
        }
    
        //This has to be here because! (I never tested what happens when it isn't, but the ai_demo said so. Explanations welcome.)
        SetCreatureOverrideAIScriptFinished(OBJECT_SELF);
    }
    
  • sunxresunxre Member Posts: 23
    edited June 2022
    Will be posting unfinished char, with AI script very soon
    Post edited by sunxre on
  • sunxresunxre Member Posts: 23
    edited June 2022
    and. as promised. here are my AI chars. Souls Ring Chars

    in the hakpack. all the ai scripts are charactername+ai. although they won't function without all the other scripts, etc.

    and. the char ai script... it's long, and broken lol.
    #include "sborneweaponmult"
    
    
    //searches surroundings for any visible objects
    object detectobject(object self, int nth, float distance);
    //true if object is within sight cone
    int objectperceived(object self, object target);
    //true if target within angle of origins facing direction
    int conespread(object origin, object target, int coneangle);
    //true if target within angle of origins. checks from facing direction of origin+angle offset
    int conespreadfromangle(object origin, object target, float coneangle, float angleoffset);
    //generates a random float between min & max
    float randomfloat(float min, float max);
    //returns relationship value. seperate to vanilla NWN reputation system
    float getrelation(object self, object target);
    //modifies relationship by value
    void modrelationvalue(object self, object target, float value);
    //basically just teleports current character to a connected door
    void transitionarea(object self, object door, int transitioning = FALSE);
    //compares self to target to see if the target appears dangerous to the character. basically myscore/enemyscore
    float intimidationvaluecomp(object self, object target);
    //simple script to get char to rotate, and look for threats
    void checkforthreat(object self, int duration = 4);
    //same as obstacle script, but for fleeing AI. checks from a further distance to help them avoid enemies
    int foundenemy(object self, object enemy, float offset);
    //used in simple pathing script used when char has nothing else to do. makes sure they don't walk into stuff
    int foundobstacle(object self, object enemy, float offset);
    //causes char to throw strikes. early action combat system.
    void attack(object self, object target, int hand, int combo);
    //below are darksouls spells. mostly are designed to function as in darksouls.
    int divint(int num1, int num2);
    float divfloat(float num1, float num2);
    void greatcombustion(object self, object enemy);
    void firetempest(object self);
    void greatfireball(object self, object target);
    
    //Thank you to the NWN community. especially from nwnLexicon for this + rgbcolours function
    const string colourtoken = "                  ##################$%&'()*+,-./0123456789:;;==?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[[]^_`abcdefghijklmnopqrstuvwxyz{|}~~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¡¢£¤¥¦§¨©ª«¬¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþþ";
    const string colorend = "</c>";
    
    int roundupfloat(float round)
    {
      int r = FloatToInt(round);
      if((IntToFloat(r) + 0.5) <= round)
      {
        return r+=1;
      }
      else
      {
       return r;
      }
    }
    string rgbcolours(int nRed=255, int nGreen=255, int nBlue=255)
    {
        return "<c" + GetSubString(colourtoken, nRed, 1) + GetSubString(colourtoken, nGreen, 1) + GetSubString(colourtoken, nBlue, 1) + ">";
    }
    
    string levelbar(object self, float current, float max, int red, int green, int blue)
    {
      if((current < 0.0 && max > 0.0) || (current > 0.0 && max < 0.0))
      {
        current = 0.0;
        max = 0.0;
      }
      if(current == 0.0 || max == 0.0)
      {
        return "("+rgbcolours(90, 90, 90)+"NNNNNNNNNNNN"+colorend+")";
      }
      int percent = roundupfloat((current/max)*12);
      int remainder;
      string bar = "(";
      if(percent > 12)
      {
        bar += rgbcolours(red, green, blue);
        remainder = 12;
        while(percent > 0)
        {
          remainder--;
          percent--;
          if(remainder == 0)
          {
            bar+= colorend+")"+rgbcolours(red, green, blue);
          }
          bar +="N";
        }
        return bar+"|"+colorend;
      }
      else if(percent == 12)
      {
         return "("+rgbcolours(red, green, blue)+"NNNNNNNNNNNN"+colorend+")";
      }
      else
      {
        remainder = 12-percent;
        bar += rgbcolours(red, green, blue);
        while(percent > 0)
        {
          percent--;
          bar +="N";
        }
        bar += "|"+colorend+rgbcolours(90, 90, 90);
        while(remainder > 0)
        {
          remainder--;
          bar +="N";
        }
      }
      return bar+colorend+")";
    }
    
    
    void main()
    {
      object self = OBJECT_SELF; object belts = GetLocalObject(self, "belts"); object inventory = GetLocalObject(self, "inventory"); object bonfire = GetLocalObject(self, "bonfire"); object door;
      int struck = GetLocalInt(self, "struck"); int stuck; int busy = GetLocalInt(self, "busy"); int humanity = GetLocalInt(self, "chaos"); if(humanity < 5) { SetLocalInt(self, "hollowing", TRUE); } else { SetLocalInt(self, "hollowing", FALSE);} int hollowing = GetLocalInt(self, "hollowing"); if(humanity < 1) { SetLocalInt(self, "hollow", TRUE); } else { SetLocalInt(self, "hollow", FALSE); } int hollow = GetLocalInt(self, "hollow"); int incombat = GetLocalInt(self, "incombat"); int emptyestusflasks; int creatureblood; float friendrank = IntToFloat(GetLocalInt(self, "friendrank")); int followtimer = GetLocalInt(self, "followtimer");
      object flasks = GetFirstItemInInventory(belts); while(GetIsObjectValid(flasks)) { if(GetTag(flasks) == "ercertearemp") { emptyestusflasks += GetItemStackSize(flasks); } if(GetTag(flasks) == "ds1blood") { creatureblood += GetItemStackSize(flasks); } flasks = GetNextItemInInventory(belts); }
      float happiness = 100.0; float currenthealth = GetLocalFloat(self, "currenthealth"); float fullhealth = GetLocalFloat(self, "health"); float currentmagicka = GetLocalFloat(self, "currentmagicka"); float fullmagicka = GetLocalFloat(self, "magicka"); float currentadrenaline = GetLocalFloat(self, "currentadrenaline"); float maxadrenaline = GetLocalFloat(self, "maxadrenaline"); float fear; float bravery = IntToFloat(GetLocalInt(self, "bravery")); float comfort = GetLocalFloat(self, "comfort"); float comforttolerance = GetLocalFloat(self, "comforttolerance"); float comfortimportance = GetLocalFloat(self, "comfortimportance");
      effect cycleeffect = GetFirstEffect(self); effect poison = cycleeffect; effect invisible;
    //-------------------Self variables
      object target = GetNearestObject(OBJECT_TYPE_ALL, self); object friend; object enemy; object injured; object poisoned; object humanitymine; object nearbybonfire; object caution;
      int hashealth; int nth = 1; int nearbyenemycount; int enemycount; int deadenemies;
      float nearestenemydist; float enemyforcestrength; float adrenalinemult = currentadrenaline; if(adrenalinemult < 10.0) { adrenalinemult = 10.0; }
    //-------------------world variables
    
    //makes aggressive AI threshold higher if character has gone hollow. they won't have to hate
    //people as much to attack them.
      float relrank;
      switch(hollowing)
      {
        case FALSE:
          relrank = 30.0;
          break;
        default:
          switch(hollow)
          {
            case FALSE:
              relrank = 50.0;
              break;
            default:
              relrank = 90.0;
              break;
          }
          break;
      }
    //custom function related to hak. basically just displays hlth/stam/magic bars
      if(GetLocalInt(GetLocalObject(self, "summoner"), "displaystatusbar"))
      {
        SpeakString(" HP "+levelbar(self, GetLocalFloat(self, "currenthealth"), GetLocalFloat(self, "health"), 240, 120, 30)+ "\nMP "+levelbar(self, GetLocalFloat(self, "currentmagicka"), GetLocalFloat(self, "magicka"), 110, 120, 255)+" ["+FloatToString(10*GetLocalFloat(self, "currentmagicka"), 0, 0)+"]\n STM"+levelbar(self, GetLocalFloat(self, "currentstamina"), GetLocalFloat(self, "stamina"), 130, 255, 170)+"\n\n"+rgbcolours(205, 200, 120)+"{}  "+IntToString(GetLocalInt(self, "chaos"))+colorend, TALKVOLUME_WHISPER);
      }
    //if health has been lowered in any way. we have been hit. very simplistic atm
      if(GetCurrentHitPoints(self) < GetLocalInt(self, "lasthealthd&d") || currenthealth < GetLocalFloat(self, "lasthealth"))
      {
        struck = TRUE;
        //all mentions of fear below are counted, and then seen if they surpass our courage scores. causes
        //fleeing behaviours
        fear += GetLocalInt(self, "lasthealth")-currenthealth;
      }
    //Object detection. basically looks around to see if anything is seen, and of interest
      while(GetIsObjectValid(target))
      {
        if(objectperceived(self, target))
        {
          if(GetObjectType(target) == OBJECT_TYPE_CREATURE)
          {
            if(!GetIsDead(target) && !GetLocalInt(target, "dead"))
            {
             //if the current detected creature is showing aggression in our direction. blame them all for striking us
              if(struck && conespread(target, self, 40) && (GetIsInCombat(target) || GetLocalInt(target, "incombat")))
              {
                modrelationvalue(self, target, -18.0*(adrenalinemult/10));
                fear += (intimidationvaluecomp(self, target)/3);
                if(!GetIsObjectValid(caution))
                {
                  caution = target;
                }
              }
             //detected creature is acting hostile. but hasn't attacked yet
              else if(GetLocalObject(target, "followtarget") != self && (conespread(target, self, 25) && (GetIsInCombat(target) || GetLocalInt(target, "incombat"))) || (conespread(target, friend, 25) && (GetIsInCombat(target) || GetLocalInt(target, "incombat"))))
              {
                modrelationvalue(self, target, -5.0*(adrenalinemult/10));
                fear += (intimidationvaluecomp(self, target)/10);
                if(!GetIsObjectValid(caution))
                {
                  caution = target;
                }
              }
             //if character is disliked enough to surpass our aggression threshold. save them for violence. flat values atm
              if(getrelation(self, target) <= relrank)
              {
                incombat = TRUE;
                SetLocalInt(self, "incombat", TRUE);
                enemycount++;
                //closer objects are more intimidating.
                if(GetDistanceToObject(target) > 15.0)
                {
                  fear += (intimidationvaluecomp(self, target)/3)/3;
                }
                else
                {
                  fear += intimidationvaluecomp(self, target)/3;
                }
                fear += intimidationvaluecomp(self, target)/3;
                if(GetDistanceToObject(target) < 13.0) { nearbyenemycount++; }
                if(!GetIsObjectValid(enemy)) { enemy = target; nearestenemydist = GetDistanceToObject(target); }
              }
              else if(getrelation(self, target) >= friendrank)
              {
                target = friend;
                //nearby allies. especially strong ones, make us feel safer
                if(GetDistanceToObject(target) > 15.0)
                {
                  fear -= (intimidationvaluecomp(self, target)/3)/3;
                }
                else
                {
                  fear -= intimidationvaluecomp(self, target)/3;
                  //vestigial from other chars script. for curing poisoned friends
                  if(!GetIsObjectValid(poisoned))
                  {
                    poison = GetFirstEffect(target);
                    while (GetIsEffectValid(poison))
                    {
                      if((GetEffectType(poison) == EFFECT_TYPE_POISON || GetEffectType(cycleeffect) == EFFECT_TYPE_DISEASE || GetLocalInt(target, "scarletrot")) && !busy)
                      {
                         target = poisoned;
                         poison = cycleeffect;
                         break;
                      }
                      poison = GetNextEffect(target);
                    }
                  }
                  //as above
                  if(!GetIsObjectValid(injured))
                  {
                    if((FloatToInt(GetMaxHitPoints(target)*0.75) >= GetCurrentHitPoints(target) || (GetLocalFloat(target, "health")*0.75) >= GetLocalFloat(target, "currenthealth")) && !GetLocalInt(target, "regeneratinghealth"))
                    {
                      injured = target;
                    }
                  }
                }
              }
              //character seems to be showing aggression to our current combat target. so we trust them more
              if((conespread(target, enemy, 25) && (GetIsInCombat(target) || GetLocalInt(target, "incombat"))))
              {
                modrelationvalue(self, target, 3.0*(currentadrenaline/10));
              }
              if(conespread(target, humanitymine, 25))
              {
                modrelationvalue(self, target, 3.0*(currentadrenaline/10)+deadenemies);
              }
            }
            else //if target is dead
            {
              if(getrelation(self, target) <= relrank)
              {
                deadenemies++;
              }
              //another vestigial feature. will re-incorporate it. pings dead character as prime sustanence
              //for some juicing souls
              if(!GetIsObjectValid(humanitymine) && !GetLocalInt(target, "humanitydrawn"))
              {
                humanitymine = target;
              }
            }
          }
          else //if target isn't creature
          {
            //for below door function. character automatically uses any areatransition door within 8m, to allow them to "dynamically" travel through the module.
            if(!GetIsObjectValid(door) && GetIsObjectValid(GetTransitionTarget(target)) && GetDistanceToObject(target) <= 8.0)
            {
              door = target;
            }
          }
        }
        nth++;
        target = GetNearestObject(OBJECT_TYPE_ALL, self, nth);
      }
      if(fear < 0.0) { fear = 0.0; }
      //adrenaline system keeps character feeling on edge until it runs out of their system.
      //simple means of keeping up the fear, even when threats aren't directly observed.
      //will later have to save "enemies" into short-term memory for them to worry about
      if((currentadrenaline- (maxadrenaline*0.05)) > 0.0)
      {
        SetLocalFloat(self, "adrenaline", currentadrenaline - (maxadrenaline*0.05));
      }
      else
      {
        SetLocalFloat(self, "adrenaline", 0.0);
      }
      //unfinished. basically a procedural AI system, using bars to determine actions, and preferences for priority.
      //not used on this char.
      if((comfort-0.5) > 0.0)
      {
        SetLocalFloat(self, "comfort", comfort-0.5);
      }
      else
      {
        SetLocalFloat(self, "comfort", 0.0);
      }
      if(fear < 0.0) { fear = 0.0; }
      happiness = 100-((1-divfloat(comfort, comforttolerance))*comfortimportance);
      fear = ((1 - (divfloat(currenthealth, fullhealth)))/100) + (fear/10) + currentadrenaline;
      if((divfloat(fear, bravery)) >= 0.6)
      {
        if((currentadrenaline+fear) < maxadrenaline)
        {
          SetLocalFloat(self, "currentadrenaline", currentadrenaline+fear);
        }
        else
        {
          SetLocalFloat(self, "currentadrenaline", maxadrenaline);
        }
      }
      else if(!GetIsObjectValid(enemy))
      {
        incombat = FALSE; SetLocalInt(self, "incombat", FALSE); SetLocalInt(self, "attackint", FALSE);
      }
      //saves whether we are trapped or not. currently used for fleeing ai, to determine whether we fight back while frozen
      while (GetIsEffectValid(cycleeffect))
      {
        if(GetEffectType(cycleeffect) == EFFECT_TYPE_ENTANGLE || GetEffectType(cycleeffect) == EFFECT_TYPE_SLOW)
        {
          stuck = TRUE;
          break;
        }
        cycleeffect = GetNextEffect(self);
      }
    //-------------------find objects, and gather info
      if((GetIsObjectValid(caution) || struck) && !incombat && !busy)
      {
        SetLocalInt(self, "busy", TRUE); busy++; SetLocalInt(self, "struck", FALSE);
        ClearAllActions();
        ActionMoveToLocation(Location(GetArea(self), GetPosition(self)+AngleToVector(GetFacing(self)), GetFacing(self)-180.0));
        checkforthreat(self, 1);
      }
    //-------------------important actions
      if(incombat && fear <= bravery)
      {
        SendMessageToPC(GetFirstPC(), GetName(self)+" in battle");
        if(currentmagicka >= 1.449 && !busy && nearbyenemycount >= 4 && nearestenemydist <= 5.0)  //casts the iconic firestorm spell(tempest is stronger version)
        {
    //      SendMessageToPC(GetFirstPC(), GetName(self)+" casting firetempest");
          SetLocalInt(self, "busy", TRUE); busy++;
          ClearAllActions();
          firetempest(self);
        }
        if(currentmagicka >= 0.772 && !busy && nearestenemydist <= 2.5) //cast combustion
        {
    //      SendMessageToPC(GetFirstPC(), GetName(self)+" casting great combustion");
          SetLocalInt(self, "busy", TRUE); busy++;
          ClearAllActions();
          greatcombustion(self, enemy);
        }
        else if(currentmagicka >= 1.545 && !busy && nearestenemydist <= 42.0 && nearestenemydist >= (0.621*GetLocalFloat(self, "magicattack")+1.5))
        {
    //      SendMessageToPC(GetFirstPC(), GetName(self)+" casting Great Fireball");
          SetLocalInt(self, "busy", TRUE); busy++;
          ClearAllActions();
          greatfireball(self, enemy);
        }//below is the beginning of the action combat Ai.
        float distance = (weaponreach(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, self))+5.0+randomfloat(-0.6, 2.5));  //avoiding striking range, based on own weapon length
        //timer character will maintain distance and circle character, rather than swinging at them. preset
        //to add consistency to the AI
        int attackint = GetLocalInt(self, "attackint");
        if(attackint < 0 && !busy)
        {
          switch(Random(5))
          {
            default:
              SetLocalInt(self, "attackint", 5);
              break;
            case 1:
              SetLocalInt(self, "attackint", 2);
              break;
            case 2:
              SetLocalInt(self, "attackint", 6);
              break;
            case 3:
              SetLocalInt(self, "attackint", 10);
              break;
            case 4:
              SetLocalInt(self, "attackint", 15);
              break;
          }
        }
        else if(attackint > 0 && !busy)
        {
          SetLocalInt(self, "attackint", attackint-1);
        }
        //move into attacking range
        if(!attackint && nearestenemydist >= (weaponreach(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, self))+2.5) && !busy)
        {
          ClearAllActions();
          if(nearestenemydist >= 15.0 || (GetLocalInt(enemy, "busy") && GetLocalInt(enemy, "incombat")) || (GetIsInCombat(enemy) &&  (GetCurrentAction(enemy) != ACTION_ATTACKOBJECT || GetCurrentAction(enemy) != ACTION_MOVETOPOINT)))
          {
            ActionMoveToObject(enemy, TRUE, 1.0);
          }
          else
          {
            ActionMoveToObject(enemy, FALSE, 1.0);
          }
        }
        //swing attack
        if(!busy && !attackint)
        {
          SetLocalInt(self, "attackint", -1);
          ClearAllActions();
          SetLocalInt(self, "busy", TRUE); busy++;
          attack(self, enemy, 1, Random(4));
          if(GetLocalInt(self, "invisible"))
          {
            SetLocalInt(self, "cancelinvisible", TRUE);
          }
        }
        //below section is just for keeping away from enemy and circling, while waiting for our timer to attack
        else if(!busy && nearestenemydist > distance)
        {
          ClearAllActions();
          if(nearestenemydist >= 15.0 || (GetLocalInt(enemy, "busy") && GetLocalInt(enemy, "incombat")) || (GetIsInCombat(enemy) &&  (GetCurrentAction(enemy) != ACTION_ATTACKOBJECT || GetCurrentAction(enemy) != ACTION_MOVETOPOINT)))
          {
            ActionMoveToObject(enemy, TRUE, distance);
          }
          else
          {
            ActionMoveToObject(enemy, FALSE, distance);
          }
        }
        else if(!busy)
        {
          ClearAllActions();
          int noenemy;
          int anglenth = 1;
          float offset;
          int circle = GetLocalInt(self, "circle");
          int circledirection = GetLocalInt(self, "circledirection");
          if(circle > 0)
          {
            SetLocalInt(self, "circle", circle-1);
          }
          else
          {
            if(!circledirection)
             {
              offset = randomfloat(-40.0, -80.0);
              SetLocalInt(self, "circledirection", TRUE);
            }
            else
            {
              offset = randomfloat(40.0, 80.0);
              SetLocalInt(self, "circledirection", FALSE);
            }
            switch(Random(5))
            {
              default:
                SetLocalInt(self, "circle", 3);
                break;
              case 1:
                SetLocalInt(self, "circle", 7);
                break;
              case 2:
                SetLocalInt(self, "circle", 2);
                break;
              case 3:
                SetLocalInt(self, "circle", 13);
                break;
            }
          }
          vector currentpos = GetPosition(enemy);
          vector targetpos = AngleToVector(offset+VectorToAngle(GetPosition(self)-GetPosition(enemy)))+GetPosition(enemy);
          float x = targetpos.x-currentpos.x; float y = targetpos.y-currentpos.y; float truex = x; float truey = y;
          if(x < 0.0)
          {
            x = 0-x;
          }
          if(y < 0.0)
          {
            y = 0-y;
          }
          float distanceportion = divfloat(distance, (x+y));
          float distancex = truex*distanceportion;
          float distancey = truey*distanceportion;
          currentpos.x += distancex; currentpos.y += distancey;
          ClearAllActions();
          ActionMoveToLocation(Location(GetArea(self), currentpos, 0.0), TRUE);
          DelayCommand(0.4, SetFacing(VectorToAngle(GetPosition(enemy)-GetPosition(self))));
        }
      }
    //-------------------combat actions
      else if(fear > bravery)     //routing AI
      {
        SendMessageToPC(GetFirstPC(), GetName(self)+"'s morales broken");
        SetLocalInt(self, "afraid", TRUE);   //used by checkforthreats script. once calm, they will look around to make sure they weren't pursued
        if(!busy)
        {
    //      SendMessageToPC(GetFirstPC(), GetName(self)+" is getting out of there");
          int noenemy;
          int anglenth = 1;
          float offset = 0.0;
          if(GetLocalInt(self, "travelint") == 0)  //i have no idea... don't remember this
          {
            SetLocalInt(self, "travelint", 1+Random(10));
            offset += randomfloat(-110.0, 110.0);
          }
          else
          {
            SetLocalInt(self, "travelint", GetLocalInt(self, "travelint")-1);
          }
          float interval;
          switch(Random(2))
          {
            default:
              interval = 30.0;
              break;
            case 1:
              interval = -30.0;
              break;
          }
          while(!noenemy && offset < 360.0) //checking for threats/obstacles in the way of our retreat, and rotating until we find a safe position
          {
            if(!foundenemy(self, enemy, offset))
            {
              noenemy++;
              break;
            }
            offset += 0-(offset*2);
            if(!foundenemy(self, enemy, offset))
            {
              noenemy++;
              break;
            }
            offset +=(offset*2)+interval;
          }
          vector currentpos = GetPosition(self);
          vector targetpos = AngleToVector(offset+GetFacing(self))+GetPosition(self);
          float x = targetpos.x-currentpos.x; float y = targetpos.y-currentpos.y; float truex = x; float truey = y;
          if(x < 0.0)
          {
            x = 0-x;
          }
          if(y < 0.0)
          {
            y = 0-y;
          }
          float distanceportion = divfloat(5.0, (x+y));
          float distancex = truex*distanceportion;
          float distancey = truey*distanceportion;
          currentpos.x += distancex; currentpos.y += distancey;
          ClearAllActions();
          ActionMoveToLocation(Location(GetArea(self), currentpos, 0.0), TRUE);
        }
      }
    //--------------------routing actions
      else //relaxed actions
      {
    //    SendMessageToPC(GetFirstPC(), GetName(self)+"'s relaxed");
        if(IsInConversation(self)) //make sure they aren't doing unecessary stuff while chatting
        {
          busy++;
        }
        else if(!busy && GetIsObjectValid(door))
        {
          SetLocalInt(self, "busy", TRUE); busy++;
          ClearAllActions();
          transitionarea(self, door);
        }
    //LAST TIME SYSTEM WAS BROKEN. MADE MANY CHANGES, BUT IS STILL JUST A CONCEPT. FEEL FREE TO COMMENT OUT FOLLOW SYSTEM
        if(!busy && ((GetIsObjectValid(friend) && GetLocalObject(friend, "followtarget") != self) || (GetIsObjectValid(GetLocalObject(self, "followtarget")) && GetLocalObject(GetLocalObject(self, "followtarget"), "followtarget") != self )))
        {
          if(!followtimer)
          {
            DeleteLocalObject(self, "followtarget");
            SetLocalInt(self, "followtimer", 20+Random(50));
          }
          SetLocalObject(self, "followtarget", friend); busy++;
          vector currentpos = GetPosition(self);
          vector targetpos = AngleToVector(GetFacing(friend)-180)+GetPosition(friend);
          float x = targetpos.x-currentpos.x; float y = targetpos.y-currentpos.y; float truex = x; float truey = y;
          if(x < 0.0)
          {
            x = 0-x;
          }
          if(y < 0.0)
          {
            y = 0-y;
          }
          float distanceportion = divfloat(2.5, (x+y));
          float distancex = truex*distanceportion;
          float distancey = truey*distanceportion;
          currentpos += Vector(distancex, distancey, GetPosition(friend).z);
          ClearAllActions();
          if(GetDistanceToObject(friend) > 5.0)
          {
            ActionMoveToLocation(Location(GetArea(self), currentpos, GetFacing(friend)), TRUE);
          }
          else
          {
            ActionMoveToLocation(Location(GetArea(self), currentpos, GetFacing(friend)), FALSE);
          }
        }
        else if(!busy)
        {
          DeleteLocalObject(self, "followtarget");
    //      SendMessageToPC(GetFirstPC(), "strolling");
          int noenemy;
          int anglenth = 1;
          float offset = 0.0;
          if(GetLocalInt(self, "travelint") == 0)
          {
            SetLocalInt(self, "travelint", 1+Random(10));
            offset += randomfloat(-110.0, 110.0);
          }
          else
          {
            SetLocalInt(self, "travelint", GetLocalInt(self, "travelint")-1);
          }
          float interval;
          switch(Random(2))
          {
            default:
              interval = 30.0;
              break;
            case 1:
              interval = -30.0;
              break;
          }
          while(!noenemy && offset < 360.0)
          {
            if(!foundobstacle(self, enemy, offset))
            {
              noenemy++;
              break;
            }
            offset += 0-(offset*2);
            if(!foundobstacle(self, enemy, offset))
            {
              noenemy++;
              break;
            }
            offset +=(offset*2)+interval;
          }
          vector currentpos = GetPosition(self);
          vector targetpos = AngleToVector(offset+GetFacing(self))+GetPosition(self);
          float x = targetpos.x-currentpos.x; float y = targetpos.y-currentpos.y; float truex = x; float truey = y;
          if(x < 0.0)
          {
            x = 0-x;
          }
          if(y < 0.0)
          {
            y = 0-y;
          }
          float distance = 5.0;
          if(GetLocalFloat(self, "distance") > 0.0)
          {
            distance = GetLocalFloat(self, "distance");
            SetLocalFloat(self, "distance", 0.0);
          }
          float distanceportion = divfloat(5.0, (x+y));
          float distancex = truex*distanceportion;
          float distancey = truey*distanceportion;
          currentpos.x += distancex; currentpos.y += distancey;
          ClearAllActions();
          switch(hollowing)
          {
            case FALSE:
              ActionMoveToLocation(Location(GetArea(self), currentpos, 0.0), FALSE);
              break;
            default:
              ActionMoveToLocation(Location(GetArea(self), currentpos, 0.0), TRUE);
              break;
          }
        }
      }
    //--------------------routine AI actions
      if(GetLocalInt(self, "aitick") < 100)   //after 100 "ticks". AI deactivates to prevent TMI. being reset by heartbeat
      {
        if(!GetIsDead(self))
        {
          SetLocalInt(self, "aitick", GetLocalInt(self, "aitick")+1);
          SetLocalInt(self, "lasthealthd&d", GetCurrentHitPoints(self));
          SetLocalFloat(self, "lasthealth", GetLocalFloat(self, "currenthealth"));
          DelayCommand(0.5, ExecuteScript("queelanaai"));
        }
        else
        {
          SetLocalInt(self, "aitick", 500);
        }
      }
    //--------------------AI reset
    }
    
    //////////////////////////////////////////////////////////
    //=========================================(END OF MAIN())
    //\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
    //|______________________________________________________|
    
    Post edited by sunxre on
  • sunxresunxre Member Posts: 23
    section 2...
    //WILL NOT BE COMMENTING ALL FUNCTIONS LOL
    
    
    //------------------------------------------------------\
    //                                                       \
    //                                                        \
    //=============UTILITIES & Miscellaneous=================  \
    //                                                         /
    //                                                        /
    //-------------------------------------------------------/
    int divint(int num1, int num2)
    {
      if(num1 || num2 == 0)
      {
        return 0;
      }
      return num1/num2;
    }
    
    float divfloat(float num1, float num2)
    {
      if(num1 == 0.0 || num2 == 0.0)
      {
        return 0.0;
      }
      return num1/num2;
    }
    
    void modrelationvalue(object self, object target, float value)
    {
      string name = GetName(target);
      float relationshipvalue = getrelation(self, target);
      if(relationshipvalue == 0.0)
      {
        relationshipvalue = 0.0;
      }
      if((relationshipvalue+value) <= 0.0)
      {
        relationshipvalue = 1.0;
      }
      else if((relationshipvalue+value) >= 100.0)
      {
        relationshipvalue = 100.0;
      }
      else
      {
       relationshipvalue += value;
      }
      SetLocalFloat(self, name, relationshipvalue);
    }
    
    
    float factionrelationbonus(object self, object target)
    {
      if(GetLocalInt(self, "wayofthewhitecovenant"))
      {
        if(GetLocalInt(target, "wayofthewhitecovenant"))
        {
          return 35.0;
        }
        else if(GetLocalInt(target, "wayofthewhiteassociate"))
        {
          return 20.0;
        }
        else
        {
          return 0.0;
        }
      }
      if(GetLocalInt(self, "wayofthewhiteassociate"))
      {
        if(GetLocalInt(target, "wayofthewhitecovenant") || GetLocalInt(target, "wayofthewhiteassociate"))
        {
          return 20.0;
        }
        else
        {
          return 0.0;
        }
      }
      return 0.0;
    }
    
    float getrelation(object self, object target)
    {
      string name = GetName(target);
      if(GetLocalFloat(self, name) == 0.0)
      {
        SetLocalFloat(self, name, 50.0+factionrelationbonus(self, target));
        return GetLocalFloat(self, name);
      }
      return GetLocalFloat(self, name);
    }
    
    float intimidationvaluecomp(object self, object target)
    {
      float personalscore; float targetscore;
      int humanoidknowledge = GetLocalInt(self, "humanoidknowledge"); int animalknowledge = GetLocalInt(self, "animalknowledge");
      int medicalknowledge = GetLocalInt(self, "medicalknowledge"); int feyknowledge = GetLocalInt(self, "feyknowledge"); int giantknowledge = GetLocalInt(self, "giantknowledge"); int magicbeastknowledge = GetLocalInt(self, "magicbeastknowledge");
      int monstrousknowledge = GetLocalInt(self, "graftingknowledge"); int undeadknowledge = GetLocalInt(self, "necromancyknowledge"); int aberrationknowledge = GetLocalInt(self, "aberrationknowledge"); int dragonknowledge = GetLocalInt(self, "dragonknowledge");
      int golemknowledge = GetLocalInt(self, "golemcraft"); int elementalknowledge = GetLocalInt(self, "elementsknowledge"); int outsiderknowledge = GetLocalInt(self, "demonology"); int oozeknowledge = GetLocalInt(self, "oozeknowledge");
      switch(GetAppearanceType(target))
      {
    //too long to post
      }
      switch(GetAppearanceType(self))
      {
    //too long to post
      }
      if(GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, self))) { personalscore += 2.0; }
      if(GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, target))) { targetscore += 2.0; }
      if(GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, self))) { personalscore += 1.0; }
      if(GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, target))) { targetscore += 1.0; }
      if(GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_HEAD, self))) { personalscore += 0.5; }
      if(GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_HEAD, target))) { targetscore += 0.5; }
      return (divfloat(personalscore, targetscore));
    }
    
    void checkforthreat(object self, int duration = 4)
    {
      object target = detectobject(self, 1, 90.0);
      int nth;
      while(GetIsObjectValid(target))
      {
        if(getrelation(self, target) <= 35.0)
        {
          break;
        }
        nth++;
        target = detectobject(self, nth, 90.0);
      }
      if(!GetIsObjectValid(target) && duration > 0 && !GetIsDead(self))
      {
        SetFacing(GetFacing(self)+randomfloat(15.0, 180.0));
        ClearAllActions();
        DelayCommand(0.4, checkforthreat(self, duration-1));
      }
      else
      {
        SetLocalInt(self, "busy", FALSE);
      }
    }
    
    void Knockback(object target, float angle, float power, float lift)  //haven't bothered making this yet. basically will be used for staggering, and sending targets flying
    {
    
    }
    
    int foundenemy(object self, object enemy, float offset)
    {
          int nth = 1;
          enemy = GetNearestObject(OBJECT_TYPE_CREATURE, self);
          while(GetIsObjectValid(enemy) && GetDistanceToObject(enemy) < 50.0)
          {
            if(conespreadfromangle(self, enemy, 30.0, 0.0+offset) && (getrelation(self, enemy) <= 30.0 || (conespread(enemy, self, 25) && (GetIsInCombat(enemy) || GetLocalInt(enemy, "incombat")))) && !GetIsDead(enemy))
            {
              return TRUE;
            }
            nth++;
            enemy = GetNearestObject(OBJECT_TYPE_CREATURE, self, nth);
          }
          vector currentpos = GetPosition(self);
          vector targetpos = AngleToVector(offset+GetFacing(self))+GetPosition(self);
          float x; float y; float truex; float truey;
          float distanceportion;
          x = targetpos.x-currentpos.x; y = targetpos.y-currentpos.y; truex = x; truey = y;
          if(x < 0.0)
          {
            x = 0-x;
          }
          if(y < 0.0)
          {
            y = 0-y;
          }
          distanceportion = divfloat(10.0, (x+y));
          float distancex = truex*distanceportion;
          float distancey = truey*distanceportion;
          currentpos.x += distancex; currentpos.y += distancey;
          if(!LineOfSightVector(GetPosition(self), currentpos))
          {
            return TRUE;
          }
          return FALSE;
    }
    
    int foundobstacle(object self, object enemy, float offset)
    {
          int nth = 1;
          enemy = GetNearestObject(OBJECT_TYPE_CREATURE, self);
          while(GetIsObjectValid(enemy) && GetDistanceToObject(enemy) <= 4.0)
          {
            if(conespreadfromangle(self, enemy, 70.0, 0.0+offset) && !GetIsDead(enemy))
            {
              return TRUE;
            }
            nth++;
            enemy = GetNearestObject(OBJECT_TYPE_CREATURE, self, nth);
          }
          vector currentpos = GetPosition(self); currentpos.z-=0.5;
          vector targetpos = AngleToVector(offset+GetFacing(self))+GetPosition(self);
          float x; float y; float truex; float truey;
          float distanceportion;
          x = targetpos.x-currentpos.x; y = targetpos.y-currentpos.y; truex = x; truey = y;
          if(x < 0.0)
          {
            x = 0-x;
          }
          if(y < 0.0)
          {
            y = 0-y;
          }
          distanceportion = divfloat(10.0, (x+y));
          float distancex = truex*distanceportion;
          float distancey = truey*distanceportion;
          targetpos = currentpos+Vector(distancex, distancey, 0.0);
          if(!LineOfSightVector(GetPosition(self), targetpos))
          {
            distanceportion /= 10;  distancex = truex*distanceportion; distancey = truey*distanceportion;
            currentpos = GetPosition(self)+Vector(distancex, distancey, 0.5);
            if(LineOfSightVector(GetPosition(self), currentpos) && !LineOfSightVector(currentpos, Vector(currentpos.x, currentpos.y, -0.5)))
            {
              currentpos = GetPosition(self)+Vector((distancex*3), (distancey*3), 1.0);
              if(LineOfSightVector(GetPosition(self), currentpos) && !LineOfSightVector(currentpos, Vector(currentpos.x, currentpos.y, -1.0)))
              {
                currentpos = GetPosition(self)+Vector((distancex*5), (distancey*5), 1.5);
                if(LineOfSightVector(GetPosition(self), currentpos) && !LineOfSightVector(currentpos, Vector(currentpos.x, currentpos.y, -1.5)))
                {
                  SetLocalFloat(self, "distance", 2.0);
                  return FALSE;
                }
              }
            }
            return TRUE;
          }
          return FALSE;
    }
    
    float randomfloat(float min, float max)
    {
      int minnum = FloatToInt(min*100);
      int randhigh = FloatToInt(max*100);
      randhigh = (Random(randhigh)+1)+(minnum/2);
      if(randhigh < minnum)
      {
        return min;
      }
      else if(randhigh > (FloatToInt(max)*100))
      {
        return max;
      }
      return IntToFloat(randhigh)/100.0;
    }
    
    object detectobject(object self, int nth, float distance)
    {
      object spotted = GetNearestObject(OBJECT_TYPE_ALL, self, nth);
      if(objectperceived(self, spotted) || ((GetIsInCombat(spotted) || GetLocalInt(spotted, "incombat")) && GetDistanceToObject(spotted) < 25.0 && (!GetIsDead(spotted) || !GetLocalInt(spotted, "dead"))) || ((GetCurrentAction(spotted) == ACTION_ATTACKOBJECT || GetCurrentAction(spotted) == ACTION_CASTSPELL || GetCurrentAction(spotted) == ACTION_COUNTERSPELL || GetCurrentAction(spotted) == ACTION_ITEMCASTSPELL || GetCurrentAction(spotted) == ACTION_MOVETOPOINT || GetCurrentAction(spotted) == ACTION_RANDOMWALK) && GetDistanceToObject(spotted) < 15.0 && (!GetIsDead(spotted) || !GetLocalInt(spotted, "dead"))))
      //if(objectperceived(self, spotted)) // only checks if char is in sight cone. which is Getfacing+60 degrees in either direction
      {
        return spotted;
      }
      return OBJECT_INVALID;
    }
    
    int objectperceived(object self, object target)
    {
      if(LineOfSightObject(self, target) && conespread(self, target, 120))
      {
        return TRUE;
      }
      return FALSE;
    }
    
    int conespread(object origin, object target, int coneangle)
    {
      float angleoffset = VectorToAngle((GetPosition(target)+Vector(GetObjectVisualTransform(target, OBJECT_VISUAL_TRANSFORM_TRANSLATE_X), GetObjectVisualTransform(target, OBJECT_VISUAL_TRANSFORM_TRANSLATE_Y), GetObjectVisualTransform(target, OBJECT_VISUAL_TRANSFORM_TRANSLATE_Z))) - (GetPosition(origin)+Vector(GetObjectVisualTransform(origin, OBJECT_VISUAL_TRANSFORM_TRANSLATE_X), GetObjectVisualTransform(origin, OBJECT_VISUAL_TRANSFORM_TRANSLATE_Y), GetObjectVisualTransform(origin, OBJECT_VISUAL_TRANSFORM_TRANSLATE_Z))))-GetFacing(origin);
      //coneangle = 180.0;
      return(abs(FloatToInt(angleoffset)) < coneangle/2);
    }
    
    int conespreadfromangle(object origin, object target, float coneangle, float angleoffset)
    {
      float angle = GetFacing(origin)+angleoffset;
      if(angle < 0.0)
      {
        angle = 360.0 + angle;
      }
      else if(angle > 360.0)
      {
        angle -= 360.0;
      }
      float angleoffset = VectorToAngle((GetPosition(target)+Vector(GetObjectVisualTransform(target, OBJECT_VISUAL_TRANSFORM_TRANSLATE_X), GetObjectVisualTransform(target, OBJECT_VISUAL_TRANSFORM_TRANSLATE_Y), GetObjectVisualTransform(target, OBJECT_VISUAL_TRANSFORM_TRANSLATE_Z))) - (GetPosition(origin)+Vector(GetObjectVisualTransform(origin, OBJECT_VISUAL_TRANSFORM_TRANSLATE_X), GetObjectVisualTransform(origin, OBJECT_VISUAL_TRANSFORM_TRANSLATE_Y), GetObjectVisualTransform(origin, OBJECT_VISUAL_TRANSFORM_TRANSLATE_Z))))-angle;
      //coneangle = 180.0;
      return(abs(FloatToInt(angleoffset)) < FloatToInt(coneangle/2));
    }
    
    void invischeck(object target, int duration)
    {
      if(GetLocalInt(target, "cancelinvisible"))
      {
        duration = -1;
        SetLocalInt(target, "cancelinvisible", FALSE);
        effect invisible = GetFirstEffect(target);
        while (GetIsEffectValid(invisible))
        {
          if(GetEffectType(invisible) == EFFECT_TYPE_INVISIBILITY)
          {
             RemoveEffect(target, invisible);
             break;
          }
          invisible = GetNextEffect(target);
        }
        SetLocalInt(target, "invisible", FALSE);
        return;
      }
      if(duration > 0)
      {
        DelayCommand(1.0, invischeck(target, duration-1));
      }
      else
      {
        SetLocalInt(target, "invisible", FALSE);
      }
    }
    
    
    void transitionarea(object self, object door, int transitioning = FALSE)
    {
      if(GetDistanceToObject(door) > 1.5 && !GetIsDead(self) && !transitioning)
      {
        SetCommandable(TRUE, self);
        ClearAllActions();
        ActionMoveToObject(door, FALSE, 0.0);
        SetCommandable(FALSE, self);
        DelayCommand(1.0, transitionarea(self, door));
      }
      else if(!GetIsDead(self) && transitioning)
      {
        if(GetIsOpen(door))
        {
          SetCommandable(TRUE, self);
          door = GetTransitionTarget(door);
          JumpToObject(door);
          SetCommandable(FALSE, self);
          DelayCommand(2.0, SetCommandable(TRUE, self));
          DelayCommand(2.0, SetLocalInt(self, "busy", FALSE));
        }
        else
        {
          SetCommandable(TRUE, self);
          switch(Random(2))
          {
           default:
             SetFacing(GetFacing(self)-randomfloat(-90.0, -140.0));
             break;
           case 1:
             SetFacing(GetFacing(self)-randomfloat(90.0, 140.0));
             break;
          }
          SetCommandable(FALSE, self);
          DelayCommand(1.0, SetCommandable(TRUE, self));
          DelayCommand(1.0, SetLocalInt(self, "busy", FALSE));
        }
      }
      else if(!GetIsDead(self))
      {
        SetCommandable(TRUE, self);
        DoDoorAction(door, DOOR_ACTION_OPEN);
        SetCommandable(FALSE, self);
        DelayCommand(1.5, transitionarea(self, door, TRUE));
      }
      else
      {
        SetCommandable(TRUE, self);
        SetLocalInt(self, "busy", FALSE);
      }
    }
    
  • sunxresunxre Member Posts: 23
    and now.. the spell section.
    //------------------------------------------------------\
    //                                                       \
    //                                                        \
    //=============Great Fireball============================  \
    //                                                         /
    //                                                        /
    //-------------------------------------------------------/
    
    void greatfiredamage(object self, location flamelocation, float damage, float explosionsize)
    {
      if(GetDistanceBetweenLocations(flamelocation, GetLocation(self)) <= (0.621*GetLocalFloat(self, "magicattack")+1.2) && !GetLocalInt(self, "busy"))
      {
         ClearAllActions();
         ActionMoveAwayFromLocation(flamelocation, TRUE, 0.621*GetLocalFloat(self, "magicattack")+1.0);
      }
      object collisiontarget = GetNearestObjectToLocation(OBJECT_TYPE_ALL, flamelocation);
      int nth = 1;
      while(GetIsObjectValid(collisiontarget) && GetDistanceBetweenLocations(flamelocation, GetLocation(collisiontarget)) <= explosionsize)
      {
        if(LineOfSightVector(GetPosition(collisiontarget), GetPositionFromLocation(flamelocation)))
        {
          ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(0, DAMAGE_TYPE_FIRE), collisiontarget);
          if(GetLocalFloat(collisiontarget, "health") > 0.0)
          {
            float realdamage = (damage - GetLocalInt(collisiontarget, "magicdefence"))*(IntToFloat(100-GetLocalInt(collisiontarget, "fireresist"))/100);
            if(realdamage > 0.0)
            {
              SetLocalFloat(collisiontarget, "currenthealth", GetLocalFloat(collisiontarget, "currenthealth")-realdamage);
              AssignCommand(collisiontarget, SpeakString(FloatToString(realdamage, 0, 0)));
            }
          }
          else
          {
            ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(FloatToInt(damage), DAMAGE_TYPE_FIRE), collisiontarget);
          }
        }
        nth++;
        collisiontarget = GetNearestObjectToLocation(OBJECT_TYPE_ALL, flamelocation, nth);
      }
    }
    
    void greatfireproj(object self, object target, object projectile, float damage, float explosionsize, float fullspeed, float speed, float upspeed, vector targetpos, vector currentpos, vector previouspos, location projloc, float xpos, float ypos, float zpos, int targeted = FALSE)
    {
      if(!targeted)
      {
        targeted++;
        vector targetspeed = GetPosition(target);
        float x = targetspeed.x-targetpos.x; float y = targetspeed.y-targetpos.y; float truex = x; float truey = y;
        if(x < 0.0)
        {
          x=0-x;
        }
        if(y < 0.0)
        {
          y=0-y;
        }
        float distance = sqrt( ((targetpos.x-targetspeed.x)*(targetpos.x-targetspeed.x)) + ((targetpos.y-targetspeed.y)*(targetpos.y-targetspeed.y)) );
        float distanceportion = divfloat(distance, (x+y)); float distancex = truex*distancex; float distancey = truey*distancey;
        float prevdistance = sqrt( ((currentpos.x-targetpos.x)*(currentpos.x-targetpos.x)) + ((currentpos.y-targetpos.y)*(currentpos.y-targetpos.y)) );
        targetpos = Vector(targetspeed.x+distancex, targetspeed.y+distancey, targetspeed.z+1.5);
        upspeed *= divfloat(distance, prevdistance);
      }
      float x = targetpos.x-currentpos.x; float y = targetpos.y-currentpos.y; float truex = x; float truey = y;
      if(x < 0.0)
      {
        x = 0-x;
      }
      if(y < 0.0)
      {
        y = 0-y;
      }
      float distanceportion = divfloat((speed*0.1), (x+y)); float distancex = truex*distanceportion; float distancey = truey*distanceportion;
      currentpos += Vector(distancex, distancey, (upspeed*0.1));
      xpos += distancex; ypos += distancey; zpos +=(upspeed*0.1);
      int nth = 1; object collisiontarget;
      switch(LineOfSightVector(previouspos, Vector(currentpos.x, currentpos.y, currentpos.z-1.0)))
      {
        case TRUE:
          upspeed -= (9.8*0.1);
          speed -= (fullspeed*0.01);
          projloc = Location(GetAreaFromLocation(projloc), currentpos, 0.0);
          if(GetDistanceBetweenLocations(projloc, GetLocation(self)) <= (0.621*GetLocalFloat(self, "magicattack")+1.2) && !GetLocalInt(self, "busy"))
          {
             ClearAllActions();
             ActionMoveAwayFromLocation(projloc, TRUE, 0.621*GetLocalFloat(self, "magicattack")+1.0);
          }
          SetObjectVisualTransform(projectile, OBJECT_VISUAL_TRANSFORM_TRANSLATE_X, xpos, OBJECT_VISUAL_TRANSFORM_LERP_LINEAR, 0.1);  SetObjectVisualTransform(projectile, OBJECT_VISUAL_TRANSFORM_TRANSLATE_Y, ypos, OBJECT_VISUAL_TRANSFORM_LERP_LINEAR, 0.1); SetObjectVisualTransform(projectile, OBJECT_VISUAL_TRANSFORM_TRANSLATE_Z, zpos, OBJECT_VISUAL_TRANSFORM_LERP_LINEAR, 0.1);
          collisiontarget = GetNearestObjectToLocation(OBJECT_TYPE_ALL, projloc);
          if(collisiontarget == projectile)
          {
            collisiontarget = GetNearestObjectToLocation(OBJECT_TYPE_ALL, projloc, 2);
          }
          ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_IMP_SUNSTRIKE, FALSE, 0.9), projectile, 0.1);
          if(GetDistanceBetweenLocations(projloc, GetLocation(collisiontarget)) <= 1.0 && collisiontarget != self)
          {
            DestroyObject(projectile);
            ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_SUNSTRIKE, FALSE, (explosionsize*0.9)), projloc);
            ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_1, FALSE, (explosionsize*0.1)), projloc);
            ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_FLAME_M, FALSE, (explosionsize*0.8)), projloc);
            DelayCommand(0.5, greatfiredamage(self, projloc, damage, explosionsize));
            collisiontarget = GetNearestObjectToLocation(OBJECT_TYPE_ALL, projloc);
            while(GetIsObjectValid(collisiontarget) && GetDistanceBetweenLocations(projloc, GetLocation(collisiontarget)) <= 1.0)
            {
              if(LineOfSightVector(currentpos, GetPosition(collisiontarget)))
              {
                SetLocalInt(collisiontarget, "struck", TRUE);
                ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(0, DAMAGE_TYPE_FIRE), collisiontarget);
                ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_FLAME_S), collisiontarget);
                if(GetLocalFloat(collisiontarget, "health") < 0.1)
                {
                  ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(FloatToInt(damage/10), DAMAGE_TYPE_FIRE), collisiontarget);
                }
                else
                {
                  damage = ((damage/10) - GetLocalInt(collisiontarget, "magicdefence"))*(IntToFloat(100-GetLocalInt(collisiontarget, "fireresist"))/100);
                  if(damage > 0.0)
                  {
                    SetLocalFloat(collisiontarget, "currenthealth", GetLocalFloat(collisiontarget, "currenthealth")-damage);
                    AssignCommand(collisiontarget, SpeakString(FloatToString(damage, 0, 0)));
                  }
                }
              }
              nth++;
              collisiontarget = GetNearestObject(OBJECT_TYPE_ALL, self, nth);
            }
            return;
          }
          DelayCommand(0.1, greatfireproj(self, target, projectile, damage, explosionsize, fullspeed, speed, upspeed, targetpos, currentpos, currentpos, projloc, xpos, ypos, zpos, targeted));
          break;
        case FALSE:
          DestroyObject(projectile);
          ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_SUNSTRIKE, FALSE, (explosionsize*0.9)), projloc);
          ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_1, FALSE, (explosionsize*0.1)), projloc);
          ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_FLAME_M, FALSE, (explosionsize*0.8)), projloc);
          DelayCommand(0.5, greatfiredamage(self, projloc, damage, explosionsize));
          collisiontarget = GetNearestObjectToLocation(OBJECT_TYPE_ALL, projloc);
          while(GetIsObjectValid(collisiontarget) && GetDistanceBetweenLocations(projloc, GetLocation(collisiontarget)) <= 1.0)
          {
            if(LineOfSightVector(currentpos, GetPosition(collisiontarget)))
            {
              SetLocalInt(collisiontarget, "struck", TRUE);
              ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(0, DAMAGE_TYPE_FIRE), collisiontarget);
              ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_FLAME_S), collisiontarget);
              if(GetLocalFloat(collisiontarget, "health") < 0.1)
              {
                ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(FloatToInt(damage/10), DAMAGE_TYPE_FIRE), collisiontarget);
              }
              else
              {
                damage = ((damage/10) - GetLocalInt(collisiontarget, "magicdefence"))*(IntToFloat(100-GetLocalInt(collisiontarget, "fireresist"))/100);
                if(damage > 0.0)
                {
                  SetLocalFloat(collisiontarget, "currenthealth", GetLocalFloat(collisiontarget, "currenthealth")-damage);
                  AssignCommand(collisiontarget, SpeakString(FloatToString(damage, 0, 0)));
                }
              }
            }
            nth++;
            collisiontarget = GetNearestObject(OBJECT_TYPE_ALL, self, nth);
          }
          break;
      }
    }
    
    void greatfireball(object self, object target)
    {
      PlayAnimation(ANIMATION_LOOPING_TALK_PLEADING, 0.4, 1.5);
      SetFacing(VectorToAngle(GetPosition(target)-GetPosition(self)));
      SetCommandable(FALSE, self);
      float speed = 18.05; float upspeed = 18.05;
      vector targetpos = GetPosition(target); vector currentpos = GetPosition(self); currentpos.x += 1.5;
      float x = targetpos.x-currentpos.x; float y = targetpos.y-currentpos.y; float z = targetpos.z-currentpos.z; float truex = x; float truey = y;
      if(x < 0.0)
      {
        x=0-x;
      }
      if(y < 0.0)
      {
        y=0-y;
      }
      if(z < 0.0)
      {
        z=0-z;
      }
      float distance = sqrt( ((currentpos.x-targetpos.x)*(currentpos.x-targetpos.x)) + ((currentpos.y-targetpos.y)*(currentpos.y-targetpos.y)) ); float hangtime = (((upspeed*0.6)+1.5)/9.8)*2;  float maxdistance = speed*hangtime-(0.3*hangtime);
      if(distance > maxdistance)
      {
        distance = maxdistance;
      }
      else
      {
        upspeed = 18.05*divfloat(distance, maxdistance);
      }
      float distanceportion = divfloat(distance, (x+y)); float distancex = truex*distanceportion; float distancey = truey*distanceportion;
      targetpos += Vector(distancex, distancey, 1.5);
      currentpos.z += 1.5;
      if(GetLocalFloat(self, "currentmagicka") < 1.545)
      {
        DelayCommand(1.2, SetCommandable(TRUE, self));
        DelayCommand(1.2, ClearAllActions());
        DelayCommand(1.2, SetLocalInt(self, "busy", FALSE));
        SetLocalFloat(self, "currentmagicka", 0.0);
        return;
      }
      SetLocalFloat(self, "currentmagicka", GetLocalFloat(self, "currentmagicka")-1.545);
      location projloc = Location(GetArea(self), currentpos+AngleToVector(GetFacing(self)), 90.0);
      object projectile = CreateObject(OBJECT_TYPE_PLACEABLE, "dsprojectile", projloc, FALSE);
      SetObjectVisualTransform(projectile, OBJECT_VISUAL_TRANSFORM_TRANSLATE_Z, 1.5);
      DelayCommand(0.8, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_SUNSTRIKE, FALSE, 0.15), projloc));
      DelayCommand(1.0, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_SUNSTRIKE, FALSE, 0.2), projloc));
      DelayCommand(1.2, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_SUNSTRIKE, FALSE, 0.25), projloc));
      DelayCommand(1.6, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_FIREBALL, FALSE, 0.4), projloc));
      DelayCommand(1.6, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_SUNSTRIKE, FALSE, 0.75), projloc));
      DelayCommand(1.5, SetCommandable(TRUE, self));
      DelayCommand(1.6, ClearAllActions());
      DelayCommand(1.85, PlayAnimation(ANIMATION_FIREFORGET_VICTORY1, 1.4));
      DelayCommand(1.9, SetCommandable(FALSE, self));
      DelayCommand(2.3, greatfireproj(self, target, projectile, 3*GetLocalFloat(self, "magicattack"), 0.621*GetLocalFloat(self, "magicattack"), speed, speed, upspeed, targetpos, currentpos, currentpos, projloc, 0.0, 0.0, 1.5));
      DelayCommand(2.9, SetCommandable(TRUE, self));
      DelayCommand(2.9, SetLocalInt(self, "busy", FALSE));
    }
    
    //------------------------------------------------------\
    //                                                       \
    //                                                        \
    //=============Great Combustion==========================  \
    //                                                         /
    //                                                        /
    //-------------------------------------------------------/
    
    void combustiondamage(object self, object target, float damage, location flamelocation)
    {
      object collisiontarget = GetNearestObjectToLocation(OBJECT_TYPE_ALL, flamelocation);
      int nth = 1;
      while(GetIsObjectValid(collisiontarget) && GetDistanceToObject(collisiontarget) <= 2.3)
      {
        if(LineOfSightVector(GetPosition(collisiontarget), GetPositionFromLocation(flamelocation)) && collisiontarget != self)
        {
          ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(0, DAMAGE_TYPE_FIRE), collisiontarget);
          if(GetLocalFloat(collisiontarget, "health") > 0.0)
          {
            float realdamage = (damage - GetLocalInt(collisiontarget, "magicdefence"))*(IntToFloat(100-GetLocalInt(collisiontarget, "fireresist"))/100);
            if(realdamage > 0.0)
            {
              SetLocalFloat(collisiontarget, "currenthealth", GetLocalFloat(collisiontarget, "currenthealth")-realdamage);
              AssignCommand(collisiontarget, SpeakString(FloatToString(realdamage, 0, 0)));
            }
          }
          else
          {
            ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(FloatToInt(damage), DAMAGE_TYPE_FIRE), collisiontarget);
          }
        }
        nth++;
        collisiontarget = GetNearestObjectToLocation(OBJECT_TYPE_ALL, flamelocation, nth);
      }
    }
    
    void greatcombustion(object self, object target)
    {
      PlayAnimation(ANIMATION_LOOPING_TALK_PLEADING, 0.4, 1.5);
      SetFacing(VectorToAngle(GetPosition(target)-GetPosition(self)));
      SetCommandable(FALSE, self);
      if(GetLocalFloat(self, "currentmagicka") < 0.772)
      {
        DelayCommand(1.2, SetCommandable(TRUE, self));
        DelayCommand(1.2, ClearAllActions());
        DelayCommand(1.2, SetLocalInt(self, "busy", FALSE));
        SetLocalFloat(self, "currentmagicka", 0.0);
        return;
      }
      SetLocalFloat(self, "currentmagicka", GetLocalFloat(self, "currentmagicka")-0.772);
      location flamelocation = Location(GetArea(self), GetPosition(self)+AngleToVector(GetFacing(self)), 0.0);
      DelayCommand(1.2, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_FLAME_S, FALSE, 1.7), flamelocation));
      DelayCommand(1.6, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_FIREBALL), flamelocation));
      DelayCommand(1.5, combustiondamage(self, target, 2.4*GetLocalFloat(self, "magicattack"), flamelocation));
      DelayCommand(1.9, SetCommandable(TRUE, self));
      DelayCommand(1.9, SetLocalInt(self, "busy", FALSE));
    }
    
    //------------------------------------------------------\
    //                                                       \
    //                                                        \
    //=============Fire Tempest==============================  \
    //                                                         /
    //                                                        /
    //-------------------------------------------------------/
    
    void firecolumndamage(object self, float damage, location flamelocation)
    {
      object collisiontarget = GetNearestObjectToLocation(OBJECT_TYPE_ALL, flamelocation);
      int nth = 1;
      vector bangarea = GetPositionFromLocation(flamelocation);
      while(GetIsObjectValid(collisiontarget) && GetDistanceBetweenLocations(flamelocation, GetLocation(collisiontarget)) <= 2.5)
      {
        if(LineOfSightVector(GetPosition(collisiontarget), bangarea) && collisiontarget != self)
        {
          ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(0, DAMAGE_TYPE_FIRE), collisiontarget);
          if(GetLocalFloat(collisiontarget, "health") > 0.0)
          {
            float realdamage = (damage - GetLocalInt(collisiontarget, "magicdefence"))*(IntToFloat(100-GetLocalInt(collisiontarget, "fireresist"))/100);
            if(realdamage > 0.0)
            {
              SetLocalFloat(collisiontarget, "currenthealth", GetLocalFloat(collisiontarget, "currenthealth")-realdamage);
              AssignCommand(collisiontarget, SpeakString(FloatToString(realdamage, 0, 0)));
            }
          }
          else
          {
            ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(FloatToInt(damage), DAMAGE_TYPE_FIRE), collisiontarget);
          }
        }
        nth++;
        collisiontarget = GetNearestObjectToLocation(OBJECT_TYPE_ALL, flamelocation, nth);
      }
    }
    
    void createfirecolumns(object self, int columns = 20, int failed = 20)
    {
      vector currentpos = GetPosition(self); vector columnpos = AngleToVector(randomfloat(0.0, 360.0))+currentpos;
      float x = columnpos.x-currentpos.x; float y = columnpos.y-currentpos.y; float truex = x; float truey = y;
      if(x < 0.0)
      {
        x = 0-x;
      }
      if(y < 0.0)
      {
        y = 0-y;
      }
      float distanceportion = divfloat(randomfloat(4.0, 7.0), (x+y)); float distancex = truex*distanceportion; float distancey = truey*distanceportion;
      columnpos = currentpos+Vector(distancex, distancey, 0.0);
      location flamelocation = Location(GetArea(self), columnpos, 0.0);
      if(LineOfSightVector(columnpos, Vector(columnpos.x, columnpos.y, columnpos.z+10.0)) && columns)
      {
        SetLocalFloat(self, "currentmagicka", GetLocalFloat(self, "currentmagicka")-0.07245);
        ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_FLAME_M, FALSE, 1.8), flamelocation);
        flamelocation = Location(GetArea(self), Vector(columnpos.x, columnpos.y, columnpos.z+1.9), 0.0);
        DelayCommand(0.14, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_FLAME_M, FALSE, 1.3), flamelocation));
        flamelocation = Location(GetArea(self), Vector(columnpos.x, columnpos.y, columnpos.z+3.3), 0.0);
        DelayCommand(0.2, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_FLAME_M, FALSE, 0.8), flamelocation));
        flamelocation = Location(GetArea(self), Vector(columnpos.x, columnpos.y, columnpos.z), 0.0);
        DelayCommand(0.25, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_COM_CHUNK_STONE_SMALL), flamelocation));
        firecolumndamage(self, 3.9*GetLocalFloat(self, "magicattack"), flamelocation);
        DelayCommand(randomfloat(0.1, 0.25), createfirecolumns(self, columns-1, failed));
      }
      else if(columns)
      {
        createfirecolumns(self, columns, failed-1);
      }
    
    }
    
    void firetempest(object self)
    {
      PlayAnimation(ANIMATION_LOOPING_GET_LOW, 0.4, 3.4);
      SetCommandable(FALSE, self);
      if(GetLocalFloat(self, "currentmagicka") < 1.449)
      {
        DelayCommand(1.2, SetCommandable(TRUE, self));
        DelayCommand(1.2, ClearAllActions());
        DelayCommand(1.2, SetLocalInt(self, "busy", FALSE));
        SetLocalFloat(self, "currentmagicka", 0.0);
        return;
      }
      DelayCommand(1.4, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_HEAD_FIRE), self));
      DelayCommand(1.4, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_DUR_PROTECTION_GOOD_MINOR), self));
      DelayCommand(2.0, createfirecolumns(self));
      DelayCommand(3.7, SetCommandable(TRUE, self));
      DelayCommand(3.7, SetLocalInt(self, "busy", FALSE));
    
    }
    
    //------------------------------------------------------\
    //                                                       \
    //                                                        \
    //=============Attack====================================  \
    //                                                         /
    //                                                        /
    //-------------------------------------------------------/
    
    void checkattack(object self, object target, int hand, int combo)
    {
      if(combo > 0 && GetDistanceBetween(self, target) < 5.0 && !GetIsDead(self) && !GetIsDead(target))
      {
         attack(self, target, hand, combo-1);
      }
      else
      {
        vector targetpos = AngleToVector(GetFacing(self)-180.0)+GetPosition(self); vector currentpos = GetPosition(self);
        float x = targetpos.x - currentpos.x; float y = targetpos.y - currentpos.y; float truex = x; float truey = y;
        if(x < 0.0)
        {
          x = 0-x;
        }
        if(y < 0.0)
        {
          y = 0-y;
        }
        float distance = 1.4;
        float distanceportion = divfloat(distance, (x+y)); float distancex = truex*distanceportion; float distancey = truey*distanceportion;
        DelayCommand(0.2, ActionMoveToLocation(Location(GetArea(self), targetpos, VectorToAngle(GetPosition(target)-GetPosition(self))), TRUE));
        targetpos = currentpos+Vector(distancex, distancey, 0.0);
        DelayCommand(0.15, SetCommandable(TRUE, self));
        DelayCommand(0.5, SetFacing(VectorToAngle(GetPosition(target)-GetPosition(self))));
        DelayCommand(0.6, SetLocalInt(self, "busy", FALSE));
      }
    }
    
    void swing1(object self, object target, float damage, int damagetype, float weaponlength, float arcsize = 0.0, float currentarc = -120.0)
    {
      if(arcsize < 0.1)
      {
        arcsize = 20.0;
      }
      else
      {
        currentarc += arcsize;
      }
      int hit;
      string resistdamage;
      object collisiontarget = GetNearestObject(OBJECT_TYPE_ALL, self);
      int nth = 1;
      while(GetIsObjectValid(collisiontarget) && GetDistanceToObject(collisiontarget) < 1.0+weaponlength)
      {
        if(conespreadfromangle(self, collisiontarget, arcsize+72.0, currentarc) && !GetIsDead(collisiontarget))
        {
          hit = 1;
          SetLocalInt(collisiontarget, "struck", TRUE);
          ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_COM_BLOOD_SPARK_MEDIUM), collisiontarget);
          if(GetLocalFloat(collisiontarget, "health") < 0.1)
          {
            if(!GetLocalInt(target, "leavescorpse"))
            {
              ExecuteScript("dsmakedestroyable", target);
            }
            ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(FloatToInt(damage), damagetype), collisiontarget);
          }
          else
          {
            if(damagetype == DAMAGE_TYPE_BLUDGEONING)
            {
              resistdamage = "resistblunt";
            }
            else if(damagetype == DAMAGE_TYPE_SLASHING)
            {
              resistdamage = "resistcut";
            }
            else if(damagetype == DAMAGE_TYPE_PIERCING)
            {
              resistdamage = "resistpierce";
            }
            float rdamage = (damage - GetLocalInt(collisiontarget, "defence"))*(IntToFloat(100-GetLocalInt(collisiontarget, resistdamage))/100);
            if(rdamage > 0.0)
            {
              SetLocalFloat(collisiontarget, "currenthealth", GetLocalFloat(collisiontarget, "currenthealth")-rdamage);
              AssignCommand(collisiontarget, SpeakString(FloatToString(rdamage, 0, 0)));
            }
            if(rdamage > 1.0)
            {
              switch(Random(6))
              {
                case 0:
                  PlayVoiceChat(VOICE_CHAT_PAIN1, collisiontarget);
                  break;
                case 1:
                  PlayVoiceChat(VOICE_CHAT_PAIN2, collisiontarget);
                  break;
                case 2:
                  PlayVoiceChat(VOICE_CHAT_PAIN3, collisiontarget);
                  break;
              }
            }
          }
          break;
        }
        nth++;
        collisiontarget = GetNearestObject(OBJECT_TYPE_ALL, self, nth);
      }
      if(currentarc < 85.0 && !hit)
      {
        DelayCommand(0.2, swing1(self, target, damage, damagetype, weaponlength, arcsize*1.15, currentarc));
      }
    }
    
    void attack(object self, object target, int hand, int combo)
    {
      SetCommandable(TRUE, self);
      vector targetpos = GetPosition(target); vector currentpos = GetPosition(self);
      SetFacing(VectorToAngle(targetpos-currentpos));
      float x = targetpos.x - currentpos.x; float y = targetpos.y - currentpos.y; float truex = x; float truey = y;
      if(x < 0.0)
      {
        x = 0-x;
      }
      if(y < 0.0)
      {
        y = 0-y;
      }
      float distance;
      if(GetDistanceToObject(target) < 1.4)
      {
        distance = GetDistanceToObject(target)-0.9;
      }
      else
      {
       distance = 1.4;
      }
      float distanceportion = divfloat(distance, (x+y)); float distancex = truex*distanceportion; float distancey = truey*distanceportion;
      targetpos = currentpos+Vector(distancex, distancey, 0.0);
      ActionMoveToLocation(Location(GetArea(self), targetpos, VectorToAngle(GetPosition(target)-GetPosition(self))), TRUE);
      ActionPlayAnimation(ANIMATION_FIREFORGET_STEAL, 1.45, 1.3);
      SetCommandable(FALSE, self);
      float weapondamage;
      int weapontype;
      float weaponlength;
      object myweapon;
      switch(hand)
      {
        default:
          myweapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, self);
          weaponlength = weaponreach(myweapon);
          weapondamage = weaponmulti(myweapon)*GetLocalFloat(self, "attack");
          weapontype = weapondamagetype(myweapon);
          break;
        case 2:
          myweapon = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, self);
          weaponlength = weaponreach(myweapon);
          weapondamage = weaponmulti(myweapon)*GetLocalFloat(self, "attack");
          weapontype = weapondamagetype(myweapon);
          break;
        case 3:
          myweapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, self);
          weaponlength = weaponreach(myweapon);
          weapondamage = weaponmulti(myweapon)*GetLocalFloat(self, "attack");
          weapontype = weapondamagetype(myweapon);
          myweapon = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, self);
          weapondamage += weaponmulti(myweapon)*GetLocalFloat(self, "attack");
          break;
      }
      DelayCommand(0.6, swing1(self, target, weapondamage, weapontype, weaponlength));
      DelayCommand(1.1, checkattack(self, target, hand, combo));
    }
    
Sign In or Register to comment.