Added attack style warnings and option to remove warned attack styles
This commit is contained in:
@@ -29,7 +29,7 @@ import net.runelite.client.config.ConfigGroup;
|
|||||||
import net.runelite.client.config.ConfigItem;
|
import net.runelite.client.config.ConfigItem;
|
||||||
|
|
||||||
@ConfigGroup(
|
@ConfigGroup(
|
||||||
keyName = "attackindicator",
|
keyName = "attackIndicator",
|
||||||
name = "Attack Indicators",
|
name = "Attack Indicators",
|
||||||
description = "Configuration for the attack indicator plugin"
|
description = "Configuration for the attack indicator plugin"
|
||||||
)
|
)
|
||||||
@@ -37,12 +37,78 @@ public interface AttackIndicatorConfig extends Config
|
|||||||
{
|
{
|
||||||
@ConfigItem(
|
@ConfigItem(
|
||||||
keyName = "enabled",
|
keyName = "enabled",
|
||||||
name = "Enabled",
|
name = "Show attack style",
|
||||||
description = "Configures whether or not the attack indicator overlay is displayed"
|
description = "Configures whether or not the attack indicator overlay is displayed",
|
||||||
|
position = 1
|
||||||
)
|
)
|
||||||
default boolean enabled()
|
default boolean enabled()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "warnForDefensive",
|
||||||
|
name = "Warn for defensive",
|
||||||
|
description = "Configures whether or not users are warned for selecting a defensive combat option",
|
||||||
|
position = 2
|
||||||
|
)
|
||||||
|
default boolean warnForDefensive()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "warnForAttack",
|
||||||
|
name = "Warn for attack",
|
||||||
|
description = "Configures whether or not users are warned for selecting an attack combat option",
|
||||||
|
position = 3
|
||||||
|
)
|
||||||
|
default boolean warnForAttack()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "warnForStrength",
|
||||||
|
name = "Warn for strength",
|
||||||
|
description = "Configures whether or not users are warned for selecting a strength combat option",
|
||||||
|
position = 4
|
||||||
|
)
|
||||||
|
default boolean warnForStrength()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "warnForRanged",
|
||||||
|
name = "Warn for ranged",
|
||||||
|
description = "Configures whether or not users are warned for selecting a ranged combat option",
|
||||||
|
position = 5
|
||||||
|
)
|
||||||
|
default boolean warnForRanged()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "warnForMagic",
|
||||||
|
name = "Warn for magic",
|
||||||
|
description = "Configures whether or not users are warned for selecting a magic combat option",
|
||||||
|
position = 6
|
||||||
|
)
|
||||||
|
default boolean warnForMagic()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "removeWarnedStyles",
|
||||||
|
name = "Remove warned styles",
|
||||||
|
description = "Configures whether or not warned styles are removed from the combat options tab",
|
||||||
|
position = 7
|
||||||
|
)
|
||||||
|
default boolean removeWarnedStyles()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,14 @@ public class AttackIndicatorOverlay extends Overlay
|
|||||||
// Render text with shadow
|
// Render text with shadow
|
||||||
graphics.setColor(Color.BLACK);
|
graphics.setColor(Color.BLACK);
|
||||||
graphics.drawString(attackStyleString, anchor.x - background.width + 3, anchor.y);
|
graphics.drawString(attackStyleString, anchor.x - background.width + 3, anchor.y);
|
||||||
graphics.setColor(Color.WHITE);
|
if (plugin.isWarnedSkillSelected())
|
||||||
|
{
|
||||||
|
graphics.setColor(Color.RED);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
graphics.setColor(Color.WHITE);
|
||||||
|
}
|
||||||
graphics.drawString(attackStyleString, anchor.x - background.width + 2, anchor.y - 1);
|
graphics.drawString(attackStyleString, anchor.x - background.width + 2, anchor.y - 1);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -24,33 +24,52 @@
|
|||||||
*/
|
*/
|
||||||
package net.runelite.client.plugins.attackindicator;
|
package net.runelite.client.plugins.attackindicator;
|
||||||
|
|
||||||
|
import com.google.common.collect.HashBasedTable;
|
||||||
|
import com.google.common.collect.Table;
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
import com.google.inject.Binder;
|
import com.google.inject.Binder;
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import net.runelite.api.Client;
|
import net.runelite.api.Client;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
import net.runelite.api.Varbits;
|
import net.runelite.api.Varbits;
|
||||||
|
import net.runelite.api.widgets.Widget;
|
||||||
|
import net.runelite.api.widgets.WidgetInfo;
|
||||||
import net.runelite.client.config.ConfigManager;
|
import net.runelite.client.config.ConfigManager;
|
||||||
|
import net.runelite.client.events.ConfigChanged;
|
||||||
import net.runelite.client.events.VarbitChanged;
|
import net.runelite.client.events.VarbitChanged;
|
||||||
import net.runelite.client.plugins.Plugin;
|
import net.runelite.client.plugins.Plugin;
|
||||||
import net.runelite.client.plugins.PluginDescriptor;
|
import net.runelite.client.plugins.PluginDescriptor;
|
||||||
import static net.runelite.client.plugins.attackindicator.AttackStyle.CASTING;
|
import static net.runelite.client.plugins.attackindicator.AttackStyle.*;
|
||||||
import static net.runelite.client.plugins.attackindicator.AttackStyle.DEFENSIVE_CASTING;
|
import net.runelite.client.task.Schedule;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@PluginDescriptor(
|
@PluginDescriptor(
|
||||||
name = "Attack indicator plugin"
|
name = "Attack indicator plugin"
|
||||||
)
|
)
|
||||||
public class AttackIndicatorPlugin extends Plugin
|
public class AttackIndicatorPlugin extends Plugin
|
||||||
{
|
{
|
||||||
@Inject
|
private static final Logger logger = LoggerFactory.getLogger(AttackIndicatorPlugin.class);
|
||||||
@Nullable
|
|
||||||
Client client;
|
|
||||||
|
|
||||||
private int attackStyleVarbit = -1;
|
private int attackStyleVarbit = -1;
|
||||||
private int equippedWeaponTypeVarbit = -1;
|
private int equippedWeaponTypeVarbit = -1;
|
||||||
private int castingModeVarbit = -1;
|
private int castingModeVarbit = -1;
|
||||||
private AttackStyle attackStyle;
|
private AttackStyle attackStyle;
|
||||||
|
private final Set<Skill> warnedSkills = new HashSet<>();
|
||||||
|
private boolean warnedSkillSelected = false;
|
||||||
|
private final Table<WeaponType, WidgetInfo, Boolean> widgetsToHide = HashBasedTable.create();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@Nullable
|
||||||
|
Client client;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AttackIndicatorConfig config;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
AttackIndicatorOverlay overlay;
|
AttackIndicatorOverlay overlay;
|
||||||
@@ -78,6 +97,43 @@ public class AttackIndicatorPlugin extends Plugin
|
|||||||
return attackStyle;
|
return attackStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isWarnedSkillSelected()
|
||||||
|
{
|
||||||
|
return warnedSkillSelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void startUp() throws Exception
|
||||||
|
{
|
||||||
|
updateWarnedSkills(config.warnForAttack(), Skill.ATTACK);
|
||||||
|
updateWarnedSkills(config.warnForStrength(), Skill.STRENGTH);
|
||||||
|
updateWarnedSkills(config.warnForDefensive(), Skill.DEFENCE);
|
||||||
|
updateWarnedSkills(config.warnForRanged(), Skill.RANGED);
|
||||||
|
updateWarnedSkills(config.warnForMagic(), Skill.MAGIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Schedule(
|
||||||
|
period = 600,
|
||||||
|
unit = ChronoUnit.MILLIS
|
||||||
|
)
|
||||||
|
public void hideWidgets()
|
||||||
|
{
|
||||||
|
if (widgetsToHide == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WeaponType equippedWeaponType = WeaponType.getWeaponType(equippedWeaponTypeVarbit);
|
||||||
|
|
||||||
|
if (widgetsToHide.containsRow(equippedWeaponType))
|
||||||
|
{
|
||||||
|
for (WidgetInfo widgetKey : widgetsToHide.row(equippedWeaponType).keySet())
|
||||||
|
{
|
||||||
|
hideWidget(client.getWidget(widgetKey), widgetsToHide.get(equippedWeaponType, widgetKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void onAttackStyleChange(VarbitChanged event)
|
public void onAttackStyleChange(VarbitChanged event)
|
||||||
{
|
{
|
||||||
@@ -86,6 +142,7 @@ public class AttackIndicatorPlugin extends Plugin
|
|||||||
attackStyleVarbit = client.getSetting(Varbits.ATTACK_STYLE);
|
attackStyleVarbit = client.getSetting(Varbits.ATTACK_STYLE);
|
||||||
updateAttackStyle(client.getSetting(Varbits.EQUIPPED_WEAPON_TYPE), attackStyleVarbit,
|
updateAttackStyle(client.getSetting(Varbits.EQUIPPED_WEAPON_TYPE), attackStyleVarbit,
|
||||||
client.getSetting(Varbits.DEFENSIVE_CASTING_MODE));
|
client.getSetting(Varbits.DEFENSIVE_CASTING_MODE));
|
||||||
|
updateWarning(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +154,7 @@ public class AttackIndicatorPlugin extends Plugin
|
|||||||
equippedWeaponTypeVarbit = client.getSetting(Varbits.EQUIPPED_WEAPON_TYPE);
|
equippedWeaponTypeVarbit = client.getSetting(Varbits.EQUIPPED_WEAPON_TYPE);
|
||||||
updateAttackStyle(equippedWeaponTypeVarbit, client.getSetting(Varbits.ATTACK_STYLE),
|
updateAttackStyle(equippedWeaponTypeVarbit, client.getSetting(Varbits.ATTACK_STYLE),
|
||||||
client.getSetting(Varbits.DEFENSIVE_CASTING_MODE));
|
client.getSetting(Varbits.DEFENSIVE_CASTING_MODE));
|
||||||
|
updateWarning(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +166,41 @@ public class AttackIndicatorPlugin extends Plugin
|
|||||||
castingModeVarbit = client.getSetting(Varbits.DEFENSIVE_CASTING_MODE);
|
castingModeVarbit = client.getSetting(Varbits.DEFENSIVE_CASTING_MODE);
|
||||||
updateAttackStyle(client.getSetting(Varbits.EQUIPPED_WEAPON_TYPE), client.getSetting(Varbits.ATTACK_STYLE),
|
updateAttackStyle(client.getSetting(Varbits.EQUIPPED_WEAPON_TYPE), client.getSetting(Varbits.ATTACK_STYLE),
|
||||||
castingModeVarbit);
|
castingModeVarbit);
|
||||||
|
updateWarning(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onConfigChanged(ConfigChanged event)
|
||||||
|
{
|
||||||
|
if (event.getGroup().equals("attackIndicator"))
|
||||||
|
{
|
||||||
|
boolean enabled = event.getNewValue().equals("true");
|
||||||
|
switch (event.getKey())
|
||||||
|
{
|
||||||
|
case "enabled":
|
||||||
|
break;
|
||||||
|
case "warnForDefensive":
|
||||||
|
updateWarnedSkills(enabled, Skill.DEFENCE);
|
||||||
|
break;
|
||||||
|
case "warnForAttack":
|
||||||
|
updateWarnedSkills(enabled, Skill.ATTACK);
|
||||||
|
break;
|
||||||
|
case "warnForStrength":
|
||||||
|
updateWarnedSkills(enabled, Skill.STRENGTH);
|
||||||
|
break;
|
||||||
|
case "warnForRanged":
|
||||||
|
updateWarnedSkills(enabled, Skill.RANGED);
|
||||||
|
break;
|
||||||
|
case "warnForMagic":
|
||||||
|
updateWarnedSkills(enabled, Skill.MAGIC);
|
||||||
|
break;
|
||||||
|
case "removeWarnedStyles":
|
||||||
|
hideWarnedStyles(enabled);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger.warn("Unreachable default case for config keys");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,13 +212,116 @@ public class AttackIndicatorPlugin extends Plugin
|
|||||||
attackStyle = attackStyles[attackStyleIndex];
|
attackStyle = attackStyles[attackStyleIndex];
|
||||||
if (attackStyle == null)
|
if (attackStyle == null)
|
||||||
{
|
{
|
||||||
return;
|
attackStyle = OTHER;
|
||||||
}
|
}
|
||||||
if ((attackStyle == CASTING) && (castingMode == 1))
|
else if ((attackStyle == CASTING) && (castingMode == 1))
|
||||||
{
|
{
|
||||||
attackStyle = DEFENSIVE_CASTING;
|
attackStyle = DEFENSIVE_CASTING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateWarnedSkills(boolean enabled, Skill skill)
|
||||||
|
{
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
warnedSkills.remove(skill);
|
||||||
|
warnedSkills.add(skill);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
warnedSkills.remove(skill);
|
||||||
|
}
|
||||||
|
updateWarning(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateWarning(boolean weaponSwitch)
|
||||||
|
{
|
||||||
|
warnedSkillSelected = false;
|
||||||
|
if (attackStyle != null)
|
||||||
|
{
|
||||||
|
for (Skill skill : attackStyle.getSkills())
|
||||||
|
{
|
||||||
|
if (warnedSkills.contains(skill))
|
||||||
|
{
|
||||||
|
if (weaponSwitch)
|
||||||
|
{
|
||||||
|
// TODO : chat message to warn players that their weapon switch also caused an unwanted attack style change
|
||||||
|
}
|
||||||
|
warnedSkillSelected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hideWarnedStyles(config.removeWarnedStyles());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideWarnedStyles(boolean enabled)
|
||||||
|
{
|
||||||
|
WeaponType equippedWeaponType = WeaponType.getWeaponType(equippedWeaponTypeVarbit);
|
||||||
|
if (equippedWeaponType == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AttackStyle[] attackStyles = equippedWeaponType.getAttackStyles();
|
||||||
|
|
||||||
|
// Iterate over attack styles
|
||||||
|
for (int i = 0; i < attackStyles.length; i++)
|
||||||
|
{
|
||||||
|
if (attackStyles[i] == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean warnedSkill = false;
|
||||||
|
for (Skill skill : attackStyles[i].getSkills())
|
||||||
|
{
|
||||||
|
if (warnedSkills.contains(skill))
|
||||||
|
{
|
||||||
|
warnedSkill = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Magic staves defensive casting mode
|
||||||
|
if (equippedWeaponType == WeaponType.TYPE_18)
|
||||||
|
{
|
||||||
|
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_DEFENSIVE_SPELL_BOX, enabled && (warnedSkills.contains(Skill.DEFENCE) || warnedSkill));
|
||||||
|
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_DEFENSIVE_SPELL_ICON, enabled && (warnedSkills.contains(Skill.DEFENCE) || warnedSkill));
|
||||||
|
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_DEFENSIVE_SPELL_SHIELD, enabled && (warnedSkills.contains(Skill.DEFENCE) || warnedSkill));
|
||||||
|
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_DEFENSIVE_SPELL_TEXT, enabled && (warnedSkills.contains(Skill.DEFENCE) || warnedSkill));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove appropriate combat option
|
||||||
|
switch (i)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_STYLE_ONE, enabled && warnedSkill);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_STYLE_TWO, enabled && warnedSkill);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_STYLE_THREE, enabled && warnedSkill);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_STYLE_FOUR, enabled && warnedSkill);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_SPELLS, enabled && warnedSkill);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger.warn("Unreachable default case for equipped weapon type attack styles");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideWidget(Widget widget, boolean hidden)
|
||||||
|
{
|
||||||
|
if (widget != null)
|
||||||
|
{
|
||||||
|
widget.setHidden(hidden);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,27 +24,36 @@
|
|||||||
*/
|
*/
|
||||||
package net.runelite.client.plugins.attackindicator;
|
package net.runelite.client.plugins.attackindicator;
|
||||||
|
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
|
||||||
public enum AttackStyle
|
public enum AttackStyle
|
||||||
{
|
{
|
||||||
ACCURATE("Accurate"),
|
ACCURATE("Accurate", Skill.ATTACK),
|
||||||
AGGRESSIVE("Aggressive"),
|
AGGRESSIVE("Aggressive", Skill.STRENGTH),
|
||||||
DEFENSIVE("Defensive"),
|
DEFENSIVE("Defensive", Skill.DEFENCE),
|
||||||
CONTROLLED("Controlled"),
|
CONTROLLED("Controlled", Skill.ATTACK, Skill.STRENGTH, Skill.DEFENCE),
|
||||||
RANGING("Ranging"),
|
RANGING("Ranging", Skill.RANGED),
|
||||||
LONGRANGE("Longrange"),
|
LONGRANGE("Longrange", Skill.RANGED, Skill.DEFENCE),
|
||||||
CASTING("Casting"),
|
CASTING("Casting", Skill.MAGIC),
|
||||||
DEFENSIVE_CASTING("Defensive Casting"),
|
DEFENSIVE_CASTING("Defensive Casting", Skill.MAGIC, Skill.DEFENCE),
|
||||||
OTHER("Other");
|
OTHER("Other");
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
private final Skill[] skills;
|
||||||
|
|
||||||
AttackStyle(String name)
|
AttackStyle(String name, Skill... skills)
|
||||||
{
|
{
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.skills = skills;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName()
|
public String getName()
|
||||||
{
|
{
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Skill[] getSkills()
|
||||||
|
{
|
||||||
|
return skills;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public enum WeaponType
|
|||||||
TYPE_4(ACCURATE, AGGRESSIVE, CONTROLLED, DEFENSIVE),
|
TYPE_4(ACCURATE, AGGRESSIVE, CONTROLLED, DEFENSIVE),
|
||||||
TYPE_5(RANGING, RANGING, null, LONGRANGE),
|
TYPE_5(RANGING, RANGING, null, LONGRANGE),
|
||||||
TYPE_6(AGGRESSIVE, RANGING, DEFENSIVE_CASTING, null),
|
TYPE_6(AGGRESSIVE, RANGING, DEFENSIVE_CASTING, null),
|
||||||
TYPE_7(RANGING, RANGING, null, DEFENSIVE_CASTING),
|
TYPE_7(RANGING, RANGING, null, LONGRANGE),
|
||||||
TYPE_8(OTHER, AGGRESSIVE, null, null),
|
TYPE_8(OTHER, AGGRESSIVE, null, null),
|
||||||
TYPE_9(ACCURATE, AGGRESSIVE, CONTROLLED, DEFENSIVE),
|
TYPE_9(ACCURATE, AGGRESSIVE, CONTROLLED, DEFENSIVE),
|
||||||
TYPE_10(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE),
|
TYPE_10(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE),
|
||||||
|
|||||||
Reference in New Issue
Block a user