Merge branch '2009ification' into 'master'

Fixed attack style being wiped on login, propagated custom server name in more places, made login screen tell the truth about credits

Closes #2221

See merge request 2009scape/2009scape!2496
This commit is contained in:
Bishop 2026-08-01 15:22:21 +00:00
commit 852bbc4492
13 changed files with 157 additions and 65 deletions

View file

@ -44,7 +44,7 @@ public final class AutocastSelectPlugin extends ComponentPlugin {
player.removeAttribute("autocast_select");
final WeaponInterface w = player.getExtension(WeaponInterface.class);
if (w != null) {
w.selectAutoSpell(button, true);
w.selectAutoSpell(button, true, false);
player.getInterfaceManager().openTab(w);
}
return true;

View file

@ -0,0 +1,23 @@
package content.global.handlers.iface
import core.ServerConstants
import core.api.setInterfaceText
import core.game.component.Component
import core.game.node.entity.player.Player
import core.game.world.GameWorld
import org.rs09.consts.Components
/**
* Makes sure the server name is always applied to the Friends List interface.
*/
class FriendsListInterface : Component(Components.FRIENDS2_550) {
companion object {
private const val TITLE_ELEMENT = 3
}
override fun open(player: Player) {
super.open(player)
setInterfaceText(player, "Friends List - ${ServerConstants.SERVER_NAME} ${GameWorld.settings!!.worldId}", Components.FRIENDS2_550, TITLE_ELEMENT)
}
}

View file

@ -1,9 +1,11 @@
package content.global.handlers.iface.bank
import core.ServerConstants
import core.api.animate
import core.api.dumpBeastOfBurden
import core.api.runWorldTask
import core.api.sendMessage
import core.api.setInterfaceText
import core.game.component.Component
import core.game.node.entity.player.Player
import org.rs09.consts.Animations
@ -21,6 +23,7 @@ class BankDepositBoxInterface : InterfaceListener {
private const val BUTTON_DEPOSIT_BOB = 13
private const val MENU_ELEMENT = 11
private const val TITLE_ELEMENT = 12
private const val OP_AMOUNT_ONE = 155
private const val OP_AMOUNT_FIVE = 196
private const val OP_AMOUNT_TEN = 124
@ -65,12 +68,14 @@ class BankDepositBoxInterface : InterfaceListener {
}
override fun defineInterfaceListeners() {
onOpen(Components.BANK_DEPOSIT_BOX_11) { player, _ ->
setInterfaceText(player, "The Bank of ${ServerConstants.SERVER_NAME} - Deposit Box", Components.BANK_DEPOSIT_BOX_11, TITLE_ELEMENT)
return@onOpen true
}
on(Components.BANK_DEPOSIT_BOX_11, ::handleDepositBoxMenu)
on(
Components.BANK_DEPOSIT_BOX_11,
BUTTON_DEPOSIT_BOB
) { player, _, _, _, _, _ ->
dumpBeastOfBurden(player); true
on(Components.BANK_DEPOSIT_BOX_11, BUTTON_DEPOSIT_BOB) { player, _, _, _, _, _ ->
dumpBeastOfBurden(player)
return@on true
}
}
}

View file

@ -82,7 +82,7 @@ public class CombatTabInterface extends ComponentPlugin {
if (button == 4 || button == 5) {
inter.openAutocastSelect();
} else if (p.getProperties().getAutocastSpell() != null) {
inter.selectAutoSpell(-1, false);
inter.selectAutoSpell(-1, false, true);
}
return true;
}

View file

@ -54,7 +54,7 @@ class MagicAltarListener : InteractionListener {
}
val weaponInterface = player.getExtension<WeaponInterface>(WeaponInterface::class.java)
if (weaponInterface != null && player.properties.autocastSpell != null) {
weaponInterface.selectAutoSpell(-1, true)
weaponInterface.selectAutoSpell(-1, true, true)
}
if (SpellBook.forInterface(player.spellBookManager.spellBook) == if (altar.id == ANCIENT_ALTAR) SpellBook.ANCIENT else SpellBook.LUNAR) {
player.dispatch(SpellbookChangeEvent(

View file

@ -215,7 +215,7 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)?
}
val weaponInterface = player.getExtension<WeaponInterface>(WeaponInterface::class.java)
if (weaponInterface != null && player.properties.autocastSpell != null) {
weaponInterface.selectAutoSpell(-1, true)
weaponInterface.selectAutoSpell(-1, true, true)
}
player.dispatch(SpellbookChangeEvent(
SpellBookManager.SpellBook.forInterface(player.spellBookManager.spellBook),

View file

@ -2,6 +2,7 @@ package content.region.asgarnia.falador.handlers
import content.global.handlers.iface.ScrollInterface
import content.global.handlers.iface.ScrollLine
import core.ServerConstants
import core.game.interaction.InteractionListener
import org.rs09.consts.Components
import org.rs09.consts.Scenery
@ -23,7 +24,7 @@ class BankNotices : InteractionListener {
)
val CONTENTS_PASSWORD = arrayOf(
ScrollLine("The Bank of RuneScape would like to remind customers that",4),
ScrollLine("The Bank of ${ServerConstants.SERVER_NAME} would like to remind customers that",4),
ScrollLine("they should NEVER tell ANYONE their password.",5),
ScrollLine("If someone asks you to say your password, please report",7),

View file

@ -82,7 +82,7 @@ class EquipHandler : InteractionListener {
if (itemEquipmentSlot == EquipmentContainer.SLOT_WEAPON) {
player.properties.autocastSpell = null
val wif = player.getExtension<WeaponInterface>(WeaponInterface::class.java)
wif.selectAutoSpell(-1, true)
wif.selectAutoSpell(-1, true, true)
wif.openAutocastSelect()
}
}

View file

@ -52,7 +52,7 @@ open class MagicSwingHandler (vararg flags: SwingHandlerFlag)
entity.properties.spell = null
val inter = entity.getExtension<WeaponInterface>(WeaponInterface::class.java)
if (inter != null) {
inter.selectAutoSpell(-1, true)
inter.selectAutoSpell(-1, true, true)
entity.properties.combatPulse.updateStyle()
}
return -1

View file

@ -125,6 +125,16 @@ public final class WeaponInterface extends Component {
*/
public static final int STYLE_CAST = 8;
/**
* Attribute storing the player's autocast spell for reload upon relog.
*/
public static final String ATTRIBUTE_AUTOCAST_BUTTON_ID = "/save:autocast:buttonid";
/**
* Attribute storing the player's autocast combat style for reload upon relog.
*/
public static final String ATTRIBUTE_AUTOCAST_STYLE = "/save:autocast:style";
/**
* The player.
*/
@ -259,7 +269,7 @@ public final class WeaponInterface extends Component {
player.getProperties().setAttackAnimation(attackAnimations[player.getSettings().getAttackStyleIndex()]);
}
if (current != WeaponInterfaces.STAFF) {
selectAutoSpell(-1, false);
selectAutoSpell(-1, false, false);
PacketRepository.send(InterfaceConfig.class, new InterfaceConfigContext(player, id, getConfig(current.getAttackStyles().length, current.getInterfaceId()), !specialBar));
} else { //if staff
PacketRepository.send(InterfaceConfig.class, new InterfaceConfigContext(player, id, 87, !specialBar));
@ -268,7 +278,7 @@ public final class WeaponInterface extends Component {
if (current == WeaponInterfaces.STAFF && player.getAttribute("autocast_select", false)) {
open();
}
selectAutoSpell(-1, true);
selectAutoSpell(-1, true, false);
}
PacketRepository.send(StringPacket.class, new StringContext(player, name, id, 0));
if (player.getSettings().isSpecialToggled()) {
@ -298,6 +308,9 @@ public final class WeaponInterface extends Component {
slot = 3;
}
}
if (current == WeaponInterfaces.STAFF && slot <= 2) {
removeAttributes(player, ATTRIBUTE_AUTOCAST_BUTTON_ID, ATTRIBUTE_AUTOCAST_STYLE);
}
if (slot < 0 || slot >= current.getAttackStyles().length) {
return false;
}
@ -317,7 +330,7 @@ public final class WeaponInterface extends Component {
*/
private void checkStaffConfigs(int slot) {
if (current != WeaponInterfaces.STAFF) {
selectAutoSpell(-1, false);
selectAutoSpell(-1, false, false);
return;
}
boolean defensive = slot == 3;
@ -354,7 +367,9 @@ public final class WeaponInterface extends Component {
* @param buttonId The button id.
* @param adjustAttackStyle If the attack style should be adjusted.
*/
public void selectAutoSpell(int buttonId, boolean adjustAttackStyle) {
public void selectAutoSpell(int buttonId, boolean adjustAttackStyle, boolean forgetAutocast) {
int stdAutocastBookChild = 83;
int defAutocastBookChild = 183;
boolean modern = player.getSpellBookManager().getSpellBook() == Components.MAGIC_192;
int[] data = modern ? MODERN_SPELL_IDS : ANCIENT_SPELL_IDS;
if (modern && player.getEquipment().getNew(3).getName().equalsIgnoreCase("Slayer's staff")) {
@ -376,8 +391,13 @@ public final class WeaponInterface extends Component {
}
}
}
if (forgetAutocast) {
removeAttributes(player, ATTRIBUTE_AUTOCAST_BUTTON_ID, ATTRIBUTE_AUTOCAST_STYLE);
}
if (buttonId < 0) {
player.getProperties().setAutocastSpell(null);
setComponentVisibility(player, Components.WEAPON_STAFF_SEL_90, defAutocastBookChild, false);
setComponentVisibility(player, Components.WEAPON_STAFF_SEL_90, stdAutocastBookChild, false);
if (adjustAttackStyle && current != null) {
setAttackStyle(3);
player.getProperties().getCombatPulse().updateStyle();
@ -385,10 +405,12 @@ public final class WeaponInterface extends Component {
return;
}
boolean defensive = player.getSettings().getAttackStyleIndex() == 3;
player.getPacketDispatch().sendInterfaceConfig(Components.WEAPON_STAFF_SEL_90, 183, defensive);
player.getPacketDispatch().sendInterfaceConfig(Components.WEAPON_STAFF_SEL_90, 83, !defensive);
setComponentVisibility(player, Components.WEAPON_STAFF_SEL_90, defAutocastBookChild, defensive);
setComponentVisibility(player, Components.WEAPON_STAFF_SEL_90, stdAutocastBookChild, !defensive);
current = (CombatSpell) (modern ? SpellBookManager.SpellBook.MODERN.getSpell(data[buttonId]) : SpellBookManager.SpellBook.ANCIENT.getSpell(data[buttonId]));
player.getProperties().setAutocastSpell(current);
setAttribute(player, ATTRIBUTE_AUTOCAST_BUTTON_ID, buttonId);
setAttribute(player, ATTRIBUTE_AUTOCAST_STYLE, player.getSettings().getAttackStyleIndex());
int configId = configStart + (2 * buttonId);
if (modern && player.getEquipment().getNew(3).getName().equalsIgnoreCase("Slayer's staff") || modern && player.getEquipment().getNew(3).getName().equalsIgnoreCase("Void knight mace")) {
boolean slayer = player.getEquipment().getNew(3).getName().equalsIgnoreCase("Slayer's staff");
@ -508,6 +530,37 @@ public final class WeaponInterface extends Component {
return WeaponInterfaces.values()[slot];
}
/**
* Restores the player's autocast settings to what they were in the prior login session.
* Returns false if the player has no autocast to restore, true otherwise.
*/
public boolean restoreAutocast() {
int buttonID = getAttribute(player, ATTRIBUTE_AUTOCAST_BUTTON_ID, -1);
int style = getAttribute(player, ATTRIBUTE_AUTOCAST_STYLE, -1);
if (buttonID < 0 || style < 0 || !canAutocast(false)) {
removeAttributes(player, ATTRIBUTE_AUTOCAST_BUTTON_ID, ATTRIBUTE_AUTOCAST_STYLE);
return false;
}
restoreAttackStyle(style);
checkStaffConfigs(style);
selectAutoSpell(buttonID, false, false);
player.getProperties().getCombatPulse().updateStyle();
return true;
}
/**
* Restores the player's attack style settings to what they were in the prior login session.
*/
public void restoreAttackStyle(int index) {
if (index < 0 || index >= current.getAttackStyles().length) return;
player.getSettings().toggleAttackStyleIndex(index);
player.getProperties().setAttackStyle(current.getAttackStyles()[index]);
if (index < attackAnimations.length && !player.getAppearance().isNpc()) {
player.getProperties().setAttackAnimation(attackAnimations[index]);
}
player.getProperties().getCombatPulse().updateStyle();
}
/**
* Represents an attack style.
* @author Emperor

View file

@ -1,5 +1,7 @@
package core.game.node.entity.player.info.login;
import core.api.Container;
import core.api.EquipmentSlot;
import core.game.component.Component;
import core.game.node.entity.player.Player;
import core.game.node.entity.player.link.emote.Emotes;
@ -11,6 +13,7 @@ import core.net.packet.out.Interface;
import core.plugin.Plugin;
import core.ServerConstants;
import core.game.interaction.InteractionListeners;
import core.game.node.entity.combat.equipment.WeaponInterface;
import content.global.handlers.iface.RulesAndInfo;
import core.tools.Log;
import core.game.world.GameWorld;
@ -103,7 +106,7 @@ public final class LoginConfiguration {
setInterfaceText(player, "", 378, 39);
setInterfaceText(player, "Discord Invite", 378, 14);
setInterfaceText(player, "Discord Invite", 378, 129);
setInterfaceText(player, "You can gain more credits by reporting bugs and various other methods of contribution.", 378, 93);
setInterfaceText(player, "You can gain more credits by testing new features and other methods of contribution.", 378, 93);
setInterfaceText(player, player.getDetails().getCredits() + "", 378, 96);
setInterfaceText(player, "Credits", 378, 94);
setInterfaceText(player, "", 378, 229);
@ -138,15 +141,28 @@ public final class LoginConfiguration {
player.getEmoteManager().unlock(Emotes.SAFETY_FIRST);
}
int savedAttackStyle = player.getSettings().getAttackStyleIndex();
for (Item item : player.getEquipment().toArray()) {
//Run equip hooks for all items equipped on login.
//We should have already been doing this.
//Frankly, I don't even want to imagine the number of bugs us *not* doing this has caused.
if (item == null) continue;
player.getEquipment().remove(item);
if (!InteractionListeners.run(item.getId(), player, item, true) || !player.getEquipment().add(item, true, false)) {
player.sendMessage(colorize("%RAs you can no longer wear " + item.getName() + ", it has been unequipped."));
addItemOrBank(player, item.getId(), item.getAmount());
if (removeItem(player, item, Container.EQUIPMENT)) {
if (!InteractionListeners.run(item.getId(), player, item, true) || !player.getEquipment().add(item, true, false)) {
player.sendMessage(colorize("%RAs you can no longer wear " + item.getName() + ", it has been unequipped."));
addItemOrBank(player, item.getId(), item.getAmount());
if (equipSlot(item.getId()) == EquipmentSlot.WEAPON) {
// Default to accurate style if weapon frisk failed
WeaponInterface inter = player.getExtension(WeaponInterface.class);
inter.restoreAttackStyle(0);
}
} else if (equipSlot(item.getId()) == EquipmentSlot.WEAPON) {
WeaponInterface inter = player.getExtension(WeaponInterface.class);
// Put player's weapon style back to how it was before it was re-equipped
if (!inter.restoreAutocast()) {
inter.restoreAttackStyle(savedAttackStyle);
}
}
}
}

View file

@ -14,6 +14,7 @@ import core.net.packet.out.Interface;
import core.net.packet.out.WindowsPane;
import core.tools.Log;
import org.rs09.consts.Components;
import content.global.handlers.iface.FriendsListInterface;
import content.region.misc.tutisland.handlers.TutorialStage;
import static core.api.ContentAPIKt.log;
@ -352,23 +353,25 @@ public final class InterfaceManager {
if (inter == null) {
player.addExtension(WeaponInterface.class, inter = new WeaponInterface(player));
}
openTab(0, inter);
openTab(i, inter);
break;
case 6:
openTab(6, new Component(player.getSpellBookManager().getSpellBook())); // Magic
openTab(i, new Component(player.getSpellBookManager().getSpellBook())); // Magic
break;
case 7:
if (player.getFamiliarManager().hasFamiliar()) {
openTab(7, new Component(662));
openTab(i, new Component(Components.LORE_STATS_SIDE_662)); // Summoning
}
break;
case 8:
openTab(i, new FriendsListInterface()); // Friends
break;
default:
openTab(i, new Component(DEFAULT_TABS[i]));
}
}
else if (tab.isHidden()) {
int child = (i < 7 ? 38 : 13) + i;
// boolean resize = isResizable(); //TODO:
player.getPacketDispatch().sendInterfaceConfig(getWindowPaneId(), child, false);
player.getPacketDispatch().sendInterfaceConfig(getWindowPaneId(), child + 7, false);
tabs[i].setHidden(false);
@ -380,31 +383,34 @@ public final class InterfaceManager {
* Opens the default tabs.
*/
public void openDefaultTabs() {
// player.getPacketDispatch().sendInterfaceConfig(548, 51, false);
WeaponInterface inter = player.getExtension(WeaponInterface.class);
if (inter == null) {
player.addExtension(WeaponInterface.class, inter = new WeaponInterface(player));
}
//sendTab(16, 747); // Summoning bar
openTab(0, inter); // Attack
openTab(1, new Component(Components.STATS_320)); // Skills
openTab(2, new Component(Components.QUESTJOURNAL_V2_274)); // Quest
openTab(3, new Component(Components.INVENTORY_149)); // inventory
openTab(4, new Component(Components.WORNITEMS_387)); // Equipment
openTab(5, new Component(Components.PRAYER_271)); // Prayer
openTab(6, new Component(player.getSpellBookManager().getSpellBook())); // Magic
if (player.getFamiliarManager().hasFamiliar()) {
openTab(7, new Component(Components.LORE_STATS_SIDE_662)); // summoning.
for (int i = 0; i < tabs.length; i++) {
switch (i) {
case 0:
openTab(i, inter);
break;
case 6:
openTab(i, new Component(player.getSpellBookManager().getSpellBook())); // Magic
break;
case 7:
if (player.getFamiliarManager().hasFamiliar()) {
openTab(i, new Component(Components.LORE_STATS_SIDE_662)); // Summoning
}
break;
case 8:
if (tabs[i] == null) {
openTab(i, new FriendsListInterface()); // Friends
}
break;
default:
openTab(i, new Component(DEFAULT_TABS[i]));
}
}
openTab(8, new Component(Components.FRIENDS2_550)); // Friends
openTab(9, new Component(Components.IGNORE2_551)); // Ignores
openTab(10, new Component(Components.CLANJOIN_589)); // Clan chat
openTab(11, new Component(Components.OPTIONS_261)); // Settings
openTab(12, new Component(Components.EMOTES_464)); // Emotes
openTab(13, new Component(Components.MUSIC_V3_187)); // Music
openTab(14, new Component(Components.LOGOUT_182)); // Logout
if (player.getProperties().getAutocastSpell() != null) {
inter.selectAutoSpell(inter.getAutospellId(player.getProperties().getAutocastSpell().getSpellId()), true);
inter.selectAutoSpell(inter.getAutospellId(player.getProperties().getAutocastSpell().getSpellId()), true, false);
}
}
@ -428,25 +434,13 @@ public final class InterfaceManager {
* Closes the default tabs.
*/
public void closeDefaultTabs() {
WeaponInterface inter = player.getExtension(WeaponInterface.class);
if (inter != null) {
close(inter); // Attack
for (int i = 0; i < tabs.length; i++) {
Component tab = tabs[i];
if (tab != null) {
close(tab);
tabs[i] = null;
}
}
close(new Component(Components.STATS_320)); // Skills
close(new Component(Components.QUESTJOURNAL_V2_274)); // Quest
close(new Component(Components.AREA_TASK_259)); // Diary
close(new Component(Components.INVENTORY_149)); // inventory
close(new Component(Components.WORNITEMS_387)); // Equipment
close(new Component(Components.PRAYER_271)); // Prayer
close(new Component(player.getSpellBookManager().getSpellBook()));
close(new Component(Components.LORE_STATS_SIDE_662)); // summoning.
close(new Component(Components.FRIENDS2_550)); // Friends
close(new Component(Components.IGNORE2_551)); // Ignores
close(new Component(Components.CLANJOIN_589)); // Clan chat
close(new Component(Components.OPTIONS_261)); // Settings
close(new Component(Components.EMOTES_464)); // Emotes
close(new Component(Components.MUSIC_V3_187)); // Music
//close(new Component(Components.LOGOUT_182)); // Logout
}
/**

View file

@ -79,7 +79,7 @@ class SpellbookSwap : PersistTimer(runInterval = minutesToTicks(2), identifier =
// Clear autocast
val weaponInterface = entity.getExtension<WeaponInterface>(WeaponInterface::class.java)
if (weaponInterface != null && entity.properties.autocastSpell != null) {
weaponInterface.selectAutoSpell(-1, true)
weaponInterface.selectAutoSpell(-1, true, true)
}
// Close autocast selection interface if it's still open
if (entity.getAttribute("autocast_select", false)) {