summaryrefslogtreecommitdiff
path: root/src/map
diff options
context:
space:
mode:
authorgepard1984 <gepard1984@54d463be-8e91-2dee-dedb-b68131a5f0ec>2012-02-13 01:19:04 +0000
committergepard1984 <gepard1984@54d463be-8e91-2dee-dedb-b68131a5f0ec>2012-02-13 01:19:04 +0000
commit526217d77d50dc27b0815e3d5895df7bfa38ff76 (patch)
tree9fb6152ef59b7d08e7f226fbdc47eb6ba9617cc6 /src/map
parent87469dc59de62990878ce6ccd29769ebd5b7d675 (diff)
downloadhercules-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.in24
-rw-r--r--src/map/atcommand.c1587
-rw-r--r--src/map/atcommand.h30
-rw-r--r--src/map/battle.c26
-rw-r--r--src/map/battle.h27
-rw-r--r--src/map/buyingstore.c10
-rw-r--r--src/map/chat.c4
-rw-r--r--src/map/chrif.c8
-rw-r--r--src/map/clif.c499
-rw-r--r--src/map/clif.h2
-rw-r--r--src/map/intif.c34
-rw-r--r--src/map/intif.h2
-rw-r--r--src/map/log.c15
-rw-r--r--src/map/log.h4
-rw-r--r--src/map/mail.c8
-rw-r--r--src/map/party.c8
-rw-r--r--src/map/pc.c144
-rw-r--r--src/map/pc.h37
-rw-r--r--src/map/pc_groups.c459
-rw-r--r--src/map/pc_groups.h20
-rw-r--r--src/map/script.c59
-rw-r--r--src/map/skill.c13
-rw-r--r--src/map/storage.c8
-rw-r--r--src/map/trade.c30
-rw-r--r--src/map/vending.c4
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;