//===== Hercules Plugin ================================================================================================= //= npc masking //===== By: ============================================================================================================= //= Garr //===== Current Version: ================================================================================================ //= 1.0 //===== Compatible With: ================================================================================================ //= Hercules //===== Description: ==================================================================================================== //= Masks NPCs for players based on their variables. //===== Additional Comments: ============================================================================================ //= Usage: //= npcmask ,,,,{,{,{,{}}}} //= npcunmask {} //= Applies and removes NPC masking, accordingly. //= - sprite NPC will show if conditions are met //= - variable name to check, only integer character variables. //= - compare method, based on following masks: //= 0x01 - Less, uses value1 //= 0x02 - Equal, uses value1 //= 0x04 - More, uses value1 //= 0x10 - "BETWEEN" - Between value1 and value2, including values 1 and 2 //= 0x20 - "EXCLUDE" - Excluding between value1 and value2, values 1 and 2 are excluded also. //= Example: 0x03 - same as less or equal, makes a check (variable <= value1) //= "BETWEEN" and "EXCLUDE" modes overwrite anything else, "EXCLUDE" will take precedence over all other. //= - value1 to compare too, used in all compare methods. //= - value2, used in "BETWEEN" and "EXCLUDE" modes. Optional parameter, if it won't be provided //= while using "BETWEEN" or "EXCLUDE" methods, those methods will be skipped. //= - size masking, defaults to medium. //= - name masking, if not provided will use NPC's original name. //= - NPC name to use masking on. If not provided, will run on NPC script is running from. //======================================================================================================================== #include #include #include #include "../map/chat.h" #include "../map/clif.h" #include "../map/map.h" #include "../map/mob.h" #include "../map/npc.h" #include "../map/packets.h" #include "../map/packets_struct.h" #include "../map/path.h" #include "../map/pc.h" #include "../map/script.h" #include "../map/status.h" #include "../common/HPMi.h" #include "../common/malloc.h" #include "../common/mmo.h" #include "../common/nullpo.h" #include "../common/socket.h" #include "../common/strlib.h" #include "../common/utils.h" #include "../common/HPMDataCheck.h" // should always be the last file included! (if you don't make it last, it'll intentionally break compile time) HPExport struct hplugin_info pinfo = { "npcmask", // Plugin name SERVER_TYPE_MAP,// Which server types this plugin works with? "0.1", // Plugin version HPM_VERSION, // HPM Version (don't change, macro is automatically updated) }; #define pcdb_checkid_sub(class_) \ ( \ ( (class_) < JOB_MAX_BASIC ) \ || ( (class_) >= JOB_NOVICE_HIGH && (class_) <= JOB_DARK_COLLECTOR ) \ || ( (class_) >= JOB_RUNE_KNIGHT && (class_) <= JOB_MECHANIC_T2 ) \ || ( (class_) >= JOB_BABY_RUNE && (class_) <= JOB_BABY_MECHANIC2 ) \ || ( (class_) >= JOB_SUPER_NOVICE_E && (class_) <= JOB_SUPER_BABY_E ) \ || ( (class_) >= JOB_KAGEROU && (class_) <= JOB_OBORO ) \ || ( (class_) >= JOB_REBELLION && (class_) < JOB_MAX ) \ ) #define pcdb_checkid(class_) pcdb_checkid_sub((unsigned int)(class_)) enum compare_method { CM_NONE = 0x00, CM_LESS = 0x01, CM_EQU = 0x02, CM_MORE = 0x04, CM_BETWEEN = 0x10, CM_EXCLUDE = 0x20, CM_MAX = 0x37, } c_m; struct npc_mask { unsigned masked : 1; // Bit to show that npc uses masking. int cmask; //class_ mask char nmask[NAME_LENGTH+1]; //name mask unsigned smask : 2; // size mask const char *varname; //Variable to check enum compare_method compare; // Compare method int value1,value2; // values to compare variable to }; static inline void WBUFPOS(uint8* p, unsigned short pos, short x, short y, unsigned char dir) { p += pos; p[0] = (uint8)(x>>2); p[1] = (uint8)((x<<6) | ((y>>4)&0x3f)); p[2] = (uint8)((y<<4) | (dir&0xf)); } uint16 GetWord(uint32 val, int idx) { switch( idx ) { case 0: return (uint16)( (val & 0x0000FFFF) ); case 1: return (uint16)( (val & 0xFFFF0000) >> 0x10 ); default: return 0; } } void clif_set_unit_idle2_hooked(struct block_list* bl, struct map_session_data *tsd, bool flag, int newclass) { #if PACKETVER < 20091103 struct view_data* vd = status->get_viewdata(bl); struct packet_idle_unit2 p; struct npc_data *nd = ((TBL_NPC*)bl); int g_id = status->get_guild_id(bl); p.PacketType = idle_unit2Type; #if PACKETVER >= 20071106 p.objecttype = pcdb_checkid(status->get_viewdata(bl)->class_)?0x0:0x6;; #endif p.GID = bl->id; p.speed = status->get_speed(bl); p.bodyState = 0; p.healthState = 0; p.effectState = nd->option; p.job = flag? newclass:vd->class_; p.head = vd->hair_style; p.weapon = vd->weapon; p.accessory = vd->head_bottom; p.shield = vd->shield; p.accessory2 = vd->head_top; p.accessory3 = vd->head_mid; if( bl->type == BL_NPC && ((!flag && vd->class_ == FLAG_CLASS) || (flag && newclass == FLAG_CLASS)) ) { //The hell, why flags work like this? p.shield = status->get_emblem_id(bl); p.accessory2 = GetWord(g_id, 1); p.accessory3 = GetWord(g_id, 0); } p.headpalette = vd->hair_color; p.bodypalette = vd->cloth_color; p.headDir = 0; p.GUID = g_id; p.GEmblemVer = status->get_emblem_id(bl); p.honor = 0; p.virtue = 0; p.isPKModeON = 0; p.sex = vd->sex; WBUFPOS(&p.PosDir[0],0,bl->x,bl->y,unit->getdir(bl)); p.xSize = p.ySize = 0; p.state = vd->dead_sit; p.clevel = 0; clif->send(&p,sizeof(p),tsd?&tsd->bl:bl,SELF); #else return; #endif } void clif_set_unit_idle_hooked(struct block_list* bl, struct map_session_data *tsd, bool flag, int newclass) { if ( bl->type == BL_NPC) { struct view_data* vd = status->get_viewdata(bl); struct packet_idle_unit p; struct npc_data *nd = ((TBL_NPC*)bl); int g_id = status->get_guild_id(bl); // nullpo_retv(bl); #if PACKETVER < 20091103 clif_set_unit_idle2_hooked(bl,tsd,flag,newclass); return; #endif p.PacketType = idle_unitType; #if PACKETVER >= 20091103 p.PacketLength = sizeof(p); p.objecttype = pcdb_checkid(status->get_viewdata(bl)->class_)?0x0:0x6;; #endif p.GID = bl->id; p.speed = status->get_speed(bl); p.bodyState = 0; p.healthState = 0; p.effectState = nd->option; p.job = flag? newclass:vd->class_; p.head = vd->hair_style; p.weapon = vd->weapon; p.accessory = vd->head_bottom; #if PACKETVER < 7 p.shield = vd->shield; #endif p.accessory2 = vd->head_top; p.accessory3 = vd->head_mid; if( bl->type == BL_NPC && ((!flag && vd->class_ == FLAG_CLASS) || (flag && newclass == FLAG_CLASS)) ) { //The hell, why flags work like this? p.accessory = status->get_emblem_id(bl); p.accessory2 = GetWord(g_id, 1); p.accessory3 = GetWord(g_id, 0); } p.headpalette = vd->hair_color; p.bodypalette = vd->cloth_color; p.headDir = 0; #if PACKETVER >= 20101124 p.robe = vd->robe; #endif p.GUID = g_id; p.GEmblemVer = status->get_emblem_id(bl); p.honor = 0; p.virtue = 0; p.isPKModeON = 0; p.sex = vd->sex; WBUFPOS(&p.PosDir[0],0,bl->x,bl->y,unit->getdir(bl)); p.xSize = p.ySize = 0; p.state = vd->dead_sit; p.clevel = 0; #if PACKETVER >= 20080102 p.font = 0; #endif #if PACKETVER >= 20150000 //actual 20120221 p.maxHP = -1; p.HP = -1; p.isBoss = 0; #endif clif->send(&p,sizeof(p),tsd?&tsd->bl:bl,SELF); return; } return; } int packet_help(int packetid) { int retval; switch(packetid) { case 0x0094: retval = 2; break; case 0x0089: #if PACKETVER >= 20040726 retval = 11; #endif #if PACKETVER >= 20040809 retval = 8; #endif break; case 0x009b: #if PACKETVER >= 20040906 retval = 10; #endif #if PACKETVER >= 20040920 retval = 6; #endif #if PACKETVER >= 20041005 retval = 11; #endif #if PACKETVER >= 20041025 retval = 6; #endif break; case 0x008c: #if PACKETVER >= 20041129 retval = 9; #endif #if PACKETVER >= 20050110 retval = 4; #endif #if PACKETVER >= 20050509 retval = 7; #endif #if PACKETVER >= 20050628 retval = 4; #endif #if PACKETVER >= 20050718 retval = 7; #endif #if PACKETVER >= 20050719 retval = 4; #endif #if PACKETVER >= 20060327 retval = 8; #endif #if PACKETVER >= 20070108 retval = 13; #endif #if PACKETVER >= 20070212 retval = 7; #endif #if PACKETVER >= 20080827 retval = 10; #endif break; #if PACKETVER >= 20101124 case 0x0368: retval = 2; break; #endif #if PACKETVER >= 20111005 case 0x088a: retval = 2; break; #endif case 0x096A: retval = 2; #if PACKETVER >= 20120410 case 0x0889: retval = 2; break; #endif #if PACKETVER >= 20120702 case 0x094a: retval = 2; break; #endif #if PACKETVER >= 20130320 case 0x0898: retval = 2; break; #endif #if PACKETVER >= 20130522 case 0x08A6: retval = 2; break; #endif #if PACKETVER >= 20130529 case 0x0863: retval = 2; break; #endif #if PACKETVER >= 20130618 case 0x0944: retval = 2; break; #endif #if PACKETVER >= 20130814 case 0x0937: retval = 2; break; #endif #if PACKETVER >= 20131230 case 0x0926: retval = 2; break; #endif #if PACKETVER >= 20140115 case 0x0802: retval = 2; break; #endif #if PACKETVER >= 20140402 case 0x088A: retval = 2; break; #endif default: retval = 0; break; } return retval; } void clif_parse_GetCharNameRequest_prehook(int *fd, struct map_session_data *sd) { int id = RFIFOL(*fd,packet_help(RFIFOW(*fd,0))); //packet_db[RFIFOW(*fd,0)].pos[0] is not working for some reason... struct block_list* bl; if( id < 0 && -id == sd->bl.id ) // for disguises [Valaris] id = sd->bl.id; bl = map->id2bl(id); if( bl == NULL ) return; // Lagged clients could request names of already gone mobs/players. [Skotlex] if( sd->bl.m != bl->m || !check_distance_bl(&sd->bl, bl, AREA_SIZE) ) return; // Block namerequests past view range if (bl->type != BL_NPC) return; unsigned char buf[103]; struct npc_data *nd = (TBL_NPC*)bl;; struct npc_mask *nm; int var; bool flag = false; // nullpo_retv(bl); WBUFW(buf,0) = 0x95; WBUFL(buf,2) = bl->id; if ( !(nm = getFromNPCD(nd,0)) ) { CREATE(nm,struct npc_mask,1); safestrncpy(nm->nmask, "\0", NAME_LENGTH); nm->cmask = 0; nm->smask = 0; nm->varname = "\0"; nm->compare = CM_NONE; nm->value1 = nm->value2 = 0; nm->masked = 0; addToNPCD(nd,nm,0,true); } var = pc_readglobalreg(sd, script->add_str(nm->varname)); if ( nm->masked && nm->compare) { //Not checking var or nm->value1/2 to make usage of variables resulting in 0 possible, or to compare with 0. if (nm->compare&CM_LESS && var < nm->value1) flag = true; if (nm->compare&CM_EQU && var == nm->value1) flag = true; if (nm->compare&CM_MORE && var > nm->value1) flag = true; if ( nm->compare&CM_BETWEEN && var >= nm->value1 && var <= nm->value2 ) flag = true; if ( nm->compare&CM_EXCLUDE && (var < nm->value1 || var > nm->value2) ) flag = true; } if (flag && nm->nmask != "\0") { memcpy(WBUFP(buf,6), nm->nmask, NAME_LENGTH); } else { memcpy(WBUFP(buf,6), ((TBL_NPC*)bl)->name, NAME_LENGTH); } WFIFOHEAD(sd->fd, 30); memcpy(WFIFOP(sd->fd, 0), buf, 30); WFIFOSET(sd->fd, 30); hookStop(); return; } void clif_getareachar_unit_prehook (struct map_session_data* sd,struct block_list *bl) { if (bl->type != BL_NPC) return; struct view_data *vd = NULL; struct npc_mask *nm; struct npc_data *nd = (TBL_NPC*)bl; int var; bool flag = false; vd = status->get_viewdata(bl); if (!vd || vd->class_ == INVISIBLE_CLASS) return; if(bl->type == BL_NPC && !nd->chat_id && (nd->option&OPTION_INVISIBLE)) return; if ( !(nm = getFromNPCD(nd,0)) ) { CREATE(nm,struct npc_mask,1); safestrncpy(nm->nmask, "\0", NAME_LENGTH); nm->cmask = 0; nm->smask = 0; nm->varname = "\0"; nm->compare = CM_NONE; nm->value1 = nm->value2 = 0; nm->masked = 0; addToNPCD(nd,nm,0,true); } var = pc_readglobalreg(sd, script->add_str(nm->varname)); if ( nm->masked && nm->compare) { //Not checking var or nm->value1/2 to make usage of variables resulting in 0 possible, or to compare with 0. if ( nm->compare&CM_LESS && var < nm->value1 ) flag = true; if ( nm->compare&CM_EQU && var == nm->value1 ) flag = true; if ( nm->compare&CM_MORE && var > nm->value1 ) flag = true; if ( nm->compare&CM_BETWEEN && var >= nm->value1 && var <= nm->value2 ) flag = true; if ( nm->compare&CM_EXCLUDE && (var < nm->value1 || var > nm->value2 ) ) flag = true; } clif_set_unit_idle_hooked(bl,sd,flag,nm->cmask); if( nd->chat_id ) clif->dispchat((struct chat_data*)map->id2bl(nd->chat_id),sd->fd); if( (!flag && nd->size == SZ_BIG) || (flag && nm->smask == SZ_BIG) ) clif->specialeffect_single(bl,423,sd->fd); else if( (!flag && nd->size == 1) || (flag && nm->smask == 1) ) clif->specialeffect_single(bl,421,sd->fd); hookStop(); return; } BUILDIN(npcmask) { struct npc_mask *nm; struct npc_data *nd; char *varn; const char *varn2; if ( script_hasdata(st,9) && script_isstringtype(st,9) ) { nd = npc->name2id(script_getstr(st,9)); } else { nd = map->id2nd(st->oid); } if( nd == NULL ) { script_pushint(st,-1); //No such NPC; return true; } varn = (char*)script_getstr(st,3); if ( !not_server_variable(varn[0]) || is_string_variable(varn) ) { ShowWarning("buildin_npcmask: Server-side or string-type variable name used. Aborting.\n"); return true; } if ( !(nm = getFromNPCD(nd,0)) ) { CREATE(nm,struct npc_mask,1); //assigning struct from given variables nm->varname = script_getstr(st,3); nm->cmask = script_getnum(st,2); nm->compare = CM_NONE; if ( script_getnum(st,4)&CM_LESS) nm->compare |= CM_LESS; if ( script_getnum(st,4)&CM_EQU) nm->compare |= CM_EQU; if ( script_getnum(st,4)&CM_MORE) nm->compare |= CM_MORE; if ( script_getnum(st,4)&CM_BETWEEN) { if ( script_hasdata(st,6) ) { nm->compare = CM_BETWEEN; } else { ShowWarning("buildin_npcmask: Compare method \"BETWEEN\" was chosen, but no value2 provided. Skipping.\n"); } } if ( script_getnum(st,4)&CM_EXCLUDE) { if ( script_hasdata(st,6) ) { nm->compare = CM_EXCLUDE; } else { ShowWarning("buildin_npcmask: Compare method \"EXCLUDE\" was chosen, but no value2 provided. Skipping.\n"); } } nm->value1 = script_getnum(st,5); if ( script_hasdata(st,6) ) { if ( script_isinttype(st,6) ) { nm->value2 = script_getnum(st,6); } else { ShowWarning("buildin_npcmask: Provided value2 is not of int type, defaulting to 0.\n"); nm->value2 = 0; } if ( nm->value2 < nm->value1 ) { int temp; temp = nm->value1; nm->value1 = nm->value2; nm->value2 = temp; } } else { nm->value2 = 0; } if ( script_hasdata(st,7) ) { if ( script_isinttype(st,7) ) { if ( script_getnum(st,7) < 0 || script_getnum(st,7) > 2 ) { ShowWarning("builin_npcmask: Provided size is unknown, defaulting to medium.\n"); nm->smask = 0; } nm->smask = script_getnum(st,7); } else { ShowWarning("buildin_npcmask: Provided size is in wrong format, defaulting to medium.\n"); nm->smask = 0; } } else { nm->smask = 0; } if ( script_hasdata(st,8) ) { if ( script_isstringtype(st,8) ) { varn2 = script_getstr(st,8); safestrncpy(nm->nmask, varn2, NAME_LENGTH); } } else { safestrncpy(nm->nmask, "\0", NAME_LENGTH); } nm->masked = 1; addToNPCD(nd,nm,0,true); } else { nm->varname = script_getstr(st,3); nm->cmask = script_getnum(st,2); nm->compare = CM_NONE; if ( script_getnum(st,4)&CM_LESS) nm->compare |= CM_LESS; if ( script_getnum(st,4)&CM_EQU) nm->compare |= CM_EQU; if ( script_getnum(st,4)&CM_MORE) nm->compare |= CM_MORE; if ( script_getnum(st,4)&CM_BETWEEN) { if ( script_hasdata(st,6) ) { nm->compare = CM_BETWEEN; } else { ShowWarning("buildin_npcmask: Compare method \"BETWEEN\" was chosen, but no value2 provided. Skipping.\n"); } } if ( script_getnum(st,4)&CM_EXCLUDE) { if ( script_hasdata(st,6) ) { nm->compare = CM_EXCLUDE; } else { ShowWarning("buildin_npcmask: Compare method \"EXCLUDE\" was chosen, but no value2 provided. Skipping.\n"); } } nm->value1 = script_getnum(st,5); if ( script_hasdata(st,6) ) { if ( script_isinttype(st,6) ) { nm->value2 = script_getnum(st,6); } else { ShowWarning("buildin_npcmask: Provided value2 is not of int type, defaulting to 0.\n"); nm->value2 = 0; } if ( nm->value2 < nm->value1 ) { int temp; temp = nm->value1; nm->value1 = nm->value2; nm->value2 = temp; } } else { nm->value2 = 0; } if ( script_hasdata(st,7) ) { if ( script_isinttype(st,7) ) { if ( script_getnum(st,7) < 0 || script_getnum(st,7) > 2 ) { ShowWarning("builin_npcmask: Provided size is unknown, defaulting to medium.\n"); nm->smask = 0; } nm->smask = script_getnum(st,7); } else { ShowWarning("buildin_npcmask: Provided size is in wrong format, defaulting to medium.\n"); nm->smask = 0; } } else { nm->smask = 0; } if ( script_hasdata(st,8) ) { if ( script_isstringtype(st,8) ) { varn2 = script_getstr(st,8); safestrncpy(nm->nmask, varn2, NAME_LENGTH); } } else { safestrncpy(nm->nmask, "\0", NAME_LENGTH); } nm->masked = 1; } script_pushint(st,1); return true; } BUILDIN(npcunmask) { struct npc_mask *nm; struct npc_data *nd; if ( script_hasdata(st,2) ) { nd = npc->name2id(script_getstr(st,2)); } else { nd = map->id2nd(st->oid); } if( nd == NULL ) { script_pushint(st,-1); //No such NPC; return true; } if ( !(nm = getFromNPCD(nd,0)) ) { CREATE(nm,struct npc_mask,1); safestrncpy(nm->nmask, "\0", NAME_LENGTH); nm->cmask = 0; nm->smask = 0; nm->varname = "\0"; nm->compare = CM_NONE; nm->value1 = nm->value2 = 0; nm->masked = 0; addToNPCD(nd,nm,0,true); } else { safestrncpy(nm->nmask, "\0", NAME_LENGTH); nm->cmask = 0; nm->smask = 0; nm->varname = "\0"; nm->compare = CM_NONE; nm->value1 = nm->value2 = 0; nm->masked = 0; } script_pushint(st,1); return true; } HPExport void plugin_init (void) { battle = GET_SYMBOL("battle"); clif = GET_SYMBOL("clif"); iMalloc = GET_SYMBOL("iMalloc"); map = GET_SYMBOL("map"); mapit = GET_SYMBOL("mapit"); npc = GET_SYMBOL("npc"); path = GET_SYMBOL("path"); pc = GET_SYMBOL("pc"); script = GET_SYMBOL("script"); session = GET_SYMBOL("session"); sockt = GET_SYMBOL("sockt"); status = GET_SYMBOL("status"); strlib = GET_SYMBOL("strlib"); unit = GET_SYMBOL("unit"); addScriptCommand( "npcmask", "isii????", npcmask ); addScriptCommand( "npcunmask", "?", npcunmask ); addHookPre("clif->getareachar_unit", clif_getareachar_unit_prehook ); addHookPre("clif->pGetCharNameRequest",clif_parse_GetCharNameRequest_prehook); }