Celestine Posted January 29, 2022 Posted January 29, 2022 Being able to remove UD, Snipe, Stealth or CoV is great, especially in Interlude where these skills don't have a penalty enchant. Many people are already used to canceling Chant of Revenge before pressing Mirage or healing by removing Celestial Shield. We will need server sources interface.u sources with compiler Everything you need is freely available. The server part The server in our case is ACIS 36x, but any one will do (perhaps PTS). All that is required is to add a command that will remove the specified buff. To keep the example simple, let's get by with a bypass To remove the buff, more modern clients send a RequestDispel packet , so we'll use the term dispel First, let's add a dispel method to L2Character. Similar to more recent versions of the game, it will not remove effects that should remain after death (for example, penalties), debuffs, dances and songs: // L2Character.java public final void dispelSkillEffect ( int skillId, int skillLevel ) { // Find skill with matching ID and level final L2Skill skill = SkillTable. getInstance ( ) . getInfo ( skillId, skillLevel ) ; // Skill wasn't found and can't be dispelled if ( skill == null ) { return ; } // Penalty-like or debuff skill effect can't be dispelled if ( skill.isStayAfterDeath ( ) || skill. isDebuff ( ) ) { return ; } // Dance-like skill effect can't be dispelled if ( skill. isDance ( ) ) { return ; } // Stop skill effect by ID _effects. stopSkillEffects ( skill. getId ( ) ) ; } Code: Java Now let's add the bypass handling to the runImpl of the RequestBypassToServer network packet that comes from the client. Since the dispelSkillEffect method requires a skill ID and skill level as arguments, the client must pass them as parameters to the dispel command : // RequestBypassToServer.java // Usage: _dispel:<int:skill_id>,<int:skill_level> // Example: _dispel:313,8 else if ( _command. startsWith ( "_dispel" ) ) { // Cut out command params String params = _command. substring ( _command. indexOf ( ":" ) + 1 ) ; // Split params into tokens StringTokenizer st = new StringTokenizer ( params, "," ) ; // Get skill ID from first token intid = Integer . parseInt ( st.nextToken ( ) ) ; _ // Get skill level from second token int level = Integer . parseInt ( st.nextToken ( ) ) ; _ // Dispel skill effect on current character activeChar. dispelSkillEffect ( id, level ) ; } Code: Java Call example: _dispel:313,8 I recommend making a command with similar parameters instead of bypass. In addition, then players will be able to write macros to remove the buff . Client-side Unfortunately, there is no easy way to track Alt + Click in the Interlude client, so we use the usual double-click of the left mouse button to remove the buff . The event will be handled by the AbnormalStatusWnd window , which displays the icons of buffs and debuffs Algorithm: We listen in the window AbnormalStatusWnd double click event (OnLButtonDblClick) Determine the buff that was clicked (via StatusIcon.GetItem) Determine the ID and skill level of this buff (via GetSkillInfo) We send a request to the server (via RequestBypassToServer or ExecuteCommand) We call dispelSkillEffect on the server with the received ID and skill level The double-click event of the left mouse button OnLButtonDblClick receives only the click coordinates as arguments. At the same time, StatusIcon.GetItem requires specifying the row and column of the cell. Accordingly, it is necessary to determine in which row and which column of our buffs the player clicked Since we know that the size of the buff cell is 24 pixels, and the size of the handle for which the window is dragged is 12 pixels, it is easy to calculate the row and cell: it is enough to determine the coordinates of the window with buffs, subtract all values and divide the remainder by the cell size. Values will be rounded correctly when cast to int First, let's add the NSTATUSICON_SIZE constant , which describes the size of the buff cell, to the top of the script. The rest of the developer's constants have already been described: // AbnormalStatusWnd.uc class AbnormalStatusWnd extends UIScript ; const NSTATUSICON_FRAMESIZE = 12 ; const NSTATUSICON_MAXCOL = 12 ; const NSTATUSICON_SIZE = 24 ; // ... Now, anywhere (for example, immediately after the OnEvent function), add the handling of the double click event: // AbnormalStatusWnd.uc function OnLButtonDblClick ( int X , int Y ) { local Rect windowBounds ; local int targetRow ; local int targetCol ; local StatusIconInfo info ; local SkillInfo skillInfo ; // Find window position windowBounds = Me. GetRect ( ) ; // Process clicks outside of window frame only if ( X > ( windowBounds. nX + NSTATUSICON_FRAMESIZE ) ) { // Calc row and col of targeted icon targetRow = ( Y - windowBounds. nY ) / NSTATUSICON_SIZE ; targetCol = ( X - windowBounds. nX - NSTATUSICON_FRAMESIZE ) / NSTATUSICON_SIZE ; // Store status info of targeted icon StatusIcon. GetItem ( targetRow , targetCol , info ) ; // Store actual skill info and make sure it is exists if ( GetSkillInfo ( info. ClassID , info. Level , skillInfo ) ) { // Request server to stop skill effect // Usage: _dispel:<int:skill_id>,<int :skill_level> // Example: _dispel:313,8 RequestBypassToServer ( "_dispel:" $ string ( skillInfo. SkillID ) $ "," $ string ( skillInfo. SkillLevel ) ) ) ; } } } Compile interface.u, copy it to the client, run the game Done! Thanks, to Freelu. 1 1 Quote
Kara Posted January 29, 2022 Posted January 29, 2022 (edited) In case you want in 1 line: public final void dispelSkillEffect(final int skillId, final int skillLevel) { Optional.ofNullable(SkillTable.getInstance().getInfo(skillId, skillLevel)).filter(Objects::nonNull) .filter(Predicate.not(Skill::isStayAfterDeath)).filter(Predicate.not(Skill::isDebuff)).filter(Predicate.not(Skill::isDance)).ifPresent(s -> _effects.stopSkillEffects(s.getId())); } Edited January 29, 2022 by Kara Quote
Celestine Posted January 29, 2022 Author Posted January 29, 2022 19 minutes ago, Kara said: In case you want in 1 line: public final void dispelSkillEffect(final int skillId, final int skillLevel) { Optional.ofNullable(SkillTable.getInstance().getInfo(skillId, skillLevel)).filter(Objects::nonNull) .filter(Predicate.not(Skill::isStayAfterDeath)).filter(Predicate.not(Skill::isDebuff)).filter(Predicate.not(Skill::isDance)).ifPresent(s -> _effects.stopSkillEffects(s.getId())); } works too. Quote
Warcelo Posted January 31, 2022 Posted January 31, 2022 On 1/29/2022 at 3:55 PM, Celestine said: // Find window position windowBounds = Me. GetRect ( ) ; Hi Celestine!, in the case of the "Me" variable, I think it was only necessary to describe to the people that it receives the window object in question that is being manipulated. But great tutorial, congratulations. Quote
Eleven Posted February 3, 2022 Posted February 3, 2022 i managed to add it on aCis 399 but i cannot dipsell the last skill of the buffs raw. all the rest are ok. any clue? 32 minutes ago, Eleven said: i managed to add it on aCis 399 but i cannot dipsell the last skill of the buffs raw. all the rest are ok. any clue? on the last buff on any raw you need to press at the very left edge to dispell it. if you click on the middle of th e buff icon nothing happened. also if you pres to remove the buff before the last in the row, it is removing the last buff. something wrong with positions 49 minutes ago, Eleven said: i managed to add it on aCis 399 but i cannot dipsell the last skill of the buffs raw. all the rest are ok. any clue? on the last buff on any raw you need to press at the very left edge to dispell it. if you click on the middle of th e buff icon nothing happened. also if you pres to remove the buff before the last in the row, it is removing the last buff. something wrong with positions const NSTATUSICON_SIZE = 26 ; this i think must be 26 and not 24. for me now is working perfect Quote
xdem Posted February 3, 2022 Posted February 3, 2022 On 1/29/2022 at 9:24 PM, Kara said: In case you want in 1 line: public final void dispelSkillEffect(final int skillId, final int skillLevel) { Optional.ofNullable(SkillTable.getInstance().getInfo(skillId, skillLevel)).filter(Objects::nonNull) .filter(Predicate.not(Skill::isStayAfterDeath)).filter(Predicate.not(Skill::isDebuff)).filter(Predicate.not(Skill::isDance)).ifPresent(s -> _effects.stopSkillEffects(s.getId())); } fckn retard. this is not oneline and the usage of lambdas is designed for passing interfaces as arguments. ur "one line" is one line as this is one line private void dumbtardOneLiner() { printf("ur retarded\n");printf("ur retarded\n");printf("ur retarded\n");printf("ur retarded\n");printf("ur retarded\n");printf("ur retarded\n"); } 3 Quote
bolsonaro Posted September 20, 2022 Posted September 20, 2022 not working for the last buff of any line Quote
andy1984 Posted October 4, 2022 Posted October 4, 2022 On 9/20/2022 at 5:34 AM, bolsonaro said: not working for the last buff of any line const NSTATUSICON_SIZE = 26 ; must be 26 and not 24. 1 Quote
bolsonaro Posted October 8, 2022 Posted October 8, 2022 On 10/4/2022 at 8:18 AM, andy1984 said: const NSTATUSICON_SIZE = 26 ; must be 26 and not 24. thank you! that solved it Quote
cryptonakos4444 Posted June 5, 2024 Posted June 5, 2024 that's for acis ? On 1/29/2022 at 8:55 PM, Celestine said: // AbnormalStatusWnd.uc function OnLButtonDblClick ( int X , int Y ) { local Rect windowBounds ; local int targetRow ; local int targetCol ; local StatusIconInfo info ; local SkillInfo skillInfo ; // Find window position windowBounds = Me. GetRect ( ) ; // Process clicks outside of window frame only if ( X > ( windowBounds. nX + NSTATUSICON_FRAMESIZE ) ) { // Calc row and col of targeted icon targetRow = ( Y - windowBounds. nY ) / NSTATUSICON_SIZE ; targetCol = ( X - windowBounds. nX - NSTATUSICON_FRAMESIZE ) / NSTATUSICON_SIZE ; // Store status info of targeted icon StatusIcon. GetItem ( targetRow , targetCol , info ) ; // Store actual skill info and make sure it is exists if ( GetSkillInfo ( info. ClassID , info. Level , skillInfo ) ) { // Request server to stop skill effect // Usage: _dispel:<int:skill_id>,<int :skill_level> // Example: _dispel:313,8 RequestBypassToServer ( "_dispel:" $ string ( skillInfo. SkillID ) $ "," $ string ( skillInfo. SkillLevel ) ) ) ; } } } your code have error my friend PLEASE DON'T RIGHT CODES WITH FEETS !!! Quote
SSnakEE Posted November 27, 2024 Posted November 27, 2024 Hello. This code works well. It removes buff with double click, but If you preffer remove buff with ALT + mouse click, place this code in AbnormalStatusWnd.uc function OnLButtonDown(WindowHandle a_WindowHandle, int X, int Y) { local Rect windowBounds; local int targetRow; local int targetCol; local StatusIconInfo info; local SkillInfo skillInfo; if (IsKeyDown(IK_alt) == false) return; // Find window position windowBounds = Me.GetRect(); // Process clicks outside of window frame only if (X > (windowBounds.nX + NSTATUSICON_FRAMESIZE)) { // Calc row and col of targeted icon targetRow = (Y - windowBounds.nY) / NSTATUSICON_SIZE; targetCol = (X - windowBounds.nX - NSTATUSICON_FRAMESIZE) / NSTATUSICON_SIZE; // Store status info of targeted icon StatusIcon.GetItem(targetRow, targetCol, info); // Store actual skill info and make sure it is exists if (GetSkillInfo(info.ClassID, info.Level, skillInfo)) { // Request server to stop skill effect // Usage: _dispel:<int:skill_id>,<int :skill_level> // Example: _dispel:313,8 RequestBypassToServer ( "_dispel:" $ string ( skillInfo. SkillID ) $ "," $ string ( skillInfo. SkillLevel ) ) ) ; } } } Quote
MonkZe Posted March 11 Posted March 11 (edited) On 11/27/2024 at 1:48 PM, SSnakEE said: Hello. This code works well. It removes buff with double click, but If you preffer remove buff with ALT + mouse click, place this code in AbnormalStatusWnd.uc function OnLButtonDown(WindowHandle a_WindowHandle, int X, int Y) { local Rect windowBounds; local int targetRow; local int targetCol; local StatusIconInfo info; local SkillInfo skillInfo; if (IsKeyDown(IK_alt) == false) return; // Find window position windowBounds = Me.GetRect(); // Process clicks outside of window frame only if (X > (windowBounds.nX + NSTATUSICON_FRAMESIZE)) { // Calc row and col of targeted icon targetRow = (Y - windowBounds.nY) / NSTATUSICON_SIZE; targetCol = (X - windowBounds.nX - NSTATUSICON_FRAMESIZE) / NSTATUSICON_SIZE; // Store status info of targeted icon StatusIcon.GetItem(targetRow, targetCol, info); // Store actual skill info and make sure it is exists if (GetSkillInfo(info.ClassID, info.Level, skillInfo)) { // Request server to stop skill effect // Usage: _dispel:<int:skill_id>,<int :skill_level> // Example: _dispel:313,8 RequestBypassToServer ( "_dispel:" $ string ( skillInfo. SkillID ) $ "," $ string ( skillInfo. SkillLevel ) ) ) ; } } } I was unable to edit that block in my interface.u, could u please share the file for ALT + mouse click? Edited March 11 by MonkZe Quote
SSnakEE Posted March 12 Posted March 12 Sure. This is the AbnormalStatusWnd.uc file with ability of removing buff with ALT + click https://pastebin.com/iVWpYysq Quote
MonkZe Posted March 12 Posted March 12 3 hours ago, SSnakEE said: Sure. This is the AbnormalStatusWnd.uc file with ability of removing buff with ALT + click https://pastebin.com/iVWpYysq I ment interface.u file it self not the code. I can view the file and code inside, but I was unable to edit interface.u file to add the code u shared, could u please share the compiled interface.u file it self? 2 hours ago, MonkZe said: I ment interface.u file it self not the code. I can view the file and code inside, but I was unable to edit interface.u file to add the code u shared, could u please share the compiled interface.u file it self? Sorry my bad, I was compiling incorrectly, I looked up for correct compiler, works nicely! Both, Celestine and your version, works nicely! Ty On 1/29/2022 at 6:55 PM, Celestine said: Being able to remove UD, Snipe, Stealth or CoV is great, especially in Interlude where these skills don't have a penalty enchant. Many people are already used to canceling Chant of Revenge before pressing Mirage or healing by removing Celestial Shield. We will need server sources interface.u sources with compiler Everything you need is freely available. The server part The server in our case is ACIS 36x, but any one will do (perhaps PTS). All that is required is to add a command that will remove the specified buff. To keep the example simple, let's get by with a bypass To remove the buff, more modern clients send a RequestDispel packet , so we'll use the term dispel First, let's add a dispel method to L2Character. Similar to more recent versions of the game, it will not remove effects that should remain after death (for example, penalties), debuffs, dances and songs: // L2Character.java public final void dispelSkillEffect ( int skillId, int skillLevel ) { // Find skill with matching ID and level final L2Skill skill = SkillTable. getInstance ( ) . getInfo ( skillId, skillLevel ) ; // Skill wasn't found and can't be dispelled if ( skill == null ) { return ; } // Penalty-like or debuff skill effect can't be dispelled if ( skill.isStayAfterDeath ( ) || skill. isDebuff ( ) ) { return ; } // Dance-like skill effect can't be dispelled if ( skill. isDance ( ) ) { return ; } // Stop skill effect by ID _effects. stopSkillEffects ( skill. getId ( ) ) ; } Code: Java Now let's add the bypass handling to the runImpl of the RequestBypassToServer network packet that comes from the client. Since the dispelSkillEffect method requires a skill ID and skill level as arguments, the client must pass them as parameters to the dispel command : // RequestBypassToServer.java // Usage: _dispel:<int:skill_id>,<int:skill_level> // Example: _dispel:313,8 else if ( _command. startsWith ( "_dispel" ) ) { // Cut out command params String params = _command. substring ( _command. indexOf ( ":" ) + 1 ) ; // Split params into tokens StringTokenizer st = new StringTokenizer ( params, "," ) ; // Get skill ID from first token intid = Integer . parseInt ( st.nextToken ( ) ) ; _ // Get skill level from second token int level = Integer . parseInt ( st.nextToken ( ) ) ; _ // Dispel skill effect on current character activeChar. dispelSkillEffect ( id, level ) ; } Code: Java Call example: _dispel:313,8 I recommend making a command with similar parameters instead of bypass. In addition, then players will be able to write macros to remove the buff . Client-side Unfortunately, there is no easy way to track Alt + Click in the Interlude client, so we use the usual double-click of the left mouse button to remove the buff . The event will be handled by the AbnormalStatusWnd window , which displays the icons of buffs and debuffs Algorithm: We listen in the window AbnormalStatusWnd double click event (OnLButtonDblClick) Determine the buff that was clicked (via StatusIcon.GetItem) Determine the ID and skill level of this buff (via GetSkillInfo) We send a request to the server (via RequestBypassToServer or ExecuteCommand) We call dispelSkillEffect on the server with the received ID and skill level The double-click event of the left mouse button OnLButtonDblClick receives only the click coordinates as arguments. At the same time, StatusIcon.GetItem requires specifying the row and column of the cell. Accordingly, it is necessary to determine in which row and which column of our buffs the player clicked Since we know that the size of the buff cell is 24 pixels, and the size of the handle for which the window is dragged is 12 pixels, it is easy to calculate the row and cell: it is enough to determine the coordinates of the window with buffs, subtract all values and divide the remainder by the cell size. Values will be rounded correctly when cast to int First, let's add the NSTATUSICON_SIZE constant , which describes the size of the buff cell, to the top of the script. The rest of the developer's constants have already been described: // AbnormalStatusWnd.uc class AbnormalStatusWnd extends UIScript ; const NSTATUSICON_FRAMESIZE = 12 ; const NSTATUSICON_MAXCOL = 12 ; const NSTATUSICON_SIZE = 24 ; // ... Now, anywhere (for example, immediately after the OnEvent function), add the handling of the double click event: // AbnormalStatusWnd.uc function OnLButtonDblClick ( int X , int Y ) { local Rect windowBounds ; local int targetRow ; local int targetCol ; local StatusIconInfo info ; local SkillInfo skillInfo ; // Find window position windowBounds = Me. GetRect ( ) ; // Process clicks outside of window frame only if ( X > ( windowBounds. nX + NSTATUSICON_FRAMESIZE ) ) { // Calc row and col of targeted icon targetRow = ( Y - windowBounds. nY ) / NSTATUSICON_SIZE ; targetCol = ( X - windowBounds. nX - NSTATUSICON_FRAMESIZE ) / NSTATUSICON_SIZE ; // Store status info of targeted icon StatusIcon. GetItem ( targetRow , targetCol , info ) ; // Store actual skill info and make sure it is exists if ( GetSkillInfo ( info. ClassID , info. Level , skillInfo ) ) { // Request server to stop skill effect // Usage: _dispel:<int:skill_id>,<int :skill_level> // Example: _dispel:313,8 RequestBypassToServer ( "_dispel:" $ string ( skillInfo. SkillID ) $ "," $ string ( skillInfo. SkillLevel ) ) ) ; } } } Compile interface.u, copy it to the client, run the game Done! Thanks, to Freelu. Question: what's the reason for disallowing songs/dances to be removed? I'm asking as some1 who has full buff npc buffer, with available option to throw on and off any buffs any time, would this block still makes usage? // Dance-like skill effect can't be dispelled if ( skill. isDance ( ) ) { return ; } Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.