diff --git a/db/import-tmpl/job_noenter_map.txt b/db/import-tmpl/job_noenter_map.txt new file mode 100644 index 0000000000..c9a25607f7 --- /dev/null +++ b/db/import-tmpl/job_noenter_map.txt @@ -0,0 +1,27 @@ +// Defines Job(s) that are restricted to enter map (by flag/zones) +// +// Structure of Database: +// JobID,FlagZone,GroupLevelBypass +// +// JobID: See JOB_* constants or use job number +// +// Legend for 'Flag' field (bitmask): +// 1 - restricted in normal maps +// 2 - restricted in PVP +// 4 - restricted in GVG +// 8 - restricted in Battlegrounds +// Restricted zones - configured by 'restricted ' mapflag +// 32 - restricted in zone 1 +// 64 - restricted in zone 2 +// 128 - restricted in zone 3 +// 256 - restricted in zone 4 +// 512 - restricted in zone 5 +// 1024 - restricted in zone 6 +// 2048 - restricted in zone 7 +// 4096 - restricted in zone 8 +// +// GroupLevelBypass: Group Level (groups.conf) to ignore the restriction +// +// NOTES: +// - Restriction will be overwritten for multiple defines with the same Job ID +// - The flag is used by 'jobcanentermap' script. diff --git a/db/pre-re/job_noenter_map.txt b/db/pre-re/job_noenter_map.txt new file mode 100644 index 0000000000..c9a25607f7 --- /dev/null +++ b/db/pre-re/job_noenter_map.txt @@ -0,0 +1,27 @@ +// Defines Job(s) that are restricted to enter map (by flag/zones) +// +// Structure of Database: +// JobID,FlagZone,GroupLevelBypass +// +// JobID: See JOB_* constants or use job number +// +// Legend for 'Flag' field (bitmask): +// 1 - restricted in normal maps +// 2 - restricted in PVP +// 4 - restricted in GVG +// 8 - restricted in Battlegrounds +// Restricted zones - configured by 'restricted ' mapflag +// 32 - restricted in zone 1 +// 64 - restricted in zone 2 +// 128 - restricted in zone 3 +// 256 - restricted in zone 4 +// 512 - restricted in zone 5 +// 1024 - restricted in zone 6 +// 2048 - restricted in zone 7 +// 4096 - restricted in zone 8 +// +// GroupLevelBypass: Group Level (groups.conf) to ignore the restriction +// +// NOTES: +// - Restriction will be overwritten for multiple defines with the same Job ID +// - The flag is used by 'jobcanentermap' script. diff --git a/db/re/job_noenter_map.txt b/db/re/job_noenter_map.txt new file mode 100644 index 0000000000..c9a25607f7 --- /dev/null +++ b/db/re/job_noenter_map.txt @@ -0,0 +1,27 @@ +// Defines Job(s) that are restricted to enter map (by flag/zones) +// +// Structure of Database: +// JobID,FlagZone,GroupLevelBypass +// +// JobID: See JOB_* constants or use job number +// +// Legend for 'Flag' field (bitmask): +// 1 - restricted in normal maps +// 2 - restricted in PVP +// 4 - restricted in GVG +// 8 - restricted in Battlegrounds +// Restricted zones - configured by 'restricted ' mapflag +// 32 - restricted in zone 1 +// 64 - restricted in zone 2 +// 128 - restricted in zone 3 +// 256 - restricted in zone 4 +// 512 - restricted in zone 5 +// 1024 - restricted in zone 6 +// 2048 - restricted in zone 7 +// 4096 - restricted in zone 8 +// +// GroupLevelBypass: Group Level (groups.conf) to ignore the restriction +// +// NOTES: +// - Restriction will be overwritten for multiple defines with the same Job ID +// - The flag is used by 'jobcanentermap' script. diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 1634026ed7..07b9b688ad 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -4302,6 +4302,17 @@ raise the specified stat from (current value - ) to current value. --------------------------------------- +*jobcanentermap(""{,}); + +Return true if player (decided by job) can enter the map, false otherwise. + +For optional 'JobID', see constant of Job_*, or use player's Class, BaseJob, +and BaseClass. If no player is attached, this param must have a value. + +See also db/[pre-]re/job_noenter_map.txt + +--------------------------------------- + *get_revision() This command will return the SVN revision number that the server is currently diff --git a/src/common/mmo.h b/src/common/mmo.h index a9bf24c670..86c40dfced 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -402,7 +402,7 @@ struct mmo_charstatus { unsigned int base_exp,job_exp; int zeny; - short class_; + short class_; ///< Player's JobID unsigned int status_point,skill_point; int hp,max_hp,sp,max_sp; unsigned int option; diff --git a/src/map/atcommand.c b/src/map/atcommand.c index b0163c28aa..6a51ee5f94 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -475,7 +475,7 @@ ACMD_FUNC(mapmove) if (!map_search_freecell(NULL, m, &x, &y, 10, 10, 1)) x = y = 0; //Invalid cell, use random spot. } - if (map[m].flag.nowarpto && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) { + if ((map[m].flag.nowarpto && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) || !pc_job_can_entermap((enum e_job)sd->status.class_, m, sd->group_level)) { clif_displaymessage(fd, msg_txt(sd,247)); return -1; } diff --git a/src/map/pc.c b/src/map/pc.c index 09bb268ac6..2ee5c6462a 100755 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -113,7 +113,7 @@ struct item_cd { * Converts a class to its array index for CLASS_COUNT defined arrays. * Note that it does not do a validity check for speed purposes, where parsing * player input make sure to use a pcdb_checkid first! -* @param class_ +* @param class_ Job ID see enum e_job * @return Class Index */ int pc_class2idx(int class_) { @@ -11117,6 +11117,31 @@ static bool pc_readdb_job_param(char* fields[], int columns, int current) return true; } +/** + * Read job_noenter_map.txt + **/ +static bool pc_readdb_job_noenter_map(char *str[], int columns, int current) { + int idx, class_ = -1; + + if (ISDIGIT(str[0][0])) { + class_ = atoi(str[0]); + } else { + if (!script_get_constant(str[0], &class_)) { + ShowError("pc_readdb_job_noenter_map: Invalid job %s specified.\n", str[0]); + return false; + } + } + + if (!pcdb_checkid(class_) || (idx = pc_class2idx(class_)) < 0) { + ShowError("pc_readdb_job_noenter_map: Invalid job %d specified.\n", str[0]); + return false; + } + + job_info[idx].noenter_map.zone = atoi(str[1]); + job_info[idx].noenter_map.group_lv = atoi(str[2]); + return true; +} + static int pc_read_statsdb(const char *basedir, int last_s, bool silent){ int i=1; char line[24000]; //FIXME this seem too big @@ -11220,6 +11245,7 @@ void pc_readdb(void) { sv_readdb(dbsubpath2, "job_basehpsp_db.txt", ',', 4, 4+500, CLASS_COUNT*2, &pc_readdb_job_basehpsp, i); //Make it support until lvl 500! #endif sv_readdb(dbsubpath2, "job_param_db.txt", ',', 2, PARAM_MAX+1, CLASS_COUNT, &pc_readdb_job_param, i); + sv_readdb(dbsubpath2, "job_noenter_map.txt", ',', 3, 3, CLASS_COUNT, &pc_readdb_job_noenter_map, i); aFree(dbsubpath1); aFree(dbsubpath2); } @@ -12177,6 +12203,40 @@ void pc_show_questinfo_reinit(struct map_session_data *sd) { #endif } +/** + * Check if a job is allowed to enter the map + * @param jobid Job ID see enum e_job or sd->status.class_ + * @param m ID -an index- for direct indexing map[] array + * @return 1 if job is allowed, 0 otherwise + **/ +bool pc_job_can_entermap(enum e_job jobid, int m, int group_lv) { + uint16 idx = 0; + + // Map is other map server. + // !FIXME: Currently, a map-server doesn't recognized map's attributes on other server, so we assume it's fine to warp. + if (m < 0) + return true; + + if (m >= MAX_MAP_PER_SERVER || !map[m].cell) + return false; + + if (!pcdb_checkid(jobid)) + return false; + + idx = pc_class2idx(jobid); + if (!job_info[idx].noenter_map.zone || group_lv > job_info[idx].noenter_map.group_lv) + return true; + + if ((!map_flag_vs(m) && job_info[idx].noenter_map.zone&1) || // Normal + (map[m].flag.pvp && job_info[idx].noenter_map.zone&2) || // PVP + (map_flag_gvg2(m) && job_info[idx].noenter_map.zone&4) || // GVG + (map[m].flag.battleground && job_info[idx].noenter_map.zone&8) || // Battleground + (map[m].flag.restricted && job_info[idx].noenter_map.zone&(8*map[m].zone)) // Zone restriction + ) + return false; + + return true; +} /*========================================== * pc Init/Terminate diff --git a/src/map/pc.h b/src/map/pc.h index c8d648a360..e5520c2b6b 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -796,6 +796,10 @@ struct { struct s_params { uint16 str, agi, vit, int_, dex, luk; } max_param; + struct s_job_noenter_map { + uint32 zone; + uint8 group_lv; + } noenter_map; } job_info[CLASS_COUNT]; #define EQP_WEAPON EQP_HAND_R @@ -1262,6 +1266,8 @@ void pc_validate_skill(struct map_session_data *sd); void pc_show_questinfo(struct map_session_data *sd); void pc_show_questinfo_reinit(struct map_session_data *sd); +bool pc_job_can_entermap(enum e_job jobid, int m, int group_lv); + #if defined(RENEWAL_DROP) || defined(RENEWAL_EXP) int pc_level_penalty_mod(int level_diff, uint32 mob_class, enum e_mode mode, int type); #endif diff --git a/src/map/script.c b/src/map/script.c index c0bf42735c..01ce302889 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -5633,7 +5633,7 @@ BUILDIN_FUNC(warpparty) TBL_PC *pl_sd; struct party_data* p; int type; - int mapindex; + int mapindex = 0, m = -1; int i; const char* str = script_getstr(st,2); @@ -5662,18 +5662,21 @@ BUILDIN_FUNC(warpparty) return SCRIPT_CMD_FAILURE; pl_sd = p->data[i].sd; mapindex = pl_sd->mapindex; + m = map_mapindex2mapid(mapindex); x = pl_sd->bl.x; y = pl_sd->bl.y; break; case 4: mapindex = mapindex_name2id(str); + if (!mapindex) {// Invalid map + return SCRIPT_CMD_FAILURE; + } + m = map_mapindex2mapid(mapindex); break; case 2: //"SavePoint" uses save point of the currently attached player if (( sd = script_rid2sd(st) ) == NULL ) return SCRIPT_CMD_SUCCESS; - default: - mapindex = 0; break; } @@ -5704,7 +5707,7 @@ BUILDIN_FUNC(warpparty) break; case 3: // Leader case 4: // m,x,y - if(!map[pl_sd->bl.m].flag.noreturn && !map[pl_sd->bl.m].flag.nowarp) + if(!map[pl_sd->bl.m].flag.noreturn && !map[pl_sd->bl.m].flag.nowarp && pc_job_can_entermap((enum e_job)pl_sd->status.class_, m, pl_sd->group_level)) pc_setpos(pl_sd,mapindex,x,y,CLR_TELEPORT); break; } @@ -5723,7 +5726,7 @@ BUILDIN_FUNC(warpguild) TBL_PC *pl_sd; struct guild* g; struct s_mapiterator* iter; - int type; + int type, mapindex = 0, m = -1; const char* str = script_getstr(st,2); int x = script_getnum(st,3); @@ -5744,6 +5747,15 @@ BUILDIN_FUNC(warpguild) return SCRIPT_CMD_SUCCESS; } + switch (type) { + case 3: + mapindex = mapindex_name2id(str); + if (!mapindex) + return SCRIPT_CMD_FAILURE; + m = map_mapindex2mapid(mapindex); + break; + } + iter = mapit_getallusers(); for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) ) { @@ -5765,8 +5777,8 @@ BUILDIN_FUNC(warpguild) pc_setpos(pl_sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT); break; case 3: // m,x,y - if(!map[pl_sd->bl.m].flag.noreturn && !map[pl_sd->bl.m].flag.nowarp) - pc_setpos(pl_sd,mapindex_name2id(str),x,y,CLR_TELEPORT); + if(!map[pl_sd->bl.m].flag.noreturn && !map[pl_sd->bl.m].flag.nowarp && pc_job_can_entermap((enum e_job)pl_sd->status.class_, m, pl_sd->group_level)) + pc_setpos(pl_sd,mapindex,x,y,CLR_TELEPORT); break; } } @@ -21681,6 +21693,44 @@ BUILDIN_FUNC(needed_status_point) { script_pushint(st, pc_need_status_point(sd, type, val)); return SCRIPT_CMD_SUCCESS; } + +/** + * jobcanentermap(""{,}); + * Check if (player with) JobID can enter the map. + * @param mapname Map name + * @param JobID Player's JobID (optional) + **/ +BUILDIN_FUNC(jobcanentermap) { + const char *mapname = script_getstr(st, 2); + int mapidx = mapindex_name2id(mapname), m = -1; + int jobid = 0; + TBL_PC *sd = NULL; + + if (!mapidx) {// Invalid map + script_pushint(st, false); + return SCRIPT_CMD_FAILURE; + } + m = map_mapindex2mapid(mapidx); + if (m == -1) { // Map is on different map server + ShowError("buildin_jobcanentermap: Map '%s' is not found in this server.\n", mapname); + script_pushint(st, false); + return SCRIPT_CMD_FAILURE; + } + + if (script_hasdata(st, 3)) { + jobid = script_getnum(st, 3); + } else { + if (!(sd = script_rid2sd(st))) { + script_pushint(st, false); + return SCRIPT_CMD_FAILURE; + } + jobid = sd->status.class_; + } + + script_pushint(st, pc_job_can_entermap((enum e_job)jobid, m, sd ? sd->group_level : 0)); + return SCRIPT_CMD_SUCCESS; +} + #include "../custom/script.inc" // declarations that were supposed to be exported from npc_chat.c @@ -22264,6 +22314,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(getequiprandomoption, "iii?"), BUILDIN_DEF(setrandomoption,"iiiii?"), BUILDIN_DEF(needed_status_point,"ii?"), + BUILDIN_DEF(jobcanentermap,"s?"), #include "../custom/script_def.inc" diff --git a/src/map/skill.c b/src/map/skill.c index 0574073a37..5184f9c9ad 100755 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -8603,6 +8603,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui if ((dstsd = g->member[i].sd) != NULL && sd != dstsd && !dstsd->state.autotrade && !pc_isdead(dstsd)) { if (map[dstsd->bl.m].flag.nowarp && !map_flag_gvg2(dstsd->bl.m)) continue; + if (!pc_job_can_entermap((enum e_job)dstsd->status.class_, src->m, dstsd->group_level)) + continue; if(map_getcell(src->m,src->x+dx[j],src->y+dy[j],CELL_CHKNOREACH)) dx[j] = dy[j] = 0; if (!pc_setpos(dstsd, map_id2index(src->m), src->x+dx[j], src->y+dy[j], CLR_RESPAWN)) @@ -13141,7 +13143,8 @@ static int skill_unit_onplace(struct skill_unit *unit, struct block_list *bl, un sg->val1 = (count<<16)|working; - pc_setpos(sd,m,x,y,CLR_TELEPORT); + if (pc_job_can_entermap((enum e_job)sd->status.class_, map_mapindex2mapid(m), sd->group_level)) + pc_setpos(sd,m,x,y,CLR_TELEPORT); } } else if(bl->type == BL_MOB && battle_config.mob_warp&2) { int16 m = map_mapindex2mapid(sg->val3); @@ -18122,13 +18125,13 @@ static int skill_unit_timer_sub(DBKey key, DBData *data, va_list ap) if(group->val1) { sd = map_charid2sd(group->val1); group->val1 = 0; - if (sd && !map[sd->bl.m].flag.nowarp) + if (sd && !map[sd->bl.m].flag.nowarp && pc_job_can_entermap((enum e_job)sd->status.class_, unit->bl.m, sd->group_level)) pc_setpos(sd,map_id2index(unit->bl.m),unit->bl.x,unit->bl.y,CLR_TELEPORT); } if(group->val2) { sd = map_charid2sd(group->val2); group->val2 = 0; - if (sd && !map[sd->bl.m].flag.nowarp) + if (sd && !map[sd->bl.m].flag.nowarp && pc_job_can_entermap((enum e_job)sd->status.class_, unit->bl.m, sd->group_level)) pc_setpos(sd,map_id2index(unit->bl.m),unit->bl.x,unit->bl.y,CLR_TELEPORT); } skill_delunit(unit); diff --git a/vcproj-10/map-server.vcxproj b/vcproj-10/map-server.vcxproj index 21460d653b..641c198152 100644 --- a/vcproj-10/map-server.vcxproj +++ b/vcproj-10/map-server.vcxproj @@ -325,6 +325,7 @@ + diff --git a/vcproj-12/map-server.vcxproj b/vcproj-12/map-server.vcxproj index e6e8bd3e85..9860aeb46a 100644 --- a/vcproj-12/map-server.vcxproj +++ b/vcproj-12/map-server.vcxproj @@ -329,6 +329,7 @@ + diff --git a/vcproj-13/map-server.vcxproj b/vcproj-13/map-server.vcxproj index 781402cc52..55b9440719 100644 --- a/vcproj-13/map-server.vcxproj +++ b/vcproj-13/map-server.vcxproj @@ -329,6 +329,7 @@ + diff --git a/vcproj-14/map-server.vcxproj b/vcproj-14/map-server.vcxproj index 45546eff48..9fd854c7e0 100644 --- a/vcproj-14/map-server.vcxproj +++ b/vcproj-14/map-server.vcxproj @@ -327,6 +327,7 @@ +