1. Análise da Arquitetura
Na arquitetura nativa e pura do Arcturus Morningstar 5.1.1, a categoria Wired Seletor (Selector) não existe no ecossistema. O motor original (WiredHandler) processa as pilhas estritamente na ordem: Triggers -> Conditions -> Effects. Seletores são uma mecânica mais recente do Habbo oficial.
Como vamos contornar isso com maestria?
Para não forçar você a reescrever o core do WiredHandler.java inteiro do seu emulador, a comunidade adapta "Seletores de Habbos" como Condições (InteractionWiredCondition). Ele vai avaliar o usuário que disparou o trigger e cortar a execução do ciclo se ele não pertencer ao grupo mapeado. Se o seu emulador for um fork modificado que já possui a classe base InteractionWiredSelector, você só precisa trocar o extends no arquivo Java que criei abaixo.
Abaixo está a solução Full Stack para criar esse Wired.
2. Estrutura de Arquivos
[SQL]wired_seletor_grupo.sql[Backend]WiredConditionType.java(Modificação)[Backend]ItemManager.java(Modificação)[Backend]WiredConditionSelectorHabbosGroup.java(Novo)[Frontend]WiredConditionLayoutCode.ts(Modificação)[Frontend]WiredConditionSelectorHabbosGroupView.tsx(Novo)
3. Banco de Dados (SQL)
Execute no MySQL. Ajuste o page_id para a aba correta do seu catálogo onde ficam as Condições (ou Seletores, se você os separou no catálogo).
INSERT INTO `items_base` (`id`, `sprite_id`, `public_name`, `item_name`, `type`, `width`, `length`, `stack_height`, `allow_stack`, `allow_sit`, `allow_lay`, `allow_walk`, `allow_gift`, `allow_trade`, `allow_recycle`, `allow_marketplace_sell`, `allow_inventory_stack`, `interaction_type`, `interaction_modes_count`, `vending_ids`, `multiheight`, `customparams`, `effect_id_male`, `effect_id_female`, `clothing_on_walk`)
VALUES (NULL, 8883, 'Wired Seletor: Habbos no grupo', 'wf_c_selector_habbos_group', 's', 1, 1, 0.5, 1, 0, 0, 0, 1, 1, 0, 0, 1, 'wf_c_selector_habbos_group', 2, '', '', '', 0, 0, '');
INSERT INTO `catalog_items` (`id`, `page_id`, `item_ids`, `catalog_name`, `cost_credits`, `cost_points`, `points_type`, `amount`, `limited_stack`, `limited_sells`, `order_num`, `offer_id`, `song_id`, `extradata`, `badge`, `offer_active`)
VALUES (NULL, 123, (SELECT id FROM items_base WHERE item_name = 'wf_c_selector_habbos_group' LIMIT 1), 'wf_c_selector_habbos_group', 5, 0, 0, 1, 0, 0, 1, -1, 0, '', '', '1');
4. Backend Arcturus (Java)
Passo A: Adicione um ID para nosso "Seletor" no Enum de Condições.
Abra com.eu.habbo.habbohotel.wired.WiredConditionType e adicione ao fim:
// ...
SELECTOR_HABBOS_IN_GROUP(32); // Certifique-se de que o ID 32 está livre no seu emulador
Passo B: Crie a classe de Interação.
Crie o arquivo WiredConditionSelectorHabbosGroup.java no package com.eu.habbo.habbohotel.items.interactions.wired.conditions:
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.wired.WiredConditionType;
import com.eu.habbo.habbohotel.wired.WiredHandler;
import com.eu.habbo.messages.ServerMessage;
import java.sql.ResultSet;
import java.sql.SQLException;
public class WiredConditionSelectorHabbosGroup extends InteractionWiredCondition {
public static final WiredConditionType type = WiredConditionType.SELECTOR_HABBOS_IN_GROUP;
private int groupSource = 0; // 0 = Grupo do Mobi/Quarto, 1 = Grupo do User Ativador
private int limit = 5; // Quantidade máxima (Habbos)
public WiredConditionSelectorHabbosGroup(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredConditionSelectorHabbosGroup(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public WiredConditionType getType() {
return type;
}
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
Habbo habbo = room.getHabbo(roomUnit);
if (habbo == null) return false;
int groupIdToCheck = 0;
if (this.groupSource == 0) {
// Grupo do quarto. No Arcturus, Mobis herdam o guildId do quarto a menos que utilizem custom guilds no extradata
if (room.getGuildId() > 0) {
groupIdToCheck = room.getGuildId();
} else {
return false; // Não há grupo definido na fonte (quarto)
}
} else if (this.groupSource == 1) {
// Verifica o grupo primário ativo do próprio usuário que pisou/ativou
groupIdToCheck = habbo.getHabboStats().guild;
if (groupIdToCheck == 0) return false;
}
// Condição real: O usuário pertence a este grupo específico?
return habbo.getHabboStats().hasGuild(groupIdToCheck);
}
@Override
public boolean saveData(WiredSettings settings) {
if (settings.getIntParams().length < 2) return false;
this.groupSource = settings.getIntParams()[0];
this.limit = settings.getIntParams()[1];
return true;
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
message.appendBoolean(false);
message.appendInt(5); // Limite de itens na interface
message.appendInt(0);
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
message.appendInt(2); // Quantidade de intParams
message.appendInt(this.groupSource);
message.appendInt(this.limit);
message.appendInt(0);
message.appendInt(this.getType().code);
}
@Override
public String getWiredData() {
return WiredHandler.getGsonBuilder().create().toJson(new JsonData(this.groupSource, this.limit));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
String wiredData = set.getString("wired_data");
if (wiredData.startsWith("{")) {
JsonData data = WiredHandler.getGsonBuilder().create().fromJson(wiredData, JsonData.class);
this.groupSource = data.groupSource;
this.limit = data.limit;
}
}
@Override
public void onPickUp() {
this.groupSource = 0;
this.limit = 5;
}
static class JsonData {
int groupSource, limit;
public JsonData(int groupSource, int limit) {
this.groupSource = groupSource;
this.limit = limit;
}
}
}
Passo C: Registre a classe em ItemManager.java.
Adicione no método de mapeamento de interações (loadInteractions):
this.registerInteraction("wf_c_selector_habbos_group", WiredConditionSelectorHabbosGroup.class);
5. Frontend Nitro React (TypeScript)
Mapearemos nossa UI na pasta de Conditions usando o LayoutCode correspondente.
Passo A: Abra src/api/wired/WiredConditionLayoutCode.ts e adicione a referência.
export class WiredConditionLayoutCode {
// ...
public static SELECTOR_HABBOS_IN_GROUP: number = 32; // Igual ao Java
}
Passo B: Crie o componente UI igual ao da imagem.
Salve em src/components/wired/views/conditions/WiredConditionSelectorHabbosGroupView.tsx:
import { FC, useEffect, useState } from 'react';
import { WiredConditionLayoutCode, WiredFurniType } from '../../../../api';
import { Column, Text } from '../../../../common';
import { useWired } from '../../../../hooks';
import { WiredConditionBaseView } from './WiredConditionBaseView';
export const WiredConditionSelectorHabbosGroupView: FC<{}> = props => {
const [groupSource, setGroupSource] = useState<number>(0);
const [limit, setLimit] = useState<number>(5);
const { trigger = null, setIntParams = null } = useWired();
const save = () => setIntParams([groupSource, limit]);
useEffect(() => {
if (!trigger || trigger.intData.length < 2) return;
setGroupSource(trigger.intData[0]);
setLimit(trigger.intData[1]);
}, [trigger]);
return (
<WiredConditionBaseView requiresFurni={WiredFurniType.STUFF_SELECTION_OPTION_NONE} hasSpecialInput={true} save={save}>
<Column gap={1} className="w-100">
<Column gap={1}>
<Text bold>Selecione a fonte do grupo</Text>
<select className="form-select form-select-sm" value={groupSource} onChange={e => setGroupSource(Number(e.target.value))}>
<option value={0}>Grupo do mobi</option>
<option value={1}>Grupo do usuário ativador</option>
</select>
</Column>
<hr className="m-0 bg-dark" />
<Column gap={1}>
<Text bold>Limite a seleção a (Habbos)</Text>
<input
type="number"
className="form-control form-control-sm"
value={limit}
min={1}
max={50}
onChange={e => setLimit(Number(e.target.value))}
/>
</Column>
</Column>
</WiredConditionBaseView>
);
}
Passo C: Registre a renderização do componente.
Geralmente no Switch do arquivo WiredView.tsx ou em WiredConditionBaseView.tsx (a depender do seu fork do Nitro):
import { WiredConditionSelectorHabbosGroupView } from './views/conditions/WiredConditionSelectorHabbosGroupView';
// ... switch (code)
case WiredConditionLayoutCode.SELECTOR_HABBOS_IN_GROUP:
return <WiredConditionSelectorHabbosGroupView />;
Implementação
Execute seu mvn clean install para compilar o Arcturus e um yarn build para recompilar o Nitro. Recomendo utilizar um sprite provisório de Condição para esse item caso não tenha a SWF nativa dos seletores!
0 Comentários