2022-04-30 09:41:17 -04:00
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2022
// www.magicbane.com
2023-08-03 08:27:08 -04:00
package engine.gameManager ;
2022-04-30 09:41:17 -04:00
2023-04-06 20:07:24 -05:00
import engine.Enum ;
import engine.net.DispatchMessage ;
import engine.net.client.msg.chat.ChatSystemMsg ;
import engine.objects.* ;
2023-08-02 19:57:32 -05:00
import org.pmw.tinylog.Logger ;
2023-04-06 20:07:24 -05:00
import java.sql.ResultSet ;
import java.sql.SQLException ;
import java.util.ArrayList ;
2022-04-30 09:41:17 -04:00
import java.util.HashMap ;
2023-04-06 20:07:24 -05:00
import java.util.Random ;
2022-04-30 09:41:17 -04:00
import java.util.concurrent.ThreadLocalRandom ;
/**
* Class contains static methods for data from Magicbane's loot tables
*/
public class LootManager {
2023-04-06 20:07:24 -05:00
//new tables
2023-07-15 09:23:48 -04:00
private static final HashMap < Integer , GenTable > generalItemTables = new HashMap <> ();
private static final HashMap < Integer , ItemTable > itemTables = new HashMap <> ();
private static final HashMap < Integer , ModTypeTable > modTypeTables = new HashMap <> ();
private static final HashMap < Integer , ModTable > modTables = new HashMap <> ();
private LootManager () {
}
2022-04-30 09:41:17 -04:00
// Bootstrap routine to load loot data from database
public static void loadLootData () {
DbManager . LootQueries . LOAD_ALL_LOOTGROUPS ();
DbManager . LootQueries . LOAD_ALL_LOOTTABLES ();
DbManager . LootQueries . LOAD_ALL_MODGROUPS ();
DbManager . LootQueries . LOAD_ALL_MODTABLES ();
}
2023-07-15 09:23:48 -04:00
public static void GenerateMobLoot ( Mob mob , boolean fromDeath ) {
2023-04-06 20:07:24 -05:00
//determine if mob is in hotzone
boolean inHotzone = ZoneManager . inHotZone ( mob . getLoc ());
//get multiplier form config manager
float multiplier = Float . parseFloat ( ConfigManager . MB_NORMAL_DROP_RATE . getValue ());
if ( inHotzone ) {
2023-07-30 11:43:26 -05:00
//if mob is inside hotzone, use the hotzone multiplier from the config instead
2023-04-06 20:07:24 -05:00
multiplier = Float . parseFloat ( ConfigManager . MB_HOTZONE_DROP_RATE . getValue ());
}
//iterate the booty sets
2023-07-30 11:43:26 -05:00
if ( mob . getMobBase (). bootySet != 0 && NPCManager . _bootySetMap . containsKey ( mob . getMobBase (). bootySet ) == true ) {
2023-07-27 23:39:14 -05:00
RunBootySet ( NPCManager . _bootySetMap . get ( mob . getMobBase (). bootySet ), mob , multiplier , inHotzone , fromDeath );
2023-04-07 15:41:40 -05:00
}
2023-07-30 11:43:26 -05:00
if ( mob . bootySet != 0 && NPCManager . _bootySetMap . containsKey ( mob . bootySet ) == true ) {
2023-04-15 11:52:19 -05:00
RunBootySet ( NPCManager . _bootySetMap . get ( mob . bootySet ), mob , multiplier , inHotzone , fromDeath );
2023-04-07 13:09:15 -05:00
}
2023-04-07 12:22:33 -05:00
//lastly, check mobs inventory for godly or disc runes to send a server announcement
2023-07-15 09:23:48 -04:00
if ( ! fromDeath ) {
2023-04-30 12:27:02 -05:00
for ( Item it : mob . getInventory ()) {
ItemBase ib = it . getItemBase ();
if ( ib . isDiscRune () || ib . getName (). toLowerCase (). contains ( "of the gods" )) {
ChatSystemMsg chatMsg = new ChatSystemMsg ( null , mob . getName () + " in " + mob . getParentZone (). getName () + " has found the " + ib . getName () + ". Are you tough enough to take it?" );
chatMsg . setMessageType ( 10 );
chatMsg . setChannel ( Enum . ChatChannelType . SYSTEM . getChannelID ());
DispatchMessage . dispatchMsgToAll ( chatMsg );
}
2023-07-27 19:54:47 -05:00
2023-04-06 20:07:24 -05:00
}
2023-04-07 12:22:33 -05:00
}
}
2023-04-15 11:52:19 -05:00
private static void RunBootySet ( ArrayList < BootySetEntry > entries , Mob mob , float multiplier , boolean inHotzone , boolean fromDeath ) {
2023-07-31 21:28:41 -05:00
if ( fromDeath ) {
DropEquipment ( mob , multiplier );
2023-08-02 20:09:07 -05:00
} else {
for ( BootySetEntry bse : entries ) {
switch ( bse . bootyType ) {
case "GOLD" :
GenerateGoldDrop ( mob , bse , inHotzone );
break ;
case "LOOT" :
GenerateLootDrop ( mob , bse . lootTable , bse . dropChance , multiplier ); //generate normal loot drop
if ( inHotzone ) {
if ( generalItemTables . containsKey ( bse . lootTable + 1 ))
GenerateLootDrop ( mob , bse . lootTable + 1 , bse . dropChance , multiplier ); //generate loot drop from hotzone table
}
break ;
case "ITEM" :
GenerateItemLootDrop ( mob , bse , multiplier );
break ;
}
2023-07-30 19:52:11 -05:00
}
2023-07-31 21:28:41 -05:00
}
2023-07-31 19:20:41 -05:00
}
2023-08-02 19:49:34 -05:00
public static MobLoot getGenTableItem ( int genTableID , Mob mob ) {
2023-07-27 23:10:00 -05:00
if ( genTableID == 0 || mob == null || generalItemTables . containsKey ( genTableID ) == false ) {
return null ;
}
MobLoot outItem ;
2023-07-31 19:20:41 -05:00
int genRoll ;
2023-08-02 19:57:32 -05:00
genRoll = new Random (). nextInt ( 99 ) + 1 ;
2023-07-27 23:10:00 -05:00
GenTableRow selectedRow = generalItemTables . get ( genTableID ). getRowForRange ( genRoll );
if ( selectedRow == null ) {
return null ;
}
int itemTableId = selectedRow . itemTableID ;
//gets the 1-320 roll for this mob
2023-08-02 19:45:18 -05:00
int roll2 = TableRoll ( mob . level );
2023-07-27 23:10:00 -05:00
ItemTableRow tableRow = itemTables . get ( itemTableId ). getRowForRange ( roll2 );
if ( tableRow == null ) {
return null ;
}
int itemUUID = tableRow . cacheID ;
if ( itemUUID == 0 ) {
return null ;
}
if ( ItemBase . getItemBase ( itemUUID ). getType (). ordinal () == Enum . ItemType . RESOURCE . ordinal ()) {
int amount = ThreadLocalRandom . current (). nextInt ( tableRow . maxSpawn - tableRow . minSpawn ) + tableRow . minSpawn ;
return new MobLoot ( mob , ItemBase . getItemBase ( itemUUID ), amount , false );
}
outItem = new MobLoot ( mob , ItemBase . getItemBase ( itemUUID ), false );
Enum . ItemType outType = outItem . getItemBase (). getType ();
if ( outType . ordinal () == Enum . ItemType . WEAPON . ordinal () || outType . ordinal () == Enum . ItemType . ARMOR . ordinal () || outType . ordinal () == Enum . ItemType . JEWELRY . ordinal ()) {
if ( outItem . getItemBase (). isGlass () == false ) {
2023-08-02 19:57:32 -05:00
try {
outItem = GeneratePrefix ( mob , outItem , genTableID , genRoll );
} catch ( Exception e ){
Logger . error ( "Failed to GeneratePrefix for item: " + outItem . getName ());
}
try {
2023-08-02 19:45:18 -05:00
outItem = GenerateSuffix ( mob , outItem , genTableID , genRoll );
2023-08-02 19:57:32 -05:00
} catch ( Exception e ){
Logger . error ( "Failed to GenerateSuffix for item: " + outItem . getName ());
}
2023-07-15 09:23:48 -04:00
}
}
2023-07-27 23:10:00 -05:00
return outItem ;
2023-04-06 20:07:24 -05:00
}
2023-08-02 19:45:18 -05:00
private static MobLoot GeneratePrefix ( Mob mob , MobLoot inItem , int genTableID , int genRoll ){
int prefixChanceRoll = ThreadLocalRandom . current (). nextInt ( 99 ) + 1 ;
double prefixChance = 2 . 057 * mob . level - 28 . 67 ;
if ( prefixChanceRoll < prefixChance ) {
GenTableRow selectedRow = generalItemTables . get ( genTableID ). getRowForRange ( genRoll );
ModTypeTable prefixTable = modTypeTables . get ( selectedRow . pModTable );
int prefixroll = ThreadLocalRandom . current (). nextInt ( 99 ) + 1 ;
if ( modTables . get ( prefixTable . getRowForRange ( prefixroll ). modTableID ) != null ) {
ModTable prefixModTable = modTables . get ( prefixTable . getRowForRange ( prefixroll ). modTableID );
ModTableRow prefixMod = prefixModTable . getRowForRange ( TableRoll ( mob . level ));
if ( prefixMod != null && prefixMod . action . length () > 0 ) {
inItem . setPrefix ( prefixMod . action );
inItem . addPermanentEnchantment ( prefixMod . action , 0 , prefixMod . level , true );
}
}
}
return inItem ;
}
private static MobLoot GenerateSuffix ( Mob mob , MobLoot inItem , int genTableID , int genRoll ){
int suffixChanceRoll = ThreadLocalRandom . current (). nextInt ( 99 ) + 1 ;
double suffixChance = 2 . 057 * mob . level - 28 . 67 ;
if ( suffixChanceRoll < suffixChance ) {
GenTableRow selectedRow = generalItemTables . get ( genTableID ). getRowForRange ( genRoll );
int suffixroll = ThreadLocalRandom . current (). nextInt ( 99 ) + 1 ;
ModTypeTable suffixTable = modTypeTables . get ( selectedRow . sModTable );
if ( modTables . get ( suffixTable . getRowForRange ( suffixroll ). modTableID ) != null ) {
ModTable suffixModTable = modTables . get ( suffixTable . getRowForRange ( suffixroll ). modTableID );
ModTableRow suffixMod = suffixModTable . getRowForRange ( TableRoll ( mob . level ));
if ( suffixMod != null && suffixMod . action . length () > 0 ) {
inItem . setSuffix ( suffixMod . action );
inItem . addPermanentEnchantment ( suffixMod . action , 0 , suffixMod . level , false );
}
}
}
return inItem ;
}
private static int TableRoll ( int mobLevel ){
2023-07-30 13:55:08 -05:00
if ( mobLevel > 65 ){
mobLevel = 65 ;
}
2023-07-30 15:57:01 -05:00
int max = ( int )( 4 . 882 * mobLevel + 127 . 0 );
if ( max > 321 ){
max = 321 ;
2023-07-17 22:25:54 -05:00
}
2023-07-27 19:06:31 -05:00
int min = ( int )( 4 . 469 * mobLevel - 3 . 469 );
2023-07-31 20:42:56 -05:00
//if(isHotzone == true){
// min += mobLevel;
// if(min > 220){
// min = 220;
// }
//}
2023-07-17 22:25:54 -05:00
int roll = ThreadLocalRandom . current (). nextInt ( max - min ) + min ;
return roll ;
}
2023-08-02 19:45:18 -05:00
public static void GenerateGoldDrop ( Mob mob , BootySetEntry bse , Boolean inHotzone ){
int chanceRoll = ThreadLocalRandom . current (). nextInt ( 99 ) + 1 ;
if ( chanceRoll > bse . dropChance ) {
2023-07-30 11:43:26 -05:00
//early exit, failed to hit minimum chance roll OR booty was generated from mob's death
return ;
}
//determine and add gold to mob inventory
2023-08-02 19:45:18 -05:00
int high = bse . highGold ;
int low = bse . lowGold ;
2023-07-30 11:43:26 -05:00
int gold = ThreadLocalRandom . current (). nextInt ( high - low ) + low ;
2023-08-02 19:45:18 -05:00
if ( inHotzone == true ){
gold = ( int )( gold * Float . parseFloat ( ConfigManager . MB_HOTZONE_GOLD_RATE . getValue ()));
} else {
gold = ( int )( gold * Float . parseFloat ( ConfigManager . MB_NORMAL_GOLD_RATE . getValue ()));
}
2023-07-30 11:43:26 -05:00
if ( gold > 0 ) {
2023-08-02 19:45:18 -05:00
MobLoot goldAmount = new MobLoot ( mob , gold );
2023-07-30 11:43:26 -05:00
mob . getCharItemManager (). addItemToInventory ( goldAmount );
}
}
2023-08-02 19:45:18 -05:00
public static void GenerateLootDrop ( Mob mob , int tableID , float dropChance , float multiplier ){
2023-07-30 13:55:08 -05:00
try {
2023-08-02 19:45:18 -05:00
int chanceRoll = ThreadLocalRandom . current (). nextInt ( 99 ) + 1 ;
if ( chanceRoll > dropChance * multiplier ) {
2023-07-30 11:43:26 -05:00
//early exit, failed to hit minimum chance roll
return ;
}
2023-08-02 19:49:34 -05:00
MobLoot toAdd = getGenTableItem ( tableID , mob );
2023-07-30 11:43:26 -05:00
if ( toAdd != null ) {
2023-07-30 13:55:08 -05:00
if ( toAdd . getPrefix () == null && toAdd . getSuffix () == null ){
2023-07-30 11:43:26 -05:00
toAdd . setIsID ( true );
}
mob . getCharItemManager (). addItemToInventory ( toAdd );
}
2023-07-30 13:55:08 -05:00
}
catch ( Exception e ){
//TODO chase down loot generation error, affects roughly 2% of drops
int i = 0 ;
}
2023-07-30 11:43:26 -05:00
}
public static void DropEquipment ( Mob mob , float multiplier ){
//do equipment here
if ( mob . getEquip () != null ) {
for ( MobEquipment me : mob . getEquip (). values ()) {
if ( me . getDropChance () == 0 )
continue ;
float equipmentRoll = ThreadLocalRandom . current (). nextInt ( 101 );
float dropChance = me . getDropChance () * 100 ;
if ( equipmentRoll <= ( dropChance * multiplier )) {
MobLoot ml = new MobLoot ( mob , me . getItemBase (), false );
if ( ml . getPrefix (). isEmpty () == true && ml . getSuffix (). isEmpty () == true ) {
ml . setIsID ( true );
}
mob . getCharItemManager (). addItemToInventory ( ml );
}
}
}
return ;
}
2023-07-30 15:57:01 -05:00
public static void GenerateItemLootDrop ( Mob mob , BootySetEntry bse , float multiplier ){
2023-08-02 19:45:18 -05:00
int chanceRoll = ThreadLocalRandom . current (). nextInt ( 99 ) + 1 ;
2023-07-30 15:57:01 -05:00
if ( chanceRoll > bse . dropChance * multiplier ) {
//early exit, failed to hit minimum chance roll
return ;
}
2023-07-30 11:43:26 -05:00
MobLoot disc = new MobLoot ( mob , ItemBase . getItemBase ( bse . itemBase ), true );
if ( disc != null )
mob . getCharItemManager (). addItemToInventory ( disc );
}
2023-07-15 09:23:48 -04:00
public static void AddGenTableRow ( int tableID , GenTableRow row ) {
if ( ! generalItemTables . containsKey ( tableID )) {
2023-04-06 20:07:24 -05:00
//create the new table
2023-04-06 21:06:23 -05:00
GenTable gt = new GenTable ();
gt . rows . add ( row );
2023-07-15 09:23:48 -04:00
generalItemTables . put ( tableID , gt );
} else {
2023-04-06 20:07:24 -05:00
//add row to existing table
GenTable toAdd = generalItemTables . get ( tableID );
toAdd . rows . add ( row );
}
}
2023-07-15 09:23:48 -04:00
public static void AddItemTableRow ( int tableID , ItemTableRow row ) {
if ( ! itemTables . containsKey ( tableID )) {
2023-04-06 20:07:24 -05:00
//create the new table
2023-04-06 21:06:23 -05:00
ItemTable it = new ItemTable ();
it . rows . add ( row );
2023-07-15 09:23:48 -04:00
itemTables . put ( tableID , it );
} else {
2023-04-06 20:07:24 -05:00
//add row to existing table
ItemTable toAdd = itemTables . get ( tableID );
toAdd . rows . add ( row );
}
}
2023-07-15 09:23:48 -04:00
public static void AddModTypeTableRow ( int tableID , ModTypeTableRow row ) {
if ( ! modTypeTables . containsKey ( tableID )) {
2023-04-06 20:07:24 -05:00
//create the new table
2023-04-06 21:06:23 -05:00
ModTypeTable mtt = new ModTypeTable ();
mtt . rows . add ( row );
2023-07-15 09:23:48 -04:00
modTypeTables . put ( tableID , mtt );
} else {
2023-04-06 20:07:24 -05:00
//add row to existing table
ModTypeTable toAdd = modTypeTables . get ( tableID );
toAdd . rows . add ( row );
}
}
2023-07-15 09:23:48 -04:00
public static void AddModTableRow ( int tableID , ModTableRow row ) {
if ( ! modTables . containsKey ( tableID )) {
2023-04-06 20:07:24 -05:00
//create the new table
2023-04-06 21:06:23 -05:00
ModTable mt = new ModTable ();
mt . rows . add ( row );
2023-07-15 09:23:48 -04:00
modTables . put ( tableID , mt );
} else {
2023-04-06 20:07:24 -05:00
//add row to existing table
ModTable toAdd = modTables . get ( tableID );
toAdd . rows . add ( row );
}
}
2023-07-15 09:23:48 -04:00
public static class GenTable {
2023-04-06 20:07:24 -05:00
public ArrayList < GenTableRow > rows = new ArrayList < GenTableRow > ();
2023-07-15 09:23:48 -04:00
public GenTableRow getRowForRange ( int roll ) {
2023-04-06 20:07:24 -05:00
GenTableRow outRow = null ;
2023-07-15 09:23:48 -04:00
for ( GenTableRow iteration : this . rows ) {
if ( roll >= iteration . minRoll && roll <= iteration . maxRoll ) {
2023-04-06 20:07:24 -05:00
outRow = iteration ;
}
}
return outRow ;
}
}
2023-07-15 09:23:48 -04:00
public static class ItemTable {
2023-04-06 20:07:24 -05:00
public ArrayList < ItemTableRow > rows = new ArrayList < ItemTableRow > ();
2023-07-15 09:23:48 -04:00
public ItemTableRow getRowForRange ( int roll ) {
if ( roll > 320 ) {
2023-04-06 20:07:24 -05:00
roll = 320 ;
}
ItemTableRow outRow = null ;
2023-07-15 09:23:48 -04:00
for ( ItemTableRow iteration : this . rows ) {
if ( roll >= iteration . minRoll && roll <= iteration . maxRoll ) {
2023-04-06 20:07:24 -05:00
outRow = iteration ;
}
}
return outRow ;
}
}
2023-07-15 09:23:48 -04:00
public static class ModTypeTable {
2023-04-06 20:07:24 -05:00
public ArrayList < ModTypeTableRow > rows = new ArrayList < ModTypeTableRow > ();
2023-07-15 09:23:48 -04:00
public ModTypeTableRow getRowForRange ( int roll ) {
2023-04-06 20:07:24 -05:00
ModTypeTableRow outRow = null ;
2023-07-15 09:23:48 -04:00
for ( ModTypeTableRow iteration : this . rows ) {
if ( roll >= iteration . minRoll && roll <= iteration . maxRoll ) {
2023-04-07 15:41:40 -05:00
return iteration ;
2023-04-06 20:07:24 -05:00
}
}
return outRow ;
}
}
2023-07-15 09:23:48 -04:00
public static class ModTable {
2023-04-06 20:07:24 -05:00
public ArrayList < ModTableRow > rows = new ArrayList < ModTableRow > ();
2023-07-15 09:23:48 -04:00
public ModTableRow getRowForRange ( int roll ) {
if ( roll > 320 ) {
2023-04-06 20:07:24 -05:00
roll = 320 ;
}
ModTableRow outRow = null ;
2023-07-15 09:23:48 -04:00
for ( ModTableRow iteration : this . rows ) {
if ( roll >= iteration . minRoll && roll <= iteration . maxRoll ) {
2023-04-06 20:07:24 -05:00
outRow = iteration ;
}
}
return outRow ;
}
}
2023-07-15 09:23:48 -04:00
public static class GenTableRow {
2023-04-06 20:07:24 -05:00
public int minRoll ;
public int maxRoll ;
public int itemTableID ;
public int pModTable ;
public int sModTable ;
2023-07-15 09:23:48 -04:00
2023-04-06 20:07:24 -05:00
public GenTableRow ( ResultSet rs ) throws SQLException {
this . minRoll = rs . getInt ( "minRoll" );
this . maxRoll = rs . getInt ( "maxRoll" );
this . itemTableID = rs . getInt ( "lootTableID" );
this . pModTable = rs . getInt ( "pModTableID" );
this . sModTable = rs . getInt ( "sModTableID" );
}
}
2023-07-15 09:23:48 -04:00
public static class ItemTableRow {
2023-04-06 20:07:24 -05:00
public int minRoll ;
public int maxRoll ;
public int cacheID ;
2023-04-07 15:41:40 -05:00
public int minSpawn ;
public int maxSpawn ;
2023-07-15 09:23:48 -04:00
2023-04-06 20:07:24 -05:00
public ItemTableRow ( ResultSet rs ) throws SQLException {
this . minRoll = rs . getInt ( "minRoll" );
this . maxRoll = rs . getInt ( "maxRoll" );
this . cacheID = rs . getInt ( "itemBaseUUID" );
2023-04-07 15:41:40 -05:00
this . minSpawn = rs . getInt ( "minSpawn" );
this . maxSpawn = rs . getInt ( "maxSpawn" );
2022-04-30 09:41:17 -04:00
2023-04-06 20:07:24 -05:00
}
}
2023-07-15 09:23:48 -04:00
public static class ModTypeTableRow {
2023-04-06 20:07:24 -05:00
public int minRoll ;
public int maxRoll ;
public int modTableID ;
2023-07-15 09:23:48 -04:00
2023-04-06 20:07:24 -05:00
public ModTypeTableRow ( ResultSet rs ) throws SQLException {
this . minRoll = rs . getInt ( "minRoll" );
this . maxRoll = rs . getInt ( "maxRoll" );
this . modTableID = rs . getInt ( "subTableID" );
}
}
2023-07-15 09:23:48 -04:00
public static class ModTableRow {
2023-04-06 20:07:24 -05:00
public int minRoll ;
public int maxRoll ;
public String action ;
public int level ;
2023-07-15 09:23:48 -04:00
2023-04-06 20:07:24 -05:00
public ModTableRow ( ResultSet rs ) throws SQLException {
this . minRoll = rs . getInt ( "minRoll" );
this . maxRoll = rs . getInt ( "maxRoll" );
this . action = rs . getString ( "action" );
this . level = rs . getInt ( "level" );
}
}
2023-04-15 12:21:31 -05:00
}