//===== Hercules Plugin ======================================
//= @showbuff
//===== By: ==================================================
//= AnnieRuru
//===== Current Version: =====================================
//= 0.4
//===== Compatible With: =====================================
//= Hercules 2018-06-05
//===== Description: =========================================
//= @showbuff on the party screen Alt + Z
//===== Topic ================================================
//= http://herc.ws/board/topic/11349-partybuff-spb/
//===== Additional Comments: =================================
//= testing ...
//= join party -> OK -> clif_party_member_info_overload
//= somebody leave party -> OK -> clif_party_withdraw_overload
//= disband party -> not yet ... I guess no need test this one
//= party member log in -> OK
//= party member log out -> OK
//= expel party -> BUG -> no choice but have to prevent them from doing so
//= you leave party -> party_leave_pre
//============================================================
#include "common/hercules.h"
#include "map/pc.h"
#include "map/clif.h"
#include "map/party.h"
#include "common/nullpo.h"
#include "common/socket.h"
#include "common/memmgr.h"
#include "plugins/HPMHooking.h"
#include "common/HPMDataCheck.h"
HPExport struct hplugin_info pinfo = {
"showbuff",
SERVER_TYPE_MAP,
"0.4",
HPM_VERSION,
};
struct player_data {
int buff;
bool showbuff;
};
int status_change_start_post( int retVal, struct block_list *src, struct block_list *bl, enum sc_type type, int rate, int val1, int val2, int val3, int val4, int tick, int flag) {
if ( bl->type == BL_PC && retVal > 0 ) {
struct map_session_data *sd = BL_CAST( BL_PC, bl );
struct party_data *p;
if ( p = party->search(sd->status.party_id )) {
struct player_data *ssd = getFromMSD( sd, 0 );
int before_buff = ssd->buff;
if ( type == SC_BLESSING )
ssd->buff |= 0x1;
if ( type == SC_INC_AGI )
ssd->buff |= 0x2;
if ( type == SC_PROTECTWEAPON || type == SC_PROTECTSHIELD || type == SC_PROTECTARMOR || type == SC_PROTECTHELM )
if ( sd->sc.data[SC_PROTECTWEAPON] && sd->sc.data[SC_PROTECTSHIELD] && sd->sc.data[SC_PROTECTARMOR] && sd->sc.data[SC_PROTECTHELM] )
ssd->buff |= 0x4;
if ( type == SC_SOULLINK )
ssd->buff |= 0x8;
if ( type == SC_DEVOTION )
ssd->buff |= 0x10;
if ( before_buff != ssd->buff ) // only send the packet if update the status is newly apply, no need to resend if just renew the status
clif->party_info( p, NULL );
}
}
return retVal;
}
int status_change_end_post( int retVal, struct block_list *bl, enum sc_type type, int tid, const char *file, int line ) {
if ( bl->type == BL_PC && retVal > 0 ) {
struct map_session_data *sd = BL_CAST( BL_PC, bl );
struct party_data *p;
if ( sd->state.active == 1 ) { // fix map-server crash when player logout
if (( p = party->search(sd->status.party_id ))) {
struct player_data *ssd = getFromMSD( sd, 0 );
int before_buff = ssd->buff;
if ( type == SC_BLESSING )
ssd->buff &= ~0x1;
if ( type == SC_INC_AGI )
ssd->buff &= ~0x2;
if ( type == SC_PROTECTWEAPON || type == SC_PROTECTSHIELD || type == SC_PROTECTARMOR || type == SC_PROTECTHELM )
if ( sd->sc.data[SC_PROTECTWEAPON] && sd->sc.data[SC_PROTECTSHIELD] && sd->sc.data[SC_PROTECTARMOR] && sd->sc.data[SC_PROTECTHELM] )
ssd->buff &= ~0x4;
if ( type == SC_SOULLINK )
ssd->buff &= ~0x8;
if ( type == SC_DEVOTION )
ssd->buff &= ~0x10;
if ( before_buff != ssd->buff ) // only send the packet if update the status is newly apply, no need to resend if just renew the status
clif->party_info( p, NULL );
}
}
}
return retVal;
}
char *showing_buff( struct map_session_data *sd ) {
struct player_data *ssd = getFromMSD( sd, 0 );
char *temp = "\0";
safesnprintf( temp, NAME_LENGTH, "[%s%s%s%s%s]%s",
( ssd->buff & 0x1 )? "B" : "_",
( ssd->buff & 0x2 )? "A" : "_",
( ssd->buff & 0x4 )? "F" : "_",
( ssd->buff & 0x8 )? "S" : "_",
( ssd->buff & 0x10 )? "+" : "_",
sd->status.name );
return temp;
}
void clif_party_info_overload( struct party_data *p, struct map_session_data *sd ) {
struct PACKET_ZC_GROUP_LIST *packet;
int i, c;
unsigned char buf[sizeof(*packet) + sizeof(struct PACKET_ZC_GROUP_LIST_SUB) * MAX_PARTY];
nullpo_retv(p);
memset( buf, 0, sizeof(buf) );
packet = (struct PACKET_ZC_GROUP_LIST *)buf;
packet->packetType = partyinfo;
memcpy( packet->partyName, p->party.name, NAME_LENGTH );
for ( i = 0, c = 0; i < MAX_PARTY; ++i ) {
struct party_member *m = &p->party.member[i];
if ( !m->account_id )
continue;
packet->members[c].AID = m->account_id;
#if PACKETVER >= 20171207
packet->members[c].GID = m->char_id;
#endif
memcpy( packet->members[c].playerName, m->name, NAME_LENGTH );
mapindex->getmapname_ext(mapindex_id2name(m->map), packet->members[c].mapName);
packet->members[c].leader = (m->leader) ? 0 : 1;
packet->members[c].offline = (m->online) ? 0 : 1;
#if PACKETVER >= 20170502
packet->members[c].class = m->class;
packet->members[c].baseLevel = m->lv;
#endif
++c;
}
packet->packetLen = sizeof(*packet) + c * sizeof( struct PACKET_ZC_GROUP_LIST_SUB );
if ( sd ) {
struct player_data *ssd = getFromMSD( sd, 0 );
if ( ssd && ssd->showbuff ) {
for ( i = 0, c = 0; i < MAX_PARTY; ++i ) {
struct party_member *m = &p->party.member[i];
if ( !m->account_id )
continue;
if ( m->online && p->data[i].sd != NULL )
memcpy( packet->members[c].playerName, showing_buff( p->data[i].sd ), NAME_LENGTH );
++c;
}
}
clif->send( buf, packet->packetLen, &sd->bl, SELF );
}
else {
int j;
for ( j = 0; j < MAX_PARTY; ++j ) {
struct party_member *m1 = &p->party.member[j];
if ( !m1 || !m1->account_id )
continue;
else {
struct map_session_data *tsd = map->id2sd( m1->account_id );
struct player_data *ssd = NULL;
if ( !tsd )
continue;
ssd = getFromMSD( tsd, 0 );
if ( ssd && ssd->showbuff ) {
for ( i = 0, c = 0; i < MAX_PARTY; ++i ) {
struct party_member *m = &p->party.member[i];
if ( !m->account_id )
continue;
if ( m->online && p->data[i].sd != NULL ) {
memcpy( packet->members[c].playerName, showing_buff( p->data[i].sd ), NAME_LENGTH );
}
++c;
}
}
else {
for ( i = 0, c = 0; i < MAX_PARTY; ++i ) {
struct party_member *m = &p->party.member[i];
if ( !m->account_id )
continue;
memcpy( packet->members[c].playerName, m->name, NAME_LENGTH );
++c;
}
}
clif->send( buf, packet->packetLen, map->id2bl( m1->account_id ), SELF );
}
}
}
}
bool pc_authok_pre( struct map_session_data **sd, int *login_id2, time_t *expiration_time, int *group_id, const struct mmo_charstatus **st, bool *changing_mapservers ) {
struct player_data *ssd;
CREATE( ssd, struct player_data, true );
ssd->buff = 0;
ssd->showbuff = 0;
addToMSD( *sd, ssd, 0, true );
return 0;
}
void clif_party_member_info_overload( struct party_data *p, struct map_session_data *sd ) {
return;
}
void clif_party_withdraw_overload( struct party_data *p, struct map_session_data *sd, int account_id, const char *name, int flag ) {
unsigned char buf[64];
nullpo_retv(p);
nullpo_retv(name);
if ( !sd && !(flag & 0xf0) ) {
int i;
ARR_FIND( 0, MAX_PARTY, i, p->data[i].sd != NULL );
if ( i != MAX_PARTY )
sd = p->data[i].sd;
}
if ( !sd )
return;
WBUFW(buf,0) = 0x105;
WBUFL(buf,2) = account_id;
memcpy( WBUFP(buf,6), name, NAME_LENGTH );
WBUFB(buf,30) = flag & 0x0f;
if ( !(flag & 0xf0) ) {
for ( int i = 0; i < MAX_PARTY; ++i ) {
struct party_member *m = &p->party.member[i];
if ( !m || !m->account_id )
continue;
else {
struct map_session_data *tsd = map->id2sd( m->account_id );
struct player_data *ssd = NULL;
if ( !tsd )
continue;
ssd = getFromMSD( tsd, 0 );
if ( ssd && ssd->showbuff )
memcpy( WBUFP(buf,6), showing_buff( sd ), NAME_LENGTH );
else
memcpy( WBUFP(buf,6), name, NAME_LENGTH );
clif->send( buf, 31, map->id2bl( m->account_id ), SELF );
}
}
}
else
clif->send( buf, 31, &sd->bl, SELF );
}
int party_removemember_pre( struct map_session_data **sd, int *account_id, const char **name ) {
struct player_data *ssd = getFromMSD( *sd, 0 );
if ( ssd->showbuff ) {
clif->message( (*sd)->fd, "You cannot expel a party member while @showbuff is turn ON." );
hookStop();
}
return 0;
}
int party_leave_pre( struct map_session_data **sd ) {
struct party_data *p;
int i;
nullpo_ret(*sd);
p = party->search( (*sd)->status.party_id );
if ( p == NULL )
return 0;
ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == (*sd)->status.account_id && p->party.member[i].char_id == (*sd)->status.char_id );
if ( i == MAX_PARTY )
return 0;
else {
struct player_data *ssd = getFromMSD( *sd, 0 );
if ( ssd->showbuff )
ssd->showbuff = 0;
}
return 0;
}
ACMD(showbuff) {
struct player_data *ssd = getFromMSD( sd, 0 );
struct party_data *p = party->search( sd->status.party_id );
if ( !p ) {
clif->message( sd->fd, "You don't have a Party." );
return false;
}
else if ( ssd->showbuff ) {
ssd->showbuff = 0;
clif->message( sd->fd, "@showbuff is now turn OFF." );
}
else {
ssd->showbuff = 1;
clif->message( sd->fd, "@showbuff is now turn ON." );
}
clif->party_info( p, sd );
return true;
}
HPExport void plugin_init (void) {
addHookPost( status, change_start, status_change_start_post );
addHookPost( status, change_end_, status_change_end_post );
clif->party_info = &clif_party_info_overload;
addHookPre( pc, authok, pc_authok_pre );
clif->party_member_info = &clif_party_member_info_overload;
clif->party_withdraw = &clif_party_withdraw_overload;
addHookPre( party, removemember, party_removemember_pre );
addHookPre( party, leave, party_leave_pre );
addAtcommand( "showbuff", showbuff );
}