Jump to content

Recommended Posts

Posted

fe88ba27bdcc6aac5dc71a887b7aa915.gif

 

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

 

cb549700b6c421ddf2b1455f686a37ad.png

 

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:

 

  1. We listen in the window AbnormalStatusWnd double click event (OnLButtonDblClick)
  2. Determine the buff that was clicked (via StatusIcon.GetItem)
  3. Determine the ID and skill level of this buff (via GetSkillInfo)
  4. We send a request to the server (via RequestBypassToServer or ExecuteCommand)
  5. 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.

Posted (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 by Kara
Posted
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.

Posted
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.

Posted

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

Posted
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");
}

 

  • 7 months later...
  • 2 weeks later...
  • 2 years later...
Posted

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 ) ) ) ; 
		} 
	} 
}

 

  • 3 months later...
Posted (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 by MonkZe
Posted
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:

fe88ba27bdcc6aac5dc71a887b7aa915.gif

 

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

 

cb549700b6c421ddf2b1455f686a37ad.png

 

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:

 

  1. We listen in the window AbnormalStatusWnd double click event (OnLButtonDblClick)
  2. Determine the buff that was clicked (via StatusIcon.GetItem)
  3. Determine the ID and skill level of this buff (via GetSkillInfo)
  4. We send a request to the server (via RequestBypassToServer or ExecuteCommand)
  5. 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 ; 
	} 
Posted

ALT + click buff removal feature is available as part of the game in higher chronicles (gracia ++) and you cannot remove dances or songs, but ofc you can allow to remove it in IL, its just retail limitation

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Posts

    • do u have the full source code for it  coz the one i have does not have code to read ukx files
    • 🎨 Design & edit without limits: Canva Pro & CapCut Pro subscriptions restocked and active with instant automated delivery!     FINAL CALL: Google is Shutting This Down After October 2026! Google will NEVER bring back this window of opportunity again. Once this month ends, this massive backdoor upgrade is gone permanently!  BUY 2, GET 1 FREE IS STILL LIVE! Stock up now for yourself, your family, your business, and your friends! Think about it: Every single person you know uses a Gmail account, and everyone is running out of storage. Don't let your loved ones get trapped paying $20–$30 every single month when you can set them up with 5TB Cloud Storage + Gemini Advanced (12–18 Months) right now for only $15! Why Buy Bulk Activations Today? Instant Gift Value: Perfect lifetime-style gift for clients, family, or team members. 100% Private & Safe: Activates on personal Gmails—zero passwords or logins required. Global Access: Works worldwide with zero VPN hassles. Instant Automated Delivery: Receive your activation links in seconds 24/7! ‍ Time is running out fast—secure your extra free licenses before the October deadline hits! Order Your Bundle Now on the Website
    • VAELOR Lineage II Interlude · L2OFF · x12   OPEN BETA 9 October 2026 · 17:00 UTC   GRAND OPENING 23 October 2026 · 17:00 UTC   Website: https://l2vaelor.com/ Discord: https://discord.gg/qMgPxH2fd4   ━━━━━━━━━━━━━━━━━━━━━━   WHAT VAELOR AIMS TO BE   Vaelor is an international L2OFF Interlude x12 server built to preserve the original Interlude experience. Combat, classes, crafting, sieges, Olympiad and progression remain close to retail. The changes are limited to the pace and convenience features that make sense on an x12 server.   RATES   EXP and SP: x12 Party EXP and SP: x13.2 Adena: x10 Drop: x5 Spoil: x9 Quest EXP and SP: x3 Quest Adena: x3 Raid Boss Drop: x3   SERVER INFORMATION   Server location: France Main language: English Level cap: 80 Safe enchant: +3, or +4 for full body armor Maximum enchant: +16 Enchant through +15: 70% physical weapons, 40% magic weapons, 66% armor Final step to +16: 35% physical weapons, 20% magic weapons, 66% armor Buff slots: 20 First and second class changes: free Third class change: original quest Game windows: 3   GAMEPLAY   No auto-farm or custom equipment. Level-scaled buffer with presets, custom schemes and support for both players and pets. The available buffs are gradually reduced as the character levels, helping early progression without replacing support classes later in the game. The Global Gatekeeper only connects towns and villages. Travel is free below level 40, then uses level-based Adena fees, with half-price travel between 18:00 and 06:00 UTC. There are no direct teleports to farming zones. The Alt+B Market is a centralized convenience shop containing only consumables and utility items already sold without limits by regular town merchants. Its prices are deliberately higher, so visiting the original NPC merchants remains the economical choice. It does not sell equipment, crafting materials, enchant scrolls or Life Stones. Drop Search finds either an item or a monster and shows where items drop or can be spoiled, including the server rates and the viewing character's level penalty. Player Shop Search and Offline Shop make it easier to find and trade with actual player-run stores while keeping the economy in players' hands. Offline shops remain active for up to 48 hours. Rankings and live Raid Boss and Grand Boss status are available through Alt+B. Character PIN and Computer Lock provide additional account and character protection. Restored and corrected Interlude content includes the Frozen Labyrinth and Ice Queen quest chain, Primeval Isle and Sailren, Wild Beast Reserve, Rainbow Springs Hall, Pagan Temple, and quests around Pavel Ruins and the Plunderous Plains. Auto learn and auto loot are enabled. Castle sieges every two weeks Olympiad daily from 18:00 to 00:00 UTC   PREMIUM AND STORE   Premium grants +15% EXP and SP, +5% Adena, Drop and Spoil, one additional game window and convenience features. The store is limited to Premium, cosmetic accessories and character services. Equipment, enchants, combat stats and access to content are not sold.   ━━━━━━━━━━━━━━━━━━━━━━   If you know Interlude well, come test the server and tell us honestly what feels wrong.   Create account: https://l2vaelor.com/register Download: https://l2vaelor.com/#download Discord: https://discord.gg/qMgPxH2fd4
    • 💎 Unlock Gemini Pro workspace integrations and full 5TB Drive storage today. Verified authentic links delivered instantly!   FINAL CALL: Google is Shutting This Down After October 2026! Google will NEVER bring back this window of opportunity again. Once this month ends, this massive backdoor upgrade is gone permanently!  BUY 2, GET 1 FREE IS STILL LIVE! Stock up now for yourself, your family, your business, and your friends! Think about it: Every single person you know uses a Gmail account, and everyone is running out of storage. Don't let your loved ones get trapped paying $20–$30 every single month when you can set them up with 5TB Cloud Storage + Gemini Advanced (12–18 Months) right now for only $15! Why Buy Bulk Activations Today? Instant Gift Value: Perfect lifetime-style gift for clients, family, or team members. 100% Private & Safe: Activates on personal Gmails—zero passwords or logins required. Global Access: Works worldwide with zero VPN hassles. Instant Automated Delivery: Receive your activation links in seconds 24/7! ‍ Time is running out fast—secure your extra free licenses before the October deadline hits! Order Your Bundle Now on the Website
  • Topics

×
×
  • Create New...

Important Information

This community uses essential cookies to function properly. Non-essential cookies and third-party services are used only with your consent. Read our Privacy Policy and We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue..