diff options
author | gepard1984 <gepard1984@54d463be-8e91-2dee-dedb-b68131a5f0ec> | 2012-02-13 01:19:04 +0000 |
---|---|---|
committer | gepard1984 <gepard1984@54d463be-8e91-2dee-dedb-b68131a5f0ec> | 2012-02-13 01:19:04 +0000 |
commit | 526217d77d50dc27b0815e3d5895df7bfa38ff76 (patch) | |
tree | 9fb6152ef59b7d08e7f226fbdc47eb6ba9617cc6 /src/map | |
parent | 87469dc59de62990878ce6ccd29769ebd5b7d675 (diff) | |
download | hercules-526217d77d50dc27b0815e3d5895df7bfa38ff76.tar.gz hercules-526217d77d50dc27b0815e3d5895df7bfa38ff76.tar.bz2 hercules-526217d77d50dc27b0815e3d5895df7bfa38ff76.tar.xz hercules-526217d77d50dc27b0815e3d5895df7bfa38ff76.zip |
- Added `libconfig` (configuration file library: http://www.hyperrealm.com/libconfig/):
- Updated VS9/10 project files.
- Updated `configure` & `Makefile`s.
- New GM, Commands & Permissions system:
- '''This is a backwards compatibility breaking update''', please read tid:58877
- Replaced GM levels with Player Groups.
- Commands permissions & other privileges now depend on group, not GM level.
- `@help` command improvements: requires "commandname" param and shows more detailed info about commands.
- Modified GM whisper system to deliver messages basing on permissions, not GM level.
- Remote trade request is now possible only if player is allowed to use `@trade` command as well.
- Added a proper permission to use `/changemaptype` command.
- `clif_displaymessage` is now capable of displaying multiline messages.
- All `ACMD_FUNC`s are static now, and the only way to invoke them is with `is_atcommand()`; all client commands (starting with `/`) are now translated into corresponding atcommands (with exception of `/kick` used on monster, as there is no atcommand to kill single monster).
- Removed nonsense "bot check" triggering when player blocked (`/ex`) Server.
- Merged `@monster`, `@monsterbig` and `@monstersmall`.
- Improved flow of atcommand execution to avoid revealing info about online players or existing commands to non-privileged players.
- Merged `atcommand` and `charcommand` script functions (`charcommand` is aliased to `atcommand`).
- Fixed `atcommand` script function reading unknown memory area (possible access violation).
git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@15572 54d463be-8e91-2dee-dedb-b68131a5f0ec
Diffstat (limited to 'src/map')
-rw-r--r-- | src/map/Makefile.in | 24 | ||||
-rw-r--r-- | src/map/atcommand.c | 1587 | ||||
-rw-r--r-- | src/map/atcommand.h | 30 | ||||
-rw-r--r-- | src/map/battle.c | 26 | ||||
-rw-r--r-- | src/map/battle.h | 27 | ||||
-rw-r--r-- | src/map/buyingstore.c | 10 | ||||
-rw-r--r-- | src/map/chat.c | 4 | ||||
-rw-r--r-- | src/map/chrif.c | 8 | ||||
-rw-r--r-- | src/map/clif.c | 499 | ||||
-rw-r--r-- | src/map/clif.h | 2 | ||||
-rw-r--r-- | src/map/intif.c | 34 | ||||
-rw-r--r-- | src/map/intif.h | 2 | ||||
-rw-r--r-- | src/map/log.c | 15 | ||||
-rw-r--r-- | src/map/log.h | 4 | ||||
-rw-r--r-- | src/map/mail.c | 8 | ||||
-rw-r--r-- | src/map/party.c | 8 | ||||
-rw-r--r-- | src/map/pc.c | 144 | ||||
-rw-r--r-- | src/map/pc.h | 37 | ||||
-rw-r--r-- | src/map/pc_groups.c | 459 | ||||
-rw-r--r-- | src/map/pc_groups.h | 20 | ||||
-rw-r--r-- | src/map/script.c | 59 | ||||
-rw-r--r-- | src/map/skill.c | 13 | ||||
-rw-r--r-- | src/map/storage.c | 8 | ||||
-rw-r--r-- | src/map/trade.c | 30 | ||||
-rw-r--r-- | src/map/vending.c | 4 |
25 files changed, 1506 insertions, 1556 deletions
diff --git a/src/map/Makefile.in b/src/map/Makefile.in index dc0e48bf9..16675086d 100644 --- a/src/map/Makefile.in +++ b/src/map/Makefile.in @@ -4,13 +4,13 @@ COMMON_OBJ = ../common/obj_all/core.o ../common/obj_all/socket.o ../common/obj_a ../common/obj_all/nullpo.o ../common/obj_all/malloc.o ../common/obj_all/showmsg.o \ ../common/obj_all/utils.o ../common/obj_all/strlib.o ../common/obj_all/grfio.o \ ../common/obj_all/mapindex.o ../common/obj_all/ers.o ../common/obj_all/md5calc.o \ - ../common/obj_all/random.o ../common/obj_all/des.o + ../common/obj_all/random.o ../common/obj_all/des.o ../common/obj_all/conf.o COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h \ ../common/db.h ../common/plugins.h ../common/lock.h \ ../common/nullpo.h ../common/malloc.h ../common/showmsg.h \ ../common/utils.h ../common/strlib.h ../common/grfio.h \ ../common/mapindex.h ../common/ers.h ../common/md5calc.h \ - ../common/random.h ../common/des.h + ../common/random.h ../common/des.h ../common/conf.h COMMON_SQL_OBJ = ../common/obj_sql/sql.o COMMON_SQL_H = ../common/sql.h @@ -19,12 +19,19 @@ MT19937AR_OBJ = ../../3rdparty/mt19937ar/mt19937ar.o MT19937AR_H = ../../3rdparty/mt19937ar/mt19937ar.h MT19937AR_INCLUDE = -I../../3rdparty/mt19937ar +LIBCONFIG_OBJ = ../../3rdparty/libconfig/libconfig.o ../../3rdparty/libconfig/grammar.o \ + ../../3rdparty/libconfig/scanctx.o ../../3rdparty/libconfig/scanner.o ../../3rdparty/libconfig/strbuf.o +LIBCONFIG_H = ../../3rdparty/libconfig/libconfig.h ../../3rdparty/libconfig/grammar.h \ + ../../3rdparty/libconfig/parsectx.h ../../3rdparty/libconfig/scanctx.h ../../3rdparty/libconfig/scanner.h \ + ../../3rdparty/libconfig/strbuf.h ../../3rdparty/libconfig/wincompat.h +LIBCONFIG_INCLUDE = -I../../3rdparty/libconfig + MAP_OBJ = map.o chrif.o clif.o pc.o status.o npc.o \ npc_chat.o chat.o path.o itemdb.o mob.o script.o \ storage.o skill.o atcommand.o battle.o battleground.o \ intif.o trade.o party.o vending.o guild.o pet.o \ log.o mail.o date.o unit.o homunculus.o mercenary.o quest.o instance.o \ - buyingstore.o searchstore.o duel.o + buyingstore.o searchstore.o duel.o pc_groups.o MAP_SQL_OBJ = $(MAP_OBJ:%=obj_sql/%) \ obj_sql/mapreg_sql.o MAP_H = map.h chrif.h clif.h pc.h status.h npc.h \ @@ -32,7 +39,7 @@ MAP_H = map.h chrif.h clif.h pc.h status.h npc.h \ storage.h skill.h atcommand.h battle.h battleground.h \ intif.h trade.h party.h vending.h guild.h pet.h \ log.h mail.h date.h unit.h homunculus.h mercenary.h quest.h instance.h mapreg.h \ - buyingstore.h searchstore.h duel.h \ + buyingstore.h searchstore.h duel.h pc_groups.h \ config/Core.h config/Renewal.h config/Secure.h config/Data/Const.h \ config/Skills/General.h config/Skills/Mage_Classes.h config/Skills/Swordsman_Classes.h @@ -95,12 +102,12 @@ obj_sql: # executables map-server_sql: obj_sql $(MAP_SQL_OBJ) $(COMMON_OBJ) $(COMMON_SQL_OBJ) - @CC@ @LDFLAGS@ -o ../../map-server_sql@EXEEXT@ $(MAP_SQL_OBJ) $(COMMON_OBJ) $(COMMON_SQL_OBJ) $(MT19937AR_OBJ) @LIBS@ @PCRE_LIBS@ @MYSQL_LIBS@ + @CC@ @LDFLAGS@ -o ../../map-server_sql@EXEEXT@ $(MAP_SQL_OBJ) $(COMMON_OBJ) $(COMMON_SQL_OBJ) $(MT19937AR_OBJ) $(LIBCONFIG_OBJ) @LIBS@ @PCRE_LIBS@ @MYSQL_LIBS@ # map object files -obj_sql/%.o: %.c $(MAP_H) $(COMMON_H) $(COMMON_SQL_H) $(MT19937AR_H) - @CC@ @CFLAGS@ $(MT19937AR_INCLUDE) $(PCRE_CFLAGS) @MYSQL_CFLAGS@ @CPPFLAGS@ -c $(OUTPUT_OPTION) $< +obj_sql/%.o: %.c $(MAP_H) $(COMMON_H) $(COMMON_SQL_H) $(MT19937AR_H) $(LIBCONFIG_H) + @CC@ @CFLAGS@ $(MT19937AR_INCLUDE) $(LIBCONFIG_INCLUDE) $(PCRE_CFLAGS) @MYSQL_CFLAGS@ @CPPFLAGS@ -c $(OUTPUT_OPTION) $< # missing object files ../common/obj_all/%.o: @@ -111,3 +118,6 @@ obj_sql/%.o: %.c $(MAP_H) $(COMMON_H) $(COMMON_SQL_H) $(MT19937AR_H) MT19937AR_OBJ: @$(MAKE) -C ../../3rdparty/mt19937ar + +LIBCONFIG_OBJ: + @$(MAKE) -C ../../3rdparty/libconfig diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 701ef0ee8..44249e8af 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -12,6 +12,7 @@ #include "../common/socket.h" #include "../common/strlib.h" #include "../common/utils.h" +#include "../common/conf.h" #include "atcommand.h" #include "battle.h" @@ -24,6 +25,7 @@ #include "log.h" #include "map.h" #include "pc.h" +#include "pc_groups.h" // groupid2name #include "status.h" #include "skill.h" #include "mob.h" @@ -39,54 +41,43 @@ #include "trade.h" #include "unit.h" - #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> -// extern variables -char atcommand_symbol = '@'; // first char of the commands -char charcommand_symbol = '#'; -char* msg_table[MAX_MSG]; // Server messages (0-499 reserved for GM commands, 500-999 reserved for others) - -// local declarations -#define ACMD_FUNC(x) int atcommand_ ## x (const int fd, struct map_session_data* sd, const char* command, const char* message) - -DBMap* atcommand_db = NULL;//name -> AtCommandInfo #define ATCOMMAND_LENGTH 50 +#define ACMD_FUNC(x) static int atcommand_ ## x (const int fd, struct map_session_data* sd, const char* command, const char* message) +#define MAX_MSG 1000 -typedef struct AtCommandInfo { - char command[ATCOMMAND_LENGTH]; - int level; - int level2; +typedef struct AtCommandInfo AtCommandInfo; +typedef struct AliasInfo AliasInfo; + +struct AtCommandInfo { + char command[ATCOMMAND_LENGTH]; AtCommandFunc func; -} AtCommandInfo; +}; -static AtCommandInfo* get_atcommandinfo_byname(const char* name); +struct AliasInfo { + AtCommandInfo *command; + char alias[ATCOMMAND_LENGTH]; +}; -ACMD_FUNC(commands); -ACMD_FUNC(charcommands); -/*========================================= - * Generic variables - *-----------------------------------------*/ -char atcmd_output[CHAT_SIZE_MAX]; -char atcmd_player_name[NAME_LENGTH]; -char atcmd_temp[100]; +char atcommand_symbol = '@'; // first char of the commands +char charcommand_symbol = '#'; -// compare function for sorting high to lowest -int hightolow_compare (const void * a, const void * b) -{ - return ( *(int*)b - *(int*)a ); -} +static char* msg_table[MAX_MSG]; // Server messages (0-499 reserved for GM commands, 500-999 reserved for others) +static DBMap* atcommand_db = NULL; //name -> AtCommandInfo +static DBMap* atcommand_alias_db = NULL; //alias -> AtCommandInfo +static config_t atcommand_config; -// compare function for sorting lowest to highest -int lowtohigh_compare (const void * a, const void * b) -{ - return ( *(int*)a - *(int*)b ); -} +static char atcmd_output[CHAT_SIZE_MAX]; +static char atcmd_player_name[NAME_LENGTH]; + +static AtCommandInfo* get_atcommandinfo_byname(const char *name); // @help +static const char* atcommand_checkalias(const char *aliasname); // @help //----------------------------------------------------------- // Return the message string of the specified number by [Yor] @@ -100,26 +91,6 @@ const char* msg_txt(int msg_number) return "??"; } -//----------------------------------------------------------- -// Returns Players title (from msg_athena.conf) [Lupus] -//----------------------------------------------------------- -static char* player_title_txt(int level) -{ - const char* format; - format = (level >= battle_config.title_lvl8) ? msg_txt(342) - : (level >= battle_config.title_lvl7) ? msg_txt(341) - : (level >= battle_config.title_lvl6) ? msg_txt(340) - : (level >= battle_config.title_lvl5) ? msg_txt(339) - : (level >= battle_config.title_lvl4) ? msg_txt(338) - : (level >= battle_config.title_lvl3) ? msg_txt(337) - : (level >= battle_config.title_lvl2) ? msg_txt(336) - : (level >= battle_config.title_lvl1) ? msg_txt(335) - : ""; - sprintf(atcmd_temp, format, level); - return atcmd_temp; -} - - /*========================================== * Read Message Data *------------------------------------------*/ @@ -447,11 +418,11 @@ 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 && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + if (map[m].flag.nowarpto && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) { clif_displaymessage(fd, msg_txt(247)); return -1; } - if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) { clif_displaymessage(fd, msg_txt(248)); return -1; } @@ -480,9 +451,9 @@ ACMD_FUNC(where) } pl_sd = map_nick2sd(atcmd_player_name); - if( pl_sd == NULL - || strncmp(pl_sd->status.name,atcmd_player_name,NAME_LENGTH) != 0 - || (battle_config.hide_GM_session && pc_isGM(sd) < pc_isGM(pl_sd) && !(battle_config.who_display_aid && pc_isGM(sd) >= battle_config.who_display_aid)) + if (pl_sd == NULL || + strncmp(pl_sd->status.name, atcmd_player_name, NAME_LENGTH) != 0 || + (pc_has_permission(pl_sd, PC_PERM_HIDE_SESSION) && pc_get_group_level(pl_sd) > pc_get_group_level(sd) && !pc_has_permission(sd, PC_PERM_WHO_DISPLAY_AID)) ) { clif_displaymessage(fd, msg_txt(3)); // Character not found. return -1; @@ -520,13 +491,13 @@ ACMD_FUNC(jumpto) return -1; } - if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) + if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarpto && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) { clif_displaymessage(fd, msg_txt(247)); // You are not authorized to warp to this map. return -1; } - if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) { clif_displaymessage(fd, msg_txt(248)); // You are not authorized to warp from your current map. return -1; @@ -558,7 +529,7 @@ ACMD_FUNC(jump) sscanf(message, "%hd %hd", &x, &y); - if (map[sd->bl.m].flag.noteleport && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + if (map[sd->bl.m].flag.noteleport && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) { clif_displaymessage(fd, msg_txt(248)); // You are not authorized to warp from your current map. return -1; } @@ -591,7 +562,7 @@ ACMD_FUNC(who3) struct map_session_data *pl_sd; struct s_mapiterator* iter; int j, count; - int pl_GM_level, GM_level; + int level; char match_text[100]; char player_name[NAME_LENGTH]; @@ -607,20 +578,19 @@ ACMD_FUNC(who3) match_text[j] = TOLOWER(match_text[j]); count = 0; - GM_level = pc_isGM(sd); + level = pc_get_group_level(sd); iter = mapit_getallusers(); for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) ) { - pl_GM_level = pc_isGM(pl_sd); - if(!( (battle_config.hide_GM_session || (pl_sd->sc.option & OPTION_INVISIBLE)) && pl_GM_level > GM_level )) + if (!((pc_has_permission(pl_sd, PC_PERM_HIDE_SESSION) || (pl_sd->sc.option & OPTION_INVISIBLE)) && pc_get_group_level(pl_sd) > level)) {// you can look only lower or same level memcpy(player_name, pl_sd->status.name, NAME_LENGTH); for (j = 0; player_name[j]; j++) player_name[j] = TOLOWER(player_name[j]); if (strstr(player_name, match_text) != NULL) { // search with no case sensitive - if (battle_config.who_display_aid > 0 && pc_isGM(sd) >= battle_config.who_display_aid) { + if (pc_has_permission(sd, PC_PERM_WHO_DISPLAY_AID)) { sprintf(atcmd_output, "(CID:%d/AID:%d) ", pl_sd->status.char_id, pl_sd->status.account_id); } else { atcmd_output[0]=0; @@ -629,9 +599,8 @@ ACMD_FUNC(who3) sprintf(temp0, msg_txt(343), pl_sd->status.name); strcat(atcmd_output,temp0); //Player title, if exists - if (pl_GM_level > 0) { - //sprintf(temp0, "(%s) ", player_title_txt(pl_GM_level) ); - sprintf(temp0, msg_txt(344), player_title_txt(pl_GM_level) ); + if (pl_sd->group_id > 0) { + sprintf(temp0, msg_txt(344), pc_group_id2name(pl_sd->group_id) ); strcat(atcmd_output,temp0); } //Players Location: map x y @@ -666,7 +635,7 @@ ACMD_FUNC(who2) struct map_session_data *pl_sd; struct s_mapiterator* iter; int j, count; - int pl_GM_level, GM_level; + int pl_level, level; char match_text[100]; char player_name[NAME_LENGTH]; @@ -682,13 +651,13 @@ ACMD_FUNC(who2) match_text[j] = TOLOWER(match_text[j]); count = 0; - GM_level = pc_isGM(sd); + level = pc_get_group_level(sd); iter = mapit_getallusers(); for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) ) { - pl_GM_level = pc_isGM(pl_sd); - if(!( (battle_config.hide_GM_session || (pl_sd->sc.option & OPTION_INVISIBLE)) && (pl_GM_level > GM_level) )) + pl_level = pc_get_group_level(pl_sd); + if (!((pc_has_permission(pl_sd, PC_PERM_HIDE_SESSION) || (pl_sd->sc.option & OPTION_INVISIBLE)) && pc_get_group_level(pl_sd) > level)) {// you can look only lower or same level memcpy(player_name, pl_sd->status.name, NAME_LENGTH); for (j = 0; player_name[j]; j++) @@ -698,9 +667,8 @@ ACMD_FUNC(who2) //sprintf(atcmd_output, "Name: %s ", pl_sd->status.name); sprintf(atcmd_output, msg_txt(343), pl_sd->status.name); //Player title, if exists - if (pl_GM_level > 0) { - //sprintf(temp0, "(%s) ", player_title_txt(pl_GM_level) ); - sprintf(temp0, msg_txt(344), player_title_txt(pl_GM_level) ); + if (pl_sd->group_id > 0) { + sprintf(temp0, msg_txt(344), pc_group_id2name(pl_sd->group_id) ); strcat(atcmd_output,temp0); } //Players Base Level / Job name @@ -736,7 +704,7 @@ ACMD_FUNC(who) struct map_session_data *pl_sd; struct s_mapiterator* iter; int j, count; - int pl_GM_level, GM_level; + int pl_level, level; char match_text[100]; char player_name[NAME_LENGTH]; struct guild *g; @@ -755,13 +723,13 @@ ACMD_FUNC(who) match_text[j] = TOLOWER(match_text[j]); count = 0; - GM_level = pc_isGM(sd); + level = pc_get_group_level(sd); iter = mapit_getallusers(); for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) ) { - pl_GM_level = pc_isGM(pl_sd); - if(!( (battle_config.hide_GM_session || (pl_sd->sc.option & OPTION_INVISIBLE)) && pl_GM_level > GM_level )) + pl_level = pc_get_group_level(pl_sd); + if (!((pc_has_permission(pl_sd, PC_PERM_HIDE_SESSION) || (pl_sd->sc.option & OPTION_INVISIBLE)) && pc_get_group_level(pl_sd) > level)) {// you can look only lower or same level memcpy(player_name, pl_sd->status.name, NAME_LENGTH); for (j = 0; player_name[j]; j++) @@ -772,8 +740,8 @@ ACMD_FUNC(who) //Players Name sprintf(atcmd_output, msg_txt(343), pl_sd->status.name); //Player title, if exists - if (pl_GM_level > 0) { - sprintf(temp0, msg_txt(344), player_title_txt(pl_GM_level) ); + if (pl_sd->group_id > 0) { + sprintf(temp0, msg_txt(344), pc_group_id2name(pl_sd->group_id) ); strcat(atcmd_output,temp0); } //Players Party if exists @@ -815,7 +783,7 @@ ACMD_FUNC(whomap3) struct map_session_data *pl_sd; struct s_mapiterator* iter; int count; - int pl_GM_level, GM_level; + int pl_level, level; int map_id; char map_name[MAP_NAME_LENGTH_EXT]; @@ -831,19 +799,19 @@ ACMD_FUNC(whomap3) } count = 0; - GM_level = pc_isGM(sd); + level = pc_get_group_level(sd); iter = mapit_getallusers(); for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) ) { - pl_GM_level = pc_isGM(pl_sd); + pl_level = pc_get_group_level(pl_sd); if( pl_sd->bl.m != map_id ) continue; - if( (battle_config.hide_GM_session || (pl_sd->sc.option & OPTION_INVISIBLE)) && (pl_GM_level > GM_level) ) + if ((pc_has_permission(pl_sd, PC_PERM_HIDE_SESSION) || (pl_sd->sc.option & OPTION_INVISIBLE)) && pl_level > level) continue; - if (pl_GM_level > 0) - sprintf(atcmd_output, "Name: %s (GM:%d) | Location: %s %d %d", pl_sd->status.name, pl_GM_level, mapindex_id2name(pl_sd->mapindex), pl_sd->bl.x, pl_sd->bl.y); + if (pl_level > 0) + sprintf(atcmd_output, "Name: %s (GM:%d) | Location: %s %d %d", pl_sd->status.name, pl_level, mapindex_id2name(pl_sd->mapindex), pl_sd->bl.x, pl_sd->bl.y); else sprintf(atcmd_output, "Name: %s | Location: %s %d %d", pl_sd->status.name, mapindex_id2name(pl_sd->mapindex), pl_sd->bl.x, pl_sd->bl.y); clif_displaymessage(fd, atcmd_output); @@ -871,7 +839,7 @@ ACMD_FUNC(whomap2) struct map_session_data *pl_sd; struct s_mapiterator* iter; int count; - int pl_GM_level, GM_level; + int pl_level, level; int map_id = 0; char map_name[MAP_NAME_LENGTH_EXT]; @@ -889,19 +857,19 @@ ACMD_FUNC(whomap2) } count = 0; - GM_level = pc_isGM(sd); + level = pc_get_group_level(sd); iter = mapit_getallusers(); for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) ) { - pl_GM_level = pc_isGM(pl_sd); + pl_level = pc_get_group_level(pl_sd); if( pl_sd->bl.m != map_id ) continue; - if( (battle_config.hide_GM_session || (pl_sd->sc.option & OPTION_INVISIBLE)) && (pl_GM_level > GM_level) ) + if ((pc_has_permission(pl_sd, PC_PERM_HIDE_SESSION) || (pl_sd->sc.option & OPTION_INVISIBLE)) && pl_level > level) continue; - if (pl_GM_level > 0) - sprintf(atcmd_output, "Name: %s (GM:%d) | BLvl: %d | Job: %s (Lvl: %d)", pl_sd->status.name, pl_GM_level, pl_sd->status.base_level, job_name(pl_sd->status.class_), pl_sd->status.job_level); + if (pl_level > 0) + sprintf(atcmd_output, "Name: %s (GM:%d) | BLvl: %d | Job: %s (Lvl: %d)", pl_sd->status.name, pl_level, pl_sd->status.base_level, job_name(pl_sd->status.class_), pl_sd->status.job_level); else sprintf(atcmd_output, "Name: %s | BLvl: %d | Job: %s (Lvl: %d)", pl_sd->status.name, pl_sd->status.base_level, job_name(pl_sd->status.class_), pl_sd->status.job_level); clif_displaymessage(fd, atcmd_output); @@ -931,7 +899,7 @@ ACMD_FUNC(whomap) struct map_session_data *pl_sd; struct s_mapiterator* iter; int count; - int pl_GM_level, GM_level; + int pl_level, level; int map_id = 0; char map_name[MAP_NAME_LENGTH_EXT]; struct guild *g; @@ -953,15 +921,15 @@ ACMD_FUNC(whomap) } count = 0; - GM_level = pc_isGM(sd); + level = pc_get_group_level(sd); iter = mapit_getallusers(); for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) ) { - pl_GM_level = pc_isGM(pl_sd); + pl_level = pc_get_group_level(pl_sd); if( pl_sd->bl.m != map_id ) continue; - if( (battle_config.hide_GM_session || (pl_sd->sc.option & OPTION_INVISIBLE)) && (pl_GM_level > GM_level) ) + if ((pc_has_permission(pl_sd, PC_PERM_HIDE_SESSION) || (pl_sd->sc.option & OPTION_INVISIBLE)) && pl_level > level) continue; g = guild_search(pl_sd->status.guild_id); @@ -974,8 +942,8 @@ ACMD_FUNC(whomap) sprintf(temp0, "None"); else sprintf(temp0, "%s", p->party.name); - if (pl_GM_level > 0) - sprintf(atcmd_output, "Name: %s (GM:%d) | Party: '%s' | Guild: '%s'", pl_sd->status.name, pl_GM_level, temp0, temp1); + if (pl_level > 0) + sprintf(atcmd_output, "Name: %s (GM:%d) | Party: '%s' | Guild: '%s'", pl_sd->status.name, pl_level, temp0, temp1); else sprintf(atcmd_output, "Name: %s | Party: '%s' | Guild: '%s'", pl_sd->status.name, temp0, temp1); clif_displaymessage(fd, atcmd_output); @@ -1003,7 +971,7 @@ ACMD_FUNC(whogm) struct map_session_data* pl_sd; struct s_mapiterator* iter; int j, count; - int pl_GM_level, GM_level; + int pl_level, level; char match_text[CHAT_SIZE_MAX]; char player_name[NAME_LENGTH]; struct guild *g; @@ -1021,13 +989,13 @@ ACMD_FUNC(whogm) match_text[j] = TOLOWER(match_text[j]); count = 0; - GM_level = pc_isGM(sd); + level = pc_get_group_level(sd); iter = mapit_getallusers(); for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) ) { - pl_GM_level = pc_isGM(pl_sd); - if (!pl_GM_level) + pl_level = pc_get_group_level(pl_sd); + if (!pl_level) continue; if (match_text[0]) @@ -1039,7 +1007,7 @@ ACMD_FUNC(whogm) if (strstr(player_name, match_text) == NULL) continue; } - if (pl_GM_level > GM_level) { + if (pl_level > level) { if (pl_sd->sc.option & OPTION_INVISIBLE) continue; sprintf(atcmd_output, "Name: %s (GM)", pl_sd->status.name); @@ -1049,7 +1017,7 @@ ACMD_FUNC(whogm) } sprintf(atcmd_output, "Name: %s (GM:%d) | Location: %s %d %d", - pl_sd->status.name, pl_GM_level, + pl_sd->status.name, pl_level, mapindex_id2name(pl_sd->mapindex), pl_sd->bl.x, pl_sd->bl.y); clif_displaymessage(fd, atcmd_output); @@ -1109,11 +1077,11 @@ ACMD_FUNC(load) nullpo_retr(-1, sd); m = map_mapindex2mapid(sd->status.save_point.map); - if (m >= 0 && map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + if (m >= 0 && map[m].flag.nowarpto && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) { clif_displaymessage(fd, msg_txt(249)); // You are not authorized to warp to your save map. return -1; } - if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) { clif_displaymessage(fd, msg_txt(248)); // You are not authorized to warp from your current map. return -1; } @@ -1552,7 +1520,7 @@ ACMD_FUNC(kill) return -1; } - if (pc_isGM(sd) < pc_isGM(pl_sd)) + if (pc_get_group_level(sd) < pc_get_group_level(pl_sd)) { // you can kill only lower or same level clif_displaymessage(fd, msg_txt(81)); // Your GM level don't authorise you to do this action on this player. return -1; @@ -1941,78 +1909,69 @@ ACMD_FUNC(joblevelup) *------------------------------------------*/ ACMD_FUNC(help) { - char buf[2048], w1[2048], w2[2048]; - int i, gm_level; - FILE* fp; - nullpo_retr(-1, sd); + config_setting_t *help; + const char *text = NULL; + const char *command_name = NULL; + char *default_command = "help"; - memset(buf, '\0', sizeof(buf)); + nullpo_retr(-1, sd); - if ((fp = fopen(help_txt, "r")) != NULL) { - clif_displaymessage(fd, msg_txt(26)); // Help commands: - gm_level = pc_isGM(sd); - while(fgets(buf, sizeof(buf), fp) != NULL) { - if (buf[0] == '/' && buf[1] == '/') - continue; - for (i = 0; buf[i] != '\0'; i++) { - if (buf[i] == '\r' || buf[i] == '\n') { - buf[i] = '\0'; - break; - } - } - if (sscanf(buf, "%2047[^:]:%2047[^\n]", w1, w2) < 2) - clif_displaymessage(fd, buf); - else if (gm_level >= atoi(w1)) - clif_displaymessage(fd, w2); - } - fclose(fp); - } else { - clif_displaymessage(fd, msg_txt(27)); // File help.txt not found. + help = config_lookup(&atcommand_config, "help"); + if (help == NULL) { + clif_displaymessage(fd, msg_txt(27)); // "Commands help is not available." return -1; } - return 0; -} + if (!message || !*message) { + command_name = default_command; // If no command_name specified, display help for @help. + } else { + if (*message == atcommand_symbol || *message == charcommand_symbol) + ++message; + command_name = atcommand_checkalias(message); + } -/*========================================== - * @help2 - Char commands [Kayla] - *------------------------------------------*/ -ACMD_FUNC(help2) -{ - char buf[2048], w1[2048], w2[2048]; - int i, gm_level; - FILE* fp; - nullpo_retr(-1, sd); + if (!pc_can_use_command(sd, command_name, COMMAND_ATCOMMAND)) { + sprintf(atcmd_output, msg_txt(153), message); // "%s is Unknown Command" + clif_displaymessage(fd, atcmd_output); + return -1; + } + + if (!config_setting_lookup_string(help, command_name, &text)) { + clif_displaymessage(fd, "There is no help for this command_name."); + return -1; + } - memset(buf, '\0', sizeof(buf)); + sprintf(atcmd_output, "Help for command %c%s:", atcommand_symbol, command_name); + clif_displaymessage(fd, atcmd_output); - if ((fp = fopen(help2_txt, "r")) != NULL) { - clif_displaymessage(fd, msg_txt(26)); // Help commands: - gm_level = pc_isGM(sd); - while(fgets(buf, sizeof(buf), fp) != NULL) { - if (buf[0] == '/' && buf[1] == '/') - continue; - for (i = 0; buf[i] != '\0'; i++) { - if (buf[i] == '\r' || buf[i] == '\n') { - buf[i] = '\0'; - break; - } + { // Display aliases + DBIterator* iter; + AtCommandInfo *command_info; + AliasInfo *alias_info = NULL; + StringBuf buf; + bool has_aliases = false; + + StringBuf_Init(&buf); + StringBuf_AppendStr(&buf, "Available aliases:"); + command_info = get_atcommandinfo_byname(command_name); + iter = db_iterator(atcommand_alias_db); + for (alias_info = (AliasInfo*)dbi_first(iter); dbi_exists(iter); alias_info = (AliasInfo*)dbi_next(iter)) { + if (alias_info->command == command_info) { + StringBuf_Printf(&buf, " %s", alias_info->alias); + has_aliases = true; } - if (sscanf(buf, "%2047[^:]:%2047[^\n]", w1, w2) < 2) - clif_displaymessage(fd, buf); - else if (gm_level >= atoi(w1)) - clif_displaymessage(fd, w2); } - fclose(fp); - } else { - clif_displaymessage(fd, msg_txt(27)); // File help.txt not found. - return -1; + iter->destroy(iter); + if (has_aliases) + clif_displaymessage(fd, StringBuf_Value(&buf)); + StringBuf_Destroy(&buf); } + // Display help contents + clif_displaymessage(fd, text); return 0; } - // helper function, used in foreach calls to stop auto-attack timers // parameter: '0' - everyone, 'id' - only those attacking someone with that id static int atcommand_stopattack(struct block_list *bl,va_list ap) @@ -2304,7 +2263,7 @@ ACMD_FUNC(go) nullpo_retr(-1, sd); - if( map[sd->bl.m].flag.nogo && battle_config.any_warp_GM_min_level > pc_isGM(sd) ) { + if( map[sd->bl.m].flag.nogo && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE) ) { clif_displaymessage(sd->fd,"You can not use @go on this map."); return 0; } @@ -2429,11 +2388,11 @@ ACMD_FUNC(go) if (town >= 0 && town < ARRAYLENGTH(data)) { m = map_mapname2mapid(data[town].map); - if (m >= 0 && map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + if (m >= 0 && map[m].flag.nowarpto && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) { clif_displaymessage(fd, msg_txt(247)); return -1; } - if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) { clif_displaymessage(fd, msg_txt(248)); return -1; } @@ -2458,6 +2417,7 @@ ACMD_FUNC(monster) { char name[NAME_LENGTH]; char monster[NAME_LENGTH]; + char eventname[EVENT_NAME_LENGTH] = ""; int mob_id; int number = 0; int count; @@ -2513,6 +2473,11 @@ ACMD_FUNC(monster) if (battle_config.atc_spawn_quantity_limit && number > battle_config.atc_spawn_quantity_limit) number = battle_config.atc_spawn_quantity_limit; + if (strcmp(command+1, "monstersmall") == 0) + strcpy(eventname, "2"); + else if (strcmp(command+1, "monsterbig") == 0) + strcpy(eventname, "4"); + if (battle_config.etc_log) ShowInfo("%s monster='%s' name='%s' id=%d count=%d (%d,%d)\n", command, monster, name, mob_id, number, sd->bl.x, sd->bl.y); @@ -2520,7 +2485,7 @@ ACMD_FUNC(monster) range = (int)sqrt((float)number) +2; // calculation of an odd number (+ 4 area around) for (i = 0; i < number; i++) { map_search_freecell(&sd->bl, 0, &mx, &my, range, range, 0); - k = mob_once_spawn(sd, sd->bl.m, mx, my, name, mob_id, 1, ""); + k = mob_once_spawn(sd, sd->bl.m, mx, my, name, mob_id, 1, eventname); count += (k != 0) ? 1 : 0; } @@ -2539,159 +2504,6 @@ ACMD_FUNC(monster) return 0; } -// small monster spawning [Valaris] -ACMD_FUNC(monstersmall) -{ - char name[NAME_LENGTH] = ""; - char monster[NAME_LENGTH] = ""; - int mob_id = 0; - int number = 0; - int x = 0; - int y = 0; - int count; - int i; - - nullpo_retr(-1, sd); - - if (!message || !*message) { - clif_displaymessage(fd, "Give a monster name/id please."); - return -1; - } - - if (sscanf(message, "\"%23[^\"]\" %23s %d %d %d", name, monster, &number, &x, &y) < 2 && - sscanf(message, "%23s \"%23[^\"]\" %d %d %d", monster, name, &number, &x, &y) < 2 && - sscanf(message, "%23s %d %23s %d %d", monster, &number, name, &x, &y) < 1) { - clif_displaymessage(fd, "Give a monster name/id please."); - return -1; - } - - // If monster identifier/name argument is a name - if ((mob_id = mobdb_searchname(monster)) == 0) // check name first (to avoid possible name begining by a number) - mob_id = atoi(monster); - - if (mob_id == 0) { - clif_displaymessage(fd, msg_txt(40)); - return -1; - } - - if (mob_id == MOBID_EMPERIUM) { - clif_displaymessage(fd, msg_txt(83)); // Cannot spawn emperium - return -1; - } - - if (mobdb_checkid(mob_id) == 0) { - clif_displaymessage(fd, "Invalid monster ID"); // Invalid Monster ID. - return -1; - } - - if (number <= 0) - number = 1; - - if( !name[0] ) - strcpy(name, "--ja--"); - - // If value of atcommand_spawn_quantity_limit directive is greater than or equal to 1 and quantity of monsters is greater than value of the directive - if (battle_config.atc_spawn_quantity_limit >= 1 && number > battle_config.atc_spawn_quantity_limit) - number = battle_config.atc_spawn_quantity_limit; - - count = 0; - for (i = 0; i < number; i++) { - int mx, my; - if (x <= 0) - mx = sd->bl.x + (rnd() % 11 - 5); - else - mx = x; - if (y <= 0) - my = sd->bl.y + (rnd() % 11 - 5); - else - my = y; - count += (mob_once_spawn(sd, sd->bl.m, mx, my, name, mob_id, 1, "2") != 0) ? 1 : 0; - } - - if (count != 0) - clif_displaymessage(fd, msg_txt(39)); // Monster Summoned!! - else - clif_displaymessage(fd, msg_txt(40)); // Invalid Monster ID. - - return 0; -} -// big monster spawning [Valaris] -ACMD_FUNC(monsterbig) -{ - char name[NAME_LENGTH] = ""; - char monster[NAME_LENGTH] = ""; - int mob_id = 0; - int number = 0; - int x = 0; - int y = 0; - int count; - int i; - - nullpo_retr(-1, sd); - - if (!message || !*message) { - clif_displaymessage(fd, "Give a monster name/id please."); - return -1; - } - - if (sscanf(message, "\"%23[^\"]\" %23s %d %d %d", name, monster, &number, &x, &y) < 2 && - sscanf(message, "%23s \"%23[^\"]\" %d %d %d", monster, name, &number, &x, &y) < 2 && - sscanf(message, "%23s %d %23s %d %d", monster, &number, name, &x, &y) < 1) { - clif_displaymessage(fd, "Give a monster name/id please."); - return -1; - } - - // If monster identifier/name argument is a name - if ((mob_id = mobdb_searchname(monster)) == 0) // check name first (to avoid possible name begining by a number) - mob_id = atoi(monster); - - if (mob_id == 0) { - clif_displaymessage(fd, msg_txt(40)); - return -1; - } - - if (mob_id == MOBID_EMPERIUM) { - clif_displaymessage(fd, msg_txt(83)); // Cannot spawn emperium - return -1; - } - - if (mobdb_checkid(mob_id) == 0) { - clif_displaymessage(fd, "Invalid monster ID"); // Invalid Monster ID. - return -1; - } - - if (number <= 0) - number = 1; - - if( !name[0] ) - strcpy(name, "--ja--"); - - // If value of atcommand_spawn_quantity_limit directive is greater than or equal to 1 and quantity of monsters is greater than value of the directive - if (battle_config.atc_spawn_quantity_limit >= 1 && number > battle_config.atc_spawn_quantity_limit) - number = battle_config.atc_spawn_quantity_limit; - - count = 0; - for (i = 0; i < number; i++) { - int mx, my; - if (x <= 0) - mx = sd->bl.x + (rnd() % 11 - 5); - else - mx = x; - if (y <= 0) - my = sd->bl.y + (rnd() % 11 - 5); - else - my = y; - count += (mob_once_spawn(sd, sd->bl.m, mx, my, name, mob_id, 1, "4") != 0) ? 1 : 0; - } - - if (count != 0) - clif_displaymessage(fd, msg_txt(39)); // Monster Summoned!! - else - clif_displaymessage(fd, msg_txt(40)); // Invalid Monster ID. - - return 0; -} - /*========================================== * *------------------------------------------*/ @@ -3443,17 +3255,17 @@ ACMD_FUNC(recall) return -1; } - if ( pc_isGM(sd) < pc_isGM(pl_sd) ) + if ( pc_get_group_level(sd) < pc_get_group_level(pl_sd) ) { clif_displaymessage(fd, msg_txt(81)); // Your GM level doesn't authorize you to preform this action on the specified player. return -1; } - if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { - clif_displaymessage(fd, "You are not authorised to warp somenone to your actual map."); + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) { + clif_displaymessage(fd, "You are not authorised to warp someone to your actual map."); return -1; } - if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) { clif_displaymessage(fd, "You are not authorized to warp this player from its actual map."); return -1; } @@ -3571,7 +3383,7 @@ ACMD_FUNC(char_ban) tmtime->tm_min = tmtime->tm_min + minute; tmtime->tm_sec = tmtime->tm_sec + second; timestamp = mktime(tmtime); - if( timestamp <= time(NULL) && get_atcommand_level("unban") > pc_isGM(sd) ) { + if( timestamp <= time(NULL) && !pc_can_use_command(sd, "unban", COMMAND_ATCOMMAND) ) { clif_displaymessage(fd,"You are not allowed to reduce the length of a ban"); return -1; } @@ -3671,7 +3483,7 @@ ACMD_FUNC(doom) iter = mapit_getallusers(); for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) ) { - if (pl_sd->fd != fd && pc_isGM(sd) >= pc_isGM(pl_sd)) + if (pl_sd->fd != fd && pc_get_group_level(sd) >= pc_get_group_level(pl_sd)) { status_kill(&pl_sd->bl); clif_specialeffect(&pl_sd->bl,450,AREA); @@ -3698,7 +3510,7 @@ ACMD_FUNC(doommap) iter = mapit_getallusers(); for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) ) { - if (pl_sd->fd != fd && sd->bl.m == pl_sd->bl.m && pc_isGM(sd) >= pc_isGM(pl_sd)) + if (pl_sd->fd != fd && sd->bl.m == pl_sd->bl.m && pc_get_group_level(sd) >= pc_get_group_level(pl_sd)) { status_kill(&pl_sd->bl); clif_specialeffect(&pl_sd->bl,450,AREA); @@ -3788,7 +3600,7 @@ ACMD_FUNC(kick) return -1; } - if ( pc_isGM(sd) < pc_isGM(pl_sd) ) + if ( pc_get_group_level(sd) < pc_get_group_level(pl_sd) ) { clif_displaymessage(fd, msg_txt(81)); // Your GM level don't authorise you to do this action on this player. return -1; @@ -3811,7 +3623,7 @@ ACMD_FUNC(kickall) iter = mapit_getallusers(); for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) ) { - if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kick only lower or same gm level + if (pc_get_group_level(sd) >= pc_get_group_level(pl_sd)) { // you can kick only lower or same gm level if (sd->status.account_id != pl_sd->status.account_id) clif_GM_kick(NULL, pl_sd); } @@ -4104,7 +3916,7 @@ ACMD_FUNC(recallall) memset(atcmd_output, '\0', sizeof(atcmd_output)); - if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) { clif_displaymessage(fd, "You are not authorised to warp somenone to your actual map."); return -1; } @@ -4113,9 +3925,9 @@ ACMD_FUNC(recallall) iter = mapit_getallusers(); for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) ) { - if (sd->status.account_id != pl_sd->status.account_id && pc_isGM(sd) >= pc_isGM(pl_sd)) + if (sd->status.account_id != pl_sd->status.account_id && pc_get_group_level(sd) >= pc_get_group_level(pl_sd)) { - if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) + if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) count++; else { if (pc_isdead(pl_sd)) { //Wake them up @@ -4157,7 +3969,7 @@ ACMD_FUNC(guildrecall) return -1; } - if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) { clif_displaymessage(fd, "You are not authorised to warp somenone to your actual map."); return -1; } @@ -4176,9 +3988,9 @@ ACMD_FUNC(guildrecall) { if (sd->status.account_id != pl_sd->status.account_id && pl_sd->status.guild_id == g->guild_id) { - if (pc_isGM(pl_sd) > pc_isGM(sd)) + if (pc_get_group_level(pl_sd) > pc_get_group_level(sd)) continue; //Skip GMs greater than you. - if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) + if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) count++; else pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, CLR_RESPAWN); @@ -4216,7 +4028,7 @@ ACMD_FUNC(partyrecall) return -1; } - if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) { clif_displaymessage(fd, "You are not authorised to warp somenone to your actual map."); return -1; } @@ -4235,9 +4047,9 @@ ACMD_FUNC(partyrecall) { if (sd->status.account_id != pl_sd->status.account_id && pl_sd->status.party_id == p->party.party_id) { - if (pc_isGM(pl_sd) > pc_isGM(sd)) + if (pc_get_group_level(pl_sd) > pc_get_group_level(sd)) continue; //Skip GMs greater than you. - if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) + if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) count++; else pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, CLR_RESPAWN); @@ -4297,11 +4109,12 @@ ACMD_FUNC(reloadskilldb) } /*========================================== - * @reloadatcommand - reloads atcommand_athena.conf + * @reloadatcommand - reloads atcommand_athena.conf groups.conf *------------------------------------------*/ void atcommand_doload(); ACMD_FUNC(reloadatcommand) { atcommand_doload(); + pc_groups_reload(); clif_displaymessage(fd, msg_txt(254)); return 0; } @@ -4833,7 +4646,7 @@ ACMD_FUNC(nuke) } if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) { - if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kill only lower or same GM level + if (pc_get_group_level(sd) >= pc_get_group_level(pl_sd)) { // you can kill only lower or same GM level skill_castend_nodamage_id(&pl_sd->bl, &pl_sd->bl, NPC_SELFDESTRUCTION, 99, gettick(), 0); clif_displaymessage(fd, msg_txt(109)); // Player has been nuked! } else { @@ -5143,7 +4956,7 @@ ACMD_FUNC(jail) return -1; } - if (pc_isGM(sd) < pc_isGM(pl_sd)) + if (pc_get_group_level(sd) < pc_get_group_level(pl_sd)) { // you can jail only lower or same GM clif_displaymessage(fd, msg_txt(81)); // Your GM level don't authorise you to do this action on this player. return -1; @@ -5195,7 +5008,7 @@ ACMD_FUNC(unjail) return -1; } - if (pc_isGM(sd) < pc_isGM(pl_sd)) { // you can jail only lower or same GM + if (pc_get_group_level(sd) < pc_get_group_level(pl_sd)) { // you can jail only lower or same GM clif_displaymessage(fd, msg_txt(81)); // Your GM level don't authorise you to do this action on this player. return -1; @@ -5275,7 +5088,7 @@ ACMD_FUNC(jailfor) return -1; } - if (pc_isGM(pl_sd) > pc_isGM(sd)) { + if (pc_get_group_level(pl_sd) > pc_get_group_level(sd)) { clif_displaymessage(fd, msg_txt(81)); // Your GM level don't authorise you to do this action on this player. return -1; } @@ -5859,7 +5672,7 @@ ACMD_FUNC(useskill) return -1; } - if ( pc_isGM(sd) < pc_isGM(pl_sd) ) + if ( pc_get_group_level(sd) < pc_get_group_level(pl_sd) ) { clif_displaymessage(fd, msg_txt(81)); // Your GM level don't authorise you to do this action on this player. return -1; @@ -6817,76 +6630,29 @@ ACMD_FUNC(summon) } /*========================================== - * @adjcmdlvl by [MouseJstr] - * - * Temp adjust the GM level required to use a GM command - * Useful during beta testing to allow players to use GM commands for short periods of time - *------------------------------------------*/ -ACMD_FUNC(adjcmdlvl) -{ - int newlev, newremotelev; - char name[100]; - AtCommandInfo* cmd; - - nullpo_retr(-1, sd); - - if (!message || !*message || sscanf(message, "%d %d %99s", &newlev, &newremotelev, name) != 3) - { - clif_displaymessage(fd, "Usage: @adjcmdlvl <lvl> <remote lvl> <command>."); - return -1; - } - - cmd = get_atcommandinfo_byname(name); - if (cmd == NULL) - { - clif_displaymessage(fd, "@command not found."); - return -1; - } - else if (newlev > pc_isGM(sd) || newremotelev > pc_isGM(sd) ) - { - clif_displaymessage(fd, "You can't make a command require higher GM level than your own."); - return -1; - } - else if (cmd->level > pc_isGM(sd) || cmd->level2 > pc_isGM(sd) ) - { - clif_displaymessage(fd, "You can't adjust the level of a command which's level is above your own."); - return -1; - } - else - { - cmd->level = newlev; - cmd->level2 = newremotelev; - clif_displaymessage(fd, "@command level changed."); - return 0; - } -} - -/*========================================== - * @adjgmlvl by [MouseJstr] - * Create a temp GM + * @adjgroup + * Temporarily move player to another group * Useful during beta testing to allow players to use GM commands for short periods of time *------------------------------------------*/ -ACMD_FUNC(adjgmlvl) +ACMD_FUNC(adjgroup) { - int newlev; - char user[NAME_LENGTH]; - struct map_session_data *pl_sd; + int new_group = 0; nullpo_retr(-1, sd); - if (!message || !*message || sscanf(message, "%d %23[^\r\n]", &newlev, user) != 2) { - clif_displaymessage(fd, "Usage: @adjgmlvl <lvl> <user>."); + if (!message || !*message || sscanf(message, "%d", &new_group) != 1) { + clif_displaymessage(fd, "Usage: @adjgroup <group_id>"); return -1; } - if ( (pl_sd = map_nick2sd(user)) == NULL ) - { - clif_displaymessage(fd, msg_txt(3)); // Character not found. + if (!pc_group_exists(new_group)) { + clif_displaymessage(fd, "Specified group does not exists."); return -1; } - - pl_sd->gmlevel = newlev; - - return 0; + + sd->group_id = new_group; + clif_displaymessage(fd, "Group changed successfully."); + clif_displaymessage(sd->fd, "Your group has changed."); + return 0; } /*========================================== @@ -7023,7 +6789,7 @@ ACMD_FUNC(mute) return -1; } - if ( pc_isGM(sd) < pc_isGM(pl_sd) ) + if ( pc_get_group_level(sd) < pc_get_group_level(pl_sd) ) { clif_displaymessage(fd, msg_txt(81)); // Your GM level don't authorise you to do this action on this player. return -1; @@ -7819,7 +7585,7 @@ static int atcommand_mutearea_sub(struct block_list *bl,va_list ap) id = va_arg(ap, int); time = va_arg(ap, int); - if (id != bl->id && !pc_isGM(pl_sd)) { + if (id != bl->id && !pc_get_group_level(pl_sd)) { pl_sd->status.manner -= time; if (pl_sd->status.manner < 0) sc_start(&pl_sd->bl,SC_NOCHAT,100,0,0); @@ -8316,7 +8082,7 @@ ACMD_FUNC(clone) return 0; } - if(pc_isGM(pl_sd) > pc_isGM(sd)) { + if(pc_get_group_level(pl_sd) > pc_get_group_level(sd)) { clif_displaymessage(fd, msg_txt(126)); // Cannot clone a player of higher GM level than yourself. return 0; } @@ -8431,7 +8197,7 @@ ACMD_FUNC(request) } sprintf(atcmd_output, msg_txt(278), message); // (@request): %s - intif_wis_message_to_gm(sd->status.name, battle_config.lowest_gm_level, atcmd_output); + intif_wis_message_to_gm(sd->status.name, PC_PERM_RECEIVE_REQUESTS, atcmd_output); clif_disp_onlyself(sd, atcmd_output, strlen(atcmd_output)); clif_displaymessage(sd->fd,msg_txt(279)); // @request sent. return 0; @@ -8860,6 +8626,72 @@ ACMD_FUNC(font) return 0; } + +/*========================================== + * type: 1 = commands (@), 2 = charcommands (#) + *------------------------------------------*/ +static void atcommand_commands_sub(struct map_session_data* sd, const int fd, AtCommandType type) +{ + char line_buff[CHATBOX_SIZE]; + char* cur = line_buff; + AtCommandInfo* cmd; + DBIterator* iter = atcommand_db->iterator(atcommand_db); + int count = 0; + + memset(line_buff,' ',CHATBOX_SIZE); + line_buff[CHATBOX_SIZE-1] = 0; + + clif_displaymessage(fd, msg_txt(273)); // "Commands available:" + + for (cmd = (AtCommandInfo*)iter->first(iter, NULL); iter->exists(iter); cmd = (AtCommandInfo*)iter->next(iter, NULL)) { + unsigned int slen = 0; + + if (!pc_can_use_command(sd, cmd->command, type)) + continue; + + slen = strlen(cmd->command); + + // flush the text buffer if this command won't fit into it + if ( slen + cur - line_buff >= CHATBOX_SIZE ) + { + clif_displaymessage(fd,line_buff); + cur = line_buff; + memset(line_buff,' ',CHATBOX_SIZE); + line_buff[CHATBOX_SIZE-1] = 0; + } + + memcpy(cur,cmd->command,slen); + cur += slen+(10-slen%10); + + count++; + } + iter->destroy(iter); + clif_displaymessage(fd,line_buff); + + sprintf(atcmd_output, msg_txt(274), count); // "%d commands found." + clif_displaymessage(fd, atcmd_output); + + return; +} + +/*========================================== + * @commands Lists available @ commands to you + *------------------------------------------*/ +ACMD_FUNC(commands) +{ + atcommand_commands_sub(sd, fd, COMMAND_ATCOMMAND); + return 0; +} + +/*========================================== + * @charcommands Lists available # commands to you + *------------------------------------------*/ +ACMD_FUNC(charcommands) +{ + atcommand_commands_sub(sd, fd, COMMAND_CHARCOMMAND); + return 0; +} + ACMD_FUNC(new_mount) { clif_displaymessage(sd->fd,"NOTICE: If you crash with mount your LUA is outdated"); if( !(sd->sc.option&OPTION_MOUNTING) ) { @@ -8875,264 +8707,262 @@ ACMD_FUNC(new_mount) { /** * Fills the reference of available commands in atcommand DBMap **/ +#define ACMD_DEF(x) { #x, atcommand_ ## x } +#define ACMD_DEF2(x2, x) { x2, atcommand_ ## x } void atcommand_basecommands(void) { /** * Command reference list, place the base of your commands here - * Dev note: I'd love to get rid of this, if you have a better idea on this please share with the poor mortals **/ AtCommandInfo atcommand_base[] = { - { "warp", 40,40, atcommand_mapmove }, // + /mm - { "where", 1,1, atcommand_where }, - { "goto", 20,20, atcommand_jumpto }, // + /shift - { "jump", 40,40, atcommand_jump }, - { "who", 20,20, atcommand_who }, - { "who2", 20,20, atcommand_who2 }, - { "who3", 20,20, atcommand_who3 }, - { "whomap", 20,20, atcommand_whomap }, - { "whomap2", 20,20, atcommand_whomap2 }, - { "whomap3", 20,20, atcommand_whomap3 }, - { "whogm", 20,20, atcommand_whogm }, - { "save", 40,40, atcommand_save }, - { "load", 40,40, atcommand_load }, - { "speed", 40,40, atcommand_speed }, - { "storage", 1,1, atcommand_storage }, - { "gstorage", 50,50, atcommand_guildstorage }, - { "option", 40,40, atcommand_option }, - { "hide", 40,40, atcommand_hide }, // + /hide - { "job", 40,40, atcommand_jobchange }, - { "die", 1,1, atcommand_die }, - { "kill", 60,60, atcommand_kill }, - { "alive", 60,60, atcommand_alive }, - { "kami", 40,40, atcommand_kami }, - { "kamib", 40,40, atcommand_kami }, - { "kamic", 40,40, atcommand_kami }, - { "heal", 40,60, atcommand_heal }, - { "item", 60,60, atcommand_item }, - { "item2", 60,60, atcommand_item2 }, - { "itemreset", 40,40, atcommand_itemreset }, - { "blvl", 60,60, atcommand_baselevelup }, - { "jlvl", 60,60, atcommand_joblevelup }, - { "help", 20,20, atcommand_help }, - { "help2", 20,20, atcommand_help2 }, - { "pvpoff", 40,40, atcommand_pvpoff }, - { "pvpon", 40,40, atcommand_pvpon }, - { "gvgoff", 40,40, atcommand_gvgoff }, - { "gvgon", 40,40, atcommand_gvgon }, - { "model", 20,20, atcommand_model }, - { "go", 10,10, atcommand_go }, - { "monster", 50,50, atcommand_monster }, - { "monstersmall", 50,50, atcommand_monstersmall }, - { "monsterbig", 50,50, atcommand_monsterbig }, - { "killmonster", 60,60, atcommand_killmonster }, - { "killmonster2", 40,40, atcommand_killmonster2 }, - { "refine", 60,60, atcommand_refine }, - { "produce", 60,60, atcommand_produce }, - { "memo", 40,40, atcommand_memo }, - { "gat", 99,99, atcommand_gat }, - { "displaystatus", 99,99, atcommand_displaystatus }, - { "stpoint", 60,60, atcommand_statuspoint }, - { "skpoint", 60,60, atcommand_skillpoint }, - { "zeny", 60,60, atcommand_zeny }, - { "str", 60,60, atcommand_param }, - { "agi", 60,60, atcommand_param }, - { "vit", 60,60, atcommand_param }, - { "int", 60,60, atcommand_param }, - { "dex", 60,60, atcommand_param }, - { "luk", 60,60, atcommand_param }, - { "glvl", 60,60, atcommand_guildlevelup }, - { "makeegg", 60,60, atcommand_makeegg }, - { "hatch", 60,60, atcommand_hatch }, - { "petfriendly", 40,40, atcommand_petfriendly }, - { "pethungry", 40,40, atcommand_pethungry }, - { "petrename", 1,1, atcommand_petrename }, - { "recall", 60,60, atcommand_recall }, // + /recall - { "night", 80,80, atcommand_night }, - { "day", 80,80, atcommand_day }, - { "doom", 80,80, atcommand_doom }, - { "doommap", 80,80, atcommand_doommap }, - { "raise", 80,80, atcommand_raise }, - { "raisemap", 80,80, atcommand_raisemap }, - { "kick", 20,20, atcommand_kick }, // + right click menu for GM "(name) force to quit" - { "kickall", 99,99, atcommand_kickall }, - { "allskill", 60,60, atcommand_allskill }, - { "questskill", 40,40, atcommand_questskill }, - { "lostskill", 40,40, atcommand_lostskill }, - { "spiritball", 40,40, atcommand_spiritball }, - { "party", 1,1, atcommand_party }, - { "guild", 50,50, atcommand_guild }, - { "agitstart", 60,60, atcommand_agitstart }, - { "agitend", 60,60, atcommand_agitend }, - { "mapexit", 99,99, atcommand_mapexit }, - { "idsearch", 60,60, atcommand_idsearch }, - { "broadcast", 40,40, atcommand_broadcast }, // + /b and /nb - { "localbroadcast", 40,40, atcommand_localbroadcast }, // + /lb and /nlb - { "recallall", 80,80, atcommand_recallall }, - { "reloaditemdb", 99,99, atcommand_reloaditemdb }, - { "reloadmobdb", 99,99, atcommand_reloadmobdb }, - { "reloadskilldb", 99,99, atcommand_reloadskilldb }, - { "reloadscript", 99,99, atcommand_reloadscript }, - { "reloadatcommand", 99,99, atcommand_reloadatcommand }, - { "reloadbattleconf", 99,99, atcommand_reloadbattleconf }, - { "reloadstatusdb", 99,99, atcommand_reloadstatusdb }, - { "reloadpcdb", 99,99, atcommand_reloadpcdb }, - { "reloadmotd", 99,99, atcommand_reloadmotd }, - { "mapinfo", 99,99, atcommand_mapinfo }, - { "dye", 40,40, atcommand_dye }, - { "hairstyle", 40,40, atcommand_hair_style }, - { "haircolor", 40,40, atcommand_hair_color }, - { "allstats", 60,60, atcommand_stat_all }, - { "block", 60,60, atcommand_char_block }, - { "ban", 60,60, atcommand_char_ban }, - { "unblock", 60,60, atcommand_char_unblock }, - { "unban", 60,60, atcommand_char_unban }, - { "mount", 20,20, atcommand_mount_peco }, - { "guildspy", 60,60, atcommand_guildspy }, - { "partyspy", 60,60, atcommand_partyspy }, - { "repairall", 60,60, atcommand_repairall }, - { "guildrecall", 60,60, atcommand_guildrecall }, - { "partyrecall", 60,60, atcommand_partyrecall }, - { "nuke", 60,60, atcommand_nuke }, - { "shownpc", 80,80, atcommand_shownpc }, - { "hidenpc", 80,80, atcommand_hidenpc }, - { "loadnpc", 80,80, atcommand_loadnpc }, - { "unloadnpc", 80,80, atcommand_unloadnpc }, - { "time", 1,1, atcommand_servertime }, - { "jail", 60,60, atcommand_jail }, - { "unjail", 60,60, atcommand_unjail }, - { "jailfor", 60,60, atcommand_jailfor }, - { "jailtime", 1,1, atcommand_jailtime }, - { "disguise", 20,20, atcommand_disguise }, - { "undisguise", 20,20, atcommand_undisguise }, - { "email", 1,1, atcommand_email }, - { "effect", 40,40, atcommand_effect }, - { "follow", 20,20, atcommand_follow }, - { "addwarp", 60,60, atcommand_addwarp }, - { "skillon", 80,80, atcommand_skillon }, - { "skilloff", 80,80, atcommand_skilloff }, - { "killer", 60,60, atcommand_killer }, - { "npcmove", 80,80, atcommand_npcmove }, - { "killable", 40,40, atcommand_killable }, - { "dropall", 40,40, atcommand_dropall }, - { "storeall", 40,40, atcommand_storeall }, - { "skillid", 40,40, atcommand_skillid }, - { "useskill", 40,40, atcommand_useskill }, - { "displayskill", 99,99, atcommand_displayskill }, - { "snow", 99,99, atcommand_snow }, - { "sakura", 99,99, atcommand_sakura }, - { "clouds", 99,99, atcommand_clouds }, - { "clouds2", 99,99, atcommand_clouds2 }, - { "fog", 99,99, atcommand_fog }, - { "fireworks", 99,99, atcommand_fireworks }, - { "leaves", 99,99, atcommand_leaves }, - { "summon", 60,60, atcommand_summon }, - { "adjgmlvl", 99,99, atcommand_adjgmlvl }, - { "adjcmdlvl", 99,99, atcommand_adjcmdlvl }, - { "trade", 60,60, atcommand_trade }, - { "send", 99,99, atcommand_send }, - { "setbattleflag", 99,99, atcommand_setbattleflag }, - { "unmute", 80,80, atcommand_unmute }, - { "clearweather", 99,99, atcommand_clearweather }, - { "uptime", 1,1, atcommand_uptime }, - { "changesex", 60,60, atcommand_changesex }, - { "mute", 80,80, atcommand_mute }, - { "refresh", 1,1, atcommand_refresh }, - { "identify", 40,40, atcommand_identify }, - { "gmotd", 20,20, atcommand_gmotd }, - { "misceffect", 50,50, atcommand_misceffect }, - { "mobsearch", 10,10, atcommand_mobsearch }, - { "cleanmap", 40,40, atcommand_cleanmap }, - { "npctalk", 20,20, atcommand_npctalk }, - { "pettalk", 10,10, atcommand_pettalk }, - { "users", 40,40, atcommand_users }, - { "reset", 40,40, atcommand_reset }, - { "skilltree", 40,40, atcommand_skilltree }, - { "marry", 40,40, atcommand_marry }, - { "divorce", 40,40, atcommand_divorce }, - { "sound", 40,40, atcommand_sound }, - { "undisguiseall", 99,99, atcommand_undisguiseall }, - { "disguiseall", 99,99, atcommand_disguiseall }, - { "changelook", 60,60, atcommand_changelook }, - { "autoloot", 10,10, atcommand_autoloot }, - { "alootid", 10,10, atcommand_autolootitem }, - { "monsterinfo", 1,1, atcommand_mobinfo }, - { "exp", 1,1, atcommand_exp }, - { "adopt", 40,40, atcommand_adopt }, - { "version", 1,1, atcommand_version }, - { "mutearea", 99,99, atcommand_mutearea }, - { "rates", 1,1, atcommand_rates }, - { "iteminfo", 1,1, atcommand_iteminfo }, - { "whodrops", 1,1, atcommand_whodrops }, - { "whereis", 10,10, atcommand_whereis }, - { "mapflag", 99,99, atcommand_mapflag }, - { "me", 20,20, atcommand_me }, - { "battleignore", 99,99, atcommand_monsterignore }, - { "fakename", 20,20, atcommand_fakename }, - { "size", 20,20, atcommand_size }, - { "showexp", 10,10, atcommand_showexp}, - { "showzeny", 10,10, atcommand_showzeny}, - { "showdelay", 1,1, atcommand_showdelay}, - { "autotrade", 10,10, atcommand_autotrade }, - { "changegm", 10,10, atcommand_changegm }, - { "changeleader", 10,10, atcommand_changeleader }, - { "partyoption", 10,10, atcommand_partyoption}, - { "invite", 1,1, atcommand_invite }, - { "duel", 1,1, atcommand_duel }, - { "leave", 1,1, atcommand_leave }, - { "accept", 1,1, atcommand_accept }, - { "reject", 1,1, atcommand_reject }, - { "main", 1,1, atcommand_main }, - { "clone", 50,50, atcommand_clone }, - { "slaveclone", 50,50, atcommand_clone }, - { "evilclone", 50,50, atcommand_clone }, - { "tonpc", 40,40, atcommand_tonpc }, - { "commands", 1,1, atcommand_commands }, - { "noask", 1,1, atcommand_noask }, - { "request", 20,20, atcommand_request }, - { "hlvl", 60,60, atcommand_homlevel }, - { "homevolve", 60,60, atcommand_homevolution }, - { "makehomun", 60,60, atcommand_makehomun }, - { "homfriendly", 60,60, atcommand_homfriendly }, - { "homhungry", 60,60, atcommand_homhungry }, - { "homtalk", 10,10, atcommand_homtalk }, - { "hominfo", 1,1, atcommand_hominfo }, - { "homstats", 1,1, atcommand_homstats }, - { "homshuffle", 60,60, atcommand_homshuffle }, - { "showmobs", 10,10, atcommand_showmobs }, - { "feelreset", 10,10, atcommand_feelreset }, - { "auction", 1,1, atcommand_auction }, - { "mail", 1,1, atcommand_mail }, - { "noks", 1,1, atcommand_ksprotection }, - { "allowks", 40,40, atcommand_allowks }, - { "cash", 60,60, atcommand_cash }, - { "points", 60,60, atcommand_cash }, - { "agitstart2", 60,60, atcommand_agitstart2 }, - { "agitend2", 60,60, atcommand_agitend2 }, - { "skreset", 60,60, atcommand_resetskill }, - { "streset", 60,60, atcommand_resetstat }, - { "storagelist", 40,40, atcommand_itemlist }, - { "cartlist", 40,40, atcommand_itemlist }, - { "itemlist", 40,40, atcommand_itemlist }, - { "stats", 40,40, atcommand_stats }, - { "delitem", 60,60, atcommand_delitem }, - { "charcommands", 1,1, atcommand_charcommands }, - { "font", 1,1, atcommand_font }, + ACMD_DEF2("warp", mapmove), + ACMD_DEF(where), + ACMD_DEF(jumpto), + ACMD_DEF(jump), + ACMD_DEF(who), + ACMD_DEF(who2), + ACMD_DEF(who3), + ACMD_DEF(whomap), + ACMD_DEF(whomap2), + ACMD_DEF(whomap3), + ACMD_DEF(whogm), + ACMD_DEF(save), + ACMD_DEF(load), + ACMD_DEF(speed), + ACMD_DEF(storage), + ACMD_DEF(guildstorage), + ACMD_DEF(option), + ACMD_DEF(hide), // + /hide + ACMD_DEF(jobchange), + ACMD_DEF(die), + ACMD_DEF(kill), + ACMD_DEF(alive), + ACMD_DEF(kami), + ACMD_DEF2("kamib", kami), + ACMD_DEF2("kamic", kami), + ACMD_DEF(heal), + ACMD_DEF(item), + ACMD_DEF(item2), + ACMD_DEF(itemreset), + ACMD_DEF2("blvl", baselevelup), + ACMD_DEF2("jlvl", joblevelup), + ACMD_DEF(help), + ACMD_DEF(pvpoff), + ACMD_DEF(pvpon), + ACMD_DEF(gvgoff), + ACMD_DEF(gvgon), + ACMD_DEF(model), + ACMD_DEF(go), + ACMD_DEF(monster), + ACMD_DEF2("monstersmall", monster), + ACMD_DEF2("monsterbig", monster), + ACMD_DEF(killmonster), + ACMD_DEF(killmonster2), + ACMD_DEF(refine), + ACMD_DEF(produce), + ACMD_DEF(memo), + ACMD_DEF(gat), + ACMD_DEF(displaystatus), + ACMD_DEF2("stpoint", statuspoint), + ACMD_DEF2("skpoint", skillpoint), + ACMD_DEF(zeny), + ACMD_DEF2("str", param), + ACMD_DEF2("agi", param), + ACMD_DEF2("vit", param), + ACMD_DEF2("int", param), + ACMD_DEF2("dex", param), + ACMD_DEF2("luk", param), + ACMD_DEF2("glvl", guildlevelup), + ACMD_DEF(makeegg), + ACMD_DEF(hatch), + ACMD_DEF(petfriendly), + ACMD_DEF(pethungry), + ACMD_DEF(petrename), + ACMD_DEF(recall), // + /recall + ACMD_DEF(night), + ACMD_DEF(day), + ACMD_DEF(doom), + ACMD_DEF(doommap), + ACMD_DEF(raise), + ACMD_DEF(raisemap), + ACMD_DEF(kick), // + right click menu for GM "(name) force to quit" + ACMD_DEF(kickall), + ACMD_DEF(allskill), + ACMD_DEF(questskill), + ACMD_DEF(lostskill), + ACMD_DEF(spiritball), + ACMD_DEF(party), + ACMD_DEF(guild), + ACMD_DEF(agitstart), + ACMD_DEF(agitend), + ACMD_DEF(mapexit), + ACMD_DEF(idsearch), + ACMD_DEF(broadcast), // + /b and /nb + ACMD_DEF(localbroadcast), // + /lb and /nlb + ACMD_DEF(recallall), + ACMD_DEF(reloaditemdb), + ACMD_DEF(reloadmobdb), + ACMD_DEF(reloadskilldb), + ACMD_DEF(reloadscript), + ACMD_DEF(reloadatcommand), + ACMD_DEF(reloadbattleconf), + ACMD_DEF(reloadstatusdb), + ACMD_DEF(reloadpcdb), + ACMD_DEF(reloadmotd), + ACMD_DEF(mapinfo), + ACMD_DEF(dye), + ACMD_DEF2("hairstyle", hair_style), + ACMD_DEF2("haircolor", hair_color), + ACMD_DEF2("allstats", stat_all), + ACMD_DEF2("block", char_block), + ACMD_DEF2("ban", char_ban), + ACMD_DEF2("unblock", char_unblock), + ACMD_DEF2("unban", char_unban), + ACMD_DEF2("mount", mount_peco), + ACMD_DEF(guildspy), + ACMD_DEF(partyspy), + ACMD_DEF(repairall), + ACMD_DEF(guildrecall), + ACMD_DEF(partyrecall), + ACMD_DEF(nuke), + ACMD_DEF(shownpc), + ACMD_DEF(hidenpc), + ACMD_DEF(loadnpc), + ACMD_DEF(unloadnpc), + ACMD_DEF2("time", servertime), + ACMD_DEF(jail), + ACMD_DEF(unjail), + ACMD_DEF(jailfor), + ACMD_DEF(jailtime), + ACMD_DEF(disguise), + ACMD_DEF(undisguise), + ACMD_DEF(email), + ACMD_DEF(effect), + ACMD_DEF(follow), + ACMD_DEF(addwarp), + ACMD_DEF(skillon), + ACMD_DEF(skilloff), + ACMD_DEF(killer), + ACMD_DEF(npcmove), + ACMD_DEF(killable), + ACMD_DEF(dropall), + ACMD_DEF(storeall), + ACMD_DEF(skillid), + ACMD_DEF(useskill), + ACMD_DEF(displayskill), + ACMD_DEF(snow), + ACMD_DEF(sakura), + ACMD_DEF(clouds), + ACMD_DEF(clouds2), + ACMD_DEF(fog), + ACMD_DEF(fireworks), + ACMD_DEF(leaves), + ACMD_DEF(summon), + ACMD_DEF(adjgroup), + ACMD_DEF(trade), + ACMD_DEF(send), + ACMD_DEF(setbattleflag), + ACMD_DEF(unmute), + ACMD_DEF(clearweather), + ACMD_DEF(uptime), + ACMD_DEF(changesex), + ACMD_DEF(mute), + ACMD_DEF(refresh), + ACMD_DEF(identify), + ACMD_DEF(gmotd), + ACMD_DEF(misceffect), + ACMD_DEF(mobsearch), + ACMD_DEF(cleanmap), + ACMD_DEF(npctalk), + ACMD_DEF(pettalk), + ACMD_DEF(users), + ACMD_DEF(reset), + ACMD_DEF(skilltree), + ACMD_DEF(marry), + ACMD_DEF(divorce), + ACMD_DEF(sound), + ACMD_DEF(undisguiseall), + ACMD_DEF(disguiseall), + ACMD_DEF(changelook), + ACMD_DEF(autoloot), + ACMD_DEF2("alootid", autolootitem), + ACMD_DEF(mobinfo), + ACMD_DEF(exp), + ACMD_DEF(adopt), + ACMD_DEF(version), + ACMD_DEF(mutearea), + ACMD_DEF(rates), + ACMD_DEF(iteminfo), + ACMD_DEF(whodrops), + ACMD_DEF(whereis), + ACMD_DEF(mapflag), + ACMD_DEF(me), + ACMD_DEF(monsterignore), + ACMD_DEF(fakename), + ACMD_DEF(size), + ACMD_DEF(showexp), + ACMD_DEF(showzeny), + ACMD_DEF(showdelay), + ACMD_DEF(autotrade), + ACMD_DEF(changegm), + ACMD_DEF(changeleader), + ACMD_DEF(partyoption), + ACMD_DEF(invite), + ACMD_DEF(duel), + ACMD_DEF(leave), + ACMD_DEF(accept), + ACMD_DEF(reject), + ACMD_DEF(main), + ACMD_DEF(clone), + ACMD_DEF2("slaveclone", clone), + ACMD_DEF2("evilclone", clone), + ACMD_DEF(tonpc), + ACMD_DEF(commands), + ACMD_DEF(noask), + ACMD_DEF(request), + ACMD_DEF(homlevel), + ACMD_DEF(homevolution), + ACMD_DEF(makehomun), + ACMD_DEF(homfriendly), + ACMD_DEF(homhungry), + ACMD_DEF(homtalk), + ACMD_DEF(hominfo), + ACMD_DEF(homstats), + ACMD_DEF(homshuffle), + ACMD_DEF(showmobs), + ACMD_DEF(feelreset), + ACMD_DEF(auction), + ACMD_DEF(mail), + ACMD_DEF2("noks", ksprotection), + ACMD_DEF(allowks), + ACMD_DEF(cash), + ACMD_DEF2("points", cash), + ACMD_DEF(agitstart2), + ACMD_DEF(agitend2), + ACMD_DEF2("skreset", resetskill), + ACMD_DEF2("streset", resetstat), + ACMD_DEF2("storagelist", itemlist), + ACMD_DEF2("cartlist", itemlist), + ACMD_DEF2("itemlist", itemlist), + ACMD_DEF(stats), + ACMD_DEF(delitem), + ACMD_DEF(charcommands), + ACMD_DEF(font), /** * For Testing Purposes, not going to be here after we're done. **/ - { "newmount", 0,99, atcommand_new_mount }, + ACMD_DEF2("newmount", new_mount), }; AtCommandInfo* atcommand; int i; for( i = 0; i < ARRAYLENGTH(atcommand_base); i++ ) { - + if(atcommand_exists(atcommand_base[i].command)) { // Should not happen if atcommand_base[] array is OK + ShowDebug("atcommand_basecommands: duplicate ACMD_DEF for '%s'.\n", atcommand_base[i].command); + continue; + } CREATE(atcommand, AtCommandInfo, 1); - - safestrncpy(atcommand->command,atcommand_base[i].command,sizeof(atcommand->command)); - atcommand->level = atcommand_base[i].level; - atcommand->level2 = atcommand_base[i].level2; + safestrncpy(atcommand->command, atcommand_base[i].command, sizeof(atcommand->command)); atcommand->func = atcommand_base[i].func; - strdb_put(atcommand_db, atcommand->command, atcommand); } return; @@ -9141,23 +8971,26 @@ void atcommand_basecommands(void) { /*========================================== * Command lookup functions *------------------------------------------*/ -static AtCommandInfo* get_atcommandinfo_byname(const char* name) { - if( *name == atcommand_symbol || *name == charcommand_symbol ) name++; // for backwards compatibility - if( strdb_exists(atcommand_db,name) ) +bool atcommand_exists(const char* name) +{ + return strdb_exists(atcommand_db, name); +} + +static AtCommandInfo* get_atcommandinfo_byname(const char *name) +{ + if (strdb_exists(atcommand_db, name)) return (AtCommandInfo*)strdb_get(atcommand_db, name); return NULL; } - -/*========================================== - * Retrieve the command's required gm level - *------------------------------------------*/ -int get_atcommand_level(const char* name) { - AtCommandInfo* info = (AtCommandInfo*)strdb_get(atcommand_db, name); - return ( info != NULL ) ? info->level : 100; // 100: command can not be used +static const char* atcommand_checkalias(const char *aliasname) +{ + AliasInfo *alias_info = NULL; + if ((alias_info = (AliasInfo*)strdb_get(atcommand_alias_db, aliasname)) != NULL) + return alias_info->command->command; + return aliasname; } - /// Executes an at-command. bool is_atcommand(const int fd, struct map_session_data* sd, const char* message, int type) { @@ -9165,7 +8998,6 @@ bool is_atcommand(const int fd, struct map_session_data* sd, const char* message char charname2[NAME_LENGTH], params2[100]; char command[100]; char output[CHAT_SIZE_MAX]; - int x, y, z; int lv = 0; //Reconstructed message @@ -9177,15 +9009,15 @@ bool is_atcommand(const int fd, struct map_session_data* sd, const char* message nullpo_retr(false, sd); //Shouldn't happen - if( !message || !*message ) + if ( !message || !*message ) return false; //Block NOCHAT but do not display it as a normal message - if( sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCOMMAND ) + if ( sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCOMMAND ) return true; // skip 10/11-langtype's codepage indicator, if detected - if( message[0] == '|' && strlen(message) >= 4 && (message[3] == atcommand_symbol || message[3] == charcommand_symbol) ) + if ( message[0] == '|' && strlen(message) >= 4 && (message[3] == atcommand_symbol || message[3] == charcommand_symbol) ) message += 3; //Should display as a normal message @@ -9194,64 +9026,49 @@ bool is_atcommand(const int fd, struct map_session_data* sd, const char* message // type value 0 = server invoked: bypass restrictions // 1 = player invoked - if( type ) - { + if ( type == 1) { //Commands are disabled on maps flagged as 'nocommand' - if( map[sd->bl.m].nocommand && pc_isGM(sd) < map[sd->bl.m].nocommand ) - { + if ( map[sd->bl.m].nocommand && pc_get_group_level(sd) < map[sd->bl.m].nocommand ) { clif_displaymessage(fd, msg_txt(143)); return false; } - - //Displays as a normal message for Non-GMs - if( battle_config.atc_gmonly != 0 && pc_isGM(sd) == 0 ) - return false; } - while (*message == charcommand_symbol) - { - //Checks to see if #command has a name or a name + parameters. - x = sscanf(message, "%99s \"%23[^\"]\" %99[^\n]", command, charname, params); - y = sscanf(message, "%99s %23s %99[^\n]", command, charname2, params2); - - //z always has the value of the scan that was successful - z = ( x > 1 ) ? x : y; + if (*message == charcommand_symbol) { + do { + int x, y, z; + + //Checks to see if #command has a name or a name + parameters. + x = sscanf(message, "%99s \"%23[^\"]\" %99[^\n]", command, charname, params); + y = sscanf(message, "%99s %23s %99[^\n]", command, charname2, params2); - if ( (ssd = map_nick2sd(charname)) == NULL && ( (ssd = map_nick2sd(charname2)) == NULL ) ) { - if( pc_isGM(sd) ) { - sprintf(output, "%s failed. Player not found.", command); - clif_displaymessage(fd, output); - } else { - sprintf(output, "Charcommand failed. Usage: #<command> <char name> <params>."); - clif_displaymessage(fd, output); + //z always has the value of the scan that was successful + z = ( x > 1 ) ? x : y; + + //#command + name means the sufficient target was used and anything else after + //can be looked at by the actual command function since most scan to see if the + //right parameters are used. + if ( x > 2 ) { + sprintf(atcmd_msg, "%s %s", command, params); + break; + } + else if ( y > 2 ) { + sprintf(atcmd_msg, "%s %s", command, params2); + break; + } + //Regardless of what style the #command is used, if it's correct, it will always have + //this value if there is no parameter. Send it as just the #command + else if ( z == 2 ) { + sprintf(atcmd_msg, "%s", command); + break; } - return true; - } - - //#command + name means the sufficient target was used and anything else after - //can be looked at by the actual command function since most scan to see if the - //right parameters are used. - if ( x > 2 ) { - sprintf(atcmd_msg, "%s %s", command, params); - break; - } - else if ( y > 2 ) { - sprintf(atcmd_msg, "%s %s", command, params2); - break; - } - //Regardless of what style the #command is used, if it's correct, it will always have - //this value if there is no parameter. Send it as just the #command - else if ( z == 2 ) { - sprintf(atcmd_msg, "%s", command); - break; - } - sprintf(output, "Charcommand failed. Usage: #<command> <char name> <params>."); - clif_displaymessage(fd, output); - return true; + sprintf(output, "Charcommand failed. Usage: %c<command> <char name> <params>.", charcommand_symbol); + clif_displaymessage(fd, output); + return true; + } while(0); } - - if (*message == atcommand_symbol) { + else if (*message == atcommand_symbol) { //atcmd_msg is constructed above differently for charcommands //it's copied from message if not a charcommand so it can //pass through the rest of the code compatible with both symbols @@ -9267,9 +9084,10 @@ bool is_atcommand(const int fd, struct map_session_data* sd, const char* message params[0] = '\0'; //Grab the command information and check for the proper GM level required to use it or if the command exists - if( (info = (AtCommandInfo*)strdb_get(atcommand_db, command+1)) == NULL || info->func == NULL || ( type && ((*atcmd_msg == atcommand_symbol && pc_isGM(sd) < info->level) || (*atcmd_msg == charcommand_symbol && pc_isGM(sd) < info->level2)) ) ) + info = get_atcommandinfo_byname(atcommand_checkalias(command + 1)); + if (info == NULL) { - if( pc_isGM(sd) ) { + if( pc_get_group_level(sd) ) { // TODO: remove or replace with proper permission sprintf(output, msg_txt(153), command); // "%s is Unknown Command." clif_displaymessage(fd, output); return true; @@ -9277,238 +9095,167 @@ bool is_atcommand(const int fd, struct map_session_data* sd, const char* message return false; } - //Log atcommands - if( *atcmd_msg == atcommand_symbol ) - log_atcommand(sd, info->level, atcmd_msg); - //Log Charcommands - else if( *atcmd_msg == charcommand_symbol && ssd != NULL ) - log_atcommand(sd, info->level2, message); - + // type == 1 : player invoked + if (type == 1) { + if ((*command == atcommand_symbol && !pc_can_use_command(sd, atcommand_checkalias(command + 1), COMMAND_ATCOMMAND)) || + (*command == charcommand_symbol && !pc_can_use_command(sd, atcommand_checkalias(command + 1), COMMAND_CHARCOMMAND))) { + return false; + } + } + + // Check if target is valid only if confirmed that player can use command. + if (*message == charcommand_symbol && + (ssd = map_nick2sd(charname)) == NULL && (ssd = map_nick2sd(charname2)) == NULL ) { + sprintf(output, "%s failed. Player not found.", command); + clif_displaymessage(fd, output); + return true; + } + //Attempt to use the command - if( strcmpi("adjgmlvl",command+1) && ssd ) { lv = ssd->gmlevel; ssd->gmlevel = sd->gmlevel; } if ( (info->func(fd, (*atcmd_msg == atcommand_symbol) ? sd : ssd, command, params) != 0) ) { sprintf(output,msg_txt(154), command); // %s failed. clif_displaymessage(fd, output); - } - if( strcmpi("adjgmlvl",command+1) && ssd ) ssd->gmlevel = lv; - - return true; -} -/** - * Splits and parses command aliases field - * Note: I'm not good (at all) with string manipulation, if you think you can improve, please do. I beg you. - **/ -void atcommand_parse_aliases(char aliases[1024],AtCommandInfo* base) { - char *str[99], *p; - int i, max = 0; - - p = aliases; - while( ISSPACE(*p) )//trim - ++p; - - //I assume nobody is getting over 98 alises in the same command lol - for( i = 0; i < 99; i++ ) { - str[i] = p; - p = strchr(p,','); - if( p == NULL ) { - max = i+1; - break;// comma not found - } - *p = '\0'; - ++p; + return true; } - if( !str[0] )//no aliases at all - return; - - for(i = 0; i < max;i++) { - AtCommandInfo* atcommand = NULL; - normalize_name(str[i]," ");//trim over - if( (atcommand = strdb_get(atcommand_db, str[i])) ) { - atcommand->level = base->level; - atcommand->level2 = base->level2; - continue; - } - - CREATE(atcommand, AtCommandInfo, 1); - - safestrncpy(atcommand->command,str[i],sizeof(atcommand->command)); - atcommand->level = base->level; - atcommand->level2 = base->level2; - atcommand->func = base->func; - - strdb_put(atcommand_db, atcommand->command, atcommand); - } + //Log only if successful. + if ( *atcmd_msg == atcommand_symbol ) + log_atcommand(sd, atcmd_msg); + else if ( *atcmd_msg == charcommand_symbol ) + log_atcommand(sd, message); - return; + return true; } + /*========================================== * *------------------------------------------*/ -int atcommand_config_read(const char* cfgName) +static void atcommand_config_read(const char* config_filename) { - char line[1024], w1[1024], w2[1024], w3[1024], w4[1024]; - AtCommandInfo* p; - FILE* fp; - int i; + config_setting_t *aliases = NULL, *help = NULL; + const char *symbol = NULL; + int num_aliases = 0; - if( (fp = fopen(cfgName, "r")) == NULL ) { - ShowError("AtCommand configuration file not found: %s\n", cfgName); - return 1; - } - - while( fgets(line, sizeof(line), fp) ) { - if( line[0] == '/' && line[1] == '/' ) - continue; - if( ( i = sscanf(line,"%1023[^:]:%1023[^,],%1023[^[][%1023[^]]",w1,w2,w3,w4) ) >= 3 ) { - if( ( p = (AtCommandInfo*)strdb_get(atcommand_db, w1) ) != NULL ) { - - p->level = atoi(w2);//update @level - p->level2 = atoi(w3);//update #level - - /** - * Parse the alises - **/ - if( i == 4 ) - atcommand_parse_aliases(w4,p); - - continue;//we're done move on + if (conf_read_file(&atcommand_config, config_filename)) + return; + + // Command symbols + if (config_lookup_string(&atcommand_config, "atcommand_symbol", &symbol)) { + if (ISPRINT(*symbol) && // no control characters + *symbol != '/' && // symbol of client commands + *symbol != '%' && // symbol of party chat + *symbol != '$' && // symbol of guild chat + *symbol != charcommand_symbol) + atcommand_symbol = *symbol; + } + + if (config_lookup_string(&atcommand_config, "charcommand_symbol", &symbol)) { + if (ISPRINT(*symbol) && // no control characters + *symbol != '/' && // symbol of client commands + *symbol != '%' && // symbol of party chat + *symbol != '$' && // symbol of guild chat + *symbol != atcommand_symbol) + charcommand_symbol = *symbol; + } + + // Command aliases + aliases = config_lookup(&atcommand_config, "aliases"); + if (aliases != NULL) { + int i = 0; + int count = config_setting_length(aliases); + + for (i = 0; i < count; ++i) { + config_setting_t *command = NULL; + const char *commandname = NULL; + int j = 0, alias_count = 0; + AtCommandInfo *commandinfo = NULL; + + command = config_setting_get_elem(aliases, i); + if (config_setting_type(command) != CONFIG_TYPE_ARRAY) + continue; + commandname = config_setting_name(command); + if (!atcommand_exists(commandname)) { + ShowConfigWarning(command, "atcommand_config_read: can not set alias for non-existent command %s", commandname); + continue; + } + commandinfo = get_atcommandinfo_byname(commandname); + alias_count = config_setting_length(command); + for (j = 0; j < alias_count; ++j) { + const char *alias = config_setting_get_string_elem(command, j); + if (alias != NULL) { + AliasInfo *alias_info; + if (strdb_exists(atcommand_alias_db, alias)) { + ShowConfigWarning(command, "atcommand_config_read: alias %s already exists", alias); + continue; + } + CREATE(alias_info, AliasInfo, 1); + alias_info->command = commandinfo; + safestrncpy(alias_info->alias, alias, sizeof(alias_info->alias)); + strdb_put(atcommand_alias_db, alias, alias_info); + ++num_aliases; + } } - } else if( strcmpi(w1, "import") != 0 && strcmpi(w1, "command_symbol") != 0 && strcmpi(w1, "char_symbol") != 0 ) { - if( i > 1 ) - ShowWarning("Unknown setting '%s' in file %s\n", w1, cfgName); - continue; } - normalize_name(w2," ");//trim - if( strcmpi(w1, "import") == 0 ) - atcommand_config_read(w2); - else if( strcmpi(w1, "command_symbol") == 0 && - w2[0] > 31 && // control characters - w2[0] != '/' && // symbol of standard ragnarok GM commands - w2[0] != '%' && // symbol of party chat speaking - w2[0] != '$' && // symbol of guild chat speaking - w2[0] != '#' ) // remote symbol - atcommand_symbol = w2[0]; - else if( strcmpi(w1, "char_symbol") == 0 && - w2[0] > 31 && - w2[0] != '/' && - w2[0] != '%' && - w2[0] != '$' && - w2[0] != '@' ) - charcommand_symbol = w2[0]; } - fclose(fp); - - return 0; -} -static int atcommand_db_free(DBKey key,void *data,va_list va) { - - aFree((AtCommandInfo*)data); - - return 1; -} + // Commands help + // We only check if all commands exist + help = config_lookup(&atcommand_config, "help"); + if (help != NULL) { + int count = config_setting_length(help); + int i; -void atcommand_db_clear() { + for (i = 0; i < count; ++i) { + config_setting_t *command = NULL; + const char *commandname = NULL; - atcommand_db->foreach(atcommand_db,atcommand_db_free); - db_destroy(atcommand_db); + command = config_setting_get_elem(help, i); + commandname = config_setting_name(command); + if (!atcommand_exists(commandname)) + ShowConfigWarning(command, "atcommand_config_read: command %s does not exist", commandname); + } + } + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' command aliases in '"CL_WHITE"%s"CL_RESET"'.\n", num_aliases, config_filename); return; } -void atcommand_doload() { - - if( atcommand_db != NULL ) - atcommand_db_clear(); - - atcommand_db = stridb_alloc(DB_OPT_DUP_KEY, 0); - atcommand_basecommands();//fills initial atcommand_db with known commands - - atcommand_config_read(ATCOMMAND_CONF_FILENAME); - - return; +static int atcommand_db_free(DBKey key, void *data, va_list va) +{ + aFree((AtCommandInfo*)data); + return 1; } -void do_init_atcommand() { - - atcommand_doload(); - +static int atcommand_alias_db_free(DBKey key, void *data, va_list va) +{ + aFree((AliasInfo*)data); + return 1; } -void do_final_atcommand() { - - atcommand_db_clear(); - - return; +void atcommand_db_clear(void) +{ + if (atcommand_db != NULL) + atcommand_db->destroy(atcommand_db, atcommand_db_free); + if (atcommand_alias_db != NULL) + atcommand_alias_db->destroy(atcommand_alias_db, atcommand_alias_db_free); } - -// commands that need to go _after_ the commands table - -/*========================================== - * type: 1 = commands (@), 2 = charcommands (#) - *------------------------------------------*/ -static void atcommand_commands_sub(struct map_session_data* sd, const int fd, int type) { - char line_buff[CHATBOX_SIZE]; - int gm_lvl = pc_isGM(sd), count = 0; - char* cur = line_buff; - AtCommandInfo* cmd; - DBIterator* iter = atcommand_db->iterator(atcommand_db); - - memset(line_buff,' ',CHATBOX_SIZE); - line_buff[CHATBOX_SIZE-1] = 0; - - clif_displaymessage(fd, msg_txt(273)); // "Commands available:" - - for( cmd = (AtCommandInfo*)iter->first(iter,NULL); iter->exists(iter); cmd = (AtCommandInfo*)iter->next(iter,NULL) ) { - unsigned int slen; - - if( type == 1 && gm_lvl < cmd->level ) - continue; - if( type == 2 && gm_lvl < cmd->level2 ) - continue; - - slen = strlen(cmd->command); - - // flush the text buffer if this command won't fit into it - if( slen + cur - line_buff >= CHATBOX_SIZE ) - { - clif_displaymessage(fd,line_buff); - cur = line_buff; - memset(line_buff,' ',CHATBOX_SIZE); - line_buff[CHATBOX_SIZE-1] = 0; - } - - memcpy(cur,cmd->command,slen); - cur += slen+(10-slen%10); - - count++; - } - iter->destroy(iter); - clif_displaymessage(fd,line_buff); - - sprintf(atcmd_output, msg_txt(274), count); // "%d commands found." - clif_displaymessage(fd, atcmd_output); - - return; +void atcommand_doload(void) +{ + atcommand_db_clear(); + atcommand_db = stridb_alloc(DB_OPT_DUP_KEY, ATCOMMAND_LENGTH); + atcommand_alias_db = stridb_alloc(DB_OPT_DUP_KEY, ATCOMMAND_LENGTH); + atcommand_basecommands(); //fills initial atcommand_db with known commands + atcommand_config_read(ATCOMMAND_CONF_FILENAME); } -/*========================================== - * @commands Lists available @ commands to you - *------------------------------------------*/ -ACMD_FUNC(commands) +void do_init_atcommand(void) { - atcommand_commands_sub(sd, fd, 1); - return 0; - } + atcommand_doload(); +} -/*========================================== - * @charcommands Lists available # commands to you - *------------------------------------------*/ -ACMD_FUNC(charcommands) +void do_final_atcommand(void) { - atcommand_commands_sub(sd, fd, 2); - return 0; -} + atcommand_db_clear(); +}
\ No newline at end of file diff --git a/src/map/atcommand.h b/src/map/atcommand.h index 415aa9305..a0a7c2286 100644 --- a/src/map/atcommand.h +++ b/src/map/atcommand.h @@ -4,7 +4,6 @@ #ifndef _ATCOMMAND_H_ #define _ATCOMMAND_H_ -//#include "map.h" struct map_session_data; //This is the distance at which @autoloot works, @@ -15,32 +14,21 @@ struct map_session_data; extern char atcommand_symbol; extern char charcommand_symbol; + +typedef enum { + COMMAND_ATCOMMAND = 1, + COMMAND_CHARCOMMAND = 2, +} AtCommandType; + typedef int (*AtCommandFunc)(const int fd, struct map_session_data* sd, const char* command, const char* message); bool is_atcommand(const int fd, struct map_session_data* sd, const char* message, int type); -int get_atcommand_level(const char* name); void do_init_atcommand(void); void do_final_atcommand(void); -int atcommand_config_read(const char *cfgName); - -int atcommand_mail(const int fd, struct map_session_data* sd,const char* command, const char* message); -int atcommand_item(const int fd, struct map_session_data* sd,const char* command, const char* message); -int atcommand_mapmove(const int fd, struct map_session_data* sd,const char* command, const char* message); -int atcommand_monster(const int fd, struct map_session_data* sd, const char* command, const char* message); -int atcommand_jumpto(const int fd, struct map_session_data* sd, const char* command, const char* message); -int atcommand_recall(const int fd, struct map_session_data* sd, const char* command, const char* message); -int atcommand_hide(const int fd, struct map_session_data* sd, const char* command, const char* message); -int atcommand_mute(const int fd, struct map_session_data* sd, const char* command, const char* message); -int atcommand_kick(const int fd, struct map_session_data* sd, const char* command, const char* message); -int atcommand_broadcast(const int fd, struct map_session_data* sd,const char* command, const char* message); -int atcommand_localbroadcast(const int fd, struct map_session_data* sd,const char* command, const char* message); -int atcommand_reset(const int fd, struct map_session_data* sd,const char* command, const char* message); -int atcommand_unloadnpc(const int fd, struct map_session_data* sd, const char* command, const char* message); -int atcommand_killmonster(const int fd, struct map_session_data* sd, const char* command, const char* message); - -#define MAX_MSG 1000 -extern char* msg_table[MAX_MSG]; + +bool atcommand_exists(const char* name); + const char* msg_txt(int msg_number); int msg_config_read(const char* cfgName); void do_final_msg(void); diff --git a/src/map/battle.c b/src/map/battle.c index cbf0610ef..a7257406e 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -4441,18 +4441,9 @@ static const struct _battle_data { { "guild_max_castles", &battle_config.guild_max_castles, 0, 0, INT_MAX, }, { "guild_skill_relog_delay", &battle_config.guild_skill_relog_delay, 0, 0, 1, }, { "emergency_call", &battle_config.emergency_call, 11, 0, 31, }, - { "lowest_gm_level", &battle_config.lowest_gm_level, 1, 0, 99, }, - { "atcommand_gm_only", &battle_config.atc_gmonly, 0, 0, 1, }, { "atcommand_spawn_quantity_limit", &battle_config.atc_spawn_quantity_limit, 100, 0, INT_MAX, }, { "atcommand_slave_clone_limit", &battle_config.atc_slave_clone_limit, 25, 0, INT_MAX, }, { "partial_name_scan", &battle_config.partial_name_scan, 0, 0, 1, }, - { "gm_all_skill", &battle_config.gm_allskill, 0, 0, 100, }, - { "gm_all_equipment", &battle_config.gm_allequip, 0, 0, 100, }, - { "gm_skill_unconditional", &battle_config.gm_skilluncond, 0, 0, 100, }, - { "gm_join_chat", &battle_config.gm_join_chat, 0, 0, 100, }, - { "gm_kick_chat", &battle_config.gm_kick_chat, 0, 0, 100, }, - { "gm_can_party", &battle_config.gm_can_party, 0, 0, 1, }, - { "gm_cant_party_min_lv", &battle_config.gm_cant_party_min_lv, 20, 0, 100, }, { "player_skillfree", &battle_config.skillfree, 0, 0, 1, }, { "player_skillup_limit", &battle_config.skillup_limit, 1, 0, 1, }, { "weapon_produce_rate", &battle_config.wp_rate, 100, 0, INT_MAX, }, @@ -4588,15 +4579,11 @@ static const struct _battle_data { { "gx_disptype", &battle_config.gx_disptype, 1, 0, 1, }, { "devotion_level_difference", &battle_config.devotion_level_difference, 10, 0, INT_MAX, }, { "player_skill_partner_check", &battle_config.player_skill_partner_check, 1, 0, 1, }, - { "hide_GM_session", &battle_config.hide_GM_session, 0, 0, 1, }, { "invite_request_check", &battle_config.invite_request_check, 1, 0, 1, }, { "skill_removetrap_type", &battle_config.skill_removetrap_type, 0, 0, 1, }, { "disp_experience", &battle_config.disp_experience, 0, 0, 1, }, { "disp_zeny", &battle_config.disp_zeny, 0, 0, 1, }, { "castle_defense_rate", &battle_config.castle_defense_rate, 100, 0, 100, }, - { "gm_cant_drop_min_lv", &battle_config.gm_cant_drop_min_lv, 1, 0, 100, }, - { "gm_cant_drop_max_lv", &battle_config.gm_cant_drop_max_lv, 0, 0, 100, }, - { "disp_hpmeter", &battle_config.disp_hpmeter, 0, 0, 100, }, { "bone_drop", &battle_config.bone_drop, 0, 0, 2, }, { "buyer_name", &battle_config.buyer_name, 1, 0, 1, }, { "skill_wall_check", &battle_config.skill_wall_check, 1, 0, 1, }, @@ -4649,9 +4636,6 @@ static const struct _battle_data { { "night_at_start", &battle_config.night_at_start, 0, 0, 1, }, { "show_mob_info", &battle_config.show_mob_info, 0, 0, 1|2|4, }, { "ban_hack_trade", &battle_config.ban_hack_trade, 0, 0, INT_MAX, }, - { "hack_info_GM_level", &battle_config.hack_info_GM_level, 60, 0, 100, }, - { "any_warp_GM_min_level", &battle_config.any_warp_GM_min_level, 20, 0, 100, }, - { "who_display_aid", &battle_config.who_display_aid, 40, 0, 100, }, { "packet_ver_flag", &battle_config.packet_ver_flag, 0xFFFFFF,0x0000,INT_MAX, }, { "min_hair_style", &battle_config.min_hair_style, 0, 0, INT_MAX, }, { "max_hair_style", &battle_config.max_hair_style, 23, 0, INT_MAX, }, @@ -4699,14 +4683,6 @@ static const struct _battle_data { { "mob_max_skilllvl", &battle_config.mob_max_skilllvl, MAX_SKILL_LEVEL, 1, MAX_SKILL_LEVEL, }, { "retaliate_to_master", &battle_config.retaliate_to_master, 1, 0, 1, }, { "rare_drop_announce", &battle_config.rare_drop_announce, 0, 0, 10000, }, - { "title_lvl1", &battle_config.title_lvl1, 1, 0, 100, }, - { "title_lvl2", &battle_config.title_lvl2, 10, 0, 100, }, - { "title_lvl3", &battle_config.title_lvl3, 20, 0, 100, }, - { "title_lvl4", &battle_config.title_lvl4, 40, 0, 100, }, - { "title_lvl5", &battle_config.title_lvl5, 50, 0, 100, }, - { "title_lvl6", &battle_config.title_lvl6, 60, 0, 100, }, - { "title_lvl7", &battle_config.title_lvl7, 80, 0, 100, }, - { "title_lvl8", &battle_config.title_lvl8, 99, 0, 100, }, { "duel_allow_pvp", &battle_config.duel_allow_pvp, 0, 0, 1, }, { "duel_allow_gvg", &battle_config.duel_allow_gvg, 0, 0, 1, }, { "duel_allow_teleport", &battle_config.duel_allow_teleport, 0, 0, 1, }, @@ -4750,7 +4726,6 @@ static const struct _battle_data { { "ksprotection", &battle_config.ksprotection, 5000, 0, INT_MAX, }, { "auction_feeperhour", &battle_config.auction_feeperhour, 12000, 0, INT_MAX, }, { "auction_maximumprice", &battle_config.auction_maximumprice, 500000000, 0, MAX_ZENY, }, - { "gm_viewequip_min_lv", &battle_config.gm_viewequip_min_lv, 0, 0, 99, }, { "homunculus_auto_vapor", &battle_config.homunculus_auto_vapor, 1, 0, 1, }, { "display_status_timers", &battle_config.display_status_timers, 1, 0, 1, }, { "skill_add_heal_rate", &battle_config.skill_add_heal_rate, 7, 0, INT_MAX, }, @@ -4760,7 +4735,6 @@ static const struct _battle_data { { "autospell_check_range", &battle_config.autospell_check_range, 0, 0, 1, }, { "client_reshuffle_dice", &battle_config.client_reshuffle_dice, 0, 0, 1, }, { "client_sort_storage", &battle_config.client_sort_storage, 0, 0, 1, }, - { "gm_check_minlevel", &battle_config.gm_check_minlevel, 60, 0, 100, }, { "feature.buying_store", &battle_config.feature_buying_store, 1, 0, 1, }, { "feature.search_stores", &battle_config.feature_search_stores, 1, 0, 1, }, { "searchstore_querydelay", &battle_config.searchstore_querydelay, 10, 0, INT_MAX, }, diff --git a/src/map/battle.h b/src/map/battle.h index 43c1d69ec..54b707f08 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -151,16 +151,9 @@ extern struct Battle_Config int monster_max_aspd; int view_range_rate; int chase_range_rate; - int lowest_gm_level; - int atc_gmonly; int atc_spawn_quantity_limit; int atc_slave_clone_limit; int partial_name_scan; - int gm_allskill; - int gm_allequip; - int gm_skilluncond; - int gm_join_chat; - int gm_kick_chat; int skillfree; int skillup_limit; int wp_rate; @@ -327,7 +320,6 @@ extern struct Battle_Config int gx_disptype; int devotion_level_difference; int player_skill_partner_check; - int hide_GM_session; int invite_request_check; int skill_removetrap_type; int disp_experience; @@ -336,21 +328,14 @@ extern struct Battle_Config int backstab_bow_penalty; int hp_rate; int sp_rate; - int gm_cant_drop_min_lv; - int gm_cant_drop_max_lv; - int disp_hpmeter; int bone_drop; int buyer_name; - int gm_cant_party_min_lv; - int gm_can_party; // [SketchyPhoenix] // eAthena additions int night_at_start; // added by [Yor] int day_duration; // added by [Yor] int night_duration; // added by [Yor] int ban_hack_trade; // added by [Yor] - int hack_info_GM_level; // added by [Yor] - int any_warp_GM_min_level; // added by [Yor] int packet_ver_flag; // added by [Yor] int min_hair_style; // added by [MouseJstr] @@ -387,7 +372,6 @@ extern struct Battle_Config int delay_battle_damage; int hide_woe_damage; int display_version; - int who_display_aid; int display_hallucination; // [Skotlex] int use_statpoint_table; // [Skotlex] @@ -415,15 +399,6 @@ extern struct Battle_Config int retaliate_to_master; //Whether when a mob is attacked by another mob, it will retaliate versus the mob or the mob's master. [Skotlex] - int title_lvl1; // Players titles [Lupus] - int title_lvl2; // Players titles [Lupus] - int title_lvl3; // Players titles [Lupus] - int title_lvl4; // Players titles [Lupus] - int title_lvl5; // Players titles [Lupus] - int title_lvl6; // Players titles [Lupus] - int title_lvl7; // Players titles [Lupus] - int title_lvl8; // Players titles [Lupus] - int duel_allow_pvp; // [LuzZza] int duel_allow_gvg; // [LuzZza] int duel_allow_teleport; // [LuzZza] @@ -466,7 +441,6 @@ extern struct Battle_Config int ksprotection; int auction_feeperhour; int auction_maximumprice; - int gm_viewequip_min_lv; int homunculus_auto_vapor; //Keep Homunculus from Vaporizing when master dies. [L0ne_W0lf] int display_status_timers; //Show or hide skill buff/delay timers in recent clients [Sara] int skill_add_heal_rate; //skills that bHealPower has effect on [Inkfish] @@ -476,7 +450,6 @@ extern struct Battle_Config int autospell_check_range; //Enable range check for autospell bonus. [L0ne_W0lf] int client_reshuffle_dice; // Reshuffle /dice int client_sort_storage; - int gm_check_minlevel; // min GM level for /check int feature_buying_store; int feature_search_stores; int searchstore_querydelay; diff --git a/src/map/buyingstore.c b/src/map/buyingstore.c index 01964a56e..a556a41a2 100644 --- a/src/map/buyingstore.c +++ b/src/map/buyingstore.c @@ -98,7 +98,7 @@ void buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned cha return; } - if( !pc_can_give_items(pc_isGM(sd)) ) + if( !pc_can_give_items(sd) ) {// custom: GM is not allowed to buy (give zeny) sd->buyingstore.slots = 0; clif_displaymessage(sd->fd, msg_txt(246)); @@ -145,7 +145,7 @@ void buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned cha break; } - if( !id->flag.buyingstore || !itemdb_cantrade_sub(id, pc_isGM(sd), pc_isGM(sd)) || ( idx = pc_search_inventory(sd, nameid) ) == -1 ) + if( !id->flag.buyingstore || !itemdb_cantrade_sub(id, pc_get_group_level(sd), pc_get_group_level(sd)) || ( idx = pc_search_inventory(sd, nameid) ) == -1 ) {// restrictions: allowed, no character-bound items and at least one must be owned break; } @@ -219,7 +219,7 @@ void buyingstore_open(struct map_session_data* sd, int account_id) return; } - if( !pc_can_give_items(pc_isGM(sd)) ) + if( !pc_can_give_items(sd) ) {// custom: GM is not allowed to sell clif_displaymessage(sd->fd, msg_txt(246)); return; @@ -257,7 +257,7 @@ void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int return; } - if( !pc_can_give_items(pc_isGM(sd)) ) + if( !pc_can_give_items(sd) ) {// custom: GM is not allowed to sell clif_displaymessage(sd->fd, msg_txt(246)); clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0); @@ -312,7 +312,7 @@ void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int return; } - if( sd->status.inventory[index].expire_time || !itemdb_cantrade(&sd->status.inventory[index], pc_isGM(sd), pc_isGM(pl_sd)) || memcmp(sd->status.inventory[index].card, buyingstore_blankslots, sizeof(buyingstore_blankslots)) ) + if( sd->status.inventory[index].expire_time || !itemdb_cantrade(&sd->status.inventory[index], pc_get_group_level(sd), pc_get_group_level(pl_sd)) || memcmp(sd->status.inventory[index].card, buyingstore_blankslots, sizeof(buyingstore_blankslots)) ) {// non-tradable item clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; diff --git a/src/map/chat.c b/src/map/chat.c index 48c0b07f1..9a59950d4 100644 --- a/src/map/chat.c +++ b/src/map/chat.c @@ -125,7 +125,7 @@ int chat_joinchat(struct map_session_data* sd, int chatid, const char* pass) return 0; } - if( !cd->pub && strncmp(pass, cd->pass, sizeof(cd->pass)) != 0 && !(battle_config.gm_join_chat && pc_isGM(sd) >= battle_config.gm_join_chat) ) + if( !cd->pub && strncmp(pass, cd->pass, sizeof(cd->pass)) != 0 && !pc_has_permission(sd, PC_PERM_JOIN_ALL_CHAT) ) { clif_joinchatfail(sd,1); return 0; @@ -316,7 +316,7 @@ int chat_kickchat(struct map_session_data* sd, const char* kickusername) if( i == cd->users ) return -1; - if( battle_config.gm_kick_chat && pc_isGM(cd->usersd[i]) >= battle_config.gm_kick_chat ) + if (pc_has_permission(cd->usersd[i], PC_PERM_NO_CHAT_KICK)) return 0; //gm kick protection [Valaris] idb_put(cd->kick_list,cd->usersd[i]->status.char_id,(void*)1); diff --git a/src/map/chrif.c b/src/map/chrif.c index 10b7135b6..5d060a16f 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -412,7 +412,7 @@ int chrif_changemapserver(struct map_session_data* sd, uint32 ip, uint16 port) WFIFOW(char_fd,28) = htons(port); WFIFOB(char_fd,30) = sd->status.sex; WFIFOL(char_fd,31) = htonl(session[sd->fd]->client_addr); - WFIFOL(char_fd,35) = sd->gmlevel; + WFIFOL(char_fd,35) = sd->group_id; WFIFOSET(char_fd,39); return 0; } @@ -578,7 +578,7 @@ void chrif_authok(int fd) uint32 login_id1; uint32 login_id2; time_t expiration_time; - int gmlevel; + int group_id; struct mmo_charstatus* status; int char_id; struct auth_node *node; @@ -595,7 +595,7 @@ void chrif_authok(int fd) login_id1 = RFIFOL(fd,8); login_id2 = RFIFOL(fd,12); expiration_time = (time_t)(int32)RFIFOL(fd,16); - gmlevel = RFIFOL(fd,20); + group_id = RFIFOL(fd,20); changing_mapservers = (RFIFOB(fd,24)); status = (struct mmo_charstatus*)RFIFOP(fd,25); char_id = status->char_id; @@ -628,7 +628,7 @@ void chrif_authok(int fd) node->char_id == char_id && node->login_id1 == login_id1 ) { //Auth Ok - if (pc_authok(sd, login_id2, expiration_time, gmlevel, status, changing_mapservers)) + if (pc_authok(sd, login_id2, expiration_time, group_id, status, changing_mapservers)) return; } else { //Auth Failed pc_authfail(sd); diff --git a/src/map/clif.c b/src/map/clif.c index f38ce5ef2..3f7ad28ee 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -1779,7 +1779,7 @@ void clif_selllist(struct map_session_data *sd) { if( sd->status.inventory[i].nameid > 0 && sd->inventory_data[i] ) { - if( !itemdb_cansell(&sd->status.inventory[i], pc_isGM(sd)) ) + if( !itemdb_cansell(&sd->status.inventory[i], pc_get_group_level(sd)) ) continue; if( sd->status.inventory[i].expire_time ) @@ -2625,6 +2625,59 @@ void clif_guild_xy_remove(struct map_session_data *sd) clif_send(buf,packet_len(0x1eb),&sd->bl,GUILD_SAMEMAP_WOS); } +/*========================================== + * + *------------------------------------------*/ +static int clif_hpmeter_sub(struct block_list *bl, va_list ap) +{ + struct map_session_data *sd, *tsd; +#if PACKETVER < 20100126 + const int cmd = 0x106; +#else + const int cmd = 0x80e; +#endif + + sd = va_arg(ap, struct map_session_data *); + tsd = (TBL_PC *)bl; + + nullpo_ret(sd); + nullpo_ret(tsd); + + if( !tsd->fd || tsd == sd ) + return 0; + + if( !pc_has_permission(tsd, PC_PERM_VIEW_HPMETER) ) + return 0; + WFIFOHEAD(tsd->fd,packet_len(cmd)); + WFIFOW(tsd->fd,0) = cmd; + WFIFOL(tsd->fd,2) = sd->status.account_id; +#if PACKETVER < 20100126 + if( sd->battle_status.max_hp > INT16_MAX ) + { //To correctly display the %hp bar. [Skotlex] + WFIFOW(tsd->fd,6) = sd->battle_status.hp/(sd->battle_status.max_hp/100); + WFIFOW(tsd->fd,8) = 100; + } else { + WFIFOW(tsd->fd,6) = sd->battle_status.hp; + WFIFOW(tsd->fd,8) = sd->battle_status.max_hp; + } +#else + WFIFOL(tsd->fd,6) = sd->battle_status.hp; + WFIFOL(tsd->fd,10) = sd->battle_status.max_hp; +#endif + WFIFOSET(tsd->fd,packet_len(cmd)); + return 0; +} + +/*========================================== + * Server tells all players that are allowed to view HP bars + * and are nearby 'sd' that 'sd' hp bar was updated. + *------------------------------------------*/ +static int clif_hpmeter(struct map_session_data *sd) +{ + nullpo_ret(sd); + map_foreachinarea(clif_hpmeter_sub, sd->bl.m, sd->bl.x-AREA_SIZE, sd->bl.y-AREA_SIZE, sd->bl.x+AREA_SIZE, sd->bl.y+AREA_SIZE, BL_PC, sd); + return 0; +} /// Notifies client of a character parameter change. /// 00b0 <var id>.W <value>.L (ZC_PAR_CHANGE) @@ -2700,8 +2753,7 @@ void clif_updatestatus(struct map_session_data *sd,int type) case SP_HP: WFIFOL(fd,4)=sd->battle_status.hp; // TODO: Won't these overwrite the current packet? - if( battle_config.disp_hpmeter ) - clif_hpmeter(sd); + clif_hpmeter(sd); if( !battle_config.party_hp_mode && sd->status.party_id ) clif_party_hp(sd); if( sd->bg_id ) @@ -3922,7 +3974,6 @@ int clif_status_load_single(int fd, int id,int type,int flag,int val1, int val2, *------------------------------------------*/ static void clif_getareachar_pc(struct map_session_data* sd,struct map_session_data* dstsd) { - int gmlvl; struct block_list *d_bl; int i; @@ -3943,7 +3994,8 @@ static void clif_getareachar_pc(struct map_session_data* sd,struct map_session_d } if( (sd->status.party_id && dstsd->status.party_id == sd->status.party_id) || //Party-mate, or hpdisp setting. (sd->bg_id && sd->bg_id == dstsd->bg_id) || //BattleGround - (battle_config.disp_hpmeter && (gmlvl = pc_isGM(sd)) >= battle_config.disp_hpmeter && gmlvl >= pc_isGM(dstsd)) ) + pc_has_permission(sd, PC_PERM_VIEW_HPMETER) + ) clif_hpmeter_single(sd->fd, dstsd->bl.id, dstsd->battle_status.hp, dstsd->battle_status.max_hp); // display link (sd - dstsd) to sd @@ -5235,26 +5287,31 @@ void clif_status_change(struct block_list *bl,int type,int flag,unsigned int tic /// 008e <packet len>.W <message>.?B void clif_displaymessage(const int fd, const char* mes) { - // invalid pointer? nullpo_retv(mes); //Scrapped, as these are shared by disconnected players =X [Skotlex] if (fd == 0) ; else { - int len_mes = strlen(mes); - - if (len_mes > 0) { // don't send a void message (it's not displaying on the client chat). @help can send void line. - WFIFOHEAD(fd, 5 + len_mes); - WFIFOW(fd,0) = 0x8e; - WFIFOW(fd,2) = 5 + len_mes; // 4 + len + NULL teminate - memcpy(WFIFOP(fd,4), mes, len_mes + 1); - WFIFOSET(fd, 5 + len_mes); + char *message, *line; + + message = aStrdup(mes); + line = strtok(message, "\n"); + while(line != NULL) { + int len = strlen(line); + if (len > 0) { // don't send a void message (it's not displaying on the client chat). @help can send void line. + WFIFOHEAD(fd, 5 + len); + WFIFOW(fd,0) = 0x8e; + WFIFOW(fd,2) = 5 + len; // 4 + len + NULL teminate + memcpy(WFIFOP(fd,4), line, len + 1); + WFIFOSET(fd, 5 + len); + } + line = strtok(NULL, "\n"); } + aFree(message); } } - /// Send broadcast message in yellow or blue without font formatting (ZC_BROADCAST). /// 009a <packet len>.W <message>.?B void clif_broadcast(struct block_list* bl, const char* mes, int len, int type, enum send_target target) @@ -5518,7 +5575,7 @@ void clif_wis_message(int fd, const char* nick, const char* mes, int mes_len) WFIFOW(fd,2) = mes_len + NAME_LENGTH + 8; safestrncpy((char*)WFIFOP(fd,4), nick, NAME_LENGTH); WFIFOL(fd,28) = 0; // isAdmin; if nonzero, also displays text above char - // TODO: WFIFOL(fd,28) = ( pc_isGM(ssd) >= battle_config.lowest_gm_level ); + // TODO: WFIFOL(fd,28) = pc_get_group_level(ssd); safestrncpy((char*)WFIFOP(fd,32), mes, mes_len); WFIFOSET(fd,WFIFOW(fd,2)); #endif @@ -6419,64 +6476,6 @@ void clif_hpmeter_single(int fd, int id, unsigned int hp, unsigned int maxhp) WFIFOSET(fd, packet_len(cmd)); } -/*========================================== - * - *------------------------------------------*/ -int clif_hpmeter_sub(struct block_list *bl, va_list ap) -{ - struct map_session_data *sd, *tsd; - int level; -#if PACKETVER < 20100126 - const int cmd = 0x106; -#else - const int cmd = 0x80e; -#endif - - sd = va_arg(ap, struct map_session_data *); - tsd = (TBL_PC *)bl; - - nullpo_ret(sd); - nullpo_ret(tsd); - - if( !tsd->fd || tsd == sd ) - return 0; - - if( (level = pc_isGM(tsd)) < battle_config.disp_hpmeter || level < pc_isGM(sd) ) - return 0; - WFIFOHEAD(tsd->fd,packet_len(cmd)); - WFIFOW(tsd->fd,0) = cmd; - WFIFOL(tsd->fd,2) = sd->status.account_id; -#if PACKETVER < 20100126 - if( sd->battle_status.max_hp > INT16_MAX ) - { //To correctly display the %hp bar. [Skotlex] - WFIFOW(tsd->fd,6) = sd->battle_status.hp/(sd->battle_status.max_hp/100); - WFIFOW(tsd->fd,8) = 100; - } else { - WFIFOW(tsd->fd,6) = sd->battle_status.hp; - WFIFOW(tsd->fd,8) = sd->battle_status.max_hp; - } -#else - WFIFOL(tsd->fd,6) = sd->battle_status.hp; - WFIFOL(tsd->fd,10) = sd->battle_status.max_hp; -#endif - WFIFOSET(tsd->fd,packet_len(cmd)); - return 0; -} - -/*========================================== - * Server tells all nearby gms to 'sd' that 'sd' hp bar was updated - *------------------------------------------*/ -int clif_hpmeter(struct map_session_data *sd) -{ - nullpo_ret(sd); - - if( battle_config.disp_hpmeter ) - map_foreachinarea(clif_hpmeter_sub, sd->bl.m, sd->bl.x-AREA_SIZE, sd->bl.y-AREA_SIZE, sd->bl.x+AREA_SIZE, sd->bl.y+AREA_SIZE, BL_PC, sd); - - return 0; -} - - /// Notifies the client, that it's attack target is too far (ZC_ATTACK_FAILURE_FOR_DISTANCE). /// 0139 <target id>.L <target x>.W <target y>.W <x>.W <y>.W <atk range>.W void clif_movetoattack(struct map_session_data *sd,struct block_list *bl) @@ -9417,7 +9416,7 @@ void clif_parse_GetCharNameRequest(int fd, struct map_session_data *sd) sc = status_get_sc(bl); if (sc && sc->option&OPTION_INVISIBLE && !disguised(bl) && bl->type != BL_NPC && //Skip hidden NPCs which can be seen using Maya Purple - pc_isGM(sd) < battle_config.hack_info_GM_level + pc_get_group_level(sd) < battle_config.hack_info_GM_level ) { char gm_msg[256]; sprintf(gm_msg, "Hack on NameRequest: character '%s' (account: %d) requested the name of an invisible target (id: %d).\n", sd->status.name, sd->status.account_id, id); @@ -9506,21 +9505,13 @@ void clif_parse_GlobalMessage(int fd, struct map_session_data* sd) /// 0140 <map name>.16B <x>.W <y>.W void clif_parse_MapMove(int fd, struct map_session_data *sd) { - char output[MAP_NAME_LENGTH_EXT+15]; // Max length of a short: ' -6XXXX' -> 7 digits - char message[MAP_NAME_LENGTH_EXT+15+5]; // "/mm "+output + char command[MAP_NAME_LENGTH_EXT+25]; char* map_name; - if (battle_config.atc_gmonly && !pc_isGM(sd)) - return; - if(pc_isGM(sd) < get_atcommand_level("warp")) - return; - map_name = (char*)RFIFOP(fd,2); map_name[MAP_NAME_LENGTH_EXT-1]='\0'; - sprintf(output, "%s %d %d", map_name, RFIFOW(fd,18), RFIFOW(fd,20)); - atcommand_mapmove(fd, sd, "@mapmove", output); - sprintf(message, "/mm %s", output); - log_atcommand(sd, get_atcommand_level("warp"), message); + sprintf(command, "@mapmove %s %d %d", map_name, RFIFOW(fd,18), RFIFOW(fd,20)); + is_atcommand(fd, sd, command, 1); } @@ -9861,7 +9852,7 @@ void clif_parse_WisMessage(int fd, struct map_session_data* sd) // if player ignores everyone if (dstsd->state.ignoreAll) { - if (dstsd->sc.option & OPTION_INVISIBLE && pc_isGM(sd) < pc_isGM(dstsd)) + if (dstsd->sc.option & OPTION_INVISIBLE && pc_get_group_level(sd) < pc_get_group_level(dstsd)) clif_wis_end(fd, 1); // 1: target character is not loged in else clif_wis_end(fd, 3); // 3: everyone ignored by target @@ -9897,25 +9888,14 @@ void clif_parse_WisMessage(int fd, struct map_session_data* sd) /// 0099 <packet len>.W <text>.?B 00 void clif_parse_Broadcast(int fd, struct map_session_data* sd) { + char command[CHAT_SIZE_MAX+11]; char* msg = (char*)RFIFOP(fd,4); unsigned int len = RFIFOW(fd,2)-4; - int lv; - - if( battle_config.atc_gmonly && !pc_isGM(sd) ) - return; - if( pc_isGM(sd) < (lv=get_atcommand_level("broadcast")) ) - return; // as the length varies depending on the command used, just block unreasonably long strings len = mes_len_check(msg, len, CHAT_SIZE_MAX); - - intif_broadcast(msg, len, 0); - - { - char logmsg[CHAT_SIZE_MAX+4]; - sprintf(logmsg, "/b %s", msg); - log_atcommand(sd, lv, logmsg); - } + sprintf(command, "@broadcast %s", msg); + is_atcommand(fd, sd, command, 1); } @@ -11066,23 +11046,10 @@ void clif_parse_SolveCharName(int fd, struct map_session_data *sd) /// 1 = skill void clif_parse_ResetChar(int fd, struct map_session_data *sd) { - if( battle_config.atc_gmonly && !pc_isGM(sd) ) - return; - - if( pc_isGM(sd) < get_atcommand_level("reset") ) - return; - if( RFIFOW(fd,2) ) - pc_resetskill(sd,1); - else { - pc_resetstate(sd); - if( sd->mission_mobid ) { //bugreport:2200 - sd->mission_mobid = 0; - sd->mission_count = 0; - pc_setglobalreg(sd,"TK_MISSION_ID", 0); - } - } - log_atcommand(sd, get_atcommand_level("reset"), RFIFOW(fd,2) ? "/resetskill" : "/resetstate"); + is_atcommand(fd, sd, "@resetskill", 1); + else + is_atcommand(fd, sd, "@resetstat", 1); } @@ -11091,26 +11058,15 @@ void clif_parse_ResetChar(int fd, struct map_session_data *sd) /// 019c <packet len>.W <text>.?B void clif_parse_LocalBroadcast(int fd, struct map_session_data* sd) { + char command[CHAT_SIZE_MAX+16]; char* msg = (char*)RFIFOP(fd,4); unsigned int len = RFIFOW(fd,2)-4; - int lv; - - if( battle_config.atc_gmonly && !pc_isGM(sd) ) - return; - - if( pc_isGM(sd) < (lv=get_atcommand_level("localbroadcast")) ) - return; - + // as the length varies depending on the command used, just block unreasonably long strings len = mes_len_check(msg, len, CHAT_SIZE_MAX); - clif_broadcast(&sd->bl, msg, len, 0, ALL_SAMEMAP); - - { - char logmsg[CHAT_SIZE_MAX+5]; - sprintf(logmsg, "/lb %s", msg); - log_atcommand(sd, lv, logmsg); - } + sprintf(command, "@localbroadcast %s", msg); + is_atcommand(fd, sd, command, 1); } @@ -12168,10 +12124,7 @@ void clif_parse_ChangePetName(int fd, struct map_session_data *sd) void clif_parse_GMKick(int fd, struct map_session_data *sd) { struct block_list *target; - int tid,lv; - - if( battle_config.atc_gmonly && !pc_isGM(sd) ) - return; + int tid; tid = RFIFOL(fd,2); target = map_id2bl(tid); @@ -12183,69 +12136,36 @@ void clif_parse_GMKick(int fd, struct map_session_data *sd) switch (target->type) { case BL_PC: { - struct map_session_data *tsd = (struct map_session_data *)target; - if (pc_isGM(sd) <= pc_isGM(tsd)) - { - clif_GM_kickack(sd, 0); - return; - } - - lv = get_atcommand_level("kick"); - if( pc_isGM(sd) < lv ) - { - clif_GM_kickack(sd, 0); - return; - } - - { - char message[256]; - sprintf(message, "/kick %s (%d)", tsd->status.name, tsd->status.char_id); - log_atcommand(sd, lv, message); - } - - clif_GM_kick(sd, tsd); + char command[NAME_LENGTH+6]; + sprintf(command, "@kick %s", status_get_name(target)); + is_atcommand(fd, sd, command, 1); } break; + + /** + * This one does not invoke any atcommand, so we need to check for permissions. + */ case BL_MOB: { - lv = get_atcommand_level("killmonster"); - if( pc_isGM(sd) < lv ) - { + char command[100]; + if( !pc_can_use_command(sd, "killmonster", COMMAND_ATCOMMAND)) { clif_GM_kickack(sd, 0); return; } - - { - char message[256]; - sprintf(message, "/kick %s (%d)", status_get_name(target), status_get_class(target)); - log_atcommand(sd, lv, message); - } - + sprintf(command, "/kick %s (%d)", status_get_name(target), status_get_class(target)); + log_atcommand(sd, command); status_percent_damage(&sd->bl, target, 100, 0, true); // can invalidate 'target' } break; + case BL_NPC: { - struct npc_data* nd = (struct npc_data *)target; - lv = get_atcommand_level("unloadnpc"); - if( pc_isGM(sd) < lv ) - { - clif_GM_kickack(sd, 0); - return; - } - - { - char message[256]; - sprintf(message, "/kick %s (%d)", status_get_name(target), status_get_class(target)); - log_atcommand(sd, lv, message); - } - - // copy-pasted from atcommand_unloadnpc - npc_unload_duplicates(nd); - npc_unload(nd); // invalidates 'target' - npc_read_event_script(); + char command[NAME_LENGTH+11]; + sprintf(command, "@unloadnpc %s", status_get_name(target)); + is_atcommand(fd, sd, command, 1); } break; + default: clif_GM_kickack(sd, 0); } @@ -12271,21 +12191,13 @@ void clif_parse_GMKickAll(int fd, struct map_session_data* sd) void clif_parse_GMShift(int fd, struct map_session_data *sd) {// FIXME: remove is supposed to receive account name for clients prior 20100803RE char *player_name; - int lv; - - if( battle_config.atc_gmonly && !pc_isGM(sd) ) - return; - if( pc_isGM(sd) < (lv=get_atcommand_level("goto")) ) - return; + char command[NAME_LENGTH+8]; player_name = (char*)RFIFOP(fd,2); player_name[NAME_LENGTH-1] = '\0'; - atcommand_jumpto(fd, sd, "@jumpto", player_name); // as @jumpto - { - char message[NAME_LENGTH+7]; - sprintf(message, "/shift %s", player_name); - log_atcommand(sd, lv, message); - } + + sprintf(command, "@jumpto %s", player_name); + is_atcommand(fd, sd, command, 1); } @@ -12294,31 +12206,15 @@ void clif_parse_GMShift(int fd, struct map_session_data *sd) /// 0843 <account id>.L void clif_parse_GMRemove2(int fd, struct map_session_data* sd) { - int account_id, lv; + int account_id; struct map_session_data* pl_sd; - if( battle_config.atc_gmonly && !pc_isGM(sd) ) - { - return; - } - - if( pc_isGM(sd) < ( lv = get_atcommand_level("goto") ) ) - { - return; - } - account_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]); - - if( ( pl_sd = map_id2sd(account_id) ) != NULL && pc_isGM(sd) >= pc_isGM(pl_sd) ) + if( (pl_sd = map_id2sd(account_id)) != NULL ) { - pc_warpto(sd, pl_sd); - } - - { - char message[32]; - - sprintf(message, "/remove %d", account_id); - log_atcommand(sd, lv, message); + char command[NAME_LENGTH+8]; + sprintf(command, "@jumpto %s", pl_sd->status.name); + is_atcommand(fd, sd, command, 1); } } @@ -12333,22 +12229,13 @@ void clif_parse_GMRemove2(int fd, struct map_session_data* sd) void clif_parse_GMRecall(int fd, struct map_session_data *sd) {// FIXME: recall is supposed to receive account name for clients prior 20100803RE char *player_name; - int lv; - - if( battle_config.atc_gmonly && !pc_isGM(sd) ) - return; - - if( pc_isGM(sd) < (lv=get_atcommand_level("recall")) ) - return; + char command [NAME_LENGTH+8]; player_name = (char*)RFIFOP(fd,2); player_name[NAME_LENGTH-1] = '\0'; - atcommand_recall(fd, sd, "@recall", player_name); // as @recall - { - char message[NAME_LENGTH+8]; - sprintf(message, "/recall %s", player_name); - log_atcommand(sd, lv, message); - } + + sprintf(command, "@recall %s", player_name); + is_atcommand(fd, sd, command, 1); } @@ -12357,31 +12244,15 @@ void clif_parse_GMRecall(int fd, struct map_session_data *sd) /// 0842 <account id>.L void clif_parse_GMRecall2(int fd, struct map_session_data* sd) { - int account_id, lv; + int account_id; struct map_session_data* pl_sd; - if( battle_config.atc_gmonly && !pc_isGM(sd) ) - { - return; - } - - if( pc_isGM(sd) < ( lv = get_atcommand_level("recall") ) ) - { - return; - } - account_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]); - - if( ( pl_sd = map_id2sd(account_id) ) != NULL && pc_isGM(sd) >= pc_isGM(pl_sd) ) + if( (pl_sd = map_id2sd(account_id)) != NULL ) { - pc_recall(sd, pl_sd); - } - - { - char message[32]; - - sprintf(message, "/recall %d", account_id); - log_atcommand(sd, lv, message); + char command[NAME_LENGTH+8]; + sprintf(command, "@recall %s", pl_sd->status.name); + is_atcommand(fd, sd, command, 1); } } @@ -12392,33 +12263,21 @@ void clif_parse_GMRecall2(int fd, struct map_session_data* sd) void clif_parse_GM_Monster_Item(int fd, struct map_session_data *sd) { char *monster_item_name; - char message[NAME_LENGTH+10]; //For logging. - int level; - - if( battle_config.atc_gmonly && !pc_isGM(sd) ) - return; + char command[NAME_LENGTH+10]; monster_item_name = (char*)RFIFOP(fd,2); monster_item_name[NAME_LENGTH-1] = '\0'; if( mobdb_searchname(monster_item_name) ) { - if( pc_isGM(sd) < (level=get_atcommand_level("monster")) ) - return; - atcommand_monster(fd, sd, "@monster", monster_item_name); // as @monster - { //Log action. [Skotlex] - snprintf(message, sizeof(message)-1, "@monster %s", monster_item_name); - log_atcommand(sd, level, message); - } + snprintf(command, sizeof(command)-1, "@monster %s", monster_item_name); + is_atcommand(fd, sd, command, 1); return; } - if( itemdb_searchname(monster_item_name) == NULL ) - return; - if( pc_isGM(sd) < (level = get_atcommand_level("item")) ) + + if( itemdb_searchname(monster_item_name) ) { + snprintf(command, sizeof(command)-1, "@item %s", monster_item_name); + is_atcommand(fd, sd, command, 1); return; - atcommand_item(fd, sd, "@item", monster_item_name); // as @item - { //Log action. [Skotlex] - sprintf(message, "@item %s", monster_item_name); - log_atcommand(sd, level, message); } } @@ -12429,26 +12288,7 @@ void clif_parse_GM_Monster_Item(int fd, struct map_session_data *sd) /// TODO: Any OPTION_* ? void clif_parse_GMHide(int fd, struct map_session_data *sd) { - if( battle_config.atc_gmonly && !pc_isGM(sd) ) - return; - - if( pc_isGM(sd) < get_atcommand_level("hide") ) - return; - - if( sd->sc.option & OPTION_INVISIBLE ) { - sd->sc.option &= ~OPTION_INVISIBLE; - if (sd->disguise) - status_set_viewdata(&sd->bl, sd->disguise); - else - status_set_viewdata(&sd->bl, sd->status.class_); - clif_displaymessage(fd, "Invisible: Off."); - } else { - sd->sc.option |= OPTION_INVISIBLE; - sd->vd.class_ = INVISIBLE_CLASS; - clif_displaymessage(fd, "Invisible: On."); - log_atcommand(sd, get_atcommand_level("hide"), "/hide"); - } - clif_changeoption(&sd->bl); + is_atcommand(fd, sd, "@hide", 1); } @@ -12460,41 +12300,28 @@ void clif_parse_GMHide(int fd, struct map_session_data *sd) /// 2 = self mute (+10 minutes) void clif_parse_GMReqNoChat(int fd,struct map_session_data *sd) { - int id, type, value, level; + int id, type, value; struct map_session_data *dstsd; + char command[NAME_LENGTH+15]; id = RFIFOL(fd,2); type = RFIFOB(fd,6); value = RFIFOW(fd,7); if( type == 0 ) - value = 0 - value; + value = -value; //If type is 2 and the ids don't match, this is a crafted hacked packet! //Disabled because clients keep self-muting when you give players public @ commands... [Skotlex] - if (type == 2 /* && (pc_isGM(sd) > 0 || sd->bl.id != id)*/) + if (type == 2 /* && (pc_get_group_level(sd) > 0 || sd->bl.id != id)*/) return; dstsd = map_id2sd(id); if( dstsd == NULL ) return; - if( (level = pc_isGM(sd)) > pc_isGM(dstsd) && level >= get_atcommand_level("mute") ) - { - clif_manner_message(sd, 0); - clif_manner_message(dstsd, 5); - - if( dstsd->status.manner < value ) { - dstsd->status.manner -= value; - sc_start(&dstsd->bl,SC_NOCHAT,100,0,0); - } else { - dstsd->status.manner = 0; - status_change_end(&dstsd->bl, SC_NOCHAT, INVALID_TIMER); - } - - if( type != 2 ) - clif_GM_silence(sd, dstsd, type); - } + sprintf(command, "@mute %d %s", value, dstsd->status.name); + is_atcommand(fd, sd, command, 1); } @@ -12503,23 +12330,12 @@ void clif_parse_GMReqNoChat(int fd,struct map_session_data *sd) /// 0212 <char name>.24B void clif_parse_GMRc(int fd, struct map_session_data* sd) { - char* name = (char*)RFIFOP(fd,2); - struct map_session_data* dstsd; - name[23] = '\0'; - dstsd = map_nick2sd(name); - if( dstsd == NULL ) - return; - - if( pc_isGM(sd) > pc_isGM(dstsd) && pc_isGM(sd) >= get_atcommand_level("mute") ) - { - clif_manner_message(sd, 0); - clif_manner_message(dstsd, 3); - - dstsd->status.manner -= 60; - sc_start(&dstsd->bl,SC_NOCHAT,100,0,0); + char command[NAME_LENGTH+15]; + char *name = (char*)RFIFOP(fd,2); - clif_GM_silence(sd, dstsd, 1); - } + name[NAME_LENGTH-1] = '\0'; + sprintf(command, "@mute %d %s", 60, name); + is_atcommand(fd, sd, command, 1); } @@ -12552,10 +12368,7 @@ void clif_parse_GMChangeMapType(int fd, struct map_session_data *sd) { int x,y,type; - if( battle_config.atc_gmonly && !pc_isGM(sd) ) - return; - - if( pc_isGM(sd) < 99 ) //TODO: add proper check + if( pc_has_permission(sd, PC_PERM_USE_CHANGEMAPTYPE) ) return; x = RFIFOW(fd,2); @@ -12576,7 +12389,6 @@ void clif_parse_GMChangeMapType(int fd, struct map_session_data *sd) /// 1 = (/in nick) allow speech from nick void clif_parse_PMIgnore(int fd, struct map_session_data* sd) { - char output[512]; char* nick; uint8 type; int i; @@ -12592,12 +12404,7 @@ void clif_parse_PMIgnore(int fd, struct map_session_data* sd) if( type == 0 ) { // Add name to ignore list (block) - - // Bot-check... - if (strcmp(wisp_server_name, nick) == 0) - { // to find possible bot users who automaticaly ignore people - sprintf(output, "Character '%s' (account: %d) has tried to block wisps from '%s' (wisp name of the server). Bot user?", sd->status.name, sd->status.account_id, wisp_server_name); - intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, output); + if (strcmp(wisp_server_name, nick) == 0) { WFIFOB(fd,3) = 1; // fail WFIFOSET(fd, packet_len(0x0d1)); return; @@ -13412,14 +13219,12 @@ void clif_parse_Check(int fd, struct map_session_data *sd) char charname[NAME_LENGTH]; struct map_session_data* pl_sd; - if( pc_isGM(sd) < battle_config.gm_check_minlevel ) - { + if(!pc_has_permission(sd, PC_PERM_USE_CHECK)) return; - } safestrncpy(charname, (const char*)RFIFOP(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]), sizeof(charname)); - if( ( pl_sd = map_nick2sd(charname) ) == NULL || pc_isGM(sd) < pc_isGM(pl_sd) ) + if( ( pl_sd = map_nick2sd(charname) ) == NULL || pc_get_group_level(sd) < pc_get_group_level(pl_sd) ) { return; } @@ -14003,9 +13808,9 @@ void clif_parse_Auction_setitem(int fd, struct map_session_data *sd) return; } - if( !pc_can_give_items(pc_isGM(sd)) || sd->status.inventory[idx].expire_time || + if( !pc_can_give_items(sd) || sd->status.inventory[idx].expire_time || !sd->status.inventory[idx].identify || - !itemdb_canauction(&sd->status.inventory[idx],pc_isGM(sd)) ) { // Quest Item or something else + !itemdb_canauction(&sd->status.inventory[idx],pc_get_group_level(sd)) ) { // Quest Item or something else clif_Auction_setitem(sd->fd, idx, true); return; } @@ -14158,7 +13963,7 @@ void clif_parse_Auction_bid(int fd, struct map_session_data *sd) unsigned int auction_id = RFIFOL(fd,2); int bid = RFIFOL(fd,6); - if( !pc_can_give_items(pc_isGM(sd)) ) { //They aren't supposed to give zeny [Inkfish] + if( !pc_can_give_items(sd) ) { //They aren't supposed to give zeny [Inkfish] clif_displaymessage(sd->fd, msg_txt(246)); return; } @@ -14456,7 +14261,7 @@ void clif_parse_ViewPlayerEquip(int fd, struct map_session_data* sd) if (!tsd) return; - if( tsd->status.show_equip || (battle_config.gm_viewequip_min_lv && pc_isGM(sd) >= battle_config.gm_viewequip_min_lv) ) + if( tsd->status.show_equip || pc_has_permission(sd, PC_PERM_VIEW_EQUIPMENT) ) clif_viewequip_ack(sd, tsd); else clif_viewequip_fail(sd); @@ -15995,11 +15800,11 @@ static int clif_parse(int fd) //Disassociate character from the socket connection. session[fd]->session_data = NULL; sd->fd = 0; - ShowInfo("%sCharacter '"CL_WHITE"%s"CL_RESET"' logged off (using @autotrade).\n", (pc_isGM(sd))?"GM ":"", sd->status.name); + ShowInfo("Character '"CL_WHITE"%s"CL_RESET"' logged off (using @autotrade).\n", sd->status.name); } else if (sd->state.active) { // Player logout display [Valaris] - ShowInfo("%sCharacter '"CL_WHITE"%s"CL_RESET"' logged off.\n", (pc_isGM(sd))?"GM ":"", sd->status.name); + ShowInfo("Character '"CL_WHITE"%s"CL_RESET"' logged off.\n", sd->status.name); clif_quitsave(fd, sd); } else { //Unusual logout (during log on/off/map-changer procedure) diff --git a/src/map/clif.h b/src/map/clif.h index 206b7d68e..3994541b5 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -504,8 +504,6 @@ void clif_party_xy(struct map_session_data *sd); void clif_party_xy_single(int fd, struct map_session_data *sd); void clif_party_hp(struct map_session_data *sd); void clif_hpmeter_single(int fd, int id, unsigned int hp, unsigned int maxhp); -int clif_hpmeter(struct map_session_data *sd); -int clif_hpmeter_sub(struct block_list *bl, va_list ap); // guild void clif_guild_created(struct map_session_data *sd,int flag); diff --git a/src/map/intif.c b/src/map/intif.c index 51002f027..f074b032b 100644 --- a/src/map/intif.c +++ b/src/map/intif.c @@ -237,22 +237,22 @@ int intif_wis_replay(int id, int flag) } // The transmission of GM only Wisp/Page from server to inter-server -int intif_wis_message_to_gm(char *Wisp_name, int min_gm_level, char *mes) +int intif_wis_message_to_gm(char *wisp_name, int permission, char *mes) { int mes_len; if (CheckForCharServer()) return 0; mes_len = strlen(mes) + 1; // + null - WFIFOHEAD(inter_fd, mes_len + 30); + WFIFOHEAD(inter_fd, mes_len + 32); WFIFOW(inter_fd,0) = 0x3003; - WFIFOW(inter_fd,2) = mes_len + 30; - memcpy(WFIFOP(inter_fd,4), Wisp_name, NAME_LENGTH); - WFIFOW(inter_fd,4+NAME_LENGTH) = (short)min_gm_level; - memcpy(WFIFOP(inter_fd,6+NAME_LENGTH), mes, mes_len); + WFIFOW(inter_fd,2) = mes_len + 32; + memcpy(WFIFOP(inter_fd,4), wisp_name, NAME_LENGTH); + WFIFOL(inter_fd,4+NAME_LENGTH) = permission; + memcpy(WFIFOP(inter_fd,8+NAME_LENGTH), mes, mes_len); WFIFOSET(inter_fd, WFIFOW(inter_fd,2)); if (battle_config.etc_log) - ShowNotice("intif_wis_message_to_gm: from: '%s', min level: %d, message: '%s'.\n", Wisp_name, min_gm_level, mes); + ShowNotice("intif_wis_message_to_gm: from: '%s', required permission: %d, message: '%s'.\n", wisp_name, permission, mes); return 0; } @@ -860,11 +860,13 @@ int intif_parse_WisEnd(int fd) static int mapif_parse_WisToGM_sub(struct map_session_data* sd,va_list va) { - int min_gm_level = va_arg(va, int); + int permission = va_arg(va, int); char *wisp_name; char *message; int len; - if (pc_isGM(sd) < min_gm_level) return 0; + + if (!pc_has_permission(sd, permission)) + return 0; wisp_name = va_arg(va, char*); message = va_arg(va, char*); len = va_arg(va, int); @@ -873,22 +875,22 @@ static int mapif_parse_WisToGM_sub(struct map_session_data* sd,va_list va) } // Received wisp message from map-server via char-server for ALL gm -// 0x3003/0x3803 <packet_len>.w <wispname>.24B <min_gm_level>.w <message>.?B +// 0x3003/0x3803 <packet_len>.w <wispname>.24B <permission>.l <message>.?B int mapif_parse_WisToGM(int fd) { - int min_gm_level, mes_len; + int permission, mes_len; char Wisp_name[NAME_LENGTH]; char mbuf[255]; char *message; - mes_len = RFIFOW(fd,2) - 30; + mes_len = RFIFOW(fd,2) - 32; message = (char *) (mes_len >= 255 ? (char *) aMallocA(mes_len) : mbuf); - min_gm_level = (int)RFIFOW(fd,28); + permission = RFIFOL(fd,28); safestrncpy(Wisp_name, (char*)RFIFOP(fd,4), NAME_LENGTH); - safestrncpy(message, (char*)RFIFOP(fd,30), mes_len); - // information is sended to all online GM - map_foreachpc(mapif_parse_WisToGM_sub, min_gm_level, Wisp_name, message, mes_len); + safestrncpy(message, (char*)RFIFOP(fd,32), mes_len); + // information is sent to all online GM + map_foreachpc(mapif_parse_WisToGM_sub, permission, Wisp_name, message, mes_len); if (message != mbuf) aFree(message); diff --git a/src/map/intif.h b/src/map/intif.h index 88e1f1ce7..b1315f40c 100644 --- a/src/map/intif.h +++ b/src/map/intif.h @@ -20,7 +20,7 @@ int intif_broadcast(const char* mes, int len, int type); int intif_broadcast2(const char* mes, int len, unsigned long fontColor, short fontType, short fontSize, short fontAlign, short fontY); int intif_wis_message(struct map_session_data *sd,char *nick,char *mes,int mes_len); -int intif_wis_message_to_gm(char *Wisp_name, int min_gm_level, char *mes); +int intif_wis_message_to_gm(char *Wisp_name, int permission, char *mes); int intif_saveregistry(struct map_session_data *sd, int type); int intif_request_registry(struct map_session_data *sd, int flag); diff --git a/src/map/log.c b/src/map/log.c index 7823b5e02..4d4fa166a 100644 --- a/src/map/log.c +++ b/src/map/log.c @@ -282,12 +282,13 @@ void log_mvpdrop(struct map_session_data* sd, int monster_id, int* log_mvp) } -/// logs used GM commands -void log_atcommand(struct map_session_data* sd, int cmdlvl, const char* message) +/// logs used atcommands +void log_atcommand(struct map_session_data* sd, const char* message) { nullpo_retv(sd); - if( cmdlvl < log_config.gm ) + if( !log_config.commands || + !pc_should_log_commands(sd) ) return; if( log_config.sql_logs ) @@ -460,8 +461,8 @@ int log_config_read(const char* cfgName) log_config.filter = config_switch(w2); else if( strcmpi(w1, "log_zeny") == 0 ) log_config.zeny = config_switch(w2); - else if( strcmpi(w1, "log_gm") == 0 ) - log_config.gm = config_switch(w2); + else if( strcmpi(w1, "log_commands") == 0 ) + log_config.commands = config_switch(w2); else if( strcmpi(w1, "log_npc") == 0 ) log_config.npc = config_switch(w2); else if( strcmpi(w1, "log_chat") == 0 ) @@ -508,9 +509,9 @@ int log_config_read(const char* cfgName) { ShowInfo("Logging chat to %s '%s'.\n", target, log_config.log_chat); } - if( log_config.gm ) + if( log_config.commands ) { - ShowInfo("Logging gm commands to %s '%s'.\n", target, log_config.log_gm); + ShowInfo("Logging commands to %s '%s'.\n", target, log_config.log_gm); } if( log_config.mvpdrop ) { diff --git a/src/map/log.h b/src/map/log.h index 5324bb599..ac85b7ccb 100644 --- a/src/map/log.h +++ b/src/map/log.h @@ -59,7 +59,7 @@ void log_zeny(struct map_session_data* sd, e_log_pick_type type, struct map_sess void log_npc(struct map_session_data* sd, const char *message); void log_chat(e_log_chat_type type, int type_id, int src_charid, int src_accid, const char* map, int x, int y, const char* dst_charname, const char* message); -void log_atcommand(struct map_session_data* sd, int cmdlvl, const char* message); +void log_atcommand(struct map_session_data* sd, const char* message); /// old, but useful logs void log_branch(struct map_session_data* sd); @@ -74,7 +74,7 @@ extern struct Log_Config bool sql_logs; bool log_chat_woe_disable; int rare_items_log,refine_items_log,price_items_log,amount_items_log; //for filter - int branch, mvpdrop, zeny, gm, npc, chat; + int branch, mvpdrop, zeny, commands, npc, chat; char log_branch[64], log_pick[64], log_zeny[64], log_mvpdrop[64], log_gm[64], log_npc[64], log_chat[64]; } log_config; diff --git a/src/map/mail.c b/src/map/mail.c index 7c1b9be21..b839c3d1f 100644 --- a/src/map/mail.c +++ b/src/map/mail.c @@ -63,7 +63,7 @@ unsigned char mail_setitem(struct map_session_data *sd, int idx, int amount) { if( idx == 0 ) { // Zeny Transfer - if( amount < 0 || !pc_can_give_items(pc_isGM(sd)) ) + if( amount < 0 || !pc_can_give_items(sd) ) return 1; if( amount > sd->status.zeny ) @@ -82,8 +82,8 @@ unsigned char mail_setitem(struct map_session_data *sd, int idx, int amount) return 1; if( amount < 0 || amount > sd->status.inventory[idx].amount ) return 1; - if( !pc_can_give_items(pc_isGM(sd)) || sd->status.inventory[idx].expire_time || - !itemdb_canmail(&sd->status.inventory[idx],pc_isGM(sd)) ) + if( !pc_can_give_items(sd) || sd->status.inventory[idx].expire_time || + !itemdb_canmail(&sd->status.inventory[idx],pc_get_group_level(sd)) ) return 1; sd->mail.index = idx; @@ -184,7 +184,7 @@ void mail_deliveryfail(struct map_session_data *sd, struct mail_message *msg) // This function only check if the mail operations are valid bool mail_invalid_operation(struct map_session_data *sd) { - if( !map[sd->bl.m].flag.town && pc_isGM(sd) < get_atcommand_level("mail") ) + if( !map[sd->bl.m].flag.town && !pc_can_use_command(sd, "mail", COMMAND_ATCOMMAND) ) { ShowWarning("clif_parse_Mail: char '%s' trying to do invalid mail operations.\n", sd->status.name); return true; diff --git a/src/map/party.c b/src/map/party.c index 2c9b45a11..8f8a0cd8a 100644 --- a/src/map/party.c +++ b/src/map/party.c @@ -344,12 +344,8 @@ int party_invite(struct map_session_data *sd,struct map_session_data *tsd) return 0; } - if ( (pc_isGM(sd) >= battle_config.lowest_gm_level && pc_isGM(tsd) < battle_config.lowest_gm_level && !battle_config.gm_can_party && pc_isGM(sd) < battle_config.gm_cant_party_min_lv) - || ( pc_isGM(sd) < battle_config.lowest_gm_level && pc_isGM(tsd) >= battle_config.lowest_gm_level && !battle_config.gm_can_party && pc_isGM(tsd) < battle_config.gm_cant_party_min_lv) ) - { - //GMs can't invite non GMs to the party if not above the invite trust level - //Likewise, as long as gm_can_party is off, players can't invite GMs. - clif_displaymessage(sd->fd, msg_txt(81)); + if (!pc_has_permission(sd, PC_PERM_PARTY) || !pc_has_permission(tsd, PC_PERM_PARTY)) { + clif_displaymessage(sd->fd, msg_txt(81)); // "Your GM level doesn't authorize you to preform this action on the specified player." return 0; } diff --git a/src/map/pc.c b/src/map/pc.c index 22f165374..94d505c15 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -37,6 +37,7 @@ #include "skill.h" #include "status.h" // struct status_data #include "pc.h" +#include "pc_groups.h" #include "quest.h" #include <stdio.h> @@ -81,9 +82,14 @@ int pc_class2idx(int class_) { return class_; } -int pc_isGM(struct map_session_data* sd) +int inline pc_get_group_id(struct map_session_data *sd) { - return sd->gmlevel; + return sd->group_id; +} + +int inline pc_get_group_level(struct map_session_data *sd) +{ + return pc_group_id2level(pc_get_group_id(sd)); } static int pc_invincible_timer(int tid, unsigned int tick, int id, intptr_t data) @@ -478,13 +484,12 @@ void pc_inventory_rental_add(struct map_session_data *sd, int seconds) sd->rental_timer = add_timer(gettick() + min(tick,3600000), pc_inventory_rental_end, sd->bl.id, 0); } -/*========================================== - Determines if the GM can give / drop / trade / vend items - Args: GM Level (current player GM level) - *------------------------------------------*/ -bool pc_can_give_items(int level) +/** + * Determines if player can give / drop / trade / vend items + */ +bool pc_can_give_items(struct map_session_data *sd) { - return( level < battle_config.gm_cant_drop_min_lv || level > battle_config.gm_cant_drop_max_lv ); + return pc_has_permission(sd, PC_PERM_TRADE); } /*========================================== @@ -815,7 +820,7 @@ int pc_isequip(struct map_session_data *sd,int n) item = sd->inventory_data[n]; - if( battle_config.gm_allequip>0 && pc_isGM(sd)>=battle_config.gm_allequip ) + if(pc_has_permission(sd, PC_PERM_USE_ALL_EQUIPMENT)) return 1; if(item == NULL) @@ -885,14 +890,14 @@ int pc_isequip(struct map_session_data *sd,int n) * session idに問題無し * char鯖から送られてきたステ?タスを設定 *------------------------------------------*/ -bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_time, int gmlevel, struct mmo_charstatus *st, bool changing_mapservers) +bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_time, int group_id, struct mmo_charstatus *st, bool changing_mapservers) { int i; unsigned long tick = gettick(); uint32 ip = session[sd->fd]->client_addr; sd->login_id2 = login_id2; - sd->gmlevel = gmlevel; + sd->group_id = group_id; memcpy(&sd->status, st, sizeof(*st)); if (st->sex != sd->status.sex) { @@ -975,7 +980,7 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim pc_setequipindex(sd); status_change_init(&sd->bl); - if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) && (pc_isGM(sd) >= get_atcommand_level("hide"))) + if (pc_can_use_command(sd, "hide", COMMAND_ATCOMMAND)) sd->status.option &= (OPTION_MASK | OPTION_INVISIBLE); else sd->status.option &= OPTION_MASK; @@ -1015,20 +1020,12 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim sd->die_counter=-1; //display login notice - if( sd->gmlevel >= battle_config.lowest_gm_level ) - ShowInfo("GM '"CL_WHITE"%s"CL_RESET"' logged in." - " (AID/CID: '"CL_WHITE"%d/%d"CL_RESET"'," - " Packet Ver: '"CL_WHITE"%d"CL_RESET"', IP: '"CL_WHITE"%d.%d.%d.%d"CL_RESET"'," - " GM Level '"CL_WHITE"%d"CL_RESET"').\n", - sd->status.name, sd->status.account_id, sd->status.char_id, - sd->packet_ver, CONVIP(ip), sd->gmlevel); - else - ShowInfo("'"CL_WHITE"%s"CL_RESET"' logged in." - " (AID/CID: '"CL_WHITE"%d/%d"CL_RESET"'," - " Packet Ver: '"CL_WHITE"%d"CL_RESET"', IP: '"CL_WHITE"%d.%d.%d.%d"CL_RESET"').\n", - sd->status.name, sd->status.account_id, sd->status.char_id, - sd->packet_ver, CONVIP(ip)); - + ShowInfo("'"CL_WHITE"%s"CL_RESET"' logged in." + " (AID/CID: '"CL_WHITE"%d/%d"CL_RESET"'," + " Packet Ver: '"CL_WHITE"%d"CL_RESET"', IP: '"CL_WHITE"%d.%d.%d.%d"CL_RESET"'," + " Group '"CL_WHITE"%d"CL_RESET"').\n", + sd->status.name, sd->status.account_id, sd->status.char_id, + sd->packet_ver, CONVIP(ip), sd->group_id); // Send friends list clif_friendslist_send(sd); @@ -1280,7 +1277,7 @@ int pc_calc_skilltree(struct map_session_data *sd) } } - if( battle_config.gm_allskill > 0 && pc_isGM(sd) >= battle_config.gm_allskill ) { + if( pc_has_permission(sd, PC_PERM_ALL_SKILL) ) { for( i = 0; i < MAX_SKILL; i++ ) { switch(i) { /** @@ -4004,7 +4001,7 @@ int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amoun return 1; data = itemdb_search(item_data->nameid); - if( !itemdb_cancartstore(item_data, pc_isGM(sd)) ) + if( !itemdb_cancartstore(item_data, pc_get_group_level(sd)) ) { // Check item trade restrictions [Skotlex] clif_displaymessage (sd->fd, msg_txt(264)); return 1; @@ -4451,45 +4448,6 @@ int pc_randomwarp(struct map_session_data *sd, clr_type type) return 0; } - -/// Warps one player to another. -/// @param sd player to warp. -/// @param pl_sd player to warp to. -int pc_warpto(struct map_session_data* sd, struct map_session_data* pl_sd) -{ - if( map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd) ) - { - return -2; - } - - if( map[pl_sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd) ) - { - return -3; - } - - return pc_setpos(sd, pl_sd->mapindex, pl_sd->bl.x, pl_sd->bl.y, CLR_TELEPORT); -} - - -/// Recalls one player to another. -/// @param sd player to warp to. -/// @param pl_sd player to warp. -int pc_recall(struct map_session_data* sd, struct map_session_data* pl_sd) -{ - if( map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd) ) - { - return -2; - } - - if( map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd) ) - { - return -3; - } - - return pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, CLR_RESPAWN); -} - - /*========================================== * Records a memo point at sd's current position * pos - entry to replace, (-1: shift oldest entry out) @@ -4501,7 +4459,7 @@ int pc_memo(struct map_session_data* sd, int pos) nullpo_ret(sd); // check mapflags - if( sd->bl.m >= 0 && (map[sd->bl.m].flag.nomemo || map[sd->bl.m].flag.nowarpto) && battle_config.any_warp_GM_min_level > pc_isGM(sd) ) { + if( sd->bl.m >= 0 && (map[sd->bl.m].flag.nomemo || map[sd->bl.m].flag.nowarpto) && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE) ) { clif_skill_teleportmessage(sd, 1); // "Saved point cannot be memorized." return 0; } @@ -5586,7 +5544,7 @@ int pc_allskillup(struct map_session_data *sd) } //pc_calc_skilltree takes care of setting the ID to valid skills. [Skotlex] - if (battle_config.gm_allskill > 0 && pc_isGM(sd) >= battle_config.gm_allskill) + if (pc_has_permission(sd, PC_PERM_ALL_SKILL)) { //Get ALL skills except npc/guild ones. [Skotlex] //and except SG_DEVIL [Komurka] and MO_TRIPLEATTACK and RG_SNATCHER [ultramage] for(i=0;i<MAX_SKILL;i++){ @@ -5761,6 +5719,13 @@ int pc_resetstate(struct map_session_data* sd) clif_updatestatus(sd,SP_ULUK); // End Addition clif_updatestatus(sd,SP_STATUSPOINT); + + if( sd->mission_mobid ) { //bugreport:2200 + sd->mission_mobid = 0; + sd->mission_count = 0; + pc_setglobalreg(sd,"TK_MISSION_ID", 0); + } + status_calc_pc(sd,0); return 1; @@ -7026,14 +6991,13 @@ int pc_setriding(TBL_PC* sd, int flag) /*========================================== * アイテムドロップ可不可判定 *------------------------------------------*/ -int pc_candrop(struct map_session_data *sd,struct item *item) +int pc_candrop(struct map_session_data *sd, struct item *item) { - int level = pc_isGM(sd); if( item && item->expire_time ) return 0; - if( !pc_can_give_items(level) ) //check if this GM level can drop items + if( !pc_can_give_items(sd) ) //check if this GM level can drop items return 0; - return (itemdb_isdropable(item, level)); + return (itemdb_isdropable(item, pc_get_group_level(sd))); } /*========================================== @@ -8255,6 +8219,37 @@ bool pc_isautolooting(struct map_session_data *sd, int nameid) return (i != AUTOLOOTITEM_SIZE); } +/** + * Checks if player can use @/#command + * @param sd Player map session data + * @param command Command name without @/# and params + * @param type is it atcommand or charcommand + */ +bool pc_can_use_command(struct map_session_data *sd, const char *command, AtCommandType type) +{ + return pc_group_can_use_command(pc_get_group_id(sd), command, type); +} + +/** + * Checks if player has a permission + * @param sd Player map session data + * @param permission permission to check + */ +bool pc_has_permission(struct map_session_data *sd, int permission) +{ + return pc_group_has_permission(pc_get_group_id(sd), permission); +} + +/** + * Checks if commands used by a player should be logged + * according to their group setting. + * @param sd Player map session data + */ +bool pc_should_log_commands(struct map_session_data *sd) +{ + return pc_group_should_log_commands(pc_get_group_id(sd)); +} + int pc_split_str(char *str,char **val,int num) { int i; @@ -8628,6 +8623,7 @@ int pc_read_motd(void) *------------------------------------------*/ void do_final_pc(void) { + do_final_pc_groups(); return; } @@ -8665,5 +8661,7 @@ int do_init_pc(void) } } + do_init_pc_groups(); + return 0; } diff --git a/src/map/pc.h b/src/map/pc.h index 34c05d7b8..47d9edead 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -6,6 +6,7 @@ #include "../common/mmo.h" // JOB_*, MAX_FAME_LIST, struct fame_list, struct mmo_charstatus #include "../common/timer.h" // INVALID_TIMER +#include "atcommand.h" // AtCommandType #include "battle.h" // battle_config #include "buyingstore.h" // struct s_buyingstore #include "itemdb.h" // MAX_ITEMGROUP @@ -159,7 +160,7 @@ struct map_session_data { } special_state; int login_id1, login_id2; unsigned short class_; //This is the internal job ID used by the map server to simplify comparisons/queries/etc. [Skotlex] - int gmlevel; + int group_id; int packet_ver; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 ... 18 struct mmo_charstatus status; @@ -568,6 +569,27 @@ enum equip_index { EQI_MAX }; +enum e_pc_permission { + PC_PERM_NONE = 0, + PC_PERM_TRADE = 0x00001, + PC_PERM_PARTY = 0x00002, + PC_PERM_ALL_SKILL = 0x00004, + PC_PERM_USE_ALL_EQUIPMENT = 0x00008, + PC_PERM_SKILL_UNCONDITIONAL = 0x00010, + PC_PERM_JOIN_ALL_CHAT = 0x00020, + PC_PERM_NO_CHAT_KICK = 0x00040, + PC_PERM_HIDE_SESSION = 0x00080, + PC_PERM_WHO_DISPLAY_AID = 0x00100, + PC_PERM_RECEIVE_HACK_INFO = 0x00200, + PC_PERM_WARP_ANYWHERE = 0x00400, + PC_PERM_VIEW_HPMETER = 0x00800, + PC_PERM_VIEW_EQUIPMENT = 0x01000, + PC_PERM_USE_CHECK = 0x02000, + PC_PERM_USE_CHANGEMAPTYPE = 0x04000, + PC_PERM_USE_ALL_COMMANDS = 0x08000, + PC_PERM_RECEIVE_REQUESTS = 0x10000, +}; + #define pc_setdead(sd) ( (sd)->state.dead_sit = (sd)->vd.dead_sit = 1 ) #define pc_setsit(sd) ( (sd)->state.dead_sit = (sd)->vd.dead_sit = 2 ) #define pc_isdead(sd) ( (sd)->state.dead_sit == 1 ) @@ -608,15 +630,20 @@ enum equip_index { ) int pc_class2idx(int class_); -int pc_isGM(struct map_session_data *sd); +int pc_get_group_level(struct map_session_data *sd); +int pc_get_group_id(struct map_session_data *sd); int pc_getrefinebonus(int lv,int type); -bool pc_can_give_items(int level); +bool pc_can_give_items(struct map_session_data *sd); + +bool pc_can_use_command(struct map_session_data *sd, const char *command, AtCommandType type); +bool pc_has_permission(struct map_session_data *sd, int permission); +bool pc_should_log_commands(struct map_session_data *sd); int pc_setrestartvalue(struct map_session_data *sd,int type); int pc_makesavestatus(struct map_session_data *); void pc_respawn(struct map_session_data* sd, clr_type clrtype); int pc_setnewpc(struct map_session_data*,int,int,int,unsigned int,int,int); -bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_time, int gmlevel, struct mmo_charstatus *st, bool changing_mapservers); +bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_time, int group_id, struct mmo_charstatus *st, bool changing_mapservers); void pc_authfail(struct map_session_data *); int pc_reg_received(struct map_session_data *sd); @@ -638,8 +665,6 @@ int pc_clean_skilltree(struct map_session_data *sd); int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y, clr_type clrtype); int pc_setsavepoint(struct map_session_data*,short,int,int); int pc_randomwarp(struct map_session_data *sd,clr_type type); -int pc_warpto(struct map_session_data* sd, struct map_session_data* pl_sd); -int pc_recall(struct map_session_data* sd, struct map_session_data* pl_sd); int pc_memo(struct map_session_data* sd, int pos); int pc_checkadditem(struct map_session_data*,int,int); diff --git a/src/map/pc_groups.c b/src/map/pc_groups.c new file mode 100644 index 000000000..3ceb905ba --- /dev/null +++ b/src/map/pc_groups.c @@ -0,0 +1,459 @@ +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include "../common/conf.h" +#include "../common/db.h" +#include "../common/malloc.h" +#include "../common/nullpo.h" +#include "../common/showmsg.h" +#include "../common/strlib.h" // strcmp + +#include "atcommand.h" // AtCommandType +#include "pc_groups.h" +#include "pc.h" // e_pc_permission + + +typedef struct GroupSettings GroupSettings; + +// Cached config settings/pointers for quick lookup +struct GroupSettings { + unsigned int id; // groups.[].id + int level; // groups.[].level + const char *name; // groups.[].name + config_setting_t *commands; // groups.[].commands + unsigned int e_permissions; // packed groups.[].permissions + bool log_commands; // groups.[].log_commands + /// Following are used only during config reading + config_setting_t *permissions; // groups.[].permissions + config_setting_t *inherit; // groups.[].inherit + bool inheritance_done; // have all inheritance rules been evaluated? + config_setting_t *root; // groups.[] +}; + + +static config_t pc_group_config; +static DBMap* pc_group_db; // id -> GroupSettings +static DBMap* pc_groupname_db; // name -> GroupSettings + +static const struct { + const char *name; + int permission; +} permission_name[] = { + { "can_trade", PC_PERM_TRADE }, + { "can_party", PC_PERM_PARTY }, + { "all_skill", PC_PERM_ALL_SKILL }, + { "all_equipment", PC_PERM_USE_ALL_EQUIPMENT }, + { "skill_unconditional", PC_PERM_SKILL_UNCONDITIONAL }, + { "join_chat", PC_PERM_JOIN_ALL_CHAT }, + { "kick_chat", PC_PERM_NO_CHAT_KICK }, + { "hide_session", PC_PERM_HIDE_SESSION }, + { "who_display_aid", PC_PERM_WHO_DISPLAY_AID }, + { "hack_info", PC_PERM_RECEIVE_HACK_INFO }, + { "any_warp", PC_PERM_WARP_ANYWHERE }, + { "view_hpmeter", PC_PERM_VIEW_HPMETER }, + { "view_equipment", PC_PERM_VIEW_EQUIPMENT }, + { "use_check", PC_PERM_USE_CHECK }, + { "use_changemaptype", PC_PERM_USE_CHANGEMAPTYPE }, + { "all_commands", PC_PERM_USE_ALL_COMMANDS }, + { "receive_requests", PC_PERM_RECEIVE_REQUESTS }, +}; + +/** + * @retval NULL if not found + * @private + */ +static inline GroupSettings* id2group(int group_id) +{ + return (GroupSettings*)idb_get(pc_group_db, group_id); +} + +/** + * @retval NULL if not found + * @private + */ +static inline GroupSettings* name2group(const char* group_name) +{ + return (GroupSettings*)strdb_get(pc_groupname_db, group_name); +} + +/** + * Loads group configuration from config file into memory. + * @private + */ +static void read_config(void) +{ + config_setting_t *groups = NULL; + const char *config_filename = "conf/groups.conf"; // FIXME hardcoded name + int group_count = 0; + + if (conf_read_file(&pc_group_config, config_filename)) + return; + + groups = config_lookup(&pc_group_config, "groups"); + + if (groups != NULL) { + GroupSettings *group_settings = NULL; + DBIterator *iter = NULL; + int i, loop = 0; + + group_count = config_setting_length(groups); + for (i = 0; i < group_count; ++i) { + int id = 0, level = 0; + const char *groupname = NULL; + int log_commands = 0; + config_setting_t *group = config_setting_get_elem(groups, i); + + if (!config_setting_lookup_int(group, "id", &id)) { + ShowConfigWarning(group, "pc_groups:read_config: \"groups\" list member #%d has undefined id, removing...", i); + config_setting_remove_elem(groups, i); + --i; + --group_count; + continue; + } + + if (id2group(id) != NULL) { + ShowConfigWarning(group, "pc_groups:read_config: duplicate group id %d, removing...", i); + config_setting_remove_elem(groups, i); + --i; + --group_count; + continue; + } + + config_setting_lookup_int(group, "level", &level); + config_setting_lookup_int(group, "log_commands", &log_commands); + + if (!config_setting_lookup_string(group, "name", &groupname)) { + char temp[20]; + config_setting_t *name = NULL; + snprintf(temp, sizeof(temp), "Group %d", id); + if ((name = config_setting_add(group, "name", CONFIG_TYPE_STRING)) == NULL || + !config_setting_set_string(name, temp)) { + ShowError("pc_groups:read_config: failed to set missing group name, id=%d, skipping... (%s:%d)\n", + id, config_setting_source_file(group), config_setting_source_line(group)); + continue; + } + config_setting_lookup_string(group, "name", &groupname); // Retrieve the pointer + } + + if (name2group(groupname) != NULL) { + ShowConfigWarning(group, "pc_groups:read_config: duplicate group name %s, removing...", groupname); + config_setting_remove_elem(groups, i); + --i; + --group_count; + continue; + } + + CREATE(group_settings, GroupSettings, 1); + group_settings->id = id; + group_settings->level = level; + group_settings->name = groupname; + group_settings->log_commands = (bool)log_commands; + group_settings->inherit = config_setting_get_member(group, "inherit"); + group_settings->commands = config_setting_get_member(group, "commands"); + group_settings->permissions = config_setting_get_member(group, "permissions"); + group_settings->inheritance_done = false; + group_settings->root = group; + + strdb_put(pc_groupname_db, groupname, group_settings); + idb_put(pc_group_db, id, group_settings); + + } + group_count = config_setting_length(groups); // Save number of groups + + // Check if all commands and permissions exist + iter = pc_group_db->iterator(pc_group_db); + for (group_settings = (GroupSettings*)iter->first(iter, NULL); + iter->exists(iter); + group_settings = (GroupSettings*)iter->next(iter, NULL)) { + config_setting_t *commands = group_settings->commands, *permissions = group_settings->permissions; + int count = 0, i; + + // Make sure there is "commands" group + if (commands == NULL) + commands = group_settings->commands = config_setting_add(group_settings->root, "commands", CONFIG_TYPE_GROUP); + count = config_setting_length(commands); + + for (i = 0; i < count; ++i) { + config_setting_t *command = config_setting_get_elem(commands, i); + const char *name = config_setting_name(command); + if (!atcommand_exists(name)) { + ShowConfigWarning(command, "pc_groups:read_config: non-existent command name '%s', removing...", name); + config_setting_remove(commands, name); + --i; + --count; + } + } + + // Make sure there is "permissions" group + if (permissions == NULL) + permissions = group_settings->permissions = config_setting_add(group_settings->root, "permissions", CONFIG_TYPE_GROUP); + count = config_setting_length(permissions); + + for(i = 0; i < count; ++i) { + config_setting_t *permission = config_setting_get_elem(permissions, i); + const char *name = config_setting_name(permission); + int j; + + ARR_FIND(0, ARRAYLENGTH(permission_name), j, strcmp(permission_name[j].name, name) == 0); + if (j == ARRAYLENGTH(permission_name)) { + ShowConfigWarning(permission, "pc_groups:read_config: non-existent permission name '%s', removing...", name); + config_setting_remove(permissions, name); + --i; + --count; + } + } + } + iter->destroy(iter); + + // Apply inheritance + i = 0; // counter for processed groups + while (i < group_count) { + iter = pc_group_db->iterator(pc_group_db); + for (group_settings = (GroupSettings*)iter->first(iter, NULL); + iter->exists(iter); + group_settings = (GroupSettings*)iter->next(iter, NULL)) { + config_setting_t *inherit = NULL, + *commands = group_settings->commands, + *permissions = group_settings->permissions; + int j, inherit_count = 0, done = 0; + + if (group_settings->inheritance_done) // group already processed + continue; + + if ((inherit = group_settings->inherit) == NULL || + (inherit_count = config_setting_length(inherit)) <= 0) { // this group does not inherit from others + ++i; + group_settings->inheritance_done = true; + continue; + } + + for (j = 0; j < inherit_count; ++j) { + GroupSettings *inherited_group = NULL; + const char *groupname = config_setting_get_string_elem(inherit, j); + + if (groupname == NULL) { + ShowConfigWarning(inherit, "pc_groups:read_config: \"inherit\" array member #%d is not a name, removing...", j); + config_setting_remove_elem(inherit,j); + continue; + } + if ((inherited_group = name2group(groupname)) == NULL) { + ShowConfigWarning(inherit, "pc_groups:read_config: non-existent group name \"%s\", removing...", groupname); + config_setting_remove_elem(inherit,j); + continue; + } + if (!inherited_group->inheritance_done) + continue; // we need to do that group first + + // Copy settings (commands/permissions) that are not defined yet + if (inherited_group->commands != NULL) { + int i = 0, commands_count = config_setting_length(inherited_group->commands); + for (i = 0; i < commands_count; ++i) + config_setting_copy(commands, config_setting_get_elem(inherited_group->commands, i)); + } + + if (inherited_group->permissions != NULL) { + int i = 0, permissions_count = config_setting_length(inherited_group->permissions); + for (i = 0; i < permissions_count; ++i) + config_setting_copy(permissions, config_setting_get_elem(inherited_group->permissions, i)); + } + + ++done; // copied commands and permissions from one of inherited groups + } + + if (done == inherit_count) { // copied commands from all of inherited groups + ++i; + group_settings->inheritance_done = true; // we're done with this group + } + } + iter->destroy(iter); + + if (++loop > group_count) { + ShowWarning("pc_groups:read_config: Could not process inheritance rules, check your config '%s' for cycles...\n", + config_filename); + break; + } + } // while(i < group_count) + + // Pack permissions into GroupSettings.e_permissions for faster checking + iter = db_iterator(pc_group_db); + for (group_settings = (GroupSettings*)dbi_first(iter); + dbi_exists(iter); + group_settings = (GroupSettings*)dbi_next(iter)) { + config_setting_t *permissions = group_settings->permissions; + int i, count = config_setting_length(permissions); + + for (i = 0; i < count; ++i) { + config_setting_t *perm = config_setting_get_elem(permissions, i); + const char *name = config_setting_name(perm); + int j; + + ARR_FIND(0, ARRAYLENGTH(permission_name), j, strcmp(permission_name[j].name, name) == 0); + group_settings->e_permissions |= permission_name[j].permission; + } + } + iter->destroy(iter); + } + + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' groups in '"CL_WHITE"%s"CL_RESET"'.\n", group_count, config_filename); +} + +/** + * Removes group configuration from memory. + * @private + */ +static void destroy_config(void) +{ + config_destroy(&pc_group_config); +} + +/** + * In group configuration file, setting for each command is either + * <commandname> : <bool> (only atcommand), or + * <commandname> : [ <bool>, <bool> ] ([ atcommand, charcommand ]) + * Maps AtCommandType enums to indexes of <commandname> value array, + * COMMAND_ATCOMMAND (1) being index 0, COMMAND_CHARCOMMAND (2) being index 1. + * @private + */ +static inline int AtCommandType2idx(AtCommandType type) { return (type-1); } + +/** + * Checks if player group can use @/#command + * @param group_id ID of the group + * @param command Command name without @/# and params + * @param type enum AtCommanndType { COMMAND_ATCOMMAND = 1, COMMAND_CHARCOMMAND = 2 } + */ +bool pc_group_can_use_command(int group_id, const char *command, AtCommandType type) +{ + int result = 0; + config_setting_t *commands = NULL; + GroupSettings *group = NULL; + + if (pc_group_has_permission(group_id, PC_PERM_USE_ALL_COMMANDS)) + return true; + + if ((group = id2group(group_id)) == NULL) + return false; + + commands = group->commands; + if (commands != NULL) { + config_setting_t *cmd = NULL; + + // <commandname> : <bool> (only atcommand) + if (type == COMMAND_ATCOMMAND && config_setting_lookup_bool(commands, command, &result)) + return (bool)result; + + // <commandname> : [ <bool>, <bool> ] ([ atcommand, charcommand ]) + if ((cmd = config_setting_get_member(commands, command)) != NULL && + config_setting_is_aggregate(cmd) && config_setting_length(cmd) == 2) + return (bool)config_setting_get_bool_elem(cmd, AtCommandType2idx(type)); + } + return false; +} + +/** + * Checks if player group has a permission + * @param group_id ID of the group + * @param permission permission to check + */ +bool pc_group_has_permission(int group_id, int permission) +{ + GroupSettings *group = NULL; + if ((group = id2group(group_id)) == NULL) + return false; + return ((group->e_permissions&permission) != 0); +} + +/** + * Checks commands used by player group should be logged + * @param group_id ID of the group + */ +bool pc_group_should_log_commands(int group_id) +{ + GroupSettings *group = NULL; + if ((group = id2group(group_id)) == NULL) + return false; + return group->log_commands; +} + +/** + * Checks if player group with given ID exists. + * @param group_id group id + * @returns true if group exists, false otherwise + */ +bool pc_group_exists(int group_id) +{ + return idb_exists(pc_group_db, group_id); +} + +/** + * Group ID -> group name lookup. Used only in @who atcommands. + * @param group_id group id + * @return group name + * @public + */ +const char* pc_group_id2name(int group_id) +{ + GroupSettings *group = id2group(group_id); + if (group == NULL) + return "Non-existent group!"; + return group->name; +} + +/** + * Group ID -> group level lookup. A way to provide backward compatibility with GM level system. + * @param group id + * @return group level + * @public + */ +int pc_group_id2level(int group_id) +{ + GroupSettings *group = id2group(group_id); + if (group == NULL) + return 0; + return group->level; +} + +/** + * Initialize PC Groups: allocate DBMaps and read config. + * @public + */ +void do_init_pc_groups(void) +{ + pc_group_db = idb_alloc(DB_OPT_BASE); + pc_groupname_db = stridb_alloc(DB_OPT_DUP_KEY, 0); + read_config(); +} + +/** + * DBApply helper function for do_final_pc_groups + * @private + */ +static int group_db_free(DBKey key, void *data, va_list args) +{ + aFree((GroupSettings*)data); + return 1; +} + +/** + * Finalize PC Groups: free DBMaps and config. + * @public + */ +void do_final_pc_groups(void) +{ + if (pc_group_db != NULL) + pc_group_db->destroy(pc_group_db, group_db_free); + if (pc_groupname_db != NULL ) + db_destroy(pc_groupname_db); + destroy_config(); +} + +/** + * Reload PC Groups + * Used in @reloadatcommand + * @public + */ +void pc_groups_reload(void) +{ + do_final_pc_groups(); + do_init_pc_groups(); +}
\ No newline at end of file diff --git a/src/map/pc_groups.h b/src/map/pc_groups.h new file mode 100644 index 000000000..1134bc01a --- /dev/null +++ b/src/map/pc_groups.h @@ -0,0 +1,20 @@ +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _PC_GROUPS_H_ +#define _PC_GROUPS_H_ + +#include "atcommand.h" // AtCommandType + +bool pc_group_exists(int group_id); +bool pc_group_can_use_command(int group_id, const char *command, AtCommandType type); +bool pc_group_has_permission(int group_id, int permission); +bool pc_group_should_log_commands(int group_id); +const char* pc_group_id2name(int group_id); +int pc_group_id2level(int group_id); + +void do_init_pc_groups(void); +void do_final_pc_groups(void); +void pc_groups_reload(void); + +#endif // _PC_GROUPS_H_
\ No newline at end of file diff --git a/src/map/script.c b/src/map/script.c index 0a92ae835..705eb041b 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -7345,7 +7345,7 @@ BUILDIN_FUNC(getgmlevel) if( sd == NULL ) return 0;// no player attached, report source - script_pushint(st, pc_isGM(sd)); + script_pushint(st, pc_get_group_level(sd)); return 0; } @@ -8704,17 +8704,18 @@ BUILDIN_FUNC(getusers) BUILDIN_FUNC(getusersname) { TBL_PC *sd, *pl_sd; - int disp_num=1; + int disp_num=1, group_level = 0; struct s_mapiterator* iter; sd = script_rid2sd(st); if (!sd) return 0; + group_level = pc_get_group_level(sd); iter = mapit_getallusers(); for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) ) { - if( battle_config.hide_GM_session && pc_isGM(pl_sd) ) - continue; // skip hidden GMs + if (pc_has_permission(pl_sd, PC_PERM_HIDE_SESSION) && pc_get_group_level(pl_sd) > group_level) + continue; // skip hidden sessions if((disp_num++)%10==0) clif_scriptnext(sd,st->oid); @@ -11586,9 +11587,6 @@ BUILDIN_FUNC(nude) /*========================================== * gmcommand [MouseJstr] - * - * suggested on the forums... - * splitted into atcommand & charcommand by [Skotlex] *------------------------------------------*/ BUILDIN_FUNC(atcommand) { @@ -11616,51 +11614,12 @@ BUILDIN_FUNC(atcommand) } } - // compatibility with previous implementation (deprecated!) - if(cmd[0] != atcommand_symbol) - { - cmd += strlen(sd->status.name); - while(*cmd != atcommand_symbol && *cmd != 0) - cmd++; - } - - is_atcommand(fd, sd, cmd, 0); - return 0; -} - -BUILDIN_FUNC(charcommand) -{ - TBL_PC dummy_sd; - TBL_PC* sd; - int fd; - const char* cmd; - - cmd = script_getstr(st,2); - - if (st->rid) { - sd = script_rid2sd(st); - fd = sd->fd; - } else { //Use a dummy character. - sd = &dummy_sd; - fd = 0; - - memset(&dummy_sd, 0, sizeof(TBL_PC)); - if (st->oid) - { - struct block_list* bl = map_id2bl(st->oid); - memcpy(&dummy_sd.bl, bl, sizeof(struct block_list)); - if (bl->type == BL_NPC) - safestrncpy(dummy_sd.status.name, ((TBL_NPC*)bl)->name, NAME_LENGTH); - } - } - - if (*cmd != charcommand_symbol) { - ShowWarning("script: buildin_charcommand: No '#' symbol!\n"); + if (!is_atcommand(fd, sd, cmd, 0)) { + ShowWarning("script: buildin_atcommand: failed to execute command '%s'\n", cmd); script_reportsrc(st); return 1; } - - is_atcommand(fd, sd, cmd, 0); + return 0; } @@ -16170,7 +16129,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(nude,""), // nude command [Valaris] BUILDIN_DEF(mapwarp,"ssii??"), // Added by RoVeRT BUILDIN_DEF(atcommand,"s"), // [MouseJstr] - BUILDIN_DEF(charcommand,"s"), // [MouseJstr] + BUILDIN_DEF2(atcommand,"charcommand","s"), // [MouseJstr] BUILDIN_DEF(movenpc,"sii?"), // [MouseJstr] BUILDIN_DEF(message,"ss"), // [MouseJstr] BUILDIN_DEF(npctalk,"s"), // [Valaris] diff --git a/src/map/skill.c b/src/map/skill.c index 2fef37baf..9bf915917 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -436,8 +436,8 @@ int skillnotok (int skillid, struct map_session_data *sd) if (i == 0) return 1; // invalid skill id - if (battle_config.gm_skilluncond && pc_isGM(sd) >= battle_config.gm_skilluncond) - return 0; // GMs can do any damn thing they want + if (pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL)) + return 0; // can do any damn thing they want if( skillid == AL_TELEPORT && sd->skillitem == skillid && sd->skillitemlv > 2 ) return 0; // Teleport lv 3 bypasses this check.[Inkfish] @@ -5412,7 +5412,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in case MC_VENDING: if(sd) { //Prevent vending of GMs with unnecessary Level to trade/drop. [Skotlex] - if ( !pc_can_give_items(pc_isGM(sd)) ) + if ( !pc_can_give_items(sd) ) clif_skill_fail(sd,skillid,USESKILL_FAIL_LEVEL,0); else { sd->state.prevend = 1; @@ -10256,8 +10256,7 @@ int skill_check_pc_partner (struct map_session_data *sd, short skill_id, short* static int p_sd[2] = { 0, 0 }; int i; - if (!battle_config.player_skill_partner_check || - (battle_config.gm_skilluncond && pc_isGM(sd) >= battle_config.gm_skilluncond)) + if (!battle_config.player_skill_partner_check || pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL)) return 99; //As if there were infinite partners. if (cast_flag) @@ -10352,7 +10351,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, short skill, sh if (lv <= 0 || sd->chatID) return 0; - if( battle_config.gm_skilluncond && pc_isGM(sd)>= battle_config.gm_skilluncond && sd->skillitem != skill ) + if( pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL) && sd->skillitem != skill ) { //GMs don't override the skillItem check, otherwise they can use items without them being consumed! [Skotlex] sd->state.arrow_atk = skill_get_ammotype(skill)?1:0; //Need to do arrow state check. sd->spiritball_old = sd->spiritball; //Need to do Spiritball check. @@ -11028,7 +11027,7 @@ int skill_check_condition_castend(struct map_session_data* sd, short skill, shor if( lv <= 0 || sd->chatID ) return 0; - if( battle_config.gm_skilluncond && pc_isGM(sd) >= battle_config.gm_skilluncond && sd->skillitem != skill ) + if( pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL) && sd->skillitem != skill ) { //GMs don't override the skillItem check, otherwise they can use items without them being consumed! [Skotlex] sd->state.arrow_atk = skill_get_ammotype(skill)?1:0; //Need to do arrow state check. sd->spiritball_old = sd->spiritball; //Need to do Spiritball check. diff --git a/src/map/storage.c b/src/map/storage.c index 12d9a8107..4f6dfff0e 100644 --- a/src/map/storage.c +++ b/src/map/storage.c @@ -95,7 +95,7 @@ int storage_storageopen(struct map_session_data *sd) if(sd->state.storage_flag) return 1; //Already open? - if( !pc_can_give_items(pc_isGM(sd)) ) + if( !pc_can_give_items(sd) ) { //check is this GM level is allowed to put items to storage clif_displaymessage(sd->fd, msg_txt(246)); return 1; @@ -138,7 +138,7 @@ static int storage_additem(struct map_session_data* sd, struct item* item_data, data = itemdb_search(item_data->nameid); - if( !itemdb_canstore(item_data, pc_isGM(sd)) ) + if( !itemdb_canstore(item_data, pc_get_group_level(sd)) ) { //Check if item is storable. [Skotlex] clif_displaymessage (sd->fd, msg_txt(264)); return 1; @@ -357,7 +357,7 @@ int storage_guild_storageopen(struct map_session_data* sd) if(sd->state.storage_flag) return 1; //Can't open both storages at a time. - if( !pc_can_give_items(pc_isGM(sd)) ) { //check is this GM level can open guild storage and store items [Lupus] + if( !pc_can_give_items(sd) ) { //check is this GM level can open guild storage and store items [Lupus] clif_displaymessage(sd->fd, msg_txt(246)); return 1; } @@ -391,7 +391,7 @@ int guild_storage_additem(struct map_session_data* sd, struct guild_storage* sto data = itemdb_search(item_data->nameid); - if( !itemdb_canguildstore(item_data, pc_isGM(sd)) || item_data->expire_time ) + if( !itemdb_canguildstore(item_data, pc_get_group_level(sd)) || item_data->expire_time ) { //Check if item is storable. [Skotlex] clif_displaymessage (sd->fd, msg_txt(264)); return 1; diff --git a/src/map/trade.c b/src/map/trade.c index 3c398ab83..cfc4992e8 100644 --- a/src/map/trade.c +++ b/src/map/trade.c @@ -29,8 +29,6 @@ *------------------------------------------*/ void trade_traderequest(struct map_session_data *sd, struct map_session_data *target_sd) { - int level; - nullpo_retv(sd); if (map[sd->bl.m].flag.notrade) { @@ -61,18 +59,16 @@ void trade_traderequest(struct map_session_data *sd, struct map_session_data *ta return; } - level = pc_isGM(sd); - if ( !pc_can_give_items(level) || !pc_can_give_items(pc_isGM(target_sd)) ) //check if both GMs are allowed to trade + if (!pc_can_give_items(sd) || !pc_can_give_items(target_sd)) //check if both GMs are allowed to trade { clif_displaymessage(sd->fd, msg_txt(246)); clif_tradestart(sd, 2); // GM is not allowed to trade return; } - //Fixed. Only real GMs can request trade from far away! [Lupus] - if (level < battle_config.lowest_gm_level && (sd->bl.m != target_sd->bl.m || - !check_distance_bl(&sd->bl, &target_sd->bl, TRADE_DISTANCE) - )) { + // Players can not request trade from far away, unless they are allowed to use @trade. + if (!pc_can_use_command(sd, "trade", COMMAND_ATCOMMAND) && + (sd->bl.m != target_sd->bl.m || !check_distance_bl(&sd->bl, &target_sd->bl, TRADE_DISTANCE))) { clif_tradestart(sd, 0); // too far return ; } @@ -127,10 +123,10 @@ void trade_tradeack(struct map_session_data *sd, int type) if (type != 3) return; //If client didn't send accept, it's a broken packet? - //Copied here as well since the original character could had warped. - if (pc_isGM(tsd) < battle_config.lowest_gm_level && (sd->bl.m != tsd->bl.m || - !check_distance_bl(&sd->bl, &tsd->bl, TRADE_DISTANCE) - )) { + // Players can not request trade from far away, unless they are allowed to use @trade. + // Check here as well since the original character could had warped. + if (!pc_can_use_command(sd, "trade", COMMAND_ATCOMMAND) && + (sd->bl.m != tsd->bl.m || !check_distance_bl(&sd->bl, &tsd->bl, TRADE_DISTANCE))) { clif_tradestart(sd, 0); // too far sd->trade_partner=0; tsd->trade_partner = 0; @@ -196,9 +192,9 @@ int impossible_trade_check(struct map_session_data *sd) if (inventory[index].amount < sd->deal.item[i].amount) { // if more than the player have -> hack sprintf(message_to_gm, msg_txt(538), sd->status.name, sd->status.account_id); // Hack on trade: character '%s' (account: %d) try to trade more items that he has. - intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm); + intif_wis_message_to_gm(wisp_server_name, PC_PERM_RECEIVE_HACK_INFO, message_to_gm); sprintf(message_to_gm, msg_txt(539), inventory[index].amount, inventory[index].nameid, sd->deal.item[i].amount); // This player has %d of a kind of item (id: %d), and try to trade %d of them. - intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm); + intif_wis_message_to_gm(wisp_server_name, PC_PERM_RECEIVE_HACK_INFO, message_to_gm); // if we block people if (battle_config.ban_hack_trade < 0) { chrif_char_ask_name(-1, sd->status.name, 1, 0, 0, 0, 0, 0, 0); // type: 1 - block @@ -215,7 +211,7 @@ int impossible_trade_check(struct map_session_data *sd) // message about the ban strcpy(message_to_gm, msg_txt(508)); // This player hasn't been banned (Ban option is disabled). - intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm); + intif_wis_message_to_gm(wisp_server_name, PC_PERM_RECEIVE_HACK_INFO, message_to_gm); return 1; } inventory[index].amount -= sd->deal.item[i].amount; // remove item from inventory @@ -345,8 +341,8 @@ void trade_tradeadditem(struct map_session_data *sd, short index, short amount) return; item = &sd->status.inventory[index]; - src_lv = pc_isGM(sd); - dst_lv = pc_isGM(target_sd); + src_lv = pc_get_group_level(sd); + dst_lv = pc_get_group_level(target_sd); if( !itemdb_cantrade(item, src_lv, dst_lv) && //Can't trade (pc_get_partner(sd) != target_sd || !itemdb_canpartnertrade(item, src_lv, dst_lv)) ) //Can't partner-trade { diff --git a/src/map/vending.c b/src/map/vending.c index 0db4ee85b..6deba0e55 100644 --- a/src/map/vending.c +++ b/src/map/vending.c @@ -54,7 +54,7 @@ void vending_vendinglistreq(struct map_session_data* sd, int id) if( !vsd->state.vending ) return; // not vending - if ( !pc_can_give_items(pc_isGM(sd)) || !pc_can_give_items(pc_isGM(vsd)) ) //check if both GMs are allowed to trade + if (!pc_can_give_items(sd) || !pc_can_give_items(vsd)) //check if both GMs are allowed to trade { // GM is not allowed to trade clif_displaymessage(sd->fd, msg_txt(246)); return; @@ -281,7 +281,7 @@ void vending_openvending(struct map_session_data* sd, const char* message, bool || !sd->status.cart[index].identify // unidentified item || sd->status.cart[index].attribute == 1 // broken item || sd->status.cart[index].expire_time // It should not be in the cart but just in case - || !itemdb_cantrade(&sd->status.cart[index], pc_isGM(sd), pc_isGM(sd)) ) // untradeable item + || !itemdb_cantrade(&sd->status.cart[index], pc_get_group_level(sd), pc_get_group_level(sd)) ) // untradeable item continue; sd->vending[i].index = index; |