Kit is built from StartingKitTemplate._kits and looked up by race name, sex and class name like the client's kit map (0x620250), replacing the hard-coded racebaseclass IDs in GetKitIDByRaceClass. Equip slots come from each item's item_eq_slots_or, filled from the last cfg item so the weapon keeps RHELD. dbKitHandler and the ResultSet ctor stay until the removal commit. Parity (scripts/p42.py, dev static_rune_validkit vs cfg): 68/68 headers, 451/451 item sets, 449/451 kits with identical slots. Value changes: - Irekei female Mage kit 3 (182460/185090/29970) was kitNumber 4 in the DB, so the client's kit 3 was refused at creation; now selectable. - Dwarf male Fighter kits 4 and 5 list two boots; the DB put Black Cuffed Boots (185120) in CHEST. They are a FEET item, so they are now skipped with a warning and Black Riding Boots (185090) stay in FEET. - Old Half-Giant male (2019) now finds the Half-Giant male kits by name instead of a missing map entry. Noob gear item set unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
194 lines
6.6 KiB
Java
194 lines
6.6 KiB
Java
|
|
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
|
// Magicbane Emulator Project © 2013 - 2026
|
|
// https://www.magicbane.com
|
|
|
|
|
|
package engine.objects;
|
|
|
|
import engine.gameManager.DbManager;
|
|
import engine.mbEnums;
|
|
import engine.mbEnums.EquipSlotType;
|
|
import engine.mbEnums.ItemContainerType;
|
|
import engine.mbEnums.OwnerType;
|
|
import engine.parsers.objects.ItemTemplate;
|
|
import engine.parsers.objects.RuneTemplate;
|
|
import engine.parsers.objects.wpak.StartingKitTemplate;
|
|
import org.pmw.tinylog.Logger;
|
|
|
|
import java.net.UnknownHostException;
|
|
import java.sql.ResultSet;
|
|
import java.sql.SQLException;
|
|
import java.util.ArrayList;
|
|
import java.util.EnumMap;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
|
|
public class Kit extends AbstractGameObject {
|
|
|
|
public static HashMap<Integer, Boolean> NoobGearIDS = new HashMap<>();
|
|
public static HashMap<Integer, ArrayList<Kit>> RaceClassIDMap = new HashMap<>();
|
|
|
|
// Keyed like the client's kit map (0x620250): race name, sex and class name, not rune ids
|
|
private static final HashMap<String, ArrayList<Kit>> _kitsByRaceClass = new HashMap<>();
|
|
|
|
private final byte kitNumber;
|
|
private final EnumMap<EquipSlotType, Integer> items = new EnumMap<>(EquipSlotType.class);
|
|
|
|
public Kit(StartingKitTemplate template) {
|
|
super();
|
|
|
|
int itemID;
|
|
ItemTemplate itemTemplate;
|
|
EquipSlotType slot;
|
|
|
|
this.kitNumber = (byte) template.kit_number;
|
|
|
|
// The cfg lists the off-hand column before the weapon; fill from the end so the weapon takes RHELD
|
|
for (int i = template.item_ids.size() - 1; i >= 0; i--) {
|
|
itemID = template.item_ids.get(i);
|
|
Kit.NoobGearIDS.put(itemID, true);
|
|
itemTemplate = ItemTemplate._items.get(itemID);
|
|
|
|
if (itemTemplate == null) {
|
|
Logger.warn("Kit " + kitKey(template.race, template.sex.name(), template.base_class) + " #" + this.kitNumber
|
|
+ ": no item template " + itemID);
|
|
continue;
|
|
}
|
|
|
|
slot = freeSlot(itemTemplate);
|
|
|
|
if (slot == null) {
|
|
Logger.warn("Kit " + kitKey(template.race, template.sex.name(), template.base_class) + " #" + this.kitNumber
|
|
+ ": no free slot for item " + itemID);
|
|
continue;
|
|
}
|
|
|
|
this.items.put(slot, itemID);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* RecordSet Constructor
|
|
*/
|
|
public Kit(ResultSet rs) throws SQLException, UnknownHostException {
|
|
super(rs);
|
|
|
|
int raceBaseClassID = rs.getInt("RaceBaseClassesID");
|
|
this.kitNumber = rs.getByte("kitNumber");
|
|
|
|
putColumn(EquipSlotType.LEGS, rs.getInt("legs"));
|
|
putColumn(EquipSlotType.CHEST, rs.getInt("chest"));
|
|
putColumn(EquipSlotType.FEET, rs.getInt("feet"));
|
|
putColumn(EquipSlotType.LHELD, rs.getInt("offhand"));
|
|
putColumn(EquipSlotType.RHELD, rs.getInt("weapon"));
|
|
|
|
if (Kit.RaceClassIDMap.containsKey(raceBaseClassID)) {
|
|
Kit.RaceClassIDMap.get(raceBaseClassID).add(this);
|
|
} else {
|
|
ArrayList<Kit> tempList = new ArrayList<>();
|
|
tempList.add(this);
|
|
Kit.RaceClassIDMap.put(raceBaseClassID, tempList);
|
|
}
|
|
}
|
|
|
|
public static void LoadAllKits() {
|
|
|
|
Kit kit;
|
|
String key;
|
|
|
|
for (StartingKitTemplate template : StartingKitTemplate._kits) {
|
|
kit = new Kit(template);
|
|
key = kitKey(template.race, template.sex.name(), template.base_class);
|
|
_kitsByRaceClass.computeIfAbsent(key, k -> new ArrayList<>()).add(kit);
|
|
}
|
|
|
|
Logger.info("Loaded " + StartingKitTemplate._kits.size() + " kits for " + _kitsByRaceClass.size() + " race/sex/class");
|
|
}
|
|
|
|
public static Kit getKit(int raceRuneID, int baseClassRuneID, int kitNumber) {
|
|
|
|
RuneTemplate raceRune = RuneTemplate._runes.get(raceRuneID);
|
|
RuneTemplate classRune = RuneTemplate._runes.get(baseClassRuneID);
|
|
mbEnums.RaceType raceType = mbEnums.RaceType.getRaceTypebyRuneID(raceRuneID);
|
|
ArrayList<Kit> kits;
|
|
|
|
if (raceRune == null || classRune == null || raceType == null)
|
|
return null;
|
|
|
|
kits = _kitsByRaceClass.get(kitKey(raceRune.obj_name, raceType.getCharacterSex().name(), classRune.obj_name));
|
|
|
|
if (kits == null || kitNumber < 0 || kitNumber >= kits.size())
|
|
return null;
|
|
|
|
return kits.get(kitNumber);
|
|
}
|
|
|
|
public static boolean IsNoobGear(int itemID) {
|
|
|
|
return Kit.NoobGearIDS.containsKey(itemID);
|
|
|
|
}
|
|
|
|
private static String kitKey(String race, String sex, String baseClass) {
|
|
return race + " " + sex + " " + baseClass;
|
|
}
|
|
|
|
private static boolean kitItemCreator(PlayerCharacter pc, int templateID, mbEnums.EquipSlotType slot) {
|
|
|
|
Item item = new Item(templateID);
|
|
item.ownerID = pc.getObjectUUID();
|
|
item.ownerType = OwnerType.PlayerCharacter;
|
|
item.containerType = ItemContainerType.EQUIPPED;
|
|
item.equipSlot = slot;
|
|
|
|
try {
|
|
item = DbManager.ItemQueries.PERSIST(item);
|
|
} catch (Exception e) {
|
|
Logger.error(e);
|
|
}
|
|
|
|
if (item == null) {
|
|
Logger.info("Ungoof this goof, something is wrong with our kit.");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private EquipSlotType freeSlot(ItemTemplate itemTemplate) {
|
|
|
|
for (EquipSlotType slot : itemTemplate.item_eq_slots_or)
|
|
if (!this.items.containsKey(slot))
|
|
return slot;
|
|
|
|
return null;
|
|
}
|
|
|
|
private void putColumn(EquipSlotType slot, int itemID) {
|
|
|
|
if (itemID == 0)
|
|
return;
|
|
|
|
this.items.put(slot, itemID);
|
|
Kit.NoobGearIDS.put(itemID, true);
|
|
}
|
|
|
|
public byte getKitNumber() {
|
|
return kitNumber;
|
|
}
|
|
|
|
public void equipPCwithKit(PlayerCharacter pc) {
|
|
for (Map.Entry<EquipSlotType, Integer> entry : this.items.entrySet())
|
|
kitItemCreator(pc, entry.getValue(), entry.getKey());
|
|
}
|
|
|
|
@Override
|
|
public void updateDatabase() {
|
|
// TODO Create update logic.
|
|
}
|
|
}
|