Implements autoloot support for Mercenary (#5594)

* Fixed #5570.
* Adds the ability for mercenary to trigger autoloot.
* Adds idle sharing and idle option configs for granular settings.
Thanks to @saya9200's suggestion and @Lemongrass3110!
Co-authored-by: Lemongrass3110 <lemongrass@kstp.at>
This commit is contained in:
Aleos 2021-02-27 17:14:10 -05:00 committed by GitHub
parent 0e9308cba6
commit 2b94f0aeff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 97 additions and 9 deletions

View File

@ -153,3 +153,35 @@ rare_drop_announce: 0
// If RENEWAL_DROP, Bubble Gum, or any other modifiers are active autoloot
// will take them into account.
autoloot_adjust: 0
// Does autoloot work when a monster is killed by mercenary only?
mercenary_autoloot: no
// Is getting items from a mercenary disabled when their master's idle?
// Set to no, or the amount of seconds (NOT milliseconds) that need to pass before considering
// a character idle.
// Characters in a chat/vending are always considered idle.
// A character's idle status is reset upon item use/skill use/attack (auto attack counts too)/movement.
// Their master will only receive items if 'mercenary_autoloot' is activated,
// otherwise they will be dropped on the ground as usual.
// NOTE: This option uses a special timer to track idle time, separated from the normal idle timer.
mer_idle_no_share: no
// How the server should measure the mercenary master's idle time? (Note 3)
// (This will only work if 'mer_idle_no_share' is enabled).
// 0x001 - Walk Request
// 0x002 - UseSkillToID Request (Targetted skill use attempt)
// 0x004 - UseSkillToPos Request (AoE skill use attempt)
// 0x008 - UseItem Request (Including equip/unequip)
// 0x010 - Attack Request
// 0x020 - Chat Request (Whisper, Party, Guild, Battlegrounds, etc)
// 0x040 - Sit/Standup Request
// 0x080 - Emotion Request
// 0x100 - DropItem Request
// 0x200 - @/#Command Request
// Please note that at least 1 option has to be enabled.
// Be mindful that the more options used, the easier it becomes to cheat this features.
// Default: walk (0x1) + useskilltoid (0x2) + useskilltopos (0x4) + useitem (0x8) + attack (0x10) = 0x1F
// NOTE: This allows you to configure different settings for mercenary, separated from normal idle timer and 'idletime_option'.
// It will only apply to mercenary-only kills and it will not affect normal autoloot and party share options.
idletime_mer_option: 0x1F

View File

@ -3939,6 +3939,14 @@ This will only work if 'hom_idle_no_share' and 'idletime_hom_option' are enabled
---------------------------------------
*checkidlemer({"<Player Name>"})
Returns the time, in seconds, that the specified player has been idle for mercenary item share.
Name is optional, and defaults to the attached player if omitted.
This will only work if 'mer_idle_no_share' and 'idletime_mer_option' are enabled (see '/conf/battle/drops.conf').
---------------------------------------
*agitcheck()
*agitcheck2()
*agitcheck3()

View File

@ -10860,6 +10860,8 @@ bool is_atcommand(const int fd, struct map_session_data* sd, const char* message
sd->idletime = last_tick;
if (battle_config.hom_idle_no_share && sd->hd && battle_config.idletime_hom_option&IDLE_ATCOMMAND)
sd->idletime_hom = last_tick;
if (battle_config.mer_idle_no_share && sd->md && battle_config.idletime_mer_option&IDLE_ATCOMMAND)
sd->idletime_mer = last_tick;
//Clearing these to be used once more.
memset(command, '\0', sizeof(command));

View File

@ -9125,6 +9125,9 @@ static const struct _battle_data {
{ "homunculus_starving_delay", &battle_config.homunculus_starving_delay, 20000, 0, INT_MAX, },
{ "drop_connection_on_quit", &battle_config.drop_connection_on_quit, 0, 0, 1, },
{ "mob_spawn_variance", &battle_config.mob_spawn_variance, 1, 0, 3, },
{ "mercenary_autoloot", &battle_config.mercenary_autoloot, 0, 0, 1, },
{ "mer_idle_no_share" , &battle_config.mer_idle_no_share, 0, 0, INT_MAX, },
{ "idletime_mer_option", &battle_config.idletime_mer_option, 0x1F, 0x1, 0xFFF, },
#include "../custom/battle_config_init.inc"
};

View File

@ -689,6 +689,9 @@ struct Battle_Config
int homunculus_starving_delay;
int drop_connection_on_quit;
int mob_spawn_variance;
int mercenary_autoloot;
int mer_idle_no_share;
int idletime_mer_option;
#include "../custom/battle_config_struct.inc"
};

View File

@ -10336,6 +10336,8 @@ static bool clif_process_message(struct map_session_data* sd, bool whisperFormat
sd->idletime = last_tick;
if (battle_config.hom_idle_no_share && sd->hd && battle_config.idletime_hom_option&IDLE_CHAT)
sd->idletime_hom = last_tick;
if (battle_config.mer_idle_no_share && sd->md && battle_config.idletime_mer_option&IDLE_CHAT)
sd->idletime_mer = last_tick;
//achievement_update_objective(sd, AG_CHATTING, 1, 1); // !TODO: Confirm how this achievement is triggered
@ -11150,6 +11152,8 @@ void clif_parse_WalkToXY(int fd, struct map_session_data *sd)
sd->idletime = last_tick;
if (battle_config.hom_idle_no_share && sd->hd && battle_config.idletime_hom_option&IDLE_WALK)
sd->idletime_hom = last_tick;
if (battle_config.mer_idle_no_share && sd->md && battle_config.idletime_mer_option&IDLE_WALK)
sd->idletime_mer = last_tick;
unit_walktoxy(&sd->bl, x, y, 4);
}
@ -11369,6 +11373,8 @@ void clif_parse_Emotion(int fd, struct map_session_data *sd)
sd->idletime = last_tick;
if (battle_config.hom_idle_no_share && sd->hd && battle_config.idletime_hom_option&IDLE_EMOTION)
sd->idletime_hom = last_tick;
if (battle_config.mer_idle_no_share && sd->md && battle_config.idletime_mer_option&IDLE_EMOTION)
sd->idletime_mer = last_tick;
if (sd->state.block_action & PCBLOCK_EMOTION) {
clif_skill_fail(sd, 1, USESKILL_FAIL_LEVEL, 1);
@ -11455,6 +11461,8 @@ void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type,
sd->idletime = last_tick;
if (battle_config.hom_idle_no_share && sd->hd && battle_config.idletime_hom_option&IDLE_ATTACK)
sd->idletime_hom = last_tick;
if (battle_config.mer_idle_no_share && sd->md && battle_config.idletime_mer_option&IDLE_ATTACK)
sd->idletime_mer = last_tick;
unit_attack(&sd->bl, target_id, action_type != 0);
break;
case 0x02: // sitdown
@ -11487,6 +11495,8 @@ void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type,
sd->idletime = last_tick;
if (battle_config.hom_idle_no_share && sd->hd && battle_config.idletime_hom_option&IDLE_SIT)
sd->idletime_hom = last_tick;
if (battle_config.mer_idle_no_share && sd->md && battle_config.idletime_mer_option&IDLE_SIT)
sd->idletime_mer = last_tick;
pc_setsit(sd);
skill_sit(sd, true);
@ -11512,6 +11522,8 @@ void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type,
sd->idletime = last_tick;
if (battle_config.hom_idle_no_share && sd->hd && battle_config.idletime_hom_option&IDLE_SIT)
sd->idletime_hom = last_tick;
if (battle_config.mer_idle_no_share && sd->md && battle_config.idletime_mer_option&IDLE_SIT)
sd->idletime_mer = last_tick;
skill_sit(sd, false);
clif_standing(&sd->bl);
}
@ -11771,6 +11783,8 @@ void clif_parse_DropItem(int fd, struct map_session_data *sd){
sd->idletime = last_tick;
if (battle_config.hom_idle_no_share && sd->hd && battle_config.idletime_hom_option&IDLE_DROPITEM)
sd->idletime_hom = last_tick;
if (battle_config.mer_idle_no_share && sd->md && battle_config.idletime_mer_option&IDLE_DROPITEM)
sd->idletime_mer = last_tick;
return;
}
@ -11803,6 +11817,8 @@ void clif_parse_UseItem(int fd, struct map_session_data *sd)
sd->idletime = last_tick;
if (battle_config.hom_idle_no_share && sd->hd && battle_config.idletime_hom_option&IDLE_USEITEM)
sd->idletime_hom = last_tick;
if (battle_config.mer_idle_no_share && sd->md && battle_config.idletime_mer_option&IDLE_USEITEM)
sd->idletime_mer = last_tick;
n = RFIFOW(fd,packet_db[RFIFOW(fd,0)].pos[0])-2;
if(n <0 || n >= MAX_INVENTORY)
@ -11852,6 +11868,8 @@ void clif_parse_EquipItem(int fd,struct map_session_data *sd)
sd->idletime = last_tick;
if (battle_config.hom_idle_no_share && sd->hd && battle_config.idletime_hom_option&IDLE_USEITEM)
sd->idletime_hom = last_tick;
if (battle_config.mer_idle_no_share && sd->md && battle_config.idletime_mer_option&IDLE_USEITEM)
sd->idletime_mer = last_tick;
//Client doesn't send the position for ammo.
if(sd->inventory_data[index]->type == IT_AMMO)
@ -11893,6 +11911,8 @@ void clif_parse_UnequipItem(int fd,struct map_session_data *sd)
sd->idletime = last_tick;
if (battle_config.hom_idle_no_share && sd->hd && battle_config.idletime_hom_option&IDLE_USEITEM)
sd->idletime_hom = last_tick;
if (battle_config.mer_idle_no_share && sd->md && battle_config.idletime_mer_option&IDLE_USEITEM)
sd->idletime_mer = last_tick;
pc_unequipitem(sd,index,1);
}
@ -12550,6 +12570,8 @@ void clif_parse_skill_toid( struct map_session_data* sd, uint16 skill_id, uint16
sd->idletime = last_tick;
if (battle_config.hom_idle_no_share && sd->hd && battle_config.idletime_hom_option&IDLE_USESKILLTOID)
sd->idletime_hom = last_tick;
if (battle_config.mer_idle_no_share && sd->md && battle_config.idletime_mer_option&IDLE_USESKILLTOID)
sd->idletime_mer = last_tick;
if( sd->npc_id ){
if( pc_hasprogress( sd, WIP_DISABLE_SKILLITEM ) || !sd->npc_item_flag || !( inf & INF_SELF_SKILL ) ){
@ -12680,6 +12702,8 @@ static void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, uin
sd->idletime = last_tick;
if (battle_config.hom_idle_no_share && sd->hd && battle_config.idletime_hom_option&IDLE_USESKILLTOPOS)
sd->idletime_hom = last_tick;
if (battle_config.mer_idle_no_share && sd->md && battle_config.idletime_mer_option&IDLE_USESKILLTOPOS)
sd->idletime_mer = last_tick;
if( skill_isNotOk(skill_id, sd) )
return;

View File

@ -2239,9 +2239,9 @@ static TIMER_FUNC(mob_delay_item_drop){
* Sets the item_drop into the item_drop_list.
* Also performs logging and autoloot if enabled.
* rate is the drop-rate of the item, required for autoloot.
* flag : Killed only by homunculus?
* flag : Killed only by homunculus/mercenary?
*------------------------------------------*/
static void mob_item_drop(struct mob_data *md, struct item_drop_list *dlist, struct item_drop *ditem, int loot, int drop_rate, unsigned short flag)
static void mob_item_drop(struct mob_data *md, struct item_drop_list *dlist, struct item_drop *ditem, int loot, int drop_rate, bool flag)
{
TBL_PC* sd;
bool test_autoloot;
@ -2253,7 +2253,7 @@ static void mob_item_drop(struct mob_data *md, struct item_drop_list *dlist, str
if( sd == NULL ) sd = map_charid2sd(dlist->third_charid);
test_autoloot = sd
&& (drop_rate <= sd->state.autoloot || pc_isautolooting(sd, ditem->item_data.nameid))
&& (flag?(battle_config.homunculus_autoloot?(battle_config.hom_idle_no_share == 0 || !pc_isidle_hom(sd)):0):
&& (flag ? ((battle_config.homunculus_autoloot ? (battle_config.hom_idle_no_share == 0 || !pc_isidle_hom(sd)) : 0) || (battle_config.mercenary_autoloot ? (battle_config.mer_idle_no_share == 0 || !pc_isidle_mer(sd)) : 0)) :
(battle_config.idle_no_autoloot == 0 || DIFF_TICK(last_tick, sd->idletime) < battle_config.idle_no_autoloot));
#ifdef AUTOLOOT_DISTANCE
test_autoloot = test_autoloot && sd->bl.m == md->bl.m
@ -2515,7 +2515,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
int dmgbltypes = 0; // bitfield of all bl types, that caused damage to the mob and are elligible for exp distribution
unsigned int mvp_damage;
t_tick tick = gettick();
bool rebirth, homkillonly;
bool rebirth, homkillonly, merckillonly;
status = &md->status;
@ -2581,6 +2581,8 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
// determines, if the monster was killed by homunculus' damage only
homkillonly = (bool)( ( dmgbltypes&BL_HOM ) && !( dmgbltypes&~BL_HOM ) );
// determines if the monster was killed by mercenary damage only
merckillonly = (bool)((dmgbltypes & BL_MER) && !(dmgbltypes & ~BL_MER));
if(!battle_config.exp_calc_type && count > 1) { //Apply first-attacker 200% exp share bonus
//TODO: Determine if this should go before calculating the MVP player instead of after.
@ -2848,7 +2850,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
}
// Announce first, or else ditem will be freed. [Lance]
// By popular demand, use base drop rate for autoloot code. [Skotlex]
mob_item_drop(md, dlist, ditem, 0, battle_config.autoloot_adjust ? drop_rate : md->db->dropitem[i].rate, homkillonly);
mob_item_drop(md, dlist, ditem, 0, battle_config.autoloot_adjust ? drop_rate : md->db->dropitem[i].rate, homkillonly || merckillonly);
}
// Ore Discovery [Celest]
@ -2857,7 +2859,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
memset(&mobdrop, 0, sizeof(struct s_mob_drop));
mobdrop.nameid = itemdb_searchrandomid(IG_FINDINGORE,1);
ditem = mob_setdropitem(&mobdrop, 1, md->mob_id);
mob_item_drop(md, dlist, ditem, 0, battle_config.finding_ore_rate/10, homkillonly);
mob_item_drop(md, dlist, ditem, 0, battle_config.finding_ore_rate/10, homkillonly || merckillonly);
}
if(sd) {
@ -2889,7 +2891,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
memset(&mobdrop, 0, sizeof(struct s_mob_drop));
mobdrop.nameid = dropid;
mob_item_drop(md, dlist, mob_setdropitem(&mobdrop,1,md->mob_id), 0, drop_rate, homkillonly);
mob_item_drop(md, dlist, mob_setdropitem(&mobdrop,1,md->mob_id), 0, drop_rate, homkillonly || merckillonly);
}
}
@ -2904,7 +2906,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
// process items looted by the mob
if (md->lootitems) {
for (i = 0; i < md->lootitem_count; i++)
mob_item_drop(md, dlist, mob_setlootitem(&md->lootitems[i], md->mob_id), 1, 10000, homkillonly);
mob_item_drop(md, dlist, mob_setlootitem(&md->lootitems[i], md->mob_id), 1, 10000, homkillonly || merckillonly);
}
if (dlist->item) //There are drop items.
add_timer(tick + (!battle_config.delay_battle_damage?500:0), mob_delay_item_drop, 0, (intptr_t)dlist);
@ -2920,7 +2922,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
dlist->third_charid = (third_sd ? third_sd->status.char_id : 0);
dlist->item = NULL;
for (i = 0; i < md->lootitem_count; i++)
mob_item_drop(md, dlist, mob_setlootitem(&md->lootitems[i], md->mob_id), 1, 10000, homkillonly);
mob_item_drop(md, dlist, mob_setlootitem(&md->lootitems[i], md->mob_id), 1, 10000, homkillonly || merckillonly);
add_timer(tick + (!battle_config.delay_battle_damage?500:0), mob_delay_item_drop, 0, (intptr_t)dlist);
}

View File

@ -390,6 +390,7 @@ struct map_session_data {
unsigned int chatID;
time_t idletime;
time_t idletime_hom;
time_t idletime_mer;
struct s_progressbar {
int npc_id;
@ -973,6 +974,7 @@ extern struct s_job_info job_info[CLASS_COUNT];
#define pc_issit(sd) ( (sd)->vd.dead_sit == 2 )
#define pc_isidle_party(sd) ( (sd)->chatID || (sd)->state.vending || (sd)->state.buyingstore || DIFF_TICK(last_tick, (sd)->idletime) >= battle_config.idle_no_share )
#define pc_isidle_hom(sd) ( (sd)->hd && ( (sd)->chatID || (sd)->state.vending || (sd)->state.buyingstore || DIFF_TICK(last_tick, (sd)->idletime_hom) >= battle_config.hom_idle_no_share ) )
#define pc_isidle_mer(sd) ( (sd)->md && ( (sd)->chatID || (sd)->state.vending || (sd)->state.buyingstore || DIFF_TICK(last_tick, (sd)->idletime_mer) >= battle_config.mer_idle_no_share ) )
#define pc_istrading(sd) ( (sd)->npc_id || (sd)->state.vending || (sd)->state.buyingstore || (sd)->state.trading )
#define pc_cant_act(sd) ( (sd)->npc_id || (sd)->state.vending || (sd)->state.buyingstore || (sd)->chatID || ((sd)->sc.opt1 && (sd)->sc.opt1 != OPT1_BURNING) || (sd)->state.trading || (sd)->state.storage_flag || (sd)->state.prevend )

View File

@ -17643,6 +17643,17 @@ BUILDIN_FUNC(checkidlehom)
return SCRIPT_CMD_SUCCESS;
}
BUILDIN_FUNC(checkidlemer)
{
TBL_PC *sd;
if( script_nick2sd(2,sd) )
script_pushint(st, DIFF_TICK(last_tick, sd->idletime_mer));
else
script_pushint(st, 0);
return SCRIPT_CMD_SUCCESS;
}
BUILDIN_FUNC(searchitem)
{
struct script_data* data = script_getdata(st, 2);
@ -25476,6 +25487,7 @@ struct script_function buildin_func[] = {
BUILDIN_DEF(checkchatting,"?"),
BUILDIN_DEF(checkidle,"?"),
BUILDIN_DEF(checkidlehom,"?"),
BUILDIN_DEF(checkidlemer,"?"),
BUILDIN_DEF(openmail,"?"),
BUILDIN_DEF(openauction,"?"),
BUILDIN_DEF(checkcell,"siii"),