L2JLisvus

Would you like to react to this message? Create an account in a few clicks or log in to continue.

3 posters

    Valley of Saints AI [COMMITED]

    Karakan
    Karakan


    Posts : 756
    Join date : 2013-10-04

    Valley of Saints AI [COMMITED] Empty Valley of Saints AI [COMMITED]

    Post  Karakan 5th June 2019, 18:07

    Retail like AI for Valley of Saints monsters.

    To do : Find a better way to prevent spawning more then 1 mob (Blade of Splendor, Punishment of Splendor, Wailing of Splendor)

    Code updated with a better implementation.


    Thanks DnR for the "onDecay" tip. That helped a lot  Cool


    ...\ai\group_template\ValleyOfSaints.java

    Code:

    /*
     * This program is free software: you can redistribute it and/or modify it under
     * the terms of the GNU General Public License as published by the Free Software
     * Foundation, either version 3 of the License, or (at your option) any later
     * version.
     *
     * This program is distributed in the hope that it will be useful, but WITHOUT
     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
     * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
     * details.
     *
     * You should have received a copy of the GNU General Public License along with
     * this program. If not, see <http://www.gnu.org/licenses/>.
     */
    package ai.group_template;

    import net.sf.l2j.gameserver.ai.CtrlIntention;
    import net.sf.l2j.gameserver.model.L2Attackable;
    import net.sf.l2j.gameserver.model.L2Object;
    import net.sf.l2j.gameserver.model.L2Skill;
    import net.sf.l2j.gameserver.model.actor.instance.L2NpcInstance;
    import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
    import net.sf.l2j.gameserver.model.quest.Quest;
    import net.sf.l2j.util.Rnd;

    /**
     * VALLEY_OF_SAINTS_AI - By Karakan for L2JLisvus
     * Blade of Splendor (1524) - 30% chance to spawn clone
     * Punishment of Splendor (1531) - 30% chance to spawn clone
     * Wailing of Splendor (1539) - 30% chance to spawn clone
     * Claws of Splendor (1521) - 30% chance to polymorph
     * Anger of Splendor (1527) - 30% chance to polymorph
     * Alliance of Splendor (1533) - 30% chance to polymorph
     * Fang of Splendor (1537) - 30% chance to polymorph
     */
    public class ValleyOfSaints extends Quest
    {
     public ValleyOfSaints (int questId, String name, String descr)
     {
     super(questId, name, descr);
            int[] mobs =
     {
     1524, 1531, 1539, 1521, 1527, 1533, 1537
     };
            registerMobs(mobs);
     }

     @Override
     public String onSkillSee(L2NpcInstance npc, L2PcInstance caster, L2Skill skill, L2Object[] targets, boolean isPet)
     {
     if (skill.isOffensive() && targets[0] == npc)
     {
     if (npc.getNpcId() == 1524)
     {
     if (npc.isScriptValue(0) && Rnd.get(10) < 3)
     {
     L2Attackable newNpc = (L2Attackable) addSpawn (1525, npc.getX()+10, npc.getY()+10, npc.getZ()+10, npc.getHeading(), false, 0);
     newNpc.addDamageHate(caster, 0, 100);
     newNpc.setRunning();
     newNpc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, caster);
     npc.setScriptValue(1);
     }
     }
     else if (npc.getNpcId() == 1531)
     {
     if (npc.isScriptValue(0) && Rnd.get(10) < 3)
     {
     L2Attackable newNpc = (L2Attackable) addSpawn (1658, npc.getX()+10, npc.getY()+10, npc.getZ()+10, npc.getHeading(), false, 0);
     newNpc.addDamageHate(caster, 0, 100);
     newNpc.setRunning();
     newNpc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, caster);
     npc.setScriptValue(1);
     }
     }
     else if (npc.getNpcId() == 1539)
     {
     if (npc.isScriptValue(0) && Rnd.get(10) < 3)
     {
     L2Attackable newNpc = (L2Attackable) addSpawn (1540, npc.getX()+10, npc.getY()+10, npc.getZ()+10, npc.getHeading(), false, 0);
     newNpc.addDamageHate(caster, 0, 100);
     newNpc.setRunning();
     newNpc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, caster);
     npc.setScriptValue(1);
     }
     }
     else if (npc.getNpcId() == 1521)
     {
     if (Rnd.get(10) < 3)
     {
     npc.onDecay();
     L2Attackable newNpc = (L2Attackable) addSpawn (1522, npc.getX()+10, npc.getY()+10, npc.getZ()+10, npc.getHeading(), false, 0);
     newNpc.onAction(caster);
     newNpc.addDamageHate(caster, 0, 100);
     newNpc.setRunning();
     newNpc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, caster);
     }
     }
     else if (npc.getNpcId() == 1527)
     {
     if (Rnd.get(10) < 3)
     {
     npc.onDecay();
     L2Attackable newNpc = (L2Attackable) addSpawn (1528, npc.getX()+10, npc.getY()+10, npc.getZ()+10, npc.getHeading(), false, 0);
     newNpc.onAction(caster);
     newNpc.addDamageHate(caster, 0, 100);
     newNpc.setRunning();
     newNpc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, caster);
     }
     }
     else if (npc.getNpcId() == 1533)
     {
     if (Rnd.get(10) < 3)
     {
     npc.onDecay();
     L2Attackable newNpc = (L2Attackable) addSpawn (1534, npc.getX()+10, npc.getY()+10, npc.getZ()+10, npc.getHeading(), false, 0);
     newNpc.onAction(caster);
     newNpc.addDamageHate(caster, 0, 100);
     newNpc.setRunning();
     newNpc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, caster);
     }
     }
     else if (npc.getNpcId() == 1537)
     {
     if (Rnd.get(10) < 3)
     {
     npc.onDecay();
     L2Attackable newNpc = (L2Attackable) addSpawn (1538, npc.getX()+10, npc.getY()+10, npc.getZ()+10, npc.getHeading(), false, 0);
     newNpc.onAction(caster);
     newNpc.addDamageHate(caster, 0, 100);
     newNpc.setRunning();
     newNpc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, caster);
     }
     }
     }

     return super.onSkillSee(npc, caster, skill, targets, isPet);
     }
     
     @Override
        public String onAttack (L2NpcInstance npc, L2PcInstance attacker, int damage, boolean isPet)
        {
     if (npc.getNpcId() == 1524)
     {
     if (npc.isScriptValue(0) && Rnd.get(10) < 3)
     {
     L2Attackable newNpc = (L2Attackable) addSpawn (1525, npc.getX()+10, npc.getY()+10, npc.getZ()+10, npc.getHeading(), false, 0);
     newNpc.addDamageHate(attacker, 0, 100);
     newNpc.setRunning();
     newNpc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker);
     npc.setScriptValue(1);
     }
     }
     else if (npc.getNpcId() == 1531)
     {
     if (npc.isScriptValue(0) && Rnd.get(10) < 3)
     {
     L2Attackable newNpc = (L2Attackable) addSpawn (1658, npc.getX()+10, npc.getY()+10, npc.getZ()+10, npc.getHeading(), false, 0);
     newNpc.addDamageHate(attacker, 0, 100);
     newNpc.setRunning();
     newNpc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker);
     npc.setScriptValue(1);
     }
     }
     else if (npc.getNpcId() == 1539)
     {
     if (npc.isScriptValue(0) && Rnd.get(10) < 3)
     {
     L2Attackable newNpc = (L2Attackable) addSpawn (1540, npc.getX()+10, npc.getY()+10, npc.getZ()+10, npc.getHeading(), false, 0);
     newNpc.addDamageHate(attacker, 0, 100);
     newNpc.setRunning();
     newNpc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker);
     npc.setScriptValue(1);
     }
     }
     else if (npc.getNpcId() == 1521)
     {
     if (Rnd.get(10) < 3)
     {
     npc.onDecay();
     L2Attackable newNpc = (L2Attackable) addSpawn (1522, npc.getX()+10, npc.getY()+10, npc.getZ()+10, npc.getHeading(), false, 0);
     newNpc.onAction(attacker);
     newNpc.addDamageHate(attacker, 0, 100);
     newNpc.setRunning();
     newNpc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker);
     }
     }
     else if (npc.getNpcId() == 1527)
     {
     if (Rnd.get(10) < 3)
     {
     npc.onDecay();
     L2Attackable newNpc = (L2Attackable) addSpawn (1528, npc.getX()+10, npc.getY()+10, npc.getZ()+10, npc.getHeading(), false, 0);
     newNpc.onAction(attacker);
     newNpc.addDamageHate(attacker, 0, 100);
     newNpc.setRunning();
     newNpc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker);
     }
     }
     else if (npc.getNpcId() == 1533)
     {
     if (Rnd.get(10) < 3)
     {
     npc.onDecay();
     L2Attackable newNpc = (L2Attackable) addSpawn (1534, npc.getX()+10, npc.getY()+10, npc.getZ()+10, npc.getHeading(), false, 0);
     newNpc.onAction(attacker);
     newNpc.addDamageHate(attacker, 0, 100);
     newNpc.setRunning();
     newNpc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker);
     }
     }
     else if (npc.getNpcId() == 1537)
     {
     if (Rnd.get(10) < 3)
     {
     npc.onDecay();
     L2Attackable newNpc = (L2Attackable) addSpawn (1538, npc.getX()+10, npc.getY()+10, npc.getZ()+10, npc.getHeading(), false, 0);
     newNpc.onAction(attacker);
     newNpc.addDamageHate(attacker, 0, 100);
     newNpc.setRunning();
     newNpc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker);
     }
     }

     return super.onAttack(npc, attacker, damage, isPet);
        }

        public static void main(String[] args)
        {
            // Quest class and state definition
            new ValleyOfSaints(-1, "valleyofsaints", "ai/group_template");
        }
    }



    In case DnR decides to commit this...

    Code:
    Index: polymorphing_onAttack.py
    ===================================================================
    --- polymorphing_onAttack.py (revision 598)
    +++ polymorphing_onAttack.py (working copy)
    @@ -16,11 +15,6 @@
                 1267: [1270, 100, 100], #Cave Ant Larva -> Cave Ant Soldier (always polymorphs)
                 1271: [1272, 66, 10], #Cave Ant -> Cave Ant Soldier
                 1272: [1273, 33, 5], #Cave Ant Soldier -> Cave Noble Ant
    -            1521: [1522, 100, 30], #Claws of Splendor -> Claws of Splendor Panther
    -            1524: [1525, 100, 30], #Blade of Splendor -> Blade of Splendor 2nd
    -            1527: [1528, 100, 30], #Anger of Splendor -> Anger of Splendor Panther
    -            1533: [1534, 100, 30], #Alliance of Splendor -> Alliance of Splendor Panther
    -            1537: [1538, 100, 30] #Fang of Splendor -> Fang of Splendor Panther
                 }
             # finally, don't forget to call the parent constructor to prepare the event triggering
             # mechanisms etc.



    Code:

    Index: scripts.cfg
    ===================================================================
    --- scripts.cfg (revision 598)
    +++ scripts.cfg (working copy)
    @@ -15,11 +15,13 @@
     ai/group_template/FrenzyOnAttack.java
     ai/group_template/polymorphing_angel.py
     ai/group_template/SearchingMaster.java
    +ai/group_template/ValleyOfSaints.java
     



    Last edited by Karakan on 12th July 2019, 15:29; edited 3 times in total
    DnR
    DnR
    Admin
    Admin


    Posts : 1475
    Join date : 2012-12-03
    Age : 34

    Valley of Saints AI [COMMITED] Empty Re: Valley of Saints AI [COMMITED]

    Post  DnR 5th June 2019, 18:51

    So, if I get it right, there are cases when instead of polymorphing, attacked spendor spawns a clone of himself.
    If there is such a case, one way would be to use a map for storing clones using original monster's object ID as key. Then, remove the key upon death of either of those monsters. Yet, I do not consider this a good solution.

    I believe the most optimized way to achieve this is to add a special method for NPCs that can do the job and be useable for general use.
    Something like npc.getPartner(), which will return the instance of the clone and also check if a clone already exists.

    I believe adding such a simple method to core is an easy task, so let me take a good look at this.
    Thanks a lot for your nice share.

    Best regards,
    DnR
    Karakan
    Karakan


    Posts : 756
    Join date : 2013-10-04

    Valley of Saints AI [COMMITED] Empty Re: Valley of Saints AI [COMMITED]

    Post  Karakan 5th June 2019, 19:35

    So, if I get it right, there are cases when instead of polymorphing, attacked spendor spawns a clone of himself.

    Jeah the mentioned 3 types (Blade,Punishment,Wailing) do spawn a clone when attacked (30% chance on retail)
    Hp doesnt matter.
    I just added that to prevent a clone spawn after every attack.

    Something like npc.getPartner(), which will return the instance of the clone and also check if a clone already exists.

    I believe adding such a simple method to core is an easy task, so let me take a good look at this.

    That would be great and would bring us one step closer to retail c4.

    Thank You Cool
    Karakan
    Karakan


    Posts : 756
    Join date : 2013-10-04

    Valley of Saints AI [COMMITED] Empty Re: Valley of Saints AI [COMMITED]

    Post  Karakan 13th June 2019, 18:29

    Updated the code.

    Removed HP-condition check. Using "isScriptedValue" instead.

    This works much better and fixes the following issues in AI scripts :

    - chat spam
    - more then one enemy getting spawned on attack



    Minor changes needed in core tho...   jocolor

    L2NpcInstance.java

    Code:

    Index: L2NpcInstance.java
    ===================================================================
    --- L2NpcInstance.java   (revision 614)
    +++ L2NpcInstance.java   (working copy)
    @@ -126,9 +127,14 @@

    +   private int _scriptValue = 0;
    +
        private int _maxSiegeHp = 0;
        
        /** Task launching the function onRandomAnimation() */

    @@ -2642,6 +2675,21 @@
           return false;
        }
        
    +   public int getScriptValue()
    +   {
    +      return _scriptValue;
    +   }
    +  
    +   public void setScriptValue(int val)
    +   {
    +      _scriptValue = val;
    +   }
    +  
    +   public boolean isScriptValue(int val)
    +   {
    +      return _scriptValue == val;
    +   }
    +  
        @Override
        protected final void notifyQuestEventSkillFinished(L2Skill skill, L2Object target)

    DnR
    DnR
    Admin
    Admin


    Posts : 1475
    Join date : 2012-12-03
    Age : 34

    Valley of Saints AI [COMMITED] Empty Re: Valley of Saints AI [COMMITED]

    Post  DnR 17th June 2019, 01:23

    I took the liberty and optimized code structure, achieving the same functionality.
    About check, I added a method called getAltSpawn() which returns the instance of L2Spawn for the desired NPC.
    One last thing is that a reset on altSpawn is required upon mob respawn, or else clones will not spawn until server restarts.

    It's all done in rev616. Thank you for your share. Smile
    xlinkinx
    xlinkinx


    Posts : 244
    Join date : 2012-12-11
    Age : 32
    Location : Russian Federation

    Valley of Saints AI [COMMITED] Empty Re: Valley of Saints AI [COMMITED]

    Post  xlinkinx 24th July 2019, 14:23

    Hi, I understand yours and your time spent, but I decided to propose a variant with the transition to PolymorphingOnAttack.java.

    Why such a solution?
    1. use 2 AI for one action is nothing.
    2. speed up the transition from python to java.
    3. We can get rid of ScriptValue as it works well only in this AI, and for others it creates problems when added.

    PS: I am not saying which is better or worse, I just propose an option.

    I checked it for work, but I cannot say that I’m right as I don’t remember.
    PolymorphingOnAttack.java
    Spoiler:
    Karakan
    Karakan


    Posts : 756
    Join date : 2013-10-04

    Valley of Saints AI [COMMITED] Empty Re: Valley of Saints AI [COMMITED]

    Post  Karakan 24th July 2019, 15:33

    The whole purpose of me doing "area based" AI scripts ,is for better overview and structure.

    There are no problems with ScriptValue at all.
    If you've problems with it, its bad coding.

    Besides :
    It is needed for all AI scripts where we use "shout msgs" or any other ONE-Time checks.

    Some tips for your script:

    - Dont use  "npc.deleteMe();"
    - "onSpellSee" part missing



    Thats all im gonna say about this, since the script me and DnR made is pretty perfect.

    xlinkinx
    xlinkinx


    Posts : 244
    Join date : 2012-12-11
    Age : 32
    Location : Russian Federation

    Valley of Saints AI [COMMITED] Empty Re: Valley of Saints AI [COMMITED]

    Post  xlinkinx 24th July 2019, 16:17

    Karakan wrote:The whole purpose of me doing "area based" AI scripts ,is for better overview and structure.

    There are no problems with ScriptValue at all.
    If you've problems with it, its bad coding.

    Besides :
    It is needed for all AI scripts where we use "shout msgs" or any other ONE-Time checks.

    Some tips for your script:

    - Dont use  "npc.deleteMe();"
    - "onSpellSee" part missing



    Thats all im gonna say about this, since the script me and DnR made is pretty perfect.


    about ScriptValue, the quickest way is the recent AI dion, if you remember, I wrote DnR about the problem when you first started, so this happens because of ScriptValue, without it, the AI starts working right away.

    I will not argue that it is not needed everywhere, but for some AI it is better not to use it for sure.

    This AI can, in fact, be edited for other conditions and exclude the already existing one, so that you can switch from python to Java.

    PS: I only wonder how many tigers are correct? 1 or 2?. (Fallen Orc Shaman -> Sharp Talon Tiger)

    I just did the same as in our AI on python.
    «npc.deleteMe ();»

    Strange, but it works without it, as I understand it is needed when using skills.
    "onSpellSee"

    Sponsored content


    Valley of Saints AI [COMMITED] Empty Re: Valley of Saints AI [COMMITED]

    Post  Sponsored content


      Current date/time is 19th May 2024, 08:25